diff options
Diffstat (limited to 'bsps/arm/imxrt/mcux-sdk/drivers/lpspi')
-rw-r--r-- | bsps/arm/imxrt/mcux-sdk/drivers/lpspi/fsl_lpspi.c | 2421 | ||||
-rw-r--r-- | bsps/arm/imxrt/mcux-sdk/drivers/lpspi/fsl_lpspi.h | 1158 | ||||
-rw-r--r-- | bsps/arm/imxrt/mcux-sdk/drivers/lpspi/fsl_lpspi_edma.c | 1052 | ||||
-rw-r--r-- | bsps/arm/imxrt/mcux-sdk/drivers/lpspi/fsl_lpspi_edma.h | 301 | ||||
-rw-r--r-- | bsps/arm/imxrt/mcux-sdk/drivers/lpspi/fsl_lpspi_freertos.h | 107 |
5 files changed, 5039 insertions, 0 deletions
diff --git a/bsps/arm/imxrt/mcux-sdk/drivers/lpspi/fsl_lpspi.c b/bsps/arm/imxrt/mcux-sdk/drivers/lpspi/fsl_lpspi.c new file mode 100644 index 0000000000..fd28621951 --- /dev/null +++ b/bsps/arm/imxrt/mcux-sdk/drivers/lpspi/fsl_lpspi.c @@ -0,0 +1,2421 @@ +/* + * Copyright (c) 2015, Freescale Semiconductor, Inc. + * Copyright 2016-2022 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "fsl_lpspi.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.lpspi" +#endif + +/*! + * @brief Default watermark values. + * + * The default watermarks are set to zero. + */ +enum _lpspi_default_watermarks +{ + kLpspiDefaultTxWatermark = 0, + kLpspiDefaultRxWatermark = 0, +}; + +/* + * <! Structure definition for variables that passed as parameters in LPSPI_MasterTransferBlocking. + * The structure is private. + */ +typedef struct _lpspi_transfer_blocking_param +{ + bool isTxMask; + bool isPcsContinuous; + uint8_t bytesEachWrite; + uint8_t bytesEachRead; + uint32_t rxRemainingByteCount; +} lpspi_transfer_blocking_param_t; + +/*! @brief Typedef for master interrupt handler. */ +typedef void (*lpspi_master_isr_t)(LPSPI_Type *base, lpspi_master_handle_t *handle); + +/*! @brief Typedef for slave interrupt handler. */ +typedef void (*lpspi_slave_isr_t)(LPSPI_Type *base, lpspi_slave_handle_t *handle); + +/******************************************************************************* + * Prototypes + ******************************************************************************/ + +/*! + * @brief Configures the LPSPI peripheral chip select polarity. + * + * This function takes in the desired peripheral chip select (Pcs) and it's corresponding desired polarity and + * configures the Pcs signal to operate with the desired characteristic. + * + * @param base LPSPI peripheral address. + * @param pcs The particular peripheral chip select (parameter value is of type lpspi_which_pcs_t) for which we wish to + * apply the active high or active low characteristic. + * @param activeLowOrHigh The setting for either "active high, inactive low (0)" or "active low, inactive high(1)" of + * type lpspi_pcs_polarity_config_t. + */ +static void LPSPI_SetOnePcsPolarity(LPSPI_Type *base, + lpspi_which_pcs_t pcs, + lpspi_pcs_polarity_config_t activeLowOrHigh); + +/*! + * @brief Combine the write data for 1 byte to 4 bytes. + * This is not a public API. + */ +static uint32_t LPSPI_CombineWriteData(uint8_t *txData, uint8_t bytesEachWrite, bool isByteSwap); + +/*! + * @brief Separate the read data for 1 byte to 4 bytes. + * This is not a public API. + */ +static void LPSPI_SeparateReadData(uint8_t *rxData, uint32_t readData, uint8_t bytesEachRead, bool isByteSwap); + +/*! + * @brief Wait for tx FIFO to be empty. + * This is not a public API. + * @param base LPSPI peripheral address. + * @return true for the tx FIFO is ready, false is not. + */ +static bool LPSPI_TxFifoReady(LPSPI_Type *base); + +/*! + * @brief Master fill up the TX FIFO with data. + * This is not a public API. + */ +static void LPSPI_MasterTransferFillUpTxFifo(LPSPI_Type *base, lpspi_master_handle_t *handle); + +/*! + * @brief Master finish up a transfer. + * It would call back if there is callback function and set the state to idle. + * This is not a public API. + */ +static void LPSPI_MasterTransferComplete(LPSPI_Type *base, lpspi_master_handle_t *handle); + +/*! + * @brief Slave fill up the TX FIFO with data. + * This is not a public API. + */ +static void LPSPI_SlaveTransferFillUpTxFifo(LPSPI_Type *base, lpspi_slave_handle_t *handle); + +/*! + * @brief Slave finish up a transfer. + * It would call back if there is callback function and set the state to idle. + * This is not a public API. + */ +static void LPSPI_SlaveTransferComplete(LPSPI_Type *base, lpspi_slave_handle_t *handle); + +/*! + * @brief LPSPI common interrupt handler. + * + * @param handle pointer to s_lpspiHandle which stores the transfer state. + */ +static void LPSPI_CommonIRQHandler(LPSPI_Type *base, void *param); + +/*! + * @brief introduce function static bool LPSPI_MasterTransferWriteAllTxData. + * This function was deal with write all Txdata. + * + * @param base LPSPI peripheral address. + * @param stateParams Pass the address of the parent function variable. + * + * @return default is true when No abnormality. + * @return false when time out. + */ +static bool LPSPI_MasterTransferWriteAllTxData(LPSPI_Type *base, + lpspi_transfer_t *transfer, + lpspi_transfer_blocking_param_t *stateParams); + +/*! + * @brief introduce function LPSPI_MasterTransferClearTCR. + * This function was deal with clear TCR. + * + * @param base LPSPI peripheral address. + * @param stateParams Pass the address of the parent function variable. + * + * @return default is true when No abnormality. + * @return false when time out. + */ +static bool LPSPI_MasterTransferClearTCR(LPSPI_Type *base, lpspi_transfer_blocking_param_t *stateParams); + +/*! + * @brief introduce function LPSPI_MasterTransferReadDataInFifo. + * This function was deal with read data in fifo. + * + * @param base LPSPI peripheral address. + * @param stateParams Pass the address of the parent function variable. + * + * @return default is true when No abnormality. + * @return false when time out. + */ +static bool LPSPI_MasterTransferReadDataInFifo(LPSPI_Type *base, + lpspi_transfer_t *transfer, + lpspi_transfer_blocking_param_t *stateParams); + +/*! + * @brief introduce function LPSPI_MasterTransferReadDataInFifoNoBuf. + * This function was deal with no buf in fifo. + * + * @param base LPSPI peripheral address. + * @param stateParams Pass the address of the parent function variable. + * + * @return default is true when No abnormality. + * @return false when time out. + */ +static bool LPSPI_MasterTransferReadDataInFifoNoBuf(LPSPI_Type *base, lpspi_transfer_blocking_param_t *stateParams); + +/******************************************************************************* + * Variables + ******************************************************************************/ + +/* Defines constant value arrays for the baud rate pre-scalar and scalar divider values.*/ +static const uint8_t s_baudratePrescaler[] = {1, 2, 4, 8, 16, 32, 64, 128}; + +/*! @brief Pointers to lpspi bases for each instance. */ +static LPSPI_Type *const s_lpspiBases[] = LPSPI_BASE_PTRS; + +/*! @brief Pointers to lpspi IRQ number for each instance. */ +static const IRQn_Type s_lpspiIRQ[] = LPSPI_IRQS; + +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) +/*! @brief Pointers to lpspi clocks for each instance. */ +static const clock_ip_name_t s_lpspiClocks[] = LPSPI_CLOCKS; + +#if defined(LPSPI_PERIPH_CLOCKS) +static const clock_ip_name_t s_LpspiPeriphClocks[] = LPSPI_PERIPH_CLOCKS; +#endif + +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ + +/*! @brief Pointers to lpspi handles for each instance. */ +static void *s_lpspiHandle[ARRAY_SIZE(s_lpspiBases)]; + +/*! @brief Pointer to master IRQ handler for each instance. */ +static lpspi_master_isr_t s_lpspiMasterIsr; +/*! @brief Pointer to slave IRQ handler for each instance. */ +static lpspi_slave_isr_t s_lpspiSlaveIsr; +/* @brief Dummy data for each instance. This data is used when user's tx buffer is NULL*/ +volatile uint8_t g_lpspiDummyData[ARRAY_SIZE(s_lpspiBases)] = {0}; + +/********************************************************************************************************************** + * Code + *********************************************************************************************************************/ + +/*! + * brief Get the LPSPI instance from peripheral base address. + * + * param base LPSPI peripheral base address. + * return LPSPI instance. + */ +uint32_t LPSPI_GetInstance(LPSPI_Type *base) +{ + uint8_t instance = 0; + + /* Find the instance index from base address mappings. */ + for (instance = 0; instance < ARRAY_SIZE(s_lpspiBases); instance++) + { + if (s_lpspiBases[instance] == base) + { + break; + } + } + + assert(instance < ARRAY_SIZE(s_lpspiBases)); + + return instance; +} + +/*! + * brief Set up the dummy data. + * + * param base LPSPI peripheral address. + * param dummyData Data to be transferred when tx buffer is NULL. + * Note: + * This API has no effect when LPSPI in slave interrupt mode, because driver + * will set the TXMSK bit to 1 if txData is NULL, no data is loaded from transmit + * FIFO and output pin is tristated. + */ +void LPSPI_SetDummyData(LPSPI_Type *base, uint8_t dummyData) +{ + uint32_t instance = LPSPI_GetInstance(base); + g_lpspiDummyData[instance] = dummyData; +} + +/*! + * brief Initializes the LPSPI master. + * + * param base LPSPI peripheral address. + * param masterConfig Pointer to structure lpspi_master_config_t. + * param srcClock_Hz Module source input clock in Hertz + */ +void LPSPI_MasterInit(LPSPI_Type *base, const lpspi_master_config_t *masterConfig, uint32_t srcClock_Hz) +{ + assert(masterConfig != NULL); + + uint32_t tcrPrescaleValue = 0; + +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) + + uint32_t instance = LPSPI_GetInstance(base); + /* Enable LPSPI clock */ + (void)CLOCK_EnableClock(s_lpspiClocks[instance]); + +#if defined(LPSPI_PERIPH_CLOCKS) + (void)CLOCK_EnableClock(s_LpspiPeriphClocks[instance]); +#endif + +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ + + /* Set LPSPI to master */ + LPSPI_SetMasterSlaveMode(base, kLPSPI_Master); + + /* Set specific PCS to active high or low */ + LPSPI_SetOnePcsPolarity(base, masterConfig->whichPcs, masterConfig->pcsActiveHighOrLow); + + /* Set Configuration Register 1 related setting.*/ + base->CFGR1 = (base->CFGR1 & ~(LPSPI_CFGR1_OUTCFG_MASK | LPSPI_CFGR1_PINCFG_MASK | LPSPI_CFGR1_NOSTALL_MASK | + LPSPI_CFGR1_SAMPLE_MASK)) | + LPSPI_CFGR1_OUTCFG(masterConfig->dataOutConfig) | LPSPI_CFGR1_PINCFG(masterConfig->pinCfg) | + LPSPI_CFGR1_NOSTALL(0) | LPSPI_CFGR1_SAMPLE((uint32_t)masterConfig->enableInputDelay); + + /* Set baudrate and delay times*/ + (void)LPSPI_MasterSetBaudRate(base, masterConfig->baudRate, srcClock_Hz, &tcrPrescaleValue); + + /* Set default watermarks */ + LPSPI_SetFifoWatermarks(base, (uint32_t)kLpspiDefaultTxWatermark, (uint32_t)kLpspiDefaultRxWatermark); + + /* Set Transmit Command Register*/ + base->TCR = LPSPI_TCR_CPOL(masterConfig->cpol) | LPSPI_TCR_CPHA(masterConfig->cpha) | + LPSPI_TCR_LSBF(masterConfig->direction) | LPSPI_TCR_FRAMESZ(masterConfig->bitsPerFrame - 1U) | + LPSPI_TCR_PRESCALE(tcrPrescaleValue) | LPSPI_TCR_PCS(masterConfig->whichPcs); + + LPSPI_Enable(base, true); + + (void)LPSPI_MasterSetDelayTimes(base, masterConfig->pcsToSckDelayInNanoSec, kLPSPI_PcsToSck, srcClock_Hz); + (void)LPSPI_MasterSetDelayTimes(base, masterConfig->lastSckToPcsDelayInNanoSec, kLPSPI_LastSckToPcs, srcClock_Hz); + (void)LPSPI_MasterSetDelayTimes(base, masterConfig->betweenTransferDelayInNanoSec, kLPSPI_BetweenTransfer, + srcClock_Hz); + + LPSPI_SetDummyData(base, LPSPI_DUMMY_DATA); +} + +/*! + * brief Sets the lpspi_master_config_t structure to default values. + * + * This API initializes the configuration structure for LPSPI_MasterInit(). + * The initialized structure can remain unchanged in LPSPI_MasterInit(), or can be modified + * before calling the LPSPI_MasterInit(). + * Example: + * code + * lpspi_master_config_t masterConfig; + * LPSPI_MasterGetDefaultConfig(&masterConfig); + * endcode + * param masterConfig pointer to lpspi_master_config_t structure + */ +void LPSPI_MasterGetDefaultConfig(lpspi_master_config_t *masterConfig) +{ + assert(masterConfig != NULL); + + /* Initializes the configure structure to zero. */ + (void)memset(masterConfig, 0, sizeof(*masterConfig)); + + masterConfig->baudRate = 500000; + masterConfig->bitsPerFrame = 8; + masterConfig->cpol = kLPSPI_ClockPolarityActiveHigh; + masterConfig->cpha = kLPSPI_ClockPhaseFirstEdge; + masterConfig->direction = kLPSPI_MsbFirst; + + masterConfig->pcsToSckDelayInNanoSec = 1000000000U / masterConfig->baudRate * 2U; + masterConfig->lastSckToPcsDelayInNanoSec = 1000000000U / masterConfig->baudRate * 2U; + masterConfig->betweenTransferDelayInNanoSec = 1000000000U / masterConfig->baudRate * 2U; + + masterConfig->whichPcs = kLPSPI_Pcs0; + masterConfig->pcsActiveHighOrLow = kLPSPI_PcsActiveLow; + + masterConfig->pinCfg = kLPSPI_SdiInSdoOut; + masterConfig->dataOutConfig = kLpspiDataOutRetained; + + masterConfig->enableInputDelay = false; +} + +/*! + * brief LPSPI slave configuration. + * + * param base LPSPI peripheral address. + * param slaveConfig Pointer to a structure lpspi_slave_config_t. + */ +void LPSPI_SlaveInit(LPSPI_Type *base, const lpspi_slave_config_t *slaveConfig) +{ + assert(slaveConfig != NULL); + +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) + + uint32_t instance = LPSPI_GetInstance(base); + /* Enable LPSPI clock */ + (void)CLOCK_EnableClock(s_lpspiClocks[instance]); + +#if defined(LPSPI_PERIPH_CLOCKS) + (void)CLOCK_EnableClock(s_LpspiPeriphClocks[instance]); +#endif + +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ + + LPSPI_SetMasterSlaveMode(base, kLPSPI_Slave); + + LPSPI_SetOnePcsPolarity(base, slaveConfig->whichPcs, slaveConfig->pcsActiveHighOrLow); + + base->CFGR1 = (base->CFGR1 & ~(LPSPI_CFGR1_OUTCFG_MASK | LPSPI_CFGR1_PINCFG_MASK)) | + LPSPI_CFGR1_OUTCFG(slaveConfig->dataOutConfig) | LPSPI_CFGR1_PINCFG(slaveConfig->pinCfg); + + LPSPI_SetFifoWatermarks(base, (uint32_t)kLpspiDefaultTxWatermark, (uint32_t)kLpspiDefaultRxWatermark); + + base->TCR = LPSPI_TCR_CPOL(slaveConfig->cpol) | LPSPI_TCR_CPHA(slaveConfig->cpha) | + LPSPI_TCR_LSBF(slaveConfig->direction) | LPSPI_TCR_FRAMESZ(slaveConfig->bitsPerFrame - 1U); + + /* This operation will set the dummy data for edma transfer, no effect in interrupt way. */ + LPSPI_SetDummyData(base, LPSPI_DUMMY_DATA); + + LPSPI_Enable(base, true); +} + +/*! + * brief Sets the lpspi_slave_config_t structure to default values. + * + * This API initializes the configuration structure for LPSPI_SlaveInit(). + * The initialized structure can remain unchanged in LPSPI_SlaveInit() or can be modified + * before calling the LPSPI_SlaveInit(). + * Example: + * code + * lpspi_slave_config_t slaveConfig; + * LPSPI_SlaveGetDefaultConfig(&slaveConfig); + * endcode + * param slaveConfig pointer to lpspi_slave_config_t structure. + */ +void LPSPI_SlaveGetDefaultConfig(lpspi_slave_config_t *slaveConfig) +{ + assert(slaveConfig != NULL); + + /* Initializes the configure structure to zero. */ + (void)memset(slaveConfig, 0, sizeof(*slaveConfig)); + + slaveConfig->bitsPerFrame = 8; /*!< Bits per frame, minimum 8, maximum 4096.*/ + slaveConfig->cpol = kLPSPI_ClockPolarityActiveHigh; /*!< Clock polarity. */ + slaveConfig->cpha = kLPSPI_ClockPhaseFirstEdge; /*!< Clock phase. */ + slaveConfig->direction = kLPSPI_MsbFirst; /*!< MSB or LSB data shift direction. */ + + slaveConfig->whichPcs = kLPSPI_Pcs0; /*!< Desired Peripheral Chip Select (pcs) */ + slaveConfig->pcsActiveHighOrLow = kLPSPI_PcsActiveLow; /*!< Desired PCS active high or low */ + + slaveConfig->pinCfg = kLPSPI_SdiInSdoOut; + slaveConfig->dataOutConfig = kLpspiDataOutRetained; +} + +/*! + * brief Restores the LPSPI peripheral to reset state. Note that this function + * sets all registers to reset state. As a result, the LPSPI module can't work after calling + * this API. + * param base LPSPI peripheral address. + */ +void LPSPI_Reset(LPSPI_Type *base) +{ + /* Reset all internal logic and registers, except the Control Register. Remains set until cleared by software.*/ + base->CR |= LPSPI_CR_RST_MASK; + + /* Software reset doesn't reset the CR, so manual reset the FIFOs */ + base->CR |= LPSPI_CR_RRF_MASK | LPSPI_CR_RTF_MASK; + + /* Master logic is not reset and module is disabled.*/ + base->CR = 0x00U; +} + +/*! + * brief De-initializes the LPSPI peripheral. Call this API to disable the LPSPI clock. + * param base LPSPI peripheral address. + */ +void LPSPI_Deinit(LPSPI_Type *base) +{ + /* Reset to default value */ + LPSPI_Reset(base); + +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) + + uint32_t instance = LPSPI_GetInstance(base); + /* Enable LPSPI clock */ + (void)CLOCK_DisableClock(s_lpspiClocks[instance]); + +#if defined(LPSPI_PERIPH_CLOCKS) + (void)CLOCK_DisableClock(s_LpspiPeriphClocks[instance]); +#endif + +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ +} + +static void LPSPI_SetOnePcsPolarity(LPSPI_Type *base, + lpspi_which_pcs_t pcs, + lpspi_pcs_polarity_config_t activeLowOrHigh) +{ + uint32_t cfgr1Value = 0; + /* Clear the PCS polarity bit */ + cfgr1Value = base->CFGR1 & ~(1UL << (LPSPI_CFGR1_PCSPOL_SHIFT + (uint32_t)pcs)); + + /* Configure the PCS polarity bit according to the activeLowOrHigh setting */ + base->CFGR1 = cfgr1Value | ((uint32_t)activeLowOrHigh << (LPSPI_CFGR1_PCSPOL_SHIFT + (uint32_t)pcs)); +} + +/*! + * brief Sets the LPSPI baud rate in bits per second. + * + * This function takes in the desired bitsPerSec (baud rate) and calculates the nearest + * possible baud rate without exceeding the desired baud rate and returns the + * calculated baud rate in bits-per-second. It requires the caller to provide + * the frequency of the module source clock (in Hertz). Note that the baud rate + * does not go into effect until the Transmit Control Register (TCR) is programmed + * with the prescale value. Hence, this function returns the prescale tcrPrescaleValue + * parameter for later programming in the TCR. The higher level + * peripheral driver should alert the user of an out of range baud rate input. + * + * Note that the LPSPI module must first be disabled before configuring this. + * Note that the LPSPI module must be configured for master mode before configuring this. + * + * param base LPSPI peripheral address. + * param baudRate_Bps The desired baud rate in bits per second. + * param srcClock_Hz Module source input clock in Hertz. + * param tcrPrescaleValue The TCR prescale value needed to program the TCR. + * return The actual calculated baud rate. This function may also return a "0" if the + * LPSPI is not configured for master mode or if the LPSPI module is not disabled. + */ + +uint32_t LPSPI_MasterSetBaudRate(LPSPI_Type *base, + uint32_t baudRate_Bps, + uint32_t srcClock_Hz, + uint32_t *tcrPrescaleValue) +{ + assert(tcrPrescaleValue != NULL); + + /* For master mode configuration only, if slave mode detected, return 0. + * Also, the LPSPI module needs to be disabled first, if enabled, return 0 + */ + if ((!LPSPI_IsMaster(base)) || ((base->CR & LPSPI_CR_MEN_MASK) != 0U)) + { + return 0U; + } + + uint32_t prescaler, bestPrescaler; + uint32_t scaler, bestScaler; + uint32_t realBaudrate, bestBaudrate; + uint32_t diff, min_diff; + uint32_t desiredBaudrate = baudRate_Bps; + + /* find combination of prescaler and scaler resulting in baudrate closest to the + * requested value + */ + min_diff = 0xFFFFFFFFU; + + /* Set to maximum divisor value bit settings so that if baud rate passed in is less + * than the minimum possible baud rate, then the SPI will be configured to the lowest + * possible baud rate + */ + bestPrescaler = 7; + bestScaler = 255; + + bestBaudrate = 0; /* required to avoid compilation warning */ + + /* In all for loops, if min_diff = 0, the exit for loop*/ + for (prescaler = 0U; prescaler < 8U; prescaler++) + { + if (min_diff == 0U) + { + break; + } + for (scaler = 0U; scaler < 256U; scaler++) + { + if (min_diff == 0U) + { + break; + } + realBaudrate = (srcClock_Hz / (s_baudratePrescaler[prescaler] * (scaler + 2U))); + + /* calculate the baud rate difference based on the conditional statement + * that states that the calculated baud rate must not exceed the desired baud rate + */ + if (desiredBaudrate >= realBaudrate) + { + diff = desiredBaudrate - realBaudrate; + if (min_diff > diff) + { + /* a better match found */ + min_diff = diff; + bestPrescaler = prescaler; + bestScaler = scaler; + bestBaudrate = realBaudrate; + } + } + } + } + + /* Write the best baud rate scalar to the CCR. + * Note, no need to check for error since we've already checked to make sure the module is + * disabled and in master mode. Also, there is a limit on the maximum divider so we will not + * exceed this. + */ +#if defined(FSL_FEATURE_LPSPI_HAS_CCR1) && FSL_FEATURE_LPSPI_HAS_CCR1 + /* When CCR1 is present, the CCR[DBT] and CCR[SCKDIV] is write only, all read will return 0 + The real DBT and SCKDIV can be obtained in CCR1, CCR[DBT]=CCR1[SCKSCK] and CCR[SCKDIV]=CCR1[SCKHLD]+CCR1[SCKSET] + So when changing either CCR[DBT] or CCR[SCKDIV] make sure the other value is not overwritten by 0 */ + base->CCR = base->CCR | LPSPI_CCR_DBT((base->CCR1 & LPSPI_CCR1_SCKSCK_MASK) >> LPSPI_CCR1_SCKSCK_SHIFT) | + LPSPI_CCR_SCKDIV(bestScaler); +#else + base->CCR = (base->CCR & ~LPSPI_CCR_SCKDIV_MASK) | LPSPI_CCR_SCKDIV(bestScaler); +#endif /* FSL_FEATURE_LPSPI_HAS_CCR1 */ + + /* return the best prescaler value for user to use later */ + *tcrPrescaleValue = bestPrescaler; + + /* return the actual calculated baud rate */ + return bestBaudrate; +} + +/*! + * brief Manually configures a specific LPSPI delay parameter (module must be disabled to + * change the delay values). + * + * This function configures the following: + * SCK to PCS delay, or + * PCS to SCK delay, or + * The configurations must occur between the transfer delay. + * + * The delay names are available in type lpspi_delay_type_t. + * + * The user passes the desired delay along with the delay value. + * This allows the user to directly set the delay values if they have + * pre-calculated them or if they simply wish to manually increment the value. + * + * Note that the LPSPI module must first be disabled before configuring this. + * Note that the LPSPI module must be configured for master mode before configuring this. + * + * param base LPSPI peripheral address. + * param scaler The 8-bit delay value 0x00 to 0xFF (255). + * param whichDelay The desired delay to configure, must be of type lpspi_delay_type_t. + */ +void LPSPI_MasterSetDelayScaler(LPSPI_Type *base, uint32_t scaler, lpspi_delay_type_t whichDelay) +{ + /*These settings are only relevant in master mode */ +#if defined(FSL_FEATURE_LPSPI_HAS_CCR1) && FSL_FEATURE_LPSPI_HAS_CCR1 + /* When CCR1 is present, the CCR[DBT] and CCR[SCKDIV] is write only, all read will return 0 + The real DBT and SCKDIV can be obtained in CCR1, CCR[DBT]=CCR1[SCKSCK] and CCR[SCKDIV]=CCR1[SCKHLD]+CCR1[SCKSET] + So when changing either CCR[DBT] or CCR[SCKDIV] make sure the other value is not overwritten by 0 */ + uint32_t dbt = (base->CCR1 & LPSPI_CCR1_SCKSCK_MASK) >> LPSPI_CCR1_SCKSCK_SHIFT; + uint32_t sckdiv = (base->CCR1 & LPSPI_CCR1_SCKHLD_MASK) >> LPSPI_CCR1_SCKHLD_SHIFT; + sckdiv += (base->CCR1 & LPSPI_CCR1_SCKSET_MASK) >> LPSPI_CCR1_SCKSET_SHIFT; + switch (whichDelay) + { + case kLPSPI_PcsToSck: + base->CCR = (base->CCR & (~LPSPI_CCR_PCSSCK_MASK)) | LPSPI_CCR_PCSSCK(scaler) | LPSPI_CCR_DBT(dbt) | + LPSPI_CCR_SCKDIV(sckdiv); + + break; + case kLPSPI_LastSckToPcs: + base->CCR = (base->CCR & (~LPSPI_CCR_SCKPCS_MASK)) | LPSPI_CCR_SCKPCS(scaler) | LPSPI_CCR_DBT(dbt) | + LPSPI_CCR_SCKDIV(sckdiv); + + break; + case kLPSPI_BetweenTransfer: + base->CCR = base->CCR | LPSPI_CCR_DBT(scaler) | LPSPI_CCR_SCKDIV(sckdiv); +#else + switch (whichDelay) + { + case kLPSPI_PcsToSck: + base->CCR = (base->CCR & (~LPSPI_CCR_PCSSCK_MASK)) | LPSPI_CCR_PCSSCK(scaler); + + break; + case kLPSPI_LastSckToPcs: + base->CCR = (base->CCR & (~LPSPI_CCR_SCKPCS_MASK)) | LPSPI_CCR_SCKPCS(scaler); + + break; + case kLPSPI_BetweenTransfer: + base->CCR = (base->CCR & (~LPSPI_CCR_DBT_MASK)) | LPSPI_CCR_DBT(scaler); +#endif /* FSL_FEATURE_LPSPI_HAS_CCR1 */ + break; + default: + assert(false); + break; + } +} + +/*! + * brief Calculates the delay based on the desired delay input in nanoseconds (module must be + * disabled to change the delay values). + * + * This function calculates the values for the following: + * SCK to PCS delay, or + * PCS to SCK delay, or + * The configurations must occur between the transfer delay. + * + * The delay names are available in type lpspi_delay_type_t. + * + * The user passes the desired delay and the desired delay value in + * nano-seconds. The function calculates the value needed for the desired delay parameter + * and returns the actual calculated delay because an exact delay match may not be possible. In this + * case, the closest match is calculated without going below the desired delay value input. + * It is possible to input a very large delay value that exceeds the capability of the part, in + * which case the maximum supported delay is returned. It is up to the higher level + * peripheral driver to alert the user of an out of range delay input. + * + * Note that the LPSPI module must be configured for master mode before configuring this. And note that + * the delayTime = LPSPI_clockSource / (PRESCALE * Delay_scaler). + * + * param base LPSPI peripheral address. + * param delayTimeInNanoSec The desired delay value in nano-seconds. + * param whichDelay The desired delay to configuration, which must be of type lpspi_delay_type_t. + * param srcClock_Hz Module source input clock in Hertz. + * return actual Calculated delay value in nano-seconds. + */ +uint32_t LPSPI_MasterSetDelayTimes(LPSPI_Type *base, + uint32_t delayTimeInNanoSec, + lpspi_delay_type_t whichDelay, + uint32_t srcClock_Hz) +{ + uint64_t realDelay, bestDelay; + uint32_t scaler, bestScaler; + uint32_t diff, min_diff; + uint64_t initialDelayNanoSec; + uint32_t clockDividedPrescaler; + + /* For delay between transfer, an additional scaler value is needed */ + uint32_t additionalScaler = 0; + + /*As the RM note, the LPSPI baud rate clock is itself divided by the PRESCALE setting, which can vary between + * transfers.*/ + clockDividedPrescaler = + srcClock_Hz / s_baudratePrescaler[(base->TCR & LPSPI_TCR_PRESCALE_MASK) >> LPSPI_TCR_PRESCALE_SHIFT]; + + /* Find combination of prescaler and scaler resulting in the delay closest to the requested value.*/ + min_diff = 0xFFFFFFFFU; + + /* Initialize scaler to max value to generate the max delay */ + bestScaler = 0xFFU; + + /* Calculate the initial (min) delay and maximum possible delay based on the specific delay as + * the delay divisors are slightly different based on which delay we are configuring. + */ + if (whichDelay == kLPSPI_BetweenTransfer) + { + /* First calculate the initial, default delay, note min delay is 2 clock cycles. Due to large size of + calculated values (uint64_t), we need to break up the calculation into several steps to ensure + accurate calculated results + */ + initialDelayNanoSec = 1000000000U; + initialDelayNanoSec *= 2U; + initialDelayNanoSec /= clockDividedPrescaler; + + /* Calculate the maximum delay */ + bestDelay = 1000000000U; + bestDelay *= 257U; /* based on DBT+2, or 255 + 2 */ + bestDelay /= clockDividedPrescaler; + + additionalScaler = 1U; + } + else + { + /* First calculate the initial, default delay, min delay is 1 clock cycle. Due to large size of calculated + values (uint64_t), we need to break up the calculation into several steps to ensure accurate calculated + results. + */ + initialDelayNanoSec = 1000000000U; + initialDelayNanoSec /= clockDividedPrescaler; + + /* Calculate the maximum delay */ + bestDelay = 1000000000U; + bestDelay *= 256U; /* based on SCKPCS+1 or PCSSCK+1, or 255 + 1 */ + bestDelay /= clockDividedPrescaler; + + additionalScaler = 0U; + } + + /* If the initial, default delay is already greater than the desired delay, then + * set the delay to their initial value (0) and return the delay. In other words, + * there is no way to decrease the delay value further. + */ + if (initialDelayNanoSec >= delayTimeInNanoSec) + { + LPSPI_MasterSetDelayScaler(base, 0, whichDelay); + return (uint32_t)initialDelayNanoSec; + } + + /* If min_diff = 0, the exit for loop */ + for (scaler = 0U; scaler < 256U; scaler++) + { + if (min_diff == 0U) + { + break; + } + /* Calculate the real delay value as we cycle through the scaler values. + Due to large size of calculated values (uint64_t), we need to break up the + calculation into several steps to ensure accurate calculated results + */ + realDelay = 1000000000U; + realDelay *= ((uint64_t)scaler + 1UL + (uint64_t)additionalScaler); + realDelay /= clockDividedPrescaler; + + /* calculate the delay difference based on the conditional statement + * that states that the calculated delay must not be less then the desired delay + */ + if (realDelay >= delayTimeInNanoSec) + { + diff = (uint32_t)(realDelay - (uint64_t)delayTimeInNanoSec); + if (min_diff > diff) + { + /* a better match found */ + min_diff = diff; + bestScaler = scaler; + bestDelay = realDelay; + } + } + } + + /* write the best scaler value for the delay */ + LPSPI_MasterSetDelayScaler(base, bestScaler, whichDelay); + + /* return the actual calculated delay value (in ns) */ + return (uint32_t)bestDelay; +} + +/*Transactional APIs -- Master*/ + +/*! + * brief Initializes the LPSPI master handle. + * + * This function initializes the LPSPI handle, which can be used for other LPSPI transactional APIs. Usually, for a + * specified LPSPI instance, call this API once to get the initialized handle. + + * param base LPSPI peripheral address. + * param handle LPSPI handle pointer to lpspi_master_handle_t. + * param callback DSPI callback. + * param userData callback function parameter. + */ +void LPSPI_MasterTransferCreateHandle(LPSPI_Type *base, + lpspi_master_handle_t *handle, + lpspi_master_transfer_callback_t callback, + void *userData) +{ + assert(handle != NULL); + + /* Zero the handle. */ + (void)memset(handle, 0, sizeof(*handle)); + + s_lpspiHandle[LPSPI_GetInstance(base)] = handle; + + /* Set irq handler. */ + s_lpspiMasterIsr = LPSPI_MasterTransferHandleIRQ; + + handle->callback = callback; + handle->userData = userData; +} + +/*! + * brief Check the argument for transfer . + * + * param base LPSPI peripheral address. + * param transfer the transfer struct to be used. + * param isEdma True to check for EDMA transfer, false to check interrupt non-blocking transfer + * return Return true for right and false for wrong. + */ +bool LPSPI_CheckTransferArgument(LPSPI_Type *base, lpspi_transfer_t *transfer, bool isEdma) +{ + assert(transfer != NULL); + uint32_t bitsPerFrame = ((base->TCR & LPSPI_TCR_FRAMESZ_MASK) >> LPSPI_TCR_FRAMESZ_SHIFT) + 1U; + uint32_t bytesPerFrame = (bitsPerFrame + 7U) / 8U; + uint32_t temp = (base->CFGR1 & LPSPI_CFGR1_PINCFG_MASK); + /* If the transfer count is zero, then return immediately.*/ + if (transfer->dataSize == 0U) + { + return false; + } + + /* If both send buffer and receive buffer is null */ + if ((NULL == (transfer->txData)) && (NULL == (transfer->rxData))) + { + return false; + } + + /*The transfer data size should be integer multiples of bytesPerFrame if bytesPerFrame is less than or equal to 4 . + *For bytesPerFrame greater than 4 situation: + *the transfer data size should be equal to bytesPerFrame if the bytesPerFrame is not integer multiples of 4 , + *otherwise , the transfer data size can be integer multiples of bytesPerFrame. + */ + if (bytesPerFrame <= 4U) + { + if ((transfer->dataSize % bytesPerFrame) != 0U) + { + return false; + } + } + else + { + if ((bytesPerFrame % 4U) != 0U) + { + if (transfer->dataSize != bytesPerFrame) + { + return false; + } + } + else + { + if ((transfer->dataSize % bytesPerFrame) != 0U) + { + return false; + } + } + } + + /* Check if using 3-wire mode and the txData is NULL, set the output pin to tristated. */ + if ((temp == LPSPI_CFGR1_PINCFG(kLPSPI_SdiInSdiOut)) || (temp == LPSPI_CFGR1_PINCFG(kLPSPI_SdoInSdoOut))) + { + /* The 3-wire mode can't send and receive data at the same time. */ + if ((transfer->txData != NULL) && (transfer->rxData != NULL)) + { + return false; + } + if (NULL == transfer->txData) + { + base->CFGR1 |= LPSPI_CFGR1_OUTCFG_MASK; + } + } + + if (isEdma && ((bytesPerFrame % 4U) == 3U)) + { + return false; + } + + return true; +} + +static bool LPSPI_MasterTransferWriteAllTxData(LPSPI_Type *base, + lpspi_transfer_t *transfer, + lpspi_transfer_blocking_param_t *stateParams) +{ + uint8_t dummyData = g_lpspiDummyData[LPSPI_GetInstance(base)]; + uint32_t bytesPerFrame = ((base->TCR & LPSPI_TCR_FRAMESZ_MASK) >> LPSPI_TCR_FRAMESZ_SHIFT) / 8U + 1U; + uint32_t txRemainingByteCount = transfer->dataSize; + bool isByteSwap = ((transfer->configFlags & (uint32_t)kLPSPI_MasterByteSwap) != 0U); + uint32_t wordToSend = + ((uint32_t)dummyData) | ((uint32_t)dummyData << 8) | ((uint32_t)dummyData << 16) | ((uint32_t)dummyData << 24); + uint32_t rxFifoMaxBytes = MIN(bytesPerFrame, 4U) * LPSPI_GetRxFifoSize(base); + uint32_t readData; + /*Write the TX data until txRemainingByteCount is equal to 0 */ + while (txRemainingByteCount > 0U) + { + if (txRemainingByteCount < (stateParams->bytesEachWrite)) + { + (stateParams->bytesEachWrite) = (uint8_t)txRemainingByteCount; + } + + /*Wait until TX FIFO is not full*/ +#if SPI_RETRY_TIMES + uint32_t waitTimes = SPI_RETRY_TIMES; + while ((LPSPI_GetTxFifoCount(base) == LPSPI_GetRxFifoSize(base)) && (--waitTimes) != 0U)) +#else + while (LPSPI_GetTxFifoCount(base) == LPSPI_GetRxFifoSize(base)) +#endif + { + } +#if SPI_RETRY_TIMES + if (waitTimes == 0U) + { + return false; + } +#endif + + /* To prevent rxfifo overflow, ensure transmitting and receiving are executed in parallel */ + if (((NULL == (transfer->rxData)) || + ((stateParams->rxRemainingByteCount) - txRemainingByteCount) < rxFifoMaxBytes)) + { + if (stateParams->isTxMask) + { + /* When TCR[TXMSK]=1, transfer is initiate by writting a new command word to TCR. TCR[TXMSK] is cleared + by hardware every time when TCR[FRAMESZ] bit of data is transfered. + In this case TCR[TXMSK] should be set to initiate each transfer. */ + base->TCR |= LPSPI_TCR_TXMSK_MASK; + if (stateParams->isPcsContinuous && (txRemainingByteCount == bytesPerFrame)) + { + /* For the last piece of frame size of data, if is PCS continous mode(TCR[CONT]), TCR[CONTC] should + * be cleared to de-assert the PCS. Be sure to clear the TXMSK as well otherwise another FRAMESZ + * of data will be received. */ + base->TCR &= ~(LPSPI_TCR_CONTC_MASK | LPSPI_TCR_CONT_MASK | LPSPI_TCR_TXMSK_MASK); + } + txRemainingByteCount -= bytesPerFrame; + } + else + { + if ((transfer->txData) != NULL) + { + wordToSend = LPSPI_CombineWriteData((transfer->txData), (stateParams->bytesEachWrite), isByteSwap); + (transfer->txData) += (stateParams->bytesEachWrite); + } + /* Otherwise push data to tx FIFO to initiate transfer */ + LPSPI_WriteData(base, wordToSend); + txRemainingByteCount -= (stateParams->bytesEachWrite); + } + } + + /* Check whether there is RX data in RX FIFO . Read out the RX data so that the RX FIFO would not overrun. */ + if (((transfer->rxData) != NULL) && ((stateParams->rxRemainingByteCount) != 0U)) + { + /* To ensure parallel execution in 3-wire mode, after writting 1 to TXMSK to generate clock of + bytesPerFrame's data wait until bytesPerFrame's data is received. */ + while ((stateParams->isTxMask) && (LPSPI_GetRxFifoCount(base) == 0U)) + { + } +#if SPI_RETRY_TIMES + uint32_t waitTimes = SPI_RETRY_TIMES; + while ((LPSPI_GetRxFifoCount(base) != 0U) && (--waitTimes != 0U)) +#else + while (LPSPI_GetRxFifoCount(base) != 0U) +#endif + { + readData = LPSPI_ReadData(base); + if ((stateParams->rxRemainingByteCount) < (stateParams->bytesEachRead)) + { + (stateParams->bytesEachRead) = (uint8_t)(stateParams->rxRemainingByteCount); + } + + LPSPI_SeparateReadData((transfer->rxData), readData, (stateParams->bytesEachRead), isByteSwap); + (transfer->rxData) += (stateParams->bytesEachRead); + + (stateParams->rxRemainingByteCount) -= (stateParams->bytesEachRead); + } +#if SPI_RETRY_TIMES + if (waitTimes == 0U) + { + return false; + } +#endif + } + } + return true; +} + +static bool LPSPI_MasterTransferClearTCR(LPSPI_Type *base, lpspi_transfer_blocking_param_t *stateParams) +{ +#if SPI_RETRY_TIMES + uint32_t waitTimes = SPI_RETRY_TIMES; + while ((LPSPI_GetTxFifoCount(base) == LPSPI_GetRxFifoSize(base)) && (--waitTimes != 0U)) +#else + while (LPSPI_GetTxFifoCount(base) == LPSPI_GetRxFifoSize(base)) +#endif + { + } +#if SPI_RETRY_TIMES + if (waitTimes == 0U) + { + return false; + } +#endif + base->TCR = (base->TCR & ~(LPSPI_TCR_CONTC_MASK | LPSPI_TCR_CONT_MASK)); + return true; +} + +static bool LPSPI_MasterTransferReadDataInFifo(LPSPI_Type *base, + lpspi_transfer_t *transfer, + lpspi_transfer_blocking_param_t *stateParams) +{ + uint32_t readData; + bool isByteSwap = ((transfer->configFlags & (uint32_t)kLPSPI_MasterByteSwap) != 0U); + while ((stateParams->rxRemainingByteCount) > 0U) + { +#if SPI_RETRY_TIMES + uint32_t waitTimes = SPI_RETRY_TIMES; + while ((LPSPI_GetRxFifoCount(base) != 0U) && (--waitTimes != 0U)) +#else + while (LPSPI_GetRxFifoCount(base) != 0U) +#endif + { + readData = LPSPI_ReadData(base); + + if ((stateParams->rxRemainingByteCount) < (stateParams->bytesEachRead)) + { + (stateParams->bytesEachRead) = (uint8_t)(stateParams->rxRemainingByteCount); + } + + LPSPI_SeparateReadData((transfer->rxData), readData, (stateParams->bytesEachRead), isByteSwap); + (transfer->rxData) += (stateParams->bytesEachRead); + + (stateParams->rxRemainingByteCount) -= (stateParams->bytesEachRead); + } +#if SPI_RETRY_TIMES + if (waitTimes == 0U) + { + return false; + } +#endif + } + return true; +} + +static bool LPSPI_MasterTransferReadDataInFifoNoBuf(LPSPI_Type *base, lpspi_transfer_blocking_param_t *stateParams) +{ +#if SPI_RETRY_TIMES + uint32_t waitTimes = SPI_RETRY_TIMES; + while (((LPSPI_GetStatusFlags(base) & (uint32_t)kLPSPI_TransferCompleteFlag) == 0U) && (--waitTimes != 0U)) +#else + while ((LPSPI_GetStatusFlags(base) & (uint32_t)kLPSPI_TransferCompleteFlag) == 0U) +#endif + { + } +#if SPI_RETRY_TIMES + if (waitTimes == 0U) + { + return false; + } +#endif + return true; +} + +/*! + * brief LPSPI master transfer data using a polling method. + * + * This function transfers data using a polling method. This is a blocking function, which does not return until all + * transfers have been completed. + * + * Note: + * The transfer data size should be integer multiples of bytesPerFrame if bytesPerFrame is less than or equal to 4. + * For bytesPerFrame greater than 4: + * The transfer data size should be equal to bytesPerFrame if the bytesPerFrame is not integer multiples of 4. + * Otherwise, the transfer data size can be an integer multiple of bytesPerFrame. + * + * param base LPSPI peripheral address. + * param transfer pointer to lpspi_transfer_t structure. + * return status of status_t. + */ +status_t LPSPI_MasterTransferBlocking(LPSPI_Type *base, lpspi_transfer_t *transfer) +{ + assert(transfer != NULL); + + /* Check that LPSPI is not busy.*/ + if ((LPSPI_GetStatusFlags(base) & (uint32_t)kLPSPI_ModuleBusyFlag) != 0U) + { + return kStatus_LPSPI_Busy; + } + LPSPI_Enable(base, false); + /* Check arguements */ + if (!LPSPI_CheckTransferArgument(base, transfer, false)) + { + return kStatus_InvalidArgument; + } + + LPSPI_FlushFifo(base, true, true); + LPSPI_ClearStatusFlags(base, (uint32_t)kLPSPI_AllStatusFlag); + + /* Variables */ + uint32_t whichPcs = (transfer->configFlags & LPSPI_MASTER_PCS_MASK) >> LPSPI_MASTER_PCS_SHIFT; + uint32_t temp = (base->CFGR1 & LPSPI_CFGR1_PINCFG_MASK); + lpspi_transfer_blocking_param_t stateParams; + (void)memset(&stateParams, 0, sizeof(stateParams)); + + stateParams.isTxMask = false; + stateParams.rxRemainingByteCount = transfer->dataSize; + /*The TX and RX FIFO sizes are always the same*/ + uint32_t bytesPerFrame = ((base->TCR & LPSPI_TCR_FRAMESZ_MASK) >> LPSPI_TCR_FRAMESZ_SHIFT) / 8U + 1U; + /* No need to configure PCS continous if the transfer byte count is smaller than frame size */ + stateParams.isPcsContinuous = (((transfer->configFlags & (uint32_t)kLPSPI_MasterPcsContinuous) != 0U) && + (bytesPerFrame < transfer->dataSize)); + + /* Mask tx data in half duplex mode */ + if (((temp == LPSPI_CFGR1_PINCFG(kLPSPI_SdiInSdiOut)) || (temp == LPSPI_CFGR1_PINCFG(kLPSPI_SdoInSdoOut))) && + (transfer->txData == NULL)) + { + stateParams.isTxMask = true; + } + + base->CFGR1 &= (~LPSPI_CFGR1_NOSTALL_MASK); + LPSPI_Enable(base, true); + + /* Configure transfer control register. */ + base->TCR = (base->TCR & ~(LPSPI_TCR_CONT_MASK | LPSPI_TCR_CONTC_MASK | LPSPI_TCR_RXMSK_MASK | + LPSPI_TCR_TXMSK_MASK | LPSPI_TCR_PCS_MASK)) | + LPSPI_TCR_PCS(whichPcs); + + /*TCR is also shared the FIFO, so wait for TCR written.*/ + if (!LPSPI_TxFifoReady(base)) + { + return kStatus_LPSPI_Timeout; + } + + /* PCS should be configured separately from the other bits, otherwise it will not take effect. */ + base->TCR |= LPSPI_TCR_CONT(stateParams.isPcsContinuous) | LPSPI_TCR_CONTC(stateParams.isPcsContinuous) | + LPSPI_TCR_RXMSK(NULL == transfer->rxData); + + /*TCR is also shared the FIFO, so wait for TCR written.*/ + if (!LPSPI_TxFifoReady(base)) + { + return kStatus_LPSPI_Timeout; + } + + if (bytesPerFrame <= 4U) + { + stateParams.bytesEachWrite = (uint8_t)bytesPerFrame; + stateParams.bytesEachRead = (uint8_t)bytesPerFrame; + } + else + { + stateParams.bytesEachWrite = 4U; + stateParams.bytesEachRead = 4U; + } + + if (false == LPSPI_MasterTransferWriteAllTxData(base, transfer, &stateParams)) + { + return kStatus_LPSPI_Timeout; + } + + if (stateParams.isPcsContinuous && !stateParams.isTxMask) + { + /* In PCS continous mode(TCR[CONT]), after write all the data in TX FIFO, TCR[CONTC] and TCR[CONT] should be + cleared to de-assert the PCS. Note that TCR register also use the TX FIFO. Also CONTC should be cleared when + tx is not masked, otherwise written to TCR register with TXMSK bit wet will initiate a new transfer. */ + if (false == LPSPI_MasterTransferClearTCR(base, &stateParams)) + { + return kStatus_LPSPI_Timeout; + } + } + + /*Read out the RX data in FIFO*/ + if (transfer->rxData != NULL) + { + if (false == LPSPI_MasterTransferReadDataInFifo(base, transfer, &stateParams)) + { + return kStatus_LPSPI_Timeout; + } + } + else + { + /* If no RX buffer, then transfer is not complete until transfer complete flag sets */ + if (false == LPSPI_MasterTransferReadDataInFifoNoBuf(base, &stateParams)) + { + return kStatus_LPSPI_Timeout; + } + } + + return kStatus_Success; +} + +/*! + * brief LPSPI master transfer data using an interrupt method. + * + * This function transfers data using an interrupt method. This is a non-blocking function, which returns right away. + * When all data is transferred, the callback function is called. + * + * Note: + * The transfer data size should be integer multiples of bytesPerFrame if bytesPerFrame is less than or equal to 4. + * For bytesPerFrame greater than 4: + * The transfer data size should be equal to bytesPerFrame if the bytesPerFrame is not integer multiples of 4. + * Otherwise, the transfer data size can be an integer multiple of bytesPerFrame. + * + * param base LPSPI peripheral address. + * param handle pointer to lpspi_master_handle_t structure which stores the transfer state. + * param transfer pointer to lpspi_transfer_t structure. + * return status of status_t. + */ +status_t LPSPI_MasterTransferNonBlocking(LPSPI_Type *base, lpspi_master_handle_t *handle, lpspi_transfer_t *transfer) +{ + assert(handle != NULL); + assert(transfer != NULL); + + /* Check that we're not busy.*/ + if (handle->state == (uint8_t)kLPSPI_Busy) + { + return kStatus_LPSPI_Busy; + } + + LPSPI_Enable(base, false); + /* Check arguements */ + if (!LPSPI_CheckTransferArgument(base, transfer, false)) + { + return kStatus_InvalidArgument; + } + + /* Flush FIFO, clear status, disable all the interrupts. */ + LPSPI_FlushFifo(base, true, true); + LPSPI_ClearStatusFlags(base, (uint32_t)kLPSPI_AllStatusFlag); + LPSPI_DisableInterrupts(base, (uint32_t)kLPSPI_AllInterruptEnable); + + /* Variables */ + bool isRxMask = false; + uint8_t txWatermark; + uint8_t dummyData = g_lpspiDummyData[LPSPI_GetInstance(base)]; + uint32_t tmpTimes; + uint32_t whichPcs = (transfer->configFlags & LPSPI_MASTER_PCS_MASK) >> LPSPI_MASTER_PCS_SHIFT; + uint32_t temp = (base->CFGR1 & LPSPI_CFGR1_PINCFG_MASK); + + /* Assign the original value for members of transfer handle. */ + handle->state = (uint8_t)kLPSPI_Busy; + handle->txData = transfer->txData; + handle->rxData = transfer->rxData; + handle->txRemainingByteCount = transfer->dataSize; + handle->rxRemainingByteCount = transfer->dataSize; + handle->totalByteCount = transfer->dataSize; + handle->writeTcrInIsr = false; + handle->bytesPerFrame = (uint16_t)((base->TCR & LPSPI_TCR_FRAMESZ_MASK) >> LPSPI_TCR_FRAMESZ_SHIFT) / 8U + 1U; + /* No need to configure PCS continous if the transfer byte count is smaller than frame size */ + bool isPcsContinuous = (((transfer->configFlags & (uint32_t)kLPSPI_MasterPcsContinuous) != 0U) && + (transfer->dataSize > handle->bytesPerFrame)); + handle->writeRegRemainingTimes = + (transfer->dataSize / (uint32_t)handle->bytesPerFrame) * (((uint32_t)handle->bytesPerFrame + 3U) / 4U); + handle->readRegRemainingTimes = handle->writeRegRemainingTimes; + handle->txBuffIfNull = + ((uint32_t)dummyData) | ((uint32_t)dummyData << 8) | ((uint32_t)dummyData << 16) | ((uint32_t)dummyData << 24); + /*The TX and RX FIFO sizes are always the same*/ + handle->fifoSize = LPSPI_GetRxFifoSize(base); + handle->isPcsContinuous = isPcsContinuous; + handle->isByteSwap = ((transfer->configFlags & (uint32_t)kLPSPI_MasterByteSwap) != 0U); + /*Calculate the bytes for write/read the TX/RX register each time*/ + if (handle->bytesPerFrame <= 4U) + { + handle->bytesEachWrite = (uint8_t)handle->bytesPerFrame; + handle->bytesEachRead = (uint8_t)handle->bytesPerFrame; + } + else + { + handle->bytesEachWrite = 4U; + handle->bytesEachRead = 4U; + } + + /*Set the RX and TX watermarks to reduce the ISR times.*/ + if (handle->fifoSize > 1U) + { + txWatermark = 1U; + handle->rxWatermark = handle->fifoSize - 2U; + } + else + { + txWatermark = 0U; + handle->rxWatermark = 0U; + } + LPSPI_SetFifoWatermarks(base, txWatermark, handle->rxWatermark); + + /* If there is no rxData, mask the receive data so that receive data is not stored in receive FIFO. */ + if (handle->rxData == NULL) + { + isRxMask = true; + handle->rxRemainingByteCount = 0; + } + + /* Mask tx data in half duplex mode since the tx/rx share the same pin, so that the data received from slave is not + * interfered. */ + if (((temp == LPSPI_CFGR1_PINCFG(kLPSPI_SdiInSdiOut)) || (temp == LPSPI_CFGR1_PINCFG(kLPSPI_SdoInSdoOut))) && + (handle->txData == NULL)) + { + handle->isTxMask = true; + } + + /*Transfers will stall when transmit FIFO is empty or receive FIFO is full. */ + base->CFGR1 &= (~LPSPI_CFGR1_NOSTALL_MASK); + + /* Enable module for following configuration of TCR to take effect. */ + LPSPI_Enable(base, true); + + /* Configure transfer control register. */ + base->TCR = (base->TCR & ~(LPSPI_TCR_CONT_MASK | LPSPI_TCR_CONTC_MASK | LPSPI_TCR_RXMSK_MASK | + LPSPI_TCR_TXMSK_MASK | LPSPI_TCR_PCS_MASK)) | + LPSPI_TCR_PCS(whichPcs); + + /*TCR is also shared the FIFO , so wait for TCR written.*/ + if (!LPSPI_TxFifoReady(base)) + { + return kStatus_LPSPI_Timeout; + } + + /* PCS should be configured separately from the other bits, otherwise it will not take effect. */ + base->TCR |= LPSPI_TCR_CONT(isPcsContinuous) | LPSPI_TCR_CONTC(isPcsContinuous) | LPSPI_TCR_RXMSK(isRxMask); + + /* Enable the NVIC for LPSPI peripheral. Note that below code is useless if the LPSPI interrupt is in INTMUX , + * and you should also enable the INTMUX interupt in your application. + */ + (void)EnableIRQ(s_lpspiIRQ[LPSPI_GetInstance(base)]); + + /*TCR is also shared the FIFO , so wait for TCR written.*/ + if (!LPSPI_TxFifoReady(base)) + { + return kStatus_LPSPI_Timeout; + } + + if (handle->isTxMask) + { + /* When TCR[TXMSK]=1, transfer is initiate by writting a new command word to TCR. TCR[TXMSK] is cleared by + hardware every time when TCR[FRAMESZ] bit of data is transfered. In this case TCR[TXMSK] should be set to + initiate each transfer. */ + + base->TCR |= LPSPI_TCR_TXMSK_MASK; + handle->txRemainingByteCount -= (uint32_t)handle->bytesPerFrame; + } + else + { + /* Fill up the TX data in FIFO to initiate transfer */ + LPSPI_MasterTransferFillUpTxFifo(base, handle); + } + + /* Since SPI is a synchronous interface, we only need to enable the RX interrupt if there is RX data. + * The IRQ handler will get the status of RX and TX interrupt flags. + */ + if (handle->rxData != NULL) + { + if (handle->isTxMask) + { + /* if tx data is masked, transfer is initiated by writing 1 to TCR[TXMSK] and TCR[FRMESZ] bits of data is + read. If rx water mark is set larger than TCR[FRMESZ], rx interrupt will not be generated. Lower the rx + water mark setting */ + if ((handle->bytesPerFrame / 4U) < (uint16_t)handle->rxWatermark) + { + handle->rxWatermark = + (uint8_t)(handle->bytesPerFrame / 4U) > 0U ? (uint8_t)(handle->bytesPerFrame / 4U - 1U) : 0U; + base->FCR = (base->FCR & (~LPSPI_FCR_RXWATER_MASK)) | LPSPI_FCR_RXWATER(handle->rxWatermark); + } + } + else + { + /*Set rxWatermark to (readRegRemainingTimes-1) if readRegRemainingTimes less than rxWatermark. Otherwise + *there is not RX interrupt for the last datas because the RX count is not greater than rxWatermark. + */ + tmpTimes = handle->readRegRemainingTimes; + if (tmpTimes <= handle->rxWatermark) + { + base->FCR = (base->FCR & (~LPSPI_FCR_RXWATER_MASK)) | LPSPI_FCR_RXWATER(tmpTimes - 1U); + } + } + + LPSPI_EnableInterrupts(base, (uint32_t)kLPSPI_RxInterruptEnable); + } + else + { + LPSPI_EnableInterrupts(base, (uint32_t)kLPSPI_TxInterruptEnable); + } + + return kStatus_Success; +} + +static void LPSPI_MasterTransferFillUpTxFifo(LPSPI_Type *base, lpspi_master_handle_t *handle) +{ + assert(handle != NULL); + + uint32_t wordToSend = 0; + uint8_t fifoSize = handle->fifoSize; + uint32_t writeRegRemainingTimes = handle->writeRegRemainingTimes; + uint32_t readRegRemainingTimes = handle->readRegRemainingTimes; + size_t txRemainingByteCount = handle->txRemainingByteCount; + uint8_t bytesEachWrite = handle->bytesEachWrite; + bool isByteSwap = handle->isByteSwap; + + /* Make sure the difference in remaining TX and RX byte counts does not exceed FIFO depth + * and that the number of TX FIFO entries does not exceed the FIFO depth. + * But no need to make the protection if there is no rxData. + */ + while ((LPSPI_GetTxFifoCount(base) < fifoSize) && + (((readRegRemainingTimes - writeRegRemainingTimes) < (uint32_t)fifoSize) || (handle->rxData == NULL))) + { + if (txRemainingByteCount < (size_t)bytesEachWrite) + { + handle->bytesEachWrite = (uint8_t)txRemainingByteCount; + bytesEachWrite = handle->bytesEachWrite; + } + + if (handle->txData != NULL) + { + wordToSend = LPSPI_CombineWriteData(handle->txData, bytesEachWrite, isByteSwap); + handle->txData += bytesEachWrite; + } + else + { + wordToSend = handle->txBuffIfNull; + } + + /*Write the word to TX register*/ + LPSPI_WriteData(base, wordToSend); + + /*Decrease the write TX register times.*/ + --handle->writeRegRemainingTimes; + writeRegRemainingTimes = handle->writeRegRemainingTimes; + + /*Decrease the remaining TX byte count.*/ + handle->txRemainingByteCount -= (size_t)bytesEachWrite; + txRemainingByteCount = handle->txRemainingByteCount; + + if (handle->txRemainingByteCount == 0U) + { + /* If PCS is continuous, update TCR to de-assert PCS */ + if (handle->isPcsContinuous) + { + /* Only write to the TCR if the FIFO has room */ + if (LPSPI_GetTxFifoCount(base) < fifoSize) + { + base->TCR = (base->TCR & ~(LPSPI_TCR_CONTC_MASK)); + handle->writeTcrInIsr = false; + } + /* Else, set a global flag to tell the ISR to do write to the TCR */ + else + { + handle->writeTcrInIsr = true; + } + } + break; + } + } +} + +static void LPSPI_MasterTransferComplete(LPSPI_Type *base, lpspi_master_handle_t *handle) +{ + assert(handle != NULL); + + /* Disable interrupt requests*/ + LPSPI_DisableInterrupts(base, (uint32_t)kLPSPI_AllInterruptEnable); + + handle->state = (uint8_t)kLPSPI_Idle; + + if (handle->callback != NULL) + { + handle->callback(base, handle, kStatus_Success, handle->userData); + } +} + +/*! + * brief Gets the master transfer remaining bytes. + * + * This function gets the master transfer remaining bytes. + * + * param base LPSPI peripheral address. + * param handle pointer to lpspi_master_handle_t structure which stores the transfer state. + * param count Number of bytes transferred so far by the non-blocking transaction. + * return status of status_t. + */ +status_t LPSPI_MasterTransferGetCount(LPSPI_Type *base, lpspi_master_handle_t *handle, size_t *count) +{ + assert(handle != NULL); + + if (NULL == count) + { + return kStatus_InvalidArgument; + } + + /* Catch when there is not an active transfer. */ + if (handle->state != (uint8_t)kLPSPI_Busy) + { + *count = 0; + return kStatus_NoTransferInProgress; + } + + size_t remainingByte; + + if (handle->rxData != NULL) + { + remainingByte = handle->rxRemainingByteCount; + } + else + { + remainingByte = handle->txRemainingByteCount; + } + + *count = handle->totalByteCount - remainingByte; + + return kStatus_Success; +} + +/*! + * brief LPSPI master abort transfer which uses an interrupt method. + * + * This function aborts a transfer which uses an interrupt method. + * + * param base LPSPI peripheral address. + * param handle pointer to lpspi_master_handle_t structure which stores the transfer state. + */ +void LPSPI_MasterTransferAbort(LPSPI_Type *base, lpspi_master_handle_t *handle) +{ + assert(handle != NULL); + + /* Disable interrupt requests*/ + LPSPI_DisableInterrupts(base, (uint32_t)kLPSPI_AllInterruptEnable); + + LPSPI_Reset(base); + + handle->state = (uint8_t)kLPSPI_Idle; + handle->txRemainingByteCount = 0; + handle->rxRemainingByteCount = 0; +} + +/*! + * brief LPSPI Master IRQ handler function. + * + * This function processes the LPSPI transmit and receive IRQ. + * + * param base LPSPI peripheral address. + * param handle pointer to lpspi_master_handle_t structure which stores the transfer state. + */ +void LPSPI_MasterTransferHandleIRQ(LPSPI_Type *base, lpspi_master_handle_t *handle) +{ + assert(handle != NULL); + + uint32_t readData; + uint8_t bytesEachRead = handle->bytesEachRead; + bool isByteSwap = handle->isByteSwap; + uint32_t readRegRemainingTimes = handle->readRegRemainingTimes; + + if (handle->rxData != NULL) + { + if (handle->rxRemainingByteCount != 0U) + { + /* First, disable the interrupts to avoid potentially triggering another interrupt + * while reading out the RX FIFO as more data may be coming into the RX FIFO. We'll + * re-enable the interrupts based on the LPSPI state after reading out the FIFO. + */ + LPSPI_DisableInterrupts(base, (uint32_t)kLPSPI_RxInterruptEnable); + + while ((LPSPI_GetRxFifoCount(base) != 0U) && (handle->rxRemainingByteCount != 0U)) + { + /*Read out the data*/ + readData = LPSPI_ReadData(base); + + /*Decrease the read RX register times.*/ + --handle->readRegRemainingTimes; + readRegRemainingTimes = handle->readRegRemainingTimes; + + if (handle->rxRemainingByteCount < (size_t)bytesEachRead) + { + handle->bytesEachRead = (uint8_t)(handle->rxRemainingByteCount); + bytesEachRead = handle->bytesEachRead; + } + + LPSPI_SeparateReadData(handle->rxData, readData, bytesEachRead, isByteSwap); + handle->rxData += bytesEachRead; + + /*Decrease the remaining RX byte count.*/ + handle->rxRemainingByteCount -= (size_t)bytesEachRead; + } + + /* Re-enable the interrupts only if rxCount indicates there is more data to receive, + * else we may get a spurious interrupt. + * */ + if (handle->rxRemainingByteCount != 0U) + { + /* Set the TDF and RDF interrupt enables simultaneously to avoid race conditions */ + LPSPI_EnableInterrupts(base, (uint32_t)kLPSPI_RxInterruptEnable); + } + } + + /*Set rxWatermark to (readRegRemainingTimes-1) if readRegRemainingTimes less than rxWatermark. Otherwise there + *is not RX interrupt for the last datas because the RX count is not greater than rxWatermark. + */ + if (readRegRemainingTimes <= (uint32_t)handle->rxWatermark) + { + base->FCR = (base->FCR & (~LPSPI_FCR_RXWATER_MASK)) | + LPSPI_FCR_RXWATER((readRegRemainingTimes > 1U) ? (readRegRemainingTimes - 1U) : (0U)); + } + } + + if (handle->txRemainingByteCount != 0U) + { + if (handle->isTxMask) + { + /* When TCR[TXMSK]=1, transfer is initiate by writting a new command word to TCR. TCR[TXMSK] is cleared by + hardware every time when TCR[FRAMESZ] bit of data is transfered. + In this case TCR[TXMSK] should be set to initiate each transfer. */ + base->TCR |= LPSPI_TCR_TXMSK_MASK; + if ((handle->txRemainingByteCount == (uint32_t)handle->bytesPerFrame) && (handle->isPcsContinuous)) + { + /* For the last piece of frame size of data, if is PCS continous mode(TCR[CONT]), TCR[CONTC] should + * be cleared to de-assert the PCS. Be sure to clear the TXMSK as well otherwise another FRAMESZ + * of data will be received. */ + base->TCR &= ~(LPSPI_TCR_CONTC_MASK | LPSPI_TCR_CONT_MASK | LPSPI_TCR_TXMSK_MASK); + } + handle->txRemainingByteCount -= (uint32_t)handle->bytesPerFrame; + } + else + { + LPSPI_MasterTransferFillUpTxFifo(base, handle); + } + } + else + { + if ((LPSPI_GetTxFifoCount(base) < (handle->fifoSize))) + { + if ((handle->isPcsContinuous) && (handle->writeTcrInIsr) && (!handle->isTxMask)) + { + base->TCR = (base->TCR & ~(LPSPI_TCR_CONTC_MASK)); + handle->writeTcrInIsr = false; + } + } + } + + if ((handle->txRemainingByteCount == 0U) && (handle->rxRemainingByteCount == 0U) && (!handle->writeTcrInIsr)) + { + /* If no RX buffer, then transfer is not complete until transfer complete flag sets */ + if (handle->rxData == NULL) + { + if ((LPSPI_GetStatusFlags(base) & (uint32_t)kLPSPI_TransferCompleteFlag) != 0U) + { + LPSPI_ClearStatusFlags(base, (uint32_t)kLPSPI_TransferCompleteFlag); + /* Complete the transfer and disable the interrupts */ + LPSPI_MasterTransferComplete(base, handle); + } + else + { + LPSPI_EnableInterrupts(base, (uint32_t)kLPSPI_TransferCompleteInterruptEnable); + LPSPI_DisableInterrupts(base, (uint32_t)kLPSPI_TxInterruptEnable | (uint32_t)kLPSPI_RxInterruptEnable); + } + } + else + { + /* Complete the transfer and disable the interrupts */ + LPSPI_MasterTransferComplete(base, handle); + } + } +} + +/*Transactional APIs -- Slave*/ +/*! + * brief Initializes the LPSPI slave handle. + * + * This function initializes the LPSPI handle, which can be used for other LPSPI transactional APIs. Usually, for a + * specified LPSPI instance, call this API once to get the initialized handle. + * + * param base LPSPI peripheral address. + * param handle LPSPI handle pointer to lpspi_slave_handle_t. + * param callback DSPI callback. + * param userData callback function parameter. + */ +void LPSPI_SlaveTransferCreateHandle(LPSPI_Type *base, + lpspi_slave_handle_t *handle, + lpspi_slave_transfer_callback_t callback, + void *userData) +{ + assert(handle != NULL); + + /* Zero the handle. */ + (void)memset(handle, 0, sizeof(*handle)); + + s_lpspiHandle[LPSPI_GetInstance(base)] = handle; + + /* Set irq handler. */ + s_lpspiSlaveIsr = LPSPI_SlaveTransferHandleIRQ; + + handle->callback = callback; + handle->userData = userData; +} + +/*! + * brief LPSPI slave transfer data using an interrupt method. + * + * This function transfer data using an interrupt method. This is a non-blocking function, which returns right away. + * When all data is transferred, the callback function is called. + * + * Note: + * The transfer data size should be integer multiples of bytesPerFrame if bytesPerFrame is less than or equal to 4. + * For bytesPerFrame greater than 4: + * The transfer data size should be equal to bytesPerFrame if the bytesPerFrame is not an integer multiple of 4. + * Otherwise, the transfer data size can be an integer multiple of bytesPerFrame. + * + * param base LPSPI peripheral address. + * param handle pointer to lpspi_slave_handle_t structure which stores the transfer state. + * param transfer pointer to lpspi_transfer_t structure. + * return status of status_t. + */ +status_t LPSPI_SlaveTransferNonBlocking(LPSPI_Type *base, lpspi_slave_handle_t *handle, lpspi_transfer_t *transfer) +{ + assert(handle != NULL); + assert(transfer != NULL); + + /* Check that we're not busy.*/ + if (handle->state == (uint8_t)kLPSPI_Busy) + { + return kStatus_LPSPI_Busy; + } + LPSPI_Enable(base, false); + /* Check arguements */ + if (!LPSPI_CheckTransferArgument(base, transfer, false)) + { + return kStatus_InvalidArgument; + } + + /* Flush FIFO, clear status, disable all the inerrupts. */ + LPSPI_FlushFifo(base, true, true); + LPSPI_ClearStatusFlags(base, (uint32_t)kLPSPI_AllStatusFlag); + LPSPI_DisableInterrupts(base, (uint32_t)kLPSPI_AllInterruptEnable); + + /* Variables */ + bool isRxMask = false; + bool isTxMask = false; + uint8_t txWatermark; + uint32_t readRegRemainingTimes; + uint32_t whichPcs = (transfer->configFlags & LPSPI_SLAVE_PCS_MASK) >> LPSPI_SLAVE_PCS_SHIFT; + uint32_t bytesPerFrame = ((base->TCR & LPSPI_TCR_FRAMESZ_MASK) >> LPSPI_TCR_FRAMESZ_SHIFT) / 8U + 1U; + + /* Assign the original value for members of transfer handle. */ + handle->state = (uint8_t)kLPSPI_Busy; + handle->txData = transfer->txData; + handle->rxData = transfer->rxData; + handle->txRemainingByteCount = transfer->dataSize; + handle->rxRemainingByteCount = transfer->dataSize; + handle->totalByteCount = transfer->dataSize; + handle->writeRegRemainingTimes = (transfer->dataSize / bytesPerFrame) * ((bytesPerFrame + 3U) / 4U); + handle->readRegRemainingTimes = handle->writeRegRemainingTimes; + /*The TX and RX FIFO sizes are always the same*/ + handle->fifoSize = LPSPI_GetRxFifoSize(base); + handle->isByteSwap = ((transfer->configFlags & (uint32_t)kLPSPI_SlaveByteSwap) != 0U); + /*Calculate the bytes for write/read the TX/RX register each time*/ + if (bytesPerFrame <= 4U) + { + handle->bytesEachWrite = (uint8_t)bytesPerFrame; + handle->bytesEachRead = (uint8_t)bytesPerFrame; + } + else + { + handle->bytesEachWrite = 4U; + handle->bytesEachRead = 4U; + } + /* Set proper RX and TX watermarks to reduce the ISR response times. */ + if (handle->fifoSize > 1U) + { + txWatermark = 1U; + handle->rxWatermark = handle->fifoSize - 2U; + } + else + { + txWatermark = 0U; + handle->rxWatermark = 0U; + } + LPSPI_SetFifoWatermarks(base, txWatermark, handle->rxWatermark); + + /* If there is no rxData, mask the receive data so that receive data is not stored in receive FIFO. */ + if (handle->rxData == NULL) + { + isRxMask = true; + handle->rxRemainingByteCount = 0U; + } + /* If there is no txData, mask the transmit data so that no data is loaded from transmit FIFO and output pin + * is tristated. */ + if (handle->txData == NULL) + { + isTxMask = true; + handle->txRemainingByteCount = 0U; + } + + /* Enable module for following configuration of TCR to take effect. */ + LPSPI_Enable(base, true); + + base->TCR = (base->TCR & ~(LPSPI_TCR_CONT_MASK | LPSPI_TCR_CONTC_MASK | LPSPI_TCR_RXMSK_MASK | + LPSPI_TCR_TXMSK_MASK | LPSPI_TCR_PCS_MASK)) | + LPSPI_TCR_RXMSK(isRxMask) | LPSPI_TCR_TXMSK(isTxMask) | LPSPI_TCR_PCS(whichPcs); + + /* Enable the NVIC for LPSPI peripheral. Note that below code is useless if the LPSPI interrupt is in INTMUX , + * and you should also enable the INTMUX interupt in your application. + */ + (void)EnableIRQ(s_lpspiIRQ[LPSPI_GetInstance(base)]); + + /*TCR is also shared the FIFO, so wait for TCR written.*/ + if (!LPSPI_TxFifoReady(base)) + { + return kStatus_LPSPI_Timeout; + } + + /* Fill up the TX data in FIFO */ + if (handle->txData != NULL) + { + LPSPI_SlaveTransferFillUpTxFifo(base, handle); + } + + /* Since SPI is a synchronous interface, we only need to enable the RX interrupt if there is RX data. + * The IRQ handler will get the status of RX and TX interrupt flags. + */ + if (handle->rxData != NULL) + { + /*Set rxWatermark to (readRegRemainingTimes-1) if readRegRemainingTimes less than rxWatermark. Otherwise there + *is not RX interrupt for the last datas because the RX count is not greater than rxWatermark. + */ + readRegRemainingTimes = handle->readRegRemainingTimes; + if (readRegRemainingTimes <= (uint32_t)handle->rxWatermark) + { + base->FCR = (base->FCR & (~LPSPI_FCR_RXWATER_MASK)) | LPSPI_FCR_RXWATER(readRegRemainingTimes - 1U); + } + + /* RX request and FIFO overflow request enable */ + LPSPI_EnableInterrupts(base, (uint32_t)kLPSPI_RxInterruptEnable | (uint32_t)kLPSPI_ReceiveErrorInterruptEnable); + } + else + { + LPSPI_EnableInterrupts(base, (uint32_t)kLPSPI_TxInterruptEnable); + } + + if (handle->txData != NULL) + { + /* TX FIFO underflow request enable */ + LPSPI_EnableInterrupts(base, (uint32_t)kLPSPI_TransmitErrorInterruptEnable); + } + + return kStatus_Success; +} + +static void LPSPI_SlaveTransferFillUpTxFifo(LPSPI_Type *base, lpspi_slave_handle_t *handle) +{ + assert(handle != NULL); + + uint32_t wordToSend = 0U; + uint8_t bytesEachWrite = handle->bytesEachWrite; + bool isByteSwap = handle->isByteSwap; + + while (LPSPI_GetTxFifoCount(base) < (handle->fifoSize)) + { + if (handle->txRemainingByteCount < (size_t)bytesEachWrite) + { + handle->bytesEachWrite = (uint8_t)handle->txRemainingByteCount; + bytesEachWrite = handle->bytesEachWrite; + } + + wordToSend = LPSPI_CombineWriteData(handle->txData, bytesEachWrite, isByteSwap); + handle->txData += bytesEachWrite; + + /*Decrease the remaining TX byte count.*/ + handle->txRemainingByteCount -= (size_t)bytesEachWrite; + + /*Write the word to TX register*/ + LPSPI_WriteData(base, wordToSend); + + if (handle->txRemainingByteCount == 0U) + { + break; + } + } +} + +static void LPSPI_SlaveTransferComplete(LPSPI_Type *base, lpspi_slave_handle_t *handle) +{ + assert(handle != NULL); + + status_t status = kStatus_Success; + + /* Disable interrupt requests*/ + LPSPI_DisableInterrupts(base, (uint32_t)kLPSPI_AllInterruptEnable); + + if (handle->state == (uint8_t)kLPSPI_Error) + { + status = kStatus_LPSPI_Error; + } + else + { + status = kStatus_Success; + } + + handle->state = (uint8_t)kLPSPI_Idle; + + if (handle->callback != NULL) + { + handle->callback(base, handle, status, handle->userData); + } +} + +/*! + * brief Gets the slave transfer remaining bytes. + * + * This function gets the slave transfer remaining bytes. + * + * param base LPSPI peripheral address. + * param handle pointer to lpspi_slave_handle_t structure which stores the transfer state. + * param count Number of bytes transferred so far by the non-blocking transaction. + * return status of status_t. + */ +status_t LPSPI_SlaveTransferGetCount(LPSPI_Type *base, lpspi_slave_handle_t *handle, size_t *count) +{ + assert(handle != NULL); + + if (NULL == count) + { + return kStatus_InvalidArgument; + } + + /* Catch when there is not an active transfer. */ + if (handle->state != (uint8_t)kLPSPI_Busy) + { + *count = 0; + return kStatus_NoTransferInProgress; + } + + size_t remainingByte; + + if (handle->rxData != NULL) + { + remainingByte = handle->rxRemainingByteCount; + } + else + { + remainingByte = handle->txRemainingByteCount; + } + + *count = handle->totalByteCount - remainingByte; + + return kStatus_Success; +} + +/*! + * brief LPSPI slave aborts a transfer which uses an interrupt method. + * + * This function aborts a transfer which uses an interrupt method. + * + * param base LPSPI peripheral address. + * param handle pointer to lpspi_slave_handle_t structure which stores the transfer state. + */ +void LPSPI_SlaveTransferAbort(LPSPI_Type *base, lpspi_slave_handle_t *handle) +{ + assert(handle != NULL); + + /* Disable interrupt requests*/ + LPSPI_DisableInterrupts(base, (uint32_t)kLPSPI_TxInterruptEnable | (uint32_t)kLPSPI_RxInterruptEnable); + + LPSPI_Reset(base); + + handle->state = (uint8_t)kLPSPI_Idle; + handle->txRemainingByteCount = 0U; + handle->rxRemainingByteCount = 0U; +} + +/*! + * brief LPSPI Slave IRQ handler function. + * + * This function processes the LPSPI transmit and receives an IRQ. + * + * param base LPSPI peripheral address. + * param handle pointer to lpspi_slave_handle_t structure which stores the transfer state. + */ +void LPSPI_SlaveTransferHandleIRQ(LPSPI_Type *base, lpspi_slave_handle_t *handle) +{ + assert(handle != NULL); + + uint32_t readData; /* variable to store word read from RX FIFO */ + uint8_t bytesEachRead = handle->bytesEachRead; + bool isByteSwap = handle->isByteSwap; + uint32_t readRegRemainingTimes; + + if (handle->rxData != NULL) + { + if (handle->rxRemainingByteCount > 0U) + { + while (LPSPI_GetRxFifoCount(base) != 0U) + { + /*Read out the data*/ + readData = LPSPI_ReadData(base); + + /*Decrease the read RX register times.*/ + --handle->readRegRemainingTimes; + + if (handle->rxRemainingByteCount < (size_t)bytesEachRead) + { + handle->bytesEachRead = (uint8_t)handle->rxRemainingByteCount; + bytesEachRead = handle->bytesEachRead; + } + + LPSPI_SeparateReadData(handle->rxData, readData, bytesEachRead, isByteSwap); + handle->rxData += bytesEachRead; + + /*Decrease the remaining RX byte count.*/ + handle->rxRemainingByteCount -= (size_t)bytesEachRead; + + if ((handle->txRemainingByteCount > 0U) && (handle->txData != NULL)) + { + LPSPI_SlaveTransferFillUpTxFifo(base, handle); + } + + if (handle->rxRemainingByteCount == 0U) + { + break; + } + } + } + + /*Set rxWatermark to (readRegRemainingTimes-1) if readRegRemainingTimes less than rxWatermark. Otherwise there + *is not RX interrupt for the last datas because the RX count is not greater than rxWatermark. + */ + readRegRemainingTimes = handle->readRegRemainingTimes; + if (readRegRemainingTimes <= (uint32_t)handle->rxWatermark) + { + base->FCR = (base->FCR & (~LPSPI_FCR_RXWATER_MASK)) | + LPSPI_FCR_RXWATER((readRegRemainingTimes > 1U) ? (readRegRemainingTimes - 1U) : (0U)); + } + } + if ((handle->rxData == NULL) && (handle->txRemainingByteCount != 0U) && (handle->txData != NULL)) + { + LPSPI_SlaveTransferFillUpTxFifo(base, handle); + } + + if ((handle->txRemainingByteCount == 0U) && (handle->rxRemainingByteCount == 0U)) + { + /* If no RX buffer, then transfer is not complete until transfer complete flag sets and the TX FIFO empty*/ + if (handle->rxData == NULL) + { + if (((LPSPI_GetStatusFlags(base) & (uint32_t)kLPSPI_FrameCompleteFlag) != 0U) && + (LPSPI_GetTxFifoCount(base) == 0U)) + { + LPSPI_ClearStatusFlags(base, (uint32_t)kLPSPI_FrameCompleteFlag); + /* Complete the transfer and disable the interrupts */ + LPSPI_SlaveTransferComplete(base, handle); + } + else + { + LPSPI_ClearStatusFlags(base, (uint32_t)kLPSPI_FrameCompleteFlag); + LPSPI_EnableInterrupts(base, (uint32_t)kLPSPI_FrameCompleteInterruptEnable); + LPSPI_DisableInterrupts(base, (uint32_t)kLPSPI_TxInterruptEnable | (uint32_t)kLPSPI_RxInterruptEnable); + } + } + else + { + /* Complete the transfer and disable the interrupts */ + LPSPI_SlaveTransferComplete(base, handle); + } + } + + /* Catch tx fifo underflow conditions, service only if tx under flow interrupt enabled */ + if (((LPSPI_GetStatusFlags(base) & (uint32_t)kLPSPI_TransmitErrorFlag) != 0U) && + ((base->IER & LPSPI_IER_TEIE_MASK) != 0U)) + { + LPSPI_ClearStatusFlags(base, (uint32_t)kLPSPI_TransmitErrorFlag); + /* Change state to error and clear flag */ + if (handle->txData != NULL) + { + handle->state = (uint8_t)kLPSPI_Error; + } + handle->errorCount++; + } + /* Catch rx fifo overflow conditions, service only if rx over flow interrupt enabled */ + if (((LPSPI_GetStatusFlags(base) & (uint32_t)kLPSPI_ReceiveErrorFlag) != 0U) && + ((base->IER & LPSPI_IER_REIE_MASK) != 0U)) + { + LPSPI_ClearStatusFlags(base, (uint32_t)kLPSPI_ReceiveErrorFlag); + /* Change state to error and clear flag */ + if (handle->txData != NULL) + { + handle->state = (uint8_t)kLPSPI_Error; + } + handle->errorCount++; + } +} + +static uint32_t LPSPI_CombineWriteData(uint8_t *txData, uint8_t bytesEachWrite, bool isByteSwap) +{ + assert(txData != NULL); + + uint32_t wordToSend = 0U; + + switch (bytesEachWrite) + { + case 1: + wordToSend = *txData; + ++txData; + break; + + case 2: + if (!isByteSwap) + { + wordToSend = *txData; + ++txData; + wordToSend |= (unsigned)(*txData) << 8U; + ++txData; + } + else + { + wordToSend = (unsigned)(*txData) << 8U; + ++txData; + wordToSend |= *txData; + ++txData; + } + + break; + + case 3: + if (!isByteSwap) + { + wordToSend = *txData; + ++txData; + wordToSend |= (unsigned)(*txData) << 8U; + ++txData; + wordToSend |= (unsigned)(*txData) << 16U; + ++txData; + } + else + { + wordToSend = (unsigned)(*txData) << 16U; + ++txData; + wordToSend |= (unsigned)(*txData) << 8U; + ++txData; + wordToSend |= *txData; + ++txData; + } + break; + + case 4: + if (!isByteSwap) + { + wordToSend = *txData; + ++txData; + wordToSend |= (unsigned)(*txData) << 8U; + ++txData; + wordToSend |= (unsigned)(*txData) << 16U; + ++txData; + wordToSend |= (unsigned)(*txData) << 24U; + ++txData; + } + else + { + wordToSend = (unsigned)(*txData) << 24U; + ++txData; + wordToSend |= (unsigned)(*txData) << 16U; + ++txData; + wordToSend |= (unsigned)(*txData) << 8U; + ++txData; + wordToSend |= *txData; + ++txData; + } + break; + + default: + assert(false); + break; + } + return wordToSend; +} + +static void LPSPI_SeparateReadData(uint8_t *rxData, uint32_t readData, uint8_t bytesEachRead, bool isByteSwap) +{ + assert(rxData != NULL); + + switch (bytesEachRead) + { + case 1: + *rxData = (uint8_t)readData; + ++rxData; + break; + + case 2: + if (!isByteSwap) + { + *rxData = (uint8_t)readData; + ++rxData; + *rxData = (uint8_t)(readData >> 8); + ++rxData; + } + else + { + *rxData = (uint8_t)(readData >> 8); + ++rxData; + *rxData = (uint8_t)readData; + ++rxData; + } + break; + + case 3: + if (!isByteSwap) + { + *rxData = (uint8_t)readData; + ++rxData; + *rxData = (uint8_t)(readData >> 8); + ++rxData; + *rxData = (uint8_t)(readData >> 16); + ++rxData; + } + else + { + *rxData = (uint8_t)(readData >> 16); + ++rxData; + *rxData = (uint8_t)(readData >> 8); + ++rxData; + *rxData = (uint8_t)readData; + ++rxData; + } + break; + + case 4: + if (!isByteSwap) + { + *rxData = (uint8_t)readData; + ++rxData; + *rxData = (uint8_t)(readData >> 8); + ++rxData; + *rxData = (uint8_t)(readData >> 16); + ++rxData; + *rxData = (uint8_t)(readData >> 24); + ++rxData; + } + else + { + *rxData = (uint8_t)(readData >> 24); + ++rxData; + *rxData = (uint8_t)(readData >> 16); + ++rxData; + *rxData = (uint8_t)(readData >> 8); + ++rxData; + *rxData = (uint8_t)readData; + ++rxData; + } + break; + + default: + assert(false); + break; + } +} + +static bool LPSPI_TxFifoReady(LPSPI_Type *base) +{ +#if SPI_RETRY_TIMES + uint32_t waitTimes = SPI_RETRY_TIMES; + while (((uint8_t)LPSPI_GetTxFifoCount(base) != 0U) && (--waitTimes != 0U)) +#else + while ((uint8_t)LPSPI_GetTxFifoCount(base) != 0U) +#endif + { + } +#if SPI_RETRY_TIMES + if (waitTimes == 0U) + { + return false; + } +#endif + return true; +} + +static void LPSPI_CommonIRQHandler(LPSPI_Type *base, void *param) +{ + if (LPSPI_IsMaster(base)) + { + s_lpspiMasterIsr(base, (lpspi_master_handle_t *)param); + } + else + { + s_lpspiSlaveIsr(base, (lpspi_slave_handle_t *)param); + } + SDK_ISR_EXIT_BARRIER; +} + +#if defined(LPSPI0) +void LPSPI0_DriverIRQHandler(void); +void LPSPI0_DriverIRQHandler(void) +{ + assert(s_lpspiHandle[0] != NULL); + LPSPI_CommonIRQHandler(LPSPI0, s_lpspiHandle[0]); +} +#endif + +#if defined(LPSPI1) +void LPSPI1_DriverIRQHandler(void); +void LPSPI1_DriverIRQHandler(void) +{ + assert(s_lpspiHandle[1] != NULL); + LPSPI_CommonIRQHandler(LPSPI1, s_lpspiHandle[1]); +} +#endif + +#if defined(LPSPI2) +void LPSPI2_DriverIRQHandler(void); +void LPSPI2_DriverIRQHandler(void) +{ + assert(s_lpspiHandle[2] != NULL); + LPSPI_CommonIRQHandler(LPSPI2, s_lpspiHandle[2]); +} +#endif + +#if defined(LPSPI3) +void LPSPI3_DriverIRQHandler(void); +void LPSPI3_DriverIRQHandler(void) +{ + assert(s_lpspiHandle[3] != NULL); + LPSPI_CommonIRQHandler(LPSPI3, s_lpspiHandle[3]); +} +#endif + +#if defined(LPSPI4) +void LPSPI4_DriverIRQHandler(void); +void LPSPI4_DriverIRQHandler(void) +{ + assert(s_lpspiHandle[4] != NULL); + LPSPI_CommonIRQHandler(LPSPI4, s_lpspiHandle[4]); +} +#endif + +#if defined(LPSPI5) +void LPSPI5_DriverIRQHandler(void); +void LPSPI5_DriverIRQHandler(void) +{ + assert(s_lpspiHandle[5] != NULL); + LPSPI_CommonIRQHandler(LPSPI5, s_lpspiHandle[5]); +} +#endif + +#if defined(DMA__LPSPI0) +void DMA_SPI0_INT_DriverIRQHandler(void); +void DMA_SPI0_INT_DriverIRQHandler(void) +{ + assert(s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI0)] != NULL); + LPSPI_CommonIRQHandler(DMA__LPSPI0, s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI0)]); +} +#endif + +#if defined(DMA__LPSPI1) +void DMA_SPI1_INT_DriverIRQHandler(void); +void DMA_SPI1_INT_DriverIRQHandler(void) +{ + assert(s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI1)] != NULL); + LPSPI_CommonIRQHandler(DMA__LPSPI1, s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI1)]); +} +#endif +#if defined(DMA__LPSPI2) +void DMA_SPI2_INT_DriverIRQHandler(void); +void DMA_SPI2_INT_DriverIRQHandler(void) +{ + assert(s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI2)] != NULL); + LPSPI_CommonIRQHandler(DMA__LPSPI2, s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI2)]); +} +#endif + +#if defined(DMA__LPSPI3) +void DMA_SPI3_INT_DriverIRQHandler(void); +void DMA_SPI3_INT_DriverIRQHandler(void) +{ + assert(s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI3)] != NULL); + LPSPI_CommonIRQHandler(DMA__LPSPI3, s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI3)]); +} +#endif + +#if defined(ADMA__LPSPI0) +void ADMA_SPI0_INT_DriverIRQHandler(void); +void ADMA_SPI0_INT_DriverIRQHandler(void) +{ + assert(s_lpspiHandle[LPSPI_GetInstance(ADMA__LPSPI0)] != NULL); + LPSPI_CommonIRQHandler(ADMA__LPSPI0, s_lpspiHandle[LPSPI_GetInstance(ADMA__LPSPI0)]); +} +#endif + +#if defined(ADMA__LPSPI1) +void ADMA_SPI1_INT_DriverIRQHandler(void); +void ADMA_SPI1_INT_DriverIRQHandler(void) +{ + assert(s_lpspiHandle[LPSPI_GetInstance(ADMA__LPSPI1)] != NULL); + LPSPI_CommonIRQHandler(ADMA__LPSPI1, s_lpspiHandle[LPSPI_GetInstance(ADMA__LPSPI1)]); +} +#endif +#if defined(ADMA__LPSPI2) +void ADMA_SPI2_INT_DriverIRQHandler(void); +void ADMA_SPI2_INT_DriverIRQHandler(void) +{ + assert(s_lpspiHandle[LPSPI_GetInstance(ADMA__LPSPI2)] != NULL); + LPSPI_CommonIRQHandler(ADMA__LPSPI2, s_lpspiHandle[LPSPI_GetInstance(ADMA__LPSPI2)]); +} +#endif + +#if defined(ADMA__LPSPI3) +void ADMA_SPI3_INT_DriverIRQHandler(void); +void ADMA_SPI3_INT_DriverIRQHandler(void) +{ + assert(s_lpspiHandle[LPSPI_GetInstance(ADMA__LPSPI3)] != NULL); + LPSPI_CommonIRQHandler(ADMA__LPSPI3, s_lpspiHandle[LPSPI_GetInstance(ADMA__LPSPI3)]); +} +#endif diff --git a/bsps/arm/imxrt/mcux-sdk/drivers/lpspi/fsl_lpspi.h b/bsps/arm/imxrt/mcux-sdk/drivers/lpspi/fsl_lpspi.h new file mode 100644 index 0000000000..98a4116982 --- /dev/null +++ b/bsps/arm/imxrt/mcux-sdk/drivers/lpspi/fsl_lpspi.h @@ -0,0 +1,1158 @@ +/* + * Copyright (c) 2015, Freescale Semiconductor, Inc. + * Copyright 2016-2022 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef _FSL_LPSPI_H_ +#define _FSL_LPSPI_H_ + +#include "fsl_common.h" + +/*! + * @addtogroup lpspi_driver + * @{ + */ + +/********************************************************************************************************************** + * Definitions + *********************************************************************************************************************/ + +/*! @name Driver version */ +/*@{*/ +/*! @brief LPSPI driver version. */ +#define FSL_LPSPI_DRIVER_VERSION (MAKE_VERSION(2, 4, 0)) +/*@}*/ + +#ifndef LPSPI_DUMMY_DATA +/*! @brief LPSPI dummy data if no Tx data.*/ +#define LPSPI_DUMMY_DATA (0x00U) /*!< Dummy data used for tx if there is not txData. */ +#endif + +/*! @brief Retry times for waiting flag. */ +#ifndef SPI_RETRY_TIMES +#define SPI_RETRY_TIMES 0U /* Define to zero means keep waiting until the flag is assert/deassert. */ +#endif + +/*! @brief Global variable for dummy data value setting. */ +extern volatile uint8_t g_lpspiDummyData[]; + +/*! @brief Status for the LPSPI driver.*/ +enum +{ + kStatus_LPSPI_Busy = MAKE_STATUS(kStatusGroup_LPSPI, 0), /*!< LPSPI transfer is busy.*/ + kStatus_LPSPI_Error = MAKE_STATUS(kStatusGroup_LPSPI, 1), /*!< LPSPI driver error. */ + kStatus_LPSPI_Idle = MAKE_STATUS(kStatusGroup_LPSPI, 2), /*!< LPSPI is idle.*/ + kStatus_LPSPI_OutOfRange = MAKE_STATUS(kStatusGroup_LPSPI, 3), /*!< LPSPI transfer out Of range. */ + kStatus_LPSPI_Timeout = MAKE_STATUS(kStatusGroup_LPSPI, 4) /*!< LPSPI timeout polling status flags. */ +}; + +/*! @brief LPSPI status flags in SPIx_SR register.*/ +enum _lpspi_flags +{ + kLPSPI_TxDataRequestFlag = LPSPI_SR_TDF_MASK, /*!< Transmit data flag */ + kLPSPI_RxDataReadyFlag = LPSPI_SR_RDF_MASK, /*!< Receive data flag */ + kLPSPI_WordCompleteFlag = LPSPI_SR_WCF_MASK, /*!< Word Complete flag */ + kLPSPI_FrameCompleteFlag = LPSPI_SR_FCF_MASK, /*!< Frame Complete flag */ + kLPSPI_TransferCompleteFlag = LPSPI_SR_TCF_MASK, /*!< Transfer Complete flag */ + kLPSPI_TransmitErrorFlag = LPSPI_SR_TEF_MASK, /*!< Transmit Error flag (FIFO underrun) */ + kLPSPI_ReceiveErrorFlag = LPSPI_SR_REF_MASK, /*!< Receive Error flag (FIFO overrun) */ + kLPSPI_DataMatchFlag = LPSPI_SR_DMF_MASK, /*!< Data Match flag */ + kLPSPI_ModuleBusyFlag = LPSPI_SR_MBF_MASK, /*!< Module Busy flag */ + kLPSPI_AllStatusFlag = (LPSPI_SR_TDF_MASK | LPSPI_SR_RDF_MASK | LPSPI_SR_WCF_MASK | LPSPI_SR_FCF_MASK | + LPSPI_SR_TCF_MASK | LPSPI_SR_TEF_MASK | LPSPI_SR_REF_MASK | LPSPI_SR_DMF_MASK | + LPSPI_SR_MBF_MASK) /*!< Used for clearing all w1c status flags */ +}; + +/*! @brief LPSPI interrupt source.*/ +enum _lpspi_interrupt_enable +{ + kLPSPI_TxInterruptEnable = LPSPI_IER_TDIE_MASK, /*!< Transmit data interrupt enable */ + kLPSPI_RxInterruptEnable = LPSPI_IER_RDIE_MASK, /*!< Receive data interrupt enable */ + kLPSPI_WordCompleteInterruptEnable = LPSPI_IER_WCIE_MASK, /*!< Word complete interrupt enable */ + kLPSPI_FrameCompleteInterruptEnable = LPSPI_IER_FCIE_MASK, /*!< Frame complete interrupt enable */ + kLPSPI_TransferCompleteInterruptEnable = LPSPI_IER_TCIE_MASK, /*!< Transfer complete interrupt enable */ + kLPSPI_TransmitErrorInterruptEnable = LPSPI_IER_TEIE_MASK, /*!< Transmit error interrupt enable(FIFO underrun)*/ + kLPSPI_ReceiveErrorInterruptEnable = LPSPI_IER_REIE_MASK, /*!< Receive Error interrupt enable (FIFO overrun) */ + kLPSPI_DataMatchInterruptEnable = LPSPI_IER_DMIE_MASK, /*!< Data Match interrupt enable */ + kLPSPI_AllInterruptEnable = + (LPSPI_IER_TDIE_MASK | LPSPI_IER_RDIE_MASK | LPSPI_IER_WCIE_MASK | LPSPI_IER_FCIE_MASK | LPSPI_IER_TCIE_MASK | + LPSPI_IER_TEIE_MASK | LPSPI_IER_REIE_MASK | LPSPI_IER_DMIE_MASK) /*!< All above interrupts enable.*/ +}; + +/*! @brief LPSPI DMA source.*/ +enum _lpspi_dma_enable +{ + kLPSPI_TxDmaEnable = LPSPI_DER_TDDE_MASK, /*!< Transmit data DMA enable */ + kLPSPI_RxDmaEnable = LPSPI_DER_RDDE_MASK /*!< Receive data DMA enable */ +}; + +/*! @brief LPSPI master or slave mode configuration.*/ +typedef enum _lpspi_master_slave_mode +{ + kLPSPI_Master = 1U, /*!< LPSPI peripheral operates in master mode.*/ + kLPSPI_Slave = 0U /*!< LPSPI peripheral operates in slave mode.*/ +} lpspi_master_slave_mode_t; + +/*! @brief LPSPI Peripheral Chip Select (PCS) configuration (which PCS to configure).*/ +typedef enum _lpspi_which_pcs_config +{ + kLPSPI_Pcs0 = 0U, /*!< PCS[0] */ + kLPSPI_Pcs1 = 1U, /*!< PCS[1] */ + kLPSPI_Pcs2 = 2U, /*!< PCS[2] */ + kLPSPI_Pcs3 = 3U /*!< PCS[3] */ +} lpspi_which_pcs_t; + +/*! @brief LPSPI Peripheral Chip Select (PCS) Polarity configuration.*/ +typedef enum _lpspi_pcs_polarity_config +{ + kLPSPI_PcsActiveHigh = 1U, /*!< PCS Active High (idles low) */ + kLPSPI_PcsActiveLow = 0U /*!< PCS Active Low (idles high) */ +} lpspi_pcs_polarity_config_t; + +/*! @brief LPSPI Peripheral Chip Select (PCS) Polarity.*/ +enum _lpspi_pcs_polarity +{ + kLPSPI_Pcs0ActiveLow = 1U << 0, /*!< Pcs0 Active Low (idles high). */ + kLPSPI_Pcs1ActiveLow = 1U << 1, /*!< Pcs1 Active Low (idles high). */ + kLPSPI_Pcs2ActiveLow = 1U << 2, /*!< Pcs2 Active Low (idles high). */ + kLPSPI_Pcs3ActiveLow = 1U << 3, /*!< Pcs3 Active Low (idles high). */ + kLPSPI_PcsAllActiveLow = 0xFU /*!< Pcs0 to Pcs5 Active Low (idles high). */ +}; + +/*! @brief LPSPI clock polarity configuration.*/ +typedef enum _lpspi_clock_polarity +{ + kLPSPI_ClockPolarityActiveHigh = 0U, /*!< CPOL=0. Active-high LPSPI clock (idles low)*/ + kLPSPI_ClockPolarityActiveLow = 1U /*!< CPOL=1. Active-low LPSPI clock (idles high)*/ +} lpspi_clock_polarity_t; + +/*! @brief LPSPI clock phase configuration.*/ +typedef enum _lpspi_clock_phase +{ + kLPSPI_ClockPhaseFirstEdge = 0U, /*!< CPHA=0. Data is captured on the leading edge of the SCK and changed on the + following edge.*/ + kLPSPI_ClockPhaseSecondEdge = 1U /*!< CPHA=1. Data is changed on the leading edge of the SCK and captured on the + following edge.*/ +} lpspi_clock_phase_t; + +/*! @brief LPSPI data shifter direction options.*/ +typedef enum _lpspi_shift_direction +{ + kLPSPI_MsbFirst = 0U, /*!< Data transfers start with most significant bit.*/ + kLPSPI_LsbFirst = 1U /*!< Data transfers start with least significant bit.*/ +} lpspi_shift_direction_t; + +/*! @brief LPSPI Host Request select configuration. */ +typedef enum _lpspi_host_request_select +{ + kLPSPI_HostReqExtPin = 0U, /*!< Host Request is an ext pin. */ + kLPSPI_HostReqInternalTrigger = 1U /*!< Host Request is an internal trigger. */ +} lpspi_host_request_select_t; + +/*! @brief LPSPI Match configuration options. */ +typedef enum _lpspi_match_config +{ + kLPSI_MatchDisabled = 0x0U, /*!< LPSPI Match Disabled. */ + kLPSI_1stWordEqualsM0orM1 = 0x2U, /*!< LPSPI Match Enabled. */ + kLPSI_AnyWordEqualsM0orM1 = 0x3U, /*!< LPSPI Match Enabled. */ + kLPSI_1stWordEqualsM0and2ndWordEqualsM1 = 0x4U, /*!< LPSPI Match Enabled. */ + kLPSI_AnyWordEqualsM0andNxtWordEqualsM1 = 0x5U, /*!< LPSPI Match Enabled. */ + kLPSI_1stWordAndM1EqualsM0andM1 = 0x6U, /*!< LPSPI Match Enabled. */ + kLPSI_AnyWordAndM1EqualsM0andM1 = 0x7U, /*!< LPSPI Match Enabled. */ +} lpspi_match_config_t; + +/*! @brief LPSPI pin (SDO and SDI) configuration. */ +typedef enum _lpspi_pin_config +{ + kLPSPI_SdiInSdoOut = 0U, /*!< LPSPI SDI input, SDO output. */ + kLPSPI_SdiInSdiOut = 1U, /*!< LPSPI SDI input, SDI output. */ + kLPSPI_SdoInSdoOut = 2U, /*!< LPSPI SDO input, SDO output. */ + kLPSPI_SdoInSdiOut = 3U /*!< LPSPI SDO input, SDI output. */ +} lpspi_pin_config_t; + +/*! @brief LPSPI data output configuration. */ +typedef enum _lpspi_data_out_config +{ + kLpspiDataOutRetained = 0U, /*!< Data out retains last value when chip select is de-asserted */ + kLpspiDataOutTristate = 1U /*!< Data out is tristated when chip select is de-asserted */ +} lpspi_data_out_config_t; + +/*! @brief LPSPI transfer width configuration. */ +typedef enum _lpspi_transfer_width +{ + kLPSPI_SingleBitXfer = 0U, /*!< 1-bit shift at a time, data out on SDO, in on SDI (normal mode) */ + kLPSPI_TwoBitXfer = 1U, /*!< 2-bits shift out on SDO/SDI and in on SDO/SDI */ + kLPSPI_FourBitXfer = 2U /*!< 4-bits shift out on SDO/SDI/PCS[3:2] and in on SDO/SDI/PCS[3:2] */ +} lpspi_transfer_width_t; + +/*! @brief LPSPI delay type selection.*/ +typedef enum _lpspi_delay_type +{ + kLPSPI_PcsToSck = 1U, /*!< PCS-to-SCK delay. */ + kLPSPI_LastSckToPcs, /*!< Last SCK edge to PCS delay. */ + kLPSPI_BetweenTransfer /*!< Delay between transfers. */ +} lpspi_delay_type_t; + +#define LPSPI_MASTER_PCS_SHIFT (4U) /*!< LPSPI master PCS shift macro , internal used. */ +#define LPSPI_MASTER_PCS_MASK (0xF0U) /*!< LPSPI master PCS shift macro , internal used. */ + +/*! @brief Use this enumeration for LPSPI master transfer configFlags. */ +enum _lpspi_transfer_config_flag_for_master +{ + kLPSPI_MasterPcs0 = 0U << LPSPI_MASTER_PCS_SHIFT, /*!< LPSPI master transfer use PCS0 signal */ + kLPSPI_MasterPcs1 = 1U << LPSPI_MASTER_PCS_SHIFT, /*!< LPSPI master transfer use PCS1 signal */ + kLPSPI_MasterPcs2 = 2U << LPSPI_MASTER_PCS_SHIFT, /*!< LPSPI master transfer use PCS2 signal */ + kLPSPI_MasterPcs3 = 3U << LPSPI_MASTER_PCS_SHIFT, /*!< LPSPI master transfer use PCS3 signal */ + + kLPSPI_MasterPcsContinuous = 1U << 20, /*!< Is PCS signal continuous */ + + kLPSPI_MasterByteSwap = + 1U << 22 /*!< Is master swap the byte. + * For example, when want to send data 1 2 3 4 5 6 7 8 (suppose you set + * lpspi_shift_direction_t to MSB). + * 1. If you set bitPerFrame = 8 , no matter the kLPSPI_MasterByteSwapyou flag is used + * or not, the waveform is 1 2 3 4 5 6 7 8. + * 2. If you set bitPerFrame = 16 : + * (1) the waveform is 2 1 4 3 6 5 8 7 if you do not use the kLPSPI_MasterByteSwap flag. + * (2) the waveform is 1 2 3 4 5 6 7 8 if you use the kLPSPI_MasterByteSwap flag. + * 3. If you set bitPerFrame = 32 : + * (1) the waveform is 4 3 2 1 8 7 6 5 if you do not use the kLPSPI_MasterByteSwap flag. + * (2) the waveform is 1 2 3 4 5 6 7 8 if you use the kLPSPI_MasterByteSwap flag. + */ +}; + +#define LPSPI_SLAVE_PCS_SHIFT (4U) /*!< LPSPI slave PCS shift macro , internal used. */ +#define LPSPI_SLAVE_PCS_MASK (0xF0U) /*!< LPSPI slave PCS shift macro , internal used. */ + +/*! @brief Use this enumeration for LPSPI slave transfer configFlags. */ +enum _lpspi_transfer_config_flag_for_slave +{ + kLPSPI_SlavePcs0 = 0U << LPSPI_SLAVE_PCS_SHIFT, /*!< LPSPI slave transfer use PCS0 signal */ + kLPSPI_SlavePcs1 = 1U << LPSPI_SLAVE_PCS_SHIFT, /*!< LPSPI slave transfer use PCS1 signal */ + kLPSPI_SlavePcs2 = 2U << LPSPI_SLAVE_PCS_SHIFT, /*!< LPSPI slave transfer use PCS2 signal */ + kLPSPI_SlavePcs3 = 3U << LPSPI_SLAVE_PCS_SHIFT, /*!< LPSPI slave transfer use PCS3 signal */ + + kLPSPI_SlaveByteSwap = + 1U << 22 /*!< Is slave swap the byte. + * For example, when want to send data 1 2 3 4 5 6 7 8 (suppose you set + * lpspi_shift_direction_t to MSB). + * 1. If you set bitPerFrame = 8 , no matter the kLPSPI_SlaveByteSwap flag is used + * or not, the waveform is 1 2 3 4 5 6 7 8. + * 2. If you set bitPerFrame = 16 : + * (1) the waveform is 2 1 4 3 6 5 8 7 if you do not use the kLPSPI_SlaveByteSwap flag. + * (2) the waveform is 1 2 3 4 5 6 7 8 if you use the kLPSPI_SlaveByteSwap flag. + * 3. If you set bitPerFrame = 32 : + * (1) the waveform is 4 3 2 1 8 7 6 5 if you do not use the kLPSPI_SlaveByteSwap flag. + * (2) the waveform is 1 2 3 4 5 6 7 8 if you use the kLPSPI_SlaveByteSwap flag. + */ +}; + +/*! @brief LPSPI transfer state, which is used for LPSPI transactional API state machine. */ +enum _lpspi_transfer_state +{ + kLPSPI_Idle = 0x0U, /*!< Nothing in the transmitter/receiver. */ + kLPSPI_Busy, /*!< Transfer queue is not finished. */ + kLPSPI_Error /*!< Transfer error. */ +}; + +/*! @brief LPSPI master configuration structure.*/ +typedef struct _lpspi_master_config +{ + uint32_t baudRate; /*!< Baud Rate for LPSPI. */ + uint32_t bitsPerFrame; /*!< Bits per frame, minimum 8, maximum 4096.*/ + lpspi_clock_polarity_t cpol; /*!< Clock polarity. */ + lpspi_clock_phase_t cpha; /*!< Clock phase. */ + lpspi_shift_direction_t direction; /*!< MSB or LSB data shift direction. */ + + uint32_t pcsToSckDelayInNanoSec; /*!< PCS to SCK delay time in nanoseconds, setting to 0 sets the minimum delay. + It sets the boundary value if out of range.*/ + uint32_t lastSckToPcsDelayInNanoSec; /*!< Last SCK to PCS delay time in nanoseconds, setting to 0 sets the minimum + delay. It sets the boundary value if out of range.*/ + uint32_t betweenTransferDelayInNanoSec; /*!< After the SCK delay time with nanoseconds, setting to 0 sets the + minimum delay. It sets the boundary value if out of range.*/ + + lpspi_which_pcs_t whichPcs; /*!< Desired Peripheral Chip Select (PCS). */ + lpspi_pcs_polarity_config_t pcsActiveHighOrLow; /*!< Desired PCS active high or low */ + + lpspi_pin_config_t pinCfg; /*!< Configures which pins are used for input and output data + *during single bit transfers.*/ + + lpspi_data_out_config_t dataOutConfig; /*!< Configures if the output data is tristated + * between accesses (LPSPI_PCS is negated). */ + bool enableInputDelay; /*!< Enable master to sample the input data on a delayed SCK. This can help improve slave + setup time. Refer to device data sheet for specific time length. */ +} lpspi_master_config_t; + +/*! @brief LPSPI slave configuration structure.*/ +typedef struct _lpspi_slave_config +{ + uint32_t bitsPerFrame; /*!< Bits per frame, minimum 8, maximum 4096.*/ + lpspi_clock_polarity_t cpol; /*!< Clock polarity. */ + lpspi_clock_phase_t cpha; /*!< Clock phase. */ + lpspi_shift_direction_t direction; /*!< MSB or LSB data shift direction. */ + + lpspi_which_pcs_t whichPcs; /*!< Desired Peripheral Chip Select (pcs) */ + lpspi_pcs_polarity_config_t pcsActiveHighOrLow; /*!< Desired PCS active high or low */ + + lpspi_pin_config_t pinCfg; /*!< Configures which pins are used for input and output data + *during single bit transfers.*/ + + lpspi_data_out_config_t dataOutConfig; /*!< Configures if the output data is tristated + * between accesses (LPSPI_PCS is negated). */ +} lpspi_slave_config_t; + +/*! + * @brief Forward declaration of the _lpspi_master_handle typedefs. + */ +typedef struct _lpspi_master_handle lpspi_master_handle_t; + +/*! + * @brief Forward declaration of the _lpspi_slave_handle typedefs. + */ +typedef struct _lpspi_slave_handle lpspi_slave_handle_t; + +/*! + * @brief Master completion callback function pointer type. + * + * @param base LPSPI peripheral address. + * @param handle Pointer to the handle for the LPSPI master. + * @param status Success or error code describing whether the transfer is completed. + * @param userData Arbitrary pointer-dataSized value passed from the application. + */ +typedef void (*lpspi_master_transfer_callback_t)(LPSPI_Type *base, + lpspi_master_handle_t *handle, + status_t status, + void *userData); + +/*! + * @brief Slave completion callback function pointer type. + * + * @param base LPSPI peripheral address. + * @param handle Pointer to the handle for the LPSPI slave. + * @param status Success or error code describing whether the transfer is completed. + * @param userData Arbitrary pointer-dataSized value passed from the application. + */ +typedef void (*lpspi_slave_transfer_callback_t)(LPSPI_Type *base, + lpspi_slave_handle_t *handle, + status_t status, + void *userData); + +/*! @brief LPSPI master/slave transfer structure.*/ +typedef struct _lpspi_transfer +{ + uint8_t *txData; /*!< Send buffer. */ + uint8_t *rxData; /*!< Receive buffer. */ + volatile size_t dataSize; /*!< Transfer bytes. */ + + uint32_t configFlags; /*!< Transfer transfer configuration flags. Set from _lpspi_transfer_config_flag_for_master if + the transfer is used for master or _lpspi_transfer_config_flag_for_slave enumeration if the + transfer is used for slave.*/ +} lpspi_transfer_t; + +/*! @brief LPSPI master transfer handle structure used for transactional API. */ +struct _lpspi_master_handle +{ + volatile bool isPcsContinuous; /*!< Is PCS continuous in transfer. */ + volatile bool writeTcrInIsr; /*!< A flag that whether should write TCR in ISR. */ + + volatile bool isByteSwap; /*!< A flag that whether should byte swap. */ + volatile bool isTxMask; /*!< A flag that whether TCR[TXMSK] is set. */ + volatile uint16_t bytesPerFrame; /*!< Number of bytes in each frame */ + + volatile uint8_t fifoSize; /*!< FIFO dataSize. */ + + volatile uint8_t rxWatermark; /*!< Rx watermark. */ + + volatile uint8_t bytesEachWrite; /*!< Bytes for each write TDR. */ + volatile uint8_t bytesEachRead; /*!< Bytes for each read RDR. */ + + uint8_t *volatile txData; /*!< Send buffer. */ + uint8_t *volatile rxData; /*!< Receive buffer. */ + volatile size_t txRemainingByteCount; /*!< Number of bytes remaining to send.*/ + volatile size_t rxRemainingByteCount; /*!< Number of bytes remaining to receive.*/ + + volatile uint32_t writeRegRemainingTimes; /*!< Write TDR register remaining times. */ + volatile uint32_t readRegRemainingTimes; /*!< Read RDR register remaining times. */ + + uint32_t totalByteCount; /*!< Number of transfer bytes*/ + + uint32_t txBuffIfNull; /*!< Used if the txData is NULL. */ + + volatile uint8_t state; /*!< LPSPI transfer state , _lpspi_transfer_state.*/ + + lpspi_master_transfer_callback_t callback; /*!< Completion callback. */ + void *userData; /*!< Callback user data. */ +}; + +/*! @brief LPSPI slave transfer handle structure used for transactional API. */ +struct _lpspi_slave_handle +{ + volatile bool isByteSwap; /*!< A flag that whether should byte swap. */ + + volatile uint8_t fifoSize; /*!< FIFO dataSize. */ + + volatile uint8_t rxWatermark; /*!< Rx watermark. */ + + volatile uint8_t bytesEachWrite; /*!< Bytes for each write TDR. */ + volatile uint8_t bytesEachRead; /*!< Bytes for each read RDR. */ + + uint8_t *volatile txData; /*!< Send buffer. */ + uint8_t *volatile rxData; /*!< Receive buffer. */ + + volatile size_t txRemainingByteCount; /*!< Number of bytes remaining to send.*/ + volatile size_t rxRemainingByteCount; /*!< Number of bytes remaining to receive.*/ + + volatile uint32_t writeRegRemainingTimes; /*!< Write TDR register remaining times. */ + volatile uint32_t readRegRemainingTimes; /*!< Read RDR register remaining times. */ + + uint32_t totalByteCount; /*!< Number of transfer bytes*/ + + volatile uint8_t state; /*!< LPSPI transfer state , _lpspi_transfer_state.*/ + + volatile uint32_t errorCount; /*!< Error count for slave transfer.*/ + + lpspi_slave_transfer_callback_t callback; /*!< Completion callback. */ + void *userData; /*!< Callback user data. */ +}; + +/********************************************************************************************************************** + * API + *********************************************************************************************************************/ +#if defined(__cplusplus) +extern "C" { +#endif /*_cplusplus*/ + +/*! + * @name Initialization and deinitialization + * @{ + */ + +/*! + * @brief Initializes the LPSPI master. + * + * @param base LPSPI peripheral address. + * @param masterConfig Pointer to structure lpspi_master_config_t. + * @param srcClock_Hz Module source input clock in Hertz + */ +void LPSPI_MasterInit(LPSPI_Type *base, const lpspi_master_config_t *masterConfig, uint32_t srcClock_Hz); + +/*! + * @brief Sets the lpspi_master_config_t structure to default values. + * + * This API initializes the configuration structure for LPSPI_MasterInit(). + * The initialized structure can remain unchanged in LPSPI_MasterInit(), or can be modified + * before calling the LPSPI_MasterInit(). + * Example: + * @code + * lpspi_master_config_t masterConfig; + * LPSPI_MasterGetDefaultConfig(&masterConfig); + * @endcode + * @param masterConfig pointer to lpspi_master_config_t structure + */ +void LPSPI_MasterGetDefaultConfig(lpspi_master_config_t *masterConfig); + +/*! + * @brief LPSPI slave configuration. + * + * @param base LPSPI peripheral address. + * @param slaveConfig Pointer to a structure lpspi_slave_config_t. + */ +void LPSPI_SlaveInit(LPSPI_Type *base, const lpspi_slave_config_t *slaveConfig); + +/*! + * @brief Sets the lpspi_slave_config_t structure to default values. + * + * This API initializes the configuration structure for LPSPI_SlaveInit(). + * The initialized structure can remain unchanged in LPSPI_SlaveInit() or can be modified + * before calling the LPSPI_SlaveInit(). + * Example: + * @code + * lpspi_slave_config_t slaveConfig; + * LPSPI_SlaveGetDefaultConfig(&slaveConfig); + * @endcode + * @param slaveConfig pointer to lpspi_slave_config_t structure. + */ +void LPSPI_SlaveGetDefaultConfig(lpspi_slave_config_t *slaveConfig); + +/*! + * @brief De-initializes the LPSPI peripheral. Call this API to disable the LPSPI clock. + * @param base LPSPI peripheral address. + */ +void LPSPI_Deinit(LPSPI_Type *base); + +/*! + * @brief Restores the LPSPI peripheral to reset state. Note that this function + * sets all registers to reset state. As a result, the LPSPI module can't work after calling + * this API. + * @param base LPSPI peripheral address. + */ +void LPSPI_Reset(LPSPI_Type *base); + +/*! + * @brief Get the LPSPI instance from peripheral base address. + * + * @param base LPSPI peripheral base address. + * @return LPSPI instance. + */ +uint32_t LPSPI_GetInstance(LPSPI_Type *base); + +/*! + * @brief Enables the LPSPI peripheral and sets the MCR MDIS to 0. + * + * @param base LPSPI peripheral address. + * @param enable Pass true to enable module, false to disable module. + */ +static inline void LPSPI_Enable(LPSPI_Type *base, bool enable) +{ + if (enable) + { + base->CR |= LPSPI_CR_MEN_MASK; + } + else + { + base->CR &= ~LPSPI_CR_MEN_MASK; + } +} + +/*! + *@} + */ + +/*! + * @name Status + * @{ + */ + +/*! + * @brief Gets the LPSPI status flag state. + * @param base LPSPI peripheral address. + * @return The LPSPI status(in SR register). + */ +static inline uint32_t LPSPI_GetStatusFlags(LPSPI_Type *base) +{ + return (base->SR); +} + +/*! + * @brief Gets the LPSPI Tx FIFO size. + * @param base LPSPI peripheral address. + * @return The LPSPI Tx FIFO size. + */ +static inline uint8_t LPSPI_GetTxFifoSize(LPSPI_Type *base) +{ + return (1U << ((base->PARAM & LPSPI_PARAM_TXFIFO_MASK) >> LPSPI_PARAM_TXFIFO_SHIFT)); +} + +/*! + * @brief Gets the LPSPI Rx FIFO size. + * @param base LPSPI peripheral address. + * @return The LPSPI Rx FIFO size. + */ +static inline uint8_t LPSPI_GetRxFifoSize(LPSPI_Type *base) +{ + return (1U << ((base->PARAM & LPSPI_PARAM_RXFIFO_MASK) >> LPSPI_PARAM_RXFIFO_SHIFT)); +} + +/*! + * @brief Gets the LPSPI Tx FIFO count. + * @param base LPSPI peripheral address. + * @return The number of words in the transmit FIFO. + */ +static inline uint32_t LPSPI_GetTxFifoCount(LPSPI_Type *base) +{ + return ((base->FSR & LPSPI_FSR_TXCOUNT_MASK) >> LPSPI_FSR_TXCOUNT_SHIFT); +} + +/*! + * @brief Gets the LPSPI Rx FIFO count. + * @param base LPSPI peripheral address. + * @return The number of words in the receive FIFO. + */ +static inline uint32_t LPSPI_GetRxFifoCount(LPSPI_Type *base) +{ + return ((base->FSR & LPSPI_FSR_RXCOUNT_MASK) >> LPSPI_FSR_RXCOUNT_SHIFT); +} + +/*! + * @brief Clears the LPSPI status flag. + * + * This function clears the desired status bit by using a write-1-to-clear. The user passes in the base and the + * desired status flag bit to clear. The list of status flags is defined in the _lpspi_flags. + * Example usage: + * @code + * LPSPI_ClearStatusFlags(base, kLPSPI_TxDataRequestFlag|kLPSPI_RxDataReadyFlag); + * @endcode + * + * @param base LPSPI peripheral address. + * @param statusFlags The status flag used from type _lpspi_flags. + */ +static inline void LPSPI_ClearStatusFlags(LPSPI_Type *base, uint32_t statusFlags) +{ + base->SR = statusFlags; /*!< The status flags are cleared by writing 1 (w1c).*/ +} + +/*! + *@} + */ + +/*! + * @name Interrupts + * @{ + */ + +/*! + * @brief Enables the LPSPI interrupts. + * + * This function configures the various interrupt masks of the LPSPI. The parameters are base and an interrupt mask. + * Note that, for Tx fill and Rx FIFO drain requests, enabling the interrupt request disables the DMA request. + * + * @code + * LPSPI_EnableInterrupts(base, kLPSPI_TxInterruptEnable | kLPSPI_RxInterruptEnable ); + * @endcode + * + * @param base LPSPI peripheral address. + * @param mask The interrupt mask; Use the enum _lpspi_interrupt_enable. + */ +static inline void LPSPI_EnableInterrupts(LPSPI_Type *base, uint32_t mask) +{ + base->IER |= mask; +} + +/*! + * @brief Disables the LPSPI interrupts. + * + * @code + * LPSPI_DisableInterrupts(base, kLPSPI_TxInterruptEnable | kLPSPI_RxInterruptEnable ); + * @endcode + * + * @param base LPSPI peripheral address. + * @param mask The interrupt mask; Use the enum _lpspi_interrupt_enable. + */ +static inline void LPSPI_DisableInterrupts(LPSPI_Type *base, uint32_t mask) +{ + base->IER &= ~mask; +} + +/*! + *@} + */ + +/*! + * @name DMA Control + * @{ + */ + +/*! + * @brief Enables the LPSPI DMA request. + * + * This function configures the Rx and Tx DMA mask of the LPSPI. The parameters are base and a DMA mask. + * @code + * LPSPI_EnableDMA(base, kLPSPI_TxDmaEnable | kLPSPI_RxDmaEnable); + * @endcode + * + * @param base LPSPI peripheral address. + * @param mask The interrupt mask; Use the enum _lpspi_dma_enable. + */ +static inline void LPSPI_EnableDMA(LPSPI_Type *base, uint32_t mask) +{ + base->DER |= mask; +} + +/*! + * @brief Disables the LPSPI DMA request. + * + * This function configures the Rx and Tx DMA mask of the LPSPI. The parameters are base and a DMA mask. + * @code + * SPI_DisableDMA(base, kLPSPI_TxDmaEnable | kLPSPI_RxDmaEnable); + * @endcode + * + * @param base LPSPI peripheral address. + * @param mask The interrupt mask; Use the enum _lpspi_dma_enable. + */ +static inline void LPSPI_DisableDMA(LPSPI_Type *base, uint32_t mask) +{ + base->DER &= ~mask; +} + +/*! + * @brief Gets the LPSPI Transmit Data Register address for a DMA operation. + * + * This function gets the LPSPI Transmit Data Register address because this value is needed + * for the DMA operation. + * This function can be used for either master or slave mode. + * + * @param base LPSPI peripheral address. + * @return The LPSPI Transmit Data Register address. + */ +static inline uint32_t LPSPI_GetTxRegisterAddress(LPSPI_Type *base) +{ + return (uint32_t) & (base->TDR); +} + +/*! + * @brief Gets the LPSPI Receive Data Register address for a DMA operation. + * + * This function gets the LPSPI Receive Data Register address because this value is needed + * for the DMA operation. + * This function can be used for either master or slave mode. + * + * @param base LPSPI peripheral address. + * @return The LPSPI Receive Data Register address. + */ +static inline uint32_t LPSPI_GetRxRegisterAddress(LPSPI_Type *base) +{ + return (uint32_t) & (base->RDR); +} + +/*! + *@} + */ + +/*! + * @name Bus Operations + * @{ + */ + +/*! + * @brief Check the argument for transfer . + * + * @param base LPSPI peripheral address. + * @param transfer the transfer struct to be used. + * @param isEdma True to check for EDMA transfer, false to check interrupt non-blocking transfer + * @return Return true for right and false for wrong. + */ +bool LPSPI_CheckTransferArgument(LPSPI_Type *base, lpspi_transfer_t *transfer, bool isEdma); + +/*! + * @brief Configures the LPSPI for either master or slave. + * + * Note that the CFGR1 should only be written when the LPSPI is disabled (LPSPIx_CR_MEN = 0). + * + * @param base LPSPI peripheral address. + * @param mode Mode setting (master or slave) of type lpspi_master_slave_mode_t. + */ +static inline void LPSPI_SetMasterSlaveMode(LPSPI_Type *base, lpspi_master_slave_mode_t mode) +{ + base->CFGR1 = (base->CFGR1 & (~LPSPI_CFGR1_MASTER_MASK)) | LPSPI_CFGR1_MASTER(mode); +} + +/*! + * @brief Configures the peripheral chip select used for the transfer. + * + * @param base LPSPI peripheral address. + * @param select LPSPI Peripheral Chip Select (PCS) configuration. + */ +static inline void LPSPI_SelectTransferPCS(LPSPI_Type *base, lpspi_which_pcs_t select) +{ + base->TCR = (base->TCR & (~LPSPI_TCR_PCS_MASK)) | LPSPI_TCR_PCS((uint8_t)select); +} + +/*! + * @brief Set the PCS signal to continuous or uncontinuous mode. + * + * @note In master mode, continuous transfer will keep the PCS asserted at the end of the frame size, until a command + * word is received that starts a new frame. So PCS must be set back to uncontinuous when transfer finishes. + * In slave mode, when continuous transfer is enabled, the LPSPI will only transmit the first frame size bits, after + * that the LPSPI will transmit received data back (assuming a 32-bit shift register). + * + * @param base LPSPI peripheral address. + * @param IsContinous True to set the transfer PCS to continuous mode, false to set to uncontinuous mode. + */ +static inline void LPSPI_SetPCSContinous(LPSPI_Type *base, bool IsContinous) +{ + if (IsContinous) + { + base->TCR |= LPSPI_TCR_CONT_MASK; + } + else + { + base->TCR &= ~LPSPI_TCR_CONT_MASK; + } +} + +/*! + * @brief Returns whether the LPSPI module is in master mode. + * + * @param base LPSPI peripheral address. + * @return Returns true if the module is in master mode or false if the module is in slave mode. + */ +static inline bool LPSPI_IsMaster(LPSPI_Type *base) +{ + return (bool)((base->CFGR1) & LPSPI_CFGR1_MASTER_MASK); +} + +/*! + * @brief Flushes the LPSPI FIFOs. + * + * @param base LPSPI peripheral address. + * @param flushTxFifo Flushes (true) the Tx FIFO, else do not flush (false) the Tx FIFO. + * @param flushRxFifo Flushes (true) the Rx FIFO, else do not flush (false) the Rx FIFO. + */ +static inline void LPSPI_FlushFifo(LPSPI_Type *base, bool flushTxFifo, bool flushRxFifo) +{ + base->CR |= ((uint32_t)flushTxFifo << LPSPI_CR_RTF_SHIFT) | ((uint32_t)flushRxFifo << LPSPI_CR_RRF_SHIFT); +} + +/*! + * @brief Sets the transmit and receive FIFO watermark values. + * + * This function allows the user to set the receive and transmit FIFO watermarks. The function + * does not compare the watermark settings to the FIFO size. The FIFO watermark should not be + * equal to or greater than the FIFO size. It is up to the higher level driver to make this check. + * + * @param base LPSPI peripheral address. + * @param txWater The TX FIFO watermark value. Writing a value equal or greater than the FIFO size is truncated. + * @param rxWater The RX FIFO watermark value. Writing a value equal or greater than the FIFO size is truncated. + */ +static inline void LPSPI_SetFifoWatermarks(LPSPI_Type *base, uint32_t txWater, uint32_t rxWater) +{ + base->FCR = LPSPI_FCR_TXWATER(txWater) | LPSPI_FCR_RXWATER(rxWater); +} + +/*! + * @brief Configures all LPSPI peripheral chip select polarities simultaneously. + * + * Note that the CFGR1 should only be written when the LPSPI is disabled (LPSPIx_CR_MEN = 0). + * + * This is an example: PCS0 and PCS1 set to active low and other PCSs set to active high. Note that the number of + * PCS is device-specific. + * @code + * LPSPI_SetAllPcsPolarity(base, kLPSPI_Pcs0ActiveLow | kLPSPI_Pcs1ActiveLow); + * @endcode + * + * @param base LPSPI peripheral address. + * @param mask The PCS polarity mask; Use the enum _lpspi_pcs_polarity. + */ +static inline void LPSPI_SetAllPcsPolarity(LPSPI_Type *base, uint32_t mask) +{ + base->CFGR1 = (base->CFGR1 & ~LPSPI_CFGR1_PCSPOL_MASK) | LPSPI_CFGR1_PCSPOL(~mask); +} + +/*! + * @brief Configures the frame size. + * + * The minimum frame size is 8-bits and the maximum frame size is 4096-bits. If the frame size is less than or equal + * to 32-bits, the word size and frame size are identical. If the frame size is greater than 32-bits, the word + * size is 32-bits for each word except the last (the last word contains the remainder bits if the frame size is not + * divisible by 32). The minimum word size is 2-bits. A frame size of 33-bits (or similar) is not supported. + * + * Note 1: The transmit command register should be initialized before enabling the LPSPI in slave mode, although + * the command register does not update until after the LPSPI is enabled. After it is enabled, the transmit command + * register + * should only be changed if the LPSPI is idle. + * + * Note 2: The transmit and command FIFO is a combined FIFO that includes both transmit data and command words. That + * means the TCR register should be written to when the Tx FIFO is not full. + * + * @param base LPSPI peripheral address. + * @param frameSize The frame size in number of bits. + */ +static inline void LPSPI_SetFrameSize(LPSPI_Type *base, uint32_t frameSize) +{ + base->TCR = (base->TCR & ~LPSPI_TCR_FRAMESZ_MASK) | LPSPI_TCR_FRAMESZ(frameSize - 1U); +} + +/*! + * @brief Sets the LPSPI baud rate in bits per second. + * + * This function takes in the desired bitsPerSec (baud rate) and calculates the nearest + * possible baud rate without exceeding the desired baud rate and returns the + * calculated baud rate in bits-per-second. It requires the caller to provide + * the frequency of the module source clock (in Hertz). Note that the baud rate + * does not go into effect until the Transmit Control Register (TCR) is programmed + * with the prescale value. Hence, this function returns the prescale tcrPrescaleValue + * parameter for later programming in the TCR. The higher level + * peripheral driver should alert the user of an out of range baud rate input. + * + * Note that the LPSPI module must first be disabled before configuring this. + * Note that the LPSPI module must be configured for master mode before configuring this. + * + * @param base LPSPI peripheral address. + * @param baudRate_Bps The desired baud rate in bits per second. + * @param srcClock_Hz Module source input clock in Hertz. + * @param tcrPrescaleValue The TCR prescale value needed to program the TCR. + * @return The actual calculated baud rate. This function may also return a "0" if the + * LPSPI is not configured for master mode or if the LPSPI module is not disabled. + */ + +uint32_t LPSPI_MasterSetBaudRate(LPSPI_Type *base, + uint32_t baudRate_Bps, + uint32_t srcClock_Hz, + uint32_t *tcrPrescaleValue); + +/*! + * @brief Manually configures a specific LPSPI delay parameter (module must be disabled to + * change the delay values). + * + * This function configures the following: + * SCK to PCS delay, or + * PCS to SCK delay, or + * The configurations must occur between the transfer delay. + * + * The delay names are available in type lpspi_delay_type_t. + * + * The user passes the desired delay along with the delay value. + * This allows the user to directly set the delay values if they have + * pre-calculated them or if they simply wish to manually increment the value. + * + * Note that the LPSPI module must first be disabled before configuring this. + * Note that the LPSPI module must be configured for master mode before configuring this. + * + * @param base LPSPI peripheral address. + * @param scaler The 8-bit delay value 0x00 to 0xFF (255). + * @param whichDelay The desired delay to configure, must be of type lpspi_delay_type_t. + */ +void LPSPI_MasterSetDelayScaler(LPSPI_Type *base, uint32_t scaler, lpspi_delay_type_t whichDelay); + +/*! + * @brief Calculates the delay based on the desired delay input in nanoseconds (module must be + * disabled to change the delay values). + * + * This function calculates the values for the following: + * SCK to PCS delay, or + * PCS to SCK delay, or + * The configurations must occur between the transfer delay. + * + * The delay names are available in type lpspi_delay_type_t. + * + * The user passes the desired delay and the desired delay value in + * nano-seconds. The function calculates the value needed for the desired delay parameter + * and returns the actual calculated delay because an exact delay match may not be possible. In this + * case, the closest match is calculated without going below the desired delay value input. + * It is possible to input a very large delay value that exceeds the capability of the part, in + * which case the maximum supported delay is returned. It is up to the higher level + * peripheral driver to alert the user of an out of range delay input. + * + * Note that the LPSPI module must be configured for master mode before configuring this. And note that + * the delayTime = LPSPI_clockSource / (PRESCALE * Delay_scaler). + * + * @param base LPSPI peripheral address. + * @param delayTimeInNanoSec The desired delay value in nano-seconds. + * @param whichDelay The desired delay to configuration, which must be of type lpspi_delay_type_t. + * @param srcClock_Hz Module source input clock in Hertz. + * @return actual Calculated delay value in nano-seconds. + */ +uint32_t LPSPI_MasterSetDelayTimes(LPSPI_Type *base, + uint32_t delayTimeInNanoSec, + lpspi_delay_type_t whichDelay, + uint32_t srcClock_Hz); + +/*! + * @brief Writes data into the transmit data buffer. + * + * This function writes data passed in by the user to the Transmit Data Register (TDR). + * The user can pass up to 32-bits of data to load into the TDR. If the frame size exceeds 32-bits, + * the user has to manage sending the data one 32-bit word at a time. + * Any writes to the TDR result in an immediate push to the transmit FIFO. + * This function can be used for either master or slave modes. + * + * @param base LPSPI peripheral address. + * @param data The data word to be sent. + */ +static inline void LPSPI_WriteData(LPSPI_Type *base, uint32_t data) +{ + base->TDR = data; +} + +/*! + * @brief Reads data from the data buffer. + * + * This function reads the data from the Receive Data Register (RDR). + * This function can be used for either master or slave mode. + * + * @param base LPSPI peripheral address. + * @return The data read from the data buffer. + */ +static inline uint32_t LPSPI_ReadData(LPSPI_Type *base) +{ + return (base->RDR); +} + +/*! + * @brief Set up the dummy data. + * + * @param base LPSPI peripheral address. + * @param dummyData Data to be transferred when tx buffer is NULL. + * Note: + * This API has no effect when LPSPI in slave interrupt mode, because driver + * will set the TXMSK bit to 1 if txData is NULL, no data is loaded from transmit + * FIFO and output pin is tristated. + */ +void LPSPI_SetDummyData(LPSPI_Type *base, uint8_t dummyData); + +/*! + *@} + */ + +/*! + * @name Transactional + * @{ + */ +/*Transactional APIs*/ + +/*! + * @brief Initializes the LPSPI master handle. + * + * This function initializes the LPSPI handle, which can be used for other LPSPI transactional APIs. Usually, for a + * specified LPSPI instance, call this API once to get the initialized handle. + + * @param base LPSPI peripheral address. + * @param handle LPSPI handle pointer to lpspi_master_handle_t. + * @param callback DSPI callback. + * @param userData callback function parameter. + */ +void LPSPI_MasterTransferCreateHandle(LPSPI_Type *base, + lpspi_master_handle_t *handle, + lpspi_master_transfer_callback_t callback, + void *userData); + +/*! + * @brief LPSPI master transfer data using a polling method. + * + * This function transfers data using a polling method. This is a blocking function, which does not return until all + * transfers have been + * completed. + * + * Note: + * The transfer data size should be integer multiples of bytesPerFrame if bytesPerFrame is less than or equal to 4. + * For bytesPerFrame greater than 4: + * The transfer data size should be equal to bytesPerFrame if the bytesPerFrame is not integer multiples of 4. + * Otherwise, the transfer data size can be an integer multiple of bytesPerFrame. + * + * @param base LPSPI peripheral address. + * @param transfer pointer to lpspi_transfer_t structure. + * @return status of status_t. + */ +status_t LPSPI_MasterTransferBlocking(LPSPI_Type *base, lpspi_transfer_t *transfer); + +/*! + * @brief LPSPI master transfer data using an interrupt method. + * + * This function transfers data using an interrupt method. This is a non-blocking function, which returns right away. + * When all data is transferred, the callback function is called. + * + * Note: + * The transfer data size should be integer multiples of bytesPerFrame if bytesPerFrame is less than or equal to 4. + * For bytesPerFrame greater than 4: + * The transfer data size should be equal to bytesPerFrame if the bytesPerFrame is not integer multiples of 4. + * Otherwise, the transfer data size can be an integer multiple of bytesPerFrame. + * + * @param base LPSPI peripheral address. + * @param handle pointer to lpspi_master_handle_t structure which stores the transfer state. + * @param transfer pointer to lpspi_transfer_t structure. + * @return status of status_t. + */ +status_t LPSPI_MasterTransferNonBlocking(LPSPI_Type *base, lpspi_master_handle_t *handle, lpspi_transfer_t *transfer); + +/*! + * @brief Gets the master transfer remaining bytes. + * + * This function gets the master transfer remaining bytes. + * + * @param base LPSPI peripheral address. + * @param handle pointer to lpspi_master_handle_t structure which stores the transfer state. + * @param count Number of bytes transferred so far by the non-blocking transaction. + * @return status of status_t. + */ +status_t LPSPI_MasterTransferGetCount(LPSPI_Type *base, lpspi_master_handle_t *handle, size_t *count); + +/*! + * @brief LPSPI master abort transfer which uses an interrupt method. + * + * This function aborts a transfer which uses an interrupt method. + * + * @param base LPSPI peripheral address. + * @param handle pointer to lpspi_master_handle_t structure which stores the transfer state. + */ +void LPSPI_MasterTransferAbort(LPSPI_Type *base, lpspi_master_handle_t *handle); + +/*! + * @brief LPSPI Master IRQ handler function. + * + * This function processes the LPSPI transmit and receive IRQ. + * + * @param base LPSPI peripheral address. + * @param handle pointer to lpspi_master_handle_t structure which stores the transfer state. + */ +void LPSPI_MasterTransferHandleIRQ(LPSPI_Type *base, lpspi_master_handle_t *handle); + +/*! + * @brief Initializes the LPSPI slave handle. + * + * This function initializes the LPSPI handle, which can be used for other LPSPI transactional APIs. Usually, for a + * specified LPSPI instance, call this API once to get the initialized handle. + * + * @param base LPSPI peripheral address. + * @param handle LPSPI handle pointer to lpspi_slave_handle_t. + * @param callback DSPI callback. + * @param userData callback function parameter. + */ +void LPSPI_SlaveTransferCreateHandle(LPSPI_Type *base, + lpspi_slave_handle_t *handle, + lpspi_slave_transfer_callback_t callback, + void *userData); + +/*! + * @brief LPSPI slave transfer data using an interrupt method. + * + * This function transfer data using an interrupt method. This is a non-blocking function, which returns right away. + * When all data is transferred, the callback function is called. + * + * Note: + * The transfer data size should be integer multiples of bytesPerFrame if bytesPerFrame is less than or equal to 4. + * For bytesPerFrame greater than 4: + * The transfer data size should be equal to bytesPerFrame if the bytesPerFrame is not an integer multiple of 4. + * Otherwise, the transfer data size can be an integer multiple of bytesPerFrame. + * + * @param base LPSPI peripheral address. + * @param handle pointer to lpspi_slave_handle_t structure which stores the transfer state. + * @param transfer pointer to lpspi_transfer_t structure. + * @return status of status_t. + */ +status_t LPSPI_SlaveTransferNonBlocking(LPSPI_Type *base, lpspi_slave_handle_t *handle, lpspi_transfer_t *transfer); + +/*! + * @brief Gets the slave transfer remaining bytes. + * + * This function gets the slave transfer remaining bytes. + * + * @param base LPSPI peripheral address. + * @param handle pointer to lpspi_slave_handle_t structure which stores the transfer state. + * @param count Number of bytes transferred so far by the non-blocking transaction. + * @return status of status_t. + */ +status_t LPSPI_SlaveTransferGetCount(LPSPI_Type *base, lpspi_slave_handle_t *handle, size_t *count); + +/*! + * @brief LPSPI slave aborts a transfer which uses an interrupt method. + * + * This function aborts a transfer which uses an interrupt method. + * + * @param base LPSPI peripheral address. + * @param handle pointer to lpspi_slave_handle_t structure which stores the transfer state. + */ +void LPSPI_SlaveTransferAbort(LPSPI_Type *base, lpspi_slave_handle_t *handle); + +/*! + * @brief LPSPI Slave IRQ handler function. + * + * This function processes the LPSPI transmit and receives an IRQ. + * + * @param base LPSPI peripheral address. + * @param handle pointer to lpspi_slave_handle_t structure which stores the transfer state. + */ +void LPSPI_SlaveTransferHandleIRQ(LPSPI_Type *base, lpspi_slave_handle_t *handle); + +/*! + *@} + */ + +#if defined(__cplusplus) +} +#endif + +/*! @}*/ + +#endif /*_FSL_LPSPI_H_*/ diff --git a/bsps/arm/imxrt/mcux-sdk/drivers/lpspi/fsl_lpspi_edma.c b/bsps/arm/imxrt/mcux-sdk/drivers/lpspi/fsl_lpspi_edma.c new file mode 100644 index 0000000000..92e8bbdb4d --- /dev/null +++ b/bsps/arm/imxrt/mcux-sdk/drivers/lpspi/fsl_lpspi_edma.c @@ -0,0 +1,1052 @@ +/* + * Copyright (c) 2015, Freescale Semiconductor, Inc. + * Copyright 2016-2022 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "fsl_lpspi_edma.h" + +/*********************************************************************************************************************** + * Definitions + ***********************************************************************************************************************/ + +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.lpspi_edma" +#endif + +/*! + * @brief Structure definition for dspi_master_edma_private_handle_t. The structure is private. + */ +typedef struct _lpspi_master_edma_private_handle +{ + LPSPI_Type *base; /*!< LPSPI peripheral base address. */ + lpspi_master_edma_handle_t *handle; /*!< lpspi_master_edma_handle_t handle */ +} lpspi_master_edma_private_handle_t; + +/*! + * @brief Structure definition for dspi_slave_edma_private_handle_t. The structure is private. + */ +typedef struct _lpspi_slave_edma_private_handle +{ + LPSPI_Type *base; /*!< LPSPI peripheral base address. */ + lpspi_slave_edma_handle_t *handle; /*!< lpspi_slave_edma_handle_t handle */ +} lpspi_slave_edma_private_handle_t; + +/*********************************************************************************************************************** + * Prototypes + ***********************************************************************************************************************/ + +/*! + * @brief EDMA_LpspiMasterCallback after the LPSPI master transfer completed by using EDMA. + * This is not a public API. + */ +static void EDMA_LpspiMasterCallback(edma_handle_t *edmaHandle, + void *g_lpspiEdmaPrivateHandle, + bool transferDone, + uint32_t tcds); + +/*! + * @brief EDMA_LpspiSlaveCallback after the LPSPI slave transfer completed by using EDMA. + * This is not a public API. + */ +static void EDMA_LpspiSlaveCallback(edma_handle_t *edmaHandle, + void *g_lpspiEdmaPrivateHandle, + bool transferDone, + uint32_t tcds); + +static void LPSPI_SeparateEdmaReadData(uint8_t *rxData, uint32_t readData, uint32_t bytesEachRead, bool isByteSwap); + +/*********************************************************************************************************************** + * Variables + ***********************************************************************************************************************/ +/*! @brief Pointers to lpspi bases for each instance. */ +static LPSPI_Type *const s_lpspiBases[] = LPSPI_BASE_PTRS; + +/*! @brief Pointers to lpspi edma handles for each instance. */ +static lpspi_master_edma_private_handle_t s_lpspiMasterEdmaPrivateHandle[ARRAY_SIZE(s_lpspiBases)]; +static lpspi_slave_edma_private_handle_t s_lpspiSlaveEdmaPrivateHandle[ARRAY_SIZE(s_lpspiBases)]; + +/*********************************************************************************************************************** + * Code + ***********************************************************************************************************************/ +static void LPSPI_SeparateEdmaReadData(uint8_t *rxData, uint32_t readData, uint32_t bytesEachRead, bool isByteSwap) +{ + assert(rxData != NULL); + + switch (bytesEachRead) + { + case 1: + if (!isByteSwap) + { + *rxData = (uint8_t)readData; + ++rxData; + } + else + { + *rxData = (uint8_t)(readData >> 24); + ++rxData; + } + break; + + case 2: + if (!isByteSwap) + { + *rxData = (uint8_t)readData; + ++rxData; + *rxData = (uint8_t)(readData >> 8); + ++rxData; + } + else + { + *rxData = (uint8_t)(readData >> 16); + ++rxData; + *rxData = (uint8_t)(readData >> 24); + ++rxData; + } + break; + + case 4: + + *rxData = (uint8_t)readData; + ++rxData; + *rxData = (uint8_t)(readData >> 8); + ++rxData; + *rxData = (uint8_t)(readData >> 16); + ++rxData; + *rxData = (uint8_t)(readData >> 24); + ++rxData; + + break; + + default: + assert(false); + break; + } +} + +/*! + * brief Initializes the LPSPI master eDMA handle. + * + * This function initializes the LPSPI eDMA handle which can be used for other LPSPI transactional APIs. Usually, for a + * specified LPSPI instance, call this API once to get the initialized handle. + * + * Note that the LPSPI eDMA has a separated (Rx and Rx as two sources) or shared (Rx and Tx are the same source) DMA + * request source. + * (1) For a separated DMA request source, enable and set the Rx DMAMUX source for edmaRxRegToRxDataHandle and + * Tx DMAMUX source for edmaIntermediaryToTxRegHandle. + * (2) For a shared DMA request source, enable and set the Rx/Rx DMAMUX source for edmaRxRegToRxDataHandle. + * + * param base LPSPI peripheral base address. + * param handle LPSPI handle pointer to lpspi_master_edma_handle_t. + * param callback LPSPI callback. + * param userData callback function parameter. + * param edmaRxRegToRxDataHandle edmaRxRegToRxDataHandle pointer to edma_handle_t. + * param edmaTxDataToTxRegHandle edmaTxDataToTxRegHandle pointer to edma_handle_t. + */ +void LPSPI_MasterTransferCreateHandleEDMA(LPSPI_Type *base, + lpspi_master_edma_handle_t *handle, + lpspi_master_edma_transfer_callback_t callback, + void *userData, + edma_handle_t *edmaRxRegToRxDataHandle, + edma_handle_t *edmaTxDataToTxRegHandle) +{ + assert(handle != NULL); + assert(edmaRxRegToRxDataHandle != NULL); + assert(edmaTxDataToTxRegHandle != NULL); + + /* Zero the handle. */ + (void)memset(handle, 0, sizeof(*handle)); + + uint32_t instance = LPSPI_GetInstance(base); + + s_lpspiMasterEdmaPrivateHandle[instance].base = base; + s_lpspiMasterEdmaPrivateHandle[instance].handle = handle; + + handle->callback = callback; + handle->userData = userData; + + handle->edmaRxRegToRxDataHandle = edmaRxRegToRxDataHandle; + handle->edmaTxDataToTxRegHandle = edmaTxDataToTxRegHandle; +} + +static void LPSPI_PrepareTransferEDMA(LPSPI_Type *base) +{ + /* Flush FIFO, clear status, disable all the inerrupts and DMA requests. */ + LPSPI_FlushFifo(base, true, true); + LPSPI_ClearStatusFlags(base, (uint32_t)kLPSPI_AllStatusFlag); + LPSPI_DisableInterrupts(base, (uint32_t)kLPSPI_AllInterruptEnable); + LPSPI_DisableDMA(base, (uint32_t)kLPSPI_RxDmaEnable | (uint32_t)kLPSPI_TxDmaEnable); +} + +/*! + * brief LPSPI master transfer data using eDMA. + * + * This function transfers data using eDMA. This is a non-blocking function, which returns right away. When all data + * is transferred, the callback function is called. + * + * Note: + * The transfer data size should be an integer multiple of bytesPerFrame if bytesPerFrame is less than or equal to 4. + * For bytesPerFrame greater than 4: + * The transfer data size should be equal to bytesPerFrame if the bytesPerFrame is not an integer multiple of 4. + * Otherwise, the transfer data size can be an integer multiple of bytesPerFrame. + * + * param base LPSPI peripheral base address. + * param handle pointer to lpspi_master_edma_handle_t structure which stores the transfer state. + * param transfer pointer to lpspi_transfer_t structure. + * return status of status_t. + */ +status_t LPSPI_MasterTransferEDMA(LPSPI_Type *base, lpspi_master_edma_handle_t *handle, lpspi_transfer_t *transfer) +{ + assert(handle != NULL); + assert(transfer != NULL); + + /* Check that we're not busy.*/ + if (handle->state == (uint8_t)kLPSPI_Busy) + { + return kStatus_LPSPI_Busy; + } + + /* Disable module before configuration */ + LPSPI_Enable(base, false); + /* Check arguements */ + if (!LPSPI_CheckTransferArgument(base, transfer, true)) + { + return kStatus_InvalidArgument; + } + + LPSPI_PrepareTransferEDMA(base); + + /* Variables */ + bool isThereExtraTxBytes = false; + bool isByteSwap = ((transfer->configFlags & (uint32_t)kLPSPI_MasterByteSwap) != 0U); + bool isPcsContinuous = ((transfer->configFlags & (uint32_t)kLPSPI_MasterPcsContinuous) != 0U); + uint32_t instance = LPSPI_GetInstance(base); + uint8_t dummyData = g_lpspiDummyData[instance]; + uint8_t bytesLastWrite = 0; + /*Used for byte swap*/ + uint32_t addrOffset = 0; + uint32_t rxAddr = LPSPI_GetRxRegisterAddress(base); + uint32_t txAddr = LPSPI_GetTxRegisterAddress(base); + uint32_t whichPcs = (transfer->configFlags & LPSPI_MASTER_PCS_MASK) >> LPSPI_MASTER_PCS_SHIFT; + uint32_t bytesPerFrame = ((base->TCR & LPSPI_TCR_FRAMESZ_MASK) >> LPSPI_TCR_FRAMESZ_SHIFT) / 8U + 1U; + edma_transfer_config_t transferConfigRx = {0}; + edma_transfer_config_t transferConfigTx = {0}; + edma_tcd_t *softwareTCD_pcsContinuous = (edma_tcd_t *)((uint32_t)(&handle->lpspiSoftwareTCD[2]) & (~0x1FU)); + edma_tcd_t *softwareTCD_extraBytes = (edma_tcd_t *)((uint32_t)(&handle->lpspiSoftwareTCD[1]) & (~0x1FU)); + + handle->state = (uint8_t)kLPSPI_Busy; + handle->txData = transfer->txData; + handle->rxData = transfer->rxData; + handle->txRemainingByteCount = transfer->dataSize; + handle->rxRemainingByteCount = transfer->dataSize; + handle->totalByteCount = transfer->dataSize; + handle->writeRegRemainingTimes = (transfer->dataSize / bytesPerFrame) * ((bytesPerFrame + 3U) / 4U); + handle->readRegRemainingTimes = handle->writeRegRemainingTimes; + handle->txBuffIfNull = + ((uint32_t)dummyData) | ((uint32_t)dummyData << 8) | ((uint32_t)dummyData << 16) | ((uint32_t)dummyData << 24); + /*The TX and RX FIFO sizes are always the same*/ + handle->fifoSize = LPSPI_GetRxFifoSize(base); + handle->isPcsContinuous = isPcsContinuous; + handle->isByteSwap = isByteSwap; + handle->isThereExtraRxBytes = false; + + /*Because DMA is fast enough , so set the RX and TX watermarks to 0 .*/ + LPSPI_SetFifoWatermarks(base, 0U, 0U); + + /* Transfers will stall when transmit FIFO is empty or receive FIFO is full. */ + base->CFGR1 &= (~LPSPI_CFGR1_NOSTALL_MASK); + + /* Enable module for following configuration of TCR to take effect. */ + LPSPI_Enable(base, true); + + /* For DMA transfer , we'd better not masked the transmit data and receive data in TCR since the transfer flow is + * hard to controlled by software. */ + base->TCR = (base->TCR & ~(LPSPI_TCR_CONT_MASK | LPSPI_TCR_CONTC_MASK | LPSPI_TCR_BYSW_MASK | LPSPI_TCR_PCS_MASK)) | + LPSPI_TCR_CONT(isPcsContinuous) | LPSPI_TCR_BYSW(isByteSwap) | LPSPI_TCR_PCS(whichPcs); + + /*Calculate the bytes for write/read the TX/RX register each time*/ + if (bytesPerFrame <= 4U) + { + handle->bytesEachWrite = (uint8_t)bytesPerFrame; + handle->bytesEachRead = (uint8_t)bytesPerFrame; + + handle->bytesLastRead = (uint8_t)bytesPerFrame; + } + else + { + handle->bytesEachWrite = 4U; + handle->bytesEachRead = 4U; + + handle->bytesLastRead = 4U; + + if ((transfer->dataSize % 4U) != 0U) + { + bytesLastWrite = (uint8_t)(transfer->dataSize % 4U); + handle->bytesLastRead = bytesLastWrite; + + isThereExtraTxBytes = true; + + --handle->writeRegRemainingTimes; + + --handle->readRegRemainingTimes; + handle->isThereExtraRxBytes = true; + } + } + + EDMA_SetCallback(handle->edmaRxRegToRxDataHandle, EDMA_LpspiMasterCallback, + &s_lpspiMasterEdmaPrivateHandle[instance]); + + /* Configure rx EDMA transfer */ + EDMA_ResetChannel(handle->edmaRxRegToRxDataHandle->base, handle->edmaRxRegToRxDataHandle->channel); + + if (handle->rxData != NULL) + { + transferConfigRx.destAddr = (uint32_t) & (handle->rxData[0]); + transferConfigRx.destOffset = 1; + } + else + { + transferConfigRx.destAddr = (uint32_t) & (handle->rxBuffIfNull); + transferConfigRx.destOffset = 0; + } + transferConfigRx.destTransferSize = kEDMA_TransferSize1Bytes; + + addrOffset = 0; + switch (handle->bytesEachRead) + { + case (1U): + transferConfigRx.srcTransferSize = kEDMA_TransferSize1Bytes; + transferConfigRx.minorLoopBytes = 1; + if (handle->isByteSwap) + { + addrOffset = 3; + } + break; + + case (2U): + transferConfigRx.srcTransferSize = kEDMA_TransferSize2Bytes; + transferConfigRx.minorLoopBytes = 2; + if (handle->isByteSwap) + { + addrOffset = 2; + } + break; + + case (4U): + transferConfigRx.srcTransferSize = kEDMA_TransferSize4Bytes; + transferConfigRx.minorLoopBytes = 4; + break; + + default: + transferConfigRx.srcTransferSize = kEDMA_TransferSize1Bytes; + transferConfigRx.minorLoopBytes = 1; + assert(false); + break; + } + + transferConfigRx.srcAddr = (uint32_t)rxAddr + addrOffset; + transferConfigRx.srcOffset = 0; + + transferConfigRx.majorLoopCounts = handle->readRegRemainingTimes; + + /* Store the initially configured eDMA minor byte transfer count into the LPSPI handle */ + handle->nbytes = (uint8_t)transferConfigRx.minorLoopBytes; + + EDMA_SetTransferConfig(handle->edmaRxRegToRxDataHandle->base, handle->edmaRxRegToRxDataHandle->channel, + &transferConfigRx, NULL); + EDMA_EnableChannelInterrupts(handle->edmaRxRegToRxDataHandle->base, handle->edmaRxRegToRxDataHandle->channel, + (uint32_t)kEDMA_MajorInterruptEnable); + + /* Configure tx EDMA transfer */ + EDMA_ResetChannel(handle->edmaTxDataToTxRegHandle->base, handle->edmaTxDataToTxRegHandle->channel); + + if (isThereExtraTxBytes) + { + if (handle->txData != NULL) + { + transferConfigTx.srcAddr = (uint32_t) & (transfer->txData[transfer->dataSize - bytesLastWrite]); + transferConfigTx.srcOffset = 1; + } + else + { + transferConfigTx.srcAddr = (uint32_t)(&handle->txBuffIfNull); + transferConfigTx.srcOffset = 0; + } + + transferConfigTx.destOffset = 0; + + transferConfigTx.srcTransferSize = kEDMA_TransferSize1Bytes; + + addrOffset = 0; + switch (bytesLastWrite) + { + case (1U): + transferConfigTx.destTransferSize = kEDMA_TransferSize1Bytes; + transferConfigTx.minorLoopBytes = 1; + if (handle->isByteSwap) + { + addrOffset = 3; + } + break; + + case (2U): + transferConfigTx.destTransferSize = kEDMA_TransferSize2Bytes; + transferConfigTx.minorLoopBytes = 2; + if (handle->isByteSwap) + { + addrOffset = 2; + } + break; + + default: + transferConfigTx.destTransferSize = kEDMA_TransferSize1Bytes; + transferConfigTx.minorLoopBytes = 1; + assert(false); + break; + } + + transferConfigTx.destAddr = (uint32_t)txAddr + addrOffset; + transferConfigTx.majorLoopCounts = 1; + + EDMA_TcdReset(softwareTCD_extraBytes); + + if (handle->isPcsContinuous) + { + EDMA_TcdSetTransferConfig(softwareTCD_extraBytes, &transferConfigTx, softwareTCD_pcsContinuous); + } + else + { + EDMA_TcdSetTransferConfig(softwareTCD_extraBytes, &transferConfigTx, NULL); + } + } + + if (handle->isPcsContinuous) + { + handle->transmitCommand = base->TCR & ~(LPSPI_TCR_CONTC_MASK | LPSPI_TCR_CONT_MASK); + + transferConfigTx.srcAddr = (uint32_t) & (handle->transmitCommand); + transferConfigTx.srcOffset = 0; + + transferConfigTx.destAddr = (uint32_t) & (base->TCR); + transferConfigTx.destOffset = 0; + + transferConfigTx.srcTransferSize = kEDMA_TransferSize4Bytes; + transferConfigTx.destTransferSize = kEDMA_TransferSize4Bytes; + transferConfigTx.minorLoopBytes = 4; + transferConfigTx.majorLoopCounts = 1; + + EDMA_TcdReset(softwareTCD_pcsContinuous); + EDMA_TcdSetTransferConfig(softwareTCD_pcsContinuous, &transferConfigTx, NULL); + } + + if (handle->txData != NULL) + { + transferConfigTx.srcAddr = (uint32_t)(handle->txData); + transferConfigTx.srcOffset = 1; + } + else + { + transferConfigTx.srcAddr = (uint32_t)(&handle->txBuffIfNull); + transferConfigTx.srcOffset = 0; + } + + transferConfigTx.destOffset = 0; + + transferConfigTx.srcTransferSize = kEDMA_TransferSize1Bytes; + + addrOffset = 0U; + switch (handle->bytesEachRead) + { + case (1U): + transferConfigTx.destTransferSize = kEDMA_TransferSize1Bytes; + transferConfigTx.minorLoopBytes = 1; + if (handle->isByteSwap) + { + addrOffset = 3; + } + break; + + case (2U): + transferConfigTx.destTransferSize = kEDMA_TransferSize2Bytes; + transferConfigTx.minorLoopBytes = 2; + + if (handle->isByteSwap) + { + addrOffset = 2; + } + break; + + case (4U): + transferConfigTx.destTransferSize = kEDMA_TransferSize4Bytes; + transferConfigTx.minorLoopBytes = 4; + break; + + default: + transferConfigTx.destTransferSize = kEDMA_TransferSize1Bytes; + transferConfigTx.minorLoopBytes = 1; + assert(false); + break; + } + + transferConfigTx.destAddr = (uint32_t)txAddr + addrOffset; + + transferConfigTx.majorLoopCounts = handle->writeRegRemainingTimes; + + if (isThereExtraTxBytes) + { + EDMA_SetTransferConfig(handle->edmaTxDataToTxRegHandle->base, handle->edmaTxDataToTxRegHandle->channel, + &transferConfigTx, softwareTCD_extraBytes); + } + else if (handle->isPcsContinuous) + { + EDMA_SetTransferConfig(handle->edmaTxDataToTxRegHandle->base, handle->edmaTxDataToTxRegHandle->channel, + &transferConfigTx, softwareTCD_pcsContinuous); + } + else + { + EDMA_SetTransferConfig(handle->edmaTxDataToTxRegHandle->base, handle->edmaTxDataToTxRegHandle->channel, + &transferConfigTx, NULL); + } + + EDMA_StartTransfer(handle->edmaTxDataToTxRegHandle); + EDMA_StartTransfer(handle->edmaRxRegToRxDataHandle); + LPSPI_EnableDMA(base, (uint32_t)kLPSPI_RxDmaEnable | (uint32_t)kLPSPI_TxDmaEnable); + + return kStatus_Success; +} + +static void EDMA_LpspiMasterCallback(edma_handle_t *edmaHandle, + void *g_lpspiEdmaPrivateHandle, + bool transferDone, + uint32_t tcds) +{ + assert(edmaHandle != NULL); + assert(g_lpspiEdmaPrivateHandle != NULL); + + uint32_t readData; + + lpspi_master_edma_private_handle_t *lpspiEdmaPrivateHandle; + + lpspiEdmaPrivateHandle = (lpspi_master_edma_private_handle_t *)g_lpspiEdmaPrivateHandle; + + size_t rxRemainingByteCount = lpspiEdmaPrivateHandle->handle->rxRemainingByteCount; + uint8_t bytesLastRead = lpspiEdmaPrivateHandle->handle->bytesLastRead; + bool isByteSwap = lpspiEdmaPrivateHandle->handle->isByteSwap; + + LPSPI_DisableDMA(lpspiEdmaPrivateHandle->base, (uint32_t)kLPSPI_TxDmaEnable | (uint32_t)kLPSPI_RxDmaEnable); + + if (lpspiEdmaPrivateHandle->handle->isThereExtraRxBytes) + { + while (LPSPI_GetRxFifoCount(lpspiEdmaPrivateHandle->base) == 0U) + { + } + readData = LPSPI_ReadData(lpspiEdmaPrivateHandle->base); + + if (lpspiEdmaPrivateHandle->handle->rxData != NULL) + { + LPSPI_SeparateEdmaReadData(&(lpspiEdmaPrivateHandle->handle->rxData[rxRemainingByteCount - bytesLastRead]), + readData, bytesLastRead, isByteSwap); + } + } + + lpspiEdmaPrivateHandle->handle->state = (uint8_t)kLPSPI_Idle; + + if (lpspiEdmaPrivateHandle->handle->callback != NULL) + { + lpspiEdmaPrivateHandle->handle->callback(lpspiEdmaPrivateHandle->base, lpspiEdmaPrivateHandle->handle, + kStatus_Success, lpspiEdmaPrivateHandle->handle->userData); + } +} + +/*! + * brief LPSPI master aborts a transfer which is using eDMA. + * + * This function aborts a transfer which is using eDMA. + * + * param base LPSPI peripheral base address. + * param handle pointer to lpspi_master_edma_handle_t structure which stores the transfer state. + */ +void LPSPI_MasterTransferAbortEDMA(LPSPI_Type *base, lpspi_master_edma_handle_t *handle) +{ + assert(handle != NULL); + + LPSPI_DisableDMA(base, (uint32_t)kLPSPI_RxDmaEnable | (uint32_t)kLPSPI_TxDmaEnable); + + EDMA_AbortTransfer(handle->edmaRxRegToRxDataHandle); + EDMA_AbortTransfer(handle->edmaTxDataToTxRegHandle); + + handle->state = (uint8_t)kLPSPI_Idle; +} + +/*! + * brief Gets the master eDMA transfer remaining bytes. + * + * This function gets the master eDMA transfer remaining bytes. + * + * param base LPSPI peripheral base address. + * param handle pointer to lpspi_master_edma_handle_t structure which stores the transfer state. + * param count Number of bytes transferred so far by the EDMA transaction. + * return status of status_t. + */ +status_t LPSPI_MasterTransferGetCountEDMA(LPSPI_Type *base, lpspi_master_edma_handle_t *handle, size_t *count) +{ + assert(handle != NULL); + + if (NULL == count) + { + return kStatus_InvalidArgument; + } + + /* Catch when there is not an active transfer. */ + if (handle->state != (uint8_t)kLPSPI_Busy) + { + *count = 0; + return kStatus_NoTransferInProgress; + } + + size_t remainingByte; + + remainingByte = + (uint32_t)handle->nbytes * EDMA_GetRemainingMajorLoopCount(handle->edmaRxRegToRxDataHandle->base, + handle->edmaRxRegToRxDataHandle->channel); + + *count = handle->totalByteCount - remainingByte; + + return kStatus_Success; +} + +/*! + * brief Initializes the LPSPI slave eDMA handle. + * + * This function initializes the LPSPI eDMA handle which can be used for other LPSPI transactional APIs. Usually, for a + * specified LPSPI instance, call this API once to get the initialized handle. + * + * Note that LPSPI eDMA has a separated (Rx and Tx as two sources) or shared (Rx and Tx as the same source) DMA request + * source. + * + * (1) For a separated DMA request source, enable and set the Rx DMAMUX source for edmaRxRegToRxDataHandle and + * Tx DMAMUX source for edmaTxDataToTxRegHandle. + * (2) For a shared DMA request source, enable and set the Rx/Rx DMAMUX source for edmaRxRegToRxDataHandle . + * + * param base LPSPI peripheral base address. + * param handle LPSPI handle pointer to lpspi_slave_edma_handle_t. + * param callback LPSPI callback. + * param userData callback function parameter. + * param edmaRxRegToRxDataHandle edmaRxRegToRxDataHandle pointer to edma_handle_t. + * param edmaTxDataToTxRegHandle edmaTxDataToTxRegHandle pointer to edma_handle_t. + */ +void LPSPI_SlaveTransferCreateHandleEDMA(LPSPI_Type *base, + lpspi_slave_edma_handle_t *handle, + lpspi_slave_edma_transfer_callback_t callback, + void *userData, + edma_handle_t *edmaRxRegToRxDataHandle, + edma_handle_t *edmaTxDataToTxRegHandle) +{ + assert(handle != NULL); + assert(edmaRxRegToRxDataHandle != NULL); + assert(edmaTxDataToTxRegHandle != NULL); + + /* Zero the handle. */ + (void)memset(handle, 0, sizeof(*handle)); + + uint32_t instance = LPSPI_GetInstance(base); + + s_lpspiSlaveEdmaPrivateHandle[instance].base = base; + s_lpspiSlaveEdmaPrivateHandle[instance].handle = handle; + + handle->callback = callback; + handle->userData = userData; + + handle->edmaRxRegToRxDataHandle = edmaRxRegToRxDataHandle; + handle->edmaTxDataToTxRegHandle = edmaTxDataToTxRegHandle; +} + +/*! + * brief LPSPI slave transfers data using eDMA. + * + * This function transfers data using eDMA. This is a non-blocking function, which return right away. When all data + * is transferred, the callback function is called. + * + * Note: + * The transfer data size should be an integer multiple of bytesPerFrame if bytesPerFrame is less than or equal to 4. + * For bytesPerFrame greater than 4: + * The transfer data size should be equal to bytesPerFrame if the bytesPerFrame is not an integer multiple of 4. + * Otherwise, the transfer data size can be an integer multiple of bytesPerFrame. + * + * param base LPSPI peripheral base address. + * param handle pointer to lpspi_slave_edma_handle_t structure which stores the transfer state. + * param transfer pointer to lpspi_transfer_t structure. + * return status of status_t. + */ +status_t LPSPI_SlaveTransferEDMA(LPSPI_Type *base, lpspi_slave_edma_handle_t *handle, lpspi_transfer_t *transfer) +{ + assert(handle != NULL); + assert(transfer != NULL); + + /* Check that we're not busy.*/ + if (handle->state == (uint8_t)kLPSPI_Busy) + { + return kStatus_LPSPI_Busy; + } + /* Disable module before configuration. */ + LPSPI_Enable(base, false); + /* Check arguements, also dma transfer can not support 3 bytes */ + if (!LPSPI_CheckTransferArgument(base, transfer, true)) + { + return kStatus_InvalidArgument; + } + + LPSPI_PrepareTransferEDMA(base); + + /* Variables */ + bool isThereExtraTxBytes = false; + bool isByteSwap = ((transfer->configFlags & (uint32_t)kLPSPI_MasterByteSwap) != 0U); + uint8_t bytesLastWrite = 0; + uint8_t dummyData = g_lpspiDummyData[LPSPI_GetInstance(base)]; + uint32_t mask = (uint32_t)kLPSPI_RxDmaEnable; + + /* Used for byte swap */ + uint32_t addrOffset = 0; + uint32_t instance = LPSPI_GetInstance(base); + uint32_t rxAddr = LPSPI_GetRxRegisterAddress(base); + uint32_t txAddr = LPSPI_GetTxRegisterAddress(base); + uint32_t whichPcs = (transfer->configFlags & LPSPI_MASTER_PCS_MASK) >> LPSPI_MASTER_PCS_SHIFT; + uint32_t bytesPerFrame = ((base->TCR & LPSPI_TCR_FRAMESZ_MASK) >> LPSPI_TCR_FRAMESZ_SHIFT) / 8U + 1U; + edma_transfer_config_t transferConfigRx = {0}; + edma_transfer_config_t transferConfigTx = {0}; + edma_tcd_t *softwareTCD_extraBytes = (edma_tcd_t *)((uint32_t)(&handle->lpspiSoftwareTCD[1]) & (~0x1FU)); + + /* Assign the original value for members of transfer handle. */ + handle->state = (uint8_t)kLPSPI_Busy; + handle->txData = transfer->txData; + handle->rxData = transfer->rxData; + handle->txRemainingByteCount = transfer->dataSize; + handle->rxRemainingByteCount = transfer->dataSize; + handle->totalByteCount = transfer->dataSize; + handle->writeRegRemainingTimes = (transfer->dataSize / bytesPerFrame) * ((bytesPerFrame + 3U) / 4U); + handle->readRegRemainingTimes = handle->writeRegRemainingTimes; + handle->txBuffIfNull = + ((uint32_t)dummyData) | ((uint32_t)dummyData << 8) | ((uint32_t)dummyData << 16) | ((uint32_t)dummyData << 24); + /*The TX and RX FIFO sizes are always the same*/ + handle->fifoSize = LPSPI_GetRxFifoSize(base); + handle->isByteSwap = isByteSwap; + handle->isThereExtraRxBytes = false; + + /* Because DMA is fast enough, set the RX and TX watermarks to 0. */ + LPSPI_SetFifoWatermarks(base, 0U, 0U); + + /* Transfers will stall when transmit FIFO is empty or receive FIFO is full. */ + base->CFGR1 &= (~LPSPI_CFGR1_NOSTALL_MASK); + + /* Enable module for following configuration of TCR to take effect. */ + LPSPI_Enable(base, true); + + /* For DMA transfer, mask the transmit data if the tx data is null, for rx the receive data should not be masked at + any time since we use rx dma transfer finish cllback to indicate transfer finish. */ + base->TCR = + (base->TCR & ~(LPSPI_TCR_CONT_MASK | LPSPI_TCR_CONTC_MASK | LPSPI_TCR_BYSW_MASK | LPSPI_TCR_TXMSK_MASK)) | + LPSPI_TCR_TXMSK(transfer->txData == NULL) | LPSPI_TCR_BYSW(isByteSwap) | LPSPI_TCR_PCS(whichPcs); + + /*Calculate the bytes for write/read the TX/RX register each time*/ + if (bytesPerFrame <= 4U) + { + handle->bytesEachWrite = (uint8_t)bytesPerFrame; + handle->bytesEachRead = (uint8_t)bytesPerFrame; + + handle->bytesLastRead = (uint8_t)bytesPerFrame; + } + else + { + handle->bytesEachWrite = 4U; + handle->bytesEachRead = 4U; + + handle->bytesLastRead = 4U; + + if ((transfer->dataSize % 4U) != 0U) + { + bytesLastWrite = (uint8_t)(transfer->dataSize % 4U); + handle->bytesLastRead = bytesLastWrite; + + isThereExtraTxBytes = true; + --handle->writeRegRemainingTimes; + + handle->isThereExtraRxBytes = true; + --handle->readRegRemainingTimes; + } + } + + EDMA_SetCallback(handle->edmaRxRegToRxDataHandle, EDMA_LpspiSlaveCallback, + &s_lpspiSlaveEdmaPrivateHandle[instance]); + + /*Rx*/ + if (handle->readRegRemainingTimes > 0U) + { + EDMA_ResetChannel(handle->edmaRxRegToRxDataHandle->base, handle->edmaRxRegToRxDataHandle->channel); + + if (handle->rxData != NULL) + { + transferConfigRx.destAddr = (uint32_t) & (handle->rxData[0]); + transferConfigRx.destOffset = 1; + } + else + { + transferConfigRx.destAddr = (uint32_t) & (handle->rxBuffIfNull); + transferConfigRx.destOffset = 0; + } + transferConfigRx.destTransferSize = kEDMA_TransferSize1Bytes; + + addrOffset = 0; + switch (handle->bytesEachRead) + { + case (1U): + transferConfigRx.srcTransferSize = kEDMA_TransferSize1Bytes; + transferConfigRx.minorLoopBytes = 1; + if (handle->isByteSwap) + { + addrOffset = 3; + } + break; + + case (2U): + transferConfigRx.srcTransferSize = kEDMA_TransferSize2Bytes; + transferConfigRx.minorLoopBytes = 2; + if (handle->isByteSwap) + { + addrOffset = 2; + } + break; + + case (4U): + transferConfigRx.srcTransferSize = kEDMA_TransferSize4Bytes; + transferConfigRx.minorLoopBytes = 4; + break; + + default: + transferConfigRx.srcTransferSize = kEDMA_TransferSize1Bytes; + transferConfigRx.minorLoopBytes = 1; + assert(false); + break; + } + + transferConfigRx.srcAddr = (uint32_t)rxAddr + addrOffset; + transferConfigRx.srcOffset = 0; + + transferConfigRx.majorLoopCounts = handle->readRegRemainingTimes; + + /* Store the initially configured eDMA minor byte transfer count into the DSPI handle */ + handle->nbytes = (uint8_t)transferConfigRx.minorLoopBytes; + + EDMA_SetTransferConfig(handle->edmaRxRegToRxDataHandle->base, handle->edmaRxRegToRxDataHandle->channel, + &transferConfigRx, NULL); + EDMA_EnableChannelInterrupts(handle->edmaRxRegToRxDataHandle->base, handle->edmaRxRegToRxDataHandle->channel, + (uint32_t)kEDMA_MajorInterruptEnable); + EDMA_StartTransfer(handle->edmaRxRegToRxDataHandle); + } + + /*Tx*/ + if (handle->txData != NULL) + { + EDMA_ResetChannel(handle->edmaTxDataToTxRegHandle->base, handle->edmaTxDataToTxRegHandle->channel); + if (isThereExtraTxBytes) + { + transferConfigTx.srcAddr = (uint32_t) & (transfer->txData[transfer->dataSize - bytesLastWrite]); + transferConfigTx.srcOffset = 1; + transferConfigTx.destOffset = 0; + transferConfigTx.srcTransferSize = kEDMA_TransferSize1Bytes; + addrOffset = 0; + switch (bytesLastWrite) + { + case (1U): + transferConfigTx.destTransferSize = kEDMA_TransferSize1Bytes; + transferConfigTx.minorLoopBytes = 1; + if (handle->isByteSwap) + { + addrOffset = 3; + } + break; + + case (2U): + transferConfigTx.destTransferSize = kEDMA_TransferSize2Bytes; + transferConfigTx.minorLoopBytes = 2; + if (handle->isByteSwap) + { + addrOffset = 2; + } + break; + + default: + transferConfigTx.destTransferSize = kEDMA_TransferSize1Bytes; + transferConfigTx.minorLoopBytes = 1; + assert(false); + break; + } + + transferConfigTx.destAddr = (uint32_t)txAddr + addrOffset; + transferConfigTx.majorLoopCounts = 1; + + EDMA_TcdReset(softwareTCD_extraBytes); + EDMA_TcdSetTransferConfig(softwareTCD_extraBytes, &transferConfigTx, NULL); + } + + transferConfigTx.srcAddr = (uint32_t)(handle->txData); + transferConfigTx.srcOffset = 1; + transferConfigTx.destOffset = 0; + transferConfigTx.srcTransferSize = kEDMA_TransferSize1Bytes; + addrOffset = 0; + switch (handle->bytesEachRead) + { + case (1U): + transferConfigTx.destTransferSize = kEDMA_TransferSize1Bytes; + transferConfigTx.minorLoopBytes = 1; + if (handle->isByteSwap) + { + addrOffset = 3; + } + break; + + case (2U): + transferConfigTx.destTransferSize = kEDMA_TransferSize2Bytes; + transferConfigTx.minorLoopBytes = 2; + + if (handle->isByteSwap) + { + addrOffset = 2; + } + break; + + case (4U): + transferConfigTx.destTransferSize = kEDMA_TransferSize4Bytes; + transferConfigTx.minorLoopBytes = 4; + break; + + default: + transferConfigTx.destTransferSize = kEDMA_TransferSize1Bytes; + transferConfigTx.minorLoopBytes = 1; + assert(false); + break; + } + + transferConfigTx.destAddr = (uint32_t)txAddr + addrOffset; + transferConfigTx.majorLoopCounts = handle->writeRegRemainingTimes; + + if (isThereExtraTxBytes) + { + EDMA_SetTransferConfig(handle->edmaTxDataToTxRegHandle->base, handle->edmaTxDataToTxRegHandle->channel, + &transferConfigTx, softwareTCD_extraBytes); + } + else + { + EDMA_SetTransferConfig(handle->edmaTxDataToTxRegHandle->base, handle->edmaTxDataToTxRegHandle->channel, + &transferConfigTx, NULL); + } + EDMA_StartTransfer(handle->edmaTxDataToTxRegHandle); + mask |= (uint32_t)kLPSPI_TxDmaEnable; + } + + LPSPI_EnableDMA(base, mask); + + return kStatus_Success; +} + +static void EDMA_LpspiSlaveCallback(edma_handle_t *edmaHandle, + void *g_lpspiEdmaPrivateHandle, + bool transferDone, + uint32_t tcds) +{ + assert(edmaHandle != NULL); + assert(g_lpspiEdmaPrivateHandle != NULL); + + uint32_t readData; + + lpspi_slave_edma_private_handle_t *lpspiEdmaPrivateHandle; + + lpspiEdmaPrivateHandle = (lpspi_slave_edma_private_handle_t *)g_lpspiEdmaPrivateHandle; + + size_t rxRemainingByteCount = lpspiEdmaPrivateHandle->handle->rxRemainingByteCount; + uint8_t bytesLastRead = lpspiEdmaPrivateHandle->handle->bytesLastRead; + bool isByteSwap = lpspiEdmaPrivateHandle->handle->isByteSwap; + + LPSPI_DisableDMA(lpspiEdmaPrivateHandle->base, (uint32_t)kLPSPI_TxDmaEnable | (uint32_t)kLPSPI_RxDmaEnable); + + if (lpspiEdmaPrivateHandle->handle->isThereExtraRxBytes) + { + while (LPSPI_GetRxFifoCount(lpspiEdmaPrivateHandle->base) == 0U) + { + } + readData = LPSPI_ReadData(lpspiEdmaPrivateHandle->base); + + if (lpspiEdmaPrivateHandle->handle->rxData != NULL) + { + LPSPI_SeparateEdmaReadData(&(lpspiEdmaPrivateHandle->handle->rxData[rxRemainingByteCount - bytesLastRead]), + readData, bytesLastRead, isByteSwap); + } + } + + lpspiEdmaPrivateHandle->handle->state = (uint8_t)kLPSPI_Idle; + + if (lpspiEdmaPrivateHandle->handle->callback != NULL) + { + lpspiEdmaPrivateHandle->handle->callback(lpspiEdmaPrivateHandle->base, lpspiEdmaPrivateHandle->handle, + kStatus_Success, lpspiEdmaPrivateHandle->handle->userData); + } +} + +/*! + * brief LPSPI slave aborts a transfer which is using eDMA. + * + * This function aborts a transfer which is using eDMA. + * + * param base LPSPI peripheral base address. + * param handle pointer to lpspi_slave_edma_handle_t structure which stores the transfer state. + */ +void LPSPI_SlaveTransferAbortEDMA(LPSPI_Type *base, lpspi_slave_edma_handle_t *handle) +{ + assert(handle != NULL); + + LPSPI_DisableDMA(base, (uint32_t)kLPSPI_RxDmaEnable | (uint32_t)kLPSPI_TxDmaEnable); + + EDMA_AbortTransfer(handle->edmaRxRegToRxDataHandle); + EDMA_AbortTransfer(handle->edmaTxDataToTxRegHandle); + + handle->state = (uint8_t)kLPSPI_Idle; +} + +/*! + * brief Gets the slave eDMA transfer remaining bytes. + * + * This function gets the slave eDMA transfer remaining bytes. + * + * param base LPSPI peripheral base address. + * param handle pointer to lpspi_slave_edma_handle_t structure which stores the transfer state. + * param count Number of bytes transferred so far by the eDMA transaction. + * return status of status_t. + */ +status_t LPSPI_SlaveTransferGetCountEDMA(LPSPI_Type *base, lpspi_slave_edma_handle_t *handle, size_t *count) +{ + assert(handle != NULL); + + if (NULL == count) + { + return kStatus_InvalidArgument; + } + + /* Catch when there is not an active transfer. */ + if (handle->state != (uint8_t)kLPSPI_Busy) + { + *count = 0; + return kStatus_NoTransferInProgress; + } + + size_t remainingByte; + + remainingByte = + (uint32_t)handle->nbytes * EDMA_GetRemainingMajorLoopCount(handle->edmaRxRegToRxDataHandle->base, + handle->edmaRxRegToRxDataHandle->channel); + + *count = handle->totalByteCount - remainingByte; + + return kStatus_Success; +} diff --git a/bsps/arm/imxrt/mcux-sdk/drivers/lpspi/fsl_lpspi_edma.h b/bsps/arm/imxrt/mcux-sdk/drivers/lpspi/fsl_lpspi_edma.h new file mode 100644 index 0000000000..02ce26103f --- /dev/null +++ b/bsps/arm/imxrt/mcux-sdk/drivers/lpspi/fsl_lpspi_edma.h @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2015, Freescale Semiconductor, Inc. + * Copyright 2016-2022 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef _FSL_LPSPI_EDMA_H_ +#define _FSL_LPSPI_EDMA_H_ + +#include "fsl_lpspi.h" +#include "fsl_edma.h" + +/*! + * @addtogroup lpspi_edma_driver + * @{ + */ + +/*********************************************************************************************************************** + * Definitions + **********************************************************************************************************************/ +/*! @name Driver version */ +/*@{*/ +/*! @brief LPSPI EDMA driver version. */ +#define FSL_LPSPI_EDMA_DRIVER_VERSION (MAKE_VERSION(2, 3, 1)) +/*@}*/ + +/*! + * @brief Forward declaration of the _lpspi_master_edma_handle typedefs. + */ +typedef struct _lpspi_master_edma_handle lpspi_master_edma_handle_t; + +/*! + * @brief Forward declaration of the _lpspi_slave_edma_handle typedefs. + */ +typedef struct _lpspi_slave_edma_handle lpspi_slave_edma_handle_t; + +/*! + * @brief Completion callback function pointer type. + * + * @param base LPSPI peripheral base address. + * @param handle Pointer to the handle for the LPSPI master. + * @param status Success or error code describing whether the transfer completed. + * @param userData Arbitrary pointer-dataSized value passed from the application. + */ +typedef void (*lpspi_master_edma_transfer_callback_t)(LPSPI_Type *base, + lpspi_master_edma_handle_t *handle, + status_t status, + void *userData); +/*! + * @brief Completion callback function pointer type. + * + * @param base LPSPI peripheral base address. + * @param handle Pointer to the handle for the LPSPI slave. + * @param status Success or error code describing whether the transfer completed. + * @param userData Arbitrary pointer-dataSized value passed from the application. + */ +typedef void (*lpspi_slave_edma_transfer_callback_t)(LPSPI_Type *base, + lpspi_slave_edma_handle_t *handle, + status_t status, + void *userData); + +/*! @brief LPSPI master eDMA transfer handle structure used for transactional API. */ +struct _lpspi_master_edma_handle +{ + volatile bool isPcsContinuous; /*!< Is PCS continuous in transfer. */ + + volatile bool isByteSwap; /*!< A flag that whether should byte swap. */ + + volatile uint8_t fifoSize; /*!< FIFO dataSize. */ + + volatile uint8_t rxWatermark; /*!< Rx watermark. */ + + volatile uint8_t bytesEachWrite; /*!< Bytes for each write TDR. */ + volatile uint8_t bytesEachRead; /*!< Bytes for each read RDR. */ + + volatile uint8_t bytesLastRead; /*!< Bytes for last read RDR. */ + volatile bool isThereExtraRxBytes; /*!< Is there extra RX byte. */ + + uint8_t *volatile txData; /*!< Send buffer. */ + uint8_t *volatile rxData; /*!< Receive buffer. */ + volatile size_t txRemainingByteCount; /*!< Number of bytes remaining to send.*/ + volatile size_t rxRemainingByteCount; /*!< Number of bytes remaining to receive.*/ + + volatile uint32_t writeRegRemainingTimes; /*!< Write TDR register remaining times. */ + volatile uint32_t readRegRemainingTimes; /*!< Read RDR register remaining times. */ + + uint32_t totalByteCount; /*!< Number of transfer bytes*/ + + uint32_t txBuffIfNull; /*!< Used if there is not txData for DMA purpose.*/ + uint32_t rxBuffIfNull; /*!< Used if there is not rxData for DMA purpose.*/ + + uint32_t transmitCommand; /*!< Used to write TCR for DMA purpose.*/ + + volatile uint8_t state; /*!< LPSPI transfer state , _lpspi_transfer_state.*/ + + uint8_t nbytes; /*!< eDMA minor byte transfer count initially configured. */ + + lpspi_master_edma_transfer_callback_t callback; /*!< Completion callback. */ + void *userData; /*!< Callback user data. */ + + edma_handle_t *edmaRxRegToRxDataHandle; /*!<edma_handle_t handle point used for RxReg to RxData buff*/ + edma_handle_t *edmaTxDataToTxRegHandle; /*!<edma_handle_t handle point used for TxData to TxReg buff*/ + + edma_tcd_t lpspiSoftwareTCD[3]; /*!<SoftwareTCD, internal used*/ +}; + +/*! @brief LPSPI slave eDMA transfer handle structure used for transactional API.*/ +struct _lpspi_slave_edma_handle +{ + volatile bool isByteSwap; /*!< A flag that whether should byte swap. */ + + volatile uint8_t fifoSize; /*!< FIFO dataSize. */ + + volatile uint8_t rxWatermark; /*!< Rx watermark. */ + + volatile uint8_t bytesEachWrite; /*!< Bytes for each write TDR. */ + volatile uint8_t bytesEachRead; /*!< Bytes for each read RDR. */ + + volatile uint8_t bytesLastRead; /*!< Bytes for last read RDR. */ + volatile bool isThereExtraRxBytes; /*!< Is there extra RX byte. */ + + uint8_t nbytes; /*!< eDMA minor byte transfer count initially configured. */ + + uint8_t *volatile txData; /*!< Send buffer. */ + uint8_t *volatile rxData; /*!< Receive buffer. */ + volatile size_t txRemainingByteCount; /*!< Number of bytes remaining to send.*/ + volatile size_t rxRemainingByteCount; /*!< Number of bytes remaining to receive.*/ + + volatile uint32_t writeRegRemainingTimes; /*!< Write TDR register remaining times. */ + volatile uint32_t readRegRemainingTimes; /*!< Read RDR register remaining times. */ + + uint32_t totalByteCount; /*!< Number of transfer bytes*/ + + uint32_t txBuffIfNull; /*!< Used if there is not txData for DMA purpose.*/ + uint32_t rxBuffIfNull; /*!< Used if there is not rxData for DMA purpose.*/ + + volatile uint8_t state; /*!< LPSPI transfer state.*/ + + uint32_t errorCount; /*!< Error count for slave transfer.*/ + + lpspi_slave_edma_transfer_callback_t callback; /*!< Completion callback. */ + void *userData; /*!< Callback user data. */ + + edma_handle_t *edmaRxRegToRxDataHandle; /*!<edma_handle_t handle point used for RxReg to RxData buff*/ + edma_handle_t *edmaTxDataToTxRegHandle; /*!<edma_handle_t handle point used for TxData to TxReg*/ + + edma_tcd_t lpspiSoftwareTCD[2]; /*!<SoftwareTCD, internal used*/ +}; + +/*********************************************************************************************************************** + * API + **********************************************************************************************************************/ +#if defined(__cplusplus) +extern "C" { +#endif /*_cplusplus*/ + +/*Transactional APIs*/ + +/*! + * @brief Initializes the LPSPI master eDMA handle. + * + * This function initializes the LPSPI eDMA handle which can be used for other LPSPI transactional APIs. Usually, for a + * specified LPSPI instance, call this API once to get the initialized handle. + * + * Note that the LPSPI eDMA has a separated (Rx and Tx as two sources) or shared (Rx and Tx are the same source) DMA + * request source. + * (1) For a separated DMA request source, enable and set the Rx DMAMUX source for edmaRxRegToRxDataHandle and + * Tx DMAMUX source for edmaTxDataToTxRegHandle. + * (2) For a shared DMA request source, enable and set the Rx/Tx DMAMUX source for edmaRxRegToRxDataHandle. + * + * @param base LPSPI peripheral base address. + * @param handle LPSPI handle pointer to lpspi_master_edma_handle_t. + * @param callback LPSPI callback. + * @param userData callback function parameter. + * @param edmaRxRegToRxDataHandle edmaRxRegToRxDataHandle pointer to edma_handle_t. + * @param edmaTxDataToTxRegHandle edmaTxDataToTxRegHandle pointer to edma_handle_t. + */ +void LPSPI_MasterTransferCreateHandleEDMA(LPSPI_Type *base, + lpspi_master_edma_handle_t *handle, + lpspi_master_edma_transfer_callback_t callback, + void *userData, + edma_handle_t *edmaRxRegToRxDataHandle, + edma_handle_t *edmaTxDataToTxRegHandle); + +/*! + * @brief LPSPI master transfer data using eDMA. + * + * This function transfers data using eDMA. This is a non-blocking function, which returns right away. When all data + * is transferred, the callback function is called. + * + * Note: + * The transfer data size should be an integer multiple of bytesPerFrame if bytesPerFrame is less than or equal to 4. + * For bytesPerFrame greater than 4: + * The transfer data size should be equal to bytesPerFrame if the bytesPerFrame is not an integer multiple of 4. + * Otherwise, the transfer data size can be an integer multiple of bytesPerFrame. + * + * @param base LPSPI peripheral base address. + * @param handle pointer to lpspi_master_edma_handle_t structure which stores the transfer state. + * @param transfer pointer to lpspi_transfer_t structure. + * @return status of status_t. + */ +status_t LPSPI_MasterTransferEDMA(LPSPI_Type *base, lpspi_master_edma_handle_t *handle, lpspi_transfer_t *transfer); + +/*! + * @brief LPSPI master aborts a transfer which is using eDMA. + * + * This function aborts a transfer which is using eDMA. + * + * @param base LPSPI peripheral base address. + * @param handle pointer to lpspi_master_edma_handle_t structure which stores the transfer state. + */ +void LPSPI_MasterTransferAbortEDMA(LPSPI_Type *base, lpspi_master_edma_handle_t *handle); + +/*! + * @brief Gets the master eDMA transfer remaining bytes. + * + * This function gets the master eDMA transfer remaining bytes. + * + * @param base LPSPI peripheral base address. + * @param handle pointer to lpspi_master_edma_handle_t structure which stores the transfer state. + * @param count Number of bytes transferred so far by the EDMA transaction. + * @return status of status_t. + */ +status_t LPSPI_MasterTransferGetCountEDMA(LPSPI_Type *base, lpspi_master_edma_handle_t *handle, size_t *count); + +/*! + * @brief Initializes the LPSPI slave eDMA handle. + * + * This function initializes the LPSPI eDMA handle which can be used for other LPSPI transactional APIs. Usually, for a + * specified LPSPI instance, call this API once to get the initialized handle. + * + * Note that LPSPI eDMA has a separated (Rx and Tx as two sources) or shared (Rx and Tx as the same source) DMA request + * source. + * + * (1) For a separated DMA request source, enable and set the Rx DMAMUX source for edmaRxRegToRxDataHandle and + * Tx DMAMUX source for edmaTxDataToTxRegHandle. + * (2) For a shared DMA request source, enable and set the Rx/Rx DMAMUX source for edmaRxRegToRxDataHandle . + * + * @param base LPSPI peripheral base address. + * @param handle LPSPI handle pointer to lpspi_slave_edma_handle_t. + * @param callback LPSPI callback. + * @param userData callback function parameter. + * @param edmaRxRegToRxDataHandle edmaRxRegToRxDataHandle pointer to edma_handle_t. + * @param edmaTxDataToTxRegHandle edmaTxDataToTxRegHandle pointer to edma_handle_t. + */ +void LPSPI_SlaveTransferCreateHandleEDMA(LPSPI_Type *base, + lpspi_slave_edma_handle_t *handle, + lpspi_slave_edma_transfer_callback_t callback, + void *userData, + edma_handle_t *edmaRxRegToRxDataHandle, + edma_handle_t *edmaTxDataToTxRegHandle); + +/*! + * @brief LPSPI slave transfers data using eDMA. + * + * This function transfers data using eDMA. This is a non-blocking function, which return right away. When all data + * is transferred, the callback function is called. + * + * Note: + * The transfer data size should be an integer multiple of bytesPerFrame if bytesPerFrame is less than or equal to 4. + * For bytesPerFrame greater than 4: + * The transfer data size should be equal to bytesPerFrame if the bytesPerFrame is not an integer multiple of 4. + * Otherwise, the transfer data size can be an integer multiple of bytesPerFrame. + * + * @param base LPSPI peripheral base address. + * @param handle pointer to lpspi_slave_edma_handle_t structure which stores the transfer state. + * @param transfer pointer to lpspi_transfer_t structure. + * @return status of status_t. + */ +status_t LPSPI_SlaveTransferEDMA(LPSPI_Type *base, lpspi_slave_edma_handle_t *handle, lpspi_transfer_t *transfer); + +/*! + * @brief LPSPI slave aborts a transfer which is using eDMA. + * + * This function aborts a transfer which is using eDMA. + * + * @param base LPSPI peripheral base address. + * @param handle pointer to lpspi_slave_edma_handle_t structure which stores the transfer state. + */ +void LPSPI_SlaveTransferAbortEDMA(LPSPI_Type *base, lpspi_slave_edma_handle_t *handle); + +/*! + * @brief Gets the slave eDMA transfer remaining bytes. + * + * This function gets the slave eDMA transfer remaining bytes. + * + * @param base LPSPI peripheral base address. + * @param handle pointer to lpspi_slave_edma_handle_t structure which stores the transfer state. + * @param count Number of bytes transferred so far by the eDMA transaction. + * @return status of status_t. + */ +status_t LPSPI_SlaveTransferGetCountEDMA(LPSPI_Type *base, lpspi_slave_edma_handle_t *handle, size_t *count); + +#if defined(__cplusplus) +} +#endif + +/*! @}*/ + +#endif /*_FSL_LPSPI_EDMA_H_*/ diff --git a/bsps/arm/imxrt/mcux-sdk/drivers/lpspi/fsl_lpspi_freertos.h b/bsps/arm/imxrt/mcux-sdk/drivers/lpspi/fsl_lpspi_freertos.h new file mode 100644 index 0000000000..ab4c647907 --- /dev/null +++ b/bsps/arm/imxrt/mcux-sdk/drivers/lpspi/fsl_lpspi_freertos.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2015, Freescale Semiconductor, Inc. + * Copyright 2016-2022 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef __FSL_LPSPI_FREERTOS_H__ +#define __FSL_LPSPI_FREERTOS_H__ + +#include "FreeRTOS.h" +#include "portable.h" +#include "semphr.h" + +#include "fsl_lpspi.h" + +/*! + * @addtogroup lpspi_freertos_driver LPSPI FreeRTOS Driver + * @{ + */ + +/********************************************************************************************************************** + * Definitions + *********************************************************************************************************************/ + +/*! @name Driver version */ +/*@{*/ +/*! @brief LPSPI FreeRTOS driver version 2.3.1. */ +#define FSL_LPSPI_FREERTOS_DRIVER_VERSION (MAKE_VERSION(2, 3, 1)) +/*@}*/ + +/*! + * @cond RTOS_PRIVATE + * @brief LPSPI FreeRTOS handle + */ +typedef struct _lpspi_rtos_handle +{ + LPSPI_Type *base; /*!< LPSPI base address */ + lpspi_master_handle_t drv_handle; /*!< Handle of the underlying driver, treated as opaque by the RTOS layer */ + status_t async_status; + SemaphoreHandle_t mutex; /*!< Mutex to lock the handle during a trasfer */ + SemaphoreHandle_t event; /*!< Semaphore to notify and unblock task when transfer ends */ +} lpspi_rtos_handle_t; +/*! \endcond */ + +/********************************************************************************************************************** + * API + *********************************************************************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif + +/*! + * @name LPSPI RTOS Operation + * @{ + */ + +/*! + * @brief Initializes LPSPI. + * + * This function initializes the LPSPI module and related RTOS context. + * + * @param handle The RTOS LPSPI handle, the pointer to an allocated space for RTOS context. + * @param base The pointer base address of the LPSPI instance to initialize. + * @param masterConfig Configuration structure to set-up LPSPI in master mode. + * @param srcClock_Hz Frequency of input clock of the LPSPI module. + * @return status of the operation. + */ +status_t LPSPI_RTOS_Init(lpspi_rtos_handle_t *handle, + LPSPI_Type *base, + const lpspi_master_config_t *masterConfig, + uint32_t srcClock_Hz); + +/*! + * @brief Deinitializes the LPSPI. + * + * This function deinitializes the LPSPI module and related RTOS context. + * + * @param handle The RTOS LPSPI handle. + */ +status_t LPSPI_RTOS_Deinit(lpspi_rtos_handle_t *handle); + +/*! + * @brief Performs SPI transfer. + * + * This function performs an SPI transfer according to data given in the transfer structure. + * + * @param handle The RTOS LPSPI handle. + * @param transfer Structure specifying the transfer parameters. + * @return status of the operation. + */ +status_t LPSPI_RTOS_Transfer(lpspi_rtos_handle_t *handle, lpspi_transfer_t *transfer); + +/*! + * @} + */ + +#if defined(__cplusplus) +} +#endif + +/*! + * @} + */ + +#endif /* __FSL_LPSPI_FREERTOS_H__ */ |