/** * * When multiple IIC devices attempt to use the bus simultaneously, only * a single device will be able to keep control as a master. Those devices * that didn't retain control over the bus are said to have lost arbitration. * When arbitration is lost, this interrupt occurs sigaling the user that * the message did not get sent as expected. * * This function, at arbitration lost: * - Disables tx empty, ½ empty and Tx error interrupts * - Clears any tx empty, ½ empty Rx Full or tx error interrupts * - Clears Arbitration lost interrupt, * - Flush Tx FIFO * - Call StatusHandler callback with the value: XII_ARB_LOST_EVENT * * @param InstancePtr is a pointer to the XIic instance to be worked on. * * @return * * None. * * @note * * None. * ******************************************************************************/ static void ArbitrationLostHandler(XIic * InstancePtr) { /* Disable tx empty and ½ empty and Tx error interrupts before clearing them * so they won't occur again */ XIic_mDisableIntr(InstancePtr->BaseAddress, XIIC_TX_INTERRUPTS); /* Clear any tx empty, ½ empty Rx Full or tx error interrupts */ XIic_mClearIntr(InstancePtr->BaseAddress, XIIC_TX_INTERRUPTS); XIic_mFlushTxFifo(InstancePtr); /* Update Status via StatusHandler callback */ InstancePtr->StatusHandler(InstancePtr->StatusCallBackRef, XII_ARB_LOST_EVENT); }
/** * * 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. * * @note * * No action is required to clear this interrupt in the device as it is a * pulse. The interrupt need only be cleared in the IpIf interface. * ******************************************************************************/ static void TxErrorHandler(XIic * InstancePtr) { u32 IntrStatus; u8 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_mFlushTxFifo(InstancePtr); XIic_mDisableIntr(InstancePtr->BaseAddress, XIIC_TX_RX_INTERRUPTS); /* If operating in Master mode, call status handler to indicate * NOACK occured */ CntlReg = XIo_In8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET); if ((CntlReg & XIIC_CR_MSMS_MASK) != 0) { InstancePtr->StatusHandler(InstancePtr-> StatusCallBackRef, XII_SLAVE_NO_ACK_EVENT); } 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_READ_IISR(InstancePtr->BaseAddress); if (IntrStatus & XIIC_INTR_RX_FULL_MASK) { /* Rx Reg/FIFO has data, Disable tx error interrupts */ XIic_mDisableIntr(InstancePtr->BaseAddress, XIIC_INTR_TX_ERROR_MASK); return; } XIic_mFlushTxFifo(InstancePtr); /* Disable and clear tx empty, ½ empty, Rx Full or tx error interrupts */ XIic_mDisableIntr(InstancePtr->BaseAddress, XIIC_TX_RX_INTERRUPTS); XIic_mClearIntr(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 = XIo_In8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET); CntlReg &= ~XIIC_CR_MSMS_MASK; XIo_Out8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET, CntlReg); /* set FIFO occupancy depth = 1 so that the first byte will throttle * next recieve msg */ XIo_Out8(InstancePtr->BaseAddress + XIIC_RFD_REG_OFFSET, 0); /* make event callback */ InstancePtr->StatusHandler(InstancePtr->StatusCallBackRef, XII_SLAVE_NO_ACK_EVENT); }
/** * * This function is called when the receive register is full. The number * of bytes received to cause the interrupt is adjustable using the Receive FIFO * Depth register. The number of bytes in the register is read in the Receive * FIFO occupancy register. Both these registers are zero based values (0-15) * such that a value of zero indicates 1 byte. * * For a Master Receiver to properly signal the end of a message, the data must * be read in up to the message length - 1, where control register bits will be * set for bus controls to occur on reading of the last byte. * * @param InstancePtr is a pointer to the XIic instance to be worked on. * * @return * * None. * * @note * * None. * ******************************************************************************/ static void RecvMasterData(XIic * InstancePtr) { u8 LoopCnt; int BytesInFifo; int BytesToRead; u8 CntlReg; /* Device is a master receiving, get the contents of the control register * and determine the number of bytes in fifo to be read out */ CntlReg = XIo_In8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET); BytesInFifo = XIo_In8(InstancePtr->BaseAddress + XIIC_RFO_REG_OFFSET) + 1; /* If data in FIFO holds all data to be retrieved - 1, set NOACK and * disable the tx error */ if ((InstancePtr->RecvByteCount - BytesInFifo) == 1) { /* Disable tx error interrupt to prevent interrupt * as this device will cause it when it set NO ACK next */ XIic_mDisableIntr(InstancePtr->BaseAddress, XIIC_INTR_TX_ERROR_MASK); XIic_mClearIntr(InstancePtr->BaseAddress, XIIC_INTR_TX_ERROR_MASK); /* Write control reg with NO ACK allowing last byte to * have the No ack set to indicate to slave last byte read. */ XIo_Out8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET, (CntlReg | XIIC_CR_NO_ACK_MASK)); /* Read one byte to clear a place for the last byte to be read * which will set the NO ACK */ XIic_mReadRecvByte(InstancePtr); } /* If data in FIFO is all the data to be received then get the data * and also leave the device in a good state for the next transaction */ else if ((InstancePtr->RecvByteCount - BytesInFifo) == 0) { /* If repeated start option is off then the master should stop * using the bus, otherwise hold the bus, setting repeated start * stops the slave from transmitting data when the FIFO is read */ if ((InstancePtr->Options & XII_REPEATED_START_OPTION) == 0) { CntlReg &= ~XIIC_CR_MSMS_MASK; } else { CntlReg |= XIIC_CR_REPEATED_START_MASK; } XIo_Out8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET, CntlReg); /* Read data from the FIFO then set zero based FIFO read depth for a byte */ for (LoopCnt = 0; LoopCnt < BytesInFifo; LoopCnt++) { XIic_mReadRecvByte(InstancePtr); } XIo_Out8(InstancePtr->BaseAddress + XIIC_RFD_REG_OFFSET, 0); /* Disable Rx full interrupt and write the control reg with ACK allowing * next byte sent to be acknowledged automatically */ XIic_mDisableIntr(InstancePtr->BaseAddress, XIIC_INTR_RX_FULL_MASK); XIo_Out8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET, (CntlReg & ~XIIC_CR_NO_ACK_MASK)); /* Send notification of msg Rx complete in RecvHandler callback */ InstancePtr->RecvHandler(InstancePtr->RecvCallBackRef, 0); } else { /* Fifo data not at n-1, read all but the last byte of data from the * slave, if more than a FIFO full yet to receive read a FIFO full */ BytesToRead = InstancePtr->RecvByteCount - BytesInFifo - 1; if (BytesToRead > IIC_RX_FIFO_DEPTH) { BytesToRead = IIC_RX_FIFO_DEPTH; } /* Read in data from the FIFO */ for (LoopCnt = 0; LoopCnt < BytesToRead; LoopCnt++) { XIic_mReadRecvByte(InstancePtr); } } }
/** * * 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. * * All XIic device interrupts are ORed into one device interrupt. This routine * reads the pending interrupts via the IpIf interface and masks that with the * interrupt mask to evaluate only the interrupts enabled. * ******************************************************************************/ void XIic_InterruptHandler(void *InstancePtr) { u8 Status; u32 IntrStatus; u32 IntrPending; u32 IntrEnable; XIic *IicPtr = NULL; u32 Clear = 0; /* * Verify that each of the inputs are valid. */ XASSERT_VOID(InstancePtr != NULL); /* * Convert the non-typed pointer to an IIC instance pointer */ IicPtr = (XIic *) InstancePtr; /* Get the interrupt Status from the IPIF. There is no clearing of * interrupts in the IPIF. Interrupts must be cleared at the source. * To find which interrupts are pending; AND interrupts pending with * interrupts masked. */ IntrPending = XIIC_READ_IISR(IicPtr->BaseAddress); IntrEnable = XIIC_READ_IIER(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_IS_GINTR_ENABLED(IicPtr->BaseAddress) == FALSE)) { return; } /* Update interrupt stats and get the contents of the status register */ IicPtr->Stats.IicInterrupts++; Status = XIo_In8(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_mDisableIntr(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); } /* Clear Interrupts */ IntrStatus = XIIC_READ_IISR(IicPtr->BaseAddress); Clear = IntrStatus & (XIIC_INTR_TX_EMPTY_MASK | XIIC_INTR_TX_HALF_MASK); } XIIC_WRITE_IISR(IicPtr->BaseAddress, Clear); }
/****************************************************************************** * * When the IIC Tx FIFO/register goes empty, this routine is called by the * interrupt service routine to fill the transmit FIFO with data to be sent. * * This function also is called by the Tx ½ empty interrupt as the data handling * is identical when you don't assume the FIFO is empty but use the Tx_FIFO_OCY * register to indicate the available free FIFO bytes. * * @param InstancePtr is a pointer to the XIic instance to be worked on. * * @return * * None. * * @note * * None. * ******************************************************************************/ static void SendMasterData(XIic * InstancePtr) { u8 CntlReg; /* The device is a master on the bus. If there is still more address bytes * to send when in master receive operation and the slave device is 10 bit * addressed. This requires the lower 7 bits of address to be resent when * the mode switches to Read instead of write (while sending addresses). */ if (InstancePtr->TxAddrMode & XIIC_TX_ADDR_MSTR_RECV_MASK) { /* Send the 1st byte of the slave address in the read operation * and change the state to indicate this has been done */ SendSlaveAddr(InstancePtr); InstancePtr->TxAddrMode = XIIC_TX_ADDR_SENT; } /* In between 1st and last byte of message, fill the FIFO with more data * to send, disable the 1/2 empty interrupt based upon data left to send */ else if (InstancePtr->SendByteCount > 1) { XIic_TransmitFifoFill(InstancePtr, XIIC_MASTER_ROLE); if (InstancePtr->SendByteCount < 2) { XIic_mDisableIntr(InstancePtr->BaseAddress, XIIC_INTR_TX_HALF_MASK); } } /* * If there is only one byte left to send, processing differs between * repeated start and normal messages */ else if (InstancePtr->SendByteCount == 1) { /* When using repeated start, another interrupt is expected after the * last byte has been sent, so the message is not done yet */ if (InstancePtr->Options & XII_REPEATED_START_OPTION) { XIic_mWriteSendByte(InstancePtr); } /* When not using repeated start, the stop condition must be generated * after the last byte is written. The bus is throttled waiting for the last * byte. */ else { /* Set the stop condition before sending the last byte of data so that * the stop condition will be generated immediately following the data * another transmit interrupt is not expected so the message is done */ CntlReg = XIo_In8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET); CntlReg &= ~XIIC_CR_MSMS_MASK; XIo_Out8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET, CntlReg); XIic_mWriteSendByte(InstancePtr); /* Wait for bus to not be busy before declaring message has * been sent for the no repeated start operation. The callback * will be called from the BusNotBusy part of the Interrupt * handler to ensure that the message is completely sent. * Disable the TX interrupts and enable the BNB interrupt */ InstancePtr->BNBOnly = FALSE; XIic_mDisableIntr(InstancePtr->BaseAddress, XIIC_TX_INTERRUPTS); XIic_mEnableIntr(InstancePtr->BaseAddress, XIIC_INTR_BNB_MASK); } } else { if (InstancePtr->Options & XII_REPEATED_START_OPTION) { /* The message being sent has completed. When using repeated start * with no more bytes to send repeated start needs to be set in * the control register so that the bus will still be held by this * master */ CntlReg = XIo_In8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET); CntlReg |= XIIC_CR_REPEATED_START_MASK; XIo_Out8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET, CntlReg); /* If the message that was being sent has finished, disable all *transmit interrupts and call the callback that was setup to * indicate the message was sent, with 0 bytes remaining */ XIic_mDisableIntr(InstancePtr->BaseAddress, XIIC_TX_INTERRUPTS); InstancePtr->SendHandler(InstancePtr->SendCallBackRef, 0); } } return; }