text: /****************************************************************************
   * arch/arm/src/c5471/c5471_ethernet.c
   *
   *   Copyright (C) 2007, 2009-2010, 2014-2015, 2017-2018 Gregory Nutt. All
   *     rights reserved.
   *   Author: Gregory Nutt <gnutt@nuttx.org>
   *
   * Based one a C5471 Linux driver and released under this BSD license with
   * special permission from the copyright holder of the Linux driver:
   * Todd Fischer, Cadenux, LLC.  Other references: "TMS320VC547x CPU and
   * Peripherals Reference Guide," TI document spru038.pdf.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   * 3. Neither the name NuttX nor the names of its contributors may be
   *    used to endorse or promote products derived from this software
   *    without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
   * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
   * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   * POSSIBILITY OF SUCH DAMAGE.
   *
   ****************************************************************************/
  
  /****************************************************************************
   * Included Files
   ****************************************************************************/
  
  #include <nuttx/config.h>
  #if defined(CONFIG_NET)
  
  #include <stdint.h>
  #include <stdbool.h>
  #include <stdio.h>
  #include <time.h>
  #include <string.h>
  #include <errno.h>
  #include <debug.h>
  
  #include <arpa/inet.h>
  #include <net/ethernet.h>
  
  #include <nuttx/wdog.h>
  #include <nuttx/irq.h>
  #include <nuttx/arch.h>
  #include <nuttx/wqueue.h>
  #include <nuttx/net/arp.h>
  #include <nuttx/net/netdev.h>
  
  #ifdef CONFIG_NET_PKT
  #  include <nuttx/net/pkt.h>
  #endif
  
  #include "chip.h"
  #include "up_arch.h"
  #include "up_internal.h"
  
  /****************************************************************************
   * Pre-processor Definitions
   ****************************************************************************/
  
  /* Configuration ************************************************************/
  /* If processing is not done at the interrupt level, then work queue support
   * is required.
   */
  
  #if !defined(CONFIG_SCHED_WORKQUEUE)
  #  error Work queue support is required in this configuration (CONFIG_SCHED_WORKQUEUE)
  #endif
  
  /* The low priority work queue is preferred.  If it is not enabled, LPWORK
   * will be the same as HPWORK.
   *
   * NOTE:  However, the network should NEVER run on the high priority work
   * queue!  That queue is intended only to service short back end interrupt
   * processing that never suspends.  Suspending the high priority work queue
   * may bring the system to its knees!
   */
  
  #define ETHWORK LPWORK
  
  /* CONFIG_C5471_NET_NINTERFACES determines the number of physical interfaces
   * that will be supported.
   */
  
  #ifndef CONFIG_C5471_NET_NINTERFACES
  # define CONFIG_C5471_NET_NINTERFACES 1
  #endif
  
  /* CONFIG_C5471_NET_STATS will enabled collection of driver statistics.
   * Default is disabled.
   */
  
  /* Mode of operation defaults to AUTONEGOTIATION */
  
  #if defined(CONFIG_C5471_AUTONEGOTIATION)
  # undef CONFIG_C5471_BASET100
  # undef CONFIG_C5471_BASET10
  #elif defined(CONFIG_C5471_BASET100)
  # undef CONFIG_C5471_AUTONEGOTIATION
  # undef CONFIG_C5471_BASET10
  #elif defined(CONFIG_C5471_BASET10)
  # undef CONFIG_C5471_AUTONEGOTIATION
  # undef CONFIG_C5471_BASET100
  #else
  # define CONFIG_C5471_AUTONEGOTIATION 1
  # undef CONFIG_C5471_BASET100
  # undef CONFIG_C5471_BASET10
  #endif
  
  /* This should be disabled unless you are performing very low level debug */
  
  #undef CONFIG_C5471_NET_DUMPBUFFER
  //#define CONFIG_C5471_NET_DUMPBUFFER 1
  
  /* Timing values ************************************************************/
  /* TX poll deley = 1 seconds. CLK_TCK=number of clock ticks per second */
  
  #define C5471_WDDELAY         (1*CLK_TCK)
  
  /* TX timeout = 1 minute */
  
  #define C5471_TXTIMEOUT       (60*CLK_TCK)
  
  /* Ethernet GPIO bit settings ***********************************************/
  
  #define GPIO_CIO_MDIO         0x00004000
  #define GPIO_IO_MDCLK         0x00008000
  
  /* Ethernet interface bit settings ******************************************/
  
  /* TX descriptor, word #0 */
  
  #define EIM_TXDESC_OWN_HOST   0x80000000 /* Bit 15: Ownership bit */
  #define EIM_TXDESC_OWN_ENET   0x00000000
  #define EIM_TXDESC_WRAP_NEXT  0x40000000 /* Bit 14: Descriptor chain wrap */
  #define EIM_TXDESC_WRAP_FIRST 0x00000000
  #define EIM_TXDESC_FIF        0x20000000 /* Bit 13: First in frame */
  #define EIM_TXDESC_LIF        0x10000000 /* Bit 12: Last in frame */
                                           /* Bits 8-11: Retry count status */
  #define EIM_TXDESC_INTRE      0x00800000 /* Bit 7: TX_IRQ int enable */
  #define EIM_TXDESC_STATUSMASK 0x007f0000 /* Bits 0-6: Status */
  #define EIM_TXDESC_RETRYERROR 0x00400000 /*   Exceed retry error */
  #define EIM_TXDESC_HEARTBEAT  0x00200000 /*   Heartbeat (SQE) */
  #define EIM_TXDESC_LCOLLISON  0x00100000 /*   Late collision error */
  #define EIM_TXDESC_COLLISION  0x00080000 /*   Collision */
  #define EIM_TXDESC_CRCERROR   0x00040000 /*   CRC error */
  #define EIM_TXDESC_UNDERRUN   0x00020000 /*   Underrun error */
  #define EIM_TXDESC_LOC        0x00010000 /*   Loss of carrier */
  
  /* Packet bytes value used for both TX and RX descriptors */
  
  #define EIM_PACKET_BYTES      0x00000040
  
  /* Count of descriptors */
  
  #define NUM_DESC_TX           32
  #define NUM_DESC_RX           64
  
  /* TX descriptor, word #1 */
                                           /* Bit 15: reserved */
  #define EIM_TXDESC_PADCRC     0x00004000 /* Bit 14: Enable padding small frames */
                                           /* Bits 11-13: reserved */
  #define EIM_TXDESC_BYTEMASK   0x000007ff /* Bits 0-10: Descriptor byte count */
  
  /* RX descriptor, word #0 */
  
  #define EIM_RXDESC_OWN_HOST   0x80000000 /* Bit 15: Ownership bit */
  #define EIM_RXDESC_OWN_ENET   0x00000000
  #define EIM_RXDESC_WRAP_NEXT  0x40000000 /* Bit 14: Descriptor chain wrap */
  #define EIM_RXDESC_WRAP_FIRST 0x00000000
  #define EIM_RXDESC_FIF        0x20000000 /* Bit 13: First in frame */
  #define EIM_RXDESC_LIF        0x10000000 /* Bit 12: Last in frame */
                                           /* Bits 8-11: reserved */
  #define EIM_RXDESC_INTRE      0x00800000 /* Bit 7: RX_IRQ int enable */
  #define EIM_RXDESC_STATUSMASK 0x007f0000 /* Bits 0-6: Status */
  #define EIM_RXDESC_MISS       0x00400000 /*   Miss */
  #define EIM_RXDESC_VLAN       0x00200000 /*   VLAN */
  #define EIM_RXDESC_LFRAME     0x00100000 /*   Long frame error */
  #define EIM_RXDESC_SFRAME     0x00080000 /*   Short frame error */
  #define EIM_RXDESC_CRCERROR   0x00040000 /*   CRC error */
  #define EIM_RXDESC_OVERRUN    0x00020000 /*   Overrun error */
  #define EIM_RXDESC_ALIGN      0x00010000 /*   Non-octect align error */
  
  #define EIM_RXDESC_PADCRC     0x00004000 /* Enable padding for small frames */
  
  /* RX descriptor, word #1 */
                                           /* Bits 11-15: reserved */
  #define EIM_RXDESC_BYTEMASK   0x000007ff /* Bits 0-10: Descriptor byte count */
  
  /* EIM_CPU_FILTER bit settings */
                                           /* Bits 5-31: reserved */
  #define EIM_FILTER_MACLA      0x00000010 /* Bit 4: Enable logical address+multicast filtering */
  #define EIM_FILTER_LOGICAL    0x00000008 /* Bit 3: Enable ENET logical filtering */
  #define EIM_FILTER_MULTICAST  0x00000004 /* Bit 2: Enable multicast filtering */
  #define EIM_FILTER_BROADCAST  0x00000002 /* Bit 1: Enable broadcast matching */
  #define EIM_FILTER_UNICAST    0x00000001 /* Bit 0: Enable dest CPU address matching */
  
  /* EIM_CTRL bit settings */
                                           /* Bits 16-31: Reserved */
  #define EIM_CTRL_ESM_EN       0x00008000 /* Bit 15: Ethernet state machine enable */
                                           /* Bits 9-14: reserved */
  #define EIM_CTRL_ENET0_EN     0x00000100 /* Bit 8: Enable routing of RX packets CPU->ENET0 */
                                           /* Bit 7: reserved */
  #define EIM_CTRL_ENET0_FLW    0x00000040 /* Bit 6: Enable ENET0 flow control RX threshold */
  #define EIM_CTRL_RXENET0_EN   0x00000020 /* Bit 5: Enable processing of ENET0 RX queue */
  #define EIM_CTRL_TXENET0_EN   0x00000010 /* Bit 4: Enable processing of ENET0 TX queue */
                                           /* Bits 2-3: reserved */
  #define EIM_CTRL_RXCPU_EN     0x00000002 /* Bit 1: Enable processing of CPU RX queue */
  #define EIM_CTRL_TXCPU_EN     0x00000001 /* Bit 0: Enable processing of CPU TX queue */
  
  /* EIM_STATUS bit settings */
                                           /* Bits 10-31: reserved */
  #define EIM_STATUS_CPU_TXLIF  0x00000200 /* Bit 9: Last descriptor of TX packet filled */
  #define EIM_STATUS_CPU_RXLIF  0x00000100 /* Bit 8: Last descriptor of RX queue processed */
  #define EIM_STATUS_CPU_TX     0x00000080 /* Bit 7: Descriptor filled in TX queue */
  #define EIM_STATUS_CPU_RX     0x00000040 /* Bit 6: Descriptor filled in RX queue */
                                           /* Bits 3-5: reserved */
  #define EIM_STATUS_ENET0_ERR  0x00000004 /* Bit 2: ENET0 error interrupt */
  #define EIM_STATUS_ENET0_TX   0x00000002 /* Bit 1: ENET0 TX interrupt */
  #define EIM_STATUS_ENET0_RX   0x00000001 /* Bit 0" ENET0 RX interrupt */
  
  /* EIM_INTEN bit settings */
  
  #define EIM_INTEN_CPU_TXLIF  0x00000200 /* Bit 9: Last descriptor of TX packet filled */
  #define EIM_INTEN_CPU_RXLIF  0x00000100 /* Bit 8: Last descriptor of RX queue processed */
  #define EIM_INTEN_CPU_TX     0x00000080 /* Bit 7: Descriptor filled in TX queue */
  #define EIM_INTEN_CPU_RX     0x00000040 /* Bit 6: Descriptor filled in RX queue */
                                           /* Bits 3-5: reserved */
  #define EIM_INTEN_ENET0_ERR  0x00000004 /* Bit 2: ENET0 error interrupt */
  #define EIM_INTEN_ENET0_TX   0x00000002 /* Bit 1: ENET0 TX interrupt */
  #define EIM_INTEN_ENET0_RX   0x00000001 /* Bit 0: ENET0 RX interrupt */
  
  /* ENET0_ADRMODE_EN bit settings */
  
  #define ENET_ADR_PROMISCUOUS  0x00000008 /* Bit 3: Enable snoop address comparison */
  #define ENET_ADR_BROADCAST    0x00000004 /* Bit 2: Enable broadcast address comparison */
  #define ENET_ADDR_LCOMPARE    0x00000002 /* Bit 1: Enable logical address comparison */
  #define ENET_ADDR_PCOMPARE    0x00000001 /* Bit 0: Enable physical address comparison */
  
  /* ENET0_MODE bit settings */
                                           /* Bits 16-31: reserved */
  #define ENET_MODO_FIFO_EN     0x00008000 /* Bit 15: Fifo enable */
                                           /* Bits 8-14: reserved */
  #define ENET_MODE_RJCT_SFE    0x00000080 /* Bit 7: Reject short frames during receive */
  #define ENET_MODE_DPNET       0x00000040 /* Bit 6: Demand priority networkd vs CSMA/CD */
  #define ENET_MODE_MWIDTH      0x00000020 /* Bit 5: Select nibble mode MII port */
  #define ENET_MODE_WRAP        0x00000010 /* Bit 4: Internal MAC loopback */
  #define ENET_MODE_FDWRAP      0x00000008 /* Bit 3: Full duplex wrap */
  #define ENET_MODE_FULLDUPLEX  0x00000004 /* Bit 2: 1:Full duplex */
  #define ENET_MODE_HALFDUPLEX  0x00000000 /*        0:Half duplex */
                                           /* Bit 1: reserved */
  #define ENET_MODE_ENABLE      0x00000001 /* Bit 0: Port enable */
  
  /* PHY registers */
  
  #define MD_PHY_CONTROL_REG    0x00
  #define MD_PHY_MSB_REG        0x02
  #define MD_PHY_LSB_REG        0x03
  #define MD_PHY_CTRL_STAT_REG  0x17
  
  /*  Lucent LU3X31T-T64 transeiver ID */
  
  #define LU3X31_T64_PHYID      0x00437421
  
  /* PHY control register bit settings */
  
  #define MODE_AUTONEG          0x1000
  #define MODE_10MBIT_HALFDUP   0x0000
  #define MODE_10MBIT_FULLDUP   0x0100
  #define MODE_100MBIT_FULLDUP  0x2100
  #define MODE_100MBIT_HALFDUP  0x2000
  
  /* Inserts an ARM "nop" instruction */
  
  #define nop() asm("    nop");
  
  /* This is a helper pointer for accessing the contents of the Ethernet header */
  
  #define BUF ((struct eth_hdr_s *)priv->c_dev.d_buf)
  
  /****************************************************************************
   * Private Types
   ****************************************************************************/
  
  /* A single packet buffer is used */
  
  static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
  
  /* The c5471_driver_s encapsulates all state information for a single c5471
   * hardware interface
   */
  
  struct c5471_driver_s
  {
    bool    c_bifup;           /* true:ifup false:ifdown */
    WDOG_ID c_txpoll;          /* TX poll timer */
    WDOG_ID c_txtimeout;       /* TX timeout timer */
    struct work_s c_irqwork;   /* For deferring interrupt work to the work queue */
    struct work_s c_pollwork;  /* For deferring poll work to the work queue */
  
    /* Note: According to the C547x documentation: "The software has to maintain
     * two pointers to the current RX-CPU and TX-CPU descriptors. At init time,
     * they have to be set to the first descriptors of each queue, and they have
     * to be incremented each time a descriptor ownership is give to the SWITCH".
     */
  
    volatile uint32_t c_txcpudesc;
    volatile uint32_t c_rxcpudesc;
  
    /* Last TX descriptor saved for error handling */
  
    uint32_t c_lastdescstart;
    uint32_t c_lastdescend;
  
    /* Shadowed registers */
  
    uint32_t c_eimstatus;
  
  #ifdef CONFIG_C5471_NET_STATS
    /* TX statistics */
  
    uint32_t c_txpackets;      /* Number of packets sent */
    uint32_t c_txmiss;         /* Miss */
    uint32_t c_txvlan;         /* VLAN */
    uint32_t c_txlframe;       /* Long frame errors */
    uint32_t c_txsframe;       /* Short frame errors */
    uint32_t c_txcrc;          /* CRC errors */
    uint32_t c_txoverrun;      /* Overrun errors */
    uint32_t c_txalign;        /* Non-octect align errors */
    uint32_t c_txtimeouts;     /* TX timeouts */
  
    uint32_t c_rxpackets;      /* Number of packets received */
    uint32_t c_rxretries;      /* Exceed retry errors */
    uint32_t c_rxheartbeat;    /* Heartbeat (SQE) */
    uint32_t c_rxlcollision;   /* Late collision errors */
    uint32_t c_rxcollision;    /* Collision */
    uint32_t c_rxcrc;          /* CRC errors */
    uint32_t c_rxunderrun;     /* Underrun errors */
    uint32_t c_rxloc;          /* Loss of carrier */
    uint32_t c_rxdropped;      /* Packets dropped because of size */
  #endif
  
    /* This holds the information visible to the NuttX network */
  
    struct net_driver_s c_dev;  /* Interface understood by the network */
  };
  
  /****************************************************************************
   * Private Data
   ****************************************************************************/
  
  static struct c5471_driver_s g_c5471[CONFIG_C5471_NET_NINTERFACES];
  
  /****************************************************************************
   * Private Function Prototypes
   ****************************************************************************/
  
  /* Transceiver interface */
  
  static void c5471_mdtxbit (int bit_state);
  static int  c5471_mdrxbit (void);
  static void c5471_mdwrite (int adr, int reg, int data);
  static int  c5471_mdread (int adr, int reg);
  static int  c5471_phyinit (void);
  
  /* Support logic */
  
  static inline void c5471_inctxcpu(struct c5471_driver_s *priv);
  static inline void c5471_incrxcpu(struct c5471_driver_s *priv);
  
  /* Common TX logic */
  
  static int  c5471_transmit(struct c5471_driver_s *priv);
  static int  c5471_txpoll(struct net_driver_s *dev);
  
  /* Interrupt handling */
  
  #ifdef CONFIG_C5471_NET_STATS
  static void c5471_rxstatus(struct c5471_driver_s *priv);
  #endif
  static void c5471_receive(struct c5471_driver_s *priv);
  #ifdef CONFIG_C5471_NET_STATS
  static void c5471_txstatus(struct c5471_driver_s *priv);
  #endif
  static void c5471_txdone(struct c5471_driver_s *priv);
  
  static void c5471_interrupt_work(FAR void *arg);
  static int  c5471_interrupt(int irq, FAR void *context, FAR void *arg);
  
  /* Watchdog timer expirations */
  
  static void c5471_txtimeout_work(FAR void *arg);
  static void c5471_txtimeout_expiry(int argc, uint32_t arg, ...);
  
  static void c5471_poll_work(FAR void *arg);
  static void c5471_poll_expiry(int argc, uint32_t arg, ...);
  
  /* NuttX callback functions */
  
  static int c5471_ifup(struct net_driver_s *dev);
  static int c5471_ifdown(struct net_driver_s *dev);
  
  static void c5471_txavail_work(FAR void *arg);
  static int c5471_txavail(struct net_driver_s *dev);
  
  #ifdef CONFIG_NET_MCASTGROUP
  static int c5471_addmac(struct net_driver_s *dev, FAR const uint8_t *mac);
  static int c5471_rmmac(struct net_driver_s *dev, FAR const uint8_t *mac);
  #endif
  
  /* Initialization functions */
  
  static void c5471_eimreset (struct c5471_driver_s *priv);
  static void c5471_eimconfig(struct c5471_driver_s *priv);
  static void c5471_reset(struct c5471_driver_s *priv);
  static void c5471_macassign(struct c5471_driver_s *priv);
  
  /****************************************************************************
   * Private Functions
   ****************************************************************************/
  
  /****************************************************************************
   * Name: c5471_dumpbuffer
   *
   * Description:
   *   Debug only
   *
   ****************************************************************************/
  
  #ifdef CONFIG_C5471_NET_DUMPBUFFER
  static inline void c5471_dumpbuffer(const char *msg, const uint8_t *buffer,
                                      unsigned int nbytes)
  {
    /* CONFIG_DEBUG_FEATURES, CONFIG_DEBUG_INFO, and CONFIG_DEBUG_NET have to be
     * defined or the following does nothing.
     */
  
    ninfodumpbuffer(msg, buffer, nbytes);
  }
  #else
  # define c5471_dumpbuffer(msg, buffer,nbytes)
  #endif
  
  /****************************************************************************
   * Name: c5471_mdtxbit
   *
   * Description:
   *   A helper routine used when serially communicating with the c547X's
   *   external ethernet transeiver device. GPIO pins are connected to the
   *   transeiver's MDCLK and MDIO pins and are used to accomplish the serial
   *   comm.
   *
   *   protocol:
   *                      ___________
   *     MDCLK   ________/           \_
   *              ________:____
   *     MDIO    <________:____>--------
   *                      :
   *                      ^
   *              Pin state internalized
   *
   ****************************************************************************/
  
  static void c5471_mdtxbit (int bit_state)
  {
    /* Note: any non-zero "bit_state" supplied by the caller means we should clk a "1"
     * out the MDIO pin.
     */
  
    /* Config MDIO as output pin. */
  
    putreg32((getreg32(GPIO_CIO) & ~GPIO_CIO_MDIO), GPIO_CIO);
  
    /* Select the bit output state */
  
    if (bit_state)
      {
        /* set MDIO state high. */
  
        putreg32((getreg32(GPIO_IO) | GPIO_CIO_MDIO), GPIO_IO);
      }
    else
      {
        /* set MDIO state low. */
  
        putreg32((getreg32(GPIO_IO) & ~GPIO_CIO_MDIO), GPIO_IO);
      }
  
    nop();
    nop();
    nop();
    nop();
  
    /* MDCLK rising edge */
  
    putreg32((getreg32(GPIO_IO) | GPIO_IO_MDCLK), GPIO_IO);
    nop();
    nop();
  
    /* release MDIO */
  
    putreg32((getreg32(GPIO_CIO) | GPIO_CIO_MDIO), GPIO_CIO);
    nop();
    nop();
  
    /* MDCLK falling edge. */
  
    putreg32((getreg32(GPIO_IO) & ~GPIO_IO_MDCLK), GPIO_IO);
  }
  
  /****************************************************************************
   * Name: c5471_mdrxbit
   *
   * Description:
   *    A helper routine used when serially communicating with the c547X's
   *    external ethernet transeiver device. GPIO pins are connected to the
   *    transeiver's MDCLK and MDIO pins and are used to accomplish the serial
   *    comm.
   *
   *    protocol:
   *                       ___________
   *       MDCLK  ________/           \_
   *              _______:_____
   *       MDIO   _______:_____>--------
   *                     :
   *                     ^
   *            pin state sample point
   *
   ****************************************************************************/
  
  static int c5471_mdrxbit (void)
  {
    register volatile uint32_t bit_state;
  
    /* config MDIO as input pin. */
  
    putreg32((getreg32(GPIO_CIO) | GPIO_CIO_MDIO), GPIO_CIO);
  
    /* Make sure the MDCLK is low */
  
    putreg32((getreg32(GPIO_IO) & ~GPIO_IO_MDCLK), GPIO_IO);
    nop();
    nop();
    nop();
    nop();
  
    /* Sample MDIO */
  
    bit_state = getreg32(GPIO_IO) & GPIO_CIO_MDIO;
  
    /* MDCLK rising edge */
  
    putreg32((getreg32(GPIO_IO) | GPIO_IO_MDCLK), GPIO_IO);
    nop();
    nop();
    nop();
    nop();
  
    /* MDCLK falling edge. */
  
    putreg32((getreg32(GPIO_IO) & ~GPIO_IO_MDCLK), GPIO_IO); /* MDCLK falling edge */
    if (bit_state)
      {
        return 1;
      }
    else
      {
        return OK;
      }
  }
  
  /****************************************************************************
   * Name: c5471_mdwrite
   *
   * Description:
   *    A helper routine used when serially communicating with the c547X's
   *    external ethernet transeiver device. GPIO pins are connected to the
   *    transeiver's MDCLK and MDIO pins and are used to accomplish the serial
   *    comm.
   *
   ****************************************************************************/
  
  static void c5471_mdwrite (int adr, int reg, int data)
  {
    int i;
  
    /* preamble: 11111111111111111111111111111111 */
  
    for (i = 0; i < 32; i++)
      {
        c5471_mdtxbit(1);
      }
  
    /* start of frame: 01 */
  
    c5471_mdtxbit(0);
    c5471_mdtxbit(1);
  
    /* operation code: 01 - write */
  
    c5471_mdtxbit(0);
    c5471_mdtxbit(1);
  
    /* PHY device address: AAAAA, msb first */
  
    for (i = 0; i < 5; i++)
      {
        c5471_mdtxbit(adr & 0x10);
        adr = adr << 1;
      }
  
    /* MII register address: RRRRR, msb first */
  
    for (i = 0; i < 5; i++)
      {
        c5471_mdtxbit(reg & 0x10);
        reg = reg << 1;
      }
  
    /* Turnaround time: ZZ */
  
    c5471_mdtxbit(1);
    c5471_mdtxbit(0);
  
    /* data: DDDDDDDDDDDDDDDD, msb first */
  
    for (i = 0; i < 16; i++)
      {
        c5471_mdtxbit(data & 0x8000);
        data = data << 1;
      }
  }
  
  /****************************************************************************
   * Name: c5471_mdread
   *
   * Description:
   *    A helper routine used when serially communicating with the c547X's
   *    external ethernet transeiver device. GPIO pins are connected to the
   *    transeiver's MDCLK and MDIO pins and are used to accomplish the serial
   *    comm.
   *
   ****************************************************************************/
  
  static int c5471_mdread (int adr, int reg)
  {
    int i;
    int data = 0;
  
    /* preamble: 11111111111111111111111111111111 */
  
    for (i = 0; i < 32; i++)
      {
        c5471_mdtxbit(1);
      }
  
    /* start of frame: 01 */
  
    c5471_mdtxbit(0);
    c5471_mdtxbit(1);
  
    /* operation code: 10 - read */
  
    c5471_mdtxbit(1);
    c5471_mdtxbit(0);
  
    /* PHY device address: AAAAA, msb first */
  
    for (i = 0; i < 5; i++)
      {
        c5471_mdtxbit(adr & 0x10);
        adr = adr << 1;
      }
  
    /* MII register address: RRRRR, msb first */
  
    for (i = 0; i < 5; i++)
      {
        c5471_mdtxbit(reg & 0x10);
        reg = reg << 1;
      }
  
    /* turnaround time: ZZ */
  
    c5471_mdrxbit();
    c5471_mdrxbit(); /* PHY should drive a 0 */
  
    /* data: DDDDDDDDDDDDDDDD, msb first */
  
    for (i = 0; i < 16; i++)
      {
        data = data << 1;
        data |= c5471_mdrxbit();
      }
  
    return data;
  }
  
  /****************************************************************************
   * Name: c5471_phyinit
   *
   * Description:
   *   The c547X EVM board uses a Lucent LU3X31T-T64 transeiver device to
   *   handle the physical layer (PHY). It's a h/w block that on the one end
   *   offers a Media Independent Interface (MII) which is connected to the
   *   Ethernet Interface Module (EIM) internal to the C547x and on the other
   *   end offers either the 10baseT or 100baseT electrical interface connecting
   *   to an RJ45 onboard network connector. The PHY transeiver has several
   *   internal registers allowing host configuration and status access. These
   *   internal registers are accessible by clocking serial data in/out of the
   *   MDIO pin of the LU3X31T-T64 chip. For c547X, the MDC and the MDIO pins
   *   are connected to the C547x GPIO15 and GPIO14 pins respectively. Host
   *   software twiddles the GPIO pins appropriately to get data serially into
   *   and out of the chip. This is typically a one time operation at boot and
   *   normal operation of the transeiver involves EIM/Transeiver interaction at
   *   the other pins of the transeiver chip and doesn't require host intervention
   *   at the MDC and MDIO pins.
   *
   ****************************************************************************/
  
  #if defined(CONFIG_C5471_PHY_LU3X31T_T64)
  static int c5471_phyinit (void)
  {
    int phyid;
    int status;
  
    /* Next, Setup GPIO pins to talk serially to the Lucent transeiver chip */
  
    /* enable gpio bits 15,14 */
  
    putreg32((getreg32(GPIO_EN) | 0x0000C000), GPIO_EN);
  
    /* config gpio(15); out -> MDCLK */
  
    putreg32((getreg32(GPIO_CIO) & ~0x00008000), GPIO_CIO);
  
    /* config gpio(14); in <- MDIO */
  
    putreg32((getreg32(GPIO_CIO) | 0x00004000), GPIO_CIO);
  
    /* initial pin state; MDCLK = 0 */
  
    putreg32((getreg32(GPIO_IO) & 0x000F3FFF), GPIO_IO);
  
    /* Next, request a chip reset */
  
    c5471_mdwrite(0, MD_PHY_CONTROL_REG, 0x8000);
    while (c5471_mdread(0, MD_PHY_CONTROL_REG) & 0x8000)
      {
        /* wait for chip reset to complete */
      }
  
    /* Next, Read out the chip ID */
  
    phyid = (c5471_mdread(0, MD_PHY_MSB_REG) << 16) | c5471_mdread(0, MD_PHY_LSB_REG);
    if (phyid != LU3X31_T64_PHYID)
      {
        nerr("ERROR: Unrecognized PHY ID: %08x\n", phyid);
        return -ENODEV;
      }
  
    /* Next, Set desired network rate, 10BaseT, 100BaseT, or auto. */
  
  #ifdef CONFIG_C5471_AUTONEGOTIATION
    ninfo("Setting PHY Transceiver for Autonegotiation\n");
    c5471_mdwrite(0, MD_PHY_CONTROL_REG, MODE_AUTONEG);
  #endif
  #ifdef CONFIG_C5471_BASET100
    ninfo("Setting PHY Transceiver for 100BaseT FullDuplex\n");
    c5471_mdwrite(0, MD_PHY_CONTROL_REG, MODE_100MBIT_FULLDUP);
  #endif
  #ifdef CONFIG_C5471_BASET10
    ninfo("Setting PHY Transceiver for 10BaseT FullDuplex\n");
    c5471_mdwrite(0, MD_PHY_CONTROL_REG, MODE_10MBIT_FULLDUP);
  #endif
  
    status = c5471_mdread(0, MD_PHY_CTRL_STAT_REG);
    return status;
  }
  
  #elif defined(CONFIG_C5471_PHY_AC101L)
  
  static int c5471_phyinit (void)
  {
    int phyid;
    int status;
  
    /* Next, Setup GPIO pins to talk serially to the Lucent transeiver chip */
  
    putreg32((getreg32(GPIO_EN)  |  0x0000C000), GPIO_EN);   /* enable gpio bits 15,14 */
    putreg32((getreg32(GPIO_CIO) & ~0x00008000), GPIO_CIO); /* config gpio(15); out -> MDCLK */
    putreg32((getreg32(GPIO_CIO) |  0x00004000), GPIO_CIO);  /* config gpio(14); in <- MDIO */
    putreg32((getreg32(GPIO_IO)  &  0x000F3FFF), GPIO_IO);   /* initial pin state; MDCLK = 0 */
  
    return 1;
  }
  
  #else
  #  define c5471_phyinit()
  #  warning "Assuming no PHY"
  #endif
  
  /****************************************************************************
   * Name: c5471_inctxcpu
   *
   * Description:
   *
   ****************************************************************************/
  
  static inline void c5471_inctxcpu(struct c5471_driver_s *priv)
  {
    if (EIM_TXDESC_WRAP_NEXT & getreg32(priv->c_txcpudesc))
      {
        /* Loop back around to base of descriptor queue */
  
        priv->c_txcpudesc = getreg32(EIM_CPU_TXBA) + EIM_RAM_START;
      }
    else
      {
        priv->c_txcpudesc += 2*sizeof(uint32_t);
      }
  
    ninfo("TX CPU desc: %08x\n", priv->c_txcpudesc);
  }
  
  /****************************************************************************
   * Name: c5471_incrxcpu
   *
   * Description:
   *
   ****************************************************************************/
  
  static inline void c5471_incrxcpu(struct c5471_driver_s *priv)
  {
    if (EIM_RXDESC_WRAP_NEXT & getreg32(priv->c_rxcpudesc))
      {
        /* Loop back around to base of descriptor queue */
  
        priv->c_rxcpudesc = getreg32(EIM_CPU_RXBA) + EIM_RAM_START;
      }
    else
      {
        priv->c_rxcpudesc += 2*sizeof(uint32_t);
      }
  
    ninfo("RX CPU desc: %08x\n", priv->c_rxcpudesc);
  }
  
  /****************************************************************************
   * Function: c5471_transmit
   *
   * Description:
   *   Start hardware transmission.  Called either from the txdone interrupt
   *   handling or from watchdog based polling.
   *
   * Input Parameters:
   *   priv  - Reference to the driver state structure
   *
   * Returned Value:
   *   OK on success; a negated errno on failure
   *
   * Assumptions:
   *
   ****************************************************************************/
  
  static int c5471_transmit(struct c5471_driver_s *priv)
  {
    struct net_driver_s *dev = &priv->c_dev;
    volatile uint16_t *packetmem;
    uint16_t framelen;
    bool bfirstframe;
    int nbytes;
    int nshorts;
    unsigned int i;
    unsigned int j;
  
    nbytes                = (dev->d_len + 1) & ~1;
    j                     = 0;
    bfirstframe           = true;
    priv->c_lastdescstart = priv->c_rxcpudesc;
  
    ninfo("Packet size: %d RX CPU desc: %08x\n", nbytes, priv->c_rxcpudesc);
    c5471_dumpbuffer("Transmit packet", dev->d_buf, dev->d_len);
  
    while (nbytes)
      {
        /* Verify that the hardware is ready to send another packet */
        /* Words #0 and #1 of descriptor */
  
        while (EIM_TXDESC_OWN_HOST & getreg32(priv->c_rxcpudesc))
          {
            /* Loop until the SWITCH lets go of the descriptor giving us access
             * rights to submit our new ether frame to it.
             */
          }
  
        if (bfirstframe)
          {
            putreg32((getreg32(priv->c_rxcpudesc) | EIM_RXDESC_FIF), priv->c_rxcpudesc);
          }
        else
          {
            putreg32((getreg32(priv->c_rxcpudesc) & ~EIM_RXDESC_FIF), priv->c_rxcpudesc);
          }
  
        putreg32((getreg32(priv->c_rxcpudesc) & ~EIM_RXDESC_PADCRC), priv->c_rxcpudesc);
  
        if (bfirstframe)
          {
            putreg32((getreg32(priv->c_rxcpudesc) | EIM_RXDESC_PADCRC), priv->c_rxcpudesc);
          }
  
        if (nbytes >= EIM_PACKET_BYTES)
          {
            framelen = EIM_PACKET_BYTES;
          }
        else
          {
            framelen = nbytes;
          }
  
        /* Submit ether frame bytes to the C5472 Ether Module packet memory space. */
        /* Get the number of 16-bit values to transfer by dividing by 2 with round up. */
  
        nshorts = (framelen + 1) >> 1;
  
        /* Words #2 and #3 of descriptor */
  
        packetmem = (uint16_t *)getreg32(priv->c_rxcpudesc + sizeof(uint32_t));
        for (i = 0; i < nshorts; i++, j++)
          {
            /* 16-bits at a time. */
  
            packetmem[i] = htons(((uint16_t *)dev->d_buf)[j]);
          }
  
        putreg32(((getreg32(priv->c_rxcpudesc) & ~EIM_RXDESC_BYTEMASK) | framelen),
                 priv->c_rxcpudesc);
        nbytes -= framelen;
        ninfo("Wrote framelen: %d nbytes: %d nshorts: %d\n", framelen, nbytes, nshorts);
  
        if (0 == nbytes)
          {
            putreg32((getreg32(priv->c_rxcpudesc) | EIM_RXDESC_LIF), priv->c_rxcpudesc);
          }
        else
          {
            putreg32((getreg32(priv->c_rxcpudesc) & ~EIM_RXDESC_LIF), priv->c_rxcpudesc);
          }
  
        /* We're done with that descriptor; give access rights back to h/w */
  
        putreg32((getreg32(priv->c_rxcpudesc) | EIM_RXDESC_OWN_HOST), priv->c_rxcpudesc);
  
        /* Next, tell Ether Module that those submitted bytes are ready for the wire */
  
        putreg32(0x00000001, EIM_CPU_RXREADY);
        priv->c_lastdescend = priv->c_rxcpudesc;
  
        /* Advance to the next free descriptor */
  
        c5471_incrxcpu(priv);
        bfirstframe = false;
      }
  
    /* Packet transferred .. Update statistics */
  
  #ifdef CONFIG_C5471_NET_STATS
    priv->c_txpackets++;
  #endif
  
    /* Setup the TX timeout watchdog (perhaps restarting the timer) */
  
    wd_start(priv->c_txtimeout, C5471_TXTIMEOUT,
             c5471_txtimeout_expiry, 1, (wdparm_t)priv);
    return OK;
  }
  
  /****************************************************************************
   * Function: c5471_txpoll
   *
   * Description:
   *   The transmitter is available, check if the network has any outgoing packets ready
   *   to send.  This is a callback from devif_poll().  devif_poll() may be called:
   *
   *   1. When the preceding TX packet send is complete,
   *   2. When the preceding TX packet send timesout and the interface is reset
   *   3. During normal TX polling
   *
   * Input Parameters:
   *   dev  - Reference to the NuttX driver state structure
   *
   * Returned Value:
   *   OK on success; a negated errno on failure
   *
   * Assumptions:
   *
   ****************************************************************************/
  
  static int c5471_txpoll(struct net_driver_s *dev)
  {
    struct c5471_driver_s *priv = (struct c5471_driver_s *)dev->d_private;
  
    /* If the polling resulted in data that should be sent out on the network,
     * the field d_len is set to a value > 0.
     */
  
    if (priv->c_dev.d_len > 0)
      {
        /* Look up the destination MAC address and add it to the Ethernet
         * header.
         */
  
  #ifdef CONFIG_NET_IPv4
  #ifdef CONFIG_NET_IPv6
        if (IFF_IS_IPv4(priv->c_dev.d_flags))
  #endif
          {
            arp_out(&priv->c_dev);
          }
  #endif /* CONFIG_NET_IPv4 */
  
  #ifdef CONFIG_NET_IPv6
  #ifdef CONFIG_NET_IPv4
        else
  #endif
          {
            neighbor_out(&priv->c_dev);
          }
  #endif /* CONFIG_NET_IPv6 */
  
        if (!devif_loopback(&priv->c_dev))
          {
            /* Send the packet */
  
            c5471_transmit(priv);
  
            /* Check if the ESM has let go of the RX descriptor giving us access
             * rights to submit another Ethernet frame.
             */
  
            if ((EIM_TXDESC_OWN_HOST & getreg32(priv->c_rxcpudesc)) != 0)
              {
                /* No, then return non-zero to terminate the poll */
  
                return 1;
              }
          }
      }
  
    /* If zero is returned, the polling will continue until all connections have
     * been examined.
     */
  
    return 0;
  }
  
  /****************************************************************************
   * Function: c5471_rxstatus
   *
   * Description:
   *   An interrupt was received indicating that the last RX packet(s) is done
   *
   * Input Parameters:
   *   priv  - Reference to the driver state structure
   *
   * Returned Value:
   *   None
   *
   * Assumptions:
   *
   ****************************************************************************/
  
  #ifdef CONFIG_C5471_NET_STATS
  static void c5471_rxstatus(struct c5471_driver_s *priv)
  {
    uint32_t desc = priv->c_txcpudesc;
    uint32_t rxstatus;
  
    /* Walk that last packet we just received to collect xmit status bits. */
  
    rxstatus = 0;
    for (; ; )
      {
        if (EIM_TXDESC_OWN_HOST & getreg32(desc))
          {
            /* The incoming packet queue is empty. */
  
            break;
          }
  
        rxstatus |= (getreg32(desc) & EIM_TXDESC_STATUSMASK);
  
        if ((getreg32(desc) & EIM_TXDESC_LIF) != 0)
          {
            break;
          }
  
        /* This packet is made up of several descriptors, find next one in chain. */
  
        if (EIM_TXDESC_WRAP_NEXT & getreg32(desc))
          {
            /* Loop back around to base of descriptor queue. */
  
            desc = getreg32(EIM_CPU_TXBA) + EIM_RAM_START;
          }
        else
          {
            desc += 2 * sizeof(uint32_t);
          }
      }
  
    if (rxstatus != 0)
      {
        if ((rxstatus & EIM_TXDESC_RETRYERROR) != 0)
          {
            priv->c_rxretries++;
            ninfo("c_rxretries: %d\n", priv->c_rxretries);
          }
  
        if ((rxstatus & EIM_TXDESC_HEARTBEAT) != 0)
          {
            priv->c_rxheartbeat++;
            ninfo("c_rxheartbeat: %d\n", priv->c_rxheartbeat);
          }
  
        if ((rxstatus & EIM_TXDESC_LCOLLISON) != 0)
          {
            priv->c_rxlcollision++;
            ninfo("c_rxlcollision: %d\n", priv->c_rxlcollision);
          }
  
        if ((rxstatus & EIM_TXDESC_COLLISION) != 0)
          {
            priv->c_rxcollision++;
            ninfo("c_rxcollision: %d\n", priv->c_rxcollision);
          }
  
        if ((rxstatus & EIM_TXDESC_CRCERROR) != 0)
          {
            priv->c_rxcrc++;
            ninfo("c_rxcrc: %d\n", priv->c_rxcrc);
          }
  
        if ((rxstatus & EIM_TXDESC_UNDERRUN) != 0)
          {
            priv->c_rxunderrun++;
            ninfo("c_rxunderrun: %d\n", priv->c_rxunderrun);
          }
  
        if ((rxstatus & EIM_TXDESC_LOC) != 0)
          {
            priv->c_rxloc++;
            ninfo("c_rxloc: %d\n", priv->c_rxloc);
          }
      }
  }
  #endif
  
  /****************************************************************************
   * Function: c5471_receive
   *
   * Description:
   *   An interrupt was received indicating the availability of a new RX packet
   *
   * Input Parameters:
   *   priv  - Reference to the driver state structure
   *
   * Returned Value:
   *   None
   *
   * Assumptions:
   *
   ****************************************************************************/
  
  static void c5471_receive(struct c5471_driver_s *priv)
  {
    struct net_driver_s *dev = &priv->c_dev;
    uint16_t *packetmem;
    bool bmore = true;
    int packetlen = 0;
    int framelen;
    int nshorts;
    int i;
    int j = 0;
  
    /* Walk the newly received packet contained within the EIM and transfer
     * its contents to the network buffer. This frees up the memory contained within
     * the EIM for additional packets that might be received later from the network.
     */
  
    ninfo("Reading TX CPU desc: %08x\n", priv->c_txcpudesc);
    while (bmore)
      {
        /* Words #0 and #1 of descriptor */
  
        if (EIM_TXDESC_OWN_HOST & getreg32(priv->c_txcpudesc))
          {
            /* No further packets to receive. */
  
            break;
          }
  
        /* Get the size of the frame from words #0 and #1 of the descriptor
         * and update the accumulated packet size
         */
  
        framelen   = (getreg32(priv->c_txcpudesc) & EIM_TXDESC_BYTEMASK);
        packetlen += framelen;
  
        /* Check if the received packet will fit within the network packet buffer */
  
        if (packetlen < (CONFIG_NET_ETH_PKTSIZE + 4))
          {
            /* Get the packet memory from words #2 and #3 of descriptor */
  
            packetmem = (uint16_t *)getreg32(priv->c_txcpudesc + sizeof(uint32_t));
  
            /* Divide by 2 with round up to get the number of 16-bit words. */
  
            nshorts = (framelen + 1) >> 1;
            ninfo("Reading framelen: %d packetlen: %d nshorts: %d packetmen: %p\n",
                   framelen, packetlen, nshorts, packetmem);
  
            for (i = 0 ; i < nshorts; i++, j++)
              {
                /* Copy the data data from the hardware to priv->c_dev.d_buf 16-bits at
                 * a time.
                 */
  
                ((uint16_t *)dev->d_buf)[j] = htons(packetmem[i]);
              }
          }
        else
          {
            ninfo("Discarding framelen: %d packetlen\n", framelen, packetlen);
          }
  
        if (getreg32(priv->c_txcpudesc) & EIM_TXDESC_LIF)
          {
            bmore = false;
          }
  
        /* Next, Clear all bits of words0/1 of the emptied descriptor except preserve
         * the settings of a select few. Can leave descriptor words 2/3 alone.
         */
  
        putreg32((getreg32(priv->c_txcpudesc) & (EIM_TXDESC_WRAP_NEXT | EIM_TXDESC_INTRE)),
                 priv->c_txcpudesc);
  
        /* Next, Give ownership of now emptied descriptor back to the Ether Module's SWITCH */
  
        putreg32((getreg32(priv->c_txcpudesc) | EIM_TXDESC_OWN_HOST), priv->c_txcpudesc);
  
        /* Advance to the next data buffer */
  
        c5471_inctxcpu(priv);
      }
  
    /* Adjust the packet length to remove the CRC bytes that the network doesn't care about. */
  
    packetlen -= 4;
  
  #ifdef CONFIG_C5471_NET_STATS
    /* Increment the count of received packets */
  
    priv->c_rxpackets++;
  #endif
  
    /* If we successfully transferred the data into the network buffer, then pass it on
     * to the network for processing.
     */
  
    if (packetlen > 0 && packetlen < CONFIG_NET_ETH_PKTSIZE)
      {
        /* Set amount of data in priv->c_dev.d_len. */
  
        dev->d_len = packetlen;
        ninfo("Received packet, packetlen: %d type: %02x\n", packetlen, ntohs(BUF->type));
        c5471_dumpbuffer("Received packet", dev->d_buf, dev->d_len);
  
  #ifdef CONFIG_NET_PKT
        /* When packet sockets are enabled, feed the frame into the packet tap */
  
        pkt_input(dev);
  #endif
  
        /* We only accept IP packets of the configured type and ARP packets */
  
  #ifdef CONFIG_NET_IPv4
        if (BUF->type == HTONS(ETHTYPE_IP))
          {
            ninfo("IPv4 frame\n");
  
            /* Handle ARP on input then give the IPv4 packet to the network
             * layer
             */
  
            arp_ipin(dev);
            ipv4_input(dev);
  
            /* If the above function invocation resulted in data that should be
             * sent out on the network, the field  d_len will set to a value > 0.
             * Send that data now if ESM has let go of the RX descriptor giving us
             * access rights to submit another Ethernet frame.
             */
  
            if (dev->d_len > 0 &&
               (EIM_TXDESC_OWN_HOST & getreg32(priv->c_rxcpudesc)) == 0)
              {
                /* Update the Ethernet header with the correct MAC address */
  
  #ifdef CONFIG_NET_IPv6
                if (IFF_IS_IPv4(dev->d_flags))
  #endif
                  {
                    arp_out(dev);
                  }
  #ifdef CONFIG_NET_IPv6
                else
                  {
                    neighbor_out(dev);
                  }
  #endif
  
                /* And send the packet */
  
                 c5471_transmit(priv);
              }
          }
        else
  #endif
  #ifdef CONFIG_NET_IPv6
        if (BUF->type == HTONS(ETHTYPE_IP6))
          {
            ninfo("IPv6 frame\n");
  
            /* Give the IPv6 packet to the network layer */
  
            ipv6_input(dev);
  
            /* If the above function invocation resulted in data that should be
             * sent out on the network, the field  d_len will set to a value > 0.
             * Send that data now if ESM has let go of the RX descriptor giving us
             * access rights to submit another Ethernet frame.
             */
  
            if (dev->d_len > 0 &&
               (EIM_TXDESC_OWN_HOST & getreg32(priv->c_rxcpudesc)) == 0)
              {
                /* Update the Ethernet header with the correct MAC address */
  
  #ifdef CONFIG_NET_IPv4
                if (IFF_IS_IPv4(dev->d_flags))
                  {
                    arp_out(dev);
                  }
                else
  #endif
  #ifdef CONFIG_NET_IPv6
                  {
                    neighbor_out(dev);
                  }
  #endif
  
                /* And send the packet */
  
                 c5471_transmit(priv);
              }
          }
        else
  #endif
  #ifdef CONFIG_NET_ARP
        if (BUF->type == HTONS(ETHTYPE_ARP))
          {
            arp_arpin(dev);
  
            /* If the above function invocation resulted in data that should be
             * sent out on the network, the field  d_len will set to a value > 0.
             * Send that data now if ESM has let go of the RX descriptor giving us
             * access rights to submit another Ethernet frame.
             */
  
            if (dev->d_len > 0 &&
               (EIM_TXDESC_OWN_HOST & getreg32(priv->c_rxcpudesc)) == 0)
              {
                c5471_transmit(priv);
              }
          }
  #endif
      }
  #ifdef CONFIG_C5471_NET_STATS
    else
      {
        /* Increment the count of dropped packets */
  
        nwarn("WARNING: Too big! packetlen: %d\n", packetlen);
        priv->c_rxdropped++;
      }
  #endif
  }
  
  /****************************************************************************
   * Function: c5471_txstatus
   *
   * Description:
   *   An interrupt was received indicating that the last TX packet(s) is done
   *
   * Input Parameters:
   *   priv  - Reference to the driver state structure
   *
   * Returned Value:
   *   None
   *
   * Assumptions:
   *
   ****************************************************************************/
  
  #ifdef CONFIG_C5471_NET_STATS
  static void c5471_txstatus(struct c5471_driver_s *priv)
  {
    uint32_t desc = priv->c_lastdescstart;
    uint32_t txstatus;
  
    /* Walk that last packet we just sent to collect xmit status bits. */
  
    txstatus = 0;
    if (priv->c_lastdescstart && priv->c_lastdescend)
      {
        for (; ; )
          {
            txstatus |= (getreg32(desc) & EIM_RXDESC_STATUSMASK);
            if (desc == priv->c_lastdescend)
              {
                break;
              }
  
            /* This packet is made up of several descriptors, find next one in chain. */
  
            if (EIM_RXDESC_WRAP_NEXT & getreg32(priv->c_rxcpudesc))
              {
                /* Loop back around to base of descriptor queue. */
  
                desc = getreg32(EIM_CPU_RXBA) + EIM_RAM_START;
              }
            else
              {
                desc += 2 * sizeof(uint32_t);
              }
          }
      }
  
    if (txstatus)
      {
        if ((txstatus & EIM_RXDESC_MISS) != 0)
          {
            priv->c_txmiss++;
            ninfo("c_txmiss: %d\n", priv->c_txmiss);
          }
  
        if ((txstatus & EIM_RXDESC_VLAN) != 0)
          {
            priv->c_txvlan++;
            ninfo("c_txvlan: %d\n", priv->c_txvlan);
          }
  
        if ((txstatus & EIM_RXDESC_LFRAME) != 0)
          {
            priv->c_txlframe++;
            ninfo("c_txlframe: %d\n", priv->c_txlframe);
          }
  
        if ((txstatus & EIM_RXDESC_SFRAME) != 0)
          {
            priv->c_txsframe++;
            ninfo("c_txsframe: %d\n", priv->c_txsframe);
          }
  
        if ((txstatus & EIM_RXDESC_CRCERROR) != 0)
          {
            priv->c_txcrc++;
            ninfo("c_txcrc: %d\n", priv->c_txcrc);
          }
  
        if ((txstatus & EIM_RXDESC_OVERRUN) != 0)
          {
            priv->c_txoverrun++;
            ninfo("c_txoverrun: %d\n", priv->c_txoverrun);
          }
  
        if ((txstatus & EIM_RXDESC_OVERRUN) != 0)
          {
            priv->c_txalign++;
            ninfo("c_txalign: %d\n", priv->c_txalign);
          }
      }
  }
  #endif
  
  /****************************************************************************
   * Function: c5471_txdone
   *
   * Description:
   *   An interrupt was received indicating that the last TX packet(s) is done
   *
   * Input Parameters:
   *   priv  - Reference to the driver state structure
   *
   * Returned Value:
   *   None
   *
   * Assumptions:
   *
   ****************************************************************************/
  
  static void c5471_txdone(struct c5471_driver_s *priv)
  {
    /* If no further xmits are pending, then cancel the TX timeout */
  
    wd_cancel(priv->c_txtimeout);
  
    /* Then poll the network for new XMIT data */
  
    devif_poll(&priv->c_dev, c5471_txpoll);
  }
  
  /****************************************************************************
   * Function: c5471_interrupt_work
   *
   * Description:
   *   Perform interrupt related work from the worker thread
   *
   * Input Parameters:
   *   arg - The argument passed when work_queue() was called.
   *
   * Returned Value:
   *   OK on success
   *
   * Assumptions:
   *   The network is locked.
   *
   ****************************************************************************/
  
  static void c5471_interrupt_work(FAR void *arg)
  {
    FAR struct c5471_driver_s *priv = (FAR struct c5471_driver_s *)arg;
  
    /* Process pending Ethernet interrupts */
  
    net_lock();
  
    /* Get and clear interrupt status bits */
  
    priv->c_eimstatus = getreg32(EIM_STATUS);
  
    /* Handle interrupts according to status bit settings */
    /* Check if we received an incoming packet, if so, call c5471_receive() */
  
    if ((EIM_STATUS_CPU_TX & priv->c_eimstatus) != 0)
      {
        /* An incoming packet has been received by the EIM from the network and
         * the interrupt associated with EIM's CPU TX queue has been asserted. It
         * is the EIM's CPU TX queue that we need to read from to get those
         * packets.  We use this terminology to stay consistent with the Orion
         * documentation.
         */
  
  #ifdef CONFIG_C5471_NET_STATS
        /* Check for RX errors */
  
        c5471_rxstatus(priv);
  #endif
  
        /* Process the received packet */
  
        c5471_receive(priv);
      }
  
    /* Check is a packet transmission just completed.  If so, call c5471_txdone */
  
    if ((EIM_STATUS_CPU_RX & priv->c_eimstatus) != 0)
      {
        /* An outgoing packet has been processed by the EIM and the interrupt
         * associated with EIM's CPU RX queue has been asserted. It is the EIM's
         * CPU RX queue that we put packets on to send them *out*. TWe use this
         * terminology to stay consistent with the Orion documentation.
         */
  
  #ifdef CONFIG_C5471_NET_STATS
        /* Check for TX errors */
  
        c5471_txstatus(priv);
  #endif
  
        /* Handle the transmission done event */
  
        c5471_txdone(priv);
      }
  
    net_unlock();
  
    /* Re-enable Ethernet interrupts */
  
    up_enable_irq(C5471_IRQ_ETHER);
  }
  
  /****************************************************************************
   * Function: c5471_interrupt
   *
   * Description:
   *   Hardware interrupt handler
   *
   * Input Parameters:
   *   irq     - Number of the IRQ that generated the interrupt
   *   context - Interrupt register state save info (architecture-specific)
   *
   * Returned Value:
   *   OK on success
   *
   * Assumptions:
   *
   ****************************************************************************/
  
  static int c5471_interrupt(int irq, FAR void *context, FAR void *arg)
  {
  #if CONFIG_C5471_NET_NINTERFACES == 1
    register struct c5471_driver_s *priv = &g_c5471[0];
  #else
  # error "Additional logic needed to support multiple interfaces"
  #endif
  
    /* Disable further Ethernet interrupts.  Because Ethernet interrupts are
     * also disabled if the TX timeout event occurs, there can be no race
     * condition here.
     */
  
    up_disable_irq(C5471_IRQ_ETHER);
  
    /* TODO: Determine if a TX transfer just completed */
  
      {
        /* If a TX transfer just completed, then cancel the TX timeout so
         * there will be no race condition between any subsequent timeout
         * expiration and the deferred interrupt processing.
         */
  
         wd_cancel(priv->c_txtimeout);
      }
  
    /* Schedule to perform the interrupt processing on the worker thread. */
  
    work_queue(ETHWORK, &priv->c_irqwork, c5471_interrupt_work, priv, 0);
    return OK;
  }
  
  /****************************************************************************
   * Function: c5471_txtimeout_work
   *
   * Description:
   *   Perform TX timeout related work from the worker thread
   *
   * Input Parameters:
   *   arg - The argument passed when work_queue() as called.
   *
   * Returned Value:
   *   OK on success
   *
   * Assumptions:
   *   The network is locked.
   *
   ****************************************************************************/
  
  static void c5471_txtimeout_work(FAR void *arg)
  {
    FAR struct c5471_driver_s *priv = (FAR struct c5471_driver_s *)arg;
  
    /* Increment statistics */
  
    net_lock();
  #ifdef CONFIG_C5471_NET_STATS
    priv->c_txtimeouts++;
    ninfo("c_txtimeouts: %d\n", priv->c_txtimeouts);
  #endif
  
    /* Then try to restart the hardware */
  
    c5471_ifdown(&priv->c_dev);
    c5471_ifup(&priv->c_dev);
  
    /* Then poll the network for new XMIT data */
  
    devif_poll(&priv->c_dev, c5471_txpoll);
    net_unlock();
  }
  
  /****************************************************************************
   * Function: c5471_txtimeout_expiry
   *
   * Description:
   *   Our TX watchdog timed out.  Called from the timer interrupt handler.
   *   The last TX never completed.  Reset the hardware and start again.
   *
   * Input Parameters:
   *   argc - The number of available arguments
   *   arg  - The first argument
   *
   * Returned Value:
   *   None
   *
   * Assumptions:
   *   Global interrupts are disabled by the watchdog logic.
   *
   ****************************************************************************/
  
  static void c5471_txtimeout_expiry(int argc, wdparm_t arg, ...)
  {
    struct c5471_driver_s *priv = (struct c5471_driver_s *)arg;
  
    /* Disable further Ethernet interrupts.  This will prevent some race
     * conditions with interrupt work.  There is still a potential race
     * condition with interrupt work that is already queued and in progress.
     */
  
    up_disable_irq(C5471_IRQ_ETHER);
  
    /* Schedule to perform the TX timeout processing on the worker thread,
     * canceling any pending IRQ work.
     */
  
    work_queue(ETHWORK, &priv->c_irqwork, c5471_txtimeout_work, priv, 0);
  }
  
  /****************************************************************************
   * Function: c5471_poll_work
   *
   * Description:
   *   Perform periodic polling from the worker thread
   *
   * Input Parameters:
   *   arg - The argument passed when work_queue() as called.
   *
   * Returned Value:
   *   OK on success
   *
   * Assumptions:
   *   The network is locked.
   *
   ****************************************************************************/
  
  static void c5471_poll_work(FAR void *arg)
  {
    FAR struct c5471_driver_s *priv = (FAR struct c5471_driver_s *)arg;
  
    /* Check if the ESM has let go of the RX descriptor giving us access rights
     * to submit another Ethernet frame.
     */
  
    net_lock();
    if ((EIM_TXDESC_OWN_HOST & getreg32(priv->c_rxcpudesc)) == 0)
      {
        /* If so, update TCP timing states and poll the network for new XMIT data */
  
        devif_timer(&priv->c_dev, C5471_WDDELAY, c5471_txpoll);
      }
  
    /* Setup the watchdog poll timer again */
  
    wd_start(priv->c_txpoll, C5471_WDDELAY, c5471_poll_expiry, 1,
             (wdparm_t)priv);
    net_unlock();
  }
  
  /****************************************************************************
   * Function: c5471_poll_expiry
   *
   * Description:
   *   Periodic timer handler.  Called from the timer interrupt handler.
   *
   * Input Parameters:
   *   argc - The number of available arguments
   *   arg  - The first argument
   *
   * Returned Value:
   *   None
   *
   * Assumptions:
   *   Global interrupts are disabled by the watchdog logic.
   *
   ****************************************************************************/
  
  static void c5471_poll_expiry(int argc, wdparm_t arg, ...)
  {
    struct c5471_driver_s *priv = (struct c5471_driver_s *)arg;
  
    /* Schedule to perform the interrupt processing on the worker thread. */
  
    work_queue(ETHWORK, &priv->c_pollwork, c5471_poll_work, priv, 0);
  }
  
  /****************************************************************************
   * Function: c5471_ifup
   *
   * Description:
   *   NuttX Callback: Bring up the Ethernet interface when an IP address is
   *   provided
   *
   * Input Parameters:
   *   dev  - Reference to the NuttX driver state structure
   *
   * Returned Value:
   *   None
   *
   * Assumptions:
   *   The user has assigned a MAC to the driver
   *
   ****************************************************************************/
  
  static int c5471_ifup(struct net_driver_s *dev)
  {
    struct c5471_driver_s *priv = (struct c5471_driver_s *)dev->d_private;
    volatile uint32_t clearbits;
  
    ninfo("Bringing up: %d.%d.%d.%d\n",
          dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff,
          (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24);
  
    /* Initialize Ethernet interface */
  
    c5471_reset(priv);
  
    /* Assign the MAC to the device */
  
    c5471_macassign(priv);
  
    /* Clear pending interrupts by reading the EIM status register */
  
    clearbits = getreg32(EIM_STATUS);
    UNUSED(clearbits);
  
    /* Enable interrupts going from EIM Module to Interrupt Module. */
  
    putreg32(((getreg32(EIM_INTEN) | EIM_INTEN_CPU_TX | EIM_INTEN_CPU_RX)),
             EIM_INTEN);
  
    /* Next, go on-line. According to the C547X documentation the enables have to
     * occur in this order to insure proper operation; ESM first then the ENET.
     */
  
    putreg32((getreg32(EIM_CTRL) | EIM_CTRL_ESM_EN), EIM_CTRL);   /* enable ESM */
    putreg32((getreg32(ENET0_MODE) | ENET_MODE_ENABLE), ENET0_MODE); /* enable ENET */
    up_mdelay(100);
  
    /* Set and activate a timer process */
  
    wd_start(priv->c_txpoll, C5471_WDDELAY, c5471_poll_expiry,
             1, (wdparm_t)priv);
  
    /* Enable the Ethernet interrupt */
  
    priv->c_bifup = true;
    up_enable_irq(C5471_IRQ_ETHER);
    return OK;
  }
  
  /****************************************************************************
   * Function: c5471_ifdown
   *
   * Description:
   *   NuttX Callback: Stop the interface.
   *
   * Input Parameters:
   *   dev  - Reference to the NuttX driver state structure
   *
   * Returned Value:
   *   None
   *
   * Assumptions:
   *
   ****************************************************************************/
  
  static int c5471_ifdown(struct net_driver_s *dev)
  {
    struct c5471_driver_s *priv = (struct c5471_driver_s *)dev->d_private;
    irqstate_t flags;
  
    ninfo("Stopping\n");
  
    /* Disable the Ethernet interrupt */
  
    flags = enter_critical_section();
    up_disable_irq(C5471_IRQ_ETHER);
  
    /* Disable interrupts going from EIM Module to Interrupt Module. */
  
    putreg32((getreg32(EIM_INTEN) & ~(EIM_INTEN_CPU_TX | EIM_INTEN_CPU_RX)),
             EIM_INTEN);
  
    /* Disable ENET */
  
    putreg32((getreg32(ENET0_MODE) & ~ENET_MODE_ENABLE), ENET0_MODE); /* disable ENET */
  
    /* Disable ESM */
  
    putreg32((getreg32(EIM_CTRL) & ~EIM_CTRL_ESM_EN), EIM_CTRL);  /* disable ESM */
  
    /* Cancel the TX poll timer and TX timeout timers */
  
    wd_cancel(priv->c_txpoll);
    wd_cancel(priv->c_txtimeout);
  
    /* Reset the device */
  
    priv->c_bifup = false;
    leave_critical_section(flags);
    return OK;
  }
  
  /****************************************************************************
   * Function: c5471_txavail_work
   *
   * Description:
   *   Perform an out-of-cycle poll on the worker thread.
   *
   * Input Parameters:
   *   arg - Reference to the NuttX driver state structure (cast to void*)
   *
   * Returned Value:
   *   None
   *
   * Assumptions:
   *   Called on the higher priority worker thread.
   *
   ****************************************************************************/
  
  static void c5471_txavail_work(FAR void *arg)
  {
    FAR struct c5471_driver_s *priv = (FAR struct c5471_driver_s *)arg;
  
    ninfo("Polling\n");
  
    /* Ignore the notification if the interface is not yet up */
  
    net_lock();
    if (priv->c_bifup)
      {
        /* Check if the ESM has let go of the RX descriptor giving us access
         * rights to submit another Ethernet frame.
         */
  
        if ((EIM_TXDESC_OWN_HOST & getreg32(priv->c_rxcpudesc)) == 0)
          {
            /* If so, then poll the network for new XMIT data */
  
            devif_poll(&priv->c_dev, c5471_txpoll);
          }
      }
  
    net_unlock();
  }
  
  /****************************************************************************
   * Function: c5471_txavail
   *
   * Description:
   *   Driver callback invoked when new TX data is available.  This is a
   *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
   *   latency.
   *
   * Input Parameters:
   *   dev - Reference to the NuttX driver state structure
   *
   * Returned Value:
   *   None
   *
   * Assumptions:
   *   Called in normal user mode
   *
   ****************************************************************************/
  
  static int c5471_txavail(FAR struct net_driver_s *dev)
  {
    struct c5471_driver_s *priv = (struct c5471_driver_s *)dev->d_private;
  
    /* Is our single work structure available?  It may not be if there are
     * pending interrupt actions and we will have to ignore the Tx
     * availability action.
     */
  
    if (work_available(&priv->c_pollwork))
      {
        /* Schedule to serialize the poll on the worker thread. */
  
        work_queue(ETHWORK, &priv->c_pollwork, c5471_txavail_work, priv, 0);
      }
  
    return OK;
  }
  
  /****************************************************************************
   * Function: c5471_addmac
   *
   * Description:
   *   NuttX Callback: Add the specified MAC address to the hardware multicast
   *   address filtering
   *
   * Input Parameters:
   *   dev  - Reference to the NuttX driver state structure
   *   mac  - The MAC address to be added
   *
   * Returned Value:
   *   None
   *
   * Assumptions:
   *
   ****************************************************************************/
  
  #ifdef CONFIG_NET_MCASTGROUP
  static int c5471_addmac(struct net_driver_s *dev, FAR const uint8_t *mac)
  {
    FAR struct c5471_driver_s *priv = (FAR struct c5471_driver_s *)dev->d_private;
  
    /* Add the MAC address to the hardware multicast routing table */
  
  #warning "Multicast MAC support not implemented"
    return OK;
  }
  #endif
  
  /****************************************************************************
   * Function: c5471_rmmac
   *
   * Description:
   *   NuttX Callback: Remove the specified MAC address from the hardware multicast
   *   address filtering
   *
   * Input Parameters:
   *   dev  - Reference to the NuttX driver state structure
   *   mac  - The MAC address to be removed
   *
   * Returned Value:
   *   None
   *
   * Assumptions:
   *
   ****************************************************************************/
  
  #ifdef CONFIG_NET_MCASTGROUP
  static int c5471_rmmac(struct net_driver_s *dev, FAR const uint8_t *mac)
  {
    FAR struct c5471_driver_s *priv = (FAR struct c5471_driver_s *)dev->d_private;
  
    /* Add the MAC address to the hardware multicast routing table */
  
  #warning "Multicast MAC support not implemented"
    return OK;
  }
  #endif
  
  /****************************************************************************
   * Name: c5471_eimreset
   *
   * Description:
   *   The C547x docs states that a module should generally be reset according
   *   to the following algorithm:
   *
   *   1. Put the module in reset.
   *   2. Switch on the module clock.
   *   3. Wait for eight clock cycles.
   *   4. Release the reset.
   *
   ****************************************************************************/
  
  static void c5471_eimreset (struct c5471_driver_s *priv)
  {
    /* Stop the EIM module clock */
  
    putreg32((getreg32(CLKM) | CLKM_EIM_CLK_STOP), CLKM);
  
    /* Put EIM module in reset */
  
    putreg32((getreg32(CLKM_RESET) & ~CLKM_RESET_EIM), CLKM_RESET);
  
    /* Start the EIM module clock */
  
    putreg32((getreg32(CLKM) & ~CLKM_EIM_CLK_STOP), CLKM);
  
    /* Assert nRESET to reset the board's PHY0/1 chips */
  
    putreg32((CLKM_CTL_RST_EXT_RESET | CLKM_CTL_RST_LEAD_RESET),
             CLKM_CTL_RST);
    up_mdelay(2);
  
    /* Release the peripheral nRESET signal */
  
    putreg32(CLKM_CTL_RST_LEAD_RESET, CLKM_CTL_RST);
  
    /* Release EIM module reset */
  
    putreg32((getreg32(CLKM_RESET) | CLKM_RESET_EIM), CLKM_RESET);
  
    /* All EIM register should now be in there power-up default states */
  
    priv->c_lastdescstart = 0;
    priv->c_lastdescend   = 0;
  }
  
  /****************************************************************************
   * Name: c5471_eimconfig
   *
   * Description:
   *    Assumes that all registers are currently in the power-up reset state.
   *    This routine then modifies that state to provide our specific ethernet
   *    configuration.
   *
   ****************************************************************************/
  
  static void c5471_eimconfig(struct c5471_driver_s *priv)
  {
    volatile uint32_t pbuf;
    volatile uint32_t desc;
    volatile uint32_t val;
    int i;
  
    desc = EIM_RAM_START;
    pbuf = EIM_RAM_START + 0x6C0;
  
    /* TX ENET 0 */
  
    ninfo("TX ENET0 desc: %08x pbuf: %08x\n", desc, pbuf);
    putreg32((desc & 0x0000ffff), ENET0_TDBA); /* 16-bit offset address */
    for (i = NUM_DESC_TX-1; i >= 0; i--)
      {
        if (i == 0)
          val = EIM_TXDESC_WRAP_NEXT;
        else
          val = EIM_TXDESC_WRAP_FIRST;
  
        val |= EIM_TXDESC_OWN_HOST | EIM_TXDESC_INTRE | EIM_TXDESC_PADCRC |
               EIM_PACKET_BYTES;
  
        putreg32(val, desc);
        desc += sizeof(uint32_t);
  
        putreg32(pbuf, desc);
        desc += sizeof(uint32_t);
  
        putreg32(0, pbuf);
        pbuf += EIM_PACKET_BYTES;
  
        putreg32(0, pbuf);
        pbuf += sizeof(uint32_t); /* Ether Module's "Buffer Usage Word" */
      }
  
    /* RX ENET 0 */
  
    ninfo("RX ENET0 desc: %08x pbuf: %08x\n", desc, pbuf);
    putreg32((desc & 0x0000ffff), ENET0_RDBA); /* 16-bit offset address */
    for (i = NUM_DESC_RX-1; i >= 0; i--)
      {
        if (i == 0)
          val = EIM_RXDESC_WRAP_NEXT;
        else
          val = EIM_RXDESC_WRAP_FIRST;
  
        val |= EIM_RXDESC_OWN_ENET | EIM_RXDESC_INTRE | EIM_RXDESC_PADCRC |
               EIM_PACKET_BYTES;
  
        putreg32(val, desc);
        desc += sizeof(uint32_t);
  
        putreg32(pbuf, desc);
        desc += sizeof(uint32_t);
  
        putreg32(0, pbuf);
        pbuf += EIM_PACKET_BYTES;
  
        putreg32(0, pbuf);
        pbuf += sizeof(uint32_t); /* Ether Module's "Buffer Usage Word" */
      }
  
    /* TX CPU */
  
    ninfo("TX CPU desc: %08x pbuf: %08x\n", desc, pbuf);
    priv->c_txcpudesc = desc;
    putreg32((desc & 0x0000ffff), EIM_CPU_TXBA); /* 16-bit offset address */
    for (i = NUM_DESC_TX-1; i >= 0; i--)
      {
        /* Set words 1+2 of the TXDESC */
  
        if (i == 0)
          val = EIM_TXDESC_WRAP_NEXT;
        else
          val = EIM_TXDESC_WRAP_FIRST;
  
        val |= EIM_TXDESC_OWN_HOST | EIM_TXDESC_INTRE | EIM_TXDESC_PADCRC |
               EIM_PACKET_BYTES;
  
        putreg32(val, desc);
        desc += sizeof(uint32_t);
  
        putreg32(pbuf, desc);
        desc += sizeof(uint32_t);
  
        putreg32(0, pbuf);
        pbuf += EIM_PACKET_BYTES;
  
        putreg32(0, pbuf);
        pbuf += sizeof(uint32_t); /* Ether Module's "Buffer Usage Word" */
      }
  
    /* RX CPU */
  
    ninfo("RX CPU desc: %08x pbuf: %08x\n", desc, pbuf);
    priv->c_rxcpudesc = desc;
    putreg32((desc & 0x0000ffff), EIM_CPU_RXBA); /* 16-bit offset address */
    for (i = NUM_DESC_RX-1; i >= 0; i--)
      {
        /* Set words 1+2 of the RXDESC */
  
        if (i == 0)
          val = EIM_RXDESC_WRAP_NEXT;
        else
          val = EIM_RXDESC_WRAP_FIRST;
  
        val |= EIM_RXDESC_OWN_ENET | EIM_RXDESC_INTRE | EIM_RXDESC_PADCRC |
               EIM_PACKET_BYTES;
  
        putreg32(val, desc);
        desc += sizeof(uint32_t);
  
        putreg32(pbuf, desc);
        desc += sizeof(uint32_t);
  
        putreg32(0, pbuf);
        pbuf += EIM_PACKET_BYTES;
  
        putreg32(0, pbuf);
        pbuf += sizeof(uint32_t); /* Ether Module's "Buffer Usage Word" */
      }
  
    ninfo("END desc: %08x pbuf: %08x\n", desc, pbuf);
  
    /* Save the descriptor packet size */
  
    putreg32(EIM_PACKET_BYTES, EIM_BUFSIZE);
  
    /* Set the filter mode */
  
  #if 0
    putreg32(EIM_FILTER_UNICAST, EIM_CPU_FILTER);
  #else
  //  putreg32(EIM_FILTER_LOGICAL | EIM_FILTER_UNICAST | EIM_FILTER_MULTICAST |
  //           EIM_FILTER_BROADCAST, EIM_CPU_FILTER);
    putreg32(EIM_FILTER_UNICAST | EIM_FILTER_MULTICAST | EIM_FILTER_BROADCAST,
             EIM_CPU_FILTER);
  #endif
  
    /* Disable all Ethernet interrupts */
  
    putreg32(0x00000000, EIM_INTEN);
  
    /* Setup the EIM control register */
  
  #if 1
    putreg32(EIM_CTRL_ENET0_EN | EIM_CTRL_RXENET0_EN | EIM_CTRL_TXENET0_EN |
             EIM_CTRL_RXCPU_EN | EIM_CTRL_TXCPU_EN, EIM_CTRL);
  #else
    putreg32(EIM_CTRL_ENET0_EN | EIM_CTRL_ENET0_FLW | EIM_CTRL_RXENET0_EN |
             EIM_CTRL_TXENET0_EN | EIM_CTRL_RXCPU_EN | EIM_CTRL_TXCPU_EN,
             EIM_CTRL);
  #endif
  
  #if 1
    putreg32(0x00000000, EIM_MFVHI);
  #else
    putreg32(0x0000ff00, EIM_MFVHI);
  #endif
  
    putreg32(0x00000000, EIM_MFVLO);
    putreg32(0x00000000, EIM_MFMHI);
    putreg32(0x00000000, EIM_MFMLO);
    putreg32(0x00000018, EIM_RXTH);
    putreg32(0x00000000, EIM_CPU_RXREADY);
  
    /* Setup the ENET0 mode register */
  
  #if 1
    putreg32(ENET_MODE_RJCT_SFE | ENET_MODE_MWIDTH | ENET_MODE_FULLDUPLEX,
             ENET0_MODE);
  #else
    putreg32(ENET_MODE_RJCT_SFE | ENET_MODE_MWIDTH | ENET_MODE_HALFDUPLEX,
             ENET0_MODE);
  #endif
  
    putreg32(0x00000000, ENET0_BOFFSEED);
    putreg32(0x00000000, ENET0_FLWPAUSE);
    putreg32(0x00000000, ENET0_FLWCONTROL);
    putreg32(0x00000000, ENET0_VTYPE);
  
  #if 0
    putreg32(ENET_ADR_BROADCAST | ENET_ADR_PROMISCUOUS, ENET0_ADRMODE_EN);
  #else
    /* The CPU port is not PROMISCUOUS, it wants a no-promiscuous address
     * match yet the SWITCH receives packets from the PROMISCUOUS ENET0
     * which routes all packets for filter matching at the CPU port which
     * then allows the s/w to see the new incoming packetes that passed
     * the filter. Here we are setting the main SWITCH closest the ether
     * wire.
     */
  
    putreg32(ENET_ADR_PROMISCUOUS, ENET0_ADRMODE_EN);
  #endif
  
    putreg32(0x00000000, ENET0_DRP);
    up_mdelay(500);
  }
  
  /****************************************************************************
   * Name: c5471_reset
   *
   * Description:
   *
   ****************************************************************************/
  
  static void c5471_reset(struct c5471_driver_s *priv)
  {
  #if defined(CONFIG_C5471_PHY_LU3X31T_T64)
    ninfo("EIM reset\n");
    c5471_eimreset(priv);
  #endif
    ninfo("PHY init\n");
    c5471_phyinit();
  
    ninfo("EIM config\n");
    c5471_eimconfig(priv);
  }
  
  /****************************************************************************
   * Name: c5471_macassign
   *
   * Description:
   *    Set the mac address of our CPU ether port so that when the SWITCH
   *    receives packets from the PROMISCUOUS ENET0 it will switch them to the
   *    CPU port and cause a packet arrival event on the Switch's CPU TX queue
   *    when an address match occurs. The CPU port is not PROMISCUOUS and wants
   *    to see only packets specifically addressed to this device.
   *
   ****************************************************************************/
  
  static void c5471_macassign(struct c5471_driver_s *priv)
  {
    struct net_driver_s *dev = &priv->c_dev;
    uint8_t *mptr = dev->d_mac.ether.ether_addr_octet;
    register uint32_t tmp;
  
    ninfo("MAC: %0x:%0x:%0x:%0x:%0x:%0x\n",
          mptr[0], mptr[1], mptr[2], mptr[3], mptr[4], mptr[5]);
  
    /* Set CPU port MAC address. S/W will only see incoming packets that match
     * this destination address.
     */
  
    tmp = (((uint32_t)mptr[0]) << 8) | ((uint32_t)mptr[1]);
    putreg32(tmp, EIM_CPU_DAHI);
  
    tmp = (((uint32_t)mptr[2]) << 24) | (((uint32_t)mptr[3]) << 16) |
          (((uint32_t)mptr[4]) <<  8) |  ((uint32_t)mptr[5]);
    putreg32(tmp, EIM_CPU_DALO);
  
  #if 0
    /* Set the ENET MAC address */
  
    putreg32(getreg32(EIM_CPU_DAHI), ENET0_PARHI);
    putreg32(getreg32(EIM_CPU_DALO), ENET0_PARLO);
    putreg32(getreg32(EIM_CPU_DAHI), ENET0_LARHI);
    putreg32(getreg32(EIM_CPU_DALO), ENET0_LARLO);
  
  #else
    /* ENET MAC assignment not needed for its PROMISCUOUS mode */
  
    putreg32(0x00000000, ENET0_PARHI);
    putreg32(0x00000000, ENET0_PARLO);
    putreg32(0x00000000, ENET0_LARHI);
    putreg32(0x00000000, ENET0_LARLO);
  
  #endif
  }
  
  /****************************************************************************
   * Public Functions
   ****************************************************************************/
  
  /****************************************************************************
   * Function: c5471_initialize
   *
   * Description:
   *   Initialize the Ethernet driver
   *
   * Input Parameters:
   *   None
   *
   * Returned Value:
   *   OK on success; Negated errno on failure.
   *
   * Assumptions:
   *
   ****************************************************************************/
  
  /* Initialize the DM90x0 chip and driver */
  
  void up_netinitialize(void)
  {
    /* Attach the IRQ to the driver */
  
    if (irq_attach(C5471_IRQ_ETHER, c5471_interrupt, NULL))
      {
        /* We could not attach the ISR to the ISR */
  
        nerr("ERROR: irq_attach() failed\n");
        return;
      }
  
    /* Initialize the driver structure */
  
    memset(g_c5471, 0, CONFIG_C5471_NET_NINTERFACES*sizeof(struct c5471_driver_s));
    g_c5471[0].c_dev.d_buf     = g_pktbuf;        /* Single packet buffer */
    g_c5471[0].c_dev.d_ifup    = c5471_ifup;      /* I/F down callback */
    g_c5471[0].c_dev.d_ifdown  = c5471_ifdown;    /* I/F up (new IP address) callback */
    g_c5471[0].c_dev.d_txavail = c5471_txavail;   /* New TX data callback */
  #ifdef CONFIG_NET_MCASTGROUP
    g_c5471[0].c_dev.d_addmac  = c5471_addmac;    /* Add multicast MAC address */
    g_c5471[0].c_dev.d_rmmac   = c5471_rmmac;     /* Remove multicast MAC address */
  #endif
    g_c5471[0].c_dev.d_private = (void *)g_c5471; /* Used to recover private state from dev */
  
    /* Create a watchdog for timing polling for and timing of transmissions */
  
    g_c5471[0].c_txpoll        = wd_create();     /* Create periodic poll timer */
    g_c5471[0].c_txtimeout     = wd_create();     /* Create TX timeout timer */
  
    /* Register the device with the OS so that socket IOCTLs can be performed */
  
    netdev_register(&g_c5471[0].c_dev, NET_LL_ETHERNET);
  }
  
  #endif /* CONFIG_NET */
  