fsl_lpi2c_edma.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. /*
  2. * The Clear BSD License
  3. * Copyright (c) 2015, Freescale Semiconductor, Inc.
  4. * Copyright 2016-2017 NXP
  5. * All rights reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without modification,
  8. * are permitted (subject to the limitations in the disclaimer below) provided
  9. * that the following conditions are met:
  10. *
  11. * o Redistributions of source code must retain the above copyright notice, this list
  12. * of conditions and the following disclaimer.
  13. *
  14. * o Redistributions in binary form must reproduce the above copyright notice, this
  15. * list of conditions and the following disclaimer in the documentation and/or
  16. * other materials provided with the distribution.
  17. *
  18. * o Neither the name of the copyright holder nor the names of its
  19. * contributors may be used to endorse or promote products derived from this
  20. * software without specific prior written permission.
  21. *
  22. * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE.
  23. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  24. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  25. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  26. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
  27. * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  28. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  29. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  30. * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  31. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  32. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  33. */
  34. #include "fsl_lpi2c_edma.h"
  35. #include <stdlib.h>
  36. #include <string.h>
  37. /*******************************************************************************
  38. * Definitions
  39. ******************************************************************************/
  40. /* Component ID definition, used by tools. */
  41. #ifndef FSL_COMPONENT_ID
  42. #define FSL_COMPONENT_ID "platform.drivers.lpi2c_edma"
  43. #endif
  44. /* @brief Mask to align an address to 32 bytes. */
  45. #define ALIGN_32_MASK (0x1fU)
  46. /*! @brief Common sets of flags used by the driver. */
  47. enum _lpi2c_flag_constants
  48. {
  49. /*! All flags which are cleared by the driver upon starting a transfer. */
  50. kMasterClearFlags = kLPI2C_MasterEndOfPacketFlag | kLPI2C_MasterStopDetectFlag | kLPI2C_MasterNackDetectFlag |
  51. kLPI2C_MasterArbitrationLostFlag | kLPI2C_MasterFifoErrFlag | kLPI2C_MasterPinLowTimeoutFlag |
  52. kLPI2C_MasterDataMatchFlag,
  53. /*! IRQ sources enabled by the non-blocking transactional API. */
  54. kMasterIrqFlags = kLPI2C_MasterArbitrationLostFlag | kLPI2C_MasterTxReadyFlag | kLPI2C_MasterRxReadyFlag |
  55. kLPI2C_MasterStopDetectFlag | kLPI2C_MasterNackDetectFlag | kLPI2C_MasterPinLowTimeoutFlag |
  56. kLPI2C_MasterFifoErrFlag,
  57. /*! Errors to check for. */
  58. kMasterErrorFlags = kLPI2C_MasterNackDetectFlag | kLPI2C_MasterArbitrationLostFlag | kLPI2C_MasterFifoErrFlag |
  59. kLPI2C_MasterPinLowTimeoutFlag,
  60. /*! All flags which are cleared by the driver upon starting a transfer. */
  61. kSlaveClearFlags = kLPI2C_SlaveRepeatedStartDetectFlag | kLPI2C_SlaveStopDetectFlag | kLPI2C_SlaveBitErrFlag |
  62. kLPI2C_SlaveFifoErrFlag,
  63. /*! IRQ sources enabled by the non-blocking transactional API. */
  64. kSlaveIrqFlags = kLPI2C_SlaveTxReadyFlag | kLPI2C_SlaveRxReadyFlag | kLPI2C_SlaveStopDetectFlag |
  65. kLPI2C_SlaveRepeatedStartDetectFlag | kLPI2C_SlaveFifoErrFlag | kLPI2C_SlaveBitErrFlag |
  66. kLPI2C_SlaveTransmitAckFlag | kLPI2C_SlaveAddressValidFlag,
  67. /*! Errors to check for. */
  68. kSlaveErrorFlags = kLPI2C_SlaveFifoErrFlag | kLPI2C_SlaveBitErrFlag,
  69. };
  70. /* ! @brief LPI2C master fifo commands. */
  71. enum _lpi2c_master_fifo_cmd
  72. {
  73. kTxDataCmd = LPI2C_MTDR_CMD(0x0U), /*!< Transmit DATA[7:0] */
  74. kRxDataCmd = LPI2C_MTDR_CMD(0X1U), /*!< Receive (DATA[7:0] + 1) bytes */
  75. kStopCmd = LPI2C_MTDR_CMD(0x2U), /*!< Generate STOP condition */
  76. kStartCmd = LPI2C_MTDR_CMD(0x4U), /*!< Generate(repeated) START and transmit address in DATA[[7:0] */
  77. };
  78. /*! @brief States for the state machine used by transactional APIs. */
  79. enum _lpi2c_transfer_states
  80. {
  81. kIdleState = 0,
  82. kSendCommandState,
  83. kIssueReadCommandState,
  84. kTransferDataState,
  85. kStopState,
  86. kWaitForCompletionState,
  87. };
  88. /*! @brief Typedef for interrupt handler. */
  89. typedef void (*lpi2c_isr_t)(LPI2C_Type *base, void *handle);
  90. /*******************************************************************************
  91. * Prototypes
  92. ******************************************************************************/
  93. static uint32_t LPI2C_GenerateCommands(lpi2c_master_edma_handle_t *handle);
  94. static void LPI2C_MasterEDMACallback(edma_handle_t *dmaHandle, void *userData, bool isTransferDone, uint32_t tcds);
  95. /*******************************************************************************
  96. * Code
  97. ******************************************************************************/
  98. void LPI2C_MasterCreateEDMAHandle(LPI2C_Type *base,
  99. lpi2c_master_edma_handle_t *handle,
  100. edma_handle_t *rxDmaHandle,
  101. edma_handle_t *txDmaHandle,
  102. lpi2c_master_edma_transfer_callback_t callback,
  103. void *userData)
  104. {
  105. assert(handle);
  106. assert(rxDmaHandle);
  107. assert(txDmaHandle);
  108. /* Clear out the handle. */
  109. memset(handle, 0, sizeof(*handle));
  110. /* Set up the handle. For combined rx/tx DMA requests, the tx channel handle is set to the rx handle */
  111. /* in order to make the transfer API code simpler. */
  112. handle->base = base;
  113. handle->completionCallback = callback;
  114. handle->userData = userData;
  115. handle->rx = rxDmaHandle;
  116. handle->tx = FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) ? txDmaHandle : rxDmaHandle;
  117. /* Set DMA channel completion callbacks. */
  118. EDMA_SetCallback(handle->rx, LPI2C_MasterEDMACallback, handle);
  119. if (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base))
  120. {
  121. EDMA_SetCallback(handle->tx, LPI2C_MasterEDMACallback, handle);
  122. }
  123. }
  124. /*!
  125. * @brief Prepares the command buffer with the sequence of commands needed to send the requested transaction.
  126. * @param handle Master DMA driver handle.
  127. * @return Number of command words.
  128. */
  129. static uint32_t LPI2C_GenerateCommands(lpi2c_master_edma_handle_t *handle)
  130. {
  131. lpi2c_master_transfer_t *xfer = &handle->transfer;
  132. uint16_t *cmd = (uint16_t *)&handle->commandBuffer;
  133. uint32_t cmdCount = 0;
  134. /* Handle no start option. */
  135. if (xfer->flags & kLPI2C_TransferNoStartFlag)
  136. {
  137. if (xfer->direction == kLPI2C_Read)
  138. {
  139. /* Need to issue read command first. */
  140. cmd[cmdCount++] = kRxDataCmd | LPI2C_MTDR_DATA(xfer->dataSize - 1);
  141. }
  142. }
  143. else
  144. {
  145. /*
  146. * Initial direction depends on whether a subaddress was provided, and of course the actual
  147. * data transfer direction.
  148. */
  149. lpi2c_direction_t direction = xfer->subaddressSize ? kLPI2C_Write : xfer->direction;
  150. /* Start command. */
  151. cmd[cmdCount++] =
  152. (uint16_t)kStartCmd | (uint16_t)((uint16_t)((uint16_t)xfer->slaveAddress << 1U) | (uint16_t)direction);
  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. /* Reads need special handling because we have to issue a read command and maybe a repeated start. */
  164. if ((xfer->dataSize) && (xfer->direction == kLPI2C_Read))
  165. {
  166. /* Need to send repeated start if switching directions to read. */
  167. if (direction == kLPI2C_Write)
  168. {
  169. cmd[cmdCount++] = (uint16_t)kStartCmd |
  170. (uint16_t)((uint16_t)((uint16_t)xfer->slaveAddress << 1U) | (uint16_t)kLPI2C_Read);
  171. }
  172. /* Read command. */
  173. cmd[cmdCount++] = kRxDataCmd | LPI2C_MTDR_DATA(xfer->dataSize - 1);
  174. }
  175. }
  176. return cmdCount;
  177. }
  178. status_t LPI2C_MasterTransferEDMA(LPI2C_Type *base,
  179. lpi2c_master_edma_handle_t *handle,
  180. lpi2c_master_transfer_t *transfer)
  181. {
  182. status_t result;
  183. assert(handle);
  184. assert(transfer);
  185. assert(transfer->subaddressSize <= sizeof(transfer->subaddress));
  186. /* Return busy if another transaction is in progress. */
  187. if (handle->isBusy)
  188. {
  189. return kStatus_LPI2C_Busy;
  190. }
  191. /* Return an error if the bus is already in use not by us. */
  192. result = LPI2C_CheckForBusyBus(base);
  193. if (result)
  194. {
  195. return result;
  196. }
  197. /* We're now busy. */
  198. handle->isBusy = true;
  199. /* Disable LPI2C IRQ and DMA sources while we configure stuff. */
  200. LPI2C_MasterDisableInterrupts(base, kMasterIrqFlags);
  201. LPI2C_MasterEnableDMA(base, false, false);
  202. /* Clear all flags. */
  203. LPI2C_MasterClearStatusFlags(base, kMasterClearFlags);
  204. /* Save transfer into handle. */
  205. handle->transfer = *transfer;
  206. /* Generate commands to send. */
  207. uint32_t commandCount = LPI2C_GenerateCommands(handle);
  208. /* If the user is transmitting no data with no start or stop, then just go ahead and invoke the callback. */
  209. if ((!commandCount) && (transfer->dataSize == 0))
  210. {
  211. if (handle->completionCallback)
  212. {
  213. handle->completionCallback(base, handle, kStatus_Success, handle->userData);
  214. }
  215. return kStatus_Success;
  216. }
  217. /* Reset DMA channels. */
  218. EDMA_ResetChannel(handle->rx->base, handle->rx->channel);
  219. if (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base))
  220. {
  221. EDMA_ResetChannel(handle->tx->base, handle->tx->channel);
  222. }
  223. /* Get a 32-byte aligned TCD pointer. */
  224. edma_tcd_t *tcd = (edma_tcd_t *)((uint32_t)(&handle->tcds[1]) & (~ALIGN_32_MASK));
  225. bool hasSendData = (transfer->direction == kLPI2C_Write) && (transfer->dataSize);
  226. bool hasReceiveData = (transfer->direction == kLPI2C_Read) && (transfer->dataSize);
  227. edma_transfer_config_t transferConfig;
  228. edma_tcd_t *linkTcd = NULL;
  229. /* Set up data transmit. */
  230. if (hasSendData)
  231. {
  232. transferConfig.srcAddr = (uint32_t)transfer->data;
  233. transferConfig.destAddr = (uint32_t)LPI2C_MasterGetTxFifoAddress(base);
  234. transferConfig.srcTransferSize = kEDMA_TransferSize1Bytes;
  235. transferConfig.destTransferSize = kEDMA_TransferSize1Bytes;
  236. transferConfig.srcOffset = sizeof(uint8_t);
  237. transferConfig.destOffset = 0;
  238. transferConfig.minorLoopBytes = sizeof(uint8_t); /* TODO optimize to fill fifo */
  239. transferConfig.majorLoopCounts = transfer->dataSize;
  240. /* Store the initially configured eDMA minor byte transfer count into the LPI2C handle */
  241. handle->nbytes = transferConfig.minorLoopBytes;
  242. if (commandCount)
  243. {
  244. /* Create a software TCD, which will be chained after the commands. */
  245. EDMA_TcdReset(tcd);
  246. EDMA_TcdSetTransferConfig(tcd, &transferConfig, NULL);
  247. EDMA_TcdEnableInterrupts(tcd, kEDMA_MajorInterruptEnable);
  248. linkTcd = tcd;
  249. }
  250. else
  251. {
  252. /* User is only transmitting data with no required commands, so this transfer can stand alone. */
  253. EDMA_SetTransferConfig(handle->tx->base, handle->tx->channel, &transferConfig, NULL);
  254. EDMA_EnableChannelInterrupts(handle->tx->base, handle->tx->channel, kEDMA_MajorInterruptEnable);
  255. }
  256. }
  257. else if (hasReceiveData)
  258. {
  259. /* Set up data receive. */
  260. transferConfig.srcAddr = (uint32_t)LPI2C_MasterGetRxFifoAddress(base);
  261. transferConfig.destAddr = (uint32_t)transfer->data;
  262. transferConfig.srcTransferSize = kEDMA_TransferSize1Bytes;
  263. transferConfig.destTransferSize = kEDMA_TransferSize1Bytes;
  264. transferConfig.srcOffset = 0;
  265. transferConfig.destOffset = sizeof(uint8_t);
  266. transferConfig.minorLoopBytes = sizeof(uint8_t); /* TODO optimize to empty fifo */
  267. transferConfig.majorLoopCounts = transfer->dataSize;
  268. /* Store the initially configured eDMA minor byte transfer count into the LPI2C handle */
  269. handle->nbytes = transferConfig.minorLoopBytes;
  270. if (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base) || (!commandCount))
  271. {
  272. /* We can put this receive transfer on its own DMA channel. */
  273. EDMA_SetTransferConfig(handle->rx->base, handle->rx->channel, &transferConfig, NULL);
  274. EDMA_EnableChannelInterrupts(handle->rx->base, handle->rx->channel, kEDMA_MajorInterruptEnable);
  275. }
  276. else
  277. {
  278. /* For shared rx/tx DMA requests when there are commands, create a software TCD which will be */
  279. /* chained onto the commands transfer, notice that in this situation assume tx/rx uses same channel */
  280. EDMA_TcdReset(tcd);
  281. EDMA_TcdSetTransferConfig(tcd, &transferConfig, NULL);
  282. EDMA_TcdEnableInterrupts(tcd, kEDMA_MajorInterruptEnable);
  283. linkTcd = tcd;
  284. }
  285. }
  286. else
  287. {
  288. /* No data to send */
  289. }
  290. /* Set up commands transfer. */
  291. if (commandCount)
  292. {
  293. transferConfig.srcAddr = (uint32_t)handle->commandBuffer;
  294. transferConfig.destAddr = (uint32_t)LPI2C_MasterGetTxFifoAddress(base);
  295. transferConfig.srcTransferSize = kEDMA_TransferSize2Bytes;
  296. transferConfig.destTransferSize = kEDMA_TransferSize2Bytes;
  297. transferConfig.srcOffset = sizeof(uint16_t);
  298. transferConfig.destOffset = 0;
  299. transferConfig.minorLoopBytes = sizeof(uint16_t); /* TODO optimize to fill fifo */
  300. transferConfig.majorLoopCounts = commandCount;
  301. EDMA_SetTransferConfig(handle->tx->base, handle->tx->channel, &transferConfig, linkTcd);
  302. }
  303. /* Start DMA transfer. */
  304. if (hasReceiveData || !FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base))
  305. {
  306. EDMA_StartTransfer(handle->rx);
  307. }
  308. if (hasReceiveData && !FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base))
  309. {
  310. EDMA_EnableChannelInterrupts(handle->tx->base, handle->tx->channel, kEDMA_MajorInterruptEnable);
  311. }
  312. if ((hasSendData || commandCount) && FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base))
  313. {
  314. EDMA_StartTransfer(handle->tx);
  315. }
  316. /* Enable DMA in both directions. This actually kicks of the transfer. */
  317. LPI2C_MasterEnableDMA(base, true, true);
  318. return result;
  319. }
  320. status_t LPI2C_MasterTransferGetCountEDMA(LPI2C_Type *base, lpi2c_master_edma_handle_t *handle, size_t *count)
  321. {
  322. assert(handle);
  323. if (!count)
  324. {
  325. return kStatus_InvalidArgument;
  326. }
  327. /* Catch when there is not an active transfer. */
  328. if (!handle->isBusy)
  329. {
  330. *count = 0;
  331. return kStatus_NoTransferInProgress;
  332. }
  333. uint32_t remaining = handle->transfer.dataSize;
  334. /* If the DMA is still on a commands transfer that chains to the actual data transfer, */
  335. /* we do nothing and return the number of transferred bytes as zero. */
  336. if (EDMA_GetNextTCDAddress(handle->tx) == 0)
  337. {
  338. if (handle->transfer.direction == kLPI2C_Write)
  339. {
  340. remaining =
  341. (uint32_t)handle->nbytes * EDMA_GetRemainingMajorLoopCount(handle->tx->base, handle->tx->channel);
  342. }
  343. else
  344. {
  345. remaining =
  346. (uint32_t)handle->nbytes * EDMA_GetRemainingMajorLoopCount(handle->rx->base, handle->rx->channel);
  347. }
  348. }
  349. *count = handle->transfer.dataSize - remaining;
  350. return kStatus_Success;
  351. }
  352. status_t LPI2C_MasterTransferAbortEDMA(LPI2C_Type *base, lpi2c_master_edma_handle_t *handle)
  353. {
  354. /* Catch when there is not an active transfer. */
  355. if (!handle->isBusy)
  356. {
  357. return kStatus_LPI2C_Idle;
  358. }
  359. /* Terminate DMA transfers. */
  360. EDMA_AbortTransfer(handle->rx);
  361. if (FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base))
  362. {
  363. EDMA_AbortTransfer(handle->tx);
  364. }
  365. /* Reset fifos. */
  366. base->MCR |= LPI2C_MCR_RRF_MASK | LPI2C_MCR_RTF_MASK;
  367. /* Send a stop command to finalize the transfer. */
  368. base->MTDR = kStopCmd;
  369. /* Reset handle. */
  370. handle->isBusy = false;
  371. return kStatus_Success;
  372. }
  373. /*!
  374. * @brief DMA completion callback.
  375. * @param dmaHandle DMA channel handle for the channel that completed.
  376. * @param userData User data associated with the channel handle. For this callback, the user data is the
  377. * LPI2C DMA driver handle.
  378. * @param isTransferDone Whether the DMA transfer has completed.
  379. * @param tcds Number of TCDs that completed.
  380. */
  381. static void LPI2C_MasterEDMACallback(edma_handle_t *dmaHandle, void *userData, bool isTransferDone, uint32_t tcds)
  382. {
  383. lpi2c_master_edma_handle_t *handle = (lpi2c_master_edma_handle_t *)userData;
  384. bool hasReceiveData;
  385. if (!handle)
  386. {
  387. return;
  388. }
  389. hasReceiveData = (handle->transfer.direction == kLPI2C_Read) && (handle->transfer.dataSize);
  390. if (hasReceiveData && !FSL_FEATURE_LPI2C_HAS_SEPARATE_DMA_RX_TX_REQn(base))
  391. {
  392. if (EDMA_GetNextTCDAddress(handle->tx) != 0)
  393. {
  394. LPI2C_MasterEnableDMA(handle->base, false, true);
  395. }
  396. }
  397. /* Check for errors. */
  398. status_t result = LPI2C_MasterCheckAndClearError(handle->base, LPI2C_MasterGetStatusFlags(handle->base));
  399. /* Done with this transaction. */
  400. handle->isBusy = false;
  401. if (!(handle->transfer.flags & kLPI2C_TransferNoStopFlag))
  402. {
  403. /* Send a stop command to finalize the transfer. */
  404. handle->base->MTDR = kStopCmd;
  405. }
  406. /* Invoke callback. */
  407. if (handle->completionCallback)
  408. {
  409. handle->completionCallback(handle->base, handle, result, handle->userData);
  410. }
  411. }