Newer
Older
barebox / arch / m68k / mach-mcfv4e / fec.c
/*
 * File:    fec.c
 * Purpose: Driver for the Fast Ethernet Controller (FEC)
 *
 * Notes:
 */
#include <common.h>
#include <linux/types.h>

#include <mach/mcf54xx-regs.h>
#include <proc/mcdapi/MCD_dma.h>
#include <proc/net/net.h>
#include <proc/fecbd.h>
#include <proc/fec.h>
#include <proc/dma_utils.h>


#define TRUE 1
#define FALSE 0
#define ASSERT(x) if (!(x)) hang();
#define nop() __asm__ __volatile__("nop\n")


FEC_EVENT_LOG fec_log[2];

/*
 * Write a value to a PHY's MII register.
 *
 * Parameters:
 *  ch          FEC channel
 *  phy_addr    Address of the PHY.
 *  reg_addr    Address of the register in the PHY.
 *  data        Data to be written to the PHY register.
 *
 * Return Values:
 *  1 on failure
 *  0 on success.
 *
 * Please refer to your PHY manual for registers and their meanings.
 * mii_write() polls for the FEC's MII interrupt event (which should
 * be masked from the interrupt handler) and clears it. If after a
 * suitable amount of time the event isn't triggered, a value of 0
 * is returned.
 */
int
fec_mii_write(uint8_t ch, uint8_t phy_addr, uint8_t reg_addr, uint16_t data)
{
    int timeout;
    uint32_t eimr;

    ASSERT(ch == 0 || ch == 1);

    /*
     * Clear the MII interrupt bit
     */
    MCF_FEC_EIR(ch) = MCF_FEC_EIR_MII;

    /*
     * Write to the MII Management Frame Register to kick-off
     * the MII write
     */
    MCF_FEC_MMFR(ch) = 0
        | MCF_FEC_MMFR_ST_01
        | MCF_FEC_MMFR_OP_WRITE
        | MCF_FEC_MMFR_PA(phy_addr)
        | MCF_FEC_MMFR_RA(reg_addr)
        | MCF_FEC_MMFR_TA_10
        | MCF_FEC_MMFR_DATA(data);

    /*
     * Mask the MII interrupt
     */
    eimr = MCF_FEC_EIMR(ch);
    MCF_FEC_EIMR(ch) &= ~MCF_FEC_EIMR_MII;

    /*
     * Poll for the MII interrupt (interrupt should be masked)
     */
    for (timeout = 0; timeout < FEC_MII_TIMEOUT; timeout++)
    {
        if (MCF_FEC_EIR(ch) & MCF_FEC_EIR_MII)
            break;
    }
    if(timeout == FEC_MII_TIMEOUT)
        return 1;

    /*
     * Clear the MII interrupt bit
     */
    MCF_FEC_EIR(ch) = MCF_FEC_EIR_MII;

    /*
     * Restore the EIMR
     */
    MCF_FEC_EIMR(ch) = eimr;

    return 0;
}

/*
 * Read a value from a PHY's MII register.
 *
 * Parameters:
 *  ch          FEC channel
 *  phy_addr    Address of the PHY.
 *  reg_addr    Address of the register in the PHY.
 *  data        Pointer to storage for the Data to be read
 *              from the PHY register (passed by reference)
 *
 * Return Values:
 *  1 on failure
 *  0 on success.
 *
 * Please refer to your PHY manual for registers and their meanings.
 * mii_read() polls for the FEC's MII interrupt event (which should
 * be masked from the interrupt handler) and clears it. If after a
 * suitable amount of time the event isn't triggered, a value of 0
 * is returned.
 */
int
fec_mii_read(uint8_t ch, uint8_t phy_addr, uint8_t reg_addr, uint16_t *data)
{
    int timeout;

    ASSERT(ch == 0 || ch == 1);

    /*
     * Clear the MII interrupt bit
     */
    MCF_FEC_EIR(ch) = MCF_FEC_EIR_MII;

    /*
     * Write to the MII Management Frame Register to kick-off
     * the MII read
     */
    MCF_FEC_MMFR(ch) = 0
        | MCF_FEC_MMFR_ST_01
        | MCF_FEC_MMFR_OP_READ
        | MCF_FEC_MMFR_PA(phy_addr)
        | MCF_FEC_MMFR_RA(reg_addr)
        | MCF_FEC_MMFR_TA_10;

    /*
     * Poll for the MII interrupt (interrupt should be masked)
     */
    for (timeout = 0; timeout < FEC_MII_TIMEOUT; timeout++)
    {
        if (MCF_FEC_EIR(ch) & MCF_FEC_EIR_MII)
            break;
    }

    if(timeout == FEC_MII_TIMEOUT)
        return 1;

    /*
     * Clear the MII interrupt bit
     */
    MCF_FEC_EIR(ch) = MCF_FEC_EIR_MII;

    *data = (uint16_t)(MCF_FEC_MMFR(ch) & 0x0000FFFF);

    return 0;
}

/*
 * Initialize the MII interface controller
 *
 * Parameters:
 *  ch      FEC channel
 *  sys_clk System Clock Frequency (in MHz)
 */
void
fec_mii_init(uint8_t ch, uint32_t sys_clk)
{
    ASSERT(ch == 0 || ch == 1);

    /*
     * Initialize the MII clock (EMDC) frequency
     *
     * Desired MII clock is 2.5MHz
     * MII Speed Setting = System_Clock / (2.5MHz * 2)
     * (plus 1 to make sure we round up)
     */
    MCF_FEC_MSCR(ch) = MCF_FEC_MSCR_MII_SPEED((sys_clk/5)+1);
}

