qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v4 0/3] hw/arm: add ethernet support to Allwinner A10
@ 2014-01-26 21:39 Beniamino Galvani
  2014-01-26 21:39 ` [Qemu-devel] [PATCH v4 1/3] util/fifo8: implement push/pop of multiple bytes Beniamino Galvani
                   ` (2 more replies)
  0 siblings, 3 replies; 14+ messages in thread
From: Beniamino Galvani @ 2014-01-26 21:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Peter Crosthwaite, Stefan Hajnoczi,
	Beniamino Galvani, Andreas Färber, Li Guang

This patch series adds support for the EMAC Fast Ethernet controller
found on Allwinner SoCs to the Allwinner A10.

Changelog:
v4: Address comments from Peter Crosthwaite and Andreas Färber:
    * Add functions for multiple bytes push/pop to Fifo8
    * Get rid of custom fifo implementation and use generic one for
      both tx and rx
    * Rename aw_emac_mdio_read -> RTL8201CP_mdio_read
    * Allow reception of new packets from net layer only when there is
      space for a full size frame
    * Change TYPE_AW_EMAC from "allwinner_emac" to "allwinner-emac"
    * Property "phyaddr" -> "phy-addr"
    * Remove trailing newlines from error_report() messages

v3: Address comments from Peter Crosthwaite and Stefan Hajnoczi:
    * Use a single, big rx fifo instead of per-packet fifo
    * Remove link_ok field from PHY state
    * Call set_link() on post_load
    * Add missing PHY registers
    * Implement reset() method
    * Rename AwEmacMii -> RTL8201CPState
    * Rename mii_{read,write} -> aw_emac_mdio_{read,write}

v2: Address Peter Crosthwaite's comments:
    * Make phy address customizable through a property
    * Call qemu_flush_queued_packets() when rx becomes possible
    * Always create EMAC instance in SoC
    * Use uint8 arrays for fifos
    * Minor cleanups

Beniamino Galvani (3):
  util/fifo8: implement push/pop of multiple bytes
  hw/net: add support for Allwinner EMAC Fast Ethernet controller
  hw/arm/allwinner-a10: initialize EMAC

 default-configs/arm-softmmu.mak |    1 +
 hw/arm/allwinner-a10.c          |   16 ++
 hw/arm/cubieboard.c             |   11 +-
 hw/net/Makefile.objs            |    1 +
 hw/net/allwinner_emac.c         |  532 +++++++++++++++++++++++++++++++++++++++
 include/hw/arm/allwinner-a10.h  |    3 +
 include/hw/net/allwinner_emac.h |  211 ++++++++++++++++
 include/qemu/fifo8.h            |   43 ++++
 util/fifo8.c                    |   44 ++++
 9 files changed, 860 insertions(+), 2 deletions(-)
 create mode 100644 hw/net/allwinner_emac.c
 create mode 100644 include/hw/net/allwinner_emac.h

-- 
1.7.10.4

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [Qemu-devel] [PATCH v4 1/3] util/fifo8: implement push/pop of multiple bytes
  2014-01-26 21:39 [Qemu-devel] [PATCH v4 0/3] hw/arm: add ethernet support to Allwinner A10 Beniamino Galvani
@ 2014-01-26 21:39 ` Beniamino Galvani
  2014-01-27 18:32   ` Peter Maydell
  2014-01-26 21:39 ` [Qemu-devel] [PATCH v4 2/3] hw/net: add support for Allwinner EMAC Fast Ethernet controller Beniamino Galvani
  2014-01-26 21:39 ` [Qemu-devel] [PATCH v4 3/3] hw/arm/allwinner-a10: initialize EMAC Beniamino Galvani
  2 siblings, 1 reply; 14+ messages in thread
From: Beniamino Galvani @ 2014-01-26 21:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Peter Crosthwaite, Stefan Hajnoczi,
	Beniamino Galvani, Andreas Färber, Li Guang

In some circumstances it is useful to be able to push the entire
content of a memory buffer to the fifo or to pop multiple bytes with a
single operation.

The functions fifo8_has_space() and fifo8_push_all() added by this
patch allow to perform the first kind of operation efficiently.

The function fifo8_pop_buf() can be used instead to pop multiple bytes
from the fifo, returning a pointer to the backing buffer.

Signed-off-by: Beniamino Galvani <b.galvani@gmail.com>
---
 include/qemu/fifo8.h |   43 +++++++++++++++++++++++++++++++++++++++++++
 util/fifo8.c         |   44 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 87 insertions(+)

diff --git a/include/qemu/fifo8.h b/include/qemu/fifo8.h
index d318f71..ea6e9f6 100644
--- a/include/qemu/fifo8.h
+++ b/include/qemu/fifo8.h
@@ -44,6 +44,19 @@ void fifo8_destroy(Fifo8 *fifo);
 void fifo8_push(Fifo8 *fifo, uint8_t data);
 
 /**
+ * fifo8_push_all:
+ * @fifo: FIFO to push to
+ * @data: data to push
+ * @size: number of bytes to push
+ *
+ * Push a byte array to the FIFO. Behaviour is undefined is the FIFO is
+ * full. Clients are responsible for checking the space left in the FIFO using
+ * fifo8_has_space().
+ */
+
+void fifo8_push_all(Fifo8 *fifo, const uint8_t *data, uint32_t num);
+
+/**
  * fifo8_pop:
  * @fifo: fifo to pop from
  *
@@ -56,6 +69,24 @@ void fifo8_push(Fifo8 *fifo, uint8_t data);
 uint8_t fifo8_pop(Fifo8 *fifo);
 
 /**
+ * fifo8_pop_buf:
+ * @fifo: FIFO to pop from
+ * @max: maximum number of bytes to pop
+ * @num: actual number of returned bytes
+ *
+ * Pop a number of elements from the FIFO up to a maximum of max. The buffer
+ * containing the popped data is returned. This buffer points directly into
+ * the FIFO backing store and data is invalidated once any of the fifo8_* APIs
+ * are called on the FIFO.
+ *
+ * May short return, the number of valid bytes returned is populated in
+ * *num. Will always return at least 1 byte.
+ *
+ * Behavior is undefined if max == 0.
+ */
+const uint8_t *fifo8_pop_buf(Fifo8 *fifo, uint32_t max, uint32_t *num);
+
+/**
  * fifo8_reset:
  * @fifo: FIFO to reset
  *
@@ -86,6 +117,18 @@ bool fifo8_is_empty(Fifo8 *fifo);
 
 bool fifo8_is_full(Fifo8 *fifo);
 
+/**
+ * fifo8_has_space:
+ * @fifo: FIFO to check
+ * @num: number of bytes
+ *
+ * Check if a given number of bytes can be pushed to a FIFO.
+ *
+ * Returns: True if the fifo can hold the given elements, false otherwise.
+ */
+
+bool fifo8_has_space(Fifo8 *fifo, uint32_t num);
+
 extern const VMStateDescription vmstate_fifo8;
 
 #define VMSTATE_FIFO8(_field, _state) {                              \
diff --git a/util/fifo8.c b/util/fifo8.c
index 013e903..2808169 100644
--- a/util/fifo8.c
+++ b/util/fifo8.c
@@ -15,6 +15,8 @@
 #include "qemu-common.h"
 #include "qemu/fifo8.h"
 
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
 void fifo8_create(Fifo8 *fifo, uint32_t capacity)
 {
     fifo->data = g_new(uint8_t, capacity);
@@ -37,6 +39,27 @@ void fifo8_push(Fifo8 *fifo, uint8_t data)
     fifo->num++;
 }
 
+void fifo8_push_all(Fifo8 *fifo, const uint8_t *data, uint32_t num)
+{
+    uint32_t start, avail;
+
+    if (fifo->num + num > fifo->capacity) {
+        abort();
+    }
+
+    start = (fifo->head + fifo->num) % fifo->capacity;
+
+    if (start + num <= fifo->capacity) {
+        memcpy(&fifo->data[start], data, num);
+    } else {
+        avail = fifo->capacity - start;
+        memcpy(&fifo->data[start], data, avail);
+        memcpy(&fifo->data[0], &data[avail], num - avail);
+    }
+
+    fifo->num += num;
+}
+
 uint8_t fifo8_pop(Fifo8 *fifo)
 {
     uint8_t ret;
@@ -50,9 +73,25 @@ uint8_t fifo8_pop(Fifo8 *fifo)
     return ret;
 }
 
+const uint8_t *fifo8_pop_buf(Fifo8 *fifo, uint32_t max, uint32_t *num)
+{
+    uint8_t *ret;
+
+    *num = min(fifo->capacity - fifo->head, max);
+    *num = min(*num, fifo->num);
+
+    ret = &fifo->data[fifo->head];
+
+    fifo->head += *num;
+    fifo->head %= fifo->capacity;
+
+    return ret;
+}
+
 void fifo8_reset(Fifo8 *fifo)
 {
     fifo->num = 0;
+    fifo->head = 0;
 }
 
 bool fifo8_is_empty(Fifo8 *fifo)
