SPC5xx DSPI driver working on SPC563Mxx.

git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@5766 35acf78f-673a-0410-8e92-d51de3d6d3f4
This commit is contained in:
gdisirio 2013-05-28 14:12:55 +00:00
parent 036f60f497
commit e9edd47802
4 changed files with 196 additions and 33 deletions

View File

@ -102,7 +102,7 @@ SPIDriver SPID4;
*/
static const edma_channel_config_t spi_dspi0_tx1_dma_config = {
SPC5_DSPI0_TX1_DMA_DEV_ID, SPC5_SPI_DSPI0_DMA_PRIO, SPC5_SPI_DSPI0_DMA_IRQ_PRIO,
NULL, spi_serve_dma_error_irq, &SPID1
spi_serve_tx_irq, spi_serve_dma_error_irq, &SPID1
};
/**
@ -128,7 +128,7 @@ static const edma_channel_config_t spi_dspi0_rx_dma_config = {
*/
static const edma_channel_config_t spi_dspi1_tx1_dma_config = {
SPC5_DSPI1_TX1_DMA_DEV_ID, SPC5_SPI_DSPI1_DMA_PRIO, SPC5_SPI_DSPI1_DMA_IRQ_PRIO,
NULL, spi_serve_dma_error_irq, &SPID2
spi_serve_tx_irq, spi_serve_dma_error_irq, &SPID2
};
/**
@ -154,7 +154,7 @@ static const edma_channel_config_t spi_dspi1_rx_dma_config = {
*/
static const edma_channel_config_t spi_dspi2_tx1_dma_config = {
SPC5_DSPI2_TX1_DMA_DEV_ID, SPC5_SPI_DSPI2_DMA_PRIO, SPC5_SPI_DSPI2_DMA_IRQ_PRIO,
NULL, spi_serve_dma_error_irq, &SPID3
spi_serve_tx_irq, spi_serve_dma_error_irq, &SPID3
};
/**
@ -180,7 +180,7 @@ static const edma_channel_config_t spi_dspi2_rx_dma_config = {
*/
static const edma_channel_config_t spi_dspi3_tx1_dma_config = {
SPC5_DSPI3_TX1_DMA_DEV_ID, SPC5_SPI_DSPI3_DMA_PRIO, SPC5_SPI_DSPI3_DMA_IRQ_PRIO,
NULL, spi_serve_dma_error_irq, &SPID4
spi_serve_tx_irq, spi_serve_dma_error_irq, &SPID4
};
/**
@ -204,6 +204,33 @@ static const edma_channel_config_t spi_dspi3_rx_dma_config = {
/* 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.
*
@ -262,6 +289,42 @@ static void spi_start_dma_rx16(SPIDriver *spip,
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.
*
@ -382,6 +445,27 @@ static void spi_start_dma_tx16(SPIDriver *spip,
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.
*
@ -442,7 +526,8 @@ static void spi_tx_prefill16(SPIDriver *spip,
static void spi_serve_rx_irq(edma_channel_t channel, void *p) {
SPIDriver *spip = (SPIDriver *)p;
(void)channel;
/* Clearing RX channel state.*/
edmaChannelStop(channel);
/* Stops the DSPI and clears the queues.*/
spip->dspi->MCR.R = DSPI_MCR_ENFORCED_BITS | SPC5_MCR_HALT |
@ -455,7 +540,7 @@ static void spi_serve_rx_irq(edma_channel_t channel, void *p) {
}
/**
* @brief Shared TX2 DMA events service routine.
* @brief Shared TX1/TX2 DMA events service routine.
*
* @param[in] channel the channel number
* @param[in] p parameter for the registered function
@ -467,6 +552,10 @@ static void spi_serve_tx_irq(edma_channel_t channel, void *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.*/
@ -836,9 +925,22 @@ void spi_lld_unselect(SPIDriver *spip) {
*/
void spi_lld_ignore(SPIDriver *spip, size_t n) {
(void)spip;
(void)n;
/* Starting transfer.*/
spip->dspi->SR.R = spip->dspi->SR.R;
spip->dspi->MCR.R = DSPI_MCR_ENFORCED_BITS | spip->config->mcr;
/* 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);
}
}
/**
@ -909,10 +1011,36 @@ void spi_lld_exchange(SPIDriver *spip, size_t n,
*/
void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf) {
(void)spip;
(void)n;
(void)txbuf;
/* Starting transfer.*/
spip->dspi->SR.R = spip->dspi->SR.R;
spip->dspi->MCR.R = DSPI_MCR_ENFORCED_BITS | spip->config->mcr;
/* 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);
}
}
}
/**
@ -930,10 +1058,29 @@ void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf) {
*/
void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf) {
(void)spip;
(void)n;
(void)rxbuf;
/* Starting transfer.*/
spip->dspi->SR.R = spip->dspi->SR.R;
spip->dspi->MCR.R = DSPI_MCR_ENFORCED_BITS | spip->config->mcr;
/* 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);
}
}
/**
@ -950,10 +1097,12 @@ void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf) {
*/
uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame) {
(void)spip;
(void)frame;
return 0;
spip->dspi->MCR.R = DSPI_MCR_ENFORCED_BITS | spip->config->mcr;
spip->dspi->PUSHR.R = (SPC5_PUSHR_EOQ | spip->config->pushr |
(uint32_t)frame) & ~SPC5_PUSHR_CONT;
while (!spip->dspi->SR.B.RFDF)
;
return (uint16_t)spip->dspi->POPR.R;
}
#endif /* HAL_USE_SPI */

View File

@ -1345,7 +1345,7 @@ edma_channel_t edmaChannelAllocate(const edma_channel_config_t *ccfg) {
*/
void edmaChannelRelease(edma_channel_t channel) {
chDbgCheck((channel < 0) && (channel >= SPC5_EDMA_NCHANNELS),
chDbgCheck((channel >= 0) && (channel < SPC5_EDMA_NCHANNELS),
"edmaChannelAllocate");
chDbgAssert(channels[channel] != NULL,
"edmaChannelRelease(), #1",

View File

@ -126,7 +126,6 @@ void spiStop(SPIDriver *spip) {
chSysLock();
chDbgAssert((spip->state == SPI_STOP) || (spip->state == SPI_READY),
"spiStop(), #1", "invalid state");
spi_lld_unselect(spip);
spi_lld_stop(spip);
spip->state = SPI_STOP;
chSysUnlock();

View File

@ -18,15 +18,15 @@
#include "hal.h"
/*
* Maximum speed SPI configuration (21MHz, CPHA=0, CPOL=0, MSb first).
* Maximum speed SPI configuration.
*/
static const SPIConfig hs_spicfg = {
NULL,
0,
0,
SPC5_MCR_PCSIS0, /* MCR. */
SPC5_CTAR_CSSCK_DIV64 | SPC5_CTAR_ASC_DIV64 | SPC5_CTAR_FMSZ(8) |
SPC5_CTAR_PBR_PRE2 | SPC5_CTAR_BR_DIV128, /* CTAR0. */
SPC5_CTAR_CSSCK_DIV2 | SPC5_CTAR_ASC_DIV2 | SPC5_CTAR_FMSZ(8) |
SPC5_CTAR_PBR_PRE2 | SPC5_CTAR_BR_DIV2, /* CTAR0. */
SPC5_PUSHR_CONT | SPC5_PUSHR_PCS(0) /* PUSHR. */
};
@ -37,8 +37,9 @@ static const SPIConfig ls_spicfg = {
NULL,
0,
0,
0, /* MCR. */
SPC5_CTAR_FMSZ(8) | SPC5_CTAR_PBR_PRE2 | SPC5_CTAR_BR_DIV256, /* CTAR0. */
SPC5_MCR_PCSIS0, /* MCR. */
SPC5_CTAR_CSSCK_DIV64 | SPC5_CTAR_ASC_DIV64 | SPC5_CTAR_FMSZ(8) |
SPC5_CTAR_PBR_PRE2 | SPC5_CTAR_BR_DIV256, /* CTAR0. */
SPC5_PUSHR_CONT | SPC5_PUSHR_PCS(0) /* PUSHR. */
};
@ -58,7 +59,7 @@ static msg_t spi_thread_1(void *p) {
chRegSetThreadName("SPI thread 1");
while (TRUE) {
spiAcquireBus(&SPID2); /* Acquire ownership of the bus. */
palSetPad(PORT11, P11_LED1); /* LED ON. */
palClearPad(PORT11, P11_LED1); /* LED ON. */
spiStart(&SPID2, &hs_spicfg); /* Setup transfer parameters. */
spiSelect(&SPID2); /* Slave Select assertion. */
spiExchange(&SPID2, 512,
@ -79,7 +80,7 @@ static msg_t spi_thread_2(void *p) {
chRegSetThreadName("SPI thread 2");
while (TRUE) {
spiAcquireBus(&SPID2); /* Acquire ownership of the bus. */
palClearPad(PORT11, P11_LED1); /* LED OFF. */
palSetPad(PORT11, P11_LED1); /* LED OFF. */
spiStart(&SPID2, &ls_spicfg); /* Setup transfer parameters. */
spiSelect(&SPID2); /* Slave Select assertion. */
spiExchange(&SPID2, 512,
@ -116,19 +117,33 @@ int main(void) {
for (i = 0; i < sizeof(txbuf); i++)
txbuf[i] = (uint8_t)i;
spiStart(&SPID2, &hs_spicfg);
/* Starting driver for test.*/
spiStart(&SPID2, &ls_spicfg);
SIU.PCR[102].R = PAL_MODE_OUTPUT_ALTERNATE(1); /* SCK */
SIU.PCR[103].R = PAL_MODE_OUTPUT_ALTERNATE(1); /* SIN */
SIU.PCR[104].R = PAL_MODE_OUTPUT_ALTERNATE(1); /* SOUT */
SIU.PCR[105].R = PAL_MODE_OUTPUT_ALTERNATE(1); /* PCS[0] */
spiExchange(&SPID2, 4, txbuf, rxbuf);
spiExchange(&SPID2, 4, txbuf, rxbuf);
spiExchange(&SPID2, 4, txbuf, rxbuf);
/* Testing sending and receiving at the same time.*/
spiExchange(&SPID2, 4, txbuf, rxbuf);
spiExchange(&SPID2, 32, txbuf, rxbuf);
spiExchange(&SPID2, 512, txbuf, rxbuf);
#if 0
/* Testing clock pulses without data buffering.*/
spiIgnore(&SPID2, 4);
spiIgnore(&SPID2, 32);
/* Testing sending data ignoring incoming data.*/
spiSend(&SPID2, 4, txbuf);
spiSend(&SPID2, 32, txbuf);
/* Testing receiving data while sending idle bits (high level).*/
spiReceive(&SPID2, 4, rxbuf);
spiReceive(&SPID2, 32, rxbuf);
/* Testing stop procedure.*/
spiStop(&SPID2);
/*
* Starting the transmitter and receiver threads.
*/
@ -136,7 +151,7 @@ int main(void) {
NORMALPRIO + 1, spi_thread_1, NULL);
chThdCreateStatic(spi_thread_2_wa, sizeof(spi_thread_2_wa),
NORMALPRIO + 1, spi_thread_2, NULL);
#endif
/*
* Normal main() thread activity, in this demo it does nothing.
*/