/** * Receive the specified data from the device that has been previously addressed * on the IIC bus. This function assumes the following: * - The Rx Fifo occupancy depth has been set to its max. * - Upon entry, the Rx Fifo is empty. * - The 7 bit address has been sent. * - The dynamic stop and number of bytes to receive has been written to Tx * Fifo. * * @param BaseAddress contains the base address of the IIC Device. * @param BufferPtr points to the buffer to hold the data that is * received. * @param ByteCount is the number of bytes to be received. The range of * this value is greater than 0 and not higher than 255. * * @return The number of bytes remaining to be received. * * @note This function contains loops that could cause the function not * to return if the hardware is not working. * ******************************************************************************/ static unsigned DynRecvData(u32 BaseAddress, u8 *BufferPtr, u8 ByteCount) { u32 StatusReg; u32 IntrStatus; u32 IntrStatusMask; while (ByteCount > 0) { /* * Setup the mask to use for checking errors because when * receiving one byte OR the last byte of a multibyte message * an error naturally occurs when the no ack is done to tell * the slave the last byte. */ if (ByteCount == 1) { IntrStatusMask = XIIC_INTR_ARB_LOST_MASK | XIIC_INTR_BNB_MASK; } else { IntrStatusMask = XIIC_INTR_ARB_LOST_MASK | XIIC_INTR_TX_ERROR_MASK | XIIC_INTR_BNB_MASK; } /* * Wait for a byte to show up in the Rx Fifo. */ while (1) { IntrStatus = XIic_ReadIisr(BaseAddress); StatusReg = XIic_ReadReg(BaseAddress, XIIC_SR_REG_OFFSET); if ((StatusReg & XIIC_SR_RX_FIFO_EMPTY_MASK) != XIIC_SR_RX_FIFO_EMPTY_MASK) { break; } /* * Check the transmit error after the receive full * because when sending only one byte transmit error * will occur because of the no ack to indicate the end * of the data. */ if (IntrStatus & IntrStatusMask) { return ByteCount; } } /* * Read in byte from the Rx Fifo. If the Fifo reached the * programmed occupancy depth as programmed in the Rx occupancy * reg, this read access will un throttle the bus such that * the next byte is read from the IIC bus. */ *BufferPtr++ = XIic_ReadReg(BaseAddress, XIIC_DRR_REG_OFFSET); ByteCount--; } return ByteCount; }
/** * * This interrupt occurs four different ways: Two as master and two as slave. * Master: * <pre> * (1) Transmitter (IMPLIES AN ERROR) * The slave receiver did not acknowledge properly. * (2) Receiver (Implies Tx complete) * Interrupt caused by setting TxAck high in the IIC to indicate to the * the last byte has been transmitted. * </pre> * * Slave: * <pre> * (3) Transmitter (Implies Tx complete) * Interrupt caused by master device indicating last byte of the message * has been transmitted. * (4) Receiver (IMPLIES AN ERROR) * Interrupt caused by setting TxAck high in the IIC to indicate Rx * IIC had a problem - set by this device and condition already known * and interrupt is not enabled. * </pre> * * This interrupt is enabled during Master send and receive and disabled * when this device knows it is going to send a negative acknowledge (Ack = No). * * Signals user of Tx error via status callback sending: XII_TX_ERROR_EVENT * * When MasterRecv has no message to send and only receives one byte of data * from the salve device, the TxError must be enabled to catch addressing * errors, yet there is not opportunity to disable TxError when there is no * data to send allowing disabling on last byte. When the slave sends the * only byte the NOAck causes a Tx Error. To disregard this as no real error, * when there is data in the Receive FIFO/register then the error was not * a device address write error, but a NOACK read error - to be ignored. * To work with or without FIFO's, the Rx Data interrupt is used to indicate * data is in the Rx register. * * @param InstancePtr is a pointer to the XIic instance to be worked on. * * @return None. * ******************************************************************************/ static void TxErrorHandler(XIic *InstancePtr) { u32 IntrStatus; u32 CntlReg; /* * When Sending as a slave, Tx error signals end of msg. Not Addressed * As Slave will handle the callbacks. this is used to only flush * the Tx fifo. The addressed as slave bit is gone as soon as the bus * has been released such that the buffer pointers are used to determine * the direction of transfer (send or receive). */ if (InstancePtr->RecvBufferPtr == NULL) { /* * Master Receiver finished reading message. Flush Tx fifo to * remove an 0xFF that was written to prevent bus throttling, * and disable all transmit and receive interrupts. */ XIic_FlushTxFifo(InstancePtr); XIic_DisableIntr(InstancePtr->BaseAddress, XIIC_TX_RX_INTERRUPTS); /* * If operating in Master mode, call status handler to indicate * NOACK occured. */ IntrStatus = XIic_ReadIisr(InstancePtr->BaseAddress); if ((IntrStatus & XIIC_INTR_AAS_MASK) == 0) { InstancePtr->StatusHandler(InstancePtr-> StatusCallBackRef, XII_SLAVE_NO_ACK_EVENT); } else { /* Decrement the Tx Error since Tx Error interrupt * implies transmit complete while sending as Slave */ InstancePtr->Stats.TxErrors--; } return; } /* * Data in the receive register from either master or slave receive * When:slave, indicates master sent last byte, message completed. * When:master, indicates a master Receive with one byte received. When * a byte is in Rx reg then the Tx error indicates the Rx data was * recovered normally Tx errors are not enabled such that this should * not occur. */ IntrStatus = XIic_ReadIisr(InstancePtr->BaseAddress); if (IntrStatus & XIIC_INTR_RX_FULL_MASK) { /* Rx Reg/FIFO has data, Disable Tx error interrupts */ XIic_DisableIntr(InstancePtr->BaseAddress, XIIC_INTR_TX_ERROR_MASK); return; } XIic_FlushTxFifo(InstancePtr); /* * Disable and clear Tx empty, � empty, Rx Full or Tx error interrupts. */ XIic_DisableIntr(InstancePtr->BaseAddress, XIIC_TX_RX_INTERRUPTS); XIic_ClearIntr(InstancePtr->BaseAddress, XIIC_TX_RX_INTERRUPTS); /* Clear MSMS as on Tx error when Rxing, the bus will be * stopped but MSMS bit is still set. Reset to proper state */ CntlReg = XIic_ReadReg(InstancePtr->BaseAddress, XIIC_CR_REG_OFFSET); CntlReg &= ~XIIC_CR_MSMS_MASK; XIic_WriteReg(InstancePtr->BaseAddress, XIIC_CR_REG_OFFSET, CntlReg); /* * Set FIFO occupancy depth = 1 so that the first byte will throttle * next recieve msg. */ XIic_WriteReg(InstancePtr->BaseAddress, XIIC_RFD_REG_OFFSET, 0); /* * Call the event callback. */ InstancePtr->StatusHandler(InstancePtr->StatusCallBackRef, XII_SLAVE_NO_ACK_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); }
/****************************************************************************** * * Send the specified buffer to the device that has been previously addressed * on the IIC bus. This function assumes that the 7 bit address has been sent. * * @param BaseAddress contains the base address of the IIC Device. * @param BufferPtr points to the data to be sent. * @param ByteCount is the number of bytes to be sent. * @param Option: XIIC_STOP = end with STOP condition, XIIC_REPEATED_START * = don't end with STOP condition. * * @return The number of bytes remaining to be sent. * * @note This function does not take advantage of the transmit Fifo * because it is designed for minimal code space and complexity. * ******************************************************************************/ static unsigned DynSendData(u32 BaseAddress, u8 *BufferPtr, u8 ByteCount, u8 Option) { u32 IntrStatus; while (ByteCount > 0) { /* * Wait for the transmit to be empty before sending any more * data by polling the interrupt status register. */ while (1) { IntrStatus = XIic_ReadIisr(BaseAddress); if (IntrStatus & (XIIC_INTR_TX_ERROR_MASK | XIIC_INTR_ARB_LOST_MASK | XIIC_INTR_BNB_MASK)) { /* * Error condition (NACK or ARB Lost or BNB * Error Has occurred. Clear the Control * register to send a STOP condition on the Bus * and return the number of bytes still to * transmit. */ XIic_WriteReg(BaseAddress, XIIC_CR_REG_OFFSET, 0x03); XIic_WriteReg(BaseAddress, XIIC_CR_REG_OFFSET, 0x01); return ByteCount; } /* * Check for the transmit Fifo to become Empty. */ if (IntrStatus & XIIC_INTR_TX_EMPTY_MASK) { break; } } /* * Send data to Tx Fifo. If a stop condition is specified and * the last byte is being sent, then set the dynamic stop bit. */ if ((ByteCount == 1) && (Option == XIIC_STOP)) { /* * The MSMS will be cleared automatically upon setting * dynamic stop. */ XIic_WriteReg(BaseAddress, XIIC_DTR_REG_OFFSET, XIIC_TX_DYN_STOP_MASK | *BufferPtr++); } else { XIic_WriteReg(BaseAddress, XIIC_DTR_REG_OFFSET, *BufferPtr++); } /* * Update the byte count to reflect the byte sent. */ ByteCount--; } if (Option == XIIC_STOP) { /* * If the Option is to release the bus after transmission of * data, Wait for the bus to transition to not busy before * returning, the IIC device cannot be disabled until this * occurs. */ while (1) { if (XIic_ReadIisr(BaseAddress) & XIIC_INTR_BNB_MASK) { break; } } } return ByteCount; }
/****************************************************************************** * * Send the specified buffer to the device that has been previously addressed * on the IIC bus. This function assumes that the 7 bit address has been sent * and it should wait for the transmit of the address to complete. * * @param BaseAddress contains the base address of the IIC device. * @param BufferPtr points to the data to be sent. * @param ByteCount is the number of bytes to be sent. * @param Option indicates whether to hold or free the bus after * transmitting the data. * * @return The number of bytes remaining to be sent. * * @note * * This function does not take advantage of the transmit FIFO because it is * designed for minimal code space and complexity. It contains loops that * that could cause the function not to return if the hardware is not working. * ******************************************************************************/ static unsigned SendData(u32 BaseAddress, u8 *BufferPtr, unsigned ByteCount, u8 Option) { u32 IntrStatus; /* * Send the specified number of bytes in the specified buffer by polling * the device registers and blocking until complete */ while (ByteCount > 0) { /* * Wait for the transmit to be empty before sending any more * data by polling the interrupt status register */ while (1) { IntrStatus = XIic_ReadIisr(BaseAddress); if (IntrStatus & (XIIC_INTR_TX_ERROR_MASK | XIIC_INTR_ARB_LOST_MASK | XIIC_INTR_BNB_MASK)) { return ByteCount; } if (IntrStatus & XIIC_INTR_TX_EMPTY_MASK) { break; } } /* If there is more than one byte to send then put the * next byte to send into the transmit FIFO */ if (ByteCount > 1) { XIic_WriteReg(BaseAddress, XIIC_DTR_REG_OFFSET, *BufferPtr++); } else { if (Option == XIIC_STOP) { /* * If the Option is to release the bus after * the last data byte, Set the stop Option * before sending the last byte of data so * that the stop Option will be generated * immediately following the data. This is * done by clearing the MSMS bit in the * control register. */ XIic_WriteReg(BaseAddress, XIIC_CR_REG_OFFSET, XIIC_CR_ENABLE_DEVICE_MASK | XIIC_CR_DIR_IS_TX_MASK); } /* * Put the last byte to send in the transmit FIFO */ XIic_WriteReg(BaseAddress, XIIC_DTR_REG_OFFSET, *BufferPtr++); if (Option == XIIC_REPEATED_START) { XIic_ClearIisr(BaseAddress, XIIC_INTR_TX_EMPTY_MASK); /* * Wait for the transmit to be empty before * setting RSTA bit. */ while (1) { IntrStatus = XIic_ReadIisr(BaseAddress); if (IntrStatus & XIIC_INTR_TX_EMPTY_MASK) { /* * RSTA bit should be set only * when the FIFO is completely * Empty. */ XIic_WriteReg(BaseAddress, XIIC_CR_REG_OFFSET, XIIC_CR_REPEATED_START_MASK | XIIC_CR_ENABLE_DEVICE_MASK | XIIC_CR_DIR_IS_TX_MASK | XIIC_CR_MSMS_MASK); break; } } } } /* * Clear the latched interrupt status register and this must be * done after the transmit FIFO has been written to or it won't * clear */ XIic_ClearIisr(BaseAddress, XIIC_INTR_TX_EMPTY_MASK); /* * Update the byte count to reflect the byte sent and clear * the latched interrupt status so it will be updated for the * new state */ ByteCount--; } if (Option == XIIC_STOP) { /* * If the Option is to release the bus after transmission of * data, Wait for the bus to transition to not busy before * returning, the IIC device cannot be disabled until this * occurs. Note that this is different from a receive operation * because the stop Option causes the bus to go not busy. */ while (1) { if (XIic_ReadIisr(BaseAddress) & XIIC_INTR_BNB_MASK) { break; } } } return ByteCount; }
/****************************************************************************** * * Receive the specified data from the device that has been previously addressed * on the IIC bus. This function assumes that the 7 bit address has been sent * and it should wait for the transmit of the address to complete. * * @param BaseAddress contains the base address of the IIC device. * @param BufferPtr points to the buffer to hold the data that is * received. * @param ByteCount is the number of bytes to be received. * @param Option indicates whether to hold or free the bus after reception * of data, XIIC_STOP = end with STOP condition, * XIIC_REPEATED_START = don't end with STOP condition. * * @return The number of bytes remaining to be received. * * @note * * This function does not take advantage of the receive FIFO because it is * designed for minimal code space and complexity. It contains loops that * that could cause the function not to return if the hardware is not working. * * This function assumes that the calling function will disable the IIC device * after this function returns. * ******************************************************************************/ static unsigned RecvData(u32 BaseAddress, u8 *BufferPtr, unsigned ByteCount, u8 Option) { u32 CntlReg; u32 IntrStatusMask; u32 IntrStatus; /* Attempt to receive the specified number of bytes on the IIC bus */ while (ByteCount > 0) { /* Setup the mask to use for checking errors because when * receiving one byte OR the last byte of a multibyte message an * error naturally occurs when the no ack is done to tell the * slave the last byte */ if (ByteCount == 1) { IntrStatusMask = XIIC_INTR_ARB_LOST_MASK | XIIC_INTR_BNB_MASK; } else { IntrStatusMask = XIIC_INTR_ARB_LOST_MASK | XIIC_INTR_TX_ERROR_MASK | XIIC_INTR_BNB_MASK; } /* Wait for the previous transmit and the 1st receive to * complete by checking the interrupt status register of the * IPIF */ while (1) { IntrStatus = XIic_ReadIisr(BaseAddress); if (IntrStatus & XIIC_INTR_RX_FULL_MASK) { break; } /* Check the transmit error after the receive full * because when sending only one byte transmit error * will occur because of the no ack to indicate the end * of the data */ if (IntrStatus & IntrStatusMask) { return ByteCount; } } CntlReg = XIic_ReadReg(BaseAddress, XIIC_CR_REG_OFFSET); /* Special conditions exist for the last two bytes so check for * them. Note that the control register must be setup for these * conditions before the data byte which was already received is * read from the receive FIFO (while the bus is throttled */ if (ByteCount == 1) { if (Option == XIIC_STOP) { /* If the Option is to release the bus after the * last data byte, it has already been read and * no ack has been done, so clear MSMS while * leaving the device enabled so it can get off * the IIC bus appropriately with a stop */ XIic_WriteReg(BaseAddress, XIIC_CR_REG_OFFSET, XIIC_CR_ENABLE_DEVICE_MASK); } } /* Before the last byte is received, set NOACK to tell the slave * IIC device that it is the end, this must be done before * reading the byte from the FIFO */ if (ByteCount == 2) { /* Write control reg with NO ACK allowing last byte to * have the No ack set to indicate to slave last byte * read */ XIic_WriteReg(BaseAddress, XIIC_CR_REG_OFFSET, CntlReg | XIIC_CR_NO_ACK_MASK); } /* Read in data from the FIFO and unthrottle the bus such that * the next byte is read from the IIC bus */ *BufferPtr++ = (u8) XIic_ReadReg(BaseAddress, XIIC_DRR_REG_OFFSET); if ((ByteCount == 1) && (Option == XIIC_REPEATED_START)) { /* RSTA bit should be set only when the FIFO is * completely Empty. */ XIic_WriteReg(BaseAddress, XIIC_CR_REG_OFFSET, XIIC_CR_ENABLE_DEVICE_MASK | XIIC_CR_MSMS_MASK | XIIC_CR_REPEATED_START_MASK); } /* Clear the latched interrupt status so that it will be updated * with the new state when it changes, this must be done after * the receive register is read */ XIic_ClearIisr(BaseAddress, XIIC_INTR_RX_FULL_MASK | XIIC_INTR_TX_ERROR_MASK | XIIC_INTR_ARB_LOST_MASK); ByteCount--; } if (Option == XIIC_STOP) { /* If the Option is to release the bus after Reception of data, * wait for the bus to transition to not busy before returning, * the IIC device cannot be disabled until this occurs. It * should transition as the MSMS bit of the control register was * cleared before the last byte was read from the FIFO */ while (1) { if (XIic_ReadIisr(BaseAddress) & XIIC_INTR_BNB_MASK) { break; } } } return ByteCount; }