diff mbox

[v3,4/5] Add ENET/Gbps Ethernet support to FEC device

Message ID b2108ddf1ebf1e4082660eeae640f9451902ba7e.1462736067.git.jcd@tribudubois.net
State New
Headers show

Commit Message

Jean-Christophe Dubois May 8, 2016, 7:41 p.m. UTC
The ENET device (present in i.MX6) is "derived" from FEC and backward
compatible with it.

This patch adds the necessary support of the added feature in the ENET
device to allow Linux to use it (on supported processors).

Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
---

Changes since v1:
 * Not present on v1
   
Changes since v2:
 * Not present on v2      

 hw/arm/fsl-imx25.c       |   3 +
 hw/net/imx_fec.c         | 706 ++++++++++++++++++++++++++++++++++++++---------
 include/hw/net/imx_fec.h | 188 ++++++++++---
 3 files changed, 735 insertions(+), 162 deletions(-)

Comments

Peter Maydell May 17, 2016, 12:59 p.m. UTC | #1
On 8 May 2016 at 20:41, Jean-Christophe Dubois <jcd@tribudubois.net> wrote:
> The ENET device (present in i.MX6) is "derived" from FEC and backward
> compatible with it.
>
> This patch adds the necessary support of the added feature in the ENET
> device to allow Linux to use it (on supported processors).
>
> Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
> ---

Thanks; I have one significant question below (about the two irq lines)
and a handful of nits.

