diff mbox

[8/9] Add xgmac ethernet model

Message ID 4EF0DEBD.70406@calxeda.com
State New
Headers show

Commit Message

Mark Langsdorf Dec. 20, 2011, 7:15 p.m. UTC
This adds very basic support for xgmac block. Missing things include:

- statistics counters
- WoL support
- rx checksum offload
- chained descriptors (only linear descriptor ring)
- broadcast and multicast handling

Signed-off-by: Rob Herring <rob.herring@calxeda.com>
Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com>
---
  Makefile.target |    1 +
  hw/xgmac.c      |  409 
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2 files changed, 410 insertions(+), 0 deletions(-)
  create mode 100644 hw/xgmac.c

+    ret = size;
+
+out:
+    enet_update_irq(s);
+    return ret;
+}
+
+static void eth_cleanup(VLANClientState *nc)
+{
+    struct XgmacEnet *s = DO_UPCAST(NICState, nc, nc)->opaque;
+    g_free(s);
+}
+
+static NetClientInfo net_xgmac_enet_info = {
+    .type = NET_CLIENT_TYPE_NIC,
+    .size = sizeof(NICState),
+    .can_receive = eth_can_rx,
+    .receive = eth_rx,
+    .cleanup = eth_cleanup,
+};
+
+static int xgmac_enet_init(SysBusDevice *dev)
+{
+    struct XgmacEnet *s = FROM_SYSBUS(typeof(*s), dev);
+
+    memory_region_init_io(&s->iomem, &enet_mem_ops, s, "xgmac", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    sysbus_init_irq(dev, &s->sbd_irq);
+    sysbus_init_irq(dev, &s->pmt_irq);
+    sysbus_init_irq(dev, &s->mci_irq);
+
+    qemu_macaddr_default_if_unset(&s->conf.macaddr);
+    s->nic = qemu_new_nic(&net_xgmac_enet_info, &s->conf,
+                          dev->qdev.info->name, dev->qdev.id, s);
+    qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+
+    s->regs[XGMAC_ADDR_HIGH(0)] = (s->conf.macaddr.a[5] << 8) |
+                                   s->conf.macaddr.a[4];
+    s->regs[XGMAC_ADDR_LOW(0)] = (s->conf.macaddr.a[3] << 24) |
+                                 (s->conf.macaddr.a[2] << 16) |
+                                 (s->conf.macaddr.a[1] << 8) |
+                                  s->conf.macaddr.a[0];
+
+    return 0;
+}
+
+static SysBusDeviceInfo xgmac_enet_info = {
+    .init = xgmac_enet_init,
+    .qdev.name  = "xgmac",
+    .qdev.size  = sizeof(struct XgmacEnet),
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_UINT32("phyaddr", struct XgmacEnet, c_phyaddr, 7),
+        DEFINE_NIC_PROPERTIES(struct XgmacEnet, conf),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+static void xgmac_enet_register(void)
+{
+    sysbus_register_withprop(&xgmac_enet_info);
+}
+
+device_init(xgmac_enet_register)

Comments

Peter Maydell Dec. 20, 2011, 8:24 p.m. UTC | #1
On 20 December 2011 19:15, Mark Langsdorf <mark.langsdorf@calxeda.com> wrote:
> This adds very basic support for xgmac block. Missing things include:
>
> - statistics counters
> - WoL support
> - rx checksum offload
> - chained descriptors (only linear descriptor ring)
> - broadcast and multicast handling

So, er, what's an xgmac? Any public documentation? Are you planning
to submit a board model that uses this?

Missing save/load support.

> Signed-off-by: Rob Herring <rob.herring@calxeda.com>
> Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com>
> ---
>  Makefile.target |    1 +
>  hw/xgmac.c      |  409
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++

Your mailer's linewrapping has damaged this patch too.

>  2 files changed, 410 insertions(+), 0 deletions(-)
>  create mode 100644 hw/xgmac.c
>
> diff --git a/Makefile.target b/Makefile.target
> index e4132d6..85f00a4 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -339,6 +339,7 @@ obj-arm-y += realview_gic.o realview.o arm_sysctl.o
> arm11mpcore.o a9mpcore.o arm
>  obj-arm-y += arm_mptimer.o
>  obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
>  obj-arm-y += pl061.o
> +obj-arm-y += xgmac.o
>  obj-arm-y += arm-semi.o
>  obj-arm-y += pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o
> pxa2xx_dma.o
>  obj-arm-y += pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o
> diff --git a/hw/xgmac.c b/hw/xgmac.c
> new file mode 100644
> index 0000000..621be5d
> --- /dev/null
> +++ b/hw/xgmac.c
> @@ -0,0 +1,409 @@
> +/*
> + * QEMU model of XGMAC Ethernet.
> + *
> + * derived from the Xilinx AXI-Ethernet by Edgar E. Iglesias.
> + *
> + * Copyright (c) 2011 Calxeda, 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 "qemu-char.h"
> +#include "qemu-log.h"
> +#include "net.h"
> +#include "net/checksum.h"
> +
> +#define XGMAC_CONTROL                 0x00000000        /* MAC
> Configuration */
> +#define XGMAC_FRAME_FILTER            0x00000001        /* MAC Frame Filter
> */
> +#define XGMAC_FLOW_CTRL               0x00000006        /* MAC Flow Control
> */
> +#define XGMAC_VLAN_TAG                0x00000007        /* VLAN Tags */
> +#define XGMAC_VERSION                 0x00000008        /* Version */
> +#define XGMAC_VLAN_INCL               0x00000009        /* VLAN tag for
> insertion or replacement into tx frames */
> +#define XGMAC_LPI_CTRL                0x0000000a        /* LPI Control and
> Status */
> +#define XGMAC_LPI_TIMER               0x0000000b        /* LPI Timers
> Control */
> +#define XGMAC_TX_PACE                 0x0000000c        /* Transmit Pace
> and Stretch */
> +#define XGMAC_VLAN_HASH               0x0000000d        /* VLAN Hash Table
> */
> +#define XGMAC_DEBUG                   0x0000000e        /* Debug */
> +#define XGMAC_INT_STATUS              0x0000000f        /* Interrupt and
> Control */
> +#define XGMAC_HASH(n)                 ((0x00000300/4) + (n))        /* HASH
> table registers */
> +#define XGMAC_NUM_HASH                16
> +#define XGMAC_OPMODE                  (0x00000400/4)    /* Operation Mode
> */
> +#define XGMAC_REMOTE_WAKE             (0x00000700/4)    /* Remote Wake-Up
> Frame Filter */
> +#define XGMAC_PMT                     (0x00000704/4)    /* PMT Control and
> Status */
> +
> +#define XGMAC_ADDR_HIGH(reg)          (0x00000010+((reg) * 2))
> +#define XGMAC_ADDR_LOW(reg)           (0x00000011+((reg) * 2))
> +
> +#define DMA_BUS_MODE                  0x000003c0        /* Bus Mode */
> +#define DMA_XMT_POLL_DEMAND           0x000003c1        /* Transmit Poll
> Demand */
> +#define DMA_RCV_POLL_DEMAND           0x000003c2        /* Received Poll
> Demand */
> +#define DMA_RCV_BASE_ADDR             0x000003c3        /* Receive List
> Base */
> +#define DMA_TX_BASE_ADDR              0x000003c4        /* Transmit List
> Base */
> +#define DMA_STATUS                    0x000003c5        /* Status Register
> */
> +#define DMA_CONTROL                   0x000003c6        /* Ctrl
> (Operational Mode) */
> +#define DMA_INTR_ENA                  0x000003c7        /* Interrupt Enable
> */
> +#define DMA_MISSED_FRAME_CTR          0x000003c8        /* Missed Frame
> Counter */
> +#define DMA_RI_WATCHDOG_TIMER         0x000003c9        /* Receive
> Interrupt Watchdog Timer */
> +#define DMA_AXI_BUS                   0x000003ca        /* AXI Bus Mode */
> +#define DMA_AXI_STATUS                0x000003cb        /* AXI Status */
> +#define DMA_CUR_TX_DESC_ADDR          0x000003d2        /* Current Host Tx
> Descriptor */
> +#define DMA_CUR_RX_DESC_ADDR          0x000003d3        /* Current Host Rx
> Descriptor */
> +#define DMA_CUR_TX_BUF_ADDR           0x000003d4        /* Current Host Tx
> Buffer */
> +#define DMA_CUR_RX_BUF_ADDR           0x000003d5        /* Current Host Rx
> Buffer */
> +#define DMA_HW_FEATURE                0x000003d6        /* Enabled Hardware
> Features */
> +
> +/* DMA Status register defines */
> +#define DMA_STATUS_GMI                0x08000000        /* MMC interrupt */
> +#define DMA_STATUS_GLI                0x04000000        /* GMAC Line
> interface int */
> +#define DMA_STATUS_EB_MASK            0x00380000        /* Error Bits Mask
> */
> +#define DMA_STATUS_EB_TX_ABORT        0x00080000        /* Error Bits - TX
> Abort */
> +#define DMA_STATUS_EB_RX_ABORT        0x00100000        /* Error Bits - RX
> Abort */
> +#define DMA_STATUS_TS_MASK            0x00700000        /* Transmit Process
> State */
> +#define DMA_STATUS_TS_SHIFT           20
> +#define DMA_STATUS_RS_MASK            0x000e0000        /* Receive Process
> State */
> +#define DMA_STATUS_RS_SHIFT           17
> +#define DMA_STATUS_NIS                0x00010000        /* Normal Interrupt
> Summary */
> +#define DMA_STATUS_AIS                0x00008000        /* Abnormal
> Interrupt Summary */
> +#define DMA_STATUS_ERI                0x00004000        /* Early Receive
> Interrupt */
> +#define DMA_STATUS_FBI                0x00002000        /* Fatal Bus Error
> Interrupt */
> +#define DMA_STATUS_ETI                0x00000400        /* Early Transmit
> Interrupt */
> +#define DMA_STATUS_RWT                0x00000200        /* Receive Watchdog
> Timeout */
> +#define DMA_STATUS_RPS                0x00000100        /* Receive Process
> Stopped */
> +#define DMA_STATUS_RU                 0x00000080        /* Receive Buffer
> Unavailable */
> +#define DMA_STATUS_RI                 0x00000040        /* Receive
> Interrupt */
> +#define DMA_STATUS_UNF                0x00000020        /* Transmit
> Underflow */
> +#define DMA_STATUS_OVF                0x00000010        /* Receive Overflow
> */
> +#define DMA_STATUS_TJT                0x00000008        /* Transmit Jabber
> Timeout */
> +#define DMA_STATUS_TU                 0x00000004        /* Transmit Buffer
> Unavailable */
> +#define DMA_STATUS_TPS                0x00000002        /* Transmit Process
> Stopped */
> +#define DMA_STATUS_TI                 0x00000001        /* Transmit
> Interrupt */
> +
> +/* DMA Control register defines */
> +#define DMA_CONTROL_ST                0x00002000        /* Start/Stop
> Transmission */
> +#define DMA_CONTROL_SR                0x00000002        /* Start/Stop
> Receive */
> +#define DMA_CONTROL_DFF               0x01000000        /* Disable flush of
> rx frames */
> +
> +struct desc {
> +    uint32_t ctl_stat;
> +    uint16_t buffer1_size;
> +    uint16_t buffer2_size;
> +    uint32_t buffer1_addr;
> +    uint32_t buffer2_addr;
> +    uint32_t ext_stat;
> +    uint32_t res[3];
> +};
> +
> +#define R_MAX 0x400
> +
> +struct XgmacEnet {
> +    SysBusDevice busdev;
> +    MemoryRegion iomem;
> +    qemu_irq sbd_irq;
> +    qemu_irq pmt_irq;
> +    qemu_irq mci_irq;
> +    void *dmach;
> +    NICState *nic;
> +    NICConf conf;
> +
> +    uint32_t c_rxmem;
> +    uint32_t c_txmem;
> +    uint32_t c_phyaddr;
> +
> +    struct {
> +        uint64_t rx_bytes;
> +        uint64_t tx_bytes;
> +
> +        uint64_t rx;
> +        uint64_t rx_bcast;
> +        uint64_t rx_mcast;
> +    } stats;
> +
> +    /* Receive configuration words.  */
> +    uint32_t rcw[2];
> +    /* Transmit config.  */
> +    uint32_t tc;
> +    uint32_t emmc;
> +    uint32_t phyc;
> +
> +    /* Unicast Address Word.  */
> +    uint32_t uaw[2];
> +    /* Unicast address filter used with extended mcast.  */
> +    uint32_t ext_uaw[2];
> +    uint32_t fmi;
> +
> +    uint32_t regs[R_MAX];
> +
> +    /* Multicast filter addrs.  */
> +    uint32_t maddr[4][2];
> +    /* 32K x 1 lookup filter.  */
> +    uint32_t ext_mtable[1024];
> +
> +
> +    uint8_t *rxmem;
> +};
> +
> +static void xgmac_read_desc(struct XgmacEnet *s, struct desc *d, int rx)
> +{
> +    uint32_t addr = rx ? s->regs[DMA_CUR_RX_DESC_ADDR] :
> +        s->regs[DMA_CUR_TX_DESC_ADDR];
> +    cpu_physical_memory_read(addr, d, sizeof(*d));
> +}
> +
> +static void xgmac_write_desc(struct XgmacEnet *s, struct desc *d, int rx)
> +{
> +    int reg = rx ? DMA_CUR_RX_DESC_ADDR : DMA_CUR_TX_DESC_ADDR;
> +    uint32_t addr = s->regs[reg];
> +
> +    if (!rx && (d->ctl_stat & 0x00200000)) {
> +        s->regs[reg] = s->regs[DMA_TX_BASE_ADDR];
> +    } else if (rx && (d->buffer1_size & 0x8000)) {
> +        s->regs[reg] = s->regs[DMA_RCV_BASE_ADDR];
> +    } else {
> +        s->regs[reg] += sizeof(*d);
> +    }
> +    cpu_physical_memory_write(addr, d, sizeof(*d));
> +}
> +
> +static void xgmac_enet_send(struct XgmacEnet *s)
> +{
> +    struct desc bd;
> +    int frame_size;
> +    int len;
> +    uint8_t frame[8192];
> +    uint8_t *ptr;
> +
> +    ptr = frame;
> +    frame_size = 0;
> +    while (1) {
> +        xgmac_read_desc(s, &bd, 0);
> +        if ((bd.ctl_stat & 0x80000000) == 0) {
> +            /* Run out of descriptors to transmit.  */
> +            break;
> +        }
> +        len = (bd.buffer1_size & 0xfff) + (bd.buffer2_size & 0xfff);
> +
> +        if ((bd.buffer1_size & 0xfff) > 2048) {
> +            fprintf(stdout, "qemu:%s:ERROR...ERROR...ERROR... -- "
> +                        "xgmac buffer 1 len on send > 2048 (0x%x)\n",
> +                         __func__, bd.buffer1_size & 0xfff);
> +            fflush(stdout);
> +        }
> +        if ((bd.buffer2_size & 0xfff) != 0) {
> +            fprintf(stdout, "qemu:%s:ERROR...ERROR...ERROR... -- "
> +                        "xgmac buffer 2 len on send != 0 (0x%x)\n",
> +                        __func__, bd.buffer2_size & 0xfff);
> +            fflush(stdout);
> +        }
> +        if (len >= sizeof(frame)) {
> +            fprintf(stdout, "qemu:%s: buffer overflow %d read into %zu "
> +                        "buffer\n" , __func__, len, sizeof(frame));
> +            fprintf(stdout, "qemu:%s: buffer1.size=%d; buffer2.size=%d\n",
> +                        __func__, bd.buffer1_size, bd.buffer2_size);
> +            fflush(stdout);
> +        }
> +
> +        cpu_physical_memory_read(bd.buffer1_addr, ptr, len);
> +        ptr += len;
> +        frame_size += len;
> +        if (bd.ctl_stat & 0x20000000) {
> +            /* Last buffer in frame.  */
> +            qemu_send_packet(&s->nic->nc, frame, len);
> +            ptr = frame;
> +            frame_size = 0;
> +            s->regs[DMA_STATUS] |= DMA_STATUS_TI | DMA_STATUS_NIS;
> +        }
> +        bd.ctl_stat &= ~0x80000000;
> +        /* Write back the modified descriptor.  */
> +        xgmac_write_desc(s, &bd, 0);
> +    }
> +}
> +
> +static void enet_update_irq(struct XgmacEnet *s)
> +{
> +    int stat = s->regs[DMA_STATUS] & s->regs[DMA_INTR_ENA];
> +    qemu_set_irq(s->sbd_irq, !!stat);
> +}
> +
> +static uint64_t enet_read(void *opaque, target_phys_addr_t addr, unsigned
> size)
> +{
> +    struct XgmacEnet *s = opaque;
> +    uint64_t r = 0;
> +    addr >>= 2;
> +
> +    switch (addr) {
> +    case XGMAC_VERSION:
> +        r = 0x1012;
> +        break;
> +    default:
> +        if (addr < ARRAY_SIZE(s->regs)) {
> +            r = s->regs[addr];
> +        }
> +        break;
> +    }
> +    return r;
> +}
> +
> +static void enet_write(void *opaque, target_phys_addr_t addr, uint64_t
> value, unsigned size)
> +{
> +    struct XgmacEnet *s = opaque;
> +
> +    addr >>= 2;
> +    switch (addr) {
> +    case DMA_BUS_MODE:
> +        s->regs[DMA_BUS_MODE] = value & ~0x1;
> +        break;
> +    case DMA_XMT_POLL_DEMAND:
> +        xgmac_enet_send(s);
> +        break;
> +    case DMA_STATUS:
> +        s->regs[DMA_STATUS] = s->regs[DMA_STATUS] & ~value;
> +        break;
> +    case DMA_RCV_BASE_ADDR:
> +        s->regs[DMA_RCV_BASE_ADDR] = s->regs[DMA_CUR_RX_DESC_ADDR] = value;
> +        break;
> +    case DMA_TX_BASE_ADDR:
> +        s->regs[DMA_TX_BASE_ADDR] = s->regs[DMA_CUR_TX_DESC_ADDR] = value;
> +        break;
> +    default:
> +        if (addr < ARRAY_SIZE(s->regs)) {
> +            s->regs[addr] = value;
> +        }
> +        break;
> +    }
> +    enet_update_irq(s);
> +}
> +
> +static const MemoryRegionOps enet_mem_ops = {
> +    .read = enet_read,
> +    .write = enet_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +};
> +
> +static int eth_can_rx(VLANClientState *nc)
> +{
> +    struct XgmacEnet *s = DO_UPCAST(NICState, nc, nc)->opaque;
> +
> +    /* RX enabled?  */
> +    return s->regs[DMA_CONTROL] & DMA_CONTROL_SR;
> +}
> +
> +static ssize_t eth_rx(VLANClientState *nc, const uint8_t *buf, size_t size)
> +{
> +    struct XgmacEnet *s = DO_UPCAST(NICState, nc, nc)->opaque;
> +    static const unsigned char sa_bcast[6] = {0xff, 0xff, 0xff,
> +                                              0xff, 0xff, 0xff};
> +    int unicast, broadcast, multicast;
> +    struct desc bd;
> +    ssize_t ret;
> +
> +    unicast = ~buf[0] & 0x1;
> +    broadcast = memcmp(buf, sa_bcast, 6) == 0;
> +    multicast = !unicast && !broadcast;
> +    if (size < 12) {
> +        s->regs[DMA_STATUS] |= DMA_STATUS_RI | DMA_STATUS_NIS;
> +        ret = -1;
> +        goto out;
> +    }
> +
> +    xgmac_read_desc(s, &bd, 1);
> +    if ((bd.ctl_stat & 0x80000000) == 0) {
> +        s->regs[DMA_STATUS] |= DMA_STATUS_RU | DMA_STATUS_AIS;
> +        ret = size;
> +        goto out;
> +    }
> +
> +    cpu_physical_memory_write(bd.buffer1_addr, buf, size);
> +
> +    /* Add in the 4 bytes for crc (the real hw returns length incl crc) */
> +    size += 4;
> +    bd.ctl_stat = (size << 16) | 0x300;
> +    xgmac_write_desc(s, &bd, 1);
> +
> +    s->stats.rx_bytes += size;
> +    s->stats.rx++;
> +    if (multicast) {
> +        s->stats.rx_mcast++;
> +    } else if (broadcast) {
> +        s->stats.rx_bcast++;
> +    }
> +
> +    s->regs[DMA_STATUS] |= DMA_STATUS_RI | DMA_STATUS_NIS;
> +    ret = size;
> +
> +out:
> +    enet_update_irq(s);
> +    return ret;
> +}
> +
> +static void eth_cleanup(VLANClientState *nc)
> +{
> +    struct XgmacEnet *s = DO_UPCAST(NICState, nc, nc)->opaque;
> +    g_free(s);
> +}

This looks suspicious: I don't think you should be freeing
your own device struct.

> +static NetClientInfo net_xgmac_enet_info = {
> +    .type = NET_CLIENT_TYPE_NIC,
> +    .size = sizeof(NICState),
> +    .can_receive = eth_can_rx,
> +    .receive = eth_rx,
> +    .cleanup = eth_cleanup,
> +};
> +
> +static int xgmac_enet_init(SysBusDevice *dev)
> +{
> +    struct XgmacEnet *s = FROM_SYSBUS(typeof(*s), dev);
> +
> +    memory_region_init_io(&s->iomem, &enet_mem_ops, s, "xgmac", 0x1000);
> +    sysbus_init_mmio(dev, &s->iomem);
> +    sysbus_init_irq(dev, &s->sbd_irq);
> +    sysbus_init_irq(dev, &s->pmt_irq);
> +    sysbus_init_irq(dev, &s->mci_irq);
> +
> +    qemu_macaddr_default_if_unset(&s->conf.macaddr);
> +    s->nic = qemu_new_nic(&net_xgmac_enet_info, &s->conf,
> +                          dev->qdev.info->name, dev->qdev.id, s);
> +    qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
> +
> +    s->regs[XGMAC_ADDR_HIGH(0)] = (s->conf.macaddr.a[5] << 8) |
> +                                   s->conf.macaddr.a[4];
> +    s->regs[XGMAC_ADDR_LOW(0)] = (s->conf.macaddr.a[3] << 24) |
> +                                 (s->conf.macaddr.a[2] << 16) |
> +                                 (s->conf.macaddr.a[1] << 8) |
> +                                  s->conf.macaddr.a[0];
> +
> +    return 0;
> +}
> +
> +static SysBusDeviceInfo xgmac_enet_info = {
> +    .init = xgmac_enet_init,
> +    .qdev.name  = "xgmac",
> +    .qdev.size  = sizeof(struct XgmacEnet),
> +    .qdev.props = (Property[]) {
> +        DEFINE_PROP_UINT32("phyaddr", struct XgmacEnet, c_phyaddr, 7),
> +        DEFINE_NIC_PROPERTIES(struct XgmacEnet, conf),
> +        DEFINE_PROP_END_OF_LIST(),
> +    }
> +};
> +static void xgmac_enet_register(void)
> +{
> +    sysbus_register_withprop(&xgmac_enet_info);
> +}
> +
> +device_init(xgmac_enet_register)
> --
> 1.7.5.4
>
Mark Langsdorf Dec. 21, 2011, 10:39 p.m. UTC | #2
On 12/20/2011 02:24 PM, Peter Maydell wrote:
> On 20 December 2011 19:15, Mark Langsdorf <mark.langsdorf@calxeda.com> wrote:
>> This adds very basic support for xgmac block. Missing things include:
>>
>> - statistics counters
>> - WoL support
>> - rx checksum offload
>> - chained descriptors (only linear descriptor ring)
>> - broadcast and multicast handling
> 
> So, er, what's an xgmac? Any public documentation? Are you planning
> to submit a board model that uses this?

It's a Synopis XG-Mac.

I have code for a board model that uses it, but it uses the pre-
MemoryRegion API including direct calls to
cpu_register_physical_memory() and qemu_ram_alloc(). I can't
submit it until I get it updated. Any suggestions for a good
example of converting a board model from one API to the other?

Thanks,
Mark Langsdorf
Calxeda, Inc.
Peter Maydell Dec. 28, 2011, 12:27 a.m. UTC | #3
On 21 December 2011 22:39, Mark Langsdorf <mark.langsdorf@calxeda.com> wrote:
> I have code for a board model that uses it, but it uses the pre-
> MemoryRegion API including direct calls to
> cpu_register_physical_memory() and qemu_ram_alloc(). I can't
> submit it until I get it updated. Any suggestions for a good
> example of converting a board model from one API to the other?

Just look for a board which does the same sort of things yours
does and then look for Avi's 'convert to memory API' patch in
its git commit log. Eg e6d17b056de for hw/vexpress.c.

-- PMM
diff mbox

Patch

diff --git a/Makefile.target b/Makefile.target
index e4132d6..85f00a4 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -339,6 +339,7 @@  obj-arm-y += realview_gic.o realview.o arm_sysctl.o 
arm11mpcore.o a9mpcore.o arm
  obj-arm-y += arm_mptimer.o
  obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
  obj-arm-y += pl061.o
+obj-arm-y += xgmac.o
  obj-arm-y += arm-semi.o
  obj-arm-y += pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o 
pxa2xx_dma.o
  obj-arm-y += pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o
diff --git a/hw/xgmac.c b/hw/xgmac.c
new file mode 100644
index 0000000..621be5d
--- /dev/null
+++ b/hw/xgmac.c
@@ -0,0 +1,409 @@ 
+/*
+ * QEMU model of XGMAC Ethernet.
+ *
+ * derived from the Xilinx AXI-Ethernet by Edgar E. Iglesias.
+ *
+ * Copyright (c) 2011 Calxeda, 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 "qemu-char.h"
+#include "qemu-log.h"
+#include "net.h"
+#include "net/checksum.h"
+
+#define XGMAC_CONTROL                 0x00000000        /* MAC 
Configuration */
+#define XGMAC_FRAME_FILTER            0x00000001        /* MAC Frame 
Filter */
+#define XGMAC_FLOW_CTRL               0x00000006        /* MAC Flow 
Control */
+#define XGMAC_VLAN_TAG                0x00000007        /* VLAN Tags */
+#define XGMAC_VERSION                 0x00000008        /* Version */
+#define XGMAC_VLAN_INCL               0x00000009        /* VLAN tag for 
insertion or replacement into tx frames */
+#define XGMAC_LPI_CTRL                0x0000000a        /* LPI Control 
and Status */
+#define XGMAC_LPI_TIMER               0x0000000b        /* LPI Timers 
Control */
+#define XGMAC_TX_PACE                 0x0000000c        /* Transmit 
Pace and Stretch */
+#define XGMAC_VLAN_HASH               0x0000000d        /* VLAN Hash 
Table */
+#define XGMAC_DEBUG                   0x0000000e        /* Debug */
+#define XGMAC_INT_STATUS              0x0000000f        /* Interrupt 
and Control */
+#define XGMAC_HASH(n)                 ((0x00000300/4) + (n))        /* 
HASH table registers */
+#define XGMAC_NUM_HASH                16
+#define XGMAC_OPMODE                  (0x00000400/4)    /* Operation 
Mode */
+#define XGMAC_REMOTE_WAKE             (0x00000700/4)    /* Remote 
Wake-Up Frame Filter */
+#define XGMAC_PMT                     (0x00000704/4)    /* PMT Control 
and Status */
+
+#define XGMAC_ADDR_HIGH(reg)          (0x00000010+((reg) * 2))
+#define XGMAC_ADDR_LOW(reg)           (0x00000011+((reg) * 2))
+
+#define DMA_BUS_MODE                  0x000003c0        /* Bus Mode */
+#define DMA_XMT_POLL_DEMAND           0x000003c1        /* Transmit 
Poll Demand */
+#define DMA_RCV_POLL_DEMAND           0x000003c2        /* Received 
Poll Demand */
+#define DMA_RCV_BASE_ADDR             0x000003c3        /* Receive List 
Base */
+#define DMA_TX_BASE_ADDR              0x000003c4        /* Transmit 
List Base */
+#define DMA_STATUS                    0x000003c5        /* Status 
Register */
+#define DMA_CONTROL                   0x000003c6        /* Ctrl 
(Operational Mode) */
+#define DMA_INTR_ENA                  0x000003c7        /* Interrupt 
Enable */
+#define DMA_MISSED_FRAME_CTR          0x000003c8        /* Missed Frame 
Counter */
+#define DMA_RI_WATCHDOG_TIMER         0x000003c9        /* Receive 
Interrupt Watchdog Timer */
+#define DMA_AXI_BUS                   0x000003ca        /* AXI Bus Mode */
+#define DMA_AXI_STATUS                0x000003cb        /* AXI Status */
+#define DMA_CUR_TX_DESC_ADDR          0x000003d2        /* Current Host 
Tx Descriptor */
+#define DMA_CUR_RX_DESC_ADDR          0x000003d3        /* Current Host 
Rx Descriptor */
+#define DMA_CUR_TX_BUF_ADDR           0x000003d4        /* Current Host 
Tx Buffer */
+#define DMA_CUR_RX_BUF_ADDR           0x000003d5        /* Current Host 
Rx Buffer */
+#define DMA_HW_FEATURE                0x000003d6        /* Enabled 
Hardware Features */
+
+/* DMA Status register defines */
+#define DMA_STATUS_GMI                0x08000000        /* MMC interrupt */
+#define DMA_STATUS_GLI                0x04000000        /* GMAC Line 
interface int */
+#define DMA_STATUS_EB_MASK            0x00380000        /* Error Bits 
Mask */
+#define DMA_STATUS_EB_TX_ABORT        0x00080000        /* Error Bits - 
TX Abort */
+#define DMA_STATUS_EB_RX_ABORT        0x00100000        /* Error Bits - 
RX Abort */
+#define DMA_STATUS_TS_MASK            0x00700000        /* Transmit 
Process State */
+#define DMA_STATUS_TS_SHIFT           20
+#define DMA_STATUS_RS_MASK            0x000e0000        /* Receive 
Process State */
+#define DMA_STATUS_RS_SHIFT           17
+#define DMA_STATUS_NIS                0x00010000        /* Normal 
Interrupt Summary */
+#define DMA_STATUS_AIS                0x00008000        /* Abnormal 
Interrupt Summary */
+#define DMA_STATUS_ERI                0x00004000        /* Early 
Receive Interrupt */
+#define DMA_STATUS_FBI                0x00002000        /* Fatal Bus 
Error Interrupt */
+#define DMA_STATUS_ETI                0x00000400        /* Early 
Transmit Interrupt */
+#define DMA_STATUS_RWT                0x00000200        /* Receive 
Watchdog Timeout */
+#define DMA_STATUS_RPS                0x00000100        /* Receive 
Process Stopped */
+#define DMA_STATUS_RU                 0x00000080        /* Receive 
Buffer Unavailable */
+#define DMA_STATUS_RI                 0x00000040        /* Receive 
Interrupt */
+#define DMA_STATUS_UNF                0x00000020        /* Transmit 
Underflow */
+#define DMA_STATUS_OVF                0x00000010        /* Receive 
Overflow */
+#define DMA_STATUS_TJT                0x00000008        /* Transmit 
Jabber Timeout */
+#define DMA_STATUS_TU                 0x00000004        /* Transmit 
Buffer Unavailable */
+#define DMA_STATUS_TPS                0x00000002        /* Transmit 
Process Stopped */
+#define DMA_STATUS_TI                 0x00000001        /* Transmit 
Interrupt */
+
+/* DMA Control register defines */
+#define DMA_CONTROL_ST                0x00002000        /* Start/Stop 
Transmission */
+#define DMA_CONTROL_SR                0x00000002        /* Start/Stop 
Receive */
+#define DMA_CONTROL_DFF               0x01000000        /* Disable 
flush of rx frames */
+
+struct desc {
+    uint32_t ctl_stat;
+    uint16_t buffer1_size;
+    uint16_t buffer2_size;
+    uint32_t buffer1_addr;
+    uint32_t buffer2_addr;
+    uint32_t ext_stat;
+    uint32_t res[3];
+};
+
+#define R_MAX 0x400
+
+struct XgmacEnet {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    qemu_irq sbd_irq;
+    qemu_irq pmt_irq;
+    qemu_irq mci_irq;
+    void *dmach;
+    NICState *nic;
+    NICConf conf;
+
+    uint32_t c_rxmem;
+    uint32_t c_txmem;
+    uint32_t c_phyaddr;
+
+    struct {
+        uint64_t rx_bytes;
+        uint64_t tx_bytes;
+
+        uint64_t rx;
+        uint64_t rx_bcast;
+        uint64_t rx_mcast;
+    } stats;
+
+    /* Receive configuration words.  */
+    uint32_t rcw[2];
+    /* Transmit config.  */
+    uint32_t tc;
+    uint32_t emmc;
+    uint32_t phyc;
+
+    /* Unicast Address Word.  */
+    uint32_t uaw[2];
+    /* Unicast address filter used with extended mcast.  */
+    uint32_t ext_uaw[2];
+    uint32_t fmi;
+
+    uint32_t regs[R_MAX];
+
+    /* Multicast filter addrs.  */
+    uint32_t maddr[4][2];
+    /* 32K x 1 lookup filter.  */
+    uint32_t ext_mtable[1024];
+
+
+    uint8_t *rxmem;
+};
+
+static void xgmac_read_desc(struct XgmacEnet *s, struct desc *d, int rx)
+{
+    uint32_t addr = rx ? s->regs[DMA_CUR_RX_DESC_ADDR] :
+        s->regs[DMA_CUR_TX_DESC_ADDR];
+    cpu_physical_memory_read(addr, d, sizeof(*d));
+}
+
+static void xgmac_write_desc(struct XgmacEnet *s, struct desc *d, int rx)
+{
+    int reg = rx ? DMA_CUR_RX_DESC_ADDR : DMA_CUR_TX_DESC_ADDR;
+    uint32_t addr = s->regs[reg];
+
+    if (!rx && (d->ctl_stat & 0x00200000)) {
+        s->regs[reg] = s->regs[DMA_TX_BASE_ADDR];
+    } else if (rx && (d->buffer1_size & 0x8000)) {
+        s->regs[reg] = s->regs[DMA_RCV_BASE_ADDR];
+    } else {
+        s->regs[reg] += sizeof(*d);
+    }
+    cpu_physical_memory_write(addr, d, sizeof(*d));
+}
+
+static void xgmac_enet_send(struct XgmacEnet *s)
+{
+    struct desc bd;
+    int frame_size;
+    int len;
+    uint8_t frame[8192];
+    uint8_t *ptr;
+
+    ptr = frame;
+    frame_size = 0;
+    while (1) {
+        xgmac_read_desc(s, &bd, 0);
+        if ((bd.ctl_stat & 0x80000000) == 0) {
+            /* Run out of descriptors to transmit.  */
+            break;
+        }
+        len = (bd.buffer1_size & 0xfff) + (bd.buffer2_size & 0xfff);
+
+        if ((bd.buffer1_size & 0xfff) > 2048) {
+            fprintf(stdout, "qemu:%s:ERROR...ERROR...ERROR... -- "
+                        "xgmac buffer 1 len on send > 2048 (0x%x)\n",
+                         __func__, bd.buffer1_size & 0xfff);
+            fflush(stdout);
+        }
+        if ((bd.buffer2_size & 0xfff) != 0) {
+            fprintf(stdout, "qemu:%s:ERROR...ERROR...ERROR... -- "
+                        "xgmac buffer 2 len on send != 0 (0x%x)\n",
+                        __func__, bd.buffer2_size & 0xfff);
+            fflush(stdout);
+        }
+        if (len >= sizeof(frame)) {
+            fprintf(stdout, "qemu:%s: buffer overflow %d read into %zu "
+                        "buffer\n" , __func__, len, sizeof(frame));
+            fprintf(stdout, "qemu:%s: buffer1.size=%d; buffer2.size=%d\n",
+                        __func__, bd.buffer1_size, bd.buffer2_size);
+            fflush(stdout);
+        }
+
+        cpu_physical_memory_read(bd.buffer1_addr, ptr, len);
+        ptr += len;
+        frame_size += len;
+        if (bd.ctl_stat & 0x20000000) {
+            /* Last buffer in frame.  */
+            qemu_send_packet(&s->nic->nc, frame, len);
+            ptr = frame;
+            frame_size = 0;
+            s->regs[DMA_STATUS] |= DMA_STATUS_TI | DMA_STATUS_NIS;
+        }
+        bd.ctl_stat &= ~0x80000000;
+        /* Write back the modified descriptor.  */
+        xgmac_write_desc(s, &bd, 0);
+    }
+}
+
+static void enet_update_irq(struct XgmacEnet *s)
+{
+    int stat = s->regs[DMA_STATUS] & s->regs[DMA_INTR_ENA];
+    qemu_set_irq(s->sbd_irq, !!stat);
+}
+
+static uint64_t enet_read(void *opaque, target_phys_addr_t addr, 
unsigned size)
+{
+    struct XgmacEnet *s = opaque;
+    uint64_t r = 0;
+    addr >>= 2;
+
+    switch (addr) {
+    case XGMAC_VERSION:
+        r = 0x1012;
+        break;
+    default:
+        if (addr < ARRAY_SIZE(s->regs)) {
+            r = s->regs[addr];
+        }
+        break;
+    }
+    return r;
+}
+
+static void enet_write(void *opaque, target_phys_addr_t addr, uint64_t 
value, unsigned size)
+{
+    struct XgmacEnet *s = opaque;
+
+    addr >>= 2;
+    switch (addr) {
+    case DMA_BUS_MODE:
+        s->regs[DMA_BUS_MODE] = value & ~0x1;
+        break;
+    case DMA_XMT_POLL_DEMAND:
+        xgmac_enet_send(s);
+        break;
+    case DMA_STATUS:
+        s->regs[DMA_STATUS] = s->regs[DMA_STATUS] & ~value;
+        break;
+    case DMA_RCV_BASE_ADDR:
+        s->regs[DMA_RCV_BASE_ADDR] = s->regs[DMA_CUR_RX_DESC_ADDR] = value;
+        break;
+    case DMA_TX_BASE_ADDR:
+        s->regs[DMA_TX_BASE_ADDR] = s->regs[DMA_CUR_TX_DESC_ADDR] = value;
+        break;
+    default:
+        if (addr < ARRAY_SIZE(s->regs)) {
+            s->regs[addr] = value;
+        }
+        break;
+    }
+    enet_update_irq(s);
+}
+
+static const MemoryRegionOps enet_mem_ops = {
+    .read = enet_read,
+    .write = enet_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static int eth_can_rx(VLANClientState *nc)
+{
+    struct XgmacEnet *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+    /* RX enabled?  */
+    return s->regs[DMA_CONTROL] & DMA_CONTROL_SR;
+}
+
+static ssize_t eth_rx(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+    struct XgmacEnet *s = DO_UPCAST(NICState, nc, nc)->opaque;
+    static const unsigned char sa_bcast[6] = {0xff, 0xff, 0xff,
+                                              0xff, 0xff, 0xff};
+    int unicast, broadcast, multicast;
+    struct desc bd;
+    ssize_t ret;
+
+    unicast = ~buf[0] & 0x1;
+    broadcast = memcmp(buf, sa_bcast, 6) == 0;
+    multicast = !unicast && !broadcast;
+    if (size < 12) {
+        s->regs[DMA_STATUS] |= DMA_STATUS_RI | DMA_STATUS_NIS;
+        ret = -1;
+        goto out;
+    }
+
+    xgmac_read_desc(s, &bd, 1);
+    if ((bd.ctl_stat & 0x80000000) == 0) {
+        s->regs[DMA_STATUS] |= DMA_STATUS_RU | DMA_STATUS_AIS;
+        ret = size;
+        goto out;
+    }
+
+    cpu_physical_memory_write(bd.buffer1_addr, buf, size);
+
+    /* Add in the 4 bytes for crc (the real hw returns length incl crc) */
+    size += 4;
+    bd.ctl_stat = (size << 16) | 0x300;
+    xgmac_write_desc(s, &bd, 1);
+
+    s->stats.rx_bytes += size;
+    s->stats.rx++;
+    if (multicast) {
+        s->stats.rx_mcast++;
+    } else if (broadcast) {
+        s->stats.rx_bcast++;
+    }
+
+    s->regs[DMA_STATUS] |= DMA_STATUS_RI | DMA_STATUS_NIS;