All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kuo-Jung Su <dantesu@gmail.com>
To: qemu-devel@nongnu.org
Cc: Peter Maydell <peter.maydell@linaro.org>,
	i.mitsyanko@samsung.com, Blue Swirl <blauwirbel@gmail.com>,
	Paul Brook <paul@codesourcery.com>,
	Kuo-Jung Su <dantesu@faraday-tech.com>,
	Andreas <afaerber@suse.de>,
	fred.konrad@greensocs.com
Subject: [Qemu-devel] [PATCH v9 22/24] hw/arm: add FTMAC110 10/100Mbps ethernet support
Date: Mon, 25 Mar 2013 20:09:58 +0800	[thread overview]
Message-ID: <1364213400-10266-23-git-send-email-dantesu@gmail.com> (raw)
In-Reply-To: <1364213400-10266-1-git-send-email-dantesu@gmail.com>

From: Kuo-Jung Su <dantesu@faraday-tech.com>

The FTMAC110 is an Ethernet controller that provides AHB master capability
and is in full compliance with the IEEE 802.3 10/100 Mbps specifications.
Its DMA controller handles all data transfers between system memory
and on-chip memories.

It supports half-word data transfer for Linux. However it has a weird DMA
alignment issue:

(1) Tx DMA Buffer Address:
    1 bytes aligned: Invalid
    2 bytes aligned: O.K
    4 bytes aligned: O.K

(2) Rx DMA Buffer Address:
    1 bytes aligned: Invalid
    2 bytes aligned: O.K
    4 bytes aligned: Invalid (It means 0x0, 0x4, 0x8, 0xC are invalid)

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/arm/Makefile.objs |    3 +-
 hw/arm/ftplat_a369.c |    7 +
 hw/faraday.h         |    3 +
 hw/ftmac110.c        |  665 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/ftmac110.h        |  166 +++++++++++++
 5 files changed, 843 insertions(+), 1 deletion(-)
 create mode 100644 hw/ftmac110.c
 create mode 100644 hw/ftmac110.h

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 93e94db..4697a76 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -26,7 +26,8 @@ obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
 obj-$(CONFIG_KVM) += kvm/arm_gic.o
 obj-y += ftintc020.o ftahbc020.o ftddrii030.o ftpwmtmr010.o ftwdt010.o \
                 ftrtc011.o ftdmac020.o ftapbbrg020.o ftnandc021.o fti2c010.o \
-                ftssp010.o ftgmac100.o ftlcdc200.o fttsc010.o ftsdc010.o
+                ftssp010.o ftgmac100.o ftlcdc200.o fttsc010.o ftsdc010.o \
+                ftmac110.o
 
 obj-y := $(addprefix ../,$(obj-y))
 
diff --git a/hw/arm/ftplat_a369.c b/hw/arm/ftplat_a369.c
index 922fb55..1b3e3cd 100644
--- a/hw/arm/ftplat_a369.c
+++ b/hw/arm/ftplat_a369.c
@@ -110,6 +110,13 @@ static void a369_board_init(QEMUMachineInitArgs *args)
                                  s->i2s[0]);
     }
 
