1 /*
2  * Copyright 2022 NXP
3  * All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include "fsl_lpi2c_edma.h"
9 #include <stdlib.h>
10 #include <string.h>
11 
12 /*******************************************************************************
13  * Definitions
14  ******************************************************************************/
15 
16 /* Component ID definition, used by tools. */
17 #ifndef FSL_COMPONENT_ID
18 #define FSL_COMPONENT_ID "platform.drivers.lpflexcomm_lpi2c_edma"
19 #endif
20 
21 /* @brief Mask to align an address to 32 bytes. */
22 #define ALIGN_32_MASK (0x1fU)
23 
24 /* ! @brief LPI2C master fifo commands. */
25 enum _lpi2c_master_fifo_cmd
26 {
27     kTxDataCmd = LPI2C_MTDR_CMD(0x0U), /*!< Transmit DATA[7:0] */
28     kRxDataCmd = LPI2C_MTDR_CMD(0X1U), /*!< Receive (DATA[7:0] + 1) bytes */
29     kStopCmd   = LPI2C_MTDR_CMD(0x2U), /*!< Generate STOP condition */
30     kStartCmd  = LPI2C_MTDR_CMD(0x4U), /*!< Generate(repeated) START and transmit address in DATA[[7:0] */
31 };
32 
33 /*! @brief States for the state machine used by transactional APIs. */
34 enum _lpi2c_transfer_states
35 {
36     kIdleState = 0,
37     kSendCommandState,
38     kIssueReadCommandState,
39     kTransferDataState,
40     kStopState,
41     kWaitForCompletionState,
42 };
43 
44 /*!
45  * @brief Used for conversion from `lpflexcomm_irq_handler_t` to `lpi2c_master_isr_t`
46  */
47 typedef union lpi2c_to_lpflexcomm_edma
48 {
49     lpi2c_master_isr_t lpi2c_master_handler;
50     lpflexcomm_irq_handler_t lpflexcomm_handler;
51 } lpi2c_to_lpflexcomm_edma_t;
52 
53 /*******************************************************************************
54  * Prototypes
55  ******************************************************************************/
56 
57 /*!
58  * @brief Prepares the command buffer with the sequence of commands needed to send the requested transaction.
59  * @param handle Master DMA driver handle.
60  * @return Number of command words.
61  */
62 static uint32_t LPI2C_GenerateCommands(lpi2c_master_edma_handle_t *handle);
63 
64 /*!
65  * @brief DMA completion callback.
66  * @param dmaHandle DMA channel handle for the channel that completed.
67  * @param userData User data associated with the channel handle. For this callback, the user data is the
68  *      LPI2C DMA driver handle.
69  * @param isTransferDone Whether the DMA transfer has completed.
70  * @param tcds Number of TCDs that completed.
71  */
72 static void LPI2C_MasterEDMACallback(edma_handle_t *dmaHandle, void *userData, bool isTransferDone, uint32_t tcds);
73 
74 /*!
75  * @brief LPI2C master edma transfer IRQ handle routine.
76  *
77  * This API handles the LPI2C bus error status and invoke callback if needed.
78  *
79  * @param base The LPI2C peripheral base address.
80  * @param lpi2cMasterEdmaHandle Pointer to the LPI2C master edma handle.
81  */
82 static void LPI2C_MasterTransferEdmaHandleIRQ(uint32_t instance, void *lpi2cMasterEdmaHandle);
83 /*******************************************************************************
84  * Variables
85  ******************************************************************************/
86 
87 static uint32_t lpi2c_edma_RecSetting = 0x02;
88 
89 /*! @brief Array to map LPI2C instance number to base pointer. */
90 static LPI2C_Type *const kLpi2cBases[] = LPI2C_BASE_PTRS;
91 
92 /*******************************************************************************
93  * Code
94  ******************************************************************************/
95 
96 /*!
97  * brief Create a new handle for the LPI2C master DMA APIs.
98  *
99  * The creation of a handle is for use with the DMA APIs. Once a handle
100  * is created, there is not a corresponding destroy handle. If the user wants to
101  * terminate a transfer, the LPI2C_MasterTransferAbortEDMA() API shall be called.
102  *
103  * For devices where the LPI2C send and receive DMA requests are OR'd together, the a txDmaHandle
104  * parameter is ignored and may be set to NULL.
105  *
106  * param base The LPI2C peripheral base address.
107  * param[out] handle Pointer to the LPI2C master driver handle.
108  * param rxDmaHandle Handle for the eDMA receive channel. Created by the user prior to calling this function.
109  * param txDmaHandle Handle for the eDMA transmit channel. Created by the user prior to calling this function.
110  * param callback User provided pointer to the asynchronous callback function.
111  * param userData User provided pointer to the application callback data.
112  */
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)113 void LPI2C_MasterCreateEDMAHandle(LPI2C_Type *base,
114                                   lpi2c_master_edma_handle_t *handle,
115                                   edma_handle_t *rxDmaHandle,
116                                   edma_handle_t *txDmaHandle,
117                                   lpi2c_master_edma_transfer_callback_t callback,
118                                   void *userData)
119 {
120     assert(handle != NULL);
121     assert(rxDmaHandle != NULL);
122     assert(txDmaHandle != NULL);
123 
124     /* Look up instance number */
125     uint32_t instance = LPI2C_GetInstance(base);
126 
127     /* Clear out the handle. */
128     (void)memset(handle, 0, sizeof(*handle));
129 
130     /* Set up the handle. For combined rx/tx DMA requests, the tx channel handle is set to the rx handle */
131     /* in order to make the transfer API code simpler. */
132     handle->base               = base;
133     handle->completionCallback = callback;
134     handle->userData           = userData;
135     handle->rx                 = rxDmaHandle;
136     handle->tx                 = (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) > 0) ? txDmaHandle : rxDmaHandle;
137 
138     if (LP_FLEXCOMM_GetBaseAddress(instance) != 0U)
139     {
140         lpi2c_to_lpflexcomm_edma_t handler;
141         handler.lpi2c_master_handler = LPI2C_MasterTransferEdmaHandleIRQ;
142 
143         LP_FLEXCOMM_SetIRQHandler(instance, handler.lpflexcomm_handler, handle, LP_FLEXCOMM_PERIPH_LPI2C);
144     }
145     else
146     {
147         /* Save the handle in global variables to support the double weak mechanism. */
148         s_lpi2cMasterHandle[instance] = handle;
149 
150         /* Set LPI2C_MasterTransferEdmaHandleIRQ as LPI2C DMA IRQ handler */
151         s_lpi2cMasterIsr = LPI2C_MasterTransferEdmaHandleIRQ;
152     }
153 
154     /* Enable interrupt in NVIC. */
155     (void)EnableIRQ(kLpi2cIrqs[instance]);
156 
157     /* Set DMA channel completion callbacks. */
158     EDMA_SetCallback(handle->rx, LPI2C_MasterEDMACallback, handle);
159     if (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) != 0)
160     {
161         EDMA_SetCallback(handle->tx, LPI2C_MasterEDMACallback, handle);
162     }
163 }
164 
LPI2C_GenerateCommands(lpi2c_master_edma_handle_t * handle)165 static uint32_t LPI2C_GenerateCommands(lpi2c_master_edma_handle_t *handle)
166 {
167     lpi2c_master_transfer_t *xfer = &handle->transfer;
168     uint16_t *cmd                 = (uint16_t *)&handle->commandBuffer;
169     uint32_t cmdCount             = 0;
170 
171     /* Handle no start option. */
172     if ((xfer->flags & (uint32_t)kLPI2C_TransferNoStartFlag) != 0U)
173     {
174         if (xfer->direction == kLPI2C_Read)
175         {
176             /* Need to issue read command first. */
177             cmd[cmdCount++] = (uint16_t)kRxDataCmd | (uint16_t)LPI2C_MTDR_DATA(xfer->dataSize - 1U);
178         }
179     }
180     else
181     {
182         /*
183          * Initial direction depends on whether a subaddress was provided, and of course the actual
184          * data transfer direction.
185          */
186         lpi2c_direction_t direction = (xfer->subaddressSize != 0U) ? kLPI2C_Write : xfer->direction;
187 
188         /* Start command. */
189         cmd[cmdCount++] =
190             (uint16_t)kStartCmd | (uint16_t)((uint16_t)((uint16_t)xfer->slaveAddress << 1U) | (uint16_t)direction);
191 
192         /* Subaddress, MSB first. */
193         if (xfer->subaddressSize != 0U)
194         {
195             uint32_t subaddressRemaining = xfer->subaddressSize;
196             while (0U != subaddressRemaining--)
197             {
198                 uint8_t subaddressByte = (uint8_t)(xfer->subaddress >> (8U * subaddressRemaining)) & 0xffU;
199                 cmd[cmdCount++]        = subaddressByte;
200             }
201         }
202 
203         /* Reads need special handling because we have to issue a read command and maybe a repeated start. */
204         if ((xfer->dataSize != 0U) && (xfer->direction == kLPI2C_Read))
205         {
206             /* Need to send repeated start if switching directions to read. */
207             if (direction == kLPI2C_Write)
208             {
209                 cmd[cmdCount++] = (uint16_t)kStartCmd |
210                                   (uint16_t)((uint16_t)((uint16_t)xfer->slaveAddress << 1U) | (uint16_t)kLPI2C_Read);
211             }
212 
213             /* Read command. A single write to MTDR can issue read operation of 0xFFU + 1 byte of data at most, so when
214               the dataSize is larger than 0x100U, push multiple read commands to MTDR until dataSize is reached. */
215             size_t tmpRxSize = xfer->dataSize;
216             while (tmpRxSize != 0U)
217             {
218                 if (tmpRxSize > 256U)
219                 {
220                     cmd[cmdCount++] = (uint16_t)kRxDataCmd | (uint16_t)LPI2C_MTDR_DATA(0xFFU);
221                     tmpRxSize -= 256U;
222                 }
223                 else
224                 {
225                     cmd[cmdCount++] = (uint16_t)kRxDataCmd | (uint16_t)LPI2C_MTDR_DATA(tmpRxSize - 1U);
226                     tmpRxSize       = 0U;
227                 }
228             }
229         }
230     }
231 
232     return cmdCount;
233 }
234 
235 /*!
236  * brief Performs a non-blocking DMA-based transaction on the I2C bus.
237  *
238  * The callback specified when the a handle was created is invoked when the transaction has
239  * completed.
240  *
241  * param base The LPI2C peripheral base address.
242  * param handle Pointer to the LPI2C master driver handle.
243  * param transfer The pointer to the transfer descriptor.
244  * retval #kStatus_Success The transaction was started successfully.
245  * retval #kStatus_LPI2C_Busy Either another master is currently utilizing the bus, or another DMA
246  *      transaction is already in progress.
247  */
LPI2C_MasterTransferEDMA(LPI2C_Type * base,lpi2c_master_edma_handle_t * handle,lpi2c_master_transfer_t * transfer)248 status_t LPI2C_MasterTransferEDMA(LPI2C_Type *base,
249                                   lpi2c_master_edma_handle_t *handle,
250                                   lpi2c_master_transfer_t *transfer)
251 {
252     status_t result;
253 
254     assert(handle != NULL);
255     assert(transfer != NULL);
256     assert(transfer->subaddressSize <= sizeof(transfer->subaddress));
257 
258     /* Check transfer data size in read operation. */
259     /* A single write to MTDR can issue read operation of 0xFFU + 1 byte of data at most, so when the dataSize is larger
260        than 0x100U, push multiple read commands to MTDR until dataSize is reached. LPI2C edma transfer uses linked
261        descriptor to transfer command and data, the command buffer is stored in handle. Allocate 4 command words to
262        carry read command which can cover nearly all use cases. */
263     if ((transfer->direction == kLPI2C_Read) && (transfer->dataSize > (256U * 4U)))
264     {
265         return kStatus_InvalidArgument;
266     }
267 
268     /* Return busy if another transaction is in progress. */
269     if (handle->isBusy)
270     {
271         return kStatus_LPI2C_Busy;
272     }
273 
274     /* Return an error if the bus is already in use not by us. */
275     result = LPI2C_CheckForBusyBus(base);
276     if (result != kStatus_Success)
277     {
278         return result;
279     }
280 
281     /* We're now busy. */
282     handle->isBusy = true;
283 
284     /* Disable LPI2C IRQ and DMA sources while we configure stuff. */
285     LPI2C_MasterDisableInterrupts(base, (uint32_t)kLPI2C_MasterIrqFlags);
286     LPI2C_MasterEnableDMA(base, false, false);
287 
288     /* Clear all flags. */
289     LPI2C_MasterClearStatusFlags(base, (uint32_t)kLPI2C_MasterClearFlags);
290 
291     /* Save transfer into handle. */
292     handle->transfer = *transfer;
293 
294     /* Generate commands to send. */
295     uint32_t commandCount = LPI2C_GenerateCommands(handle);
296 
297     /* If the user is transmitting no data with no start or stop, then just go ahead and invoke the callback. */
298     if ((0U == commandCount) && (transfer->dataSize == 0U))
299     {
300         if (handle->completionCallback != NULL)
301         {
302             handle->completionCallback(base, handle, kStatus_Success, handle->userData);
303         }
304         return kStatus_Success;
305     }
306 
307     /* Reset DMA channels. */
308     EDMA_ResetChannel(handle->rx->base, handle->rx->channel);
309     if (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) != 0)
310     {
311         EDMA_ResetChannel(handle->tx->base, handle->tx->channel);
312     }
313 
314     /* Get a 32-byte aligned TCD pointer. */
315     edma_tcd_t *tcd = (edma_tcd_t *)((uint32_t)(&handle->tcds[1]) & (~ALIGN_32_MASK));
316 
317     bool hasSendData    = (transfer->direction == kLPI2C_Write) && (transfer->dataSize != 0U);
318     bool hasReceiveData = (transfer->direction == kLPI2C_Read) && (transfer->dataSize != 0U);
319 
320     edma_transfer_config_t transferConfig = {0};
321     edma_tcd_t *linkTcd                   = NULL;
322 
323     /* Set up data transmit. */
324     if (hasSendData)
325     {
326         uint32_t *srcAddr               = (uint32_t *)transfer->data;
327         transferConfig.srcAddr          = (uint32_t)srcAddr;
328         transferConfig.destAddr         = (uint32_t)LPI2C_MasterGetTxFifoAddress(base);
329         transferConfig.srcTransferSize  = kEDMA_TransferSize1Bytes;
330         transferConfig.destTransferSize = kEDMA_TransferSize1Bytes;
331         transferConfig.srcOffset        = (int16_t)sizeof(uint8_t);
332         transferConfig.destOffset       = 0;
333         transferConfig.minorLoopBytes   = sizeof(uint8_t); /* TODO optimize to fill fifo */
334         transferConfig.majorLoopCounts  = transfer->dataSize;
335 
336         /* Store the initially configured eDMA minor byte transfer count into the LPI2C handle */
337         handle->nbytes = (uint8_t)transferConfig.minorLoopBytes;
338 
339         if (commandCount != 0U)
340         {
341 #if defined FSL_EDMA_DRIVER_EDMA4 && FSL_EDMA_DRIVER_EDMA4
342             /* Create a software TCD, which will be chained after the commands. */
343             EDMA_TcdResetExt(handle->tx->base, tcd);
344             EDMA_TcdSetTransferConfigExt(handle->tx->base, tcd, &transferConfig, NULL);
345             EDMA_TcdEnableInterruptsExt(handle->tx->base, tcd, (uint32_t)kEDMA_MajorInterruptEnable);
346 #else
347             /* Create a software TCD, which will be chained after the commands. */
348             EDMA_TcdReset(tcd);
349             EDMA_TcdSetTransferConfig(tcd, &transferConfig, NULL);
350             EDMA_TcdEnableInterrupts(tcd, (uint32_t)kEDMA_MajorInterruptEnable);
351 #endif
352             linkTcd = tcd;
353         }
354         else
355         {
356             /* User is only transmitting data with no required commands, so this transfer can stand alone. */
357             EDMA_SetTransferConfig(handle->tx->base, handle->tx->channel, &transferConfig, NULL);
358             EDMA_EnableChannelInterrupts(handle->tx->base, handle->tx->channel, (uint32_t)kEDMA_MajorInterruptEnable);
359         }
360     }
361     else if (hasReceiveData)
362     {
363         uint32_t *srcAddr = (uint32_t *)transfer->data;
364         /* Set up data receive. */
365         transferConfig.srcAddr          = (uint32_t)LPI2C_MasterGetRxFifoAddress(base);
366         transferConfig.destAddr         = (uint32_t)srcAddr;
367         transferConfig.srcTransferSize  = kEDMA_TransferSize1Bytes;
368         transferConfig.destTransferSize = kEDMA_TransferSize1Bytes;
369         transferConfig.srcOffset        = 0;
370         transferConfig.destOffset       = (int16_t)sizeof(uint8_t);
371         transferConfig.minorLoopBytes   = sizeof(uint8_t); /* TODO optimize to empty fifo */
372         transferConfig.majorLoopCounts  = transfer->dataSize;
373 
374         /* Store the initially configured eDMA minor byte transfer count into the LPI2C handle */
375         handle->nbytes = (uint8_t)transferConfig.minorLoopBytes;
376 
377         if ((FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) != 0) || (0U == commandCount))
378         {
379             /* We can put this receive transfer on its own DMA channel. */
380             EDMA_SetTransferConfig(handle->rx->base, handle->rx->channel, &transferConfig, NULL);
381             EDMA_EnableChannelInterrupts(handle->rx->base, handle->rx->channel, (uint32_t)kEDMA_MajorInterruptEnable);
382         }
383         else
384         {
385             /* For shared rx/tx DMA requests, when there are commands, create a software TCD of
386                enabling rx dma and disabling tx dma, which will be chained onto the commands transfer,
387                and create another software TCD of transfering data and chain it onto the last TCD.
388                Notice that in this situation assume tx/rx uses same channel */
389 #if defined FSL_EDMA_DRIVER_EDMA4 && FSL_EDMA_DRIVER_EDMA4
390             EDMA_TcdResetExt(handle->rx->base, tcd);
391             EDMA_TcdSetTransferConfigExt(handle->rx->base, tcd, &transferConfig, NULL);
392             EDMA_TcdEnableInterruptsExt(handle->rx->base, tcd, (uint32_t)kEDMA_MajorInterruptEnable);
393 #else
394             EDMA_TcdReset(tcd);
395             EDMA_TcdSetTransferConfig(tcd, &transferConfig, NULL);
396             EDMA_TcdEnableInterrupts(tcd, (uint32_t)kEDMA_MajorInterruptEnable);
397 #endif
398 
399             transferConfig.srcAddr          = (uint32_t)&lpi2c_edma_RecSetting;
400             transferConfig.destAddr         = (uint32_t) & (base->MDER);
401             transferConfig.srcTransferSize  = kEDMA_TransferSize1Bytes;
402             transferConfig.destTransferSize = kEDMA_TransferSize1Bytes;
403             transferConfig.srcOffset        = 0;
404             transferConfig.destOffset       = (int16_t)sizeof(uint8_t);
405             transferConfig.minorLoopBytes   = sizeof(uint8_t);
406             transferConfig.majorLoopCounts  = 1;
407 
408             edma_tcd_t *tcdSetRxClearTxDMA = (edma_tcd_t *)((uint32_t)(&handle->tcds[2]) & (~ALIGN_32_MASK));
409 
410 #if defined FSL_EDMA_DRIVER_EDMA4 && FSL_EDMA_DRIVER_EDMA4
411             EDMA_TcdResetExt(handle->rx->base, tcdSetRxClearTxDMA);
412             EDMA_TcdSetTransferConfigExt(handle->rx->base, tcdSetRxClearTxDMA, &transferConfig, tcd);
413 #else
414             EDMA_TcdReset(tcdSetRxClearTxDMA);
415             EDMA_TcdSetTransferConfig(tcdSetRxClearTxDMA, &transferConfig, tcd);
416 #endif
417             linkTcd = tcdSetRxClearTxDMA;
418         }
419     }
420     else
421     {
422         /* No data to send */
423     }
424 
425     if (hasSendData)
426     {
427     }
428 
429     /* Set up commands transfer. */
430     if (commandCount != 0U)
431     {
432         transferConfig.srcAddr          = (uint32_t)handle->commandBuffer;
433         transferConfig.destAddr         = (uint32_t)LPI2C_MasterGetTxFifoAddress(base);
434         transferConfig.srcTransferSize  = kEDMA_TransferSize2Bytes;
435         transferConfig.destTransferSize = kEDMA_TransferSize2Bytes;
436         transferConfig.srcOffset        = (int16_t)sizeof(uint16_t);
437         transferConfig.destOffset       = 0;
438         transferConfig.minorLoopBytes   = sizeof(uint16_t); /* TODO optimize to fill fifo */
439         transferConfig.majorLoopCounts  = commandCount;
440 
441         EDMA_SetTransferConfig(handle->tx->base, handle->tx->channel, &transferConfig, linkTcd);
442     }
443 
444     /* Start DMA transfer. */
445     if (hasReceiveData || (0 == FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base)))
446     {
447         EDMA_StartTransfer(handle->rx);
448     }
449 
450     if ((hasSendData || (commandCount != 0U)) && (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) != 0))
451     {
452         EDMA_StartTransfer(handle->tx);
453     }
454 
455     /* Enable DMA in both directions. This actually kicks of the transfer. */
456     LPI2C_MasterEnableDMA(base, true, true);
457 
458     /* Enable all LPI2C master interrupts */
459     LPI2C_MasterEnableInterrupts(base,
460                                  (uint32_t)kLPI2C_MasterArbitrationLostFlag | (uint32_t)kLPI2C_MasterNackDetectFlag |
461                                      (uint32_t)kLPI2C_MasterPinLowTimeoutFlag | (uint32_t)kLPI2C_MasterFifoErrFlag);
462 
463     return result;
464 }
465 
466 /*!
467  * brief Returns number of bytes transferred so far.
468  *
469  * param base The LPI2C peripheral base address.
470  * param handle Pointer to the LPI2C master driver handle.
471  * param[out] count Number of bytes transferred so far by the non-blocking transaction.
472  * retval #kStatus_Success
473  * retval #kStatus_NoTransferInProgress There is not a DMA transaction currently in progress.
474  */
LPI2C_MasterTransferGetCountEDMA(LPI2C_Type * base,lpi2c_master_edma_handle_t * handle,size_t * count)475 status_t LPI2C_MasterTransferGetCountEDMA(LPI2C_Type *base, lpi2c_master_edma_handle_t *handle, size_t *count)
476 {
477     assert(handle != NULL);
478 
479     if (NULL == count)
480     {
481         return kStatus_InvalidArgument;
482     }
483 
484     /* Catch when there is not an active transfer. */
485     if (!handle->isBusy)
486     {
487         *count = 0;
488         return kStatus_NoTransferInProgress;
489     }
490 
491     uint32_t remaining = handle->transfer.dataSize;
492 
493     /* If the DMA is still on a commands transfer that chains to the actual data transfer, */
494     /* we do nothing and return the number of transferred bytes as zero. */
495     if (EDMA_GetNextTCDAddress(handle->tx) == 0U)
496     {
497         if (handle->transfer.direction == kLPI2C_Write)
498         {
499             remaining =
500                 (uint32_t)handle->nbytes * EDMA_GetRemainingMajorLoopCount(handle->tx->base, handle->tx->channel);
501         }
502         else
503         {
504             remaining =
505                 (uint32_t)handle->nbytes * EDMA_GetRemainingMajorLoopCount(handle->rx->base, handle->rx->channel);
506         }
507     }
508 
509     *count = handle->transfer.dataSize - remaining;
510 
511     return kStatus_Success;
512 }
513 
514 /*!
515  * brief Terminates a non-blocking LPI2C master transmission early.
516  *
517  * note It is not safe to call this function from an IRQ handler that has a higher priority than the
518  *      eDMA peripheral's IRQ priority.
519  *
520  * param base The LPI2C peripheral base address.
521  * param handle Pointer to the LPI2C master driver handle.
522  * retval #kStatus_Success A transaction was successfully aborted.
523  * retval #kStatus_LPI2C_Idle There is not a DMA transaction currently in progress.
524  */
LPI2C_MasterTransferAbortEDMA(LPI2C_Type * base,lpi2c_master_edma_handle_t * handle)525 status_t LPI2C_MasterTransferAbortEDMA(LPI2C_Type *base, lpi2c_master_edma_handle_t *handle)
526 {
527     /* Catch when there is not an active transfer. */
528     if (!handle->isBusy)
529     {
530         return kStatus_LPI2C_Idle;
531     }
532 
533     /* Terminate DMA transfers. */
534     EDMA_AbortTransfer(handle->rx);
535     if (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) != 0)
536     {
537         EDMA_AbortTransfer(handle->tx);
538     }
539 
540     /* Reset fifos. */
541     base->MCR |= LPI2C_MCR_RRF_MASK | LPI2C_MCR_RTF_MASK;
542 
543     /* Disable LPI2C interrupts. */
544     LPI2C_MasterDisableInterrupts(base, (uint32_t)kLPI2C_MasterIrqFlags);
545 
546     /* If master is still busy and has not send out stop signal yet. */
547     if ((LPI2C_MasterGetStatusFlags(base) &
548          ((uint32_t)kLPI2C_MasterStopDetectFlag | (uint32_t)kLPI2C_MasterBusyFlag)) == (uint32_t)kLPI2C_MasterBusyFlag)
549     {
550         /* Send a stop command to finalize the transfer. */
551         base->MTDR = (uint32_t)kStopCmd;
552     }
553 
554     /* Reset handle. */
555     handle->isBusy = false;
556 
557     return kStatus_Success;
558 }
559 
LPI2C_MasterEDMACallback(edma_handle_t * dmaHandle,void * userData,bool isTransferDone,uint32_t tcds)560 static void LPI2C_MasterEDMACallback(edma_handle_t *dmaHandle, void *userData, bool isTransferDone, uint32_t tcds)
561 {
562     lpi2c_master_edma_handle_t *handle = (lpi2c_master_edma_handle_t *)userData;
563 
564     if (NULL == handle)
565     {
566         return;
567     }
568 
569     /* Check for errors. */
570     status_t result = LPI2C_MasterCheckAndClearError(handle->base, LPI2C_MasterGetStatusFlags(handle->base));
571 
572     /* Done with this transaction. */
573     handle->isBusy = false;
574 
575     if (0U == (handle->transfer.flags & (uint32_t)kLPI2C_TransferNoStopFlag))
576     {
577         /* Send a stop command to finalize the transfer. */
578         handle->base->MTDR = (uint32_t)kStopCmd;
579     }
580 
581     /* Invoke callback. */
582     if (handle->completionCallback != NULL)
583     {
584         handle->completionCallback(handle->base, handle, result, handle->userData);
585     }
586 }
587 
LPI2C_MasterTransferEdmaHandleIRQ(uint32_t instance,void * lpi2cMasterEdmaHandle)588 static void LPI2C_MasterTransferEdmaHandleIRQ(uint32_t instance, void *lpi2cMasterEdmaHandle)
589 {
590     assert(lpi2cMasterEdmaHandle != NULL);
591     assert(instance < ARRAY_SIZE(kLpi2cBases));
592     LPI2C_Type *base = kLpi2cBases[instance];
593 
594     lpi2c_master_edma_handle_t *handle = (lpi2c_master_edma_handle_t *)lpi2cMasterEdmaHandle;
595     uint32_t status                    = LPI2C_MasterGetStatusFlags(base);
596     status_t result                    = kStatus_Success;
597 
598     /* Terminate DMA transfers. */
599     EDMA_AbortTransfer(handle->rx);
600     if (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) != 0)
601     {
602         EDMA_AbortTransfer(handle->tx);
603     }
604 
605     /* Done with this transaction. */
606     handle->isBusy = false;
607 
608     /* Disable LPI2C interrupts. */
609     LPI2C_MasterDisableInterrupts(base, (uint32_t)kLPI2C_MasterIrqFlags);
610 
611     /* Check error status */
612     if (0U != (status & (uint32_t)kLPI2C_MasterPinLowTimeoutFlag))
613     {
614         result = kStatus_LPI2C_PinLowTimeout;
615     }
616     else if (0U != (status & (uint32_t)kLPI2C_MasterArbitrationLostFlag))
617     {
618         result = kStatus_LPI2C_ArbitrationLost;
619     }
620     else if (0U != (status & (uint32_t)kLPI2C_MasterNackDetectFlag))
621     {
622         result = kStatus_LPI2C_Nak;
623     }
624     else if (0U != (status & (uint32_t)kLPI2C_MasterFifoErrFlag))
625     {
626         result = kStatus_LPI2C_FifoError;
627     }
628     else
629     {
630         ; /* Intentional empty */
631     }
632 
633     /* Clear error status. */
634     (void)LPI2C_MasterCheckAndClearError(base, status);
635 
636     /* Send stop flag if needed */
637     if (0U == (handle->transfer.flags & (uint32_t)kLPI2C_TransferNoStopFlag))
638     {
639         status = LPI2C_MasterGetStatusFlags(base);
640         /* If bus is still busy and the master has not generate stop flag */
641         if ((status & ((uint32_t)kLPI2C_MasterBusBusyFlag | (uint32_t)kLPI2C_MasterStopDetectFlag)) ==
642             (uint32_t)kLPI2C_MasterBusBusyFlag)
643         {
644             /* Send a stop command to finalize the transfer. */
645             handle->base->MTDR = (uint32_t)kStopCmd;
646         }
647     }
648 
649     /* Invoke callback. */
650     if (handle->completionCallback != NULL)
651     {
652         handle->completionCallback(base, handle, result, handle->userData);
653     }
654 }
655