[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v3 1/2] hw/net: add support for Allwinner EMAC Fast
From: |
Beniamino Galvani |
Subject: |
[Qemu-devel] [PATCH v3 1/2] hw/net: add support for Allwinner EMAC Fast Ethernet controller |
Date: |
Mon, 20 Jan 2014 00:25:18 +0100 |
This patch adds support for the Fast Ethernet MAC found on Allwinner
SoCs, together with a basic emulation of Realtek RTL8201CP PHY.
Since there is no public documentation of the Allwinner controller, the
implementation is based on Linux kernel driver.
Signed-off-by: Beniamino Galvani <address@hidden>
---
default-configs/arm-softmmu.mak | 1 +
hw/net/Makefile.objs | 1 +
hw/net/allwinner_emac.c | 589 +++++++++++++++++++++++++++++++++++++++
include/hw/net/allwinner_emac.h | 222 +++++++++++++++
4 files changed, 813 insertions(+)
create mode 100644 hw/net/allwinner_emac.c
create mode 100644 include/hw/net/allwinner_emac.h
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index ce1d620..f3513fa 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -27,6 +27,7 @@ CONFIG_SSI_SD=y
CONFIG_SSI_M25P80=y
CONFIG_LAN9118=y
CONFIG_SMC91C111=y
+CONFIG_ALLWINNER_EMAC=y
CONFIG_DS1338=y
CONFIG_PFLASH_CFI01=y
CONFIG_PFLASH_CFI02=y
diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
index 951cca3..75e80c2 100644
--- a/hw/net/Makefile.objs
+++ b/hw/net/Makefile.objs
@@ -18,6 +18,7 @@ common-obj-$(CONFIG_OPENCORES_ETH) += opencores_eth.o
common-obj-$(CONFIG_XGMAC) += xgmac.o
common-obj-$(CONFIG_MIPSNET) += mipsnet.o
common-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o
+common-obj-$(CONFIG_ALLWINNER_EMAC) += allwinner_emac.o
common-obj-$(CONFIG_CADENCE) += cadence_gem.o
common-obj-$(CONFIG_STELLARIS_ENET) += stellaris_enet.o
diff --git a/hw/net/allwinner_emac.c b/hw/net/allwinner_emac.c
new file mode 100644
index 0000000..4cad7e0
--- /dev/null
+++ b/hw/net/allwinner_emac.c
@@ -0,0 +1,589 @@
+/*
+ * Emulation of Allwinner EMAC Fast Ethernet controller and
+ * Realtek RTL8201CP PHY
+ *
+ * Copyright (C) 2014 Beniamino Galvani <address@hidden>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include "hw/sysbus.h"
+#include "net/net.h"
+#include "hw/net/allwinner_emac.h"
+#include <zlib.h>
+
+static uint8_t padding[60];
+
+static void mii_set_link(RTL8201CPState *mii, bool link_ok)
+{
+ if (link_ok) {
+ mii->bmsr |= MII_BMSR_LINK_ST;
+ mii->anlpar |= MII_ANAR_TXFD | MII_ANAR_10FD | MII_ANAR_10 |
+ MII_ANAR_CSMACD;
+ } else {
+ mii->bmsr &= ~MII_BMSR_LINK_ST;
+ mii->anlpar = MII_ANAR_TX;
+ }
+}
+
+static void mii_reset(RTL8201CPState *mii, bool link_ok)
+{
+ mii->bmcr = MII_BMCR_FD | MII_BMCR_AUTOEN | MII_BMCR_SPEED;
+ mii->bmsr = MII_BMSR_100TX_FD | MII_BMSR_100TX_HD | MII_BMSR_10T_FD |
+ MII_BMSR_10T_HD | MII_BMSR_MFPS | MII_BMSR_AUTONEG;
+ mii->anar = MII_ANAR_TXFD | MII_ANAR_TX | MII_ANAR_10FD | MII_ANAR_10 |
+ MII_ANAR_CSMACD;
+ mii->anlpar = MII_ANAR_TX;
+
+ mii_set_link(mii, link_ok);
+}
+
+static uint16_t aw_emac_mdio_read(AwEmacState *s, uint8_t addr, uint8_t reg)
+{
+ RTL8201CPState *mii = &s->mii;
+ uint16_t ret = 0xffff;
+
+ if (addr == s->phy_addr) {
+ switch (reg) {
+ case MII_BMCR:
+ return mii->bmcr;
+ case MII_BMSR:
+ return mii->bmsr;
+ case MII_PHYID1:
+ return RTL8201CP_PHYID1;
+ case MII_PHYID2:
+ return RTL8201CP_PHYID2;
+ case MII_ANAR:
+ return mii->anar;
+ case MII_ANLPAR:
+ return mii->anlpar;
+ case MII_ANER:
+ case MII_NSR:
+ case MII_LBREMR:
+ case MII_REC:
+ case MII_SNRDR:
+ case MII_TEST:
+ qemu_log_mask(LOG_UNIMP,
+ "allwinner_emac: read from unimpl. mii reg 0x%x\n",
+ reg);
+ return 0;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "allwinner_emac: read from invalid mii reg 0x%x\n",
+ reg);
+ return 0;
+ }
+ }
+ return ret;
+}
+
+static void aw_emac_mdio_write(AwEmacState *s, uint8_t addr, uint8_t reg,
+ uint16_t value)
+{
+ RTL8201CPState *mii = &s->mii;
+ NetClientState *nc;
+
+ if (addr == s->phy_addr) {
+ switch (reg) {
+ case MII_BMCR:
+ if (value & MII_BMCR_RESET) {
+ nc = qemu_get_queue(s->nic);
+ mii_reset(mii, !nc->link_down);
+ } else {
+ mii->bmcr = value;
+ }
+ break;
+ case MII_ANAR:
+ mii->anar = value;
+ break;
+ case MII_BMSR:
+ case MII_PHYID1:
+ case MII_PHYID2:
+ case MII_ANLPAR:
+ case MII_ANER:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "allwinner_emac: write to read-only mii reg 0x%x\n",
+ reg);
+ break;
+ case MII_NSR:
+ case MII_LBREMR:
+ case MII_REC:
+ case MII_SNRDR:
+ case MII_TEST:
+ qemu_log_mask(LOG_UNIMP,
+ "allwinner_emac: write to unimpl. mii reg 0x%x\n",
+ reg);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "allwinner_emac: write to invalid mii reg 0x%x\n",
+ reg);
+ }
+ }
+}
+
+static void aw_emac_update_irq(AwEmacState *s)
+{
+ qemu_set_irq(s->irq, (s->int_sta & s->int_ctl) != 0);
+}
+
+static void aw_emac_tx_reset(AwEmacTxFifo *fifo)
+{
+ fifo->length = 0;
+ fifo->offset = 0;
+}
+
+static int aw_emac_tx_has_space(AwEmacTxFifo *fifo, int n)
+{
+ return fifo->offset + n <= TX_FIFO_SIZE;
+}
+
+static void aw_emac_tx_pushw(AwEmacTxFifo *fifo, uint32_t val)
+{
+ assert(fifo->offset + 4 <= TX_FIFO_SIZE);
+ fifo->data[fifo->offset++] = val;
+ fifo->data[fifo->offset++] = val >> 8;
+ fifo->data[fifo->offset++] = val >> 16;
+ fifo->data[fifo->offset++] = val >> 24;
+}
+
+static void aw_emac_rx_reset(AwEmacRxFifo *fifo)
+{
+ fifo->head = 0;
+ fifo->num = 0;
+ fifo->num_packets = 0;
+ fifo->packet_size = 0;
+ fifo->packet_pos = 0;
+}
+
+static int aw_emac_rx_has_space(AwEmacRxFifo *fifo, int n)
+{
+ return fifo->num + n <= RX_FIFO_SIZE;
+}
+
+static void aw_emac_rx_push(AwEmacRxFifo *fifo, const uint8_t *data, int size)
+{
+ int index;
+
+ assert(fifo->num + size <= RX_FIFO_SIZE);
+ index = (fifo->head + fifo->num) % RX_FIFO_SIZE;
+
+ if (index + size <= RX_FIFO_SIZE) {
+ memcpy(&fifo->data[index], data, size);
+ } else {
+ memcpy(&fifo->data[index], data, RX_FIFO_SIZE - index);
+ memcpy(&fifo->data[0], &data[RX_FIFO_SIZE - index],
+ size - RX_FIFO_SIZE + index);
+ }
+
+ fifo->num += size;
+}
+
+static void aw_emac_rx_pushb(AwEmacRxFifo *fifo, uint8_t val)
+{
+ return aw_emac_rx_push(fifo, &val, 1);
+}
+
+static void aw_emac_rx_pushw(AwEmacRxFifo *fifo, uint32_t val)
+{
+ aw_emac_rx_pushb(fifo, val);
+ aw_emac_rx_pushb(fifo, val >> 8);
+ aw_emac_rx_pushb(fifo, val >> 16);
+ aw_emac_rx_pushb(fifo, val >> 24);
+}
+
+static uint8_t aw_emac_rx_popb(AwEmacRxFifo *fifo)
+{
+ uint8_t ret;
+
+ assert(fifo->num > 0);
+ ret = fifo->data[fifo->head];
+ fifo->head = (fifo->head + 1) % RX_FIFO_SIZE;
+ fifo->num--;
+
+ return ret;
+}
+
+static uint32_t aw_emac_rx_popw(AwEmacRxFifo *fifo)
+{
+ uint32_t ret;
+
+ ret = aw_emac_rx_popb(fifo);
+ ret |= aw_emac_rx_popb(fifo) << 8;
+ ret |= aw_emac_rx_popb(fifo) << 16;
+ ret |= aw_emac_rx_popb(fifo) << 24;
+
+ return ret;
+}
+
+static int aw_emac_can_receive(NetClientState *nc)
+{
+ AwEmacState *s = qemu_get_nic_opaque(nc);
+
+ return s->ctl & EMAC_CTL_RX_EN;
+}
+
+static ssize_t aw_emac_receive(NetClientState *nc, const uint8_t *buf,
+ size_t size)
+{
+ AwEmacState *s = qemu_get_nic_opaque(nc);
+ AwEmacRxFifo *fifo = &s->rx_fifo;
+ size_t padded_size, total_size;
+ uint32_t crc;
+
+ padded_size = size > 60 ? size : 60;
+ total_size = QEMU_ALIGN_UP(RX_HDR_SIZE + padded_size + CRC_SIZE, 4);
+
+ if (!(s->ctl & EMAC_CTL_RX_EN) || !aw_emac_rx_has_space(fifo, total_size))
{
+ return -1;
+ }
+
+ aw_emac_rx_pushw(fifo, EMAC_UNDOCUMENTED_MAGIC);
+ aw_emac_rx_pushw(fifo, EMAC_RX_HEADER(padded_size + CRC_SIZE,
+ EMAC_RX_IO_DATA_STATUS_OK));
+ aw_emac_rx_push(fifo, buf, size);
+ crc = crc32(~0, buf, size);
+
+ if (padded_size != size) {
+ aw_emac_rx_push(fifo, padding, padded_size - size);
+ crc = crc32(crc, padding, padded_size - size);
+ }
+
+ aw_emac_rx_pushw(fifo, crc);
+ aw_emac_rx_push(fifo, padding, QEMU_ALIGN_UP(padded_size, 4) -
padded_size);
+ s->rx_fifo.num_packets++;
+
+ s->int_sta |= EMAC_INT_RX;
+ aw_emac_update_irq(s);
+
+ return size;
+}
+
+static void aw_emac_cleanup(NetClientState *nc)
+{
+ AwEmacState *s = qemu_get_nic_opaque(nc);
+
+ s->nic = NULL;
+}
+
+static void aw_emac_reset(DeviceState *dev)
+{
+ AwEmacState *s = AW_EMAC(dev);
+ NetClientState *nc = qemu_get_queue(s->nic);
+
+ s->ctl = 0;
+ s->tx_mode = 0;
+ s->int_ctl = 0;
+ s->int_sta = 0;
+ s->tx_channel = 0;
+ s->phy_target = 0;
+
+ aw_emac_tx_reset(&s->tx_fifo[0]);
+ aw_emac_tx_reset(&s->tx_fifo[1]);
+ aw_emac_rx_reset(&s->rx_fifo);
+
+ mii_reset(&s->mii, !nc->link_down);
+}
+
+static uint64_t aw_emac_read(void *opaque, hwaddr offset, unsigned size)
+{
+ AwEmacState *s = opaque;
+ AwEmacRxFifo *fifo = &s->rx_fifo;
+ uint64_t ret;
+
+ switch (offset) {
+ case EMAC_CTL_REG:
+ return s->ctl;
+ case EMAC_TX_MODE_REG:
+ return s->tx_mode;
+ case EMAC_TX_INS_REG:
+ return s->tx_channel;
+ case EMAC_RX_CTL_REG:
+ return s->rx_ctl;
+ case EMAC_RX_IO_DATA_REG:
+ if (!fifo->num_packets) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "Read IO data register when no packet available");
+ return 0;
+ }
+
+ ret = aw_emac_rx_popw(fifo);
+
+ switch (fifo->packet_pos) {
+ case 0: /* Word is magic header */
+ fifo->packet_pos += 4;
+ break;
+ case 4: /* Word is rx info header */
+ fifo->packet_pos += 4;
+ fifo->packet_size = QEMU_ALIGN_UP(extract32(ret, 0, 16), 4);
+ break;
+ default: /* Word is packet data */
+ fifo->packet_pos += 4;
+ fifo->packet_size -= 4;
+
+ if (!fifo->packet_size) {
+ fifo->packet_pos = 0;
+ fifo->num_packets--;
+ }
+ }
+ return ret;
+ case EMAC_RX_FBC_REG:
+ return s->rx_fifo.num_packets;
+ case EMAC_INT_CTL_REG:
+ return s->int_ctl;
+ case EMAC_INT_STA_REG:
+ return s->int_sta;
+ case EMAC_MAC_MRDD_REG:
+ return aw_emac_mdio_read(s, extract32(s->phy_target, PHY_ADDR_SHIFT,
8),
+ extract32(s->phy_target, PHY_REG_SHIFT, 8));
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "allwinner_emac: read access to unknown register 0x"
+ TARGET_FMT_plx "\n", offset);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static void aw_emac_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ AwEmacState *s = opaque;
+ AwEmacTxFifo *fifo;
+ NetClientState *nc = qemu_get_queue(s->nic);
+ int chan;
+
+ switch (offset) {
+ case EMAC_CTL_REG:
+ if (value & EMAC_CTL_RESET) {
+ aw_emac_reset(DEVICE(s));
+ value &= ~EMAC_CTL_RESET;
+ }
+ s->ctl = value;
+ if (aw_emac_can_receive(nc)) {
+ qemu_flush_queued_packets(nc);
+ }
+ break;
+ case EMAC_TX_MODE_REG:
+ s->tx_mode = value;
+ break;
+ case EMAC_TX_CTL0_REG:
+ case EMAC_TX_CTL1_REG:
+ chan = (offset == EMAC_TX_CTL0_REG ? 0 : 1);
+ if ((value & 1) && (s->ctl & EMAC_CTL_TX_EN)) {
+ qemu_send_packet(nc, s->tx_fifo[chan].data,
+ s->tx_fifo[chan].length);
+ /* Raise TX interrupt */
+ s->int_sta |= EMAC_INT_TX_CHAN(chan);
+ s->tx_fifo[chan].offset = 0;
+ aw_emac_update_irq(s);
+ }
+ break;
+ case EMAC_TX_INS_REG:
+ s->tx_channel = value < NUM_TX_FIFOS ? value : 0;
+ break;
+ case EMAC_TX_PL0_REG:
+ case EMAC_TX_PL1_REG:
+ chan = (offset == EMAC_TX_PL0_REG ? 0 : 1);
+ if (value > TX_FIFO_SIZE) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "allwinner_emac: invalid TX frame length %d\n",
+ (int)value);
+ value = TX_FIFO_SIZE;
+ }
+ s->tx_fifo[chan].length = value;
+ break;
+ case EMAC_TX_IO_DATA_REG:
+ fifo = &s->tx_fifo[s->tx_channel];
+ if (!aw_emac_tx_has_space(fifo, 4)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "allwinner_emac: TX data overruns fifo (%d)\n",
+ fifo->offset);
+ break;
+ }
+ aw_emac_tx_pushw(fifo, value);
+ break;
+ case EMAC_RX_CTL_REG:
+ s->rx_ctl = value;
+ break;
+ case EMAC_RX_FBC_REG:
+ if (value == 0) {
+ aw_emac_rx_reset(&s->rx_fifo);
+ }
+ break;
+ case EMAC_INT_CTL_REG:
+ s->int_ctl = value;
+ break;
+ case EMAC_INT_STA_REG:
+ s->int_sta &= ~value;
+ break;
+ case EMAC_MAC_MADR_REG:
+ s->phy_target = value;
+ break;
+ case EMAC_MAC_MWTD_REG:
+ aw_emac_mdio_write(s, extract32(s->phy_target, PHY_ADDR_SHIFT, 8),
+ extract32(s->phy_target, PHY_REG_SHIFT, 8), value);
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "allwinner_emac: write access to unknown register 0x"
+ TARGET_FMT_plx "\n", offset);
+ }
+}
+
+static void aw_emac_set_link(NetClientState *nc)
+{
+ AwEmacState *s = qemu_get_nic_opaque(nc);
+
+ mii_set_link(&s->mii, !nc->link_down);
+}
+
+static const MemoryRegionOps aw_emac_mem_ops = {
+ .read = aw_emac_read,
+ .write = aw_emac_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static NetClientInfo net_aw_emac_info = {
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .size = sizeof(NICState),
+ .can_receive = aw_emac_can_receive,
+ .receive = aw_emac_receive,
+ .cleanup = aw_emac_cleanup,
+ .link_status_changed = aw_emac_set_link,
+};
+
+static void aw_emac_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ AwEmacState *s = AW_EMAC(obj);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &aw_emac_mem_ops, s,
+ "aw_emac", 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->irq);
+}
+
+static void aw_emac_realize(DeviceState *dev, Error **errp)
+{
+ AwEmacState *s = AW_EMAC(dev);
+
+ qemu_macaddr_default_if_unset(&s->conf.macaddr);
+ s->nic = qemu_new_nic(&net_aw_emac_info, &s->conf,
+ object_get_typename(OBJECT(dev)), dev->id, s);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+}
+
+static Property aw_emac_properties[] = {
+ DEFINE_NIC_PROPERTIES(AwEmacState, conf),
+ DEFINE_PROP_UINT8("phyaddr", AwEmacState, phy_addr, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static const VMStateDescription vmstate_mii = {
+ .name = "rtl8201cp",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT16(bmcr, RTL8201CPState),
+ VMSTATE_UINT16(bmsr, RTL8201CPState),
+ VMSTATE_UINT16(anar, RTL8201CPState),
+ VMSTATE_UINT16(anlpar, RTL8201CPState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_rx_fifo = {
+ .name = "allwinner_emac_rx_fifo",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8_ARRAY(data, AwEmacRxFifo, RX_FIFO_SIZE),
+ VMSTATE_INT32(head, AwEmacRxFifo),
+ VMSTATE_INT32(num, AwEmacRxFifo),
+ VMSTATE_INT32(num_packets, AwEmacRxFifo),
+ VMSTATE_INT32(packet_size, AwEmacRxFifo),
+ VMSTATE_INT32(packet_pos, AwEmacRxFifo),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_tx_fifo = {
+ .name = "allwinner_emac_tx_fifo",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8_ARRAY(data, AwEmacTxFifo, TX_FIFO_SIZE),
+ VMSTATE_INT32(offset, AwEmacTxFifo),
+ VMSTATE_INT32(length, AwEmacTxFifo),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int aw_emac_post_load(void *opaque, int version_id)
+{
+ AwEmacState *s = opaque;
+
+ aw_emac_set_link(qemu_get_queue(s->nic));
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_aw_emac = {
+ .name = "allwinner_emac",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .post_load = aw_emac_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT(mii, AwEmacState, 1, vmstate_mii, RTL8201CPState),
+ VMSTATE_UINT32(ctl, AwEmacState),
+ VMSTATE_UINT32(tx_mode, AwEmacState),
+ VMSTATE_UINT32(rx_ctl, AwEmacState),
+ VMSTATE_UINT32(int_ctl, AwEmacState),
+ VMSTATE_UINT32(int_sta, AwEmacState),
+ VMSTATE_UINT32(phy_target, AwEmacState),
+ VMSTATE_STRUCT(rx_fifo, AwEmacState, 1, vmstate_rx_fifo, AwEmacRxFifo),
+ VMSTATE_STRUCT_ARRAY(tx_fifo, AwEmacState,
+ NUM_TX_FIFOS, 1, vmstate_tx_fifo, AwEmacTxFifo),
+ VMSTATE_INT32(tx_channel, AwEmacState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void aw_emac_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = aw_emac_realize;
+ dc->props = aw_emac_properties;
+ dc->reset = aw_emac_reset;
+ dc->vmsd = &vmstate_aw_emac;
+}
+
+static const TypeInfo aw_emac_info = {
+ .name = TYPE_AW_EMAC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AwEmacState),
+ .instance_init = aw_emac_init,
+ .class_init = aw_emac_class_init,
+};
+
+static void aw_emac_register_types(void)
+{
+ type_register_static(&aw_emac_info);
+}
+
+type_init(aw_emac_register_types)
diff --git a/include/hw/net/allwinner_emac.h b/include/hw/net/allwinner_emac.h
new file mode 100644
index 0000000..3909733
--- /dev/null
+++ b/include/hw/net/allwinner_emac.h
@@ -0,0 +1,222 @@
+/*
+ * Emulation of Allwinner EMAC Fast Ethernet controller and
+ * Realtek RTL8201CP PHY
+ *
+ * Copyright (C) 2014 Beniamino Galvani <address@hidden>
+ *
+ * Allwinner EMAC register definitions from Linux kernel are:
+ * Copyright 2012 Stefan Roese <address@hidden>
+ * Copyright 2013 Maxime Ripard <address@hidden>
+ * Copyright 1997 Sten Wang
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef AW_EMAC_H
+#define AW_EMAC_H
+
+#include "net/net.h"
+
+#define TYPE_AW_EMAC "allwinner_emac"
+#define AW_EMAC(obj) OBJECT_CHECK(AwEmacState, (obj), TYPE_AW_EMAC)
+
+/*
+ * Allwinner EMAC register list
+ */
+#define EMAC_CTL_REG 0x00
+
+#define EMAC_TX_MODE_REG 0x04
+#define EMAC_TX_FLOW_REG 0x08
+#define EMAC_TX_CTL0_REG 0x0C
+#define EMAC_TX_CTL1_REG 0x10
+#define EMAC_TX_INS_REG 0x14
+#define EMAC_TX_PL0_REG 0x18
+#define EMAC_TX_PL1_REG 0x1C
+#define EMAC_TX_STA_REG 0x20
+#define EMAC_TX_IO_DATA_REG 0x24
+#define EMAC_TX_IO_DATA1_REG 0x28
+#define EMAC_TX_TSVL0_REG 0x2C
+#define EMAC_TX_TSVH0_REG 0x30
+#define EMAC_TX_TSVL1_REG 0x34
+#define EMAC_TX_TSVH1_REG 0x38
+
+#define EMAC_RX_CTL_REG 0x3C
+#define EMAC_RX_HASH0_REG 0x40
+#define EMAC_RX_HASH1_REG 0x44
+#define EMAC_RX_STA_REG 0x48
+#define EMAC_RX_IO_DATA_REG 0x4C
+#define EMAC_RX_FBC_REG 0x50
+
+#define EMAC_INT_CTL_REG 0x54
+#define EMAC_INT_STA_REG 0x58
+
+#define EMAC_MAC_CTL0_REG 0x5C
+#define EMAC_MAC_CTL1_REG 0x60
+#define EMAC_MAC_IPGT_REG 0x64
+#define EMAC_MAC_IPGR_REG 0x68
+#define EMAC_MAC_CLRT_REG 0x6C
+#define EMAC_MAC_MAXF_REG 0x70
+#define EMAC_MAC_SUPP_REG 0x74
+#define EMAC_MAC_TEST_REG 0x78
+#define EMAC_MAC_MCFG_REG 0x7C
+#define EMAC_MAC_MCMD_REG 0x80
+#define EMAC_MAC_MADR_REG 0x84
+#define EMAC_MAC_MWTD_REG 0x88
+#define EMAC_MAC_MRDD_REG 0x8C
+#define EMAC_MAC_MIND_REG 0x90
+#define EMAC_MAC_SSRR_REG 0x94
+#define EMAC_MAC_A0_REG 0x98
+#define EMAC_MAC_A1_REG 0x9C
+#define EMAC_MAC_A2_REG 0xA0
+
+#define EMAC_SAFX_L_REG0 0xA4
+#define EMAC_SAFX_H_REG0 0xA8
+#define EMAC_SAFX_L_REG1 0xAC
+#define EMAC_SAFX_H_REG1 0xB0
+#define EMAC_SAFX_L_REG2 0xB4
+#define EMAC_SAFX_H_REG2 0xB8
+#define EMAC_SAFX_L_REG3 0xBC
+#define EMAC_SAFX_H_REG3 0xC0
+
+/* CTL register fields */
+#define EMAC_CTL_RESET (1 << 0)
+#define EMAC_CTL_TX_EN (1 << 1)
+#define EMAC_CTL_RX_EN (1 << 2)
+
+/* TX MODE register fields */
+#define EMAC_TX_MODE_ABORTED_FRAME_EN (1 << 0)
+#define EMAC_TX_MODE_DMA_EN (1 << 1)
+
+/* RX CTL register fields */
+#define EMAC_RX_CTL_AUTO_DRQ_EN (1 << 1)
+#define EMAC_RX_CTL_DMA_EN (1 << 2)
+#define EMAC_RX_CTL_PASS_ALL_EN (1 << 4)
+#define EMAC_RX_CTL_PASS_CTL_EN (1 << 5)
+#define EMAC_RX_CTL_PASS_CRC_ERR_EN (1 << 6)
+#define EMAC_RX_CTL_PASS_LEN_ERR_EN (1 << 7)
+#define EMAC_RX_CTL_PASS_LEN_OOR_EN (1 << 8)
+#define EMAC_RX_CTL_ACCEPT_UNICAST_EN (1 << 16)
+#define EMAC_RX_CTL_DA_FILTER_EN (1 << 17)
+#define EMAC_RX_CTL_ACCEPT_MULTICAST_EN (1 << 20)
+#define EMAC_RX_CTL_HASH_FILTER_EN (1 << 21)
+#define EMAC_RX_CTL_ACCEPT_BROADCAST_EN (1 << 22)
+#define EMAC_RX_CTL_SA_FILTER_EN (1 << 24)
+#define EMAC_RX_CTL_SA_FILTER_INVERT_EN (1 << 25)
+
+/* RX IO DATA register fields */
+#define EMAC_RX_HEADER(len, status) (((len) & 0xffff) | ((status) << 16))
+#define EMAC_RX_IO_DATA_STATUS_CRC_ERR (1 << 4)
+#define EMAC_RX_IO_DATA_STATUS_LEN_ERR (3 << 5)
+#define EMAC_RX_IO_DATA_STATUS_OK (1 << 7)
+#define EMAC_UNDOCUMENTED_MAGIC 0x0143414d /* header for RX frames */
+
+/* PHY registers */
+#define MII_BMCR 0
+#define MII_BMSR 1
+#define MII_PHYID1 2
+#define MII_PHYID2 3
+#define MII_ANAR 4
+#define MII_ANLPAR 5
+#define MII_ANER 6
+#define MII_NSR 16
+#define MII_LBREMR 17
+#define MII_REC 18
+#define MII_SNRDR 19
+#define MII_TEST 25
+
+/* PHY registers fields */
+#define MII_BMCR_RESET (1 << 15)
+#define MII_BMCR_LOOPBACK (1 << 14)
+#define MII_BMCR_SPEED (1 << 13)
+#define MII_BMCR_AUTOEN (1 << 12)
+#define MII_BMCR_FD (1 << 8)
+
+#define MII_BMSR_100TX_FD (1 << 14)
+#define MII_BMSR_100TX_HD (1 << 13)
+#define MII_BMSR_10T_FD (1 << 12)
+#define MII_BMSR_10T_HD (1 << 11)
+#define MII_BMSR_MFPS (1 << 6)
+#define MII_BMSR_AUTONEG (1 << 3)
+#define MII_BMSR_LINK_ST (1 << 2)
+
+#define MII_ANAR_TXFD (1 << 8)
+#define MII_ANAR_TX (1 << 7)
+#define MII_ANAR_10FD (1 << 6)
+#define MII_ANAR_10 (1 << 5)
+#define MII_ANAR_CSMACD (1 << 0)
+
+#define RTL8201CP_PHYID1 0x0000
+#define RTL8201CP_PHYID2 0x8201
+
+/* INT CTL and INT STA registers fields */
+#define EMAC_INT_TX_CHAN(x) (1 << (x))
+#define EMAC_INT_RX (1 << 8)
+
+/* Due to lack of specifications, size of fifos is chosen arbitrarily */
+#define TX_FIFO_SIZE (4 * 1024)
+#define RX_FIFO_SIZE (32 * 1024)
+
+#define NUM_TX_FIFOS 2
+#define RX_HDR_SIZE 8
+#define CRC_SIZE 4
+
+#define PHY_REG_SHIFT 0
+#define PHY_ADDR_SHIFT 8
+
+/* Buffer that holds a single packet to be transmitted */
+typedef struct AwEmacTxFifo {
+ uint8_t data[TX_FIFO_SIZE];
+ int offset;
+ int length;
+} AwEmacTxFifo;
+
+/* Circular buffer for received packets */
+typedef struct AwEmacRxFifo {
+ uint8_t data[RX_FIFO_SIZE];
+ int head;
+ int num;
+ int num_packets; /* Number of packets stored in fifo */
+ int packet_size; /* Remaining size of current packet */
+ int packet_pos; /* Position in current packet */
+} AwEmacRxFifo;
+
+typedef struct RTL8201CPState {
+ uint16_t bmcr;
+ uint16_t bmsr;
+ uint16_t anar;
+ uint16_t anlpar;
+} RTL8201CPState;
+
+typedef struct AwEmacState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+ /*< public >*/
+
+ MemoryRegion iomem;
+ qemu_irq irq;
+ NICState *nic;
+ NICConf conf;
+ RTL8201CPState mii;
+ uint8_t phy_addr;
+
+ uint32_t ctl;
+ uint32_t tx_mode;
+ uint32_t rx_ctl;
+ uint32_t int_ctl;
+ uint32_t int_sta;
+ uint32_t phy_target;
+
+ AwEmacRxFifo rx_fifo;
+ AwEmacTxFifo tx_fifo[NUM_TX_FIFOS];
+ int tx_channel;
+} AwEmacState;
+
+#endif
--
1.7.10.4