@@ -65,6 +104,11 @@ bool fifo8_is_full(Fifo8 *fifo)
     return (fifo->num == fifo->capacity);
 }
 
+bool fifo8_has_space(Fifo8 *fifo, uint32_t num)
+{
+    return (fifo->num + num <= fifo->capacity);
+}
+
 const VMStateDescription vmstate_fifo8 = {
     .name = "Fifo8",
     .version_id = 1,
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [Qemu-devel] [PATCH v4 2/3] hw/net: add support for Allwinner EMAC Fast Ethernet controller
  2014-01-26 21:39 [Qemu-devel] [PATCH v4 0/3] hw/arm: add ethernet support to Allwinner A10 Beniamino Galvani
  2014-01-26 21:39 ` [Qemu-devel] [PATCH v4 1/3] util/fifo8: implement push/pop of multiple bytes Beniamino Galvani
@ 2014-01-26 21:39 ` Beniamino Galvani
  2014-01-27 18:41   ` Peter Maydell
  2014-01-28  0:31   ` Peter Crosthwaite
  2014-01-26 21:39 ` [Qemu-devel] [PATCH v4 3/3] hw/arm/allwinner-a10: initialize EMAC Beniamino Galvani
  2 siblings, 2 replies; 14+ messages in thread
From: Beniamino Galvani @ 2014-01-26 21:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Peter Crosthwaite, Stefan Hajnoczi,
	Beniamino Galvani, Andreas Färber, Li Guang

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 <b.galvani@gmail.com>
---
 default-configs/arm-softmmu.mak |    1 +
 hw/net/Makefile.objs            |    1 +
 hw/net/allwinner_emac.c         |  532 +++++++++++++++++++++++++++++++++++++++
 include/hw/net/allwinner_emac.h |  211 ++++++++++++++++
 4 files changed, 745 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..93bad58
--- /dev/null
+++ b/hw/net/allwinner_emac.c
@@ -0,0 +1,532 @@
+/*
+ * Emulation of Allwinner EMAC Fast Ethernet controller and
+ * Realtek RTL8201CP PHY
+ *
+ * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
+ *
+ * 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 "qemu/fifo8.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 RTL8201CP_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 RTL8201CP_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(AwEmacState *s, int chan)
+{
+    fifo8_reset(&s->tx_fifo[chan]);
+    s->tx_length[chan] = 0;
+}
+
+static void aw_emac_rx_reset(AwEmacState *s)
+{
+    fifo8_reset(&s->rx_fifo);
+    s->rx_num_packets = 0;
+    s->rx_packet_size = 0;
+    s->rx_packet_pos = 0;
+}
+
+static void fifo8_push_word(Fifo8 *fifo, uint32_t val)
+{
+    fifo8_push(fifo, val);
+    fifo8_push(fifo, val >> 8);
+    fifo8_push(fifo, val >> 16);
+    fifo8_push(fifo, val >> 24);
+}
+
+static uint32_t fifo8_pop_word(Fifo8 *fifo)
+{
+    uint32_t ret;
+
+    ret = fifo8_pop(fifo);
+    ret |= fifo8_pop(fifo) << 8;
+    ret |= fifo8_pop(fifo) << 16;
+    ret |= fifo8_pop(fifo) << 24;
+
+    return ret;
+}
+
+static int aw_emac_can_receive(NetClientState *nc)
+{
+    AwEmacState *s = qemu_get_nic_opaque(nc);
+
+    /*
+     * To avoid packet drops, allow reception only when there is space
+     * for a full frame: 1522 + 8 (rx headers) + 2 (padding).
+     */
+    return (s->ctl & EMAC_CTL_RX_EN) && fifo8_has_space(&s->rx_fifo, 1532);
+}
+
+static ssize_t aw_emac_receive(NetClientState *nc, const uint8_t *buf,
+                               size_t size)
+{
+    AwEmacState *s = qemu_get_nic_opaque(nc);
+    Fifo8 *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) || !fifo8_has_space(fifo, total_size)) {
+        return -1;
+    }
+
+    fifo8_push_word(fifo, EMAC_UNDOCUMENTED_MAGIC);
+    fifo8_push_word(fifo, EMAC_RX_HEADER(padded_size + CRC_SIZE,
+                                         EMAC_RX_IO_DATA_STATUS_OK));
+    fifo8_push_all(fifo, buf, size);
+    crc = crc32(~0, buf, size);
+
+    if (padded_size != size) {
+        fifo8_push_all(fifo, padding, padded_size - size);
+        crc = crc32(crc, padding, padded_size - size);
+    }
+
+    fifo8_push_word(fifo, crc);
+    fifo8_push_all(fifo, padding, QEMU_ALIGN_UP(padded_size, 4) - padded_size);
+    s->rx_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, 0);
+    aw_emac_tx_reset(s, 1);
+    aw_emac_rx_reset(s);
+
+    mii_reset(&s->mii, !nc->link_down);
+}
+
+static uint64_t aw_emac_read(void *opaque, hwaddr offset, unsigned size)
+{
+    AwEmacState *s = opaque;
+    Fifo8 *fifo = &s->rx_fifo;
+    NetClientState *nc;
+    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 (!s->rx_num_packets) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "Read IO data register when no packet available");
+            return 0;
+        }
+
+        ret = fifo8_pop_word(fifo);
+
+        switch (s->rx_packet_pos) {
+        case 0:     /* Word is magic header */
+            s->rx_packet_pos += 4;
+            break;
+        case 4:     /* Word is rx info header */
+            s->rx_packet_pos += 4;
+            s->rx_packet_size = QEMU_ALIGN_UP(extract32(ret, 0, 16), 4);
+            break;
+        default:    /* Word is packet data */
+            s->rx_packet_pos += 4;
+            s->rx_packet_size -= 4;
+
+            if (!s->rx_packet_size) {
+                s->rx_packet_pos = 0;
+                s->rx_num_packets--;
+                nc = qemu_get_queue(s->nic);
+                if (aw_emac_can_receive(nc)) {
+                    qemu_flush_queued_packets(nc);
+                }
+            }
+        }
+        return ret;
+    case EMAC_RX_FBC_REG:
+        return s->rx_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 RTL8201CP_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;
+    Fifo8 *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)) {
+            uint32_t len, ret;
+            const uint8_t *data;
+
+            fifo = &s->tx_fifo[chan];
+            len = s->tx_length[chan];
+            if (len > 0) {
+                data = fifo8_pop_buf(fifo, len, &ret);
+                qemu_send_packet(nc, data, ret);
+                aw_emac_tx_reset(s, chan);
+                /* Raise TX interrupt */
+                s->int_sta |= EMAC_INT_TX_CHAN(chan);
+                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_length[chan] = value;
+        break;
+    case EMAC_TX_IO_DATA_REG:
+        fifo = &s->tx_fifo[s->tx_channel];
+        if (!fifo8_has_space(fifo, 4)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "allwinner_emac: TX data overruns fifo\n");
+            break;
+        }
+        fifo8_push_word(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);
+        }
+        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:
+        RTL8201CP_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);
+
+    fifo8_create(&s->rx_fifo, RX_FIFO_SIZE);
+    fifo8_create(&s->tx_fifo[0], TX_FIFO_SIZE);
+    fifo8_create(&s->tx_fifo[1], TX_FIFO_SIZE);
+}
+
+static Property aw_emac_properties[] = {
+    DEFINE_NIC_PROPERTIES(AwEmacState, conf),
+    DEFINE_PROP_UINT8("phy-addr", 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 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_FIFO8(rx_fifo, AwEmacState),
+        VMSTATE_UINT32(rx_num_packets, AwEmacState),
+        VMSTATE_UINT32(rx_packet_size, AwEmacState),
+        VMSTATE_UINT32(rx_packet_pos, AwEmacState),
+        VMSTATE_STRUCT_ARRAY(tx_fifo, AwEmacState, NUM_TX_FIFOS, 1,
+                             vmstate_fifo8, Fifo8),
+        VMSTATE_UINT32_ARRAY(tx_length, AwEmacState, NUM_TX_FIFOS),
+        VMSTATE_UINT32(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..8e1885e
--- /dev/null
+++ b/include/hw/net/allwinner_emac.h
@@ -0,0 +1,211 @@
+/*
+ * Emulation of Allwinner EMAC Fast Ethernet controller and
+ * Realtek RTL8201CP PHY
+ *
+ * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
+ *
+ * Allwinner EMAC register definitions from Linux kernel are:
+ *   Copyright 2012 Stefan Roese <sr@denx.de>
+ *   Copyright 2013 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *   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"
+#include "qemu/fifo8.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
+
+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;
+
+    Fifo8          rx_fifo;
+    uint32_t       rx_num_packets;
+    uint32_t       rx_packet_size;
+    uint32_t       rx_packet_pos;
+
+    Fifo8          tx_fifo[NUM_TX_FIFOS];
+    uint32_t       tx_length[NUM_TX_FIFOS];
+    uint32_t       tx_channel;
+} AwEmacState;
+
+#endif
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [Qemu-devel] [PATCH v4 3/3] hw/arm/allwinner-a10: initialize EMAC
  2014-01-26 21:39 [Qemu-devel] [PATCH v4 0/3] hw/arm: add ethernet support to Allwinner A10 Beniamino Galvani
  2014-01-26 21:39 ` [Qemu-devel] [PATCH v4 1/3] util/fifo8: implement push/pop of multiple bytes Beniamino Galvani
  2014-01-26 21:39 ` [Qemu-devel] [PATCH v4 2/3] hw/net: add support for Allwinner EMAC Fast Ethernet controller Beniamino Galvani
@ 2014-01-26 21:39 ` Beniamino Galvani
  2014-01-27 18:44   ` Peter Maydell
  2014-01-28  0:32   ` Peter Crosthwaite
  2 siblings, 2 replies; 14+ messages in thread
From: Beniamino Galvani @ 2014-01-26 21:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Peter Crosthwaite, Stefan Hajnoczi,
	Beniamino Galvani, Andreas Färber, Li Guang

Signed-off-by: Beniamino Galvani <b.galvani@gmail.com>
---
 hw/arm/allwinner-a10.c         |   16 ++++++++++++++++
 hw/arm/cubieboard.c            |   11 +++++++++--
 include/hw/arm/allwinner-a10.h |    3 +++
 3 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c
index 4658e19..01206f2 100644
--- a/hw/arm/allwinner-a10.c
+++ b/hw/arm/allwinner-a10.c
@@ -31,6 +31,13 @@ static void aw_a10_init(Object *obj)
 
     object_initialize(&s->timer, sizeof(s->timer), TYPE_AW_A10_PIT);
     qdev_set_parent_bus(DEVICE(&s->timer), sysbus_get_default());
+
+    object_initialize(&s->emac, sizeof(s->emac), TYPE_AW_EMAC);
+    qdev_set_parent_bus(DEVICE(&s->emac), sysbus_get_default());
+    if (nd_table[0].used) {
+        qemu_check_nic_model(&nd_table[0], TYPE_AW_EMAC);
+        qdev_set_nic_properties(DEVICE(&s->emac), &nd_table[0]);
+    }
 }
 
 static void aw_a10_realize(DeviceState *dev, Error **errp)
