From e9ffaf9bdc231c69adffd5486c2949f21390e6e1 Mon Sep 17 00:00:00 2001 From: Giovanni Di Sirio Date: Fri, 22 Sep 2017 12:43:39 +0000 Subject: Forked SPC5 drivers from SPC5-HAL project, not all of them, just the one needed for supporting the PPC port. git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@10679 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/hal/ports/SPC5/LLD/DSPI_v1/hal_spi_lld.c | 1870 +++++++++++++++++++++++++++ 1 file changed, 1870 insertions(+) create mode 100644 os/hal/ports/SPC5/LLD/DSPI_v1/hal_spi_lld.c (limited to 'os/hal/ports/SPC5/LLD/DSPI_v1/hal_spi_lld.c') 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 */ + +/** @} */ -- cgit v1.2.3