aboutsummaryrefslogtreecommitdiffstats
path: root/os/hal/ports/SPC5/LLD/DSPI_v1/hal_spi_lld.c
diff options
context:
space:
mode:
Diffstat (limited to 'os/hal/ports/SPC5/LLD/DSPI_v1/hal_spi_lld.c')
-rw-r--r--os/hal/ports/SPC5/LLD/DSPI_v1/hal_spi_lld.c1870
1 files changed, 1870 insertions, 0 deletions
diff --git a/os/hal/ports/SPC5/LLD/DSPI_v1/hal_spi_lld.c b/os/hal/ports/SPC5/LLD/DSPI_v1/hal_spi_lld.c
new file mode 100644
index 000000000..bc4fd57b2
--- /dev/null
+++ b/os/hal/ports/SPC5/LLD/DSPI_v1/hal_spi_lld.c
@@ -0,0 +1,1870 @@
+/*
+ SPC5 HAL - Copyright (C) 2013 STMicroelectronics
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+/**
+ * @file SPC5xx/DSPI_v1/hal_spi_lld.c
+ * @brief SPC5xx SPI subsystem low level driver source.
+ *
+ * @addtogroup SPI
+ * @{
+ */
+
+#include "hal.h"
+
+#if HAL_USE_SPI || defined(__DOXYGEN__)
+
+/* Some forward declarations.*/
+#if SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE
+static void spi_serve_rx_dma_irq(edma_channel_t channel, void *p);
+#endif
+
+#if SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX
+static void spi_serve_tx_dma_irq(edma_channel_t channel, void *p);
+#endif
+
+#if SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE
+static void spi_serve_dma_error_irq(edma_channel_t channel,
+ void *p,
+ uint32_t esr);
+#endif
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/* Excluded PUSHR bits.*/
+#define DSPI_PUSHR_EXCLUDED_BITS (SPC5_PUSHR_CTAS_MASK | \
+ SPC5_PUSHR_EOQ | \
+ SPC5_PUSHR_TXDATA_MASK)
+
+#define DSPI_POPR8_ADDRESS(spip) (((uint32_t)&(spip)->dspi->POPR.R) + 3)
+#define DSPI_POPR16_ADDRESS(spip) (((uint32_t)&(spip)->dspi->POPR.R) + 2)
+
+/* Set of macros dealing with the variable number of DMAs depending on
+ the chosen mode.*/
+#if SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX
+#define spi_lld_setdma(spip, tx1_cfg, tx2_cfg, rx_cfg) { \
+ (spip)->tx1_channel = edmaChannelAllocate(&(tx1_cfg)); \
+ (spip)->tx2_channel = edmaChannelAllocate(&(tx2_cfg)); \
+ (spip)->rx_channel = edmaChannelAllocate(&(rx_cfg)); \
+}
+#endif
+
+#if SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_ONLY
+#define spi_lld_setdma(spip, tx1_cfg, tx2_cfg, rx_cfg) { \
+ (spip)->rx_channel = edmaChannelAllocate(&(rx_cfg)); \
+}
+#endif
+
+#if SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_NONE
+#define spi_lld_setdma(spip, tx1_cfg, tx2_cfg, rx_cfg) { \
+}
+#endif
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/**
+ * @brief SPID1 driver identifier.
+ */
+#if SPC5_SPI_USE_DSPI0 || defined(__DOXYGEN__)
+SPIDriver SPID1;
+#endif
+
+/**
+ * @brief SPID2 driver identifier.
+ */
+#if SPC5_SPI_USE_DSPI1 || defined(__DOXYGEN__)
+SPIDriver SPID2;
+#endif
+
+/**
+ * @brief SPID3 driver identifier.
+ */
+#if SPC5_SPI_USE_DSPI2 || defined(__DOXYGEN__)
+SPIDriver SPID3;
+#endif
+
+/**
+ * @brief SPID4 driver identifier.
+ */
+#if SPC5_SPI_USE_DSPI3 || defined(__DOXYGEN__)
+SPIDriver SPID4;
+#endif
+
+/**
+ * @brief SPID5 driver identifier.
+ */
+#if SPC5_SPI_USE_DSPI4 || defined(__DOXYGEN__)
+SPIDriver SPID5;
+#endif
+
+/**
+ * @brief SPID6 driver identifier.
+ */
+#if SPC5_SPI_USE_DSPI5 || defined(__DOXYGEN__)
+SPIDriver SPID6;
+#endif
+
+/**
+ * @brief SPID7 driver identifier.
+ */
+#if SPC5_SPI_USE_DSPI6 || defined(__DOXYGEN__)
+SPIDriver SPID7;
+#endif
+
+/**
+ * @brief SPID8 driver identifier.
+ */
+#if SPC5_SPI_USE_DSPI7 || defined(__DOXYGEN__)
+SPIDriver SPID8;
+#endif
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+#if SPC5_SPI_USE_DSPI0 || defined(__DOXYGEN__)
+#if (SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX) || defined(__DOXYGEN__)
+/**
+ * @brief DMA configuration for DSPI0 TX1.
+ */
+static const edma_channel_config_t spi_dspi0_tx1_dma_config = {
+ SPC5_SPI_DSPI0_TX1_DMA_CH_ID,
+#if SPC5_EDMA_HAS_MUX
+ SPC5_DSPI0_TX1_DMA_DEV_ID,
+#endif
+ SPC5_SPI_DSPI0_DMA_IRQ_PRIO,
+ spi_serve_tx_dma_irq, spi_serve_dma_error_irq, &SPID1
+};
+
+/**
+ * @brief DMA configuration for DSPI0 TX2.
+ */
+static const edma_channel_config_t spi_dspi0_tx2_dma_config = {
+ SPC5_SPI_DSPI0_TX2_DMA_CH_ID,
+#if SPC5_EDMA_HAS_MUX
+ 0,
+#endif
+ SPC5_SPI_DSPI0_DMA_IRQ_PRIO,
+ spi_serve_tx_dma_irq, spi_serve_dma_error_irq, &SPID1
+};
+#endif /* SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX */
+
+#if (SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE) || defined(__DOXYGEN__)
+/**
+ * @brief DMA configuration for DSPI0 RX.
+ */
+static const edma_channel_config_t spi_dspi0_rx_dma_config = {
+ SPC5_SPI_DSPI0_RX_DMA_CH_ID,
+#if SPC5_EDMA_HAS_MUX
+ SPC5_DSPI0_RX_DMA_DEV_ID,
+#endif
+ SPC5_SPI_DSPI0_DMA_IRQ_PRIO,
+ spi_serve_rx_dma_irq, spi_serve_dma_error_irq, &SPID1
+};
+#endif /* SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE */
+#endif /* SPC5_SPI_USE_DSPI0 */
+
+#if SPC5_SPI_USE_DSPI1 || defined(__DOXYGEN__)
+#if (SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX) || defined(__DOXYGEN__)
+/**
+ * @brief DMA configuration for DSPI1 TX1.
+ */
+static const edma_channel_config_t spi_dspi1_tx1_dma_config = {
+ SPC5_SPI_DSPI1_TX1_DMA_CH_ID,
+#if SPC5_EDMA_HAS_MUX
+ SPC5_DSPI1_TX1_DMA_DEV_ID,
+#endif
+ SPC5_SPI_DSPI1_DMA_IRQ_PRIO,
+ spi_serve_tx_dma_irq, spi_serve_dma_error_irq, &SPID2
+};
+
+/**
+ * @brief DMA configuration for DSPI1 TX2.
+ */
+static const edma_channel_config_t spi_dspi1_tx2_dma_config = {
+ SPC5_SPI_DSPI1_TX2_DMA_CH_ID,
+#if SPC5_EDMA_HAS_MUX
+ 0,
+#endif
+ SPC5_SPI_DSPI1_DMA_IRQ_PRIO,
+ spi_serve_tx_dma_irq, spi_serve_dma_error_irq, &SPID2
+};
+#endif /* SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX */
+
+#if (SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE) || defined(__DOXYGEN__)
+/**
+ * @brief DMA configuration for DSPI1 RX.
+ */
+static const edma_channel_config_t spi_dspi1_rx_dma_config = {
+ SPC5_SPI_DSPI1_RX_DMA_CH_ID,
+#if SPC5_EDMA_HAS_MUX
+ SPC5_DSPI1_RX_DMA_DEV_ID,
+#endif
+ SPC5_SPI_DSPI1_DMA_IRQ_PRIO,
+ spi_serve_rx_dma_irq, spi_serve_dma_error_irq, &SPID2
+};
+#endif /* SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE */
+#endif /* SPC5_SPI_USE_DSPI1 */
+
+#if SPC5_SPI_USE_DSPI2 || defined(__DOXYGEN__)
+#if (SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX) || defined(__DOXYGEN__)
+/**
+ * @brief DMA configuration for DSPI2 TX1.
+ */
+static const edma_channel_config_t spi_dspi2_tx1_dma_config = {
+ SPC5_SPI_DSPI2_TX1_DMA_CH_ID,
+#if SPC5_EDMA_HAS_MUX
+ SPC5_DSPI2_TX1_DMA_DEV_ID,
+#endif
+ SPC5_SPI_DSPI2_DMA_IRQ_PRIO,
+ spi_serve_tx_dma_irq, spi_serve_dma_error_irq, &SPID3
+};
+
+/**
+ * @brief DMA configuration for DSPI2 TX2.
+ */
+static const edma_channel_config_t spi_dspi2_tx2_dma_config = {
+ SPC5_SPI_DSPI2_TX2_DMA_CH_ID,
+#if SPC5_EDMA_HAS_MUX
+ 0,
+#endif
+ SPC5_SPI_DSPI2_DMA_IRQ_PRIO,
+ spi_serve_tx_dma_irq, spi_serve_dma_error_irq, &SPID3
+};
+#endif /* SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX */
+
+#if (SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE) || defined(__DOXYGEN__)
+/**
+ * @brief DMA configuration for DSPI2 RX.
+ */
+static const edma_channel_config_t spi_dspi2_rx_dma_config = {
+ SPC5_SPI_DSPI2_RX_DMA_CH_ID,
+#if SPC5_EDMA_HAS_MUX
+ SPC5_DSPI2_RX_DMA_DEV_ID,
+#endif
+ SPC5_SPI_DSPI2_DMA_IRQ_PRIO,
+ spi_serve_rx_dma_irq, spi_serve_dma_error_irq, &SPID3
+};
+#endif /* SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE */
+#endif /* SPC5_SPI_USE_DSPI2 */
+
+#if SPC5_SPI_USE_DSPI3 || defined(__DOXYGEN__)
+#if (SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX) || defined(__DOXYGEN__)
+/**
+ * @brief DMA configuration for DSPI3 TX1.
+ */
+static const edma_channel_config_t spi_dspi3_tx1_dma_config = {
+ SPC5_SPI_DSPI3_TX1_DMA_CH_ID,
+#if SPC5_EDMA_HAS_MUX
+ SPC5_DSPI3_TX1_DMA_DEV_ID,
+#endif
+ SPC5_SPI_DSPI3_DMA_IRQ_PRIO,
+ spi_serve_tx_dma_irq, spi_serve_dma_error_irq, &SPID4
+};
+
+/**
+ * @brief DMA configuration for DSPI3 TX2.
+ */
+static const edma_channel_config_t spi_dspi3_tx2_dma_config = {
+ SPC5_SPI_DSPI3_TX2_DMA_CH_ID,
+#if SPC5_EDMA_HAS_MUX
+ 0,
+#endif
+ SPC5_SPI_DSPI3_DMA_IRQ_PRIO,
+ spi_serve_tx_dma_irq, spi_serve_dma_error_irq, &SPID4
+};
+#endif /* SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX */
+
+#if (SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE) || defined(__DOXYGEN__)
+/**
+ * @brief DMA configuration for DSPI3 RX.
+ */
+static const edma_channel_config_t spi_dspi3_rx_dma_config = {
+ SPC5_SPI_DSPI3_RX_DMA_CH_ID,
+#if SPC5_EDMA_HAS_MUX
+ SPC5_DSPI3_RX_DMA_DEV_ID,
+#endif
+ SPC5_SPI_DSPI3_DMA_IRQ_PRIO,
+ spi_serve_rx_dma_irq, spi_serve_dma_error_irq, &SPID4
+};
+#endif /* SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE */
+#endif /* SPC5_SPI_USE_DSPI3 */
+
+#if SPC5_SPI_USE_DSPI4 || defined(__DOXYGEN__)
+#if (SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX) || defined(__DOXYGEN__)
+/**
+ * @brief DMA configuration for DSPI4 TX1.
+ */
+static const edma_channel_config_t spi_dspi4_tx1_dma_config = {
+ SPC5_SPI_DSPI4_TX1_DMA_CH_ID,
+#if SPC5_EDMA_HAS_MUX
+ SPC5_DSPI4_TX1_DMA_DEV_ID,
+#endif
+ SPC5_SPI_DSPI4_DMA_IRQ_PRIO,
+ spi_serve_tx_dma_irq, spi_serve_dma_error_irq, &SPID5
+};
+
+/**
+ * @brief DMA configuration for DSPI4 TX2.
+ */
+static const edma_channel_config_t spi_dspi4_tx2_dma_config = {
+ SPC5_SPI_DSPI4_TX2_DMA_CH_ID,
+#if SPC5_EDMA_HAS_MUX
+ 0,
+#endif
+ SPC5_SPI_DSPI4_DMA_IRQ_PRIO,
+ spi_serve_tx_dma_irq, spi_serve_dma_error_irq, &SPID5
+};
+#endif /* SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX */
+
+#if (SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE) || defined(__DOXYGEN__)
+/**
+ * @brief DMA configuration for DSPI4 RX.
+ */
+static const edma_channel_config_t spi_dspi4_rx_dma_config = {
+ SPC5_SPI_DSPI4_RX_DMA_CH_ID,
+#if SPC5_EDMA_HAS_MUX
+ SPC5_DSPI4_RX_DMA_DEV_ID,
+#endif
+ SPC5_SPI_DSPI4_DMA_IRQ_PRIO,
+ spi_serve_rx_dma_irq, spi_serve_dma_error_irq, &SPID5
+};
+#endif /* SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE */
+#endif /* SPC5_SPI_USE_DSPI4 */
+
+#if SPC5_SPI_USE_DSPI5 || defined(__DOXYGEN__)
+#if (SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX) || defined(__DOXYGEN__)
+/**
+ * @brief DMA configuration for DSPI5 TX1.
+ */
+static const edma_channel_config_t spi_dspi5_tx1_dma_config = {
+ SPC5_SPI_DSPI5_TX1_DMA_CH_ID,
+#if SPC5_EDMA_HAS_MUX
+ SPC5_DSPI5_TX1_DMA_DEV_ID,
+#endif
+ SPC5_SPI_DSPI5_DMA_IRQ_PRIO,
+ spi_serve_tx_dma_irq, spi_serve_dma_error_irq, &SPID6
+};
+
+/**
+ * @brief DMA configuration for DSPI5 TX2.
+ */
+static const edma_channel_config_t spi_dspi5_tx2_dma_config = {
+ SPC5_SPI_DSPI5_TX2_DMA_CH_ID,
+#if SPC5_EDMA_HAS_MUX
+ 0,
+#endif
+ SPC5_SPI_DSPI5_DMA_IRQ_PRIO,
+ spi_serve_tx_dma_irq, spi_serve_dma_error_irq, &SPID6
+};
+#endif /* SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX */
+
+#if (SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE) || defined(__DOXYGEN__)
+/**
+ * @brief DMA configuration for DSPI5 RX.
+ */
+static const edma_channel_config_t spi_dspi5_rx_dma_config = {
+ SPC5_SPI_DSPI5_RX_DMA_CH_ID,
+#if SPC5_EDMA_HAS_MUX
+ SPC5_DSPI5_RX_DMA_DEV_ID,
+#endif
+ SPC5_SPI_DSPI5_DMA_IRQ_PRIO,
+ spi_serve_rx_dma_irq, spi_serve_dma_error_irq, &SPID6
+};
+#endif /* SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE */
+#endif /* SPC5_SPI_USE_DSPI5 */
+
+#if SPC5_SPI_USE_DSPI6 || defined(__DOXYGEN__)
+#if (SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX) || defined(__DOXYGEN__)
+/**
+ * @brief DMA configuration for DSPI6 TX1.
+ */
+static const edma_channel_config_t spi_dspi6_tx1_dma_config = {
+ SPC5_SPI_DSPI6_TX1_DMA_CH_ID,
+#if SPC5_EDMA_HAS_MUX
+ SPC5_DSPI6_TX1_DMA_DEV_ID,
+#endif
+ SPC5_SPI_DSPI6_DMA_IRQ_PRIO,
+ spi_serve_tx_dma_irq, spi_serve_dma_error_irq, &SPID7
+};
+
+/**
+ * @brief DMA configuration for DSPI6 TX2.
+ */
+static const edma_channel_config_t spi_dspi6_tx2_dma_config = {
+ SPC5_SPI_DSPI6_TX2_DMA_CH_ID,
+#if SPC5_EDMA_HAS_MUX
+ 0,
+#endif
+ SPC5_SPI_DSPI6_DMA_IRQ_PRIO,
+ spi_serve_tx_dma_irq, spi_serve_dma_error_irq, &SPID7
+};
+#endif /* SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX */
+
+#if (SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE) || defined(__DOXYGEN__)
+/**
+ * @brief DMA configuration for DSPI6 RX.
+ */
+static const edma_channel_config_t spi_dspi6_rx_dma_config = {
+ SPC5_SPI_DSPI6_RX_DMA_CH_ID,
+#if SPC5_EDMA_HAS_MUX
+ SPC5_DSPI6_RX_DMA_DEV_ID,
+#endif
+ SPC5_SPI_DSPI6_DMA_IRQ_PRIO,
+ spi_serve_rx_dma_irq, spi_serve_dma_error_irq, &SPID7
+};
+#endif /* SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE */
+#endif /* SPC5_SPI_USE_DSPI6 */
+
+#if SPC5_SPI_USE_DSPI7 || defined(__DOXYGEN__)
+#if (SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX) || defined(__DOXYGEN__)
+/**
+ * @brief DMA configuration for DSPI7 TX1.
+ */
+static const edma_channel_config_t spi_dspi7_tx1_dma_config = {
+ SPC5_SPI_DSPI7_TX1_DMA_CH_ID,
+#if SPC5_EDMA_HAS_MUX
+ SPC5_DSPI7_TX1_DMA_DEV_ID,
+#endif
+ SPC5_SPI_DSPI7_DMA_IRQ_PRIO,
+ spi_serve_tx_dma_irq, spi_serve_dma_error_irq, &SPID8
+};
+
+/**
+ * @brief DMA configuration for DSPI7 TX2.
+ */
+static const edma_channel_config_t spi_dspi7_tx2_dma_config = {
+ SPC5_SPI_DSPI7_TX2_DMA_CH_ID,
+#if SPC5_EDMA_HAS_MUX
+ 0,
+#endif
+ SPC5_SPI_DSPI7_DMA_IRQ_PRIO,
+ spi_serve_tx_dma_irq, spi_serve_dma_error_irq, &SPID8
+};
+#endif /* SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX */
+
+#if (SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE) || defined(__DOXYGEN__)
+/**
+ * @brief DMA configuration for DSPI7 RX.
+ */
+static const edma_channel_config_t spi_dspi7_rx_dma_config = {
+ SPC5_SPI_DSPI7_RX_DMA_CH_ID,
+#if SPC5_EDMA_HAS_MUX
+ SPC5_DSPI7_RX_DMA_DEV_ID,
+#endif
+ SPC5_SPI_DSPI7_DMA_IRQ_PRIO,
+ spi_serve_rx_dma_irq, spi_serve_dma_error_irq, &SPID8
+};
+#endif /* SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE */
+#endif /* SPC5_SPI_USE_DSPI7 */
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Initializes the invariant part of the @p SPIDriver structure.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] dspi the physical DSPI unit to be associated to the object
+ *
+ * @notapi
+ */
+static void spi_lld_obj_init(SPIDriver *spip, struct spc5_dspi *dspi) {
+
+ spip->dspi = dspi;
+#if SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX
+ spip->tx1_channel = EDMA_ERROR;
+ spip->tx2_channel = EDMA_ERROR;
+#endif
+#if SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE
+ spip->rx_channel = EDMA_ERROR;
+#endif
+}
+
+/**
+ * @brief DSPI unit setup for transfer.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ *
+ * @notapi
+ */
+static void spi_dspi_start(SPIDriver *spip) {
+
+ spip->dspi->SR.R = spip->dspi->SR.R;
+ spip->dspi->MCR.B.HALT = 0;
+}
+
+/**
+ * @brief DSPI unit transfer stop.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ *
+ * @notapi
+ */
+static void spi_dspi_stop(SPIDriver *spip) {
+
+ /* Stops the DSPI and clears the queues.*/
+ spip->dspi->MCR.R |= SPC5_MCR_HALT | SPC5_MCR_CLR_TXF | SPC5_MCR_CLR_RXF;
+}
+
+/**
+ * @brief Prefills the TX FIFO with idle frames.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in,out] np pointer to the number of frames to send, must be
+ * greater than zero, contains the number of remaining
+ * frames on return
+ *
+ * @notapi
+ */
+static void spi_dspi_prefill_txfifo_idle(SPIDriver *spip, size_t *np) {
+ uint32_t cmd = spip->config->pushr;
+
+ while (spip->dspi->SR.B.TXCTR < SPC5_DSPI_FIFO_DEPTH) {
+ if (--(*np) == 0) {
+ spip->dspi->PUSHR.R = (SPC5_PUSHR_EOQ | cmd | 0xFFFF) & ~SPC5_PUSHR_CONT;
+ break;
+ }
+ spip->dspi->PUSHR.R = cmd | 0x0000FFFF;
+ }
+}
+
+/**
+ * @brief Prefills the TX FIFO using 8 bits frames.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in,out] np pointer to the number of frames to send, must be
+ * greater than zero, contains the number of remaining
+ * frames on return
+ * @param[in,out] txpp pointer to the pointer to the transmit buffer
+ *
+ * @notapi
+ */
+static void spi_dspi_prefill_txfifo8(SPIDriver *spip,
+ size_t *np,
+ const uint8_t **txpp) {
+ uint32_t cmd = spip->config->pushr;
+
+ while (spip->dspi->SR.B.TXCTR < SPC5_DSPI_FIFO_DEPTH) {
+ uint32_t frame = **txpp;
+ (*txpp)++;
+
+ if (--(*np) == 0) {
+ spip->dspi->PUSHR.R = (SPC5_PUSHR_EOQ | cmd | frame) & ~SPC5_PUSHR_CONT;
+ break;
+ }
+ spip->dspi->PUSHR.R = cmd | frame;
+ }
+}
+
+/**
+ * @brief Prefills the TX FIFO using 16 bits frames.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in,out] np pointer to the number of frames to send, must be
+ * greater than zero, contains the number of remaining
+ * frames on return
+ * @param[in,out] txpp pointer to the pointer to the transmit buffer
+ *
+ * @notapi
+ */
+static void spi_dspi_prefill_txfifo16(SPIDriver *spip,
+ size_t *np,
+ const uint16_t **txpp) {
+ uint32_t cmd = spip->config->pushr;
+
+ while (spip->dspi->SR.B.TXCTR < SPC5_DSPI_FIFO_DEPTH) {
+ uint32_t frame = **txpp;
+ (*txpp)++;
+
+ if (--(*np) == 0) {
+ spip->dspi->PUSHR.R = (SPC5_PUSHR_EOQ | cmd | frame) & ~SPC5_PUSHR_CONT;
+ break;
+ }
+ spip->dspi->PUSHR.R = cmd | frame;
+ }
+}
+
+/**
+ * @brief Starts reception using DMA ignoring the received data.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] n number of words to be exchanged
+ *
+ * @notapi
+ */
+static void spi_start_rx_ignore(SPIDriver *spip, size_t n) {
+
+#if SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_NONE
+ /* Setting up the fields required for operation continuation.*/
+ spip->rx_ptr = NULL;
+ spip->rx_cnt = n;
+
+#else /* SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE */
+ static uint32_t datasink;
+
+ edmaChannelSetup(spip->rx_channel, /* channel. */
+ DSPI_POPR8_ADDRESS(spip), /* src. */
+ &datasink, /* dst. */
+ 0, /* soff, do not advance. */
+ 0, /* doff, do not advance. */
+ 0, /* ssize, 8 bits transfers. */
+ 0, /* dsize, 8 bits transfers. */
+ 1, /* nbytes, always one. */
+ n, /* iter. */
+ 0, /* slast. */
+ 0, /* dlast. */
+ EDMA_TCD_MODE_DREQ | EDMA_TCD_MODE_INT_END); /* mode.*/
+
+ edmaChannelStart(spip->rx_channel);
+#endif /* SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE */
+}
+
+/**
+ * @brief Starts reception using DMA for frames up to 8 bits.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] n number of words to be exchanged
+ * @param[out] rxbuf the pointer to the receive buffer
+ *
+ * @notapi
+ */
+static void spi_start_rx8(SPIDriver *spip, size_t n, uint8_t *rxbuf) {
+
+#if SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_NONE
+ /* Setting up the fields required for operation continuation.*/
+ spip->rx_ptr8 = rxbuf;
+ spip->rx_cnt = n;
+
+#else /* SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE */
+ edmaChannelSetup(spip->rx_channel, /* channel. */
+ DSPI_POPR8_ADDRESS(spip), /* src. */
+ rxbuf, /* dst. */
+ 0, /* soff, do not advance. */
+ 1, /* doff, advance by one. */
+ 0, /* ssize, 8 bits transfers. */
+ 0, /* dsize, 8 bits transfers. */
+ 1, /* nbytes, always one. */
+ n, /* iter. */
+ 0, /* slast. */
+ 0, /* dlast. */
+ EDMA_TCD_MODE_DREQ | EDMA_TCD_MODE_INT_END); /* mode.*/
+
+ edmaChannelStart(spip->rx_channel);
+#endif /* SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE */
+}
+
+/**
+ * @brief Starts reception using DMA for frames up to 16 bits.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] n number of words to be exchanged
+ * @param[out] rxbuf the pointer to the receive buffer
+ *
+ * @notapi
+ */
+static void spi_start_rx16(SPIDriver *spip, size_t n, uint16_t *rxbuf) {
+
+#if SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_NONE
+ /* Setting up the fields required for operation continuation.*/
+ spip->rx_ptr16 = rxbuf;
+ spip->rx_cnt = n;
+
+#else /* SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE */
+ edmaChannelSetup(spip->rx_channel, /* channel. */
+ DSPI_POPR16_ADDRESS(spip), /* src. */
+ rxbuf, /* dst. */
+ 0, /* soff, do not advance. */
+ 2, /* doff, advance by two. */
+ 1, /* ssize, 16 bits transfers.*/
+ 1, /* dsize, 16 bits transfers.*/
+ 2, /* nbytes, always two. */
+ n, /* iter. */
+ 0, /* slast, no source adjust. */
+ 0, /* dlast. */
+ EDMA_TCD_MODE_DREQ | EDMA_TCD_MODE_INT_END); /* mode. */
+
+ edmaChannelStart(spip->rx_channel);
+#endif /* SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE */
+}
+
+/**
+ * @brief Starts transmission using DMA for frames up to 8 bits.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] n number of words to be exchanged
+ *
+ * @notapi
+ */
+static void spi_start_tx_ignore(SPIDriver *spip, size_t n) {
+
+#if SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_RX_AND_TX
+ /* Preloading the TX FIFO with as much frames as possible.*/
+ spi_dspi_prefill_txfifo_idle(spip, &n);
+
+ /* This is the case where the whole operation can be satisfied using the
+ preloading alone.*/
+ if (n == 0)
+ return;
+
+ /* Setting up the fields required for operation continuation.*/
+ spip->tx_ptr = NULL;
+ spip->tx_cnt = n;
+
+ /* Enabling the TFFF interrupt source for transfer continuation.*/
+ spip->dspi->RSER.B.TFFFRE = 1;
+
+#else /* SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX */
+ /* Special case when the data to be transmitted can entirely fit into the
+ TX FIFO, in this case the TX DMAs are not activated.*/
+ if (n <= SPC5_DSPI_FIFO_DEPTH) {
+ spi_dspi_prefill_txfifo_idle(spip, &n);
+ return;
+ }
+
+ /* Preparing the TX intermediate buffer with the fixed part.*/
+ spip->tx_cmd = spip->config->pushr | (uint32_t)0xFFFF;
+
+ /* The first frame is pushed by the CPU, then the DMA is activated to
+ send the following frames. This should reduce latency on the operation
+ start.*/
+ spip->dspi->PUSHR.R = spip->tx_last = spip->tx_cmd;
+
+ /* Setting up TX1 DMA TCD parameters for 32 bits transfers.*/
+ edmaChannelSetup(spip->tx1_channel, /* channel. */
+ &spip->tx_cmd, /* src. */
+ &spip->dspi->PUSHR.R, /* dst. */
+ 0, /* soff, do not advance. */
+ 0, /* doff, do not advance. */
+ 2, /* ssize, 32 bits transfers.*/
+ 2, /* dsize, 32 bits transfers.*/
+ 4, /* nbytes, always four. */
+ n - 2, /* iter. */
+ 0, /* slast, no source adjust. */
+ 0, /* dlast, no dest.adjust. */
+ EDMA_TCD_MODE_DREQ | EDMA_TCD_MODE_INT_END); /* mode. */
+
+ /* Starting TX1 DMA channel.*/
+ edmaChannelStart(spip->tx1_channel);
+#endif /* SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX */
+}
+
+/**
+ * @brief Starts transmission using DMA for frames up to 8 bits.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] n number of words to be exchanged
+ * @param[in] txbuf the pointer to the transmit buffer
+ *
+ * @notapi
+ */
+static void spi_start_tx8(SPIDriver *spip, size_t n, const uint8_t *txbuf) {
+
+#if SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_RX_AND_TX
+ /* Preloading the TX FIFO with as much frames as possible.*/
+ spi_dspi_prefill_txfifo8(spip, &n, &txbuf);
+
+ /* This is the case where the whole operation can be satisfied using the
+ preloading alone.*/
+ if (n == 0)
+ return;
+
+ /* Setting up the fields required for operation continuation.*/
+ spip->tx_ptr8 = txbuf;
+ spip->tx_cnt = n;
+
+ /* Enabling the TFFF interrupt source for transfer continuation.*/
+ spip->dspi->RSER.B.TFFFRE = 1;
+
+#else /* SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX */
+ /* Special case when the data to be transmitted can entirely fit into the
+ TX FIFO, in this case the TX DMAs are not activated.*/
+ if (n <= SPC5_DSPI_FIFO_DEPTH) {
+ spi_dspi_prefill_txfifo8(spip, &n, &txbuf);
+ return;
+ }
+
+ /* Preparing the TX intermediate buffer with the fixed part.*/
+ spip->tx_cmd = spip->config->pushr;
+
+ /* The first frame is pushed immediately, then the DMA is activated to
+ send the following frames. This should reduce latency on the operation
+ start.*/
+ spip->dspi->PUSHR.R = spip->config->pushr | (uint32_t)*txbuf;
+
+ /* The last frame is a special case, will be pushed by the TX FIFO drain
+ interrupt handler or the DMA final callback.*/
+ spip->tx_last = txbuf[n - 1];
+
+ /* At least two frames left, the DMA is enabled in order to handle the
+ long transfer, note that the final frame is not pushed by the DMA.*/
+ /* Setting up TX1 DMA TCD parameters for 8 bits transfers.*/
+ edmaChannelSetupLinked(
+ spip->tx1_channel, /* channel. */
+ spip->tx2_channel, /* linkch. */
+ txbuf + 1, /* src. */
+ ((const uint8_t *)&spip->tx_cmd) + 3, /* dst. */
+ 1, /* soff, advance by 1. */
+ 0, /* doff, do not advance. */
+ 0, /* ssize, 8 bits transfers. */
+ 0, /* dsize, 8 bits transfers. */
+ 1, /* nbytes, always one. */
+ n - 2, /* iter. */
+ 0, /* slast, no source adjust. */
+ 0, /* dlast, no dest.adjust. */
+ EDMA_TCD_MODE_DREQ); /* mode. */
+
+ /* Setting up TX2 DMA TCD parameters for 32 bits transfers.*/
+ edmaChannelSetup(spip->tx2_channel, /* channel. */
+ &spip->tx_cmd, /* src. */
+ &spip->dspi->PUSHR.R, /* dst. */
+ 0, /* soff, do not advance. */
+ 0, /* doff, do not advance. */
+ 2, /* ssize, 32 bits transfers.*/
+ 2, /* dsize, 32 bits transfers.*/
+ 4, /* nbytes, always four. */
+ n - 2, /* iter. */
+ 0, /* slast, no source adjust. */
+ 0, /* dlast, no dest.adjust. */
+ EDMA_TCD_MODE_DREQ | EDMA_TCD_MODE_INT_END); /* mode. */
+
+ /* Starting TX DMA channels.*/
+ edmaChannelStart(spip->tx2_channel);
+ edmaChannelStart(spip->tx1_channel);
+#endif /* SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX */
+}
+
+/**
+ * @brief Starts transmission using DMA for frames up to 16 bits.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] n number of words to be exchanged
+ * @param[in] txbuf the pointer to the transmit buffer
+ *
+ * @notapi
+ */
+static void spi_start_tx16(SPIDriver *spip, size_t n, const uint16_t *txbuf) {
+
+#if SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_RX_AND_TX
+ /* Preloading the TX FIFO with as much frames as possible.*/
+ spi_dspi_prefill_txfifo16(spip, &n, &txbuf);
+
+ /* This is the case where the whole operation can be satisfied using the
+ preloading alone.*/
+ if (n == 0)
+ return;
+
+ /* Setting up the fields required for operation continuation.*/
+ spip->tx_ptr16 = txbuf;
+ spip->tx_cnt = n;
+
+ /* Enabling the TFFF interrupt source for transfer continuation.*/
+ spip->dspi->RSER.B.TFFFRE = 1;
+
+#else /* SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX */
+ /* Special case when the data to be transmitted can entirely fit into the
+ TX FIFO, in this case the TX DMAs are not activated.*/
+ if (n <= SPC5_DSPI_FIFO_DEPTH) {
+ spi_dspi_prefill_txfifo16(spip, &n, &txbuf);
+ return;
+ }
+
+ /* Preparing the TX intermediate buffer with the fixed part.*/
+ spip->tx_cmd = spip->config->pushr;
+
+ /* The first frame is pushed immediately, then the DMA is activated to
+ send the following frames. This should reduce latency on the operation
+ start.*/
+ spip->dspi->PUSHR.R = spip->config->pushr | (uint32_t)*txbuf;
+
+ /* The last frame is a special case, will be pushed by the TX FIFO drain
+ interrupt handler or the DMA final callback.*/
+ spip->tx_last = txbuf[n - 1];
+
+ /* At least two frames left, the DMA is enabled in order to handle the
+ long transfer, note that the final frame is not pushed by the DMA.*/
+ /* Setting up TX1 DMA TCD parameters for 16 bits transfers.*/
+ edmaChannelSetupLinked(
+ spip->tx1_channel, /* channel. */
+ spip->tx2_channel, /* linkch. */
+ txbuf + 1, /* src. */
+ ((const uint8_t *)&spip->tx_cmd) + 2, /* dst. */
+ 2, /* soff, advance by 2. */
+ 0, /* doff, do not advance. */
+ 1, /* ssize, 16 bits transfers.*/
+ 1, /* dsize, 16 bits transfers.*/
+ 2, /* nbytes, always two. */
+ n - 2, /* iter. */
+ 0, /* slast, no source adjust. */
+ 0, /* dlast, no dest.adjust. */
+ EDMA_TCD_MODE_DREQ); /* mode. */
+
+ /* Setting up TX2 DMA TCD parameters for 32 bits transfers.*/
+ edmaChannelSetup(spip->tx2_channel, /* channel. */
+ &spip->tx_cmd, /* src. */
+ &spip->dspi->PUSHR.R, /* dst. */
+ 0, /* soff, do not advance. */
+ 0, /* doff, do not advance. */
+ 2, /* ssize, 32 bits transfers.*/
+ 2, /* dsize, 32 bits transfers.*/
+ 4, /* nbytes, always four. */
+ n - 2, /* iter. */
+ 0, /* slast, no source adjust. */
+ 0, /* dlast, no dest.adjust. */
+ EDMA_TCD_MODE_DREQ | EDMA_TCD_MODE_INT_END); /* mode. */
+
+ /* Starting TX DMA channels.*/
+ edmaChannelStart(spip->tx2_channel);
+ edmaChannelStart(spip->tx1_channel);
+#endif /* SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX */
+}
+
+#if (SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE) || defined(__DOXYGEN__)
+/**
+ * @brief Shared RX DMA events service routine.
+ *
+ * @param[in] channel the channel number
+ * @param[in] p parameter for the registered function
+ *
+ * @notapi
+ */
+static void spi_serve_rx_dma_irq(edma_channel_t channel, void *p) {
+ SPIDriver *spip = (SPIDriver *)p;
+
+ /* Clearing RX channel state.*/
+ edmaChannelStop(channel);
+
+ /* Stops the transfer.*/
+ spi_dspi_stop(spip);
+
+ /* Portable SPI ISR code defined in the high level driver, note, it is
+ a macro.*/
+ _spi_isr_code(spip);
+}
+#endif /* SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE */
+
+#if (SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX) || defined(__DOXYGEN__)
+/**
+ * @brief Shared TX1/TX2 DMA events service routine.
+ *
+ * @param[in] channel the channel number
+ * @param[in] p parameter for the registered function
+ *
+ * @notapi
+ */
+static void spi_serve_tx_dma_irq(edma_channel_t channel, void *p) {
+ SPIDriver *spip = (SPIDriver *)p;
+
+ (void)channel;
+
+ /* Clearing TX channels state.*/
+ edmaChannelStop(spip->tx1_channel);
+ edmaChannelStop(spip->tx2_channel);
+
+ /* If the TX FIFO is full then the push of the last frame is delegated to
+ an interrupt handler else it is performed immediately. Both conditions
+ can be true depending on the SPI speed and ISR latency.*/
+ if (spip->dspi->SR.B.TFFF) {
+ spip->dspi->PUSHR.R = (spip->config->pushr | spip->tx_last | SPC5_PUSHR_EOQ) &
+ ~SPC5_PUSHR_CONT;
+ }
+ else {
+ spip->dspi->RSER.B.TFFFDIRS = 0;
+ }
+}
+#endif /* SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX */
+
+#if (SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE) || defined(__DOXYGEN__)
+/**
+ * @brief Shared ISR for DMA error events.
+ *
+ * @param[in] channel the channel number
+ * @param[in] p parameter for the registered function
+ * @param[in] esr content of the ESR register
+ *
+ * @notapi
+ */
+static void spi_serve_dma_error_irq(edma_channel_t channel,
+ void *p,
+ uint32_t esr) {
+ SPIDriver *spip = (SPIDriver *)p;
+
+ (void)channel;
+ (void)esr;
+
+ /* Stops the transfer.*/
+ spi_dspi_stop(spip);
+
+#if SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX
+ edmaChannelStop(spip->tx1_channel);
+ edmaChannelStop(spip->tx2_channel);
+#endif
+ edmaChannelStop(spip->rx_channel);
+
+ SPC5_SPI_DMA_ERROR_HOOK(spip);
+}
+#endif /* SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE */
+
+#if (SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_NONE) || defined(__DOXYGEN__)
+/**
+ * @brief Shared ISR for RFDF DSPI events.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ *
+ * @notapi
+ */
+void spi_serve_dspi_rfdf(SPIDriver *spip) {
+
+ osalSysLockFromISR();
+
+ /* Emptying the RX FIFO.*/
+ while ((spip->rx_cnt > 0) && (spip->dspi->SR.B.RXCTR > 0)) {
+ uint32_t frame = spip->dspi->POPR.R;
+ if (spip->rx_ptr != NULL) {
+ if (spip->dspi->CTAR[0].B.FMSZ < 8)
+ *spip->rx_ptr8++ = (uint8_t)frame;
+ else
+ *spip->rx_ptr16++ = (uint16_t)frame;
+ }
+ spip->rx_cnt--;
+ }
+
+ /* Interrupt served.*/
+ spip->dspi->SR.B.RFDF = 1;
+
+ if (spip->rx_cnt == 0) {
+ /* Stops the transfer.*/
+ spi_dspi_stop(spip);
+
+ /* Portable SPI ISR code defined in the high level driver, note, it is
+ a macro.*/
+ _spi_isr_code(spip);
+ }
+ else {
+ if (spip->tx_cnt > 0) {
+ /* Filling the TX FIFO.*/
+ if (spip->tx_ptr == NULL)
+ spi_dspi_prefill_txfifo_idle(spip, &spip->tx_cnt);
+ else {
+ if (spip->dspi->CTAR[0].B.FMSZ < 8)
+ spi_dspi_prefill_txfifo8(spip, &spip->tx_cnt, &spip->tx_ptr8);
+ else
+ spi_dspi_prefill_txfifo16(spip, &spip->tx_cnt, &spip->tx_ptr16);
+ }
+ }
+ }
+
+ osalSysUnlockFromISR();
+}
+#endif /* SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE */
+
+#if (SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE) || defined(__DOXYGEN__)
+/**
+ * @brief Shared ISR for TFFF DSPI events.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ *
+ * @notapi
+ */
+void spi_serve_dspi_tfff(SPIDriver *spip) {
+
+ osalSysLockFromISR();
+
+#if SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX
+ /* Interrupt served and back to DMA mode.*/
+ spip->dspi->RSER.B.TFFFDIRS = 1;
+ spip->dspi->SR.B.TFFF = 1;
+
+ /* Pushing last frame.*/
+ spip->dspi->PUSHR.R = (spip->config->pushr | spip->tx_last | SPC5_PUSHR_EOQ) &
+ ~SPC5_PUSHR_CONT;
+#endif /* SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX */
+
+#if SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_ONLY
+ /* Pushing some more frames in the TX FIFO.*/
+ if (spip->tx_ptr == NULL)
+ spi_dspi_prefill_txfifo_idle(spip, &spip->tx_cnt);
+ else {
+ if (spip->dspi->CTAR[0].B.FMSZ < 8)
+ spi_dspi_prefill_txfifo8(spip, &spip->tx_cnt, &spip->tx_ptr8);
+ else
+ spi_dspi_prefill_txfifo16(spip, &spip->tx_cnt, &spip->tx_ptr16);
+ }
+
+ /* Interrupt served.*/
+ spip->dspi->SR.B.TFFF = 1;
+
+ /* If there are no more frames to be pushed then the TFFF interrupt source
+ is disabled.*/
+ if (spip->tx_cnt == 0)
+ spip->dspi->RSER.B.TFFFRE = 0;
+#endif /* SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_ONLY */
+
+ osalSysUnlockFromISR();
+}
+#endif /* SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE */
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+#if (SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_NONE) || defined(__DOXYGEN__)
+#if SPC5_SPI_USE_DSPI0 || defined(__DOXYGEN__)
+#if !defined(SPC5_DSPI0_RFDF_HANDLER)
+#error "SPC5_DSPI0_RFDF_HANDLER not defined"
+#endif
+/**
+ * @brief DSPI0 RFDF interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SPC5_DSPI0_RFDF_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ spi_serve_dspi_rfdf(&SPID1);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* SPC5_SPI_USE_DSPI0 */
+
+#if SPC5_SPI_USE_DSPI1 || defined(__DOXYGEN__)
+#if !defined(SPC5_DSPI1_RFDF_HANDLER)
+#error "SPC5_DSPI1_RFDF_HANDLER not defined"
+#endif
+/**
+ * @brief DSPI1 RFDF interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SPC5_DSPI1_RFDF_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ spi_serve_dspi_rfdf(&SPID2);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* SPC5_SPI_USE_DSPI1 */
+
+#if SPC5_SPI_USE_DSPI2 || defined(__DOXYGEN__)
+#if !defined(SPC5_DSPI2_RFDF_HANDLER)
+#error "SPC5_DSPI2_RFDF_HANDLER not defined"
+#endif
+/**
+ * @brief DSPI2 RFDF interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SPC5_DSPI2_RFDF_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ spi_serve_dspi_rfdf(&SPID3);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* SPC5_SPI_USE_DSPI2 */
+
+#if SPC5_SPI_USE_DSPI3 || defined(__DOXYGEN__)
+#if !defined(SPC5_DSPI3_RFDF_HANDLER)
+#error "SPC5_DSPI3_RFDF_HANDLER not defined"
+#endif
+/**
+ * @brief DSPI3 RFDF interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SPC5_DSPI3_RFDF_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ spi_serve_dspi_rfdf(&SPID4);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* SPC5_SPI_USE_DSPI3 */
+
+#if SPC5_SPI_USE_DSPI4 || defined(__DOXYGEN__)
+#if !defined(SPC5_DSPI4_RFDF_HANDLER)
+#error "SPC5_DSPI4_RFDF_HANDLER not defined"
+#endif
+/**
+ * @brief DSPI4 RFDF interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SPC5_DSPI4_RFDF_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ spi_serve_dspi_rfdf(&SPID5);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* SPC5_SPI_USE_DSPI4 */
+
+#if SPC5_SPI_USE_DSPI5 || defined(__DOXYGEN__)
+#if !defined(SPC5_DSPI5_RFDF_HANDLER)
+#error "SPC5_DSPI5_RFDF_HANDLER not defined"
+#endif
+/**
+ * @brief DSPI5 RFDF interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SPC5_DSPI5_RFDF_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ spi_serve_dspi_rfdf(&SPID6);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* SPC5_SPI_USE_DSPI5 */
+
+#if SPC5_SPI_USE_DSPI6 || defined(__DOXYGEN__)
+#if !defined(SPC5_DSPI6_RFDF_HANDLER)
+#error "SPC5_DSPI6_RFDF_HANDLER not defined"
+#endif
+/**
+ * @brief DSPI6 RFDF interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SPC5_DSPI6_RFDF_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ spi_serve_dspi_rfdf(&SPID7);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* SPC5_SPI_USE_DSPI6 */
+
+#if SPC5_SPI_USE_DSPI7 || defined(__DOXYGEN__)
+#if !defined(SPC5_DSPI7_RFDF_HANDLER)
+#error "SPC5_DSPI7_RFDF_HANDLER not defined"
+#endif
+/**
+ * @brief DSPI7 RFDF interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SPC5_DSPI7_RFDF_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ spi_serve_dspi_rfdf(&SPID8);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* SPC5_SPI_USE_DSPI7 */
+#endif /* SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_NONE */
+
+#if (SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE) || defined(__DOXYGEN__)
+#if SPC5_SPI_USE_DSPI0 || defined(__DOXYGEN__)
+#if !defined(SPC5_DSPI0_TFFF_HANDLER)
+#error "SPC5_DSPI0_TFFF_HANDLER not defined"
+#endif
+/**
+ * @brief DSPI0 TFFF interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SPC5_DSPI0_TFFF_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ spi_serve_dspi_tfff(&SPID1);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* SPC5_SPI_USE_DSPI0 */
+
+#if SPC5_SPI_USE_DSPI1 || defined(__DOXYGEN__)
+#if !defined(SPC5_DSPI1_TFFF_HANDLER)
+#error "SPC5_DSPI1_TFFF_HANDLER not defined"
+#endif
+/**
+ * @brief DSPI1 TFFF interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SPC5_DSPI1_TFFF_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ spi_serve_dspi_tfff(&SPID2);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* SPC5_SPI_USE_DSPI1 */
+
+#if SPC5_SPI_USE_DSPI2 || defined(__DOXYGEN__)
+#if !defined(SPC5_DSPI2_TFFF_HANDLER)
+#error "SPC5_DSPI2_TFFF_HANDLER not defined"
+#endif
+/**
+ * @brief DSPI2 TFFF interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SPC5_DSPI2_TFFF_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ spi_serve_dspi_tfff(&SPID3);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* SPC5_SPI_USE_DSPI2 */
+
+#if SPC5_SPI_USE_DSPI3 || defined(__DOXYGEN__)
+#if !defined(SPC5_DSPI3_TFFF_HANDLER)
+#error "SPC5_DSPI3_TFFF_HANDLER not defined"
+#endif
+/**
+ * @brief DSPI3 TFFF interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SPC5_DSPI3_TFFF_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ spi_serve_dspi_tfff(&SPID4);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* SPC5_SPI_USE_DSPI3 */
+
+#if SPC5_SPI_USE_DSPI4 || defined(__DOXYGEN__)
+#if !defined(SPC5_DSPI4_TFFF_HANDLER)
+#error "SPC5_DSPI4_TFFF_HANDLER not defined"
+#endif
+/**
+ * @brief DSPI4 TFFF interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SPC5_DSPI4_TFFF_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ spi_serve_dspi_tfff(&SPID5);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* SPC5_SPI_USE_DSPI4 */
+
+#if SPC5_SPI_USE_DSPI5 || defined(__DOXYGEN__)
+#if !defined(SPC5_DSPI5_TFFF_HANDLER)
+#error "SPC5_DSPI5_TFFF_HANDLER not defined"
+#endif
+/**
+ * @brief DSPI5 TFFF interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SPC5_DSPI5_TFFF_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ spi_serve_dspi_tfff(&SPID6);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* SPC5_SPI_USE_DSPI5 */
+
+#if SPC5_SPI_USE_DSPI6 || defined(__DOXYGEN__)
+#if !defined(SPC5_DSPI6_TFFF_HANDLER)
+#error "SPC5_DSPI6_TFFF_HANDLER not defined"
+#endif
+/**
+ * @brief DSPI6 TFFF interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SPC5_DSPI6_TFFF_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ spi_serve_dspi_tfff(&SPID7);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* SPC5_SPI_USE_DSPI6 */
+
+#if SPC5_SPI_USE_DSPI7 || defined(__DOXYGEN__)
+#if !defined(SPC5_DSPI7_TFFF_HANDLER)
+#error "SPC5_DSPI7_TFFF_HANDLER not defined"
+#endif
+/**
+ * @brief DSPI7 TFFF interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SPC5_DSPI7_TFFF_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ spi_serve_dspi_tfff(&SPID8);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* SPC5_SPI_USE_DSPI7 */
+#endif /* SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE */
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Low level SPI driver initialization.
+ *
+ * @notapi
+ */
+void spi_lld_init(void) {
+
+#if SPC5_SPI_USE_DSPI0
+ /* Driver initialization.*/
+ SPC5_DSPI0_ENABLE_CLOCK();
+ spiObjectInit(&SPID1);
+ spi_lld_obj_init(&SPID1, &SPC5_DSPI0);
+ SPC5_DSPI0.MCR.R = SPC5_MCR_MSTR | SPC5_MCR_HALT | SPC5_MCR_MDIS |
+ SPC5_SPI_DSPI0_MCR;
+#if SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_NONE
+ INTC.PSR[SPC5_DSPI0_RFDF_NUMBER].R = SPC5_SPI_DSPI0_IRQ_PRIO;
+#else
+ INTC.PSR[SPC5_DSPI0_TFFF_NUMBER].R = SPC5_SPI_DSPI0_IRQ_PRIO;
+#endif
+#endif /* SPC5_SPI_USE_DSPI0 */
+
+#if SPC5_SPI_USE_DSPI1
+ /* Driver initialization.*/
+ SPC5_DSPI1_ENABLE_CLOCK();
+ spiObjectInit(&SPID2);
+ spi_lld_obj_init(&SPID2, &SPC5_DSPI1);
+ SPC5_DSPI1.MCR.R = SPC5_MCR_MSTR | SPC5_MCR_HALT | SPC5_MCR_MDIS |
+ SPC5_SPI_DSPI1_MCR;
+#if SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_NONE
+ INTC.PSR[SPC5_DSPI1_RFDF_NUMBER].R = SPC5_SPI_DSPI1_IRQ_PRIO;
+#else
+ INTC.PSR[SPC5_DSPI1_TFFF_NUMBER].R = SPC5_SPI_DSPI1_IRQ_PRIO;
+#endif
+#endif /* SPC5_SPI_USE_DSPI1 */
+
+#if SPC5_SPI_USE_DSPI2
+ /* Driver initialization.*/
+ SPC5_DSPI2_ENABLE_CLOCK();
+ spiObjectInit(&SPID3);
+ spi_lld_obj_init(&SPID3, &SPC5_DSPI2);
+ SPC5_DSPI2.MCR.R = SPC5_MCR_MSTR | SPC5_MCR_HALT | SPC5_MCR_MDIS |
+ SPC5_SPI_DSPI2_MCR;
+#if SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_NONE
+ INTC.PSR[SPC5_DSPI2_RFDF_NUMBER].R = SPC5_SPI_DSPI2_IRQ_PRIO;
+#else
+ INTC.PSR[SPC5_DSPI2_TFFF_NUMBER].R = SPC5_SPI_DSPI2_IRQ_PRIO;
+#endif
+#endif /* SPC5_SPI_USE_DSPI2 */
+
+#if SPC5_SPI_USE_DSPI3
+ /* Driver initialization.*/
+ SPC5_DSPI3_ENABLE_CLOCK();
+ spiObjectInit(&SPID4);
+ spi_lld_obj_init(&SPID4, &SPC5_DSPI3);
+ SPC5_DSPI3.MCR.R = SPC5_MCR_MSTR | SPC5_MCR_HALT | SPC5_MCR_MDIS |
+ SPC5_SPI_DSPI3_MCR;
+#if SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_NONE
+ INTC.PSR[SPC5_DSPI3_RFDF_NUMBER].R = SPC5_SPI_DSPI3_IRQ_PRIO;
+#else
+ INTC.PSR[SPC5_DSPI3_TFFF_NUMBER].R = SPC5_SPI_DSPI3_IRQ_PRIO;
+#endif
+#endif /* SPC5_SPI_USE_DSPI3 */
+
+#if SPC5_SPI_USE_DSPI4
+ /* Driver initialization.*/
+ SPC5_DSPI4_ENABLE_CLOCK();
+ spiObjectInit(&SPID5);
+ spi_lld_obj_init(&SPID5, &SPC5_DSPI4);
+ SPC5_DSPI4.MCR.R = SPC5_MCR_MSTR | SPC5_MCR_HALT | SPC5_MCR_MDIS |
+ SPC5_SPI_DSPI4_MCR;
+#if SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_NONE
+ INTC.PSR[SPC5_DSPI4_RFDF_NUMBER].R = SPC5_SPI_DSPI4_IRQ_PRIO;
+#else
+ INTC.PSR[SPC5_DSPI4_TFFF_NUMBER].R = SPC5_SPI_DSPI4_IRQ_PRIO;
+#endif
+#endif /* SPC5_SPI_USE_DSPI4 */
+
+#if SPC5_SPI_USE_DSPI5
+ /* Driver initialization.*/
+ SPC5_DSPI5_ENABLE_CLOCK();
+ spiObjectInit(&SPID6);
+ spi_lld_obj_init(&SPID6, &SPC5_DSPI5);
+ SPC5_DSPI5.MCR.R = SPC5_MCR_MSTR | SPC5_MCR_HALT | SPC5_MCR_MDIS |
+ SPC5_SPI_DSPI5_MCR;
+#if SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_NONE
+ INTC.PSR[SPC5_DSPI5_RFDF_NUMBER].R = SPC5_SPI_DSPI5_IRQ_PRIO;
+#else
+ INTC.PSR[SPC5_DSPI5_TFFF_NUMBER].R = SPC5_SPI_DSPI5_IRQ_PRIO;
+#endif
+#endif /* SPC5_SPI_USE_DSPI5 */
+
+#if SPC5_SPI_USE_DSPI6
+ /* Driver initialization.*/
+ SPC5_DSPI6_ENABLE_CLOCK();
+ spiObjectInit(&SPID7);
+ spi_lld_obj_init(&SPID7, &SPC5_DSPI6);
+ SPC5_DSPI6.MCR.R = SPC5_MCR_MSTR | SPC5_MCR_HALT | SPC5_MCR_MDIS |
+ SPC5_SPI_DSPI6_MCR;
+#if SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_NONE
+ INTC.PSR[SPC5_DSPI6_RFDF_NUMBER].R = SPC5_SPI_DSPI6_IRQ_PRIO;
+#else
+ INTC.PSR[SPC5_DSPI6_TFFF_NUMBER].R = SPC5_SPI_DSPI6_IRQ_PRIO;
+#endif
+#endif /* SPC5_SPI_USE_DSPI6 */
+
+#if SPC5_SPI_USE_DSPI7
+ /* Driver initialization.*/
+ SPC5_DSPI7_ENABLE_CLOCK();
+ spiObjectInit(&SPID8);
+ spi_lld_obj_init(&SPID8, &SPC5_DSPI7);
+ SPC5_DSPI7.MCR.R = SPC5_MCR_MSTR | SPC5_MCR_HALT | SPC5_MCR_MDIS |
+ SPC5_SPI_DSPI7_MCR;
+#if SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_NONE
+ INTC.PSR[SPC5_DSPI7_RFDF_NUMBER].R = SPC5_SPI_DSPI7_IRQ_PRIO;
+#else
+ INTC.PSR[SPC5_DSPI7_TFFF_NUMBER].R = SPC5_SPI_DSPI7_IRQ_PRIO;
+#endif
+#endif /* SPC5_SPI_USE_DSPI7 */
+}
+
+/**
+ * @brief Configures and activates the SPI peripheral.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ *
+ * @notapi
+ */
+void spi_lld_start(SPIDriver *spip) {
+
+ osalDbgAssert((spip->config->pushr & DSPI_PUSHR_EXCLUDED_BITS) == 0,
+ "invalid PUSHR bits specified");
+
+ if (spip->state == SPI_STOP) {
+ /* Enables the peripheral.*/
+
+#if SPC5_SPI_USE_DSPI0
+ if (&SPID1 == spip) {
+ spi_lld_setdma(spip,
+ spi_dspi0_tx1_dma_config,
+ spi_dspi0_tx2_dma_config,
+ spi_dspi0_rx_dma_config)
+ }
+#endif /* SPC5_SPI_USE_DSPI0 */
+
+#if SPC5_SPI_USE_DSPI1
+ if (&SPID2 == spip) {
+ spi_lld_setdma(spip,
+ spi_dspi1_tx1_dma_config,
+ spi_dspi1_tx2_dma_config,
+ spi_dspi1_rx_dma_config)
+ }
+#endif /* SPC5_SPI_USE_DSPI1 */
+
+#if SPC5_SPI_USE_DSPI2
+ if (&SPID3 == spip) {
+ spi_lld_setdma(spip,
+ spi_dspi2_tx1_dma_config,
+ spi_dspi2_tx2_dma_config,
+ spi_dspi2_rx_dma_config)
+ }
+#endif /* SPC5_SPI_USE_DSPI2 */
+
+#if SPC5_SPI_USE_DSPI3
+ if (&SPID4 == spip) {
+ spi_lld_setdma(spip,
+ spi_dspi3_tx1_dma_config,
+ spi_dspi3_tx2_dma_config,
+ spi_dspi3_rx_dma_config)
+ }
+#endif /* SPC5_SPI_USE_DSPI3 */
+
+#if SPC5_SPI_USE_DSPI4
+ if (&SPID5 == spip) {
+ spi_lld_setdma(spip,
+ spi_dspi4_tx1_dma_config,
+ spi_dspi4_tx2_dma_config,
+ spi_dspi4_rx_dma_config)
+ }
+#endif /* SPC5_SPI_USE_DSPI4 */
+
+#if SPC5_SPI_USE_DSPI5
+ if (&SPID6 == spip) {
+ spi_lld_setdma(spip,
+ spi_dspi5_tx1_dma_config,
+ spi_dspi5_tx2_dma_config,
+ spi_dspi5_rx_dma_config)
+ }
+#endif /* SPC5_SPI_USE_DSPI5 */
+
+#if SPC5_SPI_USE_DSPI6
+ if (&SPID7 == spip) {
+ spi_lld_setdma(spip,
+ spi_dspi6_tx1_dma_config,
+ spi_dspi6_tx2_dma_config,
+ spi_dspi6_rx_dma_config)
+ }
+#endif /* SPC5_SPI_USE_DSPI6 */
+
+#if SPC5_SPI_USE_DSPI7
+ if (&SPID8 == spip) {
+ spi_lld_setdma(spip,
+ spi_dspi7_tx1_dma_config,
+ spi_dspi7_tx2_dma_config,
+ spi_dspi7_rx_dma_config)
+ }
+#endif /* SPC5_SPI_USE_DSPI7 */
+
+#if SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX
+ osalDbgAssert((spip->tx1_channel != EDMA_ERROR) &&
+ (spip->tx2_channel != EDMA_ERROR),
+ "TX DMA channels cannot be allocated");
+#endif
+#if SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE
+ osalDbgAssert(spip->rx_channel != EDMA_ERROR,
+ "RX DMA channels cannot be allocated");
+#endif
+ }
+
+ /* Configures the peripheral, the RSER register setting depend on the
+ chosen DMA use mode.*/
+ spip->dspi->MCR.B.MDIS = 0;
+ spip->dspi->CTAR[0].R = spip->config->ctar0;
+#if SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_NONE
+ spip->dspi->RSER.R = SPC5_RSER_RFDF_RE;
+#endif
+#if SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_ONLY
+ spip->dspi->RSER.R = SPC5_RSER_RFDF_RE | SPC5_RSER_RFDF_DIRS;
+#endif
+#if SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX
+ spip->dspi->RSER.R = SPC5_RSER_TFFF_RE | SPC5_RSER_TFFF_DIRS |
+ SPC5_RSER_RFDF_RE | SPC5_RSER_RFDF_DIRS;
+#endif
+ spip->dspi->SR.R = spip->dspi->SR.R;
+}
+
+/**
+ * @brief Deactivates the SPI peripheral.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ *
+ * @notapi
+ */
+void spi_lld_stop(SPIDriver *spip) {
+
+ if (spip->state == SPI_READY) {
+ /* Releases the allocated EDMA channels.*/
+#if SPC5_SPI_DMA_MODE == SPC5_SPI_DMA_RX_AND_TX
+ edmaChannelRelease(spip->tx1_channel);
+ edmaChannelRelease(spip->tx2_channel);
+#endif
+#if SPC5_SPI_DMA_MODE != SPC5_SPI_DMA_NONE
+ edmaChannelRelease(spip->rx_channel);
+#endif
+
+ /* Resets the peripheral.*/
+ spip->dspi->CTAR[0].R = 0;
+ spip->dspi->RSER.R = 0;
+ spip->dspi->SR.R = spip->dspi->SR.R;
+ spip->dspi->MCR.R |= SPC5_MCR_HALT |
+ SPC5_MCR_CLR_TXF | SPC5_MCR_CLR_RXF;
+ spip->dspi->MCR.B.MDIS = 1;
+ }
+}
+
+/**
+ * @brief Asserts the slave select signal and prepares for transfers.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ *
+ * @notapi
+ */
+void spi_lld_select(SPIDriver *spip) {
+
+ palClearPad(spip->config->ssport, spip->config->sspad);
+}
+
+/**
+ * @brief Deasserts the slave select signal.
+ * @details The previously selected peripheral is unselected.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ *
+ * @notapi
+ */
+void spi_lld_unselect(SPIDriver *spip) {
+
+ palSetPad(spip->config->ssport, spip->config->sspad);
+}
+
+/**
+ * @brief Ignores data on the SPI bus.
+ * @details This asynchronous function starts the transmission of a series of
+ * idle words on the SPI bus and ignores the received data.
+ * @post At the end of the operation the configured callback is invoked.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] n number of words to be ignored
+ *
+ * @notapi
+ */
+void spi_lld_ignore(SPIDriver *spip, size_t n) {
+
+ /* Starting transfer.*/
+ spi_dspi_start(spip);
+
+ /* Setting up the DMA channels.*/
+ spi_start_rx_ignore(spip, n);
+ spi_start_tx_ignore(spip, n);
+}
+
+/**
+ * @brief Exchanges data on the SPI bus.
+ * @details This asynchronous function starts a simultaneous transmit/receive
+ * operation.
+ * @post At the end of the operation the configured callback is invoked.
+ * @note The buffers are organized as uint8_t arrays for data sizes below or
+ * equal to 8 bits else it is organized as uint16_t arrays.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] n number of words to be exchanged
+ * @param[in] txbuf the pointer to the transmit buffer
+ * @param[out] rxbuf the pointer to the receive buffer
+ *
+ * @notapi
+ */
+void spi_lld_exchange(SPIDriver *spip, size_t n,
+ const void *txbuf, void *rxbuf) {
+
+ /* Starting transfer.*/
+ spi_dspi_start(spip);
+
+ /* DMAs require a different setup depending on the frame size.*/
+ if (spip->dspi->CTAR[0].B.FMSZ < 8) {
+ spi_start_rx8(spip, n, rxbuf);
+ spi_start_tx8(spip, n, txbuf);
+ }
+ else {
+ spi_start_rx16(spip, n, rxbuf);
+ spi_start_tx16(spip, n, txbuf);
+ }
+}
+
+/**
+ * @brief Sends data over the SPI bus.
+ * @details This asynchronous function starts a transmit operation.
+ * @post At the end of the operation the configured callback is invoked.
+ * @note The buffers are organized as uint8_t arrays for data sizes below or
+ * equal to 8 bits else it is organized as uint16_t arrays.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] n number of words to send
+ * @param[in] txbuf the pointer to the transmit buffer
+ *
+ * @notapi
+ */
+void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf) {
+
+ /* Starting transfer.*/
+ spi_dspi_start(spip);
+
+ /* Setting up the RX DMA channel.*/
+ spi_start_rx_ignore(spip, n);
+
+ /* DMAs require a different setup depending on the frame size.*/
+ if (spip->dspi->CTAR[0].B.FMSZ < 8)
+ spi_start_tx8(spip, n, txbuf);
+ else
+ spi_start_tx16(spip, n, txbuf);
+}
+
+/**
+ * @brief Receives data from the SPI bus.
+ * @details This asynchronous function starts a receive operation.
+ * @post At the end of the operation the configured callback is invoked.
+ * @note The buffers are organized as uint8_t arrays for data sizes below or
+ * equal to 8 bits else it is organized as uint16_t arrays.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] n number of words to receive
+ * @param[out] rxbuf the pointer to the receive buffer
+ *
+ * @notapi
+ */
+void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf) {
+
+ /* Starting transfer.*/
+ spi_dspi_start(spip);
+
+ /* DMAs require a different setup depending on the frame size.*/
+ if (spip->dspi->CTAR[0].B.FMSZ < 8)
+ spi_start_rx8(spip, n, rxbuf);
+ else
+ spi_start_rx16(spip, n, rxbuf);
+
+ spi_start_tx_ignore(spip, n);
+}
+
+/**
+ * @brief Exchanges one frame using a polled wait.
+ * @details This synchronous function exchanges one frame using a polled
+ * synchronization method. This function is useful when exchanging
+ * small amount of data on high speed channels, usually in this
+ * situation is much more efficient just wait for completion using
+ * polling than suspending the thread waiting for an interrupt.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] frame the data frame to send over the SPI bus
+ * @return The received data frame from the SPI bus.
+ */
+uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame) {
+ uint32_t popr;
+
+ /* Starting transfer.*/
+ spi_dspi_start(spip);
+
+ /* Data exchange.*/
+ spip->dspi->PUSHR.R = (SPC5_PUSHR_EOQ | spip->config->pushr |
+ (uint32_t)frame) & ~SPC5_PUSHR_CONT;
+ while (!spip->dspi->SR.B.RFDF)
+ ;
+ popr = spip->dspi->POPR.R;
+
+ /* Stopping transfer.*/
+ spi_dspi_stop(spip);
+
+ return (uint16_t)popr;
+}
+
+#endif /* HAL_USE_SPI */
+
+/** @} */