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