1 /*
2 * Copyright (c) 2015, Freescale Semiconductor, Inc.
3 * Copyright 2016-2017 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 /* @brief Mask to align an address to 32 bytes. */
18 #define ALIGN_32_MASK (0x1fU)
19
20 /*! @brief Common sets of flags used by the driver. */
21 enum _lpi2c_flag_constants
22 {
23 /*! All flags which are cleared by the driver upon starting a transfer. */
24 kMasterClearFlags = kLPI2C_MasterEndOfPacketFlag | kLPI2C_MasterStopDetectFlag | kLPI2C_MasterNackDetectFlag |
25 kLPI2C_MasterArbitrationLostFlag | kLPI2C_MasterFifoErrFlag | kLPI2C_MasterPinLowTimeoutFlag |
26 kLPI2C_MasterDataMatchFlag,
27
28 /*! IRQ sources enabled by the non-blocking transactional API. */
29 kMasterIrqFlags = kLPI2C_MasterArbitrationLostFlag | kLPI2C_MasterTxReadyFlag | kLPI2C_MasterRxReadyFlag |
30 kLPI2C_MasterStopDetectFlag | kLPI2C_MasterNackDetectFlag | kLPI2C_MasterPinLowTimeoutFlag |
31 kLPI2C_MasterFifoErrFlag,
32
33 /*! Errors to check for. */
34 kMasterErrorFlags = kLPI2C_MasterNackDetectFlag | kLPI2C_MasterArbitrationLostFlag | kLPI2C_MasterFifoErrFlag |
35 kLPI2C_MasterPinLowTimeoutFlag,
36
37 /*! All flags which are cleared by the driver upon starting a transfer. */
38 kSlaveClearFlags = kLPI2C_SlaveRepeatedStartDetectFlag | kLPI2C_SlaveStopDetectFlag | kLPI2C_SlaveBitErrFlag |
39 kLPI2C_SlaveFifoErrFlag,
40
41 /*! IRQ sources enabled by the non-blocking transactional API. */
42 kSlaveIrqFlags = kLPI2C_SlaveTxReadyFlag | kLPI2C_SlaveRxReadyFlag | kLPI2C_SlaveStopDetectFlag |
43 kLPI2C_SlaveRepeatedStartDetectFlag | kLPI2C_SlaveFifoErrFlag | kLPI2C_SlaveBitErrFlag |
44 kLPI2C_SlaveTransmitAckFlag | kLPI2C_SlaveAddressValidFlag,
45
46 /*! Errors to check for. */
47 kSlaveErrorFlags = kLPI2C_SlaveFifoErrFlag | kLPI2C_SlaveBitErrFlag,
48 };
49
50 /* ! @brief LPI2C master fifo commands. */
51 enum _lpi2c_master_fifo_cmd
52 {
53 kTxDataCmd = LPI2C_MTDR_CMD(0x0U), /*!< Transmit DATA[7:0] */
54 kRxDataCmd = LPI2C_MTDR_CMD(0X1U), /*!< Receive (DATA[7:0] + 1) bytes */
55 kStopCmd = LPI2C_MTDR_CMD(0x2U), /*!< Generate STOP condition */
56 kStartCmd = LPI2C_MTDR_CMD(0x4U), /*!< Generate(repeated) START and transmit address in DATA[[7:0] */
57 };
58
59 /*! @brief States for the state machine used by transactional APIs. */
60 enum _lpi2c_transfer_states
61 {
62 kIdleState = 0,
63 kSendCommandState,
64 kIssueReadCommandState,
65 kTransferDataState,
66 kStopState,
67 kWaitForCompletionState,
68 };
69
70 /*! @brief Typedef for interrupt handler. */
71 typedef void (*lpi2c_isr_t)(LPI2C_Type *base, void *handle);
72
73 /*******************************************************************************
74 * Prototypes
75 ******************************************************************************/
76
77 /* Defined in fsl_lpi2c.c. */
78 extern status_t LPI2C_CheckForBusyBus(LPI2C_Type *base);
79
80 /* Defined in fsl_lpi2c.c. */
81 extern status_t LPI2C_MasterCheckAndClearError(LPI2C_Type *base, uint32_t status);
82
83 static uint32_t LPI2C_GenerateCommands(lpi2c_master_edma_handle_t *handle);
84
85 static void LPI2C_MasterEDMACallback(edma_handle_t *dmaHandle, void *userData, bool isTransferDone, uint32_t tcds);
86
87 /*******************************************************************************
88 * Code
89 ******************************************************************************/
90
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)91 void LPI2C_MasterCreateEDMAHandle(LPI2C_Type *base,
92 lpi2c_master_edma_handle_t *handle,
93 edma_handle_t *rxDmaHandle,
94 edma_handle_t *txDmaHandle,
95 lpi2c_master_edma_transfer_callback_t callback,
96 void *userData)
97 {
98 assert(handle);
99 assert(rxDmaHandle);
100 assert(txDmaHandle);
101
102 /* Clear out the handle. */
103 memset(handle, 0, sizeof(*handle));
104
105 /* Set up the handle. For combined rx/tx DMA requests, the tx channel handle is set to the rx handle */
106 /* in order to make the transfer API code simpler. */
107 handle->base = base;
108 handle->completionCallback = callback;
109 handle->userData = userData;
110 handle->rx = rxDmaHandle;
111 handle->tx = FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) ? txDmaHandle : rxDmaHandle;
112
113 /* Set DMA channel completion callbacks. */
114 EDMA_SetCallback(handle->rx, LPI2C_MasterEDMACallback, handle);
115 if (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base))
116 {
117 EDMA_SetCallback(handle->tx, LPI2C_MasterEDMACallback, handle);
118 }
119 }
120
121 /*!
122 * @brief Prepares the command buffer with the sequence of commands needed to send the requested transaction.
123 * @param handle Master DMA driver handle.
124 * @return Number of command words.
125 */
LPI2C_GenerateCommands(lpi2c_master_edma_handle_t * handle)126 static uint32_t LPI2C_GenerateCommands(lpi2c_master_edma_handle_t *handle)
127 {
128 lpi2c_master_transfer_t *xfer = &handle->transfer;
129 uint16_t *cmd = (uint16_t *)&handle->commandBuffer;
130 uint32_t cmdCount = 0;
131
132 /* Handle no start option. */
133 if (xfer->flags & kLPI2C_TransferNoStartFlag)
134 {
135 if (xfer->direction == kLPI2C_Read)
136 {
137 /* Need to issue read command first. */
138 cmd[cmdCount++] = kRxDataCmd | LPI2C_MTDR_DATA(xfer->dataSize - 1);
139 }
140 }
141 else
142 {
143 /*
144 * Initial direction depends on whether a subaddress was provided, and of course the actual
145 * data transfer direction.
146 */
147 lpi2c_direction_t direction = xfer->subaddressSize ? kLPI2C_Write : xfer->direction;
148
149 /* Start command. */
150 cmd[cmdCount++] =
151 (uint16_t)kStartCmd | (uint16_t)((uint16_t)((uint16_t)xfer->slaveAddress << 1U) | (uint16_t)direction);
152
153 /* Subaddress, MSB first. */
154 if (xfer->subaddressSize)
155 {
156 uint32_t subaddressRemaining = xfer->subaddressSize;
157 while (subaddressRemaining--)
158 {
159 uint8_t subaddressByte = (xfer->subaddress >> (8 * subaddressRemaining)) & 0xff;
160 cmd[cmdCount++] = subaddressByte;
161 }
162 }
163
164 /* Reads need special handling because we have to issue a read command and maybe a repeated start. */
165 if ((xfer->dataSize) && (xfer->direction == kLPI2C_Read))
166 {
167 /* Need to send repeated start if switching directions to read. */
168 if (direction == kLPI2C_Write)
169 {
170 cmd[cmdCount++] = (uint16_t)kStartCmd |
171 (uint16_t)((uint16_t)((uint16_t)xfer->slaveAddress << 1U) | (uint16_t)kLPI2C_Read);
172 }
173
174 /* Read command. */
175 cmd[cmdCount++] = kRxDataCmd | LPI2C_MTDR_DATA(xfer->dataSize - 1);
176 }
177 }
178
179 return cmdCount;
180 }
181
LPI2C_MasterTransferEDMA(LPI2C_Type * base,lpi2c_master_edma_handle_t * handle,lpi2c_master_transfer_t * transfer)182 status_t LPI2C_MasterTransferEDMA(LPI2C_Type *base,
183 lpi2c_master_edma_handle_t *handle,
184 lpi2c_master_transfer_t *transfer)
185 {
186 status_t result;
187
188 assert(handle);
189 assert(transfer);
190 assert(transfer->subaddressSize <= sizeof(transfer->subaddress));
191
192 /* Return busy if another transaction is in progress. */
193 if (handle->isBusy)
194 {
195 return kStatus_LPI2C_Busy;
196 }
197
198 /* Return an error if the bus is already in use not by us. */
199 result = LPI2C_CheckForBusyBus(base);
200 if (result)
201 {
202 return result;
203 }
204
205 /* We're now busy. */
206 handle->isBusy = true;
207
208 /* Disable LPI2C IRQ and DMA sources while we configure stuff. */
209 LPI2C_MasterDisableInterrupts(base, kMasterIrqFlags);
210 LPI2C_MasterEnableDMA(base, false, false);
211
212 /* Clear all flags. */
213 LPI2C_MasterClearStatusFlags(base, kMasterClearFlags);
214
215 /* Save transfer into handle. */
216 handle->transfer = *transfer;
217
218 /* Generate commands to send. */
219 uint32_t commandCount = LPI2C_GenerateCommands(handle);
220
221 /* If the user is transmitting no data with no start or stop, then just go ahead and invoke the callback. */
222 if ((!commandCount) && (transfer->dataSize == 0))
223 {
224 if (handle->completionCallback)
225 {
226 handle->completionCallback(base, handle, kStatus_Success, handle->userData);
227 }
228 return kStatus_Success;
229 }
230
231 /* Reset DMA channels. */
232 EDMA_ResetChannel(handle->rx->base, handle->rx->channel);
233 if (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base))
234 {
235 EDMA_ResetChannel(handle->tx->base, handle->tx->channel);
236 }
237
238 /* Get a 32-byte aligned TCD pointer. */
239 edma_tcd_t *tcd = (edma_tcd_t *)((uint32_t)(&handle->tcds[1]) & (~ALIGN_32_MASK));
240
241 bool hasSendData = (transfer->direction == kLPI2C_Write) && (transfer->dataSize);
242 bool hasReceiveData = (transfer->direction == kLPI2C_Read) && (transfer->dataSize);
243
244 edma_transfer_config_t transferConfig;
245 edma_tcd_t *linkTcd = NULL;
246
247 /* Set up data transmit. */
248 if (hasSendData)
249 {
250 transferConfig.srcAddr = (uint32_t)transfer->data;
251 transferConfig.destAddr = (uint32_t)LPI2C_MasterGetTxFifoAddress(base);
252 transferConfig.srcTransferSize = kEDMA_TransferSize1Bytes;
253 transferConfig.destTransferSize = kEDMA_TransferSize1Bytes;
254 transferConfig.srcOffset = sizeof(uint8_t);
255 transferConfig.destOffset = 0;
256 transferConfig.minorLoopBytes = sizeof(uint8_t); /* TODO optimize to fill fifo */
257 transferConfig.majorLoopCounts = transfer->dataSize;
258
259 /* Store the initially configured eDMA minor byte transfer count into the LPI2C handle */
260 handle->nbytes = transferConfig.minorLoopBytes;
261
262 if (commandCount)
263 {
264 /* Create a software TCD, which will be chained after the commands. */
265 EDMA_TcdReset(tcd);
266 EDMA_TcdSetTransferConfig(tcd, &transferConfig, NULL);
267 EDMA_TcdEnableInterrupts(tcd, kEDMA_MajorInterruptEnable);
268 linkTcd = tcd;
269 }
270 else
271 {
272 /* User is only transmitting data with no required commands, so this transfer can stand alone. */
273 EDMA_SetTransferConfig(handle->tx->base, handle->tx->channel, &transferConfig, NULL);
274 EDMA_EnableChannelInterrupts(handle->tx->base, handle->tx->channel, kEDMA_MajorInterruptEnable);
275 }
276 }
277 else if (hasReceiveData)
278 {
279 /* Set up data receive. */
280 transferConfig.srcAddr = (uint32_t)LPI2C_MasterGetRxFifoAddress(base);
281 transferConfig.destAddr = (uint32_t)transfer->data;
282 transferConfig.srcTransferSize = kEDMA_TransferSize1Bytes;
283 transferConfig.destTransferSize = kEDMA_TransferSize1Bytes;
284 transferConfig.srcOffset = 0;
285 transferConfig.destOffset = sizeof(uint8_t);
286 transferConfig.minorLoopBytes = sizeof(uint8_t); /* TODO optimize to empty fifo */
287 transferConfig.majorLoopCounts = transfer->dataSize;
288
289 /* Store the initially configured eDMA minor byte transfer count into the LPI2C handle */
290 handle->nbytes = transferConfig.minorLoopBytes;
291
292 if (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) || (!commandCount))
293 {
294 /* We can put this receive transfer on its own DMA channel. */
295 EDMA_SetTransferConfig(handle->rx->base, handle->rx->channel, &transferConfig, NULL);
296 EDMA_EnableChannelInterrupts(handle->rx->base, handle->rx->channel, kEDMA_MajorInterruptEnable);
297 }
298 else
299 {
300 /* For shared rx/tx DMA requests when there are commands, create a software TCD which will be */
301 /* chained onto the commands transfer. */
302 EDMA_TcdReset(tcd);
303 EDMA_TcdSetTransferConfig(tcd, &transferConfig, NULL);
304 EDMA_TcdEnableInterrupts(tcd, kEDMA_MajorInterruptEnable);
305 linkTcd = tcd;
306 }
307 }
308 else
309 {
310 /* No data to send */
311 }
312
313 /* Set up commands transfer. */
314 if (commandCount)
315 {
316 transferConfig.srcAddr = (uint32_t)handle->commandBuffer;
317 transferConfig.destAddr = (uint32_t)LPI2C_MasterGetTxFifoAddress(base);
318 transferConfig.srcTransferSize = kEDMA_TransferSize2Bytes;
319 transferConfig.destTransferSize = kEDMA_TransferSize2Bytes;
320 transferConfig.srcOffset = sizeof(uint16_t);
321 transferConfig.destOffset = 0;
322 transferConfig.minorLoopBytes = sizeof(uint16_t); /* TODO optimize to fill fifo */
323 transferConfig.majorLoopCounts = commandCount;
324
325 EDMA_SetTransferConfig(handle->tx->base, handle->tx->channel, &transferConfig, linkTcd);
326 }
327
328 /* Start DMA transfer. */
329 if (hasReceiveData || !FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base))
330 {
331 EDMA_StartTransfer(handle->rx);
332 }
333 if ((hasSendData || commandCount) && FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base))
334 {
335 EDMA_StartTransfer(handle->tx);
336 }
337
338 /* Enable DMA in both directions. This actually kicks of the transfer. */
339 LPI2C_MasterEnableDMA(base, true, true);
340
341 return result;
342 }
343
LPI2C_MasterTransferGetCountEDMA(LPI2C_Type * base,lpi2c_master_edma_handle_t * handle,size_t * count)344 status_t LPI2C_MasterTransferGetCountEDMA(LPI2C_Type *base, lpi2c_master_edma_handle_t *handle, size_t *count)
345 {
346 assert(handle);
347
348 if (!count)
349 {
350 return kStatus_InvalidArgument;
351 }
352
353 /* Catch when there is not an active transfer. */
354 if (!handle->isBusy)
355 {
356 *count = 0;
357 return kStatus_NoTransferInProgress;
358 }
359
360 uint32_t remaining = handle->transfer.dataSize;
361
362 /* If the DMA is still on a commands transfer that chains to the actual data transfer, */
363 /* we do nothing and return the number of transferred bytes as zero. */
364 if (handle->tx->base->TCD[handle->tx->channel].DLAST_SGA == 0)
365 {
366 if (handle->transfer.direction == kLPI2C_Write)
367 {
368 remaining =
369 (uint32_t)handle->nbytes * EDMA_GetRemainingMajorLoopCount(handle->tx->base, handle->tx->channel);
370 }
371 else
372 {
373 remaining =
374 (uint32_t)handle->nbytes * EDMA_GetRemainingMajorLoopCount(handle->rx->base, handle->rx->channel);
375 }
376 }
377
378 *count = handle->transfer.dataSize - remaining;
379
380 return kStatus_Success;
381 }
382
LPI2C_MasterTransferAbortEDMA(LPI2C_Type * base,lpi2c_master_edma_handle_t * handle)383 status_t LPI2C_MasterTransferAbortEDMA(LPI2C_Type *base, lpi2c_master_edma_handle_t *handle)
384 {
385 /* Catch when there is not an active transfer. */
386 if (!handle->isBusy)
387 {
388 return kStatus_LPI2C_Idle;
389 }
390
391 /* Terminate DMA transfers. */
392 EDMA_AbortTransfer(handle->rx);
393 if (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base))
394 {
395 EDMA_AbortTransfer(handle->tx);
396 }
397
398 /* Reset fifos. */
399 base->MCR |= LPI2C_MCR_RRF_MASK | LPI2C_MCR_RTF_MASK;
400
401 /* Send a stop command to finalize the transfer. */
402 base->MTDR = kStopCmd;
403
404 /* Reset handle. */
405 handle->isBusy = false;
406
407 return kStatus_Success;
408 }
409
410 /*!
411 * @brief DMA completion callback.
412 * @param dmaHandle DMA channel handle for the channel that completed.
413 * @param userData User data associated with the channel handle. For this callback, the user data is the
414 * LPI2C DMA driver handle.
415 * @param isTransferDone Whether the DMA transfer has completed.
416 * @param tcds Number of TCDs that completed.
417 */
LPI2C_MasterEDMACallback(edma_handle_t * dmaHandle,void * userData,bool isTransferDone,uint32_t tcds)418 static void LPI2C_MasterEDMACallback(edma_handle_t *dmaHandle, void *userData, bool isTransferDone, uint32_t tcds)
419 {
420 lpi2c_master_edma_handle_t *handle = (lpi2c_master_edma_handle_t *)userData;
421 if (!handle)
422 {
423 return;
424 }
425
426 /* Check for errors. */
427 status_t result = LPI2C_MasterCheckAndClearError(handle->base, LPI2C_MasterGetStatusFlags(handle->base));
428
429 /* Done with this transaction. */
430 handle->isBusy = false;
431
432 if (!(handle->transfer.flags & kLPI2C_TransferNoStopFlag))
433 {
434 /* Send a stop command to finalize the transfer. */
435 handle->base->MTDR = kStopCmd;
436 }
437
438 /* Invoke callback. */
439 if (handle->completionCallback)
440 {
441 handle->completionCallback(handle->base, handle, result, handle->userData);
442 }
443 }
444