@@ -76,6 +83,15 @@ static void aw_a10_realize(DeviceState *dev, Error **errp)
     sysbus_connect_irq(sysbusdev, 4, s->irq[67]);
     sysbus_connect_irq(sysbusdev, 5, s->irq[68]);
 
+    object_property_set_bool(OBJECT(&s->emac), true, "realized", &err);
+    if (err != NULL) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbusdev = SYS_BUS_DEVICE(&s->emac);
+    sysbus_mmio_map(sysbusdev, 0, AW_A10_EMAC_BASE);
+    sysbus_connect_irq(sysbusdev, 0, s->irq[55]);
+
     serial_mm_init(get_system_memory(), AW_A10_UART0_REG_BASE, 2, s->irq[1],
                    115200, serial_hds[0], DEVICE_NATIVE_ENDIAN);
 }
diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c
index 3fcb6d2..d95a7f3 100644
--- a/hw/arm/cubieboard.c
+++ b/hw/arm/cubieboard.c
@@ -36,10 +36,17 @@ static void cubieboard_init(QEMUMachineInitArgs *args)
     Error *err = NULL;
 
     s->a10 = AW_A10(object_new(TYPE_AW_A10));
+
+    object_property_set_int(OBJECT(&s->a10->emac), 1, "phy-addr", &err);
+    if (err != NULL) {
+        error_report("Couldn't set phy address: %s", error_get_pretty(err));
+        exit(1);
+    }
+
     object_property_set_bool(OBJECT(s->a10), true, "realized", &err);
     if (err != NULL) {
-        error_report("Couldn't realize Allwinner A10: %s\n",
-                error_get_pretty(err));
+        error_report("Couldn't realize Allwinner A10: %s",
+                     error_get_pretty(err));
         exit(1);
     }
 
diff --git a/include/hw/arm/allwinner-a10.h b/include/hw/arm/allwinner-a10.h
index da36647..01a189b 100644
--- a/include/hw/arm/allwinner-a10.h
+++ b/include/hw/arm/allwinner-a10.h
@@ -6,6 +6,7 @@
 #include "hw/arm/arm.h"
 #include "hw/timer/allwinner-a10-pit.h"
 #include "hw/intc/allwinner-a10-pic.h"
+#include "hw/net/allwinner_emac.h"
 
 #include "sysemu/sysemu.h"
 #include "exec/address-spaces.h"
@@ -14,6 +15,7 @@
 #define AW_A10_PIC_REG_BASE     0x01c20400
 #define AW_A10_PIT_REG_BASE     0x01c20c00
 #define AW_A10_UART0_REG_BASE   0x01c28000
+#define AW_A10_EMAC_BASE        0x01c0b000
 
 #define AW_A10_SDRAM_BASE       0x40000000
 
