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