xref: /qemu/hw/net/eepro100.c (revision 4f67d30b5e74e060b8dbe10528829b47345cd6e8)
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  *
9230a167cSStefan Weil  * This program is free software: you can redistribute it and/or modify
10663e8e51Sths  * it under the terms of the GNU General Public License as published by
11230a167cSStefan Weil  * the Free Software Foundation, either version 2 of the License, or
12230a167cSStefan Weil  * (at your option) version 3 or any later version.
13663e8e51Sths  *
14663e8e51Sths  * This program is distributed in the hope that it will be useful,
15663e8e51Sths  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16663e8e51Sths  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17663e8e51Sths  * GNU General Public License for more details.
18663e8e51Sths  *
19663e8e51Sths  * You should have received a copy of the GNU General Public License
20230a167cSStefan Weil  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21663e8e51Sths  *
22663e8e51Sths  * Tested features (i82559):
23e5e23ab8SStefan Weil  *      PXE boot (i386 guest, i386 / mips / mipsel / ppc host) ok
24663e8e51Sths  *      Linux networking (i386) ok
25663e8e51Sths  *
26663e8e51Sths  * Untested:
27663e8e51Sths  *      Windows networking
28663e8e51Sths  *
29663e8e51Sths  * References:
30663e8e51Sths  *
31663e8e51Sths  * Intel 8255x 10/100 Mbps Ethernet Controller Family
32663e8e51Sths  * Open Source Software Developer Manual
33ba19f2deSStefan Weil  *
34ba19f2deSStefan Weil  * TODO:
35ba19f2deSStefan Weil  *      * PHY emulation should be separated from nic emulation.
36ba19f2deSStefan Weil  *        Most nic emulations could share the same phy code.
37ba19f2deSStefan Weil  *      * i82550 is untested. It is programmed like the i82559.
38ba19f2deSStefan Weil  *      * i82562 is untested. It is programmed like the i82559.
39ba19f2deSStefan Weil  *      * Power management (i82558 and later) is not implemented.
40ba19f2deSStefan Weil  *      * Wake-on-LAN is not implemented.
41663e8e51Sths  */
42663e8e51Sths 
43e8d40465SPeter Maydell #include "qemu/osdep.h"
44872a2b7cSPhilippe Mathieu-Daudé #include "qemu/units.h"
4583c9f4caSPaolo Bonzini #include "hw/pci/pci.h"
46a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
47d6454270SMarkus Armbruster #include "migration/vmstate.h"
481422e32dSPaolo Bonzini #include "net/net.h"
497c0348bdSMark Cave-Ayland #include "net/eth.h"
500d09e41aSPaolo Bonzini #include "hw/nvram/eeprom93xx.h"
519c17d615SPaolo Bonzini #include "sysemu/sysemu.h"
529c17d615SPaolo Bonzini #include "sysemu/dma.h"
5371e8a915SMarkus Armbruster #include "sysemu/reset.h"
54949fc823SMarcel Apfelbaum #include "qemu/bitops.h"
550b8fa32fSMarkus Armbruster #include "qemu/module.h"
569a7c2a59SMao Zhongyi #include "qapi/error.h"
57663e8e51Sths 
58792f1d63SStefan Weil /* QEMU sends frames smaller than 60 bytes to ethernet nics.
59792f1d63SStefan Weil  * Such frames are rejected by real nics and their emulations.
60792f1d63SStefan Weil  * To avoid this behaviour, other nic emulations pad received
61792f1d63SStefan Weil  * frames. The following definition enables this padding for
62792f1d63SStefan Weil  * eepro100, too. We keep the define around in case it might
63792f1d63SStefan Weil  * become useful the future if the core networking is ever
64792f1d63SStefan Weil  * changed to pad short packets itself. */
65792f1d63SStefan Weil #define CONFIG_PAD_RECEIVED_FRAMES
66792f1d63SStefan Weil 
67aac443e6SStefan Weil /* Debug EEPRO100 card. */
68ce0e58b3SStefan Weil #if 0
69ce0e58b3SStefan Weil # define DEBUG_EEPRO100
70ce0e58b3SStefan Weil #endif
71663e8e51Sths 
72663e8e51Sths #ifdef DEBUG_EEPRO100
73001faf32SBlue Swirl #define logout(fmt, ...) fprintf(stderr, "EE100\t%-24s" fmt, __func__, ## __VA_ARGS__)
74663e8e51Sths #else
75001faf32SBlue Swirl #define logout(fmt, ...) ((void)0)
76663e8e51Sths #endif
77663e8e51Sths 
78663e8e51Sths /* Set flags to 0 to disable debug output. */
79aac443e6SStefan Weil #define INT     1       /* interrupt related actions */
80aac443e6SStefan Weil #define MDI     1       /* mdi related actions */
81aac443e6SStefan Weil #define OTHER   1
82aac443e6SStefan Weil #define RXTX    1
83aac443e6SStefan Weil #define EEPROM  1       /* eeprom related actions */
84663e8e51Sths 
85663e8e51Sths #define TRACE(flag, command) ((flag) ? (command) : (void)0)
86663e8e51Sths 
877f1e9d4eSKevin Wolf #define missing(text) fprintf(stderr, "eepro100: feature is missing in this emulation: " text "\n")
88663e8e51Sths 
89663e8e51Sths #define MAX_ETH_FRAME_SIZE 1514
90663e8e51Sths 
91663e8e51Sths /* This driver supports several different devices which are declared here. */
92c4c270e2SStefan Weil #define i82550          0x82550
93663e8e51Sths #define i82551          0x82551
94c4c270e2SStefan Weil #define i82557A         0x82557a
95663e8e51Sths #define i82557B         0x82557b
96663e8e51Sths #define i82557C         0x82557c
97c4c270e2SStefan Weil #define i82558A         0x82558a
98663e8e51Sths #define i82558B         0x82558b
99c4c270e2SStefan Weil #define i82559A         0x82559a
100c4c270e2SStefan Weil #define i82559B         0x82559b
101663e8e51Sths #define i82559C         0x82559c
102663e8e51Sths #define i82559ER        0x82559e
103663e8e51Sths #define i82562          0x82562
104db667a12SStefan Weil #define i82801          0x82801
105663e8e51Sths 
106aac443e6SStefan Weil /* Use 64 word EEPROM. TODO: could be a runtime option. */
107663e8e51Sths #define EEPROM_SIZE     64
108663e8e51Sths 
109663e8e51Sths #define PCI_MEM_SIZE            (4 * KiB)
110663e8e51Sths #define PCI_IO_SIZE             64
111663e8e51Sths #define PCI_FLASH_SIZE          (128 * KiB)
112663e8e51Sths 
113663e8e51Sths #define BITS(n, m) (((0xffffffffU << (31 - n)) >> (31 - n + m)) << m)
114663e8e51Sths 
115663e8e51Sths /* The SCB accepts the following controls for the Tx and Rx units: */
116663e8e51Sths #define  CU_NOP         0x0000  /* No operation. */
117663e8e51Sths #define  CU_START       0x0010  /* CU start. */
118663e8e51Sths #define  CU_RESUME      0x0020  /* CU resume. */
119663e8e51Sths #define  CU_STATSADDR   0x0040  /* Load dump counters address. */
120663e8e51Sths #define  CU_SHOWSTATS   0x0050  /* Dump statistical counters. */
121663e8e51Sths #define  CU_CMD_BASE    0x0060  /* Load CU base address. */
122663e8e51Sths #define  CU_DUMPSTATS   0x0070  /* Dump and reset statistical counters. */
123663e8e51Sths #define  CU_SRESUME     0x00a0  /* CU static resume. */
124663e8e51Sths 
125663e8e51Sths #define  RU_NOP         0x0000
126663e8e51Sths #define  RX_START       0x0001
127663e8e51Sths #define  RX_RESUME      0x0002
128e824012bSStefan Weil #define  RU_ABORT       0x0004
129663e8e51Sths #define  RX_ADDR_LOAD   0x0006
130663e8e51Sths #define  RX_RESUMENR    0x0007
131663e8e51Sths #define INT_MASK        0x0100
132663e8e51Sths #define DRVR_INT        0x0200  /* Driver generated interrupt. */
133663e8e51Sths 
134558c8634SStefan Weil typedef struct {
13539bffca2SAnthony Liguori     const char *name;
13639bffca2SAnthony Liguori     const char *desc;
13740021f08SAnthony Liguori     uint16_t device_id;
13840021f08SAnthony Liguori     uint8_t revision;
13940021f08SAnthony Liguori     uint16_t subsystem_vendor_id;
14040021f08SAnthony Liguori     uint16_t subsystem_id;
14140021f08SAnthony Liguori 
142558c8634SStefan Weil     uint32_t device;
143558c8634SStefan Weil     uint8_t stats_size;
144558c8634SStefan Weil     bool has_extended_tcb_support;
145558c8634SStefan Weil     bool power_management;
146558c8634SStefan Weil } E100PCIDeviceInfo;
147558c8634SStefan Weil 
148663e8e51Sths /* Offsets to the various registers.
149663e8e51Sths    All accesses need not be longword aligned. */
150e5e23ab8SStefan Weil typedef enum {
1510908bba1SStefan Weil     SCBStatus = 0,              /* Status Word. */
152663e8e51Sths     SCBAck = 1,
153663e8e51Sths     SCBCmd = 2,                 /* Rx/Command Unit command and status. */
154663e8e51Sths     SCBIntmask = 3,
155663e8e51Sths     SCBPointer = 4,             /* General purpose pointer. */
156663e8e51Sths     SCBPort = 8,                /* Misc. commands and operands.  */
1570908bba1SStefan Weil     SCBflash = 12,              /* Flash memory control. */
1580908bba1SStefan Weil     SCBeeprom = 14,             /* EEPROM control. */
159663e8e51Sths     SCBCtrlMDI = 16,            /* MDI interface control. */
160663e8e51Sths     SCBEarlyRx = 20,            /* Early receive byte count. */
1610908bba1SStefan Weil     SCBFlow = 24,               /* Flow Control. */
1620908bba1SStefan Weil     SCBpmdr = 27,               /* Power Management Driver. */
1630908bba1SStefan Weil     SCBgctrl = 28,              /* General Control. */
1640908bba1SStefan Weil     SCBgstat = 29,              /* General Status. */
165e5e23ab8SStefan Weil } E100RegisterOffset;
166663e8e51Sths 
167663e8e51Sths /* A speedo3 transmit buffer descriptor with two buffers... */
168663e8e51Sths typedef struct {
169663e8e51Sths     uint16_t status;
170663e8e51Sths     uint16_t command;
171663e8e51Sths     uint32_t link;              /* void * */
1727b8737deSStefan Weil     uint32_t tbd_array_addr;    /* transmit buffer descriptor array address. */
173663e8e51Sths     uint16_t tcb_bytes;         /* transmit command block byte count (in lower 14 bits */
174663e8e51Sths     uint8_t tx_threshold;       /* transmit threshold */
175663e8e51Sths     uint8_t tbd_count;          /* TBD number */
176e7493b25SStefan Weil #if 0
177e7493b25SStefan Weil     /* This constitutes two "TBD" entries: hdr and data */
178e7493b25SStefan Weil     uint32_t tx_buf_addr0;  /* void *, header of frame to be transmitted.  */
179e7493b25SStefan Weil     int32_t  tx_buf_size0;  /* Length of Tx hdr. */
180e7493b25SStefan Weil     uint32_t tx_buf_addr1;  /* void *, data to be transmitted.  */
181e7493b25SStefan Weil     int32_t  tx_buf_size1;  /* Length of Tx data. */
182e7493b25SStefan Weil #endif
183c227f099SAnthony Liguori } eepro100_tx_t;
184663e8e51Sths 
185663e8e51Sths /* Receive frame descriptor. */
186663e8e51Sths typedef struct {
187663e8e51Sths     int16_t status;
188663e8e51Sths     uint16_t command;
189663e8e51Sths     uint32_t link;              /* struct RxFD * */
190663e8e51Sths     uint32_t rx_buf_addr;       /* void * */
191663e8e51Sths     uint16_t count;
192663e8e51Sths     uint16_t size;
19327112f18SStefan Weil     /* Ethernet frame data follows. */
194c227f099SAnthony Liguori } eepro100_rx_t;
195663e8e51Sths 
196ced5296aSStefan Weil typedef enum {
197ced5296aSStefan Weil     COMMAND_EL = BIT(15),
198ced5296aSStefan Weil     COMMAND_S = BIT(14),
199ced5296aSStefan Weil     COMMAND_I = BIT(13),
200ced5296aSStefan Weil     COMMAND_NC = BIT(4),
201ced5296aSStefan Weil     COMMAND_SF = BIT(3),
202ced5296aSStefan Weil     COMMAND_CMD = BITS(2, 0),
203ced5296aSStefan Weil } scb_command_bit;
204ced5296aSStefan Weil 
205ced5296aSStefan Weil typedef enum {
206ced5296aSStefan Weil     STATUS_C = BIT(15),
207ced5296aSStefan Weil     STATUS_OK = BIT(13),
208ced5296aSStefan Weil } scb_status_bit;
209ced5296aSStefan Weil 
210663e8e51Sths typedef struct {
211663e8e51Sths     uint32_t tx_good_frames, tx_max_collisions, tx_late_collisions,
212663e8e51Sths              tx_underruns, tx_lost_crs, tx_deferred, tx_single_collisions,
213663e8e51Sths              tx_multiple_collisions, tx_total_collisions;
214663e8e51Sths     uint32_t rx_good_frames, rx_crc_errors, rx_alignment_errors,
215663e8e51Sths              rx_resource_errors, rx_overrun_errors, rx_cdt_errors,
216663e8e51Sths              rx_short_frame_errors;
217663e8e51Sths     uint32_t fc_xmt_pause, fc_rcv_pause, fc_rcv_unsupported;
218663e8e51Sths     uint16_t xmt_tco_frames, rcv_tco_frames;
219ba42b646SStefan Weil     /* TODO: i82559 has six reserved statistics but a total of 24 dwords. */
220ba42b646SStefan Weil     uint32_t reserved[4];
221c227f099SAnthony Liguori } eepro100_stats_t;
222663e8e51Sths 
223663e8e51Sths typedef enum {
224663e8e51Sths     cu_idle = 0,
225663e8e51Sths     cu_suspended = 1,
226663e8e51Sths     cu_active = 2,
227663e8e51Sths     cu_lpq_active = 2,
228663e8e51Sths     cu_hqp_active = 3
229c227f099SAnthony Liguori } cu_state_t;
230663e8e51Sths 
231663e8e51Sths typedef enum {
232663e8e51Sths     ru_idle = 0,
233663e8e51Sths     ru_suspended = 1,
234663e8e51Sths     ru_no_resources = 2,
235663e8e51Sths     ru_ready = 4
236c227f099SAnthony Liguori } ru_state_t;
237663e8e51Sths 
238663e8e51Sths typedef struct {
239273a2142SJuan Quintela     PCIDevice dev;
240010ec629SStefan Weil     /* Hash register (multicast mask array, multiple individual addresses). */
241010ec629SStefan Weil     uint8_t mult[8];
2425e6ffddeSAvi Kivity     MemoryRegion mmio_bar;
2435e6ffddeSAvi Kivity     MemoryRegion io_bar;
2445e6ffddeSAvi Kivity     MemoryRegion flash_bar;
245e00e365eSMark McLoughlin     NICState *nic;
246508ef936SGerd Hoffmann     NICConf conf;
247663e8e51Sths     uint8_t scb_stat;           /* SCB stat/ack byte */
248663e8e51Sths     uint8_t int_stat;           /* PCI interrupt status */
2493706c43fSStefan Weil     /* region must not be saved by nic_save. */
250663e8e51Sths     uint16_t mdimem[32];
251c227f099SAnthony Liguori     eeprom_t *eeprom;
252663e8e51Sths     uint32_t device;            /* device variant */
253663e8e51Sths     /* (cu_base + cu_offset) address the next command block in the command block list. */
254663e8e51Sths     uint32_t cu_base;           /* CU base address */
255663e8e51Sths     uint32_t cu_offset;         /* CU address offset */
256663e8e51Sths     /* (ru_base + ru_offset) address the RFD in the Receive Frame Area. */
257663e8e51Sths     uint32_t ru_base;           /* RU base address */
258663e8e51Sths     uint32_t ru_offset;         /* RU address offset */
259c227f099SAnthony Liguori     uint32_t statsaddr;         /* pointer to eepro100_stats_t */
260ba42b646SStefan Weil 
261f3a52e50SStefan Weil     /* Temporary status information (no need to save these values),
262f3a52e50SStefan Weil      * used while processing CU commands. */
263f3a52e50SStefan Weil     eepro100_tx_t tx;           /* transmit buffer descriptor */
264f3a52e50SStefan Weil     uint32_t cb_address;        /* = cu_base + cu_offset */
265f3a52e50SStefan Weil 
266ba42b646SStefan Weil     /* Statistical counters. Also used for wake-up packet (i82559). */
267ba42b646SStefan Weil     eepro100_stats_t statistics;
268ba42b646SStefan Weil 
269e5e23ab8SStefan Weil     /* Data in mem is always in the byte order of the controller (le).
270e5e23ab8SStefan Weil      * It must be dword aligned to allow direct access to 32 bit values. */
2713a93113aSDong Xu Wang     uint8_t mem[PCI_MEM_SIZE] __attribute__((aligned(8)));
272e5e23ab8SStefan Weil 
273663e8e51Sths     /* Configuration bytes. */
274663e8e51Sths     uint8_t configuration[22];
275663e8e51Sths 
276151b2986SJuan Quintela     /* vmstate for each particular nic */
277151b2986SJuan Quintela     VMStateDescription *vmstate;
278ba42b646SStefan Weil 
279ba42b646SStefan Weil     /* Quasi static device properties (no need to save them). */
280ba42b646SStefan Weil     uint16_t stats_size;
281ba42b646SStefan Weil     bool has_extended_tcb_support;
282663e8e51Sths } EEPRO100State;
283663e8e51Sths 
2846cded3a4SStefan Weil /* Word indices in EEPROM. */
2856cded3a4SStefan Weil typedef enum {
2866cded3a4SStefan Weil     EEPROM_CNFG_MDIX  = 0x03,
2876cded3a4SStefan Weil     EEPROM_ID         = 0x05,
2886cded3a4SStefan Weil     EEPROM_PHY_ID     = 0x06,
2896cded3a4SStefan Weil     EEPROM_VENDOR_ID  = 0x0c,
2906cded3a4SStefan Weil     EEPROM_CONFIG_ASF = 0x0d,
2916cded3a4SStefan Weil     EEPROM_DEVICE_ID  = 0x23,
2926cded3a4SStefan Weil     EEPROM_SMBUS_ADDR = 0x90,
2936cded3a4SStefan Weil } EEPROMOffset;
2946cded3a4SStefan Weil 
295b1e87018SStefan Weil /* Bit values for EEPROM ID word. */
296b1e87018SStefan Weil typedef enum {
297b1e87018SStefan Weil     EEPROM_ID_MDM = BIT(0),     /* Modem */
298b1e87018SStefan Weil     EEPROM_ID_STB = BIT(1),     /* Standby Enable */
299b1e87018SStefan Weil     EEPROM_ID_WMR = BIT(2),     /* ??? */
300b1e87018SStefan Weil     EEPROM_ID_WOL = BIT(5),     /* Wake on LAN */
301b1e87018SStefan Weil     EEPROM_ID_DPD = BIT(6),     /* Deep Power Down */
302b1e87018SStefan Weil     EEPROM_ID_ALT = BIT(7),     /* */
303b1e87018SStefan Weil     /* BITS(10, 8) device revision */
304b1e87018SStefan Weil     EEPROM_ID_BD = BIT(11),     /* boot disable */
305b1e87018SStefan Weil     EEPROM_ID_ID = BIT(13),     /* id bit */
306b1e87018SStefan Weil     /* BITS(15, 14) signature */
307b1e87018SStefan Weil     EEPROM_ID_VALID = BIT(14),  /* signature for valid eeprom */
308b1e87018SStefan Weil } eeprom_id_bit;
309b1e87018SStefan Weil 
310663e8e51Sths /* Default values for MDI (PHY) registers */
311663e8e51Sths static const uint16_t eepro100_mdi_default[] = {
312663e8e51Sths     /* MDI Registers 0 - 6, 7 */
313663e8e51Sths     0x3000, 0x780d, 0x02a8, 0x0154, 0x05e1, 0x0000, 0x0000, 0x0000,
314663e8e51Sths     /* MDI Registers 8 - 15 */
315663e8e51Sths     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
316663e8e51Sths     /* MDI Registers 16 - 31 */
317663e8e51Sths     0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
318663e8e51Sths     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
319663e8e51Sths };
320663e8e51Sths 
321663e8e51Sths /* Readonly mask for MDI (PHY) registers */
322663e8e51Sths static const uint16_t eepro100_mdi_mask[] = {
323663e8e51Sths     0x0000, 0xffff, 0xffff, 0xffff, 0xc01f, 0xffff, 0xffff, 0x0000,
324663e8e51Sths     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
325663e8e51Sths     0x0fff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
326663e8e51Sths     0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
327663e8e51Sths };
328663e8e51Sths 
32940021f08SAnthony Liguori static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s);
33040021f08SAnthony Liguori 
331e5e23ab8SStefan Weil /* Read a 16 bit control/status (CSR) register. */
332e5e23ab8SStefan Weil static uint16_t e100_read_reg2(EEPRO100State *s, E100RegisterOffset addr)
333e5e23ab8SStefan Weil {
334e5e23ab8SStefan Weil     assert(!((uintptr_t)&s->mem[addr] & 1));
3354d9be252SPeter Maydell     return lduw_le_p(&s->mem[addr]);
336e5e23ab8SStefan Weil }
337e5e23ab8SStefan Weil 
338e5e23ab8SStefan Weil /* Read a 32 bit control/status (CSR) register. */
339e5e23ab8SStefan Weil static uint32_t e100_read_reg4(EEPRO100State *s, E100RegisterOffset addr)
340e5e23ab8SStefan Weil {
341e5e23ab8SStefan Weil     assert(!((uintptr_t)&s->mem[addr] & 3));
3424d9be252SPeter Maydell     return ldl_le_p(&s->mem[addr]);
343e5e23ab8SStefan Weil }
344e5e23ab8SStefan Weil 
345e5e23ab8SStefan Weil /* Write a 16 bit control/status (CSR) register. */
346e5e23ab8SStefan Weil static void e100_write_reg2(EEPRO100State *s, E100RegisterOffset addr,
347e5e23ab8SStefan Weil                             uint16_t val)
348e5e23ab8SStefan Weil {
349e5e23ab8SStefan Weil     assert(!((uintptr_t)&s->mem[addr] & 1));
3504d9be252SPeter Maydell     stw_le_p(&s->mem[addr], val);
351e5e23ab8SStefan Weil }
352e5e23ab8SStefan Weil 
353e5e23ab8SStefan Weil /* Read a 32 bit control/status (CSR) register. */
354e5e23ab8SStefan Weil static void e100_write_reg4(EEPRO100State *s, E100RegisterOffset addr,
355e5e23ab8SStefan Weil                             uint32_t val)
356e5e23ab8SStefan Weil {
357e5e23ab8SStefan Weil     assert(!((uintptr_t)&s->mem[addr] & 3));
3584d9be252SPeter Maydell     stl_le_p(&s->mem[addr], val);
359e5e23ab8SStefan Weil }
360e5e23ab8SStefan Weil 
361663e8e51Sths #if defined(DEBUG_EEPRO100)
362663e8e51Sths static const char *nic_dump(const uint8_t * buf, unsigned size)
363663e8e51Sths {
364663e8e51Sths     static char dump[3 * 16 + 1];
365663e8e51Sths     char *p = &dump[0];
366aac443e6SStefan Weil     if (size > 16) {
367663e8e51Sths         size = 16;
368aac443e6SStefan Weil     }
369663e8e51Sths     while (size-- > 0) {
370663e8e51Sths         p += sprintf(p, " %02x", *buf++);
371663e8e51Sths     }
372663e8e51Sths     return dump;
373663e8e51Sths }
374663e8e51Sths #endif                          /* DEBUG_EEPRO100 */
375663e8e51Sths 
376663e8e51Sths enum scb_stat_ack {
377663e8e51Sths     stat_ack_not_ours = 0x00,
378663e8e51Sths     stat_ack_sw_gen = 0x04,
379663e8e51Sths     stat_ack_rnr = 0x10,
380663e8e51Sths     stat_ack_cu_idle = 0x20,
381663e8e51Sths     stat_ack_frame_rx = 0x40,
382663e8e51Sths     stat_ack_cu_cmd_done = 0x80,
383663e8e51Sths     stat_ack_not_present = 0xFF,
384663e8e51Sths     stat_ack_rx = (stat_ack_sw_gen | stat_ack_rnr | stat_ack_frame_rx),
385663e8e51Sths     stat_ack_tx = (stat_ack_cu_idle | stat_ack_cu_cmd_done),
386663e8e51Sths };
387663e8e51Sths 
388663e8e51Sths static void disable_interrupt(EEPRO100State * s)
389663e8e51Sths {
390663e8e51Sths     if (s->int_stat) {
391aac443e6SStefan Weil         TRACE(INT, logout("interrupt disabled\n"));
3929e64f8a3SMarcel Apfelbaum         pci_irq_deassert(&s->dev);
393663e8e51Sths         s->int_stat = 0;
394663e8e51Sths     }
395663e8e51Sths }
396663e8e51Sths 
397663e8e51Sths static void enable_interrupt(EEPRO100State * s)
398663e8e51Sths {
399663e8e51Sths     if (!s->int_stat) {
400aac443e6SStefan Weil         TRACE(INT, logout("interrupt enabled\n"));
4019e64f8a3SMarcel Apfelbaum         pci_irq_assert(&s->dev);
402663e8e51Sths         s->int_stat = 1;
403663e8e51Sths     }
404663e8e51Sths }
405663e8e51Sths 
406663e8e51Sths static void eepro100_acknowledge(EEPRO100State * s)
407663e8e51Sths {
408663e8e51Sths     s->scb_stat &= ~s->mem[SCBAck];
409663e8e51Sths     s->mem[SCBAck] = s->scb_stat;
410663e8e51Sths     if (s->scb_stat == 0) {
411663e8e51Sths         disable_interrupt(s);
412663e8e51Sths     }
413663e8e51Sths }
414663e8e51Sths 
415e715c8e8SStefan Weil static void eepro100_interrupt(EEPRO100State * s, uint8_t status)
416663e8e51Sths {
417663e8e51Sths     uint8_t mask = ~s->mem[SCBIntmask];
418e715c8e8SStefan Weil     s->mem[SCBAck] |= status;
419e715c8e8SStefan Weil     status = s->scb_stat = s->mem[SCBAck];
420e715c8e8SStefan Weil     status &= (mask | 0x0f);
421e7493b25SStefan Weil #if 0
422e7493b25SStefan Weil     status &= (~s->mem[SCBIntmask] | 0x0xf);
423e7493b25SStefan Weil #endif
424e715c8e8SStefan Weil     if (status && (mask & 0x01)) {
425663e8e51Sths         /* SCB mask and SCB Bit M do not disable interrupt. */
426663e8e51Sths         enable_interrupt(s);
427663e8e51Sths     } else if (s->int_stat) {
428663e8e51Sths         disable_interrupt(s);
429663e8e51Sths     }
430663e8e51Sths }
431663e8e51Sths 
432663e8e51Sths static void eepro100_cx_interrupt(EEPRO100State * s)
433663e8e51Sths {
434663e8e51Sths     /* CU completed action command. */
435663e8e51Sths     /* Transmit not ok (82557 only, not in emulation). */
436663e8e51Sths     eepro100_interrupt(s, 0x80);
437663e8e51Sths }
438663e8e51Sths 
439663e8e51Sths static void eepro100_cna_interrupt(EEPRO100State * s)
440663e8e51Sths {
441663e8e51Sths     /* CU left the active state. */
442663e8e51Sths     eepro100_interrupt(s, 0x20);
443663e8e51Sths }
444663e8e51Sths 
445663e8e51Sths static void eepro100_fr_interrupt(EEPRO100State * s)
446663e8e51Sths {
447663e8e51Sths     /* RU received a complete frame. */
448663e8e51Sths     eepro100_interrupt(s, 0x40);
449663e8e51Sths }
450663e8e51Sths 
451663e8e51Sths static void eepro100_rnr_interrupt(EEPRO100State * s)
452663e8e51Sths {
453663e8e51Sths     /* RU is not ready. */
454663e8e51Sths     eepro100_interrupt(s, 0x10);
455663e8e51Sths }
456663e8e51Sths 
457663e8e51Sths static void eepro100_mdi_interrupt(EEPRO100State * s)
458663e8e51Sths {
459663e8e51Sths     /* MDI completed read or write cycle. */
460663e8e51Sths     eepro100_interrupt(s, 0x08);
461663e8e51Sths }
462663e8e51Sths 
463663e8e51Sths static void eepro100_swi_interrupt(EEPRO100State * s)
464663e8e51Sths {
465663e8e51Sths     /* Software has requested an interrupt. */
466663e8e51Sths     eepro100_interrupt(s, 0x04);
467663e8e51Sths }
468663e8e51Sths 
469663e8e51Sths #if 0
470663e8e51Sths static void eepro100_fcp_interrupt(EEPRO100State * s)
471663e8e51Sths {
472663e8e51Sths     /* Flow control pause interrupt (82558 and later). */
473663e8e51Sths     eepro100_interrupt(s, 0x01);
474663e8e51Sths }
475663e8e51Sths #endif
476663e8e51Sths 
4779a7c2a59SMao Zhongyi static void e100_pci_reset(EEPRO100State *s, Error **errp)
478663e8e51Sths {
47940021f08SAnthony Liguori     E100PCIDeviceInfo *info = eepro100_get_class(s);
480663e8e51Sths     uint32_t device = s->device;
481273a2142SJuan Quintela     uint8_t *pci_conf = s->dev.config;
482663e8e51Sths 
483aac443e6SStefan Weil     TRACE(OTHER, logout("%p\n", s));
484663e8e51Sths 
485663e8e51Sths     /* PCI Status */
486558c8634SStefan Weil     pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM |
487558c8634SStefan Weil                                         PCI_STATUS_FAST_BACK);
488663e8e51Sths     /* PCI Latency Timer */
48915e89f59SMichael S. Tsirkin     pci_set_byte(pci_conf + PCI_LATENCY_TIMER, 0x20);   /* latency timer = 32 clocks */
490ae543b49SStefan Weil     /* Capability Pointer is set by PCI framework. */
491f62719caSStefan Weil     /* Interrupt Line */
492f62719caSStefan Weil     /* Interrupt Pin */
493f62719caSStefan Weil     pci_set_byte(pci_conf + PCI_INTERRUPT_PIN, 1);      /* interrupt pin A */
494663e8e51Sths     /* Minimum Grant */
49515e89f59SMichael S. Tsirkin     pci_set_byte(pci_conf + PCI_MIN_GNT, 0x08);
496663e8e51Sths     /* Maximum Latency */
49715e89f59SMichael S. Tsirkin     pci_set_byte(pci_conf + PCI_MAX_LAT, 0x18);
498663e8e51Sths 
49940021f08SAnthony Liguori     s->stats_size = info->stats_size;
50040021f08SAnthony Liguori     s->has_extended_tcb_support = info->has_extended_tcb_support;
501558c8634SStefan Weil 
502663e8e51Sths     switch (device) {
503ba42b646SStefan Weil     case i82550:
504663e8e51Sths     case i82551:
505ba42b646SStefan Weil     case i82557A:
506663e8e51Sths     case i82557B:
507663e8e51Sths     case i82557C:
508ba42b646SStefan Weil     case i82558A:
509663e8e51Sths     case i82558B:
510ba42b646SStefan Weil     case i82559A:
511ba42b646SStefan Weil     case i82559B:
512558c8634SStefan Weil     case i82559ER:
513558c8634SStefan Weil     case i82562:
514db667a12SStefan Weil     case i82801:
515663e8e51Sths     case i82559C:
516ba42b646SStefan Weil         break;
517663e8e51Sths     default:
518663e8e51Sths         logout("Device %X is undefined!\n", device);
519663e8e51Sths     }
520663e8e51Sths 
5213dec59a1SStefan Weil     /* Standard TxCB. */
5223dec59a1SStefan Weil     s->configuration[6] |= BIT(4);
5233dec59a1SStefan Weil 
524558c8634SStefan Weil     /* Standard statistical counters. */
525ba42b646SStefan Weil     s->configuration[6] |= BIT(5);
526ba42b646SStefan Weil 
527ba42b646SStefan Weil     if (s->stats_size == 80) {
528ba42b646SStefan Weil         /* TODO: check TCO Statistical Counters bit. Documentation not clear. */
529ba42b646SStefan Weil         if (s->configuration[6] & BIT(2)) {
530ba42b646SStefan Weil             /* TCO statistical counters. */
531ba42b646SStefan Weil             assert(s->configuration[6] & BIT(5));
532ba42b646SStefan Weil         } else {
533ba42b646SStefan Weil             if (s->configuration[6] & BIT(5)) {
534ba42b646SStefan Weil                 /* No extended statistical counters, i82557 compatible. */
535ba42b646SStefan Weil                 s->stats_size = 64;
536ba42b646SStefan Weil             } else {
537ba42b646SStefan Weil                 /* i82558 compatible. */
538ba42b646SStefan Weil                 s->stats_size = 76;
539ba42b646SStefan Weil             }
540ba42b646SStefan Weil         }
541ba42b646SStefan Weil     } else {
542ba42b646SStefan Weil         if (s->configuration[6] & BIT(5)) {
543ba42b646SStefan Weil             /* No extended statistical counters. */
544ba42b646SStefan Weil             s->stats_size = 64;
545ba42b646SStefan Weil         }
546ba42b646SStefan Weil     }
547ba42b646SStefan Weil     assert(s->stats_size > 0 && s->stats_size <= sizeof(s->statistics));
548ba42b646SStefan Weil 
54940021f08SAnthony Liguori     if (info->power_management) {
550ba42b646SStefan Weil         /* Power Management Capabilities */
5518bbd1ce2SMichael S. Tsirkin         int cfg_offset = 0xdc;
552ca77089dSIsaku Yamahata         int r = pci_add_capability(&s->dev, PCI_CAP_ID_PM,
5539a7c2a59SMao Zhongyi                                    cfg_offset, PCI_PM_SIZEOF,
5549a7c2a59SMao Zhongyi                                    errp);
5559a7c2a59SMao Zhongyi         if (r < 0) {
5569a7c2a59SMao Zhongyi             return;
5579a7c2a59SMao Zhongyi         }
5589a7c2a59SMao Zhongyi 
559ae543b49SStefan Weil         pci_set_word(pci_conf + cfg_offset + PCI_PM_PMC, 0x7e21);
560ae543b49SStefan Weil #if 0 /* TODO: replace dummy code for power management emulation. */
561ba42b646SStefan Weil         /* TODO: Power Management Control / Status. */
562ae543b49SStefan Weil         pci_set_word(pci_conf + cfg_offset + PCI_PM_CTRL, 0x0000);
563ba42b646SStefan Weil         /* TODO: Ethernet Power Consumption Registers (i82559 and later). */
564ae543b49SStefan Weil         pci_set_byte(pci_conf + cfg_offset + PCI_PM_PPB_EXTENSIONS, 0x0000);
565ae543b49SStefan Weil #endif
566ae543b49SStefan Weil     }
567ba42b646SStefan Weil 
568ba42b646SStefan Weil #if EEPROM_SIZE > 0
569663e8e51Sths     if (device == i82557C || device == i82558B || device == i82559C) {
570e7493b25SStefan Weil         /*
571e7493b25SStefan Weil         TODO: get vendor id from EEPROM for i82557C or later.
572e7493b25SStefan Weil         TODO: get device id from EEPROM for i82557C or later.
573e7493b25SStefan Weil         TODO: status bit 4 can be disabled by EEPROM for i82558, i82559.
574e7493b25SStefan Weil         TODO: header type is determined by EEPROM for i82559.
575e7493b25SStefan Weil         TODO: get subsystem id from EEPROM for i82557C or later.
576e7493b25SStefan Weil         TODO: get subsystem vendor id from EEPROM for i82557C or later.
577e7493b25SStefan Weil         TODO: exp. rom baddr depends on a bit in EEPROM for i82558 or later.
578e7493b25SStefan Weil         TODO: capability pointer depends on EEPROM for i82558.
579e7493b25SStefan Weil         */
580663e8e51Sths         logout("Get device id and revision from EEPROM!!!\n");
581663e8e51Sths     }
582ba42b646SStefan Weil #endif /* EEPROM_SIZE > 0 */
583663e8e51Sths }
584663e8e51Sths 
585663e8e51Sths static void nic_selective_reset(EEPRO100State * s)
586663e8e51Sths {
587663e8e51Sths     size_t i;
588663e8e51Sths     uint16_t *eeprom_contents = eeprom93xx_data(s->eeprom);
589e7493b25SStefan Weil #if 0
590e7493b25SStefan Weil     eeprom93xx_reset(s->eeprom);
591e7493b25SStefan Weil #endif
592508ef936SGerd Hoffmann     memcpy(eeprom_contents, s->conf.macaddr.a, 6);
593b1e87018SStefan Weil     eeprom_contents[EEPROM_ID] = EEPROM_ID_VALID;
594f4e94dfeS=?UTF-8?q?Reimar=20D=C3=B6ffinger?=     if (s->device == i82557B || s->device == i82557C)
595f4e94dfeS=?UTF-8?q?Reimar=20D=C3=B6ffinger?=         eeprom_contents[5] = 0x0100;
5966cded3a4SStefan Weil     eeprom_contents[EEPROM_PHY_ID] = 1;
597663e8e51Sths     uint16_t sum = 0;
598663e8e51Sths     for (i = 0; i < EEPROM_SIZE - 1; i++) {
599663e8e51Sths         sum += eeprom_contents[i];
600663e8e51Sths     }
601663e8e51Sths     eeprom_contents[EEPROM_SIZE - 1] = 0xbaba - sum;
602aac443e6SStefan Weil     TRACE(EEPROM, logout("checksum=0x%04x\n", eeprom_contents[EEPROM_SIZE - 1]));
603663e8e51Sths 
604663e8e51Sths     memset(s->mem, 0, sizeof(s->mem));
605e5e23ab8SStefan Weil     e100_write_reg4(s, SCBCtrlMDI, BIT(21));
606663e8e51Sths 
607663e8e51Sths     assert(sizeof(s->mdimem) == sizeof(eepro100_mdi_default));
608663e8e51Sths     memcpy(&s->mdimem[0], &eepro100_mdi_default[0], sizeof(s->mdimem));
609663e8e51Sths }
610663e8e51Sths 
611663e8e51Sths static void nic_reset(void *opaque)
612663e8e51Sths {
613769cf7a5SJuan Quintela     EEPRO100State *s = opaque;
614aac443e6SStefan Weil     TRACE(OTHER, logout("%p\n", s));
615010ec629SStefan Weil     /* TODO: Clearing of hash register for selective reset, too? */
6167b8737deSStefan Weil     memset(&s->mult[0], 0, sizeof(s->mult));
617663e8e51Sths     nic_selective_reset(s);
618663e8e51Sths }
619663e8e51Sths 
620663e8e51Sths #if defined(DEBUG_EEPRO100)
621b8f6ba0dSStefan Weil static const char * const e100_reg[PCI_IO_SIZE / 4] = {
622663e8e51Sths     "Command/Status",
623663e8e51Sths     "General Pointer",
624663e8e51Sths     "Port",
625663e8e51Sths     "EEPROM/Flash Control",
626663e8e51Sths     "MDI Control",
627663e8e51Sths     "Receive DMA Byte Count",
628b8f6ba0dSStefan Weil     "Flow Control",
629663e8e51Sths     "General Status/Control"
630663e8e51Sths };
631663e8e51Sths 
632663e8e51Sths static char *regname(uint32_t addr)
633663e8e51Sths {
634ec169288SDavid Benjamin     static char buf[32];
635663e8e51Sths     if (addr < PCI_IO_SIZE) {
636b8f6ba0dSStefan Weil         const char *r = e100_reg[addr / 4];
637663e8e51Sths         if (r != 0) {
63841cbc23cSStefan Weil             snprintf(buf, sizeof(buf), "%s+%u", r, addr % 4);
639663e8e51Sths         } else {
64041cbc23cSStefan Weil             snprintf(buf, sizeof(buf), "0x%02x", addr);
641663e8e51Sths         }
642663e8e51Sths     } else {
64341cbc23cSStefan Weil         snprintf(buf, sizeof(buf), "??? 0x%08x", addr);
644663e8e51Sths     }
645663e8e51Sths     return buf;
646663e8e51Sths }
647663e8e51Sths #endif                          /* DEBUG_EEPRO100 */
648663e8e51Sths 
649663e8e51Sths /*****************************************************************************
650663e8e51Sths  *
651663e8e51Sths  * Command emulation.
652663e8e51Sths  *
653663e8e51Sths  ****************************************************************************/
654663e8e51Sths 
655663e8e51Sths #if 0
656663e8e51Sths static uint16_t eepro100_read_command(EEPRO100State * s)
657663e8e51Sths {
658663e8e51Sths     uint16_t val = 0xffff;
659e7493b25SStefan Weil     TRACE(OTHER, logout("val=0x%04x\n", val));
660663e8e51Sths     return val;
661663e8e51Sths }
662663e8e51Sths #endif
663663e8e51Sths 
664663e8e51Sths /* Commands that can be put in a command list entry. */
665663e8e51Sths enum commands {
666663e8e51Sths     CmdNOp = 0,
667663e8e51Sths     CmdIASetup = 1,
668663e8e51Sths     CmdConfigure = 2,
669663e8e51Sths     CmdMulticastList = 3,
670663e8e51Sths     CmdTx = 4,
671663e8e51Sths     CmdTDR = 5,                 /* load microcode */
672663e8e51Sths     CmdDump = 6,
673663e8e51Sths     CmdDiagnose = 7,
674663e8e51Sths 
675663e8e51Sths     /* And some extra flags: */
676663e8e51Sths     CmdSuspend = 0x4000,        /* Suspend after completion. */
677663e8e51Sths     CmdIntr = 0x2000,           /* Interrupt after completion. */
678663e8e51Sths     CmdTxFlex = 0x0008,         /* Use "Flexible mode" for CmdTx command. */
679663e8e51Sths };
680663e8e51Sths 
681c227f099SAnthony Liguori static cu_state_t get_cu_state(EEPRO100State * s)
682663e8e51Sths {
683ced5296aSStefan Weil     return ((s->mem[SCBStatus] & BITS(7, 6)) >> 6);
684663e8e51Sths }
685663e8e51Sths 
686c227f099SAnthony Liguori static void set_cu_state(EEPRO100State * s, cu_state_t state)
687663e8e51Sths {
688ced5296aSStefan Weil     s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(7, 6)) + (state << 6);
689663e8e51Sths }
690663e8e51Sths 
691c227f099SAnthony Liguori static ru_state_t get_ru_state(EEPRO100State * s)
692663e8e51Sths {
693ced5296aSStefan Weil     return ((s->mem[SCBStatus] & BITS(5, 2)) >> 2);
694663e8e51Sths }
695663e8e51Sths 
696c227f099SAnthony Liguori static void set_ru_state(EEPRO100State * s, ru_state_t state)
697663e8e51Sths {
698ced5296aSStefan Weil     s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(5, 2)) + (state << 2);
699663e8e51Sths }
700663e8e51Sths 
701663e8e51Sths static void dump_statistics(EEPRO100State * s)
702663e8e51Sths {
703663e8e51Sths     /* Dump statistical data. Most data is never changed by the emulation
704663e8e51Sths      * and always 0, so we first just copy the whole block and then those
705663e8e51Sths      * values which really matter.
706663e8e51Sths      * Number of data should check configuration!!!
707663e8e51Sths      */
708e965d4bcSDavid Gibson     pci_dma_write(&s->dev, s->statsaddr, &s->statistics, s->stats_size);
70916ef60c9SEduard - Gabriel Munteanu     stl_le_pci_dma(&s->dev, s->statsaddr + 0,
71016ef60c9SEduard - Gabriel Munteanu                    s->statistics.tx_good_frames);
71116ef60c9SEduard - Gabriel Munteanu     stl_le_pci_dma(&s->dev, s->statsaddr + 36,
71216ef60c9SEduard - Gabriel Munteanu                    s->statistics.rx_good_frames);
71316ef60c9SEduard - Gabriel Munteanu     stl_le_pci_dma(&s->dev, s->statsaddr + 48,
71416ef60c9SEduard - Gabriel Munteanu                    s->statistics.rx_resource_errors);
71516ef60c9SEduard - Gabriel Munteanu     stl_le_pci_dma(&s->dev, s->statsaddr + 60,
71616ef60c9SEduard - Gabriel Munteanu                    s->statistics.rx_short_frame_errors);
717e7493b25SStefan Weil #if 0
71816ef60c9SEduard - Gabriel Munteanu     stw_le_pci_dma(&s->dev, s->statsaddr + 76, s->statistics.xmt_tco_frames);
71916ef60c9SEduard - Gabriel Munteanu     stw_le_pci_dma(&s->dev, s->statsaddr + 78, s->statistics.rcv_tco_frames);
720e7493b25SStefan Weil     missing("CU dump statistical counters");
721e7493b25SStefan Weil #endif
722663e8e51Sths }
723663e8e51Sths 
7243d0f4b9bSStefan Weil static void read_cb(EEPRO100State *s)
7253d0f4b9bSStefan Weil {
726e965d4bcSDavid Gibson     pci_dma_read(&s->dev, s->cb_address, &s->tx, sizeof(s->tx));
7273d0f4b9bSStefan Weil     s->tx.status = le16_to_cpu(s->tx.status);
7283d0f4b9bSStefan Weil     s->tx.command = le16_to_cpu(s->tx.command);
7293d0f4b9bSStefan Weil     s->tx.link = le32_to_cpu(s->tx.link);
7303d0f4b9bSStefan Weil     s->tx.tbd_array_addr = le32_to_cpu(s->tx.tbd_array_addr);
7313d0f4b9bSStefan Weil     s->tx.tcb_bytes = le16_to_cpu(s->tx.tcb_bytes);
7323d0f4b9bSStefan Weil }
7333d0f4b9bSStefan Weil 
734f3a52e50SStefan Weil static void tx_command(EEPRO100State *s)
735663e8e51Sths {
7368f8e8053SThomas Huth     uint32_t tbd_array = s->tx.tbd_array_addr;
7378f8e8053SThomas Huth     uint16_t tcb_bytes = s->tx.tcb_bytes & 0x3fff;
738f3a52e50SStefan Weil     /* Sends larger than MAX_ETH_FRAME_SIZE are allowed, up to 2600 bytes. */
739f3a52e50SStefan Weil     uint8_t buf[2600];
740f3a52e50SStefan Weil     uint16_t size = 0;
741f3a52e50SStefan Weil     uint32_t tbd_address = s->cb_address + 0x10;
742aac443e6SStefan Weil     TRACE(RXTX, logout
743663e8e51Sths         ("transmit, TBD array address 0x%08x, TCB byte count 0x%04x, TBD count %u\n",
744f3a52e50SStefan Weil          tbd_array, tcb_bytes, s->tx.tbd_count));
7457f1e9d4eSKevin Wolf 
7467f1e9d4eSKevin Wolf     if (tcb_bytes > 2600) {
7477f1e9d4eSKevin Wolf         logout("TCB byte count too large, using 2600\n");
7487f1e9d4eSKevin Wolf         tcb_bytes = 2600;
7497f1e9d4eSKevin Wolf     }
750663e8e51Sths     if (!((tcb_bytes > 0) || (tbd_array != 0xffffffff))) {
751663e8e51Sths         logout
752663e8e51Sths             ("illegal values of TBD array address and TCB byte count!\n");
753663e8e51Sths     }
754663e8e51Sths     assert(tcb_bytes <= sizeof(buf));
755663e8e51Sths     while (size < tcb_bytes) {
756aac443e6SStefan Weil         TRACE(RXTX, logout
757663e8e51Sths             ("TBD (simplified mode): buffer address 0x%08x, size 0x%04x\n",
7581865e288SMike Nawrocki              tbd_address, tcb_bytes));
7591865e288SMike Nawrocki         pci_dma_read(&s->dev, tbd_address, &buf[size], tcb_bytes);
7601865e288SMike Nawrocki         size += tcb_bytes;
761663e8e51Sths     }
762663e8e51Sths     if (tbd_array == 0xffffffff) {
763663e8e51Sths         /* Simplified mode. Was already handled by code above. */
764663e8e51Sths     } else {
765663e8e51Sths         /* Flexible mode. */
766663e8e51Sths         uint8_t tbd_count = 0;
767ba42b646SStefan Weil         if (s->has_extended_tcb_support && !(s->configuration[6] & BIT(4))) {
7683f9cb1c1SNaphtali Sprei             /* Extended Flexible TCB. */
769663e8e51Sths             for (; tbd_count < 2; tbd_count++) {
77016ef60c9SEduard - Gabriel Munteanu                 uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev,
77116ef60c9SEduard - Gabriel Munteanu                                                             tbd_address);
77216ef60c9SEduard - Gabriel Munteanu                 uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev,
77316ef60c9SEduard - Gabriel Munteanu                                                           tbd_address + 4);
77416ef60c9SEduard - Gabriel Munteanu                 uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev,
77516ef60c9SEduard - Gabriel Munteanu                                                         tbd_address + 6);
776663e8e51Sths                 tbd_address += 8;
777aac443e6SStefan Weil                 TRACE(RXTX, logout
7783f9cb1c1SNaphtali Sprei                     ("TBD (extended flexible mode): buffer address 0x%08x, size 0x%04x\n",
779aac443e6SStefan Weil                      tx_buffer_address, tx_buffer_size));
78024e6f355SReimar Döffinger                 tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size);
78116ef60c9SEduard - Gabriel Munteanu                 pci_dma_read(&s->dev, tx_buffer_address,
78216ef60c9SEduard - Gabriel Munteanu                              &buf[size], tx_buffer_size);
783663e8e51Sths                 size += tx_buffer_size;
784663e8e51Sths                 if (tx_buffer_el & 1) {
785663e8e51Sths                     break;
786663e8e51Sths                 }
787663e8e51Sths             }
788663e8e51Sths         }
789663e8e51Sths         tbd_address = tbd_array;
790f3a52e50SStefan Weil         for (; tbd_count < s->tx.tbd_count; tbd_count++) {
79116ef60c9SEduard - Gabriel Munteanu             uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev, tbd_address);
79216ef60c9SEduard - Gabriel Munteanu             uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev, tbd_address + 4);
79316ef60c9SEduard - Gabriel Munteanu             uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev, tbd_address + 6);
794663e8e51Sths             tbd_address += 8;
795aac443e6SStefan Weil             TRACE(RXTX, logout
796663e8e51Sths                 ("TBD (flexible mode): buffer address 0x%08x, size 0x%04x\n",
797aac443e6SStefan Weil                  tx_buffer_address, tx_buffer_size));
79824e6f355SReimar Döffinger             tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size);
79916ef60c9SEduard - Gabriel Munteanu             pci_dma_read(&s->dev, tx_buffer_address,
80016ef60c9SEduard - Gabriel Munteanu                          &buf[size], tx_buffer_size);
801663e8e51Sths             size += tx_buffer_size;
802663e8e51Sths             if (tx_buffer_el & 1) {
803663e8e51Sths                 break;
804663e8e51Sths             }
805663e8e51Sths         }
806663e8e51Sths     }
807aac443e6SStefan Weil     TRACE(RXTX, logout("%p sending frame, len=%d,%s\n", s, size, nic_dump(buf, size)));
808b356f76dSJason Wang     qemu_send_packet(qemu_get_queue(s->nic), buf, size);
809663e8e51Sths     s->statistics.tx_good_frames++;
810663e8e51Sths     /* Transmit with bad status would raise an CX/TNO interrupt.
811663e8e51Sths      * (82557 only). Emulation never has bad status. */
812e7493b25SStefan Weil #if 0
813e7493b25SStefan Weil     eepro100_cx_interrupt(s);
814e7493b25SStefan Weil #endif
815f3a52e50SStefan Weil }
816f3a52e50SStefan Weil 
8177b8737deSStefan Weil static void set_multicast_list(EEPRO100State *s)
8187b8737deSStefan Weil {
8197b8737deSStefan Weil     uint16_t multicast_count = s->tx.tbd_array_addr & BITS(13, 0);
8207b8737deSStefan Weil     uint16_t i;
8217b8737deSStefan Weil     memset(&s->mult[0], 0, sizeof(s->mult));
8227b8737deSStefan Weil     TRACE(OTHER, logout("multicast list, multicast count = %u\n", multicast_count));
8237b8737deSStefan Weil     for (i = 0; i < multicast_count; i += 6) {
8247b8737deSStefan Weil         uint8_t multicast_addr[6];
82516ef60c9SEduard - Gabriel Munteanu         pci_dma_read(&s->dev, s->cb_address + 10 + i, multicast_addr, 6);
8267b8737deSStefan Weil         TRACE(OTHER, logout("multicast entry %s\n", nic_dump(multicast_addr, 6)));
8277c0348bdSMark Cave-Ayland         unsigned mcast_idx = (net_crc32(multicast_addr, ETH_ALEN) &
8287c0348bdSMark Cave-Ayland                               BITS(7, 2)) >> 2;
8297b8737deSStefan Weil         assert(mcast_idx < 64);
8307b8737deSStefan Weil         s->mult[mcast_idx >> 3] |= (1 << (mcast_idx & 7));
8317b8737deSStefan Weil     }
8327b8737deSStefan Weil }
8337b8737deSStefan Weil 
834f3a52e50SStefan Weil static void action_command(EEPRO100State *s)
835f3a52e50SStefan Weil {
83600837731SStefan Weil     /* The loop below won't stop if it gets special handcrafted data.
83700837731SStefan Weil        Therefore we limit the number of iterations. */
83800837731SStefan Weil     unsigned max_loop_count = 16;
83900837731SStefan Weil 
840f3a52e50SStefan Weil     for (;;) {
8413d0f4b9bSStefan Weil         bool bit_el;
8423d0f4b9bSStefan Weil         bool bit_s;
8433d0f4b9bSStefan Weil         bool bit_i;
8443d0f4b9bSStefan Weil         bool bit_nc;
84575f5a6ccSStefan Weil         uint16_t ok_status = STATUS_OK;
8463d0f4b9bSStefan Weil         s->cb_address = s->cu_base + s->cu_offset;
8473d0f4b9bSStefan Weil         read_cb(s);
8483d0f4b9bSStefan Weil         bit_el = ((s->tx.command & COMMAND_EL) != 0);
8493d0f4b9bSStefan Weil         bit_s = ((s->tx.command & COMMAND_S) != 0);
8503d0f4b9bSStefan Weil         bit_i = ((s->tx.command & COMMAND_I) != 0);
8513d0f4b9bSStefan Weil         bit_nc = ((s->tx.command & COMMAND_NC) != 0);
8523d0f4b9bSStefan Weil #if 0
8533d0f4b9bSStefan Weil         bool bit_sf = ((s->tx.command & COMMAND_SF) != 0);
8543d0f4b9bSStefan Weil #endif
85500837731SStefan Weil 
85600837731SStefan Weil         if (max_loop_count-- == 0) {
85700837731SStefan Weil             /* Prevent an endless loop. */
85800837731SStefan Weil             logout("loop in %s:%u\n", __FILE__, __LINE__);
85900837731SStefan Weil             break;
86000837731SStefan Weil         }
86100837731SStefan Weil 
8623d0f4b9bSStefan Weil         s->cu_offset = s->tx.link;
8633d0f4b9bSStefan Weil         TRACE(OTHER,
8643d0f4b9bSStefan Weil               logout("val=(cu start), status=0x%04x, command=0x%04x, link=0x%08x\n",
8653d0f4b9bSStefan Weil                      s->tx.status, s->tx.command, s->tx.link));
8663d0f4b9bSStefan Weil         switch (s->tx.command & COMMAND_CMD) {
867f3a52e50SStefan Weil         case CmdNOp:
868f3a52e50SStefan Weil             /* Do nothing. */
869f3a52e50SStefan Weil             break;
870f3a52e50SStefan Weil         case CmdIASetup:
87116ef60c9SEduard - Gabriel Munteanu             pci_dma_read(&s->dev, s->cb_address + 8, &s->conf.macaddr.a[0], 6);
872ce0e58b3SStefan Weil             TRACE(OTHER, logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6)));
873f3a52e50SStefan Weil             break;
874f3a52e50SStefan Weil         case CmdConfigure:
87516ef60c9SEduard - Gabriel Munteanu             pci_dma_read(&s->dev, s->cb_address + 8,
87616ef60c9SEduard - Gabriel Munteanu                          &s->configuration[0], sizeof(s->configuration));
877010ec629SStefan Weil             TRACE(OTHER, logout("configuration: %s\n",
878010ec629SStefan Weil                                 nic_dump(&s->configuration[0], 16)));
879010ec629SStefan Weil             TRACE(OTHER, logout("configuration: %s\n",
880010ec629SStefan Weil                                 nic_dump(&s->configuration[16],
881010ec629SStefan Weil                                 ARRAY_SIZE(s->configuration) - 16)));
882010ec629SStefan Weil             if (s->configuration[20] & BIT(6)) {
883010ec629SStefan Weil                 TRACE(OTHER, logout("Multiple IA bit\n"));
884010ec629SStefan Weil             }
885f3a52e50SStefan Weil             break;
886f3a52e50SStefan Weil         case CmdMulticastList:
8877b8737deSStefan Weil             set_multicast_list(s);
888f3a52e50SStefan Weil             break;
889f3a52e50SStefan Weil         case CmdTx:
890f3a52e50SStefan Weil             if (bit_nc) {
891f3a52e50SStefan Weil                 missing("CmdTx: NC = 0");
89275f5a6ccSStefan Weil                 ok_status = 0;
893f3a52e50SStefan Weil                 break;
894f3a52e50SStefan Weil             }
895f3a52e50SStefan Weil             tx_command(s);
896663e8e51Sths             break;
897663e8e51Sths         case CmdTDR:
898aac443e6SStefan Weil             TRACE(OTHER, logout("load microcode\n"));
899663e8e51Sths             /* Starting with offset 8, the command contains
900663e8e51Sths              * 64 dwords microcode which we just ignore here. */
901663e8e51Sths             break;
902f80a7fc3SStefan Weil         case CmdDiagnose:
903f80a7fc3SStefan Weil             TRACE(OTHER, logout("diagnose\n"));
904f80a7fc3SStefan Weil             /* Make sure error flag is not set. */
905f80a7fc3SStefan Weil             s->tx.status = 0;
906f80a7fc3SStefan Weil             break;
907663e8e51Sths         default:
908663e8e51Sths             missing("undefined command");
90975f5a6ccSStefan Weil             ok_status = 0;
9107f1e9d4eSKevin Wolf             break;
911663e8e51Sths         }
9127f1e9d4eSKevin Wolf         /* Write new status. */
91316ef60c9SEduard - Gabriel Munteanu         stw_le_pci_dma(&s->dev, s->cb_address,
91416ef60c9SEduard - Gabriel Munteanu                        s->tx.status | ok_status | STATUS_C);
915663e8e51Sths         if (bit_i) {
916663e8e51Sths             /* CU completed action. */
917663e8e51Sths             eepro100_cx_interrupt(s);
918663e8e51Sths         }
919663e8e51Sths         if (bit_el) {
920aac443e6SStefan Weil             /* CU becomes idle. Terminate command loop. */
921663e8e51Sths             set_cu_state(s, cu_idle);
922663e8e51Sths             eepro100_cna_interrupt(s);
9235fa9a0aeSStefan Weil             break;
924663e8e51Sths         } else if (bit_s) {
9255fa9a0aeSStefan Weil             /* CU becomes suspended. Terminate command loop. */
926663e8e51Sths             set_cu_state(s, cu_suspended);
927663e8e51Sths             eepro100_cna_interrupt(s);
9285fa9a0aeSStefan Weil             break;
929663e8e51Sths         } else {
930663e8e51Sths             /* More entries in list. */
931aac443e6SStefan Weil             TRACE(OTHER, logout("CU list with at least one more entry\n"));
9325fa9a0aeSStefan Weil         }
933663e8e51Sths     }
934aac443e6SStefan Weil     TRACE(OTHER, logout("CU list empty\n"));
935663e8e51Sths     /* List is empty. Now CU is idle or suspended. */
9365fa9a0aeSStefan Weil }
9375fa9a0aeSStefan Weil 
9385fa9a0aeSStefan Weil static void eepro100_cu_command(EEPRO100State * s, uint8_t val)
9395fa9a0aeSStefan Weil {
940cb25a3fbSStefan Weil     cu_state_t cu_state;
9415fa9a0aeSStefan Weil     switch (val) {
9425fa9a0aeSStefan Weil     case CU_NOP:
9435fa9a0aeSStefan Weil         /* No operation. */
9445fa9a0aeSStefan Weil         break;
9455fa9a0aeSStefan Weil     case CU_START:
946cb25a3fbSStefan Weil         cu_state = get_cu_state(s);
947cb25a3fbSStefan Weil         if (cu_state != cu_idle && cu_state != cu_suspended) {
948cb25a3fbSStefan Weil             /* Intel documentation says that CU must be idle or suspended
949cb25a3fbSStefan Weil              * for the CU start command. */
950cb25a3fbSStefan Weil             logout("unexpected CU state is %u\n", cu_state);
9515fa9a0aeSStefan Weil         }
9525fa9a0aeSStefan Weil         set_cu_state(s, cu_active);
95327a05006SStefan Weil         s->cu_offset = e100_read_reg4(s, SCBPointer);
9545fa9a0aeSStefan Weil         action_command(s);
955663e8e51Sths         break;
956663e8e51Sths     case CU_RESUME:
957663e8e51Sths         if (get_cu_state(s) != cu_suspended) {
958663e8e51Sths             logout("bad CU resume from CU state %u\n", get_cu_state(s));
959663e8e51Sths             /* Workaround for bad Linux eepro100 driver which resumes
960663e8e51Sths              * from idle state. */
961e7493b25SStefan Weil #if 0
962e7493b25SStefan Weil             missing("cu resume");
963e7493b25SStefan Weil #endif
964663e8e51Sths             set_cu_state(s, cu_suspended);
965663e8e51Sths         }
966663e8e51Sths         if (get_cu_state(s) == cu_suspended) {
967aac443e6SStefan Weil             TRACE(OTHER, logout("CU resuming\n"));
968663e8e51Sths             set_cu_state(s, cu_active);
9695fa9a0aeSStefan Weil             action_command(s);
970663e8e51Sths         }
971663e8e51Sths         break;
972663e8e51Sths     case CU_STATSADDR:
973663e8e51Sths         /* Load dump counters address. */
97427a05006SStefan Weil         s->statsaddr = e100_read_reg4(s, SCBPointer);
975c16ada98SStefan Weil         TRACE(OTHER, logout("val=0x%02x (dump counters address)\n", val));
976c16ada98SStefan Weil         if (s->statsaddr & 3) {
977c16ada98SStefan Weil             /* Memory must be Dword aligned. */
978c16ada98SStefan Weil             logout("unaligned dump counters address\n");
979c16ada98SStefan Weil             /* Handling of misaligned addresses is undefined.
980c16ada98SStefan Weil              * Here we align the address by ignoring the lower bits. */
981c16ada98SStefan Weil             /* TODO: Test unaligned dump counter address on real hardware. */
982c16ada98SStefan Weil             s->statsaddr &= ~3;
983c16ada98SStefan Weil         }
984663e8e51Sths         break;
985663e8e51Sths     case CU_SHOWSTATS:
986663e8e51Sths         /* Dump statistical counters. */
987aac443e6SStefan Weil         TRACE(OTHER, logout("val=0x%02x (dump stats)\n", val));
988663e8e51Sths         dump_statistics(s);
98916ef60c9SEduard - Gabriel Munteanu         stl_le_pci_dma(&s->dev, s->statsaddr + s->stats_size, 0xa005);
990663e8e51Sths         break;
991663e8e51Sths     case CU_CMD_BASE:
992663e8e51Sths         /* Load CU base. */
993aac443e6SStefan Weil         TRACE(OTHER, logout("val=0x%02x (CU base address)\n", val));
99427a05006SStefan Weil         s->cu_base = e100_read_reg4(s, SCBPointer);
995663e8e51Sths         break;
996663e8e51Sths     case CU_DUMPSTATS:
997663e8e51Sths         /* Dump and reset statistical counters. */
998aac443e6SStefan Weil         TRACE(OTHER, logout("val=0x%02x (dump stats and reset)\n", val));
999663e8e51Sths         dump_statistics(s);
100016ef60c9SEduard - Gabriel Munteanu         stl_le_pci_dma(&s->dev, s->statsaddr + s->stats_size, 0xa007);
1001663e8e51Sths         memset(&s->statistics, 0, sizeof(s->statistics));
1002663e8e51Sths         break;
1003663e8e51Sths     case CU_SRESUME:
1004663e8e51Sths         /* CU static resume. */
1005663e8e51Sths         missing("CU static resume");
1006663e8e51Sths         break;
1007663e8e51Sths     default:
1008663e8e51Sths         missing("Undefined CU command");
1009663e8e51Sths     }
1010663e8e51Sths }
1011663e8e51Sths 
1012663e8e51Sths static void eepro100_ru_command(EEPRO100State * s, uint8_t val)
1013663e8e51Sths {
1014663e8e51Sths     switch (val) {
1015663e8e51Sths     case RU_NOP:
1016663e8e51Sths         /* No operation. */
1017663e8e51Sths         break;
1018663e8e51Sths     case RX_START:
1019663e8e51Sths         /* RU start. */
1020663e8e51Sths         if (get_ru_state(s) != ru_idle) {
1021663e8e51Sths             logout("RU state is %u, should be %u\n", get_ru_state(s), ru_idle);
1022e7493b25SStefan Weil #if 0
1023e7493b25SStefan Weil             assert(!"wrong RU state");
1024e7493b25SStefan Weil #endif
1025663e8e51Sths         }
1026663e8e51Sths         set_ru_state(s, ru_ready);
102727a05006SStefan Weil         s->ru_offset = e100_read_reg4(s, SCBPointer);
1028b356f76dSJason Wang         qemu_flush_queued_packets(qemu_get_queue(s->nic));
1029aac443e6SStefan Weil         TRACE(OTHER, logout("val=0x%02x (rx start)\n", val));
1030663e8e51Sths         break;
1031663e8e51Sths     case RX_RESUME:
1032663e8e51Sths         /* Restart RU. */
1033663e8e51Sths         if (get_ru_state(s) != ru_suspended) {
1034663e8e51Sths             logout("RU state is %u, should be %u\n", get_ru_state(s),
1035663e8e51Sths                    ru_suspended);
1036e7493b25SStefan Weil #if 0
1037e7493b25SStefan Weil             assert(!"wrong RU state");
1038e7493b25SStefan Weil #endif
1039663e8e51Sths         }
1040663e8e51Sths         set_ru_state(s, ru_ready);
1041663e8e51Sths         break;
1042e824012bSStefan Weil     case RU_ABORT:
1043e824012bSStefan Weil         /* RU abort. */
1044e824012bSStefan Weil         if (get_ru_state(s) == ru_ready) {
1045e824012bSStefan Weil             eepro100_rnr_interrupt(s);
1046e824012bSStefan Weil         }
1047e824012bSStefan Weil         set_ru_state(s, ru_idle);
1048e824012bSStefan Weil         break;
1049663e8e51Sths     case RX_ADDR_LOAD:
1050663e8e51Sths         /* Load RU base. */
1051aac443e6SStefan Weil         TRACE(OTHER, logout("val=0x%02x (RU base address)\n", val));
105227a05006SStefan Weil         s->ru_base = e100_read_reg4(s, SCBPointer);
1053663e8e51Sths         break;
1054663e8e51Sths     default:
1055663e8e51Sths         logout("val=0x%02x (undefined RU command)\n", val);
1056663e8e51Sths         missing("Undefined SU command");
1057663e8e51Sths     }
1058663e8e51Sths }
1059663e8e51Sths 
1060663e8e51Sths static void eepro100_write_command(EEPRO100State * s, uint8_t val)
1061663e8e51Sths {
1062663e8e51Sths     eepro100_ru_command(s, val & 0x0f);
1063663e8e51Sths     eepro100_cu_command(s, val & 0xf0);
1064663e8e51Sths     if ((val) == 0) {
1065aac443e6SStefan Weil         TRACE(OTHER, logout("val=0x%02x\n", val));
1066663e8e51Sths     }
1067663e8e51Sths     /* Clear command byte after command was accepted. */
1068663e8e51Sths     s->mem[SCBCmd] = 0;
1069663e8e51Sths }
1070663e8e51Sths 
1071663e8e51Sths /*****************************************************************************
1072663e8e51Sths  *
1073663e8e51Sths  * EEPROM emulation.
1074663e8e51Sths  *
1075663e8e51Sths  ****************************************************************************/
1076663e8e51Sths 
1077663e8e51Sths #define EEPROM_CS       0x02
1078663e8e51Sths #define EEPROM_SK       0x01
1079663e8e51Sths #define EEPROM_DI       0x04
1080663e8e51Sths #define EEPROM_DO       0x08
1081663e8e51Sths 
1082663e8e51Sths static uint16_t eepro100_read_eeprom(EEPRO100State * s)
1083663e8e51Sths {
1084e5e23ab8SStefan Weil     uint16_t val = e100_read_reg2(s, SCBeeprom);
1085663e8e51Sths     if (eeprom93xx_read(s->eeprom)) {
1086663e8e51Sths         val |= EEPROM_DO;
1087663e8e51Sths     } else {
1088663e8e51Sths         val &= ~EEPROM_DO;
1089663e8e51Sths     }
1090aac443e6SStefan Weil     TRACE(EEPROM, logout("val=0x%04x\n", val));
1091663e8e51Sths     return val;
1092663e8e51Sths }
1093663e8e51Sths 
1094c227f099SAnthony Liguori static void eepro100_write_eeprom(eeprom_t * eeprom, uint8_t val)
1095663e8e51Sths {
1096aac443e6SStefan Weil     TRACE(EEPROM, logout("val=0x%02x\n", val));
1097663e8e51Sths 
1098ebabb67aSStefan Weil     /* mask unwritable bits */
1099e7493b25SStefan Weil #if 0
1100e7493b25SStefan Weil     val = SET_MASKED(val, 0x31, eeprom->value);
1101e7493b25SStefan Weil #endif
1102663e8e51Sths 
1103663e8e51Sths     int eecs = ((val & EEPROM_CS) != 0);
1104663e8e51Sths     int eesk = ((val & EEPROM_SK) != 0);
1105663e8e51Sths     int eedi = ((val & EEPROM_DI) != 0);
1106663e8e51Sths     eeprom93xx_write(eeprom, eecs, eesk, eedi);
1107663e8e51Sths }
1108663e8e51Sths 
1109663e8e51Sths /*****************************************************************************
1110663e8e51Sths  *
1111663e8e51Sths  * MDI emulation.
1112663e8e51Sths  *
1113663e8e51Sths  ****************************************************************************/
1114663e8e51Sths 
1115663e8e51Sths #if defined(DEBUG_EEPRO100)
11166a0b9cc9SReimar Döffinger static const char * const mdi_op_name[] = {
1117663e8e51Sths     "opcode 0",
1118663e8e51Sths     "write",
1119663e8e51Sths     "read",
1120663e8e51Sths     "opcode 3"
1121663e8e51Sths };
1122663e8e51Sths 
11236a0b9cc9SReimar Döffinger static const char * const mdi_reg_name[] = {
1124663e8e51Sths     "Control",
1125663e8e51Sths     "Status",
1126663e8e51Sths     "PHY Identification (Word 1)",
1127663e8e51Sths     "PHY Identification (Word 2)",
1128663e8e51Sths     "Auto-Negotiation Advertisement",
1129663e8e51Sths     "Auto-Negotiation Link Partner Ability",
1130663e8e51Sths     "Auto-Negotiation Expansion"
1131663e8e51Sths };
1132aac443e6SStefan Weil 
1133aac443e6SStefan Weil static const char *reg2name(uint8_t reg)
1134aac443e6SStefan Weil {
1135aac443e6SStefan Weil     static char buffer[10];
1136aac443e6SStefan Weil     const char *p = buffer;
1137aac443e6SStefan Weil     if (reg < ARRAY_SIZE(mdi_reg_name)) {
1138aac443e6SStefan Weil         p = mdi_reg_name[reg];
1139aac443e6SStefan Weil     } else {
1140aac443e6SStefan Weil         snprintf(buffer, sizeof(buffer), "reg=0x%02x", reg);
1141aac443e6SStefan Weil     }
1142aac443e6SStefan Weil     return p;
1143aac443e6SStefan Weil }
1144663e8e51Sths #endif                          /* DEBUG_EEPRO100 */
1145663e8e51Sths 
1146663e8e51Sths static uint32_t eepro100_read_mdi(EEPRO100State * s)
1147663e8e51Sths {
1148e5e23ab8SStefan Weil     uint32_t val = e100_read_reg4(s, SCBCtrlMDI);
1149663e8e51Sths 
1150663e8e51Sths #ifdef DEBUG_EEPRO100
1151663e8e51Sths     uint8_t raiseint = (val & BIT(29)) >> 29;
1152663e8e51Sths     uint8_t opcode = (val & BITS(27, 26)) >> 26;
1153663e8e51Sths     uint8_t phy = (val & BITS(25, 21)) >> 21;
1154663e8e51Sths     uint8_t reg = (val & BITS(20, 16)) >> 16;
1155663e8e51Sths     uint16_t data = (val & BITS(15, 0));
1156663e8e51Sths #endif
1157663e8e51Sths     /* Emulation takes no time to finish MDI transaction. */
1158663e8e51Sths     val |= BIT(28);
1159663e8e51Sths     TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
1160663e8e51Sths                       val, raiseint, mdi_op_name[opcode], phy,
1161aac443e6SStefan Weil                       reg2name(reg), data));
1162663e8e51Sths     return val;
1163663e8e51Sths }
1164663e8e51Sths 
11650113f48dSStefan Weil static void eepro100_write_mdi(EEPRO100State *s)
1166663e8e51Sths {
11670113f48dSStefan Weil     uint32_t val = e100_read_reg4(s, SCBCtrlMDI);
1168663e8e51Sths     uint8_t raiseint = (val & BIT(29)) >> 29;
1169663e8e51Sths     uint8_t opcode = (val & BITS(27, 26)) >> 26;
1170663e8e51Sths     uint8_t phy = (val & BITS(25, 21)) >> 21;
1171663e8e51Sths     uint8_t reg = (val & BITS(20, 16)) >> 16;
1172663e8e51Sths     uint16_t data = (val & BITS(15, 0));
1173aac443e6SStefan Weil     TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
1174aac443e6SStefan Weil           val, raiseint, mdi_op_name[opcode], phy, reg2name(reg), data));
1175663e8e51Sths     if (phy != 1) {
1176663e8e51Sths         /* Unsupported PHY address. */
1177e7493b25SStefan Weil #if 0
1178e7493b25SStefan Weil         logout("phy must be 1 but is %u\n", phy);
1179e7493b25SStefan Weil #endif
1180663e8e51Sths         data = 0;
1181663e8e51Sths     } else if (opcode != 1 && opcode != 2) {
1182663e8e51Sths         /* Unsupported opcode. */
1183663e8e51Sths         logout("opcode must be 1 or 2 but is %u\n", opcode);
1184663e8e51Sths         data = 0;
1185663e8e51Sths     } else if (reg > 6) {
1186663e8e51Sths         /* Unsupported register. */
1187663e8e51Sths         logout("register must be 0...6 but is %u\n", reg);
1188663e8e51Sths         data = 0;
1189663e8e51Sths     } else {
1190663e8e51Sths         TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
1191663e8e51Sths                           val, raiseint, mdi_op_name[opcode], phy,
1192aac443e6SStefan Weil                           reg2name(reg), data));
1193663e8e51Sths         if (opcode == 1) {
1194663e8e51Sths             /* MDI write */
1195663e8e51Sths             switch (reg) {
1196663e8e51Sths             case 0:            /* Control Register */
1197663e8e51Sths                 if (data & 0x8000) {
1198663e8e51Sths                     /* Reset status and control registers to default. */
1199663e8e51Sths                     s->mdimem[0] = eepro100_mdi_default[0];
1200663e8e51Sths                     s->mdimem[1] = eepro100_mdi_default[1];
1201663e8e51Sths                     data = s->mdimem[reg];
1202663e8e51Sths                 } else {
1203663e8e51Sths                     /* Restart Auto Configuration = Normal Operation */
1204663e8e51Sths                     data &= ~0x0200;
1205663e8e51Sths                 }
1206663e8e51Sths                 break;
1207663e8e51Sths             case 1:            /* Status Register */
1208663e8e51Sths                 missing("not writable");
1209663e8e51Sths                 break;
1210663e8e51Sths             case 2:            /* PHY Identification Register (Word 1) */
1211663e8e51Sths             case 3:            /* PHY Identification Register (Word 2) */
1212663e8e51Sths                 missing("not implemented");
1213663e8e51Sths                 break;
1214663e8e51Sths             case 4:            /* Auto-Negotiation Advertisement Register */
1215663e8e51Sths             case 5:            /* Auto-Negotiation Link Partner Ability Register */
1216663e8e51Sths                 break;
1217663e8e51Sths             case 6:            /* Auto-Negotiation Expansion Register */
1218663e8e51Sths             default:
1219663e8e51Sths                 missing("not implemented");
1220663e8e51Sths             }
12215e80dd22SPeter Maydell             s->mdimem[reg] &= eepro100_mdi_mask[reg];
12225e80dd22SPeter Maydell             s->mdimem[reg] |= data & ~eepro100_mdi_mask[reg];
1223663e8e51Sths         } else if (opcode == 2) {
1224663e8e51Sths             /* MDI read */
1225663e8e51Sths             switch (reg) {
1226663e8e51Sths             case 0:            /* Control Register */
1227663e8e51Sths                 if (data & 0x8000) {
1228663e8e51Sths                     /* Reset status and control registers to default. */
1229663e8e51Sths                     s->mdimem[0] = eepro100_mdi_default[0];
1230663e8e51Sths                     s->mdimem[1] = eepro100_mdi_default[1];
1231663e8e51Sths                 }
1232663e8e51Sths                 break;
1233663e8e51Sths             case 1:            /* Status Register */
1234663e8e51Sths                 s->mdimem[reg] |= 0x0020;
1235663e8e51Sths                 break;
1236663e8e51Sths             case 2:            /* PHY Identification Register (Word 1) */
1237663e8e51Sths             case 3:            /* PHY Identification Register (Word 2) */
1238663e8e51Sths             case 4:            /* Auto-Negotiation Advertisement Register */
1239663e8e51Sths                 break;
1240663e8e51Sths             case 5:            /* Auto-Negotiation Link Partner Ability Register */
1241663e8e51Sths                 s->mdimem[reg] = 0x41fe;
1242663e8e51Sths                 break;
1243663e8e51Sths             case 6:            /* Auto-Negotiation Expansion Register */
1244663e8e51Sths                 s->mdimem[reg] = 0x0001;
1245663e8e51Sths                 break;
1246663e8e51Sths             }
1247663e8e51Sths             data = s->mdimem[reg];
1248663e8e51Sths         }
1249663e8e51Sths         /* Emulation takes no time to finish MDI transaction.
1250663e8e51Sths          * Set MDI bit in SCB status register. */
1251663e8e51Sths         s->mem[SCBAck] |= 0x08;
1252663e8e51Sths         val |= BIT(28);
1253663e8e51Sths         if (raiseint) {
1254663e8e51Sths             eepro100_mdi_interrupt(s);
1255663e8e51Sths         }
1256663e8e51Sths     }
1257663e8e51Sths     val = (val & 0xffff0000) + data;
1258e5e23ab8SStefan Weil     e100_write_reg4(s, SCBCtrlMDI, val);
1259663e8e51Sths }
1260663e8e51Sths 
1261663e8e51Sths /*****************************************************************************
1262663e8e51Sths  *
1263663e8e51Sths  * Port emulation.
1264663e8e51Sths  *
1265663e8e51Sths  ****************************************************************************/
1266663e8e51Sths 
1267663e8e51Sths #define PORT_SOFTWARE_RESET     0
1268663e8e51Sths #define PORT_SELFTEST           1
1269663e8e51Sths #define PORT_SELECTIVE_RESET    2
1270663e8e51Sths #define PORT_DUMP               3
1271663e8e51Sths #define PORT_SELECTION_MASK     3
1272663e8e51Sths 
1273663e8e51Sths typedef struct {
1274663e8e51Sths     uint32_t st_sign;           /* Self Test Signature */
1275663e8e51Sths     uint32_t st_result;         /* Self Test Results */
1276c227f099SAnthony Liguori } eepro100_selftest_t;
1277663e8e51Sths 
1278663e8e51Sths static uint32_t eepro100_read_port(EEPRO100State * s)
1279663e8e51Sths {
1280663e8e51Sths     return 0;
1281663e8e51Sths }
1282663e8e51Sths 
12833fd3d0b4SStefan Weil static void eepro100_write_port(EEPRO100State *s)
1284663e8e51Sths {
12853fd3d0b4SStefan Weil     uint32_t val = e100_read_reg4(s, SCBPort);
1286663e8e51Sths     uint32_t address = (val & ~PORT_SELECTION_MASK);
1287663e8e51Sths     uint8_t selection = (val & PORT_SELECTION_MASK);
1288663e8e51Sths     switch (selection) {
1289663e8e51Sths     case PORT_SOFTWARE_RESET:
1290663e8e51Sths         nic_reset(s);
1291663e8e51Sths         break;
1292663e8e51Sths     case PORT_SELFTEST:
1293aac443e6SStefan Weil         TRACE(OTHER, logout("selftest address=0x%08x\n", address));
1294c227f099SAnthony Liguori         eepro100_selftest_t data;
129516ef60c9SEduard - Gabriel Munteanu         pci_dma_read(&s->dev, address, (uint8_t *) &data, sizeof(data));
1296663e8e51Sths         data.st_sign = 0xffffffff;
1297663e8e51Sths         data.st_result = 0;
129816ef60c9SEduard - Gabriel Munteanu         pci_dma_write(&s->dev, address, (uint8_t *) &data, sizeof(data));
1299663e8e51Sths         break;
1300663e8e51Sths     case PORT_SELECTIVE_RESET:
1301aac443e6SStefan Weil         TRACE(OTHER, logout("selective reset, selftest address=0x%08x\n", address));
1302663e8e51Sths         nic_selective_reset(s);
1303663e8e51Sths         break;
1304663e8e51Sths     default:
1305663e8e51Sths         logout("val=0x%08x\n", val);
1306663e8e51Sths         missing("unknown port selection");
1307663e8e51Sths     }
1308663e8e51Sths }
1309663e8e51Sths 
1310663e8e51Sths /*****************************************************************************
1311663e8e51Sths  *
1312663e8e51Sths  * General hardware emulation.
1313663e8e51Sths  *
1314663e8e51Sths  ****************************************************************************/
1315663e8e51Sths 
1316663e8e51Sths static uint8_t eepro100_read1(EEPRO100State * s, uint32_t addr)
1317663e8e51Sths {
1318ef476062SBlue Swirl     uint8_t val = 0;
1319663e8e51Sths     if (addr <= sizeof(s->mem) - sizeof(val)) {
1320e5e23ab8SStefan Weil         val = s->mem[addr];
1321663e8e51Sths     }
1322663e8e51Sths 
1323663e8e51Sths     switch (addr) {
1324663e8e51Sths     case SCBStatus:
1325663e8e51Sths     case SCBAck:
1326aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1327663e8e51Sths         break;
1328663e8e51Sths     case SCBCmd:
1329aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1330e7493b25SStefan Weil #if 0
1331e7493b25SStefan Weil         val = eepro100_read_command(s);
1332e7493b25SStefan Weil #endif
1333663e8e51Sths         break;
1334663e8e51Sths     case SCBIntmask:
1335aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1336663e8e51Sths         break;
1337663e8e51Sths     case SCBPort + 3:
1338aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1339663e8e51Sths         break;
1340663e8e51Sths     case SCBeeprom:
1341663e8e51Sths         val = eepro100_read_eeprom(s);
1342663e8e51Sths         break;
13430113f48dSStefan Weil     case SCBCtrlMDI:
13440113f48dSStefan Weil     case SCBCtrlMDI + 1:
13450113f48dSStefan Weil     case SCBCtrlMDI + 2:
13460113f48dSStefan Weil     case SCBCtrlMDI + 3:
13470113f48dSStefan Weil         val = (uint8_t)(eepro100_read_mdi(s) >> (8 * (addr & 3)));
13480113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
13490113f48dSStefan Weil         break;
13500908bba1SStefan Weil     case SCBpmdr:       /* Power Management Driver Register */
1351663e8e51Sths         val = 0;
1352aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1353663e8e51Sths         break;
1354a39bd017SStefan Weil     case SCBgctrl:      /* General Control Register */
1355a39bd017SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1356a39bd017SStefan Weil         break;
13570908bba1SStefan Weil     case SCBgstat:      /* General Status Register */
1358663e8e51Sths         /* 100 Mbps full duplex, valid link */
1359663e8e51Sths         val = 0x07;
1360aac443e6SStefan Weil         TRACE(OTHER, logout("addr=General Status val=%02x\n", val));
1361663e8e51Sths         break;
1362663e8e51Sths     default:
1363663e8e51Sths         logout("addr=%s val=0x%02x\n", regname(addr), val);
1364663e8e51Sths         missing("unknown byte read");
1365663e8e51Sths     }
1366663e8e51Sths     return val;
1367663e8e51Sths }
1368663e8e51Sths 
1369663e8e51Sths static uint16_t eepro100_read2(EEPRO100State * s, uint32_t addr)
1370663e8e51Sths {
1371ef476062SBlue Swirl     uint16_t val = 0;
1372663e8e51Sths     if (addr <= sizeof(s->mem) - sizeof(val)) {
1373e5e23ab8SStefan Weil         val = e100_read_reg2(s, addr);
1374663e8e51Sths     }
1375663e8e51Sths 
1376663e8e51Sths     switch (addr) {
1377663e8e51Sths     case SCBStatus:
1378dbbaaff6S=?UTF-8?q?Reimar=20D=C3=B6ffinger?=     case SCBCmd:
1379aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
1380663e8e51Sths         break;
1381663e8e51Sths     case SCBeeprom:
1382663e8e51Sths         val = eepro100_read_eeprom(s);
1383aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
1384663e8e51Sths         break;
13850113f48dSStefan Weil     case SCBCtrlMDI:
13860113f48dSStefan Weil     case SCBCtrlMDI + 2:
13870113f48dSStefan Weil         val = (uint16_t)(eepro100_read_mdi(s) >> (8 * (addr & 3)));
13880113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
13890113f48dSStefan Weil         break;
1390663e8e51Sths     default:
1391663e8e51Sths         logout("addr=%s val=0x%04x\n", regname(addr), val);
1392663e8e51Sths         missing("unknown word read");
1393663e8e51Sths     }
1394663e8e51Sths     return val;
1395663e8e51Sths }
1396663e8e51Sths 
1397663e8e51Sths static uint32_t eepro100_read4(EEPRO100State * s, uint32_t addr)
1398663e8e51Sths {
1399ef476062SBlue Swirl     uint32_t val = 0;
1400663e8e51Sths     if (addr <= sizeof(s->mem) - sizeof(val)) {
1401e5e23ab8SStefan Weil         val = e100_read_reg4(s, addr);
1402663e8e51Sths     }
1403663e8e51Sths 
1404663e8e51Sths     switch (addr) {
1405663e8e51Sths     case SCBStatus:
1406aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1407663e8e51Sths         break;
1408663e8e51Sths     case SCBPointer:
1409aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1410663e8e51Sths         break;
1411663e8e51Sths     case SCBPort:
1412663e8e51Sths         val = eepro100_read_port(s);
1413aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1414663e8e51Sths         break;
1415072476eaSStefan Weil     case SCBflash:
1416072476eaSStefan Weil         val = eepro100_read_eeprom(s);
1417072476eaSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1418072476eaSStefan Weil         break;
1419663e8e51Sths     case SCBCtrlMDI:
1420663e8e51Sths         val = eepro100_read_mdi(s);
1421663e8e51Sths         break;
1422663e8e51Sths     default:
1423663e8e51Sths         logout("addr=%s val=0x%08x\n", regname(addr), val);
1424663e8e51Sths         missing("unknown longword read");
1425663e8e51Sths     }
1426663e8e51Sths     return val;
1427663e8e51Sths }
1428663e8e51Sths 
1429663e8e51Sths static void eepro100_write1(EEPRO100State * s, uint32_t addr, uint8_t val)
1430663e8e51Sths {
1431e74818f3SStefan Weil     /* SCBStatus is readonly. */
1432e74818f3SStefan Weil     if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) {
1433e5e23ab8SStefan Weil         s->mem[addr] = val;
1434663e8e51Sths     }
1435663e8e51Sths 
1436663e8e51Sths     switch (addr) {
1437663e8e51Sths     case SCBStatus:
14381b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1439663e8e51Sths         break;
1440663e8e51Sths     case SCBAck:
14411b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1442663e8e51Sths         eepro100_acknowledge(s);
1443663e8e51Sths         break;
1444663e8e51Sths     case SCBCmd:
14451b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1446663e8e51Sths         eepro100_write_command(s, val);
1447663e8e51Sths         break;
1448663e8e51Sths     case SCBIntmask:
14491b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1450663e8e51Sths         if (val & BIT(1)) {
1451663e8e51Sths             eepro100_swi_interrupt(s);
1452663e8e51Sths         }
1453663e8e51Sths         eepro100_interrupt(s, 0);
1454663e8e51Sths         break;
145527a05006SStefan Weil     case SCBPointer:
145627a05006SStefan Weil     case SCBPointer + 1:
145727a05006SStefan Weil     case SCBPointer + 2:
145827a05006SStefan Weil     case SCBPointer + 3:
145927a05006SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
146027a05006SStefan Weil         break;
14613fd3d0b4SStefan Weil     case SCBPort:
14623fd3d0b4SStefan Weil     case SCBPort + 1:
14633fd3d0b4SStefan Weil     case SCBPort + 2:
14643fd3d0b4SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
14653fd3d0b4SStefan Weil         break;
1466663e8e51Sths     case SCBPort + 3:
14673fd3d0b4SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
14683fd3d0b4SStefan Weil         eepro100_write_port(s);
14693fd3d0b4SStefan Weil         break;
1470aac443e6SStefan Weil     case SCBFlow:       /* does not exist on 82557 */
14713257d2b6Sths     case SCBFlow + 1:
14723257d2b6Sths     case SCBFlow + 2:
14730908bba1SStefan Weil     case SCBpmdr:       /* does not exist on 82557 */
1474aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1475663e8e51Sths         break;
1476663e8e51Sths     case SCBeeprom:
14771b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1478663e8e51Sths         eepro100_write_eeprom(s->eeprom, val);
1479663e8e51Sths         break;
14800113f48dSStefan Weil     case SCBCtrlMDI:
14810113f48dSStefan Weil     case SCBCtrlMDI + 1:
14820113f48dSStefan Weil     case SCBCtrlMDI + 2:
14830113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
14840113f48dSStefan Weil         break;
14850113f48dSStefan Weil     case SCBCtrlMDI + 3:
14860113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
14870113f48dSStefan Weil         eepro100_write_mdi(s);
14880113f48dSStefan Weil         break;
1489663e8e51Sths     default:
1490663e8e51Sths         logout("addr=%s val=0x%02x\n", regname(addr), val);
1491663e8e51Sths         missing("unknown byte write");
1492663e8e51Sths     }
1493663e8e51Sths }
1494663e8e51Sths 
1495663e8e51Sths static void eepro100_write2(EEPRO100State * s, uint32_t addr, uint16_t val)
1496663e8e51Sths {
1497e74818f3SStefan Weil     /* SCBStatus is readonly. */
1498e74818f3SStefan Weil     if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) {
1499e5e23ab8SStefan Weil         e100_write_reg2(s, addr, val);
1500663e8e51Sths     }
1501663e8e51Sths 
1502663e8e51Sths     switch (addr) {
1503663e8e51Sths     case SCBStatus:
15041b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
1505e74818f3SStefan Weil         s->mem[SCBAck] = (val >> 8);
1506663e8e51Sths         eepro100_acknowledge(s);
1507663e8e51Sths         break;
1508663e8e51Sths     case SCBCmd:
15091b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
1510663e8e51Sths         eepro100_write_command(s, val);
1511663e8e51Sths         eepro100_write1(s, SCBIntmask, val >> 8);
1512663e8e51Sths         break;
151327a05006SStefan Weil     case SCBPointer:
151427a05006SStefan Weil     case SCBPointer + 2:
151527a05006SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
151627a05006SStefan Weil         break;
15173fd3d0b4SStefan Weil     case SCBPort:
15183fd3d0b4SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
15193fd3d0b4SStefan Weil         break;
15203fd3d0b4SStefan Weil     case SCBPort + 2:
15213fd3d0b4SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
15223fd3d0b4SStefan Weil         eepro100_write_port(s);
15233fd3d0b4SStefan Weil         break;
1524663e8e51Sths     case SCBeeprom:
15251b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
1526663e8e51Sths         eepro100_write_eeprom(s->eeprom, val);
1527663e8e51Sths         break;
15280113f48dSStefan Weil     case SCBCtrlMDI:
15290113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
15300113f48dSStefan Weil         break;
15310113f48dSStefan Weil     case SCBCtrlMDI + 2:
15320113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
15330113f48dSStefan Weil         eepro100_write_mdi(s);
15340113f48dSStefan Weil         break;
1535663e8e51Sths     default:
1536663e8e51Sths         logout("addr=%s val=0x%04x\n", regname(addr), val);
1537663e8e51Sths         missing("unknown word write");
1538663e8e51Sths     }
1539663e8e51Sths }
1540663e8e51Sths 
1541663e8e51Sths static void eepro100_write4(EEPRO100State * s, uint32_t addr, uint32_t val)
1542663e8e51Sths {
1543663e8e51Sths     if (addr <= sizeof(s->mem) - sizeof(val)) {
1544e5e23ab8SStefan Weil         e100_write_reg4(s, addr, val);
1545663e8e51Sths     }
1546663e8e51Sths 
1547663e8e51Sths     switch (addr) {
1548663e8e51Sths     case SCBPointer:
154927a05006SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1550663e8e51Sths         break;
1551663e8e51Sths     case SCBPort:
1552aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
15533fd3d0b4SStefan Weil         eepro100_write_port(s);
1554663e8e51Sths         break;
1555072476eaSStefan Weil     case SCBflash:
1556072476eaSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1557072476eaSStefan Weil         val = val >> 16;
1558072476eaSStefan Weil         eepro100_write_eeprom(s->eeprom, val);
1559072476eaSStefan Weil         break;
1560663e8e51Sths     case SCBCtrlMDI:
15610113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
15620113f48dSStefan Weil         eepro100_write_mdi(s);
1563663e8e51Sths         break;
1564663e8e51Sths     default:
1565663e8e51Sths         logout("addr=%s val=0x%08x\n", regname(addr), val);
1566663e8e51Sths         missing("unknown longword write");
1567663e8e51Sths     }
1568663e8e51Sths }
1569663e8e51Sths 
1570a8170e5eSAvi Kivity static uint64_t eepro100_read(void *opaque, hwaddr addr,
15715e6ffddeSAvi Kivity                               unsigned size)
1572663e8e51Sths {
1573663e8e51Sths     EEPRO100State *s = opaque;
15745e6ffddeSAvi Kivity 
15755e6ffddeSAvi Kivity     switch (size) {
15765e6ffddeSAvi Kivity     case 1: return eepro100_read1(s, addr);
15775e6ffddeSAvi Kivity     case 2: return eepro100_read2(s, addr);
15785e6ffddeSAvi Kivity     case 4: return eepro100_read4(s, addr);
15795e6ffddeSAvi Kivity     default: abort();
15805e6ffddeSAvi Kivity     }
1581663e8e51Sths }
1582663e8e51Sths 
1583a8170e5eSAvi Kivity static void eepro100_write(void *opaque, hwaddr addr,
15845e6ffddeSAvi Kivity                            uint64_t data, unsigned size)
1585663e8e51Sths {
1586663e8e51Sths     EEPRO100State *s = opaque;
15875e6ffddeSAvi Kivity 
15885e6ffddeSAvi Kivity     switch (size) {
15890ed8b6f6SBlue Swirl     case 1:
15900ed8b6f6SBlue Swirl         eepro100_write1(s, addr, data);
15910ed8b6f6SBlue Swirl         break;
15920ed8b6f6SBlue Swirl     case 2:
15930ed8b6f6SBlue Swirl         eepro100_write2(s, addr, data);
15940ed8b6f6SBlue Swirl         break;
15950ed8b6f6SBlue Swirl     case 4:
15960ed8b6f6SBlue Swirl         eepro100_write4(s, addr, data);
15970ed8b6f6SBlue Swirl         break;
15980ed8b6f6SBlue Swirl     default:
15990ed8b6f6SBlue Swirl         abort();
16005e6ffddeSAvi Kivity     }
1601663e8e51Sths }
1602663e8e51Sths 
16035e6ffddeSAvi Kivity static const MemoryRegionOps eepro100_ops = {
16045e6ffddeSAvi Kivity     .read = eepro100_read,
16055e6ffddeSAvi Kivity     .write = eepro100_write,
16065e6ffddeSAvi Kivity     .endianness = DEVICE_LITTLE_ENDIAN,
1607663e8e51Sths };
1608663e8e51Sths 
16094e68f7a0SStefan Hajnoczi static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
1610663e8e51Sths {
1611663e8e51Sths     /* TODO:
1612663e8e51Sths      * - Magic packets should set bit 30 in power management driver register.
1613663e8e51Sths      * - Interesting packets should set bit 29 in power management driver register.
1614663e8e51Sths      */
1615cc1f0f45SJason Wang     EEPRO100State *s = qemu_get_nic_opaque(nc);
1616663e8e51Sths     uint16_t rfd_status = 0xa000;
1617792f1d63SStefan Weil #if defined(CONFIG_PAD_RECEIVED_FRAMES)
1618792f1d63SStefan Weil     uint8_t min_buf[60];
1619792f1d63SStefan Weil #endif
1620663e8e51Sths     static const uint8_t broadcast_macaddr[6] =
1621663e8e51Sths         { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
1622663e8e51Sths 
1623792f1d63SStefan Weil #if defined(CONFIG_PAD_RECEIVED_FRAMES)
1624792f1d63SStefan Weil     /* Pad to minimum Ethernet frame length */
1625792f1d63SStefan Weil     if (size < sizeof(min_buf)) {
1626792f1d63SStefan Weil         memcpy(min_buf, buf, size);
1627792f1d63SStefan Weil         memset(&min_buf[size], 0, sizeof(min_buf) - size);
1628792f1d63SStefan Weil         buf = min_buf;
1629792f1d63SStefan Weil         size = sizeof(min_buf);
1630792f1d63SStefan Weil     }
1631792f1d63SStefan Weil #endif
1632792f1d63SStefan Weil 
1633663e8e51Sths     if (s->configuration[8] & 0x80) {
1634663e8e51Sths         /* CSMA is disabled. */
1635663e8e51Sths         logout("%p received while CSMA is disabled\n", s);
16364f1c942bSMark McLoughlin         return -1;
1637792f1d63SStefan Weil #if !defined(CONFIG_PAD_RECEIVED_FRAMES)
1638ced5296aSStefan Weil     } else if (size < 64 && (s->configuration[7] & BIT(0))) {
1639663e8e51Sths         /* Short frame and configuration byte 7/0 (discard short receive) set:
1640663e8e51Sths          * Short frame is discarded */
1641067d01deSStefan Weil         logout("%p received short frame (%zu byte)\n", s, size);
1642663e8e51Sths         s->statistics.rx_short_frame_errors++;
1643e7493b25SStefan Weil         return -1;
1644e7493b25SStefan Weil #endif
1645ced5296aSStefan Weil     } else if ((size > MAX_ETH_FRAME_SIZE + 4) && !(s->configuration[18] & BIT(3))) {
1646663e8e51Sths         /* Long frame and configuration byte 18/3 (long receive ok) not set:
1647663e8e51Sths          * Long frames are discarded. */
1648067d01deSStefan Weil         logout("%p received long frame (%zu byte), ignored\n", s, size);
16494f1c942bSMark McLoughlin         return -1;
1650e7493b25SStefan Weil     } else if (memcmp(buf, s->conf.macaddr.a, 6) == 0) {       /* !!! */
1651663e8e51Sths         /* Frame matches individual address. */
1652663e8e51Sths         /* TODO: check configuration byte 15/4 (ignore U/L). */
1653067d01deSStefan Weil         TRACE(RXTX, logout("%p received frame for me, len=%zu\n", s, size));
1654663e8e51Sths     } else if (memcmp(buf, broadcast_macaddr, 6) == 0) {
1655663e8e51Sths         /* Broadcast frame. */
1656067d01deSStefan Weil         TRACE(RXTX, logout("%p received broadcast, len=%zu\n", s, size));
1657663e8e51Sths         rfd_status |= 0x0002;
16587b8737deSStefan Weil     } else if (buf[0] & 0x01) {
1659663e8e51Sths         /* Multicast frame. */
16607b8737deSStefan Weil         TRACE(RXTX, logout("%p received multicast, len=%zu,%s\n", s, size, nic_dump(buf, size)));
16617f1e9d4eSKevin Wolf         if (s->configuration[21] & BIT(3)) {
16627b8737deSStefan Weil           /* Multicast all bit is set, receive all multicast frames. */
16637b8737deSStefan Weil         } else {
16647c0348bdSMark Cave-Ayland           unsigned mcast_idx = (net_crc32(buf, ETH_ALEN) & BITS(7, 2)) >> 2;
16657b8737deSStefan Weil           assert(mcast_idx < 64);
16667b8737deSStefan Weil           if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) {
16677b8737deSStefan Weil             /* Multicast frame is allowed in hash table. */
1668ced5296aSStefan Weil           } else if (s->configuration[15] & BIT(0)) {
16697b8737deSStefan Weil               /* Promiscuous: receive all. */
16707b8737deSStefan Weil               rfd_status |= 0x0004;
16717b8737deSStefan Weil           } else {
16727b8737deSStefan Weil               TRACE(RXTX, logout("%p multicast ignored\n", s));
16737b8737deSStefan Weil               return -1;
16747f1e9d4eSKevin Wolf           }
1675663e8e51Sths         }
16767b8737deSStefan Weil         /* TODO: Next not for promiscuous mode? */
1677663e8e51Sths         rfd_status |= 0x0002;
1678ced5296aSStefan Weil     } else if (s->configuration[15] & BIT(0)) {
1679663e8e51Sths         /* Promiscuous: receive all. */
1680067d01deSStefan Weil         TRACE(RXTX, logout("%p received frame in promiscuous mode, len=%zu\n", s, size));
1681663e8e51Sths         rfd_status |= 0x0004;
1682010ec629SStefan Weil     } else if (s->configuration[20] & BIT(6)) {
1683010ec629SStefan Weil         /* Multiple IA bit set. */
1684d00d6d00SMark Cave-Ayland         unsigned mcast_idx = net_crc32(buf, ETH_ALEN) >> 26;
1685010ec629SStefan Weil         assert(mcast_idx < 64);
1686010ec629SStefan Weil         if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) {
1687010ec629SStefan Weil             TRACE(RXTX, logout("%p accepted, multiple IA bit set\n", s));
1688010ec629SStefan Weil         } else {
1689010ec629SStefan Weil             TRACE(RXTX, logout("%p frame ignored, multiple IA bit set\n", s));
1690010ec629SStefan Weil             return -1;
1691010ec629SStefan Weil         }
1692663e8e51Sths     } else {
1693067d01deSStefan Weil         TRACE(RXTX, logout("%p received frame, ignored, len=%zu,%s\n", s, size,
1694aac443e6SStefan Weil               nic_dump(buf, size)));
16954f1c942bSMark McLoughlin         return size;
1696663e8e51Sths     }
1697663e8e51Sths 
1698663e8e51Sths     if (get_ru_state(s) != ru_ready) {
1699aac443e6SStefan Weil         /* No resources available. */
1700aac443e6SStefan Weil         logout("no resources, state=%u\n", get_ru_state(s));
1701e824012bSStefan Weil         /* TODO: RNR interrupt only at first failed frame? */
1702e824012bSStefan Weil         eepro100_rnr_interrupt(s);
1703663e8e51Sths         s->statistics.rx_resource_errors++;
1704e7493b25SStefan Weil #if 0
1705e7493b25SStefan Weil         assert(!"no resources");
1706e7493b25SStefan Weil #endif
17074f1c942bSMark McLoughlin         return -1;
1708663e8e51Sths     }
1709e7493b25SStefan Weil     /* !!! */
1710c227f099SAnthony Liguori     eepro100_rx_t rx;
171116ef60c9SEduard - Gabriel Munteanu     pci_dma_read(&s->dev, s->ru_base + s->ru_offset,
1712e965d4bcSDavid Gibson                  &rx, sizeof(eepro100_rx_t));
1713663e8e51Sths     uint16_t rfd_command = le16_to_cpu(rx.command);
1714663e8e51Sths     uint16_t rfd_size = le16_to_cpu(rx.size);
17157f1e9d4eSKevin Wolf 
17167f1e9d4eSKevin Wolf     if (size > rfd_size) {
17177f1e9d4eSKevin Wolf         logout("Receive buffer (%" PRId16 " bytes) too small for data "
17187f1e9d4eSKevin Wolf             "(%zu bytes); data truncated\n", rfd_size, size);
17197f1e9d4eSKevin Wolf         size = rfd_size;
17207f1e9d4eSKevin Wolf     }
1721792f1d63SStefan Weil #if !defined(CONFIG_PAD_RECEIVED_FRAMES)
1722663e8e51Sths     if (size < 64) {
1723663e8e51Sths         rfd_status |= 0x0080;
1724663e8e51Sths     }
1725792f1d63SStefan Weil #endif
1726aac443e6SStefan Weil     TRACE(OTHER, logout("command 0x%04x, link 0x%08x, addr 0x%08x, size %u\n",
1727aac443e6SStefan Weil           rfd_command, rx.link, rx.rx_buf_addr, rfd_size));
172816ef60c9SEduard - Gabriel Munteanu     stw_le_pci_dma(&s->dev, s->ru_base + s->ru_offset +
1729e5e23ab8SStefan Weil                 offsetof(eepro100_rx_t, status), rfd_status);
173016ef60c9SEduard - Gabriel Munteanu     stw_le_pci_dma(&s->dev, s->ru_base + s->ru_offset +
1731e5e23ab8SStefan Weil                 offsetof(eepro100_rx_t, count), size);
1732663e8e51Sths     /* Early receive interrupt not supported. */
1733e7493b25SStefan Weil #if 0
1734e7493b25SStefan Weil     eepro100_er_interrupt(s);
1735e7493b25SStefan Weil #endif
1736663e8e51Sths     /* Receive CRC Transfer not supported. */
1737ced5296aSStefan Weil     if (s->configuration[18] & BIT(2)) {
17387f1e9d4eSKevin Wolf         missing("Receive CRC Transfer");
17397f1e9d4eSKevin Wolf         return -1;
17407f1e9d4eSKevin Wolf     }
1741663e8e51Sths     /* TODO: check stripping enable bit. */
1742e7493b25SStefan Weil #if 0
1743e7493b25SStefan Weil     assert(!(s->configuration[17] & BIT(0)));
1744e7493b25SStefan Weil #endif
174516ef60c9SEduard - Gabriel Munteanu     pci_dma_write(&s->dev, s->ru_base + s->ru_offset +
174627112f18SStefan Weil                   sizeof(eepro100_rx_t), buf, size);
1747663e8e51Sths     s->statistics.rx_good_frames++;
1748663e8e51Sths     eepro100_fr_interrupt(s);
1749663e8e51Sths     s->ru_offset = le32_to_cpu(rx.link);
1750ced5296aSStefan Weil     if (rfd_command & COMMAND_EL) {
1751663e8e51Sths         /* EL bit is set, so this was the last frame. */
17527f1e9d4eSKevin Wolf         logout("receive: Running out of frames\n");
17531069985fSBo Yang         set_ru_state(s, ru_no_resources);
17541069985fSBo Yang         eepro100_rnr_interrupt(s);
1755663e8e51Sths     }
1756ced5296aSStefan Weil     if (rfd_command & COMMAND_S) {
1757663e8e51Sths         /* S bit is set. */
1758663e8e51Sths         set_ru_state(s, ru_suspended);
1759663e8e51Sths     }
17604f1c942bSMark McLoughlin     return size;
1761663e8e51Sths }
1762663e8e51Sths 
1763151b2986SJuan Quintela static const VMStateDescription vmstate_eepro100 = {
1764151b2986SJuan Quintela     .version_id = 3,
1765151b2986SJuan Quintela     .minimum_version_id = 2,
1766151b2986SJuan Quintela     .fields = (VMStateField[]) {
1767151b2986SJuan Quintela         VMSTATE_PCI_DEVICE(dev, EEPRO100State),
1768151b2986SJuan Quintela         VMSTATE_UNUSED(32),
1769151b2986SJuan Quintela         VMSTATE_BUFFER(mult, EEPRO100State),
1770151b2986SJuan Quintela         VMSTATE_BUFFER(mem, EEPRO100State),
17713706c43fSStefan Weil         /* Save all members of struct between scb_stat and mem. */
1772151b2986SJuan Quintela         VMSTATE_UINT8(scb_stat, EEPRO100State),
1773151b2986SJuan Quintela         VMSTATE_UINT8(int_stat, EEPRO100State),
1774151b2986SJuan Quintela         VMSTATE_UNUSED(3*4),
1775151b2986SJuan Quintela         VMSTATE_MACADDR(conf.macaddr, EEPRO100State),
1776151b2986SJuan Quintela         VMSTATE_UNUSED(19*4),
1777151b2986SJuan Quintela         VMSTATE_UINT16_ARRAY(mdimem, EEPRO100State, 32),
1778aac443e6SStefan Weil         /* The eeprom should be saved and restored by its own routines. */
1779151b2986SJuan Quintela         VMSTATE_UINT32(device, EEPRO100State),
1780151b2986SJuan Quintela         /* TODO check device. */
1781151b2986SJuan Quintela         VMSTATE_UINT32(cu_base, EEPRO100State),
1782151b2986SJuan Quintela         VMSTATE_UINT32(cu_offset, EEPRO100State),
1783151b2986SJuan Quintela         VMSTATE_UINT32(ru_base, EEPRO100State),
1784151b2986SJuan Quintela         VMSTATE_UINT32(ru_offset, EEPRO100State),
1785151b2986SJuan Quintela         VMSTATE_UINT32(statsaddr, EEPRO100State),
1786ba42b646SStefan Weil         /* Save eepro100_stats_t statistics. */
1787151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_good_frames, EEPRO100State),
1788151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_max_collisions, EEPRO100State),
1789151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_late_collisions, EEPRO100State),
1790151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_underruns, EEPRO100State),
1791151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_lost_crs, EEPRO100State),
1792151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_deferred, EEPRO100State),
1793151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_single_collisions, EEPRO100State),
1794151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_multiple_collisions, EEPRO100State),
1795151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_total_collisions, EEPRO100State),
1796151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_good_frames, EEPRO100State),
1797151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_crc_errors, EEPRO100State),
1798151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_alignment_errors, EEPRO100State),
1799151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_resource_errors, EEPRO100State),
1800151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_overrun_errors, EEPRO100State),
1801151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_cdt_errors, EEPRO100State),
1802151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_short_frame_errors, EEPRO100State),
1803151b2986SJuan Quintela         VMSTATE_UINT32(statistics.fc_xmt_pause, EEPRO100State),
1804151b2986SJuan Quintela         VMSTATE_UINT32(statistics.fc_rcv_pause, EEPRO100State),
1805151b2986SJuan Quintela         VMSTATE_UINT32(statistics.fc_rcv_unsupported, EEPRO100State),
1806151b2986SJuan Quintela         VMSTATE_UINT16(statistics.xmt_tco_frames, EEPRO100State),
1807151b2986SJuan Quintela         VMSTATE_UINT16(statistics.rcv_tco_frames, EEPRO100State),
18082657c663Sbalrog         /* Configuration bytes. */
1809151b2986SJuan Quintela         VMSTATE_BUFFER(configuration, EEPRO100State),
1810151b2986SJuan Quintela         VMSTATE_END_OF_LIST()
1811663e8e51Sths     }
1812151b2986SJuan Quintela };
1813663e8e51Sths 
1814f90c2bcdSAlex Williamson static void pci_nic_uninit(PCIDevice *pci_dev)
1815b946a153Saliguori {
1816c4c270e2SStefan Weil     EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev);
1817b946a153Saliguori 
18183cad405bSMarc-André Lureau     vmstate_unregister(VMSTATE_IF(&pci_dev->qdev), s->vmstate, s);
18192634ab7fSLi Qiang     g_free(s->vmstate);
18205fce2b3eSAlex Williamson     eeprom93xx_free(&pci_dev->qdev, s->eeprom);
1821948ecf21SJason Wang     qemu_del_nic(s->nic);
1822b946a153Saliguori }
1823b946a153Saliguori 
1824e00e365eSMark McLoughlin static NetClientInfo net_eepro100_info = {
1825f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
1826e00e365eSMark McLoughlin     .size = sizeof(NICState),
1827e00e365eSMark McLoughlin     .receive = nic_receive,
1828e00e365eSMark McLoughlin };
1829e00e365eSMark McLoughlin 
18309af21dbeSMarkus Armbruster static void e100_nic_realize(PCIDevice *pci_dev, Error **errp)
1831663e8e51Sths {
1832273a2142SJuan Quintela     EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev);
183340021f08SAnthony Liguori     E100PCIDeviceInfo *info = eepro100_get_class(s);
18349a7c2a59SMao Zhongyi     Error *local_err = NULL;
1835663e8e51Sths 
1836aac443e6SStefan Weil     TRACE(OTHER, logout("\n"));
1837663e8e51Sths 
183840021f08SAnthony Liguori     s->device = info->device;
1839663e8e51Sths 
18409a7c2a59SMao Zhongyi     e100_pci_reset(s, &local_err);
18419a7c2a59SMao Zhongyi     if (local_err) {
18429a7c2a59SMao Zhongyi         error_propagate(errp, local_err);
18439a7c2a59SMao Zhongyi         return;
18449a7c2a59SMao Zhongyi     }
1845663e8e51Sths 
1846663e8e51Sths     /* Add 64 * 2 EEPROM. i82557 and i82558 support a 64 word EEPROM,
1847663e8e51Sths      * i82559 and later support 64 or 256 word EEPROM. */
18485fce2b3eSAlex Williamson     s->eeprom = eeprom93xx_new(&pci_dev->qdev, EEPROM_SIZE);
1849663e8e51Sths 
1850663e8e51Sths     /* Handler for memory-mapped I/O */
1851eedfac6fSPaolo Bonzini     memory_region_init_io(&s->mmio_bar, OBJECT(s), &eepro100_ops, s,
1852eedfac6fSPaolo Bonzini                           "eepro100-mmio", PCI_MEM_SIZE);
1853e824b2ccSAvi Kivity     pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->mmio_bar);
1854eedfac6fSPaolo Bonzini     memory_region_init_io(&s->io_bar, OBJECT(s), &eepro100_ops, s,
1855eedfac6fSPaolo Bonzini                           "eepro100-io", PCI_IO_SIZE);
1856e824b2ccSAvi Kivity     pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar);
18575e6ffddeSAvi Kivity     /* FIXME: flash aliases to mmio?! */
1858eedfac6fSPaolo Bonzini     memory_region_init_io(&s->flash_bar, OBJECT(s), &eepro100_ops, s,
1859eedfac6fSPaolo Bonzini                           "eepro100-flash", PCI_FLASH_SIZE);
1860e824b2ccSAvi Kivity     pci_register_bar(&s->dev, 2, 0, &s->flash_bar);
1861663e8e51Sths 
1862508ef936SGerd Hoffmann     qemu_macaddr_default_if_unset(&s->conf.macaddr);
1863ce0e58b3SStefan Weil     logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6));
1864663e8e51Sths 
1865663e8e51Sths     nic_reset(s);
1866663e8e51Sths 
1867e00e365eSMark McLoughlin     s->nic = qemu_new_nic(&net_eepro100_info, &s->conf,
1868f79f2bfcSAnthony Liguori                           object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s);
1869663e8e51Sths 
1870b356f76dSJason Wang     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
1871b356f76dSJason Wang     TRACE(OTHER, logout("%s\n", qemu_get_queue(s->nic)->info_str));
1872663e8e51Sths 
1873a08d4367SJan Kiszka     qemu_register_reset(nic_reset, s);
1874663e8e51Sths 
1875e4d67e4fSMarc-André Lureau     s->vmstate = g_memdup(&vmstate_eepro100, sizeof(vmstate_eepro100));
1876b356f76dSJason Wang     s->vmstate->name = qemu_get_queue(s->nic)->model;
18771df2c9a2SPeter Xu     vmstate_register(VMSTATE_IF(&pci_dev->qdev), VMSTATE_INSTANCE_ID_ANY,
18781df2c9a2SPeter Xu                      s->vmstate, s);
1879663e8e51Sths }
1880663e8e51Sths 
18817317bb17SGonglei static void eepro100_instance_init(Object *obj)
18827317bb17SGonglei {
18837317bb17SGonglei     EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, PCI_DEVICE(obj));
18847317bb17SGonglei     device_add_bootindex_property(obj, &s->conf.bootindex,
18857317bb17SGonglei                                   "bootindex", "/ethernet-phy@0",
18867317bb17SGonglei                                   DEVICE(s), NULL);
18877317bb17SGonglei }
18887317bb17SGonglei 
1889558c8634SStefan Weil static E100PCIDeviceInfo e100_devices[] = {
1890663e8e51Sths     {
189139bffca2SAnthony Liguori         .name = "i82550",
189239bffca2SAnthony Liguori         .desc = "Intel i82550 Ethernet",
1893558c8634SStefan Weil         .device = i82550,
1894558c8634SStefan Weil         /* TODO: check device id. */
189540021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82551IT,
1896558c8634SStefan Weil         /* Revision ID: 0x0c, 0x0d, 0x0e. */
189740021f08SAnthony Liguori         .revision = 0x0e,
1898558c8634SStefan Weil         /* TODO: check size of statistical counters. */
1899558c8634SStefan Weil         .stats_size = 80,
1900558c8634SStefan Weil         /* TODO: check extended tcb support. */
1901558c8634SStefan Weil         .has_extended_tcb_support = true,
1902558c8634SStefan Weil         .power_management = true,
1903558c8634SStefan Weil     },{
190439bffca2SAnthony Liguori         .name = "i82551",
190539bffca2SAnthony Liguori         .desc = "Intel i82551 Ethernet",
1906558c8634SStefan Weil         .device = i82551,
190740021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82551IT,
1908558c8634SStefan Weil         /* Revision ID: 0x0f, 0x10. */
190940021f08SAnthony Liguori         .revision = 0x0f,
1910558c8634SStefan Weil         /* TODO: check size of statistical counters. */
1911558c8634SStefan Weil         .stats_size = 80,
1912558c8634SStefan Weil         .has_extended_tcb_support = true,
1913558c8634SStefan Weil         .power_management = true,
1914558c8634SStefan Weil     },{
191539bffca2SAnthony Liguori         .name = "i82557a",
191639bffca2SAnthony Liguori         .desc = "Intel i82557A Ethernet",
1917558c8634SStefan Weil         .device = i82557A,
191840021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
191940021f08SAnthony Liguori         .revision = 0x01,
1920558c8634SStefan Weil         .power_management = false,
1921558c8634SStefan Weil     },{
192239bffca2SAnthony Liguori         .name = "i82557b",
192339bffca2SAnthony Liguori         .desc = "Intel i82557B Ethernet",
1924558c8634SStefan Weil         .device = i82557B,
192540021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
192640021f08SAnthony Liguori         .revision = 0x02,
1927558c8634SStefan Weil         .power_management = false,
1928558c8634SStefan Weil     },{
192939bffca2SAnthony Liguori         .name = "i82557c",
193039bffca2SAnthony Liguori         .desc = "Intel i82557C Ethernet",
1931558c8634SStefan Weil         .device = i82557C,
193240021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
193340021f08SAnthony Liguori         .revision = 0x03,
1934558c8634SStefan Weil         .power_management = false,
1935558c8634SStefan Weil     },{
193639bffca2SAnthony Liguori         .name = "i82558a",
193739bffca2SAnthony Liguori         .desc = "Intel i82558A Ethernet",
1938558c8634SStefan Weil         .device = i82558A,
193940021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
194040021f08SAnthony Liguori         .revision = 0x04,
1941558c8634SStefan Weil         .stats_size = 76,
1942558c8634SStefan Weil         .has_extended_tcb_support = true,
1943558c8634SStefan Weil         .power_management = true,
1944558c8634SStefan Weil     },{
194539bffca2SAnthony Liguori         .name = "i82558b",
194639bffca2SAnthony Liguori         .desc = "Intel i82558B Ethernet",
1947558c8634SStefan Weil         .device = i82558B,
194840021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
194940021f08SAnthony Liguori         .revision = 0x05,
1950558c8634SStefan Weil         .stats_size = 76,
1951558c8634SStefan Weil         .has_extended_tcb_support = true,
1952558c8634SStefan Weil         .power_management = true,
1953558c8634SStefan Weil     },{
195439bffca2SAnthony Liguori         .name = "i82559a",
195539bffca2SAnthony Liguori         .desc = "Intel i82559A Ethernet",
1956558c8634SStefan Weil         .device = i82559A,
195740021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
195840021f08SAnthony Liguori         .revision = 0x06,
1959558c8634SStefan Weil         .stats_size = 80,
1960558c8634SStefan Weil         .has_extended_tcb_support = true,
1961558c8634SStefan Weil         .power_management = true,
1962558c8634SStefan Weil     },{
196339bffca2SAnthony Liguori         .name = "i82559b",
196439bffca2SAnthony Liguori         .desc = "Intel i82559B Ethernet",
1965558c8634SStefan Weil         .device = i82559B,
196640021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
196740021f08SAnthony Liguori         .revision = 0x07,
1968558c8634SStefan Weil         .stats_size = 80,
1969558c8634SStefan Weil         .has_extended_tcb_support = true,
1970558c8634SStefan Weil         .power_management = true,
1971558c8634SStefan Weil     },{
197239bffca2SAnthony Liguori         .name = "i82559c",
197339bffca2SAnthony Liguori         .desc = "Intel i82559C Ethernet",
1974558c8634SStefan Weil         .device = i82559C,
197540021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
1976558c8634SStefan Weil #if 0
197740021f08SAnthony Liguori         .revision = 0x08,
1978558c8634SStefan Weil #endif
1979558c8634SStefan Weil         /* TODO: Windows wants revision id 0x0c. */
198040021f08SAnthony Liguori         .revision = 0x0c,
1981ad03502bSIsaku Yamahata #if EEPROM_SIZE > 0
198240021f08SAnthony Liguori         .subsystem_vendor_id = PCI_VENDOR_ID_INTEL,
198340021f08SAnthony Liguori         .subsystem_id = 0x0040,
1984ad03502bSIsaku Yamahata #endif
1985558c8634SStefan Weil         .stats_size = 80,
1986558c8634SStefan Weil         .has_extended_tcb_support = true,
1987558c8634SStefan Weil         .power_management = true,
1988558c8634SStefan Weil     },{
198939bffca2SAnthony Liguori         .name = "i82559er",
199039bffca2SAnthony Liguori         .desc = "Intel i82559ER Ethernet",
1991558c8634SStefan Weil         .device = i82559ER,
199240021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82551IT,
199340021f08SAnthony Liguori         .revision = 0x09,
1994558c8634SStefan Weil         .stats_size = 80,
1995558c8634SStefan Weil         .has_extended_tcb_support = true,
1996558c8634SStefan Weil         .power_management = true,
1997558c8634SStefan Weil     },{
199839bffca2SAnthony Liguori         .name = "i82562",
199939bffca2SAnthony Liguori         .desc = "Intel i82562 Ethernet",
2000558c8634SStefan Weil         .device = i82562,
2001558c8634SStefan Weil         /* TODO: check device id. */
200240021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82551IT,
2003558c8634SStefan Weil         /* TODO: wrong revision id. */
200440021f08SAnthony Liguori         .revision = 0x0e,
2005558c8634SStefan Weil         .stats_size = 80,
2006558c8634SStefan Weil         .has_extended_tcb_support = true,
2007558c8634SStefan Weil         .power_management = true,
2008db667a12SStefan Weil     },{
2009db667a12SStefan Weil         /* Toshiba Tecra 8200. */
201039bffca2SAnthony Liguori         .name = "i82801",
201139bffca2SAnthony Liguori         .desc = "Intel i82801 Ethernet",
2012db667a12SStefan Weil         .device = i82801,
201340021f08SAnthony Liguori         .device_id = 0x2449,
201440021f08SAnthony Liguori         .revision = 0x03,
2015db667a12SStefan Weil         .stats_size = 80,
2016db667a12SStefan Weil         .has_extended_tcb_support = true,
2017db667a12SStefan Weil         .power_management = true,
2018663e8e51Sths     }
2019558c8634SStefan Weil };
2020663e8e51Sths 
202140021f08SAnthony Liguori static E100PCIDeviceInfo *eepro100_get_class_by_name(const char *typename)
202240021f08SAnthony Liguori {
202340021f08SAnthony Liguori     E100PCIDeviceInfo *info = NULL;
202440021f08SAnthony Liguori     int i;
202540021f08SAnthony Liguori 
202640021f08SAnthony Liguori     /* This is admittedly awkward but also temporary.  QOM allows for
202740021f08SAnthony Liguori      * parameterized typing and for subclassing both of which would suitable
202840021f08SAnthony Liguori      * handle what's going on here.  But class_data is already being used as
202940021f08SAnthony Liguori      * a stop-gap hack to allow incremental qdev conversion so we cannot use it
203040021f08SAnthony Liguori      * right now.  Once we merge the final QOM series, we can come back here and
203140021f08SAnthony Liguori      * do this in a much more elegant fashion.
203240021f08SAnthony Liguori      */
203340021f08SAnthony Liguori     for (i = 0; i < ARRAY_SIZE(e100_devices); i++) {
203439bffca2SAnthony Liguori         if (strcmp(e100_devices[i].name, typename) == 0) {
203540021f08SAnthony Liguori             info = &e100_devices[i];
203640021f08SAnthony Liguori             break;
203740021f08SAnthony Liguori         }
203840021f08SAnthony Liguori     }
203940021f08SAnthony Liguori     assert(info != NULL);
204040021f08SAnthony Liguori 
204140021f08SAnthony Liguori     return info;
204240021f08SAnthony Liguori }
204340021f08SAnthony Liguori 
204440021f08SAnthony Liguori static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s)
204540021f08SAnthony Liguori {
204640021f08SAnthony Liguori     return eepro100_get_class_by_name(object_get_typename(OBJECT(s)));
204740021f08SAnthony Liguori }
204840021f08SAnthony Liguori 
204939bffca2SAnthony Liguori static Property e100_properties[] = {
205039bffca2SAnthony Liguori     DEFINE_NIC_PROPERTIES(EEPRO100State, conf),
205139bffca2SAnthony Liguori     DEFINE_PROP_END_OF_LIST(),
205239bffca2SAnthony Liguori };
205339bffca2SAnthony Liguori 
205440021f08SAnthony Liguori static void eepro100_class_init(ObjectClass *klass, void *data)
205540021f08SAnthony Liguori {
205639bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
205740021f08SAnthony Liguori     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
205840021f08SAnthony Liguori     E100PCIDeviceInfo *info;
205940021f08SAnthony Liguori 
206040021f08SAnthony Liguori     info = eepro100_get_class_by_name(object_class_get_name(klass));
206140021f08SAnthony Liguori 
2062125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
2063*4f67d30bSMarc-André Lureau     device_class_set_props(dc, e100_properties);
206439bffca2SAnthony Liguori     dc->desc = info->desc;
206540021f08SAnthony Liguori     k->vendor_id = PCI_VENDOR_ID_INTEL;
206640021f08SAnthony Liguori     k->class_id = PCI_CLASS_NETWORK_ETHERNET;
206740021f08SAnthony Liguori     k->romfile = "pxe-eepro100.rom";
20689af21dbeSMarkus Armbruster     k->realize = e100_nic_realize;
206940021f08SAnthony Liguori     k->exit = pci_nic_uninit;
207040021f08SAnthony Liguori     k->device_id = info->device_id;
207140021f08SAnthony Liguori     k->revision = info->revision;
207240021f08SAnthony Liguori     k->subsystem_vendor_id = info->subsystem_vendor_id;
207340021f08SAnthony Liguori     k->subsystem_id = info->subsystem_id;
207440021f08SAnthony Liguori }
207540021f08SAnthony Liguori 
207683f7d43aSAndreas Färber static void eepro100_register_types(void)
20779d07d757SPaul Brook {
2078558c8634SStefan Weil     size_t i;
2079558c8634SStefan Weil     for (i = 0; i < ARRAY_SIZE(e100_devices); i++) {
208039bffca2SAnthony Liguori         TypeInfo type_info = {};
208139bffca2SAnthony Liguori         E100PCIDeviceInfo *info = &e100_devices[i];
208240021f08SAnthony Liguori 
208339bffca2SAnthony Liguori         type_info.name = info->name;
208439bffca2SAnthony Liguori         type_info.parent = TYPE_PCI_DEVICE;
208539bffca2SAnthony Liguori         type_info.class_init = eepro100_class_init;
208639bffca2SAnthony Liguori         type_info.instance_size = sizeof(EEPRO100State);
20877317bb17SGonglei         type_info.instance_init = eepro100_instance_init;
2088fd3b02c8SEduardo Habkost         type_info.interfaces = (InterfaceInfo[]) {
2089fd3b02c8SEduardo Habkost             { INTERFACE_CONVENTIONAL_PCI_DEVICE },
2090fd3b02c8SEduardo Habkost             { },
2091fd3b02c8SEduardo Habkost         };
209240021f08SAnthony Liguori 
209339bffca2SAnthony Liguori         type_register(&type_info);
2094558c8634SStefan Weil     }
20959d07d757SPaul Brook }
20969d07d757SPaul Brook 
209783f7d43aSAndreas Färber type_init(eepro100_register_types)
2098