Beispiel #1
0
/**
*
* Add a descriptor, with an attached empty buffer, into the receive descriptor
* list. The buffer attached to the descriptor must be 32-bit aligned if using
* the OPB Ethernet core and 64-bit aligned if using the PLB Ethernet core.
* This function is used by the upper layer software during initialization when
* first setting up the receive descriptors, and also during reception of frames
* to replace filled buffers with empty buffers. This function can be called
* when the device is started or stopped. Note that it does start the scatter-
* gather DMA engine.  Although this is not necessary during initialization, it
* is not a problem during initialization because the MAC receiver is not yet
* started.
*
* The buffer attached to the descriptor must be aligned on both the front end
* and the back end.
*
* Notification of received frames are done asynchronously through the receive
* callback function.
*
* @param InstancePtr is a pointer to the XEmac instance to be worked on.
* @param BdPtr is a pointer to the buffer descriptor that will be added to the
*        descriptor list.
*
* @return
*
* - XST_SUCCESS if a descriptor was successfully returned to the driver
* - XST_NOT_SGDMA if the device is not in scatter-gather DMA mode
* - XST_DMA_SG_LIST_FULL if the receive descriptor list is full
* - XST_DMA_SG_BD_LOCKED if the DMA channel cannot insert the descriptor into
*   the list because a locked descriptor exists at the insert point.
* - XST_DMA_SG_NOTHING_TO_COMMIT if even after inserting a descriptor into the
*   list, the DMA channel believes there are no new descriptors to commit.
*
* @internal
*
* A status that should never be returned from this function, although
* the code is set up to handle it, is XST_DMA_SG_NO_LIST. Starting the device
* requires a list to be created, and this function requires the device to be
* started.
*
******************************************************************************/
XStatus XEmac_SgRecv(XEmac * InstancePtr, XBufDescriptor * BdPtr)
{
	XStatus Result;
	u32 BdControl;

	XASSERT_NONVOID(InstancePtr != NULL);
	XASSERT_NONVOID(BdPtr != NULL);
	XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY);

	/*
	 * Be sure the device is configured for scatter-gather DMA
	 */
	if (!XEmac_mIsSgDma(InstancePtr)) {
		return XST_NOT_SGDMA;
	}

	/*
	 * Set some descriptor control word defaults (destination address increment
	 * and local source address) and the source address (the FIFO). These are
	 * the same for every receive descriptor.
	 */
	BdControl = XBufDescriptor_GetControl(BdPtr);
	XBufDescriptor_SetControl(BdPtr, BdControl | XEM_DFT_RECV_BD_MASK);
	XBufDescriptor_SetSrcAddress(BdPtr,
				     InstancePtr->PhysAddress +
				     XEM_PFIFO_RXDATA_OFFSET);

	/*
	 * Put the descriptor into the channel's descriptor list and commit.
	 * Although this function is likely called within interrupt context, there
	 * is the possibility that the upper layer software queues it to a task.
	 * In this case, a critical section is needed here to protect shared data
	 * in the DMA component.
	 */
	XIIF_V123B_GINTR_DISABLE(InstancePtr->BaseAddress);

	Result = XDmaChannel_PutDescriptor(&InstancePtr->RecvChannel, BdPtr);
	if (Result != XST_SUCCESS) {
		XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress);
		return Result;
	}

	Result = XDmaChannel_CommitPuts(&InstancePtr->RecvChannel);
	if (Result != XST_SUCCESS) {
		XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress);
		return Result;
	}

	/*
	 * Start the DMA channel. Ignore the return status since we know the list
	 * exists and has at least one entry and we don't care if the channel is
	 * already started. The DMA component accesses data here that can be
	 * modified at interrupt or task levels, so a critical section is required.
	 */
	(void)XDmaChannel_SgStart(&InstancePtr->RecvChannel);

	XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress);

	return XST_SUCCESS;
}
Beispiel #2
0
/*
*
* 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
*               - Decrement the packet counter by one
*       - Call the scatter-gather receive callback function
*       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)
{
	XStatus Result;
	u32 IntrStatus;
	u32 NumBds;
	u32 PacketsLeft;
	XBufDescriptor *BdHeadPtr;
	XBufDescriptor *BdPtr;

	/*
	 * 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)) {
		/* Get the number of packets that need processing */
		PacketsLeft =
		    XDmaChannel_GetPktCount(&InstancePtr->RecvChannel);

		if (PacketsLeft) {
			/* Get the buffer descriptor at the head of the list */
			Result =
			    XDmaChannel_GetDescriptor(&InstancePtr->RecvChannel,
						      &BdHeadPtr);
			BdPtr = BdHeadPtr;
			NumBds = 0;

			/* Loop until all packets have been pulled or an error occurs */
			while (1) {
				NumBds++;

				/*
				 * 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 loop to service
				 * other interrupts.
				 */
				if (Result != XST_SUCCESS) {
					InstancePtr->ErrorHandler(InstancePtr->
								  ErrorRef,
								  Result);
					break;
				}

				/* Bump statistics */
				InstancePtr->Stats.RecvBytes +=
				    XBufDescriptor_GetLength(BdPtr);

				/* Have all BDs been read for this packet */
				if (XBufDescriptor_IsLastStatus(BdPtr)) {
					/*
					 * Decrement the packet count register to reflect the fact
					 * we just processed a packet
					 */
					XDmaChannel_DecrementPktCount
					    (&InstancePtr->RecvChannel);

					/* Bump statistics */
					InstancePtr->Stats.RecvFrames++;

					/* Test loop exit condition */
					if (--PacketsLeft == 0) {
						break;
					}
				}

				/* Get the next buffer descriptor in the list */
				Result =
				    XDmaChannel_GetDescriptor(&InstancePtr->
							      RecvChannel,
							      &BdPtr);
			}	/* while */

			/*
			 * 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) {
				/*
				 * Make the callback to the upper layers, passing it the first
				 * descriptor in the first packet and the number of descriptors
				 * in the list.
				 */
				InstancePtr->SgRecvHandler(InstancePtr->
							   SgRecvRef, BdHeadPtr,
							   NumBds);
			}
		}

		/* if (PacketsLeft) */
		/*
		 * 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);
	}
}
Beispiel #3
0
/**
*
* Send an Ethernet frame using scatter-gather DMA. The caller attaches the
* frame to one or more buffer descriptors, then calls this function once for
* each descriptor. The caller is responsible for allocating and setting up the
* descriptor. An entire Ethernet frame may or may not be contained within one
* descriptor.  This function simply inserts the descriptor into the scatter-
* gather engine's transmit list. The caller is responsible for providing mutual
* exclusion to guarantee that a frame is contiguous in the transmit list. The
* buffer attached to the descriptor must be 32-bit aligned if using the OPB
* Ethernet core and 64-bit aligned if using the PLB Ethernet core.
*
* The driver updates the descriptor with the device control register before
* being inserted into the transmit list.  If this is the last descriptor in
* the frame, the inserts are committed, which means the descriptors for this
* frame are now available for transmission.
*
* It is assumed that the upper layer software supplies a correctly formatted
* Ethernet frame, including the destination and source addresses, the
* type/length field, and the data field.  It is also assumed that upper layer
* software does not append FCS at the end of the frame.
*
* This call is non-blocking.  Notification of error or successful transmission
* is done asynchronously through the send or error callback function.
*
* @param InstancePtr is a pointer to the XEmac instance to be worked on.
* @param BdPtr is the address of a descriptor to be inserted into the transmit
*        ring.
* @param Delay indicates whether to start the scatter-gather DMA channel
*        immediately, or whether to wait. This allows the user to build up a
*        list of more than one descriptor before starting the transmission of
*        the packets, which allows the application to keep up with DMA and have
*        a constant stream of frames being transmitted. Use XEM_SGDMA_NODELAY or
*        XEM_SGDMA_DELAY, defined in xemac.h, as the value of this argument. If
*        the user chooses to delay and build a list, the user must call this
*        function with the XEM_SGDMA_NODELAY option or call XEmac_Start() to
*        kick off the tranmissions.
*
* @return
*
* - XST_SUCCESS if the buffer was successfull sent
* - XST_DEVICE_IS_STOPPED if the Ethernet MAC has not been started yet
* - XST_NOT_SGDMA if the device is not in scatter-gather DMA mode
* - XST_DMA_SG_LIST_FULL if the descriptor list for the DMA channel is full
* - XST_DMA_SG_BD_LOCKED if the DMA channel cannot insert the descriptor into
*   the list because a locked descriptor exists at the insert point
* - XST_DMA_SG_NOTHING_TO_COMMIT if even after inserting a descriptor into the
*   list, the DMA channel believes there are no new descriptors to commit. If
*   this is ever encountered, there is likely a thread mutual exclusion problem
*   on transmit.
*
* @note
*
* This function is not thread-safe. The user must provide mutually exclusive
* access to this function if there are to be multiple threads that can call it.
*
* @internal
*
* A status that should never be returned from this function, although
* the code is set up to handle it, is XST_DMA_SG_NO_LIST. Starting the device
* requires a list to be created, and this function requires the device to be
* started.
*
******************************************************************************/
XStatus XEmac_SgSend(XEmac * InstancePtr, XBufDescriptor * BdPtr, int Delay)
{
	XStatus Result;
	u32 BdControl;

	XASSERT_NONVOID(InstancePtr != NULL);
	XASSERT_NONVOID(BdPtr != NULL);
	XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY);

	/*
	 * Be sure the device is configured for scatter-gather DMA, then be sure
	 * it is started.
	 */
	if (!XEmac_mIsSgDma(InstancePtr)) {
		return XST_NOT_SGDMA;
	}

	/*
	 * Set some descriptor control word defaults (source address increment
	 * and local destination address) and the destination address
	 * (the FIFO).  These are the same for every transmit descriptor.
	 */
	BdControl = XBufDescriptor_GetControl(BdPtr);
	XBufDescriptor_SetControl(BdPtr, BdControl | XEM_DFT_SEND_BD_MASK);

	XBufDescriptor_SetDestAddress(BdPtr,
				      InstancePtr->PhysAddress +
				      XEM_PFIFO_TXDATA_OFFSET);

	/*
	 * Put the descriptor in the send list. The DMA component accesses data
	 * here that can also be modified in interrupt context, so a critical
	 * section is required.
	 */
	XIIF_V123B_GINTR_DISABLE(InstancePtr->BaseAddress);

	Result = XDmaChannel_PutDescriptor(&InstancePtr->SendChannel, BdPtr);
	if (Result != XST_SUCCESS) {
		XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress);
		return Result;
	}

	/*
	 * If this is the last buffer in the frame, commit the inserts and start
	 * the DMA engine if necessary
	 */
	if (XBufDescriptor_IsLastControl(BdPtr)) {
		Result = XDmaChannel_CommitPuts(&InstancePtr->SendChannel);
		if (Result != XST_SUCCESS) {
			XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress);
			return Result;
		}

		if (Delay == XEM_SGDMA_NODELAY) {
			/*
			 * Start the DMA channel. Ignore the return status since we know the
			 * list exists and has at least one entry and we don't care if the
			 * channel is already started.  The DMA component accesses data here
			 * that can be modified at interrupt or task levels, so a critical
			 * section is required.
			 */
			(void)XDmaChannel_SgStart(&InstancePtr->SendChannel);
		}
	}

	XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress);

	return XST_SUCCESS;
}
/*
*
* 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);
	}
}