| /* |
| * Copyright (c) 2016, Freescale Semiconductor, Inc. |
| * Copyright 2016-2017 NXP |
| * |
| * Redistribution and use in source and binary forms, with or without modification, |
| * are permitted provided that the following conditions are met: |
| * |
| * o Redistributions of source code must retain the above copyright notice, this list |
| * of conditions and the following disclaimer. |
| * |
| * o Redistributions in binary form must reproduce the above copyright notice, this |
| * list of conditions and the following disclaimer in the documentation and/or |
| * other materials provided with the distribution. |
| * |
| * o Neither the name of the copyright holder nor the names of its |
| * contributors may be used to endorse or promote products derived from this |
| * software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR |
| * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
| * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "fsl_lpc_spi.h" |
| #include "fsl_lpc_flexcomm.h" |
| |
| /******************************************************************************* |
| * Definitons |
| ******************************************************************************/ |
| /* Note: FIFOCFG[SIZE] has always value 1 = 8 items depth */ |
| #define SPI_FIFO_DEPTH(base) ((((base)->FIFOCFG & SPI_FIFOCFG_SIZE_MASK) >> SPI_FIFOCFG_SIZE_SHIFT) << 3) |
| |
| /* 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)) |
| |
| /******************************************************************************* |
| * 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; |
| |
| /******************************************************************************* |
| * Code |
| ******************************************************************************/ |
| |
| /* Get the index corresponding to the FLEXCOMM */ |
| uint32_t SPI_GetInstance(SPI_Type *base) |
| { |
| int i; |
| |
| for (i = 0; i < FSL_FEATURE_SOC_SPI_COUNT; i++) |
| { |
| if ((uint32_t)base == s_spiBaseAddrs[i]) |
| { |
| return i; |
| } |
| } |
| |
| assert(false); |
| return 0; |
| } |
| |
| void *SPI_GetConfig(SPI_Type *base) |
| { |
| int32_t instance; |
| instance = SPI_GetInstance(base); |
| if (instance < 0) |
| { |
| return NULL; |
| } |
| return &g_configs[instance]; |
| } |
| |
| void SPI_MasterGetDefaultConfig(spi_master_config_t *config) |
| { |
| assert(NULL != 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 = kSPI_TxFifo0; |
| config->rxWatermark = kSPI_RxFifo1; |
| } |
| |
| status_t SPI_MasterInit(SPI_Type *base, const spi_master_config_t *config, uint32_t srcClock_Hz) |
| { |
| int32_t result = 0, instance = 0; |
| uint32_t tmp; |
| |
| /* assert params */ |
| assert(!((NULL == base) || (NULL == config) || (0 == srcClock_Hz))); |
| if ((NULL == base) || (NULL == config) || (0 == srcClock_Hz)) |
| { |
| return kStatus_InvalidArgument; |
| } |
| |
| /* initialize flexcomm to SPI mode */ |
| result = FLEXCOMM_Init(base, FLEXCOMM_PERIPH_SPI); |
| assert(kStatus_Success == result); |
| 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); |
| assert(instance >= 0); |
| |
| /* configure SPI mode */ |
| tmp = base->CFG; |
| tmp &= ~(SPI_CFG_MASTER_MASK | SPI_CFG_LSBF_MASK | SPI_CFG_CPHA_MASK | SPI_CFG_CPOL_MASK | SPI_CFG_LOOP_MASK | SPI_CFG_ENABLE_MASK); |
| /* phase */ |
| tmp |= SPI_CFG_CPHA(config->phase); |
| /* polarity */ |
| tmp |= SPI_CFG_CPOL(config->polarity); |
| /* direction */ |
| tmp |= SPI_CFG_LSBF(config->direction); |
| /* master mode */ |
| tmp |= SPI_CFG_MASTER(1); |
| /* loopback */ |
| tmp |= SPI_CFG_LOOP(config->enableLoopback); |
| base->CFG = tmp; |
| |
| /* 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 */ |
| tmp = base->FIFOTRIG & (~(SPI_FIFOTRIG_RXLVL_MASK | SPI_FIFOTRIG_TXLVL_MASK)); |
| tmp |= SPI_FIFOTRIG_TXLVL(config->txWatermark) | SPI_FIFOTRIG_RXLVL(config->rxWatermark); |
| /* enable generating interrupts for FIFOTRIG levels */ |
| tmp |= SPI_FIFOTRIG_TXLVLENA_MASK | SPI_FIFOTRIG_RXLVLENA_MASK; |
| /* set FIFOTRIG */ |
| base->FIFOTRIG = tmp; |
| |
| SPI_Enable(base, config->enableMaster); |
| return kStatus_Success; |
| } |
| |
| void SPI_SlaveGetDefaultConfig(spi_slave_config_t *config) |
| { |
| assert(NULL != config); |
| |
| config->enableSlave = true; |
| config->polarity = kSPI_ClockPolarityActiveHigh; |
| config->phase = kSPI_ClockPhaseFirstEdge; |
| config->direction = kSPI_MsbFirst; |
| config->dataWidth = kSPI_Data8Bits; |
| config->txWatermark = kSPI_TxFifo0; |
| config->rxWatermark = kSPI_RxFifo1; |
| } |
| |
| status_t SPI_SlaveInit(SPI_Type *base, const spi_slave_config_t *config) |
| { |
| int32_t result = 0, instance; |
| uint32_t tmp; |
| |
| /* 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); |
| assert(kStatus_Success == result); |
| if (kStatus_Success != result) |
| { |
| return result; |
| } |
| |
| instance = SPI_GetInstance(base); |
| |
| /* configure SPI mode */ |
| tmp = base->CFG; |
| tmp &= ~(SPI_CFG_MASTER_MASK | SPI_CFG_LSBF_MASK | SPI_CFG_CPHA_MASK | SPI_CFG_CPOL_MASK | SPI_CFG_ENABLE_MASK); |
| /* phase */ |
| tmp |= SPI_CFG_CPHA(config->phase); |
| /* polarity */ |
| tmp |= SPI_CFG_CPOL(config->polarity); |
| /* direction */ |
| tmp |= SPI_CFG_LSBF(config->direction); |
| base->CFG = tmp; |
| |
| /* 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 */ |
| tmp = base->FIFOTRIG & (~(SPI_FIFOTRIG_RXLVL_MASK | SPI_FIFOTRIG_TXLVL_MASK)); |
| tmp |= SPI_FIFOTRIG_TXLVL(config->txWatermark) | SPI_FIFOTRIG_RXLVL(config->rxWatermark); |
| /* enable generating interrupts for FIFOTRIG levels */ |
| tmp |= SPI_FIFOTRIG_TXLVLENA_MASK | SPI_FIFOTRIG_RXLVLENA_MASK; |
| /* set FIFOTRIG */ |
| base->FIFOTRIG = tmp; |
| |
| SPI_Enable(base, config->enableSlave); |
| return kStatus_Success; |
| } |
| |
| 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); |
| } |
| |
| void SPI_EnableTxDMA(SPI_Type *base, bool enable) |
| { |
| if (enable) |
| { |
| base->FIFOCFG |= SPI_FIFOCFG_DMATX_MASK; |
| } |
| else |
| { |
| base->FIFOCFG &= ~SPI_FIFOCFG_DMATX_MASK; |
| } |
| } |
| |
| void SPI_EnableRxDMA(SPI_Type *base, bool enable) |
| { |
| if (enable) |
| { |
| base->FIFOCFG |= SPI_FIFOCFG_DMARX_MASK; |
| } |
| else |
| { |
| base->FIFOCFG &= ~SPI_FIFOCFG_DMARX_MASK; |
| } |
| } |
| |
| status_t SPI_MasterSetBaud(SPI_Type *base, uint32_t baudrate_Bps, uint32_t srcClock_Hz) |
| { |
| uint32_t tmp; |
| |
| /* assert params */ |
| assert(!((NULL == base) || (0 == baudrate_Bps) || (0 == srcClock_Hz))); |
| if ((NULL == base) || (0 == baudrate_Bps) || (0 == srcClock_Hz)) |
| { |
| return kStatus_InvalidArgument; |
| } |
| |
| /* calculate baudrate */ |
| tmp = (srcClock_Hz / baudrate_Bps) - 1; |
| if (tmp > 0xFFFF) |
| { |
| return kStatus_SPI_BaudrateNotSupport; |
| } |
| base->DIV &= ~SPI_DIV_DIVVAL_MASK; |
| base->DIV |= SPI_DIV_DIVVAL(tmp); |
| return kStatus_Success; |
| } |
| |
| void SPI_WriteData(SPI_Type *base, uint16_t data, uint32_t configFlags) |
| { |
| uint32_t control = 0; |
| int32_t instance; |
| |
| /* check params */ |
| assert(NULL != base); |
| /* get and check instance */ |
| instance = SPI_GetInstance(base); |
| assert(!(instance < 0)); |
| if (instance < 0) |
| { |
| return; |
| } |
| |
| /* set data width */ |
| control |= SPI_FIFOWR_LEN(g_configs[instance].dataWidth); |
| /* set sssel */ |
| control |= (SPI_DEASSERT_ALL & (~SPI_DEASSERTNUM_SSEL(g_configs[instance].sselNum))); |
| /* mask configFlags */ |
| control |= (configFlags & SPI_FIFOWR_FLAGS_MASK); |
| /* control should not affect lower 16 bits */ |
| assert(!(control & 0xFFFF)); |
| base->FIFOWR = data | control; |
| } |
| |
| status_t SPI_MasterTransferCreateHandle(SPI_Type *base, |
| spi_master_handle_t *handle, |
| spi_master_callback_t callback, |
| void *userData) |
| { |
| int32_t instance = 0; |
| |
| /* check 'base' */ |
| assert(!(NULL == base)); |
| if (NULL == base) |
| { |
| return kStatus_InvalidArgument; |
| } |
| /* check 'handle' */ |
| assert(!(NULL == handle)); |
| if (NULL == handle) |
| { |
| return kStatus_InvalidArgument; |
| } |
| /* get flexcomm instance by 'base' param */ |
| instance = SPI_GetInstance(base); |
| assert(!(instance < 0)); |
| if (instance < 0) |
| { |
| return kStatus_InvalidArgument; |
| } |
| |
| memset(handle, 0, sizeof(*handle)); |
| /* Initialize the handle */ |
| if (base->CFG & SPI_CFG_MASTER_MASK) |
| { |
| FLEXCOMM_SetIRQHandler(base, (flexcomm_irq_handler_t)(uintptr_t)SPI_MasterTransferHandleIRQ, handle); |
| } |
| else |
| { |
| FLEXCOMM_SetIRQHandler(base, (flexcomm_irq_handler_t)(uintptr_t)SPI_SlaveTransferHandleIRQ, handle); |
| } |
| |
| handle->dataWidth = g_configs[instance].dataWidth; |
| /* in slave mode, the sselNum is not important */ |
| handle->sselNum = g_configs[instance].sselNum; |
| handle->txWatermark = (spi_txfifo_watermark_t)SPI_FIFOTRIG_TXLVL_GET(base); |
| handle->rxWatermark = (spi_rxfifo_watermark_t)SPI_FIFOTRIG_RXLVL_GET(base); |
| handle->callback = callback; |
| handle->userData = userData; |
| |
| /* Enable SPI NVIC */ |
| EnableIRQ(s_spiIRQ[instance]); |
| |
| return kStatus_Success; |
| } |
| |
| status_t SPI_MasterTransferBlocking(SPI_Type *base, spi_transfer_t *xfer) |
| { |
| int32_t instance; |
| uint32_t tx_ctrl = 0, last_ctrl = 0; |
| uint32_t tmp32, rxRemainingBytes, txRemainingBytes, dataWidth; |
| uint32_t toReceiveCount = 0; |
| uint8_t *txData, *rxData; |
| uint32_t fifoDepth; |
| |
| /* 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 ? xfer->dataSize : 0; |
| rxRemainingBytes = rxData ? xfer->dataSize : 0; |
| |
| instance = SPI_GetInstance(base); |
| assert(instance >= 0); |
| dataWidth = g_configs[instance].dataWidth; |
| |
| /* dataSize (in bytes) is not aligned to 16bit (2B) transfer */ |
| assert(!((dataWidth > kSPI_Data8Bits) && (xfer->dataSize & 0x1))); |
| if ((dataWidth > kSPI_Data8Bits) && (xfer->dataSize & 0x1)) |
| { |
| 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(g_configs[instance].sselNum))); |
| /* set width of data - range asserted at entry */ |
| tx_ctrl |= SPI_FIFOWR_LEN(dataWidth); |
| /* end of transfer */ |
| last_ctrl |= (xfer->configFlags & (uint32_t)kSPI_FrameAssert) ? (uint32_t)kSPI_FrameAssert : 0; |
| /* delay end of transfer */ |
| last_ctrl |= (xfer->configFlags & (uint32_t)kSPI_FrameDelay) ? (uint32_t)kSPI_FrameDelay : 0; |
| /* last index of loop */ |
| while (txRemainingBytes || rxRemainingBytes || toReceiveCount) |
| { |
| /* if rxFIFO is not empty */ |
| if (base->FIFOSTAT & SPI_FIFOSTAT_RXNOTEMPTY_MASK) |
| { |
| tmp32 = base->FIFORD; |
| /* rxBuffer is not empty */ |
| if (rxRemainingBytes) |
| { |
| *(rxData++) = tmp32; |
| rxRemainingBytes--; |
| /* read 16 bits at once */ |
| if (dataWidth > 8) |
| { |
| *(rxData++) = tmp32 >> 8; |
| rxRemainingBytes--; |
| } |
| } |
| /* decrease number of data expected to receive */ |
| toReceiveCount -= 1; |
| } |
| /* transmit if txFIFO is not full and data to receive does not exceed FIFO depth */ |
| if ((base->FIFOSTAT & SPI_FIFOSTAT_TXNOTFULL_MASK) && (toReceiveCount < fifoDepth) && |
| ((txRemainingBytes) || (rxRemainingBytes >= SPI_COUNT_TO_BYTES(dataWidth, toReceiveCount + 1)))) |
| { |
| /* txBuffer is not empty */ |
| if (txRemainingBytes) |
| { |
| tmp32 = *(txData++); |
| txRemainingBytes--; |
| /* write 16 bit at once */ |
| if (dataWidth > 8) |
| { |
| tmp32 |= ((uint32_t)(*(txData++))) << 8U; |
| txRemainingBytes--; |
| } |
| if (!txRemainingBytes) |
| { |
| tx_ctrl |= last_ctrl; |
| } |
| } |
| else |
| { |
| tmp32 = SPI_DUMMYDATA; |
| /* last transfer */ |
| if (rxRemainingBytes == SPI_COUNT_TO_BYTES(dataWidth, toReceiveCount + 1)) |
| { |
| tx_ctrl |= last_ctrl; |
| } |
| } |
| /* send data */ |
| tmp32 = tx_ctrl | tmp32; |
| base->FIFOWR = tmp32; |
| toReceiveCount += 1; |
| } |
| } |
| /* wait if TX FIFO of previous transfer is not empty */ |
| while (!(base->FIFOSTAT & SPI_FIFOSTAT_TXEMPTY_MASK)) |
| { |
| } |
| return kStatus_Success; |
| } |
| |
| 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 > kSPI_Data8Bits) && (xfer->dataSize & 0x1))); |
| if ((handle->dataWidth > kSPI_Data8Bits) && (xfer->dataSize & 0x1)) |
| { |
| return kStatus_InvalidArgument; |
| } |
| |
| /* Check if SPI is busy */ |
| if (handle->state == 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 ? xfer->dataSize : 0; |
| handle->rxRemainingBytes = xfer->rxData ? xfer->dataSize : 0; |
| handle->totalByteCount = xfer->dataSize; |
| /* other options */ |
| handle->toReceiveCount = 0; |
| handle->configFlags = xfer->configFlags; |
| /* Set the SPI state to busy */ |
| handle->state = 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; |
| } |
| |
| status_t SPI_MasterTransferGetCount(SPI_Type *base, spi_master_handle_t *handle, size_t *count) |
| { |
| assert(NULL != handle); |
| |
| if (!count) |
| { |
| return kStatus_InvalidArgument; |
| } |
| |
| /* Catch when there is not an active transfer. */ |
| if (handle->state != kStatus_SPI_Busy) |
| { |
| *count = 0; |
| return kStatus_NoTransferInProgress; |
| } |
| |
| *count = handle->totalByteCount - handle->rxRemainingBytes; |
| return kStatus_Success; |
| } |
| |
| 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 = kStatus_SPI_Idle; |
| handle->txRemainingBytes = 0; |
| handle->rxRemainingBytes = 0; |
| } |
| |
| static void SPI_TransferHandleIRQInternal(SPI_Type *base, spi_master_handle_t *handle) |
| { |
| uint32_t tx_ctrl = 0, last_ctrl = 0, tmp32; |
| bool loopContinue; |
| uint32_t fifoDepth; |
| |
| /* 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 |= (SPI_DEASSERT_ALL & SPI_ASSERTNUM_SSEL(handle->sselNum)); |
| /* set width of data */ |
| tx_ctrl |= SPI_FIFOWR_LEN(handle->dataWidth); |
| /* end of transfer */ |
| last_ctrl |= (handle->configFlags & (uint32_t)kSPI_FrameAssert) ? (uint32_t)kSPI_FrameAssert : 0; |
| /* delay end of transfer */ |
| last_ctrl |= (handle->configFlags & (uint32_t)kSPI_FrameDelay) ? (uint32_t)kSPI_FrameDelay : 0; |
| do |
| { |
| loopContinue = false; |
| |
| /* rxFIFO is not empty */ |
| if (base->FIFOSTAT & SPI_FIFOSTAT_RXNOTEMPTY_MASK) |
| { |
| tmp32 = base->FIFORD; |
| /* rxBuffer is not empty */ |
| if (handle->rxRemainingBytes) |
| { |
| /* low byte must go first */ |
| *(handle->rxData++) = tmp32; |
| handle->rxRemainingBytes--; |
| /* read 16 bits at once */ |
| if (handle->dataWidth > kSPI_Data8Bits) |
| { |
| *(handle->rxData++) = 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 |
| */ |
| if ((base->FIFOSTAT & SPI_FIFOSTAT_TXNOTFULL_MASK) && (handle->toReceiveCount < fifoDepth) && |
| ((handle->txRemainingBytes) || |
| (handle->rxRemainingBytes >= SPI_COUNT_TO_BYTES(handle->dataWidth, handle->toReceiveCount + 1)))) |
| { |
| /* txBuffer is not empty */ |
| if (handle->txRemainingBytes) |
| { |
| /* low byte must go first */ |
| tmp32 = *(handle->txData++); |
| handle->txRemainingBytes--; |
| /* write 16 bit at once */ |
| if (handle->dataWidth > kSPI_Data8Bits) |
| { |
| tmp32 |= ((uint32_t)(*(handle->txData++))) << 8U; |
| handle->txRemainingBytes--; |
| } |
| /* last transfer */ |
| if (!handle->txRemainingBytes) |
| { |
| tx_ctrl |= last_ctrl; |
| } |
| } |
| else |
| { |
| tmp32 = SPI_DUMMYDATA; |
| /* last transfer */ |
| if (handle->rxRemainingBytes == SPI_COUNT_TO_BYTES(handle->dataWidth, handle->toReceiveCount + 1)) |
| { |
| tx_ctrl |= last_ctrl; |
| } |
| } |
| /* send data */ |
| tmp32 = tx_ctrl | tmp32; |
| base->FIFOWR = tmp32; |
| /* increase number of expected data to receive */ |
| handle->toReceiveCount += 1; |
| loopContinue = true; |
| } |
| } while (loopContinue); |
| } |
| |
| void SPI_MasterTransferHandleIRQ(SPI_Type *base, spi_master_handle_t *handle) |
| { |
| assert((NULL != base) && (NULL != handle)); |
| |
| /* 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) || (handle->rxRemainingBytes) || (handle->toReceiveCount)) |
| { |
| /* 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 ((!handle->txRemainingBytes) && (!handle->rxRemainingBytes) && (!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 */ |
| if ((!handle->txRemainingBytes) && (rxRemainingCount <= handle->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 == 0) |
| { |
| if ((handle->txRemainingBytes == 0) && (handle->toReceiveCount != 0) && |
| (handle->toReceiveCount < SPI_FIFOTRIG_RXLVL_GET(base) + 1)) |
| { |
| base->FIFOTRIG = |
| (base->FIFOTRIG & (~SPI_FIFOTRIG_RXLVL_MASK)) | SPI_FIFOTRIG_RXLVL(handle->toReceiveCount - 1); |
| } |
| } |
| /* Expected to receive less data than rxLevel value, we have to update rxLevel */ |
| else |
| { |
| if (rxRemainingCount < (SPI_FIFOTRIG_RXLVL_GET(base) + 1)) |
| { |
| base->FIFOTRIG = |
| (base->FIFOTRIG & (~SPI_FIFOTRIG_RXLVL_MASK)) | SPI_FIFOTRIG_RXLVL(rxRemainingCount - 1); |
| } |
| } |
| } |
| } |
| 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 = kStatus_SPI_Idle; |
| if (handle->callback) |
| { |
| (handle->callback)(base, handle, handle->state, handle->userData); |
| } |
| } |
| } |