diff options
Diffstat (limited to '')
-rw-r--r-- | bsps/arm/imxrt/mcux-sdk/drivers/lpi2c/fsl_lpi2c.c | 2608 | ||||
-rw-r--r-- | bsps/arm/imxrt/mcux-sdk/drivers/lpi2c/fsl_lpi2c.h | 1320 | ||||
-rw-r--r-- | bsps/arm/imxrt/mcux-sdk/drivers/lpi2c/fsl_lpi2c_edma.c | 616 | ||||
-rw-r--r-- | bsps/arm/imxrt/mcux-sdk/drivers/lpi2c/fsl_lpi2c_edma.h | 158 | ||||
-rw-r--r-- | bsps/arm/imxrt/mcux-sdk/drivers/lpi2c/fsl_lpi2c_freertos.h | 107 |
5 files changed, 4809 insertions, 0 deletions
diff --git a/bsps/arm/imxrt/mcux-sdk/drivers/lpi2c/fsl_lpi2c.c b/bsps/arm/imxrt/mcux-sdk/drivers/lpi2c/fsl_lpi2c.c new file mode 100644 index 0000000000..bdf4659050 --- /dev/null +++ b/bsps/arm/imxrt/mcux-sdk/drivers/lpi2c/fsl_lpi2c.c @@ -0,0 +1,2608 @@ +/* + * Copyright (c) 2015, Freescale Semiconductor, Inc. + * Copyright 2016-2022 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "fsl_lpi2c.h" +#include <stdlib.h> +#include <string.h> + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.lpi2c" +#endif + +/* ! @brief LPI2C master fifo commands. */ +enum +{ + kTxDataCmd = LPI2C_MTDR_CMD(0x0U), /*!< Transmit DATA[7:0] */ + kRxDataCmd = LPI2C_MTDR_CMD(0X1U), /*!< Receive (DATA[7:0] + 1) bytes */ + kStopCmd = LPI2C_MTDR_CMD(0x2U), /*!< Generate STOP condition */ + kStartCmd = LPI2C_MTDR_CMD(0x4U), /*!< Generate(repeated) START and transmit address in DATA[[7:0] */ +}; + +/*! + * @brief Default watermark values. + * + * The default watermarks are set to zero. + */ +enum +{ + kDefaultTxWatermark = 0, + kDefaultRxWatermark = 0, +}; + +/*! @brief States for the state machine used by transactional APIs. */ +enum +{ + kIdleState = 0, + kSendCommandState, + kIssueReadCommandState, + kTransferDataState, + kStopState, + kWaitForCompletionState, +}; + +/* + * <! Structure definition for variables that passed as parameters in LPI2C_RunTransferStateMachine. + * The structure is private. + */ +typedef struct _lpi2c_state_machine_param +{ + bool state_complete; + size_t rxCount; + size_t txCount; + uint32_t status; +} lpi2c_state_machine_param_t; + +/*! @brief Typedef for slave interrupt handler. */ +typedef void (*lpi2c_slave_isr_t)(LPI2C_Type *base, lpi2c_slave_handle_t *handle); + +/******************************************************************************* + * Prototypes + ******************************************************************************/ +static uint32_t LPI2C_GetCyclesForWidth( + uint32_t sourceClock_Hz, uint32_t width_ns, uint32_t minCycles, uint32_t maxCycles, uint32_t prescaler); + +static status_t LPI2C_MasterWaitForTxReady(LPI2C_Type *base); + +static status_t LPI2C_RunTransferStateMachine(LPI2C_Type *base, lpi2c_master_handle_t *handle, bool *isDone); + +static void LPI2C_InitTransferStateMachine(lpi2c_master_handle_t *handle); + +static status_t LPI2C_SlaveCheckAndClearError(LPI2C_Type *base, uint32_t flags); + +static void LPI2C_CommonIRQHandler(LPI2C_Type *base, uint32_t instance); + +/*! + * @brief introduce function LPI2C_TransferStateMachineSendCommandState. + * This function was deal with Send Command State. + * + * @param base The I2C peripheral base address. + * @param handle Master nonblocking driver handle. + * @param variable_set Pass the address of the parent function variable. + */ +static void LPI2C_TransferStateMachineSendCommand(LPI2C_Type *base, + lpi2c_master_handle_t *handle, + lpi2c_state_machine_param_t *stateParams); + +/*! + * @brief introduce function LPI2C_TransferStateMachineIssueReadCommandState. + * This function was deal with Issue Read Command State. + * + * @param base The I2C peripheral base address. + * @param handle Master nonblocking driver handle. + * @param stateParams Pass the address of the parent function variable. + */ +static void LPI2C_TransferStateMachineReadCommand(LPI2C_Type *base, + lpi2c_master_handle_t *handle, + lpi2c_state_machine_param_t *stateParams); + +/*! + * @brief introduce function LPI2C_TransferStateMachineTransferDataState. + * This function was deal with init Transfer Data State. + * + * @param base The I2C peripheral base address. + * @param handle Master nonblocking driver handle. + * @param stateParams Pass the address of the parent function variable. + */ +static void LPI2C_TransferStateMachineTransferData(LPI2C_Type *base, + lpi2c_master_handle_t *handle, + lpi2c_state_machine_param_t *stateParams); + +/*! + * @brief introduce function LPI2C_TransferStateMachineStopState. + * This function was deal with Stop State. + * + * @param base The I2C peripheral base address. + * @param handle Master nonblocking driver handle. + * @param stateParams Pass the address of the parent function variable. + * @param[out] isDone Set to true if the transfer has completed. + */ +static void LPI2C_TransferStateMachineStopState(LPI2C_Type *base, + lpi2c_master_handle_t *handle, + lpi2c_state_machine_param_t *stateParams, + bool *isDone); + +/*! + * @brief introduce function LPI2C_TransferStateMachineWaitState. + * This function was deal with Wait For Completion State. + * + * @param base The I2C peripheral base address. + * @param handle Master nonblocking driver handle. + * @param stateParams Pass the address of the parent function variable. + * @param[out] isDone Set to true if the transfer has completed. + */ +static void LPI2C_TransferStateMachineWaitState(LPI2C_Type *base, + lpi2c_master_handle_t *handle, + lpi2c_state_machine_param_t *stateParams, + bool *isDone); + +/******************************************************************************* + * Variables + ******************************************************************************/ + +/*! @brief Array to map LPI2C instance number to base pointer. */ +static LPI2C_Type *const kLpi2cBases[] = LPI2C_BASE_PTRS; + +/*! @brief Array to map LPI2C instance number to IRQ number, used internally for LPI2C master interrupt and EDMA +transactional APIs. */ +IRQn_Type const kLpi2cIrqs[] = LPI2C_IRQS; + +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) +/*! @brief Array to map LPI2C instance number to clock gate enum. */ +static clock_ip_name_t const kLpi2cClocks[] = LPI2C_CLOCKS; + +#if defined(LPI2C_PERIPH_CLOCKS) +/*! @brief Array to map LPI2C instance number to pheripheral clock gate enum. */ +static const clock_ip_name_t kLpi2cPeriphClocks[] = LPI2C_PERIPH_CLOCKS; +#endif + +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ + +/*! @brief Pointer to master IRQ handler for each instance, used internally for LPI2C master interrupt and EDMA +transactional APIs. */ +lpi2c_master_isr_t s_lpi2cMasterIsr; + +/*! @brief Pointers to master handles for each instance, used internally for LPI2C master interrupt and EDMA +transactional APIs. */ +void *s_lpi2cMasterHandle[ARRAY_SIZE(kLpi2cBases)]; + +/*! @brief Pointer to slave IRQ handler for each instance. */ +static lpi2c_slave_isr_t s_lpi2cSlaveIsr; + +/*! @brief Pointers to slave handles for each instance. */ +static lpi2c_slave_handle_t *s_lpi2cSlaveHandle[ARRAY_SIZE(kLpi2cBases)]; + +/******************************************************************************* + * Code + ******************************************************************************/ + +/*! + * brief Returns an instance number given a base address. + * + * If an invalid base address is passed, debug builds will assert. Release builds will just return + * instance number 0. + * + * param base The LPI2C peripheral base address. + * return LPI2C instance number starting from 0. + */ +uint32_t LPI2C_GetInstance(LPI2C_Type *base) +{ + uint32_t instance; + for (instance = 0U; instance < ARRAY_SIZE(kLpi2cBases); ++instance) + { + if (kLpi2cBases[instance] == base) + { + break; + } + } + + assert(instance < ARRAY_SIZE(kLpi2cBases)); + return instance; +} + +/*! + * @brief Computes a cycle count for a given time in nanoseconds. + * @param sourceClock_Hz LPI2C functional clock frequency in Hertz. + * @param width_ns Desired with in nanoseconds. + * @param minCycles Minimum cycle count. + * @param maxCycles Maximum cycle count. + * @param prescaler LPI2C prescaler setting. If the cycle period is not affected by the prescaler value, set it to 0. + */ +static uint32_t LPI2C_GetCyclesForWidth( + uint32_t sourceClock_Hz, uint32_t width_ns, uint32_t minCycles, uint32_t maxCycles, uint32_t prescaler) +{ + assert(sourceClock_Hz > 0U); + + uint32_t divider = 1U; + + while (prescaler != 0U) + { + divider *= 2U; + prescaler--; + } + + uint32_t busCycle_ns = 1000000U / (sourceClock_Hz / divider / 1000U); + /* Calculate the cycle count, round up the calculated value. */ + uint32_t cycles = (width_ns * 10U / busCycle_ns + 5U) / 10U; + + /* If the calculated value is smaller than the minimum value, use the minimum value */ + if (cycles < minCycles) + { + cycles = minCycles; + } + /* If the calculated value is larger than the maximum value, use the maxmum value */ + if (cycles > maxCycles) + { + cycles = maxCycles; + } + + return cycles; +} + +/*! + * @brief Convert provided flags to status code, and clear any errors if present. + * @param base The LPI2C peripheral base address. + * @param status Current status flags value that will be checked. + * @retval #kStatus_Success + * @retval #kStatus_LPI2C_PinLowTimeout + * @retval #kStatus_LPI2C_ArbitrationLost + * @retval #kStatus_LPI2C_Nak + * @retval #kStatus_LPI2C_FifoError + */ +/* Not static so it can be used from fsl_lpi2c_edma.c. */ +status_t LPI2C_MasterCheckAndClearError(LPI2C_Type *base, uint32_t status) +{ + status_t result = kStatus_Success; + + /* Check for error. These errors cause a stop to automatically be sent. We must */ + /* clear the errors before a new transfer can start. */ + status &= (uint32_t)kLPI2C_MasterErrorFlags; + if (0U != status) + { + /* Select the correct error code. Ordered by severity, with bus issues first. */ + if (0U != (status & (uint32_t)kLPI2C_MasterPinLowTimeoutFlag)) + { + result = kStatus_LPI2C_PinLowTimeout; + } + else if (0U != (status & (uint32_t)kLPI2C_MasterArbitrationLostFlag)) + { + result = kStatus_LPI2C_ArbitrationLost; + } + else if (0U != (status & (uint32_t)kLPI2C_MasterNackDetectFlag)) + { + result = kStatus_LPI2C_Nak; + } + else if (0U != (status & (uint32_t)kLPI2C_MasterFifoErrFlag)) + { + result = kStatus_LPI2C_FifoError; + } + else + { + ; /* Intentional empty */ + } + + /* Clear the flags. */ + LPI2C_MasterClearStatusFlags(base, status); + + /* Reset fifos. These flags clear automatically. */ + base->MCR |= LPI2C_MCR_RRF_MASK | LPI2C_MCR_RTF_MASK; + } + else + { + ; /* Intentional empty */ + } + + return result; +} + +/*! + * @brief Wait until there is room in the tx fifo. + * @param base The LPI2C peripheral base address. + * @retval #kStatus_Success + * @retval #kStatus_LPI2C_PinLowTimeout + * @retval #kStatus_LPI2C_ArbitrationLost + * @retval #kStatus_LPI2C_Nak + * @retval #kStatus_LPI2C_FifoError + */ +static status_t LPI2C_MasterWaitForTxReady(LPI2C_Type *base) +{ + status_t result = kStatus_Success; + uint32_t status; + size_t txCount; + size_t txFifoSize = (size_t)FSL_FEATURE_LPI2C_FIFO_SIZEn(base); + +#if I2C_RETRY_TIMES != 0U + uint32_t waitTimes = I2C_RETRY_TIMES; +#endif + do + { + /* Get the number of words in the tx fifo and compute empty slots. */ + LPI2C_MasterGetFifoCounts(base, NULL, &txCount); + txCount = txFifoSize - txCount; + + /* Check for error flags. */ + status = LPI2C_MasterGetStatusFlags(base); + result = LPI2C_MasterCheckAndClearError(base, status); + if (kStatus_Success != result) + { + break; + } +#if I2C_RETRY_TIMES != 0U + waitTimes--; + } while ((0U == txCount) && (0U != waitTimes)); + + if (0U == waitTimes) + { + result = kStatus_LPI2C_Timeout; + } +#else + } while (0U == txCount); +#endif + + return result; +} + +/*! + * @brief Make sure the bus isn't already busy. + * + * A busy bus is allowed if we are the one driving it. + * + * @param base The LPI2C peripheral base address. + * @retval #kStatus_Success + * @retval #kStatus_LPI2C_Busy + */ +/* Not static so it can be used from fsl_lpi2c_edma.c. */ +status_t LPI2C_CheckForBusyBus(LPI2C_Type *base) +{ + status_t ret = kStatus_Success; + + uint32_t status = LPI2C_MasterGetStatusFlags(base); + if ((0U != (status & (uint32_t)kLPI2C_MasterBusBusyFlag)) && (0U == (status & (uint32_t)kLPI2C_MasterBusyFlag))) + { + ret = kStatus_LPI2C_Busy; + } + + return ret; +} + +/*! + * brief Provides a default configuration for the LPI2C master peripheral. + * + * This function provides the following default configuration for the LPI2C master peripheral: + * code + * masterConfig->enableMaster = true; + * masterConfig->debugEnable = false; + * masterConfig->ignoreAck = false; + * masterConfig->pinConfig = kLPI2C_2PinOpenDrain; + * masterConfig->baudRate_Hz = 100000U; + * masterConfig->busIdleTimeout_ns = 0U; + * masterConfig->pinLowTimeout_ns = 0U; + * masterConfig->sdaGlitchFilterWidth_ns = 0U; + * masterConfig->sclGlitchFilterWidth_ns = 0U; + * masterConfig->hostRequest.enable = false; + * masterConfig->hostRequest.source = kLPI2C_HostRequestExternalPin; + * masterConfig->hostRequest.polarity = kLPI2C_HostRequestPinActiveHigh; + * endcode + * + * After calling this function, you can override any settings in order to customize the configuration, + * prior to initializing the master driver with LPI2C_MasterInit(). + * + * param[out] masterConfig User provided configuration structure for default values. Refer to #lpi2c_master_config_t. + */ +void LPI2C_MasterGetDefaultConfig(lpi2c_master_config_t *masterConfig) +{ + /* Initializes the configure structure to zero. */ + (void)memset(masterConfig, 0, sizeof(*masterConfig)); + + masterConfig->enableMaster = true; + masterConfig->debugEnable = false; + masterConfig->enableDoze = true; + masterConfig->ignoreAck = false; + masterConfig->pinConfig = kLPI2C_2PinOpenDrain; + masterConfig->baudRate_Hz = 100000U; + masterConfig->busIdleTimeout_ns = 0U; /* Set to 0 to disable the function */ + masterConfig->pinLowTimeout_ns = 0U; /* Set to 0 to disable the function */ + masterConfig->sdaGlitchFilterWidth_ns = 0U; /* Set to 0 to disable the function */ + masterConfig->sclGlitchFilterWidth_ns = 0U; /* Set to 0 to disable the function */ + masterConfig->hostRequest.enable = false; + masterConfig->hostRequest.source = kLPI2C_HostRequestExternalPin; + masterConfig->hostRequest.polarity = kLPI2C_HostRequestPinActiveHigh; +} + +/*! + * brief Initializes the LPI2C master peripheral. + * + * This function enables the peripheral clock and initializes the LPI2C master peripheral as described by the user + * provided configuration. A software reset is performed prior to configuration. + * + * param base The LPI2C peripheral base address. + * param masterConfig User provided peripheral configuration. Use LPI2C_MasterGetDefaultConfig() to get a set of + * defaults + * that you can override. + * param sourceClock_Hz Frequency in Hertz of the LPI2C functional clock. Used to calculate the baud rate divisors, + * filter widths, and timeout periods. + */ +void LPI2C_MasterInit(LPI2C_Type *base, const lpi2c_master_config_t *masterConfig, uint32_t sourceClock_Hz) +{ + uint32_t prescaler; + uint32_t cycles; + uint32_t cfgr2; + uint32_t value; + +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) + + uint32_t instance = LPI2C_GetInstance(base); + + /* Ungate the clock. */ + (void)CLOCK_EnableClock(kLpi2cClocks[instance]); +#if defined(LPI2C_PERIPH_CLOCKS) + /* Ungate the functional clock in initialize function. */ + CLOCK_EnableClock(kLpi2cPeriphClocks[instance]); +#endif + +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ + + /* Reset peripheral before configuring it. */ + LPI2C_MasterReset(base); + + /* Doze bit: 0 is enable, 1 is disable */ + base->MCR = LPI2C_MCR_DBGEN(masterConfig->debugEnable) | LPI2C_MCR_DOZEN(!(masterConfig->enableDoze)); + + /* host request */ + value = base->MCFGR0; + value &= (~(LPI2C_MCFGR0_HREN_MASK | LPI2C_MCFGR0_HRPOL_MASK | LPI2C_MCFGR0_HRSEL_MASK)); + value |= LPI2C_MCFGR0_HREN(masterConfig->hostRequest.enable) | + LPI2C_MCFGR0_HRPOL(masterConfig->hostRequest.polarity) | + LPI2C_MCFGR0_HRSEL(masterConfig->hostRequest.source); + base->MCFGR0 = value; + + /* pin config and ignore ack */ + value = base->MCFGR1; + value &= ~(LPI2C_MCFGR1_PINCFG_MASK | LPI2C_MCFGR1_IGNACK_MASK); + value |= LPI2C_MCFGR1_PINCFG(masterConfig->pinConfig); + value |= LPI2C_MCFGR1_IGNACK(masterConfig->ignoreAck); + base->MCFGR1 = value; + + LPI2C_MasterSetWatermarks(base, (size_t)kDefaultTxWatermark, (size_t)kDefaultRxWatermark); + + /* Configure glitch filters. */ + cfgr2 = base->MCFGR2; + if (0U != (masterConfig->sdaGlitchFilterWidth_ns)) + { + /* Calculate SDA filter width. The width is equal to FILTSDA cycles of functional clock. + And set FILTSDA to 0 disables the fileter, so the min value is 1. */ + cycles = LPI2C_GetCyclesForWidth(sourceClock_Hz, masterConfig->sdaGlitchFilterWidth_ns, 1U, + (LPI2C_MCFGR2_FILTSDA_MASK >> LPI2C_MCFGR2_FILTSDA_SHIFT), 0U); + cfgr2 &= ~LPI2C_MCFGR2_FILTSDA_MASK; + cfgr2 |= LPI2C_MCFGR2_FILTSDA(cycles); + } + if (0U != masterConfig->sclGlitchFilterWidth_ns) + { + /* Calculate SDL filter width. The width is equal to FILTSCL cycles of functional clock. + And set FILTSCL to 0 disables the fileter, so the min value is 1. */ + cycles = LPI2C_GetCyclesForWidth(sourceClock_Hz, masterConfig->sclGlitchFilterWidth_ns, 1U, + (LPI2C_MCFGR2_FILTSCL_MASK >> LPI2C_MCFGR2_FILTSCL_SHIFT), 0U); + cfgr2 &= ~LPI2C_MCFGR2_FILTSCL_MASK; + cfgr2 |= LPI2C_MCFGR2_FILTSCL(cycles); + } + base->MCFGR2 = cfgr2; + + /* Configure baudrate after the SDA/SCL glitch filter setting, + since the baudrate calculation needs them as parameter. */ + LPI2C_MasterSetBaudRate(base, sourceClock_Hz, masterConfig->baudRate_Hz); + + /* Configure bus idle and pin low timeouts after baudrate setting, + since the timeout calculation needs prescaler as parameter. */ + prescaler = (base->MCFGR1 & LPI2C_MCFGR1_PRESCALE_MASK) >> LPI2C_MCFGR1_PRESCALE_SHIFT; + + if (0U != (masterConfig->busIdleTimeout_ns)) + { + /* Calculate bus idle timeout value. The value is equal to BUSIDLE cycles of functional clock divided by + prescaler. And set BUSIDLE to 0 disables the fileter, so the min value is 1. */ + cycles = LPI2C_GetCyclesForWidth(sourceClock_Hz, masterConfig->busIdleTimeout_ns, 1U, + (LPI2C_MCFGR2_BUSIDLE_MASK >> LPI2C_MCFGR2_BUSIDLE_SHIFT), prescaler); + base->MCFGR2 = (base->MCFGR2 & (~LPI2C_MCFGR2_BUSIDLE_MASK)) | LPI2C_MCFGR2_BUSIDLE(cycles); + } + if (0U != masterConfig->pinLowTimeout_ns) + { + /* Calculate bus pin low timeout value. The value is equal to PINLOW cycles of functional clock divided by + prescaler. And set PINLOW to 0 disables the fileter, so the min value is 1. */ + cycles = LPI2C_GetCyclesForWidth(sourceClock_Hz, masterConfig->pinLowTimeout_ns / 256U, 1U, + (LPI2C_MCFGR2_BUSIDLE_MASK >> LPI2C_MCFGR2_BUSIDLE_SHIFT), prescaler); + base->MCFGR3 = (base->MCFGR3 & ~LPI2C_MCFGR3_PINLOW_MASK) | LPI2C_MCFGR3_PINLOW(cycles); + } + + LPI2C_MasterEnable(base, masterConfig->enableMaster); +} + +/*! + * brief Deinitializes the LPI2C master peripheral. + * + * This function disables the LPI2C master peripheral and gates the clock. It also performs a software + * reset to restore the peripheral to reset conditions. + * + * param base The LPI2C peripheral base address. + */ +void LPI2C_MasterDeinit(LPI2C_Type *base) +{ + /* Restore to reset state. */ + LPI2C_MasterReset(base); + +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) + + uint32_t instance = LPI2C_GetInstance(base); + + /* Gate clock. */ + (void)CLOCK_DisableClock(kLpi2cClocks[instance]); +#if defined(LPI2C_PERIPH_CLOCKS) + /* Gate the functional clock. */ + CLOCK_DisableClock(kLpi2cPeriphClocks[instance]); +#endif + +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ +} + +/*! + * brief Configures LPI2C master data match feature. + * + * param base The LPI2C peripheral base address. + * param matchConfig Settings for the data match feature. + */ +void LPI2C_MasterConfigureDataMatch(LPI2C_Type *base, const lpi2c_data_match_config_t *matchConfig) +{ + /* Disable master mode. */ + bool wasEnabled = (0U != ((base->MCR & LPI2C_MCR_MEN_MASK) >> LPI2C_MCR_MEN_SHIFT)); + LPI2C_MasterEnable(base, false); + + base->MCFGR1 = (base->MCFGR1 & ~LPI2C_MCFGR1_MATCFG_MASK) | LPI2C_MCFGR1_MATCFG(matchConfig->matchMode); + base->MCFGR0 = (base->MCFGR0 & ~LPI2C_MCFGR0_RDMO_MASK) | LPI2C_MCFGR0_RDMO(matchConfig->rxDataMatchOnly); + base->MDMR = LPI2C_MDMR_MATCH0(matchConfig->match0) | LPI2C_MDMR_MATCH1(matchConfig->match1); + + /* Restore master mode. */ + if (wasEnabled) + { + LPI2C_MasterEnable(base, true); + } +} + +/*! + * brief Sets the I2C bus frequency for master transactions. + * + * The LPI2C master is automatically disabled and re-enabled as necessary to configure the baud + * rate. Do not call this function during a transfer, or the transfer is aborted. + * + * note Please note that the second parameter is the clock frequency of LPI2C module, the third + * parameter means user configured bus baudrate, this implementation is different from other I2C drivers + * which use baudrate configuration as second parameter and source clock frequency as third parameter. + * + * param base The LPI2C peripheral base address. + * param sourceClock_Hz LPI2C functional clock frequency in Hertz. + * param baudRate_Hz Requested bus frequency in Hertz. + */ +void LPI2C_MasterSetBaudRate(LPI2C_Type *base, uint32_t sourceClock_Hz, uint32_t baudRate_Hz) +{ + bool wasEnabled; + uint8_t filtScl = (uint8_t)((base->MCFGR2 & LPI2C_MCFGR2_FILTSCL_MASK) >> LPI2C_MCFGR2_FILTSCL_SHIFT); + + uint8_t divider = 1U; + uint8_t bestDivider = 1U; + uint8_t prescale = 0U; + uint8_t bestPre = 0U; + + uint8_t clkCycle; + uint8_t bestclkCycle = 0U; + + uint32_t absError = 0U; + uint32_t bestError = 0xffffffffu; + uint32_t computedRate; + + uint32_t tmpReg = 0U; + + /* Disable master mode. */ + wasEnabled = (0U != ((base->MCR & LPI2C_MCR_MEN_MASK) >> LPI2C_MCR_MEN_SHIFT)); + LPI2C_MasterEnable(base, false); + + /* Baud rate = (sourceClock_Hz / 2 ^ prescale) / (CLKLO + 1 + CLKHI + 1 + SCL_LATENCY) + * SCL_LATENCY = ROUNDDOWN((2 + FILTSCL) / (2 ^ prescale)) + */ + for (prescale = 0U; prescale <= 7U; prescale++) + { + /* Calculate the clkCycle, clkCycle = CLKLO + CLKHI, divider = 2 ^ prescale */ + clkCycle = (uint8_t)((10U * sourceClock_Hz / divider / baudRate_Hz + 5U) / 10U - (2U + filtScl) / divider - 2U); + /* According to register description, The max value for CLKLO and CLKHI is 63. + however to meet the I2C specification of tBUF, CLKHI should be less than + clkCycle - 0.52 x sourceClock_Hz / baudRate_Hz / divider + 1U. Refer to the comment of the tmpHigh's + calculation for details. So we have: + CLKHI < clkCycle - 0.52 x sourceClock_Hz / baudRate_Hz / divider + 1U, + clkCycle = CLKHI + CLKLO and + sourceClock_Hz / baudRate_Hz / divider = clkCycle + 2 + ROUNDDOWN((2 + FILTSCL) / divider), + we can come up with: CLKHI < 0.92 x CLKLO - ROUNDDOWN(2 + FILTSCL) / divider + so the max boundary of CLKHI should be 0.92 x 63 - ROUNDDOWN(2 + FILTSCL) / divider, + and the max boundary of clkCycle is 1.92 x 63 - ROUNDDOWN(2 + FILTSCL) / divider. */ + if (clkCycle > (120U - (2U + filtScl) / divider)) + { + divider *= 2U; + continue; + } + /* Calculate the computed baudrate and compare it with the desired baudrate */ + computedRate = (sourceClock_Hz / (uint32_t)divider) / + ((uint32_t)clkCycle + 2U + (2U + (uint32_t)filtScl) / (uint32_t)divider); + absError = baudRate_Hz > computedRate ? baudRate_Hz - computedRate : computedRate - baudRate_Hz; + if (absError < bestError) + { + bestPre = prescale; + bestDivider = divider; + bestclkCycle = clkCycle; + bestError = absError; + + /* If the error is 0, then we can stop searching because we won't find a better match. */ + if (absError == 0U) + { + break; + } + } + divider *= 2U; + } + + /* SCL low time tLO should be larger than or equal to SCL high time tHI: + tLO = ((CLKLO + 1) x (2 ^ PRESCALE)) >= tHI = ((CLKHI + 1 + SCL_LATENCY) x (2 ^ PRESCALE)), + which is CLKLO >= CLKHI + (2U + filtScl) / bestDivider. + Also since bestclkCycle = CLKLO + CLKHI, bestDivider = 2 ^ PRESCALE + which makes CLKHI <= (bestclkCycle - (2U + filtScl) / bestDivider) / 2U. + + The max tBUF should be at least 0.52 times of the SCL clock cycle: + tBUF = ((CLKLO + 1) x (2 ^ PRESCALE) / sourceClock_Hz) > (0.52 / baudRate_Hz), + plus bestDivider = 2 ^ PRESCALE, bestclkCycle = CLKLO + CLKHI we can come up with + CLKHI <= (bestclkCycle - 0.52 x sourceClock_Hz / baudRate_Hz / bestDivider + 1U). + In this case to get a safe CLKHI calculation, we can assume: + */ + uint8_t tmpHigh = (bestclkCycle - (2U + filtScl) / bestDivider) / 2U; + while (tmpHigh > (bestclkCycle - 52U * sourceClock_Hz / baudRate_Hz / bestDivider / 100U + 1U)) + { + tmpHigh = tmpHigh - 1U; + } + + /* Calculate DATAVD and SETHOLD. + To meet the timing requirement of I2C spec for standard mode, fast mode and fast mode plus: */ + /* The min tHD:STA/tSU:STA/tSU:STO should be at least 0.4 times of the SCL clock cycle, use 0.5 to be safe: + tHD:STA = ((SETHOLD + 1) x (2 ^ PRESCALE) / sourceClock_Hz) > (0.5 / baudRate_Hz), bestDivider = 2 ^ PRESCALE */ + uint8_t tmpHold = (uint8_t)(sourceClock_Hz / baudRate_Hz / bestDivider / 2U) - 1U; + + /* The max tVD:DAT/tVD:ACK/tHD:DAT should be at most 0.345 times of the SCL clock cycle, use 0.25 to be safe: + tVD:DAT = ((DATAVD + 1) x (2 ^ PRESCALE) / sourceClock_Hz) < (0.25 / baudRate_Hz), bestDivider = 2 ^ PRESCALE */ + uint8_t tmpDataVd = (uint8_t)(sourceClock_Hz / baudRate_Hz / bestDivider / 4U) - 1U; + + /* The min tSU:DAT should be at least 0.05 times of the SCL clock cycle: + tSU:DAT = ((2 + FILTSDA + 2 ^ PRESCALE) / sourceClock_Hz) >= (0.05 / baud), + plus bestDivider = 2 ^ PRESCALE, we can come up with: + FILTSDA >= (0.05 x sourceClock_Hz / baudRate_Hz - bestDivider - 2) */ + if ((sourceClock_Hz / baudRate_Hz / 20U) > (bestDivider + 2U)) + { + /* Read out the FILTSDA configuration, if it is smaller than expected, change the setting. */ + uint8_t filtSda = (uint8_t)((base->MCFGR2 & LPI2C_MCFGR2_FILTSDA_MASK) >> LPI2C_MCFGR2_FILTSDA_SHIFT); + if (filtSda < (sourceClock_Hz / baudRate_Hz / 20U - bestDivider - 2U)) + { + filtSda = (uint8_t)(sourceClock_Hz / baudRate_Hz / 20U) - bestDivider - 2U; + } + base->MCFGR2 = (base->MCFGR2 & ~LPI2C_MCFGR2_FILTSDA_MASK) | LPI2C_MCFGR2_FILTSDA(filtSda); + } + + /* Set CLKHI, CLKLO, SETHOLD, DATAVD value. */ + tmpReg = LPI2C_MCCR0_CLKHI((uint32_t)tmpHigh) | + LPI2C_MCCR0_CLKLO((uint32_t)((uint32_t)bestclkCycle - (uint32_t)tmpHigh)) | + LPI2C_MCCR0_SETHOLD((uint32_t)tmpHold) | LPI2C_MCCR0_DATAVD((uint32_t)tmpDataVd); + base->MCCR0 = tmpReg; + + /* Set PRESCALE value. */ + base->MCFGR1 = (base->MCFGR1 & ~LPI2C_MCFGR1_PRESCALE_MASK) | LPI2C_MCFGR1_PRESCALE(bestPre); + + /* Restore master mode. */ + if (wasEnabled) + { + LPI2C_MasterEnable(base, true); + } +} + +/*! + * brief Sends a START signal and slave address on the I2C bus. + * + * This function is used to initiate a new master mode transfer. First, the bus state is checked to ensure + * that another master is not occupying the bus. Then a START signal is transmitted, followed by the + * 7-bit address specified in the a address parameter. Note that this function does not actually wait + * until the START and address are successfully sent on the bus before returning. + * + * param base The LPI2C peripheral base address. + * param address 7-bit slave device address, in bits [6:0]. + * param dir Master transfer direction, either #kLPI2C_Read or #kLPI2C_Write. This parameter is used to set + * the R/w bit (bit 0) in the transmitted slave address. + * retval #kStatus_Success START signal and address were successfully enqueued in the transmit FIFO. + * retval #kStatus_LPI2C_Busy Another master is currently utilizing the bus. + */ +status_t LPI2C_MasterStart(LPI2C_Type *base, uint8_t address, lpi2c_direction_t dir) +{ + /* Return an error if the bus is already in use not by us. */ + status_t result = LPI2C_CheckForBusyBus(base); + if (kStatus_Success == result) + { + /* Clear all flags. */ + LPI2C_MasterClearStatusFlags(base, (uint32_t)kLPI2C_MasterClearFlags); + + /* Turn off auto-stop option. */ + base->MCFGR1 &= ~LPI2C_MCFGR1_AUTOSTOP_MASK; + + /* Wait until there is room in the fifo. */ + result = LPI2C_MasterWaitForTxReady(base); + if (kStatus_Success == result) + { + /* Issue start command. */ + base->MTDR = (uint32_t)kStartCmd | (((uint32_t)address << 1U) | (uint32_t)dir); + } + } + + return result; +} + +/*! + * brief Sends a STOP signal on the I2C bus. + * + * This function does not return until the STOP signal is seen on the bus, or an error occurs. + * + * param base The LPI2C peripheral base address. + * retval #kStatus_Success The STOP signal was successfully sent on the bus and the transaction terminated. + * retval #kStatus_LPI2C_Busy Another master is currently utilizing the bus. + * retval #kStatus_LPI2C_Nak The slave device sent a NAK in response to a byte. + * retval #kStatus_LPI2C_FifoError FIFO under run or overrun. + * retval #kStatus_LPI2C_ArbitrationLost Arbitration lost error. + * retval #kStatus_LPI2C_PinLowTimeout SCL or SDA were held low longer than the timeout. + */ +status_t LPI2C_MasterStop(LPI2C_Type *base) +{ + /* Wait until there is room in the fifo. */ + status_t result = LPI2C_MasterWaitForTxReady(base); + if (kStatus_Success == result) + { + /* Send the STOP signal */ + base->MTDR = (uint32_t)kStopCmd; + + /* Wait for the stop detected flag to set, indicating the transfer has completed on the bus. */ + /* Also check for errors while waiting. */ +#if I2C_RETRY_TIMES != 0U + uint32_t waitTimes = I2C_RETRY_TIMES; +#endif + +#if I2C_RETRY_TIMES != 0U + while ((result == kStatus_Success) && (0U != waitTimes)) + { + waitTimes--; +#else + while (result == kStatus_Success) + { +#endif + uint32_t status = LPI2C_MasterGetStatusFlags(base); + + /* Check for error flags. */ + result = LPI2C_MasterCheckAndClearError(base, status); + + /* Check if the stop was sent successfully. */ + if ((0U != (status & (uint32_t)kLPI2C_MasterStopDetectFlag)) && + (0U != (status & (uint32_t)kLPI2C_MasterTxReadyFlag))) + { + LPI2C_MasterClearStatusFlags(base, (uint32_t)kLPI2C_MasterStopDetectFlag); + break; + } + } + +#if I2C_RETRY_TIMES != 0U + if (0U == waitTimes) + { + result = kStatus_LPI2C_Timeout; + } +#endif + } + + return result; +} + +/*! + * brief Performs a polling receive transfer on the I2C bus. + * + * param base The LPI2C peripheral base address. + * param rxBuff The pointer to the data to be transferred. + * param rxSize The length in bytes of the data to be transferred. + * retval #kStatus_Success Data was received successfully. + * retval #kStatus_LPI2C_Busy Another master is currently utilizing the bus. + * retval #kStatus_LPI2C_Nak The slave device sent a NAK in response to a byte. + * retval #kStatus_LPI2C_FifoError FIFO under run or overrun. + * retval #kStatus_LPI2C_ArbitrationLost Arbitration lost error. + * retval #kStatus_LPI2C_PinLowTimeout SCL or SDA were held low longer than the timeout. + */ +status_t LPI2C_MasterReceive(LPI2C_Type *base, void *rxBuff, size_t rxSize) +{ + assert(NULL != rxBuff); + + status_t result = kStatus_Success; + uint8_t *buf; + size_t tmpRxSize = rxSize; +#if I2C_RETRY_TIMES != 0U + uint32_t waitTimes; +#endif + + /* Check transfer data size. */ + if (rxSize > ((size_t)256 * (size_t)FSL_FEATURE_LPI2C_FIFO_SIZEn(base))) + { + return kStatus_InvalidArgument; + } + + /* Handle empty read. */ + if (rxSize != 0U) + { + /* Wait until there is room in the command fifo. */ + result = LPI2C_MasterWaitForTxReady(base); + if (kStatus_Success == result) + { + /* Issue command to receive data. A single write to MTDR can issue read operation of 0xFFU + 1 byte of data + at most, so when the rxSize is larger than 0x100U, push multiple read commands to MTDR until rxSize is + reached. */ + while (tmpRxSize != 0U) + { + if (tmpRxSize > 256U) + { + base->MTDR = (uint32_t)(kRxDataCmd) | (uint32_t)LPI2C_MTDR_DATA(0xFFU); + tmpRxSize -= 256U; + } + else + { + base->MTDR = (uint32_t)(kRxDataCmd) | (uint32_t)LPI2C_MTDR_DATA(tmpRxSize - 1U); + tmpRxSize = 0U; + } + } + + /* Receive data */ + buf = (uint8_t *)rxBuff; + while (0U != (rxSize--)) + { +#if I2C_RETRY_TIMES != 0U + waitTimes = I2C_RETRY_TIMES; +#endif + /* Read LPI2C receive fifo register. The register includes a flag to indicate whether */ + /* the FIFO is empty, so we can both get the data and check if we need to keep reading */ + /* using a single register read. */ + uint32_t value = 0U; + do + { + /* Check for errors. */ + result = LPI2C_MasterCheckAndClearError(base, LPI2C_MasterGetStatusFlags(base)); + if (kStatus_Success != result) + { + break; + } + + value = base->MRDR; +#if I2C_RETRY_TIMES != 0U + waitTimes--; + } while ((0U != (value & LPI2C_MRDR_RXEMPTY_MASK)) && (0U != waitTimes)); + if (0U == waitTimes) + { + result = kStatus_LPI2C_Timeout; + } +#else + } while (0U != (value & LPI2C_MRDR_RXEMPTY_MASK)); +#endif + if ((status_t)kStatus_Success != result) + { + break; + } + + *buf++ = (uint8_t)(value & LPI2C_MRDR_DATA_MASK); + } + } + } + + return result; +} + +/*! + * brief Performs a polling send transfer on the I2C bus. + * + * Sends up to a txSize number of bytes to the previously addressed slave device. The slave may + * reply with a NAK to any byte in order to terminate the transfer early. If this happens, this + * function returns #kStatus_LPI2C_Nak. + * + * param base The LPI2C peripheral base address. + * param txBuff The pointer to the data to be transferred. + * param txSize The length in bytes of the data to be transferred. + * retval #kStatus_Success Data was sent successfully. + * retval #kStatus_LPI2C_Busy Another master is currently utilizing the bus. + * retval #kStatus_LPI2C_Nak The slave device sent a NAK in response to a byte. + * retval #kStatus_LPI2C_FifoError FIFO under run or over run. + * retval #kStatus_LPI2C_ArbitrationLost Arbitration lost error. + * retval #kStatus_LPI2C_PinLowTimeout SCL or SDA were held low longer than the timeout. + */ +status_t LPI2C_MasterSend(LPI2C_Type *base, void *txBuff, size_t txSize) +{ + status_t result = kStatus_Success; + uint8_t *buf = (uint8_t *)txBuff; + + assert(NULL != txBuff); + + /* Send data buffer */ + while (0U != (txSize--)) + { + /* Wait until there is room in the fifo. This also checks for errors. */ + result = LPI2C_MasterWaitForTxReady(base); + if (kStatus_Success != result) + { + break; + } + + /* Write byte into LPI2C master data register. */ + base->MTDR = *buf++; + } + + return result; +} + +/*! + * brief Performs a master polling transfer on the I2C bus. + * + * note The API does not return until the transfer succeeds or fails due + * to error happens during transfer. + * + * param base The LPI2C peripheral base address. + * param transfer Pointer to the transfer structure. + * retval #kStatus_Success Data was received successfully. + * retval #kStatus_LPI2C_Busy Another master is currently utilizing the bus. + * retval #kStatus_LPI2C_Nak The slave device sent a NAK in response to a byte. + * retval #kStatus_LPI2C_FifoError FIFO under run or overrun. + * retval #kStatus_LPI2C_ArbitrationLost Arbitration lost error. + * retval #kStatus_LPI2C_PinLowTimeout SCL or SDA were held low longer than the timeout. + */ +status_t LPI2C_MasterTransferBlocking(LPI2C_Type *base, lpi2c_master_transfer_t *transfer) +{ + assert(NULL != transfer); + assert(transfer->subaddressSize <= sizeof(transfer->subaddress)); + + status_t result = kStatus_Success; + uint16_t commandBuffer[7]; + uint32_t cmdCount = 0U; + + /* Check transfer data size in read operation. */ + if ((transfer->direction == kLPI2C_Read) && + (transfer->dataSize > ((size_t)256 * (size_t)FSL_FEATURE_LPI2C_FIFO_SIZEn(base)))) + { + return kStatus_InvalidArgument; + } + + /* Enable the master function and disable the slave function. */ + LPI2C_MasterEnable(base, true); + LPI2C_SlaveEnable(base, false); + + /* Return an error if the bus is already in use not by us. */ + result = LPI2C_CheckForBusyBus(base); + if (kStatus_Success == result) + { + /* Clear all flags. */ + LPI2C_MasterClearStatusFlags(base, (uint32_t)kLPI2C_MasterClearFlags); + + /* Turn off auto-stop option. */ + base->MCFGR1 &= ~LPI2C_MCFGR1_AUTOSTOP_MASK; + + lpi2c_direction_t direction = (0U != transfer->subaddressSize) ? kLPI2C_Write : transfer->direction; + if (0U == (transfer->flags & (uint32_t)kLPI2C_TransferNoStartFlag)) + { + commandBuffer[cmdCount++] = + (uint16_t)kStartCmd | + (uint16_t)((uint16_t)((uint16_t)transfer->slaveAddress << 1U) | (uint16_t)direction); + } + + /* Subaddress, MSB first. */ + if (0U != transfer->subaddressSize) + { + uint32_t subaddressRemaining = transfer->subaddressSize; + while (0U != subaddressRemaining--) + { + uint8_t subaddressByte = (uint8_t)((transfer->subaddress >> (8U * subaddressRemaining)) & 0xffU); + commandBuffer[cmdCount++] = subaddressByte; + } + } + + /* Reads need special handling. */ + if ((0U != transfer->dataSize) && (transfer->direction == kLPI2C_Read)) + { + /* Need to send repeated start if switching directions to read. */ + if (direction == kLPI2C_Write) + { + commandBuffer[cmdCount++] = + (uint16_t)kStartCmd | + (uint16_t)((uint16_t)((uint16_t)transfer->slaveAddress << 1U) | (uint16_t)kLPI2C_Read); + } + } + + /* Send command buffer */ + uint32_t index = 0U; + while (0U != cmdCount--) + { + /* Wait until there is room in the fifo. This also checks for errors. */ + result = LPI2C_MasterWaitForTxReady(base); + if (kStatus_Success != result) + { + break; + } + + /* Write byte into LPI2C master data register. */ + base->MTDR = commandBuffer[index]; + index++; + } + + if (kStatus_Success == result) + { + /* Transmit data. */ + if ((transfer->direction == kLPI2C_Write) && (transfer->dataSize > 0U)) + { + /* Send Data. */ + result = LPI2C_MasterSend(base, transfer->data, transfer->dataSize); + } + + /* Receive Data. */ + if ((transfer->direction == kLPI2C_Read) && (transfer->dataSize > 0U)) + { + result = LPI2C_MasterReceive(base, transfer->data, transfer->dataSize); + } + + if (kStatus_Success == result) + { + if ((transfer->flags & (uint32_t)kLPI2C_TransferNoStopFlag) == 0U) + { + result = LPI2C_MasterStop(base); + } + } + } + } + + return result; +} + +/*! + * brief Creates a new handle for the LPI2C master non-blocking APIs. + * + * The creation of a handle is for use with the non-blocking APIs. Once a handle + * is created, there is not a corresponding destroy handle. If the user wants to + * terminate a transfer, the LPI2C_MasterTransferAbort() API shall be called. + * + * + * note The function also enables the NVIC IRQ for the input LPI2C. Need to notice + * that on some SoCs the LPI2C IRQ is connected to INTMUX, in this case user needs to + * enable the associated INTMUX IRQ in application. + * + * param base The LPI2C peripheral base address. + * param[out] handle Pointer to the LPI2C master driver handle. + * param callback User provided pointer to the asynchronous callback function. + * param userData User provided pointer to the application callback data. + */ +void LPI2C_MasterTransferCreateHandle(LPI2C_Type *base, + lpi2c_master_handle_t *handle, + lpi2c_master_transfer_callback_t callback, + void *userData) +{ + uint32_t instance; + + assert(NULL != handle); + + /* Clear out the handle. */ + (void)memset(handle, 0, sizeof(*handle)); + + /* Look up instance number */ + instance = LPI2C_GetInstance(base); + + /* Save base and instance. */ + handle->completionCallback = callback; + handle->userData = userData; + + /* Save this handle for IRQ use. */ + s_lpi2cMasterHandle[instance] = handle; + + /* Set irq handler. */ + s_lpi2cMasterIsr = LPI2C_MasterTransferHandleIRQ; + + /* Clear internal IRQ enables and enable NVIC IRQ. */ + LPI2C_MasterDisableInterrupts(base, (uint32_t)kLPI2C_MasterIrqFlags); + + /* Enable NVIC IRQ, this only enables the IRQ directly connected to the NVIC. + In some cases the LPI2C IRQ is configured through INTMUX, user needs to enable + INTMUX IRQ in application code. */ + (void)EnableIRQ(kLpi2cIrqs[instance]); +} + +static void LPI2C_TransferStateMachineSendCommand(LPI2C_Type *base, + lpi2c_master_handle_t *handle, + lpi2c_state_machine_param_t *stateParams) +{ + assert(stateParams != NULL); + uint16_t sendval; + + /* Make sure there is room in the tx fifo for the next command. */ + if (0U == (stateParams->txCount)--) + { + stateParams->state_complete = true; + return; + } + + /* Issue command. buf is a uint8_t* pointing at the uint16 command array. */ + sendval = ((uint16_t)handle->buf[0]) | (((uint16_t)handle->buf[1]) << 8U); + base->MTDR = sendval; + handle->buf++; + handle->buf++; + + /* Count down until all commands are sent. */ + if (--handle->remainingBytes == 0U) + { + /* Choose next state and set up buffer pointer and count. */ + if (0U != handle->transfer.dataSize) + { + /* Either a send or receive transfer is next. */ + handle->state = (uint8_t)kTransferDataState; + handle->buf = (uint8_t *)handle->transfer.data; + handle->remainingBytes = (uint16_t)handle->transfer.dataSize; + if (handle->transfer.direction == kLPI2C_Read) + { + /* Disable TX interrupt */ + LPI2C_MasterDisableInterrupts(base, (uint32_t)kLPI2C_MasterTxReadyFlag); + /* Issue command to receive data. A single write to MTDR can issue read operation of + 0xFFU + 1 byte of data at most, so when the dataSize is larger than 0x100U, push + multiple read commands to MTDR until dataSize is reached. */ + size_t tmpRxSize = handle->transfer.dataSize; + while (tmpRxSize != 0U) + { + LPI2C_MasterGetFifoCounts(base, NULL, &stateParams->txCount); + while ((size_t)FSL_FEATURE_LPI2C_FIFO_SIZEn(base) == stateParams->txCount) + { + LPI2C_MasterGetFifoCounts(base, NULL, &stateParams->txCount); + } + + if (tmpRxSize > 256U) + { + base->MTDR = (uint32_t)(kRxDataCmd) | (uint32_t)LPI2C_MTDR_DATA(0xFFU); + tmpRxSize -= 256U; + } + else + { + base->MTDR = (uint32_t)(kRxDataCmd) | (uint32_t)LPI2C_MTDR_DATA(tmpRxSize - 1U); + tmpRxSize = 0U; + } + } + } + } + else + { + /* No transfer, so move to stop state. */ + handle->state = (uint8_t)kStopState; + } + } +} + +static void LPI2C_TransferStateMachineReadCommand(LPI2C_Type *base, + lpi2c_master_handle_t *handle, + lpi2c_state_machine_param_t *stateParams) +{ + assert(stateParams != NULL); + + /* Make sure there is room in the tx fifo for the read command. */ + if (0U == (stateParams->txCount)--) + { + stateParams->state_complete = true; + return; + } + + base->MTDR = (uint32_t)kRxDataCmd | LPI2C_MTDR_DATA(handle->transfer.dataSize - 1U); + + /* Move to transfer state. */ + handle->state = (uint8_t)kTransferDataState; + if (handle->transfer.direction == kLPI2C_Read) + { + /* Disable TX interrupt */ + LPI2C_MasterDisableInterrupts(base, (uint32_t)kLPI2C_MasterTxReadyFlag); + } +} + +static void LPI2C_TransferStateMachineTransferData(LPI2C_Type *base, + lpi2c_master_handle_t *handle, + lpi2c_state_machine_param_t *stateParams) +{ + assert(stateParams != NULL); + + if (handle->transfer.direction == kLPI2C_Write) + { + /* Make sure there is room in the tx fifo. */ + if (0U == stateParams->txCount--) + { + stateParams->state_complete = true; + return; + } + + /* Put byte to send in fifo. */ + base->MTDR = *(handle->buf)++; + } + else + { + /* XXX handle receive sizes > 256, use kIssueReadCommandState */ + /* Make sure there is data in the rx fifo. */ + if (0U == stateParams->rxCount--) + { + stateParams->state_complete = true; + return; + } + + /* Read byte from fifo. */ + *(handle->buf)++ = (uint8_t)(base->MRDR & LPI2C_MRDR_DATA_MASK); + } + + /* Move to stop when the transfer is done. */ + if (--handle->remainingBytes == 0U) + { + if (handle->transfer.direction == kLPI2C_Write) + { + stateParams->state_complete = true; + } + handle->state = (uint8_t)kStopState; + } +} + +static void LPI2C_TransferStateMachineStopState(LPI2C_Type *base, + lpi2c_master_handle_t *handle, + lpi2c_state_machine_param_t *stateParams, + bool *isDone) +{ + assert(stateParams != NULL); + + /* Only issue a stop transition if the caller requested it. */ + if ((handle->transfer.flags & (uint32_t)kLPI2C_TransferNoStopFlag) == 0U) + { + /* Make sure there is room in the tx fifo for the stop command. */ + if (0U == (stateParams->txCount)--) + { + stateParams->state_complete = true; + return; + } + + base->MTDR = (uint32_t)kStopCmd; + } + else + { + /* If all data is read and no stop flag is required to send, we are done. */ + if (handle->transfer.direction == kLPI2C_Read) + { + *isDone = true; + } + stateParams->state_complete = true; + } + handle->state = (uint8_t)kWaitForCompletionState; +} + +static void LPI2C_TransferStateMachineWaitState(LPI2C_Type *base, + lpi2c_master_handle_t *handle, + lpi2c_state_machine_param_t *stateParams, + bool *isDone) +{ + assert(stateParams != NULL); + + if ((handle->transfer.flags & (uint32_t)kLPI2C_TransferNoStopFlag) == 0U) + { + /* We stay in this state until the stop state is detected. */ + if (0U != ((stateParams->status) & (uint32_t)kLPI2C_MasterStopDetectFlag)) + { + *isDone = true; + } + } + else + { + /* If all data is pushed to FIFO and no stop flag is required to send, we need to make sure they + are all send out to bus. */ + if ((handle->transfer.direction == kLPI2C_Write) && ((base->MFSR & LPI2C_MFSR_TXCOUNT_MASK) == 0U)) + { + /* We stay in this state until the data is sent out to bus. */ + *isDone = true; + } + } + stateParams->state_complete = true; +} + +/*! + * @brief Execute states until FIFOs are exhausted. + * @param handle Master nonblocking driver handle. + * @param[out] isDone Set to true if the transfer has completed. + * @retval #kStatus_Success + * @retval #kStatus_LPI2C_PinLowTimeout + * @retval #kStatus_LPI2C_ArbitrationLost + * @retval #kStatus_LPI2C_Nak + * @retval #kStatus_LPI2C_FifoError + */ +static status_t LPI2C_RunTransferStateMachine(LPI2C_Type *base, lpi2c_master_handle_t *handle, bool *isDone) +{ + assert(NULL != base && NULL != handle && NULL != isDone); + + status_t result = kStatus_Success; + lpi2c_state_machine_param_t stateParams; + (void)memset(&stateParams, 0, sizeof(stateParams)); + + stateParams.state_complete = false; + + /* Set default isDone return value. */ + *isDone = false; + + /* Check for errors. */ + stateParams.status = LPI2C_MasterGetStatusFlags(base); + + /* Get fifo counts. */ + LPI2C_MasterGetFifoCounts(base, &stateParams.rxCount, &stateParams.txCount); + + /* For the last byte, nack flag is expected. + Do not check and clear kLPI2C_MasterNackDetectFlag for the last byte, + in case FIFO is emptied when stop command has not been sent. */ + if (handle->remainingBytes == 0U) + { + /* When data size is not zero which means it is not only one byte of address is sent, and */ + /* when the txfifo is empty, or have one byte which is the stop command, then the nack status can be ignored. */ + if (((handle->transfer).dataSize != 0U) && + ((stateParams.txCount == 0U) || + (((stateParams.txCount) == 1U) && (handle->state == (uint8_t)kWaitForCompletionState) && + (((handle->transfer).flags & (uint32_t)kLPI2C_TransferNoStopFlag) == 0U)))) + { + (stateParams.status) &= ~(uint32_t)kLPI2C_MasterNackDetectFlag; + } + } + + result = LPI2C_MasterCheckAndClearError(base, stateParams.status); + + if (kStatus_Success == result) + { + /* Compute room in tx fifo */ + stateParams.txCount = (size_t)FSL_FEATURE_LPI2C_FIFO_SIZEn(base) - stateParams.txCount; + + while (!stateParams.state_complete) + { + /* Execute the state. */ + switch (handle->state) + { + case (uint8_t)kSendCommandState: + LPI2C_TransferStateMachineSendCommand(base, handle, &stateParams); + break; + + case (uint8_t)kIssueReadCommandState: + LPI2C_TransferStateMachineReadCommand(base, handle, &stateParams); + break; + + case (uint8_t)kTransferDataState: + LPI2C_TransferStateMachineTransferData(base, handle, &stateParams); + break; + + case (uint8_t)kStopState: + LPI2C_TransferStateMachineStopState(base, handle, &stateParams, isDone); + break; + + case (uint8_t)kWaitForCompletionState: + LPI2C_TransferStateMachineWaitState(base, handle, &stateParams, isDone); + break; + default: + assert(false); + break; + } + } + } + return result; +} + +/*! + * @brief Prepares the transfer state machine and fills in the command buffer. + * @param handle Master nonblocking driver handle. + */ +static void LPI2C_InitTransferStateMachine(lpi2c_master_handle_t *handle) +{ + lpi2c_master_transfer_t *xfer = &handle->transfer; + + /* Handle no start option. */ + if (0U != (xfer->flags & (uint32_t)kLPI2C_TransferNoStartFlag)) + { + if (xfer->direction == kLPI2C_Read) + { + /* Need to issue read command first. */ + handle->state = (uint8_t)kIssueReadCommandState; + } + else + { + /* Start immediately in the data transfer state. */ + handle->state = (uint8_t)kTransferDataState; + } + + handle->buf = (uint8_t *)xfer->data; + handle->remainingBytes = (uint16_t)xfer->dataSize; + } + else + { + uint16_t *cmd = (uint16_t *)&handle->commandBuffer; + uint32_t cmdCount = 0U; + + /* Initial direction depends on whether a subaddress was provided, and of course the actual */ + /* data transfer direction. */ + lpi2c_direction_t direction = (0U != xfer->subaddressSize) ? kLPI2C_Write : xfer->direction; + + /* Start command. */ + cmd[cmdCount++] = + (uint16_t)kStartCmd | (uint16_t)((uint16_t)((uint16_t)xfer->slaveAddress << 1U) | (uint16_t)direction); + + /* Subaddress, MSB first. */ + if (0U != xfer->subaddressSize) + { + uint32_t subaddressRemaining = xfer->subaddressSize; + while (0U != (subaddressRemaining--)) + { + uint8_t subaddressByte = (uint8_t)((xfer->subaddress >> (8U * subaddressRemaining)) & 0xffU); + cmd[cmdCount++] = subaddressByte; + } + } + + /* Reads need special handling. */ + if ((0U != xfer->dataSize) && (xfer->direction == kLPI2C_Read)) + { + /* Need to send repeated start if switching directions to read. */ + if (direction == kLPI2C_Write) + { + cmd[cmdCount++] = (uint16_t)kStartCmd | + (uint16_t)((uint16_t)((uint16_t)xfer->slaveAddress << 1U) | (uint16_t)kLPI2C_Read); + } + } + + /* Set up state machine for transferring the commands. */ + handle->state = (uint8_t)kSendCommandState; + handle->remainingBytes = (uint16_t)cmdCount; + handle->buf = (uint8_t *)&handle->commandBuffer; + } +} + +/*! + * brief Performs a non-blocking transaction on the I2C bus. + * + * param base The LPI2C peripheral base address. + * param handle Pointer to the LPI2C master driver handle. + * param transfer The pointer to the transfer descriptor. + * retval #kStatus_Success The transaction was started successfully. + * retval #kStatus_LPI2C_Busy Either another master is currently utilizing the bus, or a non-blocking + * transaction is already in progress. + */ +status_t LPI2C_MasterTransferNonBlocking(LPI2C_Type *base, + lpi2c_master_handle_t *handle, + lpi2c_master_transfer_t *transfer) +{ + assert(NULL != handle); + assert(NULL != transfer); + assert(transfer->subaddressSize <= sizeof(transfer->subaddress)); + + status_t result; + + /* Check transfer data size in read operation. */ + if ((transfer->direction == kLPI2C_Read) && + (transfer->dataSize > (256U * (uint32_t)FSL_FEATURE_LPI2C_FIFO_SIZEn(base)))) + { + return kStatus_InvalidArgument; + } + + /* Return busy if another transaction is in progress. */ + if (handle->state != (uint8_t)kIdleState) + { + result = kStatus_LPI2C_Busy; + } + else + { + result = LPI2C_CheckForBusyBus(base); + } + + if ((status_t)kStatus_Success == result) + { + /* Enable the master function and disable the slave function. */ + LPI2C_MasterEnable(base, true); + LPI2C_SlaveEnable(base, false); + + /* Disable LPI2C IRQ sources while we configure stuff. */ + LPI2C_MasterDisableInterrupts(base, (uint32_t)kLPI2C_MasterIrqFlags); + + /* Reset FIFO in case there are data. */ + base->MCR |= LPI2C_MCR_RRF_MASK | LPI2C_MCR_RTF_MASK; + + /* Save transfer into handle. */ + handle->transfer = *transfer; + + /* Generate commands to send. */ + LPI2C_InitTransferStateMachine(handle); + + /* Clear all flags. */ + LPI2C_MasterClearStatusFlags(base, (uint32_t)kLPI2C_MasterClearFlags); + + /* Turn off auto-stop option. */ + base->MCFGR1 &= ~LPI2C_MCFGR1_AUTOSTOP_MASK; + + /* Enable LPI2C internal IRQ sources. NVIC IRQ was enabled in CreateHandle() */ + LPI2C_MasterEnableInterrupts(base, (uint32_t)kLPI2C_MasterIrqFlags); + } + + return result; +} + +/*! + * brief Returns number of bytes transferred so far. + * param base The LPI2C peripheral base address. + * param handle Pointer to the LPI2C master driver handle. + * param[out] count Number of bytes transferred so far by the non-blocking transaction. + * retval #kStatus_Success + * retval #kStatus_NoTransferInProgress There is not a non-blocking transaction currently in progress. + */ +status_t LPI2C_MasterTransferGetCount(LPI2C_Type *base, lpi2c_master_handle_t *handle, size_t *count) +{ + status_t result = kStatus_Success; + + assert(NULL != handle); + + if (NULL == count) + { + result = kStatus_InvalidArgument; + } + + /* Catch when there is not an active transfer. */ + else if (handle->state == (uint8_t)kIdleState) + { + *count = 0; + result = kStatus_NoTransferInProgress; + } + else + { + uint8_t state; + uint16_t remainingBytes; + uint32_t dataSize; + + /* Cache some fields with IRQs disabled. This ensures all field values */ + /* are synchronized with each other during an ongoing transfer. */ + uint32_t irqs = LPI2C_MasterGetEnabledInterrupts(base); + LPI2C_MasterDisableInterrupts(base, irqs); + state = handle->state; + remainingBytes = handle->remainingBytes; + dataSize = handle->transfer.dataSize; + LPI2C_MasterEnableInterrupts(base, irqs); + + /* Get transfer count based on current transfer state. */ + switch (state) + { + case (uint8_t)kIdleState: + case (uint8_t)kSendCommandState: + case (uint8_t) + kIssueReadCommandState: /* XXX return correct value for this state when >256 reads are supported */ + *count = 0; + break; + + case (uint8_t)kTransferDataState: + *count = dataSize - remainingBytes; + break; + + case (uint8_t)kStopState: + case (uint8_t)kWaitForCompletionState: + default: + *count = dataSize; + break; + } + } + + return result; +} + +/*! + * brief Terminates a non-blocking LPI2C master transmission early. + * + * note It is not safe to call this function from an IRQ handler that has a higher priority than the + * LPI2C peripheral's IRQ priority. + * + * param base The LPI2C peripheral base address. + * param handle Pointer to the LPI2C master driver handle. + * retval #kStatus_Success A transaction was successfully aborted. + * retval #kStatus_LPI2C_Idle There is not a non-blocking transaction currently in progress. + */ +void LPI2C_MasterTransferAbort(LPI2C_Type *base, lpi2c_master_handle_t *handle) +{ + if (handle->state != (uint8_t)kIdleState) + { + /* Disable internal IRQ enables. */ + LPI2C_MasterDisableInterrupts(base, (uint32_t)kLPI2C_MasterIrqFlags); + + /* Reset fifos. */ + base->MCR |= LPI2C_MCR_RRF_MASK | LPI2C_MCR_RTF_MASK; + + /* If master is still busy and has not send out stop signal yet. */ + if ((LPI2C_MasterGetStatusFlags(base) & ((uint32_t)kLPI2C_MasterStopDetectFlag | + (uint32_t)kLPI2C_MasterBusyFlag)) == (uint32_t)kLPI2C_MasterBusyFlag) + { + /* Send a stop command to finalize the transfer. */ + base->MTDR = (uint32_t)kStopCmd; + } + + /* Reset handle. */ + handle->state = (uint8_t)kIdleState; + } +} + +/*! + * brief Reusable routine to handle master interrupts. + * note This function does not need to be called unless you are reimplementing the + * nonblocking API's interrupt handler routines to add special functionality. + * param base The LPI2C peripheral base address. + * param lpi2cMasterHandle Pointer to the LPI2C master driver handle. + */ +void LPI2C_MasterTransferHandleIRQ(LPI2C_Type *base, void *lpi2cMasterHandle) +{ + assert(lpi2cMasterHandle != NULL); + + lpi2c_master_handle_t *handle = (lpi2c_master_handle_t *)lpi2cMasterHandle; + bool isDone = false; + status_t result; + + /* Don't do anything if we don't have a valid handle. */ + if (NULL != handle) + { + if (handle->state != (uint8_t)kIdleState) + { + result = LPI2C_RunTransferStateMachine(base, handle, &isDone); + + if ((result != kStatus_Success) || isDone) + { + /* Handle error, terminate xfer */ + if (result != kStatus_Success) + { + LPI2C_MasterTransferAbort(base, handle); + } + + /* Disable internal IRQ enables. */ + LPI2C_MasterDisableInterrupts(base, (uint32_t)kLPI2C_MasterIrqFlags); + + /* Set handle to idle state. */ + handle->state = (uint8_t)kIdleState; + + /* Invoke callback. */ + if (NULL != handle->completionCallback) + { + handle->completionCallback(base, handle, result, handle->userData); + } + } + } + } +} + +/*! + * brief Provides a default configuration for the LPI2C slave peripheral. + * + * This function provides the following default configuration for the LPI2C slave peripheral: + * code + * slaveConfig->enableSlave = true; + * slaveConfig->address0 = 0U; + * slaveConfig->address1 = 0U; + * slaveConfig->addressMatchMode = kLPI2C_MatchAddress0; + * slaveConfig->filterDozeEnable = true; + * slaveConfig->filterEnable = true; + * slaveConfig->enableGeneralCall = false; + * slaveConfig->sclStall.enableAck = false; + * slaveConfig->sclStall.enableTx = true; + * slaveConfig->sclStall.enableRx = true; + * slaveConfig->sclStall.enableAddress = true; + * slaveConfig->ignoreAck = false; + * slaveConfig->enableReceivedAddressRead = false; + * slaveConfig->sdaGlitchFilterWidth_ns = 0; + * slaveConfig->sclGlitchFilterWidth_ns = 0; + * slaveConfig->dataValidDelay_ns = 0; + * slaveConfig->clockHoldTime_ns = 0; + * endcode + * + * After calling this function, override any settings to customize the configuration, + * prior to initializing the master driver with LPI2C_SlaveInit(). Be sure to override at least the a + * address0 member of the configuration structure with the desired slave address. + * + * param[out] slaveConfig User provided configuration structure that is set to default values. Refer to + * #lpi2c_slave_config_t. + */ +void LPI2C_SlaveGetDefaultConfig(lpi2c_slave_config_t *slaveConfig) +{ + /* Initializes the configure structure to zero. */ + (void)memset(slaveConfig, 0, sizeof(*slaveConfig)); + + slaveConfig->enableSlave = true; + slaveConfig->address0 = 0U; + slaveConfig->address1 = 0U; + slaveConfig->addressMatchMode = kLPI2C_MatchAddress0; + slaveConfig->filterDozeEnable = true; + slaveConfig->filterEnable = true; + slaveConfig->enableGeneralCall = false; + slaveConfig->sclStall.enableAck = false; + slaveConfig->sclStall.enableTx = true; + slaveConfig->sclStall.enableRx = true; + slaveConfig->sclStall.enableAddress = false; + slaveConfig->ignoreAck = false; + slaveConfig->enableReceivedAddressRead = false; + slaveConfig->sdaGlitchFilterWidth_ns = 0U; /* Set to 0 to disable the function */ + slaveConfig->sclGlitchFilterWidth_ns = 0U; /* Set to 0 to disable the function */ + slaveConfig->dataValidDelay_ns = 0U; + /* When enabling the slave tx SCL stall, set the default clock hold time to 250ns according + to I2C spec for standard mode baudrate(100k). User can manually change it to 100ns or 50ns + for fast-mode(400k) or fast-mode+(1m). */ + slaveConfig->clockHoldTime_ns = 250U; +} + +/*! + * brief Initializes the LPI2C slave peripheral. + * + * This function enables the peripheral clock and initializes the LPI2C slave peripheral as described by the user + * provided configuration. + * + * param base The LPI2C peripheral base address. + * param slaveConfig User provided peripheral configuration. Use LPI2C_SlaveGetDefaultConfig() to get a set of defaults + * that you can override. + * param sourceClock_Hz Frequency in Hertz of the LPI2C functional clock. Used to calculate the filter widths, + * data valid delay, and clock hold time. + */ +void LPI2C_SlaveInit(LPI2C_Type *base, const lpi2c_slave_config_t *slaveConfig, uint32_t sourceClock_Hz) +{ + uint32_t tmpReg; + uint32_t tmpCycle; + +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) + + uint32_t instance = LPI2C_GetInstance(base); + + /* Ungate the clock. */ + (void)CLOCK_EnableClock(kLpi2cClocks[instance]); +#if defined(LPI2C_PERIPH_CLOCKS) + /* Ungate the functional clock in initialize function. */ + CLOCK_EnableClock(kLpi2cPeriphClocks[instance]); +#endif + +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ + + /* Restore to reset conditions. */ + LPI2C_SlaveReset(base); + + /* Configure peripheral. */ + base->SAMR = LPI2C_SAMR_ADDR0(slaveConfig->address0) | LPI2C_SAMR_ADDR1(slaveConfig->address1); + + base->SCFGR1 = + LPI2C_SCFGR1_ADDRCFG(slaveConfig->addressMatchMode) | LPI2C_SCFGR1_IGNACK(slaveConfig->ignoreAck) | + LPI2C_SCFGR1_RXCFG(slaveConfig->enableReceivedAddressRead) | LPI2C_SCFGR1_GCEN(slaveConfig->enableGeneralCall) | + LPI2C_SCFGR1_ACKSTALL(slaveConfig->sclStall.enableAck) | LPI2C_SCFGR1_TXDSTALL(slaveConfig->sclStall.enableTx) | + LPI2C_SCFGR1_RXSTALL(slaveConfig->sclStall.enableRx) | + LPI2C_SCFGR1_ADRSTALL(slaveConfig->sclStall.enableAddress); + + /* Calculate SDA filter width. The width is equal to FILTSDA+3 cycles of functional clock. + And set FILTSDA to 0 disables the fileter, so the min value is 4. */ + tmpReg = LPI2C_SCFGR2_FILTSDA( + LPI2C_GetCyclesForWidth(sourceClock_Hz, slaveConfig->sdaGlitchFilterWidth_ns, 4U, + (LPI2C_SCFGR2_FILTSDA_MASK >> LPI2C_SCFGR2_FILTSDA_SHIFT) + 3U, 0U) - + 3U); + + /* Calculate SDL filter width. The width is equal to FILTSCL+3 cycles of functional clock. + And set FILTSCL to 0 disables the fileter, so the min value is 4. */ + tmpCycle = LPI2C_GetCyclesForWidth(sourceClock_Hz, slaveConfig->sclGlitchFilterWidth_ns, 4U, + (LPI2C_SCFGR2_FILTSCL_MASK >> LPI2C_SCFGR2_FILTSCL_SHIFT) + 3U, 0U); + tmpReg |= LPI2C_SCFGR2_FILTSCL(tmpCycle - 3U); + + /* Calculate data valid time. The time is equal to FILTSCL+DATAVD+3 cycles of functional clock. + So the min value is FILTSCL+3. */ + tmpReg |= LPI2C_SCFGR2_DATAVD( + LPI2C_GetCyclesForWidth(sourceClock_Hz, slaveConfig->dataValidDelay_ns, tmpCycle, + tmpCycle + (LPI2C_SCFGR2_DATAVD_MASK >> LPI2C_SCFGR2_DATAVD_SHIFT), 0U) - + tmpCycle); + + /* Calculate clock hold time. The time is equal to CLKHOLD+3 cycles of functional clock. + So the min value is 3. */ + base->SCFGR2 = + tmpReg | LPI2C_SCFGR2_CLKHOLD( + LPI2C_GetCyclesForWidth(sourceClock_Hz, slaveConfig->clockHoldTime_ns, 3U, + (LPI2C_SCFGR2_CLKHOLD_MASK >> LPI2C_SCFGR2_CLKHOLD_SHIFT) + 3U, 0U) - + 3U); + + /* Save SCR to last so we don't enable slave until it is configured */ + base->SCR = LPI2C_SCR_FILTDZ(!slaveConfig->filterDozeEnable) | LPI2C_SCR_FILTEN(slaveConfig->filterEnable) | + LPI2C_SCR_SEN(slaveConfig->enableSlave); +} + +/*! + * brief Deinitializes the LPI2C slave peripheral. + * + * This function disables the LPI2C slave peripheral and gates the clock. It also performs a software + * reset to restore the peripheral to reset conditions. + * + * param base The LPI2C peripheral base address. + */ +void LPI2C_SlaveDeinit(LPI2C_Type *base) +{ + LPI2C_SlaveReset(base); + +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) + + uint32_t instance = LPI2C_GetInstance(base); + + /* Gate the clock. */ + (void)CLOCK_DisableClock(kLpi2cClocks[instance]); + +#if defined(LPI2C_PERIPH_CLOCKS) + /* Gate the functional clock. */ + CLOCK_DisableClock(kLpi2cPeriphClocks[instance]); +#endif + +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ +} + +/*! + * @brief Convert provided flags to status code, and clear any errors if present. + * @param base The LPI2C peripheral base address. + * @param status Current status flags value that will be checked. + * @retval #kStatus_Success + * @retval #kStatus_LPI2C_BitError + * @retval #kStatus_LPI2C_FifoError + */ +static status_t LPI2C_SlaveCheckAndClearError(LPI2C_Type *base, uint32_t flags) +{ + status_t result = kStatus_Success; + + flags &= (uint32_t)kLPI2C_SlaveErrorFlags; + if (0U != flags) + { + if (0U != (flags & (uint32_t)kLPI2C_SlaveBitErrFlag)) + { + result = kStatus_LPI2C_BitError; + } + else if (0U != (flags & (uint32_t)kLPI2C_SlaveFifoErrFlag)) + { + result = kStatus_LPI2C_FifoError; + } + else + { + ; /* Intentional empty */ + } + + /* Clear the errors. */ + LPI2C_SlaveClearStatusFlags(base, flags); + } + else + { + ; /* Intentional empty */ + } + + return result; +} + +/*! + * brief Performs a polling send transfer on the I2C bus. + * + * param base The LPI2C peripheral base address. + * param txBuff The pointer to the data to be transferred. + * param txSize The length in bytes of the data to be transferred. + * param[out] actualTxSize + * return Error or success status returned by API. + */ +status_t LPI2C_SlaveSend(LPI2C_Type *base, void *txBuff, size_t txSize, size_t *actualTxSize) +{ + status_t result = kStatus_Success; + uint8_t *buf = (uint8_t *)txBuff; + size_t remaining = txSize; + + assert(NULL != txBuff); + +#if I2C_RETRY_TIMES != 0U + uint32_t waitTimes = I2C_RETRY_TIMES; +#endif + + /* Clear stop flag. */ + LPI2C_SlaveClearStatusFlags(base, + (uint32_t)kLPI2C_SlaveStopDetectFlag | (uint32_t)kLPI2C_SlaveRepeatedStartDetectFlag); + + while (0U != remaining) + { + uint32_t flags; + + /* Wait until we can transmit. */ + do + { + /* Check for errors */ + flags = LPI2C_SlaveGetStatusFlags(base); + result = LPI2C_SlaveCheckAndClearError(base, flags); + if (kStatus_Success != result) + { + if (NULL != actualTxSize) + { + *actualTxSize = txSize - remaining; + } + break; + } +#if I2C_RETRY_TIMES != 0U + waitTimes--; + } while ((0U == (flags & ((uint32_t)kLPI2C_SlaveTxReadyFlag | (uint32_t)kLPI2C_SlaveStopDetectFlag | + (uint32_t)kLPI2C_SlaveRepeatedStartDetectFlag))) && + (0U != waitTimes)); + if (0U == waitTimes) + { + result = kStatus_LPI2C_Timeout; + } +#else + } while (0U == (flags & ((uint32_t)kLPI2C_SlaveTxReadyFlag | (uint32_t)kLPI2C_SlaveStopDetectFlag | + (uint32_t)kLPI2C_SlaveRepeatedStartDetectFlag))); +#endif + + if (kStatus_Success != result) + { + break; + } + + /* Send a byte. */ + if (0U != (flags & (uint32_t)kLPI2C_SlaveTxReadyFlag)) + { + base->STDR = *buf++; + --remaining; + } + + /* Exit loop if we see a stop or restart in transfer*/ + if ((0U != (flags & ((uint32_t)kLPI2C_SlaveStopDetectFlag | (uint32_t)kLPI2C_SlaveRepeatedStartDetectFlag))) && + (remaining != 0U)) + { + LPI2C_SlaveClearStatusFlags( + base, (uint32_t)kLPI2C_SlaveStopDetectFlag | (uint32_t)kLPI2C_SlaveRepeatedStartDetectFlag); + break; + } + } + + if (NULL != actualTxSize) + { + *actualTxSize = txSize - remaining; + } + + return result; +} + +/*! + * brief Performs a polling receive transfer on the I2C bus. + * + * param base The LPI2C peripheral base address. + * param rxBuff The pointer to the data to be transferred. + * param rxSize The length in bytes of the data to be transferred. + * param[out] actualRxSize + * return Error or success status returned by API. + */ +status_t LPI2C_SlaveReceive(LPI2C_Type *base, void *rxBuff, size_t rxSize, size_t *actualRxSize) +{ + status_t result = kStatus_Success; + uint8_t *buf = (uint8_t *)rxBuff; + size_t remaining = rxSize; + + assert(NULL != rxBuff); + +#if I2C_RETRY_TIMES != 0U + uint32_t waitTimes = I2C_RETRY_TIMES; +#endif + + /* Clear stop flag. */ + LPI2C_SlaveClearStatusFlags(base, + (uint32_t)kLPI2C_SlaveStopDetectFlag | (uint32_t)kLPI2C_SlaveRepeatedStartDetectFlag); + + while (0U != remaining) + { + uint32_t flags; + + /* Wait until we can receive. */ + do + { + /* Check for errors */ + flags = LPI2C_SlaveGetStatusFlags(base); + result = LPI2C_SlaveCheckAndClearError(base, flags); + if (kStatus_Success != result) + { + if (NULL != actualRxSize) + { + *actualRxSize = rxSize - remaining; + } + break; + } +#if I2C_RETRY_TIMES != 0U + waitTimes--; + } while ((0U == (flags & ((uint32_t)kLPI2C_SlaveRxReadyFlag | (uint32_t)kLPI2C_SlaveStopDetectFlag | + (uint32_t)kLPI2C_SlaveRepeatedStartDetectFlag))) && + (0U != waitTimes)); + if (0U == waitTimes) + { + result = kStatus_LPI2C_Timeout; + } +#else + } while (0U == (flags & ((uint32_t)kLPI2C_SlaveRxReadyFlag | (uint32_t)kLPI2C_SlaveStopDetectFlag | + (uint32_t)kLPI2C_SlaveRepeatedStartDetectFlag))); +#endif + + if ((status_t)kStatus_Success != result) + { + break; + } + + /* Receive a byte. */ + if (0U != (flags & (uint32_t)kLPI2C_SlaveRxReadyFlag)) + { + *buf++ = (uint8_t)(base->SRDR & LPI2C_SRDR_DATA_MASK); + --remaining; + } + + /* Exit loop if we see a stop or restart */ + if ((0U != (flags & ((uint32_t)kLPI2C_SlaveStopDetectFlag | (uint32_t)kLPI2C_SlaveRepeatedStartDetectFlag))) && + (remaining != 0U)) + { + LPI2C_SlaveClearStatusFlags( + base, (uint32_t)kLPI2C_SlaveStopDetectFlag | (uint32_t)kLPI2C_SlaveRepeatedStartDetectFlag); + break; + } + } + + if (NULL != actualRxSize) + { + *actualRxSize = rxSize - remaining; + } + + return result; +} + +/*! + * brief Creates a new handle for the LPI2C slave non-blocking APIs. + * + * The creation of a handle is for use with the non-blocking APIs. Once a handle + * is created, there is not a corresponding destroy handle. If the user wants to + * terminate a transfer, the LPI2C_SlaveTransferAbort() API shall be called. + * + * note The function also enables the NVIC IRQ for the input LPI2C. Need to notice + * that on some SoCs the LPI2C IRQ is connected to INTMUX, in this case user needs to + * enable the associated INTMUX IRQ in application. + + * param base The LPI2C peripheral base address. + * param[out] handle Pointer to the LPI2C slave driver handle. + * param callback User provided pointer to the asynchronous callback function. + * param userData User provided pointer to the application callback data. + */ +void LPI2C_SlaveTransferCreateHandle(LPI2C_Type *base, + lpi2c_slave_handle_t *handle, + lpi2c_slave_transfer_callback_t callback, + void *userData) +{ + uint32_t instance; + + assert(NULL != handle); + + /* Clear out the handle. */ + (void)memset(handle, 0, sizeof(*handle)); + + /* Look up instance number */ + instance = LPI2C_GetInstance(base); + + /* Save base and instance. */ + handle->callback = callback; + handle->userData = userData; + + /* Save this handle for IRQ use. */ + s_lpi2cSlaveHandle[instance] = handle; + + /* Set irq handler. */ + s_lpi2cSlaveIsr = LPI2C_SlaveTransferHandleIRQ; + + /* Clear internal IRQ enables and enable NVIC IRQ. */ + LPI2C_SlaveDisableInterrupts(base, (uint32_t)kLPI2C_SlaveIrqFlags); + (void)EnableIRQ(kLpi2cIrqs[instance]); + + /* Nack by default. */ + base->STAR = LPI2C_STAR_TXNACK_MASK; +} + +/*! + * brief Starts accepting slave transfers. + * + * Call this API after calling I2C_SlaveInit() and LPI2C_SlaveTransferCreateHandle() to start processing + * transactions driven by an I2C master. The slave monitors the I2C bus and pass events to the + * callback that was passed into the call to LPI2C_SlaveTransferCreateHandle(). The callback is always invoked + * from the interrupt context. + * + * The set of events received by the callback is customizable. To do so, set the a eventMask parameter to + * the OR'd combination of #lpi2c_slave_transfer_event_t enumerators for the events you wish to receive. + * The #kLPI2C_SlaveTransmitEvent and #kLPI2C_SlaveReceiveEvent events are always enabled and do not need + * to be included in the mask. Alternatively, you can pass 0 to get a default set of only the transmit and + * receive events that are always enabled. In addition, the #kLPI2C_SlaveAllEvents constant is provided as + * a convenient way to enable all events. + * + * param base The LPI2C peripheral base address. + * param handle Pointer to #lpi2c_slave_handle_t structure which stores the transfer state. + * param eventMask Bit mask formed by OR'ing together #lpi2c_slave_transfer_event_t enumerators to specify + * which events to send to the callback. Other accepted values are 0 to get a default set of + * only the transmit and receive events, and #kLPI2C_SlaveAllEvents to enable all events. + * + * retval #kStatus_Success Slave transfers were successfully started. + * retval #kStatus_LPI2C_Busy Slave transfers have already been started on this handle. + */ +status_t LPI2C_SlaveTransferNonBlocking(LPI2C_Type *base, lpi2c_slave_handle_t *handle, uint32_t eventMask) +{ + status_t result = kStatus_Success; + + assert(NULL != handle); + + /* Return busy if another transaction is in progress. */ + if (handle->isBusy) + { + result = kStatus_LPI2C_Busy; + } + else + { + /* Enable the slave function and disable the master function. */ + LPI2C_MasterEnable(base, false); + LPI2C_SlaveEnable(base, true); + /* Return an error if the bus is already in use not by us. */ + uint32_t status = LPI2C_SlaveGetStatusFlags(base); + if ((0U != (status & (uint32_t)kLPI2C_SlaveBusBusyFlag)) && (0U == (status & (uint32_t)kLPI2C_SlaveBusyFlag))) + { + result = kStatus_LPI2C_Busy; + } + } + + if ((status_t)kStatus_Success == result) + { + /* Disable LPI2C IRQ sources while we configure stuff. */ + LPI2C_SlaveDisableInterrupts(base, (uint32_t)kLPI2C_SlaveIrqFlags); + + /* Clear transfer in handle. */ + (void)memset(&handle->transfer, 0, sizeof(handle->transfer)); + + /* Record that we're busy. */ + handle->isBusy = true; + + /* Set up event mask. tx and rx are always enabled. */ + handle->eventMask = eventMask | (uint32_t)kLPI2C_SlaveTransmitEvent | (uint32_t)kLPI2C_SlaveReceiveEvent; + + /* Ack by default. */ + base->STAR = 0U; + + /* Clear all flags. */ + LPI2C_SlaveClearStatusFlags(base, (uint32_t)kLPI2C_SlaveClearFlags); + + /* Enable LPI2C internal IRQ sources. NVIC IRQ was enabled in CreateHandle() */ + LPI2C_SlaveEnableInterrupts(base, (uint32_t)kLPI2C_SlaveIrqFlags); + } + + return result; +} + +/*! + * brief Gets the slave transfer status during a non-blocking transfer. + * param base The LPI2C peripheral base address. + * param handle Pointer to i2c_slave_handle_t structure. + * param[out] count Pointer to a value to hold the number of bytes transferred. May be NULL if the count is not + * required. + * retval #kStatus_Success + * retval #kStatus_NoTransferInProgress + */ +status_t LPI2C_SlaveTransferGetCount(LPI2C_Type *base, lpi2c_slave_handle_t *handle, size_t *count) +{ + status_t status = kStatus_Success; + + assert(NULL != handle); + + if (count == NULL) + { + status = kStatus_InvalidArgument; + } + + /* Catch when there is not an active transfer. */ + else if (!handle->isBusy) + { + *count = 0; + status = kStatus_NoTransferInProgress; + } + + /* For an active transfer, just return the count from the handle. */ + else + { + *count = handle->transferredCount; + } + + return status; +} + +/*! + * brief Aborts the slave non-blocking transfers. + * note This API could be called at any time to stop slave for handling the bus events. + * param base The LPI2C peripheral base address. + * param handle Pointer to #lpi2c_slave_handle_t structure which stores the transfer state. + * retval #kStatus_Success + * retval #kStatus_LPI2C_Idle + */ +void LPI2C_SlaveTransferAbort(LPI2C_Type *base, lpi2c_slave_handle_t *handle) +{ + assert(NULL != handle); + + /* Return idle if no transaction is in progress. */ + if (handle->isBusy) + { + /* Disable LPI2C IRQ sources. */ + LPI2C_SlaveDisableInterrupts(base, (uint32_t)kLPI2C_SlaveIrqFlags); + + /* Nack by default. */ + base->STAR = LPI2C_STAR_TXNACK_MASK; + + /* Reset transfer info. */ + (void)memset(&handle->transfer, 0, sizeof(handle->transfer)); + + /* We're no longer busy. */ + handle->isBusy = false; + } +} + +/*! + * brief Reusable routine to handle slave interrupts. + * note This function does not need to be called unless you are reimplementing the + * non blocking API's interrupt handler routines to add special functionality. + * param base The LPI2C peripheral base address. + * param handle Pointer to #lpi2c_slave_handle_t structure which stores the transfer state. + */ +void LPI2C_SlaveTransferHandleIRQ(LPI2C_Type *base, lpi2c_slave_handle_t *handle) +{ + uint32_t flags; + lpi2c_slave_transfer_t *xfer; + + /* Check for a valid handle in case of a spurious interrupt. */ + if (NULL != handle) + { + xfer = &handle->transfer; + + /* Get status flags. */ + flags = LPI2C_SlaveGetStatusFlags(base); + + if (0U != (flags & ((uint32_t)kLPI2C_SlaveBitErrFlag | (uint32_t)kLPI2C_SlaveFifoErrFlag))) + { + xfer->event = kLPI2C_SlaveCompletionEvent; + xfer->completionStatus = LPI2C_SlaveCheckAndClearError(base, flags); + + if ((0U != (handle->eventMask & (uint32_t)kLPI2C_SlaveCompletionEvent)) && (NULL != handle->callback)) + { + handle->callback(base, xfer, handle->userData); + } + } + else + { + if (0U != + (flags & (((uint32_t)kLPI2C_SlaveRepeatedStartDetectFlag) | ((uint32_t)kLPI2C_SlaveStopDetectFlag)))) + { + xfer->event = (0U != (flags & (uint32_t)kLPI2C_SlaveRepeatedStartDetectFlag)) ? + kLPI2C_SlaveRepeatedStartEvent : + kLPI2C_SlaveCompletionEvent; + xfer->receivedAddress = 0U; + xfer->completionStatus = kStatus_Success; + xfer->transferredCount = handle->transferredCount; + + if (xfer->event == kLPI2C_SlaveCompletionEvent) + { + handle->isBusy = false; + } + + if (handle->wasTransmit) + { + /* Subtract one from the transmit count to offset the fact that LPI2C asserts the */ + /* tx flag before it sees the nack from the master-receiver, thus causing one more */ + /* count that the master actually receives. */ + --xfer->transferredCount; + handle->wasTransmit = false; + } + + /* Clear the flag. */ + LPI2C_SlaveClearStatusFlags(base, flags & ((uint32_t)kLPI2C_SlaveRepeatedStartDetectFlag | + (uint32_t)kLPI2C_SlaveStopDetectFlag)); + + /* Revert to sending an Ack by default, in case we sent a Nack for receive. */ + base->STAR = 0U; + + if ((0U != (handle->eventMask & (uint32_t)xfer->event)) && (NULL != handle->callback)) + { + handle->callback(base, xfer, handle->userData); + } + + if (0U != (flags & (uint32_t)kLPI2C_SlaveStopDetectFlag)) + { + /* Clean up transfer info on completion, after the callback has been invoked. */ + (void)memset(&handle->transfer, 0, sizeof(handle->transfer)); + } + } + if (0U != (flags & (uint32_t)kLPI2C_SlaveAddressValidFlag)) + { + xfer->event = kLPI2C_SlaveAddressMatchEvent; + xfer->receivedAddress = (uint8_t)(base->SASR & LPI2C_SASR_RADDR_MASK); + + /* Update handle status to busy because slave is addressed. */ + handle->isBusy = true; + if ((0U != (handle->eventMask & (uint32_t)kLPI2C_SlaveAddressMatchEvent)) && (NULL != handle->callback)) + { + handle->callback(base, xfer, handle->userData); + } + } + if (0U != (flags & (uint32_t)kLPI2C_SlaveTransmitAckFlag)) + { + xfer->event = kLPI2C_SlaveTransmitAckEvent; + + if ((0U != (handle->eventMask & (uint32_t)kLPI2C_SlaveTransmitAckEvent)) && (NULL != handle->callback)) + { + handle->callback(base, xfer, handle->userData); + } + } + + /* Handle transmit and receive. */ + if (0U != (flags & (uint32_t)kLPI2C_SlaveTxReadyFlag)) + { + handle->wasTransmit = true; + + /* If we're out of data, invoke callback to get more. */ + if ((NULL == xfer->data) || (0U == xfer->dataSize)) + { + xfer->event = kLPI2C_SlaveTransmitEvent; + if (NULL != handle->callback) + { + handle->callback(base, xfer, handle->userData); + } + + /* Clear the transferred count now that we have a new buffer. */ + handle->transferredCount = 0U; + } + + /* Transmit a byte. */ + if ((NULL != xfer->data) && (0U != xfer->dataSize)) + { + base->STDR = *xfer->data++; + --xfer->dataSize; + ++handle->transferredCount; + } + } + if (0U != (flags & (uint32_t)kLPI2C_SlaveRxReadyFlag)) + { + /* If we're out of room in the buffer, invoke callback to get another. */ + if ((NULL == xfer->data) || (0U == xfer->dataSize)) + { + xfer->event = kLPI2C_SlaveReceiveEvent; + if (NULL != handle->callback) + { + handle->callback(base, xfer, handle->userData); + } + + /* Clear the transferred count now that we have a new buffer. */ + handle->transferredCount = 0U; + } + + /* Receive a byte. */ + if ((NULL != xfer->data) && (0U != xfer->dataSize)) + { + *xfer->data++ = (uint8_t)base->SRDR; + --xfer->dataSize; + ++handle->transferredCount; + } + else + { + /* We don't have any room to receive more data, so send a nack. */ + base->STAR = LPI2C_STAR_TXNACK_MASK; + } + } + } + } +} + +#if !(defined(FSL_FEATURE_I2C_HAS_NO_IRQ) && FSL_FEATURE_I2C_HAS_NO_IRQ) +/*! + * @brief Shared IRQ handler that can call both master and slave ISRs. + * + * The master and slave ISRs are called through function pointers in order to decouple + * this code from the ISR functions. Without this, the linker would always pull in both + * ISRs and every function they call, even if only the functional API was used. + * + * @param base The LPI2C peripheral base address. + * @param instance The LPI2C peripheral instance number. + */ +static void LPI2C_CommonIRQHandler(LPI2C_Type *base, uint32_t instance) +{ + /* Check for master IRQ. */ + if ((0U != (base->MCR & LPI2C_MCR_MEN_MASK)) && (NULL != s_lpi2cMasterIsr)) + { + /* Master mode. */ + s_lpi2cMasterIsr(base, s_lpi2cMasterHandle[instance]); + } + + /* Check for slave IRQ. */ + if ((0U != (base->SCR & LPI2C_SCR_SEN_MASK)) && (NULL != s_lpi2cSlaveIsr)) + { + /* Slave mode. */ + s_lpi2cSlaveIsr(base, s_lpi2cSlaveHandle[instance]); + } + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(LPI2C0) +/* Implementation of LPI2C0 handler named in startup code. */ +void LPI2C0_DriverIRQHandler(void); +void LPI2C0_DriverIRQHandler(void) +{ + LPI2C_CommonIRQHandler(LPI2C0, 0U); +} +#endif + +#if defined(LPI2C1) +/* Implementation of LPI2C1 handler named in startup code. */ +void LPI2C1_DriverIRQHandler(void); +void LPI2C1_DriverIRQHandler(void) +{ + LPI2C_CommonIRQHandler(LPI2C1, 1U); +} +#endif + +#if defined(LPI2C2) +/* Implementation of LPI2C2 handler named in startup code. */ +void LPI2C2_DriverIRQHandler(void); +void LPI2C2_DriverIRQHandler(void) +{ + LPI2C_CommonIRQHandler(LPI2C2, 2U); +} +#endif + +#if defined(LPI2C3) +/* Implementation of LPI2C3 handler named in startup code. */ +void LPI2C3_DriverIRQHandler(void); +void LPI2C3_DriverIRQHandler(void) +{ + LPI2C_CommonIRQHandler(LPI2C3, 3U); +} +#endif + +#if defined(LPI2C4) +/* Implementation of LPI2C4 handler named in startup code. */ +void LPI2C4_DriverIRQHandler(void); +void LPI2C4_DriverIRQHandler(void) +{ + LPI2C_CommonIRQHandler(LPI2C4, 4U); +} +#endif + +#if defined(LPI2C5) +/* Implementation of LPI2C5 handler named in startup code. */ +void LPI2C5_DriverIRQHandler(void); +void LPI2C5_DriverIRQHandler(void) +{ + LPI2C_CommonIRQHandler(LPI2C5, 5U); +} +#endif + +#if defined(LPI2C6) +/* Implementation of LPI2C6 handler named in startup code. */ +void LPI2C6_DriverIRQHandler(void); +void LPI2C6_DriverIRQHandler(void) +{ + LPI2C_CommonIRQHandler(LPI2C6, 6U); +} +#endif + +#if defined(CM4_0__LPI2C) +/* Implementation of CM4_0__LPI2C handler named in startup code. */ +void M4_0_LPI2C_DriverIRQHandler(void); +void M4_0_LPI2C_DriverIRQHandler(void) +{ + LPI2C_CommonIRQHandler(CM4_0__LPI2C, LPI2C_GetInstance(CM4_0__LPI2C)); +} +#endif + +#if defined(CM4__LPI2C) +/* Implementation of CM4__LPI2C handler named in startup code. */ +void M4_LPI2C_DriverIRQHandler(void); +void M4_LPI2C_DriverIRQHandler(void) +{ + LPI2C_CommonIRQHandler(CM4__LPI2C, LPI2C_GetInstance(CM4__LPI2C)); +} +#endif + +#if defined(CM4_1__LPI2C) +/* Implementation of CM4_1__LPI2C handler named in startup code. */ +void M4_1_LPI2C_DriverIRQHandler(void); +void M4_1_LPI2C_DriverIRQHandler(void) +{ + LPI2C_CommonIRQHandler(CM4_1__LPI2C, LPI2C_GetInstance(CM4_1__LPI2C)); +} +#endif + +#if defined(DMA__LPI2C0) +/* Implementation of DMA__LPI2C0 handler named in startup code. */ +void DMA_I2C0_INT_DriverIRQHandler(void); +void DMA_I2C0_INT_DriverIRQHandler(void) +{ + LPI2C_CommonIRQHandler(DMA__LPI2C0, LPI2C_GetInstance(DMA__LPI2C0)); +} +#endif + +#if defined(DMA__LPI2C1) +/* Implementation of DMA__LPI2C1 handler named in startup code. */ +void DMA_I2C1_INT_DriverIRQHandler(void); +void DMA_I2C1_INT_DriverIRQHandler(void) +{ + LPI2C_CommonIRQHandler(DMA__LPI2C1, LPI2C_GetInstance(DMA__LPI2C1)); +} +#endif + +#if defined(DMA__LPI2C2) +/* Implementation of DMA__LPI2C2 handler named in startup code. */ +void DMA_I2C2_INT_DriverIRQHandler(void); +void DMA_I2C2_INT_DriverIRQHandler(void) +{ + LPI2C_CommonIRQHandler(DMA__LPI2C2, LPI2C_GetInstance(DMA__LPI2C2)); +} +#endif + +#if defined(DMA__LPI2C3) +/* Implementation of DMA__LPI2C3 handler named in startup code. */ +void DMA_I2C3_INT_DriverIRQHandler(void); +void DMA_I2C3_INT_DriverIRQHandler(void) +{ + LPI2C_CommonIRQHandler(DMA__LPI2C3, LPI2C_GetInstance(DMA__LPI2C3)); +} +#endif + +#if defined(DMA__LPI2C4) +/* Implementation of DMA__LPI2C3 handler named in startup code. */ +void DMA_I2C4_INT_DriverIRQHandler(void); +void DMA_I2C4_INT_DriverIRQHandler(void) +{ + LPI2C_CommonIRQHandler(DMA__LPI2C4, LPI2C_GetInstance(DMA__LPI2C4)); +} +#endif + +#if defined(ADMA__LPI2C0) +/* Implementation of DMA__LPI2C0 handler named in startup code. */ +void ADMA_I2C0_INT_DriverIRQHandler(void); +void ADMA_I2C0_INT_DriverIRQHandler(void) +{ + LPI2C_CommonIRQHandler(ADMA__LPI2C0, LPI2C_GetInstance(ADMA__LPI2C0)); +} +#endif + +#if defined(ADMA__LPI2C1) +/* Implementation of DMA__LPI2C1 handler named in startup code. */ +void ADMA_I2C1_INT_DriverIRQHandler(void); +void ADMA_I2C1_INT_DriverIRQHandler(void) +{ + LPI2C_CommonIRQHandler(ADMA__LPI2C1, LPI2C_GetInstance(ADMA__LPI2C1)); +} +#endif + +#if defined(ADMA__LPI2C2) +/* Implementation of DMA__LPI2C2 handler named in startup code. */ +void ADMA_I2C2_INT_DriverIRQHandler(void); +void ADMA_I2C2_INT_DriverIRQHandler(void) +{ + LPI2C_CommonIRQHandler(ADMA__LPI2C2, LPI2C_GetInstance(ADMA__LPI2C2)); +} +#endif + +#if defined(ADMA__LPI2C3) +/* Implementation of DMA__LPI2C3 handler named in startup code. */ +void ADMA_I2C3_INT_DriverIRQHandler(void); +void ADMA_I2C3_INT_DriverIRQHandler(void) +{ + LPI2C_CommonIRQHandler(ADMA__LPI2C3, LPI2C_GetInstance(ADMA__LPI2C3)); +} +#endif + +#if defined(ADMA__LPI2C4) +/* Implementation of DMA__LPI2C3 handler named in startup code. */ +void ADMA_I2C4_INT_DriverIRQHandler(void); +void ADMA_I2C4_INT_DriverIRQHandler(void) +{ + LPI2C_CommonIRQHandler(ADMA__LPI2C4, LPI2C_GetInstance(ADMA__LPI2C4)); +} +#endif diff --git a/bsps/arm/imxrt/mcux-sdk/drivers/lpi2c/fsl_lpi2c.h b/bsps/arm/imxrt/mcux-sdk/drivers/lpi2c/fsl_lpi2c.h new file mode 100644 index 0000000000..d61a6eb5cf --- /dev/null +++ b/bsps/arm/imxrt/mcux-sdk/drivers/lpi2c/fsl_lpi2c.h @@ -0,0 +1,1320 @@ +/* + * Copyright (c) 2015, Freescale Semiconductor, Inc. + * Copyright 2016-2022 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef _FSL_LPI2C_H_ +#define _FSL_LPI2C_H_ + +#include <stddef.h> +#include "fsl_device_registers.h" +#include "fsl_common.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! + * @addtogroup lpi2c + * @{ + */ + +/*! @name Driver version */ +/*@{*/ +/*! @brief LPI2C driver version. */ +#define FSL_LPI2C_DRIVER_VERSION (MAKE_VERSION(2, 4, 1)) +/*@}*/ + +/*! @brief Retry times for waiting flag. */ +#ifndef I2C_RETRY_TIMES +#define I2C_RETRY_TIMES 0U /* Define to zero means keep waiting until the flag is assert/deassert. */ +#endif + +/*! @brief LPI2C status return codes. */ +enum +{ + kStatus_LPI2C_Busy = MAKE_STATUS(kStatusGroup_LPI2C, 0), /*!< The master is already performing a transfer. */ + kStatus_LPI2C_Idle = MAKE_STATUS(kStatusGroup_LPI2C, 1), /*!< The slave driver is idle. */ + kStatus_LPI2C_Nak = MAKE_STATUS(kStatusGroup_LPI2C, 2), /*!< The slave device sent a NAK in response to a byte. */ + kStatus_LPI2C_FifoError = MAKE_STATUS(kStatusGroup_LPI2C, 3), /*!< FIFO under run or overrun. */ + kStatus_LPI2C_BitError = MAKE_STATUS(kStatusGroup_LPI2C, 4), /*!< Transferred bit was not seen on the bus. */ + kStatus_LPI2C_ArbitrationLost = MAKE_STATUS(kStatusGroup_LPI2C, 5), /*!< Arbitration lost error. */ + kStatus_LPI2C_PinLowTimeout = + MAKE_STATUS(kStatusGroup_LPI2C, 6), /*!< SCL or SDA were held low longer than the timeout. */ + kStatus_LPI2C_NoTransferInProgress = + MAKE_STATUS(kStatusGroup_LPI2C, 7), /*!< Attempt to abort a transfer when one is not in progress. */ + kStatus_LPI2C_DmaRequestFail = MAKE_STATUS(kStatusGroup_LPI2C, 8), /*!< DMA request failed. */ + kStatus_LPI2C_Timeout = MAKE_STATUS(kStatusGroup_LPI2C, 9), /*!< Timeout polling status flags. */ +}; + +/*! @} */ + +/*! + * @addtogroup lpi2c_master_driver + * @{ + */ + +/*! + * @brief LPI2C master peripheral flags. + * + * The following status register flags can be cleared: + * - #kLPI2C_MasterEndOfPacketFlag + * - #kLPI2C_MasterStopDetectFlag + * - #kLPI2C_MasterNackDetectFlag + * - #kLPI2C_MasterArbitrationLostFlag + * - #kLPI2C_MasterFifoErrFlag + * - #kLPI2C_MasterPinLowTimeoutFlag + * - #kLPI2C_MasterDataMatchFlag + * + * All flags except #kLPI2C_MasterBusyFlag and #kLPI2C_MasterBusBusyFlag can be enabled as + * interrupts. + * + * @note These enums are meant to be OR'd together to form a bit mask. + */ +enum _lpi2c_master_flags +{ + kLPI2C_MasterTxReadyFlag = LPI2C_MSR_TDF_MASK, /*!< Transmit data flag */ + kLPI2C_MasterRxReadyFlag = LPI2C_MSR_RDF_MASK, /*!< Receive data flag */ + kLPI2C_MasterEndOfPacketFlag = LPI2C_MSR_EPF_MASK, /*!< End Packet flag */ + kLPI2C_MasterStopDetectFlag = LPI2C_MSR_SDF_MASK, /*!< Stop detect flag */ + kLPI2C_MasterNackDetectFlag = LPI2C_MSR_NDF_MASK, /*!< NACK detect flag */ + kLPI2C_MasterArbitrationLostFlag = LPI2C_MSR_ALF_MASK, /*!< Arbitration lost flag */ + kLPI2C_MasterFifoErrFlag = LPI2C_MSR_FEF_MASK, /*!< FIFO error flag */ + kLPI2C_MasterPinLowTimeoutFlag = LPI2C_MSR_PLTF_MASK, /*!< Pin low timeout flag */ + kLPI2C_MasterDataMatchFlag = LPI2C_MSR_DMF_MASK, /*!< Data match flag */ + kLPI2C_MasterBusyFlag = LPI2C_MSR_MBF_MASK, /*!< Master busy flag */ + kLPI2C_MasterBusBusyFlag = LPI2C_MSR_BBF_MASK, /*!< Bus busy flag */ + + /*! All flags which are cleared by the driver upon starting a transfer. */ + kLPI2C_MasterClearFlags = kLPI2C_MasterEndOfPacketFlag | kLPI2C_MasterStopDetectFlag | kLPI2C_MasterNackDetectFlag | + kLPI2C_MasterArbitrationLostFlag | kLPI2C_MasterFifoErrFlag | + kLPI2C_MasterPinLowTimeoutFlag | kLPI2C_MasterDataMatchFlag, + /*! IRQ sources enabled by the non-blocking transactional API. */ + kLPI2C_MasterIrqFlags = kLPI2C_MasterArbitrationLostFlag | kLPI2C_MasterTxReadyFlag | kLPI2C_MasterRxReadyFlag | + kLPI2C_MasterStopDetectFlag | kLPI2C_MasterNackDetectFlag | kLPI2C_MasterPinLowTimeoutFlag | + kLPI2C_MasterFifoErrFlag, + /*! Errors to check for. */ + kLPI2C_MasterErrorFlags = kLPI2C_MasterNackDetectFlag | kLPI2C_MasterArbitrationLostFlag | + kLPI2C_MasterFifoErrFlag | kLPI2C_MasterPinLowTimeoutFlag +}; + +/*! @brief Direction of master and slave transfers. */ +typedef enum _lpi2c_direction +{ + kLPI2C_Write = 0U, /*!< Master transmit. */ + kLPI2C_Read = 1U /*!< Master receive. */ +} lpi2c_direction_t; + +/*! @brief LPI2C pin configuration. */ +typedef enum _lpi2c_master_pin_config +{ + kLPI2C_2PinOpenDrain = 0x0U, /*!< LPI2C Configured for 2-pin open drain mode */ + kLPI2C_2PinOutputOnly = 0x1U, /*!< LPI2C Configured for 2-pin output only mode (ultra-fast mode) */ + kLPI2C_2PinPushPull = 0x2U, /*!< LPI2C Configured for 2-pin push-pull mode */ + kLPI2C_4PinPushPull = 0x3U, /*!< LPI2C Configured for 4-pin push-pull mode */ + kLPI2C_2PinOpenDrainWithSeparateSlave = + 0x4U, /*!< LPI2C Configured for 2-pin open drain mode with separate LPI2C slave */ + kLPI2C_2PinOutputOnlyWithSeparateSlave = + 0x5U, /*!< LPI2C Configured for 2-pin output only mode(ultra-fast mode) with separate LPI2C slave */ + kLPI2C_2PinPushPullWithSeparateSlave = + 0x6U, /*!< LPI2C Configured for 2-pin push-pull mode with separate LPI2C slave */ + kLPI2C_4PinPushPullWithInvertedOutput = 0x7U /*!< LPI2C Configured for 4-pin push-pull mode(inverted outputs) */ +} lpi2c_master_pin_config_t; + +/*! @brief LPI2C master host request selection. */ +typedef enum _lpi2c_host_request_source +{ + kLPI2C_HostRequestExternalPin = 0x0U, /*!< Select the LPI2C_HREQ pin as the host request input */ + kLPI2C_HostRequestInputTrigger = 0x1U, /*!< Select the input trigger as the host request input */ +} lpi2c_host_request_source_t; + +/*! @brief LPI2C master host request pin polarity configuration. */ +typedef enum _lpi2c_host_request_polarity +{ + kLPI2C_HostRequestPinActiveLow = 0x0U, /*!< Configure the LPI2C_HREQ pin active low */ + kLPI2C_HostRequestPinActiveHigh = 0x1U /*!< Configure the LPI2C_HREQ pin active high */ +} lpi2c_host_request_polarity_t; + +/*! + * @brief Structure with settings to initialize the LPI2C master module. + * + * This structure holds configuration settings for the LPI2C peripheral. To initialize this + * structure to reasonable defaults, call the LPI2C_MasterGetDefaultConfig() function and + * pass a pointer to your configuration structure instance. + * + * The configuration structure can be made constant so it resides in flash. + */ +typedef struct _lpi2c_master_config +{ + bool enableMaster; /*!< Whether to enable master mode. */ + bool enableDoze; /*!< Whether master is enabled in doze mode. */ + bool debugEnable; /*!< Enable transfers to continue when halted in debug mode. */ + bool ignoreAck; /*!< Whether to ignore ACK/NACK. */ + lpi2c_master_pin_config_t pinConfig; /*!< The pin configuration option. */ + uint32_t baudRate_Hz; /*!< Desired baud rate in Hertz. */ + uint32_t busIdleTimeout_ns; /*!< Bus idle timeout in nanoseconds. Set to 0 to disable. */ + uint32_t pinLowTimeout_ns; /*!< Pin low timeout in nanoseconds. Set to 0 to disable. */ + uint8_t sdaGlitchFilterWidth_ns; /*!< Width in nanoseconds of glitch filter on SDA pin. Set to 0 to disable. */ + uint8_t sclGlitchFilterWidth_ns; /*!< Width in nanoseconds of glitch filter on SCL pin. Set to 0 to disable. */ + struct + { + bool enable; /*!< Enable host request. */ + lpi2c_host_request_source_t source; /*!< Host request source. */ + lpi2c_host_request_polarity_t polarity; /*!< Host request pin polarity. */ + } hostRequest; /*!< Host request options. */ +} lpi2c_master_config_t; + +/*! @brief LPI2C master data match configuration modes. */ +typedef enum _lpi2c_data_match_config_mode +{ + kLPI2C_MatchDisabled = 0x0U, /*!< LPI2C Match Disabled */ + kLPI2C_1stWordEqualsM0OrM1 = 0x2U, /*!< LPI2C Match Enabled and 1st data word equals MATCH0 OR MATCH1 */ + kLPI2C_AnyWordEqualsM0OrM1 = 0x3U, /*!< LPI2C Match Enabled and any data word equals MATCH0 OR MATCH1 */ + kLPI2C_1stWordEqualsM0And2ndWordEqualsM1 = + 0x4U, /*!< LPI2C Match Enabled and 1st data word equals MATCH0, 2nd data equals MATCH1 */ + kLPI2C_AnyWordEqualsM0AndNextWordEqualsM1 = + 0x5U, /*!< LPI2C Match Enabled and any data word equals MATCH0, next data equals MATCH1 */ + kLPI2C_1stWordAndM1EqualsM0AndM1 = + 0x6U, /*!< LPI2C Match Enabled and 1st data word and MATCH0 equals MATCH0 and MATCH1 */ + kLPI2C_AnyWordAndM1EqualsM0AndM1 = + 0x7U /*!< LPI2C Match Enabled and any data word and MATCH0 equals MATCH0 and MATCH1 */ +} lpi2c_data_match_config_mode_t; + +/*! @brief LPI2C master data match configuration structure. */ +typedef struct _lpi2c_match_config +{ + lpi2c_data_match_config_mode_t matchMode; /*!< Data match configuration setting. */ + bool rxDataMatchOnly; /*!< When set to true, received data is ignored until a successful match. */ + uint32_t match0; /*!< Match value 0. */ + uint32_t match1; /*!< Match value 1. */ +} lpi2c_data_match_config_t; + +/* Forward declaration of the transfer descriptor and handle typedefs. */ +typedef struct _lpi2c_master_transfer lpi2c_master_transfer_t; +typedef struct _lpi2c_master_handle lpi2c_master_handle_t; + +/*! + * @brief Master completion callback function pointer type. + * + * This callback is used only for the non-blocking master transfer API. Specify the callback you wish to use + * in the call to LPI2C_MasterTransferCreateHandle(). + * + * @param base The LPI2C peripheral base address. + * @param completionStatus Either kStatus_Success or an error code describing how the transfer completed. + * @param userData Arbitrary pointer-sized value passed from the application. + */ +typedef void (*lpi2c_master_transfer_callback_t)(LPI2C_Type *base, + lpi2c_master_handle_t *handle, + status_t completionStatus, + void *userData); + +/*! + * @brief Transfer option flags. + * + * @note These enumerations are intended to be OR'd together to form a bit mask of options for + * the #_lpi2c_master_transfer::flags field. + */ +enum _lpi2c_master_transfer_flags +{ + kLPI2C_TransferDefaultFlag = 0x00U, /*!< Transfer starts with a start signal, stops with a stop signal. */ + kLPI2C_TransferNoStartFlag = 0x01U, /*!< Don't send a start condition, address, and sub address */ + kLPI2C_TransferRepeatedStartFlag = 0x02U, /*!< Send a repeated start condition */ + kLPI2C_TransferNoStopFlag = 0x04U, /*!< Don't send a stop condition. */ +}; + +/*! + * @brief Non-blocking transfer descriptor structure. + * + * This structure is used to pass transaction parameters to the LPI2C_MasterTransferNonBlocking() API. + */ +struct _lpi2c_master_transfer +{ + uint32_t flags; /*!< Bit mask of options for the transfer. See enumeration #_lpi2c_master_transfer_flags for + available options. Set to 0 or #kLPI2C_TransferDefaultFlag for normal transfers. */ + uint16_t slaveAddress; /*!< The 7-bit slave address. */ + lpi2c_direction_t direction; /*!< Either #kLPI2C_Read or #kLPI2C_Write. */ + uint32_t subaddress; /*!< Sub address. Transferred MSB first. */ + size_t subaddressSize; /*!< Length of sub address to send in bytes. Maximum size is 4 bytes. */ + void *data; /*!< Pointer to data to transfer. */ + size_t dataSize; /*!< Number of bytes to transfer. */ +}; + +/*! + * @brief Driver handle for master non-blocking APIs. + * @note The contents of this structure are private and subject to change. + */ +struct _lpi2c_master_handle +{ + uint8_t state; /*!< Transfer state machine current state. */ + uint16_t remainingBytes; /*!< Remaining byte count in current state. */ + uint8_t *buf; /*!< Buffer pointer for current state. */ + uint16_t commandBuffer[6]; /*!< LPI2C command sequence. When all 6 command words are used: + Start&addr&write[1 word] + subaddr[4 words] + restart&addr&read[1 word] */ + lpi2c_master_transfer_t transfer; /*!< Copy of the current transfer info. */ + lpi2c_master_transfer_callback_t completionCallback; /*!< Callback function pointer. */ + void *userData; /*!< Application data passed to callback. */ +}; + +/*! @brief Typedef for master interrupt handler, used internally for LPI2C master interrupt and EDMA transactional APIs. + */ +typedef void (*lpi2c_master_isr_t)(LPI2C_Type *base, void *handle); + +/*! @} */ + +/*! + * @addtogroup lpi2c_slave_driver + * @{ + */ + +/*! + * @brief LPI2C slave peripheral flags. + * + * The following status register flags can be cleared: + * - #kLPI2C_SlaveRepeatedStartDetectFlag + * - #kLPI2C_SlaveStopDetectFlag + * - #kLPI2C_SlaveBitErrFlag + * - #kLPI2C_SlaveFifoErrFlag + * + * All flags except #kLPI2C_SlaveBusyFlag and #kLPI2C_SlaveBusBusyFlag can be enabled as + * interrupts. + * + * @note These enumerations are meant to be OR'd together to form a bit mask. + */ +enum _lpi2c_slave_flags +{ + kLPI2C_SlaveTxReadyFlag = LPI2C_SSR_TDF_MASK, /*!< Transmit data flag */ + kLPI2C_SlaveRxReadyFlag = LPI2C_SSR_RDF_MASK, /*!< Receive data flag */ + kLPI2C_SlaveAddressValidFlag = LPI2C_SSR_AVF_MASK, /*!< Address valid flag */ + kLPI2C_SlaveTransmitAckFlag = LPI2C_SSR_TAF_MASK, /*!< Transmit ACK flag */ + kLPI2C_SlaveRepeatedStartDetectFlag = LPI2C_SSR_RSF_MASK, /*!< Repeated start detect flag */ + kLPI2C_SlaveStopDetectFlag = LPI2C_SSR_SDF_MASK, /*!< Stop detect flag */ + kLPI2C_SlaveBitErrFlag = LPI2C_SSR_BEF_MASK, /*!< Bit error flag */ + kLPI2C_SlaveFifoErrFlag = LPI2C_SSR_FEF_MASK, /*!< FIFO error flag */ + kLPI2C_SlaveAddressMatch0Flag = LPI2C_SSR_AM0F_MASK, /*!< Address match 0 flag */ + kLPI2C_SlaveAddressMatch1Flag = LPI2C_SSR_AM1F_MASK, /*!< Address match 1 flag */ + kLPI2C_SlaveGeneralCallFlag = LPI2C_SSR_GCF_MASK, /*!< General call flag */ + kLPI2C_SlaveBusyFlag = LPI2C_SSR_SBF_MASK, /*!< Master busy flag */ + kLPI2C_SlaveBusBusyFlag = LPI2C_SSR_BBF_MASK, /*!< Bus busy flag */ + /*! All flags which are cleared by the driver upon starting a transfer. */ + kLPI2C_SlaveClearFlags = kLPI2C_SlaveRepeatedStartDetectFlag | kLPI2C_SlaveStopDetectFlag | kLPI2C_SlaveBitErrFlag | + kLPI2C_SlaveFifoErrFlag, + /*! IRQ sources enabled by the non-blocking transactional API. */ + kLPI2C_SlaveIrqFlags = kLPI2C_SlaveTxReadyFlag | kLPI2C_SlaveRxReadyFlag | kLPI2C_SlaveStopDetectFlag | + kLPI2C_SlaveRepeatedStartDetectFlag | kLPI2C_SlaveFifoErrFlag | kLPI2C_SlaveBitErrFlag | + kLPI2C_SlaveTransmitAckFlag | kLPI2C_SlaveAddressValidFlag, + /*! Errors to check for. */ + kLPI2C_SlaveErrorFlags = kLPI2C_SlaveFifoErrFlag | kLPI2C_SlaveBitErrFlag +}; + +/*! @brief LPI2C slave address match options. */ +typedef enum _lpi2c_slave_address_match +{ + kLPI2C_MatchAddress0 = 0U, /*!< Match only address 0. */ + kLPI2C_MatchAddress0OrAddress1 = 2U, /*!< Match either address 0 or address 1. */ + kLPI2C_MatchAddress0ThroughAddress1 = 6U, /*!< Match a range of slave addresses from address 0 through address 1. */ +} lpi2c_slave_address_match_t; + +/*! + * @brief Structure with settings to initialize the LPI2C slave module. + * + * This structure holds configuration settings for the LPI2C slave peripheral. To initialize this + * structure to reasonable defaults, call the LPI2C_SlaveGetDefaultConfig() function and + * pass a pointer to your configuration structure instance. + * + * The configuration structure can be made constant so it resides in flash. + */ +typedef struct _lpi2c_slave_config +{ + bool enableSlave; /*!< Enable slave mode. */ + uint8_t address0; /*!< Slave's 7-bit address. */ + uint8_t address1; /*!< Alternate slave 7-bit address. */ + lpi2c_slave_address_match_t addressMatchMode; /*!< Address matching options. */ + bool filterDozeEnable; /*!< Enable digital glitch filter in doze mode. */ + bool filterEnable; /*!< Enable digital glitch filter. */ + bool enableGeneralCall; /*!< Enable general call address matching. */ + struct + { + bool enableAck; /*!< Enables SCL clock stretching during slave-transmit address byte(s) + and slave-receiver address and data byte(s) to allow software to + write the Transmit ACK Register before the ACK or NACK is transmitted. + Clock stretching occurs when transmitting the 9th bit. When + enableAckSCLStall is enabled, there is no need to set either + enableRxDataSCLStall or enableAddressSCLStall. */ + bool enableTx; /*!< Enables SCL clock stretching when the transmit data flag is set + during a slave-transmit transfer. */ + bool enableRx; /*!< Enables SCL clock stretching when receive data flag is set during + a slave-receive transfer. */ + bool enableAddress; /*!< Enables SCL clock stretching when the address valid flag is asserted. */ + } sclStall; + bool ignoreAck; /*!< Continue transfers after a NACK is detected. */ + bool enableReceivedAddressRead; /*!< Enable reading the address received address as the first byte of data. */ + uint32_t sdaGlitchFilterWidth_ns; /*!< Width in nanoseconds of the digital filter on the SDA signal. Set to 0 to + disable. */ + uint32_t sclGlitchFilterWidth_ns; /*!< Width in nanoseconds of the digital filter on the SCL signal. Set to 0 to + disable. */ + uint32_t dataValidDelay_ns; /*!< Width in nanoseconds of the data valid delay. */ + uint32_t clockHoldTime_ns; /*!< Width in nanoseconds of the clock hold time. */ +} lpi2c_slave_config_t; + +/*! + * @brief Set of events sent to the callback for non blocking slave transfers. + * + * These event enumerations are used for two related purposes. First, a bit mask created by OR'ing together + * events is passed to LPI2C_SlaveTransferNonBlocking() in order to specify which events to enable. + * Then, when the slave callback is invoked, it is passed the current event through its @a transfer + * parameter. + * + * @note These enumerations are meant to be OR'd together to form a bit mask of events. + */ +typedef enum _lpi2c_slave_transfer_event +{ + kLPI2C_SlaveAddressMatchEvent = 0x01U, /*!< Received the slave address after a start or repeated start. */ + kLPI2C_SlaveTransmitEvent = 0x02U, /*!< Callback is requested to provide data to transmit + (slave-transmitter role). */ + kLPI2C_SlaveReceiveEvent = 0x04U, /*!< Callback is requested to provide a buffer in which to place received + data (slave-receiver role). */ + kLPI2C_SlaveTransmitAckEvent = 0x08U, /*!< Callback needs to either transmit an ACK or NACK. */ + kLPI2C_SlaveRepeatedStartEvent = 0x10U, /*!< A repeated start was detected. */ + kLPI2C_SlaveCompletionEvent = 0x20U, /*!< A stop was detected, completing the transfer. */ + + /*! Bit mask of all available events. */ + kLPI2C_SlaveAllEvents = kLPI2C_SlaveAddressMatchEvent | kLPI2C_SlaveTransmitEvent | kLPI2C_SlaveReceiveEvent | + kLPI2C_SlaveTransmitAckEvent | kLPI2C_SlaveRepeatedStartEvent | kLPI2C_SlaveCompletionEvent, +} lpi2c_slave_transfer_event_t; + +/*! @brief LPI2C slave transfer structure */ +typedef struct _lpi2c_slave_transfer +{ + lpi2c_slave_transfer_event_t event; /*!< Reason the callback is being invoked. */ + uint8_t receivedAddress; /*!< Matching address send by master. */ + uint8_t *data; /*!< Transfer buffer */ + size_t dataSize; /*!< Transfer size */ + status_t completionStatus; /*!< Success or error code describing how the transfer completed. Only applies for + #kLPI2C_SlaveCompletionEvent. */ + size_t transferredCount; /*!< Number of bytes actually transferred since start or last repeated start. */ +} lpi2c_slave_transfer_t; + +/* Forward declaration. */ +typedef struct _lpi2c_slave_handle lpi2c_slave_handle_t; + +/*! + * @brief Slave event callback function pointer type. + * + * This callback is used only for the slave non-blocking transfer API. To install a callback, + * use the LPI2C_SlaveSetCallback() function after you have created a handle. + * + * @param base Base address for the LPI2C instance on which the event occurred. + * @param transfer Pointer to transfer descriptor containing values passed to and/or from the callback. + * @param userData Arbitrary pointer-sized value passed from the application. + */ +typedef void (*lpi2c_slave_transfer_callback_t)(LPI2C_Type *base, lpi2c_slave_transfer_t *transfer, void *userData); + +/*! + * @brief LPI2C slave handle structure. + * @note The contents of this structure are private and subject to change. + */ +struct _lpi2c_slave_handle +{ + lpi2c_slave_transfer_t transfer; /*!< LPI2C slave transfer copy. */ + bool isBusy; /*!< Whether transfer is busy. */ + bool wasTransmit; /*!< Whether the last transfer was a transmit. */ + uint32_t eventMask; /*!< Mask of enabled events. */ + uint32_t transferredCount; /*!< Count of bytes transferred. */ + lpi2c_slave_transfer_callback_t callback; /*!< Callback function called at transfer event. */ + void *userData; /*!< Callback parameter passed to callback. */ +}; + +/*! @} */ + +/******************************************************************************* + * Variables + ******************************************************************************/ +/*! Array to map LPI2C instance number to IRQ number, used internally for LPI2C master interrupt and EDMA transactional +APIs. */ +extern IRQn_Type const kLpi2cIrqs[]; + +/*! Pointer to master IRQ handler for each instance, used internally for LPI2C master interrupt and EDMA transactional +APIs. */ +extern lpi2c_master_isr_t s_lpi2cMasterIsr; + +/*! Pointers to master handles for each instance, used internally for LPI2C master interrupt and EDMA transactional +APIs. */ +extern void *s_lpi2cMasterHandle[]; + +/******************************************************************************* + * API + ******************************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif + +/*! + * @brief Returns an instance number given a base address. + * + * If an invalid base address is passed, debug builds will assert. Release builds will just return + * instance number 0. + * + * @param base The LPI2C peripheral base address. + * @return LPI2C instance number starting from 0. + */ +uint32_t LPI2C_GetInstance(LPI2C_Type *base); + +/*! + * @addtogroup lpi2c_master_driver + * @{ + */ + +/*! @name Initialization and deinitialization */ +/*@{*/ + +/*! + * @brief Provides a default configuration for the LPI2C master peripheral. + * + * This function provides the following default configuration for the LPI2C master peripheral: + * @code + * masterConfig->enableMaster = true; + * masterConfig->debugEnable = false; + * masterConfig->ignoreAck = false; + * masterConfig->pinConfig = kLPI2C_2PinOpenDrain; + * masterConfig->baudRate_Hz = 100000U; + * masterConfig->busIdleTimeout_ns = 0; + * masterConfig->pinLowTimeout_ns = 0; + * masterConfig->sdaGlitchFilterWidth_ns = 0; + * masterConfig->sclGlitchFilterWidth_ns = 0; + * masterConfig->hostRequest.enable = false; + * masterConfig->hostRequest.source = kLPI2C_HostRequestExternalPin; + * masterConfig->hostRequest.polarity = kLPI2C_HostRequestPinActiveHigh; + * @endcode + * + * After calling this function, you can override any settings in order to customize the configuration, + * prior to initializing the master driver with LPI2C_MasterInit(). + * + * @param[out] masterConfig User provided configuration structure for default values. Refer to #lpi2c_master_config_t. + */ +void LPI2C_MasterGetDefaultConfig(lpi2c_master_config_t *masterConfig); + +/*! + * @brief Initializes the LPI2C master peripheral. + * + * This function enables the peripheral clock and initializes the LPI2C master peripheral as described by the user + * provided configuration. A software reset is performed prior to configuration. + * + * @param base The LPI2C peripheral base address. + * @param masterConfig User provided peripheral configuration. Use LPI2C_MasterGetDefaultConfig() to get a set of + * defaults + * that you can override. + * @param sourceClock_Hz Frequency in Hertz of the LPI2C functional clock. Used to calculate the baud rate divisors, + * filter widths, and timeout periods. + */ +void LPI2C_MasterInit(LPI2C_Type *base, const lpi2c_master_config_t *masterConfig, uint32_t sourceClock_Hz); + +/*! + * @brief Deinitializes the LPI2C master peripheral. + * + * This function disables the LPI2C master peripheral and gates the clock. It also performs a software + * reset to restore the peripheral to reset conditions. + * + * @param base The LPI2C peripheral base address. + */ +void LPI2C_MasterDeinit(LPI2C_Type *base); + +/*! + * @brief Configures LPI2C master data match feature. + * + * @param base The LPI2C peripheral base address. + * @param matchConfig Settings for the data match feature. + */ +void LPI2C_MasterConfigureDataMatch(LPI2C_Type *base, const lpi2c_data_match_config_t *matchConfig); + +/* Not static so it can be used from fsl_lpi2c_edma.c. */ +status_t LPI2C_MasterCheckAndClearError(LPI2C_Type *base, uint32_t status); + +/* Not static so it can be used from fsl_lpi2c_edma.c. */ +status_t LPI2C_CheckForBusyBus(LPI2C_Type *base); + +/*! + * @brief Performs a software reset. + * + * Restores the LPI2C master peripheral to reset conditions. + * + * @param base The LPI2C peripheral base address. + */ +static inline void LPI2C_MasterReset(LPI2C_Type *base) +{ + base->MCR = LPI2C_MCR_RST_MASK; + base->MCR = 0; +} + +/*! + * @brief Enables or disables the LPI2C module as master. + * + * @param base The LPI2C peripheral base address. + * @param enable Pass true to enable or false to disable the specified LPI2C as master. + */ +static inline void LPI2C_MasterEnable(LPI2C_Type *base, bool enable) +{ + base->MCR = (base->MCR & ~LPI2C_MCR_MEN_MASK) | LPI2C_MCR_MEN(enable); +} + +/*@}*/ + +/*! @name Status */ +/*@{*/ + +/*! + * @brief Gets the LPI2C master status flags. + * + * A bit mask with the state of all LPI2C master status flags is returned. For each flag, the corresponding bit + * in the return value is set if the flag is asserted. + * + * @param base The LPI2C peripheral base address. + * @return State of the status flags: + * - 1: related status flag is set. + * - 0: related status flag is not set. + * @see _lpi2c_master_flags + */ +static inline uint32_t LPI2C_MasterGetStatusFlags(LPI2C_Type *base) +{ + return base->MSR; +} + +/*! + * @brief Clears the LPI2C master status flag state. + * + * The following status register flags can be cleared: + * - #kLPI2C_MasterEndOfPacketFlag + * - #kLPI2C_MasterStopDetectFlag + * - #kLPI2C_MasterNackDetectFlag + * - #kLPI2C_MasterArbitrationLostFlag + * - #kLPI2C_MasterFifoErrFlag + * - #kLPI2C_MasterPinLowTimeoutFlag + * - #kLPI2C_MasterDataMatchFlag + * + * Attempts to clear other flags has no effect. + * + * @param base The LPI2C peripheral base address. + * @param statusMask A bitmask of status flags that are to be cleared. The mask is composed of + * _lpi2c_master_flags enumerators OR'd together. You may pass the result of a previous call to + * LPI2C_MasterGetStatusFlags(). + * @see _lpi2c_master_flags. + */ +static inline void LPI2C_MasterClearStatusFlags(LPI2C_Type *base, uint32_t statusMask) +{ + base->MSR = statusMask; +} + +/*@}*/ + +/*! @name Interrupts */ +/*@{*/ + +/*! + * @brief Enables the LPI2C master interrupt requests. + * + * All flags except #kLPI2C_MasterBusyFlag and #kLPI2C_MasterBusBusyFlag can be enabled as + * interrupts. + * + * @param base The LPI2C peripheral base address. + * @param interruptMask Bit mask of interrupts to enable. See _lpi2c_master_flags for the set + * of constants that should be OR'd together to form the bit mask. + */ +static inline void LPI2C_MasterEnableInterrupts(LPI2C_Type *base, uint32_t interruptMask) +{ + base->MIER |= interruptMask; +} + +/*! + * @brief Disables the LPI2C master interrupt requests. + * + * All flags except #kLPI2C_MasterBusyFlag and #kLPI2C_MasterBusBusyFlag can be enabled as + * interrupts. + * + * @param base The LPI2C peripheral base address. + * @param interruptMask Bit mask of interrupts to disable. See _lpi2c_master_flags for the set + * of constants that should be OR'd together to form the bit mask. + */ +static inline void LPI2C_MasterDisableInterrupts(LPI2C_Type *base, uint32_t interruptMask) +{ + base->MIER &= ~interruptMask; +} + +/*! + * @brief Returns the set of currently enabled LPI2C master interrupt requests. + * + * @param base The LPI2C peripheral base address. + * @return A bitmask composed of _lpi2c_master_flags enumerators OR'd together to indicate the + * set of enabled interrupts. + */ +static inline uint32_t LPI2C_MasterGetEnabledInterrupts(LPI2C_Type *base) +{ + return base->MIER; +} + +/*@}*/ + +/*! @name DMA control */ +/*@{*/ + +/*! + * @brief Enables or disables LPI2C master DMA requests. + * + * @param base The LPI2C peripheral base address. + * @param enableTx Enable flag for transmit DMA request. Pass true for enable, false for disable. + * @param enableRx Enable flag for receive DMA request. Pass true for enable, false for disable. + */ +static inline void LPI2C_MasterEnableDMA(LPI2C_Type *base, bool enableTx, bool enableRx) +{ + base->MDER = LPI2C_MDER_TDDE(enableTx) | LPI2C_MDER_RDDE(enableRx); +} + +/*! + * @brief Gets LPI2C master transmit data register address for DMA transfer. + * + * @param base The LPI2C peripheral base address. + * @return The LPI2C Master Transmit Data Register address. + */ +static inline uint32_t LPI2C_MasterGetTxFifoAddress(LPI2C_Type *base) +{ + return (uint32_t)&base->MTDR; +} + +/*! + * @brief Gets LPI2C master receive data register address for DMA transfer. + * + * @param base The LPI2C peripheral base address. + * @return The LPI2C Master Receive Data Register address. + */ +static inline uint32_t LPI2C_MasterGetRxFifoAddress(LPI2C_Type *base) +{ + return (uint32_t)&base->MRDR; +} + +/*@}*/ + +/*! @name FIFO control */ +/*@{*/ + +/*! + * @brief Sets the watermarks for LPI2C master FIFOs. + * + * @param base The LPI2C peripheral base address. + * @param txWords Transmit FIFO watermark value in words. The #kLPI2C_MasterTxReadyFlag flag is set whenever + * the number of words in the transmit FIFO is equal or less than @a txWords. Writing a value equal or + * greater than the FIFO size is truncated. + * @param rxWords Receive FIFO watermark value in words. The #kLPI2C_MasterRxReadyFlag flag is set whenever + * the number of words in the receive FIFO is greater than @a rxWords. Writing a value equal or greater + * than the FIFO size is truncated. + */ +static inline void LPI2C_MasterSetWatermarks(LPI2C_Type *base, size_t txWords, size_t rxWords) +{ + base->MFCR = LPI2C_MFCR_TXWATER(txWords) | LPI2C_MFCR_RXWATER(rxWords); +} + +/*! + * @brief Gets the current number of words in the LPI2C master FIFOs. + * + * @param base The LPI2C peripheral base address. + * @param[out] txCount Pointer through which the current number of words in the transmit FIFO is returned. + * Pass NULL if this value is not required. + * @param[out] rxCount Pointer through which the current number of words in the receive FIFO is returned. + * Pass NULL if this value is not required. + */ +static inline void LPI2C_MasterGetFifoCounts(LPI2C_Type *base, size_t *rxCount, size_t *txCount) +{ + if (NULL != txCount) + { + *txCount = (base->MFSR & LPI2C_MFSR_TXCOUNT_MASK) >> LPI2C_MFSR_TXCOUNT_SHIFT; + } + if (NULL != rxCount) + { + *rxCount = (base->MFSR & LPI2C_MFSR_RXCOUNT_MASK) >> LPI2C_MFSR_RXCOUNT_SHIFT; + } +} + +/*@}*/ + +/*! @name Bus operations */ +/*@{*/ + +/*! + * @brief Sets the I2C bus frequency for master transactions. + * + * The LPI2C master is automatically disabled and re-enabled as necessary to configure the baud + * rate. Do not call this function during a transfer, or the transfer is aborted. + * + * @note Please note that the second parameter is the clock frequency of LPI2C module, the third + * parameter means user configured bus baudrate, this implementation is different from other I2C drivers + * which use baudrate configuration as second parameter and source clock frequency as third parameter. + * + * @param base The LPI2C peripheral base address. + * @param sourceClock_Hz LPI2C functional clock frequency in Hertz. + * @param baudRate_Hz Requested bus frequency in Hertz. + */ +void LPI2C_MasterSetBaudRate(LPI2C_Type *base, uint32_t sourceClock_Hz, uint32_t baudRate_Hz); + +/*! + * @brief Returns whether the bus is idle. + * + * Requires the master mode to be enabled. + * + * @param base The LPI2C peripheral base address. + * @retval true Bus is busy. + * @retval false Bus is idle. + */ +static inline bool LPI2C_MasterGetBusIdleState(LPI2C_Type *base) +{ + return ((base->MSR & LPI2C_MSR_BBF_MASK) >> LPI2C_MSR_BBF_SHIFT) == 1U ? true : false; +} + +/*! + * @brief Sends a START signal and slave address on the I2C bus. + * + * This function is used to initiate a new master mode transfer. First, the bus state is checked to ensure + * that another master is not occupying the bus. Then a START signal is transmitted, followed by the + * 7-bit address specified in the @a address parameter. Note that this function does not actually wait + * until the START and address are successfully sent on the bus before returning. + * + * @param base The LPI2C peripheral base address. + * @param address 7-bit slave device address, in bits [6:0]. + * @param dir Master transfer direction, either #kLPI2C_Read or #kLPI2C_Write. This parameter is used to set + * the R/w bit (bit 0) in the transmitted slave address. + * @retval kStatus_Success START signal and address were successfully enqueued in the transmit FIFO. + * @retval #kStatus_LPI2C_Busy Another master is currently utilizing the bus. + */ +status_t LPI2C_MasterStart(LPI2C_Type *base, uint8_t address, lpi2c_direction_t dir); + +/*! + * @brief Sends a repeated START signal and slave address on the I2C bus. + * + * This function is used to send a Repeated START signal when a transfer is already in progress. Like + * LPI2C_MasterStart(), it also sends the specified 7-bit address. + * + * @note This function exists primarily to maintain compatible APIs between LPI2C and I2C drivers, + * as well as to better document the intent of code that uses these APIs. + * + * @param base The LPI2C peripheral base address. + * @param address 7-bit slave device address, in bits [6:0]. + * @param dir Master transfer direction, either #kLPI2C_Read or #kLPI2C_Write. This parameter is used to set + * the R/w bit (bit 0) in the transmitted slave address. + * @retval kStatus_Success Repeated START signal and address were successfully enqueued in the transmit FIFO. + * @retval #kStatus_LPI2C_Busy Another master is currently utilizing the bus. + */ +static inline status_t LPI2C_MasterRepeatedStart(LPI2C_Type *base, uint8_t address, lpi2c_direction_t dir) +{ + return LPI2C_MasterStart(base, address, dir); +} + +/*! + * @brief Performs a polling send transfer on the I2C bus. + * + * Sends up to @a txSize number of bytes to the previously addressed slave device. The slave may + * reply with a NAK to any byte in order to terminate the transfer early. If this happens, this + * function returns #kStatus_LPI2C_Nak. + * + * @param base The LPI2C peripheral base address. + * @param txBuff The pointer to the data to be transferred. + * @param txSize The length in bytes of the data to be transferred. + * @retval kStatus_Success Data was sent successfully. + * @retval #kStatus_LPI2C_Busy Another master is currently utilizing the bus. + * @retval #kStatus_LPI2C_Nak The slave device sent a NAK in response to a byte. + * @retval #kStatus_LPI2C_FifoError FIFO under run or over run. + * @retval #kStatus_LPI2C_ArbitrationLost Arbitration lost error. + * @retval #kStatus_LPI2C_PinLowTimeout SCL or SDA were held low longer than the timeout. + */ +status_t LPI2C_MasterSend(LPI2C_Type *base, void *txBuff, size_t txSize); + +/*! + * @brief Performs a polling receive transfer on the I2C bus. + * + * @param base The LPI2C peripheral base address. + * @param rxBuff The pointer to the data to be transferred. + * @param rxSize The length in bytes of the data to be transferred. + * @retval kStatus_Success Data was received successfully. + * @retval #kStatus_LPI2C_Busy Another master is currently utilizing the bus. + * @retval #kStatus_LPI2C_Nak The slave device sent a NAK in response to a byte. + * @retval #kStatus_LPI2C_FifoError FIFO under run or overrun. + * @retval #kStatus_LPI2C_ArbitrationLost Arbitration lost error. + * @retval #kStatus_LPI2C_PinLowTimeout SCL or SDA were held low longer than the timeout. + */ +status_t LPI2C_MasterReceive(LPI2C_Type *base, void *rxBuff, size_t rxSize); + +/*! + * @brief Sends a STOP signal on the I2C bus. + * + * This function does not return until the STOP signal is seen on the bus, or an error occurs. + * + * @param base The LPI2C peripheral base address. + * @retval kStatus_Success The STOP signal was successfully sent on the bus and the transaction terminated. + * @retval #kStatus_LPI2C_Busy Another master is currently utilizing the bus. + * @retval #kStatus_LPI2C_Nak The slave device sent a NAK in response to a byte. + * @retval #kStatus_LPI2C_FifoError FIFO under run or overrun. + * @retval #kStatus_LPI2C_ArbitrationLost Arbitration lost error. + * @retval #kStatus_LPI2C_PinLowTimeout SCL or SDA were held low longer than the timeout. + */ +status_t LPI2C_MasterStop(LPI2C_Type *base); + +/*! + * @brief Performs a master polling transfer on the I2C bus. + * + * @note The API does not return until the transfer succeeds or fails due + * to error happens during transfer. + * + * @param base The LPI2C peripheral base address. + * @param transfer Pointer to the transfer structure. + * @retval kStatus_Success Data was received successfully. + * @retval #kStatus_LPI2C_Busy Another master is currently utilizing the bus. + * @retval #kStatus_LPI2C_Nak The slave device sent a NAK in response to a byte. + * @retval #kStatus_LPI2C_FifoError FIFO under run or overrun. + * @retval #kStatus_LPI2C_ArbitrationLost Arbitration lost error. + * @retval #kStatus_LPI2C_PinLowTimeout SCL or SDA were held low longer than the timeout. + */ +status_t LPI2C_MasterTransferBlocking(LPI2C_Type *base, lpi2c_master_transfer_t *transfer); + +/*@}*/ + +/*! @name Non-blocking */ +/*@{*/ + +/*! + * @brief Creates a new handle for the LPI2C master non-blocking APIs. + * + * The creation of a handle is for use with the non-blocking APIs. Once a handle + * is created, there is not a corresponding destroy handle. If the user wants to + * terminate a transfer, the LPI2C_MasterTransferAbort() API shall be called. + * + * + * @note The function also enables the NVIC IRQ for the input LPI2C. Need to notice + * that on some SoCs the LPI2C IRQ is connected to INTMUX, in this case user needs to + * enable the associated INTMUX IRQ in application. + * + * @param base The LPI2C peripheral base address. + * @param[out] handle Pointer to the LPI2C master driver handle. + * @param callback User provided pointer to the asynchronous callback function. + * @param userData User provided pointer to the application callback data. + */ +void LPI2C_MasterTransferCreateHandle(LPI2C_Type *base, + lpi2c_master_handle_t *handle, + lpi2c_master_transfer_callback_t callback, + void *userData); + +/*! + * @brief Performs a non-blocking transaction on the I2C bus. + * + * @param base The LPI2C peripheral base address. + * @param handle Pointer to the LPI2C master driver handle. + * @param transfer The pointer to the transfer descriptor. + * @retval kStatus_Success The transaction was started successfully. + * @retval #kStatus_LPI2C_Busy Either another master is currently utilizing the bus, or a non-blocking + * transaction is already in progress. + */ +status_t LPI2C_MasterTransferNonBlocking(LPI2C_Type *base, + lpi2c_master_handle_t *handle, + lpi2c_master_transfer_t *transfer); + +/*! + * @brief Returns number of bytes transferred so far. + * @param base The LPI2C peripheral base address. + * @param handle Pointer to the LPI2C master driver handle. + * @param[out] count Number of bytes transferred so far by the non-blocking transaction. + * @retval kStatus_Success + * @retval kStatus_NoTransferInProgress There is not a non-blocking transaction currently in progress. + */ +status_t LPI2C_MasterTransferGetCount(LPI2C_Type *base, lpi2c_master_handle_t *handle, size_t *count); + +/*! + * @brief Terminates a non-blocking LPI2C master transmission early. + * + * @note It is not safe to call this function from an IRQ handler that has a higher priority than the + * LPI2C peripheral's IRQ priority. + * + * @param base The LPI2C peripheral base address. + * @param handle Pointer to the LPI2C master driver handle. + * @retval kStatus_Success A transaction was successfully aborted. + * @retval #kStatus_LPI2C_Idle There is not a non-blocking transaction currently in progress. + */ +void LPI2C_MasterTransferAbort(LPI2C_Type *base, lpi2c_master_handle_t *handle); + +/*@}*/ + +/*! @name IRQ handler */ +/*@{*/ + +/*! + * @brief Reusable routine to handle master interrupts. + * @note This function does not need to be called unless you are reimplementing the + * nonblocking API's interrupt handler routines to add special functionality. + * @param base The LPI2C peripheral base address. + * @param lpi2cMasterHandle Pointer to the LPI2C master driver handle. + */ +void LPI2C_MasterTransferHandleIRQ(LPI2C_Type *base, void *lpi2cMasterHandle); + +/*@}*/ + +/*! @} */ + +/*! + * @addtogroup lpi2c_slave_driver + * @{ + */ + +/*! @name Slave initialization and deinitialization */ +/*@{*/ + +/*! + * @brief Provides a default configuration for the LPI2C slave peripheral. + * + * This function provides the following default configuration for the LPI2C slave peripheral: + * @code + * slaveConfig->enableSlave = true; + * slaveConfig->address0 = 0U; + * slaveConfig->address1 = 0U; + * slaveConfig->addressMatchMode = kLPI2C_MatchAddress0; + * slaveConfig->filterDozeEnable = true; + * slaveConfig->filterEnable = true; + * slaveConfig->enableGeneralCall = false; + * slaveConfig->sclStall.enableAck = false; + * slaveConfig->sclStall.enableTx = true; + * slaveConfig->sclStall.enableRx = true; + * slaveConfig->sclStall.enableAddress = true; + * slaveConfig->ignoreAck = false; + * slaveConfig->enableReceivedAddressRead = false; + * slaveConfig->sdaGlitchFilterWidth_ns = 0; + * slaveConfig->sclGlitchFilterWidth_ns = 0; + * slaveConfig->dataValidDelay_ns = 0; + * slaveConfig->clockHoldTime_ns = 0; + * @endcode + * + * After calling this function, override any settings to customize the configuration, + * prior to initializing the master driver with LPI2C_SlaveInit(). Be sure to override at least the @a + * address0 member of the configuration structure with the desired slave address. + * + * @param[out] slaveConfig User provided configuration structure that is set to default values. Refer to + * #lpi2c_slave_config_t. + */ +void LPI2C_SlaveGetDefaultConfig(lpi2c_slave_config_t *slaveConfig); + +/*! + * @brief Initializes the LPI2C slave peripheral. + * + * This function enables the peripheral clock and initializes the LPI2C slave peripheral as described by the user + * provided configuration. + * + * @param base The LPI2C peripheral base address. + * @param slaveConfig User provided peripheral configuration. Use LPI2C_SlaveGetDefaultConfig() to get a set of defaults + * that you can override. + * @param sourceClock_Hz Frequency in Hertz of the LPI2C functional clock. Used to calculate the filter widths, + * data valid delay, and clock hold time. + */ +void LPI2C_SlaveInit(LPI2C_Type *base, const lpi2c_slave_config_t *slaveConfig, uint32_t sourceClock_Hz); + +/*! + * @brief Deinitializes the LPI2C slave peripheral. + * + * This function disables the LPI2C slave peripheral and gates the clock. It also performs a software + * reset to restore the peripheral to reset conditions. + * + * @param base The LPI2C peripheral base address. + */ +void LPI2C_SlaveDeinit(LPI2C_Type *base); + +/*! + * @brief Performs a software reset of the LPI2C slave peripheral. + * + * @param base The LPI2C peripheral base address. + */ +static inline void LPI2C_SlaveReset(LPI2C_Type *base) +{ + base->SCR = LPI2C_SCR_RST_MASK; + base->SCR = 0; +} + +/*! + * @brief Enables or disables the LPI2C module as slave. + * + * @param base The LPI2C peripheral base address. + * @param enable Pass true to enable or false to disable the specified LPI2C as slave. + */ +static inline void LPI2C_SlaveEnable(LPI2C_Type *base, bool enable) +{ + base->SCR = (base->SCR & ~LPI2C_SCR_SEN_MASK) | LPI2C_SCR_SEN(enable); +} + +/*@}*/ + +/*! @name Slave status */ +/*@{*/ + +/*! + * @brief Gets the LPI2C slave status flags. + * + * A bit mask with the state of all LPI2C slave status flags is returned. For each flag, the corresponding bit + * in the return value is set if the flag is asserted. + * + * @param base The LPI2C peripheral base address. + * @return State of the status flags: + * - 1: related status flag is set. + * - 0: related status flag is not set. + * @see _lpi2c_slave_flags + */ +static inline uint32_t LPI2C_SlaveGetStatusFlags(LPI2C_Type *base) +{ + return base->SSR; +} + +/*! + * @brief Clears the LPI2C status flag state. + * + * The following status register flags can be cleared: + * - #kLPI2C_SlaveRepeatedStartDetectFlag + * - #kLPI2C_SlaveStopDetectFlag + * - #kLPI2C_SlaveBitErrFlag + * - #kLPI2C_SlaveFifoErrFlag + * + * Attempts to clear other flags has no effect. + * + * @param base The LPI2C peripheral base address. + * @param statusMask A bitmask of status flags that are to be cleared. The mask is composed of + * #_lpi2c_slave_flags enumerators OR'd together. You may pass the result of a previous call to + * LPI2C_SlaveGetStatusFlags(). + * @see _lpi2c_slave_flags. + */ +static inline void LPI2C_SlaveClearStatusFlags(LPI2C_Type *base, uint32_t statusMask) +{ + base->SSR = statusMask; +} + +/*@}*/ + +/*! @name Slave interrupts */ +/*@{*/ + +/*! + * @brief Enables the LPI2C slave interrupt requests. + * + * All flags except #kLPI2C_SlaveBusyFlag and #kLPI2C_SlaveBusBusyFlag can be enabled as + * interrupts. + * + * @param base The LPI2C peripheral base address. + * @param interruptMask Bit mask of interrupts to enable. See #_lpi2c_slave_flags for the set + * of constants that should be OR'd together to form the bit mask. + */ +static inline void LPI2C_SlaveEnableInterrupts(LPI2C_Type *base, uint32_t interruptMask) +{ + base->SIER |= interruptMask; +} + +/*! + * @brief Disables the LPI2C slave interrupt requests. + * + * All flags except #kLPI2C_SlaveBusyFlag and #kLPI2C_SlaveBusBusyFlag can be enabled as + * interrupts. + * + * @param base The LPI2C peripheral base address. + * @param interruptMask Bit mask of interrupts to disable. See #_lpi2c_slave_flags for the set + * of constants that should be OR'd together to form the bit mask. + */ +static inline void LPI2C_SlaveDisableInterrupts(LPI2C_Type *base, uint32_t interruptMask) +{ + base->SIER &= ~interruptMask; +} + +/*! + * @brief Returns the set of currently enabled LPI2C slave interrupt requests. + * @param base The LPI2C peripheral base address. + * @return A bitmask composed of #_lpi2c_slave_flags enumerators OR'd together to indicate the + * set of enabled interrupts. + */ +static inline uint32_t LPI2C_SlaveGetEnabledInterrupts(LPI2C_Type *base) +{ + return base->SIER; +} + +/*@}*/ + +/*! @name Slave DMA control */ +/*@{*/ + +/*! + * @brief Enables or disables the LPI2C slave peripheral DMA requests. + * + * @param base The LPI2C peripheral base address. + * @param enableAddressValid Enable flag for the address valid DMA request. Pass true for enable, false for disable. + * The address valid DMA request is shared with the receive data DMA request. + * @param enableRx Enable flag for the receive data DMA request. Pass true for enable, false for disable. + * @param enableTx Enable flag for the transmit data DMA request. Pass true for enable, false for disable. + */ +static inline void LPI2C_SlaveEnableDMA(LPI2C_Type *base, bool enableAddressValid, bool enableRx, bool enableTx) +{ + base->SDER = (base->SDER & ~(LPI2C_SDER_AVDE_MASK | LPI2C_SDER_RDDE_MASK | LPI2C_SDER_TDDE_MASK)) | + LPI2C_SDER_AVDE(enableAddressValid) | LPI2C_SDER_RDDE(enableRx) | LPI2C_SDER_TDDE(enableTx); +} + +/*@}*/ + +/*! @name Slave bus operations */ +/*@{*/ + +/*! + * @brief Returns whether the bus is idle. + * + * Requires the slave mode to be enabled. + * + * @param base The LPI2C peripheral base address. + * @retval true Bus is busy. + * @retval false Bus is idle. + */ +static inline bool LPI2C_SlaveGetBusIdleState(LPI2C_Type *base) +{ + return ((base->SSR & LPI2C_SSR_BBF_MASK) >> LPI2C_SSR_BBF_SHIFT) == 1U ? true : false; +} + +/*! + * @brief Transmits either an ACK or NAK on the I2C bus in response to a byte from the master. + * + * Use this function to send an ACK or NAK when the #kLPI2C_SlaveTransmitAckFlag is asserted. This + * only happens if you enable the sclStall.enableAck field of the ::lpi2c_slave_config_t configuration + * structure used to initialize the slave peripheral. + * + * @param base The LPI2C peripheral base address. + * @param ackOrNack Pass true for an ACK or false for a NAK. + */ +static inline void LPI2C_SlaveTransmitAck(LPI2C_Type *base, bool ackOrNack) +{ + base->STAR = LPI2C_STAR_TXNACK(!ackOrNack); +} + +/*! + * @brief Returns the slave address sent by the I2C master. + * + * This function should only be called if the #kLPI2C_SlaveAddressValidFlag is asserted. + * + * @param base The LPI2C peripheral base address. + * @return The 8-bit address matched by the LPI2C slave. Bit 0 contains the R/w direction bit, and + * the 7-bit slave address is in the upper 7 bits. + */ +static inline uint32_t LPI2C_SlaveGetReceivedAddress(LPI2C_Type *base) +{ + return base->SASR & LPI2C_SASR_RADDR_MASK; +} + +/*! + * @brief Performs a polling send transfer on the I2C bus. + * + * @param base The LPI2C peripheral base address. + * @param txBuff The pointer to the data to be transferred. + * @param txSize The length in bytes of the data to be transferred. + * @param[out] actualTxSize + * @return Error or success status returned by API. + */ +status_t LPI2C_SlaveSend(LPI2C_Type *base, void *txBuff, size_t txSize, size_t *actualTxSize); + +/*! + * @brief Performs a polling receive transfer on the I2C bus. + * + * @param base The LPI2C peripheral base address. + * @param rxBuff The pointer to the data to be transferred. + * @param rxSize The length in bytes of the data to be transferred. + * @param[out] actualRxSize + * @return Error or success status returned by API. + */ +status_t LPI2C_SlaveReceive(LPI2C_Type *base, void *rxBuff, size_t rxSize, size_t *actualRxSize); + +/*@}*/ + +/*! @name Slave non-blocking */ +/*@{*/ + +/*! + * @brief Creates a new handle for the LPI2C slave non-blocking APIs. + * + * The creation of a handle is for use with the non-blocking APIs. Once a handle + * is created, there is not a corresponding destroy handle. If the user wants to + * terminate a transfer, the LPI2C_SlaveTransferAbort() API shall be called. + * + * @note The function also enables the NVIC IRQ for the input LPI2C. Need to notice + * that on some SoCs the LPI2C IRQ is connected to INTMUX, in this case user needs to + * enable the associated INTMUX IRQ in application. + + * @param base The LPI2C peripheral base address. + * @param[out] handle Pointer to the LPI2C slave driver handle. + * @param callback User provided pointer to the asynchronous callback function. + * @param userData User provided pointer to the application callback data. + */ +void LPI2C_SlaveTransferCreateHandle(LPI2C_Type *base, + lpi2c_slave_handle_t *handle, + lpi2c_slave_transfer_callback_t callback, + void *userData); + +/*! + * @brief Starts accepting slave transfers. + * + * Call this API after calling I2C_SlaveInit() and LPI2C_SlaveTransferCreateHandle() to start processing + * transactions driven by an I2C master. The slave monitors the I2C bus and pass events to the + * callback that was passed into the call to LPI2C_SlaveTransferCreateHandle(). The callback is always invoked + * from the interrupt context. + * + * The set of events received by the callback is customizable. To do so, set the @a eventMask parameter to + * the OR'd combination of #lpi2c_slave_transfer_event_t enumerators for the events you wish to receive. + * The #kLPI2C_SlaveTransmitEvent and #kLPI2C_SlaveReceiveEvent events are always enabled and do not need + * to be included in the mask. Alternatively, you can pass 0 to get a default set of only the transmit and + * receive events that are always enabled. In addition, the #kLPI2C_SlaveAllEvents constant is provided as + * a convenient way to enable all events. + * + * @param base The LPI2C peripheral base address. + * @param handle Pointer to lpi2c_slave_handle_t structure which stores the transfer state. + * @param eventMask Bit mask formed by OR'ing together #lpi2c_slave_transfer_event_t enumerators to specify + * which events to send to the callback. Other accepted values are 0 to get a default set of + * only the transmit and receive events, and #kLPI2C_SlaveAllEvents to enable all events. + * + * @retval kStatus_Success Slave transfers were successfully started. + * @retval #kStatus_LPI2C_Busy Slave transfers have already been started on this handle. + */ +status_t LPI2C_SlaveTransferNonBlocking(LPI2C_Type *base, lpi2c_slave_handle_t *handle, uint32_t eventMask); + +/*! + * @brief Gets the slave transfer status during a non-blocking transfer. + * @param base The LPI2C peripheral base address. + * @param handle Pointer to i2c_slave_handle_t structure. + * @param[out] count Pointer to a value to hold the number of bytes transferred. May be NULL if the count is not + * required. + * @retval kStatus_Success + * @retval kStatus_NoTransferInProgress + */ +status_t LPI2C_SlaveTransferGetCount(LPI2C_Type *base, lpi2c_slave_handle_t *handle, size_t *count); + +/*! + * @brief Aborts the slave non-blocking transfers. + * @note This API could be called at any time to stop slave for handling the bus events. + * @param base The LPI2C peripheral base address. + * @param handle Pointer to lpi2c_slave_handle_t structure which stores the transfer state. + * @retval kStatus_Success + * @retval #kStatus_LPI2C_Idle + */ +void LPI2C_SlaveTransferAbort(LPI2C_Type *base, lpi2c_slave_handle_t *handle); + +/*@}*/ + +/*! @name Slave IRQ handler */ +/*@{*/ + +/*! + * @brief Reusable routine to handle slave interrupts. + * @note This function does not need to be called unless you are reimplementing the + * non blocking API's interrupt handler routines to add special functionality. + * @param base The LPI2C peripheral base address. + * @param handle Pointer to lpi2c_slave_handle_t structure which stores the transfer state. + */ +void LPI2C_SlaveTransferHandleIRQ(LPI2C_Type *base, lpi2c_slave_handle_t *handle); + +/*@}*/ + +/*! @} */ + +#if defined(__cplusplus) +} +#endif + +#endif /* _FSL_LPI2C_H_ */ diff --git a/bsps/arm/imxrt/mcux-sdk/drivers/lpi2c/fsl_lpi2c_edma.c b/bsps/arm/imxrt/mcux-sdk/drivers/lpi2c/fsl_lpi2c_edma.c new file mode 100644 index 0000000000..7e28c7e693 --- /dev/null +++ b/bsps/arm/imxrt/mcux-sdk/drivers/lpi2c/fsl_lpi2c_edma.c @@ -0,0 +1,616 @@ +/* + * Copyright (c) 2015, Freescale Semiconductor, Inc. + * Copyright 2016-2021 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "fsl_lpi2c_edma.h" +#include <stdlib.h> +#include <string.h> + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.lpi2c_edma" +#endif + +/* @brief Mask to align an address to 32 bytes. */ +#define ALIGN_32_MASK (0x1fU) + +/* ! @brief LPI2C master fifo commands. */ +enum _lpi2c_master_fifo_cmd +{ + kTxDataCmd = LPI2C_MTDR_CMD(0x0U), /*!< Transmit DATA[7:0] */ + kRxDataCmd = LPI2C_MTDR_CMD(0X1U), /*!< Receive (DATA[7:0] + 1) bytes */ + kStopCmd = LPI2C_MTDR_CMD(0x2U), /*!< Generate STOP condition */ + kStartCmd = LPI2C_MTDR_CMD(0x4U), /*!< Generate(repeated) START and transmit address in DATA[[7:0] */ +}; + +/*! @brief States for the state machine used by transactional APIs. */ +enum _lpi2c_transfer_states +{ + kIdleState = 0, + kSendCommandState, + kIssueReadCommandState, + kTransferDataState, + kStopState, + kWaitForCompletionState, +}; + +/*! @brief Typedef for interrupt handler. */ +typedef void (*lpi2c_isr_t)(LPI2C_Type *base, void *handle); + +/******************************************************************************* + * Prototypes + ******************************************************************************/ + +/*! + * @brief Prepares the command buffer with the sequence of commands needed to send the requested transaction. + * @param handle Master DMA driver handle. + * @return Number of command words. + */ +static uint32_t LPI2C_GenerateCommands(lpi2c_master_edma_handle_t *handle); + +/*! + * @brief DMA completion callback. + * @param dmaHandle DMA channel handle for the channel that completed. + * @param userData User data associated with the channel handle. For this callback, the user data is the + * LPI2C DMA driver handle. + * @param isTransferDone Whether the DMA transfer has completed. + * @param tcds Number of TCDs that completed. + */ +static void LPI2C_MasterEDMACallback(edma_handle_t *dmaHandle, void *userData, bool isTransferDone, uint32_t tcds); + +/*! + * @brief LPI2C master edma transfer IRQ handle routine. + * + * This API handles the LPI2C bus error status and invoke callback if needed. + * + * @param base The LPI2C peripheral base address. + * @param lpi2cMasterEdmaHandle Pointer to the LPI2C master edma handle. + */ +static void LPI2C_MasterTransferEdmaHandleIRQ(LPI2C_Type *base, void *lpi2cMasterEdmaHandle); +/******************************************************************************* + * Variables + ******************************************************************************/ + +static uint32_t lpi2c_edma_RecSetting = 0x02; + +/******************************************************************************* + * Code + ******************************************************************************/ + +/*! + * brief Create a new handle for the LPI2C master DMA APIs. + * + * The creation of a handle is for use with the DMA APIs. Once a handle + * is created, there is not a corresponding destroy handle. If the user wants to + * terminate a transfer, the LPI2C_MasterTransferAbortEDMA() API shall be called. + * + * For devices where the LPI2C send and receive DMA requests are OR'd together, the a txDmaHandle + * parameter is ignored and may be set to NULL. + * + * param base The LPI2C peripheral base address. + * param[out] handle Pointer to the LPI2C master driver handle. + * param rxDmaHandle Handle for the eDMA receive channel. Created by the user prior to calling this function. + * param txDmaHandle Handle for the eDMA transmit channel. Created by the user prior to calling this function. + * param callback User provided pointer to the asynchronous callback function. + * param userData User provided pointer to the application callback data. + */ +void LPI2C_MasterCreateEDMAHandle(LPI2C_Type *base, + lpi2c_master_edma_handle_t *handle, + edma_handle_t *rxDmaHandle, + edma_handle_t *txDmaHandle, + lpi2c_master_edma_transfer_callback_t callback, + void *userData) +{ + assert(handle != NULL); + assert(rxDmaHandle != NULL); + assert(txDmaHandle != NULL); + + /* Look up instance number */ + uint32_t instance = LPI2C_GetInstance(base); + + /* Clear out the handle. */ + (void)memset(handle, 0, sizeof(*handle)); + + /* Set up the handle. For combined rx/tx DMA requests, the tx channel handle is set to the rx handle */ + /* in order to make the transfer API code simpler. */ + handle->base = base; + handle->completionCallback = callback; + handle->userData = userData; + handle->rx = rxDmaHandle; + handle->tx = (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) > 0) ? txDmaHandle : rxDmaHandle; + + /* Save the handle in global variables to support the double weak mechanism. */ + s_lpi2cMasterHandle[instance] = handle; + + /* Set LPI2C_MasterTransferEdmaHandleIRQ as LPI2C DMA IRQ handler */ + s_lpi2cMasterIsr = LPI2C_MasterTransferEdmaHandleIRQ; + + /* Enable interrupt in NVIC. */ + (void)EnableIRQ(kLpi2cIrqs[instance]); + + /* Set DMA channel completion callbacks. */ + EDMA_SetCallback(handle->rx, LPI2C_MasterEDMACallback, handle); + if (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) != 0) + { + EDMA_SetCallback(handle->tx, LPI2C_MasterEDMACallback, handle); + } +} + +static uint32_t LPI2C_GenerateCommands(lpi2c_master_edma_handle_t *handle) +{ + lpi2c_master_transfer_t *xfer = &handle->transfer; + uint16_t *cmd = (uint16_t *)&handle->commandBuffer; + uint32_t cmdCount = 0; + + /* Handle no start option. */ + if ((xfer->flags & (uint32_t)kLPI2C_TransferNoStartFlag) != 0U) + { + if (xfer->direction == kLPI2C_Read) + { + /* Need to issue read command first. */ + cmd[cmdCount++] = (uint16_t)kRxDataCmd | (uint16_t)LPI2C_MTDR_DATA(xfer->dataSize - 1U); + } + } + else + { + /* + * Initial direction depends on whether a subaddress was provided, and of course the actual + * data transfer direction. + */ + lpi2c_direction_t direction = (xfer->subaddressSize != 0U) ? kLPI2C_Write : xfer->direction; + + /* Start command. */ + cmd[cmdCount++] = + (uint16_t)kStartCmd | (uint16_t)((uint16_t)((uint16_t)xfer->slaveAddress << 1U) | (uint16_t)direction); + + /* Subaddress, MSB first. */ + if (xfer->subaddressSize != 0U) + { + uint32_t subaddressRemaining = xfer->subaddressSize; + while (0U != subaddressRemaining--) + { + uint8_t subaddressByte = (uint8_t)(xfer->subaddress >> (8U * subaddressRemaining)) & 0xffU; + cmd[cmdCount++] = subaddressByte; + } + } + + /* Reads need special handling because we have to issue a read command and maybe a repeated start. */ + if ((xfer->dataSize != 0U) && (xfer->direction == kLPI2C_Read)) + { + /* Need to send repeated start if switching directions to read. */ + if (direction == kLPI2C_Write) + { + cmd[cmdCount++] = (uint16_t)kStartCmd | + (uint16_t)((uint16_t)((uint16_t)xfer->slaveAddress << 1U) | (uint16_t)kLPI2C_Read); + } + + /* Read command. A single write to MTDR can issue read operation of 0xFFU + 1 byte of data at most, so when + the dataSize is larger than 0x100U, push multiple read commands to MTDR until dataSize is reached. */ + size_t tmpRxSize = xfer->dataSize; + while (tmpRxSize != 0U) + { + if (tmpRxSize > 256U) + { + cmd[cmdCount++] = (uint16_t)kRxDataCmd | (uint16_t)LPI2C_MTDR_DATA(0xFFU); + tmpRxSize -= 256U; + } + else + { + cmd[cmdCount++] = (uint16_t)kRxDataCmd | (uint16_t)LPI2C_MTDR_DATA(tmpRxSize - 1U); + tmpRxSize = 0U; + } + } + } + } + + return cmdCount; +} + +/*! + * brief Performs a non-blocking DMA-based transaction on the I2C bus. + * + * The callback specified when the a handle was created is invoked when the transaction has + * completed. + * + * param base The LPI2C peripheral base address. + * param handle Pointer to the LPI2C master driver handle. + * param transfer The pointer to the transfer descriptor. + * retval #kStatus_Success The transaction was started successfully. + * retval #kStatus_LPI2C_Busy Either another master is currently utilizing the bus, or another DMA + * transaction is already in progress. + */ +status_t LPI2C_MasterTransferEDMA(LPI2C_Type *base, + lpi2c_master_edma_handle_t *handle, + lpi2c_master_transfer_t *transfer) +{ + status_t result; + + assert(handle != NULL); + assert(transfer != NULL); + assert(transfer->subaddressSize <= sizeof(transfer->subaddress)); + + /* Check transfer data size in read operation. */ + /* A single write to MTDR can issue read operation of 0xFFU + 1 byte of data at most, so when the dataSize is larger + than 0x100U, push multiple read commands to MTDR until dataSize is reached. LPI2C edma transfer uses linked + descriptor to transfer command and data, the command buffer is stored in handle. Allocate 4 command words to + carry read command which can cover nearly all use cases. */ + if ((transfer->direction == kLPI2C_Read) && (transfer->dataSize > (256U * 4U))) + { + return kStatus_InvalidArgument; + } + + /* Return busy if another transaction is in progress. */ + if (handle->isBusy) + { + return kStatus_LPI2C_Busy; + } + + /* Enable the master function and disable the slave function. */ + LPI2C_MasterEnable(base, true); + LPI2C_SlaveEnable(base, false); + + /* Return an error if the bus is already in use not by us. */ + result = LPI2C_CheckForBusyBus(base); + if (result != kStatus_Success) + { + return result; + } + + /* We're now busy. */ + handle->isBusy = true; + + /* Disable LPI2C IRQ and DMA sources while we configure stuff. */ + LPI2C_MasterDisableInterrupts(base, (uint32_t)kLPI2C_MasterIrqFlags); + LPI2C_MasterEnableDMA(base, false, false); + + /* Clear all flags. */ + LPI2C_MasterClearStatusFlags(base, (uint32_t)kLPI2C_MasterClearFlags); + + /* Save transfer into handle. */ + handle->transfer = *transfer; + + /* Generate commands to send. */ + uint32_t commandCount = LPI2C_GenerateCommands(handle); + + /* If the user is transmitting no data with no start or stop, then just go ahead and invoke the callback. */ + if ((0U == commandCount) && (transfer->dataSize == 0U)) + { + if (handle->completionCallback != NULL) + { + handle->completionCallback(base, handle, kStatus_Success, handle->userData); + } + return kStatus_Success; + } + + /* Reset DMA channels. */ + EDMA_ResetChannel(handle->rx->base, handle->rx->channel); + if (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) != 0) + { + EDMA_ResetChannel(handle->tx->base, handle->tx->channel); + } + + /* Get a 32-byte aligned TCD pointer. */ + edma_tcd_t *tcd = (edma_tcd_t *)((uint32_t)(&handle->tcds[1]) & (~ALIGN_32_MASK)); + + bool hasSendData = (transfer->direction == kLPI2C_Write) && (transfer->dataSize != 0U); + bool hasReceiveData = (transfer->direction == kLPI2C_Read) && (transfer->dataSize != 0U); + + edma_transfer_config_t transferConfig = {0}; + edma_tcd_t *linkTcd = NULL; + + /* Set up data transmit. */ + if (hasSendData) + { + uint32_t *srcAddr = (uint32_t *)transfer->data; + transferConfig.srcAddr = (uint32_t)srcAddr; + transferConfig.destAddr = (uint32_t)LPI2C_MasterGetTxFifoAddress(base); + transferConfig.srcTransferSize = kEDMA_TransferSize1Bytes; + transferConfig.destTransferSize = kEDMA_TransferSize1Bytes; + transferConfig.srcOffset = (int16_t)sizeof(uint8_t); + transferConfig.destOffset = 0; + transferConfig.minorLoopBytes = sizeof(uint8_t); /* TODO optimize to fill fifo */ + transferConfig.majorLoopCounts = transfer->dataSize; + + /* Store the initially configured eDMA minor byte transfer count into the LPI2C handle */ + handle->nbytes = (uint8_t)transferConfig.minorLoopBytes; + + if (commandCount != 0U) + { + /* Create a software TCD, which will be chained after the commands. */ + EDMA_TcdReset(tcd); + EDMA_TcdSetTransferConfig(tcd, &transferConfig, NULL); + EDMA_TcdEnableInterrupts(tcd, (uint32_t)kEDMA_MajorInterruptEnable); + linkTcd = tcd; + } + else + { + /* User is only transmitting data with no required commands, so this transfer can stand alone. */ + EDMA_SetTransferConfig(handle->tx->base, handle->tx->channel, &transferConfig, NULL); + EDMA_EnableChannelInterrupts(handle->tx->base, handle->tx->channel, (uint32_t)kEDMA_MajorInterruptEnable); + } + } + else if (hasReceiveData) + { + uint32_t *srcAddr = (uint32_t *)transfer->data; + /* Set up data receive. */ + transferConfig.srcAddr = (uint32_t)LPI2C_MasterGetRxFifoAddress(base); + transferConfig.destAddr = (uint32_t)srcAddr; + transferConfig.srcTransferSize = kEDMA_TransferSize1Bytes; + transferConfig.destTransferSize = kEDMA_TransferSize1Bytes; + transferConfig.srcOffset = 0; + transferConfig.destOffset = (int16_t)sizeof(uint8_t); + transferConfig.minorLoopBytes = sizeof(uint8_t); /* TODO optimize to empty fifo */ + transferConfig.majorLoopCounts = transfer->dataSize; + + /* Store the initially configured eDMA minor byte transfer count into the LPI2C handle */ + handle->nbytes = (uint8_t)transferConfig.minorLoopBytes; + + if ((FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) != 0) || (0U == commandCount)) + { + /* We can put this receive transfer on its own DMA channel. */ + EDMA_SetTransferConfig(handle->rx->base, handle->rx->channel, &transferConfig, NULL); + EDMA_EnableChannelInterrupts(handle->rx->base, handle->rx->channel, (uint32_t)kEDMA_MajorInterruptEnable); + } + else + { + /* For shared rx/tx DMA requests, when there are commands, create a software TCD of + enabling rx dma and disabling tx dma, which will be chained onto the commands transfer, + and create another software TCD of transfering data and chain it onto the last TCD. + Notice that in this situation assume tx/rx uses same channel */ + EDMA_TcdReset(tcd); + EDMA_TcdSetTransferConfig(tcd, &transferConfig, NULL); + EDMA_TcdEnableInterrupts(tcd, (uint32_t)kEDMA_MajorInterruptEnable); + + transferConfig.srcAddr = (uint32_t)&lpi2c_edma_RecSetting; + transferConfig.destAddr = (uint32_t) & (base->MDER); + transferConfig.srcTransferSize = kEDMA_TransferSize1Bytes; + transferConfig.destTransferSize = kEDMA_TransferSize1Bytes; + transferConfig.srcOffset = 0; + transferConfig.destOffset = (int16_t)sizeof(uint8_t); + transferConfig.minorLoopBytes = sizeof(uint8_t); + transferConfig.majorLoopCounts = 1; + + edma_tcd_t *tcdSetRxClearTxDMA = (edma_tcd_t *)((uint32_t)(&handle->tcds[2]) & (~ALIGN_32_MASK)); + + EDMA_TcdReset(tcdSetRxClearTxDMA); + EDMA_TcdSetTransferConfig(tcdSetRxClearTxDMA, &transferConfig, tcd); + linkTcd = tcdSetRxClearTxDMA; + } + } + else + { + /* No data to send */ + } + + /* Set up commands transfer. */ + if (commandCount != 0U) + { + transferConfig.srcAddr = (uint32_t)handle->commandBuffer; + transferConfig.destAddr = (uint32_t)LPI2C_MasterGetTxFifoAddress(base); + transferConfig.srcTransferSize = kEDMA_TransferSize2Bytes; + transferConfig.destTransferSize = kEDMA_TransferSize2Bytes; + transferConfig.srcOffset = (int16_t)sizeof(uint16_t); + transferConfig.destOffset = 0; + transferConfig.minorLoopBytes = sizeof(uint16_t); /* TODO optimize to fill fifo */ + transferConfig.majorLoopCounts = commandCount; + + EDMA_SetTransferConfig(handle->tx->base, handle->tx->channel, &transferConfig, linkTcd); + } + + /* Start DMA transfer. */ + if (hasReceiveData || (0 == FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base))) + { + EDMA_StartTransfer(handle->rx); + } + + if ((hasSendData || (commandCount != 0U)) && (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) != 0)) + { + EDMA_StartTransfer(handle->tx); + } + + /* Enable DMA in both directions. This actually kicks of the transfer. */ + LPI2C_MasterEnableDMA(base, true, true); + + /* Enable all LPI2C master interrupts */ + LPI2C_MasterEnableInterrupts(base, + (uint32_t)kLPI2C_MasterArbitrationLostFlag | (uint32_t)kLPI2C_MasterNackDetectFlag | + (uint32_t)kLPI2C_MasterPinLowTimeoutFlag | (uint32_t)kLPI2C_MasterFifoErrFlag); + + return result; +} + +/*! + * brief Returns number of bytes transferred so far. + * + * param base The LPI2C peripheral base address. + * param handle Pointer to the LPI2C master driver handle. + * param[out] count Number of bytes transferred so far by the non-blocking transaction. + * retval #kStatus_Success + * retval #kStatus_NoTransferInProgress There is not a DMA transaction currently in progress. + */ +status_t LPI2C_MasterTransferGetCountEDMA(LPI2C_Type *base, lpi2c_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->isBusy) + { + *count = 0; + return kStatus_NoTransferInProgress; + } + + uint32_t remaining = handle->transfer.dataSize; + + /* If the DMA is still on a commands transfer that chains to the actual data transfer, */ + /* we do nothing and return the number of transferred bytes as zero. */ + if (EDMA_GetNextTCDAddress(handle->tx) == 0U) + { + if (handle->transfer.direction == kLPI2C_Write) + { + remaining = + (uint32_t)handle->nbytes * EDMA_GetRemainingMajorLoopCount(handle->tx->base, handle->tx->channel); + } + else + { + remaining = + (uint32_t)handle->nbytes * EDMA_GetRemainingMajorLoopCount(handle->rx->base, handle->rx->channel); + } + } + + *count = handle->transfer.dataSize - remaining; + + return kStatus_Success; +} + +/*! + * brief Terminates a non-blocking LPI2C master transmission early. + * + * note It is not safe to call this function from an IRQ handler that has a higher priority than the + * eDMA peripheral's IRQ priority. + * + * param base The LPI2C peripheral base address. + * param handle Pointer to the LPI2C master driver handle. + * retval #kStatus_Success A transaction was successfully aborted. + * retval #kStatus_LPI2C_Idle There is not a DMA transaction currently in progress. + */ +status_t LPI2C_MasterTransferAbortEDMA(LPI2C_Type *base, lpi2c_master_edma_handle_t *handle) +{ + /* Catch when there is not an active transfer. */ + if (!handle->isBusy) + { + return kStatus_LPI2C_Idle; + } + + /* Terminate DMA transfers. */ + EDMA_AbortTransfer(handle->rx); + if (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) != 0) + { + EDMA_AbortTransfer(handle->tx); + } + + /* Reset fifos. */ + base->MCR |= LPI2C_MCR_RRF_MASK | LPI2C_MCR_RTF_MASK; + + /* Disable LPI2C interrupts. */ + LPI2C_MasterDisableInterrupts(base, (uint32_t)kLPI2C_MasterIrqFlags); + + /* If master is still busy and has not send out stop signal yet. */ + if ((LPI2C_MasterGetStatusFlags(base) & + ((uint32_t)kLPI2C_MasterStopDetectFlag | (uint32_t)kLPI2C_MasterBusyFlag)) == (uint32_t)kLPI2C_MasterBusyFlag) + { + /* Send a stop command to finalize the transfer. */ + base->MTDR = (uint32_t)kStopCmd; + } + + /* Reset handle. */ + handle->isBusy = false; + + return kStatus_Success; +} + +static void LPI2C_MasterEDMACallback(edma_handle_t *dmaHandle, void *userData, bool isTransferDone, uint32_t tcds) +{ + lpi2c_master_edma_handle_t *handle = (lpi2c_master_edma_handle_t *)userData; + + if (NULL == handle) + { + return; + } + + /* Check for errors. */ + status_t result = LPI2C_MasterCheckAndClearError(handle->base, LPI2C_MasterGetStatusFlags(handle->base)); + + /* Done with this transaction. */ + handle->isBusy = false; + + if (0U == (handle->transfer.flags & (uint32_t)kLPI2C_TransferNoStopFlag)) + { + /* Send a stop command to finalize the transfer. */ + handle->base->MTDR = (uint32_t)kStopCmd; + } + + /* Invoke callback. */ + if (handle->completionCallback != NULL) + { + handle->completionCallback(handle->base, handle, result, handle->userData); + } +} + +static void LPI2C_MasterTransferEdmaHandleIRQ(LPI2C_Type *base, void *lpi2cMasterEdmaHandle) +{ + assert(lpi2cMasterEdmaHandle != NULL); + + lpi2c_master_edma_handle_t *handle = (lpi2c_master_edma_handle_t *)lpi2cMasterEdmaHandle; + uint32_t status = LPI2C_MasterGetStatusFlags(base); + status_t result = kStatus_Success; + + /* Terminate DMA transfers. */ + EDMA_AbortTransfer(handle->rx); + if (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) != 0) + { + EDMA_AbortTransfer(handle->tx); + } + + /* Done with this transaction. */ + handle->isBusy = false; + + /* Disable LPI2C interrupts. */ + LPI2C_MasterDisableInterrupts(base, (uint32_t)kLPI2C_MasterIrqFlags); + + /* Check error status */ + if (0U != (status & (uint32_t)kLPI2C_MasterPinLowTimeoutFlag)) + { + result = kStatus_LPI2C_PinLowTimeout; + } + else if (0U != (status & (uint32_t)kLPI2C_MasterArbitrationLostFlag)) + { + result = kStatus_LPI2C_ArbitrationLost; + } + else if (0U != (status & (uint32_t)kLPI2C_MasterNackDetectFlag)) + { + result = kStatus_LPI2C_Nak; + } + else if (0U != (status & (uint32_t)kLPI2C_MasterFifoErrFlag)) + { + result = kStatus_LPI2C_FifoError; + } + else + { + ; /* Intentional empty */ + } + + /* Clear error status. */ + (void)LPI2C_MasterCheckAndClearError(base, status); + + /* Send stop flag if needed */ + if (0U == (handle->transfer.flags & (uint32_t)kLPI2C_TransferNoStopFlag)) + { + status = LPI2C_MasterGetStatusFlags(base); + /* If bus is still busy and the master has not generate stop flag */ + if ((status & ((uint32_t)kLPI2C_MasterBusBusyFlag | (uint32_t)kLPI2C_MasterStopDetectFlag)) == + (uint32_t)kLPI2C_MasterBusBusyFlag) + { + /* Send a stop command to finalize the transfer. */ + handle->base->MTDR = (uint32_t)kStopCmd; + } + } + + /* Invoke callback. */ + if (handle->completionCallback != NULL) + { + handle->completionCallback(base, handle, result, handle->userData); + } +} diff --git a/bsps/arm/imxrt/mcux-sdk/drivers/lpi2c/fsl_lpi2c_edma.h b/bsps/arm/imxrt/mcux-sdk/drivers/lpi2c/fsl_lpi2c_edma.h new file mode 100644 index 0000000000..f579086af2 --- /dev/null +++ b/bsps/arm/imxrt/mcux-sdk/drivers/lpi2c/fsl_lpi2c_edma.h @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2015, Freescale Semiconductor, Inc. + * Copyright 2016-2022 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef _FSL_LPI2C_EDMA_H_ +#define _FSL_LPI2C_EDMA_H_ + +#include "fsl_lpi2c.h" +#include "fsl_edma.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! @name Driver version */ +/*@{*/ +/*! @brief LPI2C EDMA driver version. */ +#define FSL_LPI2C_EDMA_DRIVER_VERSION (MAKE_VERSION(2, 4, 1)) +/*@}*/ + +/*! + * @addtogroup lpi2c_master_edma_driver + * @{ + */ + +/* Forward declaration of the transfer descriptor and handle typedefs. */ +typedef struct _lpi2c_master_edma_handle lpi2c_master_edma_handle_t; + +/*! + * @brief Master DMA completion callback function pointer type. + * + * This callback is used only for the non-blocking master transfer API. Specify the callback you wish to use + * in the call to LPI2C_MasterCreateEDMAHandle(). + * + * @param base The LPI2C peripheral base address. + * @param handle Handle associated with the completed transfer. + * @param completionStatus Either kStatus_Success or an error code describing how the transfer completed. + * @param userData Arbitrary pointer-sized value passed from the application. + */ +typedef void (*lpi2c_master_edma_transfer_callback_t)(LPI2C_Type *base, + lpi2c_master_edma_handle_t *handle, + status_t completionStatus, + void *userData); + +/*! + * @brief Driver handle for master DMA APIs. + * @note The contents of this structure are private and subject to change. + */ +struct _lpi2c_master_edma_handle +{ + LPI2C_Type *base; /*!< LPI2C base pointer. */ + bool isBusy; /*!< Transfer state machine current state. */ + uint8_t nbytes; /*!< eDMA minor byte transfer count initially configured. */ + uint16_t commandBuffer[10]; /*!< LPI2C command sequence. When all 10 command words are used: + Start&addr&write[1 word] + subaddr[4 words] + restart&addr&read[1 word] + receive&Size[4 words] */ + lpi2c_master_transfer_t transfer; /*!< Copy of the current transfer info. */ + lpi2c_master_edma_transfer_callback_t completionCallback; /*!< Callback function pointer. */ + void *userData; /*!< Application data passed to callback. */ + edma_handle_t *rx; /*!< Handle for receive DMA channel. */ + edma_handle_t *tx; /*!< Handle for transmit DMA channel. */ + edma_tcd_t tcds[3]; /*!< Software TCD. Three are allocated to provide enough room to align to 32-bytes. */ +}; + +/*! @} */ + +/******************************************************************************* + * API + ******************************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif + +/*! + * @addtogroup lpi2c_master_edma_driver + * @{ + */ + +/*! @name Master DMA */ +/*@{*/ + +/*! + * @brief Create a new handle for the LPI2C master DMA APIs. + * + * The creation of a handle is for use with the DMA APIs. Once a handle + * is created, there is not a corresponding destroy handle. If the user wants to + * terminate a transfer, the LPI2C_MasterTransferAbortEDMA() API shall be called. + * + * For devices where the LPI2C send and receive DMA requests are OR'd together, the @a txDmaHandle + * parameter is ignored and may be set to NULL. + * + * @param base The LPI2C peripheral base address. + * @param[out] handle Pointer to the LPI2C master driver handle. + * @param rxDmaHandle Handle for the eDMA receive channel. Created by the user prior to calling this function. + * @param txDmaHandle Handle for the eDMA transmit channel. Created by the user prior to calling this function. + * @param callback User provided pointer to the asynchronous callback function. + * @param userData User provided pointer to the application callback data. + */ +void LPI2C_MasterCreateEDMAHandle(LPI2C_Type *base, + lpi2c_master_edma_handle_t *handle, + edma_handle_t *rxDmaHandle, + edma_handle_t *txDmaHandle, + lpi2c_master_edma_transfer_callback_t callback, + void *userData); + +/*! + * @brief Performs a non-blocking DMA-based transaction on the I2C bus. + * + * The callback specified when the @a handle was created is invoked when the transaction has + * completed. + * + * @param base The LPI2C peripheral base address. + * @param handle Pointer to the LPI2C master driver handle. + * @param transfer The pointer to the transfer descriptor. + * @retval kStatus_Success The transaction was started successfully. + * @retval #kStatus_LPI2C_Busy Either another master is currently utilizing the bus, or another DMA + * transaction is already in progress. + */ +status_t LPI2C_MasterTransferEDMA(LPI2C_Type *base, + lpi2c_master_edma_handle_t *handle, + lpi2c_master_transfer_t *transfer); + +/*! + * @brief Returns number of bytes transferred so far. + * + * @param base The LPI2C peripheral base address. + * @param handle Pointer to the LPI2C master driver handle. + * @param[out] count Number of bytes transferred so far by the non-blocking transaction. + * @retval kStatus_Success + * @retval kStatus_NoTransferInProgress There is not a DMA transaction currently in progress. + */ +status_t LPI2C_MasterTransferGetCountEDMA(LPI2C_Type *base, lpi2c_master_edma_handle_t *handle, size_t *count); + +/*! + * @brief Terminates a non-blocking LPI2C master transmission early. + * + * @note It is not safe to call this function from an IRQ handler that has a higher priority than the + * eDMA peripheral's IRQ priority. + * + * @param base The LPI2C peripheral base address. + * @param handle Pointer to the LPI2C master driver handle. + * @retval kStatus_Success A transaction was successfully aborted. + * @retval #kStatus_LPI2C_Idle There is not a DMA transaction currently in progress. + */ +status_t LPI2C_MasterTransferAbortEDMA(LPI2C_Type *base, lpi2c_master_edma_handle_t *handle); + +/*@}*/ + +/*! @} */ + +#if defined(__cplusplus) +} +#endif + +#endif /* _FSL_LPI2C_EDMA_H_ */ diff --git a/bsps/arm/imxrt/mcux-sdk/drivers/lpi2c/fsl_lpi2c_freertos.h b/bsps/arm/imxrt/mcux-sdk/drivers/lpi2c/fsl_lpi2c_freertos.h new file mode 100644 index 0000000000..1b989326e2 --- /dev/null +++ b/bsps/arm/imxrt/mcux-sdk/drivers/lpi2c/fsl_lpi2c_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_LPI2C_FREERTOS_H__ +#define __FSL_LPI2C_FREERTOS_H__ + +#include "FreeRTOS.h" +#include "portable.h" +#include "semphr.h" + +#include "fsl_lpi2c.h" + +/*! + * @addtogroup lpi2c_freertos_driver LPI2C FreeRTOS Driver + * @{ + */ + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! @name Driver version */ +/*@{*/ +/*! @brief LPI2C FreeRTOS driver version. */ +#define FSL_LPI2C_FREERTOS_DRIVER_VERSION (MAKE_VERSION(2, 3, 2)) +/*@}*/ + +/*! + * @cond RTOS_PRIVATE + * @brief LPI2C FreeRTOS handle + */ +typedef struct _lpi2c_rtos_handle +{ + LPI2C_Type *base; /*!< LPI2C base address */ + lpi2c_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 semaphore; /*!< Semaphore to notify and unblock task when transfer ends */ +} lpi2c_rtos_handle_t; +/*! \endcond */ + +/******************************************************************************* + * API + ******************************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif + +/*! + * @name LPI2C RTOS Operation + * @{ + */ + +/*! + * @brief Initializes LPI2C. + * + * This function initializes the LPI2C module and related RTOS context. + * + * @param handle The RTOS LPI2C handle, the pointer to an allocated space for RTOS context. + * @param base The pointer base address of the LPI2C instance to initialize. + * @param masterConfig Configuration structure to set-up LPI2C in master mode. + * @param srcClock_Hz Frequency of input clock of the LPI2C module. + * @return status of the operation. + */ +status_t LPI2C_RTOS_Init(lpi2c_rtos_handle_t *handle, + LPI2C_Type *base, + const lpi2c_master_config_t *masterConfig, + uint32_t srcClock_Hz); + +/*! + * @brief Deinitializes the LPI2C. + * + * This function deinitializes the LPI2C module and related RTOS context. + * + * @param handle The RTOS LPI2C handle. + */ +status_t LPI2C_RTOS_Deinit(lpi2c_rtos_handle_t *handle); + +/*! + * @brief Performs I2C transfer. + * + * This function performs an I2C transfer using LPI2C module according to data given in the transfer structure. + * + * @param handle The RTOS LPI2C handle. + * @param transfer Structure specifying the transfer parameters. + * @return status of the operation. + */ +status_t LPI2C_RTOS_Transfer(lpi2c_rtos_handle_t *handle, lpi2c_master_transfer_t *transfer); + +/*! + * @} + */ + +#if defined(__cplusplus) +} +#endif + +/*! + * @} + */ + +#endif /* __FSL_LPI2C_FREERTOS_H__ */ |