added prescaler for 16 bit pwm in LPC1347 target
Fork of mbed-dev by
Diff: targets/hal/TARGET_Freescale/TARGET_KSDK2_MCUS/TARGET_KL43Z/drivers/fsl_i2c_dma.c
- Revision:
- 144:ef7eb2e8f9f7
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/targets/hal/TARGET_Freescale/TARGET_KSDK2_MCUS/TARGET_KL43Z/drivers/fsl_i2c_dma.c Fri Sep 02 15:07:44 2016 +0100 @@ -0,0 +1,523 @@ +/* + * 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; +}