xref: /qemu/hw/net/eepro100.c (revision e4d67e4f2e06128bc7f07263afe9acc2e2242fdc)
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"
4483c9f4caSPaolo Bonzini #include "hw/hw.h"
4583c9f4caSPaolo Bonzini #include "hw/pci/pci.h"
461422e32dSPaolo Bonzini #include "net/net.h"
470d09e41aSPaolo Bonzini #include "hw/nvram/eeprom93xx.h"
489c17d615SPaolo Bonzini #include "sysemu/sysemu.h"
499c17d615SPaolo Bonzini #include "sysemu/dma.h"
50949fc823SMarcel Apfelbaum #include "qemu/bitops.h"
519a7c2a59SMao Zhongyi #include "qapi/error.h"
52663e8e51Sths 
53792f1d63SStefan Weil /* QEMU sends frames smaller than 60 bytes to ethernet nics.
54792f1d63SStefan Weil  * Such frames are rejected by real nics and their emulations.
55792f1d63SStefan Weil  * To avoid this behaviour, other nic emulations pad received
56792f1d63SStefan Weil  * frames. The following definition enables this padding for
57792f1d63SStefan Weil  * eepro100, too. We keep the define around in case it might
58792f1d63SStefan Weil  * become useful the future if the core networking is ever
59792f1d63SStefan Weil  * changed to pad short packets itself. */
60792f1d63SStefan Weil #define CONFIG_PAD_RECEIVED_FRAMES
61792f1d63SStefan Weil 
62663e8e51Sths #define KiB 1024
63663e8e51Sths 
64aac443e6SStefan Weil /* Debug EEPRO100 card. */
65ce0e58b3SStefan Weil #if 0
66ce0e58b3SStefan Weil # define DEBUG_EEPRO100
67ce0e58b3SStefan Weil #endif
68663e8e51Sths 
69663e8e51Sths #ifdef DEBUG_EEPRO100
70001faf32SBlue Swirl #define logout(fmt, ...) fprintf(stderr, "EE100\t%-24s" fmt, __func__, ## __VA_ARGS__)
71663e8e51Sths #else
72001faf32SBlue Swirl #define logout(fmt, ...) ((void)0)
73663e8e51Sths #endif
74663e8e51Sths 
75663e8e51Sths /* Set flags to 0 to disable debug output. */
76aac443e6SStefan Weil #define INT     1       /* interrupt related actions */
77aac443e6SStefan Weil #define MDI     1       /* mdi related actions */
78aac443e6SStefan Weil #define OTHER   1
79aac443e6SStefan Weil #define RXTX    1
80aac443e6SStefan Weil #define EEPROM  1       /* eeprom related actions */
81663e8e51Sths 
82663e8e51Sths #define TRACE(flag, command) ((flag) ? (command) : (void)0)
83663e8e51Sths 
847f1e9d4eSKevin Wolf #define missing(text) fprintf(stderr, "eepro100: feature is missing in this emulation: " text "\n")
85663e8e51Sths 
86663e8e51Sths #define MAX_ETH_FRAME_SIZE 1514
87663e8e51Sths 
88663e8e51Sths /* This driver supports several different devices which are declared here. */
89c4c270e2SStefan Weil #define i82550          0x82550
90663e8e51Sths #define i82551          0x82551
91c4c270e2SStefan Weil #define i82557A         0x82557a
92663e8e51Sths #define i82557B         0x82557b
93663e8e51Sths #define i82557C         0x82557c
94c4c270e2SStefan Weil #define i82558A         0x82558a
95663e8e51Sths #define i82558B         0x82558b
96c4c270e2SStefan Weil #define i82559A         0x82559a
97c4c270e2SStefan Weil #define i82559B         0x82559b
98663e8e51Sths #define i82559C         0x82559c
99663e8e51Sths #define i82559ER        0x82559e
100663e8e51Sths #define i82562          0x82562
101db667a12SStefan Weil #define i82801          0x82801
102663e8e51Sths 
103aac443e6SStefan Weil /* Use 64 word EEPROM. TODO: could be a runtime option. */
104663e8e51Sths #define EEPROM_SIZE     64
105663e8e51Sths 
106663e8e51Sths #define PCI_MEM_SIZE            (4 * KiB)
107663e8e51Sths #define PCI_IO_SIZE             64
108663e8e51Sths #define PCI_FLASH_SIZE          (128 * KiB)
109663e8e51Sths 
110663e8e51Sths #define BITS(n, m) (((0xffffffffU << (31 - n)) >> (31 - n + m)) << m)
111663e8e51Sths 
112663e8e51Sths /* The SCB accepts the following controls for the Tx and Rx units: */
113663e8e51Sths #define  CU_NOP         0x0000  /* No operation. */
114663e8e51Sths #define  CU_START       0x0010  /* CU start. */
115663e8e51Sths #define  CU_RESUME      0x0020  /* CU resume. */
116663e8e51Sths #define  CU_STATSADDR   0x0040  /* Load dump counters address. */
117663e8e51Sths #define  CU_SHOWSTATS   0x0050  /* Dump statistical counters. */
118663e8e51Sths #define  CU_CMD_BASE    0x0060  /* Load CU base address. */
119663e8e51Sths #define  CU_DUMPSTATS   0x0070  /* Dump and reset statistical counters. */
120663e8e51Sths #define  CU_SRESUME     0x00a0  /* CU static resume. */
121663e8e51Sths 
122663e8e51Sths #define  RU_NOP         0x0000
123663e8e51Sths #define  RX_START       0x0001
124663e8e51Sths #define  RX_RESUME      0x0002
125e824012bSStefan Weil #define  RU_ABORT       0x0004
126663e8e51Sths #define  RX_ADDR_LOAD   0x0006
127663e8e51Sths #define  RX_RESUMENR    0x0007
128663e8e51Sths #define INT_MASK        0x0100
129663e8e51Sths #define DRVR_INT        0x0200  /* Driver generated interrupt. */
130663e8e51Sths 
131558c8634SStefan Weil typedef struct {
13239bffca2SAnthony Liguori     const char *name;
13339bffca2SAnthony Liguori     const char *desc;
13440021f08SAnthony Liguori     uint16_t device_id;
13540021f08SAnthony Liguori     uint8_t revision;
13640021f08SAnthony Liguori     uint16_t subsystem_vendor_id;
13740021f08SAnthony Liguori     uint16_t subsystem_id;
13840021f08SAnthony Liguori 
139558c8634SStefan Weil     uint32_t device;
140558c8634SStefan Weil     uint8_t stats_size;
141558c8634SStefan Weil     bool has_extended_tcb_support;
142558c8634SStefan Weil     bool power_management;
143558c8634SStefan Weil } E100PCIDeviceInfo;
144558c8634SStefan Weil 
145663e8e51Sths /* Offsets to the various registers.
146663e8e51Sths    All accesses need not be longword aligned. */
147e5e23ab8SStefan Weil typedef enum {
1480908bba1SStefan Weil     SCBStatus = 0,              /* Status Word. */
149663e8e51Sths     SCBAck = 1,
150663e8e51Sths     SCBCmd = 2,                 /* Rx/Command Unit command and status. */
151663e8e51Sths     SCBIntmask = 3,
152663e8e51Sths     SCBPointer = 4,             /* General purpose pointer. */
153663e8e51Sths     SCBPort = 8,                /* Misc. commands and operands.  */
1540908bba1SStefan Weil     SCBflash = 12,              /* Flash memory control. */
1550908bba1SStefan Weil     SCBeeprom = 14,             /* EEPROM control. */
156663e8e51Sths     SCBCtrlMDI = 16,            /* MDI interface control. */
157663e8e51Sths     SCBEarlyRx = 20,            /* Early receive byte count. */
1580908bba1SStefan Weil     SCBFlow = 24,               /* Flow Control. */
1590908bba1SStefan Weil     SCBpmdr = 27,               /* Power Management Driver. */
1600908bba1SStefan Weil     SCBgctrl = 28,              /* General Control. */
1610908bba1SStefan Weil     SCBgstat = 29,              /* General Status. */
162e5e23ab8SStefan Weil } E100RegisterOffset;
163663e8e51Sths 
164663e8e51Sths /* A speedo3 transmit buffer descriptor with two buffers... */
165663e8e51Sths typedef struct {
166663e8e51Sths     uint16_t status;
167663e8e51Sths     uint16_t command;
168663e8e51Sths     uint32_t link;              /* void * */
1697b8737deSStefan Weil     uint32_t tbd_array_addr;    /* transmit buffer descriptor array address. */
170663e8e51Sths     uint16_t tcb_bytes;         /* transmit command block byte count (in lower 14 bits */
171663e8e51Sths     uint8_t tx_threshold;       /* transmit threshold */
172663e8e51Sths     uint8_t tbd_count;          /* TBD number */
173e7493b25SStefan Weil #if 0
174e7493b25SStefan Weil     /* This constitutes two "TBD" entries: hdr and data */
175e7493b25SStefan Weil     uint32_t tx_buf_addr0;  /* void *, header of frame to be transmitted.  */
176e7493b25SStefan Weil     int32_t  tx_buf_size0;  /* Length of Tx hdr. */
177e7493b25SStefan Weil     uint32_t tx_buf_addr1;  /* void *, data to be transmitted.  */
178e7493b25SStefan Weil     int32_t  tx_buf_size1;  /* Length of Tx data. */
179e7493b25SStefan Weil #endif
180c227f099SAnthony Liguori } eepro100_tx_t;
181663e8e51Sths 
182663e8e51Sths /* Receive frame descriptor. */
183663e8e51Sths typedef struct {
184663e8e51Sths     int16_t status;
185663e8e51Sths     uint16_t command;
186663e8e51Sths     uint32_t link;              /* struct RxFD * */
187663e8e51Sths     uint32_t rx_buf_addr;       /* void * */
188663e8e51Sths     uint16_t count;
189663e8e51Sths     uint16_t size;
19027112f18SStefan Weil     /* Ethernet frame data follows. */
191c227f099SAnthony Liguori } eepro100_rx_t;
192663e8e51Sths 
193ced5296aSStefan Weil typedef enum {
194ced5296aSStefan Weil     COMMAND_EL = BIT(15),
195ced5296aSStefan Weil     COMMAND_S = BIT(14),
196ced5296aSStefan Weil     COMMAND_I = BIT(13),
197ced5296aSStefan Weil     COMMAND_NC = BIT(4),
198ced5296aSStefan Weil     COMMAND_SF = BIT(3),
199ced5296aSStefan Weil     COMMAND_CMD = BITS(2, 0),
200ced5296aSStefan Weil } scb_command_bit;
201ced5296aSStefan Weil 
202ced5296aSStefan Weil typedef enum {
203ced5296aSStefan Weil     STATUS_C = BIT(15),
204ced5296aSStefan Weil     STATUS_OK = BIT(13),
205ced5296aSStefan Weil } scb_status_bit;
206ced5296aSStefan Weil 
207663e8e51Sths typedef struct {
208663e8e51Sths     uint32_t tx_good_frames, tx_max_collisions, tx_late_collisions,
209663e8e51Sths              tx_underruns, tx_lost_crs, tx_deferred, tx_single_collisions,
210663e8e51Sths              tx_multiple_collisions, tx_total_collisions;
211663e8e51Sths     uint32_t rx_good_frames, rx_crc_errors, rx_alignment_errors,
212663e8e51Sths              rx_resource_errors, rx_overrun_errors, rx_cdt_errors,
213663e8e51Sths              rx_short_frame_errors;
214663e8e51Sths     uint32_t fc_xmt_pause, fc_rcv_pause, fc_rcv_unsupported;
215663e8e51Sths     uint16_t xmt_tco_frames, rcv_tco_frames;
216ba42b646SStefan Weil     /* TODO: i82559 has six reserved statistics but a total of 24 dwords. */
217ba42b646SStefan Weil     uint32_t reserved[4];
218c227f099SAnthony Liguori } eepro100_stats_t;
219663e8e51Sths 
220663e8e51Sths typedef enum {
221663e8e51Sths     cu_idle = 0,
222663e8e51Sths     cu_suspended = 1,
223663e8e51Sths     cu_active = 2,
224663e8e51Sths     cu_lpq_active = 2,
225663e8e51Sths     cu_hqp_active = 3
226c227f099SAnthony Liguori } cu_state_t;
227663e8e51Sths 
228663e8e51Sths typedef enum {
229663e8e51Sths     ru_idle = 0,
230663e8e51Sths     ru_suspended = 1,
231663e8e51Sths     ru_no_resources = 2,
232663e8e51Sths     ru_ready = 4
233c227f099SAnthony Liguori } ru_state_t;
234663e8e51Sths 
235663e8e51Sths typedef struct {
236273a2142SJuan Quintela     PCIDevice dev;
237010ec629SStefan Weil     /* Hash register (multicast mask array, multiple individual addresses). */
238010ec629SStefan Weil     uint8_t mult[8];
2395e6ffddeSAvi Kivity     MemoryRegion mmio_bar;
2405e6ffddeSAvi Kivity     MemoryRegion io_bar;
2415e6ffddeSAvi Kivity     MemoryRegion flash_bar;
242e00e365eSMark McLoughlin     NICState *nic;
243508ef936SGerd Hoffmann     NICConf conf;
244663e8e51Sths     uint8_t scb_stat;           /* SCB stat/ack byte */
245663e8e51Sths     uint8_t int_stat;           /* PCI interrupt status */
2463706c43fSStefan Weil     /* region must not be saved by nic_save. */
247663e8e51Sths     uint16_t mdimem[32];
248c227f099SAnthony Liguori     eeprom_t *eeprom;
249663e8e51Sths     uint32_t device;            /* device variant */
250663e8e51Sths     /* (cu_base + cu_offset) address the next command block in the command block list. */
251663e8e51Sths     uint32_t cu_base;           /* CU base address */
252663e8e51Sths     uint32_t cu_offset;         /* CU address offset */
253663e8e51Sths     /* (ru_base + ru_offset) address the RFD in the Receive Frame Area. */
254663e8e51Sths     uint32_t ru_base;           /* RU base address */
255663e8e51Sths     uint32_t ru_offset;         /* RU address offset */
256c227f099SAnthony Liguori     uint32_t statsaddr;         /* pointer to eepro100_stats_t */
257ba42b646SStefan Weil 
258f3a52e50SStefan Weil     /* Temporary status information (no need to save these values),
259f3a52e50SStefan Weil      * used while processing CU commands. */
260f3a52e50SStefan Weil     eepro100_tx_t tx;           /* transmit buffer descriptor */
261f3a52e50SStefan Weil     uint32_t cb_address;        /* = cu_base + cu_offset */
262f3a52e50SStefan Weil 
263ba42b646SStefan Weil     /* Statistical counters. Also used for wake-up packet (i82559). */
264ba42b646SStefan Weil     eepro100_stats_t statistics;
265ba42b646SStefan Weil 
266e5e23ab8SStefan Weil     /* Data in mem is always in the byte order of the controller (le).
267e5e23ab8SStefan Weil      * It must be dword aligned to allow direct access to 32 bit values. */
2683a93113aSDong Xu Wang     uint8_t mem[PCI_MEM_SIZE] __attribute__((aligned(8)));
269e5e23ab8SStefan Weil 
270663e8e51Sths     /* Configuration bytes. */
271663e8e51Sths     uint8_t configuration[22];
272663e8e51Sths 
273151b2986SJuan Quintela     /* vmstate for each particular nic */
274151b2986SJuan Quintela     VMStateDescription *vmstate;
275ba42b646SStefan Weil 
276ba42b646SStefan Weil     /* Quasi static device properties (no need to save them). */
277ba42b646SStefan Weil     uint16_t stats_size;
278ba42b646SStefan Weil     bool has_extended_tcb_support;
279663e8e51Sths } EEPRO100State;
280663e8e51Sths 
2816cded3a4SStefan Weil /* Word indices in EEPROM. */
2826cded3a4SStefan Weil typedef enum {
2836cded3a4SStefan Weil     EEPROM_CNFG_MDIX  = 0x03,
2846cded3a4SStefan Weil     EEPROM_ID         = 0x05,
2856cded3a4SStefan Weil     EEPROM_PHY_ID     = 0x06,
2866cded3a4SStefan Weil     EEPROM_VENDOR_ID  = 0x0c,
2876cded3a4SStefan Weil     EEPROM_CONFIG_ASF = 0x0d,
2886cded3a4SStefan Weil     EEPROM_DEVICE_ID  = 0x23,
2896cded3a4SStefan Weil     EEPROM_SMBUS_ADDR = 0x90,
2906cded3a4SStefan Weil } EEPROMOffset;
2916cded3a4SStefan Weil 
292b1e87018SStefan Weil /* Bit values for EEPROM ID word. */
293b1e87018SStefan Weil typedef enum {
294b1e87018SStefan Weil     EEPROM_ID_MDM = BIT(0),     /* Modem */
295b1e87018SStefan Weil     EEPROM_ID_STB = BIT(1),     /* Standby Enable */
296b1e87018SStefan Weil     EEPROM_ID_WMR = BIT(2),     /* ??? */
297b1e87018SStefan Weil     EEPROM_ID_WOL = BIT(5),     /* Wake on LAN */
298b1e87018SStefan Weil     EEPROM_ID_DPD = BIT(6),     /* Deep Power Down */
299b1e87018SStefan Weil     EEPROM_ID_ALT = BIT(7),     /* */
300b1e87018SStefan Weil     /* BITS(10, 8) device revision */
301b1e87018SStefan Weil     EEPROM_ID_BD = BIT(11),     /* boot disable */
302b1e87018SStefan Weil     EEPROM_ID_ID = BIT(13),     /* id bit */
303b1e87018SStefan Weil     /* BITS(15, 14) signature */
304b1e87018SStefan Weil     EEPROM_ID_VALID = BIT(14),  /* signature for valid eeprom */
305b1e87018SStefan Weil } eeprom_id_bit;
306b1e87018SStefan Weil 
307663e8e51Sths /* Default values for MDI (PHY) registers */
308663e8e51Sths static const uint16_t eepro100_mdi_default[] = {
309663e8e51Sths     /* MDI Registers 0 - 6, 7 */
310663e8e51Sths     0x3000, 0x780d, 0x02a8, 0x0154, 0x05e1, 0x0000, 0x0000, 0x0000,
311663e8e51Sths     /* MDI Registers 8 - 15 */
312663e8e51Sths     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
313663e8e51Sths     /* MDI Registers 16 - 31 */
314663e8e51Sths     0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
315663e8e51Sths     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
316663e8e51Sths };
317663e8e51Sths 
318663e8e51Sths /* Readonly mask for MDI (PHY) registers */
319663e8e51Sths static const uint16_t eepro100_mdi_mask[] = {
320663e8e51Sths     0x0000, 0xffff, 0xffff, 0xffff, 0xc01f, 0xffff, 0xffff, 0x0000,
321663e8e51Sths     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
322663e8e51Sths     0x0fff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
323663e8e51Sths     0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
324663e8e51Sths };
325663e8e51Sths 
32669f3ce78SStefan Weil #define POLYNOMIAL 0x04c11db6
32769f3ce78SStefan Weil 
32840021f08SAnthony Liguori static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s);
32940021f08SAnthony Liguori 
33069f3ce78SStefan Weil /* From FreeBSD (locally modified). */
33169f3ce78SStefan Weil static unsigned e100_compute_mcast_idx(const uint8_t *ep)
33269f3ce78SStefan Weil {
33369f3ce78SStefan Weil     uint32_t crc;
33469f3ce78SStefan Weil     int carry, i, j;
33569f3ce78SStefan Weil     uint8_t b;
33669f3ce78SStefan Weil 
33769f3ce78SStefan Weil     crc = 0xffffffff;
33869f3ce78SStefan Weil     for (i = 0; i < 6; i++) {
33969f3ce78SStefan Weil         b = *ep++;
34069f3ce78SStefan Weil         for (j = 0; j < 8; j++) {
34169f3ce78SStefan Weil             carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
34269f3ce78SStefan Weil             crc <<= 1;
34369f3ce78SStefan Weil             b >>= 1;
34469f3ce78SStefan Weil             if (carry) {
34569f3ce78SStefan Weil                 crc = ((crc ^ POLYNOMIAL) | carry);
34669f3ce78SStefan Weil             }
34769f3ce78SStefan Weil         }
34869f3ce78SStefan Weil     }
34969f3ce78SStefan Weil     return (crc & BITS(7, 2)) >> 2;
35069f3ce78SStefan Weil }
35169f3ce78SStefan Weil 
352e5e23ab8SStefan Weil /* Read a 16 bit control/status (CSR) register. */
353e5e23ab8SStefan Weil static uint16_t e100_read_reg2(EEPRO100State *s, E100RegisterOffset addr)
354e5e23ab8SStefan Weil {
355e5e23ab8SStefan Weil     assert(!((uintptr_t)&s->mem[addr] & 1));
3564d9be252SPeter Maydell     return lduw_le_p(&s->mem[addr]);
357e5e23ab8SStefan Weil }
358e5e23ab8SStefan Weil 
359e5e23ab8SStefan Weil /* Read a 32 bit control/status (CSR) register. */
360e5e23ab8SStefan Weil static uint32_t e100_read_reg4(EEPRO100State *s, E100RegisterOffset addr)
361e5e23ab8SStefan Weil {
362e5e23ab8SStefan Weil     assert(!((uintptr_t)&s->mem[addr] & 3));
3634d9be252SPeter Maydell     return ldl_le_p(&s->mem[addr]);
364e5e23ab8SStefan Weil }
365e5e23ab8SStefan Weil 
366e5e23ab8SStefan Weil /* Write a 16 bit control/status (CSR) register. */
367e5e23ab8SStefan Weil static void e100_write_reg2(EEPRO100State *s, E100RegisterOffset addr,
368e5e23ab8SStefan Weil                             uint16_t val)
369e5e23ab8SStefan Weil {
370e5e23ab8SStefan Weil     assert(!((uintptr_t)&s->mem[addr] & 1));
3714d9be252SPeter Maydell     stw_le_p(&s->mem[addr], val);
372e5e23ab8SStefan Weil }
373e5e23ab8SStefan Weil 
374e5e23ab8SStefan Weil /* Read a 32 bit control/status (CSR) register. */
375e5e23ab8SStefan Weil static void e100_write_reg4(EEPRO100State *s, E100RegisterOffset addr,
376e5e23ab8SStefan Weil                             uint32_t val)
377e5e23ab8SStefan Weil {
378e5e23ab8SStefan Weil     assert(!((uintptr_t)&s->mem[addr] & 3));
3794d9be252SPeter Maydell     stl_le_p(&s->mem[addr], val);
380e5e23ab8SStefan Weil }
381e5e23ab8SStefan Weil 
382663e8e51Sths #if defined(DEBUG_EEPRO100)
383663e8e51Sths static const char *nic_dump(const uint8_t * buf, unsigned size)
384663e8e51Sths {
385663e8e51Sths     static char dump[3 * 16 + 1];
386663e8e51Sths     char *p = &dump[0];
387aac443e6SStefan Weil     if (size > 16) {
388663e8e51Sths         size = 16;
389aac443e6SStefan Weil     }
390663e8e51Sths     while (size-- > 0) {
391663e8e51Sths         p += sprintf(p, " %02x", *buf++);
392663e8e51Sths     }
393663e8e51Sths     return dump;
394663e8e51Sths }
395663e8e51Sths #endif                          /* DEBUG_EEPRO100 */
396663e8e51Sths 
397663e8e51Sths enum scb_stat_ack {
398663e8e51Sths     stat_ack_not_ours = 0x00,
399663e8e51Sths     stat_ack_sw_gen = 0x04,
400663e8e51Sths     stat_ack_rnr = 0x10,
401663e8e51Sths     stat_ack_cu_idle = 0x20,
402663e8e51Sths     stat_ack_frame_rx = 0x40,
403663e8e51Sths     stat_ack_cu_cmd_done = 0x80,
404663e8e51Sths     stat_ack_not_present = 0xFF,
405663e8e51Sths     stat_ack_rx = (stat_ack_sw_gen | stat_ack_rnr | stat_ack_frame_rx),
406663e8e51Sths     stat_ack_tx = (stat_ack_cu_idle | stat_ack_cu_cmd_done),
407663e8e51Sths };
408663e8e51Sths 
409663e8e51Sths static void disable_interrupt(EEPRO100State * s)
410663e8e51Sths {
411663e8e51Sths     if (s->int_stat) {
412aac443e6SStefan Weil         TRACE(INT, logout("interrupt disabled\n"));
4139e64f8a3SMarcel Apfelbaum         pci_irq_deassert(&s->dev);
414663e8e51Sths         s->int_stat = 0;
415663e8e51Sths     }
416663e8e51Sths }
417663e8e51Sths 
418663e8e51Sths static void enable_interrupt(EEPRO100State * s)
419663e8e51Sths {
420663e8e51Sths     if (!s->int_stat) {
421aac443e6SStefan Weil         TRACE(INT, logout("interrupt enabled\n"));
4229e64f8a3SMarcel Apfelbaum         pci_irq_assert(&s->dev);
423663e8e51Sths         s->int_stat = 1;
424663e8e51Sths     }
425663e8e51Sths }
426663e8e51Sths 
427663e8e51Sths static void eepro100_acknowledge(EEPRO100State * s)
428663e8e51Sths {
429663e8e51Sths     s->scb_stat &= ~s->mem[SCBAck];
430663e8e51Sths     s->mem[SCBAck] = s->scb_stat;
431663e8e51Sths     if (s->scb_stat == 0) {
432663e8e51Sths         disable_interrupt(s);
433663e8e51Sths     }
434663e8e51Sths }
435663e8e51Sths 
436e715c8e8SStefan Weil static void eepro100_interrupt(EEPRO100State * s, uint8_t status)
437663e8e51Sths {
438663e8e51Sths     uint8_t mask = ~s->mem[SCBIntmask];
439e715c8e8SStefan Weil     s->mem[SCBAck] |= status;
440e715c8e8SStefan Weil     status = s->scb_stat = s->mem[SCBAck];
441e715c8e8SStefan Weil     status &= (mask | 0x0f);
442e7493b25SStefan Weil #if 0
443e7493b25SStefan Weil     status &= (~s->mem[SCBIntmask] | 0x0xf);
444e7493b25SStefan Weil #endif
445e715c8e8SStefan Weil     if (status && (mask & 0x01)) {
446663e8e51Sths         /* SCB mask and SCB Bit M do not disable interrupt. */
447663e8e51Sths         enable_interrupt(s);
448663e8e51Sths     } else if (s->int_stat) {
449663e8e51Sths         disable_interrupt(s);
450663e8e51Sths     }
451663e8e51Sths }
452663e8e51Sths 
453663e8e51Sths static void eepro100_cx_interrupt(EEPRO100State * s)
454663e8e51Sths {
455663e8e51Sths     /* CU completed action command. */
456663e8e51Sths     /* Transmit not ok (82557 only, not in emulation). */
457663e8e51Sths     eepro100_interrupt(s, 0x80);
458663e8e51Sths }
459663e8e51Sths 
460663e8e51Sths static void eepro100_cna_interrupt(EEPRO100State * s)
461663e8e51Sths {
462663e8e51Sths     /* CU left the active state. */
463663e8e51Sths     eepro100_interrupt(s, 0x20);
464663e8e51Sths }
465663e8e51Sths 
466663e8e51Sths static void eepro100_fr_interrupt(EEPRO100State * s)
467663e8e51Sths {
468663e8e51Sths     /* RU received a complete frame. */
469663e8e51Sths     eepro100_interrupt(s, 0x40);
470663e8e51Sths }
471663e8e51Sths 
472663e8e51Sths static void eepro100_rnr_interrupt(EEPRO100State * s)
473663e8e51Sths {
474663e8e51Sths     /* RU is not ready. */
475663e8e51Sths     eepro100_interrupt(s, 0x10);
476663e8e51Sths }
477663e8e51Sths 
478663e8e51Sths static void eepro100_mdi_interrupt(EEPRO100State * s)
479663e8e51Sths {
480663e8e51Sths     /* MDI completed read or write cycle. */
481663e8e51Sths     eepro100_interrupt(s, 0x08);
482663e8e51Sths }
483663e8e51Sths 
484663e8e51Sths static void eepro100_swi_interrupt(EEPRO100State * s)
485663e8e51Sths {
486663e8e51Sths     /* Software has requested an interrupt. */
487663e8e51Sths     eepro100_interrupt(s, 0x04);
488663e8e51Sths }
489663e8e51Sths 
490663e8e51Sths #if 0
491663e8e51Sths static void eepro100_fcp_interrupt(EEPRO100State * s)
492663e8e51Sths {
493663e8e51Sths     /* Flow control pause interrupt (82558 and later). */
494663e8e51Sths     eepro100_interrupt(s, 0x01);
495663e8e51Sths }
496663e8e51Sths #endif
497663e8e51Sths 
4989a7c2a59SMao Zhongyi static void e100_pci_reset(EEPRO100State *s, Error **errp)
499663e8e51Sths {
50040021f08SAnthony Liguori     E100PCIDeviceInfo *info = eepro100_get_class(s);
501663e8e51Sths     uint32_t device = s->device;
502273a2142SJuan Quintela     uint8_t *pci_conf = s->dev.config;
503663e8e51Sths 
504aac443e6SStefan Weil     TRACE(OTHER, logout("%p\n", s));
505663e8e51Sths 
506663e8e51Sths     /* PCI Status */
507558c8634SStefan Weil     pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM |
508558c8634SStefan Weil                                         PCI_STATUS_FAST_BACK);
509663e8e51Sths     /* PCI Latency Timer */
51015e89f59SMichael S. Tsirkin     pci_set_byte(pci_conf + PCI_LATENCY_TIMER, 0x20);   /* latency timer = 32 clocks */
511ae543b49SStefan Weil     /* Capability Pointer is set by PCI framework. */
512f62719caSStefan Weil     /* Interrupt Line */
513f62719caSStefan Weil     /* Interrupt Pin */
514f62719caSStefan Weil     pci_set_byte(pci_conf + PCI_INTERRUPT_PIN, 1);      /* interrupt pin A */
515663e8e51Sths     /* Minimum Grant */
51615e89f59SMichael S. Tsirkin     pci_set_byte(pci_conf + PCI_MIN_GNT, 0x08);
517663e8e51Sths     /* Maximum Latency */
51815e89f59SMichael S. Tsirkin     pci_set_byte(pci_conf + PCI_MAX_LAT, 0x18);
519663e8e51Sths 
52040021f08SAnthony Liguori     s->stats_size = info->stats_size;
52140021f08SAnthony Liguori     s->has_extended_tcb_support = info->has_extended_tcb_support;
522558c8634SStefan Weil 
523663e8e51Sths     switch (device) {
524ba42b646SStefan Weil     case i82550:
525663e8e51Sths     case i82551:
526ba42b646SStefan Weil     case i82557A:
527663e8e51Sths     case i82557B:
528663e8e51Sths     case i82557C:
529ba42b646SStefan Weil     case i82558A:
530663e8e51Sths     case i82558B:
531ba42b646SStefan Weil     case i82559A:
532ba42b646SStefan Weil     case i82559B:
533558c8634SStefan Weil     case i82559ER:
534558c8634SStefan Weil     case i82562:
535db667a12SStefan Weil     case i82801:
536663e8e51Sths     case i82559C:
537ba42b646SStefan Weil         break;
538663e8e51Sths     default:
539663e8e51Sths         logout("Device %X is undefined!\n", device);
540663e8e51Sths     }
541663e8e51Sths 
5423dec59a1SStefan Weil     /* Standard TxCB. */
5433dec59a1SStefan Weil     s->configuration[6] |= BIT(4);
5443dec59a1SStefan Weil 
545558c8634SStefan Weil     /* Standard statistical counters. */
546ba42b646SStefan Weil     s->configuration[6] |= BIT(5);
547ba42b646SStefan Weil 
548ba42b646SStefan Weil     if (s->stats_size == 80) {
549ba42b646SStefan Weil         /* TODO: check TCO Statistical Counters bit. Documentation not clear. */
550ba42b646SStefan Weil         if (s->configuration[6] & BIT(2)) {
551ba42b646SStefan Weil             /* TCO statistical counters. */
552ba42b646SStefan Weil             assert(s->configuration[6] & BIT(5));
553ba42b646SStefan Weil         } else {
554ba42b646SStefan Weil             if (s->configuration[6] & BIT(5)) {
555ba42b646SStefan Weil                 /* No extended statistical counters, i82557 compatible. */
556ba42b646SStefan Weil                 s->stats_size = 64;
557ba42b646SStefan Weil             } else {
558ba42b646SStefan Weil                 /* i82558 compatible. */
559ba42b646SStefan Weil                 s->stats_size = 76;
560ba42b646SStefan Weil             }
561ba42b646SStefan Weil         }
562ba42b646SStefan Weil     } else {
563ba42b646SStefan Weil         if (s->configuration[6] & BIT(5)) {
564ba42b646SStefan Weil             /* No extended statistical counters. */
565ba42b646SStefan Weil             s->stats_size = 64;
566ba42b646SStefan Weil         }
567ba42b646SStefan Weil     }
568ba42b646SStefan Weil     assert(s->stats_size > 0 && s->stats_size <= sizeof(s->statistics));
569ba42b646SStefan Weil 
57040021f08SAnthony Liguori     if (info->power_management) {
571ba42b646SStefan Weil         /* Power Management Capabilities */
5728bbd1ce2SMichael S. Tsirkin         int cfg_offset = 0xdc;
573ca77089dSIsaku Yamahata         int r = pci_add_capability(&s->dev, PCI_CAP_ID_PM,
5749a7c2a59SMao Zhongyi                                    cfg_offset, PCI_PM_SIZEOF,
5759a7c2a59SMao Zhongyi                                    errp);
5769a7c2a59SMao Zhongyi         if (r < 0) {
5779a7c2a59SMao Zhongyi             return;
5789a7c2a59SMao Zhongyi         }
5799a7c2a59SMao Zhongyi 
580ae543b49SStefan Weil         pci_set_word(pci_conf + cfg_offset + PCI_PM_PMC, 0x7e21);
581ae543b49SStefan Weil #if 0 /* TODO: replace dummy code for power management emulation. */
582ba42b646SStefan Weil         /* TODO: Power Management Control / Status. */
583ae543b49SStefan Weil         pci_set_word(pci_conf + cfg_offset + PCI_PM_CTRL, 0x0000);
584ba42b646SStefan Weil         /* TODO: Ethernet Power Consumption Registers (i82559 and later). */
585ae543b49SStefan Weil         pci_set_byte(pci_conf + cfg_offset + PCI_PM_PPB_EXTENSIONS, 0x0000);
586ae543b49SStefan Weil #endif
587ae543b49SStefan Weil     }
588ba42b646SStefan Weil 
589ba42b646SStefan Weil #if EEPROM_SIZE > 0
590663e8e51Sths     if (device == i82557C || device == i82558B || device == i82559C) {
591e7493b25SStefan Weil         /*
592e7493b25SStefan Weil         TODO: get vendor id from EEPROM for i82557C or later.
593e7493b25SStefan Weil         TODO: get device id from EEPROM for i82557C or later.
594e7493b25SStefan Weil         TODO: status bit 4 can be disabled by EEPROM for i82558, i82559.
595e7493b25SStefan Weil         TODO: header type is determined by EEPROM for i82559.
596e7493b25SStefan Weil         TODO: get subsystem id from EEPROM for i82557C or later.
597e7493b25SStefan Weil         TODO: get subsystem vendor id from EEPROM for i82557C or later.
598e7493b25SStefan Weil         TODO: exp. rom baddr depends on a bit in EEPROM for i82558 or later.
599e7493b25SStefan Weil         TODO: capability pointer depends on EEPROM for i82558.
600e7493b25SStefan Weil         */
601663e8e51Sths         logout("Get device id and revision from EEPROM!!!\n");
602663e8e51Sths     }
603ba42b646SStefan Weil #endif /* EEPROM_SIZE > 0 */
604663e8e51Sths }
605663e8e51Sths 
606663e8e51Sths static void nic_selective_reset(EEPRO100State * s)
607663e8e51Sths {
608663e8e51Sths     size_t i;
609663e8e51Sths     uint16_t *eeprom_contents = eeprom93xx_data(s->eeprom);
610e7493b25SStefan Weil #if 0
611e7493b25SStefan Weil     eeprom93xx_reset(s->eeprom);
612e7493b25SStefan Weil #endif
613508ef936SGerd Hoffmann     memcpy(eeprom_contents, s->conf.macaddr.a, 6);
614b1e87018SStefan Weil     eeprom_contents[EEPROM_ID] = EEPROM_ID_VALID;
615f4e94dfeS=?UTF-8?q?Reimar=20D=C3=B6ffinger?=     if (s->device == i82557B || s->device == i82557C)
616f4e94dfeS=?UTF-8?q?Reimar=20D=C3=B6ffinger?=         eeprom_contents[5] = 0x0100;
6176cded3a4SStefan Weil     eeprom_contents[EEPROM_PHY_ID] = 1;
618663e8e51Sths     uint16_t sum = 0;
619663e8e51Sths     for (i = 0; i < EEPROM_SIZE - 1; i++) {
620663e8e51Sths         sum += eeprom_contents[i];
621663e8e51Sths     }
622663e8e51Sths     eeprom_contents[EEPROM_SIZE - 1] = 0xbaba - sum;
623aac443e6SStefan Weil     TRACE(EEPROM, logout("checksum=0x%04x\n", eeprom_contents[EEPROM_SIZE - 1]));
624663e8e51Sths 
625663e8e51Sths     memset(s->mem, 0, sizeof(s->mem));
626e5e23ab8SStefan Weil     e100_write_reg4(s, SCBCtrlMDI, BIT(21));
627663e8e51Sths 
628663e8e51Sths     assert(sizeof(s->mdimem) == sizeof(eepro100_mdi_default));
629663e8e51Sths     memcpy(&s->mdimem[0], &eepro100_mdi_default[0], sizeof(s->mdimem));
630663e8e51Sths }
631663e8e51Sths 
632663e8e51Sths static void nic_reset(void *opaque)
633663e8e51Sths {
634769cf7a5SJuan Quintela     EEPRO100State *s = opaque;
635aac443e6SStefan Weil     TRACE(OTHER, logout("%p\n", s));
636010ec629SStefan Weil     /* TODO: Clearing of hash register for selective reset, too? */
6377b8737deSStefan Weil     memset(&s->mult[0], 0, sizeof(s->mult));
638663e8e51Sths     nic_selective_reset(s);
639663e8e51Sths }
640663e8e51Sths 
641663e8e51Sths #if defined(DEBUG_EEPRO100)
642b8f6ba0dSStefan Weil static const char * const e100_reg[PCI_IO_SIZE / 4] = {
643663e8e51Sths     "Command/Status",
644663e8e51Sths     "General Pointer",
645663e8e51Sths     "Port",
646663e8e51Sths     "EEPROM/Flash Control",
647663e8e51Sths     "MDI Control",
648663e8e51Sths     "Receive DMA Byte Count",
649b8f6ba0dSStefan Weil     "Flow Control",
650663e8e51Sths     "General Status/Control"
651663e8e51Sths };
652663e8e51Sths 
653663e8e51Sths static char *regname(uint32_t addr)
654663e8e51Sths {
655ec169288SDavid Benjamin     static char buf[32];
656663e8e51Sths     if (addr < PCI_IO_SIZE) {
657b8f6ba0dSStefan Weil         const char *r = e100_reg[addr / 4];
658663e8e51Sths         if (r != 0) {
65941cbc23cSStefan Weil             snprintf(buf, sizeof(buf), "%s+%u", r, addr % 4);
660663e8e51Sths         } else {
66141cbc23cSStefan Weil             snprintf(buf, sizeof(buf), "0x%02x", addr);
662663e8e51Sths         }
663663e8e51Sths     } else {
66441cbc23cSStefan Weil         snprintf(buf, sizeof(buf), "??? 0x%08x", addr);
665663e8e51Sths     }
666663e8e51Sths     return buf;
667663e8e51Sths }
668663e8e51Sths #endif                          /* DEBUG_EEPRO100 */
669663e8e51Sths 
670663e8e51Sths /*****************************************************************************
671663e8e51Sths  *
672663e8e51Sths  * Command emulation.
673663e8e51Sths  *
674663e8e51Sths  ****************************************************************************/
675663e8e51Sths 
676663e8e51Sths #if 0
677663e8e51Sths static uint16_t eepro100_read_command(EEPRO100State * s)
678663e8e51Sths {
679663e8e51Sths     uint16_t val = 0xffff;
680e7493b25SStefan Weil     TRACE(OTHER, logout("val=0x%04x\n", val));
681663e8e51Sths     return val;
682663e8e51Sths }
683663e8e51Sths #endif
684663e8e51Sths 
685663e8e51Sths /* Commands that can be put in a command list entry. */
686663e8e51Sths enum commands {
687663e8e51Sths     CmdNOp = 0,
688663e8e51Sths     CmdIASetup = 1,
689663e8e51Sths     CmdConfigure = 2,
690663e8e51Sths     CmdMulticastList = 3,
691663e8e51Sths     CmdTx = 4,
692663e8e51Sths     CmdTDR = 5,                 /* load microcode */
693663e8e51Sths     CmdDump = 6,
694663e8e51Sths     CmdDiagnose = 7,
695663e8e51Sths 
696663e8e51Sths     /* And some extra flags: */
697663e8e51Sths     CmdSuspend = 0x4000,        /* Suspend after completion. */
698663e8e51Sths     CmdIntr = 0x2000,           /* Interrupt after completion. */
699663e8e51Sths     CmdTxFlex = 0x0008,         /* Use "Flexible mode" for CmdTx command. */
700663e8e51Sths };
701663e8e51Sths 
702c227f099SAnthony Liguori static cu_state_t get_cu_state(EEPRO100State * s)
703663e8e51Sths {
704ced5296aSStefan Weil     return ((s->mem[SCBStatus] & BITS(7, 6)) >> 6);
705663e8e51Sths }
706663e8e51Sths 
707c227f099SAnthony Liguori static void set_cu_state(EEPRO100State * s, cu_state_t state)
708663e8e51Sths {
709ced5296aSStefan Weil     s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(7, 6)) + (state << 6);
710663e8e51Sths }
711663e8e51Sths 
712c227f099SAnthony Liguori static ru_state_t get_ru_state(EEPRO100State * s)
713663e8e51Sths {
714ced5296aSStefan Weil     return ((s->mem[SCBStatus] & BITS(5, 2)) >> 2);
715663e8e51Sths }
716663e8e51Sths 
717c227f099SAnthony Liguori static void set_ru_state(EEPRO100State * s, ru_state_t state)
718663e8e51Sths {
719ced5296aSStefan Weil     s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(5, 2)) + (state << 2);
720663e8e51Sths }
721663e8e51Sths 
722663e8e51Sths static void dump_statistics(EEPRO100State * s)
723663e8e51Sths {
724663e8e51Sths     /* Dump statistical data. Most data is never changed by the emulation
725663e8e51Sths      * and always 0, so we first just copy the whole block and then those
726663e8e51Sths      * values which really matter.
727663e8e51Sths      * Number of data should check configuration!!!
728663e8e51Sths      */
729e965d4bcSDavid Gibson     pci_dma_write(&s->dev, s->statsaddr, &s->statistics, s->stats_size);
73016ef60c9SEduard - Gabriel Munteanu     stl_le_pci_dma(&s->dev, s->statsaddr + 0,
73116ef60c9SEduard - Gabriel Munteanu                    s->statistics.tx_good_frames);
73216ef60c9SEduard - Gabriel Munteanu     stl_le_pci_dma(&s->dev, s->statsaddr + 36,
73316ef60c9SEduard - Gabriel Munteanu                    s->statistics.rx_good_frames);
73416ef60c9SEduard - Gabriel Munteanu     stl_le_pci_dma(&s->dev, s->statsaddr + 48,
73516ef60c9SEduard - Gabriel Munteanu                    s->statistics.rx_resource_errors);
73616ef60c9SEduard - Gabriel Munteanu     stl_le_pci_dma(&s->dev, s->statsaddr + 60,
73716ef60c9SEduard - Gabriel Munteanu                    s->statistics.rx_short_frame_errors);
738e7493b25SStefan Weil #if 0
73916ef60c9SEduard - Gabriel Munteanu     stw_le_pci_dma(&s->dev, s->statsaddr + 76, s->statistics.xmt_tco_frames);
74016ef60c9SEduard - Gabriel Munteanu     stw_le_pci_dma(&s->dev, s->statsaddr + 78, s->statistics.rcv_tco_frames);
741e7493b25SStefan Weil     missing("CU dump statistical counters");
742e7493b25SStefan Weil #endif
743663e8e51Sths }
744663e8e51Sths 
7453d0f4b9bSStefan Weil static void read_cb(EEPRO100State *s)
7463d0f4b9bSStefan Weil {
747e965d4bcSDavid Gibson     pci_dma_read(&s->dev, s->cb_address, &s->tx, sizeof(s->tx));
7483d0f4b9bSStefan Weil     s->tx.status = le16_to_cpu(s->tx.status);
7493d0f4b9bSStefan Weil     s->tx.command = le16_to_cpu(s->tx.command);
7503d0f4b9bSStefan Weil     s->tx.link = le32_to_cpu(s->tx.link);
7513d0f4b9bSStefan Weil     s->tx.tbd_array_addr = le32_to_cpu(s->tx.tbd_array_addr);
7523d0f4b9bSStefan Weil     s->tx.tcb_bytes = le16_to_cpu(s->tx.tcb_bytes);
7533d0f4b9bSStefan Weil }
7543d0f4b9bSStefan Weil 
755f3a52e50SStefan Weil static void tx_command(EEPRO100State *s)
756663e8e51Sths {
7577b8737deSStefan Weil     uint32_t tbd_array = le32_to_cpu(s->tx.tbd_array_addr);
758f3a52e50SStefan Weil     uint16_t tcb_bytes = (le16_to_cpu(s->tx.tcb_bytes) & 0x3fff);
759f3a52e50SStefan Weil     /* Sends larger than MAX_ETH_FRAME_SIZE are allowed, up to 2600 bytes. */
760f3a52e50SStefan Weil     uint8_t buf[2600];
761f3a52e50SStefan Weil     uint16_t size = 0;
762f3a52e50SStefan Weil     uint32_t tbd_address = s->cb_address + 0x10;
763aac443e6SStefan Weil     TRACE(RXTX, logout
764663e8e51Sths         ("transmit, TBD array address 0x%08x, TCB byte count 0x%04x, TBD count %u\n",
765f3a52e50SStefan Weil          tbd_array, tcb_bytes, s->tx.tbd_count));
7667f1e9d4eSKevin Wolf 
7677f1e9d4eSKevin Wolf     if (tcb_bytes > 2600) {
7687f1e9d4eSKevin Wolf         logout("TCB byte count too large, using 2600\n");
7697f1e9d4eSKevin Wolf         tcb_bytes = 2600;
7707f1e9d4eSKevin Wolf     }
771663e8e51Sths     if (!((tcb_bytes > 0) || (tbd_array != 0xffffffff))) {
772663e8e51Sths         logout
773663e8e51Sths             ("illegal values of TBD array address and TCB byte count!\n");
774663e8e51Sths     }
775663e8e51Sths     assert(tcb_bytes <= sizeof(buf));
776663e8e51Sths     while (size < tcb_bytes) {
77716ef60c9SEduard - Gabriel Munteanu         uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev, tbd_address);
77816ef60c9SEduard - Gabriel Munteanu         uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev, tbd_address + 4);
779e7493b25SStefan Weil #if 0
78016ef60c9SEduard - Gabriel Munteanu         uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev, tbd_address + 6);
781e7493b25SStefan Weil #endif
78200837731SStefan Weil         if (tx_buffer_size == 0) {
78300837731SStefan Weil             /* Prevent an endless loop. */
78400837731SStefan Weil             logout("loop in %s:%u\n", __FILE__, __LINE__);
78500837731SStefan Weil             break;
78600837731SStefan Weil         }
787663e8e51Sths         tbd_address += 8;
788aac443e6SStefan Weil         TRACE(RXTX, logout
789663e8e51Sths             ("TBD (simplified mode): buffer address 0x%08x, size 0x%04x\n",
790aac443e6SStefan Weil              tx_buffer_address, tx_buffer_size));
79124e6f355SReimar Döffinger         tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size);
79216ef60c9SEduard - Gabriel Munteanu         pci_dma_read(&s->dev, tx_buffer_address, &buf[size], tx_buffer_size);
793663e8e51Sths         size += tx_buffer_size;
794663e8e51Sths     }
795663e8e51Sths     if (tbd_array == 0xffffffff) {
796663e8e51Sths         /* Simplified mode. Was already handled by code above. */
797663e8e51Sths     } else {
798663e8e51Sths         /* Flexible mode. */
799663e8e51Sths         uint8_t tbd_count = 0;
800ba42b646SStefan Weil         if (s->has_extended_tcb_support && !(s->configuration[6] & BIT(4))) {
8013f9cb1c1SNaphtali Sprei             /* Extended Flexible TCB. */
802663e8e51Sths             for (; tbd_count < 2; tbd_count++) {
80316ef60c9SEduard - Gabriel Munteanu                 uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev,
80416ef60c9SEduard - Gabriel Munteanu                                                             tbd_address);
80516ef60c9SEduard - Gabriel Munteanu                 uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev,
80616ef60c9SEduard - Gabriel Munteanu                                                           tbd_address + 4);
80716ef60c9SEduard - Gabriel Munteanu                 uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev,
80816ef60c9SEduard - Gabriel Munteanu                                                         tbd_address + 6);
809663e8e51Sths                 tbd_address += 8;
810aac443e6SStefan Weil                 TRACE(RXTX, logout
8113f9cb1c1SNaphtali Sprei                     ("TBD (extended flexible mode): buffer address 0x%08x, size 0x%04x\n",
812aac443e6SStefan Weil                      tx_buffer_address, tx_buffer_size));
81324e6f355SReimar Döffinger                 tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size);
81416ef60c9SEduard - Gabriel Munteanu                 pci_dma_read(&s->dev, tx_buffer_address,
81516ef60c9SEduard - Gabriel Munteanu                              &buf[size], tx_buffer_size);
816663e8e51Sths                 size += tx_buffer_size;
817663e8e51Sths                 if (tx_buffer_el & 1) {
818663e8e51Sths                     break;
819663e8e51Sths                 }
820663e8e51Sths             }
821663e8e51Sths         }
822663e8e51Sths         tbd_address = tbd_array;
823f3a52e50SStefan Weil         for (; tbd_count < s->tx.tbd_count; tbd_count++) {
82416ef60c9SEduard - Gabriel Munteanu             uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev, tbd_address);
82516ef60c9SEduard - Gabriel Munteanu             uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev, tbd_address + 4);
82616ef60c9SEduard - Gabriel Munteanu             uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev, tbd_address + 6);
827663e8e51Sths             tbd_address += 8;
828aac443e6SStefan Weil             TRACE(RXTX, logout
829663e8e51Sths                 ("TBD (flexible mode): buffer address 0x%08x, size 0x%04x\n",
830aac443e6SStefan Weil                  tx_buffer_address, tx_buffer_size));
83124e6f355SReimar Döffinger             tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size);
83216ef60c9SEduard - Gabriel Munteanu             pci_dma_read(&s->dev, tx_buffer_address,
83316ef60c9SEduard - Gabriel Munteanu                          &buf[size], tx_buffer_size);
834663e8e51Sths             size += tx_buffer_size;
835663e8e51Sths             if (tx_buffer_el & 1) {
836663e8e51Sths                 break;
837663e8e51Sths             }
838663e8e51Sths         }
839663e8e51Sths     }
840aac443e6SStefan Weil     TRACE(RXTX, logout("%p sending frame, len=%d,%s\n", s, size, nic_dump(buf, size)));
841b356f76dSJason Wang     qemu_send_packet(qemu_get_queue(s->nic), buf, size);
842663e8e51Sths     s->statistics.tx_good_frames++;
843663e8e51Sths     /* Transmit with bad status would raise an CX/TNO interrupt.
844663e8e51Sths      * (82557 only). Emulation never has bad status. */
845e7493b25SStefan Weil #if 0
846e7493b25SStefan Weil     eepro100_cx_interrupt(s);
847e7493b25SStefan Weil #endif
848f3a52e50SStefan Weil }
849f3a52e50SStefan Weil 
8507b8737deSStefan Weil static void set_multicast_list(EEPRO100State *s)
8517b8737deSStefan Weil {
8527b8737deSStefan Weil     uint16_t multicast_count = s->tx.tbd_array_addr & BITS(13, 0);
8537b8737deSStefan Weil     uint16_t i;
8547b8737deSStefan Weil     memset(&s->mult[0], 0, sizeof(s->mult));
8557b8737deSStefan Weil     TRACE(OTHER, logout("multicast list, multicast count = %u\n", multicast_count));
8567b8737deSStefan Weil     for (i = 0; i < multicast_count; i += 6) {
8577b8737deSStefan Weil         uint8_t multicast_addr[6];
85816ef60c9SEduard - Gabriel Munteanu         pci_dma_read(&s->dev, s->cb_address + 10 + i, multicast_addr, 6);
8597b8737deSStefan Weil         TRACE(OTHER, logout("multicast entry %s\n", nic_dump(multicast_addr, 6)));
86069f3ce78SStefan Weil         unsigned mcast_idx = e100_compute_mcast_idx(multicast_addr);
8617b8737deSStefan Weil         assert(mcast_idx < 64);
8627b8737deSStefan Weil         s->mult[mcast_idx >> 3] |= (1 << (mcast_idx & 7));
8637b8737deSStefan Weil     }
8647b8737deSStefan Weil }
8657b8737deSStefan Weil 
866f3a52e50SStefan Weil static void action_command(EEPRO100State *s)
867f3a52e50SStefan Weil {
86800837731SStefan Weil     /* The loop below won't stop if it gets special handcrafted data.
86900837731SStefan Weil        Therefore we limit the number of iterations. */
87000837731SStefan Weil     unsigned max_loop_count = 16;
87100837731SStefan Weil 
872f3a52e50SStefan Weil     for (;;) {
8733d0f4b9bSStefan Weil         bool bit_el;
8743d0f4b9bSStefan Weil         bool bit_s;
8753d0f4b9bSStefan Weil         bool bit_i;
8763d0f4b9bSStefan Weil         bool bit_nc;
87775f5a6ccSStefan Weil         uint16_t ok_status = STATUS_OK;
8783d0f4b9bSStefan Weil         s->cb_address = s->cu_base + s->cu_offset;
8793d0f4b9bSStefan Weil         read_cb(s);
8803d0f4b9bSStefan Weil         bit_el = ((s->tx.command & COMMAND_EL) != 0);
8813d0f4b9bSStefan Weil         bit_s = ((s->tx.command & COMMAND_S) != 0);
8823d0f4b9bSStefan Weil         bit_i = ((s->tx.command & COMMAND_I) != 0);
8833d0f4b9bSStefan Weil         bit_nc = ((s->tx.command & COMMAND_NC) != 0);
8843d0f4b9bSStefan Weil #if 0
8853d0f4b9bSStefan Weil         bool bit_sf = ((s->tx.command & COMMAND_SF) != 0);
8863d0f4b9bSStefan Weil #endif
88700837731SStefan Weil 
88800837731SStefan Weil         if (max_loop_count-- == 0) {
88900837731SStefan Weil             /* Prevent an endless loop. */
89000837731SStefan Weil             logout("loop in %s:%u\n", __FILE__, __LINE__);
89100837731SStefan Weil             break;
89200837731SStefan Weil         }
89300837731SStefan Weil 
8943d0f4b9bSStefan Weil         s->cu_offset = s->tx.link;
8953d0f4b9bSStefan Weil         TRACE(OTHER,
8963d0f4b9bSStefan Weil               logout("val=(cu start), status=0x%04x, command=0x%04x, link=0x%08x\n",
8973d0f4b9bSStefan Weil                      s->tx.status, s->tx.command, s->tx.link));
8983d0f4b9bSStefan Weil         switch (s->tx.command & COMMAND_CMD) {
899f3a52e50SStefan Weil         case CmdNOp:
900f3a52e50SStefan Weil             /* Do nothing. */
901f3a52e50SStefan Weil             break;
902f3a52e50SStefan Weil         case CmdIASetup:
90316ef60c9SEduard - Gabriel Munteanu             pci_dma_read(&s->dev, s->cb_address + 8, &s->conf.macaddr.a[0], 6);
904ce0e58b3SStefan Weil             TRACE(OTHER, logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6)));
905f3a52e50SStefan Weil             break;
906f3a52e50SStefan Weil         case CmdConfigure:
90716ef60c9SEduard - Gabriel Munteanu             pci_dma_read(&s->dev, s->cb_address + 8,
90816ef60c9SEduard - Gabriel Munteanu                          &s->configuration[0], sizeof(s->configuration));
909010ec629SStefan Weil             TRACE(OTHER, logout("configuration: %s\n",
910010ec629SStefan Weil                                 nic_dump(&s->configuration[0], 16)));
911010ec629SStefan Weil             TRACE(OTHER, logout("configuration: %s\n",
912010ec629SStefan Weil                                 nic_dump(&s->configuration[16],
913010ec629SStefan Weil                                 ARRAY_SIZE(s->configuration) - 16)));
914010ec629SStefan Weil             if (s->configuration[20] & BIT(6)) {
915010ec629SStefan Weil                 TRACE(OTHER, logout("Multiple IA bit\n"));
916010ec629SStefan Weil             }
917f3a52e50SStefan Weil             break;
918f3a52e50SStefan Weil         case CmdMulticastList:
9197b8737deSStefan Weil             set_multicast_list(s);
920f3a52e50SStefan Weil             break;
921f3a52e50SStefan Weil         case CmdTx:
922f3a52e50SStefan Weil             if (bit_nc) {
923f3a52e50SStefan Weil                 missing("CmdTx: NC = 0");
92475f5a6ccSStefan Weil                 ok_status = 0;
925f3a52e50SStefan Weil                 break;
926f3a52e50SStefan Weil             }
927f3a52e50SStefan Weil             tx_command(s);
928663e8e51Sths             break;
929663e8e51Sths         case CmdTDR:
930aac443e6SStefan Weil             TRACE(OTHER, logout("load microcode\n"));
931663e8e51Sths             /* Starting with offset 8, the command contains
932663e8e51Sths              * 64 dwords microcode which we just ignore here. */
933663e8e51Sths             break;
934f80a7fc3SStefan Weil         case CmdDiagnose:
935f80a7fc3SStefan Weil             TRACE(OTHER, logout("diagnose\n"));
936f80a7fc3SStefan Weil             /* Make sure error flag is not set. */
937f80a7fc3SStefan Weil             s->tx.status = 0;
938f80a7fc3SStefan Weil             break;
939663e8e51Sths         default:
940663e8e51Sths             missing("undefined command");
94175f5a6ccSStefan Weil             ok_status = 0;
9427f1e9d4eSKevin Wolf             break;
943663e8e51Sths         }
9447f1e9d4eSKevin Wolf         /* Write new status. */
94516ef60c9SEduard - Gabriel Munteanu         stw_le_pci_dma(&s->dev, s->cb_address,
94616ef60c9SEduard - Gabriel Munteanu                        s->tx.status | ok_status | STATUS_C);
947663e8e51Sths         if (bit_i) {
948663e8e51Sths             /* CU completed action. */
949663e8e51Sths             eepro100_cx_interrupt(s);
950663e8e51Sths         }
951663e8e51Sths         if (bit_el) {
952aac443e6SStefan Weil             /* CU becomes idle. Terminate command loop. */
953663e8e51Sths             set_cu_state(s, cu_idle);
954663e8e51Sths             eepro100_cna_interrupt(s);
9555fa9a0aeSStefan Weil             break;
956663e8e51Sths         } else if (bit_s) {
9575fa9a0aeSStefan Weil             /* CU becomes suspended. Terminate command loop. */
958663e8e51Sths             set_cu_state(s, cu_suspended);
959663e8e51Sths             eepro100_cna_interrupt(s);
9605fa9a0aeSStefan Weil             break;
961663e8e51Sths         } else {
962663e8e51Sths             /* More entries in list. */
963aac443e6SStefan Weil             TRACE(OTHER, logout("CU list with at least one more entry\n"));
9645fa9a0aeSStefan Weil         }
965663e8e51Sths     }
966aac443e6SStefan Weil     TRACE(OTHER, logout("CU list empty\n"));
967663e8e51Sths     /* List is empty. Now CU is idle or suspended. */
9685fa9a0aeSStefan Weil }
9695fa9a0aeSStefan Weil 
9705fa9a0aeSStefan Weil static void eepro100_cu_command(EEPRO100State * s, uint8_t val)
9715fa9a0aeSStefan Weil {
972cb25a3fbSStefan Weil     cu_state_t cu_state;
9735fa9a0aeSStefan Weil     switch (val) {
9745fa9a0aeSStefan Weil     case CU_NOP:
9755fa9a0aeSStefan Weil         /* No operation. */
9765fa9a0aeSStefan Weil         break;
9775fa9a0aeSStefan Weil     case CU_START:
978cb25a3fbSStefan Weil         cu_state = get_cu_state(s);
979cb25a3fbSStefan Weil         if (cu_state != cu_idle && cu_state != cu_suspended) {
980cb25a3fbSStefan Weil             /* Intel documentation says that CU must be idle or suspended
981cb25a3fbSStefan Weil              * for the CU start command. */
982cb25a3fbSStefan Weil             logout("unexpected CU state is %u\n", cu_state);
9835fa9a0aeSStefan Weil         }
9845fa9a0aeSStefan Weil         set_cu_state(s, cu_active);
98527a05006SStefan Weil         s->cu_offset = e100_read_reg4(s, SCBPointer);
9865fa9a0aeSStefan Weil         action_command(s);
987663e8e51Sths         break;
988663e8e51Sths     case CU_RESUME:
989663e8e51Sths         if (get_cu_state(s) != cu_suspended) {
990663e8e51Sths             logout("bad CU resume from CU state %u\n", get_cu_state(s));
991663e8e51Sths             /* Workaround for bad Linux eepro100 driver which resumes
992663e8e51Sths              * from idle state. */
993e7493b25SStefan Weil #if 0
994e7493b25SStefan Weil             missing("cu resume");
995e7493b25SStefan Weil #endif
996663e8e51Sths             set_cu_state(s, cu_suspended);
997663e8e51Sths         }
998663e8e51Sths         if (get_cu_state(s) == cu_suspended) {
999aac443e6SStefan Weil             TRACE(OTHER, logout("CU resuming\n"));
1000663e8e51Sths             set_cu_state(s, cu_active);
10015fa9a0aeSStefan Weil             action_command(s);
1002663e8e51Sths         }
1003663e8e51Sths         break;
1004663e8e51Sths     case CU_STATSADDR:
1005663e8e51Sths         /* Load dump counters address. */
100627a05006SStefan Weil         s->statsaddr = e100_read_reg4(s, SCBPointer);
1007c16ada98SStefan Weil         TRACE(OTHER, logout("val=0x%02x (dump counters address)\n", val));
1008c16ada98SStefan Weil         if (s->statsaddr & 3) {
1009c16ada98SStefan Weil             /* Memory must be Dword aligned. */
1010c16ada98SStefan Weil             logout("unaligned dump counters address\n");
1011c16ada98SStefan Weil             /* Handling of misaligned addresses is undefined.
1012c16ada98SStefan Weil              * Here we align the address by ignoring the lower bits. */
1013c16ada98SStefan Weil             /* TODO: Test unaligned dump counter address on real hardware. */
1014c16ada98SStefan Weil             s->statsaddr &= ~3;
1015c16ada98SStefan Weil         }
1016663e8e51Sths         break;
1017663e8e51Sths     case CU_SHOWSTATS:
1018663e8e51Sths         /* Dump statistical counters. */
1019aac443e6SStefan Weil         TRACE(OTHER, logout("val=0x%02x (dump stats)\n", val));
1020663e8e51Sths         dump_statistics(s);
102116ef60c9SEduard - Gabriel Munteanu         stl_le_pci_dma(&s->dev, s->statsaddr + s->stats_size, 0xa005);
1022663e8e51Sths         break;
1023663e8e51Sths     case CU_CMD_BASE:
1024663e8e51Sths         /* Load CU base. */
1025aac443e6SStefan Weil         TRACE(OTHER, logout("val=0x%02x (CU base address)\n", val));
102627a05006SStefan Weil         s->cu_base = e100_read_reg4(s, SCBPointer);
1027663e8e51Sths         break;
1028663e8e51Sths     case CU_DUMPSTATS:
1029663e8e51Sths         /* Dump and reset statistical counters. */
1030aac443e6SStefan Weil         TRACE(OTHER, logout("val=0x%02x (dump stats and reset)\n", val));
1031663e8e51Sths         dump_statistics(s);
103216ef60c9SEduard - Gabriel Munteanu         stl_le_pci_dma(&s->dev, s->statsaddr + s->stats_size, 0xa007);
1033663e8e51Sths         memset(&s->statistics, 0, sizeof(s->statistics));
1034663e8e51Sths         break;
1035663e8e51Sths     case CU_SRESUME:
1036663e8e51Sths         /* CU static resume. */
1037663e8e51Sths         missing("CU static resume");
1038663e8e51Sths         break;
1039663e8e51Sths     default:
1040663e8e51Sths         missing("Undefined CU command");
1041663e8e51Sths     }
1042663e8e51Sths }
1043663e8e51Sths 
1044663e8e51Sths static void eepro100_ru_command(EEPRO100State * s, uint8_t val)
1045663e8e51Sths {
1046663e8e51Sths     switch (val) {
1047663e8e51Sths     case RU_NOP:
1048663e8e51Sths         /* No operation. */
1049663e8e51Sths         break;
1050663e8e51Sths     case RX_START:
1051663e8e51Sths         /* RU start. */
1052663e8e51Sths         if (get_ru_state(s) != ru_idle) {
1053663e8e51Sths             logout("RU state is %u, should be %u\n", get_ru_state(s), ru_idle);
1054e7493b25SStefan Weil #if 0
1055e7493b25SStefan Weil             assert(!"wrong RU state");
1056e7493b25SStefan Weil #endif
1057663e8e51Sths         }
1058663e8e51Sths         set_ru_state(s, ru_ready);
105927a05006SStefan Weil         s->ru_offset = e100_read_reg4(s, SCBPointer);
1060b356f76dSJason Wang         qemu_flush_queued_packets(qemu_get_queue(s->nic));
1061aac443e6SStefan Weil         TRACE(OTHER, logout("val=0x%02x (rx start)\n", val));
1062663e8e51Sths         break;
1063663e8e51Sths     case RX_RESUME:
1064663e8e51Sths         /* Restart RU. */
1065663e8e51Sths         if (get_ru_state(s) != ru_suspended) {
1066663e8e51Sths             logout("RU state is %u, should be %u\n", get_ru_state(s),
1067663e8e51Sths                    ru_suspended);
1068e7493b25SStefan Weil #if 0
1069e7493b25SStefan Weil             assert(!"wrong RU state");
1070e7493b25SStefan Weil #endif
1071663e8e51Sths         }
1072663e8e51Sths         set_ru_state(s, ru_ready);
1073663e8e51Sths         break;
1074e824012bSStefan Weil     case RU_ABORT:
1075e824012bSStefan Weil         /* RU abort. */
1076e824012bSStefan Weil         if (get_ru_state(s) == ru_ready) {
1077e824012bSStefan Weil             eepro100_rnr_interrupt(s);
1078e824012bSStefan Weil         }
1079e824012bSStefan Weil         set_ru_state(s, ru_idle);
1080e824012bSStefan Weil         break;
1081663e8e51Sths     case RX_ADDR_LOAD:
1082663e8e51Sths         /* Load RU base. */
1083aac443e6SStefan Weil         TRACE(OTHER, logout("val=0x%02x (RU base address)\n", val));
108427a05006SStefan Weil         s->ru_base = e100_read_reg4(s, SCBPointer);
1085663e8e51Sths         break;
1086663e8e51Sths     default:
1087663e8e51Sths         logout("val=0x%02x (undefined RU command)\n", val);
1088663e8e51Sths         missing("Undefined SU command");
1089663e8e51Sths     }
1090663e8e51Sths }
1091663e8e51Sths 
1092663e8e51Sths static void eepro100_write_command(EEPRO100State * s, uint8_t val)
1093663e8e51Sths {
1094663e8e51Sths     eepro100_ru_command(s, val & 0x0f);
1095663e8e51Sths     eepro100_cu_command(s, val & 0xf0);
1096663e8e51Sths     if ((val) == 0) {
1097aac443e6SStefan Weil         TRACE(OTHER, logout("val=0x%02x\n", val));
1098663e8e51Sths     }
1099663e8e51Sths     /* Clear command byte after command was accepted. */
1100663e8e51Sths     s->mem[SCBCmd] = 0;
1101663e8e51Sths }
1102663e8e51Sths 
1103663e8e51Sths /*****************************************************************************
1104663e8e51Sths  *
1105663e8e51Sths  * EEPROM emulation.
1106663e8e51Sths  *
1107663e8e51Sths  ****************************************************************************/
1108663e8e51Sths 
1109663e8e51Sths #define EEPROM_CS       0x02
1110663e8e51Sths #define EEPROM_SK       0x01
1111663e8e51Sths #define EEPROM_DI       0x04
1112663e8e51Sths #define EEPROM_DO       0x08
1113663e8e51Sths 
1114663e8e51Sths static uint16_t eepro100_read_eeprom(EEPRO100State * s)
1115663e8e51Sths {
1116e5e23ab8SStefan Weil     uint16_t val = e100_read_reg2(s, SCBeeprom);
1117663e8e51Sths     if (eeprom93xx_read(s->eeprom)) {
1118663e8e51Sths         val |= EEPROM_DO;
1119663e8e51Sths     } else {
1120663e8e51Sths         val &= ~EEPROM_DO;
1121663e8e51Sths     }
1122aac443e6SStefan Weil     TRACE(EEPROM, logout("val=0x%04x\n", val));
1123663e8e51Sths     return val;
1124663e8e51Sths }
1125663e8e51Sths 
1126c227f099SAnthony Liguori static void eepro100_write_eeprom(eeprom_t * eeprom, uint8_t val)
1127663e8e51Sths {
1128aac443e6SStefan Weil     TRACE(EEPROM, logout("val=0x%02x\n", val));
1129663e8e51Sths 
1130ebabb67aSStefan Weil     /* mask unwritable bits */
1131e7493b25SStefan Weil #if 0
1132e7493b25SStefan Weil     val = SET_MASKED(val, 0x31, eeprom->value);
1133e7493b25SStefan Weil #endif
1134663e8e51Sths 
1135663e8e51Sths     int eecs = ((val & EEPROM_CS) != 0);
1136663e8e51Sths     int eesk = ((val & EEPROM_SK) != 0);
1137663e8e51Sths     int eedi = ((val & EEPROM_DI) != 0);
1138663e8e51Sths     eeprom93xx_write(eeprom, eecs, eesk, eedi);
1139663e8e51Sths }
1140663e8e51Sths 
1141663e8e51Sths /*****************************************************************************
1142663e8e51Sths  *
1143663e8e51Sths  * MDI emulation.
1144663e8e51Sths  *
1145663e8e51Sths  ****************************************************************************/
1146663e8e51Sths 
1147663e8e51Sths #if defined(DEBUG_EEPRO100)
11486a0b9cc9SReimar Döffinger static const char * const mdi_op_name[] = {
1149663e8e51Sths     "opcode 0",
1150663e8e51Sths     "write",
1151663e8e51Sths     "read",
1152663e8e51Sths     "opcode 3"
1153663e8e51Sths };
1154663e8e51Sths 
11556a0b9cc9SReimar Döffinger static const char * const mdi_reg_name[] = {
1156663e8e51Sths     "Control",
1157663e8e51Sths     "Status",
1158663e8e51Sths     "PHY Identification (Word 1)",
1159663e8e51Sths     "PHY Identification (Word 2)",
1160663e8e51Sths     "Auto-Negotiation Advertisement",
1161663e8e51Sths     "Auto-Negotiation Link Partner Ability",
1162663e8e51Sths     "Auto-Negotiation Expansion"
1163663e8e51Sths };
1164aac443e6SStefan Weil 
1165aac443e6SStefan Weil static const char *reg2name(uint8_t reg)
1166aac443e6SStefan Weil {
1167aac443e6SStefan Weil     static char buffer[10];
1168aac443e6SStefan Weil     const char *p = buffer;
1169aac443e6SStefan Weil     if (reg < ARRAY_SIZE(mdi_reg_name)) {
1170aac443e6SStefan Weil         p = mdi_reg_name[reg];
1171aac443e6SStefan Weil     } else {
1172aac443e6SStefan Weil         snprintf(buffer, sizeof(buffer), "reg=0x%02x", reg);
1173aac443e6SStefan Weil     }
1174aac443e6SStefan Weil     return p;
1175aac443e6SStefan Weil }
1176663e8e51Sths #endif                          /* DEBUG_EEPRO100 */
1177663e8e51Sths 
1178663e8e51Sths static uint32_t eepro100_read_mdi(EEPRO100State * s)
1179663e8e51Sths {
1180e5e23ab8SStefan Weil     uint32_t val = e100_read_reg4(s, SCBCtrlMDI);
1181663e8e51Sths 
1182663e8e51Sths #ifdef DEBUG_EEPRO100
1183663e8e51Sths     uint8_t raiseint = (val & BIT(29)) >> 29;
1184663e8e51Sths     uint8_t opcode = (val & BITS(27, 26)) >> 26;
1185663e8e51Sths     uint8_t phy = (val & BITS(25, 21)) >> 21;
1186663e8e51Sths     uint8_t reg = (val & BITS(20, 16)) >> 16;
1187663e8e51Sths     uint16_t data = (val & BITS(15, 0));
1188663e8e51Sths #endif
1189663e8e51Sths     /* Emulation takes no time to finish MDI transaction. */
1190663e8e51Sths     val |= BIT(28);
1191663e8e51Sths     TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
1192663e8e51Sths                       val, raiseint, mdi_op_name[opcode], phy,
1193aac443e6SStefan Weil                       reg2name(reg), data));
1194663e8e51Sths     return val;
1195663e8e51Sths }
1196663e8e51Sths 
11970113f48dSStefan Weil static void eepro100_write_mdi(EEPRO100State *s)
1198663e8e51Sths {
11990113f48dSStefan Weil     uint32_t val = e100_read_reg4(s, SCBCtrlMDI);
1200663e8e51Sths     uint8_t raiseint = (val & BIT(29)) >> 29;
1201663e8e51Sths     uint8_t opcode = (val & BITS(27, 26)) >> 26;
1202663e8e51Sths     uint8_t phy = (val & BITS(25, 21)) >> 21;
1203663e8e51Sths     uint8_t reg = (val & BITS(20, 16)) >> 16;
1204663e8e51Sths     uint16_t data = (val & BITS(15, 0));
1205aac443e6SStefan Weil     TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
1206aac443e6SStefan Weil           val, raiseint, mdi_op_name[opcode], phy, reg2name(reg), data));
1207663e8e51Sths     if (phy != 1) {
1208663e8e51Sths         /* Unsupported PHY address. */
1209e7493b25SStefan Weil #if 0
1210e7493b25SStefan Weil         logout("phy must be 1 but is %u\n", phy);
1211e7493b25SStefan Weil #endif
1212663e8e51Sths         data = 0;
1213663e8e51Sths     } else if (opcode != 1 && opcode != 2) {
1214663e8e51Sths         /* Unsupported opcode. */
1215663e8e51Sths         logout("opcode must be 1 or 2 but is %u\n", opcode);
1216663e8e51Sths         data = 0;
1217663e8e51Sths     } else if (reg > 6) {
1218663e8e51Sths         /* Unsupported register. */
1219663e8e51Sths         logout("register must be 0...6 but is %u\n", reg);
1220663e8e51Sths         data = 0;
1221663e8e51Sths     } else {
1222663e8e51Sths         TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
1223663e8e51Sths                           val, raiseint, mdi_op_name[opcode], phy,
1224aac443e6SStefan Weil                           reg2name(reg), data));
1225663e8e51Sths         if (opcode == 1) {
1226663e8e51Sths             /* MDI write */
1227663e8e51Sths             switch (reg) {
1228663e8e51Sths             case 0:            /* Control Register */
1229663e8e51Sths                 if (data & 0x8000) {
1230663e8e51Sths                     /* Reset status and control registers to default. */
1231663e8e51Sths                     s->mdimem[0] = eepro100_mdi_default[0];
1232663e8e51Sths                     s->mdimem[1] = eepro100_mdi_default[1];
1233663e8e51Sths                     data = s->mdimem[reg];
1234663e8e51Sths                 } else {
1235663e8e51Sths                     /* Restart Auto Configuration = Normal Operation */
1236663e8e51Sths                     data &= ~0x0200;
1237663e8e51Sths                 }
1238663e8e51Sths                 break;
1239663e8e51Sths             case 1:            /* Status Register */
1240663e8e51Sths                 missing("not writable");
1241663e8e51Sths                 break;
1242663e8e51Sths             case 2:            /* PHY Identification Register (Word 1) */
1243663e8e51Sths             case 3:            /* PHY Identification Register (Word 2) */
1244663e8e51Sths                 missing("not implemented");
1245663e8e51Sths                 break;
1246663e8e51Sths             case 4:            /* Auto-Negotiation Advertisement Register */
1247663e8e51Sths             case 5:            /* Auto-Negotiation Link Partner Ability Register */
1248663e8e51Sths                 break;
1249663e8e51Sths             case 6:            /* Auto-Negotiation Expansion Register */
1250663e8e51Sths             default:
1251663e8e51Sths                 missing("not implemented");
1252663e8e51Sths             }
12535e80dd22SPeter Maydell             s->mdimem[reg] &= eepro100_mdi_mask[reg];
12545e80dd22SPeter Maydell             s->mdimem[reg] |= data & ~eepro100_mdi_mask[reg];
1255663e8e51Sths         } else if (opcode == 2) {
1256663e8e51Sths             /* MDI read */
1257663e8e51Sths             switch (reg) {
1258663e8e51Sths             case 0:            /* Control Register */
1259663e8e51Sths                 if (data & 0x8000) {
1260663e8e51Sths                     /* Reset status and control registers to default. */
1261663e8e51Sths                     s->mdimem[0] = eepro100_mdi_default[0];
1262663e8e51Sths                     s->mdimem[1] = eepro100_mdi_default[1];
1263663e8e51Sths                 }
1264663e8e51Sths                 break;
1265663e8e51Sths             case 1:            /* Status Register */
1266663e8e51Sths                 s->mdimem[reg] |= 0x0020;
1267663e8e51Sths                 break;
1268663e8e51Sths             case 2:            /* PHY Identification Register (Word 1) */
1269663e8e51Sths             case 3:            /* PHY Identification Register (Word 2) */
1270663e8e51Sths             case 4:            /* Auto-Negotiation Advertisement Register */
1271663e8e51Sths                 break;
1272663e8e51Sths             case 5:            /* Auto-Negotiation Link Partner Ability Register */
1273663e8e51Sths                 s->mdimem[reg] = 0x41fe;
1274663e8e51Sths                 break;
1275663e8e51Sths             case 6:            /* Auto-Negotiation Expansion Register */
1276663e8e51Sths                 s->mdimem[reg] = 0x0001;
1277663e8e51Sths                 break;
1278663e8e51Sths             }
1279663e8e51Sths             data = s->mdimem[reg];
1280663e8e51Sths         }
1281663e8e51Sths         /* Emulation takes no time to finish MDI transaction.
1282663e8e51Sths          * Set MDI bit in SCB status register. */
1283663e8e51Sths         s->mem[SCBAck] |= 0x08;
1284663e8e51Sths         val |= BIT(28);
1285663e8e51Sths         if (raiseint) {
1286663e8e51Sths             eepro100_mdi_interrupt(s);
1287663e8e51Sths         }
1288663e8e51Sths     }
1289663e8e51Sths     val = (val & 0xffff0000) + data;
1290e5e23ab8SStefan Weil     e100_write_reg4(s, SCBCtrlMDI, val);
1291663e8e51Sths }
1292663e8e51Sths 
1293663e8e51Sths /*****************************************************************************
1294663e8e51Sths  *
1295663e8e51Sths  * Port emulation.
1296663e8e51Sths  *
1297663e8e51Sths  ****************************************************************************/
1298663e8e51Sths 
1299663e8e51Sths #define PORT_SOFTWARE_RESET     0
1300663e8e51Sths #define PORT_SELFTEST           1
1301663e8e51Sths #define PORT_SELECTIVE_RESET    2
1302663e8e51Sths #define PORT_DUMP               3
1303663e8e51Sths #define PORT_SELECTION_MASK     3
1304663e8e51Sths 
1305663e8e51Sths typedef struct {
1306663e8e51Sths     uint32_t st_sign;           /* Self Test Signature */
1307663e8e51Sths     uint32_t st_result;         /* Self Test Results */
1308c227f099SAnthony Liguori } eepro100_selftest_t;
1309663e8e51Sths 
1310663e8e51Sths static uint32_t eepro100_read_port(EEPRO100State * s)
1311663e8e51Sths {
1312663e8e51Sths     return 0;
1313663e8e51Sths }
1314663e8e51Sths 
13153fd3d0b4SStefan Weil static void eepro100_write_port(EEPRO100State *s)
1316663e8e51Sths {
13173fd3d0b4SStefan Weil     uint32_t val = e100_read_reg4(s, SCBPort);
1318663e8e51Sths     uint32_t address = (val & ~PORT_SELECTION_MASK);
1319663e8e51Sths     uint8_t selection = (val & PORT_SELECTION_MASK);
1320663e8e51Sths     switch (selection) {
1321663e8e51Sths     case PORT_SOFTWARE_RESET:
1322663e8e51Sths         nic_reset(s);
1323663e8e51Sths         break;
1324663e8e51Sths     case PORT_SELFTEST:
1325aac443e6SStefan Weil         TRACE(OTHER, logout("selftest address=0x%08x\n", address));
1326c227f099SAnthony Liguori         eepro100_selftest_t data;
132716ef60c9SEduard - Gabriel Munteanu         pci_dma_read(&s->dev, address, (uint8_t *) &data, sizeof(data));
1328663e8e51Sths         data.st_sign = 0xffffffff;
1329663e8e51Sths         data.st_result = 0;
133016ef60c9SEduard - Gabriel Munteanu         pci_dma_write(&s->dev, address, (uint8_t *) &data, sizeof(data));
1331663e8e51Sths         break;
1332663e8e51Sths     case PORT_SELECTIVE_RESET:
1333aac443e6SStefan Weil         TRACE(OTHER, logout("selective reset, selftest address=0x%08x\n", address));
1334663e8e51Sths         nic_selective_reset(s);
1335663e8e51Sths         break;
1336663e8e51Sths     default:
1337663e8e51Sths         logout("val=0x%08x\n", val);
1338663e8e51Sths         missing("unknown port selection");
1339663e8e51Sths     }
1340663e8e51Sths }
1341663e8e51Sths 
1342663e8e51Sths /*****************************************************************************
1343663e8e51Sths  *
1344663e8e51Sths  * General hardware emulation.
1345663e8e51Sths  *
1346663e8e51Sths  ****************************************************************************/
1347663e8e51Sths 
1348663e8e51Sths static uint8_t eepro100_read1(EEPRO100State * s, uint32_t addr)
1349663e8e51Sths {
1350ef476062SBlue Swirl     uint8_t val = 0;
1351663e8e51Sths     if (addr <= sizeof(s->mem) - sizeof(val)) {
1352e5e23ab8SStefan Weil         val = s->mem[addr];
1353663e8e51Sths     }
1354663e8e51Sths 
1355663e8e51Sths     switch (addr) {
1356663e8e51Sths     case SCBStatus:
1357663e8e51Sths     case SCBAck:
1358aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1359663e8e51Sths         break;
1360663e8e51Sths     case SCBCmd:
1361aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1362e7493b25SStefan Weil #if 0
1363e7493b25SStefan Weil         val = eepro100_read_command(s);
1364e7493b25SStefan Weil #endif
1365663e8e51Sths         break;
1366663e8e51Sths     case SCBIntmask:
1367aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1368663e8e51Sths         break;
1369663e8e51Sths     case SCBPort + 3:
1370aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1371663e8e51Sths         break;
1372663e8e51Sths     case SCBeeprom:
1373663e8e51Sths         val = eepro100_read_eeprom(s);
1374663e8e51Sths         break;
13750113f48dSStefan Weil     case SCBCtrlMDI:
13760113f48dSStefan Weil     case SCBCtrlMDI + 1:
13770113f48dSStefan Weil     case SCBCtrlMDI + 2:
13780113f48dSStefan Weil     case SCBCtrlMDI + 3:
13790113f48dSStefan Weil         val = (uint8_t)(eepro100_read_mdi(s) >> (8 * (addr & 3)));
13800113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
13810113f48dSStefan Weil         break;
13820908bba1SStefan Weil     case SCBpmdr:       /* Power Management Driver Register */
1383663e8e51Sths         val = 0;
1384aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1385663e8e51Sths         break;
1386a39bd017SStefan Weil     case SCBgctrl:      /* General Control Register */
1387a39bd017SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1388a39bd017SStefan Weil         break;
13890908bba1SStefan Weil     case SCBgstat:      /* General Status Register */
1390663e8e51Sths         /* 100 Mbps full duplex, valid link */
1391663e8e51Sths         val = 0x07;
1392aac443e6SStefan Weil         TRACE(OTHER, logout("addr=General Status val=%02x\n", val));
1393663e8e51Sths         break;
1394663e8e51Sths     default:
1395663e8e51Sths         logout("addr=%s val=0x%02x\n", regname(addr), val);
1396663e8e51Sths         missing("unknown byte read");
1397663e8e51Sths     }
1398663e8e51Sths     return val;
1399663e8e51Sths }
1400663e8e51Sths 
1401663e8e51Sths static uint16_t eepro100_read2(EEPRO100State * s, uint32_t addr)
1402663e8e51Sths {
1403ef476062SBlue Swirl     uint16_t val = 0;
1404663e8e51Sths     if (addr <= sizeof(s->mem) - sizeof(val)) {
1405e5e23ab8SStefan Weil         val = e100_read_reg2(s, addr);
1406663e8e51Sths     }
1407663e8e51Sths 
1408663e8e51Sths     switch (addr) {
1409663e8e51Sths     case SCBStatus:
1410dbbaaff6S=?UTF-8?q?Reimar=20D=C3=B6ffinger?=     case SCBCmd:
1411aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
1412663e8e51Sths         break;
1413663e8e51Sths     case SCBeeprom:
1414663e8e51Sths         val = eepro100_read_eeprom(s);
1415aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
1416663e8e51Sths         break;
14170113f48dSStefan Weil     case SCBCtrlMDI:
14180113f48dSStefan Weil     case SCBCtrlMDI + 2:
14190113f48dSStefan Weil         val = (uint16_t)(eepro100_read_mdi(s) >> (8 * (addr & 3)));
14200113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
14210113f48dSStefan Weil         break;
1422663e8e51Sths     default:
1423663e8e51Sths         logout("addr=%s val=0x%04x\n", regname(addr), val);
1424663e8e51Sths         missing("unknown word read");
1425663e8e51Sths     }
1426663e8e51Sths     return val;
1427663e8e51Sths }
1428663e8e51Sths 
1429663e8e51Sths static uint32_t eepro100_read4(EEPRO100State * s, uint32_t addr)
1430663e8e51Sths {
1431ef476062SBlue Swirl     uint32_t val = 0;
1432663e8e51Sths     if (addr <= sizeof(s->mem) - sizeof(val)) {
1433e5e23ab8SStefan Weil         val = e100_read_reg4(s, addr);
1434663e8e51Sths     }
1435663e8e51Sths 
1436663e8e51Sths     switch (addr) {
1437663e8e51Sths     case SCBStatus:
1438aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1439663e8e51Sths         break;
1440663e8e51Sths     case SCBPointer:
1441aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1442663e8e51Sths         break;
1443663e8e51Sths     case SCBPort:
1444663e8e51Sths         val = eepro100_read_port(s);
1445aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1446663e8e51Sths         break;
1447072476eaSStefan Weil     case SCBflash:
1448072476eaSStefan Weil         val = eepro100_read_eeprom(s);
1449072476eaSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1450072476eaSStefan Weil         break;
1451663e8e51Sths     case SCBCtrlMDI:
1452663e8e51Sths         val = eepro100_read_mdi(s);
1453663e8e51Sths         break;
1454663e8e51Sths     default:
1455663e8e51Sths         logout("addr=%s val=0x%08x\n", regname(addr), val);
1456663e8e51Sths         missing("unknown longword read");
1457663e8e51Sths     }
1458663e8e51Sths     return val;
1459663e8e51Sths }
1460663e8e51Sths 
1461663e8e51Sths static void eepro100_write1(EEPRO100State * s, uint32_t addr, uint8_t val)
1462663e8e51Sths {
1463e74818f3SStefan Weil     /* SCBStatus is readonly. */
1464e74818f3SStefan Weil     if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) {
1465e5e23ab8SStefan Weil         s->mem[addr] = val;
1466663e8e51Sths     }
1467663e8e51Sths 
1468663e8e51Sths     switch (addr) {
1469663e8e51Sths     case SCBStatus:
14701b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1471663e8e51Sths         break;
1472663e8e51Sths     case SCBAck:
14731b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1474663e8e51Sths         eepro100_acknowledge(s);
1475663e8e51Sths         break;
1476663e8e51Sths     case SCBCmd:
14771b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1478663e8e51Sths         eepro100_write_command(s, val);
1479663e8e51Sths         break;
1480663e8e51Sths     case SCBIntmask:
14811b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1482663e8e51Sths         if (val & BIT(1)) {
1483663e8e51Sths             eepro100_swi_interrupt(s);
1484663e8e51Sths         }
1485663e8e51Sths         eepro100_interrupt(s, 0);
1486663e8e51Sths         break;
148727a05006SStefan Weil     case SCBPointer:
148827a05006SStefan Weil     case SCBPointer + 1:
148927a05006SStefan Weil     case SCBPointer + 2:
149027a05006SStefan Weil     case SCBPointer + 3:
149127a05006SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
149227a05006SStefan Weil         break;
14933fd3d0b4SStefan Weil     case SCBPort:
14943fd3d0b4SStefan Weil     case SCBPort + 1:
14953fd3d0b4SStefan Weil     case SCBPort + 2:
14963fd3d0b4SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
14973fd3d0b4SStefan Weil         break;
1498663e8e51Sths     case SCBPort + 3:
14993fd3d0b4SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
15003fd3d0b4SStefan Weil         eepro100_write_port(s);
15013fd3d0b4SStefan Weil         break;
1502aac443e6SStefan Weil     case SCBFlow:       /* does not exist on 82557 */
15033257d2b6Sths     case SCBFlow + 1:
15043257d2b6Sths     case SCBFlow + 2:
15050908bba1SStefan Weil     case SCBpmdr:       /* does not exist on 82557 */
1506aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1507663e8e51Sths         break;
1508663e8e51Sths     case SCBeeprom:
15091b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1510663e8e51Sths         eepro100_write_eeprom(s->eeprom, val);
1511663e8e51Sths         break;
15120113f48dSStefan Weil     case SCBCtrlMDI:
15130113f48dSStefan Weil     case SCBCtrlMDI + 1:
15140113f48dSStefan Weil     case SCBCtrlMDI + 2:
15150113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
15160113f48dSStefan Weil         break;
15170113f48dSStefan Weil     case SCBCtrlMDI + 3:
15180113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
15190113f48dSStefan Weil         eepro100_write_mdi(s);
15200113f48dSStefan Weil         break;
1521663e8e51Sths     default:
1522663e8e51Sths         logout("addr=%s val=0x%02x\n", regname(addr), val);
1523663e8e51Sths         missing("unknown byte write");
1524663e8e51Sths     }
1525663e8e51Sths }
1526663e8e51Sths 
1527663e8e51Sths static void eepro100_write2(EEPRO100State * s, uint32_t addr, uint16_t val)
1528663e8e51Sths {
1529e74818f3SStefan Weil     /* SCBStatus is readonly. */
1530e74818f3SStefan Weil     if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) {
1531e5e23ab8SStefan Weil         e100_write_reg2(s, addr, val);
1532663e8e51Sths     }
1533663e8e51Sths 
1534663e8e51Sths     switch (addr) {
1535663e8e51Sths     case SCBStatus:
15361b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
1537e74818f3SStefan Weil         s->mem[SCBAck] = (val >> 8);
1538663e8e51Sths         eepro100_acknowledge(s);
1539663e8e51Sths         break;
1540663e8e51Sths     case SCBCmd:
15411b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
1542663e8e51Sths         eepro100_write_command(s, val);
1543663e8e51Sths         eepro100_write1(s, SCBIntmask, val >> 8);
1544663e8e51Sths         break;
154527a05006SStefan Weil     case SCBPointer:
154627a05006SStefan Weil     case SCBPointer + 2:
154727a05006SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
154827a05006SStefan Weil         break;
15493fd3d0b4SStefan Weil     case SCBPort:
15503fd3d0b4SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
15513fd3d0b4SStefan Weil         break;
15523fd3d0b4SStefan Weil     case SCBPort + 2:
15533fd3d0b4SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
15543fd3d0b4SStefan Weil         eepro100_write_port(s);
15553fd3d0b4SStefan Weil         break;
1556663e8e51Sths     case SCBeeprom:
15571b4f97d6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
1558663e8e51Sths         eepro100_write_eeprom(s->eeprom, val);
1559663e8e51Sths         break;
15600113f48dSStefan Weil     case SCBCtrlMDI:
15610113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
15620113f48dSStefan Weil         break;
15630113f48dSStefan Weil     case SCBCtrlMDI + 2:
15640113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
15650113f48dSStefan Weil         eepro100_write_mdi(s);
15660113f48dSStefan Weil         break;
1567663e8e51Sths     default:
1568663e8e51Sths         logout("addr=%s val=0x%04x\n", regname(addr), val);
1569663e8e51Sths         missing("unknown word write");
1570663e8e51Sths     }
1571663e8e51Sths }
1572663e8e51Sths 
1573663e8e51Sths static void eepro100_write4(EEPRO100State * s, uint32_t addr, uint32_t val)
1574663e8e51Sths {
1575663e8e51Sths     if (addr <= sizeof(s->mem) - sizeof(val)) {
1576e5e23ab8SStefan Weil         e100_write_reg4(s, addr, val);
1577663e8e51Sths     }
1578663e8e51Sths 
1579663e8e51Sths     switch (addr) {
1580663e8e51Sths     case SCBPointer:
158127a05006SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1582663e8e51Sths         break;
1583663e8e51Sths     case SCBPort:
1584aac443e6SStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
15853fd3d0b4SStefan Weil         eepro100_write_port(s);
1586663e8e51Sths         break;
1587072476eaSStefan Weil     case SCBflash:
1588072476eaSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1589072476eaSStefan Weil         val = val >> 16;
1590072476eaSStefan Weil         eepro100_write_eeprom(s->eeprom, val);
1591072476eaSStefan Weil         break;
1592663e8e51Sths     case SCBCtrlMDI:
15930113f48dSStefan Weil         TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
15940113f48dSStefan Weil         eepro100_write_mdi(s);
1595663e8e51Sths         break;
1596663e8e51Sths     default:
1597663e8e51Sths         logout("addr=%s val=0x%08x\n", regname(addr), val);
1598663e8e51Sths         missing("unknown longword write");
1599663e8e51Sths     }
1600663e8e51Sths }
1601663e8e51Sths 
1602a8170e5eSAvi Kivity static uint64_t eepro100_read(void *opaque, hwaddr addr,
16035e6ffddeSAvi Kivity                               unsigned size)
1604663e8e51Sths {
1605663e8e51Sths     EEPRO100State *s = opaque;
16065e6ffddeSAvi Kivity 
16075e6ffddeSAvi Kivity     switch (size) {
16085e6ffddeSAvi Kivity     case 1: return eepro100_read1(s, addr);
16095e6ffddeSAvi Kivity     case 2: return eepro100_read2(s, addr);
16105e6ffddeSAvi Kivity     case 4: return eepro100_read4(s, addr);
16115e6ffddeSAvi Kivity     default: abort();
16125e6ffddeSAvi Kivity     }
1613663e8e51Sths }
1614663e8e51Sths 
1615a8170e5eSAvi Kivity static void eepro100_write(void *opaque, hwaddr addr,
16165e6ffddeSAvi Kivity                            uint64_t data, unsigned size)
1617663e8e51Sths {
1618663e8e51Sths     EEPRO100State *s = opaque;
16195e6ffddeSAvi Kivity 
16205e6ffddeSAvi Kivity     switch (size) {
16210ed8b6f6SBlue Swirl     case 1:
16220ed8b6f6SBlue Swirl         eepro100_write1(s, addr, data);
16230ed8b6f6SBlue Swirl         break;
16240ed8b6f6SBlue Swirl     case 2:
16250ed8b6f6SBlue Swirl         eepro100_write2(s, addr, data);
16260ed8b6f6SBlue Swirl         break;
16270ed8b6f6SBlue Swirl     case 4:
16280ed8b6f6SBlue Swirl         eepro100_write4(s, addr, data);
16290ed8b6f6SBlue Swirl         break;
16300ed8b6f6SBlue Swirl     default:
16310ed8b6f6SBlue Swirl         abort();
16325e6ffddeSAvi Kivity     }
1633663e8e51Sths }
1634663e8e51Sths 
16355e6ffddeSAvi Kivity static const MemoryRegionOps eepro100_ops = {
16365e6ffddeSAvi Kivity     .read = eepro100_read,
16375e6ffddeSAvi Kivity     .write = eepro100_write,
16385e6ffddeSAvi Kivity     .endianness = DEVICE_LITTLE_ENDIAN,
1639663e8e51Sths };
1640663e8e51Sths 
16414e68f7a0SStefan Hajnoczi static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
1642663e8e51Sths {
1643663e8e51Sths     /* TODO:
1644663e8e51Sths      * - Magic packets should set bit 30 in power management driver register.
1645663e8e51Sths      * - Interesting packets should set bit 29 in power management driver register.
1646663e8e51Sths      */
1647cc1f0f45SJason Wang     EEPRO100State *s = qemu_get_nic_opaque(nc);
1648663e8e51Sths     uint16_t rfd_status = 0xa000;
1649792f1d63SStefan Weil #if defined(CONFIG_PAD_RECEIVED_FRAMES)
1650792f1d63SStefan Weil     uint8_t min_buf[60];
1651792f1d63SStefan Weil #endif
1652663e8e51Sths     static const uint8_t broadcast_macaddr[6] =
1653663e8e51Sths         { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
1654663e8e51Sths 
1655792f1d63SStefan Weil #if defined(CONFIG_PAD_RECEIVED_FRAMES)
1656792f1d63SStefan Weil     /* Pad to minimum Ethernet frame length */
1657792f1d63SStefan Weil     if (size < sizeof(min_buf)) {
1658792f1d63SStefan Weil         memcpy(min_buf, buf, size);
1659792f1d63SStefan Weil         memset(&min_buf[size], 0, sizeof(min_buf) - size);
1660792f1d63SStefan Weil         buf = min_buf;
1661792f1d63SStefan Weil         size = sizeof(min_buf);
1662792f1d63SStefan Weil     }
1663792f1d63SStefan Weil #endif
1664792f1d63SStefan Weil 
1665663e8e51Sths     if (s->configuration[8] & 0x80) {
1666663e8e51Sths         /* CSMA is disabled. */
1667663e8e51Sths         logout("%p received while CSMA is disabled\n", s);
16684f1c942bSMark McLoughlin         return -1;
1669792f1d63SStefan Weil #if !defined(CONFIG_PAD_RECEIVED_FRAMES)
1670ced5296aSStefan Weil     } else if (size < 64 && (s->configuration[7] & BIT(0))) {
1671663e8e51Sths         /* Short frame and configuration byte 7/0 (discard short receive) set:
1672663e8e51Sths          * Short frame is discarded */
1673067d01deSStefan Weil         logout("%p received short frame (%zu byte)\n", s, size);
1674663e8e51Sths         s->statistics.rx_short_frame_errors++;
1675e7493b25SStefan Weil         return -1;
1676e7493b25SStefan Weil #endif
1677ced5296aSStefan Weil     } else if ((size > MAX_ETH_FRAME_SIZE + 4) && !(s->configuration[18] & BIT(3))) {
1678663e8e51Sths         /* Long frame and configuration byte 18/3 (long receive ok) not set:
1679663e8e51Sths          * Long frames are discarded. */
1680067d01deSStefan Weil         logout("%p received long frame (%zu byte), ignored\n", s, size);
16814f1c942bSMark McLoughlin         return -1;
1682e7493b25SStefan Weil     } else if (memcmp(buf, s->conf.macaddr.a, 6) == 0) {       /* !!! */
1683663e8e51Sths         /* Frame matches individual address. */
1684663e8e51Sths         /* TODO: check configuration byte 15/4 (ignore U/L). */
1685067d01deSStefan Weil         TRACE(RXTX, logout("%p received frame for me, len=%zu\n", s, size));
1686663e8e51Sths     } else if (memcmp(buf, broadcast_macaddr, 6) == 0) {
1687663e8e51Sths         /* Broadcast frame. */
1688067d01deSStefan Weil         TRACE(RXTX, logout("%p received broadcast, len=%zu\n", s, size));
1689663e8e51Sths         rfd_status |= 0x0002;
16907b8737deSStefan Weil     } else if (buf[0] & 0x01) {
1691663e8e51Sths         /* Multicast frame. */
16927b8737deSStefan Weil         TRACE(RXTX, logout("%p received multicast, len=%zu,%s\n", s, size, nic_dump(buf, size)));
16937f1e9d4eSKevin Wolf         if (s->configuration[21] & BIT(3)) {
16947b8737deSStefan Weil           /* Multicast all bit is set, receive all multicast frames. */
16957b8737deSStefan Weil         } else {
169669f3ce78SStefan Weil           unsigned mcast_idx = e100_compute_mcast_idx(buf);
16977b8737deSStefan Weil           assert(mcast_idx < 64);
16987b8737deSStefan Weil           if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) {
16997b8737deSStefan Weil             /* Multicast frame is allowed in hash table. */
1700ced5296aSStefan Weil           } else if (s->configuration[15] & BIT(0)) {
17017b8737deSStefan Weil               /* Promiscuous: receive all. */
17027b8737deSStefan Weil               rfd_status |= 0x0004;
17037b8737deSStefan Weil           } else {
17047b8737deSStefan Weil               TRACE(RXTX, logout("%p multicast ignored\n", s));
17057b8737deSStefan Weil               return -1;
17067f1e9d4eSKevin Wolf           }
1707663e8e51Sths         }
17087b8737deSStefan Weil         /* TODO: Next not for promiscuous mode? */
1709663e8e51Sths         rfd_status |= 0x0002;
1710ced5296aSStefan Weil     } else if (s->configuration[15] & BIT(0)) {
1711663e8e51Sths         /* Promiscuous: receive all. */
1712067d01deSStefan Weil         TRACE(RXTX, logout("%p received frame in promiscuous mode, len=%zu\n", s, size));
1713663e8e51Sths         rfd_status |= 0x0004;
1714010ec629SStefan Weil     } else if (s->configuration[20] & BIT(6)) {
1715010ec629SStefan Weil         /* Multiple IA bit set. */
1716010ec629SStefan Weil         unsigned mcast_idx = compute_mcast_idx(buf);
1717010ec629SStefan Weil         assert(mcast_idx < 64);
1718010ec629SStefan Weil         if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) {
1719010ec629SStefan Weil             TRACE(RXTX, logout("%p accepted, multiple IA bit set\n", s));
1720010ec629SStefan Weil         } else {
1721010ec629SStefan Weil             TRACE(RXTX, logout("%p frame ignored, multiple IA bit set\n", s));
1722010ec629SStefan Weil             return -1;
1723010ec629SStefan Weil         }
1724663e8e51Sths     } else {
1725067d01deSStefan Weil         TRACE(RXTX, logout("%p received frame, ignored, len=%zu,%s\n", s, size,
1726aac443e6SStefan Weil               nic_dump(buf, size)));
17274f1c942bSMark McLoughlin         return size;
1728663e8e51Sths     }
1729663e8e51Sths 
1730663e8e51Sths     if (get_ru_state(s) != ru_ready) {
1731aac443e6SStefan Weil         /* No resources available. */
1732aac443e6SStefan Weil         logout("no resources, state=%u\n", get_ru_state(s));
1733e824012bSStefan Weil         /* TODO: RNR interrupt only at first failed frame? */
1734e824012bSStefan Weil         eepro100_rnr_interrupt(s);
1735663e8e51Sths         s->statistics.rx_resource_errors++;
1736e7493b25SStefan Weil #if 0
1737e7493b25SStefan Weil         assert(!"no resources");
1738e7493b25SStefan Weil #endif
17394f1c942bSMark McLoughlin         return -1;
1740663e8e51Sths     }
1741e7493b25SStefan Weil     /* !!! */
1742c227f099SAnthony Liguori     eepro100_rx_t rx;
174316ef60c9SEduard - Gabriel Munteanu     pci_dma_read(&s->dev, s->ru_base + s->ru_offset,
1744e965d4bcSDavid Gibson                  &rx, sizeof(eepro100_rx_t));
1745663e8e51Sths     uint16_t rfd_command = le16_to_cpu(rx.command);
1746663e8e51Sths     uint16_t rfd_size = le16_to_cpu(rx.size);
17477f1e9d4eSKevin Wolf 
17487f1e9d4eSKevin Wolf     if (size > rfd_size) {
17497f1e9d4eSKevin Wolf         logout("Receive buffer (%" PRId16 " bytes) too small for data "
17507f1e9d4eSKevin Wolf             "(%zu bytes); data truncated\n", rfd_size, size);
17517f1e9d4eSKevin Wolf         size = rfd_size;
17527f1e9d4eSKevin Wolf     }
1753792f1d63SStefan Weil #if !defined(CONFIG_PAD_RECEIVED_FRAMES)
1754663e8e51Sths     if (size < 64) {
1755663e8e51Sths         rfd_status |= 0x0080;
1756663e8e51Sths     }
1757792f1d63SStefan Weil #endif
1758aac443e6SStefan Weil     TRACE(OTHER, logout("command 0x%04x, link 0x%08x, addr 0x%08x, size %u\n",
1759aac443e6SStefan Weil           rfd_command, rx.link, rx.rx_buf_addr, rfd_size));
176016ef60c9SEduard - Gabriel Munteanu     stw_le_pci_dma(&s->dev, s->ru_base + s->ru_offset +
1761e5e23ab8SStefan Weil                 offsetof(eepro100_rx_t, status), rfd_status);
176216ef60c9SEduard - Gabriel Munteanu     stw_le_pci_dma(&s->dev, s->ru_base + s->ru_offset +
1763e5e23ab8SStefan Weil                 offsetof(eepro100_rx_t, count), size);
1764663e8e51Sths     /* Early receive interrupt not supported. */
1765e7493b25SStefan Weil #if 0
1766e7493b25SStefan Weil     eepro100_er_interrupt(s);
1767e7493b25SStefan Weil #endif
1768663e8e51Sths     /* Receive CRC Transfer not supported. */
1769ced5296aSStefan Weil     if (s->configuration[18] & BIT(2)) {
17707f1e9d4eSKevin Wolf         missing("Receive CRC Transfer");
17717f1e9d4eSKevin Wolf         return -1;
17727f1e9d4eSKevin Wolf     }
1773663e8e51Sths     /* TODO: check stripping enable bit. */
1774e7493b25SStefan Weil #if 0
1775e7493b25SStefan Weil     assert(!(s->configuration[17] & BIT(0)));
1776e7493b25SStefan Weil #endif
177716ef60c9SEduard - Gabriel Munteanu     pci_dma_write(&s->dev, s->ru_base + s->ru_offset +
177827112f18SStefan Weil                   sizeof(eepro100_rx_t), buf, size);
1779663e8e51Sths     s->statistics.rx_good_frames++;
1780663e8e51Sths     eepro100_fr_interrupt(s);
1781663e8e51Sths     s->ru_offset = le32_to_cpu(rx.link);
1782ced5296aSStefan Weil     if (rfd_command & COMMAND_EL) {
1783663e8e51Sths         /* EL bit is set, so this was the last frame. */
17847f1e9d4eSKevin Wolf         logout("receive: Running out of frames\n");
17851069985fSBo Yang         set_ru_state(s, ru_no_resources);
17861069985fSBo Yang         eepro100_rnr_interrupt(s);
1787663e8e51Sths     }
1788ced5296aSStefan Weil     if (rfd_command & COMMAND_S) {
1789663e8e51Sths         /* S bit is set. */
1790663e8e51Sths         set_ru_state(s, ru_suspended);
1791663e8e51Sths     }
17924f1c942bSMark McLoughlin     return size;
1793663e8e51Sths }
1794663e8e51Sths 
1795151b2986SJuan Quintela static const VMStateDescription vmstate_eepro100 = {
1796151b2986SJuan Quintela     .version_id = 3,
1797151b2986SJuan Quintela     .minimum_version_id = 2,
1798151b2986SJuan Quintela     .fields = (VMStateField[]) {
1799151b2986SJuan Quintela         VMSTATE_PCI_DEVICE(dev, EEPRO100State),
1800151b2986SJuan Quintela         VMSTATE_UNUSED(32),
1801151b2986SJuan Quintela         VMSTATE_BUFFER(mult, EEPRO100State),
1802151b2986SJuan Quintela         VMSTATE_BUFFER(mem, EEPRO100State),
18033706c43fSStefan Weil         /* Save all members of struct between scb_stat and mem. */
1804151b2986SJuan Quintela         VMSTATE_UINT8(scb_stat, EEPRO100State),
1805151b2986SJuan Quintela         VMSTATE_UINT8(int_stat, EEPRO100State),
1806151b2986SJuan Quintela         VMSTATE_UNUSED(3*4),
1807151b2986SJuan Quintela         VMSTATE_MACADDR(conf.macaddr, EEPRO100State),
1808151b2986SJuan Quintela         VMSTATE_UNUSED(19*4),
1809151b2986SJuan Quintela         VMSTATE_UINT16_ARRAY(mdimem, EEPRO100State, 32),
1810aac443e6SStefan Weil         /* The eeprom should be saved and restored by its own routines. */
1811151b2986SJuan Quintela         VMSTATE_UINT32(device, EEPRO100State),
1812151b2986SJuan Quintela         /* TODO check device. */
1813151b2986SJuan Quintela         VMSTATE_UINT32(cu_base, EEPRO100State),
1814151b2986SJuan Quintela         VMSTATE_UINT32(cu_offset, EEPRO100State),
1815151b2986SJuan Quintela         VMSTATE_UINT32(ru_base, EEPRO100State),
1816151b2986SJuan Quintela         VMSTATE_UINT32(ru_offset, EEPRO100State),
1817151b2986SJuan Quintela         VMSTATE_UINT32(statsaddr, EEPRO100State),
1818ba42b646SStefan Weil         /* Save eepro100_stats_t statistics. */
1819151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_good_frames, EEPRO100State),
1820151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_max_collisions, EEPRO100State),
1821151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_late_collisions, EEPRO100State),
1822151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_underruns, EEPRO100State),
1823151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_lost_crs, EEPRO100State),
1824151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_deferred, EEPRO100State),
1825151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_single_collisions, EEPRO100State),
1826151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_multiple_collisions, EEPRO100State),
1827151b2986SJuan Quintela         VMSTATE_UINT32(statistics.tx_total_collisions, EEPRO100State),
1828151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_good_frames, EEPRO100State),
1829151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_crc_errors, EEPRO100State),
1830151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_alignment_errors, EEPRO100State),
1831151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_resource_errors, EEPRO100State),
1832151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_overrun_errors, EEPRO100State),
1833151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_cdt_errors, EEPRO100State),
1834151b2986SJuan Quintela         VMSTATE_UINT32(statistics.rx_short_frame_errors, EEPRO100State),
1835151b2986SJuan Quintela         VMSTATE_UINT32(statistics.fc_xmt_pause, EEPRO100State),
1836151b2986SJuan Quintela         VMSTATE_UINT32(statistics.fc_rcv_pause, EEPRO100State),
1837151b2986SJuan Quintela         VMSTATE_UINT32(statistics.fc_rcv_unsupported, EEPRO100State),
1838151b2986SJuan Quintela         VMSTATE_UINT16(statistics.xmt_tco_frames, EEPRO100State),
1839151b2986SJuan Quintela         VMSTATE_UINT16(statistics.rcv_tco_frames, EEPRO100State),
18402657c663Sbalrog         /* Configuration bytes. */
1841151b2986SJuan Quintela         VMSTATE_BUFFER(configuration, EEPRO100State),
1842151b2986SJuan Quintela         VMSTATE_END_OF_LIST()
1843663e8e51Sths     }
1844151b2986SJuan Quintela };
1845663e8e51Sths 
1846f90c2bcdSAlex Williamson static void pci_nic_uninit(PCIDevice *pci_dev)
1847b946a153Saliguori {
1848c4c270e2SStefan Weil     EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev);
1849b946a153Saliguori 
18500be71e32SAlex Williamson     vmstate_unregister(&pci_dev->qdev, s->vmstate, s);
18512634ab7fSLi Qiang     g_free(s->vmstate);
18525fce2b3eSAlex Williamson     eeprom93xx_free(&pci_dev->qdev, s->eeprom);
1853948ecf21SJason Wang     qemu_del_nic(s->nic);
1854b946a153Saliguori }
1855b946a153Saliguori 
1856e00e365eSMark McLoughlin static NetClientInfo net_eepro100_info = {
1857f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
1858e00e365eSMark McLoughlin     .size = sizeof(NICState),
1859e00e365eSMark McLoughlin     .receive = nic_receive,
1860e00e365eSMark McLoughlin };
1861e00e365eSMark McLoughlin 
18629af21dbeSMarkus Armbruster static void e100_nic_realize(PCIDevice *pci_dev, Error **errp)
1863663e8e51Sths {
1864273a2142SJuan Quintela     EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev);
186540021f08SAnthony Liguori     E100PCIDeviceInfo *info = eepro100_get_class(s);
18669a7c2a59SMao Zhongyi     Error *local_err = NULL;
1867663e8e51Sths 
1868aac443e6SStefan Weil     TRACE(OTHER, logout("\n"));
1869663e8e51Sths 
187040021f08SAnthony Liguori     s->device = info->device;
1871663e8e51Sths 
18729a7c2a59SMao Zhongyi     e100_pci_reset(s, &local_err);
18739a7c2a59SMao Zhongyi     if (local_err) {
18749a7c2a59SMao Zhongyi         error_propagate(errp, local_err);
18759a7c2a59SMao Zhongyi         return;
18769a7c2a59SMao Zhongyi     }
1877663e8e51Sths 
1878663e8e51Sths     /* Add 64 * 2 EEPROM. i82557 and i82558 support a 64 word EEPROM,
1879663e8e51Sths      * i82559 and later support 64 or 256 word EEPROM. */
18805fce2b3eSAlex Williamson     s->eeprom = eeprom93xx_new(&pci_dev->qdev, EEPROM_SIZE);
1881663e8e51Sths 
1882663e8e51Sths     /* Handler for memory-mapped I/O */
1883eedfac6fSPaolo Bonzini     memory_region_init_io(&s->mmio_bar, OBJECT(s), &eepro100_ops, s,
1884eedfac6fSPaolo Bonzini                           "eepro100-mmio", PCI_MEM_SIZE);
1885e824b2ccSAvi Kivity     pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->mmio_bar);
1886eedfac6fSPaolo Bonzini     memory_region_init_io(&s->io_bar, OBJECT(s), &eepro100_ops, s,
1887eedfac6fSPaolo Bonzini                           "eepro100-io", PCI_IO_SIZE);
1888e824b2ccSAvi Kivity     pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar);
18895e6ffddeSAvi Kivity     /* FIXME: flash aliases to mmio?! */
1890eedfac6fSPaolo Bonzini     memory_region_init_io(&s->flash_bar, OBJECT(s), &eepro100_ops, s,
1891eedfac6fSPaolo Bonzini                           "eepro100-flash", PCI_FLASH_SIZE);
1892e824b2ccSAvi Kivity     pci_register_bar(&s->dev, 2, 0, &s->flash_bar);
1893663e8e51Sths 
1894508ef936SGerd Hoffmann     qemu_macaddr_default_if_unset(&s->conf.macaddr);
1895ce0e58b3SStefan Weil     logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6));
1896663e8e51Sths 
1897663e8e51Sths     nic_reset(s);
1898663e8e51Sths 
1899e00e365eSMark McLoughlin     s->nic = qemu_new_nic(&net_eepro100_info, &s->conf,
1900f79f2bfcSAnthony Liguori                           object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s);
1901663e8e51Sths 
1902b356f76dSJason Wang     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
1903b356f76dSJason Wang     TRACE(OTHER, logout("%s\n", qemu_get_queue(s->nic)->info_str));
1904663e8e51Sths 
1905a08d4367SJan Kiszka     qemu_register_reset(nic_reset, s);
1906663e8e51Sths 
1907*e4d67e4fSMarc-André Lureau     s->vmstate = g_memdup(&vmstate_eepro100, sizeof(vmstate_eepro100));
1908b356f76dSJason Wang     s->vmstate->name = qemu_get_queue(s->nic)->model;
19090be71e32SAlex Williamson     vmstate_register(&pci_dev->qdev, -1, s->vmstate, s);
1910663e8e51Sths }
1911663e8e51Sths 
19127317bb17SGonglei static void eepro100_instance_init(Object *obj)
19137317bb17SGonglei {
19147317bb17SGonglei     EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, PCI_DEVICE(obj));
19157317bb17SGonglei     device_add_bootindex_property(obj, &s->conf.bootindex,
19167317bb17SGonglei                                   "bootindex", "/ethernet-phy@0",
19177317bb17SGonglei                                   DEVICE(s), NULL);
19187317bb17SGonglei }
19197317bb17SGonglei 
1920558c8634SStefan Weil static E100PCIDeviceInfo e100_devices[] = {
1921663e8e51Sths     {
192239bffca2SAnthony Liguori         .name = "i82550",
192339bffca2SAnthony Liguori         .desc = "Intel i82550 Ethernet",
1924558c8634SStefan Weil         .device = i82550,
1925558c8634SStefan Weil         /* TODO: check device id. */
192640021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82551IT,
1927558c8634SStefan Weil         /* Revision ID: 0x0c, 0x0d, 0x0e. */
192840021f08SAnthony Liguori         .revision = 0x0e,
1929558c8634SStefan Weil         /* TODO: check size of statistical counters. */
1930558c8634SStefan Weil         .stats_size = 80,
1931558c8634SStefan Weil         /* TODO: check extended tcb support. */
1932558c8634SStefan Weil         .has_extended_tcb_support = true,
1933558c8634SStefan Weil         .power_management = true,
1934558c8634SStefan Weil     },{
193539bffca2SAnthony Liguori         .name = "i82551",
193639bffca2SAnthony Liguori         .desc = "Intel i82551 Ethernet",
1937558c8634SStefan Weil         .device = i82551,
193840021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82551IT,
1939558c8634SStefan Weil         /* Revision ID: 0x0f, 0x10. */
194040021f08SAnthony Liguori         .revision = 0x0f,
1941558c8634SStefan Weil         /* TODO: check size of statistical counters. */
1942558c8634SStefan Weil         .stats_size = 80,
1943558c8634SStefan Weil         .has_extended_tcb_support = true,
1944558c8634SStefan Weil         .power_management = true,
1945558c8634SStefan Weil     },{
194639bffca2SAnthony Liguori         .name = "i82557a",
194739bffca2SAnthony Liguori         .desc = "Intel i82557A Ethernet",
1948558c8634SStefan Weil         .device = i82557A,
194940021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
195040021f08SAnthony Liguori         .revision = 0x01,
1951558c8634SStefan Weil         .power_management = false,
1952558c8634SStefan Weil     },{
195339bffca2SAnthony Liguori         .name = "i82557b",
195439bffca2SAnthony Liguori         .desc = "Intel i82557B Ethernet",
1955558c8634SStefan Weil         .device = i82557B,
195640021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
195740021f08SAnthony Liguori         .revision = 0x02,
1958558c8634SStefan Weil         .power_management = false,
1959558c8634SStefan Weil     },{
196039bffca2SAnthony Liguori         .name = "i82557c",
196139bffca2SAnthony Liguori         .desc = "Intel i82557C Ethernet",
1962558c8634SStefan Weil         .device = i82557C,
196340021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
196440021f08SAnthony Liguori         .revision = 0x03,
1965558c8634SStefan Weil         .power_management = false,
1966558c8634SStefan Weil     },{
196739bffca2SAnthony Liguori         .name = "i82558a",
196839bffca2SAnthony Liguori         .desc = "Intel i82558A Ethernet",
1969558c8634SStefan Weil         .device = i82558A,
197040021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
197140021f08SAnthony Liguori         .revision = 0x04,
1972558c8634SStefan Weil         .stats_size = 76,
1973558c8634SStefan Weil         .has_extended_tcb_support = true,
1974558c8634SStefan Weil         .power_management = true,
1975558c8634SStefan Weil     },{
197639bffca2SAnthony Liguori         .name = "i82558b",
197739bffca2SAnthony Liguori         .desc = "Intel i82558B Ethernet",
1978558c8634SStefan Weil         .device = i82558B,
197940021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
198040021f08SAnthony Liguori         .revision = 0x05,
1981558c8634SStefan Weil         .stats_size = 76,
1982558c8634SStefan Weil         .has_extended_tcb_support = true,
1983558c8634SStefan Weil         .power_management = true,
1984558c8634SStefan Weil     },{
198539bffca2SAnthony Liguori         .name = "i82559a",
198639bffca2SAnthony Liguori         .desc = "Intel i82559A Ethernet",
1987558c8634SStefan Weil         .device = i82559A,
198840021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
198940021f08SAnthony Liguori         .revision = 0x06,
1990558c8634SStefan Weil         .stats_size = 80,
1991558c8634SStefan Weil         .has_extended_tcb_support = true,
1992558c8634SStefan Weil         .power_management = true,
1993558c8634SStefan Weil     },{
199439bffca2SAnthony Liguori         .name = "i82559b",
199539bffca2SAnthony Liguori         .desc = "Intel i82559B Ethernet",
1996558c8634SStefan Weil         .device = i82559B,
199740021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
199840021f08SAnthony Liguori         .revision = 0x07,
1999558c8634SStefan Weil         .stats_size = 80,
2000558c8634SStefan Weil         .has_extended_tcb_support = true,
2001558c8634SStefan Weil         .power_management = true,
2002558c8634SStefan Weil     },{
200339bffca2SAnthony Liguori         .name = "i82559c",
200439bffca2SAnthony Liguori         .desc = "Intel i82559C Ethernet",
2005558c8634SStefan Weil         .device = i82559C,
200640021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82557,
2007558c8634SStefan Weil #if 0
200840021f08SAnthony Liguori         .revision = 0x08,
2009558c8634SStefan Weil #endif
2010558c8634SStefan Weil         /* TODO: Windows wants revision id 0x0c. */
201140021f08SAnthony Liguori         .revision = 0x0c,
2012ad03502bSIsaku Yamahata #if EEPROM_SIZE > 0
201340021f08SAnthony Liguori         .subsystem_vendor_id = PCI_VENDOR_ID_INTEL,
201440021f08SAnthony Liguori         .subsystem_id = 0x0040,
2015ad03502bSIsaku Yamahata #endif
2016558c8634SStefan Weil         .stats_size = 80,
2017558c8634SStefan Weil         .has_extended_tcb_support = true,
2018558c8634SStefan Weil         .power_management = true,
2019558c8634SStefan Weil     },{
202039bffca2SAnthony Liguori         .name = "i82559er",
202139bffca2SAnthony Liguori         .desc = "Intel i82559ER Ethernet",
2022558c8634SStefan Weil         .device = i82559ER,
202340021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82551IT,
202440021f08SAnthony Liguori         .revision = 0x09,
2025558c8634SStefan Weil         .stats_size = 80,
2026558c8634SStefan Weil         .has_extended_tcb_support = true,
2027558c8634SStefan Weil         .power_management = true,
2028558c8634SStefan Weil     },{
202939bffca2SAnthony Liguori         .name = "i82562",
203039bffca2SAnthony Liguori         .desc = "Intel i82562 Ethernet",
2031558c8634SStefan Weil         .device = i82562,
2032558c8634SStefan Weil         /* TODO: check device id. */
203340021f08SAnthony Liguori         .device_id = PCI_DEVICE_ID_INTEL_82551IT,
2034558c8634SStefan Weil         /* TODO: wrong revision id. */
203540021f08SAnthony Liguori         .revision = 0x0e,
2036558c8634SStefan Weil         .stats_size = 80,
2037558c8634SStefan Weil         .has_extended_tcb_support = true,
2038558c8634SStefan Weil         .power_management = true,
2039db667a12SStefan Weil     },{
2040db667a12SStefan Weil         /* Toshiba Tecra 8200. */
204139bffca2SAnthony Liguori         .name = "i82801",
204239bffca2SAnthony Liguori         .desc = "Intel i82801 Ethernet",
2043db667a12SStefan Weil         .device = i82801,
204440021f08SAnthony Liguori         .device_id = 0x2449,
204540021f08SAnthony Liguori         .revision = 0x03,
2046db667a12SStefan Weil         .stats_size = 80,
2047db667a12SStefan Weil         .has_extended_tcb_support = true,
2048db667a12SStefan Weil         .power_management = true,
2049663e8e51Sths     }
2050558c8634SStefan Weil };
2051663e8e51Sths 
205240021f08SAnthony Liguori static E100PCIDeviceInfo *eepro100_get_class_by_name(const char *typename)
205340021f08SAnthony Liguori {
205440021f08SAnthony Liguori     E100PCIDeviceInfo *info = NULL;
205540021f08SAnthony Liguori     int i;
205640021f08SAnthony Liguori 
205740021f08SAnthony Liguori     /* This is admittedly awkward but also temporary.  QOM allows for
205840021f08SAnthony Liguori      * parameterized typing and for subclassing both of which would suitable
205940021f08SAnthony Liguori      * handle what's going on here.  But class_data is already being used as
206040021f08SAnthony Liguori      * a stop-gap hack to allow incremental qdev conversion so we cannot use it
206140021f08SAnthony Liguori      * right now.  Once we merge the final QOM series, we can come back here and
206240021f08SAnthony Liguori      * do this in a much more elegant fashion.
206340021f08SAnthony Liguori      */
206440021f08SAnthony Liguori     for (i = 0; i < ARRAY_SIZE(e100_devices); i++) {
206539bffca2SAnthony Liguori         if (strcmp(e100_devices[i].name, typename) == 0) {
206640021f08SAnthony Liguori             info = &e100_devices[i];
206740021f08SAnthony Liguori             break;
206840021f08SAnthony Liguori         }
206940021f08SAnthony Liguori     }
207040021f08SAnthony Liguori     assert(info != NULL);
207140021f08SAnthony Liguori 
207240021f08SAnthony Liguori     return info;
207340021f08SAnthony Liguori }
207440021f08SAnthony Liguori 
207540021f08SAnthony Liguori static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s)
207640021f08SAnthony Liguori {
207740021f08SAnthony Liguori     return eepro100_get_class_by_name(object_get_typename(OBJECT(s)));
207840021f08SAnthony Liguori }
207940021f08SAnthony Liguori 
208039bffca2SAnthony Liguori static Property e100_properties[] = {
208139bffca2SAnthony Liguori     DEFINE_NIC_PROPERTIES(EEPRO100State, conf),
208239bffca2SAnthony Liguori     DEFINE_PROP_END_OF_LIST(),
208339bffca2SAnthony Liguori };
208439bffca2SAnthony Liguori 
208540021f08SAnthony Liguori static void eepro100_class_init(ObjectClass *klass, void *data)
208640021f08SAnthony Liguori {
208739bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
208840021f08SAnthony Liguori     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
208940021f08SAnthony Liguori     E100PCIDeviceInfo *info;
209040021f08SAnthony Liguori 
209140021f08SAnthony Liguori     info = eepro100_get_class_by_name(object_class_get_name(klass));
209240021f08SAnthony Liguori 
2093125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
209439bffca2SAnthony Liguori     dc->props = e100_properties;
209539bffca2SAnthony Liguori     dc->desc = info->desc;
209640021f08SAnthony Liguori     k->vendor_id = PCI_VENDOR_ID_INTEL;
209740021f08SAnthony Liguori     k->class_id = PCI_CLASS_NETWORK_ETHERNET;
209840021f08SAnthony Liguori     k->romfile = "pxe-eepro100.rom";
20999af21dbeSMarkus Armbruster     k->realize = e100_nic_realize;
210040021f08SAnthony Liguori     k->exit = pci_nic_uninit;
210140021f08SAnthony Liguori     k->device_id = info->device_id;
210240021f08SAnthony Liguori     k->revision = info->revision;
210340021f08SAnthony Liguori     k->subsystem_vendor_id = info->subsystem_vendor_id;
210440021f08SAnthony Liguori     k->subsystem_id = info->subsystem_id;
210540021f08SAnthony Liguori }
210640021f08SAnthony Liguori 
210783f7d43aSAndreas Färber static void eepro100_register_types(void)
21089d07d757SPaul Brook {
2109558c8634SStefan Weil     size_t i;
2110558c8634SStefan Weil     for (i = 0; i < ARRAY_SIZE(e100_devices); i++) {
211139bffca2SAnthony Liguori         TypeInfo type_info = {};
211239bffca2SAnthony Liguori         E100PCIDeviceInfo *info = &e100_devices[i];
211340021f08SAnthony Liguori 
211439bffca2SAnthony Liguori         type_info.name = info->name;
211539bffca2SAnthony Liguori         type_info.parent = TYPE_PCI_DEVICE;
211639bffca2SAnthony Liguori         type_info.class_init = eepro100_class_init;
211739bffca2SAnthony Liguori         type_info.instance_size = sizeof(EEPRO100State);
21187317bb17SGonglei         type_info.instance_init = eepro100_instance_init;
211940021f08SAnthony Liguori 
212039bffca2SAnthony Liguori         type_register(&type_info);
2121558c8634SStefan Weil     }
21229d07d757SPaul Brook }
21239d07d757SPaul Brook 
212483f7d43aSAndreas Färber type_init(eepro100_register_types)
2125