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 43663e8e51Sths #include <stddef.h> /* offsetof */ 4483c9f4caSPaolo Bonzini #include "hw/hw.h" 4583c9f4caSPaolo Bonzini #include "hw/pci/pci.h" 461422e32dSPaolo Bonzini #include "net/net.h" 470d09e41aSPaolo Bonzini #include "hw/nvram/eeprom93xx.h" 489c17d615SPaolo Bonzini #include "sysemu/sysemu.h" 499c17d615SPaolo Bonzini #include "sysemu/dma.h" 50949fc823SMarcel Apfelbaum #include "qemu/bitops.h" 51663e8e51Sths 52792f1d63SStefan Weil /* QEMU sends frames smaller than 60 bytes to ethernet nics. 53792f1d63SStefan Weil * Such frames are rejected by real nics and their emulations. 54792f1d63SStefan Weil * To avoid this behaviour, other nic emulations pad received 55792f1d63SStefan Weil * frames. The following definition enables this padding for 56792f1d63SStefan Weil * eepro100, too. We keep the define around in case it might 57792f1d63SStefan Weil * become useful the future if the core networking is ever 58792f1d63SStefan Weil * changed to pad short packets itself. */ 59792f1d63SStefan Weil #define CONFIG_PAD_RECEIVED_FRAMES 60792f1d63SStefan Weil 61663e8e51Sths #define KiB 1024 62663e8e51Sths 63aac443e6SStefan Weil /* Debug EEPRO100 card. */ 64ce0e58b3SStefan Weil #if 0 65ce0e58b3SStefan Weil # define DEBUG_EEPRO100 66ce0e58b3SStefan Weil #endif 67663e8e51Sths 68663e8e51Sths #ifdef DEBUG_EEPRO100 69001faf32SBlue Swirl #define logout(fmt, ...) fprintf(stderr, "EE100\t%-24s" fmt, __func__, ## __VA_ARGS__) 70663e8e51Sths #else 71001faf32SBlue Swirl #define logout(fmt, ...) ((void)0) 72663e8e51Sths #endif 73663e8e51Sths 74663e8e51Sths /* Set flags to 0 to disable debug output. */ 75aac443e6SStefan Weil #define INT 1 /* interrupt related actions */ 76aac443e6SStefan Weil #define MDI 1 /* mdi related actions */ 77aac443e6SStefan Weil #define OTHER 1 78aac443e6SStefan Weil #define RXTX 1 79aac443e6SStefan Weil #define EEPROM 1 /* eeprom related actions */ 80663e8e51Sths 81663e8e51Sths #define TRACE(flag, command) ((flag) ? (command) : (void)0) 82663e8e51Sths 837f1e9d4eSKevin Wolf #define missing(text) fprintf(stderr, "eepro100: feature is missing in this emulation: " text "\n") 84663e8e51Sths 85663e8e51Sths #define MAX_ETH_FRAME_SIZE 1514 86663e8e51Sths 87663e8e51Sths /* This driver supports several different devices which are declared here. */ 88c4c270e2SStefan Weil #define i82550 0x82550 89663e8e51Sths #define i82551 0x82551 90c4c270e2SStefan Weil #define i82557A 0x82557a 91663e8e51Sths #define i82557B 0x82557b 92663e8e51Sths #define i82557C 0x82557c 93c4c270e2SStefan Weil #define i82558A 0x82558a 94663e8e51Sths #define i82558B 0x82558b 95c4c270e2SStefan Weil #define i82559A 0x82559a 96c4c270e2SStefan Weil #define i82559B 0x82559b 97663e8e51Sths #define i82559C 0x82559c 98663e8e51Sths #define i82559ER 0x82559e 99663e8e51Sths #define i82562 0x82562 100db667a12SStefan Weil #define i82801 0x82801 101663e8e51Sths 102aac443e6SStefan Weil /* Use 64 word EEPROM. TODO: could be a runtime option. */ 103663e8e51Sths #define EEPROM_SIZE 64 104663e8e51Sths 105663e8e51Sths #define PCI_MEM_SIZE (4 * KiB) 106663e8e51Sths #define PCI_IO_SIZE 64 107663e8e51Sths #define PCI_FLASH_SIZE (128 * KiB) 108663e8e51Sths 109663e8e51Sths #define BITS(n, m) (((0xffffffffU << (31 - n)) >> (31 - n + m)) << m) 110663e8e51Sths 111663e8e51Sths /* The SCB accepts the following controls for the Tx and Rx units: */ 112663e8e51Sths #define CU_NOP 0x0000 /* No operation. */ 113663e8e51Sths #define CU_START 0x0010 /* CU start. */ 114663e8e51Sths #define CU_RESUME 0x0020 /* CU resume. */ 115663e8e51Sths #define CU_STATSADDR 0x0040 /* Load dump counters address. */ 116663e8e51Sths #define CU_SHOWSTATS 0x0050 /* Dump statistical counters. */ 117663e8e51Sths #define CU_CMD_BASE 0x0060 /* Load CU base address. */ 118663e8e51Sths #define CU_DUMPSTATS 0x0070 /* Dump and reset statistical counters. */ 119663e8e51Sths #define CU_SRESUME 0x00a0 /* CU static resume. */ 120663e8e51Sths 121663e8e51Sths #define RU_NOP 0x0000 122663e8e51Sths #define RX_START 0x0001 123663e8e51Sths #define RX_RESUME 0x0002 124e824012bSStefan Weil #define RU_ABORT 0x0004 125663e8e51Sths #define RX_ADDR_LOAD 0x0006 126663e8e51Sths #define RX_RESUMENR 0x0007 127663e8e51Sths #define INT_MASK 0x0100 128663e8e51Sths #define DRVR_INT 0x0200 /* Driver generated interrupt. */ 129663e8e51Sths 130558c8634SStefan Weil typedef struct { 13139bffca2SAnthony Liguori const char *name; 13239bffca2SAnthony Liguori const char *desc; 13340021f08SAnthony Liguori uint16_t device_id; 13440021f08SAnthony Liguori uint8_t revision; 13540021f08SAnthony Liguori uint16_t subsystem_vendor_id; 13640021f08SAnthony Liguori uint16_t subsystem_id; 13740021f08SAnthony Liguori 138558c8634SStefan Weil uint32_t device; 139558c8634SStefan Weil uint8_t stats_size; 140558c8634SStefan Weil bool has_extended_tcb_support; 141558c8634SStefan Weil bool power_management; 142558c8634SStefan Weil } E100PCIDeviceInfo; 143558c8634SStefan Weil 144663e8e51Sths /* Offsets to the various registers. 145663e8e51Sths All accesses need not be longword aligned. */ 146e5e23ab8SStefan Weil typedef enum { 1470908bba1SStefan Weil SCBStatus = 0, /* Status Word. */ 148663e8e51Sths SCBAck = 1, 149663e8e51Sths SCBCmd = 2, /* Rx/Command Unit command and status. */ 150663e8e51Sths SCBIntmask = 3, 151663e8e51Sths SCBPointer = 4, /* General purpose pointer. */ 152663e8e51Sths SCBPort = 8, /* Misc. commands and operands. */ 1530908bba1SStefan Weil SCBflash = 12, /* Flash memory control. */ 1540908bba1SStefan Weil SCBeeprom = 14, /* EEPROM control. */ 155663e8e51Sths SCBCtrlMDI = 16, /* MDI interface control. */ 156663e8e51Sths SCBEarlyRx = 20, /* Early receive byte count. */ 1570908bba1SStefan Weil SCBFlow = 24, /* Flow Control. */ 1580908bba1SStefan Weil SCBpmdr = 27, /* Power Management Driver. */ 1590908bba1SStefan Weil SCBgctrl = 28, /* General Control. */ 1600908bba1SStefan Weil SCBgstat = 29, /* General Status. */ 161e5e23ab8SStefan Weil } E100RegisterOffset; 162663e8e51Sths 163663e8e51Sths /* A speedo3 transmit buffer descriptor with two buffers... */ 164663e8e51Sths typedef struct { 165663e8e51Sths uint16_t status; 166663e8e51Sths uint16_t command; 167663e8e51Sths uint32_t link; /* void * */ 1687b8737deSStefan Weil uint32_t tbd_array_addr; /* transmit buffer descriptor array address. */ 169663e8e51Sths uint16_t tcb_bytes; /* transmit command block byte count (in lower 14 bits */ 170663e8e51Sths uint8_t tx_threshold; /* transmit threshold */ 171663e8e51Sths uint8_t tbd_count; /* TBD number */ 172e7493b25SStefan Weil #if 0 173e7493b25SStefan Weil /* This constitutes two "TBD" entries: hdr and data */ 174e7493b25SStefan Weil uint32_t tx_buf_addr0; /* void *, header of frame to be transmitted. */ 175e7493b25SStefan Weil int32_t tx_buf_size0; /* Length of Tx hdr. */ 176e7493b25SStefan Weil uint32_t tx_buf_addr1; /* void *, data to be transmitted. */ 177e7493b25SStefan Weil int32_t tx_buf_size1; /* Length of Tx data. */ 178e7493b25SStefan Weil #endif 179c227f099SAnthony Liguori } eepro100_tx_t; 180663e8e51Sths 181663e8e51Sths /* Receive frame descriptor. */ 182663e8e51Sths typedef struct { 183663e8e51Sths int16_t status; 184663e8e51Sths uint16_t command; 185663e8e51Sths uint32_t link; /* struct RxFD * */ 186663e8e51Sths uint32_t rx_buf_addr; /* void * */ 187663e8e51Sths uint16_t count; 188663e8e51Sths uint16_t size; 18927112f18SStefan Weil /* Ethernet frame data follows. */ 190c227f099SAnthony Liguori } eepro100_rx_t; 191663e8e51Sths 192ced5296aSStefan Weil typedef enum { 193ced5296aSStefan Weil COMMAND_EL = BIT(15), 194ced5296aSStefan Weil COMMAND_S = BIT(14), 195ced5296aSStefan Weil COMMAND_I = BIT(13), 196ced5296aSStefan Weil COMMAND_NC = BIT(4), 197ced5296aSStefan Weil COMMAND_SF = BIT(3), 198ced5296aSStefan Weil COMMAND_CMD = BITS(2, 0), 199ced5296aSStefan Weil } scb_command_bit; 200ced5296aSStefan Weil 201ced5296aSStefan Weil typedef enum { 202ced5296aSStefan Weil STATUS_C = BIT(15), 203ced5296aSStefan Weil STATUS_OK = BIT(13), 204ced5296aSStefan Weil } scb_status_bit; 205ced5296aSStefan Weil 206663e8e51Sths typedef struct { 207663e8e51Sths uint32_t tx_good_frames, tx_max_collisions, tx_late_collisions, 208663e8e51Sths tx_underruns, tx_lost_crs, tx_deferred, tx_single_collisions, 209663e8e51Sths tx_multiple_collisions, tx_total_collisions; 210663e8e51Sths uint32_t rx_good_frames, rx_crc_errors, rx_alignment_errors, 211663e8e51Sths rx_resource_errors, rx_overrun_errors, rx_cdt_errors, 212663e8e51Sths rx_short_frame_errors; 213663e8e51Sths uint32_t fc_xmt_pause, fc_rcv_pause, fc_rcv_unsupported; 214663e8e51Sths uint16_t xmt_tco_frames, rcv_tco_frames; 215ba42b646SStefan Weil /* TODO: i82559 has six reserved statistics but a total of 24 dwords. */ 216ba42b646SStefan Weil uint32_t reserved[4]; 217c227f099SAnthony Liguori } eepro100_stats_t; 218663e8e51Sths 219663e8e51Sths typedef enum { 220663e8e51Sths cu_idle = 0, 221663e8e51Sths cu_suspended = 1, 222663e8e51Sths cu_active = 2, 223663e8e51Sths cu_lpq_active = 2, 224663e8e51Sths cu_hqp_active = 3 225c227f099SAnthony Liguori } cu_state_t; 226663e8e51Sths 227663e8e51Sths typedef enum { 228663e8e51Sths ru_idle = 0, 229663e8e51Sths ru_suspended = 1, 230663e8e51Sths ru_no_resources = 2, 231663e8e51Sths ru_ready = 4 232c227f099SAnthony Liguori } ru_state_t; 233663e8e51Sths 234663e8e51Sths typedef struct { 235273a2142SJuan Quintela PCIDevice dev; 236010ec629SStefan Weil /* Hash register (multicast mask array, multiple individual addresses). */ 237010ec629SStefan Weil uint8_t mult[8]; 2385e6ffddeSAvi Kivity MemoryRegion mmio_bar; 2395e6ffddeSAvi Kivity MemoryRegion io_bar; 2405e6ffddeSAvi Kivity MemoryRegion flash_bar; 241e00e365eSMark McLoughlin NICState *nic; 242508ef936SGerd Hoffmann NICConf conf; 243663e8e51Sths uint8_t scb_stat; /* SCB stat/ack byte */ 244663e8e51Sths uint8_t int_stat; /* PCI interrupt status */ 2453706c43fSStefan Weil /* region must not be saved by nic_save. */ 246663e8e51Sths uint16_t mdimem[32]; 247c227f099SAnthony Liguori eeprom_t *eeprom; 248663e8e51Sths uint32_t device; /* device variant */ 249663e8e51Sths /* (cu_base + cu_offset) address the next command block in the command block list. */ 250663e8e51Sths uint32_t cu_base; /* CU base address */ 251663e8e51Sths uint32_t cu_offset; /* CU address offset */ 252663e8e51Sths /* (ru_base + ru_offset) address the RFD in the Receive Frame Area. */ 253663e8e51Sths uint32_t ru_base; /* RU base address */ 254663e8e51Sths uint32_t ru_offset; /* RU address offset */ 255c227f099SAnthony Liguori uint32_t statsaddr; /* pointer to eepro100_stats_t */ 256ba42b646SStefan Weil 257f3a52e50SStefan Weil /* Temporary status information (no need to save these values), 258f3a52e50SStefan Weil * used while processing CU commands. */ 259f3a52e50SStefan Weil eepro100_tx_t tx; /* transmit buffer descriptor */ 260f3a52e50SStefan Weil uint32_t cb_address; /* = cu_base + cu_offset */ 261f3a52e50SStefan Weil 262ba42b646SStefan Weil /* Statistical counters. Also used for wake-up packet (i82559). */ 263ba42b646SStefan Weil eepro100_stats_t statistics; 264ba42b646SStefan Weil 265e5e23ab8SStefan Weil /* Data in mem is always in the byte order of the controller (le). 266e5e23ab8SStefan Weil * It must be dword aligned to allow direct access to 32 bit values. */ 2673a93113aSDong Xu Wang uint8_t mem[PCI_MEM_SIZE] __attribute__((aligned(8))); 268e5e23ab8SStefan Weil 269663e8e51Sths /* Configuration bytes. */ 270663e8e51Sths uint8_t configuration[22]; 271663e8e51Sths 272151b2986SJuan Quintela /* vmstate for each particular nic */ 273151b2986SJuan Quintela VMStateDescription *vmstate; 274ba42b646SStefan Weil 275ba42b646SStefan Weil /* Quasi static device properties (no need to save them). */ 276ba42b646SStefan Weil uint16_t stats_size; 277ba42b646SStefan Weil bool has_extended_tcb_support; 278663e8e51Sths } EEPRO100State; 279663e8e51Sths 2806cded3a4SStefan Weil /* Word indices in EEPROM. */ 2816cded3a4SStefan Weil typedef enum { 2826cded3a4SStefan Weil EEPROM_CNFG_MDIX = 0x03, 2836cded3a4SStefan Weil EEPROM_ID = 0x05, 2846cded3a4SStefan Weil EEPROM_PHY_ID = 0x06, 2856cded3a4SStefan Weil EEPROM_VENDOR_ID = 0x0c, 2866cded3a4SStefan Weil EEPROM_CONFIG_ASF = 0x0d, 2876cded3a4SStefan Weil EEPROM_DEVICE_ID = 0x23, 2886cded3a4SStefan Weil EEPROM_SMBUS_ADDR = 0x90, 2896cded3a4SStefan Weil } EEPROMOffset; 2906cded3a4SStefan Weil 291b1e87018SStefan Weil /* Bit values for EEPROM ID word. */ 292b1e87018SStefan Weil typedef enum { 293b1e87018SStefan Weil EEPROM_ID_MDM = BIT(0), /* Modem */ 294b1e87018SStefan Weil EEPROM_ID_STB = BIT(1), /* Standby Enable */ 295b1e87018SStefan Weil EEPROM_ID_WMR = BIT(2), /* ??? */ 296b1e87018SStefan Weil EEPROM_ID_WOL = BIT(5), /* Wake on LAN */ 297b1e87018SStefan Weil EEPROM_ID_DPD = BIT(6), /* Deep Power Down */ 298b1e87018SStefan Weil EEPROM_ID_ALT = BIT(7), /* */ 299b1e87018SStefan Weil /* BITS(10, 8) device revision */ 300b1e87018SStefan Weil EEPROM_ID_BD = BIT(11), /* boot disable */ 301b1e87018SStefan Weil EEPROM_ID_ID = BIT(13), /* id bit */ 302b1e87018SStefan Weil /* BITS(15, 14) signature */ 303b1e87018SStefan Weil EEPROM_ID_VALID = BIT(14), /* signature for valid eeprom */ 304b1e87018SStefan Weil } eeprom_id_bit; 305b1e87018SStefan Weil 306663e8e51Sths /* Default values for MDI (PHY) registers */ 307663e8e51Sths static const uint16_t eepro100_mdi_default[] = { 308663e8e51Sths /* MDI Registers 0 - 6, 7 */ 309663e8e51Sths 0x3000, 0x780d, 0x02a8, 0x0154, 0x05e1, 0x0000, 0x0000, 0x0000, 310663e8e51Sths /* MDI Registers 8 - 15 */ 311663e8e51Sths 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 312663e8e51Sths /* MDI Registers 16 - 31 */ 313663e8e51Sths 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 314663e8e51Sths 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 315663e8e51Sths }; 316663e8e51Sths 317663e8e51Sths /* Readonly mask for MDI (PHY) registers */ 318663e8e51Sths static const uint16_t eepro100_mdi_mask[] = { 319663e8e51Sths 0x0000, 0xffff, 0xffff, 0xffff, 0xc01f, 0xffff, 0xffff, 0x0000, 320663e8e51Sths 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 321663e8e51Sths 0x0fff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 322663e8e51Sths 0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 323663e8e51Sths }; 324663e8e51Sths 32569f3ce78SStefan Weil #define POLYNOMIAL 0x04c11db6 32669f3ce78SStefan Weil 32740021f08SAnthony Liguori static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s); 32840021f08SAnthony Liguori 32969f3ce78SStefan Weil /* From FreeBSD (locally modified). */ 33069f3ce78SStefan Weil static unsigned e100_compute_mcast_idx(const uint8_t *ep) 33169f3ce78SStefan Weil { 33269f3ce78SStefan Weil uint32_t crc; 33369f3ce78SStefan Weil int carry, i, j; 33469f3ce78SStefan Weil uint8_t b; 33569f3ce78SStefan Weil 33669f3ce78SStefan Weil crc = 0xffffffff; 33769f3ce78SStefan Weil for (i = 0; i < 6; i++) { 33869f3ce78SStefan Weil b = *ep++; 33969f3ce78SStefan Weil for (j = 0; j < 8; j++) { 34069f3ce78SStefan Weil carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01); 34169f3ce78SStefan Weil crc <<= 1; 34269f3ce78SStefan Weil b >>= 1; 34369f3ce78SStefan Weil if (carry) { 34469f3ce78SStefan Weil crc = ((crc ^ POLYNOMIAL) | carry); 34569f3ce78SStefan Weil } 34669f3ce78SStefan Weil } 34769f3ce78SStefan Weil } 34869f3ce78SStefan Weil return (crc & BITS(7, 2)) >> 2; 34969f3ce78SStefan Weil } 35069f3ce78SStefan Weil 351e5e23ab8SStefan Weil /* Read a 16 bit control/status (CSR) register. */ 352e5e23ab8SStefan Weil static uint16_t e100_read_reg2(EEPRO100State *s, E100RegisterOffset addr) 353e5e23ab8SStefan Weil { 354e5e23ab8SStefan Weil assert(!((uintptr_t)&s->mem[addr] & 1)); 355e5e23ab8SStefan Weil return le16_to_cpup((uint16_t *)&s->mem[addr]); 356e5e23ab8SStefan Weil } 357e5e23ab8SStefan Weil 358e5e23ab8SStefan Weil /* Read a 32 bit control/status (CSR) register. */ 359e5e23ab8SStefan Weil static uint32_t e100_read_reg4(EEPRO100State *s, E100RegisterOffset addr) 360e5e23ab8SStefan Weil { 361e5e23ab8SStefan Weil assert(!((uintptr_t)&s->mem[addr] & 3)); 362e5e23ab8SStefan Weil return le32_to_cpup((uint32_t *)&s->mem[addr]); 363e5e23ab8SStefan Weil } 364e5e23ab8SStefan Weil 365e5e23ab8SStefan Weil /* Write a 16 bit control/status (CSR) register. */ 366e5e23ab8SStefan Weil static void e100_write_reg2(EEPRO100State *s, E100RegisterOffset addr, 367e5e23ab8SStefan Weil uint16_t val) 368e5e23ab8SStefan Weil { 369e5e23ab8SStefan Weil assert(!((uintptr_t)&s->mem[addr] & 1)); 370e5e23ab8SStefan Weil cpu_to_le16w((uint16_t *)&s->mem[addr], val); 371e5e23ab8SStefan Weil } 372e5e23ab8SStefan Weil 373e5e23ab8SStefan Weil /* Read a 32 bit control/status (CSR) register. */ 374e5e23ab8SStefan Weil static void e100_write_reg4(EEPRO100State *s, E100RegisterOffset addr, 375e5e23ab8SStefan Weil uint32_t val) 376e5e23ab8SStefan Weil { 377e5e23ab8SStefan Weil assert(!((uintptr_t)&s->mem[addr] & 3)); 378e5e23ab8SStefan Weil cpu_to_le32w((uint32_t *)&s->mem[addr], val); 379e5e23ab8SStefan Weil } 380e5e23ab8SStefan Weil 381663e8e51Sths #if defined(DEBUG_EEPRO100) 382663e8e51Sths static const char *nic_dump(const uint8_t * buf, unsigned size) 383663e8e51Sths { 384663e8e51Sths static char dump[3 * 16 + 1]; 385663e8e51Sths char *p = &dump[0]; 386aac443e6SStefan Weil if (size > 16) { 387663e8e51Sths size = 16; 388aac443e6SStefan Weil } 389663e8e51Sths while (size-- > 0) { 390663e8e51Sths p += sprintf(p, " %02x", *buf++); 391663e8e51Sths } 392663e8e51Sths return dump; 393663e8e51Sths } 394663e8e51Sths #endif /* DEBUG_EEPRO100 */ 395663e8e51Sths 396663e8e51Sths enum scb_stat_ack { 397663e8e51Sths stat_ack_not_ours = 0x00, 398663e8e51Sths stat_ack_sw_gen = 0x04, 399663e8e51Sths stat_ack_rnr = 0x10, 400663e8e51Sths stat_ack_cu_idle = 0x20, 401663e8e51Sths stat_ack_frame_rx = 0x40, 402663e8e51Sths stat_ack_cu_cmd_done = 0x80, 403663e8e51Sths stat_ack_not_present = 0xFF, 404663e8e51Sths stat_ack_rx = (stat_ack_sw_gen | stat_ack_rnr | stat_ack_frame_rx), 405663e8e51Sths stat_ack_tx = (stat_ack_cu_idle | stat_ack_cu_cmd_done), 406663e8e51Sths }; 407663e8e51Sths 408663e8e51Sths static void disable_interrupt(EEPRO100State * s) 409663e8e51Sths { 410663e8e51Sths if (s->int_stat) { 411aac443e6SStefan Weil TRACE(INT, logout("interrupt disabled\n")); 412273a2142SJuan Quintela qemu_irq_lower(s->dev.irq[0]); 413663e8e51Sths s->int_stat = 0; 414663e8e51Sths } 415663e8e51Sths } 416663e8e51Sths 417663e8e51Sths static void enable_interrupt(EEPRO100State * s) 418663e8e51Sths { 419663e8e51Sths if (!s->int_stat) { 420aac443e6SStefan Weil TRACE(INT, logout("interrupt enabled\n")); 421273a2142SJuan Quintela qemu_irq_raise(s->dev.irq[0]); 422663e8e51Sths s->int_stat = 1; 423663e8e51Sths } 424663e8e51Sths } 425663e8e51Sths 426663e8e51Sths static void eepro100_acknowledge(EEPRO100State * s) 427663e8e51Sths { 428663e8e51Sths s->scb_stat &= ~s->mem[SCBAck]; 429663e8e51Sths s->mem[SCBAck] = s->scb_stat; 430663e8e51Sths if (s->scb_stat == 0) { 431663e8e51Sths disable_interrupt(s); 432663e8e51Sths } 433663e8e51Sths } 434663e8e51Sths 435e715c8e8SStefan Weil static void eepro100_interrupt(EEPRO100State * s, uint8_t status) 436663e8e51Sths { 437663e8e51Sths uint8_t mask = ~s->mem[SCBIntmask]; 438e715c8e8SStefan Weil s->mem[SCBAck] |= status; 439e715c8e8SStefan Weil status = s->scb_stat = s->mem[SCBAck]; 440e715c8e8SStefan Weil status &= (mask | 0x0f); 441e7493b25SStefan Weil #if 0 442e7493b25SStefan Weil status &= (~s->mem[SCBIntmask] | 0x0xf); 443e7493b25SStefan Weil #endif 444e715c8e8SStefan Weil if (status && (mask & 0x01)) { 445663e8e51Sths /* SCB mask and SCB Bit M do not disable interrupt. */ 446663e8e51Sths enable_interrupt(s); 447663e8e51Sths } else if (s->int_stat) { 448663e8e51Sths disable_interrupt(s); 449663e8e51Sths } 450663e8e51Sths } 451663e8e51Sths 452663e8e51Sths static void eepro100_cx_interrupt(EEPRO100State * s) 453663e8e51Sths { 454663e8e51Sths /* CU completed action command. */ 455663e8e51Sths /* Transmit not ok (82557 only, not in emulation). */ 456663e8e51Sths eepro100_interrupt(s, 0x80); 457663e8e51Sths } 458663e8e51Sths 459663e8e51Sths static void eepro100_cna_interrupt(EEPRO100State * s) 460663e8e51Sths { 461663e8e51Sths /* CU left the active state. */ 462663e8e51Sths eepro100_interrupt(s, 0x20); 463663e8e51Sths } 464663e8e51Sths 465663e8e51Sths static void eepro100_fr_interrupt(EEPRO100State * s) 466663e8e51Sths { 467663e8e51Sths /* RU received a complete frame. */ 468663e8e51Sths eepro100_interrupt(s, 0x40); 469663e8e51Sths } 470663e8e51Sths 471663e8e51Sths static void eepro100_rnr_interrupt(EEPRO100State * s) 472663e8e51Sths { 473663e8e51Sths /* RU is not ready. */ 474663e8e51Sths eepro100_interrupt(s, 0x10); 475663e8e51Sths } 476663e8e51Sths 477663e8e51Sths static void eepro100_mdi_interrupt(EEPRO100State * s) 478663e8e51Sths { 479663e8e51Sths /* MDI completed read or write cycle. */ 480663e8e51Sths eepro100_interrupt(s, 0x08); 481663e8e51Sths } 482663e8e51Sths 483663e8e51Sths static void eepro100_swi_interrupt(EEPRO100State * s) 484663e8e51Sths { 485663e8e51Sths /* Software has requested an interrupt. */ 486663e8e51Sths eepro100_interrupt(s, 0x04); 487663e8e51Sths } 488663e8e51Sths 489663e8e51Sths #if 0 490663e8e51Sths static void eepro100_fcp_interrupt(EEPRO100State * s) 491663e8e51Sths { 492663e8e51Sths /* Flow control pause interrupt (82558 and later). */ 493663e8e51Sths eepro100_interrupt(s, 0x01); 494663e8e51Sths } 495663e8e51Sths #endif 496663e8e51Sths 49740021f08SAnthony Liguori static void e100_pci_reset(EEPRO100State * s) 498663e8e51Sths { 49940021f08SAnthony Liguori E100PCIDeviceInfo *info = eepro100_get_class(s); 500663e8e51Sths uint32_t device = s->device; 501273a2142SJuan Quintela uint8_t *pci_conf = s->dev.config; 502663e8e51Sths 503aac443e6SStefan Weil TRACE(OTHER, logout("%p\n", s)); 504663e8e51Sths 505663e8e51Sths /* PCI Status */ 506558c8634SStefan Weil pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM | 507558c8634SStefan Weil PCI_STATUS_FAST_BACK); 508663e8e51Sths /* PCI Latency Timer */ 50915e89f59SMichael S. Tsirkin pci_set_byte(pci_conf + PCI_LATENCY_TIMER, 0x20); /* latency timer = 32 clocks */ 510ae543b49SStefan Weil /* Capability Pointer is set by PCI framework. */ 511f62719caSStefan Weil /* Interrupt Line */ 512f62719caSStefan Weil /* Interrupt Pin */ 513f62719caSStefan Weil pci_set_byte(pci_conf + PCI_INTERRUPT_PIN, 1); /* interrupt pin A */ 514663e8e51Sths /* Minimum Grant */ 51515e89f59SMichael S. Tsirkin pci_set_byte(pci_conf + PCI_MIN_GNT, 0x08); 516663e8e51Sths /* Maximum Latency */ 51715e89f59SMichael S. Tsirkin pci_set_byte(pci_conf + PCI_MAX_LAT, 0x18); 518663e8e51Sths 51940021f08SAnthony Liguori s->stats_size = info->stats_size; 52040021f08SAnthony Liguori s->has_extended_tcb_support = info->has_extended_tcb_support; 521558c8634SStefan Weil 522663e8e51Sths switch (device) { 523ba42b646SStefan Weil case i82550: 524663e8e51Sths case i82551: 525ba42b646SStefan Weil case i82557A: 526663e8e51Sths case i82557B: 527663e8e51Sths case i82557C: 528ba42b646SStefan Weil case i82558A: 529663e8e51Sths case i82558B: 530ba42b646SStefan Weil case i82559A: 531ba42b646SStefan Weil case i82559B: 532558c8634SStefan Weil case i82559ER: 533558c8634SStefan Weil case i82562: 534db667a12SStefan Weil case i82801: 535663e8e51Sths case i82559C: 536ba42b646SStefan Weil break; 537663e8e51Sths default: 538663e8e51Sths logout("Device %X is undefined!\n", device); 539663e8e51Sths } 540663e8e51Sths 5413dec59a1SStefan Weil /* Standard TxCB. */ 5423dec59a1SStefan Weil s->configuration[6] |= BIT(4); 5433dec59a1SStefan Weil 544558c8634SStefan Weil /* Standard statistical counters. */ 545ba42b646SStefan Weil s->configuration[6] |= BIT(5); 546ba42b646SStefan Weil 547ba42b646SStefan Weil if (s->stats_size == 80) { 548ba42b646SStefan Weil /* TODO: check TCO Statistical Counters bit. Documentation not clear. */ 549ba42b646SStefan Weil if (s->configuration[6] & BIT(2)) { 550ba42b646SStefan Weil /* TCO statistical counters. */ 551ba42b646SStefan Weil assert(s->configuration[6] & BIT(5)); 552ba42b646SStefan Weil } else { 553ba42b646SStefan Weil if (s->configuration[6] & BIT(5)) { 554ba42b646SStefan Weil /* No extended statistical counters, i82557 compatible. */ 555ba42b646SStefan Weil s->stats_size = 64; 556ba42b646SStefan Weil } else { 557ba42b646SStefan Weil /* i82558 compatible. */ 558ba42b646SStefan Weil s->stats_size = 76; 559ba42b646SStefan Weil } 560ba42b646SStefan Weil } 561ba42b646SStefan Weil } else { 562ba42b646SStefan Weil if (s->configuration[6] & BIT(5)) { 563ba42b646SStefan Weil /* No extended statistical counters. */ 564ba42b646SStefan Weil s->stats_size = 64; 565ba42b646SStefan Weil } 566ba42b646SStefan Weil } 567ba42b646SStefan Weil assert(s->stats_size > 0 && s->stats_size <= sizeof(s->statistics)); 568ba42b646SStefan Weil 56940021f08SAnthony Liguori if (info->power_management) { 570ba42b646SStefan Weil /* Power Management Capabilities */ 5718bbd1ce2SMichael S. Tsirkin int cfg_offset = 0xdc; 572ca77089dSIsaku Yamahata int r = pci_add_capability(&s->dev, PCI_CAP_ID_PM, 5738bbd1ce2SMichael S. Tsirkin cfg_offset, PCI_PM_SIZEOF); 5748bbd1ce2SMichael S. Tsirkin assert(r >= 0); 575ae543b49SStefan Weil pci_set_word(pci_conf + cfg_offset + PCI_PM_PMC, 0x7e21); 576ae543b49SStefan Weil #if 0 /* TODO: replace dummy code for power management emulation. */ 577ba42b646SStefan Weil /* TODO: Power Management Control / Status. */ 578ae543b49SStefan Weil pci_set_word(pci_conf + cfg_offset + PCI_PM_CTRL, 0x0000); 579ba42b646SStefan Weil /* TODO: Ethernet Power Consumption Registers (i82559 and later). */ 580ae543b49SStefan Weil pci_set_byte(pci_conf + cfg_offset + PCI_PM_PPB_EXTENSIONS, 0x0000); 581ae543b49SStefan Weil #endif 582ae543b49SStefan Weil } 583ba42b646SStefan Weil 584ba42b646SStefan Weil #if EEPROM_SIZE > 0 585663e8e51Sths if (device == i82557C || device == i82558B || device == i82559C) { 586e7493b25SStefan Weil /* 587e7493b25SStefan Weil TODO: get vendor id from EEPROM for i82557C or later. 588e7493b25SStefan Weil TODO: get device id from EEPROM for i82557C or later. 589e7493b25SStefan Weil TODO: status bit 4 can be disabled by EEPROM for i82558, i82559. 590e7493b25SStefan Weil TODO: header type is determined by EEPROM for i82559. 591e7493b25SStefan Weil TODO: get subsystem id from EEPROM for i82557C or later. 592e7493b25SStefan Weil TODO: get subsystem vendor id from EEPROM for i82557C or later. 593e7493b25SStefan Weil TODO: exp. rom baddr depends on a bit in EEPROM for i82558 or later. 594e7493b25SStefan Weil TODO: capability pointer depends on EEPROM for i82558. 595e7493b25SStefan Weil */ 596663e8e51Sths logout("Get device id and revision from EEPROM!!!\n"); 597663e8e51Sths } 598ba42b646SStefan Weil #endif /* EEPROM_SIZE > 0 */ 599663e8e51Sths } 600663e8e51Sths 601663e8e51Sths static void nic_selective_reset(EEPRO100State * s) 602663e8e51Sths { 603663e8e51Sths size_t i; 604663e8e51Sths uint16_t *eeprom_contents = eeprom93xx_data(s->eeprom); 605e7493b25SStefan Weil #if 0 606e7493b25SStefan Weil eeprom93xx_reset(s->eeprom); 607e7493b25SStefan Weil #endif 608508ef936SGerd Hoffmann memcpy(eeprom_contents, s->conf.macaddr.a, 6); 609b1e87018SStefan Weil eeprom_contents[EEPROM_ID] = EEPROM_ID_VALID; 610f4e94dfeS=?UTF-8?q?Reimar=20D=C3=B6ffinger?= if (s->device == i82557B || s->device == i82557C) 611f4e94dfeS=?UTF-8?q?Reimar=20D=C3=B6ffinger?= eeprom_contents[5] = 0x0100; 6126cded3a4SStefan Weil eeprom_contents[EEPROM_PHY_ID] = 1; 613663e8e51Sths uint16_t sum = 0; 614663e8e51Sths for (i = 0; i < EEPROM_SIZE - 1; i++) { 615663e8e51Sths sum += eeprom_contents[i]; 616663e8e51Sths } 617663e8e51Sths eeprom_contents[EEPROM_SIZE - 1] = 0xbaba - sum; 618aac443e6SStefan Weil TRACE(EEPROM, logout("checksum=0x%04x\n", eeprom_contents[EEPROM_SIZE - 1])); 619663e8e51Sths 620663e8e51Sths memset(s->mem, 0, sizeof(s->mem)); 621e5e23ab8SStefan Weil e100_write_reg4(s, SCBCtrlMDI, BIT(21)); 622663e8e51Sths 623663e8e51Sths assert(sizeof(s->mdimem) == sizeof(eepro100_mdi_default)); 624663e8e51Sths memcpy(&s->mdimem[0], &eepro100_mdi_default[0], sizeof(s->mdimem)); 625663e8e51Sths } 626663e8e51Sths 627663e8e51Sths static void nic_reset(void *opaque) 628663e8e51Sths { 629769cf7a5SJuan Quintela EEPRO100State *s = opaque; 630aac443e6SStefan Weil TRACE(OTHER, logout("%p\n", s)); 631010ec629SStefan Weil /* TODO: Clearing of hash register for selective reset, too? */ 6327b8737deSStefan Weil memset(&s->mult[0], 0, sizeof(s->mult)); 633663e8e51Sths nic_selective_reset(s); 634663e8e51Sths } 635663e8e51Sths 636663e8e51Sths #if defined(DEBUG_EEPRO100) 637b8f6ba0dSStefan Weil static const char * const e100_reg[PCI_IO_SIZE / 4] = { 638663e8e51Sths "Command/Status", 639663e8e51Sths "General Pointer", 640663e8e51Sths "Port", 641663e8e51Sths "EEPROM/Flash Control", 642663e8e51Sths "MDI Control", 643663e8e51Sths "Receive DMA Byte Count", 644b8f6ba0dSStefan Weil "Flow Control", 645663e8e51Sths "General Status/Control" 646663e8e51Sths }; 647663e8e51Sths 648663e8e51Sths static char *regname(uint32_t addr) 649663e8e51Sths { 650ec169288SDavid Benjamin static char buf[32]; 651663e8e51Sths if (addr < PCI_IO_SIZE) { 652b8f6ba0dSStefan Weil const char *r = e100_reg[addr / 4]; 653663e8e51Sths if (r != 0) { 65441cbc23cSStefan Weil snprintf(buf, sizeof(buf), "%s+%u", r, addr % 4); 655663e8e51Sths } else { 65641cbc23cSStefan Weil snprintf(buf, sizeof(buf), "0x%02x", addr); 657663e8e51Sths } 658663e8e51Sths } else { 65941cbc23cSStefan Weil snprintf(buf, sizeof(buf), "??? 0x%08x", addr); 660663e8e51Sths } 661663e8e51Sths return buf; 662663e8e51Sths } 663663e8e51Sths #endif /* DEBUG_EEPRO100 */ 664663e8e51Sths 665663e8e51Sths /***************************************************************************** 666663e8e51Sths * 667663e8e51Sths * Command emulation. 668663e8e51Sths * 669663e8e51Sths ****************************************************************************/ 670663e8e51Sths 671663e8e51Sths #if 0 672663e8e51Sths static uint16_t eepro100_read_command(EEPRO100State * s) 673663e8e51Sths { 674663e8e51Sths uint16_t val = 0xffff; 675e7493b25SStefan Weil TRACE(OTHER, logout("val=0x%04x\n", val)); 676663e8e51Sths return val; 677663e8e51Sths } 678663e8e51Sths #endif 679663e8e51Sths 680663e8e51Sths /* Commands that can be put in a command list entry. */ 681663e8e51Sths enum commands { 682663e8e51Sths CmdNOp = 0, 683663e8e51Sths CmdIASetup = 1, 684663e8e51Sths CmdConfigure = 2, 685663e8e51Sths CmdMulticastList = 3, 686663e8e51Sths CmdTx = 4, 687663e8e51Sths CmdTDR = 5, /* load microcode */ 688663e8e51Sths CmdDump = 6, 689663e8e51Sths CmdDiagnose = 7, 690663e8e51Sths 691663e8e51Sths /* And some extra flags: */ 692663e8e51Sths CmdSuspend = 0x4000, /* Suspend after completion. */ 693663e8e51Sths CmdIntr = 0x2000, /* Interrupt after completion. */ 694663e8e51Sths CmdTxFlex = 0x0008, /* Use "Flexible mode" for CmdTx command. */ 695663e8e51Sths }; 696663e8e51Sths 697c227f099SAnthony Liguori static cu_state_t get_cu_state(EEPRO100State * s) 698663e8e51Sths { 699ced5296aSStefan Weil return ((s->mem[SCBStatus] & BITS(7, 6)) >> 6); 700663e8e51Sths } 701663e8e51Sths 702c227f099SAnthony Liguori static void set_cu_state(EEPRO100State * s, cu_state_t state) 703663e8e51Sths { 704ced5296aSStefan Weil s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(7, 6)) + (state << 6); 705663e8e51Sths } 706663e8e51Sths 707c227f099SAnthony Liguori static ru_state_t get_ru_state(EEPRO100State * s) 708663e8e51Sths { 709ced5296aSStefan Weil return ((s->mem[SCBStatus] & BITS(5, 2)) >> 2); 710663e8e51Sths } 711663e8e51Sths 712c227f099SAnthony Liguori static void set_ru_state(EEPRO100State * s, ru_state_t state) 713663e8e51Sths { 714ced5296aSStefan Weil s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(5, 2)) + (state << 2); 715663e8e51Sths } 716663e8e51Sths 717663e8e51Sths static void dump_statistics(EEPRO100State * s) 718663e8e51Sths { 719663e8e51Sths /* Dump statistical data. Most data is never changed by the emulation 720663e8e51Sths * and always 0, so we first just copy the whole block and then those 721663e8e51Sths * values which really matter. 722663e8e51Sths * Number of data should check configuration!!! 723663e8e51Sths */ 724e965d4bcSDavid Gibson pci_dma_write(&s->dev, s->statsaddr, &s->statistics, s->stats_size); 72516ef60c9SEduard - Gabriel Munteanu stl_le_pci_dma(&s->dev, s->statsaddr + 0, 72616ef60c9SEduard - Gabriel Munteanu s->statistics.tx_good_frames); 72716ef60c9SEduard - Gabriel Munteanu stl_le_pci_dma(&s->dev, s->statsaddr + 36, 72816ef60c9SEduard - Gabriel Munteanu s->statistics.rx_good_frames); 72916ef60c9SEduard - Gabriel Munteanu stl_le_pci_dma(&s->dev, s->statsaddr + 48, 73016ef60c9SEduard - Gabriel Munteanu s->statistics.rx_resource_errors); 73116ef60c9SEduard - Gabriel Munteanu stl_le_pci_dma(&s->dev, s->statsaddr + 60, 73216ef60c9SEduard - Gabriel Munteanu s->statistics.rx_short_frame_errors); 733e7493b25SStefan Weil #if 0 73416ef60c9SEduard - Gabriel Munteanu stw_le_pci_dma(&s->dev, s->statsaddr + 76, s->statistics.xmt_tco_frames); 73516ef60c9SEduard - Gabriel Munteanu stw_le_pci_dma(&s->dev, s->statsaddr + 78, s->statistics.rcv_tco_frames); 736e7493b25SStefan Weil missing("CU dump statistical counters"); 737e7493b25SStefan Weil #endif 738663e8e51Sths } 739663e8e51Sths 7403d0f4b9bSStefan Weil static void read_cb(EEPRO100State *s) 7413d0f4b9bSStefan Weil { 742e965d4bcSDavid Gibson pci_dma_read(&s->dev, s->cb_address, &s->tx, sizeof(s->tx)); 7433d0f4b9bSStefan Weil s->tx.status = le16_to_cpu(s->tx.status); 7443d0f4b9bSStefan Weil s->tx.command = le16_to_cpu(s->tx.command); 7453d0f4b9bSStefan Weil s->tx.link = le32_to_cpu(s->tx.link); 7463d0f4b9bSStefan Weil s->tx.tbd_array_addr = le32_to_cpu(s->tx.tbd_array_addr); 7473d0f4b9bSStefan Weil s->tx.tcb_bytes = le16_to_cpu(s->tx.tcb_bytes); 7483d0f4b9bSStefan Weil } 7493d0f4b9bSStefan Weil 750f3a52e50SStefan Weil static void tx_command(EEPRO100State *s) 751663e8e51Sths { 7527b8737deSStefan Weil uint32_t tbd_array = le32_to_cpu(s->tx.tbd_array_addr); 753f3a52e50SStefan Weil uint16_t tcb_bytes = (le16_to_cpu(s->tx.tcb_bytes) & 0x3fff); 754f3a52e50SStefan Weil /* Sends larger than MAX_ETH_FRAME_SIZE are allowed, up to 2600 bytes. */ 755f3a52e50SStefan Weil uint8_t buf[2600]; 756f3a52e50SStefan Weil uint16_t size = 0; 757f3a52e50SStefan Weil uint32_t tbd_address = s->cb_address + 0x10; 758aac443e6SStefan Weil TRACE(RXTX, logout 759663e8e51Sths ("transmit, TBD array address 0x%08x, TCB byte count 0x%04x, TBD count %u\n", 760f3a52e50SStefan Weil tbd_array, tcb_bytes, s->tx.tbd_count)); 7617f1e9d4eSKevin Wolf 7627f1e9d4eSKevin Wolf if (tcb_bytes > 2600) { 7637f1e9d4eSKevin Wolf logout("TCB byte count too large, using 2600\n"); 7647f1e9d4eSKevin Wolf tcb_bytes = 2600; 7657f1e9d4eSKevin Wolf } 766663e8e51Sths if (!((tcb_bytes > 0) || (tbd_array != 0xffffffff))) { 767663e8e51Sths logout 768663e8e51Sths ("illegal values of TBD array address and TCB byte count!\n"); 769663e8e51Sths } 770663e8e51Sths assert(tcb_bytes <= sizeof(buf)); 771663e8e51Sths while (size < tcb_bytes) { 77216ef60c9SEduard - Gabriel Munteanu uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev, tbd_address); 77316ef60c9SEduard - Gabriel Munteanu uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev, tbd_address + 4); 774e7493b25SStefan Weil #if 0 77516ef60c9SEduard - Gabriel Munteanu uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev, tbd_address + 6); 776e7493b25SStefan Weil #endif 777663e8e51Sths tbd_address += 8; 778aac443e6SStefan Weil TRACE(RXTX, logout 779663e8e51Sths ("TBD (simplified mode): buffer address 0x%08x, size 0x%04x\n", 780aac443e6SStefan Weil tx_buffer_address, tx_buffer_size)); 78124e6f355SReimar Döffinger tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size); 78216ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, tx_buffer_address, &buf[size], tx_buffer_size); 783663e8e51Sths size += tx_buffer_size; 784663e8e51Sths } 785663e8e51Sths if (tbd_array == 0xffffffff) { 786663e8e51Sths /* Simplified mode. Was already handled by code above. */ 787663e8e51Sths } else { 788663e8e51Sths /* Flexible mode. */ 789663e8e51Sths uint8_t tbd_count = 0; 790ba42b646SStefan Weil if (s->has_extended_tcb_support && !(s->configuration[6] & BIT(4))) { 7913f9cb1c1SNaphtali Sprei /* Extended Flexible TCB. */ 792663e8e51Sths for (; tbd_count < 2; tbd_count++) { 79316ef60c9SEduard - Gabriel Munteanu uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev, 79416ef60c9SEduard - Gabriel Munteanu tbd_address); 79516ef60c9SEduard - Gabriel Munteanu uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev, 79616ef60c9SEduard - Gabriel Munteanu tbd_address + 4); 79716ef60c9SEduard - Gabriel Munteanu uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev, 79816ef60c9SEduard - Gabriel Munteanu tbd_address + 6); 799663e8e51Sths tbd_address += 8; 800aac443e6SStefan Weil TRACE(RXTX, logout 8013f9cb1c1SNaphtali Sprei ("TBD (extended flexible mode): buffer address 0x%08x, size 0x%04x\n", 802aac443e6SStefan Weil tx_buffer_address, tx_buffer_size)); 80324e6f355SReimar Döffinger tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size); 80416ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, tx_buffer_address, 80516ef60c9SEduard - Gabriel Munteanu &buf[size], tx_buffer_size); 806663e8e51Sths size += tx_buffer_size; 807663e8e51Sths if (tx_buffer_el & 1) { 808663e8e51Sths break; 809663e8e51Sths } 810663e8e51Sths } 811663e8e51Sths } 812663e8e51Sths tbd_address = tbd_array; 813f3a52e50SStefan Weil for (; tbd_count < s->tx.tbd_count; tbd_count++) { 81416ef60c9SEduard - Gabriel Munteanu uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev, tbd_address); 81516ef60c9SEduard - Gabriel Munteanu uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev, tbd_address + 4); 81616ef60c9SEduard - Gabriel Munteanu uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev, tbd_address + 6); 817663e8e51Sths tbd_address += 8; 818aac443e6SStefan Weil TRACE(RXTX, logout 819663e8e51Sths ("TBD (flexible mode): buffer address 0x%08x, size 0x%04x\n", 820aac443e6SStefan Weil tx_buffer_address, tx_buffer_size)); 82124e6f355SReimar Döffinger tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size); 82216ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, tx_buffer_address, 82316ef60c9SEduard - Gabriel Munteanu &buf[size], tx_buffer_size); 824663e8e51Sths size += tx_buffer_size; 825663e8e51Sths if (tx_buffer_el & 1) { 826663e8e51Sths break; 827663e8e51Sths } 828663e8e51Sths } 829663e8e51Sths } 830aac443e6SStefan Weil TRACE(RXTX, logout("%p sending frame, len=%d,%s\n", s, size, nic_dump(buf, size))); 831b356f76dSJason Wang qemu_send_packet(qemu_get_queue(s->nic), buf, size); 832663e8e51Sths s->statistics.tx_good_frames++; 833663e8e51Sths /* Transmit with bad status would raise an CX/TNO interrupt. 834663e8e51Sths * (82557 only). Emulation never has bad status. */ 835e7493b25SStefan Weil #if 0 836e7493b25SStefan Weil eepro100_cx_interrupt(s); 837e7493b25SStefan Weil #endif 838f3a52e50SStefan Weil } 839f3a52e50SStefan Weil 8407b8737deSStefan Weil static void set_multicast_list(EEPRO100State *s) 8417b8737deSStefan Weil { 8427b8737deSStefan Weil uint16_t multicast_count = s->tx.tbd_array_addr & BITS(13, 0); 8437b8737deSStefan Weil uint16_t i; 8447b8737deSStefan Weil memset(&s->mult[0], 0, sizeof(s->mult)); 8457b8737deSStefan Weil TRACE(OTHER, logout("multicast list, multicast count = %u\n", multicast_count)); 8467b8737deSStefan Weil for (i = 0; i < multicast_count; i += 6) { 8477b8737deSStefan Weil uint8_t multicast_addr[6]; 84816ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, s->cb_address + 10 + i, multicast_addr, 6); 8497b8737deSStefan Weil TRACE(OTHER, logout("multicast entry %s\n", nic_dump(multicast_addr, 6))); 85069f3ce78SStefan Weil unsigned mcast_idx = e100_compute_mcast_idx(multicast_addr); 8517b8737deSStefan Weil assert(mcast_idx < 64); 8527b8737deSStefan Weil s->mult[mcast_idx >> 3] |= (1 << (mcast_idx & 7)); 8537b8737deSStefan Weil } 8547b8737deSStefan Weil } 8557b8737deSStefan Weil 856f3a52e50SStefan Weil static void action_command(EEPRO100State *s) 857f3a52e50SStefan Weil { 858f3a52e50SStefan Weil for (;;) { 8593d0f4b9bSStefan Weil bool bit_el; 8603d0f4b9bSStefan Weil bool bit_s; 8613d0f4b9bSStefan Weil bool bit_i; 8623d0f4b9bSStefan Weil bool bit_nc; 86375f5a6ccSStefan Weil uint16_t ok_status = STATUS_OK; 8643d0f4b9bSStefan Weil s->cb_address = s->cu_base + s->cu_offset; 8653d0f4b9bSStefan Weil read_cb(s); 8663d0f4b9bSStefan Weil bit_el = ((s->tx.command & COMMAND_EL) != 0); 8673d0f4b9bSStefan Weil bit_s = ((s->tx.command & COMMAND_S) != 0); 8683d0f4b9bSStefan Weil bit_i = ((s->tx.command & COMMAND_I) != 0); 8693d0f4b9bSStefan Weil bit_nc = ((s->tx.command & COMMAND_NC) != 0); 8703d0f4b9bSStefan Weil #if 0 8713d0f4b9bSStefan Weil bool bit_sf = ((s->tx.command & COMMAND_SF) != 0); 8723d0f4b9bSStefan Weil #endif 8733d0f4b9bSStefan Weil s->cu_offset = s->tx.link; 8743d0f4b9bSStefan Weil TRACE(OTHER, 8753d0f4b9bSStefan Weil logout("val=(cu start), status=0x%04x, command=0x%04x, link=0x%08x\n", 8763d0f4b9bSStefan Weil s->tx.status, s->tx.command, s->tx.link)); 8773d0f4b9bSStefan Weil switch (s->tx.command & COMMAND_CMD) { 878f3a52e50SStefan Weil case CmdNOp: 879f3a52e50SStefan Weil /* Do nothing. */ 880f3a52e50SStefan Weil break; 881f3a52e50SStefan Weil case CmdIASetup: 88216ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, s->cb_address + 8, &s->conf.macaddr.a[0], 6); 883ce0e58b3SStefan Weil TRACE(OTHER, logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6))); 884f3a52e50SStefan Weil break; 885f3a52e50SStefan Weil case CmdConfigure: 88616ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, s->cb_address + 8, 88716ef60c9SEduard - Gabriel Munteanu &s->configuration[0], sizeof(s->configuration)); 888010ec629SStefan Weil TRACE(OTHER, logout("configuration: %s\n", 889010ec629SStefan Weil nic_dump(&s->configuration[0], 16))); 890010ec629SStefan Weil TRACE(OTHER, logout("configuration: %s\n", 891010ec629SStefan Weil nic_dump(&s->configuration[16], 892010ec629SStefan Weil ARRAY_SIZE(s->configuration) - 16))); 893010ec629SStefan Weil if (s->configuration[20] & BIT(6)) { 894010ec629SStefan Weil TRACE(OTHER, logout("Multiple IA bit\n")); 895010ec629SStefan Weil } 896f3a52e50SStefan Weil break; 897f3a52e50SStefan Weil case CmdMulticastList: 8987b8737deSStefan Weil set_multicast_list(s); 899f3a52e50SStefan Weil break; 900f3a52e50SStefan Weil case CmdTx: 901f3a52e50SStefan Weil if (bit_nc) { 902f3a52e50SStefan Weil missing("CmdTx: NC = 0"); 90375f5a6ccSStefan Weil ok_status = 0; 904f3a52e50SStefan Weil break; 905f3a52e50SStefan Weil } 906f3a52e50SStefan Weil tx_command(s); 907663e8e51Sths break; 908663e8e51Sths case CmdTDR: 909aac443e6SStefan Weil TRACE(OTHER, logout("load microcode\n")); 910663e8e51Sths /* Starting with offset 8, the command contains 911663e8e51Sths * 64 dwords microcode which we just ignore here. */ 912663e8e51Sths break; 913f80a7fc3SStefan Weil case CmdDiagnose: 914f80a7fc3SStefan Weil TRACE(OTHER, logout("diagnose\n")); 915f80a7fc3SStefan Weil /* Make sure error flag is not set. */ 916f80a7fc3SStefan Weil s->tx.status = 0; 917f80a7fc3SStefan Weil break; 918663e8e51Sths default: 919663e8e51Sths missing("undefined command"); 92075f5a6ccSStefan Weil ok_status = 0; 9217f1e9d4eSKevin Wolf break; 922663e8e51Sths } 9237f1e9d4eSKevin Wolf /* Write new status. */ 92416ef60c9SEduard - Gabriel Munteanu stw_le_pci_dma(&s->dev, s->cb_address, 92516ef60c9SEduard - Gabriel Munteanu s->tx.status | ok_status | STATUS_C); 926663e8e51Sths if (bit_i) { 927663e8e51Sths /* CU completed action. */ 928663e8e51Sths eepro100_cx_interrupt(s); 929663e8e51Sths } 930663e8e51Sths if (bit_el) { 931aac443e6SStefan Weil /* CU becomes idle. Terminate command loop. */ 932663e8e51Sths set_cu_state(s, cu_idle); 933663e8e51Sths eepro100_cna_interrupt(s); 9345fa9a0aeSStefan Weil break; 935663e8e51Sths } else if (bit_s) { 9365fa9a0aeSStefan Weil /* CU becomes suspended. Terminate command loop. */ 937663e8e51Sths set_cu_state(s, cu_suspended); 938663e8e51Sths eepro100_cna_interrupt(s); 9395fa9a0aeSStefan Weil break; 940663e8e51Sths } else { 941663e8e51Sths /* More entries in list. */ 942aac443e6SStefan Weil TRACE(OTHER, logout("CU list with at least one more entry\n")); 9435fa9a0aeSStefan Weil } 944663e8e51Sths } 945aac443e6SStefan Weil TRACE(OTHER, logout("CU list empty\n")); 946663e8e51Sths /* List is empty. Now CU is idle or suspended. */ 9475fa9a0aeSStefan Weil } 9485fa9a0aeSStefan Weil 9495fa9a0aeSStefan Weil static void eepro100_cu_command(EEPRO100State * s, uint8_t val) 9505fa9a0aeSStefan Weil { 951cb25a3fbSStefan Weil cu_state_t cu_state; 9525fa9a0aeSStefan Weil switch (val) { 9535fa9a0aeSStefan Weil case CU_NOP: 9545fa9a0aeSStefan Weil /* No operation. */ 9555fa9a0aeSStefan Weil break; 9565fa9a0aeSStefan Weil case CU_START: 957cb25a3fbSStefan Weil cu_state = get_cu_state(s); 958cb25a3fbSStefan Weil if (cu_state != cu_idle && cu_state != cu_suspended) { 959cb25a3fbSStefan Weil /* Intel documentation says that CU must be idle or suspended 960cb25a3fbSStefan Weil * for the CU start command. */ 961cb25a3fbSStefan Weil logout("unexpected CU state is %u\n", cu_state); 9625fa9a0aeSStefan Weil } 9635fa9a0aeSStefan Weil set_cu_state(s, cu_active); 96427a05006SStefan Weil s->cu_offset = e100_read_reg4(s, SCBPointer); 9655fa9a0aeSStefan Weil action_command(s); 966663e8e51Sths break; 967663e8e51Sths case CU_RESUME: 968663e8e51Sths if (get_cu_state(s) != cu_suspended) { 969663e8e51Sths logout("bad CU resume from CU state %u\n", get_cu_state(s)); 970663e8e51Sths /* Workaround for bad Linux eepro100 driver which resumes 971663e8e51Sths * from idle state. */ 972e7493b25SStefan Weil #if 0 973e7493b25SStefan Weil missing("cu resume"); 974e7493b25SStefan Weil #endif 975663e8e51Sths set_cu_state(s, cu_suspended); 976663e8e51Sths } 977663e8e51Sths if (get_cu_state(s) == cu_suspended) { 978aac443e6SStefan Weil TRACE(OTHER, logout("CU resuming\n")); 979663e8e51Sths set_cu_state(s, cu_active); 9805fa9a0aeSStefan Weil action_command(s); 981663e8e51Sths } 982663e8e51Sths break; 983663e8e51Sths case CU_STATSADDR: 984663e8e51Sths /* Load dump counters address. */ 98527a05006SStefan Weil s->statsaddr = e100_read_reg4(s, SCBPointer); 986c16ada98SStefan Weil TRACE(OTHER, logout("val=0x%02x (dump counters address)\n", val)); 987c16ada98SStefan Weil if (s->statsaddr & 3) { 988c16ada98SStefan Weil /* Memory must be Dword aligned. */ 989c16ada98SStefan Weil logout("unaligned dump counters address\n"); 990c16ada98SStefan Weil /* Handling of misaligned addresses is undefined. 991c16ada98SStefan Weil * Here we align the address by ignoring the lower bits. */ 992c16ada98SStefan Weil /* TODO: Test unaligned dump counter address on real hardware. */ 993c16ada98SStefan Weil s->statsaddr &= ~3; 994c16ada98SStefan Weil } 995663e8e51Sths break; 996663e8e51Sths case CU_SHOWSTATS: 997663e8e51Sths /* Dump statistical counters. */ 998aac443e6SStefan Weil TRACE(OTHER, logout("val=0x%02x (dump stats)\n", val)); 999663e8e51Sths dump_statistics(s); 100016ef60c9SEduard - Gabriel Munteanu stl_le_pci_dma(&s->dev, s->statsaddr + s->stats_size, 0xa005); 1001663e8e51Sths break; 1002663e8e51Sths case CU_CMD_BASE: 1003663e8e51Sths /* Load CU base. */ 1004aac443e6SStefan Weil TRACE(OTHER, logout("val=0x%02x (CU base address)\n", val)); 100527a05006SStefan Weil s->cu_base = e100_read_reg4(s, SCBPointer); 1006663e8e51Sths break; 1007663e8e51Sths case CU_DUMPSTATS: 1008663e8e51Sths /* Dump and reset statistical counters. */ 1009aac443e6SStefan Weil TRACE(OTHER, logout("val=0x%02x (dump stats and reset)\n", val)); 1010663e8e51Sths dump_statistics(s); 101116ef60c9SEduard - Gabriel Munteanu stl_le_pci_dma(&s->dev, s->statsaddr + s->stats_size, 0xa007); 1012663e8e51Sths memset(&s->statistics, 0, sizeof(s->statistics)); 1013663e8e51Sths break; 1014663e8e51Sths case CU_SRESUME: 1015663e8e51Sths /* CU static resume. */ 1016663e8e51Sths missing("CU static resume"); 1017663e8e51Sths break; 1018663e8e51Sths default: 1019663e8e51Sths missing("Undefined CU command"); 1020663e8e51Sths } 1021663e8e51Sths } 1022663e8e51Sths 1023663e8e51Sths static void eepro100_ru_command(EEPRO100State * s, uint8_t val) 1024663e8e51Sths { 1025663e8e51Sths switch (val) { 1026663e8e51Sths case RU_NOP: 1027663e8e51Sths /* No operation. */ 1028663e8e51Sths break; 1029663e8e51Sths case RX_START: 1030663e8e51Sths /* RU start. */ 1031663e8e51Sths if (get_ru_state(s) != ru_idle) { 1032663e8e51Sths logout("RU state is %u, should be %u\n", get_ru_state(s), ru_idle); 1033e7493b25SStefan Weil #if 0 1034e7493b25SStefan Weil assert(!"wrong RU state"); 1035e7493b25SStefan Weil #endif 1036663e8e51Sths } 1037663e8e51Sths set_ru_state(s, ru_ready); 103827a05006SStefan Weil s->ru_offset = e100_read_reg4(s, SCBPointer); 1039b356f76dSJason Wang qemu_flush_queued_packets(qemu_get_queue(s->nic)); 1040aac443e6SStefan Weil TRACE(OTHER, logout("val=0x%02x (rx start)\n", val)); 1041663e8e51Sths break; 1042663e8e51Sths case RX_RESUME: 1043663e8e51Sths /* Restart RU. */ 1044663e8e51Sths if (get_ru_state(s) != ru_suspended) { 1045663e8e51Sths logout("RU state is %u, should be %u\n", get_ru_state(s), 1046663e8e51Sths ru_suspended); 1047e7493b25SStefan Weil #if 0 1048e7493b25SStefan Weil assert(!"wrong RU state"); 1049e7493b25SStefan Weil #endif 1050663e8e51Sths } 1051663e8e51Sths set_ru_state(s, ru_ready); 1052663e8e51Sths break; 1053e824012bSStefan Weil case RU_ABORT: 1054e824012bSStefan Weil /* RU abort. */ 1055e824012bSStefan Weil if (get_ru_state(s) == ru_ready) { 1056e824012bSStefan Weil eepro100_rnr_interrupt(s); 1057e824012bSStefan Weil } 1058e824012bSStefan Weil set_ru_state(s, ru_idle); 1059e824012bSStefan Weil break; 1060663e8e51Sths case RX_ADDR_LOAD: 1061663e8e51Sths /* Load RU base. */ 1062aac443e6SStefan Weil TRACE(OTHER, logout("val=0x%02x (RU base address)\n", val)); 106327a05006SStefan Weil s->ru_base = e100_read_reg4(s, SCBPointer); 1064663e8e51Sths break; 1065663e8e51Sths default: 1066663e8e51Sths logout("val=0x%02x (undefined RU command)\n", val); 1067663e8e51Sths missing("Undefined SU command"); 1068663e8e51Sths } 1069663e8e51Sths } 1070663e8e51Sths 1071663e8e51Sths static void eepro100_write_command(EEPRO100State * s, uint8_t val) 1072663e8e51Sths { 1073663e8e51Sths eepro100_ru_command(s, val & 0x0f); 1074663e8e51Sths eepro100_cu_command(s, val & 0xf0); 1075663e8e51Sths if ((val) == 0) { 1076aac443e6SStefan Weil TRACE(OTHER, logout("val=0x%02x\n", val)); 1077663e8e51Sths } 1078663e8e51Sths /* Clear command byte after command was accepted. */ 1079663e8e51Sths s->mem[SCBCmd] = 0; 1080663e8e51Sths } 1081663e8e51Sths 1082663e8e51Sths /***************************************************************************** 1083663e8e51Sths * 1084663e8e51Sths * EEPROM emulation. 1085663e8e51Sths * 1086663e8e51Sths ****************************************************************************/ 1087663e8e51Sths 1088663e8e51Sths #define EEPROM_CS 0x02 1089663e8e51Sths #define EEPROM_SK 0x01 1090663e8e51Sths #define EEPROM_DI 0x04 1091663e8e51Sths #define EEPROM_DO 0x08 1092663e8e51Sths 1093663e8e51Sths static uint16_t eepro100_read_eeprom(EEPRO100State * s) 1094663e8e51Sths { 1095e5e23ab8SStefan Weil uint16_t val = e100_read_reg2(s, SCBeeprom); 1096663e8e51Sths if (eeprom93xx_read(s->eeprom)) { 1097663e8e51Sths val |= EEPROM_DO; 1098663e8e51Sths } else { 1099663e8e51Sths val &= ~EEPROM_DO; 1100663e8e51Sths } 1101aac443e6SStefan Weil TRACE(EEPROM, logout("val=0x%04x\n", val)); 1102663e8e51Sths return val; 1103663e8e51Sths } 1104663e8e51Sths 1105c227f099SAnthony Liguori static void eepro100_write_eeprom(eeprom_t * eeprom, uint8_t val) 1106663e8e51Sths { 1107aac443e6SStefan Weil TRACE(EEPROM, logout("val=0x%02x\n", val)); 1108663e8e51Sths 1109ebabb67aSStefan Weil /* mask unwritable bits */ 1110e7493b25SStefan Weil #if 0 1111e7493b25SStefan Weil val = SET_MASKED(val, 0x31, eeprom->value); 1112e7493b25SStefan Weil #endif 1113663e8e51Sths 1114663e8e51Sths int eecs = ((val & EEPROM_CS) != 0); 1115663e8e51Sths int eesk = ((val & EEPROM_SK) != 0); 1116663e8e51Sths int eedi = ((val & EEPROM_DI) != 0); 1117663e8e51Sths eeprom93xx_write(eeprom, eecs, eesk, eedi); 1118663e8e51Sths } 1119663e8e51Sths 1120663e8e51Sths /***************************************************************************** 1121663e8e51Sths * 1122663e8e51Sths * MDI emulation. 1123663e8e51Sths * 1124663e8e51Sths ****************************************************************************/ 1125663e8e51Sths 1126663e8e51Sths #if defined(DEBUG_EEPRO100) 11276a0b9cc9SReimar Döffinger static const char * const mdi_op_name[] = { 1128663e8e51Sths "opcode 0", 1129663e8e51Sths "write", 1130663e8e51Sths "read", 1131663e8e51Sths "opcode 3" 1132663e8e51Sths }; 1133663e8e51Sths 11346a0b9cc9SReimar Döffinger static const char * const mdi_reg_name[] = { 1135663e8e51Sths "Control", 1136663e8e51Sths "Status", 1137663e8e51Sths "PHY Identification (Word 1)", 1138663e8e51Sths "PHY Identification (Word 2)", 1139663e8e51Sths "Auto-Negotiation Advertisement", 1140663e8e51Sths "Auto-Negotiation Link Partner Ability", 1141663e8e51Sths "Auto-Negotiation Expansion" 1142663e8e51Sths }; 1143aac443e6SStefan Weil 1144aac443e6SStefan Weil static const char *reg2name(uint8_t reg) 1145aac443e6SStefan Weil { 1146aac443e6SStefan Weil static char buffer[10]; 1147aac443e6SStefan Weil const char *p = buffer; 1148aac443e6SStefan Weil if (reg < ARRAY_SIZE(mdi_reg_name)) { 1149aac443e6SStefan Weil p = mdi_reg_name[reg]; 1150aac443e6SStefan Weil } else { 1151aac443e6SStefan Weil snprintf(buffer, sizeof(buffer), "reg=0x%02x", reg); 1152aac443e6SStefan Weil } 1153aac443e6SStefan Weil return p; 1154aac443e6SStefan Weil } 1155663e8e51Sths #endif /* DEBUG_EEPRO100 */ 1156663e8e51Sths 1157663e8e51Sths static uint32_t eepro100_read_mdi(EEPRO100State * s) 1158663e8e51Sths { 1159e5e23ab8SStefan Weil uint32_t val = e100_read_reg4(s, SCBCtrlMDI); 1160663e8e51Sths 1161663e8e51Sths #ifdef DEBUG_EEPRO100 1162663e8e51Sths uint8_t raiseint = (val & BIT(29)) >> 29; 1163663e8e51Sths uint8_t opcode = (val & BITS(27, 26)) >> 26; 1164663e8e51Sths uint8_t phy = (val & BITS(25, 21)) >> 21; 1165663e8e51Sths uint8_t reg = (val & BITS(20, 16)) >> 16; 1166663e8e51Sths uint16_t data = (val & BITS(15, 0)); 1167663e8e51Sths #endif 1168663e8e51Sths /* Emulation takes no time to finish MDI transaction. */ 1169663e8e51Sths val |= BIT(28); 1170663e8e51Sths TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n", 1171663e8e51Sths val, raiseint, mdi_op_name[opcode], phy, 1172aac443e6SStefan Weil reg2name(reg), data)); 1173663e8e51Sths return val; 1174663e8e51Sths } 1175663e8e51Sths 11760113f48dSStefan Weil static void eepro100_write_mdi(EEPRO100State *s) 1177663e8e51Sths { 11780113f48dSStefan Weil uint32_t val = e100_read_reg4(s, SCBCtrlMDI); 1179663e8e51Sths uint8_t raiseint = (val & BIT(29)) >> 29; 1180663e8e51Sths uint8_t opcode = (val & BITS(27, 26)) >> 26; 1181663e8e51Sths uint8_t phy = (val & BITS(25, 21)) >> 21; 1182663e8e51Sths uint8_t reg = (val & BITS(20, 16)) >> 16; 1183663e8e51Sths uint16_t data = (val & BITS(15, 0)); 1184aac443e6SStefan Weil TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n", 1185aac443e6SStefan Weil val, raiseint, mdi_op_name[opcode], phy, reg2name(reg), data)); 1186663e8e51Sths if (phy != 1) { 1187663e8e51Sths /* Unsupported PHY address. */ 1188e7493b25SStefan Weil #if 0 1189e7493b25SStefan Weil logout("phy must be 1 but is %u\n", phy); 1190e7493b25SStefan Weil #endif 1191663e8e51Sths data = 0; 1192663e8e51Sths } else if (opcode != 1 && opcode != 2) { 1193663e8e51Sths /* Unsupported opcode. */ 1194663e8e51Sths logout("opcode must be 1 or 2 but is %u\n", opcode); 1195663e8e51Sths data = 0; 1196663e8e51Sths } else if (reg > 6) { 1197663e8e51Sths /* Unsupported register. */ 1198663e8e51Sths logout("register must be 0...6 but is %u\n", reg); 1199663e8e51Sths data = 0; 1200663e8e51Sths } else { 1201663e8e51Sths TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n", 1202663e8e51Sths val, raiseint, mdi_op_name[opcode], phy, 1203aac443e6SStefan Weil reg2name(reg), data)); 1204663e8e51Sths if (opcode == 1) { 1205663e8e51Sths /* MDI write */ 1206663e8e51Sths switch (reg) { 1207663e8e51Sths case 0: /* Control Register */ 1208663e8e51Sths if (data & 0x8000) { 1209663e8e51Sths /* Reset status and control registers to default. */ 1210663e8e51Sths s->mdimem[0] = eepro100_mdi_default[0]; 1211663e8e51Sths s->mdimem[1] = eepro100_mdi_default[1]; 1212663e8e51Sths data = s->mdimem[reg]; 1213663e8e51Sths } else { 1214663e8e51Sths /* Restart Auto Configuration = Normal Operation */ 1215663e8e51Sths data &= ~0x0200; 1216663e8e51Sths } 1217663e8e51Sths break; 1218663e8e51Sths case 1: /* Status Register */ 1219663e8e51Sths missing("not writable"); 1220663e8e51Sths data = s->mdimem[reg]; 1221663e8e51Sths break; 1222663e8e51Sths case 2: /* PHY Identification Register (Word 1) */ 1223663e8e51Sths case 3: /* PHY Identification Register (Word 2) */ 1224663e8e51Sths missing("not implemented"); 1225663e8e51Sths break; 1226663e8e51Sths case 4: /* Auto-Negotiation Advertisement Register */ 1227663e8e51Sths case 5: /* Auto-Negotiation Link Partner Ability Register */ 1228663e8e51Sths break; 1229663e8e51Sths case 6: /* Auto-Negotiation Expansion Register */ 1230663e8e51Sths default: 1231663e8e51Sths missing("not implemented"); 1232663e8e51Sths } 1233663e8e51Sths s->mdimem[reg] = data; 1234663e8e51Sths } else if (opcode == 2) { 1235663e8e51Sths /* MDI read */ 1236663e8e51Sths switch (reg) { 1237663e8e51Sths case 0: /* Control Register */ 1238663e8e51Sths if (data & 0x8000) { 1239663e8e51Sths /* Reset status and control registers to default. */ 1240663e8e51Sths s->mdimem[0] = eepro100_mdi_default[0]; 1241663e8e51Sths s->mdimem[1] = eepro100_mdi_default[1]; 1242663e8e51Sths } 1243663e8e51Sths break; 1244663e8e51Sths case 1: /* Status Register */ 1245663e8e51Sths s->mdimem[reg] |= 0x0020; 1246663e8e51Sths break; 1247663e8e51Sths case 2: /* PHY Identification Register (Word 1) */ 1248663e8e51Sths case 3: /* PHY Identification Register (Word 2) */ 1249663e8e51Sths case 4: /* Auto-Negotiation Advertisement Register */ 1250663e8e51Sths break; 1251663e8e51Sths case 5: /* Auto-Negotiation Link Partner Ability Register */ 1252663e8e51Sths s->mdimem[reg] = 0x41fe; 1253663e8e51Sths break; 1254663e8e51Sths case 6: /* Auto-Negotiation Expansion Register */ 1255663e8e51Sths s->mdimem[reg] = 0x0001; 1256663e8e51Sths break; 1257663e8e51Sths } 1258663e8e51Sths data = s->mdimem[reg]; 1259663e8e51Sths } 1260663e8e51Sths /* Emulation takes no time to finish MDI transaction. 1261663e8e51Sths * Set MDI bit in SCB status register. */ 1262663e8e51Sths s->mem[SCBAck] |= 0x08; 1263663e8e51Sths val |= BIT(28); 1264663e8e51Sths if (raiseint) { 1265663e8e51Sths eepro100_mdi_interrupt(s); 1266663e8e51Sths } 1267663e8e51Sths } 1268663e8e51Sths val = (val & 0xffff0000) + data; 1269e5e23ab8SStefan Weil e100_write_reg4(s, SCBCtrlMDI, val); 1270663e8e51Sths } 1271663e8e51Sths 1272663e8e51Sths /***************************************************************************** 1273663e8e51Sths * 1274663e8e51Sths * Port emulation. 1275663e8e51Sths * 1276663e8e51Sths ****************************************************************************/ 1277663e8e51Sths 1278663e8e51Sths #define PORT_SOFTWARE_RESET 0 1279663e8e51Sths #define PORT_SELFTEST 1 1280663e8e51Sths #define PORT_SELECTIVE_RESET 2 1281663e8e51Sths #define PORT_DUMP 3 1282663e8e51Sths #define PORT_SELECTION_MASK 3 1283663e8e51Sths 1284663e8e51Sths typedef struct { 1285663e8e51Sths uint32_t st_sign; /* Self Test Signature */ 1286663e8e51Sths uint32_t st_result; /* Self Test Results */ 1287c227f099SAnthony Liguori } eepro100_selftest_t; 1288663e8e51Sths 1289663e8e51Sths static uint32_t eepro100_read_port(EEPRO100State * s) 1290663e8e51Sths { 1291663e8e51Sths return 0; 1292663e8e51Sths } 1293663e8e51Sths 12943fd3d0b4SStefan Weil static void eepro100_write_port(EEPRO100State *s) 1295663e8e51Sths { 12963fd3d0b4SStefan Weil uint32_t val = e100_read_reg4(s, SCBPort); 1297663e8e51Sths uint32_t address = (val & ~PORT_SELECTION_MASK); 1298663e8e51Sths uint8_t selection = (val & PORT_SELECTION_MASK); 1299663e8e51Sths switch (selection) { 1300663e8e51Sths case PORT_SOFTWARE_RESET: 1301663e8e51Sths nic_reset(s); 1302663e8e51Sths break; 1303663e8e51Sths case PORT_SELFTEST: 1304aac443e6SStefan Weil TRACE(OTHER, logout("selftest address=0x%08x\n", address)); 1305c227f099SAnthony Liguori eepro100_selftest_t data; 130616ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, address, (uint8_t *) &data, sizeof(data)); 1307663e8e51Sths data.st_sign = 0xffffffff; 1308663e8e51Sths data.st_result = 0; 130916ef60c9SEduard - Gabriel Munteanu pci_dma_write(&s->dev, address, (uint8_t *) &data, sizeof(data)); 1310663e8e51Sths break; 1311663e8e51Sths case PORT_SELECTIVE_RESET: 1312aac443e6SStefan Weil TRACE(OTHER, logout("selective reset, selftest address=0x%08x\n", address)); 1313663e8e51Sths nic_selective_reset(s); 1314663e8e51Sths break; 1315663e8e51Sths default: 1316663e8e51Sths logout("val=0x%08x\n", val); 1317663e8e51Sths missing("unknown port selection"); 1318663e8e51Sths } 1319663e8e51Sths } 1320663e8e51Sths 1321663e8e51Sths /***************************************************************************** 1322663e8e51Sths * 1323663e8e51Sths * General hardware emulation. 1324663e8e51Sths * 1325663e8e51Sths ****************************************************************************/ 1326663e8e51Sths 1327663e8e51Sths static uint8_t eepro100_read1(EEPRO100State * s, uint32_t addr) 1328663e8e51Sths { 1329ef476062SBlue Swirl uint8_t val = 0; 1330663e8e51Sths if (addr <= sizeof(s->mem) - sizeof(val)) { 1331e5e23ab8SStefan Weil val = s->mem[addr]; 1332663e8e51Sths } 1333663e8e51Sths 1334663e8e51Sths switch (addr) { 1335663e8e51Sths case SCBStatus: 1336663e8e51Sths case SCBAck: 1337aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1338663e8e51Sths break; 1339663e8e51Sths case SCBCmd: 1340aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1341e7493b25SStefan Weil #if 0 1342e7493b25SStefan Weil val = eepro100_read_command(s); 1343e7493b25SStefan Weil #endif 1344663e8e51Sths break; 1345663e8e51Sths case SCBIntmask: 1346aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1347663e8e51Sths break; 1348663e8e51Sths case SCBPort + 3: 1349aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1350663e8e51Sths break; 1351663e8e51Sths case SCBeeprom: 1352663e8e51Sths val = eepro100_read_eeprom(s); 1353663e8e51Sths break; 13540113f48dSStefan Weil case SCBCtrlMDI: 13550113f48dSStefan Weil case SCBCtrlMDI + 1: 13560113f48dSStefan Weil case SCBCtrlMDI + 2: 13570113f48dSStefan Weil case SCBCtrlMDI + 3: 13580113f48dSStefan Weil val = (uint8_t)(eepro100_read_mdi(s) >> (8 * (addr & 3))); 13590113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 13600113f48dSStefan Weil break; 13610908bba1SStefan Weil case SCBpmdr: /* Power Management Driver Register */ 1362663e8e51Sths val = 0; 1363aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1364663e8e51Sths break; 1365a39bd017SStefan Weil case SCBgctrl: /* General Control Register */ 1366a39bd017SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1367a39bd017SStefan Weil break; 13680908bba1SStefan Weil case SCBgstat: /* General Status Register */ 1369663e8e51Sths /* 100 Mbps full duplex, valid link */ 1370663e8e51Sths val = 0x07; 1371aac443e6SStefan Weil TRACE(OTHER, logout("addr=General Status val=%02x\n", val)); 1372663e8e51Sths break; 1373663e8e51Sths default: 1374663e8e51Sths logout("addr=%s val=0x%02x\n", regname(addr), val); 1375663e8e51Sths missing("unknown byte read"); 1376663e8e51Sths } 1377663e8e51Sths return val; 1378663e8e51Sths } 1379663e8e51Sths 1380663e8e51Sths static uint16_t eepro100_read2(EEPRO100State * s, uint32_t addr) 1381663e8e51Sths { 1382ef476062SBlue Swirl uint16_t val = 0; 1383663e8e51Sths if (addr <= sizeof(s->mem) - sizeof(val)) { 1384e5e23ab8SStefan Weil val = e100_read_reg2(s, addr); 1385663e8e51Sths } 1386663e8e51Sths 1387663e8e51Sths switch (addr) { 1388663e8e51Sths case SCBStatus: 1389dbbaaff6S=?UTF-8?q?Reimar=20D=C3=B6ffinger?= case SCBCmd: 1390aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 1391663e8e51Sths break; 1392663e8e51Sths case SCBeeprom: 1393663e8e51Sths val = eepro100_read_eeprom(s); 1394aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 1395663e8e51Sths break; 13960113f48dSStefan Weil case SCBCtrlMDI: 13970113f48dSStefan Weil case SCBCtrlMDI + 2: 13980113f48dSStefan Weil val = (uint16_t)(eepro100_read_mdi(s) >> (8 * (addr & 3))); 13990113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 14000113f48dSStefan Weil break; 1401663e8e51Sths default: 1402663e8e51Sths logout("addr=%s val=0x%04x\n", regname(addr), val); 1403663e8e51Sths missing("unknown word read"); 1404663e8e51Sths } 1405663e8e51Sths return val; 1406663e8e51Sths } 1407663e8e51Sths 1408663e8e51Sths static uint32_t eepro100_read4(EEPRO100State * s, uint32_t addr) 1409663e8e51Sths { 1410ef476062SBlue Swirl uint32_t val = 0; 1411663e8e51Sths if (addr <= sizeof(s->mem) - sizeof(val)) { 1412e5e23ab8SStefan Weil val = e100_read_reg4(s, addr); 1413663e8e51Sths } 1414663e8e51Sths 1415663e8e51Sths switch (addr) { 1416663e8e51Sths case SCBStatus: 1417aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); 1418663e8e51Sths break; 1419663e8e51Sths case SCBPointer: 1420aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); 1421663e8e51Sths break; 1422663e8e51Sths case SCBPort: 1423663e8e51Sths val = eepro100_read_port(s); 1424aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); 1425663e8e51Sths break; 1426072476eaSStefan Weil case SCBflash: 1427072476eaSStefan Weil val = eepro100_read_eeprom(s); 1428072476eaSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); 1429072476eaSStefan Weil break; 1430663e8e51Sths case SCBCtrlMDI: 1431663e8e51Sths val = eepro100_read_mdi(s); 1432663e8e51Sths break; 1433663e8e51Sths default: 1434663e8e51Sths logout("addr=%s val=0x%08x\n", regname(addr), val); 1435663e8e51Sths missing("unknown longword read"); 1436663e8e51Sths } 1437663e8e51Sths return val; 1438663e8e51Sths } 1439663e8e51Sths 1440663e8e51Sths static void eepro100_write1(EEPRO100State * s, uint32_t addr, uint8_t val) 1441663e8e51Sths { 1442e74818f3SStefan Weil /* SCBStatus is readonly. */ 1443e74818f3SStefan Weil if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) { 1444e5e23ab8SStefan Weil s->mem[addr] = val; 1445663e8e51Sths } 1446663e8e51Sths 1447663e8e51Sths switch (addr) { 1448663e8e51Sths case SCBStatus: 14491b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1450663e8e51Sths break; 1451663e8e51Sths case SCBAck: 14521b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1453663e8e51Sths eepro100_acknowledge(s); 1454663e8e51Sths break; 1455663e8e51Sths case SCBCmd: 14561b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1457663e8e51Sths eepro100_write_command(s, val); 1458663e8e51Sths break; 1459663e8e51Sths case SCBIntmask: 14601b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1461663e8e51Sths if (val & BIT(1)) { 1462663e8e51Sths eepro100_swi_interrupt(s); 1463663e8e51Sths } 1464663e8e51Sths eepro100_interrupt(s, 0); 1465663e8e51Sths break; 146627a05006SStefan Weil case SCBPointer: 146727a05006SStefan Weil case SCBPointer + 1: 146827a05006SStefan Weil case SCBPointer + 2: 146927a05006SStefan Weil case SCBPointer + 3: 147027a05006SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 147127a05006SStefan Weil break; 14723fd3d0b4SStefan Weil case SCBPort: 14733fd3d0b4SStefan Weil case SCBPort + 1: 14743fd3d0b4SStefan Weil case SCBPort + 2: 14753fd3d0b4SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 14763fd3d0b4SStefan Weil break; 1477663e8e51Sths case SCBPort + 3: 14783fd3d0b4SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 14793fd3d0b4SStefan Weil eepro100_write_port(s); 14803fd3d0b4SStefan Weil break; 1481aac443e6SStefan Weil case SCBFlow: /* does not exist on 82557 */ 14823257d2b6Sths case SCBFlow + 1: 14833257d2b6Sths case SCBFlow + 2: 14840908bba1SStefan Weil case SCBpmdr: /* does not exist on 82557 */ 1485aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1486663e8e51Sths break; 1487663e8e51Sths case SCBeeprom: 14881b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 1489663e8e51Sths eepro100_write_eeprom(s->eeprom, val); 1490663e8e51Sths break; 14910113f48dSStefan Weil case SCBCtrlMDI: 14920113f48dSStefan Weil case SCBCtrlMDI + 1: 14930113f48dSStefan Weil case SCBCtrlMDI + 2: 14940113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 14950113f48dSStefan Weil break; 14960113f48dSStefan Weil case SCBCtrlMDI + 3: 14970113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); 14980113f48dSStefan Weil eepro100_write_mdi(s); 14990113f48dSStefan Weil break; 1500663e8e51Sths default: 1501663e8e51Sths logout("addr=%s val=0x%02x\n", regname(addr), val); 1502663e8e51Sths missing("unknown byte write"); 1503663e8e51Sths } 1504663e8e51Sths } 1505663e8e51Sths 1506663e8e51Sths static void eepro100_write2(EEPRO100State * s, uint32_t addr, uint16_t val) 1507663e8e51Sths { 1508e74818f3SStefan Weil /* SCBStatus is readonly. */ 1509e74818f3SStefan Weil if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) { 1510e5e23ab8SStefan Weil e100_write_reg2(s, addr, val); 1511663e8e51Sths } 1512663e8e51Sths 1513663e8e51Sths switch (addr) { 1514663e8e51Sths case SCBStatus: 15151b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 1516e74818f3SStefan Weil s->mem[SCBAck] = (val >> 8); 1517663e8e51Sths eepro100_acknowledge(s); 1518663e8e51Sths break; 1519663e8e51Sths case SCBCmd: 15201b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 1521663e8e51Sths eepro100_write_command(s, val); 1522663e8e51Sths eepro100_write1(s, SCBIntmask, val >> 8); 1523663e8e51Sths break; 152427a05006SStefan Weil case SCBPointer: 152527a05006SStefan Weil case SCBPointer + 2: 152627a05006SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 152727a05006SStefan Weil break; 15283fd3d0b4SStefan Weil case SCBPort: 15293fd3d0b4SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 15303fd3d0b4SStefan Weil break; 15313fd3d0b4SStefan Weil case SCBPort + 2: 15323fd3d0b4SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 15333fd3d0b4SStefan Weil eepro100_write_port(s); 15343fd3d0b4SStefan Weil break; 1535663e8e51Sths case SCBeeprom: 15361b4f97d6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 1537663e8e51Sths eepro100_write_eeprom(s->eeprom, val); 1538663e8e51Sths break; 15390113f48dSStefan Weil case SCBCtrlMDI: 15400113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 15410113f48dSStefan Weil break; 15420113f48dSStefan Weil case SCBCtrlMDI + 2: 15430113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); 15440113f48dSStefan Weil eepro100_write_mdi(s); 15450113f48dSStefan Weil break; 1546663e8e51Sths default: 1547663e8e51Sths logout("addr=%s val=0x%04x\n", regname(addr), val); 1548663e8e51Sths missing("unknown word write"); 1549663e8e51Sths } 1550663e8e51Sths } 1551663e8e51Sths 1552663e8e51Sths static void eepro100_write4(EEPRO100State * s, uint32_t addr, uint32_t val) 1553663e8e51Sths { 1554663e8e51Sths if (addr <= sizeof(s->mem) - sizeof(val)) { 1555e5e23ab8SStefan Weil e100_write_reg4(s, addr, val); 1556663e8e51Sths } 1557663e8e51Sths 1558663e8e51Sths switch (addr) { 1559663e8e51Sths case SCBPointer: 156027a05006SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); 1561663e8e51Sths break; 1562663e8e51Sths case SCBPort: 1563aac443e6SStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); 15643fd3d0b4SStefan Weil eepro100_write_port(s); 1565663e8e51Sths break; 1566072476eaSStefan Weil case SCBflash: 1567072476eaSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); 1568072476eaSStefan Weil val = val >> 16; 1569072476eaSStefan Weil eepro100_write_eeprom(s->eeprom, val); 1570072476eaSStefan Weil break; 1571663e8e51Sths case SCBCtrlMDI: 15720113f48dSStefan Weil TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); 15730113f48dSStefan Weil eepro100_write_mdi(s); 1574663e8e51Sths break; 1575663e8e51Sths default: 1576663e8e51Sths logout("addr=%s val=0x%08x\n", regname(addr), val); 1577663e8e51Sths missing("unknown longword write"); 1578663e8e51Sths } 1579663e8e51Sths } 1580663e8e51Sths 1581a8170e5eSAvi Kivity static uint64_t eepro100_read(void *opaque, hwaddr addr, 15825e6ffddeSAvi Kivity unsigned size) 1583663e8e51Sths { 1584663e8e51Sths EEPRO100State *s = opaque; 15855e6ffddeSAvi Kivity 15865e6ffddeSAvi Kivity switch (size) { 15875e6ffddeSAvi Kivity case 1: return eepro100_read1(s, addr); 15885e6ffddeSAvi Kivity case 2: return eepro100_read2(s, addr); 15895e6ffddeSAvi Kivity case 4: return eepro100_read4(s, addr); 15905e6ffddeSAvi Kivity default: abort(); 15915e6ffddeSAvi Kivity } 1592663e8e51Sths } 1593663e8e51Sths 1594a8170e5eSAvi Kivity static void eepro100_write(void *opaque, hwaddr addr, 15955e6ffddeSAvi Kivity uint64_t data, unsigned size) 1596663e8e51Sths { 1597663e8e51Sths EEPRO100State *s = opaque; 15985e6ffddeSAvi Kivity 15995e6ffddeSAvi Kivity switch (size) { 16000ed8b6f6SBlue Swirl case 1: 16010ed8b6f6SBlue Swirl eepro100_write1(s, addr, data); 16020ed8b6f6SBlue Swirl break; 16030ed8b6f6SBlue Swirl case 2: 16040ed8b6f6SBlue Swirl eepro100_write2(s, addr, data); 16050ed8b6f6SBlue Swirl break; 16060ed8b6f6SBlue Swirl case 4: 16070ed8b6f6SBlue Swirl eepro100_write4(s, addr, data); 16080ed8b6f6SBlue Swirl break; 16090ed8b6f6SBlue Swirl default: 16100ed8b6f6SBlue Swirl abort(); 16115e6ffddeSAvi Kivity } 1612663e8e51Sths } 1613663e8e51Sths 16145e6ffddeSAvi Kivity static const MemoryRegionOps eepro100_ops = { 16155e6ffddeSAvi Kivity .read = eepro100_read, 16165e6ffddeSAvi Kivity .write = eepro100_write, 16175e6ffddeSAvi Kivity .endianness = DEVICE_LITTLE_ENDIAN, 1618663e8e51Sths }; 1619663e8e51Sths 16204e68f7a0SStefan Hajnoczi static int nic_can_receive(NetClientState *nc) 1621663e8e51Sths { 1622cc1f0f45SJason Wang EEPRO100State *s = qemu_get_nic_opaque(nc); 1623aac443e6SStefan Weil TRACE(RXTX, logout("%p\n", s)); 1624663e8e51Sths return get_ru_state(s) == ru_ready; 1625e7493b25SStefan Weil #if 0 1626e7493b25SStefan Weil return !eepro100_buffer_full(s); 1627e7493b25SStefan Weil #endif 1628663e8e51Sths } 1629663e8e51Sths 16304e68f7a0SStefan Hajnoczi static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size) 1631663e8e51Sths { 1632663e8e51Sths /* TODO: 1633663e8e51Sths * - Magic packets should set bit 30 in power management driver register. 1634663e8e51Sths * - Interesting packets should set bit 29 in power management driver register. 1635663e8e51Sths */ 1636cc1f0f45SJason Wang EEPRO100State *s = qemu_get_nic_opaque(nc); 1637663e8e51Sths uint16_t rfd_status = 0xa000; 1638792f1d63SStefan Weil #if defined(CONFIG_PAD_RECEIVED_FRAMES) 1639792f1d63SStefan Weil uint8_t min_buf[60]; 1640792f1d63SStefan Weil #endif 1641663e8e51Sths static const uint8_t broadcast_macaddr[6] = 1642663e8e51Sths { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 1643663e8e51Sths 1644792f1d63SStefan Weil #if defined(CONFIG_PAD_RECEIVED_FRAMES) 1645792f1d63SStefan Weil /* Pad to minimum Ethernet frame length */ 1646792f1d63SStefan Weil if (size < sizeof(min_buf)) { 1647792f1d63SStefan Weil memcpy(min_buf, buf, size); 1648792f1d63SStefan Weil memset(&min_buf[size], 0, sizeof(min_buf) - size); 1649792f1d63SStefan Weil buf = min_buf; 1650792f1d63SStefan Weil size = sizeof(min_buf); 1651792f1d63SStefan Weil } 1652792f1d63SStefan Weil #endif 1653792f1d63SStefan Weil 1654663e8e51Sths if (s->configuration[8] & 0x80) { 1655663e8e51Sths /* CSMA is disabled. */ 1656663e8e51Sths logout("%p received while CSMA is disabled\n", s); 16574f1c942bSMark McLoughlin return -1; 1658792f1d63SStefan Weil #if !defined(CONFIG_PAD_RECEIVED_FRAMES) 1659ced5296aSStefan Weil } else if (size < 64 && (s->configuration[7] & BIT(0))) { 1660663e8e51Sths /* Short frame and configuration byte 7/0 (discard short receive) set: 1661663e8e51Sths * Short frame is discarded */ 1662067d01deSStefan Weil logout("%p received short frame (%zu byte)\n", s, size); 1663663e8e51Sths s->statistics.rx_short_frame_errors++; 1664e7493b25SStefan Weil return -1; 1665e7493b25SStefan Weil #endif 1666ced5296aSStefan Weil } else if ((size > MAX_ETH_FRAME_SIZE + 4) && !(s->configuration[18] & BIT(3))) { 1667663e8e51Sths /* Long frame and configuration byte 18/3 (long receive ok) not set: 1668663e8e51Sths * Long frames are discarded. */ 1669067d01deSStefan Weil logout("%p received long frame (%zu byte), ignored\n", s, size); 16704f1c942bSMark McLoughlin return -1; 1671e7493b25SStefan Weil } else if (memcmp(buf, s->conf.macaddr.a, 6) == 0) { /* !!! */ 1672663e8e51Sths /* Frame matches individual address. */ 1673663e8e51Sths /* TODO: check configuration byte 15/4 (ignore U/L). */ 1674067d01deSStefan Weil TRACE(RXTX, logout("%p received frame for me, len=%zu\n", s, size)); 1675663e8e51Sths } else if (memcmp(buf, broadcast_macaddr, 6) == 0) { 1676663e8e51Sths /* Broadcast frame. */ 1677067d01deSStefan Weil TRACE(RXTX, logout("%p received broadcast, len=%zu\n", s, size)); 1678663e8e51Sths rfd_status |= 0x0002; 16797b8737deSStefan Weil } else if (buf[0] & 0x01) { 1680663e8e51Sths /* Multicast frame. */ 16817b8737deSStefan Weil TRACE(RXTX, logout("%p received multicast, len=%zu,%s\n", s, size, nic_dump(buf, size))); 16827f1e9d4eSKevin Wolf if (s->configuration[21] & BIT(3)) { 16837b8737deSStefan Weil /* Multicast all bit is set, receive all multicast frames. */ 16847b8737deSStefan Weil } else { 168569f3ce78SStefan Weil unsigned mcast_idx = e100_compute_mcast_idx(buf); 16867b8737deSStefan Weil assert(mcast_idx < 64); 16877b8737deSStefan Weil if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) { 16887b8737deSStefan Weil /* Multicast frame is allowed in hash table. */ 1689ced5296aSStefan Weil } else if (s->configuration[15] & BIT(0)) { 16907b8737deSStefan Weil /* Promiscuous: receive all. */ 16917b8737deSStefan Weil rfd_status |= 0x0004; 16927b8737deSStefan Weil } else { 16937b8737deSStefan Weil TRACE(RXTX, logout("%p multicast ignored\n", s)); 16947b8737deSStefan Weil return -1; 16957f1e9d4eSKevin Wolf } 1696663e8e51Sths } 16977b8737deSStefan Weil /* TODO: Next not for promiscuous mode? */ 1698663e8e51Sths rfd_status |= 0x0002; 1699ced5296aSStefan Weil } else if (s->configuration[15] & BIT(0)) { 1700663e8e51Sths /* Promiscuous: receive all. */ 1701067d01deSStefan Weil TRACE(RXTX, logout("%p received frame in promiscuous mode, len=%zu\n", s, size)); 1702663e8e51Sths rfd_status |= 0x0004; 1703010ec629SStefan Weil } else if (s->configuration[20] & BIT(6)) { 1704010ec629SStefan Weil /* Multiple IA bit set. */ 1705010ec629SStefan Weil unsigned mcast_idx = compute_mcast_idx(buf); 1706010ec629SStefan Weil assert(mcast_idx < 64); 1707010ec629SStefan Weil if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) { 1708010ec629SStefan Weil TRACE(RXTX, logout("%p accepted, multiple IA bit set\n", s)); 1709010ec629SStefan Weil } else { 1710010ec629SStefan Weil TRACE(RXTX, logout("%p frame ignored, multiple IA bit set\n", s)); 1711010ec629SStefan Weil return -1; 1712010ec629SStefan Weil } 1713663e8e51Sths } else { 1714067d01deSStefan Weil TRACE(RXTX, logout("%p received frame, ignored, len=%zu,%s\n", s, size, 1715aac443e6SStefan Weil nic_dump(buf, size))); 17164f1c942bSMark McLoughlin return size; 1717663e8e51Sths } 1718663e8e51Sths 1719663e8e51Sths if (get_ru_state(s) != ru_ready) { 1720aac443e6SStefan Weil /* No resources available. */ 1721aac443e6SStefan Weil logout("no resources, state=%u\n", get_ru_state(s)); 1722e824012bSStefan Weil /* TODO: RNR interrupt only at first failed frame? */ 1723e824012bSStefan Weil eepro100_rnr_interrupt(s); 1724663e8e51Sths s->statistics.rx_resource_errors++; 1725e7493b25SStefan Weil #if 0 1726e7493b25SStefan Weil assert(!"no resources"); 1727e7493b25SStefan Weil #endif 17284f1c942bSMark McLoughlin return -1; 1729663e8e51Sths } 1730e7493b25SStefan Weil /* !!! */ 1731c227f099SAnthony Liguori eepro100_rx_t rx; 173216ef60c9SEduard - Gabriel Munteanu pci_dma_read(&s->dev, s->ru_base + s->ru_offset, 1733e965d4bcSDavid Gibson &rx, sizeof(eepro100_rx_t)); 1734663e8e51Sths uint16_t rfd_command = le16_to_cpu(rx.command); 1735663e8e51Sths uint16_t rfd_size = le16_to_cpu(rx.size); 17367f1e9d4eSKevin Wolf 17377f1e9d4eSKevin Wolf if (size > rfd_size) { 17387f1e9d4eSKevin Wolf logout("Receive buffer (%" PRId16 " bytes) too small for data " 17397f1e9d4eSKevin Wolf "(%zu bytes); data truncated\n", rfd_size, size); 17407f1e9d4eSKevin Wolf size = rfd_size; 17417f1e9d4eSKevin Wolf } 1742792f1d63SStefan Weil #if !defined(CONFIG_PAD_RECEIVED_FRAMES) 1743663e8e51Sths if (size < 64) { 1744663e8e51Sths rfd_status |= 0x0080; 1745663e8e51Sths } 1746792f1d63SStefan Weil #endif 1747aac443e6SStefan Weil TRACE(OTHER, logout("command 0x%04x, link 0x%08x, addr 0x%08x, size %u\n", 1748aac443e6SStefan Weil rfd_command, rx.link, rx.rx_buf_addr, rfd_size)); 174916ef60c9SEduard - Gabriel Munteanu stw_le_pci_dma(&s->dev, s->ru_base + s->ru_offset + 1750e5e23ab8SStefan Weil offsetof(eepro100_rx_t, status), rfd_status); 175116ef60c9SEduard - Gabriel Munteanu stw_le_pci_dma(&s->dev, s->ru_base + s->ru_offset + 1752e5e23ab8SStefan Weil offsetof(eepro100_rx_t, count), size); 1753663e8e51Sths /* Early receive interrupt not supported. */ 1754e7493b25SStefan Weil #if 0 1755e7493b25SStefan Weil eepro100_er_interrupt(s); 1756e7493b25SStefan Weil #endif 1757663e8e51Sths /* Receive CRC Transfer not supported. */ 1758ced5296aSStefan Weil if (s->configuration[18] & BIT(2)) { 17597f1e9d4eSKevin Wolf missing("Receive CRC Transfer"); 17607f1e9d4eSKevin Wolf return -1; 17617f1e9d4eSKevin Wolf } 1762663e8e51Sths /* TODO: check stripping enable bit. */ 1763e7493b25SStefan Weil #if 0 1764e7493b25SStefan Weil assert(!(s->configuration[17] & BIT(0))); 1765e7493b25SStefan Weil #endif 176616ef60c9SEduard - Gabriel Munteanu pci_dma_write(&s->dev, s->ru_base + s->ru_offset + 176727112f18SStefan Weil sizeof(eepro100_rx_t), buf, size); 1768663e8e51Sths s->statistics.rx_good_frames++; 1769663e8e51Sths eepro100_fr_interrupt(s); 1770663e8e51Sths s->ru_offset = le32_to_cpu(rx.link); 1771ced5296aSStefan Weil if (rfd_command & COMMAND_EL) { 1772663e8e51Sths /* EL bit is set, so this was the last frame. */ 17737f1e9d4eSKevin Wolf logout("receive: Running out of frames\n"); 17741069985fSBo Yang set_ru_state(s, ru_no_resources); 17751069985fSBo Yang eepro100_rnr_interrupt(s); 1776663e8e51Sths } 1777ced5296aSStefan Weil if (rfd_command & COMMAND_S) { 1778663e8e51Sths /* S bit is set. */ 1779663e8e51Sths set_ru_state(s, ru_suspended); 1780663e8e51Sths } 17814f1c942bSMark McLoughlin return size; 1782663e8e51Sths } 1783663e8e51Sths 1784151b2986SJuan Quintela static const VMStateDescription vmstate_eepro100 = { 1785151b2986SJuan Quintela .version_id = 3, 1786151b2986SJuan Quintela .minimum_version_id = 2, 1787151b2986SJuan Quintela .minimum_version_id_old = 2, 1788151b2986SJuan Quintela .fields = (VMStateField []) { 1789151b2986SJuan Quintela VMSTATE_PCI_DEVICE(dev, EEPRO100State), 1790151b2986SJuan Quintela VMSTATE_UNUSED(32), 1791151b2986SJuan Quintela VMSTATE_BUFFER(mult, EEPRO100State), 1792151b2986SJuan Quintela VMSTATE_BUFFER(mem, EEPRO100State), 17933706c43fSStefan Weil /* Save all members of struct between scb_stat and mem. */ 1794151b2986SJuan Quintela VMSTATE_UINT8(scb_stat, EEPRO100State), 1795151b2986SJuan Quintela VMSTATE_UINT8(int_stat, EEPRO100State), 1796151b2986SJuan Quintela VMSTATE_UNUSED(3*4), 1797151b2986SJuan Quintela VMSTATE_MACADDR(conf.macaddr, EEPRO100State), 1798151b2986SJuan Quintela VMSTATE_UNUSED(19*4), 1799151b2986SJuan Quintela VMSTATE_UINT16_ARRAY(mdimem, EEPRO100State, 32), 1800aac443e6SStefan Weil /* The eeprom should be saved and restored by its own routines. */ 1801151b2986SJuan Quintela VMSTATE_UINT32(device, EEPRO100State), 1802151b2986SJuan Quintela /* TODO check device. */ 1803151b2986SJuan Quintela VMSTATE_UINT32(cu_base, EEPRO100State), 1804151b2986SJuan Quintela VMSTATE_UINT32(cu_offset, EEPRO100State), 1805151b2986SJuan Quintela VMSTATE_UINT32(ru_base, EEPRO100State), 1806151b2986SJuan Quintela VMSTATE_UINT32(ru_offset, EEPRO100State), 1807151b2986SJuan Quintela VMSTATE_UINT32(statsaddr, EEPRO100State), 1808ba42b646SStefan Weil /* Save eepro100_stats_t statistics. */ 1809151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_good_frames, EEPRO100State), 1810151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_max_collisions, EEPRO100State), 1811151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_late_collisions, EEPRO100State), 1812151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_underruns, EEPRO100State), 1813151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_lost_crs, EEPRO100State), 1814151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_deferred, EEPRO100State), 1815151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_single_collisions, EEPRO100State), 1816151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_multiple_collisions, EEPRO100State), 1817151b2986SJuan Quintela VMSTATE_UINT32(statistics.tx_total_collisions, EEPRO100State), 1818151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_good_frames, EEPRO100State), 1819151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_crc_errors, EEPRO100State), 1820151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_alignment_errors, EEPRO100State), 1821151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_resource_errors, EEPRO100State), 1822151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_overrun_errors, EEPRO100State), 1823151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_cdt_errors, EEPRO100State), 1824151b2986SJuan Quintela VMSTATE_UINT32(statistics.rx_short_frame_errors, EEPRO100State), 1825151b2986SJuan Quintela VMSTATE_UINT32(statistics.fc_xmt_pause, EEPRO100State), 1826151b2986SJuan Quintela VMSTATE_UINT32(statistics.fc_rcv_pause, EEPRO100State), 1827151b2986SJuan Quintela VMSTATE_UINT32(statistics.fc_rcv_unsupported, EEPRO100State), 1828151b2986SJuan Quintela VMSTATE_UINT16(statistics.xmt_tco_frames, EEPRO100State), 1829151b2986SJuan Quintela VMSTATE_UINT16(statistics.rcv_tco_frames, EEPRO100State), 18302657c663Sbalrog /* Configuration bytes. */ 1831151b2986SJuan Quintela VMSTATE_BUFFER(configuration, EEPRO100State), 1832151b2986SJuan Quintela VMSTATE_END_OF_LIST() 1833663e8e51Sths } 1834151b2986SJuan Quintela }; 1835663e8e51Sths 18364e68f7a0SStefan Hajnoczi static void nic_cleanup(NetClientState *nc) 1837b946a153Saliguori { 1838cc1f0f45SJason Wang EEPRO100State *s = qemu_get_nic_opaque(nc); 1839b946a153Saliguori 1840e00e365eSMark McLoughlin s->nic = NULL; 1841b946a153Saliguori } 1842b946a153Saliguori 1843f90c2bcdSAlex Williamson static void pci_nic_uninit(PCIDevice *pci_dev) 1844b946a153Saliguori { 1845c4c270e2SStefan Weil EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev); 1846b946a153Saliguori 18475e6ffddeSAvi Kivity memory_region_destroy(&s->mmio_bar); 18485e6ffddeSAvi Kivity memory_region_destroy(&s->io_bar); 18495e6ffddeSAvi Kivity memory_region_destroy(&s->flash_bar); 18500be71e32SAlex Williamson vmstate_unregister(&pci_dev->qdev, s->vmstate, s); 18515fce2b3eSAlex Williamson eeprom93xx_free(&pci_dev->qdev, s->eeprom); 1852948ecf21SJason Wang qemu_del_nic(s->nic); 1853b946a153Saliguori } 1854b946a153Saliguori 1855e00e365eSMark McLoughlin static NetClientInfo net_eepro100_info = { 18562be64a68SLaszlo Ersek .type = NET_CLIENT_OPTIONS_KIND_NIC, 1857e00e365eSMark McLoughlin .size = sizeof(NICState), 1858e00e365eSMark McLoughlin .can_receive = nic_can_receive, 1859e00e365eSMark McLoughlin .receive = nic_receive, 1860e00e365eSMark McLoughlin .cleanup = nic_cleanup, 1861e00e365eSMark McLoughlin }; 1862e00e365eSMark McLoughlin 1863558c8634SStefan Weil static int e100_nic_init(PCIDevice *pci_dev) 1864663e8e51Sths { 1865273a2142SJuan Quintela EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev); 186640021f08SAnthony Liguori E100PCIDeviceInfo *info = eepro100_get_class(s); 1867663e8e51Sths 1868aac443e6SStefan Weil TRACE(OTHER, logout("\n")); 1869663e8e51Sths 187040021f08SAnthony Liguori s->device = info->device; 1871663e8e51Sths 187240021f08SAnthony Liguori e100_pci_reset(s); 1873663e8e51Sths 1874663e8e51Sths /* Add 64 * 2 EEPROM. i82557 and i82558 support a 64 word EEPROM, 1875663e8e51Sths * i82559 and later support 64 or 256 word EEPROM. */ 18765fce2b3eSAlex Williamson s->eeprom = eeprom93xx_new(&pci_dev->qdev, EEPROM_SIZE); 1877663e8e51Sths 1878663e8e51Sths /* Handler for memory-mapped I/O */ 1879eedfac6fSPaolo Bonzini memory_region_init_io(&s->mmio_bar, OBJECT(s), &eepro100_ops, s, 1880eedfac6fSPaolo Bonzini "eepro100-mmio", PCI_MEM_SIZE); 1881e824b2ccSAvi Kivity pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->mmio_bar); 1882eedfac6fSPaolo Bonzini memory_region_init_io(&s->io_bar, OBJECT(s), &eepro100_ops, s, 1883eedfac6fSPaolo Bonzini "eepro100-io", PCI_IO_SIZE); 1884e824b2ccSAvi Kivity pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar); 18855e6ffddeSAvi Kivity /* FIXME: flash aliases to mmio?! */ 1886eedfac6fSPaolo Bonzini memory_region_init_io(&s->flash_bar, OBJECT(s), &eepro100_ops, s, 1887eedfac6fSPaolo Bonzini "eepro100-flash", PCI_FLASH_SIZE); 1888e824b2ccSAvi Kivity pci_register_bar(&s->dev, 2, 0, &s->flash_bar); 1889663e8e51Sths 1890508ef936SGerd Hoffmann qemu_macaddr_default_if_unset(&s->conf.macaddr); 1891ce0e58b3SStefan Weil logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6)); 1892663e8e51Sths 1893663e8e51Sths nic_reset(s); 1894663e8e51Sths 1895e00e365eSMark McLoughlin s->nic = qemu_new_nic(&net_eepro100_info, &s->conf, 1896f79f2bfcSAnthony Liguori object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s); 1897663e8e51Sths 1898b356f76dSJason Wang qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); 1899b356f76dSJason Wang TRACE(OTHER, logout("%s\n", qemu_get_queue(s->nic)->info_str)); 1900663e8e51Sths 1901a08d4367SJan Kiszka qemu_register_reset(nic_reset, s); 1902663e8e51Sths 19037267c094SAnthony Liguori s->vmstate = g_malloc(sizeof(vmstate_eepro100)); 1904151b2986SJuan Quintela memcpy(s->vmstate, &vmstate_eepro100, sizeof(vmstate_eepro100)); 1905b356f76dSJason Wang s->vmstate->name = qemu_get_queue(s->nic)->model; 19060be71e32SAlex Williamson vmstate_register(&pci_dev->qdev, -1, s->vmstate, s); 19074e9df06aSStefan Weil 19081ca4d09aSGleb Natapov add_boot_device_path(s->conf.bootindex, &pci_dev->qdev, "/ethernet-phy@0"); 19091ca4d09aSGleb Natapov 191081a322d4SGerd Hoffmann return 0; 1911663e8e51Sths } 1912663e8e51Sths 1913558c8634SStefan Weil static E100PCIDeviceInfo e100_devices[] = { 1914663e8e51Sths { 191539bffca2SAnthony Liguori .name = "i82550", 191639bffca2SAnthony Liguori .desc = "Intel i82550 Ethernet", 1917558c8634SStefan Weil .device = i82550, 1918558c8634SStefan Weil /* TODO: check device id. */ 191940021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82551IT, 1920558c8634SStefan Weil /* Revision ID: 0x0c, 0x0d, 0x0e. */ 192140021f08SAnthony Liguori .revision = 0x0e, 1922558c8634SStefan Weil /* TODO: check size of statistical counters. */ 1923558c8634SStefan Weil .stats_size = 80, 1924558c8634SStefan Weil /* TODO: check extended tcb support. */ 1925558c8634SStefan Weil .has_extended_tcb_support = true, 1926558c8634SStefan Weil .power_management = true, 1927558c8634SStefan Weil },{ 192839bffca2SAnthony Liguori .name = "i82551", 192939bffca2SAnthony Liguori .desc = "Intel i82551 Ethernet", 1930558c8634SStefan Weil .device = i82551, 193140021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82551IT, 1932558c8634SStefan Weil /* Revision ID: 0x0f, 0x10. */ 193340021f08SAnthony Liguori .revision = 0x0f, 1934558c8634SStefan Weil /* TODO: check size of statistical counters. */ 1935558c8634SStefan Weil .stats_size = 80, 1936558c8634SStefan Weil .has_extended_tcb_support = true, 1937558c8634SStefan Weil .power_management = true, 1938558c8634SStefan Weil },{ 193939bffca2SAnthony Liguori .name = "i82557a", 194039bffca2SAnthony Liguori .desc = "Intel i82557A Ethernet", 1941558c8634SStefan Weil .device = i82557A, 194240021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557, 194340021f08SAnthony Liguori .revision = 0x01, 1944558c8634SStefan Weil .power_management = false, 1945558c8634SStefan Weil },{ 194639bffca2SAnthony Liguori .name = "i82557b", 194739bffca2SAnthony Liguori .desc = "Intel i82557B Ethernet", 1948558c8634SStefan Weil .device = i82557B, 194940021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557, 195040021f08SAnthony Liguori .revision = 0x02, 1951558c8634SStefan Weil .power_management = false, 1952558c8634SStefan Weil },{ 195339bffca2SAnthony Liguori .name = "i82557c", 195439bffca2SAnthony Liguori .desc = "Intel i82557C Ethernet", 1955558c8634SStefan Weil .device = i82557C, 195640021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557, 195740021f08SAnthony Liguori .revision = 0x03, 1958558c8634SStefan Weil .power_management = false, 1959558c8634SStefan Weil },{ 196039bffca2SAnthony Liguori .name = "i82558a", 196139bffca2SAnthony Liguori .desc = "Intel i82558A Ethernet", 1962558c8634SStefan Weil .device = i82558A, 196340021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557, 196440021f08SAnthony Liguori .revision = 0x04, 1965558c8634SStefan Weil .stats_size = 76, 1966558c8634SStefan Weil .has_extended_tcb_support = true, 1967558c8634SStefan Weil .power_management = true, 1968558c8634SStefan Weil },{ 196939bffca2SAnthony Liguori .name = "i82558b", 197039bffca2SAnthony Liguori .desc = "Intel i82558B Ethernet", 1971558c8634SStefan Weil .device = i82558B, 197240021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557, 197340021f08SAnthony Liguori .revision = 0x05, 1974558c8634SStefan Weil .stats_size = 76, 1975558c8634SStefan Weil .has_extended_tcb_support = true, 1976558c8634SStefan Weil .power_management = true, 1977558c8634SStefan Weil },{ 197839bffca2SAnthony Liguori .name = "i82559a", 197939bffca2SAnthony Liguori .desc = "Intel i82559A Ethernet", 1980558c8634SStefan Weil .device = i82559A, 198140021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557, 198240021f08SAnthony Liguori .revision = 0x06, 1983558c8634SStefan Weil .stats_size = 80, 1984558c8634SStefan Weil .has_extended_tcb_support = true, 1985558c8634SStefan Weil .power_management = true, 1986558c8634SStefan Weil },{ 198739bffca2SAnthony Liguori .name = "i82559b", 198839bffca2SAnthony Liguori .desc = "Intel i82559B Ethernet", 1989558c8634SStefan Weil .device = i82559B, 199040021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557, 199140021f08SAnthony Liguori .revision = 0x07, 1992558c8634SStefan Weil .stats_size = 80, 1993558c8634SStefan Weil .has_extended_tcb_support = true, 1994558c8634SStefan Weil .power_management = true, 1995558c8634SStefan Weil },{ 199639bffca2SAnthony Liguori .name = "i82559c", 199739bffca2SAnthony Liguori .desc = "Intel i82559C Ethernet", 1998558c8634SStefan Weil .device = i82559C, 199940021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82557, 2000558c8634SStefan Weil #if 0 200140021f08SAnthony Liguori .revision = 0x08, 2002558c8634SStefan Weil #endif 2003558c8634SStefan Weil /* TODO: Windows wants revision id 0x0c. */ 200440021f08SAnthony Liguori .revision = 0x0c, 2005ad03502bSIsaku Yamahata #if EEPROM_SIZE > 0 200640021f08SAnthony Liguori .subsystem_vendor_id = PCI_VENDOR_ID_INTEL, 200740021f08SAnthony Liguori .subsystem_id = 0x0040, 2008ad03502bSIsaku Yamahata #endif 2009558c8634SStefan Weil .stats_size = 80, 2010558c8634SStefan Weil .has_extended_tcb_support = true, 2011558c8634SStefan Weil .power_management = true, 2012558c8634SStefan Weil },{ 201339bffca2SAnthony Liguori .name = "i82559er", 201439bffca2SAnthony Liguori .desc = "Intel i82559ER Ethernet", 2015558c8634SStefan Weil .device = i82559ER, 201640021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82551IT, 201740021f08SAnthony Liguori .revision = 0x09, 2018558c8634SStefan Weil .stats_size = 80, 2019558c8634SStefan Weil .has_extended_tcb_support = true, 2020558c8634SStefan Weil .power_management = true, 2021558c8634SStefan Weil },{ 202239bffca2SAnthony Liguori .name = "i82562", 202339bffca2SAnthony Liguori .desc = "Intel i82562 Ethernet", 2024558c8634SStefan Weil .device = i82562, 2025558c8634SStefan Weil /* TODO: check device id. */ 202640021f08SAnthony Liguori .device_id = PCI_DEVICE_ID_INTEL_82551IT, 2027558c8634SStefan Weil /* TODO: wrong revision id. */ 202840021f08SAnthony Liguori .revision = 0x0e, 2029558c8634SStefan Weil .stats_size = 80, 2030558c8634SStefan Weil .has_extended_tcb_support = true, 2031558c8634SStefan Weil .power_management = true, 2032db667a12SStefan Weil },{ 2033db667a12SStefan Weil /* Toshiba Tecra 8200. */ 203439bffca2SAnthony Liguori .name = "i82801", 203539bffca2SAnthony Liguori .desc = "Intel i82801 Ethernet", 2036db667a12SStefan Weil .device = i82801, 203740021f08SAnthony Liguori .device_id = 0x2449, 203840021f08SAnthony Liguori .revision = 0x03, 2039db667a12SStefan Weil .stats_size = 80, 2040db667a12SStefan Weil .has_extended_tcb_support = true, 2041db667a12SStefan Weil .power_management = true, 2042663e8e51Sths } 2043558c8634SStefan Weil }; 2044663e8e51Sths 204540021f08SAnthony Liguori static E100PCIDeviceInfo *eepro100_get_class_by_name(const char *typename) 204640021f08SAnthony Liguori { 204740021f08SAnthony Liguori E100PCIDeviceInfo *info = NULL; 204840021f08SAnthony Liguori int i; 204940021f08SAnthony Liguori 205040021f08SAnthony Liguori /* This is admittedly awkward but also temporary. QOM allows for 205140021f08SAnthony Liguori * parameterized typing and for subclassing both of which would suitable 205240021f08SAnthony Liguori * handle what's going on here. But class_data is already being used as 205340021f08SAnthony Liguori * a stop-gap hack to allow incremental qdev conversion so we cannot use it 205440021f08SAnthony Liguori * right now. Once we merge the final QOM series, we can come back here and 205540021f08SAnthony Liguori * do this in a much more elegant fashion. 205640021f08SAnthony Liguori */ 205740021f08SAnthony Liguori for (i = 0; i < ARRAY_SIZE(e100_devices); i++) { 205839bffca2SAnthony Liguori if (strcmp(e100_devices[i].name, typename) == 0) { 205940021f08SAnthony Liguori info = &e100_devices[i]; 206040021f08SAnthony Liguori break; 206140021f08SAnthony Liguori } 206240021f08SAnthony Liguori } 206340021f08SAnthony Liguori assert(info != NULL); 206440021f08SAnthony Liguori 206540021f08SAnthony Liguori return info; 206640021f08SAnthony Liguori } 206740021f08SAnthony Liguori 206840021f08SAnthony Liguori static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s) 206940021f08SAnthony Liguori { 207040021f08SAnthony Liguori return eepro100_get_class_by_name(object_get_typename(OBJECT(s))); 207140021f08SAnthony Liguori } 207240021f08SAnthony Liguori 207339bffca2SAnthony Liguori static Property e100_properties[] = { 207439bffca2SAnthony Liguori DEFINE_NIC_PROPERTIES(EEPRO100State, conf), 207539bffca2SAnthony Liguori DEFINE_PROP_END_OF_LIST(), 207639bffca2SAnthony Liguori }; 207739bffca2SAnthony Liguori 207840021f08SAnthony Liguori static void eepro100_class_init(ObjectClass *klass, void *data) 207940021f08SAnthony Liguori { 208039bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 208140021f08SAnthony Liguori PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); 208240021f08SAnthony Liguori E100PCIDeviceInfo *info; 208340021f08SAnthony Liguori 208440021f08SAnthony Liguori info = eepro100_get_class_by_name(object_class_get_name(klass)); 208540021f08SAnthony Liguori 2086*125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); 208739bffca2SAnthony Liguori dc->props = e100_properties; 208839bffca2SAnthony Liguori dc->desc = info->desc; 208940021f08SAnthony Liguori k->vendor_id = PCI_VENDOR_ID_INTEL; 209040021f08SAnthony Liguori k->class_id = PCI_CLASS_NETWORK_ETHERNET; 209140021f08SAnthony Liguori k->romfile = "pxe-eepro100.rom"; 209240021f08SAnthony Liguori k->init = e100_nic_init; 209340021f08SAnthony Liguori k->exit = pci_nic_uninit; 209440021f08SAnthony Liguori k->device_id = info->device_id; 209540021f08SAnthony Liguori k->revision = info->revision; 209640021f08SAnthony Liguori k->subsystem_vendor_id = info->subsystem_vendor_id; 209740021f08SAnthony Liguori k->subsystem_id = info->subsystem_id; 209840021f08SAnthony Liguori } 209940021f08SAnthony Liguori 210083f7d43aSAndreas Färber static void eepro100_register_types(void) 21019d07d757SPaul Brook { 2102558c8634SStefan Weil size_t i; 2103558c8634SStefan Weil for (i = 0; i < ARRAY_SIZE(e100_devices); i++) { 210439bffca2SAnthony Liguori TypeInfo type_info = {}; 210539bffca2SAnthony Liguori E100PCIDeviceInfo *info = &e100_devices[i]; 210640021f08SAnthony Liguori 210739bffca2SAnthony Liguori type_info.name = info->name; 210839bffca2SAnthony Liguori type_info.parent = TYPE_PCI_DEVICE; 210939bffca2SAnthony Liguori type_info.class_init = eepro100_class_init; 211039bffca2SAnthony Liguori type_info.instance_size = sizeof(EEPRO100State); 211140021f08SAnthony Liguori 211239bffca2SAnthony Liguori type_register(&type_info); 2113558c8634SStefan Weil } 21149d07d757SPaul Brook } 21159d07d757SPaul Brook 211683f7d43aSAndreas Färber type_init(eepro100_register_types) 2117