Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
mbed-os/targets/TARGET_NXP/TARGET_MCUXpresso_MCUS/TARGET_MIMXRT1050/drivers/fsl_lpi2c_edma.c@0:fbdae7e6d805, 2018-05-14 (annotated)
- Committer:
- borlanic
- Date:
- Mon May 14 11:29:06 2018 +0000
- Revision:
- 0:fbdae7e6d805
BBR
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
borlanic | 0:fbdae7e6d805 | 1 | /* |
borlanic | 0:fbdae7e6d805 | 2 | * Copyright (c) 2015, Freescale Semiconductor, Inc. |
borlanic | 0:fbdae7e6d805 | 3 | * Copyright 2016-2017 NXP |
borlanic | 0:fbdae7e6d805 | 4 | * |
borlanic | 0:fbdae7e6d805 | 5 | * Redistribution and use in source and binary forms, with or without modification, |
borlanic | 0:fbdae7e6d805 | 6 | * are permitted provided that the following conditions are met: |
borlanic | 0:fbdae7e6d805 | 7 | * |
borlanic | 0:fbdae7e6d805 | 8 | * o Redistributions of source code must retain the above copyright notice, this list |
borlanic | 0:fbdae7e6d805 | 9 | * of conditions and the following disclaimer. |
borlanic | 0:fbdae7e6d805 | 10 | * |
borlanic | 0:fbdae7e6d805 | 11 | * o Redistributions in binary form must reproduce the above copyright notice, this |
borlanic | 0:fbdae7e6d805 | 12 | * list of conditions and the following disclaimer in the documentation and/or |
borlanic | 0:fbdae7e6d805 | 13 | * other materials provided with the distribution. |
borlanic | 0:fbdae7e6d805 | 14 | * |
borlanic | 0:fbdae7e6d805 | 15 | * o Neither the name of the copyright holder nor the names of its |
borlanic | 0:fbdae7e6d805 | 16 | * contributors may be used to endorse or promote products derived from this |
borlanic | 0:fbdae7e6d805 | 17 | * software without specific prior written permission. |
borlanic | 0:fbdae7e6d805 | 18 | * |
borlanic | 0:fbdae7e6d805 | 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
borlanic | 0:fbdae7e6d805 | 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
borlanic | 0:fbdae7e6d805 | 21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
borlanic | 0:fbdae7e6d805 | 22 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR |
borlanic | 0:fbdae7e6d805 | 23 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
borlanic | 0:fbdae7e6d805 | 24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
borlanic | 0:fbdae7e6d805 | 25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
borlanic | 0:fbdae7e6d805 | 26 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
borlanic | 0:fbdae7e6d805 | 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
borlanic | 0:fbdae7e6d805 | 28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
borlanic | 0:fbdae7e6d805 | 29 | */ |
borlanic | 0:fbdae7e6d805 | 30 | |
borlanic | 0:fbdae7e6d805 | 31 | #include "fsl_lpi2c_edma.h" |
borlanic | 0:fbdae7e6d805 | 32 | #include <stdlib.h> |
borlanic | 0:fbdae7e6d805 | 33 | #include <string.h> |
borlanic | 0:fbdae7e6d805 | 34 | |
borlanic | 0:fbdae7e6d805 | 35 | /******************************************************************************* |
borlanic | 0:fbdae7e6d805 | 36 | * Definitions |
borlanic | 0:fbdae7e6d805 | 37 | ******************************************************************************/ |
borlanic | 0:fbdae7e6d805 | 38 | |
borlanic | 0:fbdae7e6d805 | 39 | /* @brief Mask to align an address to 32 bytes. */ |
borlanic | 0:fbdae7e6d805 | 40 | #define ALIGN_32_MASK (0x1fU) |
borlanic | 0:fbdae7e6d805 | 41 | |
borlanic | 0:fbdae7e6d805 | 42 | /*! @brief Common sets of flags used by the driver. */ |
borlanic | 0:fbdae7e6d805 | 43 | enum _lpi2c_flag_constants |
borlanic | 0:fbdae7e6d805 | 44 | { |
borlanic | 0:fbdae7e6d805 | 45 | /*! All flags which are cleared by the driver upon starting a transfer. */ |
borlanic | 0:fbdae7e6d805 | 46 | kMasterClearFlags = kLPI2C_MasterEndOfPacketFlag | kLPI2C_MasterStopDetectFlag | kLPI2C_MasterNackDetectFlag | |
borlanic | 0:fbdae7e6d805 | 47 | kLPI2C_MasterArbitrationLostFlag | kLPI2C_MasterFifoErrFlag | kLPI2C_MasterPinLowTimeoutFlag | |
borlanic | 0:fbdae7e6d805 | 48 | kLPI2C_MasterDataMatchFlag, |
borlanic | 0:fbdae7e6d805 | 49 | |
borlanic | 0:fbdae7e6d805 | 50 | /*! IRQ sources enabled by the non-blocking transactional API. */ |
borlanic | 0:fbdae7e6d805 | 51 | kMasterIrqFlags = kLPI2C_MasterArbitrationLostFlag | kLPI2C_MasterTxReadyFlag | kLPI2C_MasterRxReadyFlag | |
borlanic | 0:fbdae7e6d805 | 52 | kLPI2C_MasterStopDetectFlag | kLPI2C_MasterNackDetectFlag | kLPI2C_MasterPinLowTimeoutFlag | |
borlanic | 0:fbdae7e6d805 | 53 | kLPI2C_MasterFifoErrFlag, |
borlanic | 0:fbdae7e6d805 | 54 | |
borlanic | 0:fbdae7e6d805 | 55 | /*! Errors to check for. */ |
borlanic | 0:fbdae7e6d805 | 56 | kMasterErrorFlags = kLPI2C_MasterNackDetectFlag | kLPI2C_MasterArbitrationLostFlag | kLPI2C_MasterFifoErrFlag | |
borlanic | 0:fbdae7e6d805 | 57 | kLPI2C_MasterPinLowTimeoutFlag, |
borlanic | 0:fbdae7e6d805 | 58 | |
borlanic | 0:fbdae7e6d805 | 59 | /*! All flags which are cleared by the driver upon starting a transfer. */ |
borlanic | 0:fbdae7e6d805 | 60 | kSlaveClearFlags = kLPI2C_SlaveRepeatedStartDetectFlag | kLPI2C_SlaveStopDetectFlag | kLPI2C_SlaveBitErrFlag | |
borlanic | 0:fbdae7e6d805 | 61 | kLPI2C_SlaveFifoErrFlag, |
borlanic | 0:fbdae7e6d805 | 62 | |
borlanic | 0:fbdae7e6d805 | 63 | /*! IRQ sources enabled by the non-blocking transactional API. */ |
borlanic | 0:fbdae7e6d805 | 64 | kSlaveIrqFlags = kLPI2C_SlaveTxReadyFlag | kLPI2C_SlaveRxReadyFlag | kLPI2C_SlaveStopDetectFlag | |
borlanic | 0:fbdae7e6d805 | 65 | kLPI2C_SlaveRepeatedStartDetectFlag | kLPI2C_SlaveFifoErrFlag | kLPI2C_SlaveBitErrFlag | |
borlanic | 0:fbdae7e6d805 | 66 | kLPI2C_SlaveTransmitAckFlag | kLPI2C_SlaveAddressValidFlag, |
borlanic | 0:fbdae7e6d805 | 67 | |
borlanic | 0:fbdae7e6d805 | 68 | /*! Errors to check for. */ |
borlanic | 0:fbdae7e6d805 | 69 | kSlaveErrorFlags = kLPI2C_SlaveFifoErrFlag | kLPI2C_SlaveBitErrFlag, |
borlanic | 0:fbdae7e6d805 | 70 | }; |
borlanic | 0:fbdae7e6d805 | 71 | |
borlanic | 0:fbdae7e6d805 | 72 | /* ! @brief LPI2C master fifo commands. */ |
borlanic | 0:fbdae7e6d805 | 73 | enum _lpi2c_master_fifo_cmd |
borlanic | 0:fbdae7e6d805 | 74 | { |
borlanic | 0:fbdae7e6d805 | 75 | kTxDataCmd = LPI2C_MTDR_CMD(0x0U), /*!< Transmit DATA[7:0] */ |
borlanic | 0:fbdae7e6d805 | 76 | kRxDataCmd = LPI2C_MTDR_CMD(0X1U), /*!< Receive (DATA[7:0] + 1) bytes */ |
borlanic | 0:fbdae7e6d805 | 77 | kStopCmd = LPI2C_MTDR_CMD(0x2U), /*!< Generate STOP condition */ |
borlanic | 0:fbdae7e6d805 | 78 | kStartCmd = LPI2C_MTDR_CMD(0x4U), /*!< Generate(repeated) START and transmit address in DATA[[7:0] */ |
borlanic | 0:fbdae7e6d805 | 79 | }; |
borlanic | 0:fbdae7e6d805 | 80 | |
borlanic | 0:fbdae7e6d805 | 81 | /*! @brief States for the state machine used by transactional APIs. */ |
borlanic | 0:fbdae7e6d805 | 82 | enum _lpi2c_transfer_states |
borlanic | 0:fbdae7e6d805 | 83 | { |
borlanic | 0:fbdae7e6d805 | 84 | kIdleState = 0, |
borlanic | 0:fbdae7e6d805 | 85 | kSendCommandState, |
borlanic | 0:fbdae7e6d805 | 86 | kIssueReadCommandState, |
borlanic | 0:fbdae7e6d805 | 87 | kTransferDataState, |
borlanic | 0:fbdae7e6d805 | 88 | kStopState, |
borlanic | 0:fbdae7e6d805 | 89 | kWaitForCompletionState, |
borlanic | 0:fbdae7e6d805 | 90 | }; |
borlanic | 0:fbdae7e6d805 | 91 | |
borlanic | 0:fbdae7e6d805 | 92 | /*! @brief Typedef for interrupt handler. */ |
borlanic | 0:fbdae7e6d805 | 93 | typedef void (*lpi2c_isr_t)(LPI2C_Type *base, void *handle); |
borlanic | 0:fbdae7e6d805 | 94 | |
borlanic | 0:fbdae7e6d805 | 95 | /******************************************************************************* |
borlanic | 0:fbdae7e6d805 | 96 | * Prototypes |
borlanic | 0:fbdae7e6d805 | 97 | ******************************************************************************/ |
borlanic | 0:fbdae7e6d805 | 98 | |
borlanic | 0:fbdae7e6d805 | 99 | /* Defined in fsl_lpi2c.c. */ |
borlanic | 0:fbdae7e6d805 | 100 | extern status_t LPI2C_CheckForBusyBus(LPI2C_Type *base); |
borlanic | 0:fbdae7e6d805 | 101 | |
borlanic | 0:fbdae7e6d805 | 102 | /* Defined in fsl_lpi2c.c. */ |
borlanic | 0:fbdae7e6d805 | 103 | extern status_t LPI2C_MasterCheckAndClearError(LPI2C_Type *base, uint32_t status); |
borlanic | 0:fbdae7e6d805 | 104 | |
borlanic | 0:fbdae7e6d805 | 105 | static uint32_t LPI2C_GenerateCommands(lpi2c_master_edma_handle_t *handle); |
borlanic | 0:fbdae7e6d805 | 106 | |
borlanic | 0:fbdae7e6d805 | 107 | static void LPI2C_MasterEDMACallback(edma_handle_t *dmaHandle, void *userData, bool isTransferDone, uint32_t tcds); |
borlanic | 0:fbdae7e6d805 | 108 | |
borlanic | 0:fbdae7e6d805 | 109 | /******************************************************************************* |
borlanic | 0:fbdae7e6d805 | 110 | * Code |
borlanic | 0:fbdae7e6d805 | 111 | ******************************************************************************/ |
borlanic | 0:fbdae7e6d805 | 112 | |
borlanic | 0:fbdae7e6d805 | 113 | void LPI2C_MasterCreateEDMAHandle(LPI2C_Type *base, |
borlanic | 0:fbdae7e6d805 | 114 | lpi2c_master_edma_handle_t *handle, |
borlanic | 0:fbdae7e6d805 | 115 | edma_handle_t *rxDmaHandle, |
borlanic | 0:fbdae7e6d805 | 116 | edma_handle_t *txDmaHandle, |
borlanic | 0:fbdae7e6d805 | 117 | lpi2c_master_edma_transfer_callback_t callback, |
borlanic | 0:fbdae7e6d805 | 118 | void *userData) |
borlanic | 0:fbdae7e6d805 | 119 | { |
borlanic | 0:fbdae7e6d805 | 120 | assert(handle); |
borlanic | 0:fbdae7e6d805 | 121 | assert(rxDmaHandle); |
borlanic | 0:fbdae7e6d805 | 122 | assert(txDmaHandle); |
borlanic | 0:fbdae7e6d805 | 123 | |
borlanic | 0:fbdae7e6d805 | 124 | /* Clear out the handle. */ |
borlanic | 0:fbdae7e6d805 | 125 | memset(handle, 0, sizeof(*handle)); |
borlanic | 0:fbdae7e6d805 | 126 | |
borlanic | 0:fbdae7e6d805 | 127 | /* Set up the handle. For combined rx/tx DMA requests, the tx channel handle is set to the rx handle */ |
borlanic | 0:fbdae7e6d805 | 128 | /* in order to make the transfer API code simpler. */ |
borlanic | 0:fbdae7e6d805 | 129 | handle->base = base; |
borlanic | 0:fbdae7e6d805 | 130 | handle->completionCallback = callback; |
borlanic | 0:fbdae7e6d805 | 131 | handle->userData = userData; |
borlanic | 0:fbdae7e6d805 | 132 | handle->rx = rxDmaHandle; |
borlanic | 0:fbdae7e6d805 | 133 | handle->tx = FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) ? txDmaHandle : rxDmaHandle; |
borlanic | 0:fbdae7e6d805 | 134 | |
borlanic | 0:fbdae7e6d805 | 135 | /* Set DMA channel completion callbacks. */ |
borlanic | 0:fbdae7e6d805 | 136 | EDMA_SetCallback(handle->rx, LPI2C_MasterEDMACallback, handle); |
borlanic | 0:fbdae7e6d805 | 137 | if (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base)) |
borlanic | 0:fbdae7e6d805 | 138 | { |
borlanic | 0:fbdae7e6d805 | 139 | EDMA_SetCallback(handle->tx, LPI2C_MasterEDMACallback, handle); |
borlanic | 0:fbdae7e6d805 | 140 | } |
borlanic | 0:fbdae7e6d805 | 141 | } |
borlanic | 0:fbdae7e6d805 | 142 | |
borlanic | 0:fbdae7e6d805 | 143 | /*! |
borlanic | 0:fbdae7e6d805 | 144 | * @brief Prepares the command buffer with the sequence of commands needed to send the requested transaction. |
borlanic | 0:fbdae7e6d805 | 145 | * @param handle Master DMA driver handle. |
borlanic | 0:fbdae7e6d805 | 146 | * @return Number of command words. |
borlanic | 0:fbdae7e6d805 | 147 | */ |
borlanic | 0:fbdae7e6d805 | 148 | static uint32_t LPI2C_GenerateCommands(lpi2c_master_edma_handle_t *handle) |
borlanic | 0:fbdae7e6d805 | 149 | { |
borlanic | 0:fbdae7e6d805 | 150 | lpi2c_master_transfer_t *xfer = &handle->transfer; |
borlanic | 0:fbdae7e6d805 | 151 | uint16_t *cmd = (uint16_t *)&handle->commandBuffer; |
borlanic | 0:fbdae7e6d805 | 152 | uint32_t cmdCount = 0; |
borlanic | 0:fbdae7e6d805 | 153 | |
borlanic | 0:fbdae7e6d805 | 154 | /* Handle no start option. */ |
borlanic | 0:fbdae7e6d805 | 155 | if (xfer->flags & kLPI2C_TransferNoStartFlag) |
borlanic | 0:fbdae7e6d805 | 156 | { |
borlanic | 0:fbdae7e6d805 | 157 | if (xfer->direction == kLPI2C_Read) |
borlanic | 0:fbdae7e6d805 | 158 | { |
borlanic | 0:fbdae7e6d805 | 159 | /* Need to issue read command first. */ |
borlanic | 0:fbdae7e6d805 | 160 | cmd[cmdCount++] = kRxDataCmd | LPI2C_MTDR_DATA(xfer->dataSize - 1); |
borlanic | 0:fbdae7e6d805 | 161 | } |
borlanic | 0:fbdae7e6d805 | 162 | } |
borlanic | 0:fbdae7e6d805 | 163 | else |
borlanic | 0:fbdae7e6d805 | 164 | { |
borlanic | 0:fbdae7e6d805 | 165 | /* |
borlanic | 0:fbdae7e6d805 | 166 | * Initial direction depends on whether a subaddress was provided, and of course the actual |
borlanic | 0:fbdae7e6d805 | 167 | * data transfer direction. |
borlanic | 0:fbdae7e6d805 | 168 | */ |
borlanic | 0:fbdae7e6d805 | 169 | lpi2c_direction_t direction = xfer->subaddressSize ? kLPI2C_Write : xfer->direction; |
borlanic | 0:fbdae7e6d805 | 170 | |
borlanic | 0:fbdae7e6d805 | 171 | /* Start command. */ |
borlanic | 0:fbdae7e6d805 | 172 | cmd[cmdCount++] = |
borlanic | 0:fbdae7e6d805 | 173 | (uint16_t)kStartCmd | (uint16_t)((uint16_t)((uint16_t)xfer->slaveAddress << 1U) | (uint16_t)direction); |
borlanic | 0:fbdae7e6d805 | 174 | |
borlanic | 0:fbdae7e6d805 | 175 | /* Subaddress, MSB first. */ |
borlanic | 0:fbdae7e6d805 | 176 | if (xfer->subaddressSize) |
borlanic | 0:fbdae7e6d805 | 177 | { |
borlanic | 0:fbdae7e6d805 | 178 | uint32_t subaddressRemaining = xfer->subaddressSize; |
borlanic | 0:fbdae7e6d805 | 179 | while (subaddressRemaining--) |
borlanic | 0:fbdae7e6d805 | 180 | { |
borlanic | 0:fbdae7e6d805 | 181 | uint8_t subaddressByte = (xfer->subaddress >> (8 * subaddressRemaining)) & 0xff; |
borlanic | 0:fbdae7e6d805 | 182 | cmd[cmdCount++] = subaddressByte; |
borlanic | 0:fbdae7e6d805 | 183 | } |
borlanic | 0:fbdae7e6d805 | 184 | } |
borlanic | 0:fbdae7e6d805 | 185 | |
borlanic | 0:fbdae7e6d805 | 186 | /* Reads need special handling because we have to issue a read command and maybe a repeated start. */ |
borlanic | 0:fbdae7e6d805 | 187 | if ((xfer->dataSize) && (xfer->direction == kLPI2C_Read)) |
borlanic | 0:fbdae7e6d805 | 188 | { |
borlanic | 0:fbdae7e6d805 | 189 | /* Need to send repeated start if switching directions to read. */ |
borlanic | 0:fbdae7e6d805 | 190 | if (direction == kLPI2C_Write) |
borlanic | 0:fbdae7e6d805 | 191 | { |
borlanic | 0:fbdae7e6d805 | 192 | cmd[cmdCount++] = (uint16_t)kStartCmd | |
borlanic | 0:fbdae7e6d805 | 193 | (uint16_t)((uint16_t)((uint16_t)xfer->slaveAddress << 1U) | (uint16_t)kLPI2C_Read); |
borlanic | 0:fbdae7e6d805 | 194 | } |
borlanic | 0:fbdae7e6d805 | 195 | |
borlanic | 0:fbdae7e6d805 | 196 | /* Read command. */ |
borlanic | 0:fbdae7e6d805 | 197 | cmd[cmdCount++] = kRxDataCmd | LPI2C_MTDR_DATA(xfer->dataSize - 1); |
borlanic | 0:fbdae7e6d805 | 198 | } |
borlanic | 0:fbdae7e6d805 | 199 | } |
borlanic | 0:fbdae7e6d805 | 200 | |
borlanic | 0:fbdae7e6d805 | 201 | return cmdCount; |
borlanic | 0:fbdae7e6d805 | 202 | } |
borlanic | 0:fbdae7e6d805 | 203 | |
borlanic | 0:fbdae7e6d805 | 204 | status_t LPI2C_MasterTransferEDMA(LPI2C_Type *base, |
borlanic | 0:fbdae7e6d805 | 205 | lpi2c_master_edma_handle_t *handle, |
borlanic | 0:fbdae7e6d805 | 206 | lpi2c_master_transfer_t *transfer) |
borlanic | 0:fbdae7e6d805 | 207 | { |
borlanic | 0:fbdae7e6d805 | 208 | status_t result; |
borlanic | 0:fbdae7e6d805 | 209 | |
borlanic | 0:fbdae7e6d805 | 210 | assert(handle); |
borlanic | 0:fbdae7e6d805 | 211 | assert(transfer); |
borlanic | 0:fbdae7e6d805 | 212 | assert(transfer->subaddressSize <= sizeof(transfer->subaddress)); |
borlanic | 0:fbdae7e6d805 | 213 | |
borlanic | 0:fbdae7e6d805 | 214 | /* Return busy if another transaction is in progress. */ |
borlanic | 0:fbdae7e6d805 | 215 | if (handle->isBusy) |
borlanic | 0:fbdae7e6d805 | 216 | { |
borlanic | 0:fbdae7e6d805 | 217 | return kStatus_LPI2C_Busy; |
borlanic | 0:fbdae7e6d805 | 218 | } |
borlanic | 0:fbdae7e6d805 | 219 | |
borlanic | 0:fbdae7e6d805 | 220 | /* Return an error if the bus is already in use not by us. */ |
borlanic | 0:fbdae7e6d805 | 221 | result = LPI2C_CheckForBusyBus(base); |
borlanic | 0:fbdae7e6d805 | 222 | if (result) |
borlanic | 0:fbdae7e6d805 | 223 | { |
borlanic | 0:fbdae7e6d805 | 224 | return result; |
borlanic | 0:fbdae7e6d805 | 225 | } |
borlanic | 0:fbdae7e6d805 | 226 | |
borlanic | 0:fbdae7e6d805 | 227 | /* We're now busy. */ |
borlanic | 0:fbdae7e6d805 | 228 | handle->isBusy = true; |
borlanic | 0:fbdae7e6d805 | 229 | |
borlanic | 0:fbdae7e6d805 | 230 | /* Disable LPI2C IRQ and DMA sources while we configure stuff. */ |
borlanic | 0:fbdae7e6d805 | 231 | LPI2C_MasterDisableInterrupts(base, kMasterIrqFlags); |
borlanic | 0:fbdae7e6d805 | 232 | LPI2C_MasterEnableDMA(base, false, false); |
borlanic | 0:fbdae7e6d805 | 233 | |
borlanic | 0:fbdae7e6d805 | 234 | /* Clear all flags. */ |
borlanic | 0:fbdae7e6d805 | 235 | LPI2C_MasterClearStatusFlags(base, kMasterClearFlags); |
borlanic | 0:fbdae7e6d805 | 236 | |
borlanic | 0:fbdae7e6d805 | 237 | /* Save transfer into handle. */ |
borlanic | 0:fbdae7e6d805 | 238 | handle->transfer = *transfer; |
borlanic | 0:fbdae7e6d805 | 239 | |
borlanic | 0:fbdae7e6d805 | 240 | /* Generate commands to send. */ |
borlanic | 0:fbdae7e6d805 | 241 | uint32_t commandCount = LPI2C_GenerateCommands(handle); |
borlanic | 0:fbdae7e6d805 | 242 | |
borlanic | 0:fbdae7e6d805 | 243 | /* If the user is transmitting no data with no start or stop, then just go ahead and invoke the callback. */ |
borlanic | 0:fbdae7e6d805 | 244 | if ((!commandCount) && (transfer->dataSize == 0)) |
borlanic | 0:fbdae7e6d805 | 245 | { |
borlanic | 0:fbdae7e6d805 | 246 | if (handle->completionCallback) |
borlanic | 0:fbdae7e6d805 | 247 | { |
borlanic | 0:fbdae7e6d805 | 248 | handle->completionCallback(base, handle, kStatus_Success, handle->userData); |
borlanic | 0:fbdae7e6d805 | 249 | } |
borlanic | 0:fbdae7e6d805 | 250 | return kStatus_Success; |
borlanic | 0:fbdae7e6d805 | 251 | } |
borlanic | 0:fbdae7e6d805 | 252 | |
borlanic | 0:fbdae7e6d805 | 253 | /* Reset DMA channels. */ |
borlanic | 0:fbdae7e6d805 | 254 | EDMA_ResetChannel(handle->rx->base, handle->rx->channel); |
borlanic | 0:fbdae7e6d805 | 255 | if (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base)) |
borlanic | 0:fbdae7e6d805 | 256 | { |
borlanic | 0:fbdae7e6d805 | 257 | EDMA_ResetChannel(handle->tx->base, handle->tx->channel); |
borlanic | 0:fbdae7e6d805 | 258 | } |
borlanic | 0:fbdae7e6d805 | 259 | |
borlanic | 0:fbdae7e6d805 | 260 | /* Get a 32-byte aligned TCD pointer. */ |
borlanic | 0:fbdae7e6d805 | 261 | edma_tcd_t *tcd = (edma_tcd_t *)((uint32_t)(&handle->tcds[1]) & (~ALIGN_32_MASK)); |
borlanic | 0:fbdae7e6d805 | 262 | |
borlanic | 0:fbdae7e6d805 | 263 | bool hasSendData = (transfer->direction == kLPI2C_Write) && (transfer->dataSize); |
borlanic | 0:fbdae7e6d805 | 264 | bool hasReceiveData = (transfer->direction == kLPI2C_Read) && (transfer->dataSize); |
borlanic | 0:fbdae7e6d805 | 265 | |
borlanic | 0:fbdae7e6d805 | 266 | edma_transfer_config_t transferConfig; |
borlanic | 0:fbdae7e6d805 | 267 | edma_tcd_t *linkTcd = NULL; |
borlanic | 0:fbdae7e6d805 | 268 | |
borlanic | 0:fbdae7e6d805 | 269 | /* Set up data transmit. */ |
borlanic | 0:fbdae7e6d805 | 270 | if (hasSendData) |
borlanic | 0:fbdae7e6d805 | 271 | { |
borlanic | 0:fbdae7e6d805 | 272 | transferConfig.srcAddr = (uint32_t)transfer->data; |
borlanic | 0:fbdae7e6d805 | 273 | transferConfig.destAddr = (uint32_t)LPI2C_MasterGetTxFifoAddress(base); |
borlanic | 0:fbdae7e6d805 | 274 | transferConfig.srcTransferSize = kEDMA_TransferSize1Bytes; |
borlanic | 0:fbdae7e6d805 | 275 | transferConfig.destTransferSize = kEDMA_TransferSize1Bytes; |
borlanic | 0:fbdae7e6d805 | 276 | transferConfig.srcOffset = sizeof(uint8_t); |
borlanic | 0:fbdae7e6d805 | 277 | transferConfig.destOffset = 0; |
borlanic | 0:fbdae7e6d805 | 278 | transferConfig.minorLoopBytes = sizeof(uint8_t); /* TODO optimize to fill fifo */ |
borlanic | 0:fbdae7e6d805 | 279 | transferConfig.majorLoopCounts = transfer->dataSize; |
borlanic | 0:fbdae7e6d805 | 280 | |
borlanic | 0:fbdae7e6d805 | 281 | /* Store the initially configured eDMA minor byte transfer count into the LPI2C handle */ |
borlanic | 0:fbdae7e6d805 | 282 | handle->nbytes = transferConfig.minorLoopBytes; |
borlanic | 0:fbdae7e6d805 | 283 | |
borlanic | 0:fbdae7e6d805 | 284 | if (commandCount) |
borlanic | 0:fbdae7e6d805 | 285 | { |
borlanic | 0:fbdae7e6d805 | 286 | /* Create a software TCD, which will be chained after the commands. */ |
borlanic | 0:fbdae7e6d805 | 287 | EDMA_TcdReset(tcd); |
borlanic | 0:fbdae7e6d805 | 288 | EDMA_TcdSetTransferConfig(tcd, &transferConfig, NULL); |
borlanic | 0:fbdae7e6d805 | 289 | EDMA_TcdEnableInterrupts(tcd, kEDMA_MajorInterruptEnable); |
borlanic | 0:fbdae7e6d805 | 290 | linkTcd = tcd; |
borlanic | 0:fbdae7e6d805 | 291 | } |
borlanic | 0:fbdae7e6d805 | 292 | else |
borlanic | 0:fbdae7e6d805 | 293 | { |
borlanic | 0:fbdae7e6d805 | 294 | /* User is only transmitting data with no required commands, so this transfer can stand alone. */ |
borlanic | 0:fbdae7e6d805 | 295 | EDMA_SetTransferConfig(handle->tx->base, handle->tx->channel, &transferConfig, NULL); |
borlanic | 0:fbdae7e6d805 | 296 | EDMA_EnableChannelInterrupts(handle->tx->base, handle->tx->channel, kEDMA_MajorInterruptEnable); |
borlanic | 0:fbdae7e6d805 | 297 | } |
borlanic | 0:fbdae7e6d805 | 298 | } |
borlanic | 0:fbdae7e6d805 | 299 | else if (hasReceiveData) |
borlanic | 0:fbdae7e6d805 | 300 | { |
borlanic | 0:fbdae7e6d805 | 301 | /* Set up data receive. */ |
borlanic | 0:fbdae7e6d805 | 302 | transferConfig.srcAddr = (uint32_t)LPI2C_MasterGetRxFifoAddress(base); |
borlanic | 0:fbdae7e6d805 | 303 | transferConfig.destAddr = (uint32_t)transfer->data; |
borlanic | 0:fbdae7e6d805 | 304 | transferConfig.srcTransferSize = kEDMA_TransferSize1Bytes; |
borlanic | 0:fbdae7e6d805 | 305 | transferConfig.destTransferSize = kEDMA_TransferSize1Bytes; |
borlanic | 0:fbdae7e6d805 | 306 | transferConfig.srcOffset = 0; |
borlanic | 0:fbdae7e6d805 | 307 | transferConfig.destOffset = sizeof(uint8_t); |
borlanic | 0:fbdae7e6d805 | 308 | transferConfig.minorLoopBytes = sizeof(uint8_t); /* TODO optimize to empty fifo */ |
borlanic | 0:fbdae7e6d805 | 309 | transferConfig.majorLoopCounts = transfer->dataSize; |
borlanic | 0:fbdae7e6d805 | 310 | |
borlanic | 0:fbdae7e6d805 | 311 | /* Store the initially configured eDMA minor byte transfer count into the LPI2C handle */ |
borlanic | 0:fbdae7e6d805 | 312 | handle->nbytes = transferConfig.minorLoopBytes; |
borlanic | 0:fbdae7e6d805 | 313 | |
borlanic | 0:fbdae7e6d805 | 314 | if (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) || (!commandCount)) |
borlanic | 0:fbdae7e6d805 | 315 | { |
borlanic | 0:fbdae7e6d805 | 316 | /* We can put this receive transfer on its own DMA channel. */ |
borlanic | 0:fbdae7e6d805 | 317 | EDMA_SetTransferConfig(handle->rx->base, handle->rx->channel, &transferConfig, NULL); |
borlanic | 0:fbdae7e6d805 | 318 | EDMA_EnableChannelInterrupts(handle->rx->base, handle->rx->channel, kEDMA_MajorInterruptEnable); |
borlanic | 0:fbdae7e6d805 | 319 | } |
borlanic | 0:fbdae7e6d805 | 320 | else |
borlanic | 0:fbdae7e6d805 | 321 | { |
borlanic | 0:fbdae7e6d805 | 322 | /* For shared rx/tx DMA requests when there are commands, create a software TCD which will be */ |
borlanic | 0:fbdae7e6d805 | 323 | /* chained onto the commands transfer, notice that in this situation assume tx/rx uses same channel */ |
borlanic | 0:fbdae7e6d805 | 324 | EDMA_TcdReset(tcd); |
borlanic | 0:fbdae7e6d805 | 325 | EDMA_TcdSetTransferConfig(tcd, &transferConfig, NULL); |
borlanic | 0:fbdae7e6d805 | 326 | EDMA_TcdEnableInterrupts(tcd, kEDMA_MajorInterruptEnable); |
borlanic | 0:fbdae7e6d805 | 327 | linkTcd = tcd; |
borlanic | 0:fbdae7e6d805 | 328 | } |
borlanic | 0:fbdae7e6d805 | 329 | } |
borlanic | 0:fbdae7e6d805 | 330 | else |
borlanic | 0:fbdae7e6d805 | 331 | { |
borlanic | 0:fbdae7e6d805 | 332 | /* No data to send */ |
borlanic | 0:fbdae7e6d805 | 333 | } |
borlanic | 0:fbdae7e6d805 | 334 | |
borlanic | 0:fbdae7e6d805 | 335 | /* Set up commands transfer. */ |
borlanic | 0:fbdae7e6d805 | 336 | if (commandCount) |
borlanic | 0:fbdae7e6d805 | 337 | { |
borlanic | 0:fbdae7e6d805 | 338 | transferConfig.srcAddr = (uint32_t)handle->commandBuffer; |
borlanic | 0:fbdae7e6d805 | 339 | transferConfig.destAddr = (uint32_t)LPI2C_MasterGetTxFifoAddress(base); |
borlanic | 0:fbdae7e6d805 | 340 | transferConfig.srcTransferSize = kEDMA_TransferSize2Bytes; |
borlanic | 0:fbdae7e6d805 | 341 | transferConfig.destTransferSize = kEDMA_TransferSize2Bytes; |
borlanic | 0:fbdae7e6d805 | 342 | transferConfig.srcOffset = sizeof(uint16_t); |
borlanic | 0:fbdae7e6d805 | 343 | transferConfig.destOffset = 0; |
borlanic | 0:fbdae7e6d805 | 344 | transferConfig.minorLoopBytes = sizeof(uint16_t); /* TODO optimize to fill fifo */ |
borlanic | 0:fbdae7e6d805 | 345 | transferConfig.majorLoopCounts = commandCount; |
borlanic | 0:fbdae7e6d805 | 346 | |
borlanic | 0:fbdae7e6d805 | 347 | EDMA_SetTransferConfig(handle->tx->base, handle->tx->channel, &transferConfig, linkTcd); |
borlanic | 0:fbdae7e6d805 | 348 | } |
borlanic | 0:fbdae7e6d805 | 349 | |
borlanic | 0:fbdae7e6d805 | 350 | /* Start DMA transfer. */ |
borlanic | 0:fbdae7e6d805 | 351 | if (hasReceiveData || !FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base)) |
borlanic | 0:fbdae7e6d805 | 352 | { |
borlanic | 0:fbdae7e6d805 | 353 | EDMA_StartTransfer(handle->rx); |
borlanic | 0:fbdae7e6d805 | 354 | } |
borlanic | 0:fbdae7e6d805 | 355 | |
borlanic | 0:fbdae7e6d805 | 356 | if (hasReceiveData && !FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base)) |
borlanic | 0:fbdae7e6d805 | 357 | { |
borlanic | 0:fbdae7e6d805 | 358 | EDMA_EnableChannelInterrupts(handle->tx->base, handle->tx->channel, kEDMA_MajorInterruptEnable); |
borlanic | 0:fbdae7e6d805 | 359 | } |
borlanic | 0:fbdae7e6d805 | 360 | |
borlanic | 0:fbdae7e6d805 | 361 | if ((hasSendData || commandCount) && FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base)) |
borlanic | 0:fbdae7e6d805 | 362 | { |
borlanic | 0:fbdae7e6d805 | 363 | EDMA_StartTransfer(handle->tx); |
borlanic | 0:fbdae7e6d805 | 364 | } |
borlanic | 0:fbdae7e6d805 | 365 | |
borlanic | 0:fbdae7e6d805 | 366 | /* Enable DMA in both directions. This actually kicks of the transfer. */ |
borlanic | 0:fbdae7e6d805 | 367 | LPI2C_MasterEnableDMA(base, true, true); |
borlanic | 0:fbdae7e6d805 | 368 | |
borlanic | 0:fbdae7e6d805 | 369 | return result; |
borlanic | 0:fbdae7e6d805 | 370 | } |
borlanic | 0:fbdae7e6d805 | 371 | |
borlanic | 0:fbdae7e6d805 | 372 | status_t LPI2C_MasterTransferGetCountEDMA(LPI2C_Type *base, lpi2c_master_edma_handle_t *handle, size_t *count) |
borlanic | 0:fbdae7e6d805 | 373 | { |
borlanic | 0:fbdae7e6d805 | 374 | assert(handle); |
borlanic | 0:fbdae7e6d805 | 375 | |
borlanic | 0:fbdae7e6d805 | 376 | if (!count) |
borlanic | 0:fbdae7e6d805 | 377 | { |
borlanic | 0:fbdae7e6d805 | 378 | return kStatus_InvalidArgument; |
borlanic | 0:fbdae7e6d805 | 379 | } |
borlanic | 0:fbdae7e6d805 | 380 | |
borlanic | 0:fbdae7e6d805 | 381 | /* Catch when there is not an active transfer. */ |
borlanic | 0:fbdae7e6d805 | 382 | if (!handle->isBusy) |
borlanic | 0:fbdae7e6d805 | 383 | { |
borlanic | 0:fbdae7e6d805 | 384 | *count = 0; |
borlanic | 0:fbdae7e6d805 | 385 | return kStatus_NoTransferInProgress; |
borlanic | 0:fbdae7e6d805 | 386 | } |
borlanic | 0:fbdae7e6d805 | 387 | |
borlanic | 0:fbdae7e6d805 | 388 | uint32_t remaining = handle->transfer.dataSize; |
borlanic | 0:fbdae7e6d805 | 389 | |
borlanic | 0:fbdae7e6d805 | 390 | /* If the DMA is still on a commands transfer that chains to the actual data transfer, */ |
borlanic | 0:fbdae7e6d805 | 391 | /* we do nothing and return the number of transferred bytes as zero. */ |
borlanic | 0:fbdae7e6d805 | 392 | if (EDMA_GetNextTCDAddress(handle->tx) == 0) |
borlanic | 0:fbdae7e6d805 | 393 | { |
borlanic | 0:fbdae7e6d805 | 394 | if (handle->transfer.direction == kLPI2C_Write) |
borlanic | 0:fbdae7e6d805 | 395 | { |
borlanic | 0:fbdae7e6d805 | 396 | remaining = |
borlanic | 0:fbdae7e6d805 | 397 | (uint32_t)handle->nbytes * EDMA_GetRemainingMajorLoopCount(handle->tx->base, handle->tx->channel); |
borlanic | 0:fbdae7e6d805 | 398 | } |
borlanic | 0:fbdae7e6d805 | 399 | else |
borlanic | 0:fbdae7e6d805 | 400 | { |
borlanic | 0:fbdae7e6d805 | 401 | remaining = |
borlanic | 0:fbdae7e6d805 | 402 | (uint32_t)handle->nbytes * EDMA_GetRemainingMajorLoopCount(handle->rx->base, handle->rx->channel); |
borlanic | 0:fbdae7e6d805 | 403 | } |
borlanic | 0:fbdae7e6d805 | 404 | } |
borlanic | 0:fbdae7e6d805 | 405 | |
borlanic | 0:fbdae7e6d805 | 406 | *count = handle->transfer.dataSize - remaining; |
borlanic | 0:fbdae7e6d805 | 407 | |
borlanic | 0:fbdae7e6d805 | 408 | return kStatus_Success; |
borlanic | 0:fbdae7e6d805 | 409 | } |
borlanic | 0:fbdae7e6d805 | 410 | |
borlanic | 0:fbdae7e6d805 | 411 | status_t LPI2C_MasterTransferAbortEDMA(LPI2C_Type *base, lpi2c_master_edma_handle_t *handle) |
borlanic | 0:fbdae7e6d805 | 412 | { |
borlanic | 0:fbdae7e6d805 | 413 | /* Catch when there is not an active transfer. */ |
borlanic | 0:fbdae7e6d805 | 414 | if (!handle->isBusy) |
borlanic | 0:fbdae7e6d805 | 415 | { |
borlanic | 0:fbdae7e6d805 | 416 | return kStatus_LPI2C_Idle; |
borlanic | 0:fbdae7e6d805 | 417 | } |
borlanic | 0:fbdae7e6d805 | 418 | |
borlanic | 0:fbdae7e6d805 | 419 | /* Terminate DMA transfers. */ |
borlanic | 0:fbdae7e6d805 | 420 | EDMA_AbortTransfer(handle->rx); |
borlanic | 0:fbdae7e6d805 | 421 | if (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base)) |
borlanic | 0:fbdae7e6d805 | 422 | { |
borlanic | 0:fbdae7e6d805 | 423 | EDMA_AbortTransfer(handle->tx); |
borlanic | 0:fbdae7e6d805 | 424 | } |
borlanic | 0:fbdae7e6d805 | 425 | |
borlanic | 0:fbdae7e6d805 | 426 | /* Reset fifos. */ |
borlanic | 0:fbdae7e6d805 | 427 | base->MCR |= LPI2C_MCR_RRF_MASK | LPI2C_MCR_RTF_MASK; |
borlanic | 0:fbdae7e6d805 | 428 | |
borlanic | 0:fbdae7e6d805 | 429 | /* Send a stop command to finalize the transfer. */ |
borlanic | 0:fbdae7e6d805 | 430 | base->MTDR = kStopCmd; |
borlanic | 0:fbdae7e6d805 | 431 | |
borlanic | 0:fbdae7e6d805 | 432 | /* Reset handle. */ |
borlanic | 0:fbdae7e6d805 | 433 | handle->isBusy = false; |
borlanic | 0:fbdae7e6d805 | 434 | |
borlanic | 0:fbdae7e6d805 | 435 | return kStatus_Success; |
borlanic | 0:fbdae7e6d805 | 436 | } |
borlanic | 0:fbdae7e6d805 | 437 | |
borlanic | 0:fbdae7e6d805 | 438 | /*! |
borlanic | 0:fbdae7e6d805 | 439 | * @brief DMA completion callback. |
borlanic | 0:fbdae7e6d805 | 440 | * @param dmaHandle DMA channel handle for the channel that completed. |
borlanic | 0:fbdae7e6d805 | 441 | * @param userData User data associated with the channel handle. For this callback, the user data is the |
borlanic | 0:fbdae7e6d805 | 442 | * LPI2C DMA driver handle. |
borlanic | 0:fbdae7e6d805 | 443 | * @param isTransferDone Whether the DMA transfer has completed. |
borlanic | 0:fbdae7e6d805 | 444 | * @param tcds Number of TCDs that completed. |
borlanic | 0:fbdae7e6d805 | 445 | */ |
borlanic | 0:fbdae7e6d805 | 446 | static void LPI2C_MasterEDMACallback(edma_handle_t *dmaHandle, void *userData, bool isTransferDone, uint32_t tcds) |
borlanic | 0:fbdae7e6d805 | 447 | { |
borlanic | 0:fbdae7e6d805 | 448 | lpi2c_master_edma_handle_t *handle = (lpi2c_master_edma_handle_t *)userData; |
borlanic | 0:fbdae7e6d805 | 449 | bool hasReceiveData = (handle->transfer.direction == kLPI2C_Read) && (handle->transfer.dataSize); |
borlanic | 0:fbdae7e6d805 | 450 | if (hasReceiveData && !FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base)) |
borlanic | 0:fbdae7e6d805 | 451 | { |
borlanic | 0:fbdae7e6d805 | 452 | if (EDMA_GetNextTCDAddress(handle->tx) != 0) |
borlanic | 0:fbdae7e6d805 | 453 | { |
borlanic | 0:fbdae7e6d805 | 454 | LPI2C_MasterEnableDMA(handle->base, false, true); |
borlanic | 0:fbdae7e6d805 | 455 | } |
borlanic | 0:fbdae7e6d805 | 456 | } |
borlanic | 0:fbdae7e6d805 | 457 | |
borlanic | 0:fbdae7e6d805 | 458 | if (!handle) |
borlanic | 0:fbdae7e6d805 | 459 | { |
borlanic | 0:fbdae7e6d805 | 460 | return; |
borlanic | 0:fbdae7e6d805 | 461 | } |
borlanic | 0:fbdae7e6d805 | 462 | |
borlanic | 0:fbdae7e6d805 | 463 | /* Check for errors. */ |
borlanic | 0:fbdae7e6d805 | 464 | status_t result = LPI2C_MasterCheckAndClearError(handle->base, LPI2C_MasterGetStatusFlags(handle->base)); |
borlanic | 0:fbdae7e6d805 | 465 | |
borlanic | 0:fbdae7e6d805 | 466 | /* Done with this transaction. */ |
borlanic | 0:fbdae7e6d805 | 467 | handle->isBusy = false; |
borlanic | 0:fbdae7e6d805 | 468 | |
borlanic | 0:fbdae7e6d805 | 469 | if (!(handle->transfer.flags & kLPI2C_TransferNoStopFlag)) |
borlanic | 0:fbdae7e6d805 | 470 | { |
borlanic | 0:fbdae7e6d805 | 471 | /* Send a stop command to finalize the transfer. */ |
borlanic | 0:fbdae7e6d805 | 472 | handle->base->MTDR = kStopCmd; |
borlanic | 0:fbdae7e6d805 | 473 | } |
borlanic | 0:fbdae7e6d805 | 474 | |
borlanic | 0:fbdae7e6d805 | 475 | /* Invoke callback. */ |
borlanic | 0:fbdae7e6d805 | 476 | if (handle->completionCallback) |
borlanic | 0:fbdae7e6d805 | 477 | { |
borlanic | 0:fbdae7e6d805 | 478 | handle->completionCallback(handle->base, handle, result, handle->userData); |
borlanic | 0:fbdae7e6d805 | 479 | } |
borlanic | 0:fbdae7e6d805 | 480 | } |