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 /* Create a software TCD, which will be chained after the commands. */
342 EDMA_TcdReset(tcd);
343 EDMA_TcdSetTransferConfig(tcd, &transferConfig, NULL);
344 EDMA_TcdEnableInterrupts(tcd, (uint32_t)kEDMA_MajorInterruptEnable);
345 linkTcd = tcd;
346 }
347 else
348 {
349 /* User is only transmitting data with no required commands, so this transfer can stand alone. */
350 EDMA_SetTransferConfig(handle->tx->base, handle->tx->channel, &transferConfig, NULL);
351 EDMA_EnableChannelInterrupts(handle->tx->base, handle->tx->channel, (uint32_t)kEDMA_MajorInterruptEnable);
352 }
353 }
354 else if (hasReceiveData)
355 {
356 uint32_t *srcAddr = (uint32_t *)transfer->data;
357 /* Set up data receive. */
358 transferConfig.srcAddr = (uint32_t)LPI2C_MasterGetRxFifoAddress(base);
359 transferConfig.destAddr = (uint32_t)srcAddr;
360 transferConfig.srcTransferSize = kEDMA_TransferSize1Bytes;
361 transferConfig.destTransferSize = kEDMA_TransferSize1Bytes;
362 transferConfig.srcOffset = 0;
363 transferConfig.destOffset = (int16_t)sizeof(uint8_t);
364 transferConfig.minorLoopBytes = sizeof(uint8_t); /* TODO optimize to empty fifo */
365 transferConfig.majorLoopCounts = transfer->dataSize;
366
367 /* Store the initially configured eDMA minor byte transfer count into the LPI2C handle */
368 handle->nbytes = (uint8_t)transferConfig.minorLoopBytes;
369
370 if ((FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) != 0) || (0U == commandCount))
371 {
372 /* We can put this receive transfer on its own DMA channel. */
373 EDMA_SetTransferConfig(handle->rx->base, handle->rx->channel, &transferConfig, NULL);
374 EDMA_EnableChannelInterrupts(handle->rx->base, handle->rx->channel, (uint32_t)kEDMA_MajorInterruptEnable);
375 }
376 else
377 {
378 /* For shared rx/tx DMA requests, when there are commands, create a software TCD of
379 enabling rx dma and disabling tx dma, which will be chained onto the commands transfer,
380 and create another software TCD of transfering data and chain it onto the last TCD.
381 Notice that in this situation assume tx/rx uses same channel */
382 EDMA_TcdReset(tcd);
383 EDMA_TcdSetTransferConfig(tcd, &transferConfig, NULL);
384 EDMA_TcdEnableInterrupts(tcd, (uint32_t)kEDMA_MajorInterruptEnable);
385
386 transferConfig.srcAddr = (uint32_t)&lpi2c_edma_RecSetting;
387 transferConfig.destAddr = (uint32_t) & (base->MDER);
388 transferConfig.srcTransferSize = kEDMA_TransferSize1Bytes;
389 transferConfig.destTransferSize = kEDMA_TransferSize1Bytes;
390 transferConfig.srcOffset = 0;
391 transferConfig.destOffset = (int16_t)sizeof(uint8_t);
392 transferConfig.minorLoopBytes = sizeof(uint8_t);
393 transferConfig.majorLoopCounts = 1;
394
395 edma_tcd_t *tcdSetRxClearTxDMA = (edma_tcd_t *)((uint32_t)(&handle->tcds[2]) & (~ALIGN_32_MASK));
396
397 EDMA_TcdReset(tcdSetRxClearTxDMA);
398 EDMA_TcdSetTransferConfig(tcdSetRxClearTxDMA, &transferConfig, tcd);
399 linkTcd = tcdSetRxClearTxDMA;
400 }
401 }
402 else
403 {
404 /* No data to send */
405 }
406
407 if (hasSendData)
408 {
409 }
410
411 /* Set up commands transfer. */
412 if (commandCount != 0U)
413 {
414 transferConfig.srcAddr = (uint32_t)handle->commandBuffer;
415 transferConfig.destAddr = (uint32_t)LPI2C_MasterGetTxFifoAddress(base);
416 transferConfig.srcTransferSize = kEDMA_TransferSize2Bytes;
417 transferConfig.destTransferSize = kEDMA_TransferSize2Bytes;
418 transferConfig.srcOffset = (int16_t)sizeof(uint16_t);
419 transferConfig.destOffset = 0;
420 transferConfig.minorLoopBytes = sizeof(uint16_t); /* TODO optimize to fill fifo */
421 transferConfig.majorLoopCounts = commandCount;
422
423 EDMA_SetTransferConfig(handle->tx->base, handle->tx->channel, &transferConfig, linkTcd);
424 }
425
426 /* Start DMA transfer. */
427 if (hasReceiveData || (0 == FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base)))
428 {
429 EDMA_StartTransfer(handle->rx);
430 }
431
432 if ((hasSendData || (commandCount != 0U)) && (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) != 0))
433 {
434 EDMA_StartTransfer(handle->tx);
435 }
436
437 /* Enable DMA in both directions. This actually kicks of the transfer. */
438 LPI2C_MasterEnableDMA(base, true, true);
439
440 /* Enable all LPI2C master interrupts */
441 LPI2C_MasterEnableInterrupts(base,
442 (uint32_t)kLPI2C_MasterArbitrationLostFlag | (uint32_t)kLPI2C_MasterNackDetectFlag |
443 (uint32_t)kLPI2C_MasterPinLowTimeoutFlag | (uint32_t)kLPI2C_MasterFifoErrFlag);
444
445 return result;
446 }
447
448 /*!
449 * brief Returns number of bytes transferred so far.
450 *
451 * param base The LPI2C peripheral base address.
452 * param handle Pointer to the LPI2C master driver handle.
453 * param[out] count Number of bytes transferred so far by the non-blocking transaction.
454 * retval #kStatus_Success
455 * retval #kStatus_NoTransferInProgress There is not a DMA transaction currently in progress.
456 */
LPI2C_MasterTransferGetCountEDMA(LPI2C_Type * base,lpi2c_master_edma_handle_t * handle,size_t * count)457 status_t LPI2C_MasterTransferGetCountEDMA(LPI2C_Type *base, lpi2c_master_edma_handle_t *handle, size_t *count)
458 {
459 assert(handle != NULL);
460
461 if (NULL == count)
462 {
463 return kStatus_InvalidArgument;
464 }
465
466 /* Catch when there is not an active transfer. */
467 if (!handle->isBusy)
468 {
469 *count = 0;
470 return kStatus_NoTransferInProgress;
471 }
472
473 uint32_t remaining = handle->transfer.dataSize;
474
475 /* If the DMA is still on a commands transfer that chains to the actual data transfer, */
476 /* we do nothing and return the number of transferred bytes as zero. */
477 if (EDMA_GetNextTCDAddress(handle->tx) == 0U)
478 {
479 if (handle->transfer.direction == kLPI2C_Write)
480 {
481 remaining =
482 (uint32_t)handle->nbytes * EDMA_GetRemainingMajorLoopCount(handle->tx->base, handle->tx->channel);
483 }
484 else
485 {
486 remaining =
487 (uint32_t)handle->nbytes * EDMA_GetRemainingMajorLoopCount(handle->rx->base, handle->rx->channel);
488 }
489 }
490
491 *count = handle->transfer.dataSize - remaining;
492
493 return kStatus_Success;
494 }
495
496 /*!
497 * brief Terminates a non-blocking LPI2C master transmission early.
498 *
499 * note It is not safe to call this function from an IRQ handler that has a higher priority than the
500 * eDMA peripheral's IRQ priority.
501 *
502 * param base The LPI2C peripheral base address.
503 * param handle Pointer to the LPI2C master driver handle.
504 * retval #kStatus_Success A transaction was successfully aborted.
505 * retval #kStatus_LPI2C_Idle There is not a DMA transaction currently in progress.
506 */
LPI2C_MasterTransferAbortEDMA(LPI2C_Type * base,lpi2c_master_edma_handle_t * handle)507 status_t LPI2C_MasterTransferAbortEDMA(LPI2C_Type *base, lpi2c_master_edma_handle_t *handle)
508 {
509 /* Catch when there is not an active transfer. */
510 if (!handle->isBusy)
511 {
512 return kStatus_LPI2C_Idle;
513 }
514
515 /* Terminate DMA transfers. */
516 EDMA_AbortTransfer(handle->rx);
517 if (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) != 0)
518 {
519 EDMA_AbortTransfer(handle->tx);
520 }
521
522 /* Reset fifos. */
523 base->MCR |= LPI2C_MCR_RRF_MASK | LPI2C_MCR_RTF_MASK;
524
525 /* Disable LPI2C interrupts. */
526 LPI2C_MasterDisableInterrupts(base, (uint32_t)kLPI2C_MasterIrqFlags);
527
528 /* If master is still busy and has not send out stop signal yet. */
529 if ((LPI2C_MasterGetStatusFlags(base) &
530 ((uint32_t)kLPI2C_MasterStopDetectFlag | (uint32_t)kLPI2C_MasterBusyFlag)) == (uint32_t)kLPI2C_MasterBusyFlag)
531 {
532 /* Send a stop command to finalize the transfer. */
533 base->MTDR = (uint32_t)kStopCmd;
534 }
535
536 /* Reset handle. */
537 handle->isBusy = false;
538
539 return kStatus_Success;
540 }
541
LPI2C_MasterEDMACallback(edma_handle_t * dmaHandle,void * userData,bool isTransferDone,uint32_t tcds)542 static void LPI2C_MasterEDMACallback(edma_handle_t *dmaHandle, void *userData, bool isTransferDone, uint32_t tcds)
543 {
544 lpi2c_master_edma_handle_t *handle = (lpi2c_master_edma_handle_t *)userData;
545
546 if (NULL == handle)
547 {
548 return;
549 }
550
551 /* Check for errors. */
552 status_t result = LPI2C_MasterCheckAndClearError(handle->base, LPI2C_MasterGetStatusFlags(handle->base));
553
554 /* Done with this transaction. */
555 handle->isBusy = false;
556
557 if (0U == (handle->transfer.flags & (uint32_t)kLPI2C_TransferNoStopFlag))
558 {
559 /* Send a stop command to finalize the transfer. */
560 handle->base->MTDR = (uint32_t)kStopCmd;
561 }
562
563 /* Invoke callback. */
564 if (handle->completionCallback != NULL)
565 {
566 handle->completionCallback(handle->base, handle, result, handle->userData);
567 }
568 }
569
LPI2C_MasterTransferEdmaHandleIRQ(uint32_t instance,void * lpi2cMasterEdmaHandle)570 static void LPI2C_MasterTransferEdmaHandleIRQ(uint32_t instance, void *lpi2cMasterEdmaHandle)
571 {
572 assert(lpi2cMasterEdmaHandle != NULL);
573 assert(instance < ARRAY_SIZE(kLpi2cBases));
574 LPI2C_Type *base = kLpi2cBases[instance];
575
576 lpi2c_master_edma_handle_t *handle = (lpi2c_master_edma_handle_t *)lpi2cMasterEdmaHandle;
577 uint32_t status = LPI2C_MasterGetStatusFlags(base);
578 status_t result = kStatus_Success;
579
580 /* Terminate DMA transfers. */
581 EDMA_AbortTransfer(handle->rx);
582 if (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) != 0)
583 {
584 EDMA_AbortTransfer(handle->tx);
585 }
586
587 /* Done with this transaction. */
588 handle->isBusy = false;
589
590 /* Disable LPI2C interrupts. */
591 LPI2C_MasterDisableInterrupts(base, (uint32_t)kLPI2C_MasterIrqFlags);
592
593 /* Check error status */
594 if (0U != (status & (uint32_t)kLPI2C_MasterPinLowTimeoutFlag))
595 {
596 result = kStatus_LPI2C_PinLowTimeout;
597 }
598 else if (0U != (status & (uint32_t)kLPI2C_MasterArbitrationLostFlag))
599 {
600 result = kStatus_LPI2C_ArbitrationLost;
601 }
602 else if (0U != (status & (uint32_t)kLPI2C_MasterNackDetectFlag))
603 {
604 result = kStatus_LPI2C_Nak;
605 }
606 else if (0U != (status & (uint32_t)kLPI2C_MasterFifoErrFlag))
607 {
608 result = kStatus_LPI2C_FifoError;
609 }
610 else
611 {
612 ; /* Intentional empty */
613 }
614
615 /* Clear error status. */
616 (void)LPI2C_MasterCheckAndClearError(base, status);
617
618 /* Send stop flag if needed */
619 if (0U == (handle->transfer.flags & (uint32_t)kLPI2C_TransferNoStopFlag))
620 {
621 status = LPI2C_MasterGetStatusFlags(base);
622 /* If bus is still busy and the master has not generate stop flag */
623 if ((status & ((uint32_t)kLPI2C_MasterBusBusyFlag | (uint32_t)kLPI2C_MasterStopDetectFlag)) ==
624 (uint32_t)kLPI2C_MasterBusBusyFlag)
625 {
626 /* Send a stop command to finalize the transfer. */
627 handle->base->MTDR = (uint32_t)kStopCmd;
628 }
629 }
630
631 /* Invoke callback. */
632 if (handle->completionCallback != NULL)
633 {
634 handle->completionCallback(base, handle, result, handle->userData);
635 }
636 }