1 /*
2 * Copyright 2022-2023 NXP
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include "fsl_i3c_dma.h"
8
9 /*******************************************************************************
10 * Definitions
11 ******************************************************************************/
12
13 /* Component ID definition, used by tools. */
14 #ifndef FSL_COMPONENT_ID
15 #define FSL_COMPONENT_ID "platform.drivers.i3c_dma"
16 #endif
17
18 /*! @brief States for the state machine used by transactional APIs. */
19 enum _i3c_dma_transfer_states
20 {
21 kIdleState = 0,
22 kIBIWonState,
23 kSlaveStartState,
24 kSendCommandState,
25 kWaitRepeatedStartCompleteState,
26 kTransmitDataState,
27 kReceiveDataState,
28 kStopState,
29 kWaitForCompletionState,
30 kAddressMatchState,
31 };
32
33 /*! @brief Common sets of flags used by the driver. */
34 enum _i3c_dma_flag_constants
35 {
36 /*! All flags which are cleared by the driver upon starting a transfer. */
37 kMasterClearFlags = kI3C_MasterSlaveStartFlag | kI3C_MasterControlDoneFlag | kI3C_MasterCompleteFlag |
38 kI3C_MasterArbitrationWonFlag | kI3C_MasterSlave2MasterFlag | kI3C_MasterErrorFlag,
39
40 /*! IRQ sources enabled by the non-blocking transactional API. */
41 kMasterDMAIrqFlags = kI3C_MasterSlaveStartFlag | kI3C_MasterControlDoneFlag | kI3C_MasterCompleteFlag |
42 kI3C_MasterArbitrationWonFlag | kI3C_MasterErrorFlag | kI3C_MasterSlave2MasterFlag,
43
44 /*! Errors to check for. */
45 kMasterErrorFlags = kI3C_MasterErrorNackFlag | kI3C_MasterErrorWriteAbortFlag |
46 #if !defined(FSL_FEATURE_I3C_HAS_NO_MERRWARN_TERM) || (!FSL_FEATURE_I3C_HAS_NO_MERRWARN_TERM)
47 kI3C_MasterErrorTermFlag |
48 #endif
49 kI3C_MasterErrorParityFlag | kI3C_MasterErrorCrcFlag | kI3C_MasterErrorReadFlag |
50 kI3C_MasterErrorWriteFlag | kI3C_MasterErrorMsgFlag | kI3C_MasterErrorInvalidReqFlag |
51 kI3C_MasterErrorTimeoutFlag,
52 /*! All flags which are cleared by the driver upon starting a transfer. */
53 kSlaveClearFlags = kI3C_SlaveBusStartFlag | kI3C_SlaveMatchedFlag | kI3C_SlaveBusStopFlag,
54
55 /*! IRQ sources enabled by the non-blocking transactional API. */
56 kSlaveDMAIrqFlags = kI3C_SlaveBusStartFlag | kI3C_SlaveMatchedFlag |
57 kI3C_SlaveBusStopFlag | /*kI3C_SlaveRxReadyFlag |*/
58 kI3C_SlaveDynamicAddrChangedFlag | kI3C_SlaveReceivedCCCFlag | kI3C_SlaveErrorFlag |
59 kI3C_SlaveHDRCommandMatchFlag | kI3C_SlaveCCCHandledFlag | kI3C_SlaveEventSentFlag,
60
61 /*! Errors to check for. */
62 kSlaveErrorFlags = kI3C_SlaveErrorOverrunFlag | kI3C_SlaveErrorUnderrunFlag | kI3C_SlaveErrorUnderrunNakFlag |
63 kI3C_SlaveErrorTermFlag | kI3C_SlaveErrorInvalidStartFlag | kI3C_SlaveErrorSdrParityFlag |
64 kI3C_SlaveErrorHdrParityFlag | kI3C_SlaveErrorHdrCRCFlag | kI3C_SlaveErrorS0S1Flag |
65 kI3C_SlaveErrorOverreadFlag | kI3C_SlaveErrorOverwriteFlag,
66 };
67
68 /*******************************************************************************
69 * Variables
70 ******************************************************************************/
71 /*! @brief Array to map I3C instance number to base pointer. */
72 static I3C_Type *const kI3cBases[] = I3C_BASE_PTRS;
73
74 /*! @brief DMA linked transfer descriptor. */
75 DMA_ALLOCATE_LINK_DESCRIPTORS(static s_dma_table, ARRAY_SIZE(kI3cBases));
76
77 /*******************************************************************************
78 * Prototypes
79 ******************************************************************************/
80 static void I3C_MasterRunDMATransfer(
81 I3C_Type *base, i3c_master_dma_handle_t *handle, void *data, size_t dataSize, i3c_direction_t direction);
82
83 /*******************************************************************************
84 * Code
85 ******************************************************************************/
I3C_MasterTransferDMACallbackRx(dma_handle_t * dmaHandle,void * param,bool transferDone,uint32_t tcds)86 static void I3C_MasterTransferDMACallbackRx(dma_handle_t *dmaHandle, void *param, bool transferDone, uint32_t tcds)
87 {
88 i3c_master_dma_handle_t *i3cHandle = (i3c_master_dma_handle_t *)param;
89
90 if (transferDone)
91 {
92 /* Read last data byte */
93 i3cHandle->base->MCTRL |= I3C_MCTRL_RDTERM(1U);
94 size_t rxCount = 0U;
95 while (rxCount == 0U)
96 {
97 I3C_MasterGetFifoCounts(i3cHandle->base, &rxCount, NULL);
98 };
99 *(uint8_t *)((uint32_t)((uint32_t *)i3cHandle->transfer.data) + i3cHandle->transfer.dataSize - 1U) =
100 (uint8_t)i3cHandle->base->MRDATAB;
101
102 /* Disable I3C Rx DMA */
103 i3cHandle->base->MDATACTRL &= ~I3C_MDMACTRL_DMAFB_MASK;
104
105 i3cHandle->state = (uint8_t)kStopState;
106 I3C_MasterTransferDMAHandleIRQ(i3cHandle->base, i3cHandle);
107 }
108 }
109
I3C_MasterTransferDMACallbackTx(dma_handle_t * dmaHandle,void * param,bool transferDone,uint32_t tcds)110 static void I3C_MasterTransferDMACallbackTx(dma_handle_t *dmaHandle, void *param, bool transferDone, uint32_t tcds)
111 {
112 i3c_master_dma_handle_t *i3cHandle = (i3c_master_dma_handle_t *)param;
113
114 if (transferDone)
115 {
116 /* Disable I3C Tx DMA */
117 i3cHandle->base->MDATACTRL &= ~I3C_MDMACTRL_DMATB_MASK;
118 i3cHandle->state = (uint8_t)kStopState;
119
120 size_t txCount = 0U;
121 do
122 {
123 I3C_MasterGetFifoCounts(i3cHandle->base, NULL, &txCount);
124 } while (txCount != 0U);
125 I3C_MasterTransferDMAHandleIRQ(i3cHandle->base, i3cHandle);
126 }
127 }
128 /*!
129 * brief Prepares the transfer state machine and fills in the command buffer.
130 * param handle Master nonblocking driver handle.
131 */
I3C_MasterInitTransferStateMachineDMA(I3C_Type * base,i3c_master_dma_handle_t * handle)132 static status_t I3C_MasterInitTransferStateMachineDMA(I3C_Type *base, i3c_master_dma_handle_t *handle)
133 {
134 i3c_master_transfer_t *xfer = &handle->transfer;
135 status_t result = kStatus_Success;
136 i3c_direction_t direction = xfer->direction;
137
138 /* Calculate command count and put into command buffer. */
139 handle->subaddressCount = 0U;
140 if (xfer->subaddressSize != 0U)
141 {
142 for (uint32_t i = xfer->subaddressSize; i > 0U; i--)
143 {
144 handle->subaddressBuffer[handle->subaddressCount++] = (uint8_t)((xfer->subaddress) >> (8U * (i - 1U)));
145 }
146 }
147
148 /* Start condition shall be ommited, switch directly to next phase */
149 if (xfer->dataSize == 0U)
150 {
151 handle->state = (uint8_t)kStopState;
152 }
153
154 /* Handle no start option. */
155 if (0U != (xfer->flags & (uint32_t)kI3C_TransferNoStartFlag))
156 {
157 /* No need to send start flag, directly go to send command or data */
158 if (xfer->subaddressSize > 0UL)
159 {
160 handle->state = (uint8_t)kSendCommandState;
161 }
162 else
163 {
164 if (direction == kI3C_Write)
165 {
166 /* Next state, send data. */
167 handle->state = (uint8_t)kTransmitDataState;
168 }
169 else
170 {
171 /* Only support write with no stop signal. */
172 return kStatus_InvalidArgument;
173 }
174 }
175 }
176 else
177 {
178 if (xfer->subaddressSize != 0U)
179 {
180 handle->state = (uint8_t)kSendCommandState;
181 }
182 else
183 {
184 if (handle->transfer.direction == kI3C_Write)
185 {
186 handle->state = (uint8_t)kTransmitDataState;
187 }
188 else if (handle->transfer.direction == kI3C_Read)
189 {
190 handle->state = (uint8_t)kReceiveDataState;
191 }
192 else
193 {
194 return kStatus_InvalidArgument;
195 }
196 }
197
198 if (handle->transfer.direction == kI3C_Read)
199 {
200 I3C_MasterRunDMATransfer(base, handle, xfer->data, xfer->dataSize - 1U, kI3C_Read);
201 }
202
203 if (handle->state != (uint8_t)kStopState)
204 {
205 /* If repeated start is requested, send repeated start. */
206 if (0U != (xfer->flags & (uint32_t)kI3C_TransferRepeatedStartFlag))
207 {
208 result = I3C_MasterRepeatedStart(base, xfer->busType, xfer->slaveAddress, direction);
209 }
210 else /* For normal transfer, send start. */
211 {
212 result = I3C_MasterStart(base, xfer->busType, xfer->slaveAddress, direction);
213 }
214 }
215 }
216
217 I3C_MasterTransferDMAHandleIRQ(base, handle);
218 return result;
219 }
220
I3C_MasterRunDMATransfer(I3C_Type * base,i3c_master_dma_handle_t * handle,void * data,size_t dataSize,i3c_direction_t direction)221 static void I3C_MasterRunDMATransfer(
222 I3C_Type *base, i3c_master_dma_handle_t *handle, void *data, size_t dataSize, i3c_direction_t direction)
223 {
224 dma_transfer_config_t xferConfig;
225 uint32_t address;
226 bool isEnableTxDMA = false;
227 bool isEnableRxDMA = false;
228 uint32_t width;
229
230 handle->transferCount = dataSize;
231
232 switch (direction)
233 {
234 case kI3C_Write:
235 address = (uint32_t)&base->MWDATAB1;
236 DMA_PrepareTransfer(&xferConfig, data, (uint32_t *)address, sizeof(uint8_t), dataSize,
237 kDMA_MemoryToPeripheral, NULL);
238 (void)DMA_SubmitTransfer(handle->txDmaHandle, &xferConfig);
239 DMA_StartTransfer(handle->txDmaHandle);
240 isEnableTxDMA = true;
241 width = 2U;
242 break;
243
244 case kI3C_Read:
245 address = (uint32_t)&base->MRDATAB;
246 DMA_PrepareTransfer(&xferConfig, (uint32_t *)address, data, sizeof(uint8_t), dataSize,
247 kDMA_PeripheralToMemory, NULL);
248 (void)DMA_SubmitTransfer(handle->rxDmaHandle, &xferConfig);
249 DMA_StartTransfer(handle->rxDmaHandle);
250 isEnableRxDMA = true;
251 width = 1U;
252 break;
253
254 default:
255 /* This should never happen */
256 assert(0);
257 break;
258 }
259
260 I3C_MasterEnableDMA(base, isEnableTxDMA, isEnableRxDMA, width);
261 }
262
I3C_MasterRunTransferStateMachineDMA(I3C_Type * base,i3c_master_dma_handle_t * handle,bool * isDone)263 static status_t I3C_MasterRunTransferStateMachineDMA(I3C_Type *base, i3c_master_dma_handle_t *handle, bool *isDone)
264 {
265 status_t result = kStatus_Success;
266 bool state_complete = false;
267 size_t rxCount = 0;
268 i3c_master_transfer_t *xfer;
269 uint32_t status;
270 uint32_t errStatus;
271
272 /* Set default isDone return value. */
273 *isDone = false;
274
275 /* Check for errors. */
276 status = (uint32_t)I3C_MasterGetPendingInterrupts(base);
277 I3C_MasterClearStatusFlags(base, status);
278
279 i3c_master_state_t masterState = I3C_MasterGetState(base);
280 errStatus = I3C_MasterGetErrorStatusFlags(base);
281 result = I3C_MasterCheckAndClearError(base, errStatus);
282 if (kStatus_Success != result)
283 {
284 return result;
285 }
286
287 if (0UL != (status & (uint32_t)kI3C_MasterSlave2MasterFlag))
288 {
289 if (handle->callback.slave2Master != NULL)
290 {
291 handle->callback.slave2Master(base, handle->userData);
292 }
293 }
294
295 if ((0UL != (status & (uint32_t)kI3C_MasterSlaveStartFlag)) && (handle->transfer.busType != kI3C_TypeI2C))
296 {
297 handle->state = (uint8_t)kSlaveStartState;
298 }
299
300 if ((masterState == kI3C_MasterStateIbiRcv) || (masterState == kI3C_MasterStateIbiAck))
301 {
302 handle->state = (uint8_t)kIBIWonState;
303 }
304
305 if (handle->state == (uint8_t)kIdleState)
306 {
307 return result;
308 }
309
310 if (handle->state == (uint8_t)kIBIWonState)
311 {
312 /* Get Rx fifo counts. */
313 rxCount = (base->MDATACTRL & I3C_MDATACTRL_RXCOUNT_MASK) >> I3C_MDATACTRL_RXCOUNT_SHIFT;
314 }
315
316 /* Get pointer to private data. */
317 xfer = &handle->transfer;
318
319 while (!state_complete)
320 {
321 /* Execute the state. */
322 switch (handle->state)
323 {
324 case (uint8_t)kSlaveStartState:
325 /* Emit start + 0x7E */
326 I3C_MasterEmitRequest(base, kI3C_RequestAutoIbi);
327 handle->state = (uint8_t)kIBIWonState;
328 state_complete = true;
329 break;
330
331 case (uint8_t)kIBIWonState:
332 if (masterState == kI3C_MasterStateIbiAck)
333 {
334 handle->ibiType = I3C_GetIBIType(base);
335 if (handle->callback.ibiCallback != NULL)
336 {
337 handle->callback.ibiCallback(base, handle, handle->ibiType, kI3C_IbiAckNackPending);
338 }
339 else
340 {
341 I3C_MasterEmitIBIResponse(base, kI3C_IbiRespNack);
342 }
343 }
344
345 /* Make sure there is data in the rx fifo. */
346 if (0UL != rxCount)
347 {
348 if ((handle->ibiBuff == NULL) && (handle->callback.ibiCallback != NULL))
349 {
350 handle->callback.ibiCallback(base, handle, kI3C_IbiNormal, kI3C_IbiDataBuffNeed);
351 }
352 uint8_t tempData = (uint8_t)base->MRDATAB;
353 if (handle->ibiBuff != NULL)
354 {
355 handle->ibiBuff[handle->ibiPayloadSize++] = tempData;
356 }
357 rxCount--;
358 break;
359 }
360 else if (0UL != (status & (uint32_t)kI3C_MasterCompleteFlag))
361 {
362 handle->ibiType = I3C_GetIBIType(base);
363 handle->ibiAddress = I3C_GetIBIAddress(base);
364 state_complete = true;
365 result = kStatus_I3C_IBIWon;
366 }
367 else
368 {
369 state_complete = true;
370 }
371 break;
372
373 case (uint8_t)kSendCommandState:
374 /* Calculate command count and put into command buffer. */
375 if (xfer->dataSize == 0U)
376 {
377 *isDone = true;
378 }
379
380 I3C_MasterRunDMATransfer(base, handle, handle->subaddressBuffer, handle->subaddressCount, kI3C_Write);
381
382 if ((xfer->direction == kI3C_Read) || (0UL == xfer->dataSize))
383 {
384 if (0UL == xfer->dataSize)
385 {
386 handle->state = (uint8_t)kWaitForCompletionState;
387 }
388 else
389 {
390 /* xfer->dataSize != 0U, xfer->direction = kI3C_Read */
391 handle->state = (uint8_t)kWaitRepeatedStartCompleteState;
392 }
393 }
394 else
395 {
396 /* Next state, transfer data. */
397 handle->state = (uint8_t)kTransmitDataState;
398 }
399
400 state_complete = true;
401 break;
402
403 case (uint8_t)kWaitRepeatedStartCompleteState:
404 /* We stay in this state until the maste complete. */
405 if (0UL != (status & (uint32_t)kI3C_MasterCompleteFlag))
406 {
407 handle->state = (uint8_t)kReceiveDataState;
408 /* Send repeated start and slave address. */
409 result = I3C_MasterRepeatedStart(base, xfer->busType, xfer->slaveAddress, kI3C_Read);
410 }
411
412 state_complete = true;
413 break;
414
415 case (uint8_t)kTransmitDataState:
416 I3C_MasterRunDMATransfer(base, handle, xfer->data, xfer->dataSize, kI3C_Write);
417 handle->state = (uint8_t)kWaitForCompletionState;
418
419 state_complete = true;
420 break;
421
422 case (uint8_t)kReceiveDataState:
423 /* Do DMA read. */
424 handle->state = (uint8_t)kWaitForCompletionState;
425
426 state_complete = true;
427 break;
428
429 case (uint8_t)kWaitForCompletionState:
430 /* We stay in this state until the maste complete. */
431 if (0UL != (status & (uint32_t)kI3C_MasterCompleteFlag))
432 {
433 handle->state = (uint8_t)kStopState;
434 }
435 else
436 {
437 state_complete = true;
438 }
439 break;
440
441 case (uint8_t)kStopState:
442 /* Only issue a stop transition if the caller requested it. */
443 if (0UL == (xfer->flags & (uint32_t)kI3C_TransferNoStopFlag))
444 {
445 if (xfer->busType == kI3C_TypeI3CDdr)
446 {
447 I3C_MasterEmitRequest(base, kI3C_RequestForceExit);
448 }
449 else
450 {
451 I3C_MasterEmitRequest(base, kI3C_RequestEmitStop);
452 }
453 }
454 *isDone = true;
455 state_complete = true;
456 break;
457
458 default:
459 assert(false);
460 break;
461 }
462 }
463 return result;
464 }
465
I3C_MasterTransferCreateHandleDMA(I3C_Type * base,i3c_master_dma_handle_t * handle,const i3c_master_dma_callback_t * callback,void * userData,dma_handle_t * rxDmaHandle,dma_handle_t * txDmaHandle)466 void I3C_MasterTransferCreateHandleDMA(I3C_Type *base,
467 i3c_master_dma_handle_t *handle,
468 const i3c_master_dma_callback_t *callback,
469 void *userData,
470 dma_handle_t *rxDmaHandle,
471 dma_handle_t *txDmaHandle)
472 {
473 uint32_t instance;
474
475 assert(NULL != handle);
476
477 /* Clear out the handle. */
478 (void)memset(handle, 0, sizeof(*handle));
479
480 /* Look up instance number */
481 instance = I3C_GetInstance(base);
482
483 handle->base = base;
484 handle->txDmaHandle = txDmaHandle;
485 handle->rxDmaHandle = rxDmaHandle;
486 handle->callback = *callback;
487 handle->userData = userData;
488
489 /* Save this handle for IRQ use. */
490 s_i3cMasterHandle[instance] = handle;
491
492 /* Set irq handler. */
493 s_i3cMasterIsr = I3C_MasterTransferDMAHandleIRQ;
494
495 DMA_SetCallback(handle->rxDmaHandle, I3C_MasterTransferDMACallbackRx, handle);
496 DMA_SetCallback(handle->txDmaHandle, I3C_MasterTransferDMACallbackTx, handle);
497
498 /* Clear all flags. */
499 I3C_MasterClearErrorStatusFlags(base, (uint32_t)kMasterErrorFlags);
500 I3C_MasterClearStatusFlags(base, (uint32_t)kMasterClearFlags);
501 /* Reset fifos. These flags clear automatically. */
502 base->MDATACTRL |= I3C_MDATACTRL_FLUSHTB_MASK | I3C_MDATACTRL_FLUSHFB_MASK;
503
504 /* Enable NVIC IRQ, this only enables the IRQ directly connected to the NVIC.
505 In some cases the I3C IRQ is configured through INTMUX, user needs to enable
506 INTMUX IRQ in application code. */
507 (void)EnableIRQ(kI3cIrqs[instance]);
508
509 /* Clear internal IRQ enables and enable NVIC IRQ. */
510 I3C_MasterEnableInterrupts(base, (uint32_t)kMasterDMAIrqFlags);
511 }
512
513 /*!
514 * brief Performs a non-blocking DMA transaction on the I2C/I3C bus.
515 *
516 * param base The I3C peripheral base address.
517 * param handle Pointer to the I3C master driver handle.
518 * param transfer The pointer to the transfer descriptor.
519 * retval #kStatus_Success The transaction was started successfully.
520 * retval #kStatus_I3C_Busy Either another master is currently utilizing the bus, or a non-blocking
521 * transaction is already in progress.
522 */
I3C_MasterTransferDMA(I3C_Type * base,i3c_master_dma_handle_t * handle,i3c_master_transfer_t * transfer)523 status_t I3C_MasterTransferDMA(I3C_Type *base, i3c_master_dma_handle_t *handle, i3c_master_transfer_t *transfer)
524 {
525 assert(NULL != handle);
526 assert(NULL != transfer);
527 assert(transfer->subaddressSize <= sizeof(transfer->subaddress));
528 i3c_master_state_t masterState = I3C_MasterGetState(base);
529 bool checkDdrState = false;
530
531 /* Return busy if another transaction is in progress. */
532 if (handle->state != (uint8_t)kIdleState)
533 {
534 return kStatus_I3C_Busy;
535 }
536
537 /* Return an error if the bus is already in use not by us. */
538 checkDdrState = (transfer->busType == kI3C_TypeI3CDdr) ? (masterState != kI3C_MasterStateDdr) : true;
539 if ((masterState != kI3C_MasterStateIdle) && (masterState != kI3C_MasterStateNormAct) && checkDdrState)
540 {
541 return kStatus_I3C_Busy;
542 }
543
544 /* Disable I3C IRQ sources while we configure stuff. */
545 I3C_MasterDisableInterrupts(
546 base, ((uint32_t)kMasterDMAIrqFlags | (uint32_t)kI3C_MasterRxReadyFlag | (uint32_t)kI3C_MasterTxReadyFlag));
547
548 /* Save transfer into handle. */
549 handle->transfer = *transfer;
550
551 /* Configure IBI response type. */
552 base->MCTRL &= ~I3C_MCTRL_IBIRESP_MASK;
553 base->MCTRL |= I3C_MCTRL_IBIRESP(transfer->ibiResponse);
554
555 /* Clear all flags. */
556 I3C_MasterClearErrorStatusFlags(base, (uint32_t)kMasterErrorFlags);
557 I3C_MasterClearStatusFlags(base, (uint32_t)kMasterClearFlags);
558 /* Reset fifos. These flags clear automatically. */
559 base->MDATACTRL |= I3C_MDATACTRL_FLUSHTB_MASK | I3C_MDATACTRL_FLUSHFB_MASK;
560
561 /* Generate commands to send. */
562 (void)I3C_MasterInitTransferStateMachineDMA(base, handle);
563
564 /* Enable I3C internal IRQ sources. NVIC IRQ was enabled in CreateHandle() */
565 I3C_MasterEnableInterrupts(base, (uint32_t)(kMasterDMAIrqFlags));
566
567 if (transfer->busType == kI3C_TypeI2C)
568 {
569 I3C_MasterDisableInterrupts(base, (uint32_t)kI3C_MasterSlaveStartFlag);
570 }
571
572 return kStatus_Success;
573 }
574
I3C_MasterTransferDMAHandleIRQ(I3C_Type * base,void * i3cHandle)575 void I3C_MasterTransferDMAHandleIRQ(I3C_Type *base, void *i3cHandle)
576 {
577 i3c_master_dma_handle_t *handle = (i3c_master_dma_handle_t *)i3cHandle;
578
579 bool isDone;
580 status_t result;
581
582 /* Don't do anything if we don't have a valid handle. */
583 if (NULL == handle)
584 {
585 return;
586 }
587
588 result = I3C_MasterRunTransferStateMachineDMA(base, handle, &isDone);
589
590 if (handle->state == (uint8_t)kIdleState)
591 {
592 return;
593 }
594
595 if (isDone || (result != kStatus_Success))
596 {
597 /* XXX need to handle data that may be in rx fifo below watermark level? */
598
599 /* XXX handle error, terminate xfer */
600 if ((result == kStatus_I3C_Nak) || (result == kStatus_I3C_IBIWon))
601 {
602 I3C_MasterEmitRequest(base, kI3C_RequestEmitStop);
603 }
604
605 /* Set handle to idle state. */
606 handle->state = (uint8_t)kIdleState;
607
608 /* Invoke IBI user callback. */
609 if ((result == kStatus_I3C_IBIWon) && (handle->callback.ibiCallback != NULL))
610 {
611 handle->callback.ibiCallback(base, handle, handle->ibiType, kI3C_IbiReady);
612 handle->ibiPayloadSize = 0;
613 }
614
615 /* Invoke callback. */
616 if (NULL != handle->callback.transferComplete)
617 {
618 handle->callback.transferComplete(base, handle, result, handle->userData);
619 }
620 }
621 }
622
623 /*!
624 * brief Get master transfer status during a dma non-blocking transfer
625 *
626 * param base I3C peripheral base address
627 * param handle pointer to i2c_master_dma_handle_t structure
628 * param count Number of bytes transferred so far by the non-blocking transaction.
629 */
I3C_MasterTransferGetCountDMA(I3C_Type * base,i3c_master_dma_handle_t * handle,size_t * count)630 status_t I3C_MasterTransferGetCountDMA(I3C_Type *base, i3c_master_dma_handle_t *handle, size_t *count)
631 {
632 assert(handle != NULL);
633
634 if (NULL == count)
635 {
636 return kStatus_InvalidArgument;
637 }
638
639 /* Catch when there is not an active transfer. */
640 if (handle->state == (uint8_t)kIdleState)
641 {
642 *count = 0;
643 return kStatus_NoTransferInProgress;
644 }
645
646 /* There is no necessity to disable interrupts as we read a single integer value */
647 i3c_direction_t dir = handle->transfer.direction;
648
649 if (dir == kI3C_Read)
650 {
651 *count = handle->transferCount - DMA_GetRemainingBytes(handle->rxDmaHandle->base, handle->rxDmaHandle->channel);
652 }
653 else
654 {
655 *count = handle->transferCount - DMA_GetRemainingBytes(handle->txDmaHandle->base, handle->txDmaHandle->channel);
656 }
657
658 return kStatus_Success;
659 }
660
661 /*!
662 * brief Abort a master dma non-blocking transfer in a early time
663 *
664 * param base I3C peripheral base address
665 * param handle pointer to i2c_master_dma_handle_t structure
666 */
I3C_MasterTransferAbortDMA(I3C_Type * base,i3c_master_dma_handle_t * handle)667 void I3C_MasterTransferAbortDMA(I3C_Type *base, i3c_master_dma_handle_t *handle)
668 {
669 if (handle->state != (uint8_t)kIdleState)
670 {
671 DMA_AbortTransfer(handle->txDmaHandle);
672 DMA_AbortTransfer(handle->rxDmaHandle);
673
674 I3C_MasterEnableDMA(base, false, false, 0);
675
676 /* Reset fifos. These flags clear automatically. */
677 base->MDATACTRL |= I3C_MDATACTRL_FLUSHTB_MASK | I3C_MDATACTRL_FLUSHFB_MASK;
678
679 /* Send a stop command to finalize the transfer. */
680 (void)I3C_MasterStop(base);
681
682 /* Reset handle. */
683 handle->state = (uint8_t)kIdleState;
684 }
685 }
686
I3C_SlaveTransferDMACallback(dma_handle_t * dmaHandle,void * param,bool transferDone,uint32_t tcds)687 static void I3C_SlaveTransferDMACallback(dma_handle_t *dmaHandle, void *param, bool transferDone, uint32_t tcds)
688 {
689 i3c_slave_dma_handle_t *i3cHandle = (i3c_slave_dma_handle_t *)param;
690
691 if (transferDone)
692 {
693 /* Simply diable dma enablement */
694 if (i3cHandle->txDmaHandle == dmaHandle)
695 {
696 i3cHandle->base->SDMACTRL &= ~I3C_SDMACTRL_DMATB_MASK;
697 }
698 else
699 {
700 i3cHandle->base->SDMACTRL &= ~I3C_SDMACTRL_DMAFB_MASK;
701 }
702 }
703 }
704
705 /*!
706 * brief Create a new handle for the I3C slave DMA APIs.
707 *
708 * The creation of a handle is for use with the DMA APIs. Once a handle
709 * is created, there is not a corresponding destroy handle. If the user wants to
710 * terminate a transfer, the I3C_SlaveTransferAbortDMA() API shall be called.
711 *
712 * For devices where the I3C send and receive DMA requests are OR'd together, the @a txDmaHandle
713 * parameter is ignored and may be set to NULL.
714 *
715 * param base The I3C peripheral base address.
716 * param handle Pointer to the I3C slave driver handle.
717 * param callback User provided pointer to the asynchronous callback function.
718 * param userData User provided pointer to the application callback data.
719 * param rxDmaHandle Handle for the DMA receive channel. Created by the user prior to calling this function.
720 * param txDmaHandle Handle for the DMA transmit channel. Created by the user prior to calling this function.
721 */
I3C_SlaveTransferCreateHandleDMA(I3C_Type * base,i3c_slave_dma_handle_t * handle,i3c_slave_dma_callback_t callback,void * userData,dma_handle_t * rxDmaHandle,dma_handle_t * txDmaHandle)722 void I3C_SlaveTransferCreateHandleDMA(I3C_Type *base,
723 i3c_slave_dma_handle_t *handle,
724 i3c_slave_dma_callback_t callback,
725 void *userData,
726 dma_handle_t *rxDmaHandle,
727 dma_handle_t *txDmaHandle)
728 {
729 uint32_t instance;
730
731 assert(NULL != handle);
732
733 /* Clear out the handle. */
734 (void)memset(handle, 0, sizeof(*handle));
735
736 /* Look up instance number */
737 instance = I3C_GetInstance(base);
738
739 handle->base = base;
740 handle->txDmaHandle = txDmaHandle;
741 handle->rxDmaHandle = rxDmaHandle;
742 handle->callback = callback;
743 handle->userData = userData;
744
745 /* Save this handle for IRQ use. */
746 s_i3cSlaveHandle[instance] = handle;
747
748 /* Set irq handler. */
749 s_i3cSlaveIsr = I3C_SlaveTransferDMAHandleIRQ;
750
751 DMA_SetCallback(handle->rxDmaHandle, I3C_SlaveTransferDMACallback, handle);
752 DMA_SetCallback(handle->txDmaHandle, I3C_SlaveTransferDMACallback, handle);
753
754 /* Clear internal IRQ enables and enable NVIC IRQ. */
755 I3C_SlaveDisableInterrupts(base, (uint32_t)kSlaveDMAIrqFlags);
756
757 /* Enable NVIC IRQ, this only enables the IRQ directly connected to the NVIC.
758 In some cases the I3C IRQ is configured through INTMUX, user needs to enable
759 INTMUX IRQ in application code. */
760 (void)EnableIRQ(kI3cIrqs[instance]);
761
762 /* Enable IRQ. */
763 I3C_SlaveEnableInterrupts(base, (uint32_t)kSlaveDMAIrqFlags);
764 }
765
I3C_SlavePrepareTxDMA(I3C_Type * base,i3c_slave_dma_handle_t * handle)766 static void I3C_SlavePrepareTxDMA(I3C_Type *base, i3c_slave_dma_handle_t *handle)
767 {
768 i3c_slave_dma_transfer_t *xfer = &handle->transfer;
769 dma_channel_config_t txChannelConfig;
770 uint32_t *txFifoBase;
771
772 if (xfer->txDataSize == 1U)
773 {
774 txFifoBase = (uint32_t *)(uint32_t)&base->SWDATABE;
775 DMA_PrepareChannelTransfer(&txChannelConfig, xfer->txData, (void *)txFifoBase,
776 DMA_CHANNEL_XFER(false, false, true, false, 1u, kDMA_AddressInterleave1xWidth,
777 kDMA_AddressInterleave0xWidth, xfer->txDataSize),
778 kDMA_MemoryToPeripheral, NULL, NULL);
779 }
780 else
781 {
782 uint32_t instance = I3C_GetInstance(base);
783
784 txFifoBase = (uint32_t *)(uint32_t)&base->SWDATAB1;
785 DMA_PrepareChannelTransfer(&txChannelConfig, xfer->txData, (void *)txFifoBase,
786 DMA_CHANNEL_XFER(true, false, false, false, 1u, kDMA_AddressInterleave1xWidth,
787 kDMA_AddressInterleave0xWidth, xfer->txDataSize - 1U),
788 kDMA_MemoryToPeripheral, NULL, &(s_dma_table[instance]));
789
790 txFifoBase = (uint32_t *)(uint32_t)&base->SWDATABE;
791 DMA_SetupDescriptor(&(s_dma_table[instance]),
792 DMA_CHANNEL_XFER(false, false, true, false, 1u, kDMA_AddressInterleave1xWidth,
793 kDMA_AddressInterleave0xWidth, 1U),
794 xfer->txData + xfer->txDataSize - 1U, txFifoBase, NULL);
795 }
796 (void)DMA_SubmitChannelTransfer(handle->txDmaHandle, &txChannelConfig);
797
798 DMA_StartTransfer(handle->txDmaHandle);
799 }
800
I3C_SlavePrepareRxDMA(I3C_Type * base,i3c_slave_dma_handle_t * handle)801 static void I3C_SlavePrepareRxDMA(I3C_Type *base, i3c_slave_dma_handle_t *handle)
802 {
803 dma_channel_config_t rxChannelConfig;
804 uint32_t *rxFifoBase = (uint32_t *)(uint32_t)&base->SRDATAB;
805 i3c_slave_dma_transfer_t *xfer = &handle->transfer;
806
807 DMA_PrepareChannelTransfer(&rxChannelConfig, (void *)rxFifoBase, xfer->rxData,
808 DMA_CHANNEL_XFER(false, true, true, false, 1u, kDMA_AddressInterleave0xWidth,
809 kDMA_AddressInterleave1xWidth, xfer->rxDataSize),
810 kDMA_PeripheralToMemory, NULL, NULL);
811
812 (void)DMA_SubmitChannelTransfer(handle->rxDmaHandle, &rxChannelConfig);
813
814 DMA_StartTransfer(handle->rxDmaHandle);
815 }
816
817 /*!
818 * brief Prepares for a non-blocking DMA-based transaction on the I3C bus.
819 *
820 * The API will do DMA configuration according to the input transfer descriptor, and the data will be transferred when
821 * there's bus master requesting transfer from/to this slave. So the timing of call to this API need be aligned
822 * with master application to ensure the transfer is executed as expected.
823 * Callback specified when the @a handle was created is invoked when the transaction has completed.
824 *
825 * param base The I3C peripheral base address.
826 * param handle Pointer to the I3C slave driver handle.
827 * param transfer The pointer to the transfer descriptor.
828 * param eventMask Bit mask formed by OR'ing together #i3c_slave_transfer_event_t enumerators to specify
829 * which events to send to the callback. The transmit and receive events is not allowed to be enabled.
830 * retval kStatus_Success The transaction was started successfully.
831 * retval #kStatus_I3C_Busy Either another master is currently utilizing the bus, or another DMA
832 * transaction is already in progress.
833 */
I3C_SlaveTransferDMA(I3C_Type * base,i3c_slave_dma_handle_t * handle,i3c_slave_dma_transfer_t * transfer,uint32_t eventMask)834 status_t I3C_SlaveTransferDMA(I3C_Type *base,
835 i3c_slave_dma_handle_t *handle,
836 i3c_slave_dma_transfer_t *transfer,
837 uint32_t eventMask)
838 {
839 assert(NULL != handle);
840 assert(NULL != transfer);
841
842 bool txDmaEn = false, rxDmaEn = false;
843 uint32_t width;
844
845 if (handle->isBusy == true)
846 {
847 return kStatus_I3C_Busy;
848 }
849 /* Clear all flags. */
850 I3C_SlaveClearErrorStatusFlags(base, (uint32_t)kSlaveErrorFlags);
851 I3C_SlaveClearStatusFlags(base, (uint32_t)kSlaveClearFlags);
852 /* Reset fifos. These flags clear automatically. */
853 base->SDATACTRL |= I3C_SDATACTRL_FLUSHTB_MASK | I3C_SDATACTRL_FLUSHFB_MASK;
854
855 handle->transfer = *transfer;
856
857 /* Set up event mask. */
858 handle->eventMask = eventMask;
859
860 if ((transfer->txData != NULL) && (transfer->txDataSize != 0U))
861 {
862 I3C_SlavePrepareTxDMA(base, handle);
863 txDmaEn = true;
864 width = 2U;
865 }
866
867 if ((transfer->rxData != NULL) && (transfer->rxDataSize != 0U))
868 {
869 I3C_SlavePrepareRxDMA(base, handle);
870 rxDmaEn = true;
871 width = 1U;
872 }
873
874 if (txDmaEn || rxDmaEn)
875 {
876 I3C_SlaveEnableDMA(base, txDmaEn, rxDmaEn, width);
877 return kStatus_Success;
878 }
879 else
880 {
881 return kStatus_Fail;
882 }
883 }
884
I3C_SlaveTransferDMAHandleIRQ(I3C_Type * base,void * i3cHandle)885 void I3C_SlaveTransferDMAHandleIRQ(I3C_Type *base, void *i3cHandle)
886 {
887 uint32_t flags;
888 uint32_t errFlags;
889 i3c_slave_dma_transfer_t *xfer;
890
891 i3c_slave_dma_handle_t *handle = (i3c_slave_dma_handle_t *)i3cHandle;
892 /* Check for a valid handle in case of a spurious interrupt. */
893 if (NULL == handle)
894 {
895 return;
896 }
897
898 xfer = &handle->transfer;
899
900 /* Get status flags. */
901 flags = I3C_SlaveGetStatusFlags(base);
902 errFlags = I3C_SlaveGetErrorStatusFlags(base);
903
904 /* Clear status flags. */
905 I3C_SlaveClearStatusFlags(base, flags);
906
907 if (0UL != (errFlags & (uint32_t)kSlaveErrorFlags))
908 {
909 xfer->event = (uint32_t)kI3C_SlaveCompletionEvent;
910 xfer->completionStatus = I3C_SlaveCheckAndClearError(base, errFlags);
911
912 if ((0UL != (handle->eventMask & (uint32_t)kI3C_SlaveCompletionEvent)) && (NULL != handle->callback))
913 {
914 handle->callback(base, xfer, handle->userData);
915 }
916 return;
917 }
918
919 if (0UL != (flags & (uint32_t)kI3C_SlaveEventSentFlag))
920 {
921 xfer->event = (uint32_t)kI3C_SlaveRequestSentEvent;
922 if ((0UL != (handle->eventMask & xfer->event)) && (NULL != handle->callback))
923 {
924 handle->callback(base, xfer, handle->userData);
925 }
926 }
927
928 if (0UL != (flags & (uint32_t)kI3C_SlaveReceivedCCCFlag))
929 {
930 handle->isBusy = true;
931 xfer->event = (uint32_t)kI3C_SlaveReceivedCCCEvent;
932 if ((0UL != (handle->eventMask & xfer->event)) && (NULL != handle->callback))
933 {
934 handle->callback(base, xfer, handle->userData);
935 }
936 }
937
938 if (0UL != (flags & (uint32_t)kI3C_SlaveBusStopFlag))
939 {
940 if (handle->isBusy == true)
941 {
942 xfer->event = (uint32_t)kI3C_SlaveCompletionEvent;
943 xfer->completionStatus = kStatus_Success;
944 handle->isBusy = false;
945
946 if ((0UL != (handle->eventMask & xfer->event)) && (NULL != handle->callback))
947 {
948 handle->callback(base, xfer, handle->userData);
949 }
950 I3C_SlaveTransferAbortDMA(base, handle);
951 }
952 else
953 {
954 return;
955 }
956 }
957
958 if (0UL != (flags & (uint32_t)kI3C_SlaveMatchedFlag))
959 {
960 xfer->event = (uint32_t)kI3C_SlaveAddressMatchEvent;
961 handle->isBusy = true;
962 if ((0UL != (handle->eventMask & (uint32_t)kI3C_SlaveAddressMatchEvent)) && (NULL != handle->callback))
963 {
964 handle->callback(base, xfer, handle->userData);
965 }
966 }
967 }
968
969 /*!
970 * brief Abort a slave dma non-blocking transfer in a early time
971 *
972 * param base I3C peripheral base address
973 * param handle pointer to i3c_slave_dma_handle_t structure
974 */
I3C_SlaveTransferAbortDMA(I3C_Type * base,i3c_slave_dma_handle_t * handle)975 void I3C_SlaveTransferAbortDMA(I3C_Type *base, i3c_slave_dma_handle_t *handle)
976 {
977 if (handle->isBusy != false)
978 {
979 DMA_AbortTransfer(handle->txDmaHandle);
980 DMA_AbortTransfer(handle->rxDmaHandle);
981
982 I3C_SlaveEnableDMA(base, false, false, 0);
983
984 /* Reset fifos. These flags clear automatically. */
985 base->SDATACTRL |= I3C_SDATACTRL_FLUSHTB_MASK | I3C_SDATACTRL_FLUSHFB_MASK;
986 }
987 }