From ee3f9cf124f685363f6e26f1084196c36a919f04 Mon Sep 17 00:00:00 2001 From: spacecoaster Date: Sun, 21 Sep 2014 15:46:34 +0000 Subject: [KINETIS] K20x SPI converted to use DMA. git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@7300 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/hal/ports/KINETIS/K20x/spi_lld.c | 226 +++++++++++++++++++++--------------- os/hal/ports/KINETIS/K20x/spi_lld.h | 40 +------ 2 files changed, 137 insertions(+), 129 deletions(-) (limited to 'os') diff --git a/os/hal/ports/KINETIS/K20x/spi_lld.c b/os/hal/ports/KINETIS/K20x/spi_lld.c index 4288d747d..b06dceb15 100644 --- a/os/hal/ports/KINETIS/K20x/spi_lld.c +++ b/os/hal/ports/KINETIS/K20x/spi_lld.c @@ -30,6 +30,33 @@ /* Driver local definitions. */ /*===========================================================================*/ +#if !defined(KINETIS_SPI_USE_SPI0) +#define KINETIS_SPI_USE_SPI0 TRUE +#endif + +#if !defined(KINETIS_SPI0_RX_DMA_IRQ_PRIORITY) +#define KINETIS_SPI0_RX_DMA_IRQ_PRIORITY 8 +#endif + +#if !defined(KINETIS_SPI0_RX_DMAMUX_CHANNEL) +#define KINETIS_SPI0_RX_DMAMUX_CHANNEL 0 +#endif + +#if !defined(KINETIS_SPI0_RX_DMA_CHANNEL) +#define KINETIS_SPI0_RX_DMA_CHANNEL 0 +#endif + +#if !defined(KINETIS_SPI0_TX_DMAMUX_CHANNEL) +#define KINETIS_SPI0_TX_DMAMUX_CHANNEL 1 +#endif + +#if !defined(KINETIS_SPI0_TX_DMA_CHANNEL) +#define KINETIS_SPI0_TX_DMA_CHANNEL 1 +#endif + +#define DMAMUX_SPI_RX_SOURCE 16 +#define DMAMUX_SPI_TX_SOURCE 17 + /*===========================================================================*/ /* Driver exported variables. */ /*===========================================================================*/ @@ -47,40 +74,60 @@ SPIDriver SPID1; /* Driver local functions. */ /*===========================================================================*/ +volatile uint8_t dmaDummy; + static void spi_start_xfer(SPIDriver *spip, bool polling) { /* * Enable the DSPI peripheral in master mode. - * On receive overflow, new data will overwrite old data. - * Set slave selects to be active low. * Clear the TX and RX FIFOs. * */ - spip->spi->MCR = SPIx_MCR_MSTR | - SPIx_MCR_ROOE | - SPIx_MCR_PCSIS(KINETIS_SPI_PCS_ALL) | - SPIx_MCR_CLR_TXF | SPIx_MCR_CLR_RXF; + spip->spi->MCR = SPIx_MCR_MSTR | SPIx_MCR_CLR_TXF | SPIx_MCR_CLR_RXF; - /* If we are not polling then enable interrupts */ + /* If we are not polling then enable DMA */ if (!polling) { - /* Enable transmit fill, receive and end of queue interrupts */ - spip->spi->RSER = SPIx_RSER_TFFF_RE | SPIx_RSER_RFDF_RE | - SPIx_RSER_EOQF_RE; + /* Enable receive dma and transmit dma */ + spip->spi->RSER = SPIx_RSER_RFDF_DIRS | SPIx_RSER_RFDF_RE | + SPIx_RSER_TFFF_RE | SPIx_RSER_TFFF_DIRS; + + /* Use dmaDummy as the source/destination when a buffer is not provided */ + dmaDummy = 0; + + /* Configure RX DMA */ + if (spip->rxbuf) { + DMA->TCD[KINETIS_SPI0_RX_DMA_CHANNEL].DADDR = (uint32_t)spip->rxbuf; + DMA->TCD[KINETIS_SPI0_RX_DMA_CHANNEL].DOFF = spip->word_size; + } else { + DMA->TCD[KINETIS_SPI0_RX_DMA_CHANNEL].DADDR = (uint32_t)&dmaDummy; + DMA->TCD[KINETIS_SPI0_RX_DMA_CHANNEL].DOFF = 0; + } + DMA->TCD[KINETIS_SPI0_RX_DMA_CHANNEL].BITER_ELINKNO = spip->count; + DMA->TCD[KINETIS_SPI0_RX_DMA_CHANNEL].CITER_ELINKNO = spip->count; + + /* Enable Request Register (ERQ) for RX by writing 0 to SERQ */ + DMA->SERQ = KINETIS_SPI0_RX_DMA_CHANNEL; + + /* Configure TX DMA */ + if (spip->txbuf) { + DMA->TCD[KINETIS_SPI0_TX_DMA_CHANNEL].SADDR = (uint32_t)spip->txbuf; + DMA->TCD[KINETIS_SPI0_TX_DMA_CHANNEL].SOFF = spip->word_size; + } else { + DMA->TCD[KINETIS_SPI0_TX_DMA_CHANNEL].SADDR = (uint32_t)&dmaDummy; + DMA->TCD[KINETIS_SPI0_TX_DMA_CHANNEL].SOFF = 0; + } + DMA->TCD[KINETIS_SPI0_TX_DMA_CHANNEL].BITER_ELINKNO = spip->count; + DMA->TCD[KINETIS_SPI0_TX_DMA_CHANNEL].CITER_ELINKNO = spip->count; + /* Enable Request Register (ERQ) for TX by writing 1 to SERQ */ + DMA->SERQ = KINETIS_SPI0_TX_DMA_CHANNEL; } } static void spi_stop_xfer(SPIDriver *spip) { /* Halt the DSPI peripheral */ - spip->spi->MCR = SPIx_MCR_MSTR | - SPIx_MCR_ROOE | - SPIx_MCR_PCSIS(KINETIS_SPI_PCS_ALL) | - SPIx_MCR_CLR_TXF | SPIx_MCR_CLR_RXF | - SPIx_MCR_HALT; - - /* Clear all interrupt enables */ - spip->spi->RSER = 0; + spip->spi->MCR = SPIx_MCR_MSTR | SPIx_MCR_HALT; /* Clear all the flags which are currently set. */ spip->spi->SR |= spip->spi->SR; @@ -90,54 +137,15 @@ static void spi_stop_xfer(SPIDriver *spip) /* Driver interrupt handlers. */ /*===========================================================================*/ -OSAL_IRQ_HANDLER(Vector70) { +OSAL_IRQ_HANDLER(Vector40) { OSAL_IRQ_PROLOGUE(); - SPIDriver *spip = &SPID1; - uint32_t sr = spip->spi->SR; - - /* Handle Receive FIFO Drain interrupt */ - if (sr & SPIx_SR_RFDF) { - - /* Pop the data out of the fifo */ - uint8_t data = spip->spi->POPR; - - /* If we have an rxbuf and it has space then store the data */ - if (spip->rxbuf && spip->rxidx < spip->nbytes) { - spip->rxbuf[spip->rxidx++] = data; - } - - /* Clear the Receive FIFO Drain Flag */ - spip->spi->SR = SPIx_SR_RFDF; - } - - /* Handle End of Queue interrupt */ - if (sr & SPIx_SR_EOQF) { - - spip->spi->SR = SPIx_SR_EOQF; - - spi_stop_xfer(spip); - - _spi_isr_code(spip); - } + /* Clear bit 0 in Interrupt Request Register (INT) by writing 0 to CINT */ + DMA->CINT = KINETIS_SPI0_RX_DMA_CHANNEL; - /* Handle Transmit FIFO Fill interrupt */ - if (sr & SPIx_SR_TFFF) { + spi_stop_xfer(&SPID1); - /* The data to send is either the next tx byte or a filler byte */ - uint8_t data = spip->txbuf ? spip->txbuf[spip->txidx] : 0x00; - - /* Calculate the number of bytes remaining and increment the index */ - size_t n = spip->nbytes - spip->txidx++; - - /* Push the data into the FIFO, CONT if more bytes, EOQ if last byte */ - spip->spi->PUSHR = (n > 1 ? SPIx_PUSHR_CONT : SPIx_PUSHR_EOQ) | - SPIx_PUSHR_PCS(spip->config->pcs) | - SPIx_PUSHR_TXDATA(data); - - /* Clear the Transmit FIFO Fill Flag */ - spip->spi->SR = SPIx_SR_TFFF; - } + _spi_isr_code(&SPID1); OSAL_IRQ_EPILOGUE(); } @@ -152,13 +160,9 @@ OSAL_IRQ_HANDLER(Vector70) { * @notapi */ void spi_lld_init(void) { - - nvicEnableVector(SPI0_IRQn, KINETIS_SPI_SPI0_IRQ_PRIORITY); - #if KINETIS_SPI_USE_SPI0 spiObjectInit(&SPID1); #endif - } /** @@ -186,13 +190,56 @@ void spi_lld_start(SPIDriver *spip) { } else { spip->spi->CTAR[0] = KINETIS_SPI_TAR0_DEFAULT; } - - spip->spi->CTAR[1] = KINETIS_SPI_TAR1_DEFAULT; } #endif - } - /* SPI setup and enable.*/ + nvicEnableVector(DMA0_IRQn, KINETIS_SPI0_RX_DMA_IRQ_PRIORITY); + + SIM->SCGC6 |= SIM_SCGC6_DMAMUX; + SIM->SCGC7 |= SIM_SCGC7_DMA; + + /* Clear DMA error flags */ + DMA->ERR = 0x0F; + + /* Rx, select SPI Rx FIFO */ + DMAMUX->CHCFG[KINETIS_SPI0_RX_DMAMUX_CHANNEL] = DMAMUX_CHCFGn_ENBL | + DMAMUX_CHCFGn_SOURCE(DMAMUX_SPI_RX_SOURCE); + + /* Tx, select SPI Tx FIFO */ + DMAMUX->CHCFG[KINETIS_SPI0_TX_DMAMUX_CHANNEL] = DMAMUX_CHCFGn_ENBL | + DMAMUX_CHCFGn_SOURCE(DMAMUX_SPI_TX_SOURCE); + + /* Extract the frame size from the TAR */ + uint16_t frame_size = ((spip->spi->CTAR[0] >> SPIx_CTARn_FMSZ_SHIFT) & + SPIx_CTARn_FMSZ_MASK) + 1; + + /* DMA transfer size is 16 bits for a frame size > 8 bits */ + uint16_t dma_size = frame_size > 8 ? 1 : 0; + + /* DMA word size is 2 for a 16 bit frame size */ + spip->word_size = frame_size > 8 ? 2 : 1; + + /* configure DMA RX fixed values */ + DMA->TCD[KINETIS_SPI0_RX_DMA_CHANNEL].SADDR = (uint32_t)&SPI0->POPR; + DMA->TCD[KINETIS_SPI0_RX_DMA_CHANNEL].SOFF = 0; + DMA->TCD[KINETIS_SPI0_RX_DMA_CHANNEL].SLAST = 0; + DMA->TCD[KINETIS_SPI0_RX_DMA_CHANNEL].DLASTSGA = 0; + DMA->TCD[KINETIS_SPI0_RX_DMA_CHANNEL].ATTR = DMA_ATTR_SSIZE(dma_size) | + DMA_ATTR_DSIZE(dma_size); + DMA->TCD[KINETIS_SPI0_RX_DMA_CHANNEL].NBYTES_MLNO = spip->word_size; + DMA->TCD[KINETIS_SPI0_RX_DMA_CHANNEL].CSR = DMA_CSR_DREQ_MASK | + DMA_CSR_INTMAJOR_MASK; + + /* configure DMA TX fixed values */ + DMA->TCD[KINETIS_SPI0_TX_DMA_CHANNEL].SLAST = 0; + DMA->TCD[KINETIS_SPI0_TX_DMA_CHANNEL].DADDR = (uint32_t)&SPI0->PUSHR; + DMA->TCD[KINETIS_SPI0_TX_DMA_CHANNEL].DOFF = 0; + DMA->TCD[KINETIS_SPI0_TX_DMA_CHANNEL].DLASTSGA = 0; + DMA->TCD[KINETIS_SPI0_TX_DMA_CHANNEL].ATTR = DMA_ATTR_SSIZE(dma_size) | + DMA_ATTR_DSIZE(dma_size); + DMA->TCD[KINETIS_SPI0_TX_DMA_CHANNEL].NBYTES_MLNO = spip->word_size; + DMA->TCD[KINETIS_SPI0_TX_DMA_CHANNEL].CSR = DMA_CSR_DREQ_MASK; + } } /** @@ -207,8 +254,10 @@ void spi_lld_stop(SPIDriver *spip) { /* If in ready state then disables the SPI clock.*/ if (spip->state == SPI_READY) { - /* Disable the clock for SPI0 */ - SIM->SCGC6 &= ~SIM_SCGC6_SPI0; + nvicDisableVector(DMA0_IRQn); + + SIM->SCGC7 &= ~SIM_SCGC7_DMA; + SIM->SCGC6 &= ~SIM_SCGC6_DMAMUX; #if KINETIS_SPI_USE_SPI0 if (&SPID1 == spip) { @@ -216,6 +265,9 @@ void spi_lld_stop(SPIDriver *spip) { spip->spi->MCR |= SPIx_MCR_HALT; } #endif + + /* Disable the clock for SPI0 */ + SIM->SCGC6 &= ~SIM_SCGC6_SPI0; } } @@ -228,17 +280,12 @@ void spi_lld_stop(SPIDriver *spip) { */ void spi_lld_select(SPIDriver *spip) { - /* If we are not using the DSPI managed chip select then assert the SS */ - if (!spip->config->pcs) { - palClearPad(spip->config->ssport, spip->config->sspad); - } + palClearPad(spip->config->ssport, spip->config->sspad); } /** * @brief Deasserts the slave select signal. * @details The previously selected peripheral is unselected. - * This has no effect if we are using the KINETIS PCS mode to - * manage slave select. * * @param[in] spip pointer to the @p SPIDriver object * @@ -246,10 +293,7 @@ void spi_lld_select(SPIDriver *spip) { */ void spi_lld_unselect(SPIDriver *spip) { - /* If we are not using the DSPI managed chip select then deassert the SS */ - if (!spip->config->pcs) { - palSetPad(spip->config->ssport, spip->config->sspad); - } + palSetPad(spip->config->ssport, spip->config->sspad); } /** @@ -265,11 +309,9 @@ void spi_lld_unselect(SPIDriver *spip) { */ void spi_lld_ignore(SPIDriver *spip, size_t n) { - spip->nbytes = n; + spip->count = n; spip->rxbuf = NULL; - spip->rxidx = 0; spip->txbuf = NULL; - spip->txidx = 0; spi_start_xfer(spip, false); } @@ -292,11 +334,9 @@ void spi_lld_ignore(SPIDriver *spip, size_t n) { void spi_lld_exchange(SPIDriver *spip, size_t n, const void *txbuf, void *rxbuf) { - spip->nbytes = n; + spip->count = n; spip->rxbuf = rxbuf; - spip->rxidx = 0; spip->txbuf = txbuf; - spip->txidx = 0; spi_start_xfer(spip, false); } @@ -316,11 +356,9 @@ void spi_lld_exchange(SPIDriver *spip, size_t n, */ void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf) { - spip->nbytes = n; + spip->count = n; spip->rxbuf = NULL; - spip->rxidx = 0; spip->txbuf = (void *)txbuf; - spip->txidx = 0; spi_start_xfer(spip, false); } @@ -340,11 +378,9 @@ void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf) { */ void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf) { - spip->nbytes = n; + spip->count = n; spip->rxbuf = rxbuf; - spip->rxidx = 0; spip->txbuf = NULL; - spip->txidx = 0; spi_start_xfer(spip, false); } @@ -365,7 +401,7 @@ uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame) { spi_start_xfer(spip, true); - spip->spi->PUSHR = SPIx_PUSHR_PCS(0x10) | SPIx_PUSHR_TXDATA(frame); + spip->spi->PUSHR = SPIx_PUSHR_TXDATA(frame); while ((spip->spi->SR & SPIx_SR_RFDF) == 0) ; diff --git a/os/hal/ports/KINETIS/K20x/spi_lld.h b/os/hal/ports/KINETIS/K20x/spi_lld.h index 5ab97116e..97588b3a3 100644 --- a/os/hal/ports/KINETIS/K20x/spi_lld.h +++ b/os/hal/ports/KINETIS/K20x/spi_lld.h @@ -100,10 +100,6 @@ typedef struct { */ spicallback_t end_cb; /* End of the mandatory fields.*/ - /** - * @brief The PCS chip select lines. - */ - uint8_t pcs; /** * @brief The chip select line port - when not using pcs. */ @@ -151,25 +147,21 @@ struct SPIDriver { */ SPI_TypeDef *spi; /** - * @brief Number of bytes of data to transfer. + * @brief Number of bytes/words of data to transfer. */ - size_t nbytes; + size_t count; /** - * @brief Pointer to the buffer with data to send. + * @brief Word size in bytes. */ - const uint8_t *txbuf; + size_t word_size; /** - * @brief Current index in buffer when sending data. + * @brief Pointer to the buffer with data to send. */ - size_t txidx; + const uint8_t *txbuf; /** * @brief Pointer to the buffer to put received data. */ uint8_t *rxbuf; - /** - * @brief Current index in buffer when receiving data. - */ - size_t rxidx; }; /*===========================================================================*/ @@ -205,26 +197,6 @@ struct SPIDriver { #define KINETIS_SPI_TAR0_DEFAULT KINETIS_SPI_TAR_SYSCLK_DIV_2(8) #define KINETIS_SPI_TAR1_DEFAULT KINETIS_SPI_TAR_SYSCLK_DIV_2(8) -/* - * A mask of the slave select lines to be managed by KINETIS DSPI. - * - * These values are mapped to real ports and pins by selecting - * PAL_MODE_ALTERNATIVE_2 as the pin mode. Multiple values may - * be ORed together to select multiple outputs. This can be used with an - * external multiplexor to increase the number of slave selects. - * - * If KINETIS_SPI_PCS_NONE is selected for the configuration pcs field - * then the configuration ssport and sspad values are used to control the - * slave select directly and the KINETIS DSPI managed select is not used. - */ -#define KINETIS_SPI_PCS_NONE 0x00 -#define KINETIS_SPI_PCS0 0x01 -#define KINETIS_SPI_PCS1 0x02 -#define KINETIS_SPI_PCS2 0x04 -#define KINETIS_SPI_PCS3 0x08 -#define KINETIS_SPI_PCS4 0x10 -#define KINETIS_SPI_PCS_ALL 0x1F - /*===========================================================================*/ /* External declarations. */ /*===========================================================================*/ -- cgit v1.2.3