/* $Id: xemac_polled.c,v 1.1 2003/09/26 22:04:52 moleres Exp $ */
/******************************************************************************
*
*       XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS"
*       AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND
*       SOLUTIONS FOR XILINX DEVICES.  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 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.
*
*       (c) Copyright 2003 Xilinx Inc.
*       All rights reserved.
*
******************************************************************************/
/*****************************************************************************/
/**
*
* @file xemac_polled.c
*
* Contains functions used when the driver is in polled mode. Use the
* XEmac_SetOptions() function to put the driver into polled mode.
*
* <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
* 1.00d rpm  09/26/03 New version includes support PLB Ethernet and v2.00a of
*                     the packet fifo driver.
* </pre>
*
******************************************************************************/

/***************************** Include Files *********************************/

#include "xbasic_types.h"
#include "xemac_i.h"
#include "xio.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 ******************************/


/*****************************************************************************/
/**
*
* Send an Ethernet frame in polled mode.  The device/driver must be in polled
* mode before calling this function. The driver writes the frame directly to
* the MAC's packet FIFO, then enters a loop checking the device status for
* completion or error. Statistics are updated if an error occurs. The buffer
* to be sent must be 32-bit aligned.
*
* 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.
*
* @param InstancePtr is a pointer to the XEmac instance to be worked on.
* @param BufPtr is a pointer to a aligned buffer containing the Ethernet
*        frame to be sent.
* @param ByteCount is the size of the Ethernet frame.
*
* @return
*
* - XST_SUCCESS if the frame was sent successfully
* - XST_DEVICE_IS_STOPPED if the device has not yet been started
* - XST_NOT_POLLED if the device is not in polled mode
* - XST_FIFO_NO_ROOM if there is no room in the EMAC's length FIFO for this frame
* - XST_FIFO_ERROR if the FIFO was overrun or underrun. This error is critical
*   and requires the caller to reset the device.
* - XST_EMAC_COLLISION if the send failed due to excess deferral or late
*   collision
*
* @note
*
* There is the possibility that this function will not return if the hardware
* is broken (i.e., it never sets the status bit indicating that transmission is
* done). If this is of concern to the user, the user should provide protection
* from this problem - perhaps by using a different timer thread to monitor the
* PollSend thread. On a 10Mbps MAC, it takes about 1.21 msecs to transmit a
* maximum size Ethernet frame (1518 bytes). On a 100Mbps MAC, it takes about
* 121 usecs to transmit a maximum size Ethernet frame.
*
* @internal
*
* The EMAC uses FIFOs behind its length and status registers. For this reason,
* it is important to keep the length, status, and data FIFOs in sync when
* reading or writing to them.
*
******************************************************************************/
XStatus XEmac_PollSend(XEmac *InstancePtr, Xuint8 *BufPtr, Xuint32 ByteCount)
{
    Xuint32 IntrStatus;
    Xuint32 XmitStatus;
    XStatus Result;

    XASSERT_NONVOID(InstancePtr != XNULL);
    XASSERT_NONVOID(BufPtr != XNULL);
    XASSERT_NONVOID(ByteCount > XEM_HDR_SIZE);   /* send at least 1 byte */
    XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY);

    /*
     * Be sure the device is configured for polled mode and it is started
     */
    if (!InstancePtr->IsPolled)
    {
        return XST_NOT_POLLED;
    }

    if (InstancePtr->IsStarted != XCOMPONENT_IS_STARTED)
    {
        return XST_DEVICE_IS_STOPPED;
    }

    /*
     * Check for overruns and underruns for the transmit status and length
     * FIFOs and make sure the send packet FIFO is not deadlocked. Any of these
     * conditions is bad enough that we do not want to continue. The upper layer
     * software should reset the device to resolve the error.
     */
    IntrStatus = XIIF_V123B_READ_IISR(InstancePtr->BaseAddress);

    /*
     * Overrun errors
     */
    if (IntrStatus & (XEM_EIR_XMIT_SFIFO_OVER_MASK |
        XEM_EIR_XMIT_LFIFO_OVER_MASK))
    {
        InstancePtr->Stats.XmitOverrunErrors++;
        InstancePtr->Stats.FifoErrors++;
        return XST_FIFO_ERROR;
    }

    /*
     * Underrun errors
     */
    if (IntrStatus & (XEM_EIR_XMIT_SFIFO_UNDER_MASK |
        XEM_EIR_XMIT_LFIFO_UNDER_MASK))
    {
        InstancePtr->Stats.XmitUnderrunErrors++;
        InstancePtr->Stats.FifoErrors++;
        return XST_FIFO_ERROR;
    }

    if (XPF_V200A_IS_DEADLOCKED(&InstancePtr->SendFifo))
    {
        InstancePtr->Stats.FifoErrors++;
        return XST_FIFO_ERROR;
    }

    /*
     * Before writing to the data FIFO, make sure the length FIFO is not
     * full.  The data FIFO might not be full yet even though the length FIFO
     * is. This avoids an overrun condition on the length FIFO and keeps the
     * FIFOs in sync.
     */
    if (IntrStatus & XEM_EIR_XMIT_LFIFO_FULL_MASK)
    {
        /*
         * Clear the latched LFIFO_FULL bit so next time around the most
         * current status is represented
         */
        XIIF_V123B_WRITE_IISR(InstancePtr->BaseAddress,
                               XEM_EIR_XMIT_LFIFO_FULL_MASK);
        return XST_FIFO_NO_ROOM;
    }

    /*
     * This is a non-blocking write. The packet FIFO returns an error if there
     * is not enough room in the FIFO for this frame.
     */
    Result = XPacketFifoV200a_Write(&InstancePtr->SendFifo, BufPtr, ByteCount);
    if (Result != XST_SUCCESS)
    {
        return Result;
    }

    /*
     * Loop on the MAC's status to wait for any pause to complete.
     */
    IntrStatus = XIIF_V123B_READ_IISR(InstancePtr->BaseAddress);

    while ((IntrStatus & XEM_EIR_XMIT_PAUSE_MASK) != 0)
    {
        IntrStatus = XIIF_V123B_READ_IISR(InstancePtr->BaseAddress);
        /*
        * Clear the pause status from the transmit status register
        */
        XIIF_V123B_WRITE_IISR(InstancePtr->BaseAddress,
                              IntrStatus & XEM_EIR_XMIT_PAUSE_MASK);
    }

    /*
     * Set the MAC's transmit packet length register to tell it to transmit
     */
    XIo_Out32(InstancePtr->BaseAddress + XEM_TPLR_OFFSET, ByteCount);

    /*
     * Loop on the MAC's status to wait for the transmit to complete. The
     * transmit status is in the FIFO when the XMIT_DONE bit is set.
     */
    do
    {
        IntrStatus = XIIF_V123B_READ_IISR(InstancePtr->BaseAddress);
    }
    while ((IntrStatus & XEM_EIR_XMIT_DONE_MASK) == 0);

    XmitStatus = XIo_In32(InstancePtr->BaseAddress + XEM_TSR_OFFSET);

    InstancePtr->Stats.XmitFrames++;
    InstancePtr->Stats.XmitBytes += ByteCount;

    /*
     * Check for various errors, bump statistics, and return an error status.
     */

    /*
     * Overrun errors
     */
    if (IntrStatus & (XEM_EIR_XMIT_SFIFO_OVER_MASK |
        XEM_EIR_XMIT_LFIFO_OVER_MASK))
    {
        InstancePtr->Stats.XmitOverrunErrors++;
        InstancePtr->Stats.FifoErrors++;
        return XST_FIFO_ERROR;
    }

    /*
     * Underrun errors
     */
    if (IntrStatus & (XEM_EIR_XMIT_SFIFO_UNDER_MASK |
        XEM_EIR_XMIT_LFIFO_UNDER_MASK))
    {
        InstancePtr->Stats.XmitUnderrunErrors++;
        InstancePtr->Stats.FifoErrors++;
        return XST_FIFO_ERROR;
    }

    /*
     * Clear the interrupt status register of transmit statuses
     */
    XIIF_V123B_WRITE_IISR(InstancePtr->BaseAddress,
                           IntrStatus & XEM_EIR_XMIT_ALL_MASK);

    /*
     * Collision errors are stored in the transmit status register
     * instead of the interrupt status register
     */
    if (XmitStatus & XEM_TSR_EXCESS_DEFERRAL_MASK)
    {
        InstancePtr->Stats.XmitExcessDeferral++;
        return XST_EMAC_COLLISION_ERROR;
    }

    if (XmitStatus & XEM_TSR_LATE_COLLISION_MASK)
    {
        InstancePtr->Stats.XmitLateCollisionErrors++;
        return XST_EMAC_COLLISION_ERROR;
    }

    return XST_SUCCESS;
}

