Patchwork [2/2] Add Enhanced Three-Speed Ethernet Controller (eTSEC)

login
register
mail settings
Submitter Fabien Chouteau
Date July 10, 2013, 5:10 p.m.
Message ID <1373476202-11277-3-git-send-email-chouteau@adacore.com>
Download mbox | patch
Permalink /patch/258165/
State New
Headers show

Comments

Fabien Chouteau - July 10, 2013, 5:10 p.m.
This implementation doesn't include ring priority, TCP/IP Off-Load, QoS.

Signed-off-by: Fabien Chouteau <chouteau@adacore.com>
---
 default-configs/ppc-softmmu.mak |    1 +
 hw/net/Makefile.objs            |    1 +
 hw/net/etsec.c                  |  472 +++++++++++++++++++++++++++
 hw/net/etsec.h                  |  169 ++++++++++
 hw/net/etsec_miim.c             |  146 +++++++++
 hw/net/etsec_registers.c        |  295 +++++++++++++++++
 hw/net/etsec_registers.h        |  302 ++++++++++++++++++
 hw/net/etsec_rings.c            |  673 +++++++++++++++++++++++++++++++++++++++
 8 files changed, 2059 insertions(+)
 create mode 100644 hw/net/etsec.c
 create mode 100644 hw/net/etsec.h
 create mode 100644 hw/net/etsec_miim.c
 create mode 100644 hw/net/etsec_registers.c
 create mode 100644 hw/net/etsec_registers.h
 create mode 100644 hw/net/etsec_rings.c
Peter Crosthwaite - July 15, 2013, 2 a.m.
Hi Fabien,

