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" 44*872a2b7cSPhilippe Mathieu-Daudé #include "qemu/units.h" 4583c9f4caSPaolo Bonzini #include "hw/hw.h" 4683c9f4caSPaolo Bonzini #include "hw/pci/pci.h" 471422e32dSPaolo Bonzini #include "net/net.h" 487c0348bdSMark Cave-Ayland #include "net/eth.h" 490d09e41aSPaolo Bonzini #include "hw/nvram/eeprom93xx.h" 509c17d615SPaolo Bonzini #include "sysemu/sysemu.h" 519c17d615SPaolo Bonzini #include "sysemu/dma.h" 52949fc823SMarcel Apfelbaum #include "qemu/bitops.h" 539a7c2a59SMao Zhongyi #include "qapi/error.h" 54663e8e51Sths 55792f1d63SStefan Weil /* QEMU sends frames smaller than 60 bytes to ethernet nics. 56792f1d63SStefan Weil * Such frames are rejected by real nics and their emulations. 57792f1d63SStefan Weil * To avoid this behaviour, other nic emulations pad received 58792f1d63SStefan Weil * frames. The following definition enables this padding for 59792f1d63SStefan Weil * eepro100, too. We keep the define around in case it might 60792f1d63SStefan Weil * become useful the future if the core networking is ever 61792f1d63SStefan Weil * changed to pad short packets itself. */ 62792f1d63SStefan Weil #define CONFIG_PAD_RECEIVED_FRAMES 63792f1d63SStefan Weil 64aac443e6SStefan Weil /* Debug EEPRO100 card. */ 65ce0e58b3SStefan Weil #if 0 66ce0e58b3SStefan Weil # define DEBUG_EEPRO100 67ce0e58b3SStefan Weil #endif 68663e8e51Sths 69663e8e51Sths #ifdef DEBUG_EEPRO100 70001faf32SBlue Swirl #define logout(fmt, ...) fprintf(stderr, "EE100\t%-24s" fmt, __func__, ## __VA_ARGS__) 71663e8e51Sths #else 72001faf32SBlue Swirl #define logout(fmt, ...) ((void)0) 73663e8e51Sths #endif 74663e8e51Sths 75663e8e51Sths /* Set flags to 0 to disable debug output. */ 76aac443e6SStefan Weil #define INT 1 /* interrupt related actions */ 77aac443e6SStefan Weil #define MDI 1 /* mdi related actions */ 78aac443e6SStefan Weil #define OTHER 1 79aac443e6SStefan Weil #define RXTX 1 80aac443e6SStefan Weil #define EEPROM 1 /* eeprom related actions */ 81663e8e51Sths 82663e8e51Sths #define TRACE(flag, command) ((flag) ? (command) : (void)0) 83663e8e51Sths 847f1e9d4eSKevin Wolf #define missing(text) fprintf(stderr, "eepro100: feature is missing in this emulation: " text "\n") 85663e8e51Sths 86663e8e51Sths #define MAX_ETH_FRAME_SIZE 1514 87663e8e51Sths 88663e8e51Sths /* This driver supports several different devices which are declared here. */ 89c4c270e2SStefan Weil #define i82550 0x82550 90663e8e51Sths #define i82551 0x82551 91c4c270e2SStefan Weil #define i82557A 0x82557a 92663e8e51Sths #define i82557B 0x82557b 93663e8e51Sths #define i82557C 0x82557c 94c4c270e2SStefan Weil #define i82558A 0x82558a 95663e8e51Sths #define i82558B 0x82558b 96c4c270e2SStefan Weil #define i82559A 0x82559a 97c4c270e2SStefan Weil #define i82559B 0x82559b 98663e8e51Sths #define i82559C 0x82559c 99663e8e51Sths #define i82559ER 0x82559e 100663e8e51Sths #define i82562 0x82562 101db667a12SStefan Weil #define i82801 0x82801 102663e8e51Sths 103aac443e6SStefan Weil /* Use 64 word EEPROM. TODO: could be a runtime option. */ 104663e8e51Sths #define EEPROM_SIZE 64 105663e8e51Sths 106663e8e51Sths #define PCI_MEM_SIZE (4 * KiB) 107663e8e51Sths #define PCI_IO_SIZE 64 108663e8e51Sths #define PCI_FLASH_SIZE (128 * KiB) 109663e8e51Sths 110663e8e51Sths #define BITS(n, m) (((0xffffffffU << (31 - n)) >> (31 - n + m)) << m) 111663e8e51Sths 112663e8e51Sths /* The SCB accepts the following controls for the Tx and Rx units: */ 113663e8e51Sths #define CU_NOP 0x0000 /* No operation. */ 114663e8e51Sths #define CU_START 0x0010 /* CU start. */ 115663e8e51Sths #define CU_RESUME 0x0020 /* CU resume. */ 116663e8e51Sths #define CU_STATSADDR 0x0040 /* Load dump counters address. */ 117663e8e51Sths #define CU_SHOWSTATS 0x0050 /* Dump statistical counters. */ 118663e8e51Sths #define CU_CMD_BASE 0x0060 /* Load CU base address. */ 119663e8e51Sths #define CU_DUMPSTATS 0x0070 /* Dump and reset statistical counters. */ 120663e8e51Sths #define CU_SRESUME 0x00a0 /* CU static resume. */ 121663e8e51Sths 122663e8e51Sths #define RU_NOP 0x0000 123663e8e51Sths #define RX_START 0x0001 124663e8e51Sths #define RX_RESUME 0x0002 125e824012bSStefan Weil #define RU_ABORT 0x0004 126663e8e51Sths #define RX_ADDR_LOAD 0x0006 127663e8e51Sths #define RX_RESUMENR 0x0007 128663e8e51Sths #define INT_MASK 0x0100 129663e8e51Sths #define DRVR_INT 0x0200 /* Driver generated interrupt. */ 130663e8e51Sths 131558c8634SStefan Weil typedef struct { 13239bffca2SAnthony Liguori const char *name; 13339bffca2SAnthony Liguori const char *desc; 13440021f08SAnthony Liguori uint16_t device_id; 13540021f08SAnthony Liguori uint8_t revision; 13640021f08SAnthony Liguori uint16_t subsystem_vendor_id; 13740021f08SAnthony Liguori uint16_t subsystem_id; 13840021f08SAnthony Liguori 139558c8634SStefan Weil uint32_t device; 140558c8634SStefan Weil uint8_t stats_size; 141558c8634SStefan Weil bool has_extended_tcb_support; 142558c8634SStefan Weil bool power_management; 143558c8634SStefan Weil } E100PCIDeviceInfo; 144558c8634SStefan Weil 145663e8e51Sths /* Offsets to the various registers. 146663e8e51Sths All accesses need not be longword aligned. */ 147e5e23ab8SStefan Weil typedef enum { 1480908bba1SStefan Weil SCBStatus = 0, /* Status Word. */ 149663e8e51Sths SCBAck = 1, 150663e8e51Sths SCBCmd = 2, /* Rx/Command Unit command and status. */ 151663e8e51Sths SCBIntmask = 3, 152663e8e51Sths SCBPointer = 4, /* General purpose pointer. */ 153663e8e51Sths SCBPort = 8, /* Misc. commands and operands. */ 1540908bba1SStefan Weil SCBflash = 12, /* Flash memory control. */ 1550908bba1SStefan Weil SCBeeprom = 14, /* EEPROM control. */ 156663e8e51Sths SCBCtrlMDI = 16, /* MDI interface control. */ 157663e8e51Sths SCBEarlyRx = 20, /* Early receive byte count. */ 1580908bba1SStefan Weil SCBFlow = 24, /* Flow Control. */ 1590908bba1SStefan Weil SCBpmdr = 27, /* Power Management Driver. */ 1600908bba1SStefan Weil SCBgctrl = 28, /* General Control. */ 1610908bba1SStefan Weil SCBgstat = 29, /* General Status. */ 162e5e23ab8SStefan Weil } E100RegisterOffset; 163663e8e51Sths 164663e8e51Sths /* A speedo3 transmit buffer descriptor with two buffers... */ 165663e8e51Sths typedef struct { 166663e8e51Sths uint16_t status; 167663e8e51Sths uint16_t command; 168663e8e51Sths uint32_t link; /* void * */ 1697b8737deSStefan Weil uint32_t tbd_array_addr; /* transmit buffer descriptor array address. */ 170663e8e51Sths uint16_t tcb_bytes; /* transmit command block byte count (in lower 14 bits */ 171663e8e51Sths uint8_t tx_threshold; /* transmit threshold */ 172663e8e51Sths uint8_t tbd_count; /* TBD number */ 173e7493b25SStefan Weil #if 0 174e7493b25SStefan Weil /* This constitutes two "TBD" entries: hdr and data */ 175e7493b25SStefan Weil uint32_t tx_buf_addr0; /* void *, header of frame to be transmitted. */ 176e7493b25SStefan Weil int32_t tx_buf_size0; /* Length of Tx hdr. */ 177e7493b25SStefan Weil uint32_t tx_buf_addr1; /* void *, data to be transmitted. */ 178e7493b25SStefan Weil int32_t tx_buf_size1; /* Length of Tx data. */ 179e7493b25SStefan Weil #endif 180c227f099SAnthony Liguori } eepro100_tx_t; 181663e8e51Sths 182663e8e51Sths /* Receive frame descriptor. */ 183663e8e51Sths typedef struct { 184663e8e51Sths int16_t status; 185663e8e51Sths uint16_t command; 186663e8e51Sths uint32_t link; /* struct RxFD * */ 187663e8e51Sths uint32_t rx_buf_addr; /* void * */ 188663e8e51Sths uint16_t count; 189663e8e51Sths uint16_t size; 19027112f18SStefan Weil /* Ethernet frame data follows. */ 191c227f099SAnthony Liguori } eepro100_rx_t; 192663e8e51Sths 193ced5296aSStefan Weil typedef enum { 194ced5296aSStefan Weil COMMAND_EL = BIT(15), 195ced5296aSStefan Weil COMMAND_S = BIT(14), 196ced5296aSStefan Weil COMMAND_I = BIT(13), 197ced5296aSStefan Weil COMMAND_NC = BIT(4), 198ced5296aSStefan Weil COMMAND_SF = BIT(3), 199ced5296aSStefan Weil COMMAND_CMD = BITS(2, 0), 200ced5296aSStefan Weil } scb_command_bit; 201ced5296aSStefan Weil 202ced5296aSStefan Weil typedef enum { 203ced5296aSStefan Weil STATUS_C = BIT(15), 204ced5296aSStefan Weil STATUS_OK = BIT(13), 205ced5296aSStefan Weil } scb_status_bit; 206ced5296aSStefan Weil 207663e8e51Sths typedef struct { 208663e8e51Sths uint32_t tx_good_frames, tx_max_collisions, tx_late_collisions, 209663e8e51Sths tx_underruns, tx_lost_crs, tx_deferred, tx_single_collisions, 210663e8e51Sths tx_multiple_collisions, tx_total_collisions; 211663e8e51Sths uint32_t rx_good_frames, rx_crc_errors, rx_alignment_errors, 212663e8e51Sths rx_resource_errors, rx_overrun_errors, rx_cdt_errors, 213663e8e51Sths rx_short_frame_errors; 214663e8e51Sths uint32_t fc_xmt_pause, fc_rcv_pause, fc_rcv_unsupported; 215663e8e51Sths uint16_t xmt_tco_frames, rcv_tco_frames; 216ba42b646SStefan Weil /* TODO: i82559 has six reserved statistics but a total of 24 dwords. */ 217ba42b646SStefan Weil uint32_t reserved[4]; 218c227f099SAnthony Liguori } eepro100_stats_t; 219663e8e51Sths 220663e8e51Sths typedef enum { 221663e8e51Sths cu_idle = 0, 222663e8e51Sths cu_suspended = 1, 223663e8e51Sths cu_active = 2, 224663e8e51Sths cu_lpq_active = 2, 225663e8e51Sths cu_hqp_active = 3 226c227f099SAnthony Liguori } cu_state_t; 227663e8e51Sths 228663e8e51Sths typedef enum { 229663e8e51Sths ru_idle = 0, 230663e8e51Sths ru_suspended = 1, 231663e8e51Sths ru_no_resources = 2, 232663e8e51Sths ru_ready = 4 233c227f099SAnthony Liguori } ru_state_t; 234663e8e51Sths 235663e8e51Sths typedef struct { 236273a2142SJuan Quintela PCIDevice dev; 237010ec629SStefan Weil /* Hash register (multicast mask array, multiple individual addresses). */ 238010ec629SStefan Weil uint8_t mult[8]; 2395e6ffddeSAvi Kivity MemoryRegion mmio_bar; 2405e6ffddeSAvi Kivity MemoryRegion io_bar; 2415e6ffddeSAvi Kivity MemoryRegion flash_bar; 242e00e365eSMark McLoughlin NICState *nic; 243508ef936SGerd Hoffmann NICConf conf; 244663e8e51Sths uint8_t scb_stat; /* SCB stat/ack byte */ 245663e8e51Sths uint8_t int_stat; /* PCI interrupt status */ 2463706c43fSStefan Weil /* region must not be saved by nic_save. */ 247663e8e51Sths uint16_t mdimem[32]; 248c227f099SAnthony Liguori eeprom_t *eeprom; 249663e8e51Sths uint32_t device; /* device variant */ 250663e8e51Sths /* (cu_base + cu_offset) address the next command block in the command block list. */ 251663e8e51Sths uint32_t cu_base; /* CU base address */ 252663e8e51Sths uint32_t cu_offset; /* CU address offset */ 253663e8e51Sths /* (ru_base + ru_offset) address the RFD in the Receive Frame Area. */ 254663e8e51Sths uint32_t ru_base; /* RU base address */ 255663e8e51Sths uint32_t ru_offset; /* RU address offset */ 256c227f099SAnthony Liguori uint32_t statsaddr; /* pointer to eepro100_stats_t */ 257ba42b646SStefan Weil 258f3a52e50SStefan Weil /* Temporary status information (no need to save these values), 259f3a52e50SStefan Weil * used while processing CU commands. */ 260f3a52e50SStefan Weil eepro100_tx_t tx; /* transmit buffer descriptor */ 261f3a52e50SStefan Weil uint32_t cb_address; /* = cu_base + cu_offset */ 262f3a52e50SStefan Weil 263ba42b646SStefan Weil /* Statistical counters. Also used for wake-up packet (i82559). */ 264ba42b646SStefan Weil eepro100_stats_t statistics; 265ba42b646SStefan Weil 266e5e23ab8SStefan Weil /* Data in mem is always in the byte order of the controller (le). 267e5e23ab8SStefan Weil * It must be dword aligned to allow direct access to 32 bit values. */ 2683a93113aSDong Xu Wang uint8_t mem[PCI_MEM_SIZE] __attribute__((aligned(8))); 269e5e23ab8SStefan Weil 270663e8e51Sths /* Configuration bytes. */ 271663e8e51Sths uint8_t configuration[22]; 272663e8e51Sths 273151b2986SJuan Quintela /* vmstate for each particular nic */ 274151b2986SJuan Quintela VMStateDescription *vmstate; 275ba42b646SStefan Weil 276ba42b646SStefan Weil /* Quasi static device properties (no need to save them). */ 277ba42b646SStefan Weil uint16_t stats_size; 278ba42b646SStefan Weil bool has_extended_tcb_support; 279663e8e51Sths } EEPRO100State; 280663e8e51Sths 2816cded3a4SStefan Weil /* Word indices in EEPROM. */ 2826cded3a4SStefan Weil typedef enum { 2836cded3a4SStefan Weil EEPROM_CNFG_MDIX = 0x03, 2846cded3a4SStefan Weil EEPROM_ID = 0x05, 2856cded3a4SStefan Weil EEPROM_PHY_ID = 0x06, 2866cded3a4SStefan Weil EEPROM_VENDOR_ID = 0x0c, 2876cded3a4SStefan Weil EEPROM_CONFIG_ASF = 0x0d, 2886cded3a4SStefan Weil EEPROM_DEVICE_ID = 0x23, 2896cded3a4SStefan Weil EEPROM_SMBUS_ADDR = 0x90, 2906cded3a4SStefan Weil } EEPROMOffset; 2916cded3a4SStefan Weil 292b1e87018SStefan Weil /* Bit values for EEPROM ID word. */ 293b1e87018SStefan Weil typedef enum { 294b1e87018SStefan Weil EEPROM_ID_MDM = BIT(0), /* Modem */ 295b1e87018SStefan Weil EEPROM_ID_STB = BIT(1), /* Standby Enable */ 296b1e87018SStefan Weil EEPROM_ID_WMR = BIT(2), /* ??? */ 297b1e87018SStefan Weil EEPROM_ID_WOL = BIT(5), /* Wake on LAN */ 298b1e87018SStefan Weil EEPROM_ID_DPD = BIT(6), /* Deep Power Down */ 299b1e87018SStefan Weil EEPROM_ID_ALT = BIT(7), /* */ 300b1e87018SStefan Weil /* BITS(10, 8) device revision */ 301b1e87018SStefan Weil EEPROM_ID_BD = BIT(11), /* boot disable */ 302b1e87018SStefan Weil EEPROM_ID_ID = BIT(13), /* id bit */ 303b1e87018SStefan Weil /* BITS(15, 14) signature */ 304b1e87018SStefan Weil EEPROM_ID_VALID = BIT(14), /* signature for valid eeprom */ 305b1e87018SStefan Weil } eeprom_id_bit; 306b1e87018SStefan Weil 307663e8e51Sths /* Default values for MDI (PHY) registers */ 308663e8e51Sths static const uint16_t eepro100_mdi_default[] = { 309663e8e51Sths /* MDI Registers 0 - 6, 7 */ 310663e8e51Sths 0x3000, 0x780d, 0x02a8, 0x0154, 0x05e1, 0x0000, 0x0000, 0x0000, 311663e8e51Sths /* MDI Registers 8 - 15 */ 312663e8e51Sths 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 313663e8e51Sths /* MDI Registers 16 - 31 */ 314663e8e51Sths 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 315663e8e51Sths 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 316663e8e51Sths }; 317663e8e51Sths 318663e8e51Sths /* Readonly mask for MDI (PHY) registers */ 319663e8e51Sths static const uint16_t eepro100_mdi_mask[] = { 320663e8e51Sths 0x0000, 0xffff, 0xffff, 0xffff, 0xc01f, 0xffff, 0xffff, 0x0000, 321663e8e51Sths 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 322663e8e51Sths 0x0fff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 323663e8e51Sths 0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 324663e8e51Sths }; 325663e8e51Sths 32640021f08SAnthony Liguori static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s); 32740021f08SAnthony Liguori 328e5e23ab8SStefan Weil /* Read a 16 bit control/status (CSR) register. */ 329e5e23ab8SStefan Weil static uint16_t e100_read_reg2(EEPRO100State *s, E100RegisterOffset addr) 330e5e23ab8SStefan Weil { 331e5e23ab8SStefan Weil assert(!((uintptr_t)&s->mem[addr] & 1)); 3324d9be252SPeter Maydell return lduw_le_p(&s->mem[addr]); 333e5e23ab8SStefan Weil } 334e5e23ab8SStefan Weil 335e5e23ab8SStefan Weil /* Read a 32 bit control/status (CSR) register. */ 336e5e23ab8SStefan Weil static uint32_t e100_read_reg4(EEPRO100State *s, E100RegisterOffset addr) 337e5e23ab8SStefan Weil { 338e5e23ab8SStefan Weil assert(!((uintptr_t)&s->mem[addr] & 3)); 3394d9be252SPeter Maydell return ldl_le_p(&s->mem[addr]); 340e5e23ab8SStefan Weil } 341e5e23ab8SStefan Weil 342e5e23ab8SStefan Weil /* Write a 16 bit control/status (CSR) register. */ 343e5e23ab8SStefan Weil static void e100_write_reg2(EEPRO100State *s, E100RegisterOffset addr, 344e5e23ab8SStefan Weil uint16_t val) 345e5e23ab8SStefan Weil { 346e5e23ab8SStefan Weil assert(!((uintptr_t)&s->mem[addr] & 1)); 3474d9be252SPeter Maydell stw_le_p(&s->mem[addr], val); 348e5e23ab8SStefan Weil } 349e5e23ab8SStefan Weil 350e5e23ab8SStefan Weil /* Read a 32 bit control/status (CSR) register. */ 351e5e23ab8SStefan Weil static void e100_write_reg4(EEPRO100State *s, E100RegisterOffset addr, 352e5e23ab8SStefan Weil uint32_t val) 353e5e23ab8SStefan Weil { 354e5e23ab8SStefan Weil assert(!((uintptr_t)&s->mem[addr] & 3)); 3554d9be252SPeter Maydell stl_le_p(&s->mem[addr], val); 356e5e23ab8SStefan Weil } 357e5e23ab8SStefan Weil 358663e8e51Sths #if defined(DEBUG_EEPRO100) 359663e8e51Sths static const char *nic_dump(const uint8_t * buf, unsigned size) 360663e8e51Sths { 361663e8e51Sths static char dump[3 * 16 + 1]; 362663e8e51Sths char *p = &dump[0]; 363aac443e6SStefan Weil if (size > 16) { 364663e8e51Sths size = 16; 365aac443e6SStefan Weil } 366663e8e51Sths while (size-- > 0) { 367663e8e51Sths p += sprintf(p, " %02x", *buf++); 368663e8e51Sths } 369663e8e51Sths return dump; 370663e8e51Sths } 371663e8e51Sths #endif /* DEBUG_EEPRO100 */ 372663e8e51Sths 373663e8e51Sths enum scb_stat_ack { 374663e8e51Sths stat_ack_not_ours = 0x00, 375663e8e51Sths stat_ack_sw_gen = 0x04, 376663e8e51Sths stat_ack_rnr = 0x10, 377663e8e51Sths stat_ack_cu_idle = 0x20, 378663e8e51Sths stat_ack_frame_rx = 0x40, 379663e8e51Sths stat_ack_cu_cmd_done = 0x80, 380663e8e51Sths stat_ack_not_present = 0xFF, 381663e8e51Sths stat_ack_rx = (stat_ack_sw_gen | stat_ack_rnr | stat_ack_frame_rx), 382663e8e51Sths stat_ack_tx = (stat_ack_cu_idle | stat_ack_cu_cmd_done), 383663e8e51Sths }; 384663e8e51Sths 385663e8e51Sths static void disable_interrupt(EEPRO100State * s) 386663e8e51Sths { 387663e8e51Sths if (s->int_stat) { 388aac443e6SStefan Weil TRACE(INT, logout("interrupt disabled\n")); 3899e64f8a3SMarcel Apfelbaum pci_irq_deassert(&s->dev); 390663e8e51Sths s->int_stat = 0; 391663e8e51Sths } 392663e8e51Sths } 393663e8e51Sths 394663e8e51Sths static void enable_interrupt(EEPRO100State * s) 395663e8e51Sths { 396663e8e51Sths if (!s->int_stat) { 397aac443e6SStefan Weil TRACE(INT, logout("interrupt enabled\n")); 3989e64f8a3SMarcel Apfelbaum pci_irq_assert(&s->dev); 399663e8e51Sths s->int_stat = 1; 400663e8e51Sths } 401663e8e51Sths } 402663e8e51Sths 403663e8e51Sths static void eepro100_acknowledge(EEPRO100State * s) 404663e8e51Sths { 405663e8e51Sths s->scb_stat &= ~s->mem[SCBAck]; 406663e8e51Sths s->mem[SCBAck] = s->scb_stat; 407663e8e51Sths if (s->scb_stat == 0) { 408663e8e51Sths disable_interrupt(s); 409663e8e51Sths } 410663e8e51Sths } 411663e8e51Sths 412e715c8e8SStefan Weil static void eepro100_interrupt(EEPRO100State * s, uint8_t status) 413663e8e51Sths { 414663e8e51Sths uint8_t mask = ~s->mem[SCBIntmask]; 415e715c8e8SStefan Weil s->mem[SCBAck] |= status; 416e715c8e8SStefan Weil status = s->scb_stat = s->mem[SCBAck]; 417e715c8e8SStefan Weil status &= (mask | 0x0f); 418e7493b25SStefan Weil #if 0 419e7493b25SStefan Weil status &= (~s->mem[SCBIntmask] | 0x0xf); 420e7493b25SStefan Weil #endif 421e715c8e8SStefan Weil if (status && (mask & 0x01)) { 422663e8e51Sths /* SCB mask and SCB Bit M do not disable interrupt. */ 423663e8e51Sths enable_interrupt(s); 424663e8e51Sths } else if (s->int_stat) { 425663e8e51Sths disable_interrupt(s); 426663e8e51Sths } 427663e8e51Sths } 428663e8e51Sths 429663e8e51Sths static void eepro100_cx_interrupt(EEPRO100State * s) 430663e8e51Sths { 431663e8e51Sths /* CU completed action command. */ 432663e8e51Sths /* Transmit not ok (82557 only, not in emulation). */ 433663e8e51Sths eepro100_interrupt(s, 0x80); 434663e8e51Sths } 435663e8e51Sths 436663e8e51Sths static void eepro100_cna_interrupt(EEPRO100State * s) 437663e8e51Sths { 438663e8e51Sths /* CU left the active state. */ 439663e8e51Sths eepro100_interrupt(s, 0x20); 440663e8e51Sths } 441663e8e51Sths 442663e8e51Sths static void eepro100_fr_interrupt(EEPRO100State * s) 443663e8e51Sths { 444663e8e51Sths /* RU received a complete frame. */ 445663e8e51Sths eepro100_interrupt(s, 0x40); 446663e8e51Sths } 447663e8e51Sths 448663e8e51Sths static void eepro100_rnr_interrupt(EEPRO100State * s) 449663e8e51Sths { 450663e8e51Sths /* RU is not ready. */ 451663e8e51Sths eepro100_interrupt(s, 0x10); 452663e8e51Sths } 453663e8e51Sths 454663e8e51Sths static void eepro100_mdi_interrupt(EEPRO100State * s) 455663e8e51Sths { 456663e8e51Sths /* MDI completed read or write cycle. */ 457663e8e51Sths eepro100_interrupt(s, 0x08); 458663e8e51Sths } 459663e8e51Sths 460663e8e51Sths static void eepro100_swi_interrupt(EEPRO100State * s) 461663e8e51Sths { 462663e8e51Sths /* Software has requested an interrupt. */ 463663e8e51Sths eepro100_interrupt(s, 0x04); 464663e8e51Sths } 465663e8e51Sths 466663e8e51Sths #if 0 467663e8e51Sths static void eepro100_fcp_interrupt(EEPRO100State * s) 468663e8e51Sths { 469663e8e51Sths /* Flow control pause interrupt (82558 and later). */ 470663e8e51Sths eepro100_interrupt(s, 0x01); 471663e8e51Sths } 472663e8e51Sths #endif 473663e8e51Sths 4749a7c2a59SMao Zhongyi static void e100_pci_reset(EEPRO100State *s, Error **errp) 475663e8e51Sths { 47640021f08SAnthony Liguori E100PCIDeviceInfo *info = eepro100_get_class(s); 477663e8e51Sths uint32_t device = s->device; 478273a2142SJuan Quintela uint8_t *pci_conf = s->dev.config; 479663e8e51Sths 480aac443e6SStefan Weil TRACE(OTHER, logout("%p\n", s)); 481663e8e51Sths 482663e8e51Sths /* PCI Status */ 483558c8634SStefan Weil pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM | 484558c8634SStefan Weil PCI_STATUS_FAST_BACK); 485663e8e51Sths /* PCI Latency Timer */ 48615e89f59SMichael S. Tsirkin pci_set_byte(pci_conf + PCI_LATENCY_TIMER, 0x20); /* latency timer = 32 clocks */ 487ae543b49SStefan Weil /* Capability Pointer is set by PCI framework. */ 488f62719caSStefan Weil /* Interrupt Line */ 489f62719caSStefan Weil /* Interrupt Pin */ 490f62719caSStefan Weil pci_set_byte(pci_conf + PCI_INTERRUPT_PIN, 1); /* interrupt pin A */ 491663e8e51Sths /* Minimum Grant */ 49215e89f59SMichael S. Tsirkin pci_set_byte(pci_conf + PCI_MIN_GNT, 0x08); 493663e8e51Sths /* Maximum Latency */ 49415e89f59SMichael S. Tsirkin pci_set_byte(pci_conf + PCI_MAX_LAT, 0x18); 495663e8e51Sths 49640021f08SAnthony Liguori s->stats_size = info->stats_size; 49740021f08SAnthony Liguori s->has_extended_tcb_support = info->has_extended_tcb_support; 498558c8634SStefan Weil 499663e8e51Sths switch (device) { 500ba42b646SStefan Weil case i82550: 501663e8e51Sths case i82551: 502ba42b646SStefan Weil case i82557A: 503663e8e51Sths case i82557B: 504663e8e51Sths case i82557C: 505ba42b646SStefan Weil case i82558A: 506663e8e51Sths case i82558B: 507ba42b646SStefan Weil case i82559A: 508ba42b646SStefan Weil case i82559B: 509558c8634SStefan Weil case i82559ER: 510558c8634SStefan Weil case i82562: 511db667a12SStefan Weil case i82801: 512663e8e51Sths case i82559C: 513ba42b646SStefan Weil break; 514663e8e51Sths default: 515663e8e51Sths logout("Device %X is undefined!\n", device); 516663e8e51Sths } 517663e8e51Sths 5183dec59a1SStefan Weil /* Standard TxCB. */ 5193dec59a1SStefan Weil s->configuration[6] |= BIT(4); 5203dec59a1SStefan Weil 521558c8634SStefan Weil /* Standard statistical counters. */ 522ba42b646SStefan Weil s->configuration[6] |= BIT(5); 523ba42b646SStefan Weil 524ba42b646SStefan Weil if (s->stats_size == 80) { 525ba42b646SStefan Weil /* TODO: check TCO Statistical Counters bit. Documentation not clear. */ 526ba42b646SStefan Weil if (s->configuration[6] & BIT(2)) { 527ba42b646SStefan Weil /* TCO statistical counters. */ 528ba42b646SStefan Weil assert(s->configuration[6] & BIT(5)); 529ba42b646SStefan Weil } else { 530ba42b646SStefan Weil if (s->configuration[6] & BIT(5)) { 531ba42b646SStefan Weil /* No extended statistical counters, i82557 compatible. */ 532ba42b646SStefan Weil s->stats_size = 64; 533ba42b646SStefan Weil } else { 534ba42b646SStefan Weil /* i82558 compatible. */ 535ba42b646SStefan Weil s->stats_size = 76; 536ba42b646SStefan Weil } 537ba42b646SStefan Weil } 538ba42b646SStefan Weil } else { 539ba42b646SStefan Weil if (s->configuration[6] & BIT(5)) { 540ba42b646SStefan Weil /* No extended statistical counters. */ 541ba42b646SStefan Weil s->stats_size = 64; 542ba42b646SStefan Weil } 543ba42b646SStefan Weil } 544ba42b646SStefan Weil assert(s->stats_size > 0 && s->stats_size <= sizeof(s->statistics)); 545ba42b646SStefan Weil 54640021f08SAnthony Liguori if (info->power_management) { 547ba42b646SStefan Weil /* Power Management Capabilities */ 5488bbd1ce2SMichael S. Tsirkin int cfg_offset = 0xdc; 549ca77089dSIsaku Yamahata int r = pci_add_capability(&s->dev, PCI_CAP_ID_PM, 5509a7c2a59SMao Zhongyi cfg_offset, PCI_PM_SIZEOF, 5519a7c2a59SMao Zhongyi errp); 5529a7c2a59SMao Zhongyi if (r < 0) { 5539a7c2a59SMao Zhongyi return; 5549a7c2a59SMao Zhongyi } 5559a7c2a59SMao Zhongyi 556ae543b49SStefan Weil pci_set_word(pci_conf + cfg_offset + PCI_PM_PMC, 0x7e21); 557ae543b49SStefan Weil #if 0 /* TODO: replace dummy code for power management emulation. */ 558ba42b646SStefan Weil /* TODO: Power Management Control / Status. */ 559ae543b49SStefan Weil pci_set_word(pci_conf + cfg_offset + PCI_PM_CTRL, 0x0000); 560ba42b646SStefan Weil /* TODO: Ethernet Power Consumption Registers (i82559 and later). */ 561ae543b49SStefan Weil pci_set_byte(pci_conf + cfg_offset + PCI_PM_PPB_EXTENSIONS, 0x0000); 562ae543b49SStefan Weil #endif 563ae543b49SStefan Weil } 564ba42b646SStefan Weil 565ba42b646SStefan Weil #if EEPROM_SIZE > 0 566663e8e51Sths if (device == i82557C || device == i82558B || device == i82559C) { 567e7493b25SStefan Weil /* 568e7493b25SStefan Weil TODO: get vendor id from EEPROM for i82557C or later. 569e7493b25SStefan Weil TODO: get device id from EEPROM for i82557C or later. 570e7493b25SStefan Weil TODO: status bit 4 can be disabled by EEPROM for i82558, i82559. 571e7493b25SStefan Weil TODO: header type is determined by EEPROM for i82559. 572e7493b25SStefan Weil TODO: get subsystem id from EEPROM for i82557C or later. 573e7493b25SStefan Weil TODO: get subsystem vendor id from EEPROM for i82557C or later. 574e7493b25SStefan Weil TODO: exp. rom baddr depends on a bit in EEPROM for i82558 or later. 575e7493b25SStefan Weil TODO: capability pointer depends on EEPROM for i82558. 576e7493b25SStefan Weil */ 577663e8e51Sths logout("Get device id and revision from EEPROM!!!\n"); 578663e8e51Sths } 579ba42b646SStefan Weil #endif /* EEPROM_SIZE > 0 */ 580663e8e51Sths } 581663e8e51Sths 582663e8e51Sths static void nic_selective_reset(EEPRO100State * s) 583663e8e51Sths { 584663e8e51Sths size_t i; 585663e8e51Sths uint16_t *eeprom_contents = eeprom93xx_data(s->eeprom); 586e7493b25SStefan Weil #if 0 587e7493b25SStefan Weil eeprom93xx_reset(s->eeprom); 588e7493b25SStefan Weil #endif 589508ef936SGerd Hoffmann memcpy(eeprom_contents, s->conf.macaddr.a, 6); 590b1e87018SStefan Weil eeprom_contents[EEPROM_ID] = EEPROM_ID_VALID; 591f4e94dfeS=?UTF-8?q?Reimar=20D=C3=B6ffinger?= if (s->device == i82557B || s->device == i82557C) 592f4e94dfeS=?UTF-8?q?Reimar=20D=C3=B6ffinger?= eeprom_contents[5] = 0x0100; 5936cded3a4SStefan Weil eeprom_contents[EEPROM_PHY_ID] = 1; 594663e8e51Sths uint16_t sum = 0; 595663e8e51Sths for (i = 0; i < EEPROM_SIZE - 1; i++) { 596663e8e51Sths sum += eeprom_contents[i]; 597663e8e51Sths } 598663e8e51Sths eeprom_contents[EEPROM_SIZE - 1] = 0xbaba - sum; 599aac443e6SStefan Weil TRACE(EEPROM, logout("checksum=0x%04x\n", eeprom_contents[EEPROM_SIZE - 1])); 600663e8e51Sths 601663e8e51Sths memset(s->mem, 0, sizeof(s->mem)); 602e5e23ab8SStefan Weil e100_write_reg4(s, SCBCtrlMDI, BIT(21)); 603663e8e51Sths 604663e8e51Sths assert(sizeof(s->mdimem) == sizeof(eepro100_mdi_default)); 605663e8e51Sths memcpy(&s->mdimem[0], &eepro100_mdi_default[0], sizeof(s->mdimem)); 606663e8e51Sths } 607663e8e51Sths 608663e8e51Sths static void nic_reset(void *opaque) 609663e8e51Sths { 610769cf7a5SJuan Quintela EEPRO100State *s = opaque; 611aac443e6SStefan Weil TRACE(OTHER, logout("%p\n", s)); 612010ec629SStefan Weil /* TODO: Clearing of hash register for selective reset, too? */ 6137b8737deSStefan Weil memset(&s->mult[0], 0, sizeof(s->mult)); 614663e8e51Sths nic_selective_reset(s); 615663e8e51Sths } 616663e8e51Sths 617663e8e51Sths #if defined(DEBUG_EEPRO100) 618b8f6ba0dSStefan Weil static const char * const e100_reg[PCI_IO_SIZE / 4] = { 619663e8e51Sths "Command/Status", 620663e8e51Sths "General Pointer", 621663e8e51Sths "Port", 622663e8e51Sths "EEPROM/Flash Control", 623663e8e51Sths "MDI Control", 624663e8e51Sths "Receive DMA Byte Count", 625b8f6ba0dSStefan Weil "Flow Control", 626663e8e51Sths "General Status/Control" 627663e8e51Sths }; 628663e8e51Sths 629663e8e51Sths static char *regname(uint32_t addr) 630663e8e51Sths { 631ec169288SDavid Benjamin static char buf[32]; 632663e8e51Sths if (addr < PCI_IO_SIZE) { 633b8f6ba0dSStefan Weil const char *r = e100_reg[addr / 4]; 634663e8e51Sths if (r != 0) { 63541cbc23cSStefan Weil snprintf(buf, sizeof(buf), "%s+%u", r, addr % 4); 636663e8e51Sths } else { 63741cbc23cSStefan Weil snprintf(buf, sizeof(buf), "0x%02x", addr); 638663e8e51Sths } 639663e8e51Sths } else { 64041cbc23cSStefan Weil snprintf(buf, sizeof(buf), "??? 0x%08x", addr); 641663e8e51Sths } 642663e8e51Sths return buf; 643663e8e51Sths } 644663e8e51Sths #endif /* DEBUG_EEPRO100 */ 645663e8e51Sths 646663e8e51Sths /***************************************************************************** 647663e8e51Sths * 648663e8e51Sths * Command emulation. 649663e8e51Sths * 650663e8e51Sths ****************************************************************************/ 651663e8e51Sths 652663e8e51Sths #if 0 653663e8e51Sths static uint16_t eepro100_read_command(EEPRO100State * s) 654663e8e51Sths { 655663e8e51Sths uint16_t val = 0xffff; 656e7493b25SStefan Weil TRACE(OTHER, logout("val=0x%04x\n", val)); 657663e8e51Sths return val; 658663e8e51Sths } 659663e8e51Sths #endif 660663e8e51Sths 661663e8e51Sths /* Commands that can be put in a command list entry. */ 662663e8e51Sths enum commands { 663663e8e51Sths CmdNOp = 0, 664663e8e51Sths CmdIASetup = 1, 665663e8e51Sths CmdConfigure = 2, 666663e8e51Sths CmdMulticastList = 3, 667663e8e51Sths CmdTx = 4, 668663e8e51Sths CmdTDR = 5, /* load microcode */ 669663e8e51Sths CmdDump = 6, 670663e8e51Sths CmdDiagnose = 7, 671663e8e51Sths 672663e8e51Sths /* And some extra flags: */ 673663e8e51Sths CmdSuspend = 0x4000, /* Suspend after completion. */ 674663e8e51Sths CmdIntr = 0x2000, /* Interrupt after completion. */ 675663e8e51Sths CmdTxFlex = 0x0008, /* Use "Flexible mode" for CmdTx command. */ 676663e8e51Sths }; 677663e8e51Sths 678c227f099SAnthony Liguori static cu_state_t get_cu_state(EEPRO100State * s) 679663e8e51Sths { 680ced5296aSStefan Weil return ((s->mem[SCBStatus] & BITS(7, 6)) >> 6); 681663e8e51Sths } 682663e8e51Sths 683c227f099SAnthony Liguori static void set_cu_state(EEPRO100State * s, cu_state_t state) 684663e8e51Sths { 685ced5296aSStefan Weil s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(7, 6)) + (state << 6); 686663e8e51Sths } 687663e8e51Sths 688c227f099SAnthony Liguori static ru_state_t get_ru_state(EEPRO100State * s) 689663e8e51Sths { 690ced5296aSStefan Weil return ((s->mem[SCBStatus] & BITS(5, 2)) >> 2); 691663e8e51Sths } 692663e8e51Sths 693c227f099SAnthony Liguori static void set_ru_state(EEPRO100State * s, ru_state_t state) 694663e8e51Sths { 695ced5296aSStefan Weil s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(5, 2)) + (state << 2); 696663e8e51Sths } 697663e8e51Sths 698663e8e51Sths static void dump_statistics(EEPRO100State * s) 699663e8e51Sths { 700663e8e51Sths /* Dump statistical data. Most data is never changed by the emulation 701663e8e51Sths * and always 0, so we first just copy the whole block and then those 702663e8e51Sths * values which really matter. 703663e8e51Sths * Number of data should check configuration!!! 704663e8e51Sths */ 705e965d4bcSDavid Gibson pci_dma_write(&s->dev, s->statsaddr, &s->statistics, s->stats_size); 70616ef60c9SEduard - Gabriel Munteanu stl_le_pci_dma(&s->dev, s->statsaddr + 0, 70716ef60c9SEduard - Gabriel Munteanu s->statistics.tx_good_frames); 70816ef60c9SEduard - Gabriel Munteanu stl_le_pci_dma(&s->dev, s->statsaddr + 36, 70916ef60c9SEduard - Gabriel Munteanu s->statistics.rx_good_frames); 71016ef60c9SEduard - Gabriel Munteanu stl_le_pci_dma(&s->dev, s->statsaddr + 48, 71116ef60c9SEduard - Gabriel Munteanu s->statistics.rx_resource_errors); 71216ef60c9SEduard - Gabriel Munteanu stl_le_pci_dma(&s->dev, s->statsaddr + 60, 71316ef60c9SEduard - Gabriel Munteanu s->statistics.rx_short_frame_errors); 714e7493b25SStefan Weil #if 0 71516ef60c9SEduard - Gabriel Munteanu stw_le_pci_dma(&s->dev, s->statsaddr + 76, s->statistics.xmt_tco_frames); 71616ef60c9SEduard - Gabriel Munteanu stw_le_pci_dma(&s->dev, s->statsaddr + 78, s->statistics.rcv_tco_frames); 717e7493b25SStefan Weil missing("CU dump statistical counters"); 718e7493b25SStefan Weil #endif 719663e8e51Sths } 720663e8e51Sths 7213d0f4b9bSStefan Weil static void read_cb(EEPRO100State *s) 7223d0f4b9bSStefan Weil { 723e965d4bcSDavid Gibson pci_dma_read(&s->dev, s->cb_address, &s->tx, sizeof(s->tx)); 7243d0f4b9bSStefan Weil s->tx.status = le16_to_cpu(s->tx.status); 7253d0f4b9bSStefan Weil s->tx.command = le16_to_cpu(s->tx.command); 7263d0f4b9bSStefan Weil s->tx.link = le32_to_cpu(s->tx.link); 7273d0f4b9bSStefan Weil s->tx.tbd_array_addr = le32_to_cpu(s->tx.tbd_array_addr); 7283d0f4b9bSStefan Weil s->tx.tcb_bytes = le16_to_cpu(s->tx.tcb_bytes); 7293d0f4b9bSStefan Weil } 7303d0f4b9bSStefan Weil 731f3a52e50SStefan Weil static void tx_command(EEPRO100State *s) 732663e8e51Sths { 7338f8e8053SThomas Huth uint32_t tbd_array = s->tx.tbd_array_addr; 7348f8e8053SThomas Huth uint16_t tcb_bytes = s->tx.tcb_bytes & 0x3fff; 735f3a52e50SStefan Weil /* Sends larger than MAX_ETH_FRAME_SIZE are allowed, up to 2600 bytes. */ 736f3a52e50SStefan Weil uint8_t buf[2600]; 737f3a52e50SStefan Weil uint16_t size = 0; 738f3a52e50SStefan Weil uint32_t tbd_address = s->cb_address + 0x10; 739aac443e6SStefan Weil TRACE(RXTX, logout 740663e8e51Sths ("transmit, TBD array address 0x%08x, TCB byte count 0x%04x, TBD count %u\n", 741f3a52e50SStefan Weil tbd_array, tcb_bytes, s->tx.tbd_count)); 7427f1e9d4eSKevin Wolf 7437f1e9d4eSKevin Wolf if (tcb_bytes > 2600) { 7447f1e9d4eSKevin Wolf logout("TCB byte count too large, using 2600\n"); 7457f1e9d4eSKevin Wolf tcb_bytes = 2600; 7467f1e9d4eSKevin Wolf } 747663e8e51Sths if (!((tcb_bytes > 0) || (tbd_array != 0xffffffff))) { 748663e8e51Sths logout 749663e8e51Sths ("illegal values of TBD array address and TCB byte count!\n"); 750663e8e51Sths } 751663e8e51Sths assert(tcb_bytes <= sizeof(buf)); 752663e8e51Sths while (size < tcb_bytes) { 753aac443e6SStefan Weil TRACE(RXTX, logout 754663e8e51Sths ("TBD (simplified mode): buffer address 0x%08x, size 0x%04x\n", 7551865e288SMike Nawrocki tbd_address, tcb_bytes)); 7561865e288SMike Nawrocki pci_dma_read(&s->dev, tbd_address, &buf[size], tcb_bytes); 7571865e288SMike Nawrocki size += tcb_bytes; 758663e8e51Sths } 759663e8e51Sths if (tbd_array == 0xffffffff) { 760663e8e51Sths /* Simplified mode. Was already handled by code above. */ 761663e8e51Sths } else { 762663e8e51Sths /* Flexible mode. */ 763663e8e51Sths uint8_t tbd_count = 0; 764ba42b646SStefan Weil if (s->has_extended_tcb_support && !(s->configuration[6] & BIT(4))) { 7653f9cb1c1SNaphtali Sprei /* Extended Flexible TCB. */ 766663e8e51Sths for (; tbd_count < 2; tbd_count++) { 76716ef60c9SEduard - Gabriel Munteanu uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev, 76816ef60c9SEduard - Gabriel Munteanu tbd_address); 76916ef60c9SEduard - Gabriel Munteanu uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev, 77016ef60c9SEduard - Gabriel Munteanu tbd_address + 4); 77116ef60c9SEduard - Gabriel Munteanu uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev, 77216ef60c9SEduard - Gabriel Munteanu tbd_address + 6); 773663e8e51Sths tbd_address += 8; 774aac443e6SStefan Weil TRACE(RXTX, logout 7753f9cb1c1SNaphtali Sprei ("TBD (extended flexible mode): buffer address 0x%08x, size 0x%04x\n", 776aac443e6SStefan Weil tx_buffer_address, tx_buffer_size)); 77724e6f355SReimar Döffinger tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size); 77816ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, tx_buffer_address, 77916ef60c9SEduard - Gabriel Munteanu &buf[size], tx_buffer_size); 780663e8e51Sths size += tx_buffer_size; 781663e8e51Sths if (tx_buffer_el & 1) { 782663e8e51Sths break; 783663e8e51Sths } 784663e8e51Sths } 785663e8e51Sths } 786663e8e51Sths tbd_address = tbd_array; 787f3a52e50SStefan Weil for (; tbd_count < s->tx.tbd_count; tbd_count++) { 78816ef60c9SEduard - Gabriel Munteanu uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev, tbd_address); 78916ef60c9SEduard - Gabriel Munteanu uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev, tbd_address + 4); 79016ef60c9SEduard - Gabriel Munteanu uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev, tbd_address + 6); 791663e8e51Sths tbd_address += 8; 792aac443e6SStefan Weil TRACE(RXTX, logout 793663e8e51Sths ("TBD (flexible mode): buffer address 0x%08x, size 0x%04x\n", 794aac443e6SStefan Weil tx_buffer_address, tx_buffer_size)); 79524e6f355SReimar Döffinger tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size); 79616ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, tx_buffer_address, 79716ef60c9SEduard - Gabriel Munteanu &buf[size], tx_buffer_size); 798663e8e51Sths size += tx_buffer_size; 799663e8e51Sths if (tx_buffer_el & 1) { 800663e8e51Sths break; 801663e8e51Sths } 802663e8e51Sths } 803663e8e51Sths } 804aac443e6SStefan Weil TRACE(RXTX, logout("%p sending frame, len=%d,%s\n", s, size, nic_dump(buf, size))); 805b356f76dSJason Wang qemu_send_packet(qemu_get_queue(s->nic), buf, size); 806663e8e51Sths s->statistics.tx_good_frames++; 807663e8e51Sths /* Transmit with bad status would raise an CX/TNO interrupt. 808663e8e51Sths * (82557 only). Emulation never has bad status. */ 809e7493b25SStefan Weil #if 0 810e7493b25SStefan Weil eepro100_cx_interrupt(s); 811e7493b25SStefan Weil #endif 812f3a52e50SStefan Weil } 813f3a52e50SStefan Weil 8147b8737deSStefan Weil static void set_multicast_list(EEPRO100State *s) 8157b8737deSStefan Weil { 8167b8737deSStefan Weil uint16_t multicast_count = s->tx.tbd_array_addr & BITS(13, 0); 8177b8737deSStefan Weil uint16_t i; 8187b8737deSStefan Weil memset(&s->mult[0], 0, sizeof(s->mult)); 8197b8737deSStefan Weil TRACE(OTHER, logout("multicast list, multicast count = %u\n", multicast_count)); 8207b8737deSStefan Weil for (i = 0; i < multicast_count; i += 6) { 8217b8737deSStefan Weil uint8_t multicast_addr[6]; 82216ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, s->cb_address + 10 + i, multicast_addr, 6); 8237b8737deSStefan Weil TRACE(OTHER, logout("multicast entry %s\n", nic_dump(multicast_addr, 6))); 8247c0348bdSMark Cave-Ayland unsigned mcast_idx = (net_crc32(multicast_addr, ETH_ALEN) & 8257c0348bdSMark Cave-Ayland BITS(7, 2)) >> 2; 8267b8737deSStefan Weil assert(mcast_idx < 64); 8277b8737deSStefan Weil s->mult[mcast_idx >> 3] |= (1 << (mcast_idx & 7)); 8287b8737deSStefan Weil } 8297b8737deSStefan Weil } 8307b8737deSStefan Weil 831f3a52e50SStefan Weil static void action_command(EEPRO100State *s) 832f3a52e50SStefan Weil { 83300837731SStefan Weil /* The loop below won't stop if it gets special handcrafted data. 83400837731SStefan Weil Therefore we limit the number of iterations. */ 83500837731SStefan Weil unsigned max_loop_count = 16; 83600837731SStefan Weil 837f3a52e50SStefan Weil for (;;) { 8383d0f4b9bSStefan Weil bool bit_el; 8393d0f4b9bSStefan Weil bool bit_s; 8403d0f4b9bSStefan Weil bool bit_i; 8413d0f4b9bSStefan Weil bool bit_nc; 84275f5a6ccSStefan Weil uint16_t ok_status = STATUS_OK; 8433d0f4b9bSStefan Weil s->cb_address = s->cu_base + s->cu_offset; 8443d0f4b9bSStefan Weil read_cb(s); 8453d0f4b9bSStefan Weil bit_el = ((s->tx.command & COMMAND_EL) != 0); 8463d0f4b9bSStefan Weil bit_s = ((s->tx.command & COMMAND_S) != 0); 8473d0f4b9bSStefan Weil bit_i = ((s->tx.command & COMMAND_I) != 0); 8483d0f4b9bSStefan Weil bit_nc = ((s->tx.command & COMMAND_NC) != 0); 8493d0f4b9bSStefan Weil #if 0 8503d0f4b9bSStefan Weil bool bit_sf = ((s->tx.command & COMMAND_SF) != 0); 8513d0f4b9bSStefan Weil #endif 85200837731SStefan Weil 85300837731SStefan Weil if (max_loop_count-- == 0) { 85400837731SStefan Weil /* Prevent an endless loop. */ 85500837731SStefan Weil logout("loop in %s:%u\n", __FILE__, __LINE__); 85600837731SStefan Weil break; 85700837731SStefan Weil } 85800837731SStefan Weil 8593d0f4b9bSStefan Weil s->cu_offset = s->tx.link; 8603d0f4b9bSStefan Weil TRACE(OTHER, 8613d0f4b9bSStefan Weil logout("val=(cu start), status=0x%04x, command=0x%04x, link=0x%08x\n", 8623d0f4b9bSStefan Weil s->tx.status, s->tx.command, s->tx.link)); 8633d0f4b9bSStefan Weil switch (s->tx.command & COMMAND_CMD) { 864f3a52e50SStefan Weil case CmdNOp: 865f3a52e50SStefan Weil /* Do nothing. */ 866f3a52e50SStefan Weil break; 867f3a52e50SStefan Weil case CmdIASetup: 86816ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, s->cb_address + 8, &s->conf.macaddr.a[0], 6); 869ce0e58b3SStefan Weil TRACE(OTHER, logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6))); 870f3a52e50SStefan Weil break; 871f3a52e50SStefan Weil case CmdConfigure: 87216ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, s->cb_address + 8, 87316ef60c9SEduard - Gabriel Munteanu &s->configuration[0], sizeof(s->configuration)); 874010ec629SStefan Weil TRACE(OTHER, logout("configuration: %s\n", 875010ec629SStefan Weil nic_dump(&s->configuration[0], 16))); 876010ec629SStefan Weil TRACE(OTHER, logout("configuration: %s\n", 877010ec629SStefan Weil nic_dump(&s->configuration[16], 878010ec629SStefan Weil ARRAY_SIZE(s->configuration) - 16))); 879010ec629SStefan Weil if (s->configuration[20] & BIT(6)) { 880010ec629SStefan Weil TRACE(OTHER, logout("Multiple IA bit\n")); 881010ec629SStefan Weil } 882f3a52e50SStefan Weil break; 883f3a52e50SStefan Weil case CmdMulticastList: 8847b8737deSStefan Weil set_multicast_list(s); 885f3a52e50SStefan Weil break; 886f3a52e50SStefan Weil case CmdTx: 887f3a52e50SStefan Weil if (bit_nc) { 888f3a52e50SStefan Weil missing("CmdTx: NC = 0"); 88975f5a6ccSStefan Weil ok_status = 0; 890f3a52e50SStefan Weil break; 891f3a52e50SStefan Weil } 892f3a52e50SStefan Weil tx_command(s); 893663e8e51Sths break; 894663e8e51Sths case CmdTDR: 895aac443e6SStefan Weil TRACE(OTHER, logout("load microcode\n")); 896663e8e51Sths /* Starting with offset 8, the command contains 897663e8e51Sths * 64 dwords microcode which we just ignore here. */ 898663e8e51Sths break; 899f80a7fc3SStefan Weil case CmdDiagnose: 900f80a7fc3SStefan Weil TRACE(OTHER, logout("diagnose\n")); 901f80a7fc3SStefan Weil /* Make sure error flag is not set. */ 902f80a7fc3SStefan Weil s->tx.status = 0; 903f80a7fc3SStefan Weil break; 904663e8e51Sths default: 905663e8e51Sths missing("undefined command"); 90675f5a6ccSStefan Weil ok_status = 0; 9077f1e9d4eSKevin Wolf break; 908663e8e51Sths } 9097f1e9d4eSKevin Wolf /* Write new status. */ 91016ef60c9SEduard - Gabriel Munteanu stw_le_pci_dma(&s->dev, s->cb_address, 91116ef60c9SEduard - Gabriel Munteanu s->tx.status | ok_status | STATUS_C); 912663e8e51Sths if (bit_i) { 913663e8e51Sths /* CU completed action. */ 914663e8e51Sths eepro100_cx_interrupt(s); 915663e8e51Sths } 916663e8e51Sths if (bit_el) { 917aac443e6SStefan Weil /* CU becomes idle. Terminate command loop. */ 918663e8e51Sths set_cu_state(s, cu_idle); 919663e8e51Sths eepro100_cna_interrupt(s); 9205fa9a0aeSStefan Weil break; 921663e8e51Sths } else if (bit_s) { 9225fa9a0aeSStefan Weil /* CU becomes suspended. Terminate command loop. */ 923663e8e51Sths set_cu_state(s, cu_suspended); 924663e8e51Sths eepro100_cna_interrupt(s); 9255fa9a0aeSStefan Weil break; 926663e8e51Sths } else { 927663e8e51Sths /* More entries in list. */ 928aac443e6SStefan Weil TRACE(OTHER, logout("CU list with at least one more entry\n")); 9295fa9a0aeSStefan Weil } 930663e8e51Sths } 931aac443e6SStefan Weil TRACE(OTHER, logout("CU list empty\n")); 932663e8e51Sths /* List is empty. Now CU is idle or suspended. */ 9335fa9a0aeSStefan Weil } 9345fa9a0aeSStefan Weil 9355fa9a0aeSStefan Weil static void eepro100_cu_command(EEPRO100State * s, uint8_t val) 9365fa9a0aeSStefan Weil { 937cb25a3fbSStefan Weil cu_state_t cu_state; 9385fa9a0aeSStefan Weil switch (val) { 9395fa9a0aeSStefan Weil case CU_NOP: 9405fa9a0aeSStefan Weil /* No operation. */ 9415fa9a0aeSStefan Weil break; 9425fa9a0aeSStefan Weil case CU_START: 943cb25a3fbSStefan Weil cu_state = get_cu_state(s); 944cb25a3fbSStefan Weil if (cu_state != cu_idle && cu_state != cu_suspended) { 945cb25a3fbSStefan Weil /* Intel documentation says that CU must be idle or suspended 946cb25a3fbSStefan Weil * for the CU start command. */ 947cb25a3fbSStefan Weil logout("unexpected CU state is %u\n", cu_state); 9485fa9a0aeSStefan Weil } 9495fa9a0aeSStefan Weil set_cu_state(s, cu_active); 95027a05006SStefan Weil s->cu_offset = e100_read_reg4(s, SCBPointer); 9515fa9a0aeSStefan Weil action_command(s); 952663e8e51Sths break; 953663e8e51Sths case CU_RESUME: 954663e8e51Sths if (get_cu_state(s) != cu_suspended) { 955663e8e51Sths logout("bad CU resume from CU state %u\n", get_cu_state(s)); 956663e8e51Sths /* Workaround for bad Linux eepro100 driver which resumes 957663e8e51Sths * from idle state. */ 958e7493b25SStefan Weil #if 0 959e7493b25SStefan Weil missing("cu resume"); 960e7493b25SStefan Weil #endif 961663e8e51Sths set_cu_state(s, cu_suspended); 962663e8e51Sths } 963663e8e51Sths if (get_cu_state(s) == cu_suspended) { 964aac443e6SStefan Weil TRACE(OTHER, logout("CU resuming\n")); 965663e8e51Sths set_cu_state(s, cu_active); 9665fa9a0aeSStefan Weil action_command(s); 967663e8e51Sths } 968663e8e51Sths break; 969663e8e51Sths case CU_STATSADDR: 970663e8e51Sths /* Load dump counters address. */ 97127a05006SStefan Weil s->statsaddr = e100_read_reg4(s, SCBPointer); 972c16ada98SStefan Weil TRACE(OTHER, logout("val=0x%02x (dump counters address)\n", val)); 973c16ada98SStefan Weil if (s->statsaddr & 3) { 974c16ada98SStefan Weil /* Memory must be Dword aligned. */ 975c16ada98SStefan Weil logout("unaligned dump counters address\n"); 976c16ada98SStefan Weil /* Handling of misaligned addresses is undefined. 977c16ada98SStefan Weil * Here we align the address by ignoring the lower bits. */ 978c16ada98SStefan Weil /* TODO: Test unaligned dump counter address on real hardware. */ 979c16ada98SStefan Weil s->statsaddr &= ~3; 980c16ada98SStefan Weil } 981663e8e51Sths break; 982663e8e51Sths case CU_SHOWSTATS: 983663e8e51Sths /* Dump statistical counters. */ 984aac443e6SStefan Weil TRACE(OTHER, logout("val=0x%02x (dump stats)\n", val)); 985663e8e51Sths dump_statistics(s); 98616ef60c9SEduard - Gabriel Munteanu stl_le_pci_dma(&s->dev, s->statsaddr + s->stats_size, 0xa005); 987663e8e51Sths break; 988663e8e51Sths case CU_CMD_BASE: 989663e8e51Sths /* Load CU base. */ 990aac443e6SStefan Weil TRACE(OTHER, logout("val=0x%02x (CU base address)\n", val)); 99127a05006SStefan Weil s->cu_base = e100_read_reg4(s, SCBPointer); 992663e8e51Sths break; 993663e8e51Sths case CU_DUMPSTATS: 994663e8e51Sths /* Dump and reset statistical counters. */ 995aac443e6SStefan Weil TRACE(OTHER, logout("val=0x%02x (dump stats and reset)\n", val)); 996663e8e51Sths dump_statistics(s); 99716ef60c9SEduard - Gabriel Munteanu stl_le_pci_dma(&s->dev, s->statsaddr + s->stats_size, 0xa007); 998663e8e51Sths memset(&s->statistics, 0, sizeof(s->statistics)); 999663e8e51Sths break; 1000663e8e51Sths case CU_SRESUME: 1001663e8e51Sths /* CU static resume. */ 1002663e8e51Sths missing("CU static resume"); 1003663e8e51Sths break; 1004663e8e51Sths default: 1005663e8e51Sths missing("Undefined CU command"); 1006663e8e51Sths } 1007663e8e51Sths } 1008663e8e51Sths 1009663e8e51Sths static void eepro100_ru_command(EEPRO100State * s, uint8_t val) 1010663e8e51Sths { 1011663e8e51Sths switch (val) { 1012663e8e51Sths case RU_NOP: 1013663e8e51Sths /* No operation. */ 1014663e8e51Sths break; 1015663e8e51Sths case RX_START: 1016663e8e51Sths /* RU start. */ 1017663e8e51Sths if (get_ru_state(s) != ru_idle) { 1018663e8e51Sths logout("RU state is %u, should be %u\n", get_ru_state(s), ru_idle); 1019e7493b25SStefan Weil #if 0 1020e7493b25SStefan Weil assert(!"wrong RU state"); 1021e7493b25SStefan Weil #endif 1022663e8e51Sths } 1023663e8e51Sths set_ru_state(s, ru_ready); 102427a05006SStefan Weil s->ru_offset = e100_read_reg4(s, SCBPointer); 1025b356f76dSJason Wang qemu_flush_queued_packets(qemu_get_queue(s->nic)); 1026aac443e6SStefan Weil TRACE(OTHER, logout("val=0x%02x (rx start)\n", val)); 1027663e8e51Sths break; 1028663e8e51Sths case RX_RESUME: 1029663e8e51Sths /* Restart RU. */ 1030663e8e51Sths if (get_ru_state(s) != ru_suspended) { 1031663e8e51Sths logout("RU state is %u, should be %u\n", get_ru_state(s), 1032663e8e51Sths ru_suspended); 1033e7493b25SStefan Weil #if 0 1034e7493b25SStefan Weil assert(!"wrong RU state"); 1035e7493b25SStefan Weil #endif 1036663e8e51Sths } 1037663e8e51Sths set_ru_state(s, ru_ready); 1038663e8e51Sths break; 1039e824012bSStefan Weil case RU_ABORT: 1040e824012bSStefan Weil /* RU abort. */ 1041e824012bSStefan Weil if (get_ru_state(s) == ru_ready) { 1042e824012bSStefan Weil eepro100_rnr_interrupt(s); 1043e824012bSStefan Weil } 1044e824012bSStefan Weil set_ru_state(s, ru_idle); 1045e824012bSStefan Weil break; 1046663e8e51Sths case RX_ADDR_LOAD: 1047663e8e51Sths /* Load RU base. */ 1048aac443e6SStefan Weil TRACE(OTHER, logout("val=0x%02x (RU base address)\n", val)); 104927a05006SStefan Weil s->ru_base = e100_read_reg4(s, SCBPointer); 1050663e8e51Sths break; 1051663e8e51Sths default: 1052663e8e51Sths logout("val=0x%02x (undefined RU command)\n", val); 1053663e8e51Sths missing("Undefined SU command"); 1054663e8e51Sths } 1055663e8e51Sths } 1056663e8e51Sths 1057663e8e51Sths static void eepro100_write_command(EEPRO100State * s, uint8_t val) 1058663e8e51Sths { 1059663e8e51Sths eepro100_ru_command(s, val & 0x0f); 1060663e8e51Sths eepro100_cu_command(s, val & 0xf0); 1061663e8e51Sths if ((val) == 0) { 1062aac443e6SStefan Weil TRACE(OTHER, logout("val=0x%02x\n", val)); 1063663e8e51Sths } 1064663e8e51Sths /* Clear command byte after command was accepted. */ 1065663e8e51Sths s->mem[SCBCmd] = 0; 1066663e8e51Sths } 1067663e8e51Sths 1068663e8e51Sths /***************************************************************************** 1069663e8e51Sths * 1070663e8e51Sths * EEPROM emulation. 1071663e8e51Sths * 1072663e8e51Sths ****************************************************************************/ 1073663e8e51Sths 1074663e8e51Sths #define EEPROM_CS 0x02 1075663e8e51Sths #define EEPROM_SK 0x01 1076663e8e51Sths #define EEPROM_DI 0x04 1077663e8e51Sths #define EEPROM_DO 0x08 1078663e8e51Sths 1079663e8e51Sths static uint16_t eepro100_read_eeprom(EEPRO100State * s) 1080663e8e51Sths { 1081e5e23ab8SStefan Weil uint16_t val = e100_read_reg2(s, SCBeeprom); 1082663e8e51Sths if (eeprom93xx_read(s->eeprom)) { 1083663e8e51Sths val |= EEPROM_DO; 1084663e8e51Sths } else { 1085663e8e51Sths val &= ~EEPROM_DO; 1086663e8e51Sths } 1087aac443e6SStefan Weil TRACE(EEPROM, logout("val=0x%04x\n", val)); 1088663e8e51Sths return val; 1089663e8e51Sths } 1090663e8e51Sths 1091c227f099SAnthony Liguori static void eepro100_write_eeprom(eeprom_t * eeprom, uint8_t val) 1092663e8e51Sths { 1093aac443e6SStefan Weil TRACE(EEPROM, logout("val=0x%02x\n", val)); 1094663e8e51Sths 1095ebabb67aSStefan Weil /* mask unwritable bits */ 1096e7493b25SStefan Weil #if 0 1097e7493b25SStefan Weil val = SET_MASKED(val, 0x31, eeprom->value); 1098e7493b25SStefan Weil #endif 1099663e8e51Sths 1100663e8e51Sths int eecs = ((val & EEPROM_CS) != 0); 1101663e8e51Sths int eesk = ((val & EEPROM_SK) != 0); 1102663e8e51Sths int eedi = ((val & EEPROM_DI) != 0); 1103663e8e51Sths eeprom93xx_write(eeprom, eecs, eesk, eedi); 1104663e8e51Sths } 1105663e8e51Sths 1106663e8e51Sths /***************************************************************************** 1107663e8e51Sths * 1108663e8e51Sths * MDI emulation. 1109663e8e51Sths * 1110663e8e51Sths ****************************************************************************/ 1111663e8e51Sths 1112663e8e51Sths #if defined(DEBUG_EEPRO100) 11136a0b9cc9SReimar Döffinger static const char * const mdi_op_name[] = { 1114663e8e51Sths "opcode 0", 1115663e8e51Sths "write", 1116663e8e51Sths "read", 1117663e8e51Sths "opcode 3" 1118663e8e51Sths }; 1119663e8e51Sths 11206a0b9cc9SReimar Döffinger static const char * const mdi_reg_name[] = { 1121663e8e51Sths "Control", 1122663e8e51Sths "Status", 1123663e8e51Sths "PHY Identification (Word 1)", 1124663e8e51Sths "PHY Identification (Word 2)", 1125663e8e51Sths "Auto-Negotiation Advertisement", 1126663e8e51Sths "Auto-Negotiation Link Partner Ability", 1127663e8e51Sths "Auto-Negotiation Expansion" 1128663e8e51Sths }; 1129aac443e6SStefan Weil 1130aac443e6SStefan Weil static const char *reg2name(uint8_t reg) 1131aac443e6SStefan Weil { 1132aac443e6SStefan Weil static char buffer[10]; 1133aac443e6SStefan Weil const char *p = buffer; 1134aac443e6SStefan Weil if (reg < ARRAY_SIZE(mdi_reg_name)) { 1135aac443e6SStefan Weil p = mdi_reg_name[reg]; 1136aac443e6SStefan Weil } else { 1137aac443e6SStefan Weil snprintf(buffer, sizeof(buffer), "reg=0x%02x", reg); 1138aac443e6SStefan Weil } 1139aac443e6SStefan Weil return p; 1140aac443e6SStefan Weil } 1141663e8e51Sths #endif /* DEBUG_EEPRO100 */ 1142663e8e51Sths 1143663e8e51Sths static uint32_t eepro100_read_mdi(EEPRO100State * s) 1144663e8e51Sths { 1145e5e23ab8SStefan Weil uint32_t val = e100_read_reg4(s, SCBCtrlMDI); 1146663e8e51Sths 1147663e8e51Sths #ifdef DEBUG_EEPRO100 1148663e8e51Sths uint8_t raiseint = (val & BIT(29)) >> 29; 1149663e8e51Sths uint8_t opcode = (val & BITS(27, 26)) >> 26; 1150663e8e51Sths uint8_t phy = (val & BITS(25, 21)) >> 21; 1151663e8e51Sths uint8_t reg = (val & BITS(20, 16)) >> 16; 1152663e8e51Sths uint16_t data = (val & BITS(15, 0)); 1153663e8e51Sths #endif 1154663e8e51Sths /* Emulation takes no time to finish MDI transaction. */ 1155663e8e51Sths val |= BIT(28); 1156663e8e51Sths TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n", 1157663e8e51Sths val, raiseint, mdi_op_name[opcode], phy, 1158aac443e6SStefan Weil reg2name(reg), data)); 1159663e8e51Sths return val; 1160663e8e51Sths } 1161663e8e51Sths 11620113f48dSStefan Weil static void eepro100_write_mdi(EEPRO100State *s) 1163663e8e51Sths { 11640113f48dSStefan Weil uint32_t val = e100_read_reg4(s, SCBCtrlMDI); 1165663e8e51Sths uint8_t raiseint = (val & BIT(29)) >> 29; 1166663e8e51Sths uint8_t opcode = (val & BITS(27, 26)) >> 26; 1167663e8e51Sths uint8_t phy = (val & BITS(25, 21)) >> 21; 1168663e8e51Sths uint8_t reg = (val & BITS(20, 16)) >> 16; 1169663e8e51Sths uint16_t data = (val & BITS(15, 0)); 1170aac443e6SStefan Weil TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n", 1171aac443e6SStefan Weil val, raiseint, mdi_op_name[opcode], phy, reg2name(reg), data)); 1172663e8e51Sths if (phy != 1) { 1173663e8e51Sths /* Unsupported PHY address. */ 1174e7493b25SStefan Weil #if 0 1175e7493b25SStefan Weil logout("phy must be 1 but is %u\n", phy); 1176e7493b25SStefan Weil #endif 1177663e8e51Sths data = 0; 1178663e8e51Sths } else if (opcode != 1 && opcode != 2) { 1179663e8e51Sths /* Unsupported opcode. */ 1180663e8e51Sths logout("opcode must be 1 or 2 but is %u\n", opcode); 1181663e8e51Sths data = 0; 1182663e8e51Sths } else if (reg > 6) { 1183663e8e51Sths /* Unsupported register. */ 1184663e8e51Sths logout("register must be 0...6 but is %u\n", reg); 1185663e8e51Sths data = 0; 1186663e8e51Sths } else { 1187663e8e51Sths TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n", 1188663e8e51Sths val, raiseint, mdi_op_name[opcode], phy, 1189aac443e6SStefan Weil reg2name(reg), data)); 1190663e8e51Sths if (opcode == 1) { 1191663e8e51Sths /* MDI write */ 1192663e8e51Sths switch (reg) { 1193663e8e51Sths case 0: /* Control Register */ 1194663e8e51Sths if (data & 0x8000) { 1195663e8e51Sths /* Reset status and control registers to default. */ 1196663e8e51Sths s->mdimem[0] = eepro100_mdi_default[0]; 1197663e8e51Sths s->mdimem[1] = eepro100_mdi_default[1]; 1198663e8e51Sths data = s->mdimem[reg]; 1199663e8e51Sths } else { 1200663e8e51Sths /* Restart Auto Configuration = Normal Operation */ 1201663e8e51Sths data &= ~0x0200; 1202663e8e51Sths } 1203663e8e51Sths break; 1204663e8e51Sths case 1: /* Status Register */ 1205663e8e51Sths missing("not writable"); 1206663e8e51Sths break; 1207663e8e51Sths case 2: /* PHY Identification Register (Word 1) */ 1208663e8e51Sths case 3: /* PHY Identification Register (Word 2) */ 1209663e8e51Sths missing("not implemented"); 1210663e8e51Sths break; 1211663e8e51Sths case 4: /* Auto-Negotiation Advertisement Register */ 1212663e8e51Sths case 5: /* Auto-Negotiation Link Partner Ability Register */ 1213663e8e51Sths break; 1214663e8e51Sths case 6: /* Auto-Negotiation Expansion Register */ 1215663e8e51Sths default: 1216663e8e51Sths missing("not implemented"); 1217663e8e51Sths } 12185e80dd22SPeter Maydell s->mdimem[reg] &= eepro100_mdi_mask[reg]; 12195e80dd22SPeter Maydell s->mdimem[reg] |= data & ~eepro100_mdi_mask[reg]; 1220663e8e51Sths } else if (opcode == 2) { 1221663e8e51Sths /* MDI read */ 1222663e8e51Sths switch (reg) { 1223663e8e51Sths case 0: /* Control Register */ 1224663e8e51Sths if (data & 0x8000) { 1225663e8e51Sths /* Reset status and control registers to default. */ 1226663e8e51Sths s->mdimem[0] = eepro100_mdi_default[0]; 1227663e8e51Sths s->mdimem[1] = eepro100_mdi_default[1]; 1228663e8e51Sths } 1229663e8e51Sths break; 1230663e8e51Sths case 1: /* Status Register */ 1231663e8e51Sths s->mdimem[reg] |= 0x0020; 1232663e8e51Sths break; 1233663e8e51Sths case 2: /* PHY Identification Register (Word 1) */ 1234663e8e51Sths case 3: /* PHY Identification Register (Word 2) */ 1235663e8e51Sths case 4: /* Auto-Negotiation Advertisement Register */ 1236663e8e51Sths break; 1237663e8e51Sths case 5: /* Auto-Negotiation Link Partner Ability Register */ 1238663e8e51Sths s->mdimem[reg] = 0x41fe; 1239663e8e51Sths break; 1240663e8e51Sths case 6: /* Auto-Negotiation Expansion Register */ 1241663e8e51Sths s->mdimem[reg] = 0x0001; 1242663e8e51Sths break; 1243663e8e51Sths } 1244663e8e51Sths data = s->mdimem[reg]; 1245663e8e51Sths } 1246663e8e51Sths /* Emulation takes no time to finish MDI transaction. 1247663e8e51Sths * Set MDI bit in SCB status register. */ 1248663e8e51Sths s->mem[SCBAck] |= 0x08; 1249663e8e51Sths val |= BIT(28); 1250663e8e51Sths if (raiseint) { 1251663e8e51Sths eepro100_mdi_interrupt(s); 1252663e8e51Sths } 1253663e8e51Sths } 1254663e8e51Sths val = (val & 0xffff0000) + data; 1255e5e23ab8SStefan Weil e100_write_reg4(s, SCBCtrlMDI, val); 1256663e8e51Sths } 1257663e8e51Sths 1258663e8e51Sths /***************************************************************************** 1259663e8e51Sths * 1260663e8e51Sths * Port emulation. 1261663e8e51Sths * 1262663e8e51Sths ****************************************************************************/ 1263663e8e51Sths 1264663e8e51Sths #define PORT_SOFTWARE_RESET 0 1265663e8e51Sths #define PORT_SELFTEST 1 1266663e8e51Sths #define PORT_SELECTIVE_RESET 2 1267663e8e51Sths #define PORT_DUMP 3 1268663e8e51Sths #define PORT_SELECTION_MASK 3 1269663e8e51Sths 1270663e8e51Sths typedef struct { 1271663e8e51Sths uint32_t st_sign; /* Self Test Signature */ 1272663e8e51Sths uint32_t st_result; /* Self Test Results */ 1273c227f099SAnthony Liguori } eepro100_selftest_t; 1274663e8e51Sths 1275663e8e51Sths static uint32_t eepro100_read_port(EEPRO100State * s) 1276663e8e51Sths { 1277663e8e51Sths return 0; 1278663e8e51Sths } 1279663e8e51Sths 12803fd3d0b4SStefan Weil static void eepro100_write_port(EEPRO100State *s) 1281663e8e51Sths { 12823fd3d0b4SStefan Weil uint32_t val = e100_read_reg4(s, SCBPort); 1283663e8e51Sths uint32_t address = (val & ~PORT_SELECTION_MASK); 1284663e8e51Sths uint8_t selection = (val & PORT_SELECTION_MASK); 1285663e8e51Sths switch (selection) { 1286663e8e51Sths case PORT_SOFTWARE_RESET: 1287663e8e51Sths nic_reset(s); 1288663e8e51Sths break; 1289663e8e51Sths case PORT_SELFTEST: 1290aac443e6SStefan Weil TRACE(OTHER, logout("selftest address=0x%08x\n", address)); 1291c227f099SAnthony Liguori eepro100_selftest_t data; 129216ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, address, (uint8_t *) &data, sizeof(data)); 1293663e8e51Sths data.st_sign = 0xffffffff; 1294663e8e51Sths data.st_result = 0; 129516ef60c9SEduard - Gabriel Munteanu pci_dma_write(&s->dev, address, (uint8_t *) &data, sizeof(data)); 1296663e8e51Sths break; 1297663e8e51Sths case PORT_SELECTIVE_RESET: 1298aac443e6SStefan Weil TRACE(OTHER, logout("selective reset, selftest address=0x%08x\n", address)); 1299663e8e51Sths nic_selective_reset(s); 1300663e8e51Sths break; 1301663e8e51Sths default: 1302663e8e51Sths logout("val=0x%08x\n", val); 1303663e8e51Sths missing("unknown port selection"); 1304663e8e51Sths } 1305663e8e51Sths } 1306663e8e51Sths 1307663e8e51Sths /***************************************************************************** 1308663e8e51Sths * 1309663e8e51Sths * General hardware emulation. 1310663e8e51Sths * 1311663e8e51Sths ****************************************************************************/ 1312663e8e51Sths 1313663e8e51Sths static uint8_t eepro100_read1(EEPRO100State * s, uint32_t addr) 1314663e8e51Sths { 1315ef476062SBlue Swirl uint8_t val = 0; 1316663e8e51Sths if (addr <= sizeof(s->mem) - sizeof(val)) { 1317e5e23ab8SStefan Weil val = s->mem[addr]; 1318663e8e51Sths } 1319663e8e51Sths 1320663e8e51Sths switch (addr) { 1321663e8e51Sths case SCBStatus: 1322663e8e51Sths case SCBAck: 1323aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1324663e8e51Sths break; 1325663e8e51Sths case SCBCmd: 1326aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1327e7493b25SStefan Weil #if 0 1328e7493b25SStefan Weil val = eepro100_read_command(s); 1329e7493b25SStefan Weil #endif 1330663e8e51Sths break; 1331663e8e51Sths case SCBIntmask: 1332aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1333663e8e51Sths break; 1334663e8e51Sths case SCBPort + 3: 1335aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1336663e8e51Sths break; 1337663e8e51Sths case SCBeeprom: 1338663e8e51Sths val = eepro100_read_eeprom(s); 1339663e8e51Sths break; 13400113f48dSStefan Weil case SCBCtrlMDI: 13410113f48dSStefan Weil case SCBCtrlMDI + 1: 13420113f48dSStefan Weil case SCBCtrlMDI + 2: 13430113f48dSStefan Weil case SCBCtrlMDI + 3: 13440113f48dSStefan Weil val = (uint8_t)(eepro100_read_mdi(s) >> (8 * (addr & 3))); 13450113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 13460113f48dSStefan Weil break; 13470908bba1SStefan Weil case SCBpmdr: /* Power Management Driver Register */ 1348663e8e51Sths val = 0; 1349aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1350663e8e51Sths break; 1351a39bd017SStefan Weil case SCBgctrl: /* General Control Register */ 1352a39bd017SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1353a39bd017SStefan Weil break; 13540908bba1SStefan Weil case SCBgstat: /* General Status Register */ 1355663e8e51Sths /* 100 Mbps full duplex, valid link */ 1356663e8e51Sths val = 0x07; 1357aac443e6SStefan Weil TRACE(OTHER, logout("addr=General Status val=%02x\n", val)); 1358663e8e51Sths break; 1359663e8e51Sths default: 1360663e8e51Sths logout("addr=%s val=0x%02x\n", regname(addr), val); 1361663e8e51Sths missing("unknown byte read"); 1362663e8e51Sths } 1363663e8e51Sths return val; 1364663e8e51Sths } 1365663e8e51Sths 1366663e8e51Sths static uint16_t eepro100_read2(EEPRO100State * s, uint32_t addr) 1367663e8e51Sths { 1368ef476062SBlue Swirl uint16_t val = 0; 1369663e8e51Sths if (addr <= sizeof(s->mem) - sizeof(val)) { 1370e5e23ab8SStefan Weil val = e100_read_reg2(s, addr); 1371663e8e51Sths } 1372663e8e51Sths 1373663e8e51Sths switch (addr) { 1374663e8e51Sths case SCBStatus: 1375dbbaaff6S=?UTF-8?q?Reimar=20D=C3=B6ffinger?= case SCBCmd: 1376aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 1377663e8e51Sths break; 1378663e8e51Sths case SCBeeprom: 1379663e8e51Sths val = eepro100_read_eeprom(s); 1380aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 1381663e8e51Sths break; 13820113f48dSStefan Weil case SCBCtrlMDI: 13830113f48dSStefan Weil case SCBCtrlMDI + 2: 13840113f48dSStefan Weil val = (uint16_t)(eepro100_read_mdi(s) >> (8 * (addr & 3))); 13850113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 13860113f48dSStefan Weil break; 1387663e8e51Sths default: 1388663e8e51Sths logout("addr=%s val=0x%04x\n", regname(addr), val); 1389663e8e51Sths missing("unknown word read"); 1390663e8e51Sths } 1391663e8e51Sths return val; 1392663e8e51Sths } 1393663e8e51Sths 1394663e8e51Sths static uint32_t eepro100_read4(EEPRO100State * s, uint32_t addr) 1395663e8e51Sths { 1396ef476062SBlue Swirl uint32_t val = 0; 1397663e8e51Sths if (addr <= sizeof(s->mem) - sizeof(val)) { 1398e5e23ab8SStefan Weil val = e100_read_reg4(s, addr); 1399663e8e51Sths } 1400663e8e51Sths 1401663e8e51Sths switch (addr) { 1402663e8e51Sths case SCBStatus: 1403aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); 1404663e8e51Sths break; 1405663e8e51Sths case SCBPointer: 1406aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); 1407663e8e51Sths break; 1408663e8e51Sths case SCBPort: 1409663e8e51Sths val = eepro100_read_port(s); 1410aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); 1411663e8e51Sths break; 1412072476eaSStefan Weil case SCBflash: 1413072476eaSStefan Weil val = eepro100_read_eeprom(s); 1414072476eaSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); 1415072476eaSStefan Weil break; 1416663e8e51Sths case SCBCtrlMDI: 1417663e8e51Sths val = eepro100_read_mdi(s); 1418663e8e51Sths break; 1419663e8e51Sths default: 1420663e8e51Sths logout("addr=%s val=0x%08x\n", regname(addr), val); 1421663e8e51Sths missing("unknown longword read"); 1422663e8e51Sths } 1423663e8e51Sths return val; 1424663e8e51Sths } 1425663e8e51Sths 1426663e8e51Sths static void eepro100_write1(EEPRO100State * s, uint32_t addr, uint8_t val) 1427663e8e51Sths { 1428e74818f3SStefan Weil /* SCBStatus is readonly. */ 1429e74818f3SStefan Weil if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) { 1430e5e23ab8SStefan Weil s->mem[addr] = val; 1431663e8e51Sths } 1432663e8e51Sths 1433663e8e51Sths switch (addr) { 1434663e8e51Sths case SCBStatus: 14351b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1436663e8e51Sths break; 1437663e8e51Sths case SCBAck: 14381b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1439663e8e51Sths eepro100_acknowledge(s); 1440663e8e51Sths break; 1441663e8e51Sths case SCBCmd: 14421b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1443663e8e51Sths eepro100_write_command(s, val); 1444663e8e51Sths break; 1445663e8e51Sths case SCBIntmask: 14461b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1447663e8e51Sths if (val & BIT(1)) { 1448663e8e51Sths eepro100_swi_interrupt(s); 1449663e8e51Sths } 1450663e8e51Sths eepro100_interrupt(s, 0); 1451663e8e51Sths break; 145227a05006SStefan Weil case SCBPointer: 145327a05006SStefan Weil case SCBPointer + 1: 145427a05006SStefan Weil case SCBPointer + 2: 145527a05006SStefan Weil case SCBPointer + 3: 145627a05006SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 145727a05006SStefan Weil break; 14583fd3d0b4SStefan Weil case SCBPort: 14593fd3d0b4SStefan Weil case SCBPort + 1: 14603fd3d0b4SStefan Weil case SCBPort + 2: 14613fd3d0b4SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 14623fd3d0b4SStefan Weil break; 1463663e8e51Sths case SCBPort + 3: 14643fd3d0b4SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 14653fd3d0b4SStefan Weil eepro100_write_port(s); 14663fd3d0b4SStefan Weil break; 1467aac443e6SStefan Weil case SCBFlow: /* does not exist on 82557 */ 14683257d2b6Sths case SCBFlow + 1: 14693257d2b6Sths case SCBFlow + 2: 14700908bba1SStefan Weil case SCBpmdr: /* does not exist on 82557 */ 1471aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1472663e8e51Sths break; 1473663e8e51Sths case SCBeeprom: 14741b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1475663e8e51Sths eepro100_write_eeprom(s->eeprom, val); 1476663e8e51Sths break; 14770113f48dSStefan Weil case SCBCtrlMDI: 14780113f48dSStefan Weil case SCBCtrlMDI + 1: 14790113f48dSStefan Weil case SCBCtrlMDI + 2: 14800113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 14810113f48dSStefan Weil break; 14820113f48dSStefan Weil case SCBCtrlMDI + 3: 14830113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 14840113f48dSStefan Weil eepro100_write_mdi(s); 14850113f48dSStefan Weil break; 1486663e8e51Sths default: 1487663e8e51Sths logout("addr=%s val=0x%02x\n", regname(addr), val); 1488663e8e51Sths missing("unknown byte write"); 1489663e8e51Sths } 1490663e8e51Sths } 1491663e8e51Sths 1492663e8e51Sths static void eepro100_write2(EEPRO100State * s, uint32_t addr, uint16_t val) 1493663e8e51Sths { 1494e74818f3SStefan Weil /* SCBStatus is readonly. */ 1495e74818f3SStefan Weil if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) { 1496e5e23ab8SStefan Weil e100_write_reg2(s, addr, val); 1497663e8e51Sths } 1498663e8e51Sths 1499663e8e51Sths switch (addr) { 1500663e8e51Sths case SCBStatus: 15011b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 1502e74818f3SStefan Weil s->mem[SCBAck] = (val >> 8); 1503663e8e51Sths eepro100_acknowledge(s); 1504663e8e51Sths break; 1505663e8e51Sths case SCBCmd: 15061b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 1507663e8e51Sths eepro100_write_command(s, val); 1508663e8e51Sths eepro100_write1(s, SCBIntmask, val >> 8); 1509663e8e51Sths break; 151027a05006SStefan Weil case SCBPointer: 151127a05006SStefan Weil case SCBPointer + 2: 151227a05006SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 151327a05006SStefan Weil break; 15143fd3d0b4SStefan Weil case SCBPort: 15153fd3d0b4SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 15163fd3d0b4SStefan Weil break; 15173fd3d0b4SStefan Weil case SCBPort + 2: 15183fd3d0b4SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 15193fd3d0b4SStefan Weil eepro100_write_port(s); 15203fd3d0b4SStefan Weil break; 1521663e8e51Sths case SCBeeprom: 15221b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 1523663e8e51Sths eepro100_write_eeprom(s->eeprom, val); 1524663e8e51Sths break; 15250113f48dSStefan Weil case SCBCtrlMDI: 15260113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 15270113f48dSStefan Weil break; 15280113f48dSStefan Weil case SCBCtrlMDI + 2: 15290113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 15300113f48dSStefan Weil eepro100_write_mdi(s); 15310113f48dSStefan Weil break; 1532663e8e51Sths default: 1533663e8e51Sths logout("addr=%s val=0x%04x\n", regname(addr), val); 1534663e8e51Sths missing("unknown word write"); 1535663e8e51Sths } 1536663e8e51Sths } 1537663e8e51Sths 1538663e8e51Sths static void eepro100_write4(EEPRO100State * s, uint32_t addr, uint32_t val) 1539663e8e51Sths { 1540663e8e51Sths if (addr <= sizeof(s->mem) - sizeof(val)) { 1541e5e23ab8SStefan Weil e100_write_reg4(s, addr, val); 1542663e8e51Sths } 1543663e8e51Sths 1544663e8e51Sths switch (addr) { 1545663e8e51Sths case SCBPointer: 154627a05006SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); 1547663e8e51Sths break; 1548663e8e51Sths case SCBPort: 1549aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); 15503fd3d0b4SStefan Weil eepro100_write_port(s); 1551663e8e51Sths break; 1552072476eaSStefan Weil case SCBflash: 1553072476eaSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); 1554072476eaSStefan Weil val = val >> 16; 1555072476eaSStefan Weil eepro100_write_eeprom(s->eeprom, val); 1556072476eaSStefan Weil break; 1557663e8e51Sths case SCBCtrlMDI: 15580113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); 15590113f48dSStefan Weil eepro100_write_mdi(s); 1560663e8e51Sths break; 1561663e8e51Sths default: 1562663e8e51Sths logout("addr=%s val=0x%08x\n", regname(addr), val); 1563663e8e51Sths missing("unknown longword write"); 1564663e8e51Sths } 1565663e8e51Sths } 1566663e8e51Sths 1567a8170e5eSAvi Kivity static uint64_t eepro100_read(void *opaque, hwaddr addr, 15685e6ffddeSAvi Kivity unsigned size) 1569663e8e51Sths { 1570663e8e51Sths EEPRO100State *s = opaque; 15715e6ffddeSAvi Kivity 15725e6ffddeSAvi Kivity switch (size) { 15735e6ffddeSAvi Kivity case 1: return eepro100_read1(s, addr); 15745e6ffddeSAvi Kivity case 2: return eepro100_read2(s, addr); 15755e6ffddeSAvi Kivity case 4: return eepro100_read4(s, addr); 15765e6ffddeSAvi Kivity default: abort(); 15775e6ffddeSAvi Kivity } 1578663e8e51Sths } 1579663e8e51Sths 1580a8170e5eSAvi Kivity static void eepro100_write(void *opaque, hwaddr addr, 15815e6ffddeSAvi Kivity uint64_t data, unsigned size) 1582663e8e51Sths { 1583663e8e51Sths EEPRO100State *s = opaque; 15845e6ffddeSAvi Kivity 15855e6ffddeSAvi Kivity switch (size) { 15860ed8b6f6SBlue Swirl case 1: 15870ed8b6f6SBlue Swirl eepro100_write1(s, addr, data); 15880ed8b6f6SBlue Swirl break; 15890ed8b6f6SBlue Swirl case 2: 15900ed8b6f6SBlue Swirl eepro100_write2(s, addr, data); 15910ed8b6f6SBlue Swirl break; 15920ed8b6f6SBlue Swirl case 4: 15930ed8b6f6SBlue Swirl eepro100_write4(s, addr, data); 15940ed8b6f6SBlue Swirl break; 15950ed8b6f6SBlue Swirl default: 15960ed8b6f6SBlue Swirl abort(); 15975e6ffddeSAvi Kivity } 1598663e8e51Sths } 1599663e8e51Sths 16005e6ffddeSAvi Kivity static const MemoryRegionOps eepro100_ops = { 16015e6ffddeSAvi Kivity .read = eepro100_read, 16025e6ffddeSAvi Kivity .write = eepro100_write, 16035e6ffddeSAvi Kivity .endianness = DEVICE_LITTLE_ENDIAN, 1604663e8e51Sths }; 1605663e8e51Sths 16064e68f7a0SStefan Hajnoczi static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size) 1607663e8e51Sths { 1608663e8e51Sths /* TODO: 1609663e8e51Sths * - Magic packets should set bit 30 in power management driver register. 1610663e8e51Sths * - Interesting packets should set bit 29 in power management driver register. 1611663e8e51Sths */ 1612cc1f0f45SJason Wang EEPRO100State *s = qemu_get_nic_opaque(nc); 1613663e8e51Sths uint16_t rfd_status = 0xa000; 1614792f1d63SStefan Weil #if defined(CONFIG_PAD_RECEIVED_FRAMES) 1615792f1d63SStefan Weil uint8_t min_buf[60]; 1616792f1d63SStefan Weil #endif 1617663e8e51Sths static const uint8_t broadcast_macaddr[6] = 1618663e8e51Sths { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 1619663e8e51Sths 1620792f1d63SStefan Weil #if defined(CONFIG_PAD_RECEIVED_FRAMES) 1621792f1d63SStefan Weil /* Pad to minimum Ethernet frame length */ 1622792f1d63SStefan Weil if (size < sizeof(min_buf)) { 1623792f1d63SStefan Weil memcpy(min_buf, buf, size); 1624792f1d63SStefan Weil memset(&min_buf[size], 0, sizeof(min_buf) - size); 1625792f1d63SStefan Weil buf = min_buf; 1626792f1d63SStefan Weil size = sizeof(min_buf); 1627792f1d63SStefan Weil } 1628792f1d63SStefan Weil #endif 1629792f1d63SStefan Weil 1630663e8e51Sths if (s->configuration[8] & 0x80) { 1631663e8e51Sths /* CSMA is disabled. */ 1632663e8e51Sths logout("%p received while CSMA is disabled\n", s); 16334f1c942bSMark McLoughlin return -1; 1634792f1d63SStefan Weil #if !defined(CONFIG_PAD_RECEIVED_FRAMES) 1635ced5296aSStefan Weil } else if (size < 64 && (s->configuration[7] & BIT(0))) { 1636663e8e51Sths /* Short frame and configuration byte 7/0 (discard short receive) set: 1637663e8e51Sths * Short frame is discarded */ 1638067d01deSStefan Weil logout("%p received short frame (%zu byte)\n", s, size); 1639663e8e51Sths s->statistics.rx_short_frame_errors++; 1640e7493b25SStefan Weil return -1; 1641e7493b25SStefan Weil #endif 1642ced5296aSStefan Weil } else if ((size > MAX_ETH_FRAME_SIZE + 4) && !(s->configuration[18] & BIT(3))) { 1643663e8e51Sths /* Long frame and configuration byte 18/3 (long receive ok) not set: 1644663e8e51Sths * Long frames are discarded. */ 1645067d01deSStefan Weil logout("%p received long frame (%zu byte), ignored\n", s, size); 16464f1c942bSMark McLoughlin return -1; 1647e7493b25SStefan Weil } else if (memcmp(buf, s->conf.macaddr.a, 6) == 0) { /* !!! */ 1648663e8e51Sths /* Frame matches individual address. */ 1649663e8e51Sths /* TODO: check configuration byte 15/4 (ignore U/L). */ 1650067d01deSStefan Weil TRACE(RXTX, logout("%p received frame for me, len=%zu\n", s, size)); 1651663e8e51Sths } else if (memcmp(buf, broadcast_macaddr, 6) == 0) { 1652663e8e51Sths /* Broadcast frame. */ 1653067d01deSStefan Weil TRACE(RXTX, logout("%p received broadcast, len=%zu\n", s, size)); 1654663e8e51Sths rfd_status |= 0x0002; 16557b8737deSStefan Weil } else if (buf[0] & 0x01) { 1656663e8e51Sths /* Multicast frame. */ 16577b8737deSStefan Weil TRACE(RXTX, logout("%p received multicast, len=%zu,%s\n", s, size, nic_dump(buf, size))); 16587f1e9d4eSKevin Wolf if (s->configuration[21] & BIT(3)) { 16597b8737deSStefan Weil /* Multicast all bit is set, receive all multicast frames. */ 16607b8737deSStefan Weil } else { 16617c0348bdSMark Cave-Ayland unsigned mcast_idx = (net_crc32(buf, ETH_ALEN) & BITS(7, 2)) >> 2; 16627b8737deSStefan Weil assert(mcast_idx < 64); 16637b8737deSStefan Weil if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) { 16647b8737deSStefan Weil /* Multicast frame is allowed in hash table. */ 1665ced5296aSStefan Weil } else if (s->configuration[15] & BIT(0)) { 16667b8737deSStefan Weil /* Promiscuous: receive all. */ 16677b8737deSStefan Weil rfd_status |= 0x0004; 16687b8737deSStefan Weil } else { 16697b8737deSStefan Weil TRACE(RXTX, logout("%p multicast ignored\n", s)); 16707b8737deSStefan Weil return -1; 16717f1e9d4eSKevin Wolf } 1672663e8e51Sths } 16737b8737deSStefan Weil /* TODO: Next not for promiscuous mode? */ 1674663e8e51Sths rfd_status |= 0x0002; 1675ced5296aSStefan Weil } else if (s->configuration[15] & BIT(0)) { 1676663e8e51Sths /* Promiscuous: receive all. */ 1677067d01deSStefan Weil TRACE(RXTX, logout("%p received frame in promiscuous mode, len=%zu\n", s, size)); 1678663e8e51Sths rfd_status |= 0x0004; 1679010ec629SStefan Weil } else if (s->configuration[20] & BIT(6)) { 1680010ec629SStefan Weil /* Multiple IA bit set. */ 1681d00d6d00SMark Cave-Ayland unsigned mcast_idx = net_crc32(buf, ETH_ALEN) >> 26; 1682010ec629SStefan Weil assert(mcast_idx < 64); 1683010ec629SStefan Weil if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) { 1684010ec629SStefan Weil TRACE(RXTX, logout("%p accepted, multiple IA bit set\n", s)); 1685010ec629SStefan Weil } else { 1686010ec629SStefan Weil TRACE(RXTX, logout("%p frame ignored, multiple IA bit set\n", s)); 1687010ec629SStefan Weil return -1; 1688010ec629SStefan Weil } 1689663e8e51Sths } else { 1690067d01deSStefan Weil TRACE(RXTX, logout("%p received frame, ignored, len=%zu,%s\n", s, size, 1691aac443e6SStefan Weil nic_dump(buf, size))); 16924f1c942bSMark McLoughlin return size; 1693663e8e51Sths } 1694663e8e51Sths 1695663e8e51Sths if (get_ru_state(s) != ru_ready) { 1696aac443e6SStefan Weil /* No resources available. */ 1697aac443e6SStefan Weil logout("no resources, state=%u\n", get_ru_state(s)); 1698e824012bSStefan Weil /* TODO: RNR interrupt only at first failed frame? */ 1699e824012bSStefan Weil eepro100_rnr_interrupt(s); 1700663e8e51Sths s->statistics.rx_resource_errors++; 1701e7493b25SStefan Weil #if 0 1702e7493b25SStefan Weil assert(!"no resources"); 1703e7493b25SStefan Weil #endif 17044f1c942bSMark McLoughlin return -1; 1705663e8e51Sths } 1706e7493b25SStefan Weil /* !!! */ 1707c227f099SAnthony Liguori eepro100_rx_t rx; 170816ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, s->ru_base + s->ru_offset, 1709e965d4bcSDavid Gibson &rx, sizeof(eepro100_rx_t)); 1710663e8e51Sths uint16_t rfd_command = le16_to_cpu(rx.command); 1711663e8e51Sths uint16_t rfd_size = le16_to_cpu(rx.size); 17127f1e9d4eSKevin Wolf 17137f1e9d4eSKevin Wolf if (size > rfd_size) { 17147f1e9d4eSKevin Wolf logout("Receive buffer (%" PRId16 " bytes) too small for data " 17157f1e9d4eSKevin Wolf "(%zu bytes); data truncated\n", rfd_size, size); 17167f1e9d4eSKevin Wolf size = rfd_size; 17177f1e9d4eSKevin Wolf } 1718792f1d63SStefan Weil #if !defined(CONFIG_PAD_RECEIVED_FRAMES) 1719663e8e51Sths if (size < 64) { 1720663e8e51Sths rfd_status |= 0x0080; 1721663e8e51Sths } 1722792f1d63SStefan Weil #endif 1723aac443e6SStefan Weil TRACE(OTHER, logout("command 0x%04x, link 0x%08x, addr 0x%08x, size %u\n", 1724aac443e6SStefan Weil rfd_command, rx.link, rx.rx_buf_addr, rfd_size)); 172516ef60c9SEduard - Gabriel Munteanu stw_le_pci_dma(&s->dev, s->ru_base + s->ru_offset + 1726e5e23ab8SStefan Weil offsetof(eepro100_rx_t, status), rfd_status); 172716ef60c9SEduard - Gabriel Munteanu stw_le_pci_dma(&s->dev, s->ru_base + s->ru_offset + 1728e5e23ab8SStefan Weil offsetof(eepro100_rx_t, count), size); 1729663e8e51Sths /* Early receive interrupt not supported. */ 1730e7493b25SStefan Weil #if 0 1731e7493b25SStefan Weil eepro100_er_interrupt(s); 1732e7493b25SStefan Weil #endif 1733663e8e51Sths /* Receive CRC Transfer not supported. */ 1734ced5296aSStefan Weil if (s->configuration[18] & BIT(2)) { 17357f1e9d4eSKevin Wolf missing("Receive CRC Transfer"); 17367f1e9d4eSKevin Wolf return -1; 17377f1e9d4eSKevin Wolf } 1738663e8e51Sths /* TODO: check stripping enable bit. */ 1739e7493b25SStefan Weil #if 0 1740e7493b25SStefan Weil assert(!(s->configuration[17] & BIT(0))); 1741e7493b25SStefan Weil #endif 174216ef60c9SEduard - Gabriel Munteanu pci_dma_write(&s->dev, s->ru_base + s->ru_offset + 174327112f18SStefan Weil sizeof(eepro100_rx_t), buf, size); 1744663e8e51Sths s->statistics.rx_good_frames++; 1745663e8e51Sths eepro100_fr_interrupt(s); 1746663e8e51Sths s->ru_offset = le32_to_cpu(rx.link); 1747ced5296aSStefan Weil if (rfd_command & COMMAND_EL) { 1748663e8e51Sths /* EL bit is set, so this was the last frame. */ 17497f1e9d4eSKevin Wolf logout("receive: Running out of frames\n"); 17501069985fSBo Yang set_ru_state(s, ru_no_resources); 17511069985fSBo Yang eepro100_rnr_interrupt(s); 1752663e8e51Sths } 1753ced5296aSStefan Weil if (rfd_command & COMMAND_S) { 1754663e8e51Sths /* S bit is set. */ 1755663e8e51Sths set_ru_state(s, ru_suspended); 1756663e8e51Sths } 17574f1c942bSMark McLoughlin return size; 1758663e8e51Sths } 1759663e8e51Sths 1760151b2986SJuan Quintela static const VMStateDescription vmstate_eepro100 = { 1761151b2986SJuan Quintela .version_id = 3, 1762151b2986SJuan Quintela .minimum_version_id = 2, 1763151b2986SJuan Quintela .fields = (VMStateField[]) { 1764151b2986SJuan Quintela VMSTATE_PCI_DEVICE(dev, EEPRO100State), 1765151b2986SJuan Quintela VMSTATE_UNUSED(32), 1766151b2986SJuan Quintela VMSTATE_BUFFER(mult, EEPRO100State), 1767151b2986SJuan Quintela VMSTATE_BUFFER(mem, EEPRO100State), 17683706c43fSStefan Weil /* Save all members of struct between scb_stat and mem. */ 1769151b2986SJuan Quintela VMSTATE_UINT8(scb_stat, EEPRO100State), 1770151b2986SJuan Quintela VMSTATE_UINT8(int_stat, EEPRO100State), 1771151b2986SJuan Quintela VMSTATE_UNUSED(3*4), 1772151b2986SJuan Quintela VMSTATE_MACADDR(conf.macaddr, EEPRO100State), 1773151b2986SJuan Quintela VMSTATE_UNUSED(19*4), 1774151b2986SJuan Quintela VMSTATE_UINT16_ARRAY(mdimem, EEPRO100State, 32), 1775aac443e6SStefan Weil /* The eeprom should be saved and restored by its own routines. */ 1776151b2986SJuan Quintela VMSTATE_UINT32(device, EEPRO100State), 1777151b2986SJuan Quintela /* TODO check device. */ 1778151b2986SJuan Quintela VMSTATE_UINT32(cu_base, EEPRO100State), 1779151b2986SJuan Quintela VMSTATE_UINT32(cu_offset, EEPRO100State), 1780151b2986SJuan Quintela VMSTATE_UINT32(ru_base, EEPRO100State), 1781151b2986SJuan Quintela VMSTATE_UINT32(ru_offset, EEPRO100State), 1782151b2986SJuan Quintela VMSTATE_UINT32(statsaddr, EEPRO100State), 1783ba42b646SStefan Weil /* Save eepro100_stats_t statistics. */ 1784151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_good_frames, EEPRO100State), 1785151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_max_collisions, EEPRO100State), 1786151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_late_collisions, EEPRO100State), 1787151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_underruns, EEPRO100State), 1788151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_lost_crs, EEPRO100State), 1789151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_deferred, EEPRO100State), 1790151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_single_collisions, EEPRO100State), 1791151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_multiple_collisions, EEPRO100State), 1792151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_total_collisions, EEPRO100State), 1793151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_good_frames, EEPRO100State), 1794151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_crc_errors, EEPRO100State), 1795151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_alignment_errors, EEPRO100State), 1796151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_resource_errors, EEPRO100State), 1797151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_overrun_errors, EEPRO100State), 1798151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_cdt_errors, EEPRO100State), 1799151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_short_frame_errors, EEPRO100State), 1800151b2986SJuan Quintela VMSTATE_UINT32(statistics.fc_xmt_pause, EEPRO100State), 1801151b2986SJuan Quintela VMSTATE_UINT32(statistics.fc_rcv_pause, EEPRO100State), 1802151b2986SJuan Quintela VMSTATE_UINT32(statistics.fc_rcv_unsupported, EEPRO100State), 1803151b2986SJuan Quintela VMSTATE_UINT16(statistics.xmt_tco_frames, EEPRO100State), 1804151b2986SJuan Quintela VMSTATE_UINT16(statistics.rcv_tco_frames, EEPRO100State), 18052657c663Sbalrog /* Configuration bytes. */ 1806151b2986SJuan Quintela VMSTATE_BUFFER(configuration, EEPRO100State), 1807151b2986SJuan Quintela VMSTATE_END_OF_LIST() 1808663e8e51Sths } 1809151b2986SJuan Quintela }; 1810663e8e51Sths 1811f90c2bcdSAlex Williamson static void pci_nic_uninit(PCIDevice *pci_dev) 1812b946a153Saliguori { 1813c4c270e2SStefan Weil EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev); 1814b946a153Saliguori 18150be71e32SAlex Williamson vmstate_unregister(&pci_dev->qdev, s->vmstate, s); 18162634ab7fSLi Qiang g_free(s->vmstate); 18175fce2b3eSAlex Williamson eeprom93xx_free(&pci_dev->qdev, s->eeprom); 1818948ecf21SJason Wang qemu_del_nic(s->nic); 1819b946a153Saliguori } 1820b946a153Saliguori 1821e00e365eSMark McLoughlin static NetClientInfo net_eepro100_info = { 1822f394b2e2SEric Blake .type = NET_CLIENT_DRIVER_NIC, 1823e00e365eSMark McLoughlin .size = sizeof(NICState), 1824e00e365eSMark McLoughlin .receive = nic_receive, 1825e00e365eSMark McLoughlin }; 1826e00e365eSMark McLoughlin 18279af21dbeSMarkus Armbruster static void e100_nic_realize(PCIDevice *pci_dev, Error **errp) 1828663e8e51Sths { 1829273a2142SJuan Quintela EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev); 183040021f08SAnthony Liguori E100PCIDeviceInfo *info = eepro100_get_class(s); 18319a7c2a59SMao Zhongyi Error *local_err = NULL; 1832663e8e51Sths 1833aac443e6SStefan Weil TRACE(OTHER, logout("\n")); 1834663e8e51Sths 183540021f08SAnthony Liguori s->device = info->device; 1836663e8e51Sths 18379a7c2a59SMao Zhongyi e100_pci_reset(s, &local_err); 18389a7c2a59SMao Zhongyi if (local_err) { 18399a7c2a59SMao Zhongyi error_propagate(errp, local_err); 18409a7c2a59SMao Zhongyi return; 18419a7c2a59SMao Zhongyi } 1842663e8e51Sths 1843663e8e51Sths /* Add 64 * 2 EEPROM. i82557 and i82558 support a 64 word EEPROM, 1844663e8e51Sths * i82559 and later support 64 or 256 word EEPROM. */ 18455fce2b3eSAlex Williamson s->eeprom = eeprom93xx_new(&pci_dev->qdev, EEPROM_SIZE); 1846663e8e51Sths 1847663e8e51Sths /* Handler for memory-mapped I/O */ 1848eedfac6fSPaolo Bonzini memory_region_init_io(&s->mmio_bar, OBJECT(s), &eepro100_ops, s, 1849eedfac6fSPaolo Bonzini "eepro100-mmio", PCI_MEM_SIZE); 1850e824b2ccSAvi Kivity pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->mmio_bar); 1851eedfac6fSPaolo Bonzini memory_region_init_io(&s->io_bar, OBJECT(s), &eepro100_ops, s, 1852eedfac6fSPaolo Bonzini "eepro100-io", PCI_IO_SIZE); 1853e824b2ccSAvi Kivity pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar); 18545e6ffddeSAvi Kivity /* FIXME: flash aliases to mmio?! */ 1855eedfac6fSPaolo Bonzini memory_region_init_io(&s->flash_bar, OBJECT(s), &eepro100_ops, s, 1856eedfac6fSPaolo Bonzini "eepro100-flash", PCI_FLASH_SIZE); 1857e824b2ccSAvi Kivity pci_register_bar(&s->dev, 2, 0, &s->flash_bar); 1858663e8e51Sths 1859508ef936SGerd Hoffmann qemu_macaddr_default_if_unset(&s->conf.macaddr); 1860ce0e58b3SStefan Weil logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6)); 1861663e8e51Sths 1862663e8e51Sths nic_reset(s); 1863663e8e51Sths 1864e00e365eSMark McLoughlin s->nic = qemu_new_nic(&net_eepro100_info, &s->conf, 1865f79f2bfcSAnthony Liguori object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s); 1866663e8e51Sths 1867b356f76dSJason Wang qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); 1868b356f76dSJason Wang TRACE(OTHER, logout("%s\n", qemu_get_queue(s->nic)->info_str)); 1869663e8e51Sths 1870a08d4367SJan Kiszka qemu_register_reset(nic_reset, s); 1871663e8e51Sths 1872e4d67e4fSMarc-André Lureau s->vmstate = g_memdup(&vmstate_eepro100, sizeof(vmstate_eepro100)); 1873b356f76dSJason Wang s->vmstate->name = qemu_get_queue(s->nic)->model; 18740be71e32SAlex Williamson vmstate_register(&pci_dev->qdev, -1, s->vmstate, s); 1875663e8e51Sths } 1876663e8e51Sths 18777317bb17SGonglei static void eepro100_instance_init(Object *obj) 18787317bb17SGonglei { 18797317bb17SGonglei EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, PCI_DEVICE(obj)); 18807317bb17SGonglei device_add_bootindex_property(obj, &s->conf.bootindex, 18817317bb17SGonglei "bootindex", "/ethernet-phy@0", 18827317bb17SGonglei DEVICE(s), NULL); 18837317bb17SGonglei } 18847317bb17SGonglei 1885558c8634SStefan Weil static E100PCIDeviceInfo e100_devices[] = { 1886663e8e51Sths { 188739bffca2SAnthony Liguori .name = "i82550", 188839bffca2SAnthony Liguori .desc = "Intel i82550 Ethernet", 1889558c8634SStefan Weil .device = i82550, 1890558c8634SStefan Weil /* TODO: check device id. */ 189140021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82551IT, 1892558c8634SStefan Weil /* Revision ID: 0x0c, 0x0d, 0x0e. */ 189340021f08SAnthony Liguori .revision = 0x0e, 1894558c8634SStefan Weil /* TODO: check size of statistical counters. */ 1895558c8634SStefan Weil .stats_size = 80, 1896558c8634SStefan Weil /* TODO: check extended tcb support. */ 1897558c8634SStefan Weil .has_extended_tcb_support = true, 1898558c8634SStefan Weil .power_management = true, 1899558c8634SStefan Weil },{ 190039bffca2SAnthony Liguori .name = "i82551", 190139bffca2SAnthony Liguori .desc = "Intel i82551 Ethernet", 1902558c8634SStefan Weil .device = i82551, 190340021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82551IT, 1904558c8634SStefan Weil /* Revision ID: 0x0f, 0x10. */ 190540021f08SAnthony Liguori .revision = 0x0f, 1906558c8634SStefan Weil /* TODO: check size of statistical counters. */ 1907558c8634SStefan Weil .stats_size = 80, 1908558c8634SStefan Weil .has_extended_tcb_support = true, 1909558c8634SStefan Weil .power_management = true, 1910558c8634SStefan Weil },{ 191139bffca2SAnthony Liguori .name = "i82557a", 191239bffca2SAnthony Liguori .desc = "Intel i82557A Ethernet", 1913558c8634SStefan Weil .device = i82557A, 191440021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557, 191540021f08SAnthony Liguori .revision = 0x01, 1916558c8634SStefan Weil .power_management = false, 1917558c8634SStefan Weil },{ 191839bffca2SAnthony Liguori .name = "i82557b", 191939bffca2SAnthony Liguori .desc = "Intel i82557B Ethernet", 1920558c8634SStefan Weil .device = i82557B, 192140021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557, 192240021f08SAnthony Liguori .revision = 0x02, 1923558c8634SStefan Weil .power_management = false, 1924558c8634SStefan Weil },{ 192539bffca2SAnthony Liguori .name = "i82557c", 192639bffca2SAnthony Liguori .desc = "Intel i82557C Ethernet", 1927558c8634SStefan Weil .device = i82557C, 192840021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557, 192940021f08SAnthony Liguori .revision = 0x03, 1930558c8634SStefan Weil .power_management = false, 1931558c8634SStefan Weil },{ 193239bffca2SAnthony Liguori .name = "i82558a", 193339bffca2SAnthony Liguori .desc = "Intel i82558A Ethernet", 1934558c8634SStefan Weil .device = i82558A, 193540021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557, 193640021f08SAnthony Liguori .revision = 0x04, 1937558c8634SStefan Weil .stats_size = 76, 1938558c8634SStefan Weil .has_extended_tcb_support = true, 1939558c8634SStefan Weil .power_management = true, 1940558c8634SStefan Weil },{ 194139bffca2SAnthony Liguori .name = "i82558b", 194239bffca2SAnthony Liguori .desc = "Intel i82558B Ethernet", 1943558c8634SStefan Weil .device = i82558B, 194440021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557, 194540021f08SAnthony Liguori .revision = 0x05, 1946558c8634SStefan Weil .stats_size = 76, 1947558c8634SStefan Weil .has_extended_tcb_support = true, 1948558c8634SStefan Weil .power_management = true, 1949558c8634SStefan Weil },{ 195039bffca2SAnthony Liguori .name = "i82559a", 195139bffca2SAnthony Liguori .desc = "Intel i82559A Ethernet", 1952558c8634SStefan Weil .device = i82559A, 195340021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557, 195440021f08SAnthony Liguori .revision = 0x06, 1955558c8634SStefan Weil .stats_size = 80, 1956558c8634SStefan Weil .has_extended_tcb_support = true, 1957558c8634SStefan Weil .power_management = true, 1958558c8634SStefan Weil },{ 195939bffca2SAnthony Liguori .name = "i82559b", 196039bffca2SAnthony Liguori .desc = "Intel i82559B Ethernet", 1961558c8634SStefan Weil .device = i82559B, 196240021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557, 196340021f08SAnthony Liguori .revision = 0x07, 1964558c8634SStefan Weil .stats_size = 80, 1965558c8634SStefan Weil .has_extended_tcb_support = true, 1966558c8634SStefan Weil .power_management = true, 1967558c8634SStefan Weil },{ 196839bffca2SAnthony Liguori .name = "i82559c", 196939bffca2SAnthony Liguori .desc = "Intel i82559C Ethernet", 1970558c8634SStefan Weil .device = i82559C, 197140021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557, 1972558c8634SStefan Weil #if 0 197340021f08SAnthony Liguori .revision = 0x08, 1974558c8634SStefan Weil #endif 1975558c8634SStefan Weil /* TODO: Windows wants revision id 0x0c. */ 197640021f08SAnthony Liguori .revision = 0x0c, 1977ad03502bSIsaku Yamahata #if EEPROM_SIZE > 0 197840021f08SAnthony Liguori .subsystem_vendor_id = PCI_VENDOR_ID_INTEL, 197940021f08SAnthony Liguori .subsystem_id = 0x0040, 1980ad03502bSIsaku Yamahata #endif 1981558c8634SStefan Weil .stats_size = 80, 1982558c8634SStefan Weil .has_extended_tcb_support = true, 1983558c8634SStefan Weil .power_management = true, 1984558c8634SStefan Weil },{ 198539bffca2SAnthony Liguori .name = "i82559er", 198639bffca2SAnthony Liguori .desc = "Intel i82559ER Ethernet", 1987558c8634SStefan Weil .device = i82559ER, 198840021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82551IT, 198940021f08SAnthony Liguori .revision = 0x09, 1990558c8634SStefan Weil .stats_size = 80, 1991558c8634SStefan Weil .has_extended_tcb_support = true, 1992558c8634SStefan Weil .power_management = true, 1993558c8634SStefan Weil },{ 199439bffca2SAnthony Liguori .name = "i82562", 199539bffca2SAnthony Liguori .desc = "Intel i82562 Ethernet", 1996558c8634SStefan Weil .device = i82562, 1997558c8634SStefan Weil /* TODO: check device id. */ 199840021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82551IT, 1999558c8634SStefan Weil /* TODO: wrong revision id. */ 200040021f08SAnthony Liguori .revision = 0x0e, 2001558c8634SStefan Weil .stats_size = 80, 2002558c8634SStefan Weil .has_extended_tcb_support = true, 2003558c8634SStefan Weil .power_management = true, 2004db667a12SStefan Weil },{ 2005db667a12SStefan Weil /* Toshiba Tecra 8200. */ 200639bffca2SAnthony Liguori .name = "i82801", 200739bffca2SAnthony Liguori .desc = "Intel i82801 Ethernet", 2008db667a12SStefan Weil .device = i82801, 200940021f08SAnthony Liguori .device_id = 0x2449, 201040021f08SAnthony Liguori .revision = 0x03, 2011db667a12SStefan Weil .stats_size = 80, 2012db667a12SStefan Weil .has_extended_tcb_support = true, 2013db667a12SStefan Weil .power_management = true, 2014663e8e51Sths } 2015558c8634SStefan Weil }; 2016663e8e51Sths 201740021f08SAnthony Liguori static E100PCIDeviceInfo *eepro100_get_class_by_name(const char *typename) 201840021f08SAnthony Liguori { 201940021f08SAnthony Liguori E100PCIDeviceInfo *info = NULL; 202040021f08SAnthony Liguori int i; 202140021f08SAnthony Liguori 202240021f08SAnthony Liguori /* This is admittedly awkward but also temporary. QOM allows for 202340021f08SAnthony Liguori * parameterized typing and for subclassing both of which would suitable 202440021f08SAnthony Liguori * handle what's going on here. But class_data is already being used as 202540021f08SAnthony Liguori * a stop-gap hack to allow incremental qdev conversion so we cannot use it 202640021f08SAnthony Liguori * right now. Once we merge the final QOM series, we can come back here and 202740021f08SAnthony Liguori * do this in a much more elegant fashion. 202840021f08SAnthony Liguori */ 202940021f08SAnthony Liguori for (i = 0; i < ARRAY_SIZE(e100_devices); i++) { 203039bffca2SAnthony Liguori if (strcmp(e100_devices[i].name, typename) == 0) { 203140021f08SAnthony Liguori info = &e100_devices[i]; 203240021f08SAnthony Liguori break; 203340021f08SAnthony Liguori } 203440021f08SAnthony Liguori } 203540021f08SAnthony Liguori assert(info != NULL); 203640021f08SAnthony Liguori 203740021f08SAnthony Liguori return info; 203840021f08SAnthony Liguori } 203940021f08SAnthony Liguori 204040021f08SAnthony Liguori static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s) 204140021f08SAnthony Liguori { 204240021f08SAnthony Liguori return eepro100_get_class_by_name(object_get_typename(OBJECT(s))); 204340021f08SAnthony Liguori } 204440021f08SAnthony Liguori 204539bffca2SAnthony Liguori static Property e100_properties[] = { 204639bffca2SAnthony Liguori DEFINE_NIC_PROPERTIES(EEPRO100State, conf), 204739bffca2SAnthony Liguori DEFINE_PROP_END_OF_LIST(), 204839bffca2SAnthony Liguori }; 204939bffca2SAnthony Liguori 205040021f08SAnthony Liguori static void eepro100_class_init(ObjectClass *klass, void *data) 205140021f08SAnthony Liguori { 205239bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 205340021f08SAnthony Liguori PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); 205440021f08SAnthony Liguori E100PCIDeviceInfo *info; 205540021f08SAnthony Liguori 205640021f08SAnthony Liguori info = eepro100_get_class_by_name(object_class_get_name(klass)); 205740021f08SAnthony Liguori 2058125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); 205939bffca2SAnthony Liguori dc->props = e100_properties; 206039bffca2SAnthony Liguori dc->desc = info->desc; 206140021f08SAnthony Liguori k->vendor_id = PCI_VENDOR_ID_INTEL; 206240021f08SAnthony Liguori k->class_id = PCI_CLASS_NETWORK_ETHERNET; 206340021f08SAnthony Liguori k->romfile = "pxe-eepro100.rom"; 20649af21dbeSMarkus Armbruster k->realize = e100_nic_realize; 206540021f08SAnthony Liguori k->exit = pci_nic_uninit; 206640021f08SAnthony Liguori k->device_id = info->device_id; 206740021f08SAnthony Liguori k->revision = info->revision; 206840021f08SAnthony Liguori k->subsystem_vendor_id = info->subsystem_vendor_id; 206940021f08SAnthony Liguori k->subsystem_id = info->subsystem_id; 207040021f08SAnthony Liguori } 207140021f08SAnthony Liguori 207283f7d43aSAndreas Färber static void eepro100_register_types(void) 20739d07d757SPaul Brook { 2074558c8634SStefan Weil size_t i; 2075558c8634SStefan Weil for (i = 0; i < ARRAY_SIZE(e100_devices); i++) { 207639bffca2SAnthony Liguori TypeInfo type_info = {}; 207739bffca2SAnthony Liguori E100PCIDeviceInfo *info = &e100_devices[i]; 207840021f08SAnthony Liguori 207939bffca2SAnthony Liguori type_info.name = info->name; 208039bffca2SAnthony Liguori type_info.parent = TYPE_PCI_DEVICE; 208139bffca2SAnthony Liguori type_info.class_init = eepro100_class_init; 208239bffca2SAnthony Liguori type_info.instance_size = sizeof(EEPRO100State); 20837317bb17SGonglei type_info.instance_init = eepro100_instance_init; 2084fd3b02c8SEduardo Habkost type_info.interfaces = (InterfaceInfo[]) { 2085fd3b02c8SEduardo Habkost { INTERFACE_CONVENTIONAL_PCI_DEVICE }, 2086fd3b02c8SEduardo Habkost { }, 2087fd3b02c8SEduardo Habkost }; 208840021f08SAnthony Liguori 208939bffca2SAnthony Liguori type_register(&type_info); 2090558c8634SStefan Weil } 20919d07d757SPaul Brook } 20929d07d757SPaul Brook 209383f7d43aSAndreas Färber type_init(eepro100_register_types) 2094