diff options
Diffstat (limited to 'bsps/arm/imxrt/nxp/devices/MIMXRT1052/drivers/fsl_lpi2c_edma.c')
-rw-r--r-- | bsps/arm/imxrt/nxp/devices/MIMXRT1052/drivers/fsl_lpi2c_edma.c | 518 |
1 files changed, 0 insertions, 518 deletions
diff --git a/bsps/arm/imxrt/nxp/devices/MIMXRT1052/drivers/fsl_lpi2c_edma.c b/bsps/arm/imxrt/nxp/devices/MIMXRT1052/drivers/fsl_lpi2c_edma.c deleted file mode 100644 index c325405bf9..0000000000 --- a/bsps/arm/imxrt/nxp/devices/MIMXRT1052/drivers/fsl_lpi2c_edma.c +++ /dev/null @@ -1,518 +0,0 @@ -/* - * Copyright (c) 2015, Freescale Semiconductor, Inc. - * Copyright 2016-2019 NXP - * All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include "fsl_lpi2c_edma.h" -#include <stdlib.h> -#include <string.h> - -/******************************************************************************* - * Definitions - ******************************************************************************/ - -/* Component ID definition, used by tools. */ -#ifndef FSL_COMPONENT_ID -#define FSL_COMPONENT_ID "platform.drivers.lpi2c_edma" -#endif - -/* @brief Mask to align an address to 32 bytes. */ -#define ALIGN_32_MASK (0x1fU) - -/*! @brief 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 States for the state machine used by transactional APIs. */ -enum _lpi2c_transfer_states -{ - kIdleState = 0, - kSendCommandState, - kIssueReadCommandState, - kTransferDataState, - kStopState, - kWaitForCompletionState, -}; - -/*! @brief Typedef for interrupt handler. */ -typedef void (*lpi2c_isr_t)(LPI2C_Type *base, void *handle); - -/******************************************************************************* - * Prototypes - ******************************************************************************/ - -static uint32_t LPI2C_GenerateCommands(lpi2c_master_edma_handle_t *handle); - -static void LPI2C_MasterEDMACallback(edma_handle_t *dmaHandle, void *userData, bool isTransferDone, uint32_t tcds); - -/******************************************************************************* - * Variables - ******************************************************************************/ - -static uint32_t lpi2c_edma_RecSetting = 0x02; - -/******************************************************************************* - * Code - ******************************************************************************/ - -/*! - * brief Create a new handle for the LPI2C master DMA APIs. - * - * The creation of a handle is for use with the DMA APIs. Once a handle - * is created, there is not a corresponding destroy handle. If the user wants to - * terminate a transfer, the LPI2C_MasterTransferAbortEDMA() API shall be called. - * - * For devices where the LPI2C send and receive DMA requests are OR'd together, the a txDmaHandle - * parameter is ignored and may be set to NULL. - * - * param base The LPI2C peripheral base address. - * param[out] handle Pointer to the LPI2C master driver handle. - * param rxDmaHandle Handle for the eDMA receive channel. Created by the user prior to calling this function. - * param txDmaHandle Handle for the eDMA transmit channel. Created by the user prior to calling this function. - * param callback User provided pointer to the asynchronous callback function. - * param userData User provided pointer to the application callback data. - */ -void LPI2C_MasterCreateEDMAHandle(LPI2C_Type *base, - lpi2c_master_edma_handle_t *handle, - edma_handle_t *rxDmaHandle, - edma_handle_t *txDmaHandle, - lpi2c_master_edma_transfer_callback_t callback, - void *userData) -{ - assert(handle); - assert(rxDmaHandle); - assert(txDmaHandle); - - /* Clear out the handle. */ - (void)memset(handle, 0, sizeof(*handle)); - - /* Set up the handle. For combined rx/tx DMA requests, the tx channel handle is set to the rx handle */ - /* in order to make the transfer API code simpler. */ - handle->base = base; - handle->completionCallback = callback; - handle->userData = userData; - handle->rx = rxDmaHandle; - handle->tx = (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) > 0) ? txDmaHandle : rxDmaHandle; - - /* Set DMA channel completion callbacks. */ - EDMA_SetCallback(handle->rx, LPI2C_MasterEDMACallback, handle); - if (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) != 0) - { - EDMA_SetCallback(handle->tx, LPI2C_MasterEDMACallback, handle); - } -} - -/*! - * @brief Prepares the command buffer with the sequence of commands needed to send the requested transaction. - * @param handle Master DMA driver handle. - * @return Number of command words. - */ -static uint32_t LPI2C_GenerateCommands(lpi2c_master_edma_handle_t *handle) -{ - lpi2c_master_transfer_t *xfer = &handle->transfer; - uint16_t *cmd = (uint16_t *)&handle->commandBuffer; - uint32_t cmdCount = 0; - - /* Handle no start option. */ - if ((xfer->flags & (uint32_t)kLPI2C_TransferNoStartFlag) != 0U) - { - if (xfer->direction == kLPI2C_Read) - { - /* Need to issue read command first. */ - cmd[cmdCount++] = (uint16_t)kRxDataCmd | (uint16_t)LPI2C_MTDR_DATA(xfer->dataSize - 1U); - } - } - else - { - /* - * Initial direction depends on whether a subaddress was provided, and of course the actual - * data transfer direction. - */ - lpi2c_direction_t direction = (xfer->subaddressSize != 0U) ? kLPI2C_Write : xfer->direction; - - /* Start command. */ - cmd[cmdCount++] = - (uint16_t)kStartCmd | (uint16_t)((uint16_t)((uint16_t)xfer->slaveAddress << 1U) | (uint16_t)direction); - - /* Subaddress, MSB first. */ - if (xfer->subaddressSize != 0U) - { - uint32_t subaddressRemaining = xfer->subaddressSize; - while (0U != subaddressRemaining--) - { - uint8_t subaddressByte = (uint8_t)(xfer->subaddress >> (8U * subaddressRemaining)) & 0xffU; - cmd[cmdCount++] = subaddressByte; - } - } - - /* Reads need special handling because we have to issue a read command and maybe a repeated start. */ - if ((xfer->dataSize != 0U) && (xfer->direction == kLPI2C_Read)) - { - /* Need to send repeated start if switching directions to read. */ - if (direction == kLPI2C_Write) - { - cmd[cmdCount++] = (uint16_t)kStartCmd | - (uint16_t)((uint16_t)((uint16_t)xfer->slaveAddress << 1U) | (uint16_t)kLPI2C_Read); - } - - /* Read command. */ - cmd[cmdCount++] = (uint16_t)kRxDataCmd | (uint16_t)LPI2C_MTDR_DATA(xfer->dataSize - 1U); - } - } - - return cmdCount; -} - -/*! - * brief Performs a non-blocking DMA-based transaction on the I2C bus. - * - * The callback specified when the a handle was created is invoked when the transaction has - * completed. - * - * param base The LPI2C peripheral base address. - * param handle Pointer to the LPI2C master driver handle. - * param transfer The pointer to the transfer descriptor. - * retval #kStatus_Success The transaction was started successfully. - * retval #kStatus_LPI2C_Busy Either another master is currently utilizing the bus, or another DMA - * transaction is already in progress. - */ -status_t LPI2C_MasterTransferEDMA(LPI2C_Type *base, - lpi2c_master_edma_handle_t *handle, - lpi2c_master_transfer_t *transfer) -{ - status_t result; - - assert(handle); - assert(transfer); - assert(transfer->subaddressSize <= sizeof(transfer->subaddress)); - - /* 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. */ - result = LPI2C_CheckForBusyBus(base); - if (result != kStatus_Success) - { - return result; - } - - /* We're now busy. */ - handle->isBusy = true; - - /* Disable LPI2C IRQ and DMA sources while we configure stuff. */ - LPI2C_MasterDisableInterrupts(base, (uint32_t)kMasterIrqFlags); - LPI2C_MasterEnableDMA(base, false, false); - - /* Clear all flags. */ - LPI2C_MasterClearStatusFlags(base, (uint32_t)kMasterClearFlags); - - /* Save transfer into handle. */ - handle->transfer = *transfer; - - /* Generate commands to send. */ - uint32_t commandCount = LPI2C_GenerateCommands(handle); - - /* If the user is transmitting no data with no start or stop, then just go ahead and invoke the callback. */ - if ((0U == commandCount) && (transfer->dataSize == 0U)) - { - if (handle->completionCallback != NULL) - { - handle->completionCallback(base, handle, kStatus_Success, handle->userData); - } - return kStatus_Success; - } - - /* Reset DMA channels. */ - EDMA_ResetChannel(handle->rx->base, handle->rx->channel); - if (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) != 0) - { - EDMA_ResetChannel(handle->tx->base, handle->tx->channel); - } - - /* Get a 32-byte aligned TCD pointer. */ - edma_tcd_t *tcd = (edma_tcd_t *)((uint32_t)(&handle->tcds[1]) & (~ALIGN_32_MASK)); - - bool hasSendData = (transfer->direction == kLPI2C_Write) && (transfer->dataSize != 0U); - bool hasReceiveData = (transfer->direction == kLPI2C_Read) && (transfer->dataSize != 0U); - - edma_transfer_config_t transferConfig; - edma_tcd_t *linkTcd = NULL; - - /* Set up data transmit. */ - if (hasSendData) - { - uint32_t *srcAddr = (uint32_t *)transfer->data; - transferConfig.srcAddr = (uint32_t)srcAddr; - transferConfig.destAddr = (uint32_t)LPI2C_MasterGetTxFifoAddress(base); - transferConfig.srcTransferSize = kEDMA_TransferSize1Bytes; - transferConfig.destTransferSize = kEDMA_TransferSize1Bytes; - transferConfig.srcOffset = (int16_t)sizeof(uint8_t); - transferConfig.destOffset = 0; - transferConfig.minorLoopBytes = sizeof(uint8_t); /* TODO optimize to fill fifo */ - transferConfig.majorLoopCounts = transfer->dataSize; - - /* Store the initially configured eDMA minor byte transfer count into the LPI2C handle */ - handle->nbytes = (uint8_t)transferConfig.minorLoopBytes; - - if (commandCount != 0U) - { - /* Create a software TCD, which will be chained after the commands. */ - EDMA_TcdReset(tcd); - EDMA_TcdSetTransferConfig(tcd, &transferConfig, NULL); - EDMA_TcdEnableInterrupts(tcd, (uint32_t)kEDMA_MajorInterruptEnable); - linkTcd = tcd; - } - else - { - /* User is only transmitting data with no required commands, so this transfer can stand alone. */ - EDMA_SetTransferConfig(handle->tx->base, handle->tx->channel, &transferConfig, NULL); - EDMA_EnableChannelInterrupts(handle->tx->base, handle->tx->channel, (uint32_t)kEDMA_MajorInterruptEnable); - } - } - else if (hasReceiveData) - { - uint32_t *srcAddr = (uint32_t *)transfer->data; - /* Set up data receive. */ - transferConfig.srcAddr = (uint32_t)LPI2C_MasterGetRxFifoAddress(base); - transferConfig.destAddr = (uint32_t)srcAddr; - transferConfig.srcTransferSize = kEDMA_TransferSize1Bytes; - transferConfig.destTransferSize = kEDMA_TransferSize1Bytes; - transferConfig.srcOffset = 0; - transferConfig.destOffset = (int16_t)sizeof(uint8_t); - transferConfig.minorLoopBytes = sizeof(uint8_t); /* TODO optimize to empty fifo */ - transferConfig.majorLoopCounts = transfer->dataSize; - - /* Store the initially configured eDMA minor byte transfer count into the LPI2C handle */ - handle->nbytes = (uint8_t)transferConfig.minorLoopBytes; - - if ((FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) != 0) || (0U == commandCount)) - { - /* We can put this receive transfer on its own DMA channel. */ - EDMA_SetTransferConfig(handle->rx->base, handle->rx->channel, &transferConfig, NULL); - EDMA_EnableChannelInterrupts(handle->rx->base, handle->rx->channel, (uint32_t)kEDMA_MajorInterruptEnable); - } - else - { - /* For shared rx/tx DMA requests, when there are commands, create a software TCD of - enabling rx dma and disabling tx dma, which will be chained onto the commands transfer, - and create another software TCD of transfering data and chain it onto the last TCD. - Notice that in this situation assume tx/rx uses same channel */ - EDMA_TcdReset(tcd); - EDMA_TcdSetTransferConfig(tcd, &transferConfig, NULL); - EDMA_TcdEnableInterrupts(tcd, (uint32_t)kEDMA_MajorInterruptEnable); - - transferConfig.srcAddr = (uint32_t)&lpi2c_edma_RecSetting; - transferConfig.destAddr = (uint32_t) & (base->MDER); - transferConfig.srcTransferSize = kEDMA_TransferSize1Bytes; - transferConfig.destTransferSize = kEDMA_TransferSize1Bytes; - transferConfig.srcOffset = 0; - transferConfig.destOffset = (int16_t)sizeof(uint8_t); - transferConfig.minorLoopBytes = sizeof(uint8_t); - transferConfig.majorLoopCounts = 1; - - edma_tcd_t *tcdSetRxClearTxDMA = (edma_tcd_t *)((uint32_t)(&handle->tcds[2]) & (~ALIGN_32_MASK)); - - EDMA_TcdReset(tcdSetRxClearTxDMA); - EDMA_TcdSetTransferConfig(tcdSetRxClearTxDMA, &transferConfig, tcd); - linkTcd = tcdSetRxClearTxDMA; - } - } - else - { - /* No data to send */ - } - - /* Set up commands transfer. */ - if (commandCount != 0U) - { - transferConfig.srcAddr = (uint32_t)handle->commandBuffer; - transferConfig.destAddr = (uint32_t)LPI2C_MasterGetTxFifoAddress(base); - transferConfig.srcTransferSize = kEDMA_TransferSize2Bytes; - transferConfig.destTransferSize = kEDMA_TransferSize2Bytes; - transferConfig.srcOffset = (int16_t)sizeof(uint16_t); - transferConfig.destOffset = 0; - transferConfig.minorLoopBytes = sizeof(uint16_t); /* TODO optimize to fill fifo */ - transferConfig.majorLoopCounts = commandCount; - - EDMA_SetTransferConfig(handle->tx->base, handle->tx->channel, &transferConfig, linkTcd); - } - - /* Start DMA transfer. */ - if (hasReceiveData || (0 == FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base))) - { - EDMA_StartTransfer(handle->rx); - } - - if ((hasSendData || (commandCount != 0U)) && (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) != 0)) - { - EDMA_StartTransfer(handle->tx); - } - - /* Enable DMA in both directions. This actually kicks of the transfer. */ - LPI2C_MasterEnableDMA(base, true, true); - - return result; -} - -/*! - * brief Returns number of bytes transferred so far. - * - * param base The LPI2C peripheral base address. - * param handle Pointer to the LPI2C master driver handle. - * param[out] count Number of bytes transferred so far by the non-blocking transaction. - * retval #kStatus_Success - * retval #kStatus_NoTransferInProgress There is not a DMA transaction currently in progress. - */ -status_t LPI2C_MasterTransferGetCountEDMA(LPI2C_Type *base, lpi2c_master_edma_handle_t *handle, size_t *count) -{ - assert(handle); - - if (NULL == count) - { - return kStatus_InvalidArgument; - } - - /* Catch when there is not an active transfer. */ - if (!handle->isBusy) - { - *count = 0; - return kStatus_NoTransferInProgress; - } - - uint32_t remaining = handle->transfer.dataSize; - - /* If the DMA is still on a commands transfer that chains to the actual data transfer, */ - /* we do nothing and return the number of transferred bytes as zero. */ - if (EDMA_GetNextTCDAddress(handle->tx) == 0U) - { - if (handle->transfer.direction == kLPI2C_Write) - { - remaining = - (uint32_t)handle->nbytes * EDMA_GetRemainingMajorLoopCount(handle->tx->base, handle->tx->channel); - } - else - { - remaining = - (uint32_t)handle->nbytes * EDMA_GetRemainingMajorLoopCount(handle->rx->base, handle->rx->channel); - } - } - - *count = handle->transfer.dataSize - remaining; - - return kStatus_Success; -} - -/*! - * brief Terminates a non-blocking LPI2C master transmission early. - * - * note It is not safe to call this function from an IRQ handler that has a higher priority than the - * eDMA peripheral's IRQ priority. - * - * param base The LPI2C peripheral base address. - * param handle Pointer to the LPI2C master driver handle. - * retval #kStatus_Success A transaction was successfully aborted. - * retval #kStatus_LPI2C_Idle There is not a DMA transaction currently in progress. - */ -status_t LPI2C_MasterTransferAbortEDMA(LPI2C_Type *base, lpi2c_master_edma_handle_t *handle) -{ - /* Catch when there is not an active transfer. */ - if (!handle->isBusy) - { - return kStatus_LPI2C_Idle; - } - - /* Terminate DMA transfers. */ - EDMA_AbortTransfer(handle->rx); - if (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) != 0) - { - EDMA_AbortTransfer(handle->tx); - } - - /* Reset fifos. */ - base->MCR |= LPI2C_MCR_RRF_MASK | LPI2C_MCR_RTF_MASK; - - /* Send a stop command to finalize the transfer. */ - base->MTDR = (uint32_t)kStopCmd; - - /* Reset handle. */ - handle->isBusy = false; - - return kStatus_Success; -} - -/*! - * @brief DMA completion callback. - * @param dmaHandle DMA channel handle for the channel that completed. - * @param userData User data associated with the channel handle. For this callback, the user data is the - * LPI2C DMA driver handle. - * @param isTransferDone Whether the DMA transfer has completed. - * @param tcds Number of TCDs that completed. - */ -static void LPI2C_MasterEDMACallback(edma_handle_t *dmaHandle, void *userData, bool isTransferDone, uint32_t tcds) -{ - lpi2c_master_edma_handle_t *handle = (lpi2c_master_edma_handle_t *)userData; - - if (NULL == handle) - { - return; - } - - /* Check for errors. */ - status_t result = LPI2C_MasterCheckAndClearError(handle->base, LPI2C_MasterGetStatusFlags(handle->base)); - - /* Done with this transaction. */ - handle->isBusy = false; - - if (0U == (handle->transfer.flags & (uint32_t)kLPI2C_TransferNoStopFlag)) - { - /* Send a stop command to finalize the transfer. */ - handle->base->MTDR = (uint32_t)kStopCmd; - } - - /* Invoke callback. */ - if (handle->completionCallback != NULL) - { - handle->completionCallback(handle->base, handle, result, handle->userData); - } -} |