On Thu, Jul 11, 2013 at 3:10 AM, Fabien Chouteau <chouteau@adacore.com> wrote:
> This implementation doesn't include ring priority, TCP/IP Off-Load, QoS.
>recieve
> Signed-off-by: Fabien Chouteau <chouteau@adacore.com>
> ---
>  default-configs/ppc-softmmu.mak |    1 +
>  hw/net/Makefile.objs            |    1 +
>  hw/net/etsec.c                  |  472 +++++++++++++++++++++++++++
>  hw/net/etsec.h                  |  169 ++++++++++
>  hw/net/etsec_miim.c             |  146 +++++++++
>  hw/net/etsec_registers.c        |  295 +++++++++++++++++
>  hw/net/etsec_registers.h        |  302 ++++++++++++++++++
>  hw/net/etsec_rings.c            |  673 +++++++++++++++++++++++++++++++++++++++
>  8 files changed, 2059 insertions(+)
>  create mode 100644 hw/net/etsec.c
>  create mode 100644 hw/net/etsec.h
>  create mode 100644 hw/net/etsec_miim.c
>  create mode 100644 hw/net/etsec_registers.c
>  create mode 100644 hw/net/etsec_registers.h
>  create mode 100644 hw/net/etsec_rings.c
>
> diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak
> index 73e4cc5..c7541cf 100644
> --- a/default-configs/ppc-softmmu.mak
> +++ b/default-configs/ppc-softmmu.mak
> @@ -46,3 +46,4 @@ CONFIG_E500=y
>  CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM))
>  # For PReP
>  CONFIG_MC146818RTC=y
> +CONFIG_ETSEC=y
> diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
> index 951cca3..ca03c3f 100644
> --- a/hw/net/Makefile.objs
> +++ b/hw/net/Makefile.objs
> @@ -28,6 +28,7 @@ obj-$(CONFIG_COLDFIRE) += mcf_fec.o
>  obj-$(CONFIG_MILKYMIST) += milkymist-minimac2.o
>  obj-$(CONFIG_PSERIES) += spapr_llan.o
>  obj-$(CONFIG_XILINX_ETHLITE) += xilinx_ethlite.o
> +obj-$(CONFIG_ETSEC) += etsec.o etsec_registers.o etsec_rings.o etsec_miim.o
>
>  obj-$(CONFIG_VIRTIO) += virtio-net.o
>  obj-y += vhost_net.o
> diff --git a/hw/net/etsec.c b/hw/net/etsec.c
> new file mode 100644
> index 0000000..4516b01
> --- /dev/null
> +++ b/hw/net/etsec.c
> @@ -0,0 +1,472 @@
> +/*
> + * QEMU Freescale eTSEC Emulator
> + *
> + * Copyright (c) 2011-2013 AdaCore
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#include "sysemu/sysemu.h"
> +#include "hw/sysbus.h"
> +#include "trace.h"
> +#include "hw/ptimer.h"
> +#include "hw/net/etsec.h"
> +#include "hw/net/etsec_registers.h"
> +
> +/* #define HEX_DUMP */
> +/* #define DEBUG_REGISTER */
> +
> +static uint64_t etsec_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    eTSEC          *etsec     = opaque;
> +    uint32_t        reg_index = addr / 4;
> +    eTSEC_Register *reg       = NULL;
> +    uint32_t        ret       = 0x0;
> +
> +    assert(reg_index < REG_NUMBER);
> +
> +    reg = &etsec->regs[reg_index];
> +
> +
> +    switch (reg->access) {
> +    case ACC_WO:
> +        ret = 0x00000000;

worth a qemu_log_mask(LOG_GUEST_ERROR ?

> +        break;
> +
> +    case ACC_RW:
> +    case ACC_w1c:
> +    case ACC_RO:
> +    default:
> +        ret = reg->value;
> +        break;
> +    }
> +
> +#ifdef DEBUG_REGISTER
> +    printf("Read  0x%08x @ 0x" TARGET_FMT_plx
> +           "                            : %s (%s)\n",
> +           ret, addr, reg->name, reg->desc);
> +#endif
> +
> +    return ret;
> +}
> +
> +static void write_tstat(eTSEC          *etsec,
> +                        eTSEC_Register *reg,
> +                        uint32_t        reg_index,
> +                        uint32_t        value)
> +{
> +    int i = 0;
> +
> +    for (i = 0; i < 8; i++) {
> +        /* Check THLTi flag in TSTAT */
> +        if (value & (1 << (31 - i))) {
> +            walk_tx_ring(etsec, i);
> +        }
> +    }
> +
> +    /* Write 1 to clear */
> +    reg->value &= ~value;
> +}
> +
> +static void write_rstat(eTSEC          *etsec,
> +                        eTSEC_Register *reg,
> +                        uint32_t        reg_index,
> +                        uint32_t        value)
> +{
> +    int i = 0;
> +
> +    for (i = 0; i < 8; i++) {
> +        /* Check QHLTi flag in RSTAT */
> +        if (value & (1 << (23 - i)) && !(reg->value & (1 << (23 - i)))) {
> +            walk_rx_ring(etsec, i);
> +        }
> +    }
> +
> +    /* Write 1 to clear */
> +    reg->value &= ~value;
> +}
> +
> +static void write_tbasex(eTSEC          *etsec,
> +                         eTSEC_Register *reg,
> +                         uint32_t        reg_index,
> +                         uint32_t        value)
> +{
> +    reg->value = value & ~0x7;
> +
> +    /* Copy this value in the ring's TxBD pointer */
> +    etsec->regs[TBPTR0 + (reg_index - TBASE0)].value = value & ~0x7;
> +}
> +
> +static void write_rbasex(eTSEC          *etsec,
> +                         eTSEC_Register *reg,
> +                         uint32_t        reg_index,
> +                         uint32_t        value)
> +{
> +    reg->value = value & ~0x7;
> +
> +    /* Copy this value in the ring's RxBD pointer */
> +    etsec->regs[RBPTR0 + (reg_index - RBASE0)].value = value & ~0x7;
> +}
> +
> +static void write_ievent(eTSEC          *etsec,
> +                         eTSEC_Register *reg,
> +                         uint32_t        reg_index,
> +                         uint32_t        value)
> +{
> +    if (value & IEVENT_TXF) {
> +        qemu_irq_lower(etsec->tx_irq);
> +    }
> +    if (value & IEVENT_RXF) {
> +        qemu_irq_lower(etsec->rx_irq);
> +    }
> +
> +    /* Write 1 to clear */
> +    reg->value &= ~value;
> +}
> +
> +static void write_dmactrl(eTSEC          *etsec,
> +                          eTSEC_Register *reg,
> +                          uint32_t        reg_index,
> +                          uint32_t        value)
> +{
> +    reg->value = value;
> +
> +    if (value & DMACTRL_GRS) {
> +
> +        if (etsec->rx_buffer_len != 0) {
> +            /* Graceful receive stop delayed until end of frame */
> +        } else {
> +            /* Graceful receive stop now */
> +            etsec->regs[IEVENT].value |= IEVENT_GRSC;
> +            if (etsec->regs[IMASK].value & IMASK_GRSCEN) {
> +                qemu_irq_raise(etsec->err_irq);
> +            }
> +        }
> +    }
> +
> +    if (value & DMACTRL_GTS) {
> +
> +        if (etsec->tx_buffer_len != 0) {
> +            /* Graceful transmit stop delayed until end of frame */
> +        } else {
> +            /* Graceful transmit stop now */
> +            etsec->regs[IEVENT].value |= IEVENT_GTSC;
> +            if (etsec->regs[IMASK].value & IMASK_GTSCEN) {
> +                qemu_irq_raise(etsec->err_irq);
> +            }
> +        }
> +    }
> +
> +    if (!(value & DMACTRL_WOP)) {
> +        /* Start polling */
> +        ptimer_stop(etsec->ptimer);
> +        ptimer_set_count(etsec->ptimer, 1);
> +        ptimer_run(etsec->ptimer, 1);
> +    }
> +}
> +
> +static void etsec_write(void     *opaque,
> +                        hwaddr    addr,
> +                        uint64_t  value,
> +                        unsigned  size)
> +{
> +    eTSEC          *etsec     = opaque;
> +    uint32_t        reg_index = addr / 4;
> +    eTSEC_Register *reg       = NULL;
> +    uint32_t        before    = 0x0;
> +
> +    assert(reg_index < REG_NUMBER);
> +
> +    reg = &etsec->regs[reg_index];
> +    before = reg->value;
> +
> +    switch (reg_index) {
> +    case IEVENT:
> +        write_ievent(etsec, reg, reg_index, value);
> +        break;
> +
> +    case DMACTRL:
> +        write_dmactrl(etsec, reg, reg_index, value);
> +        break;
> +
> +    case TSTAT:
> +        write_tstat(etsec, reg, reg_index, value);
> +        break;
> +
> +    case RSTAT:
> +        write_rstat(etsec, reg, reg_index, value);
> +        break;
> +
> +    case TBASE0 ... TBASE7:
> +        write_tbasex(etsec, reg, reg_index, value);
> +        break;
> +
> +    case RBASE0 ... RBASE7:
> +        write_rbasex(etsec, reg, reg_index, value);
> +        break;
> +
> +    case MIIMCFG ... MIIMIND:
> +        write_miim(etsec, reg, reg_index, value);
> +        break;
> +
> +    default:
> +        /* Default handling */
> +        switch (reg->access) {
> +
> +        case ACC_RW:
> +        case ACC_WO:
> +            reg->value = value;
> +            break;
> +
> +        case ACC_w1c:
> +            reg->value &= ~value;
> +            break;
> +
> +        case ACC_RO:
> +        default:
> +            /* Read Only or Unknown register */
> +            break;
> +        }
> +    }
> +
> +#ifdef DEBUG_REGISTER
> +    printf("Write 0x%08x @ 0x" TARGET_FMT_plx" val:0x%08x->0x%08x : %s (%s)\n",
> +           (unsigned int)value, addr, before, reg->value, reg->name, reg->desc);

Last I knew, printf was a bad idea for error messages due to monitor
interference issue and nographic mode. But qemu_log or fprintf(stderr,
are both better alternatives.

> +#else
> +    (void)before; /* Unreferenced */
> +#endif
> +
> +}
> +
> +static const MemoryRegionOps etsec_ops = {
> +    .read = etsec_read,
> +    .write = etsec_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +    .impl = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +};
> +
> +static void etsec_timer_hit(void *opaque)
> +{
> +    eTSEC *etsec = opaque;
> +
> +    ptimer_stop(etsec->ptimer);
> +
> +    if (!(etsec->regs[DMACTRL].value & DMACTRL_WOP)) {
> +
> +        if (!(etsec->regs[DMACTRL].value & DMACTRL_GTS)) {
> +            walk_tx_ring(etsec, 0);
> +        }
> +        ptimer_set_count(etsec->ptimer, 1);
> +        ptimer_run(etsec->ptimer, 1);
> +    }
> +}
> +
> +static void etsec_reset(DeviceState *d)
> +{
> +    eTSEC *etsec = container_of(d, eTSEC, busdev.qdev);
> +    int i = 0;
> +    int reg_index = 0;
> +
> +    /* Default value for all registers */
> +    for (i = 0; i < REG_NUMBER; i++) {
> +        etsec->regs[i].name   = "Reserved";
> +        etsec->regs[i].desc   = "";
> +        etsec->regs[i].access = ACC_UNKNOWN;
> +        etsec->regs[i].value  = 0x00000000;
> +    }
> +
> +    /* Set-up known registers */
> +    for (i = 0; eTSEC_registers_def[i].name != NULL; i++) {
> +
> +        reg_index = eTSEC_registers_def[i].offset / 4;
> +
> +        etsec->regs[reg_index].name   = eTSEC_registers_def[i].name;
> +        etsec->regs[reg_index].desc   = eTSEC_registers_def[i].desc;
> +        etsec->regs[reg_index].access = eTSEC_registers_def[i].access;
> +        etsec->regs[reg_index].value  = eTSEC_registers_def[i].reset;
> +    }
> +
> +    etsec->tx_buffer     = NULL;
> +    etsec->tx_buffer_len = 0;
> +    etsec->rx_buffer     = NULL;
> +    etsec->rx_buffer_len = 0;
> +
> +    etsec->phy_status =
> +        MII_SR_EXTENDED_CAPS    | MII_SR_LINK_STATUS   | MII_SR_AUTONEG_CAPS  |
> +        MII_SR_AUTONEG_COMPLETE | MII_SR_PREAMBLE_SUPPRESS |
> +        MII_SR_EXTENDED_STATUS  | MII_SR_100T2_HD_CAPS | MII_SR_100T2_FD_CAPS |
> +        MII_SR_10T_HD_CAPS      | MII_SR_10T_FD_CAPS   | MII_SR_100X_HD_CAPS  |
> +        MII_SR_100X_FD_CAPS     | MII_SR_100T4_CAPS;
> +}
> +
> +static void etsec_cleanup(NetClientState *nc)
> +{
> +    /* printf("eTSEC cleanup\n"); */
> +}
> +
> +static int etsec_can_receive(NetClientState *nc)
> +{
> +    /* Yes we always can\ */
> +    return 1;

As a general rule this is a bad idea. Multiple ethernet controllers in
QEMU have tried this and had issues (particularly with the UBOOT
bootloader) with mass packet droppage. But You have access to the
information needed (the if conditions in rx_ring_write) to implement
this it seems.

> +}
> +
> +#ifdef HEX_DUMP
> +static void hex_dump(FILE *f, const uint8_t *buf, int size)
> +{
> +    int len, i, j, c;
> +
> +    for (i = 0; i < size; i += 16) {
> +        len = size - i;
> +        if (len > 16) {
> +            len = 16;
> +        }
> +        fprintf(f, "%08x ", i);
> +        for (j = 0; j < 16; j++) {
> +            if (j < len) {
> +                fprintf(f, " %02x", buf[i + j]);
> +            } else {
> +                fprintf(f, "   ");
> +            }
> +        }
> +        fprintf(f, " ");
> +        for (j = 0; j < len; j++) {
> +            c = buf[i + j];
> +            if (c < ' ' || c > '~') {
> +                c = '.';
> +            }
> +            fprintf(f, "%c", c);
> +        }
> +        fprintf(f, "\n");
> +    }
> +}
> +#endif
> +
> +static ssize_t etsec_receive(NetClientState *nc,
> +                             const uint8_t  *buf,
> +                             size_t          size)
> +{
> +    eTSEC *etsec = qemu_get_nic_opaque(nc);
> +
> +#if defined(HEX_DUMP)
> +    fprintf(stderr, "%s receive size:%d\n", etsec->nic->nc.name, size);
> +    hex_dump(stderr, buf, size);
> +#endif
> +    rx_ring_write(etsec, buf, size);
> +    return size;
> +}
> +
> +
> +static void etsec_set_link_status(NetClientState *nc)
> +{
> +    eTSEC *etsec = qemu_get_nic_opaque(nc);
> +
> +    miim_link_status(etsec, nc);
> +}
> +
> +static NetClientInfo net_etsec_info = {
> +    .type = NET_CLIENT_OPTIONS_KIND_NIC,
> +    .size = sizeof(NICState),
> +    .can_receive = etsec_can_receive,
> +    .receive = etsec_receive,
> +    .cleanup = etsec_cleanup,
> +    .link_status_changed = etsec_set_link_status,
> +};
> +
> +static int etsec_init(SysBusDevice *dev)
> +{
> +    eTSEC *etsec = FROM_SYSBUS(typeof(*etsec), dev);

Define and use QOM cast macros instead, FROM_FOO macros are deprecated.

> +
> +    memory_region_init_io(&etsec->io_area, OBJECT(etsec), &etsec_ops, etsec,
> +                          "eTSEC", 0x1000);

Constant size memory_region_init_io should be migrated to the Object::Init fm.

> +
> +    sysbus_init_mmio(dev, &etsec->io_area);
> +
> +    sysbus_init_irq(dev, &etsec->tx_irq);
> +    sysbus_init_irq(dev, &etsec->rx_irq);
> +    sysbus_init_irq(dev, &etsec->err_irq);
> +
> +    etsec->nic = qemu_new_nic(&net_etsec_info, &etsec->conf,
> +                              "eTSEC", etsec->busdev.qdev.id, etsec);
> +    qemu_format_nic_info_str(qemu_get_queue(etsec->nic), etsec->conf.macaddr.a);
> +
> +
> +    etsec->bh     = qemu_bh_new(etsec_timer_hit, etsec);
> +    etsec->ptimer = ptimer_init(etsec->bh);
> +    ptimer_set_freq(etsec->ptimer, 100);
> +
> +    return 0;
> +}
> +
> +static Property etsec_properties[] = {
> +    DEFINE_NIC_PROPERTIES(eTSEC, conf),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void etsec_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +
> +    k->init = etsec_init;

SysBusDevice::init in depracated. Please use Device::realize instead.

> +    dc->reset = etsec_reset;
> +    dc->props = etsec_properties;
> +}
> +
> +static TypeInfo etsec_info = {
> +    .name          = "eTSEC",
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(eTSEC),
> +    .class_init    = etsec_class_init,
> +};
> +
> +static void etsec_register_types(void)
> +{
> +    type_register_static(&etsec_info);
> +}
> +
> +type_init(etsec_register_types)
> +
> +DeviceState *etsec_create(hwaddr         base,
> +                          MemoryRegion * mr,
> +                          NICInfo      * nd,
> +                          qemu_irq       tx_irq,
> +                          qemu_irq       rx_irq,
> +                          qemu_irq       err_irq)
> +{
> +    DeviceState *dev;
> +
> +    dev = qdev_create(NULL, "eTSEC");
> +    qdev_set_nic_properties(dev, nd);
> +
> +    if (qdev_init(dev)) {
> +        return NULL;
> +    }
> +
> +    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, tx_irq);
> +    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, rx_irq);
> +    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, err_irq);
> +
> +    memory_region_add_subregion(mr, base,
> +                                SYS_BUS_DEVICE(dev)->mmio[0].memory);
> +
> +    return dev;
> +}
> diff --git a/hw/net/etsec.h b/hw/net/etsec.h
> new file mode 100644
> index 0000000..4fa9edc
> --- /dev/null
> +++ b/hw/net/etsec.h
> @@ -0,0 +1,169 @@
> +/*
> + * QEMU Freescale eTSEC Emulator
> + *
> + * Copyright (c) 2011-2013 AdaCore
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +#ifndef _ETSEC_H_
> +#define _ETSEC_H_
> +
> +#include "hw/qdev.h"
> +#include "hw/sysbus.h"
> +#include "net/net.h"
> +#include "hw/ptimer.h"
> +
> +/* Buffer Descriptors */
> +
> +typedef struct eTSEC_rxtx_bd {
> +    uint16_t flags;
> +    uint16_t length;
> +    uint32_t bufptr;
> +} eTSEC_rxtx_bd;
> +
> +#define BD_WRAP       (1 << 13)
> +#define BD_INTERRUPT  (1 << 12)
> +#define BD_LAST       (1 << 11)
> +
> +#define BD_TX_READY     (1 << 15)
> +#define BD_TX_PADCRC    (1 << 14)
> +#define BD_TX_TC        (1 << 10)
> +#define BD_TX_PREDEF    (1 << 9)
> +#define BD_TX_HFELC     (1 << 7)
> +#define BD_TX_CFRL      (1 << 6)
> +#define BD_TX_RC_MASK   0xF
> +#define BD_TX_RC_OFFSET 0x2
> +#define BD_TX_TOEUN     (1 << 1)
> +#define BD_TX_TR        (1 << 0)
> +
> +#define BD_RX_EMPTY     (1 << 15)
> +#define BD_RX_RO1       (1 << 14)
> +#define BD_RX_FIRST     (1 << 10)
> +#define BD_RX_MISS      (1 << 8)
> +#define BD_RX_BROADCAST (1 << 7)
> +#define BD_RX_MULTICAST (1 << 6)
> +#define BD_RX_LG        (1 << 5)
> +#define BD_RX_NO        (1 << 4)
> +#define BD_RX_SH        (1 << 3)
> +#define BD_RX_CR        (1 << 2)
> +#define BD_RX_OV        (1 << 1)
> +#define BD_RX_TR        (1 << 0)
> +
> +/* Tx FCB flags */
> +#define FCB_TX_VLN     (1 << 7)
> +#define FCB_TX_IP      (1 << 6)
> +#define FCB_TX_IP6     (1 << 5)
> +#define FCB_TX_TUP     (1 << 4)
> +#define FCB_TX_UDP     (1 << 3)
> +#define FCB_TX_CIP     (1 << 2)
> +#define FCB_TX_CTU     (1 << 1)
> +#define FCB_TX_NPH     (1 << 0)
> +
> +/* PHY Status Register */
> +#define MII_SR_EXTENDED_CAPS     0x0001    /* Extended register capabilities */
> +#define MII_SR_JABBER_DETECT     0x0002    /* Jabber Detected */
> +#define MII_SR_LINK_STATUS       0x0004    /* Link Status 1 = link */
> +#define MII_SR_AUTONEG_CAPS      0x0008    /* Auto Neg Capable */
> +#define MII_SR_REMOTE_FAULT      0x0010    /* Remote Fault Detect */
> +#define MII_SR_AUTONEG_COMPLETE  0x0020    /* Auto Neg Complete */
> +#define MII_SR_PREAMBLE_SUPPRESS 0x0040    /* Preamble may be suppressed */
> +#define MII_SR_EXTENDED_STATUS   0x0100    /* Ext. status info in Reg 0x0F */
> +#define MII_SR_100T2_HD_CAPS     0x0200    /* 100T2 Half Duplex Capable */
> +#define MII_SR_100T2_FD_CAPS     0x0400    /* 100T2 Full Duplex Capable */
> +#define MII_SR_10T_HD_CAPS       0x0800    /* 10T   Half Duplex Capable */
> +#define MII_SR_10T_FD_CAPS       0x1000    /* 10T   Full Duplex Capable */
> +#define MII_SR_100X_HD_CAPS      0x2000    /* 100X  Half Duplex Capable */
> +#define MII_SR_100X_FD_CAPS      0x4000    /* 100X  Full Duplex Capable */
> +#define MII_SR_100T4_CAPS        0x8000    /* 100T4 Capable */
> +
> +/* eTSEC */
> +
> +#define REG_NUMBER 1024
> +
> +typedef struct eTSEC_Register {
> +    const char *name;
> +    const char *desc;
> +    uint32_t    access;
> +    uint32_t    value;
> +} eTSEC_Register;
> +
> +typedef struct eTSEC {
> +    SysBusDevice  busdev;
> +
> +    MemoryRegion  io_area;
> +
> +    eTSEC_Register regs[REG_NUMBER];
> +
> +    NICState *nic;
> +    NICConf   conf;
> +
> +    /* Tx */
> +
> +    uint8_t       *tx_buffer;
> +    uint32_t       tx_buffer_len;
> +    eTSEC_rxtx_bd  first_bd;
> +
> +    /* Rx */
> +
> +    uint8_t       *rx_buffer;
> +    uint32_t       rx_buffer_len;
> +    uint32_t       rx_remaining_data;
> +    uint8_t        rx_first_in_frame;
> +    uint8_t        rx_fcb_size;
> +    eTSEC_rxtx_bd  rx_first_bd;
> +    uint8_t        rx_fcb[10];
> +    uint32_t       rx_padding;
> +
> +    /* IRQs */
> +    qemu_irq     tx_irq;
> +    qemu_irq     rx_irq;
> +    qemu_irq     err_irq;
> +
> +
> +    uint16_t phy_status;
> +    uint16_t phy_control;
> +
> +    /* Polling */
> +    QEMUBH *bh;
> +    struct ptimer_state *ptimer;
> +
> +} eTSEC;
> +
> +#define eTSEC_TRANSMIT 1
> +#define eTSEC_RECEIVE  2
> +
> +DeviceState *etsec_create(hwaddr        base,
> +                          MemoryRegion *mr,
> +                          NICInfo      *nd,
> +                          qemu_irq      tx_irq,
> +                          qemu_irq      rx_irq,
> +                          qemu_irq      err_irq);
> +
> +void walk_tx_ring(eTSEC *etsec, int ring_nbr);
> +void walk_rx_ring(eTSEC *etsec, int ring_nbr);
> +void rx_ring_write(eTSEC *etsec, const uint8_t *buf, size_t size);
> +
> +void write_miim(eTSEC          *etsec,
> +                eTSEC_Register *reg,
> +                uint32_t        reg_index,
> +                uint32_t        value);
> +
> +void miim_link_status(eTSEC *etsec, NetClientState *nc);
> +
> +#endif /* ! _ETSEC_H_ */
> diff --git a/hw/net/etsec_miim.c b/hw/net/etsec_miim.c
> new file mode 100644
> index 0000000..2bd9f0d
> --- /dev/null
> +++ b/hw/net/etsec_miim.c
> @@ -0,0 +1,146 @@
> +/*
> + * QEMU Freescale eTSEC Emulator
> + *
> + * Copyright (c) 2011-2013 AdaCore
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#include "etsec.h"
> +#include "etsec_registers.h"
> +
> +/* #define DEBUG_MIIM */
> +
> +#define MIIM_CONTROL    0
> +#define MIIM_STATUS     1
> +#define MIIM_PHY_ID_1   2
> +#define MIIM_PHY_ID_2   3
> +#define MIIM_T2_STATUS  10
> +#define MIIM_EXT_STATUS 15
> +
> +static void miim_read_cycle(eTSEC *etsec)
> +{
> +    uint8_t  phy;
> +    uint8_t  addr;
> +    uint16_t value;
> +
> +    phy  = (etsec->regs[MIIMADD].value >> 8) & 0x1F;
> +    (void)phy; /* Unreferenced */
> +    addr = etsec->regs[MIIMADD].value & 0x1F;
> +
> +    switch (addr) {
> +    case MIIM_CONTROL:
> +        value = etsec->phy_control;
> +        break;
> +    case MIIM_STATUS:
> +        value = etsec->phy_status;
> +        break;
> +    case MIIM_T2_STATUS:
> +        value = 0x1800;           /* Local and remote receivers OK */
> +        break;
> +    default:
> +        value = 0x0;
> +        break;
> +    };
> +
> +#ifdef DEBUG_MIIM
> +    printf("%s phy:%d addr:0x%x value:0x%x\n", __func__, phy, addr, value);
> +#endif
> +
> +    etsec->regs[MIIMSTAT].value = value;
> +}
> +
> +static void miim_write_cycle(eTSEC *etsec)
> +{
> +    uint8_t  phy;
> +    uint8_t  addr;
> +    uint16_t value;
> +
> +    phy   = (etsec->regs[MIIMADD].value >> 8) & 0x1F;
> +    (void)phy; /* Unreferenced */
> +    addr  = etsec->regs[MIIMADD].value & 0x1F;
> +    value = etsec->regs[MIIMCON].value & 0xffff;
> +
> +#ifdef DEBUG_MIIM
> +    printf("%s phy:%d addr:0x%x value:0x%x\n", __func__, phy, addr, value);
> +#endif
> +
> +    switch (addr) {
> +    case MIIM_CONTROL:
> +        etsec->phy_control = value & ~(0x8100);
> +        break;
> +    default:
> +        break;
> +    };
> +}
> +
> +void write_miim(eTSEC          *etsec,
> +               eTSEC_Register *reg,
> +               uint32_t        reg_index,
> +               uint32_t        value)
> +{
> +
> +    switch (reg_index) {
> +
> +    case MIIMCOM:
> +        /* Read and scan cycle */
> +
> +        if ((!(reg->value & MIIMCOM_READ)) && (value & MIIMCOM_READ)) {
> +            /* Read */
> +            miim_read_cycle(etsec);
> +        }
> +        reg->value = value;
> +        break;
> +
> +    case MIIMCON:
> +        reg->value = value & 0xffff;
> +        miim_write_cycle(etsec);
> +        break;
> +
> +    default:
> +        /* Default handling */
> +        switch (reg->access) {
> +
> +        case ACC_RW:
> +        case ACC_WO:
> +            reg->value = value;
> +            break;
> +
> +        case ACC_w1c:
> +            reg->value &= ~value;
> +            break;
> +
> +        case ACC_RO:
> +        default:
> +            /* Read Only or Unknown register */
> +            break;
> +        }
> +    }
> +
> +}
> +
> +void miim_link_status(eTSEC *etsec, NetClientState *nc)
> +{
> +    /* Set link status */
> +    if (nc->link_down) {
> +        etsec->phy_status &= ~MII_SR_LINK_STATUS;
> +    } else {
> +        etsec->phy_status |= MII_SR_LINK_STATUS;
> +    }
> +}
> diff --git a/hw/net/etsec_registers.c b/hw/net/etsec_registers.c
> new file mode 100644
> index 0000000..719a886
> --- /dev/null
> +++ b/hw/net/etsec_registers.c
> @@ -0,0 +1,295 @@
> +/*
> + * QEMU Freescale eTSEC Emulator
> + *
> + * Copyright (c) 2011-2013 AdaCore
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +#include "etsec_registers.h"
> +
> +const eTSEC_Register_Definition eTSEC_registers_def[] = {
> +{0x000, "TSEC_ID",  "Controller ID register",    ACC_RO,  0x01240000},
> +{0x004, "TSEC_ID2", "Controller ID register 2",  ACC_RO,  0x003000F0},
> +{0x010, "IEVENT",   "Interrupt event register",  ACC_w1c, 0x00000000},
> +{0x014, "IMASK",    "Interrupt mask register",   ACC_RW,  0x00000000},
> +{0x018, "EDIS",     "Error disabled register",   ACC_RW,  0x00000000},
> +{0x020, "ECNTRL",   "Ethernet control register", ACC_RW,  0x00000040},
> +{0x028, "PTV",      "Pause time value register", ACC_RW,  0x00000000},
> +{0x02C, "DMACTRL",  "DMA control register",      ACC_RW,  0x00000000},
> +{0x030, "TBIPA",    "TBI PHY address register",  ACC_RW,  0x00000000},
> +
> +/* eTSEC FIFO Control and Status Registers */
> +
> +{0x058, "FIFO_RX_ALARM",          "FIFO receive alarm start threshold register",    ACC_RW, 0x00000040},
> +{0x05C, "FIFO_RX_ALARM_SHUTOFF",  "FIFO receive alarm shut-off threshold register", ACC_RW, 0x00000080},
> +{0x08C, "FIFO_TX_THR",            "FIFO transmit threshold register",               ACC_RW, 0x00000080},
> +{0x098, "FIFO_TX_STARVE",         "FIFO transmit starve register",                  ACC_RW, 0x00000040},
> +{0x09C, "FIFO_TX_STARVE_SHUTOFF", "FIFO transmit starve shut-off register",         ACC_RW, 0x00000080},
> +
> +/* eTSEC Transmit Control and Status Registers */
> +
> +{0x100, "TCTRL",        "Transmit control register",                ACC_RW,  0x00000000},
> +{0x104, "TSTAT",        "Transmit status register",                 ACC_w1c, 0x00000000},
> +{0x108, "DFVLAN",       "Default VLAN control word",                ACC_RW,  0x81000000},
> +{0x110, "TXIC",         "Transmit interrupt coalescing register",   ACC_RW,  0x00000000},
> +{0x114, "TQUEUE",       "Transmit queue control register",          ACC_RW,  0x00008000},
> +{0x140, "TR03WT",       "TxBD Rings 0-3 round-robin weightings",    ACC_RW,  0x00000000},
> +{0x144, "TR47WT",       "TxBD Rings 4-7 round-robin weightings",    ACC_RW,  0x00000000},
> +{0x180, "TBDBPH",       "Tx data buffer pointer high bits",         ACC_RW,  0x00000000},
> +{0x184, "TBPTR0",       "TxBD pointer for ring 0",                  ACC_RW,  0x00000000},
> +{0x18C, "TBPTR1",       "TxBD pointer for ring 1",                  ACC_RW,  0x00000000},
> +{0x194, "TBPTR2",       "TxBD pointer for ring 2",                  ACC_RW,  0x00000000},
> +{0x19C, "TBPTR3",       "TxBD pointer for ring 3",                  ACC_RW,  0x00000000},
> +{0x1A4, "TBPTR4",       "TxBD pointer for ring 4",                  ACC_RW,  0x00000000},
> +{0x1AC, "TBPTR5",       "TxBD pointer for ring 5",                  ACC_RW,  0x00000000},
> +{0x1B4, "TBPTR6",       "TxBD pointer for ring 6",                  ACC_RW,  0x00000000},
> +{0x1BC, "TBPTR7",       "TxBD pointer for ring 7",                  ACC_RW,  0x00000000},
> +{0x200, "TBASEH",       "TxBD base address high bits",              ACC_RW,  0x00000000},
> +{0x204, "TBASE0",       "TxBD base address of ring 0",              ACC_RW,  0x00000000},
> +{0x20C, "TBASE1",       "TxBD base address of ring 1",              ACC_RW,  0x00000000},
> +{0x214, "TBASE2",       "TxBD base address of ring 2",              ACC_RW,  0x00000000},
> +{0x21C, "TBASE3",       "TxBD base address of ring 3",              ACC_RW,  0x00000000},
> +{0x224, "TBASE4",       "TxBD base address of ring 4",              ACC_RW,  0x00000000},
> +{0x22C, "TBASE5",       "TxBD base address of ring 5",              ACC_RW,  0x00000000},
> +{0x234, "TBASE6",       "TxBD base address of ring 6",              ACC_RW,  0x00000000},
> +{0x23C, "TBASE7",       "TxBD base address of ring 7",              ACC_RW,  0x00000000},
> +{0x280, "TMR_TXTS1_ID", "Tx time stamp identification tag (set 1)", ACC_RO,  0x00000000},
> +{0x284, "TMR_TXTS2_ID", "Tx time stamp identification tag (set 2)", ACC_RO,  0x00000000},
> +{0x2C0, "TMR_TXTS1_H",  "Tx time stamp high (set 1)",               ACC_RO,  0x00000000},
> +{0x2C4, "TMR_TXTS1_L",  "Tx time stamp high (set 1)",               ACC_RO,  0x00000000},
> +{0x2C8, "TMR_TXTS2_H",  "Tx time stamp high (set 2)",               ACC_RO,  0x00000000},
> +{0x2CC, "TMR_TXTS2_L",  "Tx time stamp high (set 2)",               ACC_RO,  0x00000000},
> +
> +/* eTSEC Receive Control and Status Registers */
> +
> +{0x300, "RCTRL",      "Receive control register",                     ACC_RW,  0x00000000},
> +{0x304, "RSTAT",      "Receive status register",                      ACC_w1c, 0x00000000},
> +{0x310, "RXIC",       "Receive interrupt coalescing register",        ACC_RW,  0x00000000},
> +{0x314, "RQUEUE",     "Receive queue control register.",              ACC_RW,  0x00800080},
> +{0x330, "RBIFX",      "Receive bit field extract control register",   ACC_RW,  0x00000000},
> +{0x334, "RQFAR",      "Receive queue filing table address register",  ACC_RW,  0x00000000},
> +{0x338, "RQFCR",      "Receive queue filing table control register",  ACC_RW,  0x00000000},
> +{0x33C, "RQFPR",      "Receive queue filing table property register", ACC_RW,  0x00000000},
> +{0x340, "MRBLR",      "Maximum receive buffer length register",       ACC_RW,  0x00000000},
> +{0x380, "RBDBPH",     "Rx data buffer pointer high bits",             ACC_RW,  0x00000000},
> +{0x384, "RBPTR0",     "RxBD pointer for ring 0",                      ACC_RW,  0x00000000},
> +{0x38C, "RBPTR1",     "RxBD pointer for ring 1",                      ACC_RW,  0x00000000},
> +{0x394, "RBPTR2",     "RxBD pointer for ring 2",                      ACC_RW,  0x00000000},
> +{0x39C, "RBPTR3",     "RxBD pointer for ring 3",                      ACC_RW,  0x00000000},
> +{0x3A4, "RBPTR4",     "RxBD pointer for ring 4",                      ACC_RW,  0x00000000},
> +{0x3AC, "RBPTR5",     "RxBD pointer for ring 5",                      ACC_RW,  0x00000000},
> +{0x3B4, "RBPTR6",     "RxBD pointer for ring 6",                      ACC_RW,  0x00000000},
> +{0x3BC, "RBPTR7",     "RxBD pointer for ring 7",                      ACC_RW,  0x00000000},
> +{0x400, "RBASEH",     "RxBD base address high bits",                  ACC_RW,  0x00000000},
> +{0x404, "RBASE0",     "RxBD base address of ring 0",                  ACC_RW,  0x00000000},
> +{0x40C, "RBASE1",     "RxBD base address of ring 1",                  ACC_RW,  0x00000000},
> +{0x414, "RBASE2",     "RxBD base address of ring 2",                  ACC_RW,  0x00000000},
> +{0x41C, "RBASE3",     "RxBD base address of ring 3",                  ACC_RW,  0x00000000},
> +{0x424, "RBASE4",     "RxBD base address of ring 4",                  ACC_RW,  0x00000000},
> +{0x42C, "RBASE5",     "RxBD base address of ring 5",                  ACC_RW,  0x00000000},
> +{0x434, "RBASE6",     "RxBD base address of ring 6",                  ACC_RW,  0x00000000},
> +{0x43C, "RBASE7",     "RxBD base address of ring 7",                  ACC_RW,  0x00000000},
> +{0x4C0, "TMR_RXTS_H", "Rx timer time stamp register high",            ACC_RW,  0x00000000},
> +{0x4C4, "TMR_RXTS_L", "Rx timer time stamp register low",             ACC_RW,  0x00000000},
> +
> +/* eTSEC MAC Registers */
> +
> +{0x500, "MACCFG1",     "MAC configuration register 1",          ACC_RW, 0x00000000},
> +{0x504, "MACCFG2",     "MAC configuration register 2",          ACC_RW, 0x00007000},
> +{0x508, "IPGIFG",      "Inter-packet/inter-frame gap register", ACC_RW, 0x40605060},
> +{0x50C, "HAFDUP",      "Half-duplex control",                   ACC_RW, 0x00A1F037},
> +{0x510, "MAXFRM",      "Maximum frame length",                  ACC_RW, 0x00000600},
> +{0x520, "MIIMCFG",     "MII management configuration",          ACC_RW, 0x00000007},
> +{0x524, "MIIMCOM",     "MII management command",                ACC_RW, 0x00000000},
> +{0x528, "MIIMADD",     "MII management address",                ACC_RW, 0x00000000},
> +{0x52C, "MIIMCON",     "MII management control",                ACC_WO, 0x00000000},
> +{0x530, "MIIMSTAT",    "MII management status",                 ACC_RO, 0x00000000},
> +{0x534, "MIIMIND",     "MII management indicator",              ACC_RO, 0x00000000},
> +{0x53C, "IFSTAT",      "Interface status",                      ACC_RO, 0x00000000},
> +{0x540, "MACSTNADDR1", "MAC station address register 1",        ACC_RW, 0x00000000},
> +{0x544, "MACSTNADDR2", "MAC station address register 2",        ACC_RW, 0x00000000},
> +{0x548, "MAC01ADDR1",  "MAC exact match address 1, part 1",     ACC_RW, 0x00000000},
> +{0x54C, "MAC01ADDR2",  "MAC exact match address 1, part 2",     ACC_RW, 0x00000000},
> +{0x550, "MAC02ADDR1",  "MAC exact match address 2, part 1",     ACC_RW, 0x00000000},
> +{0x554, "MAC02ADDR2",  "MAC exact match address 2, part 2",     ACC_RW, 0x00000000},
> +{0x558, "MAC03ADDR1",  "MAC exact match address 3, part 1",     ACC_RW, 0x00000000},
> +{0x55C, "MAC03ADDR2",  "MAC exact match address 3, part 2",     ACC_RW, 0x00000000},
> +{0x560, "MAC04ADDR1",  "MAC exact match address 4, part 1",     ACC_RW, 0x00000000},
> +{0x564, "MAC04ADDR2",  "MAC exact match address 4, part 2",     ACC_RW, 0x00000000},
> +{0x568, "MAC05ADDR1",  "MAC exact match address 5, part 1",     ACC_RW, 0x00000000},
> +{0x56C, "MAC05ADDR2",  "MAC exact match address 5, part 2",     ACC_RW, 0x00000000},
> +{0x570, "MAC06ADDR1",  "MAC exact match address 6, part 1",     ACC_RW, 0x00000000},
> +{0x574, "MAC06ADDR2",  "MAC exact match address 6, part 2",     ACC_RW, 0x00000000},
> +{0x578, "MAC07ADDR1",  "MAC exact match address 7, part 1",     ACC_RW, 0x00000000},
> +{0x57C, "MAC07ADDR2",  "MAC exact match address 7, part 2",     ACC_RW, 0x00000000},
> +{0x580, "MAC08ADDR1",  "MAC exact match address 8, part 1",     ACC_RW, 0x00000000},
> +{0x584, "MAC08ADDR2",  "MAC exact match address 8, part 2",     ACC_RW, 0x00000000},
> +{0x588, "MAC09ADDR1",  "MAC exact match address 9, part 1",     ACC_RW, 0x00000000},
> +{0x58C, "MAC09ADDR2",  "MAC exact match address 9, part 2",     ACC_RW, 0x00000000},
> +{0x590, "MAC10ADDR1",  "MAC exact match address 10, part 1",    ACC_RW, 0x00000000},
> +{0x594, "MAC10ADDR2",  "MAC exact match address 10, part 2",    ACC_RW, 0x00000000},
> +{0x598, "MAC11ADDR1",  "MAC exact match address 11, part 1",    ACC_RW, 0x00000000},
> +{0x59C, "MAC11ADDR2",  "MAC exact match address 11, part 2",    ACC_RW, 0x00000000},
> +{0x5A0, "MAC12ADDR1",  "MAC exact match address 12, part 1",    ACC_RW, 0x00000000},
> +{0x5A4, "MAC12ADDR2",  "MAC exact match address 12, part 2",    ACC_RW, 0x00000000},
> +{0x5A8, "MAC13ADDR1",  "MAC exact match address 13, part 1",    ACC_RW, 0x00000000},
> +{0x5AC, "MAC13ADDR2",  "MAC exact match address 13, part 2",    ACC_RW, 0x00000000},
> +{0x5B0, "MAC14ADDR1",  "MAC exact match address 14, part 1",    ACC_RW, 0x00000000},
> +{0x5B4, "MAC14ADDR2",  "MAC exact match address 14, part 2",    ACC_RW, 0x00000000},
> +{0x5B8, "MAC15ADDR1",  "MAC exact match address 15, part 1",    ACC_RW, 0x00000000},
> +{0x5BC, "MAC15ADDR2",  "MAC exact match address 15, part 2",    ACC_RW, 0x00000000},
> +
> +/* eTSEC, "Transmit", "and", Receive, Counters */
> +
> +{0x680, "TR64",  "Transmit and receive 64-byte frame counter ",                   ACC_RW, 0x00000000},
> +{0x684, "TR127", "Transmit and receive 65- to 127-byte frame counter",            ACC_RW, 0x00000000},
> +{0x688, "TR255", "Transmit and receive 128- to 255-byte frame counter",           ACC_RW, 0x00000000},
> +{0x68C, "TR511", "Transmit and receive 256- to 511-byte frame counter",           ACC_RW, 0x00000000},
> +{0x690, "TR1K",  "Transmit and receive 512- to 1023-byte frame counter",          ACC_RW, 0x00000000},
> +{0x694, "TRMAX", "Transmit and receive 1024- to 1518-byte frame counter",         ACC_RW, 0x00000000},
> +{0x698, "TRMGV", "Transmit and receive 1519- to 1522-byte good VLAN frame count", ACC_RW, 0x00000000},
> +
> +/* eTSEC Receive Counters */
> +
> +{0x69C, "RBYT", "Receive byte counter",                  ACC_RW, 0x00000000},
> +{0x6A0, "RPKT", "Receive packet counter",                ACC_RW, 0x00000000},
> +{0x6A4, "RFCS", "Receive FCS error counter",             ACC_RW, 0x00000000},
> +{0x6A8, "RMCA", "Receive multicast packet counter",      ACC_RW, 0x00000000},
> +{0x6AC, "RBCA", "Receive broadcast packet counter",      ACC_RW, 0x00000000},
> +{0x6B0, "RXCF", "Receive control frame packet counter ", ACC_RW, 0x00000000},
> +{0x6B4, "RXPF", "Receive PAUSE frame packet counter",    ACC_RW, 0x00000000},
> +{0x6B8, "RXUO", "Receive unknown OP code counter ",      ACC_RW, 0x00000000},
> +{0x6BC, "RALN", "Receive alignment error counter ",      ACC_RW, 0x00000000},
> +{0x6C0, "RFLR", "Receive frame length error counter ",   ACC_RW, 0x00000000},
> +{0x6C4, "RCDE", "Receive code error counter ",           ACC_RW, 0x00000000},
> +{0x6C8, "RCSE", "Receive carrier sense error counter",   ACC_RW, 0x00000000},
> +{0x6CC, "RUND", "Receive undersize packet counter",      ACC_RW, 0x00000000},
> +{0x6D0, "ROVR", "Receive oversize packet counter ",      ACC_RW, 0x00000000},
> +{0x6D4, "RFRG", "Receive fragments counter",             ACC_RW, 0x00000000},
> +{0x6D8, "RJBR", "Receive jabber counter ",               ACC_RW, 0x00000000},
> +{0x6DC, "RDRP", "Receive drop counter",                  ACC_RW, 0x00000000},
> +
> +/* eTSEC Transmit Counters */
> +
> +{0x6E0, "TBYT", "Transmit byte counter",                       ACC_RW, 0x00000000},
> +{0x6E4, "TPKT", "Transmit packet counter",                     ACC_RW, 0x00000000},
> +{0x6E8, "TMCA", "Transmit multicast packet counter ",          ACC_RW, 0x00000000},
> +{0x6EC, "TBCA", "Transmit broadcast packet counter ",          ACC_RW, 0x00000000},
> +{0x6F0, "TXPF", "Transmit PAUSE control frame counter ",       ACC_RW, 0x00000000},
> +{0x6F4, "TDFR", "Transmit deferral packet counter ",           ACC_RW, 0x00000000},
> +{0x6F8, "TEDF", "Transmit excessive deferral packet counter ", ACC_RW, 0x00000000},
> +{0x6FC, "TSCL", "Transmit single collision packet counter",    ACC_RW, 0x00000000},
> +{0x700, "TMCL", "Transmit multiple collision packet counter",  ACC_RW, 0x00000000},
> +{0x704, "TLCL", "Transmit late collision packet counter",      ACC_RW, 0x00000000},
> +{0x708, "TXCL", "Transmit excessive collision packet counter", ACC_RW, 0x00000000},
> +{0x70C, "TNCL", "Transmit total collision counter ",           ACC_RW, 0x00000000},
> +{0x714, "TDRP", "Transmit drop frame counter",                 ACC_RW, 0x00000000},
> +{0x718, "TJBR", "Transmit jabber frame counter ",              ACC_RW, 0x00000000},
> +{0x71C, "TFCS", "Transmit FCS error counter",                  ACC_RW, 0x00000000},
> +{0x720, "TXCF", "Transmit control frame counter ",             ACC_RW, 0x00000000},
> +{0x724, "TOVR", "Transmit oversize frame counter",             ACC_RW, 0x00000000},
> +{0x728, "TUND", "Transmit undersize frame counter ",           ACC_RW, 0x00000000},
> +{0x72C, "TFRG", "Transmit fragments frame counter ",           ACC_RW, 0x00000000},
> +
> +/* eTSEC Counter Control and TOE Statistics Registers */
> +
> +{0x730, "CAR1", "Carry register one register",           ACC_w1c, 0x00000000},
> +{0x734, "CAR2", "Carry register two register ",          ACC_w1c, 0x00000000},
> +{0x738, "CAM1", "Carry register one mask register ",     ACC_RW,  0xFE03FFFF},
> +{0x73C, "CAM2", "Carry register two mask register ",     ACC_RW,  0x000FFFFD},
> +{0x740, "RREJ", "Receive filer rejected packet counter", ACC_RW,  0x00000000},
> +
> +/* Hash Function Registers */
> +
> +{0x800, "IGADDR0", "Individual/group address register 0", ACC_RW, 0x00000000},
> +{0x804, "IGADDR1", "Individual/group address register 1", ACC_RW, 0x00000000},
> +{0x808, "IGADDR2", "Individual/group address register 2", ACC_RW, 0x00000000},
> +{0x80C, "IGADDR3", "Individual/group address register 3", ACC_RW, 0x00000000},
> +{0x810, "IGADDR4", "Individual/group address register 4", ACC_RW, 0x00000000},
> +{0x814, "IGADDR5", "Individual/group address register 5", ACC_RW, 0x00000000},
> +{0x818, "IGADDR6", "Individual/group address register 6", ACC_RW, 0x00000000},
> +{0x81C, "IGADDR7", "Individual/group address register 7", ACC_RW, 0x00000000},
> +{0x880, "GADDR0",  "Group address register 0",            ACC_RW, 0x00000000},
> +{0x884, "GADDR1",  "Group address register 1",            ACC_RW, 0x00000000},
> +{0x888, "GADDR2",  "Group address register 2",            ACC_RW, 0x00000000},
> +{0x88C, "GADDR3",  "Group address register 3",            ACC_RW, 0x00000000},
> +{0x890, "GADDR4",  "Group address register 4",            ACC_RW, 0x00000000},
> +{0x894, "GADDR5",  "Group address register 5",            ACC_RW, 0x00000000},
> +{0x898, "GADDR6",  "Group address register 6",            ACC_RW, 0x00000000},
> +{0x89C, "GADDR7",  "Group address register 7",            ACC_RW, 0x00000000},
> +
> +/* eTSEC DMA Attribute Registers */
> +
> +{0xBF8, "ATTR",    "Attribute register",                                  ACC_RW, 0x00000000},
> +{0xBFC, "ATTRELI", "Attribute extract length and extract index register", ACC_RW, 0x00000000},
> +
> +
> +/* eTSEC Lossless Flow Control Registers */
> +
> +{0xC00, "RQPRM0",  "Receive Queue Parameters register 0 ", ACC_RW, 0x00000000},
> +{0xC04, "RQPRM1",  "Receive Queue Parameters register 1 ", ACC_RW, 0x00000000},
> +{0xC08, "RQPRM2",  "Receive Queue Parameters register 2 ", ACC_RW, 0x00000000},
> +{0xC0C, "RQPRM3",  "Receive Queue Parameters register 3 ", ACC_RW, 0x00000000},
> +{0xC10, "RQPRM4",  "Receive Queue Parameters register 4 ", ACC_RW, 0x00000000},
> +{0xC14, "RQPRM5",  "Receive Queue Parameters register 5 ", ACC_RW, 0x00000000},
> +{0xC18, "RQPRM6",  "Receive Queue Parameters register 6 ", ACC_RW, 0x00000000},
> +{0xC1C, "RQPRM7",  "Receive Queue Parameters register 7 ", ACC_RW, 0x00000000},
> +{0xC44, "RFBPTR0", "Last Free RxBD pointer for ring 0",    ACC_RW, 0x00000000},
> +{0xC4C, "RFBPTR1", "Last Free RxBD pointer for ring 1",    ACC_RW, 0x00000000},
> +{0xC54, "RFBPTR2", "Last Free RxBD pointer for ring 2",    ACC_RW, 0x00000000},
> +{0xC5C, "RFBPTR3", "Last Free RxBD pointer for ring 3",    ACC_RW, 0x00000000},
> +{0xC64, "RFBPTR4", "Last Free RxBD pointer for ring 4",    ACC_RW, 0x00000000},
> +{0xC6C, "RFBPTR5", "Last Free RxBD pointer for ring 5",    ACC_RW, 0x00000000},
> +{0xC74, "RFBPTR6", "Last Free RxBD pointer for ring 6",    ACC_RW, 0x00000000},
> +{0xC7C, "RFBPTR7", "Last Free RxBD pointer for ring 7",    ACC_RW, 0x00000000},
> +
> +/* eTSEC Future Expansion Space */
> +
> +/* Reserved*/
> +
> +/* eTSEC IEEE 1588 Registers */
> +
> +{0xE00, "TMR_CTRL",     "Timer control register",                          ACC_RW,  0x00010001},
> +{0xE04, "TMR_TEVENT",   "time stamp event register",                       ACC_w1c, 0x00000000},
> +{0xE08, "TMR_TEMASK",   "Timer event mask register",                       ACC_RW,  0x00000000},
> +{0xE0C, "TMR_PEVENT",   "time stamp event register",                       ACC_RW,  0x00000000},
> +{0xE10, "TMR_PEMASK",   "Timer event mask register",                       ACC_RW,  0x00000000},
> +{0xE14, "TMR_STAT",     "time stamp status register",                      ACC_RW,  0x00000000},
> +{0xE18, "TMR_CNT_H",    "timer counter high register",                     ACC_RW,  0x00000000},
> +{0xE1C, "TMR_CNT_L",    "timer counter low register",                      ACC_RW,  0x00000000},
> +{0xE20, "TMR_ADD",      "Timer drift compensation addend register",        ACC_RW,  0x00000000},
> +{0xE24, "TMR_ACC",      "Timer accumulator register",                      ACC_RW,  0x00000000},
> +{0xE28, "TMR_PRSC",     "Timer prescale",                                  ACC_RW,  0x00000002},
> +{0xE30, "TMROFF_H",     "Timer offset high",                               ACC_RW,  0x00000000},
> +{0xE34, "TMROFF_L",     "Timer offset low",                                ACC_RW,  0x00000000},
> +{0xE40, "TMR_ALARM1_H", "Timer alarm 1 high register",                     ACC_RW,  0xFFFFFFFF},
> +{0xE44, "TMR_ALARM1_L", "Timer alarm 1 high register",                     ACC_RW,  0xFFFFFFFF},
> +{0xE48, "TMR_ALARM2_H", "Timer alarm 2 high register",                     ACC_RW,  0xFFFFFFFF},
> +{0xE4C, "TMR_ALARM2_L", "Timer alarm 2 high register",                     ACC_RW,  0xFFFFFFFF},
> +{0xE80, "TMR_FIPER1",   "Timer fixed period interval",                     ACC_RW,  0xFFFFFFFF},
> +{0xE84, "TMR_FIPER2",   "Timer fixed period interval",                     ACC_RW,  0xFFFFFFFF},
> +{0xE88, "TMR_FIPER3",   "Timer fixed period interval",                     ACC_RW,  0xFFFFFFFF},
> +{0xEA0, "TMR_ETTS1_H",  "Time stamp of general purpose external trigger ", ACC_RW,  0x00000000},
> +{0xEA4, "TMR_ETTS1_L",  "Time stamp of general purpose external trigger",  ACC_RW,  0x00000000},
> +{0xEA8, "TMR_ETTS2_H",  "Time stamp of general purpose external trigger ", ACC_RW,  0x00000000},
> +{0xEAC, "TMR_ETTS2_L",  "Time stamp of general purpose external trigger",  ACC_RW,  0x00000000},
> +
> +/* End Of Table */
> +{0x0, 0x0, 0x0, 0x0, 0x0}
> +};
> diff --git a/hw/net/etsec_registers.h b/hw/net/etsec_registers.h
> new file mode 100644
> index 0000000..7faeaa9
> --- /dev/null
> +++ b/hw/net/etsec_registers.h
> @@ -0,0 +1,302 @@
> +/*
> + * QEMU Freescale eTSEC Emulator
> + *
> + * Copyright (c) 2011-2013 AdaCore
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +#ifndef _ETSEC_REGISTERS_H_
> +#define _ETSEC_REGISTERS_H_
> +
> +#include <stdint.h>
> +
> +typedef struct eTSEC_Register_Definition {
> +    uint32_t offset;
> +    const char *name;
> +    const char *desc;
> +    uint32_t access;
> +    uint32_t reset;
> +} eTSEC_Register_Definition;
> +
> +#define ACC_RW      1           /* Read/Write */
> +#define ACC_RO      2           /* Read Only */
> +#define ACC_WO      3           /* Write Only */
> +#define ACC_w1c     4           /* Write 1 to clear */

