diff mbox

[RFC,4/7] cadence_gem: first revision

Message ID 0487f9705e9a7b0cdc2ce01eac446e24f4271cb0.1327302677.git.peter.crosthwaite@petalogix.com
State New
Headers show

Commit Message

Peter A. G. Crosthwaite Jan. 23, 2012, 7:20 a.m. UTC
Device mode for cadence gem ethernet controller.

Signed-off-by: Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com>
---
 Makefile.target  |    1 +
 hw/cadence_gem.c | 1441 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1442 insertions(+), 0 deletions(-)
 create mode 100644 hw/cadence_gem.c

Comments

John Linn Jan. 24, 2012, 2:05 a.m. UTC | #1
> -----Original Message-----
> From: Peter A. G. Crosthwaite [mailto:peter.crosthwaite@petalogix.com]
> Sent: Sunday, January 22, 2012 11:21 PM
> To: qemu-devel@nongnu.org; monstr@monstr.eu;
> john.williams@petalogix.com; peter.crosthwaite@petalogix.com;
> edgar.iglesias@petalogix.com; Duy Le; John Linn
> Subject: [RFC PATCH 4/7] cadence_gem: first revision
> 
> Device mode for cadence gem ethernet controller.
> 
> Signed-off-by: Peter A. G. Crosthwaite
> <peter.crosthwaite@petalogix.com>

Signed-off-by: John Linn <john.linn@xilinx.com>

*** My apologies, please ignore the legal footer below which I'm working
on getting removed ***

This email and any attachments are intended for the sole use of the named recipient(s) and contain(s) confidential information that may be proprietary, privileged or copyrighted under applicable law. If you are not the intended recipient, do not read, copy, or forward this email message or any attachments. Delete this email message and any attachments immediately.
diff mbox

Patch

diff --git a/Makefile.target b/Makefile.target
index c7abcde..e62ff59 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -337,6 +337,7 @@  obj-arm-y += versatile_pci.o
 obj-arm-y += cadence_uart.o
 obj-arm-y += cadence_ttc.o
 obj-arm-y += cadence_wdt.o
+obj-arm-y += cadence_gem.o
 obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
 obj-arm-y += arm_l2x0.o
 obj-arm-y += arm_mptimer.o