/* Initialize the MIB counters
 *
 * Parameters:
 *  ch      FEC channel
 */
void
fec_mib_init(uint8_t ch)
{
    ASSERT(ch == 0 || ch == 1);
//To do
}

/* Display the MIB counters
 *
 * Parameters:
 *  ch      FEC channel
 */
void
fec_mib_dump(uint8_t ch)
{
    ASSERT(ch == 0 || ch == 1);
//To do
}

/* Initialize the FEC log
 *
 * Parameters:
 *  ch      FEC channel
 */
void
fec_log_init(uint8_t ch)
{
    ASSERT(ch == 0 || ch == 1);
    memset(&fec_log[ch],0,sizeof(FEC_EVENT_LOG));
}

/* Display the FEC log
 *
 * Parameters:
 *  ch      FEC channel
 */
void
fec_log_dump(uint8_t ch)
{
    ASSERT(ch == 0 || ch == 1);
    printf("\n   FEC%d Log\n---------------\n",ch);
    printf("Total: %4d\n",fec_log[ch].total);
    printf("hberr: %4d\n",fec_log[ch].hberr);
    printf("babr:  %4d\n",fec_log[ch].babr);
    printf("babt:  %4d\n",fec_log[ch].babt);
    printf("gra:   %4d\n",fec_log[ch].gra);
    printf("txf:   %4d\n",fec_log[ch].txf);
    printf("mii:   %4d\n",fec_log[ch].mii);
    printf("lc:    %4d\n",fec_log[ch].lc);
    printf("rl:    %4d\n",fec_log[ch].rl);
    printf("xfun:  %4d\n",fec_log[ch].xfun);
    printf("xferr: %4d\n",fec_log[ch].xferr);
    printf("rferr: %4d\n",fec_log[ch].rferr);
    printf("dtxf:  %4d\n",fec_log[ch].dtxf);
    printf("drxf:  %4d\n",fec_log[ch].drxf);
    printf("\nRFSW:\n");
    printf("inv:   %4d\n",fec_log[ch].rfsw_inv);
    printf("m:     %4d\n",fec_log[ch].rfsw_m);
    printf("bc:    %4d\n",fec_log[ch].rfsw_bc);
    printf("mc:    %4d\n",fec_log[ch].rfsw_mc);
    printf("lg:    %4d\n",fec_log[ch].rfsw_lg);
    printf("no:    %4d\n",fec_log[ch].rfsw_no);
    printf("cr:    %4d\n",fec_log[ch].rfsw_cr);
    printf("ov:    %4d\n",fec_log[ch].rfsw_ov);
    printf("tr:    %4d\n",fec_log[ch].rfsw_tr);
    printf("---------------\n\n");
}

/*
 * Display some of the registers for debugging
 *
 * Parameters:
 *  ch      FEC channel
 */
void
fec_debug_dump(uint8_t ch)
{
    printf("\n------------- FEC%d -------------\n",ch);
    printf("EIR      %08x        \n",MCF_FEC_EIR(ch));
    printf("EIMR     %08x        \n",MCF_FEC_EIMR(ch));
    printf("ECR      %08x        \n",MCF_FEC_ECR(ch));
    printf("RCR      %08x        \n",MCF_FEC_RCR(ch));
    printf("R_HASH   %08x        \n",MCF_FEC_R_HASH(ch));
    printf("TCR      %08x        \n",MCF_FEC_TCR(ch));
    printf("FECTFWR  %08x        \n",MCF_FEC_FECTFWR(ch));
    printf("FECRFSR  %08x        \n",MCF_FEC_FECRFSR(ch));
    printf("FECRFCR  %08x        \n",MCF_FEC_FECRFCR(ch));
    printf("FECRLRFP %08x        \n",MCF_FEC_FECRLRFP(ch));
    printf("FECRLWFP %08x        \n",MCF_FEC_FECRLWFP(ch));
    printf("FECRFAR  %08x        \n",MCF_FEC_FECRFAR(ch));
    printf("FECRFRP  %08x        \n",MCF_FEC_FECRFRP(ch));
    printf("FECRFWP  %08x        \n",MCF_FEC_FECRFWP(ch));
    printf("FECTFSR  %08x        \n",MCF_FEC_FECTFSR(ch));
    printf("FECTFCR  %08x        \n",MCF_FEC_FECTFCR(ch));
    printf("FECTLRFP %08x        \n",MCF_FEC_FECTLRFP(ch));
    printf("FECTLWFP %08x        \n",MCF_FEC_FECTLWFP(ch));
    printf("FECTFAR  %08x        \n",MCF_FEC_FECTFAR(ch));
    printf("FECTFRP  %08x        \n",MCF_FEC_FECTFRP(ch));
    printf("FECTFWP  %08x        \n",MCF_FEC_FECTFWP(ch));
    printf("FRST     %08x        \n",MCF_FEC_FRST(ch));
    printf("--------------------------------\n\n");
}

/*
 * Set the duplex on the selected FEC controller
 *
 * Parameters:
 *  ch      FEC channel
 *  duplex  FEC_MII_FULL_DUPLEX or FEC_MII_HALF_DUPLEX
 */
