tinySA/os/hal/platforms/SPC5xx/DSPI_v1/spi_lld.c

1219 lines
40 KiB
C

/*
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/spi_lld.c
* @brief SPC5xx SPI subsystem low level driver source.
*
* @addtogroup SPI
* @{
*/
#include "ch.h"
#include "hal.h"
#if HAL_USE_SPI || defined(__DOXYGEN__)
/* Some forward declarations.*/
static void spi_serve_rx_irq(edma_channel_t channel, void *p);
static void spi_serve_tx_irq(edma_channel_t channel, void *p);
static void spi_serve_dma_error_irq(edma_channel_t channel,
void *p,
uint32_t esr);
/*===========================================================================*/
/* 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)
/*===========================================================================*/
/* 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
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
#if SPC5_SPI_USE_DSPI0 || defined(__DOXYGEN__)
/**
* @brief DMA configuration for DSPI0 TX1.
*/
static const edma_channel_config_t spi_dspi0_tx1_dma_config = {
SPC5_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_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_DSPI0_TX2_DMA_CH_ID,
#if SPC5_EDMA_HAS_MUX
0,
#endif
SPC5_SPI_DSPI0_DMA_IRQ_PRIO,
spi_serve_tx_irq, spi_serve_dma_error_irq, &SPID1
};
/**
* @brief DMA configuration for DSPI0 RX.
*/
static const edma_channel_config_t spi_dspi0_rx_dma_config = {
SPC5_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_irq, spi_serve_dma_error_irq, &SPID1
};
#endif /* SPC5_SPI_USE_DSPI0 */
#if SPC5_SPI_USE_DSPI1 || defined(__DOXYGEN__)
/**
* @brief DMA configuration for DSPI1 TX1.
*/
static const edma_channel_config_t spi_dspi1_tx1_dma_config = {
SPC5_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_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_DSPI1_TX2_DMA_CH_ID,
#if SPC5_EDMA_HAS_MUX
0,
#endif
SPC5_SPI_DSPI1_DMA_IRQ_PRIO,
spi_serve_tx_irq, spi_serve_dma_error_irq, &SPID2
};
/**
* @brief DMA configuration for DSPI1 RX.
*/
static const edma_channel_config_t spi_dspi1_rx_dma_config = {
SPC5_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_irq, spi_serve_dma_error_irq, &SPID2
};
#endif /* SPC5_SPI_USE_DSPI1 */
#if SPC5_SPI_USE_DSPI2 || defined(__DOXYGEN__)
/**
* @brief DMA configuration for DSPI2 TX1.
*/
static const edma_channel_config_t spi_dspi2_tx1_dma_config = {
SPC5_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_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_DSPI2_TX2_DMA_CH_ID,
#if SPC5_EDMA_HAS_MUX
0,
#endif
SPC5_SPI_DSPI2_DMA_IRQ_PRIO,
spi_serve_tx_irq, spi_serve_dma_error_irq, &SPID3
};
/**
* @brief DMA configuration for DSPI2 RX.
*/
static const edma_channel_config_t spi_dspi2_rx_dma_config = {
SPC5_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_irq, spi_serve_dma_error_irq, &SPID3
};
#endif /* SPC5_SPI_USE_DSPI2 */
#if SPC5_SPI_USE_DSPI3 || defined(__DOXYGEN__)
/**
* @brief DMA configuration for DSPI3 TX1.
*/
static const edma_channel_config_t spi_dspi3_tx1_dma_config = {
SPC5_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_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_DSPI3_TX2_DMA_CH_ID,
#if SPC5_EDMA_HAS_MUX
0,
#endif
SPC5_SPI_DSPI3_DMA_IRQ_PRIO,
spi_serve_tx_irq, spi_serve_dma_error_irq, &SPID4
};
/**
* @brief DMA configuration for DSPI3 RX.
*/
static const edma_channel_config_t spi_dspi3_rx_dma_config = {
SPC5_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_irq, spi_serve_dma_error_irq, &SPID4
};
#endif /* SPC5_SPI_USE_DSPI3 */
#if SPC5_SPI_USE_DSPI4 || defined(__DOXYGEN__)
/**
* @brief DMA configuration for DSPI4 TX1.
*/
static const edma_channel_config_t spi_dspi4_tx1_dma_config = {
SPC5_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_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_DSPI4_TX2_DMA_CH_ID,
#if SPC5_EDMA_HAS_MUX
0,
#endif
SPC5_SPI_DSPI4_DMA_IRQ_PRIO,
spi_serve_tx_irq, spi_serve_dma_error_irq, &SPID5
};
/**
* @brief DMA configuration for DSPI4 RX.
*/
static const edma_channel_config_t spi_dspi4_rx_dma_config = {
SPC5_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_irq, spi_serve_dma_error_irq, &SPID5
};
#endif /* SPC5_SPI_USE_DSPI4 */
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/**
* @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_dma_rx_ignore(SPIDriver *spip, size_t n) {
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, 16 bits transfers.*/
0, /* dsize, 16 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);
}
/**
* @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_dma_rx8(SPIDriver *spip,
size_t n,
uint8_t *rxbuf) {
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);
}
/**
* @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_dma_rx16(SPIDriver *spip,
size_t n,
uint16_t *rxbuf) {
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);
}
/**
* @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_dma_tx_ignore(SPIDriver *spip, size_t n) {
/* Preparing the TX intermediate buffer with the fixed part.*/
spip->tx_intbuf = 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_intbuf;
/* Setting up TX1 DMA TCD parameters for 32 bits transfers.*/
edmaChannelSetup(spip->tx1_channel, /* channel. */
&spip->tx_intbuf, /* 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);
}
/**
* @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_dma_tx8(SPIDriver *spip,
size_t n,
const uint8_t *txbuf) {
/* Preparing the TX intermediate buffer with the fixed part.*/
spip->tx_intbuf = spip->config->pushr;
/* 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_intbuf | (uint32_t)*txbuf;
/* 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_intbuf) + 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_intbuf, /* 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. */
/* The last frame will be pushed by the TX DMA operation completion
callback.*/
spip->tx_last = txbuf[n - 1];
/* Starting TX DMA channels.*/
edmaChannelStart(spip->tx2_channel);
edmaChannelStart(spip->tx1_channel);
}
/**
* @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_dma_tx16(SPIDriver *spip,
size_t n,
const uint16_t *txbuf) {
/* Preparing the TX intermediate buffer with the fixed part.*/
spip->tx_intbuf = spip->config->pushr;
/* 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_intbuf | (uint32_t)*txbuf;
/* 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_intbuf) + 2, /* dst. */
1, /* soff, advance by 1. */
0, /* doff, do not advance. */
1, /* ssize, 16 bits transfers.*/
1, /* dsize, 16 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_intbuf, /* 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. */
/* The last frame will be pushed by the TX DMA operation completion
callback.*/
spip->tx_last = txbuf[n - 1];
/* Starting TX DMA channels.*/
edmaChannelStart(spip->tx2_channel);
edmaChannelStart(spip->tx1_channel);
}
/**
* @brief Starts idle bits using FIFO pre-filling.
*
* @param[in] spip pointer to the @p SPIDriver object
* @param[in] n number of words to be exchanged
*
* @notapi
*/
static void spi_tx_prefill_ignore(SPIDriver *spip, size_t n) {
uint32_t cmd = spip->config->pushr;
do {
if (--n == 0) {
spip->dspi->PUSHR.R = (SPC5_PUSHR_EOQ | cmd | (uint32_t)0xFFFF) &
~SPC5_PUSHR_CONT;
break;
}
spip->dspi->PUSHR.R = cmd | (uint32_t)0xFFFF;
} while (TRUE);
}
/**
* @brief Starts transmission using FIFO pre-filling 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_tx_prefill8(SPIDriver *spip,
size_t n,
const uint8_t *txbuf) {
uint32_t cmd = spip->config->pushr;
do {
if (--n == 0) {
spip->dspi->PUSHR.R = (SPC5_PUSHR_EOQ | cmd | (uint32_t)*txbuf) &
~SPC5_PUSHR_CONT;
break;
}
spip->dspi->PUSHR.R = cmd | (uint32_t)*txbuf;
txbuf++;
} while (TRUE);
}
/**
* @brief Starts transmission using FIFO pre-filling 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_tx_prefill16(SPIDriver *spip,
size_t n,
const uint16_t *txbuf) {
uint32_t cmd = spip->config->pushr;
do {
if (--n == 0) {
spip->dspi->PUSHR.R = SPC5_PUSHR_EOQ | cmd | (uint32_t)*txbuf;
break;
}
spip->dspi->PUSHR.R = cmd | (uint32_t)*txbuf;
txbuf++;
} while (TRUE);
}
/**
* @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_irq(edma_channel_t channel, void *p) {
SPIDriver *spip = (SPIDriver *)p;
/* Clearing RX channel state.*/
edmaChannelStop(channel);
/* Stops the DSPI and clears the queues.*/
spip->dspi->MCR.R |= SPC5_MCR_HALT | SPC5_MCR_CLR_TXF | SPC5_MCR_CLR_RXF;
/* Portable SPI ISR code defined in the high level driver, note, it is
a macro.*/
_spi_isr_code(spip);
}
/**
* @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_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 delagated 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;
}
}
/**
* @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 DSPI and clears the queues.*/
spip->dspi->MCR.R |= SPC5_MCR_HALT | SPC5_MCR_CLR_TXF | SPC5_MCR_CLR_RXF;
edmaChannelStop(spip->tx1_channel);
edmaChannelStop(spip->tx2_channel);
edmaChannelStop(spip->rx_channel);
SPC5_SPI_DMA_ERROR_HOOK(spip);
}
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
#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
*/
CH_IRQ_HANDLER(SPC5_DSPI0_TFFF_HANDLER) {
CH_IRQ_PROLOGUE();
chSysLockFromIsr();
/* Interrupt served and back to DMA mode.*/
SPC5_DSPI0.RSER.B.TFFFDIRS = 1;
SPC5_DSPI0.SR.B.TFFF = 1;
/* Pushing last frame.*/
SPC5_DSPI0.PUSHR.R = (SPID1.config->pushr | SPID1.tx_last | SPC5_PUSHR_EOQ) &
~SPC5_PUSHR_CONT;
chSysUnlockFromIsr();
CH_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
*/
CH_IRQ_HANDLER(SPC5_DSPI1_TFFF_HANDLER) {
CH_IRQ_PROLOGUE();
chSysLockFromIsr();
/* Interrupt served and back to DMA mode.*/
SPC5_DSPI1.RSER.B.TFFFDIRS = 1;
SPC5_DSPI1.SR.B.TFFF = 1;
/* Pushing last frame.*/
SPC5_DSPI1.PUSHR.R = (SPID2.config->pushr | SPID2.tx_last | SPC5_PUSHR_EOQ) &
~SPC5_PUSHR_CONT;
chSysUnlockFromIsr();
CH_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
*/
CH_IRQ_HANDLER(SPC5_DSPI2_TFFF_HANDLER) {
CH_IRQ_PROLOGUE();
chSysLockFromIsr();
/* Interrupt served and back to DMA mode.*/
SPC5_DSPI2.RSER.B.TFFFDIRS = 1;
SPC5_DSPI2.SR.B.TFFF = 1;
/* Pushing last frame.*/
SPC5_DSPI2.PUSHR.R = (SPID3.config->pushr | SPID3.tx_last | SPC5_PUSHR_EOQ) &
~SPC5_PUSHR_CONT;
chSysUnlockFromIsr();
CH_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
*/
CH_IRQ_HANDLER(SPC5_DSPI3_TFFF_HANDLER) {
CH_IRQ_PROLOGUE();
chSysLockFromIsr();
/* Interrupt served and back to DMA mode.*/
SPC5_DSPI3.RSER.B.TFFFDIRS = 1;
SPC5_DSPI3.SR.B.TFFF = 1;
/* Pushing last frame.*/
SPC5_DSPI3.PUSHR.R = (SPID4.config->pushr | SPID4.tx_last | SPC5_PUSHR_EOQ) &
~SPC5_PUSHR_CONT;
chSysUnlockFromIsr();
CH_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
*/
CH_IRQ_HANDLER(SPC5_DSPI4_TFFF_HANDLER) {
CH_IRQ_PROLOGUE();
chSysLockFromIsr();
/* Interrupt served and back to DMA mode.*/
SPC5_DSPI4.RSER.B.TFFFDIRS = 1;
SPC5_DSPI4.SR.B.TFFF = 1;
/* Pushing last frame.*/
SPC5_DSPI4.PUSHR.R = (SPID5.config->pushr | SPID5.tx_last | SPC5_PUSHR_EOQ) &
~SPC5_PUSHR_CONT;
chSysUnlockFromIsr();
CH_IRQ_EPILOGUE();
}
#endif /* SPC5_SPI_USE_DSPI4 */
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level SPI driver initialization.
*
* @notapi
*/
void spi_lld_init(void) {
#if SPC5_SPI_USE_DSPI0
/* Driver initialization.*/
spiObjectInit(&SPID1);
SPC5_DSPI0_ENABLE_CLOCK();
SPID1.dspi = &SPC5_DSPI0;
SPID1.tx1_channel = EDMA_ERROR;
SPID1.tx2_channel = EDMA_ERROR;
SPID1.rx_channel = EDMA_ERROR;
SPC5_DSPI0.MCR.R = SPC5_MCR_MSTR | SPC5_MCR_HALT | SPC5_MCR_MDIS |
SPC5_SPI_DSPI0_MCR;
INTC.PSR[SPC5_DSPI0_TFFF_NUMBER].R = SPC5_SPI_DSPI0_IRQ_PRIO;
#endif /* SPC5_SPI_USE_DSPI0 */
#if SPC5_SPI_USE_DSPI1
/* Driver initialization.*/
SPC5_DSPI1_ENABLE_CLOCK();
spiObjectInit(&SPID2);
SPID2.dspi = &SPC5_DSPI1;
SPID2.tx1_channel = EDMA_ERROR;
SPID2.tx2_channel = EDMA_ERROR;
SPID2.rx_channel = EDMA_ERROR;
SPC5_DSPI1.MCR.R = SPC5_MCR_MSTR | SPC5_MCR_HALT | SPC5_MCR_MDIS |
SPC5_SPI_DSPI1_MCR;
INTC.PSR[SPC5_DSPI1_TFFF_NUMBER].R = SPC5_SPI_DSPI1_IRQ_PRIO;
#endif /* SPC5_SPI_USE_DSPI1 */
#if SPC5_SPI_USE_DSPI2
/* Driver initialization.*/
spiObjectInit(&SPID3);
SPC5_DSPI2_ENABLE_CLOCK();
SPID3.dspi = &SPC5_DSPI2;
SPID3.tx1_channel = EDMA_ERROR;
SPID3.tx2_channel = EDMA_ERROR;
SPID3.rx_channel = EDMA_ERROR;
SPC5_DSPI2.MCR.R = SPC5_MCR_MSTR | SPC5_MCR_HALT | SPC5_MCR_MDIS |
SPC5_SPI_DSPI2_MCR;
INTC.PSR[SPC5_DSPI2_TFFF_NUMBER].R = SPC5_SPI_DSPI2_IRQ_PRIO;
#endif /* SPC5_SPI_USE_DSPI2 */
#if SPC5_SPI_USE_DSPI3
/* Driver initialization.*/
spiObjectInit(&SPID4);
SPC5_DSPI3_ENABLE_CLOCK();
SPID4.dspi = &SPC5_DSPI3;
SPID4.tx1_channel = EDMA_ERROR;
SPID4.tx2_channel = EDMA_ERROR;
SPID4.rx_channel = EDMA_ERROR;
SPC5_DSPI3.MCR.R = SPC5_MCR_MSTR | SPC5_MCR_HALT | SPC5_MCR_MDIS |
SPC5_SPI_DSPI3_MCR;
INTC.PSR[SPC5_DSPI3_TFFF_NUMBER].R = SPC5_SPI_DSPI3_IRQ_PRIO;
#endif /* SPC5_SPI_USE_DSPI3 */
#if SPC5_SPI_USE_DSPI4
/* Driver initialization.*/
spiObjectInit(&SPID5);
SPC5_DSPI4_ENABLE_CLOCK();
SPID5.dspi = &SPC5_DSPI4;
SPID5.tx1_channel = EDMA_ERROR;
SPID5.tx2_channel = EDMA_ERROR;
SPID5.rx_channel = EDMA_ERROR;
SPC5_DSPI4.MCR.R = SPC5_MCR_MSTR | SPC5_MCR_HALT | SPC5_MCR_MDIS |
SPC5_SPI_DSPI4_MCR;
INTC.PSR[SPC5_DSPI4_TFFF_NUMBER].R = SPC5_SPI_DSPI4_IRQ_PRIO;
#endif /* SPC5_SPI_USE_DSPI4 */
}
/**
* @brief Configures and activates the SPI peripheral.
*
* @param[in] spip pointer to the @p SPIDriver object
*
* @notapi
*/
void spi_lld_start(SPIDriver *spip) {
chDbgAssert((spip->config->pushr & DSPI_PUSHR_EXCLUDED_BITS) == 0,
"spi_lld_start(), #1", "invalid PUSHR bits specified");
if (spip->state == SPI_STOP) {
/* Enables the peripheral.*/
#if SPC5_SPI_USE_DSPI0
if (&SPID1 == spip) {
spip->tx1_channel = edmaChannelAllocate(&spi_dspi0_tx1_dma_config);
spip->tx2_channel = edmaChannelAllocate(&spi_dspi0_tx2_dma_config);
spip->rx_channel = edmaChannelAllocate(&spi_dspi0_rx_dma_config);
}
#endif /* SPC5_SPI_USE_DSPI0 */
#if SPC5_SPI_USE_DSPI1
if (&SPID2 == spip) {
spip->tx1_channel = edmaChannelAllocate(&spi_dspi1_tx1_dma_config);
spip->tx2_channel = edmaChannelAllocate(&spi_dspi1_tx2_dma_config);
spip->rx_channel = edmaChannelAllocate(&spi_dspi1_rx_dma_config);
}
#endif /* SPC5_SPI_USE_DSPI1 */
#if SPC5_SPI_USE_DSPI2
if (&SPID3 == spip) {
spip->tx1_channel = edmaChannelAllocate(&spi_dspi2_tx1_dma_config);
spip->tx2_channel = edmaChannelAllocate(&spi_dspi2_tx2_dma_config);
spip->rx_channel = edmaChannelAllocate(&spi_dspi2_rx_dma_config);
}
#endif /* SPC5_SPI_USE_DSPI2 */
#if SPC5_SPI_USE_DSPI3
if (&SPID4 == spip) {
spip->tx1_channel = edmaChannelAllocate(&spi_dspi3_tx1_dma_config);
spip->tx2_channel = edmaChannelAllocate(&spi_dspi3_tx2_dma_config);
spip->rx_channel = edmaChannelAllocate(&spi_dspi3_rx_dma_config);
}
#endif /* SPC5_SPI_USE_DSPI3 */
#if SPC5_SPI_USE_DSPI4
if (&SPID5 == spip) {
spip->tx1_channel = edmaChannelAllocate(&spi_dspi4_tx1_dma_config);
spip->tx2_channel = edmaChannelAllocate(&spi_dspi4_tx2_dma_config);
spip->rx_channel = edmaChannelAllocate(&spi_dspi4_rx_dma_config);
}
#endif /* SPC5_SPI_USE_DSPI5 */
chDbgAssert((spip->tx1_channel != EDMA_ERROR) &&
(spip->tx2_channel != EDMA_ERROR) &&
(spip->rx_channel != EDMA_ERROR),
"spi_lld_start(), #2", "channel cannot be allocated");
}
/* Configures the peripheral.*/
spip->dspi->MCR.B.MDIS = 0;
spip->dspi->CTAR[0].R = spip->config->ctar0;
spip->dspi->RSER.R = SPC5_RSER_TFFF_RE | SPC5_RSER_TFFF_DIRS |
SPC5_RSER_RFDF_RE | SPC5_RSER_RFDF_DIRS;
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.*/
edmaChannelRelease(spip->tx1_channel);
edmaChannelRelease(spip->tx2_channel);
edmaChannelRelease(spip->rx_channel);
/* 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.*/
spip->dspi->SR.R = spip->dspi->SR.R;
spip->dspi->MCR.B.HALT = 0;
/* Setting up the RX DMA channel.*/
spi_start_dma_rx_ignore(spip, n);
if (n <= SPC5_DSPI_FIFO_DEPTH) {
/* If the total transfer size is smaller than the TX FIFO size then
the whole transmitted data is pushed here and the TX DMA is not
activated.*/
spi_tx_prefill_ignore(spip, n);
}
else {
spi_start_dma_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.*/
spip->dspi->SR.R = spip->dspi->SR.R;
spip->dspi->MCR.B.HALT = 0;
/* DMAs require a different setup depending on the frame size.*/
if (spip->dspi->CTAR[0].B.FMSZ < 8) {
/* Setting up the RX DMA channel.*/
spi_start_dma_rx8(spip, n, rxbuf);
if (n <= SPC5_DSPI_FIFO_DEPTH) {
/* If the total transfer size is smaller than the TX FIFO size then
the whole transmitted data is pushed here and the TX DMA is not
activated.*/
spi_tx_prefill8(spip, n, txbuf);
}
else {
spi_start_dma_tx8(spip, n, txbuf);
}
}
else {
/* Setting up the RX DMA channel.*/
spi_start_dma_rx16(spip, n, rxbuf);
if (n <= SPC5_DSPI_FIFO_DEPTH) {
/* If the total transfer size is smaller than the TX FIFO size then
the whole transmitted data is pushed here and the TX DMA is not
activated.*/
spi_tx_prefill16(spip, n, txbuf);
}
else {
spi_start_dma_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.*/
spip->dspi->SR.R = spip->dspi->SR.R;
spip->dspi->MCR.B.HALT = 0;
/* Setting up the RX DMA channel.*/
spi_start_dma_rx_ignore(spip, n);
/* DMAs require a different setup depending on the frame size.*/
if (spip->dspi->CTAR[0].B.FMSZ < 8) {
if (n <= SPC5_DSPI_FIFO_DEPTH) {
/* If the total transfer size is smaller than the TX FIFO size then
the whole transmitted data is pushed here and the TX DMA is not
activated.*/
spi_tx_prefill8(spip, n, txbuf);
}
else {
spi_start_dma_tx8(spip, n, txbuf);
}
}
else {
if (n <= SPC5_DSPI_FIFO_DEPTH) {
/* If the total transfer size is smaller than the TX FIFO size then
the whole transmitted data is pushed here and the TX DMA is not
activated.*/
spi_tx_prefill16(spip, n, txbuf);
}
else {
spi_start_dma_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.*/
spip->dspi->SR.R = spip->dspi->SR.R;
spip->dspi->MCR.B.HALT = 0;
/* DMAs require a different setup depending on the frame size.*/
if (spip->dspi->CTAR[0].B.FMSZ < 8) {
/* Setting up the RX DMA channel.*/
spi_start_dma_rx8(spip, n, rxbuf);
}
else {
/* Setting up the RX DMA channel.*/
spi_start_dma_rx16(spip, n, rxbuf);
}
if (n <= SPC5_DSPI_FIFO_DEPTH) {
/* If the total transfer size is smaller than the TX FIFO size then
the whole transmitted data is pushed here and the TX DMA is not
activated.*/
spi_tx_prefill_ignore(spip, n);
}
else {
spi_start_dma_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;
spip->dspi->MCR.B.HALT = 0;
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;
spip->dspi->MCR.B.HALT = 1;
return (uint16_t)popr;
}
#endif /* HAL_USE_SPI */
/** @} */