diff --git a/hw/cadence_gem.c b/hw/cadence_gem.c
new file mode 100644
index 0000000..76bcb70
--- /dev/null
+++ b/hw/cadence_gem.c
@@ -0,0 +1,1441 @@ 
+/*
+ * QEMU Xilinx GEM emulation
+ *
+ * Copyright (c) 2011 Xilinx, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "net.h"
+#include "devices.h"
+#include "sysemu.h"
+#include "net/checksum.h"
+
+/* For crc32 */
+#include <zlib.h>
+
+#include <strings.h>
+
+#define GEM_NWCTRL      0x00000000 /* Network Control reg */
+#define GEM_NWCFG       0x00000004 /* Network Config reg */
+#define GEM_NWSTATUS    0x00000008 /* Network Status reg */
+#define GEM_USERIO      0x0000000C /* User IO reg */
+#define GEM_DMACFG      0x00000010 /* DMA Control reg */
+#define GEM_TXSTATUS    0x00000014 /* TX Status reg */
+#define GEM_RXQBASE     0x00000018 /* RX Q Base address reg */
+#define GEM_TXQBASE     0x0000001C /* TX Q Base address reg */
+#define GEM_RXSTATUS    0x00000020 /* RX Status reg */
+#define GEM_ISR         0x00000024 /* Interrupt Status reg */
+#define GEM_IER         0x00000028 /* Interrupt Enable reg */
+#define GEM_IDR         0x0000002C /* Interrupt Disable reg */
+#define GEM_IMR         0x00000030 /* Interrupt Mask reg */
+#define GEM_PHYMNTNC    0x00000034 /* Phy Maintaince reg */
+#define GEM_RXPAUSE     0x00000038 /* RX Pause Time reg */
+#define GEM_TXPAUSE     0x0000003C /* TX Pause Time reg */
+#define GEM_TXPARTIALSF 0x00000040 /* TX Partial Store and Forward */
+#define GEM_RXPARTIALSF 0x00000044 /* RX Partial Store and Forward */
+#define GEM_HASHLO      0x00000080 /* Hash Low address reg */
+#define GEM_HASHHI      0x00000084 /* Hash High address reg */
+#define GEM_SPADDR1LO   0x00000088 /* Specific addr 1 low reg */
+#define GEM_SPADDR1HI   0x0000008C /* Specific addr 1 high reg */
+#define GEM_SPADDR2LO   0x00000090 /* Specific addr 2 low reg */
+#define GEM_SPADDR2HI   0x00000094 /* Specific addr 2 high reg */
+#define GEM_SPADDR3LO   0x00000098 /* Specific addr 3 low reg */
+#define GEM_SPADDR3HI   0x0000009C /* Specific addr 3 high reg */
+#define GEM_SPADDR4LO   0x000000A0 /* Specific addr 4 low reg */
+#define GEM_SPADDR4HI   0x000000A4 /* Specific addr 4 high reg */
+#define GEM_TIDMATCH1   0x000000A8 /* Type ID1 Match reg */
+#define GEM_TIDMATCH2   0x000000AC /* Type ID2 Match reg */
+#define GEM_TIDMATCH3   0x000000B0 /* Type ID3 Match reg */
+#define GEM_TIDMATCH4   0x000000B4 /* Type ID4 Match reg */
+#define GEM_WOLAN       0x000000B8 /* Wake on LAN reg */
+#define GEM_IPGSTRETCH  0x000000BC /* IPG Stretch reg */
+#define GEM_SVLAN       0x000000C0 /* Stacked VLAN reg */
+#define GEM_MODID       0x000000FC /* Module ID reg */
+#define GEM_OCTTXLO     0x00000100 /* Octects transmitted Low reg */
+#define GEM_OCTTXHI     0x00000104 /* Octects transmitted High reg */
+#define GEM_TXCNT       0x00000108 /* Error-free Frmaes transmitted counter */
+#define GEM_TXBCNT      0x0000010C /* Error-free Broadcast Frames counter*/
+#define GEM_TXMCNT      0x00000110 /* Error-free Multicast Frame counter */
+#define GEM_TXPAUSECNT  0x00000114 /* Pause Frames Transmitted Counter */
+#define GEM_TX64CNT     0x00000118 /* Error-free 64 TX */
+#define GEM_TX65CNT     0x0000011C /* Error-free 65-127 TX */
+#define GEM_TX128CNT    0x00000120 /* Error-free 128-255 TX */
+#define GEM_TX256CNT    0x00000124 /* Error-free 256-511 */
+#define GEM_TX512CNT    0x00000128 /* Error-free 512-1023 TX */
+#define GEM_TX1024CNT   0x0000012C /* Error-free 1024-1518 TX */
+#define GEM_TX1519CNT   0x00000130 /* Error-free larger than 1519 TX */
+#define GEM_TXURUNCNT   0x00000134 /* TX under run error counter */
+#define GEM_SINGLECOLLCNT   0x00000138 /* Single Collision Frame Counter */
+#define GEM_MULTCOLLCNT     0x0000013C /* Multiple Collision Frame Counter */
+#define GEM_EXCESSCOLLCNT   0x00000140 /* Excessive Collision Frame Counter */
+#define GEM_LATECOLLCNT     0x00000144 /* Late Collision Frame Counter */
+#define GEM_DEFERTXCNT  0x00000148 /* Deferred Transmission Frame Counter */
+#define GEM_CSENSECNT   0x0000014C /* Carrier Sense Error Counter */
+#define GEM_OCTRXLO     0x00000150 /* Octects Received register Low */
+#define GEM_OCTRXHI     0x00000154 /* Octects Received register High */
+#define GEM_RXCNT       0x00000158 /* Error-free Frames Received Counter */
+#define GEM_RXBROADCNT  0x0000015C /* Error-free Broadcast Frames RX */
+#define GEM_RXMULTICNT  0x00000160 /* Error-free Multicast Frames RX */
+#define GEM_RXPAUSECNT  0x00000164 /* Pause Frames Received Counter */
+#define GEM_RX64CNT     0x00000168 /* Error-free 64 byte Frames RX */
+#define GEM_RX65CNT     0x0000016C /* Error-free 65-127 byte Frames RX */
+#define GEM_RX128CNT    0x00000170 /* Error-free 128-255 byte Frames RX */
+#define GEM_RX256CNT    0x00000174 /* Error-free 256-512 byte Frames RX */
+#define GEM_RX512CNT    0x00000178 /* Error-free 512-1023 byte Frames RX */
+#define GEM_RX1024CNT   0x0000017C /* Error-free 1024-1518 byte Frames RX */
+#define GEM_RX1519CNT   0x00000180 /* Error-free 1519-max byte Frames RX */
+#define GEM_RXUNDERCNT  0x00000184 /* Undersize Frames Received Counter */
+#define GEM_RXOVERCNT   0x00000188 /* Oversize Frames Received Counter */
+#define GEM_RXJABCNT    0x0000018C /* Jabbers Received Counter */
+#define GEM_RXFCSCNT    0x00000190 /* Frame Check Sequence Error Counter */
+#define GEM_RXLENERRCNT 0x00000194 /* Length Field Error Counter */
+#define GEM_RXSYMERRCNT 0x00000198 /* Symbol Error Counter */
+#define GEM_RXALIGNERRCNT   0x0000019C /* Alignment Error Counter */
+#define GEM_RXRSCERRCNT 0x000001A0 /* Receive Resource Error Counter */
+#define GEM_RXORUNCNT   0x000001A4 /* Receive Overrun Counter */
+#define GEM_RXIPCSERRCNT    0x000001A8 /* IP header Checksum Error Counter */
+#define GEM_RXTCPCCNT   0x000001AC /* TCP Checksum Error Counter */
+#define GEM_RXUDPCCNT   0x000001B0 /* UDP Checksum Error Counter */
+
+#define GEM_1588S       0x000001D0 /* 1588 Timer Seconds */
+#define GEM_1588NS      0x000001D4 /* 1588 Timer Nanoseconds */
+#define GEM_1588ADJ     0x000001D8 /* 1588 Timer Adjust */
+#define GEM_1588INC     0x000001DC /* 1588 Timer Increment */
+#define GEM_PTPETXS     0x000001E0 /* PTP Event Frame Transmitted Seconds */
+#define GEM_PTPETXNS    0x000001E4 /* PTP Event Frame Transmitted Nanoseconds */
+#define GEM_PTPERXS     0x000001E8 /* PTP Event Frame Received Seconds */
+#define GEM_PTPERXNS    0x000001EC /* PTP Event Frame Received Nanoseconds */
+#define GEM_PTPPTXS     0x000001E0 /* PTP Peer Frame Transmitted Seconds */
+#define GEM_PTPPTXNS    0x000001E4 /* PTP Peer Frame Transmitted Nanoseconds */
+#define GEM_PTPPRXS     0x000001E8 /* PTP Peer Frame Received Seconds */
+#define GEM_PTPPRXNS    0x000001EC /* PTP Peer Frame Received Nanoseconds */
+
+#define GEM_DESCONF     0x00000280 /* Design Configuration Register */
+#define GEM_DESCONF2    0x00000284 /* Design Configuration Register */
+#define GEM_DESCONF3    0x00000288 /* Design Configuration Register */
+#define GEM_DESCONF4    0x0000028C /* Design Configuration Register */
+#define GEM_DESCONF5    0x00000290 /* Design Configuration Register */
+#define GEM_DESCONF6    0x00000294 /* Design Configuration Register */
+#define GEM_DESCONF7    0x00000298 /* Design Configuration Register */
+
+#define GEM_MAXREG      0x00000640  /* Last valid GEM address */
+
+/*****************************************/
+#define GEM_NWCTRL_TXSTART     0x00000200 /* Transmit Enable */
+#define GEM_NWCTRL_TXENA       0x00000008 /* Transmit Enable */
+#define GEM_NWCTRL_RXENA       0x00000004 /* Receive Enable */
+#define GEM_NWCTRL_LOCALLOOP   0x00000002 /* Local Loopback */
+
+#define GEM_NWCFG_STRIP_FCS    0x00020000 /* Strip FCS field */
+#define GEM_NWCFG_LERR_DISC    0x00010000 /* Discard RX frames with lenth err */
+#define GEM_NWCFG_BUFF_OFST_M  0x0000C000 /* Receive buffer offset mask */
+#define GEM_NWCFG_BUFF_OFST_S  14         /* Receive buffer offset shift */
+#define GEM_NWCFG_UCAST_HASH   0x00000080 /* accept unicast if hash match */
+#define GEM_NWCFG_MCAST_HASH   0x00000040 /* accept multicast if hash match */
+#define GEM_NWCFG_BCAST_REJ    0x00000020 /* Reject broadcast packets */
+#define GEM_NWCFG_PERMISC      0x00000010 /* Accept all packets */
+
+#define GEM_DMACFG_TXCSUM_OFFL 0x00000800 /* Transmit checksum offload */
+#define GEM_DMACFG_RBUFSZ_M    0x007F0000 /* DMA RX Buffer Size mask */
+#define GEM_DMACFG_RBUFSZ_S    16         /* DMA RX Buffer Size shift */
+#define GEM_DMACFG_RBUFSZ_MUL  64         /* DMA RX Buffer Size multiplier */
+
+#define GEM_TXSTATUS_TXCMPL    0x00000020 /* Transmit Complete */
+#define GEM_TXSTATUS_USED      0x00000001 /* sw owned descriptor encountered */
+
+#define GEM_RXSTATUS_FRMRCVD   0x00000002 /* Frame received */
+#define GEM_RXSTATUS_NOBUF     0x00000001 /* Buffer unavailable */
+
+/* GEM_ISR GEM_IER GEM_IDR GEM_IMR */
+#define GEM_INT_TXCOMPL        0x00000080 /* Transmit Complete */
+#define GEM_INT_TXUSED         0x00000008
+#define GEM_INT_RXUSED         0x00000004
+#define GEM_INT_RXCOMPL        0x00000002
+
+#define GEM_PHYMNTNC_OP_R      0x20000000 /* read operation */
+#define GEM_PHYMNTNC_OP_W      0x10000000 /* write operation */
+#define GEM_PHYMNTNC_ADDR      0x0F800000 /* Address bits */
+#define GEM_PHYMNTNC_ADDR_SHFT 23
+#define GEM_PHYMNTNC_REG       0x007C0000 /* register bits */
+#define GEM_PHYMNTNC_REG_SHIFT 18
+
+/* Marvell PHY definitions */
+#define BOARD_PHY_ADDRESS    23 /* PHY address we will emulate a device at */
+
+#define PHY_REG_CONTROL      0
+#define PHY_REG_STATUS       1
+#define PHY_REG_PHYID1       2
+#define PHY_REG_PHYID2       3
+#define PHY_REG_ANEGADV      4
+#define PHY_REG_LINKPABIL    5
+#define PHY_REG_ANEGEXP      6
+#define PHY_REG_NEXTP        7
+#define PHY_REG_LINKPNEXTP   8
+#define PHY_REG_100BTCTRL    9
+#define PHY_REG_1000BTSTAT   10
+#define PHY_REG_EXTSTAT      15
+#define PHY_REG_PHYSPCFC_CTL 16
+#define PHY_REG_PHYSPCFC_ST  17
+#define PHY_REG_INT_EN       18
+#define PHY_REG_INT_ST       19
+#define PHY_REG_EXT_PHYSPCFC_CTL  20
+#define PHY_REG_RXERR        21
+#define PHY_REG_EACD         22
+#define PHY_REG_LED          24
+#define PHY_REG_LED_OVRD     25
+#define PHY_REG_EXT_PHYSPCFC_CTL2 26
+#define PHY_REG_EXT_PHYSPCFC_ST   27
+#define PHY_REG_CABLE_DIAG   28
+
+#define PHY_REG_CONTROL_RST  0x8000
+#define PHY_REG_CONTROL_LOOP 0x4000
+#define PHY_REG_CONTROL_ANEG 0x1000
+
+#define PHY_REG_STATUS_LINK     0x0004
+#define PHY_REG_STATUS_ANEGCMPL 0x0020
+
+#define PHY_REG_INT_ST_ANEGCMPL 0x0800
+#define PHY_REG_INT_ST_LINKC    0x0400
+#define PHY_REG_INT_ST_ENERGY   0x0010
+
+/***********************************************************************/
+#define GEM_RX_REJECT  1
+#define GEM_RX_ACCEPT  0
+
+/***********************************************************************/
+
+static inline unsigned tx_desc_get_buffer(unsigned *desc)
+{
+    return desc[0];
+}
+static inline unsigned tx_desc_get_used(unsigned *desc)
+{
+    return (desc[1] & 0x80000000) ? 1 : 0;
+}
+static inline void tx_desc_set_used(unsigned *desc)
+{
+    desc[1] |= 0x80000000;
+}
+static inline unsigned tx_desc_get_wrap(unsigned *desc)
+{
+    return (desc[1] & 0x40000000) ? 1 : 0;
+}
+static inline unsigned tx_desc_get_last(unsigned *desc)
+{
+    return (desc[1] & 0x00008000) ? 1 : 0;
+}
+static inline unsigned tx_desc_get_length(unsigned *desc)
+{
+    return desc[1] & 0x1FFF;
+}
+
+static inline void print_gem_tx_desc (unsigned *desc)
+{
+    printf("TXDESC: \n");
+    printf(" bufaddr: 0x%08x\n", *desc);
+    printf(" used_hw: %d\n", tx_desc_get_used(desc));
+    printf(" wrap:    %d\n", tx_desc_get_wrap(desc));
+    printf(" last:    %d\n", tx_desc_get_last(desc));
+    printf(" length:  %d\n", tx_desc_get_length(desc));
+}
+
+static inline unsigned rx_desc_get_buffer(unsigned *desc)
+{
+    return (desc[0] & 0xFFFFFFFC);
+}
+static inline unsigned rx_desc_get_wrap(unsigned *desc)
+{
+    return (desc[0] & 0x00000002) ? 1 : 0;
+}
+static inline unsigned rx_desc_get_ownership(unsigned *desc)
+{
+    return (desc[0] & 0x00000001) ? 1 : 0;
+}
+static inline void rx_desc_set_ownership(unsigned *desc)
+{
+    desc[0] |= 0x00000001;
+}
+static inline void rx_desc_set_sof(unsigned *desc)
+{
+    desc[1] |= 0x00004000;
+}
+static inline void rx_desc_set_eof(unsigned *desc)
+{
+    desc[1] |= 0x00008000;
+}
+static inline void rx_desc_set_length(unsigned *desc, unsigned len)
+{
+    desc[1] &= 0xFFFFE000;
+    desc[1] |= len;
+}
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    NICState *nic;
+    NICConf conf;
+    qemu_irq irq;
+
+    /* GEM registers backing store */
+    uint32_t regs[GEM_MAXREG/4];
+    /* Mask of register bits which are write only */
+    uint32_t regs_wo[GEM_MAXREG/4];
+    /* Mask of register bits which are read only */
+    uint32_t regs_ro[GEM_MAXREG/4];
+    /* Mask of register bits which are clear on read */
+    uint32_t regs_rtc[GEM_MAXREG/4];
+    /* Mask of register bits which are write 1 to clear */
+    uint32_t regs_w1c[GEM_MAXREG/4];
+
+    /* PHY registers backing store */
+    uint16_t phy_regs[32];
+
+    int phy_loop; /* Are we in phy loopback? */
+
+    /* The current DMA descriptor pointers */
+    target_phys_addr_t rx_desc_addr;
+    target_phys_addr_t tx_desc_addr;
+
+} gem_state;
+
+/* The broadcast MAC address: 0xFFFFFFFFFFFF */
+const uint8_t Broadcast_Addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+
+/*
+ * gem_init_register_masks:
+ * One time initialization.
+ * Set masks to identify which register bits have magical clear properties
+ */
+static void gem_init_register_masks(gem_state *s)
+{
+    /* Mask of register bits which are read only*/
+    bzero(&s->regs_ro[0], GEM_MAXREG);
+    s->regs_ro[GEM_NWCTRL/4]   = 0xFFF80000;
+    s->regs_ro[GEM_NWSTATUS/4] = 0xFFFFFFFF;
+    s->regs_ro[GEM_DMACFG/4]   = 0xFE00F000;
+    s->regs_ro[GEM_TXSTATUS/4] = 0xFFFFFE08;
+    s->regs_ro[GEM_RXQBASE/4]  = 0x00000003;
+    s->regs_ro[GEM_TXQBASE/4]  = 0x00000003;
+    s->regs_ro[GEM_RXSTATUS/4] = 0xFFFFFFF0;
+    s->regs_ro[GEM_ISR/4]      = 0xFFFFFFFF;
+    s->regs_ro[GEM_IMR/4]      = 0xFFFFFFFF;
+    s->regs_ro[GEM_MODID/4]    = 0xFFFFFFFF;
+
+    /* Mask of register bits which are clear on read */
+    bzero(&s->regs_rtc[0], GEM_MAXREG);
+    s->regs_rtc[GEM_ISR/4]      = 0xFFFFFFFF;
+
+    /* Mask of register bits which are write 1 to clear */
+    bzero(&s->regs_w1c[0], GEM_MAXREG);
+    s->regs_w1c[GEM_TXSTATUS/4] = 0x000001F7;
+    s->regs_w1c[GEM_RXSTATUS/4]  = 0x0000000F;
+
+    /* Mask of register bits which are write only */
+    bzero(&s->regs_wo[0], GEM_MAXREG);
+    s->regs_wo[GEM_NWCTRL/4]   = 0x00073E60;
+    s->regs_wo[GEM_IER/4]      = 0x07FFFFFF;
+    s->regs_wo[GEM_IDR/4]      = 0x07FFFFFF;
+}
+
+/*
+ * phy_update_link:
+ * Make the emulated PHY link state match the QEMU "interface" state.
+ */
+static void phy_update_link(gem_state *s)
+{
+#ifdef GEM_DEBUG_FUNCTIONS
+    printf("QEMU: %s: down %d\n", __FUNCTION__, s->nic->nc.link_down);
+#endif
+
+    /* Autonegotiation status mirrors link status.  */
+    if (s->nic->nc.link_down) {
+        s->phy_regs[PHY_REG_STATUS] &= ~(PHY_REG_STATUS_ANEGCMPL |
+                                         PHY_REG_STATUS_LINK);
+        s->phy_regs[PHY_REG_INT_ST] |= PHY_REG_INT_ST_LINKC;
+    } else {
+        s->phy_regs[PHY_REG_STATUS] |= (PHY_REG_STATUS_ANEGCMPL |
+                                         PHY_REG_STATUS_LINK);
+        s->phy_regs[PHY_REG_INT_ST] |= (PHY_REG_INT_ST_LINKC |
+                                        PHY_REG_INT_ST_ANEGCMPL |
+                                        PHY_REG_INT_ST_ENERGY);
+    }
+}
+
+static int gem_can_receive(VLANClientState *nc)
+{
+    gem_state *s;
+
+    s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+#ifdef GEM_DEBUG_FUNCTIONS
+    printf("QEMU: %s:\n", __FUNCTION__);
+#endif
+
+    /* Do nothing if receive is not enabled. */
+    if (!(s->regs[GEM_NWCTRL/4] & GEM_NWCTRL_RXENA)) {
+        return 0;
+    }
+
+    return 1;
+}
+
+/*
+ * gem_update_int_status:
+ * Raise or lower interrupt based on current status.
+ */
+static void gem_update_int_status(gem_state *s)
+{
+    /* Packet transmitted ? */
+    if (s->regs[GEM_TXSTATUS/4] & GEM_TXSTATUS_TXCMPL) {
+        /* TX Completion ints enabled ? */
+        if ((s->regs[GEM_IMR/4] & GEM_INT_TXCOMPL) == 0) {
+            s->regs[GEM_ISR/4] |= GEM_INT_TXCOMPL;
+        }
+    }
+    /* End of TX ring ? */
+    if (s->regs[GEM_TXSTATUS/4] & GEM_TXSTATUS_USED) {
+        /* int enabled ? */
+        if ((s->regs[GEM_IMR/4] & GEM_INT_TXUSED) == 0) {
+            s->regs[GEM_ISR/4] |= GEM_INT_TXUSED;
+        }
+    }
+
+    /* Frame received ? */
+    if (s->regs[GEM_RXSTATUS/4] & GEM_RXSTATUS_FRMRCVD) {
+        /* int enabled ? */
+        if ((s->regs[GEM_IMR/4] & GEM_INT_RXCOMPL) == 0) {
+            s->regs[GEM_ISR/4] |= GEM_INT_RXCOMPL;
+        }
+    }
+    /* RX ring full ? */
+    if (s->regs[GEM_RXSTATUS/4] & GEM_RXSTATUS_NOBUF) {
+        /* int enabled ? */
+        if ((s->regs[GEM_IMR/4] & GEM_INT_RXUSED) == 0) {
+            s->regs[GEM_ISR/4] |= GEM_INT_RXUSED;
+        }
+    }
+
+    if (s->regs[GEM_ISR/4]) {
+#ifdef GEM_DEBUG_INT
+        printf("QEMU: %s: asserting int. (0x%08x)\n",
+            __FUNCTION__, s->regs[GEM_ISR/4]);
+#endif
+        qemu_set_irq(s->irq, 1);
+    } else {
+        qemu_set_irq(s->irq, 0);
+    }
+}
+
+#ifdef GEM_DEBUG_PACKET
+/*
+ * print_packet:
+ * Decode provided packet.
+ */
+static void print_packet (const uint8_t *packet, unsigned len, int rx_tx)
+{
+    int i;
+    unsigned ethtype;
+
+    printf("PACKET: %s length: %d\n",
+     (rx_tx ? "RX" : "TX"), len);
+
+    printf("DST:     ");
+    for (i=0; i<6; i++) {
+        printf("%02x", *packet);
+        packet++;
+    }
+    printf("\n");
+    len -= 6;
+
+    printf("SRC:     ");
+    for (i=0; i<6; i++) {
+        printf("%02x", *packet);
+        packet++;
+    }
+    len -= 6;
+    printf("\n");
+
+    printf("ETHTYPE: ");
+    ethtype = 0;
+    for (i=0; i<2; i++) {
+        ethtype <<= 8;
+        ethtype |= *packet;
+        packet++;
+    }
+    len -= 2;
+    printf("%02x\n", ethtype);
+
+    if (ethtype == 0x800) {
+        unsigned ip_proto;
+
+        packet += 9;
+        len -= 9;
+        ip_proto = *packet;
+        printf("IPPROTO:  %d ", ip_proto);
+        switch(ip_proto) {
+        case 1:
+            printf("ICMP ");
+            break;
+        case 6:
+            printf("TCP ");
+            break;
+        case 17:
+            printf("UDP ");
+            break;
+        }
+        printf("\n");
+
+        printf("IPSRC: ");
+        packet += 3;
+        len -= 3;
+        for (i=0; i<4; i++) {
+            printf("%d", *packet);
+            if (i != 3) printf(".");
+            packet++;
+        }
+        len -= 4;
+        printf("\n");
+
+        printf("IPDST: ");
+        for (i=0; i<4; i++) {
+            printf("%d", *packet);
+            if (i != 3) printf(".");
+            packet++;
+        }
+        len -= 4;
+        printf("\n");
+
+        switch(ip_proto) {
+        case 1:
+            printf("ICMP ");
+            break;
+        case 6:
+            printf("TCP SOURCE PORT: 0x%02x%02x\n",
+                packet[0], packet[1]);
+            packet += 2;
+            len -= 2;
+
+            printf("TCP DEST PORT:   0x%02x%02x\n",
+                packet[0], packet[1]);
+            packet += 2;
+            len -= 2;
+
+            printf("TCP SEQ:         0x%02x%02x%02x%02x\n",
+                packet[0], packet[1], packet[2], packet[3]);
+            packet += 4;
+            len -= 4;
+
+            printf("TCP ACK:         0x%02x%02x%02x%02x\n",
+                packet[0], packet[1], packet[2], packet[3]);
+            packet += 4;
+            len -= 4;
+
+            packet++;
+            len--;
+            printf("TCP FLAGS: ");
+            if (*packet & 0x80) {
+                printf("CWR ");
+            }
+            if (*packet & 0x40) {
+                printf("ECE ");
+            }
+            if (*packet & 0x20) {
+                printf("URG ");
+            }
+            if (*packet & 0x10) {
+                printf("ACK ");
+            }
+            if (*packet & 0x08) {
+                printf("PSH ");
+            }
+            if (*packet & 0x04) {
+                printf("RST ");
+            }
+            if (*packet & 0x02) {
+                printf("SYN ");
+            }
+            if (*packet & 0x01) {
+                printf("FIN ");
+            }
+            printf("\n");
+
+            packet += 3;
+            len -= 3;
+            printf("TCP CHECKSUM:    0x%02x%02x\n",
+                packet[0], packet[1]);
+            packet += 2;
+            len -= 2;
+
+            break;
+        case 17:
+            printf("UDP ");
+            break;
+        }
+    }
+    if (ethtype == 0x806) {
+        unsigned op;
+
+        packet += 7;
+        len -= 7;
+        op = *packet;
+
+        if (op == 1) {
+            printf("WHOHAS: ");
+            packet += 17;
+            len -= 17;
+        } else {
+            printf("IHAVE:  ");
+            packet += 7;
+            len -= 7;
+        }
+        for (i=0; i<4; i++) {
+            printf("%d", *packet);
+            if (i != 3) printf(".");
+            packet++;
+        }
+        len -= 4;
+        printf("\n");
+    }
+
+    printf("DATA:  ");
+    for (i=0; i<MIN(len,16); i++) {
+        printf("%02x", *packet);
+        packet++;
+    }
+    printf("\n\n");
+}
+#endif
+
+/*
+ * gem_receive_updatestats:
+ * Increment receive statistics.
+ */
+static void gem_receive_updatestats(gem_state *s, const uint8_t *packet,
+                                    unsigned bytes)
+{
+    uint64_t octets;
+
+    /* Total octets (bytes) received */
+    octets = ((uint64_t)(s->regs[GEM_OCTRXLO/4]) << 32) |
+             s->regs[GEM_OCTRXHI/4];
+    octets += bytes;
+    s->regs[GEM_OCTRXLO/4] = octets >> 32;
+    s->regs[GEM_OCTRXHI/4] = octets;
+
+    /* Error-free Frames reveived */
+    s->regs[GEM_RXCNT/4]++;
+
+    /* Error-free Broadcast Frames counter */
+    if (memcmp(packet, Broadcast_Addr, 6) == 0) {
+        s->regs[GEM_RXBROADCNT/4]++;
+    }
+
+    /* Error-free Multicast Frames counter */
+    if (packet[0] == 0x01) {
+        s->regs[GEM_RXMULTICNT/4]++;
+    }
+
+    if (bytes <= 64) {
+        s->regs[GEM_RX64CNT/4]++;
+    } else if (bytes <= 127) {
+        s->regs[GEM_RX65CNT/4]++;
+    } else if (bytes <= 255) {
+        s->regs[GEM_RX128CNT/4]++;
+    } else if (bytes <= 511) {
+        s->regs[GEM_RX256CNT/4]++;
+    } else if (bytes <= 1023) {
+        s->regs[GEM_RX512CNT/4]++;
+    } else if (bytes <= 1518) {
+        s->regs[GEM_RX1024CNT/4]++;
+    } else {
+        s->regs[GEM_RX1519CNT/4]++;
+    }
+}
+
+/*
+ * Get the MAC Address bit from the specified position
+ */
+static unsigned get_bit(const uint8_t *mac, unsigned bit)
+{
+    unsigned byte;
+
+    byte = mac[bit / 8];
+    byte >>= (bit & 0x7);
+    byte &= 1;
+
+    return byte;
+}
+
+/*
+ * Calculate a GEM MAC Address hash index
+ */
+static unsigned calc_mac_hash(const uint8_t *mac)
+{
+    int index_bit, mac_bit;
+    unsigned hash_index;
+
+    hash_index = 0;
+    mac_bit = 5;
+    for (index_bit = 5; index_bit >= 0; index_bit--) {
+        hash_index |= (get_bit(mac,  mac_bit) ^
+                               get_bit(mac, mac_bit + 6) ^
+                               get_bit(mac, mac_bit + 12) ^
+                               get_bit(mac, mac_bit + 18) ^
+                               get_bit(mac, mac_bit + 24) ^
+                               get_bit(mac, mac_bit + 30) ^
+                               get_bit(mac, mac_bit + 36) ^
+                               get_bit(mac, mac_bit + 42)) << index_bit;
+        mac_bit--;
+    }
+
+    return hash_index;
+}
+
+/*
+ * gem_mac_address_filter:
+ * Accept or reject this destination address?
+ * Returns:
+ * GEM_RX_REJECT: reject
+ * GEM_RX_ACCEPT: accept
+ */
+static int gem_mac_address_filter(gem_state *s, const uint8_t *packet)
+{
+    uint8_t *gem_spaddr;
+    int i;
+
+    /* Permiscuous mode? */
+    if (s->regs[GEM_NWCFG/4] & GEM_NWCFG_PERMISC) {
+        return GEM_RX_ACCEPT;
+    }
+
+    if (memcmp(packet, Broadcast_Addr, 6) == 0) {
+        /* Recject broadcast packets? */
+        if (s->regs[GEM_NWCFG/4] & GEM_NWCFG_BCAST_REJ) {
+            return GEM_RX_REJECT;
+        } else {
+            return GEM_RX_ACCEPT;
+        }
+    }
+
+    /* Accept packets -w- hash match? */
+    if ((packet[0] == 0x01 && (s->regs[GEM_NWCFG/4] & GEM_NWCFG_MCAST_HASH)) ||
+        (packet[0] != 0x01 && (s->regs[GEM_NWCFG/4] & GEM_NWCFG_UCAST_HASH))) {
+        unsigned hash_index;
+
+        hash_index = calc_mac_hash(packet);
+        if (hash_index < 32) {
+            if (s->regs[GEM_HASHLO/4] & (1<<hash_index)) {
+                return GEM_RX_ACCEPT;
+            }
+        } else {
+            hash_index -= 32;
+            if (s->regs[GEM_HASHHI/4] & (1<<hash_index)) {
+                return GEM_RX_ACCEPT;
+            }
+        }
+    }
+
+    /* Check all 4 specific addresses */
+    gem_spaddr = (uint8_t*)&(s->regs[GEM_SPADDR1LO/4]);
+    for (i=0; i<4; i++) {
+        if (memcmp(packet, gem_spaddr, 6) == 0) {
+            return GEM_RX_ACCEPT;
+        }
+
+        gem_spaddr += 8;
+    }
+
+    /* No address match; reject the packet */
+    return GEM_RX_REJECT;
+}
+
+/*
+ * gem_receive:
+ * Fit a packet handed to us by QEMU into the receive descriptor ring.
+ */
+static ssize_t gem_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+    unsigned    desc[2];
+    target_phys_addr_t packet_desc_addr, last_desc_addr;
+    gem_state *s;
+    unsigned   rxbufsize, bytes_to_copy;
+    unsigned   rxbuf_offset;
+    uint8_t    rxbuf[2048];
+    uint8_t   *rxbuf_ptr;
+
+    s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+    /* Do nothing if receive is not enabled. */
+    if (!(s->regs[GEM_NWCTRL/4] & GEM_NWCTRL_RXENA)) {
+        return -1;
+    }
+
+    /* Is this destination MAC address "for us" ? */
+    if (gem_mac_address_filter(s, buf) == GEM_RX_REJECT) {
+        return -1;
+    }
+
+    /* Discard packets with receive length error enabled ? */
+    if (s->regs[GEM_NWCFG/4] & GEM_NWCFG_LERR_DISC) {
+        unsigned type_len;
+
+        /* Fish the ethertype / length field out of the RX packet */
+        type_len = buf[12] << 8 | buf[13];
+        /* It is a length field, not an ethertype */
+        if (type_len < 0x600) {
+            if (size < type_len) {
+                /* discard */
+                return -1;
+            }
+        }
+    }
+
+    /*
+     * Determine configured receive buffer offset (probably 0)
+     */
+    rxbuf_offset = (s->regs[GEM_NWCFG/4] & GEM_NWCFG_BUFF_OFST_M) >>
+                   GEM_NWCFG_BUFF_OFST_S;
+
+    /* The configure size of each receive buffer.  Determines how many
+     * buffers needed to hold this packet.
+     */
+    rxbufsize = ((s->regs[GEM_DMACFG/4] & GEM_DMACFG_RBUFSZ_M) >>
+                 GEM_DMACFG_RBUFSZ_S) * GEM_DMACFG_RBUFSZ_MUL;
+    bytes_to_copy = size;
+
+    /* Strip of FCS field ? (usually yes) */
+    if (s->regs[GEM_NWCFG/4] & GEM_NWCFG_STRIP_FCS) {
+        rxbuf_ptr = (void*)buf;
+    } else {
+        unsigned crc_val;
+        int      crc_offset;
+
+        /* The application wants the FCS field, which QEMU does not provide.
+         * We must try and caclculate one.
+         */
+
+        memcpy(rxbuf, buf, size);
+        bzero(rxbuf + size, sizeof(rxbuf - size));
+        rxbuf_ptr = rxbuf;
+        crc_val = cpu_to_le32(crc32(0, rxbuf, MAX(size, 60)));
+        if (size < 60) {
+            crc_offset = 60;
+        } else {
+            crc_offset = size;
+        }
+        memcpy(rxbuf + crc_offset, &crc_val, sizeof(crc_val));
+
+        bytes_to_copy += 4;
+        size += 4;
+    }
+
+    /* Pad to minimum length */
+    if (size < 64) {
+        size = 64;
+    }
+
+#ifdef GEM_DEBUG_FUNCTIONS
+    printf("QEMU: %s: config bufsize: %d packet size: %ld\n",
+        __FUNCTION__, rxbufsize, size);
+#endif
+
+    packet_desc_addr = s->rx_desc_addr;
+    while (1) {
+#ifdef GEM_DEBUG_RX
+        printf("QEMU: %s: read descriptor 0x%x \n",
+            __FUNCTION__, packet_desc_addr);
+#endif
+        /* read current descriptor */
+        cpu_physical_memory_read(packet_desc_addr,
+                                 (uint8_t *)&desc[0], sizeof(desc));
+
+        /* Descriptor owned by software ? */
+        if (rx_desc_get_ownership(desc) == 1) {
+#ifdef GEM_DEBUG_RX
+            printf("QEMU: %s: descriptor 0x%x owned by sw.\n",
+                __FUNCTION__, packet_desc_addr);
+#endif
+            s->regs[GEM_RXSTATUS/4] |= GEM_RXSTATUS_NOBUF;
+            /* Handle interrupt consequences */
+            gem_update_int_status(s);
+            return -1;
+        }
+
+#ifdef GEM_DEBUG_RX
+        printf("QEMU: %s: copy %d bytes to 0x%x\n", __FUNCTION__,
+                MIN(bytes_to_copy, rxbufsize),
+                rx_desc_get_buffer(desc));
+#endif
+
+        /*
+         * Let's have QEMU lend a helping hand.
+         */
+        if (rx_desc_get_buffer(desc) == 0) {
+            printf("QEMU: GEM: Invalid RX buffer (NULL) for descriptor 0x%x\n",
+                packet_desc_addr);
+            break;
+        }
+
+        /* Copy packet data to emulated DMA buffer */
+        cpu_physical_memory_write(rx_desc_get_buffer(desc) + rxbuf_offset,
+                                  rxbuf_ptr, MIN(bytes_to_copy, rxbufsize));
+        bytes_to_copy -= MIN(bytes_to_copy, rxbufsize);
+        rxbuf_ptr += MIN(bytes_to_copy, rxbufsize);
+        if (bytes_to_copy == 0) {
+            break;
+        }
+
+        /* Next descriptor */
+        if (rx_desc_get_wrap(desc)) {
+            packet_desc_addr = s->regs[GEM_RXQBASE/4];
+        } else {
+            packet_desc_addr += 8;
+        }
+    }
+
+#ifdef GEM_DEBUG_PACKET
+    print_packet(buf, size, 1);
+#endif
+
+#ifdef GEM_DEBUG_RX
+    printf("QEMU: %s: set length: %ld, EOF on descriptor 0x%x\n", __FUNCTION__,
+         size, (unsigned)packet_desc_addr);
+#endif
+
+    /* Update last descriptor with EOF and total length */
+    rx_desc_set_eof(desc);
+    rx_desc_set_length(desc, size);
+    cpu_physical_memory_write(packet_desc_addr,
+                              (uint8_t *)&desc[0], sizeof(desc));
+
+    /* Advance RX packet descriptor Q */
+    last_desc_addr = packet_desc_addr;
+    packet_desc_addr = s->rx_desc_addr;
+    s->rx_desc_addr = last_desc_addr;
+    if (rx_desc_get_wrap(desc)) {
+        s->rx_desc_addr = s->regs[GEM_RXQBASE/4];
+    } else {
+        s->rx_desc_addr += 8;
+    }
+
+#ifdef GEM_DEBUG_RX
+    printf("QEMU: %s: set SOF, OWN on descriptor 0x%08x\n", __FUNCTION__,
+        packet_desc_addr);
+#endif
+
+    /* Count it */
+    gem_receive_updatestats(s, buf, size);
+
+    /* Update first descriptor (which could also be the last) */
+    /* read descriptor */
+    cpu_physical_memory_read(packet_desc_addr,
+                             (uint8_t *)&desc[0], sizeof(desc));
+    rx_desc_set_sof(desc);
+    rx_desc_set_ownership(desc);
+    cpu_physical_memory_write(packet_desc_addr,
+                              (uint8_t *)&desc[0], sizeof(desc));
+
+    s->regs[GEM_RXSTATUS/4] |= GEM_RXSTATUS_FRMRCVD;
+
+    /* Handle interrupt consequences */
+    gem_update_int_status(s);
+
+    return size;
+}
+
+/*
+ * gem_transmit_updatestats:
+ * Increment transmit statistics.
+ */
+static void gem_transmit_updatestats(gem_state *s, const uint8_t *packet,
+                                     unsigned bytes)
+{
+    uint64_t octets;
+
+    /* Total octets (bytes) transmitted */
+    octets = ((uint64_t)(s->regs[GEM_OCTTXLO/4]) << 32) |
+             s->regs[GEM_OCTTXHI/4];
+    octets += bytes;
+    s->regs[GEM_OCTTXLO/4] = octets >> 32;
+    s->regs[GEM_OCTTXHI/4] = octets;
+
+    /* Error-free Frmaes transmitted */
+    s->regs[GEM_TXCNT/4]++;
+
+    /* Error-free Broadcast Frames counter */
+    if (memcmp(packet, Broadcast_Addr, 6) == 0) {
+        s->regs[GEM_TXBCNT/4]++;
+    }
+
+    /* Error-free Multicast Frames counter */
+    if (packet[0] == 0x01) {
+        s->regs[GEM_TXMCNT/4]++;
+    }
+
+    if (bytes <= 64) {
+        s->regs[GEM_TX64CNT/4]++;
+    } else if (bytes <= 127) {
+        s->regs[GEM_TX65CNT/4]++;
+    } else if (bytes <= 255) {
+        s->regs[GEM_TX128CNT/4]++;
+    } else if (bytes <= 511) {
+        s->regs[GEM_TX256CNT/4]++;
+    } else if (bytes <= 1023) {
+        s->regs[GEM_TX512CNT/4]++;
+    } else if (bytes <= 1518) {
+        s->regs[GEM_TX1024CNT/4]++;
+    } else {
+        s->regs[GEM_TX1519CNT/4]++;
+    }
+}
+
+/*
+ * gem_transmit:
+ * Fish packets out of the descriptor ring and feed them to QEMU
+ */
+static void gem_transmit(gem_state *s)
+{
+    unsigned    desc[2];
+    target_phys_addr_t packet_desc_addr;
+    uint8_t     tx_packet[2048];
+    uint8_t     *p;
+    unsigned    total_bytes;
+
+    /* Do nothing if transmit is not enabled. */
+    if (!(s->regs[GEM_NWCTRL/4] & GEM_NWCTRL_TXENA)) {
+        return;
+    }
+
+#ifdef GEM_DEBUG_FUNCTIONS
+    printf("QEMU: %s:\n", __FUNCTION__);
+#endif
+
+    /* The packet we will hand off to qemu.
+     * Packets scattered across multiple descriptors are gathered to this
+     * one contiguous buffer first.
+     */
+    p = tx_packet;
+    total_bytes = 0;
+
+    /* read current descriptor */
+    packet_desc_addr = s->tx_desc_addr;
+    cpu_physical_memory_read(packet_desc_addr,
+                             (uint8_t *)&desc[0], sizeof(desc));
+    /* Handle all decriptors owned by hardware */
+    while (tx_desc_get_used(desc) == 0) {
+
+        /* Do nothing if transmit is not enabled. */
+        if (!(s->regs[GEM_NWCTRL/4] & GEM_NWCTRL_TXENA)) {
+            return;
+        }
+#ifdef GEM_DEBUG_TX
+        print_gem_tx_desc(desc);
+#endif
+
+        /* The real hardware would eat this (and possibly crash).
+         * For QEMU let's lend a helping hand.
+         */
+        if ((tx_desc_get_buffer(desc) == 0) ||
+            (tx_desc_get_length(desc) == 0)) {
+            printf("QEMU: GEM: Invalid TX descriptor @ 0x%x\n",
+                packet_desc_addr);
+            break;
+        }
+
+        /* Gather this fragment of the packet from "dma memory" to our contig.
+         * buffer.
+         */
+        cpu_physical_memory_read(tx_desc_get_buffer(desc), p,
+                                 tx_desc_get_length(desc));
+        p += tx_desc_get_length(desc);
+        total_bytes += tx_desc_get_length(desc);
+
+        /* Last descriptor for this packet; hand the whole thing off */
+        if (tx_desc_get_last(desc)) {
+            /* Modifiy the 1st descriptor of this packet to be owned by
+             * the processor.
+             */
+            cpu_physical_memory_read(s->tx_desc_addr,
+                                     (uint8_t *)&desc[0], sizeof(desc));
+            tx_desc_set_used(desc);
+            cpu_physical_memory_write(s->tx_desc_addr,
+                                      (uint8_t *)&desc[0], sizeof(desc));
+            /* Advance the hardare current descriptor past this packet */
+            if (tx_desc_get_wrap(desc)) {
+                s->tx_desc_addr = s->regs[GEM_TXQBASE/4];
+            } else {
+                s->tx_desc_addr = packet_desc_addr + 8;
+            }
+#ifdef GEM_DEBUG_TX
+            printf("QEMU: GEM: TX descriptor next: 0x%08x\n", s->tx_desc_addr);
+#endif
+
+            s->regs[GEM_TXSTATUS/4] |= GEM_TXSTATUS_TXCMPL;
+
+            /* Handle interrupt consequences */
+            gem_update_int_status(s);
+
+#ifdef GEM_DEBUG_PACKET
+            print_packet(tx_packet, total_bytes, 0);
+#endif
+
+            /* Is checksum offload enabled? */
+            if (s->regs[GEM_DMACFG/4] & GEM_DMACFG_TXCSUM_OFFL) {
+                net_checksum_calculate(tx_packet, total_bytes);
+            }
+
+            /* Update MAC statistics */
+            gem_transmit_updatestats(s, tx_packet, total_bytes);
+
+            /* Send the packet somewhere */
+            if (s->phy_loop) {
+                gem_receive(&s->nic->nc, tx_packet, total_bytes);
+            } else {
+                qemu_send_packet(&s->nic->nc, tx_packet, total_bytes);
+            }
+
+            /* Prepare for next packet */
+            p = tx_packet;
+            total_bytes = 0;
+        }
+
+        /* read next descriptor */
+        if (tx_desc_get_wrap(desc)) {
+            packet_desc_addr = s->regs[GEM_TXQBASE/4];
+        } else {
+            packet_desc_addr += 8;
+        }
+        cpu_physical_memory_read(packet_desc_addr,
+                                 (uint8_t *)&desc[0], sizeof(desc));
+    }
+
+    if (tx_desc_get_used(desc)) {
+        s->regs[GEM_TXSTATUS/4] |= GEM_TXSTATUS_USED;
+        gem_update_int_status(s);
+    }
+}
+
+static void gem_reset(DeviceState *d)
+{
+    gem_state *s = FROM_SYSBUS(gem_state, sysbus_from_qdev(d));
+
+#ifdef GEM_DEBUG_FUNCTIONS
+    printf("%s:\n", __FUNCTION__);
+#endif
+    /* Set post reset register values */
+    bzero(&s->regs[0], GEM_MAXREG);
+    s->regs[GEM_NWCFG/4] = 0x00080000;
+    s->regs[GEM_NWSTATUS/4] = 0x00000006;
+    s->regs[GEM_DMACFG/4] = 0x00020784;
+    s->regs[GEM_IMR/4] = 0x07ffffff;
+    s->regs[GEM_TXPAUSE/4] = 0x0000ffff;
+    s->regs[GEM_TXPARTIALSF/4] = 0x000003ff;
+    s->regs[GEM_RXPARTIALSF/4] = 0x000003ff;
+    s->regs[GEM_MODID/4] = 0x00020118;
+    s->regs[GEM_DESCONF/4] = 0x02500111;
+    s->regs[GEM_DESCONF2/4] = 0x2ab13fff;
+    s->regs[GEM_DESCONF5/4] = 0x002f2145;
+    s->regs[GEM_DESCONF6/4] = 0x00000200;
+
+    bzero(&s->phy_regs[0], sizeof(s->phy_regs));
+    s->phy_regs[PHY_REG_CONTROL] = 0x1140;
+    s->phy_regs[PHY_REG_STATUS] = 0x7969;
+    s->phy_regs[PHY_REG_PHYID1] = 0x0141;
+    s->phy_regs[PHY_REG_PHYID2] = 0x0CC2;
+    s->phy_regs[PHY_REG_ANEGADV] = 0x01E1;
+    s->phy_regs[PHY_REG_LINKPABIL] = 0xCDE1;
+    s->phy_regs[PHY_REG_ANEGEXP] = 0x000F;
+    s->phy_regs[PHY_REG_NEXTP] = 0x2001;
+    s->phy_regs[PHY_REG_LINKPNEXTP] = 0x40E6;
+    s->phy_regs[PHY_REG_100BTCTRL] = 0x0300;
+    s->phy_regs[PHY_REG_1000BTSTAT] = 0x7C00;
+    s->phy_regs[PHY_REG_EXTSTAT] = 0x3000;
+    s->phy_regs[PHY_REG_PHYSPCFC_CTL] = 0x0078;
+    s->phy_regs[PHY_REG_PHYSPCFC_ST] = 0xBC00;
+    s->phy_regs[PHY_REG_EXT_PHYSPCFC_CTL] = 0x0C60;
+    s->phy_regs[PHY_REG_LED] = 0x4100;
+    s->phy_regs[PHY_REG_EXT_PHYSPCFC_CTL2] = 0x000A;
+    s->phy_regs[PHY_REG_EXT_PHYSPCFC_ST] = 0x848B;
+
+    phy_update_link(s);
+    gem_update_int_status(s);
+}
+
+static uint16_t gem_phy_read(gem_state *s, unsigned reg_num)
+{
+#ifdef GEM_DEBUG_PHY
+    printf("gem_phy_read:  reg: %d value: 0x%04x\n",
+           reg_num, s->phy_regs[reg_num]);
+#endif
+    return s->phy_regs[reg_num];
+}
+
+static void gem_phy_write(gem_state *s, unsigned reg_num, uint16_t val)
+{
+#ifdef GEM_DEBUG_PHY
+    printf("gem_phy_write: reg: %d value: 0x%04x\n",
+           reg_num, val);
+#endif
+
+    switch(reg_num) {
+    case PHY_REG_CONTROL:
+        if (val & PHY_REG_CONTROL_RST) {
+            /* Anything to do for phy reset? */
+            val &= ~(PHY_REG_CONTROL_RST | PHY_REG_CONTROL_LOOP);
+            s->phy_loop = 0;
+        }
+        if (val & PHY_REG_CONTROL_ANEG) {
+            /* Complete autonegotiation imediately */
+            val &= ~PHY_REG_CONTROL_ANEG;
+            s->phy_regs[PHY_REG_STATUS] |= PHY_REG_STATUS_ANEGCMPL;
+        }
+        if (val & PHY_REG_CONTROL_LOOP) {
+#ifdef GEM_DEBUG_PHY
+            printf("gem_phy_write: PHY placed in loopback\n");
+#endif
+            s->phy_loop = 1;
+        } else {
+            s->phy_loop = 0;
+        }
+        break;
+    }
+    s->phy_regs[reg_num] = val;
+}
+
+/*
+ * gem_read32:
+ * Read a GEM register.
+ */
+static uint64_t gem_read(void *opaque, target_phys_addr_t offset, unsigned size)
+{
+    gem_state *s;
+    uint32_t retval;
+
+    s = (gem_state *)opaque;
+
+    retval = s->regs[offset/4];
+
+#ifdef GEM_DEBUG_REG
+    printf("gem_read32:  offset: 0x%04x read: 0x%08x ", offset, retval);
+#endif
+    switch (offset) {
+    case GEM_ISR:
+        qemu_set_irq(s->irq, 0);
+        break;
+    case GEM_PHYMNTNC:
+        if (retval & GEM_PHYMNTNC_OP_R) {
+            uint32_t phy_addr, reg_num;
+
+            phy_addr = (retval & GEM_PHYMNTNC_ADDR) >> GEM_PHYMNTNC_ADDR_SHFT;
+            if (phy_addr == BOARD_PHY_ADDRESS) {
+                reg_num = (retval & GEM_PHYMNTNC_REG) >> GEM_PHYMNTNC_REG_SHIFT;
+                retval &= 0xFFFF0000;
+                retval |= gem_phy_read(s, reg_num);
+            } else {
+                retval |= 0xFFFF; /* No device at this address */
+            }
+        }
+        break;
+    }
+
+    /* Squash read to clear bits */
+    s->regs[offset/4] &= ~(s->regs_rtc[offset/4]);
+
+    /* Do not provide write only bits */
+    retval &= ~(s->regs_wo[offset/4]);
+
+#ifdef GEM_DEBUG_REG
+    printf("0x%08x\n", retval);
+#endif
+    return retval;
+}
+
+/*
+ * gem_write32:
+ * Write a GEM register.
+ */
+static void gem_write(void *opaque, target_phys_addr_t offset, uint64_t val,
+        unsigned size)
+{
+    gem_state *s = (gem_state *)opaque;
+    uint32_t readonly;
+
+#ifdef GEM_DEBUG_REG
+    printf("gem_writel: offset: 0x%04x write: 0x%08x ", offset, (unsigned)val);
+#endif
+
+    /* Squash bits which are read only in write value */
+    val &= ~(s->regs_ro[offset/4]);
+    /* Preserve (only) bits which are read only in register */
+    readonly = s->regs[offset/4];
+    readonly &= s->regs_ro[offset/4];
+
+    /* Squash bits which are write 1 to clear */
+    val &= ~(s->regs_w1c[offset/4] & val);
+
+    /* Copy register write to backing store */
+    s->regs[offset/4] = val | readonly;
+
+    /* Handle register write side effects */
+    switch (offset) {
+    case GEM_NWCTRL:
+        if (val & GEM_NWCTRL_TXSTART) {
+            gem_transmit(s);
+        }
+        if (!(val & GEM_NWCTRL_TXENA)) {
+            /* Reset to start of Q when transmit disabled. */
+            s->tx_desc_addr = s->regs[GEM_TXQBASE/4];
+        }
+        if (!(val & GEM_NWCTRL_RXENA)) {
+            /* Reset to start of Q when receive disabled. */
+            s->rx_desc_addr = s->regs[GEM_RXQBASE/4];
+        }
+        break;
+
+    case GEM_TXSTATUS:
+        gem_update_int_status(s);
+        break;
+    case GEM_RXQBASE:
+        s->rx_desc_addr = val;
+        break;
+    case GEM_TXQBASE:
+        s->tx_desc_addr = val;
+        break;
+    case GEM_RXSTATUS:
+        gem_update_int_status(s);
+        break;
+    case GEM_IER:
+        s->regs[GEM_IMR/4] &= ~val;
+        gem_update_int_status(s);
+        break;
+    case GEM_IDR:
+        s->regs[GEM_IMR/4] |= val;
+        gem_update_int_status(s);
+        break;
+    case GEM_PHYMNTNC:
+        if (val & GEM_PHYMNTNC_OP_W) {
+            uint32_t phy_addr, reg_num;
+
+            phy_addr = (val & GEM_PHYMNTNC_ADDR) >> GEM_PHYMNTNC_ADDR_SHFT;
+            if (phy_addr == BOARD_PHY_ADDRESS) {
+                reg_num = (val & GEM_PHYMNTNC_REG) >> GEM_PHYMNTNC_REG_SHIFT;
+                gem_phy_write(s, reg_num, val);
+            }
+        }
+        break;
+    }
+
+#ifdef GEM_DEBUG_REG
+    printf("newval: 0x%08x\n", s->regs[offset/4]);
+#endif
+}
+
+static const MemoryRegionOps gem_ops = {
+    .read = gem_read,
+    .write = gem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void gem_cleanup(VLANClientState *nc)
+{
+    gem_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+#ifdef GEM_DEBUG_FUNCTIONS
+    printf("%s:\n", __FUNCTION__);
+#endif
+
+    s->nic = NULL;
+}
+
+static void gem_set_link(VLANClientState *nc)
+{
+#ifdef GEM_DEBUG_FUNCTIONS
+    printf("%s:\n", __FUNCTION__);
+#endif
+    phy_update_link(DO_UPCAST(NICState, nc, nc)->opaque);
+}
+
+static NetClientInfo net_gem_info = {
+    .type = NET_CLIENT_TYPE_NIC,
+    .size = sizeof(NICState),
+    .can_receive = gem_can_receive,
+    .receive = gem_receive,
+    .cleanup = gem_cleanup,
+    .link_status_changed = gem_set_link,
+};
+
+static int gem_init(SysBusDevice *dev)
+{
+    gem_state *s;
+#ifdef GEM_DEBUG_FUNCTIONS
+    printf("%s:\n", __FUNCTION__);
+#endif
+
+    s = FROM_SYSBUS(gem_state, dev);
+    gem_init_register_masks(s);
+    memory_region_init_io(&s->iomem, &gem_ops, s, "enet", GEM_MAXREG);
+    sysbus_init_mmio(dev, &s->iomem);
+    sysbus_init_irq(dev, &s->irq);
+    qemu_macaddr_default_if_unset(&s->conf.macaddr);
+
+    s->nic = qemu_new_nic(&net_gem_info, &s->conf,
+                          dev->qdev.info->name, dev->qdev.id, s);
+
+    return 0;
+}
+
+static SysBusDeviceInfo gem_info = {
+    .init = gem_init,
+    .qdev.name  = "cadence_gem",
+    .qdev.size  = sizeof(gem_state),
+    .qdev.reset = gem_reset,
+    .qdev.props = (Property[]) {
+        DEFINE_NIC_PROPERTIES(gem_state, conf),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+static void gem_register_devices(void)
+{
+#ifdef GEM_DEBUG_FUNCTIONS
+    printf("%s:\n", __FUNCTION__);
+#endif
+    sysbus_register_withprop(&gem_info);
+}
+
+void gem_init2(NICInfo *nd, uint32_t base, qemu_irq irq);
+void gem_init2(NICInfo *nd, uint32_t base, qemu_irq irq)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+
+#ifdef GEM_DEBUG_FUNCTIONS
+    printf("%s: base: 0x%08x irq: %p\n", __FUNCTION__, base, irq);
+#endif
+    qemu_check_nic_model(nd, "cadence_gem");
+    dev = qdev_create(NULL, "cadence_gem");
+    qdev_set_nic_properties(dev, nd);
+    qdev_init_nofail(dev);
+    s = sysbus_from_qdev(dev);
+    sysbus_mmio_map(s, 0, base);
+    sysbus_connect_irq(s, 0, irq);
+}
+
+device_init(gem_register_devices)