diff options
Diffstat (limited to 'bsps/arm/imxrt/mcux-sdk/drivers/lpi2c/fsl_lpi2c.c')
-rw-r--r-- | bsps/arm/imxrt/mcux-sdk/drivers/lpi2c/fsl_lpi2c.c | 2608 |
1 files changed, 2608 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 |