From: Mark Langsdorf <mark.langsdorf@calxeda.com>
To: qemu-devel@nongnu.org, paul@codesourcery.com, peter.maydell@linaro.org
Subject: [Qemu-devel] [PATCH 8/9] Add xgmac ethernet model
Date: Tue, 20 Dec 2011 13:15:09 -0600 [thread overview]
Message-ID: <4EF0DEBD.70406@calxeda.com> (raw)
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
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);
+}
+
+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
next reply other threads:[~2011-12-20 19:15 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-12-20 19:15 Mark Langsdorf [this message]
2011-12-20 20:24 ` [Qemu-devel] [PATCH 8/9] Add xgmac ethernet model Peter Maydell
2011-12-21 22:39 ` Mark Langsdorf
2011-12-28 0:27 ` Peter Maydell
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=4EF0DEBD.70406@calxeda.com \
--to=mark.langsdorf@calxeda.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.