/* spi.c: SPI Driver for SPC58EC, 24.04.2023, Rosy Sebastian */ #include "spi.h" #include "clock_cfg.h" #include "clock.h" #include "spihandler.h" SPIDriver SPID1; SPIDriver SPID2; /** * @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; } /** * @brief Low level SPI driver initialization. * * @init */ void spi_lld_init(void) { /* Driver initialization DSPI0 */ SPCSetPeripheralClockMode(SPC5_DSPI0_PCTL, SPC5_SPI_DSPI0_START_PCTL); SPID1.config = NULL; spi_lld_obj_init(&SPID1, &SPC5_DSPI0); SPC5_DSPI0.MCR.R = SPC5_MCR_HALT | SPC5_MCR_MDIS | SPC5_SPI_DSPI0_MCR; INTC_PSR(SPC5_DSPI0_RFDF_NUMBER) = SPC5_SPI_DSPI0_IRQ_PRIO; INTC_PSR(SPC5_DSPI0_EOQF_NUMBER) = SPC5_SPI_DSPI0_IRQ_PRIO; INTC_PSR(SPC5_DSPI0_TFFF_NUMBER) = SPC5_SPI_DSPI0_IRQ_PRIO; /* Driver initialization DSPI1 */ SPCSetPeripheralClockMode(SPC5_DSPI1_PCTL, SPC5_SPI_DSPI1_START_PCTL); SPID2.config = NULL; spi_lld_obj_init(&SPID2, &SPC5_DSPI1); SPC5_DSPI1.MCR.R = SPC5_MCR_HALT | SPC5_MCR_MDIS | SPC5_SPI_DSPI1_MCR; INTC_PSR(SPC5_DSPI1_RFDF_NUMBER) = SPC5_SPI_DSPI1_IRQ_PRIO; INTC_PSR(SPC5_DSPI1_TFFF_NUMBER) = SPC5_SPI_DSPI1_IRQ_PRIO; INTC_PSR(SPC5_DSPI1_EOQF_NUMBER) = SPC5_SPI_DSPI1_IRQ_PRIO; } /** * @brief Configures and activates the SPI peripheral. * * @param[in] spip pointer to the @p SPIDriver object * @param[in] config pointer to the @p SPIConfig object * * @api */ void spi_lld_start(SPIDriver *spip, const SPIConfig *config) { spip->config = config; /* Set SPI mode (Master/Slave).*/ if (spip->config->mode == SPI_MASTER) { spip->dspi->MCR.B.MSTR = 1U; } else { spip->dspi->MCR.B.MSTR = 0U; } /* 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; spip->dspi->SR.R = 0xFFFFFFFFUL; } /** * @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 = 0xFFFFFFFFUL; 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 */ spip->dspi->MCR.R |= SPC5_MCR_HALT; /* Waits till the DSPI is in stopped state and the transfer is completed.*/ while (spip->dspi->SR.B.TXRXS != 0U || spip->dspi->SR.B.TCF != 1U) { ; } /* Clears DSPI queues.*/ spip->dspi->MCR.R |= SPC5_MCR_CLR_TXF | SPC5_MCR_CLR_RXF; } void spi_receive_deinit(SPIDriver* spip) { /* Done with transfer, disable interrupt */ spip->dspi->RSER.B.RFDFRE = 0; /* Stops the transfer.*/ spi_dspi_stop(spip); } /** * @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; uint32_t frame; while (spip->dspi->SR.B.TXCTR < SPC5_DSPI_FIFO_DEPTH) { frame = **txpp; (*txpp)++; if (--(*np) == 0U) { spip->dspi->PUSHR.R = (SPC5_PUSHR_EOQ | cmd | frame) & ~SPC5_PUSHR_CONT; break; } spip->dspi->PUSHR.R = cmd | frame; /* Emptying the RX FIFO.*/ while ((spip->rx_cnt > 0U) && (spip->dspi->SR.B.RXCTR > 0U)) { frame = spip->dspi->POPR.R; //if (spip->rx_ptr != NULL) { fifo_write(spip->spi_rx_fifo, frame); //} spip->rx_cnt--; } } } /** * @brief Starts reception 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, FIFO *rx_fifo) { /* Setting up the fields required for operation continuation.*/ spip->spi_rx_fifo = rx_fifo; spip->rx_cnt = n; spip->dspi->RSER.B.RFDFRE = 1; } /** * @brief Starts transmission 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) { /* Enabling the EOQF interrupt source for transfer complete. * This interrupt is not valid in slave mode. */ if (spip->dspi->MCR.B.MSTR == 1UL) { spip->dspi->RSER.B.EOQFRE = 1; } /* 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 == 0U) { 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; } /** * @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) == 0U) { spip->dspi->PUSHR.R = (SPC5_PUSHR_EOQ | cmd | 0xFFFFUL) & ~SPC5_PUSHR_CONT; break; } spip->dspi->PUSHR.R = cmd | 0x0000FFFFUL; /* Emptying the RX FIFO.*/ while ((spip->rx_cnt > 0U) && (spip->dspi->SR.B.RXCTR > 0U)) { uint32_t frame = spip->dspi->POPR.R; if (spip->rx_ptr != NULL) { fifo_write(spip->spi_rx_fifo, frame); } spip->rx_cnt--; } } } /** * @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) { /* Setting up the fields required for operation continuation.*/ spip->rx_ptr = NULL; spip->rx_cnt = n; /*spip->spi_mrx_fifo = &spihandler_mrx_fifo; spip->rx_cnt = n;*/ spip->dspi->RSER.B.RFDFRE = 1; } /** * @brief Starts transmission 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) { /* 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 == 0U) { 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; } /** * @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 * * @api */ void spi_lld_receive(SPIDriver *spip, size_t n, FIFO *rx_fifo) { /* Starting transfer.*/ spi_dspi_start(spip); SPC5_SPI_TXRX_IN_PROGRESS(spip); /* DMAs require a different setup depending on the frame size.*/ if (spip->dspi->CTAR[0].B.FMSZ < 8U) { spi_start_rx8(spip, n, rx_fifo); } /* Only for master receive */ if (spip->dspi->MCR.B.MSTR == 1UL) { /* Clock-in the data */ spi_start_tx_ignore(spip, n); } SPC5_SPI_WAIT_FOR_TXRX_COMPLETION(spip); } /** * @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 * * @api */ void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf) { /* Starting transfer.*/ spi_dspi_start(spip); SPC5_SPI_TXRX_IN_PROGRESS(spip); /* Only for slave transmit */ if (spip->dspi->MCR.B.MSTR == 0UL) { spi_start_rx_ignore(spip, n); } if (spip->dspi->CTAR[0].B.FMSZ < 8U) { spi_start_tx8(spip, n, txbuf); } SPC5_SPI_WAIT_FOR_TXRX_COMPLETION(spip); } /** * @brief Shared ISR for RFDF DSPI events. * * @param[in] spip pointer to the @p SPIDriver object * * @notapi */ static void spi_serve_dspi_rfdf(SPIDriver *spip) { /* valid only in slave mode */ uint32_t cs0; /* Emptying the RX FIFO.*/ if (spip->dspi->MCR.B.MSTR == 0U) { // slave mode while ((spip->rx_cnt > 0U) && (spip->dspi->SR.B.RXCTR > 0U)) { uint32_t frame = spip->dspi->POPR.R; //if (spip->rx_ptr != NULL) { fifo_write(spip->spi_rx_fifo, frame); //} spip->rx_cnt--; } } else { //master mode while ((spip->rx_cnt > 0U) && (spip->dspi->SR.B.RXCTR > 0U)) { uint32_t frame = spip->dspi->POPR.R; //if (spip->rx_ptr != NULL) { fifo_write(spip->spi_rx_fifo, frame); //} spip->rx_cnt--; } } /* Interrupt served.*/ spip->dspi->SR.R = SPC5_RSER_RFDF_RE; if (spip->dspi->MCR.B.MSTR == 1U) { /* this DSPI is in master mode */ cs0 = 0U; } else { /* this DSPI is in slave mode, actually assuming CS0 is active low */ cs0 = pal_readpad(spip->config->ssport, spip->config->sspad); } if ((cs0 != 0U) || (spip->rx_cnt == 0U)) { /* Done with transfer, disable interrupt */ spip->dspi->RSER.B.RFDFRE = 0; /* Stops the transfer.*/ spi_dspi_stop(spip); /* Call the related callback.*/ if (spip->config->end_cb != NULL) { spip->config->end_cb(spip); } SPC5_SPI_TXRX_DONE(spip); } } /** * @brief Shared ISR for EOQF DSPI events. * * @param[in] spip pointer to the @p SPIDriver object * * @notapi */ static void spi_serve_dspi_eoqf(SPIDriver *spip) { /* Emptying the RX FIFO.*/ while ((spip->rx_cnt > 0U) && (spip->dspi->SR.B.RXCTR > 0U)) { uint32_t frame = spip->dspi->POPR.R; //if (spip->rx_ptr != NULL) { fifo_write(spip->spi_rx_fifo, frame); //} spip->rx_cnt--; } /* Interrupt served.*/ spip->dspi->SR.R = SPC5_RSER_EOQF_RE; /* Done with transfer, disable interrupt */ spip->dspi->RSER.B.EOQFRE = 0; if (spip->rx_cnt == 0U) { /* Stops the transfer.*/ spi_dspi_stop(spip); /* Call the related callback.*/ if (spip->config->end_cb != NULL) { spip->config->end_cb(spip); } SPC5_SPI_TXRX_DONE(spip); } } /** * @brief Shared ISR for TFFF DSPI events. * * @param[in] spip pointer to the @p SPIDriver object * * @notapi */ static void spi_serve_dspi_tfff(SPIDriver *spip) { if (spip->tx_cnt > 0U) { /* 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 < 8U) { 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.R = SPC5_RSER_TFFF_RE; /* Done with transfer, disable interrupt */ if (spip->tx_cnt == 0U) { spip->dspi->RSER.B.TFFFRE = 0; } } /** * @brief Deactivates the SPI peripheral. * * @param[in] spip pointer to the @p SPIDriver object * * @api */ void spi_lld_stop(SPIDriver *spip) { /* Resets the peripheral.*/ spip->dspi->CTAR[0].R = 0; spip->dspi->RSER.R = 0; spip->dspi->SR.R = 0xFFFFFFFFUL; spip->dspi->MCR.R |= SPC5_MCR_HALT | SPC5_MCR_CLR_TXF | SPC5_MCR_CLR_RXF; spip->dspi->MCR.B.MDIS = 1; } /*===========================================================================*/ /* Driver interrupt handlers. */ /*===========================================================================*/ /** * @brief DSPI0 RFDF interrupt handler. * * @isr */ IRQ_HANDLER(SPC5_DSPI0_RFDF_HANDLER) { IRQ_PROLOGUE(); spi_serve_dspi_rfdf(&SPID1); IRQ_EPILOGUE(); } /** * @brief DSPI0 EOQF interrupt handler. * * @isr */ IRQ_HANDLER(SPC5_DSPI0_EOQF_HANDLER) { IRQ_PROLOGUE(); spi_serve_dspi_eoqf(&SPID1); IRQ_EPILOGUE(); } /** * @brief DSPI0 TFFF interrupt handler. * * @isr */ IRQ_HANDLER(SPC5_DSPI0_TFFF_HANDLER) { IRQ_PROLOGUE(); spi_serve_dspi_tfff(&SPID1); IRQ_EPILOGUE(); } /** * @brief DSPI1 RFDF interrupt handler. * * @isr */ IRQ_HANDLER(SPC5_DSPI1_RFDF_HANDLER) { IRQ_PROLOGUE(); spi_serve_dspi_rfdf(&SPID2); IRQ_EPILOGUE(); } /** * @brief DSPI1 EOQF interrupt handler. * * @isr */ IRQ_HANDLER(SPC5_DSPI1_EOQF_HANDLER) { IRQ_PROLOGUE(); spi_serve_dspi_eoqf(&SPID2); IRQ_EPILOGUE(); } /** * @brief DSPI1 TFFF interrupt handler. * * @isr */ IRQ_HANDLER(SPC5_DSPI1_TFFF_HANDLER) { IRQ_PROLOGUE(); spi_serve_dspi_tfff(&SPID2); IRQ_EPILOGUE(); } /*===========================================================================*/