ACC_W1C. Would it be cleaner with an enum instead?

> +#define ACC_UNKNOWN 4           /* Unknown register*/
> +
> +extern const eTSEC_Register_Definition eTSEC_registers_def[];
> +
> +#define DMACTRL_LE  (1 << 15)
> +#define DMACTRL_GRS (1 <<  4)
> +#define DMACTRL_GTS (1 <<  3)
> +#define DMACTRL_WOP (1 <<  0)
> +
> +#define IEVENT_RXF  (1 <<  7)
> +#define IEVENT_GRSC (1 <<  8)
> +#define IEVENT_RXB  (1 << 15)
> +#define IEVENT_TXF  (1 << 20)
> +#define IEVENT_TXB  (1 << 21)
> +#define IEVENT_TXC  (1 << 23)
> +#define IEVENT_GTSC (1 << 25)
> +#define IEVENT_BSY  (1 << 29)
> +#define IEVENT_RXC  (1 << 30)
> +
> +#define IMASK_RXFEN  (1 <<  7)
> +#define IMASK_GRSCEN (1 <<  8)
> +#define IMASK_RXBEN  (1 << 15)
> +#define IMASK_TXFEN  (1 << 20)
> +#define IMASK_TXBEN  (1 << 21)
> +#define IMASK_GTSCEN (1 << 25)
> +
> +#define MACCFG1_TX_EN  (1 << 0)
> +#define MACCFG1_RX_EN  (1 << 2)
> +
> +#define MACCFG2_CRC_EN  (1 << 1)
> +#define MACCFG2_PADCRC  (1 << 2)
> +
> +#define MIIMCOM_READ (1 << 0)
> +#define MIIMCOM_SCAN (1 << 1)
> +
> +#define RCTRL_PRSDEP_MASK   (0x3)
> +#define RCTRL_PRSDEP_OFFSET (6)
> +#define RCTRL_RSF           (1 << 2)
> +
> +/* Index of each register */
> +
> +#define TSEC_ID      (0x000 / 4)
> +#define TSEC_ID2     (0x004 / 4)
> +#define IEVENT       (0x010 / 4)
> +#define IMASK        (0x014 / 4)
> +#define EDIS         (0x018 / 4)
> +#define ECNTRL       (0x020 / 4)
> +#define PTV          (0x028 / 4)
> +#define DMACTRL      (0x02C / 4)
> +#define TBIPA        (0x030 / 4)
> +#define TCTRL        (0x100 / 4)
> +#define TSTAT        (0x104 / 4)
> +#define DFVLAN       (0x108 / 4)
> +#define TXIC         (0x110 / 4)
> +#define TQUEUE       (0x114 / 4)
> +#define TR03WT       (0x140 / 4)
> +#define TR47WT       (0x144 / 4)
> +#define TBDBPH       (0x180 / 4)
> +#define TBPTR0       (0x184 / 4)
> +#define TBPTR1       (0x18C / 4)
> +#define TBPTR2       (0x194 / 4)
> +#define TBPTR3       (0x19C / 4)
> +#define TBPTR4       (0x1A4 / 4)
> +#define TBPTR5       (0x1AC / 4)
> +#define TBPTR6       (0x1B4 / 4)
> +#define TBPTR7       (0x1BC / 4)
> +#define TBASEH       (0x200 / 4)
> +#define TBASE0       (0x204 / 4)
> +#define TBASE1       (0x20C / 4)
> +#define TBASE2       (0x214 / 4)
> +#define TBASE3       (0x21C / 4)
> +#define TBASE4       (0x224 / 4)
> +#define TBASE5       (0x22C / 4)
> +#define TBASE6       (0x234 / 4)
> +#define TBASE7       (0x23C / 4)
> +#define TMR_TXTS1_ID (0x280 / 4)
> +#define TMR_TXTS2_ID (0x284 / 4)
> +#define TMR_TXTS1_H  (0x2C0 / 4)
> +#define TMR_TXTS1_L  (0x2C4 / 4)
> +#define TMR_TXTS2_H  (0x2C8 / 4)
> +#define TMR_TXTS2_L  (0x2CC / 4)
> +#define RCTRL        (0x300 / 4)
> +#define RSTAT        (0x304 / 4)
> +#define RXIC         (0x310 / 4)
> +#define RQUEUE       (0x314 / 4)
> +#define RBIFX        (0x330 / 4)
> +#define RQFAR        (0x334 / 4)
> +#define RQFCR        (0x338 / 4)
> +#define RQFPR        (0x33C / 4)
> +#define MRBLR        (0x340 / 4)
> +#define RBDBPH       (0x380 / 4)
> +#define RBPTR0       (0x384 / 4)
> +#define RBPTR1       (0x38C / 4)
> +#define RBPTR2       (0x394 / 4)
> +#define RBPTR3       (0x39C / 4)
> +#define RBPTR4       (0x3A4 / 4)
> +#define RBPTR5       (0x3AC / 4)
> +#define RBPTR6       (0x3B4 / 4)
> +#define RBPTR7       (0x3BC / 4)
> +#define RBASEH       (0x400 / 4)
> +#define RBASE0       (0x404 / 4)
> +#define RBASE1       (0x40C / 4)
> +#define RBASE2       (0x414 / 4)
> +#define RBASE3       (0x41C / 4)
> +#define RBASE4       (0x424 / 4)
> +#define RBASE5       (0x42C / 4)
> +#define RBASE6       (0x434 / 4)
> +#define RBASE7       (0x43C / 4)
> +#define TMR_RXTS_H   (0x4C0 / 4)
> +#define TMR_RXTS_L   (0x4C4 / 4)
> +#define MACCFG1      (0x500 / 4)
> +#define MACCFG2      (0x504 / 4)
> +#define IPGIFG       (0x508 / 4)
> +#define HAFDUP       (0x50C / 4)
> +#define MAXFRM       (0x510 / 4)
> +#define MIIMCFG      (0x520 / 4)
> +#define MIIMCOM      (0x524 / 4)
> +#define MIIMADD      (0x528 / 4)
> +#define MIIMCON      (0x52C / 4)
> +#define MIIMSTAT     (0x530 / 4)
> +#define MIIMIND      (0x534 / 4)
> +#define IFSTAT       (0x53C / 4)
> +#define MACSTNADDR1  (0x540 / 4)
> +#define MACSTNADDR2  (0x544 / 4)
> +#define MAC01ADDR1   (0x548 / 4)
> +#define MAC01ADDR2   (0x54C / 4)
> +#define MAC02ADDR1   (0x550 / 4)
> +#define MAC02ADDR2   (0x554 / 4)
> +#define MAC03ADDR1   (0x558 / 4)
> +#define MAC03ADDR2   (0x55C / 4)
> +#define MAC04ADDR1   (0x560 / 4)
> +#define MAC04ADDR2   (0x564 / 4)
> +#define MAC05ADDR1   (0x568 / 4)
> +#define MAC05ADDR2   (0x56C / 4)
> +#define MAC06ADDR1   (0x570 / 4)
> +#define MAC06ADDR2   (0x574 / 4)
> +#define MAC07ADDR1   (0x578 / 4)
> +#define MAC07ADDR2   (0x57C / 4)
> +#define MAC08ADDR1   (0x580 / 4)
> +#define MAC08ADDR2   (0x584 / 4)
> +#define MAC09ADDR1   (0x588 / 4)
> +#define MAC09ADDR2   (0x58C / 4)
> +#define MAC10ADDR1   (0x590 / 4)
> +#define MAC10ADDR2   (0x594 / 4)
> +#define MAC11ADDR1   (0x598 / 4)
> +#define MAC11ADDR2   (0x59C / 4)
> +#define MAC12ADDR1   (0x5A0 / 4)
> +#define MAC12ADDR2   (0x5A4 / 4)
> +#define MAC13ADDR1   (0x5A8 / 4)
> +#define MAC13ADDR2   (0x5AC / 4)
> +#define MAC14ADDR1   (0x5B0 / 4)
> +#define MAC14ADDR2   (0x5B4 / 4)
> +#define MAC15ADDR1   (0x5B8 / 4)
> +#define MAC15ADDR2   (0x5BC / 4)
> +#define TR64         (0x680 / 4)
> +#define TR127        (0x684 / 4)
> +#define TR255        (0x688 / 4)
> +#define TR511        (0x68C / 4)
> +#define TR1K         (0x690 / 4)
> +#define TRMAX        (0x694 / 4)
> +#define TRMGV        (0x698 / 4)
> +#define RBYT         (0x69C / 4)
> +#define RPKT         (0x6A0 / 4)
> +#define RFCS         (0x6A4 / 4)
> +#define RMCA         (0x6A8 / 4)
> +#define RBCA         (0x6AC / 4)
> +#define RXCF         (0x6B0 / 4)
> +#define RXPF         (0x6B4 / 4)
> +#define RXUO         (0x6B8 / 4)
> +#define RALN         (0x6BC / 4)
> +#define RFLR         (0x6C0 / 4)
> +#define RCDE         (0x6C4 / 4)
> +#define RCSE         (0x6C8 / 4)
> +#define RUND         (0x6CC / 4)
> +#define ROVR         (0x6D0 / 4)
> +#define RFRG         (0x6D4 / 4)
> +#define RJBR         (0x6D8 / 4)
> +#define RDRP         (0x6DC / 4)
> +#define TBYT         (0x6E0 / 4)
> +#define TPKT         (0x6E4 / 4)
> +#define TMCA         (0x6E8 / 4)
> +#define TBCA         (0x6EC / 4)
> +#define TXPF         (0x6F0 / 4)
> +#define TDFR         (0x6F4 / 4)
> +#define TEDF         (0x6F8 / 4)
> +#define TSCL         (0x6FC / 4)
> +#define TMCL         (0x700 / 4)
> +#define TLCL         (0x704 / 4)
> +#define TXCL         (0x708 / 4)
> +#define TNCL         (0x70C / 4)
> +#define TDRP         (0x714 / 4)
> +#define TJBR         (0x718 / 4)
> +#define TFCS         (0x71C / 4)
> +#define TXCF         (0x720 / 4)
> +#define TOVR         (0x724 / 4)
> +#define TUND         (0x728 / 4)
> +#define TFRG         (0x72C / 4)
> +#define CAR1         (0x730 / 4)
> +#define CAR2         (0x734 / 4)
> +#define CAM1         (0x738 / 4)
> +#define CAM2         (0x73C / 4)
> +#define RREJ         (0x740 / 4)
> +#define IGADDR0      (0x800 / 4)
> +#define IGADDR1      (0x804 / 4)
> +#define IGADDR2      (0x808 / 4)
> +#define IGADDR3      (0x80C / 4)
> +#define IGADDR4      (0x810 / 4)
> +#define IGADDR5      (0x814 / 4)
> +#define IGADDR6      (0x818 / 4)
> +#define IGADDR7      (0x81C / 4)
> +#define GADDR0       (0x880 / 4)
> +#define GADDR1       (0x884 / 4)
> +#define GADDR2       (0x888 / 4)
> +#define GADDR3       (0x88C / 4)
> +#define GADDR4       (0x890 / 4)
> +#define GADDR5       (0x894 / 4)
> +#define GADDR6       (0x898 / 4)
> +#define GADDR7       (0x89C / 4)
> +#define ATTR         (0xBF8 / 4)
> +#define ATTRELI      (0xBFC / 4)
> +#define RQPRM0       (0xC00 / 4)
> +#define RQPRM1       (0xC04 / 4)
> +#define RQPRM2       (0xC08 / 4)
> +#define RQPRM3       (0xC0C / 4)
> +#define RQPRM4       (0xC10 / 4)
> +#define RQPRM5       (0xC14 / 4)
> +#define RQPRM6       (0xC18 / 4)
> +#define RQPRM7       (0xC1C / 4)
> +#define RFBPTR0      (0xC44 / 4)
> +#define RFBPTR1      (0xC4C / 4)
> +#define RFBPTR2      (0xC54 / 4)
> +#define RFBPTR3      (0xC5C / 4)
> +#define RFBPTR4      (0xC64 / 4)
> +#define RFBPTR5      (0xC6C / 4)
> +#define RFBPTR6      (0xC74 / 4)
> +#define RFBPTR7      (0xC7C / 4)
> +#define TMR_CTRL     (0xE00 / 4)
> +#define TMR_TEVENT   (0xE04 / 4)
> +#define TMR_TEMASK   (0xE08 / 4)
> +#define TMR_PEVENT   (0xE0C / 4)
> +#define TMR_PEMASK   (0xE10 / 4)
> +#define TMR_STAT     (0xE14 / 4)
> +#define TMR_CNT_H    (0xE18 / 4)
> +#define TMR_CNT_L    (0xE1C / 4)
> +#define TMR_ADD      (0xE20 / 4)
> +#define TMR_ACC      (0xE24 / 4)
> +#define TMR_PRSC     (0xE28 / 4)
> +#define TMROFF_H     (0xE30 / 4)
> +#define TMROFF_L     (0xE34 / 4)
> +#define TMR_ALARM1_H (0xE40 / 4)
> +#define TMR_ALARM1_L (0xE44 / 4)
> +#define TMR_ALARM2_H (0xE48 / 4)
> +#define TMR_ALARM2_L (0xE4C / 4)
> +#define TMR_FIPER1   (0xE80 / 4)
> +#define TMR_FIPER2   (0xE84 / 4)
> +#define TMR_FIPER3   (0xE88 / 4)
> +#define TMR_ETTS1_H  (0xEA0 / 4)
> +#define TMR_ETTS1_L  (0xEA4 / 4)
> +#define TMR_ETTS2_H  (0xEA8 / 4)
> +#define TMR_ETTS2_L  (0xEAC / 4)
> +
> +#endif /* ! _ETSEC_REGISTERS_H_ */
> diff --git a/hw/net/etsec_rings.c b/hw/net/etsec_rings.c
> new file mode 100644
> index 0000000..15c0616
> --- /dev/null
> +++ b/hw/net/etsec_rings.c
> @@ -0,0 +1,673 @@
> +/*
> + * QEMU Freescale eTSEC Emulator
> + *
> + * Copyright (c) 2011-2013 AdaCore
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +#include "qemu/bswap.h"
> +#include "net/checksum.h"
> +
> +#include "etsec.h"
> +#include "etsec_registers.h"
> +
> +/* #define ETSEC_RING_DEBUG */
> +/* #define HEX_DUMP */
> +/* #define DEBUG_BD */
> +
> +#ifdef ETSEC_RING_DEBUG
> +#define RING_DEBUG(fmt, ...) printf("%s:%s " fmt, __func__ ,\
> +                                    etsec->nic->nc.name, ## __VA_ARGS__)
> +#else
> +#define RING_DEBUG(...)
> +#endif  /* ETSEC_RING_DEBUG */
> +
> +#define RING_DEBUG_A(fmt, ...) printf("%s:%s " fmt, __func__ ,\
> +                                      etsec->nic->nc.name, ## __VA_ARGS__)
> +
> +#ifdef DEBUG_BD
> +
> +static void print_tx_bd_flags(uint16_t flags)
> +{
> +    printf("      Ready: %d\n", !!(flags & BD_TX_READY));
> +    printf("      PAD/CRC: %d\n", !!(flags & BD_TX_PADCRC));
> +    printf("      Wrap: %d\n", !!(flags & BD_WRAP));
> +    printf("      Interrupt: %d\n", !!(flags & BD_INTERRUPT));
> +    printf("      Last in frame: %d\n", !!(flags & BD_LAST));
> +    printf("      Tx CRC: %d\n", !!(flags & BD_TX_TC));
> +    printf("      User-defined preamble / defer: %d\n",
> +           !!(flags & BD_TX_PREDEF));
> +    printf("      Huge frame enable / Late collision: %d\n",
> +           !!(flags & BD_TX_HFELC));
> +    printf("      Control frame / Retransmission Limit: %d\n",
> +           !!(flags & BD_TX_CFRL));
> +    printf("      Retry count: %d\n",
> +           (flags >> BD_TX_RC_OFFSET) & BD_TX_RC_MASK);
> +    printf("      Underrun / TCP/IP off-load enable: %d\n",
> +           !!(flags & BD_TX_TOEUN));
> +    printf("      Truncation: %d\n", !!(flags & BD_TX_TR));
> +}
> +
> +static void print_rx_bd_flags(uint16_t flags)
> +{
> +    printf("      Empty: %d\n", !!(flags & BD_RX_EMPTY));
> +    printf("      Receive software ownership: %d\n", !!(flags & BD_RX_RO1));
> +    printf("      Wrap: %d\n", !!(flags & BD_WRAP));
> +    printf("      Interrupt: %d\n", !!(flags & BD_INTERRUPT));
> +    printf("      Last in frame: %d\n", !!(flags & BD_LAST));
> +    printf("      First in frame: %d\n", !!(flags & BD_RX_FIRST));
> +    printf("      Miss: %d\n", !!(flags & BD_RX_MISS));
> +    printf("      Broadcast: %d\n", !!(flags & BD_RX_BROADCAST));
> +    printf("      Multicast: %d\n", !!(flags & BD_RX_MULTICAST));
> +    printf("      Rx frame length violation: %d\n", !!(flags & BD_RX_LG));
> +    printf("      Rx non-octet aligned frame: %d\n", !!(flags & BD_RX_NO));
> +    printf("      Short frame: %d\n", !!(flags & BD_RX_SH));
> +    printf("      Rx CRC Error: %d\n", !!(flags & BD_RX_CR));
> +    printf("      Overrun: %d\n", !!(flags & BD_RX_OV));
> +    printf("      Truncation: %d\n", !!(flags & BD_RX_TR));
> +}
> +
> +
> +static void print_bd(eTSEC_rxtx_bd bd, int mode, uint32_t index)
> +{
> +    printf("eTSEC %s Data Buffer Descriptor (%u)\n",
> +           mode == eTSEC_TRANSMIT ? "Transmit" : "Receive",
> +           index);
> +    printf("   Flags   : 0x%04x\n", bd.flags);
> +    if (mode == eTSEC_TRANSMIT) {
> +        print_tx_bd_flags(bd.flags);
> +    } else {
> +        print_rx_bd_flags(bd.flags);
> +    }
> +    printf("   Length  : 0x%04x\n", bd.length);
> +    printf("   Pointer : 0x%08x\n", bd.bufptr);
> +}
> +
> +#endif  /* DEBUG_BD */
> +
> +#ifdef HEX_DUMP
> +
> +static void hex_dump(FILE *f, const uint8_t *buf, int size)

can you just use qemu_hexdump?

check util/hexdump.c

Regards,
Peter
Fabien Chouteau - July 15, 2013, 2:23 p.m.
On 07/15/2013 04:00 AM, Peter Crosthwaite wrote:
> Hi Fabien,
> 

Hi Peter,

