added prescaler for 16 bit pwm in LPC1347 target
Fork of mbed-dev by
targets/hal/TARGET_Freescale/TARGET_KSDK2_MCUS/TARGET_KL43Z/drivers/fsl_i2c_dma.c
- Committer:
- <>
- Date:
- 2016-09-02
- Revision:
- 144:ef7eb2e8f9f7
File content as of revision 144:ef7eb2e8f9f7:
/* * Copyright (c) 2015, Freescale Semiconductor, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, this list * of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, this * list of conditions and the following disclaimer in the documentation and/or * other materials provided with the distribution. * * o Neither the name of Freescale Semiconductor, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "fsl_i2c_dma.h" /******************************************************************************* * Definitions ******************************************************************************/ /*<! @brief Structure definition for i2c_master_dma_handle_t. The structure is private. */ typedef struct _i2c_master_dma_private_handle { I2C_Type *base; i2c_master_dma_handle_t *handle; } i2c_master_dma_private_handle_t; /*! @brief i2c master DMA transfer state. */ enum _i2c_master_dma_transfer_states { kIdleState = 0x0U, /*!< I2C bus idle. */ kTransferDataState = 0x1U, /*!< 7-bit address check state. */ }; /*! @brief Common sets of flags used by the driver. */ enum _i2c_flag_constants { /*! All flags which are cleared by the driver upon starting a transfer. */ #if defined(FSL_FEATURE_I2C_HAS_START_STOP_DETECT) && FSL_FEATURE_I2C_HAS_START_STOP_DETECT kClearFlags = kI2C_ArbitrationLostFlag | kI2C_IntPendingFlag | kI2C_StartDetectFlag | kI2C_StopDetectFlag, #elif defined(FSL_FEATURE_I2C_HAS_STOP_DETECT) && FSL_FEATURE_I2C_HAS_STOP_DETECT kClearFlags = kI2C_ArbitrationLostFlag | kI2C_IntPendingFlag | kI2C_StopDetectFlag, #else kClearFlags = kI2C_ArbitrationLostFlag | kI2C_IntPendingFlag, #endif }; /******************************************************************************* * Prototypes ******************************************************************************/ /*! * @brief DMA callback for I2C master DMA driver. * * @param handle DMA handler for I2C master DMA driver * @param userData user param passed to the callback function */ static void I2C_MasterTransferCallbackDMA(dma_handle_t *handle, void *userData); /*! * @brief Check and clear status operation. * * @param base I2C peripheral base address. * @param status current i2c hardware status * @retval kStatus_Success No error found. * @retval kStatus_I2C_ArbitrationLost Transfer error, arbitration lost. * @retval kStatus_I2C_Nak Received Nak error. */ static status_t I2C_CheckAndClearError(I2C_Type *base, uint32_t status); /*! * @brief DMA config for I2C master driver. * * @param base I2C peripheral base address. * @param handle pointer to i2c_master_dma_handle_t structure which stores the transfer state. */ static void I2C_MasterTransferDMAConfig(I2C_Type *base, i2c_master_dma_handle_t *handle); /*! * @brief Set up master transfer, send slave address and sub address(if any), wait until the * wait until address sent status return. * * @param base I2C peripheral base address. * @param handle pointer to i2c_master_dma_handle_t structure which stores the transfer state. * @param xfer pointer to i2c_master_transfer_t structure. */ static status_t I2C_InitTransferStateMachineDMA(I2C_Type *base, i2c_master_dma_handle_t *handle, i2c_master_transfer_t *xfer); /*! * @brief Get the I2C instance from peripheral base address. * * @param base I2C peripheral base address. * @return I2C instance. */ extern uint32_t I2C_GetInstance(I2C_Type *base); /******************************************************************************* * Variables ******************************************************************************/ /*<! Private handle only used for internally. */ static i2c_master_dma_private_handle_t s_dmaPrivateHandle[FSL_FEATURE_SOC_I2C_COUNT]; /******************************************************************************* * Codes ******************************************************************************/ static void I2C_MasterTransferCallbackDMA(dma_handle_t *handle, void *userData) { i2c_master_dma_private_handle_t *i2cPrivateHandle = (i2c_master_dma_private_handle_t *)userData; status_t result = kStatus_Success; /* Disable DMA. */ I2C_EnableDMA(i2cPrivateHandle->base, false); /* Send stop if kI2C_TransferNoStop flag is not asserted. */ if (!(i2cPrivateHandle->handle->transfer.flags & kI2C_TransferNoStopFlag)) { if (i2cPrivateHandle->handle->transfer.direction == kI2C_Read) { /* Change to send NAK at the last byte. */ i2cPrivateHandle->base->C1 |= I2C_C1_TXAK_MASK; /* Wait the last data to be received. */ while (!(i2cPrivateHandle->base->S & kI2C_TransferCompleteFlag)) { } /* Send stop signal. */ result = I2C_MasterStop(i2cPrivateHandle->base); /* Read the last data byte. */ *(i2cPrivateHandle->handle->transfer.data + i2cPrivateHandle->handle->transfer.dataSize - 1) = i2cPrivateHandle->base->D; } else { /* Wait the last data to be sent. */ while (!(i2cPrivateHandle->base->S & kI2C_TransferCompleteFlag)) { } /* Send stop signal. */ result = I2C_MasterStop(i2cPrivateHandle->base); } } i2cPrivateHandle->handle->state = kIdleState; if (i2cPrivateHandle->handle->completionCallback) { i2cPrivateHandle->handle->completionCallback(i2cPrivateHandle->base, i2cPrivateHandle->handle, result, i2cPrivateHandle->handle->userData); } } static status_t I2C_CheckAndClearError(I2C_Type *base, uint32_t status) { status_t result = kStatus_Success; /* Check arbitration lost. */ if (status & kI2C_ArbitrationLostFlag) { /* Clear arbitration lost flag. */ base->S = kI2C_ArbitrationLostFlag; result = kStatus_I2C_ArbitrationLost; } /* Check NAK */ else if (status & kI2C_ReceiveNakFlag) { result = kStatus_I2C_Nak; } else { } return result; } static status_t I2C_InitTransferStateMachineDMA(I2C_Type *base, i2c_master_dma_handle_t *handle, i2c_master_transfer_t *xfer) { assert(handle); assert(xfer); /* Set up transfer first. */ i2c_direction_t direction = xfer->direction; status_t result = kStatus_Success; uint16_t timeout = UINT16_MAX; if (handle->state != kIdleState) { return kStatus_I2C_Busy; } else { /* Init the handle member. */ handle->transfer = *xfer; /* Save total transfer size. */ handle->transferSize = xfer->dataSize; handle->state = kTransferDataState; /* Wait until ready to complete. */ while ((!(base->S & kI2C_TransferCompleteFlag)) && (--timeout)) { } /* Failed to start the transfer. */ if (timeout == 0) { return kStatus_I2C_Timeout; } /* Clear all status before transfer. */ I2C_MasterClearStatusFlags(base, kClearFlags); /* Change to send write address when it's a read operation with command. */ if ((xfer->subaddressSize > 0) && (xfer->direction == kI2C_Read)) { direction = kI2C_Write; } /* If repeated start is requested, send repeated start. */ if (handle->transfer.flags & kI2C_TransferRepeatedStartFlag) { result = I2C_MasterRepeatedStart(base, handle->transfer.slaveAddress, direction); } else /* For normal transfer, send start. */ { result = I2C_MasterStart(base, handle->transfer.slaveAddress, direction); } /* Send subaddress. */ if (handle->transfer.subaddressSize) { do { /* Wait until data transfer complete. */ while (!(base->S & kI2C_IntPendingFlag)) { } /* Clear interrupt pending flag. */ base->S = kI2C_IntPendingFlag; handle->transfer.subaddressSize--; base->D = ((handle->transfer.subaddress) >> (8 * handle->transfer.subaddressSize)); /* Check if there's transfer error. */ result = I2C_CheckAndClearError(base, base->S); if (result) { return result; } } while ((handle->transfer.subaddressSize > 0) && (result == kStatus_Success)); if (handle->transfer.direction == kI2C_Read) { /* Wait until data transfer complete. */ while (!(base->S & kI2C_IntPendingFlag)) { } /* Clear pending flag. */ base->S = kI2C_IntPendingFlag; /* Send repeated start and slave address. */ result = I2C_MasterRepeatedStart(base, handle->transfer.slaveAddress, kI2C_Read); } } if (result) { return result; } /* Wait until data transfer complete. */ while (!(base->S & kI2C_IntPendingFlag)) { } /* Clear pending flag. */ base->S = kI2C_IntPendingFlag; /* Check if there's transfer error. */ result = I2C_CheckAndClearError(base, base->S); } return result; } static void I2C_MasterTransferDMAConfig(I2C_Type *base, i2c_master_dma_handle_t *handle) { dma_transfer_config_t transfer_config; dma_transfer_options_t transfer_options = kDMA_EnableInterrupt; if (handle->transfer.direction == kI2C_Read) { transfer_config.srcAddr = (uint32_t)I2C_GetDataRegAddr(base); transfer_config.destAddr = (uint32_t)(handle->transfer.data); /* Send stop if kI2C_TransferNoStop flag is not asserted. */ if (!(handle->transfer.flags & kI2C_TransferNoStopFlag)) { transfer_config.transferSize = (handle->transfer.dataSize - 1); } else { transfer_config.transferSize = handle->transfer.dataSize; } transfer_config.srcSize = kDMA_Transfersize8bits; transfer_config.enableSrcIncrement = false; transfer_config.destSize = kDMA_Transfersize8bits; transfer_config.enableDestIncrement = true; } else { transfer_config.srcAddr = (uint32_t)(handle->transfer.data + 1); transfer_config.destAddr = (uint32_t)I2C_GetDataRegAddr(base); transfer_config.transferSize = (handle->transfer.dataSize - 1); transfer_config.srcSize = kDMA_Transfersize8bits; transfer_config.enableSrcIncrement = true; transfer_config.destSize = kDMA_Transfersize8bits; transfer_config.enableDestIncrement = false; } DMA_SubmitTransfer(handle->dmaHandle, &transfer_config, transfer_options); DMA_StartTransfer(handle->dmaHandle); } void I2C_MasterTransferCreateHandleDMA(I2C_Type *base, i2c_master_dma_handle_t *handle, i2c_master_dma_transfer_callback_t callback, void *userData, dma_handle_t *dmaHandle) { assert(handle); assert(dmaHandle); uint32_t instance = I2C_GetInstance(base); /* Zero handle. */ memset(handle, 0, sizeof(*handle)); /* Set the user callback and userData. */ handle->completionCallback = callback; handle->userData = userData; /* Set the handle for DMA. */ handle->dmaHandle = dmaHandle; s_dmaPrivateHandle[instance].base = base; s_dmaPrivateHandle[instance].handle = handle; DMA_SetCallback(dmaHandle, (dma_callback)I2C_MasterTransferCallbackDMA, &s_dmaPrivateHandle[instance]); } status_t I2C_MasterTransferDMA(I2C_Type *base, i2c_master_dma_handle_t *handle, i2c_master_transfer_t *xfer) { assert(handle); assert(xfer); status_t result; uint8_t tmpReg; volatile uint8_t dummy = 0; /* Add this to avoid build warning. */ dummy++; /* Disable dma transfer. */ I2C_EnableDMA(base, false); /* Send address and command buffer(if there is), until senddata phase or receive data phase. */ result = I2C_InitTransferStateMachineDMA(base, handle, xfer); if (result != kStatus_Success) { /* Send stop if received Nak. */ if (result == kStatus_I2C_Nak) { if (I2C_MasterStop(base) != kStatus_Success) { result = kStatus_I2C_Timeout; } } /* Reset the state to idle state. */ handle->state = kIdleState; return result; } /* Configure dma transfer. */ /* For i2c send, need to send 1 byte first to trigger the dma, for i2c read, need to send stop before reading the last byte, so the dma transfer size should be (xSize - 1). */ if (handle->transfer.dataSize > 1) { I2C_MasterTransferDMAConfig(base, handle); if (handle->transfer.direction == kI2C_Read) { /* Change direction for receive. */ base->C1 &= ~I2C_C1_TX_MASK; /* Read dummy to release the bus. */ dummy = base->D; /* Enabe dma transfer. */ I2C_EnableDMA(base, true); } else { /* Enabe dma transfer. */ I2C_EnableDMA(base, true); /* Send the first data. */ base->D = *handle->transfer.data; } } else /* If transfer size is 1, use polling method. */ { if (handle->transfer.direction == kI2C_Read) { tmpReg = base->C1; /* Change direction to Rx. */ tmpReg &= ~I2C_C1_TX_MASK; /* Configure send NAK */ tmpReg |= I2C_C1_TXAK_MASK; base->C1 = tmpReg; /* Read dummy to release the bus. */ dummy = base->D; } else { base->D = *handle->transfer.data; } /* Wait until data transfer complete. */ while (!(base->S & kI2C_IntPendingFlag)) { } /* Clear pending flag. */ base->S = kI2C_IntPendingFlag; /* Send stop if kI2C_TransferNoStop flag is not asserted. */ if (!(handle->transfer.flags & kI2C_TransferNoStopFlag)) { result = I2C_MasterStop(base); } /* Read the last byte of data. */ if (handle->transfer.direction == kI2C_Read) { *handle->transfer.data = base->D; } /* Reset the state to idle. */ handle->state = kIdleState; } return result; } status_t I2C_MasterTransferGetCountDMA(I2C_Type *base, i2c_master_dma_handle_t *handle, size_t *count) { assert(handle); if (!count) { return kStatus_InvalidArgument; } if (kIdleState != handle->state) { *count = (handle->transferSize - DMA_GetRemainingBytes(handle->dmaHandle->base, handle->dmaHandle->channel)); } else { *count = handle->transferSize; } return kStatus_Success; } void I2C_MasterTransferAbortDMA(I2C_Type *base, i2c_master_dma_handle_t *handle) { DMA_AbortTransfer(handle->dmaHandle); /* Disable dma transfer. */ I2C_EnableDMA(base, false); /* Reset the state to idle. */ handle->state = kIdleState; }