/* * Copyright (c) 2016, Freescale Semiconductor, Inc. * Copyright 2016-2020,2022 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include "fsl_spi.h" #include "fsl_flexcomm.h" /******************************************************************************* * Definitions ******************************************************************************/ /* Component ID definition, used by tools. */ #ifndef FSL_COMPONENT_ID #define FSL_COMPONENT_ID "platform.drivers.flexcomm_spi" #endif /* Note: FIFOCFG[SIZE] has always value 1 = 8 items depth */ #if defined(FSL_FEATURE_SPI_FIFOSIZE_CFG) && (FSL_FEATURE_SPI_FIFOSIZE_CFG) #define SPI_FIFO_DEPTH(base) 4 #else #define SPI_FIFO_DEPTH(base) ((((base)->FIFOCFG & SPI_FIFOCFG_SIZE_MASK) >> SPI_FIFOCFG_SIZE_SHIFT) << 3) #endif /*FSL_FEATURE_SPI_FIFOSIZE_CFG*/ /* Convert transfer count to transfer bytes. dataWidth is a * range <0,15>. Range <8,15> represents 2B transfer */ #define SPI_COUNT_TO_BYTES(dataWidth, count) ((count) << ((dataWidth) >> 3U)) #define SPI_BYTES_TO_COUNT(dataWidth, bytes) ((bytes) >> ((dataWidth) >> 3U)) #if defined(FSL_FEATURE_SPI_IS_SSEL_PIN_COUNT_EQUAL_TO_THREE) && (FSL_FEATURE_SPI_IS_SSEL_PIN_COUNT_EQUAL_TO_THREE) #define SPI_SSELPOL_MASK ((SPI_CFG_SPOL0_MASK) | (SPI_CFG_SPOL1_MASK) | (SPI_CFG_SPOL2_MASK)) #else #if (defined(FSL_FEATURE_SPI_SSEL_COUNT) && (FSL_FEATURE_SPI_SSEL_COUNT == 2)) #define SPI_SSELPOL_MASK ((SPI_CFG_SPOL0_MASK) | (SPI_CFG_SPOL1_MASK)) #else #define SPI_SSELPOL_MASK ((SPI_CFG_SPOL0_MASK) | (SPI_CFG_SPOL1_MASK) | (SPI_CFG_SPOL2_MASK) | (SPI_CFG_SPOL3_MASK)) #endif /* FSL_FEATURE_SPI_SSEL_COUNT == 2 */ #endif /*FSL_FEATURE_SPI_IS_SSEL_PIN_COUNT_EQUAL_TO_THREE*/ /*! * @brief Used for conversion from `flexcomm_irq_handler_t` to `flexcomm_spi_master_irq_handler_t` and * `flexcomm_spi_slave_irq_handler_t`. */ typedef union spi_to_flexcomm { flexcomm_spi_master_irq_handler_t spi_master_handler; flexcomm_spi_slave_irq_handler_t spi_slave_handler; flexcomm_irq_handler_t flexcomm_handler; } spi_to_flexcomm_t; /******************************************************************************* * Variables ******************************************************************************/ /*! @brief internal SPI config array */ static spi_config_t g_configs[FSL_FEATURE_SOC_SPI_COUNT] = {(spi_data_width_t)0}; /*! @brief Array to map SPI instance number to base address. */ static const uint32_t s_spiBaseAddrs[FSL_FEATURE_SOC_SPI_COUNT] = SPI_BASE_ADDRS; /*! @brief IRQ name array */ static const IRQn_Type s_spiIRQ[] = SPI_IRQS; /* @brief Dummy data for each instance. This data is used when user's tx buffer is NULL*/ volatile uint8_t s_dummyData[FSL_FEATURE_SOC_SPI_COUNT] = {0}; /******************************************************************************* * Code ******************************************************************************/ /* Get the index corresponding to the FLEXCOMM */ /*! brief Returns instance number for SPI peripheral base address. */ uint32_t SPI_GetInstance(SPI_Type *base) { uint32_t i; for (i = 0U; i < (uint32_t)FSL_FEATURE_SOC_SPI_COUNT; i++) { if ((uint32_t)base == s_spiBaseAddrs[i]) { break; } } assert(i < (uint32_t)FSL_FEATURE_SOC_SPI_COUNT); return i; } /*! * brief Set up the dummy data. * * param base SPI peripheral address. * param dummyData Data to be transferred when tx buffer is NULL. */ void SPI_SetDummyData(SPI_Type *base, uint8_t dummyData) { uint32_t instance = SPI_GetInstance(base); s_dummyData[instance] = dummyData; } /*! * brief Returns the configurations. * * param base SPI peripheral address. * return return configurations which contain datawidth and SSEL numbers. * return data type is a pointer of spi_config_t. */ void *SPI_GetConfig(SPI_Type *base) { uint32_t instance; instance = SPI_GetInstance(base); return &g_configs[instance]; } /*! * brief Sets the SPI master configuration structure to default values. * * The purpose of this API is to get the configuration structure initialized for use in SPI_MasterInit(). * User may use the initialized structure unchanged in SPI_MasterInit(), or modify * some fields of the structure before calling SPI_MasterInit(). After calling this API, * the master is ready to transfer. * Example: code spi_master_config_t config; SPI_MasterGetDefaultConfig(&config); endcode * * param config pointer to master config structure */ void SPI_MasterGetDefaultConfig(spi_master_config_t *config) { assert(NULL != config); /* Initializes the configure structure to zero. */ (void)memset(config, 0, sizeof(*config)); config->enableLoopback = false; config->enableMaster = true; config->polarity = kSPI_ClockPolarityActiveHigh; config->phase = kSPI_ClockPhaseFirstEdge; config->direction = kSPI_MsbFirst; config->baudRate_Bps = 500000U; config->dataWidth = kSPI_Data8Bits; config->sselNum = kSPI_Ssel0; config->txWatermark = (uint8_t)kSPI_TxFifo0; config->rxWatermark = (uint8_t)kSPI_RxFifo1; config->sselPol = kSPI_SpolActiveAllLow; config->delayConfig.preDelay = 0U; config->delayConfig.postDelay = 0U; config->delayConfig.frameDelay = 0U; config->delayConfig.transferDelay = 0U; } /*! * brief Initializes the SPI with master configuration. * * The configuration structure can be filled by user from scratch, or be set with default * values by SPI_MasterGetDefaultConfig(). After calling this API, the slave is ready to transfer. * Example code spi_master_config_t config = { .baudRate_Bps = 400000, ... }; SPI_MasterInit(SPI0, &config); endcode * * param base SPI base pointer * param config pointer to master configuration structure * param srcClock_Hz Source clock frequency. */ status_t SPI_MasterInit(SPI_Type *base, const spi_master_config_t *config, uint32_t srcClock_Hz) { status_t result = kStatus_Success; uint32_t instance; uint32_t tmpConfig; /* assert params */ assert(!((NULL == base) || (NULL == config) || (0U == srcClock_Hz))); if ((NULL == base) || (NULL == config) || (0U == srcClock_Hz)) { return kStatus_InvalidArgument; } /* initialize flexcomm to SPI mode */ result = FLEXCOMM_Init(base, FLEXCOMM_PERIPH_SPI); if (kStatus_Success != result) { return result; } /* set divider */ result = SPI_MasterSetBaud(base, config->baudRate_Bps, srcClock_Hz); if (kStatus_Success != result) { return result; } /* get instance number */ instance = SPI_GetInstance(base); /* configure SPI mode */ tmpConfig = base->CFG; tmpConfig &= ~(SPI_CFG_MASTER_MASK | SPI_CFG_LSBF_MASK | SPI_CFG_CPHA_MASK | SPI_CFG_CPOL_MASK | SPI_CFG_LOOP_MASK | SPI_CFG_ENABLE_MASK | SPI_SSELPOL_MASK); /* phase */ tmpConfig |= SPI_CFG_CPHA(config->phase); /* polarity */ tmpConfig |= SPI_CFG_CPOL(config->polarity); /* direction */ tmpConfig |= SPI_CFG_LSBF(config->direction); /* master mode */ tmpConfig |= SPI_CFG_MASTER(1); /* loopback */ tmpConfig |= SPI_CFG_LOOP(config->enableLoopback); /* configure active level for all CS */ tmpConfig |= ((uint32_t)config->sselPol & (SPI_SSELPOL_MASK)); base->CFG = tmpConfig; /* store configuration */ g_configs[instance].dataWidth = config->dataWidth; g_configs[instance].sselNum = config->sselNum; /* enable FIFOs */ base->FIFOCFG |= SPI_FIFOCFG_EMPTYTX_MASK | SPI_FIFOCFG_EMPTYRX_MASK; base->FIFOCFG |= SPI_FIFOCFG_ENABLETX_MASK | SPI_FIFOCFG_ENABLERX_MASK; /* trigger level - empty txFIFO, one item in rxFIFO */ tmpConfig = base->FIFOTRIG & (~(SPI_FIFOTRIG_RXLVL_MASK | SPI_FIFOTRIG_TXLVL_MASK)); tmpConfig |= SPI_FIFOTRIG_TXLVL(config->txWatermark) | SPI_FIFOTRIG_RXLVL(config->rxWatermark); /* enable generating interrupts for FIFOTRIG levels */ tmpConfig |= SPI_FIFOTRIG_TXLVLENA_MASK | SPI_FIFOTRIG_RXLVLENA_MASK; /* set FIFOTRIG */ base->FIFOTRIG = tmpConfig; /* Set the delay configuration. */ SPI_SetTransferDelay(base, &config->delayConfig); /* Set the dummy data. */ SPI_SetDummyData(base, (uint8_t)SPI_DUMMYDATA); SPI_Enable(base, config->enableMaster); return kStatus_Success; } /*! * brief Sets the SPI slave configuration structure to default values. * * The purpose of this API is to get the configuration structure initialized for use in SPI_SlaveInit(). * Modify some fields of the structure before calling SPI_SlaveInit(). * Example: code spi_slave_config_t config; SPI_SlaveGetDefaultConfig(&config); endcode * * param config pointer to slave configuration structure */ void SPI_SlaveGetDefaultConfig(spi_slave_config_t *config) { assert(NULL != config); /* Initializes the configure structure to zero. */ (void)memset(config, 0, sizeof(*config)); config->enableSlave = true; config->polarity = kSPI_ClockPolarityActiveHigh; config->phase = kSPI_ClockPhaseFirstEdge; config->direction = kSPI_MsbFirst; config->dataWidth = kSPI_Data8Bits; config->txWatermark = (uint8_t)kSPI_TxFifo0; config->rxWatermark = (uint8_t)kSPI_RxFifo1; config->sselPol = kSPI_SpolActiveAllLow; } /*! * brief Initializes the SPI with slave configuration. * * The configuration structure can be filled by user from scratch or be set with * default values by SPI_SlaveGetDefaultConfig(). * After calling this API, the slave is ready to transfer. * Example code spi_slave_config_t config = { .polarity = flexSPIClockPolarity_ActiveHigh; .phase = flexSPIClockPhase_FirstEdge; .direction = flexSPIMsbFirst; ... }; SPI_SlaveInit(SPI0, &config); endcode * * param base SPI base pointer * param config pointer to slave configuration structure */ status_t SPI_SlaveInit(SPI_Type *base, const spi_slave_config_t *config) { status_t result = kStatus_Success; uint32_t instance; uint32_t tmpConfig; /* assert params */ assert(!((NULL == base) || (NULL == config))); if ((NULL == base) || (NULL == config)) { return kStatus_InvalidArgument; } /* configure flexcomm to SPI, enable clock gate */ result = FLEXCOMM_Init(base, FLEXCOMM_PERIPH_SPI); if (kStatus_Success != result) { return result; } instance = SPI_GetInstance(base); /* configure SPI mode */ tmpConfig = base->CFG; tmpConfig &= ~(SPI_CFG_MASTER_MASK | SPI_CFG_LSBF_MASK | SPI_CFG_CPHA_MASK | SPI_CFG_CPOL_MASK | SPI_CFG_ENABLE_MASK | SPI_SSELPOL_MASK); /* phase */ tmpConfig |= SPI_CFG_CPHA(config->phase); /* polarity */ tmpConfig |= SPI_CFG_CPOL(config->polarity); /* direction */ tmpConfig |= SPI_CFG_LSBF(config->direction); /* configure active level for all CS */ tmpConfig |= ((uint32_t)config->sselPol & (SPI_SSELPOL_MASK)); base->CFG = tmpConfig; /* store configuration */ g_configs[instance].dataWidth = config->dataWidth; /* empty and enable FIFOs */ base->FIFOCFG |= SPI_FIFOCFG_EMPTYTX_MASK | SPI_FIFOCFG_EMPTYRX_MASK; base->FIFOCFG |= SPI_FIFOCFG_ENABLETX_MASK | SPI_FIFOCFG_ENABLERX_MASK; /* trigger level - empty txFIFO, one item in rxFIFO */ tmpConfig = base->FIFOTRIG & (~(SPI_FIFOTRIG_RXLVL_MASK | SPI_FIFOTRIG_TXLVL_MASK)); tmpConfig |= SPI_FIFOTRIG_TXLVL(config->txWatermark) | SPI_FIFOTRIG_RXLVL(config->rxWatermark); /* enable generating interrupts for FIFOTRIG levels */ tmpConfig |= SPI_FIFOTRIG_TXLVLENA_MASK | SPI_FIFOTRIG_RXLVLENA_MASK; /* set FIFOTRIG */ base->FIFOTRIG = tmpConfig; SPI_SetDummyData(base, (uint8_t)SPI_DUMMYDATA); SPI_Enable(base, config->enableSlave); return kStatus_Success; } /*! * brief De-initializes the SPI. * * Calling this API resets the SPI module, gates the SPI clock. * The SPI module can't work unless calling the SPI_MasterInit/SPI_SlaveInit to initialize module. * * param base SPI base pointer */ void SPI_Deinit(SPI_Type *base) { /* Assert arguments */ assert(NULL != base); /* Disable interrupts, disable dma requests, disable peripheral */ base->FIFOINTENCLR = SPI_FIFOINTENCLR_TXERR_MASK | SPI_FIFOINTENCLR_RXERR_MASK | SPI_FIFOINTENCLR_TXLVL_MASK | SPI_FIFOINTENCLR_RXLVL_MASK; base->FIFOCFG &= ~(SPI_FIFOCFG_DMATX_MASK | SPI_FIFOCFG_DMARX_MASK); base->CFG &= ~(SPI_CFG_ENABLE_MASK); } /*! * brief Enables the DMA request from SPI txFIFO. * * param base SPI base pointer * param enable True means enable DMA, false means disable DMA */ void SPI_EnableTxDMA(SPI_Type *base, bool enable) { if (enable) { base->FIFOCFG |= SPI_FIFOCFG_DMATX_MASK; } else { base->FIFOCFG &= ~SPI_FIFOCFG_DMATX_MASK; } } /*! * brief Enables the DMA request from SPI rxFIFO. * * param base SPI base pointer * param enable True means enable DMA, false means disable DMA */ void SPI_EnableRxDMA(SPI_Type *base, bool enable) { if (enable) { base->FIFOCFG |= SPI_FIFOCFG_DMARX_MASK; } else { base->FIFOCFG &= ~SPI_FIFOCFG_DMARX_MASK; } } /*! * brief Sets the baud rate for SPI transfer. This is only used in master. * * param base SPI base pointer * param baudrate_Bps baud rate needed in Hz. * param srcClock_Hz SPI source clock frequency in Hz. */ status_t SPI_MasterSetBaud(SPI_Type *base, uint32_t baudrate_Bps, uint32_t srcClock_Hz) { uint32_t tmpDiv; /* assert params */ assert(!((NULL == base) || (0U == baudrate_Bps) || (0U == srcClock_Hz))); if ((NULL == base) || (0U == baudrate_Bps) || (0U == srcClock_Hz)) { return kStatus_InvalidArgument; } /* calculate baudrate, round up the result */ tmpDiv = ((srcClock_Hz * 10U) / baudrate_Bps + 5U) / 10U - 1U; if (tmpDiv > 0xFFFFU) { return kStatus_SPI_BaudrateNotSupport; } base->DIV &= ~SPI_DIV_DIVVAL_MASK; base->DIV |= SPI_DIV_DIVVAL(tmpDiv); return kStatus_Success; } /*! * brief Writes a data into the SPI data register. * * param base SPI base pointer * param data needs to be write. * param configFlags transfer configuration options ref spi_xfer_option_t */ void SPI_WriteData(SPI_Type *base, uint16_t data, uint32_t configFlags) { uint32_t control = 0U; uint32_t instance; /* check params */ assert(NULL != base); /* get and check instance */ instance = SPI_GetInstance(base); /* set data width */ control |= (uint32_t)SPI_FIFOWR_LEN((g_configs[instance].dataWidth)); /* set sssel */ control |= (SPI_DEASSERT_ALL & (~SPI_DEASSERTNUM_SSEL((uint32_t)(g_configs[instance].sselNum)))); /* mask configFlags */ control |= (configFlags & (uint32_t)SPI_FIFOWR_FLAGS_MASK); /* control should not affect lower 16 bits */ assert(0U == (control & 0xFFFFU)); base->FIFOWR = data | control; } /*! * brief Initializes the SPI master handle. * * This function initializes the SPI master handle which can be used for other SPI master transactional APIs. Usually, * for a specified SPI instance, call this API once to get the initialized handle. * * param base SPI peripheral base address. * param handle SPI handle pointer. * param callback Callback function. * param userData User data. */ status_t SPI_MasterTransferCreateHandle(SPI_Type *base, spi_master_handle_t *handle, spi_master_callback_t callback, void *userData) { /* check 'base' */ assert(NULL != base); /* check 'handle' */ assert(NULL != handle); uint32_t instance; spi_to_flexcomm_t handler; /* get flexcomm instance by 'base' param */ instance = SPI_GetInstance(base); (void)memset(handle, 0, sizeof(*handle)); /* Initialize the handle */ if ((base->CFG & SPI_CFG_MASTER_MASK) != 0U) { handler.spi_master_handler = SPI_MasterTransferHandleIRQ; FLEXCOMM_SetIRQHandler(base, handler.flexcomm_handler, handle); } else { handler.spi_slave_handler = SPI_SlaveTransferHandleIRQ; FLEXCOMM_SetIRQHandler(base, handler.flexcomm_handler, handle); } handle->dataWidth = (uint8_t)(g_configs[instance].dataWidth); /* in slave mode, the sselNum is not important */ handle->sselNum = (uint8_t)(g_configs[instance].sselNum); handle->txWatermark = (uint8_t)SPI_FIFOTRIG_TXLVL_GET(base); handle->rxWatermark = (uint8_t)SPI_FIFOTRIG_RXLVL_GET(base); handle->callback = callback; handle->userData = userData; /* Enable SPI NVIC */ (void)EnableIRQ(s_spiIRQ[instance]); return kStatus_Success; } /*! * brief Transfers a block of data using a polling method. * * param base SPI base pointer * param xfer pointer to spi_xfer_config_t structure * retval kStatus_Success Successfully start a transfer. * retval kStatus_InvalidArgument Input argument is invalid. * retval kStatus_SPI_Timeout The transfer timed out and was aborted. */ status_t SPI_MasterTransferBlocking(SPI_Type *base, spi_transfer_t *xfer) { uint32_t instance; uint32_t tx_ctrl = 0U, last_ctrl = 0U; uint32_t tmp32, rxRemainingBytes, txRemainingBytes, dataWidth; uint32_t toReceiveCount = 0; uint8_t *txData, *rxData; uint32_t fifoDepth; #if SPI_RETRY_TIMES uint32_t waitTimes = SPI_RETRY_TIMES; #endif /* check params */ assert(!((NULL == base) || (NULL == xfer) || ((NULL == xfer->txData) && (NULL == xfer->rxData)))); if ((NULL == base) || (NULL == xfer) || ((NULL == xfer->txData) && (NULL == xfer->rxData))) { return kStatus_InvalidArgument; } fifoDepth = SPI_FIFO_DEPTH(base); txData = xfer->txData; rxData = xfer->rxData; txRemainingBytes = (txData != NULL) ? xfer->dataSize : 0U; rxRemainingBytes = (rxData != NULL) ? xfer->dataSize : 0U; instance = SPI_GetInstance(base); dataWidth = (uint32_t)(g_configs[instance].dataWidth); /* dataSize (in bytes) is not aligned to 16bit (2B) transfer */ if ((dataWidth > (uint32_t)kSPI_Data8Bits) && ((xfer->dataSize & 0x1U) != 0U)) { return kStatus_InvalidArgument; } /* clear tx/rx errors and empty FIFOs */ base->FIFOCFG |= SPI_FIFOCFG_EMPTYTX_MASK | SPI_FIFOCFG_EMPTYRX_MASK; base->FIFOSTAT |= SPI_FIFOSTAT_TXERR_MASK | SPI_FIFOSTAT_RXERR_MASK; /* select slave to talk with */ tx_ctrl |= (SPI_DEASSERT_ALL & (~SPI_DEASSERTNUM_SSEL((uint32_t)(g_configs[instance].sselNum)))); /* set width of data - range asserted at entry */ tx_ctrl |= SPI_FIFOWR_LEN(dataWidth); /* delay for frames */ tx_ctrl |= ((xfer->configFlags & (uint32_t)kSPI_FrameDelay) != 0U) ? (uint32_t)kSPI_FrameDelay : 0U; /* end of transfer */ last_ctrl |= ((xfer->configFlags & (uint32_t)kSPI_FrameAssert) != 0U) ? (uint32_t)kSPI_FrameAssert : 0U; /* last index of loop */ while ((txRemainingBytes != 0U) || (rxRemainingBytes != 0U) || (toReceiveCount != 0U)) { #if SPI_RETRY_TIMES if (--waitTimes == 0U) { return kStatus_SPI_Timeout; } #endif /* if rxFIFO is not empty */ if ((base->FIFOSTAT & SPI_FIFOSTAT_RXNOTEMPTY_MASK) != 0U) { tmp32 = base->FIFORD; /* rxBuffer is not empty */ if (rxRemainingBytes != 0U) { *(rxData++) = (uint8_t)tmp32; rxRemainingBytes--; /* read 16 bits at once */ if (dataWidth > 8U) { *(rxData++) = (uint8_t)(tmp32 >> 8); rxRemainingBytes--; } } /* decrease number of data expected to receive */ toReceiveCount -= 1U; } /* transmit if txFIFO is not full and data to receive does not exceed FIFO depth */ if (((base->FIFOSTAT & SPI_FIFOSTAT_TXNOTFULL_MASK) != 0U) && (toReceiveCount < fifoDepth) && ((txRemainingBytes != 0U) || (rxRemainingBytes >= SPI_COUNT_TO_BYTES(dataWidth, toReceiveCount + 1U)))) { /* txBuffer is not empty */ if (txRemainingBytes != 0U) { tmp32 = *(txData++); txRemainingBytes--; /* write 16 bit at once */ if (dataWidth > 8U) { tmp32 |= ((uint32_t)(*(txData++))) << 8U; txRemainingBytes--; } if (txRemainingBytes == 0U) { tx_ctrl |= last_ctrl; } } else { tmp32 = (uint32_t)s_dummyData[instance]; tmp32 |= (uint32_t)s_dummyData[instance] << 8U; /* last transfer */ if (rxRemainingBytes == SPI_COUNT_TO_BYTES(dataWidth, toReceiveCount + 1U)) { tx_ctrl |= last_ctrl; } } /* send data */ tmp32 = tx_ctrl | tmp32; base->FIFOWR = tmp32; toReceiveCount += 1U; } } /* wait if TX FIFO of previous transfer is not empty */ #if SPI_RETRY_TIMES waitTimes = SPI_RETRY_TIMES; while ((0U == (base->FIFOSTAT & SPI_FIFOSTAT_TXEMPTY_MASK)) && (0U != --waitTimes)) #else while (0U == (base->FIFOSTAT & SPI_FIFOSTAT_TXEMPTY_MASK)) #endif { } #if SPI_RETRY_TIMES if (waitTimes == 0U) { return kStatus_SPI_Timeout; } #endif return kStatus_Success; } /*! * brief Performs a non-blocking SPI interrupt transfer. * * param base SPI peripheral base address. * param handle pointer to spi_master_handle_t structure which stores the transfer state * param xfer pointer to spi_xfer_config_t structure * retval kStatus_Success Successfully start a transfer. * retval kStatus_InvalidArgument Input argument is invalid. * retval kStatus_SPI_Busy SPI is not idle, is running another transfer. */ status_t SPI_MasterTransferNonBlocking(SPI_Type *base, spi_master_handle_t *handle, spi_transfer_t *xfer) { /* check params */ assert( !((NULL == base) || (NULL == handle) || (NULL == xfer) || ((NULL == xfer->txData) && (NULL == xfer->rxData)))); if ((NULL == base) || (NULL == handle) || (NULL == xfer) || ((NULL == xfer->txData) && (NULL == xfer->rxData))) { return kStatus_InvalidArgument; } /* dataSize (in bytes) is not aligned to 16bit (2B) transfer */ assert(!((handle->dataWidth > (uint8_t)kSPI_Data8Bits) && ((xfer->dataSize & 0x1U) != 0U))); if ((handle->dataWidth > (uint8_t)kSPI_Data8Bits) && ((xfer->dataSize & 0x1U) != 0U)) { return kStatus_InvalidArgument; } /* Check if SPI is busy */ if (handle->state == (uint32_t)kStatus_SPI_Busy) { return kStatus_SPI_Busy; } /* Set the handle information */ handle->txData = xfer->txData; handle->rxData = xfer->rxData; /* set count */ handle->txRemainingBytes = (xfer->txData != NULL) ? xfer->dataSize : 0U; handle->rxRemainingBytes = (xfer->rxData != NULL) ? xfer->dataSize : 0U; handle->totalByteCount = xfer->dataSize; /* other options */ handle->toReceiveCount = 0; handle->configFlags = xfer->configFlags; /* Set the SPI state to busy */ handle->state = (uint32_t)kStatus_SPI_Busy; /* clear FIFOs when transfer starts */ base->FIFOCFG |= SPI_FIFOCFG_EMPTYTX_MASK | SPI_FIFOCFG_EMPTYRX_MASK; base->FIFOSTAT |= SPI_FIFOSTAT_TXERR_MASK | SPI_FIFOSTAT_RXERR_MASK; /* enable generating txIRQ and rxIRQ, first transfer is fired by empty txFIFO */ base->FIFOINTENSET |= SPI_FIFOINTENSET_TXLVL_MASK | SPI_FIFOINTENSET_RXLVL_MASK; return kStatus_Success; } /*! * brief Transfers a block of data using a polling method. * * This function will do a half-duplex transfer for SPI master, This is a blocking function, * which does not retuen until all transfer have been completed. And data transfer mechanism is half-duplex, * users can set transmit first or receive first. * * param base SPI base pointer * param xfer pointer to spi_half_duplex_transfer_t structure * return status of status_t. */ status_t SPI_MasterHalfDuplexTransferBlocking(SPI_Type *base, spi_half_duplex_transfer_t *xfer) { assert(xfer != NULL); spi_transfer_t tempXfer = {0}; status_t status; if (xfer->isTransmitFirst) { tempXfer.txData = xfer->txData; tempXfer.rxData = NULL; tempXfer.dataSize = xfer->txDataSize; } else { tempXfer.txData = NULL; tempXfer.rxData = xfer->rxData; tempXfer.dataSize = xfer->rxDataSize; } /* If the pcs pin keep assert between transmit and receive. */ if (xfer->isPcsAssertInTransfer) { tempXfer.configFlags = (xfer->configFlags) & (~(uint32_t)kSPI_FrameAssert); } else { tempXfer.configFlags = (xfer->configFlags) | (uint32_t)kSPI_FrameAssert; } status = SPI_MasterTransferBlocking(base, &tempXfer); if (status != kStatus_Success) { return status; } if (xfer->isTransmitFirst) { tempXfer.txData = NULL; tempXfer.rxData = xfer->rxData; tempXfer.dataSize = xfer->rxDataSize; } else { tempXfer.txData = xfer->txData; tempXfer.rxData = NULL; tempXfer.dataSize = xfer->txDataSize; } tempXfer.configFlags = xfer->configFlags; /* SPI transfer blocking. */ status = SPI_MasterTransferBlocking(base, &tempXfer); return status; } /*! * brief Performs a non-blocking SPI interrupt transfer. * * This function using polling way to do the first half transimission and using interrupts to * do the second half transimission, the transfer mechanism is half-duplex. * When do the second half transimission, code will return right away. When all data is transferred, * the callback function is called. * * param base SPI peripheral base address. * param handle pointer to spi_master_handle_t structure which stores the transfer state * param xfer pointer to spi_half_duplex_transfer_t structure * return status of status_t. */ status_t SPI_MasterHalfDuplexTransferNonBlocking(SPI_Type *base, spi_master_handle_t *handle, spi_half_duplex_transfer_t *xfer) { assert(xfer != NULL); assert(handle != NULL); spi_transfer_t tempXfer = {0}; status_t status; if (xfer->isTransmitFirst) { tempXfer.txData = xfer->txData; tempXfer.rxData = NULL; tempXfer.dataSize = xfer->txDataSize; } else { tempXfer.txData = NULL; tempXfer.rxData = xfer->rxData; tempXfer.dataSize = xfer->rxDataSize; } /* If the PCS pin keep assert between transmit and receive. */ if (xfer->isPcsAssertInTransfer) { tempXfer.configFlags = (xfer->configFlags) & (~(uint32_t)kSPI_FrameAssert); } else { tempXfer.configFlags = (xfer->configFlags) | (uint32_t)kSPI_FrameAssert; } status = SPI_MasterTransferBlocking(base, &tempXfer); if (status != kStatus_Success) { return status; } if (xfer->isTransmitFirst) { tempXfer.txData = NULL; tempXfer.rxData = xfer->rxData; tempXfer.dataSize = xfer->rxDataSize; } else { tempXfer.txData = xfer->txData; tempXfer.rxData = NULL; tempXfer.dataSize = xfer->txDataSize; } tempXfer.configFlags = xfer->configFlags; status = SPI_MasterTransferNonBlocking(base, handle, &tempXfer); return status; } /*! * brief Gets the master transfer count. * * This function gets the master transfer count. * * param base SPI peripheral base address. * param handle Pointer to the spi_master_handle_t structure which stores the transfer state. * param count The number of bytes transferred by using the non-blocking transaction. * return status of status_t. */ status_t SPI_MasterTransferGetCount(SPI_Type *base, spi_master_handle_t *handle, size_t *count) { assert(NULL != handle); if (NULL == count) { return kStatus_InvalidArgument; } /* Catch when there is not an active transfer. */ if (handle->state != (uint32_t)kStatus_SPI_Busy) { *count = 0; return kStatus_NoTransferInProgress; } *count = handle->totalByteCount - handle->rxRemainingBytes; return kStatus_Success; } /*! * brief SPI master aborts a transfer using an interrupt. * * This function aborts a transfer using an interrupt. * * param base SPI peripheral base address. * param handle Pointer to the spi_master_handle_t structure which stores the transfer state. */ void SPI_MasterTransferAbort(SPI_Type *base, spi_master_handle_t *handle) { assert(NULL != handle); /* Disable interrupt requests*/ base->FIFOINTENSET &= ~(SPI_FIFOINTENSET_TXLVL_MASK | SPI_FIFOINTENSET_RXLVL_MASK); /* Empty FIFOs */ base->FIFOCFG |= SPI_FIFOCFG_EMPTYTX_MASK | SPI_FIFOCFG_EMPTYRX_MASK; handle->state = (uint32_t)kStatus_SPI_Idle; handle->txRemainingBytes = 0U; handle->rxRemainingBytes = 0U; } static void SPI_TransferHandleIRQInternal(SPI_Type *base, spi_master_handle_t *handle) { uint32_t tx_ctrl = 0U, last_ctrl = 0U, tmp32; bool loopContinue; uint32_t fifoDepth; /* Get flexcomm instance by 'base' param */ uint32_t instance = SPI_GetInstance(base); size_t txRemainingBytes; size_t rxRemainingBytes; uint8_t toReceiveCount; /* check params */ assert((NULL != base) && (NULL != handle) && ((NULL != handle->txData) || (NULL != handle->rxData))); fifoDepth = SPI_FIFO_DEPTH(base); /* select slave to talk with */ tx_ctrl |= ((uint32_t)SPI_DEASSERT_ALL & (uint32_t)SPI_ASSERTNUM_SSEL(handle->sselNum)); /* set width of data */ tx_ctrl |= SPI_FIFOWR_LEN(handle->dataWidth); /* delay for frames */ tx_ctrl |= ((handle->configFlags & (uint32_t)kSPI_FrameDelay) != 0U) ? (uint32_t)kSPI_FrameDelay : 0U; /* end of transfer */ last_ctrl |= ((handle->configFlags & (uint32_t)kSPI_FrameAssert) != 0U) ? (uint32_t)kSPI_FrameAssert : 0U; do { loopContinue = false; /* rxFIFO is not empty */ if ((base->FIFOSTAT & SPI_FIFOSTAT_RXNOTEMPTY_MASK) != 0U) { tmp32 = base->FIFORD; /* rxBuffer is not empty */ if (handle->rxRemainingBytes != 0U) { /* low byte must go first */ *(handle->rxData++) = (uint8_t)tmp32; handle->rxRemainingBytes--; /* read 16 bits at once */ if (handle->dataWidth > (uint8_t)kSPI_Data8Bits) { *(handle->rxData++) = (uint8_t)(tmp32 >> 8); handle->rxRemainingBytes--; } } /* decrease number of data expected to receive */ handle->toReceiveCount -= 1; loopContinue = true; } /* - txFIFO is not full * - we cannot cause rxFIFO overflow by sending more data than is the depth of FIFO * - txBuffer is not empty or the next 'toReceiveCount' data can fit into rxBuffer */ txRemainingBytes = handle->txRemainingBytes; rxRemainingBytes = handle->rxRemainingBytes; toReceiveCount = (handle->toReceiveCount > 0) ? (uint8_t)handle->toReceiveCount : 0U; if (((base->FIFOSTAT & SPI_FIFOSTAT_TXNOTFULL_MASK) != 0U) && ((uint32_t)toReceiveCount < fifoDepth) && ((txRemainingBytes != 0U) || (rxRemainingBytes >= SPI_COUNT_TO_BYTES(handle->dataWidth, (uint32_t)toReceiveCount + 1U)))) { /* txBuffer is not empty */ if ((txRemainingBytes != 0U) && (handle->txData != NULL)) { /* low byte must go first */ tmp32 = *(handle->txData++); handle->txRemainingBytes--; txRemainingBytes = handle->txRemainingBytes; /* write 16 bit at once */ if (handle->dataWidth > (uint8_t)kSPI_Data8Bits) { tmp32 |= ((uint32_t)(*(handle->txData++))) << 8U; handle->txRemainingBytes--; txRemainingBytes = handle->txRemainingBytes; } /* last transfer */ if (handle->txRemainingBytes == 0U) { tx_ctrl |= last_ctrl; } } else { tmp32 = (uint32_t)s_dummyData[instance]; tmp32 |= (uint32_t)s_dummyData[instance] << 8U; /* last transfer */ if (rxRemainingBytes == SPI_COUNT_TO_BYTES(handle->dataWidth, (uint32_t)toReceiveCount + 1U)) { tx_ctrl |= last_ctrl; } } /* send data */ tmp32 = tx_ctrl | tmp32; base->FIFOWR = tmp32; /* increase number of expected data to receive */ handle->toReceiveCount += 1; toReceiveCount = (handle->toReceiveCount > 0) ? (uint8_t)handle->toReceiveCount : 0U; loopContinue = true; } } while (loopContinue); } /*! * brief Interrupts the handler for the SPI. * * param base SPI peripheral base address. * param handle pointer to spi_master_handle_t structure which stores the transfer state. */ void SPI_MasterTransferHandleIRQ(SPI_Type *base, spi_master_handle_t *handle) { assert((NULL != base) && (NULL != handle)); size_t txRemainingBytes; uint8_t toReceiveCount; /* IRQ behaviour: * - first interrupt is triggered by empty txFIFO. The transfer function * then tries empty rxFIFO and fill txFIFO interleaved that results to * strategy to process as many items as possible. * - the next IRQs can be: * rxIRQ from nonempty rxFIFO which requires to empty rxFIFO. * txIRQ from empty txFIFO which requires to refill txFIFO. * - last interrupt is triggered by empty txFIFO. The last state is * known by empty rxBuffer and txBuffer. If there is nothing to receive * or send - both operations have been finished and interrupts can be * disabled. */ /* Data to send or read or expected to receive */ if ((handle->txRemainingBytes != 0U) || (handle->rxRemainingBytes != 0U) || (handle->toReceiveCount != 0)) { /* Transmit or receive data */ SPI_TransferHandleIRQInternal(base, handle); /* No data to send or read or receive. Transfer ends. Set txTrigger to 0 level and * enable txIRQ to confirm when txFIFO becomes empty */ if ((0U == handle->txRemainingBytes) && (0U == handle->rxRemainingBytes) && (0 == handle->toReceiveCount)) { base->FIFOTRIG = base->FIFOTRIG & (~SPI_FIFOTRIG_TXLVL_MASK); base->FIFOINTENSET |= SPI_FIFOINTENSET_TXLVL_MASK; } else { uint32_t rxRemainingCount = SPI_BYTES_TO_COUNT(handle->dataWidth, handle->rxRemainingBytes); /* If, there are no data to send or rxFIFO is already filled with necessary number of dummy data, * disable txIRQ. From this point only rxIRQ is used to receive data without any transmission */ toReceiveCount = (handle->toReceiveCount > 0) ? (uint8_t)handle->toReceiveCount : 0U; if ((0U == handle->txRemainingBytes) && (rxRemainingCount <= toReceiveCount)) { base->FIFOINTENCLR = SPI_FIFOINTENCLR_TXLVL_MASK; } /* Nothing to receive or transmit, but we still have pending data which are bellow rxLevel. * Cannot clear rxFIFO, txFIFO might be still active */ if (rxRemainingCount == 0U) { txRemainingBytes = handle->txRemainingBytes; if ((txRemainingBytes == 0U) && (toReceiveCount != 0U) && (toReceiveCount < SPI_FIFOTRIG_RXLVL_GET(base) + 1U)) { base->FIFOTRIG = (base->FIFOTRIG & (~SPI_FIFOTRIG_RXLVL_MASK)) | SPI_FIFOTRIG_RXLVL((uint32_t)toReceiveCount - 1U); } } else { /* Expected to receive less data than rxLevel value, we have to update rxLevel */ if (rxRemainingCount < (SPI_FIFOTRIG_RXLVL_GET(base) + 1U)) { base->FIFOTRIG = (base->FIFOTRIG & (~SPI_FIFOTRIG_RXLVL_MASK)) | SPI_FIFOTRIG_RXLVL(rxRemainingCount - 1U); } } } } else { /* Empty txFIFO is confirmed. Disable IRQs and restore triggers values */ base->FIFOINTENCLR = SPI_FIFOINTENCLR_RXLVL_MASK | SPI_FIFOINTENCLR_TXLVL_MASK; base->FIFOTRIG = (base->FIFOTRIG & (~(SPI_FIFOTRIG_RXLVL_MASK | SPI_FIFOTRIG_RXLVL_MASK))) | SPI_FIFOTRIG_RXLVL(handle->rxWatermark) | SPI_FIFOTRIG_TXLVL(handle->txWatermark); /* set idle state and call user callback */ handle->state = (uint32_t)kStatus_SPI_Idle; if (handle->callback != NULL) { (handle->callback)(base, handle, handle->state, handle->userData); } } }