void
fec_duplex (uint8_t ch, uint8_t duplex)
{
    ASSERT(ch == 0 || ch == 1);

    switch (duplex)
    {
        case FEC_MII_HALF_DUPLEX:
            MCF_FEC_RCR(ch) |= MCF_FEC_RCR_DRT;
            MCF_FEC_TCR(ch) &= (uint32_t)~MCF_FEC_TCR_FDEN;
            break;
        case FEC_MII_FULL_DUPLEX:
        default:
            MCF_FEC_RCR(ch) &= (uint32_t)~MCF_FEC_RCR_DRT;
            MCF_FEC_TCR(ch) |= MCF_FEC_TCR_FDEN;
            break;
    }
}

/*
 * Generate the hash table settings for the given address
 *
 * Parameters:
 *  addr    48-bit (6 byte) Address to generate the hash for
 *
 * Return Value:
 *  The 6 most significant bits of the 32-bit CRC result
 */
uint8_t
fec_hash_address(const uint8_t *addr)
{
    uint32_t crc;
    uint8_t byte;
    int i, j;

    crc = 0xFFFFFFFF;
    for(i=0; i<6; ++i)
    {
        byte = addr[i];
        for(j=0; j<8; ++j)
        {
            if((byte & 0x01)^(crc & 0x01))
            {
                crc >>= 1;
                crc = crc ^ 0xEDB88320;
            }
            else
                crc >>= 1;
            byte >>= 1;
        }
    }
    return (uint8_t)(crc >> 26);
}

/*
 * Set the Physical (Hardware) Address and the Individual Address
 * Hash in the selected FEC
 *
 * Parameters:
 *  ch  FEC channel
 *  pa  Physical (Hardware) Address for the selected FEC
 */
void
fec_set_address (uint8_t ch, const uint8_t *pa)
{
    uint8_t crc;

    ASSERT(ch == 0 || ch == 1);

    /*
     * Set the Physical Address
     */
    MCF_FEC_PALR(ch) = (uint32_t)((pa[0]<<24) | (pa[1]<<16) | (pa[2]<<8) | pa[3]);
    MCF_FEC_PAUR(ch) = (uint32_t)((pa[4]<<24) | (pa[5]<<16));

    /*
     * Calculate and set the hash for given Physical Address
     * in the  Individual Address Hash registers
     */
    crc = fec_hash_address(pa);
    if(crc >= 32)
        MCF_FEC_IAUR(ch) |= (uint32_t)(1 << (crc - 32));
    else
        MCF_FEC_IALR(ch) |= (uint32_t)(1 << crc);
}

/*
 * Reset the selected FEC controller
 *
 * Parameters:
 *  ch      FEC channel
 */
void
fec_reset (uint8_t ch)
{
    int i;

    ASSERT(ch == 0 || ch == 1);

    /* Clear any events in the FIFO status registers */
    MCF_FEC_FECRFSR(ch) = (0
        | MCF_FEC_FECRFSR_OF
        | MCF_FEC_FECRFSR_UF
        | MCF_FEC_FECRFSR_RXW
        | MCF_FEC_FECRFSR_FAE
        | MCF_FEC_FECRFSR_IP);
    MCF_FEC_FECTFSR(ch) = (0
        | MCF_FEC_FECRFSR_OF
        | MCF_FEC_FECRFSR_UF
        | MCF_FEC_FECRFSR_RXW
        | MCF_FEC_FECRFSR_FAE
        | MCF_FEC_FECRFSR_IP);

    /* Reset the FIFOs */
    MCF_FEC_FRST(ch) |= MCF_FEC_FRST_SW_RST;
    MCF_FEC_FRST(ch) &= ~MCF_FEC_FRST_SW_RST;

    /* Set the Reset bit and clear the Enable bit */
    MCF_FEC_ECR(ch) = MCF_FEC_ECR_RESET;

    /* Wait at least 8 clock cycles */
    for (i=0; i<10; ++i)
        nop();
}

/*
 * Initialize the selected FEC
 *
 * Parameters:
 *  ch      FEC channel
 *  mode    External interface mode (MII, 7-wire, or internal loopback)
 *  pa      Physical (Hardware) Address for the selected FEC
 */
