CONFIG_LM832X=y
CONFIG_TMP105=y
CONFIG_STELLARIS_INPUT=y
+CONFIG_STELLARIS_ENET=y
CONFIG_SSD0303=y
CONFIG_SSD0323=y
CONFIG_ADS7846=y
# Default configuration for cris-softmmu
+CONFIG_ETRAXFS=y
CONFIG_NAND=y
CONFIG_PTIMER=y
CONFIG_PFLASH_CFI02=y
# Default configuration for lm32-softmmu
+CONFIG_MILKYMIST=y
CONFIG_PTIMER=y
CONFIG_PFLASH_CFI01=y
CONFIG_PFLASH_CFI02=y
include pci.mak
include usb.mak
+CONFIG_COLDFIRE=y
CONFIG_GDBSTUB_XML=y
CONFIG_PTIMER=y
CONFIG_SERIAL=y
CONFIG_XILINX=y
CONFIG_XILINX_AXI=y
+CONFIG_XILINX_ETHLITE=y
CONFIG_SSI=y
CONFIG_SSI_M25P80=y
CONFIG_SERIAL=y
CONFIG_XILINX=y
CONFIG_XILINX_AXI=y
+CONFIG_XILINX_ETHLITE=y
CONFIG_SSI=y
CONFIG_SSI_M25P80=y
CONFIG_PTIMER=y
CONFIG_I8259=y
CONFIG_XILINX=y
+CONFIG_XILINX_ETHLITE=y
CONFIG_E500=$(CONFIG_FDT)
# For PReP
CONFIG_MC146818RTC=y
CONFIG_PTIMER=y
CONFIG_I8259=y
CONFIG_XILINX=y
+CONFIG_XILINX_ETHLITE=y
CONFIG_PSERIES=$(CONFIG_FDT)
CONFIG_E500=$(CONFIG_FDT)
# For pSeries
CONFIG_PTIMER=y
CONFIG_I8259=y
CONFIG_XILINX=y
+CONFIG_XILINX_ETHLITE=y
CONFIG_E500=$(CONFIG_FDT)
# For PReP
CONFIG_MC146818RTC=y
CONFIG_FDC=y
CONFIG_EMPTY_SLOT=y
CONFIG_PCNET_COMMON=y
+CONFIG_LANCE=y
obj-y += exynos4210_pmu.o exynos4210_mct.o exynos4210_fimd.o
obj-y += exynos4210_rtc.o exynos4210_i2c.o
obj-y += arm_mptimer.o a15mpcore.o
-obj-y += armv7m_nvic.o stellaris_enet.o
+obj-y += armv7m_nvic.o
obj-y += pxa2xx_timer.o pxa2xx_dma.o
obj-y += pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o
obj-y += zaurus.o tc6393xb.o
# IO blocks
obj-y += etraxfs_dma.o
obj-y += etraxfs_pic.o
-obj-y += etraxfs_eth.o
obj-y += etraxfs_timer.o
obj-y += etraxfs_ser.o
+++ /dev/null
-/*
- * QEMU ETRAX Ethernet Controller.
- *
- * Copyright (c) 2008 Edgar E. Iglesias, Axis Communications AB.
- *
- * 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 <stdio.h>
-#include "hw/sysbus.h"
-#include "net/net.h"
-#include "hw/cris/etraxfs.h"
-
-#define D(x)
-
-/* Advertisement control register. */
-#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */
-#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */
-#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */
-#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */
-
-/*
- * The MDIO extensions in the TDK PHY model were reversed engineered from the
- * linux driver (PHYID and Diagnostics reg).
- * TODO: Add friendly names for the register nums.
- */
-struct qemu_phy
-{
- uint32_t regs[32];
-
- int link;
-
- unsigned int (*read)(struct qemu_phy *phy, unsigned int req);
- void (*write)(struct qemu_phy *phy, unsigned int req, unsigned int data);
-};
-
-static unsigned int tdk_read(struct qemu_phy *phy, unsigned int req)
-{
- int regnum;
- unsigned r = 0;
-
- regnum = req & 0x1f;
-
- switch (regnum) {
- case 1:
- if (!phy->link) {
- break;
- }
- /* MR1. */
- /* Speeds and modes. */
- r |= (1 << 13) | (1 << 14);
- r |= (1 << 11) | (1 << 12);
- r |= (1 << 5); /* Autoneg complete. */
- r |= (1 << 3); /* Autoneg able. */
- r |= (1 << 2); /* link. */
- break;
- case 5:
- /* Link partner ability.
- We are kind; always agree with whatever best mode
- the guest advertises. */
- r = 1 << 14; /* Success. */
- /* Copy advertised modes. */
- r |= phy->regs[4] & (15 << 5);
- /* Autoneg support. */
- r |= 1;
- break;
- case 18:
- {
- /* Diagnostics reg. */
- int duplex = 0;
- int speed_100 = 0;
-
- if (!phy->link) {
- break;
- }
-
- /* Are we advertising 100 half or 100 duplex ? */
- speed_100 = !!(phy->regs[4] & ADVERTISE_100HALF);
- speed_100 |= !!(phy->regs[4] & ADVERTISE_100FULL);
-
- /* Are we advertising 10 duplex or 100 duplex ? */
- duplex = !!(phy->regs[4] & ADVERTISE_100FULL);
- duplex |= !!(phy->regs[4] & ADVERTISE_10FULL);
- r = (speed_100 << 10) | (duplex << 11);
- }
- break;
-
- default:
- r = phy->regs[regnum];
- break;
- }
- D(printf("\n%s %x = reg[%d]\n", __func__, r, regnum));
- return r;
-}
-
-static void
-tdk_write(struct qemu_phy *phy, unsigned int req, unsigned int data)
-{
- int regnum;
-
- regnum = req & 0x1f;
- D(printf("%s reg[%d] = %x\n", __func__, regnum, data));
- switch (regnum) {
- default:
- phy->regs[regnum] = data;
- break;
- }
-}
-
-static void
-tdk_init(struct qemu_phy *phy)
-{
- phy->regs[0] = 0x3100;
- /* PHY Id. */
- phy->regs[2] = 0x0300;
- phy->regs[3] = 0xe400;
- /* Autonegotiation advertisement reg. */
- phy->regs[4] = 0x01E1;
- phy->link = 1;
-
- phy->read = tdk_read;
- phy->write = tdk_write;
-}
-
-struct qemu_mdio
-{
- /* bus. */
- int mdc;
- int mdio;
-
- /* decoder. */
- enum {
- PREAMBLE,
- SOF,
- OPC,
- ADDR,
- REQ,
- TURNAROUND,
- DATA
- } state;
- unsigned int drive;
-
- unsigned int cnt;
- unsigned int addr;
- unsigned int opc;
- unsigned int req;
- unsigned int data;
-
- struct qemu_phy *devs[32];
-};
-
-static void
-mdio_attach(struct qemu_mdio *bus, struct qemu_phy *phy, unsigned int addr)
-{
- bus->devs[addr & 0x1f] = phy;
-}
-
-#ifdef USE_THIS_DEAD_CODE
-static void
-mdio_detach(struct qemu_mdio *bus, struct qemu_phy *phy, unsigned int addr)
-{
- bus->devs[addr & 0x1f] = NULL;
-}
-#endif
-
-static void mdio_read_req(struct qemu_mdio *bus)
-{
- struct qemu_phy *phy;
-
- phy = bus->devs[bus->addr];
- if (phy && phy->read) {
- bus->data = phy->read(phy, bus->req);
- } else {
- bus->data = 0xffff;
- }
-}
-
-static void mdio_write_req(struct qemu_mdio *bus)
-{
- struct qemu_phy *phy;
-
- phy = bus->devs[bus->addr];
- if (phy && phy->write) {
- phy->write(phy, bus->req, bus->data);
- }
-}
-
-static void mdio_cycle(struct qemu_mdio *bus)
-{
- bus->cnt++;
-
- D(printf("mdc=%d mdio=%d state=%d cnt=%d drv=%d\n",
- bus->mdc, bus->mdio, bus->state, bus->cnt, bus->drive));
-#if 0
- if (bus->mdc) {
- printf("%d", bus->mdio);
- }
-#endif
- switch (bus->state) {
- case PREAMBLE:
- if (bus->mdc) {
- if (bus->cnt >= (32 * 2) && !bus->mdio) {
- bus->cnt = 0;
- bus->state = SOF;
- bus->data = 0;
- }
- }
- break;
- case SOF:
- if (bus->mdc) {
- if (bus->mdio != 1) {
- printf("WARNING: no SOF\n");
- }
- if (bus->cnt == 1*2) {
- bus->cnt = 0;
- bus->opc = 0;
- bus->state = OPC;
- }
- }
- break;
- case OPC:
- if (bus->mdc) {
- bus->opc <<= 1;
- bus->opc |= bus->mdio & 1;
- if (bus->cnt == 2*2) {
- bus->cnt = 0;
- bus->addr = 0;
- bus->state = ADDR;
- }
- }
- break;
- case ADDR:
- if (bus->mdc) {
- bus->addr <<= 1;
- bus->addr |= bus->mdio & 1;
-
- if (bus->cnt == 5*2) {
- bus->cnt = 0;
- bus->req = 0;
- bus->state = REQ;
- }
- }
- break;
- case REQ:
- if (bus->mdc) {
- bus->req <<= 1;
- bus->req |= bus->mdio & 1;
- if (bus->cnt == 5*2) {
- bus->cnt = 0;
- bus->state = TURNAROUND;
- }
- }
- break;
- case TURNAROUND:
- if (bus->mdc && bus->cnt == 2*2) {
- bus->mdio = 0;
- bus->cnt = 0;
-
- if (bus->opc == 2) {
- bus->drive = 1;
- mdio_read_req(bus);
- bus->mdio = bus->data & 1;
- }
- bus->state = DATA;
- }
- break;
- case DATA:
- if (!bus->mdc) {
- if (bus->drive) {
- bus->mdio = !!(bus->data & (1 << 15));
- bus->data <<= 1;
- }
- } else {
- if (!bus->drive) {
- bus->data <<= 1;
- bus->data |= bus->mdio;
- }
- if (bus->cnt == 16 * 2) {
- bus->cnt = 0;
- bus->state = PREAMBLE;
- if (!bus->drive) {
- mdio_write_req(bus);
- }
- bus->drive = 0;
- }
- }
- break;
- default:
- break;
- }
-}
-
-/* ETRAX-FS Ethernet MAC block starts here. */
-
-#define RW_MA0_LO 0x00
-#define RW_MA0_HI 0x01
-#define RW_MA1_LO 0x02
-#define RW_MA1_HI 0x03
-#define RW_GA_LO 0x04
-#define RW_GA_HI 0x05
-#define RW_GEN_CTRL 0x06
-#define RW_REC_CTRL 0x07
-#define RW_TR_CTRL 0x08
-#define RW_CLR_ERR 0x09
-#define RW_MGM_CTRL 0x0a
-#define R_STAT 0x0b
-#define FS_ETH_MAX_REGS 0x17
-
-struct fs_eth
-{
- SysBusDevice busdev;
- MemoryRegion mmio;
- NICState *nic;
- NICConf conf;
-
- /* Two addrs in the filter. */
- uint8_t macaddr[2][6];
- uint32_t regs[FS_ETH_MAX_REGS];
-
- union {
- void *vdma_out;
- struct etraxfs_dma_client *dma_out;
- };
- union {
- void *vdma_in;
- struct etraxfs_dma_client *dma_in;
- };
-
- /* MDIO bus. */
- struct qemu_mdio mdio_bus;
- unsigned int phyaddr;
- int duplex_mismatch;
-
- /* PHY. */
- struct qemu_phy phy;
-};
-
-static void eth_validate_duplex(struct fs_eth *eth)
-{
- struct qemu_phy *phy;
- unsigned int phy_duplex;
- unsigned int mac_duplex;
- int new_mm = 0;
-
- phy = eth->mdio_bus.devs[eth->phyaddr];
- phy_duplex = !!(phy->read(phy, 18) & (1 << 11));
- mac_duplex = !!(eth->regs[RW_REC_CTRL] & 128);
-
- if (mac_duplex != phy_duplex) {
- new_mm = 1;
- }
-
- if (eth->regs[RW_GEN_CTRL] & 1) {
- if (new_mm != eth->duplex_mismatch) {
- if (new_mm) {
- printf("HW: WARNING ETH duplex mismatch MAC=%d PHY=%d\n",
- mac_duplex, phy_duplex);
- } else {
- printf("HW: ETH duplex ok.\n");
- }
- }
- eth->duplex_mismatch = new_mm;
- }
-}
-
-static uint64_t
-eth_read(void *opaque, hwaddr addr, unsigned int size)
-{
- struct fs_eth *eth = opaque;
- uint32_t r = 0;
-
- addr >>= 2;
-
- switch (addr) {
- case R_STAT:
- r = eth->mdio_bus.mdio & 1;
- break;
- default:
- r = eth->regs[addr];
- D(printf("%s %x\n", __func__, addr * 4));
- break;
- }
- return r;
-}
-
-static void eth_update_ma(struct fs_eth *eth, int ma)
-{
- int reg;
- int i = 0;
-
- ma &= 1;
-
- reg = RW_MA0_LO;
- if (ma) {
- reg = RW_MA1_LO;
- }
-
- eth->macaddr[ma][i++] = eth->regs[reg];
- eth->macaddr[ma][i++] = eth->regs[reg] >> 8;
- eth->macaddr[ma][i++] = eth->regs[reg] >> 16;
- eth->macaddr[ma][i++] = eth->regs[reg] >> 24;
- eth->macaddr[ma][i++] = eth->regs[reg + 1];
- eth->macaddr[ma][i] = eth->regs[reg + 1] >> 8;
-
- D(printf("set mac%d=%x.%x.%x.%x.%x.%x\n", ma,
- eth->macaddr[ma][0], eth->macaddr[ma][1],
- eth->macaddr[ma][2], eth->macaddr[ma][3],
- eth->macaddr[ma][4], eth->macaddr[ma][5]));
-}
-
-static void
-eth_write(void *opaque, hwaddr addr,
- uint64_t val64, unsigned int size)
-{
- struct fs_eth *eth = opaque;
- uint32_t value = val64;
-
- addr >>= 2;
- switch (addr) {
- case RW_MA0_LO:
- case RW_MA0_HI:
- eth->regs[addr] = value;
- eth_update_ma(eth, 0);
- break;
- case RW_MA1_LO:
- case RW_MA1_HI:
- eth->regs[addr] = value;
- eth_update_ma(eth, 1);
- break;
-
- case RW_MGM_CTRL:
- /* Attach an MDIO/PHY abstraction. */
- if (value & 2) {
- eth->mdio_bus.mdio = value & 1;
- }
- if (eth->mdio_bus.mdc != (value & 4)) {
- mdio_cycle(ð->mdio_bus);
- eth_validate_duplex(eth);
- }
- eth->mdio_bus.mdc = !!(value & 4);
- eth->regs[addr] = value;
- break;
-
- case RW_REC_CTRL:
- eth->regs[addr] = value;
- eth_validate_duplex(eth);
- break;
-
- default:
- eth->regs[addr] = value;
- D(printf("%s %x %x\n", __func__, addr, value));
- break;
- }
-}
-
-/* The ETRAX FS has a groupt address table (GAT) which works like a k=1 bloom
- filter dropping group addresses we have not joined. The filter has 64
- bits (m). The has function is a simple nible xor of the group addr. */
-static int eth_match_groupaddr(struct fs_eth *eth, const unsigned char *sa)
-{
- unsigned int hsh;
- int m_individual = eth->regs[RW_REC_CTRL] & 4;
- int match;
-
- /* First bit on the wire of a MAC address signals multicast or
- physical address. */
- if (!m_individual && !(sa[0] & 1)) {
- return 0;
- }
-
- /* Calculate the hash index for the GA registers. */
- hsh = 0;
- hsh ^= (*sa) & 0x3f;
- hsh ^= ((*sa) >> 6) & 0x03;
- ++sa;
- hsh ^= ((*sa) << 2) & 0x03c;
- hsh ^= ((*sa) >> 4) & 0xf;
- ++sa;
- hsh ^= ((*sa) << 4) & 0x30;
- hsh ^= ((*sa) >> 2) & 0x3f;
- ++sa;
- hsh ^= (*sa) & 0x3f;
- hsh ^= ((*sa) >> 6) & 0x03;
- ++sa;
- hsh ^= ((*sa) << 2) & 0x03c;
- hsh ^= ((*sa) >> 4) & 0xf;
- ++sa;
- hsh ^= ((*sa) << 4) & 0x30;
- hsh ^= ((*sa) >> 2) & 0x3f;
-
- hsh &= 63;
- if (hsh > 31) {
- match = eth->regs[RW_GA_HI] & (1 << (hsh - 32));
- } else {
- match = eth->regs[RW_GA_LO] & (1 << hsh);
- }
- D(printf("hsh=%x ga=%x.%x mtch=%d\n", hsh,
- eth->regs[RW_GA_HI], eth->regs[RW_GA_LO], match));
- return match;
-}
-
-static int eth_can_receive(NetClientState *nc)
-{
- return 1;
-}
-
-static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size)
-{
- unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
- struct fs_eth *eth = qemu_get_nic_opaque(nc);
- int use_ma0 = eth->regs[RW_REC_CTRL] & 1;
- int use_ma1 = eth->regs[RW_REC_CTRL] & 2;
- int r_bcast = eth->regs[RW_REC_CTRL] & 8;
-
- if (size < 12) {
- return -1;
- }
-
- D(printf("%x.%x.%x.%x.%x.%x ma=%d %d bc=%d\n",
- buf[0], buf[1], buf[2], buf[3], buf[4], buf[5],
- use_ma0, use_ma1, r_bcast));
-
- /* Does the frame get through the address filters? */
- if ((!use_ma0 || memcmp(buf, eth->macaddr[0], 6))
- && (!use_ma1 || memcmp(buf, eth->macaddr[1], 6))
- && (!r_bcast || memcmp(buf, sa_bcast, 6))
- && !eth_match_groupaddr(eth, buf)) {
- return size;
- }
-
- /* FIXME: Find another way to pass on the fake csum. */
- etraxfs_dmac_input(eth->dma_in, (void *)buf, size + 4, 1);
-
- return size;
-}
-
-static int eth_tx_push(void *opaque, unsigned char *buf, int len, bool eop)
-{
- struct fs_eth *eth = opaque;
-
- D(printf("%s buf=%p len=%d\n", __func__, buf, len));
- qemu_send_packet(qemu_get_queue(eth->nic), buf, len);
- return len;
-}
-
-static void eth_set_link(NetClientState *nc)
-{
- struct fs_eth *eth = qemu_get_nic_opaque(nc);
- D(printf("%s %d\n", __func__, nc->link_down));
- eth->phy.link = !nc->link_down;
-}
-
-static const MemoryRegionOps eth_ops = {
- .read = eth_read,
- .write = eth_write,
- .endianness = DEVICE_LITTLE_ENDIAN,
- .valid = {
- .min_access_size = 4,
- .max_access_size = 4
- }
-};
-
-static void eth_cleanup(NetClientState *nc)
-{
- struct fs_eth *eth = qemu_get_nic_opaque(nc);
-
- /* Disconnect the client. */
- eth->dma_out->client.push = NULL;
- eth->dma_out->client.opaque = NULL;
- eth->dma_in->client.opaque = NULL;
- eth->dma_in->client.pull = NULL;
- g_free(eth);
-}
-
-static NetClientInfo net_etraxfs_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
- .size = sizeof(NICState),
- .can_receive = eth_can_receive,
- .receive = eth_receive,
- .cleanup = eth_cleanup,
- .link_status_changed = eth_set_link,
-};
-
-static int fs_eth_init(SysBusDevice *dev)
-{
- struct fs_eth *s = FROM_SYSBUS(typeof(*s), dev);
-
- if (!s->dma_out || !s->dma_in) {
- hw_error("Unconnected ETRAX-FS Ethernet MAC.\n");
- }
-
- s->dma_out->client.push = eth_tx_push;
- s->dma_out->client.opaque = s;
- s->dma_in->client.opaque = s;
- s->dma_in->client.pull = NULL;
-
- memory_region_init_io(&s->mmio, ð_ops, s, "etraxfs-eth", 0x5c);
- sysbus_init_mmio(dev, &s->mmio);
-
- qemu_macaddr_default_if_unset(&s->conf.macaddr);
- s->nic = qemu_new_nic(&net_etraxfs_info, &s->conf,
- object_get_typename(OBJECT(s)), dev->qdev.id, s);
- qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
-
-
- tdk_init(&s->phy);
- mdio_attach(&s->mdio_bus, &s->phy, s->phyaddr);
- return 0;
-}
-
-static Property etraxfs_eth_properties[] = {
- DEFINE_PROP_UINT32("phyaddr", struct fs_eth, phyaddr, 1),
- DEFINE_PROP_PTR("dma_out", struct fs_eth, vdma_out),
- DEFINE_PROP_PTR("dma_in", struct fs_eth, vdma_in),
- DEFINE_NIC_PROPERTIES(struct fs_eth, conf),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void etraxfs_eth_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
- k->init = fs_eth_init;
- dc->props = etraxfs_eth_properties;
-}
-
-static const TypeInfo etraxfs_eth_info = {
- .name = "etraxfs-eth",
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(struct fs_eth),
- .class_init = etraxfs_eth_class_init,
-};
-
-static void etraxfs_eth_register_types(void)
-{
- type_register_static(&etraxfs_eth_info);
-}
-
-type_init(etraxfs_eth_register_types)
+++ /dev/null
-/*
- * QEMU AMD PC-Net II (Am79C970A) emulation
- *
- * Copyright (c) 2004 Antony T Curtis
- *
- * 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.
- */
-
-/* This software was written to be compatible with the specification:
- * AMD Am79C970A PCnet-PCI II Ethernet Controller Data-Sheet
- * AMD Publication# 19436 Rev:E Amendment/0 Issue Date: June 2000
- */
-
-/*
- * On Sparc32, this is the Lance (Am7990) part of chip STP2000 (Master I/O), also
- * produced as NCR89C100. See
- * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt
- * and
- * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR92C990.txt
- */
-
-#include "hw/sysbus.h"
-#include "net/net.h"
-#include "qemu/timer.h"
-#include "qemu/sockets.h"
-#include "hw/sparc/sun4m.h"
-#include "hw/pcnet.h"
-#include "trace.h"
-
-typedef struct {
- SysBusDevice busdev;
- PCNetState state;
-} SysBusPCNetState;
-
-static void parent_lance_reset(void *opaque, int irq, int level)
-{
- SysBusPCNetState *d = opaque;
- if (level)
- pcnet_h_reset(&d->state);
-}
-
-static void lance_mem_write(void *opaque, hwaddr addr,
- uint64_t val, unsigned size)
-{
- SysBusPCNetState *d = opaque;
-
- trace_lance_mem_writew(addr, val & 0xffff);
- pcnet_ioport_writew(&d->state, addr, val & 0xffff);
-}
-
-static uint64_t lance_mem_read(void *opaque, hwaddr addr,
- unsigned size)
-{
- SysBusPCNetState *d = opaque;
- uint32_t val;
-
- val = pcnet_ioport_readw(&d->state, addr);
- trace_lance_mem_readw(addr, val & 0xffff);
- return val & 0xffff;
-}
-
-static const MemoryRegionOps lance_mem_ops = {
- .read = lance_mem_read,
- .write = lance_mem_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
- .valid = {
- .min_access_size = 2,
- .max_access_size = 2,
- },
-};
-
-static void lance_cleanup(NetClientState *nc)
-{
- PCNetState *d = qemu_get_nic_opaque(nc);
-
- pcnet_common_cleanup(d);
-}
-
-static NetClientInfo net_lance_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
- .size = sizeof(NICState),
- .can_receive = pcnet_can_receive,
- .receive = pcnet_receive,
- .link_status_changed = pcnet_set_link_status,
- .cleanup = lance_cleanup,
-};
-
-static const VMStateDescription vmstate_lance = {
- .name = "pcnet",
- .version_id = 3,
- .minimum_version_id = 2,
- .minimum_version_id_old = 2,
- .fields = (VMStateField []) {
- VMSTATE_STRUCT(state, SysBusPCNetState, 0, vmstate_pcnet, PCNetState),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static int lance_init(SysBusDevice *dev)
-{
- SysBusPCNetState *d = FROM_SYSBUS(SysBusPCNetState, dev);
- PCNetState *s = &d->state;
-
- memory_region_init_io(&s->mmio, &lance_mem_ops, d, "lance-mmio", 4);
-
- qdev_init_gpio_in(&dev->qdev, parent_lance_reset, 1);
-
- sysbus_init_mmio(dev, &s->mmio);
-
- sysbus_init_irq(dev, &s->irq);
-
- s->phys_mem_read = ledma_memory_read;
- s->phys_mem_write = ledma_memory_write;
- return pcnet_common_init(&dev->qdev, s, &net_lance_info);
-}
-
-static void lance_reset(DeviceState *dev)
-{
- SysBusPCNetState *d = DO_UPCAST(SysBusPCNetState, busdev.qdev, dev);
-
- pcnet_h_reset(&d->state);
-}
-
-static Property lance_properties[] = {
- DEFINE_PROP_PTR("dma", SysBusPCNetState, state.dma_opaque),
- DEFINE_NIC_PROPERTIES(SysBusPCNetState, state.conf),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void lance_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
- k->init = lance_init;
- dc->fw_name = "ethernet";
- dc->reset = lance_reset;
- dc->vmsd = &vmstate_lance;
- dc->props = lance_properties;
-}
-
-static const TypeInfo lance_info = {
- .name = "lance",
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(SysBusPCNetState),
- .class_init = lance_class_init,
-};
-
-static void lance_register_types(void)
-{
- type_register_static(&lance_info);
-}
-
-type_init(lance_register_types)
obj-y += milkymist-ac97.o
obj-y += milkymist-hpdmc.o
obj-y += milkymist-memcard.o
-obj-y += milkymist-minimac2.o
obj-y += milkymist-pfpu.o
obj-y += milkymist-softusb.o
obj-y += milkymist-sysctl.o
-obj-y = mcf_uart.o mcf_fec.o
+obj-y = mcf_uart.o
obj-y := $(addprefix ../,$(obj-y))
+++ /dev/null
-/*
- * ColdFire Fast Ethernet Controller emulation.
- *
- * Copyright (c) 2007 CodeSourcery.
- *
- * This code is licensed under the GPL
- */
-#include "hw/hw.h"
-#include "net/net.h"
-#include "hw/m68k/mcf.h"
-/* For crc32 */
-#include <zlib.h>
-#include "exec/address-spaces.h"
-
-//#define DEBUG_FEC 1
-
-#ifdef DEBUG_FEC
-#define DPRINTF(fmt, ...) \
-do { printf("mcf_fec: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...) do {} while(0)
-#endif
-
-#define FEC_MAX_FRAME_SIZE 2032
-
-typedef struct {
- MemoryRegion *sysmem;
- MemoryRegion iomem;
- qemu_irq *irq;
- NICState *nic;
- NICConf conf;
- uint32_t irq_state;
- uint32_t eir;
- uint32_t eimr;
- int rx_enabled;
- uint32_t rx_descriptor;
- uint32_t tx_descriptor;
- uint32_t ecr;
- uint32_t mmfr;
- uint32_t mscr;
- uint32_t rcr;
- uint32_t tcr;
- uint32_t tfwr;
- uint32_t rfsr;
- uint32_t erdsr;
- uint32_t etdsr;
- uint32_t emrbr;
-} mcf_fec_state;
-
-#define FEC_INT_HB 0x80000000
-#define FEC_INT_BABR 0x40000000
-#define FEC_INT_BABT 0x20000000
-#define FEC_INT_GRA 0x10000000
-#define FEC_INT_TXF 0x08000000
-#define FEC_INT_TXB 0x04000000
-#define FEC_INT_RXF 0x02000000
-#define FEC_INT_RXB 0x01000000
-#define FEC_INT_MII 0x00800000
-#define FEC_INT_EB 0x00400000
-#define FEC_INT_LC 0x00200000
-#define FEC_INT_RL 0x00100000
-#define FEC_INT_UN 0x00080000
-
-#define FEC_EN 2
-#define FEC_RESET 1
-
-/* Map interrupt flags onto IRQ lines. */
-#define FEC_NUM_IRQ 13
-static const uint32_t mcf_fec_irq_map[FEC_NUM_IRQ] = {
- FEC_INT_TXF,
- FEC_INT_TXB,
- FEC_INT_UN,
- FEC_INT_RL,
- FEC_INT_RXF,
- FEC_INT_RXB,
- FEC_INT_MII,
- FEC_INT_LC,
- FEC_INT_HB,
- FEC_INT_GRA,
- FEC_INT_EB,
- FEC_INT_BABT,
- FEC_INT_BABR
-};
-
-/* Buffer Descriptor. */
-typedef struct {
- uint16_t flags;
- uint16_t length;
- uint32_t data;
-} mcf_fec_bd;
-
-#define FEC_BD_R 0x8000
-#define FEC_BD_E 0x8000
-#define FEC_BD_O1 0x4000
-#define FEC_BD_W 0x2000
-#define FEC_BD_O2 0x1000
-#define FEC_BD_L 0x0800
-#define FEC_BD_TC 0x0400
-#define FEC_BD_ABC 0x0200
-#define FEC_BD_M 0x0100
-#define FEC_BD_BC 0x0080
-#define FEC_BD_MC 0x0040
-#define FEC_BD_LG 0x0020
-#define FEC_BD_NO 0x0010
-#define FEC_BD_CR 0x0004
-#define FEC_BD_OV 0x0002
-#define FEC_BD_TR 0x0001
-
-static void mcf_fec_read_bd(mcf_fec_bd *bd, uint32_t addr)
-{
- cpu_physical_memory_read(addr, (uint8_t *)bd, sizeof(*bd));
- be16_to_cpus(&bd->flags);
- be16_to_cpus(&bd->length);
- be32_to_cpus(&bd->data);
-}
-
-static void mcf_fec_write_bd(mcf_fec_bd *bd, uint32_t addr)
-{
- mcf_fec_bd tmp;
- tmp.flags = cpu_to_be16(bd->flags);
- tmp.length = cpu_to_be16(bd->length);
- tmp.data = cpu_to_be32(bd->data);
- cpu_physical_memory_write(addr, (uint8_t *)&tmp, sizeof(tmp));
-}
-
-static void mcf_fec_update(mcf_fec_state *s)
-{
- uint32_t active;
- uint32_t changed;
- uint32_t mask;
- int i;
-
- active = s->eir & s->eimr;
- changed = active ^s->irq_state;
- for (i = 0; i < FEC_NUM_IRQ; i++) {
- mask = mcf_fec_irq_map[i];
- if (changed & mask) {
- DPRINTF("IRQ %d = %d\n", i, (active & mask) != 0);
- qemu_set_irq(s->irq[i], (active & mask) != 0);
- }
- }
- s->irq_state = active;
-}
-
-static void mcf_fec_do_tx(mcf_fec_state *s)
-{
- uint32_t addr;
- mcf_fec_bd bd;
- int frame_size;
- int len;
- uint8_t frame[FEC_MAX_FRAME_SIZE];
- uint8_t *ptr;
-
- DPRINTF("do_tx\n");
- ptr = frame;
- frame_size = 0;
- addr = s->tx_descriptor;
- while (1) {
- mcf_fec_read_bd(&bd, addr);
- DPRINTF("tx_bd %x flags %04x len %d data %08x\n",
- addr, bd.flags, bd.length, bd.data);
- if ((bd.flags & FEC_BD_R) == 0) {
- /* Run out of descriptors to transmit. */
- break;
- }
- len = bd.length;
- if (frame_size + len > FEC_MAX_FRAME_SIZE) {
- len = FEC_MAX_FRAME_SIZE - frame_size;
- s->eir |= FEC_INT_BABT;
- }
- cpu_physical_memory_read(bd.data, ptr, len);
- ptr += len;
- frame_size += len;
- if (bd.flags & FEC_BD_L) {
- /* Last buffer in frame. */
- DPRINTF("Sending packet\n");
- qemu_send_packet(qemu_get_queue(s->nic), frame, len);
- ptr = frame;
- frame_size = 0;
- s->eir |= FEC_INT_TXF;
- }
- s->eir |= FEC_INT_TXB;
- bd.flags &= ~FEC_BD_R;
- /* Write back the modified descriptor. */
- mcf_fec_write_bd(&bd, addr);
- /* Advance to the next descriptor. */
- if ((bd.flags & FEC_BD_W) != 0) {
- addr = s->etdsr;
- } else {
- addr += 8;
- }
- }
- s->tx_descriptor = addr;
-}
-
-static void mcf_fec_enable_rx(mcf_fec_state *s)
-{
- mcf_fec_bd bd;
-
- mcf_fec_read_bd(&bd, s->rx_descriptor);
- s->rx_enabled = ((bd.flags & FEC_BD_E) != 0);
- if (!s->rx_enabled)
- DPRINTF("RX buffer full\n");
-}
-
-static void mcf_fec_reset(mcf_fec_state *s)
-{
- s->eir = 0;
- s->eimr = 0;
- s->rx_enabled = 0;
- s->ecr = 0;
- s->mscr = 0;
- s->rcr = 0x05ee0001;
- s->tcr = 0;
- s->tfwr = 0;
- s->rfsr = 0x500;
-}
-
-static uint64_t mcf_fec_read(void *opaque, hwaddr addr,
- unsigned size)
-{
- mcf_fec_state *s = (mcf_fec_state *)opaque;
- switch (addr & 0x3ff) {
- case 0x004: return s->eir;
- case 0x008: return s->eimr;
- case 0x010: return s->rx_enabled ? (1 << 24) : 0; /* RDAR */
- case 0x014: return 0; /* TDAR */
- case 0x024: return s->ecr;
- case 0x040: return s->mmfr;
- case 0x044: return s->mscr;
- case 0x064: return 0; /* MIBC */
- case 0x084: return s->rcr;
- case 0x0c4: return s->tcr;
- case 0x0e4: /* PALR */
- return (s->conf.macaddr.a[0] << 24) | (s->conf.macaddr.a[1] << 16)
- | (s->conf.macaddr.a[2] << 8) | s->conf.macaddr.a[3];
- break;
- case 0x0e8: /* PAUR */
- return (s->conf.macaddr.a[4] << 24) | (s->conf.macaddr.a[5] << 16) | 0x8808;
- case 0x0ec: return 0x10000; /* OPD */
- case 0x118: return 0;
- case 0x11c: return 0;
- case 0x120: return 0;
- case 0x124: return 0;
- case 0x144: return s->tfwr;
- case 0x14c: return 0x600;
- case 0x150: return s->rfsr;
- case 0x180: return s->erdsr;
- case 0x184: return s->etdsr;
- case 0x188: return s->emrbr;
- default:
- hw_error("mcf_fec_read: Bad address 0x%x\n", (int)addr);
- return 0;
- }
-}
-
-static void mcf_fec_write(void *opaque, hwaddr addr,
- uint64_t value, unsigned size)
-{
- mcf_fec_state *s = (mcf_fec_state *)opaque;
- switch (addr & 0x3ff) {
- case 0x004:
- s->eir &= ~value;
- break;
- case 0x008:
- s->eimr = value;
- break;
- case 0x010: /* RDAR */
- if ((s->ecr & FEC_EN) && !s->rx_enabled) {
- DPRINTF("RX enable\n");
- mcf_fec_enable_rx(s);
- }
- break;
- case 0x014: /* TDAR */
- if (s->ecr & FEC_EN) {
- mcf_fec_do_tx(s);
- }
- break;
- case 0x024:
- s->ecr = value;
- if (value & FEC_RESET) {
- DPRINTF("Reset\n");
- mcf_fec_reset(s);
- }
- if ((s->ecr & FEC_EN) == 0) {
- s->rx_enabled = 0;
- }
- break;
- case 0x040:
- /* TODO: Implement MII. */
- s->mmfr = value;
- break;
- case 0x044:
- s->mscr = value & 0xfe;
- break;
- case 0x064:
- /* TODO: Implement MIB. */
- break;
- case 0x084:
- s->rcr = value & 0x07ff003f;
- /* TODO: Implement LOOP mode. */
- break;
- case 0x0c4: /* TCR */
- /* We transmit immediately, so raise GRA immediately. */
- s->tcr = value;
- if (value & 1)
- s->eir |= FEC_INT_GRA;
- break;
- case 0x0e4: /* PALR */
- s->conf.macaddr.a[0] = value >> 24;
- s->conf.macaddr.a[1] = value >> 16;
- s->conf.macaddr.a[2] = value >> 8;
- s->conf.macaddr.a[3] = value;
- break;
- case 0x0e8: /* PAUR */
- s->conf.macaddr.a[4] = value >> 24;
- s->conf.macaddr.a[5] = value >> 16;
- break;
- case 0x0ec:
- /* OPD */
- break;
- case 0x118:
- case 0x11c:
- case 0x120:
- case 0x124:
- /* TODO: implement MAC hash filtering. */
- break;
- case 0x144:
- s->tfwr = value & 3;
- break;
- case 0x14c:
- /* FRBR writes ignored. */
- break;
- case 0x150:
- s->rfsr = (value & 0x3fc) | 0x400;
- break;
- case 0x180:
- s->erdsr = value & ~3;
- s->rx_descriptor = s->erdsr;
- break;
- case 0x184:
- s->etdsr = value & ~3;
- s->tx_descriptor = s->etdsr;
- break;
- case 0x188:
- s->emrbr = value & 0x7f0;
- break;
- default:
- hw_error("mcf_fec_write Bad address 0x%x\n", (int)addr);
- }
- mcf_fec_update(s);
-}
-
-static int mcf_fec_can_receive(NetClientState *nc)
-{
- mcf_fec_state *s = qemu_get_nic_opaque(nc);
- return s->rx_enabled;
-}
-
-static ssize_t mcf_fec_receive(NetClientState *nc, const uint8_t *buf, size_t size)
-{
- mcf_fec_state *s = qemu_get_nic_opaque(nc);
- mcf_fec_bd bd;
- uint32_t flags = 0;
- uint32_t addr;
- uint32_t crc;
- uint32_t buf_addr;
- uint8_t *crc_ptr;
- unsigned int buf_len;
-
- DPRINTF("do_rx len %d\n", size);
- if (!s->rx_enabled) {
- fprintf(stderr, "mcf_fec_receive: Unexpected packet\n");
- }
- /* 4 bytes for the CRC. */
- size += 4;
- crc = cpu_to_be32(crc32(~0, buf, size));
- crc_ptr = (uint8_t *)&crc;
- /* Huge frames are truncted. */
- if (size > FEC_MAX_FRAME_SIZE) {
- size = FEC_MAX_FRAME_SIZE;
- flags |= FEC_BD_TR | FEC_BD_LG;
- }
- /* Frames larger than the user limit just set error flags. */
- if (size > (s->rcr >> 16)) {
- flags |= FEC_BD_LG;
- }
- addr = s->rx_descriptor;
- while (size > 0) {
- mcf_fec_read_bd(&bd, addr);
- if ((bd.flags & FEC_BD_E) == 0) {
- /* No descriptors available. Bail out. */
- /* FIXME: This is wrong. We should probably either save the
- remainder for when more RX buffers are available, or
- flag an error. */
- fprintf(stderr, "mcf_fec: Lost end of frame\n");
- break;
- }
- buf_len = (size <= s->emrbr) ? size: s->emrbr;
- bd.length = buf_len;
- size -= buf_len;
- DPRINTF("rx_bd %x length %d\n", addr, bd.length);
- /* The last 4 bytes are the CRC. */
- if (size < 4)
- buf_len += size - 4;
- buf_addr = bd.data;
- cpu_physical_memory_write(buf_addr, buf, buf_len);
- buf += buf_len;
- if (size < 4) {
- cpu_physical_memory_write(buf_addr + buf_len, crc_ptr, 4 - size);
- crc_ptr += 4 - size;
- }
- bd.flags &= ~FEC_BD_E;
- if (size == 0) {
- /* Last buffer in frame. */
- bd.flags |= flags | FEC_BD_L;
- DPRINTF("rx frame flags %04x\n", bd.flags);
- s->eir |= FEC_INT_RXF;
- } else {
- s->eir |= FEC_INT_RXB;
- }
- mcf_fec_write_bd(&bd, addr);
- /* Advance to the next descriptor. */
- if ((bd.flags & FEC_BD_W) != 0) {
- addr = s->erdsr;
- } else {
- addr += 8;
- }
- }
- s->rx_descriptor = addr;
- mcf_fec_enable_rx(s);
- mcf_fec_update(s);
- return size;
-}
-
-static const MemoryRegionOps mcf_fec_ops = {
- .read = mcf_fec_read,
- .write = mcf_fec_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void mcf_fec_cleanup(NetClientState *nc)
-{
- mcf_fec_state *s = qemu_get_nic_opaque(nc);
-
- memory_region_del_subregion(s->sysmem, &s->iomem);
- memory_region_destroy(&s->iomem);
-
- g_free(s);
-}
-
-static NetClientInfo net_mcf_fec_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
- .size = sizeof(NICState),
- .can_receive = mcf_fec_can_receive,
- .receive = mcf_fec_receive,
- .cleanup = mcf_fec_cleanup,
-};
-
-void mcf_fec_init(MemoryRegion *sysmem, NICInfo *nd,
- hwaddr base, qemu_irq *irq)
-{
- mcf_fec_state *s;
-
- qemu_check_nic_model(nd, "mcf_fec");
-
- s = (mcf_fec_state *)g_malloc0(sizeof(mcf_fec_state));
- s->sysmem = sysmem;
- s->irq = irq;
-
- memory_region_init_io(&s->iomem, &mcf_fec_ops, s, "fec", 0x400);
- memory_region_add_subregion(sysmem, base, &s->iomem);
-
- s->conf.macaddr = nd->macaddr;
- s->conf.peers.ncs[0] = nd->netdev;
-
- s->nic = qemu_new_nic(&net_mcf_fec_info, &s->conf, nd->model, nd->name, s);
-
- qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
-}
obj-y += xilinx_spi.o
-obj-y += xilinx_ethlite.o
obj-y := $(addprefix ../,$(obj-y))
+++ /dev/null
-/*
- * QEMU model of the Milkymist minimac2 block.
- *
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- *
- *
- * Specification available at:
- * not available yet
- *
- */
-
-#include "hw/hw.h"
-#include "hw/sysbus.h"
-#include "trace.h"
-#include "net/net.h"
-#include "qemu/error-report.h"
-#include "hw/qdev-addr.h"
-
-#include <zlib.h>
-
-enum {
- R_SETUP = 0,
- R_MDIO,
- R_STATE0,
- R_COUNT0,
- R_STATE1,
- R_COUNT1,
- R_TXCOUNT,
- R_MAX
-};
-
-enum {
- SETUP_PHY_RST = (1<<0),
-};
-
-enum {
- MDIO_DO = (1<<0),
- MDIO_DI = (1<<1),
- MDIO_OE = (1<<2),
- MDIO_CLK = (1<<3),
-};
-
-enum {
- STATE_EMPTY = 0,
- STATE_LOADED = 1,
- STATE_PENDING = 2,
-};
-
-enum {
- MDIO_OP_WRITE = 1,
- MDIO_OP_READ = 2,
-};
-
-enum mdio_state {
- MDIO_STATE_IDLE,
- MDIO_STATE_READING,
- MDIO_STATE_WRITING,
-};
-
-enum {
- R_PHY_ID1 = 2,
- R_PHY_ID2 = 3,
- R_PHY_MAX = 32
-};
-
-#define MINIMAC2_MTU 1530
-#define MINIMAC2_BUFFER_SIZE 2048
-
-struct MilkymistMinimac2MdioState {
- int last_clk;
- int count;
- uint32_t data;
- uint16_t data_out;
- int state;
-
- uint8_t phy_addr;
- uint8_t reg_addr;
-};
-typedef struct MilkymistMinimac2MdioState MilkymistMinimac2MdioState;
-
-struct MilkymistMinimac2State {
- SysBusDevice busdev;
- NICState *nic;
- NICConf conf;
- char *phy_model;
- MemoryRegion buffers;
- MemoryRegion regs_region;
-
- qemu_irq rx_irq;
- qemu_irq tx_irq;
-
- uint32_t regs[R_MAX];
-
- MilkymistMinimac2MdioState mdio;
-
- uint16_t phy_regs[R_PHY_MAX];
-
- uint8_t *rx0_buf;
- uint8_t *rx1_buf;
- uint8_t *tx_buf;
-};
-typedef struct MilkymistMinimac2State MilkymistMinimac2State;
-
-static const uint8_t preamble_sfd[] = {
- 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xd5
-};
-
-static void minimac2_mdio_write_reg(MilkymistMinimac2State *s,
- uint8_t phy_addr, uint8_t reg_addr, uint16_t value)
-{
- trace_milkymist_minimac2_mdio_write(phy_addr, reg_addr, value);
-
- /* nop */
-}
-
-static uint16_t minimac2_mdio_read_reg(MilkymistMinimac2State *s,
- uint8_t phy_addr, uint8_t reg_addr)
-{
- uint16_t r = s->phy_regs[reg_addr];
-
- trace_milkymist_minimac2_mdio_read(phy_addr, reg_addr, r);
-
- return r;
-}
-
-static void minimac2_update_mdio(MilkymistMinimac2State *s)
-{
- MilkymistMinimac2MdioState *m = &s->mdio;
-
- /* detect rising clk edge */
- if (m->last_clk == 0 && (s->regs[R_MDIO] & MDIO_CLK)) {
- /* shift data in */
- int bit = ((s->regs[R_MDIO] & MDIO_DO)
- && (s->regs[R_MDIO] & MDIO_OE)) ? 1 : 0;
- m->data = (m->data << 1) | bit;
-
- /* check for sync */
- if (m->data == 0xffffffff) {
- m->count = 32;
- }
-
- if (m->count == 16) {
- uint8_t start = (m->data >> 14) & 0x3;
- uint8_t op = (m->data >> 12) & 0x3;
- uint8_t ta = (m->data) & 0x3;
-
- if (start == 1 && op == MDIO_OP_WRITE && ta == 2) {
- m->state = MDIO_STATE_WRITING;
- } else if (start == 1 && op == MDIO_OP_READ && (ta & 1) == 0) {
- m->state = MDIO_STATE_READING;
- } else {
- m->state = MDIO_STATE_IDLE;
- }
-
- if (m->state != MDIO_STATE_IDLE) {
- m->phy_addr = (m->data >> 7) & 0x1f;
- m->reg_addr = (m->data >> 2) & 0x1f;
- }
-
- if (m->state == MDIO_STATE_READING) {
- m->data_out = minimac2_mdio_read_reg(s, m->phy_addr,
- m->reg_addr);
- }
- }
-
- if (m->count < 16 && m->state == MDIO_STATE_READING) {
- int bit = (m->data_out & 0x8000) ? 1 : 0;
- m->data_out <<= 1;
-
- if (bit) {
- s->regs[R_MDIO] |= MDIO_DI;
- } else {
- s->regs[R_MDIO] &= ~MDIO_DI;
- }
- }
-
- if (m->count == 0 && m->state) {
- if (m->state == MDIO_STATE_WRITING) {
- uint16_t data = m->data & 0xffff;
- minimac2_mdio_write_reg(s, m->phy_addr, m->reg_addr, data);
- }
- m->state = MDIO_STATE_IDLE;
- }
- m->count--;
- }
-
- m->last_clk = (s->regs[R_MDIO] & MDIO_CLK) ? 1 : 0;
-}
-
-static size_t assemble_frame(uint8_t *buf, size_t size,
- const uint8_t *payload, size_t payload_size)
-{
- uint32_t crc;
-
- if (size < payload_size + 12) {
- error_report("milkymist_minimac2: received too big ethernet frame");
- return 0;
- }
-
- /* prepend preamble and sfd */
- memcpy(buf, preamble_sfd, 8);
-
- /* now copy the payload */
- memcpy(buf + 8, payload, payload_size);
-
- /* pad frame if needed */
- if (payload_size < 60) {
- memset(buf + payload_size + 8, 0, 60 - payload_size);
- payload_size = 60;
- }
-
- /* append fcs */
- crc = cpu_to_le32(crc32(0, buf + 8, payload_size));
- memcpy(buf + payload_size + 8, &crc, 4);
-
- return payload_size + 12;
-}
-
-static void minimac2_tx(MilkymistMinimac2State *s)
-{
- uint32_t txcount = s->regs[R_TXCOUNT];
- uint8_t *buf = s->tx_buf;
-
- if (txcount < 64) {
- error_report("milkymist_minimac2: ethernet frame too small (%u < %u)",
- txcount, 64);
- goto err;
- }
-
- if (txcount > MINIMAC2_MTU) {
- error_report("milkymist_minimac2: MTU exceeded (%u > %u)",
- txcount, MINIMAC2_MTU);
- goto err;
- }
-
- if (memcmp(buf, preamble_sfd, 8) != 0) {
- error_report("milkymist_minimac2: frame doesn't contain the preamble "
- "and/or the SFD (%02x %02x %02x %02x %02x %02x %02x %02x)",
- buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
- goto err;
- }
-
- trace_milkymist_minimac2_tx_frame(txcount - 12);
-
- /* send packet, skipping preamble and sfd */
- qemu_send_packet_raw(qemu_get_queue(s->nic), buf + 8, txcount - 12);
-
- s->regs[R_TXCOUNT] = 0;
-
-err:
- trace_milkymist_minimac2_pulse_irq_tx();
- qemu_irq_pulse(s->tx_irq);
-}
-
-static void update_rx_interrupt(MilkymistMinimac2State *s)
-{
- if (s->regs[R_STATE0] == STATE_PENDING
- || s->regs[R_STATE1] == STATE_PENDING) {
- trace_milkymist_minimac2_raise_irq_rx();
- qemu_irq_raise(s->rx_irq);
- } else {
- trace_milkymist_minimac2_lower_irq_rx();
- qemu_irq_lower(s->rx_irq);
- }
-}
-
-static ssize_t minimac2_rx(NetClientState *nc, const uint8_t *buf, size_t size)
-{
- MilkymistMinimac2State *s = qemu_get_nic_opaque(nc);
-
- uint32_t r_count;
- uint32_t r_state;
- uint8_t *rx_buf;
-
- size_t frame_size;
-
- trace_milkymist_minimac2_rx_frame(buf, size);
-
- /* choose appropriate slot */
- if (s->regs[R_STATE0] == STATE_LOADED) {
- r_count = R_COUNT0;
- r_state = R_STATE0;
- rx_buf = s->rx0_buf;
- } else if (s->regs[R_STATE1] == STATE_LOADED) {
- r_count = R_COUNT1;
- r_state = R_STATE1;
- rx_buf = s->rx1_buf;
- } else {
- trace_milkymist_minimac2_drop_rx_frame(buf);
- return size;
- }
-
- /* assemble frame */
- frame_size = assemble_frame(rx_buf, MINIMAC2_BUFFER_SIZE, buf, size);
-
- if (frame_size == 0) {
- return size;
- }
-
- trace_milkymist_minimac2_rx_transfer(rx_buf, frame_size);
-
- /* update slot */
- s->regs[r_count] = frame_size;
- s->regs[r_state] = STATE_PENDING;
-
- update_rx_interrupt(s);
-
- return size;
-}
-
-static uint64_t
-minimac2_read(void *opaque, hwaddr addr, unsigned size)
-{
- MilkymistMinimac2State *s = opaque;
- uint32_t r = 0;
-
- addr >>= 2;
- switch (addr) {
- case R_SETUP:
- case R_MDIO:
- case R_STATE0:
- case R_COUNT0:
- case R_STATE1:
- case R_COUNT1:
- case R_TXCOUNT:
- r = s->regs[addr];
- break;
-
- default:
- error_report("milkymist_minimac2: read access to unknown register 0x"
- TARGET_FMT_plx, addr << 2);
- break;
- }
-
- trace_milkymist_minimac2_memory_read(addr << 2, r);
-
- return r;
-}
-
-static void
-minimac2_write(void *opaque, hwaddr addr, uint64_t value,
- unsigned size)
-{
- MilkymistMinimac2State *s = opaque;
-
- trace_milkymist_minimac2_memory_read(addr, value);
-
- addr >>= 2;
- switch (addr) {
- case R_MDIO:
- {
- /* MDIO_DI is read only */
- int mdio_di = (s->regs[R_MDIO] & MDIO_DI);
- s->regs[R_MDIO] = value;
- if (mdio_di) {
- s->regs[R_MDIO] |= mdio_di;
- } else {
- s->regs[R_MDIO] &= ~mdio_di;
- }
-
- minimac2_update_mdio(s);
- } break;
- case R_TXCOUNT:
- s->regs[addr] = value;
- if (value > 0) {
- minimac2_tx(s);
- }
- break;
- case R_STATE0:
- case R_STATE1:
- s->regs[addr] = value;
- update_rx_interrupt(s);
- break;
- case R_SETUP:
- case R_COUNT0:
- case R_COUNT1:
- s->regs[addr] = value;
- break;
-
- default:
- error_report("milkymist_minimac2: write access to unknown register 0x"
- TARGET_FMT_plx, addr << 2);
- break;
- }
-}
-
-static const MemoryRegionOps minimac2_ops = {
- .read = minimac2_read,
- .write = minimac2_write,
- .valid = {
- .min_access_size = 4,
- .max_access_size = 4,
- },
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static int minimac2_can_rx(NetClientState *nc)
-{
- MilkymistMinimac2State *s = qemu_get_nic_opaque(nc);
-
- if (s->regs[R_STATE0] == STATE_LOADED) {
- return 1;
- }
- if (s->regs[R_STATE1] == STATE_LOADED) {
- return 1;
- }
-
- return 0;
-}
-
-static void minimac2_cleanup(NetClientState *nc)
-{
- MilkymistMinimac2State *s = qemu_get_nic_opaque(nc);
-
- s->nic = NULL;
-}
-
-static void milkymist_minimac2_reset(DeviceState *d)
-{
- MilkymistMinimac2State *s =
- container_of(d, MilkymistMinimac2State, busdev.qdev);
- int i;
-
- for (i = 0; i < R_MAX; i++) {
- s->regs[i] = 0;
- }
- for (i = 0; i < R_PHY_MAX; i++) {
- s->phy_regs[i] = 0;
- }
-
- /* defaults */
- s->phy_regs[R_PHY_ID1] = 0x0022; /* Micrel KSZ8001L */
- s->phy_regs[R_PHY_ID2] = 0x161a;
-}
-
-static NetClientInfo net_milkymist_minimac2_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
- .size = sizeof(NICState),
- .can_receive = minimac2_can_rx,
- .receive = minimac2_rx,
- .cleanup = minimac2_cleanup,
-};
-
-static int milkymist_minimac2_init(SysBusDevice *dev)
-{
- MilkymistMinimac2State *s = FROM_SYSBUS(typeof(*s), dev);
- size_t buffers_size = TARGET_PAGE_ALIGN(3 * MINIMAC2_BUFFER_SIZE);
-
- sysbus_init_irq(dev, &s->rx_irq);
- sysbus_init_irq(dev, &s->tx_irq);
-
- memory_region_init_io(&s->regs_region, &minimac2_ops, s,
- "milkymist-minimac2", R_MAX * 4);
- sysbus_init_mmio(dev, &s->regs_region);
-
- /* register buffers memory */
- memory_region_init_ram(&s->buffers, "milkymist-minimac2.buffers",
- buffers_size);
- vmstate_register_ram_global(&s->buffers);
- s->rx0_buf = memory_region_get_ram_ptr(&s->buffers);
- s->rx1_buf = s->rx0_buf + MINIMAC2_BUFFER_SIZE;
- s->tx_buf = s->rx1_buf + MINIMAC2_BUFFER_SIZE;
-
- sysbus_init_mmio(dev, &s->buffers);
-
- qemu_macaddr_default_if_unset(&s->conf.macaddr);
- s->nic = qemu_new_nic(&net_milkymist_minimac2_info, &s->conf,
- object_get_typename(OBJECT(dev)), dev->qdev.id, s);
- qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
-
- return 0;
-}
-
-static const VMStateDescription vmstate_milkymist_minimac2_mdio = {
- .name = "milkymist-minimac2-mdio",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField[]) {
- VMSTATE_INT32(last_clk, MilkymistMinimac2MdioState),
- VMSTATE_INT32(count, MilkymistMinimac2MdioState),
- VMSTATE_UINT32(data, MilkymistMinimac2MdioState),
- VMSTATE_UINT16(data_out, MilkymistMinimac2MdioState),
- VMSTATE_INT32(state, MilkymistMinimac2MdioState),
- VMSTATE_UINT8(phy_addr, MilkymistMinimac2MdioState),
- VMSTATE_UINT8(reg_addr, MilkymistMinimac2MdioState),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static const VMStateDescription vmstate_milkymist_minimac2 = {
- .name = "milkymist-minimac2",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField[]) {
- VMSTATE_UINT32_ARRAY(regs, MilkymistMinimac2State, R_MAX),
- VMSTATE_UINT16_ARRAY(phy_regs, MilkymistMinimac2State, R_PHY_MAX),
- VMSTATE_STRUCT(mdio, MilkymistMinimac2State, 0,
- vmstate_milkymist_minimac2_mdio, MilkymistMinimac2MdioState),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static Property milkymist_minimac2_properties[] = {
- DEFINE_NIC_PROPERTIES(MilkymistMinimac2State, conf),
- DEFINE_PROP_STRING("phy_model", MilkymistMinimac2State, phy_model),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void milkymist_minimac2_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
- k->init = milkymist_minimac2_init;
- dc->reset = milkymist_minimac2_reset;
- dc->vmsd = &vmstate_milkymist_minimac2;
- dc->props = milkymist_minimac2_properties;
-}
-
-static const TypeInfo milkymist_minimac2_info = {
- .name = "milkymist-minimac2",
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(MilkymistMinimac2State),
- .class_init = milkymist_minimac2_class_init,
-};
-
-static void milkymist_minimac2_register_types(void)
-{
- type_register_static(&milkymist_minimac2_info);
-}
-
-type_init(milkymist_minimac2_register_types)
common-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o
common-obj-$(CONFIG_CADENCE) += cadence_gem.o
+common-obj-$(CONFIG_STELLARIS_ENET) += stellaris_enet.o
+common-obj-$(CONFIG_LANCE) += lance.o
+
+obj-$(CONFIG_ETRAXFS) += etraxfs_eth.o
+obj-$(CONFIG_COLDFIRE) += mcf_fec.o
+obj-$(CONFIG_MILKYMIST) += milkymist-minimac2.o
+obj-$(CONFIG_PSERIES) += spapr_llan.o
+obj-$(CONFIG_XILINX_ETHLITE) += xilinx_ethlite.o
obj-$(CONFIG_VIRTIO) += virtio-net.o
obj-y += vhost_net.o
--- /dev/null
+/*
+ * QEMU ETRAX Ethernet Controller.
+ *
+ * Copyright (c) 2008 Edgar E. Iglesias, Axis Communications AB.
+ *
+ * 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 <stdio.h>
+#include "hw/sysbus.h"
+#include "net/net.h"
+#include "hw/cris/etraxfs.h"
+
+#define D(x)
+
+/* Advertisement control register. */
+#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */
+#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */
+#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */
+#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */
+
+/*
+ * The MDIO extensions in the TDK PHY model were reversed engineered from the
+ * linux driver (PHYID and Diagnostics reg).
+ * TODO: Add friendly names for the register nums.
+ */
+struct qemu_phy
+{
+ uint32_t regs[32];
+
+ int link;
+
+ unsigned int (*read)(struct qemu_phy *phy, unsigned int req);
+ void (*write)(struct qemu_phy *phy, unsigned int req, unsigned int data);
+};
+
+static unsigned int tdk_read(struct qemu_phy *phy, unsigned int req)
+{
+ int regnum;
+ unsigned r = 0;
+
+ regnum = req & 0x1f;
+
+ switch (regnum) {
+ case 1:
+ if (!phy->link) {
+ break;
+ }
+ /* MR1. */
+ /* Speeds and modes. */
+ r |= (1 << 13) | (1 << 14);
+ r |= (1 << 11) | (1 << 12);
+ r |= (1 << 5); /* Autoneg complete. */
+ r |= (1 << 3); /* Autoneg able. */
+ r |= (1 << 2); /* link. */
+ break;
+ case 5:
+ /* Link partner ability.
+ We are kind; always agree with whatever best mode
+ the guest advertises. */
+ r = 1 << 14; /* Success. */
+ /* Copy advertised modes. */
+ r |= phy->regs[4] & (15 << 5);
+ /* Autoneg support. */
+ r |= 1;
+ break;
+ case 18:
+ {
+ /* Diagnostics reg. */
+ int duplex = 0;
+ int speed_100 = 0;
+
+ if (!phy->link) {
+ break;
+ }
+
+ /* Are we advertising 100 half or 100 duplex ? */
+ speed_100 = !!(phy->regs[4] & ADVERTISE_100HALF);
+ speed_100 |= !!(phy->regs[4] & ADVERTISE_100FULL);
+
+ /* Are we advertising 10 duplex or 100 duplex ? */
+ duplex = !!(phy->regs[4] & ADVERTISE_100FULL);
+ duplex |= !!(phy->regs[4] & ADVERTISE_10FULL);
+ r = (speed_100 << 10) | (duplex << 11);
+ }
+ break;
+
+ default:
+ r = phy->regs[regnum];
+ break;
+ }
+ D(printf("\n%s %x = reg[%d]\n", __func__, r, regnum));
+ return r;
+}
+
+static void
+tdk_write(struct qemu_phy *phy, unsigned int req, unsigned int data)
+{
+ int regnum;
+
+ regnum = req & 0x1f;
+ D(printf("%s reg[%d] = %x\n", __func__, regnum, data));
+ switch (regnum) {
+ default:
+ phy->regs[regnum] = data;
+ break;
+ }
+}
+
+static void
+tdk_init(struct qemu_phy *phy)
+{
+ phy->regs[0] = 0x3100;
+ /* PHY Id. */
+ phy->regs[2] = 0x0300;
+ phy->regs[3] = 0xe400;
+ /* Autonegotiation advertisement reg. */
+ phy->regs[4] = 0x01E1;
+ phy->link = 1;
+
+ phy->read = tdk_read;
+ phy->write = tdk_write;
+}
+
+struct qemu_mdio
+{
+ /* bus. */
+ int mdc;
+ int mdio;
+
+ /* decoder. */
+ enum {
+ PREAMBLE,
+ SOF,
+ OPC,
+ ADDR,
+ REQ,
+ TURNAROUND,
+ DATA
+ } state;
+ unsigned int drive;
+
+ unsigned int cnt;
+ unsigned int addr;
+ unsigned int opc;
+ unsigned int req;
+ unsigned int data;
+
+ struct qemu_phy *devs[32];
+};
+
+static void
+mdio_attach(struct qemu_mdio *bus, struct qemu_phy *phy, unsigned int addr)
+{
+ bus->devs[addr & 0x1f] = phy;
+}
+
+#ifdef USE_THIS_DEAD_CODE
+static void
+mdio_detach(struct qemu_mdio *bus, struct qemu_phy *phy, unsigned int addr)
+{
+ bus->devs[addr & 0x1f] = NULL;
+}
+#endif
+
+static void mdio_read_req(struct qemu_mdio *bus)
+{
+ struct qemu_phy *phy;
+
+ phy = bus->devs[bus->addr];
+ if (phy && phy->read) {
+ bus->data = phy->read(phy, bus->req);
+ } else {
+ bus->data = 0xffff;
+ }
+}
+
+static void mdio_write_req(struct qemu_mdio *bus)
+{
+ struct qemu_phy *phy;
+
+ phy = bus->devs[bus->addr];
+ if (phy && phy->write) {
+ phy->write(phy, bus->req, bus->data);
+ }
+}
+
+static void mdio_cycle(struct qemu_mdio *bus)
+{
+ bus->cnt++;
+
+ D(printf("mdc=%d mdio=%d state=%d cnt=%d drv=%d\n",
+ bus->mdc, bus->mdio, bus->state, bus->cnt, bus->drive));
+#if 0
+ if (bus->mdc) {
+ printf("%d", bus->mdio);
+ }
+#endif
+ switch (bus->state) {
+ case PREAMBLE:
+ if (bus->mdc) {
+ if (bus->cnt >= (32 * 2) && !bus->mdio) {
+ bus->cnt = 0;
+ bus->state = SOF;
+ bus->data = 0;
+ }
+ }
+ break;
+ case SOF:
+ if (bus->mdc) {
+ if (bus->mdio != 1) {
+ printf("WARNING: no SOF\n");
+ }
+ if (bus->cnt == 1*2) {
+ bus->cnt = 0;
+ bus->opc = 0;
+ bus->state = OPC;
+ }
+ }
+ break;
+ case OPC:
+ if (bus->mdc) {
+ bus->opc <<= 1;
+ bus->opc |= bus->mdio & 1;
+ if (bus->cnt == 2*2) {
+ bus->cnt = 0;
+ bus->addr = 0;
+ bus->state = ADDR;
+ }
+ }
+ break;
+ case ADDR:
+ if (bus->mdc) {
+ bus->addr <<= 1;
+ bus->addr |= bus->mdio & 1;
+
+ if (bus->cnt == 5*2) {
+ bus->cnt = 0;
+ bus->req = 0;
+ bus->state = REQ;
+ }
+ }
+ break;
+ case REQ:
+ if (bus->mdc) {
+ bus->req <<= 1;
+ bus->req |= bus->mdio & 1;
+ if (bus->cnt == 5*2) {
+ bus->cnt = 0;
+ bus->state = TURNAROUND;
+ }
+ }
+ break;
+ case TURNAROUND:
+ if (bus->mdc && bus->cnt == 2*2) {
+ bus->mdio = 0;
+ bus->cnt = 0;
+
+ if (bus->opc == 2) {
+ bus->drive = 1;
+ mdio_read_req(bus);
+ bus->mdio = bus->data & 1;
+ }
+ bus->state = DATA;
+ }
+ break;
+ case DATA:
+ if (!bus->mdc) {
+ if (bus->drive) {
+ bus->mdio = !!(bus->data & (1 << 15));
+ bus->data <<= 1;
+ }
+ } else {
+ if (!bus->drive) {
+ bus->data <<= 1;
+ bus->data |= bus->mdio;
+ }
+ if (bus->cnt == 16 * 2) {
+ bus->cnt = 0;
+ bus->state = PREAMBLE;
+ if (!bus->drive) {
+ mdio_write_req(bus);
+ }
+ bus->drive = 0;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/* ETRAX-FS Ethernet MAC block starts here. */
+
+#define RW_MA0_LO 0x00
+#define RW_MA0_HI 0x01
+#define RW_MA1_LO 0x02
+#define RW_MA1_HI 0x03
+#define RW_GA_LO 0x04
+#define RW_GA_HI 0x05
+#define RW_GEN_CTRL 0x06
+#define RW_REC_CTRL 0x07
+#define RW_TR_CTRL 0x08
+#define RW_CLR_ERR 0x09
+#define RW_MGM_CTRL 0x0a
+#define R_STAT 0x0b
+#define FS_ETH_MAX_REGS 0x17
+
+struct fs_eth
+{
+ SysBusDevice busdev;
+ MemoryRegion mmio;
+ NICState *nic;
+ NICConf conf;
+
+ /* Two addrs in the filter. */
+ uint8_t macaddr[2][6];
+ uint32_t regs[FS_ETH_MAX_REGS];
+
+ union {
+ void *vdma_out;
+ struct etraxfs_dma_client *dma_out;
+ };
+ union {
+ void *vdma_in;
+ struct etraxfs_dma_client *dma_in;
+ };
+
+ /* MDIO bus. */
+ struct qemu_mdio mdio_bus;
+ unsigned int phyaddr;
+ int duplex_mismatch;
+
+ /* PHY. */
+ struct qemu_phy phy;
+};
+
+static void eth_validate_duplex(struct fs_eth *eth)
+{
+ struct qemu_phy *phy;
+ unsigned int phy_duplex;
+ unsigned int mac_duplex;
+ int new_mm = 0;
+
+ phy = eth->mdio_bus.devs[eth->phyaddr];
+ phy_duplex = !!(phy->read(phy, 18) & (1 << 11));
+ mac_duplex = !!(eth->regs[RW_REC_CTRL] & 128);
+
+ if (mac_duplex != phy_duplex) {
+ new_mm = 1;
+ }
+
+ if (eth->regs[RW_GEN_CTRL] & 1) {
+ if (new_mm != eth->duplex_mismatch) {
+ if (new_mm) {
+ printf("HW: WARNING ETH duplex mismatch MAC=%d PHY=%d\n",
+ mac_duplex, phy_duplex);
+ } else {
+ printf("HW: ETH duplex ok.\n");
+ }
+ }
+ eth->duplex_mismatch = new_mm;
+ }
+}
+
+static uint64_t
+eth_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ struct fs_eth *eth = opaque;
+ uint32_t r = 0;
+
+ addr >>= 2;
+
+ switch (addr) {
+ case R_STAT:
+ r = eth->mdio_bus.mdio & 1;
+ break;
+ default:
+ r = eth->regs[addr];
+ D(printf("%s %x\n", __func__, addr * 4));
+ break;
+ }
+ return r;
+}
+
+static void eth_update_ma(struct fs_eth *eth, int ma)
+{
+ int reg;
+ int i = 0;
+
+ ma &= 1;
+
+ reg = RW_MA0_LO;
+ if (ma) {
+ reg = RW_MA1_LO;
+ }
+
+ eth->macaddr[ma][i++] = eth->regs[reg];
+ eth->macaddr[ma][i++] = eth->regs[reg] >> 8;
+ eth->macaddr[ma][i++] = eth->regs[reg] >> 16;
+ eth->macaddr[ma][i++] = eth->regs[reg] >> 24;
+ eth->macaddr[ma][i++] = eth->regs[reg + 1];
+ eth->macaddr[ma][i] = eth->regs[reg + 1] >> 8;
+
+ D(printf("set mac%d=%x.%x.%x.%x.%x.%x\n", ma,
+ eth->macaddr[ma][0], eth->macaddr[ma][1],
+ eth->macaddr[ma][2], eth->macaddr[ma][3],
+ eth->macaddr[ma][4], eth->macaddr[ma][5]));
+}
+
+static void
+eth_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ struct fs_eth *eth = opaque;
+ uint32_t value = val64;
+
+ addr >>= 2;
+ switch (addr) {
+ case RW_MA0_LO:
+ case RW_MA0_HI:
+ eth->regs[addr] = value;
+ eth_update_ma(eth, 0);
+ break;
+ case RW_MA1_LO:
+ case RW_MA1_HI:
+ eth->regs[addr] = value;
+ eth_update_ma(eth, 1);
+ break;
+
+ case RW_MGM_CTRL:
+ /* Attach an MDIO/PHY abstraction. */
+ if (value & 2) {
+ eth->mdio_bus.mdio = value & 1;
+ }
+ if (eth->mdio_bus.mdc != (value & 4)) {
+ mdio_cycle(ð->mdio_bus);
+ eth_validate_duplex(eth);
+ }
+ eth->mdio_bus.mdc = !!(value & 4);
+ eth->regs[addr] = value;
+ break;
+
+ case RW_REC_CTRL:
+ eth->regs[addr] = value;
+ eth_validate_duplex(eth);
+ break;
+
+ default:
+ eth->regs[addr] = value;
+ D(printf("%s %x %x\n", __func__, addr, value));
+ break;
+ }
+}
+
+/* The ETRAX FS has a groupt address table (GAT) which works like a k=1 bloom
+ filter dropping group addresses we have not joined. The filter has 64
+ bits (m). The has function is a simple nible xor of the group addr. */
+static int eth_match_groupaddr(struct fs_eth *eth, const unsigned char *sa)
+{
+ unsigned int hsh;
+ int m_individual = eth->regs[RW_REC_CTRL] & 4;
+ int match;
+
+ /* First bit on the wire of a MAC address signals multicast or
+ physical address. */
+ if (!m_individual && !(sa[0] & 1)) {
+ return 0;
+ }
+
+ /* Calculate the hash index for the GA registers. */
+ hsh = 0;
+ hsh ^= (*sa) & 0x3f;
+ hsh ^= ((*sa) >> 6) & 0x03;
+ ++sa;
+ hsh ^= ((*sa) << 2) & 0x03c;
+ hsh ^= ((*sa) >> 4) & 0xf;
+ ++sa;
+ hsh ^= ((*sa) << 4) & 0x30;
+ hsh ^= ((*sa) >> 2) & 0x3f;
+ ++sa;
+ hsh ^= (*sa) & 0x3f;
+ hsh ^= ((*sa) >> 6) & 0x03;
+ ++sa;
+ hsh ^= ((*sa) << 2) & 0x03c;
+ hsh ^= ((*sa) >> 4) & 0xf;
+ ++sa;
+ hsh ^= ((*sa) << 4) & 0x30;
+ hsh ^= ((*sa) >> 2) & 0x3f;
+
+ hsh &= 63;
+ if (hsh > 31) {
+ match = eth->regs[RW_GA_HI] & (1 << (hsh - 32));
+ } else {
+ match = eth->regs[RW_GA_LO] & (1 << hsh);
+ }
+ D(printf("hsh=%x ga=%x.%x mtch=%d\n", hsh,
+ eth->regs[RW_GA_HI], eth->regs[RW_GA_LO], match));
+ return match;
+}
+
+static int eth_can_receive(NetClientState *nc)
+{
+ return 1;
+}
+
+static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+ unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+ struct fs_eth *eth = qemu_get_nic_opaque(nc);
+ int use_ma0 = eth->regs[RW_REC_CTRL] & 1;
+ int use_ma1 = eth->regs[RW_REC_CTRL] & 2;
+ int r_bcast = eth->regs[RW_REC_CTRL] & 8;
+
+ if (size < 12) {
+ return -1;
+ }
+
+ D(printf("%x.%x.%x.%x.%x.%x ma=%d %d bc=%d\n",
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5],
+ use_ma0, use_ma1, r_bcast));
+
+ /* Does the frame get through the address filters? */
+ if ((!use_ma0 || memcmp(buf, eth->macaddr[0], 6))
+ && (!use_ma1 || memcmp(buf, eth->macaddr[1], 6))
+ && (!r_bcast || memcmp(buf, sa_bcast, 6))
+ && !eth_match_groupaddr(eth, buf)) {
+ return size;
+ }
+
+ /* FIXME: Find another way to pass on the fake csum. */
+ etraxfs_dmac_input(eth->dma_in, (void *)buf, size + 4, 1);
+
+ return size;
+}
+
+static int eth_tx_push(void *opaque, unsigned char *buf, int len, bool eop)
+{
+ struct fs_eth *eth = opaque;
+
+ D(printf("%s buf=%p len=%d\n", __func__, buf, len));
+ qemu_send_packet(qemu_get_queue(eth->nic), buf, len);
+ return len;
+}
+
+static void eth_set_link(NetClientState *nc)
+{
+ struct fs_eth *eth = qemu_get_nic_opaque(nc);
+ D(printf("%s %d\n", __func__, nc->link_down));
+ eth->phy.link = !nc->link_down;
+}
+
+static const MemoryRegionOps eth_ops = {
+ .read = eth_read,
+ .write = eth_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+static void eth_cleanup(NetClientState *nc)
+{
+ struct fs_eth *eth = qemu_get_nic_opaque(nc);
+
+ /* Disconnect the client. */
+ eth->dma_out->client.push = NULL;
+ eth->dma_out->client.opaque = NULL;
+ eth->dma_in->client.opaque = NULL;
+ eth->dma_in->client.pull = NULL;
+ g_free(eth);
+}
+
+static NetClientInfo net_etraxfs_info = {
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .size = sizeof(NICState),
+ .can_receive = eth_can_receive,
+ .receive = eth_receive,
+ .cleanup = eth_cleanup,
+ .link_status_changed = eth_set_link,
+};
+
+static int fs_eth_init(SysBusDevice *dev)
+{
+ struct fs_eth *s = FROM_SYSBUS(typeof(*s), dev);
+
+ if (!s->dma_out || !s->dma_in) {
+ hw_error("Unconnected ETRAX-FS Ethernet MAC.\n");
+ }
+
+ s->dma_out->client.push = eth_tx_push;
+ s->dma_out->client.opaque = s;
+ s->dma_in->client.opaque = s;
+ s->dma_in->client.pull = NULL;
+
+ memory_region_init_io(&s->mmio, ð_ops, s, "etraxfs-eth", 0x5c);
+ sysbus_init_mmio(dev, &s->mmio);
+
+ qemu_macaddr_default_if_unset(&s->conf.macaddr);
+ s->nic = qemu_new_nic(&net_etraxfs_info, &s->conf,
+ object_get_typename(OBJECT(s)), dev->qdev.id, s);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+
+
+ tdk_init(&s->phy);
+ mdio_attach(&s->mdio_bus, &s->phy, s->phyaddr);
+ return 0;
+}
+
+static Property etraxfs_eth_properties[] = {
+ DEFINE_PROP_UINT32("phyaddr", struct fs_eth, phyaddr, 1),
+ DEFINE_PROP_PTR("dma_out", struct fs_eth, vdma_out),
+ DEFINE_PROP_PTR("dma_in", struct fs_eth, vdma_in),
+ DEFINE_NIC_PROPERTIES(struct fs_eth, conf),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void etraxfs_eth_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = fs_eth_init;
+ dc->props = etraxfs_eth_properties;
+}
+
+static const TypeInfo etraxfs_eth_info = {
+ .name = "etraxfs-eth",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(struct fs_eth),
+ .class_init = etraxfs_eth_class_init,
+};
+
+static void etraxfs_eth_register_types(void)
+{
+ type_register_static(&etraxfs_eth_info);
+}
+
+type_init(etraxfs_eth_register_types)
--- /dev/null
+/*
+ * QEMU AMD PC-Net II (Am79C970A) emulation
+ *
+ * Copyright (c) 2004 Antony T Curtis
+ *
+ * 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.
+ */
+
+/* This software was written to be compatible with the specification:
+ * AMD Am79C970A PCnet-PCI II Ethernet Controller Data-Sheet
+ * AMD Publication# 19436 Rev:E Amendment/0 Issue Date: June 2000
+ */
+
+/*
+ * On Sparc32, this is the Lance (Am7990) part of chip STP2000 (Master I/O), also
+ * produced as NCR89C100. See
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt
+ * and
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR92C990.txt
+ */
+
+#include "hw/sysbus.h"
+#include "net/net.h"
+#include "qemu/timer.h"
+#include "qemu/sockets.h"
+#include "hw/sparc/sun4m.h"
+#include "hw/pcnet.h"
+#include "trace.h"
+
+typedef struct {
+ SysBusDevice busdev;
+ PCNetState state;
+} SysBusPCNetState;
+
+static void parent_lance_reset(void *opaque, int irq, int level)
+{
+ SysBusPCNetState *d = opaque;
+ if (level)
+ pcnet_h_reset(&d->state);
+}
+
+static void lance_mem_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ SysBusPCNetState *d = opaque;
+
+ trace_lance_mem_writew(addr, val & 0xffff);
+ pcnet_ioport_writew(&d->state, addr, val & 0xffff);
+}
+
+static uint64_t lance_mem_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ SysBusPCNetState *d = opaque;
+ uint32_t val;
+
+ val = pcnet_ioport_readw(&d->state, addr);
+ trace_lance_mem_readw(addr, val & 0xffff);
+ return val & 0xffff;
+}
+
+static const MemoryRegionOps lance_mem_ops = {
+ .read = lance_mem_read,
+ .write = lance_mem_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 2,
+ .max_access_size = 2,
+ },
+};
+
+static void lance_cleanup(NetClientState *nc)
+{
+ PCNetState *d = qemu_get_nic_opaque(nc);
+
+ pcnet_common_cleanup(d);
+}
+
+static NetClientInfo net_lance_info = {
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .size = sizeof(NICState),
+ .can_receive = pcnet_can_receive,
+ .receive = pcnet_receive,
+ .link_status_changed = pcnet_set_link_status,
+ .cleanup = lance_cleanup,
+};
+
+static const VMStateDescription vmstate_lance = {
+ .name = "pcnet",
+ .version_id = 3,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .fields = (VMStateField []) {
+ VMSTATE_STRUCT(state, SysBusPCNetState, 0, vmstate_pcnet, PCNetState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int lance_init(SysBusDevice *dev)
+{
+ SysBusPCNetState *d = FROM_SYSBUS(SysBusPCNetState, dev);
+ PCNetState *s = &d->state;
+
+ memory_region_init_io(&s->mmio, &lance_mem_ops, d, "lance-mmio", 4);
+
+ qdev_init_gpio_in(&dev->qdev, parent_lance_reset, 1);
+
+ sysbus_init_mmio(dev, &s->mmio);
+
+ sysbus_init_irq(dev, &s->irq);
+
+ s->phys_mem_read = ledma_memory_read;
+ s->phys_mem_write = ledma_memory_write;
+ return pcnet_common_init(&dev->qdev, s, &net_lance_info);
+}
+
+static void lance_reset(DeviceState *dev)
+{
+ SysBusPCNetState *d = DO_UPCAST(SysBusPCNetState, busdev.qdev, dev);
+
+ pcnet_h_reset(&d->state);
+}
+
+static Property lance_properties[] = {
+ DEFINE_PROP_PTR("dma", SysBusPCNetState, state.dma_opaque),
+ DEFINE_NIC_PROPERTIES(SysBusPCNetState, state.conf),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void lance_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = lance_init;
+ dc->fw_name = "ethernet";
+ dc->reset = lance_reset;
+ dc->vmsd = &vmstate_lance;
+ dc->props = lance_properties;
+}
+
+static const TypeInfo lance_info = {
+ .name = "lance",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SysBusPCNetState),
+ .class_init = lance_class_init,
+};
+
+static void lance_register_types(void)
+{
+ type_register_static(&lance_info);
+}
+
+type_init(lance_register_types)
--- /dev/null
+/*
+ * ColdFire Fast Ethernet Controller emulation.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ * This code is licensed under the GPL
+ */
+#include "hw/hw.h"
+#include "net/net.h"
+#include "hw/m68k/mcf.h"
+/* For crc32 */
+#include <zlib.h>
+#include "exec/address-spaces.h"
+
+//#define DEBUG_FEC 1
+
+#ifdef DEBUG_FEC
+#define DPRINTF(fmt, ...) \
+do { printf("mcf_fec: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#endif
+
+#define FEC_MAX_FRAME_SIZE 2032
+
+typedef struct {
+ MemoryRegion *sysmem;
+ MemoryRegion iomem;
+ qemu_irq *irq;
+ NICState *nic;
+ NICConf conf;
+ uint32_t irq_state;
+ uint32_t eir;
+ uint32_t eimr;
+ int rx_enabled;
+ uint32_t rx_descriptor;
+ uint32_t tx_descriptor;
+ uint32_t ecr;
+ uint32_t mmfr;
+ uint32_t mscr;
+ uint32_t rcr;
+ uint32_t tcr;
+ uint32_t tfwr;
+ uint32_t rfsr;
+ uint32_t erdsr;
+ uint32_t etdsr;
+ uint32_t emrbr;
+} mcf_fec_state;
+
+#define FEC_INT_HB 0x80000000
+#define FEC_INT_BABR 0x40000000
+#define FEC_INT_BABT 0x20000000
+#define FEC_INT_GRA 0x10000000
+#define FEC_INT_TXF 0x08000000
+#define FEC_INT_TXB 0x04000000
+#define FEC_INT_RXF 0x02000000
+#define FEC_INT_RXB 0x01000000
+#define FEC_INT_MII 0x00800000
+#define FEC_INT_EB 0x00400000
+#define FEC_INT_LC 0x00200000
+#define FEC_INT_RL 0x00100000
+#define FEC_INT_UN 0x00080000
+
+#define FEC_EN 2
+#define FEC_RESET 1
+
+/* Map interrupt flags onto IRQ lines. */
+#define FEC_NUM_IRQ 13
+static const uint32_t mcf_fec_irq_map[FEC_NUM_IRQ] = {
+ FEC_INT_TXF,
+ FEC_INT_TXB,
+ FEC_INT_UN,
+ FEC_INT_RL,
+ FEC_INT_RXF,
+ FEC_INT_RXB,
+ FEC_INT_MII,
+ FEC_INT_LC,
+ FEC_INT_HB,
+ FEC_INT_GRA,
+ FEC_INT_EB,
+ FEC_INT_BABT,
+ FEC_INT_BABR
+};
+
+/* Buffer Descriptor. */
+typedef struct {
+ uint16_t flags;
+ uint16_t length;
+ uint32_t data;
+} mcf_fec_bd;
+
+#define FEC_BD_R 0x8000
+#define FEC_BD_E 0x8000
+#define FEC_BD_O1 0x4000
+#define FEC_BD_W 0x2000
+#define FEC_BD_O2 0x1000
+#define FEC_BD_L 0x0800
+#define FEC_BD_TC 0x0400
+#define FEC_BD_ABC 0x0200
+#define FEC_BD_M 0x0100
+#define FEC_BD_BC 0x0080
+#define FEC_BD_MC 0x0040
+#define FEC_BD_LG 0x0020
+#define FEC_BD_NO 0x0010
+#define FEC_BD_CR 0x0004
+#define FEC_BD_OV 0x0002
+#define FEC_BD_TR 0x0001
+
+static void mcf_fec_read_bd(mcf_fec_bd *bd, uint32_t addr)
+{
+ cpu_physical_memory_read(addr, (uint8_t *)bd, sizeof(*bd));
+ be16_to_cpus(&bd->flags);
+ be16_to_cpus(&bd->length);
+ be32_to_cpus(&bd->data);
+}
+
+static void mcf_fec_write_bd(mcf_fec_bd *bd, uint32_t addr)
+{
+ mcf_fec_bd tmp;
+ tmp.flags = cpu_to_be16(bd->flags);
+ tmp.length = cpu_to_be16(bd->length);
+ tmp.data = cpu_to_be32(bd->data);
+ cpu_physical_memory_write(addr, (uint8_t *)&tmp, sizeof(tmp));
+}
+
+static void mcf_fec_update(mcf_fec_state *s)
+{
+ uint32_t active;
+ uint32_t changed;
+ uint32_t mask;
+ int i;
+
+ active = s->eir & s->eimr;
+ changed = active ^s->irq_state;
+ for (i = 0; i < FEC_NUM_IRQ; i++) {
+ mask = mcf_fec_irq_map[i];
+ if (changed & mask) {
+ DPRINTF("IRQ %d = %d\n", i, (active & mask) != 0);
+ qemu_set_irq(s->irq[i], (active & mask) != 0);
+ }
+ }
+ s->irq_state = active;
+}
+
+static void mcf_fec_do_tx(mcf_fec_state *s)
+{
+ uint32_t addr;
+ mcf_fec_bd bd;
+ int frame_size;
+ int len;
+ uint8_t frame[FEC_MAX_FRAME_SIZE];
+ uint8_t *ptr;
+
+ DPRINTF("do_tx\n");
+ ptr = frame;
+ frame_size = 0;
+ addr = s->tx_descriptor;
+ while (1) {
+ mcf_fec_read_bd(&bd, addr);
+ DPRINTF("tx_bd %x flags %04x len %d data %08x\n",
+ addr, bd.flags, bd.length, bd.data);
+ if ((bd.flags & FEC_BD_R) == 0) {
+ /* Run out of descriptors to transmit. */
+ break;
+ }
+ len = bd.length;
+ if (frame_size + len > FEC_MAX_FRAME_SIZE) {
+ len = FEC_MAX_FRAME_SIZE - frame_size;
+ s->eir |= FEC_INT_BABT;
+ }
+ cpu_physical_memory_read(bd.data, ptr, len);
+ ptr += len;
+ frame_size += len;
+ if (bd.flags & FEC_BD_L) {
+ /* Last buffer in frame. */
+ DPRINTF("Sending packet\n");
+ qemu_send_packet(qemu_get_queue(s->nic), frame, len);
+ ptr = frame;
+ frame_size = 0;
+ s->eir |= FEC_INT_TXF;
+ }
+ s->eir |= FEC_INT_TXB;
+ bd.flags &= ~FEC_BD_R;
+ /* Write back the modified descriptor. */
+ mcf_fec_write_bd(&bd, addr);
+ /* Advance to the next descriptor. */
+ if ((bd.flags & FEC_BD_W) != 0) {
+ addr = s->etdsr;
+ } else {
+ addr += 8;
+ }
+ }
+ s->tx_descriptor = addr;
+}
+
+static void mcf_fec_enable_rx(mcf_fec_state *s)
+{
+ mcf_fec_bd bd;
+
+ mcf_fec_read_bd(&bd, s->rx_descriptor);
+ s->rx_enabled = ((bd.flags & FEC_BD_E) != 0);
+ if (!s->rx_enabled)
+ DPRINTF("RX buffer full\n");
+}
+
+static void mcf_fec_reset(mcf_fec_state *s)
+{
+ s->eir = 0;
+ s->eimr = 0;
+ s->rx_enabled = 0;
+ s->ecr = 0;
+ s->mscr = 0;
+ s->rcr = 0x05ee0001;
+ s->tcr = 0;
+ s->tfwr = 0;
+ s->rfsr = 0x500;
+}
+
+static uint64_t mcf_fec_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ mcf_fec_state *s = (mcf_fec_state *)opaque;
+ switch (addr & 0x3ff) {
+ case 0x004: return s->eir;
+ case 0x008: return s->eimr;
+ case 0x010: return s->rx_enabled ? (1 << 24) : 0; /* RDAR */
+ case 0x014: return 0; /* TDAR */
+ case 0x024: return s->ecr;
+ case 0x040: return s->mmfr;
+ case 0x044: return s->mscr;
+ case 0x064: return 0; /* MIBC */
+ case 0x084: return s->rcr;
+ case 0x0c4: return s->tcr;
+ case 0x0e4: /* PALR */
+ return (s->conf.macaddr.a[0] << 24) | (s->conf.macaddr.a[1] << 16)
+ | (s->conf.macaddr.a[2] << 8) | s->conf.macaddr.a[3];
+ break;
+ case 0x0e8: /* PAUR */
+ return (s->conf.macaddr.a[4] << 24) | (s->conf.macaddr.a[5] << 16) | 0x8808;
+ case 0x0ec: return 0x10000; /* OPD */
+ case 0x118: return 0;
+ case 0x11c: return 0;
+ case 0x120: return 0;
+ case 0x124: return 0;
+ case 0x144: return s->tfwr;
+ case 0x14c: return 0x600;
+ case 0x150: return s->rfsr;
+ case 0x180: return s->erdsr;
+ case 0x184: return s->etdsr;
+ case 0x188: return s->emrbr;
+ default:
+ hw_error("mcf_fec_read: Bad address 0x%x\n", (int)addr);
+ return 0;
+ }
+}
+
+static void mcf_fec_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ mcf_fec_state *s = (mcf_fec_state *)opaque;
+ switch (addr & 0x3ff) {
+ case 0x004:
+ s->eir &= ~value;
+ break;
+ case 0x008:
+ s->eimr = value;
+ break;
+ case 0x010: /* RDAR */
+ if ((s->ecr & FEC_EN) && !s->rx_enabled) {
+ DPRINTF("RX enable\n");
+ mcf_fec_enable_rx(s);
+ }
+ break;
+ case 0x014: /* TDAR */
+ if (s->ecr & FEC_EN) {
+ mcf_fec_do_tx(s);
+ }
+ break;
+ case 0x024:
+ s->ecr = value;
+ if (value & FEC_RESET) {
+ DPRINTF("Reset\n");
+ mcf_fec_reset(s);
+ }
+ if ((s->ecr & FEC_EN) == 0) {
+ s->rx_enabled = 0;
+ }
+ break;
+ case 0x040:
+ /* TODO: Implement MII. */
+ s->mmfr = value;
+ break;
+ case 0x044:
+ s->mscr = value & 0xfe;
+ break;
+ case 0x064:
+ /* TODO: Implement MIB. */
+ break;
+ case 0x084:
+ s->rcr = value & 0x07ff003f;
+ /* TODO: Implement LOOP mode. */
+ break;
+ case 0x0c4: /* TCR */
+ /* We transmit immediately, so raise GRA immediately. */
+ s->tcr = value;
+ if (value & 1)
+ s->eir |= FEC_INT_GRA;
+ break;
+ case 0x0e4: /* PALR */
+ s->conf.macaddr.a[0] = value >> 24;
+ s->conf.macaddr.a[1] = value >> 16;
+ s->conf.macaddr.a[2] = value >> 8;
+ s->conf.macaddr.a[3] = value;
+ break;
+ case 0x0e8: /* PAUR */
+ s->conf.macaddr.a[4] = value >> 24;
+ s->conf.macaddr.a[5] = value >> 16;
+ break;
+ case 0x0ec:
+ /* OPD */
+ break;
+ case 0x118:
+ case 0x11c:
+ case 0x120:
+ case 0x124:
+ /* TODO: implement MAC hash filtering. */
+ break;
+ case 0x144:
+ s->tfwr = value & 3;
+ break;
+ case 0x14c:
+ /* FRBR writes ignored. */
+ break;
+ case 0x150:
+ s->rfsr = (value & 0x3fc) | 0x400;
+ break;
+ case 0x180:
+ s->erdsr = value & ~3;
+ s->rx_descriptor = s->erdsr;
+ break;
+ case 0x184:
+ s->etdsr = value & ~3;
+ s->tx_descriptor = s->etdsr;
+ break;
+ case 0x188:
+ s->emrbr = value & 0x7f0;
+ break;
+ default:
+ hw_error("mcf_fec_write Bad address 0x%x\n", (int)addr);
+ }
+ mcf_fec_update(s);
+}
+
+static int mcf_fec_can_receive(NetClientState *nc)
+{
+ mcf_fec_state *s = qemu_get_nic_opaque(nc);
+ return s->rx_enabled;
+}
+
+static ssize_t mcf_fec_receive(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+ mcf_fec_state *s = qemu_get_nic_opaque(nc);
+ mcf_fec_bd bd;
+ uint32_t flags = 0;
+ uint32_t addr;
+ uint32_t crc;
+ uint32_t buf_addr;
+ uint8_t *crc_ptr;
+ unsigned int buf_len;
+
+ DPRINTF("do_rx len %d\n", size);
+ if (!s->rx_enabled) {
+ fprintf(stderr, "mcf_fec_receive: Unexpected packet\n");
+ }
+ /* 4 bytes for the CRC. */
+ size += 4;
+ crc = cpu_to_be32(crc32(~0, buf, size));
+ crc_ptr = (uint8_t *)&crc;
+ /* Huge frames are truncted. */
+ if (size > FEC_MAX_FRAME_SIZE) {
+ size = FEC_MAX_FRAME_SIZE;
+ flags |= FEC_BD_TR | FEC_BD_LG;
+ }
+ /* Frames larger than the user limit just set error flags. */
+ if (size > (s->rcr >> 16)) {
+ flags |= FEC_BD_LG;
+ }
+ addr = s->rx_descriptor;
+ while (size > 0) {
+ mcf_fec_read_bd(&bd, addr);
+ if ((bd.flags & FEC_BD_E) == 0) {
+ /* No descriptors available. Bail out. */
+ /* FIXME: This is wrong. We should probably either save the
+ remainder for when more RX buffers are available, or
+ flag an error. */
+ fprintf(stderr, "mcf_fec: Lost end of frame\n");
+ break;
+ }
+ buf_len = (size <= s->emrbr) ? size: s->emrbr;
+ bd.length = buf_len;
+ size -= buf_len;
+ DPRINTF("rx_bd %x length %d\n", addr, bd.length);
+ /* The last 4 bytes are the CRC. */
+ if (size < 4)
+ buf_len += size - 4;
+ buf_addr = bd.data;
+ cpu_physical_memory_write(buf_addr, buf, buf_len);
+ buf += buf_len;
+ if (size < 4) {
+ cpu_physical_memory_write(buf_addr + buf_len, crc_ptr, 4 - size);
+ crc_ptr += 4 - size;
+ }
+ bd.flags &= ~FEC_BD_E;
+ if (size == 0) {
+ /* Last buffer in frame. */
+ bd.flags |= flags | FEC_BD_L;
+ DPRINTF("rx frame flags %04x\n", bd.flags);
+ s->eir |= FEC_INT_RXF;
+ } else {
+ s->eir |= FEC_INT_RXB;
+ }
+ mcf_fec_write_bd(&bd, addr);
+ /* Advance to the next descriptor. */
+ if ((bd.flags & FEC_BD_W) != 0) {
+ addr = s->erdsr;
+ } else {
+ addr += 8;
+ }
+ }
+ s->rx_descriptor = addr;
+ mcf_fec_enable_rx(s);
+ mcf_fec_update(s);
+ return size;
+}
+
+static const MemoryRegionOps mcf_fec_ops = {
+ .read = mcf_fec_read,
+ .write = mcf_fec_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void mcf_fec_cleanup(NetClientState *nc)
+{
+ mcf_fec_state *s = qemu_get_nic_opaque(nc);
+
+ memory_region_del_subregion(s->sysmem, &s->iomem);
+ memory_region_destroy(&s->iomem);
+
+ g_free(s);
+}
+
+static NetClientInfo net_mcf_fec_info = {
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .size = sizeof(NICState),
+ .can_receive = mcf_fec_can_receive,
+ .receive = mcf_fec_receive,
+ .cleanup = mcf_fec_cleanup,
+};
+
+void mcf_fec_init(MemoryRegion *sysmem, NICInfo *nd,
+ hwaddr base, qemu_irq *irq)
+{
+ mcf_fec_state *s;
+
+ qemu_check_nic_model(nd, "mcf_fec");
+
+ s = (mcf_fec_state *)g_malloc0(sizeof(mcf_fec_state));
+ s->sysmem = sysmem;
+ s->irq = irq;
+
+ memory_region_init_io(&s->iomem, &mcf_fec_ops, s, "fec", 0x400);
+ memory_region_add_subregion(sysmem, base, &s->iomem);
+
+ s->conf.macaddr = nd->macaddr;
+ s->conf.peers.ncs[0] = nd->netdev;
+
+ s->nic = qemu_new_nic(&net_mcf_fec_info, &s->conf, nd->model, nd->name, s);
+
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+}
--- /dev/null
+/*
+ * QEMU model of the Milkymist minimac2 block.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Specification available at:
+ * not available yet
+ *
+ */
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "trace.h"
+#include "net/net.h"
+#include "qemu/error-report.h"
+#include "hw/qdev-addr.h"
+
+#include <zlib.h>
+
+enum {
+ R_SETUP = 0,
+ R_MDIO,
+ R_STATE0,
+ R_COUNT0,
+ R_STATE1,
+ R_COUNT1,
+ R_TXCOUNT,
+ R_MAX
+};
+
+enum {
+ SETUP_PHY_RST = (1<<0),
+};
+
+enum {
+ MDIO_DO = (1<<0),
+ MDIO_DI = (1<<1),
+ MDIO_OE = (1<<2),
+ MDIO_CLK = (1<<3),
+};
+
+enum {
+ STATE_EMPTY = 0,
+ STATE_LOADED = 1,
+ STATE_PENDING = 2,
+};
+
+enum {
+ MDIO_OP_WRITE = 1,
+ MDIO_OP_READ = 2,
+};
+
+enum mdio_state {
+ MDIO_STATE_IDLE,
+ MDIO_STATE_READING,
+ MDIO_STATE_WRITING,
+};
+
+enum {
+ R_PHY_ID1 = 2,
+ R_PHY_ID2 = 3,
+ R_PHY_MAX = 32
+};
+
+#define MINIMAC2_MTU 1530
+#define MINIMAC2_BUFFER_SIZE 2048
+
+struct MilkymistMinimac2MdioState {
+ int last_clk;
+ int count;
+ uint32_t data;
+ uint16_t data_out;
+ int state;
+
+ uint8_t phy_addr;
+ uint8_t reg_addr;
+};
+typedef struct MilkymistMinimac2MdioState MilkymistMinimac2MdioState;
+
+struct MilkymistMinimac2State {
+ SysBusDevice busdev;
+ NICState *nic;
+ NICConf conf;
+ char *phy_model;
+ MemoryRegion buffers;
+ MemoryRegion regs_region;
+
+ qemu_irq rx_irq;
+ qemu_irq tx_irq;
+
+ uint32_t regs[R_MAX];
+
+ MilkymistMinimac2MdioState mdio;
+
+ uint16_t phy_regs[R_PHY_MAX];
+
+ uint8_t *rx0_buf;
+ uint8_t *rx1_buf;
+ uint8_t *tx_buf;
+};
+typedef struct MilkymistMinimac2State MilkymistMinimac2State;
+
+static const uint8_t preamble_sfd[] = {
+ 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xd5
+};
+
+static void minimac2_mdio_write_reg(MilkymistMinimac2State *s,
+ uint8_t phy_addr, uint8_t reg_addr, uint16_t value)
+{
+ trace_milkymist_minimac2_mdio_write(phy_addr, reg_addr, value);
+
+ /* nop */
+}
+
+static uint16_t minimac2_mdio_read_reg(MilkymistMinimac2State *s,
+ uint8_t phy_addr, uint8_t reg_addr)
+{
+ uint16_t r = s->phy_regs[reg_addr];
+
+ trace_milkymist_minimac2_mdio_read(phy_addr, reg_addr, r);
+
+ return r;
+}
+
+static void minimac2_update_mdio(MilkymistMinimac2State *s)
+{
+ MilkymistMinimac2MdioState *m = &s->mdio;
+
+ /* detect rising clk edge */
+ if (m->last_clk == 0 && (s->regs[R_MDIO] & MDIO_CLK)) {
+ /* shift data in */
+ int bit = ((s->regs[R_MDIO] & MDIO_DO)
+ && (s->regs[R_MDIO] & MDIO_OE)) ? 1 : 0;
+ m->data = (m->data << 1) | bit;
+
+ /* check for sync */
+ if (m->data == 0xffffffff) {
+ m->count = 32;
+ }
+
+ if (m->count == 16) {
+ uint8_t start = (m->data >> 14) & 0x3;
+ uint8_t op = (m->data >> 12) & 0x3;
+ uint8_t ta = (m->data) & 0x3;
+
+ if (start == 1 && op == MDIO_OP_WRITE && ta == 2) {
+ m->state = MDIO_STATE_WRITING;
+ } else if (start == 1 && op == MDIO_OP_READ && (ta & 1) == 0) {
+ m->state = MDIO_STATE_READING;
+ } else {
+ m->state = MDIO_STATE_IDLE;
+ }
+
+ if (m->state != MDIO_STATE_IDLE) {
+ m->phy_addr = (m->data >> 7) & 0x1f;
+ m->reg_addr = (m->data >> 2) & 0x1f;
+ }
+
+ if (m->state == MDIO_STATE_READING) {
+ m->data_out = minimac2_mdio_read_reg(s, m->phy_addr,
+ m->reg_addr);
+ }
+ }
+
+ if (m->count < 16 && m->state == MDIO_STATE_READING) {
+ int bit = (m->data_out & 0x8000) ? 1 : 0;
+ m->data_out <<= 1;
+
+ if (bit) {
+ s->regs[R_MDIO] |= MDIO_DI;
+ } else {
+ s->regs[R_MDIO] &= ~MDIO_DI;
+ }
+ }
+
+ if (m->count == 0 && m->state) {
+ if (m->state == MDIO_STATE_WRITING) {
+ uint16_t data = m->data & 0xffff;
+ minimac2_mdio_write_reg(s, m->phy_addr, m->reg_addr, data);
+ }
+ m->state = MDIO_STATE_IDLE;
+ }
+ m->count--;
+ }
+
+ m->last_clk = (s->regs[R_MDIO] & MDIO_CLK) ? 1 : 0;
+}
+
+static size_t assemble_frame(uint8_t *buf, size_t size,
+ const uint8_t *payload, size_t payload_size)
+{
+ uint32_t crc;
+
+ if (size < payload_size + 12) {
+ error_report("milkymist_minimac2: received too big ethernet frame");
+ return 0;
+ }
+
+ /* prepend preamble and sfd */
+ memcpy(buf, preamble_sfd, 8);
+
+ /* now copy the payload */
+ memcpy(buf + 8, payload, payload_size);
+
+ /* pad frame if needed */
+ if (payload_size < 60) {
+ memset(buf + payload_size + 8, 0, 60 - payload_size);
+ payload_size = 60;
+ }
+
+ /* append fcs */
+ crc = cpu_to_le32(crc32(0, buf + 8, payload_size));
+ memcpy(buf + payload_size + 8, &crc, 4);
+
+ return payload_size + 12;
+}
+
+static void minimac2_tx(MilkymistMinimac2State *s)
+{
+ uint32_t txcount = s->regs[R_TXCOUNT];
+ uint8_t *buf = s->tx_buf;
+
+ if (txcount < 64) {
+ error_report("milkymist_minimac2: ethernet frame too small (%u < %u)",
+ txcount, 64);
+ goto err;
+ }
+
+ if (txcount > MINIMAC2_MTU) {
+ error_report("milkymist_minimac2: MTU exceeded (%u > %u)",
+ txcount, MINIMAC2_MTU);
+ goto err;
+ }
+
+ if (memcmp(buf, preamble_sfd, 8) != 0) {
+ error_report("milkymist_minimac2: frame doesn't contain the preamble "
+ "and/or the SFD (%02x %02x %02x %02x %02x %02x %02x %02x)",
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
+ goto err;
+ }
+
+ trace_milkymist_minimac2_tx_frame(txcount - 12);
+
+ /* send packet, skipping preamble and sfd */
+ qemu_send_packet_raw(qemu_get_queue(s->nic), buf + 8, txcount - 12);
+
+ s->regs[R_TXCOUNT] = 0;
+
+err:
+ trace_milkymist_minimac2_pulse_irq_tx();
+ qemu_irq_pulse(s->tx_irq);
+}
+
+static void update_rx_interrupt(MilkymistMinimac2State *s)
+{
+ if (s->regs[R_STATE0] == STATE_PENDING
+ || s->regs[R_STATE1] == STATE_PENDING) {
+ trace_milkymist_minimac2_raise_irq_rx();
+ qemu_irq_raise(s->rx_irq);
+ } else {
+ trace_milkymist_minimac2_lower_irq_rx();
+ qemu_irq_lower(s->rx_irq);
+ }
+}
+
+static ssize_t minimac2_rx(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+ MilkymistMinimac2State *s = qemu_get_nic_opaque(nc);
+
+ uint32_t r_count;
+ uint32_t r_state;
+ uint8_t *rx_buf;
+
+ size_t frame_size;
+
+ trace_milkymist_minimac2_rx_frame(buf, size);
+
+ /* choose appropriate slot */
+ if (s->regs[R_STATE0] == STATE_LOADED) {
+ r_count = R_COUNT0;
+ r_state = R_STATE0;
+ rx_buf = s->rx0_buf;
+ } else if (s->regs[R_STATE1] == STATE_LOADED) {
+ r_count = R_COUNT1;
+ r_state = R_STATE1;
+ rx_buf = s->rx1_buf;
+ } else {
+ trace_milkymist_minimac2_drop_rx_frame(buf);
+ return size;
+ }
+
+ /* assemble frame */
+ frame_size = assemble_frame(rx_buf, MINIMAC2_BUFFER_SIZE, buf, size);
+
+ if (frame_size == 0) {
+ return size;
+ }
+
+ trace_milkymist_minimac2_rx_transfer(rx_buf, frame_size);
+
+ /* update slot */
+ s->regs[r_count] = frame_size;
+ s->regs[r_state] = STATE_PENDING;
+
+ update_rx_interrupt(s);
+
+ return size;
+}
+
+static uint64_t
+minimac2_read(void *opaque, hwaddr addr, unsigned size)
+{
+ MilkymistMinimac2State *s = opaque;
+ uint32_t r = 0;
+
+ addr >>= 2;
+ switch (addr) {
+ case R_SETUP:
+ case R_MDIO:
+ case R_STATE0:
+ case R_COUNT0:
+ case R_STATE1:
+ case R_COUNT1:
+ case R_TXCOUNT:
+ r = s->regs[addr];
+ break;
+
+ default:
+ error_report("milkymist_minimac2: read access to unknown register 0x"
+ TARGET_FMT_plx, addr << 2);
+ break;
+ }
+
+ trace_milkymist_minimac2_memory_read(addr << 2, r);
+
+ return r;
+}
+
+static void
+minimac2_write(void *opaque, hwaddr addr, uint64_t value,
+ unsigned size)
+{
+ MilkymistMinimac2State *s = opaque;
+
+ trace_milkymist_minimac2_memory_read(addr, value);
+
+ addr >>= 2;
+ switch (addr) {
+ case R_MDIO:
+ {
+ /* MDIO_DI is read only */
+ int mdio_di = (s->regs[R_MDIO] & MDIO_DI);
+ s->regs[R_MDIO] = value;
+ if (mdio_di) {
+ s->regs[R_MDIO] |= mdio_di;
+ } else {
+ s->regs[R_MDIO] &= ~mdio_di;
+ }
+
+ minimac2_update_mdio(s);
+ } break;
+ case R_TXCOUNT:
+ s->regs[addr] = value;
+ if (value > 0) {
+ minimac2_tx(s);
+ }
+ break;
+ case R_STATE0:
+ case R_STATE1:
+ s->regs[addr] = value;
+ update_rx_interrupt(s);
+ break;
+ case R_SETUP:
+ case R_COUNT0:
+ case R_COUNT1:
+ s->regs[addr] = value;
+ break;
+
+ default:
+ error_report("milkymist_minimac2: write access to unknown register 0x"
+ TARGET_FMT_plx, addr << 2);
+ break;
+ }
+}
+
+static const MemoryRegionOps minimac2_ops = {
+ .read = minimac2_read,
+ .write = minimac2_write,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int minimac2_can_rx(NetClientState *nc)
+{
+ MilkymistMinimac2State *s = qemu_get_nic_opaque(nc);
+
+ if (s->regs[R_STATE0] == STATE_LOADED) {
+ return 1;
+ }
+ if (s->regs[R_STATE1] == STATE_LOADED) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static void minimac2_cleanup(NetClientState *nc)
+{
+ MilkymistMinimac2State *s = qemu_get_nic_opaque(nc);
+
+ s->nic = NULL;
+}
+
+static void milkymist_minimac2_reset(DeviceState *d)
+{
+ MilkymistMinimac2State *s =
+ container_of(d, MilkymistMinimac2State, busdev.qdev);
+ int i;
+
+ for (i = 0; i < R_MAX; i++) {
+ s->regs[i] = 0;
+ }
+ for (i = 0; i < R_PHY_MAX; i++) {
+ s->phy_regs[i] = 0;
+ }
+
+ /* defaults */
+ s->phy_regs[R_PHY_ID1] = 0x0022; /* Micrel KSZ8001L */
+ s->phy_regs[R_PHY_ID2] = 0x161a;
+}
+
+static NetClientInfo net_milkymist_minimac2_info = {
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .size = sizeof(NICState),
+ .can_receive = minimac2_can_rx,
+ .receive = minimac2_rx,
+ .cleanup = minimac2_cleanup,
+};
+
+static int milkymist_minimac2_init(SysBusDevice *dev)
+{
+ MilkymistMinimac2State *s = FROM_SYSBUS(typeof(*s), dev);
+ size_t buffers_size = TARGET_PAGE_ALIGN(3 * MINIMAC2_BUFFER_SIZE);
+
+ sysbus_init_irq(dev, &s->rx_irq);
+ sysbus_init_irq(dev, &s->tx_irq);
+
+ memory_region_init_io(&s->regs_region, &minimac2_ops, s,
+ "milkymist-minimac2", R_MAX * 4);
+ sysbus_init_mmio(dev, &s->regs_region);
+
+ /* register buffers memory */
+ memory_region_init_ram(&s->buffers, "milkymist-minimac2.buffers",
+ buffers_size);
+ vmstate_register_ram_global(&s->buffers);
+ s->rx0_buf = memory_region_get_ram_ptr(&s->buffers);
+ s->rx1_buf = s->rx0_buf + MINIMAC2_BUFFER_SIZE;
+ s->tx_buf = s->rx1_buf + MINIMAC2_BUFFER_SIZE;
+
+ sysbus_init_mmio(dev, &s->buffers);
+
+ qemu_macaddr_default_if_unset(&s->conf.macaddr);
+ s->nic = qemu_new_nic(&net_milkymist_minimac2_info, &s->conf,
+ object_get_typename(OBJECT(dev)), dev->qdev.id, s);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_milkymist_minimac2_mdio = {
+ .name = "milkymist-minimac2-mdio",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32(last_clk, MilkymistMinimac2MdioState),
+ VMSTATE_INT32(count, MilkymistMinimac2MdioState),
+ VMSTATE_UINT32(data, MilkymistMinimac2MdioState),
+ VMSTATE_UINT16(data_out, MilkymistMinimac2MdioState),
+ VMSTATE_INT32(state, MilkymistMinimac2MdioState),
+ VMSTATE_UINT8(phy_addr, MilkymistMinimac2MdioState),
+ VMSTATE_UINT8(reg_addr, MilkymistMinimac2MdioState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_milkymist_minimac2 = {
+ .name = "milkymist-minimac2",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(regs, MilkymistMinimac2State, R_MAX),
+ VMSTATE_UINT16_ARRAY(phy_regs, MilkymistMinimac2State, R_PHY_MAX),
+ VMSTATE_STRUCT(mdio, MilkymistMinimac2State, 0,
+ vmstate_milkymist_minimac2_mdio, MilkymistMinimac2MdioState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property milkymist_minimac2_properties[] = {
+ DEFINE_NIC_PROPERTIES(MilkymistMinimac2State, conf),
+ DEFINE_PROP_STRING("phy_model", MilkymistMinimac2State, phy_model),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void milkymist_minimac2_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = milkymist_minimac2_init;
+ dc->reset = milkymist_minimac2_reset;
+ dc->vmsd = &vmstate_milkymist_minimac2;
+ dc->props = milkymist_minimac2_properties;
+}
+
+static const TypeInfo milkymist_minimac2_info = {
+ .name = "milkymist-minimac2",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(MilkymistMinimac2State),
+ .class_init = milkymist_minimac2_class_init,
+};
+
+static void milkymist_minimac2_register_types(void)
+{
+ type_register_static(&milkymist_minimac2_info);
+}
+
+type_init(milkymist_minimac2_register_types)
--- /dev/null
+/*
+ * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
+ *
+ * PAPR Inter-VM Logical Lan, aka ibmveth
+ *
+ * Copyright (c) 2010,2011 David Gibson, IBM Corporation.
+ *
+ * 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 "hw/hw.h"
+#include "net/net.h"
+#include "hw/qdev.h"
+#include "hw/ppc/spapr.h"
+#include "hw/ppc/spapr_vio.h"
+
+#include <libfdt.h>
+
+#define ETH_ALEN 6
+#define MAX_PACKET_SIZE 65536
+
+/*#define DEBUG*/
+
+#ifdef DEBUG
+#define dprintf(fmt...) do { fprintf(stderr, fmt); } while (0)
+#else
+#define dprintf(fmt...)
+#endif
+
+/*
+ * Virtual LAN device
+ */
+
+typedef uint64_t vlan_bd_t;
+
+#define VLAN_BD_VALID 0x8000000000000000ULL
+#define VLAN_BD_TOGGLE 0x4000000000000000ULL
+#define VLAN_BD_NO_CSUM 0x0200000000000000ULL
+#define VLAN_BD_CSUM_GOOD 0x0100000000000000ULL
+#define VLAN_BD_LEN_MASK 0x00ffffff00000000ULL
+#define VLAN_BD_LEN(bd) (((bd) & VLAN_BD_LEN_MASK) >> 32)
+#define VLAN_BD_ADDR_MASK 0x00000000ffffffffULL
+#define VLAN_BD_ADDR(bd) ((bd) & VLAN_BD_ADDR_MASK)
+
+#define VLAN_VALID_BD(addr, len) (VLAN_BD_VALID | \
+ (((len) << 32) & VLAN_BD_LEN_MASK) | \
+ (addr & VLAN_BD_ADDR_MASK))
+
+#define VLAN_RXQC_TOGGLE 0x80
+#define VLAN_RXQC_VALID 0x40
+#define VLAN_RXQC_NO_CSUM 0x02
+#define VLAN_RXQC_CSUM_GOOD 0x01
+
+#define VLAN_RQ_ALIGNMENT 16
+#define VLAN_RXQ_BD_OFF 0
+#define VLAN_FILTER_BD_OFF 8
+#define VLAN_RX_BDS_OFF 16
+#define VLAN_MAX_BUFS ((SPAPR_TCE_PAGE_SIZE - VLAN_RX_BDS_OFF) / 8)
+
+typedef struct VIOsPAPRVLANDevice {
+ VIOsPAPRDevice sdev;
+ NICConf nicconf;
+ NICState *nic;
+ int isopen;
+ target_ulong buf_list;
+ int add_buf_ptr, use_buf_ptr, rx_bufs;
+ target_ulong rxq_ptr;
+} VIOsPAPRVLANDevice;
+
+static int spapr_vlan_can_receive(NetClientState *nc)
+{
+ VIOsPAPRVLANDevice *dev = qemu_get_nic_opaque(nc);
+
+ return (dev->isopen && dev->rx_bufs > 0);
+}
+
+static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf,
+ size_t size)
+{
+ VIOsPAPRDevice *sdev = qemu_get_nic_opaque(nc);
+ VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev;
+ vlan_bd_t rxq_bd = vio_ldq(sdev, dev->buf_list + VLAN_RXQ_BD_OFF);
+ vlan_bd_t bd;
+ int buf_ptr = dev->use_buf_ptr;
+ uint64_t handle;
+ uint8_t control;
+
+ dprintf("spapr_vlan_receive() [%s] rx_bufs=%d\n", sdev->qdev.id,
+ dev->rx_bufs);
+
+ if (!dev->isopen) {
+ return -1;
+ }
+
+ if (!dev->rx_bufs) {
+ return -1;
+ }
+
+ do {
+ buf_ptr += 8;
+ if (buf_ptr >= SPAPR_TCE_PAGE_SIZE) {
+ buf_ptr = VLAN_RX_BDS_OFF;
+ }
+
+ bd = vio_ldq(sdev, dev->buf_list + buf_ptr);
+ dprintf("use_buf_ptr=%d bd=0x%016llx\n",
+ buf_ptr, (unsigned long long)bd);
+ } while ((!(bd & VLAN_BD_VALID) || (VLAN_BD_LEN(bd) < (size + 8)))
+ && (buf_ptr != dev->use_buf_ptr));
+
+ if (!(bd & VLAN_BD_VALID) || (VLAN_BD_LEN(bd) < (size + 8))) {
+ /* Failed to find a suitable buffer */
+ return -1;
+ }
+
+ /* Remove the buffer from the pool */
+ dev->rx_bufs--;
+ dev->use_buf_ptr = buf_ptr;
+ vio_stq(sdev, dev->buf_list + dev->use_buf_ptr, 0);
+
+ dprintf("Found buffer: ptr=%d num=%d\n", dev->use_buf_ptr, dev->rx_bufs);
+
+ /* Transfer the packet data */
+ if (spapr_vio_dma_write(sdev, VLAN_BD_ADDR(bd) + 8, buf, size) < 0) {
+ return -1;
+ }
+
+ dprintf("spapr_vlan_receive: DMA write completed\n");
+
+ /* Update the receive queue */
+ control = VLAN_RXQC_TOGGLE | VLAN_RXQC_VALID;
+ if (rxq_bd & VLAN_BD_TOGGLE) {
+ control ^= VLAN_RXQC_TOGGLE;
+ }
+
+ handle = vio_ldq(sdev, VLAN_BD_ADDR(bd));
+ vio_stq(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 8, handle);
+ vio_stl(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 4, size);
+ vio_sth(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 2, 8);
+ vio_stb(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr, control);
+
+ dprintf("wrote rxq entry (ptr=0x%llx): 0x%016llx 0x%016llx\n",
+ (unsigned long long)dev->rxq_ptr,
+ (unsigned long long)vio_ldq(sdev, VLAN_BD_ADDR(rxq_bd) +
+ dev->rxq_ptr),
+ (unsigned long long)vio_ldq(sdev, VLAN_BD_ADDR(rxq_bd) +
+ dev->rxq_ptr + 8));
+
+ dev->rxq_ptr += 16;
+ if (dev->rxq_ptr >= VLAN_BD_LEN(rxq_bd)) {
+ dev->rxq_ptr = 0;
+ vio_stq(sdev, dev->buf_list + VLAN_RXQ_BD_OFF, rxq_bd ^ VLAN_BD_TOGGLE);
+ }
+
+ if (sdev->signal_state & 1) {
+ qemu_irq_pulse(spapr_vio_qirq(sdev));
+ }
+
+ return size;
+}
+
+static void spapr_vlan_cleanup(NetClientState *nc)
+{
+ VIOsPAPRVLANDevice *dev = qemu_get_nic_opaque(nc);
+
+ dev->nic = NULL;
+}
+
+static NetClientInfo net_spapr_vlan_info = {
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .size = sizeof(NICState),
+ .can_receive = spapr_vlan_can_receive,
+ .receive = spapr_vlan_receive,
+ .cleanup = spapr_vlan_cleanup,
+};
+
+static void spapr_vlan_reset(VIOsPAPRDevice *sdev)
+{
+ VIOsPAPRVLANDevice *dev = DO_UPCAST(VIOsPAPRVLANDevice, sdev, sdev);
+
+ dev->buf_list = 0;
+ dev->rx_bufs = 0;
+ dev->isopen = 0;
+}
+
+static int spapr_vlan_init(VIOsPAPRDevice *sdev)
+{
+ VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev;
+
+ qemu_macaddr_default_if_unset(&dev->nicconf.macaddr);
+
+ dev->nic = qemu_new_nic(&net_spapr_vlan_info, &dev->nicconf,
+ object_get_typename(OBJECT(sdev)), sdev->qdev.id, dev);
+ qemu_format_nic_info_str(qemu_get_queue(dev->nic), dev->nicconf.macaddr.a);
+
+ return 0;
+}
+
+void spapr_vlan_create(VIOsPAPRBus *bus, NICInfo *nd)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(&bus->bus, "spapr-vlan");
+
+ qdev_set_nic_properties(dev, nd);
+
+ qdev_init_nofail(dev);
+}
+
+static int spapr_vlan_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off)
+{
+ VIOsPAPRVLANDevice *vdev = (VIOsPAPRVLANDevice *)dev;
+ uint8_t padded_mac[8] = {0, 0};
+ int ret;
+
+ /* Some old phyp versions give the mac address in an 8-byte
+ * property. The kernel driver has an insane workaround for this;
+ * rather than doing the obvious thing and checking the property
+ * length, it checks whether the first byte has 0b10 in the low
+ * bits. If a correct 6-byte property has a different first byte
+ * the kernel will get the wrong mac address, overrunning its
+ * buffer in the process (read only, thank goodness).
+ *
+ * Here we workaround the kernel workaround by always supplying an
+ * 8-byte property, with the mac address in the last six bytes */
+ memcpy(&padded_mac[2], &vdev->nicconf.macaddr, ETH_ALEN);
+ ret = fdt_setprop(fdt, node_off, "local-mac-address",
+ padded_mac, sizeof(padded_mac));
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = fdt_setprop_cell(fdt, node_off, "ibm,mac-address-filters", 0);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+static int check_bd(VIOsPAPRVLANDevice *dev, vlan_bd_t bd,
+ target_ulong alignment)
+{
+ if ((VLAN_BD_ADDR(bd) % alignment)
+ || (VLAN_BD_LEN(bd) % alignment)) {
+ return -1;
+ }
+
+ if (!spapr_vio_dma_valid(&dev->sdev, VLAN_BD_ADDR(bd),
+ VLAN_BD_LEN(bd), DMA_DIRECTION_FROM_DEVICE)
+ || !spapr_vio_dma_valid(&dev->sdev, VLAN_BD_ADDR(bd),
+ VLAN_BD_LEN(bd), DMA_DIRECTION_TO_DEVICE)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static target_ulong h_register_logical_lan(PowerPCCPU *cpu,
+ sPAPREnvironment *spapr,
+ target_ulong opcode,
+ target_ulong *args)
+{
+ target_ulong reg = args[0];
+ target_ulong buf_list = args[1];
+ target_ulong rec_queue = args[2];
+ target_ulong filter_list = args[3];
+ VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
+ VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev;
+ vlan_bd_t filter_list_bd;
+
+ if (!dev) {
+ return H_PARAMETER;
+ }
+
+ if (dev->isopen) {
+ hcall_dprintf("H_REGISTER_LOGICAL_LAN called twice without "
+ "H_FREE_LOGICAL_LAN\n");
+ return H_RESOURCE;
+ }
+
+ if (check_bd(dev, VLAN_VALID_BD(buf_list, SPAPR_TCE_PAGE_SIZE),
+ SPAPR_TCE_PAGE_SIZE) < 0) {
+ hcall_dprintf("Bad buf_list 0x" TARGET_FMT_lx "\n", buf_list);
+ return H_PARAMETER;
+ }
+
+ filter_list_bd = VLAN_VALID_BD(filter_list, SPAPR_TCE_PAGE_SIZE);
+ if (check_bd(dev, filter_list_bd, SPAPR_TCE_PAGE_SIZE) < 0) {
+ hcall_dprintf("Bad filter_list 0x" TARGET_FMT_lx "\n", filter_list);
+ return H_PARAMETER;
+ }
+
+ if (!(rec_queue & VLAN_BD_VALID)
+ || (check_bd(dev, rec_queue, VLAN_RQ_ALIGNMENT) < 0)) {
+ hcall_dprintf("Bad receive queue\n");
+ return H_PARAMETER;
+ }
+
+ dev->buf_list = buf_list;
+ sdev->signal_state = 0;
+
+ rec_queue &= ~VLAN_BD_TOGGLE;
+
+ /* Initialize the buffer list */
+ vio_stq(sdev, buf_list, rec_queue);
+ vio_stq(sdev, buf_list + 8, filter_list_bd);
+ spapr_vio_dma_set(sdev, buf_list + VLAN_RX_BDS_OFF, 0,
+ SPAPR_TCE_PAGE_SIZE - VLAN_RX_BDS_OFF);
+ dev->add_buf_ptr = VLAN_RX_BDS_OFF - 8;
+ dev->use_buf_ptr = VLAN_RX_BDS_OFF - 8;
+ dev->rx_bufs = 0;
+ dev->rxq_ptr = 0;
+
+ /* Initialize the receive queue */
+ spapr_vio_dma_set(sdev, VLAN_BD_ADDR(rec_queue), 0, VLAN_BD_LEN(rec_queue));
+
+ dev->isopen = 1;
+ return H_SUCCESS;
+}
+
+
+static target_ulong h_free_logical_lan(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ target_ulong reg = args[0];
+ VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
+ VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev;
+
+ if (!dev) {
+ return H_PARAMETER;
+ }
+
+ if (!dev->isopen) {
+ hcall_dprintf("H_FREE_LOGICAL_LAN called without "
+ "H_REGISTER_LOGICAL_LAN\n");
+ return H_RESOURCE;
+ }
+
+ spapr_vlan_reset(sdev);
+ return H_SUCCESS;
+}
+
+static target_ulong h_add_logical_lan_buffer(PowerPCCPU *cpu,
+ sPAPREnvironment *spapr,
+ target_ulong opcode,
+ target_ulong *args)
+{
+ target_ulong reg = args[0];
+ target_ulong buf = args[1];
+ VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
+ VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev;
+ vlan_bd_t bd;
+
+ dprintf("H_ADD_LOGICAL_LAN_BUFFER(0x" TARGET_FMT_lx
+ ", 0x" TARGET_FMT_lx ")\n", reg, buf);
+
+ if (!sdev) {
+ hcall_dprintf("Bad device\n");
+ return H_PARAMETER;
+ }
+
+ if ((check_bd(dev, buf, 4) < 0)
+ || (VLAN_BD_LEN(buf) < 16)) {
+ hcall_dprintf("Bad buffer enqueued\n");
+ return H_PARAMETER;
+ }
+
+ if (!dev->isopen || dev->rx_bufs >= VLAN_MAX_BUFS) {
+ return H_RESOURCE;
+ }
+
+ do {
+ dev->add_buf_ptr += 8;
+ if (dev->add_buf_ptr >= SPAPR_TCE_PAGE_SIZE) {
+ dev->add_buf_ptr = VLAN_RX_BDS_OFF;
+ }
+
+ bd = vio_ldq(sdev, dev->buf_list + dev->add_buf_ptr);
+ } while (bd & VLAN_BD_VALID);
+
+ vio_stq(sdev, dev->buf_list + dev->add_buf_ptr, buf);
+
+ dev->rx_bufs++;
+
+ dprintf("h_add_logical_lan_buffer(): Added buf ptr=%d rx_bufs=%d"
+ " bd=0x%016llx\n", dev->add_buf_ptr, dev->rx_bufs,
+ (unsigned long long)buf);
+
+ return H_SUCCESS;
+}
+
+static target_ulong h_send_logical_lan(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ target_ulong reg = args[0];
+ target_ulong *bufs = args + 1;
+ target_ulong continue_token = args[7];
+ VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
+ VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev;
+ unsigned total_len;
+ uint8_t *lbuf, *p;
+ int i, nbufs;
+ int ret;
+
+ dprintf("H_SEND_LOGICAL_LAN(0x" TARGET_FMT_lx ", <bufs>, 0x"
+ TARGET_FMT_lx ")\n", reg, continue_token);
+
+ if (!sdev) {
+ return H_PARAMETER;
+ }
+
+ dprintf("rxbufs = %d\n", dev->rx_bufs);
+
+ if (!dev->isopen) {
+ return H_DROPPED;
+ }
+
+ if (continue_token) {
+ return H_HARDWARE; /* FIXME actually handle this */
+ }
+
+ total_len = 0;
+ for (i = 0; i < 6; i++) {
+ dprintf(" buf desc: 0x" TARGET_FMT_lx "\n", bufs[i]);
+ if (!(bufs[i] & VLAN_BD_VALID)) {
+ break;
+ }
+ total_len += VLAN_BD_LEN(bufs[i]);
+ }
+
+ nbufs = i;
+ dprintf("h_send_logical_lan() %d buffers, total length 0x%x\n",
+ nbufs, total_len);
+
+ if (total_len == 0) {
+ return H_SUCCESS;
+ }
+
+ if (total_len > MAX_PACKET_SIZE) {
+ /* Don't let the guest force too large an allocation */
+ return H_RESOURCE;
+ }
+
+ lbuf = alloca(total_len);
+ p = lbuf;
+ for (i = 0; i < nbufs; i++) {
+ ret = spapr_vio_dma_read(sdev, VLAN_BD_ADDR(bufs[i]),
+ p, VLAN_BD_LEN(bufs[i]));
+ if (ret < 0) {
+ return ret;
+ }
+
+ p += VLAN_BD_LEN(bufs[i]);
+ }
+
+ qemu_send_packet(qemu_get_queue(dev->nic), lbuf, total_len);
+
+ return H_SUCCESS;
+}
+
+static target_ulong h_multicast_ctrl(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ target_ulong reg = args[0];
+ VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
+
+ if (!dev) {
+ return H_PARAMETER;
+ }
+
+ return H_SUCCESS;
+}
+
+static Property spapr_vlan_properties[] = {
+ DEFINE_SPAPR_PROPERTIES(VIOsPAPRVLANDevice, sdev),
+ DEFINE_NIC_PROPERTIES(VIOsPAPRVLANDevice, nicconf),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void spapr_vlan_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VIOsPAPRDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass);
+
+ k->init = spapr_vlan_init;
+ k->reset = spapr_vlan_reset;
+ k->devnode = spapr_vlan_devnode;
+ k->dt_name = "l-lan";
+ k->dt_type = "network";
+ k->dt_compatible = "IBM,l-lan";
+ k->signal_mask = 0x1;
+ dc->props = spapr_vlan_properties;
+ k->rtce_window_size = 0x10000000;
+}
+
+static const TypeInfo spapr_vlan_info = {
+ .name = "spapr-vlan",
+ .parent = TYPE_VIO_SPAPR_DEVICE,
+ .instance_size = sizeof(VIOsPAPRVLANDevice),
+ .class_init = spapr_vlan_class_init,
+};
+
+static void spapr_vlan_register_types(void)
+{
+ spapr_register_hypercall(H_REGISTER_LOGICAL_LAN, h_register_logical_lan);
+ spapr_register_hypercall(H_FREE_LOGICAL_LAN, h_free_logical_lan);
+ spapr_register_hypercall(H_SEND_LOGICAL_LAN, h_send_logical_lan);
+ spapr_register_hypercall(H_ADD_LOGICAL_LAN_BUFFER,
+ h_add_logical_lan_buffer);
+ spapr_register_hypercall(H_MULTICAST_CTRL, h_multicast_ctrl);
+ type_register_static(&spapr_vlan_info);
+}
+
+type_init(spapr_vlan_register_types)
--- /dev/null
+/*
+ * Luminary Micro Stellaris Ethernet Controller
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+#include "hw/sysbus.h"
+#include "net/net.h"
+#include <zlib.h>
+
+//#define DEBUG_STELLARIS_ENET 1
+
+#ifdef DEBUG_STELLARIS_ENET
+#define DPRINTF(fmt, ...) \
+do { printf("stellaris_enet: " fmt , ## __VA_ARGS__); } while (0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "stellaris_enet: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "stellaris_enet: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+#define SE_INT_RX 0x01
+#define SE_INT_TXER 0x02
+#define SE_INT_TXEMP 0x04
+#define SE_INT_FOV 0x08
+#define SE_INT_RXER 0x10
+#define SE_INT_MD 0x20
+#define SE_INT_PHY 0x40
+
+#define SE_RCTL_RXEN 0x01
+#define SE_RCTL_AMUL 0x02
+#define SE_RCTL_PRMS 0x04
+#define SE_RCTL_BADCRC 0x08
+#define SE_RCTL_RSTFIFO 0x10
+
+#define SE_TCTL_TXEN 0x01
+#define SE_TCTL_PADEN 0x02
+#define SE_TCTL_CRC 0x04
+#define SE_TCTL_DUPLEX 0x08
+
+typedef struct {
+ SysBusDevice busdev;
+ uint32_t ris;
+ uint32_t im;
+ uint32_t rctl;
+ uint32_t tctl;
+ uint32_t thr;
+ uint32_t mctl;
+ uint32_t mdv;
+ uint32_t mtxd;
+ uint32_t mrxd;
+ uint32_t np;
+ int tx_frame_len;
+ int tx_fifo_len;
+ uint8_t tx_fifo[2048];
+ /* Real hardware has a 2k fifo, which works out to be at most 31 packets.
+ We implement a full 31 packet fifo. */
+ struct {
+ uint8_t data[2048];
+ int len;
+ } rx[31];
+ uint8_t *rx_fifo;
+ int rx_fifo_len;
+ int next_packet;
+ NICState *nic;
+ NICConf conf;
+ qemu_irq irq;
+ MemoryRegion mmio;
+} stellaris_enet_state;
+
+static void stellaris_enet_update(stellaris_enet_state *s)
+{
+ qemu_set_irq(s->irq, (s->ris & s->im) != 0);
+}
+
+/* TODO: Implement MAC address filtering. */
+static ssize_t stellaris_enet_receive(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+ stellaris_enet_state *s = qemu_get_nic_opaque(nc);
+ int n;
+ uint8_t *p;
+ uint32_t crc;
+
+ if ((s->rctl & SE_RCTL_RXEN) == 0)
+ return -1;
+ if (s->np >= 31) {
+ DPRINTF("Packet dropped\n");
+ return -1;
+ }
+
+ DPRINTF("Received packet len=%d\n", size);
+ n = s->next_packet + s->np;
+ if (n >= 31)
+ n -= 31;
+ s->np++;
+
+ s->rx[n].len = size + 6;
+ p = s->rx[n].data;
+ *(p++) = (size + 6);
+ *(p++) = (size + 6) >> 8;
+ memcpy (p, buf, size);
+ p += size;
+ crc = crc32(~0, buf, size);
+ *(p++) = crc;
+ *(p++) = crc >> 8;
+ *(p++) = crc >> 16;
+ *(p++) = crc >> 24;
+ /* Clear the remaining bytes in the last word. */
+ if ((size & 3) != 2) {
+ memset(p, 0, (6 - size) & 3);
+ }
+
+ s->ris |= SE_INT_RX;
+ stellaris_enet_update(s);
+
+ return size;
+}
+
+static int stellaris_enet_can_receive(NetClientState *nc)
+{
+ stellaris_enet_state *s = qemu_get_nic_opaque(nc);
+
+ if ((s->rctl & SE_RCTL_RXEN) == 0)
+ return 1;
+
+ return (s->np < 31);
+}
+
+static uint64_t stellaris_enet_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ stellaris_enet_state *s = (stellaris_enet_state *)opaque;
+ uint32_t val;
+
+ switch (offset) {
+ case 0x00: /* RIS */
+ DPRINTF("IRQ status %02x\n", s->ris);
+ return s->ris;
+ case 0x04: /* IM */
+ return s->im;
+ case 0x08: /* RCTL */
+ return s->rctl;
+ case 0x0c: /* TCTL */
+ return s->tctl;
+ case 0x10: /* DATA */
+ if (s->rx_fifo_len == 0) {
+ if (s->np == 0) {
+ BADF("RX underflow\n");
+ return 0;
+ }
+ s->rx_fifo_len = s->rx[s->next_packet].len;
+ s->rx_fifo = s->rx[s->next_packet].data;
+ DPRINTF("RX FIFO start packet len=%d\n", s->rx_fifo_len);
+ }
+ val = s->rx_fifo[0] | (s->rx_fifo[1] << 8) | (s->rx_fifo[2] << 16)
+ | (s->rx_fifo[3] << 24);
+ s->rx_fifo += 4;
+ s->rx_fifo_len -= 4;
+ if (s->rx_fifo_len <= 0) {
+ s->rx_fifo_len = 0;
+ s->next_packet++;
+ if (s->next_packet >= 31)
+ s->next_packet = 0;
+ s->np--;
+ DPRINTF("RX done np=%d\n", s->np);
+ }
+ return val;
+ case 0x14: /* IA0 */
+ return s->conf.macaddr.a[0] | (s->conf.macaddr.a[1] << 8)
+ | (s->conf.macaddr.a[2] << 16) | (s->conf.macaddr.a[3] << 24);
+ case 0x18: /* IA1 */
+ return s->conf.macaddr.a[4] | (s->conf.macaddr.a[5] << 8);
+ case 0x1c: /* THR */
+ return s->thr;
+ case 0x20: /* MCTL */
+ return s->mctl;
+ case 0x24: /* MDV */
+ return s->mdv;
+ case 0x28: /* MADD */
+ return 0;
+ case 0x2c: /* MTXD */
+ return s->mtxd;
+ case 0x30: /* MRXD */
+ return s->mrxd;
+ case 0x34: /* NP */
+ return s->np;
+ case 0x38: /* TR */
+ return 0;
+ case 0x3c: /* Undocuented: Timestamp? */
+ return 0;
+ default:
+ hw_error("stellaris_enet_read: Bad offset %x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void stellaris_enet_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ stellaris_enet_state *s = (stellaris_enet_state *)opaque;
+
+ switch (offset) {
+ case 0x00: /* IACK */
+ s->ris &= ~value;
+ DPRINTF("IRQ ack %02x/%02x\n", value, s->ris);
+ stellaris_enet_update(s);
+ /* Clearing TXER also resets the TX fifo. */
+ if (value & SE_INT_TXER)
+ s->tx_frame_len = -1;
+ break;
+ case 0x04: /* IM */
+ DPRINTF("IRQ mask %02x/%02x\n", value, s->ris);
+ s->im = value;
+ stellaris_enet_update(s);
+ break;
+ case 0x08: /* RCTL */
+ s->rctl = value;
+ if (value & SE_RCTL_RSTFIFO) {
+ s->rx_fifo_len = 0;
+ s->np = 0;
+ stellaris_enet_update(s);
+ }
+ break;
+ case 0x0c: /* TCTL */
+ s->tctl = value;
+ break;
+ case 0x10: /* DATA */
+ if (s->tx_frame_len == -1) {
+ s->tx_frame_len = value & 0xffff;
+ if (s->tx_frame_len > 2032) {
+ DPRINTF("TX frame too long (%d)\n", s->tx_frame_len);
+ s->tx_frame_len = 0;
+ s->ris |= SE_INT_TXER;
+ stellaris_enet_update(s);
+ } else {
+ DPRINTF("Start TX frame len=%d\n", s->tx_frame_len);
+ /* The value written does not include the ethernet header. */
+ s->tx_frame_len += 14;
+ if ((s->tctl & SE_TCTL_CRC) == 0)
+ s->tx_frame_len += 4;
+ s->tx_fifo_len = 0;
+ s->tx_fifo[s->tx_fifo_len++] = value >> 16;
+ s->tx_fifo[s->tx_fifo_len++] = value >> 24;
+ }
+ } else {
+ s->tx_fifo[s->tx_fifo_len++] = value;
+ s->tx_fifo[s->tx_fifo_len++] = value >> 8;
+ s->tx_fifo[s->tx_fifo_len++] = value >> 16;
+ s->tx_fifo[s->tx_fifo_len++] = value >> 24;
+ if (s->tx_fifo_len >= s->tx_frame_len) {
+ /* We don't implement explicit CRC, so just chop it off. */
+ if ((s->tctl & SE_TCTL_CRC) == 0)
+ s->tx_frame_len -= 4;
+ if ((s->tctl & SE_TCTL_PADEN) && s->tx_frame_len < 60) {
+ memset(&s->tx_fifo[s->tx_frame_len], 0, 60 - s->tx_frame_len);
+ s->tx_fifo_len = 60;
+ }
+ qemu_send_packet(qemu_get_queue(s->nic), s->tx_fifo,
+ s->tx_frame_len);
+ s->tx_frame_len = -1;
+ s->ris |= SE_INT_TXEMP;
+ stellaris_enet_update(s);
+ DPRINTF("Done TX\n");
+ }
+ }
+ break;
+ case 0x14: /* IA0 */
+ s->conf.macaddr.a[0] = value;
+ s->conf.macaddr.a[1] = value >> 8;
+ s->conf.macaddr.a[2] = value >> 16;
+ s->conf.macaddr.a[3] = value >> 24;
+ break;
+ case 0x18: /* IA1 */
+ s->conf.macaddr.a[4] = value;
+ s->conf.macaddr.a[5] = value >> 8;
+ break;
+ case 0x1c: /* THR */
+ s->thr = value;
+ break;
+ case 0x20: /* MCTL */
+ s->mctl = value;
+ break;
+ case 0x24: /* MDV */
+ s->mdv = value;
+ break;
+ case 0x28: /* MADD */
+ /* ignored. */
+ break;
+ case 0x2c: /* MTXD */
+ s->mtxd = value & 0xff;
+ break;
+ case 0x30: /* MRXD */
+ case 0x34: /* NP */
+ case 0x38: /* TR */
+ /* Ignored. */
+ case 0x3c: /* Undocuented: Timestamp? */
+ /* Ignored. */
+ break;
+ default:
+ hw_error("stellaris_enet_write: Bad offset %x\n", (int)offset);
+ }
+}
+
+static const MemoryRegionOps stellaris_enet_ops = {
+ .read = stellaris_enet_read,
+ .write = stellaris_enet_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void stellaris_enet_reset(stellaris_enet_state *s)
+{
+ s->mdv = 0x80;
+ s->rctl = SE_RCTL_BADCRC;
+ s->im = SE_INT_PHY | SE_INT_MD | SE_INT_RXER | SE_INT_FOV | SE_INT_TXEMP
+ | SE_INT_TXER | SE_INT_RX;
+ s->thr = 0x3f;
+ s->tx_frame_len = -1;
+}
+
+static void stellaris_enet_save(QEMUFile *f, void *opaque)
+{
+ stellaris_enet_state *s = (stellaris_enet_state *)opaque;
+ int i;
+
+ qemu_put_be32(f, s->ris);
+ qemu_put_be32(f, s->im);
+ qemu_put_be32(f, s->rctl);
+ qemu_put_be32(f, s->tctl);
+ qemu_put_be32(f, s->thr);
+ qemu_put_be32(f, s->mctl);
+ qemu_put_be32(f, s->mdv);
+ qemu_put_be32(f, s->mtxd);
+ qemu_put_be32(f, s->mrxd);
+ qemu_put_be32(f, s->np);
+ qemu_put_be32(f, s->tx_frame_len);
+ qemu_put_be32(f, s->tx_fifo_len);
+ qemu_put_buffer(f, s->tx_fifo, sizeof(s->tx_fifo));
+ for (i = 0; i < 31; i++) {
+ qemu_put_be32(f, s->rx[i].len);
+ qemu_put_buffer(f, s->rx[i].data, sizeof(s->rx[i].data));
+
+ }
+ qemu_put_be32(f, s->next_packet);
+ qemu_put_be32(f, s->rx_fifo - s->rx[s->next_packet].data);
+ qemu_put_be32(f, s->rx_fifo_len);
+}
+
+static int stellaris_enet_load(QEMUFile *f, void *opaque, int version_id)
+{
+ stellaris_enet_state *s = (stellaris_enet_state *)opaque;
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->ris = qemu_get_be32(f);
+ s->im = qemu_get_be32(f);
+ s->rctl = qemu_get_be32(f);
+ s->tctl = qemu_get_be32(f);
+ s->thr = qemu_get_be32(f);
+ s->mctl = qemu_get_be32(f);
+ s->mdv = qemu_get_be32(f);
+ s->mtxd = qemu_get_be32(f);
+ s->mrxd = qemu_get_be32(f);
+ s->np = qemu_get_be32(f);
+ s->tx_frame_len = qemu_get_be32(f);
+ s->tx_fifo_len = qemu_get_be32(f);
+ qemu_get_buffer(f, s->tx_fifo, sizeof(s->tx_fifo));
+ for (i = 0; i < 31; i++) {
+ s->rx[i].len = qemu_get_be32(f);
+ qemu_get_buffer(f, s->rx[i].data, sizeof(s->rx[i].data));
+
+ }
+ s->next_packet = qemu_get_be32(f);
+ s->rx_fifo = s->rx[s->next_packet].data + qemu_get_be32(f);
+ s->rx_fifo_len = qemu_get_be32(f);
+
+ return 0;
+}
+
+static void stellaris_enet_cleanup(NetClientState *nc)
+{
+ stellaris_enet_state *s = qemu_get_nic_opaque(nc);
+
+ unregister_savevm(&s->busdev.qdev, "stellaris_enet", s);
+
+ memory_region_destroy(&s->mmio);
+
+ g_free(s);
+}
+
+static NetClientInfo net_stellaris_enet_info = {
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .size = sizeof(NICState),
+ .can_receive = stellaris_enet_can_receive,
+ .receive = stellaris_enet_receive,
+ .cleanup = stellaris_enet_cleanup,
+};
+
+static int stellaris_enet_init(SysBusDevice *dev)
+{
+ stellaris_enet_state *s = FROM_SYSBUS(stellaris_enet_state, dev);
+
+ memory_region_init_io(&s->mmio, &stellaris_enet_ops, s, "stellaris_enet",
+ 0x1000);
+ sysbus_init_mmio(dev, &s->mmio);
+ sysbus_init_irq(dev, &s->irq);
+ qemu_macaddr_default_if_unset(&s->conf.macaddr);
+
+ s->nic = qemu_new_nic(&net_stellaris_enet_info, &s->conf,
+ object_get_typename(OBJECT(dev)), dev->qdev.id, s);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+
+ stellaris_enet_reset(s);
+ register_savevm(&s->busdev.qdev, "stellaris_enet", -1, 1,
+ stellaris_enet_save, stellaris_enet_load, s);
+ return 0;
+}
+
+static Property stellaris_enet_properties[] = {
+ DEFINE_NIC_PROPERTIES(stellaris_enet_state, conf),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void stellaris_enet_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = stellaris_enet_init;
+ dc->props = stellaris_enet_properties;
+}
+
+static const TypeInfo stellaris_enet_info = {
+ .name = "stellaris_enet",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(stellaris_enet_state),
+ .class_init = stellaris_enet_class_init,
+};
+
+static void stellaris_enet_register_types(void)
+{
+ type_register_static(&stellaris_enet_info);
+}
+
+type_init(stellaris_enet_register_types)
--- /dev/null
+/*
+ * QEMU model of the Xilinx Ethernet Lite MAC.
+ *
+ * Copyright (c) 2009 Edgar E. Iglesias.
+ *
+ * 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 "hw/sysbus.h"
+#include "hw/hw.h"
+#include "net/net.h"
+
+#define D(x)
+#define R_TX_BUF0 0
+#define R_TX_LEN0 (0x07f4 / 4)
+#define R_TX_GIE0 (0x07f8 / 4)
+#define R_TX_CTRL0 (0x07fc / 4)
+#define R_TX_BUF1 (0x0800 / 4)
+#define R_TX_LEN1 (0x0ff4 / 4)
+#define R_TX_CTRL1 (0x0ffc / 4)
+
+#define R_RX_BUF0 (0x1000 / 4)
+#define R_RX_CTRL0 (0x17fc / 4)
+#define R_RX_BUF1 (0x1800 / 4)
+#define R_RX_CTRL1 (0x1ffc / 4)
+#define R_MAX (0x2000 / 4)
+
+#define GIE_GIE 0x80000000
+
+#define CTRL_I 0x8
+#define CTRL_P 0x2
+#define CTRL_S 0x1
+
+struct xlx_ethlite
+{
+ SysBusDevice busdev;
+ MemoryRegion mmio;
+ qemu_irq irq;
+ NICState *nic;
+ NICConf conf;
+
+ uint32_t c_tx_pingpong;
+ uint32_t c_rx_pingpong;
+ unsigned int txbuf;
+ unsigned int rxbuf;
+
+ uint32_t regs[R_MAX];
+};
+
+static inline void eth_pulse_irq(struct xlx_ethlite *s)
+{
+ /* Only the first gie reg is active. */
+ if (s->regs[R_TX_GIE0] & GIE_GIE) {
+ qemu_irq_pulse(s->irq);
+ }
+}
+
+static uint64_t
+eth_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ struct xlx_ethlite *s = opaque;
+ uint32_t r = 0;
+
+ addr >>= 2;
+
+ switch (addr)
+ {
+ case R_TX_GIE0:
+ case R_TX_LEN0:
+ case R_TX_LEN1:
+ case R_TX_CTRL1:
+ case R_TX_CTRL0:
+ case R_RX_CTRL1:
+ case R_RX_CTRL0:
+ r = s->regs[addr];
+ D(qemu_log("%s " TARGET_FMT_plx "=%x\n", __func__, addr * 4, r));
+ break;
+
+ default:
+ r = tswap32(s->regs[addr]);
+ break;
+ }
+ return r;
+}
+
+static void
+eth_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ struct xlx_ethlite *s = opaque;
+ unsigned int base = 0;
+ uint32_t value = val64;
+
+ addr >>= 2;
+ switch (addr)
+ {
+ case R_TX_CTRL0:
+ case R_TX_CTRL1:
+ if (addr == R_TX_CTRL1)
+ base = 0x800 / 4;
+
+ D(qemu_log("%s addr=" TARGET_FMT_plx " val=%x\n",
+ __func__, addr * 4, value));
+ if ((value & (CTRL_P | CTRL_S)) == CTRL_S) {
+ qemu_send_packet(qemu_get_queue(s->nic),
+ (void *) &s->regs[base],
+ s->regs[base + R_TX_LEN0]);
+ D(qemu_log("eth_tx %d\n", s->regs[base + R_TX_LEN0]));
+ if (s->regs[base + R_TX_CTRL0] & CTRL_I)
+ eth_pulse_irq(s);
+ } else if ((value & (CTRL_P | CTRL_S)) == (CTRL_P | CTRL_S)) {
+ memcpy(&s->conf.macaddr.a[0], &s->regs[base], 6);
+ if (s->regs[base + R_TX_CTRL0] & CTRL_I)
+ eth_pulse_irq(s);
+ }
+
+ /* We are fast and get ready pretty much immediately so
+ we actually never flip the S nor P bits to one. */
+ s->regs[addr] = value & ~(CTRL_P | CTRL_S);
+ break;
+
+ /* Keep these native. */
+ case R_RX_CTRL0:
+ case R_RX_CTRL1:
+ if (!(value & CTRL_S)) {
+ qemu_flush_queued_packets(qemu_get_queue(s->nic));
+ }
+ case R_TX_LEN0:
+ case R_TX_LEN1:
+ case R_TX_GIE0:
+ D(qemu_log("%s addr=" TARGET_FMT_plx " val=%x\n",
+ __func__, addr * 4, value));
+ s->regs[addr] = value;
+ break;
+
+ default:
+ s->regs[addr] = tswap32(value);
+ break;
+ }
+}
+
+static const MemoryRegionOps eth_ops = {
+ .read = eth_read,
+ .write = eth_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+static int eth_can_rx(NetClientState *nc)
+{
+ struct xlx_ethlite *s = qemu_get_nic_opaque(nc);
+ unsigned int rxbase = s->rxbuf * (0x800 / 4);
+
+ return !(s->regs[rxbase + R_RX_CTRL0] & CTRL_S);
+}
+
+static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+ struct xlx_ethlite *s = qemu_get_nic_opaque(nc);
+ unsigned int rxbase = s->rxbuf * (0x800 / 4);
+
+ /* DA filter. */
+ if (!(buf[0] & 0x80) && memcmp(&s->conf.macaddr.a[0], buf, 6))
+ return size;
+
+ if (s->regs[rxbase + R_RX_CTRL0] & CTRL_S) {
+ D(qemu_log("ethlite lost packet %x\n", s->regs[R_RX_CTRL0]));
+ return -1;
+ }
+
+ D(qemu_log("%s %zd rxbase=%x\n", __func__, size, rxbase));
+ memcpy(&s->regs[rxbase + R_RX_BUF0], buf, size);
+
+ s->regs[rxbase + R_RX_CTRL0] |= CTRL_S;
+ if (s->regs[rxbase + R_RX_CTRL0] & CTRL_I)
+ eth_pulse_irq(s);
+
+ /* If c_rx_pingpong was set flip buffers. */
+ s->rxbuf ^= s->c_rx_pingpong;
+ return size;
+}
+
+static void eth_cleanup(NetClientState *nc)
+{
+ struct xlx_ethlite *s = qemu_get_nic_opaque(nc);
+
+ s->nic = NULL;
+}
+
+static NetClientInfo net_xilinx_ethlite_info = {
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .size = sizeof(NICState),
+ .can_receive = eth_can_rx,
+ .receive = eth_rx,
+ .cleanup = eth_cleanup,
+};
+
+static int xilinx_ethlite_init(SysBusDevice *dev)
+{
+ struct xlx_ethlite *s = FROM_SYSBUS(typeof (*s), dev);
+
+ sysbus_init_irq(dev, &s->irq);
+ s->rxbuf = 0;
+
+ memory_region_init_io(&s->mmio, ð_ops, s, "xlnx.xps-ethernetlite",
+ R_MAX * 4);
+ sysbus_init_mmio(dev, &s->mmio);
+
+ qemu_macaddr_default_if_unset(&s->conf.macaddr);
+ s->nic = qemu_new_nic(&net_xilinx_ethlite_info, &s->conf,
+ object_get_typename(OBJECT(dev)), dev->qdev.id, s);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+ return 0;
+}
+
+static Property xilinx_ethlite_properties[] = {
+ DEFINE_PROP_UINT32("tx-ping-pong", struct xlx_ethlite, c_tx_pingpong, 1),
+ DEFINE_PROP_UINT32("rx-ping-pong", struct xlx_ethlite, c_rx_pingpong, 1),
+ DEFINE_NIC_PROPERTIES(struct xlx_ethlite, conf),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void xilinx_ethlite_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = xilinx_ethlite_init;
+ dc->props = xilinx_ethlite_properties;
+}
+
+static const TypeInfo xilinx_ethlite_info = {
+ .name = "xlnx.xps-ethernetlite",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(struct xlx_ethlite),
+ .class_init = xilinx_ethlite_class_init,
+};
+
+static void xilinx_ethlite_register_types(void)
+{
+ type_register_static(&xilinx_ethlite_info);
+}
+
+type_init(xilinx_ethlite_register_types)
# IBM pSeries (sPAPR)
-obj-$(CONFIG_PSERIES) += spapr_vty.o spapr_llan.o spapr_vscsi.o
+obj-$(CONFIG_PSERIES) += spapr_vty.o spapr_vscsi.o
obj-$(CONFIG_PSERIES) += spapr_pci.o
obj-$(CONFIG_PSERIES) += spapr_nvram.o
# PowerPC 4xx boards
# PowerPC OpenPIC
obj-y += openpic.o
-# Xilinx PPC peripherals
-obj-y += xilinx_ethlite.o
-
obj-y := $(addprefix ../,$(obj-y))
# shared objects
+++ /dev/null
-/*
- * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
- *
- * PAPR Inter-VM Logical Lan, aka ibmveth
- *
- * Copyright (c) 2010,2011 David Gibson, IBM Corporation.
- *
- * 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 "hw/hw.h"
-#include "net/net.h"
-#include "hw/qdev.h"
-#include "hw/ppc/spapr.h"
-#include "hw/ppc/spapr_vio.h"
-
-#include <libfdt.h>
-
-#define ETH_ALEN 6
-#define MAX_PACKET_SIZE 65536
-
-/*#define DEBUG*/
-
-#ifdef DEBUG
-#define dprintf(fmt...) do { fprintf(stderr, fmt); } while (0)
-#else
-#define dprintf(fmt...)
-#endif
-
-/*
- * Virtual LAN device
- */
-
-typedef uint64_t vlan_bd_t;
-
-#define VLAN_BD_VALID 0x8000000000000000ULL
-#define VLAN_BD_TOGGLE 0x4000000000000000ULL
-#define VLAN_BD_NO_CSUM 0x0200000000000000ULL
-#define VLAN_BD_CSUM_GOOD 0x0100000000000000ULL
-#define VLAN_BD_LEN_MASK 0x00ffffff00000000ULL
-#define VLAN_BD_LEN(bd) (((bd) & VLAN_BD_LEN_MASK) >> 32)
-#define VLAN_BD_ADDR_MASK 0x00000000ffffffffULL
-#define VLAN_BD_ADDR(bd) ((bd) & VLAN_BD_ADDR_MASK)
-
-#define VLAN_VALID_BD(addr, len) (VLAN_BD_VALID | \
- (((len) << 32) & VLAN_BD_LEN_MASK) | \
- (addr & VLAN_BD_ADDR_MASK))
-
-#define VLAN_RXQC_TOGGLE 0x80
-#define VLAN_RXQC_VALID 0x40
-#define VLAN_RXQC_NO_CSUM 0x02
-#define VLAN_RXQC_CSUM_GOOD 0x01
-
-#define VLAN_RQ_ALIGNMENT 16
-#define VLAN_RXQ_BD_OFF 0
-#define VLAN_FILTER_BD_OFF 8
-#define VLAN_RX_BDS_OFF 16
-#define VLAN_MAX_BUFS ((SPAPR_TCE_PAGE_SIZE - VLAN_RX_BDS_OFF) / 8)
-
-typedef struct VIOsPAPRVLANDevice {
- VIOsPAPRDevice sdev;
- NICConf nicconf;
- NICState *nic;
- int isopen;
- target_ulong buf_list;
- int add_buf_ptr, use_buf_ptr, rx_bufs;
- target_ulong rxq_ptr;
-} VIOsPAPRVLANDevice;
-
-static int spapr_vlan_can_receive(NetClientState *nc)
-{
- VIOsPAPRVLANDevice *dev = qemu_get_nic_opaque(nc);
-
- return (dev->isopen && dev->rx_bufs > 0);
-}
-
-static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf,
- size_t size)
-{
- VIOsPAPRDevice *sdev = qemu_get_nic_opaque(nc);
- VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev;
- vlan_bd_t rxq_bd = vio_ldq(sdev, dev->buf_list + VLAN_RXQ_BD_OFF);
- vlan_bd_t bd;
- int buf_ptr = dev->use_buf_ptr;
- uint64_t handle;
- uint8_t control;
-
- dprintf("spapr_vlan_receive() [%s] rx_bufs=%d\n", sdev->qdev.id,
- dev->rx_bufs);
-
- if (!dev->isopen) {
- return -1;
- }
-
- if (!dev->rx_bufs) {
- return -1;
- }
-
- do {
- buf_ptr += 8;
- if (buf_ptr >= SPAPR_TCE_PAGE_SIZE) {
- buf_ptr = VLAN_RX_BDS_OFF;
- }
-
- bd = vio_ldq(sdev, dev->buf_list + buf_ptr);
- dprintf("use_buf_ptr=%d bd=0x%016llx\n",
- buf_ptr, (unsigned long long)bd);
- } while ((!(bd & VLAN_BD_VALID) || (VLAN_BD_LEN(bd) < (size + 8)))
- && (buf_ptr != dev->use_buf_ptr));
-
- if (!(bd & VLAN_BD_VALID) || (VLAN_BD_LEN(bd) < (size + 8))) {
- /* Failed to find a suitable buffer */
- return -1;
- }
-
- /* Remove the buffer from the pool */
- dev->rx_bufs--;
- dev->use_buf_ptr = buf_ptr;
- vio_stq(sdev, dev->buf_list + dev->use_buf_ptr, 0);
-
- dprintf("Found buffer: ptr=%d num=%d\n", dev->use_buf_ptr, dev->rx_bufs);
-
- /* Transfer the packet data */
- if (spapr_vio_dma_write(sdev, VLAN_BD_ADDR(bd) + 8, buf, size) < 0) {
- return -1;
- }
-
- dprintf("spapr_vlan_receive: DMA write completed\n");
-
- /* Update the receive queue */
- control = VLAN_RXQC_TOGGLE | VLAN_RXQC_VALID;
- if (rxq_bd & VLAN_BD_TOGGLE) {
- control ^= VLAN_RXQC_TOGGLE;
- }
-
- handle = vio_ldq(sdev, VLAN_BD_ADDR(bd));
- vio_stq(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 8, handle);
- vio_stl(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 4, size);
- vio_sth(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 2, 8);
- vio_stb(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr, control);
-
- dprintf("wrote rxq entry (ptr=0x%llx): 0x%016llx 0x%016llx\n",
- (unsigned long long)dev->rxq_ptr,
- (unsigned long long)vio_ldq(sdev, VLAN_BD_ADDR(rxq_bd) +
- dev->rxq_ptr),
- (unsigned long long)vio_ldq(sdev, VLAN_BD_ADDR(rxq_bd) +
- dev->rxq_ptr + 8));
-
- dev->rxq_ptr += 16;
- if (dev->rxq_ptr >= VLAN_BD_LEN(rxq_bd)) {
- dev->rxq_ptr = 0;
- vio_stq(sdev, dev->buf_list + VLAN_RXQ_BD_OFF, rxq_bd ^ VLAN_BD_TOGGLE);
- }
-
- if (sdev->signal_state & 1) {
- qemu_irq_pulse(spapr_vio_qirq(sdev));
- }
-
- return size;
-}
-
-static void spapr_vlan_cleanup(NetClientState *nc)
-{
- VIOsPAPRVLANDevice *dev = qemu_get_nic_opaque(nc);
-
- dev->nic = NULL;
-}
-
-static NetClientInfo net_spapr_vlan_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
- .size = sizeof(NICState),
- .can_receive = spapr_vlan_can_receive,
- .receive = spapr_vlan_receive,
- .cleanup = spapr_vlan_cleanup,
-};
-
-static void spapr_vlan_reset(VIOsPAPRDevice *sdev)
-{
- VIOsPAPRVLANDevice *dev = DO_UPCAST(VIOsPAPRVLANDevice, sdev, sdev);
-
- dev->buf_list = 0;
- dev->rx_bufs = 0;
- dev->isopen = 0;
-}
-
-static int spapr_vlan_init(VIOsPAPRDevice *sdev)
-{
- VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev;
-
- qemu_macaddr_default_if_unset(&dev->nicconf.macaddr);
-
- dev->nic = qemu_new_nic(&net_spapr_vlan_info, &dev->nicconf,
- object_get_typename(OBJECT(sdev)), sdev->qdev.id, dev);
- qemu_format_nic_info_str(qemu_get_queue(dev->nic), dev->nicconf.macaddr.a);
-
- return 0;
-}
-
-void spapr_vlan_create(VIOsPAPRBus *bus, NICInfo *nd)
-{
- DeviceState *dev;
-
- dev = qdev_create(&bus->bus, "spapr-vlan");
-
- qdev_set_nic_properties(dev, nd);
-
- qdev_init_nofail(dev);
-}
-
-static int spapr_vlan_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off)
-{
- VIOsPAPRVLANDevice *vdev = (VIOsPAPRVLANDevice *)dev;
- uint8_t padded_mac[8] = {0, 0};
- int ret;
-
- /* Some old phyp versions give the mac address in an 8-byte
- * property. The kernel driver has an insane workaround for this;
- * rather than doing the obvious thing and checking the property
- * length, it checks whether the first byte has 0b10 in the low
- * bits. If a correct 6-byte property has a different first byte
- * the kernel will get the wrong mac address, overrunning its
- * buffer in the process (read only, thank goodness).
- *
- * Here we workaround the kernel workaround by always supplying an
- * 8-byte property, with the mac address in the last six bytes */
- memcpy(&padded_mac[2], &vdev->nicconf.macaddr, ETH_ALEN);
- ret = fdt_setprop(fdt, node_off, "local-mac-address",
- padded_mac, sizeof(padded_mac));
- if (ret < 0) {
- return ret;
- }
-
- ret = fdt_setprop_cell(fdt, node_off, "ibm,mac-address-filters", 0);
- if (ret < 0) {
- return ret;
- }
-
- return 0;
-}
-
-static int check_bd(VIOsPAPRVLANDevice *dev, vlan_bd_t bd,
- target_ulong alignment)
-{
- if ((VLAN_BD_ADDR(bd) % alignment)
- || (VLAN_BD_LEN(bd) % alignment)) {
- return -1;
- }
-
- if (!spapr_vio_dma_valid(&dev->sdev, VLAN_BD_ADDR(bd),
- VLAN_BD_LEN(bd), DMA_DIRECTION_FROM_DEVICE)
- || !spapr_vio_dma_valid(&dev->sdev, VLAN_BD_ADDR(bd),
- VLAN_BD_LEN(bd), DMA_DIRECTION_TO_DEVICE)) {
- return -1;
- }
-
- return 0;
-}
-
-static target_ulong h_register_logical_lan(PowerPCCPU *cpu,
- sPAPREnvironment *spapr,
- target_ulong opcode,
- target_ulong *args)
-{
- target_ulong reg = args[0];
- target_ulong buf_list = args[1];
- target_ulong rec_queue = args[2];
- target_ulong filter_list = args[3];
- VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
- VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev;
- vlan_bd_t filter_list_bd;
-
- if (!dev) {
- return H_PARAMETER;
- }
-
- if (dev->isopen) {
- hcall_dprintf("H_REGISTER_LOGICAL_LAN called twice without "
- "H_FREE_LOGICAL_LAN\n");
- return H_RESOURCE;
- }
-
- if (check_bd(dev, VLAN_VALID_BD(buf_list, SPAPR_TCE_PAGE_SIZE),
- SPAPR_TCE_PAGE_SIZE) < 0) {
- hcall_dprintf("Bad buf_list 0x" TARGET_FMT_lx "\n", buf_list);
- return H_PARAMETER;
- }
-
- filter_list_bd = VLAN_VALID_BD(filter_list, SPAPR_TCE_PAGE_SIZE);
- if (check_bd(dev, filter_list_bd, SPAPR_TCE_PAGE_SIZE) < 0) {
- hcall_dprintf("Bad filter_list 0x" TARGET_FMT_lx "\n", filter_list);
- return H_PARAMETER;
- }
-
- if (!(rec_queue & VLAN_BD_VALID)
- || (check_bd(dev, rec_queue, VLAN_RQ_ALIGNMENT) < 0)) {
- hcall_dprintf("Bad receive queue\n");
- return H_PARAMETER;
- }
-
- dev->buf_list = buf_list;
- sdev->signal_state = 0;
-
- rec_queue &= ~VLAN_BD_TOGGLE;
-
- /* Initialize the buffer list */
- vio_stq(sdev, buf_list, rec_queue);
- vio_stq(sdev, buf_list + 8, filter_list_bd);
- spapr_vio_dma_set(sdev, buf_list + VLAN_RX_BDS_OFF, 0,
- SPAPR_TCE_PAGE_SIZE - VLAN_RX_BDS_OFF);
- dev->add_buf_ptr = VLAN_RX_BDS_OFF - 8;
- dev->use_buf_ptr = VLAN_RX_BDS_OFF - 8;
- dev->rx_bufs = 0;
- dev->rxq_ptr = 0;
-
- /* Initialize the receive queue */
- spapr_vio_dma_set(sdev, VLAN_BD_ADDR(rec_queue), 0, VLAN_BD_LEN(rec_queue));
-
- dev->isopen = 1;
- return H_SUCCESS;
-}
-
-
-static target_ulong h_free_logical_lan(PowerPCCPU *cpu, sPAPREnvironment *spapr,
- target_ulong opcode, target_ulong *args)
-{
- target_ulong reg = args[0];
- VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
- VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev;
-
- if (!dev) {
- return H_PARAMETER;
- }
-
- if (!dev->isopen) {
- hcall_dprintf("H_FREE_LOGICAL_LAN called without "
- "H_REGISTER_LOGICAL_LAN\n");
- return H_RESOURCE;
- }
-
- spapr_vlan_reset(sdev);
- return H_SUCCESS;
-}
-
-static target_ulong h_add_logical_lan_buffer(PowerPCCPU *cpu,
- sPAPREnvironment *spapr,
- target_ulong opcode,
- target_ulong *args)
-{
- target_ulong reg = args[0];
- target_ulong buf = args[1];
- VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
- VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev;
- vlan_bd_t bd;
-
- dprintf("H_ADD_LOGICAL_LAN_BUFFER(0x" TARGET_FMT_lx
- ", 0x" TARGET_FMT_lx ")\n", reg, buf);
-
- if (!sdev) {
- hcall_dprintf("Bad device\n");
- return H_PARAMETER;
- }
-
- if ((check_bd(dev, buf, 4) < 0)
- || (VLAN_BD_LEN(buf) < 16)) {
- hcall_dprintf("Bad buffer enqueued\n");
- return H_PARAMETER;
- }
-
- if (!dev->isopen || dev->rx_bufs >= VLAN_MAX_BUFS) {
- return H_RESOURCE;
- }
-
- do {
- dev->add_buf_ptr += 8;
- if (dev->add_buf_ptr >= SPAPR_TCE_PAGE_SIZE) {
- dev->add_buf_ptr = VLAN_RX_BDS_OFF;
- }
-
- bd = vio_ldq(sdev, dev->buf_list + dev->add_buf_ptr);
- } while (bd & VLAN_BD_VALID);
-
- vio_stq(sdev, dev->buf_list + dev->add_buf_ptr, buf);
-
- dev->rx_bufs++;
-
- dprintf("h_add_logical_lan_buffer(): Added buf ptr=%d rx_bufs=%d"
- " bd=0x%016llx\n", dev->add_buf_ptr, dev->rx_bufs,
- (unsigned long long)buf);
-
- return H_SUCCESS;
-}
-
-static target_ulong h_send_logical_lan(PowerPCCPU *cpu, sPAPREnvironment *spapr,
- target_ulong opcode, target_ulong *args)
-{
- target_ulong reg = args[0];
- target_ulong *bufs = args + 1;
- target_ulong continue_token = args[7];
- VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
- VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev;
- unsigned total_len;
- uint8_t *lbuf, *p;
- int i, nbufs;
- int ret;
-
- dprintf("H_SEND_LOGICAL_LAN(0x" TARGET_FMT_lx ", <bufs>, 0x"
- TARGET_FMT_lx ")\n", reg, continue_token);
-
- if (!sdev) {
- return H_PARAMETER;
- }
-
- dprintf("rxbufs = %d\n", dev->rx_bufs);
-
- if (!dev->isopen) {
- return H_DROPPED;
- }
-
- if (continue_token) {
- return H_HARDWARE; /* FIXME actually handle this */
- }
-
- total_len = 0;
- for (i = 0; i < 6; i++) {
- dprintf(" buf desc: 0x" TARGET_FMT_lx "\n", bufs[i]);
- if (!(bufs[i] & VLAN_BD_VALID)) {
- break;
- }
- total_len += VLAN_BD_LEN(bufs[i]);
- }
-
- nbufs = i;
- dprintf("h_send_logical_lan() %d buffers, total length 0x%x\n",
- nbufs, total_len);
-
- if (total_len == 0) {
- return H_SUCCESS;
- }
-
- if (total_len > MAX_PACKET_SIZE) {
- /* Don't let the guest force too large an allocation */
- return H_RESOURCE;
- }
-
- lbuf = alloca(total_len);
- p = lbuf;
- for (i = 0; i < nbufs; i++) {
- ret = spapr_vio_dma_read(sdev, VLAN_BD_ADDR(bufs[i]),
- p, VLAN_BD_LEN(bufs[i]));
- if (ret < 0) {
- return ret;
- }
-
- p += VLAN_BD_LEN(bufs[i]);
- }
-
- qemu_send_packet(qemu_get_queue(dev->nic), lbuf, total_len);
-
- return H_SUCCESS;
-}
-
-static target_ulong h_multicast_ctrl(PowerPCCPU *cpu, sPAPREnvironment *spapr,
- target_ulong opcode, target_ulong *args)
-{
- target_ulong reg = args[0];
- VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
-
- if (!dev) {
- return H_PARAMETER;
- }
-
- return H_SUCCESS;
-}
-
-static Property spapr_vlan_properties[] = {
- DEFINE_SPAPR_PROPERTIES(VIOsPAPRVLANDevice, sdev),
- DEFINE_NIC_PROPERTIES(VIOsPAPRVLANDevice, nicconf),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void spapr_vlan_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- VIOsPAPRDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass);
-
- k->init = spapr_vlan_init;
- k->reset = spapr_vlan_reset;
- k->devnode = spapr_vlan_devnode;
- k->dt_name = "l-lan";
- k->dt_type = "network";
- k->dt_compatible = "IBM,l-lan";
- k->signal_mask = 0x1;
- dc->props = spapr_vlan_properties;
- k->rtce_window_size = 0x10000000;
-}
-
-static const TypeInfo spapr_vlan_info = {
- .name = "spapr-vlan",
- .parent = TYPE_VIO_SPAPR_DEVICE,
- .instance_size = sizeof(VIOsPAPRVLANDevice),
- .class_init = spapr_vlan_class_init,
-};
-
-static void spapr_vlan_register_types(void)
-{
- spapr_register_hypercall(H_REGISTER_LOGICAL_LAN, h_register_logical_lan);
- spapr_register_hypercall(H_FREE_LOGICAL_LAN, h_free_logical_lan);
- spapr_register_hypercall(H_SEND_LOGICAL_LAN, h_send_logical_lan);
- spapr_register_hypercall(H_ADD_LOGICAL_LAN_BUFFER,
- h_add_logical_lan_buffer);
- spapr_register_hypercall(H_MULTICAST_CTRL, h_multicast_ctrl);
- type_register_static(&spapr_vlan_info);
-}
-
-type_init(spapr_vlan_register_types)
-obj-y = lance.o tcx.o sun4m_iommu.o slavio_intctl.o
+obj-y = tcx.o sun4m_iommu.o slavio_intctl.o
obj-y += slavio_timer.o slavio_misc.o sparc32_dma.o
obj-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o
+++ /dev/null
-/*
- * Luminary Micro Stellaris Ethernet Controller
- *
- * Copyright (c) 2007 CodeSourcery.
- * Written by Paul Brook
- *
- * This code is licensed under the GPL.
- */
-#include "hw/sysbus.h"
-#include "net/net.h"
-#include <zlib.h>
-
-//#define DEBUG_STELLARIS_ENET 1
-
-#ifdef DEBUG_STELLARIS_ENET
-#define DPRINTF(fmt, ...) \
-do { printf("stellaris_enet: " fmt , ## __VA_ARGS__); } while (0)
-#define BADF(fmt, ...) \
-do { fprintf(stderr, "stellaris_enet: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
-#else
-#define DPRINTF(fmt, ...) do {} while(0)
-#define BADF(fmt, ...) \
-do { fprintf(stderr, "stellaris_enet: error: " fmt , ## __VA_ARGS__);} while (0)
-#endif
-
-#define SE_INT_RX 0x01
-#define SE_INT_TXER 0x02
-#define SE_INT_TXEMP 0x04
-#define SE_INT_FOV 0x08
-#define SE_INT_RXER 0x10
-#define SE_INT_MD 0x20
-#define SE_INT_PHY 0x40
-
-#define SE_RCTL_RXEN 0x01
-#define SE_RCTL_AMUL 0x02
-#define SE_RCTL_PRMS 0x04
-#define SE_RCTL_BADCRC 0x08
-#define SE_RCTL_RSTFIFO 0x10
-
-#define SE_TCTL_TXEN 0x01
-#define SE_TCTL_PADEN 0x02
-#define SE_TCTL_CRC 0x04
-#define SE_TCTL_DUPLEX 0x08
-
-typedef struct {
- SysBusDevice busdev;
- uint32_t ris;
- uint32_t im;
- uint32_t rctl;
- uint32_t tctl;
- uint32_t thr;
- uint32_t mctl;
- uint32_t mdv;
- uint32_t mtxd;
- uint32_t mrxd;
- uint32_t np;
- int tx_frame_len;
- int tx_fifo_len;
- uint8_t tx_fifo[2048];
- /* Real hardware has a 2k fifo, which works out to be at most 31 packets.
- We implement a full 31 packet fifo. */
- struct {
- uint8_t data[2048];
- int len;
- } rx[31];
- uint8_t *rx_fifo;
- int rx_fifo_len;
- int next_packet;
- NICState *nic;
- NICConf conf;
- qemu_irq irq;
- MemoryRegion mmio;
-} stellaris_enet_state;
-
-static void stellaris_enet_update(stellaris_enet_state *s)
-{
- qemu_set_irq(s->irq, (s->ris & s->im) != 0);
-}
-
-/* TODO: Implement MAC address filtering. */
-static ssize_t stellaris_enet_receive(NetClientState *nc, const uint8_t *buf, size_t size)
-{
- stellaris_enet_state *s = qemu_get_nic_opaque(nc);
- int n;
- uint8_t *p;
- uint32_t crc;
-
- if ((s->rctl & SE_RCTL_RXEN) == 0)
- return -1;
- if (s->np >= 31) {
- DPRINTF("Packet dropped\n");
- return -1;
- }
-
- DPRINTF("Received packet len=%d\n", size);
- n = s->next_packet + s->np;
- if (n >= 31)
- n -= 31;
- s->np++;
-
- s->rx[n].len = size + 6;
- p = s->rx[n].data;
- *(p++) = (size + 6);
- *(p++) = (size + 6) >> 8;
- memcpy (p, buf, size);
- p += size;
- crc = crc32(~0, buf, size);
- *(p++) = crc;
- *(p++) = crc >> 8;
- *(p++) = crc >> 16;
- *(p++) = crc >> 24;
- /* Clear the remaining bytes in the last word. */
- if ((size & 3) != 2) {
- memset(p, 0, (6 - size) & 3);
- }
-
- s->ris |= SE_INT_RX;
- stellaris_enet_update(s);
-
- return size;
-}
-
-static int stellaris_enet_can_receive(NetClientState *nc)
-{
- stellaris_enet_state *s = qemu_get_nic_opaque(nc);
-
- if ((s->rctl & SE_RCTL_RXEN) == 0)
- return 1;
-
- return (s->np < 31);
-}
-
-static uint64_t stellaris_enet_read(void *opaque, hwaddr offset,
- unsigned size)
-{
- stellaris_enet_state *s = (stellaris_enet_state *)opaque;
- uint32_t val;
-
- switch (offset) {
- case 0x00: /* RIS */
- DPRINTF("IRQ status %02x\n", s->ris);
- return s->ris;
- case 0x04: /* IM */
- return s->im;
- case 0x08: /* RCTL */
- return s->rctl;
- case 0x0c: /* TCTL */
- return s->tctl;
- case 0x10: /* DATA */
- if (s->rx_fifo_len == 0) {
- if (s->np == 0) {
- BADF("RX underflow\n");
- return 0;
- }
- s->rx_fifo_len = s->rx[s->next_packet].len;
- s->rx_fifo = s->rx[s->next_packet].data;
- DPRINTF("RX FIFO start packet len=%d\n", s->rx_fifo_len);
- }
- val = s->rx_fifo[0] | (s->rx_fifo[1] << 8) | (s->rx_fifo[2] << 16)
- | (s->rx_fifo[3] << 24);
- s->rx_fifo += 4;
- s->rx_fifo_len -= 4;
- if (s->rx_fifo_len <= 0) {
- s->rx_fifo_len = 0;
- s->next_packet++;
- if (s->next_packet >= 31)
- s->next_packet = 0;
- s->np--;
- DPRINTF("RX done np=%d\n", s->np);
- }
- return val;
- case 0x14: /* IA0 */
- return s->conf.macaddr.a[0] | (s->conf.macaddr.a[1] << 8)
- | (s->conf.macaddr.a[2] << 16) | (s->conf.macaddr.a[3] << 24);
- case 0x18: /* IA1 */
- return s->conf.macaddr.a[4] | (s->conf.macaddr.a[5] << 8);
- case 0x1c: /* THR */
- return s->thr;
- case 0x20: /* MCTL */
- return s->mctl;
- case 0x24: /* MDV */
- return s->mdv;
- case 0x28: /* MADD */
- return 0;
- case 0x2c: /* MTXD */
- return s->mtxd;
- case 0x30: /* MRXD */
- return s->mrxd;
- case 0x34: /* NP */
- return s->np;
- case 0x38: /* TR */
- return 0;
- case 0x3c: /* Undocuented: Timestamp? */
- return 0;
- default:
- hw_error("stellaris_enet_read: Bad offset %x\n", (int)offset);
- return 0;
- }
-}
-
-static void stellaris_enet_write(void *opaque, hwaddr offset,
- uint64_t value, unsigned size)
-{
- stellaris_enet_state *s = (stellaris_enet_state *)opaque;
-
- switch (offset) {
- case 0x00: /* IACK */
- s->ris &= ~value;
- DPRINTF("IRQ ack %02x/%02x\n", value, s->ris);
- stellaris_enet_update(s);
- /* Clearing TXER also resets the TX fifo. */
- if (value & SE_INT_TXER)
- s->tx_frame_len = -1;
- break;
- case 0x04: /* IM */
- DPRINTF("IRQ mask %02x/%02x\n", value, s->ris);
- s->im = value;
- stellaris_enet_update(s);
- break;
- case 0x08: /* RCTL */
- s->rctl = value;
- if (value & SE_RCTL_RSTFIFO) {
- s->rx_fifo_len = 0;
- s->np = 0;
- stellaris_enet_update(s);
- }
- break;
- case 0x0c: /* TCTL */
- s->tctl = value;
- break;
- case 0x10: /* DATA */
- if (s->tx_frame_len == -1) {
- s->tx_frame_len = value & 0xffff;
- if (s->tx_frame_len > 2032) {
- DPRINTF("TX frame too long (%d)\n", s->tx_frame_len);
- s->tx_frame_len = 0;
- s->ris |= SE_INT_TXER;
- stellaris_enet_update(s);
- } else {
- DPRINTF("Start TX frame len=%d\n", s->tx_frame_len);
- /* The value written does not include the ethernet header. */
- s->tx_frame_len += 14;
- if ((s->tctl & SE_TCTL_CRC) == 0)
- s->tx_frame_len += 4;
- s->tx_fifo_len = 0;
- s->tx_fifo[s->tx_fifo_len++] = value >> 16;
- s->tx_fifo[s->tx_fifo_len++] = value >> 24;
- }
- } else {
- s->tx_fifo[s->tx_fifo_len++] = value;
- s->tx_fifo[s->tx_fifo_len++] = value >> 8;
- s->tx_fifo[s->tx_fifo_len++] = value >> 16;
- s->tx_fifo[s->tx_fifo_len++] = value >> 24;
- if (s->tx_fifo_len >= s->tx_frame_len) {
- /* We don't implement explicit CRC, so just chop it off. */
- if ((s->tctl & SE_TCTL_CRC) == 0)
- s->tx_frame_len -= 4;
- if ((s->tctl & SE_TCTL_PADEN) && s->tx_frame_len < 60) {
- memset(&s->tx_fifo[s->tx_frame_len], 0, 60 - s->tx_frame_len);
- s->tx_fifo_len = 60;
- }
- qemu_send_packet(qemu_get_queue(s->nic), s->tx_fifo,
- s->tx_frame_len);
- s->tx_frame_len = -1;
- s->ris |= SE_INT_TXEMP;
- stellaris_enet_update(s);
- DPRINTF("Done TX\n");
- }
- }
- break;
- case 0x14: /* IA0 */
- s->conf.macaddr.a[0] = value;
- s->conf.macaddr.a[1] = value >> 8;
- s->conf.macaddr.a[2] = value >> 16;
- s->conf.macaddr.a[3] = value >> 24;
- break;
- case 0x18: /* IA1 */
- s->conf.macaddr.a[4] = value;
- s->conf.macaddr.a[5] = value >> 8;
- break;
- case 0x1c: /* THR */
- s->thr = value;
- break;
- case 0x20: /* MCTL */
- s->mctl = value;
- break;
- case 0x24: /* MDV */
- s->mdv = value;
- break;
- case 0x28: /* MADD */
- /* ignored. */
- break;
- case 0x2c: /* MTXD */
- s->mtxd = value & 0xff;
- break;
- case 0x30: /* MRXD */
- case 0x34: /* NP */
- case 0x38: /* TR */
- /* Ignored. */
- case 0x3c: /* Undocuented: Timestamp? */
- /* Ignored. */
- break;
- default:
- hw_error("stellaris_enet_write: Bad offset %x\n", (int)offset);
- }
-}
-
-static const MemoryRegionOps stellaris_enet_ops = {
- .read = stellaris_enet_read,
- .write = stellaris_enet_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void stellaris_enet_reset(stellaris_enet_state *s)
-{
- s->mdv = 0x80;
- s->rctl = SE_RCTL_BADCRC;
- s->im = SE_INT_PHY | SE_INT_MD | SE_INT_RXER | SE_INT_FOV | SE_INT_TXEMP
- | SE_INT_TXER | SE_INT_RX;
- s->thr = 0x3f;
- s->tx_frame_len = -1;
-}
-
-static void stellaris_enet_save(QEMUFile *f, void *opaque)
-{
- stellaris_enet_state *s = (stellaris_enet_state *)opaque;
- int i;
-
- qemu_put_be32(f, s->ris);
- qemu_put_be32(f, s->im);
- qemu_put_be32(f, s->rctl);
- qemu_put_be32(f, s->tctl);
- qemu_put_be32(f, s->thr);
- qemu_put_be32(f, s->mctl);
- qemu_put_be32(f, s->mdv);
- qemu_put_be32(f, s->mtxd);
- qemu_put_be32(f, s->mrxd);
- qemu_put_be32(f, s->np);
- qemu_put_be32(f, s->tx_frame_len);
- qemu_put_be32(f, s->tx_fifo_len);
- qemu_put_buffer(f, s->tx_fifo, sizeof(s->tx_fifo));
- for (i = 0; i < 31; i++) {
- qemu_put_be32(f, s->rx[i].len);
- qemu_put_buffer(f, s->rx[i].data, sizeof(s->rx[i].data));
-
- }
- qemu_put_be32(f, s->next_packet);
- qemu_put_be32(f, s->rx_fifo - s->rx[s->next_packet].data);
- qemu_put_be32(f, s->rx_fifo_len);
-}
-
-static int stellaris_enet_load(QEMUFile *f, void *opaque, int version_id)
-{
- stellaris_enet_state *s = (stellaris_enet_state *)opaque;
- int i;
-
- if (version_id != 1)
- return -EINVAL;
-
- s->ris = qemu_get_be32(f);
- s->im = qemu_get_be32(f);
- s->rctl = qemu_get_be32(f);
- s->tctl = qemu_get_be32(f);
- s->thr = qemu_get_be32(f);
- s->mctl = qemu_get_be32(f);
- s->mdv = qemu_get_be32(f);
- s->mtxd = qemu_get_be32(f);
- s->mrxd = qemu_get_be32(f);
- s->np = qemu_get_be32(f);
- s->tx_frame_len = qemu_get_be32(f);
- s->tx_fifo_len = qemu_get_be32(f);
- qemu_get_buffer(f, s->tx_fifo, sizeof(s->tx_fifo));
- for (i = 0; i < 31; i++) {
- s->rx[i].len = qemu_get_be32(f);
- qemu_get_buffer(f, s->rx[i].data, sizeof(s->rx[i].data));
-
- }
- s->next_packet = qemu_get_be32(f);
- s->rx_fifo = s->rx[s->next_packet].data + qemu_get_be32(f);
- s->rx_fifo_len = qemu_get_be32(f);
-
- return 0;
-}
-
-static void stellaris_enet_cleanup(NetClientState *nc)
-{
- stellaris_enet_state *s = qemu_get_nic_opaque(nc);
-
- unregister_savevm(&s->busdev.qdev, "stellaris_enet", s);
-
- memory_region_destroy(&s->mmio);
-
- g_free(s);
-}
-
-static NetClientInfo net_stellaris_enet_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
- .size = sizeof(NICState),
- .can_receive = stellaris_enet_can_receive,
- .receive = stellaris_enet_receive,
- .cleanup = stellaris_enet_cleanup,
-};
-
-static int stellaris_enet_init(SysBusDevice *dev)
-{
- stellaris_enet_state *s = FROM_SYSBUS(stellaris_enet_state, dev);
-
- memory_region_init_io(&s->mmio, &stellaris_enet_ops, s, "stellaris_enet",
- 0x1000);
- sysbus_init_mmio(dev, &s->mmio);
- sysbus_init_irq(dev, &s->irq);
- qemu_macaddr_default_if_unset(&s->conf.macaddr);
-
- s->nic = qemu_new_nic(&net_stellaris_enet_info, &s->conf,
- object_get_typename(OBJECT(dev)), dev->qdev.id, s);
- qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
-
- stellaris_enet_reset(s);
- register_savevm(&s->busdev.qdev, "stellaris_enet", -1, 1,
- stellaris_enet_save, stellaris_enet_load, s);
- return 0;
-}
-
-static Property stellaris_enet_properties[] = {
- DEFINE_NIC_PROPERTIES(stellaris_enet_state, conf),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void stellaris_enet_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
- k->init = stellaris_enet_init;
- dc->props = stellaris_enet_properties;
-}
-
-static const TypeInfo stellaris_enet_info = {
- .name = "stellaris_enet",
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(stellaris_enet_state),
- .class_init = stellaris_enet_class_init,
-};
-
-static void stellaris_enet_register_types(void)
-{
- type_register_static(&stellaris_enet_info);
-}
-
-type_init(stellaris_enet_register_types)
+++ /dev/null
-/*
- * QEMU model of the Xilinx Ethernet Lite MAC.
- *
- * Copyright (c) 2009 Edgar E. Iglesias.
- *
- * 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 "hw/sysbus.h"
-#include "hw/hw.h"
-#include "net/net.h"
-
-#define D(x)
-#define R_TX_BUF0 0
-#define R_TX_LEN0 (0x07f4 / 4)
-#define R_TX_GIE0 (0x07f8 / 4)
-#define R_TX_CTRL0 (0x07fc / 4)
-#define R_TX_BUF1 (0x0800 / 4)
-#define R_TX_LEN1 (0x0ff4 / 4)
-#define R_TX_CTRL1 (0x0ffc / 4)
-
-#define R_RX_BUF0 (0x1000 / 4)
-#define R_RX_CTRL0 (0x17fc / 4)
-#define R_RX_BUF1 (0x1800 / 4)
-#define R_RX_CTRL1 (0x1ffc / 4)
-#define R_MAX (0x2000 / 4)
-
-#define GIE_GIE 0x80000000
-
-#define CTRL_I 0x8
-#define CTRL_P 0x2
-#define CTRL_S 0x1
-
-struct xlx_ethlite
-{
- SysBusDevice busdev;
- MemoryRegion mmio;
- qemu_irq irq;
- NICState *nic;
- NICConf conf;
-
- uint32_t c_tx_pingpong;
- uint32_t c_rx_pingpong;
- unsigned int txbuf;
- unsigned int rxbuf;
-
- uint32_t regs[R_MAX];
-};
-
-static inline void eth_pulse_irq(struct xlx_ethlite *s)
-{
- /* Only the first gie reg is active. */
- if (s->regs[R_TX_GIE0] & GIE_GIE) {
- qemu_irq_pulse(s->irq);
- }
-}
-
-static uint64_t
-eth_read(void *opaque, hwaddr addr, unsigned int size)
-{
- struct xlx_ethlite *s = opaque;
- uint32_t r = 0;
-
- addr >>= 2;
-
- switch (addr)
- {
- case R_TX_GIE0:
- case R_TX_LEN0:
- case R_TX_LEN1:
- case R_TX_CTRL1:
- case R_TX_CTRL0:
- case R_RX_CTRL1:
- case R_RX_CTRL0:
- r = s->regs[addr];
- D(qemu_log("%s " TARGET_FMT_plx "=%x\n", __func__, addr * 4, r));
- break;
-
- default:
- r = tswap32(s->regs[addr]);
- break;
- }
- return r;
-}
-
-static void
-eth_write(void *opaque, hwaddr addr,
- uint64_t val64, unsigned int size)
-{
- struct xlx_ethlite *s = opaque;
- unsigned int base = 0;
- uint32_t value = val64;
-
- addr >>= 2;
- switch (addr)
- {
- case R_TX_CTRL0:
- case R_TX_CTRL1:
- if (addr == R_TX_CTRL1)
- base = 0x800 / 4;
-
- D(qemu_log("%s addr=" TARGET_FMT_plx " val=%x\n",
- __func__, addr * 4, value));
- if ((value & (CTRL_P | CTRL_S)) == CTRL_S) {
- qemu_send_packet(qemu_get_queue(s->nic),
- (void *) &s->regs[base],
- s->regs[base + R_TX_LEN0]);
- D(qemu_log("eth_tx %d\n", s->regs[base + R_TX_LEN0]));
- if (s->regs[base + R_TX_CTRL0] & CTRL_I)
- eth_pulse_irq(s);
- } else if ((value & (CTRL_P | CTRL_S)) == (CTRL_P | CTRL_S)) {
- memcpy(&s->conf.macaddr.a[0], &s->regs[base], 6);
- if (s->regs[base + R_TX_CTRL0] & CTRL_I)
- eth_pulse_irq(s);
- }
-
- /* We are fast and get ready pretty much immediately so
- we actually never flip the S nor P bits to one. */
- s->regs[addr] = value & ~(CTRL_P | CTRL_S);
- break;
-
- /* Keep these native. */
- case R_RX_CTRL0:
- case R_RX_CTRL1:
- if (!(value & CTRL_S)) {
- qemu_flush_queued_packets(qemu_get_queue(s->nic));
- }
- case R_TX_LEN0:
- case R_TX_LEN1:
- case R_TX_GIE0:
- D(qemu_log("%s addr=" TARGET_FMT_plx " val=%x\n",
- __func__, addr * 4, value));
- s->regs[addr] = value;
- break;
-
- default:
- s->regs[addr] = tswap32(value);
- break;
- }
-}
-
-static const MemoryRegionOps eth_ops = {
- .read = eth_read,
- .write = eth_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
- .valid = {
- .min_access_size = 4,
- .max_access_size = 4
- }
-};
-
-static int eth_can_rx(NetClientState *nc)
-{
- struct xlx_ethlite *s = qemu_get_nic_opaque(nc);
- unsigned int rxbase = s->rxbuf * (0x800 / 4);
-
- return !(s->regs[rxbase + R_RX_CTRL0] & CTRL_S);
-}
-
-static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
-{
- struct xlx_ethlite *s = qemu_get_nic_opaque(nc);
- unsigned int rxbase = s->rxbuf * (0x800 / 4);
-
- /* DA filter. */
- if (!(buf[0] & 0x80) && memcmp(&s->conf.macaddr.a[0], buf, 6))
- return size;
-
- if (s->regs[rxbase + R_RX_CTRL0] & CTRL_S) {
- D(qemu_log("ethlite lost packet %x\n", s->regs[R_RX_CTRL0]));
- return -1;
- }
-
- D(qemu_log("%s %zd rxbase=%x\n", __func__, size, rxbase));
- memcpy(&s->regs[rxbase + R_RX_BUF0], buf, size);
-
- s->regs[rxbase + R_RX_CTRL0] |= CTRL_S;
- if (s->regs[rxbase + R_RX_CTRL0] & CTRL_I)
- eth_pulse_irq(s);
-
- /* If c_rx_pingpong was set flip buffers. */
- s->rxbuf ^= s->c_rx_pingpong;
- return size;
-}
-
-static void eth_cleanup(NetClientState *nc)
-{
- struct xlx_ethlite *s = qemu_get_nic_opaque(nc);
-
- s->nic = NULL;
-}
-
-static NetClientInfo net_xilinx_ethlite_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
- .size = sizeof(NICState),
- .can_receive = eth_can_rx,
- .receive = eth_rx,
- .cleanup = eth_cleanup,
-};
-
-static int xilinx_ethlite_init(SysBusDevice *dev)
-{
- struct xlx_ethlite *s = FROM_SYSBUS(typeof (*s), dev);
-
- sysbus_init_irq(dev, &s->irq);
- s->rxbuf = 0;
-
- memory_region_init_io(&s->mmio, ð_ops, s, "xlnx.xps-ethernetlite",
- R_MAX * 4);
- sysbus_init_mmio(dev, &s->mmio);
-
- qemu_macaddr_default_if_unset(&s->conf.macaddr);
- s->nic = qemu_new_nic(&net_xilinx_ethlite_info, &s->conf,
- object_get_typename(OBJECT(dev)), dev->qdev.id, s);
- qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
- return 0;
-}
-
-static Property xilinx_ethlite_properties[] = {
- DEFINE_PROP_UINT32("tx-ping-pong", struct xlx_ethlite, c_tx_pingpong, 1),
- DEFINE_PROP_UINT32("rx-ping-pong", struct xlx_ethlite, c_rx_pingpong, 1),
- DEFINE_NIC_PROPERTIES(struct xlx_ethlite, conf),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void xilinx_ethlite_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
- k->init = xilinx_ethlite_init;
- dc->props = xilinx_ethlite_properties;
-}
-
-static const TypeInfo xilinx_ethlite_info = {
- .name = "xlnx.xps-ethernetlite",
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(struct xlx_ethlite),
- .class_init = xilinx_ethlite_class_init,
-};
-
-static void xilinx_ethlite_register_types(void)
-{
- type_register_static(&xilinx_ethlite_info);
-}
-
-type_init(xilinx_ethlite_register_types)