qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [RFC] hw/net: add DEC Tulip NIC emulation


From: Dmitry Smagin
Subject: [Qemu-devel] [RFC] hw/net: add DEC Tulip NIC emulation
Date: Fri, 22 Nov 2013 18:47:49 +0400

From: Antony Pavlov <address@hidden>

This patch adds emulation of DEC/Intel Tulip 21143 with some external chips:
  * Intel LXT971A 10/100 Mbps PHY MII Tranceiver;
  * Microchip 93LC46B 1K Microwire Compatible Serial EEPROM.

Restrictions and TODOs:
  - Tulip always work in promisc-mode with no packet filtering
    (TODO: check qemu packet filtering interfaces)

  - Address errors in packet descriptors are not checked

  - Tulip is bound to PCI, no other bus is possible for now
    (TODO: Rewrite code to permit Tulip to live on buses other than PCI)

  - Internal transceiver is not emulated
    The reason: internal transceiver works with 10 Mbps but we need 100Mbps
    which only external transceivers can give.

  - Link status is not emulated, it's always ON
    (TODO: use standard qemu interface)

  - Incorrect behavior of the Tulip driver is not checked and there's no error
    signaling via register CSR5

  - Only transfer/receive (TI/RI) interrupts are emulated

  - Subsystem IDs in EEPROM image are not good
    (TODO: find proper IDs)

TESTING
-------

NOTE:
  buildroot with tulip-friendly config for qemu is here:
    https://github.com/dmitrysmagin/buildroot.git : master
    (configs/qemu_mips_malta_tulip_defconfig)

  $ git clone https://github.com/dmitrysmagin/qemu.git
  $ cd qemu
  $ ./configure --target-list=mips-softmmu
  ...
  $ make
  ...
  $ ./mips-softmmu/qemu-system-mips \
       -M malta \
       -m 256 \
       -nodefaults \
       -nographic \
       -kernel qemu-malta-testing/vmlinux \
       -initrd qemu-malta-testing/rootfs.cpio \
       -serial stdio \
       -net nic,vlan=0,model=tulip,macaddr=00:11:22:33:44:ee

  Linux version 3.12.0 (address@hidden) (gcc version 4.7.3 (Sourcery CodeBench 
Lite 2013.05-66) ) #1 SMP Thu Nov 14 11:58:04 MSK 2013
  ...
  Welcome to Buildroot
  buildroot login: root
  # lspci -d 0x1011:
  00:12.0 Ethernet controller: Digital Equipment Corporation DECchip 21142/43 
(rev 41)
  # ifconfig eth0
  eth0      Link encap:Ethernet  HWaddr 00:11:22:33:44:EE
            BROADCAST MULTICAST  MTU:1500  Metric:1
            RX packets:0 errors:0 dropped:0 overruns:0 frame:0
            TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
            collisions:0 txqueuelen:1000
            RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

  To test data transfer please use '-net tap,vlan=0,...' or
  '-net vde,vlan=0,...' (depends on your local network settings).

Signed-off-by: Antony Pavlov <address@hidden>
Signed-off-by: Dmitry Smagin <address@hidden>
---
 default-configs/pci.mak     |   1 +
 hw/net/Makefile.objs        |   1 +
 hw/net/tulip.c              | 680 ++++++++++++++++++++++++++++++++++++++++++++
 hw/net/tulip_mdio.c         | 196 +++++++++++++
 hw/net/tulip_mdio.h         |  98 +++++++
 hw/net/tulip_uwire_eeprom.c | 111 ++++++++
 hw/net/tulip_uwire_eeprom.h |  60 ++++
 hw/pci/pci.c                |   2 +
 include/hw/pci/pci_ids.h    |   1 +
 9 files changed, 1150 insertions(+)
 create mode 100644 hw/net/tulip.c
 create mode 100644 hw/net/tulip_mdio.c
 create mode 100644 hw/net/tulip_mdio.h
 create mode 100644 hw/net/tulip_uwire_eeprom.c
 create mode 100644 hw/net/tulip_uwire_eeprom.h

diff --git a/default-configs/pci.mak b/default-configs/pci.mak
index 91b1e92..28a282e 100644
--- a/default-configs/pci.mak
+++ b/default-configs/pci.mak
@@ -18,6 +18,7 @@ CONFIG_MEGASAS_SCSI_PCI=y
 CONFIG_RTL8139_PCI=y
 CONFIG_E1000_PCI=y
 CONFIG_VMXNET3_PCI=y
+CONFIG_TULIP_PCI=y
 CONFIG_IDE_CORE=y
 CONFIG_IDE_QDEV=y
 CONFIG_IDE_PCI=y
diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
index 951cca3..014c24b 100644
--- a/hw/net/Makefile.objs
+++ b/hw/net/Makefile.objs
@@ -10,6 +10,7 @@ common-obj-$(CONFIG_E1000_PCI) += e1000.o
 common-obj-$(CONFIG_RTL8139_PCI) += rtl8139.o
 common-obj-$(CONFIG_VMXNET3_PCI) += vmxnet_tx_pkt.o vmxnet_rx_pkt.o
 common-obj-$(CONFIG_VMXNET3_PCI) += vmxnet3.o
+common-obj-$(CONFIG_TULIP_PCI) += tulip.o tulip_uwire_eeprom.o tulip_mdio.o
 
 common-obj-$(CONFIG_SMC91C111) += smc91c111.o
 common-obj-$(CONFIG_LAN9118) += lan9118.o
