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