Message ID | 4EF0DEBD.70406@calxeda.com |
---|---|
State | New |
Headers | show |
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 >
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.
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 --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;