aboutsummaryrefslogtreecommitdiff
path: root/hw/net
diff options
context:
space:
mode:
authorFabien Chouteau <chouteau@adacore.com>2014-02-05 14:59:28 +0100
committerAlexander Graf <agraf@suse.de>2014-03-05 03:06:45 +0100
commiteb1e7c3e514665bb948357ef39845efd9af29de1 (patch)
treed3af2de96278984ca5103deaa846eccf0dea2024 /hw/net
parentb36f100e17c8e2e737b48d78c9049b96752e4adf (diff)
Add Enhanced Three-Speed Ethernet Controller (eTSEC)
This implementation doesn't include ring priority, TCP/IP Off-Load, QoS. Signed-off-by: Fabien Chouteau <chouteau@adacore.com> Signed-off-by: Alexander Graf <agraf@suse.de>
Diffstat (limited to 'hw/net')
-rw-r--r--hw/net/Makefile.objs3
-rw-r--r--hw/net/fsl_etsec/etsec.c465
-rw-r--r--hw/net/fsl_etsec/etsec.h174
-rw-r--r--hw/net/fsl_etsec/miim.c146
-rw-r--r--hw/net/fsl_etsec/registers.c295
-rw-r--r--hw/net/fsl_etsec/registers.h320
-rw-r--r--hw/net/fsl_etsec/rings.c650
7 files changed, 2053 insertions, 0 deletions
diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
index 75e80c2c48..ea93293122 100644
--- a/hw/net/Makefile.objs
+++ b/hw/net/Makefile.objs
@@ -32,3 +32,6 @@ obj-$(CONFIG_XILINX_ETHLITE) += xilinx_ethlite.o
obj-$(CONFIG_VIRTIO) += virtio-net.o
obj-y += vhost_net.o
+
+obj-$(CONFIG_ETSEC) += fsl_etsec/etsec.o fsl_etsec/registers.o \
+ fsl_etsec/rings.o fsl_etsec/miim.o
diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c
new file mode 100644
index 0000000000..d4b4429446
--- /dev/null
+++ b/hw/net/fsl_etsec/etsec.c
@@ -0,0 +1,465 @@
+/*
+ * 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.
+ */
+
+/*
+ * This implementation doesn't include ring priority, TCP/IP Off-Load, QoS.
+ */
+
+#include "sysemu/sysemu.h"
+#include "hw/sysbus.h"
+#include "trace.h"
+#include "hw/ptimer.h"
+#include "etsec.h"
+#include "registers.h"
+
+/* #define HEX_DUMP */
+/* #define DEBUG_REGISTER */
+
+#ifdef DEBUG_REGISTER
+static const int debug_etsec = 1;
+#else
+static const int debug_etsec;
+#endif
+
+#define DPRINTF(fmt, ...) do { \
+ if (debug_etsec) { \
+ qemu_log(fmt , ## __VA_ARGS__); \
+ } \
+ } while (0)
+
+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 < ETSEC_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;
+ }
+
+ DPRINTF("Read 0x%08x @ 0x" TARGET_FMT_plx
+ " : %s (%s)\n",
+ ret, addr, reg->name, reg->desc);
+
+ 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))) {
+ etsec_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)))) {
+ etsec_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)
+{
+ /* Write 1 to clear */
+ reg->value &= ~value;
+
+ if (!(reg->value & (IEVENT_TXF | IEVENT_TXF))) {
+ qemu_irq_lower(etsec->tx_irq);
+ }
+ if (!(reg->value & (IEVENT_RXF | IEVENT_RXF))) {
+ qemu_irq_lower(etsec->rx_irq);
+ }
+
+ if (!(reg->value & (IEVENT_MAG | IEVENT_GTSC | IEVENT_GRSC | IEVENT_TXC |
+ IEVENT_RXC | IEVENT_BABR | IEVENT_BABT | IEVENT_LC |
+ IEVENT_CRL | IEVENT_FGPI | IEVENT_FIR | IEVENT_FIQ |
+ IEVENT_DPE | IEVENT_PERR | IEVENT_EBERR | IEVENT_TXE |
+ IEVENT_XFUN | IEVENT_BSY | IEVENT_MSRO | IEVENT_MMRD |
+ IEVENT_MMRW))) {
+ qemu_irq_lower(etsec->err_irq);
+ }
+}
+
+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 < ETSEC_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:
+ etsec_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;
+ }
+ }
+
+ DPRINTF("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);
+}
+
+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)) {
+ etsec_walk_tx_ring(etsec, 0);
+ }
+ ptimer_set_count(etsec->ptimer, 1);
+ ptimer_run(etsec->ptimer, 1);
+ }
+}
+
+static void etsec_reset(DeviceState *d)
+{
+ eTSEC *etsec = ETSEC_COMMON(d);
+ int i = 0;
+ int reg_index = 0;
+
+ /* Default value for all registers */
+ for (i = 0; i < ETSEC_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)
+{
+ /* qemu_log("eTSEC cleanup\n"); */
+}
+
+static int etsec_can_receive(NetClientState *nc)
+{
+ eTSEC *etsec = qemu_get_nic_opaque(nc);
+
+ return etsec->rx_buffer_len == 0;
+}
+
+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);
+ qemu_hexdump(buf, stderr, "", size);
+#endif
+ etsec_rx_ring_write(etsec, buf, size);
+ return size;
+}
+
+
+static void etsec_set_link_status(NetClientState *nc)
+{
+ eTSEC *etsec = qemu_get_nic_opaque(nc);
+
+ etsec_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 void etsec_realize(DeviceState *dev, Error **errp)
+{
+ eTSEC *etsec = ETSEC_COMMON(dev);
+
+ etsec->nic = qemu_new_nic(&net_etsec_info, &etsec->conf,
+ object_get_typename(OBJECT(dev)), dev->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);
+}
+
+static void etsec_instance_init(Object *obj)
+{
+ eTSEC *etsec = ETSEC_COMMON(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+
+ memory_region_init_io(&etsec->io_area, OBJECT(etsec), &etsec_ops, etsec,
+ "eTSEC", 0x1000);
+ sysbus_init_mmio(sbd, &etsec->io_area);
+
+ sysbus_init_irq(sbd, &etsec->tx_irq);
+ sysbus_init_irq(sbd, &etsec->rx_irq);
+ sysbus_init_irq(sbd, &etsec->err_irq);
+}
+
+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);
+
+ dc->realize = etsec_realize;
+ 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,
+ .instance_init = etsec_instance_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/fsl_etsec/etsec.h b/hw/net/fsl_etsec/etsec.h
new file mode 100644
index 0000000000..78d2c57ed3
--- /dev/null
+++ b/hw/net/fsl_etsec/etsec.h
@@ -0,0 +1,174 @@
+/*
+ * 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 */
+
+/* Number of register in the device */
+#define ETSEC_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[ETSEC_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 TYPE_ETSEC_COMMON "eTSEC"
+#define ETSEC_COMMON(obj) \
+ OBJECT_CHECK(eTSEC, (obj), TYPE_ETSEC_COMMON)
+
+#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 etsec_walk_tx_ring(eTSEC *etsec, int ring_nbr);
+void etsec_walk_rx_ring(eTSEC *etsec, int ring_nbr);
+void etsec_rx_ring_write(eTSEC *etsec, const uint8_t *buf, size_t size);
+
+void etsec_write_miim(eTSEC *etsec,
+ eTSEC_Register *reg,
+ uint32_t reg_index,
+ uint32_t value);
+
+void etsec_miim_link_status(eTSEC *etsec, NetClientState *nc);
+
+#endif /* ! _ETSEC_H_ */
diff --git a/hw/net/fsl_etsec/miim.c b/hw/net/fsl_etsec/miim.c
new file mode 100644
index 0000000000..1931b74e6c
--- /dev/null
+++ b/hw/net/fsl_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 "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
+ qemu_log("%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
+ qemu_log("%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 etsec_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 etsec_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/fsl_etsec/registers.c b/hw/net/fsl_etsec/registers.c
new file mode 100644
index 0000000000..a7bbfa113f
--- /dev/null
+++ b/hw/net/fsl_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 "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/fsl_etsec/registers.h b/hw/net/fsl_etsec/registers.h
new file mode 100644
index 0000000000..7ad7686470
--- /dev/null
+++ b/hw/net/fsl_etsec/registers.h
@@ -0,0 +1,320 @@
+/*
+ * 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>
+
+enum eTSEC_Register_Access_Type {
+ ACC_RW = 1, /* Read/Write */
+ ACC_RO = 2, /* Read Only */
+ ACC_WO = 3, /* Write Only */
+ ACC_W1C = 4, /* Write 1 to clear */
+ ACC_UNKNOWN = 5 /* Unknown register*/
+};
+
+typedef struct eTSEC_Register_Definition {
+ uint32_t offset;
+ const char *name;
+ const char *desc;
+ enum eTSEC_Register_Access_Type access;
+ uint32_t reset;
+} eTSEC_Register_Definition;
+
+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_PERR (1 << 0)
+#define IEVENT_DPE (1 << 1)
+#define IEVENT_FIQ (1 << 2)
+#define IEVENT_FIR (1 << 3)
+#define IEVENT_FGPI (1 << 4)
+#define IEVENT_RXF (1 << 7)
+#define IEVENT_GRSC (1 << 8)
+#define IEVENT_MMRW (1 << 9)
+#define IEVENT_MMRD (1 << 10)
+#define IEVENT_MAG (1 << 11)
+#define IEVENT_RXB (1 << 15)
+#define IEVENT_XFUN (1 << 16)
+#define IEVENT_CRL (1 << 17)
+#define IEVENT_LC (1 << 18)
+#define IEVENT_TXF (1 << 20)
+#define IEVENT_TXB (1 << 21)
+#define IEVENT_TXE (1 << 22)
+#define IEVENT_TXC (1 << 23)
+#define IEVENT_BABT (1 << 24)
+#define IEVENT_GTSC (1 << 25)
+#define IEVENT_MSRO (1 << 26)
+#define IEVENT_EBERR (1 << 28)
+#define IEVENT_BSY (1 << 29)
+#define IEVENT_RXC (1 << 30)
+#define IEVENT_BABR (1 << 31)
+
+#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/fsl_etsec/rings.c b/hw/net/fsl_etsec/rings.c
new file mode 100644
index 0000000000..77602722b3
--- /dev/null
+++ b/hw/net/fsl_etsec/rings.c
@@ -0,0 +1,650 @@
+/*
+ * 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 "net/checksum.h"
+
+#include "etsec.h"
+#include "registers.h"
+
+/* #define ETSEC_RING_DEBUG */
+/* #define HEX_DUMP */
+/* #define DEBUG_BD */
+
+#ifdef ETSEC_RING_DEBUG
+static const int debug_etsec = 1;
+#else
+static const int debug_etsec;
+#endif
+
+#define RING_DEBUG(fmt, ...) do { \
+ if (debug_etsec) { \
+ qemu_log(fmt , ## __VA_ARGS__); \
+ } \
+ } while (0)
+
+#ifdef DEBUG_BD
+
+static void print_tx_bd_flags(uint16_t flags)
+{
+ qemu_log(" Ready: %d\n", !!(flags & BD_TX_READY));
+ qemu_log(" PAD/CRC: %d\n", !!(flags & BD_TX_PADCRC));
+ qemu_log(" Wrap: %d\n", !!(flags & BD_WRAP));
+ qemu_log(" Interrupt: %d\n", !!(flags & BD_INTERRUPT));
+ qemu_log(" Last in frame: %d\n", !!(flags & BD_LAST));
+ qemu_log(" Tx CRC: %d\n", !!(flags & BD_TX_TC));
+ qemu_log(" User-defined preamble / defer: %d\n",
+ !!(flags & BD_TX_PREDEF));
+ qemu_log(" Huge frame enable / Late collision: %d\n",
+ !!(flags & BD_TX_HFELC));
+ qemu_log(" Control frame / Retransmission Limit: %d\n",
+ !!(flags & BD_TX_CFRL));
+ qemu_log(" Retry count: %d\n",
+ (flags >> BD_TX_RC_OFFSET) & BD_TX_RC_MASK);
+ qemu_log(" Underrun / TCP/IP off-load enable: %d\n",
+ !!(flags & BD_TX_TOEUN));
+ qemu_log(" Truncation: %d\n", !!(flags & BD_TX_TR));
+}
+
+static void print_rx_bd_flags(uint16_t flags)
+{
+ qemu_log(" Empty: %d\n", !!(flags & BD_RX_EMPTY));
+ qemu_log(" Receive software ownership: %d\n", !!(flags & BD_RX_RO1));
+ qemu_log(" Wrap: %d\n", !!(flags & BD_WRAP));
+ qemu_log(" Interrupt: %d\n", !!(flags & BD_INTERRUPT));
+ qemu_log(" Last in frame: %d\n", !!(flags & BD_LAST));
+ qemu_log(" First in frame: %d\n", !!(flags & BD_RX_FIRST));
+ qemu_log(" Miss: %d\n", !!(flags & BD_RX_MISS));
+ qemu_log(" Broadcast: %d\n", !!(flags & BD_RX_BROADCAST));
+ qemu_log(" Multicast: %d\n", !!(flags & BD_RX_MULTICAST));
+ qemu_log(" Rx frame length violation: %d\n", !!(flags & BD_RX_LG));
+ qemu_log(" Rx non-octet aligned frame: %d\n", !!(flags & BD_RX_NO));
+ qemu_log(" Short frame: %d\n", !!(flags & BD_RX_SH));
+ qemu_log(" Rx CRC Error: %d\n", !!(flags & BD_RX_CR));
+ qemu_log(" Overrun: %d\n", !!(flags & BD_RX_OV));
+ qemu_log(" Truncation: %d\n", !!(flags & BD_RX_TR));
+}
+
+
+static void print_bd(eTSEC_rxtx_bd bd, int mode, uint32_t index)
+{
+ qemu_log("eTSEC %s Data Buffer Descriptor (%u)\n",
+ mode == eTSEC_TRANSMIT ? "Transmit" : "Receive",
+ index);
+ qemu_log(" Flags : 0x%04x\n", bd.flags);
+ if (mode == eTSEC_TRANSMIT) {
+ print_tx_bd_flags(bd.flags);
+ } else {
+ print_rx_bd_flags(bd.flags);
+ }
+ qemu_log(" Length : 0x%04x\n", bd.length);
+ qemu_log(" Pointer : 0x%08x\n", bd.bufptr);
+}
+
+#endif /* DEBUG_BD */
+
+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 = lduw_le_p(&bd->flags);
+ bd->length = lduw_le_p(&bd->length);
+ bd->bufptr = ldl_le_p(&bd->bufptr);
+ } else {
+ bd->flags = lduw_be_p(&bd->flags);
+ bd->length = lduw_be_p(&bd->length);
+ bd->bufptr = ldl_be_p(&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) {
+ stw_le_p(&bd->flags, bd->flags);
+ stw_le_p(&bd->length, bd->length);
+ stl_le_p(&bd->bufptr, bd->bufptr);
+ } else {
+ stw_be_p(&bd->flags, bd->flags);
+ stw_be_p(&bd->length, bd->length);
+ stl_be_p(&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;
+ hwaddr tbdbth = (hwaddr)(etsec->regs[TBDBPH].value & 0xF) << 32;
+
+ 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 + tbdbth, 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)
+ qemu_log("eTSEC Send packet size:%d\n", etsec->tx_buffer_len);
+ qemu_hexdump(etsec->tx_buffer, stderr, "", 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 etsec_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 = (hwaddr)(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;
+ hwaddr bufptr = bd->bufptr +
+ ((hwaddr)(etsec->regs[TBDBPH].value & 0xF) << 32);
+ uint8_t padd[etsec->rx_padding];
+ uint8_t rem;
+
+ RING_DEBUG("eTSEC fill Rx buffer @ 0x%016" HWADDR_PRIx
+ " size:%zu(padding + crc:%u) + fcb:%u\n",
+ bufptr, *size, etsec->rx_padding, etsec->rx_fcb_size);
+
+ bd->length = 0;
+
+ /* This operation will only write FCB */
+ 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;
+ etsec->rx_fcb_size = 0;
+
+ }
+
+ /* We remove padding from the computation of to_write because it is not
+ * allocated in the buffer.
+ */
+ to_write = MIN(*size - etsec->rx_padding,
+ etsec->regs[MRBLR].value - etsec->rx_fcb_size);
+
+ /* This operation can only write packet data and no padding */
+ 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 only for padding which is not actually
+ * allocated in the data 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 + RCTRL[PAL]) */
+ fcb_size = 8 + ((etsec->regs[RCTRL].value >> 16) & 0x1F);
+
+ 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;
+
+ /* CRC padding (We don't have to compute the CRC) */
+ etsec->rx_padding = 4;
+
+ 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 etsec_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;
+ }
+
+ if ((etsec->regs[RCTRL].value & RCTRL_RSF) && (size < 60)) {
+ /* CRC is not in the packet yet, so short frame is below 60 bytes */
+ RING_DEBUG("%s: Drop short frame\n", __func__);
+ return;
+ }
+
+ rx_init_frame(etsec, buf, size);
+
+ etsec_walk_rx_ring(etsec, ring_nbr);
+}
+
+void etsec_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 = (hwaddr)(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 */
+ qemu_log("%s frame length violation: size:%zu MAXFRM:%d\n",
+ __func__, size, etsec->regs[MAXFRM].value);
+
+ bd.flags |= BD_RX_LG;
+ }
+
+ if (size < 64) {
+ /* Short frame */
+ 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:%zu\n", remaining_data);
+}