void
fec_init (uint8_t ch, uint8_t mode, const uint8_t *pa)
{
    ASSERT(ch == 0 || ch == 1);

    /*
     * Enable all the external interface signals
     */
    if (mode == FEC_MODE_7WIRE)
    {
        if (ch == 1)
            MCF_GPIO_PAR_FECI2CIRQ |= MCF_GPIO_PAR_FECI2CIRQ_PAR_E17;
        else
            MCF_GPIO_PAR_FECI2CIRQ |= MCF_GPIO_PAR_FECI2CIRQ_PAR_E07;
    }
    else if (mode == FEC_MODE_MII)
    {
        if (ch == 1)
            MCF_GPIO_PAR_FECI2CIRQ |= 0
                | MCF_GPIO_PAR_FECI2CIRQ_PAR_E1MDC_EMDC
                | MCF_GPIO_PAR_FECI2CIRQ_PAR_E1MDIO_EMDIO
                | MCF_GPIO_PAR_FECI2CIRQ_PAR_E1MII
                | MCF_GPIO_PAR_FECI2CIRQ_PAR_E17;
        else
            MCF_GPIO_PAR_FECI2CIRQ |= 0
                | MCF_GPIO_PAR_FECI2CIRQ_PAR_E0MDC
                | MCF_GPIO_PAR_FECI2CIRQ_PAR_E0MDIO
                | MCF_GPIO_PAR_FECI2CIRQ_PAR_E0MII
                | MCF_GPIO_PAR_FECI2CIRQ_PAR_E07;
    }

    /*
     * Clear the Individual and Group Address Hash registers
     */
    MCF_FEC_IALR(ch) = 0;
    MCF_FEC_IAUR(ch) = 0;
    MCF_FEC_GALR(ch) = 0;
    MCF_FEC_GAUR(ch) = 0;

    /*
     * Set the Physical Address for the selected FEC
     */
    fec_set_address(ch, pa);

    /*
     * Mask all FEC interrupts
     */
    MCF_FEC_EIMR(ch) = MCF_FEC_EIMR_MASK_ALL;

    /*
     * Clear all FEC interrupt events
     */
    MCF_FEC_EIR(ch) = MCF_FEC_EIR_CLEAR_ALL;

    /*
     * Initialize the Receive Control Register
     */
    MCF_FEC_RCR(ch) = 0
        | MCF_FEC_RCR_MAX_FL(ETH_MAX_FRM)
    #ifdef FEC_PROMISCUOUS
        | MCF_FEC_RCR_PROM
    #endif
        | MCF_FEC_RCR_FCE;

    if (mode == FEC_MODE_MII)
        MCF_FEC_RCR(ch) |= MCF_FEC_RCR_MII_MODE;

    else if (mode == FEC_MODE_LOOPBACK)
        MCF_FEC_RCR(ch) |= MCF_FEC_RCR_LOOP;

    /*
     * Initialize the Transmit Control Register
     */
    MCF_FEC_TCR(ch) = MCF_FEC_TCR_FDEN;

    /*
     * Set Rx FIFO alarm and granularity
     */
    MCF_FEC_FECRFCR(ch) = 0
        | MCF_FEC_FECRFCR_FRM
        | MCF_FEC_FECRFCR_RXW_MSK
        | MCF_FEC_FECRFCR_GR(7);
    MCF_FEC_FECRFAR(ch) = MCF_FEC_FECRFAR_ALARM(768);

    /*
     * Set Tx FIFO watermark, alarm and granularity
     */
    MCF_FEC_FECTFCR(ch) = 0
        | MCF_FEC_FECTFCR_FRM
        | MCF_FEC_FECTFCR_TXW_MSK
        | MCF_FEC_FECTFCR_GR(7);
    MCF_FEC_FECTFAR(ch) = MCF_FEC_FECTFAR_ALARM(256);
    MCF_FEC_FECTFWR(ch) = MCF_FEC_FECTFWR_X_WMRK_256;

    /*
     * Enable the transmitter to append the CRC
     */
    MCF_FEC_CTCWR(ch) = 0
        | MCF_FEC_CTCWR_TFCW
        | MCF_FEC_CTCWR_CRC;
}

/*
 * Start the FEC Rx DMA task
 *
 * Parameters:
 *  ch      FEC channel
 *  rxbd    First Rx buffer descriptor in the chain
 */
void
fec_rx_start(uint8_t ch, int8_t *rxbd)
{
    uint32_t initiator;
    int channel, result;

    ASSERT(ch == 0 || ch == 1);

    /*
     * Make the initiator assignment
     */
    result = dma_set_initiator(DMA_FEC_RX(ch));
    ASSERT(result == 0);

    /*
     * Grab the initiator number
     */
    initiator = dma_get_initiator(DMA_FEC_RX(ch));

    /*
     * Determine the DMA channel running the task for the
     * selected FEC
     */
    channel = dma_set_channel(DMA_FEC_RX(ch),
                              (ch == 0) ? fec0_rx_frame : fec1_rx_frame);
    ASSERT(channel != -1);

    /*
     * Start the Rx DMA task
     */
    /*
     * Start the Rx DMA task
     */
    MCD_startDma(channel,
                 (s8*)rxbd,
                 0,
                 (s8*)MCF_FEC_FECRFDR_ADDR(ch),
                 0,
                 RX_BUF_SZ,
                 0,
                 initiator,
                 FECRX_DMA_PRI(ch),
                 0
                   | MCD_FECRX_DMA
                   | MCD_INTERRUPT
                   | MCD_TT_FLAGS_CW
                   | MCD_TT_FLAGS_RL
                   | MCD_TT_FLAGS_SP
                   ,
                 0
                   | MCD_NO_CSUM
                   | MCD_NO_BYTE_SWAP
                 );
}

/*
 * Continue the Rx DMA task
 *
 * This routine is called after the DMA task has halted after
 * encountering an Rx buffer descriptor that wasn't marked as
 * ready. There is no harm in calling the DMA continue routine
 * if the DMA is not halted.
 *
 * Parameters:
 *  ch      FEC channel
 */
void
fec_rx_continue(uint8_t ch)
{
    int channel;

    ASSERT(ch == 0 || ch == 1);

    /*
     * Determine the DMA channel running the task for the
     * selected FEC
     */
    channel = dma_get_channel(DMA_FEC_RX(ch));
    ASSERT(channel != -1);

    /*
     * Continue/restart the DMA task
     */
    MCD_continDma(channel);
}

/*
 * Stop all frame receptions on the selected FEC
 *
 * Parameters:
 *  ch  FEC channel
 */
void
fec_rx_stop (uint8_t ch)
{
    uint32_t mask;
    int channel;

    ASSERT(ch == 0 || ch == 1);

    /* Save off the EIMR value */
    mask = MCF_FEC_EIMR(ch);

    /* Mask all interrupts */
    MCF_FEC_EIMR(ch) = 0;

    /*
     * Determine the DMA channel running the task for the
     * selected FEC
     */
    channel = dma_get_channel(DMA_FEC_RX(ch));
    ASSERT(channel != -1);

    /* Kill the FEC Rx DMA task */
    MCD_killDma(channel);

    /*
     * Free up the FEC requestor from the software maintained
     * initiator list
     */
    dma_free_initiator(DMA_FEC_RX(ch));

    /* Free up the DMA channel */
    dma_free_channel(DMA_FEC_RX(ch));

    /* Restore the interrupt mask register value */
    MCF_FEC_EIMR(ch) = mask;
}

