/** * * Get the packet wait bound timer for this driver/device. The packet wait bound * is used during interrupt coalescing to trigger an interrupt when not enough * packets have been received to reach the packet count threshold. A packet is a * generic term used by the scatter-gather DMA engine, and is equivalent to an * Ethernet frame in our case. The timer is in milliseconds. * * @param InstancePtr is a pointer to the XEmac instance to be worked on. * @param Direction indicates the channel, send or receive, from which the * threshold register is read. * @param WaitPtr is a pointer to the byte into which the current value of the * packet wait bound register will be copied. An output parameter. Units * are in milliseconds in the range 0 - 1023. A value of 0 indicates the * packet wait bound timer is disabled. * * @return * * - XST_SUCCESS if the packet wait bound was retrieved successfully * - XST_NOT_SGDMA if the MAC is not configured for scatter-gather DMA * - XST_INVALID_PARAM if the Direction parameter is invalid. Turning on * asserts would also catch this error. * * @note * * None. * ******************************************************************************/ XStatus XEmac_GetPktWaitBound(XEmac * InstancePtr, u32 Direction, u32 * WaitPtr) { XASSERT_NONVOID(InstancePtr != NULL); XASSERT_NONVOID(Direction == XEM_SEND || Direction == XEM_RECV); XASSERT_NONVOID(WaitPtr != NULL); XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); if (!XEmac_mIsSgDma(InstancePtr)) { return XST_NOT_SGDMA; } /* * Based on the direction, return the packet wait bound set in the * corresponding DMA channel component. Default to the value in * the receive channel wait bound register (if an invalid Direction * is passed). */ switch (Direction) { case XEM_SEND: *WaitPtr = XDmaChannel_GetPktWaitBound(&InstancePtr->SendChannel); break; case XEM_RECV: *WaitPtr = XDmaChannel_GetPktWaitBound(&InstancePtr->RecvChannel); break; default: return XST_INVALID_PARAM; } return XST_SUCCESS; }
/** * * Set the packet count threshold for this device. The device must be stopped * before setting the threshold. The packet count threshold is used for interrupt * coalescing, which reduces the frequency of interrupts from the device to the * processor. In this case, the scatter-gather DMA engine only interrupts when * the packet count threshold is reached, instead of interrupting for each packet. * A packet is a generic term used by the scatter-gather DMA engine, and is * equivalent to an Ethernet frame in our case. * * @param InstancePtr is a pointer to the XEmac instance to be worked on. * @param Direction indicates the channel, send or receive, from which the * threshold register is read. * @param Threshold is the value of the packet threshold count used during * interrupt coalescing. A value of 0 disables the use of packet threshold * by the hardware. * * @return * * - XST_SUCCESS if the threshold was successfully set * - XST_NOT_SGDMA if the MAC is not configured for scatter-gather DMA * - XST_DEVICE_IS_STARTED if the device has not been stopped * - XST_INVALID_PARAM if the Direction parameter is invalid. Turning on * asserts would also catch this error. * * @note * * The packet threshold could be set to larger than the number of descriptors * allocated to the DMA channel. In this case, the wait bound will take over * and always indicate data arrival. There was a check in this function that * returned an error if the treshold was larger than the number of descriptors, * but that was removed because users would then have to set the threshold * only after they set descriptor space, which is an order dependency that * caused confustion. * ******************************************************************************/ XStatus XEmac_SetPktThreshold(XEmac * InstancePtr, u32 Direction, u8 Threshold) { XASSERT_NONVOID(InstancePtr != NULL); XASSERT_NONVOID(Direction == XEM_SEND || Direction == XEM_RECV); XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); /* * Be sure device is configured for scatter-gather DMA and has been stopped */ if (!XEmac_mIsSgDma(InstancePtr)) { return XST_NOT_SGDMA; } if (InstancePtr->IsStarted == XCOMPONENT_IS_STARTED) { return XST_DEVICE_IS_STARTED; } /* * Based on the direction, set the packet threshold in the * corresponding DMA channel component. Default to the receive * channel threshold register (if an invalid Direction is passed). */ switch (Direction) { case XEM_SEND: return XDmaChannel_SetPktThreshold(&InstancePtr->SendChannel, Threshold); case XEM_RECV: return XDmaChannel_SetPktThreshold(&InstancePtr->RecvChannel, Threshold); default: return XST_INVALID_PARAM; } }
/** * * Add a descriptor, with an attached empty buffer, into the receive descriptor * list. The buffer attached to the descriptor must be 32-bit aligned if using * the OPB Ethernet core and 64-bit aligned if using the PLB Ethernet core. * This function is used by the upper layer software during initialization when * first setting up the receive descriptors, and also during reception of frames * to replace filled buffers with empty buffers. This function can be called * when the device is started or stopped. Note that it does start the scatter- * gather DMA engine. Although this is not necessary during initialization, it * is not a problem during initialization because the MAC receiver is not yet * started. * * The buffer attached to the descriptor must be aligned on both the front end * and the back end. * * Notification of received frames are done asynchronously through the receive * callback function. * * @param InstancePtr is a pointer to the XEmac instance to be worked on. * @param BdPtr is a pointer to the buffer descriptor that will be added to the * descriptor list. * * @return * * - XST_SUCCESS if a descriptor was successfully returned to the driver * - XST_NOT_SGDMA if the device is not in scatter-gather DMA mode * - XST_DMA_SG_LIST_FULL if the receive descriptor list is full * - XST_DMA_SG_BD_LOCKED if the DMA channel cannot insert the descriptor into * the list because a locked descriptor exists at the insert point. * - XST_DMA_SG_NOTHING_TO_COMMIT if even after inserting a descriptor into the * list, the DMA channel believes there are no new descriptors to commit. * * @internal * * A status that should never be returned from this function, although * the code is set up to handle it, is XST_DMA_SG_NO_LIST. Starting the device * requires a list to be created, and this function requires the device to be * started. * ******************************************************************************/ XStatus XEmac_SgRecv(XEmac * InstancePtr, XBufDescriptor * BdPtr) { XStatus Result; u32 BdControl; XASSERT_NONVOID(InstancePtr != NULL); XASSERT_NONVOID(BdPtr != NULL); XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); /* * Be sure the device is configured for scatter-gather DMA */ if (!XEmac_mIsSgDma(InstancePtr)) { return XST_NOT_SGDMA; } /* * Set some descriptor control word defaults (destination address increment * and local source address) and the source address (the FIFO). These are * the same for every receive descriptor. */ BdControl = XBufDescriptor_GetControl(BdPtr); XBufDescriptor_SetControl(BdPtr, BdControl | XEM_DFT_RECV_BD_MASK); XBufDescriptor_SetSrcAddress(BdPtr, InstancePtr->PhysAddress + XEM_PFIFO_RXDATA_OFFSET); /* * Put the descriptor into the channel's descriptor list and commit. * Although this function is likely called within interrupt context, there * is the possibility that the upper layer software queues it to a task. * In this case, a critical section is needed here to protect shared data * in the DMA component. */ XIIF_V123B_GINTR_DISABLE(InstancePtr->BaseAddress); Result = XDmaChannel_PutDescriptor(&InstancePtr->RecvChannel, BdPtr); if (Result != XST_SUCCESS) { XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress); return Result; } Result = XDmaChannel_CommitPuts(&InstancePtr->RecvChannel); if (Result != XST_SUCCESS) { XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress); return Result; } /* * Start the DMA channel. Ignore the return status since we know the list * exists and has at least one entry and we don't care if the channel is * already started. The DMA component accesses data here that can be * modified at interrupt or task levels, so a critical section is required. */ (void)XDmaChannel_SgStart(&InstancePtr->RecvChannel); XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress); return XST_SUCCESS; }
/** * * Set the callback function for handling confirmation of transmitted frames in * scatter-gather DMA mode. The upper layer software should call this function * during initialization. The callback is called once per frame sent. The head * of a descriptor list is passed in along with the number of descriptors in * the list. The callback is responsible for freeing buffers attached to these * descriptors. * * The callback is invoked by the driver within interrupt context, so it needs * to do its job quickly. If there are potentially slow operations within the * callback, these should be done at task-level. * * @param InstancePtr is a pointer to the XEmac instance to be worked on. * @param CallBackRef is a reference pointer to be passed back to the adapter in * the callback. This helps the adapter correlate the callback to a * particular driver. * @param FuncPtr is the pointer to the callback function. * * @return * * None. * * @note * * None. * ******************************************************************************/ void XEmac_SetSgSendHandler(XEmac * InstancePtr, void *CallBackRef, XEmac_SgHandler FuncPtr) { /* * Asserted IsDmaSg here instead of run-time check because there is really * no ill-effects of setting these when not configured for scatter-gather. */ XASSERT_VOID(InstancePtr != NULL); XASSERT_VOID(FuncPtr != NULL); XASSERT_VOID(XEmac_mIsSgDma(InstancePtr)); XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); InstancePtr->SgSendHandler = FuncPtr; InstancePtr->SgSendRef = CallBackRef; }
/** * * Give the driver the memory space to be used for the scatter-gather DMA * transmit descriptor list. This function should only be called once, during * initialization of the Ethernet driver. The memory space must be big enough * to hold some number of descriptors, depending on the needs of the system. * The xemac.h file defines minimum and default numbers of descriptors * which can be used to allocate this memory space. * * The memory space must be 32-bit aligned. An assert will occur if asserts * are turned on and the memory is not aligned. * * @param InstancePtr is a pointer to the XEmac instance to be worked on. * @param MemoryPtr is a pointer to the aligned memory. * @param ByteCount is the length, in bytes, of the memory space. * * @return * * - XST_SUCCESS if the space was initialized successfully * - XST_NOT_SGDMA if the MAC is not configured for scatter-gather DMA * - XST_DMA_SG_LIST_EXISTS if this list space has already been created * * @note * * If the device is configured for scatter-gather DMA, this function must be * called AFTER the XEmac_Initialize() function because the DMA channel * components must be initialized before the memory space is set. * ******************************************************************************/ XStatus XEmac_SetSgSendSpace(XEmac * InstancePtr, u32 * MemoryPtr, u32 ByteCount, void *PhyPtr) { XASSERT_NONVOID(InstancePtr != NULL); XASSERT_NONVOID(MemoryPtr != NULL); XASSERT_NONVOID(ByteCount != 0); XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); if (!XEmac_mIsSgDma(InstancePtr)) { return XST_NOT_SGDMA; } return XDmaChannel_CreateSgList(&InstancePtr->SendChannel, MemoryPtr, ByteCount, PhyPtr); }
/** * * Send an Ethernet frame using scatter-gather DMA. The caller attaches the * frame to one or more buffer descriptors, then calls this function once for * each descriptor. The caller is responsible for allocating and setting up the * descriptor. An entire Ethernet frame may or may not be contained within one * descriptor. This function simply inserts the descriptor into the scatter- * gather engine's transmit list. The caller is responsible for providing mutual * exclusion to guarantee that a frame is contiguous in the transmit list. The * buffer attached to the descriptor must be 32-bit aligned if using the OPB * Ethernet core and 64-bit aligned if using the PLB Ethernet core. * * The driver updates the descriptor with the device control register before * being inserted into the transmit list. If this is the last descriptor in * the frame, the inserts are committed, which means the descriptors for this * frame are now available for transmission. * * It is assumed that the upper layer software supplies a correctly formatted * Ethernet frame, including the destination and source addresses, the * type/length field, and the data field. It is also assumed that upper layer * software does not append FCS at the end of the frame. * * This call is non-blocking. Notification of error or successful transmission * is done asynchronously through the send or error callback function. * * @param InstancePtr is a pointer to the XEmac instance to be worked on. * @param BdPtr is the address of a descriptor to be inserted into the transmit * ring. * @param Delay indicates whether to start the scatter-gather DMA channel * immediately, or whether to wait. This allows the user to build up a * list of more than one descriptor before starting the transmission of * the packets, which allows the application to keep up with DMA and have * a constant stream of frames being transmitted. Use XEM_SGDMA_NODELAY or * XEM_SGDMA_DELAY, defined in xemac.h, as the value of this argument. If * the user chooses to delay and build a list, the user must call this * function with the XEM_SGDMA_NODELAY option or call XEmac_Start() to * kick off the tranmissions. * * @return * * - XST_SUCCESS if the buffer was successfull sent * - XST_DEVICE_IS_STOPPED if the Ethernet MAC has not been started yet * - XST_NOT_SGDMA if the device is not in scatter-gather DMA mode * - XST_DMA_SG_LIST_FULL if the descriptor list for the DMA channel is full * - XST_DMA_SG_BD_LOCKED if the DMA channel cannot insert the descriptor into * the list because a locked descriptor exists at the insert point * - XST_DMA_SG_NOTHING_TO_COMMIT if even after inserting a descriptor into the * list, the DMA channel believes there are no new descriptors to commit. If * this is ever encountered, there is likely a thread mutual exclusion problem * on transmit. * * @note * * This function is not thread-safe. The user must provide mutually exclusive * access to this function if there are to be multiple threads that can call it. * * @internal * * A status that should never be returned from this function, although * the code is set up to handle it, is XST_DMA_SG_NO_LIST. Starting the device * requires a list to be created, and this function requires the device to be * started. * ******************************************************************************/ XStatus XEmac_SgSend(XEmac * InstancePtr, XBufDescriptor * BdPtr, int Delay) { XStatus Result; u32 BdControl; XASSERT_NONVOID(InstancePtr != NULL); XASSERT_NONVOID(BdPtr != NULL); XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); /* * Be sure the device is configured for scatter-gather DMA, then be sure * it is started. */ if (!XEmac_mIsSgDma(InstancePtr)) { return XST_NOT_SGDMA; } /* * Set some descriptor control word defaults (source address increment * and local destination address) and the destination address * (the FIFO). These are the same for every transmit descriptor. */ BdControl = XBufDescriptor_GetControl(BdPtr); XBufDescriptor_SetControl(BdPtr, BdControl | XEM_DFT_SEND_BD_MASK); XBufDescriptor_SetDestAddress(BdPtr, InstancePtr->PhysAddress + XEM_PFIFO_TXDATA_OFFSET); /* * Put the descriptor in the send list. The DMA component accesses data * here that can also be modified in interrupt context, so a critical * section is required. */ XIIF_V123B_GINTR_DISABLE(InstancePtr->BaseAddress); Result = XDmaChannel_PutDescriptor(&InstancePtr->SendChannel, BdPtr); if (Result != XST_SUCCESS) { XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress); return Result; } /* * If this is the last buffer in the frame, commit the inserts and start * the DMA engine if necessary */ if (XBufDescriptor_IsLastControl(BdPtr)) { Result = XDmaChannel_CommitPuts(&InstancePtr->SendChannel); if (Result != XST_SUCCESS) { XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress); return Result; } if (Delay == XEM_SGDMA_NODELAY) { /* * Start the DMA channel. Ignore the return status since we know the * list exists and has at least one entry and we don't care if the * channel is already started. The DMA component accesses data here * that can be modified at interrupt or task levels, so a critical * section is required. */ (void)XDmaChannel_SgStart(&InstancePtr->SendChannel); } } XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress); return XST_SUCCESS; }
/* * * Check the interrupt status bits of the Ethernet MAC for errors. Errors * currently handled are: * - Receive length FIFO overrun. Indicates data was lost due to the receive * length FIFO becoming full during the reception of a packet. Only a device * reset clears this condition. * - Receive length FIFO underrun. An attempt to read an empty FIFO. Only a * device reset clears this condition. * - Transmit status FIFO overrun. Indicates data was lost due to the transmit * status FIFO becoming full following the transmission of a packet. Only a * device reset clears this condition. * - Transmit status FIFO underrun. An attempt to read an empty FIFO. Only a * device reset clears this condition. * - Transmit length FIFO overrun. Indicates data was lost due to the transmit * length FIFO becoming full following the transmission of a packet. Only a * device reset clears this condition. * - Transmit length FIFO underrun. An attempt to read an empty FIFO. Only a * device reset clears this condition. * - Receive data FIFO overrun. Indicates data was lost due to the receive data * FIFO becoming full during the reception of a packet. * - Receive data errors: * - Receive missed frame error. Valid data was lost by the MAC. * - Receive collision error. Data was lost by the MAC due to a collision. * - Receive FCS error. Data was dicarded by the MAC due to FCS error. * - Receive length field error. Data was dicarded by the MAC due to an invalid * length field in the packet. * - Receive short error. Data was dicarded by the MAC because a packet was * shorter than allowed. * - Receive long error. Data was dicarded by the MAC because a packet was * longer than allowed. * - Receive alignment error. Data was truncated by the MAC because its length * was not byte-aligned. * * @param InstancePtr is a pointer to the XEmac instance to be worked on. * @param IntrStatus is the contents of the interrupt status register to be checked * * @return * * None. * * @note * * This function is intended for internal use only. * ******************************************************************************/ void XEmac_CheckEmacError(XEmac * InstancePtr, u32 IntrStatus) { u32 ResetError = FALSE; /* * First check for receive fifo overrun/underrun errors. Most require a * reset by the user to clear, but the data FIFO overrun error does not. */ if (IntrStatus & XEM_EIR_RECV_DFIFO_OVER_MASK) { InstancePtr->Stats.RecvOverrunErrors++; InstancePtr->Stats.FifoErrors++; } if (IntrStatus & XEM_EIR_RECV_LFIFO_OVER_MASK) { /* * Receive Length FIFO overrun interrupts no longer occur in v1.00l * and later of the EMAC device. Frames are just dropped by the EMAC * if the length FIFO is full. The user would notice the Receive Missed * Frame count incrementing without any other errors being reported. * This code is left here for backward compatibility with v1.00k and * older EMAC devices. */ InstancePtr->Stats.RecvOverrunErrors++; InstancePtr->Stats.FifoErrors++; ResetError = TRUE; /* requires a reset */ } if (IntrStatus & XEM_EIR_RECV_LFIFO_UNDER_MASK) { InstancePtr->Stats.RecvUnderrunErrors++; InstancePtr->Stats.FifoErrors++; ResetError = TRUE; /* requires a reset */ } /* * Now check for general receive errors. Get the latest count where * available, otherwise just bump the statistic so we know the interrupt * occurred. */ if (IntrStatus & XEM_EIR_RECV_ERROR_MASK) { if (IntrStatus & XEM_EIR_RECV_MISSED_FRAME_MASK) { /* * Caused by length FIFO or data FIFO overruns on receive side */ InstancePtr->Stats.RecvMissedFrameErrors = XIo_In32(InstancePtr->BaseAddress + XEM_RMFC_OFFSET); } if (IntrStatus & XEM_EIR_RECV_COLLISION_MASK) { InstancePtr->Stats.RecvCollisionErrors = XIo_In32(InstancePtr->BaseAddress + XEM_RCC_OFFSET); } if (IntrStatus & XEM_EIR_RECV_FCS_ERROR_MASK) { InstancePtr->Stats.RecvFcsErrors = XIo_In32(InstancePtr->BaseAddress + XEM_RFCSEC_OFFSET); } if (IntrStatus & XEM_EIR_RECV_LEN_ERROR_MASK) { InstancePtr->Stats.RecvLengthFieldErrors++; } if (IntrStatus & XEM_EIR_RECV_SHORT_ERROR_MASK) { InstancePtr->Stats.RecvShortErrors++; } if (IntrStatus & XEM_EIR_RECV_LONG_ERROR_MASK) { InstancePtr->Stats.RecvLongErrors++; } if (IntrStatus & XEM_EIR_RECV_ALIGN_ERROR_MASK) { InstancePtr->Stats.RecvAlignmentErrors = XIo_In32(InstancePtr->BaseAddress + XEM_RAEC_OFFSET); } /* * Bump recv interrupts stats only if not scatter-gather DMA (this * stat gets bumped elsewhere in that case) */ if (!XEmac_mIsSgDma(InstancePtr)) { InstancePtr->Stats.RecvInterrupts++; /* TODO: double bump? */ } } /* * Check for transmit errors. These apply to both DMA and non-DMA modes * of operation. The entire device should be reset after overruns or * underruns. */ if (IntrStatus & (XEM_EIR_XMIT_SFIFO_OVER_MASK | XEM_EIR_XMIT_LFIFO_OVER_MASK)) { InstancePtr->Stats.XmitOverrunErrors++; InstancePtr->Stats.FifoErrors++; ResetError = TRUE; } if (IntrStatus & (XEM_EIR_XMIT_SFIFO_UNDER_MASK | XEM_EIR_XMIT_LFIFO_UNDER_MASK)) { InstancePtr->Stats.XmitUnderrunErrors++; InstancePtr->Stats.FifoErrors++; ResetError = TRUE; } if (ResetError) { /* * If a reset error occurred, disable the EMAC interrupts since the * reset-causing interrupt(s) is latched in the EMAC - meaning it will * keep occurring until the device is reset. In order to give the higher * layer software time to reset the device, we have to disable the * overrun/underrun interrupts until that happens. We trust that the * higher layer resets the device. We are able to get away with disabling * all EMAC interrupts since the only interrupts it generates are for * error conditions, and we don't care about any more errors right now. */ XIIF_V123B_WRITE_IIER(InstancePtr->BaseAddress, 0); /* * Invoke the error handler callback, which should result in a reset * of the device by the upper layer software. */ InstancePtr->ErrorHandler(InstancePtr->ErrorRef, XST_RESET_ERROR); } }