1663e8e51Sths /*
2663e8e51Sths * QEMU i8255x (PRO100) emulation
3663e8e51Sths *
41b4f97d6SStefan Weil * Copyright (C) 2006-2011 Stefan Weil
5663e8e51Sths *
6663e8e51Sths * Portions of the code are copies from grub / etherboot eepro100.c
7663e8e51Sths * and linux e100.c.
8663e8e51Sths *
9dc86dd55SPhilippe Mathieu-Daudé * SPDX-License-Identifier: GPL-2.0-or-later
10dc86dd55SPhilippe Mathieu-Daudé *
11230a167cSStefan Weil * This program is free software: you can redistribute it and/or modify
12663e8e51Sths * it under the terms of the GNU General Public License as published by
13230a167cSStefan Weil * the Free Software Foundation, either version 2 of the License, or
14dc86dd55SPhilippe Mathieu-Daudé * (at your option) any later version.
15663e8e51Sths *
16663e8e51Sths * This program is distributed in the hope that it will be useful,
17663e8e51Sths * but WITHOUT ANY WARRANTY; without even the implied warranty of
18663e8e51Sths * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19663e8e51Sths * GNU General Public License for more details.
20663e8e51Sths *
21663e8e51Sths * You should have received a copy of the GNU General Public License
22230a167cSStefan Weil * along with this program. If not, see <http://www.gnu.org/licenses/>.
23663e8e51Sths *
24663e8e51Sths * Tested features (i82559):
25e5e23ab8SStefan Weil * PXE boot (i386 guest, i386 / mips / mipsel / ppc host) ok
26663e8e51Sths * Linux networking (i386) ok
27663e8e51Sths *
28663e8e51Sths * Untested:
29663e8e51Sths * Windows networking
30663e8e51Sths *
31663e8e51Sths * References:
32663e8e51Sths *
33663e8e51Sths * Intel 8255x 10/100 Mbps Ethernet Controller Family
34663e8e51Sths * Open Source Software Developer Manual
35ba19f2deSStefan Weil *
36ba19f2deSStefan Weil * TODO:
37ba19f2deSStefan Weil * * PHY emulation should be separated from nic emulation.
38ba19f2deSStefan Weil * Most nic emulations could share the same phy code.
39ba19f2deSStefan Weil * * i82550 is untested. It is programmed like the i82559.
40ba19f2deSStefan Weil * * i82562 is untested. It is programmed like the i82559.
41ba19f2deSStefan Weil * * Power management (i82558 and later) is not implemented.
42ba19f2deSStefan Weil * * Wake-on-LAN is not implemented.
43663e8e51Sths */
44663e8e51Sths
45e8d40465SPeter Maydell #include "qemu/osdep.h"
46872a2b7cSPhilippe Mathieu-Daudé #include "qemu/units.h"
47edf5ca5dSMarkus Armbruster #include "hw/pci/pci_device.h"
48a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
49d6454270SMarkus Armbruster #include "migration/vmstate.h"
501422e32dSPaolo Bonzini #include "net/net.h"
517c0348bdSMark Cave-Ayland #include "net/eth.h"
520d09e41aSPaolo Bonzini #include "hw/nvram/eeprom93xx.h"
5332cad1ffSPhilippe Mathieu-Daudé #include "system/system.h"
5432cad1ffSPhilippe Mathieu-Daudé #include "system/dma.h"
5532cad1ffSPhilippe Mathieu-Daudé #include "system/reset.h"
56949fc823SMarcel Apfelbaum #include "qemu/bitops.h"
570b8fa32fSMarkus Armbruster #include "qemu/module.h"
589a7c2a59SMao Zhongyi #include "qapi/error.h"
59663e8e51Sths
60792f1d63SStefan Weil /* QEMU sends frames smaller than 60 bytes to ethernet nics.
61792f1d63SStefan Weil * Such frames are rejected by real nics and their emulations.
62792f1d63SStefan Weil * To avoid this behaviour, other nic emulations pad received
63792f1d63SStefan Weil * frames. The following definition enables this padding for
64792f1d63SStefan Weil * eepro100, too. We keep the define around in case it might
65792f1d63SStefan Weil * become useful the future if the core networking is ever
66792f1d63SStefan Weil * changed to pad short packets itself. */
67792f1d63SStefan Weil #define CONFIG_PAD_RECEIVED_FRAMES
68792f1d63SStefan Weil
69aac443e6SStefan Weil /* Debug EEPRO100 card. */
70ce0e58b3SStefan Weil #if 0
71ce0e58b3SStefan Weil # define DEBUG_EEPRO100
72ce0e58b3SStefan Weil #endif
73663e8e51Sths
74663e8e51Sths #ifdef DEBUG_EEPRO100
75001faf32SBlue Swirl #define logout(fmt, ...) fprintf(stderr, "EE100\t%-24s" fmt, __func__, ## __VA_ARGS__)
76663e8e51Sths #else
77001faf32SBlue Swirl #define logout(fmt, ...) ((void)0)
78663e8e51Sths #endif
79663e8e51Sths
80663e8e51Sths /* Set flags to 0 to disable debug output. */
81aac443e6SStefan Weil #define INT 1 /* interrupt related actions */
82aac443e6SStefan Weil #define MDI 1 /* mdi related actions */
83aac443e6SStefan Weil #define OTHER 1
84aac443e6SStefan Weil #define RXTX 1
85aac443e6SStefan Weil #define EEPROM 1 /* eeprom related actions */
86663e8e51Sths
87663e8e51Sths #define TRACE(flag, command) ((flag) ? (command) : (void)0)
88663e8e51Sths
897f1e9d4eSKevin Wolf #define missing(text) fprintf(stderr, "eepro100: feature is missing in this emulation: " text "\n")
90663e8e51Sths
91663e8e51Sths #define MAX_ETH_FRAME_SIZE 1514
92663e8e51Sths
93663e8e51Sths /* This driver supports several different devices which are declared here. */
94c4c270e2SStefan Weil #define i82550 0x82550
95663e8e51Sths #define i82551 0x82551
96c4c270e2SStefan Weil #define i82557A 0x82557a
97663e8e51Sths #define i82557B 0x82557b
98663e8e51Sths #define i82557C 0x82557c
99c4c270e2SStefan Weil #define i82558A 0x82558a
100663e8e51Sths #define i82558B 0x82558b
101c4c270e2SStefan Weil #define i82559A 0x82559a
102c4c270e2SStefan Weil #define i82559B 0x82559b
103663e8e51Sths #define i82559C 0x82559c
104663e8e51Sths #define i82559ER 0x82559e
105663e8e51Sths #define i82562 0x82562
106db667a12SStefan Weil #define i82801 0x82801
107663e8e51Sths
108aac443e6SStefan Weil /* Use 64 word EEPROM. TODO: could be a runtime option. */
109663e8e51Sths #define EEPROM_SIZE 64
110663e8e51Sths
111663e8e51Sths #define PCI_MEM_SIZE (4 * KiB)
112663e8e51Sths #define PCI_IO_SIZE 64
113663e8e51Sths #define PCI_FLASH_SIZE (128 * KiB)
114663e8e51Sths
115663e8e51Sths #define BITS(n, m) (((0xffffffffU << (31 - n)) >> (31 - n + m)) << m)
116663e8e51Sths
117663e8e51Sths /* The SCB accepts the following controls for the Tx and Rx units: */
118663e8e51Sths #define CU_NOP 0x0000 /* No operation. */
119663e8e51Sths #define CU_START 0x0010 /* CU start. */
120663e8e51Sths #define CU_RESUME 0x0020 /* CU resume. */
121663e8e51Sths #define CU_STATSADDR 0x0040 /* Load dump counters address. */
122663e8e51Sths #define CU_SHOWSTATS 0x0050 /* Dump statistical counters. */
123663e8e51Sths #define CU_CMD_BASE 0x0060 /* Load CU base address. */
124663e8e51Sths #define CU_DUMPSTATS 0x0070 /* Dump and reset statistical counters. */
125663e8e51Sths #define CU_SRESUME 0x00a0 /* CU static resume. */
126663e8e51Sths
127663e8e51Sths #define RU_NOP 0x0000
128663e8e51Sths #define RX_START 0x0001
129663e8e51Sths #define RX_RESUME 0x0002
130e824012bSStefan Weil #define RU_ABORT 0x0004
131663e8e51Sths #define RX_ADDR_LOAD 0x0006
132663e8e51Sths #define RX_RESUMENR 0x0007
133663e8e51Sths #define INT_MASK 0x0100
134663e8e51Sths #define DRVR_INT 0x0200 /* Driver generated interrupt. */
135663e8e51Sths
136558c8634SStefan Weil typedef struct {
13739bffca2SAnthony Liguori const char *name;
13839bffca2SAnthony Liguori const char *desc;
13940021f08SAnthony Liguori uint16_t device_id;
14040021f08SAnthony Liguori uint8_t revision;
14140021f08SAnthony Liguori uint16_t subsystem_vendor_id;
14240021f08SAnthony Liguori uint16_t subsystem_id;
14340021f08SAnthony Liguori
144558c8634SStefan Weil uint32_t device;
145558c8634SStefan Weil uint8_t stats_size;
146558c8634SStefan Weil bool has_extended_tcb_support;
147558c8634SStefan Weil bool power_management;
148558c8634SStefan Weil } E100PCIDeviceInfo;
149558c8634SStefan Weil
150663e8e51Sths /* Offsets to the various registers.
151663e8e51Sths All accesses need not be longword aligned. */
152e5e23ab8SStefan Weil typedef enum {
1530908bba1SStefan Weil SCBStatus = 0, /* Status Word. */
154663e8e51Sths SCBAck = 1,
155663e8e51Sths SCBCmd = 2, /* Rx/Command Unit command and status. */
156663e8e51Sths SCBIntmask = 3,
157663e8e51Sths SCBPointer = 4, /* General purpose pointer. */
158663e8e51Sths SCBPort = 8, /* Misc. commands and operands. */
1590908bba1SStefan Weil SCBflash = 12, /* Flash memory control. */
1600908bba1SStefan Weil SCBeeprom = 14, /* EEPROM control. */
161663e8e51Sths SCBCtrlMDI = 16, /* MDI interface control. */
162663e8e51Sths SCBEarlyRx = 20, /* Early receive byte count. */
1630908bba1SStefan Weil SCBFlow = 24, /* Flow Control. */
1640908bba1SStefan Weil SCBpmdr = 27, /* Power Management Driver. */
1650908bba1SStefan Weil SCBgctrl = 28, /* General Control. */
1660908bba1SStefan Weil SCBgstat = 29, /* General Status. */
167e5e23ab8SStefan Weil } E100RegisterOffset;
168663e8e51Sths
169663e8e51Sths /* A speedo3 transmit buffer descriptor with two buffers... */
170663e8e51Sths typedef struct {
171663e8e51Sths uint16_t status;
172663e8e51Sths uint16_t command;
173663e8e51Sths uint32_t link; /* void * */
1747b8737deSStefan Weil uint32_t tbd_array_addr; /* transmit buffer descriptor array address. */
175663e8e51Sths uint16_t tcb_bytes; /* transmit command block byte count (in lower 14 bits */
176663e8e51Sths uint8_t tx_threshold; /* transmit threshold */
177663e8e51Sths uint8_t tbd_count; /* TBD number */
178e7493b25SStefan Weil #if 0
179e7493b25SStefan Weil /* This constitutes two "TBD" entries: hdr and data */
180e7493b25SStefan Weil uint32_t tx_buf_addr0; /* void *, header of frame to be transmitted. */
181e7493b25SStefan Weil int32_t tx_buf_size0; /* Length of Tx hdr. */
182e7493b25SStefan Weil uint32_t tx_buf_addr1; /* void *, data to be transmitted. */
183e7493b25SStefan Weil int32_t tx_buf_size1; /* Length of Tx data. */
184e7493b25SStefan Weil #endif
185c227f099SAnthony Liguori } eepro100_tx_t;
186663e8e51Sths
187663e8e51Sths /* Receive frame descriptor. */
188663e8e51Sths typedef struct {
189663e8e51Sths int16_t status;
190663e8e51Sths uint16_t command;
191663e8e51Sths uint32_t link; /* struct RxFD * */
192663e8e51Sths uint32_t rx_buf_addr; /* void * */
193663e8e51Sths uint16_t count;
194663e8e51Sths uint16_t size;
19527112f18SStefan Weil /* Ethernet frame data follows. */
196c227f099SAnthony Liguori } eepro100_rx_t;
197663e8e51Sths
198ced5296aSStefan Weil typedef enum {
199ced5296aSStefan Weil COMMAND_EL = BIT(15),
200ced5296aSStefan Weil COMMAND_S = BIT(14),
201ced5296aSStefan Weil COMMAND_I = BIT(13),
202ced5296aSStefan Weil COMMAND_NC = BIT(4),
203ced5296aSStefan Weil COMMAND_SF = BIT(3),
204ced5296aSStefan Weil COMMAND_CMD = BITS(2, 0),
205ced5296aSStefan Weil } scb_command_bit;
206ced5296aSStefan Weil
207ced5296aSStefan Weil typedef enum {
208ced5296aSStefan Weil STATUS_C = BIT(15),
209ced5296aSStefan Weil STATUS_OK = BIT(13),
210ced5296aSStefan Weil } scb_status_bit;
211ced5296aSStefan Weil
212663e8e51Sths typedef struct {
213663e8e51Sths uint32_t tx_good_frames, tx_max_collisions, tx_late_collisions,
214663e8e51Sths tx_underruns, tx_lost_crs, tx_deferred, tx_single_collisions,
215663e8e51Sths tx_multiple_collisions, tx_total_collisions;
216663e8e51Sths uint32_t rx_good_frames, rx_crc_errors, rx_alignment_errors,
217663e8e51Sths rx_resource_errors, rx_overrun_errors, rx_cdt_errors,
218663e8e51Sths rx_short_frame_errors;
219663e8e51Sths uint32_t fc_xmt_pause, fc_rcv_pause, fc_rcv_unsupported;
220663e8e51Sths uint16_t xmt_tco_frames, rcv_tco_frames;
221ba42b646SStefan Weil /* TODO: i82559 has six reserved statistics but a total of 24 dwords. */
222ba42b646SStefan Weil uint32_t reserved[4];
223c227f099SAnthony Liguori } eepro100_stats_t;
224663e8e51Sths
225663e8e51Sths typedef enum {
226663e8e51Sths cu_idle = 0,
227663e8e51Sths cu_suspended = 1,
228663e8e51Sths cu_active = 2,
229663e8e51Sths cu_lpq_active = 2,
230663e8e51Sths cu_hqp_active = 3
231c227f099SAnthony Liguori } cu_state_t;
232663e8e51Sths
233663e8e51Sths typedef enum {
234663e8e51Sths ru_idle = 0,
235663e8e51Sths ru_suspended = 1,
236663e8e51Sths ru_no_resources = 2,
237663e8e51Sths ru_ready = 4
238c227f099SAnthony Liguori } ru_state_t;
239663e8e51Sths
240663e8e51Sths typedef struct {
241273a2142SJuan Quintela PCIDevice dev;
242010ec629SStefan Weil /* Hash register (multicast mask array, multiple individual addresses). */
243010ec629SStefan Weil uint8_t mult[8];
2445e6ffddeSAvi Kivity MemoryRegion mmio_bar;
2455e6ffddeSAvi Kivity MemoryRegion io_bar;
2465e6ffddeSAvi Kivity MemoryRegion flash_bar;
247e00e365eSMark McLoughlin NICState *nic;
248508ef936SGerd Hoffmann NICConf conf;
249663e8e51Sths uint8_t scb_stat; /* SCB stat/ack byte */
250663e8e51Sths uint8_t int_stat; /* PCI interrupt status */
2513706c43fSStefan Weil /* region must not be saved by nic_save. */
252663e8e51Sths uint16_t mdimem[32];
253c227f099SAnthony Liguori eeprom_t *eeprom;
254663e8e51Sths uint32_t device; /* device variant */
255663e8e51Sths /* (cu_base + cu_offset) address the next command block in the command block list. */
256663e8e51Sths uint32_t cu_base; /* CU base address */
257663e8e51Sths uint32_t cu_offset; /* CU address offset */
258663e8e51Sths /* (ru_base + ru_offset) address the RFD in the Receive Frame Area. */
259663e8e51Sths uint32_t ru_base; /* RU base address */
260663e8e51Sths uint32_t ru_offset; /* RU address offset */
261c227f099SAnthony Liguori uint32_t statsaddr; /* pointer to eepro100_stats_t */
262ba42b646SStefan Weil
263f3a52e50SStefan Weil /* Temporary status information (no need to save these values),
264f3a52e50SStefan Weil * used while processing CU commands. */
265f3a52e50SStefan Weil eepro100_tx_t tx; /* transmit buffer descriptor */
266f3a52e50SStefan Weil uint32_t cb_address; /* = cu_base + cu_offset */
267f3a52e50SStefan Weil
268ba42b646SStefan Weil /* Statistical counters. Also used for wake-up packet (i82559). */
269ba42b646SStefan Weil eepro100_stats_t statistics;
270ba42b646SStefan Weil
271e5e23ab8SStefan Weil /* Data in mem is always in the byte order of the controller (le).
272e5e23ab8SStefan Weil * It must be dword aligned to allow direct access to 32 bit values. */
2733a93113aSDong Xu Wang uint8_t mem[PCI_MEM_SIZE] __attribute__((aligned(8)));
274e5e23ab8SStefan Weil
275663e8e51Sths /* Configuration bytes. */
276663e8e51Sths uint8_t configuration[22];
277663e8e51Sths
278151b2986SJuan Quintela /* vmstate for each particular nic */
279151b2986SJuan Quintela VMStateDescription *vmstate;
280ba42b646SStefan Weil
281ba42b646SStefan Weil /* Quasi static device properties (no need to save them). */
282ba42b646SStefan Weil uint16_t stats_size;
283ba42b646SStefan Weil bool has_extended_tcb_support;
284663e8e51Sths } EEPRO100State;
285663e8e51Sths
2866cded3a4SStefan Weil /* Word indices in EEPROM. */
2876cded3a4SStefan Weil typedef enum {
2886cded3a4SStefan Weil EEPROM_CNFG_MDIX = 0x03,
2896cded3a4SStefan Weil EEPROM_ID = 0x05,
2906cded3a4SStefan Weil EEPROM_PHY_ID = 0x06,
2916cded3a4SStefan Weil EEPROM_VENDOR_ID = 0x0c,
2926cded3a4SStefan Weil EEPROM_CONFIG_ASF = 0x0d,
2936cded3a4SStefan Weil EEPROM_DEVICE_ID = 0x23,
2946cded3a4SStefan Weil EEPROM_SMBUS_ADDR = 0x90,
2956cded3a4SStefan Weil } EEPROMOffset;
2966cded3a4SStefan Weil
297b1e87018SStefan Weil /* Bit values for EEPROM ID word. */
298b1e87018SStefan Weil typedef enum {
299b1e87018SStefan Weil EEPROM_ID_MDM = BIT(0), /* Modem */
300b1e87018SStefan Weil EEPROM_ID_STB = BIT(1), /* Standby Enable */
301b1e87018SStefan Weil EEPROM_ID_WMR = BIT(2), /* ??? */
302b1e87018SStefan Weil EEPROM_ID_WOL = BIT(5), /* Wake on LAN */
303b1e87018SStefan Weil EEPROM_ID_DPD = BIT(6), /* Deep Power Down */
304b1e87018SStefan Weil EEPROM_ID_ALT = BIT(7), /* */
305b1e87018SStefan Weil /* BITS(10, 8) device revision */
306b1e87018SStefan Weil EEPROM_ID_BD = BIT(11), /* boot disable */
307b1e87018SStefan Weil EEPROM_ID_ID = BIT(13), /* id bit */
308b1e87018SStefan Weil /* BITS(15, 14) signature */
309b1e87018SStefan Weil EEPROM_ID_VALID = BIT(14), /* signature for valid eeprom */
310b1e87018SStefan Weil } eeprom_id_bit;
311b1e87018SStefan Weil
312663e8e51Sths /* Default values for MDI (PHY) registers */
313663e8e51Sths static const uint16_t eepro100_mdi_default[] = {
314663e8e51Sths /* MDI Registers 0 - 6, 7 */
315663e8e51Sths 0x3000, 0x780d, 0x02a8, 0x0154, 0x05e1, 0x0000, 0x0000, 0x0000,
316663e8e51Sths /* MDI Registers 8 - 15 */
317663e8e51Sths 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
318663e8e51Sths /* MDI Registers 16 - 31 */
319663e8e51Sths 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
320663e8e51Sths 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
321663e8e51Sths };
322663e8e51Sths
323663e8e51Sths /* Readonly mask for MDI (PHY) registers */
324663e8e51Sths static const uint16_t eepro100_mdi_mask[] = {
325663e8e51Sths 0x0000, 0xffff, 0xffff, 0xffff, 0xc01f, 0xffff, 0xffff, 0x0000,
326663e8e51Sths 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
327663e8e51Sths 0x0fff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
328663e8e51Sths 0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
329663e8e51Sths };
330663e8e51Sths
33140021f08SAnthony Liguori static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s);
33240021f08SAnthony Liguori
333e5e23ab8SStefan Weil /* Read a 16 bit control/status (CSR) register. */
e100_read_reg2(EEPRO100State * s,E100RegisterOffset addr)334e5e23ab8SStefan Weil static uint16_t e100_read_reg2(EEPRO100State *s, E100RegisterOffset addr)
335e5e23ab8SStefan Weil {
336e5e23ab8SStefan Weil assert(!((uintptr_t)&s->mem[addr] & 1));
3374d9be252SPeter Maydell return lduw_le_p(&s->mem[addr]);
338e5e23ab8SStefan Weil }
339e5e23ab8SStefan Weil
340e5e23ab8SStefan Weil /* Read a 32 bit control/status (CSR) register. */
e100_read_reg4(EEPRO100State * s,E100RegisterOffset addr)341e5e23ab8SStefan Weil static uint32_t e100_read_reg4(EEPRO100State *s, E100RegisterOffset addr)
342e5e23ab8SStefan Weil {
343e5e23ab8SStefan Weil assert(!((uintptr_t)&s->mem[addr] & 3));
3444d9be252SPeter Maydell return ldl_le_p(&s->mem[addr]);
345e5e23ab8SStefan Weil }
346e5e23ab8SStefan Weil
347e5e23ab8SStefan Weil /* Write a 16 bit control/status (CSR) register. */
e100_write_reg2(EEPRO100State * s,E100RegisterOffset addr,uint16_t val)348e5e23ab8SStefan Weil static void e100_write_reg2(EEPRO100State *s, E100RegisterOffset addr,
349e5e23ab8SStefan Weil uint16_t val)
350e5e23ab8SStefan Weil {
351e5e23ab8SStefan Weil assert(!((uintptr_t)&s->mem[addr] & 1));
3524d9be252SPeter Maydell stw_le_p(&s->mem[addr], val);
353e5e23ab8SStefan Weil }
354e5e23ab8SStefan Weil
355e5e23ab8SStefan Weil /* Read a 32 bit control/status (CSR) register. */
e100_write_reg4(EEPRO100State * s,E100RegisterOffset addr,uint32_t val)356e5e23ab8SStefan Weil static void e100_write_reg4(EEPRO100State *s, E100RegisterOffset addr,
357e5e23ab8SStefan Weil uint32_t val)
358e5e23ab8SStefan Weil {
359e5e23ab8SStefan Weil assert(!((uintptr_t)&s->mem[addr] & 3));
3604d9be252SPeter Maydell stl_le_p(&s->mem[addr], val);
361e5e23ab8SStefan Weil }
362e5e23ab8SStefan Weil
363663e8e51Sths #if defined(DEBUG_EEPRO100)
nic_dump(const uint8_t * buf,unsigned size)364663e8e51Sths static const char *nic_dump(const uint8_t * buf, unsigned size)
365663e8e51Sths {
366663e8e51Sths static char dump[3 * 16 + 1];
367663e8e51Sths char *p = &dump[0];
368aac443e6SStefan Weil if (size > 16) {
369663e8e51Sths size = 16;
370aac443e6SStefan Weil }
371663e8e51Sths while (size-- > 0) {
372663e8e51Sths p += sprintf(p, " %02x", *buf++);
373663e8e51Sths }
374663e8e51Sths return dump;
375663e8e51Sths }
376663e8e51Sths #endif /* DEBUG_EEPRO100 */
377663e8e51Sths
378663e8e51Sths enum scb_stat_ack {
379663e8e51Sths stat_ack_not_ours = 0x00,
380663e8e51Sths stat_ack_sw_gen = 0x04,
381663e8e51Sths stat_ack_rnr = 0x10,
382663e8e51Sths stat_ack_cu_idle = 0x20,
383663e8e51Sths stat_ack_frame_rx = 0x40,
384663e8e51Sths stat_ack_cu_cmd_done = 0x80,
385663e8e51Sths stat_ack_not_present = 0xFF,
386663e8e51Sths stat_ack_rx = (stat_ack_sw_gen | stat_ack_rnr | stat_ack_frame_rx),
387663e8e51Sths stat_ack_tx = (stat_ack_cu_idle | stat_ack_cu_cmd_done),
388663e8e51Sths };
389663e8e51Sths
disable_interrupt(EEPRO100State * s)390663e8e51Sths static void disable_interrupt(EEPRO100State * s)
391663e8e51Sths {
392663e8e51Sths if (s->int_stat) {
393aac443e6SStefan Weil TRACE(INT, logout("interrupt disabled\n"));
3949e64f8a3SMarcel Apfelbaum pci_irq_deassert(&s->dev);
395663e8e51Sths s->int_stat = 0;
396663e8e51Sths }
397663e8e51Sths }
398663e8e51Sths
enable_interrupt(EEPRO100State * s)399663e8e51Sths static void enable_interrupt(EEPRO100State * s)
400663e8e51Sths {
401663e8e51Sths if (!s->int_stat) {
402aac443e6SStefan Weil TRACE(INT, logout("interrupt enabled\n"));
4039e64f8a3SMarcel Apfelbaum pci_irq_assert(&s->dev);
404663e8e51Sths s->int_stat = 1;
405663e8e51Sths }
406663e8e51Sths }
407663e8e51Sths
eepro100_acknowledge(EEPRO100State * s)408663e8e51Sths static void eepro100_acknowledge(EEPRO100State * s)
409663e8e51Sths {
410663e8e51Sths s->scb_stat &= ~s->mem[SCBAck];
411663e8e51Sths s->mem[SCBAck] = s->scb_stat;
412663e8e51Sths if (s->scb_stat == 0) {
413663e8e51Sths disable_interrupt(s);
414663e8e51Sths }
415663e8e51Sths }
416663e8e51Sths
eepro100_interrupt(EEPRO100State * s,uint8_t status)417e715c8e8SStefan Weil static void eepro100_interrupt(EEPRO100State * s, uint8_t status)
418663e8e51Sths {
419663e8e51Sths uint8_t mask = ~s->mem[SCBIntmask];
420e715c8e8SStefan Weil s->mem[SCBAck] |= status;
421e715c8e8SStefan Weil status = s->scb_stat = s->mem[SCBAck];
422e715c8e8SStefan Weil status &= (mask | 0x0f);
423e7493b25SStefan Weil #if 0
424e7493b25SStefan Weil status &= (~s->mem[SCBIntmask] | 0x0xf);
425e7493b25SStefan Weil #endif
426e715c8e8SStefan Weil if (status && (mask & 0x01)) {
427663e8e51Sths /* SCB mask and SCB Bit M do not disable interrupt. */
428663e8e51Sths enable_interrupt(s);
429663e8e51Sths } else if (s->int_stat) {
430663e8e51Sths disable_interrupt(s);
431663e8e51Sths }
432663e8e51Sths }
433663e8e51Sths
eepro100_cx_interrupt(EEPRO100State * s)434663e8e51Sths static void eepro100_cx_interrupt(EEPRO100State * s)
435663e8e51Sths {
436663e8e51Sths /* CU completed action command. */
437663e8e51Sths /* Transmit not ok (82557 only, not in emulation). */
438663e8e51Sths eepro100_interrupt(s, 0x80);
439663e8e51Sths }
440663e8e51Sths
eepro100_cna_interrupt(EEPRO100State * s)441663e8e51Sths static void eepro100_cna_interrupt(EEPRO100State * s)
442663e8e51Sths {
443663e8e51Sths /* CU left the active state. */
444663e8e51Sths eepro100_interrupt(s, 0x20);
445663e8e51Sths }
446663e8e51Sths
eepro100_fr_interrupt(EEPRO100State * s)447663e8e51Sths static void eepro100_fr_interrupt(EEPRO100State * s)
448663e8e51Sths {
449663e8e51Sths /* RU received a complete frame. */
450663e8e51Sths eepro100_interrupt(s, 0x40);
451663e8e51Sths }
452663e8e51Sths
eepro100_rnr_interrupt(EEPRO100State * s)453663e8e51Sths static void eepro100_rnr_interrupt(EEPRO100State * s)
454663e8e51Sths {
455663e8e51Sths /* RU is not ready. */
456663e8e51Sths eepro100_interrupt(s, 0x10);
457663e8e51Sths }
458663e8e51Sths
eepro100_mdi_interrupt(EEPRO100State * s)459663e8e51Sths static void eepro100_mdi_interrupt(EEPRO100State * s)
460663e8e51Sths {
461663e8e51Sths /* MDI completed read or write cycle. */
462663e8e51Sths eepro100_interrupt(s, 0x08);
463663e8e51Sths }
464663e8e51Sths
eepro100_swi_interrupt(EEPRO100State * s)465663e8e51Sths static void eepro100_swi_interrupt(EEPRO100State * s)
466663e8e51Sths {
467663e8e51Sths /* Software has requested an interrupt. */
468663e8e51Sths eepro100_interrupt(s, 0x04);
469663e8e51Sths }
470663e8e51Sths
471663e8e51Sths #if 0
472663e8e51Sths static void eepro100_fcp_interrupt(EEPRO100State * s)
473663e8e51Sths {
474663e8e51Sths /* Flow control pause interrupt (82558 and later). */
475663e8e51Sths eepro100_interrupt(s, 0x01);
476663e8e51Sths }
477663e8e51Sths #endif
478663e8e51Sths
e100_pci_reset(EEPRO100State * s,Error ** errp)4799a7c2a59SMao Zhongyi static void e100_pci_reset(EEPRO100State *s, Error **errp)
480663e8e51Sths {
48140021f08SAnthony Liguori E100PCIDeviceInfo *info = eepro100_get_class(s);
482663e8e51Sths uint32_t device = s->device;
483273a2142SJuan Quintela uint8_t *pci_conf = s->dev.config;
484663e8e51Sths
485aac443e6SStefan Weil TRACE(OTHER, logout("%p\n", s));
486663e8e51Sths
487663e8e51Sths /* PCI Status */
488558c8634SStefan Weil pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM |
489558c8634SStefan Weil PCI_STATUS_FAST_BACK);
490663e8e51Sths /* PCI Latency Timer */
49115e89f59SMichael S. Tsirkin pci_set_byte(pci_conf + PCI_LATENCY_TIMER, 0x20); /* latency timer = 32 clocks */
492ae543b49SStefan Weil /* Capability Pointer is set by PCI framework. */
493f62719caSStefan Weil /* Interrupt Line */
494f62719caSStefan Weil /* Interrupt Pin */
495f62719caSStefan Weil pci_set_byte(pci_conf + PCI_INTERRUPT_PIN, 1); /* interrupt pin A */
496663e8e51Sths /* Minimum Grant */
49715e89f59SMichael S. Tsirkin pci_set_byte(pci_conf + PCI_MIN_GNT, 0x08);
498663e8e51Sths /* Maximum Latency */
49915e89f59SMichael S. Tsirkin pci_set_byte(pci_conf + PCI_MAX_LAT, 0x18);
500663e8e51Sths
50140021f08SAnthony Liguori s->stats_size = info->stats_size;
50240021f08SAnthony Liguori s->has_extended_tcb_support = info->has_extended_tcb_support;
503558c8634SStefan Weil
504663e8e51Sths switch (device) {
505ba42b646SStefan Weil case i82550:
506663e8e51Sths case i82551:
507ba42b646SStefan Weil case i82557A:
508663e8e51Sths case i82557B:
509663e8e51Sths case i82557C:
510ba42b646SStefan Weil case i82558A:
511663e8e51Sths case i82558B:
512ba42b646SStefan Weil case i82559A:
513ba42b646SStefan Weil case i82559B:
514558c8634SStefan Weil case i82559ER:
515558c8634SStefan Weil case i82562:
516db667a12SStefan Weil case i82801:
517663e8e51Sths case i82559C:
518ba42b646SStefan Weil break;
519663e8e51Sths default:
520663e8e51Sths logout("Device %X is undefined!\n", device);
521663e8e51Sths }
522663e8e51Sths
5233dec59a1SStefan Weil /* Standard TxCB. */
5243dec59a1SStefan Weil s->configuration[6] |= BIT(4);
5253dec59a1SStefan Weil
526558c8634SStefan Weil /* Standard statistical counters. */
527ba42b646SStefan Weil s->configuration[6] |= BIT(5);
528ba42b646SStefan Weil
529ba42b646SStefan Weil if (s->stats_size == 80) {
530ba42b646SStefan Weil /* TODO: check TCO Statistical Counters bit. Documentation not clear. */
531ba42b646SStefan Weil if (s->configuration[6] & BIT(2)) {
532ba42b646SStefan Weil /* TCO statistical counters. */
533ba42b646SStefan Weil assert(s->configuration[6] & BIT(5));
534ba42b646SStefan Weil } else {
535ba42b646SStefan Weil if (s->configuration[6] & BIT(5)) {
536ba42b646SStefan Weil /* No extended statistical counters, i82557 compatible. */
537ba42b646SStefan Weil s->stats_size = 64;
538ba42b646SStefan Weil } else {
539ba42b646SStefan Weil /* i82558 compatible. */
540ba42b646SStefan Weil s->stats_size = 76;
541ba42b646SStefan Weil }
542ba42b646SStefan Weil }
543ba42b646SStefan Weil } else {
544ba42b646SStefan Weil if (s->configuration[6] & BIT(5)) {
545ba42b646SStefan Weil /* No extended statistical counters. */
546ba42b646SStefan Weil s->stats_size = 64;
547ba42b646SStefan Weil }
548ba42b646SStefan Weil }
549ba42b646SStefan Weil assert(s->stats_size > 0 && s->stats_size <= sizeof(s->statistics));
550ba42b646SStefan Weil
55140021f08SAnthony Liguori if (info->power_management) {
552ba42b646SStefan Weil /* Power Management Capabilities */
5538bbd1ce2SMichael S. Tsirkin int cfg_offset = 0xdc;
5540681ec25SAlex Williamson int r = pci_pm_init(&s->dev, cfg_offset, errp);
5559a7c2a59SMao Zhongyi if (r < 0) {
5569a7c2a59SMao Zhongyi return;
5579a7c2a59SMao Zhongyi }
5589a7c2a59SMao Zhongyi
559ae543b49SStefan Weil pci_set_word(pci_conf + cfg_offset + PCI_PM_PMC, 0x7e21);
560ae543b49SStefan Weil #if 0 /* TODO: replace dummy code for power management emulation. */
561ba42b646SStefan Weil /* TODO: Power Management Control / Status. */
562ae543b49SStefan Weil pci_set_word(pci_conf + cfg_offset + PCI_PM_CTRL, 0x0000);
563ba42b646SStefan Weil /* TODO: Ethernet Power Consumption Registers (i82559 and later). */
564ae543b49SStefan Weil pci_set_byte(pci_conf + cfg_offset + PCI_PM_PPB_EXTENSIONS, 0x0000);
565ae543b49SStefan Weil #endif
566ae543b49SStefan Weil }
567ba42b646SStefan Weil
568ba42b646SStefan Weil #if EEPROM_SIZE > 0
569663e8e51Sths if (device == i82557C || device == i82558B || device == i82559C) {
570e7493b25SStefan Weil /*
571e7493b25SStefan Weil TODO: get vendor id from EEPROM for i82557C or later.
572e7493b25SStefan Weil TODO: get device id from EEPROM for i82557C or later.
573e7493b25SStefan Weil TODO: status bit 4 can be disabled by EEPROM for i82558, i82559.
574e7493b25SStefan Weil TODO: header type is determined by EEPROM for i82559.
575e7493b25SStefan Weil TODO: get subsystem id from EEPROM for i82557C or later.
576e7493b25SStefan Weil TODO: get subsystem vendor id from EEPROM for i82557C or later.
577e7493b25SStefan Weil TODO: exp. rom baddr depends on a bit in EEPROM for i82558 or later.
578e7493b25SStefan Weil TODO: capability pointer depends on EEPROM for i82558.
579e7493b25SStefan Weil */
580663e8e51Sths logout("Get device id and revision from EEPROM!!!\n");
581663e8e51Sths }
582ba42b646SStefan Weil #endif /* EEPROM_SIZE > 0 */
583663e8e51Sths }
584663e8e51Sths
nic_selective_reset(EEPRO100State * s)585663e8e51Sths static void nic_selective_reset(EEPRO100State * s)
586663e8e51Sths {
587663e8e51Sths size_t i;
588663e8e51Sths uint16_t *eeprom_contents = eeprom93xx_data(s->eeprom);
589e7493b25SStefan Weil #if 0
590e7493b25SStefan Weil eeprom93xx_reset(s->eeprom);
591e7493b25SStefan Weil #endif
592508ef936SGerd Hoffmann memcpy(eeprom_contents, s->conf.macaddr.a, 6);
593b1e87018SStefan Weil eeprom_contents[EEPROM_ID] = EEPROM_ID_VALID;
594f4e94dfeS=?UTF-8?q?Reimar=20D=C3=B6ffinger?= if (s->device == i82557B || s->device == i82557C)
595f4e94dfeS=?UTF-8?q?Reimar=20D=C3=B6ffinger?= eeprom_contents[5] = 0x0100;
5966cded3a4SStefan Weil eeprom_contents[EEPROM_PHY_ID] = 1;
597663e8e51Sths uint16_t sum = 0;
598663e8e51Sths for (i = 0; i < EEPROM_SIZE - 1; i++) {
599663e8e51Sths sum += eeprom_contents[i];
600663e8e51Sths }
601663e8e51Sths eeprom_contents[EEPROM_SIZE - 1] = 0xbaba - sum;
602aac443e6SStefan Weil TRACE(EEPROM, logout("checksum=0x%04x\n", eeprom_contents[EEPROM_SIZE - 1]));
603663e8e51Sths
604663e8e51Sths memset(s->mem, 0, sizeof(s->mem));
605e5e23ab8SStefan Weil e100_write_reg4(s, SCBCtrlMDI, BIT(21));
606663e8e51Sths
607663e8e51Sths assert(sizeof(s->mdimem) == sizeof(eepro100_mdi_default));
608663e8e51Sths memcpy(&s->mdimem[0], &eepro100_mdi_default[0], sizeof(s->mdimem));
609663e8e51Sths }
610663e8e51Sths
nic_reset(void * opaque)611663e8e51Sths static void nic_reset(void *opaque)
612663e8e51Sths {
613769cf7a5SJuan Quintela EEPRO100State *s = opaque;
614aac443e6SStefan Weil TRACE(OTHER, logout("%p\n", s));
615010ec629SStefan Weil /* TODO: Clearing of hash register for selective reset, too? */
6167b8737deSStefan Weil memset(&s->mult[0], 0, sizeof(s->mult));
617663e8e51Sths nic_selective_reset(s);
618663e8e51Sths }
619663e8e51Sths
620663e8e51Sths #if defined(DEBUG_EEPRO100)
621b8f6ba0dSStefan Weil static const char * const e100_reg[PCI_IO_SIZE / 4] = {
622663e8e51Sths "Command/Status",
623663e8e51Sths "General Pointer",
624663e8e51Sths "Port",
625663e8e51Sths "EEPROM/Flash Control",
626663e8e51Sths "MDI Control",
627663e8e51Sths "Receive DMA Byte Count",
628b8f6ba0dSStefan Weil "Flow Control",
629663e8e51Sths "General Status/Control"
630663e8e51Sths };
631663e8e51Sths
regname(uint32_t addr)632663e8e51Sths static char *regname(uint32_t addr)
633663e8e51Sths {
634ec169288SDavid Benjamin static char buf[32];
635663e8e51Sths if (addr < PCI_IO_SIZE) {
636b8f6ba0dSStefan Weil const char *r = e100_reg[addr / 4];
637663e8e51Sths if (r != 0) {
63841cbc23cSStefan Weil snprintf(buf, sizeof(buf), "%s+%u", r, addr % 4);
639663e8e51Sths } else {
64041cbc23cSStefan Weil snprintf(buf, sizeof(buf), "0x%02x", addr);
641663e8e51Sths }
642663e8e51Sths } else {
64341cbc23cSStefan Weil snprintf(buf, sizeof(buf), "??? 0x%08x", addr);
644663e8e51Sths }
645663e8e51Sths return buf;
646663e8e51Sths }
647663e8e51Sths #endif /* DEBUG_EEPRO100 */
648663e8e51Sths
649663e8e51Sths /*****************************************************************************
650663e8e51Sths *
651663e8e51Sths * Command emulation.
652663e8e51Sths *
653663e8e51Sths ****************************************************************************/
654663e8e51Sths
655663e8e51Sths #if 0
656663e8e51Sths static uint16_t eepro100_read_command(EEPRO100State * s)
657663e8e51Sths {
658663e8e51Sths uint16_t val = 0xffff;
659e7493b25SStefan Weil TRACE(OTHER, logout("val=0x%04x\n", val));
660663e8e51Sths return val;
661663e8e51Sths }
662663e8e51Sths #endif
663663e8e51Sths
664663e8e51Sths /* Commands that can be put in a command list entry. */
665663e8e51Sths enum commands {
666663e8e51Sths CmdNOp = 0,
667663e8e51Sths CmdIASetup = 1,
668663e8e51Sths CmdConfigure = 2,
669663e8e51Sths CmdMulticastList = 3,
670663e8e51Sths CmdTx = 4,
671663e8e51Sths CmdTDR = 5, /* load microcode */
672663e8e51Sths CmdDump = 6,
673663e8e51Sths CmdDiagnose = 7,
674663e8e51Sths
675663e8e51Sths /* And some extra flags: */
676663e8e51Sths CmdSuspend = 0x4000, /* Suspend after completion. */
677663e8e51Sths CmdIntr = 0x2000, /* Interrupt after completion. */
678663e8e51Sths CmdTxFlex = 0x0008, /* Use "Flexible mode" for CmdTx command. */
679663e8e51Sths };
680663e8e51Sths
get_cu_state(EEPRO100State * s)681c227f099SAnthony Liguori static cu_state_t get_cu_state(EEPRO100State * s)
682663e8e51Sths {
683ced5296aSStefan Weil return ((s->mem[SCBStatus] & BITS(7, 6)) >> 6);
684663e8e51Sths }
685663e8e51Sths
set_cu_state(EEPRO100State * s,cu_state_t state)686c227f099SAnthony Liguori static void set_cu_state(EEPRO100State * s, cu_state_t state)
687663e8e51Sths {
688ced5296aSStefan Weil s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(7, 6)) + (state << 6);
689663e8e51Sths }
690663e8e51Sths
get_ru_state(EEPRO100State * s)691c227f099SAnthony Liguori static ru_state_t get_ru_state(EEPRO100State * s)
692663e8e51Sths {
693ced5296aSStefan Weil return ((s->mem[SCBStatus] & BITS(5, 2)) >> 2);
694663e8e51Sths }
695663e8e51Sths
set_ru_state(EEPRO100State * s,ru_state_t state)696c227f099SAnthony Liguori static void set_ru_state(EEPRO100State * s, ru_state_t state)
697663e8e51Sths {
698ced5296aSStefan Weil s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(5, 2)) + (state << 2);
699663e8e51Sths }
700663e8e51Sths
dump_statistics(EEPRO100State * s)701663e8e51Sths static void dump_statistics(EEPRO100State * s)
702663e8e51Sths {
703a423a1b5SPhilippe Mathieu-Daudé const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
704a423a1b5SPhilippe Mathieu-Daudé
705663e8e51Sths /* Dump statistical data. Most data is never changed by the emulation
706663e8e51Sths * and always 0, so we first just copy the whole block and then those
707663e8e51Sths * values which really matter.
708663e8e51Sths * Number of data should check configuration!!!
709663e8e51Sths */
710e965d4bcSDavid Gibson pci_dma_write(&s->dev, s->statsaddr, &s->statistics, s->stats_size);
71116ef60c9SEduard - Gabriel Munteanu stl_le_pci_dma(&s->dev, s->statsaddr + 0,
712a423a1b5SPhilippe Mathieu-Daudé s->statistics.tx_good_frames, attrs);
71316ef60c9SEduard - Gabriel Munteanu stl_le_pci_dma(&s->dev, s->statsaddr + 36,
714a423a1b5SPhilippe Mathieu-Daudé s->statistics.rx_good_frames, attrs);
71516ef60c9SEduard - Gabriel Munteanu stl_le_pci_dma(&s->dev, s->statsaddr + 48,
716a423a1b5SPhilippe Mathieu-Daudé s->statistics.rx_resource_errors, attrs);
71716ef60c9SEduard - Gabriel Munteanu stl_le_pci_dma(&s->dev, s->statsaddr + 60,
718a423a1b5SPhilippe Mathieu-Daudé s->statistics.rx_short_frame_errors, attrs);
719e7493b25SStefan Weil #if 0
720a423a1b5SPhilippe Mathieu-Daudé stw_le_pci_dma(&s->dev, s->statsaddr + 76,
721a423a1b5SPhilippe Mathieu-Daudé s->statistics.xmt_tco_frames, attrs);
722a423a1b5SPhilippe Mathieu-Daudé stw_le_pci_dma(&s->dev, s->statsaddr + 78,
723a423a1b5SPhilippe Mathieu-Daudé s->statistics.rcv_tco_frames, attrs);
724e7493b25SStefan Weil missing("CU dump statistical counters");
725e7493b25SStefan Weil #endif
726663e8e51Sths }
727663e8e51Sths
read_cb(EEPRO100State * s)7283d0f4b9bSStefan Weil static void read_cb(EEPRO100State *s)
7293d0f4b9bSStefan Weil {
730e965d4bcSDavid Gibson pci_dma_read(&s->dev, s->cb_address, &s->tx, sizeof(s->tx));
7313d0f4b9bSStefan Weil s->tx.status = le16_to_cpu(s->tx.status);
7323d0f4b9bSStefan Weil s->tx.command = le16_to_cpu(s->tx.command);
7333d0f4b9bSStefan Weil s->tx.link = le32_to_cpu(s->tx.link);
7343d0f4b9bSStefan Weil s->tx.tbd_array_addr = le32_to_cpu(s->tx.tbd_array_addr);
7353d0f4b9bSStefan Weil s->tx.tcb_bytes = le16_to_cpu(s->tx.tcb_bytes);
7363d0f4b9bSStefan Weil }
7373d0f4b9bSStefan Weil
tx_command(EEPRO100State * s)738f3a52e50SStefan Weil static void tx_command(EEPRO100State *s)
739663e8e51Sths {
740398f9a84SPhilippe Mathieu-Daudé const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
7418f8e8053SThomas Huth uint32_t tbd_array = s->tx.tbd_array_addr;
7428f8e8053SThomas Huth uint16_t tcb_bytes = s->tx.tcb_bytes & 0x3fff;
743f3a52e50SStefan Weil /* Sends larger than MAX_ETH_FRAME_SIZE are allowed, up to 2600 bytes. */
744f3a52e50SStefan Weil uint8_t buf[2600];
745f3a52e50SStefan Weil uint16_t size = 0;
746f3a52e50SStefan Weil uint32_t tbd_address = s->cb_address + 0x10;
747aac443e6SStefan Weil TRACE(RXTX, logout
748663e8e51Sths ("transmit, TBD array address 0x%08x, TCB byte count 0x%04x, TBD count %u\n",
749f3a52e50SStefan Weil tbd_array, tcb_bytes, s->tx.tbd_count));
7507f1e9d4eSKevin Wolf
7517f1e9d4eSKevin Wolf if (tcb_bytes > 2600) {
7527f1e9d4eSKevin Wolf logout("TCB byte count too large, using 2600\n");
7537f1e9d4eSKevin Wolf tcb_bytes = 2600;
7547f1e9d4eSKevin Wolf }
755663e8e51Sths if (!((tcb_bytes > 0) || (tbd_array != 0xffffffff))) {
756663e8e51Sths logout
757663e8e51Sths ("illegal values of TBD array address and TCB byte count!\n");
758663e8e51Sths }
759663e8e51Sths assert(tcb_bytes <= sizeof(buf));
760663e8e51Sths while (size < tcb_bytes) {
761aac443e6SStefan Weil TRACE(RXTX, logout
762663e8e51Sths ("TBD (simplified mode): buffer address 0x%08x, size 0x%04x\n",
7631865e288SMike Nawrocki tbd_address, tcb_bytes));
7641865e288SMike Nawrocki pci_dma_read(&s->dev, tbd_address, &buf[size], tcb_bytes);
7651865e288SMike Nawrocki size += tcb_bytes;
766663e8e51Sths }
767663e8e51Sths if (tbd_array == 0xffffffff) {
768663e8e51Sths /* Simplified mode. Was already handled by code above. */
769663e8e51Sths } else {
770663e8e51Sths /* Flexible mode. */
771663e8e51Sths uint8_t tbd_count = 0;
7724a63054bSPhilippe Mathieu-Daudé uint32_t tx_buffer_address;
7734a63054bSPhilippe Mathieu-Daudé uint16_t tx_buffer_size;
7744a63054bSPhilippe Mathieu-Daudé uint16_t tx_buffer_el;
7754a63054bSPhilippe Mathieu-Daudé
776ba42b646SStefan Weil if (s->has_extended_tcb_support && !(s->configuration[6] & BIT(4))) {
7773f9cb1c1SNaphtali Sprei /* Extended Flexible TCB. */
778663e8e51Sths for (; tbd_count < 2; tbd_count++) {
7794a63054bSPhilippe Mathieu-Daudé ldl_le_pci_dma(&s->dev, tbd_address, &tx_buffer_address, attrs);
7804a63054bSPhilippe Mathieu-Daudé lduw_le_pci_dma(&s->dev, tbd_address + 4, &tx_buffer_size, attrs);
7814a63054bSPhilippe Mathieu-Daudé lduw_le_pci_dma(&s->dev, tbd_address + 6, &tx_buffer_el, attrs);
782663e8e51Sths tbd_address += 8;
783aac443e6SStefan Weil TRACE(RXTX, logout
7843f9cb1c1SNaphtali Sprei ("TBD (extended flexible mode): buffer address 0x%08x, size 0x%04x\n",
785aac443e6SStefan Weil tx_buffer_address, tx_buffer_size));
78624e6f355SReimar Döffinger tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size);
78716ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, tx_buffer_address,
78816ef60c9SEduard - Gabriel Munteanu &buf[size], tx_buffer_size);
789663e8e51Sths size += tx_buffer_size;
790663e8e51Sths if (tx_buffer_el & 1) {
791663e8e51Sths break;
792663e8e51Sths }
793663e8e51Sths }
794663e8e51Sths }
795663e8e51Sths tbd_address = tbd_array;
796f3a52e50SStefan Weil for (; tbd_count < s->tx.tbd_count; tbd_count++) {
7974a63054bSPhilippe Mathieu-Daudé ldl_le_pci_dma(&s->dev, tbd_address, &tx_buffer_address, attrs);
7984a63054bSPhilippe Mathieu-Daudé lduw_le_pci_dma(&s->dev, tbd_address + 4, &tx_buffer_size, attrs);
7994a63054bSPhilippe Mathieu-Daudé lduw_le_pci_dma(&s->dev, tbd_address + 6, &tx_buffer_el, attrs);
800663e8e51Sths tbd_address += 8;
801aac443e6SStefan Weil TRACE(RXTX, logout
802663e8e51Sths ("TBD (flexible mode): buffer address 0x%08x, size 0x%04x\n",
803aac443e6SStefan Weil tx_buffer_address, tx_buffer_size));
80424e6f355SReimar Döffinger tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size);
80516ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, tx_buffer_address,
80616ef60c9SEduard - Gabriel Munteanu &buf[size], tx_buffer_size);
807663e8e51Sths size += tx_buffer_size;
808663e8e51Sths if (tx_buffer_el & 1) {
809663e8e51Sths break;
810663e8e51Sths }
811663e8e51Sths }
812663e8e51Sths }
813aac443e6SStefan Weil TRACE(RXTX, logout("%p sending frame, len=%d,%s\n", s, size, nic_dump(buf, size)));
814b356f76dSJason Wang qemu_send_packet(qemu_get_queue(s->nic), buf, size);
815663e8e51Sths s->statistics.tx_good_frames++;
816663e8e51Sths /* Transmit with bad status would raise an CX/TNO interrupt.
817663e8e51Sths * (82557 only). Emulation never has bad status. */
818e7493b25SStefan Weil #if 0
819e7493b25SStefan Weil eepro100_cx_interrupt(s);
820e7493b25SStefan Weil #endif
821f3a52e50SStefan Weil }
822f3a52e50SStefan Weil
set_multicast_list(EEPRO100State * s)8237b8737deSStefan Weil static void set_multicast_list(EEPRO100State *s)
8247b8737deSStefan Weil {
8257b8737deSStefan Weil uint16_t multicast_count = s->tx.tbd_array_addr & BITS(13, 0);
8267b8737deSStefan Weil uint16_t i;
8277b8737deSStefan Weil memset(&s->mult[0], 0, sizeof(s->mult));
8287b8737deSStefan Weil TRACE(OTHER, logout("multicast list, multicast count = %u\n", multicast_count));
8297b8737deSStefan Weil for (i = 0; i < multicast_count; i += 6) {
8307b8737deSStefan Weil uint8_t multicast_addr[6];
83116ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, s->cb_address + 10 + i, multicast_addr, 6);
8327b8737deSStefan Weil TRACE(OTHER, logout("multicast entry %s\n", nic_dump(multicast_addr, 6)));
8337c0348bdSMark Cave-Ayland unsigned mcast_idx = (net_crc32(multicast_addr, ETH_ALEN) &
8347c0348bdSMark Cave-Ayland BITS(7, 2)) >> 2;
8357b8737deSStefan Weil assert(mcast_idx < 64);
8367b8737deSStefan Weil s->mult[mcast_idx >> 3] |= (1 << (mcast_idx & 7));
8377b8737deSStefan Weil }
8387b8737deSStefan Weil }
8397b8737deSStefan Weil
action_command(EEPRO100State * s)840f3a52e50SStefan Weil static void action_command(EEPRO100State *s)
841f3a52e50SStefan Weil {
842a423a1b5SPhilippe Mathieu-Daudé const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
84300837731SStefan Weil /* The loop below won't stop if it gets special handcrafted data.
84400837731SStefan Weil Therefore we limit the number of iterations. */
84500837731SStefan Weil unsigned max_loop_count = 16;
84600837731SStefan Weil
847f3a52e50SStefan Weil for (;;) {
8483d0f4b9bSStefan Weil bool bit_el;
8493d0f4b9bSStefan Weil bool bit_s;
8503d0f4b9bSStefan Weil bool bit_i;
8513d0f4b9bSStefan Weil bool bit_nc;
85275f5a6ccSStefan Weil uint16_t ok_status = STATUS_OK;
8533d0f4b9bSStefan Weil s->cb_address = s->cu_base + s->cu_offset;
8543d0f4b9bSStefan Weil read_cb(s);
8553d0f4b9bSStefan Weil bit_el = ((s->tx.command & COMMAND_EL) != 0);
8563d0f4b9bSStefan Weil bit_s = ((s->tx.command & COMMAND_S) != 0);
8573d0f4b9bSStefan Weil bit_i = ((s->tx.command & COMMAND_I) != 0);
8583d0f4b9bSStefan Weil bit_nc = ((s->tx.command & COMMAND_NC) != 0);
8593d0f4b9bSStefan Weil #if 0
8603d0f4b9bSStefan Weil bool bit_sf = ((s->tx.command & COMMAND_SF) != 0);
8613d0f4b9bSStefan Weil #endif
86200837731SStefan Weil
86300837731SStefan Weil if (max_loop_count-- == 0) {
86400837731SStefan Weil /* Prevent an endless loop. */
86500837731SStefan Weil logout("loop in %s:%u\n", __FILE__, __LINE__);
86600837731SStefan Weil break;
86700837731SStefan Weil }
86800837731SStefan Weil
8693d0f4b9bSStefan Weil s->cu_offset = s->tx.link;
8703d0f4b9bSStefan Weil TRACE(OTHER,
8713d0f4b9bSStefan Weil logout("val=(cu start), status=0x%04x, command=0x%04x, link=0x%08x\n",
8723d0f4b9bSStefan Weil s->tx.status, s->tx.command, s->tx.link));
8733d0f4b9bSStefan Weil switch (s->tx.command & COMMAND_CMD) {
874f3a52e50SStefan Weil case CmdNOp:
875f3a52e50SStefan Weil /* Do nothing. */
876f3a52e50SStefan Weil break;
877f3a52e50SStefan Weil case CmdIASetup:
87816ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, s->cb_address + 8, &s->conf.macaddr.a[0], 6);
879ce0e58b3SStefan Weil TRACE(OTHER, logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6)));
880f3a52e50SStefan Weil break;
881f3a52e50SStefan Weil case CmdConfigure:
88216ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, s->cb_address + 8,
88316ef60c9SEduard - Gabriel Munteanu &s->configuration[0], sizeof(s->configuration));
884010ec629SStefan Weil TRACE(OTHER, logout("configuration: %s\n",
885010ec629SStefan Weil nic_dump(&s->configuration[0], 16)));
886010ec629SStefan Weil TRACE(OTHER, logout("configuration: %s\n",
887010ec629SStefan Weil nic_dump(&s->configuration[16],
888010ec629SStefan Weil ARRAY_SIZE(s->configuration) - 16)));
889010ec629SStefan Weil if (s->configuration[20] & BIT(6)) {
890010ec629SStefan Weil TRACE(OTHER, logout("Multiple IA bit\n"));
891010ec629SStefan Weil }
892f3a52e50SStefan Weil break;
893f3a52e50SStefan Weil case CmdMulticastList:
8947b8737deSStefan Weil set_multicast_list(s);
895f3a52e50SStefan Weil break;
896f3a52e50SStefan Weil case CmdTx:
897f3a52e50SStefan Weil if (bit_nc) {
898f3a52e50SStefan Weil missing("CmdTx: NC = 0");
89975f5a6ccSStefan Weil ok_status = 0;
900f3a52e50SStefan Weil break;
901f3a52e50SStefan Weil }
902f3a52e50SStefan Weil tx_command(s);
903663e8e51Sths break;
904663e8e51Sths case CmdTDR:
905aac443e6SStefan Weil TRACE(OTHER, logout("load microcode\n"));
906663e8e51Sths /* Starting with offset 8, the command contains
907663e8e51Sths * 64 dwords microcode which we just ignore here. */
908663e8e51Sths break;
909f80a7fc3SStefan Weil case CmdDiagnose:
910f80a7fc3SStefan Weil TRACE(OTHER, logout("diagnose\n"));
911f80a7fc3SStefan Weil /* Make sure error flag is not set. */
912f80a7fc3SStefan Weil s->tx.status = 0;
913f80a7fc3SStefan Weil break;
914663e8e51Sths default:
915663e8e51Sths missing("undefined command");
91675f5a6ccSStefan Weil ok_status = 0;
9177f1e9d4eSKevin Wolf break;
918663e8e51Sths }
9197f1e9d4eSKevin Wolf /* Write new status. */
92016ef60c9SEduard - Gabriel Munteanu stw_le_pci_dma(&s->dev, s->cb_address,
921a423a1b5SPhilippe Mathieu-Daudé s->tx.status | ok_status | STATUS_C, attrs);
922663e8e51Sths if (bit_i) {
923663e8e51Sths /* CU completed action. */
924663e8e51Sths eepro100_cx_interrupt(s);
925663e8e51Sths }
926663e8e51Sths if (bit_el) {
927aac443e6SStefan Weil /* CU becomes idle. Terminate command loop. */
928663e8e51Sths set_cu_state(s, cu_idle);
929663e8e51Sths eepro100_cna_interrupt(s);
9305fa9a0aeSStefan Weil break;
931663e8e51Sths } else if (bit_s) {
9325fa9a0aeSStefan Weil /* CU becomes suspended. Terminate command loop. */
933663e8e51Sths set_cu_state(s, cu_suspended);
934663e8e51Sths eepro100_cna_interrupt(s);
9355fa9a0aeSStefan Weil break;
936663e8e51Sths } else {
937663e8e51Sths /* More entries in list. */
938aac443e6SStefan Weil TRACE(OTHER, logout("CU list with at least one more entry\n"));
9395fa9a0aeSStefan Weil }
940663e8e51Sths }
941aac443e6SStefan Weil TRACE(OTHER, logout("CU list empty\n"));
942663e8e51Sths /* List is empty. Now CU is idle or suspended. */
9435fa9a0aeSStefan Weil }
9445fa9a0aeSStefan Weil
eepro100_cu_command(EEPRO100State * s,uint8_t val)9455fa9a0aeSStefan Weil static void eepro100_cu_command(EEPRO100State * s, uint8_t val)
9465fa9a0aeSStefan Weil {
947a423a1b5SPhilippe Mathieu-Daudé const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
948cb25a3fbSStefan Weil cu_state_t cu_state;
9495fa9a0aeSStefan Weil switch (val) {
9505fa9a0aeSStefan Weil case CU_NOP:
9515fa9a0aeSStefan Weil /* No operation. */
9525fa9a0aeSStefan Weil break;
9535fa9a0aeSStefan Weil case CU_START:
954cb25a3fbSStefan Weil cu_state = get_cu_state(s);
955cb25a3fbSStefan Weil if (cu_state != cu_idle && cu_state != cu_suspended) {
956cb25a3fbSStefan Weil /* Intel documentation says that CU must be idle or suspended
957cb25a3fbSStefan Weil * for the CU start command. */
958cb25a3fbSStefan Weil logout("unexpected CU state is %u\n", cu_state);
9595fa9a0aeSStefan Weil }
9605fa9a0aeSStefan Weil set_cu_state(s, cu_active);
96127a05006SStefan Weil s->cu_offset = e100_read_reg4(s, SCBPointer);
9625fa9a0aeSStefan Weil action_command(s);
963663e8e51Sths break;
964663e8e51Sths case CU_RESUME:
965663e8e51Sths if (get_cu_state(s) != cu_suspended) {
966663e8e51Sths logout("bad CU resume from CU state %u\n", get_cu_state(s));
967663e8e51Sths /* Workaround for bad Linux eepro100 driver which resumes
968663e8e51Sths * from idle state. */
969e7493b25SStefan Weil #if 0
970e7493b25SStefan Weil missing("cu resume");
971e7493b25SStefan Weil #endif
972663e8e51Sths set_cu_state(s, cu_suspended);
973663e8e51Sths }
974663e8e51Sths if (get_cu_state(s) == cu_suspended) {
975aac443e6SStefan Weil TRACE(OTHER, logout("CU resuming\n"));
976663e8e51Sths set_cu_state(s, cu_active);
9775fa9a0aeSStefan Weil action_command(s);
978663e8e51Sths }
979663e8e51Sths break;
980663e8e51Sths case CU_STATSADDR:
981663e8e51Sths /* Load dump counters address. */
98227a05006SStefan Weil s->statsaddr = e100_read_reg4(s, SCBPointer);
983c16ada98SStefan Weil TRACE(OTHER, logout("val=0x%02x (dump counters address)\n", val));
984c16ada98SStefan Weil if (s->statsaddr & 3) {
985c16ada98SStefan Weil /* Memory must be Dword aligned. */
986c16ada98SStefan Weil logout("unaligned dump counters address\n");
987c16ada98SStefan Weil /* Handling of misaligned addresses is undefined.
988c16ada98SStefan Weil * Here we align the address by ignoring the lower bits. */
989c16ada98SStefan Weil /* TODO: Test unaligned dump counter address on real hardware. */
990c16ada98SStefan Weil s->statsaddr &= ~3;
991c16ada98SStefan Weil }
992663e8e51Sths break;
993663e8e51Sths case CU_SHOWSTATS:
994663e8e51Sths /* Dump statistical counters. */
995aac443e6SStefan Weil TRACE(OTHER, logout("val=0x%02x (dump stats)\n", val));
996663e8e51Sths dump_statistics(s);
997a423a1b5SPhilippe Mathieu-Daudé stl_le_pci_dma(&s->dev, s->statsaddr + s->stats_size, 0xa005, attrs);
998663e8e51Sths break;
999663e8e51Sths case CU_CMD_BASE:
1000663e8e51Sths /* Load CU base. */
1001aac443e6SStefan Weil TRACE(OTHER, logout("val=0x%02x (CU base address)\n", val));
100227a05006SStefan Weil s->cu_base = e100_read_reg4(s, SCBPointer);
1003663e8e51Sths break;
1004663e8e51Sths case CU_DUMPSTATS:
1005663e8e51Sths /* Dump and reset statistical counters. */
1006aac443e6SStefan Weil TRACE(OTHER, logout("val=0x%02x (dump stats and reset)\n", val));
1007663e8e51Sths dump_statistics(s);
1008a423a1b5SPhilippe Mathieu-Daudé stl_le_pci_dma(&s->dev, s->statsaddr + s->stats_size, 0xa007, attrs);
1009663e8e51Sths memset(&s->statistics, 0, sizeof(s->statistics));
1010663e8e51Sths break;
1011663e8e51Sths case CU_SRESUME:
1012663e8e51Sths /* CU static resume. */
1013663e8e51Sths missing("CU static resume");
1014663e8e51Sths break;
1015663e8e51Sths default:
1016663e8e51Sths missing("Undefined CU command");
1017663e8e51Sths }
1018663e8e51Sths }
1019663e8e51Sths
eepro100_ru_command(EEPRO100State * s,uint8_t val)1020663e8e51Sths static void eepro100_ru_command(EEPRO100State * s, uint8_t val)
1021663e8e51Sths {
1022663e8e51Sths switch (val) {
1023663e8e51Sths case RU_NOP:
1024663e8e51Sths /* No operation. */
1025663e8e51Sths break;
1026663e8e51Sths case RX_START:
1027663e8e51Sths /* RU start. */
1028663e8e51Sths if (get_ru_state(s) != ru_idle) {
1029663e8e51Sths logout("RU state is %u, should be %u\n", get_ru_state(s), ru_idle);
1030e7493b25SStefan Weil #if 0
1031e7493b25SStefan Weil assert(!"wrong RU state");
1032e7493b25SStefan Weil #endif
1033663e8e51Sths }
1034663e8e51Sths set_ru_state(s, ru_ready);
103527a05006SStefan Weil s->ru_offset = e100_read_reg4(s, SCBPointer);
1036b356f76dSJason Wang qemu_flush_queued_packets(qemu_get_queue(s->nic));
1037aac443e6SStefan Weil TRACE(OTHER, logout("val=0x%02x (rx start)\n", val));
1038663e8e51Sths break;
1039663e8e51Sths case RX_RESUME:
1040663e8e51Sths /* Restart RU. */
1041663e8e51Sths if (get_ru_state(s) != ru_suspended) {
1042663e8e51Sths logout("RU state is %u, should be %u\n", get_ru_state(s),
1043663e8e51Sths ru_suspended);
1044e7493b25SStefan Weil #if 0
1045e7493b25SStefan Weil assert(!"wrong RU state");
1046e7493b25SStefan Weil #endif
1047663e8e51Sths }
1048663e8e51Sths set_ru_state(s, ru_ready);
1049663e8e51Sths break;
1050e824012bSStefan Weil case RU_ABORT:
1051e824012bSStefan Weil /* RU abort. */
1052e824012bSStefan Weil if (get_ru_state(s) == ru_ready) {
1053e824012bSStefan Weil eepro100_rnr_interrupt(s);
1054e824012bSStefan Weil }
1055e824012bSStefan Weil set_ru_state(s, ru_idle);
1056e824012bSStefan Weil break;
1057663e8e51Sths case RX_ADDR_LOAD:
1058663e8e51Sths /* Load RU base. */
1059aac443e6SStefan Weil TRACE(OTHER, logout("val=0x%02x (RU base address)\n", val));
106027a05006SStefan Weil s->ru_base = e100_read_reg4(s, SCBPointer);
1061663e8e51Sths break;
1062663e8e51Sths default:
1063663e8e51Sths logout("val=0x%02x (undefined RU command)\n", val);
1064663e8e51Sths missing("Undefined SU command");
1065663e8e51Sths }
1066663e8e51Sths }
1067663e8e51Sths
eepro100_write_command(EEPRO100State * s,uint8_t val)1068663e8e51Sths static void eepro100_write_command(EEPRO100State * s, uint8_t val)
1069663e8e51Sths {
1070663e8e51Sths eepro100_ru_command(s, val & 0x0f);
1071663e8e51Sths eepro100_cu_command(s, val & 0xf0);
1072663e8e51Sths if ((val) == 0) {
1073aac443e6SStefan Weil TRACE(OTHER, logout("val=0x%02x\n", val));
1074663e8e51Sths }
1075663e8e51Sths /* Clear command byte after command was accepted. */
1076663e8e51Sths s->mem[SCBCmd] = 0;
1077663e8e51Sths }
1078663e8e51Sths
1079663e8e51Sths /*****************************************************************************
1080663e8e51Sths *
1081663e8e51Sths * EEPROM emulation.
1082663e8e51Sths *
1083663e8e51Sths ****************************************************************************/
1084663e8e51Sths
1085663e8e51Sths #define EEPROM_CS 0x02
1086663e8e51Sths #define EEPROM_SK 0x01
1087663e8e51Sths #define EEPROM_DI 0x04
1088663e8e51Sths #define EEPROM_DO 0x08
1089663e8e51Sths
eepro100_read_eeprom(EEPRO100State * s)1090663e8e51Sths static uint16_t eepro100_read_eeprom(EEPRO100State * s)
1091663e8e51Sths {
1092e5e23ab8SStefan Weil uint16_t val = e100_read_reg2(s, SCBeeprom);
1093663e8e51Sths if (eeprom93xx_read(s->eeprom)) {
1094663e8e51Sths val |= EEPROM_DO;
1095663e8e51Sths } else {
1096663e8e51Sths val &= ~EEPROM_DO;
1097663e8e51Sths }
1098aac443e6SStefan Weil TRACE(EEPROM, logout("val=0x%04x\n", val));
1099663e8e51Sths return val;
1100663e8e51Sths }
1101663e8e51Sths
eepro100_write_eeprom(eeprom_t * eeprom,uint8_t val)1102c227f099SAnthony Liguori static void eepro100_write_eeprom(eeprom_t * eeprom, uint8_t val)
1103663e8e51Sths {
1104aac443e6SStefan Weil TRACE(EEPROM, logout("val=0x%02x\n", val));
1105663e8e51Sths
1106ebabb67aSStefan Weil /* mask unwritable bits */
1107e7493b25SStefan Weil #if 0
1108e7493b25SStefan Weil val = SET_MASKED(val, 0x31, eeprom->value);
1109e7493b25SStefan Weil #endif
1110663e8e51Sths
1111663e8e51Sths int eecs = ((val & EEPROM_CS) != 0);
1112663e8e51Sths int eesk = ((val & EEPROM_SK) != 0);
1113663e8e51Sths int eedi = ((val & EEPROM_DI) != 0);
1114663e8e51Sths eeprom93xx_write(eeprom, eecs, eesk, eedi);
1115663e8e51Sths }
1116663e8e51Sths
1117663e8e51Sths /*****************************************************************************
1118663e8e51Sths *
1119663e8e51Sths * MDI emulation.
1120663e8e51Sths *
1121663e8e51Sths ****************************************************************************/
1122663e8e51Sths
1123663e8e51Sths #if defined(DEBUG_EEPRO100)
11246a0b9cc9SReimar Döffinger static const char * const mdi_op_name[] = {
1125663e8e51Sths "opcode 0",
1126663e8e51Sths "write",
1127663e8e51Sths "read",
1128663e8e51Sths "opcode 3"
1129663e8e51Sths };
1130663e8e51Sths
11316a0b9cc9SReimar Döffinger static const char * const mdi_reg_name[] = {
1132663e8e51Sths "Control",
1133663e8e51Sths "Status",
1134663e8e51Sths "PHY Identification (Word 1)",
1135663e8e51Sths "PHY Identification (Word 2)",
1136663e8e51Sths "Auto-Negotiation Advertisement",
1137663e8e51Sths "Auto-Negotiation Link Partner Ability",
1138663e8e51Sths "Auto-Negotiation Expansion"
1139663e8e51Sths };
1140aac443e6SStefan Weil
reg2name(uint8_t reg)1141aac443e6SStefan Weil static const char *reg2name(uint8_t reg)
1142aac443e6SStefan Weil {
1143aac443e6SStefan Weil static char buffer[10];
1144aac443e6SStefan Weil const char *p = buffer;
1145aac443e6SStefan Weil if (reg < ARRAY_SIZE(mdi_reg_name)) {
1146aac443e6SStefan Weil p = mdi_reg_name[reg];
1147aac443e6SStefan Weil } else {
1148aac443e6SStefan Weil snprintf(buffer, sizeof(buffer), "reg=0x%02x", reg);
1149aac443e6SStefan Weil }
1150aac443e6SStefan Weil return p;
1151aac443e6SStefan Weil }
1152663e8e51Sths #endif /* DEBUG_EEPRO100 */
1153663e8e51Sths
eepro100_read_mdi(EEPRO100State * s)1154663e8e51Sths static uint32_t eepro100_read_mdi(EEPRO100State * s)
1155663e8e51Sths {
1156e5e23ab8SStefan Weil uint32_t val = e100_read_reg4(s, SCBCtrlMDI);
1157663e8e51Sths
1158663e8e51Sths #ifdef DEBUG_EEPRO100
1159663e8e51Sths uint8_t raiseint = (val & BIT(29)) >> 29;
1160663e8e51Sths uint8_t opcode = (val & BITS(27, 26)) >> 26;
1161663e8e51Sths uint8_t phy = (val & BITS(25, 21)) >> 21;
1162663e8e51Sths uint8_t reg = (val & BITS(20, 16)) >> 16;
1163663e8e51Sths uint16_t data = (val & BITS(15, 0));
1164663e8e51Sths #endif
1165663e8e51Sths /* Emulation takes no time to finish MDI transaction. */
1166663e8e51Sths val |= BIT(28);
1167663e8e51Sths TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
1168663e8e51Sths val, raiseint, mdi_op_name[opcode], phy,
1169aac443e6SStefan Weil reg2name(reg), data));
1170663e8e51Sths return val;
1171663e8e51Sths }
1172663e8e51Sths
eepro100_write_mdi(EEPRO100State * s)11730113f48dSStefan Weil static void eepro100_write_mdi(EEPRO100State *s)
1174663e8e51Sths {
11750113f48dSStefan Weil uint32_t val = e100_read_reg4(s, SCBCtrlMDI);
1176663e8e51Sths uint8_t raiseint = (val & BIT(29)) >> 29;
1177663e8e51Sths uint8_t opcode = (val & BITS(27, 26)) >> 26;
1178663e8e51Sths uint8_t phy = (val & BITS(25, 21)) >> 21;
1179663e8e51Sths uint8_t reg = (val & BITS(20, 16)) >> 16;
1180663e8e51Sths uint16_t data = (val & BITS(15, 0));
1181aac443e6SStefan Weil TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
1182aac443e6SStefan Weil val, raiseint, mdi_op_name[opcode], phy, reg2name(reg), data));
1183663e8e51Sths if (phy != 1) {
1184663e8e51Sths /* Unsupported PHY address. */
1185e7493b25SStefan Weil #if 0
1186e7493b25SStefan Weil logout("phy must be 1 but is %u\n", phy);
1187e7493b25SStefan Weil #endif
1188663e8e51Sths data = 0;
1189663e8e51Sths } else if (opcode != 1 && opcode != 2) {
1190663e8e51Sths /* Unsupported opcode. */
1191663e8e51Sths logout("opcode must be 1 or 2 but is %u\n", opcode);
1192663e8e51Sths data = 0;
1193663e8e51Sths } else if (reg > 6) {
1194663e8e51Sths /* Unsupported register. */
1195663e8e51Sths logout("register must be 0...6 but is %u\n", reg);
1196663e8e51Sths data = 0;
1197663e8e51Sths } else {
1198663e8e51Sths TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
1199663e8e51Sths val, raiseint, mdi_op_name[opcode], phy,
1200aac443e6SStefan Weil reg2name(reg), data));
1201663e8e51Sths if (opcode == 1) {
1202663e8e51Sths /* MDI write */
1203663e8e51Sths switch (reg) {
1204663e8e51Sths case 0: /* Control Register */
1205663e8e51Sths if (data & 0x8000) {
1206663e8e51Sths /* Reset status and control registers to default. */
1207663e8e51Sths s->mdimem[0] = eepro100_mdi_default[0];
1208663e8e51Sths s->mdimem[1] = eepro100_mdi_default[1];
1209663e8e51Sths data = s->mdimem[reg];
1210663e8e51Sths } else {
1211663e8e51Sths /* Restart Auto Configuration = Normal Operation */
1212663e8e51Sths data &= ~0x0200;
1213663e8e51Sths }
1214663e8e51Sths break;
1215663e8e51Sths case 1: /* Status Register */
1216663e8e51Sths missing("not writable");
1217663e8e51Sths break;
1218663e8e51Sths case 2: /* PHY Identification Register (Word 1) */
1219663e8e51Sths case 3: /* PHY Identification Register (Word 2) */
1220663e8e51Sths missing("not implemented");
1221663e8e51Sths break;
1222663e8e51Sths case 4: /* Auto-Negotiation Advertisement Register */
1223663e8e51Sths case 5: /* Auto-Negotiation Link Partner Ability Register */
1224663e8e51Sths break;
1225663e8e51Sths case 6: /* Auto-Negotiation Expansion Register */
1226663e8e51Sths default:
1227663e8e51Sths missing("not implemented");
1228663e8e51Sths }
12295e80dd22SPeter Maydell s->mdimem[reg] &= eepro100_mdi_mask[reg];
12305e80dd22SPeter Maydell s->mdimem[reg] |= data & ~eepro100_mdi_mask[reg];
1231663e8e51Sths } else if (opcode == 2) {
1232663e8e51Sths /* MDI read */
1233663e8e51Sths switch (reg) {
1234663e8e51Sths case 0: /* Control Register */
1235663e8e51Sths if (data & 0x8000) {
1236663e8e51Sths /* Reset status and control registers to default. */
1237663e8e51Sths s->mdimem[0] = eepro100_mdi_default[0];
1238663e8e51Sths s->mdimem[1] = eepro100_mdi_default[1];
1239663e8e51Sths }
1240663e8e51Sths break;
1241663e8e51Sths case 1: /* Status Register */
1242663e8e51Sths s->mdimem[reg] |= 0x0020;
1243663e8e51Sths break;
1244663e8e51Sths case 2: /* PHY Identification Register (Word 1) */
1245663e8e51Sths case 3: /* PHY Identification Register (Word 2) */
1246663e8e51Sths case 4: /* Auto-Negotiation Advertisement Register */
1247663e8e51Sths break;
1248663e8e51Sths case 5: /* Auto-Negotiation Link Partner Ability Register */
1249663e8e51Sths s->mdimem[reg] = 0x41fe;
1250663e8e51Sths break;
1251663e8e51Sths case 6: /* Auto-Negotiation Expansion Register */
1252663e8e51Sths s->mdimem[reg] = 0x0001;
1253663e8e51Sths break;
1254663e8e51Sths }
1255663e8e51Sths data = s->mdimem[reg];
1256663e8e51Sths }
1257663e8e51Sths /* Emulation takes no time to finish MDI transaction.
1258663e8e51Sths * Set MDI bit in SCB status register. */
1259663e8e51Sths s->mem[SCBAck] |= 0x08;
1260663e8e51Sths val |= BIT(28);
1261663e8e51Sths if (raiseint) {
1262663e8e51Sths eepro100_mdi_interrupt(s);
1263663e8e51Sths }
1264663e8e51Sths }
1265663e8e51Sths val = (val & 0xffff0000) + data;
1266e5e23ab8SStefan Weil e100_write_reg4(s, SCBCtrlMDI, val);
1267663e8e51Sths }
1268663e8e51Sths
1269663e8e51Sths /*****************************************************************************
1270663e8e51Sths *
1271663e8e51Sths * Port emulation.
1272663e8e51Sths *
1273663e8e51Sths ****************************************************************************/
1274663e8e51Sths
1275663e8e51Sths #define PORT_SOFTWARE_RESET 0
1276663e8e51Sths #define PORT_SELFTEST 1
1277663e8e51Sths #define PORT_SELECTIVE_RESET 2
1278663e8e51Sths #define PORT_DUMP 3
1279663e8e51Sths #define PORT_SELECTION_MASK 3
1280663e8e51Sths
1281663e8e51Sths typedef struct {
1282663e8e51Sths uint32_t st_sign; /* Self Test Signature */
1283663e8e51Sths uint32_t st_result; /* Self Test Results */
1284c227f099SAnthony Liguori } eepro100_selftest_t;
1285663e8e51Sths
eepro100_read_port(EEPRO100State * s)1286663e8e51Sths static uint32_t eepro100_read_port(EEPRO100State * s)
1287663e8e51Sths {
1288663e8e51Sths return 0;
1289663e8e51Sths }
1290663e8e51Sths
eepro100_write_port(EEPRO100State * s)12913fd3d0b4SStefan Weil static void eepro100_write_port(EEPRO100State *s)
1292663e8e51Sths {
12933fd3d0b4SStefan Weil uint32_t val = e100_read_reg4(s, SCBPort);
1294663e8e51Sths uint32_t address = (val & ~PORT_SELECTION_MASK);
1295663e8e51Sths uint8_t selection = (val & PORT_SELECTION_MASK);
1296663e8e51Sths switch (selection) {
1297663e8e51Sths case PORT_SOFTWARE_RESET:
1298663e8e51Sths nic_reset(s);
1299663e8e51Sths break;
1300663e8e51Sths case PORT_SELFTEST:
1301aac443e6SStefan Weil TRACE(OTHER, logout("selftest address=0x%08x\n", address));
1302c227f099SAnthony Liguori eepro100_selftest_t data;
130316ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, address, (uint8_t *) &data, sizeof(data));
1304663e8e51Sths data.st_sign = 0xffffffff;
1305663e8e51Sths data.st_result = 0;
130616ef60c9SEduard - Gabriel Munteanu pci_dma_write(&s->dev, address, (uint8_t *) &data, sizeof(data));
1307663e8e51Sths break;
1308663e8e51Sths case PORT_SELECTIVE_RESET:
1309aac443e6SStefan Weil TRACE(OTHER, logout("selective reset, selftest address=0x%08x\n", address));
1310663e8e51Sths nic_selective_reset(s);
1311663e8e51Sths break;
1312663e8e51Sths default:
1313663e8e51Sths logout("val=0x%08x\n", val);
1314663e8e51Sths missing("unknown port selection");
1315663e8e51Sths }
1316663e8e51Sths }
1317663e8e51Sths
1318663e8e51Sths /*****************************************************************************
1319663e8e51Sths *
1320663e8e51Sths * General hardware emulation.
1321663e8e51Sths *
1322663e8e51Sths ****************************************************************************/
1323663e8e51Sths
eepro100_read1(EEPRO100State * s,uint32_t addr)1324663e8e51Sths static uint8_t eepro100_read1(EEPRO100State * s, uint32_t addr)
1325663e8e51Sths {
1326ef476062SBlue Swirl uint8_t val = 0;
1327663e8e51Sths if (addr <= sizeof(s->mem) - sizeof(val)) {
1328e5e23ab8SStefan Weil val = s->mem[addr];
1329663e8e51Sths }
1330663e8e51Sths
1331663e8e51Sths switch (addr) {
1332663e8e51Sths case SCBStatus:
1333663e8e51Sths case SCBAck:
1334aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1335663e8e51Sths break;
1336663e8e51Sths case SCBCmd:
1337aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1338e7493b25SStefan Weil #if 0
1339e7493b25SStefan Weil val = eepro100_read_command(s);
1340e7493b25SStefan Weil #endif
1341663e8e51Sths break;
1342663e8e51Sths case SCBIntmask:
1343aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1344663e8e51Sths break;
1345663e8e51Sths case SCBPort + 3:
1346aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1347663e8e51Sths break;
1348663e8e51Sths case SCBeeprom:
1349663e8e51Sths val = eepro100_read_eeprom(s);
1350663e8e51Sths break;
13510113f48dSStefan Weil case SCBCtrlMDI:
13520113f48dSStefan Weil case SCBCtrlMDI + 1:
13530113f48dSStefan Weil case SCBCtrlMDI + 2:
13540113f48dSStefan Weil case SCBCtrlMDI + 3:
13550113f48dSStefan Weil val = (uint8_t)(eepro100_read_mdi(s) >> (8 * (addr & 3)));
13560113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
13570113f48dSStefan Weil break;
13580908bba1SStefan Weil case SCBpmdr: /* Power Management Driver Register */
1359663e8e51Sths val = 0;
1360aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1361663e8e51Sths break;
1362a39bd017SStefan Weil case SCBgctrl: /* General Control Register */
1363a39bd017SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1364a39bd017SStefan Weil break;
13650908bba1SStefan Weil case SCBgstat: /* General Status Register */
1366663e8e51Sths /* 100 Mbps full duplex, valid link */
1367663e8e51Sths val = 0x07;
1368aac443e6SStefan Weil TRACE(OTHER, logout("addr=General Status val=%02x\n", val));
1369663e8e51Sths break;
1370663e8e51Sths default:
1371663e8e51Sths logout("addr=%s val=0x%02x\n", regname(addr), val);
1372663e8e51Sths missing("unknown byte read");
1373663e8e51Sths }
1374663e8e51Sths return val;
1375663e8e51Sths }
1376663e8e51Sths
eepro100_read2(EEPRO100State * s,uint32_t addr)1377663e8e51Sths static uint16_t eepro100_read2(EEPRO100State * s, uint32_t addr)
1378663e8e51Sths {
1379ef476062SBlue Swirl uint16_t val = 0;
1380663e8e51Sths if (addr <= sizeof(s->mem) - sizeof(val)) {
1381e5e23ab8SStefan Weil val = e100_read_reg2(s, addr);
1382663e8e51Sths }
1383663e8e51Sths
1384663e8e51Sths switch (addr) {
1385663e8e51Sths case SCBStatus:
1386dbbaaff6S=?UTF-8?q?Reimar=20D=C3=B6ffinger?= case SCBCmd:
1387aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
1388663e8e51Sths break;
1389663e8e51Sths case SCBeeprom:
1390663e8e51Sths val = eepro100_read_eeprom(s);
1391aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
1392663e8e51Sths break;
13930113f48dSStefan Weil case SCBCtrlMDI:
13940113f48dSStefan Weil case SCBCtrlMDI + 2:
13950113f48dSStefan Weil val = (uint16_t)(eepro100_read_mdi(s) >> (8 * (addr & 3)));
13960113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
13970113f48dSStefan Weil break;
1398663e8e51Sths default:
1399663e8e51Sths logout("addr=%s val=0x%04x\n", regname(addr), val);
1400663e8e51Sths missing("unknown word read");
1401663e8e51Sths }
1402663e8e51Sths return val;
1403663e8e51Sths }
1404663e8e51Sths
eepro100_read4(EEPRO100State * s,uint32_t addr)1405663e8e51Sths static uint32_t eepro100_read4(EEPRO100State * s, uint32_t addr)
1406663e8e51Sths {
1407ef476062SBlue Swirl uint32_t val = 0;
1408663e8e51Sths if (addr <= sizeof(s->mem) - sizeof(val)) {
1409e5e23ab8SStefan Weil val = e100_read_reg4(s, addr);
1410663e8e51Sths }
1411663e8e51Sths
1412663e8e51Sths switch (addr) {
1413663e8e51Sths case SCBStatus:
1414aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1415663e8e51Sths break;
1416663e8e51Sths case SCBPointer:
1417aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1418663e8e51Sths break;
1419663e8e51Sths case SCBPort:
1420663e8e51Sths val = eepro100_read_port(s);
1421aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1422663e8e51Sths break;
1423072476eaSStefan Weil case SCBflash:
1424072476eaSStefan Weil val = eepro100_read_eeprom(s);
1425072476eaSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1426072476eaSStefan Weil break;
1427663e8e51Sths case SCBCtrlMDI:
1428663e8e51Sths val = eepro100_read_mdi(s);
1429663e8e51Sths break;
1430663e8e51Sths default:
1431663e8e51Sths logout("addr=%s val=0x%08x\n", regname(addr), val);
1432663e8e51Sths missing("unknown longword read");
1433663e8e51Sths }
1434663e8e51Sths return val;
1435663e8e51Sths }
1436663e8e51Sths
eepro100_write1(EEPRO100State * s,uint32_t addr,uint8_t val)1437663e8e51Sths static void eepro100_write1(EEPRO100State * s, uint32_t addr, uint8_t val)
1438663e8e51Sths {
1439e74818f3SStefan Weil /* SCBStatus is readonly. */
1440e74818f3SStefan Weil if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) {
1441e5e23ab8SStefan Weil s->mem[addr] = val;
1442663e8e51Sths }
1443663e8e51Sths
1444663e8e51Sths switch (addr) {
1445663e8e51Sths case SCBStatus:
14461b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1447663e8e51Sths break;
1448663e8e51Sths case SCBAck:
14491b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1450663e8e51Sths eepro100_acknowledge(s);
1451663e8e51Sths break;
1452663e8e51Sths case SCBCmd:
14531b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1454663e8e51Sths eepro100_write_command(s, val);
1455663e8e51Sths break;
1456663e8e51Sths case SCBIntmask:
14571b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1458663e8e51Sths if (val & BIT(1)) {
1459663e8e51Sths eepro100_swi_interrupt(s);
1460663e8e51Sths }
1461663e8e51Sths eepro100_interrupt(s, 0);
1462663e8e51Sths break;
146327a05006SStefan Weil case SCBPointer:
146427a05006SStefan Weil case SCBPointer + 1:
146527a05006SStefan Weil case SCBPointer + 2:
146627a05006SStefan Weil case SCBPointer + 3:
146727a05006SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
146827a05006SStefan Weil break;
14693fd3d0b4SStefan Weil case SCBPort:
14703fd3d0b4SStefan Weil case SCBPort + 1:
14713fd3d0b4SStefan Weil case SCBPort + 2:
14723fd3d0b4SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
14733fd3d0b4SStefan Weil break;
1474663e8e51Sths case SCBPort + 3:
14753fd3d0b4SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
14763fd3d0b4SStefan Weil eepro100_write_port(s);
14773fd3d0b4SStefan Weil break;
1478aac443e6SStefan Weil case SCBFlow: /* does not exist on 82557 */
14793257d2b6Sths case SCBFlow + 1:
14803257d2b6Sths case SCBFlow + 2:
14810908bba1SStefan Weil case SCBpmdr: /* does not exist on 82557 */
1482aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1483663e8e51Sths break;
1484663e8e51Sths case SCBeeprom:
14851b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
1486663e8e51Sths eepro100_write_eeprom(s->eeprom, val);
1487663e8e51Sths break;
14880113f48dSStefan Weil case SCBCtrlMDI:
14890113f48dSStefan Weil case SCBCtrlMDI + 1:
14900113f48dSStefan Weil case SCBCtrlMDI + 2:
14910113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
14920113f48dSStefan Weil break;
14930113f48dSStefan Weil case SCBCtrlMDI + 3:
14940113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
14950113f48dSStefan Weil eepro100_write_mdi(s);
14960113f48dSStefan Weil break;
1497663e8e51Sths default:
1498663e8e51Sths logout("addr=%s val=0x%02x\n", regname(addr), val);
1499663e8e51Sths missing("unknown byte write");
1500663e8e51Sths }
1501663e8e51Sths }
1502663e8e51Sths
eepro100_write2(EEPRO100State * s,uint32_t addr,uint16_t val)1503663e8e51Sths static void eepro100_write2(EEPRO100State * s, uint32_t addr, uint16_t val)
1504663e8e51Sths {
1505e74818f3SStefan Weil /* SCBStatus is readonly. */
1506e74818f3SStefan Weil if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) {
1507e5e23ab8SStefan Weil e100_write_reg2(s, addr, val);
1508663e8e51Sths }
1509663e8e51Sths
1510663e8e51Sths switch (addr) {
1511663e8e51Sths case SCBStatus:
15121b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
1513e74818f3SStefan Weil s->mem[SCBAck] = (val >> 8);
1514663e8e51Sths eepro100_acknowledge(s);
1515663e8e51Sths break;
1516663e8e51Sths case SCBCmd:
15171b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
1518663e8e51Sths eepro100_write_command(s, val);
1519663e8e51Sths eepro100_write1(s, SCBIntmask, val >> 8);
1520663e8e51Sths break;
152127a05006SStefan Weil case SCBPointer:
152227a05006SStefan Weil case SCBPointer + 2:
152327a05006SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
152427a05006SStefan Weil break;
15253fd3d0b4SStefan Weil case SCBPort:
15263fd3d0b4SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
15273fd3d0b4SStefan Weil break;
15283fd3d0b4SStefan Weil case SCBPort + 2:
15293fd3d0b4SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
15303fd3d0b4SStefan Weil eepro100_write_port(s);
15313fd3d0b4SStefan Weil break;
1532663e8e51Sths case SCBeeprom:
15331b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
1534663e8e51Sths eepro100_write_eeprom(s->eeprom, val);
1535663e8e51Sths break;
15360113f48dSStefan Weil case SCBCtrlMDI:
15370113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
15380113f48dSStefan Weil break;
15390113f48dSStefan Weil case SCBCtrlMDI + 2:
15400113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
15410113f48dSStefan Weil eepro100_write_mdi(s);
15420113f48dSStefan Weil break;
1543663e8e51Sths default:
1544663e8e51Sths logout("addr=%s val=0x%04x\n", regname(addr), val);
1545663e8e51Sths missing("unknown word write");
1546663e8e51Sths }
1547663e8e51Sths }
1548663e8e51Sths
eepro100_write4(EEPRO100State * s,uint32_t addr,uint32_t val)1549663e8e51Sths static void eepro100_write4(EEPRO100State * s, uint32_t addr, uint32_t val)
1550663e8e51Sths {
1551663e8e51Sths if (addr <= sizeof(s->mem) - sizeof(val)) {
1552e5e23ab8SStefan Weil e100_write_reg4(s, addr, val);
1553663e8e51Sths }
1554663e8e51Sths
1555663e8e51Sths switch (addr) {
1556663e8e51Sths case SCBPointer:
155727a05006SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1558663e8e51Sths break;
1559663e8e51Sths case SCBPort:
1560aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
15613fd3d0b4SStefan Weil eepro100_write_port(s);
1562663e8e51Sths break;
1563072476eaSStefan Weil case SCBflash:
1564072476eaSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
1565072476eaSStefan Weil val = val >> 16;
1566072476eaSStefan Weil eepro100_write_eeprom(s->eeprom, val);
1567072476eaSStefan Weil break;
1568663e8e51Sths case SCBCtrlMDI:
15690113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
15700113f48dSStefan Weil eepro100_write_mdi(s);
1571663e8e51Sths break;
1572663e8e51Sths default:
1573663e8e51Sths logout("addr=%s val=0x%08x\n", regname(addr), val);
1574663e8e51Sths missing("unknown longword write");
1575663e8e51Sths }
1576663e8e51Sths }
1577663e8e51Sths
eepro100_read(void * opaque,hwaddr addr,unsigned size)1578a8170e5eSAvi Kivity static uint64_t eepro100_read(void *opaque, hwaddr addr,
15795e6ffddeSAvi Kivity unsigned size)
1580663e8e51Sths {
1581663e8e51Sths EEPRO100State *s = opaque;
15825e6ffddeSAvi Kivity
15835e6ffddeSAvi Kivity switch (size) {
15845e6ffddeSAvi Kivity case 1: return eepro100_read1(s, addr);
15855e6ffddeSAvi Kivity case 2: return eepro100_read2(s, addr);
15865e6ffddeSAvi Kivity case 4: return eepro100_read4(s, addr);
15875e6ffddeSAvi Kivity default: abort();
15885e6ffddeSAvi Kivity }
1589663e8e51Sths }
1590663e8e51Sths
eepro100_write(void * opaque,hwaddr addr,uint64_t data,unsigned size)1591a8170e5eSAvi Kivity static void eepro100_write(void *opaque, hwaddr addr,
15925e6ffddeSAvi Kivity uint64_t data, unsigned size)
1593663e8e51Sths {
1594663e8e51Sths EEPRO100State *s = opaque;
15955e6ffddeSAvi Kivity
15965e6ffddeSAvi Kivity switch (size) {
15970ed8b6f6SBlue Swirl case 1:
15980ed8b6f6SBlue Swirl eepro100_write1(s, addr, data);
15990ed8b6f6SBlue Swirl break;
16000ed8b6f6SBlue Swirl case 2:
16010ed8b6f6SBlue Swirl eepro100_write2(s, addr, data);
16020ed8b6f6SBlue Swirl break;
16030ed8b6f6SBlue Swirl case 4:
16040ed8b6f6SBlue Swirl eepro100_write4(s, addr, data);
16050ed8b6f6SBlue Swirl break;
16060ed8b6f6SBlue Swirl default:
16070ed8b6f6SBlue Swirl abort();
16085e6ffddeSAvi Kivity }
1609663e8e51Sths }
1610663e8e51Sths
16115e6ffddeSAvi Kivity static const MemoryRegionOps eepro100_ops = {
16125e6ffddeSAvi Kivity .read = eepro100_read,
16135e6ffddeSAvi Kivity .write = eepro100_write,
16145e6ffddeSAvi Kivity .endianness = DEVICE_LITTLE_ENDIAN,
1615663e8e51Sths };
1616663e8e51Sths
nic_receive(NetClientState * nc,const uint8_t * buf,size_t size)16174e68f7a0SStefan Hajnoczi static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
1618663e8e51Sths {
1619663e8e51Sths /* TODO:
1620663e8e51Sths * - Magic packets should set bit 30 in power management driver register.
1621663e8e51Sths * - Interesting packets should set bit 29 in power management driver register.
1622663e8e51Sths */
1623a423a1b5SPhilippe Mathieu-Daudé const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
1624cc1f0f45SJason Wang EEPRO100State *s = qemu_get_nic_opaque(nc);
1625663e8e51Sths uint16_t rfd_status = 0xa000;
1626792f1d63SStefan Weil #if defined(CONFIG_PAD_RECEIVED_FRAMES)
1627792f1d63SStefan Weil uint8_t min_buf[60];
1628792f1d63SStefan Weil #endif
1629663e8e51Sths static const uint8_t broadcast_macaddr[6] =
1630663e8e51Sths { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
1631663e8e51Sths
1632792f1d63SStefan Weil #if defined(CONFIG_PAD_RECEIVED_FRAMES)
1633792f1d63SStefan Weil /* Pad to minimum Ethernet frame length */
1634792f1d63SStefan Weil if (size < sizeof(min_buf)) {
1635792f1d63SStefan Weil memcpy(min_buf, buf, size);
1636792f1d63SStefan Weil memset(&min_buf[size], 0, sizeof(min_buf) - size);
1637792f1d63SStefan Weil buf = min_buf;
1638792f1d63SStefan Weil size = sizeof(min_buf);
1639792f1d63SStefan Weil }
1640792f1d63SStefan Weil #endif
1641792f1d63SStefan Weil
1642663e8e51Sths if (s->configuration[8] & 0x80) {
1643663e8e51Sths /* CSMA is disabled. */
1644663e8e51Sths logout("%p received while CSMA is disabled\n", s);
16454f1c942bSMark McLoughlin return -1;
1646792f1d63SStefan Weil #if !defined(CONFIG_PAD_RECEIVED_FRAMES)
1647ced5296aSStefan Weil } else if (size < 64 && (s->configuration[7] & BIT(0))) {
1648663e8e51Sths /* Short frame and configuration byte 7/0 (discard short receive) set:
1649663e8e51Sths * Short frame is discarded */
1650067d01deSStefan Weil logout("%p received short frame (%zu byte)\n", s, size);
1651663e8e51Sths s->statistics.rx_short_frame_errors++;
1652e7493b25SStefan Weil return -1;
1653e7493b25SStefan Weil #endif
1654ced5296aSStefan Weil } else if ((size > MAX_ETH_FRAME_SIZE + 4) && !(s->configuration[18] & BIT(3))) {
1655663e8e51Sths /* Long frame and configuration byte 18/3 (long receive ok) not set:
1656663e8e51Sths * Long frames are discarded. */
1657067d01deSStefan Weil logout("%p received long frame (%zu byte), ignored\n", s, size);
16584f1c942bSMark McLoughlin return -1;
1659e7493b25SStefan Weil } else if (memcmp(buf, s->conf.macaddr.a, 6) == 0) { /* !!! */
1660663e8e51Sths /* Frame matches individual address. */
1661663e8e51Sths /* TODO: check configuration byte 15/4 (ignore U/L). */
1662067d01deSStefan Weil TRACE(RXTX, logout("%p received frame for me, len=%zu\n", s, size));
1663663e8e51Sths } else if (memcmp(buf, broadcast_macaddr, 6) == 0) {
1664663e8e51Sths /* Broadcast frame. */
1665067d01deSStefan Weil TRACE(RXTX, logout("%p received broadcast, len=%zu\n", s, size));
1666663e8e51Sths rfd_status |= 0x0002;
16677b8737deSStefan Weil } else if (buf[0] & 0x01) {
1668663e8e51Sths /* Multicast frame. */
16697b8737deSStefan Weil TRACE(RXTX, logout("%p received multicast, len=%zu,%s\n", s, size, nic_dump(buf, size)));
16707f1e9d4eSKevin Wolf if (s->configuration[21] & BIT(3)) {
16717b8737deSStefan Weil /* Multicast all bit is set, receive all multicast frames. */
16727b8737deSStefan Weil } else {
16737c0348bdSMark Cave-Ayland unsigned mcast_idx = (net_crc32(buf, ETH_ALEN) & BITS(7, 2)) >> 2;
16747b8737deSStefan Weil assert(mcast_idx < 64);
16757b8737deSStefan Weil if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) {
16767b8737deSStefan Weil /* Multicast frame is allowed in hash table. */
1677ced5296aSStefan Weil } else if (s->configuration[15] & BIT(0)) {
16787b8737deSStefan Weil /* Promiscuous: receive all. */
16797b8737deSStefan Weil rfd_status |= 0x0004;
16807b8737deSStefan Weil } else {
16817b8737deSStefan Weil TRACE(RXTX, logout("%p multicast ignored\n", s));
16827b8737deSStefan Weil return -1;
16837f1e9d4eSKevin Wolf }
1684663e8e51Sths }
16857b8737deSStefan Weil /* TODO: Next not for promiscuous mode? */
1686663e8e51Sths rfd_status |= 0x0002;
1687ced5296aSStefan Weil } else if (s->configuration[15] & BIT(0)) {
1688663e8e51Sths /* Promiscuous: receive all. */
1689067d01deSStefan Weil TRACE(RXTX, logout("%p received frame in promiscuous mode, len=%zu\n", s, size));
1690663e8e51Sths rfd_status |= 0x0004;
1691010ec629SStefan Weil } else if (s->configuration[20] & BIT(6)) {
1692010ec629SStefan Weil /* Multiple IA bit set. */
1693d00d6d00SMark Cave-Ayland unsigned mcast_idx = net_crc32(buf, ETH_ALEN) >> 26;
1694010ec629SStefan Weil assert(mcast_idx < 64);
1695010ec629SStefan Weil if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) {
1696010ec629SStefan Weil TRACE(RXTX, logout("%p accepted, multiple IA bit set\n", s));
1697010ec629SStefan Weil } else {
1698010ec629SStefan Weil TRACE(RXTX, logout("%p frame ignored, multiple IA bit set\n", s));
1699010ec629SStefan Weil return -1;
1700010ec629SStefan Weil }
1701663e8e51Sths } else {
1702067d01deSStefan Weil TRACE(RXTX, logout("%p received frame, ignored, len=%zu,%s\n", s, size,
1703aac443e6SStefan Weil nic_dump(buf, size)));
17044f1c942bSMark McLoughlin return size;
1705663e8e51Sths }
1706663e8e51Sths
1707663e8e51Sths if (get_ru_state(s) != ru_ready) {
1708aac443e6SStefan Weil /* No resources available. */
1709aac443e6SStefan Weil logout("no resources, state=%u\n", get_ru_state(s));
1710e824012bSStefan Weil /* TODO: RNR interrupt only at first failed frame? */
1711e824012bSStefan Weil eepro100_rnr_interrupt(s);
1712663e8e51Sths s->statistics.rx_resource_errors++;
1713e7493b25SStefan Weil #if 0
1714e7493b25SStefan Weil assert(!"no resources");
1715e7493b25SStefan Weil #endif
17164f1c942bSMark McLoughlin return -1;
1717663e8e51Sths }
1718e7493b25SStefan Weil /* !!! */
1719c227f099SAnthony Liguori eepro100_rx_t rx;
172016ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, s->ru_base + s->ru_offset,
1721e965d4bcSDavid Gibson &rx, sizeof(eepro100_rx_t));
1722663e8e51Sths uint16_t rfd_command = le16_to_cpu(rx.command);
1723663e8e51Sths uint16_t rfd_size = le16_to_cpu(rx.size);
17247f1e9d4eSKevin Wolf
17257f1e9d4eSKevin Wolf if (size > rfd_size) {
17267f1e9d4eSKevin Wolf logout("Receive buffer (%" PRId16 " bytes) too small for data "
17277f1e9d4eSKevin Wolf "(%zu bytes); data truncated\n", rfd_size, size);
17287f1e9d4eSKevin Wolf size = rfd_size;
17297f1e9d4eSKevin Wolf }
1730792f1d63SStefan Weil #if !defined(CONFIG_PAD_RECEIVED_FRAMES)
1731663e8e51Sths if (size < 64) {
1732663e8e51Sths rfd_status |= 0x0080;
1733663e8e51Sths }
1734792f1d63SStefan Weil #endif
1735aac443e6SStefan Weil TRACE(OTHER, logout("command 0x%04x, link 0x%08x, addr 0x%08x, size %u\n",
1736aac443e6SStefan Weil rfd_command, rx.link, rx.rx_buf_addr, rfd_size));
173716ef60c9SEduard - Gabriel Munteanu stw_le_pci_dma(&s->dev, s->ru_base + s->ru_offset +
1738a423a1b5SPhilippe Mathieu-Daudé offsetof(eepro100_rx_t, status), rfd_status, attrs);
173916ef60c9SEduard - Gabriel Munteanu stw_le_pci_dma(&s->dev, s->ru_base + s->ru_offset +
1740a423a1b5SPhilippe Mathieu-Daudé offsetof(eepro100_rx_t, count), size, attrs);
1741663e8e51Sths /* Early receive interrupt not supported. */
1742e7493b25SStefan Weil #if 0
1743e7493b25SStefan Weil eepro100_er_interrupt(s);
1744e7493b25SStefan Weil #endif
1745663e8e51Sths /* Receive CRC Transfer not supported. */
1746ced5296aSStefan Weil if (s->configuration[18] & BIT(2)) {
17477f1e9d4eSKevin Wolf missing("Receive CRC Transfer");
17487f1e9d4eSKevin Wolf return -1;
17497f1e9d4eSKevin Wolf }
1750663e8e51Sths /* TODO: check stripping enable bit. */
1751e7493b25SStefan Weil #if 0
1752e7493b25SStefan Weil assert(!(s->configuration[17] & BIT(0)));
1753e7493b25SStefan Weil #endif
175416ef60c9SEduard - Gabriel Munteanu pci_dma_write(&s->dev, s->ru_base + s->ru_offset +
175527112f18SStefan Weil sizeof(eepro100_rx_t), buf, size);
1756663e8e51Sths s->statistics.rx_good_frames++;
1757663e8e51Sths eepro100_fr_interrupt(s);
1758663e8e51Sths s->ru_offset = le32_to_cpu(rx.link);
1759ced5296aSStefan Weil if (rfd_command & COMMAND_EL) {
1760663e8e51Sths /* EL bit is set, so this was the last frame. */
17617f1e9d4eSKevin Wolf logout("receive: Running out of frames\n");
17621069985fSBo Yang set_ru_state(s, ru_no_resources);
17631069985fSBo Yang eepro100_rnr_interrupt(s);
1764663e8e51Sths }
1765ced5296aSStefan Weil if (rfd_command & COMMAND_S) {
1766663e8e51Sths /* S bit is set. */
1767663e8e51Sths set_ru_state(s, ru_suspended);
1768663e8e51Sths }
17694f1c942bSMark McLoughlin return size;
1770663e8e51Sths }
1771663e8e51Sths
1772151b2986SJuan Quintela static const VMStateDescription vmstate_eepro100 = {
1773151b2986SJuan Quintela .version_id = 3,
1774151b2986SJuan Quintela .minimum_version_id = 2,
17751de81b42SRichard Henderson .fields = (const VMStateField[]) {
1776151b2986SJuan Quintela VMSTATE_PCI_DEVICE(dev, EEPRO100State),
1777151b2986SJuan Quintela VMSTATE_UNUSED(32),
1778151b2986SJuan Quintela VMSTATE_BUFFER(mult, EEPRO100State),
1779151b2986SJuan Quintela VMSTATE_BUFFER(mem, EEPRO100State),
17803706c43fSStefan Weil /* Save all members of struct between scb_stat and mem. */
1781151b2986SJuan Quintela VMSTATE_UINT8(scb_stat, EEPRO100State),
1782151b2986SJuan Quintela VMSTATE_UINT8(int_stat, EEPRO100State),
1783151b2986SJuan Quintela VMSTATE_UNUSED(3*4),
1784151b2986SJuan Quintela VMSTATE_MACADDR(conf.macaddr, EEPRO100State),
1785151b2986SJuan Quintela VMSTATE_UNUSED(19*4),
1786151b2986SJuan Quintela VMSTATE_UINT16_ARRAY(mdimem, EEPRO100State, 32),
1787aac443e6SStefan Weil /* The eeprom should be saved and restored by its own routines. */
1788151b2986SJuan Quintela VMSTATE_UINT32(device, EEPRO100State),
1789151b2986SJuan Quintela /* TODO check device. */
1790151b2986SJuan Quintela VMSTATE_UINT32(cu_base, EEPRO100State),
1791151b2986SJuan Quintela VMSTATE_UINT32(cu_offset, EEPRO100State),
1792151b2986SJuan Quintela VMSTATE_UINT32(ru_base, EEPRO100State),
1793151b2986SJuan Quintela VMSTATE_UINT32(ru_offset, EEPRO100State),
1794151b2986SJuan Quintela VMSTATE_UINT32(statsaddr, EEPRO100State),
1795ba42b646SStefan Weil /* Save eepro100_stats_t statistics. */
1796151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_good_frames, EEPRO100State),
1797151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_max_collisions, EEPRO100State),
1798151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_late_collisions, EEPRO100State),
1799151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_underruns, EEPRO100State),
1800151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_lost_crs, EEPRO100State),
1801151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_deferred, EEPRO100State),
1802151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_single_collisions, EEPRO100State),
1803151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_multiple_collisions, EEPRO100State),
1804151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_total_collisions, EEPRO100State),
1805151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_good_frames, EEPRO100State),
1806151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_crc_errors, EEPRO100State),
1807151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_alignment_errors, EEPRO100State),
1808151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_resource_errors, EEPRO100State),
1809151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_overrun_errors, EEPRO100State),
1810151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_cdt_errors, EEPRO100State),
1811151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_short_frame_errors, EEPRO100State),
1812151b2986SJuan Quintela VMSTATE_UINT32(statistics.fc_xmt_pause, EEPRO100State),
1813151b2986SJuan Quintela VMSTATE_UINT32(statistics.fc_rcv_pause, EEPRO100State),
1814151b2986SJuan Quintela VMSTATE_UINT32(statistics.fc_rcv_unsupported, EEPRO100State),
1815151b2986SJuan Quintela VMSTATE_UINT16(statistics.xmt_tco_frames, EEPRO100State),
1816151b2986SJuan Quintela VMSTATE_UINT16(statistics.rcv_tco_frames, EEPRO100State),
18172657c663Sbalrog /* Configuration bytes. */
1818151b2986SJuan Quintela VMSTATE_BUFFER(configuration, EEPRO100State),
1819151b2986SJuan Quintela VMSTATE_END_OF_LIST()
1820663e8e51Sths }
1821151b2986SJuan Quintela };
1822663e8e51Sths
pci_nic_uninit(PCIDevice * pci_dev)1823f90c2bcdSAlex Williamson static void pci_nic_uninit(PCIDevice *pci_dev)
1824b946a153Saliguori {
1825c4c270e2SStefan Weil EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev);
1826b946a153Saliguori
18273cad405bSMarc-André Lureau vmstate_unregister(VMSTATE_IF(&pci_dev->qdev), s->vmstate, s);
18282634ab7fSLi Qiang g_free(s->vmstate);
18295fce2b3eSAlex Williamson eeprom93xx_free(&pci_dev->qdev, s->eeprom);
1830948ecf21SJason Wang qemu_del_nic(s->nic);
1831b946a153Saliguori }
1832b946a153Saliguori
1833e00e365eSMark McLoughlin static NetClientInfo net_eepro100_info = {
1834f394b2e2SEric Blake .type = NET_CLIENT_DRIVER_NIC,
1835e00e365eSMark McLoughlin .size = sizeof(NICState),
1836e00e365eSMark McLoughlin .receive = nic_receive,
1837e00e365eSMark McLoughlin };
1838e00e365eSMark McLoughlin
e100_nic_realize(PCIDevice * pci_dev,Error ** errp)18399af21dbeSMarkus Armbruster static void e100_nic_realize(PCIDevice *pci_dev, Error **errp)
1840663e8e51Sths {
1841273a2142SJuan Quintela EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev);
184240021f08SAnthony Liguori E100PCIDeviceInfo *info = eepro100_get_class(s);
18439a7c2a59SMao Zhongyi Error *local_err = NULL;
1844663e8e51Sths
1845aac443e6SStefan Weil TRACE(OTHER, logout("\n"));
1846663e8e51Sths
184740021f08SAnthony Liguori s->device = info->device;
1848663e8e51Sths
18499a7c2a59SMao Zhongyi e100_pci_reset(s, &local_err);
18509a7c2a59SMao Zhongyi if (local_err) {
18519a7c2a59SMao Zhongyi error_propagate(errp, local_err);
18529a7c2a59SMao Zhongyi return;
18539a7c2a59SMao Zhongyi }
1854663e8e51Sths
1855663e8e51Sths /* Add 64 * 2 EEPROM. i82557 and i82558 support a 64 word EEPROM,
1856663e8e51Sths * i82559 and later support 64 or 256 word EEPROM. */
18575fce2b3eSAlex Williamson s->eeprom = eeprom93xx_new(&pci_dev->qdev, EEPROM_SIZE);
1858663e8e51Sths
1859663e8e51Sths /* Handler for memory-mapped I/O */
1860eedfac6fSPaolo Bonzini memory_region_init_io(&s->mmio_bar, OBJECT(s), &eepro100_ops, s,
1861eedfac6fSPaolo Bonzini "eepro100-mmio", PCI_MEM_SIZE);
1862e824b2ccSAvi Kivity pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->mmio_bar);
1863eedfac6fSPaolo Bonzini memory_region_init_io(&s->io_bar, OBJECT(s), &eepro100_ops, s,
1864eedfac6fSPaolo Bonzini "eepro100-io", PCI_IO_SIZE);
1865e824b2ccSAvi Kivity pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar);
18665e6ffddeSAvi Kivity /* FIXME: flash aliases to mmio?! */
1867eedfac6fSPaolo Bonzini memory_region_init_io(&s->flash_bar, OBJECT(s), &eepro100_ops, s,
1868eedfac6fSPaolo Bonzini "eepro100-flash", PCI_FLASH_SIZE);
1869e824b2ccSAvi Kivity pci_register_bar(&s->dev, 2, 0, &s->flash_bar);
1870663e8e51Sths
1871508ef936SGerd Hoffmann qemu_macaddr_default_if_unset(&s->conf.macaddr);
1872ce0e58b3SStefan Weil logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6));
1873663e8e51Sths
1874663e8e51Sths nic_reset(s);
1875663e8e51Sths
1876e00e365eSMark McLoughlin s->nic = qemu_new_nic(&net_eepro100_info, &s->conf,
18777d0fefdfSAkihiko Odaki object_get_typename(OBJECT(pci_dev)),
18787d0fefdfSAkihiko Odaki pci_dev->qdev.id,
18797d0fefdfSAkihiko Odaki &pci_dev->qdev.mem_reentrancy_guard, s);
1880663e8e51Sths
1881b356f76dSJason Wang qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
1882b356f76dSJason Wang TRACE(OTHER, logout("%s\n", qemu_get_queue(s->nic)->info_str));
1883663e8e51Sths
1884a08d4367SJan Kiszka qemu_register_reset(nic_reset, s);
1885663e8e51Sths
1886e4d67e4fSMarc-André Lureau s->vmstate = g_memdup(&vmstate_eepro100, sizeof(vmstate_eepro100));
1887b356f76dSJason Wang s->vmstate->name = qemu_get_queue(s->nic)->model;
188899b16e8eSJuan Quintela vmstate_register_any(VMSTATE_IF(&pci_dev->qdev), s->vmstate, s);
1889663e8e51Sths }
1890663e8e51Sths
eepro100_instance_init(Object * obj)18917317bb17SGonglei static void eepro100_instance_init(Object *obj)
18927317bb17SGonglei {
18937317bb17SGonglei EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, PCI_DEVICE(obj));
18947317bb17SGonglei device_add_bootindex_property(obj, &s->conf.bootindex,
18957317bb17SGonglei "bootindex", "/ethernet-phy@0",
189640c2281cSMarkus Armbruster DEVICE(s));
18977317bb17SGonglei }
18987317bb17SGonglei
1899558c8634SStefan Weil static E100PCIDeviceInfo e100_devices[] = {
1900663e8e51Sths {
190139bffca2SAnthony Liguori .name = "i82550",
190239bffca2SAnthony Liguori .desc = "Intel i82550 Ethernet",
1903558c8634SStefan Weil .device = i82550,
1904558c8634SStefan Weil /* TODO: check device id. */
190540021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82551IT,
1906558c8634SStefan Weil /* Revision ID: 0x0c, 0x0d, 0x0e. */
190740021f08SAnthony Liguori .revision = 0x0e,
1908558c8634SStefan Weil /* TODO: check size of statistical counters. */
1909558c8634SStefan Weil .stats_size = 80,
1910558c8634SStefan Weil /* TODO: check extended tcb support. */
1911558c8634SStefan Weil .has_extended_tcb_support = true,
1912558c8634SStefan Weil .power_management = true,
1913558c8634SStefan Weil },{
191439bffca2SAnthony Liguori .name = "i82551",
191539bffca2SAnthony Liguori .desc = "Intel i82551 Ethernet",
1916558c8634SStefan Weil .device = i82551,
191740021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82551IT,
1918558c8634SStefan Weil /* Revision ID: 0x0f, 0x10. */
191940021f08SAnthony Liguori .revision = 0x0f,
1920558c8634SStefan Weil /* TODO: check size of statistical counters. */
1921558c8634SStefan Weil .stats_size = 80,
1922558c8634SStefan Weil .has_extended_tcb_support = true,
1923558c8634SStefan Weil .power_management = true,
1924558c8634SStefan Weil },{
192539bffca2SAnthony Liguori .name = "i82557a",
192639bffca2SAnthony Liguori .desc = "Intel i82557A Ethernet",
1927558c8634SStefan Weil .device = i82557A,
192840021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557,
192940021f08SAnthony Liguori .revision = 0x01,
1930558c8634SStefan Weil .power_management = false,
1931558c8634SStefan Weil },{
193239bffca2SAnthony Liguori .name = "i82557b",
193339bffca2SAnthony Liguori .desc = "Intel i82557B Ethernet",
1934558c8634SStefan Weil .device = i82557B,
193540021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557,
193640021f08SAnthony Liguori .revision = 0x02,
1937558c8634SStefan Weil .power_management = false,
1938558c8634SStefan Weil },{
193939bffca2SAnthony Liguori .name = "i82557c",
194039bffca2SAnthony Liguori .desc = "Intel i82557C Ethernet",
1941558c8634SStefan Weil .device = i82557C,
194240021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557,
194340021f08SAnthony Liguori .revision = 0x03,
1944558c8634SStefan Weil .power_management = false,
1945558c8634SStefan Weil },{
194639bffca2SAnthony Liguori .name = "i82558a",
194739bffca2SAnthony Liguori .desc = "Intel i82558A Ethernet",
1948558c8634SStefan Weil .device = i82558A,
194940021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557,
195040021f08SAnthony Liguori .revision = 0x04,
1951558c8634SStefan Weil .stats_size = 76,
1952558c8634SStefan Weil .has_extended_tcb_support = true,
1953558c8634SStefan Weil .power_management = true,
1954558c8634SStefan Weil },{
195539bffca2SAnthony Liguori .name = "i82558b",
195639bffca2SAnthony Liguori .desc = "Intel i82558B Ethernet",
1957558c8634SStefan Weil .device = i82558B,
195840021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557,
195940021f08SAnthony Liguori .revision = 0x05,
1960558c8634SStefan Weil .stats_size = 76,
1961558c8634SStefan Weil .has_extended_tcb_support = true,
1962558c8634SStefan Weil .power_management = true,
1963558c8634SStefan Weil },{
196439bffca2SAnthony Liguori .name = "i82559a",
196539bffca2SAnthony Liguori .desc = "Intel i82559A Ethernet",
1966558c8634SStefan Weil .device = i82559A,
196740021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557,
196840021f08SAnthony Liguori .revision = 0x06,
1969558c8634SStefan Weil .stats_size = 80,
1970558c8634SStefan Weil .has_extended_tcb_support = true,
1971558c8634SStefan Weil .power_management = true,
1972558c8634SStefan Weil },{
197339bffca2SAnthony Liguori .name = "i82559b",
197439bffca2SAnthony Liguori .desc = "Intel i82559B Ethernet",
1975558c8634SStefan Weil .device = i82559B,
197640021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557,
197740021f08SAnthony Liguori .revision = 0x07,
1978558c8634SStefan Weil .stats_size = 80,
1979558c8634SStefan Weil .has_extended_tcb_support = true,
1980558c8634SStefan Weil .power_management = true,
1981558c8634SStefan Weil },{
198239bffca2SAnthony Liguori .name = "i82559c",
198339bffca2SAnthony Liguori .desc = "Intel i82559C Ethernet",
1984558c8634SStefan Weil .device = i82559C,
198540021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557,
1986558c8634SStefan Weil #if 0
198740021f08SAnthony Liguori .revision = 0x08,
1988558c8634SStefan Weil #endif
1989558c8634SStefan Weil /* TODO: Windows wants revision id 0x0c. */
199040021f08SAnthony Liguori .revision = 0x0c,
1991ad03502bSIsaku Yamahata #if EEPROM_SIZE > 0
199240021f08SAnthony Liguori .subsystem_vendor_id = PCI_VENDOR_ID_INTEL,
199340021f08SAnthony Liguori .subsystem_id = 0x0040,
1994ad03502bSIsaku Yamahata #endif
1995558c8634SStefan Weil .stats_size = 80,
1996558c8634SStefan Weil .has_extended_tcb_support = true,
1997558c8634SStefan Weil .power_management = true,
1998558c8634SStefan Weil },{
199939bffca2SAnthony Liguori .name = "i82559er",
200039bffca2SAnthony Liguori .desc = "Intel i82559ER Ethernet",
2001558c8634SStefan Weil .device = i82559ER,
200240021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82551IT,
200340021f08SAnthony Liguori .revision = 0x09,
2004558c8634SStefan Weil .stats_size = 80,
2005558c8634SStefan Weil .has_extended_tcb_support = true,
2006558c8634SStefan Weil .power_management = true,
2007558c8634SStefan Weil },{
200839bffca2SAnthony Liguori .name = "i82562",
200939bffca2SAnthony Liguori .desc = "Intel i82562 Ethernet",
2010558c8634SStefan Weil .device = i82562,
2011558c8634SStefan Weil /* TODO: check device id. */
201240021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82551IT,
2013558c8634SStefan Weil /* TODO: wrong revision id. */
201440021f08SAnthony Liguori .revision = 0x0e,
2015558c8634SStefan Weil .stats_size = 80,
2016558c8634SStefan Weil .has_extended_tcb_support = true,
2017558c8634SStefan Weil .power_management = true,
2018db667a12SStefan Weil },{
2019db667a12SStefan Weil /* Toshiba Tecra 8200. */
202039bffca2SAnthony Liguori .name = "i82801",
202139bffca2SAnthony Liguori .desc = "Intel i82801 Ethernet",
2022db667a12SStefan Weil .device = i82801,
202340021f08SAnthony Liguori .device_id = 0x2449,
202440021f08SAnthony Liguori .revision = 0x03,
2025db667a12SStefan Weil .stats_size = 80,
2026db667a12SStefan Weil .has_extended_tcb_support = true,
2027db667a12SStefan Weil .power_management = true,
2028663e8e51Sths }
2029558c8634SStefan Weil };
2030663e8e51Sths
eepro100_get_class_by_name(const char * typename)203140021f08SAnthony Liguori static E100PCIDeviceInfo *eepro100_get_class_by_name(const char *typename)
203240021f08SAnthony Liguori {
203340021f08SAnthony Liguori E100PCIDeviceInfo *info = NULL;
203440021f08SAnthony Liguori int i;
203540021f08SAnthony Liguori
203640021f08SAnthony Liguori /* This is admittedly awkward but also temporary. QOM allows for
203740021f08SAnthony Liguori * parameterized typing and for subclassing both of which would suitable
203840021f08SAnthony Liguori * handle what's going on here. But class_data is already being used as
203940021f08SAnthony Liguori * a stop-gap hack to allow incremental qdev conversion so we cannot use it
204040021f08SAnthony Liguori * right now. Once we merge the final QOM series, we can come back here and
204140021f08SAnthony Liguori * do this in a much more elegant fashion.
204240021f08SAnthony Liguori */
204340021f08SAnthony Liguori for (i = 0; i < ARRAY_SIZE(e100_devices); i++) {
204439bffca2SAnthony Liguori if (strcmp(e100_devices[i].name, typename) == 0) {
204540021f08SAnthony Liguori info = &e100_devices[i];
204640021f08SAnthony Liguori break;
204740021f08SAnthony Liguori }
204840021f08SAnthony Liguori }
204940021f08SAnthony Liguori assert(info != NULL);
205040021f08SAnthony Liguori
205140021f08SAnthony Liguori return info;
205240021f08SAnthony Liguori }
205340021f08SAnthony Liguori
eepro100_get_class(EEPRO100State * s)205440021f08SAnthony Liguori static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s)
205540021f08SAnthony Liguori {
205640021f08SAnthony Liguori return eepro100_get_class_by_name(object_get_typename(OBJECT(s)));
205740021f08SAnthony Liguori }
205840021f08SAnthony Liguori
2059e732f00fSRichard Henderson static const Property e100_properties[] = {
206039bffca2SAnthony Liguori DEFINE_NIC_PROPERTIES(EEPRO100State, conf),
206139bffca2SAnthony Liguori };
206239bffca2SAnthony Liguori
eepro100_class_init(ObjectClass * klass,const void * data)206312d1a768SPhilippe Mathieu-Daudé static void eepro100_class_init(ObjectClass *klass, const void *data)
206440021f08SAnthony Liguori {
206539bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass);
206640021f08SAnthony Liguori PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
206740021f08SAnthony Liguori E100PCIDeviceInfo *info;
206840021f08SAnthony Liguori
206940021f08SAnthony Liguori info = eepro100_get_class_by_name(object_class_get_name(klass));
207040021f08SAnthony Liguori
2071125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
20724f67d30bSMarc-André Lureau device_class_set_props(dc, e100_properties);
207339bffca2SAnthony Liguori dc->desc = info->desc;
207440021f08SAnthony Liguori k->vendor_id = PCI_VENDOR_ID_INTEL;
207540021f08SAnthony Liguori k->class_id = PCI_CLASS_NETWORK_ETHERNET;
207640021f08SAnthony Liguori k->romfile = "pxe-eepro100.rom";
20779af21dbeSMarkus Armbruster k->realize = e100_nic_realize;
207840021f08SAnthony Liguori k->exit = pci_nic_uninit;
207940021f08SAnthony Liguori k->device_id = info->device_id;
208040021f08SAnthony Liguori k->revision = info->revision;
208140021f08SAnthony Liguori k->subsystem_vendor_id = info->subsystem_vendor_id;
208240021f08SAnthony Liguori k->subsystem_id = info->subsystem_id;
208340021f08SAnthony Liguori }
208440021f08SAnthony Liguori
eepro100_register_types(void)208583f7d43aSAndreas Färber static void eepro100_register_types(void)
20869d07d757SPaul Brook {
2087558c8634SStefan Weil size_t i;
2088558c8634SStefan Weil for (i = 0; i < ARRAY_SIZE(e100_devices); i++) {
208939bffca2SAnthony Liguori TypeInfo type_info = {};
209039bffca2SAnthony Liguori E100PCIDeviceInfo *info = &e100_devices[i];
209140021f08SAnthony Liguori
209239bffca2SAnthony Liguori type_info.name = info->name;
209339bffca2SAnthony Liguori type_info.parent = TYPE_PCI_DEVICE;
209439bffca2SAnthony Liguori type_info.class_init = eepro100_class_init;
209539bffca2SAnthony Liguori type_info.instance_size = sizeof(EEPRO100State);
20967317bb17SGonglei type_info.instance_init = eepro100_instance_init;
2097*2cd09e47SPhilippe Mathieu-Daudé type_info.interfaces = (const InterfaceInfo[]) {
2098fd3b02c8SEduardo Habkost { INTERFACE_CONVENTIONAL_PCI_DEVICE },
2099fd3b02c8SEduardo Habkost { },
2100fd3b02c8SEduardo Habkost };
210140021f08SAnthony Liguori
210229ec04f0SZhao Liu type_register_static(&type_info);
2103558c8634SStefan Weil }
21049d07d757SPaul Brook }
21059d07d757SPaul Brook
210683f7d43aSAndreas Färber type_init(eepro100_register_types)
2107