xref: /qemu/hw/net/eepro100.c (revision 32cad1ffb81dcecf6f4a8af56d6e5892682839b1)
1663e8e51Sths /*
2663e8e51Sths  * QEMU i8255x (PRO100) emulation
3663e8e51Sths  *
41b4f97d6SStefan Weil  * Copyright (C) 2006-2011 Stefan Weil
5663e8e51Sths  *
6663e8e51Sths  * Portions of the code are copies from grub / etherboot eepro100.c
7663e8e51Sths  * and linux e100.c.
8663e8e51Sths  *
9dc86dd55SPhilippe Mathieu-Daudé  * SPDX-License-Identifier: GPL-2.0-or-later
10dc86dd55SPhilippe Mathieu-Daudé  *
11230a167cSStefan Weil  * This program is free software: you can redistribute it and/or modify
12663e8e51Sths  * it under the terms of the GNU General Public License as published by
13230a167cSStefan Weil  * the Free Software Foundation, either version 2 of the License, or
14dc86dd55SPhilippe Mathieu-Daudé  * (at your option) any later version.
15663e8e51Sths  *
16663e8e51Sths  * This program is distributed in the hope that it will be useful,
17663e8e51Sths  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18663e8e51Sths  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19663e8e51Sths  * GNU General Public License for more details.
20663e8e51Sths  *
21663e8e51Sths  * You should have received a copy of the GNU General Public License
22230a167cSStefan Weil  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23663e8e51Sths  *
24663e8e51Sths  * Tested features (i82559):
25e5e23ab8SStefan Weil  *      PXE boot (i386 guest, i386 / mips / mipsel / ppc host) ok
26663e8e51Sths  *      Linux networking (i386) ok
27663e8e51Sths  *
28663e8e51Sths  * Untested:
29663e8e51Sths  *      Windows networking
30663e8e51Sths  *
31663e8e51Sths  * References:
32663e8e51Sths  *
33663e8e51Sths  * Intel 8255x 10/100 Mbps Ethernet Controller Family
34663e8e51Sths  * Open Source Software Developer Manual
35ba19f2deSStefan Weil  *
36ba19f2deSStefan Weil  * TODO:
37ba19f2deSStefan Weil  *      * PHY emulation should be separated from nic emulation.
38ba19f2deSStefan Weil  *        Most nic emulations could share the same phy code.
39ba19f2deSStefan Weil  *      * i82550 is untested. It is programmed like the i82559.
40ba19f2deSStefan Weil  *      * i82562 is untested. It is programmed like the i82559.
41ba19f2deSStefan Weil  *      * Power management (i82558 and later) is not implemented.
42ba19f2deSStefan Weil  *      * Wake-on-LAN is not implemented.
43663e8e51Sths  */
44663e8e51Sths 
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"
501422e32dSPaolo Bonzini #include "net/net.h"
517c0348bdSMark Cave-Ayland #include "net/eth.h"
520d09e41aSPaolo Bonzini #include "hw/nvram/eeprom93xx.h"
53*32cad1ffSPhilippe Mathieu-Daudé #include "system/system.h"
54*32cad1ffSPhilippe Mathieu-Daudé #include "system/dma.h"
55*32cad1ffSPhilippe Mathieu-Daudé #include "system/reset.h"
56949fc823SMarcel Apfelbaum #include "qemu/bitops.h"
570b8fa32fSMarkus Armbruster #include "qemu/module.h"
589a7c2a59SMao Zhongyi #include "qapi/error.h"
59663e8e51Sths 
60792f1d63SStefan Weil /* QEMU sends frames smaller than 60 bytes to ethernet nics.
61792f1d63SStefan Weil  * Such frames are rejected by real nics and their emulations.
62792f1d63SStefan Weil  * To avoid this behaviour, other nic emulations pad received
63792f1d63SStefan Weil  * frames. The following definition enables this padding for
64792f1d63SStefan Weil  * eepro100, too. We keep the define around in case it might
65792f1d63SStefan Weil  * become useful the future if the core networking is ever
66792f1d63SStefan Weil  * changed to pad short packets itself. */
67792f1d63SStefan Weil #define CONFIG_PAD_RECEIVED_FRAMES
68792f1d63SStefan Weil 
69aac443e6SStefan Weil /* Debug EEPRO100 card. */
70ce0e58b3SStefan Weil #if 0
71ce0e58b3SStefan Weil # define DEBUG_EEPRO100
72ce0e58b3SStefan Weil #endif
73663e8e51Sths 
74663e8e51Sths #ifdef DEBUG_EEPRO100
75001faf32SBlue Swirl #define logout(fmt, ...) fprintf(stderr, "EE100\t%-24s" fmt, __func__, ## __VA_ARGS__)
76663e8e51Sths #else
77001faf32SBlue Swirl #define logout(fmt, ...) ((void)0)
78663e8e51Sths #endif
79663e8e51Sths 
80663e8e51Sths /* Set flags to 0 to disable debug output. */
81aac443e6SStefan Weil #define INT     1       /* interrupt related actions */
82aac443e6SStefan Weil #define MDI     1       /* mdi related actions */
83aac443e6SStefan Weil #define OTHER   1
84aac443e6SStefan Weil #define RXTX    1
85aac443e6SStefan Weil #define EEPROM  1       /* eeprom related actions */
86663e8e51Sths 
87663e8e51Sths #define TRACE(flag, command) ((flag) ? (command) : (void)0)
88663e8e51Sths 
897f1e9d4eSKevin Wolf #define missing(text) fprintf(stderr, "eepro100: feature is missing in this emulation: " text "\n")
90663e8e51Sths 
91663e8e51Sths #define MAX_ETH_FRAME_SIZE 1514
92663e8e51Sths 
93663e8e51Sths /* This driver supports several different devices which are declared here. */
94c4c270e2SStefan Weil #define i82550          0x82550
95663e8e51Sths #define i82551          0x82551
96c4c270e2SStefan Weil #define i82557A         0x82557a
97663e8e51Sths #define i82557B         0x82557b
98663e8e51Sths #define i82557C         0x82557c
99c4c270e2SStefan Weil #define i82558A         0x82558a
100663e8e51Sths #define i82558B         0x82558b
101c4c270e2SStefan Weil #define i82559A         0x82559a
102c4c270e2SStefan Weil #define i82559B         0x82559b
103663e8e51Sths #define i82559C         0x82559c
104663e8e51Sths #define i82559ER        0x82559e
105663e8e51Sths #define i82562          0x82562
106db667a12SStefan Weil #define i82801          0x82801
107663e8e51Sths 
108aac443e6SStefan Weil /* Use 64 word EEPROM. TODO: could be a runtime option. */
109663e8e51Sths #define EEPROM_SIZE     64
110663e8e51Sths 
111663e8e51Sths #define PCI_MEM_SIZE            (4 * KiB)
112663e8e51Sths #define PCI_IO_SIZE             64
113663e8e51Sths #define PCI_FLASH_SIZE          (128 * KiB)
114663e8e51Sths 
115663e8e51Sths #define BITS(n, m) (((0xffffffffU << (31 - n)) >> (31 - n + m)) << m)
116663e8e51Sths 
117663e8e51Sths /* The SCB accepts the following controls for the Tx and Rx units: */
118663e8e51Sths #define  CU_NOP         0x0000  /* No operation. */
119663e8e51Sths #define  CU_START       0x0010  /* CU start. */
120663e8e51Sths #define  CU_RESUME      0x0020  /* CU resume. */
121663e8e51Sths #define  CU_STATSADDR   0x0040  /* Load dump counters address. */
122663e8e51Sths #define  CU_SHOWSTATS   0x0050  /* Dump statistical counters. */
123663e8e51Sths #define  CU_CMD_BASE    0x0060  /* Load CU base address. */
124663e8e51Sths #define  CU_DUMPSTATS   0x0070  /* Dump and reset statistical counters. */
125663e8e51Sths #define  CU_SRESUME     0x00a0  /* CU static resume. */
126663e8e51Sths 
127663e8e51Sths #define  RU_NOP         0x0000
128663e8e51Sths #define  RX_START       0x0001
129663e8e51Sths #define  RX_RESUME      0x0002
130e824012bSStefan Weil #define  RU_ABORT       0x0004
131663e8e51Sths #define  RX_ADDR_LOAD   0x0006
132663e8e51Sths #define  RX_RESUMENR    0x0007
133663e8e51Sths #define INT_MASK        0x0100
134663e8e51Sths #define DRVR_INT        0x0200  /* Driver generated interrupt. */
135663e8e51Sths 
136558c8634SStefan Weil typedef struct {
13739bffca2SAnthony Liguori     const char *name;
13839bffca2SAnthony Liguori     const char *desc;
13940021f08SAnthony Liguori     uint16_t device_id;
14040021f08SAnthony Liguori     uint8_t revision;
14140021f08SAnthony Liguori     uint16_t subsystem_vendor_id;
14240021f08SAnthony Liguori     uint16_t subsystem_id;
14340021f08SAnthony Liguori 
144558c8634SStefan Weil     uint32_t device;
145558c8634SStefan Weil     uint8_t stats_size;
146558c8634SStefan Weil     bool has_extended_tcb_support;
147558c8634SStefan Weil     bool power_management;
148558c8634SStefan Weil } E100PCIDeviceInfo;
149558c8634SStefan Weil 
150663e8e51Sths /* Offsets to the various registers.
151663e8e51Sths    All accesses need not be longword aligned. */
152e5e23ab8SStefan Weil typedef enum {
1530908bba1SStefan Weil     SCBStatus = 0,              /* Status Word. */
154663e8e51Sths     SCBAck = 1,
155663e8e51Sths     SCBCmd = 2,                 /* Rx/Command Unit command and status. */
156663e8e51Sths     SCBIntmask = 3,
157663e8e51Sths     SCBPointer = 4,             /* General purpose pointer. */
158663e8e51Sths     SCBPort = 8,                /* Misc. commands and operands.  */
1590908bba1SStefan Weil     SCBflash = 12,              /* Flash memory control. */
1600908bba1SStefan Weil     SCBeeprom = 14,             /* EEPROM control. */
161663e8e51Sths     SCBCtrlMDI = 16,            /* MDI interface control. */
162663e8e51Sths     SCBEarlyRx = 20,            /* Early receive byte count. */
1630908bba1SStefan Weil     SCBFlow = 24,               /* Flow Control. */
1640908bba1SStefan Weil     SCBpmdr = 27,               /* Power Management Driver. */
1650908bba1SStefan Weil     SCBgctrl = 28,              /* General Control. */
1660908bba1SStefan Weil     SCBgstat = 29,              /* General Status. */
167e5e23ab8SStefan Weil } E100RegisterOffset;
168663e8e51Sths 
169663e8e51Sths /* A speedo3 transmit buffer descriptor with two buffers... */
170663e8e51Sths typedef struct {
171663e8e51Sths     uint16_t status;
172663e8e51Sths     uint16_t command;
173663e8e51Sths     uint32_t link;              /* void * */
1747b8737deSStefan Weil     uint32_t tbd_array_addr;    /* transmit buffer descriptor array address. */
175663e8e51Sths     uint16_t tcb_bytes;         /* transmit command block byte count (in lower 14 bits */
176663e8e51Sths     uint8_t tx_threshold;       /* transmit threshold */
177663e8e51Sths     uint8_t tbd_count;          /* TBD number */
178e7493b25SStefan Weil #if 0
179e7493b25SStefan Weil     /* This constitutes two "TBD" entries: hdr and data */
180e7493b25SStefan Weil     uint32_t tx_buf_addr0;  /* void *, header of frame to be transmitted.  */
181e7493b25SStefan Weil     int32_t  tx_buf_size0;  /* Length of Tx hdr. */
182e7493b25SStefan Weil     uint32_t tx_buf_addr1;  /* void *, data to be transmitted.  */
183e7493b25SStefan Weil     int32_t  tx_buf_size1;  /* Length of Tx data. */
184e7493b25SStefan Weil #endif
185c227f099SAnthony Liguori } eepro100_tx_t;
186663e8e51Sths 
187663e8e51Sths /* Receive frame descriptor. */
188663e8e51Sths typedef struct {
189663e8e51Sths     int16_t status;
190663e8e51Sths     uint16_t command;
191663e8e51Sths     uint32_t link;              /* struct RxFD * */
192663e8e51Sths     uint32_t rx_buf_addr;       /* void * */
193663e8e51Sths     uint16_t count;
194663e8e51Sths     uint16_t size;
19527112f18SStefan Weil     /* Ethernet frame data follows. */
196c227f099SAnthony Liguori } eepro100_rx_t;
197663e8e51Sths 
198ced5296aSStefan Weil typedef enum {
199ced5296aSStefan Weil     COMMAND_EL = BIT(15),
200ced5296aSStefan Weil     COMMAND_S = BIT(14),
201ced5296aSStefan Weil     COMMAND_I = BIT(13),
202ced5296aSStefan Weil     COMMAND_NC = BIT(4),
203ced5296aSStefan Weil     COMMAND_SF = BIT(3),
204ced5296aSStefan Weil     COMMAND_CMD = BITS(2, 0),
205ced5296aSStefan Weil } scb_command_bit;
206ced5296aSStefan Weil 
207ced5296aSStefan Weil typedef enum {
208ced5296aSStefan Weil     STATUS_C = BIT(15),
209ced5296aSStefan Weil     STATUS_OK = BIT(13),
210ced5296aSStefan Weil } scb_status_bit;
211ced5296aSStefan Weil 
212663e8e51Sths typedef struct {
213663e8e51Sths     uint32_t tx_good_frames, tx_max_collisions, tx_late_collisions,
214663e8e51Sths              tx_underruns, tx_lost_crs, tx_deferred, tx_single_collisions,
215663e8e51Sths              tx_multiple_collisions, tx_total_collisions;
216663e8e51Sths     uint32_t rx_good_frames, rx_crc_errors, rx_alignment_errors,
217663e8e51Sths              rx_resource_errors, rx_overrun_errors, rx_cdt_errors,
218663e8e51Sths              rx_short_frame_errors;
219663e8e51Sths     uint32_t fc_xmt_pause, fc_rcv_pause, fc_rcv_unsupported;
220663e8e51Sths     uint16_t xmt_tco_frames, rcv_tco_frames;
221ba42b646SStefan Weil     /* TODO: i82559 has six reserved statistics but a total of 24 dwords. */
222ba42b646SStefan Weil     uint32_t reserved[4];
223c227f099SAnthony Liguori } eepro100_stats_t;
224663e8e51Sths 
225663e8e51Sths typedef enum {
226663e8e51Sths     cu_idle = 0,
227663e8e51Sths     cu_suspended = 1,
228663e8e51Sths     cu_active = 2,
229663e8e51Sths     cu_lpq_active = 2,
230663e8e51Sths     cu_hqp_active = 3
231c227f099SAnthony Liguori } cu_state_t;
232663e8e51Sths 
233663e8e51Sths typedef enum {
234663e8e51Sths     ru_idle = 0,
235663e8e51Sths     ru_suspended = 1,
236663e8e51Sths     ru_no_resources = 2,
237663e8e51Sths     ru_ready = 4
238c227f099SAnthony Liguori } ru_state_t;
239663e8e51Sths 
240663e8e51Sths typedef struct {
241273a2142SJuan Quintela     PCIDevice dev;
242010ec629SStefan Weil     /* Hash register (multicast mask array, multiple individual addresses). */
243010ec629SStefan Weil     uint8_t mult[8];
2445e6ffddeSAvi Kivity     MemoryRegion mmio_bar;
2455e6ffddeSAvi Kivity     MemoryRegion io_bar;
2465e6ffddeSAvi Kivity     MemoryRegion flash_bar;
247e00e365eSMark McLoughlin     NICState *nic;
248508ef936SGerd Hoffmann     NICConf conf;
249663e8e51Sths     uint8_t scb_stat;           /* SCB stat/ack byte */
250663e8e51Sths     uint8_t int_stat;           /* PCI interrupt status */
2513706c43fSStefan Weil     /* region must not be saved by nic_save. */
252663e8e51Sths     uint16_t mdimem[32];
253c227f099SAnthony Liguori     eeprom_t *eeprom;
254663e8e51Sths     uint32_t device;            /* device variant */
255663e8e51Sths     /* (cu_base + cu_offset) address the next command block in the command block list. */
256663e8e51Sths     uint32_t cu_base;           /* CU base address */
257663e8e51Sths     uint32_t cu_offset;         /* CU address offset */
258663e8e51Sths     /* (ru_base + ru_offset) address the RFD in the Receive Frame Area. */
259663e8e51Sths     uint32_t ru_base;           /* RU base address */
260663e8e51Sths     uint32_t ru_offset;         /* RU address offset */
261c227f099SAnthony Liguori     uint32_t statsaddr;         /* pointer to eepro100_stats_t */
262ba42b646SStefan Weil 
263f3a52e50SStefan Weil     /* Temporary status information (no need to save these values),
264f3a52e50SStefan Weil      * used while processing CU commands. */
265f3a52e50SStefan Weil     eepro100_tx_t tx;           /* transmit buffer descriptor */
266f3a52e50SStefan Weil     uint32_t cb_address;        /* = cu_base + cu_offset */
267f3a52e50SStefan Weil 
268ba42b646SStefan Weil     /* Statistical counters. Also used for wake-up packet (i82559). */
269ba42b646SStefan Weil     eepro100_stats_t statistics;
270ba42b646SStefan Weil 
271e5e23ab8SStefan Weil     /* Data in mem is always in the byte order of the controller (le).
272e5e23ab8SStefan Weil      * It must be dword aligned to allow direct access to 32 bit values. */
2733a93113aSDong Xu Wang     uint8_t mem[PCI_MEM_SIZE] __attribute__((aligned(8)));
274e5e23ab8SStefan Weil 
275663e8e51Sths     /* Configuration bytes. */
276663e8e51Sths     uint8_t configuration[22];
277663e8e51Sths 
278151b2986SJuan Quintela     /* vmstate for each particular nic */
279151b2986SJuan Quintela     VMStateDescription *vmstate;
280ba42b646SStefan Weil 
281ba42b646SStefan Weil     /* Quasi static device properties (no need to save them). */
282ba42b646SStefan Weil     uint16_t stats_size;
283ba42b646SStefan Weil     bool has_extended_tcb_support;
284663e8e51Sths } EEPRO100State;
285663e8e51Sths 
2866cded3a4SStefan Weil /* Word indices in EEPROM. */
2876cded3a4SStefan Weil typedef enum {
2886cded3a4SStefan Weil     EEPROM_CNFG_MDIX  = 0x03,
2896cded3a4SStefan Weil     EEPROM_ID         = 0x05,
2906cded3a4SStefan Weil     EEPROM_PHY_ID     = 0x06,
2916cded3a4SStefan Weil     EEPROM_VENDOR_ID  = 0x0c,
2926cded3a4SStefan Weil     EEPROM_CONFIG_ASF = 0x0d,
2936cded3a4SStefan Weil     EEPROM_DEVICE_ID  = 0x23,
2946cded3a4SStefan Weil     EEPROM_SMBUS_ADDR = 0x90,
2956cded3a4SStefan Weil } EEPROMOffset;
2966cded3a4SStefan Weil 
297b1e87018SStefan Weil /* Bit values for EEPROM ID word. */
298b1e87018SStefan Weil typedef enum {
299b1e87018SStefan Weil     EEPROM_ID_MDM = BIT(0),     /* Modem */
300b1e87018SStefan Weil     EEPROM_ID_STB = BIT(1),     /* Standby Enable */
301b1e87018SStefan Weil     EEPROM_ID_WMR = BIT(2),     /* ??? */
302b1e87018SStefan Weil     EEPROM_ID_WOL = BIT(5),     /* Wake on LAN */
303b1e87018SStefan Weil     EEPROM_ID_DPD = BIT(6),     /* Deep Power Down */
304b1e87018SStefan Weil     EEPROM_ID_ALT = BIT(7),     /* */
305b1e87018SStefan Weil     /* BITS(10, 8) device revision */
306b1e87018SStefan Weil     EEPROM_ID_BD = BIT(11),     /* boot disable */
307b1e87018SStefan Weil     EEPROM_ID_ID = BIT(13),     /* id bit */
308b1e87018SStefan Weil     /* BITS(15, 14) signature */
309b1e87018SStefan Weil     EEPROM_ID_VALID = BIT(14),  /* signature for valid eeprom */
310b1e87018SStefan Weil } eeprom_id_bit;
311b1e87018SStefan Weil 
312663e8e51Sths /* Default values for MDI (PHY) registers */
313663e8e51Sths static const uint16_t eepro100_mdi_default[] = {
314663e8e51Sths     /* MDI Registers 0 - 6, 7 */
315663e8e51Sths     0x3000, 0x780d, 0x02a8, 0x0154, 0x05e1, 0x0000, 0x0000, 0x0000,
316663e8e51Sths     /* MDI Registers 8 - 15 */
317663e8e51Sths     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
318663e8e51Sths     /* MDI Registers 16 - 31 */
319663e8e51Sths     0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
320663e8e51Sths     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
321663e8e51Sths };
322663e8e51Sths 
323663e8e51Sths /* Readonly mask for MDI (PHY) registers */
324663e8e51Sths static const uint16_t eepro100_mdi_mask[] = {
325663e8e51Sths     0x0000, 0xffff, 0xffff, 0xffff, 0xc01f, 0xffff, 0xffff, 0x0000,
326663e8e51Sths     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
327663e8e51Sths     0x0fff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
328663e8e51Sths     0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
329663e8e51Sths };
330663e8e51Sths 
33140021f08SAnthony Liguori static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s);
33240021f08SAnthony Liguori 
333e5e23ab8SStefan Weil /* Read a 16 bit control/status (CSR) register. */
334e5e23ab8SStefan Weil static uint16_t e100_read_reg2(EEPRO100State *s, E100RegisterOffset addr)
335e5e23ab8SStefan Weil {
336e5e23ab8SStefan Weil     assert(!((uintptr_t)&s->mem[addr] & 1));
3374d9be252SPeter Maydell     return lduw_le_p(&s->mem[addr]);
338e5e23ab8SStefan Weil }
339e5e23ab8SStefan Weil 
340e5e23ab8SStefan Weil /* Read a 32 bit control/status (CSR) register. */
341e5e23ab8SStefan Weil static uint32_t e100_read_reg4(EEPRO100State *s, E100RegisterOffset addr)
342e5e23ab8SStefan Weil {
343e5e23ab8SStefan Weil     assert(!((uintptr_t)&s->mem[addr] & 3));
3444d9be252SPeter Maydell     return ldl_le_p(&s->mem[addr]);
345e5e23ab8SStefan Weil }
346e5e23ab8SStefan Weil 
347e5e23ab8SStefan Weil /* Write a 16 bit control/status (CSR) register. */
348e5e23ab8SStefan Weil static void e100_write_reg2(EEPRO100State *s, E100RegisterOffset addr,
349e5e23ab8SStefan Weil                             uint16_t val)
350e5e23ab8SStefan Weil {
351e5e23ab8SStefan Weil     assert(!((uintptr_t)&s->mem[addr] & 1));
3524d9be252SPeter Maydell     stw_le_p(&s->mem[addr], val);
353e5e23ab8SStefan Weil }
354e5e23ab8SStefan Weil 
355e5e23ab8SStefan Weil /* Read a 32 bit control/status (CSR) register. */
356e5e23ab8SStefan Weil static void e100_write_reg4(EEPRO100State *s, E100RegisterOffset addr,
357e5e23ab8SStefan Weil                             uint32_t val)
358e5e23ab8SStefan Weil {
359e5e23ab8SStefan Weil     assert(!((uintptr_t)&s->mem[addr] & 3));
3604d9be252SPeter Maydell     stl_le_p(&s->mem[addr], val);
361e5e23ab8SStefan Weil }
362e5e23ab8SStefan Weil 
363663e8e51Sths #if defined(DEBUG_EEPRO100)
364663e8e51Sths static const char *nic_dump(const uint8_t * buf, unsigned size)
365663e8e51Sths {
366663e8e51Sths     static char dump[3 * 16 + 1];
367663e8e51Sths     char *p = &dump[0];
368aac443e6SStefan Weil     if (size > 16) {
369663e8e51Sths         size = 16;
370aac443e6SStefan Weil     }
371663e8e51Sths     while (size-- > 0) {
372663e8e51Sths         p += sprintf(p, " %02x", *buf++);
373663e8e51Sths     }
374663e8e51Sths     return dump;
375663e8e51Sths }
376663e8e51Sths #endif                          /* DEBUG_EEPRO100 */
377663e8e51Sths 
378663e8e51Sths enum scb_stat_ack {
379663e8e51Sths     stat_ack_not_ours = 0x00,
380663e8e51Sths     stat_ack_sw_gen = 0x04,
381663e8e51Sths     stat_ack_rnr = 0x10,
382663e8e51Sths     stat_ack_cu_idle = 0x20,
383663e8e51Sths     stat_ack_frame_rx = 0x40,
384663e8e51Sths     stat_ack_cu_cmd_done = 0x80,
385663e8e51Sths     stat_ack_not_present = 0xFF,
386663e8e51Sths     stat_ack_rx = (stat_ack_sw_gen | stat_ack_rnr | stat_ack_frame_rx),
387663e8e51Sths     stat_ack_tx = (stat_ack_cu_idle | stat_ack_cu_cmd_done),
388663e8e51Sths };
389663e8e51Sths 
390663e8e51Sths static void disable_interrupt(EEPRO100State * s)
391663e8e51Sths {
392663e8e51Sths     if (s->int_stat) {
393aac443e6SStefan Weil         TRACE(INT, logout("interrupt disabled\n"));
3949e64f8a3SMarcel Apfelbaum         pci_irq_deassert(&s->dev);
395663e8e51Sths         s->int_stat = 0;
396663e8e51Sths     }
397663e8e51Sths }
398663e8e51Sths 
399663e8e51Sths static void enable_interrupt(EEPRO100State * s)
400663e8e51Sths {
401663e8e51Sths     if (!s->int_stat) {
402aac443e6SStefan Weil         TRACE(INT, logout("interrupt enabled\n"));
4039e64f8a3SMarcel Apfelbaum         pci_irq_assert(&s->dev);
404663e8e51Sths         s->int_stat = 1;
405663e8e51Sths     }
406663e8e51Sths }
407663e8e51Sths 
408663e8e51Sths static void eepro100_acknowledge(EEPRO100State * s)
409663e8e51Sths {
410663e8e51Sths     s->scb_stat &= ~s->mem[SCBAck];
411663e8e51Sths     s->mem[SCBAck] = s->scb_stat;
412663e8e51Sths     if (s->scb_stat == 0) {
413663e8e51Sths         disable_interrupt(s);
414663e8e51Sths     }
415663e8e51Sths }
416663e8e51Sths 
417e715c8e8SStefan Weil static void eepro100_interrupt(EEPRO100State * s, uint8_t status)
418663e8e51Sths {
419663e8e51Sths     uint8_t mask = ~s->mem[SCBIntmask];
420e715c8e8SStefan Weil     s->mem[SCBAck] |= status;
421e715c8e8SStefan Weil     status = s->scb_stat = s->mem[SCBAck];
422e715c8e8SStefan Weil     status &= (mask | 0x0f);
423e7493b25SStefan Weil #if 0
424e7493b25SStefan Weil     status &= (~s->mem[SCBIntmask] | 0x0xf);
425e7493b25SStefan Weil #endif
426e715c8e8SStefan Weil     if (status && (mask & 0x01)) {
427663e8e51Sths         /* SCB mask and SCB Bit M do not disable interrupt. */
428663e8e51Sths         enable_interrupt(s);
429663e8e51Sths     } else if (s->int_stat) {
430663e8e51Sths         disable_interrupt(s);
431663e8e51Sths     }
432663e8e51Sths }
433663e8e51Sths 
434663e8e51Sths static void eepro100_cx_interrupt(EEPRO100State * s)
435663e8e51Sths {
436663e8e51Sths     /* CU completed action command. */
437663e8e51Sths     /* Transmit not ok (82557 only, not in emulation). */
438663e8e51Sths     eepro100_interrupt(s, 0x80);
439663e8e51Sths }
440663e8e51Sths 
441663e8e51Sths static void eepro100_cna_interrupt(EEPRO100State * s)
442663e8e51Sths {
443663e8e51Sths     /* CU left the active state. */
444663e8e51Sths     eepro100_interrupt(s, 0x20);
445663e8e51Sths }
446663e8e51Sths 
447663e8e51Sths static void eepro100_fr_interrupt(EEPRO100State * s)
448663e8e51Sths {
449663e8e51Sths     /* RU received a complete frame. */
450663e8e51Sths     eepro100_interrupt(s, 0x40);
451663e8e51Sths }
452663e8e51Sths 
453663e8e51Sths static void eepro100_rnr_interrupt(EEPRO100State * s)
454663e8e51Sths {
455663e8e51Sths     /* RU is not ready. */
456663e8e51Sths     eepro100_interrupt(s, 0x10);
457663e8e51Sths }
458663e8e51Sths 
459663e8e51Sths static void eepro100_mdi_interrupt(EEPRO100State * s)
460663e8e51Sths {
461663e8e51Sths     /* MDI completed read or write cycle. */
462663e8e51Sths     eepro100_interrupt(s, 0x08);
463663e8e51Sths }
464663e8e51Sths 
465663e8e51Sths static void eepro100_swi_interrupt(EEPRO100State * s)
466663e8e51Sths {
467663e8e51Sths     /* Software has requested an interrupt. */
468663e8e51Sths     eepro100_interrupt(s, 0x04);
469663e8e51Sths }
470663e8e51Sths 
471663e8e51Sths #if 0
472663e8e51Sths static void eepro100_fcp_interrupt(EEPRO100State * s)
473663e8e51Sths {
474663e8e51Sths     /* Flow control pause interrupt (82558 and later). */
475663e8e51Sths     eepro100_interrupt(s, 0x01);
476663e8e51Sths }
477663e8e51Sths #endif
478663e8e51Sths 
4799a7c2a59SMao Zhongyi static void e100_pci_reset(EEPRO100State *s, Error **errp)
480663e8e51Sths {
48140021f08SAnthony Liguori     E100PCIDeviceInfo *info = eepro100_get_class(s);
482663e8e51Sths     uint32_t device = s->device;
483273a2142SJuan Quintela     uint8_t *pci_conf = s->dev.config;
484663e8e51Sths 
485aac443e6SStefan Weil     TRACE(OTHER, logout("%p\n", s));
486663e8e51Sths 
487663e8e51Sths     /* PCI Status */
488558c8634SStefan Weil     pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM |
489558c8634SStefan Weil                                         PCI_STATUS_FAST_BACK);
490663e8e51Sths     /* PCI Latency Timer */
49115e89f59SMichael S. Tsirkin     pci_set_byte(pci_conf + PCI_LATENCY_TIMER, 0x20);   /* latency timer = 32 clocks */
492ae543b49SStefan Weil     /* Capability Pointer is set by PCI framework. */
493f62719caSStefan Weil     /* Interrupt Line */
494f62719caSStefan Weil     /* Interrupt Pin */
495f62719caSStefan Weil     pci_set_byte(pci_conf + PCI_INTERRUPT_PIN, 1);      /* interrupt pin A */
496663e8e51Sths     /* Minimum Grant */
49715e89f59SMichael S. Tsirkin     pci_set_byte(pci_conf + PCI_MIN_GNT, 0x08);
498663e8e51Sths     /* Maximum Latency */
49915e89f59SMichael S. Tsirkin     pci_set_byte(pci_conf + PCI_MAX_LAT, 0x18);
500663e8e51Sths 
50140021f08SAnthony Liguori     s->stats_size = info->stats_size;
50240021f08SAnthony Liguori     s->has_extended_tcb_support = info->has_extended_tcb_support;
503558c8634SStefan Weil 
504663e8e51Sths     switch (device) {
505ba42b646SStefan Weil     case i82550:
506663e8e51Sths     case i82551:
507ba42b646SStefan Weil     case i82557A:
508663e8e51Sths     case i82557B:
509663e8e51Sths     case i82557C:
510ba42b646SStefan Weil     case i82558A:
511663e8e51Sths     case i82558B:
512ba42b646SStefan Weil     case i82559A:
513ba42b646SStefan Weil     case i82559B:
514558c8634SStefan Weil     case i82559ER:
515558c8634SStefan Weil     case i82562:
516db667a12SStefan Weil     case i82801:
517663e8e51Sths     case i82559C:
518ba42b646SStefan Weil         break;
519663e8e51Sths     default:
520663e8e51Sths         logout("Device %X is undefined!\n", device);
521663e8e51Sths     }
522663e8e51Sths 
5233dec59a1SStefan Weil     /* Standard TxCB. */
5243dec59a1SStefan Weil     s->configuration[6] |= BIT(4);
5253dec59a1SStefan Weil 
526558c8634SStefan Weil     /* Standard statistical counters. */
527ba42b646SStefan Weil     s->configuration[6] |= BIT(5);
528ba42b646SStefan Weil 
529ba42b646SStefan Weil     if (s->stats_size == 80) {
530ba42b646SStefan Weil         /* TODO: check TCO Statistical Counters bit. Documentation not clear. */
531ba42b646SStefan Weil         if (s->configuration[6] & BIT(2)) {
532ba42b646SStefan Weil             /* TCO statistical counters. */
533ba42b646SStefan Weil             assert(s->configuration[6] & BIT(5));
534ba42b646SStefan Weil         } else {
535ba42b646SStefan Weil             if (s->configuration[6] & BIT(5)) {
536ba42b646SStefan Weil                 /* No extended statistical counters, i82557 compatible. */
537ba42b646SStefan Weil                 s->stats_size = 64;
538ba42b646SStefan Weil             } else {
539ba42b646SStefan Weil                 /* i82558 compatible. */
540ba42b646SStefan Weil                 s->stats_size = 76;
541ba42b646SStefan Weil             }
542ba42b646SStefan Weil         }
543ba42b646SStefan Weil     } else {
544ba42b646SStefan Weil         if (s->configuration[6] & BIT(5)) {
545ba42b646SStefan Weil             /* No extended statistical counters. */
546ba42b646SStefan Weil             s->stats_size = 64;
547ba42b646SStefan Weil         }
548ba42b646SStefan Weil     }
549ba42b646SStefan Weil     assert(s->stats_size > 0 && s->stats_size <= sizeof(s->statistics));
550ba42b646SStefan Weil 
55140021f08SAnthony Liguori     if (info->power_management) {
552ba42b646SStefan Weil         /* Power Management Capabilities */
5538bbd1ce2SMichael S. Tsirkin         int cfg_offset = 0xdc;
554ca77089dSIsaku Yamahata         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 
561ae543b49SStefan Weil         pci_set_word(pci_conf + cfg_offset + PCI_PM_PMC, 0x7e21);
562ae543b49SStefan Weil #if 0 /* TODO: replace dummy code for power management emulation. */
563ba42b646SStefan Weil         /* TODO: Power Management Control / Status. */
564ae543b49SStefan Weil         pci_set_word(pci_conf + cfg_offset + PCI_PM_CTRL, 0x0000);
565ba42b646SStefan Weil         /* TODO: Ethernet Power Consumption Registers (i82559 and later). */
566ae543b49SStefan Weil         pci_set_byte(pci_conf + cfg_offset + PCI_PM_PPB_EXTENSIONS, 0x0000);
567ae543b49SStefan Weil #endif
568ae543b49SStefan Weil     }
569ba42b646SStefan Weil 
570ba42b646SStefan Weil #if EEPROM_SIZE > 0
571663e8e51Sths     if (device == i82557C || device == i82558B || device == i82559C) {
572e7493b25SStefan Weil         /*
573e7493b25SStefan Weil         TODO: get vendor id from EEPROM for i82557C or later.
574e7493b25SStefan Weil         TODO: get device id from EEPROM for i82557C or later.
575e7493b25SStefan Weil         TODO: status bit 4 can be disabled by EEPROM for i82558, i82559.
576e7493b25SStefan Weil         TODO: header type is determined by EEPROM for i82559.
577e7493b25SStefan Weil         TODO: get subsystem id from EEPROM for i82557C or later.
578e7493b25SStefan Weil         TODO: get subsystem vendor id from EEPROM for i82557C or later.
579e7493b25SStefan Weil         TODO: exp. rom baddr depends on a bit in EEPROM for i82558 or later.
580e7493b25SStefan Weil         TODO: capability pointer depends on EEPROM for i82558.
581e7493b25SStefan Weil         */
582663e8e51Sths         logout("Get device id and revision from EEPROM!!!\n");
583663e8e51Sths     }
584ba42b646SStefan Weil #endif /* EEPROM_SIZE > 0 */
585663e8e51Sths }
586663e8e51Sths 
587663e8e51Sths static void nic_selective_reset(EEPRO100State * s)
588663e8e51Sths {
589663e8e51Sths     size_t i;
590663e8e51Sths     uint16_t *eeprom_contents = eeprom93xx_data(s->eeprom);
591e7493b25SStefan Weil #if 0
592e7493b25SStefan Weil     eeprom93xx_reset(s->eeprom);
593e7493b25SStefan Weil #endif
594508ef936SGerd Hoffmann     memcpy(eeprom_contents, s->conf.macaddr.a, 6);
595b1e87018SStefan Weil     eeprom_contents[EEPROM_ID] = EEPROM_ID_VALID;
596f4e94dfeS=?UTF-8?q?Reimar=20D=C3=B6ffinger?=     if (s->device == i82557B || s->device == i82557C)
597f4e94dfeS=?UTF-8?q?Reimar=20D=C3=B6ffinger?=         eeprom_contents[5] = 0x0100;
5986cded3a4SStefan Weil     eeprom_contents[EEPROM_PHY_ID] = 1;
599663e8e51Sths     uint16_t sum = 0;
600663e8e51Sths     for (i = 0; i < EEPROM_SIZE - 1; i++) {
601663e8e51Sths         sum += eeprom_contents[i];
602663e8e51Sths     }
603663e8e51Sths     eeprom_contents[EEPROM_SIZE - 1] = 0xbaba - sum;
604aac443e6SStefan Weil     TRACE(EEPROM, logout("checksum=0x%04x\n", eeprom_contents[EEPROM_SIZE - 1]));
605663e8e51Sths 
606663e8e51Sths     memset(s->mem, 0, sizeof(s->mem));
607e5e23ab8SStefan Weil     e100_write_reg4(s, SCBCtrlMDI, BIT(21));
608663e8e51Sths 
609663e8e51Sths     assert(sizeof(s->mdimem) == sizeof(eepro100_mdi_default));
610663e8e51Sths     memcpy(&s->mdimem[0], &eepro100_mdi_default[0], sizeof(s->mdimem));
611663e8e51Sths }
612663e8e51Sths 
613663e8e51Sths static void nic_reset(void *opaque)
614663e8e51Sths {
615769cf7a5SJuan Quintela     EEPRO100State *s = opaque;
616aac443e6SStefan Weil     TRACE(OTHER, logout("%p\n", s));
617010ec629SStefan Weil     /* TODO: Clearing of hash register for selective reset, too? */
6187b8737deSStefan Weil     memset(&s->mult[0], 0, sizeof(s->mult));
619663e8e51Sths     nic_selective_reset(s);
620663e8e51Sths }
621663e8e51Sths 
622663e8e51Sths #if defined(DEBUG_EEPRO100)
623b8f6ba0dSStefan Weil static const char * const e100_reg[PCI_IO_SIZE / 4] = {
624663e8e51Sths     "Command/Status",
625663e8e51Sths     "General Pointer",
626663e8e51Sths     "Port",
627663e8e51Sths     "EEPROM/Flash Control",
628663e8e51Sths     "MDI Control",
629663e8e51Sths     "Receive DMA Byte Count",
630b8f6ba0dSStefan Weil     "Flow Control",
631663e8e51Sths     "General Status/Control"
632663e8e51Sths };
633663e8e51Sths 
634663e8e51Sths static char *regname(uint32_t addr)
635663e8e51Sths {
636ec169288SDavid Benjamin     static char buf[32];
637663e8e51Sths     if (addr < PCI_IO_SIZE) {
638b8f6ba0dSStefan Weil         const char *r = e100_reg[addr / 4];
639663e8e51Sths         if (r != 0) {
64041cbc23cSStefan Weil             snprintf(buf, sizeof(buf), "%s+%u", r, addr % 4);
641663e8e51Sths         } else {
64241cbc23cSStefan Weil             snprintf(buf, sizeof(buf), "0x%02x", addr);
643663e8e51Sths         }
644663e8e51Sths     } else {
64541cbc23cSStefan Weil         snprintf(buf, sizeof(buf), "??? 0x%08x", addr);
646663e8e51Sths     }
647663e8e51Sths     return buf;
648663e8e51Sths }
649663e8e51Sths #endif                          /* DEBUG_EEPRO100 */
650663e8e51Sths 
651663e8e51Sths /*****************************************************************************
652663e8e51Sths  *
653663e8e51Sths  * Command emulation.
654663e8e51Sths  *
655663e8e51Sths  ****************************************************************************/
656663e8e51Sths 
657663e8e51Sths #if 0
658663e8e51Sths static uint16_t eepro100_read_command(EEPRO100State * s)
659663e8e51Sths {
660663e8e51Sths     uint16_t val = 0xffff;
661e7493b25SStefan Weil     TRACE(OTHER, logout("val=0x%04x\n", val));
662663e8e51Sths     return val;
663663e8e51Sths }
664663e8e51Sths #endif
665663e8e51Sths 
666663e8e51Sths /* Commands that can be put in a command list entry. */
667663e8e51Sths enum commands {
668663e8e51Sths     CmdNOp = 0,
669663e8e51Sths     CmdIASetup = 1,
670663e8e51Sths     CmdConfigure = 2,
671663e8e51Sths     CmdMulticastList = 3,
672663e8e51Sths     CmdTx = 4,
673663e8e51Sths     CmdTDR = 5,                 /* load microcode */
674663e8e51Sths     CmdDump = 6,
675663e8e51Sths     CmdDiagnose = 7,
676663e8e51Sths 
677663e8e51Sths     /* And some extra flags: */
678663e8e51Sths     CmdSuspend = 0x4000,        /* Suspend after completion. */
679663e8e51Sths     CmdIntr = 0x2000,           /* Interrupt after completion. */
680663e8e51Sths     CmdTxFlex = 0x0008,         /* Use "Flexible mode" for CmdTx command. */
681663e8e51Sths };
682663e8e51Sths 
683c227f099SAnthony Liguori static cu_state_t get_cu_state(EEPRO100State * s)
684663e8e51Sths {
685ced5296aSStefan Weil     return ((s->mem[SCBStatus] & BITS(7, 6)) >> 6);
686663e8e51Sths }
687663e8e51Sths 
688c227f099SAnthony Liguori static void set_cu_state(EEPRO100State * s, cu_state_t state)
689663e8e51Sths {
690ced5296aSStefan Weil     s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(7, 6)) + (state << 6);
691663e8e51Sths }
692663e8e51Sths 
693c227f099SAnthony Liguori static ru_state_t get_ru_state(EEPRO100State * s)
694663e8e51Sths {
695ced5296aSStefan Weil     return ((s->mem[SCBStatus] & BITS(5, 2)) >> 2);
696663e8e51Sths }
697663e8e51Sths 
698c227f099SAnthony Liguori static void set_ru_state(EEPRO100State * s, ru_state_t state)
699663e8e51Sths {
700ced5296aSStefan Weil     s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(5, 2)) + (state << 2);
701663e8e51Sths }
702663e8e51Sths 
703663e8e51Sths static void dump_statistics(EEPRO100State * s)
704663e8e51Sths {
705a423a1b5SPhilippe Mathieu-Daudé     const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
706a423a1b5SPhilippe Mathieu-Daudé 
707663e8e51Sths     /* Dump statistical data. Most data is never changed by the emulation
708663e8e51Sths      * and always 0, so we first just copy the whole block and then those
709663e8e51Sths      * values which really matter.
710663e8e51Sths      * Number of data should check configuration!!!
711663e8e51Sths      */
712e965d4bcSDavid Gibson     pci_dma_write(&s->dev, s->statsaddr, &s->statistics, s->stats_size);
71316ef60c9SEduard - Gabriel Munteanu     stl_le_pci_dma(&s->dev, s->statsaddr + 0,
714a423a1b5SPhilippe Mathieu-Daudé                    s->statistics.tx_good_frames, attrs);
71516ef60c9SEduard - Gabriel Munteanu     stl_le_pci_dma(&s->dev, s->statsaddr + 36,
716a423a1b5SPhilippe Mathieu-Daudé                    s->statistics.rx_good_frames, attrs);
71716ef60c9SEduard - Gabriel Munteanu     stl_le_pci_dma(&s->dev, s->statsaddr + 48,
718a423a1b5SPhilippe Mathieu-Daudé                    s->statistics.rx_resource_errors, attrs);
71916ef60c9SEduard - Gabriel Munteanu     stl_le_pci_dma(&s->dev, s->statsaddr + 60,
720a423a1b5SPhilippe Mathieu-Daudé                    s->statistics.rx_short_frame_errors, attrs);
721e7493b25SStefan Weil #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);
726e7493b25SStefan Weil     missing("CU dump statistical counters");
727e7493b25SStefan Weil #endif
728663e8e51Sths }
729663e8e51Sths 
7303d0f4b9bSStefan Weil static void read_cb(EEPRO100State *s)
7313d0f4b9bSStefan Weil {
732e965d4bcSDavid Gibson     pci_dma_read(&s->dev, s->cb_address, &s->tx, sizeof(s->tx));
7333d0f4b9bSStefan Weil     s->tx.status = le16_to_cpu(s->tx.status);
7343d0f4b9bSStefan Weil     s->tx.command = le16_to_cpu(s->tx.command);
7353d0f4b9bSStefan Weil     s->tx.link = le32_to_cpu(s->tx.link);
7363d0f4b9bSStefan Weil     s->tx.tbd_array_addr = le32_to_cpu(s->tx.tbd_array_addr);
7373d0f4b9bSStefan Weil     s->tx.tcb_bytes = le16_to_cpu(s->tx.tcb_bytes);
7383d0f4b9bSStefan Weil }
7393d0f4b9bSStefan Weil 
740f3a52e50SStefan Weil static void tx_command(EEPRO100State *s)
741663e8e51Sths {
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;
745f3a52e50SStefan Weil     /* Sends larger than MAX_ETH_FRAME_SIZE are allowed, up to 2600 bytes. */
746f3a52e50SStefan Weil     uint8_t buf[2600];
747f3a52e50SStefan Weil     uint16_t size = 0;
748f3a52e50SStefan Weil     uint32_t tbd_address = s->cb_address + 0x10;
749aac443e6SStefan Weil     TRACE(RXTX, logout
750663e8e51Sths         ("transmit, TBD array address 0x%08x, TCB byte count 0x%04x, TBD count %u\n",
751f3a52e50SStefan Weil          tbd_array, tcb_bytes, s->tx.tbd_count));
7527f1e9d4eSKevin Wolf 
7537f1e9d4eSKevin Wolf     if (tcb_bytes > 2600) {
7547f1e9d4eSKevin Wolf         logout("TCB byte count too large, using 2600\n");
7557f1e9d4eSKevin Wolf         tcb_bytes = 2600;
7567f1e9d4eSKevin Wolf     }
757663e8e51Sths     if (!((tcb_bytes > 0) || (tbd_array != 0xffffffff))) {
758663e8e51Sths         logout
759663e8e51Sths             ("illegal values of TBD array address and TCB byte count!\n");
760663e8e51Sths     }
761663e8e51Sths     assert(tcb_bytes <= sizeof(buf));
762663e8e51Sths     while (size < tcb_bytes) {
763aac443e6SStefan Weil         TRACE(RXTX, logout
764663e8e51Sths             ("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;
768663e8e51Sths     }
769663e8e51Sths     if (tbd_array == 0xffffffff) {
770663e8e51Sths         /* Simplified mode. Was already handled by code above. */
771663e8e51Sths     } else {
772663e8e51Sths         /* Flexible mode. */
773663e8e51Sths         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é 
778ba42b646SStefan Weil         if (s->has_extended_tcb_support && !(s->configuration[6] & BIT(4))) {
7793f9cb1c1SNaphtali Sprei             /* Extended Flexible TCB. */
780663e8e51Sths             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);
784663e8e51Sths                 tbd_address += 8;
785aac443e6SStefan Weil                 TRACE(RXTX, logout
7863f9cb1c1SNaphtali Sprei                     ("TBD (extended flexible mode): buffer address 0x%08x, size 0x%04x\n",
787aac443e6SStefan Weil                      tx_buffer_address, tx_buffer_size));
78824e6f355SReimar Döffinger                 tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size);
78916ef60c9SEduard - Gabriel Munteanu                 pci_dma_read(&s->dev, tx_buffer_address,
79016ef60c9SEduard - Gabriel Munteanu                              &buf[size], tx_buffer_size);
791663e8e51Sths                 size += tx_buffer_size;
792663e8e51Sths                 if (tx_buffer_el & 1) {
793663e8e51Sths                     break;
794663e8e51Sths                 }
795663e8e51Sths             }
796663e8e51Sths         }
797663e8e51Sths         tbd_address = tbd_array;
798f3a52e50SStefan Weil         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);
802663e8e51Sths             tbd_address += 8;
803aac443e6SStefan Weil             TRACE(RXTX, logout
804663e8e51Sths                 ("TBD (flexible mode): buffer address 0x%08x, size 0x%04x\n",
805aac443e6SStefan Weil                  tx_buffer_address, tx_buffer_size));
80624e6f355SReimar Döffinger             tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size);
80716ef60c9SEduard - Gabriel Munteanu             pci_dma_read(&s->dev, tx_buffer_address,
80816ef60c9SEduard - Gabriel Munteanu                          &buf[size], tx_buffer_size);
809663e8e51Sths             size += tx_buffer_size;
810663e8e51Sths             if (tx_buffer_el & 1) {
811663e8e51Sths                 break;
812663e8e51Sths             }
813663e8e51Sths         }
814663e8e51Sths     }
815aac443e6SStefan Weil     TRACE(RXTX, logout("%p sending frame, len=%d,%s\n", s, size, nic_dump(buf, size)));
816b356f76dSJason Wang     qemu_send_packet(qemu_get_queue(s->nic), buf, size);
817663e8e51Sths     s->statistics.tx_good_frames++;
818663e8e51Sths     /* Transmit with bad status would raise an CX/TNO interrupt.
819663e8e51Sths      * (82557 only). Emulation never has bad status. */
820e7493b25SStefan Weil #if 0
821e7493b25SStefan Weil     eepro100_cx_interrupt(s);
822e7493b25SStefan Weil #endif
823f3a52e50SStefan Weil }
824f3a52e50SStefan Weil 
8257b8737deSStefan Weil static void set_multicast_list(EEPRO100State *s)
8267b8737deSStefan Weil {
8277b8737deSStefan Weil     uint16_t multicast_count = s->tx.tbd_array_addr & BITS(13, 0);
8287b8737deSStefan Weil     uint16_t i;
8297b8737deSStefan Weil     memset(&s->mult[0], 0, sizeof(s->mult));
8307b8737deSStefan Weil     TRACE(OTHER, logout("multicast list, multicast count = %u\n", multicast_count));
8317b8737deSStefan Weil     for (i = 0; i < multicast_count; i += 6) {
8327b8737deSStefan Weil         uint8_t multicast_addr[6];
83316ef60c9SEduard - Gabriel Munteanu         pci_dma_read(&s->dev, s->cb_address + 10 + i, multicast_addr, 6);
8347b8737deSStefan Weil         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;
8377b8737deSStefan Weil         assert(mcast_idx < 64);
8387b8737deSStefan Weil         s->mult[mcast_idx >> 3] |= (1 << (mcast_idx & 7));
8397b8737deSStefan Weil     }
8407b8737deSStefan Weil }
8417b8737deSStefan Weil 
842f3a52e50SStefan Weil static void action_command(EEPRO100State *s)
843f3a52e50SStefan Weil {
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 
849f3a52e50SStefan Weil     for (;;) {
8503d0f4b9bSStefan Weil         bool bit_el;
8513d0f4b9bSStefan Weil         bool bit_s;
8523d0f4b9bSStefan Weil         bool bit_i;
8533d0f4b9bSStefan Weil         bool bit_nc;
85475f5a6ccSStefan Weil         uint16_t ok_status = STATUS_OK;
8553d0f4b9bSStefan Weil         s->cb_address = s->cu_base + s->cu_offset;
8563d0f4b9bSStefan Weil         read_cb(s);
8573d0f4b9bSStefan Weil         bit_el = ((s->tx.command & COMMAND_EL) != 0);
8583d0f4b9bSStefan Weil         bit_s = ((s->tx.command & COMMAND_S) != 0);
8593d0f4b9bSStefan Weil         bit_i = ((s->tx.command & COMMAND_I) != 0);
8603d0f4b9bSStefan Weil         bit_nc = ((s->tx.command & COMMAND_NC) != 0);
8613d0f4b9bSStefan Weil #if 0
8623d0f4b9bSStefan Weil         bool bit_sf = ((s->tx.command & COMMAND_SF) != 0);
8633d0f4b9bSStefan Weil #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 
8713d0f4b9bSStefan Weil         s->cu_offset = s->tx.link;
8723d0f4b9bSStefan Weil         TRACE(OTHER,
8733d0f4b9bSStefan Weil               logout("val=(cu start), status=0x%04x, command=0x%04x, link=0x%08x\n",
8743d0f4b9bSStefan Weil                      s->tx.status, s->tx.command, s->tx.link));
8753d0f4b9bSStefan Weil         switch (s->tx.command & COMMAND_CMD) {
876f3a52e50SStefan Weil         case CmdNOp:
877f3a52e50SStefan Weil             /* Do nothing. */
878f3a52e50SStefan Weil             break;
879f3a52e50SStefan Weil         case CmdIASetup:
88016ef60c9SEduard - Gabriel Munteanu             pci_dma_read(&s->dev, s->cb_address + 8, &s->conf.macaddr.a[0], 6);
881ce0e58b3SStefan Weil             TRACE(OTHER, logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6)));
882f3a52e50SStefan Weil             break;
883f3a52e50SStefan Weil         case CmdConfigure:
88416ef60c9SEduard - Gabriel Munteanu             pci_dma_read(&s->dev, s->cb_address + 8,
88516ef60c9SEduard - Gabriel Munteanu                          &s->configuration[0], sizeof(s->configuration));
886010ec629SStefan Weil             TRACE(OTHER, logout("configuration: %s\n",
887010ec629SStefan Weil                                 nic_dump(&s->configuration[0], 16)));
888010ec629SStefan Weil             TRACE(OTHER, logout("configuration: %s\n",
889010ec629SStefan Weil                                 nic_dump(&s->configuration[16],
890010ec629SStefan Weil                                 ARRAY_SIZE(s->configuration) - 16)));
891010ec629SStefan Weil             if (s->configuration[20] & BIT(6)) {
892010ec629SStefan Weil                 TRACE(OTHER, logout("Multiple IA bit\n"));
893010ec629SStefan Weil             }
894f3a52e50SStefan Weil             break;
895f3a52e50SStefan Weil         case CmdMulticastList:
8967b8737deSStefan Weil             set_multicast_list(s);
897f3a52e50SStefan Weil             break;
898f3a52e50SStefan Weil         case CmdTx:
899f3a52e50SStefan Weil             if (bit_nc) {
900f3a52e50SStefan Weil                 missing("CmdTx: NC = 0");
90175f5a6ccSStefan Weil                 ok_status = 0;
902f3a52e50SStefan Weil                 break;
903f3a52e50SStefan Weil             }
904f3a52e50SStefan Weil             tx_command(s);
905663e8e51Sths             break;
906663e8e51Sths         case CmdTDR:
907aac443e6SStefan Weil             TRACE(OTHER, logout("load microcode\n"));
908663e8e51Sths             /* Starting with offset 8, the command contains
909663e8e51Sths              * 64 dwords microcode which we just ignore here. */
910663e8e51Sths             break;
911f80a7fc3SStefan Weil         case CmdDiagnose:
912f80a7fc3SStefan Weil             TRACE(OTHER, logout("diagnose\n"));
913f80a7fc3SStefan Weil             /* Make sure error flag is not set. */
914f80a7fc3SStefan Weil             s->tx.status = 0;
915f80a7fc3SStefan Weil             break;
916663e8e51Sths         default:
917663e8e51Sths             missing("undefined command");
91875f5a6ccSStefan Weil             ok_status = 0;
9197f1e9d4eSKevin Wolf             break;
920663e8e51Sths         }
9217f1e9d4eSKevin Wolf         /* Write new status. */
92216ef60c9SEduard - Gabriel Munteanu         stw_le_pci_dma(&s->dev, s->cb_address,
923a423a1b5SPhilippe Mathieu-Daudé                        s->tx.status | ok_status | STATUS_C, attrs);
924663e8e51Sths         if (bit_i) {
925663e8e51Sths             /* CU completed action. */
926663e8e51Sths             eepro100_cx_interrupt(s);
927663e8e51Sths         }
928663e8e51Sths         if (bit_el) {
929aac443e6SStefan Weil             /* CU becomes idle. Terminate command loop. */
930663e8e51Sths             set_cu_state(s, cu_idle);
931663e8e51Sths             eepro100_cna_interrupt(s);
9325fa9a0aeSStefan Weil             break;
933663e8e51Sths         } else if (bit_s) {
9345fa9a0aeSStefan Weil             /* CU becomes suspended. Terminate command loop. */
935663e8e51Sths             set_cu_state(s, cu_suspended);
936663e8e51Sths             eepro100_cna_interrupt(s);
9375fa9a0aeSStefan Weil             break;
938663e8e51Sths         } else {
939663e8e51Sths             /* More entries in list. */
940aac443e6SStefan Weil             TRACE(OTHER, logout("CU list with at least one more entry\n"));
9415fa9a0aeSStefan Weil         }
942663e8e51Sths     }
943aac443e6SStefan Weil     TRACE(OTHER, logout("CU list empty\n"));
944663e8e51Sths     /* List is empty. Now CU is idle or suspended. */
9455fa9a0aeSStefan Weil }
9465fa9a0aeSStefan Weil 
9475fa9a0aeSStefan Weil static void eepro100_cu_command(EEPRO100State * s, uint8_t val)
9485fa9a0aeSStefan Weil {
949a423a1b5SPhilippe Mathieu-Daudé     const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
950cb25a3fbSStefan Weil     cu_state_t cu_state;
9515fa9a0aeSStefan Weil     switch (val) {
9525fa9a0aeSStefan Weil     case CU_NOP:
9535fa9a0aeSStefan Weil         /* No operation. */
9545fa9a0aeSStefan Weil         break;
9555fa9a0aeSStefan Weil     case CU_START:
956cb25a3fbSStefan Weil         cu_state = get_cu_state(s);
957cb25a3fbSStefan Weil         if (cu_state != cu_idle && cu_state != cu_suspended) {
958cb25a3fbSStefan Weil             /* Intel documentation says that CU must be idle or suspended
959cb25a3fbSStefan Weil              * for the CU start command. */
960cb25a3fbSStefan Weil             logout("unexpected CU state is %u\n", cu_state);
9615fa9a0aeSStefan Weil         }
9625fa9a0aeSStefan Weil         set_cu_state(s, cu_active);
96327a05006SStefan Weil         s->cu_offset = e100_read_reg4(s, SCBPointer);
9645fa9a0aeSStefan Weil         action_command(s);
965663e8e51Sths         break;
966663e8e51Sths     case CU_RESUME:
967663e8e51Sths         if (get_cu_state(s) != cu_suspended) {
968663e8e51Sths             logout("bad CU resume from CU state %u\n", get_cu_state(s));
969663e8e51Sths             /* Workaround for bad Linux eepro100 driver which resumes
970663e8e51Sths              * from idle state. */
971e7493b25SStefan Weil #if 0
972e7493b25SStefan Weil             missing("cu resume");
973e7493b25SStefan Weil #endif
974663e8e51Sths             set_cu_state(s, cu_suspended);
975663e8e51Sths         }
976663e8e51Sths         if (get_cu_state(s) == cu_suspended) {
977aac443e6SStefan Weil             TRACE(OTHER, logout("CU resuming\n"));
978663e8e51Sths             set_cu_state(s, cu_active);
9795fa9a0aeSStefan Weil             action_command(s);
980663e8e51Sths         }
981663e8e51Sths         break;
982663e8e51Sths     case CU_STATSADDR:
983663e8e51Sths         /* Load dump counters address. */
98427a05006SStefan Weil         s->statsaddr = e100_read_reg4(s, SCBPointer);
985c16ada98SStefan Weil         TRACE(OTHER, logout("val=0x%02x (dump counters address)\n", val));
986c16ada98SStefan Weil         if (s->statsaddr & 3) {
987c16ada98SStefan Weil             /* Memory must be Dword aligned. */
988c16ada98SStefan Weil             logout("unaligned dump counters address\n");
989c16ada98SStefan Weil             /* Handling of misaligned addresses is undefined.
990c16ada98SStefan Weil              * Here we align the address by ignoring the lower bits. */
991c16ada98SStefan Weil             /* TODO: Test unaligned dump counter address on real hardware. */
992c16ada98SStefan Weil             s->statsaddr &= ~3;
993c16ada98SStefan Weil         }
994663e8e51Sths         break;
995663e8e51Sths     case CU_SHOWSTATS:
996663e8e51Sths         /* Dump statistical counters. */
997aac443e6SStefan Weil         TRACE(OTHER, logout("val=0x%02x (dump stats)\n", val));
998663e8e51Sths         dump_statistics(s);
999a423a1b5SPhilippe Mathieu-Daudé         stl_le_pci_dma(&s->dev, s->statsaddr + s->stats_size, 0xa005, attrs);
1000663e8e51Sths         break;
1001663e8e51Sths     case CU_CMD_BASE:
1002663e8e51Sths         /* Load CU base. */
1003aac443e6SStefan Weil         TRACE(OTHER, logout("val=0x%02x (CU base address)\n", val));
100427a05006SStefan Weil         s->cu_base = e100_read_reg4(s, SCBPointer);
1005663e8e51Sths         break;
1006663e8e51Sths     case CU_DUMPSTATS:
1007663e8e51Sths         /* Dump and reset statistical counters. */
1008aac443e6SStefan Weil         TRACE(OTHER, logout("val=0x%02x (dump stats and reset)\n", val));
1009663e8e51Sths         dump_statistics(s);
1010a423a1b5SPhilippe Mathieu-Daudé         stl_le_pci_dma(&s->dev, s->statsaddr + s->stats_size, 0xa007, attrs);
1011663e8e51Sths         memset(&s->statistics, 0, sizeof(s->statistics));
1012663e8e51Sths         break;
1013663e8e51Sths     case CU_SRESUME:
1014663e8e51Sths         /* CU static resume. */
1015663e8e51Sths         missing("CU static resume");
1016663e8e51Sths         break;
1017663e8e51Sths     default:
1018663e8e51Sths         missing("Undefined CU command");
1019663e8e51Sths     }
1020663e8e51Sths }
1021663e8e51Sths 
1022663e8e51Sths static void eepro100_ru_command(EEPRO100State * s, uint8_t val)
1023663e8e51Sths {
1024663e8e51Sths     switch (val) {
1025663e8e51Sths     case RU_NOP:
1026663e8e51Sths         /* No operation. */
1027663e8e51Sths         break;
1028663e8e51Sths     case RX_START:
1029663e8e51Sths         /* RU start. */
1030663e8e51Sths         if (get_ru_state(s) != ru_idle) {
1031663e8e51Sths             logout("RU state is %u, should be %u\n", get_ru_state(s), ru_idle);
1032e7493b25SStefan Weil #if 0
1033e7493b25SStefan Weil             assert(!"wrong RU state");
1034e7493b25SStefan Weil #endif
1035663e8e51Sths         }
1036663e8e51Sths         set_ru_state(s, ru_ready);
103727a05006SStefan Weil         s->ru_offset = e100_read_reg4(s, SCBPointer);
1038b356f76dSJason Wang         qemu_flush_queued_packets(qemu_get_queue(s->nic));
1039aac443e6SStefan Weil         TRACE(OTHER, logout("val=0x%02x (rx start)\n", val));
1040663e8e51Sths         break;
1041663e8e51Sths     case RX_RESUME:
1042663e8e51Sths         /* Restart RU. */
1043663e8e51Sths         if (get_ru_state(s) != ru_suspended) {
1044663e8e51Sths             logout("RU state is %u, should be %u\n", get_ru_state(s),
1045663e8e51Sths                    ru_suspended);
1046e7493b25SStefan Weil #if 0
1047e7493b25SStefan Weil             assert(!"wrong RU state");
1048e7493b25SStefan Weil #endif
1049663e8e51Sths         }
1050663e8e51Sths         set_ru_state(s, ru_ready);
1051663e8e51Sths         break;
1052e824012bSStefan Weil     case RU_ABORT:
1053e824012bSStefan Weil         /* RU abort. */
1054e824012bSStefan Weil         if (get_ru_state(s) == ru_ready) {
1055e824012bSStefan Weil             eepro100_rnr_interrupt(s);
1056e824012bSStefan Weil         }
1057e824012bSStefan Weil         set_ru_state(s, ru_idle);
1058e824012bSStefan Weil         break;
1059663e8e51Sths     case RX_ADDR_LOAD:
1060663e8e51Sths         /* Load RU base. */
1061aac443e6SStefan Weil         TRACE(OTHER, logout("val=0x%02x (RU base address)\n", val));
106227a05006SStefan Weil         s->ru_base = e100_read_reg4(s, SCBPointer);
1063663e8e51Sths         break;
1064663e8e51Sths     default:
1065663e8e51Sths         logout("val=0x%02x (undefined RU command)\n", val);
1066663e8e51Sths         missing("Undefined SU command");
1067663e8e51Sths     }
1068663e8e51Sths }
1069663e8e51Sths 
1070663e8e51Sths static void eepro100_write_command(EEPRO100State * s, uint8_t val)
1071663e8e51Sths {
1072663e8e51Sths     eepro100_ru_command(s, val & 0x0f);
1073663e8e51Sths     eepro100_cu_command(s, val & 0xf0);
1074663e8e51Sths     if ((val) == 0) {
1075aac443e6SStefan Weil         TRACE(OTHER, logout("val=0x%02x\n", val));
1076663e8e51Sths     }
1077663e8e51Sths     /* Clear command byte after command was accepted. */
1078663e8e51Sths     s->mem[SCBCmd] = 0;
1079663e8e51Sths }
1080663e8e51Sths 
1081663e8e51Sths /*****************************************************************************
1082663e8e51Sths  *
1083663e8e51Sths  * EEPROM emulation.
1084663e8e51Sths  *
1085663e8e51Sths  ****************************************************************************/
1086663e8e51Sths 
1087663e8e51Sths #define EEPROM_CS       0x02
1088663e8e51Sths #define EEPROM_SK       0x01
1089663e8e51Sths #define EEPROM_DI       0x04
1090663e8e51Sths #define EEPROM_DO       0x08
1091663e8e51Sths 
1092663e8e51Sths static uint16_t eepro100_read_eeprom(EEPRO100State * s)
1093663e8e51Sths {
1094e5e23ab8SStefan Weil     uint16_t val = e100_read_reg2(s, SCBeeprom);
1095663e8e51Sths     if (eeprom93xx_read(s->eeprom)) {
1096663e8e51Sths         val |= EEPROM_DO;
1097663e8e51Sths     } else {
1098663e8e51Sths         val &= ~EEPROM_DO;
1099663e8e51Sths     }
1100aac443e6SStefan Weil     TRACE(EEPROM, logout("val=0x%04x\n", val));
1101663e8e51Sths     return val;
1102663e8e51Sths }
1103663e8e51Sths 
1104c227f099SAnthony Liguori static void eepro100_write_eeprom(eeprom_t * eeprom, uint8_t val)
1105663e8e51Sths {
1106aac443e6SStefan Weil     TRACE(EEPROM, logout("val=0x%02x\n", val));
1107663e8e51Sths 
1108ebabb67aSStefan Weil     /* mask unwritable bits */
1109e7493b25SStefan Weil #if 0
1110e7493b25SStefan Weil     val = SET_MASKED(val, 0x31, eeprom->value);
1111e7493b25SStefan Weil #endif
1112663e8e51Sths 
1113663e8e51Sths     int eecs = ((val & EEPROM_CS) != 0);
1114663e8e51Sths     int eesk = ((val & EEPROM_SK) != 0);
1115663e8e51Sths     int eedi = ((val & EEPROM_DI) != 0);
1116663e8e51Sths     eeprom93xx_write(eeprom, eecs, eesk, eedi);
1117663e8e51Sths }
1118663e8e51Sths 
1119663e8e51Sths /*****************************************************************************
1120663e8e51Sths  *
1121663e8e51Sths  * MDI emulation.
1122663e8e51Sths  *
1123663e8e51Sths  ****************************************************************************/
1124663e8e51Sths 
1125663e8e51Sths #if defined(DEBUG_EEPRO100)
11266a0b9cc9SReimar Döffinger static const char * const mdi_op_name[] = {
1127663e8e51Sths     "opcode 0",
1128663e8e51Sths     "write",
1129663e8e51Sths     "read",
1130663e8e51Sths     "opcode 3"
1131663e8e51Sths };
1132663e8e51Sths 
11336a0b9cc9SReimar Döffinger static const char * const mdi_reg_name[] = {
1134663e8e51Sths     "Control",
1135663e8e51Sths     "Status",
1136663e8e51Sths     "PHY Identification (Word 1)",
1137663e8e51Sths     "PHY Identification (Word 2)",
1138663e8e51Sths     "Auto-Negotiation Advertisement",
1139663e8e51Sths     "Auto-Negotiation Link Partner Ability",
1140663e8e51Sths     "Auto-Negotiation Expansion"
1141663e8e51Sths };
1142aac443e6SStefan Weil 
1143aac443e6SStefan Weil static const char *reg2name(uint8_t reg)
1144aac443e6SStefan Weil {
1145aac443e6SStefan Weil     static char buffer[10];
1146aac443e6SStefan Weil     const char *p = buffer;
1147aac443e6SStefan Weil     if (reg < ARRAY_SIZE(mdi_reg_name)) {
1148aac443e6SStefan Weil         p = mdi_reg_name[reg];
1149aac443e6SStefan Weil     } else {
1150aac443e6SStefan Weil         snprintf(buffer, sizeof(buffer), "reg=0x%02x", reg);
1151aac443e6SStefan Weil     }
1152aac443e6SStefan Weil     return p;
1153aac443e6SStefan Weil }
1154663e8e51Sths #endif                          /* DEBUG_EEPRO100 */
1155663e8e51Sths 
1156663e8e51Sths static uint32_t eepro100_read_mdi(EEPRO100State * s)
1157663e8e51Sths {
1158e5e23ab8SStefan Weil     uint32_t val = e100_read_reg4(s, SCBCtrlMDI);
1159663e8e51Sths 
1160663e8e51Sths #ifdef DEBUG_EEPRO100
1161663e8e51Sths     uint8_t raiseint = (val & BIT(29)) >> 29;
1162663e8e51Sths     uint8_t opcode = (val & BITS(27, 26)) >> 26;
1163663e8e51Sths     uint8_t phy = (val & BITS(25, 21)) >> 21;
1164663e8e51Sths     uint8_t reg = (val & BITS(20, 16)) >> 16;
1165663e8e51Sths     uint16_t data = (val & BITS(15, 0));
1166663e8e51Sths #endif
1167663e8e51Sths     /* Emulation takes no time to finish MDI transaction. */
1168663e8e51Sths     val |= BIT(28);
1169663e8e51Sths     TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
1170663e8e51Sths                       val, raiseint, mdi_op_name[opcode], phy,
1171aac443e6SStefan Weil                       reg2name(reg), data));
1172663e8e51Sths     return val;
1173663e8e51Sths }
1174663e8e51Sths 
11750113f48dSStefan Weil static void eepro100_write_mdi(EEPRO100State *s)
1176663e8e51Sths {
11770113f48dSStefan Weil     uint32_t val = e100_read_reg4(s, SCBCtrlMDI);
1178663e8e51Sths     uint8_t raiseint = (val & BIT(29)) >> 29;
1179663e8e51Sths     uint8_t opcode = (val & BITS(27, 26)) >> 26;
1180663e8e51Sths     uint8_t phy = (val & BITS(25, 21)) >> 21;
1181663e8e51Sths     uint8_t reg = (val & BITS(20, 16)) >> 16;
1182663e8e51Sths     uint16_t data = (val & BITS(15, 0));
1183aac443e6SStefan Weil     TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
1184aac443e6SStefan Weil           val, raiseint, mdi_op_name[opcode], phy, reg2name(reg), data));
1185663e8e51Sths     if (phy != 1) {
1186663e8e51Sths         /* Unsupported PHY address. */
1187e7493b25SStefan Weil #if 0
1188e7493b25SStefan Weil         logout("phy must be 1 but is %u\n", phy);
1189e7493b25SStefan Weil #endif
1190663e8e51Sths         data = 0;
1191663e8e51Sths     } else if (opcode != 1 && opcode != 2) {
1192663e8e51Sths         /* Unsupported opcode. */
1193663e8e51Sths         logout("opcode must be 1 or 2 but is %u\n", opcode);
1194663e8e51Sths         data = 0;
1195663e8e51Sths     } else if (reg > 6) {
1196663e8e51Sths         /* Unsupported register. */
1197663e8e51Sths         logout("register must be 0...6 but is %u\n", reg);
1198663e8e51Sths         data = 0;
1199663e8e51Sths     } else {
1200663e8e51Sths         TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
1201663e8e51Sths                           val, raiseint, mdi_op_name[opcode], phy,
1202aac443e6SStefan Weil                           reg2name(reg), data));
1203663e8e51Sths         if (opcode == 1) {
1204663e8e51Sths             /* MDI write */
1205663e8e51Sths             switch (reg) {
1206663e8e51Sths             case 0:            /* Control Register */
1207663e8e51Sths                 if (data & 0x8000) {
1208663e8e51Sths                     /* Reset status and control registers to default. */
1209663e8e51Sths                     s->mdimem[0] = eepro100_mdi_default[0];
1210663e8e51Sths                     s->mdimem[1] = eepro100_mdi_default[1];
1211663e8e51Sths                     data = s->mdimem[reg];
1212663e8e51Sths                 } else {
1213663e8e51Sths                     /* Restart Auto Configuration = Normal Operation */
1214663e8e51Sths                     data &= ~0x0200;
1215663e8e51Sths                 }
1216663e8e51Sths                 break;
1217663e8e51Sths             case 1:            /* Status Register */
1218663e8e51Sths                 missing("not writable");
1219663e8e51Sths                 break;
1220663e8e51Sths             case 2:            /* PHY Identification Register (Word 1) */
1221663e8e51Sths             case 3:            /* PHY Identification Register (Word 2) */
1222663e8e51Sths                 missing("not implemented");
1223663e8e51Sths                 break;
1224663e8e51Sths             case 4:            /* Auto-Negotiation Advertisement Register */
1225663e8e51Sths             case 5:            /* Auto-Negotiation Link Partner Ability Register */
1226663e8e51Sths                 break;
1227663e8e51Sths             case 6:            /* Auto-Negotiation Expansion Register */
1228663e8e51Sths             default:
1229663e8e51Sths                 missing("not implemented");
1230663e8e51Sths             }
12315e80dd22SPeter Maydell             s->mdimem[reg] &= eepro100_mdi_mask[reg];
12325e80dd22SPeter Maydell             s->mdimem[reg] |= data & ~eepro100_mdi_mask[reg];
1233663e8e51Sths         } else if (opcode == 2) {
1234663e8e51Sths             /* MDI read */
1235663e8e51Sths             switch (reg) {
1236663e8e51Sths             case 0:            /* Control Register */
1237663e8e51Sths                 if (data & 0x8000) {
1238663e8e51Sths                     /* Reset status and control registers to default. */
1239663e8e51Sths                     s->mdimem[0] = eepro100_mdi_default[0];
1240663e8e51Sths                     s->mdimem[1] = eepro100_mdi_default[1];
1241663e8e51Sths                 }
1242663e8e51Sths                 break;
1243663e8e51Sths             case 1:            /* Status Register */
1244663e8e51Sths                 s->mdimem[reg] |= 0x0020;
1245663e8e51Sths                 break;
1246663e8e51Sths             case 2:            /* PHY Identification Register (Word 1) */
1247663e8e51Sths             case 3:            /* PHY Identification Register (Word 2) */
1248663e8e51Sths             case 4:            /* Auto-Negotiation Advertisement Register */
1249663e8e51Sths                 break;
1250663e8e51Sths             case 5:            /* Auto-Negotiation Link Partner Ability Register */
1251663e8e51Sths                 s->mdimem[reg] = 0x41fe;
1252663e8e51Sths                 break;
1253663e8e51Sths             case 6:            /* Auto-Negotiation Expansion Register */
1254663e8e51Sths                 s->mdimem[reg] = 0x0001;
1255663e8e51Sths                 break;
1256663e8e51Sths             }
1257663e8e51Sths             data = s->mdimem[reg];
1258663e8e51Sths         }
1259663e8e51Sths         /* Emulation takes no time to finish MDI transaction.
1260663e8e51Sths          * Set MDI bit in SCB status register. */
1261663e8e51Sths         s->mem[SCBAck] |= 0x08;
1262663e8e51Sths         val |= BIT(28);
1263663e8e51Sths         if (raiseint) {
1264663e8e51Sths             eepro100_mdi_interrupt(s);
1265663e8e51Sths         }
1266663e8e51Sths     }
1267663e8e51Sths     val = (val & 0xffff0000) + data;
1268e5e23ab8SStefan Weil     e100_write_reg4(s, SCBCtrlMDI, val);
1269663e8e51Sths }
1270663e8e51Sths 
1271663e8e51Sths /*****************************************************************************
1272663e8e51Sths  *
1273663e8e51Sths  * Port emulation.
1274663e8e51Sths  *
1275663e8e51Sths  ****************************************************************************/
1276663e8e51Sths 
1277663e8e51Sths #define PORT_SOFTWARE_RESET     0
1278663e8e51Sths #define PORT_SELFTEST           1
1279663e8e51Sths #define PORT_SELECTIVE_RESET    2
1280663e8e51Sths #define PORT_DUMP               3
1281663e8e51Sths #define PORT_SELECTION_MASK     3
1282663e8e51Sths 
1283663e8e51Sths typedef struct {
1284663e8e51Sths     uint32_t st_sign;           /* Self Test Signature */
1285663e8e51Sths     uint32_t st_result;         /* Self Test Results */
1286c227f099SAnthony Liguori } eepro100_selftest_t;
1287663e8e51Sths 
1288663e8e51Sths static uint32_t eepro100_read_port(EEPRO100State * s)
1289663e8e51Sths {
1290663e8e51Sths     return 0;
1291663e8e51Sths }
1292663e8e51Sths 
12933fd3d0b4SStefan Weil static void eepro100_write_port(EEPRO100State *s)
1294663e8e51Sths {
12953fd3d0b4SStefan Weil     uint32_t val = e100_read_reg4(s, SCBPort);
1296663e8e51Sths     uint32_t address = (val & ~PORT_SELECTION_MASK);
1297663e8e51Sths     uint8_t selection = (val & PORT_SELECTION_MASK);
1298663e8e51Sths     switch (selection) {
1299663e8e51Sths     case PORT_SOFTWARE_RESET:
1300663e8e51Sths         nic_reset(s);
1301663e8e51Sths         break;
1302663e8e51Sths     case PORT_SELFTEST:
1303aac443e6SStefan Weil         TRACE(OTHER, logout("selftest address=0x%08x\n", address));
1304c227f099SAnthony Liguori         eepro100_selftest_t data;
130516ef60c9SEduard - Gabriel Munteanu         pci_dma_read(&s->dev, address, (uint8_t *) &data, sizeof(data));
1306663e8e51Sths         data.st_sign = 0xffffffff;
1307663e8e51Sths         data.st_result = 0;
130816ef60c9SEduard - Gabriel Munteanu         pci_dma_write(&s->dev, address, (uint8_t *) &data, sizeof(data));
1309663e8e51Sths         break;
1310663e8e51Sths     case PORT_SELECTIVE_RESET:
1311aac443e6SStefan Weil         TRACE(OTHER, logout("selective reset, selftest address=0x%08x\n", address));
1312663e8e51Sths         nic_selective_reset(s);
1313663e8e51Sths         break;
1314663e8e51Sths     default:
1315663e8e51Sths         logout("val=0x%08x\n", val);
1316663e8e51Sths         missing("unknown port selection");
1317663e8e51Sths     }
1318663e8e51Sths }
1319663e8e51Sths 
1320663e8e51Sths /*****************************************************************************
1321663e8e51Sths  *
1322663e8e51Sths  * General hardware emulation.
1323663e8e51Sths  *
1324663e8e51Sths  ****************************************************************************/
1325663e8e51Sths 
1326663e8e51Sths static uint8_t eepro100_read1(EEPRO100State * s, uint32_t addr)
1327663e8e51Sths {
1328ef476062SBlue Swirl     uint8_t val = 0;
1329663e8e51Sths     if (addr <= sizeof(s->mem) - sizeof(val)) {
1330e5e23ab8SStefan Weil         val = s->mem[addr];
1331663e8e51Sths     }
1332663e8e51Sths 
1333663e8e51Sths     switch (addr) {
1334663e8e51Sths     case SCBStatus:
1335663e8e51Sths     case SCBAck:
1336aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1337663e8e51Sths         break;
1338663e8e51Sths     case SCBCmd:
1339aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1340e7493b25SStefan Weil #if 0
1341e7493b25SStefan Weil         val = eepro100_read_command(s);
1342e7493b25SStefan Weil #endif
1343663e8e51Sths         break;
1344663e8e51Sths     case SCBIntmask:
1345aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1346663e8e51Sths         break;
1347663e8e51Sths     case SCBPort + 3:
1348aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1349663e8e51Sths         break;
1350663e8e51Sths     case SCBeeprom:
1351663e8e51Sths         val = eepro100_read_eeprom(s);
1352663e8e51Sths         break;
13530113f48dSStefan Weil     case SCBCtrlMDI:
13540113f48dSStefan Weil     case SCBCtrlMDI + 1:
13550113f48dSStefan Weil     case SCBCtrlMDI + 2:
13560113f48dSStefan Weil     case SCBCtrlMDI + 3:
13570113f48dSStefan Weil         val = (uint8_t)(eepro100_read_mdi(s) >> (8 * (addr & 3)));
13580113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
13590113f48dSStefan Weil         break;
13600908bba1SStefan Weil     case SCBpmdr:       /* Power Management Driver Register */
1361663e8e51Sths         val = 0;
1362aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1363663e8e51Sths         break;
1364a39bd017SStefan Weil     case SCBgctrl:      /* General Control Register */
1365a39bd017SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1366a39bd017SStefan Weil         break;
13670908bba1SStefan Weil     case SCBgstat:      /* General Status Register */
1368663e8e51Sths         /* 100 Mbps full duplex, valid link */
1369663e8e51Sths         val = 0x07;
1370aac443e6SStefan Weil         TRACE(OTHER, logout("addr=General Status val=%02x\n", val));
1371663e8e51Sths         break;
1372663e8e51Sths     default:
1373663e8e51Sths         logout("addr=%s val=0x%02x\n", regname(addr), val);
1374663e8e51Sths         missing("unknown byte read");
1375663e8e51Sths     }
1376663e8e51Sths     return val;
1377663e8e51Sths }
1378663e8e51Sths 
1379663e8e51Sths static uint16_t eepro100_read2(EEPRO100State * s, uint32_t addr)
1380663e8e51Sths {
1381ef476062SBlue Swirl     uint16_t val = 0;
1382663e8e51Sths     if (addr <= sizeof(s->mem) - sizeof(val)) {
1383e5e23ab8SStefan Weil         val = e100_read_reg2(s, addr);
1384663e8e51Sths     }
1385663e8e51Sths 
1386663e8e51Sths     switch (addr) {
1387663e8e51Sths     case SCBStatus:
1388dbbaaff6S=?UTF-8?q?Reimar=20D=C3=B6ffinger?=     case SCBCmd:
1389aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
1390663e8e51Sths         break;
1391663e8e51Sths     case SCBeeprom:
1392663e8e51Sths         val = eepro100_read_eeprom(s);
1393aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
1394663e8e51Sths         break;
13950113f48dSStefan Weil     case SCBCtrlMDI:
13960113f48dSStefan Weil     case SCBCtrlMDI + 2:
13970113f48dSStefan Weil         val = (uint16_t)(eepro100_read_mdi(s) >> (8 * (addr & 3)));
13980113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
13990113f48dSStefan Weil         break;
1400663e8e51Sths     default:
1401663e8e51Sths         logout("addr=%s val=0x%04x\n", regname(addr), val);
1402663e8e51Sths         missing("unknown word read");
1403663e8e51Sths     }
1404663e8e51Sths     return val;
1405663e8e51Sths }
1406663e8e51Sths 
1407663e8e51Sths static uint32_t eepro100_read4(EEPRO100State * s, uint32_t addr)
1408663e8e51Sths {
1409ef476062SBlue Swirl     uint32_t val = 0;
1410663e8e51Sths     if (addr <= sizeof(s->mem) - sizeof(val)) {
1411e5e23ab8SStefan Weil         val = e100_read_reg4(s, addr);
1412663e8e51Sths     }
1413663e8e51Sths 
1414663e8e51Sths     switch (addr) {
1415663e8e51Sths     case SCBStatus:
1416aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1417663e8e51Sths         break;
1418663e8e51Sths     case SCBPointer:
1419aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1420663e8e51Sths         break;
1421663e8e51Sths     case SCBPort:
1422663e8e51Sths         val = eepro100_read_port(s);
1423aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1424663e8e51Sths         break;
1425072476eaSStefan Weil     case SCBflash:
1426072476eaSStefan Weil         val = eepro100_read_eeprom(s);
1427072476eaSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1428072476eaSStefan Weil         break;
1429663e8e51Sths     case SCBCtrlMDI:
1430663e8e51Sths         val = eepro100_read_mdi(s);
1431663e8e51Sths         break;
1432663e8e51Sths     default:
1433663e8e51Sths         logout("addr=%s val=0x%08x\n", regname(addr), val);
1434663e8e51Sths         missing("unknown longword read");
1435663e8e51Sths     }
1436663e8e51Sths     return val;
1437663e8e51Sths }
1438663e8e51Sths 
1439663e8e51Sths static void eepro100_write1(EEPRO100State * s, uint32_t addr, uint8_t val)
1440663e8e51Sths {
1441e74818f3SStefan Weil     /* SCBStatus is readonly. */
1442e74818f3SStefan Weil     if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) {
1443e5e23ab8SStefan Weil         s->mem[addr] = val;
1444663e8e51Sths     }
1445663e8e51Sths 
1446663e8e51Sths     switch (addr) {
1447663e8e51Sths     case SCBStatus:
14481b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1449663e8e51Sths         break;
1450663e8e51Sths     case SCBAck:
14511b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1452663e8e51Sths         eepro100_acknowledge(s);
1453663e8e51Sths         break;
1454663e8e51Sths     case SCBCmd:
14551b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1456663e8e51Sths         eepro100_write_command(s, val);
1457663e8e51Sths         break;
1458663e8e51Sths     case SCBIntmask:
14591b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1460663e8e51Sths         if (val & BIT(1)) {
1461663e8e51Sths             eepro100_swi_interrupt(s);
1462663e8e51Sths         }
1463663e8e51Sths         eepro100_interrupt(s, 0);
1464663e8e51Sths         break;
146527a05006SStefan Weil     case SCBPointer:
146627a05006SStefan Weil     case SCBPointer + 1:
146727a05006SStefan Weil     case SCBPointer + 2:
146827a05006SStefan Weil     case SCBPointer + 3:
146927a05006SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
147027a05006SStefan Weil         break;
14713fd3d0b4SStefan Weil     case SCBPort:
14723fd3d0b4SStefan Weil     case SCBPort + 1:
14733fd3d0b4SStefan Weil     case SCBPort + 2:
14743fd3d0b4SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
14753fd3d0b4SStefan Weil         break;
1476663e8e51Sths     case SCBPort + 3:
14773fd3d0b4SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
14783fd3d0b4SStefan Weil         eepro100_write_port(s);
14793fd3d0b4SStefan Weil         break;
1480aac443e6SStefan Weil     case SCBFlow:       /* does not exist on 82557 */
14813257d2b6Sths     case SCBFlow + 1:
14823257d2b6Sths     case SCBFlow + 2:
14830908bba1SStefan Weil     case SCBpmdr:       /* does not exist on 82557 */
1484aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1485663e8e51Sths         break;
1486663e8e51Sths     case SCBeeprom:
14871b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1488663e8e51Sths         eepro100_write_eeprom(s->eeprom, val);
1489663e8e51Sths         break;
14900113f48dSStefan Weil     case SCBCtrlMDI:
14910113f48dSStefan Weil     case SCBCtrlMDI + 1:
14920113f48dSStefan Weil     case SCBCtrlMDI + 2:
14930113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
14940113f48dSStefan Weil         break;
14950113f48dSStefan Weil     case SCBCtrlMDI + 3:
14960113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
14970113f48dSStefan Weil         eepro100_write_mdi(s);
14980113f48dSStefan Weil         break;
1499663e8e51Sths     default:
1500663e8e51Sths         logout("addr=%s val=0x%02x\n", regname(addr), val);
1501663e8e51Sths         missing("unknown byte write");
1502663e8e51Sths     }
1503663e8e51Sths }
1504663e8e51Sths 
1505663e8e51Sths static void eepro100_write2(EEPRO100State * s, uint32_t addr, uint16_t val)
1506663e8e51Sths {
1507e74818f3SStefan Weil     /* SCBStatus is readonly. */
1508e74818f3SStefan Weil     if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) {
1509e5e23ab8SStefan Weil         e100_write_reg2(s, addr, val);
1510663e8e51Sths     }
1511663e8e51Sths 
1512663e8e51Sths     switch (addr) {
1513663e8e51Sths     case SCBStatus:
15141b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
1515e74818f3SStefan Weil         s->mem[SCBAck] = (val >> 8);
1516663e8e51Sths         eepro100_acknowledge(s);
1517663e8e51Sths         break;
1518663e8e51Sths     case SCBCmd:
15191b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
1520663e8e51Sths         eepro100_write_command(s, val);
1521663e8e51Sths         eepro100_write1(s, SCBIntmask, val >> 8);
1522663e8e51Sths         break;
152327a05006SStefan Weil     case SCBPointer:
152427a05006SStefan Weil     case SCBPointer + 2:
152527a05006SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
152627a05006SStefan Weil         break;
15273fd3d0b4SStefan Weil     case SCBPort:
15283fd3d0b4SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
15293fd3d0b4SStefan Weil         break;
15303fd3d0b4SStefan Weil     case SCBPort + 2:
15313fd3d0b4SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
15323fd3d0b4SStefan Weil         eepro100_write_port(s);
15333fd3d0b4SStefan Weil         break;
1534663e8e51Sths     case SCBeeprom:
15351b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
1536663e8e51Sths         eepro100_write_eeprom(s->eeprom, val);
1537663e8e51Sths         break;
15380113f48dSStefan Weil     case SCBCtrlMDI:
15390113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
15400113f48dSStefan Weil         break;
15410113f48dSStefan Weil     case SCBCtrlMDI + 2:
15420113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
15430113f48dSStefan Weil         eepro100_write_mdi(s);
15440113f48dSStefan Weil         break;
1545663e8e51Sths     default:
1546663e8e51Sths         logout("addr=%s val=0x%04x\n", regname(addr), val);
1547663e8e51Sths         missing("unknown word write");
1548663e8e51Sths     }
1549663e8e51Sths }
1550663e8e51Sths 
1551663e8e51Sths static void eepro100_write4(EEPRO100State * s, uint32_t addr, uint32_t val)
1552663e8e51Sths {
1553663e8e51Sths     if (addr <= sizeof(s->mem) - sizeof(val)) {
1554e5e23ab8SStefan Weil         e100_write_reg4(s, addr, val);
1555663e8e51Sths     }
1556663e8e51Sths 
1557663e8e51Sths     switch (addr) {
1558663e8e51Sths     case SCBPointer:
155927a05006SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1560663e8e51Sths         break;
1561663e8e51Sths     case SCBPort:
1562aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
15633fd3d0b4SStefan Weil         eepro100_write_port(s);
1564663e8e51Sths         break;
1565072476eaSStefan Weil     case SCBflash:
1566072476eaSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1567072476eaSStefan Weil         val = val >> 16;
1568072476eaSStefan Weil         eepro100_write_eeprom(s->eeprom, val);
1569072476eaSStefan Weil         break;
1570663e8e51Sths     case SCBCtrlMDI:
15710113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
15720113f48dSStefan Weil         eepro100_write_mdi(s);
1573663e8e51Sths         break;
1574663e8e51Sths     default:
1575663e8e51Sths         logout("addr=%s val=0x%08x\n", regname(addr), val);
1576663e8e51Sths         missing("unknown longword write");
1577663e8e51Sths     }
1578663e8e51Sths }
1579663e8e51Sths 
1580a8170e5eSAvi Kivity static uint64_t eepro100_read(void *opaque, hwaddr addr,
15815e6ffddeSAvi Kivity                               unsigned size)
1582663e8e51Sths {
1583663e8e51Sths     EEPRO100State *s = opaque;
15845e6ffddeSAvi Kivity 
15855e6ffddeSAvi Kivity     switch (size) {
15865e6ffddeSAvi Kivity     case 1: return eepro100_read1(s, addr);
15875e6ffddeSAvi Kivity     case 2: return eepro100_read2(s, addr);
15885e6ffddeSAvi Kivity     case 4: return eepro100_read4(s, addr);
15895e6ffddeSAvi Kivity     default: abort();
15905e6ffddeSAvi Kivity     }
1591663e8e51Sths }
1592663e8e51Sths 
1593a8170e5eSAvi Kivity static void eepro100_write(void *opaque, hwaddr addr,
15945e6ffddeSAvi Kivity                            uint64_t data, unsigned size)
1595663e8e51Sths {
1596663e8e51Sths     EEPRO100State *s = opaque;
15975e6ffddeSAvi Kivity 
15985e6ffddeSAvi Kivity     switch (size) {
15990ed8b6f6SBlue Swirl     case 1:
16000ed8b6f6SBlue Swirl         eepro100_write1(s, addr, data);
16010ed8b6f6SBlue Swirl         break;
16020ed8b6f6SBlue Swirl     case 2:
16030ed8b6f6SBlue Swirl         eepro100_write2(s, addr, data);
16040ed8b6f6SBlue Swirl         break;
16050ed8b6f6SBlue Swirl     case 4:
16060ed8b6f6SBlue Swirl         eepro100_write4(s, addr, data);
16070ed8b6f6SBlue Swirl         break;
16080ed8b6f6SBlue Swirl     default:
16090ed8b6f6SBlue Swirl         abort();
16105e6ffddeSAvi Kivity     }
1611663e8e51Sths }
1612663e8e51Sths 
16135e6ffddeSAvi Kivity static const MemoryRegionOps eepro100_ops = {
16145e6ffddeSAvi Kivity     .read = eepro100_read,
16155e6ffddeSAvi Kivity     .write = eepro100_write,
16165e6ffddeSAvi Kivity     .endianness = DEVICE_LITTLE_ENDIAN,
1617663e8e51Sths };
1618663e8e51Sths 
16194e68f7a0SStefan Hajnoczi static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
1620663e8e51Sths {
1621663e8e51Sths     /* TODO:
1622663e8e51Sths      * - Magic packets should set bit 30 in power management driver register.
1623663e8e51Sths      * - Interesting packets should set bit 29 in power management driver register.
1624663e8e51Sths      */
1625a423a1b5SPhilippe Mathieu-Daudé     const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
1626cc1f0f45SJason Wang     EEPRO100State *s = qemu_get_nic_opaque(nc);
1627663e8e51Sths     uint16_t rfd_status = 0xa000;
1628792f1d63SStefan Weil #if defined(CONFIG_PAD_RECEIVED_FRAMES)
1629792f1d63SStefan Weil     uint8_t min_buf[60];
1630792f1d63SStefan Weil #endif
1631663e8e51Sths     static const uint8_t broadcast_macaddr[6] =
1632663e8e51Sths         { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
1633663e8e51Sths 
1634792f1d63SStefan Weil #if defined(CONFIG_PAD_RECEIVED_FRAMES)
1635792f1d63SStefan Weil     /* Pad to minimum Ethernet frame length */
1636792f1d63SStefan Weil     if (size < sizeof(min_buf)) {
1637792f1d63SStefan Weil         memcpy(min_buf, buf, size);
1638792f1d63SStefan Weil         memset(&min_buf[size], 0, sizeof(min_buf) - size);
1639792f1d63SStefan Weil         buf = min_buf;
1640792f1d63SStefan Weil         size = sizeof(min_buf);
1641792f1d63SStefan Weil     }
1642792f1d63SStefan Weil #endif
1643792f1d63SStefan Weil 
1644663e8e51Sths     if (s->configuration[8] & 0x80) {
1645663e8e51Sths         /* CSMA is disabled. */
1646663e8e51Sths         logout("%p received while CSMA is disabled\n", s);
16474f1c942bSMark McLoughlin         return -1;
1648792f1d63SStefan Weil #if !defined(CONFIG_PAD_RECEIVED_FRAMES)
1649ced5296aSStefan Weil     } else if (size < 64 && (s->configuration[7] & BIT(0))) {
1650663e8e51Sths         /* Short frame and configuration byte 7/0 (discard short receive) set:
1651663e8e51Sths          * Short frame is discarded */
1652067d01deSStefan Weil         logout("%p received short frame (%zu byte)\n", s, size);
1653663e8e51Sths         s->statistics.rx_short_frame_errors++;
1654e7493b25SStefan Weil         return -1;
1655e7493b25SStefan Weil #endif
1656ced5296aSStefan Weil     } else if ((size > MAX_ETH_FRAME_SIZE + 4) && !(s->configuration[18] & BIT(3))) {
1657663e8e51Sths         /* Long frame and configuration byte 18/3 (long receive ok) not set:
1658663e8e51Sths          * Long frames are discarded. */
1659067d01deSStefan Weil         logout("%p received long frame (%zu byte), ignored\n", s, size);
16604f1c942bSMark McLoughlin         return -1;
1661e7493b25SStefan Weil     } else if (memcmp(buf, s->conf.macaddr.a, 6) == 0) {       /* !!! */
1662663e8e51Sths         /* Frame matches individual address. */
1663663e8e51Sths         /* TODO: check configuration byte 15/4 (ignore U/L). */
1664067d01deSStefan Weil         TRACE(RXTX, logout("%p received frame for me, len=%zu\n", s, size));
1665663e8e51Sths     } else if (memcmp(buf, broadcast_macaddr, 6) == 0) {
1666663e8e51Sths         /* Broadcast frame. */
1667067d01deSStefan Weil         TRACE(RXTX, logout("%p received broadcast, len=%zu\n", s, size));
1668663e8e51Sths         rfd_status |= 0x0002;
16697b8737deSStefan Weil     } else if (buf[0] & 0x01) {
1670663e8e51Sths         /* Multicast frame. */
16717b8737deSStefan Weil         TRACE(RXTX, logout("%p received multicast, len=%zu,%s\n", s, size, nic_dump(buf, size)));
16727f1e9d4eSKevin Wolf         if (s->configuration[21] & BIT(3)) {
16737b8737deSStefan Weil           /* Multicast all bit is set, receive all multicast frames. */
16747b8737deSStefan Weil         } else {
16757c0348bdSMark Cave-Ayland           unsigned mcast_idx = (net_crc32(buf, ETH_ALEN) & BITS(7, 2)) >> 2;
16767b8737deSStefan Weil           assert(mcast_idx < 64);
16777b8737deSStefan Weil           if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) {
16787b8737deSStefan Weil             /* Multicast frame is allowed in hash table. */
1679ced5296aSStefan Weil           } else if (s->configuration[15] & BIT(0)) {
16807b8737deSStefan Weil               /* Promiscuous: receive all. */
16817b8737deSStefan Weil               rfd_status |= 0x0004;
16827b8737deSStefan Weil           } else {
16837b8737deSStefan Weil               TRACE(RXTX, logout("%p multicast ignored\n", s));
16847b8737deSStefan Weil               return -1;
16857f1e9d4eSKevin Wolf           }
1686663e8e51Sths         }
16877b8737deSStefan Weil         /* TODO: Next not for promiscuous mode? */
1688663e8e51Sths         rfd_status |= 0x0002;
1689ced5296aSStefan Weil     } else if (s->configuration[15] & BIT(0)) {
1690663e8e51Sths         /* Promiscuous: receive all. */
1691067d01deSStefan Weil         TRACE(RXTX, logout("%p received frame in promiscuous mode, len=%zu\n", s, size));
1692663e8e51Sths         rfd_status |= 0x0004;
1693010ec629SStefan Weil     } else if (s->configuration[20] & BIT(6)) {
1694010ec629SStefan Weil         /* Multiple IA bit set. */
1695d00d6d00SMark Cave-Ayland         unsigned mcast_idx = net_crc32(buf, ETH_ALEN) >> 26;
1696010ec629SStefan Weil         assert(mcast_idx < 64);
1697010ec629SStefan Weil         if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) {
1698010ec629SStefan Weil             TRACE(RXTX, logout("%p accepted, multiple IA bit set\n", s));
1699010ec629SStefan Weil         } else {
1700010ec629SStefan Weil             TRACE(RXTX, logout("%p frame ignored, multiple IA bit set\n", s));
1701010ec629SStefan Weil             return -1;
1702010ec629SStefan Weil         }
1703663e8e51Sths     } else {
1704067d01deSStefan Weil         TRACE(RXTX, logout("%p received frame, ignored, len=%zu,%s\n", s, size,
1705aac443e6SStefan Weil               nic_dump(buf, size)));
17064f1c942bSMark McLoughlin         return size;
1707663e8e51Sths     }
1708663e8e51Sths 
1709663e8e51Sths     if (get_ru_state(s) != ru_ready) {
1710aac443e6SStefan Weil         /* No resources available. */
1711aac443e6SStefan Weil         logout("no resources, state=%u\n", get_ru_state(s));
1712e824012bSStefan Weil         /* TODO: RNR interrupt only at first failed frame? */
1713e824012bSStefan Weil         eepro100_rnr_interrupt(s);
1714663e8e51Sths         s->statistics.rx_resource_errors++;
1715e7493b25SStefan Weil #if 0
1716e7493b25SStefan Weil         assert(!"no resources");
1717e7493b25SStefan Weil #endif
17184f1c942bSMark McLoughlin         return -1;
1719663e8e51Sths     }
1720e7493b25SStefan Weil     /* !!! */
1721c227f099SAnthony Liguori     eepro100_rx_t rx;
172216ef60c9SEduard - Gabriel Munteanu     pci_dma_read(&s->dev, s->ru_base + s->ru_offset,
1723e965d4bcSDavid Gibson                  &rx, sizeof(eepro100_rx_t));
1724663e8e51Sths     uint16_t rfd_command = le16_to_cpu(rx.command);
1725663e8e51Sths     uint16_t rfd_size = le16_to_cpu(rx.size);
17267f1e9d4eSKevin Wolf 
17277f1e9d4eSKevin Wolf     if (size > rfd_size) {
17287f1e9d4eSKevin Wolf         logout("Receive buffer (%" PRId16 " bytes) too small for data "
17297f1e9d4eSKevin Wolf             "(%zu bytes); data truncated\n", rfd_size, size);
17307f1e9d4eSKevin Wolf         size = rfd_size;
17317f1e9d4eSKevin Wolf     }
1732792f1d63SStefan Weil #if !defined(CONFIG_PAD_RECEIVED_FRAMES)
1733663e8e51Sths     if (size < 64) {
1734663e8e51Sths         rfd_status |= 0x0080;
1735663e8e51Sths     }
1736792f1d63SStefan Weil #endif
1737aac443e6SStefan Weil     TRACE(OTHER, logout("command 0x%04x, link 0x%08x, addr 0x%08x, size %u\n",
1738aac443e6SStefan Weil           rfd_command, rx.link, rx.rx_buf_addr, rfd_size));
173916ef60c9SEduard - Gabriel Munteanu     stw_le_pci_dma(&s->dev, s->ru_base + s->ru_offset +
1740a423a1b5SPhilippe Mathieu-Daudé                 offsetof(eepro100_rx_t, status), rfd_status, attrs);
174116ef60c9SEduard - Gabriel Munteanu     stw_le_pci_dma(&s->dev, s->ru_base + s->ru_offset +
1742a423a1b5SPhilippe Mathieu-Daudé                 offsetof(eepro100_rx_t, count), size, attrs);
1743663e8e51Sths     /* Early receive interrupt not supported. */
1744e7493b25SStefan Weil #if 0
1745e7493b25SStefan Weil     eepro100_er_interrupt(s);
1746e7493b25SStefan Weil #endif
1747663e8e51Sths     /* Receive CRC Transfer not supported. */
1748ced5296aSStefan Weil     if (s->configuration[18] & BIT(2)) {
17497f1e9d4eSKevin Wolf         missing("Receive CRC Transfer");
17507f1e9d4eSKevin Wolf         return -1;
17517f1e9d4eSKevin Wolf     }
1752663e8e51Sths     /* TODO: check stripping enable bit. */
1753e7493b25SStefan Weil #if 0
1754e7493b25SStefan Weil     assert(!(s->configuration[17] & BIT(0)));
1755e7493b25SStefan Weil #endif
175616ef60c9SEduard - Gabriel Munteanu     pci_dma_write(&s->dev, s->ru_base + s->ru_offset +
175727112f18SStefan Weil                   sizeof(eepro100_rx_t), buf, size);
1758663e8e51Sths     s->statistics.rx_good_frames++;
1759663e8e51Sths     eepro100_fr_interrupt(s);
1760663e8e51Sths     s->ru_offset = le32_to_cpu(rx.link);
1761ced5296aSStefan Weil     if (rfd_command & COMMAND_EL) {
1762663e8e51Sths         /* EL bit is set, so this was the last frame. */
17637f1e9d4eSKevin Wolf         logout("receive: Running out of frames\n");
17641069985fSBo Yang         set_ru_state(s, ru_no_resources);
17651069985fSBo Yang         eepro100_rnr_interrupt(s);
1766663e8e51Sths     }
1767ced5296aSStefan Weil     if (rfd_command & COMMAND_S) {
1768663e8e51Sths         /* S bit is set. */
1769663e8e51Sths         set_ru_state(s, ru_suspended);
1770663e8e51Sths     }
17714f1c942bSMark McLoughlin     return size;
1772663e8e51Sths }
1773663e8e51Sths 
1774151b2986SJuan Quintela static const VMStateDescription vmstate_eepro100 = {
1775151b2986SJuan Quintela     .version_id = 3,
1776151b2986SJuan Quintela     .minimum_version_id = 2,
17771de81b42SRichard Henderson     .fields = (const VMStateField[]) {
1778151b2986SJuan Quintela         VMSTATE_PCI_DEVICE(dev, EEPRO100State),
1779151b2986SJuan Quintela         VMSTATE_UNUSED(32),
1780151b2986SJuan Quintela         VMSTATE_BUFFER(mult, EEPRO100State),
1781151b2986SJuan Quintela         VMSTATE_BUFFER(mem, EEPRO100State),
17823706c43fSStefan Weil         /* Save all members of struct between scb_stat and mem. */
1783151b2986SJuan Quintela         VMSTATE_UINT8(scb_stat, EEPRO100State),
1784151b2986SJuan Quintela         VMSTATE_UINT8(int_stat, EEPRO100State),
1785151b2986SJuan Quintela         VMSTATE_UNUSED(3*4),
1786151b2986SJuan Quintela         VMSTATE_MACADDR(conf.macaddr, EEPRO100State),
1787151b2986SJuan Quintela         VMSTATE_UNUSED(19*4),
1788151b2986SJuan Quintela         VMSTATE_UINT16_ARRAY(mdimem, EEPRO100State, 32),
1789aac443e6SStefan Weil         /* The eeprom should be saved and restored by its own routines. */
1790151b2986SJuan Quintela         VMSTATE_UINT32(device, EEPRO100State),
1791151b2986SJuan Quintela         /* TODO check device. */
1792151b2986SJuan Quintela         VMSTATE_UINT32(cu_base, EEPRO100State),
1793151b2986SJuan Quintela         VMSTATE_UINT32(cu_offset, EEPRO100State),
1794151b2986SJuan Quintela         VMSTATE_UINT32(ru_base, EEPRO100State),
1795151b2986SJuan Quintela         VMSTATE_UINT32(ru_offset, EEPRO100State),
1796151b2986SJuan Quintela         VMSTATE_UINT32(statsaddr, EEPRO100State),
1797ba42b646SStefan Weil         /* Save eepro100_stats_t statistics. */
1798151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_good_frames, EEPRO100State),
1799151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_max_collisions, EEPRO100State),
1800151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_late_collisions, EEPRO100State),
1801151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_underruns, EEPRO100State),
1802151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_lost_crs, EEPRO100State),
1803151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_deferred, EEPRO100State),
1804151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_single_collisions, EEPRO100State),
1805151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_multiple_collisions, EEPRO100State),
1806151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_total_collisions, EEPRO100State),
1807151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_good_frames, EEPRO100State),
1808151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_crc_errors, EEPRO100State),
1809151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_alignment_errors, EEPRO100State),
1810151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_resource_errors, EEPRO100State),
1811151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_overrun_errors, EEPRO100State),
1812151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_cdt_errors, EEPRO100State),
1813151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_short_frame_errors, EEPRO100State),
1814151b2986SJuan Quintela         VMSTATE_UINT32(statistics.fc_xmt_pause, EEPRO100State),
1815151b2986SJuan Quintela         VMSTATE_UINT32(statistics.fc_rcv_pause, EEPRO100State),
1816151b2986SJuan Quintela         VMSTATE_UINT32(statistics.fc_rcv_unsupported, EEPRO100State),
1817151b2986SJuan Quintela         VMSTATE_UINT16(statistics.xmt_tco_frames, EEPRO100State),
1818151b2986SJuan Quintela         VMSTATE_UINT16(statistics.rcv_tco_frames, EEPRO100State),
18192657c663Sbalrog         /* Configuration bytes. */
1820151b2986SJuan Quintela         VMSTATE_BUFFER(configuration, EEPRO100State),
1821151b2986SJuan Quintela         VMSTATE_END_OF_LIST()
1822663e8e51Sths     }
1823151b2986SJuan Quintela };
1824663e8e51Sths 
1825f90c2bcdSAlex Williamson static void pci_nic_uninit(PCIDevice *pci_dev)
1826b946a153Saliguori {
1827c4c270e2SStefan Weil     EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev);
1828b946a153Saliguori 
18293cad405bSMarc-André Lureau     vmstate_unregister(VMSTATE_IF(&pci_dev->qdev), s->vmstate, s);
18302634ab7fSLi Qiang     g_free(s->vmstate);
18315fce2b3eSAlex Williamson     eeprom93xx_free(&pci_dev->qdev, s->eeprom);
1832948ecf21SJason Wang     qemu_del_nic(s->nic);
1833b946a153Saliguori }
1834b946a153Saliguori 
1835e00e365eSMark McLoughlin static NetClientInfo net_eepro100_info = {
1836f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
1837e00e365eSMark McLoughlin     .size = sizeof(NICState),
1838e00e365eSMark McLoughlin     .receive = nic_receive,
1839e00e365eSMark McLoughlin };
1840e00e365eSMark McLoughlin 
18419af21dbeSMarkus Armbruster static void e100_nic_realize(PCIDevice *pci_dev, Error **errp)
1842663e8e51Sths {
1843273a2142SJuan Quintela     EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev);
184440021f08SAnthony Liguori     E100PCIDeviceInfo *info = eepro100_get_class(s);
18459a7c2a59SMao Zhongyi     Error *local_err = NULL;
1846663e8e51Sths 
1847aac443e6SStefan Weil     TRACE(OTHER, logout("\n"));
1848663e8e51Sths 
184940021f08SAnthony Liguori     s->device = info->device;
1850663e8e51Sths 
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     }
1856663e8e51Sths 
1857663e8e51Sths     /* Add 64 * 2 EEPROM. i82557 and i82558 support a 64 word EEPROM,
1858663e8e51Sths      * i82559 and later support 64 or 256 word EEPROM. */
18595fce2b3eSAlex Williamson     s->eeprom = eeprom93xx_new(&pci_dev->qdev, EEPROM_SIZE);
1860663e8e51Sths 
1861663e8e51Sths     /* 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);
1864e824b2ccSAvi Kivity     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);
1867e824b2ccSAvi Kivity     pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar);
18685e6ffddeSAvi Kivity     /* 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);
1871e824b2ccSAvi Kivity     pci_register_bar(&s->dev, 2, 0, &s->flash_bar);
1872663e8e51Sths 
1873508ef936SGerd Hoffmann     qemu_macaddr_default_if_unset(&s->conf.macaddr);
1874ce0e58b3SStefan Weil     logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6));
1875663e8e51Sths 
1876663e8e51Sths     nic_reset(s);
1877663e8e51Sths 
1878e00e365eSMark McLoughlin     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);
1882663e8e51Sths 
1883b356f76dSJason Wang     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
1884b356f76dSJason Wang     TRACE(OTHER, logout("%s\n", qemu_get_queue(s->nic)->info_str));
1885663e8e51Sths 
1886a08d4367SJan Kiszka     qemu_register_reset(nic_reset, s);
1887663e8e51Sths 
1888e4d67e4fSMarc-André Lureau     s->vmstate = g_memdup(&vmstate_eepro100, sizeof(vmstate_eepro100));
1889b356f76dSJason Wang     s->vmstate->name = qemu_get_queue(s->nic)->model;
189099b16e8eSJuan Quintela     vmstate_register_any(VMSTATE_IF(&pci_dev->qdev), s->vmstate, s);
1891663e8e51Sths }
1892663e8e51Sths 
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 
1901558c8634SStefan Weil static E100PCIDeviceInfo e100_devices[] = {
1902663e8e51Sths     {
190339bffca2SAnthony Liguori         .name = "i82550",
190439bffca2SAnthony Liguori         .desc = "Intel i82550 Ethernet",
1905558c8634SStefan Weil         .device = i82550,
1906558c8634SStefan Weil         /* TODO: check device id. */
190740021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82551IT,
1908558c8634SStefan Weil         /* Revision ID: 0x0c, 0x0d, 0x0e. */
190940021f08SAnthony Liguori         .revision = 0x0e,
1910558c8634SStefan Weil         /* TODO: check size of statistical counters. */
1911558c8634SStefan Weil         .stats_size = 80,
1912558c8634SStefan Weil         /* TODO: check extended tcb support. */
1913558c8634SStefan Weil         .has_extended_tcb_support = true,
1914558c8634SStefan Weil         .power_management = true,
1915558c8634SStefan Weil     },{
191639bffca2SAnthony Liguori         .name = "i82551",
191739bffca2SAnthony Liguori         .desc = "Intel i82551 Ethernet",
1918558c8634SStefan Weil         .device = i82551,
191940021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82551IT,
1920558c8634SStefan Weil         /* Revision ID: 0x0f, 0x10. */
192140021f08SAnthony Liguori         .revision = 0x0f,
1922558c8634SStefan Weil         /* TODO: check size of statistical counters. */
1923558c8634SStefan Weil         .stats_size = 80,
1924558c8634SStefan Weil         .has_extended_tcb_support = true,
1925558c8634SStefan Weil         .power_management = true,
1926558c8634SStefan Weil     },{
192739bffca2SAnthony Liguori         .name = "i82557a",
192839bffca2SAnthony Liguori         .desc = "Intel i82557A Ethernet",
1929558c8634SStefan Weil         .device = i82557A,
193040021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
193140021f08SAnthony Liguori         .revision = 0x01,
1932558c8634SStefan Weil         .power_management = false,
1933558c8634SStefan Weil     },{
193439bffca2SAnthony Liguori         .name = "i82557b",
193539bffca2SAnthony Liguori         .desc = "Intel i82557B Ethernet",
1936558c8634SStefan Weil         .device = i82557B,
193740021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
193840021f08SAnthony Liguori         .revision = 0x02,
1939558c8634SStefan Weil         .power_management = false,
1940558c8634SStefan Weil     },{
194139bffca2SAnthony Liguori         .name = "i82557c",
194239bffca2SAnthony Liguori         .desc = "Intel i82557C Ethernet",
1943558c8634SStefan Weil         .device = i82557C,
194440021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
194540021f08SAnthony Liguori         .revision = 0x03,
1946558c8634SStefan Weil         .power_management = false,
1947558c8634SStefan Weil     },{
194839bffca2SAnthony Liguori         .name = "i82558a",
194939bffca2SAnthony Liguori         .desc = "Intel i82558A Ethernet",
1950558c8634SStefan Weil         .device = i82558A,
195140021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
195240021f08SAnthony Liguori         .revision = 0x04,
1953558c8634SStefan Weil         .stats_size = 76,
1954558c8634SStefan Weil         .has_extended_tcb_support = true,
1955558c8634SStefan Weil         .power_management = true,
1956558c8634SStefan Weil     },{
195739bffca2SAnthony Liguori         .name = "i82558b",
195839bffca2SAnthony Liguori         .desc = "Intel i82558B Ethernet",
1959558c8634SStefan Weil         .device = i82558B,
196040021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
196140021f08SAnthony Liguori         .revision = 0x05,
1962558c8634SStefan Weil         .stats_size = 76,
1963558c8634SStefan Weil         .has_extended_tcb_support = true,
1964558c8634SStefan Weil         .power_management = true,
1965558c8634SStefan Weil     },{
196639bffca2SAnthony Liguori         .name = "i82559a",
196739bffca2SAnthony Liguori         .desc = "Intel i82559A Ethernet",
1968558c8634SStefan Weil         .device = i82559A,
196940021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
197040021f08SAnthony Liguori         .revision = 0x06,
1971558c8634SStefan Weil         .stats_size = 80,
1972558c8634SStefan Weil         .has_extended_tcb_support = true,
1973558c8634SStefan Weil         .power_management = true,
1974558c8634SStefan Weil     },{
197539bffca2SAnthony Liguori         .name = "i82559b",
197639bffca2SAnthony Liguori         .desc = "Intel i82559B Ethernet",
1977558c8634SStefan Weil         .device = i82559B,
197840021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
197940021f08SAnthony Liguori         .revision = 0x07,
1980558c8634SStefan Weil         .stats_size = 80,
1981558c8634SStefan Weil         .has_extended_tcb_support = true,
1982558c8634SStefan Weil         .power_management = true,
1983558c8634SStefan Weil     },{
198439bffca2SAnthony Liguori         .name = "i82559c",
198539bffca2SAnthony Liguori         .desc = "Intel i82559C Ethernet",
1986558c8634SStefan Weil         .device = i82559C,
198740021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
1988558c8634SStefan Weil #if 0
198940021f08SAnthony Liguori         .revision = 0x08,
1990558c8634SStefan Weil #endif
1991558c8634SStefan Weil         /* TODO: Windows wants revision id 0x0c. */
199240021f08SAnthony Liguori         .revision = 0x0c,
1993ad03502bSIsaku Yamahata #if EEPROM_SIZE > 0
199440021f08SAnthony Liguori         .subsystem_vendor_id = PCI_VENDOR_ID_INTEL,
199540021f08SAnthony Liguori         .subsystem_id = 0x0040,
1996ad03502bSIsaku Yamahata #endif
1997558c8634SStefan Weil         .stats_size = 80,
1998558c8634SStefan Weil         .has_extended_tcb_support = true,
1999558c8634SStefan Weil         .power_management = true,
2000558c8634SStefan Weil     },{
200139bffca2SAnthony Liguori         .name = "i82559er",
200239bffca2SAnthony Liguori         .desc = "Intel i82559ER Ethernet",
2003558c8634SStefan Weil         .device = i82559ER,
200440021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82551IT,
200540021f08SAnthony Liguori         .revision = 0x09,
2006558c8634SStefan Weil         .stats_size = 80,
2007558c8634SStefan Weil         .has_extended_tcb_support = true,
2008558c8634SStefan Weil         .power_management = true,
2009558c8634SStefan Weil     },{
201039bffca2SAnthony Liguori         .name = "i82562",
201139bffca2SAnthony Liguori         .desc = "Intel i82562 Ethernet",
2012558c8634SStefan Weil         .device = i82562,
2013558c8634SStefan Weil         /* TODO: check device id. */
201440021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82551IT,
2015558c8634SStefan Weil         /* TODO: wrong revision id. */
201640021f08SAnthony Liguori         .revision = 0x0e,
2017558c8634SStefan Weil         .stats_size = 80,
2018558c8634SStefan Weil         .has_extended_tcb_support = true,
2019558c8634SStefan Weil         .power_management = true,
2020db667a12SStefan Weil     },{
2021db667a12SStefan Weil         /* Toshiba Tecra 8200. */
202239bffca2SAnthony Liguori         .name = "i82801",
202339bffca2SAnthony Liguori         .desc = "Intel i82801 Ethernet",
2024db667a12SStefan Weil         .device = i82801,
202540021f08SAnthony Liguori         .device_id = 0x2449,
202640021f08SAnthony Liguori         .revision = 0x03,
2027db667a12SStefan Weil         .stats_size = 80,
2028db667a12SStefan Weil         .has_extended_tcb_support = true,
2029db667a12SStefan Weil         .power_management = true,
2030663e8e51Sths     }
2031558c8634SStefan Weil };
2032663e8e51Sths 
203340021f08SAnthony Liguori static E100PCIDeviceInfo *eepro100_get_class_by_name(const char *typename)
203440021f08SAnthony Liguori {
203540021f08SAnthony Liguori     E100PCIDeviceInfo *info = NULL;
203640021f08SAnthony Liguori     int i;
203740021f08SAnthony Liguori 
203840021f08SAnthony Liguori     /* This is admittedly awkward but also temporary.  QOM allows for
203940021f08SAnthony Liguori      * parameterized typing and for subclassing both of which would suitable
204040021f08SAnthony Liguori      * handle what's going on here.  But class_data is already being used as
204140021f08SAnthony Liguori      * a stop-gap hack to allow incremental qdev conversion so we cannot use it
204240021f08SAnthony Liguori      * right now.  Once we merge the final QOM series, we can come back here and
204340021f08SAnthony Liguori      * do this in a much more elegant fashion.
204440021f08SAnthony Liguori      */
204540021f08SAnthony Liguori     for (i = 0; i < ARRAY_SIZE(e100_devices); i++) {
204639bffca2SAnthony Liguori         if (strcmp(e100_devices[i].name, typename) == 0) {
204740021f08SAnthony Liguori             info = &e100_devices[i];
204840021f08SAnthony Liguori             break;
204940021f08SAnthony Liguori         }
205040021f08SAnthony Liguori     }
205140021f08SAnthony Liguori     assert(info != NULL);
205240021f08SAnthony Liguori 
205340021f08SAnthony Liguori     return info;
205440021f08SAnthony Liguori }
205540021f08SAnthony Liguori 
205640021f08SAnthony Liguori static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s)
205740021f08SAnthony Liguori {
205840021f08SAnthony Liguori     return eepro100_get_class_by_name(object_get_typename(OBJECT(s)));
205940021f08SAnthony Liguori }
206040021f08SAnthony Liguori 
2061e732f00fSRichard Henderson static const Property e100_properties[] = {
206239bffca2SAnthony Liguori     DEFINE_NIC_PROPERTIES(EEPRO100State, conf),
206339bffca2SAnthony Liguori     DEFINE_PROP_END_OF_LIST(),
206439bffca2SAnthony Liguori };
206539bffca2SAnthony Liguori 
206640021f08SAnthony Liguori static void eepro100_class_init(ObjectClass *klass, void *data)
206740021f08SAnthony Liguori {
206839bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
206940021f08SAnthony Liguori     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
207040021f08SAnthony Liguori     E100PCIDeviceInfo *info;
207140021f08SAnthony Liguori 
207240021f08SAnthony Liguori     info = eepro100_get_class_by_name(object_class_get_name(klass));
207340021f08SAnthony Liguori 
2074125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
20754f67d30bSMarc-André Lureau     device_class_set_props(dc, e100_properties);
207639bffca2SAnthony Liguori     dc->desc = info->desc;
207740021f08SAnthony Liguori     k->vendor_id = PCI_VENDOR_ID_INTEL;
207840021f08SAnthony Liguori     k->class_id = PCI_CLASS_NETWORK_ETHERNET;
207940021f08SAnthony Liguori     k->romfile = "pxe-eepro100.rom";
20809af21dbeSMarkus Armbruster     k->realize = e100_nic_realize;
208140021f08SAnthony Liguori     k->exit = pci_nic_uninit;
208240021f08SAnthony Liguori     k->device_id = info->device_id;
208340021f08SAnthony Liguori     k->revision = info->revision;
208440021f08SAnthony Liguori     k->subsystem_vendor_id = info->subsystem_vendor_id;
208540021f08SAnthony Liguori     k->subsystem_id = info->subsystem_id;
208640021f08SAnthony Liguori }
208740021f08SAnthony Liguori 
208883f7d43aSAndreas Färber static void eepro100_register_types(void)
20899d07d757SPaul Brook {
2090558c8634SStefan Weil     size_t i;
2091558c8634SStefan Weil     for (i = 0; i < ARRAY_SIZE(e100_devices); i++) {
209239bffca2SAnthony Liguori         TypeInfo type_info = {};
209339bffca2SAnthony Liguori         E100PCIDeviceInfo *info = &e100_devices[i];
209440021f08SAnthony Liguori 
209539bffca2SAnthony Liguori         type_info.name = info->name;
209639bffca2SAnthony Liguori         type_info.parent = TYPE_PCI_DEVICE;
209739bffca2SAnthony Liguori         type_info.class_init = eepro100_class_init;
209839bffca2SAnthony Liguori         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         };
210440021f08SAnthony Liguori 
210529ec04f0SZhao Liu         type_register_static(&type_info);
2106558c8634SStefan Weil     }
21079d07d757SPaul Brook }
21089d07d757SPaul Brook 
210983f7d43aSAndreas Färber type_init(eepro100_register_types)
2110