1663e8e51Sths /* 2663e8e51Sths * QEMU i8255x (PRO100) emulation 3663e8e51Sths * 41b4f97d6SStefan Weil * Copyright (C) 2006-2011 Stefan Weil 5663e8e51Sths * 6663e8e51Sths * Portions of the code are copies from grub / etherboot eepro100.c 7663e8e51Sths * and linux e100.c. 8663e8e51Sths * 9230a167cSStefan Weil * This program is free software: you can redistribute it and/or modify 10663e8e51Sths * it under the terms of the GNU General Public License as published by 11230a167cSStefan Weil * the Free Software Foundation, either version 2 of the License, or 12230a167cSStefan Weil * (at your option) version 3 or any later version. 13663e8e51Sths * 14663e8e51Sths * This program is distributed in the hope that it will be useful, 15663e8e51Sths * but WITHOUT ANY WARRANTY; without even the implied warranty of 16663e8e51Sths * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17663e8e51Sths * GNU General Public License for more details. 18663e8e51Sths * 19663e8e51Sths * You should have received a copy of the GNU General Public License 20230a167cSStefan Weil * along with this program. If not, see <http://www.gnu.org/licenses/>. 21663e8e51Sths * 22663e8e51Sths * Tested features (i82559): 23e5e23ab8SStefan Weil * PXE boot (i386 guest, i386 / mips / mipsel / ppc host) ok 24663e8e51Sths * Linux networking (i386) ok 25663e8e51Sths * 26663e8e51Sths * Untested: 27663e8e51Sths * Windows networking 28663e8e51Sths * 29663e8e51Sths * References: 30663e8e51Sths * 31663e8e51Sths * Intel 8255x 10/100 Mbps Ethernet Controller Family 32663e8e51Sths * Open Source Software Developer Manual 33ba19f2deSStefan Weil * 34ba19f2deSStefan Weil * TODO: 35ba19f2deSStefan Weil * * PHY emulation should be separated from nic emulation. 36ba19f2deSStefan Weil * Most nic emulations could share the same phy code. 37ba19f2deSStefan Weil * * i82550 is untested. It is programmed like the i82559. 38ba19f2deSStefan Weil * * i82562 is untested. It is programmed like the i82559. 39ba19f2deSStefan Weil * * Power management (i82558 and later) is not implemented. 40ba19f2deSStefan Weil * * Wake-on-LAN is not implemented. 41663e8e51Sths */ 42663e8e51Sths 43e8d40465SPeter Maydell #include "qemu/osdep.h" 44872a2b7cSPhilippe Mathieu-Daudé #include "qemu/units.h" 4583c9f4caSPaolo Bonzini #include "hw/pci/pci.h" 46a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 47d6454270SMarkus Armbruster #include "migration/vmstate.h" 481422e32dSPaolo Bonzini #include "net/net.h" 497c0348bdSMark Cave-Ayland #include "net/eth.h" 500d09e41aSPaolo Bonzini #include "hw/nvram/eeprom93xx.h" 519c17d615SPaolo Bonzini #include "sysemu/sysemu.h" 529c17d615SPaolo Bonzini #include "sysemu/dma.h" 5371e8a915SMarkus Armbruster #include "sysemu/reset.h" 54949fc823SMarcel Apfelbaum #include "qemu/bitops.h" 550b8fa32fSMarkus Armbruster #include "qemu/module.h" 569a7c2a59SMao Zhongyi #include "qapi/error.h" 57663e8e51Sths 58792f1d63SStefan Weil /* QEMU sends frames smaller than 60 bytes to ethernet nics. 59792f1d63SStefan Weil * Such frames are rejected by real nics and their emulations. 60792f1d63SStefan Weil * To avoid this behaviour, other nic emulations pad received 61792f1d63SStefan Weil * frames. The following definition enables this padding for 62792f1d63SStefan Weil * eepro100, too. We keep the define around in case it might 63792f1d63SStefan Weil * become useful the future if the core networking is ever 64792f1d63SStefan Weil * changed to pad short packets itself. */ 65792f1d63SStefan Weil #define CONFIG_PAD_RECEIVED_FRAMES 66792f1d63SStefan Weil 67aac443e6SStefan Weil /* Debug EEPRO100 card. */ 68ce0e58b3SStefan Weil #if 0 69ce0e58b3SStefan Weil # define DEBUG_EEPRO100 70ce0e58b3SStefan Weil #endif 71663e8e51Sths 72663e8e51Sths #ifdef DEBUG_EEPRO100 73001faf32SBlue Swirl #define logout(fmt, ...) fprintf(stderr, "EE100\t%-24s" fmt, __func__, ## __VA_ARGS__) 74663e8e51Sths #else 75001faf32SBlue Swirl #define logout(fmt, ...) ((void)0) 76663e8e51Sths #endif 77663e8e51Sths 78663e8e51Sths /* Set flags to 0 to disable debug output. */ 79aac443e6SStefan Weil #define INT 1 /* interrupt related actions */ 80aac443e6SStefan Weil #define MDI 1 /* mdi related actions */ 81aac443e6SStefan Weil #define OTHER 1 82aac443e6SStefan Weil #define RXTX 1 83aac443e6SStefan Weil #define EEPROM 1 /* eeprom related actions */ 84663e8e51Sths 85663e8e51Sths #define TRACE(flag, command) ((flag) ? (command) : (void)0) 86663e8e51Sths 877f1e9d4eSKevin Wolf #define missing(text) fprintf(stderr, "eepro100: feature is missing in this emulation: " text "\n") 88663e8e51Sths 89663e8e51Sths #define MAX_ETH_FRAME_SIZE 1514 90663e8e51Sths 91663e8e51Sths /* This driver supports several different devices which are declared here. */ 92c4c270e2SStefan Weil #define i82550 0x82550 93663e8e51Sths #define i82551 0x82551 94c4c270e2SStefan Weil #define i82557A 0x82557a 95663e8e51Sths #define i82557B 0x82557b 96663e8e51Sths #define i82557C 0x82557c 97c4c270e2SStefan Weil #define i82558A 0x82558a 98663e8e51Sths #define i82558B 0x82558b 99c4c270e2SStefan Weil #define i82559A 0x82559a 100c4c270e2SStefan Weil #define i82559B 0x82559b 101663e8e51Sths #define i82559C 0x82559c 102663e8e51Sths #define i82559ER 0x82559e 103663e8e51Sths #define i82562 0x82562 104db667a12SStefan Weil #define i82801 0x82801 105663e8e51Sths 106aac443e6SStefan Weil /* Use 64 word EEPROM. TODO: could be a runtime option. */ 107663e8e51Sths #define EEPROM_SIZE 64 108663e8e51Sths 109663e8e51Sths #define PCI_MEM_SIZE (4 * KiB) 110663e8e51Sths #define PCI_IO_SIZE 64 111663e8e51Sths #define PCI_FLASH_SIZE (128 * KiB) 112663e8e51Sths 113663e8e51Sths #define BITS(n, m) (((0xffffffffU << (31 - n)) >> (31 - n + m)) << m) 114663e8e51Sths 115663e8e51Sths /* The SCB accepts the following controls for the Tx and Rx units: */ 116663e8e51Sths #define CU_NOP 0x0000 /* No operation. */ 117663e8e51Sths #define CU_START 0x0010 /* CU start. */ 118663e8e51Sths #define CU_RESUME 0x0020 /* CU resume. */ 119663e8e51Sths #define CU_STATSADDR 0x0040 /* Load dump counters address. */ 120663e8e51Sths #define CU_SHOWSTATS 0x0050 /* Dump statistical counters. */ 121663e8e51Sths #define CU_CMD_BASE 0x0060 /* Load CU base address. */ 122663e8e51Sths #define CU_DUMPSTATS 0x0070 /* Dump and reset statistical counters. */ 123663e8e51Sths #define CU_SRESUME 0x00a0 /* CU static resume. */ 124663e8e51Sths 125663e8e51Sths #define RU_NOP 0x0000 126663e8e51Sths #define RX_START 0x0001 127663e8e51Sths #define RX_RESUME 0x0002 128e824012bSStefan Weil #define RU_ABORT 0x0004 129663e8e51Sths #define RX_ADDR_LOAD 0x0006 130663e8e51Sths #define RX_RESUMENR 0x0007 131663e8e51Sths #define INT_MASK 0x0100 132663e8e51Sths #define DRVR_INT 0x0200 /* Driver generated interrupt. */ 133663e8e51Sths 134558c8634SStefan Weil typedef struct { 13539bffca2SAnthony Liguori const char *name; 13639bffca2SAnthony Liguori const char *desc; 13740021f08SAnthony Liguori uint16_t device_id; 13840021f08SAnthony Liguori uint8_t revision; 13940021f08SAnthony Liguori uint16_t subsystem_vendor_id; 14040021f08SAnthony Liguori uint16_t subsystem_id; 14140021f08SAnthony Liguori 142558c8634SStefan Weil uint32_t device; 143558c8634SStefan Weil uint8_t stats_size; 144558c8634SStefan Weil bool has_extended_tcb_support; 145558c8634SStefan Weil bool power_management; 146558c8634SStefan Weil } E100PCIDeviceInfo; 147558c8634SStefan Weil 148663e8e51Sths /* Offsets to the various registers. 149663e8e51Sths All accesses need not be longword aligned. */ 150e5e23ab8SStefan Weil typedef enum { 1510908bba1SStefan Weil SCBStatus = 0, /* Status Word. */ 152663e8e51Sths SCBAck = 1, 153663e8e51Sths SCBCmd = 2, /* Rx/Command Unit command and status. */ 154663e8e51Sths SCBIntmask = 3, 155663e8e51Sths SCBPointer = 4, /* General purpose pointer. */ 156663e8e51Sths SCBPort = 8, /* Misc. commands and operands. */ 1570908bba1SStefan Weil SCBflash = 12, /* Flash memory control. */ 1580908bba1SStefan Weil SCBeeprom = 14, /* EEPROM control. */ 159663e8e51Sths SCBCtrlMDI = 16, /* MDI interface control. */ 160663e8e51Sths SCBEarlyRx = 20, /* Early receive byte count. */ 1610908bba1SStefan Weil SCBFlow = 24, /* Flow Control. */ 1620908bba1SStefan Weil SCBpmdr = 27, /* Power Management Driver. */ 1630908bba1SStefan Weil SCBgctrl = 28, /* General Control. */ 1640908bba1SStefan Weil SCBgstat = 29, /* General Status. */ 165e5e23ab8SStefan Weil } E100RegisterOffset; 166663e8e51Sths 167663e8e51Sths /* A speedo3 transmit buffer descriptor with two buffers... */ 168663e8e51Sths typedef struct { 169663e8e51Sths uint16_t status; 170663e8e51Sths uint16_t command; 171663e8e51Sths uint32_t link; /* void * */ 1727b8737deSStefan Weil uint32_t tbd_array_addr; /* transmit buffer descriptor array address. */ 173663e8e51Sths uint16_t tcb_bytes; /* transmit command block byte count (in lower 14 bits */ 174663e8e51Sths uint8_t tx_threshold; /* transmit threshold */ 175663e8e51Sths uint8_t tbd_count; /* TBD number */ 176e7493b25SStefan Weil #if 0 177e7493b25SStefan Weil /* This constitutes two "TBD" entries: hdr and data */ 178e7493b25SStefan Weil uint32_t tx_buf_addr0; /* void *, header of frame to be transmitted. */ 179e7493b25SStefan Weil int32_t tx_buf_size0; /* Length of Tx hdr. */ 180e7493b25SStefan Weil uint32_t tx_buf_addr1; /* void *, data to be transmitted. */ 181e7493b25SStefan Weil int32_t tx_buf_size1; /* Length of Tx data. */ 182e7493b25SStefan Weil #endif 183c227f099SAnthony Liguori } eepro100_tx_t; 184663e8e51Sths 185663e8e51Sths /* Receive frame descriptor. */ 186663e8e51Sths typedef struct { 187663e8e51Sths int16_t status; 188663e8e51Sths uint16_t command; 189663e8e51Sths uint32_t link; /* struct RxFD * */ 190663e8e51Sths uint32_t rx_buf_addr; /* void * */ 191663e8e51Sths uint16_t count; 192663e8e51Sths uint16_t size; 19327112f18SStefan Weil /* Ethernet frame data follows. */ 194c227f099SAnthony Liguori } eepro100_rx_t; 195663e8e51Sths 196ced5296aSStefan Weil typedef enum { 197ced5296aSStefan Weil COMMAND_EL = BIT(15), 198ced5296aSStefan Weil COMMAND_S = BIT(14), 199ced5296aSStefan Weil COMMAND_I = BIT(13), 200ced5296aSStefan Weil COMMAND_NC = BIT(4), 201ced5296aSStefan Weil COMMAND_SF = BIT(3), 202ced5296aSStefan Weil COMMAND_CMD = BITS(2, 0), 203ced5296aSStefan Weil } scb_command_bit; 204ced5296aSStefan Weil 205ced5296aSStefan Weil typedef enum { 206ced5296aSStefan Weil STATUS_C = BIT(15), 207ced5296aSStefan Weil STATUS_OK = BIT(13), 208ced5296aSStefan Weil } scb_status_bit; 209ced5296aSStefan Weil 210663e8e51Sths typedef struct { 211663e8e51Sths uint32_t tx_good_frames, tx_max_collisions, tx_late_collisions, 212663e8e51Sths tx_underruns, tx_lost_crs, tx_deferred, tx_single_collisions, 213663e8e51Sths tx_multiple_collisions, tx_total_collisions; 214663e8e51Sths uint32_t rx_good_frames, rx_crc_errors, rx_alignment_errors, 215663e8e51Sths rx_resource_errors, rx_overrun_errors, rx_cdt_errors, 216663e8e51Sths rx_short_frame_errors; 217663e8e51Sths uint32_t fc_xmt_pause, fc_rcv_pause, fc_rcv_unsupported; 218663e8e51Sths uint16_t xmt_tco_frames, rcv_tco_frames; 219ba42b646SStefan Weil /* TODO: i82559 has six reserved statistics but a total of 24 dwords. */ 220ba42b646SStefan Weil uint32_t reserved[4]; 221c227f099SAnthony Liguori } eepro100_stats_t; 222663e8e51Sths 223663e8e51Sths typedef enum { 224663e8e51Sths cu_idle = 0, 225663e8e51Sths cu_suspended = 1, 226663e8e51Sths cu_active = 2, 227663e8e51Sths cu_lpq_active = 2, 228663e8e51Sths cu_hqp_active = 3 229c227f099SAnthony Liguori } cu_state_t; 230663e8e51Sths 231663e8e51Sths typedef enum { 232663e8e51Sths ru_idle = 0, 233663e8e51Sths ru_suspended = 1, 234663e8e51Sths ru_no_resources = 2, 235663e8e51Sths ru_ready = 4 236c227f099SAnthony Liguori } ru_state_t; 237663e8e51Sths 238663e8e51Sths typedef struct { 239273a2142SJuan Quintela PCIDevice dev; 240010ec629SStefan Weil /* Hash register (multicast mask array, multiple individual addresses). */ 241010ec629SStefan Weil uint8_t mult[8]; 2425e6ffddeSAvi Kivity MemoryRegion mmio_bar; 2435e6ffddeSAvi Kivity MemoryRegion io_bar; 2445e6ffddeSAvi Kivity MemoryRegion flash_bar; 245e00e365eSMark McLoughlin NICState *nic; 246508ef936SGerd Hoffmann NICConf conf; 247663e8e51Sths uint8_t scb_stat; /* SCB stat/ack byte */ 248663e8e51Sths uint8_t int_stat; /* PCI interrupt status */ 2493706c43fSStefan Weil /* region must not be saved by nic_save. */ 250663e8e51Sths uint16_t mdimem[32]; 251c227f099SAnthony Liguori eeprom_t *eeprom; 252663e8e51Sths uint32_t device; /* device variant */ 253663e8e51Sths /* (cu_base + cu_offset) address the next command block in the command block list. */ 254663e8e51Sths uint32_t cu_base; /* CU base address */ 255663e8e51Sths uint32_t cu_offset; /* CU address offset */ 256663e8e51Sths /* (ru_base + ru_offset) address the RFD in the Receive Frame Area. */ 257663e8e51Sths uint32_t ru_base; /* RU base address */ 258663e8e51Sths uint32_t ru_offset; /* RU address offset */ 259c227f099SAnthony Liguori uint32_t statsaddr; /* pointer to eepro100_stats_t */ 260ba42b646SStefan Weil 261f3a52e50SStefan Weil /* Temporary status information (no need to save these values), 262f3a52e50SStefan Weil * used while processing CU commands. */ 263f3a52e50SStefan Weil eepro100_tx_t tx; /* transmit buffer descriptor */ 264f3a52e50SStefan Weil uint32_t cb_address; /* = cu_base + cu_offset */ 265f3a52e50SStefan Weil 266ba42b646SStefan Weil /* Statistical counters. Also used for wake-up packet (i82559). */ 267ba42b646SStefan Weil eepro100_stats_t statistics; 268ba42b646SStefan Weil 269e5e23ab8SStefan Weil /* Data in mem is always in the byte order of the controller (le). 270e5e23ab8SStefan Weil * It must be dword aligned to allow direct access to 32 bit values. */ 2713a93113aSDong Xu Wang uint8_t mem[PCI_MEM_SIZE] __attribute__((aligned(8))); 272e5e23ab8SStefan Weil 273663e8e51Sths /* Configuration bytes. */ 274663e8e51Sths uint8_t configuration[22]; 275663e8e51Sths 276151b2986SJuan Quintela /* vmstate for each particular nic */ 277151b2986SJuan Quintela VMStateDescription *vmstate; 278ba42b646SStefan Weil 279ba42b646SStefan Weil /* Quasi static device properties (no need to save them). */ 280ba42b646SStefan Weil uint16_t stats_size; 281ba42b646SStefan Weil bool has_extended_tcb_support; 282663e8e51Sths } EEPRO100State; 283663e8e51Sths 2846cded3a4SStefan Weil /* Word indices in EEPROM. */ 2856cded3a4SStefan Weil typedef enum { 2866cded3a4SStefan Weil EEPROM_CNFG_MDIX = 0x03, 2876cded3a4SStefan Weil EEPROM_ID = 0x05, 2886cded3a4SStefan Weil EEPROM_PHY_ID = 0x06, 2896cded3a4SStefan Weil EEPROM_VENDOR_ID = 0x0c, 2906cded3a4SStefan Weil EEPROM_CONFIG_ASF = 0x0d, 2916cded3a4SStefan Weil EEPROM_DEVICE_ID = 0x23, 2926cded3a4SStefan Weil EEPROM_SMBUS_ADDR = 0x90, 2936cded3a4SStefan Weil } EEPROMOffset; 2946cded3a4SStefan Weil 295b1e87018SStefan Weil /* Bit values for EEPROM ID word. */ 296b1e87018SStefan Weil typedef enum { 297b1e87018SStefan Weil EEPROM_ID_MDM = BIT(0), /* Modem */ 298b1e87018SStefan Weil EEPROM_ID_STB = BIT(1), /* Standby Enable */ 299b1e87018SStefan Weil EEPROM_ID_WMR = BIT(2), /* ??? */ 300b1e87018SStefan Weil EEPROM_ID_WOL = BIT(5), /* Wake on LAN */ 301b1e87018SStefan Weil EEPROM_ID_DPD = BIT(6), /* Deep Power Down */ 302b1e87018SStefan Weil EEPROM_ID_ALT = BIT(7), /* */ 303b1e87018SStefan Weil /* BITS(10, 8) device revision */ 304b1e87018SStefan Weil EEPROM_ID_BD = BIT(11), /* boot disable */ 305b1e87018SStefan Weil EEPROM_ID_ID = BIT(13), /* id bit */ 306b1e87018SStefan Weil /* BITS(15, 14) signature */ 307b1e87018SStefan Weil EEPROM_ID_VALID = BIT(14), /* signature for valid eeprom */ 308b1e87018SStefan Weil } eeprom_id_bit; 309b1e87018SStefan Weil 310663e8e51Sths /* Default values for MDI (PHY) registers */ 311663e8e51Sths static const uint16_t eepro100_mdi_default[] = { 312663e8e51Sths /* MDI Registers 0 - 6, 7 */ 313663e8e51Sths 0x3000, 0x780d, 0x02a8, 0x0154, 0x05e1, 0x0000, 0x0000, 0x0000, 314663e8e51Sths /* MDI Registers 8 - 15 */ 315663e8e51Sths 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 316663e8e51Sths /* MDI Registers 16 - 31 */ 317663e8e51Sths 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 318663e8e51Sths 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 319663e8e51Sths }; 320663e8e51Sths 321663e8e51Sths /* Readonly mask for MDI (PHY) registers */ 322663e8e51Sths static const uint16_t eepro100_mdi_mask[] = { 323663e8e51Sths 0x0000, 0xffff, 0xffff, 0xffff, 0xc01f, 0xffff, 0xffff, 0x0000, 324663e8e51Sths 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 325663e8e51Sths 0x0fff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 326663e8e51Sths 0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 327663e8e51Sths }; 328663e8e51Sths 32940021f08SAnthony Liguori static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s); 33040021f08SAnthony Liguori 331e5e23ab8SStefan Weil /* Read a 16 bit control/status (CSR) register. */ 332e5e23ab8SStefan Weil static uint16_t e100_read_reg2(EEPRO100State *s, E100RegisterOffset addr) 333e5e23ab8SStefan Weil { 334e5e23ab8SStefan Weil assert(!((uintptr_t)&s->mem[addr] & 1)); 3354d9be252SPeter Maydell return lduw_le_p(&s->mem[addr]); 336e5e23ab8SStefan Weil } 337e5e23ab8SStefan Weil 338e5e23ab8SStefan Weil /* Read a 32 bit control/status (CSR) register. */ 339e5e23ab8SStefan Weil static uint32_t e100_read_reg4(EEPRO100State *s, E100RegisterOffset addr) 340e5e23ab8SStefan Weil { 341e5e23ab8SStefan Weil assert(!((uintptr_t)&s->mem[addr] & 3)); 3424d9be252SPeter Maydell return ldl_le_p(&s->mem[addr]); 343e5e23ab8SStefan Weil } 344e5e23ab8SStefan Weil 345e5e23ab8SStefan Weil /* Write a 16 bit control/status (CSR) register. */ 346e5e23ab8SStefan Weil static void e100_write_reg2(EEPRO100State *s, E100RegisterOffset addr, 347e5e23ab8SStefan Weil uint16_t val) 348e5e23ab8SStefan Weil { 349e5e23ab8SStefan Weil assert(!((uintptr_t)&s->mem[addr] & 1)); 3504d9be252SPeter Maydell stw_le_p(&s->mem[addr], val); 351e5e23ab8SStefan Weil } 352e5e23ab8SStefan Weil 353e5e23ab8SStefan Weil /* Read a 32 bit control/status (CSR) register. */ 354e5e23ab8SStefan Weil static void e100_write_reg4(EEPRO100State *s, E100RegisterOffset addr, 355e5e23ab8SStefan Weil uint32_t val) 356e5e23ab8SStefan Weil { 357e5e23ab8SStefan Weil assert(!((uintptr_t)&s->mem[addr] & 3)); 3584d9be252SPeter Maydell stl_le_p(&s->mem[addr], val); 359e5e23ab8SStefan Weil } 360e5e23ab8SStefan Weil 361663e8e51Sths #if defined(DEBUG_EEPRO100) 362663e8e51Sths static const char *nic_dump(const uint8_t * buf, unsigned size) 363663e8e51Sths { 364663e8e51Sths static char dump[3 * 16 + 1]; 365663e8e51Sths char *p = &dump[0]; 366aac443e6SStefan Weil if (size > 16) { 367663e8e51Sths size = 16; 368aac443e6SStefan Weil } 369663e8e51Sths while (size-- > 0) { 370663e8e51Sths p += sprintf(p, " %02x", *buf++); 371663e8e51Sths } 372663e8e51Sths return dump; 373663e8e51Sths } 374663e8e51Sths #endif /* DEBUG_EEPRO100 */ 375663e8e51Sths 376663e8e51Sths enum scb_stat_ack { 377663e8e51Sths stat_ack_not_ours = 0x00, 378663e8e51Sths stat_ack_sw_gen = 0x04, 379663e8e51Sths stat_ack_rnr = 0x10, 380663e8e51Sths stat_ack_cu_idle = 0x20, 381663e8e51Sths stat_ack_frame_rx = 0x40, 382663e8e51Sths stat_ack_cu_cmd_done = 0x80, 383663e8e51Sths stat_ack_not_present = 0xFF, 384663e8e51Sths stat_ack_rx = (stat_ack_sw_gen | stat_ack_rnr | stat_ack_frame_rx), 385663e8e51Sths stat_ack_tx = (stat_ack_cu_idle | stat_ack_cu_cmd_done), 386663e8e51Sths }; 387663e8e51Sths 388663e8e51Sths static void disable_interrupt(EEPRO100State * s) 389663e8e51Sths { 390663e8e51Sths if (s->int_stat) { 391aac443e6SStefan Weil TRACE(INT, logout("interrupt disabled\n")); 3929e64f8a3SMarcel Apfelbaum pci_irq_deassert(&s->dev); 393663e8e51Sths s->int_stat = 0; 394663e8e51Sths } 395663e8e51Sths } 396663e8e51Sths 397663e8e51Sths static void enable_interrupt(EEPRO100State * s) 398663e8e51Sths { 399663e8e51Sths if (!s->int_stat) { 400aac443e6SStefan Weil TRACE(INT, logout("interrupt enabled\n")); 4019e64f8a3SMarcel Apfelbaum pci_irq_assert(&s->dev); 402663e8e51Sths s->int_stat = 1; 403663e8e51Sths } 404663e8e51Sths } 405663e8e51Sths 406663e8e51Sths static void eepro100_acknowledge(EEPRO100State * s) 407663e8e51Sths { 408663e8e51Sths s->scb_stat &= ~s->mem[SCBAck]; 409663e8e51Sths s->mem[SCBAck] = s->scb_stat; 410663e8e51Sths if (s->scb_stat == 0) { 411663e8e51Sths disable_interrupt(s); 412663e8e51Sths } 413663e8e51Sths } 414663e8e51Sths 415e715c8e8SStefan Weil static void eepro100_interrupt(EEPRO100State * s, uint8_t status) 416663e8e51Sths { 417663e8e51Sths uint8_t mask = ~s->mem[SCBIntmask]; 418e715c8e8SStefan Weil s->mem[SCBAck] |= status; 419e715c8e8SStefan Weil status = s->scb_stat = s->mem[SCBAck]; 420e715c8e8SStefan Weil status &= (mask | 0x0f); 421e7493b25SStefan Weil #if 0 422e7493b25SStefan Weil status &= (~s->mem[SCBIntmask] | 0x0xf); 423e7493b25SStefan Weil #endif 424e715c8e8SStefan Weil if (status && (mask & 0x01)) { 425663e8e51Sths /* SCB mask and SCB Bit M do not disable interrupt. */ 426663e8e51Sths enable_interrupt(s); 427663e8e51Sths } else if (s->int_stat) { 428663e8e51Sths disable_interrupt(s); 429663e8e51Sths } 430663e8e51Sths } 431663e8e51Sths 432663e8e51Sths static void eepro100_cx_interrupt(EEPRO100State * s) 433663e8e51Sths { 434663e8e51Sths /* CU completed action command. */ 435663e8e51Sths /* Transmit not ok (82557 only, not in emulation). */ 436663e8e51Sths eepro100_interrupt(s, 0x80); 437663e8e51Sths } 438663e8e51Sths 439663e8e51Sths static void eepro100_cna_interrupt(EEPRO100State * s) 440663e8e51Sths { 441663e8e51Sths /* CU left the active state. */ 442663e8e51Sths eepro100_interrupt(s, 0x20); 443663e8e51Sths } 444663e8e51Sths 445663e8e51Sths static void eepro100_fr_interrupt(EEPRO100State * s) 446663e8e51Sths { 447663e8e51Sths /* RU received a complete frame. */ 448663e8e51Sths eepro100_interrupt(s, 0x40); 449663e8e51Sths } 450663e8e51Sths 451663e8e51Sths static void eepro100_rnr_interrupt(EEPRO100State * s) 452663e8e51Sths { 453663e8e51Sths /* RU is not ready. */ 454663e8e51Sths eepro100_interrupt(s, 0x10); 455663e8e51Sths } 456663e8e51Sths 457663e8e51Sths static void eepro100_mdi_interrupt(EEPRO100State * s) 458663e8e51Sths { 459663e8e51Sths /* MDI completed read or write cycle. */ 460663e8e51Sths eepro100_interrupt(s, 0x08); 461663e8e51Sths } 462663e8e51Sths 463663e8e51Sths static void eepro100_swi_interrupt(EEPRO100State * s) 464663e8e51Sths { 465663e8e51Sths /* Software has requested an interrupt. */ 466663e8e51Sths eepro100_interrupt(s, 0x04); 467663e8e51Sths } 468663e8e51Sths 469663e8e51Sths #if 0 470663e8e51Sths static void eepro100_fcp_interrupt(EEPRO100State * s) 471663e8e51Sths { 472663e8e51Sths /* Flow control pause interrupt (82558 and later). */ 473663e8e51Sths eepro100_interrupt(s, 0x01); 474663e8e51Sths } 475663e8e51Sths #endif 476663e8e51Sths 4779a7c2a59SMao Zhongyi static void e100_pci_reset(EEPRO100State *s, Error **errp) 478663e8e51Sths { 47940021f08SAnthony Liguori E100PCIDeviceInfo *info = eepro100_get_class(s); 480663e8e51Sths uint32_t device = s->device; 481273a2142SJuan Quintela uint8_t *pci_conf = s->dev.config; 482663e8e51Sths 483aac443e6SStefan Weil TRACE(OTHER, logout("%p\n", s)); 484663e8e51Sths 485663e8e51Sths /* PCI Status */ 486558c8634SStefan Weil pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM | 487558c8634SStefan Weil PCI_STATUS_FAST_BACK); 488663e8e51Sths /* PCI Latency Timer */ 48915e89f59SMichael S. Tsirkin pci_set_byte(pci_conf + PCI_LATENCY_TIMER, 0x20); /* latency timer = 32 clocks */ 490ae543b49SStefan Weil /* Capability Pointer is set by PCI framework. */ 491f62719caSStefan Weil /* Interrupt Line */ 492f62719caSStefan Weil /* Interrupt Pin */ 493f62719caSStefan Weil pci_set_byte(pci_conf + PCI_INTERRUPT_PIN, 1); /* interrupt pin A */ 494663e8e51Sths /* Minimum Grant */ 49515e89f59SMichael S. Tsirkin pci_set_byte(pci_conf + PCI_MIN_GNT, 0x08); 496663e8e51Sths /* Maximum Latency */ 49715e89f59SMichael S. Tsirkin pci_set_byte(pci_conf + PCI_MAX_LAT, 0x18); 498663e8e51Sths 49940021f08SAnthony Liguori s->stats_size = info->stats_size; 50040021f08SAnthony Liguori s->has_extended_tcb_support = info->has_extended_tcb_support; 501558c8634SStefan Weil 502663e8e51Sths switch (device) { 503ba42b646SStefan Weil case i82550: 504663e8e51Sths case i82551: 505ba42b646SStefan Weil case i82557A: 506663e8e51Sths case i82557B: 507663e8e51Sths case i82557C: 508ba42b646SStefan Weil case i82558A: 509663e8e51Sths case i82558B: 510ba42b646SStefan Weil case i82559A: 511ba42b646SStefan Weil case i82559B: 512558c8634SStefan Weil case i82559ER: 513558c8634SStefan Weil case i82562: 514db667a12SStefan Weil case i82801: 515663e8e51Sths case i82559C: 516ba42b646SStefan Weil break; 517663e8e51Sths default: 518663e8e51Sths logout("Device %X is undefined!\n", device); 519663e8e51Sths } 520663e8e51Sths 5213dec59a1SStefan Weil /* Standard TxCB. */ 5223dec59a1SStefan Weil s->configuration[6] |= BIT(4); 5233dec59a1SStefan Weil 524558c8634SStefan Weil /* Standard statistical counters. */ 525ba42b646SStefan Weil s->configuration[6] |= BIT(5); 526ba42b646SStefan Weil 527ba42b646SStefan Weil if (s->stats_size == 80) { 528ba42b646SStefan Weil /* TODO: check TCO Statistical Counters bit. Documentation not clear. */ 529ba42b646SStefan Weil if (s->configuration[6] & BIT(2)) { 530ba42b646SStefan Weil /* TCO statistical counters. */ 531ba42b646SStefan Weil assert(s->configuration[6] & BIT(5)); 532ba42b646SStefan Weil } else { 533ba42b646SStefan Weil if (s->configuration[6] & BIT(5)) { 534ba42b646SStefan Weil /* No extended statistical counters, i82557 compatible. */ 535ba42b646SStefan Weil s->stats_size = 64; 536ba42b646SStefan Weil } else { 537ba42b646SStefan Weil /* i82558 compatible. */ 538ba42b646SStefan Weil s->stats_size = 76; 539ba42b646SStefan Weil } 540ba42b646SStefan Weil } 541ba42b646SStefan Weil } else { 542ba42b646SStefan Weil if (s->configuration[6] & BIT(5)) { 543ba42b646SStefan Weil /* No extended statistical counters. */ 544ba42b646SStefan Weil s->stats_size = 64; 545ba42b646SStefan Weil } 546ba42b646SStefan Weil } 547ba42b646SStefan Weil assert(s->stats_size > 0 && s->stats_size <= sizeof(s->statistics)); 548ba42b646SStefan Weil 54940021f08SAnthony Liguori if (info->power_management) { 550ba42b646SStefan Weil /* Power Management Capabilities */ 5518bbd1ce2SMichael S. Tsirkin int cfg_offset = 0xdc; 552ca77089dSIsaku Yamahata int r = pci_add_capability(&s->dev, PCI_CAP_ID_PM, 5539a7c2a59SMao Zhongyi cfg_offset, PCI_PM_SIZEOF, 5549a7c2a59SMao Zhongyi errp); 5559a7c2a59SMao Zhongyi if (r < 0) { 5569a7c2a59SMao Zhongyi return; 5579a7c2a59SMao Zhongyi } 5589a7c2a59SMao Zhongyi 559ae543b49SStefan Weil pci_set_word(pci_conf + cfg_offset + PCI_PM_PMC, 0x7e21); 560ae543b49SStefan Weil #if 0 /* TODO: replace dummy code for power management emulation. */ 561ba42b646SStefan Weil /* TODO: Power Management Control / Status. */ 562ae543b49SStefan Weil pci_set_word(pci_conf + cfg_offset + PCI_PM_CTRL, 0x0000); 563ba42b646SStefan Weil /* TODO: Ethernet Power Consumption Registers (i82559 and later). */ 564ae543b49SStefan Weil pci_set_byte(pci_conf + cfg_offset + PCI_PM_PPB_EXTENSIONS, 0x0000); 565ae543b49SStefan Weil #endif 566ae543b49SStefan Weil } 567ba42b646SStefan Weil 568ba42b646SStefan Weil #if EEPROM_SIZE > 0 569663e8e51Sths if (device == i82557C || device == i82558B || device == i82559C) { 570e7493b25SStefan Weil /* 571e7493b25SStefan Weil TODO: get vendor id from EEPROM for i82557C or later. 572e7493b25SStefan Weil TODO: get device id from EEPROM for i82557C or later. 573e7493b25SStefan Weil TODO: status bit 4 can be disabled by EEPROM for i82558, i82559. 574e7493b25SStefan Weil TODO: header type is determined by EEPROM for i82559. 575e7493b25SStefan Weil TODO: get subsystem id from EEPROM for i82557C or later. 576e7493b25SStefan Weil TODO: get subsystem vendor id from EEPROM for i82557C or later. 577e7493b25SStefan Weil TODO: exp. rom baddr depends on a bit in EEPROM for i82558 or later. 578e7493b25SStefan Weil TODO: capability pointer depends on EEPROM for i82558. 579e7493b25SStefan Weil */ 580663e8e51Sths logout("Get device id and revision from EEPROM!!!\n"); 581663e8e51Sths } 582ba42b646SStefan Weil #endif /* EEPROM_SIZE > 0 */ 583663e8e51Sths } 584663e8e51Sths 585663e8e51Sths static void nic_selective_reset(EEPRO100State * s) 586663e8e51Sths { 587663e8e51Sths size_t i; 588663e8e51Sths uint16_t *eeprom_contents = eeprom93xx_data(s->eeprom); 589e7493b25SStefan Weil #if 0 590e7493b25SStefan Weil eeprom93xx_reset(s->eeprom); 591e7493b25SStefan Weil #endif 592508ef936SGerd Hoffmann memcpy(eeprom_contents, s->conf.macaddr.a, 6); 593b1e87018SStefan Weil eeprom_contents[EEPROM_ID] = EEPROM_ID_VALID; 594f4e94dfeS=?UTF-8?q?Reimar=20D=C3=B6ffinger?= if (s->device == i82557B || s->device == i82557C) 595f4e94dfeS=?UTF-8?q?Reimar=20D=C3=B6ffinger?= eeprom_contents[5] = 0x0100; 5966cded3a4SStefan Weil eeprom_contents[EEPROM_PHY_ID] = 1; 597663e8e51Sths uint16_t sum = 0; 598663e8e51Sths for (i = 0; i < EEPROM_SIZE - 1; i++) { 599663e8e51Sths sum += eeprom_contents[i]; 600663e8e51Sths } 601663e8e51Sths eeprom_contents[EEPROM_SIZE - 1] = 0xbaba - sum; 602aac443e6SStefan Weil TRACE(EEPROM, logout("checksum=0x%04x\n", eeprom_contents[EEPROM_SIZE - 1])); 603663e8e51Sths 604663e8e51Sths memset(s->mem, 0, sizeof(s->mem)); 605e5e23ab8SStefan Weil e100_write_reg4(s, SCBCtrlMDI, BIT(21)); 606663e8e51Sths 607663e8e51Sths assert(sizeof(s->mdimem) == sizeof(eepro100_mdi_default)); 608663e8e51Sths memcpy(&s->mdimem[0], &eepro100_mdi_default[0], sizeof(s->mdimem)); 609663e8e51Sths } 610663e8e51Sths 611663e8e51Sths static void nic_reset(void *opaque) 612663e8e51Sths { 613769cf7a5SJuan Quintela EEPRO100State *s = opaque; 614aac443e6SStefan Weil TRACE(OTHER, logout("%p\n", s)); 615010ec629SStefan Weil /* TODO: Clearing of hash register for selective reset, too? */ 6167b8737deSStefan Weil memset(&s->mult[0], 0, sizeof(s->mult)); 617663e8e51Sths nic_selective_reset(s); 618663e8e51Sths } 619663e8e51Sths 620663e8e51Sths #if defined(DEBUG_EEPRO100) 621b8f6ba0dSStefan Weil static const char * const e100_reg[PCI_IO_SIZE / 4] = { 622663e8e51Sths "Command/Status", 623663e8e51Sths "General Pointer", 624663e8e51Sths "Port", 625663e8e51Sths "EEPROM/Flash Control", 626663e8e51Sths "MDI Control", 627663e8e51Sths "Receive DMA Byte Count", 628b8f6ba0dSStefan Weil "Flow Control", 629663e8e51Sths "General Status/Control" 630663e8e51Sths }; 631663e8e51Sths 632663e8e51Sths static char *regname(uint32_t addr) 633663e8e51Sths { 634ec169288SDavid Benjamin static char buf[32]; 635663e8e51Sths if (addr < PCI_IO_SIZE) { 636b8f6ba0dSStefan Weil const char *r = e100_reg[addr / 4]; 637663e8e51Sths if (r != 0) { 63841cbc23cSStefan Weil snprintf(buf, sizeof(buf), "%s+%u", r, addr % 4); 639663e8e51Sths } else { 64041cbc23cSStefan Weil snprintf(buf, sizeof(buf), "0x%02x", addr); 641663e8e51Sths } 642663e8e51Sths } else { 64341cbc23cSStefan Weil snprintf(buf, sizeof(buf), "??? 0x%08x", addr); 644663e8e51Sths } 645663e8e51Sths return buf; 646663e8e51Sths } 647663e8e51Sths #endif /* DEBUG_EEPRO100 */ 648663e8e51Sths 649663e8e51Sths /***************************************************************************** 650663e8e51Sths * 651663e8e51Sths * Command emulation. 652663e8e51Sths * 653663e8e51Sths ****************************************************************************/ 654663e8e51Sths 655663e8e51Sths #if 0 656663e8e51Sths static uint16_t eepro100_read_command(EEPRO100State * s) 657663e8e51Sths { 658663e8e51Sths uint16_t val = 0xffff; 659e7493b25SStefan Weil TRACE(OTHER, logout("val=0x%04x\n", val)); 660663e8e51Sths return val; 661663e8e51Sths } 662663e8e51Sths #endif 663663e8e51Sths 664663e8e51Sths /* Commands that can be put in a command list entry. */ 665663e8e51Sths enum commands { 666663e8e51Sths CmdNOp = 0, 667663e8e51Sths CmdIASetup = 1, 668663e8e51Sths CmdConfigure = 2, 669663e8e51Sths CmdMulticastList = 3, 670663e8e51Sths CmdTx = 4, 671663e8e51Sths CmdTDR = 5, /* load microcode */ 672663e8e51Sths CmdDump = 6, 673663e8e51Sths CmdDiagnose = 7, 674663e8e51Sths 675663e8e51Sths /* And some extra flags: */ 676663e8e51Sths CmdSuspend = 0x4000, /* Suspend after completion. */ 677663e8e51Sths CmdIntr = 0x2000, /* Interrupt after completion. */ 678663e8e51Sths CmdTxFlex = 0x0008, /* Use "Flexible mode" for CmdTx command. */ 679663e8e51Sths }; 680663e8e51Sths 681c227f099SAnthony Liguori static cu_state_t get_cu_state(EEPRO100State * s) 682663e8e51Sths { 683ced5296aSStefan Weil return ((s->mem[SCBStatus] & BITS(7, 6)) >> 6); 684663e8e51Sths } 685663e8e51Sths 686c227f099SAnthony Liguori static void set_cu_state(EEPRO100State * s, cu_state_t state) 687663e8e51Sths { 688ced5296aSStefan Weil s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(7, 6)) + (state << 6); 689663e8e51Sths } 690663e8e51Sths 691c227f099SAnthony Liguori static ru_state_t get_ru_state(EEPRO100State * s) 692663e8e51Sths { 693ced5296aSStefan Weil return ((s->mem[SCBStatus] & BITS(5, 2)) >> 2); 694663e8e51Sths } 695663e8e51Sths 696c227f099SAnthony Liguori static void set_ru_state(EEPRO100State * s, ru_state_t state) 697663e8e51Sths { 698ced5296aSStefan Weil s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(5, 2)) + (state << 2); 699663e8e51Sths } 700663e8e51Sths 701663e8e51Sths static void dump_statistics(EEPRO100State * s) 702663e8e51Sths { 703*a423a1b5SPhilippe Mathieu-Daudé const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; 704*a423a1b5SPhilippe 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, 712*a423a1b5SPhilippe Mathieu-Daudé s->statistics.tx_good_frames, attrs); 71316ef60c9SEduard - Gabriel Munteanu stl_le_pci_dma(&s->dev, s->statsaddr + 36, 714*a423a1b5SPhilippe Mathieu-Daudé s->statistics.rx_good_frames, attrs); 71516ef60c9SEduard - Gabriel Munteanu stl_le_pci_dma(&s->dev, s->statsaddr + 48, 716*a423a1b5SPhilippe Mathieu-Daudé s->statistics.rx_resource_errors, attrs); 71716ef60c9SEduard - Gabriel Munteanu stl_le_pci_dma(&s->dev, s->statsaddr + 60, 718*a423a1b5SPhilippe Mathieu-Daudé s->statistics.rx_short_frame_errors, attrs); 719e7493b25SStefan Weil #if 0 720*a423a1b5SPhilippe Mathieu-Daudé stw_le_pci_dma(&s->dev, s->statsaddr + 76, 721*a423a1b5SPhilippe Mathieu-Daudé s->statistics.xmt_tco_frames, attrs); 722*a423a1b5SPhilippe Mathieu-Daudé stw_le_pci_dma(&s->dev, s->statsaddr + 78, 723*a423a1b5SPhilippe Mathieu-Daudé s->statistics.rcv_tco_frames, attrs); 724e7493b25SStefan Weil missing("CU dump statistical counters"); 725e7493b25SStefan Weil #endif 726663e8e51Sths } 727663e8e51Sths 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 738f3a52e50SStefan Weil static void tx_command(EEPRO100State *s) 739663e8e51Sths { 7408f8e8053SThomas Huth uint32_t tbd_array = s->tx.tbd_array_addr; 7418f8e8053SThomas Huth uint16_t tcb_bytes = s->tx.tcb_bytes & 0x3fff; 742f3a52e50SStefan Weil /* Sends larger than MAX_ETH_FRAME_SIZE are allowed, up to 2600 bytes. */ 743f3a52e50SStefan Weil uint8_t buf[2600]; 744f3a52e50SStefan Weil uint16_t size = 0; 745f3a52e50SStefan Weil uint32_t tbd_address = s->cb_address + 0x10; 746aac443e6SStefan Weil TRACE(RXTX, logout 747663e8e51Sths ("transmit, TBD array address 0x%08x, TCB byte count 0x%04x, TBD count %u\n", 748f3a52e50SStefan Weil tbd_array, tcb_bytes, s->tx.tbd_count)); 7497f1e9d4eSKevin Wolf 7507f1e9d4eSKevin Wolf if (tcb_bytes > 2600) { 7517f1e9d4eSKevin Wolf logout("TCB byte count too large, using 2600\n"); 7527f1e9d4eSKevin Wolf tcb_bytes = 2600; 7537f1e9d4eSKevin Wolf } 754663e8e51Sths if (!((tcb_bytes > 0) || (tbd_array != 0xffffffff))) { 755663e8e51Sths logout 756663e8e51Sths ("illegal values of TBD array address and TCB byte count!\n"); 757663e8e51Sths } 758663e8e51Sths assert(tcb_bytes <= sizeof(buf)); 759663e8e51Sths while (size < tcb_bytes) { 760aac443e6SStefan Weil TRACE(RXTX, logout 761663e8e51Sths ("TBD (simplified mode): buffer address 0x%08x, size 0x%04x\n", 7621865e288SMike Nawrocki tbd_address, tcb_bytes)); 7631865e288SMike Nawrocki pci_dma_read(&s->dev, tbd_address, &buf[size], tcb_bytes); 7641865e288SMike Nawrocki size += tcb_bytes; 765663e8e51Sths } 766663e8e51Sths if (tbd_array == 0xffffffff) { 767663e8e51Sths /* Simplified mode. Was already handled by code above. */ 768663e8e51Sths } else { 769663e8e51Sths /* Flexible mode. */ 770663e8e51Sths uint8_t tbd_count = 0; 771ba42b646SStefan Weil if (s->has_extended_tcb_support && !(s->configuration[6] & BIT(4))) { 7723f9cb1c1SNaphtali Sprei /* Extended Flexible TCB. */ 773663e8e51Sths for (; tbd_count < 2; tbd_count++) { 77416ef60c9SEduard - Gabriel Munteanu uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev, 77516ef60c9SEduard - Gabriel Munteanu tbd_address); 77616ef60c9SEduard - Gabriel Munteanu uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev, 77716ef60c9SEduard - Gabriel Munteanu tbd_address + 4); 77816ef60c9SEduard - Gabriel Munteanu uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev, 77916ef60c9SEduard - Gabriel Munteanu tbd_address + 6); 780663e8e51Sths tbd_address += 8; 781aac443e6SStefan Weil TRACE(RXTX, logout 7823f9cb1c1SNaphtali Sprei ("TBD (extended flexible mode): buffer address 0x%08x, size 0x%04x\n", 783aac443e6SStefan Weil tx_buffer_address, tx_buffer_size)); 78424e6f355SReimar Döffinger tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size); 78516ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, tx_buffer_address, 78616ef60c9SEduard - Gabriel Munteanu &buf[size], tx_buffer_size); 787663e8e51Sths size += tx_buffer_size; 788663e8e51Sths if (tx_buffer_el & 1) { 789663e8e51Sths break; 790663e8e51Sths } 791663e8e51Sths } 792663e8e51Sths } 793663e8e51Sths tbd_address = tbd_array; 794f3a52e50SStefan Weil for (; tbd_count < s->tx.tbd_count; tbd_count++) { 79516ef60c9SEduard - Gabriel Munteanu uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev, tbd_address); 79616ef60c9SEduard - Gabriel Munteanu uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev, tbd_address + 4); 79716ef60c9SEduard - Gabriel Munteanu uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev, tbd_address + 6); 798663e8e51Sths tbd_address += 8; 799aac443e6SStefan Weil TRACE(RXTX, logout 800663e8e51Sths ("TBD (flexible mode): buffer address 0x%08x, size 0x%04x\n", 801aac443e6SStefan Weil tx_buffer_address, tx_buffer_size)); 80224e6f355SReimar Döffinger tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size); 80316ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, tx_buffer_address, 80416ef60c9SEduard - Gabriel Munteanu &buf[size], tx_buffer_size); 805663e8e51Sths size += tx_buffer_size; 806663e8e51Sths if (tx_buffer_el & 1) { 807663e8e51Sths break; 808663e8e51Sths } 809663e8e51Sths } 810663e8e51Sths } 811aac443e6SStefan Weil TRACE(RXTX, logout("%p sending frame, len=%d,%s\n", s, size, nic_dump(buf, size))); 812b356f76dSJason Wang qemu_send_packet(qemu_get_queue(s->nic), buf, size); 813663e8e51Sths s->statistics.tx_good_frames++; 814663e8e51Sths /* Transmit with bad status would raise an CX/TNO interrupt. 815663e8e51Sths * (82557 only). Emulation never has bad status. */ 816e7493b25SStefan Weil #if 0 817e7493b25SStefan Weil eepro100_cx_interrupt(s); 818e7493b25SStefan Weil #endif 819f3a52e50SStefan Weil } 820f3a52e50SStefan Weil 8217b8737deSStefan Weil static void set_multicast_list(EEPRO100State *s) 8227b8737deSStefan Weil { 8237b8737deSStefan Weil uint16_t multicast_count = s->tx.tbd_array_addr & BITS(13, 0); 8247b8737deSStefan Weil uint16_t i; 8257b8737deSStefan Weil memset(&s->mult[0], 0, sizeof(s->mult)); 8267b8737deSStefan Weil TRACE(OTHER, logout("multicast list, multicast count = %u\n", multicast_count)); 8277b8737deSStefan Weil for (i = 0; i < multicast_count; i += 6) { 8287b8737deSStefan Weil uint8_t multicast_addr[6]; 82916ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, s->cb_address + 10 + i, multicast_addr, 6); 8307b8737deSStefan Weil TRACE(OTHER, logout("multicast entry %s\n", nic_dump(multicast_addr, 6))); 8317c0348bdSMark Cave-Ayland unsigned mcast_idx = (net_crc32(multicast_addr, ETH_ALEN) & 8327c0348bdSMark Cave-Ayland BITS(7, 2)) >> 2; 8337b8737deSStefan Weil assert(mcast_idx < 64); 8347b8737deSStefan Weil s->mult[mcast_idx >> 3] |= (1 << (mcast_idx & 7)); 8357b8737deSStefan Weil } 8367b8737deSStefan Weil } 8377b8737deSStefan Weil 838f3a52e50SStefan Weil static void action_command(EEPRO100State *s) 839f3a52e50SStefan Weil { 840*a423a1b5SPhilippe Mathieu-Daudé const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; 84100837731SStefan Weil /* The loop below won't stop if it gets special handcrafted data. 84200837731SStefan Weil Therefore we limit the number of iterations. */ 84300837731SStefan Weil unsigned max_loop_count = 16; 84400837731SStefan Weil 845f3a52e50SStefan Weil for (;;) { 8463d0f4b9bSStefan Weil bool bit_el; 8473d0f4b9bSStefan Weil bool bit_s; 8483d0f4b9bSStefan Weil bool bit_i; 8493d0f4b9bSStefan Weil bool bit_nc; 85075f5a6ccSStefan Weil uint16_t ok_status = STATUS_OK; 8513d0f4b9bSStefan Weil s->cb_address = s->cu_base + s->cu_offset; 8523d0f4b9bSStefan Weil read_cb(s); 8533d0f4b9bSStefan Weil bit_el = ((s->tx.command & COMMAND_EL) != 0); 8543d0f4b9bSStefan Weil bit_s = ((s->tx.command & COMMAND_S) != 0); 8553d0f4b9bSStefan Weil bit_i = ((s->tx.command & COMMAND_I) != 0); 8563d0f4b9bSStefan Weil bit_nc = ((s->tx.command & COMMAND_NC) != 0); 8573d0f4b9bSStefan Weil #if 0 8583d0f4b9bSStefan Weil bool bit_sf = ((s->tx.command & COMMAND_SF) != 0); 8593d0f4b9bSStefan Weil #endif 86000837731SStefan Weil 86100837731SStefan Weil if (max_loop_count-- == 0) { 86200837731SStefan Weil /* Prevent an endless loop. */ 86300837731SStefan Weil logout("loop in %s:%u\n", __FILE__, __LINE__); 86400837731SStefan Weil break; 86500837731SStefan Weil } 86600837731SStefan Weil 8673d0f4b9bSStefan Weil s->cu_offset = s->tx.link; 8683d0f4b9bSStefan Weil TRACE(OTHER, 8693d0f4b9bSStefan Weil logout("val=(cu start), status=0x%04x, command=0x%04x, link=0x%08x\n", 8703d0f4b9bSStefan Weil s->tx.status, s->tx.command, s->tx.link)); 8713d0f4b9bSStefan Weil switch (s->tx.command & COMMAND_CMD) { 872f3a52e50SStefan Weil case CmdNOp: 873f3a52e50SStefan Weil /* Do nothing. */ 874f3a52e50SStefan Weil break; 875f3a52e50SStefan Weil case CmdIASetup: 87616ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, s->cb_address + 8, &s->conf.macaddr.a[0], 6); 877ce0e58b3SStefan Weil TRACE(OTHER, logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6))); 878f3a52e50SStefan Weil break; 879f3a52e50SStefan Weil case CmdConfigure: 88016ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, s->cb_address + 8, 88116ef60c9SEduard - Gabriel Munteanu &s->configuration[0], sizeof(s->configuration)); 882010ec629SStefan Weil TRACE(OTHER, logout("configuration: %s\n", 883010ec629SStefan Weil nic_dump(&s->configuration[0], 16))); 884010ec629SStefan Weil TRACE(OTHER, logout("configuration: %s\n", 885010ec629SStefan Weil nic_dump(&s->configuration[16], 886010ec629SStefan Weil ARRAY_SIZE(s->configuration) - 16))); 887010ec629SStefan Weil if (s->configuration[20] & BIT(6)) { 888010ec629SStefan Weil TRACE(OTHER, logout("Multiple IA bit\n")); 889010ec629SStefan Weil } 890f3a52e50SStefan Weil break; 891f3a52e50SStefan Weil case CmdMulticastList: 8927b8737deSStefan Weil set_multicast_list(s); 893f3a52e50SStefan Weil break; 894f3a52e50SStefan Weil case CmdTx: 895f3a52e50SStefan Weil if (bit_nc) { 896f3a52e50SStefan Weil missing("CmdTx: NC = 0"); 89775f5a6ccSStefan Weil ok_status = 0; 898f3a52e50SStefan Weil break; 899f3a52e50SStefan Weil } 900f3a52e50SStefan Weil tx_command(s); 901663e8e51Sths break; 902663e8e51Sths case CmdTDR: 903aac443e6SStefan Weil TRACE(OTHER, logout("load microcode\n")); 904663e8e51Sths /* Starting with offset 8, the command contains 905663e8e51Sths * 64 dwords microcode which we just ignore here. */ 906663e8e51Sths break; 907f80a7fc3SStefan Weil case CmdDiagnose: 908f80a7fc3SStefan Weil TRACE(OTHER, logout("diagnose\n")); 909f80a7fc3SStefan Weil /* Make sure error flag is not set. */ 910f80a7fc3SStefan Weil s->tx.status = 0; 911f80a7fc3SStefan Weil break; 912663e8e51Sths default: 913663e8e51Sths missing("undefined command"); 91475f5a6ccSStefan Weil ok_status = 0; 9157f1e9d4eSKevin Wolf break; 916663e8e51Sths } 9177f1e9d4eSKevin Wolf /* Write new status. */ 91816ef60c9SEduard - Gabriel Munteanu stw_le_pci_dma(&s->dev, s->cb_address, 919*a423a1b5SPhilippe Mathieu-Daudé s->tx.status | ok_status | STATUS_C, attrs); 920663e8e51Sths if (bit_i) { 921663e8e51Sths /* CU completed action. */ 922663e8e51Sths eepro100_cx_interrupt(s); 923663e8e51Sths } 924663e8e51Sths if (bit_el) { 925aac443e6SStefan Weil /* CU becomes idle. Terminate command loop. */ 926663e8e51Sths set_cu_state(s, cu_idle); 927663e8e51Sths eepro100_cna_interrupt(s); 9285fa9a0aeSStefan Weil break; 929663e8e51Sths } else if (bit_s) { 9305fa9a0aeSStefan Weil /* CU becomes suspended. Terminate command loop. */ 931663e8e51Sths set_cu_state(s, cu_suspended); 932663e8e51Sths eepro100_cna_interrupt(s); 9335fa9a0aeSStefan Weil break; 934663e8e51Sths } else { 935663e8e51Sths /* More entries in list. */ 936aac443e6SStefan Weil TRACE(OTHER, logout("CU list with at least one more entry\n")); 9375fa9a0aeSStefan Weil } 938663e8e51Sths } 939aac443e6SStefan Weil TRACE(OTHER, logout("CU list empty\n")); 940663e8e51Sths /* List is empty. Now CU is idle or suspended. */ 9415fa9a0aeSStefan Weil } 9425fa9a0aeSStefan Weil 9435fa9a0aeSStefan Weil static void eepro100_cu_command(EEPRO100State * s, uint8_t val) 9445fa9a0aeSStefan Weil { 945*a423a1b5SPhilippe Mathieu-Daudé const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; 946cb25a3fbSStefan Weil cu_state_t cu_state; 9475fa9a0aeSStefan Weil switch (val) { 9485fa9a0aeSStefan Weil case CU_NOP: 9495fa9a0aeSStefan Weil /* No operation. */ 9505fa9a0aeSStefan Weil break; 9515fa9a0aeSStefan Weil case CU_START: 952cb25a3fbSStefan Weil cu_state = get_cu_state(s); 953cb25a3fbSStefan Weil if (cu_state != cu_idle && cu_state != cu_suspended) { 954cb25a3fbSStefan Weil /* Intel documentation says that CU must be idle or suspended 955cb25a3fbSStefan Weil * for the CU start command. */ 956cb25a3fbSStefan Weil logout("unexpected CU state is %u\n", cu_state); 9575fa9a0aeSStefan Weil } 9585fa9a0aeSStefan Weil set_cu_state(s, cu_active); 95927a05006SStefan Weil s->cu_offset = e100_read_reg4(s, SCBPointer); 9605fa9a0aeSStefan Weil action_command(s); 961663e8e51Sths break; 962663e8e51Sths case CU_RESUME: 963663e8e51Sths if (get_cu_state(s) != cu_suspended) { 964663e8e51Sths logout("bad CU resume from CU state %u\n", get_cu_state(s)); 965663e8e51Sths /* Workaround for bad Linux eepro100 driver which resumes 966663e8e51Sths * from idle state. */ 967e7493b25SStefan Weil #if 0 968e7493b25SStefan Weil missing("cu resume"); 969e7493b25SStefan Weil #endif 970663e8e51Sths set_cu_state(s, cu_suspended); 971663e8e51Sths } 972663e8e51Sths if (get_cu_state(s) == cu_suspended) { 973aac443e6SStefan Weil TRACE(OTHER, logout("CU resuming\n")); 974663e8e51Sths set_cu_state(s, cu_active); 9755fa9a0aeSStefan Weil action_command(s); 976663e8e51Sths } 977663e8e51Sths break; 978663e8e51Sths case CU_STATSADDR: 979663e8e51Sths /* Load dump counters address. */ 98027a05006SStefan Weil s->statsaddr = e100_read_reg4(s, SCBPointer); 981c16ada98SStefan Weil TRACE(OTHER, logout("val=0x%02x (dump counters address)\n", val)); 982c16ada98SStefan Weil if (s->statsaddr & 3) { 983c16ada98SStefan Weil /* Memory must be Dword aligned. */ 984c16ada98SStefan Weil logout("unaligned dump counters address\n"); 985c16ada98SStefan Weil /* Handling of misaligned addresses is undefined. 986c16ada98SStefan Weil * Here we align the address by ignoring the lower bits. */ 987c16ada98SStefan Weil /* TODO: Test unaligned dump counter address on real hardware. */ 988c16ada98SStefan Weil s->statsaddr &= ~3; 989c16ada98SStefan Weil } 990663e8e51Sths break; 991663e8e51Sths case CU_SHOWSTATS: 992663e8e51Sths /* Dump statistical counters. */ 993aac443e6SStefan Weil TRACE(OTHER, logout("val=0x%02x (dump stats)\n", val)); 994663e8e51Sths dump_statistics(s); 995*a423a1b5SPhilippe Mathieu-Daudé stl_le_pci_dma(&s->dev, s->statsaddr + s->stats_size, 0xa005, attrs); 996663e8e51Sths break; 997663e8e51Sths case CU_CMD_BASE: 998663e8e51Sths /* Load CU base. */ 999aac443e6SStefan Weil TRACE(OTHER, logout("val=0x%02x (CU base address)\n", val)); 100027a05006SStefan Weil s->cu_base = e100_read_reg4(s, SCBPointer); 1001663e8e51Sths break; 1002663e8e51Sths case CU_DUMPSTATS: 1003663e8e51Sths /* Dump and reset statistical counters. */ 1004aac443e6SStefan Weil TRACE(OTHER, logout("val=0x%02x (dump stats and reset)\n", val)); 1005663e8e51Sths dump_statistics(s); 1006*a423a1b5SPhilippe Mathieu-Daudé stl_le_pci_dma(&s->dev, s->statsaddr + s->stats_size, 0xa007, attrs); 1007663e8e51Sths memset(&s->statistics, 0, sizeof(s->statistics)); 1008663e8e51Sths break; 1009663e8e51Sths case CU_SRESUME: 1010663e8e51Sths /* CU static resume. */ 1011663e8e51Sths missing("CU static resume"); 1012663e8e51Sths break; 1013663e8e51Sths default: 1014663e8e51Sths missing("Undefined CU command"); 1015663e8e51Sths } 1016663e8e51Sths } 1017663e8e51Sths 1018663e8e51Sths static void eepro100_ru_command(EEPRO100State * s, uint8_t val) 1019663e8e51Sths { 1020663e8e51Sths switch (val) { 1021663e8e51Sths case RU_NOP: 1022663e8e51Sths /* No operation. */ 1023663e8e51Sths break; 1024663e8e51Sths case RX_START: 1025663e8e51Sths /* RU start. */ 1026663e8e51Sths if (get_ru_state(s) != ru_idle) { 1027663e8e51Sths logout("RU state is %u, should be %u\n", get_ru_state(s), ru_idle); 1028e7493b25SStefan Weil #if 0 1029e7493b25SStefan Weil assert(!"wrong RU state"); 1030e7493b25SStefan Weil #endif 1031663e8e51Sths } 1032663e8e51Sths set_ru_state(s, ru_ready); 103327a05006SStefan Weil s->ru_offset = e100_read_reg4(s, SCBPointer); 1034b356f76dSJason Wang qemu_flush_queued_packets(qemu_get_queue(s->nic)); 1035aac443e6SStefan Weil TRACE(OTHER, logout("val=0x%02x (rx start)\n", val)); 1036663e8e51Sths break; 1037663e8e51Sths case RX_RESUME: 1038663e8e51Sths /* Restart RU. */ 1039663e8e51Sths if (get_ru_state(s) != ru_suspended) { 1040663e8e51Sths logout("RU state is %u, should be %u\n", get_ru_state(s), 1041663e8e51Sths ru_suspended); 1042e7493b25SStefan Weil #if 0 1043e7493b25SStefan Weil assert(!"wrong RU state"); 1044e7493b25SStefan Weil #endif 1045663e8e51Sths } 1046663e8e51Sths set_ru_state(s, ru_ready); 1047663e8e51Sths break; 1048e824012bSStefan Weil case RU_ABORT: 1049e824012bSStefan Weil /* RU abort. */ 1050e824012bSStefan Weil if (get_ru_state(s) == ru_ready) { 1051e824012bSStefan Weil eepro100_rnr_interrupt(s); 1052e824012bSStefan Weil } 1053e824012bSStefan Weil set_ru_state(s, ru_idle); 1054e824012bSStefan Weil break; 1055663e8e51Sths case RX_ADDR_LOAD: 1056663e8e51Sths /* Load RU base. */ 1057aac443e6SStefan Weil TRACE(OTHER, logout("val=0x%02x (RU base address)\n", val)); 105827a05006SStefan Weil s->ru_base = e100_read_reg4(s, SCBPointer); 1059663e8e51Sths break; 1060663e8e51Sths default: 1061663e8e51Sths logout("val=0x%02x (undefined RU command)\n", val); 1062663e8e51Sths missing("Undefined SU command"); 1063663e8e51Sths } 1064663e8e51Sths } 1065663e8e51Sths 1066663e8e51Sths static void eepro100_write_command(EEPRO100State * s, uint8_t val) 1067663e8e51Sths { 1068663e8e51Sths eepro100_ru_command(s, val & 0x0f); 1069663e8e51Sths eepro100_cu_command(s, val & 0xf0); 1070663e8e51Sths if ((val) == 0) { 1071aac443e6SStefan Weil TRACE(OTHER, logout("val=0x%02x\n", val)); 1072663e8e51Sths } 1073663e8e51Sths /* Clear command byte after command was accepted. */ 1074663e8e51Sths s->mem[SCBCmd] = 0; 1075663e8e51Sths } 1076663e8e51Sths 1077663e8e51Sths /***************************************************************************** 1078663e8e51Sths * 1079663e8e51Sths * EEPROM emulation. 1080663e8e51Sths * 1081663e8e51Sths ****************************************************************************/ 1082663e8e51Sths 1083663e8e51Sths #define EEPROM_CS 0x02 1084663e8e51Sths #define EEPROM_SK 0x01 1085663e8e51Sths #define EEPROM_DI 0x04 1086663e8e51Sths #define EEPROM_DO 0x08 1087663e8e51Sths 1088663e8e51Sths static uint16_t eepro100_read_eeprom(EEPRO100State * s) 1089663e8e51Sths { 1090e5e23ab8SStefan Weil uint16_t val = e100_read_reg2(s, SCBeeprom); 1091663e8e51Sths if (eeprom93xx_read(s->eeprom)) { 1092663e8e51Sths val |= EEPROM_DO; 1093663e8e51Sths } else { 1094663e8e51Sths val &= ~EEPROM_DO; 1095663e8e51Sths } 1096aac443e6SStefan Weil TRACE(EEPROM, logout("val=0x%04x\n", val)); 1097663e8e51Sths return val; 1098663e8e51Sths } 1099663e8e51Sths 1100c227f099SAnthony Liguori static void eepro100_write_eeprom(eeprom_t * eeprom, uint8_t val) 1101663e8e51Sths { 1102aac443e6SStefan Weil TRACE(EEPROM, logout("val=0x%02x\n", val)); 1103663e8e51Sths 1104ebabb67aSStefan Weil /* mask unwritable bits */ 1105e7493b25SStefan Weil #if 0 1106e7493b25SStefan Weil val = SET_MASKED(val, 0x31, eeprom->value); 1107e7493b25SStefan Weil #endif 1108663e8e51Sths 1109663e8e51Sths int eecs = ((val & EEPROM_CS) != 0); 1110663e8e51Sths int eesk = ((val & EEPROM_SK) != 0); 1111663e8e51Sths int eedi = ((val & EEPROM_DI) != 0); 1112663e8e51Sths eeprom93xx_write(eeprom, eecs, eesk, eedi); 1113663e8e51Sths } 1114663e8e51Sths 1115663e8e51Sths /***************************************************************************** 1116663e8e51Sths * 1117663e8e51Sths * MDI emulation. 1118663e8e51Sths * 1119663e8e51Sths ****************************************************************************/ 1120663e8e51Sths 1121663e8e51Sths #if defined(DEBUG_EEPRO100) 11226a0b9cc9SReimar Döffinger static const char * const mdi_op_name[] = { 1123663e8e51Sths "opcode 0", 1124663e8e51Sths "write", 1125663e8e51Sths "read", 1126663e8e51Sths "opcode 3" 1127663e8e51Sths }; 1128663e8e51Sths 11296a0b9cc9SReimar Döffinger static const char * const mdi_reg_name[] = { 1130663e8e51Sths "Control", 1131663e8e51Sths "Status", 1132663e8e51Sths "PHY Identification (Word 1)", 1133663e8e51Sths "PHY Identification (Word 2)", 1134663e8e51Sths "Auto-Negotiation Advertisement", 1135663e8e51Sths "Auto-Negotiation Link Partner Ability", 1136663e8e51Sths "Auto-Negotiation Expansion" 1137663e8e51Sths }; 1138aac443e6SStefan Weil 1139aac443e6SStefan Weil static const char *reg2name(uint8_t reg) 1140aac443e6SStefan Weil { 1141aac443e6SStefan Weil static char buffer[10]; 1142aac443e6SStefan Weil const char *p = buffer; 1143aac443e6SStefan Weil if (reg < ARRAY_SIZE(mdi_reg_name)) { 1144aac443e6SStefan Weil p = mdi_reg_name[reg]; 1145aac443e6SStefan Weil } else { 1146aac443e6SStefan Weil snprintf(buffer, sizeof(buffer), "reg=0x%02x", reg); 1147aac443e6SStefan Weil } 1148aac443e6SStefan Weil return p; 1149aac443e6SStefan Weil } 1150663e8e51Sths #endif /* DEBUG_EEPRO100 */ 1151663e8e51Sths 1152663e8e51Sths static uint32_t eepro100_read_mdi(EEPRO100State * s) 1153663e8e51Sths { 1154e5e23ab8SStefan Weil uint32_t val = e100_read_reg4(s, SCBCtrlMDI); 1155663e8e51Sths 1156663e8e51Sths #ifdef DEBUG_EEPRO100 1157663e8e51Sths uint8_t raiseint = (val & BIT(29)) >> 29; 1158663e8e51Sths uint8_t opcode = (val & BITS(27, 26)) >> 26; 1159663e8e51Sths uint8_t phy = (val & BITS(25, 21)) >> 21; 1160663e8e51Sths uint8_t reg = (val & BITS(20, 16)) >> 16; 1161663e8e51Sths uint16_t data = (val & BITS(15, 0)); 1162663e8e51Sths #endif 1163663e8e51Sths /* Emulation takes no time to finish MDI transaction. */ 1164663e8e51Sths val |= BIT(28); 1165663e8e51Sths TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n", 1166663e8e51Sths val, raiseint, mdi_op_name[opcode], phy, 1167aac443e6SStefan Weil reg2name(reg), data)); 1168663e8e51Sths return val; 1169663e8e51Sths } 1170663e8e51Sths 11710113f48dSStefan Weil static void eepro100_write_mdi(EEPRO100State *s) 1172663e8e51Sths { 11730113f48dSStefan Weil uint32_t val = e100_read_reg4(s, SCBCtrlMDI); 1174663e8e51Sths uint8_t raiseint = (val & BIT(29)) >> 29; 1175663e8e51Sths uint8_t opcode = (val & BITS(27, 26)) >> 26; 1176663e8e51Sths uint8_t phy = (val & BITS(25, 21)) >> 21; 1177663e8e51Sths uint8_t reg = (val & BITS(20, 16)) >> 16; 1178663e8e51Sths uint16_t data = (val & BITS(15, 0)); 1179aac443e6SStefan Weil TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n", 1180aac443e6SStefan Weil val, raiseint, mdi_op_name[opcode], phy, reg2name(reg), data)); 1181663e8e51Sths if (phy != 1) { 1182663e8e51Sths /* Unsupported PHY address. */ 1183e7493b25SStefan Weil #if 0 1184e7493b25SStefan Weil logout("phy must be 1 but is %u\n", phy); 1185e7493b25SStefan Weil #endif 1186663e8e51Sths data = 0; 1187663e8e51Sths } else if (opcode != 1 && opcode != 2) { 1188663e8e51Sths /* Unsupported opcode. */ 1189663e8e51Sths logout("opcode must be 1 or 2 but is %u\n", opcode); 1190663e8e51Sths data = 0; 1191663e8e51Sths } else if (reg > 6) { 1192663e8e51Sths /* Unsupported register. */ 1193663e8e51Sths logout("register must be 0...6 but is %u\n", reg); 1194663e8e51Sths data = 0; 1195663e8e51Sths } else { 1196663e8e51Sths TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n", 1197663e8e51Sths val, raiseint, mdi_op_name[opcode], phy, 1198aac443e6SStefan Weil reg2name(reg), data)); 1199663e8e51Sths if (opcode == 1) { 1200663e8e51Sths /* MDI write */ 1201663e8e51Sths switch (reg) { 1202663e8e51Sths case 0: /* Control Register */ 1203663e8e51Sths if (data & 0x8000) { 1204663e8e51Sths /* Reset status and control registers to default. */ 1205663e8e51Sths s->mdimem[0] = eepro100_mdi_default[0]; 1206663e8e51Sths s->mdimem[1] = eepro100_mdi_default[1]; 1207663e8e51Sths data = s->mdimem[reg]; 1208663e8e51Sths } else { 1209663e8e51Sths /* Restart Auto Configuration = Normal Operation */ 1210663e8e51Sths data &= ~0x0200; 1211663e8e51Sths } 1212663e8e51Sths break; 1213663e8e51Sths case 1: /* Status Register */ 1214663e8e51Sths missing("not writable"); 1215663e8e51Sths break; 1216663e8e51Sths case 2: /* PHY Identification Register (Word 1) */ 1217663e8e51Sths case 3: /* PHY Identification Register (Word 2) */ 1218663e8e51Sths missing("not implemented"); 1219663e8e51Sths break; 1220663e8e51Sths case 4: /* Auto-Negotiation Advertisement Register */ 1221663e8e51Sths case 5: /* Auto-Negotiation Link Partner Ability Register */ 1222663e8e51Sths break; 1223663e8e51Sths case 6: /* Auto-Negotiation Expansion Register */ 1224663e8e51Sths default: 1225663e8e51Sths missing("not implemented"); 1226663e8e51Sths } 12275e80dd22SPeter Maydell s->mdimem[reg] &= eepro100_mdi_mask[reg]; 12285e80dd22SPeter Maydell s->mdimem[reg] |= data & ~eepro100_mdi_mask[reg]; 1229663e8e51Sths } else if (opcode == 2) { 1230663e8e51Sths /* MDI read */ 1231663e8e51Sths switch (reg) { 1232663e8e51Sths case 0: /* Control Register */ 1233663e8e51Sths if (data & 0x8000) { 1234663e8e51Sths /* Reset status and control registers to default. */ 1235663e8e51Sths s->mdimem[0] = eepro100_mdi_default[0]; 1236663e8e51Sths s->mdimem[1] = eepro100_mdi_default[1]; 1237663e8e51Sths } 1238663e8e51Sths break; 1239663e8e51Sths case 1: /* Status Register */ 1240663e8e51Sths s->mdimem[reg] |= 0x0020; 1241663e8e51Sths break; 1242663e8e51Sths case 2: /* PHY Identification Register (Word 1) */ 1243663e8e51Sths case 3: /* PHY Identification Register (Word 2) */ 1244663e8e51Sths case 4: /* Auto-Negotiation Advertisement Register */ 1245663e8e51Sths break; 1246663e8e51Sths case 5: /* Auto-Negotiation Link Partner Ability Register */ 1247663e8e51Sths s->mdimem[reg] = 0x41fe; 1248663e8e51Sths break; 1249663e8e51Sths case 6: /* Auto-Negotiation Expansion Register */ 1250663e8e51Sths s->mdimem[reg] = 0x0001; 1251663e8e51Sths break; 1252663e8e51Sths } 1253663e8e51Sths data = s->mdimem[reg]; 1254663e8e51Sths } 1255663e8e51Sths /* Emulation takes no time to finish MDI transaction. 1256663e8e51Sths * Set MDI bit in SCB status register. */ 1257663e8e51Sths s->mem[SCBAck] |= 0x08; 1258663e8e51Sths val |= BIT(28); 1259663e8e51Sths if (raiseint) { 1260663e8e51Sths eepro100_mdi_interrupt(s); 1261663e8e51Sths } 1262663e8e51Sths } 1263663e8e51Sths val = (val & 0xffff0000) + data; 1264e5e23ab8SStefan Weil e100_write_reg4(s, SCBCtrlMDI, val); 1265663e8e51Sths } 1266663e8e51Sths 1267663e8e51Sths /***************************************************************************** 1268663e8e51Sths * 1269663e8e51Sths * Port emulation. 1270663e8e51Sths * 1271663e8e51Sths ****************************************************************************/ 1272663e8e51Sths 1273663e8e51Sths #define PORT_SOFTWARE_RESET 0 1274663e8e51Sths #define PORT_SELFTEST 1 1275663e8e51Sths #define PORT_SELECTIVE_RESET 2 1276663e8e51Sths #define PORT_DUMP 3 1277663e8e51Sths #define PORT_SELECTION_MASK 3 1278663e8e51Sths 1279663e8e51Sths typedef struct { 1280663e8e51Sths uint32_t st_sign; /* Self Test Signature */ 1281663e8e51Sths uint32_t st_result; /* Self Test Results */ 1282c227f099SAnthony Liguori } eepro100_selftest_t; 1283663e8e51Sths 1284663e8e51Sths static uint32_t eepro100_read_port(EEPRO100State * s) 1285663e8e51Sths { 1286663e8e51Sths return 0; 1287663e8e51Sths } 1288663e8e51Sths 12893fd3d0b4SStefan Weil static void eepro100_write_port(EEPRO100State *s) 1290663e8e51Sths { 12913fd3d0b4SStefan Weil uint32_t val = e100_read_reg4(s, SCBPort); 1292663e8e51Sths uint32_t address = (val & ~PORT_SELECTION_MASK); 1293663e8e51Sths uint8_t selection = (val & PORT_SELECTION_MASK); 1294663e8e51Sths switch (selection) { 1295663e8e51Sths case PORT_SOFTWARE_RESET: 1296663e8e51Sths nic_reset(s); 1297663e8e51Sths break; 1298663e8e51Sths case PORT_SELFTEST: 1299aac443e6SStefan Weil TRACE(OTHER, logout("selftest address=0x%08x\n", address)); 1300c227f099SAnthony Liguori eepro100_selftest_t data; 130116ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, address, (uint8_t *) &data, sizeof(data)); 1302663e8e51Sths data.st_sign = 0xffffffff; 1303663e8e51Sths data.st_result = 0; 130416ef60c9SEduard - Gabriel Munteanu pci_dma_write(&s->dev, address, (uint8_t *) &data, sizeof(data)); 1305663e8e51Sths break; 1306663e8e51Sths case PORT_SELECTIVE_RESET: 1307aac443e6SStefan Weil TRACE(OTHER, logout("selective reset, selftest address=0x%08x\n", address)); 1308663e8e51Sths nic_selective_reset(s); 1309663e8e51Sths break; 1310663e8e51Sths default: 1311663e8e51Sths logout("val=0x%08x\n", val); 1312663e8e51Sths missing("unknown port selection"); 1313663e8e51Sths } 1314663e8e51Sths } 1315663e8e51Sths 1316663e8e51Sths /***************************************************************************** 1317663e8e51Sths * 1318663e8e51Sths * General hardware emulation. 1319663e8e51Sths * 1320663e8e51Sths ****************************************************************************/ 1321663e8e51Sths 1322663e8e51Sths static uint8_t eepro100_read1(EEPRO100State * s, uint32_t addr) 1323663e8e51Sths { 1324ef476062SBlue Swirl uint8_t val = 0; 1325663e8e51Sths if (addr <= sizeof(s->mem) - sizeof(val)) { 1326e5e23ab8SStefan Weil val = s->mem[addr]; 1327663e8e51Sths } 1328663e8e51Sths 1329663e8e51Sths switch (addr) { 1330663e8e51Sths case SCBStatus: 1331663e8e51Sths case SCBAck: 1332aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1333663e8e51Sths break; 1334663e8e51Sths case SCBCmd: 1335aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1336e7493b25SStefan Weil #if 0 1337e7493b25SStefan Weil val = eepro100_read_command(s); 1338e7493b25SStefan Weil #endif 1339663e8e51Sths break; 1340663e8e51Sths case SCBIntmask: 1341aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1342663e8e51Sths break; 1343663e8e51Sths case SCBPort + 3: 1344aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1345663e8e51Sths break; 1346663e8e51Sths case SCBeeprom: 1347663e8e51Sths val = eepro100_read_eeprom(s); 1348663e8e51Sths break; 13490113f48dSStefan Weil case SCBCtrlMDI: 13500113f48dSStefan Weil case SCBCtrlMDI + 1: 13510113f48dSStefan Weil case SCBCtrlMDI + 2: 13520113f48dSStefan Weil case SCBCtrlMDI + 3: 13530113f48dSStefan Weil val = (uint8_t)(eepro100_read_mdi(s) >> (8 * (addr & 3))); 13540113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 13550113f48dSStefan Weil break; 13560908bba1SStefan Weil case SCBpmdr: /* Power Management Driver Register */ 1357663e8e51Sths val = 0; 1358aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1359663e8e51Sths break; 1360a39bd017SStefan Weil case SCBgctrl: /* General Control Register */ 1361a39bd017SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1362a39bd017SStefan Weil break; 13630908bba1SStefan Weil case SCBgstat: /* General Status Register */ 1364663e8e51Sths /* 100 Mbps full duplex, valid link */ 1365663e8e51Sths val = 0x07; 1366aac443e6SStefan Weil TRACE(OTHER, logout("addr=General Status val=%02x\n", val)); 1367663e8e51Sths break; 1368663e8e51Sths default: 1369663e8e51Sths logout("addr=%s val=0x%02x\n", regname(addr), val); 1370663e8e51Sths missing("unknown byte read"); 1371663e8e51Sths } 1372663e8e51Sths return val; 1373663e8e51Sths } 1374663e8e51Sths 1375663e8e51Sths static uint16_t eepro100_read2(EEPRO100State * s, uint32_t addr) 1376663e8e51Sths { 1377ef476062SBlue Swirl uint16_t val = 0; 1378663e8e51Sths if (addr <= sizeof(s->mem) - sizeof(val)) { 1379e5e23ab8SStefan Weil val = e100_read_reg2(s, addr); 1380663e8e51Sths } 1381663e8e51Sths 1382663e8e51Sths switch (addr) { 1383663e8e51Sths case SCBStatus: 1384dbbaaff6S=?UTF-8?q?Reimar=20D=C3=B6ffinger?= case SCBCmd: 1385aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 1386663e8e51Sths break; 1387663e8e51Sths case SCBeeprom: 1388663e8e51Sths val = eepro100_read_eeprom(s); 1389aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 1390663e8e51Sths break; 13910113f48dSStefan Weil case SCBCtrlMDI: 13920113f48dSStefan Weil case SCBCtrlMDI + 2: 13930113f48dSStefan Weil val = (uint16_t)(eepro100_read_mdi(s) >> (8 * (addr & 3))); 13940113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 13950113f48dSStefan Weil break; 1396663e8e51Sths default: 1397663e8e51Sths logout("addr=%s val=0x%04x\n", regname(addr), val); 1398663e8e51Sths missing("unknown word read"); 1399663e8e51Sths } 1400663e8e51Sths return val; 1401663e8e51Sths } 1402663e8e51Sths 1403663e8e51Sths static uint32_t eepro100_read4(EEPRO100State * s, uint32_t addr) 1404663e8e51Sths { 1405ef476062SBlue Swirl uint32_t val = 0; 1406663e8e51Sths if (addr <= sizeof(s->mem) - sizeof(val)) { 1407e5e23ab8SStefan Weil val = e100_read_reg4(s, addr); 1408663e8e51Sths } 1409663e8e51Sths 1410663e8e51Sths switch (addr) { 1411663e8e51Sths case SCBStatus: 1412aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); 1413663e8e51Sths break; 1414663e8e51Sths case SCBPointer: 1415aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); 1416663e8e51Sths break; 1417663e8e51Sths case SCBPort: 1418663e8e51Sths val = eepro100_read_port(s); 1419aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); 1420663e8e51Sths break; 1421072476eaSStefan Weil case SCBflash: 1422072476eaSStefan Weil val = eepro100_read_eeprom(s); 1423072476eaSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); 1424072476eaSStefan Weil break; 1425663e8e51Sths case SCBCtrlMDI: 1426663e8e51Sths val = eepro100_read_mdi(s); 1427663e8e51Sths break; 1428663e8e51Sths default: 1429663e8e51Sths logout("addr=%s val=0x%08x\n", regname(addr), val); 1430663e8e51Sths missing("unknown longword read"); 1431663e8e51Sths } 1432663e8e51Sths return val; 1433663e8e51Sths } 1434663e8e51Sths 1435663e8e51Sths static void eepro100_write1(EEPRO100State * s, uint32_t addr, uint8_t val) 1436663e8e51Sths { 1437e74818f3SStefan Weil /* SCBStatus is readonly. */ 1438e74818f3SStefan Weil if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) { 1439e5e23ab8SStefan Weil s->mem[addr] = val; 1440663e8e51Sths } 1441663e8e51Sths 1442663e8e51Sths switch (addr) { 1443663e8e51Sths case SCBStatus: 14441b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1445663e8e51Sths break; 1446663e8e51Sths case SCBAck: 14471b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1448663e8e51Sths eepro100_acknowledge(s); 1449663e8e51Sths break; 1450663e8e51Sths case SCBCmd: 14511b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1452663e8e51Sths eepro100_write_command(s, val); 1453663e8e51Sths break; 1454663e8e51Sths case SCBIntmask: 14551b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1456663e8e51Sths if (val & BIT(1)) { 1457663e8e51Sths eepro100_swi_interrupt(s); 1458663e8e51Sths } 1459663e8e51Sths eepro100_interrupt(s, 0); 1460663e8e51Sths break; 146127a05006SStefan Weil case SCBPointer: 146227a05006SStefan Weil case SCBPointer + 1: 146327a05006SStefan Weil case SCBPointer + 2: 146427a05006SStefan Weil case SCBPointer + 3: 146527a05006SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 146627a05006SStefan Weil break; 14673fd3d0b4SStefan Weil case SCBPort: 14683fd3d0b4SStefan Weil case SCBPort + 1: 14693fd3d0b4SStefan Weil case SCBPort + 2: 14703fd3d0b4SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 14713fd3d0b4SStefan Weil break; 1472663e8e51Sths case SCBPort + 3: 14733fd3d0b4SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 14743fd3d0b4SStefan Weil eepro100_write_port(s); 14753fd3d0b4SStefan Weil break; 1476aac443e6SStefan Weil case SCBFlow: /* does not exist on 82557 */ 14773257d2b6Sths case SCBFlow + 1: 14783257d2b6Sths case SCBFlow + 2: 14790908bba1SStefan Weil case SCBpmdr: /* does not exist on 82557 */ 1480aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1481663e8e51Sths break; 1482663e8e51Sths case SCBeeprom: 14831b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1484663e8e51Sths eepro100_write_eeprom(s->eeprom, val); 1485663e8e51Sths break; 14860113f48dSStefan Weil case SCBCtrlMDI: 14870113f48dSStefan Weil case SCBCtrlMDI + 1: 14880113f48dSStefan Weil case SCBCtrlMDI + 2: 14890113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 14900113f48dSStefan Weil break; 14910113f48dSStefan Weil case SCBCtrlMDI + 3: 14920113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 14930113f48dSStefan Weil eepro100_write_mdi(s); 14940113f48dSStefan Weil break; 1495663e8e51Sths default: 1496663e8e51Sths logout("addr=%s val=0x%02x\n", regname(addr), val); 1497663e8e51Sths missing("unknown byte write"); 1498663e8e51Sths } 1499663e8e51Sths } 1500663e8e51Sths 1501663e8e51Sths static void eepro100_write2(EEPRO100State * s, uint32_t addr, uint16_t val) 1502663e8e51Sths { 1503e74818f3SStefan Weil /* SCBStatus is readonly. */ 1504e74818f3SStefan Weil if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) { 1505e5e23ab8SStefan Weil e100_write_reg2(s, addr, val); 1506663e8e51Sths } 1507663e8e51Sths 1508663e8e51Sths switch (addr) { 1509663e8e51Sths case SCBStatus: 15101b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 1511e74818f3SStefan Weil s->mem[SCBAck] = (val >> 8); 1512663e8e51Sths eepro100_acknowledge(s); 1513663e8e51Sths break; 1514663e8e51Sths case SCBCmd: 15151b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 1516663e8e51Sths eepro100_write_command(s, val); 1517663e8e51Sths eepro100_write1(s, SCBIntmask, val >> 8); 1518663e8e51Sths break; 151927a05006SStefan Weil case SCBPointer: 152027a05006SStefan Weil case SCBPointer + 2: 152127a05006SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 152227a05006SStefan Weil break; 15233fd3d0b4SStefan Weil case SCBPort: 15243fd3d0b4SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 15253fd3d0b4SStefan Weil break; 15263fd3d0b4SStefan Weil case SCBPort + 2: 15273fd3d0b4SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 15283fd3d0b4SStefan Weil eepro100_write_port(s); 15293fd3d0b4SStefan Weil break; 1530663e8e51Sths case SCBeeprom: 15311b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 1532663e8e51Sths eepro100_write_eeprom(s->eeprom, val); 1533663e8e51Sths break; 15340113f48dSStefan Weil case SCBCtrlMDI: 15350113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 15360113f48dSStefan Weil break; 15370113f48dSStefan Weil case SCBCtrlMDI + 2: 15380113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 15390113f48dSStefan Weil eepro100_write_mdi(s); 15400113f48dSStefan Weil break; 1541663e8e51Sths default: 1542663e8e51Sths logout("addr=%s val=0x%04x\n", regname(addr), val); 1543663e8e51Sths missing("unknown word write"); 1544663e8e51Sths } 1545663e8e51Sths } 1546663e8e51Sths 1547663e8e51Sths static void eepro100_write4(EEPRO100State * s, uint32_t addr, uint32_t val) 1548663e8e51Sths { 1549663e8e51Sths if (addr <= sizeof(s->mem) - sizeof(val)) { 1550e5e23ab8SStefan Weil e100_write_reg4(s, addr, val); 1551663e8e51Sths } 1552663e8e51Sths 1553663e8e51Sths switch (addr) { 1554663e8e51Sths case SCBPointer: 155527a05006SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); 1556663e8e51Sths break; 1557663e8e51Sths case SCBPort: 1558aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); 15593fd3d0b4SStefan Weil eepro100_write_port(s); 1560663e8e51Sths break; 1561072476eaSStefan Weil case SCBflash: 1562072476eaSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); 1563072476eaSStefan Weil val = val >> 16; 1564072476eaSStefan Weil eepro100_write_eeprom(s->eeprom, val); 1565072476eaSStefan Weil break; 1566663e8e51Sths case SCBCtrlMDI: 15670113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); 15680113f48dSStefan Weil eepro100_write_mdi(s); 1569663e8e51Sths break; 1570663e8e51Sths default: 1571663e8e51Sths logout("addr=%s val=0x%08x\n", regname(addr), val); 1572663e8e51Sths missing("unknown longword write"); 1573663e8e51Sths } 1574663e8e51Sths } 1575663e8e51Sths 1576a8170e5eSAvi Kivity static uint64_t eepro100_read(void *opaque, hwaddr addr, 15775e6ffddeSAvi Kivity unsigned size) 1578663e8e51Sths { 1579663e8e51Sths EEPRO100State *s = opaque; 15805e6ffddeSAvi Kivity 15815e6ffddeSAvi Kivity switch (size) { 15825e6ffddeSAvi Kivity case 1: return eepro100_read1(s, addr); 15835e6ffddeSAvi Kivity case 2: return eepro100_read2(s, addr); 15845e6ffddeSAvi Kivity case 4: return eepro100_read4(s, addr); 15855e6ffddeSAvi Kivity default: abort(); 15865e6ffddeSAvi Kivity } 1587663e8e51Sths } 1588663e8e51Sths 1589a8170e5eSAvi Kivity static void eepro100_write(void *opaque, hwaddr addr, 15905e6ffddeSAvi Kivity uint64_t data, unsigned size) 1591663e8e51Sths { 1592663e8e51Sths EEPRO100State *s = opaque; 15935e6ffddeSAvi Kivity 15945e6ffddeSAvi Kivity switch (size) { 15950ed8b6f6SBlue Swirl case 1: 15960ed8b6f6SBlue Swirl eepro100_write1(s, addr, data); 15970ed8b6f6SBlue Swirl break; 15980ed8b6f6SBlue Swirl case 2: 15990ed8b6f6SBlue Swirl eepro100_write2(s, addr, data); 16000ed8b6f6SBlue Swirl break; 16010ed8b6f6SBlue Swirl case 4: 16020ed8b6f6SBlue Swirl eepro100_write4(s, addr, data); 16030ed8b6f6SBlue Swirl break; 16040ed8b6f6SBlue Swirl default: 16050ed8b6f6SBlue Swirl abort(); 16065e6ffddeSAvi Kivity } 1607663e8e51Sths } 1608663e8e51Sths 16095e6ffddeSAvi Kivity static const MemoryRegionOps eepro100_ops = { 16105e6ffddeSAvi Kivity .read = eepro100_read, 16115e6ffddeSAvi Kivity .write = eepro100_write, 16125e6ffddeSAvi Kivity .endianness = DEVICE_LITTLE_ENDIAN, 1613663e8e51Sths }; 1614663e8e51Sths 16154e68f7a0SStefan Hajnoczi static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size) 1616663e8e51Sths { 1617663e8e51Sths /* TODO: 1618663e8e51Sths * - Magic packets should set bit 30 in power management driver register. 1619663e8e51Sths * - Interesting packets should set bit 29 in power management driver register. 1620663e8e51Sths */ 1621*a423a1b5SPhilippe Mathieu-Daudé const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; 1622cc1f0f45SJason Wang EEPRO100State *s = qemu_get_nic_opaque(nc); 1623663e8e51Sths uint16_t rfd_status = 0xa000; 1624792f1d63SStefan Weil #if defined(CONFIG_PAD_RECEIVED_FRAMES) 1625792f1d63SStefan Weil uint8_t min_buf[60]; 1626792f1d63SStefan Weil #endif 1627663e8e51Sths static const uint8_t broadcast_macaddr[6] = 1628663e8e51Sths { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 1629663e8e51Sths 1630792f1d63SStefan Weil #if defined(CONFIG_PAD_RECEIVED_FRAMES) 1631792f1d63SStefan Weil /* Pad to minimum Ethernet frame length */ 1632792f1d63SStefan Weil if (size < sizeof(min_buf)) { 1633792f1d63SStefan Weil memcpy(min_buf, buf, size); 1634792f1d63SStefan Weil memset(&min_buf[size], 0, sizeof(min_buf) - size); 1635792f1d63SStefan Weil buf = min_buf; 1636792f1d63SStefan Weil size = sizeof(min_buf); 1637792f1d63SStefan Weil } 1638792f1d63SStefan Weil #endif 1639792f1d63SStefan Weil 1640663e8e51Sths if (s->configuration[8] & 0x80) { 1641663e8e51Sths /* CSMA is disabled. */ 1642663e8e51Sths logout("%p received while CSMA is disabled\n", s); 16434f1c942bSMark McLoughlin return -1; 1644792f1d63SStefan Weil #if !defined(CONFIG_PAD_RECEIVED_FRAMES) 1645ced5296aSStefan Weil } else if (size < 64 && (s->configuration[7] & BIT(0))) { 1646663e8e51Sths /* Short frame and configuration byte 7/0 (discard short receive) set: 1647663e8e51Sths * Short frame is discarded */ 1648067d01deSStefan Weil logout("%p received short frame (%zu byte)\n", s, size); 1649663e8e51Sths s->statistics.rx_short_frame_errors++; 1650e7493b25SStefan Weil return -1; 1651e7493b25SStefan Weil #endif 1652ced5296aSStefan Weil } else if ((size > MAX_ETH_FRAME_SIZE + 4) && !(s->configuration[18] & BIT(3))) { 1653663e8e51Sths /* Long frame and configuration byte 18/3 (long receive ok) not set: 1654663e8e51Sths * Long frames are discarded. */ 1655067d01deSStefan Weil logout("%p received long frame (%zu byte), ignored\n", s, size); 16564f1c942bSMark McLoughlin return -1; 1657e7493b25SStefan Weil } else if (memcmp(buf, s->conf.macaddr.a, 6) == 0) { /* !!! */ 1658663e8e51Sths /* Frame matches individual address. */ 1659663e8e51Sths /* TODO: check configuration byte 15/4 (ignore U/L). */ 1660067d01deSStefan Weil TRACE(RXTX, logout("%p received frame for me, len=%zu\n", s, size)); 1661663e8e51Sths } else if (memcmp(buf, broadcast_macaddr, 6) == 0) { 1662663e8e51Sths /* Broadcast frame. */ 1663067d01deSStefan Weil TRACE(RXTX, logout("%p received broadcast, len=%zu\n", s, size)); 1664663e8e51Sths rfd_status |= 0x0002; 16657b8737deSStefan Weil } else if (buf[0] & 0x01) { 1666663e8e51Sths /* Multicast frame. */ 16677b8737deSStefan Weil TRACE(RXTX, logout("%p received multicast, len=%zu,%s\n", s, size, nic_dump(buf, size))); 16687f1e9d4eSKevin Wolf if (s->configuration[21] & BIT(3)) { 16697b8737deSStefan Weil /* Multicast all bit is set, receive all multicast frames. */ 16707b8737deSStefan Weil } else { 16717c0348bdSMark Cave-Ayland unsigned mcast_idx = (net_crc32(buf, ETH_ALEN) & BITS(7, 2)) >> 2; 16727b8737deSStefan Weil assert(mcast_idx < 64); 16737b8737deSStefan Weil if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) { 16747b8737deSStefan Weil /* Multicast frame is allowed in hash table. */ 1675ced5296aSStefan Weil } else if (s->configuration[15] & BIT(0)) { 16767b8737deSStefan Weil /* Promiscuous: receive all. */ 16777b8737deSStefan Weil rfd_status |= 0x0004; 16787b8737deSStefan Weil } else { 16797b8737deSStefan Weil TRACE(RXTX, logout("%p multicast ignored\n", s)); 16807b8737deSStefan Weil return -1; 16817f1e9d4eSKevin Wolf } 1682663e8e51Sths } 16837b8737deSStefan Weil /* TODO: Next not for promiscuous mode? */ 1684663e8e51Sths rfd_status |= 0x0002; 1685ced5296aSStefan Weil } else if (s->configuration[15] & BIT(0)) { 1686663e8e51Sths /* Promiscuous: receive all. */ 1687067d01deSStefan Weil TRACE(RXTX, logout("%p received frame in promiscuous mode, len=%zu\n", s, size)); 1688663e8e51Sths rfd_status |= 0x0004; 1689010ec629SStefan Weil } else if (s->configuration[20] & BIT(6)) { 1690010ec629SStefan Weil /* Multiple IA bit set. */ 1691d00d6d00SMark Cave-Ayland unsigned mcast_idx = net_crc32(buf, ETH_ALEN) >> 26; 1692010ec629SStefan Weil assert(mcast_idx < 64); 1693010ec629SStefan Weil if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) { 1694010ec629SStefan Weil TRACE(RXTX, logout("%p accepted, multiple IA bit set\n", s)); 1695010ec629SStefan Weil } else { 1696010ec629SStefan Weil TRACE(RXTX, logout("%p frame ignored, multiple IA bit set\n", s)); 1697010ec629SStefan Weil return -1; 1698010ec629SStefan Weil } 1699663e8e51Sths } else { 1700067d01deSStefan Weil TRACE(RXTX, logout("%p received frame, ignored, len=%zu,%s\n", s, size, 1701aac443e6SStefan Weil nic_dump(buf, size))); 17024f1c942bSMark McLoughlin return size; 1703663e8e51Sths } 1704663e8e51Sths 1705663e8e51Sths if (get_ru_state(s) != ru_ready) { 1706aac443e6SStefan Weil /* No resources available. */ 1707aac443e6SStefan Weil logout("no resources, state=%u\n", get_ru_state(s)); 1708e824012bSStefan Weil /* TODO: RNR interrupt only at first failed frame? */ 1709e824012bSStefan Weil eepro100_rnr_interrupt(s); 1710663e8e51Sths s->statistics.rx_resource_errors++; 1711e7493b25SStefan Weil #if 0 1712e7493b25SStefan Weil assert(!"no resources"); 1713e7493b25SStefan Weil #endif 17144f1c942bSMark McLoughlin return -1; 1715663e8e51Sths } 1716e7493b25SStefan Weil /* !!! */ 1717c227f099SAnthony Liguori eepro100_rx_t rx; 171816ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, s->ru_base + s->ru_offset, 1719e965d4bcSDavid Gibson &rx, sizeof(eepro100_rx_t)); 1720663e8e51Sths uint16_t rfd_command = le16_to_cpu(rx.command); 1721663e8e51Sths uint16_t rfd_size = le16_to_cpu(rx.size); 17227f1e9d4eSKevin Wolf 17237f1e9d4eSKevin Wolf if (size > rfd_size) { 17247f1e9d4eSKevin Wolf logout("Receive buffer (%" PRId16 " bytes) too small for data " 17257f1e9d4eSKevin Wolf "(%zu bytes); data truncated\n", rfd_size, size); 17267f1e9d4eSKevin Wolf size = rfd_size; 17277f1e9d4eSKevin Wolf } 1728792f1d63SStefan Weil #if !defined(CONFIG_PAD_RECEIVED_FRAMES) 1729663e8e51Sths if (size < 64) { 1730663e8e51Sths rfd_status |= 0x0080; 1731663e8e51Sths } 1732792f1d63SStefan Weil #endif 1733aac443e6SStefan Weil TRACE(OTHER, logout("command 0x%04x, link 0x%08x, addr 0x%08x, size %u\n", 1734aac443e6SStefan Weil rfd_command, rx.link, rx.rx_buf_addr, rfd_size)); 173516ef60c9SEduard - Gabriel Munteanu stw_le_pci_dma(&s->dev, s->ru_base + s->ru_offset + 1736*a423a1b5SPhilippe Mathieu-Daudé offsetof(eepro100_rx_t, status), rfd_status, attrs); 173716ef60c9SEduard - Gabriel Munteanu stw_le_pci_dma(&s->dev, s->ru_base + s->ru_offset + 1738*a423a1b5SPhilippe Mathieu-Daudé offsetof(eepro100_rx_t, count), size, attrs); 1739663e8e51Sths /* Early receive interrupt not supported. */ 1740e7493b25SStefan Weil #if 0 1741e7493b25SStefan Weil eepro100_er_interrupt(s); 1742e7493b25SStefan Weil #endif 1743663e8e51Sths /* Receive CRC Transfer not supported. */ 1744ced5296aSStefan Weil if (s->configuration[18] & BIT(2)) { 17457f1e9d4eSKevin Wolf missing("Receive CRC Transfer"); 17467f1e9d4eSKevin Wolf return -1; 17477f1e9d4eSKevin Wolf } 1748663e8e51Sths /* TODO: check stripping enable bit. */ 1749e7493b25SStefan Weil #if 0 1750e7493b25SStefan Weil assert(!(s->configuration[17] & BIT(0))); 1751e7493b25SStefan Weil #endif 175216ef60c9SEduard - Gabriel Munteanu pci_dma_write(&s->dev, s->ru_base + s->ru_offset + 175327112f18SStefan Weil sizeof(eepro100_rx_t), buf, size); 1754663e8e51Sths s->statistics.rx_good_frames++; 1755663e8e51Sths eepro100_fr_interrupt(s); 1756663e8e51Sths s->ru_offset = le32_to_cpu(rx.link); 1757ced5296aSStefan Weil if (rfd_command & COMMAND_EL) { 1758663e8e51Sths /* EL bit is set, so this was the last frame. */ 17597f1e9d4eSKevin Wolf logout("receive: Running out of frames\n"); 17601069985fSBo Yang set_ru_state(s, ru_no_resources); 17611069985fSBo Yang eepro100_rnr_interrupt(s); 1762663e8e51Sths } 1763ced5296aSStefan Weil if (rfd_command & COMMAND_S) { 1764663e8e51Sths /* S bit is set. */ 1765663e8e51Sths set_ru_state(s, ru_suspended); 1766663e8e51Sths } 17674f1c942bSMark McLoughlin return size; 1768663e8e51Sths } 1769663e8e51Sths 1770151b2986SJuan Quintela static const VMStateDescription vmstate_eepro100 = { 1771151b2986SJuan Quintela .version_id = 3, 1772151b2986SJuan Quintela .minimum_version_id = 2, 1773151b2986SJuan Quintela .fields = (VMStateField[]) { 1774151b2986SJuan Quintela VMSTATE_PCI_DEVICE(dev, EEPRO100State), 1775151b2986SJuan Quintela VMSTATE_UNUSED(32), 1776151b2986SJuan Quintela VMSTATE_BUFFER(mult, EEPRO100State), 1777151b2986SJuan Quintela VMSTATE_BUFFER(mem, EEPRO100State), 17783706c43fSStefan Weil /* Save all members of struct between scb_stat and mem. */ 1779151b2986SJuan Quintela VMSTATE_UINT8(scb_stat, EEPRO100State), 1780151b2986SJuan Quintela VMSTATE_UINT8(int_stat, EEPRO100State), 1781151b2986SJuan Quintela VMSTATE_UNUSED(3*4), 1782151b2986SJuan Quintela VMSTATE_MACADDR(conf.macaddr, EEPRO100State), 1783151b2986SJuan Quintela VMSTATE_UNUSED(19*4), 1784151b2986SJuan Quintela VMSTATE_UINT16_ARRAY(mdimem, EEPRO100State, 32), 1785aac443e6SStefan Weil /* The eeprom should be saved and restored by its own routines. */ 1786151b2986SJuan Quintela VMSTATE_UINT32(device, EEPRO100State), 1787151b2986SJuan Quintela /* TODO check device. */ 1788151b2986SJuan Quintela VMSTATE_UINT32(cu_base, EEPRO100State), 1789151b2986SJuan Quintela VMSTATE_UINT32(cu_offset, EEPRO100State), 1790151b2986SJuan Quintela VMSTATE_UINT32(ru_base, EEPRO100State), 1791151b2986SJuan Quintela VMSTATE_UINT32(ru_offset, EEPRO100State), 1792151b2986SJuan Quintela VMSTATE_UINT32(statsaddr, EEPRO100State), 1793ba42b646SStefan Weil /* Save eepro100_stats_t statistics. */ 1794151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_good_frames, EEPRO100State), 1795151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_max_collisions, EEPRO100State), 1796151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_late_collisions, EEPRO100State), 1797151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_underruns, EEPRO100State), 1798151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_lost_crs, EEPRO100State), 1799151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_deferred, EEPRO100State), 1800151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_single_collisions, EEPRO100State), 1801151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_multiple_collisions, EEPRO100State), 1802151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_total_collisions, EEPRO100State), 1803151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_good_frames, EEPRO100State), 1804151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_crc_errors, EEPRO100State), 1805151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_alignment_errors, EEPRO100State), 1806151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_resource_errors, EEPRO100State), 1807151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_overrun_errors, EEPRO100State), 1808151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_cdt_errors, EEPRO100State), 1809151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_short_frame_errors, EEPRO100State), 1810151b2986SJuan Quintela VMSTATE_UINT32(statistics.fc_xmt_pause, EEPRO100State), 1811151b2986SJuan Quintela VMSTATE_UINT32(statistics.fc_rcv_pause, EEPRO100State), 1812151b2986SJuan Quintela VMSTATE_UINT32(statistics.fc_rcv_unsupported, EEPRO100State), 1813151b2986SJuan Quintela VMSTATE_UINT16(statistics.xmt_tco_frames, EEPRO100State), 1814151b2986SJuan Quintela VMSTATE_UINT16(statistics.rcv_tco_frames, EEPRO100State), 18152657c663Sbalrog /* Configuration bytes. */ 1816151b2986SJuan Quintela VMSTATE_BUFFER(configuration, EEPRO100State), 1817151b2986SJuan Quintela VMSTATE_END_OF_LIST() 1818663e8e51Sths } 1819151b2986SJuan Quintela }; 1820663e8e51Sths 1821f90c2bcdSAlex Williamson static void pci_nic_uninit(PCIDevice *pci_dev) 1822b946a153Saliguori { 1823c4c270e2SStefan Weil EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev); 1824b946a153Saliguori 18253cad405bSMarc-André Lureau vmstate_unregister(VMSTATE_IF(&pci_dev->qdev), s->vmstate, s); 18262634ab7fSLi Qiang g_free(s->vmstate); 18275fce2b3eSAlex Williamson eeprom93xx_free(&pci_dev->qdev, s->eeprom); 1828948ecf21SJason Wang qemu_del_nic(s->nic); 1829b946a153Saliguori } 1830b946a153Saliguori 1831e00e365eSMark McLoughlin static NetClientInfo net_eepro100_info = { 1832f394b2e2SEric Blake .type = NET_CLIENT_DRIVER_NIC, 1833e00e365eSMark McLoughlin .size = sizeof(NICState), 1834e00e365eSMark McLoughlin .receive = nic_receive, 1835e00e365eSMark McLoughlin }; 1836e00e365eSMark McLoughlin 18379af21dbeSMarkus Armbruster static void e100_nic_realize(PCIDevice *pci_dev, Error **errp) 1838663e8e51Sths { 1839273a2142SJuan Quintela EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev); 184040021f08SAnthony Liguori E100PCIDeviceInfo *info = eepro100_get_class(s); 18419a7c2a59SMao Zhongyi Error *local_err = NULL; 1842663e8e51Sths 1843aac443e6SStefan Weil TRACE(OTHER, logout("\n")); 1844663e8e51Sths 184540021f08SAnthony Liguori s->device = info->device; 1846663e8e51Sths 18479a7c2a59SMao Zhongyi e100_pci_reset(s, &local_err); 18489a7c2a59SMao Zhongyi if (local_err) { 18499a7c2a59SMao Zhongyi error_propagate(errp, local_err); 18509a7c2a59SMao Zhongyi return; 18519a7c2a59SMao Zhongyi } 1852663e8e51Sths 1853663e8e51Sths /* Add 64 * 2 EEPROM. i82557 and i82558 support a 64 word EEPROM, 1854663e8e51Sths * i82559 and later support 64 or 256 word EEPROM. */ 18555fce2b3eSAlex Williamson s->eeprom = eeprom93xx_new(&pci_dev->qdev, EEPROM_SIZE); 1856663e8e51Sths 1857663e8e51Sths /* Handler for memory-mapped I/O */ 1858eedfac6fSPaolo Bonzini memory_region_init_io(&s->mmio_bar, OBJECT(s), &eepro100_ops, s, 1859eedfac6fSPaolo Bonzini "eepro100-mmio", PCI_MEM_SIZE); 1860e824b2ccSAvi Kivity pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->mmio_bar); 1861eedfac6fSPaolo Bonzini memory_region_init_io(&s->io_bar, OBJECT(s), &eepro100_ops, s, 1862eedfac6fSPaolo Bonzini "eepro100-io", PCI_IO_SIZE); 1863e824b2ccSAvi Kivity pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar); 18645e6ffddeSAvi Kivity /* FIXME: flash aliases to mmio?! */ 1865eedfac6fSPaolo Bonzini memory_region_init_io(&s->flash_bar, OBJECT(s), &eepro100_ops, s, 1866eedfac6fSPaolo Bonzini "eepro100-flash", PCI_FLASH_SIZE); 1867e824b2ccSAvi Kivity pci_register_bar(&s->dev, 2, 0, &s->flash_bar); 1868663e8e51Sths 1869508ef936SGerd Hoffmann qemu_macaddr_default_if_unset(&s->conf.macaddr); 1870ce0e58b3SStefan Weil logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6)); 1871663e8e51Sths 1872663e8e51Sths nic_reset(s); 1873663e8e51Sths 1874e00e365eSMark McLoughlin s->nic = qemu_new_nic(&net_eepro100_info, &s->conf, 1875f79f2bfcSAnthony Liguori object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s); 1876663e8e51Sths 1877b356f76dSJason Wang qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); 1878b356f76dSJason Wang TRACE(OTHER, logout("%s\n", qemu_get_queue(s->nic)->info_str)); 1879663e8e51Sths 1880a08d4367SJan Kiszka qemu_register_reset(nic_reset, s); 1881663e8e51Sths 1882e4d67e4fSMarc-André Lureau s->vmstate = g_memdup(&vmstate_eepro100, sizeof(vmstate_eepro100)); 1883b356f76dSJason Wang s->vmstate->name = qemu_get_queue(s->nic)->model; 18841df2c9a2SPeter Xu vmstate_register(VMSTATE_IF(&pci_dev->qdev), VMSTATE_INSTANCE_ID_ANY, 18851df2c9a2SPeter Xu s->vmstate, s); 1886663e8e51Sths } 1887663e8e51Sths 18887317bb17SGonglei static void eepro100_instance_init(Object *obj) 18897317bb17SGonglei { 18907317bb17SGonglei EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, PCI_DEVICE(obj)); 18917317bb17SGonglei device_add_bootindex_property(obj, &s->conf.bootindex, 18927317bb17SGonglei "bootindex", "/ethernet-phy@0", 189340c2281cSMarkus Armbruster DEVICE(s)); 18947317bb17SGonglei } 18957317bb17SGonglei 1896558c8634SStefan Weil static E100PCIDeviceInfo e100_devices[] = { 1897663e8e51Sths { 189839bffca2SAnthony Liguori .name = "i82550", 189939bffca2SAnthony Liguori .desc = "Intel i82550 Ethernet", 1900558c8634SStefan Weil .device = i82550, 1901558c8634SStefan Weil /* TODO: check device id. */ 190240021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82551IT, 1903558c8634SStefan Weil /* Revision ID: 0x0c, 0x0d, 0x0e. */ 190440021f08SAnthony Liguori .revision = 0x0e, 1905558c8634SStefan Weil /* TODO: check size of statistical counters. */ 1906558c8634SStefan Weil .stats_size = 80, 1907558c8634SStefan Weil /* TODO: check extended tcb support. */ 1908558c8634SStefan Weil .has_extended_tcb_support = true, 1909558c8634SStefan Weil .power_management = true, 1910558c8634SStefan Weil },{ 191139bffca2SAnthony Liguori .name = "i82551", 191239bffca2SAnthony Liguori .desc = "Intel i82551 Ethernet", 1913558c8634SStefan Weil .device = i82551, 191440021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82551IT, 1915558c8634SStefan Weil /* Revision ID: 0x0f, 0x10. */ 191640021f08SAnthony Liguori .revision = 0x0f, 1917558c8634SStefan Weil /* TODO: check size of statistical counters. */ 1918558c8634SStefan Weil .stats_size = 80, 1919558c8634SStefan Weil .has_extended_tcb_support = true, 1920558c8634SStefan Weil .power_management = true, 1921558c8634SStefan Weil },{ 192239bffca2SAnthony Liguori .name = "i82557a", 192339bffca2SAnthony Liguori .desc = "Intel i82557A Ethernet", 1924558c8634SStefan Weil .device = i82557A, 192540021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557, 192640021f08SAnthony Liguori .revision = 0x01, 1927558c8634SStefan Weil .power_management = false, 1928558c8634SStefan Weil },{ 192939bffca2SAnthony Liguori .name = "i82557b", 193039bffca2SAnthony Liguori .desc = "Intel i82557B Ethernet", 1931558c8634SStefan Weil .device = i82557B, 193240021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557, 193340021f08SAnthony Liguori .revision = 0x02, 1934558c8634SStefan Weil .power_management = false, 1935558c8634SStefan Weil },{ 193639bffca2SAnthony Liguori .name = "i82557c", 193739bffca2SAnthony Liguori .desc = "Intel i82557C Ethernet", 1938558c8634SStefan Weil .device = i82557C, 193940021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557, 194040021f08SAnthony Liguori .revision = 0x03, 1941558c8634SStefan Weil .power_management = false, 1942558c8634SStefan Weil },{ 194339bffca2SAnthony Liguori .name = "i82558a", 194439bffca2SAnthony Liguori .desc = "Intel i82558A Ethernet", 1945558c8634SStefan Weil .device = i82558A, 194640021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557, 194740021f08SAnthony Liguori .revision = 0x04, 1948558c8634SStefan Weil .stats_size = 76, 1949558c8634SStefan Weil .has_extended_tcb_support = true, 1950558c8634SStefan Weil .power_management = true, 1951558c8634SStefan Weil },{ 195239bffca2SAnthony Liguori .name = "i82558b", 195339bffca2SAnthony Liguori .desc = "Intel i82558B Ethernet", 1954558c8634SStefan Weil .device = i82558B, 195540021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557, 195640021f08SAnthony Liguori .revision = 0x05, 1957558c8634SStefan Weil .stats_size = 76, 1958558c8634SStefan Weil .has_extended_tcb_support = true, 1959558c8634SStefan Weil .power_management = true, 1960558c8634SStefan Weil },{ 196139bffca2SAnthony Liguori .name = "i82559a", 196239bffca2SAnthony Liguori .desc = "Intel i82559A Ethernet", 1963558c8634SStefan Weil .device = i82559A, 196440021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557, 196540021f08SAnthony Liguori .revision = 0x06, 1966558c8634SStefan Weil .stats_size = 80, 1967558c8634SStefan Weil .has_extended_tcb_support = true, 1968558c8634SStefan Weil .power_management = true, 1969558c8634SStefan Weil },{ 197039bffca2SAnthony Liguori .name = "i82559b", 197139bffca2SAnthony Liguori .desc = "Intel i82559B Ethernet", 1972558c8634SStefan Weil .device = i82559B, 197340021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557, 197440021f08SAnthony Liguori .revision = 0x07, 1975558c8634SStefan Weil .stats_size = 80, 1976558c8634SStefan Weil .has_extended_tcb_support = true, 1977558c8634SStefan Weil .power_management = true, 1978558c8634SStefan Weil },{ 197939bffca2SAnthony Liguori .name = "i82559c", 198039bffca2SAnthony Liguori .desc = "Intel i82559C Ethernet", 1981558c8634SStefan Weil .device = i82559C, 198240021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557, 1983558c8634SStefan Weil #if 0 198440021f08SAnthony Liguori .revision = 0x08, 1985558c8634SStefan Weil #endif 1986558c8634SStefan Weil /* TODO: Windows wants revision id 0x0c. */ 198740021f08SAnthony Liguori .revision = 0x0c, 1988ad03502bSIsaku Yamahata #if EEPROM_SIZE > 0 198940021f08SAnthony Liguori .subsystem_vendor_id = PCI_VENDOR_ID_INTEL, 199040021f08SAnthony Liguori .subsystem_id = 0x0040, 1991ad03502bSIsaku Yamahata #endif 1992558c8634SStefan Weil .stats_size = 80, 1993558c8634SStefan Weil .has_extended_tcb_support = true, 1994558c8634SStefan Weil .power_management = true, 1995558c8634SStefan Weil },{ 199639bffca2SAnthony Liguori .name = "i82559er", 199739bffca2SAnthony Liguori .desc = "Intel i82559ER Ethernet", 1998558c8634SStefan Weil .device = i82559ER, 199940021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82551IT, 200040021f08SAnthony Liguori .revision = 0x09, 2001558c8634SStefan Weil .stats_size = 80, 2002558c8634SStefan Weil .has_extended_tcb_support = true, 2003558c8634SStefan Weil .power_management = true, 2004558c8634SStefan Weil },{ 200539bffca2SAnthony Liguori .name = "i82562", 200639bffca2SAnthony Liguori .desc = "Intel i82562 Ethernet", 2007558c8634SStefan Weil .device = i82562, 2008558c8634SStefan Weil /* TODO: check device id. */ 200940021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82551IT, 2010558c8634SStefan Weil /* TODO: wrong revision id. */ 201140021f08SAnthony Liguori .revision = 0x0e, 2012558c8634SStefan Weil .stats_size = 80, 2013558c8634SStefan Weil .has_extended_tcb_support = true, 2014558c8634SStefan Weil .power_management = true, 2015db667a12SStefan Weil },{ 2016db667a12SStefan Weil /* Toshiba Tecra 8200. */ 201739bffca2SAnthony Liguori .name = "i82801", 201839bffca2SAnthony Liguori .desc = "Intel i82801 Ethernet", 2019db667a12SStefan Weil .device = i82801, 202040021f08SAnthony Liguori .device_id = 0x2449, 202140021f08SAnthony Liguori .revision = 0x03, 2022db667a12SStefan Weil .stats_size = 80, 2023db667a12SStefan Weil .has_extended_tcb_support = true, 2024db667a12SStefan Weil .power_management = true, 2025663e8e51Sths } 2026558c8634SStefan Weil }; 2027663e8e51Sths 202840021f08SAnthony Liguori static E100PCIDeviceInfo *eepro100_get_class_by_name(const char *typename) 202940021f08SAnthony Liguori { 203040021f08SAnthony Liguori E100PCIDeviceInfo *info = NULL; 203140021f08SAnthony Liguori int i; 203240021f08SAnthony Liguori 203340021f08SAnthony Liguori /* This is admittedly awkward but also temporary. QOM allows for 203440021f08SAnthony Liguori * parameterized typing and for subclassing both of which would suitable 203540021f08SAnthony Liguori * handle what's going on here. But class_data is already being used as 203640021f08SAnthony Liguori * a stop-gap hack to allow incremental qdev conversion so we cannot use it 203740021f08SAnthony Liguori * right now. Once we merge the final QOM series, we can come back here and 203840021f08SAnthony Liguori * do this in a much more elegant fashion. 203940021f08SAnthony Liguori */ 204040021f08SAnthony Liguori for (i = 0; i < ARRAY_SIZE(e100_devices); i++) { 204139bffca2SAnthony Liguori if (strcmp(e100_devices[i].name, typename) == 0) { 204240021f08SAnthony Liguori info = &e100_devices[i]; 204340021f08SAnthony Liguori break; 204440021f08SAnthony Liguori } 204540021f08SAnthony Liguori } 204640021f08SAnthony Liguori assert(info != NULL); 204740021f08SAnthony Liguori 204840021f08SAnthony Liguori return info; 204940021f08SAnthony Liguori } 205040021f08SAnthony Liguori 205140021f08SAnthony Liguori static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s) 205240021f08SAnthony Liguori { 205340021f08SAnthony Liguori return eepro100_get_class_by_name(object_get_typename(OBJECT(s))); 205440021f08SAnthony Liguori } 205540021f08SAnthony Liguori 205639bffca2SAnthony Liguori static Property e100_properties[] = { 205739bffca2SAnthony Liguori DEFINE_NIC_PROPERTIES(EEPRO100State, conf), 205839bffca2SAnthony Liguori DEFINE_PROP_END_OF_LIST(), 205939bffca2SAnthony Liguori }; 206039bffca2SAnthony Liguori 206140021f08SAnthony Liguori static void eepro100_class_init(ObjectClass *klass, void *data) 206240021f08SAnthony Liguori { 206339bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 206440021f08SAnthony Liguori PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); 206540021f08SAnthony Liguori E100PCIDeviceInfo *info; 206640021f08SAnthony Liguori 206740021f08SAnthony Liguori info = eepro100_get_class_by_name(object_class_get_name(klass)); 206840021f08SAnthony Liguori 2069125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); 20704f67d30bSMarc-André Lureau device_class_set_props(dc, e100_properties); 207139bffca2SAnthony Liguori dc->desc = info->desc; 207240021f08SAnthony Liguori k->vendor_id = PCI_VENDOR_ID_INTEL; 207340021f08SAnthony Liguori k->class_id = PCI_CLASS_NETWORK_ETHERNET; 207440021f08SAnthony Liguori k->romfile = "pxe-eepro100.rom"; 20759af21dbeSMarkus Armbruster k->realize = e100_nic_realize; 207640021f08SAnthony Liguori k->exit = pci_nic_uninit; 207740021f08SAnthony Liguori k->device_id = info->device_id; 207840021f08SAnthony Liguori k->revision = info->revision; 207940021f08SAnthony Liguori k->subsystem_vendor_id = info->subsystem_vendor_id; 208040021f08SAnthony Liguori k->subsystem_id = info->subsystem_id; 208140021f08SAnthony Liguori } 208240021f08SAnthony Liguori 208383f7d43aSAndreas Färber static void eepro100_register_types(void) 20849d07d757SPaul Brook { 2085558c8634SStefan Weil size_t i; 2086558c8634SStefan Weil for (i = 0; i < ARRAY_SIZE(e100_devices); i++) { 208739bffca2SAnthony Liguori TypeInfo type_info = {}; 208839bffca2SAnthony Liguori E100PCIDeviceInfo *info = &e100_devices[i]; 208940021f08SAnthony Liguori 209039bffca2SAnthony Liguori type_info.name = info->name; 209139bffca2SAnthony Liguori type_info.parent = TYPE_PCI_DEVICE; 209239bffca2SAnthony Liguori type_info.class_init = eepro100_class_init; 209339bffca2SAnthony Liguori type_info.instance_size = sizeof(EEPRO100State); 20947317bb17SGonglei type_info.instance_init = eepro100_instance_init; 2095fd3b02c8SEduardo Habkost type_info.interfaces = (InterfaceInfo[]) { 2096fd3b02c8SEduardo Habkost { INTERFACE_CONVENTIONAL_PCI_DEVICE }, 2097fd3b02c8SEduardo Habkost { }, 2098fd3b02c8SEduardo Habkost }; 209940021f08SAnthony Liguori 210039bffca2SAnthony Liguori type_register(&type_info); 2101558c8634SStefan Weil } 21029d07d757SPaul Brook } 21039d07d757SPaul Brook 210483f7d43aSAndreas Färber type_init(eepro100_register_types) 2105