/*
 * Receive Frame interrupt handler - this handler is called by the
 * DMA interrupt handler indicating that a packet was successfully
 * transferred out of the Rx FIFO.
 *
 * Parameters:
 *  nif     Pointer to Network Interface structure
 *  ch      FEC channel
 */
NBUF *
fec_rx_frame(uint8_t ch, NIF *nif)
{
//    ETH_HDR *eth_hdr;
    FECBD *pRxBD;
    NBUF *cur_nbuf, *new_nbuf;
    int keep;

    while ((pRxBD = fecbd_rx_alloc(ch)) != NULL)
    {
        fec_log[ch].drxf++;
        keep = TRUE;

        /*
         * Check the Receive Frame Status Word for errors
         *  - The L bit should always be set
         *  - No undefined bits should be set
         *  - The upper 5 bits of the length should be cleared
         */
        if (!(pRxBD->status & RX_BD_L) || (pRxBD->status & 0x0608)
                                       || (pRxBD->length & 0xF800))
        {
            keep = FALSE;
            fec_log[ch].rfsw_inv++;
        }
        else if (pRxBD->status & RX_BD_ERROR)
        {
            keep = FALSE;
            if (pRxBD->status & RX_BD_NO)
                fec_log[ch].rfsw_no++;
            if (pRxBD->status & RX_BD_CR)
                fec_log[ch].rfsw_cr++;
            if (pRxBD->status & RX_BD_OV)
                fec_log[ch].rfsw_ov++;
            if (pRxBD->status & RX_BD_TR)
                fec_log[ch].rfsw_tr++;
        }
        else
        {
            if (pRxBD->status & RX_BD_LG)
                fec_log[ch].rfsw_lg++;
            if (pRxBD->status & RX_BD_M)
                fec_log[ch].rfsw_m++;
            if (pRxBD->status & RX_BD_BC)
                fec_log[ch].rfsw_bc++;
            if (pRxBD->status & RX_BD_MC)
                fec_log[ch].rfsw_mc++;
        }

        if (keep)
        {
            /*
             * Pull the network buffer off the Rx ring queue
             */
            cur_nbuf = nbuf_remove(NBUF_RX_RING);
            ASSERT(cur_nbuf);
            ASSERT(cur_nbuf->data == pRxBD->data);

            /*
             * Copy the buffer descriptor information to the network buffer
             */
//            cur_nbuf->length = (pRxBD->length - (ETH_HDR_LEN + ETH_CRC_LEN));
//            cur_nbuf->offset = ETH_HDR_LEN;
            cur_nbuf->length = (pRxBD->length - (ETH_CRC_LEN));
            cur_nbuf->offset = 0;

            /*
             * Get a new buffer pointer for this buffer descriptor
             */
            new_nbuf = nbuf_alloc();
            if (new_nbuf == NULL)
            {
                #ifdef CONFIG_DRIVER_NET_MCF54XX_DEBUG
                    printf("nbuf_alloc() failed\n");
                #endif
                /*
                 * Can't allocate a new network buffer, so we
                 * have to trash the received data and reuse the buffer
                 * hoping that some buffers will free up in the system
                 * and this frame will be re-transmitted by the host
                 */
                pRxBD->length = RX_BUF_SZ;
                pRxBD->status &= (RX_BD_W | RX_BD_INTERRUPT);
                pRxBD->status |= RX_BD_E;
                nbuf_add(NBUF_RX_RING, cur_nbuf);
                fec_rx_continue(ch);
                continue;
            }

            /*
             * Add the new network buffer to the Rx ring queue
             */
            nbuf_add(NBUF_RX_RING, new_nbuf);

            /*
             * Re-initialize the buffer descriptor - pointing it
             * to the new data buffer.  The previous data buffer
             * will be passed up the stack
             */
            pRxBD->data = new_nbuf->data;
            pRxBD->length = RX_BUF_SZ;
            pRxBD->status &= (RX_BD_W | RX_BD_INTERRUPT);
            pRxBD->status |= RX_BD_E;


            /*
             * Let the DMA know that there is a new Rx BD (in case the
             * ring was full and the DMA was waiting for an empty one)
             */
            fec_rx_continue(ch);

            /*
             * Get pointer to the frame data inside the network buffer
             */
//            eth_hdr = (ETH_HDR *)cur_nbuf->data;

            /*
             * Pass the received packet up the network stack if the
             * protocol is supported in our network interface (NIF)
             */
//FIXME      if (nif_protocol_exist(nif,eth_hdr->type))
//            {
//                nif_protocol_handler(nif, eth_hdr->type, cur_nbuf);
//            }
//            else
//                nbuf_free(cur_nbuf);
		return(cur_nbuf);
        }
        else
        {
            /*
             * This frame isn't a keeper
             * Reset the status and length, but don't need to get another
             * buffer since we are trashing the data in the current one
             */
            pRxBD->length = RX_BUF_SZ;
            pRxBD->status &= (RX_BD_W | RX_BD_INTERRUPT);
            pRxBD->status |= RX_BD_E;

            /*
             * Move the current buffer from the beginning to the end of the
             * Rx ring queue
             */
            cur_nbuf = nbuf_remove(NBUF_RX_RING);
            nbuf_add(NBUF_RX_RING, cur_nbuf);

            /*
             * Let the DMA know that there are new Rx BDs (in case
             * it is waiting for an empty one)
             */
            fec_rx_continue(ch);
        }
    }
    return NULL;
}

