xref: /qemu/hw/net/eepro100.c (revision 0b8fa32f551e863bb548a11394239239270dd3dc)
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/hw.h"
4683c9f4caSPaolo Bonzini #include "hw/pci/pci.h"
471422e32dSPaolo Bonzini #include "net/net.h"
487c0348bdSMark Cave-Ayland #include "net/eth.h"
490d09e41aSPaolo Bonzini #include "hw/nvram/eeprom93xx.h"
509c17d615SPaolo Bonzini #include "sysemu/sysemu.h"
519c17d615SPaolo Bonzini #include "sysemu/dma.h"
52949fc823SMarcel Apfelbaum #include "qemu/bitops.h"
53*0b8fa32fSMarkus Armbruster #include "qemu/module.h"
549a7c2a59SMao Zhongyi #include "qapi/error.h"
55663e8e51Sths 
56792f1d63SStefan Weil /* QEMU sends frames smaller than 60 bytes to ethernet nics.
57792f1d63SStefan Weil  * Such frames are rejected by real nics and their emulations.
58792f1d63SStefan Weil  * To avoid this behaviour, other nic emulations pad received
59792f1d63SStefan Weil  * frames. The following definition enables this padding for
60792f1d63SStefan Weil  * eepro100, too. We keep the define around in case it might
61792f1d63SStefan Weil  * become useful the future if the core networking is ever
62792f1d63SStefan Weil  * changed to pad short packets itself. */
63792f1d63SStefan Weil #define CONFIG_PAD_RECEIVED_FRAMES
64792f1d63SStefan Weil 
65aac443e6SStefan Weil /* Debug EEPRO100 card. */
66ce0e58b3SStefan Weil #if 0
67ce0e58b3SStefan Weil # define DEBUG_EEPRO100
68ce0e58b3SStefan Weil #endif
69663e8e51Sths 
70663e8e51Sths #ifdef DEBUG_EEPRO100
71001faf32SBlue Swirl #define logout(fmt, ...) fprintf(stderr, "EE100\t%-24s" fmt, __func__, ## __VA_ARGS__)
72663e8e51Sths #else
73001faf32SBlue Swirl #define logout(fmt, ...) ((void)0)
74663e8e51Sths #endif
75663e8e51Sths 
76663e8e51Sths /* Set flags to 0 to disable debug output. */
77aac443e6SStefan Weil #define INT     1       /* interrupt related actions */
78aac443e6SStefan Weil #define MDI     1       /* mdi related actions */
79aac443e6SStefan Weil #define OTHER   1
80aac443e6SStefan Weil #define RXTX    1
81aac443e6SStefan Weil #define EEPROM  1       /* eeprom related actions */
82663e8e51Sths 
83663e8e51Sths #define TRACE(flag, command) ((flag) ? (command) : (void)0)
84663e8e51Sths 
857f1e9d4eSKevin Wolf #define missing(text) fprintf(stderr, "eepro100: feature is missing in this emulation: " text "\n")
86663e8e51Sths 
87663e8e51Sths #define MAX_ETH_FRAME_SIZE 1514
88663e8e51Sths 
89663e8e51Sths /* This driver supports several different devices which are declared here. */
90c4c270e2SStefan Weil #define i82550          0x82550
91663e8e51Sths #define i82551          0x82551
92c4c270e2SStefan Weil #define i82557A         0x82557a
93663e8e51Sths #define i82557B         0x82557b
94663e8e51Sths #define i82557C         0x82557c
95c4c270e2SStefan Weil #define i82558A         0x82558a
96663e8e51Sths #define i82558B         0x82558b
97c4c270e2SStefan Weil #define i82559A         0x82559a
98c4c270e2SStefan Weil #define i82559B         0x82559b
99663e8e51Sths #define i82559C         0x82559c
100663e8e51Sths #define i82559ER        0x82559e
101663e8e51Sths #define i82562          0x82562
102db667a12SStefan Weil #define i82801          0x82801
103663e8e51Sths 
104aac443e6SStefan Weil /* Use 64 word EEPROM. TODO: could be a runtime option. */
105663e8e51Sths #define EEPROM_SIZE     64
106663e8e51Sths 
107663e8e51Sths #define PCI_MEM_SIZE            (4 * KiB)
108663e8e51Sths #define PCI_IO_SIZE             64
109663e8e51Sths #define PCI_FLASH_SIZE          (128 * KiB)
110663e8e51Sths 
111663e8e51Sths #define BITS(n, m) (((0xffffffffU << (31 - n)) >> (31 - n + m)) << m)
112663e8e51Sths 
113663e8e51Sths /* The SCB accepts the following controls for the Tx and Rx units: */
114663e8e51Sths #define  CU_NOP         0x0000  /* No operation. */
115663e8e51Sths #define  CU_START       0x0010  /* CU start. */
116663e8e51Sths #define  CU_RESUME      0x0020  /* CU resume. */
117663e8e51Sths #define  CU_STATSADDR   0x0040  /* Load dump counters address. */
118663e8e51Sths #define  CU_SHOWSTATS   0x0050  /* Dump statistical counters. */
119663e8e51Sths #define  CU_CMD_BASE    0x0060  /* Load CU base address. */
120663e8e51Sths #define  CU_DUMPSTATS   0x0070  /* Dump and reset statistical counters. */
121663e8e51Sths #define  CU_SRESUME     0x00a0  /* CU static resume. */
122663e8e51Sths 
123663e8e51Sths #define  RU_NOP         0x0000
124663e8e51Sths #define  RX_START       0x0001
125663e8e51Sths #define  RX_RESUME      0x0002
126e824012bSStefan Weil #define  RU_ABORT       0x0004
127663e8e51Sths #define  RX_ADDR_LOAD   0x0006
128663e8e51Sths #define  RX_RESUMENR    0x0007
129663e8e51Sths #define INT_MASK        0x0100
130663e8e51Sths #define DRVR_INT        0x0200  /* Driver generated interrupt. */
131663e8e51Sths 
132558c8634SStefan Weil typedef struct {
13339bffca2SAnthony Liguori     const char *name;
13439bffca2SAnthony Liguori     const char *desc;
13540021f08SAnthony Liguori     uint16_t device_id;
13640021f08SAnthony Liguori     uint8_t revision;
13740021f08SAnthony Liguori     uint16_t subsystem_vendor_id;
13840021f08SAnthony Liguori     uint16_t subsystem_id;
13940021f08SAnthony Liguori 
140558c8634SStefan Weil     uint32_t device;
141558c8634SStefan Weil     uint8_t stats_size;
142558c8634SStefan Weil     bool has_extended_tcb_support;
143558c8634SStefan Weil     bool power_management;
144558c8634SStefan Weil } E100PCIDeviceInfo;
145558c8634SStefan Weil 
146663e8e51Sths /* Offsets to the various registers.
147663e8e51Sths    All accesses need not be longword aligned. */
148e5e23ab8SStefan Weil typedef enum {
1490908bba1SStefan Weil     SCBStatus = 0,              /* Status Word. */
150663e8e51Sths     SCBAck = 1,
151663e8e51Sths     SCBCmd = 2,                 /* Rx/Command Unit command and status. */
152663e8e51Sths     SCBIntmask = 3,
153663e8e51Sths     SCBPointer = 4,             /* General purpose pointer. */
154663e8e51Sths     SCBPort = 8,                /* Misc. commands and operands.  */
1550908bba1SStefan Weil     SCBflash = 12,              /* Flash memory control. */
1560908bba1SStefan Weil     SCBeeprom = 14,             /* EEPROM control. */
157663e8e51Sths     SCBCtrlMDI = 16,            /* MDI interface control. */
158663e8e51Sths     SCBEarlyRx = 20,            /* Early receive byte count. */
1590908bba1SStefan Weil     SCBFlow = 24,               /* Flow Control. */
1600908bba1SStefan Weil     SCBpmdr = 27,               /* Power Management Driver. */
1610908bba1SStefan Weil     SCBgctrl = 28,              /* General Control. */
1620908bba1SStefan Weil     SCBgstat = 29,              /* General Status. */
163e5e23ab8SStefan Weil } E100RegisterOffset;
164663e8e51Sths 
165663e8e51Sths /* A speedo3 transmit buffer descriptor with two buffers... */
166663e8e51Sths typedef struct {
167663e8e51Sths     uint16_t status;
168663e8e51Sths     uint16_t command;
169663e8e51Sths     uint32_t link;              /* void * */
1707b8737deSStefan Weil     uint32_t tbd_array_addr;    /* transmit buffer descriptor array address. */
171663e8e51Sths     uint16_t tcb_bytes;         /* transmit command block byte count (in lower 14 bits */
172663e8e51Sths     uint8_t tx_threshold;       /* transmit threshold */
173663e8e51Sths     uint8_t tbd_count;          /* TBD number */
174e7493b25SStefan Weil #if 0
175e7493b25SStefan Weil     /* This constitutes two "TBD" entries: hdr and data */
176e7493b25SStefan Weil     uint32_t tx_buf_addr0;  /* void *, header of frame to be transmitted.  */
177e7493b25SStefan Weil     int32_t  tx_buf_size0;  /* Length of Tx hdr. */
178e7493b25SStefan Weil     uint32_t tx_buf_addr1;  /* void *, data to be transmitted.  */
179e7493b25SStefan Weil     int32_t  tx_buf_size1;  /* Length of Tx data. */
180e7493b25SStefan Weil #endif
181c227f099SAnthony Liguori } eepro100_tx_t;
182663e8e51Sths 
183663e8e51Sths /* Receive frame descriptor. */
184663e8e51Sths typedef struct {
185663e8e51Sths     int16_t status;
186663e8e51Sths     uint16_t command;
187663e8e51Sths     uint32_t link;              /* struct RxFD * */
188663e8e51Sths     uint32_t rx_buf_addr;       /* void * */
189663e8e51Sths     uint16_t count;
190663e8e51Sths     uint16_t size;
19127112f18SStefan Weil     /* Ethernet frame data follows. */
192c227f099SAnthony Liguori } eepro100_rx_t;
193663e8e51Sths 
194ced5296aSStefan Weil typedef enum {
195ced5296aSStefan Weil     COMMAND_EL = BIT(15),
196ced5296aSStefan Weil     COMMAND_S = BIT(14),
197ced5296aSStefan Weil     COMMAND_I = BIT(13),
198ced5296aSStefan Weil     COMMAND_NC = BIT(4),
199ced5296aSStefan Weil     COMMAND_SF = BIT(3),
200ced5296aSStefan Weil     COMMAND_CMD = BITS(2, 0),
201ced5296aSStefan Weil } scb_command_bit;
202ced5296aSStefan Weil 
203ced5296aSStefan Weil typedef enum {
204ced5296aSStefan Weil     STATUS_C = BIT(15),
205ced5296aSStefan Weil     STATUS_OK = BIT(13),
206ced5296aSStefan Weil } scb_status_bit;
207ced5296aSStefan Weil 
208663e8e51Sths typedef struct {
209663e8e51Sths     uint32_t tx_good_frames, tx_max_collisions, tx_late_collisions,
210663e8e51Sths              tx_underruns, tx_lost_crs, tx_deferred, tx_single_collisions,
211663e8e51Sths              tx_multiple_collisions, tx_total_collisions;
212663e8e51Sths     uint32_t rx_good_frames, rx_crc_errors, rx_alignment_errors,
213663e8e51Sths              rx_resource_errors, rx_overrun_errors, rx_cdt_errors,
214663e8e51Sths              rx_short_frame_errors;
215663e8e51Sths     uint32_t fc_xmt_pause, fc_rcv_pause, fc_rcv_unsupported;
216663e8e51Sths     uint16_t xmt_tco_frames, rcv_tco_frames;
217ba42b646SStefan Weil     /* TODO: i82559 has six reserved statistics but a total of 24 dwords. */
218ba42b646SStefan Weil     uint32_t reserved[4];
219c227f099SAnthony Liguori } eepro100_stats_t;
220663e8e51Sths 
221663e8e51Sths typedef enum {
222663e8e51Sths     cu_idle = 0,
223663e8e51Sths     cu_suspended = 1,
224663e8e51Sths     cu_active = 2,
225663e8e51Sths     cu_lpq_active = 2,
226663e8e51Sths     cu_hqp_active = 3
227c227f099SAnthony Liguori } cu_state_t;
228663e8e51Sths 
229663e8e51Sths typedef enum {
230663e8e51Sths     ru_idle = 0,
231663e8e51Sths     ru_suspended = 1,
232663e8e51Sths     ru_no_resources = 2,
233663e8e51Sths     ru_ready = 4
234c227f099SAnthony Liguori } ru_state_t;
235663e8e51Sths 
236663e8e51Sths typedef struct {
237273a2142SJuan Quintela     PCIDevice dev;
238010ec629SStefan Weil     /* Hash register (multicast mask array, multiple individual addresses). */
239010ec629SStefan Weil     uint8_t mult[8];
2405e6ffddeSAvi Kivity     MemoryRegion mmio_bar;
2415e6ffddeSAvi Kivity     MemoryRegion io_bar;
2425e6ffddeSAvi Kivity     MemoryRegion flash_bar;
243e00e365eSMark McLoughlin     NICState *nic;
244508ef936SGerd Hoffmann     NICConf conf;
245663e8e51Sths     uint8_t scb_stat;           /* SCB stat/ack byte */
246663e8e51Sths     uint8_t int_stat;           /* PCI interrupt status */
2473706c43fSStefan Weil     /* region must not be saved by nic_save. */
248663e8e51Sths     uint16_t mdimem[32];
249c227f099SAnthony Liguori     eeprom_t *eeprom;
250663e8e51Sths     uint32_t device;            /* device variant */
251663e8e51Sths     /* (cu_base + cu_offset) address the next command block in the command block list. */
252663e8e51Sths     uint32_t cu_base;           /* CU base address */
253663e8e51Sths     uint32_t cu_offset;         /* CU address offset */
254663e8e51Sths     /* (ru_base + ru_offset) address the RFD in the Receive Frame Area. */
255663e8e51Sths     uint32_t ru_base;           /* RU base address */
256663e8e51Sths     uint32_t ru_offset;         /* RU address offset */
257c227f099SAnthony Liguori     uint32_t statsaddr;         /* pointer to eepro100_stats_t */
258ba42b646SStefan Weil 
259f3a52e50SStefan Weil     /* Temporary status information (no need to save these values),
260f3a52e50SStefan Weil      * used while processing CU commands. */
261f3a52e50SStefan Weil     eepro100_tx_t tx;           /* transmit buffer descriptor */
262f3a52e50SStefan Weil     uint32_t cb_address;        /* = cu_base + cu_offset */
263f3a52e50SStefan Weil 
264ba42b646SStefan Weil     /* Statistical counters. Also used for wake-up packet (i82559). */
265ba42b646SStefan Weil     eepro100_stats_t statistics;
266ba42b646SStefan Weil 
267e5e23ab8SStefan Weil     /* Data in mem is always in the byte order of the controller (le).
268e5e23ab8SStefan Weil      * It must be dword aligned to allow direct access to 32 bit values. */
2693a93113aSDong Xu Wang     uint8_t mem[PCI_MEM_SIZE] __attribute__((aligned(8)));
270e5e23ab8SStefan Weil 
271663e8e51Sths     /* Configuration bytes. */
272663e8e51Sths     uint8_t configuration[22];
273663e8e51Sths 
274151b2986SJuan Quintela     /* vmstate for each particular nic */
275151b2986SJuan Quintela     VMStateDescription *vmstate;
276ba42b646SStefan Weil 
277ba42b646SStefan Weil     /* Quasi static device properties (no need to save them). */
278ba42b646SStefan Weil     uint16_t stats_size;
279ba42b646SStefan Weil     bool has_extended_tcb_support;
280663e8e51Sths } EEPRO100State;
281663e8e51Sths 
2826cded3a4SStefan Weil /* Word indices in EEPROM. */
2836cded3a4SStefan Weil typedef enum {
2846cded3a4SStefan Weil     EEPROM_CNFG_MDIX  = 0x03,
2856cded3a4SStefan Weil     EEPROM_ID         = 0x05,
2866cded3a4SStefan Weil     EEPROM_PHY_ID     = 0x06,
2876cded3a4SStefan Weil     EEPROM_VENDOR_ID  = 0x0c,
2886cded3a4SStefan Weil     EEPROM_CONFIG_ASF = 0x0d,
2896cded3a4SStefan Weil     EEPROM_DEVICE_ID  = 0x23,
2906cded3a4SStefan Weil     EEPROM_SMBUS_ADDR = 0x90,
2916cded3a4SStefan Weil } EEPROMOffset;
2926cded3a4SStefan Weil 
293b1e87018SStefan Weil /* Bit values for EEPROM ID word. */
294b1e87018SStefan Weil typedef enum {
295b1e87018SStefan Weil     EEPROM_ID_MDM = BIT(0),     /* Modem */
296b1e87018SStefan Weil     EEPROM_ID_STB = BIT(1),     /* Standby Enable */
297b1e87018SStefan Weil     EEPROM_ID_WMR = BIT(2),     /* ??? */
298b1e87018SStefan Weil     EEPROM_ID_WOL = BIT(5),     /* Wake on LAN */
299b1e87018SStefan Weil     EEPROM_ID_DPD = BIT(6),     /* Deep Power Down */
300b1e87018SStefan Weil     EEPROM_ID_ALT = BIT(7),     /* */
301b1e87018SStefan Weil     /* BITS(10, 8) device revision */
302b1e87018SStefan Weil     EEPROM_ID_BD = BIT(11),     /* boot disable */
303b1e87018SStefan Weil     EEPROM_ID_ID = BIT(13),     /* id bit */
304b1e87018SStefan Weil     /* BITS(15, 14) signature */
305b1e87018SStefan Weil     EEPROM_ID_VALID = BIT(14),  /* signature for valid eeprom */
306b1e87018SStefan Weil } eeprom_id_bit;
307b1e87018SStefan Weil 
308663e8e51Sths /* Default values for MDI (PHY) registers */
309663e8e51Sths static const uint16_t eepro100_mdi_default[] = {
310663e8e51Sths     /* MDI Registers 0 - 6, 7 */
311663e8e51Sths     0x3000, 0x780d, 0x02a8, 0x0154, 0x05e1, 0x0000, 0x0000, 0x0000,
312663e8e51Sths     /* MDI Registers 8 - 15 */
313663e8e51Sths     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
314663e8e51Sths     /* MDI Registers 16 - 31 */
315663e8e51Sths     0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
316663e8e51Sths     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
317663e8e51Sths };
318663e8e51Sths 
319663e8e51Sths /* Readonly mask for MDI (PHY) registers */
320663e8e51Sths static const uint16_t eepro100_mdi_mask[] = {
321663e8e51Sths     0x0000, 0xffff, 0xffff, 0xffff, 0xc01f, 0xffff, 0xffff, 0x0000,
322663e8e51Sths     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
323663e8e51Sths     0x0fff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
324663e8e51Sths     0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
325663e8e51Sths };
326663e8e51Sths 
32740021f08SAnthony Liguori static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s);
32840021f08SAnthony Liguori 
329e5e23ab8SStefan Weil /* Read a 16 bit control/status (CSR) register. */
330e5e23ab8SStefan Weil static uint16_t e100_read_reg2(EEPRO100State *s, E100RegisterOffset addr)
331e5e23ab8SStefan Weil {
332e5e23ab8SStefan Weil     assert(!((uintptr_t)&s->mem[addr] & 1));
3334d9be252SPeter Maydell     return lduw_le_p(&s->mem[addr]);
334e5e23ab8SStefan Weil }
335e5e23ab8SStefan Weil 
336e5e23ab8SStefan Weil /* Read a 32 bit control/status (CSR) register. */
337e5e23ab8SStefan Weil static uint32_t e100_read_reg4(EEPRO100State *s, E100RegisterOffset addr)
338e5e23ab8SStefan Weil {
339e5e23ab8SStefan Weil     assert(!((uintptr_t)&s->mem[addr] & 3));
3404d9be252SPeter Maydell     return ldl_le_p(&s->mem[addr]);
341e5e23ab8SStefan Weil }
342e5e23ab8SStefan Weil 
343e5e23ab8SStefan Weil /* Write a 16 bit control/status (CSR) register. */
344e5e23ab8SStefan Weil static void e100_write_reg2(EEPRO100State *s, E100RegisterOffset addr,
345e5e23ab8SStefan Weil                             uint16_t val)
346e5e23ab8SStefan Weil {
347e5e23ab8SStefan Weil     assert(!((uintptr_t)&s->mem[addr] & 1));
3484d9be252SPeter Maydell     stw_le_p(&s->mem[addr], val);
349e5e23ab8SStefan Weil }
350e5e23ab8SStefan Weil 
351e5e23ab8SStefan Weil /* Read a 32 bit control/status (CSR) register. */
352e5e23ab8SStefan Weil static void e100_write_reg4(EEPRO100State *s, E100RegisterOffset addr,
353e5e23ab8SStefan Weil                             uint32_t val)
354e5e23ab8SStefan Weil {
355e5e23ab8SStefan Weil     assert(!((uintptr_t)&s->mem[addr] & 3));
3564d9be252SPeter Maydell     stl_le_p(&s->mem[addr], val);
357e5e23ab8SStefan Weil }
358e5e23ab8SStefan Weil 
359663e8e51Sths #if defined(DEBUG_EEPRO100)
360663e8e51Sths static const char *nic_dump(const uint8_t * buf, unsigned size)
361663e8e51Sths {
362663e8e51Sths     static char dump[3 * 16 + 1];
363663e8e51Sths     char *p = &dump[0];
364aac443e6SStefan Weil     if (size > 16) {
365663e8e51Sths         size = 16;
366aac443e6SStefan Weil     }
367663e8e51Sths     while (size-- > 0) {
368663e8e51Sths         p += sprintf(p, " %02x", *buf++);
369663e8e51Sths     }
370663e8e51Sths     return dump;
371663e8e51Sths }
372663e8e51Sths #endif                          /* DEBUG_EEPRO100 */
373663e8e51Sths 
374663e8e51Sths enum scb_stat_ack {
375663e8e51Sths     stat_ack_not_ours = 0x00,
376663e8e51Sths     stat_ack_sw_gen = 0x04,
377663e8e51Sths     stat_ack_rnr = 0x10,
378663e8e51Sths     stat_ack_cu_idle = 0x20,
379663e8e51Sths     stat_ack_frame_rx = 0x40,
380663e8e51Sths     stat_ack_cu_cmd_done = 0x80,
381663e8e51Sths     stat_ack_not_present = 0xFF,
382663e8e51Sths     stat_ack_rx = (stat_ack_sw_gen | stat_ack_rnr | stat_ack_frame_rx),
383663e8e51Sths     stat_ack_tx = (stat_ack_cu_idle | stat_ack_cu_cmd_done),
384663e8e51Sths };
385663e8e51Sths 
386663e8e51Sths static void disable_interrupt(EEPRO100State * s)
387663e8e51Sths {
388663e8e51Sths     if (s->int_stat) {
389aac443e6SStefan Weil         TRACE(INT, logout("interrupt disabled\n"));
3909e64f8a3SMarcel Apfelbaum         pci_irq_deassert(&s->dev);
391663e8e51Sths         s->int_stat = 0;
392663e8e51Sths     }
393663e8e51Sths }
394663e8e51Sths 
395663e8e51Sths static void enable_interrupt(EEPRO100State * s)
396663e8e51Sths {
397663e8e51Sths     if (!s->int_stat) {
398aac443e6SStefan Weil         TRACE(INT, logout("interrupt enabled\n"));
3999e64f8a3SMarcel Apfelbaum         pci_irq_assert(&s->dev);
400663e8e51Sths         s->int_stat = 1;
401663e8e51Sths     }
402663e8e51Sths }
403663e8e51Sths 
404663e8e51Sths static void eepro100_acknowledge(EEPRO100State * s)
405663e8e51Sths {
406663e8e51Sths     s->scb_stat &= ~s->mem[SCBAck];
407663e8e51Sths     s->mem[SCBAck] = s->scb_stat;
408663e8e51Sths     if (s->scb_stat == 0) {
409663e8e51Sths         disable_interrupt(s);
410663e8e51Sths     }
411663e8e51Sths }
412663e8e51Sths 
413e715c8e8SStefan Weil static void eepro100_interrupt(EEPRO100State * s, uint8_t status)
414663e8e51Sths {
415663e8e51Sths     uint8_t mask = ~s->mem[SCBIntmask];
416e715c8e8SStefan Weil     s->mem[SCBAck] |= status;
417e715c8e8SStefan Weil     status = s->scb_stat = s->mem[SCBAck];
418e715c8e8SStefan Weil     status &= (mask | 0x0f);
419e7493b25SStefan Weil #if 0
420e7493b25SStefan Weil     status &= (~s->mem[SCBIntmask] | 0x0xf);
421e7493b25SStefan Weil #endif
422e715c8e8SStefan Weil     if (status && (mask & 0x01)) {
423663e8e51Sths         /* SCB mask and SCB Bit M do not disable interrupt. */
424663e8e51Sths         enable_interrupt(s);
425663e8e51Sths     } else if (s->int_stat) {
426663e8e51Sths         disable_interrupt(s);
427663e8e51Sths     }
428663e8e51Sths }
429663e8e51Sths 
430663e8e51Sths static void eepro100_cx_interrupt(EEPRO100State * s)
431663e8e51Sths {
432663e8e51Sths     /* CU completed action command. */
433663e8e51Sths     /* Transmit not ok (82557 only, not in emulation). */
434663e8e51Sths     eepro100_interrupt(s, 0x80);
435663e8e51Sths }
436663e8e51Sths 
437663e8e51Sths static void eepro100_cna_interrupt(EEPRO100State * s)
438663e8e51Sths {
439663e8e51Sths     /* CU left the active state. */
440663e8e51Sths     eepro100_interrupt(s, 0x20);
441663e8e51Sths }
442663e8e51Sths 
443663e8e51Sths static void eepro100_fr_interrupt(EEPRO100State * s)
444663e8e51Sths {
445663e8e51Sths     /* RU received a complete frame. */
446663e8e51Sths     eepro100_interrupt(s, 0x40);
447663e8e51Sths }
448663e8e51Sths 
449663e8e51Sths static void eepro100_rnr_interrupt(EEPRO100State * s)
450663e8e51Sths {
451663e8e51Sths     /* RU is not ready. */
452663e8e51Sths     eepro100_interrupt(s, 0x10);
453663e8e51Sths }
454663e8e51Sths 
455663e8e51Sths static void eepro100_mdi_interrupt(EEPRO100State * s)
456663e8e51Sths {
457663e8e51Sths     /* MDI completed read or write cycle. */
458663e8e51Sths     eepro100_interrupt(s, 0x08);
459663e8e51Sths }
460663e8e51Sths 
461663e8e51Sths static void eepro100_swi_interrupt(EEPRO100State * s)
462663e8e51Sths {
463663e8e51Sths     /* Software has requested an interrupt. */
464663e8e51Sths     eepro100_interrupt(s, 0x04);
465663e8e51Sths }
466663e8e51Sths 
467663e8e51Sths #if 0
468663e8e51Sths static void eepro100_fcp_interrupt(EEPRO100State * s)
469663e8e51Sths {
470663e8e51Sths     /* Flow control pause interrupt (82558 and later). */
471663e8e51Sths     eepro100_interrupt(s, 0x01);
472663e8e51Sths }
473663e8e51Sths #endif
474663e8e51Sths 
4759a7c2a59SMao Zhongyi static void e100_pci_reset(EEPRO100State *s, Error **errp)
476663e8e51Sths {
47740021f08SAnthony Liguori     E100PCIDeviceInfo *info = eepro100_get_class(s);
478663e8e51Sths     uint32_t device = s->device;
479273a2142SJuan Quintela     uint8_t *pci_conf = s->dev.config;
480663e8e51Sths 
481aac443e6SStefan Weil     TRACE(OTHER, logout("%p\n", s));
482663e8e51Sths 
483663e8e51Sths     /* PCI Status */
484558c8634SStefan Weil     pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM |
485558c8634SStefan Weil                                         PCI_STATUS_FAST_BACK);
486663e8e51Sths     /* PCI Latency Timer */
48715e89f59SMichael S. Tsirkin     pci_set_byte(pci_conf + PCI_LATENCY_TIMER, 0x20);   /* latency timer = 32 clocks */
488ae543b49SStefan Weil     /* Capability Pointer is set by PCI framework. */
489f62719caSStefan Weil     /* Interrupt Line */
490f62719caSStefan Weil     /* Interrupt Pin */
491f62719caSStefan Weil     pci_set_byte(pci_conf + PCI_INTERRUPT_PIN, 1);      /* interrupt pin A */
492663e8e51Sths     /* Minimum Grant */
49315e89f59SMichael S. Tsirkin     pci_set_byte(pci_conf + PCI_MIN_GNT, 0x08);
494663e8e51Sths     /* Maximum Latency */
49515e89f59SMichael S. Tsirkin     pci_set_byte(pci_conf + PCI_MAX_LAT, 0x18);
496663e8e51Sths 
49740021f08SAnthony Liguori     s->stats_size = info->stats_size;
49840021f08SAnthony Liguori     s->has_extended_tcb_support = info->has_extended_tcb_support;
499558c8634SStefan Weil 
500663e8e51Sths     switch (device) {
501ba42b646SStefan Weil     case i82550:
502663e8e51Sths     case i82551:
503ba42b646SStefan Weil     case i82557A:
504663e8e51Sths     case i82557B:
505663e8e51Sths     case i82557C:
506ba42b646SStefan Weil     case i82558A:
507663e8e51Sths     case i82558B:
508ba42b646SStefan Weil     case i82559A:
509ba42b646SStefan Weil     case i82559B:
510558c8634SStefan Weil     case i82559ER:
511558c8634SStefan Weil     case i82562:
512db667a12SStefan Weil     case i82801:
513663e8e51Sths     case i82559C:
514ba42b646SStefan Weil         break;
515663e8e51Sths     default:
516663e8e51Sths         logout("Device %X is undefined!\n", device);
517663e8e51Sths     }
518663e8e51Sths 
5193dec59a1SStefan Weil     /* Standard TxCB. */
5203dec59a1SStefan Weil     s->configuration[6] |= BIT(4);
5213dec59a1SStefan Weil 
522558c8634SStefan Weil     /* Standard statistical counters. */
523ba42b646SStefan Weil     s->configuration[6] |= BIT(5);
524ba42b646SStefan Weil 
525ba42b646SStefan Weil     if (s->stats_size == 80) {
526ba42b646SStefan Weil         /* TODO: check TCO Statistical Counters bit. Documentation not clear. */
527ba42b646SStefan Weil         if (s->configuration[6] & BIT(2)) {
528ba42b646SStefan Weil             /* TCO statistical counters. */
529ba42b646SStefan Weil             assert(s->configuration[6] & BIT(5));
530ba42b646SStefan Weil         } else {
531ba42b646SStefan Weil             if (s->configuration[6] & BIT(5)) {
532ba42b646SStefan Weil                 /* No extended statistical counters, i82557 compatible. */
533ba42b646SStefan Weil                 s->stats_size = 64;
534ba42b646SStefan Weil             } else {
535ba42b646SStefan Weil                 /* i82558 compatible. */
536ba42b646SStefan Weil                 s->stats_size = 76;
537ba42b646SStefan Weil             }
538ba42b646SStefan Weil         }
539ba42b646SStefan Weil     } else {
540ba42b646SStefan Weil         if (s->configuration[6] & BIT(5)) {
541ba42b646SStefan Weil             /* No extended statistical counters. */
542ba42b646SStefan Weil             s->stats_size = 64;
543ba42b646SStefan Weil         }
544ba42b646SStefan Weil     }
545ba42b646SStefan Weil     assert(s->stats_size > 0 && s->stats_size <= sizeof(s->statistics));
546ba42b646SStefan Weil 
54740021f08SAnthony Liguori     if (info->power_management) {
548ba42b646SStefan Weil         /* Power Management Capabilities */
5498bbd1ce2SMichael S. Tsirkin         int cfg_offset = 0xdc;
550ca77089dSIsaku Yamahata         int r = pci_add_capability(&s->dev, PCI_CAP_ID_PM,
5519a7c2a59SMao Zhongyi                                    cfg_offset, PCI_PM_SIZEOF,
5529a7c2a59SMao Zhongyi                                    errp);
5539a7c2a59SMao Zhongyi         if (r < 0) {
5549a7c2a59SMao Zhongyi             return;
5559a7c2a59SMao Zhongyi         }
5569a7c2a59SMao Zhongyi 
557ae543b49SStefan Weil         pci_set_word(pci_conf + cfg_offset + PCI_PM_PMC, 0x7e21);
558ae543b49SStefan Weil #if 0 /* TODO: replace dummy code for power management emulation. */
559ba42b646SStefan Weil         /* TODO: Power Management Control / Status. */
560ae543b49SStefan Weil         pci_set_word(pci_conf + cfg_offset + PCI_PM_CTRL, 0x0000);
561ba42b646SStefan Weil         /* TODO: Ethernet Power Consumption Registers (i82559 and later). */
562ae543b49SStefan Weil         pci_set_byte(pci_conf + cfg_offset + PCI_PM_PPB_EXTENSIONS, 0x0000);
563ae543b49SStefan Weil #endif
564ae543b49SStefan Weil     }
565ba42b646SStefan Weil 
566ba42b646SStefan Weil #if EEPROM_SIZE > 0
567663e8e51Sths     if (device == i82557C || device == i82558B || device == i82559C) {
568e7493b25SStefan Weil         /*
569e7493b25SStefan Weil         TODO: get vendor id from EEPROM for i82557C or later.
570e7493b25SStefan Weil         TODO: get device id from EEPROM for i82557C or later.
571e7493b25SStefan Weil         TODO: status bit 4 can be disabled by EEPROM for i82558, i82559.
572e7493b25SStefan Weil         TODO: header type is determined by EEPROM for i82559.
573e7493b25SStefan Weil         TODO: get subsystem id from EEPROM for i82557C or later.
574e7493b25SStefan Weil         TODO: get subsystem vendor id from EEPROM for i82557C or later.
575e7493b25SStefan Weil         TODO: exp. rom baddr depends on a bit in EEPROM for i82558 or later.
576e7493b25SStefan Weil         TODO: capability pointer depends on EEPROM for i82558.
577e7493b25SStefan Weil         */
578663e8e51Sths         logout("Get device id and revision from EEPROM!!!\n");
579663e8e51Sths     }
580ba42b646SStefan Weil #endif /* EEPROM_SIZE > 0 */
581663e8e51Sths }
582663e8e51Sths 
583663e8e51Sths static void nic_selective_reset(EEPRO100State * s)
584663e8e51Sths {
585663e8e51Sths     size_t i;
586663e8e51Sths     uint16_t *eeprom_contents = eeprom93xx_data(s->eeprom);
587e7493b25SStefan Weil #if 0
588e7493b25SStefan Weil     eeprom93xx_reset(s->eeprom);
589e7493b25SStefan Weil #endif
590508ef936SGerd Hoffmann     memcpy(eeprom_contents, s->conf.macaddr.a, 6);
591b1e87018SStefan Weil     eeprom_contents[EEPROM_ID] = EEPROM_ID_VALID;
592f4e94dfeS=?UTF-8?q?Reimar=20D=C3=B6ffinger?=     if (s->device == i82557B || s->device == i82557C)
593f4e94dfeS=?UTF-8?q?Reimar=20D=C3=B6ffinger?=         eeprom_contents[5] = 0x0100;
5946cded3a4SStefan Weil     eeprom_contents[EEPROM_PHY_ID] = 1;
595663e8e51Sths     uint16_t sum = 0;
596663e8e51Sths     for (i = 0; i < EEPROM_SIZE - 1; i++) {
597663e8e51Sths         sum += eeprom_contents[i];
598663e8e51Sths     }
599663e8e51Sths     eeprom_contents[EEPROM_SIZE - 1] = 0xbaba - sum;
600aac443e6SStefan Weil     TRACE(EEPROM, logout("checksum=0x%04x\n", eeprom_contents[EEPROM_SIZE - 1]));
601663e8e51Sths 
602663e8e51Sths     memset(s->mem, 0, sizeof(s->mem));
603e5e23ab8SStefan Weil     e100_write_reg4(s, SCBCtrlMDI, BIT(21));
604663e8e51Sths 
605663e8e51Sths     assert(sizeof(s->mdimem) == sizeof(eepro100_mdi_default));
606663e8e51Sths     memcpy(&s->mdimem[0], &eepro100_mdi_default[0], sizeof(s->mdimem));
607663e8e51Sths }
608663e8e51Sths 
609663e8e51Sths static void nic_reset(void *opaque)
610663e8e51Sths {
611769cf7a5SJuan Quintela     EEPRO100State *s = opaque;
612aac443e6SStefan Weil     TRACE(OTHER, logout("%p\n", s));
613010ec629SStefan Weil     /* TODO: Clearing of hash register for selective reset, too? */
6147b8737deSStefan Weil     memset(&s->mult[0], 0, sizeof(s->mult));
615663e8e51Sths     nic_selective_reset(s);
616663e8e51Sths }
617663e8e51Sths 
618663e8e51Sths #if defined(DEBUG_EEPRO100)
619b8f6ba0dSStefan Weil static const char * const e100_reg[PCI_IO_SIZE / 4] = {
620663e8e51Sths     "Command/Status",
621663e8e51Sths     "General Pointer",
622663e8e51Sths     "Port",
623663e8e51Sths     "EEPROM/Flash Control",
624663e8e51Sths     "MDI Control",
625663e8e51Sths     "Receive DMA Byte Count",
626b8f6ba0dSStefan Weil     "Flow Control",
627663e8e51Sths     "General Status/Control"
628663e8e51Sths };
629663e8e51Sths 
630663e8e51Sths static char *regname(uint32_t addr)
631663e8e51Sths {
632ec169288SDavid Benjamin     static char buf[32];
633663e8e51Sths     if (addr < PCI_IO_SIZE) {
634b8f6ba0dSStefan Weil         const char *r = e100_reg[addr / 4];
635663e8e51Sths         if (r != 0) {
63641cbc23cSStefan Weil             snprintf(buf, sizeof(buf), "%s+%u", r, addr % 4);
637663e8e51Sths         } else {
63841cbc23cSStefan Weil             snprintf(buf, sizeof(buf), "0x%02x", addr);
639663e8e51Sths         }
640663e8e51Sths     } else {
64141cbc23cSStefan Weil         snprintf(buf, sizeof(buf), "??? 0x%08x", addr);
642663e8e51Sths     }
643663e8e51Sths     return buf;
644663e8e51Sths }
645663e8e51Sths #endif                          /* DEBUG_EEPRO100 */
646663e8e51Sths 
647663e8e51Sths /*****************************************************************************
648663e8e51Sths  *
649663e8e51Sths  * Command emulation.
650663e8e51Sths  *
651663e8e51Sths  ****************************************************************************/
652663e8e51Sths 
653663e8e51Sths #if 0
654663e8e51Sths static uint16_t eepro100_read_command(EEPRO100State * s)
655663e8e51Sths {
656663e8e51Sths     uint16_t val = 0xffff;
657e7493b25SStefan Weil     TRACE(OTHER, logout("val=0x%04x\n", val));
658663e8e51Sths     return val;
659663e8e51Sths }
660663e8e51Sths #endif
661663e8e51Sths 
662663e8e51Sths /* Commands that can be put in a command list entry. */
663663e8e51Sths enum commands {
664663e8e51Sths     CmdNOp = 0,
665663e8e51Sths     CmdIASetup = 1,
666663e8e51Sths     CmdConfigure = 2,
667663e8e51Sths     CmdMulticastList = 3,
668663e8e51Sths     CmdTx = 4,
669663e8e51Sths     CmdTDR = 5,                 /* load microcode */
670663e8e51Sths     CmdDump = 6,
671663e8e51Sths     CmdDiagnose = 7,
672663e8e51Sths 
673663e8e51Sths     /* And some extra flags: */
674663e8e51Sths     CmdSuspend = 0x4000,        /* Suspend after completion. */
675663e8e51Sths     CmdIntr = 0x2000,           /* Interrupt after completion. */
676663e8e51Sths     CmdTxFlex = 0x0008,         /* Use "Flexible mode" for CmdTx command. */
677663e8e51Sths };
678663e8e51Sths 
679c227f099SAnthony Liguori static cu_state_t get_cu_state(EEPRO100State * s)
680663e8e51Sths {
681ced5296aSStefan Weil     return ((s->mem[SCBStatus] & BITS(7, 6)) >> 6);
682663e8e51Sths }
683663e8e51Sths 
684c227f099SAnthony Liguori static void set_cu_state(EEPRO100State * s, cu_state_t state)
685663e8e51Sths {
686ced5296aSStefan Weil     s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(7, 6)) + (state << 6);
687663e8e51Sths }
688663e8e51Sths 
689c227f099SAnthony Liguori static ru_state_t get_ru_state(EEPRO100State * s)
690663e8e51Sths {
691ced5296aSStefan Weil     return ((s->mem[SCBStatus] & BITS(5, 2)) >> 2);
692663e8e51Sths }
693663e8e51Sths 
694c227f099SAnthony Liguori static void set_ru_state(EEPRO100State * s, ru_state_t state)
695663e8e51Sths {
696ced5296aSStefan Weil     s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(5, 2)) + (state << 2);
697663e8e51Sths }
698663e8e51Sths 
699663e8e51Sths static void dump_statistics(EEPRO100State * s)
700663e8e51Sths {
701663e8e51Sths     /* Dump statistical data. Most data is never changed by the emulation
702663e8e51Sths      * and always 0, so we first just copy the whole block and then those
703663e8e51Sths      * values which really matter.
704663e8e51Sths      * Number of data should check configuration!!!
705663e8e51Sths      */
706e965d4bcSDavid Gibson     pci_dma_write(&s->dev, s->statsaddr, &s->statistics, s->stats_size);
70716ef60c9SEduard - Gabriel Munteanu     stl_le_pci_dma(&s->dev, s->statsaddr + 0,
70816ef60c9SEduard - Gabriel Munteanu                    s->statistics.tx_good_frames);
70916ef60c9SEduard - Gabriel Munteanu     stl_le_pci_dma(&s->dev, s->statsaddr + 36,
71016ef60c9SEduard - Gabriel Munteanu                    s->statistics.rx_good_frames);
71116ef60c9SEduard - Gabriel Munteanu     stl_le_pci_dma(&s->dev, s->statsaddr + 48,
71216ef60c9SEduard - Gabriel Munteanu                    s->statistics.rx_resource_errors);
71316ef60c9SEduard - Gabriel Munteanu     stl_le_pci_dma(&s->dev, s->statsaddr + 60,
71416ef60c9SEduard - Gabriel Munteanu                    s->statistics.rx_short_frame_errors);
715e7493b25SStefan Weil #if 0
71616ef60c9SEduard - Gabriel Munteanu     stw_le_pci_dma(&s->dev, s->statsaddr + 76, s->statistics.xmt_tco_frames);
71716ef60c9SEduard - Gabriel Munteanu     stw_le_pci_dma(&s->dev, s->statsaddr + 78, s->statistics.rcv_tco_frames);
718e7493b25SStefan Weil     missing("CU dump statistical counters");
719e7493b25SStefan Weil #endif
720663e8e51Sths }
721663e8e51Sths 
7223d0f4b9bSStefan Weil static void read_cb(EEPRO100State *s)
7233d0f4b9bSStefan Weil {
724e965d4bcSDavid Gibson     pci_dma_read(&s->dev, s->cb_address, &s->tx, sizeof(s->tx));
7253d0f4b9bSStefan Weil     s->tx.status = le16_to_cpu(s->tx.status);
7263d0f4b9bSStefan Weil     s->tx.command = le16_to_cpu(s->tx.command);
7273d0f4b9bSStefan Weil     s->tx.link = le32_to_cpu(s->tx.link);
7283d0f4b9bSStefan Weil     s->tx.tbd_array_addr = le32_to_cpu(s->tx.tbd_array_addr);
7293d0f4b9bSStefan Weil     s->tx.tcb_bytes = le16_to_cpu(s->tx.tcb_bytes);
7303d0f4b9bSStefan Weil }
7313d0f4b9bSStefan Weil 
732f3a52e50SStefan Weil static void tx_command(EEPRO100State *s)
733663e8e51Sths {
7348f8e8053SThomas Huth     uint32_t tbd_array = s->tx.tbd_array_addr;
7358f8e8053SThomas Huth     uint16_t tcb_bytes = s->tx.tcb_bytes & 0x3fff;
736f3a52e50SStefan Weil     /* Sends larger than MAX_ETH_FRAME_SIZE are allowed, up to 2600 bytes. */
737f3a52e50SStefan Weil     uint8_t buf[2600];
738f3a52e50SStefan Weil     uint16_t size = 0;
739f3a52e50SStefan Weil     uint32_t tbd_address = s->cb_address + 0x10;
740aac443e6SStefan Weil     TRACE(RXTX, logout
741663e8e51Sths         ("transmit, TBD array address 0x%08x, TCB byte count 0x%04x, TBD count %u\n",
742f3a52e50SStefan Weil          tbd_array, tcb_bytes, s->tx.tbd_count));
7437f1e9d4eSKevin Wolf 
7447f1e9d4eSKevin Wolf     if (tcb_bytes > 2600) {
7457f1e9d4eSKevin Wolf         logout("TCB byte count too large, using 2600\n");
7467f1e9d4eSKevin Wolf         tcb_bytes = 2600;
7477f1e9d4eSKevin Wolf     }
748663e8e51Sths     if (!((tcb_bytes > 0) || (tbd_array != 0xffffffff))) {
749663e8e51Sths         logout
750663e8e51Sths             ("illegal values of TBD array address and TCB byte count!\n");
751663e8e51Sths     }
752663e8e51Sths     assert(tcb_bytes <= sizeof(buf));
753663e8e51Sths     while (size < tcb_bytes) {
754aac443e6SStefan Weil         TRACE(RXTX, logout
755663e8e51Sths             ("TBD (simplified mode): buffer address 0x%08x, size 0x%04x\n",
7561865e288SMike Nawrocki              tbd_address, tcb_bytes));
7571865e288SMike Nawrocki         pci_dma_read(&s->dev, tbd_address, &buf[size], tcb_bytes);
7581865e288SMike Nawrocki         size += tcb_bytes;
759663e8e51Sths     }
760663e8e51Sths     if (tbd_array == 0xffffffff) {
761663e8e51Sths         /* Simplified mode. Was already handled by code above. */
762663e8e51Sths     } else {
763663e8e51Sths         /* Flexible mode. */
764663e8e51Sths         uint8_t tbd_count = 0;
765ba42b646SStefan Weil         if (s->has_extended_tcb_support && !(s->configuration[6] & BIT(4))) {
7663f9cb1c1SNaphtali Sprei             /* Extended Flexible TCB. */
767663e8e51Sths             for (; tbd_count < 2; tbd_count++) {
76816ef60c9SEduard - Gabriel Munteanu                 uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev,
76916ef60c9SEduard - Gabriel Munteanu                                                             tbd_address);
77016ef60c9SEduard - Gabriel Munteanu                 uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev,
77116ef60c9SEduard - Gabriel Munteanu                                                           tbd_address + 4);
77216ef60c9SEduard - Gabriel Munteanu                 uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev,
77316ef60c9SEduard - Gabriel Munteanu                                                         tbd_address + 6);
774663e8e51Sths                 tbd_address += 8;
775aac443e6SStefan Weil                 TRACE(RXTX, logout
7763f9cb1c1SNaphtali Sprei                     ("TBD (extended flexible mode): buffer address 0x%08x, size 0x%04x\n",
777aac443e6SStefan Weil                      tx_buffer_address, tx_buffer_size));
77824e6f355SReimar Döffinger                 tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size);
77916ef60c9SEduard - Gabriel Munteanu                 pci_dma_read(&s->dev, tx_buffer_address,
78016ef60c9SEduard - Gabriel Munteanu                              &buf[size], tx_buffer_size);
781663e8e51Sths                 size += tx_buffer_size;
782663e8e51Sths                 if (tx_buffer_el & 1) {
783663e8e51Sths                     break;
784663e8e51Sths                 }
785663e8e51Sths             }
786663e8e51Sths         }
787663e8e51Sths         tbd_address = tbd_array;
788f3a52e50SStefan Weil         for (; tbd_count < s->tx.tbd_count; tbd_count++) {
78916ef60c9SEduard - Gabriel Munteanu             uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev, tbd_address);
79016ef60c9SEduard - Gabriel Munteanu             uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev, tbd_address + 4);
79116ef60c9SEduard - Gabriel Munteanu             uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev, tbd_address + 6);
792663e8e51Sths             tbd_address += 8;
793aac443e6SStefan Weil             TRACE(RXTX, logout
794663e8e51Sths                 ("TBD (flexible mode): buffer address 0x%08x, size 0x%04x\n",
795aac443e6SStefan Weil                  tx_buffer_address, tx_buffer_size));
79624e6f355SReimar Döffinger             tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size);
79716ef60c9SEduard - Gabriel Munteanu             pci_dma_read(&s->dev, tx_buffer_address,
79816ef60c9SEduard - Gabriel Munteanu                          &buf[size], tx_buffer_size);
799663e8e51Sths             size += tx_buffer_size;
800663e8e51Sths             if (tx_buffer_el & 1) {
801663e8e51Sths                 break;
802663e8e51Sths             }
803663e8e51Sths         }
804663e8e51Sths     }
805aac443e6SStefan Weil     TRACE(RXTX, logout("%p sending frame, len=%d,%s\n", s, size, nic_dump(buf, size)));
806b356f76dSJason Wang     qemu_send_packet(qemu_get_queue(s->nic), buf, size);
807663e8e51Sths     s->statistics.tx_good_frames++;
808663e8e51Sths     /* Transmit with bad status would raise an CX/TNO interrupt.
809663e8e51Sths      * (82557 only). Emulation never has bad status. */
810e7493b25SStefan Weil #if 0
811e7493b25SStefan Weil     eepro100_cx_interrupt(s);
812e7493b25SStefan Weil #endif
813f3a52e50SStefan Weil }
814f3a52e50SStefan Weil 
8157b8737deSStefan Weil static void set_multicast_list(EEPRO100State *s)
8167b8737deSStefan Weil {
8177b8737deSStefan Weil     uint16_t multicast_count = s->tx.tbd_array_addr & BITS(13, 0);
8187b8737deSStefan Weil     uint16_t i;
8197b8737deSStefan Weil     memset(&s->mult[0], 0, sizeof(s->mult));
8207b8737deSStefan Weil     TRACE(OTHER, logout("multicast list, multicast count = %u\n", multicast_count));
8217b8737deSStefan Weil     for (i = 0; i < multicast_count; i += 6) {
8227b8737deSStefan Weil         uint8_t multicast_addr[6];
82316ef60c9SEduard - Gabriel Munteanu         pci_dma_read(&s->dev, s->cb_address + 10 + i, multicast_addr, 6);
8247b8737deSStefan Weil         TRACE(OTHER, logout("multicast entry %s\n", nic_dump(multicast_addr, 6)));
8257c0348bdSMark Cave-Ayland         unsigned mcast_idx = (net_crc32(multicast_addr, ETH_ALEN) &
8267c0348bdSMark Cave-Ayland                               BITS(7, 2)) >> 2;
8277b8737deSStefan Weil         assert(mcast_idx < 64);
8287b8737deSStefan Weil         s->mult[mcast_idx >> 3] |= (1 << (mcast_idx & 7));
8297b8737deSStefan Weil     }
8307b8737deSStefan Weil }
8317b8737deSStefan Weil 
832f3a52e50SStefan Weil static void action_command(EEPRO100State *s)
833f3a52e50SStefan Weil {
83400837731SStefan Weil     /* The loop below won't stop if it gets special handcrafted data.
83500837731SStefan Weil        Therefore we limit the number of iterations. */
83600837731SStefan Weil     unsigned max_loop_count = 16;
83700837731SStefan Weil 
838f3a52e50SStefan Weil     for (;;) {
8393d0f4b9bSStefan Weil         bool bit_el;
8403d0f4b9bSStefan Weil         bool bit_s;
8413d0f4b9bSStefan Weil         bool bit_i;
8423d0f4b9bSStefan Weil         bool bit_nc;
84375f5a6ccSStefan Weil         uint16_t ok_status = STATUS_OK;
8443d0f4b9bSStefan Weil         s->cb_address = s->cu_base + s->cu_offset;
8453d0f4b9bSStefan Weil         read_cb(s);
8463d0f4b9bSStefan Weil         bit_el = ((s->tx.command & COMMAND_EL) != 0);
8473d0f4b9bSStefan Weil         bit_s = ((s->tx.command & COMMAND_S) != 0);
8483d0f4b9bSStefan Weil         bit_i = ((s->tx.command & COMMAND_I) != 0);
8493d0f4b9bSStefan Weil         bit_nc = ((s->tx.command & COMMAND_NC) != 0);
8503d0f4b9bSStefan Weil #if 0
8513d0f4b9bSStefan Weil         bool bit_sf = ((s->tx.command & COMMAND_SF) != 0);
8523d0f4b9bSStefan Weil #endif
85300837731SStefan Weil 
85400837731SStefan Weil         if (max_loop_count-- == 0) {
85500837731SStefan Weil             /* Prevent an endless loop. */
85600837731SStefan Weil             logout("loop in %s:%u\n", __FILE__, __LINE__);
85700837731SStefan Weil             break;
85800837731SStefan Weil         }
85900837731SStefan Weil 
8603d0f4b9bSStefan Weil         s->cu_offset = s->tx.link;
8613d0f4b9bSStefan Weil         TRACE(OTHER,
8623d0f4b9bSStefan Weil               logout("val=(cu start), status=0x%04x, command=0x%04x, link=0x%08x\n",
8633d0f4b9bSStefan Weil                      s->tx.status, s->tx.command, s->tx.link));
8643d0f4b9bSStefan Weil         switch (s->tx.command & COMMAND_CMD) {
865f3a52e50SStefan Weil         case CmdNOp:
866f3a52e50SStefan Weil             /* Do nothing. */
867f3a52e50SStefan Weil             break;
868f3a52e50SStefan Weil         case CmdIASetup:
86916ef60c9SEduard - Gabriel Munteanu             pci_dma_read(&s->dev, s->cb_address + 8, &s->conf.macaddr.a[0], 6);
870ce0e58b3SStefan Weil             TRACE(OTHER, logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6)));
871f3a52e50SStefan Weil             break;
872f3a52e50SStefan Weil         case CmdConfigure:
87316ef60c9SEduard - Gabriel Munteanu             pci_dma_read(&s->dev, s->cb_address + 8,
87416ef60c9SEduard - Gabriel Munteanu                          &s->configuration[0], sizeof(s->configuration));
875010ec629SStefan Weil             TRACE(OTHER, logout("configuration: %s\n",
876010ec629SStefan Weil                                 nic_dump(&s->configuration[0], 16)));
877010ec629SStefan Weil             TRACE(OTHER, logout("configuration: %s\n",
878010ec629SStefan Weil                                 nic_dump(&s->configuration[16],
879010ec629SStefan Weil                                 ARRAY_SIZE(s->configuration) - 16)));
880010ec629SStefan Weil             if (s->configuration[20] & BIT(6)) {
881010ec629SStefan Weil                 TRACE(OTHER, logout("Multiple IA bit\n"));
882010ec629SStefan Weil             }
883f3a52e50SStefan Weil             break;
884f3a52e50SStefan Weil         case CmdMulticastList:
8857b8737deSStefan Weil             set_multicast_list(s);
886f3a52e50SStefan Weil             break;
887f3a52e50SStefan Weil         case CmdTx:
888f3a52e50SStefan Weil             if (bit_nc) {
889f3a52e50SStefan Weil                 missing("CmdTx: NC = 0");
89075f5a6ccSStefan Weil                 ok_status = 0;
891f3a52e50SStefan Weil                 break;
892f3a52e50SStefan Weil             }
893f3a52e50SStefan Weil             tx_command(s);
894663e8e51Sths             break;
895663e8e51Sths         case CmdTDR:
896aac443e6SStefan Weil             TRACE(OTHER, logout("load microcode\n"));
897663e8e51Sths             /* Starting with offset 8, the command contains
898663e8e51Sths              * 64 dwords microcode which we just ignore here. */
899663e8e51Sths             break;
900f80a7fc3SStefan Weil         case CmdDiagnose:
901f80a7fc3SStefan Weil             TRACE(OTHER, logout("diagnose\n"));
902f80a7fc3SStefan Weil             /* Make sure error flag is not set. */
903f80a7fc3SStefan Weil             s->tx.status = 0;
904f80a7fc3SStefan Weil             break;
905663e8e51Sths         default:
906663e8e51Sths             missing("undefined command");
90775f5a6ccSStefan Weil             ok_status = 0;
9087f1e9d4eSKevin Wolf             break;
909663e8e51Sths         }
9107f1e9d4eSKevin Wolf         /* Write new status. */
91116ef60c9SEduard - Gabriel Munteanu         stw_le_pci_dma(&s->dev, s->cb_address,
91216ef60c9SEduard - Gabriel Munteanu                        s->tx.status | ok_status | STATUS_C);
913663e8e51Sths         if (bit_i) {
914663e8e51Sths             /* CU completed action. */
915663e8e51Sths             eepro100_cx_interrupt(s);
916663e8e51Sths         }
917663e8e51Sths         if (bit_el) {
918aac443e6SStefan Weil             /* CU becomes idle. Terminate command loop. */
919663e8e51Sths             set_cu_state(s, cu_idle);
920663e8e51Sths             eepro100_cna_interrupt(s);
9215fa9a0aeSStefan Weil             break;
922663e8e51Sths         } else if (bit_s) {
9235fa9a0aeSStefan Weil             /* CU becomes suspended. Terminate command loop. */
924663e8e51Sths             set_cu_state(s, cu_suspended);
925663e8e51Sths             eepro100_cna_interrupt(s);
9265fa9a0aeSStefan Weil             break;
927663e8e51Sths         } else {
928663e8e51Sths             /* More entries in list. */
929aac443e6SStefan Weil             TRACE(OTHER, logout("CU list with at least one more entry\n"));
9305fa9a0aeSStefan Weil         }
931663e8e51Sths     }
932aac443e6SStefan Weil     TRACE(OTHER, logout("CU list empty\n"));
933663e8e51Sths     /* List is empty. Now CU is idle or suspended. */
9345fa9a0aeSStefan Weil }
9355fa9a0aeSStefan Weil 
9365fa9a0aeSStefan Weil static void eepro100_cu_command(EEPRO100State * s, uint8_t val)
9375fa9a0aeSStefan Weil {
938cb25a3fbSStefan Weil     cu_state_t cu_state;
9395fa9a0aeSStefan Weil     switch (val) {
9405fa9a0aeSStefan Weil     case CU_NOP:
9415fa9a0aeSStefan Weil         /* No operation. */
9425fa9a0aeSStefan Weil         break;
9435fa9a0aeSStefan Weil     case CU_START:
944cb25a3fbSStefan Weil         cu_state = get_cu_state(s);
945cb25a3fbSStefan Weil         if (cu_state != cu_idle && cu_state != cu_suspended) {
946cb25a3fbSStefan Weil             /* Intel documentation says that CU must be idle or suspended
947cb25a3fbSStefan Weil              * for the CU start command. */
948cb25a3fbSStefan Weil             logout("unexpected CU state is %u\n", cu_state);
9495fa9a0aeSStefan Weil         }
9505fa9a0aeSStefan Weil         set_cu_state(s, cu_active);
95127a05006SStefan Weil         s->cu_offset = e100_read_reg4(s, SCBPointer);
9525fa9a0aeSStefan Weil         action_command(s);
953663e8e51Sths         break;
954663e8e51Sths     case CU_RESUME:
955663e8e51Sths         if (get_cu_state(s) != cu_suspended) {
956663e8e51Sths             logout("bad CU resume from CU state %u\n", get_cu_state(s));
957663e8e51Sths             /* Workaround for bad Linux eepro100 driver which resumes
958663e8e51Sths              * from idle state. */
959e7493b25SStefan Weil #if 0
960e7493b25SStefan Weil             missing("cu resume");
961e7493b25SStefan Weil #endif
962663e8e51Sths             set_cu_state(s, cu_suspended);
963663e8e51Sths         }
964663e8e51Sths         if (get_cu_state(s) == cu_suspended) {
965aac443e6SStefan Weil             TRACE(OTHER, logout("CU resuming\n"));
966663e8e51Sths             set_cu_state(s, cu_active);
9675fa9a0aeSStefan Weil             action_command(s);
968663e8e51Sths         }
969663e8e51Sths         break;
970663e8e51Sths     case CU_STATSADDR:
971663e8e51Sths         /* Load dump counters address. */
97227a05006SStefan Weil         s->statsaddr = e100_read_reg4(s, SCBPointer);
973c16ada98SStefan Weil         TRACE(OTHER, logout("val=0x%02x (dump counters address)\n", val));
974c16ada98SStefan Weil         if (s->statsaddr & 3) {
975c16ada98SStefan Weil             /* Memory must be Dword aligned. */
976c16ada98SStefan Weil             logout("unaligned dump counters address\n");
977c16ada98SStefan Weil             /* Handling of misaligned addresses is undefined.
978c16ada98SStefan Weil              * Here we align the address by ignoring the lower bits. */
979c16ada98SStefan Weil             /* TODO: Test unaligned dump counter address on real hardware. */
980c16ada98SStefan Weil             s->statsaddr &= ~3;
981c16ada98SStefan Weil         }
982663e8e51Sths         break;
983663e8e51Sths     case CU_SHOWSTATS:
984663e8e51Sths         /* Dump statistical counters. */
985aac443e6SStefan Weil         TRACE(OTHER, logout("val=0x%02x (dump stats)\n", val));
986663e8e51Sths         dump_statistics(s);
98716ef60c9SEduard - Gabriel Munteanu         stl_le_pci_dma(&s->dev, s->statsaddr + s->stats_size, 0xa005);
988663e8e51Sths         break;
989663e8e51Sths     case CU_CMD_BASE:
990663e8e51Sths         /* Load CU base. */
991aac443e6SStefan Weil         TRACE(OTHER, logout("val=0x%02x (CU base address)\n", val));
99227a05006SStefan Weil         s->cu_base = e100_read_reg4(s, SCBPointer);
993663e8e51Sths         break;
994663e8e51Sths     case CU_DUMPSTATS:
995663e8e51Sths         /* Dump and reset statistical counters. */
996aac443e6SStefan Weil         TRACE(OTHER, logout("val=0x%02x (dump stats and reset)\n", val));
997663e8e51Sths         dump_statistics(s);
99816ef60c9SEduard - Gabriel Munteanu         stl_le_pci_dma(&s->dev, s->statsaddr + s->stats_size, 0xa007);
999663e8e51Sths         memset(&s->statistics, 0, sizeof(s->statistics));
1000663e8e51Sths         break;
1001663e8e51Sths     case CU_SRESUME:
1002663e8e51Sths         /* CU static resume. */
1003663e8e51Sths         missing("CU static resume");
1004663e8e51Sths         break;
1005663e8e51Sths     default:
1006663e8e51Sths         missing("Undefined CU command");
1007663e8e51Sths     }
1008663e8e51Sths }
1009663e8e51Sths 
1010663e8e51Sths static void eepro100_ru_command(EEPRO100State * s, uint8_t val)
1011663e8e51Sths {
1012663e8e51Sths     switch (val) {
1013663e8e51Sths     case RU_NOP:
1014663e8e51Sths         /* No operation. */
1015663e8e51Sths         break;
1016663e8e51Sths     case RX_START:
1017663e8e51Sths         /* RU start. */
1018663e8e51Sths         if (get_ru_state(s) != ru_idle) {
1019663e8e51Sths             logout("RU state is %u, should be %u\n", get_ru_state(s), ru_idle);
1020e7493b25SStefan Weil #if 0
1021e7493b25SStefan Weil             assert(!"wrong RU state");
1022e7493b25SStefan Weil #endif
1023663e8e51Sths         }
1024663e8e51Sths         set_ru_state(s, ru_ready);
102527a05006SStefan Weil         s->ru_offset = e100_read_reg4(s, SCBPointer);
1026b356f76dSJason Wang         qemu_flush_queued_packets(qemu_get_queue(s->nic));
1027aac443e6SStefan Weil         TRACE(OTHER, logout("val=0x%02x (rx start)\n", val));
1028663e8e51Sths         break;
1029663e8e51Sths     case RX_RESUME:
1030663e8e51Sths         /* Restart RU. */
1031663e8e51Sths         if (get_ru_state(s) != ru_suspended) {
1032663e8e51Sths             logout("RU state is %u, should be %u\n", get_ru_state(s),
1033663e8e51Sths                    ru_suspended);
1034e7493b25SStefan Weil #if 0
1035e7493b25SStefan Weil             assert(!"wrong RU state");
1036e7493b25SStefan Weil #endif
1037663e8e51Sths         }
1038663e8e51Sths         set_ru_state(s, ru_ready);
1039663e8e51Sths         break;
1040e824012bSStefan Weil     case RU_ABORT:
1041e824012bSStefan Weil         /* RU abort. */
1042e824012bSStefan Weil         if (get_ru_state(s) == ru_ready) {
1043e824012bSStefan Weil             eepro100_rnr_interrupt(s);
1044e824012bSStefan Weil         }
1045e824012bSStefan Weil         set_ru_state(s, ru_idle);
1046e824012bSStefan Weil         break;
1047663e8e51Sths     case RX_ADDR_LOAD:
1048663e8e51Sths         /* Load RU base. */
1049aac443e6SStefan Weil         TRACE(OTHER, logout("val=0x%02x (RU base address)\n", val));
105027a05006SStefan Weil         s->ru_base = e100_read_reg4(s, SCBPointer);
1051663e8e51Sths         break;
1052663e8e51Sths     default:
1053663e8e51Sths         logout("val=0x%02x (undefined RU command)\n", val);
1054663e8e51Sths         missing("Undefined SU command");
1055663e8e51Sths     }
1056663e8e51Sths }
1057663e8e51Sths 
1058663e8e51Sths static void eepro100_write_command(EEPRO100State * s, uint8_t val)
1059663e8e51Sths {
1060663e8e51Sths     eepro100_ru_command(s, val & 0x0f);
1061663e8e51Sths     eepro100_cu_command(s, val & 0xf0);
1062663e8e51Sths     if ((val) == 0) {
1063aac443e6SStefan Weil         TRACE(OTHER, logout("val=0x%02x\n", val));
1064663e8e51Sths     }
1065663e8e51Sths     /* Clear command byte after command was accepted. */
1066663e8e51Sths     s->mem[SCBCmd] = 0;
1067663e8e51Sths }
1068663e8e51Sths 
1069663e8e51Sths /*****************************************************************************
1070663e8e51Sths  *
1071663e8e51Sths  * EEPROM emulation.
1072663e8e51Sths  *
1073663e8e51Sths  ****************************************************************************/
1074663e8e51Sths 
1075663e8e51Sths #define EEPROM_CS       0x02
1076663e8e51Sths #define EEPROM_SK       0x01
1077663e8e51Sths #define EEPROM_DI       0x04
1078663e8e51Sths #define EEPROM_DO       0x08
1079663e8e51Sths 
1080663e8e51Sths static uint16_t eepro100_read_eeprom(EEPRO100State * s)
1081663e8e51Sths {
1082e5e23ab8SStefan Weil     uint16_t val = e100_read_reg2(s, SCBeeprom);
1083663e8e51Sths     if (eeprom93xx_read(s->eeprom)) {
1084663e8e51Sths         val |= EEPROM_DO;
1085663e8e51Sths     } else {
1086663e8e51Sths         val &= ~EEPROM_DO;
1087663e8e51Sths     }
1088aac443e6SStefan Weil     TRACE(EEPROM, logout("val=0x%04x\n", val));
1089663e8e51Sths     return val;
1090663e8e51Sths }
1091663e8e51Sths 
1092c227f099SAnthony Liguori static void eepro100_write_eeprom(eeprom_t * eeprom, uint8_t val)
1093663e8e51Sths {
1094aac443e6SStefan Weil     TRACE(EEPROM, logout("val=0x%02x\n", val));
1095663e8e51Sths 
1096ebabb67aSStefan Weil     /* mask unwritable bits */
1097e7493b25SStefan Weil #if 0
1098e7493b25SStefan Weil     val = SET_MASKED(val, 0x31, eeprom->value);
1099e7493b25SStefan Weil #endif
1100663e8e51Sths 
1101663e8e51Sths     int eecs = ((val & EEPROM_CS) != 0);
1102663e8e51Sths     int eesk = ((val & EEPROM_SK) != 0);
1103663e8e51Sths     int eedi = ((val & EEPROM_DI) != 0);
1104663e8e51Sths     eeprom93xx_write(eeprom, eecs, eesk, eedi);
1105663e8e51Sths }
1106663e8e51Sths 
1107663e8e51Sths /*****************************************************************************
1108663e8e51Sths  *
1109663e8e51Sths  * MDI emulation.
1110663e8e51Sths  *
1111663e8e51Sths  ****************************************************************************/
1112663e8e51Sths 
1113663e8e51Sths #if defined(DEBUG_EEPRO100)
11146a0b9cc9SReimar Döffinger static const char * const mdi_op_name[] = {
1115663e8e51Sths     "opcode 0",
1116663e8e51Sths     "write",
1117663e8e51Sths     "read",
1118663e8e51Sths     "opcode 3"
1119663e8e51Sths };
1120663e8e51Sths 
11216a0b9cc9SReimar Döffinger static const char * const mdi_reg_name[] = {
1122663e8e51Sths     "Control",
1123663e8e51Sths     "Status",
1124663e8e51Sths     "PHY Identification (Word 1)",
1125663e8e51Sths     "PHY Identification (Word 2)",
1126663e8e51Sths     "Auto-Negotiation Advertisement",
1127663e8e51Sths     "Auto-Negotiation Link Partner Ability",
1128663e8e51Sths     "Auto-Negotiation Expansion"
1129663e8e51Sths };
1130aac443e6SStefan Weil 
1131aac443e6SStefan Weil static const char *reg2name(uint8_t reg)
1132aac443e6SStefan Weil {
1133aac443e6SStefan Weil     static char buffer[10];
1134aac443e6SStefan Weil     const char *p = buffer;
1135aac443e6SStefan Weil     if (reg < ARRAY_SIZE(mdi_reg_name)) {
1136aac443e6SStefan Weil         p = mdi_reg_name[reg];
1137aac443e6SStefan Weil     } else {
1138aac443e6SStefan Weil         snprintf(buffer, sizeof(buffer), "reg=0x%02x", reg);
1139aac443e6SStefan Weil     }
1140aac443e6SStefan Weil     return p;
1141aac443e6SStefan Weil }
1142663e8e51Sths #endif                          /* DEBUG_EEPRO100 */
1143663e8e51Sths 
1144663e8e51Sths static uint32_t eepro100_read_mdi(EEPRO100State * s)
1145663e8e51Sths {
1146e5e23ab8SStefan Weil     uint32_t val = e100_read_reg4(s, SCBCtrlMDI);
1147663e8e51Sths 
1148663e8e51Sths #ifdef DEBUG_EEPRO100
1149663e8e51Sths     uint8_t raiseint = (val & BIT(29)) >> 29;
1150663e8e51Sths     uint8_t opcode = (val & BITS(27, 26)) >> 26;
1151663e8e51Sths     uint8_t phy = (val & BITS(25, 21)) >> 21;
1152663e8e51Sths     uint8_t reg = (val & BITS(20, 16)) >> 16;
1153663e8e51Sths     uint16_t data = (val & BITS(15, 0));
1154663e8e51Sths #endif
1155663e8e51Sths     /* Emulation takes no time to finish MDI transaction. */
1156663e8e51Sths     val |= BIT(28);
1157663e8e51Sths     TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
1158663e8e51Sths                       val, raiseint, mdi_op_name[opcode], phy,
1159aac443e6SStefan Weil                       reg2name(reg), data));
1160663e8e51Sths     return val;
1161663e8e51Sths }
1162663e8e51Sths 
11630113f48dSStefan Weil static void eepro100_write_mdi(EEPRO100State *s)
1164663e8e51Sths {
11650113f48dSStefan Weil     uint32_t val = e100_read_reg4(s, SCBCtrlMDI);
1166663e8e51Sths     uint8_t raiseint = (val & BIT(29)) >> 29;
1167663e8e51Sths     uint8_t opcode = (val & BITS(27, 26)) >> 26;
1168663e8e51Sths     uint8_t phy = (val & BITS(25, 21)) >> 21;
1169663e8e51Sths     uint8_t reg = (val & BITS(20, 16)) >> 16;
1170663e8e51Sths     uint16_t data = (val & BITS(15, 0));
1171aac443e6SStefan Weil     TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
1172aac443e6SStefan Weil           val, raiseint, mdi_op_name[opcode], phy, reg2name(reg), data));
1173663e8e51Sths     if (phy != 1) {
1174663e8e51Sths         /* Unsupported PHY address. */
1175e7493b25SStefan Weil #if 0
1176e7493b25SStefan Weil         logout("phy must be 1 but is %u\n", phy);
1177e7493b25SStefan Weil #endif
1178663e8e51Sths         data = 0;
1179663e8e51Sths     } else if (opcode != 1 && opcode != 2) {
1180663e8e51Sths         /* Unsupported opcode. */
1181663e8e51Sths         logout("opcode must be 1 or 2 but is %u\n", opcode);
1182663e8e51Sths         data = 0;
1183663e8e51Sths     } else if (reg > 6) {
1184663e8e51Sths         /* Unsupported register. */
1185663e8e51Sths         logout("register must be 0...6 but is %u\n", reg);
1186663e8e51Sths         data = 0;
1187663e8e51Sths     } else {
1188663e8e51Sths         TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
1189663e8e51Sths                           val, raiseint, mdi_op_name[opcode], phy,
1190aac443e6SStefan Weil                           reg2name(reg), data));
1191663e8e51Sths         if (opcode == 1) {
1192663e8e51Sths             /* MDI write */
1193663e8e51Sths             switch (reg) {
1194663e8e51Sths             case 0:            /* Control Register */
1195663e8e51Sths                 if (data & 0x8000) {
1196663e8e51Sths                     /* Reset status and control registers to default. */
1197663e8e51Sths                     s->mdimem[0] = eepro100_mdi_default[0];
1198663e8e51Sths                     s->mdimem[1] = eepro100_mdi_default[1];
1199663e8e51Sths                     data = s->mdimem[reg];
1200663e8e51Sths                 } else {
1201663e8e51Sths                     /* Restart Auto Configuration = Normal Operation */
1202663e8e51Sths                     data &= ~0x0200;
1203663e8e51Sths                 }
1204663e8e51Sths                 break;
1205663e8e51Sths             case 1:            /* Status Register */
1206663e8e51Sths                 missing("not writable");
1207663e8e51Sths                 break;
1208663e8e51Sths             case 2:            /* PHY Identification Register (Word 1) */
1209663e8e51Sths             case 3:            /* PHY Identification Register (Word 2) */
1210663e8e51Sths                 missing("not implemented");
1211663e8e51Sths                 break;
1212663e8e51Sths             case 4:            /* Auto-Negotiation Advertisement Register */
1213663e8e51Sths             case 5:            /* Auto-Negotiation Link Partner Ability Register */
1214663e8e51Sths                 break;
1215663e8e51Sths             case 6:            /* Auto-Negotiation Expansion Register */
1216663e8e51Sths             default:
1217663e8e51Sths                 missing("not implemented");
1218663e8e51Sths             }
12195e80dd22SPeter Maydell             s->mdimem[reg] &= eepro100_mdi_mask[reg];
12205e80dd22SPeter Maydell             s->mdimem[reg] |= data & ~eepro100_mdi_mask[reg];
1221663e8e51Sths         } else if (opcode == 2) {
1222663e8e51Sths             /* MDI read */
1223663e8e51Sths             switch (reg) {
1224663e8e51Sths             case 0:            /* Control Register */
1225663e8e51Sths                 if (data & 0x8000) {
1226663e8e51Sths                     /* Reset status and control registers to default. */
1227663e8e51Sths                     s->mdimem[0] = eepro100_mdi_default[0];
1228663e8e51Sths                     s->mdimem[1] = eepro100_mdi_default[1];
1229663e8e51Sths                 }
1230663e8e51Sths                 break;
1231663e8e51Sths             case 1:            /* Status Register */
1232663e8e51Sths                 s->mdimem[reg] |= 0x0020;
1233663e8e51Sths                 break;
1234663e8e51Sths             case 2:            /* PHY Identification Register (Word 1) */
1235663e8e51Sths             case 3:            /* PHY Identification Register (Word 2) */
1236663e8e51Sths             case 4:            /* Auto-Negotiation Advertisement Register */
1237663e8e51Sths                 break;
1238663e8e51Sths             case 5:            /* Auto-Negotiation Link Partner Ability Register */
1239663e8e51Sths                 s->mdimem[reg] = 0x41fe;
1240663e8e51Sths                 break;
1241663e8e51Sths             case 6:            /* Auto-Negotiation Expansion Register */
1242663e8e51Sths                 s->mdimem[reg] = 0x0001;
1243663e8e51Sths                 break;
1244663e8e51Sths             }
1245663e8e51Sths             data = s->mdimem[reg];
1246663e8e51Sths         }
1247663e8e51Sths         /* Emulation takes no time to finish MDI transaction.
1248663e8e51Sths          * Set MDI bit in SCB status register. */
1249663e8e51Sths         s->mem[SCBAck] |= 0x08;
1250663e8e51Sths         val |= BIT(28);
1251663e8e51Sths         if (raiseint) {
1252663e8e51Sths             eepro100_mdi_interrupt(s);
1253663e8e51Sths         }
1254663e8e51Sths     }
1255663e8e51Sths     val = (val & 0xffff0000) + data;
1256e5e23ab8SStefan Weil     e100_write_reg4(s, SCBCtrlMDI, val);
1257663e8e51Sths }
1258663e8e51Sths 
1259663e8e51Sths /*****************************************************************************
1260663e8e51Sths  *
1261663e8e51Sths  * Port emulation.
1262663e8e51Sths  *
1263663e8e51Sths  ****************************************************************************/
1264663e8e51Sths 
1265663e8e51Sths #define PORT_SOFTWARE_RESET     0
1266663e8e51Sths #define PORT_SELFTEST           1
1267663e8e51Sths #define PORT_SELECTIVE_RESET    2
1268663e8e51Sths #define PORT_DUMP               3
1269663e8e51Sths #define PORT_SELECTION_MASK     3
1270663e8e51Sths 
1271663e8e51Sths typedef struct {
1272663e8e51Sths     uint32_t st_sign;           /* Self Test Signature */
1273663e8e51Sths     uint32_t st_result;         /* Self Test Results */
1274c227f099SAnthony Liguori } eepro100_selftest_t;
1275663e8e51Sths 
1276663e8e51Sths static uint32_t eepro100_read_port(EEPRO100State * s)
1277663e8e51Sths {
1278663e8e51Sths     return 0;
1279663e8e51Sths }
1280663e8e51Sths 
12813fd3d0b4SStefan Weil static void eepro100_write_port(EEPRO100State *s)
1282663e8e51Sths {
12833fd3d0b4SStefan Weil     uint32_t val = e100_read_reg4(s, SCBPort);
1284663e8e51Sths     uint32_t address = (val & ~PORT_SELECTION_MASK);
1285663e8e51Sths     uint8_t selection = (val & PORT_SELECTION_MASK);
1286663e8e51Sths     switch (selection) {
1287663e8e51Sths     case PORT_SOFTWARE_RESET:
1288663e8e51Sths         nic_reset(s);
1289663e8e51Sths         break;
1290663e8e51Sths     case PORT_SELFTEST:
1291aac443e6SStefan Weil         TRACE(OTHER, logout("selftest address=0x%08x\n", address));
1292c227f099SAnthony Liguori         eepro100_selftest_t data;
129316ef60c9SEduard - Gabriel Munteanu         pci_dma_read(&s->dev, address, (uint8_t *) &data, sizeof(data));
1294663e8e51Sths         data.st_sign = 0xffffffff;
1295663e8e51Sths         data.st_result = 0;
129616ef60c9SEduard - Gabriel Munteanu         pci_dma_write(&s->dev, address, (uint8_t *) &data, sizeof(data));
1297663e8e51Sths         break;
1298663e8e51Sths     case PORT_SELECTIVE_RESET:
1299aac443e6SStefan Weil         TRACE(OTHER, logout("selective reset, selftest address=0x%08x\n", address));
1300663e8e51Sths         nic_selective_reset(s);
1301663e8e51Sths         break;
1302663e8e51Sths     default:
1303663e8e51Sths         logout("val=0x%08x\n", val);
1304663e8e51Sths         missing("unknown port selection");
1305663e8e51Sths     }
1306663e8e51Sths }
1307663e8e51Sths 
1308663e8e51Sths /*****************************************************************************
1309663e8e51Sths  *
1310663e8e51Sths  * General hardware emulation.
1311663e8e51Sths  *
1312663e8e51Sths  ****************************************************************************/
1313663e8e51Sths 
1314663e8e51Sths static uint8_t eepro100_read1(EEPRO100State * s, uint32_t addr)
1315663e8e51Sths {
1316ef476062SBlue Swirl     uint8_t val = 0;
1317663e8e51Sths     if (addr <= sizeof(s->mem) - sizeof(val)) {
1318e5e23ab8SStefan Weil         val = s->mem[addr];
1319663e8e51Sths     }
1320663e8e51Sths 
1321663e8e51Sths     switch (addr) {
1322663e8e51Sths     case SCBStatus:
1323663e8e51Sths     case SCBAck:
1324aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1325663e8e51Sths         break;
1326663e8e51Sths     case SCBCmd:
1327aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1328e7493b25SStefan Weil #if 0
1329e7493b25SStefan Weil         val = eepro100_read_command(s);
1330e7493b25SStefan Weil #endif
1331663e8e51Sths         break;
1332663e8e51Sths     case SCBIntmask:
1333aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1334663e8e51Sths         break;
1335663e8e51Sths     case SCBPort + 3:
1336aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1337663e8e51Sths         break;
1338663e8e51Sths     case SCBeeprom:
1339663e8e51Sths         val = eepro100_read_eeprom(s);
1340663e8e51Sths         break;
13410113f48dSStefan Weil     case SCBCtrlMDI:
13420113f48dSStefan Weil     case SCBCtrlMDI + 1:
13430113f48dSStefan Weil     case SCBCtrlMDI + 2:
13440113f48dSStefan Weil     case SCBCtrlMDI + 3:
13450113f48dSStefan Weil         val = (uint8_t)(eepro100_read_mdi(s) >> (8 * (addr & 3)));
13460113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
13470113f48dSStefan Weil         break;
13480908bba1SStefan Weil     case SCBpmdr:       /* Power Management Driver Register */
1349663e8e51Sths         val = 0;
1350aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1351663e8e51Sths         break;
1352a39bd017SStefan Weil     case SCBgctrl:      /* General Control Register */
1353a39bd017SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1354a39bd017SStefan Weil         break;
13550908bba1SStefan Weil     case SCBgstat:      /* General Status Register */
1356663e8e51Sths         /* 100 Mbps full duplex, valid link */
1357663e8e51Sths         val = 0x07;
1358aac443e6SStefan Weil         TRACE(OTHER, logout("addr=General Status val=%02x\n", val));
1359663e8e51Sths         break;
1360663e8e51Sths     default:
1361663e8e51Sths         logout("addr=%s val=0x%02x\n", regname(addr), val);
1362663e8e51Sths         missing("unknown byte read");
1363663e8e51Sths     }
1364663e8e51Sths     return val;
1365663e8e51Sths }
1366663e8e51Sths 
1367663e8e51Sths static uint16_t eepro100_read2(EEPRO100State * s, uint32_t addr)
1368663e8e51Sths {
1369ef476062SBlue Swirl     uint16_t val = 0;
1370663e8e51Sths     if (addr <= sizeof(s->mem) - sizeof(val)) {
1371e5e23ab8SStefan Weil         val = e100_read_reg2(s, addr);
1372663e8e51Sths     }
1373663e8e51Sths 
1374663e8e51Sths     switch (addr) {
1375663e8e51Sths     case SCBStatus:
1376dbbaaff6S=?UTF-8?q?Reimar=20D=C3=B6ffinger?=     case SCBCmd:
1377aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
1378663e8e51Sths         break;
1379663e8e51Sths     case SCBeeprom:
1380663e8e51Sths         val = eepro100_read_eeprom(s);
1381aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
1382663e8e51Sths         break;
13830113f48dSStefan Weil     case SCBCtrlMDI:
13840113f48dSStefan Weil     case SCBCtrlMDI + 2:
13850113f48dSStefan Weil         val = (uint16_t)(eepro100_read_mdi(s) >> (8 * (addr & 3)));
13860113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
13870113f48dSStefan Weil         break;
1388663e8e51Sths     default:
1389663e8e51Sths         logout("addr=%s val=0x%04x\n", regname(addr), val);
1390663e8e51Sths         missing("unknown word read");
1391663e8e51Sths     }
1392663e8e51Sths     return val;
1393663e8e51Sths }
1394663e8e51Sths 
1395663e8e51Sths static uint32_t eepro100_read4(EEPRO100State * s, uint32_t addr)
1396663e8e51Sths {
1397ef476062SBlue Swirl     uint32_t val = 0;
1398663e8e51Sths     if (addr <= sizeof(s->mem) - sizeof(val)) {
1399e5e23ab8SStefan Weil         val = e100_read_reg4(s, addr);
1400663e8e51Sths     }
1401663e8e51Sths 
1402663e8e51Sths     switch (addr) {
1403663e8e51Sths     case SCBStatus:
1404aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1405663e8e51Sths         break;
1406663e8e51Sths     case SCBPointer:
1407aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1408663e8e51Sths         break;
1409663e8e51Sths     case SCBPort:
1410663e8e51Sths         val = eepro100_read_port(s);
1411aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1412663e8e51Sths         break;
1413072476eaSStefan Weil     case SCBflash:
1414072476eaSStefan Weil         val = eepro100_read_eeprom(s);
1415072476eaSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1416072476eaSStefan Weil         break;
1417663e8e51Sths     case SCBCtrlMDI:
1418663e8e51Sths         val = eepro100_read_mdi(s);
1419663e8e51Sths         break;
1420663e8e51Sths     default:
1421663e8e51Sths         logout("addr=%s val=0x%08x\n", regname(addr), val);
1422663e8e51Sths         missing("unknown longword read");
1423663e8e51Sths     }
1424663e8e51Sths     return val;
1425663e8e51Sths }
1426663e8e51Sths 
1427663e8e51Sths static void eepro100_write1(EEPRO100State * s, uint32_t addr, uint8_t val)
1428663e8e51Sths {
1429e74818f3SStefan Weil     /* SCBStatus is readonly. */
1430e74818f3SStefan Weil     if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) {
1431e5e23ab8SStefan Weil         s->mem[addr] = val;
1432663e8e51Sths     }
1433663e8e51Sths 
1434663e8e51Sths     switch (addr) {
1435663e8e51Sths     case SCBStatus:
14361b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1437663e8e51Sths         break;
1438663e8e51Sths     case SCBAck:
14391b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1440663e8e51Sths         eepro100_acknowledge(s);
1441663e8e51Sths         break;
1442663e8e51Sths     case SCBCmd:
14431b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1444663e8e51Sths         eepro100_write_command(s, val);
1445663e8e51Sths         break;
1446663e8e51Sths     case SCBIntmask:
14471b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1448663e8e51Sths         if (val & BIT(1)) {
1449663e8e51Sths             eepro100_swi_interrupt(s);
1450663e8e51Sths         }
1451663e8e51Sths         eepro100_interrupt(s, 0);
1452663e8e51Sths         break;
145327a05006SStefan Weil     case SCBPointer:
145427a05006SStefan Weil     case SCBPointer + 1:
145527a05006SStefan Weil     case SCBPointer + 2:
145627a05006SStefan Weil     case SCBPointer + 3:
145727a05006SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
145827a05006SStefan Weil         break;
14593fd3d0b4SStefan Weil     case SCBPort:
14603fd3d0b4SStefan Weil     case SCBPort + 1:
14613fd3d0b4SStefan Weil     case SCBPort + 2:
14623fd3d0b4SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
14633fd3d0b4SStefan Weil         break;
1464663e8e51Sths     case SCBPort + 3:
14653fd3d0b4SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
14663fd3d0b4SStefan Weil         eepro100_write_port(s);
14673fd3d0b4SStefan Weil         break;
1468aac443e6SStefan Weil     case SCBFlow:       /* does not exist on 82557 */
14693257d2b6Sths     case SCBFlow + 1:
14703257d2b6Sths     case SCBFlow + 2:
14710908bba1SStefan Weil     case SCBpmdr:       /* does not exist on 82557 */
1472aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1473663e8e51Sths         break;
1474663e8e51Sths     case SCBeeprom:
14751b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1476663e8e51Sths         eepro100_write_eeprom(s->eeprom, val);
1477663e8e51Sths         break;
14780113f48dSStefan Weil     case SCBCtrlMDI:
14790113f48dSStefan Weil     case SCBCtrlMDI + 1:
14800113f48dSStefan Weil     case SCBCtrlMDI + 2:
14810113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
14820113f48dSStefan Weil         break;
14830113f48dSStefan Weil     case SCBCtrlMDI + 3:
14840113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
14850113f48dSStefan Weil         eepro100_write_mdi(s);
14860113f48dSStefan Weil         break;
1487663e8e51Sths     default:
1488663e8e51Sths         logout("addr=%s val=0x%02x\n", regname(addr), val);
1489663e8e51Sths         missing("unknown byte write");
1490663e8e51Sths     }
1491663e8e51Sths }
1492663e8e51Sths 
1493663e8e51Sths static void eepro100_write2(EEPRO100State * s, uint32_t addr, uint16_t val)
1494663e8e51Sths {
1495e74818f3SStefan Weil     /* SCBStatus is readonly. */
1496e74818f3SStefan Weil     if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) {
1497e5e23ab8SStefan Weil         e100_write_reg2(s, addr, val);
1498663e8e51Sths     }
1499663e8e51Sths 
1500663e8e51Sths     switch (addr) {
1501663e8e51Sths     case SCBStatus:
15021b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
1503e74818f3SStefan Weil         s->mem[SCBAck] = (val >> 8);
1504663e8e51Sths         eepro100_acknowledge(s);
1505663e8e51Sths         break;
1506663e8e51Sths     case SCBCmd:
15071b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
1508663e8e51Sths         eepro100_write_command(s, val);
1509663e8e51Sths         eepro100_write1(s, SCBIntmask, val >> 8);
1510663e8e51Sths         break;
151127a05006SStefan Weil     case SCBPointer:
151227a05006SStefan Weil     case SCBPointer + 2:
151327a05006SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
151427a05006SStefan Weil         break;
15153fd3d0b4SStefan Weil     case SCBPort:
15163fd3d0b4SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
15173fd3d0b4SStefan Weil         break;
15183fd3d0b4SStefan Weil     case SCBPort + 2:
15193fd3d0b4SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
15203fd3d0b4SStefan Weil         eepro100_write_port(s);
15213fd3d0b4SStefan Weil         break;
1522663e8e51Sths     case SCBeeprom:
15231b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
1524663e8e51Sths         eepro100_write_eeprom(s->eeprom, val);
1525663e8e51Sths         break;
15260113f48dSStefan Weil     case SCBCtrlMDI:
15270113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
15280113f48dSStefan Weil         break;
15290113f48dSStefan Weil     case SCBCtrlMDI + 2:
15300113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
15310113f48dSStefan Weil         eepro100_write_mdi(s);
15320113f48dSStefan Weil         break;
1533663e8e51Sths     default:
1534663e8e51Sths         logout("addr=%s val=0x%04x\n", regname(addr), val);
1535663e8e51Sths         missing("unknown word write");
1536663e8e51Sths     }
1537663e8e51Sths }
1538663e8e51Sths 
1539663e8e51Sths static void eepro100_write4(EEPRO100State * s, uint32_t addr, uint32_t val)
1540663e8e51Sths {
1541663e8e51Sths     if (addr <= sizeof(s->mem) - sizeof(val)) {
1542e5e23ab8SStefan Weil         e100_write_reg4(s, addr, val);
1543663e8e51Sths     }
1544663e8e51Sths 
1545663e8e51Sths     switch (addr) {
1546663e8e51Sths     case SCBPointer:
154727a05006SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1548663e8e51Sths         break;
1549663e8e51Sths     case SCBPort:
1550aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
15513fd3d0b4SStefan Weil         eepro100_write_port(s);
1552663e8e51Sths         break;
1553072476eaSStefan Weil     case SCBflash:
1554072476eaSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1555072476eaSStefan Weil         val = val >> 16;
1556072476eaSStefan Weil         eepro100_write_eeprom(s->eeprom, val);
1557072476eaSStefan Weil         break;
1558663e8e51Sths     case SCBCtrlMDI:
15590113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
15600113f48dSStefan Weil         eepro100_write_mdi(s);
1561663e8e51Sths         break;
1562663e8e51Sths     default:
1563663e8e51Sths         logout("addr=%s val=0x%08x\n", regname(addr), val);
1564663e8e51Sths         missing("unknown longword write");
1565663e8e51Sths     }
1566663e8e51Sths }
1567663e8e51Sths 
1568a8170e5eSAvi Kivity static uint64_t eepro100_read(void *opaque, hwaddr addr,
15695e6ffddeSAvi Kivity                               unsigned size)
1570663e8e51Sths {
1571663e8e51Sths     EEPRO100State *s = opaque;
15725e6ffddeSAvi Kivity 
15735e6ffddeSAvi Kivity     switch (size) {
15745e6ffddeSAvi Kivity     case 1: return eepro100_read1(s, addr);
15755e6ffddeSAvi Kivity     case 2: return eepro100_read2(s, addr);
15765e6ffddeSAvi Kivity     case 4: return eepro100_read4(s, addr);
15775e6ffddeSAvi Kivity     default: abort();
15785e6ffddeSAvi Kivity     }
1579663e8e51Sths }
1580663e8e51Sths 
1581a8170e5eSAvi Kivity static void eepro100_write(void *opaque, hwaddr addr,
15825e6ffddeSAvi Kivity                            uint64_t data, unsigned size)
1583663e8e51Sths {
1584663e8e51Sths     EEPRO100State *s = opaque;
15855e6ffddeSAvi Kivity 
15865e6ffddeSAvi Kivity     switch (size) {
15870ed8b6f6SBlue Swirl     case 1:
15880ed8b6f6SBlue Swirl         eepro100_write1(s, addr, data);
15890ed8b6f6SBlue Swirl         break;
15900ed8b6f6SBlue Swirl     case 2:
15910ed8b6f6SBlue Swirl         eepro100_write2(s, addr, data);
15920ed8b6f6SBlue Swirl         break;
15930ed8b6f6SBlue Swirl     case 4:
15940ed8b6f6SBlue Swirl         eepro100_write4(s, addr, data);
15950ed8b6f6SBlue Swirl         break;
15960ed8b6f6SBlue Swirl     default:
15970ed8b6f6SBlue Swirl         abort();
15985e6ffddeSAvi Kivity     }
1599663e8e51Sths }
1600663e8e51Sths 
16015e6ffddeSAvi Kivity static const MemoryRegionOps eepro100_ops = {
16025e6ffddeSAvi Kivity     .read = eepro100_read,
16035e6ffddeSAvi Kivity     .write = eepro100_write,
16045e6ffddeSAvi Kivity     .endianness = DEVICE_LITTLE_ENDIAN,
1605663e8e51Sths };
1606663e8e51Sths 
16074e68f7a0SStefan Hajnoczi static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
1608663e8e51Sths {
1609663e8e51Sths     /* TODO:
1610663e8e51Sths      * - Magic packets should set bit 30 in power management driver register.
1611663e8e51Sths      * - Interesting packets should set bit 29 in power management driver register.
1612663e8e51Sths      */
1613cc1f0f45SJason Wang     EEPRO100State *s = qemu_get_nic_opaque(nc);
1614663e8e51Sths     uint16_t rfd_status = 0xa000;
1615792f1d63SStefan Weil #if defined(CONFIG_PAD_RECEIVED_FRAMES)
1616792f1d63SStefan Weil     uint8_t min_buf[60];
1617792f1d63SStefan Weil #endif
1618663e8e51Sths     static const uint8_t broadcast_macaddr[6] =
1619663e8e51Sths         { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
1620663e8e51Sths 
1621792f1d63SStefan Weil #if defined(CONFIG_PAD_RECEIVED_FRAMES)
1622792f1d63SStefan Weil     /* Pad to minimum Ethernet frame length */
1623792f1d63SStefan Weil     if (size < sizeof(min_buf)) {
1624792f1d63SStefan Weil         memcpy(min_buf, buf, size);
1625792f1d63SStefan Weil         memset(&min_buf[size], 0, sizeof(min_buf) - size);
1626792f1d63SStefan Weil         buf = min_buf;
1627792f1d63SStefan Weil         size = sizeof(min_buf);
1628792f1d63SStefan Weil     }
1629792f1d63SStefan Weil #endif
1630792f1d63SStefan Weil 
1631663e8e51Sths     if (s->configuration[8] & 0x80) {
1632663e8e51Sths         /* CSMA is disabled. */
1633663e8e51Sths         logout("%p received while CSMA is disabled\n", s);
16344f1c942bSMark McLoughlin         return -1;
1635792f1d63SStefan Weil #if !defined(CONFIG_PAD_RECEIVED_FRAMES)
1636ced5296aSStefan Weil     } else if (size < 64 && (s->configuration[7] & BIT(0))) {
1637663e8e51Sths         /* Short frame and configuration byte 7/0 (discard short receive) set:
1638663e8e51Sths          * Short frame is discarded */
1639067d01deSStefan Weil         logout("%p received short frame (%zu byte)\n", s, size);
1640663e8e51Sths         s->statistics.rx_short_frame_errors++;
1641e7493b25SStefan Weil         return -1;
1642e7493b25SStefan Weil #endif
1643ced5296aSStefan Weil     } else if ((size > MAX_ETH_FRAME_SIZE + 4) && !(s->configuration[18] & BIT(3))) {
1644663e8e51Sths         /* Long frame and configuration byte 18/3 (long receive ok) not set:
1645663e8e51Sths          * Long frames are discarded. */
1646067d01deSStefan Weil         logout("%p received long frame (%zu byte), ignored\n", s, size);
16474f1c942bSMark McLoughlin         return -1;
1648e7493b25SStefan Weil     } else if (memcmp(buf, s->conf.macaddr.a, 6) == 0) {       /* !!! */
1649663e8e51Sths         /* Frame matches individual address. */
1650663e8e51Sths         /* TODO: check configuration byte 15/4 (ignore U/L). */
1651067d01deSStefan Weil         TRACE(RXTX, logout("%p received frame for me, len=%zu\n", s, size));
1652663e8e51Sths     } else if (memcmp(buf, broadcast_macaddr, 6) == 0) {
1653663e8e51Sths         /* Broadcast frame. */
1654067d01deSStefan Weil         TRACE(RXTX, logout("%p received broadcast, len=%zu\n", s, size));
1655663e8e51Sths         rfd_status |= 0x0002;
16567b8737deSStefan Weil     } else if (buf[0] & 0x01) {
1657663e8e51Sths         /* Multicast frame. */
16587b8737deSStefan Weil         TRACE(RXTX, logout("%p received multicast, len=%zu,%s\n", s, size, nic_dump(buf, size)));
16597f1e9d4eSKevin Wolf         if (s->configuration[21] & BIT(3)) {
16607b8737deSStefan Weil           /* Multicast all bit is set, receive all multicast frames. */
16617b8737deSStefan Weil         } else {
16627c0348bdSMark Cave-Ayland           unsigned mcast_idx = (net_crc32(buf, ETH_ALEN) & BITS(7, 2)) >> 2;
16637b8737deSStefan Weil           assert(mcast_idx < 64);
16647b8737deSStefan Weil           if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) {
16657b8737deSStefan Weil             /* Multicast frame is allowed in hash table. */
1666ced5296aSStefan Weil           } else if (s->configuration[15] & BIT(0)) {
16677b8737deSStefan Weil               /* Promiscuous: receive all. */
16687b8737deSStefan Weil               rfd_status |= 0x0004;
16697b8737deSStefan Weil           } else {
16707b8737deSStefan Weil               TRACE(RXTX, logout("%p multicast ignored\n", s));
16717b8737deSStefan Weil               return -1;
16727f1e9d4eSKevin Wolf           }
1673663e8e51Sths         }
16747b8737deSStefan Weil         /* TODO: Next not for promiscuous mode? */
1675663e8e51Sths         rfd_status |= 0x0002;
1676ced5296aSStefan Weil     } else if (s->configuration[15] & BIT(0)) {
1677663e8e51Sths         /* Promiscuous: receive all. */
1678067d01deSStefan Weil         TRACE(RXTX, logout("%p received frame in promiscuous mode, len=%zu\n", s, size));
1679663e8e51Sths         rfd_status |= 0x0004;
1680010ec629SStefan Weil     } else if (s->configuration[20] & BIT(6)) {
1681010ec629SStefan Weil         /* Multiple IA bit set. */
1682d00d6d00SMark Cave-Ayland         unsigned mcast_idx = net_crc32(buf, ETH_ALEN) >> 26;
1683010ec629SStefan Weil         assert(mcast_idx < 64);
1684010ec629SStefan Weil         if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) {
1685010ec629SStefan Weil             TRACE(RXTX, logout("%p accepted, multiple IA bit set\n", s));
1686010ec629SStefan Weil         } else {
1687010ec629SStefan Weil             TRACE(RXTX, logout("%p frame ignored, multiple IA bit set\n", s));
1688010ec629SStefan Weil             return -1;
1689010ec629SStefan Weil         }
1690663e8e51Sths     } else {
1691067d01deSStefan Weil         TRACE(RXTX, logout("%p received frame, ignored, len=%zu,%s\n", s, size,
1692aac443e6SStefan Weil               nic_dump(buf, size)));
16934f1c942bSMark McLoughlin         return size;
1694663e8e51Sths     }
1695663e8e51Sths 
1696663e8e51Sths     if (get_ru_state(s) != ru_ready) {
1697aac443e6SStefan Weil         /* No resources available. */
1698aac443e6SStefan Weil         logout("no resources, state=%u\n", get_ru_state(s));
1699e824012bSStefan Weil         /* TODO: RNR interrupt only at first failed frame? */
1700e824012bSStefan Weil         eepro100_rnr_interrupt(s);
1701663e8e51Sths         s->statistics.rx_resource_errors++;
1702e7493b25SStefan Weil #if 0
1703e7493b25SStefan Weil         assert(!"no resources");
1704e7493b25SStefan Weil #endif
17054f1c942bSMark McLoughlin         return -1;
1706663e8e51Sths     }
1707e7493b25SStefan Weil     /* !!! */
1708c227f099SAnthony Liguori     eepro100_rx_t rx;
170916ef60c9SEduard - Gabriel Munteanu     pci_dma_read(&s->dev, s->ru_base + s->ru_offset,
1710e965d4bcSDavid Gibson                  &rx, sizeof(eepro100_rx_t));
1711663e8e51Sths     uint16_t rfd_command = le16_to_cpu(rx.command);
1712663e8e51Sths     uint16_t rfd_size = le16_to_cpu(rx.size);
17137f1e9d4eSKevin Wolf 
17147f1e9d4eSKevin Wolf     if (size > rfd_size) {
17157f1e9d4eSKevin Wolf         logout("Receive buffer (%" PRId16 " bytes) too small for data "
17167f1e9d4eSKevin Wolf             "(%zu bytes); data truncated\n", rfd_size, size);
17177f1e9d4eSKevin Wolf         size = rfd_size;
17187f1e9d4eSKevin Wolf     }
1719792f1d63SStefan Weil #if !defined(CONFIG_PAD_RECEIVED_FRAMES)
1720663e8e51Sths     if (size < 64) {
1721663e8e51Sths         rfd_status |= 0x0080;
1722663e8e51Sths     }
1723792f1d63SStefan Weil #endif
1724aac443e6SStefan Weil     TRACE(OTHER, logout("command 0x%04x, link 0x%08x, addr 0x%08x, size %u\n",
1725aac443e6SStefan Weil           rfd_command, rx.link, rx.rx_buf_addr, rfd_size));
172616ef60c9SEduard - Gabriel Munteanu     stw_le_pci_dma(&s->dev, s->ru_base + s->ru_offset +
1727e5e23ab8SStefan Weil                 offsetof(eepro100_rx_t, status), rfd_status);
172816ef60c9SEduard - Gabriel Munteanu     stw_le_pci_dma(&s->dev, s->ru_base + s->ru_offset +
1729e5e23ab8SStefan Weil                 offsetof(eepro100_rx_t, count), size);
1730663e8e51Sths     /* Early receive interrupt not supported. */
1731e7493b25SStefan Weil #if 0
1732e7493b25SStefan Weil     eepro100_er_interrupt(s);
1733e7493b25SStefan Weil #endif
1734663e8e51Sths     /* Receive CRC Transfer not supported. */
1735ced5296aSStefan Weil     if (s->configuration[18] & BIT(2)) {
17367f1e9d4eSKevin Wolf         missing("Receive CRC Transfer");
17377f1e9d4eSKevin Wolf         return -1;
17387f1e9d4eSKevin Wolf     }
1739663e8e51Sths     /* TODO: check stripping enable bit. */
1740e7493b25SStefan Weil #if 0
1741e7493b25SStefan Weil     assert(!(s->configuration[17] & BIT(0)));
1742e7493b25SStefan Weil #endif
174316ef60c9SEduard - Gabriel Munteanu     pci_dma_write(&s->dev, s->ru_base + s->ru_offset +
174427112f18SStefan Weil                   sizeof(eepro100_rx_t), buf, size);
1745663e8e51Sths     s->statistics.rx_good_frames++;
1746663e8e51Sths     eepro100_fr_interrupt(s);
1747663e8e51Sths     s->ru_offset = le32_to_cpu(rx.link);
1748ced5296aSStefan Weil     if (rfd_command & COMMAND_EL) {
1749663e8e51Sths         /* EL bit is set, so this was the last frame. */
17507f1e9d4eSKevin Wolf         logout("receive: Running out of frames\n");
17511069985fSBo Yang         set_ru_state(s, ru_no_resources);
17521069985fSBo Yang         eepro100_rnr_interrupt(s);
1753663e8e51Sths     }
1754ced5296aSStefan Weil     if (rfd_command & COMMAND_S) {
1755663e8e51Sths         /* S bit is set. */
1756663e8e51Sths         set_ru_state(s, ru_suspended);
1757663e8e51Sths     }
17584f1c942bSMark McLoughlin     return size;
1759663e8e51Sths }
1760663e8e51Sths 
1761151b2986SJuan Quintela static const VMStateDescription vmstate_eepro100 = {
1762151b2986SJuan Quintela     .version_id = 3,
1763151b2986SJuan Quintela     .minimum_version_id = 2,
1764151b2986SJuan Quintela     .fields = (VMStateField[]) {
1765151b2986SJuan Quintela         VMSTATE_PCI_DEVICE(dev, EEPRO100State),
1766151b2986SJuan Quintela         VMSTATE_UNUSED(32),
1767151b2986SJuan Quintela         VMSTATE_BUFFER(mult, EEPRO100State),
1768151b2986SJuan Quintela         VMSTATE_BUFFER(mem, EEPRO100State),
17693706c43fSStefan Weil         /* Save all members of struct between scb_stat and mem. */
1770151b2986SJuan Quintela         VMSTATE_UINT8(scb_stat, EEPRO100State),
1771151b2986SJuan Quintela         VMSTATE_UINT8(int_stat, EEPRO100State),
1772151b2986SJuan Quintela         VMSTATE_UNUSED(3*4),
1773151b2986SJuan Quintela         VMSTATE_MACADDR(conf.macaddr, EEPRO100State),
1774151b2986SJuan Quintela         VMSTATE_UNUSED(19*4),
1775151b2986SJuan Quintela         VMSTATE_UINT16_ARRAY(mdimem, EEPRO100State, 32),
1776aac443e6SStefan Weil         /* The eeprom should be saved and restored by its own routines. */
1777151b2986SJuan Quintela         VMSTATE_UINT32(device, EEPRO100State),
1778151b2986SJuan Quintela         /* TODO check device. */
1779151b2986SJuan Quintela         VMSTATE_UINT32(cu_base, EEPRO100State),
1780151b2986SJuan Quintela         VMSTATE_UINT32(cu_offset, EEPRO100State),
1781151b2986SJuan Quintela         VMSTATE_UINT32(ru_base, EEPRO100State),
1782151b2986SJuan Quintela         VMSTATE_UINT32(ru_offset, EEPRO100State),
1783151b2986SJuan Quintela         VMSTATE_UINT32(statsaddr, EEPRO100State),
1784ba42b646SStefan Weil         /* Save eepro100_stats_t statistics. */
1785151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_good_frames, EEPRO100State),
1786151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_max_collisions, EEPRO100State),
1787151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_late_collisions, EEPRO100State),
1788151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_underruns, EEPRO100State),
1789151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_lost_crs, EEPRO100State),
1790151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_deferred, EEPRO100State),
1791151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_single_collisions, EEPRO100State),
1792151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_multiple_collisions, EEPRO100State),
1793151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_total_collisions, EEPRO100State),
1794151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_good_frames, EEPRO100State),
1795151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_crc_errors, EEPRO100State),
1796151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_alignment_errors, EEPRO100State),
1797151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_resource_errors, EEPRO100State),
1798151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_overrun_errors, EEPRO100State),
1799151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_cdt_errors, EEPRO100State),
1800151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_short_frame_errors, EEPRO100State),
1801151b2986SJuan Quintela         VMSTATE_UINT32(statistics.fc_xmt_pause, EEPRO100State),
1802151b2986SJuan Quintela         VMSTATE_UINT32(statistics.fc_rcv_pause, EEPRO100State),
1803151b2986SJuan Quintela         VMSTATE_UINT32(statistics.fc_rcv_unsupported, EEPRO100State),
1804151b2986SJuan Quintela         VMSTATE_UINT16(statistics.xmt_tco_frames, EEPRO100State),
1805151b2986SJuan Quintela         VMSTATE_UINT16(statistics.rcv_tco_frames, EEPRO100State),
18062657c663Sbalrog         /* Configuration bytes. */
1807151b2986SJuan Quintela         VMSTATE_BUFFER(configuration, EEPRO100State),
1808151b2986SJuan Quintela         VMSTATE_END_OF_LIST()
1809663e8e51Sths     }
1810151b2986SJuan Quintela };
1811663e8e51Sths 
1812f90c2bcdSAlex Williamson static void pci_nic_uninit(PCIDevice *pci_dev)
1813b946a153Saliguori {
1814c4c270e2SStefan Weil     EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev);
1815b946a153Saliguori 
18160be71e32SAlex Williamson     vmstate_unregister(&pci_dev->qdev, s->vmstate, s);
18172634ab7fSLi Qiang     g_free(s->vmstate);
18185fce2b3eSAlex Williamson     eeprom93xx_free(&pci_dev->qdev, s->eeprom);
1819948ecf21SJason Wang     qemu_del_nic(s->nic);
1820b946a153Saliguori }
1821b946a153Saliguori 
1822e00e365eSMark McLoughlin static NetClientInfo net_eepro100_info = {
1823f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
1824e00e365eSMark McLoughlin     .size = sizeof(NICState),
1825e00e365eSMark McLoughlin     .receive = nic_receive,
1826e00e365eSMark McLoughlin };
1827e00e365eSMark McLoughlin 
18289af21dbeSMarkus Armbruster static void e100_nic_realize(PCIDevice *pci_dev, Error **errp)
1829663e8e51Sths {
1830273a2142SJuan Quintela     EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev);
183140021f08SAnthony Liguori     E100PCIDeviceInfo *info = eepro100_get_class(s);
18329a7c2a59SMao Zhongyi     Error *local_err = NULL;
1833663e8e51Sths 
1834aac443e6SStefan Weil     TRACE(OTHER, logout("\n"));
1835663e8e51Sths 
183640021f08SAnthony Liguori     s->device = info->device;
1837663e8e51Sths 
18389a7c2a59SMao Zhongyi     e100_pci_reset(s, &local_err);
18399a7c2a59SMao Zhongyi     if (local_err) {
18409a7c2a59SMao Zhongyi         error_propagate(errp, local_err);
18419a7c2a59SMao Zhongyi         return;
18429a7c2a59SMao Zhongyi     }
1843663e8e51Sths 
1844663e8e51Sths     /* Add 64 * 2 EEPROM. i82557 and i82558 support a 64 word EEPROM,
1845663e8e51Sths      * i82559 and later support 64 or 256 word EEPROM. */
18465fce2b3eSAlex Williamson     s->eeprom = eeprom93xx_new(&pci_dev->qdev, EEPROM_SIZE);
1847663e8e51Sths 
1848663e8e51Sths     /* Handler for memory-mapped I/O */
1849eedfac6fSPaolo Bonzini     memory_region_init_io(&s->mmio_bar, OBJECT(s), &eepro100_ops, s,
1850eedfac6fSPaolo Bonzini                           "eepro100-mmio", PCI_MEM_SIZE);
1851e824b2ccSAvi Kivity     pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->mmio_bar);
1852eedfac6fSPaolo Bonzini     memory_region_init_io(&s->io_bar, OBJECT(s), &eepro100_ops, s,
1853eedfac6fSPaolo Bonzini                           "eepro100-io", PCI_IO_SIZE);
1854e824b2ccSAvi Kivity     pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar);
18555e6ffddeSAvi Kivity     /* FIXME: flash aliases to mmio?! */
1856eedfac6fSPaolo Bonzini     memory_region_init_io(&s->flash_bar, OBJECT(s), &eepro100_ops, s,
1857eedfac6fSPaolo Bonzini                           "eepro100-flash", PCI_FLASH_SIZE);
1858e824b2ccSAvi Kivity     pci_register_bar(&s->dev, 2, 0, &s->flash_bar);
1859663e8e51Sths 
1860508ef936SGerd Hoffmann     qemu_macaddr_default_if_unset(&s->conf.macaddr);
1861ce0e58b3SStefan Weil     logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6));
1862663e8e51Sths 
1863663e8e51Sths     nic_reset(s);
1864663e8e51Sths 
1865e00e365eSMark McLoughlin     s->nic = qemu_new_nic(&net_eepro100_info, &s->conf,
1866f79f2bfcSAnthony Liguori                           object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s);
1867663e8e51Sths 
1868b356f76dSJason Wang     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
1869b356f76dSJason Wang     TRACE(OTHER, logout("%s\n", qemu_get_queue(s->nic)->info_str));
1870663e8e51Sths 
1871a08d4367SJan Kiszka     qemu_register_reset(nic_reset, s);
1872663e8e51Sths 
1873e4d67e4fSMarc-André Lureau     s->vmstate = g_memdup(&vmstate_eepro100, sizeof(vmstate_eepro100));
1874b356f76dSJason Wang     s->vmstate->name = qemu_get_queue(s->nic)->model;
18750be71e32SAlex Williamson     vmstate_register(&pci_dev->qdev, -1, s->vmstate, s);
1876663e8e51Sths }
1877663e8e51Sths 
18787317bb17SGonglei static void eepro100_instance_init(Object *obj)
18797317bb17SGonglei {
18807317bb17SGonglei     EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, PCI_DEVICE(obj));
18817317bb17SGonglei     device_add_bootindex_property(obj, &s->conf.bootindex,
18827317bb17SGonglei                                   "bootindex", "/ethernet-phy@0",
18837317bb17SGonglei                                   DEVICE(s), NULL);
18847317bb17SGonglei }
18857317bb17SGonglei 
1886558c8634SStefan Weil static E100PCIDeviceInfo e100_devices[] = {
1887663e8e51Sths     {
188839bffca2SAnthony Liguori         .name = "i82550",
188939bffca2SAnthony Liguori         .desc = "Intel i82550 Ethernet",
1890558c8634SStefan Weil         .device = i82550,
1891558c8634SStefan Weil         /* TODO: check device id. */
189240021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82551IT,
1893558c8634SStefan Weil         /* Revision ID: 0x0c, 0x0d, 0x0e. */
189440021f08SAnthony Liguori         .revision = 0x0e,
1895558c8634SStefan Weil         /* TODO: check size of statistical counters. */
1896558c8634SStefan Weil         .stats_size = 80,
1897558c8634SStefan Weil         /* TODO: check extended tcb support. */
1898558c8634SStefan Weil         .has_extended_tcb_support = true,
1899558c8634SStefan Weil         .power_management = true,
1900558c8634SStefan Weil     },{
190139bffca2SAnthony Liguori         .name = "i82551",
190239bffca2SAnthony Liguori         .desc = "Intel i82551 Ethernet",
1903558c8634SStefan Weil         .device = i82551,
190440021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82551IT,
1905558c8634SStefan Weil         /* Revision ID: 0x0f, 0x10. */
190640021f08SAnthony Liguori         .revision = 0x0f,
1907558c8634SStefan Weil         /* TODO: check size of statistical counters. */
1908558c8634SStefan Weil         .stats_size = 80,
1909558c8634SStefan Weil         .has_extended_tcb_support = true,
1910558c8634SStefan Weil         .power_management = true,
1911558c8634SStefan Weil     },{
191239bffca2SAnthony Liguori         .name = "i82557a",
191339bffca2SAnthony Liguori         .desc = "Intel i82557A Ethernet",
1914558c8634SStefan Weil         .device = i82557A,
191540021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
191640021f08SAnthony Liguori         .revision = 0x01,
1917558c8634SStefan Weil         .power_management = false,
1918558c8634SStefan Weil     },{
191939bffca2SAnthony Liguori         .name = "i82557b",
192039bffca2SAnthony Liguori         .desc = "Intel i82557B Ethernet",
1921558c8634SStefan Weil         .device = i82557B,
192240021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
192340021f08SAnthony Liguori         .revision = 0x02,
1924558c8634SStefan Weil         .power_management = false,
1925558c8634SStefan Weil     },{
192639bffca2SAnthony Liguori         .name = "i82557c",
192739bffca2SAnthony Liguori         .desc = "Intel i82557C Ethernet",
1928558c8634SStefan Weil         .device = i82557C,
192940021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
193040021f08SAnthony Liguori         .revision = 0x03,
1931558c8634SStefan Weil         .power_management = false,
1932558c8634SStefan Weil     },{
193339bffca2SAnthony Liguori         .name = "i82558a",
193439bffca2SAnthony Liguori         .desc = "Intel i82558A Ethernet",
1935558c8634SStefan Weil         .device = i82558A,
193640021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
193740021f08SAnthony Liguori         .revision = 0x04,
1938558c8634SStefan Weil         .stats_size = 76,
1939558c8634SStefan Weil         .has_extended_tcb_support = true,
1940558c8634SStefan Weil         .power_management = true,
1941558c8634SStefan Weil     },{
194239bffca2SAnthony Liguori         .name = "i82558b",
194339bffca2SAnthony Liguori         .desc = "Intel i82558B Ethernet",
1944558c8634SStefan Weil         .device = i82558B,
194540021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
194640021f08SAnthony Liguori         .revision = 0x05,
1947558c8634SStefan Weil         .stats_size = 76,
1948558c8634SStefan Weil         .has_extended_tcb_support = true,
1949558c8634SStefan Weil         .power_management = true,
1950558c8634SStefan Weil     },{
195139bffca2SAnthony Liguori         .name = "i82559a",
195239bffca2SAnthony Liguori         .desc = "Intel i82559A Ethernet",
1953558c8634SStefan Weil         .device = i82559A,
195440021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
195540021f08SAnthony Liguori         .revision = 0x06,
1956558c8634SStefan Weil         .stats_size = 80,
1957558c8634SStefan Weil         .has_extended_tcb_support = true,
1958558c8634SStefan Weil         .power_management = true,
1959558c8634SStefan Weil     },{
196039bffca2SAnthony Liguori         .name = "i82559b",
196139bffca2SAnthony Liguori         .desc = "Intel i82559B Ethernet",
1962558c8634SStefan Weil         .device = i82559B,
196340021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
196440021f08SAnthony Liguori         .revision = 0x07,
1965558c8634SStefan Weil         .stats_size = 80,
1966558c8634SStefan Weil         .has_extended_tcb_support = true,
1967558c8634SStefan Weil         .power_management = true,
1968558c8634SStefan Weil     },{
196939bffca2SAnthony Liguori         .name = "i82559c",
197039bffca2SAnthony Liguori         .desc = "Intel i82559C Ethernet",
1971558c8634SStefan Weil         .device = i82559C,
197240021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
1973558c8634SStefan Weil #if 0
197440021f08SAnthony Liguori         .revision = 0x08,
1975558c8634SStefan Weil #endif
1976558c8634SStefan Weil         /* TODO: Windows wants revision id 0x0c. */
197740021f08SAnthony Liguori         .revision = 0x0c,
1978ad03502bSIsaku Yamahata #if EEPROM_SIZE > 0
197940021f08SAnthony Liguori         .subsystem_vendor_id = PCI_VENDOR_ID_INTEL,
198040021f08SAnthony Liguori         .subsystem_id = 0x0040,
1981ad03502bSIsaku Yamahata #endif
1982558c8634SStefan Weil         .stats_size = 80,
1983558c8634SStefan Weil         .has_extended_tcb_support = true,
1984558c8634SStefan Weil         .power_management = true,
1985558c8634SStefan Weil     },{
198639bffca2SAnthony Liguori         .name = "i82559er",
198739bffca2SAnthony Liguori         .desc = "Intel i82559ER Ethernet",
1988558c8634SStefan Weil         .device = i82559ER,
198940021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82551IT,
199040021f08SAnthony Liguori         .revision = 0x09,
1991558c8634SStefan Weil         .stats_size = 80,
1992558c8634SStefan Weil         .has_extended_tcb_support = true,
1993558c8634SStefan Weil         .power_management = true,
1994558c8634SStefan Weil     },{
199539bffca2SAnthony Liguori         .name = "i82562",
199639bffca2SAnthony Liguori         .desc = "Intel i82562 Ethernet",
1997558c8634SStefan Weil         .device = i82562,
1998558c8634SStefan Weil         /* TODO: check device id. */
199940021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82551IT,
2000558c8634SStefan Weil         /* TODO: wrong revision id. */
200140021f08SAnthony Liguori         .revision = 0x0e,
2002558c8634SStefan Weil         .stats_size = 80,
2003558c8634SStefan Weil         .has_extended_tcb_support = true,
2004558c8634SStefan Weil         .power_management = true,
2005db667a12SStefan Weil     },{
2006db667a12SStefan Weil         /* Toshiba Tecra 8200. */
200739bffca2SAnthony Liguori         .name = "i82801",
200839bffca2SAnthony Liguori         .desc = "Intel i82801 Ethernet",
2009db667a12SStefan Weil         .device = i82801,
201040021f08SAnthony Liguori         .device_id = 0x2449,
201140021f08SAnthony Liguori         .revision = 0x03,
2012db667a12SStefan Weil         .stats_size = 80,
2013db667a12SStefan Weil         .has_extended_tcb_support = true,
2014db667a12SStefan Weil         .power_management = true,
2015663e8e51Sths     }
2016558c8634SStefan Weil };
2017663e8e51Sths 
201840021f08SAnthony Liguori static E100PCIDeviceInfo *eepro100_get_class_by_name(const char *typename)
201940021f08SAnthony Liguori {
202040021f08SAnthony Liguori     E100PCIDeviceInfo *info = NULL;
202140021f08SAnthony Liguori     int i;
202240021f08SAnthony Liguori 
202340021f08SAnthony Liguori     /* This is admittedly awkward but also temporary.  QOM allows for
202440021f08SAnthony Liguori      * parameterized typing and for subclassing both of which would suitable
202540021f08SAnthony Liguori      * handle what's going on here.  But class_data is already being used as
202640021f08SAnthony Liguori      * a stop-gap hack to allow incremental qdev conversion so we cannot use it
202740021f08SAnthony Liguori      * right now.  Once we merge the final QOM series, we can come back here and
202840021f08SAnthony Liguori      * do this in a much more elegant fashion.
202940021f08SAnthony Liguori      */
203040021f08SAnthony Liguori     for (i = 0; i < ARRAY_SIZE(e100_devices); i++) {
203139bffca2SAnthony Liguori         if (strcmp(e100_devices[i].name, typename) == 0) {
203240021f08SAnthony Liguori             info = &e100_devices[i];
203340021f08SAnthony Liguori             break;
203440021f08SAnthony Liguori         }
203540021f08SAnthony Liguori     }
203640021f08SAnthony Liguori     assert(info != NULL);
203740021f08SAnthony Liguori 
203840021f08SAnthony Liguori     return info;
203940021f08SAnthony Liguori }
204040021f08SAnthony Liguori 
204140021f08SAnthony Liguori static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s)
204240021f08SAnthony Liguori {
204340021f08SAnthony Liguori     return eepro100_get_class_by_name(object_get_typename(OBJECT(s)));
204440021f08SAnthony Liguori }
204540021f08SAnthony Liguori 
204639bffca2SAnthony Liguori static Property e100_properties[] = {
204739bffca2SAnthony Liguori     DEFINE_NIC_PROPERTIES(EEPRO100State, conf),
204839bffca2SAnthony Liguori     DEFINE_PROP_END_OF_LIST(),
204939bffca2SAnthony Liguori };
205039bffca2SAnthony Liguori 
205140021f08SAnthony Liguori static void eepro100_class_init(ObjectClass *klass, void *data)
205240021f08SAnthony Liguori {
205339bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
205440021f08SAnthony Liguori     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
205540021f08SAnthony Liguori     E100PCIDeviceInfo *info;
205640021f08SAnthony Liguori 
205740021f08SAnthony Liguori     info = eepro100_get_class_by_name(object_class_get_name(klass));
205840021f08SAnthony Liguori 
2059125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
206039bffca2SAnthony Liguori     dc->props = e100_properties;
206139bffca2SAnthony Liguori     dc->desc = info->desc;
206240021f08SAnthony Liguori     k->vendor_id = PCI_VENDOR_ID_INTEL;
206340021f08SAnthony Liguori     k->class_id = PCI_CLASS_NETWORK_ETHERNET;
206440021f08SAnthony Liguori     k->romfile = "pxe-eepro100.rom";
20659af21dbeSMarkus Armbruster     k->realize = e100_nic_realize;
206640021f08SAnthony Liguori     k->exit = pci_nic_uninit;
206740021f08SAnthony Liguori     k->device_id = info->device_id;
206840021f08SAnthony Liguori     k->revision = info->revision;
206940021f08SAnthony Liguori     k->subsystem_vendor_id = info->subsystem_vendor_id;
207040021f08SAnthony Liguori     k->subsystem_id = info->subsystem_id;
207140021f08SAnthony Liguori }
207240021f08SAnthony Liguori 
207383f7d43aSAndreas Färber static void eepro100_register_types(void)
20749d07d757SPaul Brook {
2075558c8634SStefan Weil     size_t i;
2076558c8634SStefan Weil     for (i = 0; i < ARRAY_SIZE(e100_devices); i++) {
207739bffca2SAnthony Liguori         TypeInfo type_info = {};
207839bffca2SAnthony Liguori         E100PCIDeviceInfo *info = &e100_devices[i];
207940021f08SAnthony Liguori 
208039bffca2SAnthony Liguori         type_info.name = info->name;
208139bffca2SAnthony Liguori         type_info.parent = TYPE_PCI_DEVICE;
208239bffca2SAnthony Liguori         type_info.class_init = eepro100_class_init;
208339bffca2SAnthony Liguori         type_info.instance_size = sizeof(EEPRO100State);
20847317bb17SGonglei         type_info.instance_init = eepro100_instance_init;
2085fd3b02c8SEduardo Habkost         type_info.interfaces = (InterfaceInfo[]) {
2086fd3b02c8SEduardo Habkost             { INTERFACE_CONVENTIONAL_PCI_DEVICE },
2087fd3b02c8SEduardo Habkost             { },
2088fd3b02c8SEduardo Habkost         };
208940021f08SAnthony Liguori 
209039bffca2SAnthony Liguori         type_register(&type_info);
2091558c8634SStefan Weil     }
20929d07d757SPaul Brook }
20939d07d757SPaul Brook 
209483f7d43aSAndreas Färber type_init(eepro100_register_types)
2095