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