void
fec0_rx_frame(void)
{
//    extern NIF nif1;
//    fec_rx_frame(0, 0);
}

void
fec1_rx_frame(void)
{
//    extern NIF nif1;
//    fec_rx_frame(1, 0);
}

/*
 * Start the FEC Tx DMA task
 *
 * Parameters:
 *  ch      FEC channel
 *  txbd    First Tx buffer descriptor in the chain
 */
void
fec_tx_start(uint8_t ch, int8_t *txbd)
{
    uint32_t initiator;
    int channel, result;
    void fec0_tx_frame(void);
    void fec1_tx_frame(void);

    /*
     * Make the initiator assignment
     */
    result = dma_set_initiator(DMA_FEC_TX(ch));
    ASSERT(result == 0);

    /*
     * Grab the initiator number
     */
    initiator = dma_get_initiator(DMA_FEC_TX(ch));
    ASSERT(initiator != 0);

    /*
     * Determine the DMA channel running the task for the
     * selected FEC
     */
    channel = dma_set_channel(DMA_FEC_TX(ch),
                              (ch == 0) ? fec0_tx_frame : fec1_tx_frame);
    ASSERT(channel != -1);

    /*
     * Start the Tx DMA task
     */
    MCD_startDma(channel,
                 (s8*)txbd,
                 0,
                 (s8*)MCF_FEC_FECTFDR_ADDR(ch),
                 0,
                 ETH_MTU,
                 0,
                 initiator,
                 FECTX_DMA_PRI(ch),
                 0
                   | MCD_FECTX_DMA
                   | MCD_INTERRUPT
                   | MCD_TT_FLAGS_CW
                   | MCD_TT_FLAGS_RL
                   | MCD_TT_FLAGS_SP
                   ,
                 0
                   | MCD_NO_CSUM
                   | MCD_NO_BYTE_SWAP
                 );
}

/*
 * Continue the Tx DMA task
 *
 * This routine is called after the DMA task has halted after
 * encountering an Tx buffer descriptor that wasn't marked as
 * ready.  There is no harm in calling the continue DMA routine
 * if the DMA was not paused.
 *
 * Parameters:
 *  ch      FEC channel
 */
void
fec_tx_continue(uint8_t ch)
{
    int channel;

    /*
     * Determine the DMA channel running the task for the
     * selected FEC
     */
    channel = dma_get_channel(DMA_FEC_TX(ch));
    ASSERT(channel > 0);

    /*
     * Continue/restart the DMA task
     */
    MCD_continDma((int)channel);
}

/*
 * Stop all transmissions on the selected FEC and kill the DMA task
 *
 * Parameters:
 *  ch  FEC channel
 */
void
fec_tx_stop (uint8_t ch)
{
    uint32_t mask;
    int channel;

    ASSERT(ch == 0 || ch == 1);

    /* Save off the EIMR value */
    mask = MCF_FEC_EIMR(ch);

    /* Mask all interrupts */
    MCF_FEC_EIMR(ch) = 0;

    /* If the Ethernet is still enabled... */
    if (MCF_FEC_ECR(ch) & MCF_FEC_ECR_ETHER_EN)
    {
        /* Issue the Graceful Transmit Stop */
        MCF_FEC_TCR(ch) |= MCF_FEC_TCR_GTS;

        /* Wait for the Graceful Stop Complete interrupt */
        while(!(MCF_FEC_EIR(ch) & MCF_FEC_EIR_GRA))
        {
            if (!(MCF_FEC_ECR(ch) & MCF_FEC_ECR_ETHER_EN))
                break;
        }

        /* Clear the Graceful Stop Complete interrupt */
        MCF_FEC_EIR(ch) = MCF_FEC_EIR_GRA;
    }

    /*
     * Determine the DMA channel running the task for the
     * selected FEC
     */
    channel = dma_get_channel(DMA_FEC_TX(ch));
    ASSERT(channel > 0);

    /* Kill the FEC Tx DMA task */
    MCD_killDma(channel);

    /*
     * Free up the FEC requestor from the software maintained
     * initiator list
     */
    dma_free_initiator(DMA_FEC_TX(ch));

    /* Free up the DMA channel */
    dma_free_channel(DMA_FEC_TX(ch));

    /* Restore the interrupt mask register value */
    MCF_FEC_EIMR(ch) = mask;
}

/*
 * Trasmit Frame interrupt handler - this handler is called by the
 * DMA interrupt handler indicating that a packet was successfully
 * transferred to the Tx FIFO.
 *
 * Parameters:
 *  ch      FEC channel
 */
void
fec_tx_frame(uint8_t ch)
{
    FECBD *pTxBD;
    NBUF *pNbuf;

    while ((pTxBD = fecbd_tx_free(ch)) != NULL)
    {
        fec_log[ch].dtxf++;

        /*
         * Grab the network buffer associated with this buffer descriptor
         */
        pNbuf = nbuf_remove(NBUF_TX_RING);
        ASSERT(pNbuf);
        ASSERT(pNbuf->data == pTxBD->data);

        /*
         * Free up the network buffer that was just transmitted
         */
        nbuf_free(pNbuf);

        /*
         * Re-initialize the Tx BD
         */
        pTxBD->data = NULL;
        pTxBD->length = 0;
    }
}

void
fec0_tx_frame(void)
{
    fec_tx_frame(0);
}