@@ -29,6 +31,7 @@ typedef struct AwA10State {
     qemu_irq irq[AW_A10_PIC_INT_NR];
     AwA10PITState timer;
     AwA10PICState intc;
+    AwEmacState emac;
 } AwA10State;
 
 #define ALLWINNER_H_
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH v4 1/3] util/fifo8: implement push/pop of multiple bytes
  2014-01-26 21:39 ` [Qemu-devel] [PATCH v4 1/3] util/fifo8: implement push/pop of multiple bytes Beniamino Galvani
@ 2014-01-27 18:32   ` Peter Maydell
  2014-01-28  0:04     ` Peter Crosthwaite
  0 siblings, 1 reply; 14+ messages in thread
From: Peter Maydell @ 2014-01-27 18:32 UTC (permalink / raw)
  To: Beniamino Galvani
  Cc: Stefan Hajnoczi, Peter Crosthwaite, QEMU Developers, Li Guang,
	Andreas Färber

On 26 January 2014 21:39, Beniamino Galvani <b.galvani@gmail.com> wrote:
> In some circumstances it is useful to be able to push the entire
> content of a memory buffer to the fifo or to pop multiple bytes with a
> single operation.
>
> The functions fifo8_has_space() and fifo8_push_all() added by this
> patch allow to perform the first kind of operation efficiently.
>
> The function fifo8_pop_buf() can be used instead to pop multiple bytes
> from the fifo, returning a pointer to the backing buffer.


Thanks; a few nits below but mostly this looks OK.


> Signed-off-by: Beniamino Galvani <b.galvani@gmail.com>
> ---
>  include/qemu/fifo8.h |   43 +++++++++++++++++++++++++++++++++++++++++++
>  util/fifo8.c         |   44 ++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 87 insertions(+)
>
> diff --git a/include/qemu/fifo8.h b/include/qemu/fifo8.h
> index d318f71..ea6e9f6 100644
> --- a/include/qemu/fifo8.h
> +++ b/include/qemu/fifo8.h
> @@ -44,6 +44,19 @@ void fifo8_destroy(Fifo8 *fifo);
>  void fifo8_push(Fifo8 *fifo, uint8_t data);
>
>  /**
> + * fifo8_push_all:
> + * @fifo: FIFO to push to
> + * @data: data to push
> + * @size: number of bytes to push
> + *
> + * Push a byte array to the FIFO. Behaviour is undefined is the FIFO is

"if the FIFO"

> + * full. Clients are responsible for checking the space left in the FIFO using
> + * fifo8_has_space().
> + */
> +
> +void fifo8_push_all(Fifo8 *fifo, const uint8_t *data, uint32_t num);
> +
> +/**
>   * fifo8_pop:
>   * @fifo: fifo to pop from
>   *
> @@ -56,6 +69,24 @@ void fifo8_push(Fifo8 *fifo, uint8_t data);
>  uint8_t fifo8_pop(Fifo8 *fifo);
>
>  /**
> + * fifo8_pop_buf:
> + * @fifo: FIFO to pop from
> + * @max: maximum number of bytes to pop
> + * @num: actual number of returned bytes
> + *
> + * Pop a number of elements from the FIFO up to a maximum of max. The buffer
> + * containing the popped data is returned. This buffer points directly into
> + * the FIFO backing store and data is invalidated once any of the fifo8_* APIs
> + * are called on the FIFO.
> + *
> + * May short return, the number of valid bytes returned is populated in
> + * *num. Will always return at least 1 byte.

What does "May short return" mean here? Rephrasing might help.

Like fifo8_pop, you should say that clients are responsible
for checking that the fifo is empty before calling this.

> + *
> + * Behavior is undefined if max == 0.
> + */
> +const uint8_t *fifo8_pop_buf(Fifo8 *fifo, uint32_t max, uint32_t *num);
> +
> +/**
>   * fifo8_reset:
>   * @fifo: FIFO to reset
>   *
> @@ -86,6 +117,18 @@ bool fifo8_is_empty(Fifo8 *fifo);
>
>  bool fifo8_is_full(Fifo8 *fifo);
>
> +/**
> + * fifo8_has_space:
> + * @fifo: FIFO to check
> + * @num: number of bytes
> + *
> + * Check if a given number of bytes can be pushed to a FIFO.
> + *
> + * Returns: True if the fifo can hold the given elements, false otherwise.
> + */
> +
> +bool fifo8_has_space(Fifo8 *fifo, uint32_t num);
> +
>  extern const VMStateDescription vmstate_fifo8;
>
>  #define VMSTATE_FIFO8(_field, _state) {                              \
> diff --git a/util/fifo8.c b/util/fifo8.c
> index 013e903..2808169 100644
> --- a/util/fifo8.c
> +++ b/util/fifo8.c
> @@ -15,6 +15,8 @@
>  #include "qemu-common.h"
>  #include "qemu/fifo8.h"
>
> +#define min(a, b) ((a) < (b) ? (a) : (b))

Just use MIN, the QEMU include files define it for you.

>  void fifo8_create(Fifo8 *fifo, uint32_t capacity)
>  {
>      fifo->data = g_new(uint8_t, capacity);
> @@ -37,6 +39,27 @@ void fifo8_push(Fifo8 *fifo, uint8_t data)
>      fifo->num++;
>  }
>
> +void fifo8_push_all(Fifo8 *fifo, const uint8_t *data, uint32_t num)
> +{
> +    uint32_t start, avail;
> +
> +    if (fifo->num + num > fifo->capacity) {
> +        abort();
> +    }
> +
> +    start = (fifo->head + fifo->num) % fifo->capacity;
> +
> +    if (start + num <= fifo->capacity) {
> +        memcpy(&fifo->data[start], data, num);
> +    } else {
> +        avail = fifo->capacity - start;
> +        memcpy(&fifo->data[start], data, avail);
> +        memcpy(&fifo->data[0], &data[avail], num - avail);
> +    }
> +
> +    fifo->num += num;
> +}
> +
>  uint8_t fifo8_pop(Fifo8 *fifo)
>  {
>      uint8_t ret;
> @@ -50,9 +73,25 @@ uint8_t fifo8_pop(Fifo8 *fifo)
>      return ret;
>  }
>
> +const uint8_t *fifo8_pop_buf(Fifo8 *fifo, uint32_t max, uint32_t *num)
> +{
> +    uint8_t *ret;
> +
> +    *num = min(fifo->capacity - fifo->head, max);
> +    *num = min(*num, fifo->num);
> +
> +    ret = &fifo->data[fifo->head];
> +
> +    fifo->head += *num;
> +    fifo->head %= fifo->capacity;
> +
> +    return ret;
> +}
> +
>  void fifo8_reset(Fifo8 *fifo)
>  {
>      fifo->num = 0;
> +    fifo->head = 0;

This is a bug fix, right? It should go in its own patch.

>  }
>
>  bool fifo8_is_empty(Fifo8 *fifo)
> @@ -65,6 +104,11 @@ bool fifo8_is_full(Fifo8 *fifo)
>      return (fifo->num == fifo->capacity);
>  }
>
> +bool fifo8_has_space(Fifo8 *fifo, uint32_t num)
> +{
> +    return (fifo->num + num <= fifo->capacity);
> +}
> +
>  const VMStateDescription vmstate_fifo8 = {
>      .name = "Fifo8",
>      .version_id = 1,
> --
> 1.7.10.4
>

thanks
-- PMM

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH v4 2/3] hw/net: add support for Allwinner EMAC Fast Ethernet controller
  2014-01-26 21:39 ` [Qemu-devel] [PATCH v4 2/3] hw/net: add support for Allwinner EMAC Fast Ethernet controller Beniamino Galvani
@ 2014-01-27 18:41   ` Peter Maydell
  2014-01-28  0:31   ` Peter Crosthwaite
  1 sibling, 0 replies; 14+ messages in thread
From: Peter Maydell @ 2014-01-27 18:41 UTC (permalink / raw)
  To: Beniamino Galvani
  Cc: Stefan Hajnoczi, Peter Crosthwaite, QEMU Developers, Li Guang,
	Andreas Färber

On 26 January 2014 21:39, Beniamino Galvani <b.galvani@gmail.com> wrote:
> 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.

It would be nice to mention that the device model is based
from reverse-engineering from the Linux kernel sources in a
comment in the allwinner_emac.c too, for the benefit of anybody
else trying to deal with the model later.