/*****************************************************************************/
/**
*
* Receive an Ethernet frame in polled mode. The device/driver must be in polled
* mode before calling this function. The driver receives the frame directly
* from the MAC's packet FIFO. This is a non-blocking receive, in that if there
* is no frame ready to be received at the device, the function returns with an
* error. The MAC's error status is not checked, so statistics are not updated
* for polled receive. The buffer into which the frame will be received must be
* 32-bit aligned.
*
* @param InstancePtr is a pointer to the XEmac instance to be worked on.
* @param BufPtr is a pointer to a aligned buffer into which the received
*        Ethernet frame will be copied.
* @param ByteCountPtr is both an input and an output parameter. It is a pointer
*        to a 32-bit word that contains the size of the buffer on entry into the
*        function and the size the received frame on return from the function.
*
* @return
*
* - XST_SUCCESS if the frame was sent successfully
* - XST_DEVICE_IS_STOPPED if the device has not yet been started
* - XST_NOT_POLLED if the device is not in polled mode
* - XST_NO_DATA if there is no frame to be received from the FIFO
* - XST_BUFFER_TOO_SMALL if the buffer to receive the frame is too small for
*   the frame waiting in the FIFO.
*
* @note
*
* Input buffer must be big enough to hold the largest Ethernet frame. 
*
* @internal
*
* The EMAC uses FIFOs behind its length and status registers. For this reason,
* it is important to keep the length, status, and data FIFOs in sync when
* reading or writing to them.
*
******************************************************************************/
XStatus XEmac_PollRecv(XEmac *InstancePtr, Xuint8 *BufPtr,
                       Xuint32 *ByteCountPtr)
{
    XStatus Result;
    Xuint32 PktLength;
    Xuint32 IntrStatus;

    XASSERT_NONVOID(InstancePtr != XNULL);
    XASSERT_NONVOID(BufPtr != XNULL);
    XASSERT_NONVOID(ByteCountPtr != XNULL);
    XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY);

    /*
     * Be sure the device is configured for polled mode and it is started
     */
    if (!InstancePtr->IsPolled)
    {
        return XST_NOT_POLLED;
    }

    if (InstancePtr->IsStarted != XCOMPONENT_IS_STARTED)
    {
        return XST_DEVICE_IS_STOPPED;
    }

    /*
     * Make sure the buffer is big enough to hold the maximum frame size.
     * We need to do this because as soon as we read the MAC's packet length
     * register, which is actually a FIFO, we remove that length from the
     * FIFO.  We do not want to read the length FIFO without also reading the
     * data FIFO since this would get the FIFOs out of sync.  So we have to
     * make this restriction.
     */
    if (*ByteCountPtr < XEM_MAX_FRAME_SIZE)
    {
        return XST_BUFFER_TOO_SMALL;
    }

    /*
     * First check for packet FIFO deadlock and return an error if it has
     * occurred. A reset by the caller is necessary to correct this problem.
     */
    if (XPF_V200A_IS_DEADLOCKED(&InstancePtr->RecvFifo))
    {
        InstancePtr->Stats.FifoErrors++;
        return XST_FIFO_ERROR;
    }

    /*
     * Get the interrupt status to know what happened (whether an error occurred
     * and/or whether frames have been received successfully). When clearing the
     * intr status register, clear only statuses that pertain to receive.
     */
    IntrStatus = XIIF_V123B_READ_IISR(InstancePtr->BaseAddress);
    XIIF_V123B_WRITE_IISR(InstancePtr->BaseAddress,
                           IntrStatus & XEM_EIR_RECV_ALL_MASK);

    /*
     * Check receive errors and bump statistics so the caller will have a clue
     * as to why data may not have been received. We continue on if an error
     * occurred since there still may be frames that were received successfully.
     */
    if (IntrStatus & (XEM_EIR_RECV_LFIFO_OVER_MASK |
        XEM_EIR_RECV_DFIFO_OVER_MASK))
    {
        InstancePtr->Stats.RecvOverrunErrors++;
        InstancePtr->Stats.FifoErrors++;
    }

    if (IntrStatus & XEM_EIR_RECV_LFIFO_UNDER_MASK)
    {
        InstancePtr->Stats.RecvUnderrunErrors++;
        InstancePtr->Stats.FifoErrors++;
    }

    /*
     * General receive errors
     */
    if (IntrStatus & XEM_EIR_RECV_ERROR_MASK)
    {
        if (IntrStatus & XEM_EIR_RECV_MISSED_FRAME_MASK)
        {
            InstancePtr->Stats.RecvMissedFrameErrors =
                    XIo_In32(InstancePtr->BaseAddress + XEM_RMFC_OFFSET);
        }

        if (IntrStatus & XEM_EIR_RECV_COLLISION_MASK)
        {
            InstancePtr->Stats.RecvCollisionErrors =
                    XIo_In32(InstancePtr->BaseAddress + XEM_RCC_OFFSET);
        }

        if (IntrStatus & XEM_EIR_RECV_FCS_ERROR_MASK)
        {
            InstancePtr->Stats.RecvFcsErrors =
                    XIo_In32(InstancePtr->BaseAddress + XEM_RFCSEC_OFFSET);
        }

        if (IntrStatus & XEM_EIR_RECV_LEN_ERROR_MASK)
        {
            InstancePtr->Stats.RecvLengthFieldErrors++;
        }

        if (IntrStatus & XEM_EIR_RECV_SHORT_ERROR_MASK)
        {
            InstancePtr->Stats.RecvShortErrors++;
        }

        if (IntrStatus & XEM_EIR_RECV_LONG_ERROR_MASK)
        {
            InstancePtr->Stats.RecvLongErrors++;
        }

        if (IntrStatus & XEM_EIR_RECV_ALIGN_ERROR_MASK)
        {
            InstancePtr->Stats.RecvAlignmentErrors =
                    XIo_In32(InstancePtr->BaseAddress + XEM_RAEC_OFFSET);
        }
    }

    /*
     * Before reading from the length FIFO, make sure the length FIFO is not
     * empty. We could cause an underrun error if we try to read from an
     * empty FIFO.
     */
    if ((IntrStatus & XEM_EIR_RECV_DONE_MASK) == 0)
    {
        return XST_NO_DATA;
    }

    /*
     * Determine, from the MAC, the length of the next packet available
     * in the data FIFO (there should be a non-zero length here)
     */
    PktLength = XIo_In32(InstancePtr->BaseAddress + XEM_RPLR_OFFSET);
    if (PktLength == 0)
    {
        return XST_NO_DATA;
    }

    /*
     * Write the RECV_DONE bit in the status register to clear it. This bit
     * indicates the RPLR is non-empty, and we know it's set at this point.
     * We clear it so that subsequent entry into this routine will reflect the
     * current status. This is done because the non-empty bit is latched in the
     * IPIF, which means it may indicate a non-empty condition even though
     * there is something in the FIFO.
     */
    XIIF_V123B_WRITE_IISR(InstancePtr->BaseAddress, XEM_EIR_RECV_DONE_MASK);

    /*
     * We assume that the MAC never has a length bigger than the largest
     * Ethernet frame, so no need to make another check here.
     */

    /*
     * This is a non-blocking read. The FIFO returns an error if there is
     * not at least the requested amount of data in the FIFO.
     */
    Result = XPacketFifoV200a_Read(&InstancePtr->RecvFifo, BufPtr, PktLength);
    if (Result != XST_SUCCESS)
    {
        return Result;
    }

    InstancePtr->Stats.RecvFrames++;
    InstancePtr->Stats.RecvBytes += PktLength;

    *ByteCountPtr = PktLength;

    return XST_SUCCESS;
}