> Changes since v1:
>  * Not present on v1
>
> Changes since v2:
>  * Not present on v2
>
>  hw/arm/fsl-imx25.c       |   3 +
>  hw/net/imx_fec.c         | 706 ++++++++++++++++++++++++++++++++++++++---------
>  include/hw/net/imx_fec.h | 188 ++++++++++---
>  3 files changed, 735 insertions(+), 162 deletions(-)
>
> diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c
> index 2f878b9..ddb2b22 100644
> --- a/hw/arm/fsl-imx25.c
> +++ b/hw/arm/fsl-imx25.c
> @@ -191,6 +191,9 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp)
>      }
>
>      qdev_set_nic_properties(DEVICE(&s->fec), &nd_table[0]);
> +
> +    object_property_set_bool(OBJECT(&s->fec), true, "is-fec", &error_abort);
> +
>      object_property_set_bool(OBJECT(&s->fec), true, "realized", &err);
>      if (err) {
>          error_propagate(errp, err);
> diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c
> index 20e6524..b9893ee 100644
> --- a/hw/net/imx_fec.c
> +++ b/hw/net/imx_fec.c
> @@ -24,6 +24,8 @@
>  #include "qemu/osdep.h"
>  #include "hw/net/imx_fec.h"
>  #include "sysemu/dma.h"
> +#include "net/checksum.h"
> +#include "net/eth.h"
>
>  /* For crc32 */
>  #include <zlib.h>
> @@ -52,10 +54,93 @@
>          } \
>      } while (0)
>
> -static const char *imx_fec_reg_name(IMXFECState *s, uint32_t index)
> +static const char *imx_default_reg_name(IMXFECState *s, uint32_t index)
>  {
>      static char tmp[20];
> +    sprintf(tmp, "index %d", index);
> +    return tmp;
> +}
> +
> +static const char *imx_fec_reg_name(IMXFECState *s, uint32_t index)
> +{
> +    switch (index) {
> +    case ENET_FRBR:
> +        return "FRBR";
> +    case ENET_FRSR:
> +        return "FRSR";
> +    case ENET_MIIGSK_CFGR:
> +        return "MIIGSK_CFGR";
> +    case ENET_MIIGSK_ENR:
> +        return "MIIGSK_ENR";
> +    default:
> +        return imx_default_reg_name(s, index);
> +    }
> +}
>
> +static const char *imx_enet_reg_name(IMXFECState *s, uint32_t index)
> +{
> +    switch (index) {
> +    case ENET_RSFL:
> +        return "RSFL";
> +    case ENET_RSEM:
> +        return "RSEM";
> +    case ENET_RAEM:
> +        return "RAEM";
> +    case ENET_RAFL:
> +        return "RAFL";
> +    case ENET_TSEM:
> +        return "TSEM";
> +    case ENET_TAEM:
> +        return "TAEM";
> +    case ENET_TAFL:
> +        return "TAFL";
> +    case ENET_TIPG:
> +        return "TIPG";
> +    case ENET_FTRL:
> +        return "FTRL";
> +    case ENET_TACC:
> +        return "TACC";
> +    case ENET_RACC:
> +        return "RACC";
> +    case ENET_ATCR:
> +        return "ATCR";
> +    case ENET_ATVR:
> +        return "ATVR";
> +    case ENET_ATOFF:
> +        return "ATOFF";
> +    case ENET_ATPER:
> +        return "ATPER";
> +    case ENET_ATCOR:
> +        return "ATCOR";
> +    case ENET_ATINC:
> +        return "ATINC";
> +    case ENET_ATSTMP:
> +        return "ATSTMP";
> +    case ENET_TGSR:
> +        return "TGSR";
> +    case ENET_TCSR0:
> +        return "TCSR0";
> +    case ENET_TCCR0:
> +        return "TCCR0";
> +    case ENET_TCSR1:
> +        return "TCSR1";
> +    case ENET_TCCR1:
> +        return "TCCR1";
> +    case ENET_TCSR2:
> +        return "TCSR2";
> +    case ENET_TCCR2:
> +        return "TCCR2";
> +    case ENET_TCSR3:
> +        return "TCSR3";
> +    case ENET_TCCR3:
> +        return "TCCR3";
> +    default:
> +        return imx_default_reg_name(s, index);
> +    }
> +}
> +
> +static const char *imx_eth_reg_name(IMXFECState *s, uint32_t index)
> +{
>      switch (index) {
>      case ENET_EIR:
>          return "EIR";
> @@ -99,21 +184,16 @@ static const char *imx_fec_reg_name(IMXFECState *s, uint32_t index)
>          return "TDSR";
>      case ENET_MRBR:
>          return "MRBR";
> -    case ENET_FRBR:
> -        return "FRBR";
> -    case ENET_FRSR:
> -        return "FRSR";
> -    case ENET_MIIGSK_CFGR:
> -        return "MIIGSK_CFGR";
> -    case ENET_MIIGSK_ENR:
> -        return "MIIGSK_ENR";
>      default:
> -        sprintf(tmp, "index %d", index);
> -        return tmp;
> +        if (s->is_fec) {
> +            return imx_fec_reg_name(s, index);
> +        } else {
> +            return imx_enet_reg_name(s, index);
> +        }
>      }
>  }
>
> -static const VMStateDescription vmstate_imx_fec = {
> +static const VMStateDescription vmstate_imx_eth = {
>      .name = TYPE_IMX_FEC,
>      .version_id = 2,
>      .minimum_version_id = 2,
> @@ -127,6 +207,7 @@ static const VMStateDescription vmstate_imx_fec = {
>          VMSTATE_UINT32(phy_advertise, IMXFECState),
>          VMSTATE_UINT32(phy_int, IMXFECState),
>          VMSTATE_UINT32(phy_int_mask, IMXFECState),
> +
>          VMSTATE_END_OF_LIST()

Stray whitespace change.

>      }
>  };
> @@ -139,7 +220,7 @@ static const VMStateDescription vmstate_imx_fec = {
>  #define PHY_INT_PARFAULT            (1 << 2)
>  #define PHY_INT_AUTONEG_PAGE        (1 << 1)
>
> -static void imx_fec_update(IMXFECState *s);
> +static void imx_eth_update(IMXFECState *s);
>
>  /*
>   * The MII phy could raise a GPIO to the processor which in turn
> @@ -149,7 +230,7 @@ static void imx_fec_update(IMXFECState *s);
>   */
>  static void phy_update_irq(IMXFECState *s)
>  {
> -    imx_fec_update(s);
> +    imx_eth_update(s);
>  }
>
>  static void phy_update_link(IMXFECState *s)
> @@ -168,7 +249,7 @@ static void phy_update_link(IMXFECState *s)
>      phy_update_irq(s);
>  }
>
> -static void imx_fec_set_link(NetClientState *nc)
> +static void imx_eth_set_link(NetClientState *nc)
>  {
>      phy_update_link(IMX_FEC(qemu_get_nic_opaque(nc)));
>  }
> @@ -294,21 +375,33 @@ static void imx_fec_write_bd(IMXFECBufDesc *bd, dma_addr_t addr)
>      dma_memory_write(&address_space_memory, addr, bd, sizeof(*bd));
>  }
>
> -static void imx_fec_update(IMXFECState *s)
> +static void imx_enet_read_bd(IMXENETBufDesc *bd, dma_addr_t addr)
> +{
> +    dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd));
> +}
> +
> +static void imx_enet_write_bd(IMXENETBufDesc *bd, dma_addr_t addr)
> +{
> +    dma_memory_write(&address_space_memory, addr, bd, sizeof(*bd));
> +}
> +
> +static void imx_eth_update(IMXFECState *s)
>  {
>      if (s->regs[ENET_EIR] & s->regs[ENET_EIMR]) {
>          FEC_PRINTF("interupt raised\n");
> -        qemu_set_irq(s->irq, 1);
> +        qemu_set_irq(s->irq[0], 1);
> +        qemu_set_irq(s->irq[1], 1);
>      } else {
>          FEC_PRINTF("interupt lowered\n");
> -        qemu_set_irq(s->irq, 0);
> +        qemu_set_irq(s->irq[0], 0);
> +        qemu_set_irq(s->irq[1], 0);

Does this device really have two IRQ outputs which always update
in lockstep? What's their purpose?

>      }
>  }
>
>  static void imx_fec_do_tx(IMXFECState *s)
>  {
>      int frame_size = 0;
> -    uint8_t frame[FEC_MAX_FRAME_SIZE];
> +    uint8_t frame[ENET_MAX_FRAME_SIZE];
>      uint8_t *ptr = frame;
>      uint32_t addr = s->tx_descriptor;
>
> @@ -318,32 +411,105 @@ static void imx_fec_do_tx(IMXFECState *s)
>
>          imx_fec_read_bd(&bd, addr);
>          FEC_PRINTF("tx_bd %x flags %04x len %d data %08x\n",
> -                   addr, bd.flags, bd.length, bd.data);
> -        if ((bd.flags & FEC_BD_R) == 0) {
> +                    addr, bd.flags, bd.length, bd.data);
> +        if ((bd.flags & ENET_BD_R) == 0) {
>              /* Run out of descriptors to transmit.  */
> +            FEC_PRINTF("tx_bd running out of descriptor to transmit\n");

"ran out of descriptors to transmit"

>              break;
>          }
>          len = bd.length;
> -        if (frame_size + len > FEC_MAX_FRAME_SIZE) {
> -            len = FEC_MAX_FRAME_SIZE - frame_size;
> -            s->regs[ENET_EIR] |= FEC_INT_BABT;
> +        if (frame_size + len > ENET_MAX_FRAME_SIZE) {
> +            len = ENET_MAX_FRAME_SIZE - frame_size;
> +            s->regs[ENET_EIR] |= ENET_INT_BABT;
>          }
>          dma_memory_read(&address_space_memory, bd.data, ptr, len);
>          ptr += len;
>          frame_size += len;
> -        if (bd.flags & FEC_BD_L) {
> +        if (bd.flags & ENET_BD_L) {
> +            FEC_PRINTF("sending %d bytes frame\n", len);
>              /* Last buffer in frame.  */
>              qemu_send_packet(qemu_get_queue(s->nic), frame, len);
>              ptr = frame;
>              frame_size = 0;
> -            s->regs[ENET_EIR] |= FEC_INT_TXF;
> +            s->regs[ENET_EIR] |= ENET_INT_TXF;
>          }
> -        s->regs[ENET_EIR] |= FEC_INT_TXB;
> -        bd.flags &= ~FEC_BD_R;
> +        s->regs[ENET_EIR] |= ENET_INT_TXB;
> +        bd.flags &= ~ENET_BD_R;
>          /* Write back the modified descriptor.  */
>          imx_fec_write_bd(&bd, addr);
>          /* Advance to the next descriptor.  */
> -        if ((bd.flags & FEC_BD_W) != 0) {
> +        if ((bd.flags & ENET_BD_W) != 0) {
> +            addr = s->regs[ENET_TDSR];
> +        } else {
> +            addr += sizeof(bd);
> +        }
> +    }
> +
> +    s->tx_descriptor = addr;
> +
> +    imx_eth_update(s);
> +}
> +
> +static void imx_enet_do_tx(IMXFECState *s)
> +{
> +    int frame_size = 0;
> +    uint8_t frame[ENET_MAX_FRAME_SIZE];
> +    uint8_t *ptr = frame;
> +    uint32_t addr = s->tx_descriptor;
> +
> +    while (1) {
> +        IMXENETBufDesc bd;
> +        int len;
> +
> +        imx_enet_read_bd(&bd, addr);
> +        FEC_PRINTF("tx_bd %x flags %04x len %d data %08x option %04x "
> +                   "status %04x\n", addr, bd.flags, bd.length, bd.data,
> +                   bd.option, bd.status);
> +        if ((bd.flags & ENET_BD_R) == 0) {
> +            /* Run out of descriptors to transmit.  */
> +            break;
> +        }
> +        len = bd.length;
> +        if (frame_size + len > ENET_MAX_FRAME_SIZE) {
> +            len = ENET_MAX_FRAME_SIZE - frame_size;
> +            s->regs[ENET_EIR] |= ENET_INT_BABT;
> +        }
> +        dma_memory_read(&address_space_memory, bd.data, ptr, len);
> +        ptr += len;
> +        frame_size += len;
> +        if (bd.flags & ENET_BD_L) {
> +            if (bd.option & ENET_BD_PINS) {
> +                struct ip_header *ip_hd = PKT_GET_IP_HDR(frame);
> +                if (IP_HEADER_VERSION(ip_hd) == 4) {
> +                    net_checksum_calculate(frame, frame_size);
> +                }
> +            }
> +            if (bd.option & ENET_BD_IINS) {
> +                struct ip_header *ip_hd = PKT_GET_IP_HDR(frame);
> +                /* We compute checksum only for IPv4 frames */
> +                if (IP_HEADER_VERSION(ip_hd) == 4) {
> +                    uint16_t csum;
> +                    ip_hd->ip_sum = 0;
> +                    csum = net_raw_checksum((uint8_t *)ip_hd, sizeof(*ip_hd));
> +                    ip_hd->ip_sum = cpu_to_be16(csum);
> +                }
> +            }
> +            /* Last buffer in frame.  */
> +            qemu_send_packet(qemu_get_queue(s->nic), frame, len);
> +            ptr = frame;
> +            frame_size = 0;
> +            if (bd.option & ENET_BD_TX_INT) {
> +                s->regs[ENET_EIR] |= ENET_INT_TXF;
> +            }
> +        }
> +        if (bd.option & ENET_BD_TX_INT) {
> +            s->regs[ENET_EIR] |= ENET_INT_TXB;
> +        }
> +        bd.flags &= ~ENET_BD_R;
> +        /* Write back the modified descriptor.  */
> +        imx_enet_write_bd(&bd, addr);
> +        /* Advance to the next descriptor.  */
> +        if ((bd.flags & ENET_BD_W) != 0) {
>              addr = s->regs[ENET_TDSR];
>          } else {
>              addr += sizeof(bd);
> @@ -352,17 +518,26 @@ static void imx_fec_do_tx(IMXFECState *s)
>
>      s->tx_descriptor = addr;
>
> -    imx_fec_update(s);
> +    imx_eth_update(s);
> +}
> +
> +static void imx_eth_do_tx(IMXFECState *s)
> +{
> +    if (!s->is_fec && (s->regs[ENET_ECR] & ENET_ECR_EN1588)) {
> +        imx_enet_do_tx(s);
> +    } else {
> +        imx_fec_do_tx(s);
> +    }
>  }
>
> -static void imx_fec_enable_rx(IMXFECState *s)
> +static void imx_eth_enable_rx(IMXFECState *s)
>  {
>      IMXFECBufDesc bd;
>      bool tmp;
>
>      imx_fec_read_bd(&bd, s->rx_descriptor);
>
> -    tmp = ((bd.flags & FEC_BD_E) != 0);
> +    tmp = ((bd.flags & ENET_BD_E) != 0);
>
>      if (!tmp) {
>          FEC_PRINTF("RX buffer full\n");
> @@ -373,11 +548,11 @@ static void imx_fec_enable_rx(IMXFECState *s)
>      s->regs[ENET_RDAR] = tmp ? ENET_RDAR_RDAR : 0;
>  }
>
> -static void imx_fec_reset(DeviceState *d)
> +static void imx_eth_reset(DeviceState *d)
>  {
>      IMXFECState *s = IMX_FEC(d);
>
> -    /* Reset the FEC */
> +    /* Reset the Device */
>      memset(s->regs, 0, sizeof(s->regs));
>      s->regs[ENET_ECR]   = 0xf0000000;
>      s->regs[ENET_MIBC]  = 0xc0000000;
> @@ -392,19 +567,85 @@ static void imx_fec_reset(DeviceState *d)
>                            | (s->conf.macaddr.a[5] << 16)
>                            | 0x8808;
>
> -    s->regs[ENET_FRBR]  = 0x00000600;
> -    s->regs[ENET_FRSR]  = 0x00000500;
> -    s->regs[ENET_MIIGSK_ENR]  = 0x00000006;
> +    if (s->is_fec) {
> +        s->regs[ENET_FRBR]  = 0x00000600;
> +        s->regs[ENET_FRSR]  = 0x00000500;
> +        s->regs[ENET_MIIGSK_ENR]  = 0x00000006;
> +    } else {
> +        s->regs[ENET_RAEM]  = 0x00000004;
> +        s->regs[ENET_RAFL]  = 0x00000004;
> +        s->regs[ENET_TAEM]  = 0x00000004;
> +        s->regs[ENET_TAFL]  = 0x00000008;
> +        s->regs[ENET_TIPG]  = 0x0000000c;
> +        s->regs[ENET_FTRL]  = 0x000007ff;
> +        s->regs[ENET_ATPER] = 0x3b9aca00;
> +    }
>
>      /* We also reset the PHY */
>      phy_reset(s);
>  }
>
> -static uint64_t imx_fec_read(void *opaque, hwaddr addr, unsigned size)
> +static uint32_t imx_default_read(IMXFECState *s, uint32_t index)
> +{
> +    qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
> +                  PRIx32 "\n", TYPE_IMX_FEC, __func__, index * 4);
> +    return 0;
> +}
> +
> +static uint32_t imx_fec_read(IMXFECState *s, uint32_t index)
> +{
> +    switch (index) {
> +    case ENET_FRBR:
> +    case ENET_FRSR:
> +    case ENET_MIIGSK_CFGR:
> +    case ENET_MIIGSK_ENR:
> +        return s->regs[index];
> +    default:
> +        return imx_default_read(s, index);
> +    }
> +}
> +
> +static uint32_t imx_enet_read(IMXFECState *s, uint32_t index)
> +{
> +    switch (index) {
> +    case ENET_RSFL:
> +    case ENET_RSEM:
> +    case ENET_RAEM:
> +    case ENET_RAFL:
> +    case ENET_TSEM:
> +    case ENET_TAEM:
> +    case ENET_TAFL:
> +    case ENET_TIPG:
> +    case ENET_FTRL:
> +    case ENET_TACC:
> +    case ENET_RACC:
> +    case ENET_ATCR:
> +    case ENET_ATVR:
> +    case ENET_ATOFF:
> +    case ENET_ATPER:
> +    case ENET_ATCOR:
> +    case ENET_ATINC:
> +    case ENET_ATSTMP:
> +    case ENET_TGSR:
> +    case ENET_TCSR0:
> +    case ENET_TCCR0:
> +    case ENET_TCSR1:
> +    case ENET_TCCR1:
> +    case ENET_TCSR2:
> +    case ENET_TCCR2:
> +    case ENET_TCSR3:
> +    case ENET_TCCR3:
> +        return s->regs[index];
> +    default:
> +        return imx_default_read(s, index);
> +    }
> +}
> +
> +static uint64_t imx_eth_read(void *opaque, hwaddr offset, unsigned size)
>  {
>      uint32_t value = 0;
>      IMXFECState *s = IMX_FEC(opaque);
> -    uint32_t index = addr >> 2;
> +    uint32_t index = offset >> 2;
>
>      switch (index) {
>      case ENET_EIR:
> @@ -428,32 +669,126 @@ static uint64_t imx_fec_read(void *opaque, hwaddr addr, unsigned size)
>      case ENET_RDSR:
>      case ENET_TDSR:
>      case ENET_MRBR:
> -    case ENET_FRBR:
> -    case ENET_FRSR:
> -    case ENET_MIIGSK_CFGR:
> -    case ENET_MIIGSK_ENR:
>          value = s->regs[index];
>          break;
>      default:
> -        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
> -                      PRIx32 "\n", TYPE_IMX_FEC, __func__, index * 4);
> +        if (s->is_fec) {
> +            value = imx_fec_read(s, index);
> +        } else {
> +            value = imx_enet_read(s, index);
> +        }
>          break;
>      }
>
> -    FEC_PRINTF("reg[%s] => 0x%" PRIx32 "\n", imx_fec_reg_name(s, index),
> +    FEC_PRINTF("reg[%s] => 0x%" PRIx32 "\n", imx_eth_reg_name(s, index),
>                                                value);
>
>      return value;
>  }
>
> -static void imx_fec_write(void *opaque, hwaddr addr,
> -                          uint64_t value, unsigned size)
> +static void imx_default_write(IMXFECState *s, uint32_t index, uint32_t value)
> +{
> +    qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
> +                  PRIx32 "\n", TYPE_IMX_FEC, __func__, index * 4);
> +    return;
> +}
> +
> +static void imx_fec_write(IMXFECState *s, uint32_t index, uint32_t value)
> +{
> +    switch (index) {
> +    case ENET_FRBR:
> +        /* FRBR is read only */
> +        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Register FRBR is read only\n",
> +                      TYPE_IMX_FEC, __func__);
> +        break;
> +    case ENET_FRSR:
> +        s->regs[index] = (value & 0x000003fc) | 0x00000400;
> +        break;
> +    case ENET_MIIGSK_CFGR:
> +        s->regs[index] = value & 0x00000053;
> +        break;
> +    case ENET_MIIGSK_ENR:
> +        s->regs[index] = (value & 0x00000002) ? 0x00000006 : 0;
> +        break;
> +    default:
> +        imx_default_write(s, index, value);
> +        break;
> +    }
> +}
> +
> +static void imx_enet_write(IMXFECState *s, uint32_t index, uint32_t value)
> +{
> +    switch (index) {
> +    case ENET_RSFL:
> +    case ENET_RSEM:
> +    case ENET_RAEM:
> +    case ENET_RAFL:
> +    case ENET_TSEM:
> +    case ENET_TAEM:
> +    case ENET_TAFL:
> +        s->regs[index] = value & 0x000001ff;
> +        break;
> +    case ENET_TIPG:
> +        s->regs[index] = value & 0x0000001f;
> +        break;
> +    case ENET_FTRL:
> +        s->regs[index] = value & 0x00003fff;
> +        break;
> +    case ENET_TACC:
> +        s->regs[index] = value & 0x00000019;
> +        break;
> +    case ENET_RACC:
> +        s->regs[index] = value & 0x000000C7;
> +        break;
> +    case ENET_ATCR:
> +        s->regs[index] = value & 0x00002a9d;
> +        break;
> +    case ENET_ATVR:
> +    case ENET_ATOFF:
> +    case ENET_ATPER:
> +        s->regs[index] = value;
> +        break;
> +    case ENET_ATSTMP:
> +        /* ATSTMP is read only */
> +        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Register ATSTMP is read only\n",
> +                      TYPE_IMX_FEC, __func__);
> +        break;
> +    case ENET_ATCOR:
> +        s->regs[index] = value & 0x7fffffff;
> +        break;
> +    case ENET_ATINC:
> +        s->regs[index] = value & 0x00007f7f;
> +        break;
> +    case ENET_TGSR:
> +        /* implement clear timer flag */
> +        value = value & 0x0000000f;
> +        break;
> +    case ENET_TCSR0:
> +    case ENET_TCSR1:
> +    case ENET_TCSR2:
> +    case ENET_TCSR3:
> +        value = value & 0x000000fd;
> +        break;
> +    case ENET_TCCR0:
> +    case ENET_TCCR1:
> +    case ENET_TCCR2:
> +    case ENET_TCCR3:
> +        s->regs[index] = value;
> +        break;
> +    default:
> +        imx_default_write(s, index, value);
> +        break;
> +    }
> +}
> +
> +static void imx_eth_write(void *opaque, hwaddr offset, uint64_t value,
> +                           unsigned size)
>  {
>      IMXFECState *s = IMX_FEC(opaque);
> -    uint32_t index = addr >> 2;
> +    uint32_t index = offset >> 2;
>
> -    FEC_PRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx_fec_reg_name(s, index),
> -                                             (uint32_t)value);
> +    FEC_PRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx_eth_reg_name(s, index),
> +                (uint32_t)value);
>
>      switch (index) {
>      case ENET_EIR:
> @@ -465,26 +800,26 @@ static void imx_fec_write(void *opaque, hwaddr addr,
>      case ENET_RDAR:
>          value = ENET_RDAR_RDAR;
>          s->regs[index] = value;
> -        if ((s->regs[ENET_ECR] & FEC_EN) && !s->regs[ENET_RDAR]) {
> -            imx_fec_enable_rx(s);
> +        if ((s->regs[ENET_ECR] & ENET_ECR_ETHEREN) && !s->regs[ENET_RDAR]) {
> +            imx_eth_enable_rx(s);
>          }
>          break;
>      case ENET_TDAR:
>          /* TDAR bit is always writen to 1 whatever the value writen to reg */
>          value = ENET_TDAR_TDAR;
>          s->regs[index] = value;
> -        if (s->regs[ENET_ECR] & FEC_EN) {
> -            imx_fec_do_tx(s);
> +        if (s->regs[ENET_ECR] & ENET_ECR_ETHEREN) {
> +            imx_eth_do_tx(s);
>          }
>          break;
>      case ENET_ECR:
> -        if (value & FEC_RESET) {
> -            value &= ~FEC_EN;
> +        if (value & ENET_ECR_RESET) {
> +            value &= ~ENET_ECR_ETHEREN;
>              s->regs[ENET_RDAR] = 0;
>              s->regs[ENET_TDAR] = 0;
>          }
>          s->regs[index] = value;
> -        if ((s->regs[ENET_ECR] & FEC_EN) == 0) {
> +        if ((s->regs[ENET_ECR] & ENET_ECR_ETHEREN) == 0) {
>              s->regs[ENET_RDAR] = 0;
>          }
>          break;
> @@ -500,7 +835,7 @@ static void imx_fec_write(void *opaque, hwaddr addr,
>              do_phy_write(s, extract32(value, 18, 10), extract32(value, 0, 16));
>          }
>          /* raise the interrupt as the PHY operation is done */
> -        s->regs[ENET_EIR] |= FEC_INT_MII;
> +        s->regs[ENET_EIR] |= ENET_INT_MII;
>          break;
>      case ENET_MSCR:
>          s->regs[index] = value & 0xfe;
> @@ -517,7 +852,7 @@ static void imx_fec_write(void *opaque, hwaddr addr,
>          /* We transmit immediately, so raise GRA immediately.  */
>          s->regs[index] = value;
>          if (value & 1) {
> -            s->regs[ENET_EIR] |= FEC_INT_GRA;
> +            s->regs[ENET_EIR] |= ENET_INT_GRA;
>          }
>          break;
>      case ENET_PALR:
> @@ -542,46 +877,49 @@ static void imx_fec_write(void *opaque, hwaddr addr,
>          /* TODO: implement MAC hash filtering.  */
>          break;
>      case ENET_TFWR:
> -        s->regs[index] = value & 0x00000003;
> -        break;
> -    case ENET_FRBR:
> -        /* FRBR is read only */
> -        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Register FRBR is read only\n",
> -                      TYPE_IMX_FEC, __func__);
> -        break;
> -    case ENET_FRSR:
> -        s->regs[index] = (value & 0x000003fc) | 0x00000400;
> +        if (s->is_fec) {
> +            s->regs[index] = value & 0x00000003;
> +        } else {
> +            s->regs[index] = value & 0x0000013f;
> +        }
>          break;
>      case ENET_RDSR:
> -        s->regs[index] = value & ~3;
> +        if (s->is_fec) {
> +            s->regs[index] = value & ~3;
> +        } else {
> +            s->regs[index] = value & ~7;
> +        }
>          s->rx_descriptor = s->regs[index];
>          break;
>      case ENET_TDSR:
> -        s->regs[index] = value & ~3;
> +        if (s->is_fec) {
> +            s->regs[index] = value & ~3;
> +        } else {
> +            s->regs[index] = value & ~7;
> +        }
>          s->tx_descriptor = s->regs[index];
>          break;
>      case ENET_MRBR:
> -        s->regs[index] = value & 0x000007f0;
> -        break;
> -    case ENET_MIIGSK_CFGR:
> -        s->regs[index] = value & 0x00000053;
> -        break;
> -    case ENET_MIIGSK_ENR:
> -        s->regs[index] = (value & 0x00000002) ? 0x00000006 : 0;
> +        s->regs[index] = value & 0x00003ff0;
>          break;
>      default:
> -        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
> -                      PRIx32 "\n", TYPE_IMX_FEC, __func__, index * 4);
> -        break;
> +        if (s->is_fec) {
> +            imx_fec_write(s, index, value);
> +        } else {
> +            imx_enet_write(s, index, value);
> +        }
> +        return;
>      }
>
> -    imx_fec_update(s);
> +    imx_eth_update(s);
>  }
>
> -static int imx_fec_can_receive(NetClientState *nc)
> +static int imx_eth_can_receive(NetClientState *nc)
>  {
>      IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
>
> +    FEC_PRINTF("\n");
> +
>      return s->regs[ENET_RDAR] ? 1 : 0;
>  }
>
> @@ -612,20 +950,20 @@ static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf,
>      crc_ptr = (uint8_t *) &crc;
>
>      /* Huge frames are truncted.  */
> -    if (size > FEC_MAX_FRAME_SIZE) {
> -        size = FEC_MAX_FRAME_SIZE;
> -        flags |= FEC_BD_TR | FEC_BD_LG;
> +    if (size > ENET_MAX_FRAME_SIZE) {
> +        size = ENET_MAX_FRAME_SIZE;
> +        flags |= ENET_BD_TR | ENET_BD_LG;
>      }
>
>      /* Frames larger than the user limit just set error flags.  */
>      if (size > (s->regs[ENET_RCR] >> 16)) {
> -        flags |= FEC_BD_LG;
> +        flags |= ENET_BD_LG;
>      }
>
>      addr = s->rx_descriptor;
>      while (size > 0) {
>          imx_fec_read_bd(&bd, addr);
> -        if ((bd.flags & FEC_BD_E) == 0) {
> +        if ((bd.flags & ENET_BD_E) == 0) {
>              /* No descriptors available.  Bail out.  */
>              /*
>               * FIXME: This is wrong. We should probably either
> @@ -654,99 +992,213 @@ static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf,
>                               crc_ptr, 4 - size);
>              crc_ptr += 4 - size;
>          }
> -        bd.flags &= ~FEC_BD_E;
> +        bd.flags &= ~ENET_BD_E;
>          if (size == 0) {
>              /* Last buffer in frame.  */
> -            bd.flags |= flags | FEC_BD_L;
> +            bd.flags |= flags | ENET_BD_L;
>              FEC_PRINTF("rx frame flags %04x\n", bd.flags);
> -            s->regs[ENET_EIR] |= FEC_INT_RXF;
> +            s->regs[ENET_EIR] |= ENET_INT_RXF;
>          } else {
> -            s->regs[ENET_EIR] |= FEC_INT_RXB;
> +            s->regs[ENET_EIR] |= ENET_INT_RXB;
>          }
>          imx_fec_write_bd(&bd, addr);
>          /* Advance to the next descriptor.  */
> -        if ((bd.flags & FEC_BD_W) != 0) {
> +        if ((bd.flags & ENET_BD_W) != 0) {
> +            addr = s->regs[ENET_RDSR];
> +        } else {
> +            addr += sizeof(bd);
> +        }
> +    }
> +    s->rx_descriptor = addr;
> +    imx_eth_enable_rx(s);
> +    imx_eth_update(s);
> +    return len;
> +}
> +
> +static ssize_t imx_enet_receive(NetClientState *nc, const uint8_t *buf,
> +                                size_t len)
> +{
> +    IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
> +    IMXENETBufDesc bd;
> +    uint32_t flags = 0;
> +    uint32_t addr;
> +    uint32_t crc;
> +    uint32_t buf_addr;
> +    uint8_t *crc_ptr;
> +    unsigned int buf_len;
> +    size_t size = len;
> +
> +    FEC_PRINTF("len %d\n", (int)size);
> +
> +    if (!s->regs[ENET_RDAR]) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Unexpected packet\n",
> +                      TYPE_IMX_FEC, __func__);
> +        return 0;
> +    }
> +
> +    /* 4 bytes for the CRC.  */
> +    size += 4;
> +    crc = cpu_to_be32(crc32(~0, buf, size));
> +    crc_ptr = (uint8_t *) &crc;
> +
> +    /* Huge frames are truncted.  */

"truncated"

> +    if (size > ENET_MAX_FRAME_SIZE) {
> +        size = ENET_MAX_FRAME_SIZE;
> +        flags |= ENET_BD_TR | ENET_BD_LG;
> +    }
> +
> +    /* Frames larger than the user limit just set error flags.  */
> +    if (size > (s->regs[ENET_RCR] >> 16)) {
> +        flags |= ENET_BD_LG;
> +    }
> +
> +    addr = s->rx_descriptor;
> +    while (size > 0) {
> +        imx_enet_read_bd(&bd, addr);
> +        if ((bd.flags & ENET_BD_E) == 0) {
> +            /* No descriptors available.  Bail out.  */
> +            /*
> +             * FIXME: This is wrong. We should probably either
> +             * save the remainder for when more RX buffers are
> +             * available, or flag an error.
> +             */
> +            qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Lost end of frame\n",
> +                          TYPE_IMX_FEC, __func__);
> +            break;
> +        }
> +        buf_len = (size <= s->regs[ENET_MRBR]) ? size : s->regs[ENET_MRBR];
> +        bd.length = buf_len;
> +        size -= buf_len;
> +
> +        FEC_PRINTF("rx_bd 0x%x length %d\n", addr, bd.length);
> +
> +        /* The last 4 bytes are the CRC.  */
> +        if (size < 4) {
> +            buf_len += size - 4;
> +        }
> +        buf_addr = bd.data;
> +        dma_memory_write(&address_space_memory, buf_addr, buf, buf_len);
> +        buf += buf_len;
> +        if (size < 4) {
> +            dma_memory_write(&address_space_memory, buf_addr + buf_len,
> +                             crc_ptr, 4 - size);
> +            crc_ptr += 4 - size;
> +        }
> +        bd.flags &= ~ENET_BD_E;
> +        if (size == 0) {
> +            /* Last buffer in frame.  */
> +            bd.flags |= flags | ENET_BD_L;
> +            FEC_PRINTF("rx frame flags %04x\n", bd.flags);
> +            if (bd.option & ENET_BD_RX_INT) {
> +                s->regs[ENET_EIR] |= ENET_INT_RXF;
> +            }
> +        } else {
> +            if (bd.option & ENET_BD_RX_INT) {
> +                s->regs[ENET_EIR] |= ENET_INT_RXB;
> +            }
> +        }
> +        imx_enet_write_bd(&bd, addr);
> +        /* Advance to the next descriptor.  */
> +        if ((bd.flags & ENET_BD_W) != 0) {
>              addr = s->regs[ENET_RDSR];
>          } else {
>              addr += sizeof(bd);
>          }
>      }
>      s->rx_descriptor = addr;
> -    imx_fec_enable_rx(s);
> -    imx_fec_update(s);
> +    imx_eth_enable_rx(s);
> +    imx_eth_update(s);
>      return len;
>  }
>
> -static const MemoryRegionOps imx_fec_ops = {
> -    .read = imx_fec_read,
> -    .write = imx_fec_write,
> +static ssize_t imx_eth_receive(NetClientState *nc, const uint8_t *buf,
> +                                size_t len)
> +{
> +    IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
> +
> +    if (!s->is_fec && (s->regs[ENET_ECR] & ENET_ECR_EN1588)) {
> +        return imx_enet_receive(nc, buf, len);
> +    } else {
> +        return imx_fec_receive(nc, buf, len);
> +    }
> +}
> +
> +static const MemoryRegionOps imx_eth_ops = {
> +    .read                  = imx_eth_read,
> +    .write                 = imx_eth_write,
>      .valid.min_access_size = 4,
>      .valid.max_access_size = 4,
> -    .endianness = DEVICE_NATIVE_ENDIAN,
> +    .endianness            = DEVICE_NATIVE_ENDIAN,
>  };
>
> -static void imx_fec_cleanup(NetClientState *nc)
> +static void imx_eth_cleanup(NetClientState *nc)
>  {
>      IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
>
> +    FEC_PRINTF("\n");

I guess it's only debug printing, but why print a blank line here?

> +
>      s->nic = NULL;
>  }
>
> -static NetClientInfo net_imx_fec_info = {
> -    .type = NET_CLIENT_OPTIONS_KIND_NIC,
> -    .size = sizeof(NICState),
> -    .can_receive = imx_fec_can_receive,
> -    .receive = imx_fec_receive,
> -    .cleanup = imx_fec_cleanup,
> -    .link_status_changed = imx_fec_set_link,
> +static NetClientInfo imx_eth_net_info = {
> +    .type                = NET_CLIENT_OPTIONS_KIND_NIC,
> +    .size                = sizeof(NICState),
> +    .can_receive         = imx_eth_can_receive,
> +    .receive             = imx_eth_receive,
> +    .cleanup             = imx_eth_cleanup,
> +    .link_status_changed = imx_eth_set_link,
>  };
>
>
> -static void imx_fec_realize(DeviceState *dev, Error **errp)
> +static void imx_eth_realize(DeviceState *dev, Error **errp)
>  {
>      IMXFECState *s = IMX_FEC(dev);
>      SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
>
> -    memory_region_init_io(&s->iomem, OBJECT(dev), &imx_fec_ops, s,
> +    memory_region_init_io(&s->iomem, OBJECT(dev), &imx_eth_ops, s,
>                            TYPE_IMX_FEC, 0x400);
>      sysbus_init_mmio(sbd, &s->iomem);
> -    sysbus_init_irq(sbd, &s->irq);
> +    sysbus_init_irq(sbd, &s->irq[0]);
> +    sysbus_init_irq(sbd, &s->irq[1]);
> +
>      qemu_macaddr_default_if_unset(&s->conf.macaddr);
>
>      s->conf.peers.ncs[0] = nd_table[0].netdev;
>
> -    s->nic = qemu_new_nic(&net_imx_fec_info, &s->conf,
> -                          object_get_typename(OBJECT(dev)), DEVICE(dev)->id,
> -                          s);
> +    s->nic = qemu_new_nic(&imx_eth_net_info, &s->conf,
> +                          object_get_typename(OBJECT(dev)),
> +                          DEVICE(dev)->id, s);
> +
>      qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
>  }
>
> -static Property imx_fec_properties[] = {
> +static Property imx_eth_properties[] = {
>      DEFINE_NIC_PROPERTIES(IMXFECState, conf),
> +    DEFINE_PROP_BOOL("is-fec", IMXFECState, is_fec, false),
>      DEFINE_PROP_END_OF_LIST(),
>  };
>
> -static void imx_fec_class_init(ObjectClass *klass, void *data)
> +static void imx_eth_class_init(ObjectClass *klass, void *data)
>  {
>      DeviceClass *dc = DEVICE_CLASS(klass);
>
> -    dc->vmsd = &vmstate_imx_fec;
> -    dc->reset = imx_fec_reset;
> -    dc->props = imx_fec_properties;
> -    dc->realize = imx_fec_realize;
> -    dc->desc = "i.MX FEC Ethernet Controller";
> +    dc->vmsd    = &vmstate_imx_eth;
> +    dc->reset   = imx_eth_reset;
> +    dc->props   = imx_eth_properties;
> +    dc->realize = imx_eth_realize;
> +    dc->desc    = "i.MX FEC/ENET Ethernet Controller";
>  }
>
> -static const TypeInfo imx_fec_info = {
> -    .name = TYPE_IMX_FEC,
> -    .parent = TYPE_SYS_BUS_DEVICE,
> +static const TypeInfo imx_eth_info = {
> +    .name          = TYPE_IMX_FEC,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
>      .instance_size = sizeof(IMXFECState),
> -    .class_init = imx_fec_class_init,
> +    .class_init    = imx_eth_class_init,
>  };
>
> -static void imx_fec_register_types(void)
> +static void imx_eth_register_types(void)
>  {
> -    type_register_static(&imx_fec_info);
> +    type_register_static(&imx_eth_info);
>  }
>
> -type_init(imx_fec_register_types)
> +type_init(imx_eth_register_types)
> diff --git a/include/hw/net/imx_fec.h b/include/hw/net/imx_fec.h
> index 709f8a0..176716b 100644
> --- a/include/hw/net/imx_fec.h
> +++ b/include/hw/net/imx_fec.h
> @@ -1,5 +1,5 @@
>  /*
> - * i.MX Fast Ethernet Controller emulation.
> + * i.MX FEC/ENET Ethernet Controller emulation.
>   *
>   * Copyright (c) 2013 Jean-Christophe Dubois. <jcd@tribudubois.net>
>   *
> @@ -53,25 +53,57 @@
>  #define ENET_RDSR              96
>  #define ENET_TDSR              97
>  #define ENET_MRBR              98
> +#define ENET_RSFL              100
> +#define ENET_RSEM              101
> +#define ENET_RAEM              102
> +#define ENET_RAFL              103
> +#define ENET_TSEM              104
> +#define ENET_TAEM              105
> +#define ENET_TAFL              106
> +#define ENET_TIPG              107
> +#define ENET_FTRL              108
> +#define ENET_TACC              112
> +#define ENET_RACC              113
>  #define ENET_MIIGSK_CFGR       192
>  #define ENET_MIIGSK_ENR        194
> +#define ENET_ATCR              256
> +#define ENET_ATVR              257
> +#define ENET_ATOFF             258
> +#define ENET_ATPER             259
> +#define ENET_ATCOR             260
> +#define ENET_ATINC             261
> +#define ENET_ATSTMP            262
> +#define ENET_TGSR              385
> +#define ENET_TCSR0             386
> +#define ENET_TCCR0             387
> +#define ENET_TCSR1             388
> +#define ENET_TCCR1             389
> +#define ENET_TCSR2             390
> +#define ENET_TCCR2             391
> +#define ENET_TCSR3             392
> +#define ENET_TCCR3             393
>  #define ENET_MAX               400
>
> -#define FEC_MAX_FRAME_SIZE 2032
> -
> -#define FEC_INT_HB      (1 << 31)
> -#define FEC_INT_BABR    (1 << 30)
> -#define FEC_INT_BABT    (1 << 29)
> -#define FEC_INT_GRA     (1 << 28)
> -#define FEC_INT_TXF     (1 << 27)
> -#define FEC_INT_TXB     (1 << 26)
> -#define FEC_INT_RXF     (1 << 25)
> -#define FEC_INT_RXB     (1 << 24)
> -#define FEC_INT_MII     (1 << 23)
> -#define FEC_INT_EBERR   (1 << 22)
> -#define FEC_INT_LC      (1 << 21)
> -#define FEC_INT_RL      (1 << 20)
> -#define FEC_INT_UN      (1 << 19)
> +#define ENET_MAX_FRAME_SIZE    2032
> +
> +/* EIR and EIMR */
> +#define ENET_INT_HB            (1 << 31)
> +#define ENET_INT_BABR          (1 << 30)
> +#define ENET_INT_BABT          (1 << 29)
> +#define ENET_INT_GRA           (1 << 28)
> +#define ENET_INT_TXF           (1 << 27)
> +#define ENET_INT_TXB           (1 << 26)
> +#define ENET_INT_RXF           (1 << 25)
> +#define ENET_INT_RXB           (1 << 24)
> +#define ENET_INT_MII           (1 << 23)
> +#define ENET_INT_EBERR         (1 << 22)
> +#define ENET_INT_LC            (1 << 21)
> +#define ENET_INT_RL            (1 << 20)
> +#define ENET_INT_UN            (1 << 19)
> +#define ENET_INT_PLR           (1 << 18)
> +#define ENET_INT_WAKEUP        (1 << 17)
> +#define ENET_INT_TS_AVAIL      (1 << 16)
> +#define ENET_INT_TS_TIMER      (1 << 15)
>
>  /* RDAR */
>  #define ENET_RDAR_RDAR         (1 << 24)
> @@ -79,8 +111,54 @@
>  /* TDAR */
>  #define ENET_TDAR_TDAR         (1 << 24)
>
> -#define FEC_EN                 (1 << 1)
> -#define FEC_RESET              (1 << 0)
> +/* ECR */
> +#define ENET_ECR_RESET         (1 << 0)
> +#define ENET_ECR_ETHEREN       (1 << 1)
> +#define ENET_ECR_MAGICEN       (1 << 2)
> +#define ENET_ECR_SLEEP         (1 << 3)
> +#define ENET_ECR_EN1588        (1 << 4)
> +#define ENET_ECR_SPEED         (1 << 5)
> +#define ENET_ECR_DBGEN         (1 << 6)
> +#define ENET_ECR_STOPEN        (1 << 7)
> +#define ENET_ECR_DSBWP         (1 << 8)
> +
> +/* MIBC */
> +#define ENET_MIBC_MIB_DIS      (1 << 31)
> +#define ENET_MIBC_MIB_IDLE     (1 << 30)
> +#define ENET_MIBC_MIB_CLEAR    (1 << 29)
> +
> +/* RCR */
> +#define ENET_RCR_LOOP          (1 << 0)
> +#define ENET_RCR_DRT           (1 << 1)
> +#define ENET_RCR_MII_MODE      (1 << 2)
> +#define ENET_RCR_PROM          (1 << 3)
> +#define ENET_RCR_BC_REJ        (1 << 4)
> +#define ENET_RCR_FCE           (1 << 5)
> +#define ENET_RCR_RGMII_EN      (1 << 6)
> +#define ENET_RCR_RMII_MODE     (1 << 8)
> +#define ENET_RCR_RMII_10T      (1 << 9)
> +#define ENET_RCR_PADEN         (1 << 12)
> +#define ENET_RCR_PAUFWD        (1 << 13)
> +#define ENET_RCR_CRCFWD        (1 << 14)
> +#define ENET_RCR_CFEN          (1 << 15)
> +#define ENET_RCR_MAX_FL_SHIFT  (16)
> +#define ENET_RCR_MAX_FL_LENGTH (14)
> +#define ENET_RCR_NLC           (1 << 30)
> +#define ENET_RCR_GRS           (1 << 31)
> +
> +/* TCR */
> +#define ENET_TCR_GTS           (1 << 0)
> +#define ENET_TCR_FDEN          (1 << 2)
> +#define ENET_TCR_TFC_PAUSE     (1 << 3)
> +#define ENET_TCR_RFC_PAUSE     (1 << 4)
> +#define ENET_TCR_ADDSEL_SHIFT  (5)
> +#define ENET_TCR_ADDSEL_LENGTH (3)
> +#define ENET_TCR_CRCFWD        (1 << 9)
> +
> +/* RDSR */
> +#define ENET_TWFR_TFWR_SHIFT   (0)
> +#define ENET_TWFR_TFWR_LENGTH  (6)
> +#define ENET_TWFR_STRFWD       (1 << 8)
>
>  /* Buffer Descriptor.  */
>  typedef struct {
> @@ -89,22 +167,60 @@ typedef struct {
>      uint32_t data;
>  } IMXFECBufDesc;
>
> -#define FEC_BD_R    (1 << 15)
> -#define FEC_BD_E    (1 << 15)
> -#define FEC_BD_O1   (1 << 14)
> -#define FEC_BD_W    (1 << 13)
> -#define FEC_BD_O2   (1 << 12)
> -#define FEC_BD_L    (1 << 11)
> -#define FEC_BD_TC   (1 << 10)
> -#define FEC_BD_ABC  (1 << 9)
> -#define FEC_BD_M    (1 << 8)
> -#define FEC_BD_BC   (1 << 7)
> -#define FEC_BD_MC   (1 << 6)
> -#define FEC_BD_LG   (1 << 5)
> -#define FEC_BD_NO   (1 << 4)
> -#define FEC_BD_CR   (1 << 2)
> -#define FEC_BD_OV   (1 << 1)
> -#define FEC_BD_TR   (1 << 0)
> +#define ENET_BD_R              (1 << 15)
> +#define ENET_BD_E              (1 << 15)
> +#define ENET_BD_O1             (1 << 14)
> +#define ENET_BD_W              (1 << 13)
> +#define ENET_BD_O2             (1 << 12)
> +#define ENET_BD_L              (1 << 11)
> +#define ENET_BD_TC             (1 << 10)
> +#define ENET_BD_ABC            (1 << 9)
> +#define ENET_BD_M              (1 << 8)
> +#define ENET_BD_BC             (1 << 7)
> +#define ENET_BD_MC             (1 << 6)
> +#define ENET_BD_LG             (1 << 5)
> +#define ENET_BD_NO             (1 << 4)
> +#define ENET_BD_CR             (1 << 2)
> +#define ENET_BD_OV             (1 << 1)
> +#define ENET_BD_TR             (1 << 0)
> +
> +typedef struct {
> +    uint16_t length;
> +    uint16_t flags;
> +    uint32_t data;
> +    uint16_t status;
> +    uint16_t option;
> +    uint16_t checksum;
> +    uint16_t head_proto;
> +    uint32_t last_buffer;
> +    uint32_t timestamp;
> +    uint32_t reserved[2];
> +} IMXENETBufDesc;
> +
> +#define ENET_BD_ME             (1 << 15)
> +#define ENET_BD_TX_INT         (1 << 14)
> +#define ENET_BD_TS             (1 << 13)
> +#define ENET_BD_PINS           (1 << 12)
> +#define ENET_BD_IINS           (1 << 11)
> +#define ENET_BD_PE             (1 << 10)
> +#define ENET_BD_CE             (1 << 9)
> +#define ENET_BD_UC             (1 << 8)
> +#define ENET_BD_RX_INT         (1 << 7)
> +
> +#define ENET_BD_TXE            (1 << 15)
> +#define ENET_BD_UE             (1 << 13)
> +#define ENET_BD_EE             (1 << 12)
> +#define ENET_BD_FE             (1 << 11)
> +#define ENET_BD_LCE            (1 << 10)
> +#define ENET_BD_OE             (1 << 9)
> +#define ENET_BD_TSE            (1 << 8)
> +#define ENET_BD_ICE            (1 << 5)
> +#define ENET_BD_PCR            (1 << 4)
> +#define ENET_BD_VLAN           (1 << 2)
> +#define ENET_BD_IPV6           (1 << 1)
> +#define ENET_BD_FRAG           (1 << 0)
> +
> +#define ENET_BD_BDU            (1 << 31)
>
>  typedef struct IMXFECState {
>      /*< private >*/
> @@ -113,7 +229,7 @@ typedef struct IMXFECState {
>      /*< public >*/
>      NICState *nic;
>      NICConf conf;
> -    qemu_irq irq;
> +    qemu_irq irq[2];
>      MemoryRegion iomem;
>
>      uint32_t regs[ENET_MAX];
> @@ -125,6 +241,8 @@ typedef struct IMXFECState {
>      uint32_t phy_advertise;
>      uint32_t phy_int;
>      uint32_t phy_int_mask;
> +
> +    bool is_fec;
>  } IMXFECState;

thanks
-- PMM
diff mbox

Patch

diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c
index 2f878b9..ddb2b22 100644
--- a/hw/arm/fsl-imx25.c
+++ b/hw/arm/fsl-imx25.c
@@ -191,6 +191,9 @@  static void fsl_imx25_realize(DeviceState *dev, Error **errp)
     }
 
     qdev_set_nic_properties(DEVICE(&s->fec), &nd_table[0]);
+
+    object_property_set_bool(OBJECT(&s->fec), true, "is-fec", &error_abort);
+
     object_property_set_bool(OBJECT(&s->fec), true, "realized", &err);
     if (err) {
         error_propagate(errp, err);
diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c
index 20e6524..b9893ee 100644
--- a/hw/net/imx_fec.c
+++ b/hw/net/imx_fec.c
@@ -24,6 +24,8 @@ 
 #include "qemu/osdep.h"
 #include "hw/net/imx_fec.h"
 #include "sysemu/dma.h"
+#include "net/checksum.h"
+#include "net/eth.h"
 
 /* For crc32 */
 #include <zlib.h>
@@ -52,10 +54,93 @@ 
         } \
     } while (0)
 
-static const char *imx_fec_reg_name(IMXFECState *s, uint32_t index)
+static const char *imx_default_reg_name(IMXFECState *s, uint32_t index)
 {
     static char tmp[20];
+    sprintf(tmp, "index %d", index);
+    return tmp;
+}
+
+static const char *imx_fec_reg_name(IMXFECState *s, uint32_t index)
+{
+    switch (index) {
+    case ENET_FRBR:
+        return "FRBR";
+    case ENET_FRSR:
+        return "FRSR";
+    case ENET_MIIGSK_CFGR:
+        return "MIIGSK_CFGR";
+    case ENET_MIIGSK_ENR:
+        return "MIIGSK_ENR";
+    default:
+        return imx_default_reg_name(s, index);
+    }
+}
 
+static const char *imx_enet_reg_name(IMXFECState *s, uint32_t index)
+{
+    switch (index) {
+    case ENET_RSFL:
+        return "RSFL";
+    case ENET_RSEM:
+        return "RSEM";
+    case ENET_RAEM:
+        return "RAEM";
+    case ENET_RAFL:
+        return "RAFL";
+    case ENET_TSEM:
+        return "TSEM";
+    case ENET_TAEM:
+        return "TAEM";
+    case ENET_TAFL:
+        return "TAFL";
+    case ENET_TIPG:
+        return "TIPG";
+    case ENET_FTRL:
+        return "FTRL";
+    case ENET_TACC:
+        return "TACC";
+    case ENET_RACC:
+        return "RACC";
+    case ENET_ATCR:
+        return "ATCR";
+    case ENET_ATVR:
+        return "ATVR";
+    case ENET_ATOFF:
+        return "ATOFF";
+    case ENET_ATPER:
+        return "ATPER";
+    case ENET_ATCOR:
+        return "ATCOR";
+    case ENET_ATINC:
+        return "ATINC";
+    case ENET_ATSTMP:
+        return "ATSTMP";
+    case ENET_TGSR:
+        return "TGSR";
+    case ENET_TCSR0:
+        return "TCSR0";
+    case ENET_TCCR0:
+        return "TCCR0";
+    case ENET_TCSR1:
+        return "TCSR1";
+    case ENET_TCCR1:
+        return "TCCR1";
+    case ENET_TCSR2:
+        return "TCSR2";
+    case ENET_TCCR2:
+        return "TCCR2";
+    case ENET_TCSR3:
+        return "TCSR3";
+    case ENET_TCCR3:
+        return "TCCR3";
+    default:
+        return imx_default_reg_name(s, index);
+    }
+}
+
+static const char *imx_eth_reg_name(IMXFECState *s, uint32_t index)
+{
     switch (index) {
     case ENET_EIR:
         return "EIR";
@@ -99,21 +184,16 @@  static const char *imx_fec_reg_name(IMXFECState *s, uint32_t index)
         return "TDSR";
     case ENET_MRBR:
         return "MRBR";
-    case ENET_FRBR:
-        return "FRBR";
-    case ENET_FRSR:
-        return "FRSR";
-    case ENET_MIIGSK_CFGR:
-        return "MIIGSK_CFGR";
-    case ENET_MIIGSK_ENR:
-        return "MIIGSK_ENR";
     default:
-        sprintf(tmp, "index %d", index);
-        return tmp;
+        if (s->is_fec) {
+            return imx_fec_reg_name(s, index);
+        } else {
+            return imx_enet_reg_name(s, index);
+        }
     }
 }
 
-static const VMStateDescription vmstate_imx_fec = {
+static const VMStateDescription vmstate_imx_eth = {
     .name = TYPE_IMX_FEC,
     .version_id = 2,
     .minimum_version_id = 2,
@@ -127,6 +207,7 @@  static const VMStateDescription vmstate_imx_fec = {
         VMSTATE_UINT32(phy_advertise, IMXFECState),
         VMSTATE_UINT32(phy_int, IMXFECState),
         VMSTATE_UINT32(phy_int_mask, IMXFECState),
+
         VMSTATE_END_OF_LIST()
     }
 };
@@ -139,7 +220,7 @@  static const VMStateDescription vmstate_imx_fec = {
 #define PHY_INT_PARFAULT            (1 << 2)
 #define PHY_INT_AUTONEG_PAGE        (1 << 1)
 
-static void imx_fec_update(IMXFECState *s);
+static void imx_eth_update(IMXFECState *s);
 
 /*
  * The MII phy could raise a GPIO to the processor which in turn
@@ -149,7 +230,7 @@  static void imx_fec_update(IMXFECState *s);
  */
 static void phy_update_irq(IMXFECState *s)
 {
-    imx_fec_update(s);
+    imx_eth_update(s);
 }
 
 static void phy_update_link(IMXFECState *s)
@@ -168,7 +249,7 @@  static void phy_update_link(IMXFECState *s)
     phy_update_irq(s);
 }
 
-static void imx_fec_set_link(NetClientState *nc)
+static void imx_eth_set_link(NetClientState *nc)
 {
     phy_update_link(IMX_FEC(qemu_get_nic_opaque(nc)));
 }
@@ -294,21 +375,33 @@  static void imx_fec_write_bd(IMXFECBufDesc *bd, dma_addr_t addr)
     dma_memory_write(&address_space_memory, addr, bd, sizeof(*bd));
 }
 
-static void imx_fec_update(IMXFECState *s)
+static void imx_enet_read_bd(IMXENETBufDesc *bd, dma_addr_t addr)
+{
+    dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd));
+}
+
+static void imx_enet_write_bd(IMXENETBufDesc *bd, dma_addr_t addr)
+{
+    dma_memory_write(&address_space_memory, addr, bd, sizeof(*bd));
+}
+
+static void imx_eth_update(IMXFECState *s)
 {
     if (s->regs[ENET_EIR] & s->regs[ENET_EIMR]) {
         FEC_PRINTF("interupt raised\n");
-        qemu_set_irq(s->irq, 1);
+        qemu_set_irq(s->irq[0], 1);
+        qemu_set_irq(s->irq[1], 1);
     } else {
         FEC_PRINTF("interupt lowered\n");
-        qemu_set_irq(s->irq, 0);
+        qemu_set_irq(s->irq[0], 0);
+        qemu_set_irq(s->irq[1], 0);
     }
 }
 
 static void imx_fec_do_tx(IMXFECState *s)
 {
     int frame_size = 0;
-    uint8_t frame[FEC_MAX_FRAME_SIZE];
+    uint8_t frame[ENET_MAX_FRAME_SIZE];
     uint8_t *ptr = frame;
     uint32_t addr = s->tx_descriptor;
 
@@ -318,32 +411,105 @@  static void imx_fec_do_tx(IMXFECState *s)
 
         imx_fec_read_bd(&bd, addr);
         FEC_PRINTF("tx_bd %x flags %04x len %d data %08x\n",
-                   addr, bd.flags, bd.length, bd.data);
-        if ((bd.flags & FEC_BD_R) == 0) {
+                    addr, bd.flags, bd.length, bd.data);
+        if ((bd.flags & ENET_BD_R) == 0) {
             /* Run out of descriptors to transmit.  */
+            FEC_PRINTF("tx_bd running out of descriptor to transmit\n");
             break;
         }
         len = bd.length;
-        if (frame_size + len > FEC_MAX_FRAME_SIZE) {
-            len = FEC_MAX_FRAME_SIZE - frame_size;
-            s->regs[ENET_EIR] |= FEC_INT_BABT;
+        if (frame_size + len > ENET_MAX_FRAME_SIZE) {
+            len = ENET_MAX_FRAME_SIZE - frame_size;
+            s->regs[ENET_EIR] |= ENET_INT_BABT;
         }
         dma_memory_read(&address_space_memory, bd.data, ptr, len);
         ptr += len;
         frame_size += len;
-        if (bd.flags & FEC_BD_L) {
+        if (bd.flags & ENET_BD_L) {
+            FEC_PRINTF("sending %d bytes frame\n", len);
             /* Last buffer in frame.  */
             qemu_send_packet(qemu_get_queue(s->nic), frame, len);
             ptr = frame;
             frame_size = 0;
-            s->regs[ENET_EIR] |= FEC_INT_TXF;
+            s->regs[ENET_EIR] |= ENET_INT_TXF;
         }
-        s->regs[ENET_EIR] |= FEC_INT_TXB;
-        bd.flags &= ~FEC_BD_R;
+        s->regs[ENET_EIR] |= ENET_INT_TXB;
+        bd.flags &= ~ENET_BD_R;
         /* Write back the modified descriptor.  */
         imx_fec_write_bd(&bd, addr);
         /* Advance to the next descriptor.  */
-        if ((bd.flags & FEC_BD_W) != 0) {
+        if ((bd.flags & ENET_BD_W) != 0) {
+            addr = s->regs[ENET_TDSR];
+        } else {
+            addr += sizeof(bd);
+        }
+    }
+
+    s->tx_descriptor = addr;
+
+    imx_eth_update(s);
+}
+
+static void imx_enet_do_tx(IMXFECState *s)
+{
+    int frame_size = 0;
+    uint8_t frame[ENET_MAX_FRAME_SIZE];
+    uint8_t *ptr = frame;
+    uint32_t addr = s->tx_descriptor;
+
+    while (1) {
+        IMXENETBufDesc bd;
+        int len;
+
+        imx_enet_read_bd(&bd, addr);
+        FEC_PRINTF("tx_bd %x flags %04x len %d data %08x option %04x "
+                   "status %04x\n", addr, bd.flags, bd.length, bd.data,
+                   bd.option, bd.status);
+        if ((bd.flags & ENET_BD_R) == 0) {
+            /* Run out of descriptors to transmit.  */
+            break;
+        }
+        len = bd.length;
+        if (frame_size + len > ENET_MAX_FRAME_SIZE) {
+            len = ENET_MAX_FRAME_SIZE - frame_size;
+            s->regs[ENET_EIR] |= ENET_INT_BABT;
+        }
+        dma_memory_read(&address_space_memory, bd.data, ptr, len);
+        ptr += len;
+        frame_size += len;
+        if (bd.flags & ENET_BD_L) {
+            if (bd.option & ENET_BD_PINS) {
+                struct ip_header *ip_hd = PKT_GET_IP_HDR(frame);
+                if (IP_HEADER_VERSION(ip_hd) == 4) {
+                    net_checksum_calculate(frame, frame_size);
+                }
+            }
+            if (bd.option & ENET_BD_IINS) {
+                struct ip_header *ip_hd = PKT_GET_IP_HDR(frame);
+                /* We compute checksum only for IPv4 frames */
+                if (IP_HEADER_VERSION(ip_hd) == 4) {
+                    uint16_t csum;
+                    ip_hd->ip_sum = 0;
+                    csum = net_raw_checksum((uint8_t *)ip_hd, sizeof(*ip_hd));
+                    ip_hd->ip_sum = cpu_to_be16(csum);
+                }
+            }
+            /* Last buffer in frame.  */
+            qemu_send_packet(qemu_get_queue(s->nic), frame, len);
+            ptr = frame;
+            frame_size = 0;
+            if (bd.option & ENET_BD_TX_INT) {
+                s->regs[ENET_EIR] |= ENET_INT_TXF;
+            }
+        }
+        if (bd.option & ENET_BD_TX_INT) {
+            s->regs[ENET_EIR] |= ENET_INT_TXB;
+        }
+        bd.flags &= ~ENET_BD_R;
+        /* Write back the modified descriptor.  */
+        imx_enet_write_bd(&bd, addr);
+        /* Advance to the next descriptor.  */
+        if ((bd.flags & ENET_BD_W) != 0) {
             addr = s->regs[ENET_TDSR];
         } else {
             addr += sizeof(bd);
@@ -352,17 +518,26 @@  static void imx_fec_do_tx(IMXFECState *s)
 
     s->tx_descriptor = addr;
 
-    imx_fec_update(s);
+    imx_eth_update(s);
+}
+
+static void imx_eth_do_tx(IMXFECState *s)
+{
+    if (!s->is_fec && (s->regs[ENET_ECR] & ENET_ECR_EN1588)) {
+        imx_enet_do_tx(s);
+    } else {
+        imx_fec_do_tx(s);
+    }
 }
 
-static void imx_fec_enable_rx(IMXFECState *s)
+static void imx_eth_enable_rx(IMXFECState *s)
 {
     IMXFECBufDesc bd;
     bool tmp;
 
     imx_fec_read_bd(&bd, s->rx_descriptor);
 
-    tmp = ((bd.flags & FEC_BD_E) != 0);
+    tmp = ((bd.flags & ENET_BD_E) != 0);
 
     if (!tmp) {
         FEC_PRINTF("RX buffer full\n");
@@ -373,11 +548,11 @@  static void imx_fec_enable_rx(IMXFECState *s)
     s->regs[ENET_RDAR] = tmp ? ENET_RDAR_RDAR : 0;
 }
 
-static void imx_fec_reset(DeviceState *d)
+static void imx_eth_reset(DeviceState *d)
 {
     IMXFECState *s = IMX_FEC(d);
 
-    /* Reset the FEC */
+    /* Reset the Device */
     memset(s->regs, 0, sizeof(s->regs));
     s->regs[ENET_ECR]   = 0xf0000000;
     s->regs[ENET_MIBC]  = 0xc0000000;
@@ -392,19 +567,85 @@  static void imx_fec_reset(DeviceState *d)
                           | (s->conf.macaddr.a[5] << 16)
                           | 0x8808;
 
-    s->regs[ENET_FRBR]  = 0x00000600;
-    s->regs[ENET_FRSR]  = 0x00000500;
-    s->regs[ENET_MIIGSK_ENR]  = 0x00000006;
+    if (s->is_fec) {
+        s->regs[ENET_FRBR]  = 0x00000600;
+        s->regs[ENET_FRSR]  = 0x00000500;
+        s->regs[ENET_MIIGSK_ENR]  = 0x00000006;
+    } else {
+        s->regs[ENET_RAEM]  = 0x00000004;
+        s->regs[ENET_RAFL]  = 0x00000004;
+        s->regs[ENET_TAEM]  = 0x00000004;
+        s->regs[ENET_TAFL]  = 0x00000008;
+        s->regs[ENET_TIPG]  = 0x0000000c;
+        s->regs[ENET_FTRL]  = 0x000007ff;
+        s->regs[ENET_ATPER] = 0x3b9aca00;
+    }
 
     /* We also reset the PHY */
     phy_reset(s);
 }
 
-static uint64_t imx_fec_read(void *opaque, hwaddr addr, unsigned size)
+static uint32_t imx_default_read(IMXFECState *s, uint32_t index)
+{
+    qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+                  PRIx32 "\n", TYPE_IMX_FEC, __func__, index * 4);
+    return 0;
+}
+
+static uint32_t imx_fec_read(IMXFECState *s, uint32_t index)
+{
+    switch (index) {
+    case ENET_FRBR:
+    case ENET_FRSR:
+    case ENET_MIIGSK_CFGR:
+    case ENET_MIIGSK_ENR:
+        return s->regs[index];
+    default:
+        return imx_default_read(s, index);
+    }
+}
+
+static uint32_t imx_enet_read(IMXFECState *s, uint32_t index)
+{
+    switch (index) {
+    case ENET_RSFL:
+    case ENET_RSEM:
+    case ENET_RAEM:
+    case ENET_RAFL:
+    case ENET_TSEM:
+    case ENET_TAEM:
+    case ENET_TAFL:
+    case ENET_TIPG:
+    case ENET_FTRL:
+    case ENET_TACC:
+    case ENET_RACC:
+    case ENET_ATCR:
+    case ENET_ATVR:
+    case ENET_ATOFF:
+    case ENET_ATPER:
+    case ENET_ATCOR:
+    case ENET_ATINC:
+    case ENET_ATSTMP:
+    case ENET_TGSR:
+    case ENET_TCSR0:
+    case ENET_TCCR0:
+    case ENET_TCSR1:
+    case ENET_TCCR1:
+    case ENET_TCSR2:
+    case ENET_TCCR2:
+    case ENET_TCSR3:
+    case ENET_TCCR3:
+        return s->regs[index];
+    default:
+        return imx_default_read(s, index);
+    }
+}
+
+static uint64_t imx_eth_read(void *opaque, hwaddr offset, unsigned size)
 {
     uint32_t value = 0;
     IMXFECState *s = IMX_FEC(opaque);
-    uint32_t index = addr >> 2;
+    uint32_t index = offset >> 2;
 
     switch (index) {
     case ENET_EIR:
@@ -428,32 +669,126 @@  static uint64_t imx_fec_read(void *opaque, hwaddr addr, unsigned size)
     case ENET_RDSR:
     case ENET_TDSR:
     case ENET_MRBR:
-    case ENET_FRBR:
-    case ENET_FRSR:
-    case ENET_MIIGSK_CFGR:
-    case ENET_MIIGSK_ENR:
         value = s->regs[index];
         break;
     default:
-        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
-                      PRIx32 "\n", TYPE_IMX_FEC, __func__, index * 4);
+        if (s->is_fec) {
+            value = imx_fec_read(s, index);
+        } else {
+            value = imx_enet_read(s, index);
+        }
         break;
     }
 
-    FEC_PRINTF("reg[%s] => 0x%" PRIx32 "\n", imx_fec_reg_name(s, index),
+    FEC_PRINTF("reg[%s] => 0x%" PRIx32 "\n", imx_eth_reg_name(s, index),
                                               value);
 
     return value;
 }
 
-static void imx_fec_write(void *opaque, hwaddr addr,
-                          uint64_t value, unsigned size)
+static void imx_default_write(IMXFECState *s, uint32_t index, uint32_t value)
+{
+    qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
+                  PRIx32 "\n", TYPE_IMX_FEC, __func__, index * 4);
+    return;
+}
+
+static void imx_fec_write(IMXFECState *s, uint32_t index, uint32_t value)
+{
+    switch (index) {
+    case ENET_FRBR:
+        /* FRBR is read only */
+        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Register FRBR is read only\n",
+                      TYPE_IMX_FEC, __func__);
+        break;
+    case ENET_FRSR:
+        s->regs[index] = (value & 0x000003fc) | 0x00000400;
+        break;
+    case ENET_MIIGSK_CFGR:
+        s->regs[index] = value & 0x00000053;
+        break;
+    case ENET_MIIGSK_ENR:
+        s->regs[index] = (value & 0x00000002) ? 0x00000006 : 0;
+        break;
+    default:
+        imx_default_write(s, index, value);
+        break;
+    }
+}
+
+static void imx_enet_write(IMXFECState *s, uint32_t index, uint32_t value)
+{
+    switch (index) {
+    case ENET_RSFL:
+    case ENET_RSEM:
+    case ENET_RAEM:
+    case ENET_RAFL:
+    case ENET_TSEM:
+    case ENET_TAEM:
+    case ENET_TAFL:
+        s->regs[index] = value & 0x000001ff;
+        break;
+    case ENET_TIPG:
+        s->regs[index] = value & 0x0000001f;
+        break;
+    case ENET_FTRL:
+        s->regs[index] = value & 0x00003fff;
+        break;
+    case ENET_TACC:
+        s->regs[index] = value & 0x00000019;
+        break;
+    case ENET_RACC:
+        s->regs[index] = value & 0x000000C7;
+        break;
+    case ENET_ATCR:
+        s->regs[index] = value & 0x00002a9d;
+        break;
+    case ENET_ATVR:
+    case ENET_ATOFF:
+    case ENET_ATPER:
+        s->regs[index] = value;
+        break;
+    case ENET_ATSTMP:
+        /* ATSTMP is read only */
+        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Register ATSTMP is read only\n",
+                      TYPE_IMX_FEC, __func__);
+        break;
+    case ENET_ATCOR:
+        s->regs[index] = value & 0x7fffffff;
+        break;
+    case ENET_ATINC:
+        s->regs[index] = value & 0x00007f7f;
+        break;
+    case ENET_TGSR:
+        /* implement clear timer flag */
+        value = value & 0x0000000f;
+        break;
+    case ENET_TCSR0:
+    case ENET_TCSR1:
+    case ENET_TCSR2:
+    case ENET_TCSR3:
+        value = value & 0x000000fd;
+        break;
+    case ENET_TCCR0:
+    case ENET_TCCR1:
+    case ENET_TCCR2:
+    case ENET_TCCR3:
+        s->regs[index] = value;
+        break;
+    default:
+        imx_default_write(s, index, value);
+        break;
+    }
+}
+
+static void imx_eth_write(void *opaque, hwaddr offset, uint64_t value,
+                           unsigned size)
 {
     IMXFECState *s = IMX_FEC(opaque);
-    uint32_t index = addr >> 2;
+    uint32_t index = offset >> 2;
 
-    FEC_PRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx_fec_reg_name(s, index),
-                                             (uint32_t)value);
+    FEC_PRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx_eth_reg_name(s, index),
+                (uint32_t)value);
 
     switch (index) {
     case ENET_EIR:
@@ -465,26 +800,26 @@  static void imx_fec_write(void *opaque, hwaddr addr,
     case ENET_RDAR:
         value = ENET_RDAR_RDAR;
         s->regs[index] = value;
-        if ((s->regs[ENET_ECR] & FEC_EN) && !s->regs[ENET_RDAR]) {
-            imx_fec_enable_rx(s);
+        if ((s->regs[ENET_ECR] & ENET_ECR_ETHEREN) && !s->regs[ENET_RDAR]) {
+            imx_eth_enable_rx(s);
         }
         break;
     case ENET_TDAR:
         /* TDAR bit is always writen to 1 whatever the value writen to reg */
         value = ENET_TDAR_TDAR;
         s->regs[index] = value;
-        if (s->regs[ENET_ECR] & FEC_EN) {
-            imx_fec_do_tx(s);
+        if (s->regs[ENET_ECR] & ENET_ECR_ETHEREN) {
+            imx_eth_do_tx(s);
         }
         break;
     case ENET_ECR:
-        if (value & FEC_RESET) {
-            value &= ~FEC_EN;
+        if (value & ENET_ECR_RESET) {
+            value &= ~ENET_ECR_ETHEREN;
             s->regs[ENET_RDAR] = 0;
             s->regs[ENET_TDAR] = 0;
         }
         s->regs[index] = value;
-        if ((s->regs[ENET_ECR] & FEC_EN) == 0) {
+        if ((s->regs[ENET_ECR] & ENET_ECR_ETHEREN) == 0) {
             s->regs[ENET_RDAR] = 0;
         }
         break;
@@ -500,7 +835,7 @@  static void imx_fec_write(void *opaque, hwaddr addr,
             do_phy_write(s, extract32(value, 18, 10), extract32(value, 0, 16));
         }
         /* raise the interrupt as the PHY operation is done */
-        s->regs[ENET_EIR] |= FEC_INT_MII;
+        s->regs[ENET_EIR] |= ENET_INT_MII;
         break;
     case ENET_MSCR:
         s->regs[index] = value & 0xfe;
@@ -517,7 +852,7 @@  static void imx_fec_write(void *opaque, hwaddr addr,
         /* We transmit immediately, so raise GRA immediately.  */
         s->regs[index] = value;
         if (value & 1) {
-            s->regs[ENET_EIR] |= FEC_INT_GRA;
+            s->regs[ENET_EIR] |= ENET_INT_GRA;
         }
         break;
     case ENET_PALR:
@@ -542,46 +877,49 @@  static void imx_fec_write(void *opaque, hwaddr addr,
         /* TODO: implement MAC hash filtering.  */
         break;
     case ENET_TFWR:
-        s->regs[index] = value & 0x00000003;
-        break;
-    case ENET_FRBR:
-        /* FRBR is read only */
-        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Register FRBR is read only\n",
-                      TYPE_IMX_FEC, __func__);
-        break;
-    case ENET_FRSR:
-        s->regs[index] = (value & 0x000003fc) | 0x00000400;
+        if (s->is_fec) {
+            s->regs[index] = value & 0x00000003;
+        } else {
+            s->regs[index] = value & 0x0000013f;
+        }
         break;
     case ENET_RDSR:
-        s->regs[index] = value & ~3;
+        if (s->is_fec) {
+            s->regs[index] = value & ~3;
+        } else {
+            s->regs[index] = value & ~7;
+        }
         s->rx_descriptor = s->regs[index];
         break;
     case ENET_TDSR:
-        s->regs[index] = value & ~3;
+        if (s->is_fec) {
+            s->regs[index] = value & ~3;
+        } else {
+            s->regs[index] = value & ~7;
+        }
         s->tx_descriptor = s->regs[index];
         break;
     case ENET_MRBR:
-        s->regs[index] = value & 0x000007f0;
-        break;
-    case ENET_MIIGSK_CFGR:
-        s->regs[index] = value & 0x00000053;
-        break;
-    case ENET_MIIGSK_ENR:
-        s->regs[index] = (value & 0x00000002) ? 0x00000006 : 0;
+        s->regs[index] = value & 0x00003ff0;
         break;
     default:
-        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
-                      PRIx32 "\n", TYPE_IMX_FEC, __func__, index * 4);
-        break;
+        if (s->is_fec) {
+            imx_fec_write(s, index, value);
+        } else {
+            imx_enet_write(s, index, value);
+        }
+        return;
     }
 
-    imx_fec_update(s);
+    imx_eth_update(s);
 }
 
-static int imx_fec_can_receive(NetClientState *nc)
+static int imx_eth_can_receive(NetClientState *nc)
 {
     IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
 
+    FEC_PRINTF("\n");
+
     return s->regs[ENET_RDAR] ? 1 : 0;
 }
 
@@ -612,20 +950,20 @@  static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf,
     crc_ptr = (uint8_t *) &crc;
 
     /* Huge frames are truncted.  */
-    if (size > FEC_MAX_FRAME_SIZE) {
-        size = FEC_MAX_FRAME_SIZE;
-        flags |= FEC_BD_TR | FEC_BD_LG;
+    if (size > ENET_MAX_FRAME_SIZE) {
+        size = ENET_MAX_FRAME_SIZE;
+        flags |= ENET_BD_TR | ENET_BD_LG;
     }
 
     /* Frames larger than the user limit just set error flags.  */
     if (size > (s->regs[ENET_RCR] >> 16)) {
-        flags |= FEC_BD_LG;
+        flags |= ENET_BD_LG;
     }
 
     addr = s->rx_descriptor;
     while (size > 0) {
         imx_fec_read_bd(&bd, addr);
-        if ((bd.flags & FEC_BD_E) == 0) {
+        if ((bd.flags & ENET_BD_E) == 0) {
             /* No descriptors available.  Bail out.  */
             /*
              * FIXME: This is wrong. We should probably either
@@ -654,99 +992,213 @@  static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf,
                              crc_ptr, 4 - size);
             crc_ptr += 4 - size;
         }
-        bd.flags &= ~FEC_BD_E;
+        bd.flags &= ~ENET_BD_E;
         if (size == 0) {
             /* Last buffer in frame.  */
-            bd.flags |= flags | FEC_BD_L;
+            bd.flags |= flags | ENET_BD_L;
             FEC_PRINTF("rx frame flags %04x\n", bd.flags);
-            s->regs[ENET_EIR] |= FEC_INT_RXF;
+            s->regs[ENET_EIR] |= ENET_INT_RXF;
         } else {
-            s->regs[ENET_EIR] |= FEC_INT_RXB;
+            s->regs[ENET_EIR] |= ENET_INT_RXB;
         }
         imx_fec_write_bd(&bd, addr);
         /* Advance to the next descriptor.  */
-        if ((bd.flags & FEC_BD_W) != 0) {
+        if ((bd.flags & ENET_BD_W) != 0) {
+            addr = s->regs[ENET_RDSR];
+        } else {
+            addr += sizeof(bd);
+        }
+    }
+    s->rx_descriptor = addr;
+    imx_eth_enable_rx(s);
+    imx_eth_update(s);
+    return len;
+}
+
+static ssize_t imx_enet_receive(NetClientState *nc, const uint8_t *buf,
+                                size_t len)
+{
+    IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
+    IMXENETBufDesc bd;
+    uint32_t flags = 0;
+    uint32_t addr;
+    uint32_t crc;
+    uint32_t buf_addr;
+    uint8_t *crc_ptr;
+    unsigned int buf_len;
+    size_t size = len;
+
+    FEC_PRINTF("len %d\n", (int)size);
+
+    if (!s->regs[ENET_RDAR]) {
+        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Unexpected packet\n",
+                      TYPE_IMX_FEC, __func__);
+        return 0;
+    }
+
+    /* 4 bytes for the CRC.  */
+    size += 4;
+    crc = cpu_to_be32(crc32(~0, buf, size));
+    crc_ptr = (uint8_t *) &crc;
+
+    /* Huge frames are truncted.  */
+    if (size > ENET_MAX_FRAME_SIZE) {
+        size = ENET_MAX_FRAME_SIZE;
+        flags |= ENET_BD_TR | ENET_BD_LG;
+    }
+
+    /* Frames larger than the user limit just set error flags.  */
+    if (size > (s->regs[ENET_RCR] >> 16)) {
+        flags |= ENET_BD_LG;
+    }
+
+    addr = s->rx_descriptor;
+    while (size > 0) {
+        imx_enet_read_bd(&bd, addr);
+        if ((bd.flags & ENET_BD_E) == 0) {
+            /* No descriptors available.  Bail out.  */
+            /*
+             * FIXME: This is wrong. We should probably either
+             * save the remainder for when more RX buffers are
+             * available, or flag an error.
+             */
+            qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Lost end of frame\n",
+                          TYPE_IMX_FEC, __func__);
+            break;
+        }
+        buf_len = (size <= s->regs[ENET_MRBR]) ? size : s->regs[ENET_MRBR];
+        bd.length = buf_len;
+        size -= buf_len;
+
+        FEC_PRINTF("rx_bd 0x%x length %d\n", addr, bd.length);
+
+        /* The last 4 bytes are the CRC.  */
+        if (size < 4) {
+            buf_len += size - 4;
+        }
+        buf_addr = bd.data;
+        dma_memory_write(&address_space_memory, buf_addr, buf, buf_len);
+        buf += buf_len;
+        if (size < 4) {
+            dma_memory_write(&address_space_memory, buf_addr + buf_len,
+                             crc_ptr, 4 - size);
+            crc_ptr += 4 - size;
+        }
+        bd.flags &= ~ENET_BD_E;
+        if (size == 0) {
+            /* Last buffer in frame.  */
+            bd.flags |= flags | ENET_BD_L;
+            FEC_PRINTF("rx frame flags %04x\n", bd.flags);
+            if (bd.option & ENET_BD_RX_INT) {
+                s->regs[ENET_EIR] |= ENET_INT_RXF;
+            }
+        } else {
+            if (bd.option & ENET_BD_RX_INT) {
+                s->regs[ENET_EIR] |= ENET_INT_RXB;
+            }
+        }
+        imx_enet_write_bd(&bd, addr);
+        /* Advance to the next descriptor.  */
+        if ((bd.flags & ENET_BD_W) != 0) {
             addr = s->regs[ENET_RDSR];
         } else {
             addr += sizeof(bd);
         }
     }
     s->rx_descriptor = addr;
-    imx_fec_enable_rx(s);
-    imx_fec_update(s);
+    imx_eth_enable_rx(s);
+    imx_eth_update(s);
     return len;
 }
 
-static const MemoryRegionOps imx_fec_ops = {
-    .read = imx_fec_read,
-    .write = imx_fec_write,
+static ssize_t imx_eth_receive(NetClientState *nc, const uint8_t *buf,
+                                size_t len)
+{
+    IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
+
+    if (!s->is_fec && (s->regs[ENET_ECR] & ENET_ECR_EN1588)) {
+        return imx_enet_receive(nc, buf, len);
+    } else {
+        return imx_fec_receive(nc, buf, len);
+    }
+}
+
+static const MemoryRegionOps imx_eth_ops = {
+    .read                  = imx_eth_read,
+    .write                 = imx_eth_write,
     .valid.min_access_size = 4,
     .valid.max_access_size = 4,
-    .endianness = DEVICE_NATIVE_ENDIAN,
+    .endianness            = DEVICE_NATIVE_ENDIAN,
 };
 
-static void imx_fec_cleanup(NetClientState *nc)
+static void imx_eth_cleanup(NetClientState *nc)
 {
     IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
 
+    FEC_PRINTF("\n");
+
     s->nic = NULL;
 }
 
-static NetClientInfo net_imx_fec_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
-    .size = sizeof(NICState),
-    .can_receive = imx_fec_can_receive,
-    .receive = imx_fec_receive,
-    .cleanup = imx_fec_cleanup,
-    .link_status_changed = imx_fec_set_link,
+static NetClientInfo imx_eth_net_info = {
+    .type                = NET_CLIENT_OPTIONS_KIND_NIC,
+    .size                = sizeof(NICState),
+    .can_receive         = imx_eth_can_receive,
+    .receive             = imx_eth_receive,
+    .cleanup             = imx_eth_cleanup,
+    .link_status_changed = imx_eth_set_link,
 };
 
 
-static void imx_fec_realize(DeviceState *dev, Error **errp)
+static void imx_eth_realize(DeviceState *dev, Error **errp)
 {
     IMXFECState *s = IMX_FEC(dev);
     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
 
-    memory_region_init_io(&s->iomem, OBJECT(dev), &imx_fec_ops, s,
+    memory_region_init_io(&s->iomem, OBJECT(dev), &imx_eth_ops, s,
                           TYPE_IMX_FEC, 0x400);
     sysbus_init_mmio(sbd, &s->iomem);
-    sysbus_init_irq(sbd, &s->irq);
+    sysbus_init_irq(sbd, &s->irq[0]);
+    sysbus_init_irq(sbd, &s->irq[1]);
+
     qemu_macaddr_default_if_unset(&s->conf.macaddr);
 
     s->conf.peers.ncs[0] = nd_table[0].netdev;
 
-    s->nic = qemu_new_nic(&net_imx_fec_info, &s->conf,
-                          object_get_typename(OBJECT(dev)), DEVICE(dev)->id,
-                          s);
+    s->nic = qemu_new_nic(&imx_eth_net_info, &s->conf,
+                          object_get_typename(OBJECT(dev)),
+                          DEVICE(dev)->id, s);
+
     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
 }
 
-static Property imx_fec_properties[] = {
+static Property imx_eth_properties[] = {
     DEFINE_NIC_PROPERTIES(IMXFECState, conf),
+    DEFINE_PROP_BOOL("is-fec", IMXFECState, is_fec, false),
     DEFINE_PROP_END_OF_LIST(),
 };
 
-static void imx_fec_class_init(ObjectClass *klass, void *data)
+static void imx_eth_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
 
-    dc->vmsd = &vmstate_imx_fec;
-    dc->reset = imx_fec_reset;
-    dc->props = imx_fec_properties;
-    dc->realize = imx_fec_realize;
-    dc->desc = "i.MX FEC Ethernet Controller";
+    dc->vmsd    = &vmstate_imx_eth;
+    dc->reset   = imx_eth_reset;
+    dc->props   = imx_eth_properties;
+    dc->realize = imx_eth_realize;
+    dc->desc    = "i.MX FEC/ENET Ethernet Controller";
 }
 
-static const TypeInfo imx_fec_info = {
-    .name = TYPE_IMX_FEC,
-    .parent = TYPE_SYS_BUS_DEVICE,
+static const TypeInfo imx_eth_info = {
+    .name          = TYPE_IMX_FEC,
+    .parent        = TYPE_SYS_BUS_DEVICE,
     .instance_size = sizeof(IMXFECState),
-    .class_init = imx_fec_class_init,
+    .class_init    = imx_eth_class_init,
 };
 
-static void imx_fec_register_types(void)
+static void imx_eth_register_types(void)
 {
-    type_register_static(&imx_fec_info);
+    type_register_static(&imx_eth_info);
 }
 
-type_init(imx_fec_register_types)
+type_init(imx_eth_register_types)
diff --git a/include/hw/net/imx_fec.h b/include/hw/net/imx_fec.h
index 709f8a0..176716b 100644
--- a/include/hw/net/imx_fec.h
+++ b/include/hw/net/imx_fec.h
@@ -1,5 +1,5 @@ 
 /*
- * i.MX Fast Ethernet Controller emulation.
+ * i.MX FEC/ENET Ethernet Controller emulation.
  *
  * Copyright (c) 2013 Jean-Christophe Dubois. <jcd@tribudubois.net>
  *
@@ -53,25 +53,57 @@ 
 #define ENET_RDSR              96
 #define ENET_TDSR              97
 #define ENET_MRBR              98
+#define ENET_RSFL              100
+#define ENET_RSEM              101
+#define ENET_RAEM              102
+#define ENET_RAFL              103
+#define ENET_TSEM              104
+#define ENET_TAEM              105
+#define ENET_TAFL              106
+#define ENET_TIPG              107
+#define ENET_FTRL              108
+#define ENET_TACC              112
+#define ENET_RACC              113
 #define ENET_MIIGSK_CFGR       192
 #define ENET_MIIGSK_ENR        194
+#define ENET_ATCR              256
+#define ENET_ATVR              257
+#define ENET_ATOFF             258
+#define ENET_ATPER             259
+#define ENET_ATCOR             260
+#define ENET_ATINC             261
+#define ENET_ATSTMP            262
+#define ENET_TGSR              385
+#define ENET_TCSR0             386
+#define ENET_TCCR0             387
+#define ENET_TCSR1             388
+#define ENET_TCCR1             389
+#define ENET_TCSR2             390
+#define ENET_TCCR2             391
+#define ENET_TCSR3             392
+#define ENET_TCCR3             393
 #define ENET_MAX               400
 
-#define FEC_MAX_FRAME_SIZE 2032
-
-#define FEC_INT_HB      (1 << 31)
-#define FEC_INT_BABR    (1 << 30)
-#define FEC_INT_BABT    (1 << 29)
-#define FEC_INT_GRA     (1 << 28)
-#define FEC_INT_TXF     (1 << 27)
-#define FEC_INT_TXB     (1 << 26)
-#define FEC_INT_RXF     (1 << 25)
-#define FEC_INT_RXB     (1 << 24)
-#define FEC_INT_MII     (1 << 23)
-#define FEC_INT_EBERR   (1 << 22)
-#define FEC_INT_LC      (1 << 21)
-#define FEC_INT_RL      (1 << 20)
-#define FEC_INT_UN      (1 << 19)
+#define ENET_MAX_FRAME_SIZE    2032
+
+/* EIR and EIMR */
+#define ENET_INT_HB            (1 << 31)
+#define ENET_INT_BABR          (1 << 30)
+#define ENET_INT_BABT          (1 << 29)
+#define ENET_INT_GRA           (1 << 28)
+#define ENET_INT_TXF           (1 << 27)
+#define ENET_INT_TXB           (1 << 26)
+#define ENET_INT_RXF           (1 << 25)
+#define ENET_INT_RXB           (1 << 24)
+#define ENET_INT_MII           (1 << 23)
+#define ENET_INT_EBERR         (1 << 22)
+#define ENET_INT_LC            (1 << 21)
+#define ENET_INT_RL            (1 << 20)
+#define ENET_INT_UN            (1 << 19)
+#define ENET_INT_PLR           (1 << 18)
+#define ENET_INT_WAKEUP        (1 << 17)
+#define ENET_INT_TS_AVAIL      (1 << 16)
+#define ENET_INT_TS_TIMER      (1 << 15)
 
 /* RDAR */
 #define ENET_RDAR_RDAR         (1 << 24)
@@ -79,8 +111,54 @@ 
 /* TDAR */
 #define ENET_TDAR_TDAR         (1 << 24)
 
-#define FEC_EN                 (1 << 1)
-#define FEC_RESET              (1 << 0)
+/* ECR */
+#define ENET_ECR_RESET         (1 << 0)
+#define ENET_ECR_ETHEREN       (1 << 1)
+#define ENET_ECR_MAGICEN       (1 << 2)
+#define ENET_ECR_SLEEP         (1 << 3)
+#define ENET_ECR_EN1588        (1 << 4)
+#define ENET_ECR_SPEED         (1 << 5)
+#define ENET_ECR_DBGEN         (1 << 6)
+#define ENET_ECR_STOPEN        (1 << 7)
+#define ENET_ECR_DSBWP         (1 << 8)
+
+/* MIBC */
+#define ENET_MIBC_MIB_DIS      (1 << 31)
+#define ENET_MIBC_MIB_IDLE     (1 << 30)
+#define ENET_MIBC_MIB_CLEAR    (1 << 29)
+
+/* RCR */
+#define ENET_RCR_LOOP          (1 << 0)
+#define ENET_RCR_DRT           (1 << 1)
+#define ENET_RCR_MII_MODE      (1 << 2)
+#define ENET_RCR_PROM          (1 << 3)
+#define ENET_RCR_BC_REJ        (1 << 4)
+#define ENET_RCR_FCE           (1 << 5)
+#define ENET_RCR_RGMII_EN      (1 << 6)
+#define ENET_RCR_RMII_MODE     (1 << 8)
+#define ENET_RCR_RMII_10T      (1 << 9)
+#define ENET_RCR_PADEN         (1 << 12)
+#define ENET_RCR_PAUFWD        (1 << 13)
+#define ENET_RCR_CRCFWD        (1 << 14)
+#define ENET_RCR_CFEN          (1 << 15)
+#define ENET_RCR_MAX_FL_SHIFT  (16)
+#define ENET_RCR_MAX_FL_LENGTH (14)
+#define ENET_RCR_NLC           (1 << 30)
+#define ENET_RCR_GRS           (1 << 31)
+
+/* TCR */
+#define ENET_TCR_GTS           (1 << 0)
+#define ENET_TCR_FDEN          (1 << 2)
+#define ENET_TCR_TFC_PAUSE     (1 << 3)
+#define ENET_TCR_RFC_PAUSE     (1 << 4)
+#define ENET_TCR_ADDSEL_SHIFT  (5)
+#define ENET_TCR_ADDSEL_LENGTH (3)
+#define ENET_TCR_CRCFWD        (1 << 9)
+
+/* RDSR */
+#define ENET_TWFR_TFWR_SHIFT   (0)
+#define ENET_TWFR_TFWR_LENGTH  (6)
+#define ENET_TWFR_STRFWD       (1 << 8)
 
 /* Buffer Descriptor.  */
 typedef struct {
@@ -89,22 +167,60 @@  typedef struct {
     uint32_t data;
 } IMXFECBufDesc;
 
-#define FEC_BD_R    (1 << 15)
-#define FEC_BD_E    (1 << 15)
-#define FEC_BD_O1   (1 << 14)
-#define FEC_BD_W    (1 << 13)
-#define FEC_BD_O2   (1 << 12)
-#define FEC_BD_L    (1 << 11)
-#define FEC_BD_TC   (1 << 10)
-#define FEC_BD_ABC  (1 << 9)
-#define FEC_BD_M    (1 << 8)
-#define FEC_BD_BC   (1 << 7)
-#define FEC_BD_MC   (1 << 6)
-#define FEC_BD_LG   (1 << 5)
-#define FEC_BD_NO   (1 << 4)
-#define FEC_BD_CR   (1 << 2)
-#define FEC_BD_OV   (1 << 1)
-#define FEC_BD_TR   (1 << 0)
+#define ENET_BD_R              (1 << 15)
+#define ENET_BD_E              (1 << 15)
+#define ENET_BD_O1             (1 << 14)
+#define ENET_BD_W              (1 << 13)
+#define ENET_BD_O2             (1 << 12)
+#define ENET_BD_L              (1 << 11)
+#define ENET_BD_TC             (1 << 10)
+#define ENET_BD_ABC            (1 << 9)
+#define ENET_BD_M              (1 << 8)
+#define ENET_BD_BC             (1 << 7)
+#define ENET_BD_MC             (1 << 6)
+#define ENET_BD_LG             (1 << 5)
+#define ENET_BD_NO             (1 << 4)
+#define ENET_BD_CR             (1 << 2)
+#define ENET_BD_OV             (1 << 1)
+#define ENET_BD_TR             (1 << 0)
+
+typedef struct {
+    uint16_t length;
+    uint16_t flags;
+    uint32_t data;
+    uint16_t status;
+    uint16_t option;
+    uint16_t checksum;
+    uint16_t head_proto;
+    uint32_t last_buffer;
+    uint32_t timestamp;
+    uint32_t reserved[2];
+} IMXENETBufDesc;
+
+#define ENET_BD_ME             (1 << 15)
+#define ENET_BD_TX_INT         (1 << 14)
+#define ENET_BD_TS             (1 << 13)
+#define ENET_BD_PINS           (1 << 12)
+#define ENET_BD_IINS           (1 << 11)
+#define ENET_BD_PE             (1 << 10)
+#define ENET_BD_CE             (1 << 9)
+#define ENET_BD_UC             (1 << 8)
+#define ENET_BD_RX_INT         (1 << 7)
+
+#define ENET_BD_TXE            (1 << 15)
+#define ENET_BD_UE             (1 << 13)
+#define ENET_BD_EE             (1 << 12)
+#define ENET_BD_FE             (1 << 11)
+#define ENET_BD_LCE            (1 << 10)
+#define ENET_BD_OE             (1 << 9)
+#define ENET_BD_TSE            (1 << 8)
+#define ENET_BD_ICE            (1 << 5)
+#define ENET_BD_PCR            (1 << 4)
+#define ENET_BD_VLAN           (1 << 2)
+#define ENET_BD_IPV6           (1 << 1)
+#define ENET_BD_FRAG           (1 << 0)
+
+#define ENET_BD_BDU            (1 << 31)
 
 typedef struct IMXFECState {
     /*< private >*/
@@ -113,7 +229,7 @@  typedef struct IMXFECState {
     /*< public >*/
     NICState *nic;
     NICConf conf;
-    qemu_irq irq;
+    qemu_irq irq[2];
     MemoryRegion iomem;
 
     uint32_t regs[ENET_MAX];
@@ -125,6 +241,8 @@  typedef struct IMXFECState {
     uint32_t phy_advertise;
     uint32_t phy_int;
     uint32_t phy_int_mask;
+
+    bool is_fec;
 } IMXFECState;
 
 #endif