> --- /dev/null
> +++ b/include/hw/net/allwinner_emac.h
> @@ -0,0 +1,211 @@
> +/*
> + * Emulation of Allwinner EMAC Fast Ethernet controller and
> + * Realtek RTL8201CP PHY
> + *
> + * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
> + *
> + * Allwinner EMAC register definitions from Linux kernel are:
> + *   Copyright 2012 Stefan Roese <sr@denx.de>
> + *   Copyright 2013 Maxime Ripard <maxime.ripard@free-electrons.com>
> + *   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.

Either the register definitions are significant enough for
copyright to apply, in which case the license comes with
them and they must be GPLv2-only (since that's what the Linux
kernel is), or they're not significant enough, in which case
you can ditch the copyright attribution.

I would suggest you err on the safe side and make this file
GPLv2-only.

If you fix those two things you can mark your next version
of this patch
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>

thanks
-- PMM

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/3] hw/arm/allwinner-a10: initialize EMAC
  2014-01-26 21:39 ` [Qemu-devel] [PATCH v4 3/3] hw/arm/allwinner-a10: initialize EMAC Beniamino Galvani
@ 2014-01-27 18:44   ` Peter Maydell
  2014-01-28  0:32   ` Peter Crosthwaite
  1 sibling, 0 replies; 14+ messages in thread
From: Peter Maydell @ 2014-01-27 18:44 UTC (permalink / raw)
  To: Beniamino Galvani
  Cc: Stefan Hajnoczi, Peter Crosthwaite, QEMU Developers, Li Guang,
	Andreas Färber

On 26 January 2014 21:39, Beniamino Galvani <b.galvani@gmail.com> wrote:
> Signed-off-by: Beniamino Galvani <b.galvani@gmail.com>

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>

thanks
-- PMM

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH v4 1/3] util/fifo8: implement push/pop of multiple bytes
  2014-01-27 18:32   ` Peter Maydell
@ 2014-01-28  0:04     ` Peter Crosthwaite
  2014-01-28 10:43       ` Peter Maydell
  2014-01-28 18:44       ` Beniamino Galvani
  0 siblings, 2 replies; 14+ messages in thread
From: Peter Crosthwaite @ 2014-01-28  0:04 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Beniamino Galvani, Stefan Hajnoczi, QEMU Developers, Li Guang,
	Andreas Färber

On Tue, Jan 28, 2014 at 4:32 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 26 January 2014 21:39, Beniamino Galvani <b.galvani@gmail.com> wrote:
>> In some circumstances it is useful to be able to push the entire
>> content of a memory buffer to the fifo or to pop multiple bytes with a
>> single operation.
>>
>> The functions fifo8_has_space() and fifo8_push_all() added by this
>> patch allow to perform the first kind of operation efficiently.
>>
>> The function fifo8_pop_buf() can be used instead to pop multiple bytes
>> from the fifo, returning a pointer to the backing buffer.
>
>
> Thanks; a few nits below but mostly this looks OK.
>
>
>> Signed-off-by: Beniamino Galvani <b.galvani@gmail.com>
>> ---
>>  include/qemu/fifo8.h |   43 +++++++++++++++++++++++++++++++++++++++++++
>>  util/fifo8.c         |   44 ++++++++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 87 insertions(+)
>>
>> diff --git a/include/qemu/fifo8.h b/include/qemu/fifo8.h
>> index d318f71..ea6e9f6 100644
>> --- a/include/qemu/fifo8.h
>> +++ b/include/qemu/fifo8.h
>> @@ -44,6 +44,19 @@ void fifo8_destroy(Fifo8 *fifo);
>>  void fifo8_push(Fifo8 *fifo, uint8_t data);
>>
>>  /**
>> + * fifo8_push_all:
>> + * @fifo: FIFO to push to
>> + * @data: data to push
>> + * @size: number of bytes to push
>> + *
>> + * Push a byte array to the FIFO. Behaviour is undefined is the FIFO is
>
> "if the FIFO"
>
>> + * full. Clients are responsible for checking the space left in the FIFO using
>> + * fifo8_has_space().
>> + */
>> +
>> +void fifo8_push_all(Fifo8 *fifo, const uint8_t *data, uint32_t num);
>> +
>> +/**
>>   * fifo8_pop:
>>   * @fifo: fifo to pop from
>>   *
>> @@ -56,6 +69,24 @@ void fifo8_push(Fifo8 *fifo, uint8_t data);
>>  uint8_t fifo8_pop(Fifo8 *fifo);
>>
>>  /**
>> + * fifo8_pop_buf:
>> + * @fifo: FIFO to pop from
>> + * @max: maximum number of bytes to pop
>> + * @num: actual number of returned bytes
>> + *
>> + * Pop a number of elements from the FIFO up to a maximum of max. The buffer
>> + * containing the popped data is returned. This buffer points directly into
>> + * the FIFO backing store and data is invalidated once any of the fifo8_* APIs
>> + * are called on the FIFO.
>> + *
>> + * May short return, the number of valid bytes returned is populated in
>> + * *num. Will always return at least 1 byte.
>
> What does "May short return" mean here? Rephrasing might help.
>
> Like fifo8_pop, you should say that clients are responsible
> for checking that the fifo is empty before calling this.
>
>> + *
>> + * Behavior is undefined if max == 0.
>> + */
>> +const uint8_t *fifo8_pop_buf(Fifo8 *fifo, uint32_t max, uint32_t *num);
>> +
>> +/**
>>   * fifo8_reset:
>>   * @fifo: FIFO to reset
>>   *
>> @@ -86,6 +117,18 @@ bool fifo8_is_empty(Fifo8 *fifo);
>>
>>  bool fifo8_is_full(Fifo8 *fifo);
>>
>> +/**
>> + * fifo8_has_space:
>> + * @fifo: FIFO to check
>> + * @num: number of bytes
>> + *
>> + * Check if a given number of bytes can be pushed to a FIFO.
>> + *
>> + * Returns: True if the fifo can hold the given elements, false otherwise.
>> + */
>> +
>> +bool fifo8_has_space(Fifo8 *fifo, uint32_t num);
>> +
>>  extern const VMStateDescription vmstate_fifo8;
>>
>>  #define VMSTATE_FIFO8(_field, _state) {                              \
>> diff --git a/util/fifo8.c b/util/fifo8.c
>> index 013e903..2808169 100644
>> --- a/util/fifo8.c
>> +++ b/util/fifo8.c
>> @@ -15,6 +15,8 @@
>>  #include "qemu-common.h"
>>  #include "qemu/fifo8.h"
>>
>> +#define min(a, b) ((a) < (b) ? (a) : (b))
>
> Just use MIN, the QEMU include files define it for you.
>
>>  void fifo8_create(Fifo8 *fifo, uint32_t capacity)
>>  {
>>      fifo->data = g_new(uint8_t, capacity);
>> @@ -37,6 +39,27 @@ void fifo8_push(Fifo8 *fifo, uint8_t data)
>>      fifo->num++;
>>  }
>>
>> +void fifo8_push_all(Fifo8 *fifo, const uint8_t *data, uint32_t num)
>> +{
>> +    uint32_t start, avail;
>> +
>> +    if (fifo->num + num > fifo->capacity) {
>> +        abort();
>> +    }
>> +
>> +    start = (fifo->head + fifo->num) % fifo->capacity;
>> +
>> +    if (start + num <= fifo->capacity) {
>> +        memcpy(&fifo->data[start], data, num);
>> +    } else {
>> +        avail = fifo->capacity - start;
>> +        memcpy(&fifo->data[start], data, avail);
>> +        memcpy(&fifo->data[0], &data[avail], num - avail);
>> +    }
>> +
>> +    fifo->num += num;
>> +}
>> +
>>  uint8_t fifo8_pop(Fifo8 *fifo)
>>  {
>>      uint8_t ret;
>> @@ -50,9 +73,25 @@ uint8_t fifo8_pop(Fifo8 *fifo)
>>      return ret;
>>  }
>>
>> +const uint8_t *fifo8_pop_buf(Fifo8 *fifo, uint32_t max, uint32_t *num)
>> +{
>> +    uint8_t *ret;
>> +
>> +    *num = min(fifo->capacity - fifo->head, max);
>> +    *num = min(*num, fifo->num);
>> +
>> +    ret = &fifo->data[fifo->head];
>> +
>> +    fifo->head += *num;
>> +    fifo->head %= fifo->capacity;
>> +
>> +    return ret;
>> +}
>> +
>>  void fifo8_reset(Fifo8 *fifo)
>>  {
>>      fifo->num = 0;
>> +    fifo->head = 0;
>
> This is a bug fix, right? It should go in its own patch.
>

No bug - where the ring buffer starts following a reset is undefined
and need not be defined. But it improves the predicatability of the
newly added pop_buf fn as you can now following a reset, guarantee
that a single pop_buf will take all contents if its the first pop
(which is how its being used in P2).

>>  }
>>
>>  bool fifo8_is_empty(Fifo8 *fifo)
>> @@ -65,6 +104,11 @@ bool fifo8_is_full(Fifo8 *fifo)
>>      return (fifo->num == fifo->capacity);
>>  }
>>
>> +bool fifo8_has_space(Fifo8 *fifo, uint32_t num)
>> +{
>> +    return (fifo->num + num <= fifo->capacity);
>> +}
>> +

This is a little specific and perhaps limited in usage. Can we just
add occupancy/vacancy getters and let the caller do what it wants:

uint32_t fifo8_num_free(Fifo *fifo) {
    return fifo->capacity - fifo->num;
}

Regards,
Peter

>>  const VMStateDescription vmstate_fifo8 = {
>>      .name = "Fifo8",
>>      .version_id = 1,
>> --
>> 1.7.10.4
>>
>
> thanks
> -- PMM
>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH v4 2/3] hw/net: add support for Allwinner EMAC Fast Ethernet controller
  2014-01-26 21:39 ` [Qemu-devel] [PATCH v4 2/3] hw/net: add support for Allwinner EMAC Fast Ethernet controller Beniamino Galvani
  2014-01-27 18:41   ` Peter Maydell