void
fec1_tx_frame(void)
{
    fec_tx_frame(1);
}

/*
 * Send a packet out the selected FEC
 *
 * Parameters:
 *  ch      FEC channel
 *  nif     Pointer to Network Interface (NIF) structure
 *  dst     Destination MAC Address
 *  src     Source MAC Address
 *  type    Ethernet Frame Type
 *  length  Number of bytes to be transmitted (doesn't include type,
 *          src, or dest byte count)
 *  pkt     Pointer packet network buffer
 *
 * Return Value:
 *  1       success
 *  0       otherwise
 */
int
fec_send (uint8_t ch, NIF *nif, uint8_t *dst, uint8_t *src, uint16_t type, NBUF *nbuf)
{
    FECBD *pTxBD;
    ASSERT(ch == 0 || ch == 1);

    /* Check the length */
    if ((nbuf->length + ETH_HDR_LEN) > ETH_MTU)
        return 0;

    /*
     * Copy the destination address, source address, and Ethernet
     * type into the packet
     */
//    memcpy(&nbuf->data[0],  dst,   6);
//    memcpy(&nbuf->data[6],  src,   6);
//    memcpy(&nbuf->data[12], &type, 2);

    /*
     * Grab the next available Tx Buffer Descriptor
     */
    while ((pTxBD = fecbd_tx_alloc(ch)) == NULL) {};

    /*
     * Put the network buffer into the Tx waiting queue
     */
    nbuf_add(NBUF_TX_RING, nbuf);

    /*
     * Setup the buffer descriptor for transmission
     */
    pTxBD->data = nbuf->data;
    pTxBD->length = nbuf->length; // + ETH_HDR_LEN;
    pTxBD->status |= (TX_BD_R | TX_BD_L);

    /*
     * Continue the Tx DMA task (in case it was waiting for a new
     * TxBD to be ready
     */
    fec_tx_continue(ch);

    return 1;
}

int
fec0_send(NIF *nif, uint8_t *dst, uint8_t *src, uint16_t type, NBUF *nbuf)
{
    return fec_send(0, nif, dst, src, type, nbuf);
}

int
fec1_send(NIF *nif, uint8_t *dst, uint8_t *src, uint16_t type, NBUF *nbuf)
{
    return fec_send(1, nif, dst, src, type, nbuf);
}

/*
 * Enable interrupts on the selected FEC
 *
 * Parameters:
 *  ch      FEC channel
 *  pri     Interrupt Priority
 *  lvl     Interrupt Level
 */
void
fec_irq_enable(uint8_t ch, uint8_t lvl, uint8_t pri)
{
    ASSERT(ch == 0 || ch == 1);
    ASSERT(lvl > 0 && lvl < 8);
    ASSERT(pri < 8);

    /*
     * Setup the appropriate ICR
     */
    MCF_INTC_ICRn((ch == 0) ? 39 : 38) = (uint8_t)(0
        | MCF_INTC_ICRn_IP(pri)
        | MCF_INTC_ICRn_IL(lvl));

    /*
     * Clear any pending FEC interrupt events
     */
    MCF_FEC_EIR(ch) = MCF_FEC_EIR_CLEAR_ALL;

    /*
     * Unmask all FEC interrupts
     */
    MCF_FEC_EIMR(ch) = MCF_FEC_EIMR_UNMASK_ALL;

    /*
     * Unmask the FEC interrupt in the interrupt controller
     */
    if (ch == 0)
        MCF_INTC_IMRH &= ~MCF_INTC_IMRH_INT_MASK39;
    else
        MCF_INTC_IMRH &= ~MCF_INTC_IMRH_INT_MASK38;
}

/*
 * Disable interrupts on the selected FEC
 *
 * Parameters:
 *  ch      FEC channel
 */
void
fec_irq_disable(uint8_t ch)
{
    ASSERT(ch == 0 || ch == 1);

    /*
     * Mask all FEC interrupts
     */
    MCF_FEC_EIMR(ch) = MCF_FEC_EIMR_MASK_ALL;

    /*
     * Mask the FEC interrupt in the interrupt controller
     */
    if (ch == 0)
        MCF_INTC_IMRH |= MCF_INTC_IMRH_INT_MASK39;
    else
        MCF_INTC_IMRH |= MCF_INTC_IMRH_INT_MASK38;
}

/*
 * FEC interrupt handler
 * All interrupts are multiplexed into a single vector for each
 * FEC module. The lower level interrupt handler passes in the
 * channel to this handler. Note that the receive interrupt is
 * generated by the Multi-channel DMA FEC Rx task.
 *
 * Parameters:
 * ch       FEC channel
 */
