162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright(c) 1999 - 2006 Intel Corporation. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci/* 562306a36Sopenharmony_ci * e100.c: Intel(R) PRO/100 ethernet driver 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * (Re)written 2003 by scott.feldman@intel.com. Based loosely on 862306a36Sopenharmony_ci * original e100 driver, but better described as a munging of 962306a36Sopenharmony_ci * e100, e1000, eepro100, tg3, 8139cp, and other drivers. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * References: 1262306a36Sopenharmony_ci * Intel 8255x 10/100 Mbps Ethernet Controller Family, 1362306a36Sopenharmony_ci * Open Source Software Developers Manual, 1462306a36Sopenharmony_ci * http://sourceforge.net/projects/e1000 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * Theory of Operation 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * I. General 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * The driver supports Intel(R) 10/100 Mbps PCI Fast Ethernet 2262306a36Sopenharmony_ci * controller family, which includes the 82557, 82558, 82559, 82550, 2362306a36Sopenharmony_ci * 82551, and 82562 devices. 82558 and greater controllers 2462306a36Sopenharmony_ci * integrate the Intel 82555 PHY. The controllers are used in 2562306a36Sopenharmony_ci * server and client network interface cards, as well as in 2662306a36Sopenharmony_ci * LAN-On-Motherboard (LOM), CardBus, MiniPCI, and ICHx 2762306a36Sopenharmony_ci * configurations. 8255x supports a 32-bit linear addressing 2862306a36Sopenharmony_ci * mode and operates at 33Mhz PCI clock rate. 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * II. Driver Operation 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci * Memory-mapped mode is used exclusively to access the device's 3362306a36Sopenharmony_ci * shared-memory structure, the Control/Status Registers (CSR). All 3462306a36Sopenharmony_ci * setup, configuration, and control of the device, including queuing 3562306a36Sopenharmony_ci * of Tx, Rx, and configuration commands is through the CSR. 3662306a36Sopenharmony_ci * cmd_lock serializes accesses to the CSR command register. cb_lock 3762306a36Sopenharmony_ci * protects the shared Command Block List (CBL). 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * 8255x is highly MII-compliant and all access to the PHY go 4062306a36Sopenharmony_ci * through the Management Data Interface (MDI). Consequently, the 4162306a36Sopenharmony_ci * driver leverages the mii.c library shared with other MII-compliant 4262306a36Sopenharmony_ci * devices. 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * Big- and Little-Endian byte order as well as 32- and 64-bit 4562306a36Sopenharmony_ci * archs are supported. Weak-ordered memory and non-cache-coherent 4662306a36Sopenharmony_ci * archs are supported. 4762306a36Sopenharmony_ci * 4862306a36Sopenharmony_ci * III. Transmit 4962306a36Sopenharmony_ci * 5062306a36Sopenharmony_ci * A Tx skb is mapped and hangs off of a TCB. TCBs are linked 5162306a36Sopenharmony_ci * together in a fixed-size ring (CBL) thus forming the flexible mode 5262306a36Sopenharmony_ci * memory structure. A TCB marked with the suspend-bit indicates 5362306a36Sopenharmony_ci * the end of the ring. The last TCB processed suspends the 5462306a36Sopenharmony_ci * controller, and the controller can be restarted by issue a CU 5562306a36Sopenharmony_ci * resume command to continue from the suspend point, or a CU start 5662306a36Sopenharmony_ci * command to start at a given position in the ring. 5762306a36Sopenharmony_ci * 5862306a36Sopenharmony_ci * Non-Tx commands (config, multicast setup, etc) are linked 5962306a36Sopenharmony_ci * into the CBL ring along with Tx commands. The common structure 6062306a36Sopenharmony_ci * used for both Tx and non-Tx commands is the Command Block (CB). 6162306a36Sopenharmony_ci * 6262306a36Sopenharmony_ci * cb_to_use is the next CB to use for queuing a command; cb_to_clean 6362306a36Sopenharmony_ci * is the next CB to check for completion; cb_to_send is the first 6462306a36Sopenharmony_ci * CB to start on in case of a previous failure to resume. CB clean 6562306a36Sopenharmony_ci * up happens in interrupt context in response to a CU interrupt. 6662306a36Sopenharmony_ci * cbs_avail keeps track of number of free CB resources available. 6762306a36Sopenharmony_ci * 6862306a36Sopenharmony_ci * Hardware padding of short packets to minimum packet size is 6962306a36Sopenharmony_ci * enabled. 82557 pads with 7Eh, while the later controllers pad 7062306a36Sopenharmony_ci * with 00h. 7162306a36Sopenharmony_ci * 7262306a36Sopenharmony_ci * IV. Receive 7362306a36Sopenharmony_ci * 7462306a36Sopenharmony_ci * The Receive Frame Area (RFA) comprises a ring of Receive Frame 7562306a36Sopenharmony_ci * Descriptors (RFD) + data buffer, thus forming the simplified mode 7662306a36Sopenharmony_ci * memory structure. Rx skbs are allocated to contain both the RFD 7762306a36Sopenharmony_ci * and the data buffer, but the RFD is pulled off before the skb is 7862306a36Sopenharmony_ci * indicated. The data buffer is aligned such that encapsulated 7962306a36Sopenharmony_ci * protocol headers are u32-aligned. Since the RFD is part of the 8062306a36Sopenharmony_ci * mapped shared memory, and completion status is contained within 8162306a36Sopenharmony_ci * the RFD, the RFD must be dma_sync'ed to maintain a consistent 8262306a36Sopenharmony_ci * view from software and hardware. 8362306a36Sopenharmony_ci * 8462306a36Sopenharmony_ci * In order to keep updates to the RFD link field from colliding with 8562306a36Sopenharmony_ci * hardware writes to mark packets complete, we use the feature that 8662306a36Sopenharmony_ci * hardware will not write to a size 0 descriptor and mark the previous 8762306a36Sopenharmony_ci * packet as end-of-list (EL). After updating the link, we remove EL 8862306a36Sopenharmony_ci * and only then restore the size such that hardware may use the 8962306a36Sopenharmony_ci * previous-to-end RFD. 9062306a36Sopenharmony_ci * 9162306a36Sopenharmony_ci * Under typical operation, the receive unit (RU) is start once, 9262306a36Sopenharmony_ci * and the controller happily fills RFDs as frames arrive. If 9362306a36Sopenharmony_ci * replacement RFDs cannot be allocated, or the RU goes non-active, 9462306a36Sopenharmony_ci * the RU must be restarted. Frame arrival generates an interrupt, 9562306a36Sopenharmony_ci * and Rx indication and re-allocation happen in the same context, 9662306a36Sopenharmony_ci * therefore no locking is required. A software-generated interrupt 9762306a36Sopenharmony_ci * is generated from the watchdog to recover from a failed allocation 9862306a36Sopenharmony_ci * scenario where all Rx resources have been indicated and none re- 9962306a36Sopenharmony_ci * placed. 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * V. Miscellaneous 10262306a36Sopenharmony_ci * 10362306a36Sopenharmony_ci * VLAN offloading of tagging, stripping and filtering is not 10462306a36Sopenharmony_ci * supported, but driver will accommodate the extra 4-byte VLAN tag 10562306a36Sopenharmony_ci * for processing by upper layers. Tx/Rx Checksum offloading is not 10662306a36Sopenharmony_ci * supported. Tx Scatter/Gather is not supported. Jumbo Frames is 10762306a36Sopenharmony_ci * not supported (hardware limitation). 10862306a36Sopenharmony_ci * 10962306a36Sopenharmony_ci * MagicPacket(tm) WoL support is enabled/disabled via ethtool. 11062306a36Sopenharmony_ci * 11162306a36Sopenharmony_ci * Thanks to JC (jchapman@katalix.com) for helping with 11262306a36Sopenharmony_ci * testing/troubleshooting the development driver. 11362306a36Sopenharmony_ci * 11462306a36Sopenharmony_ci * TODO: 11562306a36Sopenharmony_ci * o several entry points race with dev->close 11662306a36Sopenharmony_ci * o check for tx-no-resources/stop Q races with tx clean/wake Q 11762306a36Sopenharmony_ci * 11862306a36Sopenharmony_ci * FIXES: 11962306a36Sopenharmony_ci * 2005/12/02 - Michael O'Donnell <Michael.ODonnell at stratus dot com> 12062306a36Sopenharmony_ci * - Stratus87247: protect MDI control register manipulations 12162306a36Sopenharmony_ci * 2009/06/01 - Andreas Mohr <andi at lisas dot de> 12262306a36Sopenharmony_ci * - add clean lowlevel I/O emulation for cards with MII-lacking PHYs 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci#include <linux/hardirq.h> 12862306a36Sopenharmony_ci#include <linux/interrupt.h> 12962306a36Sopenharmony_ci#include <linux/module.h> 13062306a36Sopenharmony_ci#include <linux/moduleparam.h> 13162306a36Sopenharmony_ci#include <linux/kernel.h> 13262306a36Sopenharmony_ci#include <linux/types.h> 13362306a36Sopenharmony_ci#include <linux/sched.h> 13462306a36Sopenharmony_ci#include <linux/slab.h> 13562306a36Sopenharmony_ci#include <linux/delay.h> 13662306a36Sopenharmony_ci#include <linux/init.h> 13762306a36Sopenharmony_ci#include <linux/pci.h> 13862306a36Sopenharmony_ci#include <linux/dma-mapping.h> 13962306a36Sopenharmony_ci#include <linux/dmapool.h> 14062306a36Sopenharmony_ci#include <linux/netdevice.h> 14162306a36Sopenharmony_ci#include <linux/etherdevice.h> 14262306a36Sopenharmony_ci#include <linux/mii.h> 14362306a36Sopenharmony_ci#include <linux/if_vlan.h> 14462306a36Sopenharmony_ci#include <linux/skbuff.h> 14562306a36Sopenharmony_ci#include <linux/ethtool.h> 14662306a36Sopenharmony_ci#include <linux/string.h> 14762306a36Sopenharmony_ci#include <linux/firmware.h> 14862306a36Sopenharmony_ci#include <linux/rtnetlink.h> 14962306a36Sopenharmony_ci#include <asm/unaligned.h> 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci#define DRV_NAME "e100" 15362306a36Sopenharmony_ci#define DRV_DESCRIPTION "Intel(R) PRO/100 Network Driver" 15462306a36Sopenharmony_ci#define DRV_COPYRIGHT "Copyright(c) 1999-2006 Intel Corporation" 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci#define E100_WATCHDOG_PERIOD (2 * HZ) 15762306a36Sopenharmony_ci#define E100_NAPI_WEIGHT 16 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci#define FIRMWARE_D101M "e100/d101m_ucode.bin" 16062306a36Sopenharmony_ci#define FIRMWARE_D101S "e100/d101s_ucode.bin" 16162306a36Sopenharmony_ci#define FIRMWARE_D102E "e100/d102e_ucode.bin" 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESCRIPTION); 16462306a36Sopenharmony_ciMODULE_AUTHOR(DRV_COPYRIGHT); 16562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 16662306a36Sopenharmony_ciMODULE_FIRMWARE(FIRMWARE_D101M); 16762306a36Sopenharmony_ciMODULE_FIRMWARE(FIRMWARE_D101S); 16862306a36Sopenharmony_ciMODULE_FIRMWARE(FIRMWARE_D102E); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic int debug = 3; 17162306a36Sopenharmony_cistatic int eeprom_bad_csum_allow = 0; 17262306a36Sopenharmony_cistatic int use_io = 0; 17362306a36Sopenharmony_cimodule_param(debug, int, 0); 17462306a36Sopenharmony_cimodule_param(eeprom_bad_csum_allow, int, 0); 17562306a36Sopenharmony_cimodule_param(use_io, int, 0); 17662306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); 17762306a36Sopenharmony_ciMODULE_PARM_DESC(eeprom_bad_csum_allow, "Allow bad eeprom checksums"); 17862306a36Sopenharmony_ciMODULE_PARM_DESC(use_io, "Force use of i/o access mode"); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci#define INTEL_8255X_ETHERNET_DEVICE(device_id, ich) {\ 18162306a36Sopenharmony_ci PCI_VENDOR_ID_INTEL, device_id, PCI_ANY_ID, PCI_ANY_ID, \ 18262306a36Sopenharmony_ci PCI_CLASS_NETWORK_ETHERNET << 8, 0xFFFF00, ich } 18362306a36Sopenharmony_cistatic const struct pci_device_id e100_id_table[] = { 18462306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1029, 0), 18562306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1030, 0), 18662306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1031, 3), 18762306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1032, 3), 18862306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1033, 3), 18962306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1034, 3), 19062306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1038, 3), 19162306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1039, 4), 19262306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x103A, 4), 19362306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x103B, 4), 19462306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x103C, 4), 19562306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x103D, 4), 19662306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x103E, 4), 19762306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1050, 5), 19862306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1051, 5), 19962306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1052, 5), 20062306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1053, 5), 20162306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1054, 5), 20262306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1055, 5), 20362306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1056, 5), 20462306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1057, 5), 20562306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1059, 0), 20662306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1064, 6), 20762306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1065, 6), 20862306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1066, 6), 20962306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1067, 6), 21062306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1068, 6), 21162306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1069, 6), 21262306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x106A, 6), 21362306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x106B, 6), 21462306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1091, 7), 21562306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1092, 7), 21662306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1093, 7), 21762306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1094, 7), 21862306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1095, 7), 21962306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x10fe, 7), 22062306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1209, 0), 22162306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x1229, 0), 22262306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x2449, 2), 22362306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x2459, 2), 22462306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x245D, 2), 22562306a36Sopenharmony_ci INTEL_8255X_ETHERNET_DEVICE(0x27DC, 7), 22662306a36Sopenharmony_ci { 0, } 22762306a36Sopenharmony_ci}; 22862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, e100_id_table); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cienum mac { 23162306a36Sopenharmony_ci mac_82557_D100_A = 0, 23262306a36Sopenharmony_ci mac_82557_D100_B = 1, 23362306a36Sopenharmony_ci mac_82557_D100_C = 2, 23462306a36Sopenharmony_ci mac_82558_D101_A4 = 4, 23562306a36Sopenharmony_ci mac_82558_D101_B0 = 5, 23662306a36Sopenharmony_ci mac_82559_D101M = 8, 23762306a36Sopenharmony_ci mac_82559_D101S = 9, 23862306a36Sopenharmony_ci mac_82550_D102 = 12, 23962306a36Sopenharmony_ci mac_82550_D102_C = 13, 24062306a36Sopenharmony_ci mac_82551_E = 14, 24162306a36Sopenharmony_ci mac_82551_F = 15, 24262306a36Sopenharmony_ci mac_82551_10 = 16, 24362306a36Sopenharmony_ci mac_unknown = 0xFF, 24462306a36Sopenharmony_ci}; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cienum phy { 24762306a36Sopenharmony_ci phy_100a = 0x000003E0, 24862306a36Sopenharmony_ci phy_100c = 0x035002A8, 24962306a36Sopenharmony_ci phy_82555_tx = 0x015002A8, 25062306a36Sopenharmony_ci phy_nsc_tx = 0x5C002000, 25162306a36Sopenharmony_ci phy_82562_et = 0x033002A8, 25262306a36Sopenharmony_ci phy_82562_em = 0x032002A8, 25362306a36Sopenharmony_ci phy_82562_ek = 0x031002A8, 25462306a36Sopenharmony_ci phy_82562_eh = 0x017002A8, 25562306a36Sopenharmony_ci phy_82552_v = 0xd061004d, 25662306a36Sopenharmony_ci phy_unknown = 0xFFFFFFFF, 25762306a36Sopenharmony_ci}; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci/* CSR (Control/Status Registers) */ 26062306a36Sopenharmony_cistruct csr { 26162306a36Sopenharmony_ci struct { 26262306a36Sopenharmony_ci u8 status; 26362306a36Sopenharmony_ci u8 stat_ack; 26462306a36Sopenharmony_ci u8 cmd_lo; 26562306a36Sopenharmony_ci u8 cmd_hi; 26662306a36Sopenharmony_ci u32 gen_ptr; 26762306a36Sopenharmony_ci } scb; 26862306a36Sopenharmony_ci u32 port; 26962306a36Sopenharmony_ci u16 flash_ctrl; 27062306a36Sopenharmony_ci u8 eeprom_ctrl_lo; 27162306a36Sopenharmony_ci u8 eeprom_ctrl_hi; 27262306a36Sopenharmony_ci u32 mdi_ctrl; 27362306a36Sopenharmony_ci u32 rx_dma_count; 27462306a36Sopenharmony_ci}; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cienum scb_status { 27762306a36Sopenharmony_ci rus_no_res = 0x08, 27862306a36Sopenharmony_ci rus_ready = 0x10, 27962306a36Sopenharmony_ci rus_mask = 0x3C, 28062306a36Sopenharmony_ci}; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cienum ru_state { 28362306a36Sopenharmony_ci RU_SUSPENDED = 0, 28462306a36Sopenharmony_ci RU_RUNNING = 1, 28562306a36Sopenharmony_ci RU_UNINITIALIZED = -1, 28662306a36Sopenharmony_ci}; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cienum scb_stat_ack { 28962306a36Sopenharmony_ci stat_ack_not_ours = 0x00, 29062306a36Sopenharmony_ci stat_ack_sw_gen = 0x04, 29162306a36Sopenharmony_ci stat_ack_rnr = 0x10, 29262306a36Sopenharmony_ci stat_ack_cu_idle = 0x20, 29362306a36Sopenharmony_ci stat_ack_frame_rx = 0x40, 29462306a36Sopenharmony_ci stat_ack_cu_cmd_done = 0x80, 29562306a36Sopenharmony_ci stat_ack_not_present = 0xFF, 29662306a36Sopenharmony_ci stat_ack_rx = (stat_ack_sw_gen | stat_ack_rnr | stat_ack_frame_rx), 29762306a36Sopenharmony_ci stat_ack_tx = (stat_ack_cu_idle | stat_ack_cu_cmd_done), 29862306a36Sopenharmony_ci}; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cienum scb_cmd_hi { 30162306a36Sopenharmony_ci irq_mask_none = 0x00, 30262306a36Sopenharmony_ci irq_mask_all = 0x01, 30362306a36Sopenharmony_ci irq_sw_gen = 0x02, 30462306a36Sopenharmony_ci}; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cienum scb_cmd_lo { 30762306a36Sopenharmony_ci cuc_nop = 0x00, 30862306a36Sopenharmony_ci ruc_start = 0x01, 30962306a36Sopenharmony_ci ruc_load_base = 0x06, 31062306a36Sopenharmony_ci cuc_start = 0x10, 31162306a36Sopenharmony_ci cuc_resume = 0x20, 31262306a36Sopenharmony_ci cuc_dump_addr = 0x40, 31362306a36Sopenharmony_ci cuc_dump_stats = 0x50, 31462306a36Sopenharmony_ci cuc_load_base = 0x60, 31562306a36Sopenharmony_ci cuc_dump_reset = 0x70, 31662306a36Sopenharmony_ci}; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cienum cuc_dump { 31962306a36Sopenharmony_ci cuc_dump_complete = 0x0000A005, 32062306a36Sopenharmony_ci cuc_dump_reset_complete = 0x0000A007, 32162306a36Sopenharmony_ci}; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cienum port { 32462306a36Sopenharmony_ci software_reset = 0x0000, 32562306a36Sopenharmony_ci selftest = 0x0001, 32662306a36Sopenharmony_ci selective_reset = 0x0002, 32762306a36Sopenharmony_ci}; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cienum eeprom_ctrl_lo { 33062306a36Sopenharmony_ci eesk = 0x01, 33162306a36Sopenharmony_ci eecs = 0x02, 33262306a36Sopenharmony_ci eedi = 0x04, 33362306a36Sopenharmony_ci eedo = 0x08, 33462306a36Sopenharmony_ci}; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cienum mdi_ctrl { 33762306a36Sopenharmony_ci mdi_write = 0x04000000, 33862306a36Sopenharmony_ci mdi_read = 0x08000000, 33962306a36Sopenharmony_ci mdi_ready = 0x10000000, 34062306a36Sopenharmony_ci}; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cienum eeprom_op { 34362306a36Sopenharmony_ci op_write = 0x05, 34462306a36Sopenharmony_ci op_read = 0x06, 34562306a36Sopenharmony_ci op_ewds = 0x10, 34662306a36Sopenharmony_ci op_ewen = 0x13, 34762306a36Sopenharmony_ci}; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cienum eeprom_offsets { 35062306a36Sopenharmony_ci eeprom_cnfg_mdix = 0x03, 35162306a36Sopenharmony_ci eeprom_phy_iface = 0x06, 35262306a36Sopenharmony_ci eeprom_id = 0x0A, 35362306a36Sopenharmony_ci eeprom_config_asf = 0x0D, 35462306a36Sopenharmony_ci eeprom_smbus_addr = 0x90, 35562306a36Sopenharmony_ci}; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cienum eeprom_cnfg_mdix { 35862306a36Sopenharmony_ci eeprom_mdix_enabled = 0x0080, 35962306a36Sopenharmony_ci}; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cienum eeprom_phy_iface { 36262306a36Sopenharmony_ci NoSuchPhy = 0, 36362306a36Sopenharmony_ci I82553AB, 36462306a36Sopenharmony_ci I82553C, 36562306a36Sopenharmony_ci I82503, 36662306a36Sopenharmony_ci DP83840, 36762306a36Sopenharmony_ci S80C240, 36862306a36Sopenharmony_ci S80C24, 36962306a36Sopenharmony_ci I82555, 37062306a36Sopenharmony_ci DP83840A = 10, 37162306a36Sopenharmony_ci}; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cienum eeprom_id { 37462306a36Sopenharmony_ci eeprom_id_wol = 0x0020, 37562306a36Sopenharmony_ci}; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cienum eeprom_config_asf { 37862306a36Sopenharmony_ci eeprom_asf = 0x8000, 37962306a36Sopenharmony_ci eeprom_gcl = 0x4000, 38062306a36Sopenharmony_ci}; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cienum cb_status { 38362306a36Sopenharmony_ci cb_complete = 0x8000, 38462306a36Sopenharmony_ci cb_ok = 0x2000, 38562306a36Sopenharmony_ci}; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci/* 38862306a36Sopenharmony_ci * cb_command - Command Block flags 38962306a36Sopenharmony_ci * @cb_tx_nc: 0: controller does CRC (normal), 1: CRC from skb memory 39062306a36Sopenharmony_ci */ 39162306a36Sopenharmony_cienum cb_command { 39262306a36Sopenharmony_ci cb_nop = 0x0000, 39362306a36Sopenharmony_ci cb_iaaddr = 0x0001, 39462306a36Sopenharmony_ci cb_config = 0x0002, 39562306a36Sopenharmony_ci cb_multi = 0x0003, 39662306a36Sopenharmony_ci cb_tx = 0x0004, 39762306a36Sopenharmony_ci cb_ucode = 0x0005, 39862306a36Sopenharmony_ci cb_dump = 0x0006, 39962306a36Sopenharmony_ci cb_tx_sf = 0x0008, 40062306a36Sopenharmony_ci cb_tx_nc = 0x0010, 40162306a36Sopenharmony_ci cb_cid = 0x1f00, 40262306a36Sopenharmony_ci cb_i = 0x2000, 40362306a36Sopenharmony_ci cb_s = 0x4000, 40462306a36Sopenharmony_ci cb_el = 0x8000, 40562306a36Sopenharmony_ci}; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistruct rfd { 40862306a36Sopenharmony_ci __le16 status; 40962306a36Sopenharmony_ci __le16 command; 41062306a36Sopenharmony_ci __le32 link; 41162306a36Sopenharmony_ci __le32 rbd; 41262306a36Sopenharmony_ci __le16 actual_size; 41362306a36Sopenharmony_ci __le16 size; 41462306a36Sopenharmony_ci}; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistruct rx { 41762306a36Sopenharmony_ci struct rx *next, *prev; 41862306a36Sopenharmony_ci struct sk_buff *skb; 41962306a36Sopenharmony_ci dma_addr_t dma_addr; 42062306a36Sopenharmony_ci}; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci#if defined(__BIG_ENDIAN_BITFIELD) 42362306a36Sopenharmony_ci#define X(a,b) b,a 42462306a36Sopenharmony_ci#else 42562306a36Sopenharmony_ci#define X(a,b) a,b 42662306a36Sopenharmony_ci#endif 42762306a36Sopenharmony_cistruct config { 42862306a36Sopenharmony_ci/*0*/ u8 X(byte_count:6, pad0:2); 42962306a36Sopenharmony_ci/*1*/ u8 X(X(rx_fifo_limit:4, tx_fifo_limit:3), pad1:1); 43062306a36Sopenharmony_ci/*2*/ u8 adaptive_ifs; 43162306a36Sopenharmony_ci/*3*/ u8 X(X(X(X(mwi_enable:1, type_enable:1), read_align_enable:1), 43262306a36Sopenharmony_ci term_write_cache_line:1), pad3:4); 43362306a36Sopenharmony_ci/*4*/ u8 X(rx_dma_max_count:7, pad4:1); 43462306a36Sopenharmony_ci/*5*/ u8 X(tx_dma_max_count:7, dma_max_count_enable:1); 43562306a36Sopenharmony_ci/*6*/ u8 X(X(X(X(X(X(X(late_scb_update:1, direct_rx_dma:1), 43662306a36Sopenharmony_ci tno_intr:1), cna_intr:1), standard_tcb:1), standard_stat_counter:1), 43762306a36Sopenharmony_ci rx_save_overruns : 1), rx_save_bad_frames : 1); 43862306a36Sopenharmony_ci/*7*/ u8 X(X(X(X(X(rx_discard_short_frames:1, tx_underrun_retry:2), 43962306a36Sopenharmony_ci pad7:2), rx_extended_rfd:1), tx_two_frames_in_fifo:1), 44062306a36Sopenharmony_ci tx_dynamic_tbd:1); 44162306a36Sopenharmony_ci/*8*/ u8 X(X(mii_mode:1, pad8:6), csma_disabled:1); 44262306a36Sopenharmony_ci/*9*/ u8 X(X(X(X(X(rx_tcpudp_checksum:1, pad9:3), vlan_arp_tco:1), 44362306a36Sopenharmony_ci link_status_wake:1), arp_wake:1), mcmatch_wake:1); 44462306a36Sopenharmony_ci/*10*/ u8 X(X(X(pad10:3, no_source_addr_insertion:1), preamble_length:2), 44562306a36Sopenharmony_ci loopback:2); 44662306a36Sopenharmony_ci/*11*/ u8 X(linear_priority:3, pad11:5); 44762306a36Sopenharmony_ci/*12*/ u8 X(X(linear_priority_mode:1, pad12:3), ifs:4); 44862306a36Sopenharmony_ci/*13*/ u8 ip_addr_lo; 44962306a36Sopenharmony_ci/*14*/ u8 ip_addr_hi; 45062306a36Sopenharmony_ci/*15*/ u8 X(X(X(X(X(X(X(promiscuous_mode:1, broadcast_disabled:1), 45162306a36Sopenharmony_ci wait_after_win:1), pad15_1:1), ignore_ul_bit:1), crc_16_bit:1), 45262306a36Sopenharmony_ci pad15_2:1), crs_or_cdt:1); 45362306a36Sopenharmony_ci/*16*/ u8 fc_delay_lo; 45462306a36Sopenharmony_ci/*17*/ u8 fc_delay_hi; 45562306a36Sopenharmony_ci/*18*/ u8 X(X(X(X(X(rx_stripping:1, tx_padding:1), rx_crc_transfer:1), 45662306a36Sopenharmony_ci rx_long_ok:1), fc_priority_threshold:3), pad18:1); 45762306a36Sopenharmony_ci/*19*/ u8 X(X(X(X(X(X(X(addr_wake:1, magic_packet_disable:1), 45862306a36Sopenharmony_ci fc_disable:1), fc_restop:1), fc_restart:1), fc_reject:1), 45962306a36Sopenharmony_ci full_duplex_force:1), full_duplex_pin:1); 46062306a36Sopenharmony_ci/*20*/ u8 X(X(X(pad20_1:5, fc_priority_location:1), multi_ia:1), pad20_2:1); 46162306a36Sopenharmony_ci/*21*/ u8 X(X(pad21_1:3, multicast_all:1), pad21_2:4); 46262306a36Sopenharmony_ci/*22*/ u8 X(X(rx_d102_mode:1, rx_vlan_drop:1), pad22:6); 46362306a36Sopenharmony_ci u8 pad_d102[9]; 46462306a36Sopenharmony_ci}; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci#define E100_MAX_MULTICAST_ADDRS 64 46762306a36Sopenharmony_cistruct multi { 46862306a36Sopenharmony_ci __le16 count; 46962306a36Sopenharmony_ci u8 addr[E100_MAX_MULTICAST_ADDRS * ETH_ALEN + 2/*pad*/]; 47062306a36Sopenharmony_ci}; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci/* Important: keep total struct u32-aligned */ 47362306a36Sopenharmony_ci#define UCODE_SIZE 134 47462306a36Sopenharmony_cistruct cb { 47562306a36Sopenharmony_ci __le16 status; 47662306a36Sopenharmony_ci __le16 command; 47762306a36Sopenharmony_ci __le32 link; 47862306a36Sopenharmony_ci union { 47962306a36Sopenharmony_ci u8 iaaddr[ETH_ALEN]; 48062306a36Sopenharmony_ci __le32 ucode[UCODE_SIZE]; 48162306a36Sopenharmony_ci struct config config; 48262306a36Sopenharmony_ci struct multi multi; 48362306a36Sopenharmony_ci struct { 48462306a36Sopenharmony_ci u32 tbd_array; 48562306a36Sopenharmony_ci u16 tcb_byte_count; 48662306a36Sopenharmony_ci u8 threshold; 48762306a36Sopenharmony_ci u8 tbd_count; 48862306a36Sopenharmony_ci struct { 48962306a36Sopenharmony_ci __le32 buf_addr; 49062306a36Sopenharmony_ci __le16 size; 49162306a36Sopenharmony_ci u16 eol; 49262306a36Sopenharmony_ci } tbd; 49362306a36Sopenharmony_ci } tcb; 49462306a36Sopenharmony_ci __le32 dump_buffer_addr; 49562306a36Sopenharmony_ci } u; 49662306a36Sopenharmony_ci struct cb *next, *prev; 49762306a36Sopenharmony_ci dma_addr_t dma_addr; 49862306a36Sopenharmony_ci struct sk_buff *skb; 49962306a36Sopenharmony_ci}; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cienum loopback { 50262306a36Sopenharmony_ci lb_none = 0, lb_mac = 1, lb_phy = 3, 50362306a36Sopenharmony_ci}; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistruct stats { 50662306a36Sopenharmony_ci __le32 tx_good_frames, tx_max_collisions, tx_late_collisions, 50762306a36Sopenharmony_ci tx_underruns, tx_lost_crs, tx_deferred, tx_single_collisions, 50862306a36Sopenharmony_ci tx_multiple_collisions, tx_total_collisions; 50962306a36Sopenharmony_ci __le32 rx_good_frames, rx_crc_errors, rx_alignment_errors, 51062306a36Sopenharmony_ci rx_resource_errors, rx_overrun_errors, rx_cdt_errors, 51162306a36Sopenharmony_ci rx_short_frame_errors; 51262306a36Sopenharmony_ci __le32 fc_xmt_pause, fc_rcv_pause, fc_rcv_unsupported; 51362306a36Sopenharmony_ci __le16 xmt_tco_frames, rcv_tco_frames; 51462306a36Sopenharmony_ci __le32 complete; 51562306a36Sopenharmony_ci}; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistruct mem { 51862306a36Sopenharmony_ci struct { 51962306a36Sopenharmony_ci u32 signature; 52062306a36Sopenharmony_ci u32 result; 52162306a36Sopenharmony_ci } selftest; 52262306a36Sopenharmony_ci struct stats stats; 52362306a36Sopenharmony_ci u8 dump_buf[596]; 52462306a36Sopenharmony_ci}; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistruct param_range { 52762306a36Sopenharmony_ci u32 min; 52862306a36Sopenharmony_ci u32 max; 52962306a36Sopenharmony_ci u32 count; 53062306a36Sopenharmony_ci}; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistruct params { 53362306a36Sopenharmony_ci struct param_range rfds; 53462306a36Sopenharmony_ci struct param_range cbs; 53562306a36Sopenharmony_ci}; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_cistruct nic { 53862306a36Sopenharmony_ci /* Begin: frequently used values: keep adjacent for cache effect */ 53962306a36Sopenharmony_ci u32 msg_enable ____cacheline_aligned; 54062306a36Sopenharmony_ci struct net_device *netdev; 54162306a36Sopenharmony_ci struct pci_dev *pdev; 54262306a36Sopenharmony_ci u16 (*mdio_ctrl)(struct nic *nic, u32 addr, u32 dir, u32 reg, u16 data); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci struct rx *rxs ____cacheline_aligned; 54562306a36Sopenharmony_ci struct rx *rx_to_use; 54662306a36Sopenharmony_ci struct rx *rx_to_clean; 54762306a36Sopenharmony_ci struct rfd blank_rfd; 54862306a36Sopenharmony_ci enum ru_state ru_running; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci spinlock_t cb_lock ____cacheline_aligned; 55162306a36Sopenharmony_ci spinlock_t cmd_lock; 55262306a36Sopenharmony_ci struct csr __iomem *csr; 55362306a36Sopenharmony_ci enum scb_cmd_lo cuc_cmd; 55462306a36Sopenharmony_ci unsigned int cbs_avail; 55562306a36Sopenharmony_ci struct napi_struct napi; 55662306a36Sopenharmony_ci struct cb *cbs; 55762306a36Sopenharmony_ci struct cb *cb_to_use; 55862306a36Sopenharmony_ci struct cb *cb_to_send; 55962306a36Sopenharmony_ci struct cb *cb_to_clean; 56062306a36Sopenharmony_ci __le16 tx_command; 56162306a36Sopenharmony_ci /* End: frequently used values: keep adjacent for cache effect */ 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci enum { 56462306a36Sopenharmony_ci ich = (1 << 0), 56562306a36Sopenharmony_ci promiscuous = (1 << 1), 56662306a36Sopenharmony_ci multicast_all = (1 << 2), 56762306a36Sopenharmony_ci wol_magic = (1 << 3), 56862306a36Sopenharmony_ci ich_10h_workaround = (1 << 4), 56962306a36Sopenharmony_ci } flags ____cacheline_aligned; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci enum mac mac; 57262306a36Sopenharmony_ci enum phy phy; 57362306a36Sopenharmony_ci struct params params; 57462306a36Sopenharmony_ci struct timer_list watchdog; 57562306a36Sopenharmony_ci struct mii_if_info mii; 57662306a36Sopenharmony_ci struct work_struct tx_timeout_task; 57762306a36Sopenharmony_ci enum loopback loopback; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci struct mem *mem; 58062306a36Sopenharmony_ci dma_addr_t dma_addr; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci struct dma_pool *cbs_pool; 58362306a36Sopenharmony_ci dma_addr_t cbs_dma_addr; 58462306a36Sopenharmony_ci u8 adaptive_ifs; 58562306a36Sopenharmony_ci u8 tx_threshold; 58662306a36Sopenharmony_ci u32 tx_frames; 58762306a36Sopenharmony_ci u32 tx_collisions; 58862306a36Sopenharmony_ci u32 tx_deferred; 58962306a36Sopenharmony_ci u32 tx_single_collisions; 59062306a36Sopenharmony_ci u32 tx_multiple_collisions; 59162306a36Sopenharmony_ci u32 tx_fc_pause; 59262306a36Sopenharmony_ci u32 tx_tco_frames; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci u32 rx_fc_pause; 59562306a36Sopenharmony_ci u32 rx_fc_unsupported; 59662306a36Sopenharmony_ci u32 rx_tco_frames; 59762306a36Sopenharmony_ci u32 rx_short_frame_errors; 59862306a36Sopenharmony_ci u32 rx_over_length_errors; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci u16 eeprom_wc; 60162306a36Sopenharmony_ci __le16 eeprom[256]; 60262306a36Sopenharmony_ci spinlock_t mdio_lock; 60362306a36Sopenharmony_ci const struct firmware *fw; 60462306a36Sopenharmony_ci}; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cistatic inline void e100_write_flush(struct nic *nic) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci /* Flush previous PCI writes through intermediate bridges 60962306a36Sopenharmony_ci * by doing a benign read */ 61062306a36Sopenharmony_ci (void)ioread8(&nic->csr->scb.status); 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cistatic void e100_enable_irq(struct nic *nic) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci unsigned long flags; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci spin_lock_irqsave(&nic->cmd_lock, flags); 61862306a36Sopenharmony_ci iowrite8(irq_mask_none, &nic->csr->scb.cmd_hi); 61962306a36Sopenharmony_ci e100_write_flush(nic); 62062306a36Sopenharmony_ci spin_unlock_irqrestore(&nic->cmd_lock, flags); 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic void e100_disable_irq(struct nic *nic) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci unsigned long flags; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci spin_lock_irqsave(&nic->cmd_lock, flags); 62862306a36Sopenharmony_ci iowrite8(irq_mask_all, &nic->csr->scb.cmd_hi); 62962306a36Sopenharmony_ci e100_write_flush(nic); 63062306a36Sopenharmony_ci spin_unlock_irqrestore(&nic->cmd_lock, flags); 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic void e100_hw_reset(struct nic *nic) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci /* Put CU and RU into idle with a selective reset to get 63662306a36Sopenharmony_ci * device off of PCI bus */ 63762306a36Sopenharmony_ci iowrite32(selective_reset, &nic->csr->port); 63862306a36Sopenharmony_ci e100_write_flush(nic); udelay(20); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci /* Now fully reset device */ 64162306a36Sopenharmony_ci iowrite32(software_reset, &nic->csr->port); 64262306a36Sopenharmony_ci e100_write_flush(nic); udelay(20); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci /* Mask off our interrupt line - it's unmasked after reset */ 64562306a36Sopenharmony_ci e100_disable_irq(nic); 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cistatic int e100_self_test(struct nic *nic) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci u32 dma_addr = nic->dma_addr + offsetof(struct mem, selftest); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci /* Passing the self-test is a pretty good indication 65362306a36Sopenharmony_ci * that the device can DMA to/from host memory */ 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci nic->mem->selftest.signature = 0; 65662306a36Sopenharmony_ci nic->mem->selftest.result = 0xFFFFFFFF; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci iowrite32(selftest | dma_addr, &nic->csr->port); 65962306a36Sopenharmony_ci e100_write_flush(nic); 66062306a36Sopenharmony_ci /* Wait 10 msec for self-test to complete */ 66162306a36Sopenharmony_ci msleep(10); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci /* Interrupts are enabled after self-test */ 66462306a36Sopenharmony_ci e100_disable_irq(nic); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci /* Check results of self-test */ 66762306a36Sopenharmony_ci if (nic->mem->selftest.result != 0) { 66862306a36Sopenharmony_ci netif_err(nic, hw, nic->netdev, 66962306a36Sopenharmony_ci "Self-test failed: result=0x%08X\n", 67062306a36Sopenharmony_ci nic->mem->selftest.result); 67162306a36Sopenharmony_ci return -ETIMEDOUT; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci if (nic->mem->selftest.signature == 0) { 67462306a36Sopenharmony_ci netif_err(nic, hw, nic->netdev, "Self-test failed: timed out\n"); 67562306a36Sopenharmony_ci return -ETIMEDOUT; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci return 0; 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic void e100_eeprom_write(struct nic *nic, u16 addr_len, u16 addr, __le16 data) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci u32 cmd_addr_data[3]; 68462306a36Sopenharmony_ci u8 ctrl; 68562306a36Sopenharmony_ci int i, j; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci /* Three cmds: write/erase enable, write data, write/erase disable */ 68862306a36Sopenharmony_ci cmd_addr_data[0] = op_ewen << (addr_len - 2); 68962306a36Sopenharmony_ci cmd_addr_data[1] = (((op_write << addr_len) | addr) << 16) | 69062306a36Sopenharmony_ci le16_to_cpu(data); 69162306a36Sopenharmony_ci cmd_addr_data[2] = op_ewds << (addr_len - 2); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci /* Bit-bang cmds to write word to eeprom */ 69462306a36Sopenharmony_ci for (j = 0; j < 3; j++) { 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci /* Chip select */ 69762306a36Sopenharmony_ci iowrite8(eecs | eesk, &nic->csr->eeprom_ctrl_lo); 69862306a36Sopenharmony_ci e100_write_flush(nic); udelay(4); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci for (i = 31; i >= 0; i--) { 70162306a36Sopenharmony_ci ctrl = (cmd_addr_data[j] & (1 << i)) ? 70262306a36Sopenharmony_ci eecs | eedi : eecs; 70362306a36Sopenharmony_ci iowrite8(ctrl, &nic->csr->eeprom_ctrl_lo); 70462306a36Sopenharmony_ci e100_write_flush(nic); udelay(4); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci iowrite8(ctrl | eesk, &nic->csr->eeprom_ctrl_lo); 70762306a36Sopenharmony_ci e100_write_flush(nic); udelay(4); 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_ci /* Wait 10 msec for cmd to complete */ 71062306a36Sopenharmony_ci msleep(10); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci /* Chip deselect */ 71362306a36Sopenharmony_ci iowrite8(0, &nic->csr->eeprom_ctrl_lo); 71462306a36Sopenharmony_ci e100_write_flush(nic); udelay(4); 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci}; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci/* General technique stolen from the eepro100 driver - very clever */ 71962306a36Sopenharmony_cistatic __le16 e100_eeprom_read(struct nic *nic, u16 *addr_len, u16 addr) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci u32 cmd_addr_data; 72262306a36Sopenharmony_ci u16 data = 0; 72362306a36Sopenharmony_ci u8 ctrl; 72462306a36Sopenharmony_ci int i; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci cmd_addr_data = ((op_read << *addr_len) | addr) << 16; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci /* Chip select */ 72962306a36Sopenharmony_ci iowrite8(eecs | eesk, &nic->csr->eeprom_ctrl_lo); 73062306a36Sopenharmony_ci e100_write_flush(nic); udelay(4); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci /* Bit-bang to read word from eeprom */ 73362306a36Sopenharmony_ci for (i = 31; i >= 0; i--) { 73462306a36Sopenharmony_ci ctrl = (cmd_addr_data & (1 << i)) ? eecs | eedi : eecs; 73562306a36Sopenharmony_ci iowrite8(ctrl, &nic->csr->eeprom_ctrl_lo); 73662306a36Sopenharmony_ci e100_write_flush(nic); udelay(4); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci iowrite8(ctrl | eesk, &nic->csr->eeprom_ctrl_lo); 73962306a36Sopenharmony_ci e100_write_flush(nic); udelay(4); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci /* Eeprom drives a dummy zero to EEDO after receiving 74262306a36Sopenharmony_ci * complete address. Use this to adjust addr_len. */ 74362306a36Sopenharmony_ci ctrl = ioread8(&nic->csr->eeprom_ctrl_lo); 74462306a36Sopenharmony_ci if (!(ctrl & eedo) && i > 16) { 74562306a36Sopenharmony_ci *addr_len -= (i - 16); 74662306a36Sopenharmony_ci i = 17; 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci data = (data << 1) | (ctrl & eedo ? 1 : 0); 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci /* Chip deselect */ 75362306a36Sopenharmony_ci iowrite8(0, &nic->csr->eeprom_ctrl_lo); 75462306a36Sopenharmony_ci e100_write_flush(nic); udelay(4); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci return cpu_to_le16(data); 75762306a36Sopenharmony_ci}; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci/* Load entire EEPROM image into driver cache and validate checksum */ 76062306a36Sopenharmony_cistatic int e100_eeprom_load(struct nic *nic) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci u16 addr, addr_len = 8, checksum = 0; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci /* Try reading with an 8-bit addr len to discover actual addr len */ 76562306a36Sopenharmony_ci e100_eeprom_read(nic, &addr_len, 0); 76662306a36Sopenharmony_ci nic->eeprom_wc = 1 << addr_len; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci for (addr = 0; addr < nic->eeprom_wc; addr++) { 76962306a36Sopenharmony_ci nic->eeprom[addr] = e100_eeprom_read(nic, &addr_len, addr); 77062306a36Sopenharmony_ci if (addr < nic->eeprom_wc - 1) 77162306a36Sopenharmony_ci checksum += le16_to_cpu(nic->eeprom[addr]); 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci /* The checksum, stored in the last word, is calculated such that 77562306a36Sopenharmony_ci * the sum of words should be 0xBABA */ 77662306a36Sopenharmony_ci if (cpu_to_le16(0xBABA - checksum) != nic->eeprom[nic->eeprom_wc - 1]) { 77762306a36Sopenharmony_ci netif_err(nic, probe, nic->netdev, "EEPROM corrupted\n"); 77862306a36Sopenharmony_ci if (!eeprom_bad_csum_allow) 77962306a36Sopenharmony_ci return -EAGAIN; 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci return 0; 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci/* Save (portion of) driver EEPROM cache to device and update checksum */ 78662306a36Sopenharmony_cistatic int e100_eeprom_save(struct nic *nic, u16 start, u16 count) 78762306a36Sopenharmony_ci{ 78862306a36Sopenharmony_ci u16 addr, addr_len = 8, checksum = 0; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci /* Try reading with an 8-bit addr len to discover actual addr len */ 79162306a36Sopenharmony_ci e100_eeprom_read(nic, &addr_len, 0); 79262306a36Sopenharmony_ci nic->eeprom_wc = 1 << addr_len; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci if (start + count >= nic->eeprom_wc) 79562306a36Sopenharmony_ci return -EINVAL; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci for (addr = start; addr < start + count; addr++) 79862306a36Sopenharmony_ci e100_eeprom_write(nic, addr_len, addr, nic->eeprom[addr]); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci /* The checksum, stored in the last word, is calculated such that 80162306a36Sopenharmony_ci * the sum of words should be 0xBABA */ 80262306a36Sopenharmony_ci for (addr = 0; addr < nic->eeprom_wc - 1; addr++) 80362306a36Sopenharmony_ci checksum += le16_to_cpu(nic->eeprom[addr]); 80462306a36Sopenharmony_ci nic->eeprom[nic->eeprom_wc - 1] = cpu_to_le16(0xBABA - checksum); 80562306a36Sopenharmony_ci e100_eeprom_write(nic, addr_len, nic->eeprom_wc - 1, 80662306a36Sopenharmony_ci nic->eeprom[nic->eeprom_wc - 1]); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci return 0; 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci#define E100_WAIT_SCB_TIMEOUT 20000 /* we might have to wait 100ms!!! */ 81262306a36Sopenharmony_ci#define E100_WAIT_SCB_FAST 20 /* delay like the old code */ 81362306a36Sopenharmony_cistatic int e100_exec_cmd(struct nic *nic, u8 cmd, dma_addr_t dma_addr) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci unsigned long flags; 81662306a36Sopenharmony_ci unsigned int i; 81762306a36Sopenharmony_ci int err = 0; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci spin_lock_irqsave(&nic->cmd_lock, flags); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci /* Previous command is accepted when SCB clears */ 82262306a36Sopenharmony_ci for (i = 0; i < E100_WAIT_SCB_TIMEOUT; i++) { 82362306a36Sopenharmony_ci if (likely(!ioread8(&nic->csr->scb.cmd_lo))) 82462306a36Sopenharmony_ci break; 82562306a36Sopenharmony_ci cpu_relax(); 82662306a36Sopenharmony_ci if (unlikely(i > E100_WAIT_SCB_FAST)) 82762306a36Sopenharmony_ci udelay(5); 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci if (unlikely(i == E100_WAIT_SCB_TIMEOUT)) { 83062306a36Sopenharmony_ci err = -EAGAIN; 83162306a36Sopenharmony_ci goto err_unlock; 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci if (unlikely(cmd != cuc_resume)) 83562306a36Sopenharmony_ci iowrite32(dma_addr, &nic->csr->scb.gen_ptr); 83662306a36Sopenharmony_ci iowrite8(cmd, &nic->csr->scb.cmd_lo); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_cierr_unlock: 83962306a36Sopenharmony_ci spin_unlock_irqrestore(&nic->cmd_lock, flags); 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci return err; 84262306a36Sopenharmony_ci} 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_cistatic int e100_exec_cb(struct nic *nic, struct sk_buff *skb, 84562306a36Sopenharmony_ci int (*cb_prepare)(struct nic *, struct cb *, struct sk_buff *)) 84662306a36Sopenharmony_ci{ 84762306a36Sopenharmony_ci struct cb *cb; 84862306a36Sopenharmony_ci unsigned long flags; 84962306a36Sopenharmony_ci int err; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci spin_lock_irqsave(&nic->cb_lock, flags); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci if (unlikely(!nic->cbs_avail)) { 85462306a36Sopenharmony_ci err = -ENOMEM; 85562306a36Sopenharmony_ci goto err_unlock; 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci cb = nic->cb_to_use; 85962306a36Sopenharmony_ci nic->cb_to_use = cb->next; 86062306a36Sopenharmony_ci nic->cbs_avail--; 86162306a36Sopenharmony_ci cb->skb = skb; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci err = cb_prepare(nic, cb, skb); 86462306a36Sopenharmony_ci if (err) 86562306a36Sopenharmony_ci goto err_unlock; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci if (unlikely(!nic->cbs_avail)) 86862306a36Sopenharmony_ci err = -ENOSPC; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci /* Order is important otherwise we'll be in a race with h/w: 87262306a36Sopenharmony_ci * set S-bit in current first, then clear S-bit in previous. */ 87362306a36Sopenharmony_ci cb->command |= cpu_to_le16(cb_s); 87462306a36Sopenharmony_ci dma_wmb(); 87562306a36Sopenharmony_ci cb->prev->command &= cpu_to_le16(~cb_s); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci while (nic->cb_to_send != nic->cb_to_use) { 87862306a36Sopenharmony_ci if (unlikely(e100_exec_cmd(nic, nic->cuc_cmd, 87962306a36Sopenharmony_ci nic->cb_to_send->dma_addr))) { 88062306a36Sopenharmony_ci /* Ok, here's where things get sticky. It's 88162306a36Sopenharmony_ci * possible that we can't schedule the command 88262306a36Sopenharmony_ci * because the controller is too busy, so 88362306a36Sopenharmony_ci * let's just queue the command and try again 88462306a36Sopenharmony_ci * when another command is scheduled. */ 88562306a36Sopenharmony_ci if (err == -ENOSPC) { 88662306a36Sopenharmony_ci //request a reset 88762306a36Sopenharmony_ci schedule_work(&nic->tx_timeout_task); 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci break; 89062306a36Sopenharmony_ci } else { 89162306a36Sopenharmony_ci nic->cuc_cmd = cuc_resume; 89262306a36Sopenharmony_ci nic->cb_to_send = nic->cb_to_send->next; 89362306a36Sopenharmony_ci } 89462306a36Sopenharmony_ci } 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_cierr_unlock: 89762306a36Sopenharmony_ci spin_unlock_irqrestore(&nic->cb_lock, flags); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci return err; 90062306a36Sopenharmony_ci} 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_cistatic int mdio_read(struct net_device *netdev, int addr, int reg) 90362306a36Sopenharmony_ci{ 90462306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 90562306a36Sopenharmony_ci return nic->mdio_ctrl(nic, addr, mdi_read, reg, 0); 90662306a36Sopenharmony_ci} 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_cistatic void mdio_write(struct net_device *netdev, int addr, int reg, int data) 90962306a36Sopenharmony_ci{ 91062306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci nic->mdio_ctrl(nic, addr, mdi_write, reg, data); 91362306a36Sopenharmony_ci} 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci/* the standard mdio_ctrl() function for usual MII-compliant hardware */ 91662306a36Sopenharmony_cistatic u16 mdio_ctrl_hw(struct nic *nic, u32 addr, u32 dir, u32 reg, u16 data) 91762306a36Sopenharmony_ci{ 91862306a36Sopenharmony_ci u32 data_out = 0; 91962306a36Sopenharmony_ci unsigned int i; 92062306a36Sopenharmony_ci unsigned long flags; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci /* 92462306a36Sopenharmony_ci * Stratus87247: we shouldn't be writing the MDI control 92562306a36Sopenharmony_ci * register until the Ready bit shows True. Also, since 92662306a36Sopenharmony_ci * manipulation of the MDI control registers is a multi-step 92762306a36Sopenharmony_ci * procedure it should be done under lock. 92862306a36Sopenharmony_ci */ 92962306a36Sopenharmony_ci spin_lock_irqsave(&nic->mdio_lock, flags); 93062306a36Sopenharmony_ci for (i = 100; i; --i) { 93162306a36Sopenharmony_ci if (ioread32(&nic->csr->mdi_ctrl) & mdi_ready) 93262306a36Sopenharmony_ci break; 93362306a36Sopenharmony_ci udelay(20); 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci if (unlikely(!i)) { 93662306a36Sopenharmony_ci netdev_err(nic->netdev, "e100.mdio_ctrl won't go Ready\n"); 93762306a36Sopenharmony_ci spin_unlock_irqrestore(&nic->mdio_lock, flags); 93862306a36Sopenharmony_ci return 0; /* No way to indicate timeout error */ 93962306a36Sopenharmony_ci } 94062306a36Sopenharmony_ci iowrite32((reg << 16) | (addr << 21) | dir | data, &nic->csr->mdi_ctrl); 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci for (i = 0; i < 100; i++) { 94362306a36Sopenharmony_ci udelay(20); 94462306a36Sopenharmony_ci if ((data_out = ioread32(&nic->csr->mdi_ctrl)) & mdi_ready) 94562306a36Sopenharmony_ci break; 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci spin_unlock_irqrestore(&nic->mdio_lock, flags); 94862306a36Sopenharmony_ci netif_printk(nic, hw, KERN_DEBUG, nic->netdev, 94962306a36Sopenharmony_ci "%s:addr=%d, reg=%d, data_in=0x%04X, data_out=0x%04X\n", 95062306a36Sopenharmony_ci dir == mdi_read ? "READ" : "WRITE", 95162306a36Sopenharmony_ci addr, reg, data, data_out); 95262306a36Sopenharmony_ci return (u16)data_out; 95362306a36Sopenharmony_ci} 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci/* slightly tweaked mdio_ctrl() function for phy_82552_v specifics */ 95662306a36Sopenharmony_cistatic u16 mdio_ctrl_phy_82552_v(struct nic *nic, 95762306a36Sopenharmony_ci u32 addr, 95862306a36Sopenharmony_ci u32 dir, 95962306a36Sopenharmony_ci u32 reg, 96062306a36Sopenharmony_ci u16 data) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci if ((reg == MII_BMCR) && (dir == mdi_write)) { 96362306a36Sopenharmony_ci if (data & (BMCR_ANRESTART | BMCR_ANENABLE)) { 96462306a36Sopenharmony_ci u16 advert = mdio_read(nic->netdev, nic->mii.phy_id, 96562306a36Sopenharmony_ci MII_ADVERTISE); 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci /* 96862306a36Sopenharmony_ci * Workaround Si issue where sometimes the part will not 96962306a36Sopenharmony_ci * autoneg to 100Mbps even when advertised. 97062306a36Sopenharmony_ci */ 97162306a36Sopenharmony_ci if (advert & ADVERTISE_100FULL) 97262306a36Sopenharmony_ci data |= BMCR_SPEED100 | BMCR_FULLDPLX; 97362306a36Sopenharmony_ci else if (advert & ADVERTISE_100HALF) 97462306a36Sopenharmony_ci data |= BMCR_SPEED100; 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci } 97762306a36Sopenharmony_ci return mdio_ctrl_hw(nic, addr, dir, reg, data); 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci/* Fully software-emulated mdio_ctrl() function for cards without 98162306a36Sopenharmony_ci * MII-compliant PHYs. 98262306a36Sopenharmony_ci * For now, this is mainly geared towards 80c24 support; in case of further 98362306a36Sopenharmony_ci * requirements for other types (i82503, ...?) either extend this mechanism 98462306a36Sopenharmony_ci * or split it, whichever is cleaner. 98562306a36Sopenharmony_ci */ 98662306a36Sopenharmony_cistatic u16 mdio_ctrl_phy_mii_emulated(struct nic *nic, 98762306a36Sopenharmony_ci u32 addr, 98862306a36Sopenharmony_ci u32 dir, 98962306a36Sopenharmony_ci u32 reg, 99062306a36Sopenharmony_ci u16 data) 99162306a36Sopenharmony_ci{ 99262306a36Sopenharmony_ci /* might need to allocate a netdev_priv'ed register array eventually 99362306a36Sopenharmony_ci * to be able to record state changes, but for now 99462306a36Sopenharmony_ci * some fully hardcoded register handling ought to be ok I guess. */ 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci if (dir == mdi_read) { 99762306a36Sopenharmony_ci switch (reg) { 99862306a36Sopenharmony_ci case MII_BMCR: 99962306a36Sopenharmony_ci /* Auto-negotiation, right? */ 100062306a36Sopenharmony_ci return BMCR_ANENABLE | 100162306a36Sopenharmony_ci BMCR_FULLDPLX; 100262306a36Sopenharmony_ci case MII_BMSR: 100362306a36Sopenharmony_ci return BMSR_LSTATUS /* for mii_link_ok() */ | 100462306a36Sopenharmony_ci BMSR_ANEGCAPABLE | 100562306a36Sopenharmony_ci BMSR_10FULL; 100662306a36Sopenharmony_ci case MII_ADVERTISE: 100762306a36Sopenharmony_ci /* 80c24 is a "combo card" PHY, right? */ 100862306a36Sopenharmony_ci return ADVERTISE_10HALF | 100962306a36Sopenharmony_ci ADVERTISE_10FULL; 101062306a36Sopenharmony_ci default: 101162306a36Sopenharmony_ci netif_printk(nic, hw, KERN_DEBUG, nic->netdev, 101262306a36Sopenharmony_ci "%s:addr=%d, reg=%d, data=0x%04X: unimplemented emulation!\n", 101362306a36Sopenharmony_ci dir == mdi_read ? "READ" : "WRITE", 101462306a36Sopenharmony_ci addr, reg, data); 101562306a36Sopenharmony_ci return 0xFFFF; 101662306a36Sopenharmony_ci } 101762306a36Sopenharmony_ci } else { 101862306a36Sopenharmony_ci switch (reg) { 101962306a36Sopenharmony_ci default: 102062306a36Sopenharmony_ci netif_printk(nic, hw, KERN_DEBUG, nic->netdev, 102162306a36Sopenharmony_ci "%s:addr=%d, reg=%d, data=0x%04X: unimplemented emulation!\n", 102262306a36Sopenharmony_ci dir == mdi_read ? "READ" : "WRITE", 102362306a36Sopenharmony_ci addr, reg, data); 102462306a36Sopenharmony_ci return 0xFFFF; 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci } 102762306a36Sopenharmony_ci} 102862306a36Sopenharmony_cistatic inline int e100_phy_supports_mii(struct nic *nic) 102962306a36Sopenharmony_ci{ 103062306a36Sopenharmony_ci /* for now, just check it by comparing whether we 103162306a36Sopenharmony_ci are using MII software emulation. 103262306a36Sopenharmony_ci */ 103362306a36Sopenharmony_ci return (nic->mdio_ctrl != mdio_ctrl_phy_mii_emulated); 103462306a36Sopenharmony_ci} 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_cistatic void e100_get_defaults(struct nic *nic) 103762306a36Sopenharmony_ci{ 103862306a36Sopenharmony_ci struct param_range rfds = { .min = 16, .max = 256, .count = 256 }; 103962306a36Sopenharmony_ci struct param_range cbs = { .min = 64, .max = 256, .count = 128 }; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci /* MAC type is encoded as rev ID; exception: ICH is treated as 82559 */ 104262306a36Sopenharmony_ci nic->mac = (nic->flags & ich) ? mac_82559_D101M : nic->pdev->revision; 104362306a36Sopenharmony_ci if (nic->mac == mac_unknown) 104462306a36Sopenharmony_ci nic->mac = mac_82557_D100_A; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci nic->params.rfds = rfds; 104762306a36Sopenharmony_ci nic->params.cbs = cbs; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci /* Quadwords to DMA into FIFO before starting frame transmit */ 105062306a36Sopenharmony_ci nic->tx_threshold = 0xE0; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci /* no interrupt for every tx completion, delay = 256us if not 557 */ 105362306a36Sopenharmony_ci nic->tx_command = cpu_to_le16(cb_tx | cb_tx_sf | 105462306a36Sopenharmony_ci ((nic->mac >= mac_82558_D101_A4) ? cb_cid : cb_i)); 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci /* Template for a freshly allocated RFD */ 105762306a36Sopenharmony_ci nic->blank_rfd.command = 0; 105862306a36Sopenharmony_ci nic->blank_rfd.rbd = cpu_to_le32(0xFFFFFFFF); 105962306a36Sopenharmony_ci nic->blank_rfd.size = cpu_to_le16(VLAN_ETH_FRAME_LEN + ETH_FCS_LEN); 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci /* MII setup */ 106262306a36Sopenharmony_ci nic->mii.phy_id_mask = 0x1F; 106362306a36Sopenharmony_ci nic->mii.reg_num_mask = 0x1F; 106462306a36Sopenharmony_ci nic->mii.dev = nic->netdev; 106562306a36Sopenharmony_ci nic->mii.mdio_read = mdio_read; 106662306a36Sopenharmony_ci nic->mii.mdio_write = mdio_write; 106762306a36Sopenharmony_ci} 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_cistatic int e100_configure(struct nic *nic, struct cb *cb, struct sk_buff *skb) 107062306a36Sopenharmony_ci{ 107162306a36Sopenharmony_ci struct config *config = &cb->u.config; 107262306a36Sopenharmony_ci u8 *c = (u8 *)config; 107362306a36Sopenharmony_ci struct net_device *netdev = nic->netdev; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci cb->command = cpu_to_le16(cb_config); 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci memset(config, 0, sizeof(struct config)); 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci config->byte_count = 0x16; /* bytes in this struct */ 108062306a36Sopenharmony_ci config->rx_fifo_limit = 0x8; /* bytes in FIFO before DMA */ 108162306a36Sopenharmony_ci config->direct_rx_dma = 0x1; /* reserved */ 108262306a36Sopenharmony_ci config->standard_tcb = 0x1; /* 1=standard, 0=extended */ 108362306a36Sopenharmony_ci config->standard_stat_counter = 0x1; /* 1=standard, 0=extended */ 108462306a36Sopenharmony_ci config->rx_discard_short_frames = 0x1; /* 1=discard, 0=pass */ 108562306a36Sopenharmony_ci config->tx_underrun_retry = 0x3; /* # of underrun retries */ 108662306a36Sopenharmony_ci if (e100_phy_supports_mii(nic)) 108762306a36Sopenharmony_ci config->mii_mode = 1; /* 1=MII mode, 0=i82503 mode */ 108862306a36Sopenharmony_ci config->pad10 = 0x6; 108962306a36Sopenharmony_ci config->no_source_addr_insertion = 0x1; /* 1=no, 0=yes */ 109062306a36Sopenharmony_ci config->preamble_length = 0x2; /* 0=1, 1=3, 2=7, 3=15 bytes */ 109162306a36Sopenharmony_ci config->ifs = 0x6; /* x16 = inter frame spacing */ 109262306a36Sopenharmony_ci config->ip_addr_hi = 0xF2; /* ARP IP filter - not used */ 109362306a36Sopenharmony_ci config->pad15_1 = 0x1; 109462306a36Sopenharmony_ci config->pad15_2 = 0x1; 109562306a36Sopenharmony_ci config->crs_or_cdt = 0x0; /* 0=CRS only, 1=CRS or CDT */ 109662306a36Sopenharmony_ci config->fc_delay_hi = 0x40; /* time delay for fc frame */ 109762306a36Sopenharmony_ci config->tx_padding = 0x1; /* 1=pad short frames */ 109862306a36Sopenharmony_ci config->fc_priority_threshold = 0x7; /* 7=priority fc disabled */ 109962306a36Sopenharmony_ci config->pad18 = 0x1; 110062306a36Sopenharmony_ci config->full_duplex_pin = 0x1; /* 1=examine FDX# pin */ 110162306a36Sopenharmony_ci config->pad20_1 = 0x1F; 110262306a36Sopenharmony_ci config->fc_priority_location = 0x1; /* 1=byte#31, 0=byte#19 */ 110362306a36Sopenharmony_ci config->pad21_1 = 0x5; 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci config->adaptive_ifs = nic->adaptive_ifs; 110662306a36Sopenharmony_ci config->loopback = nic->loopback; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci if (nic->mii.force_media && nic->mii.full_duplex) 110962306a36Sopenharmony_ci config->full_duplex_force = 0x1; /* 1=force, 0=auto */ 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci if (nic->flags & promiscuous || nic->loopback) { 111262306a36Sopenharmony_ci config->rx_save_bad_frames = 0x1; /* 1=save, 0=discard */ 111362306a36Sopenharmony_ci config->rx_discard_short_frames = 0x0; /* 1=discard, 0=save */ 111462306a36Sopenharmony_ci config->promiscuous_mode = 0x1; /* 1=on, 0=off */ 111562306a36Sopenharmony_ci } 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci if (unlikely(netdev->features & NETIF_F_RXFCS)) 111862306a36Sopenharmony_ci config->rx_crc_transfer = 0x1; /* 1=save, 0=discard */ 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci if (nic->flags & multicast_all) 112162306a36Sopenharmony_ci config->multicast_all = 0x1; /* 1=accept, 0=no */ 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci /* disable WoL when up */ 112462306a36Sopenharmony_ci if (netif_running(nic->netdev) || !(nic->flags & wol_magic)) 112562306a36Sopenharmony_ci config->magic_packet_disable = 0x1; /* 1=off, 0=on */ 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci if (nic->mac >= mac_82558_D101_A4) { 112862306a36Sopenharmony_ci config->fc_disable = 0x1; /* 1=Tx fc off, 0=Tx fc on */ 112962306a36Sopenharmony_ci config->mwi_enable = 0x1; /* 1=enable, 0=disable */ 113062306a36Sopenharmony_ci config->standard_tcb = 0x0; /* 1=standard, 0=extended */ 113162306a36Sopenharmony_ci config->rx_long_ok = 0x1; /* 1=VLANs ok, 0=standard */ 113262306a36Sopenharmony_ci if (nic->mac >= mac_82559_D101M) { 113362306a36Sopenharmony_ci config->tno_intr = 0x1; /* TCO stats enable */ 113462306a36Sopenharmony_ci /* Enable TCO in extended config */ 113562306a36Sopenharmony_ci if (nic->mac >= mac_82551_10) { 113662306a36Sopenharmony_ci config->byte_count = 0x20; /* extended bytes */ 113762306a36Sopenharmony_ci config->rx_d102_mode = 0x1; /* GMRC for TCO */ 113862306a36Sopenharmony_ci } 113962306a36Sopenharmony_ci } else { 114062306a36Sopenharmony_ci config->standard_stat_counter = 0x0; 114162306a36Sopenharmony_ci } 114262306a36Sopenharmony_ci } 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci if (netdev->features & NETIF_F_RXALL) { 114562306a36Sopenharmony_ci config->rx_save_overruns = 0x1; /* 1=save, 0=discard */ 114662306a36Sopenharmony_ci config->rx_save_bad_frames = 0x1; /* 1=save, 0=discard */ 114762306a36Sopenharmony_ci config->rx_discard_short_frames = 0x0; /* 1=discard, 0=save */ 114862306a36Sopenharmony_ci } 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci netif_printk(nic, hw, KERN_DEBUG, nic->netdev, "[00-07]=%8ph\n", 115162306a36Sopenharmony_ci c + 0); 115262306a36Sopenharmony_ci netif_printk(nic, hw, KERN_DEBUG, nic->netdev, "[08-15]=%8ph\n", 115362306a36Sopenharmony_ci c + 8); 115462306a36Sopenharmony_ci netif_printk(nic, hw, KERN_DEBUG, nic->netdev, "[16-23]=%8ph\n", 115562306a36Sopenharmony_ci c + 16); 115662306a36Sopenharmony_ci return 0; 115762306a36Sopenharmony_ci} 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci/************************************************************************* 116062306a36Sopenharmony_ci* CPUSaver parameters 116162306a36Sopenharmony_ci* 116262306a36Sopenharmony_ci* All CPUSaver parameters are 16-bit literals that are part of a 116362306a36Sopenharmony_ci* "move immediate value" instruction. By changing the value of 116462306a36Sopenharmony_ci* the literal in the instruction before the code is loaded, the 116562306a36Sopenharmony_ci* driver can change the algorithm. 116662306a36Sopenharmony_ci* 116762306a36Sopenharmony_ci* INTDELAY - This loads the dead-man timer with its initial value. 116862306a36Sopenharmony_ci* When this timer expires the interrupt is asserted, and the 116962306a36Sopenharmony_ci* timer is reset each time a new packet is received. (see 117062306a36Sopenharmony_ci* BUNDLEMAX below to set the limit on number of chained packets) 117162306a36Sopenharmony_ci* The current default is 0x600 or 1536. Experiments show that 117262306a36Sopenharmony_ci* the value should probably stay within the 0x200 - 0x1000. 117362306a36Sopenharmony_ci* 117462306a36Sopenharmony_ci* BUNDLEMAX - 117562306a36Sopenharmony_ci* This sets the maximum number of frames that will be bundled. In 117662306a36Sopenharmony_ci* some situations, such as the TCP windowing algorithm, it may be 117762306a36Sopenharmony_ci* better to limit the growth of the bundle size than let it go as 117862306a36Sopenharmony_ci* high as it can, because that could cause too much added latency. 117962306a36Sopenharmony_ci* The default is six, because this is the number of packets in the 118062306a36Sopenharmony_ci* default TCP window size. A value of 1 would make CPUSaver indicate 118162306a36Sopenharmony_ci* an interrupt for every frame received. If you do not want to put 118262306a36Sopenharmony_ci* a limit on the bundle size, set this value to xFFFF. 118362306a36Sopenharmony_ci* 118462306a36Sopenharmony_ci* BUNDLESMALL - 118562306a36Sopenharmony_ci* This contains a bit-mask describing the minimum size frame that 118662306a36Sopenharmony_ci* will be bundled. The default masks the lower 7 bits, which means 118762306a36Sopenharmony_ci* that any frame less than 128 bytes in length will not be bundled, 118862306a36Sopenharmony_ci* but will instead immediately generate an interrupt. This does 118962306a36Sopenharmony_ci* not affect the current bundle in any way. Any frame that is 128 119062306a36Sopenharmony_ci* bytes or large will be bundled normally. This feature is meant 119162306a36Sopenharmony_ci* to provide immediate indication of ACK frames in a TCP environment. 119262306a36Sopenharmony_ci* Customers were seeing poor performance when a machine with CPUSaver 119362306a36Sopenharmony_ci* enabled was sending but not receiving. The delay introduced when 119462306a36Sopenharmony_ci* the ACKs were received was enough to reduce total throughput, because 119562306a36Sopenharmony_ci* the sender would sit idle until the ACK was finally seen. 119662306a36Sopenharmony_ci* 119762306a36Sopenharmony_ci* The current default is 0xFF80, which masks out the lower 7 bits. 119862306a36Sopenharmony_ci* This means that any frame which is x7F (127) bytes or smaller 119962306a36Sopenharmony_ci* will cause an immediate interrupt. Because this value must be a 120062306a36Sopenharmony_ci* bit mask, there are only a few valid values that can be used. To 120162306a36Sopenharmony_ci* turn this feature off, the driver can write the value xFFFF to the 120262306a36Sopenharmony_ci* lower word of this instruction (in the same way that the other 120362306a36Sopenharmony_ci* parameters are used). Likewise, a value of 0xF800 (2047) would 120462306a36Sopenharmony_ci* cause an interrupt to be generated for every frame, because all 120562306a36Sopenharmony_ci* standard Ethernet frames are <= 2047 bytes in length. 120662306a36Sopenharmony_ci*************************************************************************/ 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci/* if you wish to disable the ucode functionality, while maintaining the 120962306a36Sopenharmony_ci * workarounds it provides, set the following defines to: 121062306a36Sopenharmony_ci * BUNDLESMALL 0 121162306a36Sopenharmony_ci * BUNDLEMAX 1 121262306a36Sopenharmony_ci * INTDELAY 1 121362306a36Sopenharmony_ci */ 121462306a36Sopenharmony_ci#define BUNDLESMALL 1 121562306a36Sopenharmony_ci#define BUNDLEMAX (u16)6 121662306a36Sopenharmony_ci#define INTDELAY (u16)1536 /* 0x600 */ 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci/* Initialize firmware */ 121962306a36Sopenharmony_cistatic const struct firmware *e100_request_firmware(struct nic *nic) 122062306a36Sopenharmony_ci{ 122162306a36Sopenharmony_ci const char *fw_name; 122262306a36Sopenharmony_ci const struct firmware *fw = nic->fw; 122362306a36Sopenharmony_ci u8 timer, bundle, min_size; 122462306a36Sopenharmony_ci int err = 0; 122562306a36Sopenharmony_ci bool required = false; 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci /* do not load u-code for ICH devices */ 122862306a36Sopenharmony_ci if (nic->flags & ich) 122962306a36Sopenharmony_ci return NULL; 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci /* Search for ucode match against h/w revision 123262306a36Sopenharmony_ci * 123362306a36Sopenharmony_ci * Based on comments in the source code for the FreeBSD fxp 123462306a36Sopenharmony_ci * driver, the FIRMWARE_D102E ucode includes both CPUSaver and 123562306a36Sopenharmony_ci * 123662306a36Sopenharmony_ci * "fixes for bugs in the B-step hardware (specifically, bugs 123762306a36Sopenharmony_ci * with Inline Receive)." 123862306a36Sopenharmony_ci * 123962306a36Sopenharmony_ci * So we must fail if it cannot be loaded. 124062306a36Sopenharmony_ci * 124162306a36Sopenharmony_ci * The other microcode files are only required for the optional 124262306a36Sopenharmony_ci * CPUSaver feature. Nice to have, but no reason to fail. 124362306a36Sopenharmony_ci */ 124462306a36Sopenharmony_ci if (nic->mac == mac_82559_D101M) { 124562306a36Sopenharmony_ci fw_name = FIRMWARE_D101M; 124662306a36Sopenharmony_ci } else if (nic->mac == mac_82559_D101S) { 124762306a36Sopenharmony_ci fw_name = FIRMWARE_D101S; 124862306a36Sopenharmony_ci } else if (nic->mac == mac_82551_F || nic->mac == mac_82551_10) { 124962306a36Sopenharmony_ci fw_name = FIRMWARE_D102E; 125062306a36Sopenharmony_ci required = true; 125162306a36Sopenharmony_ci } else { /* No ucode on other devices */ 125262306a36Sopenharmony_ci return NULL; 125362306a36Sopenharmony_ci } 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci /* If the firmware has not previously been loaded, request a pointer 125662306a36Sopenharmony_ci * to it. If it was previously loaded, we are reinitializing the 125762306a36Sopenharmony_ci * adapter, possibly in a resume from hibernate, in which case 125862306a36Sopenharmony_ci * request_firmware() cannot be used. 125962306a36Sopenharmony_ci */ 126062306a36Sopenharmony_ci if (!fw) 126162306a36Sopenharmony_ci err = request_firmware(&fw, fw_name, &nic->pdev->dev); 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci if (err) { 126462306a36Sopenharmony_ci if (required) { 126562306a36Sopenharmony_ci netif_err(nic, probe, nic->netdev, 126662306a36Sopenharmony_ci "Failed to load firmware \"%s\": %d\n", 126762306a36Sopenharmony_ci fw_name, err); 126862306a36Sopenharmony_ci return ERR_PTR(err); 126962306a36Sopenharmony_ci } else { 127062306a36Sopenharmony_ci netif_info(nic, probe, nic->netdev, 127162306a36Sopenharmony_ci "CPUSaver disabled. Needs \"%s\": %d\n", 127262306a36Sopenharmony_ci fw_name, err); 127362306a36Sopenharmony_ci return NULL; 127462306a36Sopenharmony_ci } 127562306a36Sopenharmony_ci } 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci /* Firmware should be precisely UCODE_SIZE (words) plus three bytes 127862306a36Sopenharmony_ci indicating the offsets for BUNDLESMALL, BUNDLEMAX, INTDELAY */ 127962306a36Sopenharmony_ci if (fw->size != UCODE_SIZE * 4 + 3) { 128062306a36Sopenharmony_ci netif_err(nic, probe, nic->netdev, 128162306a36Sopenharmony_ci "Firmware \"%s\" has wrong size %zu\n", 128262306a36Sopenharmony_ci fw_name, fw->size); 128362306a36Sopenharmony_ci release_firmware(fw); 128462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 128562306a36Sopenharmony_ci } 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci /* Read timer, bundle and min_size from end of firmware blob */ 128862306a36Sopenharmony_ci timer = fw->data[UCODE_SIZE * 4]; 128962306a36Sopenharmony_ci bundle = fw->data[UCODE_SIZE * 4 + 1]; 129062306a36Sopenharmony_ci min_size = fw->data[UCODE_SIZE * 4 + 2]; 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci if (timer >= UCODE_SIZE || bundle >= UCODE_SIZE || 129362306a36Sopenharmony_ci min_size >= UCODE_SIZE) { 129462306a36Sopenharmony_ci netif_err(nic, probe, nic->netdev, 129562306a36Sopenharmony_ci "\"%s\" has bogus offset values (0x%x,0x%x,0x%x)\n", 129662306a36Sopenharmony_ci fw_name, timer, bundle, min_size); 129762306a36Sopenharmony_ci release_firmware(fw); 129862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 129962306a36Sopenharmony_ci } 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci /* OK, firmware is validated and ready to use. Save a pointer 130262306a36Sopenharmony_ci * to it in the nic */ 130362306a36Sopenharmony_ci nic->fw = fw; 130462306a36Sopenharmony_ci return fw; 130562306a36Sopenharmony_ci} 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_cistatic int e100_setup_ucode(struct nic *nic, struct cb *cb, 130862306a36Sopenharmony_ci struct sk_buff *skb) 130962306a36Sopenharmony_ci{ 131062306a36Sopenharmony_ci const struct firmware *fw = (void *)skb; 131162306a36Sopenharmony_ci u8 timer, bundle, min_size; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci /* It's not a real skb; we just abused the fact that e100_exec_cb 131462306a36Sopenharmony_ci will pass it through to here... */ 131562306a36Sopenharmony_ci cb->skb = NULL; 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci /* firmware is stored as little endian already */ 131862306a36Sopenharmony_ci memcpy(cb->u.ucode, fw->data, UCODE_SIZE * 4); 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci /* Read timer, bundle and min_size from end of firmware blob */ 132162306a36Sopenharmony_ci timer = fw->data[UCODE_SIZE * 4]; 132262306a36Sopenharmony_ci bundle = fw->data[UCODE_SIZE * 4 + 1]; 132362306a36Sopenharmony_ci min_size = fw->data[UCODE_SIZE * 4 + 2]; 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci /* Insert user-tunable settings in cb->u.ucode */ 132662306a36Sopenharmony_ci cb->u.ucode[timer] &= cpu_to_le32(0xFFFF0000); 132762306a36Sopenharmony_ci cb->u.ucode[timer] |= cpu_to_le32(INTDELAY); 132862306a36Sopenharmony_ci cb->u.ucode[bundle] &= cpu_to_le32(0xFFFF0000); 132962306a36Sopenharmony_ci cb->u.ucode[bundle] |= cpu_to_le32(BUNDLEMAX); 133062306a36Sopenharmony_ci cb->u.ucode[min_size] &= cpu_to_le32(0xFFFF0000); 133162306a36Sopenharmony_ci cb->u.ucode[min_size] |= cpu_to_le32((BUNDLESMALL) ? 0xFFFF : 0xFF80); 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci cb->command = cpu_to_le16(cb_ucode | cb_el); 133462306a36Sopenharmony_ci return 0; 133562306a36Sopenharmony_ci} 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_cistatic inline int e100_load_ucode_wait(struct nic *nic) 133862306a36Sopenharmony_ci{ 133962306a36Sopenharmony_ci const struct firmware *fw; 134062306a36Sopenharmony_ci int err = 0, counter = 50; 134162306a36Sopenharmony_ci struct cb *cb = nic->cb_to_clean; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci fw = e100_request_firmware(nic); 134462306a36Sopenharmony_ci /* If it's NULL, then no ucode is required */ 134562306a36Sopenharmony_ci if (IS_ERR_OR_NULL(fw)) 134662306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(fw); 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci if ((err = e100_exec_cb(nic, (void *)fw, e100_setup_ucode))) 134962306a36Sopenharmony_ci netif_err(nic, probe, nic->netdev, 135062306a36Sopenharmony_ci "ucode cmd failed with error %d\n", err); 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci /* must restart cuc */ 135362306a36Sopenharmony_ci nic->cuc_cmd = cuc_start; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci /* wait for completion */ 135662306a36Sopenharmony_ci e100_write_flush(nic); 135762306a36Sopenharmony_ci udelay(10); 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci /* wait for possibly (ouch) 500ms */ 136062306a36Sopenharmony_ci while (!(cb->status & cpu_to_le16(cb_complete))) { 136162306a36Sopenharmony_ci msleep(10); 136262306a36Sopenharmony_ci if (!--counter) break; 136362306a36Sopenharmony_ci } 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci /* ack any interrupts, something could have been set */ 136662306a36Sopenharmony_ci iowrite8(~0, &nic->csr->scb.stat_ack); 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci /* if the command failed, or is not OK, notify and return */ 136962306a36Sopenharmony_ci if (!counter || !(cb->status & cpu_to_le16(cb_ok))) { 137062306a36Sopenharmony_ci netif_err(nic, probe, nic->netdev, "ucode load failed\n"); 137162306a36Sopenharmony_ci err = -EPERM; 137262306a36Sopenharmony_ci } 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci return err; 137562306a36Sopenharmony_ci} 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_cistatic int e100_setup_iaaddr(struct nic *nic, struct cb *cb, 137862306a36Sopenharmony_ci struct sk_buff *skb) 137962306a36Sopenharmony_ci{ 138062306a36Sopenharmony_ci cb->command = cpu_to_le16(cb_iaaddr); 138162306a36Sopenharmony_ci memcpy(cb->u.iaaddr, nic->netdev->dev_addr, ETH_ALEN); 138262306a36Sopenharmony_ci return 0; 138362306a36Sopenharmony_ci} 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_cistatic int e100_dump(struct nic *nic, struct cb *cb, struct sk_buff *skb) 138662306a36Sopenharmony_ci{ 138762306a36Sopenharmony_ci cb->command = cpu_to_le16(cb_dump); 138862306a36Sopenharmony_ci cb->u.dump_buffer_addr = cpu_to_le32(nic->dma_addr + 138962306a36Sopenharmony_ci offsetof(struct mem, dump_buf)); 139062306a36Sopenharmony_ci return 0; 139162306a36Sopenharmony_ci} 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_cistatic int e100_phy_check_without_mii(struct nic *nic) 139462306a36Sopenharmony_ci{ 139562306a36Sopenharmony_ci u8 phy_type; 139662306a36Sopenharmony_ci int without_mii; 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci phy_type = (le16_to_cpu(nic->eeprom[eeprom_phy_iface]) >> 8) & 0x0f; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci switch (phy_type) { 140162306a36Sopenharmony_ci case NoSuchPhy: /* Non-MII PHY; UNTESTED! */ 140262306a36Sopenharmony_ci case I82503: /* Non-MII PHY; UNTESTED! */ 140362306a36Sopenharmony_ci case S80C24: /* Non-MII PHY; tested and working */ 140462306a36Sopenharmony_ci /* paragraph from the FreeBSD driver, "FXP_PHY_80C24": 140562306a36Sopenharmony_ci * The Seeq 80c24 AutoDUPLEX(tm) Ethernet Interface Adapter 140662306a36Sopenharmony_ci * doesn't have a programming interface of any sort. The 140762306a36Sopenharmony_ci * media is sensed automatically based on how the link partner 140862306a36Sopenharmony_ci * is configured. This is, in essence, manual configuration. 140962306a36Sopenharmony_ci */ 141062306a36Sopenharmony_ci netif_info(nic, probe, nic->netdev, 141162306a36Sopenharmony_ci "found MII-less i82503 or 80c24 or other PHY\n"); 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci nic->mdio_ctrl = mdio_ctrl_phy_mii_emulated; 141462306a36Sopenharmony_ci nic->mii.phy_id = 0; /* is this ok for an MII-less PHY? */ 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci /* these might be needed for certain MII-less cards... 141762306a36Sopenharmony_ci * nic->flags |= ich; 141862306a36Sopenharmony_ci * nic->flags |= ich_10h_workaround; */ 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci without_mii = 1; 142162306a36Sopenharmony_ci break; 142262306a36Sopenharmony_ci default: 142362306a36Sopenharmony_ci without_mii = 0; 142462306a36Sopenharmony_ci break; 142562306a36Sopenharmony_ci } 142662306a36Sopenharmony_ci return without_mii; 142762306a36Sopenharmony_ci} 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci#define NCONFIG_AUTO_SWITCH 0x0080 143062306a36Sopenharmony_ci#define MII_NSC_CONG MII_RESV1 143162306a36Sopenharmony_ci#define NSC_CONG_ENABLE 0x0100 143262306a36Sopenharmony_ci#define NSC_CONG_TXREADY 0x0400 143362306a36Sopenharmony_cistatic int e100_phy_init(struct nic *nic) 143462306a36Sopenharmony_ci{ 143562306a36Sopenharmony_ci struct net_device *netdev = nic->netdev; 143662306a36Sopenharmony_ci u32 addr; 143762306a36Sopenharmony_ci u16 bmcr, stat, id_lo, id_hi, cong; 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci /* Discover phy addr by searching addrs in order {1,0,2,..., 31} */ 144062306a36Sopenharmony_ci for (addr = 0; addr < 32; addr++) { 144162306a36Sopenharmony_ci nic->mii.phy_id = (addr == 0) ? 1 : (addr == 1) ? 0 : addr; 144262306a36Sopenharmony_ci bmcr = mdio_read(netdev, nic->mii.phy_id, MII_BMCR); 144362306a36Sopenharmony_ci stat = mdio_read(netdev, nic->mii.phy_id, MII_BMSR); 144462306a36Sopenharmony_ci stat = mdio_read(netdev, nic->mii.phy_id, MII_BMSR); 144562306a36Sopenharmony_ci if (!((bmcr == 0xFFFF) || ((stat == 0) && (bmcr == 0)))) 144662306a36Sopenharmony_ci break; 144762306a36Sopenharmony_ci } 144862306a36Sopenharmony_ci if (addr == 32) { 144962306a36Sopenharmony_ci /* uhoh, no PHY detected: check whether we seem to be some 145062306a36Sopenharmony_ci * weird, rare variant which is *known* to not have any MII. 145162306a36Sopenharmony_ci * But do this AFTER MII checking only, since this does 145262306a36Sopenharmony_ci * lookup of EEPROM values which may easily be unreliable. */ 145362306a36Sopenharmony_ci if (e100_phy_check_without_mii(nic)) 145462306a36Sopenharmony_ci return 0; /* simply return and hope for the best */ 145562306a36Sopenharmony_ci else { 145662306a36Sopenharmony_ci /* for unknown cases log a fatal error */ 145762306a36Sopenharmony_ci netif_err(nic, hw, nic->netdev, 145862306a36Sopenharmony_ci "Failed to locate any known PHY, aborting\n"); 145962306a36Sopenharmony_ci return -EAGAIN; 146062306a36Sopenharmony_ci } 146162306a36Sopenharmony_ci } else 146262306a36Sopenharmony_ci netif_printk(nic, hw, KERN_DEBUG, nic->netdev, 146362306a36Sopenharmony_ci "phy_addr = %d\n", nic->mii.phy_id); 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci /* Get phy ID */ 146662306a36Sopenharmony_ci id_lo = mdio_read(netdev, nic->mii.phy_id, MII_PHYSID1); 146762306a36Sopenharmony_ci id_hi = mdio_read(netdev, nic->mii.phy_id, MII_PHYSID2); 146862306a36Sopenharmony_ci nic->phy = (u32)id_hi << 16 | (u32)id_lo; 146962306a36Sopenharmony_ci netif_printk(nic, hw, KERN_DEBUG, nic->netdev, 147062306a36Sopenharmony_ci "phy ID = 0x%08X\n", nic->phy); 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci /* Select the phy and isolate the rest */ 147362306a36Sopenharmony_ci for (addr = 0; addr < 32; addr++) { 147462306a36Sopenharmony_ci if (addr != nic->mii.phy_id) { 147562306a36Sopenharmony_ci mdio_write(netdev, addr, MII_BMCR, BMCR_ISOLATE); 147662306a36Sopenharmony_ci } else if (nic->phy != phy_82552_v) { 147762306a36Sopenharmony_ci bmcr = mdio_read(netdev, addr, MII_BMCR); 147862306a36Sopenharmony_ci mdio_write(netdev, addr, MII_BMCR, 147962306a36Sopenharmony_ci bmcr & ~BMCR_ISOLATE); 148062306a36Sopenharmony_ci } 148162306a36Sopenharmony_ci } 148262306a36Sopenharmony_ci /* 148362306a36Sopenharmony_ci * Workaround for 82552: 148462306a36Sopenharmony_ci * Clear the ISOLATE bit on selected phy_id last (mirrored on all 148562306a36Sopenharmony_ci * other phy_id's) using bmcr value from addr discovery loop above. 148662306a36Sopenharmony_ci */ 148762306a36Sopenharmony_ci if (nic->phy == phy_82552_v) 148862306a36Sopenharmony_ci mdio_write(netdev, nic->mii.phy_id, MII_BMCR, 148962306a36Sopenharmony_ci bmcr & ~BMCR_ISOLATE); 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci /* Handle National tx phys */ 149262306a36Sopenharmony_ci#define NCS_PHY_MODEL_MASK 0xFFF0FFFF 149362306a36Sopenharmony_ci if ((nic->phy & NCS_PHY_MODEL_MASK) == phy_nsc_tx) { 149462306a36Sopenharmony_ci /* Disable congestion control */ 149562306a36Sopenharmony_ci cong = mdio_read(netdev, nic->mii.phy_id, MII_NSC_CONG); 149662306a36Sopenharmony_ci cong |= NSC_CONG_TXREADY; 149762306a36Sopenharmony_ci cong &= ~NSC_CONG_ENABLE; 149862306a36Sopenharmony_ci mdio_write(netdev, nic->mii.phy_id, MII_NSC_CONG, cong); 149962306a36Sopenharmony_ci } 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci if (nic->phy == phy_82552_v) { 150262306a36Sopenharmony_ci u16 advert = mdio_read(netdev, nic->mii.phy_id, MII_ADVERTISE); 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci /* assign special tweaked mdio_ctrl() function */ 150562306a36Sopenharmony_ci nic->mdio_ctrl = mdio_ctrl_phy_82552_v; 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci /* Workaround Si not advertising flow-control during autoneg */ 150862306a36Sopenharmony_ci advert |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM; 150962306a36Sopenharmony_ci mdio_write(netdev, nic->mii.phy_id, MII_ADVERTISE, advert); 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci /* Reset for the above changes to take effect */ 151262306a36Sopenharmony_ci bmcr = mdio_read(netdev, nic->mii.phy_id, MII_BMCR); 151362306a36Sopenharmony_ci bmcr |= BMCR_RESET; 151462306a36Sopenharmony_ci mdio_write(netdev, nic->mii.phy_id, MII_BMCR, bmcr); 151562306a36Sopenharmony_ci } else if ((nic->mac >= mac_82550_D102) || ((nic->flags & ich) && 151662306a36Sopenharmony_ci (mdio_read(netdev, nic->mii.phy_id, MII_TPISTATUS) & 0x8000) && 151762306a36Sopenharmony_ci (le16_to_cpu(nic->eeprom[eeprom_cnfg_mdix]) & eeprom_mdix_enabled))) { 151862306a36Sopenharmony_ci /* enable/disable MDI/MDI-X auto-switching. */ 151962306a36Sopenharmony_ci mdio_write(netdev, nic->mii.phy_id, MII_NCONFIG, 152062306a36Sopenharmony_ci nic->mii.force_media ? 0 : NCONFIG_AUTO_SWITCH); 152162306a36Sopenharmony_ci } 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci return 0; 152462306a36Sopenharmony_ci} 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_cistatic int e100_hw_init(struct nic *nic) 152762306a36Sopenharmony_ci{ 152862306a36Sopenharmony_ci int err = 0; 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci e100_hw_reset(nic); 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci netif_err(nic, hw, nic->netdev, "e100_hw_init\n"); 153362306a36Sopenharmony_ci if ((err = e100_self_test(nic))) 153462306a36Sopenharmony_ci return err; 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci if ((err = e100_phy_init(nic))) 153762306a36Sopenharmony_ci return err; 153862306a36Sopenharmony_ci if ((err = e100_exec_cmd(nic, cuc_load_base, 0))) 153962306a36Sopenharmony_ci return err; 154062306a36Sopenharmony_ci if ((err = e100_exec_cmd(nic, ruc_load_base, 0))) 154162306a36Sopenharmony_ci return err; 154262306a36Sopenharmony_ci if ((err = e100_load_ucode_wait(nic))) 154362306a36Sopenharmony_ci return err; 154462306a36Sopenharmony_ci if ((err = e100_exec_cb(nic, NULL, e100_configure))) 154562306a36Sopenharmony_ci return err; 154662306a36Sopenharmony_ci if ((err = e100_exec_cb(nic, NULL, e100_setup_iaaddr))) 154762306a36Sopenharmony_ci return err; 154862306a36Sopenharmony_ci if ((err = e100_exec_cmd(nic, cuc_dump_addr, 154962306a36Sopenharmony_ci nic->dma_addr + offsetof(struct mem, stats)))) 155062306a36Sopenharmony_ci return err; 155162306a36Sopenharmony_ci if ((err = e100_exec_cmd(nic, cuc_dump_reset, 0))) 155262306a36Sopenharmony_ci return err; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci e100_disable_irq(nic); 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci return 0; 155762306a36Sopenharmony_ci} 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_cistatic int e100_multi(struct nic *nic, struct cb *cb, struct sk_buff *skb) 156062306a36Sopenharmony_ci{ 156162306a36Sopenharmony_ci struct net_device *netdev = nic->netdev; 156262306a36Sopenharmony_ci struct netdev_hw_addr *ha; 156362306a36Sopenharmony_ci u16 i, count = min(netdev_mc_count(netdev), E100_MAX_MULTICAST_ADDRS); 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci cb->command = cpu_to_le16(cb_multi); 156662306a36Sopenharmony_ci cb->u.multi.count = cpu_to_le16(count * ETH_ALEN); 156762306a36Sopenharmony_ci i = 0; 156862306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, netdev) { 156962306a36Sopenharmony_ci if (i == count) 157062306a36Sopenharmony_ci break; 157162306a36Sopenharmony_ci memcpy(&cb->u.multi.addr[i++ * ETH_ALEN], &ha->addr, 157262306a36Sopenharmony_ci ETH_ALEN); 157362306a36Sopenharmony_ci } 157462306a36Sopenharmony_ci return 0; 157562306a36Sopenharmony_ci} 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_cistatic void e100_set_multicast_list(struct net_device *netdev) 157862306a36Sopenharmony_ci{ 157962306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci netif_printk(nic, hw, KERN_DEBUG, nic->netdev, 158262306a36Sopenharmony_ci "mc_count=%d, flags=0x%04X\n", 158362306a36Sopenharmony_ci netdev_mc_count(netdev), netdev->flags); 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci if (netdev->flags & IFF_PROMISC) 158662306a36Sopenharmony_ci nic->flags |= promiscuous; 158762306a36Sopenharmony_ci else 158862306a36Sopenharmony_ci nic->flags &= ~promiscuous; 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci if (netdev->flags & IFF_ALLMULTI || 159162306a36Sopenharmony_ci netdev_mc_count(netdev) > E100_MAX_MULTICAST_ADDRS) 159262306a36Sopenharmony_ci nic->flags |= multicast_all; 159362306a36Sopenharmony_ci else 159462306a36Sopenharmony_ci nic->flags &= ~multicast_all; 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci e100_exec_cb(nic, NULL, e100_configure); 159762306a36Sopenharmony_ci e100_exec_cb(nic, NULL, e100_multi); 159862306a36Sopenharmony_ci} 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_cistatic void e100_update_stats(struct nic *nic) 160162306a36Sopenharmony_ci{ 160262306a36Sopenharmony_ci struct net_device *dev = nic->netdev; 160362306a36Sopenharmony_ci struct net_device_stats *ns = &dev->stats; 160462306a36Sopenharmony_ci struct stats *s = &nic->mem->stats; 160562306a36Sopenharmony_ci __le32 *complete = (nic->mac < mac_82558_D101_A4) ? &s->fc_xmt_pause : 160662306a36Sopenharmony_ci (nic->mac < mac_82559_D101M) ? (__le32 *)&s->xmt_tco_frames : 160762306a36Sopenharmony_ci &s->complete; 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci /* Device's stats reporting may take several microseconds to 161062306a36Sopenharmony_ci * complete, so we're always waiting for results of the 161162306a36Sopenharmony_ci * previous command. */ 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci if (*complete == cpu_to_le32(cuc_dump_reset_complete)) { 161462306a36Sopenharmony_ci *complete = 0; 161562306a36Sopenharmony_ci nic->tx_frames = le32_to_cpu(s->tx_good_frames); 161662306a36Sopenharmony_ci nic->tx_collisions = le32_to_cpu(s->tx_total_collisions); 161762306a36Sopenharmony_ci ns->tx_aborted_errors += le32_to_cpu(s->tx_max_collisions); 161862306a36Sopenharmony_ci ns->tx_window_errors += le32_to_cpu(s->tx_late_collisions); 161962306a36Sopenharmony_ci ns->tx_carrier_errors += le32_to_cpu(s->tx_lost_crs); 162062306a36Sopenharmony_ci ns->tx_fifo_errors += le32_to_cpu(s->tx_underruns); 162162306a36Sopenharmony_ci ns->collisions += nic->tx_collisions; 162262306a36Sopenharmony_ci ns->tx_errors += le32_to_cpu(s->tx_max_collisions) + 162362306a36Sopenharmony_ci le32_to_cpu(s->tx_lost_crs); 162462306a36Sopenharmony_ci nic->rx_short_frame_errors += 162562306a36Sopenharmony_ci le32_to_cpu(s->rx_short_frame_errors); 162662306a36Sopenharmony_ci ns->rx_length_errors = nic->rx_short_frame_errors + 162762306a36Sopenharmony_ci nic->rx_over_length_errors; 162862306a36Sopenharmony_ci ns->rx_crc_errors += le32_to_cpu(s->rx_crc_errors); 162962306a36Sopenharmony_ci ns->rx_frame_errors += le32_to_cpu(s->rx_alignment_errors); 163062306a36Sopenharmony_ci ns->rx_over_errors += le32_to_cpu(s->rx_overrun_errors); 163162306a36Sopenharmony_ci ns->rx_fifo_errors += le32_to_cpu(s->rx_overrun_errors); 163262306a36Sopenharmony_ci ns->rx_missed_errors += le32_to_cpu(s->rx_resource_errors); 163362306a36Sopenharmony_ci ns->rx_errors += le32_to_cpu(s->rx_crc_errors) + 163462306a36Sopenharmony_ci le32_to_cpu(s->rx_alignment_errors) + 163562306a36Sopenharmony_ci le32_to_cpu(s->rx_short_frame_errors) + 163662306a36Sopenharmony_ci le32_to_cpu(s->rx_cdt_errors); 163762306a36Sopenharmony_ci nic->tx_deferred += le32_to_cpu(s->tx_deferred); 163862306a36Sopenharmony_ci nic->tx_single_collisions += 163962306a36Sopenharmony_ci le32_to_cpu(s->tx_single_collisions); 164062306a36Sopenharmony_ci nic->tx_multiple_collisions += 164162306a36Sopenharmony_ci le32_to_cpu(s->tx_multiple_collisions); 164262306a36Sopenharmony_ci if (nic->mac >= mac_82558_D101_A4) { 164362306a36Sopenharmony_ci nic->tx_fc_pause += le32_to_cpu(s->fc_xmt_pause); 164462306a36Sopenharmony_ci nic->rx_fc_pause += le32_to_cpu(s->fc_rcv_pause); 164562306a36Sopenharmony_ci nic->rx_fc_unsupported += 164662306a36Sopenharmony_ci le32_to_cpu(s->fc_rcv_unsupported); 164762306a36Sopenharmony_ci if (nic->mac >= mac_82559_D101M) { 164862306a36Sopenharmony_ci nic->tx_tco_frames += 164962306a36Sopenharmony_ci le16_to_cpu(s->xmt_tco_frames); 165062306a36Sopenharmony_ci nic->rx_tco_frames += 165162306a36Sopenharmony_ci le16_to_cpu(s->rcv_tco_frames); 165262306a36Sopenharmony_ci } 165362306a36Sopenharmony_ci } 165462306a36Sopenharmony_ci } 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci if (e100_exec_cmd(nic, cuc_dump_reset, 0)) 165862306a36Sopenharmony_ci netif_printk(nic, tx_err, KERN_DEBUG, nic->netdev, 165962306a36Sopenharmony_ci "exec cuc_dump_reset failed\n"); 166062306a36Sopenharmony_ci} 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_cistatic void e100_adjust_adaptive_ifs(struct nic *nic, int speed, int duplex) 166362306a36Sopenharmony_ci{ 166462306a36Sopenharmony_ci /* Adjust inter-frame-spacing (IFS) between two transmits if 166562306a36Sopenharmony_ci * we're getting collisions on a half-duplex connection. */ 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci if (duplex == DUPLEX_HALF) { 166862306a36Sopenharmony_ci u32 prev = nic->adaptive_ifs; 166962306a36Sopenharmony_ci u32 min_frames = (speed == SPEED_100) ? 1000 : 100; 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci if ((nic->tx_frames / 32 < nic->tx_collisions) && 167262306a36Sopenharmony_ci (nic->tx_frames > min_frames)) { 167362306a36Sopenharmony_ci if (nic->adaptive_ifs < 60) 167462306a36Sopenharmony_ci nic->adaptive_ifs += 5; 167562306a36Sopenharmony_ci } else if (nic->tx_frames < min_frames) { 167662306a36Sopenharmony_ci if (nic->adaptive_ifs >= 5) 167762306a36Sopenharmony_ci nic->adaptive_ifs -= 5; 167862306a36Sopenharmony_ci } 167962306a36Sopenharmony_ci if (nic->adaptive_ifs != prev) 168062306a36Sopenharmony_ci e100_exec_cb(nic, NULL, e100_configure); 168162306a36Sopenharmony_ci } 168262306a36Sopenharmony_ci} 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_cistatic void e100_watchdog(struct timer_list *t) 168562306a36Sopenharmony_ci{ 168662306a36Sopenharmony_ci struct nic *nic = from_timer(nic, t, watchdog); 168762306a36Sopenharmony_ci struct ethtool_cmd cmd = { .cmd = ETHTOOL_GSET }; 168862306a36Sopenharmony_ci u32 speed; 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci netif_printk(nic, timer, KERN_DEBUG, nic->netdev, 169162306a36Sopenharmony_ci "right now = %ld\n", jiffies); 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci /* mii library handles link maintenance tasks */ 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci mii_ethtool_gset(&nic->mii, &cmd); 169662306a36Sopenharmony_ci speed = ethtool_cmd_speed(&cmd); 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci if (mii_link_ok(&nic->mii) && !netif_carrier_ok(nic->netdev)) { 169962306a36Sopenharmony_ci netdev_info(nic->netdev, "NIC Link is Up %u Mbps %s Duplex\n", 170062306a36Sopenharmony_ci speed == SPEED_100 ? 100 : 10, 170162306a36Sopenharmony_ci cmd.duplex == DUPLEX_FULL ? "Full" : "Half"); 170262306a36Sopenharmony_ci } else if (!mii_link_ok(&nic->mii) && netif_carrier_ok(nic->netdev)) { 170362306a36Sopenharmony_ci netdev_info(nic->netdev, "NIC Link is Down\n"); 170462306a36Sopenharmony_ci } 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_ci mii_check_link(&nic->mii); 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci /* Software generated interrupt to recover from (rare) Rx 170962306a36Sopenharmony_ci * allocation failure. 171062306a36Sopenharmony_ci * Unfortunately have to use a spinlock to not re-enable interrupts 171162306a36Sopenharmony_ci * accidentally, due to hardware that shares a register between the 171262306a36Sopenharmony_ci * interrupt mask bit and the SW Interrupt generation bit */ 171362306a36Sopenharmony_ci spin_lock_irq(&nic->cmd_lock); 171462306a36Sopenharmony_ci iowrite8(ioread8(&nic->csr->scb.cmd_hi) | irq_sw_gen,&nic->csr->scb.cmd_hi); 171562306a36Sopenharmony_ci e100_write_flush(nic); 171662306a36Sopenharmony_ci spin_unlock_irq(&nic->cmd_lock); 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ci e100_update_stats(nic); 171962306a36Sopenharmony_ci e100_adjust_adaptive_ifs(nic, speed, cmd.duplex); 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci if (nic->mac <= mac_82557_D100_C) 172262306a36Sopenharmony_ci /* Issue a multicast command to workaround a 557 lock up */ 172362306a36Sopenharmony_ci e100_set_multicast_list(nic->netdev); 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci if (nic->flags & ich && speed == SPEED_10 && cmd.duplex == DUPLEX_HALF) 172662306a36Sopenharmony_ci /* Need SW workaround for ICH[x] 10Mbps/half duplex Tx hang. */ 172762306a36Sopenharmony_ci nic->flags |= ich_10h_workaround; 172862306a36Sopenharmony_ci else 172962306a36Sopenharmony_ci nic->flags &= ~ich_10h_workaround; 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci mod_timer(&nic->watchdog, 173262306a36Sopenharmony_ci round_jiffies(jiffies + E100_WATCHDOG_PERIOD)); 173362306a36Sopenharmony_ci} 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_cistatic int e100_xmit_prepare(struct nic *nic, struct cb *cb, 173662306a36Sopenharmony_ci struct sk_buff *skb) 173762306a36Sopenharmony_ci{ 173862306a36Sopenharmony_ci dma_addr_t dma_addr; 173962306a36Sopenharmony_ci cb->command = nic->tx_command; 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci dma_addr = dma_map_single(&nic->pdev->dev, skb->data, skb->len, 174262306a36Sopenharmony_ci DMA_TO_DEVICE); 174362306a36Sopenharmony_ci /* If we can't map the skb, have the upper layer try later */ 174462306a36Sopenharmony_ci if (dma_mapping_error(&nic->pdev->dev, dma_addr)) 174562306a36Sopenharmony_ci return -ENOMEM; 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci /* 174862306a36Sopenharmony_ci * Use the last 4 bytes of the SKB payload packet as the CRC, used for 174962306a36Sopenharmony_ci * testing, ie sending frames with bad CRC. 175062306a36Sopenharmony_ci */ 175162306a36Sopenharmony_ci if (unlikely(skb->no_fcs)) 175262306a36Sopenharmony_ci cb->command |= cpu_to_le16(cb_tx_nc); 175362306a36Sopenharmony_ci else 175462306a36Sopenharmony_ci cb->command &= ~cpu_to_le16(cb_tx_nc); 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci /* interrupt every 16 packets regardless of delay */ 175762306a36Sopenharmony_ci if ((nic->cbs_avail & ~15) == nic->cbs_avail) 175862306a36Sopenharmony_ci cb->command |= cpu_to_le16(cb_i); 175962306a36Sopenharmony_ci cb->u.tcb.tbd_array = cb->dma_addr + offsetof(struct cb, u.tcb.tbd); 176062306a36Sopenharmony_ci cb->u.tcb.tcb_byte_count = 0; 176162306a36Sopenharmony_ci cb->u.tcb.threshold = nic->tx_threshold; 176262306a36Sopenharmony_ci cb->u.tcb.tbd_count = 1; 176362306a36Sopenharmony_ci cb->u.tcb.tbd.buf_addr = cpu_to_le32(dma_addr); 176462306a36Sopenharmony_ci cb->u.tcb.tbd.size = cpu_to_le16(skb->len); 176562306a36Sopenharmony_ci skb_tx_timestamp(skb); 176662306a36Sopenharmony_ci return 0; 176762306a36Sopenharmony_ci} 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_cistatic netdev_tx_t e100_xmit_frame(struct sk_buff *skb, 177062306a36Sopenharmony_ci struct net_device *netdev) 177162306a36Sopenharmony_ci{ 177262306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 177362306a36Sopenharmony_ci int err; 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci if (nic->flags & ich_10h_workaround) { 177662306a36Sopenharmony_ci /* SW workaround for ICH[x] 10Mbps/half duplex Tx hang. 177762306a36Sopenharmony_ci Issue a NOP command followed by a 1us delay before 177862306a36Sopenharmony_ci issuing the Tx command. */ 177962306a36Sopenharmony_ci if (e100_exec_cmd(nic, cuc_nop, 0)) 178062306a36Sopenharmony_ci netif_printk(nic, tx_err, KERN_DEBUG, nic->netdev, 178162306a36Sopenharmony_ci "exec cuc_nop failed\n"); 178262306a36Sopenharmony_ci udelay(1); 178362306a36Sopenharmony_ci } 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci err = e100_exec_cb(nic, skb, e100_xmit_prepare); 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci switch (err) { 178862306a36Sopenharmony_ci case -ENOSPC: 178962306a36Sopenharmony_ci /* We queued the skb, but now we're out of space. */ 179062306a36Sopenharmony_ci netif_printk(nic, tx_err, KERN_DEBUG, nic->netdev, 179162306a36Sopenharmony_ci "No space for CB\n"); 179262306a36Sopenharmony_ci netif_stop_queue(netdev); 179362306a36Sopenharmony_ci break; 179462306a36Sopenharmony_ci case -ENOMEM: 179562306a36Sopenharmony_ci /* This is a hard error - log it. */ 179662306a36Sopenharmony_ci netif_printk(nic, tx_err, KERN_DEBUG, nic->netdev, 179762306a36Sopenharmony_ci "Out of Tx resources, returning skb\n"); 179862306a36Sopenharmony_ci netif_stop_queue(netdev); 179962306a36Sopenharmony_ci return NETDEV_TX_BUSY; 180062306a36Sopenharmony_ci } 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci return NETDEV_TX_OK; 180362306a36Sopenharmony_ci} 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_cistatic int e100_tx_clean(struct nic *nic) 180662306a36Sopenharmony_ci{ 180762306a36Sopenharmony_ci struct net_device *dev = nic->netdev; 180862306a36Sopenharmony_ci struct cb *cb; 180962306a36Sopenharmony_ci int tx_cleaned = 0; 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci spin_lock(&nic->cb_lock); 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci /* Clean CBs marked complete */ 181462306a36Sopenharmony_ci for (cb = nic->cb_to_clean; 181562306a36Sopenharmony_ci cb->status & cpu_to_le16(cb_complete); 181662306a36Sopenharmony_ci cb = nic->cb_to_clean = cb->next) { 181762306a36Sopenharmony_ci dma_rmb(); /* read skb after status */ 181862306a36Sopenharmony_ci netif_printk(nic, tx_done, KERN_DEBUG, nic->netdev, 181962306a36Sopenharmony_ci "cb[%d]->status = 0x%04X\n", 182062306a36Sopenharmony_ci (int)(((void*)cb - (void*)nic->cbs)/sizeof(struct cb)), 182162306a36Sopenharmony_ci cb->status); 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci if (likely(cb->skb != NULL)) { 182462306a36Sopenharmony_ci dev->stats.tx_packets++; 182562306a36Sopenharmony_ci dev->stats.tx_bytes += cb->skb->len; 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_ci dma_unmap_single(&nic->pdev->dev, 182862306a36Sopenharmony_ci le32_to_cpu(cb->u.tcb.tbd.buf_addr), 182962306a36Sopenharmony_ci le16_to_cpu(cb->u.tcb.tbd.size), 183062306a36Sopenharmony_ci DMA_TO_DEVICE); 183162306a36Sopenharmony_ci dev_kfree_skb_any(cb->skb); 183262306a36Sopenharmony_ci cb->skb = NULL; 183362306a36Sopenharmony_ci tx_cleaned = 1; 183462306a36Sopenharmony_ci } 183562306a36Sopenharmony_ci cb->status = 0; 183662306a36Sopenharmony_ci nic->cbs_avail++; 183762306a36Sopenharmony_ci } 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_ci spin_unlock(&nic->cb_lock); 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci /* Recover from running out of Tx resources in xmit_frame */ 184262306a36Sopenharmony_ci if (unlikely(tx_cleaned && netif_queue_stopped(nic->netdev))) 184362306a36Sopenharmony_ci netif_wake_queue(nic->netdev); 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci return tx_cleaned; 184662306a36Sopenharmony_ci} 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_cistatic void e100_clean_cbs(struct nic *nic) 184962306a36Sopenharmony_ci{ 185062306a36Sopenharmony_ci if (nic->cbs) { 185162306a36Sopenharmony_ci while (nic->cbs_avail != nic->params.cbs.count) { 185262306a36Sopenharmony_ci struct cb *cb = nic->cb_to_clean; 185362306a36Sopenharmony_ci if (cb->skb) { 185462306a36Sopenharmony_ci dma_unmap_single(&nic->pdev->dev, 185562306a36Sopenharmony_ci le32_to_cpu(cb->u.tcb.tbd.buf_addr), 185662306a36Sopenharmony_ci le16_to_cpu(cb->u.tcb.tbd.size), 185762306a36Sopenharmony_ci DMA_TO_DEVICE); 185862306a36Sopenharmony_ci dev_kfree_skb(cb->skb); 185962306a36Sopenharmony_ci } 186062306a36Sopenharmony_ci nic->cb_to_clean = nic->cb_to_clean->next; 186162306a36Sopenharmony_ci nic->cbs_avail++; 186262306a36Sopenharmony_ci } 186362306a36Sopenharmony_ci dma_pool_free(nic->cbs_pool, nic->cbs, nic->cbs_dma_addr); 186462306a36Sopenharmony_ci nic->cbs = NULL; 186562306a36Sopenharmony_ci nic->cbs_avail = 0; 186662306a36Sopenharmony_ci } 186762306a36Sopenharmony_ci nic->cuc_cmd = cuc_start; 186862306a36Sopenharmony_ci nic->cb_to_use = nic->cb_to_send = nic->cb_to_clean = 186962306a36Sopenharmony_ci nic->cbs; 187062306a36Sopenharmony_ci} 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_cistatic int e100_alloc_cbs(struct nic *nic) 187362306a36Sopenharmony_ci{ 187462306a36Sopenharmony_ci struct cb *cb; 187562306a36Sopenharmony_ci unsigned int i, count = nic->params.cbs.count; 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci nic->cuc_cmd = cuc_start; 187862306a36Sopenharmony_ci nic->cb_to_use = nic->cb_to_send = nic->cb_to_clean = NULL; 187962306a36Sopenharmony_ci nic->cbs_avail = 0; 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci nic->cbs = dma_pool_zalloc(nic->cbs_pool, GFP_KERNEL, 188262306a36Sopenharmony_ci &nic->cbs_dma_addr); 188362306a36Sopenharmony_ci if (!nic->cbs) 188462306a36Sopenharmony_ci return -ENOMEM; 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci for (cb = nic->cbs, i = 0; i < count; cb++, i++) { 188762306a36Sopenharmony_ci cb->next = (i + 1 < count) ? cb + 1 : nic->cbs; 188862306a36Sopenharmony_ci cb->prev = (i == 0) ? nic->cbs + count - 1 : cb - 1; 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci cb->dma_addr = nic->cbs_dma_addr + i * sizeof(struct cb); 189162306a36Sopenharmony_ci cb->link = cpu_to_le32(nic->cbs_dma_addr + 189262306a36Sopenharmony_ci ((i+1) % count) * sizeof(struct cb)); 189362306a36Sopenharmony_ci } 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci nic->cb_to_use = nic->cb_to_send = nic->cb_to_clean = nic->cbs; 189662306a36Sopenharmony_ci nic->cbs_avail = count; 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_ci return 0; 189962306a36Sopenharmony_ci} 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_cistatic inline void e100_start_receiver(struct nic *nic, struct rx *rx) 190262306a36Sopenharmony_ci{ 190362306a36Sopenharmony_ci if (!nic->rxs) return; 190462306a36Sopenharmony_ci if (RU_SUSPENDED != nic->ru_running) return; 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_ci /* handle init time starts */ 190762306a36Sopenharmony_ci if (!rx) rx = nic->rxs; 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci /* (Re)start RU if suspended or idle and RFA is non-NULL */ 191062306a36Sopenharmony_ci if (rx->skb) { 191162306a36Sopenharmony_ci e100_exec_cmd(nic, ruc_start, rx->dma_addr); 191262306a36Sopenharmony_ci nic->ru_running = RU_RUNNING; 191362306a36Sopenharmony_ci } 191462306a36Sopenharmony_ci} 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_ci#define RFD_BUF_LEN (sizeof(struct rfd) + VLAN_ETH_FRAME_LEN + ETH_FCS_LEN) 191762306a36Sopenharmony_cistatic int e100_rx_alloc_skb(struct nic *nic, struct rx *rx) 191862306a36Sopenharmony_ci{ 191962306a36Sopenharmony_ci if (!(rx->skb = netdev_alloc_skb_ip_align(nic->netdev, RFD_BUF_LEN))) 192062306a36Sopenharmony_ci return -ENOMEM; 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci /* Init, and map the RFD. */ 192362306a36Sopenharmony_ci skb_copy_to_linear_data(rx->skb, &nic->blank_rfd, sizeof(struct rfd)); 192462306a36Sopenharmony_ci rx->dma_addr = dma_map_single(&nic->pdev->dev, rx->skb->data, 192562306a36Sopenharmony_ci RFD_BUF_LEN, DMA_BIDIRECTIONAL); 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_ci if (dma_mapping_error(&nic->pdev->dev, rx->dma_addr)) { 192862306a36Sopenharmony_ci dev_kfree_skb_any(rx->skb); 192962306a36Sopenharmony_ci rx->skb = NULL; 193062306a36Sopenharmony_ci rx->dma_addr = 0; 193162306a36Sopenharmony_ci return -ENOMEM; 193262306a36Sopenharmony_ci } 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci /* Link the RFD to end of RFA by linking previous RFD to 193562306a36Sopenharmony_ci * this one. We are safe to touch the previous RFD because 193662306a36Sopenharmony_ci * it is protected by the before last buffer's el bit being set */ 193762306a36Sopenharmony_ci if (rx->prev->skb) { 193862306a36Sopenharmony_ci struct rfd *prev_rfd = (struct rfd *)rx->prev->skb->data; 193962306a36Sopenharmony_ci put_unaligned_le32(rx->dma_addr, &prev_rfd->link); 194062306a36Sopenharmony_ci dma_sync_single_for_device(&nic->pdev->dev, 194162306a36Sopenharmony_ci rx->prev->dma_addr, 194262306a36Sopenharmony_ci sizeof(struct rfd), 194362306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 194462306a36Sopenharmony_ci } 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_ci return 0; 194762306a36Sopenharmony_ci} 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_cistatic int e100_rx_indicate(struct nic *nic, struct rx *rx, 195062306a36Sopenharmony_ci unsigned int *work_done, unsigned int work_to_do) 195162306a36Sopenharmony_ci{ 195262306a36Sopenharmony_ci struct net_device *dev = nic->netdev; 195362306a36Sopenharmony_ci struct sk_buff *skb = rx->skb; 195462306a36Sopenharmony_ci struct rfd *rfd = (struct rfd *)skb->data; 195562306a36Sopenharmony_ci u16 rfd_status, actual_size; 195662306a36Sopenharmony_ci u16 fcs_pad = 0; 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci if (unlikely(work_done && *work_done >= work_to_do)) 195962306a36Sopenharmony_ci return -EAGAIN; 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci /* Need to sync before taking a peek at cb_complete bit */ 196262306a36Sopenharmony_ci dma_sync_single_for_cpu(&nic->pdev->dev, rx->dma_addr, 196362306a36Sopenharmony_ci sizeof(struct rfd), DMA_BIDIRECTIONAL); 196462306a36Sopenharmony_ci rfd_status = le16_to_cpu(rfd->status); 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci netif_printk(nic, rx_status, KERN_DEBUG, nic->netdev, 196762306a36Sopenharmony_ci "status=0x%04X\n", rfd_status); 196862306a36Sopenharmony_ci dma_rmb(); /* read size after status bit */ 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_ci /* If data isn't ready, nothing to indicate */ 197162306a36Sopenharmony_ci if (unlikely(!(rfd_status & cb_complete))) { 197262306a36Sopenharmony_ci /* If the next buffer has the el bit, but we think the receiver 197362306a36Sopenharmony_ci * is still running, check to see if it really stopped while 197462306a36Sopenharmony_ci * we had interrupts off. 197562306a36Sopenharmony_ci * This allows for a fast restart without re-enabling 197662306a36Sopenharmony_ci * interrupts */ 197762306a36Sopenharmony_ci if ((le16_to_cpu(rfd->command) & cb_el) && 197862306a36Sopenharmony_ci (RU_RUNNING == nic->ru_running)) 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ci if (ioread8(&nic->csr->scb.status) & rus_no_res) 198162306a36Sopenharmony_ci nic->ru_running = RU_SUSPENDED; 198262306a36Sopenharmony_ci dma_sync_single_for_device(&nic->pdev->dev, rx->dma_addr, 198362306a36Sopenharmony_ci sizeof(struct rfd), 198462306a36Sopenharmony_ci DMA_FROM_DEVICE); 198562306a36Sopenharmony_ci return -ENODATA; 198662306a36Sopenharmony_ci } 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci /* Get actual data size */ 198962306a36Sopenharmony_ci if (unlikely(dev->features & NETIF_F_RXFCS)) 199062306a36Sopenharmony_ci fcs_pad = 4; 199162306a36Sopenharmony_ci actual_size = le16_to_cpu(rfd->actual_size) & 0x3FFF; 199262306a36Sopenharmony_ci if (unlikely(actual_size > RFD_BUF_LEN - sizeof(struct rfd))) 199362306a36Sopenharmony_ci actual_size = RFD_BUF_LEN - sizeof(struct rfd); 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_ci /* Get data */ 199662306a36Sopenharmony_ci dma_unmap_single(&nic->pdev->dev, rx->dma_addr, RFD_BUF_LEN, 199762306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci /* If this buffer has the el bit, but we think the receiver 200062306a36Sopenharmony_ci * is still running, check to see if it really stopped while 200162306a36Sopenharmony_ci * we had interrupts off. 200262306a36Sopenharmony_ci * This allows for a fast restart without re-enabling interrupts. 200362306a36Sopenharmony_ci * This can happen when the RU sees the size change but also sees 200462306a36Sopenharmony_ci * the el bit set. */ 200562306a36Sopenharmony_ci if ((le16_to_cpu(rfd->command) & cb_el) && 200662306a36Sopenharmony_ci (RU_RUNNING == nic->ru_running)) { 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_ci if (ioread8(&nic->csr->scb.status) & rus_no_res) 200962306a36Sopenharmony_ci nic->ru_running = RU_SUSPENDED; 201062306a36Sopenharmony_ci } 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci /* Pull off the RFD and put the actual data (minus eth hdr) */ 201362306a36Sopenharmony_ci skb_reserve(skb, sizeof(struct rfd)); 201462306a36Sopenharmony_ci skb_put(skb, actual_size); 201562306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, nic->netdev); 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci /* If we are receiving all frames, then don't bother 201862306a36Sopenharmony_ci * checking for errors. 201962306a36Sopenharmony_ci */ 202062306a36Sopenharmony_ci if (unlikely(dev->features & NETIF_F_RXALL)) { 202162306a36Sopenharmony_ci if (actual_size > ETH_DATA_LEN + VLAN_ETH_HLEN + fcs_pad) 202262306a36Sopenharmony_ci /* Received oversized frame, but keep it. */ 202362306a36Sopenharmony_ci nic->rx_over_length_errors++; 202462306a36Sopenharmony_ci goto process_skb; 202562306a36Sopenharmony_ci } 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_ci if (unlikely(!(rfd_status & cb_ok))) { 202862306a36Sopenharmony_ci /* Don't indicate if hardware indicates errors */ 202962306a36Sopenharmony_ci dev_kfree_skb_any(skb); 203062306a36Sopenharmony_ci } else if (actual_size > ETH_DATA_LEN + VLAN_ETH_HLEN + fcs_pad) { 203162306a36Sopenharmony_ci /* Don't indicate oversized frames */ 203262306a36Sopenharmony_ci nic->rx_over_length_errors++; 203362306a36Sopenharmony_ci dev_kfree_skb_any(skb); 203462306a36Sopenharmony_ci } else { 203562306a36Sopenharmony_ciprocess_skb: 203662306a36Sopenharmony_ci dev->stats.rx_packets++; 203762306a36Sopenharmony_ci dev->stats.rx_bytes += (actual_size - fcs_pad); 203862306a36Sopenharmony_ci netif_receive_skb(skb); 203962306a36Sopenharmony_ci if (work_done) 204062306a36Sopenharmony_ci (*work_done)++; 204162306a36Sopenharmony_ci } 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_ci rx->skb = NULL; 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci return 0; 204662306a36Sopenharmony_ci} 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_cistatic void e100_rx_clean(struct nic *nic, unsigned int *work_done, 204962306a36Sopenharmony_ci unsigned int work_to_do) 205062306a36Sopenharmony_ci{ 205162306a36Sopenharmony_ci struct rx *rx; 205262306a36Sopenharmony_ci int restart_required = 0, err = 0; 205362306a36Sopenharmony_ci struct rx *old_before_last_rx, *new_before_last_rx; 205462306a36Sopenharmony_ci struct rfd *old_before_last_rfd, *new_before_last_rfd; 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_ci /* Indicate newly arrived packets */ 205762306a36Sopenharmony_ci for (rx = nic->rx_to_clean; rx->skb; rx = nic->rx_to_clean = rx->next) { 205862306a36Sopenharmony_ci err = e100_rx_indicate(nic, rx, work_done, work_to_do); 205962306a36Sopenharmony_ci /* Hit quota or no more to clean */ 206062306a36Sopenharmony_ci if (-EAGAIN == err || -ENODATA == err) 206162306a36Sopenharmony_ci break; 206262306a36Sopenharmony_ci } 206362306a36Sopenharmony_ci 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci /* On EAGAIN, hit quota so have more work to do, restart once 206662306a36Sopenharmony_ci * cleanup is complete. 206762306a36Sopenharmony_ci * Else, are we already rnr? then pay attention!!! this ensures that 206862306a36Sopenharmony_ci * the state machine progression never allows a start with a 206962306a36Sopenharmony_ci * partially cleaned list, avoiding a race between hardware 207062306a36Sopenharmony_ci * and rx_to_clean when in NAPI mode */ 207162306a36Sopenharmony_ci if (-EAGAIN != err && RU_SUSPENDED == nic->ru_running) 207262306a36Sopenharmony_ci restart_required = 1; 207362306a36Sopenharmony_ci 207462306a36Sopenharmony_ci old_before_last_rx = nic->rx_to_use->prev->prev; 207562306a36Sopenharmony_ci old_before_last_rfd = (struct rfd *)old_before_last_rx->skb->data; 207662306a36Sopenharmony_ci 207762306a36Sopenharmony_ci /* Alloc new skbs to refill list */ 207862306a36Sopenharmony_ci for (rx = nic->rx_to_use; !rx->skb; rx = nic->rx_to_use = rx->next) { 207962306a36Sopenharmony_ci if (unlikely(e100_rx_alloc_skb(nic, rx))) 208062306a36Sopenharmony_ci break; /* Better luck next time (see watchdog) */ 208162306a36Sopenharmony_ci } 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci new_before_last_rx = nic->rx_to_use->prev->prev; 208462306a36Sopenharmony_ci if (new_before_last_rx != old_before_last_rx) { 208562306a36Sopenharmony_ci /* Set the el-bit on the buffer that is before the last buffer. 208662306a36Sopenharmony_ci * This lets us update the next pointer on the last buffer 208762306a36Sopenharmony_ci * without worrying about hardware touching it. 208862306a36Sopenharmony_ci * We set the size to 0 to prevent hardware from touching this 208962306a36Sopenharmony_ci * buffer. 209062306a36Sopenharmony_ci * When the hardware hits the before last buffer with el-bit 209162306a36Sopenharmony_ci * and size of 0, it will RNR interrupt, the RUS will go into 209262306a36Sopenharmony_ci * the No Resources state. It will not complete nor write to 209362306a36Sopenharmony_ci * this buffer. */ 209462306a36Sopenharmony_ci new_before_last_rfd = 209562306a36Sopenharmony_ci (struct rfd *)new_before_last_rx->skb->data; 209662306a36Sopenharmony_ci new_before_last_rfd->size = 0; 209762306a36Sopenharmony_ci new_before_last_rfd->command |= cpu_to_le16(cb_el); 209862306a36Sopenharmony_ci dma_sync_single_for_device(&nic->pdev->dev, 209962306a36Sopenharmony_ci new_before_last_rx->dma_addr, 210062306a36Sopenharmony_ci sizeof(struct rfd), 210162306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci /* Now that we have a new stopping point, we can clear the old 210462306a36Sopenharmony_ci * stopping point. We must sync twice to get the proper 210562306a36Sopenharmony_ci * ordering on the hardware side of things. */ 210662306a36Sopenharmony_ci old_before_last_rfd->command &= ~cpu_to_le16(cb_el); 210762306a36Sopenharmony_ci dma_sync_single_for_device(&nic->pdev->dev, 210862306a36Sopenharmony_ci old_before_last_rx->dma_addr, 210962306a36Sopenharmony_ci sizeof(struct rfd), 211062306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 211162306a36Sopenharmony_ci old_before_last_rfd->size = cpu_to_le16(VLAN_ETH_FRAME_LEN 211262306a36Sopenharmony_ci + ETH_FCS_LEN); 211362306a36Sopenharmony_ci dma_sync_single_for_device(&nic->pdev->dev, 211462306a36Sopenharmony_ci old_before_last_rx->dma_addr, 211562306a36Sopenharmony_ci sizeof(struct rfd), 211662306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 211762306a36Sopenharmony_ci } 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_ci if (restart_required) { 212062306a36Sopenharmony_ci // ack the rnr? 212162306a36Sopenharmony_ci iowrite8(stat_ack_rnr, &nic->csr->scb.stat_ack); 212262306a36Sopenharmony_ci e100_start_receiver(nic, nic->rx_to_clean); 212362306a36Sopenharmony_ci if (work_done) 212462306a36Sopenharmony_ci (*work_done)++; 212562306a36Sopenharmony_ci } 212662306a36Sopenharmony_ci} 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_cistatic void e100_rx_clean_list(struct nic *nic) 212962306a36Sopenharmony_ci{ 213062306a36Sopenharmony_ci struct rx *rx; 213162306a36Sopenharmony_ci unsigned int i, count = nic->params.rfds.count; 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_ci nic->ru_running = RU_UNINITIALIZED; 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci if (nic->rxs) { 213662306a36Sopenharmony_ci for (rx = nic->rxs, i = 0; i < count; rx++, i++) { 213762306a36Sopenharmony_ci if (rx->skb) { 213862306a36Sopenharmony_ci dma_unmap_single(&nic->pdev->dev, 213962306a36Sopenharmony_ci rx->dma_addr, RFD_BUF_LEN, 214062306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 214162306a36Sopenharmony_ci dev_kfree_skb(rx->skb); 214262306a36Sopenharmony_ci } 214362306a36Sopenharmony_ci } 214462306a36Sopenharmony_ci kfree(nic->rxs); 214562306a36Sopenharmony_ci nic->rxs = NULL; 214662306a36Sopenharmony_ci } 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_ci nic->rx_to_use = nic->rx_to_clean = NULL; 214962306a36Sopenharmony_ci} 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_cistatic int e100_rx_alloc_list(struct nic *nic) 215262306a36Sopenharmony_ci{ 215362306a36Sopenharmony_ci struct rx *rx; 215462306a36Sopenharmony_ci unsigned int i, count = nic->params.rfds.count; 215562306a36Sopenharmony_ci struct rfd *before_last; 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci nic->rx_to_use = nic->rx_to_clean = NULL; 215862306a36Sopenharmony_ci nic->ru_running = RU_UNINITIALIZED; 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci if (!(nic->rxs = kcalloc(count, sizeof(struct rx), GFP_KERNEL))) 216162306a36Sopenharmony_ci return -ENOMEM; 216262306a36Sopenharmony_ci 216362306a36Sopenharmony_ci for (rx = nic->rxs, i = 0; i < count; rx++, i++) { 216462306a36Sopenharmony_ci rx->next = (i + 1 < count) ? rx + 1 : nic->rxs; 216562306a36Sopenharmony_ci rx->prev = (i == 0) ? nic->rxs + count - 1 : rx - 1; 216662306a36Sopenharmony_ci if (e100_rx_alloc_skb(nic, rx)) { 216762306a36Sopenharmony_ci e100_rx_clean_list(nic); 216862306a36Sopenharmony_ci return -ENOMEM; 216962306a36Sopenharmony_ci } 217062306a36Sopenharmony_ci } 217162306a36Sopenharmony_ci /* Set the el-bit on the buffer that is before the last buffer. 217262306a36Sopenharmony_ci * This lets us update the next pointer on the last buffer without 217362306a36Sopenharmony_ci * worrying about hardware touching it. 217462306a36Sopenharmony_ci * We set the size to 0 to prevent hardware from touching this buffer. 217562306a36Sopenharmony_ci * When the hardware hits the before last buffer with el-bit and size 217662306a36Sopenharmony_ci * of 0, it will RNR interrupt, the RU will go into the No Resources 217762306a36Sopenharmony_ci * state. It will not complete nor write to this buffer. */ 217862306a36Sopenharmony_ci rx = nic->rxs->prev->prev; 217962306a36Sopenharmony_ci before_last = (struct rfd *)rx->skb->data; 218062306a36Sopenharmony_ci before_last->command |= cpu_to_le16(cb_el); 218162306a36Sopenharmony_ci before_last->size = 0; 218262306a36Sopenharmony_ci dma_sync_single_for_device(&nic->pdev->dev, rx->dma_addr, 218362306a36Sopenharmony_ci sizeof(struct rfd), DMA_BIDIRECTIONAL); 218462306a36Sopenharmony_ci 218562306a36Sopenharmony_ci nic->rx_to_use = nic->rx_to_clean = nic->rxs; 218662306a36Sopenharmony_ci nic->ru_running = RU_SUSPENDED; 218762306a36Sopenharmony_ci 218862306a36Sopenharmony_ci return 0; 218962306a36Sopenharmony_ci} 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_cistatic irqreturn_t e100_intr(int irq, void *dev_id) 219262306a36Sopenharmony_ci{ 219362306a36Sopenharmony_ci struct net_device *netdev = dev_id; 219462306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 219562306a36Sopenharmony_ci u8 stat_ack = ioread8(&nic->csr->scb.stat_ack); 219662306a36Sopenharmony_ci 219762306a36Sopenharmony_ci netif_printk(nic, intr, KERN_DEBUG, nic->netdev, 219862306a36Sopenharmony_ci "stat_ack = 0x%02X\n", stat_ack); 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_ci if (stat_ack == stat_ack_not_ours || /* Not our interrupt */ 220162306a36Sopenharmony_ci stat_ack == stat_ack_not_present) /* Hardware is ejected */ 220262306a36Sopenharmony_ci return IRQ_NONE; 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_ci /* Ack interrupt(s) */ 220562306a36Sopenharmony_ci iowrite8(stat_ack, &nic->csr->scb.stat_ack); 220662306a36Sopenharmony_ci 220762306a36Sopenharmony_ci /* We hit Receive No Resource (RNR); restart RU after cleaning */ 220862306a36Sopenharmony_ci if (stat_ack & stat_ack_rnr) 220962306a36Sopenharmony_ci nic->ru_running = RU_SUSPENDED; 221062306a36Sopenharmony_ci 221162306a36Sopenharmony_ci if (likely(napi_schedule_prep(&nic->napi))) { 221262306a36Sopenharmony_ci e100_disable_irq(nic); 221362306a36Sopenharmony_ci __napi_schedule(&nic->napi); 221462306a36Sopenharmony_ci } 221562306a36Sopenharmony_ci 221662306a36Sopenharmony_ci return IRQ_HANDLED; 221762306a36Sopenharmony_ci} 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_cistatic int e100_poll(struct napi_struct *napi, int budget) 222062306a36Sopenharmony_ci{ 222162306a36Sopenharmony_ci struct nic *nic = container_of(napi, struct nic, napi); 222262306a36Sopenharmony_ci unsigned int work_done = 0; 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ci e100_rx_clean(nic, &work_done, budget); 222562306a36Sopenharmony_ci e100_tx_clean(nic); 222662306a36Sopenharmony_ci 222762306a36Sopenharmony_ci /* If budget fully consumed, continue polling */ 222862306a36Sopenharmony_ci if (work_done == budget) 222962306a36Sopenharmony_ci return budget; 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ci /* only re-enable interrupt if stack agrees polling is really done */ 223262306a36Sopenharmony_ci if (likely(napi_complete_done(napi, work_done))) 223362306a36Sopenharmony_ci e100_enable_irq(nic); 223462306a36Sopenharmony_ci 223562306a36Sopenharmony_ci return work_done; 223662306a36Sopenharmony_ci} 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 223962306a36Sopenharmony_cistatic void e100_netpoll(struct net_device *netdev) 224062306a36Sopenharmony_ci{ 224162306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_ci e100_disable_irq(nic); 224462306a36Sopenharmony_ci e100_intr(nic->pdev->irq, netdev); 224562306a36Sopenharmony_ci e100_tx_clean(nic); 224662306a36Sopenharmony_ci e100_enable_irq(nic); 224762306a36Sopenharmony_ci} 224862306a36Sopenharmony_ci#endif 224962306a36Sopenharmony_ci 225062306a36Sopenharmony_cistatic int e100_set_mac_address(struct net_device *netdev, void *p) 225162306a36Sopenharmony_ci{ 225262306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 225362306a36Sopenharmony_ci struct sockaddr *addr = p; 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 225662306a36Sopenharmony_ci return -EADDRNOTAVAIL; 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci eth_hw_addr_set(netdev, addr->sa_data); 225962306a36Sopenharmony_ci e100_exec_cb(nic, NULL, e100_setup_iaaddr); 226062306a36Sopenharmony_ci 226162306a36Sopenharmony_ci return 0; 226262306a36Sopenharmony_ci} 226362306a36Sopenharmony_ci 226462306a36Sopenharmony_cistatic int e100_asf(struct nic *nic) 226562306a36Sopenharmony_ci{ 226662306a36Sopenharmony_ci /* ASF can be enabled from eeprom */ 226762306a36Sopenharmony_ci return (nic->pdev->device >= 0x1050) && (nic->pdev->device <= 0x1057) && 226862306a36Sopenharmony_ci (le16_to_cpu(nic->eeprom[eeprom_config_asf]) & eeprom_asf) && 226962306a36Sopenharmony_ci !(le16_to_cpu(nic->eeprom[eeprom_config_asf]) & eeprom_gcl) && 227062306a36Sopenharmony_ci ((le16_to_cpu(nic->eeprom[eeprom_smbus_addr]) & 0xFF) != 0xFE); 227162306a36Sopenharmony_ci} 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_cistatic int e100_up(struct nic *nic) 227462306a36Sopenharmony_ci{ 227562306a36Sopenharmony_ci int err; 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ci if ((err = e100_rx_alloc_list(nic))) 227862306a36Sopenharmony_ci return err; 227962306a36Sopenharmony_ci if ((err = e100_alloc_cbs(nic))) 228062306a36Sopenharmony_ci goto err_rx_clean_list; 228162306a36Sopenharmony_ci if ((err = e100_hw_init(nic))) 228262306a36Sopenharmony_ci goto err_clean_cbs; 228362306a36Sopenharmony_ci e100_set_multicast_list(nic->netdev); 228462306a36Sopenharmony_ci e100_start_receiver(nic, NULL); 228562306a36Sopenharmony_ci mod_timer(&nic->watchdog, jiffies); 228662306a36Sopenharmony_ci if ((err = request_irq(nic->pdev->irq, e100_intr, IRQF_SHARED, 228762306a36Sopenharmony_ci nic->netdev->name, nic->netdev))) 228862306a36Sopenharmony_ci goto err_no_irq; 228962306a36Sopenharmony_ci netif_wake_queue(nic->netdev); 229062306a36Sopenharmony_ci napi_enable(&nic->napi); 229162306a36Sopenharmony_ci /* enable ints _after_ enabling poll, preventing a race between 229262306a36Sopenharmony_ci * disable ints+schedule */ 229362306a36Sopenharmony_ci e100_enable_irq(nic); 229462306a36Sopenharmony_ci return 0; 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_cierr_no_irq: 229762306a36Sopenharmony_ci del_timer_sync(&nic->watchdog); 229862306a36Sopenharmony_cierr_clean_cbs: 229962306a36Sopenharmony_ci e100_clean_cbs(nic); 230062306a36Sopenharmony_cierr_rx_clean_list: 230162306a36Sopenharmony_ci e100_rx_clean_list(nic); 230262306a36Sopenharmony_ci return err; 230362306a36Sopenharmony_ci} 230462306a36Sopenharmony_ci 230562306a36Sopenharmony_cistatic void e100_down(struct nic *nic) 230662306a36Sopenharmony_ci{ 230762306a36Sopenharmony_ci /* wait here for poll to complete */ 230862306a36Sopenharmony_ci napi_disable(&nic->napi); 230962306a36Sopenharmony_ci netif_stop_queue(nic->netdev); 231062306a36Sopenharmony_ci e100_hw_reset(nic); 231162306a36Sopenharmony_ci free_irq(nic->pdev->irq, nic->netdev); 231262306a36Sopenharmony_ci del_timer_sync(&nic->watchdog); 231362306a36Sopenharmony_ci netif_carrier_off(nic->netdev); 231462306a36Sopenharmony_ci e100_clean_cbs(nic); 231562306a36Sopenharmony_ci e100_rx_clean_list(nic); 231662306a36Sopenharmony_ci} 231762306a36Sopenharmony_ci 231862306a36Sopenharmony_cistatic void e100_tx_timeout(struct net_device *netdev, unsigned int txqueue) 231962306a36Sopenharmony_ci{ 232062306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 232162306a36Sopenharmony_ci 232262306a36Sopenharmony_ci /* Reset outside of interrupt context, to avoid request_irq 232362306a36Sopenharmony_ci * in interrupt context */ 232462306a36Sopenharmony_ci schedule_work(&nic->tx_timeout_task); 232562306a36Sopenharmony_ci} 232662306a36Sopenharmony_ci 232762306a36Sopenharmony_cistatic void e100_tx_timeout_task(struct work_struct *work) 232862306a36Sopenharmony_ci{ 232962306a36Sopenharmony_ci struct nic *nic = container_of(work, struct nic, tx_timeout_task); 233062306a36Sopenharmony_ci struct net_device *netdev = nic->netdev; 233162306a36Sopenharmony_ci 233262306a36Sopenharmony_ci netif_printk(nic, tx_err, KERN_DEBUG, nic->netdev, 233362306a36Sopenharmony_ci "scb.status=0x%02X\n", ioread8(&nic->csr->scb.status)); 233462306a36Sopenharmony_ci 233562306a36Sopenharmony_ci rtnl_lock(); 233662306a36Sopenharmony_ci if (netif_running(netdev)) { 233762306a36Sopenharmony_ci e100_down(netdev_priv(netdev)); 233862306a36Sopenharmony_ci e100_up(netdev_priv(netdev)); 233962306a36Sopenharmony_ci } 234062306a36Sopenharmony_ci rtnl_unlock(); 234162306a36Sopenharmony_ci} 234262306a36Sopenharmony_ci 234362306a36Sopenharmony_cistatic int e100_loopback_test(struct nic *nic, enum loopback loopback_mode) 234462306a36Sopenharmony_ci{ 234562306a36Sopenharmony_ci int err; 234662306a36Sopenharmony_ci struct sk_buff *skb; 234762306a36Sopenharmony_ci 234862306a36Sopenharmony_ci /* Use driver resources to perform internal MAC or PHY 234962306a36Sopenharmony_ci * loopback test. A single packet is prepared and transmitted 235062306a36Sopenharmony_ci * in loopback mode, and the test passes if the received 235162306a36Sopenharmony_ci * packet compares byte-for-byte to the transmitted packet. */ 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_ci if ((err = e100_rx_alloc_list(nic))) 235462306a36Sopenharmony_ci return err; 235562306a36Sopenharmony_ci if ((err = e100_alloc_cbs(nic))) 235662306a36Sopenharmony_ci goto err_clean_rx; 235762306a36Sopenharmony_ci 235862306a36Sopenharmony_ci /* ICH PHY loopback is broken so do MAC loopback instead */ 235962306a36Sopenharmony_ci if (nic->flags & ich && loopback_mode == lb_phy) 236062306a36Sopenharmony_ci loopback_mode = lb_mac; 236162306a36Sopenharmony_ci 236262306a36Sopenharmony_ci nic->loopback = loopback_mode; 236362306a36Sopenharmony_ci if ((err = e100_hw_init(nic))) 236462306a36Sopenharmony_ci goto err_loopback_none; 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_ci if (loopback_mode == lb_phy) 236762306a36Sopenharmony_ci mdio_write(nic->netdev, nic->mii.phy_id, MII_BMCR, 236862306a36Sopenharmony_ci BMCR_LOOPBACK); 236962306a36Sopenharmony_ci 237062306a36Sopenharmony_ci e100_start_receiver(nic, NULL); 237162306a36Sopenharmony_ci 237262306a36Sopenharmony_ci if (!(skb = netdev_alloc_skb(nic->netdev, ETH_DATA_LEN))) { 237362306a36Sopenharmony_ci err = -ENOMEM; 237462306a36Sopenharmony_ci goto err_loopback_none; 237562306a36Sopenharmony_ci } 237662306a36Sopenharmony_ci skb_put(skb, ETH_DATA_LEN); 237762306a36Sopenharmony_ci memset(skb->data, 0xFF, ETH_DATA_LEN); 237862306a36Sopenharmony_ci e100_xmit_frame(skb, nic->netdev); 237962306a36Sopenharmony_ci 238062306a36Sopenharmony_ci msleep(10); 238162306a36Sopenharmony_ci 238262306a36Sopenharmony_ci dma_sync_single_for_cpu(&nic->pdev->dev, nic->rx_to_clean->dma_addr, 238362306a36Sopenharmony_ci RFD_BUF_LEN, DMA_BIDIRECTIONAL); 238462306a36Sopenharmony_ci 238562306a36Sopenharmony_ci if (memcmp(nic->rx_to_clean->skb->data + sizeof(struct rfd), 238662306a36Sopenharmony_ci skb->data, ETH_DATA_LEN)) 238762306a36Sopenharmony_ci err = -EAGAIN; 238862306a36Sopenharmony_ci 238962306a36Sopenharmony_cierr_loopback_none: 239062306a36Sopenharmony_ci mdio_write(nic->netdev, nic->mii.phy_id, MII_BMCR, 0); 239162306a36Sopenharmony_ci nic->loopback = lb_none; 239262306a36Sopenharmony_ci e100_clean_cbs(nic); 239362306a36Sopenharmony_ci e100_hw_reset(nic); 239462306a36Sopenharmony_cierr_clean_rx: 239562306a36Sopenharmony_ci e100_rx_clean_list(nic); 239662306a36Sopenharmony_ci return err; 239762306a36Sopenharmony_ci} 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_ci#define MII_LED_CONTROL 0x1B 240062306a36Sopenharmony_ci#define E100_82552_LED_OVERRIDE 0x19 240162306a36Sopenharmony_ci#define E100_82552_LED_ON 0x000F /* LEDTX and LED_RX both on */ 240262306a36Sopenharmony_ci#define E100_82552_LED_OFF 0x000A /* LEDTX and LED_RX both off */ 240362306a36Sopenharmony_ci 240462306a36Sopenharmony_cistatic int e100_get_link_ksettings(struct net_device *netdev, 240562306a36Sopenharmony_ci struct ethtool_link_ksettings *cmd) 240662306a36Sopenharmony_ci{ 240762306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 240862306a36Sopenharmony_ci 240962306a36Sopenharmony_ci mii_ethtool_get_link_ksettings(&nic->mii, cmd); 241062306a36Sopenharmony_ci 241162306a36Sopenharmony_ci return 0; 241262306a36Sopenharmony_ci} 241362306a36Sopenharmony_ci 241462306a36Sopenharmony_cistatic int e100_set_link_ksettings(struct net_device *netdev, 241562306a36Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 241662306a36Sopenharmony_ci{ 241762306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 241862306a36Sopenharmony_ci int err; 241962306a36Sopenharmony_ci 242062306a36Sopenharmony_ci mdio_write(netdev, nic->mii.phy_id, MII_BMCR, BMCR_RESET); 242162306a36Sopenharmony_ci err = mii_ethtool_set_link_ksettings(&nic->mii, cmd); 242262306a36Sopenharmony_ci e100_exec_cb(nic, NULL, e100_configure); 242362306a36Sopenharmony_ci 242462306a36Sopenharmony_ci return err; 242562306a36Sopenharmony_ci} 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_cistatic void e100_get_drvinfo(struct net_device *netdev, 242862306a36Sopenharmony_ci struct ethtool_drvinfo *info) 242962306a36Sopenharmony_ci{ 243062306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 243162306a36Sopenharmony_ci strscpy(info->driver, DRV_NAME, sizeof(info->driver)); 243262306a36Sopenharmony_ci strscpy(info->bus_info, pci_name(nic->pdev), 243362306a36Sopenharmony_ci sizeof(info->bus_info)); 243462306a36Sopenharmony_ci} 243562306a36Sopenharmony_ci 243662306a36Sopenharmony_ci#define E100_PHY_REGS 0x1D 243762306a36Sopenharmony_cistatic int e100_get_regs_len(struct net_device *netdev) 243862306a36Sopenharmony_ci{ 243962306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 244062306a36Sopenharmony_ci 244162306a36Sopenharmony_ci /* We know the number of registers, and the size of the dump buffer. 244262306a36Sopenharmony_ci * Calculate the total size in bytes. 244362306a36Sopenharmony_ci */ 244462306a36Sopenharmony_ci return (1 + E100_PHY_REGS) * sizeof(u32) + sizeof(nic->mem->dump_buf); 244562306a36Sopenharmony_ci} 244662306a36Sopenharmony_ci 244762306a36Sopenharmony_cistatic void e100_get_regs(struct net_device *netdev, 244862306a36Sopenharmony_ci struct ethtool_regs *regs, void *p) 244962306a36Sopenharmony_ci{ 245062306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 245162306a36Sopenharmony_ci u32 *buff = p; 245262306a36Sopenharmony_ci int i; 245362306a36Sopenharmony_ci 245462306a36Sopenharmony_ci regs->version = (1 << 24) | nic->pdev->revision; 245562306a36Sopenharmony_ci buff[0] = ioread8(&nic->csr->scb.cmd_hi) << 24 | 245662306a36Sopenharmony_ci ioread8(&nic->csr->scb.cmd_lo) << 16 | 245762306a36Sopenharmony_ci ioread16(&nic->csr->scb.status); 245862306a36Sopenharmony_ci for (i = 0; i < E100_PHY_REGS; i++) 245962306a36Sopenharmony_ci /* Note that we read the registers in reverse order. This 246062306a36Sopenharmony_ci * ordering is the ABI apparently used by ethtool and other 246162306a36Sopenharmony_ci * applications. 246262306a36Sopenharmony_ci */ 246362306a36Sopenharmony_ci buff[1 + i] = mdio_read(netdev, nic->mii.phy_id, 246462306a36Sopenharmony_ci E100_PHY_REGS - 1 - i); 246562306a36Sopenharmony_ci memset(nic->mem->dump_buf, 0, sizeof(nic->mem->dump_buf)); 246662306a36Sopenharmony_ci e100_exec_cb(nic, NULL, e100_dump); 246762306a36Sopenharmony_ci msleep(10); 246862306a36Sopenharmony_ci memcpy(&buff[1 + E100_PHY_REGS], nic->mem->dump_buf, 246962306a36Sopenharmony_ci sizeof(nic->mem->dump_buf)); 247062306a36Sopenharmony_ci} 247162306a36Sopenharmony_ci 247262306a36Sopenharmony_cistatic void e100_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) 247362306a36Sopenharmony_ci{ 247462306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 247562306a36Sopenharmony_ci wol->supported = (nic->mac >= mac_82558_D101_A4) ? WAKE_MAGIC : 0; 247662306a36Sopenharmony_ci wol->wolopts = (nic->flags & wol_magic) ? WAKE_MAGIC : 0; 247762306a36Sopenharmony_ci} 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_cistatic int e100_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) 248062306a36Sopenharmony_ci{ 248162306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 248262306a36Sopenharmony_ci 248362306a36Sopenharmony_ci if ((wol->wolopts && wol->wolopts != WAKE_MAGIC) || 248462306a36Sopenharmony_ci !device_can_wakeup(&nic->pdev->dev)) 248562306a36Sopenharmony_ci return -EOPNOTSUPP; 248662306a36Sopenharmony_ci 248762306a36Sopenharmony_ci if (wol->wolopts) 248862306a36Sopenharmony_ci nic->flags |= wol_magic; 248962306a36Sopenharmony_ci else 249062306a36Sopenharmony_ci nic->flags &= ~wol_magic; 249162306a36Sopenharmony_ci 249262306a36Sopenharmony_ci device_set_wakeup_enable(&nic->pdev->dev, wol->wolopts); 249362306a36Sopenharmony_ci 249462306a36Sopenharmony_ci e100_exec_cb(nic, NULL, e100_configure); 249562306a36Sopenharmony_ci 249662306a36Sopenharmony_ci return 0; 249762306a36Sopenharmony_ci} 249862306a36Sopenharmony_ci 249962306a36Sopenharmony_cistatic u32 e100_get_msglevel(struct net_device *netdev) 250062306a36Sopenharmony_ci{ 250162306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 250262306a36Sopenharmony_ci return nic->msg_enable; 250362306a36Sopenharmony_ci} 250462306a36Sopenharmony_ci 250562306a36Sopenharmony_cistatic void e100_set_msglevel(struct net_device *netdev, u32 value) 250662306a36Sopenharmony_ci{ 250762306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 250862306a36Sopenharmony_ci nic->msg_enable = value; 250962306a36Sopenharmony_ci} 251062306a36Sopenharmony_ci 251162306a36Sopenharmony_cistatic int e100_nway_reset(struct net_device *netdev) 251262306a36Sopenharmony_ci{ 251362306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 251462306a36Sopenharmony_ci return mii_nway_restart(&nic->mii); 251562306a36Sopenharmony_ci} 251662306a36Sopenharmony_ci 251762306a36Sopenharmony_cistatic u32 e100_get_link(struct net_device *netdev) 251862306a36Sopenharmony_ci{ 251962306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 252062306a36Sopenharmony_ci return mii_link_ok(&nic->mii); 252162306a36Sopenharmony_ci} 252262306a36Sopenharmony_ci 252362306a36Sopenharmony_cistatic int e100_get_eeprom_len(struct net_device *netdev) 252462306a36Sopenharmony_ci{ 252562306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 252662306a36Sopenharmony_ci return nic->eeprom_wc << 1; 252762306a36Sopenharmony_ci} 252862306a36Sopenharmony_ci 252962306a36Sopenharmony_ci#define E100_EEPROM_MAGIC 0x1234 253062306a36Sopenharmony_cistatic int e100_get_eeprom(struct net_device *netdev, 253162306a36Sopenharmony_ci struct ethtool_eeprom *eeprom, u8 *bytes) 253262306a36Sopenharmony_ci{ 253362306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 253462306a36Sopenharmony_ci 253562306a36Sopenharmony_ci eeprom->magic = E100_EEPROM_MAGIC; 253662306a36Sopenharmony_ci memcpy(bytes, &((u8 *)nic->eeprom)[eeprom->offset], eeprom->len); 253762306a36Sopenharmony_ci 253862306a36Sopenharmony_ci return 0; 253962306a36Sopenharmony_ci} 254062306a36Sopenharmony_ci 254162306a36Sopenharmony_cistatic int e100_set_eeprom(struct net_device *netdev, 254262306a36Sopenharmony_ci struct ethtool_eeprom *eeprom, u8 *bytes) 254362306a36Sopenharmony_ci{ 254462306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 254562306a36Sopenharmony_ci 254662306a36Sopenharmony_ci if (eeprom->magic != E100_EEPROM_MAGIC) 254762306a36Sopenharmony_ci return -EINVAL; 254862306a36Sopenharmony_ci 254962306a36Sopenharmony_ci memcpy(&((u8 *)nic->eeprom)[eeprom->offset], bytes, eeprom->len); 255062306a36Sopenharmony_ci 255162306a36Sopenharmony_ci return e100_eeprom_save(nic, eeprom->offset >> 1, 255262306a36Sopenharmony_ci (eeprom->len >> 1) + 1); 255362306a36Sopenharmony_ci} 255462306a36Sopenharmony_ci 255562306a36Sopenharmony_cistatic void e100_get_ringparam(struct net_device *netdev, 255662306a36Sopenharmony_ci struct ethtool_ringparam *ring, 255762306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_ring, 255862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 255962306a36Sopenharmony_ci{ 256062306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 256162306a36Sopenharmony_ci struct param_range *rfds = &nic->params.rfds; 256262306a36Sopenharmony_ci struct param_range *cbs = &nic->params.cbs; 256362306a36Sopenharmony_ci 256462306a36Sopenharmony_ci ring->rx_max_pending = rfds->max; 256562306a36Sopenharmony_ci ring->tx_max_pending = cbs->max; 256662306a36Sopenharmony_ci ring->rx_pending = rfds->count; 256762306a36Sopenharmony_ci ring->tx_pending = cbs->count; 256862306a36Sopenharmony_ci} 256962306a36Sopenharmony_ci 257062306a36Sopenharmony_cistatic int e100_set_ringparam(struct net_device *netdev, 257162306a36Sopenharmony_ci struct ethtool_ringparam *ring, 257262306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_ring, 257362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 257462306a36Sopenharmony_ci{ 257562306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 257662306a36Sopenharmony_ci struct param_range *rfds = &nic->params.rfds; 257762306a36Sopenharmony_ci struct param_range *cbs = &nic->params.cbs; 257862306a36Sopenharmony_ci 257962306a36Sopenharmony_ci if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending)) 258062306a36Sopenharmony_ci return -EINVAL; 258162306a36Sopenharmony_ci 258262306a36Sopenharmony_ci if (netif_running(netdev)) 258362306a36Sopenharmony_ci e100_down(nic); 258462306a36Sopenharmony_ci rfds->count = max(ring->rx_pending, rfds->min); 258562306a36Sopenharmony_ci rfds->count = min(rfds->count, rfds->max); 258662306a36Sopenharmony_ci cbs->count = max(ring->tx_pending, cbs->min); 258762306a36Sopenharmony_ci cbs->count = min(cbs->count, cbs->max); 258862306a36Sopenharmony_ci netif_info(nic, drv, nic->netdev, "Ring Param settings: rx: %d, tx %d\n", 258962306a36Sopenharmony_ci rfds->count, cbs->count); 259062306a36Sopenharmony_ci if (netif_running(netdev)) 259162306a36Sopenharmony_ci e100_up(nic); 259262306a36Sopenharmony_ci 259362306a36Sopenharmony_ci return 0; 259462306a36Sopenharmony_ci} 259562306a36Sopenharmony_ci 259662306a36Sopenharmony_cistatic const char e100_gstrings_test[][ETH_GSTRING_LEN] = { 259762306a36Sopenharmony_ci "Link test (on/offline)", 259862306a36Sopenharmony_ci "Eeprom test (on/offline)", 259962306a36Sopenharmony_ci "Self test (offline)", 260062306a36Sopenharmony_ci "Mac loopback (offline)", 260162306a36Sopenharmony_ci "Phy loopback (offline)", 260262306a36Sopenharmony_ci}; 260362306a36Sopenharmony_ci#define E100_TEST_LEN ARRAY_SIZE(e100_gstrings_test) 260462306a36Sopenharmony_ci 260562306a36Sopenharmony_cistatic void e100_diag_test(struct net_device *netdev, 260662306a36Sopenharmony_ci struct ethtool_test *test, u64 *data) 260762306a36Sopenharmony_ci{ 260862306a36Sopenharmony_ci struct ethtool_cmd cmd; 260962306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 261062306a36Sopenharmony_ci int i; 261162306a36Sopenharmony_ci 261262306a36Sopenharmony_ci memset(data, 0, E100_TEST_LEN * sizeof(u64)); 261362306a36Sopenharmony_ci data[0] = !mii_link_ok(&nic->mii); 261462306a36Sopenharmony_ci data[1] = e100_eeprom_load(nic); 261562306a36Sopenharmony_ci if (test->flags & ETH_TEST_FL_OFFLINE) { 261662306a36Sopenharmony_ci 261762306a36Sopenharmony_ci /* save speed, duplex & autoneg settings */ 261862306a36Sopenharmony_ci mii_ethtool_gset(&nic->mii, &cmd); 261962306a36Sopenharmony_ci 262062306a36Sopenharmony_ci if (netif_running(netdev)) 262162306a36Sopenharmony_ci e100_down(nic); 262262306a36Sopenharmony_ci data[2] = e100_self_test(nic); 262362306a36Sopenharmony_ci data[3] = e100_loopback_test(nic, lb_mac); 262462306a36Sopenharmony_ci data[4] = e100_loopback_test(nic, lb_phy); 262562306a36Sopenharmony_ci 262662306a36Sopenharmony_ci /* restore speed, duplex & autoneg settings */ 262762306a36Sopenharmony_ci mii_ethtool_sset(&nic->mii, &cmd); 262862306a36Sopenharmony_ci 262962306a36Sopenharmony_ci if (netif_running(netdev)) 263062306a36Sopenharmony_ci e100_up(nic); 263162306a36Sopenharmony_ci } 263262306a36Sopenharmony_ci for (i = 0; i < E100_TEST_LEN; i++) 263362306a36Sopenharmony_ci test->flags |= data[i] ? ETH_TEST_FL_FAILED : 0; 263462306a36Sopenharmony_ci 263562306a36Sopenharmony_ci msleep_interruptible(4 * 1000); 263662306a36Sopenharmony_ci} 263762306a36Sopenharmony_ci 263862306a36Sopenharmony_cistatic int e100_set_phys_id(struct net_device *netdev, 263962306a36Sopenharmony_ci enum ethtool_phys_id_state state) 264062306a36Sopenharmony_ci{ 264162306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 264262306a36Sopenharmony_ci enum led_state { 264362306a36Sopenharmony_ci led_on = 0x01, 264462306a36Sopenharmony_ci led_off = 0x04, 264562306a36Sopenharmony_ci led_on_559 = 0x05, 264662306a36Sopenharmony_ci led_on_557 = 0x07, 264762306a36Sopenharmony_ci }; 264862306a36Sopenharmony_ci u16 led_reg = (nic->phy == phy_82552_v) ? E100_82552_LED_OVERRIDE : 264962306a36Sopenharmony_ci MII_LED_CONTROL; 265062306a36Sopenharmony_ci u16 leds = 0; 265162306a36Sopenharmony_ci 265262306a36Sopenharmony_ci switch (state) { 265362306a36Sopenharmony_ci case ETHTOOL_ID_ACTIVE: 265462306a36Sopenharmony_ci return 2; 265562306a36Sopenharmony_ci 265662306a36Sopenharmony_ci case ETHTOOL_ID_ON: 265762306a36Sopenharmony_ci leds = (nic->phy == phy_82552_v) ? E100_82552_LED_ON : 265862306a36Sopenharmony_ci (nic->mac < mac_82559_D101M) ? led_on_557 : led_on_559; 265962306a36Sopenharmony_ci break; 266062306a36Sopenharmony_ci 266162306a36Sopenharmony_ci case ETHTOOL_ID_OFF: 266262306a36Sopenharmony_ci leds = (nic->phy == phy_82552_v) ? E100_82552_LED_OFF : led_off; 266362306a36Sopenharmony_ci break; 266462306a36Sopenharmony_ci 266562306a36Sopenharmony_ci case ETHTOOL_ID_INACTIVE: 266662306a36Sopenharmony_ci break; 266762306a36Sopenharmony_ci } 266862306a36Sopenharmony_ci 266962306a36Sopenharmony_ci mdio_write(netdev, nic->mii.phy_id, led_reg, leds); 267062306a36Sopenharmony_ci return 0; 267162306a36Sopenharmony_ci} 267262306a36Sopenharmony_ci 267362306a36Sopenharmony_cistatic const char e100_gstrings_stats[][ETH_GSTRING_LEN] = { 267462306a36Sopenharmony_ci "rx_packets", "tx_packets", "rx_bytes", "tx_bytes", "rx_errors", 267562306a36Sopenharmony_ci "tx_errors", "rx_dropped", "tx_dropped", "multicast", "collisions", 267662306a36Sopenharmony_ci "rx_length_errors", "rx_over_errors", "rx_crc_errors", 267762306a36Sopenharmony_ci "rx_frame_errors", "rx_fifo_errors", "rx_missed_errors", 267862306a36Sopenharmony_ci "tx_aborted_errors", "tx_carrier_errors", "tx_fifo_errors", 267962306a36Sopenharmony_ci "tx_heartbeat_errors", "tx_window_errors", 268062306a36Sopenharmony_ci /* device-specific stats */ 268162306a36Sopenharmony_ci "tx_deferred", "tx_single_collisions", "tx_multi_collisions", 268262306a36Sopenharmony_ci "tx_flow_control_pause", "rx_flow_control_pause", 268362306a36Sopenharmony_ci "rx_flow_control_unsupported", "tx_tco_packets", "rx_tco_packets", 268462306a36Sopenharmony_ci "rx_short_frame_errors", "rx_over_length_errors", 268562306a36Sopenharmony_ci}; 268662306a36Sopenharmony_ci#define E100_NET_STATS_LEN 21 268762306a36Sopenharmony_ci#define E100_STATS_LEN ARRAY_SIZE(e100_gstrings_stats) 268862306a36Sopenharmony_ci 268962306a36Sopenharmony_cistatic int e100_get_sset_count(struct net_device *netdev, int sset) 269062306a36Sopenharmony_ci{ 269162306a36Sopenharmony_ci switch (sset) { 269262306a36Sopenharmony_ci case ETH_SS_TEST: 269362306a36Sopenharmony_ci return E100_TEST_LEN; 269462306a36Sopenharmony_ci case ETH_SS_STATS: 269562306a36Sopenharmony_ci return E100_STATS_LEN; 269662306a36Sopenharmony_ci default: 269762306a36Sopenharmony_ci return -EOPNOTSUPP; 269862306a36Sopenharmony_ci } 269962306a36Sopenharmony_ci} 270062306a36Sopenharmony_ci 270162306a36Sopenharmony_cistatic void e100_get_ethtool_stats(struct net_device *netdev, 270262306a36Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 270362306a36Sopenharmony_ci{ 270462306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 270562306a36Sopenharmony_ci int i; 270662306a36Sopenharmony_ci 270762306a36Sopenharmony_ci for (i = 0; i < E100_NET_STATS_LEN; i++) 270862306a36Sopenharmony_ci data[i] = ((unsigned long *)&netdev->stats)[i]; 270962306a36Sopenharmony_ci 271062306a36Sopenharmony_ci data[i++] = nic->tx_deferred; 271162306a36Sopenharmony_ci data[i++] = nic->tx_single_collisions; 271262306a36Sopenharmony_ci data[i++] = nic->tx_multiple_collisions; 271362306a36Sopenharmony_ci data[i++] = nic->tx_fc_pause; 271462306a36Sopenharmony_ci data[i++] = nic->rx_fc_pause; 271562306a36Sopenharmony_ci data[i++] = nic->rx_fc_unsupported; 271662306a36Sopenharmony_ci data[i++] = nic->tx_tco_frames; 271762306a36Sopenharmony_ci data[i++] = nic->rx_tco_frames; 271862306a36Sopenharmony_ci data[i++] = nic->rx_short_frame_errors; 271962306a36Sopenharmony_ci data[i++] = nic->rx_over_length_errors; 272062306a36Sopenharmony_ci} 272162306a36Sopenharmony_ci 272262306a36Sopenharmony_cistatic void e100_get_strings(struct net_device *netdev, u32 stringset, u8 *data) 272362306a36Sopenharmony_ci{ 272462306a36Sopenharmony_ci switch (stringset) { 272562306a36Sopenharmony_ci case ETH_SS_TEST: 272662306a36Sopenharmony_ci memcpy(data, e100_gstrings_test, sizeof(e100_gstrings_test)); 272762306a36Sopenharmony_ci break; 272862306a36Sopenharmony_ci case ETH_SS_STATS: 272962306a36Sopenharmony_ci memcpy(data, e100_gstrings_stats, sizeof(e100_gstrings_stats)); 273062306a36Sopenharmony_ci break; 273162306a36Sopenharmony_ci } 273262306a36Sopenharmony_ci} 273362306a36Sopenharmony_ci 273462306a36Sopenharmony_cistatic const struct ethtool_ops e100_ethtool_ops = { 273562306a36Sopenharmony_ci .get_drvinfo = e100_get_drvinfo, 273662306a36Sopenharmony_ci .get_regs_len = e100_get_regs_len, 273762306a36Sopenharmony_ci .get_regs = e100_get_regs, 273862306a36Sopenharmony_ci .get_wol = e100_get_wol, 273962306a36Sopenharmony_ci .set_wol = e100_set_wol, 274062306a36Sopenharmony_ci .get_msglevel = e100_get_msglevel, 274162306a36Sopenharmony_ci .set_msglevel = e100_set_msglevel, 274262306a36Sopenharmony_ci .nway_reset = e100_nway_reset, 274362306a36Sopenharmony_ci .get_link = e100_get_link, 274462306a36Sopenharmony_ci .get_eeprom_len = e100_get_eeprom_len, 274562306a36Sopenharmony_ci .get_eeprom = e100_get_eeprom, 274662306a36Sopenharmony_ci .set_eeprom = e100_set_eeprom, 274762306a36Sopenharmony_ci .get_ringparam = e100_get_ringparam, 274862306a36Sopenharmony_ci .set_ringparam = e100_set_ringparam, 274962306a36Sopenharmony_ci .self_test = e100_diag_test, 275062306a36Sopenharmony_ci .get_strings = e100_get_strings, 275162306a36Sopenharmony_ci .set_phys_id = e100_set_phys_id, 275262306a36Sopenharmony_ci .get_ethtool_stats = e100_get_ethtool_stats, 275362306a36Sopenharmony_ci .get_sset_count = e100_get_sset_count, 275462306a36Sopenharmony_ci .get_ts_info = ethtool_op_get_ts_info, 275562306a36Sopenharmony_ci .get_link_ksettings = e100_get_link_ksettings, 275662306a36Sopenharmony_ci .set_link_ksettings = e100_set_link_ksettings, 275762306a36Sopenharmony_ci}; 275862306a36Sopenharmony_ci 275962306a36Sopenharmony_cistatic int e100_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) 276062306a36Sopenharmony_ci{ 276162306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 276262306a36Sopenharmony_ci 276362306a36Sopenharmony_ci return generic_mii_ioctl(&nic->mii, if_mii(ifr), cmd, NULL); 276462306a36Sopenharmony_ci} 276562306a36Sopenharmony_ci 276662306a36Sopenharmony_cistatic int e100_alloc(struct nic *nic) 276762306a36Sopenharmony_ci{ 276862306a36Sopenharmony_ci nic->mem = dma_alloc_coherent(&nic->pdev->dev, sizeof(struct mem), 276962306a36Sopenharmony_ci &nic->dma_addr, GFP_KERNEL); 277062306a36Sopenharmony_ci return nic->mem ? 0 : -ENOMEM; 277162306a36Sopenharmony_ci} 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_cistatic void e100_free(struct nic *nic) 277462306a36Sopenharmony_ci{ 277562306a36Sopenharmony_ci if (nic->mem) { 277662306a36Sopenharmony_ci dma_free_coherent(&nic->pdev->dev, sizeof(struct mem), 277762306a36Sopenharmony_ci nic->mem, nic->dma_addr); 277862306a36Sopenharmony_ci nic->mem = NULL; 277962306a36Sopenharmony_ci } 278062306a36Sopenharmony_ci} 278162306a36Sopenharmony_ci 278262306a36Sopenharmony_cistatic int e100_open(struct net_device *netdev) 278362306a36Sopenharmony_ci{ 278462306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 278562306a36Sopenharmony_ci int err = 0; 278662306a36Sopenharmony_ci 278762306a36Sopenharmony_ci netif_carrier_off(netdev); 278862306a36Sopenharmony_ci if ((err = e100_up(nic))) 278962306a36Sopenharmony_ci netif_err(nic, ifup, nic->netdev, "Cannot open interface, aborting\n"); 279062306a36Sopenharmony_ci return err; 279162306a36Sopenharmony_ci} 279262306a36Sopenharmony_ci 279362306a36Sopenharmony_cistatic int e100_close(struct net_device *netdev) 279462306a36Sopenharmony_ci{ 279562306a36Sopenharmony_ci e100_down(netdev_priv(netdev)); 279662306a36Sopenharmony_ci return 0; 279762306a36Sopenharmony_ci} 279862306a36Sopenharmony_ci 279962306a36Sopenharmony_cistatic int e100_set_features(struct net_device *netdev, 280062306a36Sopenharmony_ci netdev_features_t features) 280162306a36Sopenharmony_ci{ 280262306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 280362306a36Sopenharmony_ci netdev_features_t changed = features ^ netdev->features; 280462306a36Sopenharmony_ci 280562306a36Sopenharmony_ci if (!(changed & (NETIF_F_RXFCS | NETIF_F_RXALL))) 280662306a36Sopenharmony_ci return 0; 280762306a36Sopenharmony_ci 280862306a36Sopenharmony_ci netdev->features = features; 280962306a36Sopenharmony_ci e100_exec_cb(nic, NULL, e100_configure); 281062306a36Sopenharmony_ci return 1; 281162306a36Sopenharmony_ci} 281262306a36Sopenharmony_ci 281362306a36Sopenharmony_cistatic const struct net_device_ops e100_netdev_ops = { 281462306a36Sopenharmony_ci .ndo_open = e100_open, 281562306a36Sopenharmony_ci .ndo_stop = e100_close, 281662306a36Sopenharmony_ci .ndo_start_xmit = e100_xmit_frame, 281762306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 281862306a36Sopenharmony_ci .ndo_set_rx_mode = e100_set_multicast_list, 281962306a36Sopenharmony_ci .ndo_set_mac_address = e100_set_mac_address, 282062306a36Sopenharmony_ci .ndo_eth_ioctl = e100_do_ioctl, 282162306a36Sopenharmony_ci .ndo_tx_timeout = e100_tx_timeout, 282262306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 282362306a36Sopenharmony_ci .ndo_poll_controller = e100_netpoll, 282462306a36Sopenharmony_ci#endif 282562306a36Sopenharmony_ci .ndo_set_features = e100_set_features, 282662306a36Sopenharmony_ci}; 282762306a36Sopenharmony_ci 282862306a36Sopenharmony_cistatic int e100_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 282962306a36Sopenharmony_ci{ 283062306a36Sopenharmony_ci struct net_device *netdev; 283162306a36Sopenharmony_ci struct nic *nic; 283262306a36Sopenharmony_ci int err; 283362306a36Sopenharmony_ci 283462306a36Sopenharmony_ci if (!(netdev = alloc_etherdev(sizeof(struct nic)))) 283562306a36Sopenharmony_ci return -ENOMEM; 283662306a36Sopenharmony_ci 283762306a36Sopenharmony_ci netdev->hw_features |= NETIF_F_RXFCS; 283862306a36Sopenharmony_ci netdev->priv_flags |= IFF_SUPP_NOFCS; 283962306a36Sopenharmony_ci netdev->hw_features |= NETIF_F_RXALL; 284062306a36Sopenharmony_ci 284162306a36Sopenharmony_ci netdev->netdev_ops = &e100_netdev_ops; 284262306a36Sopenharmony_ci netdev->ethtool_ops = &e100_ethtool_ops; 284362306a36Sopenharmony_ci netdev->watchdog_timeo = E100_WATCHDOG_PERIOD; 284462306a36Sopenharmony_ci strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1); 284562306a36Sopenharmony_ci 284662306a36Sopenharmony_ci nic = netdev_priv(netdev); 284762306a36Sopenharmony_ci netif_napi_add_weight(netdev, &nic->napi, e100_poll, E100_NAPI_WEIGHT); 284862306a36Sopenharmony_ci nic->netdev = netdev; 284962306a36Sopenharmony_ci nic->pdev = pdev; 285062306a36Sopenharmony_ci nic->msg_enable = (1 << debug) - 1; 285162306a36Sopenharmony_ci nic->mdio_ctrl = mdio_ctrl_hw; 285262306a36Sopenharmony_ci pci_set_drvdata(pdev, netdev); 285362306a36Sopenharmony_ci 285462306a36Sopenharmony_ci if ((err = pci_enable_device(pdev))) { 285562306a36Sopenharmony_ci netif_err(nic, probe, nic->netdev, "Cannot enable PCI device, aborting\n"); 285662306a36Sopenharmony_ci goto err_out_free_dev; 285762306a36Sopenharmony_ci } 285862306a36Sopenharmony_ci 285962306a36Sopenharmony_ci if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { 286062306a36Sopenharmony_ci netif_err(nic, probe, nic->netdev, "Cannot find proper PCI device base address, aborting\n"); 286162306a36Sopenharmony_ci err = -ENODEV; 286262306a36Sopenharmony_ci goto err_out_disable_pdev; 286362306a36Sopenharmony_ci } 286462306a36Sopenharmony_ci 286562306a36Sopenharmony_ci if ((err = pci_request_regions(pdev, DRV_NAME))) { 286662306a36Sopenharmony_ci netif_err(nic, probe, nic->netdev, "Cannot obtain PCI resources, aborting\n"); 286762306a36Sopenharmony_ci goto err_out_disable_pdev; 286862306a36Sopenharmony_ci } 286962306a36Sopenharmony_ci 287062306a36Sopenharmony_ci if ((err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)))) { 287162306a36Sopenharmony_ci netif_err(nic, probe, nic->netdev, "No usable DMA configuration, aborting\n"); 287262306a36Sopenharmony_ci goto err_out_free_res; 287362306a36Sopenharmony_ci } 287462306a36Sopenharmony_ci 287562306a36Sopenharmony_ci SET_NETDEV_DEV(netdev, &pdev->dev); 287662306a36Sopenharmony_ci 287762306a36Sopenharmony_ci if (use_io) 287862306a36Sopenharmony_ci netif_info(nic, probe, nic->netdev, "using i/o access mode\n"); 287962306a36Sopenharmony_ci 288062306a36Sopenharmony_ci nic->csr = pci_iomap(pdev, (use_io ? 1 : 0), sizeof(struct csr)); 288162306a36Sopenharmony_ci if (!nic->csr) { 288262306a36Sopenharmony_ci netif_err(nic, probe, nic->netdev, "Cannot map device registers, aborting\n"); 288362306a36Sopenharmony_ci err = -ENOMEM; 288462306a36Sopenharmony_ci goto err_out_free_res; 288562306a36Sopenharmony_ci } 288662306a36Sopenharmony_ci 288762306a36Sopenharmony_ci if (ent->driver_data) 288862306a36Sopenharmony_ci nic->flags |= ich; 288962306a36Sopenharmony_ci else 289062306a36Sopenharmony_ci nic->flags &= ~ich; 289162306a36Sopenharmony_ci 289262306a36Sopenharmony_ci e100_get_defaults(nic); 289362306a36Sopenharmony_ci 289462306a36Sopenharmony_ci /* D100 MAC doesn't allow rx of vlan packets with normal MTU */ 289562306a36Sopenharmony_ci if (nic->mac < mac_82558_D101_A4) 289662306a36Sopenharmony_ci netdev->features |= NETIF_F_VLAN_CHALLENGED; 289762306a36Sopenharmony_ci 289862306a36Sopenharmony_ci /* locks must be initialized before calling hw_reset */ 289962306a36Sopenharmony_ci spin_lock_init(&nic->cb_lock); 290062306a36Sopenharmony_ci spin_lock_init(&nic->cmd_lock); 290162306a36Sopenharmony_ci spin_lock_init(&nic->mdio_lock); 290262306a36Sopenharmony_ci 290362306a36Sopenharmony_ci /* Reset the device before pci_set_master() in case device is in some 290462306a36Sopenharmony_ci * funky state and has an interrupt pending - hint: we don't have the 290562306a36Sopenharmony_ci * interrupt handler registered yet. */ 290662306a36Sopenharmony_ci e100_hw_reset(nic); 290762306a36Sopenharmony_ci 290862306a36Sopenharmony_ci pci_set_master(pdev); 290962306a36Sopenharmony_ci 291062306a36Sopenharmony_ci timer_setup(&nic->watchdog, e100_watchdog, 0); 291162306a36Sopenharmony_ci 291262306a36Sopenharmony_ci INIT_WORK(&nic->tx_timeout_task, e100_tx_timeout_task); 291362306a36Sopenharmony_ci 291462306a36Sopenharmony_ci if ((err = e100_alloc(nic))) { 291562306a36Sopenharmony_ci netif_err(nic, probe, nic->netdev, "Cannot alloc driver memory, aborting\n"); 291662306a36Sopenharmony_ci goto err_out_iounmap; 291762306a36Sopenharmony_ci } 291862306a36Sopenharmony_ci 291962306a36Sopenharmony_ci if ((err = e100_eeprom_load(nic))) 292062306a36Sopenharmony_ci goto err_out_free; 292162306a36Sopenharmony_ci 292262306a36Sopenharmony_ci e100_phy_init(nic); 292362306a36Sopenharmony_ci 292462306a36Sopenharmony_ci eth_hw_addr_set(netdev, (u8 *)nic->eeprom); 292562306a36Sopenharmony_ci if (!is_valid_ether_addr(netdev->dev_addr)) { 292662306a36Sopenharmony_ci if (!eeprom_bad_csum_allow) { 292762306a36Sopenharmony_ci netif_err(nic, probe, nic->netdev, "Invalid MAC address from EEPROM, aborting\n"); 292862306a36Sopenharmony_ci err = -EAGAIN; 292962306a36Sopenharmony_ci goto err_out_free; 293062306a36Sopenharmony_ci } else { 293162306a36Sopenharmony_ci netif_err(nic, probe, nic->netdev, "Invalid MAC address from EEPROM, you MUST configure one.\n"); 293262306a36Sopenharmony_ci } 293362306a36Sopenharmony_ci } 293462306a36Sopenharmony_ci 293562306a36Sopenharmony_ci /* Wol magic packet can be enabled from eeprom */ 293662306a36Sopenharmony_ci if ((nic->mac >= mac_82558_D101_A4) && 293762306a36Sopenharmony_ci (le16_to_cpu(nic->eeprom[eeprom_id]) & eeprom_id_wol)) { 293862306a36Sopenharmony_ci nic->flags |= wol_magic; 293962306a36Sopenharmony_ci device_set_wakeup_enable(&pdev->dev, true); 294062306a36Sopenharmony_ci } 294162306a36Sopenharmony_ci 294262306a36Sopenharmony_ci /* ack any pending wake events, disable PME */ 294362306a36Sopenharmony_ci pci_pme_active(pdev, false); 294462306a36Sopenharmony_ci 294562306a36Sopenharmony_ci strcpy(netdev->name, "eth%d"); 294662306a36Sopenharmony_ci if ((err = register_netdev(netdev))) { 294762306a36Sopenharmony_ci netif_err(nic, probe, nic->netdev, "Cannot register net device, aborting\n"); 294862306a36Sopenharmony_ci goto err_out_free; 294962306a36Sopenharmony_ci } 295062306a36Sopenharmony_ci nic->cbs_pool = dma_pool_create(netdev->name, 295162306a36Sopenharmony_ci &nic->pdev->dev, 295262306a36Sopenharmony_ci nic->params.cbs.max * sizeof(struct cb), 295362306a36Sopenharmony_ci sizeof(u32), 295462306a36Sopenharmony_ci 0); 295562306a36Sopenharmony_ci if (!nic->cbs_pool) { 295662306a36Sopenharmony_ci netif_err(nic, probe, nic->netdev, "Cannot create DMA pool, aborting\n"); 295762306a36Sopenharmony_ci err = -ENOMEM; 295862306a36Sopenharmony_ci goto err_out_pool; 295962306a36Sopenharmony_ci } 296062306a36Sopenharmony_ci netif_info(nic, probe, nic->netdev, 296162306a36Sopenharmony_ci "addr 0x%llx, irq %d, MAC addr %pM\n", 296262306a36Sopenharmony_ci (unsigned long long)pci_resource_start(pdev, use_io ? 1 : 0), 296362306a36Sopenharmony_ci pdev->irq, netdev->dev_addr); 296462306a36Sopenharmony_ci 296562306a36Sopenharmony_ci return 0; 296662306a36Sopenharmony_ci 296762306a36Sopenharmony_cierr_out_pool: 296862306a36Sopenharmony_ci unregister_netdev(netdev); 296962306a36Sopenharmony_cierr_out_free: 297062306a36Sopenharmony_ci e100_free(nic); 297162306a36Sopenharmony_cierr_out_iounmap: 297262306a36Sopenharmony_ci pci_iounmap(pdev, nic->csr); 297362306a36Sopenharmony_cierr_out_free_res: 297462306a36Sopenharmony_ci pci_release_regions(pdev); 297562306a36Sopenharmony_cierr_out_disable_pdev: 297662306a36Sopenharmony_ci pci_disable_device(pdev); 297762306a36Sopenharmony_cierr_out_free_dev: 297862306a36Sopenharmony_ci free_netdev(netdev); 297962306a36Sopenharmony_ci return err; 298062306a36Sopenharmony_ci} 298162306a36Sopenharmony_ci 298262306a36Sopenharmony_cistatic void e100_remove(struct pci_dev *pdev) 298362306a36Sopenharmony_ci{ 298462306a36Sopenharmony_ci struct net_device *netdev = pci_get_drvdata(pdev); 298562306a36Sopenharmony_ci 298662306a36Sopenharmony_ci if (netdev) { 298762306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 298862306a36Sopenharmony_ci unregister_netdev(netdev); 298962306a36Sopenharmony_ci e100_free(nic); 299062306a36Sopenharmony_ci pci_iounmap(pdev, nic->csr); 299162306a36Sopenharmony_ci dma_pool_destroy(nic->cbs_pool); 299262306a36Sopenharmony_ci free_netdev(netdev); 299362306a36Sopenharmony_ci pci_release_regions(pdev); 299462306a36Sopenharmony_ci pci_disable_device(pdev); 299562306a36Sopenharmony_ci } 299662306a36Sopenharmony_ci} 299762306a36Sopenharmony_ci 299862306a36Sopenharmony_ci#define E100_82552_SMARTSPEED 0x14 /* SmartSpeed Ctrl register */ 299962306a36Sopenharmony_ci#define E100_82552_REV_ANEG 0x0200 /* Reverse auto-negotiation */ 300062306a36Sopenharmony_ci#define E100_82552_ANEG_NOW 0x0400 /* Auto-negotiate now */ 300162306a36Sopenharmony_cistatic void __e100_shutdown(struct pci_dev *pdev, bool *enable_wake) 300262306a36Sopenharmony_ci{ 300362306a36Sopenharmony_ci struct net_device *netdev = pci_get_drvdata(pdev); 300462306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 300562306a36Sopenharmony_ci 300662306a36Sopenharmony_ci netif_device_detach(netdev); 300762306a36Sopenharmony_ci 300862306a36Sopenharmony_ci if (netif_running(netdev)) 300962306a36Sopenharmony_ci e100_down(nic); 301062306a36Sopenharmony_ci 301162306a36Sopenharmony_ci if ((nic->flags & wol_magic) | e100_asf(nic)) { 301262306a36Sopenharmony_ci /* enable reverse auto-negotiation */ 301362306a36Sopenharmony_ci if (nic->phy == phy_82552_v) { 301462306a36Sopenharmony_ci u16 smartspeed = mdio_read(netdev, nic->mii.phy_id, 301562306a36Sopenharmony_ci E100_82552_SMARTSPEED); 301662306a36Sopenharmony_ci 301762306a36Sopenharmony_ci mdio_write(netdev, nic->mii.phy_id, 301862306a36Sopenharmony_ci E100_82552_SMARTSPEED, smartspeed | 301962306a36Sopenharmony_ci E100_82552_REV_ANEG | E100_82552_ANEG_NOW); 302062306a36Sopenharmony_ci } 302162306a36Sopenharmony_ci *enable_wake = true; 302262306a36Sopenharmony_ci } else { 302362306a36Sopenharmony_ci *enable_wake = false; 302462306a36Sopenharmony_ci } 302562306a36Sopenharmony_ci 302662306a36Sopenharmony_ci pci_disable_device(pdev); 302762306a36Sopenharmony_ci} 302862306a36Sopenharmony_ci 302962306a36Sopenharmony_cistatic int __e100_power_off(struct pci_dev *pdev, bool wake) 303062306a36Sopenharmony_ci{ 303162306a36Sopenharmony_ci if (wake) 303262306a36Sopenharmony_ci return pci_prepare_to_sleep(pdev); 303362306a36Sopenharmony_ci 303462306a36Sopenharmony_ci pci_wake_from_d3(pdev, false); 303562306a36Sopenharmony_ci pci_set_power_state(pdev, PCI_D3hot); 303662306a36Sopenharmony_ci 303762306a36Sopenharmony_ci return 0; 303862306a36Sopenharmony_ci} 303962306a36Sopenharmony_ci 304062306a36Sopenharmony_cistatic int __maybe_unused e100_suspend(struct device *dev_d) 304162306a36Sopenharmony_ci{ 304262306a36Sopenharmony_ci bool wake; 304362306a36Sopenharmony_ci 304462306a36Sopenharmony_ci __e100_shutdown(to_pci_dev(dev_d), &wake); 304562306a36Sopenharmony_ci 304662306a36Sopenharmony_ci return 0; 304762306a36Sopenharmony_ci} 304862306a36Sopenharmony_ci 304962306a36Sopenharmony_cistatic int __maybe_unused e100_resume(struct device *dev_d) 305062306a36Sopenharmony_ci{ 305162306a36Sopenharmony_ci struct net_device *netdev = dev_get_drvdata(dev_d); 305262306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 305362306a36Sopenharmony_ci int err; 305462306a36Sopenharmony_ci 305562306a36Sopenharmony_ci err = pci_enable_device(to_pci_dev(dev_d)); 305662306a36Sopenharmony_ci if (err) { 305762306a36Sopenharmony_ci netdev_err(netdev, "Resume cannot enable PCI device, aborting\n"); 305862306a36Sopenharmony_ci return err; 305962306a36Sopenharmony_ci } 306062306a36Sopenharmony_ci pci_set_master(to_pci_dev(dev_d)); 306162306a36Sopenharmony_ci 306262306a36Sopenharmony_ci /* disable reverse auto-negotiation */ 306362306a36Sopenharmony_ci if (nic->phy == phy_82552_v) { 306462306a36Sopenharmony_ci u16 smartspeed = mdio_read(netdev, nic->mii.phy_id, 306562306a36Sopenharmony_ci E100_82552_SMARTSPEED); 306662306a36Sopenharmony_ci 306762306a36Sopenharmony_ci mdio_write(netdev, nic->mii.phy_id, 306862306a36Sopenharmony_ci E100_82552_SMARTSPEED, 306962306a36Sopenharmony_ci smartspeed & ~(E100_82552_REV_ANEG)); 307062306a36Sopenharmony_ci } 307162306a36Sopenharmony_ci 307262306a36Sopenharmony_ci if (netif_running(netdev)) 307362306a36Sopenharmony_ci e100_up(nic); 307462306a36Sopenharmony_ci 307562306a36Sopenharmony_ci netif_device_attach(netdev); 307662306a36Sopenharmony_ci 307762306a36Sopenharmony_ci return 0; 307862306a36Sopenharmony_ci} 307962306a36Sopenharmony_ci 308062306a36Sopenharmony_cistatic void e100_shutdown(struct pci_dev *pdev) 308162306a36Sopenharmony_ci{ 308262306a36Sopenharmony_ci bool wake; 308362306a36Sopenharmony_ci __e100_shutdown(pdev, &wake); 308462306a36Sopenharmony_ci if (system_state == SYSTEM_POWER_OFF) 308562306a36Sopenharmony_ci __e100_power_off(pdev, wake); 308662306a36Sopenharmony_ci} 308762306a36Sopenharmony_ci 308862306a36Sopenharmony_ci/* ------------------ PCI Error Recovery infrastructure -------------- */ 308962306a36Sopenharmony_ci/** 309062306a36Sopenharmony_ci * e100_io_error_detected - called when PCI error is detected. 309162306a36Sopenharmony_ci * @pdev: Pointer to PCI device 309262306a36Sopenharmony_ci * @state: The current pci connection state 309362306a36Sopenharmony_ci */ 309462306a36Sopenharmony_cistatic pci_ers_result_t e100_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state) 309562306a36Sopenharmony_ci{ 309662306a36Sopenharmony_ci struct net_device *netdev = pci_get_drvdata(pdev); 309762306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 309862306a36Sopenharmony_ci 309962306a36Sopenharmony_ci netif_device_detach(netdev); 310062306a36Sopenharmony_ci 310162306a36Sopenharmony_ci if (state == pci_channel_io_perm_failure) 310262306a36Sopenharmony_ci return PCI_ERS_RESULT_DISCONNECT; 310362306a36Sopenharmony_ci 310462306a36Sopenharmony_ci if (netif_running(netdev)) 310562306a36Sopenharmony_ci e100_down(nic); 310662306a36Sopenharmony_ci pci_disable_device(pdev); 310762306a36Sopenharmony_ci 310862306a36Sopenharmony_ci /* Request a slot reset. */ 310962306a36Sopenharmony_ci return PCI_ERS_RESULT_NEED_RESET; 311062306a36Sopenharmony_ci} 311162306a36Sopenharmony_ci 311262306a36Sopenharmony_ci/** 311362306a36Sopenharmony_ci * e100_io_slot_reset - called after the pci bus has been reset. 311462306a36Sopenharmony_ci * @pdev: Pointer to PCI device 311562306a36Sopenharmony_ci * 311662306a36Sopenharmony_ci * Restart the card from scratch. 311762306a36Sopenharmony_ci */ 311862306a36Sopenharmony_cistatic pci_ers_result_t e100_io_slot_reset(struct pci_dev *pdev) 311962306a36Sopenharmony_ci{ 312062306a36Sopenharmony_ci struct net_device *netdev = pci_get_drvdata(pdev); 312162306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 312262306a36Sopenharmony_ci 312362306a36Sopenharmony_ci if (pci_enable_device(pdev)) { 312462306a36Sopenharmony_ci pr_err("Cannot re-enable PCI device after reset\n"); 312562306a36Sopenharmony_ci return PCI_ERS_RESULT_DISCONNECT; 312662306a36Sopenharmony_ci } 312762306a36Sopenharmony_ci pci_set_master(pdev); 312862306a36Sopenharmony_ci 312962306a36Sopenharmony_ci /* Only one device per card can do a reset */ 313062306a36Sopenharmony_ci if (0 != PCI_FUNC(pdev->devfn)) 313162306a36Sopenharmony_ci return PCI_ERS_RESULT_RECOVERED; 313262306a36Sopenharmony_ci e100_hw_reset(nic); 313362306a36Sopenharmony_ci e100_phy_init(nic); 313462306a36Sopenharmony_ci 313562306a36Sopenharmony_ci return PCI_ERS_RESULT_RECOVERED; 313662306a36Sopenharmony_ci} 313762306a36Sopenharmony_ci 313862306a36Sopenharmony_ci/** 313962306a36Sopenharmony_ci * e100_io_resume - resume normal operations 314062306a36Sopenharmony_ci * @pdev: Pointer to PCI device 314162306a36Sopenharmony_ci * 314262306a36Sopenharmony_ci * Resume normal operations after an error recovery 314362306a36Sopenharmony_ci * sequence has been completed. 314462306a36Sopenharmony_ci */ 314562306a36Sopenharmony_cistatic void e100_io_resume(struct pci_dev *pdev) 314662306a36Sopenharmony_ci{ 314762306a36Sopenharmony_ci struct net_device *netdev = pci_get_drvdata(pdev); 314862306a36Sopenharmony_ci struct nic *nic = netdev_priv(netdev); 314962306a36Sopenharmony_ci 315062306a36Sopenharmony_ci /* ack any pending wake events, disable PME */ 315162306a36Sopenharmony_ci pci_enable_wake(pdev, PCI_D0, 0); 315262306a36Sopenharmony_ci 315362306a36Sopenharmony_ci netif_device_attach(netdev); 315462306a36Sopenharmony_ci if (netif_running(netdev)) { 315562306a36Sopenharmony_ci e100_open(netdev); 315662306a36Sopenharmony_ci mod_timer(&nic->watchdog, jiffies); 315762306a36Sopenharmony_ci } 315862306a36Sopenharmony_ci} 315962306a36Sopenharmony_ci 316062306a36Sopenharmony_cistatic const struct pci_error_handlers e100_err_handler = { 316162306a36Sopenharmony_ci .error_detected = e100_io_error_detected, 316262306a36Sopenharmony_ci .slot_reset = e100_io_slot_reset, 316362306a36Sopenharmony_ci .resume = e100_io_resume, 316462306a36Sopenharmony_ci}; 316562306a36Sopenharmony_ci 316662306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(e100_pm_ops, e100_suspend, e100_resume); 316762306a36Sopenharmony_ci 316862306a36Sopenharmony_cistatic struct pci_driver e100_driver = { 316962306a36Sopenharmony_ci .name = DRV_NAME, 317062306a36Sopenharmony_ci .id_table = e100_id_table, 317162306a36Sopenharmony_ci .probe = e100_probe, 317262306a36Sopenharmony_ci .remove = e100_remove, 317362306a36Sopenharmony_ci 317462306a36Sopenharmony_ci /* Power Management hooks */ 317562306a36Sopenharmony_ci .driver.pm = &e100_pm_ops, 317662306a36Sopenharmony_ci 317762306a36Sopenharmony_ci .shutdown = e100_shutdown, 317862306a36Sopenharmony_ci .err_handler = &e100_err_handler, 317962306a36Sopenharmony_ci}; 318062306a36Sopenharmony_ci 318162306a36Sopenharmony_cistatic int __init e100_init_module(void) 318262306a36Sopenharmony_ci{ 318362306a36Sopenharmony_ci if (((1 << debug) - 1) & NETIF_MSG_DRV) { 318462306a36Sopenharmony_ci pr_info("%s\n", DRV_DESCRIPTION); 318562306a36Sopenharmony_ci pr_info("%s\n", DRV_COPYRIGHT); 318662306a36Sopenharmony_ci } 318762306a36Sopenharmony_ci return pci_register_driver(&e100_driver); 318862306a36Sopenharmony_ci} 318962306a36Sopenharmony_ci 319062306a36Sopenharmony_cistatic void __exit e100_cleanup_module(void) 319162306a36Sopenharmony_ci{ 319262306a36Sopenharmony_ci pci_unregister_driver(&e100_driver); 319362306a36Sopenharmony_ci} 319462306a36Sopenharmony_ci 319562306a36Sopenharmony_cimodule_init(e100_init_module); 319662306a36Sopenharmony_cimodule_exit(e100_cleanup_module); 3197