> On Thu, Jul 11, 2013 at 3:10 AM, Fabien Chouteau <chouteau@adacore.com> wrote:
>> +#ifdef DEBUG_REGISTER
>> +    printf("Write 0x%08x @ 0x" TARGET_FMT_plx" val:0x%08x->0x%08x : %s (%s)\n",
>> +           (unsigned int)value, addr, before, reg->value, reg->name, reg->desc);
> 
> Last I knew, printf was a bad idea for error messages due to monitor
> interference issue and nographic mode. But qemu_log or fprintf(stderr,
> are both better alternatives.
> 

Fixed.

>> +static int etsec_can_receive(NetClientState *nc)
>> +{
>> +    /* Yes we always can\ */
>> +    return 1;
> 
> As a general rule this is a bad idea. Multiple ethernet controllers in
> QEMU have tried this and had issues (particularly with the UBOOT
> bootloader) with mass packet droppage. But You have access to the
> information needed (the if conditions in rx_ring_write) to implement
> this it seems.
> 

Fixed.

>> +static int etsec_init(SysBusDevice *dev)
>> +{
>> +    eTSEC *etsec = FROM_SYSBUS(typeof(*etsec), dev);
> 
> Define and use QOM cast macros instead, FROM_FOO macros are deprecated.
> 

Something like:

#define TYPE_ETSEC_COMMON "eTSEC"
#define ETSEC_COMMON(obj) \
     OBJECT_CHECK(eTSEC, (obj), TYPE_ETSEC_COMMON)


static int etsec_init(SysBusDevice *dev)
{
    eTSEC *etsec = ETSEC_COMMON(dev);

?


>> +
>> +    memory_region_init_io(&etsec->io_area, OBJECT(etsec), &etsec_ops, etsec,
>> +                          "eTSEC", 0x1000);
> 
> Constant size memory_region_init_io should be migrated to the Object::Init fm.
> 

What is Object::Init()? Do you have an example?

>> +static void etsec_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
>> +
>> +    k->init = etsec_init;
> 
> SysBusDevice::init in depracated. Please use Device::realize instead.
> 

Fixed.

>> +#define ACC_RW      1           /* Read/Write */
>> +#define ACC_RO      2           /* Read Only */
>> +#define ACC_WO      3           /* Write Only */
>> +#define ACC_w1c     4           /* Write 1 to clear */
> 
> ACC_W1C. Would it be cleaner with an enum instead?
> 
Fixed.

>> +#ifdef HEX_DUMP
>> +
>> +static void hex_dump(FILE *f, const uint8_t *buf, int size)
> 
> can you just use qemu_hexdump?
> 
> check util/hexdump.c
> 

Didn't know there was one. Fixed.

Thanks for the review.
Peter Crosthwaite - July 16, 2013, 1:06 a.m.
Hi Fabien,

On Tue, Jul 16, 2013 at 12:23 AM, Fabien Chouteau <chouteau@adacore.com> wrote:
> On 07/15/2013 04:00 AM, Peter Crosthwaite wrote:
>> Hi Fabien,
>>
>
> Hi Peter,
>
>> On Thu, Jul 11, 2013 at 3:10 AM, Fabien Chouteau <chouteau@adacore.com> wrote:
>>> +#ifdef DEBUG_REGISTER
>>> +    printf("Write 0x%08x @ 0x" TARGET_FMT_plx" val:0x%08x->0x%08x : %s (%s)\n",
>>> +           (unsigned int)value, addr, before, reg->value, reg->name, reg->desc);
>>
>> Last I knew, printf was a bad idea for error messages due to monitor
>> interference issue and nographic mode. But qemu_log or fprintf(stderr,
>> are both better alternatives.
>>
>
> Fixed.
>
>>> +static int etsec_can_receive(NetClientState *nc)
>>> +{
>>> +    /* Yes we always can\ */
>>> +    return 1;
>>
>> As a general rule this is a bad idea. Multiple ethernet controllers in
>> QEMU have tried this and had issues (particularly with the UBOOT
>> bootloader) with mass packet droppage. But You have access to the
>> information needed (the if conditions in rx_ring_write) to implement
>> this it seems.
>>
>
> Fixed.
>
>>> +static int etsec_init(SysBusDevice *dev)
>>> +{
>>> +    eTSEC *etsec = FROM_SYSBUS(typeof(*etsec), dev);
>>
>> Define and use QOM cast macros instead, FROM_FOO macros are deprecated.
>>
>
> Something like:
>
> #define TYPE_ETSEC_COMMON "eTSEC"
> #define ETSEC_COMMON(obj) \
>      OBJECT_CHECK(eTSEC, (obj), TYPE_ETSEC_COMMON)
>
>
> static int etsec_init(SysBusDevice *dev)
> {
>     eTSEC *etsec = ETSEC_COMMON(dev);
>
> ?
>

Yes thats the one.

>
>>> +
>>> +    memory_region_init_io(&etsec->io_area, OBJECT(etsec), &etsec_ops, etsec,
>>> +                          "eTSEC", 0x1000);
>>
>> Constant size memory_region_init_io should be migrated to the Object::Init fm.
>>
>
> What is Object::Init()? Do you have an example?
>

hw/dma/xilinx_axidma.c - xilinx_axidma_init() and
xilinx_axidma_realize() has an example of splitting init task between
early and late. Note the memory_region_init_io is in the _init.

Regards,
Peter


>>> +static void etsec_class_init(ObjectClass *klass, void *data)
>>> +{
>>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>>> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
>>> +
>>> +    k->init = etsec_init;
>>
>> SysBusDevice::init in depracated. Please use Device::realize instead.
>>
>
> Fixed.
>
>>> +#define ACC_RW      1           /* Read/Write */
>>> +#define ACC_RO      2           /* Read Only */
>>> +#define ACC_WO      3           /* Write Only */
>>> +#define ACC_w1c     4           /* Write 1 to clear */
>>
>> ACC_W1C. Would it be cleaner with an enum instead?
>>
> Fixed.
>
>>> +#ifdef HEX_DUMP
>>> +
>>> +static void hex_dump(FILE *f, const uint8_t *buf, int size)
>>
>> can you just use qemu_hexdump?
>>
>> check util/hexdump.c
>>
>
> Didn't know there was one. Fixed.
>
> Thanks for the review.
>
> --
> Fabien Chouteau
>
Scott Wood - July 16, 2013, 2:06 a.m.
On 07/10/2013 12:10:02 PM, Fabien Chouteau wrote:
> This implementation doesn't include ring priority, TCP/IP Off-Load, QoS.
> 
> Signed-off-by: Fabien Chouteau <chouteau@adacore.com>

From the code comments I gather this has been tested on VxWorks.  Has it
been tested on Linux, or anywhere else?

> ---
>  default-configs/ppc-softmmu.mak |    1 +
>  hw/net/Makefile.objs            |    1 +
>  hw/net/etsec.c                  |  472 +++++++++++++++++++++++++++
>  hw/net/etsec.h                  |  169 ++++++++++
>  hw/net/etsec_miim.c             |  146 +++++++++
>  hw/net/etsec_registers.c        |  295 +++++++++++++++++
>  hw/net/etsec_registers.h        |  302 ++++++++++++++++++
>  hw/net/etsec_rings.c            |  673 +++++++++++++++++++++++++++++++++++++++
>  8 files changed, 2059 insertions(+)
>  create mode 100644 hw/net/etsec.c
>  create mode 100644 hw/uuunet/etsec.h
>  create mode 100644 hw/net/etsec_miim.c
>  create mode 100644 hw/net/etsec_registers.c
>  create mode 100644 hw/net/etsec_registers.h
>  create mode 100644 hw/net/etsec_rings.c

This should probably be namespaced as something like fsl_etsec.

> diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak
> index 73e4cc5..c7541cf 100644
> --- a/default-configs/ppc-softmmu.mak
> +++ b/default-configs/ppc-softmmu.mak
> @@ -46,3 +46,4 @@ CONFIG_E500=y
>  CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM))
>  # For PReP
>  CONFIG_MC146818RTC=y
> +CONFIG_ETSEC=y
> diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
> index 951cca3..ca03c3f 100644
> --- a/hw/net/Makefile.objs
> +++ b/hw/net/Makefile.objs
> @@ -28,6 +28,7 @@ obj-$(CONFIG_COLDFIRE) += mcf_fec.o
>  obj-$(CONFIG_MILKYMIST) += milkymist-minimac2.o
>  obj-$(CONFIG_PSERIES) += spapr_llan.o
>  obj-$(CONFIG_XILINX_ETHLITE) += xilinx_ethlite.o
> +obj-$(CONFIG_ETSEC) += etsec.o etsec_registers.o etsec_rings.o etsec_miim.o

Maybe an fsl_etsec directory?

> +static uint64_t etsec_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    eTSEC          *etsec     = opaque;
> +    uint32_t        reg_index = addr / 4;
> +    eTSEC_Register *reg       = NULL;
> +    uint32_t        ret       = 0x0;

It's always awkward when QEMU's type naming convention collides with
names that have pre-existing significant capitalization, but I suspect
this ought to be Etsec and EtsecRegister.  Or maybe ETSEC and
ETSECRegister?  Oh well.

> +    assert(reg_index < REG_NUMBER);
> +
> +    reg = &etsec->regs[reg_index];
> +
> +
> +    switch (reg->access) {
> +    case ACC_WO:
> +        ret = 0x00000000;
> +        break;
> +
> +    case ACC_RW:
> +    case ACC_w1c:
> +    case ACC_RO:
> +    default:
> +        ret = reg->value;
> +        break;
> +    }

Why is "w1c" lowercase when the rest are uppercase?  I realize the
hardware docs do that, but in this case I don't think that takes
precedence over consistent coding style for #defines.

> +#ifdef DEBUG_REGISTER
> +    printf("Read  0x%08x @ 0x" TARGET_FMT_plx
> +           "                            : %s (%s)\n",
> +           ret, addr, reg->name, reg->desc);
> +#endif

This is likely to bitrot -- please consider doing something similar to DPRINTF in hw/intc/openpic.c.

> +static void write_ievent(eTSEC          *etsec,
> +                         eTSEC_Register *reg,
> +                         uint32_t        reg_index,
> +                         uint32_t        value)
> +{
> +    if (value & IEVENT_TXF) {
> +        qemu_irq_lower(etsec->tx_irq);
> +    }
> +    if (value & IEVENT_RXF) {
> +        qemu_irq_lower(etsec->rx_irq);
> +    }
> +
> +    /* Write 1 to clear */
> +    reg->value &= ~value;
> +}

What about the error interrupt?  You raise it but never lower it that I
can see.

TXB/RXB should also be included, and you should only lower the interrupt
if neither ?XB nor ?XF are set for a particular direction.

> +#ifdef HEX_DUMP
> +static void hex_dump(FILE *f, const uint8_t *buf, int size)
> +{
> +    int len, i, j, c;
> +
> +    for (i = 0; i < size; i += 16) {
> +        len = size - i;
> +        if (len > 16) {
> +            len = 16;
> +        }
> +        fprintf(f, "%08x ", i);
> +        for (j = 0; j < 16; j++) {
> +            if (j < len) {
> +                fprintf(f, " %02x", buf[i + j]);
> +            } else {
> +                fprintf(f, "   ");
> +            }
> +        }
> +        fprintf(f, " ");
> +        for (j = 0; j < len; j++) {
> +            c = buf[i + j];
> +            if (c < ' ' || c > '~') {
> +                c = '.';
> +            }
> +            fprintf(f, "%c", c);
> +        }
> +        fprintf(f, "\n");
> +    }
> +}
> +#endif

qemu_hexdump()

> +static int etsec_init(SysBusDevice *dev)
> +{
> +    eTSEC *etsec = FROM_SYSBUS(typeof(*etsec), dev);

I was recently yelled at for using FROM_SYSBUS and related
deprecated infrastructure -- see http://wiki.qemu.org/QOMConventions

> +DeviceState *etsec_create(hwaddr         base,
> +                          MemoryRegion * mr,
> +                          NICInfo      * nd,
> +                          qemu_irq       tx_irq,
> +                          qemu_irq       rx_irq,
> +                          qemu_irq       err_irq)
> +{
> +    DeviceState *dev;
> +
> +    dev = qdev_create(NULL, "eTSEC");
> +    qdev_set_nic_properties(dev, nd);
> +
> +    if (qdev_init(dev)) {
> +        return NULL;
> +    }
> +
> +    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, tx_irq);
> +    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, rx_irq);
> +    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, err_irq);
> +
> +    memory_region_add_subregion(mr, base,
> +                                SYS_BUS_DEVICE(dev)->mmio[0].memory);
> +
> +    return dev;
> +}

Do you plan to update hw/ppc/e500.c (or maybe some other platform?) to
call this?

If you're centralizing this part of device creation, how about the device
tree bits as well?

> +/* eTSEC */
> +
> +#define REG_NUMBER 1024

This is pretty vague.

> +DeviceState *etsec_create(hwaddr        base,
> +                          MemoryRegion *mr,
> +                          NICInfo      *nd,
> +                          qemu_irq      tx_irq,
> +                          qemu_irq      rx_irq,
> +                          qemu_irq      err_irq);

You've got stuff in this file that isn't properly namespaced for
inclusion by arbitrary QEMU code (especially board code that needs to
include headers for several devices), such as REG_NUMBER, yet you declare
etsec_create here which has to be called from board code.

> +#ifdef ETSEC_RING_DEBUG
> +#define RING_DEBUG(fmt, ...) printf("%s:%s " fmt, __func__ ,\
> +                                    etsec->nic->nc.name, ## __VA_ARGS__)
> +#else
> +#define RING_DEBUG(...)
> +#endif  /* ETSEC_RING_DEBUG */

Please consume the arguments even if debug output is not enabled (so you
don't get unused variable warnings), and ideally do a printf inside an
if-statement (on a constant so it will be optimized away) so you still
get format checking -- again, similar to DPRINTF in hw/intc/openpic.c.

> +#define RING_DEBUG_A(fmt, ...) printf("%s:%s " fmt, __func__ ,\
> +                                      etsec->nic->nc.name, ## __VA_ARGS__)

"A"?

If this means "always", why not define RING_DEBUG in terms of this?

> +#ifdef HEX_DUMP
> +
> +static void hex_dump(FILE *f, const uint8_t *buf, int size)
> +{
> +    int len, i, j, c;
> +
> +    for (i = 0; i < size; i += 16) {
> +        len = size - i;
> +        if (len > 16) {
> +            len = 16;
> +        }
> +        fprintf(f, "%08x ", i);
> +        for (j = 0; j < 16; j++) {
> +            if (j < len) {
> +                fprintf(f, " %02x", buf[i + j]);
> +            } else {
> +                fprintf(f, "   ");
> +            }
> +        }
> +        fprintf(f, " ");
> +        for (j = 0; j < len; j++) {
> +            c = buf[i + j];
> +            if (c < ' ' || c > '~') {
> +                c = '.';
> +            }
> +            fprintf(f, "%c", c);
> +        }
> +        fprintf(f, "\n");
> +    }
> +}
> +
> +#endif

Two instances of this even in the same driver?

> +static void fill_rx_bd(eTSEC          *etsec,
> +                       eTSEC_rxtx_bd  *bd,
> +                       const uint8_t **buf,
> +                       size_t         *size)
> +{
> +    uint16_t to_write = MIN(etsec->rx_fcb_size + *size - etsec->rx_padding,
> +                            etsec->regs[MRBLR].value);
> +    uint32_t bufptr   = bd->bufptr;
> +    uint8_t  padd[etsec->rx_padding];
> +    uint8_t  rem;
> +
> +    RING_DEBUG("eTSEC fill Rx buffer @ 0x%08x"
> +               " size:%u(padding + crc:%u) + fcb:%u\n",
> +               bufptr, *size, etsec->rx_padding, etsec->rx_fcb_size);
> +
> +    bd->length = 0;
> +    if (etsec->rx_fcb_size != 0) {
> +        cpu_physical_memory_write(bufptr, etsec->rx_fcb, etsec->rx_fcb_size);
> +
> +        bufptr             += etsec->rx_fcb_size;
> +        bd->length         += etsec->rx_fcb_size;
> +        to_write           -= etsec->rx_fcb_size;
> +        etsec->rx_fcb_size  = 0;
> +
> +    }
> +
> +    if (to_write > 0) {
> +        cpu_physical_memory_write(bufptr, *buf, to_write);
> +
> +        *buf   += to_write;
> +        bufptr += to_write;
> +        *size  -= to_write;
> +
> +        bd->flags  &= ~BD_RX_EMPTY;
> +        bd->length += to_write;
> +    }
> +
> +    if (*size == etsec->rx_padding) {
> +        /* The remaining bytes are for padding which is not actually allocated
> +           in the buffer */
> +
> +        rem = MIN(etsec->regs[MRBLR].value - bd->length, etsec->rx_padding);
> +
> +        if (rem > 0) {
> +            memset(padd, 0x0, sizeof(padd));
> +            etsec->rx_padding -= rem;
> +            *size             -= rem;
> +            bd->length        += rem;
> +            cpu_physical_memory_write(bufptr, padd, rem);
> +        }
> +    }

What if *size > 0 && *size < etsec->rx_padding?

> +static void rx_init_frame(eTSEC *etsec, const uint8_t *buf, size_t size)
> +{
> +    uint32_t fcb_size = 0;
> +    uint8_t  prsdep   = (etsec->regs[RCTRL].value >> RCTRL_PRSDEP_OFFSET)
> +        & RCTRL_PRSDEP_MASK;
> +
> +    if (prsdep != 0) {
> +        /* Prepend FCB */
> +        fcb_size = 8 + 2;          /* FCB size + align */
> +        /* I can't find this 2 bytes alignement in fsl documentation but VxWorks
> +           expects them */

Could these 2 bytes be from RCTRL[PAD], which you seem to ignore?

> +    etsec->rx_padding    = 4;
> +    if (size < 60) {
> +        etsec->rx_padding += 60 - size;
> +    }

Could you explain what you're doing with rx_padding?

> +    /* ring_base = (etsec->regs[RBASEH].value & 0xF) << 32; */
> +    ring_base     += etsec->regs[RBASE0 + ring_nbr].value & ~0x7;
> +    start_bd_addr  = bd_addr = etsec->regs[RBPTR0 + ring_nbr].value & ~0x7;

What about RBDBPH (upper bits of physical address)?  Likewise for TX.

> +                /* NOTE: non-octet aligned frame is impossible in qemu */

Is it possible in eTSEC?

-Scott
Fabien Chouteau - July 16, 2013, 8:35 a.m.
On 07/16/2013 03:06 AM, Peter Crosthwaite wrote:
>>>> +
>>>> +    memory_region_init_io(&etsec->io_area, OBJECT(etsec), &etsec_ops, etsec,
>>>> +                          "eTSEC", 0x1000);
>>>
>>> Constant size memory_region_init_io should be migrated to the Object::Init fm.
>>>
>>
>> What is Object::Init()? Do you have an example?
>>
> 
> hw/dma/xilinx_axidma.c - xilinx_axidma_init() and
> xilinx_axidma_realize() has an example of splitting init task between
> early and late. Note the memory_region_init_io is in the _init.
> 

OK thanks,
Fabien Chouteau - July 16, 2013, 3:28 p.m.
On 07/16/2013 04:06 AM, Scott Wood wrote:
> On 07/10/2013 12:10:02 PM, Fabien Chouteau wrote:
>> This implementation doesn't include ring priority, TCP/IP Off-Load, QoS.
>>
>> Signed-off-by: Fabien Chouteau <chouteau@adacore.com>
> 
> From the code comments I gather this has been tested on VxWorks.  Has it
> been tested on Linux, or anywhere else?
> 

You're right, as I said in the cover letter, this has only been tested on vxWorks.

>>  create mode 100644 hw/net/etsec.c
>>  create mode 100644 hw/uuunet/etsec.h
>>  create mode 100644 hw/net/etsec_miim.c
>>  create mode 100644 hw/net/etsec_registers.c
>>  create mode 100644 hw/net/etsec_registers.h
>>  create mode 100644 hw/net/etsec_rings.c
> 
> This should probably be namespaced as something like fsl_etsec.
> 

Fixed.

>> diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak
>> index 73e4cc5..c7541cf 100644
>> --- a/default-configs/ppc-softmmu.mak
>> +++ b/default-configs/ppc-softmmu.mak
>> @@ -46,3 +46,4 @@ CONFIG_E500=y
>>  CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM))
>>  # For PReP
>>  CONFIG_MC146818RTC=y
>> +CONFIG_ETSEC=y
>> diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
>> index 951cca3..ca03c3f 100644
>> --- a/hw/net/Makefile.objs
>> +++ b/hw/net/Makefile.objs
>> @@ -28,6 +28,7 @@ obj-$(CONFIG_COLDFIRE) += mcf_fec.o
>>  obj-$(CONFIG_MILKYMIST) += milkymist-minimac2.o
>>  obj-$(CONFIG_PSERIES) += spapr_llan.o
>>  obj-$(CONFIG_XILINX_ETHLITE) += xilinx_ethlite.o
>> +obj-$(CONFIG_ETSEC) += etsec.o etsec_registers.o etsec_rings.o etsec_miim.o
> 
> Maybe an fsl_etsec directory?
> 

Is it really necessary?

>> +static uint64_t etsec_read(void *opaque, hwaddr addr, unsigned size)
>> +{
>> +    eTSEC          *etsec     = opaque;
>> +    uint32_t        reg_index = addr / 4;
>> +    eTSEC_Register *reg       = NULL;
>> +    uint32_t        ret       = 0x0;
> 
> It's always awkward when QEMU's type naming convention collides with
> names that have pre-existing significant capitalization, but I suspect
> this ought to be Etsec and EtsecRegister.  Or maybe ETSEC and
> ETSECRegister?  Oh well.

Oh well... :)

> 
>> +    assert(reg_index < REG_NUMBER);
>> +
>> +    reg = &etsec->regs[reg_index];
>> +
>> +
>> +    switch (reg->access) {
>> +    case ACC_WO:
>> +        ret = 0x00000000;
>> +        break;
>> +
>> +    case ACC_RW:
>> +    case ACC_w1c:
>> +    case ACC_RO:
>> +    default:
>> +        ret = reg->value;
>> +        break;
>> +    }
> 
> Why is "w1c" lowercase when the rest are uppercase?  I realize the
> hardware docs do that, but in this case I don't think that takes
> precedence over consistent coding style for #defines.

Fixed.

>> +#ifdef DEBUG_REGISTER
>> +    printf("Read  0x%08x @ 0x" TARGET_FMT_plx
>> +           "                            : %s (%s)\n",
>> +           ret, addr, reg->name, reg->desc);
>> +#endif
> 
> This is likely to bitrot -- please consider doing something similar to DPRINTF in hw/intc/openpic.c.

Fixed.

> 
>> +static void write_ievent(eTSEC          *etsec,
>> +                         eTSEC_Register *reg,
>> +                         uint32_t        reg_index,
>> +                         uint32_t        value)
>> +{
>> +    if (value & IEVENT_TXF) {
>> +        qemu_irq_lower(etsec->tx_irq);
>> +    }
>> +    if (value & IEVENT_RXF) {
>> +        qemu_irq_lower(etsec->rx_irq);
>> +    }
>> +
>> +    /* Write 1 to clear */
>> +    reg->value &= ~value;
>> +}
> 
> What about the error interrupt?  You raise it but never lower it that I
> can see.
>
> TXB/RXB should also be included, and you should only lower the interrupt
> if neither ?XB nor ?XF are set for a particular direction.
> 

I don't claim to support all interrupt flags but I will fix this...

>> +#ifdef HEX_DUMP
>> +static void hex_dump(FILE *f, const uint8_t *buf, int size)
>> +{
>> ...
>> +}
>> +#endif
> 
> qemu_hexdump()
> 

Fixed.

>> +static int etsec_init(SysBusDevice *dev)
>> +{
>> +    eTSEC *etsec = FROM_SYSBUS(typeof(*etsec), dev);
> 
> I was recently yelled at for using FROM_SYSBUS and related
> deprecated infrastructure -- see http://wiki.qemu.org/QOMConventions

Me too ;) Fixed.

> 
>> +DeviceState *etsec_create(hwaddr         base,
>> +                          MemoryRegion * mr,
>> +                          NICInfo      * nd,
>> +                          qemu_irq       tx_irq,
>> +                          qemu_irq       rx_irq,
>> +                          qemu_irq       err_irq)
>>
> Do you plan to update hw/ppc/e500.c (or maybe some other platform?) to
> call this?
> 

No I don't, not for the moment. I use it in one of our machine (that is not in mainstream).
e500.c would require PCI support I think.

> If you're centralizing this part of device creation, how about the device
> tree bits as well?
> 

I don't know about device tree...

>> +/* eTSEC */
>> +
>> +#define REG_NUMBER 1024
> 
> This is pretty vague.
> 

Fixed.

>> +DeviceState *etsec_create(hwaddr        base,
>> +                          MemoryRegion *mr,
>> +                          NICInfo      *nd,
>> +                          qemu_irq      tx_irq,
>> +                          qemu_irq      rx_irq,
>> +                          qemu_irq      err_irq);
> 
> You've got stuff in this file that isn't properly namespaced for
> inclusion by arbitrary QEMU code (especially board code that needs to
> include headers for several devices), such as REG_NUMBER, yet you declare
> etsec_create here which has to be called from board code.

Fixed.

> 
>> +#ifdef ETSEC_RING_DEBUG
>> +#define RING_DEBUG(fmt, ...) printf("%s:%s " fmt, __func__ ,\
>> +                                    etsec->nic->nc.name, ## __VA_ARGS__)
>> +#else
>> +#define RING_DEBUG(...)
>> +#endif  /* ETSEC_RING_DEBUG */
> 
> Please consume the arguments even if debug output is not enabled (so you
> don't get unused variable warnings), and ideally do a printf inside an
> if-statement (on a constant so it will be optimized away) so you still
> get format checking -- again, similar to DPRINTF in hw/intc/openpic.c.

Fixed.

> 
>> +#define RING_DEBUG_A(fmt, ...) printf("%s:%s " fmt, __func__ ,\
>> +                                      etsec->nic->nc.name, ## __VA_ARGS__)
> 
> "A"?
> 
> If this means "always", why not define RING_DEBUG in terms of this?
> 

This was just a handy trick, I will remove it.

> 
> Two instances of this even in the same driver?
> 

Fixed.

>> +static void fill_rx_bd(eTSEC          *etsec,
>> +                       eTSEC_rxtx_bd  *bd,
>> +                       const uint8_t **buf,
>> +                       size_t         *size)
>> +{
>> +    uint16_t to_write = MIN(etsec->rx_fcb_size + *size - etsec->rx_padding,
>> +                            etsec->regs[MRBLR].value);
>> +    uint32_t bufptr   = bd->bufptr;
>> +    uint8_t  padd[etsec->rx_padding];
>> +    uint8_t  rem;
>> +
>> +    RING_DEBUG("eTSEC fill Rx buffer @ 0x%08x"
>> +               " size:%u(padding + crc:%u) + fcb:%u\n",
>> +               bufptr, *size, etsec->rx_padding, etsec->rx_fcb_size);
>> +
>> +    bd->length = 0;
>> +    if (etsec->rx_fcb_size != 0) {
>> +        cpu_physical_memory_write(bufptr, etsec->rx_fcb, etsec->rx_fcb_size);
>> +
>> +        bufptr             += etsec->rx_fcb_size;
>> +        bd->length         += etsec->rx_fcb_size;
>> +        to_write           -= etsec->rx_fcb_size;
>> +        etsec->rx_fcb_size  = 0;
>> +
>> +    }
>> +
>> +    if (to_write > 0) {
>> +        cpu_physical_memory_write(bufptr, *buf, to_write);
>> +
>> +        *buf   += to_write;
>> +        bufptr += to_write;
>> +        *size  -= to_write;
>> +
>> +        bd->flags  &= ~BD_RX_EMPTY;
>> +        bd->length += to_write;
>> +    }
>> +
>> +    if (*size == etsec->rx_padding) {
>> +        /* The remaining bytes are for padding which is not actually allocated
>> +           in the buffer */
>> +
>> +        rem = MIN(etsec->regs[MRBLR].value - bd->length, etsec->rx_padding);
>> +
>> +        if (rem > 0) {
>> +            memset(padd, 0x0, sizeof(padd));
>> +            etsec->rx_padding -= rem;
>> +            *size             -= rem;
>> +            bd->length        += rem;
>> +            cpu_physical_memory_write(bufptr, padd, rem);
>> +        }
>> +    }
> 
> What if *size > 0 && *size < etsec->rx_padding?

I don't think it's possible...

> 
>> +static void rx_init_frame(eTSEC *etsec, const uint8_t *buf, size_t size)
>> +{
>> +    uint32_t fcb_size = 0;
>> +    uint8_t  prsdep   = (etsec->regs[RCTRL].value >> RCTRL_PRSDEP_OFFSET)
>> +        & RCTRL_PRSDEP_MASK;
>> +
>> +    if (prsdep != 0) {
>> +        /* Prepend FCB */
>> +        fcb_size = 8 + 2;          /* FCB size + align */
>> +        /* I can't find this 2 bytes alignement in fsl documentation but VxWorks
>> +           expects them */
> 
> Could these 2 bytes be from RCTRL[PAD], which you seem to ignore?

Did you mean RCTRL[PAL]? It could be that.

> 
>> +    etsec->rx_padding    = 4;

CRC.

>> +    if (size < 60) {
>> +        etsec->rx_padding += 60 - size;
>> +    }
> 
> Could you explain what you're doing with rx_padding?

Avoiding short frames. I'll add a comments.

> 
>> +    /* ring_base = (etsec->regs[RBASEH].value & 0xF) << 32; */
>> +    ring_base     += etsec->regs[RBASE0 + ring_nbr].value & ~0x7;
>> +    start_bd_addr  = bd_addr = etsec->regs[RBPTR0 + ring_nbr].value & ~0x7;
> 
> What about RBDBPH (upper bits of physical address)?  Likewise for TX.
> 

I'm only interested in 32bits address spaces, so RBASEH, TBASEH, RBDBPH or TBDBPH.

>> +                /* NOTE: non-octet aligned frame is impossible in qemu */
> 
> Is it possible in eTSEC?
> 

I think eTSEC can handle it and there's a flag in RxBD for that:

NO | Rx non-octet aligned frame, written by the eTSEC (only valid if L is set).
A frame that contained a number of bits not divisible by eight was received.

But we will never receive such frame from QEMU.


Thanks for your review.
Alexander Graf - July 16, 2013, 3:37 p.m.
On 07/16/2013 05:28 PM, Fabien Chouteau wrote:
> On 07/16/2013 04:06 AM, Scott Wood wrote:
>> On 07/10/2013 12:10:02 PM, Fabien Chouteau wrote:
>>> This implementation doesn't include ring priority, TCP/IP Off-Load, QoS.
>>>
>>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>>  From the code comments I gather this has been tested on VxWorks.  Has it
>> been tested on Linux, or anywhere else?
>>
> You're right, as I said in the cover letter, this has only been tested on vxWorks.

Could you please give it a try? IIRC eTSEC support should be in upstream 
Linux.

[...]

>>> +    /* ring_base = (etsec->regs[RBASEH].value&  0xF)<<  32; */
>>> +    ring_base     += etsec->regs[RBASE0 + ring_nbr].value&  ~0x7;
>>> +    start_bd_addr  = bd_addr = etsec->regs[RBPTR0 + ring_nbr].value&  ~0x7;
>> What about RBDBPH (upper bits of physical address)?  Likewise for TX.
>>
> I'm only interested in 32bits address spaces, so RBASEH, TBASEH, RBDBPH or TBDBPH.

Why? I thought e500mc and above can access more than 32bits of physical 
address space? Oh, but they're always DPAA?


Alex
Fabien Chouteau - July 16, 2013, 4:15 p.m.
On 07/16/2013 05:37 PM, Alexander Graf wrote:
> On 07/16/2013 05:28 PM, Fabien Chouteau wrote:
>> On 07/16/2013 04:06 AM, Scott Wood wrote:
>>> On 07/10/2013 12:10:02 PM, Fabien Chouteau wrote:
>>>> This implementation doesn't include ring priority, TCP/IP Off-Load, QoS.
>>>>
>>>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>>>  From the code comments I gather this has been tested on VxWorks.  Has it
>>> been tested on Linux, or anywhere else?
>>>
>> You're right, as I said in the cover letter, this has only been tested on vxWorks.
>
> Could you please give it a try? IIRC eTSEC support should be in upstream Linux.
>

I don't have time for that. As I said in the cover letter, I submit this
patch for those interested in eTSEC, but I won't be able to test/fix it
for Linux.

>
>>>> +    /* ring_base = (etsec->regs[RBASEH].value&  0xF)<<  32; */
>>>> +    ring_base     += etsec->regs[RBASE0 + ring_nbr].value&  ~0x7;
>>>> +    start_bd_addr  = bd_addr = etsec->regs[RBPTR0 + ring_nbr].value&  ~0x7;
>>> What about RBDBPH (upper bits of physical address)?  Likewise for TX.
>>>
>> I'm only interested in 32bits address spaces, so RBASEH, TBASEH, RBDBPH or TBDBPH.
>
> Why? I thought e500mc and above can access more than 32bits of physical address space? 

Yes but this is not emulated by QEMU, right? sizeof (hwaddr) for
qemu-system-ppc is 8...

> Oh, but they're always DPAA?
> 

I don't understand...

Regards,
Scott Wood - July 16, 2013, 4:54 p.m.
On 07/16/2013 11:15:51 AM, Fabien Chouteau wrote:
> On 07/16/2013 05:37 PM, Alexander Graf wrote:
> > On 07/16/2013 05:28 PM, Fabien Chouteau wrote:
> >> On 07/16/2013 04:06 AM, Scott Wood wrote:
> >>> On 07/10/2013 12:10:02 PM, Fabien Chouteau wrote:
> >>>> This implementation doesn't include ring priority, TCP/IP  
> Off-Load, QoS.
> >>>>
> >>>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
> >>>  From the code comments I gather this has been tested on  
> VxWorks.  Has it
> >>> been tested on Linux, or anywhere else?
> >>>
> >> You're right, as I said in the cover letter, this has only been  
> tested on vxWorks.
> >
> > Could you please give it a try? IIRC eTSEC support should be in  
> upstream Linux.
> >
> 
> I don't have time for that. As I said in the cover letter, I submit  
> this
> patch for those interested in eTSEC, but I won't be able to test/fix  
> it
> for Linux.

Could you please at least document more fully the known limitations,  
such as "I'm only interested in 32bits address spaces"?

> >>>> +    /* ring_base = (etsec->regs[RBASEH].value&  0xF)<<  32; */
> >>>> +    ring_base     += etsec->regs[RBASE0 + ring_nbr].value&   
> ~0x7;
> >>>> +    start_bd_addr  = bd_addr = etsec->regs[RBPTR0 +  
> ring_nbr].value&  ~0x7;
> >>> What about RBDBPH (upper bits of physical address)?  Likewise for  
> TX.
> >>>
> >> I'm only interested in 32bits address spaces, so RBASEH, TBASEH,  
> RBDBPH or TBDBPH.
> >
> > Why? I thought e500mc and above can access more than 32bits of  
> physical address space?
> 
> Yes but this is not emulated by QEMU, right? sizeof (hwaddr) for
> qemu-system-ppc is 8...

36bit physical is emulated by QEMU.  Currently we put CCSR in a place  
that would make it difficult to use memory above 4G, but that should  
change at some point.

> > Oh, but they're always DPAA?
> >
> 
> I don't understand...

It doesn't matter, because it's not true.  We do support 36-bit address  
layouts on mpc85xx and mpc86xx, though we don't make it the only  
supported config in U-Boot as we do on e500mc+.

-Scott
Fabien Chouteau - July 17, 2013, 8:24 a.m.
On 07/16/2013 06:54 PM, Scott Wood wrote:
> On 07/16/2013 11:15:51 AM, Fabien Chouteau wrote:
>> On 07/16/2013 05:37 PM, Alexander Graf wrote:
>> > On 07/16/2013 05:28 PM, Fabien Chouteau wrote:
>> >> On 07/16/2013 04:06 AM, Scott Wood wrote:
>> >>> On 07/10/2013 12:10:02 PM, Fabien Chouteau wrote:
>> >>>> This implementation doesn't include ring priority, TCP/IP Off-Load, QoS.
>> >>>>
>> >>>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>> >>>  From the code comments I gather this has been tested on VxWorks.  Has it
>> >>> been tested on Linux, or anywhere else?
>> >>>
>> >> You're right, as I said in the cover letter, this has only been tested on vxWorks.
>> >
>> > Could you please give it a try? IIRC eTSEC support should be in upstream Linux.
>> >
>>
>> I don't have time for that. As I said in the cover letter, I submit this
>> patch for those interested in eTSEC, but I won't be able to test/fix it
>> for Linux.
> 
> Could you please at least document more fully the known limitations, such as "I'm only interested in 32bits address spaces"?
>

I will, but this device is very complex and I don't even know all the
limitation of my implementation ;)

>> >>>> +    /* ring_base = (etsec->regs[RBASEH].value&  0xF)<<  32; */
>> >>>> +    ring_base     += etsec->regs[RBASE0 + ring_nbr].value&  ~0x7;
>> >>>> +    start_bd_addr  = bd_addr = etsec->regs[RBPTR0 + ring_nbr].value&  ~0x7;
>> >>> What about RBDBPH (upper bits of physical address)?  Likewise for TX.
>> >>>
>> >> I'm only interested in 32bits address spaces, so RBASEH, TBASEH, RBDBPH or TBDBPH.
>> >
>> > Why? I thought e500mc and above can access more than 32bits of physical address space?
>>
>> Yes but this is not emulated by QEMU, right? sizeof (hwaddr) for
>> qemu-system-ppc is 8...
> 
> 36bit physical is emulated by QEMU.  Currently we put CCSR in a place that would make it difficult to use memory above 4G, but that should change at some point.


But hwaddr is 32 bits, how could you call cpu_physical_memory_read()? to
a 36bits address?

Regards,
Alexander Graf - July 17, 2013, 8:29 a.m.
Am 17.07.2013 um 10:24 schrieb Fabien Chouteau <chouteau@adacore.com>:

> On 07/16/2013 06:54 PM, Scott Wood wrote:
>> On 07/16/2013 11:15:51 AM, Fabien Chouteau wrote:
>>> On 07/16/2013 05:37 PM, Alexander Graf wrote:
>>>> On 07/16/2013 05:28 PM, Fabien Chouteau wrote:
>>>>> On 07/16/2013 04:06 AM, Scott Wood wrote:
>>>>>> On 07/10/2013 12:10:02 PM, Fabien Chouteau wrote:
>>>>>>> This implementation doesn't include ring priority, TCP/IP Off-Load, QoS.
>>>>>>> 
>>>>>>> Signed-off-by: Fabien Chouteau<chouteau@adacore.com>
>>>>>> From the code comments I gather this has been tested on VxWorks.  Has it
>>>>>> been tested on Linux, or anywhere else?
>>>>> You're right, as I said in the cover letter, this has only been tested on vxWorks.
>>>> 
>>>> Could you please give it a try? IIRC eTSEC support should be in upstream Linux.
>>> 
>>> I don't have time for that. As I said in the cover letter, I submit this
>>> patch for those interested in eTSEC, but I won't be able to test/fix it
>>> for Linux.
>> 
>> Could you please at least document more fully the known limitations, such as "I'm only interested in 32bits address spaces"?
> 
> I will, but this device is very complex and I don't even know all the
> limitation of my implementation ;)
> 
>>>>>>> +    /* ring_base = (etsec->regs[RBASEH].value&  0xF)<<  32; */
>>>>>>> +    ring_base     += etsec->regs[RBASE0 + ring_nbr].value&  ~0x7;
>>>>>>> +    start_bd_addr  = bd_addr = etsec->regs[RBPTR0 + ring_nbr].value&  ~0x7;
>>>>>> What about RBDBPH (upper bits of physical address)?  Likewise for TX.
>>>>> I'm only interested in 32bits address spaces, so RBASEH, TBASEH, RBDBPH or TBDBPH.
>>>> 
>>>> Why? I thought e500mc and above can access more than 32bits of physical address space?
>>> 
>>> Yes but this is not emulated by QEMU, right? sizeof (hwaddr) for
>>> qemu-system-ppc is 8...
>> 
>> 36bit physical is emulated by QEMU.  Currently we put CCSR in a place that would make it difficult to use memory above 4G, but that should change at some point.
> 
> 
> But hwaddr is 32 bits, how could you call cpu_physical_memory_read()? to
> a 36bits address?

8 x 8 = 64, no? :)

Alex

> 
> Regards,
> 
> -- 
> Fabien Chouteau
Fabien Chouteau - July 17, 2013, 10:27 a.m.
On 07/17/2013 10:29 AM, Alexander Graf wrote:
> 
> 
> Am 17.07.2013 um 10:24 schrieb Fabien Chouteau <chouteau@adacore.com>:
> 
>> On 07/16/2013 06:54 PM, Scott Wood wrote:
>>> On 07/16/2013 11:15:51 AM, Fabien Chouteau wrote:
>>>> On 07/16/2013 05:37 PM, Alexander Graf wrote:
>>>>> On 07/16/2013 05:28 PM, Fabien Chouteau wrote:
>>>>>> On 07/16/2013 04:06 AM, Scott Wood wrote:
>>>>>>> On 07/10/2013 12:10:02 PM, Fabien Chouteau wrote:
>>>>>>>> +    /* ring_base = (etsec->regs[RBASEH].value&  0xF)<<  32; */
>>>>>>>> +    ring_base     += etsec->regs[RBASE0 + ring_nbr].value&  ~0x7;
>>>>>>>> +    start_bd_addr  = bd_addr = etsec->regs[RBPTR0 + ring_nbr].value&  ~0x7;
>>>>>>> What about RBDBPH (upper bits of physical address)?  Likewise for TX.
>>>>>> I'm only interested in 32bits address spaces, so RBASEH, TBASEH, RBDBPH or TBDBPH.
>>>>>
>>>>> Why? I thought e500mc and above can access more than 32bits of physical address space?
>>>>
>>>> Yes but this is not emulated by QEMU, right? sizeof (hwaddr) for
>>>> qemu-system-ppc is 8...
>>>
>>> 36bit physical is emulated by QEMU.  Currently we put CCSR in a place that would make it difficult to use memory above 4G, but that should change at some point.
>>
>>
>> But hwaddr is 32 bits, how could you call cpu_physical_memory_read()? to
>> a 36bits address?
> 
> 8 x 8 = 64, no? :)
> 

Oups, I was so sure of myself :)

Patch

diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak
index 73e4cc5..c7541cf 100644
--- a/default-configs/ppc-softmmu.mak
+++ b/default-configs/ppc-softmmu.mak
@@ -46,3 +46,4 @@  CONFIG_E500=y
 CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM))
 # For PReP
 CONFIG_MC146818RTC=y
+CONFIG_ETSEC=y
diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
index 951cca3..ca03c3f 100644
--- a/hw/net/Makefile.objs
+++ b/hw/net/Makefile.objs
@@ -28,6 +28,7 @@  obj-$(CONFIG_COLDFIRE) += mcf_fec.o
 obj-$(CONFIG_MILKYMIST) += milkymist-minimac2.o
 obj-$(CONFIG_PSERIES) += spapr_llan.o
 obj-$(CONFIG_XILINX_ETHLITE) += xilinx_ethlite.o
+obj-$(CONFIG_ETSEC) += etsec.o etsec_registers.o etsec_rings.o etsec_miim.o
 
 obj-$(CONFIG_VIRTIO) += virtio-net.o
 obj-y += vhost_net.o
diff --git a/hw/net/etsec.c b/hw/net/etsec.c
new file mode 100644
index 0000000..4516b01
--- /dev/null
+++ b/hw/net/etsec.c
@@ -0,0 +1,472 @@ 
+/*
+ * QEMU Freescale eTSEC Emulator
+ *
+ * Copyright (c) 2011-2013 AdaCore
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysemu/sysemu.h"
+#include "hw/sysbus.h"
+#include "trace.h"
+#include "hw/ptimer.h"
+#include "hw/net/etsec.h"
+#include "hw/net/etsec_registers.h"
+
+/* #define HEX_DUMP */
+/* #define DEBUG_REGISTER */
+
+static uint64_t etsec_read(void *opaque, hwaddr addr, unsigned size)
+{
+    eTSEC          *etsec     = opaque;
+    uint32_t        reg_index = addr / 4;
+    eTSEC_Register *reg       = NULL;
+    uint32_t        ret       = 0x0;
+
+    assert(reg_index < REG_NUMBER);
+
+    reg = &etsec->regs[reg_index];
+
+
+    switch (reg->access) {
+    case ACC_WO:
+        ret = 0x00000000;
+        break;
+
+    case ACC_RW:
+    case ACC_w1c:
+    case ACC_RO:
+    default:
+        ret = reg->value;
+        break;
+    }
+
+#ifdef DEBUG_REGISTER
+    printf("Read  0x%08x @ 0x" TARGET_FMT_plx
+           "                            : %s (%s)\n",
+           ret, addr, reg->name, reg->desc);
+#endif
+
+    return ret;
+}
+
+static void write_tstat(eTSEC          *etsec,
+                        eTSEC_Register *reg,
+                        uint32_t        reg_index,
+                        uint32_t        value)
+{
+    int i = 0;
+
+    for (i = 0; i < 8; i++) {
+        /* Check THLTi flag in TSTAT */
+        if (value & (1 << (31 - i))) {
+            walk_tx_ring(etsec, i);
+        }
+    }
+
+    /* Write 1 to clear */
+    reg->value &= ~value;
+}
+
+static void write_rstat(eTSEC          *etsec,
+                        eTSEC_Register *reg,
+                        uint32_t        reg_index,
+                        uint32_t        value)
+{
+    int i = 0;
+
+    for (i = 0; i < 8; i++) {
+        /* Check QHLTi flag in RSTAT */
+        if (value & (1 << (23 - i)) && !(reg->value & (1 << (23 - i)))) {
+            walk_rx_ring(etsec, i);
+        }
+    }
+
+    /* Write 1 to clear */
+    reg->value &= ~value;
+}
+
+static void write_tbasex(eTSEC          *etsec,
+                         eTSEC_Register *reg,
+                         uint32_t        reg_index,
+                         uint32_t        value)
+{
+    reg->value = value & ~0x7;
+
+    /* Copy this value in the ring's TxBD pointer */
+    etsec->regs[TBPTR0 + (reg_index - TBASE0)].value = value & ~0x7;
+}
+
+static void write_rbasex(eTSEC          *etsec,
+                         eTSEC_Register *reg,
+                         uint32_t        reg_index,
+                         uint32_t        value)
+{
+    reg->value = value & ~0x7;
+
+    /* Copy this value in the ring's RxBD pointer */
+    etsec->regs[RBPTR0 + (reg_index - RBASE0)].value = value & ~0x7;
+}
+
+static void write_ievent(eTSEC          *etsec,
+                         eTSEC_Register *reg,
+                         uint32_t        reg_index,
+                         uint32_t        value)
+{
+    if (value & IEVENT_TXF) {
+        qemu_irq_lower(etsec->tx_irq);
+    }
+    if (value & IEVENT_RXF) {
+        qemu_irq_lower(etsec->rx_irq);
+    }
+
+    /* Write 1 to clear */
+    reg->value &= ~value;
+}
+
+static void write_dmactrl(eTSEC          *etsec,
+                          eTSEC_Register *reg,
+                          uint32_t        reg_index,
+                          uint32_t        value)
+{
+    reg->value = value;
+
+    if (value & DMACTRL_GRS) {
+
+        if (etsec->rx_buffer_len != 0) {
+            /* Graceful receive stop delayed until end of frame */
+        } else {
+            /* Graceful receive stop now */
+            etsec->regs[IEVENT].value |= IEVENT_GRSC;
+            if (etsec->regs[IMASK].value & IMASK_GRSCEN) {
+                qemu_irq_raise(etsec->err_irq);
+            }
+        }
+    }
+
+    if (value & DMACTRL_GTS) {
+
+        if (etsec->tx_buffer_len != 0) {
+            /* Graceful transmit stop delayed until end of frame */
+        } else {
+            /* Graceful transmit stop now */
+            etsec->regs[IEVENT].value |= IEVENT_GTSC;
+            if (etsec->regs[IMASK].value & IMASK_GTSCEN) {
+                qemu_irq_raise(etsec->err_irq);
+            }
+        }
+    }
+
+    if (!(value & DMACTRL_WOP)) {
+        /* Start polling */
+        ptimer_stop(etsec->ptimer);
+        ptimer_set_count(etsec->ptimer, 1);
+        ptimer_run(etsec->ptimer, 1);
+    }
+}
+
+static void etsec_write(void     *opaque,
+                        hwaddr    addr,
+                        uint64_t  value,
+                        unsigned  size)
+{
+    eTSEC          *etsec     = opaque;
+    uint32_t        reg_index = addr / 4;
+    eTSEC_Register *reg       = NULL;
+    uint32_t        before    = 0x0;
+
+    assert(reg_index < REG_NUMBER);
+
+    reg = &etsec->regs[reg_index];
+    before = reg->value;
+
+    switch (reg_index) {
+    case IEVENT:
+        write_ievent(etsec, reg, reg_index, value);
+        break;
+
+    case DMACTRL:
+        write_dmactrl(etsec, reg, reg_index, value);
+        break;
+
+    case TSTAT:
+        write_tstat(etsec, reg, reg_index, value);
+        break;
+
+    case RSTAT:
+        write_rstat(etsec, reg, reg_index, value);
+        break;
+
+    case TBASE0 ... TBASE7:
+        write_tbasex(etsec, reg, reg_index, value);
+        break;
+
+    case RBASE0 ... RBASE7:
+        write_rbasex(etsec, reg, reg_index, value);
+        break;
+
+    case MIIMCFG ... MIIMIND:
+        write_miim(etsec, reg, reg_index, value);
+        break;
+
+    default:
+        /* Default handling */
+        switch (reg->access) {
+
+        case ACC_RW:
+        case ACC_WO:
+            reg->value = value;
+            break;
+
+        case ACC_w1c:
+            reg->value &= ~value;
+            break;
+
+        case ACC_RO:
+        default:
+            /* Read Only or Unknown register */
+            break;
+        }
+    }
+
+#ifdef DEBUG_REGISTER
+    printf("Write 0x%08x @ 0x" TARGET_FMT_plx" val:0x%08x->0x%08x : %s (%s)\n",
+           (unsigned int)value, addr, before, reg->value, reg->name, reg->desc);
+#else
+    (void)before; /* Unreferenced */
+#endif
+
+}
+
+static const MemoryRegionOps etsec_ops = {
+    .read = etsec_read,
+    .write = etsec_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static void etsec_timer_hit(void *opaque)
+{
+    eTSEC *etsec = opaque;
+
+    ptimer_stop(etsec->ptimer);
+
+    if (!(etsec->regs[DMACTRL].value & DMACTRL_WOP)) {
+
+        if (!(etsec->regs[DMACTRL].value & DMACTRL_GTS)) {
+            walk_tx_ring(etsec, 0);
+        }
+        ptimer_set_count(etsec->ptimer, 1);
+        ptimer_run(etsec->ptimer, 1);
+    }
+}
+
+static void etsec_reset(DeviceState *d)
+{
+    eTSEC *etsec = container_of(d, eTSEC, busdev.qdev);
+    int i = 0;
+    int reg_index = 0;
+
+    /* Default value for all registers */
+    for (i = 0; i < REG_NUMBER; i++) {
+        etsec->regs[i].name   = "Reserved";
+        etsec->regs[i].desc   = "";
+        etsec->regs[i].access = ACC_UNKNOWN;
+        etsec->regs[i].value  = 0x00000000;
+    }
+
+    /* Set-up known registers */
+    for (i = 0; eTSEC_registers_def[i].name != NULL; i++) {
+
+        reg_index = eTSEC_registers_def[i].offset / 4;
+
+        etsec->regs[reg_index].name   = eTSEC_registers_def[i].name;
+        etsec->regs[reg_index].desc   = eTSEC_registers_def[i].desc;
+        etsec->regs[reg_index].access = eTSEC_registers_def[i].access;
+        etsec->regs[reg_index].value  = eTSEC_registers_def[i].reset;
+    }
+
+    etsec->tx_buffer     = NULL;
+    etsec->tx_buffer_len = 0;
+    etsec->rx_buffer     = NULL;
+    etsec->rx_buffer_len = 0;
+
+    etsec->phy_status =
+        MII_SR_EXTENDED_CAPS    | MII_SR_LINK_STATUS   | MII_SR_AUTONEG_CAPS  |
+        MII_SR_AUTONEG_COMPLETE | MII_SR_PREAMBLE_SUPPRESS |
+        MII_SR_EXTENDED_STATUS  | MII_SR_100T2_HD_CAPS | MII_SR_100T2_FD_CAPS |
+        MII_SR_10T_HD_CAPS      | MII_SR_10T_FD_CAPS   | MII_SR_100X_HD_CAPS  |
+        MII_SR_100X_FD_CAPS     | MII_SR_100T4_CAPS;
+}
+
+static void etsec_cleanup(NetClientState *nc)
+{
+    /* printf("eTSEC cleanup\n"); */
+}
+
+static int etsec_can_receive(NetClientState *nc)
+{
+    /* Yes we always can\ */
+    return 1;
+}
+
+#ifdef HEX_DUMP
+static void hex_dump(FILE *f, const uint8_t *buf, int size)
+{
+    int len, i, j, c;
+
+    for (i = 0; i < size; i += 16) {
+        len = size - i;
+        if (len > 16) {
+            len = 16;
+        }
+        fprintf(f, "%08x ", i);
+        for (j = 0; j < 16; j++) {
+            if (j < len) {
+                fprintf(f, " %02x", buf[i + j]);
+            } else {
+                fprintf(f, "   ");
+            }
+        }
+        fprintf(f, " ");
+        for (j = 0; j < len; j++) {
+            c = buf[i + j];
+            if (c < ' ' || c > '~') {
+                c = '.';
+            }
+            fprintf(f, "%c", c);
+        }
+        fprintf(f, "\n");
+    }
+}
+#endif
+
+static ssize_t etsec_receive(NetClientState *nc,
+                             const uint8_t  *buf,
+                             size_t          size)
+{
+    eTSEC *etsec = qemu_get_nic_opaque(nc);
+
+#if defined(HEX_DUMP)
+    fprintf(stderr, "%s receive size:%d\n", etsec->nic->nc.name, size);
+    hex_dump(stderr, buf, size);
+#endif
+    rx_ring_write(etsec, buf, size);
+    return size;
+}
+
+
+static void etsec_set_link_status(NetClientState *nc)
+{
+    eTSEC *etsec = qemu_get_nic_opaque(nc);
+
+    miim_link_status(etsec, nc);
+}
+
+static NetClientInfo net_etsec_info = {
+    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .size = sizeof(NICState),
+    .can_receive = etsec_can_receive,
+    .receive = etsec_receive,
+    .cleanup = etsec_cleanup,
+    .link_status_changed = etsec_set_link_status,
+};
+
+static int etsec_init(SysBusDevice *dev)
+{
+    eTSEC *etsec = FROM_SYSBUS(typeof(*etsec), dev);
+
+    memory_region_init_io(&etsec->io_area, OBJECT(etsec), &etsec_ops, etsec,
+                          "eTSEC", 0x1000);
+
+    sysbus_init_mmio(dev, &etsec->io_area);
+
+    sysbus_init_irq(dev, &etsec->tx_irq);
+    sysbus_init_irq(dev, &etsec->rx_irq);
+    sysbus_init_irq(dev, &etsec->err_irq);
+
+    etsec->nic = qemu_new_nic(&net_etsec_info, &etsec->conf,
+                              "eTSEC", etsec->busdev.qdev.id, etsec);
+    qemu_format_nic_info_str(qemu_get_queue(etsec->nic), etsec->conf.macaddr.a);
+
+
+    etsec->bh     = qemu_bh_new(etsec_timer_hit, etsec);
+    etsec->ptimer = ptimer_init(etsec->bh);
+    ptimer_set_freq(etsec->ptimer, 100);
+
+    return 0;
+}
+
+static Property etsec_properties[] = {
+    DEFINE_NIC_PROPERTIES(eTSEC, conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void etsec_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = etsec_init;
+    dc->reset = etsec_reset;
+    dc->props = etsec_properties;
+}
+
+static TypeInfo etsec_info = {
+    .name          = "eTSEC",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(eTSEC),
+    .class_init    = etsec_class_init,
+};
+
+static void etsec_register_types(void)
+{
+    type_register_static(&etsec_info);
+}
+
+type_init(etsec_register_types)
+
+DeviceState *etsec_create(hwaddr         base,
+                          MemoryRegion * mr,
+                          NICInfo      * nd,
+                          qemu_irq       tx_irq,
+                          qemu_irq       rx_irq,
+                          qemu_irq       err_irq)
+{
+    DeviceState *dev;
+
+    dev = qdev_create(NULL, "eTSEC");
+    qdev_set_nic_properties(dev, nd);
+
+    if (qdev_init(dev)) {
+        return NULL;
+    }
+
+    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, tx_irq);
+    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, rx_irq);
+    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, err_irq);
+
+    memory_region_add_subregion(mr, base,
+                                SYS_BUS_DEVICE(dev)->mmio[0].memory);
+
+    return dev;
+}
diff --git a/hw/net/etsec.h b/hw/net/etsec.h
new file mode 100644
index 0000000..4fa9edc
--- /dev/null
+++ b/hw/net/etsec.h
@@ -0,0 +1,169 @@ 
+/*
+ * QEMU Freescale eTSEC Emulator
+ *
+ * Copyright (c) 2011-2013 AdaCore
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef _ETSEC_H_
+#define _ETSEC_H_
+
+#include "hw/qdev.h"
+#include "hw/sysbus.h"
+#include "net/net.h"
+#include "hw/ptimer.h"
+
+/* Buffer Descriptors */
+
+typedef struct eTSEC_rxtx_bd {
+    uint16_t flags;
+    uint16_t length;
+    uint32_t bufptr;
+} eTSEC_rxtx_bd;
+
+#define BD_WRAP       (1 << 13)
+#define BD_INTERRUPT  (1 << 12)
+#define BD_LAST       (1 << 11)
+
+#define BD_TX_READY     (1 << 15)
+#define BD_TX_PADCRC    (1 << 14)
+#define BD_TX_TC        (1 << 10)
+#define BD_TX_PREDEF    (1 << 9)
+#define BD_TX_HFELC     (1 << 7)
+#define BD_TX_CFRL      (1 << 6)
+#define BD_TX_RC_MASK   0xF
+#define BD_TX_RC_OFFSET 0x2
+#define BD_TX_TOEUN     (1 << 1)
+#define BD_TX_TR        (1 << 0)
+
+#define BD_RX_EMPTY     (1 << 15)
+#define BD_RX_RO1       (1 << 14)
+#define BD_RX_FIRST     (1 << 10)
+#define BD_RX_MISS      (1 << 8)
+#define BD_RX_BROADCAST (1 << 7)
+#define BD_RX_MULTICAST (1 << 6)
+#define BD_RX_LG        (1 << 5)
+#define BD_RX_NO        (1 << 4)
+#define BD_RX_SH        (1 << 3)
+#define BD_RX_CR        (1 << 2)
+#define BD_RX_OV        (1 << 1)
+#define BD_RX_TR        (1 << 0)
+
+/* Tx FCB flags */
+#define FCB_TX_VLN     (1 << 7)
+#define FCB_TX_IP      (1 << 6)
+#define FCB_TX_IP6     (1 << 5)
+#define FCB_TX_TUP     (1 << 4)
+#define FCB_TX_UDP     (1 << 3)
+#define FCB_TX_CIP     (1 << 2)
+#define FCB_TX_CTU     (1 << 1)
+#define FCB_TX_NPH     (1 << 0)
+
+/* PHY Status Register */
+#define MII_SR_EXTENDED_CAPS     0x0001    /* Extended register capabilities */
+#define MII_SR_JABBER_DETECT     0x0002    /* Jabber Detected */
+#define MII_SR_LINK_STATUS       0x0004    /* Link Status 1 = link */
+#define MII_SR_AUTONEG_CAPS      0x0008    /* Auto Neg Capable */
+#define MII_SR_REMOTE_FAULT      0x0010    /* Remote Fault Detect */
+#define MII_SR_AUTONEG_COMPLETE  0x0020    /* Auto Neg Complete */
+#define MII_SR_PREAMBLE_SUPPRESS 0x0040    /* Preamble may be suppressed */
+#define MII_SR_EXTENDED_STATUS   0x0100    /* Ext. status info in Reg 0x0F */
+#define MII_SR_100T2_HD_CAPS     0x0200    /* 100T2 Half Duplex Capable */
+#define MII_SR_100T2_FD_CAPS     0x0400    /* 100T2 Full Duplex Capable */
+#define MII_SR_10T_HD_CAPS       0x0800    /* 10T   Half Duplex Capable */
+#define MII_SR_10T_FD_CAPS       0x1000    /* 10T   Full Duplex Capable */
+#define MII_SR_100X_HD_CAPS      0x2000    /* 100X  Half Duplex Capable */
+#define MII_SR_100X_FD_CAPS      0x4000    /* 100X  Full Duplex Capable */
+#define MII_SR_100T4_CAPS        0x8000    /* 100T4 Capable */
+
+/* eTSEC */
+
+#define REG_NUMBER 1024
+
+typedef struct eTSEC_Register {
+    const char *name;
+    const char *desc;
+    uint32_t    access;
+    uint32_t    value;
+} eTSEC_Register;
+
+typedef struct eTSEC {
+    SysBusDevice  busdev;
+
+    MemoryRegion  io_area;
+
+    eTSEC_Register regs[REG_NUMBER];
+
+    NICState *nic;
+    NICConf   conf;
+
+    /* Tx */
+
+    uint8_t       *tx_buffer;
+    uint32_t       tx_buffer_len;
+    eTSEC_rxtx_bd  first_bd;
+
+    /* Rx */
+
+    uint8_t       *rx_buffer;
+    uint32_t       rx_buffer_len;
+    uint32_t       rx_remaining_data;
+    uint8_t        rx_first_in_frame;
+    uint8_t        rx_fcb_size;
+    eTSEC_rxtx_bd  rx_first_bd;
+    uint8_t        rx_fcb[10];
+    uint32_t       rx_padding;
+
+    /* IRQs */
+    qemu_irq     tx_irq;
+    qemu_irq     rx_irq;
+    qemu_irq     err_irq;
+
+
+    uint16_t phy_status;
+    uint16_t phy_control;
+
+    /* Polling */
+    QEMUBH *bh;
+    struct ptimer_state *ptimer;
+
+} eTSEC;
+
+#define eTSEC_TRANSMIT 1
+#define eTSEC_RECEIVE  2
+
+DeviceState *etsec_create(hwaddr        base,
+                          MemoryRegion *mr,
+                          NICInfo      *nd,
+                          qemu_irq      tx_irq,
+                          qemu_irq      rx_irq,
+                          qemu_irq      err_irq);
+
+void walk_tx_ring(eTSEC *etsec, int ring_nbr);
+void walk_rx_ring(eTSEC *etsec, int ring_nbr);
+void rx_ring_write(eTSEC *etsec, const uint8_t *buf, size_t size);
+
+void write_miim(eTSEC          *etsec,
+                eTSEC_Register *reg,
+                uint32_t        reg_index,
+                uint32_t        value);
+
+void miim_link_status(eTSEC *etsec, NetClientState *nc);
+
+#endif /* ! _ETSEC_H_ */
diff --git a/hw/net/etsec_miim.c b/hw/net/etsec_miim.c
new file mode 100644
index 0000000..2bd9f0d
--- /dev/null
+++ b/hw/net/etsec_miim.c
@@ -0,0 +1,146 @@ 
+/*
+ * QEMU Freescale eTSEC Emulator
+ *
+ * Copyright (c) 2011-2013 AdaCore
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "etsec.h"
+#include "etsec_registers.h"
+
+/* #define DEBUG_MIIM */
+
+#define MIIM_CONTROL    0
+#define MIIM_STATUS     1
+#define MIIM_PHY_ID_1   2
+#define MIIM_PHY_ID_2   3
+#define MIIM_T2_STATUS  10
+#define MIIM_EXT_STATUS 15
+
+static void miim_read_cycle(eTSEC *etsec)
+{
+    uint8_t  phy;
+    uint8_t  addr;
+    uint16_t value;
+
+    phy  = (etsec->regs[MIIMADD].value >> 8) & 0x1F;
+    (void)phy; /* Unreferenced */
+    addr = etsec->regs[MIIMADD].value & 0x1F;
+
+    switch (addr) {
+    case MIIM_CONTROL:
+        value = etsec->phy_control;
+        break;
+    case MIIM_STATUS:
+        value = etsec->phy_status;
+        break;
+    case MIIM_T2_STATUS:
+        value = 0x1800;           /* Local and remote receivers OK */
+        break;
+    default:
+        value = 0x0;
+        break;
+    };
+
+#ifdef DEBUG_MIIM
+    printf("%s phy:%d addr:0x%x value:0x%x\n", __func__, phy, addr, value);
+#endif
+
+    etsec->regs[MIIMSTAT].value = value;
+}
+
+static void miim_write_cycle(eTSEC *etsec)
+{
+    uint8_t  phy;
+    uint8_t  addr;
+    uint16_t value;
+
+    phy   = (etsec->regs[MIIMADD].value >> 8) & 0x1F;
+    (void)phy; /* Unreferenced */
+    addr  = etsec->regs[MIIMADD].value & 0x1F;
+    value = etsec->regs[MIIMCON].value & 0xffff;
+
+#ifdef DEBUG_MIIM
+    printf("%s phy:%d addr:0x%x value:0x%x\n", __func__, phy, addr, value);
+#endif
+
+    switch (addr) {
+    case MIIM_CONTROL:
+        etsec->phy_control = value & ~(0x8100);
+        break;
+    default:
+        break;
+    };
+}
+
+void write_miim(eTSEC          *etsec,
+               eTSEC_Register *reg,
+               uint32_t        reg_index,
+               uint32_t        value)
+{
+
+    switch (reg_index) {
+
+    case MIIMCOM:
+        /* Read and scan cycle */
+
+        if ((!(reg->value & MIIMCOM_READ)) && (value & MIIMCOM_READ)) {
+            /* Read */
+            miim_read_cycle(etsec);
+        }
+        reg->value = value;
+        break;
+
+    case MIIMCON:
+        reg->value = value & 0xffff;
+        miim_write_cycle(etsec);
+        break;
+
+    default:
+        /* Default handling */
+        switch (reg->access) {
+
+        case ACC_RW:
+        case ACC_WO:
+            reg->value = value;
+            break;
+
+        case ACC_w1c:
+            reg->value &= ~value;
+            break;
+
+        case ACC_RO:
+        default:
+            /* Read Only or Unknown register */
+            break;
+        }
+    }
+
+}
+
+void miim_link_status(eTSEC *etsec, NetClientState *nc)
+{
+    /* Set link status */
+    if (nc->link_down) {
+        etsec->phy_status &= ~MII_SR_LINK_STATUS;
+    } else {
+        etsec->phy_status |= MII_SR_LINK_STATUS;
+    }
+}
diff --git a/hw/net/etsec_registers.c b/hw/net/etsec_registers.c
new file mode 100644
index 0000000..719a886
--- /dev/null
+++ b/hw/net/etsec_registers.c
@@ -0,0 +1,295 @@ 
+/*
+ * QEMU Freescale eTSEC Emulator
+ *
+ * Copyright (c) 2011-2013 AdaCore
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "etsec_registers.h"
+
+const eTSEC_Register_Definition eTSEC_registers_def[] = {
+{0x000, "TSEC_ID",  "Controller ID register",    ACC_RO,  0x01240000},
+{0x004, "TSEC_ID2", "Controller ID register 2",  ACC_RO,  0x003000F0},
+{0x010, "IEVENT",   "Interrupt event register",  ACC_w1c, 0x00000000},
+{0x014, "IMASK",    "Interrupt mask register",   ACC_RW,  0x00000000},
+{0x018, "EDIS",     "Error disabled register",   ACC_RW,  0x00000000},
+{0x020, "ECNTRL",   "Ethernet control register", ACC_RW,  0x00000040},
+{0x028, "PTV",      "Pause time value register", ACC_RW,  0x00000000},
+{0x02C, "DMACTRL",  "DMA control register",      ACC_RW,  0x00000000},
+{0x030, "TBIPA",    "TBI PHY address register",  ACC_RW,  0x00000000},
+
+/* eTSEC FIFO Control and Status Registers */
+
+{0x058, "FIFO_RX_ALARM",          "FIFO receive alarm start threshold register",    ACC_RW, 0x00000040},
+{0x05C, "FIFO_RX_ALARM_SHUTOFF",  "FIFO receive alarm shut-off threshold register", ACC_RW, 0x00000080},
+{0x08C, "FIFO_TX_THR",            "FIFO transmit threshold register",               ACC_RW, 0x00000080},
+{0x098, "FIFO_TX_STARVE",         "FIFO transmit starve register",                  ACC_RW, 0x00000040},
+{0x09C, "FIFO_TX_STARVE_SHUTOFF", "FIFO transmit starve shut-off register",         ACC_RW, 0x00000080},
+
+/* eTSEC Transmit Control and Status Registers */
+
+{0x100, "TCTRL",        "Transmit control register",                ACC_RW,  0x00000000},
+{0x104, "TSTAT",        "Transmit status register",                 ACC_w1c, 0x00000000},
+{0x108, "DFVLAN",       "Default VLAN control word",                ACC_RW,  0x81000000},
+{0x110, "TXIC",         "Transmit interrupt coalescing register",   ACC_RW,  0x00000000},
+{0x114, "TQUEUE",       "Transmit queue control register",          ACC_RW,  0x00008000},
+{0x140, "TR03WT",       "TxBD Rings 0-3 round-robin weightings",    ACC_RW,  0x00000000},
+{0x144, "TR47WT",       "TxBD Rings 4-7 round-robin weightings",    ACC_RW,  0x00000000},
+{0x180, "TBDBPH",       "Tx data buffer pointer high bits",         ACC_RW,  0x00000000},
+{0x184, "TBPTR0",       "TxBD pointer for ring 0",                  ACC_RW,  0x00000000},
+{0x18C, "TBPTR1",       "TxBD pointer for ring 1",                  ACC_RW,  0x00000000},
+{0x194, "TBPTR2",       "TxBD pointer for ring 2",                  ACC_RW,  0x00000000},
+{0x19C, "TBPTR3",       "TxBD pointer for ring 3",                  ACC_RW,  0x00000000},
+{0x1A4, "TBPTR4",       "TxBD pointer for ring 4",                  ACC_RW,  0x00000000},
+{0x1AC, "TBPTR5",       "TxBD pointer for ring 5",                  ACC_RW,  0x00000000},
+{0x1B4, "TBPTR6",       "TxBD pointer for ring 6",                  ACC_RW,  0x00000000},
+{0x1BC, "TBPTR7",       "TxBD pointer for ring 7",                  ACC_RW,  0x00000000},
+{0x200, "TBASEH",       "TxBD base address high bits",              ACC_RW,  0x00000000},
+{0x204, "TBASE0",       "TxBD base address of ring 0",              ACC_RW,  0x00000000},
+{0x20C, "TBASE1",       "TxBD base address of ring 1",              ACC_RW,  0x00000000},
+{0x214, "TBASE2",       "TxBD base address of ring 2",              ACC_RW,  0x00000000},
+{0x21C, "TBASE3",       "TxBD base address of ring 3",              ACC_RW,  0x00000000},
+{0x224, "TBASE4",       "TxBD base address of ring 4",              ACC_RW,  0x00000000},
+{0x22C, "TBASE5",       "TxBD base address of ring 5",              ACC_RW,  0x00000000},
+{0x234, "TBASE6",       "TxBD base address of ring 6",              ACC_RW,  0x00000000},
+{0x23C, "TBASE7",       "TxBD base address of ring 7",              ACC_RW,  0x00000000},
+{0x280, "TMR_TXTS1_ID", "Tx time stamp identification tag (set 1)", ACC_RO,  0x00000000},
+{0x284, "TMR_TXTS2_ID", "Tx time stamp identification tag (set 2)", ACC_RO,  0x00000000},
+{0x2C0, "TMR_TXTS1_H",  "Tx time stamp high (set 1)",               ACC_RO,  0x00000000},
+{0x2C4, "TMR_TXTS1_L",  "Tx time stamp high (set 1)",               ACC_RO,  0x00000000},
+{0x2C8, "TMR_TXTS2_H",  "Tx time stamp high (set 2)",               ACC_RO,  0x00000000},
+{0x2CC, "TMR_TXTS2_L",  "Tx time stamp high (set 2)",               ACC_RO,  0x00000000},
+
+/* eTSEC Receive Control and Status Registers */
+
+{0x300, "RCTRL",      "Receive control register",                     ACC_RW,  0x00000000},
+{0x304, "RSTAT",      "Receive status register",                      ACC_w1c, 0x00000000},
+{0x310, "RXIC",       "Receive interrupt coalescing register",        ACC_RW,  0x00000000},
+{0x314, "RQUEUE",     "Receive queue control register.",              ACC_RW,  0x00800080},
+{0x330, "RBIFX",      "Receive bit field extract control register",   ACC_RW,  0x00000000},
+{0x334, "RQFAR",      "Receive queue filing table address register",  ACC_RW,  0x00000000},
+{0x338, "RQFCR",      "Receive queue filing table control register",  ACC_RW,  0x00000000},
+{0x33C, "RQFPR",      "Receive queue filing table property register", ACC_RW,  0x00000000},
+{0x340, "MRBLR",      "Maximum receive buffer length register",       ACC_RW,  0x00000000},
+{0x380, "RBDBPH",     "Rx data buffer pointer high bits",             ACC_RW,  0x00000000},
+{0x384, "RBPTR0",     "RxBD pointer for ring 0",                      ACC_RW,  0x00000000},
+{0x38C, "RBPTR1",     "RxBD pointer for ring 1",                      ACC_RW,  0x00000000},
+{0x394, "RBPTR2",     "RxBD pointer for ring 2",                      ACC_RW,  0x00000000},
+{0x39C, "RBPTR3",     "RxBD pointer for ring 3",                      ACC_RW,  0x00000000},
+{0x3A4, "RBPTR4",     "RxBD pointer for ring 4",                      ACC_RW,  0x00000000},
+{0x3AC, "RBPTR5",     "RxBD pointer for ring 5",                      ACC_RW,  0x00000000},
+{0x3B4, "RBPTR6",     "RxBD pointer for ring 6",                      ACC_RW,  0x00000000},
+{0x3BC, "RBPTR7",     "RxBD pointer for ring 7",                      ACC_RW,  0x00000000},
+{0x400, "RBASEH",     "RxBD base address high bits",                  ACC_RW,  0x00000000},
+{0x404, "RBASE0",     "RxBD base address of ring 0",                  ACC_RW,  0x00000000},
+{0x40C, "RBASE1",     "RxBD base address of ring 1",                  ACC_RW,  0x00000000},
+{0x414, "RBASE2",     "RxBD base address of ring 2",                  ACC_RW,  0x00000000},
+{0x41C, "RBASE3",     "RxBD base address of ring 3",                  ACC_RW,  0x00000000},
+{0x424, "RBASE4",     "RxBD base address of ring 4",                  ACC_RW,  0x00000000},
+{0x42C, "RBASE5",     "RxBD base address of ring 5",                  ACC_RW,  0x00000000},
+{0x434, "RBASE6",     "RxBD base address of ring 6",                  ACC_RW,  0x00000000},
+{0x43C, "RBASE7",     "RxBD base address of ring 7",                  ACC_RW,  0x00000000},
+{0x4C0, "TMR_RXTS_H", "Rx timer time stamp register high",            ACC_RW,  0x00000000},
+{0x4C4, "TMR_RXTS_L", "Rx timer time stamp register low",             ACC_RW,  0x00000000},
+
+/* eTSEC MAC Registers */
+
+{0x500, "MACCFG1",     "MAC configuration register 1",          ACC_RW, 0x00000000},
+{0x504, "MACCFG2",     "MAC configuration register 2",          ACC_RW, 0x00007000},
+{0x508, "IPGIFG",      "Inter-packet/inter-frame gap register", ACC_RW, 0x40605060},
+{0x50C, "HAFDUP",      "Half-duplex control",                   ACC_RW, 0x00A1F037},
+{0x510, "MAXFRM",      "Maximum frame length",                  ACC_RW, 0x00000600},
+{0x520, "MIIMCFG",     "MII management configuration",          ACC_RW, 0x00000007},
+{0x524, "MIIMCOM",     "MII management command",                ACC_RW, 0x00000000},
+{0x528, "MIIMADD",     "MII management address",                ACC_RW, 0x00000000},
+{0x52C, "MIIMCON",     "MII management control",                ACC_WO, 0x00000000},
+{0x530, "MIIMSTAT",    "MII management status",                 ACC_RO, 0x00000000},
+{0x534, "MIIMIND",     "MII management indicator",              ACC_RO, 0x00000000},
+{0x53C, "IFSTAT",      "Interface status",                      ACC_RO, 0x00000000},
+{0x540, "MACSTNADDR1", "MAC station address register 1",        ACC_RW, 0x00000000},
+{0x544, "MACSTNADDR2", "MAC station address register 2",        ACC_RW, 0x00000000},
+{0x548, "MAC01ADDR1",  "MAC exact match address 1, part 1",     ACC_RW, 0x00000000},
+{0x54C, "MAC01ADDR2",  "MAC exact match address 1, part 2",     ACC_RW, 0x00000000},
+{0x550, "MAC02ADDR1",  "MAC exact match address 2, part 1",     ACC_RW, 0x00000000},
+{0x554, "MAC02ADDR2",  "MAC exact match address 2, part 2",     ACC_RW, 0x00000000},
+{0x558, "MAC03ADDR1",  "MAC exact match address 3, part 1",     ACC_RW, 0x00000000},
+{0x55C, "MAC03ADDR2",  "MAC exact match address 3, part 2",     ACC_RW, 0x00000000},
+{0x560, "MAC04ADDR1",  "MAC exact match address 4, part 1",     ACC_RW, 0x00000000},
+{0x564, "MAC04ADDR2",  "MAC exact match address 4, part 2",     ACC_RW, 0x00000000},
+{0x568, "MAC05ADDR1",  "MAC exact match address 5, part 1",     ACC_RW, 0x00000000},
+{0x56C, "MAC05ADDR2",  "MAC exact match address 5, part 2",     ACC_RW, 0x00000000},
+{0x570, "MAC06ADDR1",  "MAC exact match address 6, part 1",     ACC_RW, 0x00000000},
+{0x574, "MAC06ADDR2",  "MAC exact match address 6, part 2",     ACC_RW, 0x00000000},
+{0x578, "MAC07ADDR1",  "MAC exact match address 7, part 1",     ACC_RW, 0x00000000},
+{0x57C, "MAC07ADDR2",  "MAC exact match address 7, part 2",     ACC_RW, 0x00000000},
+{0x580, "MAC08ADDR1",  "MAC exact match address 8, part 1",     ACC_RW, 0x00000000},
+{0x584, "MAC08ADDR2",  "MAC exact match address 8, part 2",     ACC_RW, 0x00000000},
+{0x588, "MAC09ADDR1",  "MAC exact match address 9, part 1",     ACC_RW, 0x00000000},
+{0x58C, "MAC09ADDR2",  "MAC exact match address 9, part 2",     ACC_RW, 0x00000000},
+{0x590, "MAC10ADDR1",  "MAC exact match address 10, part 1",    ACC_RW, 0x00000000},
+{0x594, "MAC10ADDR2",  "MAC exact match address 10, part 2",    ACC_RW, 0x00000000},
+{0x598, "MAC11ADDR1",  "MAC exact match address 11, part 1",    ACC_RW, 0x00000000},
+{0x59C, "MAC11ADDR2",  "MAC exact match address 11, part 2",    ACC_RW, 0x00000000},
+{0x5A0, "MAC12ADDR1",  "MAC exact match address 12, part 1",    ACC_RW, 0x00000000},
+{0x5A4, "MAC12ADDR2",  "MAC exact match address 12, part 2",    ACC_RW, 0x00000000},
+{0x5A8, "MAC13ADDR1",  "MAC exact match address 13, part 1",    ACC_RW, 0x00000000},
+{0x5AC, "MAC13ADDR2",  "MAC exact match address 13, part 2",    ACC_RW, 0x00000000},
+{0x5B0, "MAC14ADDR1",  "MAC exact match address 14, part 1",    ACC_RW, 0x00000000},
+{0x5B4, "MAC14ADDR2",  "MAC exact match address 14, part 2",    ACC_RW, 0x00000000},
+{0x5B8, "MAC15ADDR1",  "MAC exact match address 15, part 1",    ACC_RW, 0x00000000},
+{0x5BC, "MAC15ADDR2",  "MAC exact match address 15, part 2",    ACC_RW, 0x00000000},
+
+/* eTSEC, "Transmit", "and", Receive, Counters */
+
+{0x680, "TR64",  "Transmit and receive 64-byte frame counter ",                   ACC_RW, 0x00000000},
+{0x684, "TR127", "Transmit and receive 65- to 127-byte frame counter",            ACC_RW, 0x00000000},
+{0x688, "TR255", "Transmit and receive 128- to 255-byte frame counter",           ACC_RW, 0x00000000},
+{0x68C, "TR511", "Transmit and receive 256- to 511-byte frame counter",           ACC_RW, 0x00000000},
+{0x690, "TR1K",  "Transmit and receive 512- to 1023-byte frame counter",          ACC_RW, 0x00000000},
+{0x694, "TRMAX", "Transmit and receive 1024- to 1518-byte frame counter",         ACC_RW, 0x00000000},
+{0x698, "TRMGV", "Transmit and receive 1519- to 1522-byte good VLAN frame count", ACC_RW, 0x00000000},
+
+/* eTSEC Receive Counters */
+
+{0x69C, "RBYT", "Receive byte counter",                  ACC_RW, 0x00000000},
+{0x6A0, "RPKT", "Receive packet counter",                ACC_RW, 0x00000000},
+{0x6A4, "RFCS", "Receive FCS error counter",             ACC_RW, 0x00000000},
+{0x6A8, "RMCA", "Receive multicast packet counter",      ACC_RW, 0x00000000},
+{0x6AC, "RBCA", "Receive broadcast packet counter",      ACC_RW, 0x00000000},
+{0x6B0, "RXCF", "Receive control frame packet counter ", ACC_RW, 0x00000000},
+{0x6B4, "RXPF", "Receive PAUSE frame packet counter",    ACC_RW, 0x00000000},
+{0x6B8, "RXUO", "Receive unknown OP code counter ",      ACC_RW, 0x00000000},
+{0x6BC, "RALN", "Receive alignment error counter ",      ACC_RW, 0x00000000},
+{0x6C0, "RFLR", "Receive frame length error counter ",   ACC_RW, 0x00000000},
+{0x6C4, "RCDE", "Receive code error counter ",           ACC_RW, 0x00000000},
+{0x6C8, "RCSE", "Receive carrier sense error counter",   ACC_RW, 0x00000000},
+{0x6CC, "RUND", "Receive undersize packet counter",      ACC_RW, 0x00000000},
+{0x6D0, "ROVR", "Receive oversize packet counter ",      ACC_RW, 0x00000000},
+{0x6D4, "RFRG", "Receive fragments counter",             ACC_RW, 0x00000000},
+{0x6D8, "RJBR", "Receive jabber counter ",               ACC_RW, 0x00000000},
+{0x6DC, "RDRP", "Receive drop counter",                  ACC_RW, 0x00000000},
+
+/* eTSEC Transmit Counters */
+
+{0x6E0, "TBYT", "Transmit byte counter",                       ACC_RW, 0x00000000},
+{0x6E4, "TPKT", "Transmit packet counter",                     ACC_RW, 0x00000000},
+{0x6E8, "TMCA", "Transmit multicast packet counter ",          ACC_RW, 0x00000000},
+{0x6EC, "TBCA", "Transmit broadcast packet counter ",          ACC_RW, 0x00000000},
+{0x6F0, "TXPF", "Transmit PAUSE control frame counter ",       ACC_RW, 0x00000000},
+{0x6F4, "TDFR", "Transmit deferral packet counter ",           ACC_RW, 0x00000000},
+{0x6F8, "TEDF", "Transmit excessive deferral packet counter ", ACC_RW, 0x00000000},
+{0x6FC, "TSCL", "Transmit single collision packet counter",    ACC_RW, 0x00000000},
+{0x700, "TMCL", "Transmit multiple collision packet counter",  ACC_RW, 0x00000000},
+{0x704, "TLCL", "Transmit late collision packet counter",      ACC_RW, 0x00000000},
+{0x708, "TXCL", "Transmit excessive collision packet counter", ACC_RW, 0x00000000},
+{0x70C, "TNCL", "Transmit total collision counter ",           ACC_RW, 0x00000000},
+{0x714, "TDRP", "Transmit drop frame counter",                 ACC_RW, 0x00000000},
+{0x718, "TJBR", "Transmit jabber frame counter ",              ACC_RW, 0x00000000},
+{0x71C, "TFCS", "Transmit FCS error counter",                  ACC_RW, 0x00000000},
+{0x720, "TXCF", "Transmit control frame counter ",             ACC_RW, 0x00000000},
+{0x724, "TOVR", "Transmit oversize frame counter",             ACC_RW, 0x00000000},
+{0x728, "TUND", "Transmit undersize frame counter ",           ACC_RW, 0x00000000},
+{0x72C, "TFRG", "Transmit fragments frame counter ",           ACC_RW, 0x00000000},
+
+/* eTSEC Counter Control and TOE Statistics Registers */
+
+{0x730, "CAR1", "Carry register one register",           ACC_w1c, 0x00000000},
+{0x734, "CAR2", "Carry register two register ",          ACC_w1c, 0x00000000},
+{0x738, "CAM1", "Carry register one mask register ",     ACC_RW,  0xFE03FFFF},
+{0x73C, "CAM2", "Carry register two mask register ",     ACC_RW,  0x000FFFFD},
+{0x740, "RREJ", "Receive filer rejected packet counter", ACC_RW,  0x00000000},
+
+/* Hash Function Registers */
+
+{0x800, "IGADDR0", "Individual/group address register 0", ACC_RW, 0x00000000},
+{0x804, "IGADDR1", "Individual/group address register 1", ACC_RW, 0x00000000},
+{0x808, "IGADDR2", "Individual/group address register 2", ACC_RW, 0x00000000},
+{0x80C, "IGADDR3", "Individual/group address register 3", ACC_RW, 0x00000000},
+{0x810, "IGADDR4", "Individual/group address register 4", ACC_RW, 0x00000000},
+{0x814, "IGADDR5", "Individual/group address register 5", ACC_RW, 0x00000000},
+{0x818, "IGADDR6", "Individual/group address register 6", ACC_RW, 0x00000000},
+{0x81C, "IGADDR7", "Individual/group address register 7", ACC_RW, 0x00000000},
+{0x880, "GADDR0",  "Group address register 0",            ACC_RW, 0x00000000},
+{0x884, "GADDR1",  "Group address register 1",            ACC_RW, 0x00000000},
+{0x888, "GADDR2",  "Group address register 2",            ACC_RW, 0x00000000},
+{0x88C, "GADDR3",  "Group address register 3",            ACC_RW, 0x00000000},
+{0x890, "GADDR4",  "Group address register 4",            ACC_RW, 0x00000000},
+{0x894, "GADDR5",  "Group address register 5",            ACC_RW, 0x00000000},
+{0x898, "GADDR6",  "Group address register 6",            ACC_RW, 0x00000000},
+{0x89C, "GADDR7",  "Group address register 7",            ACC_RW, 0x00000000},
+
+/* eTSEC DMA Attribute Registers */
+
+{0xBF8, "ATTR",    "Attribute register",                                  ACC_RW, 0x00000000},
+{0xBFC, "ATTRELI", "Attribute extract length and extract index register", ACC_RW, 0x00000000},
+
+
+/* eTSEC Lossless Flow Control Registers */
+
+{0xC00, "RQPRM0",  "Receive Queue Parameters register 0 ", ACC_RW, 0x00000000},
+{0xC04, "RQPRM1",  "Receive Queue Parameters register 1 ", ACC_RW, 0x00000000},
+{0xC08, "RQPRM2",  "Receive Queue Parameters register 2 ", ACC_RW, 0x00000000},
+{0xC0C, "RQPRM3",  "Receive Queue Parameters register 3 ", ACC_RW, 0x00000000},
+{0xC10, "RQPRM4",  "Receive Queue Parameters register 4 ", ACC_RW, 0x00000000},
+{0xC14, "RQPRM5",  "Receive Queue Parameters register 5 ", ACC_RW, 0x00000000},
+{0xC18, "RQPRM6",  "Receive Queue Parameters register 6 ", ACC_RW, 0x00000000},
+{0xC1C, "RQPRM7",  "Receive Queue Parameters register 7 ", ACC_RW, 0x00000000},
+{0xC44, "RFBPTR0", "Last Free RxBD pointer for ring 0",    ACC_RW, 0x00000000},
+{0xC4C, "RFBPTR1", "Last Free RxBD pointer for ring 1",    ACC_RW, 0x00000000},
+{0xC54, "RFBPTR2", "Last Free RxBD pointer for ring 2",    ACC_RW, 0x00000000},
+{0xC5C, "RFBPTR3", "Last Free RxBD pointer for ring 3",    ACC_RW, 0x00000000},
+{0xC64, "RFBPTR4", "Last Free RxBD pointer for ring 4",    ACC_RW, 0x00000000},
+{0xC6C, "RFBPTR5", "Last Free RxBD pointer for ring 5",    ACC_RW, 0x00000000},
+{0xC74, "RFBPTR6", "Last Free RxBD pointer for ring 6",    ACC_RW, 0x00000000},
+{0xC7C, "RFBPTR7", "Last Free RxBD pointer for ring 7",    ACC_RW, 0x00000000},
+
+/* eTSEC Future Expansion Space */
+
+/* Reserved*/
+
+/* eTSEC IEEE 1588 Registers */
+
+{0xE00, "TMR_CTRL",     "Timer control register",                          ACC_RW,  0x00010001},
+{0xE04, "TMR_TEVENT",   "time stamp event register",                       ACC_w1c, 0x00000000},
+{0xE08, "TMR_TEMASK",   "Timer event mask register",                       ACC_RW,  0x00000000},
+{0xE0C, "TMR_PEVENT",   "time stamp event register",                       ACC_RW,  0x00000000},
+{0xE10, "TMR_PEMASK",   "Timer event mask register",                       ACC_RW,  0x00000000},
+{0xE14, "TMR_STAT",     "time stamp status register",                      ACC_RW,  0x00000000},
+{0xE18, "TMR_CNT_H",    "timer counter high register",                     ACC_RW,  0x00000000},
+{0xE1C, "TMR_CNT_L",    "timer counter low register",                      ACC_RW,  0x00000000},
+{0xE20, "TMR_ADD",      "Timer drift compensation addend register",        ACC_RW,  0x00000000},
+{0xE24, "TMR_ACC",      "Timer accumulator register",                      ACC_RW,  0x00000000},
+{0xE28, "TMR_PRSC",     "Timer prescale",                                  ACC_RW,  0x00000002},
+{0xE30, "TMROFF_H",     "Timer offset high",                               ACC_RW,  0x00000000},
+{0xE34, "TMROFF_L",     "Timer offset low",                                ACC_RW,  0x00000000},
+{0xE40, "TMR_ALARM1_H", "Timer alarm 1 high register",                     ACC_RW,  0xFFFFFFFF},
+{0xE44, "TMR_ALARM1_L", "Timer alarm 1 high register",                     ACC_RW,  0xFFFFFFFF},
+{0xE48, "TMR_ALARM2_H", "Timer alarm 2 high register",                     ACC_RW,  0xFFFFFFFF},
+{0xE4C, "TMR_ALARM2_L", "Timer alarm 2 high register",                     ACC_RW,  0xFFFFFFFF},
+{0xE80, "TMR_FIPER1",   "Timer fixed period interval",                     ACC_RW,  0xFFFFFFFF},
+{0xE84, "TMR_FIPER2",   "Timer fixed period interval",                     ACC_RW,  0xFFFFFFFF},
+{0xE88, "TMR_FIPER3",   "Timer fixed period interval",                     ACC_RW,  0xFFFFFFFF},
+{0xEA0, "TMR_ETTS1_H",  "Time stamp of general purpose external trigger ", ACC_RW,  0x00000000},
+{0xEA4, "TMR_ETTS1_L",  "Time stamp of general purpose external trigger",  ACC_RW,  0x00000000},
+{0xEA8, "TMR_ETTS2_H",  "Time stamp of general purpose external trigger ", ACC_RW,  0x00000000},
+{0xEAC, "TMR_ETTS2_L",  "Time stamp of general purpose external trigger",  ACC_RW,  0x00000000},
+
+/* End Of Table */
+{0x0, 0x0, 0x0, 0x0, 0x0}
+};
diff --git a/hw/net/etsec_registers.h b/hw/net/etsec_registers.h
new file mode 100644
index 0000000..7faeaa9
--- /dev/null
+++ b/hw/net/etsec_registers.h
@@ -0,0 +1,302 @@ 
+/*
+ * QEMU Freescale eTSEC Emulator
+ *
+ * Copyright (c) 2011-2013 AdaCore
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef _ETSEC_REGISTERS_H_
+#define _ETSEC_REGISTERS_H_
+
+#include <stdint.h>
+
+typedef struct eTSEC_Register_Definition {
+    uint32_t offset;
+    const char *name;
+    const char *desc;
+    uint32_t access;
+    uint32_t reset;
+} eTSEC_Register_Definition;
+
+#define ACC_RW      1           /* Read/Write */
+#define ACC_RO      2           /* Read Only */
+#define ACC_WO      3           /* Write Only */
+#define ACC_w1c     4           /* Write 1 to clear */
+#define ACC_UNKNOWN 4           /* Unknown register*/
+
+extern const eTSEC_Register_Definition eTSEC_registers_def[];
+
+#define DMACTRL_LE  (1 << 15)
+#define DMACTRL_GRS (1 <<  4)
+#define DMACTRL_GTS (1 <<  3)
+#define DMACTRL_WOP (1 <<  0)
+
+#define IEVENT_RXF  (1 <<  7)
+#define IEVENT_GRSC (1 <<  8)
+#define IEVENT_RXB  (1 << 15)
+#define IEVENT_TXF  (1 << 20)
+#define IEVENT_TXB  (1 << 21)
+#define IEVENT_TXC  (1 << 23)
+#define IEVENT_GTSC (1 << 25)
+#define IEVENT_BSY  (1 << 29)
+#define IEVENT_RXC  (1 << 30)
+
+#define IMASK_RXFEN  (1 <<  7)
+#define IMASK_GRSCEN (1 <<  8)
+#define IMASK_RXBEN  (1 << 15)
+#define IMASK_TXFEN  (1 << 20)
+#define IMASK_TXBEN  (1 << 21)
+#define IMASK_GTSCEN (1 << 25)
+
+#define MACCFG1_TX_EN  (1 << 0)
+#define MACCFG1_RX_EN  (1 << 2)
+
+#define MACCFG2_CRC_EN  (1 << 1)
+#define MACCFG2_PADCRC  (1 << 2)
+
+#define MIIMCOM_READ (1 << 0)
+#define MIIMCOM_SCAN (1 << 1)
+
+#define RCTRL_PRSDEP_MASK   (0x3)
+#define RCTRL_PRSDEP_OFFSET (6)
+#define RCTRL_RSF           (1 << 2)
+
+/* Index of each register */
+
+#define TSEC_ID      (0x000 / 4)
+#define TSEC_ID2     (0x004 / 4)
+#define IEVENT       (0x010 / 4)
+#define IMASK        (0x014 / 4)
+#define EDIS         (0x018 / 4)
+#define ECNTRL       (0x020 / 4)
+#define PTV          (0x028 / 4)
+#define DMACTRL      (0x02C / 4)
+#define TBIPA        (0x030 / 4)
+#define TCTRL        (0x100 / 4)
+#define TSTAT        (0x104 / 4)
+#define DFVLAN       (0x108 / 4)
+#define TXIC         (0x110 / 4)
+#define TQUEUE       (0x114 / 4)
+#define TR03WT       (0x140 / 4)
+#define TR47WT       (0x144 / 4)
+#define TBDBPH       (0x180 / 4)
+#define TBPTR0       (0x184 / 4)
+#define TBPTR1       (0x18C / 4)
+#define TBPTR2       (0x194 / 4)
+#define TBPTR3       (0x19C / 4)
+#define TBPTR4       (0x1A4 / 4)
+#define TBPTR5       (0x1AC / 4)
+#define TBPTR6       (0x1B4 / 4)
+#define TBPTR7       (0x1BC / 4)
+#define TBASEH       (0x200 / 4)
+#define TBASE0       (0x204 / 4)
+#define TBASE1       (0x20C / 4)
+#define TBASE2       (0x214 / 4)
+#define TBASE3       (0x21C / 4)
+#define TBASE4       (0x224 / 4)
+#define TBASE5       (0x22C / 4)
+#define TBASE6       (0x234 / 4)
+#define TBASE7       (0x23C / 4)
+#define TMR_TXTS1_ID (0x280 / 4)
+#define TMR_TXTS2_ID (0x284 / 4)
+#define TMR_TXTS1_H  (0x2C0 / 4)
+#define TMR_TXTS1_L  (0x2C4 / 4)
+#define TMR_TXTS2_H  (0x2C8 / 4)
+#define TMR_TXTS2_L  (0x2CC / 4)
+#define RCTRL        (0x300 / 4)
+#define RSTAT        (0x304 / 4)
+#define RXIC         (0x310 / 4)
+#define RQUEUE       (0x314 / 4)
+#define RBIFX        (0x330 / 4)
+#define RQFAR        (0x334 / 4)
+#define RQFCR        (0x338 / 4)
+#define RQFPR        (0x33C / 4)
+#define MRBLR        (0x340 / 4)
+#define RBDBPH       (0x380 / 4)
+#define RBPTR0       (0x384 / 4)
+#define RBPTR1       (0x38C / 4)
+#define RBPTR2       (0x394 / 4)
+#define RBPTR3       (0x39C / 4)
+#define RBPTR4       (0x3A4 / 4)
+#define RBPTR5       (0x3AC / 4)
+#define RBPTR6       (0x3B4 / 4)
+#define RBPTR7       (0x3BC / 4)
+#define RBASEH       (0x400 / 4)
+#define RBASE0       (0x404 / 4)
+#define RBASE1       (0x40C / 4)
+#define RBASE2       (0x414 / 4)
+#define RBASE3       (0x41C / 4)
+#define RBASE4       (0x424 / 4)
+#define RBASE5       (0x42C / 4)
+#define RBASE6       (0x434 / 4)
+#define RBASE7       (0x43C / 4)
+#define TMR_RXTS_H   (0x4C0 / 4)
+#define TMR_RXTS_L   (0x4C4 / 4)
+#define MACCFG1      (0x500 / 4)
+#define MACCFG2      (0x504 / 4)
+#define IPGIFG       (0x508 / 4)
+#define HAFDUP       (0x50C / 4)
+#define MAXFRM       (0x510 / 4)
+#define MIIMCFG      (0x520 / 4)
+#define MIIMCOM      (0x524 / 4)
+#define MIIMADD      (0x528 / 4)
+#define MIIMCON      (0x52C / 4)
+#define MIIMSTAT     (0x530 / 4)
+#define MIIMIND      (0x534 / 4)
+#define IFSTAT       (0x53C / 4)
+#define MACSTNADDR1  (0x540 / 4)
+#define MACSTNADDR2  (0x544 / 4)
+#define MAC01ADDR1   (0x548 / 4)
+#define MAC01ADDR2   (0x54C / 4)
+#define MAC02ADDR1   (0x550 / 4)
+#define MAC02ADDR2   (0x554 / 4)
+#define MAC03ADDR1   (0x558 / 4)
+#define MAC03ADDR2   (0x55C / 4)
+#define MAC04ADDR1   (0x560 / 4)
+#define MAC04ADDR2   (0x564 / 4)
+#define MAC05ADDR1   (0x568 / 4)
+#define MAC05ADDR2   (0x56C / 4)
+#define MAC06ADDR1   (0x570 / 4)
+#define MAC06ADDR2   (0x574 / 4)
+#define MAC07ADDR1   (0x578 / 4)
+#define MAC07ADDR2   (0x57C / 4)
+#define MAC08ADDR1   (0x580 / 4)
+#define MAC08ADDR2   (0x584 / 4)
+#define MAC09ADDR1   (0x588 / 4)
+#define MAC09ADDR2   (0x58C / 4)
+#define MAC10ADDR1   (0x590 / 4)
+#define MAC10ADDR2   (0x594 / 4)
+#define MAC11ADDR1   (0x598 / 4)
+#define MAC11ADDR2   (0x59C / 4)
+#define MAC12ADDR1   (0x5A0 / 4)
+#define MAC12ADDR2   (0x5A4 / 4)
+#define MAC13ADDR1   (0x5A8 / 4)
+#define MAC13ADDR2   (0x5AC / 4)
+#define MAC14ADDR1   (0x5B0 / 4)
+#define MAC14ADDR2   (0x5B4 / 4)
+#define MAC15ADDR1   (0x5B8 / 4)
+#define MAC15ADDR2   (0x5BC / 4)
+#define TR64         (0x680 / 4)
+#define TR127        (0x684 / 4)
+#define TR255        (0x688 / 4)
+#define TR511        (0x68C / 4)
+#define TR1K         (0x690 / 4)
+#define TRMAX        (0x694 / 4)
+#define TRMGV        (0x698 / 4)
+#define RBYT         (0x69C / 4)
+#define RPKT         (0x6A0 / 4)
+#define RFCS         (0x6A4 / 4)
+#define RMCA         (0x6A8 / 4)
+#define RBCA         (0x6AC / 4)
+#define RXCF         (0x6B0 / 4)
+#define RXPF         (0x6B4 / 4)
+#define RXUO         (0x6B8 / 4)
+#define RALN         (0x6BC / 4)
+#define RFLR         (0x6C0 / 4)
+#define RCDE         (0x6C4 / 4)
+#define RCSE         (0x6C8 / 4)
+#define RUND         (0x6CC / 4)
+#define ROVR         (0x6D0 / 4)
+#define RFRG         (0x6D4 / 4)
+#define RJBR         (0x6D8 / 4)
+#define RDRP         (0x6DC / 4)
+#define TBYT         (0x6E0 / 4)
+#define TPKT         (0x6E4 / 4)
+#define TMCA         (0x6E8 / 4)
+#define TBCA         (0x6EC / 4)
+#define TXPF         (0x6F0 / 4)
+#define TDFR         (0x6F4 / 4)
+#define TEDF         (0x6F8 / 4)
+#define TSCL         (0x6FC / 4)
+#define TMCL         (0x700 / 4)
+#define TLCL         (0x704 / 4)
+#define TXCL         (0x708 / 4)
+#define TNCL         (0x70C / 4)
+#define TDRP         (0x714 / 4)
+#define TJBR         (0x718 / 4)
+#define TFCS         (0x71C / 4)
+#define TXCF         (0x720 / 4)
+#define TOVR         (0x724 / 4)
+#define TUND         (0x728 / 4)
+#define TFRG         (0x72C / 4)
+#define CAR1         (0x730 / 4)
+#define CAR2         (0x734 / 4)
+#define CAM1         (0x738 / 4)
+#define CAM2         (0x73C / 4)
+#define RREJ         (0x740 / 4)
+#define IGADDR0      (0x800 / 4)
+#define IGADDR1      (0x804 / 4)
+#define IGADDR2      (0x808 / 4)
+#define IGADDR3      (0x80C / 4)
+#define IGADDR4      (0x810 / 4)
+#define IGADDR5      (0x814 / 4)
+#define IGADDR6      (0x818 / 4)
+#define IGADDR7      (0x81C / 4)
+#define GADDR0       (0x880 / 4)
+#define GADDR1       (0x884 / 4)
+#define GADDR2       (0x888 / 4)
+#define GADDR3       (0x88C / 4)
+#define GADDR4       (0x890 / 4)
+#define GADDR5       (0x894 / 4)
+#define GADDR6       (0x898 / 4)
+#define GADDR7       (0x89C / 4)
+#define ATTR         (0xBF8 / 4)
+#define ATTRELI      (0xBFC / 4)
+#define RQPRM0       (0xC00 / 4)
+#define RQPRM1       (0xC04 / 4)
+#define RQPRM2       (0xC08 / 4)
+#define RQPRM3       (0xC0C / 4)
+#define RQPRM4       (0xC10 / 4)
+#define RQPRM5       (0xC14 / 4)
+#define RQPRM6       (0xC18 / 4)
+#define RQPRM7       (0xC1C / 4)
+#define RFBPTR0      (0xC44 / 4)
+#define RFBPTR1      (0xC4C / 4)
+#define RFBPTR2      (0xC54 / 4)
+#define RFBPTR3      (0xC5C / 4)
+#define RFBPTR4      (0xC64 / 4)
+#define RFBPTR5      (0xC6C / 4)
+#define RFBPTR6      (0xC74 / 4)
+#define RFBPTR7      (0xC7C / 4)
+#define TMR_CTRL     (0xE00 / 4)
+#define TMR_TEVENT   (0xE04 / 4)
+#define TMR_TEMASK   (0xE08 / 4)
+#define TMR_PEVENT   (0xE0C / 4)
+#define TMR_PEMASK   (0xE10 / 4)
+#define TMR_STAT     (0xE14 / 4)
+#define TMR_CNT_H    (0xE18 / 4)
+#define TMR_CNT_L    (0xE1C / 4)
+#define TMR_ADD      (0xE20 / 4)
+#define TMR_ACC      (0xE24 / 4)
+#define TMR_PRSC     (0xE28 / 4)
+#define TMROFF_H     (0xE30 / 4)
+#define TMROFF_L     (0xE34 / 4)
+#define TMR_ALARM1_H (0xE40 / 4)
+#define TMR_ALARM1_L (0xE44 / 4)
+#define TMR_ALARM2_H (0xE48 / 4)
+#define TMR_ALARM2_L (0xE4C / 4)
+#define TMR_FIPER1   (0xE80 / 4)
+#define TMR_FIPER2   (0xE84 / 4)
+#define TMR_FIPER3   (0xE88 / 4)
+#define TMR_ETTS1_H  (0xEA0 / 4)
+#define TMR_ETTS1_L  (0xEA4 / 4)
+#define TMR_ETTS2_H  (0xEA8 / 4)
+#define TMR_ETTS2_L  (0xEAC / 4)
+
+#endif /* ! _ETSEC_REGISTERS_H_ */
diff --git a/hw/net/etsec_rings.c b/hw/net/etsec_rings.c
new file mode 100644
index 0000000..15c0616
--- /dev/null
+++ b/hw/net/etsec_rings.c
@@ -0,0 +1,673 @@ 
+/*
+ * QEMU Freescale eTSEC Emulator
+ *
+ * Copyright (c) 2011-2013 AdaCore
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/bswap.h"
+#include "net/checksum.h"
+
+#include "etsec.h"
+#include "etsec_registers.h"
+
+/* #define ETSEC_RING_DEBUG */
+/* #define HEX_DUMP */
+/* #define DEBUG_BD */
+
+#ifdef ETSEC_RING_DEBUG
+#define RING_DEBUG(fmt, ...) printf("%s:%s " fmt, __func__ ,\
+                                    etsec->nic->nc.name, ## __VA_ARGS__)
+#else
+#define RING_DEBUG(...)
+#endif  /* ETSEC_RING_DEBUG */
+
+#define RING_DEBUG_A(fmt, ...) printf("%s:%s " fmt, __func__ ,\
+                                      etsec->nic->nc.name, ## __VA_ARGS__)
+
+#ifdef DEBUG_BD
+
+static void print_tx_bd_flags(uint16_t flags)
+{
+    printf("      Ready: %d\n", !!(flags & BD_TX_READY));
+    printf("      PAD/CRC: %d\n", !!(flags & BD_TX_PADCRC));
+    printf("      Wrap: %d\n", !!(flags & BD_WRAP));
+    printf("      Interrupt: %d\n", !!(flags & BD_INTERRUPT));
+    printf("      Last in frame: %d\n", !!(flags & BD_LAST));
+    printf("      Tx CRC: %d\n", !!(flags & BD_TX_TC));
+    printf("      User-defined preamble / defer: %d\n",
+           !!(flags & BD_TX_PREDEF));
+    printf("      Huge frame enable / Late collision: %d\n",
+           !!(flags & BD_TX_HFELC));
+    printf("      Control frame / Retransmission Limit: %d\n",
+           !!(flags & BD_TX_CFRL));
+    printf("      Retry count: %d\n",
+           (flags >> BD_TX_RC_OFFSET) & BD_TX_RC_MASK);
+    printf("      Underrun / TCP/IP off-load enable: %d\n",
+           !!(flags & BD_TX_TOEUN));
+    printf("      Truncation: %d\n", !!(flags & BD_TX_TR));
+}
+
+static void print_rx_bd_flags(uint16_t flags)
+{
+    printf("      Empty: %d\n", !!(flags & BD_RX_EMPTY));
+    printf("      Receive software ownership: %d\n", !!(flags & BD_RX_RO1));
+    printf("      Wrap: %d\n", !!(flags & BD_WRAP));
+    printf("      Interrupt: %d\n", !!(flags & BD_INTERRUPT));
+    printf("      Last in frame: %d\n", !!(flags & BD_LAST));
+    printf("      First in frame: %d\n", !!(flags & BD_RX_FIRST));
+    printf("      Miss: %d\n", !!(flags & BD_RX_MISS));
+    printf("      Broadcast: %d\n", !!(flags & BD_RX_BROADCAST));
+    printf("      Multicast: %d\n", !!(flags & BD_RX_MULTICAST));
+    printf("      Rx frame length violation: %d\n", !!(flags & BD_RX_LG));
+    printf("      Rx non-octet aligned frame: %d\n", !!(flags & BD_RX_NO));
+    printf("      Short frame: %d\n", !!(flags & BD_RX_SH));
+    printf("      Rx CRC Error: %d\n", !!(flags & BD_RX_CR));
+    printf("      Overrun: %d\n", !!(flags & BD_RX_OV));
+    printf("      Truncation: %d\n", !!(flags & BD_RX_TR));
+}
+
+
+static void print_bd(eTSEC_rxtx_bd bd, int mode, uint32_t index)
+{
+    printf("eTSEC %s Data Buffer Descriptor (%u)\n",
+           mode == eTSEC_TRANSMIT ? "Transmit" : "Receive",
+           index);
+    printf("   Flags   : 0x%04x\n", bd.flags);
+    if (mode == eTSEC_TRANSMIT) {
+        print_tx_bd_flags(bd.flags);
+    } else {
+        print_rx_bd_flags(bd.flags);
+    }
+    printf("   Length  : 0x%04x\n", bd.length);
+    printf("   Pointer : 0x%08x\n", bd.bufptr);
+}
+
+#endif  /* DEBUG_BD */
+
+#ifdef HEX_DUMP
+
+static void hex_dump(FILE *f, const uint8_t *buf, int size)
+{
+    int len, i, j, c;
+
+    for (i = 0; i < size; i += 16) {
+        len = size - i;
+        if (len > 16) {
+            len = 16;
+        }
+        fprintf(f, "%08x ", i);
+        for (j = 0; j < 16; j++) {
+            if (j < len) {
+                fprintf(f, " %02x", buf[i + j]);
+            } else {
+                fprintf(f, "   ");
+            }
+        }
+        fprintf(f, " ");
+        for (j = 0; j < len; j++) {
+            c = buf[i + j];
+            if (c < ' ' || c > '~') {
+                c = '.';
+            }
+            fprintf(f, "%c", c);
+        }
+        fprintf(f, "\n");
+    }
+}
+
+#endif
+
+static void read_buffer_descriptor(eTSEC         *etsec,
+                                   hwaddr         addr,
+                                   eTSEC_rxtx_bd *bd)
+{
+    assert(bd != NULL);
+
+    RING_DEBUG("READ Buffer Descriptor @ 0x" TARGET_FMT_plx"\n", addr);
+    cpu_physical_memory_read(addr,
+                             bd,
+                             sizeof(eTSEC_rxtx_bd));
+
+    if (etsec->regs[DMACTRL].value & DMACTRL_LE) {
+        bd->flags  = le16_to_cpupu(&bd->flags);
+        bd->length = le16_to_cpupu(&bd->length);
+        bd->bufptr = le32_to_cpupu(&bd->bufptr);
+    } else {
+        bd->flags  = be16_to_cpupu(&bd->flags);
+        bd->length = be16_to_cpupu(&bd->length);
+        bd->bufptr = be32_to_cpupu(&bd->bufptr);
+    }
+}
+
+static void write_buffer_descriptor(eTSEC         *etsec,
+                                    hwaddr         addr,
+                                    eTSEC_rxtx_bd *bd)
+{
+    assert(bd != NULL);
+
+    if (etsec->regs[DMACTRL].value & DMACTRL_LE) {
+        cpu_to_le16wu(&bd->flags, bd->flags);
+        cpu_to_le16wu(&bd->length, bd->length);
+        cpu_to_le32wu(&bd->bufptr, bd->bufptr);
+    } else {
+        cpu_to_be16wu(&bd->flags, bd->flags);
+        cpu_to_be16wu(&bd->length, bd->length);
+        cpu_to_be32wu(&bd->bufptr, bd->bufptr);
+    }
+
+    RING_DEBUG("Write Buffer Descriptor @ 0x" TARGET_FMT_plx"\n", addr);
+    cpu_physical_memory_write(addr,
+                              bd,
+                              sizeof(eTSEC_rxtx_bd));
+}
+
+static void ievent_set(eTSEC    *etsec,
+                       uint32_t  flags)
+{
+    etsec->regs[IEVENT].value |= flags;
+
+    if ((flags & IEVENT_TXB && etsec->regs[IMASK].value & IMASK_TXBEN)
+        || (flags & IEVENT_TXF && etsec->regs[IMASK].value & IMASK_TXFEN)) {
+        qemu_irq_raise(etsec->tx_irq);
+        RING_DEBUG("%s Raise Tx IRQ\n", __func__);
+    }
+
+    if ((flags & IEVENT_RXB && etsec->regs[IMASK].value & IMASK_RXBEN)
+        || (flags & IEVENT_RXF && etsec->regs[IMASK].value & IMASK_RXFEN)) {
+        qemu_irq_pulse(etsec->rx_irq);
+        RING_DEBUG("%s Raise Rx IRQ\n", __func__);
+    }
+}
+
+static void tx_padding_and_crc(eTSEC *etsec, uint32_t min_frame_len)
+{
+    int add = min_frame_len - etsec->tx_buffer_len;
+
+    /* Padding */
+    if (add > 0) {
+        RING_DEBUG("pad:%u\n", add);
+        etsec->tx_buffer = g_realloc(etsec->tx_buffer,
+                                        etsec->tx_buffer_len + add);
+
+        memset(etsec->tx_buffer + etsec->tx_buffer_len, 0x0, add);
+        etsec->tx_buffer_len += add;
+    }
+
+    /* Never add CRC in QEMU */
+}
+
+static void process_tx_fcb(eTSEC *etsec)
+{
+    uint8_t flags = (uint8_t)(*etsec->tx_buffer);
+    /* L3 header offset from start of frame */
+    uint8_t l3_header_offset = (uint8_t)*(etsec->tx_buffer + 3);
+    /* L4 header offset from start of L3 header */
+    uint8_t l4_header_offset = (uint8_t)*(etsec->tx_buffer + 2);
+    /* L3 header */
+    uint8_t *l3_header = etsec->tx_buffer + 8 + l3_header_offset;
+    /* L4 header */
+    uint8_t *l4_header = l3_header + l4_header_offset;
+
+    /* if packet is IP4 and IP checksum is requested */
+    if (flags & FCB_TX_IP && flags & FCB_TX_CIP) {
+        /* do IP4 checksum (TODO This funtion does TCP/UDP checksum but not sure
+         * if it also does IP4 checksum. */
+        net_checksum_calculate(etsec->tx_buffer + 8,
+                etsec->tx_buffer_len - 8);
+    }
+    /* TODO Check the correct usage of the PHCS field of the FCB in case the NPH
+     * flag is on */
+
+    /* if packet is IP4 and TCP or UDP */
+    if (flags & FCB_TX_IP && flags & FCB_TX_TUP) {
+        /* if UDP */
+        if (flags & FCB_TX_UDP) {
+            /* if checksum is requested */
+            if (flags & FCB_TX_CTU) {
+                /* do UDP checksum */
+
+                net_checksum_calculate(etsec->tx_buffer + 8,
+                        etsec->tx_buffer_len - 8);
+            } else {
+                /* set checksum field to 0 */
+                l4_header[6] = 0;
+                l4_header[7] = 0;
+            }
+        } else if (flags & FCB_TX_CTU) { /* if TCP and checksum is requested */
+            /* do TCP checksum */
+            net_checksum_calculate(etsec->tx_buffer + 8,
+                                   etsec->tx_buffer_len - 8);
+        }
+    }
+}
+
+static void process_tx_bd(eTSEC         *etsec,
+                          eTSEC_rxtx_bd *bd)
+{
+    uint8_t *tmp_buff = NULL;
+
+    if (bd->length == 0) {
+        /* ERROR */
+        return;
+    }
+
+    if (etsec->tx_buffer_len == 0) {
+        /* It's the first BD */
+        etsec->first_bd = *bd;
+    }
+
+    /* TODO: if TxBD[TOE/UN] skip the Tx Frame Control Block*/
+
+    /* Load this Data Buffer */
+    etsec->tx_buffer = g_realloc(etsec->tx_buffer,
+                                    etsec->tx_buffer_len + bd->length);
+    tmp_buff = etsec->tx_buffer + etsec->tx_buffer_len;
+    cpu_physical_memory_read(bd->bufptr, tmp_buff, bd->length);
+
+    /* Update buffer length */
+    etsec->tx_buffer_len += bd->length;
+
+
+    if (etsec->tx_buffer_len != 0 && (bd->flags & BD_LAST)) {
+        if (etsec->regs[MACCFG1].value & MACCFG1_TX_EN) {
+            /* MAC Transmit enabled */
+
+            /* Process offload Tx FCB */
+            if (etsec->first_bd.flags & BD_TX_TOEUN) {
+                process_tx_fcb(etsec);
+            }
+
+            if (etsec->first_bd.flags & BD_TX_PADCRC
+                || etsec->regs[MACCFG2].value & MACCFG2_PADCRC) {
+
+                /* Padding and CRC (Padding implies CRC) */
+                tx_padding_and_crc(etsec, 64);
+
+            } else if (etsec->first_bd.flags & BD_TX_TC
+                       || etsec->regs[MACCFG2].value & MACCFG2_CRC_EN) {
+
+                /* Only CRC */
+                /* Never add CRC in QEMU */
+            }
+
+#if defined(HEX_DUMP)
+            fprintf(stderr, "eTSEC Send packet size:%d\n",
+                    etsec->tx_buffer_len);
+            hex_dump(stderr, etsec->tx_buffer, etsec->tx_buffer_len);
+#endif  /* ETSEC_RING_DEBUG */
+
+            if (etsec->first_bd.flags & BD_TX_TOEUN) {
+                qemu_send_packet(qemu_get_queue(etsec->nic),
+                        etsec->tx_buffer + 8,
+                        etsec->tx_buffer_len - 8);
+            } else {
+                qemu_send_packet(qemu_get_queue(etsec->nic),
+                        etsec->tx_buffer,
+                        etsec->tx_buffer_len);
+            }
+
+        }
+
+        etsec->tx_buffer_len = 0;
+
+        if (bd->flags & BD_INTERRUPT) {
+            ievent_set(etsec, IEVENT_TXF);
+        }
+    } else {
+        if (bd->flags & BD_INTERRUPT) {
+            ievent_set(etsec, IEVENT_TXB);
+        }
+    }
+
+    /* Update DB flags */
+
+    /* Clear Ready */
+    bd->flags &= ~BD_TX_READY;
+
+    /* Clear Defer */
+    bd->flags &= ~BD_TX_PREDEF;
+
+    /* Clear Late Collision */
+    bd->flags &= ~BD_TX_HFELC;
+
+    /* Clear Retransmission Limit */
+    bd->flags &= ~BD_TX_CFRL;
+
+    /* Clear Retry Count */
+    bd->flags &= ~(BD_TX_RC_MASK << BD_TX_RC_OFFSET);
+
+    /* Clear Underrun */
+    bd->flags &= ~BD_TX_TOEUN;
+
+    /* Clear Truncation */
+    bd->flags &= ~BD_TX_TR;
+}
+
+void walk_tx_ring(eTSEC *etsec, int ring_nbr)
+{
+    hwaddr        ring_base = 0;
+    hwaddr        bd_addr   = 0;
+    eTSEC_rxtx_bd bd;
+    uint16_t      bd_flags;
+
+    if (!(etsec->regs[MACCFG1].value & MACCFG1_TX_EN)) {
+        RING_DEBUG("%s: MAC Transmit not enabled\n", __func__);
+        return;
+    }
+
+    /* ring_base = (etsec->regs[TBASEH].value & 0xF) << 32; */
+    ring_base += etsec->regs[TBASE0 + ring_nbr].value & ~0x7;
+    bd_addr    = etsec->regs[TBPTR0 + ring_nbr].value & ~0x7;
+
+    do {
+        read_buffer_descriptor(etsec, bd_addr, &bd);
+
+#ifdef DEBUG_BD
+        print_bd(bd,
+                 eTSEC_TRANSMIT,
+                 (bd_addr - ring_base) / sizeof(eTSEC_rxtx_bd));
+
+#endif  /* DEBUG_BD */
+
+        /* Save flags before BD update */
+        bd_flags = bd.flags;
+
+        if (bd_flags & BD_TX_READY) {
+            process_tx_bd(etsec, &bd);
+
+            /* Write back BD after update */
+            write_buffer_descriptor(etsec, bd_addr, &bd);
+        }
+
+        /* Wrap or next BD */
+        if (bd_flags & BD_WRAP) {
+            bd_addr = ring_base;
+        } else {
+            bd_addr += sizeof(eTSEC_rxtx_bd);
+        }
+
+    } while (bd_addr != ring_base);
+
+    bd_addr = ring_base;
+
+    /* Save the Buffer Descriptor Pointers to current bd */
+    etsec->regs[TBPTR0 + ring_nbr].value = bd_addr;
+
+    /* Set transmit halt THLTx */
+    etsec->regs[TSTAT].value |= 1 << (31 - ring_nbr);
+}
+
+static void fill_rx_bd(eTSEC          *etsec,
+                       eTSEC_rxtx_bd  *bd,
+                       const uint8_t **buf,
+                       size_t         *size)
+{
+    uint16_t to_write = MIN(etsec->rx_fcb_size + *size - etsec->rx_padding,
+                            etsec->regs[MRBLR].value);
+    uint32_t bufptr   = bd->bufptr;
+    uint8_t  padd[etsec->rx_padding];
+    uint8_t  rem;
+
+    RING_DEBUG("eTSEC fill Rx buffer @ 0x%08x"
+               " size:%u(padding + crc:%u) + fcb:%u\n",
+               bufptr, *size, etsec->rx_padding, etsec->rx_fcb_size);
+
+    bd->length = 0;
+    if (etsec->rx_fcb_size != 0) {
+        cpu_physical_memory_write(bufptr, etsec->rx_fcb, etsec->rx_fcb_size);
+
+        bufptr             += etsec->rx_fcb_size;
+        bd->length         += etsec->rx_fcb_size;
+        to_write           -= etsec->rx_fcb_size;
+        etsec->rx_fcb_size  = 0;
+
+    }
+
+    if (to_write > 0) {
+        cpu_physical_memory_write(bufptr, *buf, to_write);
+
+        *buf   += to_write;
+        bufptr += to_write;
+        *size  -= to_write;
+
+        bd->flags  &= ~BD_RX_EMPTY;
+        bd->length += to_write;
+    }
+
+    if (*size == etsec->rx_padding) {
+        /* The remaining bytes are for padding which is not actually allocated
+           in the buffer */
+
+        rem = MIN(etsec->regs[MRBLR].value - bd->length, etsec->rx_padding);
+
+        if (rem > 0) {
+            memset(padd, 0x0, sizeof(padd));
+            etsec->rx_padding -= rem;
+            *size             -= rem;
+            bd->length        += rem;
+            cpu_physical_memory_write(bufptr, padd, rem);
+        }
+    }
+}
+
+static void rx_init_frame(eTSEC *etsec, const uint8_t *buf, size_t size)
+{
+    uint32_t fcb_size = 0;
+    uint8_t  prsdep   = (etsec->regs[RCTRL].value >> RCTRL_PRSDEP_OFFSET)
+        & RCTRL_PRSDEP_MASK;
+
+    if (prsdep != 0) {
+        /* Prepend FCB */
+        fcb_size = 8 + 2;          /* FCB size + align */
+        /* I can't find this 2 bytes alignement in fsl documentation but VxWorks
+           expects them */
+
+        etsec->rx_fcb_size = fcb_size;
+
+        /* TODO: fill_FCB(etsec); */
+        memset(etsec->rx_fcb, 0x0, sizeof(etsec->rx_fcb));
+
+    } else {
+        etsec->rx_fcb_size = 0;
+    }
+
+    if (etsec->rx_buffer != NULL) {
+        g_free(etsec->rx_buffer);
+    }
+
+    /* Do not copy the frame for now */
+    etsec->rx_buffer     = (uint8_t *)buf;
+    etsec->rx_buffer_len = size;
+    etsec->rx_padding    = 4;
+
+    if (size < 60) {
+        etsec->rx_padding += 60 - size;
+    }
+
+    etsec->rx_first_in_frame = 1;
+    etsec->rx_remaining_data = etsec->rx_buffer_len;
+    RING_DEBUG("%s: rx_buffer_len:%u rx_padding+crc:%u\n", __func__,
+               etsec->rx_buffer_len, etsec->rx_padding);
+}
+
+void rx_ring_write(eTSEC *etsec, const uint8_t *buf, size_t size)
+{
+    int ring_nbr = 0;           /* Always use ring0 (no filer) */
+
+    if (etsec->rx_buffer_len != 0) {
+        RING_DEBUG("%s: We can't receive now,"
+                   " a buffer is already in the pipe\n", __func__);
+        return;
+    }
+
+    if (etsec->regs[RSTAT].value & 1 << (23 - ring_nbr)) {
+        RING_DEBUG("%s: The ring is halted\n", __func__);
+        return;
+    }
+
+    if (etsec->regs[DMACTRL].value & DMACTRL_GRS) {
+        RING_DEBUG("%s: Graceful receive stop\n", __func__);
+        return;
+    }
+
+    if (!(etsec->regs[MACCFG1].value & MACCFG1_RX_EN)) {
+        RING_DEBUG("%s: MAC Receive not enabled\n", __func__);
+        return;
+    }
+
+    /* Don't drop short packets, just add padding (later) */
+
+    rx_init_frame(etsec, buf, size);
+
+    walk_rx_ring(etsec, ring_nbr);
+}
+
+void walk_rx_ring(eTSEC *etsec, int ring_nbr)
+{
+    hwaddr         ring_base     = 0;
+    hwaddr         bd_addr       = 0;
+    hwaddr         start_bd_addr = 0;
+    eTSEC_rxtx_bd  bd;
+    uint16_t       bd_flags;
+    size_t         remaining_data;
+    const uint8_t *buf;
+    uint8_t       *tmp_buf;
+    size_t         size;
+
+    if (etsec->rx_buffer_len == 0) {
+        /* No frame to send */
+        RING_DEBUG("No frame to send\n");
+        return;
+    }
+
+    remaining_data = etsec->rx_remaining_data + etsec->rx_padding;
+    buf            = etsec->rx_buffer
+        + (etsec->rx_buffer_len - etsec->rx_remaining_data);
+    size           = etsec->rx_buffer_len + etsec->rx_padding;
+
+    /* ring_base = (etsec->regs[RBASEH].value & 0xF) << 32; */
+    ring_base     += etsec->regs[RBASE0 + ring_nbr].value & ~0x7;
+    start_bd_addr  = bd_addr = etsec->regs[RBPTR0 + ring_nbr].value & ~0x7;
+
+    do {
+        read_buffer_descriptor(etsec, bd_addr, &bd);
+
+#ifdef DEBUG_BD
+        print_bd(bd,
+                 eTSEC_RECEIVE,
+                 (bd_addr - ring_base) / sizeof(eTSEC_rxtx_bd));
+
+#endif  /* DEBUG_BD */
+
+        /* Save flags before BD update */
+        bd_flags = bd.flags;
+
+        if (bd_flags & BD_RX_EMPTY) {
+            fill_rx_bd(etsec, &bd, &buf, &remaining_data);
+
+            if (etsec->rx_first_in_frame) {
+                bd.flags |= BD_RX_FIRST;
+                etsec->rx_first_in_frame = 0;
+                etsec->rx_first_bd = bd;
+            }
+
+            /* Last in frame */
+            if (remaining_data == 0) {
+
+                /* Clear flags */
+
+                bd.flags &= ~0x7ff;
+
+                bd.flags |= BD_LAST;
+
+                /* NOTE: non-octet aligned frame is impossible in qemu */
+
+                if (size >= etsec->regs[MAXFRM].value) {
+                    /* frame length violation */
+                    printf("%s frame length violation: size:%d MAXFRM:%d\n",
+                           __func__, size, etsec->regs[MAXFRM].value);
+
+                    bd.flags |= BD_RX_LG;
+                }
+
+                if (size  < 64) {
+                    /* Short frame */
+                    printf("%s Short frame: %d\n", __func__, size);
+                    bd.flags |= BD_RX_SH;
+                }
+
+                /* TODO: Broadcast and Multicast */
+
+                if (bd.flags | BD_INTERRUPT) {
+                    /* Set RXFx */
+                    etsec->regs[RSTAT].value |= 1 << (7 - ring_nbr);
+
+                    /* Set IEVENT */
+                    ievent_set(etsec, IEVENT_RXF);
+                }
+
+            } else {
+                if (bd.flags | BD_INTERRUPT) {
+                    /* Set IEVENT */
+                    ievent_set(etsec, IEVENT_RXB);
+                }
+            }
+
+            /* Write back BD after update */
+            write_buffer_descriptor(etsec, bd_addr, &bd);
+        }
+
+        /* Wrap or next BD */
+        if (bd_flags & BD_WRAP) {
+            bd_addr = ring_base;
+        } else {
+            bd_addr += sizeof(eTSEC_rxtx_bd);
+        }
+    } while (remaining_data != 0
+             && (bd_flags & BD_RX_EMPTY)
+             && bd_addr != start_bd_addr);
+
+    /* Reset ring ptr */
+    etsec->regs[RBPTR0 + ring_nbr].value = bd_addr;
+
+    /* The frame is too large to fit in the Rx ring */
+    if (remaining_data > 0) {
+
+        /* Set RSTAT[QHLTx] */
+        etsec->regs[RSTAT].value |= 1 << (23 - ring_nbr);
+
+        /* Save remaining data to send the end of the frame when the ring will
+         * be restarted
+         */
+        etsec->rx_remaining_data = remaining_data;
+
+        /* Copy the frame */
+        tmp_buf = g_malloc(size);
+        memcpy(tmp_buf, etsec->rx_buffer, size);
+        etsec->rx_buffer = tmp_buf;
+
+        RING_DEBUG("no empty RxBD available any more\n");
+    } else {
+        etsec->rx_buffer_len = 0;
+        etsec->rx_buffer     = NULL;
+    }
+
+    RING_DEBUG("eTSEC End of ring_write: remaining_data:%u\n", remaining_data);
+}