/** * * Runs a limited self-test on the driver/device. This test does a read/write * test of the Interrupt Registers There is no loopback capabilities for the * device such that this test does not send or receive data. * * @param InstancePtr is a pointer to the XIic instance to be worked on. * * @return * - XST_SUCCESS if no errors are found * - XST_FAILURE if errors are found * * @note None. * ****************************************************************************/ int XIic_SelfTest(XIic *InstancePtr) { int Status = XST_SUCCESS; int GlobalIntrStatus; u32 IntrEnableStatus; Xil_AssertNonvoid(InstancePtr != NULL); Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY); /* * Store the Global Interrupt Register and the Interrupt Enable Register * contents. */ GlobalIntrStatus = XIic_IsIntrGlobalEnabled(InstancePtr->BaseAddress); IntrEnableStatus = XIic_ReadIier(InstancePtr->BaseAddress); /* * Reset the device so it's in a known state and the default state of * the interrupt registers can be tested. */ XIic_Reset(InstancePtr); if (XIic_IsIntrGlobalEnabled(InstancePtr->BaseAddress)!= 0) { Status = XST_FAILURE; } if (XIic_ReadIier(InstancePtr->BaseAddress)!= 0) { Status = XST_FAILURE; } /* * Test Read/Write to the Interrupt Enable register. */ XIic_WriteIier(InstancePtr->BaseAddress, XIIC_TX_RX_INTERRUPTS); if (XIic_ReadIier(InstancePtr->BaseAddress)!= XIIC_TX_RX_INTERRUPTS) { Status = XST_FAILURE; } /* * Reset device to remove the affects of the previous test. */ XIic_Reset(InstancePtr); /* * Restore the Global Interrupt Register and the Interrupt Enable * Register contents. */ if (GlobalIntrStatus == TRUE) { XIic_IntrGlobalEnable(InstancePtr->BaseAddress); } XIic_WriteIier(InstancePtr->BaseAddress, IntrEnableStatus); return Status; }
/** * * The IIC bus busy signals when a master has control of the bus. Until the bus * is released, i.e. not busy, other devices must wait to use it. * * When this interrupt occurs, it signals that the previous master has released * the bus for another user. * * This interrupt is only enabled when the master Tx is waiting for the bus. * * This interrupt causes the following tasks: * - Disable Bus not busy interrupt * - Enable bus Ack * Should the slave receive have disabled acknowledgement, enable to allow * acknowledgment of the sending of our address to again be addresed as * slave * - Flush Rx FIFO * Should the slave receive have disabled acknowledgement, a few bytes may * be in FIFO if Rx full did not occur because of not enough byte in FIFO * to have caused an interrupt. * - Send status to user via status callback with the value: * XII_BUS_NOT_BUSY_EVENT * * @param InstancePtr is a pointer to the XIic instance to be worked on. * * @return None. * * @note None. * ******************************************************************************/ static void BusNotBusyHandler(XIic *InstancePtr) { u32 Status; u32 CntlReg; /* * Should the slave receive have disabled acknowledgement, * enable to allow acknowledgment of the sending of our address to * again be addresed as slave. */ CntlReg = XIic_ReadReg(InstancePtr->BaseAddress, XIIC_CR_REG_OFFSET); XIic_WriteReg(InstancePtr->BaseAddress, XIIC_CR_REG_OFFSET, (CntlReg & ~XIIC_CR_NO_ACK_MASK)); /* * Flush Tx FIFO by toggling TxFIFOResetBit. FIFO runs normally at 0 * Do this incase needed to Tx FIFO with more than expected if what * was set to Tx was less than what the Master expected - read more * from this slave so FIFO had junk in it. */ XIic_FlushTxFifo(InstancePtr); /* * Flush Rx FIFO should slave Rx had a problem, sent No ack but * still received a few bytes. Should the slave receive have disabled * acknowledgement, clear Rx FIFO. */ XIic_FlushRxFifo(InstancePtr); /* * Send Application messaging status via callbacks. Disable either Tx or * Receive interrupt. Which callback depends on messaging direction. */ Status = XIic_ReadIier(InstancePtr->BaseAddress); if (InstancePtr->RecvBufferPtr == NULL) { /* * Slave was sending data (master was reading), disable * all the transmit interrupts. */ XIic_WriteIier(InstancePtr->BaseAddress, (Status & ~XIIC_TX_INTERRUPTS)); } else { /* * Slave was receiving data (master was writing) disable receive * interrupts. */ XIic_WriteIier(InstancePtr->BaseAddress, (Status & ~XIIC_INTR_RX_FULL_MASK)); } /* * Send Status in StatusHandler callback. */ InstancePtr->StatusHandler(InstancePtr->StatusCallBackRef, XII_BUS_NOT_BUSY_EVENT); }
/** * * This function is the interrupt handler for the XIic driver. This function * should be connected to the interrupt system. * * Only one interrupt source is handled for each interrupt allowing * higher priority system interrupts quicker response time. * * @param InstancePtr is a pointer to the XIic instance to be worked on. * * @return None. * * @internal * * The XIIC_INTR_ARB_LOST_MASK and XIIC_INTR_TX_ERROR_MASK interrupts must have * higher priority than the other device interrupts so that the IIC device does * not get into a potentially confused state. The remaining interrupts may be * rearranged with no harm. * ******************************************************************************/ void XIic_InterruptHandler(void *InstancePtr) { u32 Status; u32 IntrStatus; u32 IntrPending; u32 IntrEnable; XIic *IicPtr = NULL; u32 Clear = 0; /* * Verify that each of the inputs are valid. */ Xil_AssertVoid(InstancePtr != NULL); /* * Convert the non-typed pointer to an IIC instance pointer */ IicPtr = (XIic *) InstancePtr; /* * Get the interrupt Status. */ IntrPending = XIic_ReadIisr(IicPtr->BaseAddress); IntrEnable = XIic_ReadIier(IicPtr->BaseAddress); IntrStatus = IntrPending & IntrEnable; /* * Do not processes a devices interrupts if the device has no * interrupts pending or the global interrupts have been disabled. */ if ((IntrStatus == 0) || (XIic_IsIntrGlobalEnabled(IicPtr->BaseAddress) == FALSE)) { return; } /* * Update interrupt stats and get the contents of the status register. */ IicPtr->Stats.IicInterrupts++; Status = XIic_ReadReg(IicPtr->BaseAddress, XIIC_SR_REG_OFFSET); /* * Service requesting interrupt. */ if (IntrStatus & XIIC_INTR_ARB_LOST_MASK) { /* Bus Arbritration Lost */ IicPtr->Stats.ArbitrationLost++; XIic_ArbLostFuncPtr(IicPtr); Clear = XIIC_INTR_ARB_LOST_MASK; } else if (IntrStatus & XIIC_INTR_TX_ERROR_MASK) { /* Transmit errors (no acknowledge) received */ IicPtr->Stats.TxErrors++; TxErrorHandler(IicPtr); Clear = XIIC_INTR_TX_ERROR_MASK; } else if (IntrStatus & XIIC_INTR_NAAS_MASK) { /* Not Addressed As Slave */ XIic_NotAddrAsSlaveFuncPtr(IicPtr); Clear = XIIC_INTR_NAAS_MASK; } else if (IntrStatus & XIIC_INTR_RX_FULL_MASK) { /* Receive register/FIFO is full */ IicPtr->Stats.RecvInterrupts++; if (Status & XIIC_SR_ADDR_AS_SLAVE_MASK) { XIic_RecvSlaveFuncPtr(IicPtr); } else { XIic_RecvMasterFuncPtr(IicPtr); } Clear = XIIC_INTR_RX_FULL_MASK; } else if (IntrStatus & XIIC_INTR_AAS_MASK) { /* Addressed As Slave */ XIic_AddrAsSlaveFuncPtr(IicPtr); Clear = XIIC_INTR_AAS_MASK; } else if (IntrStatus & XIIC_INTR_BNB_MASK) { /* IIC bus has transitioned to not busy */ /* Check if send callback needs to run */ if (IicPtr->BNBOnly == TRUE) { XIic_BusNotBusyFuncPtr(IicPtr); IicPtr->BNBOnly = FALSE; } else { IicPtr->SendHandler(IicPtr->SendCallBackRef, 0); } Clear = XIIC_INTR_BNB_MASK; /* The bus is not busy, disable BusNotBusy interrupt */ XIic_DisableIntr(IicPtr->BaseAddress, XIIC_INTR_BNB_MASK); } else if ((IntrStatus & XIIC_INTR_TX_EMPTY_MASK) || (IntrStatus & XIIC_INTR_TX_HALF_MASK)) { /* Transmit register/FIFO is empty or � empty */ IicPtr->Stats.SendInterrupts++; if (Status & XIIC_SR_ADDR_AS_SLAVE_MASK) { XIic_SendSlaveFuncPtr(IicPtr); } else { XIic_SendMasterFuncPtr(IicPtr); } IntrStatus = XIic_ReadIisr(IicPtr->BaseAddress); Clear = IntrStatus & (XIIC_INTR_TX_EMPTY_MASK | XIIC_INTR_TX_HALF_MASK); } /* * Clear Interrupts. */ XIic_WriteIisr(IicPtr->BaseAddress, Clear); }
/** * * This function is called when the IIC device receives Not Addressed As Slave * (NAAS) interrupt which indicates that the master has released the bus implying * a data transfer is complete. * * @param InstancePtr is a pointer to the XIic instance to be worked on. * * @return None. * * @note None. * ******************************************************************************/ static void NotAddrAsSlaveHandler(XIic *InstancePtr) { u32 Status; u32 CntlReg; u8 BytesToRead; u8 LoopCnt; /* * Disable NAAS so that the condition will not continue to interrupt * and enable the addressed as slave interrupt to know when a master * selects a slave on the bus. */ XIic_DisableIntr(InstancePtr->BaseAddress, XIIC_INTR_NAAS_MASK); XIic_ClearEnableIntr(InstancePtr->BaseAddress, XIIC_INTR_AAS_MASK); /* * Flush Tx FIFO by toggling TxFIFOResetBit. FIFO runs normally at 0 * Do this incase needed to Tx FIFO with more than expected if what * was set to Tx was less than what the Master expected - read more * from this slave so FIFO had junk in it. */ XIic_FlushTxFifo(InstancePtr); /* * NAAS interrupt was asserted but received data in recieve FIFO is * less than Rc_FIFO_PIRQ to assert an receive full interrupt,in this * condition as data recieved is valid we have to read data before FIFO * flush. */ Status = XIic_ReadReg(InstancePtr->BaseAddress, XIIC_SR_REG_OFFSET); if (!(Status & XIIC_SR_RX_FIFO_EMPTY_MASK)) { BytesToRead = (XIic_ReadReg(InstancePtr->BaseAddress, XIIC_RFO_REG_OFFSET)) + 1; if (InstancePtr->RecvByteCount > BytesToRead) { for (LoopCnt = 0; LoopCnt < BytesToRead; LoopCnt++) { XIic_ReadRecvByte(InstancePtr); } } } InstancePtr->RecvByteCount = 0; /* * Flush Rx FIFO should slave Rx had a problem, sent No ack but * still received a few bytes. Should the slave receive have disabled * acknowledgement, clear Rx FIFO. */ XIic_FlushRxFifo(InstancePtr); /* * Set FIFO occupancy depth = 1 so that the first byte will throttle * next recieve msg. */ XIic_WriteReg(InstancePtr->BaseAddress, XIIC_RFD_REG_OFFSET, 0); /* * Should the slave receive have disabled acknowledgement, * enable to allow acknowledgment for receipt of our address to * again be used as a slave. */ CntlReg = XIic_ReadReg(InstancePtr->BaseAddress, XIIC_CR_REG_OFFSET); XIic_WriteReg(InstancePtr->BaseAddress, XIIC_CR_REG_OFFSET, (CntlReg & ~XIIC_CR_NO_ACK_MASK)); /* * Which callback depends on messaging direction, the buffer pointer NOT * being used indicates the direction of data transfer. */ Status = XIic_ReadIier(InstancePtr->BaseAddress); if (InstancePtr->RecvBufferPtr == NULL) { /* * Slave was sending data so disable all transmit interrupts and * call the callback handler to indicate the transfer is * complete. */ XIic_WriteIier(InstancePtr->BaseAddress, (Status & ~XIIC_TX_INTERRUPTS)); InstancePtr->SendHandler(InstancePtr->SendCallBackRef, InstancePtr->SendByteCount); } else { /* * Slave was receiving data so disable receive full interrupt * and call the callback handler to notify the transfer is * complete. */ XIic_WriteIier(InstancePtr->BaseAddress, (Status & ~XIIC_INTR_RX_FULL_MASK)); InstancePtr->RecvHandler(InstancePtr->RecvCallBackRef, InstancePtr->RecvByteCount); } return; }