+    /* External AHB devices */
+
+    /* Ethernet: FTMAC110 */
+    if (nb_nics > 1) {
+        ftmac110_init(&nd_table[1], 0xC0100000, s->pic[5]);
+    }
+
     /* System start-up */
 
     if (args->kernel_filename) {
diff --git a/hw/faraday.h b/hw/faraday.h
index 068b799..ba7ea0e 100644
--- a/hw/faraday.h
+++ b/hw/faraday.h
@@ -127,4 +127,7 @@ void ftssp010_i2s_data_req(void *opaque, int tx, int rx);
 /* ftgmac100.c */
 void ftgmac100_init(NICInfo *nd, uint32_t base, qemu_irq irq);
 
+/* ftmac110.c */
+void ftmac110_init(NICInfo *nd, uint32_t base, qemu_irq irq);
+
 #endif
diff --git a/hw/ftmac110.c b/hw/ftmac110.c
new file mode 100644
index 0000000..de1164d
--- /dev/null
+++ b/hw/ftmac110.c
@@ -0,0 +1,665 @@
+/*
+ * QEMU model of the FTMAC110 Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+/*******************************************************************/
+/*               FTMAC110 DMA design issue                         */
+/*                                             Dante Su 2010.02.03 */
+/*                                                                 */
+/* The DMA engine has a weird restriction that its Rx DMA engine   */
+/* accepts only 16-bits aligned address, 32-bits aligned is still  */
+/* invalid. However this restriction does not apply to Tx DMA.     */
+/* Conclusion:                                                     */
+/* (1) Tx DMA Buffer Address:                                      */
+/*     1 bytes aligned: Invalid                                    */
+/*     2 bytes aligned: O.K                                        */
+/*     4 bytes aligned: O.K (-> u-boot ZeroCopy is possible)       */
+/* (2) Rx DMA Buffer Address:                                      */
+/*     1 bytes aligned: Invalid                                    */
+/*     2 bytes aligned: O.K                                        */
+/*     4 bytes aligned: Invalid                                    */
+/*******************************************************************/
+
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/dma.h"
+#include "net/net.h"
+
+#include "hw/faraday.h"
+#include "hw/ftmac110.h"
+
+#ifndef DEBUG
+#define DEBUG   0
+#endif
+
+#define DPRINTF(fmt, ...) \
+    do { \
+        if (DEBUG) { \
+            fprintf(stderr, fmt , ## __VA_ARGS__); \
+        } \
+    } while (0)
+
+#define TYPE_FTMAC110   "ftmac110"
+
+#define CFG_MAXFRMLEN   1536    /* Max. frame length */
+#define CFG_REGSIZE     (0x100 / 4)
+
+typedef struct Ftmac110State {
+    /*< private >*/
+    SysBusDevice parent;
+
+    /*< public >*/
+    MemoryRegion mmio;
+
+    QEMUBH *bh;
+    qemu_irq irq;
+    NICState *nic;
+    NICConf conf;
+    DMAContext *dma;
+    QEMUTimer *qtimer;
+
+    bool phycr_rd;
+
+    struct {
+        uint8_t  buf[CFG_MAXFRMLEN];
+        uint32_t len;
+    } txbuff;
+
+    uint32_t tx_idx;
+    uint32_t rx_idx;
+
+    /* HW register cache */
+    uint32_t regs[CFG_REGSIZE];
+} Ftmac110State;
+
+#define FTMAC110(obj) \
+    OBJECT_CHECK(Ftmac110State, obj, TYPE_FTMAC110)
+
+#define MAC_REG32(s, off) \
+    ((s)->regs[(off) / 4])
+
+static int ftmac110_mcast_hash(int len, const uint8_t *p)
+{
+#define CRCPOLY_LE 0xedb88320
+    int i;
+    uint32_t crc = 0xFFFFFFFF;
+
+    while (len--) {
+        crc ^= *p++;
+        for (i = 0; i < 8; i++) {
+            crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
+        }
+    }
+
+    /* Reverse CRC32 and return MSB 6 bits only */
+    return bitrev8(crc >> 24) >> 2;
+}
+
+static void
+ftmac110_read_rxdesc(Ftmac110State *s, hwaddr addr, Ftmac110RXD *desc)
+{
+    int i;
+    uint32_t *p = (uint32_t *)desc;
+
+    if (addr & 0x0f) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                 "ftmac110: Rx desc is not 16-byte aligned!\n"
+                 "It's fine in QEMU but the real HW would panic.\n");
+    }
+
+    dma_memory_read(s->dma, addr, desc, sizeof(*desc));
+
+    for (i = 0; i < sizeof(*desc); i += 4) {
+        *p = le32_to_cpu(*p);
+    }
+
+    if ((desc->buf & 0x1) || !(desc->buf % 4)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                 "ftmac110: rx buffer is not exactly 16-bit aligned.\n");
+    }
+}
+
+static void
+ftmac110_write_rxdesc(Ftmac110State *s, hwaddr addr, Ftmac110RXD *desc)
+{
+    int i;
+    uint32_t *p = (uint32_t *)desc;
+
+    for (i = 0; i < sizeof(*desc); i += 4) {
+        *p = cpu_to_le32(*p);
+    }
+
+    dma_memory_write(s->dma, addr, desc, sizeof(*desc));
+}
+
+static void
+ftmac110_read_txdesc(Ftmac110State *s, hwaddr addr, Ftmac110TXD *desc)
+{
+    int i;
+    uint32_t *p = (uint32_t *)desc;
+
+    if (addr & 0x0f) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                 "ftmac110: Tx desc is not 16-byte aligned!\n"
+                 "It's fine in QEMU but the real HW would panic.\n");
+    }
+
+    dma_memory_read(s->dma, addr, desc, sizeof(*desc));
+
+    for (i = 0; i < sizeof(*desc); i += 4) {
+        *p = le32_to_cpu(*p);
+    }
+
+    if (desc->buf & 0x1) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                 "ftmac110: tx buffer is not 16-bit aligned.\n");
+    }
+}
+
+static void
+ftmac110_write_txdesc(Ftmac110State *s, hwaddr addr, Ftmac110TXD *desc)
+{
+    int i;
+    uint32_t *p = (uint32_t *)desc;
+
+    for (i = 0; i < sizeof(*desc); i += 4) {
+        *p = cpu_to_le32(*p);
+    }
+
+    dma_memory_write(s->dma, addr, desc, sizeof(*desc));
+}
+
+static void ftmac110_update_irq(Ftmac110State *s)
+{
+    qemu_set_irq(s->irq, !!(MAC_REG32(s, REG_ISR) & MAC_REG32(s, REG_IMR)));
+}
+
+static int ftmac110_can_receive(NetClientState *nc)
+{
+    int ret = 0;
+    uint32_t val;
+    Ftmac110State *s = qemu_get_nic_opaque(nc);
+    Ftmac110RXD rxd;
+    hwaddr off = MAC_REG32(s, REG_RXBAR) + s->rx_idx * sizeof(rxd);
+
+    val = MAC_REG32(s, REG_MACCR);
+    if ((val & MACCR_RCV_EN) && (val & MACCR_RDMA_EN)) {
+        ftmac110_read_rxdesc(s, off, &rxd);
+        ret = rxd.owner;
+        if (!ret) {
+            qemu_mod_timer(s->qtimer, qemu_get_clock_ms(vm_clock) + 10);
+        }
+    }
+
+    return ret;
+}
+
+static ssize_t ftmac110_receive(NetClientState *nc,
+                                const uint8_t  *buf,
+                                size_t          size)
+{
+    const uint8_t *ptr = buf;
+    hwaddr off;
+    size_t len;
+    Ftmac110RXD rxd;
+    Ftmac110State *s = qemu_get_nic_opaque(nc);
+    int bcst, mcst, ftl, proto;
+
+    MAC_REG32(s, REG_RXPKT) += 1;
+
+    /*
+     * Check if it's a long frame. (CRC32 is excluded)
+     */
+    proto = (buf[12] << 8) | buf[13];
+    if (proto == 0x8100) {  /* 802.1Q VLAN */
+        ftl = (size > 1518) ? 1 : 0;
+    } else {
+        ftl = (size > 1514) ? 1 : 0;
+    }
+    if (ftl) {
+        MAC_REG32(s, REG_RXCRCFTL) = (MAC_REG32(s, REG_RXCRCFTL) + 1) & 0xffff;
+        DPRINTF("ftmac110_receive: frame too long, drop it\n");
+        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;
+        MAC_REG32(s, REG_RXBCST) += 1;
+        if (!(MAC_REG32(s, REG_MACCR) & MACCR_RCV_ALL)
+            && !(MAC_REG32(s, REG_MACCR) & MACCR_RX_BROADPKT)) {
+            DPRINTF("ftmac110_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;
+        MAC_REG32(s, REG_RXMCST) += 1;
+        if (!(MAC_REG32(s, REG_MACCR) & MACCR_RCV_ALL)
+            && !(MAC_REG32(s, REG_MACCR) & MACCR_RX_MULTIPKT)) {
+            int hash, id;
+            if (!(MAC_REG32(s, REG_MACCR) & MACCR_HT_MULTI_EN)) {
+                DPRINTF("ftmac110_receive: mcst filtered\n");
+                return -1;
+            }
+            hash = ftmac110_mcast_hash(6, buf);
+            id = (hash >= 32) ? 1 : 0;
+            if (!(MAC_REG32(s, REG_MHASH0 + id * 4) & BIT(hash % 32))) {
+                DPRINTF("ftmac110_receive: mcst filtered\n");
+                return -1;
+            }
+        }
+    } else {
+        mcst = 0;
+    }
+
+    /* check if the destination matches NIC mac address */
+    if (!(MAC_REG32(s, REG_MACCR) & MACCR_RCV_ALL) && !bcst && !mcst) {
+        if (memcmp(s->conf.macaddr.a, buf, 6)) {
+            return -1;
+        }
+    }
+
+    while (size > 0) {
+        off = MAC_REG32(s, REG_RXBAR) + s->rx_idx * sizeof(rxd);
+        ftmac110_read_rxdesc(s, off, &rxd);
+        if (!rxd.owner) {
+            MAC_REG32(s, REG_ISR) |= ISR_NORXBUF;
+            DPRINTF("ftmac110: out of rxd!?\n");
+            return -1;
+        }
+
+        if (ptr == buf) {
+            rxd.frs = 1;
+        } else {
+            rxd.frs = 0;
+        }
+
+        len = size > rxd.bufsz ? rxd.bufsz : size;
+        dma_memory_write(s->dma, 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 = 0;
+
+        /* write-back the rx descriptor */
+        ftmac110_write_rxdesc(s, off, &rxd);
+
+        if (rxd.end) {
+            s->rx_idx = 0;
+        } else {
+            s->rx_idx += 1;
+        }
+    }
+
+    /* update interrupt signal */
+    MAC_REG32(s, REG_ISR) |= ISR_RPKT_OK | ISR_RPKT_FINISH;
+    ftmac110_update_irq(s);
+
+    return (ssize_t)((uint32_t)ptr - (uint32_t)buf);
+}
+
+static void ftmac110_transmit(Ftmac110State *s, uint32_t bar, uint32_t *idx)
+{
+    hwaddr off;
+    uint8_t *buf;
+    int ftl, proto;
+    Ftmac110TXD txd;
+
+    if ((MAC_REG32(s, REG_MACCR) & (MACCR_XMT_EN | MACCR_XDMA_EN))
+            != (MACCR_XMT_EN | MACCR_XDMA_EN)) {
+        return;
+    }
+
+    do {
+        off = bar + (*idx) * sizeof(txd);
+        ftmac110_read_txdesc(s, off, &txd);
+        if (!txd.owner) {
+            MAC_REG32(s, REG_ISR) |= ISR_NOTXBUF;
+            break;
+        }
+        if (txd.fts) {
+            s->txbuff.len = 0;
+        }
+        if (txd.len + s->txbuff.len > sizeof(s->txbuff.buf)) {
+            fprintf(stderr, "ftmac110: tx buffer overflow!\n");
+            abort();
+        }
+        buf = s->txbuff.buf + s->txbuff.len;
+        dma_memory_read(s->dma, txd.buf, (uint8_t *)buf, txd.len);
+        s->txbuff.len += txd.len;
+        /* Check if it's a long frame. (CRC32 is excluded) */
+        proto = (s->txbuff.buf[12] << 8) | s->txbuff.buf[13];
+        if (proto == 0x8100) {
+            ftl = (s->txbuff.len > 1518) ? 1 : 0;
+        } else {
+            ftl = (s->txbuff.len > 1514) ? 1 : 0;
+        }
+        if (ftl) {
+            fprintf(stderr, "ftmac110_transmit: frame too long\n");
+            abort();
+        }
+        if (txd.lts) {
+            MAC_REG32(s, REG_TXPKT) += 1;
+            if (MAC_REG32(s, REG_MACCR) & MACCR_LOOP_EN) {
+                ftmac110_receive(qemu_get_queue(s->nic),
+                                 s->txbuff.buf,
+                                 s->txbuff.len);
+            } else {
+                qemu_send_packet(qemu_get_queue(s->nic),
+                                 s->txbuff.buf,
+                                 s->txbuff.len);
+            }
+        }
+        if (txd.end) {
+            *idx = 0;
+        } else {
+            *idx += 1;
+        }
+        if (txd.tx2fic) {
+            MAC_REG32(s, REG_ISR) |= ISR_XPKT_OK;
+        }
+        if (txd.txic) {
+            MAC_REG32(s, REG_ISR) |= ISR_XPKT_FINISH;
+        }
+        txd.owner = 0;
+        ftmac110_write_txdesc(s, off, &txd);
+    } while (1);
+}
+
+static void ftmac110_bh(void *opaque)
+{
+    Ftmac110State *s = FTMAC110(opaque);
+
+    if (MAC_REG32(s, REG_TXBAR)) {
+        ftmac110_transmit(s, MAC_REG32(s, REG_TXBAR), &s->tx_idx);
+    }
+
+    ftmac110_update_irq(s);
+}
+
+static void ftmac110_chip_reset(Ftmac110State *s)
+{
+    s->phycr_rd = false;
+    s->txbuff.len = 0;
+    s->tx_idx = 0;
+    s->rx_idx = 0;
+    memset(s->regs, 0, sizeof(s->regs));
+
+    MAC_REG32(s, REG_APTC) = 0x00000001; /* timing control */
+    MAC_REG32(s, REG_DBLAC) = 0x00022f72; /* dma burst, arbitration */
+    MAC_REG32(s, REG_REVR) = 0x00000700; /* rev. = 0.7.0 */
+    MAC_REG32(s, REG_FEAR) = 0x00000006; /* support full/half duplex */
+
+    if (s->bh) {
+        qemu_bh_cancel(s->bh);
+    }
+
+    if (s->qtimer) {
+        qemu_del_timer(s->qtimer);
+    }
+
+    ftmac110_update_irq(s);
+}
+
+static uint64_t
+ftmac110_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    Ftmac110State *s = FTMAC110(opaque);
+    uint32_t dev, reg, ret = 0;
+
+    if (addr > REG_RXCRCFTL) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftmac110: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        return ret;
+    }
+
+    switch (addr) {
+    case REG_ISR:
+        ret = MAC_REG32(s, REG_ISR);
+        MAC_REG32(s, REG_ISR) = 0;
+        ftmac110_update_irq(s);
+        break;
+    case REG_IMR:
+        return MAC_REG32(s, REG_IMR);
+    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_MACSR:
+        ret = MAC_REG32(s, REG_MACSR);
+        MAC_REG32(s, REG_MACSR) = 0;
+        break;
+    case REG_PHYCR:
+        dev = extract32(MAC_REG32(s, REG_PHYCR), 16, 5);
+        reg = extract32(MAC_REG32(s, REG_PHYCR), 21, 5);
+        if (!dev && s->phycr_rd) {
+            /* Emulating a Davicom PHY with 100Mbps link state */
+            switch (reg) {
+            case 0:    /* PHY control register */
+                return MAC_REG32(s, REG_PHYCR) | 0x3100;
+            case 1:    /* PHY status register */
+                return MAC_REG32(s, REG_PHYCR) | 0x786d;
+            case 2:    /* PHY ID 1 register */
+                return MAC_REG32(s, REG_PHYCR) | 0x0181;
+            case 3:    /* PHY ID 2 register */
+                return MAC_REG32(s, REG_PHYCR) | 0xb8a0;
+            case 4:    /* Autonegotiation advertisement register */
+                return MAC_REG32(s, REG_PHYCR) | 0x01e1;
+            case 5:    /* Autonegotiation partner abilities register */
+                return MAC_REG32(s, REG_PHYCR) | 0x45e1;
+            }
+        }
+        break;
+    default:
+        ret = s->regs[addr / 4];
+        break;
+    }
+
+    return ret;
+}
+
+static void
+ftmac110_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    Ftmac110State *s = FTMAC110(opaque);
+
+    if (addr > REG_BPR) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftmac110: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        return;
+    }
+
+    switch (addr) {
+    case REG_IMR:
+        MAC_REG32(s, REG_IMR) = (uint32_t)val;
+        ftmac110_update_irq(s);
+        break;
+    case REG_HMAC:
+        s->conf.macaddr.a[1] = extract32((uint32_t)val, 0, 8);
+        s->conf.macaddr.a[0] = extract32((uint32_t)val, 8, 8);
+        break;
+    case REG_LMAC:
+        s->conf.macaddr.a[5] = extract32((uint32_t)val, 0, 8);
+        s->conf.macaddr.a[4] = extract32((uint32_t)val, 8, 8);
+        s->conf.macaddr.a[3] = extract32((uint32_t)val, 16, 8);
+        s->conf.macaddr.a[2] = extract32((uint32_t)val, 24, 8);
+        break;
+    case REG_MACCR:
+        MAC_REG32(s, REG_MACCR) = (uint32_t)val;
+        if (val & MACCR_SW_RST) {
+            ftmac110_chip_reset(s);
+            MAC_REG32(s, REG_MACCR) &= ~MACCR_SW_RST;
+        }
+        if ((val & MACCR_RCV_EN) && (val & MACCR_RDMA_EN)) {
+            if (ftmac110_can_receive(qemu_get_queue(s->nic))) {
+                qemu_flush_queued_packets(qemu_get_queue(s->nic));
+            }
+        } else {
+            qemu_del_timer(s->qtimer);
+        }
+        break;
+    case REG_PHYCR:
+        s->phycr_rd = (val & PHYCR_MDIORD) ? true : false;
+        MAC_REG32(s, REG_PHYCR) = (uint32_t)val
+                                & ~(PHYCR_MDIOWR | PHYCR_MDIORD);
+        break;
+    case REG_TXPD:
+        qemu_bh_schedule(s->bh);
+        break;
+    case REG_RXPD:
+    case REG_REVR:
+    case REG_FEAR:
+        break;
+    default:
+        s->regs[addr / 4] = (uint32_t)val;
+        break;
+    }
+}
+
+static const MemoryRegionOps mmio_ops = {
+    .read  = ftmac110_mem_read,
+    .write = ftmac110_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void ftmac110_cleanup(NetClientState *nc)
+{
+    Ftmac110State *s = qemu_get_nic_opaque(nc);
+
+    s->nic = NULL;
+}
+
+static NetClientInfo net_ftmac110_info = {
+    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .size = sizeof(NICState),
+    .can_receive = ftmac110_can_receive,
+    .receive = ftmac110_receive,
+    .cleanup = ftmac110_cleanup,
+};
+
+static void ftmac110_reset(DeviceState *ds)
+{
+    Ftmac110State *s = FTMAC110(SYS_BUS_DEVICE(ds));
+
+    ftmac110_chip_reset(s);
+}
+
+static void ftmac110_watchdog(void *opaque)
+{
+    Ftmac110State *s = FTMAC110(opaque);
+
+    if (ftmac110_can_receive(qemu_get_queue(s->nic))) {
+        qemu_flush_queued_packets(qemu_get_queue(s->nic));
+    }
+}
+
+static void ftmac110_realize(DeviceState *dev, Error **errp)
+{
+    Ftmac110State *s = FTMAC110(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+    memory_region_init_io(&s->mmio, &mmio_ops, s, TYPE_FTMAC110, 0x1000);
+    sysbus_init_mmio(sbd, &s->mmio);
+    sysbus_init_irq(sbd, &s->irq);
+
+    qemu_macaddr_default_if_unset(&s->conf.macaddr);
+    s->nic = qemu_new_nic(&net_ftmac110_info,
+                          &s->conf,
+                          object_get_typename(OBJECT(dev)),
+                          sbd->qdev.id,
+                          s);
+    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+
+    s->qtimer = qemu_new_timer_ms(vm_clock, ftmac110_watchdog, s);
+    s->dma = &dma_context_memory;
+    s->bh = qemu_bh_new(ftmac110_bh, s);
+
+    ftmac110_chip_reset(s);
+}
+
+static const VMStateDescription vmstate_ftmac110 = {
+    .name = TYPE_FTMAC110,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, Ftmac110State, CFG_REGSIZE),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property ftmac110_properties[] = {
+    DEFINE_NIC_PROPERTIES(Ftmac110State, conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void ftmac110_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd  = &vmstate_ftmac110;
+    dc->props = ftmac110_properties;
+    dc->reset = ftmac110_reset;
+    dc->realize = ftmac110_realize;
+    dc->no_user = 1;
+}
+
+static const TypeInfo ftmac110_info = {
+    .name           = TYPE_FTMAC110,
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(Ftmac110State),
+    .class_init     = ftmac110_class_init,
+};
+
+static void ftmac110_register_types(void)
+{
+    type_register_static(&ftmac110_info);
+}
+
+/* Legacy helper function.  Should go away when machine config files are
+   implemented.  */
+void ftmac110_init(NICInfo *nd, uint32_t base, qemu_irq irq)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+
+    qemu_check_nic_model(nd, TYPE_FTMAC110);
+    dev = qdev_create(NULL, TYPE_FTMAC110);
+    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(ftmac110_register_types)
diff --git a/hw/ftmac110.h b/hw/ftmac110.h
new file mode 100644
index 0000000..710d32d
--- /dev/null
+++ b/hw/ftmac110.h
@@ -0,0 +1,166 @@
+/*
+ * QEMU model of the FTMAC110 Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+#ifndef HW_ARM_FTMAC110_H
+#define HW_ARM_FTMAC110_H
+
+#define REG_ISR             0x00    /* interrupt status */
+#define REG_IMR             0x04    /* interrupt mask */
+#define REG_HMAC            0x08    /* mac address */
+#define REG_LMAC            0x0c
+#define REG_MHASH0          0x10    /* multicast hash table */
+#define REG_MHASH1          0x14
+#define REG_TXPD            0x18    /* kick tx dma */
+#define REG_RXPD            0x1c    /* kick rx dma */
+#define REG_TXBAR           0x20    /* tx list/ring base address */
+#define REG_RXBAR           0x24    /* rx list/ring base address */
+#define REG_ITC             0x28    /* interrupt timer control */
+#define REG_APTC            0x2C    /* auto polling timer control */
+#define REG_DBLAC           0x30    /* dma burst length, arbitration control */
+#define REG_REVR            0x34    /* revision */
+#define REG_FEAR            0x38    /* feature */
+
+#define REG_MACCR           0x88    /* mac control register */
+#define REG_MACSR           0x8C    /* mac status register */
+#define REG_PHYCR           0x90    /* phy control register */
+#define REG_PHYDR           0x94    /* phy data register */
+#define REG_FCR             0x98    /* flow control register */
+#define REG_BPR             0x9c    /* back pressure register */
+
+#define REG_TXPKT           0xf8    /* tx packet counter */
+#define REG_RXPKT           0xf4    /* rx packet counter */
+#define REG_RXBCST          0xec    /* rx broadcast packet counter */
+#define REG_RXMCST          0xf0    /* rx multicast packet counter */
+#define REG_RXRUNT          0xe0    /* rx err: runt */
+#define REG_RXCRCFTL        0xc4    /* rx err: BIT[31-16]#crc; BIT[15-0]#ftl */
+
+/* interrupt status register */
+#define ISR_PHYSTS_CHG      (1UL<<9)/* phy status change */
+#define ISR_AHB_ERR         (1UL<<8)/* AHB error */
+#define ISR_RPKT_LOST       (1UL<<7)/* rx packet lost */
+#define ISR_RPKT_OK         (1UL<<6)/* rx packet ok (fifo) */
+#define ISR_XPKT_LOST       (1UL<<5)/* tx packet lost */
+#define ISR_XPKT_OK         (1UL<<4)/* tx packet ok (fifo) */
+#define ISR_NOTXBUF         (1UL<<3)/* out of tx buffer */
+#define ISR_XPKT_FINISH     (1UL<<2)/* tx packet finished (phy) */
+#define ISR_NORXBUF         (1UL<<1)/* out of rx buffer */
+#define ISR_RPKT_FINISH     (1UL<<0)/* rx packet finished (ram) */
+
+/* MAC control register */
+#define MACCR_100M              (1UL<<18)/* 100Mbps */
+#define MACCR_RX_BROADPKT       (1UL<<17)/* recv all broadcast packets */
+#define MACCR_RX_MULTIPKT       (1UL<<16)/* recv all multicast packets */
+#define MACCR_FULLDUP           (1UL<<15)/* full duplex */
+#define MACCR_CRC_APD           (1UL<<14)/* crc append */
+#define MACCR_RCV_ALL           (1UL<<12)/* recv all packets */
+#define MACCR_RX_FTL            (1UL<<11)/* recv ftl packets */
+#define MACCR_RX_RUNT           (1UL<<10)/* recv runt packets */
+#define MACCR_HT_MULTI_EN       (1UL<<9) /* recv multicast by hash */
+#define MACCR_RCV_EN            (1UL<<8) /* rx enabled */
+#define MACCR_ENRX_IN_HALFTX    (1UL<<6) /* rx while tx in half duplex */
+#define MACCR_XMT_EN            (1UL<<5) /* tx enabled */
+#define MACCR_CRC_DIS           (1UL<<4) /* discard crc erro */
+#define MACCR_LOOP_EN           (1UL<<3) /* loopback */
+#define MACCR_SW_RST            (1UL<<2) /* software reset */
+#define MACCR_RDMA_EN           (1UL<<1) /* rx dma enabled */
+#define MACCR_XDMA_EN           (1UL<<0) /* tx dma enabled */
+
+/*
+ * MDIO
+ */
+#define PHYCR_MDIOWR            (1 << 27)/* mdio write */
+#define PHYCR_MDIORD            (1 << 26)/* mdio read */
+
+/*
+ * Tx/Rx descriptors
+ */
+typedef struct Ftmac110RXD {
+    /* RXDES0 */
+#ifdef HOST_WORDS_BIGENDIAN
+    uint32_t owner:1;   /* BIT31: owner - 1:HW, 0: SW */
+    uint32_t rsvd3:1;
+    uint32_t frs:1;     /* first rx segment */
+    uint32_t lrs:1;     /* last rx segment */
+    uint32_t rsvd2:5;
+    uint32_t error:5;   /* rx error */
+    uint32_t bcast:1;   /* broadcast */
+    uint32_t mcast:1;   /* multicast */
+    uint32_t rsvd1:5;
+    uint32_t len:11;    /* received packet length */
+#else
+    uint32_t len:11;
+    uint32_t rsvd1:5;
+    uint32_t mcast:1;
+    uint32_t bcast:1;
+    uint32_t error:5;
+    uint32_t rsvd2:5;
+    uint32_t lrs:1;
+    uint32_t frs:1;
+    uint32_t rsvd3:1;
+    uint32_t owner:1;
+#endif  /* #ifdef HOST_WORDS_BIGENDIAN */
+
+    /* RXDES1 */
+#ifdef HOST_WORDS_BIGENDIAN
+    uint32_t end:1;     /* end of descriptor list/ring */
+    uint32_t rsvd4:20;
+    uint32_t bufsz:11;  /* max. packet length */
+#else
+    uint32_t bufsz:11;
+    uint32_t rsvd4:20;
+    uint32_t end:1;
+#endif  /* #ifdef HOST_WORDS_BIGENDIAN */
+
+    /* RXDES2 */
+    uint32_t buf;
+
+    /* RXDES3 */
+    void     *skb;
+} __attribute__ ((aligned (16))) Ftmac110RXD;
+
+typedef struct Ftmac110TXD {
+    /* TXDES0 */
+#ifdef HOST_WORDS_BIGENDIAN
+    uint32_t owner:1;   /* BIT31: owner - 1:HW, 0: SW */
+    uint32_t rsvd1:29;
+    uint32_t error:2;
+#else
+    uint32_t error:2;
+    uint32_t rsvd1:29;
+    uint32_t owner:1;
+#endif  /* #ifdef HOST_WORDS_BIGENDIAN */
+
+    /* TXDES1 */
+#ifdef HOST_WORDS_BIGENDIAN
+    uint32_t end:1;     /* end of descriptor list/ring */
+    uint32_t txic:1;    /* interrupt when data has been copied to phy */
+    uint32_t tx2fic:1;  /* interrupt when data has been copied to fifo */
+    uint32_t fts:1;     /* first tx segment */
+    uint32_t lts:1;     /* last tx segment */
+    uint32_t rsvd2:16;
+    uint32_t len:11;    /* packet length */
+#else
+    uint32_t len:11;
+    uint32_t rsvd2:16;
+    uint32_t lts:1;
+    uint32_t fts:1;
+    uint32_t tx2fic:1;
+    uint32_t txic:1;
+    uint32_t end:1;
+#endif  /* #ifdef HOST_WORDS_BIGENDIAN */
+
+    /* TXDES2 */
+    uint32_t buf;
+
+    /* TXDES3 */
+    void     *skb;
+
+} __attribute__ ((aligned (16))) Ftmac110TXD;
+
+#endif  /* HW_ARM_FTMAC110_H */
-- 
1.7.9.5

  parent reply	other threads:[~2013-03-25 12:11 UTC|newest]

Thread overview: 35+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-03-25 12:09 [Qemu-devel] [PATCH v9 00/24] hw/arm: add Faraday A369 SoC platform support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 01/24] target-arm: add Faraday ARMv5TE processors support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 02/24] hw/arm: add Faraday a369 SoC platform support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 03/24] hw/arm: add FTINTC020 interrupt controller support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 04/24] hw/arm: add FTAHBC020 AHB " Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 05/24] hw/arm: add FTDDRII030 DDRII " Kuo-Jung Su
2013-03-28  0:09   ` Peter Crosthwaite
2013-03-28  3:24     ` Kuo-Jung Su
2013-03-28  3:58       ` Peter Crosthwaite
2013-03-28  5:28         ` Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 06/24] hw/arm: add FTPWMTMR010 timer support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 07/24] hw/arm: add FTWDT010 watchdog " Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 08/24] hw/arm: add FTRTC011 RTC " Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 09/24] tests: add QTest for FTRTC011 Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 10/24] hw/arm: add FTDMAC020 AHB DMA support Kuo-Jung Su
2013-03-29  0:22   ` Peter Crosthwaite
2013-03-29  7:23     ` Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 11/24] hw/arm: add FTAPBBRG020 APB " Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 12/24] hw/arm: add FTNANDC021 nand flash controller support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 13/24] hw/arm: add FTI2C010 I2C " Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 14/24] hw: Add AudioCodecClass for wm87xx audio class abstration Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 15/24] hw: add WM8731 audio codec support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 16/24] The FTSSP010 is a multi-function synchronous serial port interface controller which supports SSP, SPI, I2S, AC97 and SPDIF Kuo-Jung Su
2013-03-31  2:39   ` Peter Crosthwaite
2013-04-01  1:18     ` Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 17/24] qemu/bitops.h: add the bit ordering reversal functions Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 18/24] hw/arm: add FTGMAC100 1Gbps ethernet support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 19/24] hw/arm: add FTLCDC200 LCD controller support Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 20/24] hw/arm: add FTTSC010 touchscreen " Kuo-Jung Su
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 21/24] hw/arm: add FTSDC010 MMC/SD " Kuo-Jung Su
2013-03-25 12:09 ` Kuo-Jung Su [this message]
2013-03-25 12:09 ` [Qemu-devel] [PATCH v9 23/24] hw/arm: add FTTMR010 timer support Kuo-Jung Su
2013-03-25 12:10 ` [Qemu-devel] [PATCH v9 24/24] hw/arm: add FTSPI020 SPI flash controller support Kuo-Jung Su
2013-03-29  0:02   ` Peter Crosthwaite
2013-03-29  7:15     ` Kuo-Jung Su

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1364213400-10266-23-git-send-email-dantesu@gmail.com \
    --to=dantesu@gmail.com \
    --cc=afaerber@suse.de \
    --cc=blauwirbel@gmail.com \
    --cc=dantesu@faraday-tech.com \
    --cc=fred.konrad@greensocs.com \
    --cc=i.mitsyanko@samsung.com \
    --cc=paul@codesourcery.com \
    --cc=peter.maydell@linaro.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.