/****************************************************************************** * * Author: Xilinx, Inc. * * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A * COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS * ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, * XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE * FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING * ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. * XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO * THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY * WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM * CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE. * * * Xilinx hardware products are not intended for use in life support * appliances, devices, or systems. Use in such applications is * expressly prohibited. * * * (c) Copyright 2002-2004 Xilinx Inc. * All rights reserved. * * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 675 Mass Ave, Cambridge, MA 02139, USA. * ******************************************************************************/ /*****************************************************************************/ /** * * @file xemac_intr_dma.c * * Contains functions used in interrupt mode when configured with scatter-gather * DMA. * * The interrupt handler, XEmac_IntrHandlerDma(), must be connected by the user * to the interrupt controller. * *
* MODIFICATION HISTORY:
*
* Ver   Who  Date     Changes
* ----- ---- -------- ---------------------------------------------------------
* 1.00a rpm  07/31/01 First release
* 1.00b rpm  02/20/02 Repartitioned files and functions
* 1.00c rpm  12/05/02 New version includes support for simple DMA and the delay
*                     argument to SgSend
* 1.00c rpm  02/03/03 The XST_DMA_SG_COUNT_EXCEEDED return code was removed
*                     from SetPktThreshold in the internal DMA driver. Also
*                     avoided compiler warnings by initializing Result in the
*                     interrupt service routines.
* 1.00c rpm  03/26/03 Fixed a problem in the interrupt service routines where
*                     the interrupt status was toggled clear after a call to
*                     ErrorHandler, but if ErrorHandler reset the device the
*                     toggle actually asserted the interrupt because the
*                     reset had cleared it.
* 
* ******************************************************************************/ /***************************** Include Files *********************************/ #include "xbasic_types.h" #include "xemac_i.h" #include "xio.h" #include "xbuf_descriptor.h" #include "xdma_channel.h" #include "xipif_v1_23_b.h" /* Uses v1.23b of the IPIF */ /************************** Constant Definitions *****************************/ /**************************** Type Definitions *******************************/ /***************** Macros (Inline Functions) Definitions *********************/ /************************** Variable Definitions *****************************/ /************************** Function Prototypes ******************************/ static void HandleDmaRecvIntr(XEmac * InstancePtr); static void HandleDmaSendIntr(XEmac * InstancePtr); static void HandleEmacDmaIntr(XEmac * InstancePtr); /*****************************************************************************/ /** * * 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 word-aligned. * * 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. * * The buffer attached to the descriptor must be word-aligned on the front end. * * 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->BaseAddress + 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; } /*****************************************************************************/ /** * * Add a descriptor, with an attached empty buffer, into the receive descriptor * list. The buffer attached to the descriptor must be word-aligned. This 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 word-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->BaseAddress + 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; } /*****************************************************************************/ /** * * The interrupt handler for the Ethernet driver when configured with scatter- * gather DMA. * * Get the interrupt status from the IpIf to determine the source of the * interrupt. The source can be: MAC, Recv Packet FIFO, Send Packet FIFO, Recv * DMA channel, or Send DMA channel. The packet FIFOs only interrupt during * "deadlock" conditions. * * @param InstancePtr is a pointer to the XEmac instance that just interrupted. * * @return * * None. * * @note * * None. * ******************************************************************************/ void XEmac_IntrHandlerDma(void *InstancePtr) { u32 IntrStatus; XEmac *EmacPtr = (XEmac *) InstancePtr; EmacPtr->Stats.TotalIntrs++; /* * Get the interrupt status from the IPIF. There is no clearing of * interrupts in the IPIF. Interrupts must be cleared at the source. */ IntrStatus = XIIF_V123B_READ_DIPR(EmacPtr->BaseAddress); /* * See which type of interrupt is being requested, and service it */ if (IntrStatus & XEM_IPIF_RECV_DMA_MASK) { /* Receive DMA interrupt */ EmacPtr->Stats.RecvInterrupts++; HandleDmaRecvIntr(EmacPtr); } if (IntrStatus & XEM_IPIF_SEND_DMA_MASK) { /* Send DMA interrupt */ EmacPtr->Stats.XmitInterrupts++; HandleDmaSendIntr(EmacPtr); } if (IntrStatus & XEM_IPIF_EMAC_MASK) { /* MAC interrupt */ EmacPtr->Stats.EmacInterrupts++; HandleEmacDmaIntr(EmacPtr); } if (IntrStatus & XEM_IPIF_RECV_FIFO_MASK) { /* Receive FIFO interrupt */ EmacPtr->Stats.RecvInterrupts++; XEmac_CheckFifoRecvError(EmacPtr); } if (IntrStatus & XEM_IPIF_SEND_FIFO_MASK) { /* Send FIFO interrupt */ EmacPtr->Stats.XmitInterrupts++; XEmac_CheckFifoSendError(EmacPtr); } if (IntrStatus & XIIF_V123B_ERROR_MASK) { /* * An error occurred internal to the IPIF. This is more of a debug and * integration issue rather than a production error. Don't do anything * other than clear it, which provides a spot for software to trap * on the interrupt and begin debugging. */ XIIF_V123B_WRITE_DISR(EmacPtr->BaseAddress, XIIF_V123B_ERROR_MASK); } } /*****************************************************************************/ /** * * 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; } } /*****************************************************************************/ /** * * Get the value of the packet count threshold for this driver/device. 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 ThreshPtr is a pointer to the byte into which the current value of the * packet threshold register will be copied. An output parameter. A value * of 0 indicates the use of packet threshold by the hardware is disabled. * * @return * * - XST_SUCCESS if the packet threshold 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_GetPktThreshold(XEmac * InstancePtr, u32 Direction, u8 * ThreshPtr) { XASSERT_NONVOID(InstancePtr != NULL); XASSERT_NONVOID(Direction == XEM_SEND || Direction == XEM_RECV); XASSERT_NONVOID(ThreshPtr != NULL); XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); if (!XEmac_mIsSgDma(InstancePtr)) { return XST_NOT_SGDMA; } /* * Based on the direction, return the packet threshold set in the * corresponding DMA channel component. Default to the value in * the receive channel threshold register (if an invalid Direction * is passed). */ switch (Direction) { case XEM_SEND: *ThreshPtr = XDmaChannel_GetPktThreshold(&InstancePtr->SendChannel); break; case XEM_RECV: *ThreshPtr = XDmaChannel_GetPktThreshold(&InstancePtr->RecvChannel); break; default: return XST_INVALID_PARAM; } return XST_SUCCESS; } /*****************************************************************************/ /** * * Set the packet wait bound timer for this driver/device. The device must be * stopped before setting the timer value. 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 TimerValue is the value of the packet wait bound used during interrupt * coalescing. It is in milliseconds in the range 0 - 1023. A value of 0 * disables the packet wait bound timer. * * @return * * - XST_SUCCESS if the packet wait bound was set successfully * - 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 * * None. * ******************************************************************************/ XStatus XEmac_SetPktWaitBound(XEmac * InstancePtr, u32 Direction, u32 TimerValue) { XASSERT_NONVOID(InstancePtr != NULL); XASSERT_NONVOID(Direction == XEM_SEND || Direction == XEM_RECV); XASSERT_NONVOID(TimerValue <= XEM_SGDMA_MAX_WAITBOUND); 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 wait bound in the * corresponding DMA channel component. Default to the receive * channel wait bound register (if an invalid Direction is passed). */ switch (Direction) { case XEM_SEND: XDmaChannel_SetPktWaitBound(&InstancePtr->SendChannel, TimerValue); break; case XEM_RECV: XDmaChannel_SetPktWaitBound(&InstancePtr->RecvChannel, TimerValue); break; default: return XST_INVALID_PARAM; } return XST_SUCCESS; } /*****************************************************************************/ /** * * 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; } /*****************************************************************************/ /** * * Give the driver the memory space to be used for the scatter-gather DMA * receive 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 word-aligned. An assert will occur if asserts are * turned on and the memory is not word-aligned. * * @param InstancePtr is a pointer to the XEmac instance to be worked on. * @param MemoryPtr is a pointer to the word-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_SetSgRecvSpace(XEmac * InstancePtr, u32 * MemoryPtr, u32 ByteCount) { 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->RecvChannel, MemoryPtr, ByteCount); } /*****************************************************************************/ /** * * 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 word-aligned. An assert will occur if asserts are * turned on and the memory is not word-aligned. * * @param InstancePtr is a pointer to the XEmac instance to be worked on. * @param MemoryPtr is a pointer to the word-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) { 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); } /*****************************************************************************/ /** * * Set the callback function for handling received frames in scatter-gather DMA * mode. The upper layer software should call this function during * initialization. The callback is called once per frame received. The head of * a descriptor list is passed in along with the number of descriptors in the * list. Before leaving the callback, the upper layer software should attach a * new buffer to each descriptor in the list. * * The callback is invoked by the driver within interrupt context, so it needs * to do its job quickly. Sending the received frame up the protocol stack * should be done at task-level. If there are other potentially slow operations * within the callback, these too 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_SetSgRecvHandler(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->SgRecvHandler = FuncPtr; InstancePtr->SgRecvRef = CallBackRef; } /*****************************************************************************/ /** * * 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; } /*****************************************************************************/ /* * * Handle an interrupt from the DMA receive channel. DMA interrupts are: * * - DMA error. DMA encountered a bus error or timeout. This is a fatal error * that requires reset of the channel. The driver calls the error handler * of the upper layer software with an error code indicating the device should * be reset. * - Packet count threshold reached. For scatter-gather operations, indicates * the threshold for the number of packets not serviced by software has been * reached. The driver behaves as follows: * - Get the value of the packet counter, which tells us how many packets * are ready to be serviced * - For each packet * - For each descriptor, remove it from the scatter-gather list * - Check for the last descriptor in the frame, and if set * - Bump frame statistics * - Call the scatter-gather receive callback function * - Decrement the packet counter by one * Note that there are no receive errors reported in the status word of * the buffer descriptor. If receive errors occur, the MAC drops the * packet, and we only find out about the errors through various error * count registers. * - Packet wait bound reached. For scatter-gather, indicates the time to wait * for the next packet has expired. The driver follows the same logic as when * the packet count threshold interrupt is received. * - Scatter-gather end acknowledge. Hardware has reached the end of the * descriptor list. The driver follows the same logic as when the packet count * threshold interrupt is received. In addition, the driver restarts the DMA * scatter-gather channel in case there are newly inserted descriptors. * * @param InstancePtr is a pointer to the XEmac instance to be worked on. * * @return * * Although the function returns void, there are asynchronous errors that can * be generated (by calling the ErrorHandler) from this function. These are: * - XST_DMA_SG_LIST_EMPTY indicates we tried to get a buffer descriptor from the * DMA channel, but there was not one ready for software. * - XST_DMA_ERROR indicates a DMA bus error or timeout occurred. This is a fatal * error that requires reset. * * @note * * None. * ******************************************************************************/ static void HandleDmaRecvIntr(XEmac * InstancePtr) { u32 IntrStatus; /* * Read the interrupt status */ IntrStatus = XDmaChannel_GetIntrStatus(&InstancePtr->RecvChannel); /* * For packet threshold or wait bound interrupts, process desciptors. Also * process descriptors on a SG end acknowledgement, which means the end of * the descriptor list has been reached by the hardware. For receive, this * is potentially trouble since it means the descriptor list is full, * unless software can process enough packets quickly enough so the * hardware has room to put new packets. */ if (IntrStatus & (XDC_IXR_PKT_THRESHOLD_MASK | XDC_IXR_PKT_WAIT_BOUND_MASK | XDC_IXR_SG_END_MASK)) { XStatus Result = XST_SUCCESS; u32 NumFrames; u32 NumProcessed; u32 NumBuffers; u32 NumBytes; u32 IsLast; XBufDescriptor *FirstBdPtr; XBufDescriptor *BdPtr; /* * Get the number of unserviced packets */ NumFrames = XDmaChannel_GetPktCount(&InstancePtr->RecvChannel); for (NumProcessed = 0; NumProcessed < NumFrames; NumProcessed++) { IsLast = FALSE; FirstBdPtr = NULL; NumBuffers = 0; NumBytes = 0; /* * For each packet, get the descriptor from the list. On the * last one in the frame, make the callback to the upper layer. */ while (!IsLast) { Result = XDmaChannel_GetDescriptor(&InstancePtr-> RecvChannel, &BdPtr); if (Result != XST_SUCCESS) { /* * An error getting a buffer descriptor from the list. * This should not happen, but if it does, report it to * the error callback and break out of the loops to service * other interrupts. */ InstancePtr->ErrorHandler(InstancePtr-> ErrorRef, Result); break; } /* * Keep a pointer to the first descriptor in the list, as it * will be passed to the upper layers in a bit. By the fact * that we received this packet means no errors occurred, so * no need to check the device status word for errors. */ if (FirstBdPtr == NULL) { FirstBdPtr = BdPtr; } NumBytes += XBufDescriptor_GetLength(BdPtr); /* * Check to see if this is the last descriptor in the frame, * and if so, set the IsLast flag to get out of the loop. */ if (XBufDescriptor_IsLastStatus(BdPtr)) { IsLast = TRUE; } /* * Bump the number of buffers in this packet */ NumBuffers++; } /* end while loop */ /* * Check for error that occurred inside the while loop, and break * out of the for loop if there was one so other interrupts can * be serviced. */ if (Result != XST_SUCCESS) { break; } InstancePtr->Stats.RecvFrames++; InstancePtr->Stats.RecvBytes += NumBytes; /* * Make the callback to the upper layers, passing it the first * descriptor in the packet and the number of descriptors in the * packet. */ InstancePtr->SgRecvHandler(InstancePtr->SgRecvRef, FirstBdPtr, NumBuffers); /* * Decrement the packet count register to reflect the fact we * just processed a packet */ XDmaChannel_DecrementPktCount(&InstancePtr-> RecvChannel); } /* end for loop */ /* * If the interrupt was an end-ack, check the descriptor list again to * see if it is empty. If not, go ahead and restart the scatter-gather * channel. This is to fix a possible race condition where, on receive, * the driver attempted to start a scatter-gather channel that was * already started, which resulted in no action from the XDmaChannel * component. But, just after the XDmaChannel component saw that the * hardware was already started, the hardware stopped because it * reached the end of the list. In that case, this interrupt is * generated and we can restart the hardware here. */ if (IntrStatus & XDC_IXR_SG_END_MASK) { /* * Ignore the return status since we know the list exists and we * don't care if the list is empty or the channel is already started. */ (void) XDmaChannel_SgStart(&InstancePtr->RecvChannel); } } /* * All interrupts are handled (except the error below) so acknowledge * (clear) the interrupts by writing the value read above back to the status * register. The packet count interrupt must be acknowledged after the * decrement, otherwise it will come right back. We clear the interrupts * before we handle the error interrupt because the ErrorHandler should * result in a reset, which clears the interrupt status register. So we * don't want to toggle the interrupt back on by writing the interrupt * status register with an old value after a reset. */ XDmaChannel_SetIntrStatus(&InstancePtr->RecvChannel, IntrStatus); /* * Check for DMA errors and call the error callback function if an error * occurred (DMA bus or timeout error), which should result in a reset of * the device by the upper layer software. */ if (IntrStatus & XDC_IXR_DMA_ERROR_MASK) { InstancePtr->Stats.DmaErrors++; InstancePtr->ErrorHandler(InstancePtr->ErrorRef, XST_DMA_ERROR); } } /*****************************************************************************/ /* * * Handle an interrupt from the DMA send channel. DMA interrupts are: * * - DMA error. DMA encountered a bus error or timeout. This is a fatal error * that requires reset of the channel. The driver calls the error handler * of the upper layer software with an error code indicating the device should * be reset. * - Packet count threshold reached. For scatter-gather operations, indicates * the threshold for the number of packets not serviced by software has been * reached. The driver behaves as follows: * - Get the value of the packet counter, which tells us how many packets * are ready to be serviced * - For each packet * - For each descriptor, remove it from the scatter-gather list * - Check for the last descriptor in the frame, and if set * - Bump frame statistics * - Call the scatter-gather receive callback function * - Decrement the packet counter by one * Note that there are no receive errors reported in the status word of * the buffer descriptor. If receive errors occur, the MAC drops the * packet, and we only find out about the errors through various error * count registers. * - Packet wait bound reached. For scatter-gather, indicates the time to wait * for the next packet has expired. The driver follows the same logic as when * the packet count threshold interrupt is received. * - Scatter-gather end acknowledge. Hardware has reached the end of the * descriptor list. The driver follows the same logic as when the packet count * threshold interrupt is received. In addition, the driver restarts the DMA * scatter-gather channel in case there are newly inserted descriptors. * * @param InstancePtr is a pointer to the XEmac instance to be worked on. * * @return * * Although the function returns void, there are asynchronous errors * that can be generated from this function. These are: * - XST_DMA_SG_LIST_EMPTY indicates we tried to get a buffer descriptor from * the DMA channel, but there was not one ready for software. * - XST_DMA_ERROR indicates a DMA bus error or timeout occurred. This is a * fatal error that requires reset. * * @note * * None. * ******************************************************************************/ static void HandleDmaSendIntr(XEmac * InstancePtr) { u32 IntrStatus; /* * Read the interrupt status */ IntrStatus = XDmaChannel_GetIntrStatus(&InstancePtr->SendChannel); /* * For packet threshold or wait bound interrupt, process descriptors. Also * process descriptors on a SG end acknowledgement, which means the end of * the descriptor list has been reached by the hardware. For transmit, * this is a normal condition during times of light traffic. In fact, the * wait bound interrupt may be masked for transmit since the end-ack would * always occur before the wait bound expires. */ if (IntrStatus & (XDC_IXR_PKT_THRESHOLD_MASK | XDC_IXR_PKT_WAIT_BOUND_MASK | XDC_IXR_SG_END_MASK)) { XStatus Result = XST_SUCCESS; u32 NumFrames; u32 NumProcessed; u32 NumBuffers; u32 NumBytes; u32 IsLast; XBufDescriptor *FirstBdPtr; XBufDescriptor *BdPtr; /* * Get the number of unserviced packets */ NumFrames = XDmaChannel_GetPktCount(&InstancePtr->SendChannel); for (NumProcessed = 0; NumProcessed < NumFrames; NumProcessed++) { IsLast = FALSE; FirstBdPtr = NULL; NumBuffers = 0; NumBytes = 0; /* * For each frame, traverse the descriptor list and look for * errors. On the last one in the frame, make the callback. */ while (!IsLast) { Result = XDmaChannel_GetDescriptor(&InstancePtr-> SendChannel, &BdPtr); if (Result != XST_SUCCESS) { /* * An error getting a buffer descriptor from the list. * This should not happen, but if it does, report it to * the error callback and break out of the loops to service * other interrupts */ InstancePtr->ErrorHandler(InstancePtr-> ErrorRef, Result); break; } /* * Keep a pointer to the first descriptor in the list and * check the device status for errors. The device status is * only available in the first descriptor of a packet. */ if (FirstBdPtr == NULL) { u32 XmitStatus; FirstBdPtr = BdPtr; XmitStatus = XBufDescriptor_GetDeviceStatus (BdPtr); if (XmitStatus & XEM_TSR_EXCESS_DEFERRAL_MASK) { InstancePtr->Stats. XmitExcessDeferral++; } if (XmitStatus & XEM_TSR_LATE_COLLISION_MASK) { InstancePtr->Stats. XmitLateCollisionErrors++; } } NumBytes += XBufDescriptor_GetLength(BdPtr); /* * Check to see if this is the last descriptor in the frame, * and if so, set the IsLast flag to get out of the loop. The * transmit channel must check the last bit in the control * word, not the status word (the DMA engine does not update * the last bit in the status word for the transmit direction). */ if (XBufDescriptor_IsLastControl(BdPtr)) { IsLast = TRUE; } /* * Bump the number of buffers in this packet */ NumBuffers++; } /* end while loop */ /* * Check for error that occurred inside the while loop, and break * out of the for loop if there was one so other interrupts can * be serviced. */ if (Result != XST_SUCCESS) { break; } InstancePtr->Stats.XmitFrames++; InstancePtr->Stats.XmitBytes += NumBytes; /* * Make the callback to the upper layers, passing it the first * descriptor in the packet and the number of descriptors in the * packet. */ InstancePtr->SgSendHandler(InstancePtr->SgSendRef, FirstBdPtr, NumBuffers); /* * Decrement the packet count register to reflect the fact we * just processed a packet */ XDmaChannel_DecrementPktCount(&InstancePtr-> SendChannel); } /* end for loop */ /* * If the interrupt was an end-ack, check the descriptor list again to * see if it is empty. If not, go ahead and restart the scatter-gather * channel. This is to fix a possible race condition where, on transmit, * the driver attempted to start a scatter-gather channel that was * already started, which resulted in no action from the XDmaChannel * component. But, just after the XDmaChannel component saw that the * hardware was already started, the hardware stopped because it * reached the end of the list. In that case, this interrupt is * generated and we can restart the hardware here. */ if (IntrStatus & XDC_IXR_SG_END_MASK) { /* * Ignore the return status since we know the list exists and we * don't care if the list is empty or the channel is already started. */ (void) XDmaChannel_SgStart(&InstancePtr->SendChannel); } } /* * All interrupts are handled (except the error below) so acknowledge * (clear) the interrupts by writing the value read above back to the status * register. The packet count interrupt must be acknowledged after the * decrement, otherwise it will come right back. We clear the interrupts * before we handle the error interrupt because the ErrorHandler should * result in a reset, which clears the interrupt status register. So we * don't want to toggle the interrupt back on by writing the interrupt * status register with an old value after a reset. */ XDmaChannel_SetIntrStatus(&InstancePtr->SendChannel, IntrStatus); /* * Check for DMA errors and call the error callback function if an error * occurred (DMA bus or timeout error), which should result in a reset of * the device by the upper layer software. */ if (IntrStatus & XDC_IXR_DMA_ERROR_MASK) { InstancePtr->Stats.DmaErrors++; InstancePtr->ErrorHandler(InstancePtr->ErrorRef, XST_DMA_ERROR); } } /*****************************************************************************/ /* * * Handle an interrupt from the Ethernet MAC when configured with scatter-gather * DMA. The only interrupts handled in this case are errors. * * @param InstancePtr is a pointer to the XEmac instance to be worked on. * * @return * * None. * * @note * * None. * ******************************************************************************/ static void HandleEmacDmaIntr(XEmac * InstancePtr) { u32 IntrStatus; /* * When configured with DMA, the EMAC generates interrupts only when errors * occur. We clear the interrupts immediately so that any latched status * interrupt bits will reflect the true status of the device, and so any * pulsed interrupts (non-status) generated during the Isr will not be lost. */ IntrStatus = XIIF_V123B_READ_IISR(InstancePtr->BaseAddress); XIIF_V123B_WRITE_IISR(InstancePtr->BaseAddress, IntrStatus); /* * Check the MAC for errors */ XEmac_CheckEmacError(InstancePtr, IntrStatus); }