| /****************************************************************************** |
| * |
| * Copyright (C) 2010 - 2016 Xilinx, Inc. All rights reserved. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a copy |
| * of this software and associated documentation files (the "Software"), to deal |
| * in the Software without restriction, including without limitation the rights |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| * copies of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * Use of the Software is limited solely to applications: |
| * (a) running on a Xilinx device, or |
| * (b) that interact with a Xilinx device through a bus or interconnect. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
| * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF |
| * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| * |
| * Except as contained in this notice, the name of the Xilinx shall not be used |
| * in advertising or otherwise to promote the sale, use or other dealings in |
| * this Software without prior written authorization from Xilinx. |
| * |
| ******************************************************************************/ |
| /*****************************************************************************/ |
| /** |
| * |
| * @file xiicps_master.c |
| * @addtogroup iicps_v3_0 |
| * @{ |
| * |
| * Handles master mode transfers. |
| * |
| * <pre> MODIFICATION HISTORY: |
| * |
| * Ver Who Date Changes |
| * ----- --- -------- --------------------------------------------- |
| * 1.00a jz 01/30/10 First release |
| * 1.00a sdm 09/21/11 Updated the XIicPs_SetupMaster to not check for |
| * Bus Busy condition when the Hold Bit is set. |
| * 1.01a sg 03/30/12 Fixed an issue in XIicPs_MasterSendPolled where a |
| * check for transfer completion is added, which indicates |
| * the completion of current transfer. |
| * 2.0 hk 03/07/14 Added check for error status in the while loop that |
| * checks for completion. CR# 762244, 764875. |
| * 2.1 hk 04/24/14 Fix for CR# 789821 to handle >14 byte transfers. |
| * Fix for CR# 761060 - provision for repeated start. |
| * 2.2 hk 08/23/14 Slave monitor mode changes - clear FIFO, enable |
| * read mode and clear transfer size register. |
| * Disable NACK to avoid interrupts on each retry. |
| * 2.3 sk 10/06/14 Fill transmit fifo before address register when sending. |
| * Replaced XIICPS_DATA_INTR_DEPTH with XIICPS_FIFO_DEPTH. |
| * Repeated start feature removed. |
| * 3.0 sk 12/06/14 Implemented Repeated start feature. |
| * 01/31/15 Modified the code according to MISRAC 2012 Compliant. |
| * 02/18/15 Implemented larger data transfer using repeated start |
| * in Zynq UltraScale MP. |
| * 3.3 kvn 05/05/16 Modified latest code for MISRA-C:2012 Compliance. |
| * |
| * </pre> |
| * |
| ******************************************************************************/ |
| |
| /***************************** Include Files *********************************/ |
| |
| #include "xiicps.h" |
| |
| /************************** Constant Definitions *****************************/ |
| |
| /**************************** Type Definitions *******************************/ |
| |
| /***************** Macros (Inline Functions) Definitions *********************/ |
| |
| /************************** Function Prototypes ******************************/ |
| s32 TransmitFifoFill(XIicPs *InstancePtr); |
| |
| static s32 XIicPs_SetupMaster(XIicPs *InstancePtr, s32 Role); |
| static void MasterSendData(XIicPs *InstancePtr); |
| |
| /************************* Variable Definitions *****************************/ |
| |
| /*****************************************************************************/ |
| /** |
| * This function initiates an interrupt-driven send in master mode. |
| * |
| * It tries to send the first FIFO-full of data, then lets the interrupt |
| * handler to handle the rest of the data if there is any. |
| * |
| * @param InstancePtr is a pointer to the XIicPs instance. |
| * @param MsgPtr is the pointer to the send buffer. |
| * @param ByteCount is the number of bytes to be sent. |
| * @param SlaveAddr is the address of the slave we are sending to. |
| * |
| * @return None. |
| * |
| * @note This send routine is for interrupt-driven transfer only. |
| * |
| ****************************************************************************/ |
| void XIicPs_MasterSend(XIicPs *InstancePtr, u8 *MsgPtr, s32 ByteCount, |
| u16 SlaveAddr) |
| { |
| u32 BaseAddr; |
| u32 Platform = XGetPlatform_Info(); |
| |
| /* |
| * Assert validates the input arguments. |
| */ |
| Xil_AssertVoid(InstancePtr != NULL); |
| Xil_AssertVoid(MsgPtr != NULL); |
| Xil_AssertVoid(InstancePtr->IsReady == (u32)XIL_COMPONENT_IS_READY); |
| Xil_AssertVoid(XIICPS_ADDR_MASK >= SlaveAddr); |
| |
| |
| BaseAddr = InstancePtr->Config.BaseAddress; |
| InstancePtr->SendBufferPtr = MsgPtr; |
| InstancePtr->SendByteCount = ByteCount; |
| InstancePtr->RecvBufferPtr = NULL; |
| InstancePtr->IsSend = 1; |
| |
| /* |
| * Set repeated start if sending more than FIFO of data. |
| */ |
| if (((InstancePtr->IsRepeatedStart) != 0)|| |
| ((ByteCount > XIICPS_FIFO_DEPTH) != 0U)) { |
| XIicPs_WriteReg(BaseAddr, (u32)XIICPS_CR_OFFSET, |
| XIicPs_ReadReg(BaseAddr, (u32)XIICPS_CR_OFFSET) | |
| (u32)XIICPS_CR_HOLD_MASK); |
| } |
| |
| /* |
| * Setup as a master sending role. |
| */ |
| (void)XIicPs_SetupMaster(InstancePtr, SENDING_ROLE); |
| |
| (void)TransmitFifoFill(InstancePtr); |
| |
| XIicPs_EnableInterrupts(BaseAddr, |
| (u32)XIICPS_IXR_NACK_MASK | (u32)XIICPS_IXR_COMP_MASK | |
| (u32)XIICPS_IXR_ARB_LOST_MASK); |
| /* |
| * Do the address transfer to notify the slave. |
| */ |
| XIicPs_WriteReg(BaseAddr, XIICPS_ADDR_OFFSET, (u32)SlaveAddr); |
| |
| /* Clear the Hold bit in ZYNQ if receive byte count is less than |
| * the FIFO depth to get the completion interrupt properly. |
| */ |
| if ((ByteCount < XIICPS_FIFO_DEPTH) && (Platform == (u32)XPLAT_ZYNQ)) |
| { |
| XIicPs_WriteReg(BaseAddr, XIICPS_CR_OFFSET, |
| XIicPs_ReadReg(BaseAddr, (u32)XIICPS_CR_OFFSET) & |
| (u32)(~XIICPS_CR_HOLD_MASK)); |
| } |
| |
| } |
| |
| /*****************************************************************************/ |
| /** |
| * This function initiates an interrupt-driven receive in master mode. |
| * |
| * It sets the transfer size register so the slave can send data to us. |
| * The rest of the work is managed by interrupt handler. |
| * |
| * @param InstancePtr is a pointer to the XIicPs instance. |
| * @param MsgPtr is the pointer to the receive buffer. |
| * @param ByteCount is the number of bytes to be received. |
| * @param SlaveAddr is the address of the slave we are receiving from. |
| * |
| * @return None. |
| * |
| * @note This receive routine is for interrupt-driven transfer only. |
| * |
| ****************************************************************************/ |
| void XIicPs_MasterRecv(XIicPs *InstancePtr, u8 *MsgPtr, s32 ByteCount, |
| u16 SlaveAddr) |
| { |
| u32 BaseAddr; |
| |
| /* |
| * Assert validates the input arguments. |
| */ |
| Xil_AssertVoid(InstancePtr != NULL); |
| Xil_AssertVoid(MsgPtr != NULL); |
| Xil_AssertVoid(InstancePtr->IsReady == (u32)XIL_COMPONENT_IS_READY); |
| Xil_AssertVoid(XIICPS_ADDR_MASK >= SlaveAddr); |
| |
| BaseAddr = InstancePtr->Config.BaseAddress; |
| InstancePtr->RecvBufferPtr = MsgPtr; |
| InstancePtr->RecvByteCount = ByteCount; |
| InstancePtr->SendBufferPtr = NULL; |
| InstancePtr->IsSend = 0; |
| |
| if ((ByteCount > XIICPS_FIFO_DEPTH) || |
| ((InstancePtr->IsRepeatedStart) !=0)) |
| { |
| XIicPs_WriteReg(BaseAddr, XIICPS_CR_OFFSET, |
| XIicPs_ReadReg(BaseAddr, (u32)XIICPS_CR_OFFSET) | |
| (u32)XIICPS_CR_HOLD_MASK); |
| } |
| |
| /* |
| * Initialize for a master receiving role. |
| */ |
| (void)XIicPs_SetupMaster(InstancePtr, RECVING_ROLE); |
| /* |
| * Setup the transfer size register so the slave knows how much |
| * to send to us. |
| */ |
| if (ByteCount > (s32)XIICPS_MAX_TRANSFER_SIZE) { |
| XIicPs_WriteReg(BaseAddr, XIICPS_TRANS_SIZE_OFFSET, |
| XIICPS_MAX_TRANSFER_SIZE); |
| InstancePtr->CurrByteCount = (s32)XIICPS_MAX_TRANSFER_SIZE; |
| InstancePtr->UpdateTxSize = 1; |
| }else { |
| InstancePtr->CurrByteCount = ByteCount; |
| XIicPs_WriteReg(BaseAddr, (u32)(XIICPS_TRANS_SIZE_OFFSET), |
| (u32)ByteCount); |
| InstancePtr->UpdateTxSize = 0; |
| } |
| |
| XIicPs_EnableInterrupts(BaseAddr, |
| (u32)XIICPS_IXR_NACK_MASK | (u32)XIICPS_IXR_DATA_MASK | |
| (u32)XIICPS_IXR_RX_OVR_MASK | (u32)XIICPS_IXR_COMP_MASK | |
| (u32)XIICPS_IXR_ARB_LOST_MASK); |
| /* |
| * Do the address transfer to signal the slave. |
| */ |
| XIicPs_WriteReg(BaseAddr, XIICPS_ADDR_OFFSET, (u32)SlaveAddr); |
| |
| } |
| |
| /*****************************************************************************/ |
| /** |
| * This function initiates a polled mode send in master mode. |
| * |
| * It sends data to the FIFO and waits for the slave to pick them up. |
| * If slave fails to remove data from FIFO, the send fails with |
| * time out. |
| * |
| * @param InstancePtr is a pointer to the XIicPs instance. |
| * @param MsgPtr is the pointer to the send buffer. |
| * @param ByteCount is the number of bytes to be sent. |
| * @param SlaveAddr is the address of the slave we are sending to. |
| * |
| * @return |
| * - XST_SUCCESS if everything went well. |
| * - XST_FAILURE if timed out. |
| * |
| * @note This send routine is for polled mode transfer only. |
| * |
| ****************************************************************************/ |
| s32 XIicPs_MasterSendPolled(XIicPs *InstancePtr, u8 *MsgPtr, |
| s32 ByteCount, u16 SlaveAddr) |
| { |
| u32 IntrStatusReg; |
| u32 StatusReg; |
| u32 BaseAddr; |
| u32 Intrs; |
| _Bool Value; |
| |
| /* |
| * Assert validates the input arguments. |
| */ |
| Xil_AssertNonvoid(InstancePtr != NULL); |
| Xil_AssertNonvoid(MsgPtr != NULL); |
| Xil_AssertNonvoid(InstancePtr->IsReady == (u32)XIL_COMPONENT_IS_READY); |
| Xil_AssertNonvoid((u16)XIICPS_ADDR_MASK >= SlaveAddr); |
| |
| BaseAddr = InstancePtr->Config.BaseAddress; |
| InstancePtr->SendBufferPtr = MsgPtr; |
| InstancePtr->SendByteCount = ByteCount; |
| |
| if (((InstancePtr->IsRepeatedStart) != 0) || |
| ((ByteCount > XIICPS_FIFO_DEPTH) != 0U)) { |
| XIicPs_WriteReg(BaseAddr, XIICPS_CR_OFFSET, |
| XIicPs_ReadReg(BaseAddr, (u32)XIICPS_CR_OFFSET) | |
| (u32)XIICPS_CR_HOLD_MASK); |
| } |
| |
| (void)XIicPs_SetupMaster(InstancePtr, SENDING_ROLE); |
| |
| /* |
| * Intrs keeps all the error-related interrupts. |
| */ |
| Intrs = (u32)XIICPS_IXR_ARB_LOST_MASK | (u32)XIICPS_IXR_TX_OVR_MASK | |
| (u32)XIICPS_IXR_NACK_MASK; |
| |
| /* |
| * Clear the interrupt status register before use it to monitor. |
| */ |
| IntrStatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_ISR_OFFSET); |
| XIicPs_WriteReg(BaseAddr, XIICPS_ISR_OFFSET, IntrStatusReg); |
| |
| /* |
| * Transmit first FIFO full of data. |
| */ |
| (void)TransmitFifoFill(InstancePtr); |
| |
| XIicPs_WriteReg(BaseAddr, XIICPS_ADDR_OFFSET, (u32)SlaveAddr); |
| |
| IntrStatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_ISR_OFFSET); |
| |
| /* |
| * Continue sending as long as there is more data and |
| * there are no errors. |
| */ |
| Value = ((InstancePtr->SendByteCount > (s32)0) && |
| ((IntrStatusReg & Intrs) == (u32)0U)); |
| while (Value != FALSE) { |
| StatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_SR_OFFSET); |
| |
| /* |
| * Wait until transmit FIFO is empty. |
| */ |
| if ((StatusReg & XIICPS_SR_TXDV_MASK) != 0U) { |
| IntrStatusReg = XIicPs_ReadReg(BaseAddr, |
| XIICPS_ISR_OFFSET); |
| Value = ((InstancePtr->SendByteCount > (s32)0) && |
| ((IntrStatusReg & Intrs) == (u32)0U)); |
| continue; |
| } |
| |
| /* |
| * Send more data out through transmit FIFO. |
| */ |
| (void)TransmitFifoFill(InstancePtr); |
| Value = ((InstancePtr->SendByteCount > (s32)0) && |
| ((IntrStatusReg & Intrs) == (u32)0U)); |
| } |
| |
| /* |
| * Check for completion of transfer. |
| */ |
| while ((IntrStatusReg & XIICPS_IXR_COMP_MASK) != XIICPS_IXR_COMP_MASK){ |
| |
| IntrStatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_ISR_OFFSET); |
| /* |
| * If there is an error, tell the caller. |
| */ |
| if ((IntrStatusReg & Intrs) != 0U) { |
| return (s32)XST_FAILURE; |
| } |
| } |
| |
| if ((!(InstancePtr->IsRepeatedStart)) != 0) { |
| XIicPs_WriteReg(BaseAddr, XIICPS_CR_OFFSET, |
| XIicPs_ReadReg(BaseAddr,XIICPS_CR_OFFSET) & |
| (~XIICPS_CR_HOLD_MASK)); |
| } |
| |
| return (s32)XST_SUCCESS; |
| } |
| |
| /*****************************************************************************/ |
| /** |
| * This function initiates a polled mode receive in master mode. |
| * |
| * It repeatedly sets the transfer size register so the slave can |
| * send data to us. It polls the data register for data to come in. |
| * If slave fails to send us data, it fails with time out. |
| * |
| * @param InstancePtr is a pointer to the XIicPs instance. |
| * @param MsgPtr is the pointer to the receive buffer. |
| * @param ByteCount is the number of bytes to be received. |
| * @param SlaveAddr is the address of the slave we are receiving from. |
| * |
| * @return |
| * - XST_SUCCESS if everything went well. |
| * - XST_FAILURE if timed out. |
| * |
| * @note This receive routine is for polled mode transfer only. |
| * |
| ****************************************************************************/ |
| s32 XIicPs_MasterRecvPolled(XIicPs *InstancePtr, u8 *MsgPtr, |
| s32 ByteCount, u16 SlaveAddr) |
| { |
| u32 IntrStatusReg; |
| u32 Intrs; |
| u32 StatusReg; |
| u32 BaseAddr; |
| s32 Result; |
| s32 IsHold; |
| s32 UpdateTxSize = 0; |
| s32 ByteCountVar = ByteCount; |
| u32 Platform; |
| |
| /* |
| * Assert validates the input arguments. |
| */ |
| Xil_AssertNonvoid(InstancePtr != NULL); |
| Xil_AssertNonvoid(MsgPtr != NULL); |
| Xil_AssertNonvoid(InstancePtr->IsReady == (u32)XIL_COMPONENT_IS_READY); |
| Xil_AssertNonvoid(XIICPS_ADDR_MASK >= SlaveAddr); |
| |
| BaseAddr = InstancePtr->Config.BaseAddress; |
| InstancePtr->RecvBufferPtr = MsgPtr; |
| InstancePtr->RecvByteCount = ByteCountVar; |
| |
| Platform = XGetPlatform_Info(); |
| |
| if((ByteCountVar > XIICPS_FIFO_DEPTH) || |
| ((InstancePtr->IsRepeatedStart) !=0)) |
| { |
| XIicPs_WriteReg(BaseAddr, XIICPS_CR_OFFSET, |
| XIicPs_ReadReg(BaseAddr, (u32)XIICPS_CR_OFFSET) | |
| (u32)XIICPS_CR_HOLD_MASK); |
| IsHold = 1; |
| } else { |
| IsHold = 0; |
| } |
| |
| (void)XIicPs_SetupMaster(InstancePtr, RECVING_ROLE); |
| |
| /* |
| * Clear the interrupt status register before use it to monitor. |
| */ |
| IntrStatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_ISR_OFFSET); |
| XIicPs_WriteReg(BaseAddr, XIICPS_ISR_OFFSET, IntrStatusReg); |
| |
| XIicPs_WriteReg(BaseAddr, XIICPS_ADDR_OFFSET, SlaveAddr); |
| |
| /* |
| * Set up the transfer size register so the slave knows how much |
| * to send to us. |
| */ |
| if (ByteCountVar > (s32)XIICPS_MAX_TRANSFER_SIZE) { |
| XIicPs_WriteReg(BaseAddr, XIICPS_TRANS_SIZE_OFFSET, |
| XIICPS_MAX_TRANSFER_SIZE); |
| ByteCountVar = (s32)XIICPS_MAX_TRANSFER_SIZE; |
| UpdateTxSize = 1; |
| }else { |
| XIicPs_WriteReg(BaseAddr, XIICPS_TRANS_SIZE_OFFSET, |
| ByteCountVar); |
| } |
| |
| /* |
| * Intrs keeps all the error-related interrupts. |
| */ |
| Intrs = (u32)XIICPS_IXR_ARB_LOST_MASK | (u32)XIICPS_IXR_RX_OVR_MASK | |
| (u32)XIICPS_IXR_RX_UNF_MASK | (u32)XIICPS_IXR_NACK_MASK; |
| /* |
| * Poll the interrupt status register to find the errors. |
| */ |
| IntrStatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_ISR_OFFSET); |
| while ((InstancePtr->RecvByteCount > 0) && |
| ((IntrStatusReg & Intrs) == 0U)) { |
| StatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_SR_OFFSET); |
| |
| while ((StatusReg & XIICPS_SR_RXDV_MASK) != 0U) { |
| if (((InstancePtr->RecvByteCount < |
| XIICPS_DATA_INTR_DEPTH) != 0U) && (IsHold != 0) && |
| ((!InstancePtr->IsRepeatedStart) != 0) && |
| (UpdateTxSize == 0)) { |
| IsHold = 0; |
| XIicPs_WriteReg(BaseAddr, XIICPS_CR_OFFSET, |
| XIicPs_ReadReg(BaseAddr, |
| XIICPS_CR_OFFSET) & |
| (~XIICPS_CR_HOLD_MASK)); |
| } |
| XIicPs_RecvByte(InstancePtr); |
| ByteCountVar --; |
| |
| if (Platform == (u32)XPLAT_ZYNQ) { |
| if ((UpdateTxSize != 0) && |
| (ByteCountVar == (XIICPS_FIFO_DEPTH + 1))) { |
| break; |
| } |
| } |
| |
| StatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_SR_OFFSET); |
| } |
| if (Platform == (u32)XPLAT_ZYNQ) { |
| if ((UpdateTxSize != 0) && |
| (ByteCountVar == (XIICPS_FIFO_DEPTH + 1))) { |
| /* wait while fifo is full */ |
| while (XIicPs_ReadReg(BaseAddr, |
| XIICPS_TRANS_SIZE_OFFSET) != |
| (u32)(ByteCountVar - XIICPS_FIFO_DEPTH)) { ; |
| } |
| |
| if ((InstancePtr->RecvByteCount - XIICPS_FIFO_DEPTH) > |
| (s32)XIICPS_MAX_TRANSFER_SIZE) { |
| |
| XIicPs_WriteReg(BaseAddr, |
| XIICPS_TRANS_SIZE_OFFSET, |
| XIICPS_MAX_TRANSFER_SIZE); |
| ByteCountVar = (s32)XIICPS_MAX_TRANSFER_SIZE + |
| XIICPS_FIFO_DEPTH; |
| } else { |
| XIicPs_WriteReg(BaseAddr, |
| XIICPS_TRANS_SIZE_OFFSET, |
| InstancePtr->RecvByteCount - |
| XIICPS_FIFO_DEPTH); |
| UpdateTxSize = 0; |
| ByteCountVar = InstancePtr->RecvByteCount; |
| } |
| } |
| } else { |
| if ((InstancePtr->RecvByteCount > 0) && (ByteCountVar == 0)) { |
| /* |
| * Clear the interrupt status register before use it to |
| * monitor. |
| */ |
| IntrStatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_ISR_OFFSET); |
| XIicPs_WriteReg(BaseAddr, XIICPS_ISR_OFFSET, IntrStatusReg); |
| |
| XIicPs_WriteReg(BaseAddr, XIICPS_ADDR_OFFSET, SlaveAddr); |
| |
| if ((InstancePtr->RecvByteCount) > |
| (s32)XIICPS_MAX_TRANSFER_SIZE) { |
| |
| XIicPs_WriteReg(BaseAddr, |
| XIICPS_TRANS_SIZE_OFFSET, |
| XIICPS_MAX_TRANSFER_SIZE); |
| ByteCountVar = (s32)XIICPS_MAX_TRANSFER_SIZE; |
| } else { |
| XIicPs_WriteReg(BaseAddr, |
| XIICPS_TRANS_SIZE_OFFSET, |
| InstancePtr->RecvByteCount); |
| UpdateTxSize = 0; |
| ByteCountVar = InstancePtr->RecvByteCount; |
| } |
| } |
| } |
| |
| IntrStatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_ISR_OFFSET); |
| } |
| |
| if ((!(InstancePtr->IsRepeatedStart)) != 0) { |
| XIicPs_WriteReg(BaseAddr, XIICPS_CR_OFFSET, |
| XIicPs_ReadReg(BaseAddr,XIICPS_CR_OFFSET) & |
| (~XIICPS_CR_HOLD_MASK)); |
| } |
| if ((IntrStatusReg & Intrs) != 0x0U) { |
| Result = (s32)XST_FAILURE; |
| } |
| else { |
| Result = (s32)XST_SUCCESS; |
| } |
| |
| return Result; |
| } |
| |
| /*****************************************************************************/ |
| /** |
| * This function enables the slave monitor mode. |
| * |
| * It enables slave monitor in the control register and enables |
| * slave ready interrupt. It then does an address transfer to slave. |
| * Interrupt handler will signal the caller if slave responds to |
| * the address transfer. |
| * |
| * @param InstancePtr is a pointer to the XIicPs instance. |
| * @param SlaveAddr is the address of the slave we want to contact. |
| * |
| * @return None. |
| * |
| * @note None. |
| * |
| ****************************************************************************/ |
| void XIicPs_EnableSlaveMonitor(XIicPs *InstancePtr, u16 SlaveAddr) |
| { |
| u32 BaseAddr; |
| u32 ConfigReg; |
| |
| Xil_AssertVoid(InstancePtr != NULL); |
| |
| BaseAddr = InstancePtr->Config.BaseAddress; |
| |
| /* Clear transfer size register */ |
| XIicPs_WriteReg(BaseAddr, (u32)XIICPS_TRANS_SIZE_OFFSET, 0x0U); |
| |
| /* |
| * Enable slave monitor mode in control register. |
| */ |
| ConfigReg = XIicPs_ReadReg(BaseAddr, (u32)XIICPS_CR_OFFSET); |
| ConfigReg |= (u32)XIICPS_CR_MS_MASK | (u32)XIICPS_CR_NEA_MASK | |
| (u32)XIICPS_CR_CLR_FIFO_MASK | (u32)XIICPS_CR_SLVMON_MASK; |
| ConfigReg &= (u32)(~XIICPS_CR_RD_WR_MASK); |
| |
| XIicPs_WriteReg(BaseAddr, (u32)XIICPS_CR_OFFSET, ConfigReg); |
| |
| /* |
| * Set up interrupt flag for slave monitor interrupt. |
| * Dont enable NACK. |
| */ |
| XIicPs_EnableInterrupts(BaseAddr, (u32)XIICPS_IXR_SLV_RDY_MASK); |
| |
| /* |
| * Initialize the slave monitor register. |
| */ |
| XIicPs_WriteReg(BaseAddr, (u32)XIICPS_SLV_PAUSE_OFFSET, 0xFU); |
| |
| /* |
| * Set the slave address to start the slave address transmission. |
| */ |
| XIicPs_WriteReg(BaseAddr, (u32)XIICPS_ADDR_OFFSET, (u32)SlaveAddr); |
| |
| return; |
| } |
| |
| /*****************************************************************************/ |
| /** |
| * This function disables slave monitor mode. |
| * |
| * @param InstancePtr is a pointer to the XIicPs instance. |
| * |
| * @return None. |
| * |
| * @note None. |
| * |
| ****************************************************************************/ |
| void XIicPs_DisableSlaveMonitor(XIicPs *InstancePtr) |
| { |
| u32 BaseAddr; |
| |
| Xil_AssertVoid(InstancePtr != NULL); |
| |
| BaseAddr = InstancePtr->Config.BaseAddress; |
| |
| /* |
| * Clear slave monitor control bit. |
| */ |
| XIicPs_WriteReg(BaseAddr, XIICPS_CR_OFFSET, |
| XIicPs_ReadReg(BaseAddr, XIICPS_CR_OFFSET) |
| & (~XIICPS_CR_SLVMON_MASK)); |
| |
| /* |
| * Clear interrupt flag for slave monitor interrupt. |
| */ |
| XIicPs_DisableInterrupts(BaseAddr, XIICPS_IXR_SLV_RDY_MASK); |
| |
| return; |
| } |
| |
| /*****************************************************************************/ |
| /** |
| * The interrupt handler for the master mode. It does the protocol handling for |
| * the interrupt-driven transfers. |
| * |
| * Completion events and errors are signaled to upper layer for proper handling. |
| * |
| * <pre> |
| * The interrupts that are handled are: |
| * - DATA |
| * This case is handled only for master receive data. |
| * The master has to request for more data (if there is more data to |
| * receive) and read the data from the FIFO . |
| * |
| * - COMP |
| * If the Master is transmitting data and there is more data to be |
| * sent then the data is written to the FIFO. If there is no more data to |
| * be transmitted then a completion event is signalled to the upper layer |
| * by calling the callback handler. |
| * |
| * If the Master is receiving data then the data is read from the FIFO and |
| * the Master has to request for more data (if there is more data to |
| * receive). If all the data has been received then a completion event |
| * is signalled to the upper layer by calling the callback handler. |
| * It is an error if the amount of received data is more than expected. |
| * |
| * - NAK and SLAVE_RDY |
| * This is signalled to the upper layer by calling the callback handler. |
| * |
| * - All Other interrupts |
| * These interrupts are marked as error. This is signalled to the upper |
| * layer by calling the callback handler. |
| * |
| * </pre> |
| * |
| * @param InstancePtr is a pointer to the XIicPs instance. |
| * |
| * @return None. |
| * |
| * @note None. |
| * |
| ****************************************************************************/ |
| void XIicPs_MasterInterruptHandler(XIicPs *InstancePtr) |
| { |
| u32 IntrStatusReg; |
| u32 StatusEvent = 0U; |
| u32 BaseAddr; |
| u16 SlaveAddr; |
| s32 ByteCnt; |
| s32 IsHold; |
| u32 Platform; |
| |
| /* |
| * Assert validates the input arguments. |
| */ |
| Xil_AssertVoid(InstancePtr != NULL); |
| Xil_AssertVoid(InstancePtr->IsReady == (u32)XIL_COMPONENT_IS_READY); |
| |
| BaseAddr = InstancePtr->Config.BaseAddress; |
| |
| Platform = XGetPlatform_Info(); |
| |
| /* |
| * Read the Interrupt status register. |
| */ |
| IntrStatusReg = XIicPs_ReadReg(BaseAddr, |
| (u32)XIICPS_ISR_OFFSET); |
| |
| /* |
| * Write the status back to clear the interrupts so no events are |
| * missed while processing this interrupt. |
| */ |
| XIicPs_WriteReg(BaseAddr, (u32)XIICPS_ISR_OFFSET, IntrStatusReg); |
| |
| /* |
| * Use the Mask register AND with the Interrupt Status register so |
| * disabled interrupts are not processed. |
| */ |
| IntrStatusReg &= ~(XIicPs_ReadReg(BaseAddr, (u32)XIICPS_IMR_OFFSET)); |
| |
| ByteCnt = InstancePtr->CurrByteCount; |
| |
| IsHold = 0; |
| if ((XIicPs_ReadReg(BaseAddr, (u32)XIICPS_CR_OFFSET) & (u32)XIICPS_CR_HOLD_MASK) != 0U) { |
| IsHold = 1; |
| } |
| |
| /* |
| * Send |
| */ |
| if (((InstancePtr->IsSend) != 0) && |
| ((u32)0U != (IntrStatusReg & (u32)XIICPS_IXR_COMP_MASK))) { |
| if (InstancePtr->SendByteCount > 0) { |
| MasterSendData(InstancePtr); |
| } else { |
| StatusEvent |= XIICPS_EVENT_COMPLETE_SEND; |
| } |
| } |
| |
| |
| /* |
| * Receive |
| */ |
| if (((!(InstancePtr->IsSend))!= 0) && |
| ((0 != (IntrStatusReg & (u32)XIICPS_IXR_DATA_MASK)) || |
| (0 != (IntrStatusReg & (u32)XIICPS_IXR_COMP_MASK)))){ |
| |
| while ((XIicPs_ReadReg(BaseAddr, (u32)XIICPS_SR_OFFSET) & |
| XIICPS_SR_RXDV_MASK) != 0U) { |
| if (((InstancePtr->RecvByteCount < |
| XIICPS_DATA_INTR_DEPTH)!= 0U) && (IsHold != 0) && |
| ((!InstancePtr->IsRepeatedStart)!= 0) && |
| (InstancePtr->UpdateTxSize == 0)) { |
| IsHold = 0; |
| XIicPs_WriteReg(BaseAddr, XIICPS_CR_OFFSET, |
| XIicPs_ReadReg(BaseAddr, |
| XIICPS_CR_OFFSET) & |
| (~XIICPS_CR_HOLD_MASK)); |
| } |
| XIicPs_RecvByte(InstancePtr); |
| ByteCnt--; |
| |
| if (Platform == (u32)XPLAT_ZYNQ) { |
| if ((InstancePtr->UpdateTxSize != 0) && |
| (ByteCnt == (XIICPS_FIFO_DEPTH + 1))) { |
| break; |
| } |
| } |
| } |
| |
| if (Platform == (u32)XPLAT_ZYNQ) { |
| if ((InstancePtr->UpdateTxSize != 0) && |
| (ByteCnt == (XIICPS_FIFO_DEPTH + 1))) { |
| /* wait while fifo is full */ |
| while (XIicPs_ReadReg(BaseAddr, |
| XIICPS_TRANS_SIZE_OFFSET) != |
| (u32)(ByteCnt - XIICPS_FIFO_DEPTH)) { |
| } |
| |
| if ((InstancePtr->RecvByteCount - XIICPS_FIFO_DEPTH) > |
| (s32)XIICPS_MAX_TRANSFER_SIZE) { |
| |
| XIicPs_WriteReg(BaseAddr, |
| XIICPS_TRANS_SIZE_OFFSET, |
| XIICPS_MAX_TRANSFER_SIZE); |
| ByteCnt = (s32)XIICPS_MAX_TRANSFER_SIZE + |
| XIICPS_FIFO_DEPTH; |
| } else { |
| XIicPs_WriteReg(BaseAddr, |
| XIICPS_TRANS_SIZE_OFFSET, |
| InstancePtr->RecvByteCount - |
| XIICPS_FIFO_DEPTH); |
| InstancePtr->UpdateTxSize = 0; |
| ByteCnt = InstancePtr->RecvByteCount; |
| } |
| } |
| } else { |
| if ((InstancePtr->RecvByteCount > 0) && (ByteCnt == 0)) { |
| /* |
| * Clear the interrupt status register before use it to |
| * monitor. |
| */ |
| IntrStatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_ISR_OFFSET); |
| XIicPs_WriteReg(BaseAddr, XIICPS_ISR_OFFSET, IntrStatusReg); |
| |
| SlaveAddr = (u16)XIicPs_ReadReg(BaseAddr, (u32)XIICPS_ADDR_OFFSET); |
| XIicPs_WriteReg(BaseAddr, XIICPS_ADDR_OFFSET, SlaveAddr); |
| |
| if ((InstancePtr->RecvByteCount) > |
| (s32)XIICPS_MAX_TRANSFER_SIZE) { |
| |
| XIicPs_WriteReg(BaseAddr, |
| XIICPS_TRANS_SIZE_OFFSET, |
| XIICPS_MAX_TRANSFER_SIZE); |
| ByteCnt = (s32)XIICPS_MAX_TRANSFER_SIZE; |
| } else { |
| XIicPs_WriteReg(BaseAddr, |
| XIICPS_TRANS_SIZE_OFFSET, |
| InstancePtr->RecvByteCount); |
| InstancePtr->UpdateTxSize = 0; |
| ByteCnt = InstancePtr->RecvByteCount; |
| } |
| XIicPs_EnableInterrupts(BaseAddr, |
| (u32)XIICPS_IXR_NACK_MASK | (u32)XIICPS_IXR_DATA_MASK | |
| (u32)XIICPS_IXR_RX_OVR_MASK | (u32)XIICPS_IXR_COMP_MASK | |
| (u32)XIICPS_IXR_ARB_LOST_MASK); |
| } |
| } |
| InstancePtr->CurrByteCount = ByteCnt; |
| } |
| |
| if (((!(InstancePtr->IsSend)) != 0) && |
| (0U != (IntrStatusReg & XIICPS_IXR_COMP_MASK))) { |
| /* |
| * If all done, tell the application. |
| */ |
| if (InstancePtr->RecvByteCount == 0){ |
| if ((!(InstancePtr->IsRepeatedStart)) != 0) { |
| XIicPs_WriteReg(BaseAddr, XIICPS_CR_OFFSET, |
| XIicPs_ReadReg(BaseAddr, |
| XIICPS_CR_OFFSET) & |
| (~XIICPS_CR_HOLD_MASK)); |
| } |
| StatusEvent |= XIICPS_EVENT_COMPLETE_RECV; |
| } |
| } |
| |
| |
| /* |
| * Slave ready interrupt, it is only meaningful for master mode. |
| */ |
| if (0U != (IntrStatusReg & XIICPS_IXR_SLV_RDY_MASK)) { |
| StatusEvent |= XIICPS_EVENT_SLAVE_RDY; |
| } |
| |
| if (0U != (IntrStatusReg & XIICPS_IXR_NACK_MASK)) { |
| if ((!(InstancePtr->IsRepeatedStart)) != 0 ) { |
| XIicPs_WriteReg(BaseAddr, XIICPS_CR_OFFSET, |
| XIicPs_ReadReg(BaseAddr, |
| XIICPS_CR_OFFSET) & |
| (~XIICPS_CR_HOLD_MASK)); |
| } |
| StatusEvent |= XIICPS_EVENT_NACK; |
| } |
| |
| /* |
| * Arbitration lost interrupt |
| */ |
| if (0U != (IntrStatusReg & XIICPS_IXR_ARB_LOST_MASK)) { |
| StatusEvent |= XIICPS_EVENT_ARB_LOST; |
| } |
| |
| /* |
| * All other interrupts are treated as error. |
| */ |
| if (0U != (IntrStatusReg & (XIICPS_IXR_NACK_MASK | |
| XIICPS_IXR_RX_UNF_MASK | XIICPS_IXR_TX_OVR_MASK | |
| XIICPS_IXR_RX_OVR_MASK))) { |
| if ((!(InstancePtr->IsRepeatedStart)) != 0) { |
| XIicPs_WriteReg(BaseAddr, XIICPS_CR_OFFSET, |
| XIicPs_ReadReg(BaseAddr, |
| XIICPS_CR_OFFSET) & |
| (~XIICPS_CR_HOLD_MASK)); |
| } |
| StatusEvent |= XIICPS_EVENT_ERROR; |
| } |
| |
| /* |
| * Signal application if there are any events. |
| */ |
| if (StatusEvent != 0U) { |
| InstancePtr->StatusHandler(InstancePtr->CallBackRef, |
| StatusEvent); |
| } |
| |
| } |
| |
| /*****************************************************************************/ |
| /* |
| * This function prepares a device to transfers as a master. |
| * |
| * @param InstancePtr is a pointer to the XIicPs instance. |
| * |
| * @param Role specifies whether the device is sending or receiving. |
| * |
| * @return |
| * - XST_SUCCESS if everything went well. |
| * - XST_FAILURE if bus is busy. |
| * |
| * @note Interrupts are always disabled, device which needs to use |
| * interrupts needs to setup interrupts after this call. |
| * |
| ****************************************************************************/ |
| static s32 XIicPs_SetupMaster(XIicPs *InstancePtr, s32 Role) |
| { |
| u32 ControlReg; |
| u32 BaseAddr; |
| |
| Xil_AssertNonvoid(InstancePtr != NULL); |
| |
| BaseAddr = InstancePtr->Config.BaseAddress; |
| ControlReg = XIicPs_ReadReg(BaseAddr, XIICPS_CR_OFFSET); |
| |
| |
| /* |
| * Only check if bus is busy when repeated start option is not set. |
| */ |
| if ((ControlReg & XIICPS_CR_HOLD_MASK) == 0U) { |
| if (XIicPs_BusIsBusy(InstancePtr) == (s32)1) { |
| return (s32)XST_FAILURE; |
| } |
| } |
| |
| /* |
| * Set up master, AckEn, nea and also clear fifo. |
| */ |
| ControlReg |= (u32)XIICPS_CR_ACKEN_MASK | (u32)XIICPS_CR_CLR_FIFO_MASK | |
| (u32)XIICPS_CR_NEA_MASK | (u32)XIICPS_CR_MS_MASK; |
| |
| if (Role == RECVING_ROLE) { |
| ControlReg |= (u32)XIICPS_CR_RD_WR_MASK; |
| }else { |
| ControlReg &= (u32)(~XIICPS_CR_RD_WR_MASK); |
| } |
| |
| XIicPs_WriteReg(BaseAddr, XIICPS_CR_OFFSET, ControlReg); |
| |
| XIicPs_DisableAllInterrupts(BaseAddr); |
| |
| return (s32)XST_SUCCESS; |
| } |
| |
| /*****************************************************************************/ |
| /* |
| * This function handles continuation of sending data. It is invoked |
| * from interrupt handler. |
| * |
| * @param InstancePtr is a pointer to the XIicPs instance. |
| * |
| * @return None. |
| * |
| * @note None. |
| * |
| ****************************************************************************/ |
| static void MasterSendData(XIicPs *InstancePtr) |
| { |
| (void)TransmitFifoFill(InstancePtr); |
| |
| /* |
| * Clear hold bit if done, so stop can be sent out. |
| */ |
| if (InstancePtr->SendByteCount == 0) { |
| |
| /* |
| * If user has enabled repeated start as an option, |
| * do not disable it. |
| */ |
| if ((!(InstancePtr->IsRepeatedStart)) != 0) { |
| |
| XIicPs_WriteReg(InstancePtr->Config.BaseAddress, |
| (u32)XIICPS_CR_OFFSET, |
| XIicPs_ReadReg(InstancePtr->Config.BaseAddress, |
| (u32)XIICPS_CR_OFFSET) & (u32)(~ XIICPS_CR_HOLD_MASK)); |
| } |
| } |
| |
| return; |
| } |
| /** @} */ |