@ 2014-01-28  0:31   ` Peter Crosthwaite
  1 sibling, 0 replies; 14+ messages in thread
From: Peter Crosthwaite @ 2014-01-28  0:31 UTC (permalink / raw)
  To: Beniamino Galvani
  Cc: Peter Maydell, Li Guang, qemu-devel@nongnu.org Developers,
	Andreas Färber, Stefan Hajnoczi

On Mon, Jan 27, 2014 at 7:39 AM, Beniamino Galvani <b.galvani@gmail.com> wrote:
> 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 <b.galvani@gmail.com>
> ---
>  default-configs/arm-softmmu.mak |    1 +
>  hw/net/Makefile.objs            |    1 +
>  hw/net/allwinner_emac.c         |  532 +++++++++++++++++++++++++++++++++++++++
>  include/hw/net/allwinner_emac.h |  211 ++++++++++++++++
>  4 files changed, 745 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..93bad58
> --- /dev/null
> +++ b/hw/net/allwinner_emac.c
> @@ -0,0 +1,532 @@
> +/*
> + * Emulation of Allwinner EMAC Fast Ethernet controller and
> + * Realtek RTL8201CP PHY
> + *
> + * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
> + *
> + * 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 "qemu/fifo8.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 RTL8201CP_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 RTL8201CP_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(AwEmacState *s, int chan)
> +{
> +    fifo8_reset(&s->tx_fifo[chan]);
> +    s->tx_length[chan] = 0;
> +}
> +
> +static void aw_emac_rx_reset(AwEmacState *s)
> +{
> +    fifo8_reset(&s->rx_fifo);
> +    s->rx_num_packets = 0;
> +    s->rx_packet_size = 0;
> +    s->rx_packet_pos = 0;
> +}
> +
> +static void fifo8_push_word(Fifo8 *fifo, uint32_t val)
> +{
> +    fifo8_push(fifo, val);
> +    fifo8_push(fifo, val >> 8);
> +    fifo8_push(fifo, val >> 16);
> +    fifo8_push(fifo, val >> 24);
> +}
> +
> +static uint32_t fifo8_pop_word(Fifo8 *fifo)
> +{
> +    uint32_t ret;
> +
> +    ret = fifo8_pop(fifo);
> +    ret |= fifo8_pop(fifo) << 8;
> +    ret |= fifo8_pop(fifo) << 16;
> +    ret |= fifo8_pop(fifo) << 24;
> +
> +    return ret;
> +}
> +
> +static int aw_emac_can_receive(NetClientState *nc)
> +{
> +    AwEmacState *s = qemu_get_nic_opaque(nc);
> +
> +    /*
> +     * To avoid packet drops, allow reception only when there is space
> +     * for a full frame: 1522 + 8 (rx headers) + 2 (padding).
> +     */
> +    return (s->ctl & EMAC_CTL_RX_EN) && fifo8_has_space(&s->rx_fifo, 1532);
> +}
> +
> +static ssize_t aw_emac_receive(NetClientState *nc, const uint8_t *buf,
> +                               size_t size)
> +{
> +    AwEmacState *s = qemu_get_nic_opaque(nc);
> +    Fifo8 *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) || !fifo8_has_space(fifo, total_size)) {
> +        return -1;
> +    }
> +
> +    fifo8_push_word(fifo, EMAC_UNDOCUMENTED_MAGIC);
> +    fifo8_push_word(fifo, EMAC_RX_HEADER(padded_size + CRC_SIZE,
> +                                         EMAC_RX_IO_DATA_STATUS_OK));
> +    fifo8_push_all(fifo, buf, size);
> +    crc = crc32(~0, buf, size);
> +
> +    if (padded_size != size) {
> +        fifo8_push_all(fifo, padding, padded_size - size);
> +        crc = crc32(crc, padding, padded_size - size);
> +    }
> +
> +    fifo8_push_word(fifo, crc);
> +    fifo8_push_all(fifo, padding, QEMU_ALIGN_UP(padded_size, 4) - padded_size);
> +    s->rx_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, 0);
> +    aw_emac_tx_reset(s, 1);
> +    aw_emac_rx_reset(s);
> +
> +    mii_reset(&s->mii, !nc->link_down);
> +}
> +
> +static uint64_t aw_emac_read(void *opaque, hwaddr offset, unsigned size)
> +{
> +    AwEmacState *s = opaque;
> +    Fifo8 *fifo = &s->rx_fifo;
> +    NetClientState *nc;
> +    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 (!s->rx_num_packets) {
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                          "Read IO data register when no packet available");
> +            return 0;
> +        }
> +
> +        ret = fifo8_pop_word(fifo);
> +
> +        switch (s->rx_packet_pos) {
> +        case 0:     /* Word is magic header */
> +            s->rx_packet_pos += 4;
> +            break;
> +        case 4:     /* Word is rx info header */
> +            s->rx_packet_pos += 4;
> +            s->rx_packet_size = QEMU_ALIGN_UP(extract32(ret, 0, 16), 4);
> +            break;
> +        default:    /* Word is packet data */
> +            s->rx_packet_pos += 4;
> +            s->rx_packet_size -= 4;
> +
> +            if (!s->rx_packet_size) {
> +                s->rx_packet_pos = 0;
> +                s->rx_num_packets--;
> +                nc = qemu_get_queue(s->nic);
> +                if (aw_emac_can_receive(nc)) {
> +                    qemu_flush_queued_packets(nc);
> +                }
> +            }
> +        }
> +        return ret;
> +    case EMAC_RX_FBC_REG:
> +        return s->rx_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 RTL8201CP_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;
> +    Fifo8 *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)) {
> +            uint32_t len, ret;
> +            const uint8_t *data;
> +
> +            fifo = &s->tx_fifo[chan];
> +            len = s->tx_length[chan];
> +            if (len > 0) {

s->tx_length is guest configurable so, you should catch (and
LOG_GUEST_ERROR) the case where the guest asks you to transmit more
data than you have. Adding a fifo occupancy helper would make this
easy with:

if (len > fifo8_num_used(...)) {
    len = fifo8_num_used(...);
    qemu_log(LOG_GUEST_ERROR ...
}

With this fix (and Peters two),

Reviewed-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>

> +                data = fifo8_pop_buf(fifo, len, &ret);
> +                qemu_send_packet(nc, data, ret);
> +                aw_emac_tx_reset(s, chan);
> +                /* Raise TX interrupt */
> +                s->int_sta |= EMAC_INT_TX_CHAN(chan);
> +                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_length[chan] = value;
> +        break;
> +    case EMAC_TX_IO_DATA_REG:
> +        fifo = &s->tx_fifo[s->tx_channel];
> +        if (!fifo8_has_space(fifo, 4)) {
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                          "allwinner_emac: TX data overruns fifo\n");
> +            break;
> +        }
> +        fifo8_push_word(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);
> +        }
> +        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:
> +        RTL8201CP_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);
> +
> +    fifo8_create(&s->rx_fifo, RX_FIFO_SIZE);
> +    fifo8_create(&s->tx_fifo[0], TX_FIFO_SIZE);
> +    fifo8_create(&s->tx_fifo[1], TX_FIFO_SIZE);
> +}
> +
> +static Property aw_emac_properties[] = {
> +    DEFINE_NIC_PROPERTIES(AwEmacState, conf),
> +    DEFINE_PROP_UINT8("phy-addr", 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 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_FIFO8(rx_fifo, AwEmacState),
> +        VMSTATE_UINT32(rx_num_packets, AwEmacState),
> +        VMSTATE_UINT32(rx_packet_size, AwEmacState),
> +        VMSTATE_UINT32(rx_packet_pos, AwEmacState),
> +        VMSTATE_STRUCT_ARRAY(tx_fifo, AwEmacState, NUM_TX_FIFOS, 1,
> +                             vmstate_fifo8, Fifo8),
> +        VMSTATE_UINT32_ARRAY(tx_length, AwEmacState, NUM_TX_FIFOS),
> +        VMSTATE_UINT32(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..8e1885e
> --- /dev/null
> +++ b/include/hw/net/allwinner_emac.h
> @@ -0,0 +1,211 @@
> +/*
> + * Emulation of Allwinner EMAC Fast Ethernet controller and
> + * Realtek RTL8201CP PHY
> + *
> + * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
> + *
> + * Allwinner EMAC register definitions from Linux kernel are:
> + *   Copyright 2012 Stefan Roese <sr@denx.de>
> + *   Copyright 2013 Maxime Ripard <maxime.ripard@free-electrons.com>
> + *   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"
> +#include "qemu/fifo8.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
> +
> +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;
> +
> +    Fifo8          rx_fifo;
> +    uint32_t       rx_num_packets;
> +    uint32_t       rx_packet_size;
> +    uint32_t       rx_packet_pos;
> +
> +    Fifo8          tx_fifo[NUM_TX_FIFOS];
> +    uint32_t       tx_length[NUM_TX_FIFOS];
> +    uint32_t       tx_channel;
> +} AwEmacState;
> +
> +#endif
> --
> 1.7.10.4
>
>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/3] hw/arm/allwinner-a10: initialize EMAC
  2014-01-26 21:39 ` [Qemu-devel] [PATCH v4 3/3] hw/arm/allwinner-a10: initialize EMAC Beniamino Galvani
  2014-01-27 18:44   ` Peter Maydell
@ 2014-01-28  0:32   ` Peter Crosthwaite
  1 sibling, 0 replies; 14+ messages in thread
From: Peter Crosthwaite @ 2014-01-28  0:32 UTC (permalink / raw)
  To: Beniamino Galvani
  Cc: Peter Maydell, Li Guang, qemu-devel@nongnu.org Developers,
	Andreas Färber, Stefan Hajnoczi

On Mon, Jan 27, 2014 at 7:39 AM, Beniamino Galvani <b.galvani@gmail.com> wrote:
> Signed-off-by: Beniamino Galvani <b.galvani@gmail.com>

Reviewed-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>

