| /****************************************************************************** |
| * |
| * 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. |
| * |
| * <pre> |
| * 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. |
| * </pre> |
| * |
| ******************************************************************************/ |
| |
| /***************************** 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); |
| } |