| /* |
| * Copyright (c) 2016, Freescale Semiconductor, Inc. |
| * Copyright 2016-2019 NXP |
| * All rights reserved. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include "fsl_ecspi.h" |
| |
| /******************************************************************************* |
| * Definitions |
| ******************************************************************************/ |
| |
| /* Component ID definition, used by tools. */ |
| #ifndef FSL_COMPONENT_ID |
| #define FSL_COMPONENT_ID "platform.drivers.ecspi" |
| #endif |
| |
| /*! @brief ECSPI transfer state, which is used for ECSPI transactiaonl APIs' internal state. */ |
| enum |
| { |
| kECSPI_Idle = 0x0, /*!< ECSPI is idle state */ |
| kECSPI_Busy /*!< ECSPI is busy tranferring data. */ |
| }; |
| |
| /*! @brief Typedef for ecspi master interrupt handler. ecspi master and slave handle is the same. */ |
| typedef void (*ecspi_isr_t)(ECSPI_Type *base, ecspi_master_handle_t *ecspiHandle); |
| |
| /******************************************************************************* |
| * Prototypes |
| ******************************************************************************/ |
| /*! |
| * @brief Sends a buffer of data bytes in non-blocking way. |
| * |
| * @param base ECSPI base pointer |
| * @param buffer The data bytes to send |
| * @param size The number of data bytes to send |
| */ |
| static void ECSPI_WriteNonBlocking(ECSPI_Type *base, uint32_t *buffer, size_t size); |
| |
| /*! |
| * @brief Receive a buffer of data bytes in non-blocking way. |
| * |
| * @param base ECSPI base pointer |
| * @param buffer The data bytes to send |
| * @param size The number of data bytes to send |
| */ |
| static void ECSPI_ReadNonBlocking(ECSPI_Type *base, uint32_t *buffer, size_t size); |
| |
| /*! |
| * @brief Send a piece of data for ECSPI. |
| * |
| * This function computes the number of data to be written into D register or Tx FIFO, |
| * and write the data into it. At the same time, this function updates the values in |
| * master handle structure. |
| * |
| * @param base ECSPI base pointer |
| * @param handle Pointer to ECSPI master handle structure. |
| */ |
| static void ECSPI_SendTransfer(ECSPI_Type *base, ecspi_master_handle_t *handle); |
| |
| /*! |
| * @brief Receive a piece of data for ECSPI master. |
| * |
| * This function computes the number of data to receive from D register or Rx FIFO, |
| * and write the data to destination address. At the same time, this function updates |
| * the values in master handle structure. |
| * |
| * @param base ECSPI base pointer |
| * @param handle Pointer to ECSPI master handle structure. |
| */ |
| static void ECSPI_ReceiveTransfer(ECSPI_Type *base, ecspi_master_handle_t *handle); |
| |
| /*! |
| * @brief Sets the ECSPI channel configuration structure to default values. |
| * |
| * This function is to get the channel configuration structure initialized for use in ECSPI_SetChannelConfig(). |
| * User may use the initialized structure unchanged in ECSPI_SetChannelConfig(), or modify |
| * some fields of the structure before calling ECSPI_SetChannelConfig(). |
| * |
| * @param config pointer to config structure |
| */ |
| static void ECSPI_GetDefaultChannelConfig(ecspi_channel_config_t *config); |
| |
| /*! |
| * @brief Common IRQ handler for SPI. |
| * |
| * @param base SPI base pointer. |
| * @param instance SPI instance number. |
| */ |
| static void ECSPI_CommonIRQHandler(ECSPI_Type *base, ecspi_master_handle_t *handle); |
| |
| /******************************************************************************* |
| * Variables |
| ******************************************************************************/ |
| /*! @brief Base pointer array */ |
| static ECSPI_Type *const s_ecspiBases[] = ECSPI_BASE_PTRS; |
| /*! @brief ECSPI internal handle pointer array */ |
| static ecspi_master_handle_t *s_ecspiHandle[ARRAY_SIZE(s_ecspiBases)]; |
| /*! @brief IRQ name array */ |
| static const IRQn_Type s_ecspiIRQ[] = ECSPI_IRQS; |
| #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) |
| /*! @brief Clock array name */ |
| static const clock_ip_name_t s_ecspiClock[] = ECSPI_CLOCKS; |
| #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ |
| |
| /*! @brief Pointer to master IRQ handler for each instance. */ |
| static ecspi_isr_t s_ecspiMasterIsr; |
| /*! @brief Pointer to slave IRQ handler for each instance. */ |
| static ecspi_isr_t s_ecspiSlaveIsr; |
| |
| /******************************************************************************* |
| * Code |
| ******************************************************************************/ |
| /*! |
| * brief Get the instance for ECSPI module. |
| * |
| * param base ECSPI base address |
| */ |
| uint32_t ECSPI_GetInstance(ECSPI_Type *base) |
| { |
| uint32_t instance; |
| |
| /* Find the instance index from base address mappings. */ |
| for (instance = 0; instance < ARRAY_SIZE(s_ecspiBases); instance++) |
| { |
| if (s_ecspiBases[instance] == base) |
| { |
| break; |
| } |
| } |
| assert(instance <= ARRAY_SIZE(s_ecspiBases)); |
| return instance; |
| } |
| |
| static void ECSPI_WriteNonBlocking(ECSPI_Type *base, uint32_t *buffer, size_t size) |
| { |
| size_t i = 0U; |
| |
| for (i = 0U; i < size; i++) |
| { |
| if (buffer != NULL) |
| { |
| base->TXDATA = *buffer++; |
| } |
| else |
| { |
| ECSPI_WriteData(base, ECSPI_DUMMYDATA); |
| } |
| } |
| } |
| |
| static void ECSPI_ReadNonBlocking(ECSPI_Type *base, uint32_t *buffer, size_t size) |
| { |
| if (NULL != buffer) |
| { |
| while ((size--) != 0UL) |
| { |
| *buffer++ = ECSPI_ReadData(base); |
| } |
| } |
| else |
| { |
| while ((size--) != 0UL) |
| { |
| (void)ECSPI_ReadData(base); |
| } |
| } |
| } |
| |
| static void ECSPI_SendTransfer(ECSPI_Type *base, ecspi_master_handle_t *handle) |
| { |
| assert(base); |
| assert(handle); |
| |
| uint32_t dataCounts = 0U; |
| uint32_t txRemainingBytes = (uint32_t)(handle->txRemainingBytes); |
| /* Caculate the data size to send */ |
| dataCounts = |
| ((uint32_t)FSL_FEATURE_ECSPI_TX_FIFO_SIZEn(base) - (uint32_t)ECSPI_GetTxFifoCount(base)) < txRemainingBytes ? |
| ((uint32_t)FSL_FEATURE_ECSPI_TX_FIFO_SIZEn(base) - (uint32_t)ECSPI_GetTxFifoCount(base)) : |
| txRemainingBytes; |
| while ((dataCounts--) != 0UL) |
| { |
| ECSPI_WriteNonBlocking(base, handle->txData, 1); |
| if (NULL != handle->txData) |
| { |
| handle->txData += 1U; |
| } |
| handle->txRemainingBytes -= 1U; |
| } |
| } |
| |
| static void ECSPI_ReceiveTransfer(ECSPI_Type *base, ecspi_master_handle_t *handle) |
| { |
| assert(base); |
| |
| uint32_t dataCounts = 0U; |
| /* Caculate the data size need to receive */ |
| dataCounts = |
| (ECSPI_GetRxFifoCount(base) < handle->rxRemainingBytes) ? ECSPI_GetRxFifoCount(base) : handle->rxRemainingBytes; |
| |
| ECSPI_ReadNonBlocking(base, handle->rxData, dataCounts); |
| if (NULL != handle->rxData) |
| { |
| handle->rxData += dataCounts; |
| } |
| handle->rxRemainingBytes -= dataCounts; |
| } |
| static void ECSPI_GetDefaultChannelConfig(ecspi_channel_config_t *config) |
| { |
| /* Initializes the configure structure to zero. */ |
| (void)memset(config, 0, sizeof(*config)); |
| |
| config->channelMode = kECSPI_Slave; /*!< ECSPI peripheral operates in slave mode.*/ |
| config->clockInactiveState = kECSPI_ClockInactiveStateLow; /*!< Clock line (SCLK) inactive state */ |
| config->dataLineInactiveState = kECSPI_DataLineInactiveStateLow; /*!< Data line (MOSI&MISO) inactive state */ |
| config->chipSlectActiveState = kECSPI_ChipSelectActiveStateLow; /*!< Chip select(SS) line active state */ |
| config->waveForm = kECSPI_WaveFormSingle; /*!< ECSPI SS wave form */ |
| config->polarity = kECSPI_PolarityActiveHigh; /*!< Clock polarity */ |
| config->phase = kECSPI_ClockPhaseFirstEdge; /*!< clock phase */ |
| } |
| |
| /*! |
| * brief Sets the ECSPI configuration structure to default values. |
| * |
| * The purpose of this API is to get the configuration structure initialized for use in ECSPI_MasterInit(). |
| * User may use the initialized structure unchanged in ECSPI_MasterInit, or modify |
| * some fields of the structure before calling ECSPI_MasterInit. After calling this API, |
| * the master is ready to transfer. |
| * Example: |
| code |
| ecspi_master_config_t config; |
| ECSPI_MasterGetDefaultConfig(&config); |
| endcode |
| * |
| * param config pointer to config structure |
| */ |
| void ECSPI_MasterGetDefaultConfig(ecspi_master_config_t *config) |
| { |
| /* Initializes the configure structure to zero. */ |
| (void)memset(config, 0, sizeof(*config)); |
| |
| config->channel = kECSPI_Channel0; |
| config->burstLength = 8; |
| config->samplePeriodClock = kECSPI_spiClock; |
| config->baudRate_Bps = 500000; |
| config->chipSelectDelay = 0; |
| config->samplePeriod = 0; |
| config->txFifoThreshold = 1; |
| config->rxFifoThreshold = 0; |
| /* Default configuration of channel */ |
| ECSPI_GetDefaultChannelConfig(&config->channelConfig); |
| /*!< ECSPI peripheral operates in slave mode.*/ |
| config->channelConfig.channelMode = kECSPI_Master; |
| config->enableLoopback = false; |
| } |
| |
| /*! |
| * brief Initializes the ECSPI with configuration. |
| * |
| * The configuration structure can be filled by user from scratch, or be set with default |
| * values by ECSPI_MasterGetDefaultConfig(). After calling this API, the slave is ready to transfer. |
| * Example |
| code |
| ecspi_master_config_t config = { |
| .baudRate_Bps = 400000, |
| ... |
| }; |
| ECSPI_MasterInit(ECSPI0, &config); |
| endcode |
| * |
| * param base ECSPI base pointer |
| * param config pointer to master configuration structure |
| * param srcClock_Hz Source clock frequency. |
| */ |
| void ECSPI_MasterInit(ECSPI_Type *base, const ecspi_master_config_t *config, uint32_t srcClock_Hz) |
| { |
| assert(config && srcClock_Hz); |
| #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) |
| /* Open clock gate for SPI and open interrupt */ |
| CLOCK_EnableClock(s_ecspiClock[ECSPI_GetInstance(base)]); |
| #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ |
| /* Reset control register to default value */ |
| ECSPI_SoftwareReset(base); |
| /* Config CONREG register */ |
| base->CONREG = |
| ECSPI_CONREG_BURST_LENGTH((uint32_t)config->burstLength - 1UL) | ECSPI_CONREG_SMC(1U) | ECSPI_CONREG_EN(1U); |
| /* Config CONFIGREG register */ |
| ECSPI_SetChannelConfig(base, config->channel, &config->channelConfig); |
| /* Config DMAREG register */ |
| base->DMAREG |= |
| ECSPI_DMAREG_TX_THRESHOLD(config->txFifoThreshold) | ECSPI_DMAREG_RX_THRESHOLD(config->rxFifoThreshold); |
| /* Config PERIODREG register */ |
| base->PERIODREG |= ECSPI_PERIODREG_CSRC(config->samplePeriodClock) | |
| ECSPI_PERIODREG_SAMPLE_PERIOD(config->samplePeriod) | |
| ECSPI_PERIODREG_CSD_CTL(config->chipSelectDelay); |
| /* Config TESTING register if enable the loopback function. */ |
| base->TESTREG |= ECSPI_TESTREG_LBC(config->enableLoopback); |
| /* Set baud rate */ |
| ECSPI_SetBaudRate(base, config->baudRate_Bps, srcClock_Hz); |
| } |
| |
| /*! |
| * brief Sets the ECSPI configuration structure to default values. |
| * |
| * The purpose of this API is to get the configuration structure initialized for use in ECSPI_SlaveInit(). |
| * User may use the initialized structure unchanged in ECSPI_SlaveInit(), or modify |
| * some fields of the structure before calling ECSPI_SlaveInit(). After calling this API, |
| * the master is ready to transfer. |
| * Example: |
| code |
| ecspi_Slaveconfig_t config; |
| ECSPI_SlaveGetDefaultConfig(&config); |
| endcode |
| * |
| * param config pointer to config structure |
| */ |
| void ECSPI_SlaveGetDefaultConfig(ecspi_slave_config_t *config) |
| { |
| /* Default configuration of channel nember */ |
| |
| /* Initializes the configure structure to zero. */ |
| (void)memset(config, 0, sizeof(*config)); |
| config->channel = kECSPI_Channel0; |
| |
| config->burstLength = 8; |
| config->txFifoThreshold = 1; |
| config->rxFifoThreshold = 0; |
| /* Set default channel configuration */ |
| ECSPI_GetDefaultChannelConfig(&config->channelConfig); |
| /* ECSPI peripheral operates in slave mode.*/ |
| config->channelConfig.channelMode = kECSPI_Slave; |
| } |
| |
| /*! |
| * brief Initializes the ECSPI with configuration. |
| * |
| * The configuration structure can be filled by user from scratch, or be set with default |
| * values by ECSPI_SlaveGetDefaultConfig(). After calling this API, the slave is ready to transfer. |
| * Example |
| code |
| ecspi_Salveconfig_t config = { |
| .baudRate_Bps = 400000, |
| ... |
| }; |
| ECSPI_SlaveInit(ECSPI1, &config); |
| endcode |
| * |
| * param base ECSPI base pointer |
| * param config pointer to master configuration structure |
| */ |
| void ECSPI_SlaveInit(ECSPI_Type *base, const ecspi_slave_config_t *config) |
| { |
| assert(base && config); |
| |
| #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) |
| /* Open clock gate for SPI and open interrupt */ |
| CLOCK_EnableClock(s_ecspiClock[ECSPI_GetInstance(base)]); |
| #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ |
| |
| /* Reset control register to default value */ |
| ECSPI_SoftwareReset(base); |
| /* Config CONREG register */ |
| base->CONREG = ECSPI_CONREG_BURST_LENGTH(config->burstLength - 1UL) | ECSPI_CONREG_EN(1UL); |
| /* Config DMAREG register */ |
| base->DMAREG |= |
| ECSPI_DMAREG_TX_THRESHOLD(config->txFifoThreshold) | ECSPI_DMAREG_RX_THRESHOLD(config->rxFifoThreshold); |
| /* Setup channel configuration */ |
| ECSPI_SetChannelConfig(base, config->channel, &config->channelConfig); |
| } |
| |
| /*! |
| * brief De-initializes the ECSPI. |
| * |
| * Calling this API resets the ECSPI module, gates the ECSPI clock. |
| * The ECSPI module can't work unless calling the ECSPI_MasterInit/ECSPI_SlaveInit to initialize module. |
| * |
| * param base ECSPI base pointer |
| */ |
| void ECSPI_Deinit(ECSPI_Type *base) |
| { |
| /* Disable ECSPI module before shutting down */ |
| base->CONREG &= ~ECSPI_CONREG_EN_MASK; |
| |
| #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) |
| /* Gate the clock */ |
| CLOCK_DisableClock(s_ecspiClock[ECSPI_GetInstance(base)]); |
| #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ |
| } |
| |
| /*! |
| * brief Sets the baud rate for ECSPI transfer. This is only used in master. |
| * |
| * param base ECSPI base pointer |
| * param baudRate_Bps baud rate needed in Hz. |
| * param srcClock_Hz ECSPI source clock frequency in Hz. |
| */ |
| void ECSPI_SetBaudRate(ECSPI_Type *base, uint32_t baudRate_Bps, uint32_t srcClock_Hz) |
| { |
| assert(base); |
| |
| uint8_t bestPreDividerValue = 0U, preDividerValue = 0U; |
| uint8_t bestPostDividerValue = 0U, postDividerValue = 0U; |
| uint32_t realBaudrate = 0U; |
| uint32_t diff = 0xFFFFFFFFU; |
| uint32_t min_diff = 0xFFFFFFFFU; |
| |
| for (preDividerValue = 0U; preDividerValue < 16U; preDividerValue++) |
| { |
| if (diff == 0U) |
| { |
| break; |
| } |
| |
| for (postDividerValue = 0U; postDividerValue < 16U; postDividerValue++) |
| { |
| if (diff == 0U) |
| { |
| break; |
| } |
| |
| realBaudrate = (srcClock_Hz / (preDividerValue + 1UL)) >> postDividerValue; |
| if (realBaudrate > baudRate_Bps) |
| { |
| diff = realBaudrate - baudRate_Bps; |
| if (diff < min_diff) |
| { |
| min_diff = diff; |
| bestPreDividerValue = preDividerValue; |
| bestPostDividerValue = postDividerValue; |
| } |
| } |
| else |
| { |
| diff = baudRate_Bps - realBaudrate; |
| if (diff < min_diff) |
| { |
| min_diff = diff; |
| bestPreDividerValue = preDividerValue; |
| bestPostDividerValue = postDividerValue; |
| } |
| } |
| } |
| } |
| |
| base->CONREG |= ECSPI_CONREG_PRE_DIVIDER(bestPreDividerValue) | ECSPI_CONREG_POST_DIVIDER(bestPostDividerValue); |
| } |
| |
| /*! |
| * brief Set channel select configuration for transfer. |
| * |
| * The purpose of this API is to set the channel will be use to transfer. |
| * User may use this API after instance has been initialized or before transfer start. |
| * The configuration structure #_ecspi_channel_config_ can be filled by user from scratch. |
| * After calling this API, user can select this channel as transfer channel. |
| * |
| * param base ECSPI base pointer |
| * param channel Channel source. |
| * param config Configuration struct of channel |
| */ |
| void ECSPI_SetChannelConfig(ECSPI_Type *base, ecspi_channel_source_t channel, const ecspi_channel_config_t *config) |
| { |
| switch (channel) |
| { |
| case kECSPI_Channel0: |
| base->CONREG |= ECSPI_CONREG_CHANNEL_MODE(config->channelMode); |
| base->CONFIGREG |= |
| (ECSPI_CONFIGREG_SCLK_CTL(config->clockInactiveState) | |
| ECSPI_CONFIGREG_DATA_CTL(config->dataLineInactiveState) | |
| ECSPI_CONFIGREG_SS_POL(config->chipSlectActiveState) | ECSPI_CONFIGREG_SS_CTL(config->waveForm) | |
| ECSPI_CONFIGREG_SCLK_POL(config->polarity) | ECSPI_CONFIGREG_SCLK_PHA(config->phase)); |
| break; |
| |
| case kECSPI_Channel1: |
| base->CONREG |= ECSPI_CONREG_CHANNEL_MODE(config->channelMode) << 1U; |
| base->CONFIGREG |= |
| ((ECSPI_CONFIGREG_SCLK_CTL(config->clockInactiveState) << 1U) | |
| (ECSPI_CONFIGREG_DATA_CTL(config->dataLineInactiveState) << 1U) | |
| (ECSPI_CONFIGREG_SS_POL(config->chipSlectActiveState) << 1U) | |
| (ECSPI_CONFIGREG_SS_CTL(config->waveForm) << 1U) | (ECSPI_CONFIGREG_SCLK_POL(config->polarity) << 1U) | |
| (ECSPI_CONFIGREG_SCLK_PHA(config->phase) << 1U)); |
| break; |
| |
| case kECSPI_Channel2: |
| base->CONREG |= ECSPI_CONREG_CHANNEL_MODE(config->channelMode) << 2; |
| base->CONFIGREG |= |
| ((ECSPI_CONFIGREG_SCLK_CTL(config->clockInactiveState) << 2) | |
| (ECSPI_CONFIGREG_DATA_CTL(config->dataLineInactiveState) << 2) | |
| (ECSPI_CONFIGREG_SS_POL(config->chipSlectActiveState) << 2) | |
| (ECSPI_CONFIGREG_SS_CTL(config->waveForm) << 2) | (ECSPI_CONFIGREG_SCLK_POL(config->polarity) << 2) | |
| (ECSPI_CONFIGREG_SCLK_PHA(config->phase) << 2)); |
| break; |
| |
| case kECSPI_Channel3: |
| base->CONREG |= ECSPI_CONREG_CHANNEL_MODE(config->channelMode) << 3; |
| base->CONFIGREG |= |
| ((ECSPI_CONFIGREG_SCLK_CTL(config->clockInactiveState) << 3) | |
| (ECSPI_CONFIGREG_DATA_CTL(config->dataLineInactiveState) << 3) | |
| (ECSPI_CONFIGREG_SS_POL(config->chipSlectActiveState) << 3) | |
| (ECSPI_CONFIGREG_SS_CTL(config->waveForm) << 3) | (ECSPI_CONFIGREG_SCLK_POL(config->polarity) << 3) | |
| (ECSPI_CONFIGREG_SCLK_PHA(config->phase) << 3)); |
| break; |
| |
| default: |
| assert(false); |
| break; |
| } |
| } |
| |
| /*! |
| * brief Sends a buffer of data bytes using a blocking method. |
| * |
| * note This function blocks via polling until all bytes have been sent. |
| * |
| * param base ECSPI base pointer |
| * param buffer The data bytes to send |
| * param size The number of data bytes to send |
| * retval kStatus_Success Successfully start a transfer. |
| * retval kStatus_ECSPI_Timeout The transfer timed out and was aborted. |
| */ |
| status_t ECSPI_WriteBlocking(ECSPI_Type *base, uint32_t *buffer, size_t size) |
| { |
| size_t i = 0U; |
| #if SPI_RETRY_TIMES |
| uint32_t waitTimes; |
| #endif |
| |
| while (i < size) |
| { |
| /* Wait for TX fifo buffer empty */ |
| #if SPI_RETRY_TIMES |
| waitTimes = SPI_RETRY_TIMES; |
| while (((base->STATREG & ECSPI_STATREG_TE_MASK) == 0UL) && (--waitTimes != 0U)) |
| #else |
| while ((base->STATREG & ECSPI_STATREG_TE_MASK) == 0UL) |
| #endif |
| { |
| } |
| |
| #if SPI_RETRY_TIMES |
| if (waitTimes == 0U) |
| { |
| return kStatus_ECSPI_Timeout; |
| } |
| #endif |
| |
| /* Write data to tx register */ |
| if (NULL != buffer) |
| { |
| ECSPI_WriteData(base, *buffer++); |
| } |
| else |
| { |
| ECSPI_WriteData(base, ECSPI_DUMMYDATA); |
| } |
| i++; |
| } |
| return kStatus_Success; |
| } |
| |
| static status_t ECSPI_ReadBlocking(ECSPI_Type *base, uint32_t *buffer, size_t size) |
| { |
| assert(base); |
| |
| uint32_t state = 0U; |
| size_t i = 0U; |
| #if SPI_RETRY_TIMES |
| uint32_t waitTimes; |
| #endif |
| |
| while (i < size) |
| { |
| /* Wait for RX FIFO buffer ready */ |
| #if SPI_RETRY_TIMES |
| waitTimes = SPI_RETRY_TIMES; |
| while (((base->STATREG & ECSPI_STATREG_RR_MASK) == 0UL) && (--waitTimes != 0U)) |
| #else |
| while ((base->STATREG & ECSPI_STATREG_RR_MASK) == 0UL) |
| #endif |
| { |
| /* Get status flags of ECSPI */ |
| state = ECSPI_GetStatusFlags(base); |
| /* If hardware overflow happen */ |
| if ((ECSPI_STATREG_RO_MASK & state) != 0UL) |
| { |
| /* Clear overflow flag for next transfer */ |
| ECSPI_ClearStatusFlags(base, kECSPI_RxFifoOverFlowFlag); |
| return kStatus_ECSPI_HardwareOverFlow; |
| } |
| } |
| |
| #if SPI_RETRY_TIMES |
| if (waitTimes == 0U) |
| { |
| return kStatus_ECSPI_Timeout; |
| } |
| #endif |
| |
| /* Read data from rx register */ |
| if (NULL != buffer) |
| { |
| *buffer++ = ECSPI_ReadData(base); |
| } |
| else |
| { |
| (void)ECSPI_ReadData(base); |
| } |
| i++; |
| } |
| return kStatus_Success; |
| } |
| |
| /*! |
| * brief Initializes the ECSPI master handle. |
| * |
| * This function initializes the ECSPI master handle which can be used for other ECSPI master transactional APIs. |
| * Usually, |
| * for a specified ECSPI instance, call this API once to get the initialized handle. |
| * |
| * param base ECSPI peripheral base address. |
| * param handle ECSPI handle pointer. |
| * param callback Callback function. |
| * param userData User data. |
| */ |
| void ECSPI_MasterTransferCreateHandle(ECSPI_Type *base, |
| ecspi_master_handle_t *handle, |
| ecspi_master_callback_t callback, |
| void *userData) |
| { |
| assert(base); |
| assert(handle); |
| |
| uint32_t instance = ECSPI_GetInstance(base); |
| |
| /* Initialize the handle */ |
| s_ecspiHandle[instance] = handle; |
| handle->callback = callback; |
| handle->userData = userData; |
| s_ecspiMasterIsr = ECSPI_MasterTransferHandleIRQ; |
| |
| /* Enable ECSPI NVIC */ |
| (void)EnableIRQ(s_ecspiIRQ[instance]); |
| } |
| |
| /*! |
| * 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_ECSPI_Timeout The transfer timed out and was aborted. |
| */ |
| status_t ECSPI_MasterTransferBlocking(ECSPI_Type *base, ecspi_transfer_t *xfer) |
| { |
| assert(base && xfer); |
| |
| status_t state; |
| uint32_t burstLength = 0U; |
| uint32_t dataCounts = 0U; |
| /* Check if the argument is legal */ |
| if ((xfer->txData == NULL) && (xfer->rxData == NULL)) |
| { |
| return kStatus_InvalidArgument; |
| } |
| /* Select ECSPI channel to current channel |
| * Note: |
| * xfer.channel must be configured before transfer, because every channel has |
| * it's own configuration,if don't configure this parameter, transfer channel |
| * will use the default channel0. |
| */ |
| ECSPI_SetChannelSelect(base, xfer->channel); |
| /* Caculate the data size need to be send for one burst */ |
| burstLength = ((base->CONREG & ECSPI_CONREG_BURST_LENGTH_MASK) >> ECSPI_CONREG_BURST_LENGTH_SHIFT) + 1UL; |
| dataCounts = ((burstLength % 32UL) != 0UL) ? (burstLength / 32UL + 1UL) : (burstLength / 32UL); |
| |
| while (xfer->dataSize > 0UL) |
| { |
| /* ECSPI will transmit and receive at the same time, if txData is NULL, |
| * instance will transmit dummy data, the dummy data can be set by user. |
| * if rxData is NULL, data will be read from RX FIFO buffer, but the |
| * data will be ignored by driver. |
| * Note that, txData and rxData can not be both NULL. |
| */ |
| state = ECSPI_WriteBlocking(base, xfer->txData, dataCounts); |
| if (kStatus_Success != state) |
| { |
| return state; |
| } |
| if (NULL != xfer->txData) |
| { |
| xfer->txData += dataCounts; |
| } |
| state = ECSPI_ReadBlocking(base, xfer->rxData, dataCounts); |
| if (kStatus_Success != state) |
| { |
| return state; |
| } |
| if (NULL != xfer->rxData) |
| { |
| xfer->rxData += dataCounts; |
| } |
| |
| xfer->dataSize -= dataCounts; |
| } |
| |
| return kStatus_Success; |
| } |
| |
| /*! |
| * brief Performs a non-blocking ECSPI interrupt transfer. |
| * |
| * note The API immediately returns after transfer initialization is finished. |
| * note If ECSPI transfer data frame size is 16 bits, the transfer size cannot be an odd number. |
| * |
| * param base ECSPI peripheral base address. |
| * param handle pointer to ecspi_master_handle_t structure which stores the transfer state |
| * param xfer pointer to ecspi_transfer_t structure |
| * retval kStatus_Success Successfully start a transfer. |
| * retval kStatus_InvalidArgument Input argument is invalid. |
| * retval kStatus_ECSPI_Busy ECSPI is not idle, is running another transfer. |
| */ |
| status_t ECSPI_MasterTransferNonBlocking(ECSPI_Type *base, ecspi_master_handle_t *handle, ecspi_transfer_t *xfer) |
| { |
| assert(base && handle && xfer); |
| |
| /* Check if ECSPI is busy */ |
| if (handle->state == (uint32_t)kECSPI_Busy) |
| { |
| return kStatus_ECSPI_Busy; |
| } |
| |
| /* Check if the input arguments valid */ |
| if (((xfer->txData == NULL) && (xfer->rxData == NULL)) || (xfer->dataSize == 0U)) |
| { |
| return kStatus_InvalidArgument; |
| } |
| |
| /* Set the handle information */ |
| handle->channel = xfer->channel; |
| handle->txData = xfer->txData; |
| handle->rxData = xfer->rxData; |
| handle->transferSize = xfer->dataSize; |
| handle->txRemainingBytes = xfer->dataSize; |
| handle->rxRemainingBytes = xfer->dataSize; |
| |
| /* Set the ECSPI state to busy */ |
| handle->state = kECSPI_Busy; |
| |
| /* Select ECSPI channel to current channel |
| * Note: |
| * xfer.channel must be configured before transferfer, because every channel has |
| * it's own configuration, if don't configure this parameter, transfer channel |
| * will use the default channel0. |
| */ |
| ECSPI_SetChannelSelect(base, xfer->channel); |
| |
| /* First send data to Tx FIFO to start a ECSPI transfer */ |
| ECSPI_SendTransfer(base, handle); |
| |
| if (NULL != xfer->rxData) |
| { |
| /* Enable Rx data request interrupt and receive overflow interrupt, when data in RX FIFO buffer is greater |
| * than the RX_THRESHOLD, then a interrupt occurred. Only enable Rx interrupt, |
| * use rx interrupt to driver ECSPI transfer. |
| */ |
| ECSPI_EnableInterrupts( |
| base, (uint32_t)kECSPI_RxFifoReadyInterruptEnable | (uint32_t)kECSPI_RxFifoOverFlowInterruptEnable); |
| } |
| else |
| { |
| /* Enable Tx data request interrupt, when data in TX FIFO buffer is greater |
| * than the TX_THRESHOLD, then a interrupt occurred. |
| */ |
| ECSPI_EnableInterrupts(base, kECSPI_TxFifoDataRequstInterruptEnable); |
| } |
| |
| return kStatus_Success; |
| } |
| |
| /*! |
| * brief Gets the bytes of the ECSPI interrupt transferred. |
| * |
| * param base ECSPI peripheral base address. |
| * param handle Pointer to ECSPI transfer handle, this should be a static variable. |
| * param count Transferred bytes of ECSPI master. |
| * retval kStatus_ECSPI_Success Succeed get the transfer count. |
| * retval kStatus_NoTransferInProgress There is not a non-blocking transaction currently in progress. |
| */ |
| status_t ECSPI_MasterTransferGetCount(ECSPI_Type *base, ecspi_master_handle_t *handle, size_t *count) |
| { |
| assert(handle); |
| |
| status_t status = kStatus_Success; |
| |
| if (handle->state != (uint32_t)kStatus_ECSPI_Busy) |
| { |
| status = kStatus_NoTransferInProgress; |
| } |
| else |
| { |
| /* Return remaing bytes in different cases */ |
| if (handle->rxData != NULL) |
| { |
| *count = handle->transferSize - handle->rxRemainingBytes; |
| } |
| else |
| { |
| *count = handle->transferSize - handle->txRemainingBytes; |
| } |
| } |
| |
| return status; |
| } |
| |
| /*! |
| * brief Aborts an ECSPI transfer using interrupt. |
| * |
| * param base ECSPI peripheral base address. |
| * param handle Pointer to ECSPI transfer handle, this should be a static variable. |
| */ |
| void ECSPI_MasterTransferAbort(ECSPI_Type *base, ecspi_master_handle_t *handle) |
| { |
| assert(handle); |
| |
| /* Stop interrupts */ |
| if (NULL != handle->rxData) |
| { |
| ECSPI_DisableInterrupts( |
| base, (uint32_t)kECSPI_RxFifoReadyInterruptEnable | (uint32_t)kECSPI_RxFifoOverFlowInterruptEnable); |
| } |
| else |
| { |
| ECSPI_DisableInterrupts(base, kECSPI_TxFifoDataRequstInterruptEnable); |
| } |
| /* Transfer finished, set the state to Done*/ |
| handle->state = kECSPI_Idle; |
| |
| /* Clear the internal state */ |
| handle->rxRemainingBytes = 0; |
| handle->txRemainingBytes = 0; |
| } |
| |
| /*! |
| * brief Interrupts the handler for the ECSPI. |
| * |
| * param base ECSPI peripheral base address. |
| * param handle pointer to ecspi_master_handle_t structure which stores the transfer state. |
| */ |
| void ECSPI_MasterTransferHandleIRQ(ECSPI_Type *base, ecspi_master_handle_t *handle) |
| { |
| assert(handle); |
| |
| /* If hardware overflow happens */ |
| if ((base->STATREG & ECSPI_STATREG_RO_MASK) != 0UL) |
| { |
| /* Clear overflow flag for next transfer */ |
| ECSPI_ClearStatusFlags(base, kECSPI_RxFifoOverFlowFlag); |
| if ((handle->callback) != NULL) |
| { |
| (handle->callback)(base, handle, kStatus_ECSPI_HardwareOverFlow, handle->userData); |
| } |
| } |
| /* If need to receive data, do a receive */ |
| if ((handle->rxRemainingBytes) != 0UL) |
| { |
| ECSPI_ReceiveTransfer(base, handle); |
| } |
| |
| /* We always need to send a data to make the ECSPI run */ |
| if ((handle->txRemainingBytes) != 0UL) |
| { |
| ECSPI_SendTransfer(base, handle); |
| } |
| |
| /* All the transfer finished */ |
| if ((handle->txRemainingBytes == 0UL) && (handle->rxRemainingBytes == 0UL)) |
| { |
| /* Complete the transfer */ |
| ECSPI_MasterTransferAbort(base, handle); |
| |
| if ((handle->callback) != NULL) |
| { |
| (handle->callback)(base, handle, kStatus_Success, handle->userData); |
| } |
| } |
| } |
| |
| /*! |
| * brief Initializes the ECSPI slave handle. |
| * |
| * This function initializes the ECSPI slave handle which can be used for other ECSPI slave transactional APIs. Usually, |
| * for a specified ECSPI instance, call this API once to get the initialized handle. |
| * |
| * param base ECSPI peripheral base address. |
| * param handle ECSPI handle pointer. |
| * param callback Callback function. |
| * param userData User data. |
| */ |
| void ECSPI_SlaveTransferCreateHandle(ECSPI_Type *base, |
| ecspi_slave_handle_t *handle, |
| ecspi_slave_callback_t callback, |
| void *userData) |
| { |
| assert(handle); |
| |
| /* Slave create handle share same logic with master create handle, the only difference |
| is the Isr pointer. */ |
| ECSPI_MasterTransferCreateHandle(base, handle, callback, userData); |
| s_ecspiSlaveIsr = ECSPI_SlaveTransferHandleIRQ; |
| } |
| |
| /*! |
| * brief Interrupts a handler for the ECSPI slave. |
| * |
| * param base ECSPI peripheral base address. |
| * param handle pointer to ecspi_slave_handle_t structure which stores the transfer state |
| */ |
| void ECSPI_SlaveTransferHandleIRQ(ECSPI_Type *base, ecspi_slave_handle_t *handle) |
| { |
| assert(handle); |
| /* If hardware overflow happens */ |
| if ((base->STATREG & ECSPI_STATREG_RO_MASK) != 0UL) |
| { |
| /* Clear overflow flag for next transfer */ |
| ECSPI_ClearStatusFlags(base, kECSPI_RxFifoOverFlowFlag); |
| if ((handle->callback) != NULL) |
| { |
| (handle->callback)(base, handle, kStatus_ECSPI_HardwareOverFlow, handle->userData); |
| } |
| } |
| /* If needs to receive data, do a receive */ |
| if ((handle->rxRemainingBytes) != 0UL) |
| { |
| ECSPI_ReceiveTransfer(base, handle); |
| } |
| |
| /* We always need to send a data to make the ECSPI run */ |
| if ((handle->txRemainingBytes) != 0UL) |
| { |
| ECSPI_SendTransfer(base, handle); |
| } |
| |
| /* All the transfer finished */ |
| if ((handle->txRemainingBytes == 0UL) && (handle->rxRemainingBytes == 0UL)) |
| { |
| /* Complete the transfer */ |
| ECSPI_SlaveTransferAbort(base, handle); |
| |
| if ((handle->callback) != NULL) |
| { |
| (handle->callback)(base, handle, kStatus_Success, handle->userData); |
| } |
| } |
| } |
| |
| static void ECSPI_CommonIRQHandler(ECSPI_Type *base, ecspi_master_handle_t *handle) |
| { |
| if (ECSPI_IsMaster(base, handle->channel)) |
| { |
| s_ecspiMasterIsr(base, handle); |
| } |
| else |
| { |
| s_ecspiSlaveIsr(base, handle); |
| } |
| SDK_ISR_EXIT_BARRIER; |
| } |
| |
| #if defined(ECSPI1) |
| void ECSPI1_DriverIRQHandler(void) |
| { |
| assert(s_ecspiHandle[1]); |
| ECSPI_CommonIRQHandler(ECSPI1, s_ecspiHandle[1]); |
| } |
| #endif /* ECSPI1 */ |
| |
| #if defined(ECSPI2) |
| void ECSPI2_DriverIRQHandler(void) |
| { |
| assert(s_ecspiHandle[2]); |
| ECSPI_CommonIRQHandler(ECSPI2, s_ecspiHandle[2]); |
| } |
| #endif /* ECSPI2 */ |
| |
| #if defined(ECSPI3) |
| void ECSPI3_DriverIRQHandler(void) |
| { |
| assert(s_ecspiHandle[3]); |
| ECSPI_CommonIRQHandler(ECSPI3, s_ecspiHandle[3]); |
| } |
| #endif /* ECSPI3 */ |
| |
| #if defined(ECSPI4) |
| void ECSPI4_DriverIRQHandler(void) |
| { |
| assert(s_ecspiHandle[4]); |
| ECSPI_CommonIRQHandler(ECSPI4, s_ecspiHandle[4]); |
| } |
| #endif /* ECSPI4 */ |