| /* |
| * Copyright (c) 2015, Freescale Semiconductor, Inc. |
| * Copyright 2016-2017 NXP |
| * |
| * Redistribution and use in source and binary forms, with or without modification, |
| * are permitted provided that the following conditions are met: |
| * |
| * o Redistributions of source code must retain the above copyright notice, this list |
| * of conditions and the following disclaimer. |
| * |
| * o Redistributions in binary form must reproduce the above copyright notice, this |
| * list of conditions and the following disclaimer in the documentation and/or |
| * other materials provided with the distribution. |
| * |
| * o Neither the name of the copyright holder nor the names of its |
| * contributors may be used to endorse or promote products derived from this |
| * software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR |
| * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
| * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "fsl_lpi2c.h" |
| #include <stdlib.h> |
| #include <string.h> |
| |
| /******************************************************************************* |
| * Definitions |
| ******************************************************************************/ |
| |
| /*! @brief Common sets of flags used by the driver. */ |
| enum _lpi2c_flag_constants |
| { |
| /*! All flags which are cleared by the driver upon starting a transfer. */ |
| kMasterClearFlags = kLPI2C_MasterEndOfPacketFlag | kLPI2C_MasterStopDetectFlag | kLPI2C_MasterNackDetectFlag | |
| kLPI2C_MasterArbitrationLostFlag | kLPI2C_MasterFifoErrFlag | kLPI2C_MasterPinLowTimeoutFlag | |
| kLPI2C_MasterDataMatchFlag, |
| |
| /*! IRQ sources enabled by the non-blocking transactional API. */ |
| kMasterIrqFlags = kLPI2C_MasterArbitrationLostFlag | kLPI2C_MasterTxReadyFlag | kLPI2C_MasterRxReadyFlag | |
| kLPI2C_MasterStopDetectFlag | kLPI2C_MasterNackDetectFlag | kLPI2C_MasterPinLowTimeoutFlag | |
| kLPI2C_MasterFifoErrFlag, |
| |
| /*! Errors to check for. */ |
| kMasterErrorFlags = kLPI2C_MasterNackDetectFlag | kLPI2C_MasterArbitrationLostFlag | kLPI2C_MasterFifoErrFlag | |
| kLPI2C_MasterPinLowTimeoutFlag, |
| |
| /*! All flags which are cleared by the driver upon starting a transfer. */ |
| kSlaveClearFlags = kLPI2C_SlaveRepeatedStartDetectFlag | kLPI2C_SlaveStopDetectFlag | kLPI2C_SlaveBitErrFlag | |
| kLPI2C_SlaveFifoErrFlag, |
| |
| /*! IRQ sources enabled by the non-blocking transactional API. */ |
| kSlaveIrqFlags = kLPI2C_SlaveTxReadyFlag | kLPI2C_SlaveRxReadyFlag | kLPI2C_SlaveStopDetectFlag | |
| kLPI2C_SlaveRepeatedStartDetectFlag | kLPI2C_SlaveFifoErrFlag | kLPI2C_SlaveBitErrFlag | |
| kLPI2C_SlaveTransmitAckFlag | kLPI2C_SlaveAddressValidFlag, |
| |
| /*! Errors to check for. */ |
| kSlaveErrorFlags = kLPI2C_SlaveFifoErrFlag | kLPI2C_SlaveBitErrFlag, |
| }; |
| |
| /* ! @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 Default watermark values. |
| * |
| * The default watermarks are set to zero. |
| */ |
| enum _lpi2c_default_watermarks |
| { |
| kDefaultTxWatermark = 0, |
| kDefaultRxWatermark = 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 master interrupt handler. */ |
| typedef void (*lpi2c_master_isr_t)(LPI2C_Type *base, lpi2c_master_handle_t *handle); |
| |
| /*! @brief Typedef for slave interrupt handler. */ |
| typedef void (*lpi2c_slave_isr_t)(LPI2C_Type *base, lpi2c_slave_handle_t *handle); |
| |
| /******************************************************************************* |
| * Prototypes |
| ******************************************************************************/ |
| |
| /* Not static so it can be used from fsl_lpi2c_edma.c. */ |
| uint32_t LPI2C_GetInstance(LPI2C_Type *base); |
| |
| static uint32_t LPI2C_GetCyclesForWidth(uint32_t sourceClock_Hz, |
| uint32_t width_ns, |
| uint32_t maxCycles, |
| uint32_t prescaler); |
| |
| /* Not static so it can be used from fsl_lpi2c_edma.c. */ |
| status_t LPI2C_MasterCheckAndClearError(LPI2C_Type *base, uint32_t status); |
| |
| static status_t LPI2C_MasterWaitForTxReady(LPI2C_Type *base); |
| |
| /* Not static so it can be used from fsl_lpi2c_edma.c. */ |
| status_t LPI2C_CheckForBusyBus(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); |
| |
| /******************************************************************************* |
| * 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. */ |
| static 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. */ |
| static lpi2c_master_isr_t s_lpi2cMasterIsr; |
| |
| /*! @brief Pointers to master handles for each instance. */ |
| static lpi2c_master_handle_t *s_lpi2cMasterHandle[FSL_FEATURE_SOC_LPI2C_COUNT]; |
| |
| /*! @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[FSL_FEATURE_SOC_LPI2C_COUNT]; |
| |
| /******************************************************************************* |
| * 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 = 0; instance < ARRAY_SIZE(kLpi2cBases); ++instance) |
| { |
| if (kLpi2cBases[instance] == base) |
| { |
| return instance; |
| } |
| } |
| |
| assert(false); |
| return 0; |
| } |
| |
| /*! |
| * @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 maxCycles Maximum cycle count, determined by the number of bits wide the cycle count field is. |
| * @param prescaler LPI2C prescaler setting. Pass 1 if the prescaler should not be used, as for slave glitch widths. |
| */ |
| static uint32_t LPI2C_GetCyclesForWidth(uint32_t sourceClock_Hz, |
| uint32_t width_ns, |
| uint32_t maxCycles, |
| uint32_t prescaler) |
| { |
| uint32_t busCycle_ns = 1000000 / (sourceClock_Hz / prescaler / 1000); |
| uint32_t cycles = 0; |
| |
| /* Search for the cycle count just below the desired glitch width. */ |
| while ((((cycles + 1) * busCycle_ns) < width_ns) && (cycles + 1 < maxCycles)) |
| { |
| ++cycles; |
| } |
| |
| /* If we end up with zero cycles, then set the filter to a single cycle unless the */ |
| /* bus clock is greater than 10x the desired glitch width. */ |
| if ((cycles == 0) && (busCycle_ns <= (width_ns * 10))) |
| { |
| cycles = 1; |
| } |
| |
| 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 |
| */ |
| 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 &= kMasterErrorFlags; |
| if (status) |
| { |
| /* Select the correct error code. Ordered by severity, with bus issues first. */ |
| if (status & kLPI2C_MasterPinLowTimeoutFlag) |
| { |
| result = kStatus_LPI2C_PinLowTimeout; |
| } |
| else if (status & kLPI2C_MasterArbitrationLostFlag) |
| { |
| result = kStatus_LPI2C_ArbitrationLost; |
| } |
| else if (status & kLPI2C_MasterNackDetectFlag) |
| { |
| result = kStatus_LPI2C_Nak; |
| } |
| else if (status & kLPI2C_MasterFifoErrFlag) |
| { |
| result = kStatus_LPI2C_FifoError; |
| } |
| else |
| { |
| assert(false); |
| } |
| |
| /* Clear the flags. */ |
| LPI2C_MasterClearStatusFlags(base, status); |
| |
| /* Reset fifos. These flags clear automatically. */ |
| base->MCR |= LPI2C_MCR_RRF_MASK | LPI2C_MCR_RTF_MASK; |
| } |
| |
| 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) |
| { |
| uint32_t status; |
| size_t txCount; |
| size_t txFifoSize = FSL_FEATURE_LPI2C_FIFO_SIZEn(base); |
| |
| #if LPI2C_WAIT_TIMEOUT |
| uint32_t waitTimes = LPI2C_WAIT_TIMEOUT; |
| #endif |
| do |
| { |
| status_t result; |
| |
| /* 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 (result) |
| { |
| return result; |
| } |
| #if LPI2C_WAIT_TIMEOUT |
| } while ((!txCount) && (--waitTimes)); |
| |
| if (waitTimes == 0) |
| { |
| return kStatus_LPI2C_Timeout; |
| } |
| #else |
| } while (!txCount); |
| #endif |
| |
| return kStatus_Success; |
| } |
| |
| /*! |
| * @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 |
| */ |
| status_t LPI2C_CheckForBusyBus(LPI2C_Type *base) |
| { |
| uint32_t status = LPI2C_MasterGetStatusFlags(base); |
| if ((status & kLPI2C_MasterBusBusyFlag) && (!(status & kLPI2C_MasterBusyFlag))) |
| { |
| return kStatus_LPI2C_Busy; |
| } |
| |
| return kStatus_Success; |
| } |
| |
| void LPI2C_MasterGetDefaultConfig(lpi2c_master_config_t *masterConfig) |
| { |
| masterConfig->enableMaster = true; |
| masterConfig->debugEnable = false; |
| masterConfig->enableDoze = true; |
| 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; |
| } |
| |
| 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. */ |
| 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, kDefaultTxWatermark, kDefaultRxWatermark); |
| |
| LPI2C_MasterSetBaudRate(base, sourceClock_Hz, masterConfig->baudRate_Hz); |
| |
| /* Configure glitch filters and bus idle and pin low timeouts. */ |
| prescaler = (base->MCFGR1 & LPI2C_MCFGR1_PRESCALE_MASK) >> LPI2C_MCFGR1_PRESCALE_SHIFT; |
| cfgr2 = base->MCFGR2; |
| if (masterConfig->busIdleTimeout_ns) |
| { |
| cycles = LPI2C_GetCyclesForWidth(sourceClock_Hz, masterConfig->busIdleTimeout_ns, |
| (LPI2C_MCFGR2_BUSIDLE_MASK >> LPI2C_MCFGR2_BUSIDLE_SHIFT), prescaler); |
| cfgr2 &= ~LPI2C_MCFGR2_BUSIDLE_MASK; |
| cfgr2 |= LPI2C_MCFGR2_BUSIDLE(cycles); |
| } |
| if (masterConfig->sdaGlitchFilterWidth_ns) |
| { |
| cycles = LPI2C_GetCyclesForWidth(sourceClock_Hz, masterConfig->sdaGlitchFilterWidth_ns, |
| (LPI2C_MCFGR2_FILTSDA_MASK >> LPI2C_MCFGR2_FILTSDA_SHIFT), 1); |
| cfgr2 &= ~LPI2C_MCFGR2_FILTSDA_MASK; |
| cfgr2 |= LPI2C_MCFGR2_FILTSDA(cycles); |
| } |
| if (masterConfig->sclGlitchFilterWidth_ns) |
| { |
| cycles = LPI2C_GetCyclesForWidth(sourceClock_Hz, masterConfig->sclGlitchFilterWidth_ns, |
| (LPI2C_MCFGR2_FILTSCL_MASK >> LPI2C_MCFGR2_FILTSCL_SHIFT), 1); |
| cfgr2 &= ~LPI2C_MCFGR2_FILTSCL_MASK; |
| cfgr2 |= LPI2C_MCFGR2_FILTSCL(cycles); |
| } |
| base->MCFGR2 = cfgr2; |
| if (masterConfig->pinLowTimeout_ns) |
| { |
| cycles = LPI2C_GetCyclesForWidth(sourceClock_Hz, masterConfig->pinLowTimeout_ns / 256, |
| (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); |
| } |
| |
| 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. */ |
| 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 */ |
| } |
| |
| void LPI2C_MasterConfigureDataMatch(LPI2C_Type *base, const lpi2c_data_match_config_t *config) |
| { |
| /* Disable master mode. */ |
| bool wasEnabled = (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(config->matchMode); |
| base->MCFGR0 = (base->MCFGR0 & ~LPI2C_MCFGR0_RDMO_MASK) | LPI2C_MCFGR0_RDMO(config->rxDataMatchOnly); |
| base->MDMR = LPI2C_MDMR_MATCH0(config->match0) | LPI2C_MDMR_MATCH1(config->match1); |
| |
| /* Restore master mode. */ |
| if (wasEnabled) |
| { |
| LPI2C_MasterEnable(base, true); |
| } |
| } |
| |
| void LPI2C_MasterSetBaudRate(LPI2C_Type *base, uint32_t sourceClock_Hz, uint32_t baudRate_Hz) |
| { |
| uint32_t prescale = 0; |
| uint32_t bestPre = 0; |
| uint32_t bestClkHi = 0; |
| uint32_t absError = 0; |
| uint32_t bestError = 0xffffffffu; |
| uint32_t value; |
| uint32_t clkHiCycle; |
| uint32_t computedRate; |
| int i; |
| bool wasEnabled; |
| |
| /* Disable master mode. */ |
| wasEnabled = (base->MCR & LPI2C_MCR_MEN_MASK) >> LPI2C_MCR_MEN_SHIFT; |
| LPI2C_MasterEnable(base, false); |
| |
| /* Baud rate = (sourceClock_Hz/2^prescale)/(CLKLO+1+CLKHI+1 + ROUNDDOWN((2+FILTSCL)/2^prescale) */ |
| /* Assume CLKLO = 2*CLKHI, SETHOLD = CLKHI, DATAVD = CLKHI/2. */ |
| for (prescale = 1; (prescale <= 128) && (bestError != 0); prescale = 2 * prescale) |
| { |
| for (clkHiCycle = 1; clkHiCycle < 32; clkHiCycle++) |
| { |
| if (clkHiCycle == 1) |
| { |
| computedRate = (sourceClock_Hz / prescale) / (1 + 3 + 2 + 2 / prescale); |
| } |
| else |
| { |
| computedRate = (sourceClock_Hz / prescale) / (3 * clkHiCycle + 2 + 2 / prescale); |
| } |
| |
| absError = baudRate_Hz > computedRate ? baudRate_Hz - computedRate : computedRate - baudRate_Hz; |
| |
| if (absError < bestError) |
| { |
| bestPre = prescale; |
| bestClkHi = clkHiCycle; |
| bestError = absError; |
| |
| /* If the error is 0, then we can stop searching because we won't find a better match. */ |
| if (absError == 0) |
| { |
| break; |
| } |
| } |
| } |
| } |
| |
| /* Standard, fast, fast mode plus and ultra-fast transfers. */ |
| value = LPI2C_MCCR0_CLKHI(bestClkHi); |
| |
| if (bestClkHi < 2) |
| { |
| value |= LPI2C_MCCR0_CLKLO(3) | LPI2C_MCCR0_SETHOLD(2) | LPI2C_MCCR0_DATAVD(1); |
| } |
| else |
| { |
| value |= LPI2C_MCCR0_CLKLO(2 * bestClkHi) | LPI2C_MCCR0_SETHOLD(bestClkHi) | LPI2C_MCCR0_DATAVD(bestClkHi / 2); |
| } |
| |
| base->MCCR0 = value; |
| |
| for (i = 0; i < 8; i++) |
| { |
| if (bestPre == (1U << i)) |
| { |
| bestPre = i; |
| break; |
| } |
| } |
| base->MCFGR1 = (base->MCFGR1 & ~LPI2C_MCFGR1_PRESCALE_MASK) | LPI2C_MCFGR1_PRESCALE(bestPre); |
| |
| /* Restore master mode. */ |
| if (wasEnabled) |
| { |
| LPI2C_MasterEnable(base, true); |
| } |
| } |
| |
| 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 (result) |
| { |
| return result; |
| } |
| |
| /* Clear all flags. */ |
| LPI2C_MasterClearStatusFlags(base, kMasterClearFlags); |
| |
| /* Turn off auto-stop option. */ |
| base->MCFGR1 &= ~LPI2C_MCFGR1_AUTOSTOP_MASK; |
| |
| /* Wait until there is room in the fifo. */ |
| result = LPI2C_MasterWaitForTxReady(base); |
| if (result) |
| { |
| return result; |
| } |
| |
| /* Issue start command. */ |
| base->MTDR = kStartCmd | (((uint32_t)address << 1U) | (uint32_t)dir); |
| |
| return kStatus_Success; |
| } |
| |
| status_t LPI2C_MasterStop(LPI2C_Type *base) |
| { |
| /* Wait until there is room in the fifo. */ |
| status_t result = LPI2C_MasterWaitForTxReady(base); |
| if (result) |
| { |
| return result; |
| } |
| |
| /* Send the STOP signal */ |
| base->MTDR = kStopCmd; |
| |
| /* Wait for the stop detected flag to set, indicating the transfer has completed on the bus. */ |
| /* Also check for errors while waiting. */ |
| #if LPI2C_WAIT_TIMEOUT |
| uint32_t waitTimes = LPI2C_WAIT_TIMEOUT; |
| #endif |
| |
| #if LPI2C_WAIT_TIMEOUT |
| while ((result == kStatus_Success) && (--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 (status & kLPI2C_MasterStopDetectFlag) |
| { |
| LPI2C_MasterClearStatusFlags(base, kLPI2C_MasterStopDetectFlag); |
| break; |
| } |
| } |
| |
| #if LPI2C_WAIT_TIMEOUT |
| if (waitTimes == 0) |
| { |
| return kStatus_LPI2C_Timeout; |
| } |
| #endif |
| |
| return result; |
| } |
| |
| status_t LPI2C_MasterReceive(LPI2C_Type *base, void *rxBuff, size_t rxSize) |
| { |
| status_t result; |
| uint8_t *buf; |
| |
| assert(rxBuff); |
| |
| /* Handle empty read. */ |
| if (!rxSize) |
| { |
| return kStatus_Success; |
| } |
| |
| /* Wait until there is room in the command fifo. */ |
| result = LPI2C_MasterWaitForTxReady(base); |
| if (result) |
| { |
| return result; |
| } |
| |
| /* Issue command to receive data. */ |
| base->MTDR = kRxDataCmd | LPI2C_MTDR_DATA(rxSize - 1); |
| |
| #if LPI2C_WAIT_TIMEOUT |
| uint32_t waitTimes = LPI2C_WAIT_TIMEOUT; |
| #endif |
| |
| /* Receive data */ |
| buf = (uint8_t *)rxBuff; |
| while (rxSize--) |
| { |
| /* 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; |
| do |
| { |
| /* Check for errors. */ |
| result = LPI2C_MasterCheckAndClearError(base, LPI2C_MasterGetStatusFlags(base)); |
| if (result) |
| { |
| return result; |
| } |
| |
| value = base->MRDR; |
| #if LPI2C_WAIT_TIMEOUT |
| } while ((value & LPI2C_MRDR_RXEMPTY_MASK) && (--waitTimes)); |
| if (waitTimes == 0) |
| { |
| return kStatus_LPI2C_Timeout; |
| } |
| #else |
| } while (value & LPI2C_MRDR_RXEMPTY_MASK); |
| #endif |
| |
| *buf++ = value & LPI2C_MRDR_DATA_MASK; |
| } |
| |
| return kStatus_Success; |
| } |
| |
| status_t LPI2C_MasterSend(LPI2C_Type *base, const void *txBuff, size_t txSize) |
| { |
| uint8_t *buf = (uint8_t *)((void *)txBuff); |
| |
| assert(txBuff); |
| |
| /* Send data buffer */ |
| while (txSize--) |
| { |
| /* Wait until there is room in the fifo. This also checks for errors. */ |
| status_t result = LPI2C_MasterWaitForTxReady(base); |
| if (result) |
| { |
| return result; |
| } |
| |
| /* Write byte into LPI2C master data register. */ |
| base->MTDR = *buf++; |
| } |
| |
| return kStatus_Success; |
| } |
| |
| status_t LPI2C_MasterTransferBlocking(LPI2C_Type *base, lpi2c_master_transfer_t *transfer) |
| { |
| status_t result = kStatus_Success; |
| uint16_t commandBuffer[7]; |
| uint32_t cmdCount = 0; |
| |
| assert(transfer); |
| assert(transfer->subaddressSize <= sizeof(transfer->subaddress)); |
| |
| /* Return an error if the bus is already in use not by us. */ |
| result = LPI2C_CheckForBusyBus(base); |
| if (result) |
| { |
| return result; |
| } |
| |
| /* Clear all flags. */ |
| LPI2C_MasterClearStatusFlags(base, kMasterClearFlags); |
| |
| /* Turn off auto-stop option. */ |
| base->MCFGR1 &= ~LPI2C_MCFGR1_AUTOSTOP_MASK; |
| |
| lpi2c_direction_t direction = transfer->subaddressSize ? kLPI2C_Write : transfer->direction; |
| if (!(transfer->flags & kLPI2C_TransferNoStartFlag)) |
| { |
| commandBuffer[cmdCount++] = |
| (uint16_t)kStartCmd | (uint16_t)((uint16_t)((uint16_t)transfer->slaveAddress << 1U) | (uint16_t)direction); |
| } |
| |
| /* Subaddress, MSB first. */ |
| if (transfer->subaddressSize) |
| { |
| uint32_t subaddressRemaining = transfer->subaddressSize; |
| while (subaddressRemaining--) |
| { |
| uint8_t subaddressByte = (transfer->subaddress >> (8 * subaddressRemaining)) & 0xff; |
| commandBuffer[cmdCount++] = subaddressByte; |
| } |
| } |
| |
| /* Reads need special handling. */ |
| if ((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 = 0; |
| while (cmdCount--) |
| { |
| /* Wait until there is room in the fifo. This also checks for errors. */ |
| result = LPI2C_MasterWaitForTxReady(base); |
| if (result) |
| { |
| return result; |
| } |
| |
| /* Write byte into LPI2C master data register. */ |
| base->MTDR = commandBuffer[index]; |
| index++; |
| } |
| |
| /* Transmit data. */ |
| if ((transfer->direction == kLPI2C_Write) && (transfer->dataSize > 0)) |
| { |
| /* Send Data. */ |
| result = LPI2C_MasterSend(base, transfer->data, transfer->dataSize); |
| } |
| |
| /* Receive Data. */ |
| if ((transfer->direction == kLPI2C_Read) && (transfer->dataSize > 0)) |
| { |
| result = LPI2C_MasterReceive(base, transfer->data, transfer->dataSize); |
| } |
| |
| if (result) |
| { |
| return result; |
| } |
| |
| if ((transfer->flags & kLPI2C_TransferNoStopFlag) == 0) |
| { |
| result = LPI2C_MasterStop(base); |
| } |
| |
| return result; |
| } |
| |
| void LPI2C_MasterTransferCreateHandle(LPI2C_Type *base, |
| lpi2c_master_handle_t *handle, |
| lpi2c_master_transfer_callback_t callback, |
| void *userData) |
| { |
| uint32_t instance; |
| |
| assert(handle); |
| |
| /* Clear out the handle. */ |
| 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, kMasterIrqFlags); |
| EnableIRQ(kLpi2cIrqs[instance]); |
| } |
| |
| /*! |
| * @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) |
| { |
| uint32_t status; |
| status_t result = kStatus_Success; |
| lpi2c_master_transfer_t *xfer; |
| size_t txCount; |
| size_t rxCount; |
| size_t txFifoSize = FSL_FEATURE_LPI2C_FIFO_SIZEn(base); |
| bool state_complete = false; |
| |
| /* Set default isDone return value. */ |
| *isDone = false; |
| |
| /* Check for errors. */ |
| status = LPI2C_MasterGetStatusFlags(base); |
| result = LPI2C_MasterCheckAndClearError(base, status); |
| if (result) |
| { |
| return result; |
| } |
| |
| /* Get pointer to private data. */ |
| xfer = &handle->transfer; |
| |
| /* Get fifo counts and compute room in tx fifo. */ |
| LPI2C_MasterGetFifoCounts(base, &rxCount, &txCount); |
| txCount = txFifoSize - txCount; |
| |
| while (!state_complete) |
| { |
| /* Execute the state. */ |
| switch (handle->state) |
| { |
| case kSendCommandState: |
| { |
| /* Make sure there is room in the tx fifo for the next command. */ |
| if (!txCount--) |
| { |
| state_complete = true; |
| break; |
| } |
| |
| /* Issue command. buf is a uint8_t* pointing at the uint16 command array. */ |
| base->MTDR = *(uint16_t *)handle->buf; |
| handle->buf += sizeof(uint16_t); |
| |
| /* Count down until all commands are sent. */ |
| if (--handle->remainingBytes == 0) |
| { |
| /* Choose next state and set up buffer pointer and count. */ |
| if (xfer->dataSize) |
| { |
| /* Either a send or receive transfer is next. */ |
| handle->state = kTransferDataState; |
| handle->buf = (uint8_t *)xfer->data; |
| handle->remainingBytes = xfer->dataSize; |
| if (xfer->direction == kLPI2C_Read) |
| { |
| /* Disable TX interrupt */ |
| LPI2C_MasterDisableInterrupts(base, kLPI2C_MasterTxReadyFlag); |
| } |
| } |
| else |
| { |
| /* No transfer, so move to stop state. */ |
| handle->state = kStopState; |
| } |
| } |
| break; |
| } |
| |
| case kIssueReadCommandState: |
| /* Make sure there is room in the tx fifo for the read command. */ |
| if (!txCount--) |
| { |
| state_complete = true; |
| break; |
| } |
| |
| base->MTDR = kRxDataCmd | LPI2C_MTDR_DATA(xfer->dataSize - 1); |
| |
| /* Move to transfer state. */ |
| handle->state = kTransferDataState; |
| if (xfer->direction == kLPI2C_Read) |
| { |
| /* Disable TX interrupt */ |
| LPI2C_MasterDisableInterrupts(base, kLPI2C_MasterTxReadyFlag); |
| } |
| break; |
| |
| case kTransferDataState: |
| if (xfer->direction == kLPI2C_Write) |
| { |
| /* Make sure there is room in the tx fifo. */ |
| if (!txCount--) |
| { |
| state_complete = true; |
| break; |
| } |
| |
| /* 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 (!rxCount--) |
| { |
| state_complete = true; |
| break; |
| } |
| |
| /* Read byte from fifo. */ |
| *(handle->buf)++ = base->MRDR & LPI2C_MRDR_DATA_MASK; |
| } |
| |
| /* Move to stop when the transfer is done. */ |
| if (--handle->remainingBytes == 0) |
| { |
| handle->state = kStopState; |
| } |
| break; |
| |
| case kStopState: |
| /* Only issue a stop transition if the caller requested it. */ |
| if ((xfer->flags & kLPI2C_TransferNoStopFlag) == 0) |
| { |
| /* Make sure there is room in the tx fifo for the stop command. */ |
| if (!txCount--) |
| { |
| state_complete = true; |
| break; |
| } |
| |
| base->MTDR = kStopCmd; |
| } |
| else |
| { |
| /* Caller doesn't want to send a stop, so we're done now. */ |
| *isDone = true; |
| state_complete = true; |
| break; |
| } |
| handle->state = kWaitForCompletionState; |
| break; |
| |
| case kWaitForCompletionState: |
| /* We stay in this state until the stop state is detected. */ |
| if (status & kLPI2C_MasterStopDetectFlag) |
| { |
| *isDone = true; |
| } |
| state_complete = true; |
| 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 (xfer->flags & kLPI2C_TransferNoStartFlag) |
| { |
| if (xfer->direction == kLPI2C_Read) |
| { |
| /* Need to issue read command first. */ |
| handle->state = kIssueReadCommandState; |
| } |
| else |
| { |
| /* Start immediately in the data transfer state. */ |
| handle->state = kTransferDataState; |
| } |
| |
| handle->buf = (uint8_t *)xfer->data; |
| handle->remainingBytes = xfer->dataSize; |
| } |
| else |
| { |
| uint16_t *cmd = (uint16_t *)&handle->commandBuffer; |
| uint32_t cmdCount = 0; |
| |
| /* Initial direction depends on whether a subaddress was provided, and of course the actual */ |
| /* data transfer direction. */ |
| lpi2c_direction_t direction = 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 (xfer->subaddressSize) |
| { |
| uint32_t subaddressRemaining = xfer->subaddressSize; |
| while (subaddressRemaining--) |
| { |
| uint8_t subaddressByte = (xfer->subaddress >> (8 * subaddressRemaining)) & 0xff; |
| cmd[cmdCount++] = subaddressByte; |
| } |
| } |
| |
| /* Reads need special handling. */ |
| if ((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); |
| } |
| |
| /* Read command. */ |
| cmd[cmdCount++] = kRxDataCmd | LPI2C_MTDR_DATA(xfer->dataSize - 1); |
| } |
| |
| /* Set up state machine for transferring the commands. */ |
| handle->state = kSendCommandState; |
| handle->remainingBytes = cmdCount; |
| handle->buf = (uint8_t *)&handle->commandBuffer; |
| } |
| } |
| |
| status_t LPI2C_MasterTransferNonBlocking(LPI2C_Type *base, |
| lpi2c_master_handle_t *handle, |
| lpi2c_master_transfer_t *transfer) |
| { |
| status_t result; |
| |
| assert(handle); |
| assert(transfer); |
| assert(transfer->subaddressSize <= sizeof(transfer->subaddress)); |
| |
| /* Return busy if another transaction is in progress. */ |
| if (handle->state != kIdleState) |
| { |
| return kStatus_LPI2C_Busy; |
| } |
| |
| /* Return an error if the bus is already in use not by us. */ |
| result = LPI2C_CheckForBusyBus(base); |
| if (result) |
| { |
| return result; |
| } |
| |
| /* Disable LPI2C IRQ sources while we configure stuff. */ |
| LPI2C_MasterDisableInterrupts(base, kMasterIrqFlags); |
| |
| /* Save transfer into handle. */ |
| handle->transfer = *transfer; |
| |
| /* Generate commands to send. */ |
| LPI2C_InitTransferStateMachine(handle); |
| |
| /* Clear all flags. */ |
| LPI2C_MasterClearStatusFlags(base, kMasterClearFlags); |
| |
| /* 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, kMasterIrqFlags); |
| |
| return result; |
| } |
| |
| status_t LPI2C_MasterTransferGetCount(LPI2C_Type *base, lpi2c_master_handle_t *handle, size_t *count) |
| { |
| assert(handle); |
| |
| if (!count) |
| { |
| return kStatus_InvalidArgument; |
| } |
| |
| /* Catch when there is not an active transfer. */ |
| if (handle->state == kIdleState) |
| { |
| *count = 0; |
| return kStatus_NoTransferInProgress; |
| } |
| |
| 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 kIdleState: |
| case kSendCommandState: |
| case kIssueReadCommandState: /* XXX return correct value for this state when >256 reads are supported */ |
| *count = 0; |
| break; |
| |
| case kTransferDataState: |
| *count = dataSize - remainingBytes; |
| break; |
| |
| case kStopState: |
| case kWaitForCompletionState: |
| default: |
| *count = dataSize; |
| break; |
| } |
| |
| return kStatus_Success; |
| } |
| |
| void LPI2C_MasterTransferAbort(LPI2C_Type *base, lpi2c_master_handle_t *handle) |
| { |
| if (handle->state != kIdleState) |
| { |
| /* Disable internal IRQ enables. */ |
| LPI2C_MasterDisableInterrupts(base, kMasterIrqFlags); |
| |
| /* Reset fifos. */ |
| base->MCR |= LPI2C_MCR_RRF_MASK | LPI2C_MCR_RTF_MASK; |
| |
| /* Send a stop command to finalize the transfer. */ |
| base->MTDR = kStopCmd; |
| |
| /* Reset handle. */ |
| handle->state = kIdleState; |
| } |
| } |
| |
| void LPI2C_MasterTransferHandleIRQ(LPI2C_Type *base, lpi2c_master_handle_t *handle) |
| { |
| bool isDone; |
| status_t result; |
| |
| /* Don't do anything if we don't have a valid handle. */ |
| if (!handle) |
| { |
| return; |
| } |
| |
| if (handle->state == kIdleState) |
| { |
| return; |
| } |
| |
| result = LPI2C_RunTransferStateMachine(base, handle, &isDone); |
| |
| if (isDone || (result != kStatus_Success)) |
| { |
| /* XXX need to handle data that may be in rx fifo below watermark level? */ |
| |
| /* XXX handle error, terminate xfer */ |
| |
| /* Disable internal IRQ enables. */ |
| LPI2C_MasterDisableInterrupts(base, kMasterIrqFlags); |
| |
| /* Set handle to idle state. */ |
| handle->state = kIdleState; |
| |
| /* Invoke callback. */ |
| if (handle->completionCallback) |
| { |
| handle->completionCallback(base, handle, result, handle->userData); |
| } |
| } |
| } |
| |
| void LPI2C_SlaveGetDefaultConfig(lpi2c_slave_config_t *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 = 0; /* TODO determine default width values */ |
| slaveConfig->sclGlitchFilterWidth_ns = 0; |
| slaveConfig->dataValidDelay_ns = 0; |
| slaveConfig->clockHoldTime_ns = 0; |
| } |
| |
| void LPI2C_SlaveInit(LPI2C_Type *base, const lpi2c_slave_config_t *slaveConfig, uint32_t sourceClock_Hz) |
| { |
| #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) |
| |
| uint32_t instance = LPI2C_GetInstance(base); |
| |
| /* Ungate the clock. */ |
| 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); |
| |
| base->SCFGR2 = |
| LPI2C_SCFGR2_FILTSDA(LPI2C_GetCyclesForWidth(sourceClock_Hz, slaveConfig->sdaGlitchFilterWidth_ns, |
| (LPI2C_SCFGR2_FILTSDA_MASK >> LPI2C_SCFGR2_FILTSDA_SHIFT), 1)) | |
| LPI2C_SCFGR2_FILTSCL(LPI2C_GetCyclesForWidth(sourceClock_Hz, slaveConfig->sclGlitchFilterWidth_ns, |
| (LPI2C_SCFGR2_FILTSCL_MASK >> LPI2C_SCFGR2_FILTSCL_SHIFT), 1)) | |
| LPI2C_SCFGR2_DATAVD(LPI2C_GetCyclesForWidth(sourceClock_Hz, slaveConfig->dataValidDelay_ns, |
| (LPI2C_SCFGR2_DATAVD_MASK >> LPI2C_SCFGR2_DATAVD_SHIFT), 1)) | |
| LPI2C_SCFGR2_CLKHOLD(LPI2C_GetCyclesForWidth(sourceClock_Hz, slaveConfig->clockHoldTime_ns, |
| (LPI2C_SCFGR2_CLKHOLD_MASK >> LPI2C_SCFGR2_CLKHOLD_SHIFT), 1)); |
| |
| /* 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); |
| } |
| |
| 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. */ |
| 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 &= kSlaveErrorFlags; |
| if (flags) |
| { |
| if (flags & kLPI2C_SlaveBitErrFlag) |
| { |
| result = kStatus_LPI2C_BitError; |
| } |
| else if (flags & kLPI2C_SlaveFifoErrFlag) |
| { |
| result = kStatus_LPI2C_FifoError; |
| } |
| else |
| { |
| assert(false); |
| } |
| |
| /* Clear the errors. */ |
| LPI2C_SlaveClearStatusFlags(base, flags); |
| } |
| |
| return result; |
| } |
| |
| status_t LPI2C_SlaveSend(LPI2C_Type *base, const void *txBuff, size_t txSize, size_t *actualTxSize) |
| { |
| uint8_t *buf = (uint8_t *)((void *)txBuff); |
| size_t remaining = txSize; |
| |
| assert(txBuff); |
| |
| #if LPI2C_WAIT_TIMEOUT |
| uint32_t waitTimes = LPI2C_WAIT_TIMEOUT; |
| #endif |
| |
| while (remaining) |
| { |
| uint32_t flags; |
| status_t result; |
| |
| /* Wait until we can transmit. */ |
| do |
| { |
| /* Check for errors */ |
| flags = LPI2C_SlaveGetStatusFlags(base); |
| result = LPI2C_SlaveCheckAndClearError(base, flags); |
| if (result) |
| { |
| if (actualTxSize) |
| { |
| *actualTxSize = txSize - remaining; |
| } |
| return result; |
| } |
| #if LPI2C_WAIT_TIMEOUT |
| } while ( |
| (!(flags & (kLPI2C_SlaveTxReadyFlag | kLPI2C_SlaveStopDetectFlag | kLPI2C_SlaveRepeatedStartDetectFlag))) && |
| (--waitTimes)); |
| if (waitTimes == 0) |
| { |
| return kStatus_LPI2C_Timeout; |
| } |
| #else |
| } while ( |
| !(flags & (kLPI2C_SlaveTxReadyFlag | kLPI2C_SlaveStopDetectFlag | kLPI2C_SlaveRepeatedStartDetectFlag))); |
| #endif |
| |
| /* Send a byte. */ |
| if (flags & kLPI2C_SlaveTxReadyFlag) |
| { |
| base->STDR = *buf++; |
| --remaining; |
| } |
| |
| /* Exit loop if we see a stop or restart */ |
| if (flags & (kLPI2C_SlaveStopDetectFlag | kLPI2C_SlaveRepeatedStartDetectFlag)) |
| { |
| LPI2C_SlaveClearStatusFlags(base, kLPI2C_SlaveStopDetectFlag | kLPI2C_SlaveRepeatedStartDetectFlag); |
| break; |
| } |
| } |
| |
| if (actualTxSize) |
| { |
| *actualTxSize = txSize - remaining; |
| } |
| |
| return kStatus_Success; |
| } |
| |
| status_t LPI2C_SlaveReceive(LPI2C_Type *base, void *rxBuff, size_t rxSize, size_t *actualRxSize) |
| { |
| uint8_t *buf = (uint8_t *)rxBuff; |
| size_t remaining = rxSize; |
| |
| assert(rxBuff); |
| |
| #if LPI2C_WAIT_TIMEOUT |
| uint32_t waitTimes = LPI2C_WAIT_TIMEOUT; |
| #endif |
| |
| while (remaining) |
| { |
| uint32_t flags; |
| status_t result; |
| |
| /* Wait until we can receive. */ |
| do |
| { |
| /* Check for errors */ |
| flags = LPI2C_SlaveGetStatusFlags(base); |
| result = LPI2C_SlaveCheckAndClearError(base, flags); |
| if (result) |
| { |
| if (actualRxSize) |
| { |
| *actualRxSize = rxSize - remaining; |
| } |
| return result; |
| } |
| #if LPI2C_WAIT_TIMEOUT |
| } while ( |
| (!(flags & (kLPI2C_SlaveRxReadyFlag | kLPI2C_SlaveStopDetectFlag | kLPI2C_SlaveRepeatedStartDetectFlag))) && |
| (--waitTimes)); |
| if (waitTimes == 0) |
| { |
| return kStatus_LPI2C_Timeout; |
| } |
| #else |
| } while ( |
| !(flags & (kLPI2C_SlaveRxReadyFlag | kLPI2C_SlaveStopDetectFlag | kLPI2C_SlaveRepeatedStartDetectFlag))); |
| #endif |
| |
| /* Receive a byte. */ |
| if (flags & kLPI2C_SlaveRxReadyFlag) |
| { |
| *buf++ = base->SRDR & LPI2C_SRDR_DATA_MASK; |
| --remaining; |
| } |
| |
| /* Exit loop if we see a stop or restart */ |
| if (flags & (kLPI2C_SlaveStopDetectFlag | kLPI2C_SlaveRepeatedStartDetectFlag)) |
| { |
| LPI2C_SlaveClearStatusFlags(base, kLPI2C_SlaveStopDetectFlag | kLPI2C_SlaveRepeatedStartDetectFlag); |
| break; |
| } |
| } |
| |
| if (actualRxSize) |
| { |
| *actualRxSize = rxSize - remaining; |
| } |
| |
| return kStatus_Success; |
| } |
| |
| void LPI2C_SlaveTransferCreateHandle(LPI2C_Type *base, |
| lpi2c_slave_handle_t *handle, |
| lpi2c_slave_transfer_callback_t callback, |
| void *userData) |
| { |
| uint32_t instance; |
| |
| assert(handle); |
| |
| /* Clear out the handle. */ |
| 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, kSlaveIrqFlags); |
| EnableIRQ(kLpi2cIrqs[instance]); |
| |
| /* Nack by default. */ |
| base->STAR = LPI2C_STAR_TXNACK_MASK; |
| } |
| |
| status_t LPI2C_SlaveTransferNonBlocking(LPI2C_Type *base, lpi2c_slave_handle_t *handle, uint32_t eventMask) |
| { |
| uint32_t status; |
| |
| assert(handle); |
| |
| /* Return busy if another transaction is in progress. */ |
| if (handle->isBusy) |
| { |
| return kStatus_LPI2C_Busy; |
| } |
| |
| /* Return an error if the bus is already in use not by us. */ |
| status = LPI2C_SlaveGetStatusFlags(base); |
| if ((status & kLPI2C_SlaveBusBusyFlag) && (!(status & kLPI2C_SlaveBusyFlag))) |
| { |
| return kStatus_LPI2C_Busy; |
| } |
| |
| /* Disable LPI2C IRQ sources while we configure stuff. */ |
| LPI2C_SlaveDisableInterrupts(base, kSlaveIrqFlags); |
| |
| /* Clear transfer in handle. */ |
| 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 | kLPI2C_SlaveTransmitEvent | kLPI2C_SlaveReceiveEvent; |
| |
| /* Ack by default. */ |
| base->STAR = 0; |
| |
| /* Clear all flags. */ |
| LPI2C_SlaveClearStatusFlags(base, kSlaveClearFlags); |
| |
| /* Enable LPI2C internal IRQ sources. NVIC IRQ was enabled in CreateHandle() */ |
| LPI2C_SlaveEnableInterrupts(base, kSlaveIrqFlags); |
| |
| return kStatus_Success; |
| } |
| |
| status_t LPI2C_SlaveTransferGetCount(LPI2C_Type *base, lpi2c_slave_handle_t *handle, size_t *count) |
| { |
| assert(handle); |
| |
| if (!count) |
| { |
| return kStatus_InvalidArgument; |
| } |
| |
| /* Catch when there is not an active transfer. */ |
| if (!handle->isBusy) |
| { |
| *count = 0; |
| return kStatus_NoTransferInProgress; |
| } |
| |
| /* For an active transfer, just return the count from the handle. */ |
| *count = handle->transferredCount; |
| |
| return kStatus_Success; |
| } |
| |
| void LPI2C_SlaveTransferAbort(LPI2C_Type *base, lpi2c_slave_handle_t *handle) |
| { |
| assert(handle); |
| |
| /* Return idle if no transaction is in progress. */ |
| if (handle->isBusy) |
| { |
| /* Disable LPI2C IRQ sources. */ |
| LPI2C_SlaveDisableInterrupts(base, kSlaveIrqFlags); |
| |
| /* Nack by default. */ |
| base->STAR = LPI2C_STAR_TXNACK_MASK; |
| |
| /* Reset transfer info. */ |
| memset(&handle->transfer, 0, sizeof(handle->transfer)); |
| |
| /* We're no longer busy. */ |
| handle->isBusy = false; |
| } |
| } |
| |
| 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 (!handle) |
| { |
| return; |
| } |
| |
| xfer = &handle->transfer; |
| |
| /* Get status flags. */ |
| flags = LPI2C_SlaveGetStatusFlags(base); |
| |
| if (flags & (kLPI2C_SlaveBitErrFlag | kLPI2C_SlaveFifoErrFlag)) |
| { |
| xfer->event = kLPI2C_SlaveCompletionEvent; |
| xfer->completionStatus = LPI2C_SlaveCheckAndClearError(base, flags); |
| |
| if ((handle->eventMask & kLPI2C_SlaveCompletionEvent) && (handle->callback)) |
| { |
| handle->callback(base, xfer, handle->userData); |
| } |
| return; |
| } |
| if (flags & (kLPI2C_SlaveRepeatedStartDetectFlag | kLPI2C_SlaveStopDetectFlag)) |
| { |
| xfer->event = (flags & kLPI2C_SlaveRepeatedStartDetectFlag) ? kLPI2C_SlaveRepeatedStartEvent : |
| kLPI2C_SlaveCompletionEvent; |
| xfer->receivedAddress = 0; |
| 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 & (kLPI2C_SlaveRepeatedStartDetectFlag | kLPI2C_SlaveStopDetectFlag)); |
| |
| /* Revert to sending an Ack by default, in case we sent a Nack for receive. */ |
| base->STAR = 0; |
| |
| if ((handle->eventMask & xfer->event) && (handle->callback)) |
| { |
| handle->callback(base, xfer, handle->userData); |
| } |
| |
| /* Clean up transfer info on completion, after the callback has been invoked. */ |
| memset(&handle->transfer, 0, sizeof(handle->transfer)); |
| } |
| if (flags & kLPI2C_SlaveAddressValidFlag) |
| { |
| xfer->event = kLPI2C_SlaveAddressMatchEvent; |
| xfer->receivedAddress = base->SASR & LPI2C_SASR_RADDR_MASK; |
| |
| if ((handle->eventMask & kLPI2C_SlaveAddressMatchEvent) && (handle->callback)) |
| { |
| handle->callback(base, xfer, handle->userData); |
| } |
| } |
| if (flags & kLPI2C_SlaveTransmitAckFlag) |
| { |
| xfer->event = kLPI2C_SlaveTransmitAckEvent; |
| |
| if ((handle->eventMask & kLPI2C_SlaveTransmitAckEvent) && (handle->callback)) |
| { |
| handle->callback(base, xfer, handle->userData); |
| } |
| } |
| |
| /* Handle transmit and receive. */ |
| if (flags & kLPI2C_SlaveTxReadyFlag) |
| { |
| handle->wasTransmit = true; |
| |
| /* If we're out of data, invoke callback to get more. */ |
| if ((!xfer->data) || (!xfer->dataSize)) |
| { |
| xfer->event = kLPI2C_SlaveTransmitEvent; |
| if (handle->callback) |
| { |
| handle->callback(base, xfer, handle->userData); |
| } |
| |
| /* Clear the transferred count now that we have a new buffer. */ |
| handle->transferredCount = 0; |
| } |
| |
| /* Transmit a byte. */ |
| if ((xfer->data) && (xfer->dataSize)) |
| { |
| base->STDR = *xfer->data++; |
| --xfer->dataSize; |
| ++handle->transferredCount; |
| } |
| } |
| if (flags & kLPI2C_SlaveRxReadyFlag) |
| { |
| /* If we're out of room in the buffer, invoke callback to get another. */ |
| if ((!xfer->data) || (!xfer->dataSize)) |
| { |
| xfer->event = kLPI2C_SlaveReceiveEvent; |
| if (handle->callback) |
| { |
| handle->callback(base, xfer, handle->userData); |
| } |
| |
| /* Clear the transferred count now that we have a new buffer. */ |
| handle->transferredCount = 0; |
| } |
| |
| /* Receive a byte. */ |
| if ((xfer->data) && (xfer->dataSize)) |
| { |
| *xfer->data++ = 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; |
| } |
| } |
| } |
| |
| /*! |
| * @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 ((base->MCR & LPI2C_MCR_MEN_MASK) && s_lpi2cMasterIsr) |
| { |
| /* Master mode. */ |
| s_lpi2cMasterIsr(base, s_lpi2cMasterHandle[instance]); |
| } |
| |
| /* Check for slave IRQ. */ |
| if ((base->SCR & LPI2C_SCR_SEN_MASK) && s_lpi2cSlaveIsr) |
| { |
| /* Slave mode. */ |
| s_lpi2cSlaveIsr(base, s_lpi2cSlaveHandle[instance]); |
| } |
| /* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping |
| exception return operation might vector to incorrect interrupt */ |
| #if defined __CORTEX_M && (__CORTEX_M == 4U) |
| __DSB(); |
| #endif |
| } |
| |
| #if defined(LPI2C0) |
| /* Implementation of LPI2C0 handler named in startup code. */ |
| void LPI2C0_DriverIRQHandler(void) |
| { |
| LPI2C_CommonIRQHandler(LPI2C0, 0); |
| } |
| #endif |
| |
| #if defined(LPI2C1) |
| /* Implementation of LPI2C1 handler named in startup code. */ |
| void LPI2C1_DriverIRQHandler(void) |
| { |
| LPI2C_CommonIRQHandler(LPI2C1, 1); |
| } |
| #endif |
| |
| #if defined(LPI2C2) |
| /* Implementation of LPI2C2 handler named in startup code. */ |
| void LPI2C2_DriverIRQHandler(void) |
| { |
| LPI2C_CommonIRQHandler(LPI2C2, 2); |
| } |
| #endif |
| |
| #if defined(LPI2C3) |
| /* Implementation of LPI2C3 handler named in startup code. */ |
| void LPI2C3_DriverIRQHandler(void) |
| { |
| LPI2C_CommonIRQHandler(LPI2C3, 3); |
| } |
| #endif |
| |
| #if defined(CM4_0__LPI2C) |
| /* Implementation of CM4_0__LPI2C handler named in startup code. */ |
| void M4_0_LPI2C_DriverIRQHandler(void) |
| { |
| LPI2C_CommonIRQHandler(CM4_0__LPI2C, LPI2C_GetInstance(CM4_0__LPI2C)); |
| } |
| #endif |
| |
| #if defined(CM4_1__LPI2C) |
| /* Implementation of CM4_1__LPI2C handler named in startup code. */ |
| 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) |
| { |
| 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) |
| { |
| 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) |
| { |
| 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) |
| { |
| 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) |
| { |
| LPI2C_CommonIRQHandler(DMA__LPI2C4, LPI2C_GetInstance(DMA__LPI2C4)); |
| } |
| #endif |