> ---
>  hw/arm/allwinner-a10.c         |   16 ++++++++++++++++
>  hw/arm/cubieboard.c            |   11 +++++++++--
>  include/hw/arm/allwinner-a10.h |    3 +++
>  3 files changed, 28 insertions(+), 2 deletions(-)
>
> diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c
> index 4658e19..01206f2 100644
> --- a/hw/arm/allwinner-a10.c
> +++ b/hw/arm/allwinner-a10.c
> @@ -31,6 +31,13 @@ static void aw_a10_init(Object *obj)
>
>      object_initialize(&s->timer, sizeof(s->timer), TYPE_AW_A10_PIT);
>      qdev_set_parent_bus(DEVICE(&s->timer), sysbus_get_default());
> +
> +    object_initialize(&s->emac, sizeof(s->emac), TYPE_AW_EMAC);
> +    qdev_set_parent_bus(DEVICE(&s->emac), sysbus_get_default());
> +    if (nd_table[0].used) {
> +        qemu_check_nic_model(&nd_table[0], TYPE_AW_EMAC);
> +        qdev_set_nic_properties(DEVICE(&s->emac), &nd_table[0]);
> +    }
>  }
>
>  static void aw_a10_realize(DeviceState *dev, Error **errp)
> @@ -76,6 +83,15 @@ static void aw_a10_realize(DeviceState *dev, Error **errp)
>      sysbus_connect_irq(sysbusdev, 4, s->irq[67]);
>      sysbus_connect_irq(sysbusdev, 5, s->irq[68]);
>
> +    object_property_set_bool(OBJECT(&s->emac), true, "realized", &err);
> +    if (err != NULL) {
> +        error_propagate(errp, err);
> +        return;
> +    }
> +    sysbusdev = SYS_BUS_DEVICE(&s->emac);
> +    sysbus_mmio_map(sysbusdev, 0, AW_A10_EMAC_BASE);
> +    sysbus_connect_irq(sysbusdev, 0, s->irq[55]);
> +
>      serial_mm_init(get_system_memory(), AW_A10_UART0_REG_BASE, 2, s->irq[1],
>                     115200, serial_hds[0], DEVICE_NATIVE_ENDIAN);
>  }
> diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c
> index 3fcb6d2..d95a7f3 100644
> --- a/hw/arm/cubieboard.c
> +++ b/hw/arm/cubieboard.c
> @@ -36,10 +36,17 @@ static void cubieboard_init(QEMUMachineInitArgs *args)
>      Error *err = NULL;
>
>      s->a10 = AW_A10(object_new(TYPE_AW_A10));
> +
> +    object_property_set_int(OBJECT(&s->a10->emac), 1, "phy-addr", &err);
> +    if (err != NULL) {
> +        error_report("Couldn't set phy address: %s", error_get_pretty(err));
> +        exit(1);
> +    }
> +
>      object_property_set_bool(OBJECT(s->a10), true, "realized", &err);
>      if (err != NULL) {
> -        error_report("Couldn't realize Allwinner A10: %s\n",
> -                error_get_pretty(err));
> +        error_report("Couldn't realize Allwinner A10: %s",
> +                     error_get_pretty(err));
>          exit(1);
>      }
>
> diff --git a/include/hw/arm/allwinner-a10.h b/include/hw/arm/allwinner-a10.h
> index da36647..01a189b 100644
> --- a/include/hw/arm/allwinner-a10.h
> +++ b/include/hw/arm/allwinner-a10.h
> @@ -6,6 +6,7 @@
>  #include "hw/arm/arm.h"
>  #include "hw/timer/allwinner-a10-pit.h"
>  #include "hw/intc/allwinner-a10-pic.h"
> +#include "hw/net/allwinner_emac.h"
>
>  #include "sysemu/sysemu.h"
>  #include "exec/address-spaces.h"
> @@ -14,6 +15,7 @@
>  #define AW_A10_PIC_REG_BASE     0x01c20400
>  #define AW_A10_PIT_REG_BASE     0x01c20c00
>  #define AW_A10_UART0_REG_BASE   0x01c28000
> +#define AW_A10_EMAC_BASE        0x01c0b000
>
>  #define AW_A10_SDRAM_BASE       0x40000000
>
> @@ -29,6 +31,7 @@ typedef struct AwA10State {
>      qemu_irq irq[AW_A10_PIC_INT_NR];
>      AwA10PITState timer;
>      AwA10PICState intc;
> +    AwEmacState emac;
>  } AwA10State;
>
>  #define ALLWINNER_H_
> --
> 1.7.10.4
>
>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH v4 1/3] util/fifo8: implement push/pop of multiple bytes
  2014-01-28  0:04     ` Peter Crosthwaite
@ 2014-01-28 10:43       ` Peter Maydell
  2014-01-28 18:48         ` Beniamino Galvani
  2014-01-28 18:44       ` Beniamino Galvani
  1 sibling, 1 reply; 14+ messages in thread
From: Peter Maydell @ 2014-01-28 10:43 UTC (permalink / raw)
  To: Peter Crosthwaite
  Cc: Beniamino Galvani, Stefan Hajnoczi, QEMU Developers, Li Guang,
	Andreas Färber

On 28 January 2014 00:04, Peter Crosthwaite
<peter.crosthwaite@xilinx.com> wrote:
> On Tue, Jan 28, 2014 at 4:32 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
>>>  void fifo8_reset(Fifo8 *fifo)
>>>  {
>>>      fifo->num = 0;
>>> +    fifo->head = 0;
>>
>> This is a bug fix, right? It should go in its own patch.
>>
>
> No bug - where the ring buffer starts following a reset is undefined
> and need not be defined. But it improves the predicatability of the
> newly added pop_buf fn as you can now following a reset, guarantee
> that a single pop_buf will take all contents if its the first pop
> (which is how its being used in P2).

True. I still think it should have its own patch (and
indeed it would be worth saying what you just did as
part of the commit message for that patch...)

I think it's also nicer for any state that gets migrated
to be reset cleanly.

thanks
-- PMM

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH v4 1/3] util/fifo8: implement push/pop of multiple bytes
  2014-01-28  0:04     ` Peter Crosthwaite
  2014-01-28 10:43       ` Peter Maydell
@ 2014-01-28 18:44       ` Beniamino Galvani
  1 sibling, 0 replies; 14+ messages in thread
From: Beniamino Galvani @ 2014-01-28 18:44 UTC (permalink / raw)
  To: Peter Crosthwaite
  Cc: Peter Maydell, Andreas Färber, QEMU Developers, Li Guang,
	Stefan Hajnoczi

On Tue, Jan 28, 2014 at 10:04:09AM +1000, Peter Crosthwaite wrote:
> On Tue, Jan 28, 2014 at 4:32 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
> > On 26 January 2014 21:39, Beniamino Galvani <b.galvani@gmail.com> wrote:
> >> In some circumstances it is useful to be able to push the entire
> >> content of a memory buffer to the fifo or to pop multiple bytes with a
> >> single operation.
> >>
> >> The functions fifo8_has_space() and fifo8_push_all() added by this
> >> patch allow to perform the first kind of operation efficiently.
> >>
> >> The function fifo8_pop_buf() can be used instead to pop multiple bytes
> >> from the fifo, returning a pointer to the backing buffer.
> >
> >
> > Thanks; a few nits below but mostly this looks OK.
> >
> >
> >> Signed-off-by: Beniamino Galvani <b.galvani@gmail.com>
> >> ---
> >>  include/qemu/fifo8.h |   43 +++++++++++++++++++++++++++++++++++++++++++
> >>  util/fifo8.c         |   44 ++++++++++++++++++++++++++++++++++++++++++++
> >>  2 files changed, 87 insertions(+)
> >>
> >> diff --git a/include/qemu/fifo8.h b/include/qemu/fifo8.h
> >> index d318f71..ea6e9f6 100644
> >> --- a/include/qemu/fifo8.h
> >> +++ b/include/qemu/fifo8.h
> >> @@ -44,6 +44,19 @@ void fifo8_destroy(Fifo8 *fifo);
> >>  void fifo8_push(Fifo8 *fifo, uint8_t data);
> >>
> >>  /**
> >> + * fifo8_push_all:
> >> + * @fifo: FIFO to push to
> >> + * @data: data to push
> >> + * @size: number of bytes to push
> >> + *
> >> + * Push a byte array to the FIFO. Behaviour is undefined is the FIFO is
> >
> > "if the FIFO"
> >
> >> + * full. Clients are responsible for checking the space left in the FIFO using
> >> + * fifo8_has_space().
> >> + */
> >> +
> >> +void fifo8_push_all(Fifo8 *fifo, const uint8_t *data, uint32_t num);
> >> +
> >> +/**
> >>   * fifo8_pop:
> >>   * @fifo: fifo to pop from
> >>   *
> >> @@ -56,6 +69,24 @@ void fifo8_push(Fifo8 *fifo, uint8_t data);
> >>  uint8_t fifo8_pop(Fifo8 *fifo);
> >>
> >>  /**
> >> + * fifo8_pop_buf:
> >> + * @fifo: FIFO to pop from
> >> + * @max: maximum number of bytes to pop
> >> + * @num: actual number of returned bytes
> >> + *
> >> + * Pop a number of elements from the FIFO up to a maximum of max. The buffer
> >> + * containing the popped data is returned. This buffer points directly into
> >> + * the FIFO backing store and data is invalidated once any of the fifo8_* APIs
> >> + * are called on the FIFO.
> >> + *
> >> + * May short return, the number of valid bytes returned is populated in
> >> + * *num. Will always return at least 1 byte.
> >
> > What does "May short return" mean here? Rephrasing might help.
> >
> > Like fifo8_pop, you should say that clients are responsible
> > for checking that the fifo is empty before calling this.
> >
> >> + *
> >> + * Behavior is undefined if max == 0.
> >> + */
> >> +const uint8_t *fifo8_pop_buf(Fifo8 *fifo, uint32_t max, uint32_t *num);
> >> +
> >> +/**
> >>   * fifo8_reset:
> >>   * @fifo: FIFO to reset
> >>   *
> >> @@ -86,6 +117,18 @@ bool fifo8_is_empty(Fifo8 *fifo);
> >>
> >>  bool fifo8_is_full(Fifo8 *fifo);
> >>
> >> +/**
> >> + * fifo8_has_space:
> >> + * @fifo: FIFO to check
> >> + * @num: number of bytes
> >> + *
> >> + * Check if a given number of bytes can be pushed to a FIFO.
> >> + *
> >> + * Returns: True if the fifo can hold the given elements, false otherwise.
> >> + */
> >> +
> >> +bool fifo8_has_space(Fifo8 *fifo, uint32_t num);
> >> +
> >>  extern const VMStateDescription vmstate_fifo8;
> >>
> >>  #define VMSTATE_FIFO8(_field, _state) {                              \
> >> diff --git a/util/fifo8.c b/util/fifo8.c
> >> index 013e903..2808169 100644
> >> --- a/util/fifo8.c
> >> +++ b/util/fifo8.c
> >> @@ -15,6 +15,8 @@
> >>  #include "qemu-common.h"
> >>  #include "qemu/fifo8.h"
> >>
> >> +#define min(a, b) ((a) < (b) ? (a) : (b))
> >
> > Just use MIN, the QEMU include files define it for you.
> >
> >>  void fifo8_create(Fifo8 *fifo, uint32_t capacity)
> >>  {
> >>      fifo->data = g_new(uint8_t, capacity);
> >> @@ -37,6 +39,27 @@ void fifo8_push(Fifo8 *fifo, uint8_t data)
> >>      fifo->num++;
> >>  }
> >>
> >> +void fifo8_push_all(Fifo8 *fifo, const uint8_t *data, uint32_t num)
> >> +{
> >> +    uint32_t start, avail;
> >> +
> >> +    if (fifo->num + num > fifo->capacity) {
> >> +        abort();
> >> +    }
> >> +
> >> +    start = (fifo->head + fifo->num) % fifo->capacity;
> >> +
> >> +    if (start + num <= fifo->capacity) {
> >> +        memcpy(&fifo->data[start], data, num);
> >> +    } else {
> >> +        avail = fifo->capacity - start;
> >> +        memcpy(&fifo->data[start], data, avail);
> >> +        memcpy(&fifo->data[0], &data[avail], num - avail);
> >> +    }
> >> +
> >> +    fifo->num += num;
> >> +}
> >> +
> >>  uint8_t fifo8_pop(Fifo8 *fifo)
> >>  {
> >>      uint8_t ret;
> >> @@ -50,9 +73,25 @@ uint8_t fifo8_pop(Fifo8 *fifo)
> >>      return ret;
> >>  }
> >>
> >> +const uint8_t *fifo8_pop_buf(Fifo8 *fifo, uint32_t max, uint32_t *num)
> >> +{
> >> +    uint8_t *ret;
> >> +
> >> +    *num = min(fifo->capacity - fifo->head, max);
> >> +    *num = min(*num, fifo->num);
> >> +
> >> +    ret = &fifo->data[fifo->head];
> >> +
> >> +    fifo->head += *num;
> >> +    fifo->head %= fifo->capacity;
> >> +
> >> +    return ret;
> >> +}
> >> +
> >>  void fifo8_reset(Fifo8 *fifo)
> >>  {
> >>      fifo->num = 0;
> >> +    fifo->head = 0;
> >
> > This is a bug fix, right? It should go in its own patch.
> >
> 
> No bug - where the ring buffer starts following a reset is undefined
> and need not be defined. But it improves the predicatability of the
> newly added pop_buf fn as you can now following a reset, guarantee
> that a single pop_buf will take all contents if its the first pop
> (which is how its being used in P2).

Right; in the next patch I will add an explanation.
 
> >>  }
> >>
> >>  bool fifo8_is_empty(Fifo8 *fifo)
> >> @@ -65,6 +104,11 @@ bool fifo8_is_full(Fifo8 *fifo)
> >>      return (fifo->num == fifo->capacity);
> >>  }
> >>
> >> +bool fifo8_has_space(Fifo8 *fifo, uint32_t num)
> >> +{
> >> +    return (fifo->num + num <= fifo->capacity);
> >> +}
> >> +
> 
> This is a little specific and perhaps limited in usage. Can we just
> add occupancy/vacancy getters and let the caller do what it wants:
> 
> uint32_t fifo8_num_free(Fifo *fifo) {
>     return fifo->capacity - fifo->num;
> }

Ok.

Thanks,
Beniamino

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH v4 1/3] util/fifo8: implement push/pop of multiple bytes
  2014-01-28 10:43       ` Peter Maydell