diff --git a/hw/net/tulip.c b/hw/net/tulip.c
new file mode 100644
index 0000000..bd52413
--- /dev/null
+++ b/hw/net/tulip.c
@@ -0,0 +1,680 @@
+/*
+ * QEMU DEC 21143 (Tulip) emulation
+ *
+ * Copyright (C) 2011, 2013 Antony Pavlov
+ * Copyright (C) 2013 Dmitry Smagin
+ *
+ * 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/>.
+ */
+
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "net/net.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/dma.h"
+#include "qemu/timer.h"
+
+#include "tulip_uwire_eeprom.h"
+#include "tulip_mdio.h"
+
+#define TULIP_CSR0  0x00
+ #define TULIP_CSR0_SWR     0x01
+ #define TULIP_CSR0_TAP_MASK    (0x03 << 17)
+#define TULIP_CSR1  0x08
+#define TULIP_CSR2  0x10
+#define TULIP_CSR3  0x18
+#define TULIP_CSR4  0x20
+#define TULIP_CSR5  0x28
+ #define TULIP_CSR5_TI         (1 <<  0) /* Transmit Interrupt */
+ #define TULIP_CSR5_RI         (1 <<  6) /* Receive Interrupt */
+ #define TULIP_CSR5_NIS        (1 << 16) /* Or'ed bits 0, 2, 6, 11, 14  */
+ #define TULIP_CSR5_NIS_SHIFT  (16)
+ #define TULIP_CSR5_RS_MASK    (7 << 17)
+ #define TULIP_CSR5_TS_MASK    (7 << 20)
+
+#define TULIP_CSR6  0x30 /* Operation Mode Register */
+ #define TULIP_CSR6_TXON    0x2000
+ #define TULIP_CSR6_RXON    0x0002
+
+#define TULIP_CSR7  0x38 /* Interrupt Enable Register */
+#define TULIP_CSR8  0x40
+#define TULIP_CSR9  0x48 /* Boot ROM, Serial ROM, and MII Management Register 
*/
+ #define TULIP_CSR9_MDI         (1 << 19) /* MDIO Data In */
+ #define TULIP_CSR9_MDOM        (1 << 18) /* MDIO Operation Mode */
+ #define TULIP_CSR9_MDO         (1 << 17) /* MDIO Data Out */
+ #define TULIP_CSR9_MDC         (1 << 16) /* MDIO Clock */
+ #define TULIP_CSR9_RD          (1 << 14)
+ #define TULIP_CSR9_WR          (1 << 13)
+ #define TULIP_CSR9_SR          (1 << 11) /* Serial ROM Select */
+ #define TULIP_CSR9_SRDO        (1 << 3) /* Serial ROM Data Out */
+ #define TULIP_CSR9_SRDI        (1 << 2) /* Serial ROM Data In */
+ #define TULIP_CSR9_SRCK        (1 << 1) /* Serial ROM Clock */
+ #define TULIP_CSR9_SRCS        (1) /* Serial ROM Chip Select */
+#define TULIP_CSR10 0x50
+#define TULIP_CSR11 0x58
+
+#define TULIP_CSR12 0x60
+
+#define TULIP_CSR13 0x68
+
+#define TULIP_CSR14 0x70 /* SIA Transmit and Receive Register */
+#define TULIP_CSR15 0x78
+
+#define defreg(x)   x = (TULIP_##x>>2)
+enum {
+    defreg(CSR0),
+    defreg(CSR1),
+    defreg(CSR2),
+    defreg(CSR3),
+    defreg(CSR4),
+    defreg(CSR5),
+    defreg(CSR6),
+    defreg(CSR7),
+    defreg(CSR8),
+    defreg(CSR9),
+    defreg(CSR10),
+    defreg(CSR11),
+    defreg(CSR12),
+    defreg(CSR13),
+    defreg(CSR14),
+    defreg(CSR15),
+};
+
+/* The Tulip Receive Descriptor */
+struct tulip_rx_desc {
+    uint32_t status;
+    uint32_t length;
+    uint32_t buffer1;
+    uint32_t buffer2;
+};
+
+#define TULIP_RDES0_OWN   (1 << 31)
+#define TULIP_RDES0_FS    (1 <<  9) /* First descriptor */
+#define TULIP_RDES0_LS    (1 <<  8) /* Last descriptor */
+
+/* The Tulip Transmit Descriptor */
+struct tulip_tx_desc {
+    uint32_t status;
+    uint32_t length;
+    uint32_t buffer1;
+    uint32_t buffer2;  /* Linux use only buffer 1. */
+};
+
+#define TULIP_TDES0_OWN   (1 << 31)
+
+#define TULIP_TDES1_IC    (1 << 31) /* Interrupt on Completion */
+#define TULIP_TDES1_LS    (1 << 30) /* Last Segment */
+#define TULIP_TDES1_FS    (1 << 29) /* First Segment */
+#define TULIP_TDES1_SET   (1 << 27) /* Setup Packet */
+
+#define TULIP_CSR_REGION_SIZE       0x80
+
+typedef struct TulipState_st {
+    PCIDevice dev;
+    NICState *nic;
+    NICConf conf;
+    MemoryRegion mmio;
+    MemoryRegion io;
+    QEMUTimer *timer;
+    qemu_irq irq;
+
+    struct MicrowireEeprom eeprom;
+    struct MiiTransceiver mii;
+
+    uint32_t mac_reg[TULIP_CSR_REGION_SIZE >> 2];
+
+    uint32_t cur_tx_desc;
+    uint32_t cur_rx_desc;
+    int tx_polling;
+} TulipState;
+
+#define EEPROM_TEMPLATE_SIZE 64
+#define EEPROM_MACADDR_OFFSET 10
+/*
+ * DEC has developed its own EEPROM format
+ * see Digital Semiconductor 21X4 Serial ROM Format ver. 4.05 2-Mar-1998
+ * for details.
+ *
+ * Also see tulip-diag utility code from nictools-pci package
+ *     http://ftp.debian.org/debian/pool/main/n/nictools-pci/
+ */
+static const uint16_t tulip_eeprom_template[EEPROM_TEMPLATE_SIZE] = {
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* ^^^^^^^^^^^^^^ Subsystem IDs */
+    0x0000, 0x0104, 0x5554, 0x494c, 0x0050, 0x1e00, 0x0000, 0x0800,
+                 /* ^^^^^^^^^^^^^^^^^^^^^^ MAC address here */
+    0x8d01, 0x0003, 0x0000, 0x7800, 0x01e0, 0x5000, 0x1800, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+     /* we have no Magic Packet block, so checksum is here  ^^^^^^ */
+};
+
+static void tulip_reset(void *opaque);
+static void tulip_update_irq(TulipState *s);
+
+static inline uint32_t mac_readreg(TulipState *s, int index)
+{
+    return s->mac_reg[index];
+}
+
+static inline void mac_writereg(TulipState *s, int index, uint32_t val)
+{
+    s->mac_reg[index] = val;
+}
+
+/* FIXME: we use external MII tranceiver, so we must change it status here */
+static void tulip_set_link_status(NetClientState *nc)
+{
+}
+
+static int tulip_can_receive(NetClientState *nc)
+{
+    TulipState *s = qemu_get_nic_opaque(nc);
+
+    return mac_readreg(s, CSR6) & TULIP_CSR6_RXON;
+}
+
+static ssize_t tulip_receive(NetClientState *nc, const uint8_t *buf,
+                             size_t size)
+{
+    TulipState *s = qemu_get_nic_opaque(nc);
+    struct tulip_rx_desc desc;
+    uint32_t status, buffer1, buffer2;
+    uint32_t cur_rx_desc;
+
+    if (!(mac_readreg(s, CSR6) & TULIP_CSR6_RXON)) {
+        return -1;
+    }
+
+    cur_rx_desc = s->cur_rx_desc;
+    while (1) {
+        pci_dma_read(&s->dev, cur_rx_desc, (void *)&desc, sizeof(desc));
+        status = le32_to_cpu(desc.status);
+        buffer1 = le32_to_cpu(desc.buffer1);
+        buffer2 = le32_to_cpu(desc.buffer2);
+
+        /* FIXME: check desc.length too */
+        if (!(status & TULIP_RDES0_OWN)) {
+            return -1;
+        }
+
+        desc.status = cpu_to_le32(
+            (((size + 4) << 16) & 0x3fff0000)
+            | TULIP_RDES0_FS | TULIP_RDES0_LS);
+
+        pci_dma_write(&s->dev, buffer1, buf, size);
+        pci_dma_write(&s->dev, cur_rx_desc, (void *)&desc, sizeof(desc));
+        mac_writereg(s, CSR5, TULIP_CSR5_RI | mac_readreg(s, CSR5));
+
+        s->cur_rx_desc = buffer2;
+        break;
+    }
+
+    tulip_update_irq(s);
+
+    return size;
+}
+
+static inline void tulip_csr0_write(TulipState *s, uint32_t val)
+{
+    if (val & TULIP_CSR0_SWR) {
+        tulip_reset(s);
+        return;
+    }
+
+    if (val & TULIP_CSR0_TAP_MASK) {
+        s->tx_polling = 1;
+    } else {
+        s->tx_polling = 0;
+    }
+
+    mac_writereg(s, CSR0, val);
+}
+
+static inline void tulip_csr5_write(TulipState *s, uint32_t val)
+{
+    uint32_t csr5_write_mask = 0x02fe0000;
+
+    val = val & (~csr5_write_mask);
+
+    mac_writereg(s, CSR5, mac_readreg(s, CSR5) & (~val));
+}
+
+static inline void tulip_csr6_write(TulipState *s, uint32_t val)
+{
+    mac_writereg(s, CSR6, val);
+}
+
+static inline void tulip_csr9_write(TulipState *s, uint32_t val)
+{
+    if (val & TULIP_CSR9_SR) { /* Serial ROM */
+        int srdo;
+
+        srdo = microwire_tick(&s->eeprom,
+                (val & TULIP_CSR9_SRCK) >> 1,
+                (val & TULIP_CSR9_SRCS),
+                (val & TULIP_CSR9_SRDI) >> 2);
+        if (srdo) {
+            mac_writereg(s, CSR9, val | TULIP_CSR9_SRDO);
+        } else {
+            mac_writereg(s, CSR9, val & ~TULIP_CSR9_SRDO);
+        }
+    } else {
+        int mdo;
+
+        mdo = mii_tick(&s->mii, (val & TULIP_CSR9_MDC) >> 16,
+                        (val & TULIP_CSR9_MDO) >> 17);
+
+        if (val & TULIP_CSR9_MDOM) {
+            if (mdo) {
+                val |= TULIP_CSR9_MDI;
+            } else {
+                val &= ~TULIP_CSR9_MDI;
+            }
+        }
+        mac_writereg(s, CSR9, val);
+    }
+}
+
+/* Just now we don't use MAC-address filtering (we always use promisc mode),
+   so just drop any setup frame */
+static void process_setup_frame(TulipState *s, uint8_t *a)
+{
+}
+
+static void process_tx(TulipState *s)
+{
+    struct tulip_tx_desc desc;
+    uint32_t status, length, buffer1, buffer2;
+    uint32_t cur_tx_desc;
+
+    uint8_t a[1600];
+    int cur_offset;
+    int frame_length;
+
+    cur_offset = 0;
+    frame_length = 0;
+
+    cur_tx_desc = s->cur_tx_desc;
+
+    while (1) {
+        uint32_t to_copy;
+
+        pci_dma_read(&s->dev, cur_tx_desc, (void *)&desc, sizeof(desc));
+        status = le32_to_cpu(desc.status);
+        length = le32_to_cpu(desc.length);
+        buffer1 = le32_to_cpu(desc.buffer1);
+        buffer2 = le32_to_cpu(desc.buffer2);
+
+        if (!(status & TULIP_TDES0_OWN)) {
+            break;
+        }
+
+        to_copy = 0x7ff & length;
+
+        /* First Segment */
+        if (length & TULIP_TDES1_FS) {
+            cur_offset = 0;
+            frame_length = to_copy;
+        }
+
+        if (length & TULIP_TDES1_SET) { /* Setup Frame */
+            /* FIXME: check (to_copy == 192) */
+            pci_dma_read(&s->dev, buffer1, (void *)a, to_copy);
+
+            process_setup_frame(s, a);
+        } else {
+
+            pci_dma_read(&s->dev, buffer1, (void *)(a + cur_offset), to_copy);
+
+            cur_offset += to_copy;
+
+            /* ! First Segment */
+            if (!(length & TULIP_TDES1_FS)) {
+                frame_length += to_copy;
+            }
+
+            /* Last Segment */
+            if (length & TULIP_TDES1_LS) {
+                qemu_send_packet(qemu_get_queue(s->nic), a, frame_length);
+            }
+        }
+
+        desc.status = cpu_to_le32(status & ~TULIP_TDES0_OWN);
+        pci_dma_write(&s->dev, cur_tx_desc, (void *)&desc, sizeof(desc));
+
+        /* Last Segment */
+        if (length & TULIP_TDES1_LS) {
+            /* FIXME: check IC */
+            /* FIXME: mac_writereg -> tulip_csr5_write */
+            mac_writereg(s, CSR5, TULIP_CSR5_TI | mac_readreg(s, CSR5));
+        }
+
+        cur_tx_desc = buffer2;
+        s->cur_tx_desc = cur_tx_desc;
+    }
+}
+
+static void tulip_csr_write(void *opaque, hwaddr addr, uint64_t val,
+                 unsigned size)
+{
+    TulipState *s = opaque;
+    unsigned int index = addr & (TULIP_CSR_REGION_SIZE - 1);
+
+    switch (index) {
+    case TULIP_CSR0: /* Bus Mode Register */
+        tulip_csr0_write(s, (uint32_t)val);
+        break;
+
+    case TULIP_CSR1: /* Transmit Poll Demand/Current Descriptor Address */
+        if (mac_readreg(s, CSR6) & TULIP_CSR6_TXON) {
+            process_tx(s);
+            tulip_update_irq(s);
+        }
+        break;
+
+    case TULIP_CSR2: /* Receive Poll Demand/Current Descriptor Address */
+        break;
+
+    case TULIP_CSR3: /* Start of Receive List */
+        mac_writereg(s, CSR3, val);
+        s->cur_rx_desc = val;
+        break;
+
+    case TULIP_CSR4: /* Start of Transmit List */
+        mac_writereg(s, CSR4, val);
+        s->cur_tx_desc = val;
+        break;
+
+    case TULIP_CSR5: /* Status Register */
+        tulip_csr5_write(s, (uint32_t)val);
+        tulip_update_irq(s);
+        break;
+
+    case TULIP_CSR6: /* Command/Mode Register */
+        tulip_csr6_write(s, (uint32_t)val);
+        break;
+
+    case TULIP_CSR9: /* Boot/Serial ROM and MII Management Register */
+        tulip_csr9_write(s, (uint32_t)val);
+        break;
+
+    case TULIP_CSR13:
+        mac_writereg(s, CSR13, val);
+        break;
+
+    case TULIP_CSR14:
+    case TULIP_CSR15:
+        break;
+
+    default:
+        qemu_log_mask(LOG_UNIMP,
+            "tulip: write access to unknown register 0x" TARGET_FMT_plx, addr);
+    }
+}
+
+static uint64_t tulip_csr_read(void *opaque, hwaddr addr, unsigned size)
+{
+    TulipState *s = opaque;
+    unsigned int index = addr & (TULIP_CSR_REGION_SIZE - 1);
+    uint64_t ret;
+
+    ret = 0;
+
+    switch (index) {
+    case TULIP_CSR0: /* Bus Mode Register */
+    case TULIP_CSR5: /* Status Register */
+    case TULIP_CSR6: /* Command/Mode Register */
+    case TULIP_CSR8:
+    case TULIP_CSR9: /* Boot/Serial ROM and MII Management Register */
+    case TULIP_CSR12:
+    case TULIP_CSR13:
+    /* FIXME: tulip_csr_read: unimplemented register index = 70 */
+    case TULIP_CSR14:
+    case TULIP_CSR15:
+        ret = (uint64_t)mac_readreg(s, (index >> 2));
+        break;
+
+    default:
+        qemu_log_mask(LOG_UNIMP,
+            "tulip: read access to unknown register 0x" TARGET_FMT_plx, addr);
+
+    }
+
+    return ret;
+}
+
+static const MemoryRegionOps tulip_mmio_ops = {
+    .read = tulip_csr_read,
+    .write = tulip_csr_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static const VMStateDescription vmstate_tulip = {
+    .name = "tulip",
+    .version_id = 2,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_PCI_DEVICE(dev, TulipState),
+        VMSTATE_MACADDR(conf.macaddr, TulipState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const uint32_t mac_reg_init[] = {
+    [CSR0] = 0xfe000000,
+    [CSR1] = 0xffffffff,
+    [CSR2] = 0x00000000,
+    [CSR3] = 0x00000000,
+    [CSR4] = 0x00000000,
+    [CSR5] = 0xf0000000,
+    [CSR6] = 0x00000000,
+    [CSR7] = 0x00000000,
+    [CSR8] = 0xe0000000,
+    [CSR9] = 0xfff483ff,
+    [CSR10] = 0x00000000,
+    [CSR11] = 0x00000000,
+    [CSR12] = 0x00000000,
+    [CSR13] = 0x00000000,
+    [CSR14] = 0x00000000,
+    [CSR15] = 0x00000000,
+};
+
+static void tulip_cleanup(NetClientState *nc)
+{
+    TulipState *s = qemu_get_nic_opaque(nc);
+
+    s->nic = NULL;
+}
+
+static void pci_tulip_uninit(PCIDevice *dev)
+{
+    TulipState *d = DO_UPCAST(TulipState, dev, dev);
+
+    qemu_free_irq(d->irq);
+    memory_region_destroy(&d->mmio);
+    memory_region_destroy(&d->io);
+    timer_del(d->timer);
+    timer_free(d->timer);
+    qemu_del_nic(d->nic);
+}
+
+static void tulip_reset(void *opaque)
+{
+    TulipState *d = opaque;
+
+    memset(d->mac_reg, 0, sizeof d->mac_reg);
+    memmove(d->mac_reg, mac_reg_init, sizeof mac_reg_init);
+
+    d->tx_polling = 0;
+}
+
+static NetClientInfo net_tulip_info = {
+    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .size = sizeof(NICState),
+    .can_receive = tulip_can_receive,
+    .receive = tulip_receive,
+    .cleanup = tulip_cleanup,
+    .link_status_changed = tulip_set_link_status,
+};
+
+static void tulip_update_irq(TulipState *s)
+{
+    int isr = 0;
+
+    /* calculate NIS (sum of normal interrupts) */
+    s->mac_reg[CSR5] &= ~TULIP_CSR5_NIS;
+
+    if (s->mac_reg[CSR5] & (TULIP_CSR5_TI | TULIP_CSR5_RI)) {
+        s->mac_reg[CSR5] |= TULIP_CSR5_NIS;
+        isr = 1;
+    }
+
+    /* FIXME: when calculating NIS take into account masked interupts */
+
+    /* FIXME: can we report about Abnormal Interrupt Summary too? */
+
+    qemu_set_irq(s->irq, isr);
+}
+
+static void tulip_timer(void *opaque)
+{
+    TulipState *s = opaque;
+
+    if (s->tx_polling) {
+        process_tx(s);
+    }
+
+    tulip_update_irq(s);
+
+    timer_mod(s->timer,
+        qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + get_ticks_per_sec() / 10);
+}
+
+static int pci_tulip_init(PCIDevice *pci_dev)
+{
+    TulipState *d = DO_UPCAST(TulipState, dev, pci_dev);
+    uint8_t *pci_conf;
+    uint8_t *macaddr;
+    uint16_t eeprom_template_copy[EEPROM_TEMPLATE_SIZE];
+    int i;
+
+    pci_conf = d->dev.config;
+
+    /* TODO: RST# value should be 0, PCI spec 6.2.4 */
+    pci_conf[PCI_CACHE_LINE_SIZE] = 0x10;
+
+    pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin A */
+
+    /* PCI interface */
+    memory_region_init_io(&d->mmio, OBJECT(d), &tulip_mmio_ops, d,
+                          "tulip-mmio", TULIP_CSR_REGION_SIZE);
+    memory_region_init_io(&d->io, OBJECT(d), &tulip_mmio_ops, d,
+                          "tulip-io", TULIP_CSR_REGION_SIZE);
+
+    pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &d->io);
+    pci_register_bar(&d->dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio);
+
+    d->irq = pci_allocate_irq(pci_dev);
+
+    lxt971_init(&d->mii, 0);
+
+    qemu_macaddr_default_if_unset(&d->conf.macaddr);
+    macaddr = d->conf.macaddr.a;
+
+    memmove(eeprom_template_copy, tulip_eeprom_template,
+            sizeof(eeprom_template_copy));
+
+    /* copy macaddr to eeprom as linux driver want find it there */
+    for (i = 0; i < 3; i++) {
+        eeprom_template_copy[EEPROM_MACADDR_OFFSET + i] =
+                (macaddr[2 * i + 1] << 8) | macaddr[2 * i];
+    }
+
+    /*
+     * FIXME: we have to update eeprom checksum too.
+     *
+     * see tulip-diag utility code from nictools-pci package
+     *   http://ftp.debian.org/debian/pool/main/n/nictools-pci/
+     */
+
+    microwire_reset(&d->eeprom);
+    microwire_load_data(&d->eeprom, eeprom_template_copy);
+
+    d->nic = qemu_new_nic(&net_tulip_info, &d->conf,
+                          object_get_typename(OBJECT(pci_dev)),
+                          d->dev.qdev.id, d);
+
+    qemu_format_nic_info_str(qemu_get_queue(d->nic), macaddr);
+
+    add_boot_device_path(d->conf.bootindex, &pci_dev->qdev, "/address@hidden");
+
+    d->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tulip_timer, d);
+    timer_mod(d->timer,
+              qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + get_ticks_per_sec());
+
+    return 0;
+}
+
+static void qdev_tulip_reset(DeviceState *dev)
+{
+    TulipState *d = DO_UPCAST(TulipState, dev.qdev, dev);
+
+    tulip_reset(d);
+}
+
+static Property tulip_properties[] = {
+    DEFINE_NIC_PROPERTIES(TulipState, conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void tulip_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = pci_tulip_init;
+    k->exit = pci_tulip_uninit;
+    k->vendor_id = PCI_VENDOR_ID_DEC;
+    k->device_id = PCI_DEVICE_ID_DEC_21142;
+    k->revision = 0x41; /* 21143 chip */
+    k->class_id = PCI_CLASS_NETWORK_ETHERNET;
+    dc->desc = "DEC 21143 Tulip";
+    dc->reset = qdev_tulip_reset;
+    dc->vmsd = &vmstate_tulip;
+    dc->props = tulip_properties;
+    set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
+}
+
+static const TypeInfo tulip_info = {
+    .name = "tulip",
+    .parent = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(TulipState),
+    .class_init = tulip_class_init,
+};
+
+static void tulip_register_types(void)
+{
+    type_register_static(&tulip_info);
+}
+
+type_init(tulip_register_types)
diff --git a/hw/net/tulip_mdio.c b/hw/net/tulip_mdio.c
new file mode 100644
index 0000000..0c7e9b1
--- /dev/null
+++ b/hw/net/tulip_mdio.c
@@ -0,0 +1,196 @@
+/*
+ * QEMU Intel LXT971A 10/100 Mbps PHY MII Transceiver emulation
+ *
+ * Copyright (C) 2011, 2013 Antony Pavlov
+ *
+ * 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/>.
+ */
+
+#include "net/net.h"
+
+#include "tulip_mdio.h"
+
+void mii_reset(struct MiiTransceiver *s)
+{
+    s->state = MII_STATE_IDLE;
+    s->bit_num = 0;
+    s->cmd = 0;
+    s->addr = 0;
+    s->data = 0;
+    s->last_clk = 0;
+    s->last_mdio = 0;
+    s->last_mdi = 0;
+    s->selected_phy = 0;
+}
+
+int mii_tick(struct MiiTransceiver *s, int clk, int io)
+{
+    int ret;
+
+    ret = s->last_mdio;
+
+    if (s->last_clk == 0 && clk == 1) {
+        switch (s->state) {
+        case MII_STATE_IDLE:
+            s->state = MII_STATE_READY;
+            break;
+
+        case MII_STATE_READY:
+            if (io == 0 && s->last_mdi == 1) {
+                s->state = MII_STATE_WAIT_START;
+            }
+            break;
+
+        case MII_STATE_WAIT_START:
+            if (io == 1) {
+                s->state = MII_STATE_WAIT_CMD;
+                s->bit_num = 0;
+                s->cmd = 0;
+            } else {
+                s->state = MII_STATE_IDLE;
+            }
+            break;
+
+        case MII_STATE_WAIT_CMD:
+            s->cmd = (s->cmd << 1) | (io & 1);
+            s->bit_num++;
+            if (s->bit_num == 2) {
+                s->state = MII_STATE_WAIT_PHYA;
+                s->addr = 0;
+                s->bit_num = 0;
+            }
+            break;
+
+        case MII_STATE_WAIT_PHYA:
+            s->addr = (s->addr << 1) | (io & 1);
+            s->bit_num++;
+
+            if (s->bit_num == 5) {
+                s->selected_phy = s->addr;
+                s->state = MII_STATE_WAIT_REGA;
+                s->bit_num = 0;
+                s->addr = 0;
+            }
+
+            break;
+
+        case MII_STATE_WAIT_REGA:
+            s->addr = (s->addr << 1) | (io & 1);
+            s->bit_num++;
+
+            if (s->bit_num == 5) {
+                s->bit_num = 0;
+                if (s->cmd == MII_CMD_READ) {
+                    s->state = MII_STATE_READ_SYNC_CYCL;
+                    if (s->selected_phy == s->taddr) {
+                        s->data = s->readreg(s, s->addr);
+                    } else {
+                        s->data = 0xffff;
+                    }
+                }
+                if (s->cmd == MII_CMD_WRITE) {
+                    s->state = MII_STATE_WRITE_SYNC_CYCL;
+                    s->data = 0;
+                }
+            }
+            break;
+
+        case MII_STATE_WRITE_SYNC_CYCL:
+            s->bit_num++;
+
+            if (s->bit_num == 2) {
+                s->bit_num = 0;
+                s->state = MII_STATE_GET_DATA;
+                if (s->selected_phy == s->taddr) {
+                    ret = 0;
+                } else {
+                    ret = 1;
+                }
+            }
+            break;
+
+        case MII_STATE_READ_SYNC_CYCL:
+            s->bit_num++;
+
+            if (s->bit_num == 1) {
+                s->bit_num = 0;
+                s->state = MII_STATE_PUT_DATA;
+                ret = 0;
+            }
+            break;
+
+        case MII_STATE_PUT_DATA:
+            ret = ((s->data >> (15 - s->bit_num)) & 1);
+            s->bit_num++;
+            if (s->bit_num == 16) {
+                s->state = MII_STATE_IDLE;
+            }
+            break;
+
+        case MII_STATE_GET_DATA:
+            s->data = (s->data << 1) | (io & 1);
+            s->bit_num++;
+            if (s->bit_num == 16) {
+                s->state = MII_STATE_IDLE;
+                /* FIXME: check phy_addr */
+                s->writereg(s, s->addr, s->data);
+            }
+            break;
+        }
+        s->last_mdio = ret;
+        s->last_mdi = io;
+    }
+
+    s->last_clk = clk;
+
+    return ret;
+}
+
+static uint16_t lxt971_readreg(struct MiiTransceiver *s, int index)
+{
+    return s->mii_data[index];
+}
+
+static void lxt971_writereg(struct MiiTransceiver *s, int index, uint16_t val)
+{
+    s->mii_data[index] = val;
+}
+
+static void lxt971_reset(struct MiiTransceiver *s)
+{
+    uint16_t a[] = {
+    0x1000, 0x782d, 0x0013, 0x78e2, 0x01e1, 0x45e1, 0x0007, 0x2001,
+    0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+    0x0084, 0x4780, 0x0000, 0x00f4, 0x0422, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x00c8, 0x0000, 0xffff, 0x0000, 0x0000, 0x3678,
+    };
+    int i;
+
+    mii_reset(s);
+
+    for (i = 0; i < 32; i++) {
+        s->mii_data[i] = a[i];
+    }
+}
+
+void lxt971_init(struct MiiTransceiver *s, int phy_addr)
+{
+    s->reset = lxt971_reset;
+    s->readreg = lxt971_readreg;
+    s->writereg = lxt971_writereg;
+
+    s->taddr = phy_addr;
+
+    lxt971_reset(s);
+}
diff --git a/hw/net/tulip_mdio.h b/hw/net/tulip_mdio.h
new file mode 100644
index 0000000..c87e6bb
--- /dev/null
+++ b/hw/net/tulip_mdio.h
@@ -0,0 +1,98 @@
+/*
+ * vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+ *
+ * QEMU LXT971A MII Transceiver emulation
+ * Copyright (C) 2011, 2013 Antony Pavlov
+ *
+ * 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/>.
+ */
+
+#ifndef HW_NET_TULIP_MDIO_H
+#define HW_NET_TULIP_MDIO_H
+
+struct MiiTransceiver {
+    int state;
+#define MII_STATE_IDLE        0
+#define MII_STATE_READY       1
+#define MII_STATE_WAIT_START  2
+#define MII_STATE_WAIT_CMD    3
+#define MII_STATE_WAIT_PHYA   4
+#define MII_STATE_WAIT_REGA   5
+#define MII_STATE_READ_SYNC_CYCL   6
+#define MII_STATE_WRITE_SYNC_CYCL  7
+#define MII_STATE_PUT_DATA    8
+#define MII_STATE_GET_DATA    9
+#define MII_STATE_ERROR       10
+
+    int taddr;
+
+    int last_mdio;
+    int last_mdi;
+    int last_clk;
+    int bit_num;
+    int selected_phy;
+
+    int cmd;
+#define MII_CMD_READ          2
+#define MII_CMD_WRITE         1
+
+    int addr;
+    int data;
+
+    uint16_t mii_data[32];
+
+    void (*reset)(struct MiiTransceiver *s);
+    uint16_t (*readreg)(struct MiiTransceiver *, int);
+    void (*writereg)(struct MiiTransceiver *, int, uint16_t);
+};
+
+/* PHY Registers defined by IEEE */
+#define PHY_CTRL         0x00 /* Control Register */
+#define PHY_STATUS       0x01 /* Status Regiser */
+#define PHY_ID1          0x02 /* Phy Id Reg (word 1) */
+#define PHY_ID2          0x03 /* Phy Id Reg (word 2) */
+#define PHY_AUTONEG_ADV  0x04 /* Autoneg Advertisement */
+#define PHY_LP_ABILITY   0x05 /* Link Partner Ability (Base Page) */
+#define PHY_AUTONEG_EXP  0x06 /* Autoneg Expansion Reg */
+#define PHY_NEXT_PAGE_TX 0x07 /* Next Page TX */
+#define PHY_LP_NEXT_PAGE 0x08 /* Link Partner Next Page */
+#define PHY_1000T_CTRL   0x09 /* 1000Base-T Control Reg */
+#define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */
+#define PHY_EXT_STATUS   0x0F /* Extended Status Reg */
+
+#define MAX_PHY_REG_ADDRESS        0x1F  /* 5 bit address bus (0-0x1F) */
+#define MAX_PHY_MULTI_PAGE_REG     0xF   /* Registers equal on all pages */
+
+/* PHY Status Register */
+#define MII_SR_EXTENDED_CAPS     0x0001 /* Extended register capabilities */
+#define MII_SR_JABBER_DETECT     0x0002 /* Jabber Detected */
+#define MII_SR_LINK_STATUS       0x0004 /* Link Status 1 = link */
+#define MII_SR_AUTONEG_CAPS      0x0008 /* Auto Neg Capable */
+#define MII_SR_REMOTE_FAULT      0x0010 /* Remote Fault Detect */
+#define MII_SR_AUTONEG_COMPLETE  0x0020 /* Auto Neg Complete */
+#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */
+#define MII_SR_EXTENDED_STATUS   0x0100 /* Ext. status info in Reg 0x0F */
+#define MII_SR_100T2_HD_CAPS     0x0200 /* 100T2 Half Duplex Capable */
+#define MII_SR_100T2_FD_CAPS     0x0400 /* 100T2 Full Duplex Capable */
+#define MII_SR_10T_HD_CAPS       0x0800 /* 10T   Half Duplex Capable */
+#define MII_SR_10T_FD_CAPS       0x1000 /* 10T   Full Duplex Capable */
+#define MII_SR_100X_HD_CAPS      0x2000 /* 100X  Half Duplex Capable */
+#define MII_SR_100X_FD_CAPS      0x4000 /* 100X  Full Duplex Capable */
+#define MII_SR_100T4_CAPS        0x8000 /* 100T4 Capable */
+
+extern void mii_reset(struct MiiTransceiver *s);
+extern int mii_tick(struct MiiTransceiver *s, int clk, int io);
+extern void lxt971_init(struct MiiTransceiver *s, int phy_addr);
+
+#endif /* HW_NET_TULIP_MDIO_H */
diff --git a/hw/net/tulip_uwire_eeprom.c b/hw/net/tulip_uwire_eeprom.c
new file mode 100644
index 0000000..370c5e4
--- /dev/null
+++ b/hw/net/tulip_uwire_eeprom.c
@@ -0,0 +1,111 @@
+/*
+ * QEMU Microchip 93LC46B 1K Microwire Compatible Serial EEPROM emulation
+ *
+ * Copyright (C) 2011, 2013 Antony Pavlov
+ *
+ * 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/>.
+ */
+
+#include "net/net.h"
+
+#include "tulip_uwire_eeprom.h"
+
+void microwire_reset(struct MicrowireEeprom *s)
+{
+    s->state = MW_EEPROM_STATE_IDLE;
+    s->bit_num = 0;
+    s->cmd = 0;
+    s->addr = 0;
+    s->data = 0;
+    s->last_clk = 0;
+}
+
+void microwire_load_data(struct MicrowireEeprom *s, const uint16_t *data)
+{
+    memmove(s->eeprom_data, data, sizeof(s->eeprom_data));
+}
+
+int microwire_tick(struct MicrowireEeprom *s, int clk, int en, int din)
+{
+    int ret;
+
+    ret = 0;
+
+    /* Microwire eeprom disabled */
+    if (en == 0) {
+        s->state = MW_EEPROM_STATE_IDLE;
+        return 0;
+    }
+
+    if (s->last_clk == 0 && clk == 1) {
+        switch (s->state) {
+        case MW_EEPROM_STATE_IDLE:
+            /*
+             * The Start bit is detected by the device if CS and DI are
+             * both high with respect to the positive edge of CLK for
+             * the first time.
+             */
+            if (!din) {
+                break;
+            }
+
+            s->state = MW_EEPROM_STATE_WAIT_CMD;
+            s->cmd = din & 1;
+            s->bit_num = 0;
+            break;
+
+        case MW_EEPROM_STATE_WAIT_CMD:
+            s->cmd = (s->cmd << 1) | (din & 1);
+            s->bit_num++;
+            if (s->bit_num == 2) {
+                if (s->cmd == 0) {
+                    s->state = MW_EEPROM_STATE_IDLE;
+                }
+                if (s->cmd == MW_EEPROM_CMD_READ) {
+                    s->state = MW_EEPROM_STATE_WAIT_ADDR;
+                    s->addr = 0;
+                    s->bit_num = 0;
+                }
+            }
+            break;
+
+        case MW_EEPROM_STATE_WAIT_ADDR:
+            s->addr = (s->addr << 1) | (din & 1);
+            s->bit_num++;
+            if (s->bit_num == 6) { /* ROM width = 6 */
+                if (s->cmd == MW_EEPROM_CMD_READ) {
+                    s->state = MW_EEPROM_STATE_OUT_DATA;
+                    s->bit_num = 0;
+                }
+            }
+            break;
+
+        case MW_EEPROM_STATE_OUT_DATA:
+            s->bit_num++;
+            if (s->bit_num == 16) {
+                if (s->cmd == MW_EEPROM_CMD_READ) {
+                    s->state = MW_EEPROM_STATE_IDLE;
+                }
+            }
+
+            ret = ((s->eeprom_data[s->addr] >> (16 - s->bit_num)) & 1);
+
+            break;
+        }
+    }
+
+    s->last_clk = clk;
+
+    return ret;
+}
diff --git a/hw/net/tulip_uwire_eeprom.h b/hw/net/tulip_uwire_eeprom.h
new file mode 100644
index 0000000..4d310cb
--- /dev/null
+++ b/hw/net/tulip_uwire_eeprom.h
@@ -0,0 +1,60 @@
+/*
+ * vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+ *
+ * QEMU 93LC46B uWire-connected EEPROM emulation
+ * Copyright (C) 2011, 2013 Antony Pavlov
+ *
+ * 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/>.
+ */
+
+#ifndef HW_NET_TULIP_UWIRE_EEPROM_H
+#define HW_NET_TULIP_UWIRE_EEPROM_H
+
+struct MicrowireEeprom {
+    /* 93LC46B */
+    int state;
+#define MW_EEPROM_STATE_IDLE        0
+#define MW_EEPROM_STATE_WAIT_CMD    1
+#define MW_EEPROM_STATE_WAIT_ADDR   2
+#define MW_EEPROM_STATE_WAIT_DATA   3
+#define MW_EEPROM_STATE_OUT_DATA    4
+#define MW_EEPROM_STATE_ERROR       5
+
+    int last_clk;
+    int bit_num;
+    int cmd;
+#define MW_EEPROM_CMD_ERASE         7
+#define MW_EEPROM_CMD_READ          6
+#define MW_EEPROM_CMD_WRITE         5
+#define MW_EEPROM_CMD_ERASE_ALL     4
+
+    int addr;
+    int data;
+
+    uint16_t eeprom_data[64];
+};
+
+/* EEPROM Commands - Microwire */
+#define EEPROM_READ_OPCODE_MICROWIRE  0x6  /* EEPROM read opcode */
+#define EEPROM_WRITE_OPCODE_MICROWIRE 0x5  /* EEPROM write opcode */
+#define EEPROM_ERASE_OPCODE_MICROWIRE 0x7  /* EEPROM erase opcode */
+#define EEPROM_EWEN_OPCODE_MICROWIRE  0x13 /* EEPROM erase/write enable */
+#define EEPROM_EWDS_OPCODE_MICROWIRE  0x10 /* EEPROM erast/write disable */
+
+extern int microwire_tick(struct MicrowireEeprom *s, int clk, int en, int din);
+extern void microwire_reset(struct MicrowireEeprom *s);
+extern void microwire_load_data(struct MicrowireEeprom *s,
+    const uint16_t *data);
+
+#endif /* HW_NET_TULIP_UWIRE_EEPROM_H */
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index a98c8a0..82d0239 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -1600,6 +1600,7 @@ static const char * const pci_nic_models[] = {
     "e1000",
     "pcnet",
     "virtio",
+    "tulip",
     NULL
 };
 
@@ -1612,6 +1613,7 @@ static const char * const pci_nic_names[] = {
     "e1000",
     "pcnet",
     "virtio-net-pci",
+    "tulip",
     NULL
 };
 
diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
index 4c0002b..d14618a 100644
--- a/include/hw/pci/pci_ids.h
+++ b/include/hw/pci/pci_ids.h
@@ -58,6 +58,7 @@
 #define PCI_DEVICE_ID_LSI_SAS1078        0x0060
 
 #define PCI_VENDOR_ID_DEC                0x1011
+#define PCI_DEVICE_ID_DEC_21142          0x0019
 #define PCI_DEVICE_ID_DEC_21154          0x0026
 
 #define PCI_VENDOR_ID_CIRRUS             0x1013
-- 
1.8.4.rc3




reply via email to

[Prev in Thread] Current Thread [Next in Thread]