xref: /openbmc/qemu/hw/net/eepro100.c (revision f394b2e20d9a666fb194fb692179a0eeaca5daea)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * QEMU i8255x (PRO100) emulation
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  * Copyright (C) 2006-2011 Stefan Weil
549ab747fSPaolo Bonzini  *
649ab747fSPaolo Bonzini  * Portions of the code are copies from grub / etherboot eepro100.c
749ab747fSPaolo Bonzini  * and linux e100.c.
849ab747fSPaolo Bonzini  *
949ab747fSPaolo Bonzini  * This program is free software: you can redistribute it and/or modify
1049ab747fSPaolo Bonzini  * it under the terms of the GNU General Public License as published by
1149ab747fSPaolo Bonzini  * the Free Software Foundation, either version 2 of the License, or
1249ab747fSPaolo Bonzini  * (at your option) version 3 or any later version.
1349ab747fSPaolo Bonzini  *
1449ab747fSPaolo Bonzini  * This program is distributed in the hope that it will be useful,
1549ab747fSPaolo Bonzini  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1649ab747fSPaolo Bonzini  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1749ab747fSPaolo Bonzini  * GNU General Public License for more details.
1849ab747fSPaolo Bonzini  *
1949ab747fSPaolo Bonzini  * You should have received a copy of the GNU General Public License
2049ab747fSPaolo Bonzini  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
2149ab747fSPaolo Bonzini  *
2249ab747fSPaolo Bonzini  * Tested features (i82559):
2349ab747fSPaolo Bonzini  *      PXE boot (i386 guest, i386 / mips / mipsel / ppc host) ok
2449ab747fSPaolo Bonzini  *      Linux networking (i386) ok
2549ab747fSPaolo Bonzini  *
2649ab747fSPaolo Bonzini  * Untested:
2749ab747fSPaolo Bonzini  *      Windows networking
2849ab747fSPaolo Bonzini  *
2949ab747fSPaolo Bonzini  * References:
3049ab747fSPaolo Bonzini  *
3149ab747fSPaolo Bonzini  * Intel 8255x 10/100 Mbps Ethernet Controller Family
3249ab747fSPaolo Bonzini  * Open Source Software Developer Manual
3349ab747fSPaolo Bonzini  *
3449ab747fSPaolo Bonzini  * TODO:
3549ab747fSPaolo Bonzini  *      * PHY emulation should be separated from nic emulation.
3649ab747fSPaolo Bonzini  *        Most nic emulations could share the same phy code.
3749ab747fSPaolo Bonzini  *      * i82550 is untested. It is programmed like the i82559.
3849ab747fSPaolo Bonzini  *      * i82562 is untested. It is programmed like the i82559.
3949ab747fSPaolo Bonzini  *      * Power management (i82558 and later) is not implemented.
4049ab747fSPaolo Bonzini  *      * Wake-on-LAN is not implemented.
4149ab747fSPaolo Bonzini  */
4249ab747fSPaolo Bonzini 
43e8d40465SPeter Maydell #include "qemu/osdep.h"
4449ab747fSPaolo Bonzini #include "hw/hw.h"
4549ab747fSPaolo Bonzini #include "hw/pci/pci.h"
4649ab747fSPaolo Bonzini #include "net/net.h"
4749ab747fSPaolo Bonzini #include "hw/nvram/eeprom93xx.h"
4849ab747fSPaolo Bonzini #include "sysemu/sysemu.h"
4949ab747fSPaolo Bonzini #include "sysemu/dma.h"
50949fc823SMarcel Apfelbaum #include "qemu/bitops.h"
5149ab747fSPaolo Bonzini 
5249ab747fSPaolo Bonzini /* QEMU sends frames smaller than 60 bytes to ethernet nics.
5349ab747fSPaolo Bonzini  * Such frames are rejected by real nics and their emulations.
5449ab747fSPaolo Bonzini  * To avoid this behaviour, other nic emulations pad received
5549ab747fSPaolo Bonzini  * frames. The following definition enables this padding for
5649ab747fSPaolo Bonzini  * eepro100, too. We keep the define around in case it might
5749ab747fSPaolo Bonzini  * become useful the future if the core networking is ever
5849ab747fSPaolo Bonzini  * changed to pad short packets itself. */
5949ab747fSPaolo Bonzini #define CONFIG_PAD_RECEIVED_FRAMES
6049ab747fSPaolo Bonzini 
6149ab747fSPaolo Bonzini #define KiB 1024
6249ab747fSPaolo Bonzini 
6349ab747fSPaolo Bonzini /* Debug EEPRO100 card. */
6449ab747fSPaolo Bonzini #if 0
6549ab747fSPaolo Bonzini # define DEBUG_EEPRO100
6649ab747fSPaolo Bonzini #endif
6749ab747fSPaolo Bonzini 
6849ab747fSPaolo Bonzini #ifdef DEBUG_EEPRO100
6949ab747fSPaolo Bonzini #define logout(fmt, ...) fprintf(stderr, "EE100\t%-24s" fmt, __func__, ## __VA_ARGS__)
7049ab747fSPaolo Bonzini #else
7149ab747fSPaolo Bonzini #define logout(fmt, ...) ((void)0)
7249ab747fSPaolo Bonzini #endif
7349ab747fSPaolo Bonzini 
7449ab747fSPaolo Bonzini /* Set flags to 0 to disable debug output. */
7549ab747fSPaolo Bonzini #define INT     1       /* interrupt related actions */
7649ab747fSPaolo Bonzini #define MDI     1       /* mdi related actions */
7749ab747fSPaolo Bonzini #define OTHER   1
7849ab747fSPaolo Bonzini #define RXTX    1
7949ab747fSPaolo Bonzini #define EEPROM  1       /* eeprom related actions */
8049ab747fSPaolo Bonzini 
8149ab747fSPaolo Bonzini #define TRACE(flag, command) ((flag) ? (command) : (void)0)
8249ab747fSPaolo Bonzini 
8349ab747fSPaolo Bonzini #define missing(text) fprintf(stderr, "eepro100: feature is missing in this emulation: " text "\n")
8449ab747fSPaolo Bonzini 
8549ab747fSPaolo Bonzini #define MAX_ETH_FRAME_SIZE 1514
8649ab747fSPaolo Bonzini 
8749ab747fSPaolo Bonzini /* This driver supports several different devices which are declared here. */
8849ab747fSPaolo Bonzini #define i82550          0x82550
8949ab747fSPaolo Bonzini #define i82551          0x82551
9049ab747fSPaolo Bonzini #define i82557A         0x82557a
9149ab747fSPaolo Bonzini #define i82557B         0x82557b
9249ab747fSPaolo Bonzini #define i82557C         0x82557c
9349ab747fSPaolo Bonzini #define i82558A         0x82558a
9449ab747fSPaolo Bonzini #define i82558B         0x82558b
9549ab747fSPaolo Bonzini #define i82559A         0x82559a
9649ab747fSPaolo Bonzini #define i82559B         0x82559b
9749ab747fSPaolo Bonzini #define i82559C         0x82559c
9849ab747fSPaolo Bonzini #define i82559ER        0x82559e
9949ab747fSPaolo Bonzini #define i82562          0x82562
10049ab747fSPaolo Bonzini #define i82801          0x82801
10149ab747fSPaolo Bonzini 
10249ab747fSPaolo Bonzini /* Use 64 word EEPROM. TODO: could be a runtime option. */
10349ab747fSPaolo Bonzini #define EEPROM_SIZE     64
10449ab747fSPaolo Bonzini 
10549ab747fSPaolo Bonzini #define PCI_MEM_SIZE            (4 * KiB)
10649ab747fSPaolo Bonzini #define PCI_IO_SIZE             64
10749ab747fSPaolo Bonzini #define PCI_FLASH_SIZE          (128 * KiB)
10849ab747fSPaolo Bonzini 
10949ab747fSPaolo Bonzini #define BITS(n, m) (((0xffffffffU << (31 - n)) >> (31 - n + m)) << m)
11049ab747fSPaolo Bonzini 
11149ab747fSPaolo Bonzini /* The SCB accepts the following controls for the Tx and Rx units: */
11249ab747fSPaolo Bonzini #define  CU_NOP         0x0000  /* No operation. */
11349ab747fSPaolo Bonzini #define  CU_START       0x0010  /* CU start. */
11449ab747fSPaolo Bonzini #define  CU_RESUME      0x0020  /* CU resume. */
11549ab747fSPaolo Bonzini #define  CU_STATSADDR   0x0040  /* Load dump counters address. */
11649ab747fSPaolo Bonzini #define  CU_SHOWSTATS   0x0050  /* Dump statistical counters. */
11749ab747fSPaolo Bonzini #define  CU_CMD_BASE    0x0060  /* Load CU base address. */
11849ab747fSPaolo Bonzini #define  CU_DUMPSTATS   0x0070  /* Dump and reset statistical counters. */
11949ab747fSPaolo Bonzini #define  CU_SRESUME     0x00a0  /* CU static resume. */
12049ab747fSPaolo Bonzini 
12149ab747fSPaolo Bonzini #define  RU_NOP         0x0000
12249ab747fSPaolo Bonzini #define  RX_START       0x0001
12349ab747fSPaolo Bonzini #define  RX_RESUME      0x0002
12449ab747fSPaolo Bonzini #define  RU_ABORT       0x0004
12549ab747fSPaolo Bonzini #define  RX_ADDR_LOAD   0x0006
12649ab747fSPaolo Bonzini #define  RX_RESUMENR    0x0007
12749ab747fSPaolo Bonzini #define INT_MASK        0x0100
12849ab747fSPaolo Bonzini #define DRVR_INT        0x0200  /* Driver generated interrupt. */
12949ab747fSPaolo Bonzini 
13049ab747fSPaolo Bonzini typedef struct {
13149ab747fSPaolo Bonzini     const char *name;
13249ab747fSPaolo Bonzini     const char *desc;
13349ab747fSPaolo Bonzini     uint16_t device_id;
13449ab747fSPaolo Bonzini     uint8_t revision;
13549ab747fSPaolo Bonzini     uint16_t subsystem_vendor_id;
13649ab747fSPaolo Bonzini     uint16_t subsystem_id;
13749ab747fSPaolo Bonzini 
13849ab747fSPaolo Bonzini     uint32_t device;
13949ab747fSPaolo Bonzini     uint8_t stats_size;
14049ab747fSPaolo Bonzini     bool has_extended_tcb_support;
14149ab747fSPaolo Bonzini     bool power_management;
14249ab747fSPaolo Bonzini } E100PCIDeviceInfo;
14349ab747fSPaolo Bonzini 
14449ab747fSPaolo Bonzini /* Offsets to the various registers.
14549ab747fSPaolo Bonzini    All accesses need not be longword aligned. */
14649ab747fSPaolo Bonzini typedef enum {
14749ab747fSPaolo Bonzini     SCBStatus = 0,              /* Status Word. */
14849ab747fSPaolo Bonzini     SCBAck = 1,
14949ab747fSPaolo Bonzini     SCBCmd = 2,                 /* Rx/Command Unit command and status. */
15049ab747fSPaolo Bonzini     SCBIntmask = 3,
15149ab747fSPaolo Bonzini     SCBPointer = 4,             /* General purpose pointer. */
15249ab747fSPaolo Bonzini     SCBPort = 8,                /* Misc. commands and operands.  */
15349ab747fSPaolo Bonzini     SCBflash = 12,              /* Flash memory control. */
15449ab747fSPaolo Bonzini     SCBeeprom = 14,             /* EEPROM control. */
15549ab747fSPaolo Bonzini     SCBCtrlMDI = 16,            /* MDI interface control. */
15649ab747fSPaolo Bonzini     SCBEarlyRx = 20,            /* Early receive byte count. */
15749ab747fSPaolo Bonzini     SCBFlow = 24,               /* Flow Control. */
15849ab747fSPaolo Bonzini     SCBpmdr = 27,               /* Power Management Driver. */
15949ab747fSPaolo Bonzini     SCBgctrl = 28,              /* General Control. */
16049ab747fSPaolo Bonzini     SCBgstat = 29,              /* General Status. */
16149ab747fSPaolo Bonzini } E100RegisterOffset;
16249ab747fSPaolo Bonzini 
16349ab747fSPaolo Bonzini /* A speedo3 transmit buffer descriptor with two buffers... */
16449ab747fSPaolo Bonzini typedef struct {
16549ab747fSPaolo Bonzini     uint16_t status;
16649ab747fSPaolo Bonzini     uint16_t command;
16749ab747fSPaolo Bonzini     uint32_t link;              /* void * */
16849ab747fSPaolo Bonzini     uint32_t tbd_array_addr;    /* transmit buffer descriptor array address. */
16949ab747fSPaolo Bonzini     uint16_t tcb_bytes;         /* transmit command block byte count (in lower 14 bits */
17049ab747fSPaolo Bonzini     uint8_t tx_threshold;       /* transmit threshold */
17149ab747fSPaolo Bonzini     uint8_t tbd_count;          /* TBD number */
17249ab747fSPaolo Bonzini #if 0
17349ab747fSPaolo Bonzini     /* This constitutes two "TBD" entries: hdr and data */
17449ab747fSPaolo Bonzini     uint32_t tx_buf_addr0;  /* void *, header of frame to be transmitted.  */
17549ab747fSPaolo Bonzini     int32_t  tx_buf_size0;  /* Length of Tx hdr. */
17649ab747fSPaolo Bonzini     uint32_t tx_buf_addr1;  /* void *, data to be transmitted.  */
17749ab747fSPaolo Bonzini     int32_t  tx_buf_size1;  /* Length of Tx data. */
17849ab747fSPaolo Bonzini #endif
17949ab747fSPaolo Bonzini } eepro100_tx_t;
18049ab747fSPaolo Bonzini 
18149ab747fSPaolo Bonzini /* Receive frame descriptor. */
18249ab747fSPaolo Bonzini typedef struct {
18349ab747fSPaolo Bonzini     int16_t status;
18449ab747fSPaolo Bonzini     uint16_t command;
18549ab747fSPaolo Bonzini     uint32_t link;              /* struct RxFD * */
18649ab747fSPaolo Bonzini     uint32_t rx_buf_addr;       /* void * */
18749ab747fSPaolo Bonzini     uint16_t count;
18849ab747fSPaolo Bonzini     uint16_t size;
18949ab747fSPaolo Bonzini     /* Ethernet frame data follows. */
19049ab747fSPaolo Bonzini } eepro100_rx_t;
19149ab747fSPaolo Bonzini 
19249ab747fSPaolo Bonzini typedef enum {
19349ab747fSPaolo Bonzini     COMMAND_EL = BIT(15),
19449ab747fSPaolo Bonzini     COMMAND_S = BIT(14),
19549ab747fSPaolo Bonzini     COMMAND_I = BIT(13),
19649ab747fSPaolo Bonzini     COMMAND_NC = BIT(4),
19749ab747fSPaolo Bonzini     COMMAND_SF = BIT(3),
19849ab747fSPaolo Bonzini     COMMAND_CMD = BITS(2, 0),
19949ab747fSPaolo Bonzini } scb_command_bit;
20049ab747fSPaolo Bonzini 
20149ab747fSPaolo Bonzini typedef enum {
20249ab747fSPaolo Bonzini     STATUS_C = BIT(15),
20349ab747fSPaolo Bonzini     STATUS_OK = BIT(13),
20449ab747fSPaolo Bonzini } scb_status_bit;
20549ab747fSPaolo Bonzini 
20649ab747fSPaolo Bonzini typedef struct {
20749ab747fSPaolo Bonzini     uint32_t tx_good_frames, tx_max_collisions, tx_late_collisions,
20849ab747fSPaolo Bonzini              tx_underruns, tx_lost_crs, tx_deferred, tx_single_collisions,
20949ab747fSPaolo Bonzini              tx_multiple_collisions, tx_total_collisions;
21049ab747fSPaolo Bonzini     uint32_t rx_good_frames, rx_crc_errors, rx_alignment_errors,
21149ab747fSPaolo Bonzini              rx_resource_errors, rx_overrun_errors, rx_cdt_errors,
21249ab747fSPaolo Bonzini              rx_short_frame_errors;
21349ab747fSPaolo Bonzini     uint32_t fc_xmt_pause, fc_rcv_pause, fc_rcv_unsupported;
21449ab747fSPaolo Bonzini     uint16_t xmt_tco_frames, rcv_tco_frames;
21549ab747fSPaolo Bonzini     /* TODO: i82559 has six reserved statistics but a total of 24 dwords. */
21649ab747fSPaolo Bonzini     uint32_t reserved[4];
21749ab747fSPaolo Bonzini } eepro100_stats_t;
21849ab747fSPaolo Bonzini 
21949ab747fSPaolo Bonzini typedef enum {
22049ab747fSPaolo Bonzini     cu_idle = 0,
22149ab747fSPaolo Bonzini     cu_suspended = 1,
22249ab747fSPaolo Bonzini     cu_active = 2,
22349ab747fSPaolo Bonzini     cu_lpq_active = 2,
22449ab747fSPaolo Bonzini     cu_hqp_active = 3
22549ab747fSPaolo Bonzini } cu_state_t;
22649ab747fSPaolo Bonzini 
22749ab747fSPaolo Bonzini typedef enum {
22849ab747fSPaolo Bonzini     ru_idle = 0,
22949ab747fSPaolo Bonzini     ru_suspended = 1,
23049ab747fSPaolo Bonzini     ru_no_resources = 2,
23149ab747fSPaolo Bonzini     ru_ready = 4
23249ab747fSPaolo Bonzini } ru_state_t;
23349ab747fSPaolo Bonzini 
23449ab747fSPaolo Bonzini typedef struct {
23549ab747fSPaolo Bonzini     PCIDevice dev;
23649ab747fSPaolo Bonzini     /* Hash register (multicast mask array, multiple individual addresses). */
23749ab747fSPaolo Bonzini     uint8_t mult[8];
23849ab747fSPaolo Bonzini     MemoryRegion mmio_bar;
23949ab747fSPaolo Bonzini     MemoryRegion io_bar;
24049ab747fSPaolo Bonzini     MemoryRegion flash_bar;
24149ab747fSPaolo Bonzini     NICState *nic;
24249ab747fSPaolo Bonzini     NICConf conf;
24349ab747fSPaolo Bonzini     uint8_t scb_stat;           /* SCB stat/ack byte */
24449ab747fSPaolo Bonzini     uint8_t int_stat;           /* PCI interrupt status */
24549ab747fSPaolo Bonzini     /* region must not be saved by nic_save. */
24649ab747fSPaolo Bonzini     uint16_t mdimem[32];
24749ab747fSPaolo Bonzini     eeprom_t *eeprom;
24849ab747fSPaolo Bonzini     uint32_t device;            /* device variant */
24949ab747fSPaolo Bonzini     /* (cu_base + cu_offset) address the next command block in the command block list. */
25049ab747fSPaolo Bonzini     uint32_t cu_base;           /* CU base address */
25149ab747fSPaolo Bonzini     uint32_t cu_offset;         /* CU address offset */
25249ab747fSPaolo Bonzini     /* (ru_base + ru_offset) address the RFD in the Receive Frame Area. */
25349ab747fSPaolo Bonzini     uint32_t ru_base;           /* RU base address */
25449ab747fSPaolo Bonzini     uint32_t ru_offset;         /* RU address offset */
25549ab747fSPaolo Bonzini     uint32_t statsaddr;         /* pointer to eepro100_stats_t */
25649ab747fSPaolo Bonzini 
25749ab747fSPaolo Bonzini     /* Temporary status information (no need to save these values),
25849ab747fSPaolo Bonzini      * used while processing CU commands. */
25949ab747fSPaolo Bonzini     eepro100_tx_t tx;           /* transmit buffer descriptor */
26049ab747fSPaolo Bonzini     uint32_t cb_address;        /* = cu_base + cu_offset */
26149ab747fSPaolo Bonzini 
26249ab747fSPaolo Bonzini     /* Statistical counters. Also used for wake-up packet (i82559). */
26349ab747fSPaolo Bonzini     eepro100_stats_t statistics;
26449ab747fSPaolo Bonzini 
26549ab747fSPaolo Bonzini     /* Data in mem is always in the byte order of the controller (le).
26649ab747fSPaolo Bonzini      * It must be dword aligned to allow direct access to 32 bit values. */
26749ab747fSPaolo Bonzini     uint8_t mem[PCI_MEM_SIZE] __attribute__((aligned(8)));
26849ab747fSPaolo Bonzini 
26949ab747fSPaolo Bonzini     /* Configuration bytes. */
27049ab747fSPaolo Bonzini     uint8_t configuration[22];
27149ab747fSPaolo Bonzini 
27249ab747fSPaolo Bonzini     /* vmstate for each particular nic */
27349ab747fSPaolo Bonzini     VMStateDescription *vmstate;
27449ab747fSPaolo Bonzini 
27549ab747fSPaolo Bonzini     /* Quasi static device properties (no need to save them). */
27649ab747fSPaolo Bonzini     uint16_t stats_size;
27749ab747fSPaolo Bonzini     bool has_extended_tcb_support;
27849ab747fSPaolo Bonzini } EEPRO100State;
27949ab747fSPaolo Bonzini 
28049ab747fSPaolo Bonzini /* Word indices in EEPROM. */
28149ab747fSPaolo Bonzini typedef enum {
28249ab747fSPaolo Bonzini     EEPROM_CNFG_MDIX  = 0x03,
28349ab747fSPaolo Bonzini     EEPROM_ID         = 0x05,
28449ab747fSPaolo Bonzini     EEPROM_PHY_ID     = 0x06,
28549ab747fSPaolo Bonzini     EEPROM_VENDOR_ID  = 0x0c,
28649ab747fSPaolo Bonzini     EEPROM_CONFIG_ASF = 0x0d,
28749ab747fSPaolo Bonzini     EEPROM_DEVICE_ID  = 0x23,
28849ab747fSPaolo Bonzini     EEPROM_SMBUS_ADDR = 0x90,
28949ab747fSPaolo Bonzini } EEPROMOffset;
29049ab747fSPaolo Bonzini 
29149ab747fSPaolo Bonzini /* Bit values for EEPROM ID word. */
29249ab747fSPaolo Bonzini typedef enum {
29349ab747fSPaolo Bonzini     EEPROM_ID_MDM = BIT(0),     /* Modem */
29449ab747fSPaolo Bonzini     EEPROM_ID_STB = BIT(1),     /* Standby Enable */
29549ab747fSPaolo Bonzini     EEPROM_ID_WMR = BIT(2),     /* ??? */
29649ab747fSPaolo Bonzini     EEPROM_ID_WOL = BIT(5),     /* Wake on LAN */
29749ab747fSPaolo Bonzini     EEPROM_ID_DPD = BIT(6),     /* Deep Power Down */
29849ab747fSPaolo Bonzini     EEPROM_ID_ALT = BIT(7),     /* */
29949ab747fSPaolo Bonzini     /* BITS(10, 8) device revision */
30049ab747fSPaolo Bonzini     EEPROM_ID_BD = BIT(11),     /* boot disable */
30149ab747fSPaolo Bonzini     EEPROM_ID_ID = BIT(13),     /* id bit */
30249ab747fSPaolo Bonzini     /* BITS(15, 14) signature */
30349ab747fSPaolo Bonzini     EEPROM_ID_VALID = BIT(14),  /* signature for valid eeprom */
30449ab747fSPaolo Bonzini } eeprom_id_bit;
30549ab747fSPaolo Bonzini 
30649ab747fSPaolo Bonzini /* Default values for MDI (PHY) registers */
30749ab747fSPaolo Bonzini static const uint16_t eepro100_mdi_default[] = {
30849ab747fSPaolo Bonzini     /* MDI Registers 0 - 6, 7 */
30949ab747fSPaolo Bonzini     0x3000, 0x780d, 0x02a8, 0x0154, 0x05e1, 0x0000, 0x0000, 0x0000,
31049ab747fSPaolo Bonzini     /* MDI Registers 8 - 15 */
31149ab747fSPaolo Bonzini     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
31249ab747fSPaolo Bonzini     /* MDI Registers 16 - 31 */
31349ab747fSPaolo Bonzini     0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
31449ab747fSPaolo Bonzini     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
31549ab747fSPaolo Bonzini };
31649ab747fSPaolo Bonzini 
31749ab747fSPaolo Bonzini /* Readonly mask for MDI (PHY) registers */
31849ab747fSPaolo Bonzini static const uint16_t eepro100_mdi_mask[] = {
31949ab747fSPaolo Bonzini     0x0000, 0xffff, 0xffff, 0xffff, 0xc01f, 0xffff, 0xffff, 0x0000,
32049ab747fSPaolo Bonzini     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
32149ab747fSPaolo Bonzini     0x0fff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
32249ab747fSPaolo Bonzini     0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
32349ab747fSPaolo Bonzini };
32449ab747fSPaolo Bonzini 
32549ab747fSPaolo Bonzini #define POLYNOMIAL 0x04c11db6
32649ab747fSPaolo Bonzini 
32749ab747fSPaolo Bonzini static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s);
32849ab747fSPaolo Bonzini 
32949ab747fSPaolo Bonzini /* From FreeBSD (locally modified). */
33049ab747fSPaolo Bonzini static unsigned e100_compute_mcast_idx(const uint8_t *ep)
33149ab747fSPaolo Bonzini {
33249ab747fSPaolo Bonzini     uint32_t crc;
33349ab747fSPaolo Bonzini     int carry, i, j;
33449ab747fSPaolo Bonzini     uint8_t b;
33549ab747fSPaolo Bonzini 
33649ab747fSPaolo Bonzini     crc = 0xffffffff;
33749ab747fSPaolo Bonzini     for (i = 0; i < 6; i++) {
33849ab747fSPaolo Bonzini         b = *ep++;
33949ab747fSPaolo Bonzini         for (j = 0; j < 8; j++) {
34049ab747fSPaolo Bonzini             carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
34149ab747fSPaolo Bonzini             crc <<= 1;
34249ab747fSPaolo Bonzini             b >>= 1;
34349ab747fSPaolo Bonzini             if (carry) {
34449ab747fSPaolo Bonzini                 crc = ((crc ^ POLYNOMIAL) | carry);
34549ab747fSPaolo Bonzini             }
34649ab747fSPaolo Bonzini         }
34749ab747fSPaolo Bonzini     }
34849ab747fSPaolo Bonzini     return (crc & BITS(7, 2)) >> 2;
34949ab747fSPaolo Bonzini }
35049ab747fSPaolo Bonzini 
35149ab747fSPaolo Bonzini /* Read a 16 bit control/status (CSR) register. */
35249ab747fSPaolo Bonzini static uint16_t e100_read_reg2(EEPRO100State *s, E100RegisterOffset addr)
35349ab747fSPaolo Bonzini {
35449ab747fSPaolo Bonzini     assert(!((uintptr_t)&s->mem[addr] & 1));
3554d9be252SPeter Maydell     return lduw_le_p(&s->mem[addr]);
35649ab747fSPaolo Bonzini }
35749ab747fSPaolo Bonzini 
35849ab747fSPaolo Bonzini /* Read a 32 bit control/status (CSR) register. */
35949ab747fSPaolo Bonzini static uint32_t e100_read_reg4(EEPRO100State *s, E100RegisterOffset addr)
36049ab747fSPaolo Bonzini {
36149ab747fSPaolo Bonzini     assert(!((uintptr_t)&s->mem[addr] & 3));
3624d9be252SPeter Maydell     return ldl_le_p(&s->mem[addr]);
36349ab747fSPaolo Bonzini }
36449ab747fSPaolo Bonzini 
36549ab747fSPaolo Bonzini /* Write a 16 bit control/status (CSR) register. */
36649ab747fSPaolo Bonzini static void e100_write_reg2(EEPRO100State *s, E100RegisterOffset addr,
36749ab747fSPaolo Bonzini                             uint16_t val)
36849ab747fSPaolo Bonzini {
36949ab747fSPaolo Bonzini     assert(!((uintptr_t)&s->mem[addr] & 1));
3704d9be252SPeter Maydell     stw_le_p(&s->mem[addr], val);
37149ab747fSPaolo Bonzini }
37249ab747fSPaolo Bonzini 
37349ab747fSPaolo Bonzini /* Read a 32 bit control/status (CSR) register. */
37449ab747fSPaolo Bonzini static void e100_write_reg4(EEPRO100State *s, E100RegisterOffset addr,
37549ab747fSPaolo Bonzini                             uint32_t val)
37649ab747fSPaolo Bonzini {
37749ab747fSPaolo Bonzini     assert(!((uintptr_t)&s->mem[addr] & 3));
3784d9be252SPeter Maydell     stl_le_p(&s->mem[addr], val);
37949ab747fSPaolo Bonzini }
38049ab747fSPaolo Bonzini 
38149ab747fSPaolo Bonzini #if defined(DEBUG_EEPRO100)
38249ab747fSPaolo Bonzini static const char *nic_dump(const uint8_t * buf, unsigned size)
38349ab747fSPaolo Bonzini {
38449ab747fSPaolo Bonzini     static char dump[3 * 16 + 1];
38549ab747fSPaolo Bonzini     char *p = &dump[0];
38649ab747fSPaolo Bonzini     if (size > 16) {
38749ab747fSPaolo Bonzini         size = 16;
38849ab747fSPaolo Bonzini     }
38949ab747fSPaolo Bonzini     while (size-- > 0) {
39049ab747fSPaolo Bonzini         p += sprintf(p, " %02x", *buf++);
39149ab747fSPaolo Bonzini     }
39249ab747fSPaolo Bonzini     return dump;
39349ab747fSPaolo Bonzini }
39449ab747fSPaolo Bonzini #endif                          /* DEBUG_EEPRO100 */
39549ab747fSPaolo Bonzini 
39649ab747fSPaolo Bonzini enum scb_stat_ack {
39749ab747fSPaolo Bonzini     stat_ack_not_ours = 0x00,
39849ab747fSPaolo Bonzini     stat_ack_sw_gen = 0x04,
39949ab747fSPaolo Bonzini     stat_ack_rnr = 0x10,
40049ab747fSPaolo Bonzini     stat_ack_cu_idle = 0x20,
40149ab747fSPaolo Bonzini     stat_ack_frame_rx = 0x40,
40249ab747fSPaolo Bonzini     stat_ack_cu_cmd_done = 0x80,
40349ab747fSPaolo Bonzini     stat_ack_not_present = 0xFF,
40449ab747fSPaolo Bonzini     stat_ack_rx = (stat_ack_sw_gen | stat_ack_rnr | stat_ack_frame_rx),
40549ab747fSPaolo Bonzini     stat_ack_tx = (stat_ack_cu_idle | stat_ack_cu_cmd_done),
40649ab747fSPaolo Bonzini };
40749ab747fSPaolo Bonzini 
40849ab747fSPaolo Bonzini static void disable_interrupt(EEPRO100State * s)
40949ab747fSPaolo Bonzini {
41049ab747fSPaolo Bonzini     if (s->int_stat) {
41149ab747fSPaolo Bonzini         TRACE(INT, logout("interrupt disabled\n"));
4129e64f8a3SMarcel Apfelbaum         pci_irq_deassert(&s->dev);
41349ab747fSPaolo Bonzini         s->int_stat = 0;
41449ab747fSPaolo Bonzini     }
41549ab747fSPaolo Bonzini }
41649ab747fSPaolo Bonzini 
41749ab747fSPaolo Bonzini static void enable_interrupt(EEPRO100State * s)
41849ab747fSPaolo Bonzini {
41949ab747fSPaolo Bonzini     if (!s->int_stat) {
42049ab747fSPaolo Bonzini         TRACE(INT, logout("interrupt enabled\n"));
4219e64f8a3SMarcel Apfelbaum         pci_irq_assert(&s->dev);
42249ab747fSPaolo Bonzini         s->int_stat = 1;
42349ab747fSPaolo Bonzini     }
42449ab747fSPaolo Bonzini }
42549ab747fSPaolo Bonzini 
42649ab747fSPaolo Bonzini static void eepro100_acknowledge(EEPRO100State * s)
42749ab747fSPaolo Bonzini {
42849ab747fSPaolo Bonzini     s->scb_stat &= ~s->mem[SCBAck];
42949ab747fSPaolo Bonzini     s->mem[SCBAck] = s->scb_stat;
43049ab747fSPaolo Bonzini     if (s->scb_stat == 0) {
43149ab747fSPaolo Bonzini         disable_interrupt(s);
43249ab747fSPaolo Bonzini     }
43349ab747fSPaolo Bonzini }
43449ab747fSPaolo Bonzini 
43549ab747fSPaolo Bonzini static void eepro100_interrupt(EEPRO100State * s, uint8_t status)
43649ab747fSPaolo Bonzini {
43749ab747fSPaolo Bonzini     uint8_t mask = ~s->mem[SCBIntmask];
43849ab747fSPaolo Bonzini     s->mem[SCBAck] |= status;
43949ab747fSPaolo Bonzini     status = s->scb_stat = s->mem[SCBAck];
44049ab747fSPaolo Bonzini     status &= (mask | 0x0f);
44149ab747fSPaolo Bonzini #if 0
44249ab747fSPaolo Bonzini     status &= (~s->mem[SCBIntmask] | 0x0xf);
44349ab747fSPaolo Bonzini #endif
44449ab747fSPaolo Bonzini     if (status && (mask & 0x01)) {
44549ab747fSPaolo Bonzini         /* SCB mask and SCB Bit M do not disable interrupt. */
44649ab747fSPaolo Bonzini         enable_interrupt(s);
44749ab747fSPaolo Bonzini     } else if (s->int_stat) {
44849ab747fSPaolo Bonzini         disable_interrupt(s);
44949ab747fSPaolo Bonzini     }
45049ab747fSPaolo Bonzini }
45149ab747fSPaolo Bonzini 
45249ab747fSPaolo Bonzini static void eepro100_cx_interrupt(EEPRO100State * s)
45349ab747fSPaolo Bonzini {
45449ab747fSPaolo Bonzini     /* CU completed action command. */
45549ab747fSPaolo Bonzini     /* Transmit not ok (82557 only, not in emulation). */
45649ab747fSPaolo Bonzini     eepro100_interrupt(s, 0x80);
45749ab747fSPaolo Bonzini }
45849ab747fSPaolo Bonzini 
45949ab747fSPaolo Bonzini static void eepro100_cna_interrupt(EEPRO100State * s)
46049ab747fSPaolo Bonzini {
46149ab747fSPaolo Bonzini     /* CU left the active state. */
46249ab747fSPaolo Bonzini     eepro100_interrupt(s, 0x20);
46349ab747fSPaolo Bonzini }
46449ab747fSPaolo Bonzini 
46549ab747fSPaolo Bonzini static void eepro100_fr_interrupt(EEPRO100State * s)
46649ab747fSPaolo Bonzini {
46749ab747fSPaolo Bonzini     /* RU received a complete frame. */
46849ab747fSPaolo Bonzini     eepro100_interrupt(s, 0x40);
46949ab747fSPaolo Bonzini }
47049ab747fSPaolo Bonzini 
47149ab747fSPaolo Bonzini static void eepro100_rnr_interrupt(EEPRO100State * s)
47249ab747fSPaolo Bonzini {
47349ab747fSPaolo Bonzini     /* RU is not ready. */
47449ab747fSPaolo Bonzini     eepro100_interrupt(s, 0x10);
47549ab747fSPaolo Bonzini }
47649ab747fSPaolo Bonzini 
47749ab747fSPaolo Bonzini static void eepro100_mdi_interrupt(EEPRO100State * s)
47849ab747fSPaolo Bonzini {
47949ab747fSPaolo Bonzini     /* MDI completed read or write cycle. */
48049ab747fSPaolo Bonzini     eepro100_interrupt(s, 0x08);
48149ab747fSPaolo Bonzini }
48249ab747fSPaolo Bonzini 
48349ab747fSPaolo Bonzini static void eepro100_swi_interrupt(EEPRO100State * s)
48449ab747fSPaolo Bonzini {
48549ab747fSPaolo Bonzini     /* Software has requested an interrupt. */
48649ab747fSPaolo Bonzini     eepro100_interrupt(s, 0x04);
48749ab747fSPaolo Bonzini }
48849ab747fSPaolo Bonzini 
48949ab747fSPaolo Bonzini #if 0
49049ab747fSPaolo Bonzini static void eepro100_fcp_interrupt(EEPRO100State * s)
49149ab747fSPaolo Bonzini {
49249ab747fSPaolo Bonzini     /* Flow control pause interrupt (82558 and later). */
49349ab747fSPaolo Bonzini     eepro100_interrupt(s, 0x01);
49449ab747fSPaolo Bonzini }
49549ab747fSPaolo Bonzini #endif
49649ab747fSPaolo Bonzini 
49749ab747fSPaolo Bonzini static void e100_pci_reset(EEPRO100State * s)
49849ab747fSPaolo Bonzini {
49949ab747fSPaolo Bonzini     E100PCIDeviceInfo *info = eepro100_get_class(s);
50049ab747fSPaolo Bonzini     uint32_t device = s->device;
50149ab747fSPaolo Bonzini     uint8_t *pci_conf = s->dev.config;
50249ab747fSPaolo Bonzini 
50349ab747fSPaolo Bonzini     TRACE(OTHER, logout("%p\n", s));
50449ab747fSPaolo Bonzini 
50549ab747fSPaolo Bonzini     /* PCI Status */
50649ab747fSPaolo Bonzini     pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM |
50749ab747fSPaolo Bonzini                                         PCI_STATUS_FAST_BACK);
50849ab747fSPaolo Bonzini     /* PCI Latency Timer */
50949ab747fSPaolo Bonzini     pci_set_byte(pci_conf + PCI_LATENCY_TIMER, 0x20);   /* latency timer = 32 clocks */
51049ab747fSPaolo Bonzini     /* Capability Pointer is set by PCI framework. */
51149ab747fSPaolo Bonzini     /* Interrupt Line */
51249ab747fSPaolo Bonzini     /* Interrupt Pin */
51349ab747fSPaolo Bonzini     pci_set_byte(pci_conf + PCI_INTERRUPT_PIN, 1);      /* interrupt pin A */
51449ab747fSPaolo Bonzini     /* Minimum Grant */
51549ab747fSPaolo Bonzini     pci_set_byte(pci_conf + PCI_MIN_GNT, 0x08);
51649ab747fSPaolo Bonzini     /* Maximum Latency */
51749ab747fSPaolo Bonzini     pci_set_byte(pci_conf + PCI_MAX_LAT, 0x18);
51849ab747fSPaolo Bonzini 
51949ab747fSPaolo Bonzini     s->stats_size = info->stats_size;
52049ab747fSPaolo Bonzini     s->has_extended_tcb_support = info->has_extended_tcb_support;
52149ab747fSPaolo Bonzini 
52249ab747fSPaolo Bonzini     switch (device) {
52349ab747fSPaolo Bonzini     case i82550:
52449ab747fSPaolo Bonzini     case i82551:
52549ab747fSPaolo Bonzini     case i82557A:
52649ab747fSPaolo Bonzini     case i82557B:
52749ab747fSPaolo Bonzini     case i82557C:
52849ab747fSPaolo Bonzini     case i82558A:
52949ab747fSPaolo Bonzini     case i82558B:
53049ab747fSPaolo Bonzini     case i82559A:
53149ab747fSPaolo Bonzini     case i82559B:
53249ab747fSPaolo Bonzini     case i82559ER:
53349ab747fSPaolo Bonzini     case i82562:
53449ab747fSPaolo Bonzini     case i82801:
53549ab747fSPaolo Bonzini     case i82559C:
53649ab747fSPaolo Bonzini         break;
53749ab747fSPaolo Bonzini     default:
53849ab747fSPaolo Bonzini         logout("Device %X is undefined!\n", device);
53949ab747fSPaolo Bonzini     }
54049ab747fSPaolo Bonzini 
54149ab747fSPaolo Bonzini     /* Standard TxCB. */
54249ab747fSPaolo Bonzini     s->configuration[6] |= BIT(4);
54349ab747fSPaolo Bonzini 
54449ab747fSPaolo Bonzini     /* Standard statistical counters. */
54549ab747fSPaolo Bonzini     s->configuration[6] |= BIT(5);
54649ab747fSPaolo Bonzini 
54749ab747fSPaolo Bonzini     if (s->stats_size == 80) {
54849ab747fSPaolo Bonzini         /* TODO: check TCO Statistical Counters bit. Documentation not clear. */
54949ab747fSPaolo Bonzini         if (s->configuration[6] & BIT(2)) {
55049ab747fSPaolo Bonzini             /* TCO statistical counters. */
55149ab747fSPaolo Bonzini             assert(s->configuration[6] & BIT(5));
55249ab747fSPaolo Bonzini         } else {
55349ab747fSPaolo Bonzini             if (s->configuration[6] & BIT(5)) {
55449ab747fSPaolo Bonzini                 /* No extended statistical counters, i82557 compatible. */
55549ab747fSPaolo Bonzini                 s->stats_size = 64;
55649ab747fSPaolo Bonzini             } else {
55749ab747fSPaolo Bonzini                 /* i82558 compatible. */
55849ab747fSPaolo Bonzini                 s->stats_size = 76;
55949ab747fSPaolo Bonzini             }
56049ab747fSPaolo Bonzini         }
56149ab747fSPaolo Bonzini     } else {
56249ab747fSPaolo Bonzini         if (s->configuration[6] & BIT(5)) {
56349ab747fSPaolo Bonzini             /* No extended statistical counters. */
56449ab747fSPaolo Bonzini             s->stats_size = 64;
56549ab747fSPaolo Bonzini         }
56649ab747fSPaolo Bonzini     }
56749ab747fSPaolo Bonzini     assert(s->stats_size > 0 && s->stats_size <= sizeof(s->statistics));
56849ab747fSPaolo Bonzini 
56949ab747fSPaolo Bonzini     if (info->power_management) {
57049ab747fSPaolo Bonzini         /* Power Management Capabilities */
57149ab747fSPaolo Bonzini         int cfg_offset = 0xdc;
57249ab747fSPaolo Bonzini         int r = pci_add_capability(&s->dev, PCI_CAP_ID_PM,
57349ab747fSPaolo Bonzini                                    cfg_offset, PCI_PM_SIZEOF);
57449ab747fSPaolo Bonzini         assert(r >= 0);
57549ab747fSPaolo Bonzini         pci_set_word(pci_conf + cfg_offset + PCI_PM_PMC, 0x7e21);
57649ab747fSPaolo Bonzini #if 0 /* TODO: replace dummy code for power management emulation. */
57749ab747fSPaolo Bonzini         /* TODO: Power Management Control / Status. */
57849ab747fSPaolo Bonzini         pci_set_word(pci_conf + cfg_offset + PCI_PM_CTRL, 0x0000);
57949ab747fSPaolo Bonzini         /* TODO: Ethernet Power Consumption Registers (i82559 and later). */
58049ab747fSPaolo Bonzini         pci_set_byte(pci_conf + cfg_offset + PCI_PM_PPB_EXTENSIONS, 0x0000);
58149ab747fSPaolo Bonzini #endif
58249ab747fSPaolo Bonzini     }
58349ab747fSPaolo Bonzini 
58449ab747fSPaolo Bonzini #if EEPROM_SIZE > 0
58549ab747fSPaolo Bonzini     if (device == i82557C || device == i82558B || device == i82559C) {
58649ab747fSPaolo Bonzini         /*
58749ab747fSPaolo Bonzini         TODO: get vendor id from EEPROM for i82557C or later.
58849ab747fSPaolo Bonzini         TODO: get device id from EEPROM for i82557C or later.
58949ab747fSPaolo Bonzini         TODO: status bit 4 can be disabled by EEPROM for i82558, i82559.
59049ab747fSPaolo Bonzini         TODO: header type is determined by EEPROM for i82559.
59149ab747fSPaolo Bonzini         TODO: get subsystem id from EEPROM for i82557C or later.
59249ab747fSPaolo Bonzini         TODO: get subsystem vendor id from EEPROM for i82557C or later.
59349ab747fSPaolo Bonzini         TODO: exp. rom baddr depends on a bit in EEPROM for i82558 or later.
59449ab747fSPaolo Bonzini         TODO: capability pointer depends on EEPROM for i82558.
59549ab747fSPaolo Bonzini         */
59649ab747fSPaolo Bonzini         logout("Get device id and revision from EEPROM!!!\n");
59749ab747fSPaolo Bonzini     }
59849ab747fSPaolo Bonzini #endif /* EEPROM_SIZE > 0 */
59949ab747fSPaolo Bonzini }
60049ab747fSPaolo Bonzini 
60149ab747fSPaolo Bonzini static void nic_selective_reset(EEPRO100State * s)
60249ab747fSPaolo Bonzini {
60349ab747fSPaolo Bonzini     size_t i;
60449ab747fSPaolo Bonzini     uint16_t *eeprom_contents = eeprom93xx_data(s->eeprom);
60549ab747fSPaolo Bonzini #if 0
60649ab747fSPaolo Bonzini     eeprom93xx_reset(s->eeprom);
60749ab747fSPaolo Bonzini #endif
60849ab747fSPaolo Bonzini     memcpy(eeprom_contents, s->conf.macaddr.a, 6);
60949ab747fSPaolo Bonzini     eeprom_contents[EEPROM_ID] = EEPROM_ID_VALID;
61049ab747fSPaolo Bonzini     if (s->device == i82557B || s->device == i82557C)
61149ab747fSPaolo Bonzini         eeprom_contents[5] = 0x0100;
61249ab747fSPaolo Bonzini     eeprom_contents[EEPROM_PHY_ID] = 1;
61349ab747fSPaolo Bonzini     uint16_t sum = 0;
61449ab747fSPaolo Bonzini     for (i = 0; i < EEPROM_SIZE - 1; i++) {
61549ab747fSPaolo Bonzini         sum += eeprom_contents[i];
61649ab747fSPaolo Bonzini     }
61749ab747fSPaolo Bonzini     eeprom_contents[EEPROM_SIZE - 1] = 0xbaba - sum;
61849ab747fSPaolo Bonzini     TRACE(EEPROM, logout("checksum=0x%04x\n", eeprom_contents[EEPROM_SIZE - 1]));
61949ab747fSPaolo Bonzini 
62049ab747fSPaolo Bonzini     memset(s->mem, 0, sizeof(s->mem));
62149ab747fSPaolo Bonzini     e100_write_reg4(s, SCBCtrlMDI, BIT(21));
62249ab747fSPaolo Bonzini 
62349ab747fSPaolo Bonzini     assert(sizeof(s->mdimem) == sizeof(eepro100_mdi_default));
62449ab747fSPaolo Bonzini     memcpy(&s->mdimem[0], &eepro100_mdi_default[0], sizeof(s->mdimem));
62549ab747fSPaolo Bonzini }
62649ab747fSPaolo Bonzini 
62749ab747fSPaolo Bonzini static void nic_reset(void *opaque)
62849ab747fSPaolo Bonzini {
62949ab747fSPaolo Bonzini     EEPRO100State *s = opaque;
63049ab747fSPaolo Bonzini     TRACE(OTHER, logout("%p\n", s));
63149ab747fSPaolo Bonzini     /* TODO: Clearing of hash register for selective reset, too? */
63249ab747fSPaolo Bonzini     memset(&s->mult[0], 0, sizeof(s->mult));
63349ab747fSPaolo Bonzini     nic_selective_reset(s);
63449ab747fSPaolo Bonzini }
63549ab747fSPaolo Bonzini 
63649ab747fSPaolo Bonzini #if defined(DEBUG_EEPRO100)
63749ab747fSPaolo Bonzini static const char * const e100_reg[PCI_IO_SIZE / 4] = {
63849ab747fSPaolo Bonzini     "Command/Status",
63949ab747fSPaolo Bonzini     "General Pointer",
64049ab747fSPaolo Bonzini     "Port",
64149ab747fSPaolo Bonzini     "EEPROM/Flash Control",
64249ab747fSPaolo Bonzini     "MDI Control",
64349ab747fSPaolo Bonzini     "Receive DMA Byte Count",
64449ab747fSPaolo Bonzini     "Flow Control",
64549ab747fSPaolo Bonzini     "General Status/Control"
64649ab747fSPaolo Bonzini };
64749ab747fSPaolo Bonzini 
64849ab747fSPaolo Bonzini static char *regname(uint32_t addr)
64949ab747fSPaolo Bonzini {
65049ab747fSPaolo Bonzini     static char buf[32];
65149ab747fSPaolo Bonzini     if (addr < PCI_IO_SIZE) {
65249ab747fSPaolo Bonzini         const char *r = e100_reg[addr / 4];
65349ab747fSPaolo Bonzini         if (r != 0) {
65449ab747fSPaolo Bonzini             snprintf(buf, sizeof(buf), "%s+%u", r, addr % 4);
65549ab747fSPaolo Bonzini         } else {
65649ab747fSPaolo Bonzini             snprintf(buf, sizeof(buf), "0x%02x", addr);
65749ab747fSPaolo Bonzini         }
65849ab747fSPaolo Bonzini     } else {
65949ab747fSPaolo Bonzini         snprintf(buf, sizeof(buf), "??? 0x%08x", addr);
66049ab747fSPaolo Bonzini     }
66149ab747fSPaolo Bonzini     return buf;
66249ab747fSPaolo Bonzini }
66349ab747fSPaolo Bonzini #endif                          /* DEBUG_EEPRO100 */
66449ab747fSPaolo Bonzini 
66549ab747fSPaolo Bonzini /*****************************************************************************
66649ab747fSPaolo Bonzini  *
66749ab747fSPaolo Bonzini  * Command emulation.
66849ab747fSPaolo Bonzini  *
66949ab747fSPaolo Bonzini  ****************************************************************************/
67049ab747fSPaolo Bonzini 
67149ab747fSPaolo Bonzini #if 0
67249ab747fSPaolo Bonzini static uint16_t eepro100_read_command(EEPRO100State * s)
67349ab747fSPaolo Bonzini {
67449ab747fSPaolo Bonzini     uint16_t val = 0xffff;
67549ab747fSPaolo Bonzini     TRACE(OTHER, logout("val=0x%04x\n", val));
67649ab747fSPaolo Bonzini     return val;
67749ab747fSPaolo Bonzini }
67849ab747fSPaolo Bonzini #endif
67949ab747fSPaolo Bonzini 
68049ab747fSPaolo Bonzini /* Commands that can be put in a command list entry. */
68149ab747fSPaolo Bonzini enum commands {
68249ab747fSPaolo Bonzini     CmdNOp = 0,
68349ab747fSPaolo Bonzini     CmdIASetup = 1,
68449ab747fSPaolo Bonzini     CmdConfigure = 2,
68549ab747fSPaolo Bonzini     CmdMulticastList = 3,
68649ab747fSPaolo Bonzini     CmdTx = 4,
68749ab747fSPaolo Bonzini     CmdTDR = 5,                 /* load microcode */
68849ab747fSPaolo Bonzini     CmdDump = 6,
68949ab747fSPaolo Bonzini     CmdDiagnose = 7,
69049ab747fSPaolo Bonzini 
69149ab747fSPaolo Bonzini     /* And some extra flags: */
69249ab747fSPaolo Bonzini     CmdSuspend = 0x4000,        /* Suspend after completion. */
69349ab747fSPaolo Bonzini     CmdIntr = 0x2000,           /* Interrupt after completion. */
69449ab747fSPaolo Bonzini     CmdTxFlex = 0x0008,         /* Use "Flexible mode" for CmdTx command. */
69549ab747fSPaolo Bonzini };
69649ab747fSPaolo Bonzini 
69749ab747fSPaolo Bonzini static cu_state_t get_cu_state(EEPRO100State * s)
69849ab747fSPaolo Bonzini {
69949ab747fSPaolo Bonzini     return ((s->mem[SCBStatus] & BITS(7, 6)) >> 6);
70049ab747fSPaolo Bonzini }
70149ab747fSPaolo Bonzini 
70249ab747fSPaolo Bonzini static void set_cu_state(EEPRO100State * s, cu_state_t state)
70349ab747fSPaolo Bonzini {
70449ab747fSPaolo Bonzini     s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(7, 6)) + (state << 6);
70549ab747fSPaolo Bonzini }
70649ab747fSPaolo Bonzini 
70749ab747fSPaolo Bonzini static ru_state_t get_ru_state(EEPRO100State * s)
70849ab747fSPaolo Bonzini {
70949ab747fSPaolo Bonzini     return ((s->mem[SCBStatus] & BITS(5, 2)) >> 2);
71049ab747fSPaolo Bonzini }
71149ab747fSPaolo Bonzini 
71249ab747fSPaolo Bonzini static void set_ru_state(EEPRO100State * s, ru_state_t state)
71349ab747fSPaolo Bonzini {
71449ab747fSPaolo Bonzini     s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(5, 2)) + (state << 2);
71549ab747fSPaolo Bonzini }
71649ab747fSPaolo Bonzini 
71749ab747fSPaolo Bonzini static void dump_statistics(EEPRO100State * s)
71849ab747fSPaolo Bonzini {
71949ab747fSPaolo Bonzini     /* Dump statistical data. Most data is never changed by the emulation
72049ab747fSPaolo Bonzini      * and always 0, so we first just copy the whole block and then those
72149ab747fSPaolo Bonzini      * values which really matter.
72249ab747fSPaolo Bonzini      * Number of data should check configuration!!!
72349ab747fSPaolo Bonzini      */
72449ab747fSPaolo Bonzini     pci_dma_write(&s->dev, s->statsaddr, &s->statistics, s->stats_size);
72549ab747fSPaolo Bonzini     stl_le_pci_dma(&s->dev, s->statsaddr + 0,
72649ab747fSPaolo Bonzini                    s->statistics.tx_good_frames);
72749ab747fSPaolo Bonzini     stl_le_pci_dma(&s->dev, s->statsaddr + 36,
72849ab747fSPaolo Bonzini                    s->statistics.rx_good_frames);
72949ab747fSPaolo Bonzini     stl_le_pci_dma(&s->dev, s->statsaddr + 48,
73049ab747fSPaolo Bonzini                    s->statistics.rx_resource_errors);
73149ab747fSPaolo Bonzini     stl_le_pci_dma(&s->dev, s->statsaddr + 60,
73249ab747fSPaolo Bonzini                    s->statistics.rx_short_frame_errors);
73349ab747fSPaolo Bonzini #if 0
73449ab747fSPaolo Bonzini     stw_le_pci_dma(&s->dev, s->statsaddr + 76, s->statistics.xmt_tco_frames);
73549ab747fSPaolo Bonzini     stw_le_pci_dma(&s->dev, s->statsaddr + 78, s->statistics.rcv_tco_frames);
73649ab747fSPaolo Bonzini     missing("CU dump statistical counters");
73749ab747fSPaolo Bonzini #endif
73849ab747fSPaolo Bonzini }
73949ab747fSPaolo Bonzini 
74049ab747fSPaolo Bonzini static void read_cb(EEPRO100State *s)
74149ab747fSPaolo Bonzini {
74249ab747fSPaolo Bonzini     pci_dma_read(&s->dev, s->cb_address, &s->tx, sizeof(s->tx));
74349ab747fSPaolo Bonzini     s->tx.status = le16_to_cpu(s->tx.status);
74449ab747fSPaolo Bonzini     s->tx.command = le16_to_cpu(s->tx.command);
74549ab747fSPaolo Bonzini     s->tx.link = le32_to_cpu(s->tx.link);
74649ab747fSPaolo Bonzini     s->tx.tbd_array_addr = le32_to_cpu(s->tx.tbd_array_addr);
74749ab747fSPaolo Bonzini     s->tx.tcb_bytes = le16_to_cpu(s->tx.tcb_bytes);
74849ab747fSPaolo Bonzini }
74949ab747fSPaolo Bonzini 
75049ab747fSPaolo Bonzini static void tx_command(EEPRO100State *s)
75149ab747fSPaolo Bonzini {
75249ab747fSPaolo Bonzini     uint32_t tbd_array = le32_to_cpu(s->tx.tbd_array_addr);
75349ab747fSPaolo Bonzini     uint16_t tcb_bytes = (le16_to_cpu(s->tx.tcb_bytes) & 0x3fff);
75449ab747fSPaolo Bonzini     /* Sends larger than MAX_ETH_FRAME_SIZE are allowed, up to 2600 bytes. */
75549ab747fSPaolo Bonzini     uint8_t buf[2600];
75649ab747fSPaolo Bonzini     uint16_t size = 0;
75749ab747fSPaolo Bonzini     uint32_t tbd_address = s->cb_address + 0x10;
75849ab747fSPaolo Bonzini     TRACE(RXTX, logout
75949ab747fSPaolo Bonzini         ("transmit, TBD array address 0x%08x, TCB byte count 0x%04x, TBD count %u\n",
76049ab747fSPaolo Bonzini          tbd_array, tcb_bytes, s->tx.tbd_count));
76149ab747fSPaolo Bonzini 
76249ab747fSPaolo Bonzini     if (tcb_bytes > 2600) {
76349ab747fSPaolo Bonzini         logout("TCB byte count too large, using 2600\n");
76449ab747fSPaolo Bonzini         tcb_bytes = 2600;
76549ab747fSPaolo Bonzini     }
76649ab747fSPaolo Bonzini     if (!((tcb_bytes > 0) || (tbd_array != 0xffffffff))) {
76749ab747fSPaolo Bonzini         logout
76849ab747fSPaolo Bonzini             ("illegal values of TBD array address and TCB byte count!\n");
76949ab747fSPaolo Bonzini     }
77049ab747fSPaolo Bonzini     assert(tcb_bytes <= sizeof(buf));
77149ab747fSPaolo Bonzini     while (size < tcb_bytes) {
77249ab747fSPaolo Bonzini         uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev, tbd_address);
77349ab747fSPaolo Bonzini         uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev, tbd_address + 4);
77449ab747fSPaolo Bonzini #if 0
77549ab747fSPaolo Bonzini         uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev, tbd_address + 6);
77649ab747fSPaolo Bonzini #endif
77700837731SStefan Weil         if (tx_buffer_size == 0) {
77800837731SStefan Weil             /* Prevent an endless loop. */
77900837731SStefan Weil             logout("loop in %s:%u\n", __FILE__, __LINE__);
78000837731SStefan Weil             break;
78100837731SStefan Weil         }
78249ab747fSPaolo Bonzini         tbd_address += 8;
78349ab747fSPaolo Bonzini         TRACE(RXTX, logout
78449ab747fSPaolo Bonzini             ("TBD (simplified mode): buffer address 0x%08x, size 0x%04x\n",
78549ab747fSPaolo Bonzini              tx_buffer_address, tx_buffer_size));
78649ab747fSPaolo Bonzini         tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size);
78749ab747fSPaolo Bonzini         pci_dma_read(&s->dev, tx_buffer_address, &buf[size], tx_buffer_size);
78849ab747fSPaolo Bonzini         size += tx_buffer_size;
78949ab747fSPaolo Bonzini     }
79049ab747fSPaolo Bonzini     if (tbd_array == 0xffffffff) {
79149ab747fSPaolo Bonzini         /* Simplified mode. Was already handled by code above. */
79249ab747fSPaolo Bonzini     } else {
79349ab747fSPaolo Bonzini         /* Flexible mode. */
79449ab747fSPaolo Bonzini         uint8_t tbd_count = 0;
79549ab747fSPaolo Bonzini         if (s->has_extended_tcb_support && !(s->configuration[6] & BIT(4))) {
79649ab747fSPaolo Bonzini             /* Extended Flexible TCB. */
79749ab747fSPaolo Bonzini             for (; tbd_count < 2; tbd_count++) {
79849ab747fSPaolo Bonzini                 uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev,
79949ab747fSPaolo Bonzini                                                             tbd_address);
80049ab747fSPaolo Bonzini                 uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev,
80149ab747fSPaolo Bonzini                                                           tbd_address + 4);
80249ab747fSPaolo Bonzini                 uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev,
80349ab747fSPaolo Bonzini                                                         tbd_address + 6);
80449ab747fSPaolo Bonzini                 tbd_address += 8;
80549ab747fSPaolo Bonzini                 TRACE(RXTX, logout
80649ab747fSPaolo Bonzini                     ("TBD (extended flexible mode): buffer address 0x%08x, size 0x%04x\n",
80749ab747fSPaolo Bonzini                      tx_buffer_address, tx_buffer_size));
80849ab747fSPaolo Bonzini                 tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size);
80949ab747fSPaolo Bonzini                 pci_dma_read(&s->dev, tx_buffer_address,
81049ab747fSPaolo Bonzini                              &buf[size], tx_buffer_size);
81149ab747fSPaolo Bonzini                 size += tx_buffer_size;
81249ab747fSPaolo Bonzini                 if (tx_buffer_el & 1) {
81349ab747fSPaolo Bonzini                     break;
81449ab747fSPaolo Bonzini                 }
81549ab747fSPaolo Bonzini             }
81649ab747fSPaolo Bonzini         }
81749ab747fSPaolo Bonzini         tbd_address = tbd_array;
81849ab747fSPaolo Bonzini         for (; tbd_count < s->tx.tbd_count; tbd_count++) {
81949ab747fSPaolo Bonzini             uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev, tbd_address);
82049ab747fSPaolo Bonzini             uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev, tbd_address + 4);
82149ab747fSPaolo Bonzini             uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev, tbd_address + 6);
82249ab747fSPaolo Bonzini             tbd_address += 8;
82349ab747fSPaolo Bonzini             TRACE(RXTX, logout
82449ab747fSPaolo Bonzini                 ("TBD (flexible mode): buffer address 0x%08x, size 0x%04x\n",
82549ab747fSPaolo Bonzini                  tx_buffer_address, tx_buffer_size));
82649ab747fSPaolo Bonzini             tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size);
82749ab747fSPaolo Bonzini             pci_dma_read(&s->dev, tx_buffer_address,
82849ab747fSPaolo Bonzini                          &buf[size], tx_buffer_size);
82949ab747fSPaolo Bonzini             size += tx_buffer_size;
83049ab747fSPaolo Bonzini             if (tx_buffer_el & 1) {
83149ab747fSPaolo Bonzini                 break;
83249ab747fSPaolo Bonzini             }
83349ab747fSPaolo Bonzini         }
83449ab747fSPaolo Bonzini     }
83549ab747fSPaolo Bonzini     TRACE(RXTX, logout("%p sending frame, len=%d,%s\n", s, size, nic_dump(buf, size)));
83649ab747fSPaolo Bonzini     qemu_send_packet(qemu_get_queue(s->nic), buf, size);
83749ab747fSPaolo Bonzini     s->statistics.tx_good_frames++;
83849ab747fSPaolo Bonzini     /* Transmit with bad status would raise an CX/TNO interrupt.
83949ab747fSPaolo Bonzini      * (82557 only). Emulation never has bad status. */
84049ab747fSPaolo Bonzini #if 0
84149ab747fSPaolo Bonzini     eepro100_cx_interrupt(s);
84249ab747fSPaolo Bonzini #endif
84349ab747fSPaolo Bonzini }
84449ab747fSPaolo Bonzini 
84549ab747fSPaolo Bonzini static void set_multicast_list(EEPRO100State *s)
84649ab747fSPaolo Bonzini {
84749ab747fSPaolo Bonzini     uint16_t multicast_count = s->tx.tbd_array_addr & BITS(13, 0);
84849ab747fSPaolo Bonzini     uint16_t i;
84949ab747fSPaolo Bonzini     memset(&s->mult[0], 0, sizeof(s->mult));
85049ab747fSPaolo Bonzini     TRACE(OTHER, logout("multicast list, multicast count = %u\n", multicast_count));
85149ab747fSPaolo Bonzini     for (i = 0; i < multicast_count; i += 6) {
85249ab747fSPaolo Bonzini         uint8_t multicast_addr[6];
85349ab747fSPaolo Bonzini         pci_dma_read(&s->dev, s->cb_address + 10 + i, multicast_addr, 6);
85449ab747fSPaolo Bonzini         TRACE(OTHER, logout("multicast entry %s\n", nic_dump(multicast_addr, 6)));
85549ab747fSPaolo Bonzini         unsigned mcast_idx = e100_compute_mcast_idx(multicast_addr);
85649ab747fSPaolo Bonzini         assert(mcast_idx < 64);
85749ab747fSPaolo Bonzini         s->mult[mcast_idx >> 3] |= (1 << (mcast_idx & 7));
85849ab747fSPaolo Bonzini     }
85949ab747fSPaolo Bonzini }
86049ab747fSPaolo Bonzini 
86149ab747fSPaolo Bonzini static void action_command(EEPRO100State *s)
86249ab747fSPaolo Bonzini {
86300837731SStefan Weil     /* The loop below won't stop if it gets special handcrafted data.
86400837731SStefan Weil        Therefore we limit the number of iterations. */
86500837731SStefan Weil     unsigned max_loop_count = 16;
86600837731SStefan Weil 
86749ab747fSPaolo Bonzini     for (;;) {
86849ab747fSPaolo Bonzini         bool bit_el;
86949ab747fSPaolo Bonzini         bool bit_s;
87049ab747fSPaolo Bonzini         bool bit_i;
87149ab747fSPaolo Bonzini         bool bit_nc;
87249ab747fSPaolo Bonzini         uint16_t ok_status = STATUS_OK;
87349ab747fSPaolo Bonzini         s->cb_address = s->cu_base + s->cu_offset;
87449ab747fSPaolo Bonzini         read_cb(s);
87549ab747fSPaolo Bonzini         bit_el = ((s->tx.command & COMMAND_EL) != 0);
87649ab747fSPaolo Bonzini         bit_s = ((s->tx.command & COMMAND_S) != 0);
87749ab747fSPaolo Bonzini         bit_i = ((s->tx.command & COMMAND_I) != 0);
87849ab747fSPaolo Bonzini         bit_nc = ((s->tx.command & COMMAND_NC) != 0);
87949ab747fSPaolo Bonzini #if 0
88049ab747fSPaolo Bonzini         bool bit_sf = ((s->tx.command & COMMAND_SF) != 0);
88149ab747fSPaolo Bonzini #endif
88200837731SStefan Weil 
88300837731SStefan Weil         if (max_loop_count-- == 0) {
88400837731SStefan Weil             /* Prevent an endless loop. */
88500837731SStefan Weil             logout("loop in %s:%u\n", __FILE__, __LINE__);
88600837731SStefan Weil             break;
88700837731SStefan Weil         }
88800837731SStefan Weil 
88949ab747fSPaolo Bonzini         s->cu_offset = s->tx.link;
89049ab747fSPaolo Bonzini         TRACE(OTHER,
89149ab747fSPaolo Bonzini               logout("val=(cu start), status=0x%04x, command=0x%04x, link=0x%08x\n",
89249ab747fSPaolo Bonzini                      s->tx.status, s->tx.command, s->tx.link));
89349ab747fSPaolo Bonzini         switch (s->tx.command & COMMAND_CMD) {
89449ab747fSPaolo Bonzini         case CmdNOp:
89549ab747fSPaolo Bonzini             /* Do nothing. */
89649ab747fSPaolo Bonzini             break;
89749ab747fSPaolo Bonzini         case CmdIASetup:
89849ab747fSPaolo Bonzini             pci_dma_read(&s->dev, s->cb_address + 8, &s->conf.macaddr.a[0], 6);
89949ab747fSPaolo Bonzini             TRACE(OTHER, logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6)));
90049ab747fSPaolo Bonzini             break;
90149ab747fSPaolo Bonzini         case CmdConfigure:
90249ab747fSPaolo Bonzini             pci_dma_read(&s->dev, s->cb_address + 8,
90349ab747fSPaolo Bonzini                          &s->configuration[0], sizeof(s->configuration));
90449ab747fSPaolo Bonzini             TRACE(OTHER, logout("configuration: %s\n",
90549ab747fSPaolo Bonzini                                 nic_dump(&s->configuration[0], 16)));
90649ab747fSPaolo Bonzini             TRACE(OTHER, logout("configuration: %s\n",
90749ab747fSPaolo Bonzini                                 nic_dump(&s->configuration[16],
90849ab747fSPaolo Bonzini                                 ARRAY_SIZE(s->configuration) - 16)));
90949ab747fSPaolo Bonzini             if (s->configuration[20] & BIT(6)) {
91049ab747fSPaolo Bonzini                 TRACE(OTHER, logout("Multiple IA bit\n"));
91149ab747fSPaolo Bonzini             }
91249ab747fSPaolo Bonzini             break;
91349ab747fSPaolo Bonzini         case CmdMulticastList:
91449ab747fSPaolo Bonzini             set_multicast_list(s);
91549ab747fSPaolo Bonzini             break;
91649ab747fSPaolo Bonzini         case CmdTx:
91749ab747fSPaolo Bonzini             if (bit_nc) {
91849ab747fSPaolo Bonzini                 missing("CmdTx: NC = 0");
91949ab747fSPaolo Bonzini                 ok_status = 0;
92049ab747fSPaolo Bonzini                 break;
92149ab747fSPaolo Bonzini             }
92249ab747fSPaolo Bonzini             tx_command(s);
92349ab747fSPaolo Bonzini             break;
92449ab747fSPaolo Bonzini         case CmdTDR:
92549ab747fSPaolo Bonzini             TRACE(OTHER, logout("load microcode\n"));
92649ab747fSPaolo Bonzini             /* Starting with offset 8, the command contains
92749ab747fSPaolo Bonzini              * 64 dwords microcode which we just ignore here. */
92849ab747fSPaolo Bonzini             break;
92949ab747fSPaolo Bonzini         case CmdDiagnose:
93049ab747fSPaolo Bonzini             TRACE(OTHER, logout("diagnose\n"));
93149ab747fSPaolo Bonzini             /* Make sure error flag is not set. */
93249ab747fSPaolo Bonzini             s->tx.status = 0;
93349ab747fSPaolo Bonzini             break;
93449ab747fSPaolo Bonzini         default:
93549ab747fSPaolo Bonzini             missing("undefined command");
93649ab747fSPaolo Bonzini             ok_status = 0;
93749ab747fSPaolo Bonzini             break;
93849ab747fSPaolo Bonzini         }
93949ab747fSPaolo Bonzini         /* Write new status. */
94049ab747fSPaolo Bonzini         stw_le_pci_dma(&s->dev, s->cb_address,
94149ab747fSPaolo Bonzini                        s->tx.status | ok_status | STATUS_C);
94249ab747fSPaolo Bonzini         if (bit_i) {
94349ab747fSPaolo Bonzini             /* CU completed action. */
94449ab747fSPaolo Bonzini             eepro100_cx_interrupt(s);
94549ab747fSPaolo Bonzini         }
94649ab747fSPaolo Bonzini         if (bit_el) {
94749ab747fSPaolo Bonzini             /* CU becomes idle. Terminate command loop. */
94849ab747fSPaolo Bonzini             set_cu_state(s, cu_idle);
94949ab747fSPaolo Bonzini             eepro100_cna_interrupt(s);
95049ab747fSPaolo Bonzini             break;
95149ab747fSPaolo Bonzini         } else if (bit_s) {
95249ab747fSPaolo Bonzini             /* CU becomes suspended. Terminate command loop. */
95349ab747fSPaolo Bonzini             set_cu_state(s, cu_suspended);
95449ab747fSPaolo Bonzini             eepro100_cna_interrupt(s);
95549ab747fSPaolo Bonzini             break;
95649ab747fSPaolo Bonzini         } else {
95749ab747fSPaolo Bonzini             /* More entries in list. */
95849ab747fSPaolo Bonzini             TRACE(OTHER, logout("CU list with at least one more entry\n"));
95949ab747fSPaolo Bonzini         }
96049ab747fSPaolo Bonzini     }
96149ab747fSPaolo Bonzini     TRACE(OTHER, logout("CU list empty\n"));
96249ab747fSPaolo Bonzini     /* List is empty. Now CU is idle or suspended. */
96349ab747fSPaolo Bonzini }
96449ab747fSPaolo Bonzini 
96549ab747fSPaolo Bonzini static void eepro100_cu_command(EEPRO100State * s, uint8_t val)
96649ab747fSPaolo Bonzini {
96749ab747fSPaolo Bonzini     cu_state_t cu_state;
96849ab747fSPaolo Bonzini     switch (val) {
96949ab747fSPaolo Bonzini     case CU_NOP:
97049ab747fSPaolo Bonzini         /* No operation. */
97149ab747fSPaolo Bonzini         break;
97249ab747fSPaolo Bonzini     case CU_START:
97349ab747fSPaolo Bonzini         cu_state = get_cu_state(s);
97449ab747fSPaolo Bonzini         if (cu_state != cu_idle && cu_state != cu_suspended) {
97549ab747fSPaolo Bonzini             /* Intel documentation says that CU must be idle or suspended
97649ab747fSPaolo Bonzini              * for the CU start command. */
97749ab747fSPaolo Bonzini             logout("unexpected CU state is %u\n", cu_state);
97849ab747fSPaolo Bonzini         }
97949ab747fSPaolo Bonzini         set_cu_state(s, cu_active);
98049ab747fSPaolo Bonzini         s->cu_offset = e100_read_reg4(s, SCBPointer);
98149ab747fSPaolo Bonzini         action_command(s);
98249ab747fSPaolo Bonzini         break;
98349ab747fSPaolo Bonzini     case CU_RESUME:
98449ab747fSPaolo Bonzini         if (get_cu_state(s) != cu_suspended) {
98549ab747fSPaolo Bonzini             logout("bad CU resume from CU state %u\n", get_cu_state(s));
98649ab747fSPaolo Bonzini             /* Workaround for bad Linux eepro100 driver which resumes
98749ab747fSPaolo Bonzini              * from idle state. */
98849ab747fSPaolo Bonzini #if 0
98949ab747fSPaolo Bonzini             missing("cu resume");
99049ab747fSPaolo Bonzini #endif
99149ab747fSPaolo Bonzini             set_cu_state(s, cu_suspended);
99249ab747fSPaolo Bonzini         }
99349ab747fSPaolo Bonzini         if (get_cu_state(s) == cu_suspended) {
99449ab747fSPaolo Bonzini             TRACE(OTHER, logout("CU resuming\n"));
99549ab747fSPaolo Bonzini             set_cu_state(s, cu_active);
99649ab747fSPaolo Bonzini             action_command(s);
99749ab747fSPaolo Bonzini         }
99849ab747fSPaolo Bonzini         break;
99949ab747fSPaolo Bonzini     case CU_STATSADDR:
100049ab747fSPaolo Bonzini         /* Load dump counters address. */
100149ab747fSPaolo Bonzini         s->statsaddr = e100_read_reg4(s, SCBPointer);
100249ab747fSPaolo Bonzini         TRACE(OTHER, logout("val=0x%02x (dump counters address)\n", val));
100349ab747fSPaolo Bonzini         if (s->statsaddr & 3) {
100449ab747fSPaolo Bonzini             /* Memory must be Dword aligned. */
100549ab747fSPaolo Bonzini             logout("unaligned dump counters address\n");
100649ab747fSPaolo Bonzini             /* Handling of misaligned addresses is undefined.
100749ab747fSPaolo Bonzini              * Here we align the address by ignoring the lower bits. */
100849ab747fSPaolo Bonzini             /* TODO: Test unaligned dump counter address on real hardware. */
100949ab747fSPaolo Bonzini             s->statsaddr &= ~3;
101049ab747fSPaolo Bonzini         }
101149ab747fSPaolo Bonzini         break;
101249ab747fSPaolo Bonzini     case CU_SHOWSTATS:
101349ab747fSPaolo Bonzini         /* Dump statistical counters. */
101449ab747fSPaolo Bonzini         TRACE(OTHER, logout("val=0x%02x (dump stats)\n", val));
101549ab747fSPaolo Bonzini         dump_statistics(s);
101649ab747fSPaolo Bonzini         stl_le_pci_dma(&s->dev, s->statsaddr + s->stats_size, 0xa005);
101749ab747fSPaolo Bonzini         break;
101849ab747fSPaolo Bonzini     case CU_CMD_BASE:
101949ab747fSPaolo Bonzini         /* Load CU base. */
102049ab747fSPaolo Bonzini         TRACE(OTHER, logout("val=0x%02x (CU base address)\n", val));
102149ab747fSPaolo Bonzini         s->cu_base = e100_read_reg4(s, SCBPointer);
102249ab747fSPaolo Bonzini         break;
102349ab747fSPaolo Bonzini     case CU_DUMPSTATS:
102449ab747fSPaolo Bonzini         /* Dump and reset statistical counters. */
102549ab747fSPaolo Bonzini         TRACE(OTHER, logout("val=0x%02x (dump stats and reset)\n", val));
102649ab747fSPaolo Bonzini         dump_statistics(s);
102749ab747fSPaolo Bonzini         stl_le_pci_dma(&s->dev, s->statsaddr + s->stats_size, 0xa007);
102849ab747fSPaolo Bonzini         memset(&s->statistics, 0, sizeof(s->statistics));
102949ab747fSPaolo Bonzini         break;
103049ab747fSPaolo Bonzini     case CU_SRESUME:
103149ab747fSPaolo Bonzini         /* CU static resume. */
103249ab747fSPaolo Bonzini         missing("CU static resume");
103349ab747fSPaolo Bonzini         break;
103449ab747fSPaolo Bonzini     default:
103549ab747fSPaolo Bonzini         missing("Undefined CU command");
103649ab747fSPaolo Bonzini     }
103749ab747fSPaolo Bonzini }
103849ab747fSPaolo Bonzini 
103949ab747fSPaolo Bonzini static void eepro100_ru_command(EEPRO100State * s, uint8_t val)
104049ab747fSPaolo Bonzini {
104149ab747fSPaolo Bonzini     switch (val) {
104249ab747fSPaolo Bonzini     case RU_NOP:
104349ab747fSPaolo Bonzini         /* No operation. */
104449ab747fSPaolo Bonzini         break;
104549ab747fSPaolo Bonzini     case RX_START:
104649ab747fSPaolo Bonzini         /* RU start. */
104749ab747fSPaolo Bonzini         if (get_ru_state(s) != ru_idle) {
104849ab747fSPaolo Bonzini             logout("RU state is %u, should be %u\n", get_ru_state(s), ru_idle);
104949ab747fSPaolo Bonzini #if 0
105049ab747fSPaolo Bonzini             assert(!"wrong RU state");
105149ab747fSPaolo Bonzini #endif
105249ab747fSPaolo Bonzini         }
105349ab747fSPaolo Bonzini         set_ru_state(s, ru_ready);
105449ab747fSPaolo Bonzini         s->ru_offset = e100_read_reg4(s, SCBPointer);
105549ab747fSPaolo Bonzini         qemu_flush_queued_packets(qemu_get_queue(s->nic));
105649ab747fSPaolo Bonzini         TRACE(OTHER, logout("val=0x%02x (rx start)\n", val));
105749ab747fSPaolo Bonzini         break;
105849ab747fSPaolo Bonzini     case RX_RESUME:
105949ab747fSPaolo Bonzini         /* Restart RU. */
106049ab747fSPaolo Bonzini         if (get_ru_state(s) != ru_suspended) {
106149ab747fSPaolo Bonzini             logout("RU state is %u, should be %u\n", get_ru_state(s),
106249ab747fSPaolo Bonzini                    ru_suspended);
106349ab747fSPaolo Bonzini #if 0
106449ab747fSPaolo Bonzini             assert(!"wrong RU state");
106549ab747fSPaolo Bonzini #endif
106649ab747fSPaolo Bonzini         }
106749ab747fSPaolo Bonzini         set_ru_state(s, ru_ready);
106849ab747fSPaolo Bonzini         break;
106949ab747fSPaolo Bonzini     case RU_ABORT:
107049ab747fSPaolo Bonzini         /* RU abort. */
107149ab747fSPaolo Bonzini         if (get_ru_state(s) == ru_ready) {
107249ab747fSPaolo Bonzini             eepro100_rnr_interrupt(s);
107349ab747fSPaolo Bonzini         }
107449ab747fSPaolo Bonzini         set_ru_state(s, ru_idle);
107549ab747fSPaolo Bonzini         break;
107649ab747fSPaolo Bonzini     case RX_ADDR_LOAD:
107749ab747fSPaolo Bonzini         /* Load RU base. */
107849ab747fSPaolo Bonzini         TRACE(OTHER, logout("val=0x%02x (RU base address)\n", val));
107949ab747fSPaolo Bonzini         s->ru_base = e100_read_reg4(s, SCBPointer);
108049ab747fSPaolo Bonzini         break;
108149ab747fSPaolo Bonzini     default:
108249ab747fSPaolo Bonzini         logout("val=0x%02x (undefined RU command)\n", val);
108349ab747fSPaolo Bonzini         missing("Undefined SU command");
108449ab747fSPaolo Bonzini     }
108549ab747fSPaolo Bonzini }
108649ab747fSPaolo Bonzini 
108749ab747fSPaolo Bonzini static void eepro100_write_command(EEPRO100State * s, uint8_t val)
108849ab747fSPaolo Bonzini {
108949ab747fSPaolo Bonzini     eepro100_ru_command(s, val & 0x0f);
109049ab747fSPaolo Bonzini     eepro100_cu_command(s, val & 0xf0);
109149ab747fSPaolo Bonzini     if ((val) == 0) {
109249ab747fSPaolo Bonzini         TRACE(OTHER, logout("val=0x%02x\n", val));
109349ab747fSPaolo Bonzini     }
109449ab747fSPaolo Bonzini     /* Clear command byte after command was accepted. */
109549ab747fSPaolo Bonzini     s->mem[SCBCmd] = 0;
109649ab747fSPaolo Bonzini }
109749ab747fSPaolo Bonzini 
109849ab747fSPaolo Bonzini /*****************************************************************************
109949ab747fSPaolo Bonzini  *
110049ab747fSPaolo Bonzini  * EEPROM emulation.
110149ab747fSPaolo Bonzini  *
110249ab747fSPaolo Bonzini  ****************************************************************************/
110349ab747fSPaolo Bonzini 
110449ab747fSPaolo Bonzini #define EEPROM_CS       0x02
110549ab747fSPaolo Bonzini #define EEPROM_SK       0x01
110649ab747fSPaolo Bonzini #define EEPROM_DI       0x04
110749ab747fSPaolo Bonzini #define EEPROM_DO       0x08
110849ab747fSPaolo Bonzini 
110949ab747fSPaolo Bonzini static uint16_t eepro100_read_eeprom(EEPRO100State * s)
111049ab747fSPaolo Bonzini {
111149ab747fSPaolo Bonzini     uint16_t val = e100_read_reg2(s, SCBeeprom);
111249ab747fSPaolo Bonzini     if (eeprom93xx_read(s->eeprom)) {
111349ab747fSPaolo Bonzini         val |= EEPROM_DO;
111449ab747fSPaolo Bonzini     } else {
111549ab747fSPaolo Bonzini         val &= ~EEPROM_DO;
111649ab747fSPaolo Bonzini     }
111749ab747fSPaolo Bonzini     TRACE(EEPROM, logout("val=0x%04x\n", val));
111849ab747fSPaolo Bonzini     return val;
111949ab747fSPaolo Bonzini }
112049ab747fSPaolo Bonzini 
112149ab747fSPaolo Bonzini static void eepro100_write_eeprom(eeprom_t * eeprom, uint8_t val)
112249ab747fSPaolo Bonzini {
112349ab747fSPaolo Bonzini     TRACE(EEPROM, logout("val=0x%02x\n", val));
112449ab747fSPaolo Bonzini 
112549ab747fSPaolo Bonzini     /* mask unwritable bits */
112649ab747fSPaolo Bonzini #if 0
112749ab747fSPaolo Bonzini     val = SET_MASKED(val, 0x31, eeprom->value);
112849ab747fSPaolo Bonzini #endif
112949ab747fSPaolo Bonzini 
113049ab747fSPaolo Bonzini     int eecs = ((val & EEPROM_CS) != 0);
113149ab747fSPaolo Bonzini     int eesk = ((val & EEPROM_SK) != 0);
113249ab747fSPaolo Bonzini     int eedi = ((val & EEPROM_DI) != 0);
113349ab747fSPaolo Bonzini     eeprom93xx_write(eeprom, eecs, eesk, eedi);
113449ab747fSPaolo Bonzini }
113549ab747fSPaolo Bonzini 
113649ab747fSPaolo Bonzini /*****************************************************************************
113749ab747fSPaolo Bonzini  *
113849ab747fSPaolo Bonzini  * MDI emulation.
113949ab747fSPaolo Bonzini  *
114049ab747fSPaolo Bonzini  ****************************************************************************/
114149ab747fSPaolo Bonzini 
114249ab747fSPaolo Bonzini #if defined(DEBUG_EEPRO100)
114349ab747fSPaolo Bonzini static const char * const mdi_op_name[] = {
114449ab747fSPaolo Bonzini     "opcode 0",
114549ab747fSPaolo Bonzini     "write",
114649ab747fSPaolo Bonzini     "read",
114749ab747fSPaolo Bonzini     "opcode 3"
114849ab747fSPaolo Bonzini };
114949ab747fSPaolo Bonzini 
115049ab747fSPaolo Bonzini static const char * const mdi_reg_name[] = {
115149ab747fSPaolo Bonzini     "Control",
115249ab747fSPaolo Bonzini     "Status",
115349ab747fSPaolo Bonzini     "PHY Identification (Word 1)",
115449ab747fSPaolo Bonzini     "PHY Identification (Word 2)",
115549ab747fSPaolo Bonzini     "Auto-Negotiation Advertisement",
115649ab747fSPaolo Bonzini     "Auto-Negotiation Link Partner Ability",
115749ab747fSPaolo Bonzini     "Auto-Negotiation Expansion"
115849ab747fSPaolo Bonzini };
115949ab747fSPaolo Bonzini 
116049ab747fSPaolo Bonzini static const char *reg2name(uint8_t reg)
116149ab747fSPaolo Bonzini {
116249ab747fSPaolo Bonzini     static char buffer[10];
116349ab747fSPaolo Bonzini     const char *p = buffer;
116449ab747fSPaolo Bonzini     if (reg < ARRAY_SIZE(mdi_reg_name)) {
116549ab747fSPaolo Bonzini         p = mdi_reg_name[reg];
116649ab747fSPaolo Bonzini     } else {
116749ab747fSPaolo Bonzini         snprintf(buffer, sizeof(buffer), "reg=0x%02x", reg);
116849ab747fSPaolo Bonzini     }
116949ab747fSPaolo Bonzini     return p;
117049ab747fSPaolo Bonzini }
117149ab747fSPaolo Bonzini #endif                          /* DEBUG_EEPRO100 */
117249ab747fSPaolo Bonzini 
117349ab747fSPaolo Bonzini static uint32_t eepro100_read_mdi(EEPRO100State * s)
117449ab747fSPaolo Bonzini {
117549ab747fSPaolo Bonzini     uint32_t val = e100_read_reg4(s, SCBCtrlMDI);
117649ab747fSPaolo Bonzini 
117749ab747fSPaolo Bonzini #ifdef DEBUG_EEPRO100
117849ab747fSPaolo Bonzini     uint8_t raiseint = (val & BIT(29)) >> 29;
117949ab747fSPaolo Bonzini     uint8_t opcode = (val & BITS(27, 26)) >> 26;
118049ab747fSPaolo Bonzini     uint8_t phy = (val & BITS(25, 21)) >> 21;
118149ab747fSPaolo Bonzini     uint8_t reg = (val & BITS(20, 16)) >> 16;
118249ab747fSPaolo Bonzini     uint16_t data = (val & BITS(15, 0));
118349ab747fSPaolo Bonzini #endif
118449ab747fSPaolo Bonzini     /* Emulation takes no time to finish MDI transaction. */
118549ab747fSPaolo Bonzini     val |= BIT(28);
118649ab747fSPaolo Bonzini     TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
118749ab747fSPaolo Bonzini                       val, raiseint, mdi_op_name[opcode], phy,
118849ab747fSPaolo Bonzini                       reg2name(reg), data));
118949ab747fSPaolo Bonzini     return val;
119049ab747fSPaolo Bonzini }
119149ab747fSPaolo Bonzini 
119249ab747fSPaolo Bonzini static void eepro100_write_mdi(EEPRO100State *s)
119349ab747fSPaolo Bonzini {
119449ab747fSPaolo Bonzini     uint32_t val = e100_read_reg4(s, SCBCtrlMDI);
119549ab747fSPaolo Bonzini     uint8_t raiseint = (val & BIT(29)) >> 29;
119649ab747fSPaolo Bonzini     uint8_t opcode = (val & BITS(27, 26)) >> 26;
119749ab747fSPaolo Bonzini     uint8_t phy = (val & BITS(25, 21)) >> 21;
119849ab747fSPaolo Bonzini     uint8_t reg = (val & BITS(20, 16)) >> 16;
119949ab747fSPaolo Bonzini     uint16_t data = (val & BITS(15, 0));
120049ab747fSPaolo Bonzini     TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
120149ab747fSPaolo Bonzini           val, raiseint, mdi_op_name[opcode], phy, reg2name(reg), data));
120249ab747fSPaolo Bonzini     if (phy != 1) {
120349ab747fSPaolo Bonzini         /* Unsupported PHY address. */
120449ab747fSPaolo Bonzini #if 0
120549ab747fSPaolo Bonzini         logout("phy must be 1 but is %u\n", phy);
120649ab747fSPaolo Bonzini #endif
120749ab747fSPaolo Bonzini         data = 0;
120849ab747fSPaolo Bonzini     } else if (opcode != 1 && opcode != 2) {
120949ab747fSPaolo Bonzini         /* Unsupported opcode. */
121049ab747fSPaolo Bonzini         logout("opcode must be 1 or 2 but is %u\n", opcode);
121149ab747fSPaolo Bonzini         data = 0;
121249ab747fSPaolo Bonzini     } else if (reg > 6) {
121349ab747fSPaolo Bonzini         /* Unsupported register. */
121449ab747fSPaolo Bonzini         logout("register must be 0...6 but is %u\n", reg);
121549ab747fSPaolo Bonzini         data = 0;
121649ab747fSPaolo Bonzini     } else {
121749ab747fSPaolo Bonzini         TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
121849ab747fSPaolo Bonzini                           val, raiseint, mdi_op_name[opcode], phy,
121949ab747fSPaolo Bonzini                           reg2name(reg), data));
122049ab747fSPaolo Bonzini         if (opcode == 1) {
122149ab747fSPaolo Bonzini             /* MDI write */
122249ab747fSPaolo Bonzini             switch (reg) {
122349ab747fSPaolo Bonzini             case 0:            /* Control Register */
122449ab747fSPaolo Bonzini                 if (data & 0x8000) {
122549ab747fSPaolo Bonzini                     /* Reset status and control registers to default. */
122649ab747fSPaolo Bonzini                     s->mdimem[0] = eepro100_mdi_default[0];
122749ab747fSPaolo Bonzini                     s->mdimem[1] = eepro100_mdi_default[1];
122849ab747fSPaolo Bonzini                     data = s->mdimem[reg];
122949ab747fSPaolo Bonzini                 } else {
123049ab747fSPaolo Bonzini                     /* Restart Auto Configuration = Normal Operation */
123149ab747fSPaolo Bonzini                     data &= ~0x0200;
123249ab747fSPaolo Bonzini                 }
123349ab747fSPaolo Bonzini                 break;
123449ab747fSPaolo Bonzini             case 1:            /* Status Register */
123549ab747fSPaolo Bonzini                 missing("not writable");
123649ab747fSPaolo Bonzini                 break;
123749ab747fSPaolo Bonzini             case 2:            /* PHY Identification Register (Word 1) */
123849ab747fSPaolo Bonzini             case 3:            /* PHY Identification Register (Word 2) */
123949ab747fSPaolo Bonzini                 missing("not implemented");
124049ab747fSPaolo Bonzini                 break;
124149ab747fSPaolo Bonzini             case 4:            /* Auto-Negotiation Advertisement Register */
124249ab747fSPaolo Bonzini             case 5:            /* Auto-Negotiation Link Partner Ability Register */
124349ab747fSPaolo Bonzini                 break;
124449ab747fSPaolo Bonzini             case 6:            /* Auto-Negotiation Expansion Register */
124549ab747fSPaolo Bonzini             default:
124649ab747fSPaolo Bonzini                 missing("not implemented");
124749ab747fSPaolo Bonzini             }
12485e80dd22SPeter Maydell             s->mdimem[reg] &= eepro100_mdi_mask[reg];
12495e80dd22SPeter Maydell             s->mdimem[reg] |= data & ~eepro100_mdi_mask[reg];
125049ab747fSPaolo Bonzini         } else if (opcode == 2) {
125149ab747fSPaolo Bonzini             /* MDI read */
125249ab747fSPaolo Bonzini             switch (reg) {
125349ab747fSPaolo Bonzini             case 0:            /* Control Register */
125449ab747fSPaolo Bonzini                 if (data & 0x8000) {
125549ab747fSPaolo Bonzini                     /* Reset status and control registers to default. */
125649ab747fSPaolo Bonzini                     s->mdimem[0] = eepro100_mdi_default[0];
125749ab747fSPaolo Bonzini                     s->mdimem[1] = eepro100_mdi_default[1];
125849ab747fSPaolo Bonzini                 }
125949ab747fSPaolo Bonzini                 break;
126049ab747fSPaolo Bonzini             case 1:            /* Status Register */
126149ab747fSPaolo Bonzini                 s->mdimem[reg] |= 0x0020;
126249ab747fSPaolo Bonzini                 break;
126349ab747fSPaolo Bonzini             case 2:            /* PHY Identification Register (Word 1) */
126449ab747fSPaolo Bonzini             case 3:            /* PHY Identification Register (Word 2) */
126549ab747fSPaolo Bonzini             case 4:            /* Auto-Negotiation Advertisement Register */
126649ab747fSPaolo Bonzini                 break;
126749ab747fSPaolo Bonzini             case 5:            /* Auto-Negotiation Link Partner Ability Register */
126849ab747fSPaolo Bonzini                 s->mdimem[reg] = 0x41fe;
126949ab747fSPaolo Bonzini                 break;
127049ab747fSPaolo Bonzini             case 6:            /* Auto-Negotiation Expansion Register */
127149ab747fSPaolo Bonzini                 s->mdimem[reg] = 0x0001;
127249ab747fSPaolo Bonzini                 break;
127349ab747fSPaolo Bonzini             }
127449ab747fSPaolo Bonzini             data = s->mdimem[reg];
127549ab747fSPaolo Bonzini         }
127649ab747fSPaolo Bonzini         /* Emulation takes no time to finish MDI transaction.
127749ab747fSPaolo Bonzini          * Set MDI bit in SCB status register. */
127849ab747fSPaolo Bonzini         s->mem[SCBAck] |= 0x08;
127949ab747fSPaolo Bonzini         val |= BIT(28);
128049ab747fSPaolo Bonzini         if (raiseint) {
128149ab747fSPaolo Bonzini             eepro100_mdi_interrupt(s);
128249ab747fSPaolo Bonzini         }
128349ab747fSPaolo Bonzini     }
128449ab747fSPaolo Bonzini     val = (val & 0xffff0000) + data;
128549ab747fSPaolo Bonzini     e100_write_reg4(s, SCBCtrlMDI, val);
128649ab747fSPaolo Bonzini }
128749ab747fSPaolo Bonzini 
128849ab747fSPaolo Bonzini /*****************************************************************************
128949ab747fSPaolo Bonzini  *
129049ab747fSPaolo Bonzini  * Port emulation.
129149ab747fSPaolo Bonzini  *
129249ab747fSPaolo Bonzini  ****************************************************************************/
129349ab747fSPaolo Bonzini 
129449ab747fSPaolo Bonzini #define PORT_SOFTWARE_RESET     0
129549ab747fSPaolo Bonzini #define PORT_SELFTEST           1
129649ab747fSPaolo Bonzini #define PORT_SELECTIVE_RESET    2
129749ab747fSPaolo Bonzini #define PORT_DUMP               3
129849ab747fSPaolo Bonzini #define PORT_SELECTION_MASK     3
129949ab747fSPaolo Bonzini 
130049ab747fSPaolo Bonzini typedef struct {
130149ab747fSPaolo Bonzini     uint32_t st_sign;           /* Self Test Signature */
130249ab747fSPaolo Bonzini     uint32_t st_result;         /* Self Test Results */
130349ab747fSPaolo Bonzini } eepro100_selftest_t;
130449ab747fSPaolo Bonzini 
130549ab747fSPaolo Bonzini static uint32_t eepro100_read_port(EEPRO100State * s)
130649ab747fSPaolo Bonzini {
130749ab747fSPaolo Bonzini     return 0;
130849ab747fSPaolo Bonzini }
130949ab747fSPaolo Bonzini 
131049ab747fSPaolo Bonzini static void eepro100_write_port(EEPRO100State *s)
131149ab747fSPaolo Bonzini {
131249ab747fSPaolo Bonzini     uint32_t val = e100_read_reg4(s, SCBPort);
131349ab747fSPaolo Bonzini     uint32_t address = (val & ~PORT_SELECTION_MASK);
131449ab747fSPaolo Bonzini     uint8_t selection = (val & PORT_SELECTION_MASK);
131549ab747fSPaolo Bonzini     switch (selection) {
131649ab747fSPaolo Bonzini     case PORT_SOFTWARE_RESET:
131749ab747fSPaolo Bonzini         nic_reset(s);
131849ab747fSPaolo Bonzini         break;
131949ab747fSPaolo Bonzini     case PORT_SELFTEST:
132049ab747fSPaolo Bonzini         TRACE(OTHER, logout("selftest address=0x%08x\n", address));
132149ab747fSPaolo Bonzini         eepro100_selftest_t data;
132249ab747fSPaolo Bonzini         pci_dma_read(&s->dev, address, (uint8_t *) &data, sizeof(data));
132349ab747fSPaolo Bonzini         data.st_sign = 0xffffffff;
132449ab747fSPaolo Bonzini         data.st_result = 0;
132549ab747fSPaolo Bonzini         pci_dma_write(&s->dev, address, (uint8_t *) &data, sizeof(data));
132649ab747fSPaolo Bonzini         break;
132749ab747fSPaolo Bonzini     case PORT_SELECTIVE_RESET:
132849ab747fSPaolo Bonzini         TRACE(OTHER, logout("selective reset, selftest address=0x%08x\n", address));
132949ab747fSPaolo Bonzini         nic_selective_reset(s);
133049ab747fSPaolo Bonzini         break;
133149ab747fSPaolo Bonzini     default:
133249ab747fSPaolo Bonzini         logout("val=0x%08x\n", val);
133349ab747fSPaolo Bonzini         missing("unknown port selection");
133449ab747fSPaolo Bonzini     }
133549ab747fSPaolo Bonzini }
133649ab747fSPaolo Bonzini 
133749ab747fSPaolo Bonzini /*****************************************************************************
133849ab747fSPaolo Bonzini  *
133949ab747fSPaolo Bonzini  * General hardware emulation.
134049ab747fSPaolo Bonzini  *
134149ab747fSPaolo Bonzini  ****************************************************************************/
134249ab747fSPaolo Bonzini 
134349ab747fSPaolo Bonzini static uint8_t eepro100_read1(EEPRO100State * s, uint32_t addr)
134449ab747fSPaolo Bonzini {
134549ab747fSPaolo Bonzini     uint8_t val = 0;
134649ab747fSPaolo Bonzini     if (addr <= sizeof(s->mem) - sizeof(val)) {
134749ab747fSPaolo Bonzini         val = s->mem[addr];
134849ab747fSPaolo Bonzini     }
134949ab747fSPaolo Bonzini 
135049ab747fSPaolo Bonzini     switch (addr) {
135149ab747fSPaolo Bonzini     case SCBStatus:
135249ab747fSPaolo Bonzini     case SCBAck:
135349ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
135449ab747fSPaolo Bonzini         break;
135549ab747fSPaolo Bonzini     case SCBCmd:
135649ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
135749ab747fSPaolo Bonzini #if 0
135849ab747fSPaolo Bonzini         val = eepro100_read_command(s);
135949ab747fSPaolo Bonzini #endif
136049ab747fSPaolo Bonzini         break;
136149ab747fSPaolo Bonzini     case SCBIntmask:
136249ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
136349ab747fSPaolo Bonzini         break;
136449ab747fSPaolo Bonzini     case SCBPort + 3:
136549ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
136649ab747fSPaolo Bonzini         break;
136749ab747fSPaolo Bonzini     case SCBeeprom:
136849ab747fSPaolo Bonzini         val = eepro100_read_eeprom(s);
136949ab747fSPaolo Bonzini         break;
137049ab747fSPaolo Bonzini     case SCBCtrlMDI:
137149ab747fSPaolo Bonzini     case SCBCtrlMDI + 1:
137249ab747fSPaolo Bonzini     case SCBCtrlMDI + 2:
137349ab747fSPaolo Bonzini     case SCBCtrlMDI + 3:
137449ab747fSPaolo Bonzini         val = (uint8_t)(eepro100_read_mdi(s) >> (8 * (addr & 3)));
137549ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
137649ab747fSPaolo Bonzini         break;
137749ab747fSPaolo Bonzini     case SCBpmdr:       /* Power Management Driver Register */
137849ab747fSPaolo Bonzini         val = 0;
137949ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
138049ab747fSPaolo Bonzini         break;
138149ab747fSPaolo Bonzini     case SCBgctrl:      /* General Control Register */
138249ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
138349ab747fSPaolo Bonzini         break;
138449ab747fSPaolo Bonzini     case SCBgstat:      /* General Status Register */
138549ab747fSPaolo Bonzini         /* 100 Mbps full duplex, valid link */
138649ab747fSPaolo Bonzini         val = 0x07;
138749ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=General Status val=%02x\n", val));
138849ab747fSPaolo Bonzini         break;
138949ab747fSPaolo Bonzini     default:
139049ab747fSPaolo Bonzini         logout("addr=%s val=0x%02x\n", regname(addr), val);
139149ab747fSPaolo Bonzini         missing("unknown byte read");
139249ab747fSPaolo Bonzini     }
139349ab747fSPaolo Bonzini     return val;
139449ab747fSPaolo Bonzini }
139549ab747fSPaolo Bonzini 
139649ab747fSPaolo Bonzini static uint16_t eepro100_read2(EEPRO100State * s, uint32_t addr)
139749ab747fSPaolo Bonzini {
139849ab747fSPaolo Bonzini     uint16_t val = 0;
139949ab747fSPaolo Bonzini     if (addr <= sizeof(s->mem) - sizeof(val)) {
140049ab747fSPaolo Bonzini         val = e100_read_reg2(s, addr);
140149ab747fSPaolo Bonzini     }
140249ab747fSPaolo Bonzini 
140349ab747fSPaolo Bonzini     switch (addr) {
140449ab747fSPaolo Bonzini     case SCBStatus:
140549ab747fSPaolo Bonzini     case SCBCmd:
140649ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
140749ab747fSPaolo Bonzini         break;
140849ab747fSPaolo Bonzini     case SCBeeprom:
140949ab747fSPaolo Bonzini         val = eepro100_read_eeprom(s);
141049ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
141149ab747fSPaolo Bonzini         break;
141249ab747fSPaolo Bonzini     case SCBCtrlMDI:
141349ab747fSPaolo Bonzini     case SCBCtrlMDI + 2:
141449ab747fSPaolo Bonzini         val = (uint16_t)(eepro100_read_mdi(s) >> (8 * (addr & 3)));
141549ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
141649ab747fSPaolo Bonzini         break;
141749ab747fSPaolo Bonzini     default:
141849ab747fSPaolo Bonzini         logout("addr=%s val=0x%04x\n", regname(addr), val);
141949ab747fSPaolo Bonzini         missing("unknown word read");
142049ab747fSPaolo Bonzini     }
142149ab747fSPaolo Bonzini     return val;
142249ab747fSPaolo Bonzini }
142349ab747fSPaolo Bonzini 
142449ab747fSPaolo Bonzini static uint32_t eepro100_read4(EEPRO100State * s, uint32_t addr)
142549ab747fSPaolo Bonzini {
142649ab747fSPaolo Bonzini     uint32_t val = 0;
142749ab747fSPaolo Bonzini     if (addr <= sizeof(s->mem) - sizeof(val)) {
142849ab747fSPaolo Bonzini         val = e100_read_reg4(s, addr);
142949ab747fSPaolo Bonzini     }
143049ab747fSPaolo Bonzini 
143149ab747fSPaolo Bonzini     switch (addr) {
143249ab747fSPaolo Bonzini     case SCBStatus:
143349ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
143449ab747fSPaolo Bonzini         break;
143549ab747fSPaolo Bonzini     case SCBPointer:
143649ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
143749ab747fSPaolo Bonzini         break;
143849ab747fSPaolo Bonzini     case SCBPort:
143949ab747fSPaolo Bonzini         val = eepro100_read_port(s);
144049ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
144149ab747fSPaolo Bonzini         break;
144249ab747fSPaolo Bonzini     case SCBflash:
144349ab747fSPaolo Bonzini         val = eepro100_read_eeprom(s);
144449ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
144549ab747fSPaolo Bonzini         break;
144649ab747fSPaolo Bonzini     case SCBCtrlMDI:
144749ab747fSPaolo Bonzini         val = eepro100_read_mdi(s);
144849ab747fSPaolo Bonzini         break;
144949ab747fSPaolo Bonzini     default:
145049ab747fSPaolo Bonzini         logout("addr=%s val=0x%08x\n", regname(addr), val);
145149ab747fSPaolo Bonzini         missing("unknown longword read");
145249ab747fSPaolo Bonzini     }
145349ab747fSPaolo Bonzini     return val;
145449ab747fSPaolo Bonzini }
145549ab747fSPaolo Bonzini 
145649ab747fSPaolo Bonzini static void eepro100_write1(EEPRO100State * s, uint32_t addr, uint8_t val)
145749ab747fSPaolo Bonzini {
145849ab747fSPaolo Bonzini     /* SCBStatus is readonly. */
145949ab747fSPaolo Bonzini     if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) {
146049ab747fSPaolo Bonzini         s->mem[addr] = val;
146149ab747fSPaolo Bonzini     }
146249ab747fSPaolo Bonzini 
146349ab747fSPaolo Bonzini     switch (addr) {
146449ab747fSPaolo Bonzini     case SCBStatus:
146549ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
146649ab747fSPaolo Bonzini         break;
146749ab747fSPaolo Bonzini     case SCBAck:
146849ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
146949ab747fSPaolo Bonzini         eepro100_acknowledge(s);
147049ab747fSPaolo Bonzini         break;
147149ab747fSPaolo Bonzini     case SCBCmd:
147249ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
147349ab747fSPaolo Bonzini         eepro100_write_command(s, val);
147449ab747fSPaolo Bonzini         break;
147549ab747fSPaolo Bonzini     case SCBIntmask:
147649ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
147749ab747fSPaolo Bonzini         if (val & BIT(1)) {
147849ab747fSPaolo Bonzini             eepro100_swi_interrupt(s);
147949ab747fSPaolo Bonzini         }
148049ab747fSPaolo Bonzini         eepro100_interrupt(s, 0);
148149ab747fSPaolo Bonzini         break;
148249ab747fSPaolo Bonzini     case SCBPointer:
148349ab747fSPaolo Bonzini     case SCBPointer + 1:
148449ab747fSPaolo Bonzini     case SCBPointer + 2:
148549ab747fSPaolo Bonzini     case SCBPointer + 3:
148649ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
148749ab747fSPaolo Bonzini         break;
148849ab747fSPaolo Bonzini     case SCBPort:
148949ab747fSPaolo Bonzini     case SCBPort + 1:
149049ab747fSPaolo Bonzini     case SCBPort + 2:
149149ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
149249ab747fSPaolo Bonzini         break;
149349ab747fSPaolo Bonzini     case SCBPort + 3:
149449ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
149549ab747fSPaolo Bonzini         eepro100_write_port(s);
149649ab747fSPaolo Bonzini         break;
149749ab747fSPaolo Bonzini     case SCBFlow:       /* does not exist on 82557 */
149849ab747fSPaolo Bonzini     case SCBFlow + 1:
149949ab747fSPaolo Bonzini     case SCBFlow + 2:
150049ab747fSPaolo Bonzini     case SCBpmdr:       /* does not exist on 82557 */
150149ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
150249ab747fSPaolo Bonzini         break;
150349ab747fSPaolo Bonzini     case SCBeeprom:
150449ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
150549ab747fSPaolo Bonzini         eepro100_write_eeprom(s->eeprom, val);
150649ab747fSPaolo Bonzini         break;
150749ab747fSPaolo Bonzini     case SCBCtrlMDI:
150849ab747fSPaolo Bonzini     case SCBCtrlMDI + 1:
150949ab747fSPaolo Bonzini     case SCBCtrlMDI + 2:
151049ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
151149ab747fSPaolo Bonzini         break;
151249ab747fSPaolo Bonzini     case SCBCtrlMDI + 3:
151349ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
151449ab747fSPaolo Bonzini         eepro100_write_mdi(s);
151549ab747fSPaolo Bonzini         break;
151649ab747fSPaolo Bonzini     default:
151749ab747fSPaolo Bonzini         logout("addr=%s val=0x%02x\n", regname(addr), val);
151849ab747fSPaolo Bonzini         missing("unknown byte write");
151949ab747fSPaolo Bonzini     }
152049ab747fSPaolo Bonzini }
152149ab747fSPaolo Bonzini 
152249ab747fSPaolo Bonzini static void eepro100_write2(EEPRO100State * s, uint32_t addr, uint16_t val)
152349ab747fSPaolo Bonzini {
152449ab747fSPaolo Bonzini     /* SCBStatus is readonly. */
152549ab747fSPaolo Bonzini     if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) {
152649ab747fSPaolo Bonzini         e100_write_reg2(s, addr, val);
152749ab747fSPaolo Bonzini     }
152849ab747fSPaolo Bonzini 
152949ab747fSPaolo Bonzini     switch (addr) {
153049ab747fSPaolo Bonzini     case SCBStatus:
153149ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
153249ab747fSPaolo Bonzini         s->mem[SCBAck] = (val >> 8);
153349ab747fSPaolo Bonzini         eepro100_acknowledge(s);
153449ab747fSPaolo Bonzini         break;
153549ab747fSPaolo Bonzini     case SCBCmd:
153649ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
153749ab747fSPaolo Bonzini         eepro100_write_command(s, val);
153849ab747fSPaolo Bonzini         eepro100_write1(s, SCBIntmask, val >> 8);
153949ab747fSPaolo Bonzini         break;
154049ab747fSPaolo Bonzini     case SCBPointer:
154149ab747fSPaolo Bonzini     case SCBPointer + 2:
154249ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
154349ab747fSPaolo Bonzini         break;
154449ab747fSPaolo Bonzini     case SCBPort:
154549ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
154649ab747fSPaolo Bonzini         break;
154749ab747fSPaolo Bonzini     case SCBPort + 2:
154849ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
154949ab747fSPaolo Bonzini         eepro100_write_port(s);
155049ab747fSPaolo Bonzini         break;
155149ab747fSPaolo Bonzini     case SCBeeprom:
155249ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
155349ab747fSPaolo Bonzini         eepro100_write_eeprom(s->eeprom, val);
155449ab747fSPaolo Bonzini         break;
155549ab747fSPaolo Bonzini     case SCBCtrlMDI:
155649ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
155749ab747fSPaolo Bonzini         break;
155849ab747fSPaolo Bonzini     case SCBCtrlMDI + 2:
155949ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
156049ab747fSPaolo Bonzini         eepro100_write_mdi(s);
156149ab747fSPaolo Bonzini         break;
156249ab747fSPaolo Bonzini     default:
156349ab747fSPaolo Bonzini         logout("addr=%s val=0x%04x\n", regname(addr), val);
156449ab747fSPaolo Bonzini         missing("unknown word write");
156549ab747fSPaolo Bonzini     }
156649ab747fSPaolo Bonzini }
156749ab747fSPaolo Bonzini 
156849ab747fSPaolo Bonzini static void eepro100_write4(EEPRO100State * s, uint32_t addr, uint32_t val)
156949ab747fSPaolo Bonzini {
157049ab747fSPaolo Bonzini     if (addr <= sizeof(s->mem) - sizeof(val)) {
157149ab747fSPaolo Bonzini         e100_write_reg4(s, addr, val);
157249ab747fSPaolo Bonzini     }
157349ab747fSPaolo Bonzini 
157449ab747fSPaolo Bonzini     switch (addr) {
157549ab747fSPaolo Bonzini     case SCBPointer:
157649ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
157749ab747fSPaolo Bonzini         break;
157849ab747fSPaolo Bonzini     case SCBPort:
157949ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
158049ab747fSPaolo Bonzini         eepro100_write_port(s);
158149ab747fSPaolo Bonzini         break;
158249ab747fSPaolo Bonzini     case SCBflash:
158349ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
158449ab747fSPaolo Bonzini         val = val >> 16;
158549ab747fSPaolo Bonzini         eepro100_write_eeprom(s->eeprom, val);
158649ab747fSPaolo Bonzini         break;
158749ab747fSPaolo Bonzini     case SCBCtrlMDI:
158849ab747fSPaolo Bonzini         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
158949ab747fSPaolo Bonzini         eepro100_write_mdi(s);
159049ab747fSPaolo Bonzini         break;
159149ab747fSPaolo Bonzini     default:
159249ab747fSPaolo Bonzini         logout("addr=%s val=0x%08x\n", regname(addr), val);
159349ab747fSPaolo Bonzini         missing("unknown longword write");
159449ab747fSPaolo Bonzini     }
159549ab747fSPaolo Bonzini }
159649ab747fSPaolo Bonzini 
159749ab747fSPaolo Bonzini static uint64_t eepro100_read(void *opaque, hwaddr addr,
159849ab747fSPaolo Bonzini                               unsigned size)
159949ab747fSPaolo Bonzini {
160049ab747fSPaolo Bonzini     EEPRO100State *s = opaque;
160149ab747fSPaolo Bonzini 
160249ab747fSPaolo Bonzini     switch (size) {
160349ab747fSPaolo Bonzini     case 1: return eepro100_read1(s, addr);
160449ab747fSPaolo Bonzini     case 2: return eepro100_read2(s, addr);
160549ab747fSPaolo Bonzini     case 4: return eepro100_read4(s, addr);
160649ab747fSPaolo Bonzini     default: abort();
160749ab747fSPaolo Bonzini     }
160849ab747fSPaolo Bonzini }
160949ab747fSPaolo Bonzini 
161049ab747fSPaolo Bonzini static void eepro100_write(void *opaque, hwaddr addr,
161149ab747fSPaolo Bonzini                            uint64_t data, unsigned size)
161249ab747fSPaolo Bonzini {
161349ab747fSPaolo Bonzini     EEPRO100State *s = opaque;
161449ab747fSPaolo Bonzini 
161549ab747fSPaolo Bonzini     switch (size) {
161649ab747fSPaolo Bonzini     case 1:
161749ab747fSPaolo Bonzini         eepro100_write1(s, addr, data);
161849ab747fSPaolo Bonzini         break;
161949ab747fSPaolo Bonzini     case 2:
162049ab747fSPaolo Bonzini         eepro100_write2(s, addr, data);
162149ab747fSPaolo Bonzini         break;
162249ab747fSPaolo Bonzini     case 4:
162349ab747fSPaolo Bonzini         eepro100_write4(s, addr, data);
162449ab747fSPaolo Bonzini         break;
162549ab747fSPaolo Bonzini     default:
162649ab747fSPaolo Bonzini         abort();
162749ab747fSPaolo Bonzini     }
162849ab747fSPaolo Bonzini }
162949ab747fSPaolo Bonzini 
163049ab747fSPaolo Bonzini static const MemoryRegionOps eepro100_ops = {
163149ab747fSPaolo Bonzini     .read = eepro100_read,
163249ab747fSPaolo Bonzini     .write = eepro100_write,
163349ab747fSPaolo Bonzini     .endianness = DEVICE_LITTLE_ENDIAN,
163449ab747fSPaolo Bonzini };
163549ab747fSPaolo Bonzini 
163649ab747fSPaolo Bonzini static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
163749ab747fSPaolo Bonzini {
163849ab747fSPaolo Bonzini     /* TODO:
163949ab747fSPaolo Bonzini      * - Magic packets should set bit 30 in power management driver register.
164049ab747fSPaolo Bonzini      * - Interesting packets should set bit 29 in power management driver register.
164149ab747fSPaolo Bonzini      */
164249ab747fSPaolo Bonzini     EEPRO100State *s = qemu_get_nic_opaque(nc);
164349ab747fSPaolo Bonzini     uint16_t rfd_status = 0xa000;
164449ab747fSPaolo Bonzini #if defined(CONFIG_PAD_RECEIVED_FRAMES)
164549ab747fSPaolo Bonzini     uint8_t min_buf[60];
164649ab747fSPaolo Bonzini #endif
164749ab747fSPaolo Bonzini     static const uint8_t broadcast_macaddr[6] =
164849ab747fSPaolo Bonzini         { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
164949ab747fSPaolo Bonzini 
165049ab747fSPaolo Bonzini #if defined(CONFIG_PAD_RECEIVED_FRAMES)
165149ab747fSPaolo Bonzini     /* Pad to minimum Ethernet frame length */
165249ab747fSPaolo Bonzini     if (size < sizeof(min_buf)) {
165349ab747fSPaolo Bonzini         memcpy(min_buf, buf, size);
165449ab747fSPaolo Bonzini         memset(&min_buf[size], 0, sizeof(min_buf) - size);
165549ab747fSPaolo Bonzini         buf = min_buf;
165649ab747fSPaolo Bonzini         size = sizeof(min_buf);
165749ab747fSPaolo Bonzini     }
165849ab747fSPaolo Bonzini #endif
165949ab747fSPaolo Bonzini 
166049ab747fSPaolo Bonzini     if (s->configuration[8] & 0x80) {
166149ab747fSPaolo Bonzini         /* CSMA is disabled. */
166249ab747fSPaolo Bonzini         logout("%p received while CSMA is disabled\n", s);
166349ab747fSPaolo Bonzini         return -1;
166449ab747fSPaolo Bonzini #if !defined(CONFIG_PAD_RECEIVED_FRAMES)
166549ab747fSPaolo Bonzini     } else if (size < 64 && (s->configuration[7] & BIT(0))) {
166649ab747fSPaolo Bonzini         /* Short frame and configuration byte 7/0 (discard short receive) set:
166749ab747fSPaolo Bonzini          * Short frame is discarded */
166849ab747fSPaolo Bonzini         logout("%p received short frame (%zu byte)\n", s, size);
166949ab747fSPaolo Bonzini         s->statistics.rx_short_frame_errors++;
167049ab747fSPaolo Bonzini         return -1;
167149ab747fSPaolo Bonzini #endif
167249ab747fSPaolo Bonzini     } else if ((size > MAX_ETH_FRAME_SIZE + 4) && !(s->configuration[18] & BIT(3))) {
167349ab747fSPaolo Bonzini         /* Long frame and configuration byte 18/3 (long receive ok) not set:
167449ab747fSPaolo Bonzini          * Long frames are discarded. */
167549ab747fSPaolo Bonzini         logout("%p received long frame (%zu byte), ignored\n", s, size);
167649ab747fSPaolo Bonzini         return -1;
167749ab747fSPaolo Bonzini     } else if (memcmp(buf, s->conf.macaddr.a, 6) == 0) {       /* !!! */
167849ab747fSPaolo Bonzini         /* Frame matches individual address. */
167949ab747fSPaolo Bonzini         /* TODO: check configuration byte 15/4 (ignore U/L). */
168049ab747fSPaolo Bonzini         TRACE(RXTX, logout("%p received frame for me, len=%zu\n", s, size));
168149ab747fSPaolo Bonzini     } else if (memcmp(buf, broadcast_macaddr, 6) == 0) {
168249ab747fSPaolo Bonzini         /* Broadcast frame. */
168349ab747fSPaolo Bonzini         TRACE(RXTX, logout("%p received broadcast, len=%zu\n", s, size));
168449ab747fSPaolo Bonzini         rfd_status |= 0x0002;
168549ab747fSPaolo Bonzini     } else if (buf[0] & 0x01) {
168649ab747fSPaolo Bonzini         /* Multicast frame. */
168749ab747fSPaolo Bonzini         TRACE(RXTX, logout("%p received multicast, len=%zu,%s\n", s, size, nic_dump(buf, size)));
168849ab747fSPaolo Bonzini         if (s->configuration[21] & BIT(3)) {
168949ab747fSPaolo Bonzini           /* Multicast all bit is set, receive all multicast frames. */
169049ab747fSPaolo Bonzini         } else {
169149ab747fSPaolo Bonzini           unsigned mcast_idx = e100_compute_mcast_idx(buf);
169249ab747fSPaolo Bonzini           assert(mcast_idx < 64);
169349ab747fSPaolo Bonzini           if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) {
169449ab747fSPaolo Bonzini             /* Multicast frame is allowed in hash table. */
169549ab747fSPaolo Bonzini           } else if (s->configuration[15] & BIT(0)) {
169649ab747fSPaolo Bonzini               /* Promiscuous: receive all. */
169749ab747fSPaolo Bonzini               rfd_status |= 0x0004;
169849ab747fSPaolo Bonzini           } else {
169949ab747fSPaolo Bonzini               TRACE(RXTX, logout("%p multicast ignored\n", s));
170049ab747fSPaolo Bonzini               return -1;
170149ab747fSPaolo Bonzini           }
170249ab747fSPaolo Bonzini         }
170349ab747fSPaolo Bonzini         /* TODO: Next not for promiscuous mode? */
170449ab747fSPaolo Bonzini         rfd_status |= 0x0002;
170549ab747fSPaolo Bonzini     } else if (s->configuration[15] & BIT(0)) {
170649ab747fSPaolo Bonzini         /* Promiscuous: receive all. */
170749ab747fSPaolo Bonzini         TRACE(RXTX, logout("%p received frame in promiscuous mode, len=%zu\n", s, size));
170849ab747fSPaolo Bonzini         rfd_status |= 0x0004;
170949ab747fSPaolo Bonzini     } else if (s->configuration[20] & BIT(6)) {
171049ab747fSPaolo Bonzini         /* Multiple IA bit set. */
171149ab747fSPaolo Bonzini         unsigned mcast_idx = compute_mcast_idx(buf);
171249ab747fSPaolo Bonzini         assert(mcast_idx < 64);
171349ab747fSPaolo Bonzini         if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) {
171449ab747fSPaolo Bonzini             TRACE(RXTX, logout("%p accepted, multiple IA bit set\n", s));
171549ab747fSPaolo Bonzini         } else {
171649ab747fSPaolo Bonzini             TRACE(RXTX, logout("%p frame ignored, multiple IA bit set\n", s));
171749ab747fSPaolo Bonzini             return -1;
171849ab747fSPaolo Bonzini         }
171949ab747fSPaolo Bonzini     } else {
172049ab747fSPaolo Bonzini         TRACE(RXTX, logout("%p received frame, ignored, len=%zu,%s\n", s, size,
172149ab747fSPaolo Bonzini               nic_dump(buf, size)));
172249ab747fSPaolo Bonzini         return size;
172349ab747fSPaolo Bonzini     }
172449ab747fSPaolo Bonzini 
172549ab747fSPaolo Bonzini     if (get_ru_state(s) != ru_ready) {
172649ab747fSPaolo Bonzini         /* No resources available. */
172749ab747fSPaolo Bonzini         logout("no resources, state=%u\n", get_ru_state(s));
172849ab747fSPaolo Bonzini         /* TODO: RNR interrupt only at first failed frame? */
172949ab747fSPaolo Bonzini         eepro100_rnr_interrupt(s);
173049ab747fSPaolo Bonzini         s->statistics.rx_resource_errors++;
173149ab747fSPaolo Bonzini #if 0
173249ab747fSPaolo Bonzini         assert(!"no resources");
173349ab747fSPaolo Bonzini #endif
173449ab747fSPaolo Bonzini         return -1;
173549ab747fSPaolo Bonzini     }
173649ab747fSPaolo Bonzini     /* !!! */
173749ab747fSPaolo Bonzini     eepro100_rx_t rx;
173849ab747fSPaolo Bonzini     pci_dma_read(&s->dev, s->ru_base + s->ru_offset,
173949ab747fSPaolo Bonzini                  &rx, sizeof(eepro100_rx_t));
174049ab747fSPaolo Bonzini     uint16_t rfd_command = le16_to_cpu(rx.command);
174149ab747fSPaolo Bonzini     uint16_t rfd_size = le16_to_cpu(rx.size);
174249ab747fSPaolo Bonzini 
174349ab747fSPaolo Bonzini     if (size > rfd_size) {
174449ab747fSPaolo Bonzini         logout("Receive buffer (%" PRId16 " bytes) too small for data "
174549ab747fSPaolo Bonzini             "(%zu bytes); data truncated\n", rfd_size, size);
174649ab747fSPaolo Bonzini         size = rfd_size;
174749ab747fSPaolo Bonzini     }
174849ab747fSPaolo Bonzini #if !defined(CONFIG_PAD_RECEIVED_FRAMES)
174949ab747fSPaolo Bonzini     if (size < 64) {
175049ab747fSPaolo Bonzini         rfd_status |= 0x0080;
175149ab747fSPaolo Bonzini     }
175249ab747fSPaolo Bonzini #endif
175349ab747fSPaolo Bonzini     TRACE(OTHER, logout("command 0x%04x, link 0x%08x, addr 0x%08x, size %u\n",
175449ab747fSPaolo Bonzini           rfd_command, rx.link, rx.rx_buf_addr, rfd_size));
175549ab747fSPaolo Bonzini     stw_le_pci_dma(&s->dev, s->ru_base + s->ru_offset +
175649ab747fSPaolo Bonzini                 offsetof(eepro100_rx_t, status), rfd_status);
175749ab747fSPaolo Bonzini     stw_le_pci_dma(&s->dev, s->ru_base + s->ru_offset +
175849ab747fSPaolo Bonzini                 offsetof(eepro100_rx_t, count), size);
175949ab747fSPaolo Bonzini     /* Early receive interrupt not supported. */
176049ab747fSPaolo Bonzini #if 0
176149ab747fSPaolo Bonzini     eepro100_er_interrupt(s);
176249ab747fSPaolo Bonzini #endif
176349ab747fSPaolo Bonzini     /* Receive CRC Transfer not supported. */
176449ab747fSPaolo Bonzini     if (s->configuration[18] & BIT(2)) {
176549ab747fSPaolo Bonzini         missing("Receive CRC Transfer");
176649ab747fSPaolo Bonzini         return -1;
176749ab747fSPaolo Bonzini     }
176849ab747fSPaolo Bonzini     /* TODO: check stripping enable bit. */
176949ab747fSPaolo Bonzini #if 0
177049ab747fSPaolo Bonzini     assert(!(s->configuration[17] & BIT(0)));
177149ab747fSPaolo Bonzini #endif
177249ab747fSPaolo Bonzini     pci_dma_write(&s->dev, s->ru_base + s->ru_offset +
177349ab747fSPaolo Bonzini                   sizeof(eepro100_rx_t), buf, size);
177449ab747fSPaolo Bonzini     s->statistics.rx_good_frames++;
177549ab747fSPaolo Bonzini     eepro100_fr_interrupt(s);
177649ab747fSPaolo Bonzini     s->ru_offset = le32_to_cpu(rx.link);
177749ab747fSPaolo Bonzini     if (rfd_command & COMMAND_EL) {
177849ab747fSPaolo Bonzini         /* EL bit is set, so this was the last frame. */
177949ab747fSPaolo Bonzini         logout("receive: Running out of frames\n");
178049ab747fSPaolo Bonzini         set_ru_state(s, ru_no_resources);
178149ab747fSPaolo Bonzini         eepro100_rnr_interrupt(s);
178249ab747fSPaolo Bonzini     }
178349ab747fSPaolo Bonzini     if (rfd_command & COMMAND_S) {
178449ab747fSPaolo Bonzini         /* S bit is set. */
178549ab747fSPaolo Bonzini         set_ru_state(s, ru_suspended);
178649ab747fSPaolo Bonzini     }
178749ab747fSPaolo Bonzini     return size;
178849ab747fSPaolo Bonzini }
178949ab747fSPaolo Bonzini 
179049ab747fSPaolo Bonzini static const VMStateDescription vmstate_eepro100 = {
179149ab747fSPaolo Bonzini     .version_id = 3,
179249ab747fSPaolo Bonzini     .minimum_version_id = 2,
179349ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
179449ab747fSPaolo Bonzini         VMSTATE_PCI_DEVICE(dev, EEPRO100State),
179549ab747fSPaolo Bonzini         VMSTATE_UNUSED(32),
179649ab747fSPaolo Bonzini         VMSTATE_BUFFER(mult, EEPRO100State),
179749ab747fSPaolo Bonzini         VMSTATE_BUFFER(mem, EEPRO100State),
179849ab747fSPaolo Bonzini         /* Save all members of struct between scb_stat and mem. */
179949ab747fSPaolo Bonzini         VMSTATE_UINT8(scb_stat, EEPRO100State),
180049ab747fSPaolo Bonzini         VMSTATE_UINT8(int_stat, EEPRO100State),
180149ab747fSPaolo Bonzini         VMSTATE_UNUSED(3*4),
180249ab747fSPaolo Bonzini         VMSTATE_MACADDR(conf.macaddr, EEPRO100State),
180349ab747fSPaolo Bonzini         VMSTATE_UNUSED(19*4),
180449ab747fSPaolo Bonzini         VMSTATE_UINT16_ARRAY(mdimem, EEPRO100State, 32),
180549ab747fSPaolo Bonzini         /* The eeprom should be saved and restored by its own routines. */
180649ab747fSPaolo Bonzini         VMSTATE_UINT32(device, EEPRO100State),
180749ab747fSPaolo Bonzini         /* TODO check device. */
180849ab747fSPaolo Bonzini         VMSTATE_UINT32(cu_base, EEPRO100State),
180949ab747fSPaolo Bonzini         VMSTATE_UINT32(cu_offset, EEPRO100State),
181049ab747fSPaolo Bonzini         VMSTATE_UINT32(ru_base, EEPRO100State),
181149ab747fSPaolo Bonzini         VMSTATE_UINT32(ru_offset, EEPRO100State),
181249ab747fSPaolo Bonzini         VMSTATE_UINT32(statsaddr, EEPRO100State),
181349ab747fSPaolo Bonzini         /* Save eepro100_stats_t statistics. */
181449ab747fSPaolo Bonzini         VMSTATE_UINT32(statistics.tx_good_frames, EEPRO100State),
181549ab747fSPaolo Bonzini         VMSTATE_UINT32(statistics.tx_max_collisions, EEPRO100State),
181649ab747fSPaolo Bonzini         VMSTATE_UINT32(statistics.tx_late_collisions, EEPRO100State),
181749ab747fSPaolo Bonzini         VMSTATE_UINT32(statistics.tx_underruns, EEPRO100State),
181849ab747fSPaolo Bonzini         VMSTATE_UINT32(statistics.tx_lost_crs, EEPRO100State),
181949ab747fSPaolo Bonzini         VMSTATE_UINT32(statistics.tx_deferred, EEPRO100State),
182049ab747fSPaolo Bonzini         VMSTATE_UINT32(statistics.tx_single_collisions, EEPRO100State),
182149ab747fSPaolo Bonzini         VMSTATE_UINT32(statistics.tx_multiple_collisions, EEPRO100State),
182249ab747fSPaolo Bonzini         VMSTATE_UINT32(statistics.tx_total_collisions, EEPRO100State),
182349ab747fSPaolo Bonzini         VMSTATE_UINT32(statistics.rx_good_frames, EEPRO100State),
182449ab747fSPaolo Bonzini         VMSTATE_UINT32(statistics.rx_crc_errors, EEPRO100State),
182549ab747fSPaolo Bonzini         VMSTATE_UINT32(statistics.rx_alignment_errors, EEPRO100State),
182649ab747fSPaolo Bonzini         VMSTATE_UINT32(statistics.rx_resource_errors, EEPRO100State),
182749ab747fSPaolo Bonzini         VMSTATE_UINT32(statistics.rx_overrun_errors, EEPRO100State),
182849ab747fSPaolo Bonzini         VMSTATE_UINT32(statistics.rx_cdt_errors, EEPRO100State),
182949ab747fSPaolo Bonzini         VMSTATE_UINT32(statistics.rx_short_frame_errors, EEPRO100State),
183049ab747fSPaolo Bonzini         VMSTATE_UINT32(statistics.fc_xmt_pause, EEPRO100State),
183149ab747fSPaolo Bonzini         VMSTATE_UINT32(statistics.fc_rcv_pause, EEPRO100State),
183249ab747fSPaolo Bonzini         VMSTATE_UINT32(statistics.fc_rcv_unsupported, EEPRO100State),
183349ab747fSPaolo Bonzini         VMSTATE_UINT16(statistics.xmt_tco_frames, EEPRO100State),
183449ab747fSPaolo Bonzini         VMSTATE_UINT16(statistics.rcv_tco_frames, EEPRO100State),
183549ab747fSPaolo Bonzini         /* Configuration bytes. */
183649ab747fSPaolo Bonzini         VMSTATE_BUFFER(configuration, EEPRO100State),
183749ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
183849ab747fSPaolo Bonzini     }
183949ab747fSPaolo Bonzini };
184049ab747fSPaolo Bonzini 
184149ab747fSPaolo Bonzini static void pci_nic_uninit(PCIDevice *pci_dev)
184249ab747fSPaolo Bonzini {
184349ab747fSPaolo Bonzini     EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev);
184449ab747fSPaolo Bonzini 
184549ab747fSPaolo Bonzini     vmstate_unregister(&pci_dev->qdev, s->vmstate, s);
184649ab747fSPaolo Bonzini     eeprom93xx_free(&pci_dev->qdev, s->eeprom);
184749ab747fSPaolo Bonzini     qemu_del_nic(s->nic);
184849ab747fSPaolo Bonzini }
184949ab747fSPaolo Bonzini 
185049ab747fSPaolo Bonzini static NetClientInfo net_eepro100_info = {
1851*f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
185249ab747fSPaolo Bonzini     .size = sizeof(NICState),
185349ab747fSPaolo Bonzini     .receive = nic_receive,
185449ab747fSPaolo Bonzini };
185549ab747fSPaolo Bonzini 
18569af21dbeSMarkus Armbruster static void e100_nic_realize(PCIDevice *pci_dev, Error **errp)
185749ab747fSPaolo Bonzini {
185849ab747fSPaolo Bonzini     EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev);
185949ab747fSPaolo Bonzini     E100PCIDeviceInfo *info = eepro100_get_class(s);
186049ab747fSPaolo Bonzini 
186149ab747fSPaolo Bonzini     TRACE(OTHER, logout("\n"));
186249ab747fSPaolo Bonzini 
186349ab747fSPaolo Bonzini     s->device = info->device;
186449ab747fSPaolo Bonzini 
186549ab747fSPaolo Bonzini     e100_pci_reset(s);
186649ab747fSPaolo Bonzini 
186749ab747fSPaolo Bonzini     /* Add 64 * 2 EEPROM. i82557 and i82558 support a 64 word EEPROM,
186849ab747fSPaolo Bonzini      * i82559 and later support 64 or 256 word EEPROM. */
186949ab747fSPaolo Bonzini     s->eeprom = eeprom93xx_new(&pci_dev->qdev, EEPROM_SIZE);
187049ab747fSPaolo Bonzini 
187149ab747fSPaolo Bonzini     /* Handler for memory-mapped I/O */
1872eedfac6fSPaolo Bonzini     memory_region_init_io(&s->mmio_bar, OBJECT(s), &eepro100_ops, s,
1873eedfac6fSPaolo Bonzini                           "eepro100-mmio", PCI_MEM_SIZE);
187449ab747fSPaolo Bonzini     pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->mmio_bar);
1875eedfac6fSPaolo Bonzini     memory_region_init_io(&s->io_bar, OBJECT(s), &eepro100_ops, s,
1876eedfac6fSPaolo Bonzini                           "eepro100-io", PCI_IO_SIZE);
187749ab747fSPaolo Bonzini     pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar);
187849ab747fSPaolo Bonzini     /* FIXME: flash aliases to mmio?! */
1879eedfac6fSPaolo Bonzini     memory_region_init_io(&s->flash_bar, OBJECT(s), &eepro100_ops, s,
1880eedfac6fSPaolo Bonzini                           "eepro100-flash", PCI_FLASH_SIZE);
188149ab747fSPaolo Bonzini     pci_register_bar(&s->dev, 2, 0, &s->flash_bar);
188249ab747fSPaolo Bonzini 
188349ab747fSPaolo Bonzini     qemu_macaddr_default_if_unset(&s->conf.macaddr);
188449ab747fSPaolo Bonzini     logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6));
188549ab747fSPaolo Bonzini 
188649ab747fSPaolo Bonzini     nic_reset(s);
188749ab747fSPaolo Bonzini 
188849ab747fSPaolo Bonzini     s->nic = qemu_new_nic(&net_eepro100_info, &s->conf,
188949ab747fSPaolo Bonzini                           object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s);
189049ab747fSPaolo Bonzini 
189149ab747fSPaolo Bonzini     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
189249ab747fSPaolo Bonzini     TRACE(OTHER, logout("%s\n", qemu_get_queue(s->nic)->info_str));
189349ab747fSPaolo Bonzini 
189449ab747fSPaolo Bonzini     qemu_register_reset(nic_reset, s);
189549ab747fSPaolo Bonzini 
189649ab747fSPaolo Bonzini     s->vmstate = g_malloc(sizeof(vmstate_eepro100));
189749ab747fSPaolo Bonzini     memcpy(s->vmstate, &vmstate_eepro100, sizeof(vmstate_eepro100));
189849ab747fSPaolo Bonzini     s->vmstate->name = qemu_get_queue(s->nic)->model;
189949ab747fSPaolo Bonzini     vmstate_register(&pci_dev->qdev, -1, s->vmstate, s);
190049ab747fSPaolo Bonzini }
190149ab747fSPaolo Bonzini 
19027317bb17SGonglei static void eepro100_instance_init(Object *obj)
19037317bb17SGonglei {
19047317bb17SGonglei     EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, PCI_DEVICE(obj));
19057317bb17SGonglei     device_add_bootindex_property(obj, &s->conf.bootindex,
19067317bb17SGonglei                                   "bootindex", "/ethernet-phy@0",
19077317bb17SGonglei                                   DEVICE(s), NULL);
19087317bb17SGonglei }
19097317bb17SGonglei 
191049ab747fSPaolo Bonzini static E100PCIDeviceInfo e100_devices[] = {
191149ab747fSPaolo Bonzini     {
191249ab747fSPaolo Bonzini         .name = "i82550",
191349ab747fSPaolo Bonzini         .desc = "Intel i82550 Ethernet",
191449ab747fSPaolo Bonzini         .device = i82550,
191549ab747fSPaolo Bonzini         /* TODO: check device id. */
191649ab747fSPaolo Bonzini         .device_id = PCI_DEVICE_ID_INTEL_82551IT,
191749ab747fSPaolo Bonzini         /* Revision ID: 0x0c, 0x0d, 0x0e. */
191849ab747fSPaolo Bonzini         .revision = 0x0e,
191949ab747fSPaolo Bonzini         /* TODO: check size of statistical counters. */
192049ab747fSPaolo Bonzini         .stats_size = 80,
192149ab747fSPaolo Bonzini         /* TODO: check extended tcb support. */
192249ab747fSPaolo Bonzini         .has_extended_tcb_support = true,
192349ab747fSPaolo Bonzini         .power_management = true,
192449ab747fSPaolo Bonzini     },{
192549ab747fSPaolo Bonzini         .name = "i82551",
192649ab747fSPaolo Bonzini         .desc = "Intel i82551 Ethernet",
192749ab747fSPaolo Bonzini         .device = i82551,
192849ab747fSPaolo Bonzini         .device_id = PCI_DEVICE_ID_INTEL_82551IT,
192949ab747fSPaolo Bonzini         /* Revision ID: 0x0f, 0x10. */
193049ab747fSPaolo Bonzini         .revision = 0x0f,
193149ab747fSPaolo Bonzini         /* TODO: check size of statistical counters. */
193249ab747fSPaolo Bonzini         .stats_size = 80,
193349ab747fSPaolo Bonzini         .has_extended_tcb_support = true,
193449ab747fSPaolo Bonzini         .power_management = true,
193549ab747fSPaolo Bonzini     },{
193649ab747fSPaolo Bonzini         .name = "i82557a",
193749ab747fSPaolo Bonzini         .desc = "Intel i82557A Ethernet",
193849ab747fSPaolo Bonzini         .device = i82557A,
193949ab747fSPaolo Bonzini         .device_id = PCI_DEVICE_ID_INTEL_82557,
194049ab747fSPaolo Bonzini         .revision = 0x01,
194149ab747fSPaolo Bonzini         .power_management = false,
194249ab747fSPaolo Bonzini     },{
194349ab747fSPaolo Bonzini         .name = "i82557b",
194449ab747fSPaolo Bonzini         .desc = "Intel i82557B Ethernet",
194549ab747fSPaolo Bonzini         .device = i82557B,
194649ab747fSPaolo Bonzini         .device_id = PCI_DEVICE_ID_INTEL_82557,
194749ab747fSPaolo Bonzini         .revision = 0x02,
194849ab747fSPaolo Bonzini         .power_management = false,
194949ab747fSPaolo Bonzini     },{
195049ab747fSPaolo Bonzini         .name = "i82557c",
195149ab747fSPaolo Bonzini         .desc = "Intel i82557C Ethernet",
195249ab747fSPaolo Bonzini         .device = i82557C,
195349ab747fSPaolo Bonzini         .device_id = PCI_DEVICE_ID_INTEL_82557,
195449ab747fSPaolo Bonzini         .revision = 0x03,
195549ab747fSPaolo Bonzini         .power_management = false,
195649ab747fSPaolo Bonzini     },{
195749ab747fSPaolo Bonzini         .name = "i82558a",
195849ab747fSPaolo Bonzini         .desc = "Intel i82558A Ethernet",
195949ab747fSPaolo Bonzini         .device = i82558A,
196049ab747fSPaolo Bonzini         .device_id = PCI_DEVICE_ID_INTEL_82557,
196149ab747fSPaolo Bonzini         .revision = 0x04,
196249ab747fSPaolo Bonzini         .stats_size = 76,
196349ab747fSPaolo Bonzini         .has_extended_tcb_support = true,
196449ab747fSPaolo Bonzini         .power_management = true,
196549ab747fSPaolo Bonzini     },{
196649ab747fSPaolo Bonzini         .name = "i82558b",
196749ab747fSPaolo Bonzini         .desc = "Intel i82558B Ethernet",
196849ab747fSPaolo Bonzini         .device = i82558B,
196949ab747fSPaolo Bonzini         .device_id = PCI_DEVICE_ID_INTEL_82557,
197049ab747fSPaolo Bonzini         .revision = 0x05,
197149ab747fSPaolo Bonzini         .stats_size = 76,
197249ab747fSPaolo Bonzini         .has_extended_tcb_support = true,
197349ab747fSPaolo Bonzini         .power_management = true,
197449ab747fSPaolo Bonzini     },{
197549ab747fSPaolo Bonzini         .name = "i82559a",
197649ab747fSPaolo Bonzini         .desc = "Intel i82559A Ethernet",
197749ab747fSPaolo Bonzini         .device = i82559A,
197849ab747fSPaolo Bonzini         .device_id = PCI_DEVICE_ID_INTEL_82557,
197949ab747fSPaolo Bonzini         .revision = 0x06,
198049ab747fSPaolo Bonzini         .stats_size = 80,
198149ab747fSPaolo Bonzini         .has_extended_tcb_support = true,
198249ab747fSPaolo Bonzini         .power_management = true,
198349ab747fSPaolo Bonzini     },{
198449ab747fSPaolo Bonzini         .name = "i82559b",
198549ab747fSPaolo Bonzini         .desc = "Intel i82559B Ethernet",
198649ab747fSPaolo Bonzini         .device = i82559B,
198749ab747fSPaolo Bonzini         .device_id = PCI_DEVICE_ID_INTEL_82557,
198849ab747fSPaolo Bonzini         .revision = 0x07,
198949ab747fSPaolo Bonzini         .stats_size = 80,
199049ab747fSPaolo Bonzini         .has_extended_tcb_support = true,
199149ab747fSPaolo Bonzini         .power_management = true,
199249ab747fSPaolo Bonzini     },{
199349ab747fSPaolo Bonzini         .name = "i82559c",
199449ab747fSPaolo Bonzini         .desc = "Intel i82559C Ethernet",
199549ab747fSPaolo Bonzini         .device = i82559C,
199649ab747fSPaolo Bonzini         .device_id = PCI_DEVICE_ID_INTEL_82557,
199749ab747fSPaolo Bonzini #if 0
199849ab747fSPaolo Bonzini         .revision = 0x08,
199949ab747fSPaolo Bonzini #endif
200049ab747fSPaolo Bonzini         /* TODO: Windows wants revision id 0x0c. */
200149ab747fSPaolo Bonzini         .revision = 0x0c,
200249ab747fSPaolo Bonzini #if EEPROM_SIZE > 0
200349ab747fSPaolo Bonzini         .subsystem_vendor_id = PCI_VENDOR_ID_INTEL,
200449ab747fSPaolo Bonzini         .subsystem_id = 0x0040,
200549ab747fSPaolo Bonzini #endif
200649ab747fSPaolo Bonzini         .stats_size = 80,
200749ab747fSPaolo Bonzini         .has_extended_tcb_support = true,
200849ab747fSPaolo Bonzini         .power_management = true,
200949ab747fSPaolo Bonzini     },{
201049ab747fSPaolo Bonzini         .name = "i82559er",
201149ab747fSPaolo Bonzini         .desc = "Intel i82559ER Ethernet",
201249ab747fSPaolo Bonzini         .device = i82559ER,
201349ab747fSPaolo Bonzini         .device_id = PCI_DEVICE_ID_INTEL_82551IT,
201449ab747fSPaolo Bonzini         .revision = 0x09,
201549ab747fSPaolo Bonzini         .stats_size = 80,
201649ab747fSPaolo Bonzini         .has_extended_tcb_support = true,
201749ab747fSPaolo Bonzini         .power_management = true,
201849ab747fSPaolo Bonzini     },{
201949ab747fSPaolo Bonzini         .name = "i82562",
202049ab747fSPaolo Bonzini         .desc = "Intel i82562 Ethernet",
202149ab747fSPaolo Bonzini         .device = i82562,
202249ab747fSPaolo Bonzini         /* TODO: check device id. */
202349ab747fSPaolo Bonzini         .device_id = PCI_DEVICE_ID_INTEL_82551IT,
202449ab747fSPaolo Bonzini         /* TODO: wrong revision id. */
202549ab747fSPaolo Bonzini         .revision = 0x0e,
202649ab747fSPaolo Bonzini         .stats_size = 80,
202749ab747fSPaolo Bonzini         .has_extended_tcb_support = true,
202849ab747fSPaolo Bonzini         .power_management = true,
202949ab747fSPaolo Bonzini     },{
203049ab747fSPaolo Bonzini         /* Toshiba Tecra 8200. */
203149ab747fSPaolo Bonzini         .name = "i82801",
203249ab747fSPaolo Bonzini         .desc = "Intel i82801 Ethernet",
203349ab747fSPaolo Bonzini         .device = i82801,
203449ab747fSPaolo Bonzini         .device_id = 0x2449,
203549ab747fSPaolo Bonzini         .revision = 0x03,
203649ab747fSPaolo Bonzini         .stats_size = 80,
203749ab747fSPaolo Bonzini         .has_extended_tcb_support = true,
203849ab747fSPaolo Bonzini         .power_management = true,
203949ab747fSPaolo Bonzini     }
204049ab747fSPaolo Bonzini };
204149ab747fSPaolo Bonzini 
204249ab747fSPaolo Bonzini static E100PCIDeviceInfo *eepro100_get_class_by_name(const char *typename)
204349ab747fSPaolo Bonzini {
204449ab747fSPaolo Bonzini     E100PCIDeviceInfo *info = NULL;
204549ab747fSPaolo Bonzini     int i;
204649ab747fSPaolo Bonzini 
204749ab747fSPaolo Bonzini     /* This is admittedly awkward but also temporary.  QOM allows for
204849ab747fSPaolo Bonzini      * parameterized typing and for subclassing both of which would suitable
204949ab747fSPaolo Bonzini      * handle what's going on here.  But class_data is already being used as
205049ab747fSPaolo Bonzini      * a stop-gap hack to allow incremental qdev conversion so we cannot use it
205149ab747fSPaolo Bonzini      * right now.  Once we merge the final QOM series, we can come back here and
205249ab747fSPaolo Bonzini      * do this in a much more elegant fashion.
205349ab747fSPaolo Bonzini      */
205449ab747fSPaolo Bonzini     for (i = 0; i < ARRAY_SIZE(e100_devices); i++) {
205549ab747fSPaolo Bonzini         if (strcmp(e100_devices[i].name, typename) == 0) {
205649ab747fSPaolo Bonzini             info = &e100_devices[i];
205749ab747fSPaolo Bonzini             break;
205849ab747fSPaolo Bonzini         }
205949ab747fSPaolo Bonzini     }
206049ab747fSPaolo Bonzini     assert(info != NULL);
206149ab747fSPaolo Bonzini 
206249ab747fSPaolo Bonzini     return info;
206349ab747fSPaolo Bonzini }
206449ab747fSPaolo Bonzini 
206549ab747fSPaolo Bonzini static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s)
206649ab747fSPaolo Bonzini {
206749ab747fSPaolo Bonzini     return eepro100_get_class_by_name(object_get_typename(OBJECT(s)));
206849ab747fSPaolo Bonzini }
206949ab747fSPaolo Bonzini 
207049ab747fSPaolo Bonzini static Property e100_properties[] = {
207149ab747fSPaolo Bonzini     DEFINE_NIC_PROPERTIES(EEPRO100State, conf),
207249ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
207349ab747fSPaolo Bonzini };
207449ab747fSPaolo Bonzini 
207549ab747fSPaolo Bonzini static void eepro100_class_init(ObjectClass *klass, void *data)
207649ab747fSPaolo Bonzini {
207749ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
207849ab747fSPaolo Bonzini     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
207949ab747fSPaolo Bonzini     E100PCIDeviceInfo *info;
208049ab747fSPaolo Bonzini 
208149ab747fSPaolo Bonzini     info = eepro100_get_class_by_name(object_class_get_name(klass));
208249ab747fSPaolo Bonzini 
2083125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
208449ab747fSPaolo Bonzini     dc->props = e100_properties;
208549ab747fSPaolo Bonzini     dc->desc = info->desc;
208649ab747fSPaolo Bonzini     k->vendor_id = PCI_VENDOR_ID_INTEL;
208749ab747fSPaolo Bonzini     k->class_id = PCI_CLASS_NETWORK_ETHERNET;
208849ab747fSPaolo Bonzini     k->romfile = "pxe-eepro100.rom";
20899af21dbeSMarkus Armbruster     k->realize = e100_nic_realize;
209049ab747fSPaolo Bonzini     k->exit = pci_nic_uninit;
209149ab747fSPaolo Bonzini     k->device_id = info->device_id;
209249ab747fSPaolo Bonzini     k->revision = info->revision;
209349ab747fSPaolo Bonzini     k->subsystem_vendor_id = info->subsystem_vendor_id;
209449ab747fSPaolo Bonzini     k->subsystem_id = info->subsystem_id;
209549ab747fSPaolo Bonzini }
209649ab747fSPaolo Bonzini 
209749ab747fSPaolo Bonzini static void eepro100_register_types(void)
209849ab747fSPaolo Bonzini {
209949ab747fSPaolo Bonzini     size_t i;
210049ab747fSPaolo Bonzini     for (i = 0; i < ARRAY_SIZE(e100_devices); i++) {
210149ab747fSPaolo Bonzini         TypeInfo type_info = {};
210249ab747fSPaolo Bonzini         E100PCIDeviceInfo *info = &e100_devices[i];
210349ab747fSPaolo Bonzini 
210449ab747fSPaolo Bonzini         type_info.name = info->name;
210549ab747fSPaolo Bonzini         type_info.parent = TYPE_PCI_DEVICE;
210649ab747fSPaolo Bonzini         type_info.class_init = eepro100_class_init;
210749ab747fSPaolo Bonzini         type_info.instance_size = sizeof(EEPRO100State);
21087317bb17SGonglei         type_info.instance_init = eepro100_instance_init;
210949ab747fSPaolo Bonzini 
211049ab747fSPaolo Bonzini         type_register(&type_info);
211149ab747fSPaolo Bonzini     }
211249ab747fSPaolo Bonzini }
211349ab747fSPaolo Bonzini 
211449ab747fSPaolo Bonzini type_init(eepro100_register_types)
2115