@ 2014-01-28 18:48         ` Beniamino Galvani
  2014-01-29 12:02           ` Peter Crosthwaite
  0 siblings, 1 reply; 14+ messages in thread
From: Beniamino Galvani @ 2014-01-28 18:48 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Stefan Hajnoczi, Peter Crosthwaite, QEMU Developers, Li Guang,
	Andreas Färber

On Tue, Jan 28, 2014 at 10:43:28AM +0000, Peter Maydell wrote:
> On 28 January 2014 00:04, Peter Crosthwaite
> <peter.crosthwaite@xilinx.com> wrote:
> > On Tue, Jan 28, 2014 at 4:32 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
> >>>  void fifo8_reset(Fifo8 *fifo)
> >>>  {
> >>>      fifo->num = 0;
> >>> +    fifo->head = 0;
> >>
> >> This is a bug fix, right? It should go in its own patch.
> >>
> >
> > No bug - where the ring buffer starts following a reset is undefined
> > and need not be defined. But it improves the predicatability of the
> > newly added pop_buf fn as you can now following a reset, guarantee
> > that a single pop_buf will take all contents if its the first pop
> > (which is how its being used in P2).
> 
> True. I still think it should have its own patch (and
> indeed it would be worth saying what you just did as
> part of the commit message for that patch...)

Ok, I will move the change to a new patch.

> I think it's also nicer for any state that gets migrated
> to be reset cleanly.

Do you mean also the buffer content? In the emac the tx fifo gets
reset after each transmission. Isn't this too costly?

Beniamino

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH v4 1/3] util/fifo8: implement push/pop of multiple bytes
  2014-01-28 18:48         ` Beniamino Galvani
@ 2014-01-29 12:02           ` Peter Crosthwaite
  0 siblings, 0 replies; 14+ messages in thread
From: Peter Crosthwaite @ 2014-01-29 12:02 UTC (permalink / raw)
  To: Beniamino Galvani
  Cc: Peter Maydell, Andreas Färber, QEMU Developers, Li Guang,
	Stefan Hajnoczi

On Wed, Jan 29, 2014 at 4:48 AM, Beniamino Galvani <b.galvani@gmail.com> wrote:
> On Tue, Jan 28, 2014 at 10:43:28AM +0000, Peter Maydell wrote:
>> On 28 January 2014 00:04, Peter Crosthwaite
>> <peter.crosthwaite@xilinx.com> wrote:
>> > On Tue, Jan 28, 2014 at 4:32 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
>> >>>  void fifo8_reset(Fifo8 *fifo)
>> >>>  {
>> >>>      fifo->num = 0;
>> >>> +    fifo->head = 0;
>> >>
>> >> This is a bug fix, right? It should go in its own patch.
>> >>
>> >
>> > No bug - where the ring buffer starts following a reset is undefined
>> > and need not be defined. But it improves the predicatability of the
>> > newly added pop_buf fn as you can now following a reset, guarantee
>> > that a single pop_buf will take all contents if its the first pop
>> > (which is how its being used in P2).
>>
>> True. I still think it should have its own patch (and
>> indeed it would be worth saying what you just did as
>> part of the commit message for that patch...)
>
> Ok, I will move the change to a new patch.
>
>> I think it's also nicer for any state that gets migrated
>> to be reset cleanly.
>
> Do you mean also the buffer content? In the emac the tx fifo gets
> reset after each transmission. Isn't this too costly?
>

I would say yes, it is too costly. Out-of-range buffer content is
undefined and there is no good reason that I can see to memset to 0 at
the cost of performance. If we are worried about stale information
leak via VMSD images or anything wierd like that then it should be
handled pre-save and post-load - not here in reset() which is
potentially a fast path.

However resetting the head pointer as you have done in this change is
a win-win, as it's both cleaner (as per Peter's argument) as well as
increasing performance for your use case.

Regards.
Peter

> Beniamino
>

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2014-01-29 12:02 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-01-26 21:39 [Qemu-devel] [PATCH v4 0/3] hw/arm: add ethernet support to Allwinner A10 Beniamino Galvani
2014-01-26 21:39 ` [Qemu-devel] [PATCH v4 1/3] util/fifo8: implement push/pop of multiple bytes Beniamino Galvani
2014-01-27 18:32   ` Peter Maydell
2014-01-28  0:04     ` Peter Crosthwaite
2014-01-28 10:43       ` Peter Maydell
2014-01-28 18:48         ` Beniamino Galvani
2014-01-29 12:02           ` Peter Crosthwaite
2014-01-28 18:44       ` Beniamino Galvani
2014-01-26 21:39 ` [Qemu-devel] [PATCH v4 2/3] hw/net: add support for Allwinner EMAC Fast Ethernet controller Beniamino Galvani
2014-01-27 18:41   ` Peter Maydell
2014-01-28  0:31   ` Peter Crosthwaite
2014-01-26 21:39 ` [Qemu-devel] [PATCH v4 3/3] hw/arm/allwinner-a10: initialize EMAC Beniamino Galvani
2014-01-27 18:44   ` Peter Maydell
2014-01-28  0:32   ` Peter Crosthwaite

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).