static void
fec_irq_handler(uint8_t ch)
{
    uint32_t event, eir;

    /*
     * Determine which interrupt(s) asserted by AND'ing the
     * pending interrupts with those that aren't masked.
     */
    eir = MCF_FEC_EIR(ch);
    event = eir & MCF_FEC_EIMR(ch);

    #ifdef CONFIG_DRIVER_NET_MCF54XX_DEBUG
    if (event != eir)
        printf("Pending but not enabled: 0x%08X\n",(event ^ eir));
    #endif

    /*
     * Clear the event(s) in the EIR immediately
     */
    MCF_FEC_EIR(ch) = event;

    if (event & MCF_FEC_EIR_RFERR)
    {
        fec_log[ch].total++;
        fec_log[ch].rferr++;
        #ifdef CONFIG_DRIVER_NET_MCF54XX_DEBUG
        printf("RFERR\n");
        printf("FECRFSR%d = 0x%08x\n",ch,MCF_FEC_FECRFSR(ch));
        fec_eth_stop(ch);
        #endif
    }
    if (event & MCF_FEC_EIR_XFERR)
    {
        fec_log[ch].total++;
        fec_log[ch].xferr++;
        #ifdef CONFIG_DRIVER_NET_MCF54XX_DEBUG
        printf("XFERR\n");
        #endif
    }
    if (event & MCF_FEC_EIR_XFUN)
    {
        fec_log[ch].total++;
        fec_log[ch].xfun++;
        #ifdef CONFIG_DRIVER_NET_MCF54XX_DEBUG
        printf("XFUN\n");
        fec_eth_stop(ch);
        #endif
    }
    if (event & MCF_FEC_EIR_RL)
    {
        fec_log[ch].total++;
        fec_log[ch].rl++;
        #ifdef CONFIG_DRIVER_NET_MCF54XX_DEBUG
        printf("RL\n");
        #endif
    }
    if (event & MCF_FEC_EIR_LC)
    {
        fec_log[ch].total++;
        fec_log[ch].lc++;
        #ifdef CONFIG_DRIVER_NET_MCF54XX_DEBUG
        printf("LC\n");
        #endif
    }
    if (event & MCF_FEC_EIR_MII)
    {
        fec_log[ch].mii++;
    }
    if (event & MCF_FEC_EIR_TXF)
    {
        fec_log[ch].txf++;
    }
    if (event & MCF_FEC_EIR_GRA)
    {
        fec_log[ch].gra++;
    }
    if (event & MCF_FEC_EIR_BABT)
    {
        fec_log[ch].total++;
        fec_log[ch].babt++;
        #ifdef CONFIG_DRIVER_NET_MCF54XX_DEBUG
        printf("BABT\n");
        #endif
    }
    if (event & MCF_FEC_EIR_BABR)
    {
        fec_log[ch].total++;
        fec_log[ch].babr++;
        #ifdef CONFIG_DRIVER_NET_MCF54XX_DEBUG
        printf("BABR\n");
        #endif
    }
    if (event & MCF_FEC_EIR_HBERR)
    {
        fec_log[ch].total++;
        fec_log[ch].hberr++;
        #ifdef CONFIG_DRIVER_NET_MCF54XX_DEBUG
        printf("HBERR\n");
        #endif
    }
}

int
fec0_interrupt_handler(void* arg1, void* arg2)
{
    (void) arg1;
    (void) arg2;
    fec_irq_handler(0);
    return 1;
}

int
fec1_interrupt_handler(void* arg1, void* arg2)
{
    (void) arg1;
    (void) arg2;
    fec_irq_handler(1);
    return 1;
}

/*
 * Configure the selected Ethernet port and enable all operations
 *
 * Parameters:
 *  ch      FEC channel
 *  trcvr   Transceiver mode (MII, 7-Wire or internal loopback)
 *  speed   Maximum operating speed (MII only)
 *  duplex  Full or Half-duplex (MII only)
 *  mac     Physical (MAC) Address
 */
void
fec_eth_setup(uint8_t ch, uint8_t trcvr, uint8_t speed, uint8_t duplex, const uint8_t *mac)
{
    ASSERT(ch == 0 || ch == 1);

    /*
     * Disable FEC interrupts
     */
    fec_irq_disable(ch);

    /*
     * Initialize the event log
     */
    fec_log_init(ch);

    /*
     * Initialize the network buffers and fec buffer descriptors
     */
    nbuf_init();
    fecbd_init(ch);

    /*
     * Initialize the FEC
     */
    fec_reset(ch);
    fec_init(ch,trcvr,mac);

    if (trcvr == FEC_MODE_MII)
    {
        /*
         * Initialize the MII interface
         */
        fec_mii_init(ch, CFG_SYSTEM_CORE_CLOCK);
    }

    /*
     * Initialize and enable FEC interrupts
     */
    fec_irq_enable(ch, FEC_INTC_LVL(ch), FEC_INTC_PRI(ch));

    /*
     * Enable the multi-channel DMA tasks
     */
    fec_rx_start(ch, (int8_t*)fecbd_get_start(ch,Rx));
    fec_tx_start(ch, (int8_t*)fecbd_get_start(ch,Tx));

    /*
     * Enable the FEC channel
     */
    MCF_FEC_ECR(ch) |= MCF_FEC_ECR_ETHER_EN;
}
/*
 * Reset the selected Ethernet port
 *
 * Parameters:
 *  ch      FEC channel
 */
void
fec_eth_reset(uint8_t ch)
{
// To do
}

/*
 * Stop the selected Ethernet port
 *
 * Parameters:
 *  ch      FEC channel
 */
void
fec_eth_stop(uint8_t ch)
{
    int level;

    /*
     * Disable interrupts
     */
    level = asm_set_ipl(7);

    /*
     * Gracefully disable the receiver and transmitter
     */
    fec_tx_stop(ch);
    fec_rx_stop(ch);

    /*
     * Disable FEC interrupts
     */
    fec_irq_disable(ch);

    /*
     * Disable the FEC channel
     */
    MCF_FEC_ECR(ch) &= ~MCF_FEC_ECR_ETHER_EN;

    #ifdef CONFIG_DRIVER_NET_MCF54XX_DEBUG
        nbuf_debug_dump();
        fec_log_dump(ch);
    #endif

    /*
     * Flush the network buffers
     */
    nbuf_flush();

    /*
     * Restore interrupt level
     */
    asm_set_ipl(level);
}