Patchwork [v2,05/20] arm: add Faraday FTGMAC100 1Gbps ethernet support

login
register
mail settings
Submitter Kuo-Jung Su
Date Jan. 25, 2013, 8:19 a.m.
Message ID <1359101996-11667-6-git-send-email-dantesu@gmail.com>
Download mbox | patch
Permalink /patch/215598/
State New
Headers show

Comments

Kuo-Jung Su - Jan. 25, 2013, 8:19 a.m.
From: Kuo-Jung Su <dantesu@faraday-tech.com>

The FTGMAC100 is a high-quality Ethernet controller with DMA function.
It includes the AHB wrapper, DMA engine, on-chip memories (TX FIFO
and RX FIFO), MAC, and MII/GMII interfaces.

The FTGMAC100 is an Ethernet controller that provides AHB master capability
and full compliance with IEEE 802.3 specification for 10/100 Mbps Ethernet
and IEEE 802.3z specification for 1000 Mbps Ethernet. FTGMAC100 Ethernet
controller with DMA function handles all data transfers between the system
memory and on-chip memories. With the DMA engine, it can reduce the CPU
loading, maximize the performance and minimize the FIFO size. It also has
on-chip memories for buffering, so that the external local-buffer memory
is not needed. The MII interfaces support two specific data rates, 10 Mbps
and 100 Mbps. The GMII interfaces support 1000 Mbps data rate.

In order to reduce the processing load of the host CPU, the FTGMAC100
implements TCP, UDP, and IP V4 checksum generation and validation, and
supports VLAN tagging. For QoS and CoS requirements, it supports high-priority
queues to reduce the processing load of hosts CPU for transmitting packets.

The FTGMAC100 also provides a Wake-On-LAN function. It supports three
wake-up events: link status change, magic packet, and wake-up frame.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/ftgmac100.c |  647 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/ftgmac100.h |  177 ++++++++++++++++
 2 files changed, 824 insertions(+)
 create mode 100644 hw/ftgmac100.c
 create mode 100644 hw/ftgmac100.h
Paul Brook - Jan. 25, 2013, 11:18 p.m.
> In order to reduce the processing load of the host CPU, the FTGMAC100
> implements TCP, UDP, and IP V4 checksum generation and validation, and
> supports VLAN tagging.

I see no evidence of these features in the code.

> +static void ftgmac100_read_desc(hwaddr addr, void *desc)
> +{
> +    int i;
> +    uint32_t *p = desc;
> +
> +    cpu_physical_memory_read(addr, desc, 16);
> +
> +    for (i = 0; i < 16; i += 4) {
> +        *p = le32_to_cpu(*p);
> +    }
> +}

You're relying on the compiler choosing a particular bitfield and structure 
layout. Don't do that.  Especially when one of the fields is a void*.  Clearly 
never been tested on a 64-bit host. "void *desc" is just plain lazy.

> +        buf = s->txbuff.buf + s->txbuff.len;
> +        cpu_physical_memory_read(txd.buf, (uint8_t *)buf, txd.len);

Buffer overflow.  In at least two differnt ways.

> +            if (!(s->maccr & MACCR_HT_MULTI_EN)) {
> +                printf("[qemu] ftgmac100_receive: mcst filtered\n");
> +                return -1;

Looks like stray debug code.  Several other occurences.

> +    case REG_TXPD:
> +    case REG_HPTXPD:
> +        qemu_mod_timer(s->qtimer, qemu_get_clock_ns(vm_clock) + 1);

Using a timer here is wrong.  Either you should transmit immediately, or you 
should wait for something else to happen.  Delaying by 1ns is never the right 
answer.

Paul
Kuo-Jung Su - Jan. 28, 2013, 6:52 a.m.
2013/1/26 Paul Brook <paul@codesourcery.com>

> > In order to reduce the processing load of the host CPU, the FTGMAC100
> > implements TCP, UDP, and IP V4 checksum generation and validation, and
> > supports VLAN tagging.
>
> I see no evidence of these features in the code.
>
>
My bad .... and yes, the VLAN and checksum offload are not yet implemented.
I simply fotget to remove these description ... :P
but since the checksum offload feature of the real hardware actually
malfunctioned.
(becuase of the alignment issue).
I'll only implement the VLAN tagging feature in the next version.

> +static void ftgmac100_read_desc(hwaddr addr, void *desc)
> > +{
> > +    int i;
> > +    uint32_t *p = desc;
> > +
> > +    cpu_physical_memory_read(addr, desc, 16);
> > +
> > +    for (i = 0; i < 16; i += 4) {
> > +        *p = le32_to_cpu(*p);
> > +    }
> > +}
>
> You're relying on the compiler choosing a particular bitfield and structure
> layout. Don't do that.  Especially when one of the fields is a void*.
>  Clearly
> never been tested on a 64-bit host. "void *desc" is just plain lazy.
>
>
yes, it's my bad. It'll be fixed later


> > +        buf = s->txbuff.buf + s->txbuff.len;
> > +        cpu_physical_memory_read(txd.buf, (uint8_t *)buf, txd.len);
>
> Buffer overflow.  In at least two differnt ways.
>

yes, it's my bad. It'll be fixed later
>
>
> > +            if (!(s->maccr & MACCR_HT_MULTI_EN)) {
> > +                printf("[qemu] ftgmac100_receive: mcst filtered\n");
> > +                return -1;
>
> Looks like stray debug code.  Several other occurences.
>
> > +    case REG_TXPD:
> > +    case REG_HPTXPD:
> > +        qemu_mod_timer(s->qtimer, qemu_get_clock_ns(vm_clock) + 1);
>
> Using a timer here is wrong.  Either you should transmit immediately, or
> you
> should wait for something else to happen.  Delaying by 1ns is never the
> right
> answer.
>
> Paul
>

yes, it's my bad. I'll try to use mutex later

Patch

diff --git a/hw/ftgmac100.c b/hw/ftgmac100.c
new file mode 100644
index 0000000..48f10bb
--- /dev/null
+++ b/hw/ftgmac100.c
@@ -0,0 +1,647 @@ 
+/*
+ * QEMU model of the FTGMAC100 Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2.
+ */
+
+#include "sysbus.h"
+#include "sysemu/sysemu.h"
+#include "net/net.h"
+
+#include "faraday.h"
+#include "ftgmac100.h"
+
+#define TYPE_FTGMAC100    "ftgmac100"
+
+typedef struct Ftgmac100State {
+    SysBusDevice busdev;
+    MemoryRegion mmio;
+
+    QEMUTimer *qtimer;
+    qemu_irq irq;
+    NICState *nic;
+    NICConf conf;
+
+    uint32_t isr;
+    uint32_t ier;
+    uint32_t mhash[2];
+    uint32_t tx_bar;
+    uint32_t rx_bar;
+    uint32_t hptx_bar;
+    uint32_t tx_idx;
+    uint32_t rx_idx;
+    uint32_t hptx_idx;
+    uint32_t maccr;
+    uint32_t macsr;
+    uint32_t phycr;
+    uint32_t phycr_rd;
+
+    struct {
+        uint8_t  buf[2048];
+        uint32_t len;
+    } txbuff;
+
+    uint32_t rx_pkt;
+    uint32_t rx_bcst;
+    uint32_t rx_mcst;
+    uint16_t rx_runt;
+    uint16_t rx_drop;
+    uint16_t rx_crc;
+    uint16_t rx_ftl;
+    uint32_t tx_pkt;
+
+} ftgmac100_state;
+
+#define FTGMAC100(obj) \
+    OBJECT_CHECK(ftgmac100_state, obj, TYPE_FTGMAC100)
+
+static uint8_t bitrev8(uint8_t v)
+{
+    int i;
+    uint8_t r = 0;
+    for (i = 0; i < 8; ++i) {
+        if (v & (1 << i)) {
+            r |= (1 << (7 - i));
+        }
+    }
+    return r;
+}
+
+static int ftgmac100_mcast_hash(ftgmac100_state *s, const uint8_t *data)
+{
+#define CRCPOLY_BE    0x04c11db7
+    int i, len;
+    uint32_t crc = 0xFFFFFFFF;
+
+    if (s->maccr & MACCR_GMODE) {
+        len = 5;
+    } else {
+        len = 6;
+    }
+
+    while (len--) {
+        uint32_t c = *(data++);
+        for (i = 0; i < 8; ++i) {
+            crc = (crc << 1) ^ ((((crc >> 31) ^ c) & 0x01) ? CRCPOLY_BE : 0);
+            c >>= 1;
+        }
+    }
+    crc = ~crc;
+
+    /* Reverse CRC32 and return MSB 6 bits only */
+    return bitrev8(crc >> 24) >> 2;
+}
+
+static void ftgmac100_read_desc(hwaddr addr, void *desc)
+{
+    int i;
+    uint32_t *p = desc;
+
+    cpu_physical_memory_read(addr, desc, 16);
+
+    for (i = 0; i < 16; i += 4) {
+        *p = le32_to_cpu(*p);
+    }
+}
+
+static void ftgmac100_write_desc(hwaddr addr, void *desc)
+{
+    int i;
+    uint32_t *p = desc;
+
+    for (i = 0; i < 16; i += 4) {
+        *p = cpu_to_le32(*p);
+    }
+
+    cpu_physical_memory_write(addr, desc, 16);
+}
+
+static void ftgmac100_update_irq(ftgmac100_state *s)
+{
+    if (s->isr & s->ier) {
+        qemu_set_irq(s->irq, 1);
+    } else {
+        qemu_set_irq(s->irq, 0);
+    }
+}
+
+static int ftgmac100_can_receive(NetClientState *nc)
+{
+    ftgmac100_state *s = FTGMAC100(DO_UPCAST(NICState, nc, nc)->opaque);
+    ftgmac100_rxdesc_t rxd;
+    hwaddr off = s->rx_bar + s->rx_idx * sizeof(rxd);
+
+    if ((s->maccr & (MACCR_RCV_EN | MACCR_RDMA_EN))
+            != (MACCR_RCV_EN | MACCR_RDMA_EN)) {
+        return 0;
+    }
+
+    ftgmac100_read_desc(off, &rxd);
+
+    return !rxd.owner;
+}
+
+static ssize_t ftgmac100_receive(NetClientState *nc,
+                                 const uint8_t  *buf,
+                                 size_t          size)
+{
+    const uint8_t *ptr = buf;
+    hwaddr off;
+    size_t len;
+    ftgmac100_rxdesc_t rxd;
+    ftgmac100_state *s = FTGMAC100(DO_UPCAST(NICState, nc, nc)->opaque);
+    int bcst, mcst;
+
+    if ((s->maccr & (MACCR_RCV_EN | MACCR_RDMA_EN))
+            != (MACCR_RCV_EN | MACCR_RDMA_EN)) {
+        return -1;
+    }
+
+    /* if it's a broadcast */
+    if ((buf[0] == 0xff) && (buf[1] == 0xff) && (buf[2] == 0xff)
+            && (buf[3] == 0xff) && (buf[4] == 0xff) && (buf[5] == 0xff)) {
+        bcst = 1;
+        if (!(s->maccr & MACCR_RCV_ALL) && !(s->maccr & MACCR_RX_BROADPKT)) {
+            printf("[qemu] ftgmac100_receive: bcst filtered\n");
+            return -1;
+        }
+    } else {
+        bcst = 0;
+    }
+
+    /* if it's a multicast */
+    if ((buf[0] == 0x01) && (buf[1] == 0x00) && (buf[2] == 0x5e)
+        && (buf[3] <= 0x7f)) {
+        mcst = 1;
+        if (!(s->maccr & MACCR_RCV_ALL) && !(s->maccr & MACCR_RX_MULTIPKT)) {
+            int hash;
+            if (!(s->maccr & MACCR_HT_MULTI_EN)) {
+                printf("[qemu] ftgmac100_receive: mcst filtered\n");
+                return -1;
+            }
+            hash = ftgmac100_mcast_hash(s, buf);
+            if (!(s->mhash[hash / 32] & (1 << (hash % 32)))) {
+                printf("[qemu] ftgmac100_receive: mcst filtered\n");
+                return -1;
+            }
+        }
+    } else {
+        mcst = 0;
+    }
+
+    /* check if the destination matches NIC mac address */
+    if (!(s->maccr & MACCR_RCV_ALL) && !bcst && !mcst) {
+        if (memcmp(s->conf.macaddr.a, buf, 6)) {
+            return -1;
+        }
+    }
+
+    while (size > 0) {
+        off = s->rx_bar + s->rx_idx * sizeof(rxd);
+        ftgmac100_read_desc(off, &rxd);
+        if (rxd.owner) {
+            s->isr |= ISR_NORXBUF;
+            printf("[qemu] ftgmac100: out of rxd!?\n");
+            return -1;
+        }
+
+        if (ptr == buf) {
+            rxd.frs = 1;
+        } else {
+            rxd.frs = 0;
+        }
+
+        len = size > rxd.len ? rxd.len : size;
+        cpu_physical_memory_write(rxd.buf, (uint8_t *)ptr, len);
+        ptr  += len;
+        size -= len;
+
+        if (size <= 0) {
+            rxd.lrs = 1;
+        } else {
+            rxd.lrs = 0;
+        }
+
+        rxd.len = len;
+        rxd.bcast = bcst;
+        rxd.mcast = mcst;
+        rxd.owner = 1;
+
+        /* write-back the rx descriptor */
+        ftgmac100_write_desc(off, &rxd);
+
+        if (rxd.end) {
+            s->rx_idx = 0;
+        } else {
+            s->rx_idx += 1;
+        }
+    }
+
+    /* update interrupt signal */
+    s->isr |= ISR_RPKT_OK | ISR_RPKT_FINISH;
+    ftgmac100_update_irq(s);
+
+    return (ssize_t)((uint32_t)ptr - (uint32_t)buf);
+}
+
+static void ftgmac100_transmit(ftgmac100_state *s,
+                               uint32_t        *bar,
+                               uint32_t        *idx)
+{
+    hwaddr off;
+    uint8_t *buf;
+    ftgmac100_txdesc_t txd;
+
+    if ((s->maccr & (MACCR_XMT_EN | MACCR_XDMA_EN))
+            != (MACCR_XMT_EN | MACCR_XDMA_EN)) {
+        return;
+    }
+
+    do {
+        off = (*bar) + (*idx) * sizeof(txd);
+        ftgmac100_read_desc(off, &txd);
+        if (!txd.owner) {
+            s->isr |= ISR_NOTXBUF;
+            break;
+        }
+        if (txd.fts) {
+            s->txbuff.len = 0;
+        }
+        buf = s->txbuff.buf + s->txbuff.len;
+        cpu_physical_memory_read(txd.buf, (uint8_t *)buf, txd.len);
+        s->txbuff.len += txd.len;
+        if (txd.lts) {
+            if (s->maccr & MACCR_LOOP_EN) {
+                ftgmac100_receive(&s->nic->nc, s->txbuff.buf, s->txbuff.len);
+            } else {
+                qemu_send_packet(&s->nic->nc, s->txbuff.buf, s->txbuff.len);
+            }
+        }
+        if (txd.end) {
+            *idx = 0;
+        } else {
+            *idx += 1;
+        }
+        if (txd.tx2fic) {
+            s->isr |= ISR_XPKT_OK;
+        }
+        if (txd.txic) {
+            s->isr |= ISR_XPKT_FINISH;
+        }
+        txd.owner = 0;
+        ftgmac100_write_desc(off, &txd);
+    } while (1);
+}
+
+static void ftgmac100_timer_tick(void *opaque)
+{
+    ftgmac100_state *s = FTGMAC100(opaque);
+
+    /* 1. process high priority tx ring */
+    if (s->hptx_bar && (s->maccr & MACCR_HPTXR_EN)) {
+        ftgmac100_transmit(s, &s->hptx_bar, &s->hptx_idx);
+    }
+
+    /* 2. process normal priority tx ring */
+    if (s->tx_bar) {
+        ftgmac100_transmit(s, &s->tx_bar, &s->tx_idx);
+    }
+
+    /* 3. update interrupt signal */
+    ftgmac100_update_irq(s);
+}
+
+static uint64_t ftgmac100_mem_read(void    *opaque,
+                                   hwaddr   addr,
+                                   unsigned size)
+{
+    ftgmac100_state *s = FTGMAC100(opaque);
+    uint32_t rc = 0;
+
+    switch (addr) {
+    case REG_ISR:
+        return s->isr;
+    case REG_IMR:
+        return s->ier;
+    case REG_HMAC:
+        return s->conf.macaddr.a[1]
+               | (s->conf.macaddr.a[0] << 8);
+    case REG_LMAC:
+        return s->conf.macaddr.a[5]
+               | (s->conf.macaddr.a[4] << 8)
+               | (s->conf.macaddr.a[3] << 16)
+               | (s->conf.macaddr.a[2] << 24);
+    case REG_MHASH0:
+        return s->mhash[0];
+    case REG_MHASH1:
+        return s->mhash[1];
+    case REG_TXBAR:
+        return s->tx_bar;
+    case REG_RXBAR:
+        return s->rx_bar;
+    case REG_HPTXBAR:
+        return s->hptx_bar;
+    case REG_ITC:
+        return 0x00000000;
+    case REG_APTC:
+        return 0x00000001;
+    case REG_DBLAC:
+        return 0x00022f72;
+    case REG_DMAFIFO:
+        return 0x0c000000;
+    case REG_REVR:
+        return 0x00000600;
+    case REG_FEAR:
+        return 0x0000001b;
+    case REG_TPAFCR:
+        return 0x000000f1;
+    case REG_RBSR:
+        return 0x00000640;
+    case REG_MACCR:
+        return s->maccr;
+    case REG_MACSR:
+        return s->macsr;
+    case REG_PHYCTRL:
+        return s->phycr;
+    case REG_PHYDATA:
+        do {
+            uint8_t dev = (s->phycr >> 16) & 0x1f;
+            uint8_t reg = (s->phycr >> 21) & 0x1f;
+            if (dev != 0) {
+                break;
+            }
+            if (s->phycr_rd) {
+                switch (reg) {
+                case 0:    /* PHY control register */
+                    return 0x1140 << 16;
+                case 1:    /* PHY status register */
+                    return 0x796d << 16;
+                case 2:    /* PHY ID 1 register */
+                    return 0x0141 << 16;
+                case 3:    /* PHY ID 2 register */
+                    return 0x0cc2 << 16;
+                case 4:    /* Autonegotiation advertisement register */
+                    return 0x0de1 << 16;
+                case 5:    /* Autonegotiation partner abilities register */
+                    return 0x45e1 << 16;
+                case 17:/* Marvell 88E1111 */
+                    rc = (2 << 14) | (1 << 13) | (1 << 11) | (1 << 10);
+                    return rc << 16;
+                }
+            }
+        } while (0);
+        break;
+    case REG_FCR:
+        return 2 << 9;
+    case REG_BPR:
+        return 4 << 8;
+    case REG_TXPTR:
+        return s->tx_idx;
+    case REG_HPTXPTR:
+        return s->hptx_idx;
+    case REG_RXPTR:
+        return s->rx_idx;
+    case REG_TXPKT:
+        return s->tx_pkt;
+    case REG_RXPKT:
+        return s->rx_pkt;
+    case REG_RXBCST:
+        return s->rx_bcst;
+    case REG_RXMCST:
+        return s->rx_mcst;
+    case REG_RXRUNT:
+        return s->rx_runt;
+    case REG_RXERR0:
+        return (s->rx_crc << 16) | (s->rx_ftl);
+    case REG_RXERR1:
+        return s->rx_drop << 16;
+    default:
+        break;
+    }
+
+    return rc;
+}
+
+static void ftgmac100_chip_reset(ftgmac100_state *s)
+{
+    s->isr = 0;
+    s->ier = 0;
+    s->mhash[0] = 0;
+    s->mhash[1] = 0;
+    s->tx_bar = 0;
+    s->rx_bar = 0;
+    s->hptx_bar = 0;
+    s->tx_idx = 0;
+    s->rx_idx = 0;
+    s->hptx_idx = 0;
+    s->maccr = 0;
+    s->macsr = 0;
+    s->phycr = 0;
+    s->txbuff.len = 0;
+    s->rx_pkt = 0;
+    s->rx_bcst = 0;
+    s->rx_mcst = 0;
+    s->rx_runt = 0;
+    s->rx_drop = 0;
+    s->rx_crc = 0;
+    s->rx_ftl = 0;
+    s->tx_pkt = 0;
+
+    if (s->qtimer) {
+        qemu_del_timer(s->qtimer);
+    }
+
+    ftgmac100_update_irq(s);
+}
+
+static void ftgmac100_mem_write(void    *opaque,
+                                hwaddr   addr,
+                                uint64_t val,
+                                unsigned size)
+{
+    ftgmac100_state *s = FTGMAC100(opaque);
+
+    switch (addr) {
+    case REG_ISR:
+        s->isr &= ~((uint32_t)val);
+        ftgmac100_update_irq(s);
+        break;
+    case REG_IMR:
+        s->ier = (uint32_t)val;
+        ftgmac100_update_irq(s);
+        break;
+    case REG_HMAC:
+        s->conf.macaddr.a[1] = (val >> 0) & 0xff;
+        s->conf.macaddr.a[0] = (val >> 8) & 0xff;
+        break;
+    case REG_LMAC:
+        s->conf.macaddr.a[5] = (val >> 0) & 0xff;
+        s->conf.macaddr.a[4] = (val >> 8) & 0xff;
+        s->conf.macaddr.a[3] = (val >> 16) & 0xff;
+        s->conf.macaddr.a[2] = (val >> 24) & 0xff;
+        break;
+    case REG_MHASH0:
+        s->mhash[0] = (uint32_t)val;
+        break;
+    case REG_MHASH1:
+        s->mhash[1] = (uint32_t)val;
+        break;
+    case REG_TXBAR:
+        s->tx_bar = (uint32_t)val;
+        break;
+    case REG_RXBAR:
+        s->rx_bar = (uint32_t)val;
+        break;
+    case REG_HPTXBAR:
+        s->hptx_bar = (uint32_t)val;
+        break;
+    case REG_MACCR:
+        s->maccr = (uint32_t)val;
+        if (s->maccr & MACCR_SW_RST) {
+            ftgmac100_chip_reset(s);
+            s->maccr &= ~MACCR_SW_RST;
+        }
+        break;
+    case REG_MACSR:
+        s->macsr &= ~((uint32_t)val);
+        break;
+    case REG_PHYCTRL:
+        s->phycr = (uint32_t)val;
+        if (s->phycr & PHYCR_MDIORD) {
+            s->phycr_rd = 1;
+        } else {
+            s->phycr_rd = 0;
+        }
+        s->phycr &= ~(PHYCR_MDIOWR | PHYCR_MDIORD);
+        break;
+    case REG_TXPD:
+    case REG_HPTXPD:
+        qemu_mod_timer(s->qtimer, qemu_get_clock_ns(vm_clock) + 1);
+        break;
+    default:
+        break;
+    }
+}
+
+static const MemoryRegionOps bus_ops = {
+    .read  = ftgmac100_mem_read,
+    .write = ftgmac100_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void ftgmac100_cleanup(NetClientState *nc)
+{
+    ftgmac100_state *s = FTGMAC100(DO_UPCAST(NICState, nc, nc)->opaque);
+
+    s->nic = NULL;
+}
+
+static NetClientInfo net_ftgmac100_info = {
+    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .size = sizeof(NICState),
+    .can_receive = ftgmac100_can_receive,
+    .receive = ftgmac100_receive,
+    .cleanup = ftgmac100_cleanup,
+};
+
+static void ftgmac100_reset(DeviceState *ds)
+{
+    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
+    ftgmac100_state *s = FTGMAC100(FROM_SYSBUS(ftgmac100_state, busdev));
+
+    ftgmac100_chip_reset(s);
+}
+
+static int ftgmac100_init1(SysBusDevice *dev)
+{
+    ftgmac100_state *s = FTGMAC100(FROM_SYSBUS(ftgmac100_state, dev));
+
+    memory_region_init_io(&s->mmio, &bus_ops, s, TYPE_FTGMAC100, 0x1000);
+    sysbus_init_mmio(dev, &s->mmio);
+    sysbus_init_irq(dev, &s->irq);
+
+    qemu_macaddr_default_if_unset(&s->conf.macaddr);
+    s->nic = qemu_new_nic(&net_ftgmac100_info, &s->conf,
+                          object_get_typename(OBJECT(dev)), dev->qdev.id, s);
+    qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+
+    s->qtimer = qemu_new_timer_ns(vm_clock, ftgmac100_timer_tick, s);
+
+    ftgmac100_chip_reset(s);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_ftgmac100 = {
+    .name = TYPE_FTGMAC100,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(ier, ftgmac100_state),
+        VMSTATE_UINT32(tx_bar, ftgmac100_state),
+        VMSTATE_UINT32(rx_bar, ftgmac100_state),
+        VMSTATE_UINT32(hptx_bar, ftgmac100_state),
+        VMSTATE_UINT32(tx_idx, ftgmac100_state),
+        VMSTATE_UINT32(rx_idx, ftgmac100_state),
+        VMSTATE_UINT32(hptx_idx, ftgmac100_state),
+        VMSTATE_UINT32(maccr, ftgmac100_state),
+        VMSTATE_UINT32(macsr, ftgmac100_state),
+        VMSTATE_UINT32(phycr, ftgmac100_state),
+        VMSTATE_UINT32_ARRAY(mhash, ftgmac100_state, 2),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property ftgmac100_properties[] = {
+    DEFINE_NIC_PROPERTIES(ftgmac100_state, conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void ftgmac100_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init   = ftgmac100_init1;
+    dc->reset = ftgmac100_reset;
+    dc->vmsd  = &vmstate_ftgmac100;
+    dc->props = ftgmac100_properties;
+}
+
+static const TypeInfo ftgmac100_info = {
+    .name           = TYPE_FTGMAC100,
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(ftgmac100_state),
+    .class_init     = ftgmac100_class_init,
+};
+
+static void ftgmac100_register_types(void)
+{
+    type_register_static(&ftgmac100_info);
+}
+
+/* Legacy helper function.  Should go away when machine config files are
+   implemented.  */
+void ftgmac100_init(NICInfo *nd, uint32_t base, qemu_irq irq)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+
+    qemu_check_nic_model(nd, TYPE_FTGMAC100);
+    dev = qdev_create(NULL, TYPE_FTGMAC100);
+    qdev_set_nic_properties(dev, nd);
+    qdev_init_nofail(dev);
+    s = SYS_BUS_DEVICE(dev);
+    sysbus_mmio_map(s, 0, base);
+    sysbus_connect_irq(s, 0, irq);
+}
+
+type_init(ftgmac100_register_types)
diff --git a/hw/ftgmac100.h b/hw/ftgmac100.h
new file mode 100644
index 0000000..40100e3
--- /dev/null
+++ b/hw/ftgmac100.h
@@ -0,0 +1,177 @@ 
+/*
+ * QEMU model of the FTGMAC100 Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL.
+ */
+
+#ifndef _FTGMAC100_H
+#define _FTGMAC100_H
+
+#define REG_ISR             0x00
+#define REG_IMR             0x04
+#define REG_HMAC            0x08
+#define REG_LMAC            0x0c
+#define REG_MHASH0          0x10
+#define REG_MHASH1          0x14
+#define REG_TXPD            0x18
+#define REG_RXPD            0x1c
+#define REG_TXBAR           0x20
+#define REG_RXBAR           0x24
+#define REG_HPTXPD          0x28
+#define REG_HPTXBAR         0x2C
+#define REG_ITC             0x30
+#define REG_APTC            0x34
+#define REG_DBLAC           0x38
+#define REG_DMAFIFO         0x3C
+#define REG_REVR            0x40
+#define REG_FEAR            0x44
+#define REG_TPAFCR          0x48
+#define REG_RBSR            0x4C
+#define REG_MACCR           0x50
+#define REG_MACSR           0x54
+#define REG_TSTMODE         0x58
+#define REG_PHYCTRL         0x60
+#define REG_PHYDATA         0x64
+#define REG_FCR             0x68
+#define REG_BPR             0x6c
+#define REG_WOLCR           0x70
+#define REG_WOLSR           0x74
+#define REG_WFCRC           0x78
+#define REG_WFBM1           0x80
+#define REG_WFBM2           0x84
+#define REG_WFBM3           0x88
+#define REG_WFBM4           0x8c
+#define REG_TXPTR           0x90
+#define REG_HPTXPTR         0x94
+#define REG_RXPTR           0x98
+#define REG_TXPKT           0xa0
+#define REG_TXERR0          0xa4
+#define REG_TXERR1          0xa8
+#define REG_TXERR2          0xac
+#define REG_RXPKT           0xb0
+#define REG_RXBCST          0xb4
+#define REG_RXMCST          0xb8
+#define REG_RXRUNT          0xc0
+#define REG_RXERR0          0xc4
+#define REG_RXERR1          0xc8
+
+/* interrupt status register */
+#define ISR_NOHTXB          (1UL<<10)
+#define ISR_PHYSTS_CHG      (1UL<<9)
+#define ISR_AHB_ERR         (1UL<<8)
+#define ISR_XPKT_LOST       (1UL<<7)
+#define ISR_NOTXBUF         (1UL<<6)
+#define ISR_XPKT_OK         (1UL<<5)
+#define ISR_XPKT_FINISH     (1UL<<4)
+#define ISR_RPKT_LOST       (1UL<<3)
+#define ISR_NORXBUF         (1UL<<2)
+#define ISR_RPKT_OK         (1UL<<1)
+#define ISR_RPKT_FINISH     (1UL<<0)
+
+/* MAC control register */
+#define MACCR_SW_RST            (1UL<<31)
+#define MACCR_100M              (1UL<<19)
+#define MACCR_CRC_DIS           (1UL<<18)
+#define MACCR_RX_BROADPKT       (1UL<<17)
+#define MACCR_RX_MULTIPKT       (1UL<<16)
+#define MACCR_HT_MULTI_EN       (1UL<<15)
+#define MACCR_RCV_ALL           (1UL<<14)
+#define MACCR_JUMBO_LF          (1UL<<13)
+#define MACCR_RX_RUNT           (1UL<<12)
+#define MACCR_CRC_APD           (1UL<<10)
+#define MACCR_GMODE             (1UL<<9)
+#define MACCR_FULLDUP           (1UL<<8)
+#define MACCR_ENRX_IN_HALFTX    (1UL<<7)
+#define MACCR_LOOP_EN           (1UL<<6)
+#define MACCR_HPTXR_EN          (1UL<<5)
+#define MACCR_VLAN_RM           (1UL<<4)
+#define MACCR_RCV_EN            (1UL<<3)
+#define MACCR_XMT_EN            (1UL<<2)
+#define MACCR_RDMA_EN           (1UL<<1)
+#define MACCR_XDMA_EN           (1UL<<0)
+
+/*
+ * MDIO
+ */
+#define PHYCR_MDIOWR            (1 << 27)
+#define PHYCR_MDIORD            (1 << 26)
+
+/*
+ * Tx/Rx Descriptors
+ */
+typedef struct {
+    /* RXDES0 */
+    uint32_t len:14;
+    uint32_t rsvd1:1;
+    uint32_t end:1;
+    uint32_t mcast:1;
+    uint32_t bcast:1;
+    uint32_t rxerr:1;
+    uint32_t crcerr:1;
+    uint32_t ftl:1;
+    uint32_t runt:1;
+    uint32_t oddnb:1;
+    uint32_t fifofull:1;
+    uint32_t pauseopc:1;
+    uint32_t pausefrm:1;
+    uint32_t rsvd2:2;
+    uint32_t lrs:1;
+    uint32_t frs:1;
+    uint32_t rsvd3:1;
+    uint32_t owner:1; /* 31 - 1:SW, 0: HW */
+
+    /* RXDES1 */
+    uint32_t vlantag:16;
+    uint32_t rsvd4:4;
+    uint32_t proto:2;
+    uint32_t llc:1;
+    uint32_t df:1;
+    uint32_t vlan:1;
+    uint32_t tcpcs:1;
+    uint32_t udpcs:1;
+    uint32_t ipcs:1;
+    uint32_t rsvd5:4;
+
+    /* RXDES2 */
+    void    *skb;
+
+    /* RXDES3 */
+    uint32_t buf;
+} ftgmac100_rxdesc_t;
+
+typedef struct {
+    /* TXDES0 */
+    uint32_t len:14;
+    uint32_t rsvd1:1;
+    uint32_t end:1;
+    uint32_t rsvd2:3;
+    uint32_t crcerr:1;
+    uint32_t rsvd3:8;
+    uint32_t lts:1;
+    uint32_t fts:1;
+    uint32_t rsvd4:1;
+    uint32_t owner:1;   /* 31 - 1:HW, 0: SW */
+
+    /* TXDES1 */
+    uint32_t vlantag:16;
+    uint32_t vlan:1;
+    uint32_t tcpcs:1;
+    uint32_t udpcs:1;
+    uint32_t ipcs:1;
+    uint32_t rsvd5:2;
+    uint32_t llc:1;
+    uint32_t rsvd6:7;
+    uint32_t tx2fic:1;
+    uint32_t txic:1;
+
+    /* TXDES2 */
+    void    *skb;
+
+    /* TXDES3 */
+    uint32_t buf;
+} ftgmac100_txdesc_t;
+
+#endif    /* #ifndef _FTGMAC100_H */