18c2ecf20Sopenharmony_ci/************************************************************************* 28c2ecf20Sopenharmony_ci * myri10ge.c: Myricom Myri-10G Ethernet driver. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2005 - 2011 Myricom, Inc. 58c2ecf20Sopenharmony_ci * All rights reserved. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 88c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 98c2ecf20Sopenharmony_ci * are met: 108c2ecf20Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 118c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 128c2ecf20Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright 138c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in the 148c2ecf20Sopenharmony_ci * documentation and/or other materials provided with the distribution. 158c2ecf20Sopenharmony_ci * 3. Neither the name of Myricom, Inc. nor the names of its contributors 168c2ecf20Sopenharmony_ci * may be used to endorse or promote products derived from this software 178c2ecf20Sopenharmony_ci * without specific prior written permission. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 208c2ecf20Sopenharmony_ci * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 218c2ecf20Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 228c2ecf20Sopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 238c2ecf20Sopenharmony_ci * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 248c2ecf20Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 258c2ecf20Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 268c2ecf20Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 278c2ecf20Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 288c2ecf20Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 298c2ecf20Sopenharmony_ci * POSSIBILITY OF SUCH DAMAGE. 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * If the eeprom on your board is not recent enough, you will need to get a 338c2ecf20Sopenharmony_ci * newer firmware image at: 348c2ecf20Sopenharmony_ci * http://www.myri.com/scs/download-Myri10GE.html 358c2ecf20Sopenharmony_ci * 368c2ecf20Sopenharmony_ci * Contact Information: 378c2ecf20Sopenharmony_ci * <help@myri.com> 388c2ecf20Sopenharmony_ci * Myricom, Inc., 325N Santa Anita Avenue, Arcadia, CA 91006 398c2ecf20Sopenharmony_ci *************************************************************************/ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#include <linux/tcp.h> 448c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 458c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 468c2ecf20Sopenharmony_ci#include <linux/string.h> 478c2ecf20Sopenharmony_ci#include <linux/module.h> 488c2ecf20Sopenharmony_ci#include <linux/pci.h> 498c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 508c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 518c2ecf20Sopenharmony_ci#include <linux/if_ether.h> 528c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 538c2ecf20Sopenharmony_ci#include <linux/dca.h> 548c2ecf20Sopenharmony_ci#include <linux/ip.h> 558c2ecf20Sopenharmony_ci#include <linux/inet.h> 568c2ecf20Sopenharmony_ci#include <linux/in.h> 578c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 588c2ecf20Sopenharmony_ci#include <linux/firmware.h> 598c2ecf20Sopenharmony_ci#include <linux/delay.h> 608c2ecf20Sopenharmony_ci#include <linux/timer.h> 618c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 628c2ecf20Sopenharmony_ci#include <linux/crc32.h> 638c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 648c2ecf20Sopenharmony_ci#include <linux/io.h> 658c2ecf20Sopenharmony_ci#include <linux/log2.h> 668c2ecf20Sopenharmony_ci#include <linux/slab.h> 678c2ecf20Sopenharmony_ci#include <linux/prefetch.h> 688c2ecf20Sopenharmony_ci#include <net/checksum.h> 698c2ecf20Sopenharmony_ci#include <net/ip.h> 708c2ecf20Sopenharmony_ci#include <net/tcp.h> 718c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 728c2ecf20Sopenharmony_ci#include <asm/processor.h> 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#include "myri10ge_mcp.h" 758c2ecf20Sopenharmony_ci#include "myri10ge_mcp_gen_header.h" 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#define MYRI10GE_VERSION_STR "1.5.3-1.534" 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Myricom 10G driver (10GbE)"); 808c2ecf20Sopenharmony_ciMODULE_AUTHOR("Maintainer: help@myri.com"); 818c2ecf20Sopenharmony_ciMODULE_VERSION(MYRI10GE_VERSION_STR); 828c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci#define MYRI10GE_MAX_ETHER_MTU 9014 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define MYRI10GE_ETH_STOPPED 0 878c2ecf20Sopenharmony_ci#define MYRI10GE_ETH_STOPPING 1 888c2ecf20Sopenharmony_ci#define MYRI10GE_ETH_STARTING 2 898c2ecf20Sopenharmony_ci#define MYRI10GE_ETH_RUNNING 3 908c2ecf20Sopenharmony_ci#define MYRI10GE_ETH_OPEN_FAILED 4 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci#define MYRI10GE_EEPROM_STRINGS_SIZE 256 938c2ecf20Sopenharmony_ci#define MYRI10GE_MAX_SEND_DESC_TSO ((65536 / 2048) * 2) 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci#define MYRI10GE_NO_CONFIRM_DATA htonl(0xffffffff) 968c2ecf20Sopenharmony_ci#define MYRI10GE_NO_RESPONSE_RESULT 0xffffffff 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci#define MYRI10GE_ALLOC_ORDER 0 998c2ecf20Sopenharmony_ci#define MYRI10GE_ALLOC_SIZE ((1 << MYRI10GE_ALLOC_ORDER) * PAGE_SIZE) 1008c2ecf20Sopenharmony_ci#define MYRI10GE_MAX_FRAGS_PER_FRAME (MYRI10GE_MAX_ETHER_MTU/MYRI10GE_ALLOC_SIZE + 1) 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci#define MYRI10GE_MAX_SLICES 32 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistruct myri10ge_rx_buffer_state { 1058c2ecf20Sopenharmony_ci struct page *page; 1068c2ecf20Sopenharmony_ci int page_offset; 1078c2ecf20Sopenharmony_ci DEFINE_DMA_UNMAP_ADDR(bus); 1088c2ecf20Sopenharmony_ci DEFINE_DMA_UNMAP_LEN(len); 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistruct myri10ge_tx_buffer_state { 1128c2ecf20Sopenharmony_ci struct sk_buff *skb; 1138c2ecf20Sopenharmony_ci int last; 1148c2ecf20Sopenharmony_ci DEFINE_DMA_UNMAP_ADDR(bus); 1158c2ecf20Sopenharmony_ci DEFINE_DMA_UNMAP_LEN(len); 1168c2ecf20Sopenharmony_ci}; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistruct myri10ge_cmd { 1198c2ecf20Sopenharmony_ci u32 data0; 1208c2ecf20Sopenharmony_ci u32 data1; 1218c2ecf20Sopenharmony_ci u32 data2; 1228c2ecf20Sopenharmony_ci}; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistruct myri10ge_rx_buf { 1258c2ecf20Sopenharmony_ci struct mcp_kreq_ether_recv __iomem *lanai; /* lanai ptr for recv ring */ 1268c2ecf20Sopenharmony_ci struct mcp_kreq_ether_recv *shadow; /* host shadow of recv ring */ 1278c2ecf20Sopenharmony_ci struct myri10ge_rx_buffer_state *info; 1288c2ecf20Sopenharmony_ci struct page *page; 1298c2ecf20Sopenharmony_ci dma_addr_t bus; 1308c2ecf20Sopenharmony_ci int page_offset; 1318c2ecf20Sopenharmony_ci int cnt; 1328c2ecf20Sopenharmony_ci int fill_cnt; 1338c2ecf20Sopenharmony_ci int alloc_fail; 1348c2ecf20Sopenharmony_ci int mask; /* number of rx slots -1 */ 1358c2ecf20Sopenharmony_ci int watchdog_needed; 1368c2ecf20Sopenharmony_ci}; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistruct myri10ge_tx_buf { 1398c2ecf20Sopenharmony_ci struct mcp_kreq_ether_send __iomem *lanai; /* lanai ptr for sendq */ 1408c2ecf20Sopenharmony_ci __be32 __iomem *send_go; /* "go" doorbell ptr */ 1418c2ecf20Sopenharmony_ci __be32 __iomem *send_stop; /* "stop" doorbell ptr */ 1428c2ecf20Sopenharmony_ci struct mcp_kreq_ether_send *req_list; /* host shadow of sendq */ 1438c2ecf20Sopenharmony_ci char *req_bytes; 1448c2ecf20Sopenharmony_ci struct myri10ge_tx_buffer_state *info; 1458c2ecf20Sopenharmony_ci int mask; /* number of transmit slots -1 */ 1468c2ecf20Sopenharmony_ci int req ____cacheline_aligned; /* transmit slots submitted */ 1478c2ecf20Sopenharmony_ci int pkt_start; /* packets started */ 1488c2ecf20Sopenharmony_ci int stop_queue; 1498c2ecf20Sopenharmony_ci int linearized; 1508c2ecf20Sopenharmony_ci int done ____cacheline_aligned; /* transmit slots completed */ 1518c2ecf20Sopenharmony_ci int pkt_done; /* packets completed */ 1528c2ecf20Sopenharmony_ci int wake_queue; 1538c2ecf20Sopenharmony_ci int queue_active; 1548c2ecf20Sopenharmony_ci}; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistruct myri10ge_rx_done { 1578c2ecf20Sopenharmony_ci struct mcp_slot *entry; 1588c2ecf20Sopenharmony_ci dma_addr_t bus; 1598c2ecf20Sopenharmony_ci int cnt; 1608c2ecf20Sopenharmony_ci int idx; 1618c2ecf20Sopenharmony_ci}; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistruct myri10ge_slice_netstats { 1648c2ecf20Sopenharmony_ci unsigned long rx_packets; 1658c2ecf20Sopenharmony_ci unsigned long tx_packets; 1668c2ecf20Sopenharmony_ci unsigned long rx_bytes; 1678c2ecf20Sopenharmony_ci unsigned long tx_bytes; 1688c2ecf20Sopenharmony_ci unsigned long rx_dropped; 1698c2ecf20Sopenharmony_ci unsigned long tx_dropped; 1708c2ecf20Sopenharmony_ci}; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistruct myri10ge_slice_state { 1738c2ecf20Sopenharmony_ci struct myri10ge_tx_buf tx; /* transmit ring */ 1748c2ecf20Sopenharmony_ci struct myri10ge_rx_buf rx_small; 1758c2ecf20Sopenharmony_ci struct myri10ge_rx_buf rx_big; 1768c2ecf20Sopenharmony_ci struct myri10ge_rx_done rx_done; 1778c2ecf20Sopenharmony_ci struct net_device *dev; 1788c2ecf20Sopenharmony_ci struct napi_struct napi; 1798c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp; 1808c2ecf20Sopenharmony_ci struct myri10ge_slice_netstats stats; 1818c2ecf20Sopenharmony_ci __be32 __iomem *irq_claim; 1828c2ecf20Sopenharmony_ci struct mcp_irq_data *fw_stats; 1838c2ecf20Sopenharmony_ci dma_addr_t fw_stats_bus; 1848c2ecf20Sopenharmony_ci int watchdog_tx_done; 1858c2ecf20Sopenharmony_ci int watchdog_tx_req; 1868c2ecf20Sopenharmony_ci int watchdog_rx_done; 1878c2ecf20Sopenharmony_ci int stuck; 1888c2ecf20Sopenharmony_ci#ifdef CONFIG_MYRI10GE_DCA 1898c2ecf20Sopenharmony_ci int cached_dca_tag; 1908c2ecf20Sopenharmony_ci int cpu; 1918c2ecf20Sopenharmony_ci __be32 __iomem *dca_tag; 1928c2ecf20Sopenharmony_ci#endif 1938c2ecf20Sopenharmony_ci char irq_desc[32]; 1948c2ecf20Sopenharmony_ci}; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistruct myri10ge_priv { 1978c2ecf20Sopenharmony_ci struct myri10ge_slice_state *ss; 1988c2ecf20Sopenharmony_ci int tx_boundary; /* boundary transmits cannot cross */ 1998c2ecf20Sopenharmony_ci int num_slices; 2008c2ecf20Sopenharmony_ci int running; /* running? */ 2018c2ecf20Sopenharmony_ci int small_bytes; 2028c2ecf20Sopenharmony_ci int big_bytes; 2038c2ecf20Sopenharmony_ci int max_intr_slots; 2048c2ecf20Sopenharmony_ci struct net_device *dev; 2058c2ecf20Sopenharmony_ci u8 __iomem *sram; 2068c2ecf20Sopenharmony_ci int sram_size; 2078c2ecf20Sopenharmony_ci unsigned long board_span; 2088c2ecf20Sopenharmony_ci unsigned long iomem_base; 2098c2ecf20Sopenharmony_ci __be32 __iomem *irq_deassert; 2108c2ecf20Sopenharmony_ci char *mac_addr_string; 2118c2ecf20Sopenharmony_ci struct mcp_cmd_response *cmd; 2128c2ecf20Sopenharmony_ci dma_addr_t cmd_bus; 2138c2ecf20Sopenharmony_ci struct pci_dev *pdev; 2148c2ecf20Sopenharmony_ci int msi_enabled; 2158c2ecf20Sopenharmony_ci int msix_enabled; 2168c2ecf20Sopenharmony_ci struct msix_entry *msix_vectors; 2178c2ecf20Sopenharmony_ci#ifdef CONFIG_MYRI10GE_DCA 2188c2ecf20Sopenharmony_ci int dca_enabled; 2198c2ecf20Sopenharmony_ci int relaxed_order; 2208c2ecf20Sopenharmony_ci#endif 2218c2ecf20Sopenharmony_ci u32 link_state; 2228c2ecf20Sopenharmony_ci unsigned int rdma_tags_available; 2238c2ecf20Sopenharmony_ci int intr_coal_delay; 2248c2ecf20Sopenharmony_ci __be32 __iomem *intr_coal_delay_ptr; 2258c2ecf20Sopenharmony_ci int wc_cookie; 2268c2ecf20Sopenharmony_ci int down_cnt; 2278c2ecf20Sopenharmony_ci wait_queue_head_t down_wq; 2288c2ecf20Sopenharmony_ci struct work_struct watchdog_work; 2298c2ecf20Sopenharmony_ci struct timer_list watchdog_timer; 2308c2ecf20Sopenharmony_ci int watchdog_resets; 2318c2ecf20Sopenharmony_ci int watchdog_pause; 2328c2ecf20Sopenharmony_ci int pause; 2338c2ecf20Sopenharmony_ci bool fw_name_allocated; 2348c2ecf20Sopenharmony_ci char *fw_name; 2358c2ecf20Sopenharmony_ci char eeprom_strings[MYRI10GE_EEPROM_STRINGS_SIZE]; 2368c2ecf20Sopenharmony_ci char *product_code_string; 2378c2ecf20Sopenharmony_ci char fw_version[128]; 2388c2ecf20Sopenharmony_ci int fw_ver_major; 2398c2ecf20Sopenharmony_ci int fw_ver_minor; 2408c2ecf20Sopenharmony_ci int fw_ver_tiny; 2418c2ecf20Sopenharmony_ci int adopted_rx_filter_bug; 2428c2ecf20Sopenharmony_ci u8 mac_addr[ETH_ALEN]; /* eeprom mac address */ 2438c2ecf20Sopenharmony_ci unsigned long serial_number; 2448c2ecf20Sopenharmony_ci int vendor_specific_offset; 2458c2ecf20Sopenharmony_ci int fw_multicast_support; 2468c2ecf20Sopenharmony_ci u32 features; 2478c2ecf20Sopenharmony_ci u32 max_tso6; 2488c2ecf20Sopenharmony_ci u32 read_dma; 2498c2ecf20Sopenharmony_ci u32 write_dma; 2508c2ecf20Sopenharmony_ci u32 read_write_dma; 2518c2ecf20Sopenharmony_ci u32 link_changes; 2528c2ecf20Sopenharmony_ci u32 msg_enable; 2538c2ecf20Sopenharmony_ci unsigned int board_number; 2548c2ecf20Sopenharmony_ci int rebooted; 2558c2ecf20Sopenharmony_ci}; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic char *myri10ge_fw_unaligned = "myri10ge_ethp_z8e.dat"; 2588c2ecf20Sopenharmony_cistatic char *myri10ge_fw_aligned = "myri10ge_eth_z8e.dat"; 2598c2ecf20Sopenharmony_cistatic char *myri10ge_fw_rss_unaligned = "myri10ge_rss_ethp_z8e.dat"; 2608c2ecf20Sopenharmony_cistatic char *myri10ge_fw_rss_aligned = "myri10ge_rss_eth_z8e.dat"; 2618c2ecf20Sopenharmony_ciMODULE_FIRMWARE("myri10ge_ethp_z8e.dat"); 2628c2ecf20Sopenharmony_ciMODULE_FIRMWARE("myri10ge_eth_z8e.dat"); 2638c2ecf20Sopenharmony_ciMODULE_FIRMWARE("myri10ge_rss_ethp_z8e.dat"); 2648c2ecf20Sopenharmony_ciMODULE_FIRMWARE("myri10ge_rss_eth_z8e.dat"); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci/* Careful: must be accessed under kernel_param_lock() */ 2678c2ecf20Sopenharmony_cistatic char *myri10ge_fw_name = NULL; 2688c2ecf20Sopenharmony_cimodule_param(myri10ge_fw_name, charp, 0644); 2698c2ecf20Sopenharmony_ciMODULE_PARM_DESC(myri10ge_fw_name, "Firmware image name"); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci#define MYRI10GE_MAX_BOARDS 8 2728c2ecf20Sopenharmony_cistatic char *myri10ge_fw_names[MYRI10GE_MAX_BOARDS] = 2738c2ecf20Sopenharmony_ci {[0 ... (MYRI10GE_MAX_BOARDS - 1)] = NULL }; 2748c2ecf20Sopenharmony_cimodule_param_array_named(myri10ge_fw_names, myri10ge_fw_names, charp, NULL, 2758c2ecf20Sopenharmony_ci 0444); 2768c2ecf20Sopenharmony_ciMODULE_PARM_DESC(myri10ge_fw_names, "Firmware image names per board"); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic int myri10ge_ecrc_enable = 1; 2798c2ecf20Sopenharmony_cimodule_param(myri10ge_ecrc_enable, int, 0444); 2808c2ecf20Sopenharmony_ciMODULE_PARM_DESC(myri10ge_ecrc_enable, "Enable Extended CRC on PCI-E"); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic int myri10ge_small_bytes = -1; /* -1 == auto */ 2838c2ecf20Sopenharmony_cimodule_param(myri10ge_small_bytes, int, 0644); 2848c2ecf20Sopenharmony_ciMODULE_PARM_DESC(myri10ge_small_bytes, "Threshold of small packets"); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic int myri10ge_msi = 1; /* enable msi by default */ 2878c2ecf20Sopenharmony_cimodule_param(myri10ge_msi, int, 0644); 2888c2ecf20Sopenharmony_ciMODULE_PARM_DESC(myri10ge_msi, "Enable Message Signalled Interrupts"); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic int myri10ge_intr_coal_delay = 75; 2918c2ecf20Sopenharmony_cimodule_param(myri10ge_intr_coal_delay, int, 0444); 2928c2ecf20Sopenharmony_ciMODULE_PARM_DESC(myri10ge_intr_coal_delay, "Interrupt coalescing delay"); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic int myri10ge_flow_control = 1; 2958c2ecf20Sopenharmony_cimodule_param(myri10ge_flow_control, int, 0444); 2968c2ecf20Sopenharmony_ciMODULE_PARM_DESC(myri10ge_flow_control, "Pause parameter"); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int myri10ge_deassert_wait = 1; 2998c2ecf20Sopenharmony_cimodule_param(myri10ge_deassert_wait, int, 0644); 3008c2ecf20Sopenharmony_ciMODULE_PARM_DESC(myri10ge_deassert_wait, 3018c2ecf20Sopenharmony_ci "Wait when deasserting legacy interrupts"); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic int myri10ge_force_firmware = 0; 3048c2ecf20Sopenharmony_cimodule_param(myri10ge_force_firmware, int, 0444); 3058c2ecf20Sopenharmony_ciMODULE_PARM_DESC(myri10ge_force_firmware, 3068c2ecf20Sopenharmony_ci "Force firmware to assume aligned completions"); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic int myri10ge_initial_mtu = MYRI10GE_MAX_ETHER_MTU - ETH_HLEN; 3098c2ecf20Sopenharmony_cimodule_param(myri10ge_initial_mtu, int, 0444); 3108c2ecf20Sopenharmony_ciMODULE_PARM_DESC(myri10ge_initial_mtu, "Initial MTU"); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic int myri10ge_napi_weight = 64; 3138c2ecf20Sopenharmony_cimodule_param(myri10ge_napi_weight, int, 0444); 3148c2ecf20Sopenharmony_ciMODULE_PARM_DESC(myri10ge_napi_weight, "Set NAPI weight"); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic int myri10ge_watchdog_timeout = 1; 3178c2ecf20Sopenharmony_cimodule_param(myri10ge_watchdog_timeout, int, 0444); 3188c2ecf20Sopenharmony_ciMODULE_PARM_DESC(myri10ge_watchdog_timeout, "Set watchdog timeout"); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic int myri10ge_max_irq_loops = 1048576; 3218c2ecf20Sopenharmony_cimodule_param(myri10ge_max_irq_loops, int, 0444); 3228c2ecf20Sopenharmony_ciMODULE_PARM_DESC(myri10ge_max_irq_loops, 3238c2ecf20Sopenharmony_ci "Set stuck legacy IRQ detection threshold"); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci#define MYRI10GE_MSG_DEFAULT NETIF_MSG_LINK 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic int myri10ge_debug = -1; /* defaults above */ 3288c2ecf20Sopenharmony_cimodule_param(myri10ge_debug, int, 0); 3298c2ecf20Sopenharmony_ciMODULE_PARM_DESC(myri10ge_debug, "Debug level (0=none,...,16=all)"); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic int myri10ge_fill_thresh = 256; 3328c2ecf20Sopenharmony_cimodule_param(myri10ge_fill_thresh, int, 0644); 3338c2ecf20Sopenharmony_ciMODULE_PARM_DESC(myri10ge_fill_thresh, "Number of empty rx slots allowed"); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic int myri10ge_reset_recover = 1; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic int myri10ge_max_slices = 1; 3388c2ecf20Sopenharmony_cimodule_param(myri10ge_max_slices, int, 0444); 3398c2ecf20Sopenharmony_ciMODULE_PARM_DESC(myri10ge_max_slices, "Max tx/rx queues"); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic int myri10ge_rss_hash = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT; 3428c2ecf20Sopenharmony_cimodule_param(myri10ge_rss_hash, int, 0444); 3438c2ecf20Sopenharmony_ciMODULE_PARM_DESC(myri10ge_rss_hash, "Type of RSS hashing to do"); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic int myri10ge_dca = 1; 3468c2ecf20Sopenharmony_cimodule_param(myri10ge_dca, int, 0444); 3478c2ecf20Sopenharmony_ciMODULE_PARM_DESC(myri10ge_dca, "Enable DCA if possible"); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci#define MYRI10GE_FW_OFFSET 1024*1024 3508c2ecf20Sopenharmony_ci#define MYRI10GE_HIGHPART_TO_U32(X) \ 3518c2ecf20Sopenharmony_ci(sizeof (X) == 8) ? ((u32)((u64)(X) >> 32)) : (0) 3528c2ecf20Sopenharmony_ci#define MYRI10GE_LOWPART_TO_U32(X) ((u32)(X)) 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci#define myri10ge_pio_copy(to,from,size) __iowrite64_copy(to,from,size/8) 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic void myri10ge_set_multicast_list(struct net_device *dev); 3578c2ecf20Sopenharmony_cistatic netdev_tx_t myri10ge_sw_tso(struct sk_buff *skb, 3588c2ecf20Sopenharmony_ci struct net_device *dev); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic inline void put_be32(__be32 val, __be32 __iomem * p) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci __raw_writel((__force __u32) val, (__force void __iomem *)p); 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic void myri10ge_get_stats(struct net_device *dev, 3668c2ecf20Sopenharmony_ci struct rtnl_link_stats64 *stats); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic void set_fw_name(struct myri10ge_priv *mgp, char *name, bool allocated) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci if (mgp->fw_name_allocated) 3718c2ecf20Sopenharmony_ci kfree(mgp->fw_name); 3728c2ecf20Sopenharmony_ci mgp->fw_name = name; 3738c2ecf20Sopenharmony_ci mgp->fw_name_allocated = allocated; 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic int 3778c2ecf20Sopenharmony_cimyri10ge_send_cmd(struct myri10ge_priv *mgp, u32 cmd, 3788c2ecf20Sopenharmony_ci struct myri10ge_cmd *data, int atomic) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci struct mcp_cmd *buf; 3818c2ecf20Sopenharmony_ci char buf_bytes[sizeof(*buf) + 8]; 3828c2ecf20Sopenharmony_ci struct mcp_cmd_response *response = mgp->cmd; 3838c2ecf20Sopenharmony_ci char __iomem *cmd_addr = mgp->sram + MXGEFW_ETH_CMD; 3848c2ecf20Sopenharmony_ci u32 dma_low, dma_high, result, value; 3858c2ecf20Sopenharmony_ci int sleep_total = 0; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* ensure buf is aligned to 8 bytes */ 3888c2ecf20Sopenharmony_ci buf = (struct mcp_cmd *)ALIGN((unsigned long)buf_bytes, 8); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci buf->data0 = htonl(data->data0); 3918c2ecf20Sopenharmony_ci buf->data1 = htonl(data->data1); 3928c2ecf20Sopenharmony_ci buf->data2 = htonl(data->data2); 3938c2ecf20Sopenharmony_ci buf->cmd = htonl(cmd); 3948c2ecf20Sopenharmony_ci dma_low = MYRI10GE_LOWPART_TO_U32(mgp->cmd_bus); 3958c2ecf20Sopenharmony_ci dma_high = MYRI10GE_HIGHPART_TO_U32(mgp->cmd_bus); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci buf->response_addr.low = htonl(dma_low); 3988c2ecf20Sopenharmony_ci buf->response_addr.high = htonl(dma_high); 3998c2ecf20Sopenharmony_ci response->result = htonl(MYRI10GE_NO_RESPONSE_RESULT); 4008c2ecf20Sopenharmony_ci mb(); 4018c2ecf20Sopenharmony_ci myri10ge_pio_copy(cmd_addr, buf, sizeof(*buf)); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* wait up to 15ms. Longest command is the DMA benchmark, 4048c2ecf20Sopenharmony_ci * which is capped at 5ms, but runs from a timeout handler 4058c2ecf20Sopenharmony_ci * that runs every 7.8ms. So a 15ms timeout leaves us with 4068c2ecf20Sopenharmony_ci * a 2.2ms margin 4078c2ecf20Sopenharmony_ci */ 4088c2ecf20Sopenharmony_ci if (atomic) { 4098c2ecf20Sopenharmony_ci /* if atomic is set, do not sleep, 4108c2ecf20Sopenharmony_ci * and try to get the completion quickly 4118c2ecf20Sopenharmony_ci * (1ms will be enough for those commands) */ 4128c2ecf20Sopenharmony_ci for (sleep_total = 0; 4138c2ecf20Sopenharmony_ci sleep_total < 1000 && 4148c2ecf20Sopenharmony_ci response->result == htonl(MYRI10GE_NO_RESPONSE_RESULT); 4158c2ecf20Sopenharmony_ci sleep_total += 10) { 4168c2ecf20Sopenharmony_ci udelay(10); 4178c2ecf20Sopenharmony_ci mb(); 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci } else { 4208c2ecf20Sopenharmony_ci /* use msleep for most command */ 4218c2ecf20Sopenharmony_ci for (sleep_total = 0; 4228c2ecf20Sopenharmony_ci sleep_total < 15 && 4238c2ecf20Sopenharmony_ci response->result == htonl(MYRI10GE_NO_RESPONSE_RESULT); 4248c2ecf20Sopenharmony_ci sleep_total++) 4258c2ecf20Sopenharmony_ci msleep(1); 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci result = ntohl(response->result); 4298c2ecf20Sopenharmony_ci value = ntohl(response->data); 4308c2ecf20Sopenharmony_ci if (result != MYRI10GE_NO_RESPONSE_RESULT) { 4318c2ecf20Sopenharmony_ci if (result == 0) { 4328c2ecf20Sopenharmony_ci data->data0 = value; 4338c2ecf20Sopenharmony_ci return 0; 4348c2ecf20Sopenharmony_ci } else if (result == MXGEFW_CMD_UNKNOWN) { 4358c2ecf20Sopenharmony_ci return -ENOSYS; 4368c2ecf20Sopenharmony_ci } else if (result == MXGEFW_CMD_ERROR_UNALIGNED) { 4378c2ecf20Sopenharmony_ci return -E2BIG; 4388c2ecf20Sopenharmony_ci } else if (result == MXGEFW_CMD_ERROR_RANGE && 4398c2ecf20Sopenharmony_ci cmd == MXGEFW_CMD_ENABLE_RSS_QUEUES && 4408c2ecf20Sopenharmony_ci (data-> 4418c2ecf20Sopenharmony_ci data1 & MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES) != 4428c2ecf20Sopenharmony_ci 0) { 4438c2ecf20Sopenharmony_ci return -ERANGE; 4448c2ecf20Sopenharmony_ci } else { 4458c2ecf20Sopenharmony_ci dev_err(&mgp->pdev->dev, 4468c2ecf20Sopenharmony_ci "command %d failed, result = %d\n", 4478c2ecf20Sopenharmony_ci cmd, result); 4488c2ecf20Sopenharmony_ci return -ENXIO; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci dev_err(&mgp->pdev->dev, "command %d timed out, result = %d\n", 4538c2ecf20Sopenharmony_ci cmd, result); 4548c2ecf20Sopenharmony_ci return -EAGAIN; 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci/* 4588c2ecf20Sopenharmony_ci * The eeprom strings on the lanaiX have the format 4598c2ecf20Sopenharmony_ci * SN=x\0 4608c2ecf20Sopenharmony_ci * MAC=x:x:x:x:x:x\0 4618c2ecf20Sopenharmony_ci * PT:ddd mmm xx xx:xx:xx xx\0 4628c2ecf20Sopenharmony_ci * PV:ddd mmm xx xx:xx:xx xx\0 4638c2ecf20Sopenharmony_ci */ 4648c2ecf20Sopenharmony_cistatic int myri10ge_read_mac_addr(struct myri10ge_priv *mgp) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci char *ptr, *limit; 4678c2ecf20Sopenharmony_ci int i; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci ptr = mgp->eeprom_strings; 4708c2ecf20Sopenharmony_ci limit = mgp->eeprom_strings + MYRI10GE_EEPROM_STRINGS_SIZE; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci while (*ptr != '\0' && ptr < limit) { 4738c2ecf20Sopenharmony_ci if (memcmp(ptr, "MAC=", 4) == 0) { 4748c2ecf20Sopenharmony_ci ptr += 4; 4758c2ecf20Sopenharmony_ci mgp->mac_addr_string = ptr; 4768c2ecf20Sopenharmony_ci for (i = 0; i < 6; i++) { 4778c2ecf20Sopenharmony_ci if ((ptr + 2) > limit) 4788c2ecf20Sopenharmony_ci goto abort; 4798c2ecf20Sopenharmony_ci mgp->mac_addr[i] = 4808c2ecf20Sopenharmony_ci simple_strtoul(ptr, &ptr, 16); 4818c2ecf20Sopenharmony_ci ptr += 1; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci if (memcmp(ptr, "PC=", 3) == 0) { 4858c2ecf20Sopenharmony_ci ptr += 3; 4868c2ecf20Sopenharmony_ci mgp->product_code_string = ptr; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci if (memcmp((const void *)ptr, "SN=", 3) == 0) { 4898c2ecf20Sopenharmony_ci ptr += 3; 4908c2ecf20Sopenharmony_ci mgp->serial_number = simple_strtoul(ptr, &ptr, 10); 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci while (ptr < limit && *ptr++) ; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci return 0; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ciabort: 4988c2ecf20Sopenharmony_ci dev_err(&mgp->pdev->dev, "failed to parse eeprom_strings\n"); 4998c2ecf20Sopenharmony_ci return -ENXIO; 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci/* 5038c2ecf20Sopenharmony_ci * Enable or disable periodic RDMAs from the host to make certain 5048c2ecf20Sopenharmony_ci * chipsets resend dropped PCIe messages 5058c2ecf20Sopenharmony_ci */ 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic void myri10ge_dummy_rdma(struct myri10ge_priv *mgp, int enable) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci char __iomem *submit; 5108c2ecf20Sopenharmony_ci __be32 buf[16] __attribute__ ((__aligned__(8))); 5118c2ecf20Sopenharmony_ci u32 dma_low, dma_high; 5128c2ecf20Sopenharmony_ci int i; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci /* clear confirmation addr */ 5158c2ecf20Sopenharmony_ci mgp->cmd->data = 0; 5168c2ecf20Sopenharmony_ci mb(); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* send a rdma command to the PCIe engine, and wait for the 5198c2ecf20Sopenharmony_ci * response in the confirmation address. The firmware should 5208c2ecf20Sopenharmony_ci * write a -1 there to indicate it is alive and well 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_ci dma_low = MYRI10GE_LOWPART_TO_U32(mgp->cmd_bus); 5238c2ecf20Sopenharmony_ci dma_high = MYRI10GE_HIGHPART_TO_U32(mgp->cmd_bus); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci buf[0] = htonl(dma_high); /* confirm addr MSW */ 5268c2ecf20Sopenharmony_ci buf[1] = htonl(dma_low); /* confirm addr LSW */ 5278c2ecf20Sopenharmony_ci buf[2] = MYRI10GE_NO_CONFIRM_DATA; /* confirm data */ 5288c2ecf20Sopenharmony_ci buf[3] = htonl(dma_high); /* dummy addr MSW */ 5298c2ecf20Sopenharmony_ci buf[4] = htonl(dma_low); /* dummy addr LSW */ 5308c2ecf20Sopenharmony_ci buf[5] = htonl(enable); /* enable? */ 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci submit = mgp->sram + MXGEFW_BOOT_DUMMY_RDMA; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci myri10ge_pio_copy(submit, &buf, sizeof(buf)); 5358c2ecf20Sopenharmony_ci for (i = 0; mgp->cmd->data != MYRI10GE_NO_CONFIRM_DATA && i < 20; i++) 5368c2ecf20Sopenharmony_ci msleep(1); 5378c2ecf20Sopenharmony_ci if (mgp->cmd->data != MYRI10GE_NO_CONFIRM_DATA) 5388c2ecf20Sopenharmony_ci dev_err(&mgp->pdev->dev, "dummy rdma %s failed\n", 5398c2ecf20Sopenharmony_ci (enable ? "enable" : "disable")); 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic int 5438c2ecf20Sopenharmony_cimyri10ge_validate_firmware(struct myri10ge_priv *mgp, 5448c2ecf20Sopenharmony_ci struct mcp_gen_header *hdr) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci struct device *dev = &mgp->pdev->dev; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci /* check firmware type */ 5498c2ecf20Sopenharmony_ci if (ntohl(hdr->mcp_type) != MCP_TYPE_ETH) { 5508c2ecf20Sopenharmony_ci dev_err(dev, "Bad firmware type: 0x%x\n", ntohl(hdr->mcp_type)); 5518c2ecf20Sopenharmony_ci return -EINVAL; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci /* save firmware version for ethtool */ 5558c2ecf20Sopenharmony_ci strncpy(mgp->fw_version, hdr->version, sizeof(mgp->fw_version)); 5568c2ecf20Sopenharmony_ci mgp->fw_version[sizeof(mgp->fw_version) - 1] = '\0'; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci sscanf(mgp->fw_version, "%d.%d.%d", &mgp->fw_ver_major, 5598c2ecf20Sopenharmony_ci &mgp->fw_ver_minor, &mgp->fw_ver_tiny); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci if (!(mgp->fw_ver_major == MXGEFW_VERSION_MAJOR && 5628c2ecf20Sopenharmony_ci mgp->fw_ver_minor == MXGEFW_VERSION_MINOR)) { 5638c2ecf20Sopenharmony_ci dev_err(dev, "Found firmware version %s\n", mgp->fw_version); 5648c2ecf20Sopenharmony_ci dev_err(dev, "Driver needs %d.%d\n", MXGEFW_VERSION_MAJOR, 5658c2ecf20Sopenharmony_ci MXGEFW_VERSION_MINOR); 5668c2ecf20Sopenharmony_ci return -EINVAL; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci return 0; 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_cistatic int myri10ge_load_hotplug_firmware(struct myri10ge_priv *mgp, u32 * size) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci unsigned crc, reread_crc; 5748c2ecf20Sopenharmony_ci const struct firmware *fw; 5758c2ecf20Sopenharmony_ci struct device *dev = &mgp->pdev->dev; 5768c2ecf20Sopenharmony_ci unsigned char *fw_readback; 5778c2ecf20Sopenharmony_ci struct mcp_gen_header *hdr; 5788c2ecf20Sopenharmony_ci size_t hdr_offset; 5798c2ecf20Sopenharmony_ci int status; 5808c2ecf20Sopenharmony_ci unsigned i; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci if ((status = request_firmware(&fw, mgp->fw_name, dev)) < 0) { 5838c2ecf20Sopenharmony_ci dev_err(dev, "Unable to load %s firmware image via hotplug\n", 5848c2ecf20Sopenharmony_ci mgp->fw_name); 5858c2ecf20Sopenharmony_ci status = -EINVAL; 5868c2ecf20Sopenharmony_ci goto abort_with_nothing; 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci /* check size */ 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci if (fw->size >= mgp->sram_size - MYRI10GE_FW_OFFSET || 5928c2ecf20Sopenharmony_ci fw->size < MCP_HEADER_PTR_OFFSET + 4) { 5938c2ecf20Sopenharmony_ci dev_err(dev, "Firmware size invalid:%d\n", (int)fw->size); 5948c2ecf20Sopenharmony_ci status = -EINVAL; 5958c2ecf20Sopenharmony_ci goto abort_with_fw; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci /* check id */ 5998c2ecf20Sopenharmony_ci hdr_offset = ntohl(*(__be32 *) (fw->data + MCP_HEADER_PTR_OFFSET)); 6008c2ecf20Sopenharmony_ci if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw->size) { 6018c2ecf20Sopenharmony_ci dev_err(dev, "Bad firmware file\n"); 6028c2ecf20Sopenharmony_ci status = -EINVAL; 6038c2ecf20Sopenharmony_ci goto abort_with_fw; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci hdr = (void *)(fw->data + hdr_offset); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci status = myri10ge_validate_firmware(mgp, hdr); 6088c2ecf20Sopenharmony_ci if (status != 0) 6098c2ecf20Sopenharmony_ci goto abort_with_fw; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci crc = crc32(~0, fw->data, fw->size); 6128c2ecf20Sopenharmony_ci for (i = 0; i < fw->size; i += 256) { 6138c2ecf20Sopenharmony_ci myri10ge_pio_copy(mgp->sram + MYRI10GE_FW_OFFSET + i, 6148c2ecf20Sopenharmony_ci fw->data + i, 6158c2ecf20Sopenharmony_ci min(256U, (unsigned)(fw->size - i))); 6168c2ecf20Sopenharmony_ci mb(); 6178c2ecf20Sopenharmony_ci readb(mgp->sram); 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci fw_readback = vmalloc(fw->size); 6208c2ecf20Sopenharmony_ci if (!fw_readback) { 6218c2ecf20Sopenharmony_ci status = -ENOMEM; 6228c2ecf20Sopenharmony_ci goto abort_with_fw; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci /* corruption checking is good for parity recovery and buggy chipset */ 6258c2ecf20Sopenharmony_ci memcpy_fromio(fw_readback, mgp->sram + MYRI10GE_FW_OFFSET, fw->size); 6268c2ecf20Sopenharmony_ci reread_crc = crc32(~0, fw_readback, fw->size); 6278c2ecf20Sopenharmony_ci vfree(fw_readback); 6288c2ecf20Sopenharmony_ci if (crc != reread_crc) { 6298c2ecf20Sopenharmony_ci dev_err(dev, "CRC failed(fw-len=%u), got 0x%x (expect 0x%x)\n", 6308c2ecf20Sopenharmony_ci (unsigned)fw->size, reread_crc, crc); 6318c2ecf20Sopenharmony_ci status = -EIO; 6328c2ecf20Sopenharmony_ci goto abort_with_fw; 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci *size = (u32) fw->size; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ciabort_with_fw: 6378c2ecf20Sopenharmony_ci release_firmware(fw); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ciabort_with_nothing: 6408c2ecf20Sopenharmony_ci return status; 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_cistatic int myri10ge_adopt_running_firmware(struct myri10ge_priv *mgp) 6448c2ecf20Sopenharmony_ci{ 6458c2ecf20Sopenharmony_ci struct mcp_gen_header *hdr; 6468c2ecf20Sopenharmony_ci struct device *dev = &mgp->pdev->dev; 6478c2ecf20Sopenharmony_ci const size_t bytes = sizeof(struct mcp_gen_header); 6488c2ecf20Sopenharmony_ci size_t hdr_offset; 6498c2ecf20Sopenharmony_ci int status; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci /* find running firmware header */ 6528c2ecf20Sopenharmony_ci hdr_offset = swab32(readl(mgp->sram + MCP_HEADER_PTR_OFFSET)); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > mgp->sram_size) { 6558c2ecf20Sopenharmony_ci dev_err(dev, "Running firmware has bad header offset (%d)\n", 6568c2ecf20Sopenharmony_ci (int)hdr_offset); 6578c2ecf20Sopenharmony_ci return -EIO; 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci /* copy header of running firmware from SRAM to host memory to 6618c2ecf20Sopenharmony_ci * validate firmware */ 6628c2ecf20Sopenharmony_ci hdr = kmalloc(bytes, GFP_KERNEL); 6638c2ecf20Sopenharmony_ci if (hdr == NULL) 6648c2ecf20Sopenharmony_ci return -ENOMEM; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci memcpy_fromio(hdr, mgp->sram + hdr_offset, bytes); 6678c2ecf20Sopenharmony_ci status = myri10ge_validate_firmware(mgp, hdr); 6688c2ecf20Sopenharmony_ci kfree(hdr); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci /* check to see if adopted firmware has bug where adopting 6718c2ecf20Sopenharmony_ci * it will cause broadcasts to be filtered unless the NIC 6728c2ecf20Sopenharmony_ci * is kept in ALLMULTI mode */ 6738c2ecf20Sopenharmony_ci if (mgp->fw_ver_major == 1 && mgp->fw_ver_minor == 4 && 6748c2ecf20Sopenharmony_ci mgp->fw_ver_tiny >= 4 && mgp->fw_ver_tiny <= 11) { 6758c2ecf20Sopenharmony_ci mgp->adopted_rx_filter_bug = 1; 6768c2ecf20Sopenharmony_ci dev_warn(dev, "Adopting fw %d.%d.%d: " 6778c2ecf20Sopenharmony_ci "working around rx filter bug\n", 6788c2ecf20Sopenharmony_ci mgp->fw_ver_major, mgp->fw_ver_minor, 6798c2ecf20Sopenharmony_ci mgp->fw_ver_tiny); 6808c2ecf20Sopenharmony_ci } 6818c2ecf20Sopenharmony_ci return status; 6828c2ecf20Sopenharmony_ci} 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_cistatic int myri10ge_get_firmware_capabilities(struct myri10ge_priv *mgp) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci struct myri10ge_cmd cmd; 6878c2ecf20Sopenharmony_ci int status; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci /* probe for IPv6 TSO support */ 6908c2ecf20Sopenharmony_ci mgp->features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_TSO; 6918c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_MAX_TSO6_HDR_SIZE, 6928c2ecf20Sopenharmony_ci &cmd, 0); 6938c2ecf20Sopenharmony_ci if (status == 0) { 6948c2ecf20Sopenharmony_ci mgp->max_tso6 = cmd.data0; 6958c2ecf20Sopenharmony_ci mgp->features |= NETIF_F_TSO6; 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd, 0); 6998c2ecf20Sopenharmony_ci if (status != 0) { 7008c2ecf20Sopenharmony_ci dev_err(&mgp->pdev->dev, 7018c2ecf20Sopenharmony_ci "failed MXGEFW_CMD_GET_RX_RING_SIZE\n"); 7028c2ecf20Sopenharmony_ci return -ENXIO; 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci mgp->max_intr_slots = 2 * (cmd.data0 / sizeof(struct mcp_dma_addr)); 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci return 0; 7088c2ecf20Sopenharmony_ci} 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_cistatic int myri10ge_load_firmware(struct myri10ge_priv *mgp, int adopt) 7118c2ecf20Sopenharmony_ci{ 7128c2ecf20Sopenharmony_ci char __iomem *submit; 7138c2ecf20Sopenharmony_ci __be32 buf[16] __attribute__ ((__aligned__(8))); 7148c2ecf20Sopenharmony_ci u32 dma_low, dma_high, size; 7158c2ecf20Sopenharmony_ci int status, i; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci size = 0; 7188c2ecf20Sopenharmony_ci status = myri10ge_load_hotplug_firmware(mgp, &size); 7198c2ecf20Sopenharmony_ci if (status) { 7208c2ecf20Sopenharmony_ci if (!adopt) 7218c2ecf20Sopenharmony_ci return status; 7228c2ecf20Sopenharmony_ci dev_warn(&mgp->pdev->dev, "hotplug firmware loading failed\n"); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci /* Do not attempt to adopt firmware if there 7258c2ecf20Sopenharmony_ci * was a bad crc */ 7268c2ecf20Sopenharmony_ci if (status == -EIO) 7278c2ecf20Sopenharmony_ci return status; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci status = myri10ge_adopt_running_firmware(mgp); 7308c2ecf20Sopenharmony_ci if (status != 0) { 7318c2ecf20Sopenharmony_ci dev_err(&mgp->pdev->dev, 7328c2ecf20Sopenharmony_ci "failed to adopt running firmware\n"); 7338c2ecf20Sopenharmony_ci return status; 7348c2ecf20Sopenharmony_ci } 7358c2ecf20Sopenharmony_ci dev_info(&mgp->pdev->dev, 7368c2ecf20Sopenharmony_ci "Successfully adopted running firmware\n"); 7378c2ecf20Sopenharmony_ci if (mgp->tx_boundary == 4096) { 7388c2ecf20Sopenharmony_ci dev_warn(&mgp->pdev->dev, 7398c2ecf20Sopenharmony_ci "Using firmware currently running on NIC" 7408c2ecf20Sopenharmony_ci ". For optimal\n"); 7418c2ecf20Sopenharmony_ci dev_warn(&mgp->pdev->dev, 7428c2ecf20Sopenharmony_ci "performance consider loading optimized " 7438c2ecf20Sopenharmony_ci "firmware\n"); 7448c2ecf20Sopenharmony_ci dev_warn(&mgp->pdev->dev, "via hotplug\n"); 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci set_fw_name(mgp, "adopted", false); 7488c2ecf20Sopenharmony_ci mgp->tx_boundary = 2048; 7498c2ecf20Sopenharmony_ci myri10ge_dummy_rdma(mgp, 1); 7508c2ecf20Sopenharmony_ci status = myri10ge_get_firmware_capabilities(mgp); 7518c2ecf20Sopenharmony_ci return status; 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci /* clear confirmation addr */ 7558c2ecf20Sopenharmony_ci mgp->cmd->data = 0; 7568c2ecf20Sopenharmony_ci mb(); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci /* send a reload command to the bootstrap MCP, and wait for the 7598c2ecf20Sopenharmony_ci * response in the confirmation address. The firmware should 7608c2ecf20Sopenharmony_ci * write a -1 there to indicate it is alive and well 7618c2ecf20Sopenharmony_ci */ 7628c2ecf20Sopenharmony_ci dma_low = MYRI10GE_LOWPART_TO_U32(mgp->cmd_bus); 7638c2ecf20Sopenharmony_ci dma_high = MYRI10GE_HIGHPART_TO_U32(mgp->cmd_bus); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci buf[0] = htonl(dma_high); /* confirm addr MSW */ 7668c2ecf20Sopenharmony_ci buf[1] = htonl(dma_low); /* confirm addr LSW */ 7678c2ecf20Sopenharmony_ci buf[2] = MYRI10GE_NO_CONFIRM_DATA; /* confirm data */ 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci /* FIX: All newest firmware should un-protect the bottom of 7708c2ecf20Sopenharmony_ci * the sram before handoff. However, the very first interfaces 7718c2ecf20Sopenharmony_ci * do not. Therefore the handoff copy must skip the first 8 bytes 7728c2ecf20Sopenharmony_ci */ 7738c2ecf20Sopenharmony_ci buf[3] = htonl(MYRI10GE_FW_OFFSET + 8); /* where the code starts */ 7748c2ecf20Sopenharmony_ci buf[4] = htonl(size - 8); /* length of code */ 7758c2ecf20Sopenharmony_ci buf[5] = htonl(8); /* where to copy to */ 7768c2ecf20Sopenharmony_ci buf[6] = htonl(0); /* where to jump to */ 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci submit = mgp->sram + MXGEFW_BOOT_HANDOFF; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci myri10ge_pio_copy(submit, &buf, sizeof(buf)); 7818c2ecf20Sopenharmony_ci mb(); 7828c2ecf20Sopenharmony_ci msleep(1); 7838c2ecf20Sopenharmony_ci mb(); 7848c2ecf20Sopenharmony_ci i = 0; 7858c2ecf20Sopenharmony_ci while (mgp->cmd->data != MYRI10GE_NO_CONFIRM_DATA && i < 9) { 7868c2ecf20Sopenharmony_ci msleep(1 << i); 7878c2ecf20Sopenharmony_ci i++; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci if (mgp->cmd->data != MYRI10GE_NO_CONFIRM_DATA) { 7908c2ecf20Sopenharmony_ci dev_err(&mgp->pdev->dev, "handoff failed\n"); 7918c2ecf20Sopenharmony_ci return -ENXIO; 7928c2ecf20Sopenharmony_ci } 7938c2ecf20Sopenharmony_ci myri10ge_dummy_rdma(mgp, 1); 7948c2ecf20Sopenharmony_ci status = myri10ge_get_firmware_capabilities(mgp); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci return status; 7978c2ecf20Sopenharmony_ci} 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_cistatic int myri10ge_update_mac_address(struct myri10ge_priv *mgp, u8 * addr) 8008c2ecf20Sopenharmony_ci{ 8018c2ecf20Sopenharmony_ci struct myri10ge_cmd cmd; 8028c2ecf20Sopenharmony_ci int status; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci cmd.data0 = ((addr[0] << 24) | (addr[1] << 16) 8058c2ecf20Sopenharmony_ci | (addr[2] << 8) | addr[3]); 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci cmd.data1 = ((addr[4] << 8) | (addr[5])); 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, MXGEFW_SET_MAC_ADDRESS, &cmd, 0); 8108c2ecf20Sopenharmony_ci return status; 8118c2ecf20Sopenharmony_ci} 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_cistatic int myri10ge_change_pause(struct myri10ge_priv *mgp, int pause) 8148c2ecf20Sopenharmony_ci{ 8158c2ecf20Sopenharmony_ci struct myri10ge_cmd cmd; 8168c2ecf20Sopenharmony_ci int status, ctl; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci ctl = pause ? MXGEFW_ENABLE_FLOW_CONTROL : MXGEFW_DISABLE_FLOW_CONTROL; 8198c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, ctl, &cmd, 0); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci if (status) { 8228c2ecf20Sopenharmony_ci netdev_err(mgp->dev, "Failed to set flow control mode\n"); 8238c2ecf20Sopenharmony_ci return status; 8248c2ecf20Sopenharmony_ci } 8258c2ecf20Sopenharmony_ci mgp->pause = pause; 8268c2ecf20Sopenharmony_ci return 0; 8278c2ecf20Sopenharmony_ci} 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_cistatic void 8308c2ecf20Sopenharmony_cimyri10ge_change_promisc(struct myri10ge_priv *mgp, int promisc, int atomic) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci struct myri10ge_cmd cmd; 8338c2ecf20Sopenharmony_ci int status, ctl; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci ctl = promisc ? MXGEFW_ENABLE_PROMISC : MXGEFW_DISABLE_PROMISC; 8368c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, ctl, &cmd, atomic); 8378c2ecf20Sopenharmony_ci if (status) 8388c2ecf20Sopenharmony_ci netdev_err(mgp->dev, "Failed to set promisc mode\n"); 8398c2ecf20Sopenharmony_ci} 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_cistatic int myri10ge_dma_test(struct myri10ge_priv *mgp, int test_type) 8428c2ecf20Sopenharmony_ci{ 8438c2ecf20Sopenharmony_ci struct myri10ge_cmd cmd; 8448c2ecf20Sopenharmony_ci int status; 8458c2ecf20Sopenharmony_ci u32 len; 8468c2ecf20Sopenharmony_ci struct page *dmatest_page; 8478c2ecf20Sopenharmony_ci dma_addr_t dmatest_bus; 8488c2ecf20Sopenharmony_ci char *test = " "; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci dmatest_page = alloc_page(GFP_KERNEL); 8518c2ecf20Sopenharmony_ci if (!dmatest_page) 8528c2ecf20Sopenharmony_ci return -ENOMEM; 8538c2ecf20Sopenharmony_ci dmatest_bus = pci_map_page(mgp->pdev, dmatest_page, 0, PAGE_SIZE, 8548c2ecf20Sopenharmony_ci DMA_BIDIRECTIONAL); 8558c2ecf20Sopenharmony_ci if (unlikely(pci_dma_mapping_error(mgp->pdev, dmatest_bus))) { 8568c2ecf20Sopenharmony_ci __free_page(dmatest_page); 8578c2ecf20Sopenharmony_ci return -ENOMEM; 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci /* Run a small DMA test. 8618c2ecf20Sopenharmony_ci * The magic multipliers to the length tell the firmware 8628c2ecf20Sopenharmony_ci * to do DMA read, write, or read+write tests. The 8638c2ecf20Sopenharmony_ci * results are returned in cmd.data0. The upper 16 8648c2ecf20Sopenharmony_ci * bits or the return is the number of transfers completed. 8658c2ecf20Sopenharmony_ci * The lower 16 bits is the time in 0.5us ticks that the 8668c2ecf20Sopenharmony_ci * transfers took to complete. 8678c2ecf20Sopenharmony_ci */ 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci len = mgp->tx_boundary; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci cmd.data0 = MYRI10GE_LOWPART_TO_U32(dmatest_bus); 8728c2ecf20Sopenharmony_ci cmd.data1 = MYRI10GE_HIGHPART_TO_U32(dmatest_bus); 8738c2ecf20Sopenharmony_ci cmd.data2 = len * 0x10000; 8748c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, test_type, &cmd, 0); 8758c2ecf20Sopenharmony_ci if (status != 0) { 8768c2ecf20Sopenharmony_ci test = "read"; 8778c2ecf20Sopenharmony_ci goto abort; 8788c2ecf20Sopenharmony_ci } 8798c2ecf20Sopenharmony_ci mgp->read_dma = ((cmd.data0 >> 16) * len * 2) / (cmd.data0 & 0xffff); 8808c2ecf20Sopenharmony_ci cmd.data0 = MYRI10GE_LOWPART_TO_U32(dmatest_bus); 8818c2ecf20Sopenharmony_ci cmd.data1 = MYRI10GE_HIGHPART_TO_U32(dmatest_bus); 8828c2ecf20Sopenharmony_ci cmd.data2 = len * 0x1; 8838c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, test_type, &cmd, 0); 8848c2ecf20Sopenharmony_ci if (status != 0) { 8858c2ecf20Sopenharmony_ci test = "write"; 8868c2ecf20Sopenharmony_ci goto abort; 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci mgp->write_dma = ((cmd.data0 >> 16) * len * 2) / (cmd.data0 & 0xffff); 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci cmd.data0 = MYRI10GE_LOWPART_TO_U32(dmatest_bus); 8918c2ecf20Sopenharmony_ci cmd.data1 = MYRI10GE_HIGHPART_TO_U32(dmatest_bus); 8928c2ecf20Sopenharmony_ci cmd.data2 = len * 0x10001; 8938c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, test_type, &cmd, 0); 8948c2ecf20Sopenharmony_ci if (status != 0) { 8958c2ecf20Sopenharmony_ci test = "read/write"; 8968c2ecf20Sopenharmony_ci goto abort; 8978c2ecf20Sopenharmony_ci } 8988c2ecf20Sopenharmony_ci mgp->read_write_dma = ((cmd.data0 >> 16) * len * 2 * 2) / 8998c2ecf20Sopenharmony_ci (cmd.data0 & 0xffff); 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ciabort: 9028c2ecf20Sopenharmony_ci pci_unmap_page(mgp->pdev, dmatest_bus, PAGE_SIZE, DMA_BIDIRECTIONAL); 9038c2ecf20Sopenharmony_ci put_page(dmatest_page); 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST) 9068c2ecf20Sopenharmony_ci dev_warn(&mgp->pdev->dev, "DMA %s benchmark failed: %d\n", 9078c2ecf20Sopenharmony_ci test, status); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci return status; 9108c2ecf20Sopenharmony_ci} 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_cistatic int myri10ge_reset(struct myri10ge_priv *mgp) 9138c2ecf20Sopenharmony_ci{ 9148c2ecf20Sopenharmony_ci struct myri10ge_cmd cmd; 9158c2ecf20Sopenharmony_ci struct myri10ge_slice_state *ss; 9168c2ecf20Sopenharmony_ci int i, status; 9178c2ecf20Sopenharmony_ci size_t bytes; 9188c2ecf20Sopenharmony_ci#ifdef CONFIG_MYRI10GE_DCA 9198c2ecf20Sopenharmony_ci unsigned long dca_tag_off; 9208c2ecf20Sopenharmony_ci#endif 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci /* try to send a reset command to the card to see if it 9238c2ecf20Sopenharmony_ci * is alive */ 9248c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 9258c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, MXGEFW_CMD_RESET, &cmd, 0); 9268c2ecf20Sopenharmony_ci if (status != 0) { 9278c2ecf20Sopenharmony_ci dev_err(&mgp->pdev->dev, "failed reset\n"); 9288c2ecf20Sopenharmony_ci return -ENXIO; 9298c2ecf20Sopenharmony_ci } 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci (void)myri10ge_dma_test(mgp, MXGEFW_DMA_TEST); 9328c2ecf20Sopenharmony_ci /* 9338c2ecf20Sopenharmony_ci * Use non-ndis mcp_slot (eg, 4 bytes total, 9348c2ecf20Sopenharmony_ci * no toeplitz hash value returned. Older firmware will 9358c2ecf20Sopenharmony_ci * not understand this command, but will use the correct 9368c2ecf20Sopenharmony_ci * sized mcp_slot, so we ignore error returns 9378c2ecf20Sopenharmony_ci */ 9388c2ecf20Sopenharmony_ci cmd.data0 = MXGEFW_RSS_MCP_SLOT_TYPE_MIN; 9398c2ecf20Sopenharmony_ci (void)myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_RSS_MCP_SLOT_TYPE, &cmd, 0); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci /* Now exchange information about interrupts */ 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci bytes = mgp->max_intr_slots * sizeof(*mgp->ss[0].rx_done.entry); 9448c2ecf20Sopenharmony_ci cmd.data0 = (u32) bytes; 9458c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd, 0); 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci /* 9488c2ecf20Sopenharmony_ci * Even though we already know how many slices are supported 9498c2ecf20Sopenharmony_ci * via myri10ge_probe_slices() MXGEFW_CMD_GET_MAX_RSS_QUEUES 9508c2ecf20Sopenharmony_ci * has magic side effects, and must be called after a reset. 9518c2ecf20Sopenharmony_ci * It must be called prior to calling any RSS related cmds, 9528c2ecf20Sopenharmony_ci * including assigning an interrupt queue for anything but 9538c2ecf20Sopenharmony_ci * slice 0. It must also be called *after* 9548c2ecf20Sopenharmony_ci * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by 9558c2ecf20Sopenharmony_ci * the firmware to compute offsets. 9568c2ecf20Sopenharmony_ci */ 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci if (mgp->num_slices > 1) { 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci /* ask the maximum number of slices it supports */ 9618c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_MAX_RSS_QUEUES, 9628c2ecf20Sopenharmony_ci &cmd, 0); 9638c2ecf20Sopenharmony_ci if (status != 0) { 9648c2ecf20Sopenharmony_ci dev_err(&mgp->pdev->dev, 9658c2ecf20Sopenharmony_ci "failed to get number of slices\n"); 9668c2ecf20Sopenharmony_ci } 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci /* 9698c2ecf20Sopenharmony_ci * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior 9708c2ecf20Sopenharmony_ci * to setting up the interrupt queue DMA 9718c2ecf20Sopenharmony_ci */ 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci cmd.data0 = mgp->num_slices; 9748c2ecf20Sopenharmony_ci cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE; 9758c2ecf20Sopenharmony_ci if (mgp->dev->real_num_tx_queues > 1) 9768c2ecf20Sopenharmony_ci cmd.data1 |= MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES; 9778c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, MXGEFW_CMD_ENABLE_RSS_QUEUES, 9788c2ecf20Sopenharmony_ci &cmd, 0); 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci /* Firmware older than 1.4.32 only supports multiple 9818c2ecf20Sopenharmony_ci * RX queues, so if we get an error, first retry using a 9828c2ecf20Sopenharmony_ci * single TX queue before giving up */ 9838c2ecf20Sopenharmony_ci if (status != 0 && mgp->dev->real_num_tx_queues > 1) { 9848c2ecf20Sopenharmony_ci netif_set_real_num_tx_queues(mgp->dev, 1); 9858c2ecf20Sopenharmony_ci cmd.data0 = mgp->num_slices; 9868c2ecf20Sopenharmony_ci cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE; 9878c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, 9888c2ecf20Sopenharmony_ci MXGEFW_CMD_ENABLE_RSS_QUEUES, 9898c2ecf20Sopenharmony_ci &cmd, 0); 9908c2ecf20Sopenharmony_ci } 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci if (status != 0) { 9938c2ecf20Sopenharmony_ci dev_err(&mgp->pdev->dev, 9948c2ecf20Sopenharmony_ci "failed to set number of slices\n"); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci return status; 9978c2ecf20Sopenharmony_ci } 9988c2ecf20Sopenharmony_ci } 9998c2ecf20Sopenharmony_ci for (i = 0; i < mgp->num_slices; i++) { 10008c2ecf20Sopenharmony_ci ss = &mgp->ss[i]; 10018c2ecf20Sopenharmony_ci cmd.data0 = MYRI10GE_LOWPART_TO_U32(ss->rx_done.bus); 10028c2ecf20Sopenharmony_ci cmd.data1 = MYRI10GE_HIGHPART_TO_U32(ss->rx_done.bus); 10038c2ecf20Sopenharmony_ci cmd.data2 = i; 10048c2ecf20Sopenharmony_ci status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_INTRQ_DMA, 10058c2ecf20Sopenharmony_ci &cmd, 0); 10068c2ecf20Sopenharmony_ci } 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci status |= 10098c2ecf20Sopenharmony_ci myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd, 0); 10108c2ecf20Sopenharmony_ci for (i = 0; i < mgp->num_slices; i++) { 10118c2ecf20Sopenharmony_ci ss = &mgp->ss[i]; 10128c2ecf20Sopenharmony_ci ss->irq_claim = 10138c2ecf20Sopenharmony_ci (__iomem __be32 *) (mgp->sram + cmd.data0 + 8 * i); 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET, 10168c2ecf20Sopenharmony_ci &cmd, 0); 10178c2ecf20Sopenharmony_ci mgp->irq_deassert = (__iomem __be32 *) (mgp->sram + cmd.data0); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci status |= myri10ge_send_cmd 10208c2ecf20Sopenharmony_ci (mgp, MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd, 0); 10218c2ecf20Sopenharmony_ci mgp->intr_coal_delay_ptr = (__iomem __be32 *) (mgp->sram + cmd.data0); 10228c2ecf20Sopenharmony_ci if (status != 0) { 10238c2ecf20Sopenharmony_ci dev_err(&mgp->pdev->dev, "failed set interrupt parameters\n"); 10248c2ecf20Sopenharmony_ci return status; 10258c2ecf20Sopenharmony_ci } 10268c2ecf20Sopenharmony_ci put_be32(htonl(mgp->intr_coal_delay), mgp->intr_coal_delay_ptr); 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci#ifdef CONFIG_MYRI10GE_DCA 10298c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_DCA_OFFSET, &cmd, 0); 10308c2ecf20Sopenharmony_ci dca_tag_off = cmd.data0; 10318c2ecf20Sopenharmony_ci for (i = 0; i < mgp->num_slices; i++) { 10328c2ecf20Sopenharmony_ci ss = &mgp->ss[i]; 10338c2ecf20Sopenharmony_ci if (status == 0) { 10348c2ecf20Sopenharmony_ci ss->dca_tag = (__iomem __be32 *) 10358c2ecf20Sopenharmony_ci (mgp->sram + dca_tag_off + 4 * i); 10368c2ecf20Sopenharmony_ci } else { 10378c2ecf20Sopenharmony_ci ss->dca_tag = NULL; 10388c2ecf20Sopenharmony_ci } 10398c2ecf20Sopenharmony_ci } 10408c2ecf20Sopenharmony_ci#endif /* CONFIG_MYRI10GE_DCA */ 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci /* reset mcp/driver shared state back to 0 */ 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci mgp->link_changes = 0; 10458c2ecf20Sopenharmony_ci for (i = 0; i < mgp->num_slices; i++) { 10468c2ecf20Sopenharmony_ci ss = &mgp->ss[i]; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci memset(ss->rx_done.entry, 0, bytes); 10498c2ecf20Sopenharmony_ci ss->tx.req = 0; 10508c2ecf20Sopenharmony_ci ss->tx.done = 0; 10518c2ecf20Sopenharmony_ci ss->tx.pkt_start = 0; 10528c2ecf20Sopenharmony_ci ss->tx.pkt_done = 0; 10538c2ecf20Sopenharmony_ci ss->rx_big.cnt = 0; 10548c2ecf20Sopenharmony_ci ss->rx_small.cnt = 0; 10558c2ecf20Sopenharmony_ci ss->rx_done.idx = 0; 10568c2ecf20Sopenharmony_ci ss->rx_done.cnt = 0; 10578c2ecf20Sopenharmony_ci ss->tx.wake_queue = 0; 10588c2ecf20Sopenharmony_ci ss->tx.stop_queue = 0; 10598c2ecf20Sopenharmony_ci } 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci status = myri10ge_update_mac_address(mgp, mgp->dev->dev_addr); 10628c2ecf20Sopenharmony_ci myri10ge_change_pause(mgp, mgp->pause); 10638c2ecf20Sopenharmony_ci myri10ge_set_multicast_list(mgp->dev); 10648c2ecf20Sopenharmony_ci return status; 10658c2ecf20Sopenharmony_ci} 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci#ifdef CONFIG_MYRI10GE_DCA 10688c2ecf20Sopenharmony_cistatic int myri10ge_toggle_relaxed(struct pci_dev *pdev, int on) 10698c2ecf20Sopenharmony_ci{ 10708c2ecf20Sopenharmony_ci int ret; 10718c2ecf20Sopenharmony_ci u16 ctl; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci pcie_capability_read_word(pdev, PCI_EXP_DEVCTL, &ctl); 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci ret = (ctl & PCI_EXP_DEVCTL_RELAX_EN) >> 4; 10768c2ecf20Sopenharmony_ci if (ret != on) { 10778c2ecf20Sopenharmony_ci ctl &= ~PCI_EXP_DEVCTL_RELAX_EN; 10788c2ecf20Sopenharmony_ci ctl |= (on << 4); 10798c2ecf20Sopenharmony_ci pcie_capability_write_word(pdev, PCI_EXP_DEVCTL, ctl); 10808c2ecf20Sopenharmony_ci } 10818c2ecf20Sopenharmony_ci return ret; 10828c2ecf20Sopenharmony_ci} 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_cistatic void 10858c2ecf20Sopenharmony_cimyri10ge_write_dca(struct myri10ge_slice_state *ss, int cpu, int tag) 10868c2ecf20Sopenharmony_ci{ 10878c2ecf20Sopenharmony_ci ss->cached_dca_tag = tag; 10888c2ecf20Sopenharmony_ci put_be32(htonl(tag), ss->dca_tag); 10898c2ecf20Sopenharmony_ci} 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_cistatic inline void myri10ge_update_dca(struct myri10ge_slice_state *ss) 10928c2ecf20Sopenharmony_ci{ 10938c2ecf20Sopenharmony_ci int cpu = get_cpu(); 10948c2ecf20Sopenharmony_ci int tag; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci if (cpu != ss->cpu) { 10978c2ecf20Sopenharmony_ci tag = dca3_get_tag(&ss->mgp->pdev->dev, cpu); 10988c2ecf20Sopenharmony_ci if (ss->cached_dca_tag != tag) 10998c2ecf20Sopenharmony_ci myri10ge_write_dca(ss, cpu, tag); 11008c2ecf20Sopenharmony_ci ss->cpu = cpu; 11018c2ecf20Sopenharmony_ci } 11028c2ecf20Sopenharmony_ci put_cpu(); 11038c2ecf20Sopenharmony_ci} 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_cistatic void myri10ge_setup_dca(struct myri10ge_priv *mgp) 11068c2ecf20Sopenharmony_ci{ 11078c2ecf20Sopenharmony_ci int err, i; 11088c2ecf20Sopenharmony_ci struct pci_dev *pdev = mgp->pdev; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci if (mgp->ss[0].dca_tag == NULL || mgp->dca_enabled) 11118c2ecf20Sopenharmony_ci return; 11128c2ecf20Sopenharmony_ci if (!myri10ge_dca) { 11138c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "dca disabled by administrator\n"); 11148c2ecf20Sopenharmony_ci return; 11158c2ecf20Sopenharmony_ci } 11168c2ecf20Sopenharmony_ci err = dca_add_requester(&pdev->dev); 11178c2ecf20Sopenharmony_ci if (err) { 11188c2ecf20Sopenharmony_ci if (err != -ENODEV) 11198c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 11208c2ecf20Sopenharmony_ci "dca_add_requester() failed, err=%d\n", err); 11218c2ecf20Sopenharmony_ci return; 11228c2ecf20Sopenharmony_ci } 11238c2ecf20Sopenharmony_ci mgp->relaxed_order = myri10ge_toggle_relaxed(pdev, 0); 11248c2ecf20Sopenharmony_ci mgp->dca_enabled = 1; 11258c2ecf20Sopenharmony_ci for (i = 0; i < mgp->num_slices; i++) { 11268c2ecf20Sopenharmony_ci mgp->ss[i].cpu = -1; 11278c2ecf20Sopenharmony_ci mgp->ss[i].cached_dca_tag = -1; 11288c2ecf20Sopenharmony_ci myri10ge_update_dca(&mgp->ss[i]); 11298c2ecf20Sopenharmony_ci } 11308c2ecf20Sopenharmony_ci} 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_cistatic void myri10ge_teardown_dca(struct myri10ge_priv *mgp) 11338c2ecf20Sopenharmony_ci{ 11348c2ecf20Sopenharmony_ci struct pci_dev *pdev = mgp->pdev; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci if (!mgp->dca_enabled) 11378c2ecf20Sopenharmony_ci return; 11388c2ecf20Sopenharmony_ci mgp->dca_enabled = 0; 11398c2ecf20Sopenharmony_ci if (mgp->relaxed_order) 11408c2ecf20Sopenharmony_ci myri10ge_toggle_relaxed(pdev, 1); 11418c2ecf20Sopenharmony_ci dca_remove_requester(&pdev->dev); 11428c2ecf20Sopenharmony_ci} 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_cistatic int myri10ge_notify_dca_device(struct device *dev, void *data) 11458c2ecf20Sopenharmony_ci{ 11468c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp; 11478c2ecf20Sopenharmony_ci unsigned long event; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci mgp = dev_get_drvdata(dev); 11508c2ecf20Sopenharmony_ci event = *(unsigned long *)data; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci if (event == DCA_PROVIDER_ADD) 11538c2ecf20Sopenharmony_ci myri10ge_setup_dca(mgp); 11548c2ecf20Sopenharmony_ci else if (event == DCA_PROVIDER_REMOVE) 11558c2ecf20Sopenharmony_ci myri10ge_teardown_dca(mgp); 11568c2ecf20Sopenharmony_ci return 0; 11578c2ecf20Sopenharmony_ci} 11588c2ecf20Sopenharmony_ci#endif /* CONFIG_MYRI10GE_DCA */ 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_cistatic inline void 11618c2ecf20Sopenharmony_cimyri10ge_submit_8rx(struct mcp_kreq_ether_recv __iomem * dst, 11628c2ecf20Sopenharmony_ci struct mcp_kreq_ether_recv *src) 11638c2ecf20Sopenharmony_ci{ 11648c2ecf20Sopenharmony_ci __be32 low; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci low = src->addr_low; 11678c2ecf20Sopenharmony_ci src->addr_low = htonl(DMA_BIT_MASK(32)); 11688c2ecf20Sopenharmony_ci myri10ge_pio_copy(dst, src, 4 * sizeof(*src)); 11698c2ecf20Sopenharmony_ci mb(); 11708c2ecf20Sopenharmony_ci myri10ge_pio_copy(dst + 4, src + 4, 4 * sizeof(*src)); 11718c2ecf20Sopenharmony_ci mb(); 11728c2ecf20Sopenharmony_ci src->addr_low = low; 11738c2ecf20Sopenharmony_ci put_be32(low, &dst->addr_low); 11748c2ecf20Sopenharmony_ci mb(); 11758c2ecf20Sopenharmony_ci} 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_cistatic void 11788c2ecf20Sopenharmony_cimyri10ge_alloc_rx_pages(struct myri10ge_priv *mgp, struct myri10ge_rx_buf *rx, 11798c2ecf20Sopenharmony_ci int bytes, int watchdog) 11808c2ecf20Sopenharmony_ci{ 11818c2ecf20Sopenharmony_ci struct page *page; 11828c2ecf20Sopenharmony_ci dma_addr_t bus; 11838c2ecf20Sopenharmony_ci int idx; 11848c2ecf20Sopenharmony_ci#if MYRI10GE_ALLOC_SIZE > 4096 11858c2ecf20Sopenharmony_ci int end_offset; 11868c2ecf20Sopenharmony_ci#endif 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci if (unlikely(rx->watchdog_needed && !watchdog)) 11898c2ecf20Sopenharmony_ci return; 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci /* try to refill entire ring */ 11928c2ecf20Sopenharmony_ci while (rx->fill_cnt != (rx->cnt + rx->mask + 1)) { 11938c2ecf20Sopenharmony_ci idx = rx->fill_cnt & rx->mask; 11948c2ecf20Sopenharmony_ci if (rx->page_offset + bytes <= MYRI10GE_ALLOC_SIZE) { 11958c2ecf20Sopenharmony_ci /* we can use part of previous page */ 11968c2ecf20Sopenharmony_ci get_page(rx->page); 11978c2ecf20Sopenharmony_ci } else { 11988c2ecf20Sopenharmony_ci /* we need a new page */ 11998c2ecf20Sopenharmony_ci page = 12008c2ecf20Sopenharmony_ci alloc_pages(GFP_ATOMIC | __GFP_COMP, 12018c2ecf20Sopenharmony_ci MYRI10GE_ALLOC_ORDER); 12028c2ecf20Sopenharmony_ci if (unlikely(page == NULL)) { 12038c2ecf20Sopenharmony_ci if (rx->fill_cnt - rx->cnt < 16) 12048c2ecf20Sopenharmony_ci rx->watchdog_needed = 1; 12058c2ecf20Sopenharmony_ci return; 12068c2ecf20Sopenharmony_ci } 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci bus = pci_map_page(mgp->pdev, page, 0, 12098c2ecf20Sopenharmony_ci MYRI10GE_ALLOC_SIZE, 12108c2ecf20Sopenharmony_ci PCI_DMA_FROMDEVICE); 12118c2ecf20Sopenharmony_ci if (unlikely(pci_dma_mapping_error(mgp->pdev, bus))) { 12128c2ecf20Sopenharmony_ci __free_pages(page, MYRI10GE_ALLOC_ORDER); 12138c2ecf20Sopenharmony_ci if (rx->fill_cnt - rx->cnt < 16) 12148c2ecf20Sopenharmony_ci rx->watchdog_needed = 1; 12158c2ecf20Sopenharmony_ci return; 12168c2ecf20Sopenharmony_ci } 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci rx->page = page; 12198c2ecf20Sopenharmony_ci rx->page_offset = 0; 12208c2ecf20Sopenharmony_ci rx->bus = bus; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci } 12238c2ecf20Sopenharmony_ci rx->info[idx].page = rx->page; 12248c2ecf20Sopenharmony_ci rx->info[idx].page_offset = rx->page_offset; 12258c2ecf20Sopenharmony_ci /* note that this is the address of the start of the 12268c2ecf20Sopenharmony_ci * page */ 12278c2ecf20Sopenharmony_ci dma_unmap_addr_set(&rx->info[idx], bus, rx->bus); 12288c2ecf20Sopenharmony_ci rx->shadow[idx].addr_low = 12298c2ecf20Sopenharmony_ci htonl(MYRI10GE_LOWPART_TO_U32(rx->bus) + rx->page_offset); 12308c2ecf20Sopenharmony_ci rx->shadow[idx].addr_high = 12318c2ecf20Sopenharmony_ci htonl(MYRI10GE_HIGHPART_TO_U32(rx->bus)); 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci /* start next packet on a cacheline boundary */ 12348c2ecf20Sopenharmony_ci rx->page_offset += SKB_DATA_ALIGN(bytes); 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci#if MYRI10GE_ALLOC_SIZE > 4096 12378c2ecf20Sopenharmony_ci /* don't cross a 4KB boundary */ 12388c2ecf20Sopenharmony_ci end_offset = rx->page_offset + bytes - 1; 12398c2ecf20Sopenharmony_ci if ((unsigned)(rx->page_offset ^ end_offset) > 4095) 12408c2ecf20Sopenharmony_ci rx->page_offset = end_offset & ~4095; 12418c2ecf20Sopenharmony_ci#endif 12428c2ecf20Sopenharmony_ci rx->fill_cnt++; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci /* copy 8 descriptors to the firmware at a time */ 12458c2ecf20Sopenharmony_ci if ((idx & 7) == 7) { 12468c2ecf20Sopenharmony_ci myri10ge_submit_8rx(&rx->lanai[idx - 7], 12478c2ecf20Sopenharmony_ci &rx->shadow[idx - 7]); 12488c2ecf20Sopenharmony_ci } 12498c2ecf20Sopenharmony_ci } 12508c2ecf20Sopenharmony_ci} 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_cistatic inline void 12538c2ecf20Sopenharmony_cimyri10ge_unmap_rx_page(struct pci_dev *pdev, 12548c2ecf20Sopenharmony_ci struct myri10ge_rx_buffer_state *info, int bytes) 12558c2ecf20Sopenharmony_ci{ 12568c2ecf20Sopenharmony_ci /* unmap the recvd page if we're the only or last user of it */ 12578c2ecf20Sopenharmony_ci if (bytes >= MYRI10GE_ALLOC_SIZE / 2 || 12588c2ecf20Sopenharmony_ci (info->page_offset + 2 * bytes) > MYRI10GE_ALLOC_SIZE) { 12598c2ecf20Sopenharmony_ci pci_unmap_page(pdev, (dma_unmap_addr(info, bus) 12608c2ecf20Sopenharmony_ci & ~(MYRI10GE_ALLOC_SIZE - 1)), 12618c2ecf20Sopenharmony_ci MYRI10GE_ALLOC_SIZE, PCI_DMA_FROMDEVICE); 12628c2ecf20Sopenharmony_ci } 12638c2ecf20Sopenharmony_ci} 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci/* 12668c2ecf20Sopenharmony_ci * GRO does not support acceleration of tagged vlan frames, and 12678c2ecf20Sopenharmony_ci * this NIC does not support vlan tag offload, so we must pop 12688c2ecf20Sopenharmony_ci * the tag ourselves to be able to achieve GRO performance that 12698c2ecf20Sopenharmony_ci * is comparable to LRO. 12708c2ecf20Sopenharmony_ci */ 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_cistatic inline void 12738c2ecf20Sopenharmony_cimyri10ge_vlan_rx(struct net_device *dev, void *addr, struct sk_buff *skb) 12748c2ecf20Sopenharmony_ci{ 12758c2ecf20Sopenharmony_ci u8 *va; 12768c2ecf20Sopenharmony_ci struct vlan_ethhdr *veh; 12778c2ecf20Sopenharmony_ci skb_frag_t *frag; 12788c2ecf20Sopenharmony_ci __wsum vsum; 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci va = addr; 12818c2ecf20Sopenharmony_ci va += MXGEFW_PAD; 12828c2ecf20Sopenharmony_ci veh = (struct vlan_ethhdr *)va; 12838c2ecf20Sopenharmony_ci if ((dev->features & NETIF_F_HW_VLAN_CTAG_RX) == 12848c2ecf20Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_RX && 12858c2ecf20Sopenharmony_ci veh->h_vlan_proto == htons(ETH_P_8021Q)) { 12868c2ecf20Sopenharmony_ci /* fixup csum if needed */ 12878c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_COMPLETE) { 12888c2ecf20Sopenharmony_ci vsum = csum_partial(va + ETH_HLEN, VLAN_HLEN, 0); 12898c2ecf20Sopenharmony_ci skb->csum = csum_sub(skb->csum, vsum); 12908c2ecf20Sopenharmony_ci } 12918c2ecf20Sopenharmony_ci /* pop tag */ 12928c2ecf20Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ntohs(veh->h_vlan_TCI)); 12938c2ecf20Sopenharmony_ci memmove(va + VLAN_HLEN, va, 2 * ETH_ALEN); 12948c2ecf20Sopenharmony_ci skb->len -= VLAN_HLEN; 12958c2ecf20Sopenharmony_ci skb->data_len -= VLAN_HLEN; 12968c2ecf20Sopenharmony_ci frag = skb_shinfo(skb)->frags; 12978c2ecf20Sopenharmony_ci skb_frag_off_add(frag, VLAN_HLEN); 12988c2ecf20Sopenharmony_ci skb_frag_size_sub(frag, VLAN_HLEN); 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci} 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci#define MYRI10GE_HLEN 64 /* Bytes to copy from page to skb linear memory */ 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_cistatic inline int 13058c2ecf20Sopenharmony_cimyri10ge_rx_done(struct myri10ge_slice_state *ss, int len, __wsum csum) 13068c2ecf20Sopenharmony_ci{ 13078c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = ss->mgp; 13088c2ecf20Sopenharmony_ci struct sk_buff *skb; 13098c2ecf20Sopenharmony_ci skb_frag_t *rx_frags; 13108c2ecf20Sopenharmony_ci struct myri10ge_rx_buf *rx; 13118c2ecf20Sopenharmony_ci int i, idx, remainder, bytes; 13128c2ecf20Sopenharmony_ci struct pci_dev *pdev = mgp->pdev; 13138c2ecf20Sopenharmony_ci struct net_device *dev = mgp->dev; 13148c2ecf20Sopenharmony_ci u8 *va; 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci if (len <= mgp->small_bytes) { 13178c2ecf20Sopenharmony_ci rx = &ss->rx_small; 13188c2ecf20Sopenharmony_ci bytes = mgp->small_bytes; 13198c2ecf20Sopenharmony_ci } else { 13208c2ecf20Sopenharmony_ci rx = &ss->rx_big; 13218c2ecf20Sopenharmony_ci bytes = mgp->big_bytes; 13228c2ecf20Sopenharmony_ci } 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci len += MXGEFW_PAD; 13258c2ecf20Sopenharmony_ci idx = rx->cnt & rx->mask; 13268c2ecf20Sopenharmony_ci va = page_address(rx->info[idx].page) + rx->info[idx].page_offset; 13278c2ecf20Sopenharmony_ci prefetch(va); 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci skb = napi_get_frags(&ss->napi); 13308c2ecf20Sopenharmony_ci if (unlikely(skb == NULL)) { 13318c2ecf20Sopenharmony_ci ss->stats.rx_dropped++; 13328c2ecf20Sopenharmony_ci for (i = 0, remainder = len; remainder > 0; i++) { 13338c2ecf20Sopenharmony_ci myri10ge_unmap_rx_page(pdev, &rx->info[idx], bytes); 13348c2ecf20Sopenharmony_ci put_page(rx->info[idx].page); 13358c2ecf20Sopenharmony_ci rx->cnt++; 13368c2ecf20Sopenharmony_ci idx = rx->cnt & rx->mask; 13378c2ecf20Sopenharmony_ci remainder -= MYRI10GE_ALLOC_SIZE; 13388c2ecf20Sopenharmony_ci } 13398c2ecf20Sopenharmony_ci return 0; 13408c2ecf20Sopenharmony_ci } 13418c2ecf20Sopenharmony_ci rx_frags = skb_shinfo(skb)->frags; 13428c2ecf20Sopenharmony_ci /* Fill skb_frag_t(s) with data from our receive */ 13438c2ecf20Sopenharmony_ci for (i = 0, remainder = len; remainder > 0; i++) { 13448c2ecf20Sopenharmony_ci myri10ge_unmap_rx_page(pdev, &rx->info[idx], bytes); 13458c2ecf20Sopenharmony_ci skb_fill_page_desc(skb, i, rx->info[idx].page, 13468c2ecf20Sopenharmony_ci rx->info[idx].page_offset, 13478c2ecf20Sopenharmony_ci remainder < MYRI10GE_ALLOC_SIZE ? 13488c2ecf20Sopenharmony_ci remainder : MYRI10GE_ALLOC_SIZE); 13498c2ecf20Sopenharmony_ci rx->cnt++; 13508c2ecf20Sopenharmony_ci idx = rx->cnt & rx->mask; 13518c2ecf20Sopenharmony_ci remainder -= MYRI10GE_ALLOC_SIZE; 13528c2ecf20Sopenharmony_ci } 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci /* remove padding */ 13558c2ecf20Sopenharmony_ci skb_frag_off_add(&rx_frags[0], MXGEFW_PAD); 13568c2ecf20Sopenharmony_ci skb_frag_size_sub(&rx_frags[0], MXGEFW_PAD); 13578c2ecf20Sopenharmony_ci len -= MXGEFW_PAD; 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci skb->len = len; 13608c2ecf20Sopenharmony_ci skb->data_len = len; 13618c2ecf20Sopenharmony_ci skb->truesize += len; 13628c2ecf20Sopenharmony_ci if (dev->features & NETIF_F_RXCSUM) { 13638c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_COMPLETE; 13648c2ecf20Sopenharmony_ci skb->csum = csum; 13658c2ecf20Sopenharmony_ci } 13668c2ecf20Sopenharmony_ci myri10ge_vlan_rx(mgp->dev, va, skb); 13678c2ecf20Sopenharmony_ci skb_record_rx_queue(skb, ss - &mgp->ss[0]); 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci napi_gro_frags(&ss->napi); 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci return 1; 13728c2ecf20Sopenharmony_ci} 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_cistatic inline void 13758c2ecf20Sopenharmony_cimyri10ge_tx_done(struct myri10ge_slice_state *ss, int mcp_index) 13768c2ecf20Sopenharmony_ci{ 13778c2ecf20Sopenharmony_ci struct pci_dev *pdev = ss->mgp->pdev; 13788c2ecf20Sopenharmony_ci struct myri10ge_tx_buf *tx = &ss->tx; 13798c2ecf20Sopenharmony_ci struct netdev_queue *dev_queue; 13808c2ecf20Sopenharmony_ci struct sk_buff *skb; 13818c2ecf20Sopenharmony_ci int idx, len; 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci while (tx->pkt_done != mcp_index) { 13848c2ecf20Sopenharmony_ci idx = tx->done & tx->mask; 13858c2ecf20Sopenharmony_ci skb = tx->info[idx].skb; 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci /* Mark as free */ 13888c2ecf20Sopenharmony_ci tx->info[idx].skb = NULL; 13898c2ecf20Sopenharmony_ci if (tx->info[idx].last) { 13908c2ecf20Sopenharmony_ci tx->pkt_done++; 13918c2ecf20Sopenharmony_ci tx->info[idx].last = 0; 13928c2ecf20Sopenharmony_ci } 13938c2ecf20Sopenharmony_ci tx->done++; 13948c2ecf20Sopenharmony_ci len = dma_unmap_len(&tx->info[idx], len); 13958c2ecf20Sopenharmony_ci dma_unmap_len_set(&tx->info[idx], len, 0); 13968c2ecf20Sopenharmony_ci if (skb) { 13978c2ecf20Sopenharmony_ci ss->stats.tx_bytes += skb->len; 13988c2ecf20Sopenharmony_ci ss->stats.tx_packets++; 13998c2ecf20Sopenharmony_ci dev_consume_skb_irq(skb); 14008c2ecf20Sopenharmony_ci if (len) 14018c2ecf20Sopenharmony_ci pci_unmap_single(pdev, 14028c2ecf20Sopenharmony_ci dma_unmap_addr(&tx->info[idx], 14038c2ecf20Sopenharmony_ci bus), len, 14048c2ecf20Sopenharmony_ci PCI_DMA_TODEVICE); 14058c2ecf20Sopenharmony_ci } else { 14068c2ecf20Sopenharmony_ci if (len) 14078c2ecf20Sopenharmony_ci pci_unmap_page(pdev, 14088c2ecf20Sopenharmony_ci dma_unmap_addr(&tx->info[idx], 14098c2ecf20Sopenharmony_ci bus), len, 14108c2ecf20Sopenharmony_ci PCI_DMA_TODEVICE); 14118c2ecf20Sopenharmony_ci } 14128c2ecf20Sopenharmony_ci } 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci dev_queue = netdev_get_tx_queue(ss->dev, ss - ss->mgp->ss); 14158c2ecf20Sopenharmony_ci /* 14168c2ecf20Sopenharmony_ci * Make a minimal effort to prevent the NIC from polling an 14178c2ecf20Sopenharmony_ci * idle tx queue. If we can't get the lock we leave the queue 14188c2ecf20Sopenharmony_ci * active. In this case, either a thread was about to start 14198c2ecf20Sopenharmony_ci * using the queue anyway, or we lost a race and the NIC will 14208c2ecf20Sopenharmony_ci * waste some of its resources polling an inactive queue for a 14218c2ecf20Sopenharmony_ci * while. 14228c2ecf20Sopenharmony_ci */ 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci if ((ss->mgp->dev->real_num_tx_queues > 1) && 14258c2ecf20Sopenharmony_ci __netif_tx_trylock(dev_queue)) { 14268c2ecf20Sopenharmony_ci if (tx->req == tx->done) { 14278c2ecf20Sopenharmony_ci tx->queue_active = 0; 14288c2ecf20Sopenharmony_ci put_be32(htonl(1), tx->send_stop); 14298c2ecf20Sopenharmony_ci mb(); 14308c2ecf20Sopenharmony_ci } 14318c2ecf20Sopenharmony_ci __netif_tx_unlock(dev_queue); 14328c2ecf20Sopenharmony_ci } 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci /* start the queue if we've stopped it */ 14358c2ecf20Sopenharmony_ci if (netif_tx_queue_stopped(dev_queue) && 14368c2ecf20Sopenharmony_ci tx->req - tx->done < (tx->mask >> 1) && 14378c2ecf20Sopenharmony_ci ss->mgp->running == MYRI10GE_ETH_RUNNING) { 14388c2ecf20Sopenharmony_ci tx->wake_queue++; 14398c2ecf20Sopenharmony_ci netif_tx_wake_queue(dev_queue); 14408c2ecf20Sopenharmony_ci } 14418c2ecf20Sopenharmony_ci} 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_cistatic inline int 14448c2ecf20Sopenharmony_cimyri10ge_clean_rx_done(struct myri10ge_slice_state *ss, int budget) 14458c2ecf20Sopenharmony_ci{ 14468c2ecf20Sopenharmony_ci struct myri10ge_rx_done *rx_done = &ss->rx_done; 14478c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = ss->mgp; 14488c2ecf20Sopenharmony_ci unsigned long rx_bytes = 0; 14498c2ecf20Sopenharmony_ci unsigned long rx_packets = 0; 14508c2ecf20Sopenharmony_ci unsigned long rx_ok; 14518c2ecf20Sopenharmony_ci int idx = rx_done->idx; 14528c2ecf20Sopenharmony_ci int cnt = rx_done->cnt; 14538c2ecf20Sopenharmony_ci int work_done = 0; 14548c2ecf20Sopenharmony_ci u16 length; 14558c2ecf20Sopenharmony_ci __wsum checksum; 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci while (rx_done->entry[idx].length != 0 && work_done < budget) { 14588c2ecf20Sopenharmony_ci length = ntohs(rx_done->entry[idx].length); 14598c2ecf20Sopenharmony_ci rx_done->entry[idx].length = 0; 14608c2ecf20Sopenharmony_ci checksum = csum_unfold(rx_done->entry[idx].checksum); 14618c2ecf20Sopenharmony_ci rx_ok = myri10ge_rx_done(ss, length, checksum); 14628c2ecf20Sopenharmony_ci rx_packets += rx_ok; 14638c2ecf20Sopenharmony_ci rx_bytes += rx_ok * (unsigned long)length; 14648c2ecf20Sopenharmony_ci cnt++; 14658c2ecf20Sopenharmony_ci idx = cnt & (mgp->max_intr_slots - 1); 14668c2ecf20Sopenharmony_ci work_done++; 14678c2ecf20Sopenharmony_ci } 14688c2ecf20Sopenharmony_ci rx_done->idx = idx; 14698c2ecf20Sopenharmony_ci rx_done->cnt = cnt; 14708c2ecf20Sopenharmony_ci ss->stats.rx_packets += rx_packets; 14718c2ecf20Sopenharmony_ci ss->stats.rx_bytes += rx_bytes; 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci /* restock receive rings if needed */ 14748c2ecf20Sopenharmony_ci if (ss->rx_small.fill_cnt - ss->rx_small.cnt < myri10ge_fill_thresh) 14758c2ecf20Sopenharmony_ci myri10ge_alloc_rx_pages(mgp, &ss->rx_small, 14768c2ecf20Sopenharmony_ci mgp->small_bytes + MXGEFW_PAD, 0); 14778c2ecf20Sopenharmony_ci if (ss->rx_big.fill_cnt - ss->rx_big.cnt < myri10ge_fill_thresh) 14788c2ecf20Sopenharmony_ci myri10ge_alloc_rx_pages(mgp, &ss->rx_big, mgp->big_bytes, 0); 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci return work_done; 14818c2ecf20Sopenharmony_ci} 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_cistatic inline void myri10ge_check_statblock(struct myri10ge_priv *mgp) 14848c2ecf20Sopenharmony_ci{ 14858c2ecf20Sopenharmony_ci struct mcp_irq_data *stats = mgp->ss[0].fw_stats; 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci if (unlikely(stats->stats_updated)) { 14888c2ecf20Sopenharmony_ci unsigned link_up = ntohl(stats->link_up); 14898c2ecf20Sopenharmony_ci if (mgp->link_state != link_up) { 14908c2ecf20Sopenharmony_ci mgp->link_state = link_up; 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci if (mgp->link_state == MXGEFW_LINK_UP) { 14938c2ecf20Sopenharmony_ci netif_info(mgp, link, mgp->dev, "link up\n"); 14948c2ecf20Sopenharmony_ci netif_carrier_on(mgp->dev); 14958c2ecf20Sopenharmony_ci mgp->link_changes++; 14968c2ecf20Sopenharmony_ci } else { 14978c2ecf20Sopenharmony_ci netif_info(mgp, link, mgp->dev, "link %s\n", 14988c2ecf20Sopenharmony_ci (link_up == MXGEFW_LINK_MYRINET ? 14998c2ecf20Sopenharmony_ci "mismatch (Myrinet detected)" : 15008c2ecf20Sopenharmony_ci "down")); 15018c2ecf20Sopenharmony_ci netif_carrier_off(mgp->dev); 15028c2ecf20Sopenharmony_ci mgp->link_changes++; 15038c2ecf20Sopenharmony_ci } 15048c2ecf20Sopenharmony_ci } 15058c2ecf20Sopenharmony_ci if (mgp->rdma_tags_available != 15068c2ecf20Sopenharmony_ci ntohl(stats->rdma_tags_available)) { 15078c2ecf20Sopenharmony_ci mgp->rdma_tags_available = 15088c2ecf20Sopenharmony_ci ntohl(stats->rdma_tags_available); 15098c2ecf20Sopenharmony_ci netdev_warn(mgp->dev, "RDMA timed out! %d tags left\n", 15108c2ecf20Sopenharmony_ci mgp->rdma_tags_available); 15118c2ecf20Sopenharmony_ci } 15128c2ecf20Sopenharmony_ci mgp->down_cnt += stats->link_down; 15138c2ecf20Sopenharmony_ci if (stats->link_down) 15148c2ecf20Sopenharmony_ci wake_up(&mgp->down_wq); 15158c2ecf20Sopenharmony_ci } 15168c2ecf20Sopenharmony_ci} 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_cistatic int myri10ge_poll(struct napi_struct *napi, int budget) 15198c2ecf20Sopenharmony_ci{ 15208c2ecf20Sopenharmony_ci struct myri10ge_slice_state *ss = 15218c2ecf20Sopenharmony_ci container_of(napi, struct myri10ge_slice_state, napi); 15228c2ecf20Sopenharmony_ci int work_done; 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_ci#ifdef CONFIG_MYRI10GE_DCA 15258c2ecf20Sopenharmony_ci if (ss->mgp->dca_enabled) 15268c2ecf20Sopenharmony_ci myri10ge_update_dca(ss); 15278c2ecf20Sopenharmony_ci#endif 15288c2ecf20Sopenharmony_ci /* process as many rx events as NAPI will allow */ 15298c2ecf20Sopenharmony_ci work_done = myri10ge_clean_rx_done(ss, budget); 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci if (work_done < budget) { 15328c2ecf20Sopenharmony_ci napi_complete_done(napi, work_done); 15338c2ecf20Sopenharmony_ci put_be32(htonl(3), ss->irq_claim); 15348c2ecf20Sopenharmony_ci } 15358c2ecf20Sopenharmony_ci return work_done; 15368c2ecf20Sopenharmony_ci} 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_cistatic irqreturn_t myri10ge_intr(int irq, void *arg) 15398c2ecf20Sopenharmony_ci{ 15408c2ecf20Sopenharmony_ci struct myri10ge_slice_state *ss = arg; 15418c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = ss->mgp; 15428c2ecf20Sopenharmony_ci struct mcp_irq_data *stats = ss->fw_stats; 15438c2ecf20Sopenharmony_ci struct myri10ge_tx_buf *tx = &ss->tx; 15448c2ecf20Sopenharmony_ci u32 send_done_count; 15458c2ecf20Sopenharmony_ci int i; 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci /* an interrupt on a non-zero receive-only slice is implicitly 15488c2ecf20Sopenharmony_ci * valid since MSI-X irqs are not shared */ 15498c2ecf20Sopenharmony_ci if ((mgp->dev->real_num_tx_queues == 1) && (ss != mgp->ss)) { 15508c2ecf20Sopenharmony_ci napi_schedule(&ss->napi); 15518c2ecf20Sopenharmony_ci return IRQ_HANDLED; 15528c2ecf20Sopenharmony_ci } 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci /* make sure it is our IRQ, and that the DMA has finished */ 15558c2ecf20Sopenharmony_ci if (unlikely(!stats->valid)) 15568c2ecf20Sopenharmony_ci return IRQ_NONE; 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci /* low bit indicates receives are present, so schedule 15598c2ecf20Sopenharmony_ci * napi poll handler */ 15608c2ecf20Sopenharmony_ci if (stats->valid & 1) 15618c2ecf20Sopenharmony_ci napi_schedule(&ss->napi); 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci if (!mgp->msi_enabled && !mgp->msix_enabled) { 15648c2ecf20Sopenharmony_ci put_be32(0, mgp->irq_deassert); 15658c2ecf20Sopenharmony_ci if (!myri10ge_deassert_wait) 15668c2ecf20Sopenharmony_ci stats->valid = 0; 15678c2ecf20Sopenharmony_ci mb(); 15688c2ecf20Sopenharmony_ci } else 15698c2ecf20Sopenharmony_ci stats->valid = 0; 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci /* Wait for IRQ line to go low, if using INTx */ 15728c2ecf20Sopenharmony_ci i = 0; 15738c2ecf20Sopenharmony_ci while (1) { 15748c2ecf20Sopenharmony_ci i++; 15758c2ecf20Sopenharmony_ci /* check for transmit completes and receives */ 15768c2ecf20Sopenharmony_ci send_done_count = ntohl(stats->send_done_count); 15778c2ecf20Sopenharmony_ci if (send_done_count != tx->pkt_done) 15788c2ecf20Sopenharmony_ci myri10ge_tx_done(ss, (int)send_done_count); 15798c2ecf20Sopenharmony_ci if (unlikely(i > myri10ge_max_irq_loops)) { 15808c2ecf20Sopenharmony_ci netdev_warn(mgp->dev, "irq stuck?\n"); 15818c2ecf20Sopenharmony_ci stats->valid = 0; 15828c2ecf20Sopenharmony_ci schedule_work(&mgp->watchdog_work); 15838c2ecf20Sopenharmony_ci } 15848c2ecf20Sopenharmony_ci if (likely(stats->valid == 0)) 15858c2ecf20Sopenharmony_ci break; 15868c2ecf20Sopenharmony_ci cpu_relax(); 15878c2ecf20Sopenharmony_ci barrier(); 15888c2ecf20Sopenharmony_ci } 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci /* Only slice 0 updates stats */ 15918c2ecf20Sopenharmony_ci if (ss == mgp->ss) 15928c2ecf20Sopenharmony_ci myri10ge_check_statblock(mgp); 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci put_be32(htonl(3), ss->irq_claim + 1); 15958c2ecf20Sopenharmony_ci return IRQ_HANDLED; 15968c2ecf20Sopenharmony_ci} 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_cistatic int 15998c2ecf20Sopenharmony_cimyri10ge_get_link_ksettings(struct net_device *netdev, 16008c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *cmd) 16018c2ecf20Sopenharmony_ci{ 16028c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = netdev_priv(netdev); 16038c2ecf20Sopenharmony_ci char *ptr; 16048c2ecf20Sopenharmony_ci int i; 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci cmd->base.autoneg = AUTONEG_DISABLE; 16078c2ecf20Sopenharmony_ci cmd->base.speed = SPEED_10000; 16088c2ecf20Sopenharmony_ci cmd->base.duplex = DUPLEX_FULL; 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci /* 16118c2ecf20Sopenharmony_ci * parse the product code to deterimine the interface type 16128c2ecf20Sopenharmony_ci * (CX4, XFP, Quad Ribbon Fiber) by looking at the character 16138c2ecf20Sopenharmony_ci * after the 3rd dash in the driver's cached copy of the 16148c2ecf20Sopenharmony_ci * EEPROM's product code string. 16158c2ecf20Sopenharmony_ci */ 16168c2ecf20Sopenharmony_ci ptr = mgp->product_code_string; 16178c2ecf20Sopenharmony_ci if (ptr == NULL) { 16188c2ecf20Sopenharmony_ci netdev_err(netdev, "Missing product code\n"); 16198c2ecf20Sopenharmony_ci return 0; 16208c2ecf20Sopenharmony_ci } 16218c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++, ptr++) { 16228c2ecf20Sopenharmony_ci ptr = strchr(ptr, '-'); 16238c2ecf20Sopenharmony_ci if (ptr == NULL) { 16248c2ecf20Sopenharmony_ci netdev_err(netdev, "Invalid product code %s\n", 16258c2ecf20Sopenharmony_ci mgp->product_code_string); 16268c2ecf20Sopenharmony_ci return 0; 16278c2ecf20Sopenharmony_ci } 16288c2ecf20Sopenharmony_ci } 16298c2ecf20Sopenharmony_ci if (*ptr == '2') 16308c2ecf20Sopenharmony_ci ptr++; 16318c2ecf20Sopenharmony_ci if (*ptr == 'R' || *ptr == 'Q' || *ptr == 'S') { 16328c2ecf20Sopenharmony_ci /* We've found either an XFP, quad ribbon fiber, or SFP+ */ 16338c2ecf20Sopenharmony_ci cmd->base.port = PORT_FIBRE; 16348c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE); 16358c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, advertising, FIBRE); 16368c2ecf20Sopenharmony_ci } else { 16378c2ecf20Sopenharmony_ci cmd->base.port = PORT_OTHER; 16388c2ecf20Sopenharmony_ci } 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci return 0; 16418c2ecf20Sopenharmony_ci} 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_cistatic void 16448c2ecf20Sopenharmony_cimyri10ge_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) 16458c2ecf20Sopenharmony_ci{ 16468c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = netdev_priv(netdev); 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci strlcpy(info->driver, "myri10ge", sizeof(info->driver)); 16498c2ecf20Sopenharmony_ci strlcpy(info->version, MYRI10GE_VERSION_STR, sizeof(info->version)); 16508c2ecf20Sopenharmony_ci strlcpy(info->fw_version, mgp->fw_version, sizeof(info->fw_version)); 16518c2ecf20Sopenharmony_ci strlcpy(info->bus_info, pci_name(mgp->pdev), sizeof(info->bus_info)); 16528c2ecf20Sopenharmony_ci} 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_cistatic int 16558c2ecf20Sopenharmony_cimyri10ge_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *coal) 16568c2ecf20Sopenharmony_ci{ 16578c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = netdev_priv(netdev); 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci coal->rx_coalesce_usecs = mgp->intr_coal_delay; 16608c2ecf20Sopenharmony_ci return 0; 16618c2ecf20Sopenharmony_ci} 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_cistatic int 16648c2ecf20Sopenharmony_cimyri10ge_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *coal) 16658c2ecf20Sopenharmony_ci{ 16668c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = netdev_priv(netdev); 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci mgp->intr_coal_delay = coal->rx_coalesce_usecs; 16698c2ecf20Sopenharmony_ci put_be32(htonl(mgp->intr_coal_delay), mgp->intr_coal_delay_ptr); 16708c2ecf20Sopenharmony_ci return 0; 16718c2ecf20Sopenharmony_ci} 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_cistatic void 16748c2ecf20Sopenharmony_cimyri10ge_get_pauseparam(struct net_device *netdev, 16758c2ecf20Sopenharmony_ci struct ethtool_pauseparam *pause) 16768c2ecf20Sopenharmony_ci{ 16778c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = netdev_priv(netdev); 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_ci pause->autoneg = 0; 16808c2ecf20Sopenharmony_ci pause->rx_pause = mgp->pause; 16818c2ecf20Sopenharmony_ci pause->tx_pause = mgp->pause; 16828c2ecf20Sopenharmony_ci} 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_cistatic int 16858c2ecf20Sopenharmony_cimyri10ge_set_pauseparam(struct net_device *netdev, 16868c2ecf20Sopenharmony_ci struct ethtool_pauseparam *pause) 16878c2ecf20Sopenharmony_ci{ 16888c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = netdev_priv(netdev); 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci if (pause->tx_pause != mgp->pause) 16918c2ecf20Sopenharmony_ci return myri10ge_change_pause(mgp, pause->tx_pause); 16928c2ecf20Sopenharmony_ci if (pause->rx_pause != mgp->pause) 16938c2ecf20Sopenharmony_ci return myri10ge_change_pause(mgp, pause->rx_pause); 16948c2ecf20Sopenharmony_ci if (pause->autoneg != 0) 16958c2ecf20Sopenharmony_ci return -EINVAL; 16968c2ecf20Sopenharmony_ci return 0; 16978c2ecf20Sopenharmony_ci} 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_cistatic void 17008c2ecf20Sopenharmony_cimyri10ge_get_ringparam(struct net_device *netdev, 17018c2ecf20Sopenharmony_ci struct ethtool_ringparam *ring) 17028c2ecf20Sopenharmony_ci{ 17038c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = netdev_priv(netdev); 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_ci ring->rx_mini_max_pending = mgp->ss[0].rx_small.mask + 1; 17068c2ecf20Sopenharmony_ci ring->rx_max_pending = mgp->ss[0].rx_big.mask + 1; 17078c2ecf20Sopenharmony_ci ring->rx_jumbo_max_pending = 0; 17088c2ecf20Sopenharmony_ci ring->tx_max_pending = mgp->ss[0].tx.mask + 1; 17098c2ecf20Sopenharmony_ci ring->rx_mini_pending = ring->rx_mini_max_pending; 17108c2ecf20Sopenharmony_ci ring->rx_pending = ring->rx_max_pending; 17118c2ecf20Sopenharmony_ci ring->rx_jumbo_pending = ring->rx_jumbo_max_pending; 17128c2ecf20Sopenharmony_ci ring->tx_pending = ring->tx_max_pending; 17138c2ecf20Sopenharmony_ci} 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_cistatic const char myri10ge_gstrings_main_stats[][ETH_GSTRING_LEN] = { 17168c2ecf20Sopenharmony_ci "rx_packets", "tx_packets", "rx_bytes", "tx_bytes", "rx_errors", 17178c2ecf20Sopenharmony_ci "tx_errors", "rx_dropped", "tx_dropped", "multicast", "collisions", 17188c2ecf20Sopenharmony_ci "rx_length_errors", "rx_over_errors", "rx_crc_errors", 17198c2ecf20Sopenharmony_ci "rx_frame_errors", "rx_fifo_errors", "rx_missed_errors", 17208c2ecf20Sopenharmony_ci "tx_aborted_errors", "tx_carrier_errors", "tx_fifo_errors", 17218c2ecf20Sopenharmony_ci "tx_heartbeat_errors", "tx_window_errors", 17228c2ecf20Sopenharmony_ci /* device-specific stats */ 17238c2ecf20Sopenharmony_ci "tx_boundary", "irq", "MSI", "MSIX", 17248c2ecf20Sopenharmony_ci "read_dma_bw_MBs", "write_dma_bw_MBs", "read_write_dma_bw_MBs", 17258c2ecf20Sopenharmony_ci "serial_number", "watchdog_resets", 17268c2ecf20Sopenharmony_ci#ifdef CONFIG_MYRI10GE_DCA 17278c2ecf20Sopenharmony_ci "dca_capable_firmware", "dca_device_present", 17288c2ecf20Sopenharmony_ci#endif 17298c2ecf20Sopenharmony_ci "link_changes", "link_up", "dropped_link_overflow", 17308c2ecf20Sopenharmony_ci "dropped_link_error_or_filtered", 17318c2ecf20Sopenharmony_ci "dropped_pause", "dropped_bad_phy", "dropped_bad_crc32", 17328c2ecf20Sopenharmony_ci "dropped_unicast_filtered", "dropped_multicast_filtered", 17338c2ecf20Sopenharmony_ci "dropped_runt", "dropped_overrun", "dropped_no_small_buffer", 17348c2ecf20Sopenharmony_ci "dropped_no_big_buffer" 17358c2ecf20Sopenharmony_ci}; 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_cistatic const char myri10ge_gstrings_slice_stats[][ETH_GSTRING_LEN] = { 17388c2ecf20Sopenharmony_ci "----------- slice ---------", 17398c2ecf20Sopenharmony_ci "tx_pkt_start", "tx_pkt_done", "tx_req", "tx_done", 17408c2ecf20Sopenharmony_ci "rx_small_cnt", "rx_big_cnt", 17418c2ecf20Sopenharmony_ci "wake_queue", "stop_queue", "tx_linearized", 17428c2ecf20Sopenharmony_ci}; 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci#define MYRI10GE_NET_STATS_LEN 21 17458c2ecf20Sopenharmony_ci#define MYRI10GE_MAIN_STATS_LEN ARRAY_SIZE(myri10ge_gstrings_main_stats) 17468c2ecf20Sopenharmony_ci#define MYRI10GE_SLICE_STATS_LEN ARRAY_SIZE(myri10ge_gstrings_slice_stats) 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_cistatic void 17498c2ecf20Sopenharmony_cimyri10ge_get_strings(struct net_device *netdev, u32 stringset, u8 * data) 17508c2ecf20Sopenharmony_ci{ 17518c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = netdev_priv(netdev); 17528c2ecf20Sopenharmony_ci int i; 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ci switch (stringset) { 17558c2ecf20Sopenharmony_ci case ETH_SS_STATS: 17568c2ecf20Sopenharmony_ci memcpy(data, *myri10ge_gstrings_main_stats, 17578c2ecf20Sopenharmony_ci sizeof(myri10ge_gstrings_main_stats)); 17588c2ecf20Sopenharmony_ci data += sizeof(myri10ge_gstrings_main_stats); 17598c2ecf20Sopenharmony_ci for (i = 0; i < mgp->num_slices; i++) { 17608c2ecf20Sopenharmony_ci memcpy(data, *myri10ge_gstrings_slice_stats, 17618c2ecf20Sopenharmony_ci sizeof(myri10ge_gstrings_slice_stats)); 17628c2ecf20Sopenharmony_ci data += sizeof(myri10ge_gstrings_slice_stats); 17638c2ecf20Sopenharmony_ci } 17648c2ecf20Sopenharmony_ci break; 17658c2ecf20Sopenharmony_ci } 17668c2ecf20Sopenharmony_ci} 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_cistatic int myri10ge_get_sset_count(struct net_device *netdev, int sset) 17698c2ecf20Sopenharmony_ci{ 17708c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = netdev_priv(netdev); 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci switch (sset) { 17738c2ecf20Sopenharmony_ci case ETH_SS_STATS: 17748c2ecf20Sopenharmony_ci return MYRI10GE_MAIN_STATS_LEN + 17758c2ecf20Sopenharmony_ci mgp->num_slices * MYRI10GE_SLICE_STATS_LEN; 17768c2ecf20Sopenharmony_ci default: 17778c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 17788c2ecf20Sopenharmony_ci } 17798c2ecf20Sopenharmony_ci} 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_cistatic void 17828c2ecf20Sopenharmony_cimyri10ge_get_ethtool_stats(struct net_device *netdev, 17838c2ecf20Sopenharmony_ci struct ethtool_stats *stats, u64 * data) 17848c2ecf20Sopenharmony_ci{ 17858c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = netdev_priv(netdev); 17868c2ecf20Sopenharmony_ci struct myri10ge_slice_state *ss; 17878c2ecf20Sopenharmony_ci struct rtnl_link_stats64 link_stats; 17888c2ecf20Sopenharmony_ci int slice; 17898c2ecf20Sopenharmony_ci int i; 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci /* force stats update */ 17928c2ecf20Sopenharmony_ci memset(&link_stats, 0, sizeof(link_stats)); 17938c2ecf20Sopenharmony_ci (void)myri10ge_get_stats(netdev, &link_stats); 17948c2ecf20Sopenharmony_ci for (i = 0; i < MYRI10GE_NET_STATS_LEN; i++) 17958c2ecf20Sopenharmony_ci data[i] = ((u64 *)&link_stats)[i]; 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_ci data[i++] = (unsigned int)mgp->tx_boundary; 17988c2ecf20Sopenharmony_ci data[i++] = (unsigned int)mgp->pdev->irq; 17998c2ecf20Sopenharmony_ci data[i++] = (unsigned int)mgp->msi_enabled; 18008c2ecf20Sopenharmony_ci data[i++] = (unsigned int)mgp->msix_enabled; 18018c2ecf20Sopenharmony_ci data[i++] = (unsigned int)mgp->read_dma; 18028c2ecf20Sopenharmony_ci data[i++] = (unsigned int)mgp->write_dma; 18038c2ecf20Sopenharmony_ci data[i++] = (unsigned int)mgp->read_write_dma; 18048c2ecf20Sopenharmony_ci data[i++] = (unsigned int)mgp->serial_number; 18058c2ecf20Sopenharmony_ci data[i++] = (unsigned int)mgp->watchdog_resets; 18068c2ecf20Sopenharmony_ci#ifdef CONFIG_MYRI10GE_DCA 18078c2ecf20Sopenharmony_ci data[i++] = (unsigned int)(mgp->ss[0].dca_tag != NULL); 18088c2ecf20Sopenharmony_ci data[i++] = (unsigned int)(mgp->dca_enabled); 18098c2ecf20Sopenharmony_ci#endif 18108c2ecf20Sopenharmony_ci data[i++] = (unsigned int)mgp->link_changes; 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci /* firmware stats are useful only in the first slice */ 18138c2ecf20Sopenharmony_ci ss = &mgp->ss[0]; 18148c2ecf20Sopenharmony_ci data[i++] = (unsigned int)ntohl(ss->fw_stats->link_up); 18158c2ecf20Sopenharmony_ci data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_link_overflow); 18168c2ecf20Sopenharmony_ci data[i++] = 18178c2ecf20Sopenharmony_ci (unsigned int)ntohl(ss->fw_stats->dropped_link_error_or_filtered); 18188c2ecf20Sopenharmony_ci data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_pause); 18198c2ecf20Sopenharmony_ci data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_bad_phy); 18208c2ecf20Sopenharmony_ci data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_bad_crc32); 18218c2ecf20Sopenharmony_ci data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_unicast_filtered); 18228c2ecf20Sopenharmony_ci data[i++] = 18238c2ecf20Sopenharmony_ci (unsigned int)ntohl(ss->fw_stats->dropped_multicast_filtered); 18248c2ecf20Sopenharmony_ci data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_runt); 18258c2ecf20Sopenharmony_ci data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_overrun); 18268c2ecf20Sopenharmony_ci data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_no_small_buffer); 18278c2ecf20Sopenharmony_ci data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_no_big_buffer); 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci for (slice = 0; slice < mgp->num_slices; slice++) { 18308c2ecf20Sopenharmony_ci ss = &mgp->ss[slice]; 18318c2ecf20Sopenharmony_ci data[i++] = slice; 18328c2ecf20Sopenharmony_ci data[i++] = (unsigned int)ss->tx.pkt_start; 18338c2ecf20Sopenharmony_ci data[i++] = (unsigned int)ss->tx.pkt_done; 18348c2ecf20Sopenharmony_ci data[i++] = (unsigned int)ss->tx.req; 18358c2ecf20Sopenharmony_ci data[i++] = (unsigned int)ss->tx.done; 18368c2ecf20Sopenharmony_ci data[i++] = (unsigned int)ss->rx_small.cnt; 18378c2ecf20Sopenharmony_ci data[i++] = (unsigned int)ss->rx_big.cnt; 18388c2ecf20Sopenharmony_ci data[i++] = (unsigned int)ss->tx.wake_queue; 18398c2ecf20Sopenharmony_ci data[i++] = (unsigned int)ss->tx.stop_queue; 18408c2ecf20Sopenharmony_ci data[i++] = (unsigned int)ss->tx.linearized; 18418c2ecf20Sopenharmony_ci } 18428c2ecf20Sopenharmony_ci} 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_cistatic void myri10ge_set_msglevel(struct net_device *netdev, u32 value) 18458c2ecf20Sopenharmony_ci{ 18468c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = netdev_priv(netdev); 18478c2ecf20Sopenharmony_ci mgp->msg_enable = value; 18488c2ecf20Sopenharmony_ci} 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_cistatic u32 myri10ge_get_msglevel(struct net_device *netdev) 18518c2ecf20Sopenharmony_ci{ 18528c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = netdev_priv(netdev); 18538c2ecf20Sopenharmony_ci return mgp->msg_enable; 18548c2ecf20Sopenharmony_ci} 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci/* 18578c2ecf20Sopenharmony_ci * Use a low-level command to change the LED behavior. Rather than 18588c2ecf20Sopenharmony_ci * blinking (which is the normal case), when identify is used, the 18598c2ecf20Sopenharmony_ci * yellow LED turns solid. 18608c2ecf20Sopenharmony_ci */ 18618c2ecf20Sopenharmony_cistatic int myri10ge_led(struct myri10ge_priv *mgp, int on) 18628c2ecf20Sopenharmony_ci{ 18638c2ecf20Sopenharmony_ci struct mcp_gen_header *hdr; 18648c2ecf20Sopenharmony_ci struct device *dev = &mgp->pdev->dev; 18658c2ecf20Sopenharmony_ci size_t hdr_off, pattern_off, hdr_len; 18668c2ecf20Sopenharmony_ci u32 pattern = 0xfffffffe; 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci /* find running firmware header */ 18698c2ecf20Sopenharmony_ci hdr_off = swab32(readl(mgp->sram + MCP_HEADER_PTR_OFFSET)); 18708c2ecf20Sopenharmony_ci if ((hdr_off & 3) || hdr_off + sizeof(*hdr) > mgp->sram_size) { 18718c2ecf20Sopenharmony_ci dev_err(dev, "Running firmware has bad header offset (%d)\n", 18728c2ecf20Sopenharmony_ci (int)hdr_off); 18738c2ecf20Sopenharmony_ci return -EIO; 18748c2ecf20Sopenharmony_ci } 18758c2ecf20Sopenharmony_ci hdr_len = swab32(readl(mgp->sram + hdr_off + 18768c2ecf20Sopenharmony_ci offsetof(struct mcp_gen_header, header_length))); 18778c2ecf20Sopenharmony_ci pattern_off = hdr_off + offsetof(struct mcp_gen_header, led_pattern); 18788c2ecf20Sopenharmony_ci if (pattern_off >= (hdr_len + hdr_off)) { 18798c2ecf20Sopenharmony_ci dev_info(dev, "Firmware does not support LED identification\n"); 18808c2ecf20Sopenharmony_ci return -EINVAL; 18818c2ecf20Sopenharmony_ci } 18828c2ecf20Sopenharmony_ci if (!on) 18838c2ecf20Sopenharmony_ci pattern = swab32(readl(mgp->sram + pattern_off + 4)); 18848c2ecf20Sopenharmony_ci writel(swab32(pattern), mgp->sram + pattern_off); 18858c2ecf20Sopenharmony_ci return 0; 18868c2ecf20Sopenharmony_ci} 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_cistatic int 18898c2ecf20Sopenharmony_cimyri10ge_phys_id(struct net_device *netdev, enum ethtool_phys_id_state state) 18908c2ecf20Sopenharmony_ci{ 18918c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = netdev_priv(netdev); 18928c2ecf20Sopenharmony_ci int rc; 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci switch (state) { 18958c2ecf20Sopenharmony_ci case ETHTOOL_ID_ACTIVE: 18968c2ecf20Sopenharmony_ci rc = myri10ge_led(mgp, 1); 18978c2ecf20Sopenharmony_ci break; 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci case ETHTOOL_ID_INACTIVE: 19008c2ecf20Sopenharmony_ci rc = myri10ge_led(mgp, 0); 19018c2ecf20Sopenharmony_ci break; 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_ci default: 19048c2ecf20Sopenharmony_ci rc = -EINVAL; 19058c2ecf20Sopenharmony_ci } 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci return rc; 19088c2ecf20Sopenharmony_ci} 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_cistatic const struct ethtool_ops myri10ge_ethtool_ops = { 19118c2ecf20Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS, 19128c2ecf20Sopenharmony_ci .get_drvinfo = myri10ge_get_drvinfo, 19138c2ecf20Sopenharmony_ci .get_coalesce = myri10ge_get_coalesce, 19148c2ecf20Sopenharmony_ci .set_coalesce = myri10ge_set_coalesce, 19158c2ecf20Sopenharmony_ci .get_pauseparam = myri10ge_get_pauseparam, 19168c2ecf20Sopenharmony_ci .set_pauseparam = myri10ge_set_pauseparam, 19178c2ecf20Sopenharmony_ci .get_ringparam = myri10ge_get_ringparam, 19188c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 19198c2ecf20Sopenharmony_ci .get_strings = myri10ge_get_strings, 19208c2ecf20Sopenharmony_ci .get_sset_count = myri10ge_get_sset_count, 19218c2ecf20Sopenharmony_ci .get_ethtool_stats = myri10ge_get_ethtool_stats, 19228c2ecf20Sopenharmony_ci .set_msglevel = myri10ge_set_msglevel, 19238c2ecf20Sopenharmony_ci .get_msglevel = myri10ge_get_msglevel, 19248c2ecf20Sopenharmony_ci .set_phys_id = myri10ge_phys_id, 19258c2ecf20Sopenharmony_ci .get_link_ksettings = myri10ge_get_link_ksettings, 19268c2ecf20Sopenharmony_ci}; 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_cistatic int myri10ge_allocate_rings(struct myri10ge_slice_state *ss) 19298c2ecf20Sopenharmony_ci{ 19308c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = ss->mgp; 19318c2ecf20Sopenharmony_ci struct myri10ge_cmd cmd; 19328c2ecf20Sopenharmony_ci struct net_device *dev = mgp->dev; 19338c2ecf20Sopenharmony_ci int tx_ring_size, rx_ring_size; 19348c2ecf20Sopenharmony_ci int tx_ring_entries, rx_ring_entries; 19358c2ecf20Sopenharmony_ci int i, slice, status; 19368c2ecf20Sopenharmony_ci size_t bytes; 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_ci /* get ring sizes */ 19398c2ecf20Sopenharmony_ci slice = ss - mgp->ss; 19408c2ecf20Sopenharmony_ci cmd.data0 = slice; 19418c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd, 0); 19428c2ecf20Sopenharmony_ci tx_ring_size = cmd.data0; 19438c2ecf20Sopenharmony_ci cmd.data0 = slice; 19448c2ecf20Sopenharmony_ci status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd, 0); 19458c2ecf20Sopenharmony_ci if (status != 0) 19468c2ecf20Sopenharmony_ci return status; 19478c2ecf20Sopenharmony_ci rx_ring_size = cmd.data0; 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci tx_ring_entries = tx_ring_size / sizeof(struct mcp_kreq_ether_send); 19508c2ecf20Sopenharmony_ci rx_ring_entries = rx_ring_size / sizeof(struct mcp_dma_addr); 19518c2ecf20Sopenharmony_ci ss->tx.mask = tx_ring_entries - 1; 19528c2ecf20Sopenharmony_ci ss->rx_small.mask = ss->rx_big.mask = rx_ring_entries - 1; 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci status = -ENOMEM; 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_ci /* allocate the host shadow rings */ 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci bytes = 8 + (MYRI10GE_MAX_SEND_DESC_TSO + 4) 19598c2ecf20Sopenharmony_ci * sizeof(*ss->tx.req_list); 19608c2ecf20Sopenharmony_ci ss->tx.req_bytes = kzalloc(bytes, GFP_KERNEL); 19618c2ecf20Sopenharmony_ci if (ss->tx.req_bytes == NULL) 19628c2ecf20Sopenharmony_ci goto abort_with_nothing; 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_ci /* ensure req_list entries are aligned to 8 bytes */ 19658c2ecf20Sopenharmony_ci ss->tx.req_list = (struct mcp_kreq_ether_send *) 19668c2ecf20Sopenharmony_ci ALIGN((unsigned long)ss->tx.req_bytes, 8); 19678c2ecf20Sopenharmony_ci ss->tx.queue_active = 0; 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci bytes = rx_ring_entries * sizeof(*ss->rx_small.shadow); 19708c2ecf20Sopenharmony_ci ss->rx_small.shadow = kzalloc(bytes, GFP_KERNEL); 19718c2ecf20Sopenharmony_ci if (ss->rx_small.shadow == NULL) 19728c2ecf20Sopenharmony_ci goto abort_with_tx_req_bytes; 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci bytes = rx_ring_entries * sizeof(*ss->rx_big.shadow); 19758c2ecf20Sopenharmony_ci ss->rx_big.shadow = kzalloc(bytes, GFP_KERNEL); 19768c2ecf20Sopenharmony_ci if (ss->rx_big.shadow == NULL) 19778c2ecf20Sopenharmony_ci goto abort_with_rx_small_shadow; 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci /* allocate the host info rings */ 19808c2ecf20Sopenharmony_ci 19818c2ecf20Sopenharmony_ci bytes = tx_ring_entries * sizeof(*ss->tx.info); 19828c2ecf20Sopenharmony_ci ss->tx.info = kzalloc(bytes, GFP_KERNEL); 19838c2ecf20Sopenharmony_ci if (ss->tx.info == NULL) 19848c2ecf20Sopenharmony_ci goto abort_with_rx_big_shadow; 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci bytes = rx_ring_entries * sizeof(*ss->rx_small.info); 19878c2ecf20Sopenharmony_ci ss->rx_small.info = kzalloc(bytes, GFP_KERNEL); 19888c2ecf20Sopenharmony_ci if (ss->rx_small.info == NULL) 19898c2ecf20Sopenharmony_ci goto abort_with_tx_info; 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci bytes = rx_ring_entries * sizeof(*ss->rx_big.info); 19928c2ecf20Sopenharmony_ci ss->rx_big.info = kzalloc(bytes, GFP_KERNEL); 19938c2ecf20Sopenharmony_ci if (ss->rx_big.info == NULL) 19948c2ecf20Sopenharmony_ci goto abort_with_rx_small_info; 19958c2ecf20Sopenharmony_ci 19968c2ecf20Sopenharmony_ci /* Fill the receive rings */ 19978c2ecf20Sopenharmony_ci ss->rx_big.cnt = 0; 19988c2ecf20Sopenharmony_ci ss->rx_small.cnt = 0; 19998c2ecf20Sopenharmony_ci ss->rx_big.fill_cnt = 0; 20008c2ecf20Sopenharmony_ci ss->rx_small.fill_cnt = 0; 20018c2ecf20Sopenharmony_ci ss->rx_small.page_offset = MYRI10GE_ALLOC_SIZE; 20028c2ecf20Sopenharmony_ci ss->rx_big.page_offset = MYRI10GE_ALLOC_SIZE; 20038c2ecf20Sopenharmony_ci ss->rx_small.watchdog_needed = 0; 20048c2ecf20Sopenharmony_ci ss->rx_big.watchdog_needed = 0; 20058c2ecf20Sopenharmony_ci if (mgp->small_bytes == 0) { 20068c2ecf20Sopenharmony_ci ss->rx_small.fill_cnt = ss->rx_small.mask + 1; 20078c2ecf20Sopenharmony_ci } else { 20088c2ecf20Sopenharmony_ci myri10ge_alloc_rx_pages(mgp, &ss->rx_small, 20098c2ecf20Sopenharmony_ci mgp->small_bytes + MXGEFW_PAD, 0); 20108c2ecf20Sopenharmony_ci } 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci if (ss->rx_small.fill_cnt < ss->rx_small.mask + 1) { 20138c2ecf20Sopenharmony_ci netdev_err(dev, "slice-%d: alloced only %d small bufs\n", 20148c2ecf20Sopenharmony_ci slice, ss->rx_small.fill_cnt); 20158c2ecf20Sopenharmony_ci goto abort_with_rx_small_ring; 20168c2ecf20Sopenharmony_ci } 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci myri10ge_alloc_rx_pages(mgp, &ss->rx_big, mgp->big_bytes, 0); 20198c2ecf20Sopenharmony_ci if (ss->rx_big.fill_cnt < ss->rx_big.mask + 1) { 20208c2ecf20Sopenharmony_ci netdev_err(dev, "slice-%d: alloced only %d big bufs\n", 20218c2ecf20Sopenharmony_ci slice, ss->rx_big.fill_cnt); 20228c2ecf20Sopenharmony_ci goto abort_with_rx_big_ring; 20238c2ecf20Sopenharmony_ci } 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_ci return 0; 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ciabort_with_rx_big_ring: 20288c2ecf20Sopenharmony_ci for (i = ss->rx_big.cnt; i < ss->rx_big.fill_cnt; i++) { 20298c2ecf20Sopenharmony_ci int idx = i & ss->rx_big.mask; 20308c2ecf20Sopenharmony_ci myri10ge_unmap_rx_page(mgp->pdev, &ss->rx_big.info[idx], 20318c2ecf20Sopenharmony_ci mgp->big_bytes); 20328c2ecf20Sopenharmony_ci put_page(ss->rx_big.info[idx].page); 20338c2ecf20Sopenharmony_ci } 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ciabort_with_rx_small_ring: 20368c2ecf20Sopenharmony_ci if (mgp->small_bytes == 0) 20378c2ecf20Sopenharmony_ci ss->rx_small.fill_cnt = ss->rx_small.cnt; 20388c2ecf20Sopenharmony_ci for (i = ss->rx_small.cnt; i < ss->rx_small.fill_cnt; i++) { 20398c2ecf20Sopenharmony_ci int idx = i & ss->rx_small.mask; 20408c2ecf20Sopenharmony_ci myri10ge_unmap_rx_page(mgp->pdev, &ss->rx_small.info[idx], 20418c2ecf20Sopenharmony_ci mgp->small_bytes + MXGEFW_PAD); 20428c2ecf20Sopenharmony_ci put_page(ss->rx_small.info[idx].page); 20438c2ecf20Sopenharmony_ci } 20448c2ecf20Sopenharmony_ci 20458c2ecf20Sopenharmony_ci kfree(ss->rx_big.info); 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ciabort_with_rx_small_info: 20488c2ecf20Sopenharmony_ci kfree(ss->rx_small.info); 20498c2ecf20Sopenharmony_ci 20508c2ecf20Sopenharmony_ciabort_with_tx_info: 20518c2ecf20Sopenharmony_ci kfree(ss->tx.info); 20528c2ecf20Sopenharmony_ci 20538c2ecf20Sopenharmony_ciabort_with_rx_big_shadow: 20548c2ecf20Sopenharmony_ci kfree(ss->rx_big.shadow); 20558c2ecf20Sopenharmony_ci 20568c2ecf20Sopenharmony_ciabort_with_rx_small_shadow: 20578c2ecf20Sopenharmony_ci kfree(ss->rx_small.shadow); 20588c2ecf20Sopenharmony_ci 20598c2ecf20Sopenharmony_ciabort_with_tx_req_bytes: 20608c2ecf20Sopenharmony_ci kfree(ss->tx.req_bytes); 20618c2ecf20Sopenharmony_ci ss->tx.req_bytes = NULL; 20628c2ecf20Sopenharmony_ci ss->tx.req_list = NULL; 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ciabort_with_nothing: 20658c2ecf20Sopenharmony_ci return status; 20668c2ecf20Sopenharmony_ci} 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_cistatic void myri10ge_free_rings(struct myri10ge_slice_state *ss) 20698c2ecf20Sopenharmony_ci{ 20708c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = ss->mgp; 20718c2ecf20Sopenharmony_ci struct sk_buff *skb; 20728c2ecf20Sopenharmony_ci struct myri10ge_tx_buf *tx; 20738c2ecf20Sopenharmony_ci int i, len, idx; 20748c2ecf20Sopenharmony_ci 20758c2ecf20Sopenharmony_ci /* If not allocated, skip it */ 20768c2ecf20Sopenharmony_ci if (ss->tx.req_list == NULL) 20778c2ecf20Sopenharmony_ci return; 20788c2ecf20Sopenharmony_ci 20798c2ecf20Sopenharmony_ci for (i = ss->rx_big.cnt; i < ss->rx_big.fill_cnt; i++) { 20808c2ecf20Sopenharmony_ci idx = i & ss->rx_big.mask; 20818c2ecf20Sopenharmony_ci if (i == ss->rx_big.fill_cnt - 1) 20828c2ecf20Sopenharmony_ci ss->rx_big.info[idx].page_offset = MYRI10GE_ALLOC_SIZE; 20838c2ecf20Sopenharmony_ci myri10ge_unmap_rx_page(mgp->pdev, &ss->rx_big.info[idx], 20848c2ecf20Sopenharmony_ci mgp->big_bytes); 20858c2ecf20Sopenharmony_ci put_page(ss->rx_big.info[idx].page); 20868c2ecf20Sopenharmony_ci } 20878c2ecf20Sopenharmony_ci 20888c2ecf20Sopenharmony_ci if (mgp->small_bytes == 0) 20898c2ecf20Sopenharmony_ci ss->rx_small.fill_cnt = ss->rx_small.cnt; 20908c2ecf20Sopenharmony_ci for (i = ss->rx_small.cnt; i < ss->rx_small.fill_cnt; i++) { 20918c2ecf20Sopenharmony_ci idx = i & ss->rx_small.mask; 20928c2ecf20Sopenharmony_ci if (i == ss->rx_small.fill_cnt - 1) 20938c2ecf20Sopenharmony_ci ss->rx_small.info[idx].page_offset = 20948c2ecf20Sopenharmony_ci MYRI10GE_ALLOC_SIZE; 20958c2ecf20Sopenharmony_ci myri10ge_unmap_rx_page(mgp->pdev, &ss->rx_small.info[idx], 20968c2ecf20Sopenharmony_ci mgp->small_bytes + MXGEFW_PAD); 20978c2ecf20Sopenharmony_ci put_page(ss->rx_small.info[idx].page); 20988c2ecf20Sopenharmony_ci } 20998c2ecf20Sopenharmony_ci tx = &ss->tx; 21008c2ecf20Sopenharmony_ci while (tx->done != tx->req) { 21018c2ecf20Sopenharmony_ci idx = tx->done & tx->mask; 21028c2ecf20Sopenharmony_ci skb = tx->info[idx].skb; 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ci /* Mark as free */ 21058c2ecf20Sopenharmony_ci tx->info[idx].skb = NULL; 21068c2ecf20Sopenharmony_ci tx->done++; 21078c2ecf20Sopenharmony_ci len = dma_unmap_len(&tx->info[idx], len); 21088c2ecf20Sopenharmony_ci dma_unmap_len_set(&tx->info[idx], len, 0); 21098c2ecf20Sopenharmony_ci if (skb) { 21108c2ecf20Sopenharmony_ci ss->stats.tx_dropped++; 21118c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 21128c2ecf20Sopenharmony_ci if (len) 21138c2ecf20Sopenharmony_ci pci_unmap_single(mgp->pdev, 21148c2ecf20Sopenharmony_ci dma_unmap_addr(&tx->info[idx], 21158c2ecf20Sopenharmony_ci bus), len, 21168c2ecf20Sopenharmony_ci PCI_DMA_TODEVICE); 21178c2ecf20Sopenharmony_ci } else { 21188c2ecf20Sopenharmony_ci if (len) 21198c2ecf20Sopenharmony_ci pci_unmap_page(mgp->pdev, 21208c2ecf20Sopenharmony_ci dma_unmap_addr(&tx->info[idx], 21218c2ecf20Sopenharmony_ci bus), len, 21228c2ecf20Sopenharmony_ci PCI_DMA_TODEVICE); 21238c2ecf20Sopenharmony_ci } 21248c2ecf20Sopenharmony_ci } 21258c2ecf20Sopenharmony_ci kfree(ss->rx_big.info); 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci kfree(ss->rx_small.info); 21288c2ecf20Sopenharmony_ci 21298c2ecf20Sopenharmony_ci kfree(ss->tx.info); 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_ci kfree(ss->rx_big.shadow); 21328c2ecf20Sopenharmony_ci 21338c2ecf20Sopenharmony_ci kfree(ss->rx_small.shadow); 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci kfree(ss->tx.req_bytes); 21368c2ecf20Sopenharmony_ci ss->tx.req_bytes = NULL; 21378c2ecf20Sopenharmony_ci ss->tx.req_list = NULL; 21388c2ecf20Sopenharmony_ci} 21398c2ecf20Sopenharmony_ci 21408c2ecf20Sopenharmony_cistatic int myri10ge_request_irq(struct myri10ge_priv *mgp) 21418c2ecf20Sopenharmony_ci{ 21428c2ecf20Sopenharmony_ci struct pci_dev *pdev = mgp->pdev; 21438c2ecf20Sopenharmony_ci struct myri10ge_slice_state *ss; 21448c2ecf20Sopenharmony_ci struct net_device *netdev = mgp->dev; 21458c2ecf20Sopenharmony_ci int i; 21468c2ecf20Sopenharmony_ci int status; 21478c2ecf20Sopenharmony_ci 21488c2ecf20Sopenharmony_ci mgp->msi_enabled = 0; 21498c2ecf20Sopenharmony_ci mgp->msix_enabled = 0; 21508c2ecf20Sopenharmony_ci status = 0; 21518c2ecf20Sopenharmony_ci if (myri10ge_msi) { 21528c2ecf20Sopenharmony_ci if (mgp->num_slices > 1) { 21538c2ecf20Sopenharmony_ci status = pci_enable_msix_range(pdev, mgp->msix_vectors, 21548c2ecf20Sopenharmony_ci mgp->num_slices, mgp->num_slices); 21558c2ecf20Sopenharmony_ci if (status < 0) { 21568c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 21578c2ecf20Sopenharmony_ci "Error %d setting up MSI-X\n", status); 21588c2ecf20Sopenharmony_ci return status; 21598c2ecf20Sopenharmony_ci } 21608c2ecf20Sopenharmony_ci mgp->msix_enabled = 1; 21618c2ecf20Sopenharmony_ci } 21628c2ecf20Sopenharmony_ci if (mgp->msix_enabled == 0) { 21638c2ecf20Sopenharmony_ci status = pci_enable_msi(pdev); 21648c2ecf20Sopenharmony_ci if (status != 0) { 21658c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 21668c2ecf20Sopenharmony_ci "Error %d setting up MSI; falling back to xPIC\n", 21678c2ecf20Sopenharmony_ci status); 21688c2ecf20Sopenharmony_ci } else { 21698c2ecf20Sopenharmony_ci mgp->msi_enabled = 1; 21708c2ecf20Sopenharmony_ci } 21718c2ecf20Sopenharmony_ci } 21728c2ecf20Sopenharmony_ci } 21738c2ecf20Sopenharmony_ci if (mgp->msix_enabled) { 21748c2ecf20Sopenharmony_ci for (i = 0; i < mgp->num_slices; i++) { 21758c2ecf20Sopenharmony_ci ss = &mgp->ss[i]; 21768c2ecf20Sopenharmony_ci snprintf(ss->irq_desc, sizeof(ss->irq_desc), 21778c2ecf20Sopenharmony_ci "%s:slice-%d", netdev->name, i); 21788c2ecf20Sopenharmony_ci status = request_irq(mgp->msix_vectors[i].vector, 21798c2ecf20Sopenharmony_ci myri10ge_intr, 0, ss->irq_desc, 21808c2ecf20Sopenharmony_ci ss); 21818c2ecf20Sopenharmony_ci if (status != 0) { 21828c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 21838c2ecf20Sopenharmony_ci "slice %d failed to allocate IRQ\n", i); 21848c2ecf20Sopenharmony_ci i--; 21858c2ecf20Sopenharmony_ci while (i >= 0) { 21868c2ecf20Sopenharmony_ci free_irq(mgp->msix_vectors[i].vector, 21878c2ecf20Sopenharmony_ci &mgp->ss[i]); 21888c2ecf20Sopenharmony_ci i--; 21898c2ecf20Sopenharmony_ci } 21908c2ecf20Sopenharmony_ci pci_disable_msix(pdev); 21918c2ecf20Sopenharmony_ci return status; 21928c2ecf20Sopenharmony_ci } 21938c2ecf20Sopenharmony_ci } 21948c2ecf20Sopenharmony_ci } else { 21958c2ecf20Sopenharmony_ci status = request_irq(pdev->irq, myri10ge_intr, IRQF_SHARED, 21968c2ecf20Sopenharmony_ci mgp->dev->name, &mgp->ss[0]); 21978c2ecf20Sopenharmony_ci if (status != 0) { 21988c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to allocate IRQ\n"); 21998c2ecf20Sopenharmony_ci if (mgp->msi_enabled) 22008c2ecf20Sopenharmony_ci pci_disable_msi(pdev); 22018c2ecf20Sopenharmony_ci } 22028c2ecf20Sopenharmony_ci } 22038c2ecf20Sopenharmony_ci return status; 22048c2ecf20Sopenharmony_ci} 22058c2ecf20Sopenharmony_ci 22068c2ecf20Sopenharmony_cistatic void myri10ge_free_irq(struct myri10ge_priv *mgp) 22078c2ecf20Sopenharmony_ci{ 22088c2ecf20Sopenharmony_ci struct pci_dev *pdev = mgp->pdev; 22098c2ecf20Sopenharmony_ci int i; 22108c2ecf20Sopenharmony_ci 22118c2ecf20Sopenharmony_ci if (mgp->msix_enabled) { 22128c2ecf20Sopenharmony_ci for (i = 0; i < mgp->num_slices; i++) 22138c2ecf20Sopenharmony_ci free_irq(mgp->msix_vectors[i].vector, &mgp->ss[i]); 22148c2ecf20Sopenharmony_ci } else { 22158c2ecf20Sopenharmony_ci free_irq(pdev->irq, &mgp->ss[0]); 22168c2ecf20Sopenharmony_ci } 22178c2ecf20Sopenharmony_ci if (mgp->msi_enabled) 22188c2ecf20Sopenharmony_ci pci_disable_msi(pdev); 22198c2ecf20Sopenharmony_ci if (mgp->msix_enabled) 22208c2ecf20Sopenharmony_ci pci_disable_msix(pdev); 22218c2ecf20Sopenharmony_ci} 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_cistatic int myri10ge_get_txrx(struct myri10ge_priv *mgp, int slice) 22248c2ecf20Sopenharmony_ci{ 22258c2ecf20Sopenharmony_ci struct myri10ge_cmd cmd; 22268c2ecf20Sopenharmony_ci struct myri10ge_slice_state *ss; 22278c2ecf20Sopenharmony_ci int status; 22288c2ecf20Sopenharmony_ci 22298c2ecf20Sopenharmony_ci ss = &mgp->ss[slice]; 22308c2ecf20Sopenharmony_ci status = 0; 22318c2ecf20Sopenharmony_ci if (slice == 0 || (mgp->dev->real_num_tx_queues > 1)) { 22328c2ecf20Sopenharmony_ci cmd.data0 = slice; 22338c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_SEND_OFFSET, 22348c2ecf20Sopenharmony_ci &cmd, 0); 22358c2ecf20Sopenharmony_ci ss->tx.lanai = (struct mcp_kreq_ether_send __iomem *) 22368c2ecf20Sopenharmony_ci (mgp->sram + cmd.data0); 22378c2ecf20Sopenharmony_ci } 22388c2ecf20Sopenharmony_ci cmd.data0 = slice; 22398c2ecf20Sopenharmony_ci status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_SMALL_RX_OFFSET, 22408c2ecf20Sopenharmony_ci &cmd, 0); 22418c2ecf20Sopenharmony_ci ss->rx_small.lanai = (struct mcp_kreq_ether_recv __iomem *) 22428c2ecf20Sopenharmony_ci (mgp->sram + cmd.data0); 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_ci cmd.data0 = slice; 22458c2ecf20Sopenharmony_ci status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd, 0); 22468c2ecf20Sopenharmony_ci ss->rx_big.lanai = (struct mcp_kreq_ether_recv __iomem *) 22478c2ecf20Sopenharmony_ci (mgp->sram + cmd.data0); 22488c2ecf20Sopenharmony_ci 22498c2ecf20Sopenharmony_ci ss->tx.send_go = (__iomem __be32 *) 22508c2ecf20Sopenharmony_ci (mgp->sram + MXGEFW_ETH_SEND_GO + 64 * slice); 22518c2ecf20Sopenharmony_ci ss->tx.send_stop = (__iomem __be32 *) 22528c2ecf20Sopenharmony_ci (mgp->sram + MXGEFW_ETH_SEND_STOP + 64 * slice); 22538c2ecf20Sopenharmony_ci return status; 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_ci} 22568c2ecf20Sopenharmony_ci 22578c2ecf20Sopenharmony_cistatic int myri10ge_set_stats(struct myri10ge_priv *mgp, int slice) 22588c2ecf20Sopenharmony_ci{ 22598c2ecf20Sopenharmony_ci struct myri10ge_cmd cmd; 22608c2ecf20Sopenharmony_ci struct myri10ge_slice_state *ss; 22618c2ecf20Sopenharmony_ci int status; 22628c2ecf20Sopenharmony_ci 22638c2ecf20Sopenharmony_ci ss = &mgp->ss[slice]; 22648c2ecf20Sopenharmony_ci cmd.data0 = MYRI10GE_LOWPART_TO_U32(ss->fw_stats_bus); 22658c2ecf20Sopenharmony_ci cmd.data1 = MYRI10GE_HIGHPART_TO_U32(ss->fw_stats_bus); 22668c2ecf20Sopenharmony_ci cmd.data2 = sizeof(struct mcp_irq_data) | (slice << 16); 22678c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd, 0); 22688c2ecf20Sopenharmony_ci if (status == -ENOSYS) { 22698c2ecf20Sopenharmony_ci dma_addr_t bus = ss->fw_stats_bus; 22708c2ecf20Sopenharmony_ci if (slice != 0) 22718c2ecf20Sopenharmony_ci return -EINVAL; 22728c2ecf20Sopenharmony_ci bus += offsetof(struct mcp_irq_data, send_done_count); 22738c2ecf20Sopenharmony_ci cmd.data0 = MYRI10GE_LOWPART_TO_U32(bus); 22748c2ecf20Sopenharmony_ci cmd.data1 = MYRI10GE_HIGHPART_TO_U32(bus); 22758c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, 22768c2ecf20Sopenharmony_ci MXGEFW_CMD_SET_STATS_DMA_OBSOLETE, 22778c2ecf20Sopenharmony_ci &cmd, 0); 22788c2ecf20Sopenharmony_ci /* Firmware cannot support multicast without STATS_DMA_V2 */ 22798c2ecf20Sopenharmony_ci mgp->fw_multicast_support = 0; 22808c2ecf20Sopenharmony_ci } else { 22818c2ecf20Sopenharmony_ci mgp->fw_multicast_support = 1; 22828c2ecf20Sopenharmony_ci } 22838c2ecf20Sopenharmony_ci return 0; 22848c2ecf20Sopenharmony_ci} 22858c2ecf20Sopenharmony_ci 22868c2ecf20Sopenharmony_cistatic int myri10ge_open(struct net_device *dev) 22878c2ecf20Sopenharmony_ci{ 22888c2ecf20Sopenharmony_ci struct myri10ge_slice_state *ss; 22898c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = netdev_priv(dev); 22908c2ecf20Sopenharmony_ci struct myri10ge_cmd cmd; 22918c2ecf20Sopenharmony_ci int i, status, big_pow2, slice; 22928c2ecf20Sopenharmony_ci u8 __iomem *itable; 22938c2ecf20Sopenharmony_ci 22948c2ecf20Sopenharmony_ci if (mgp->running != MYRI10GE_ETH_STOPPED) 22958c2ecf20Sopenharmony_ci return -EBUSY; 22968c2ecf20Sopenharmony_ci 22978c2ecf20Sopenharmony_ci mgp->running = MYRI10GE_ETH_STARTING; 22988c2ecf20Sopenharmony_ci status = myri10ge_reset(mgp); 22998c2ecf20Sopenharmony_ci if (status != 0) { 23008c2ecf20Sopenharmony_ci netdev_err(dev, "failed reset\n"); 23018c2ecf20Sopenharmony_ci goto abort_with_nothing; 23028c2ecf20Sopenharmony_ci } 23038c2ecf20Sopenharmony_ci 23048c2ecf20Sopenharmony_ci if (mgp->num_slices > 1) { 23058c2ecf20Sopenharmony_ci cmd.data0 = mgp->num_slices; 23068c2ecf20Sopenharmony_ci cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE; 23078c2ecf20Sopenharmony_ci if (mgp->dev->real_num_tx_queues > 1) 23088c2ecf20Sopenharmony_ci cmd.data1 |= MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES; 23098c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, MXGEFW_CMD_ENABLE_RSS_QUEUES, 23108c2ecf20Sopenharmony_ci &cmd, 0); 23118c2ecf20Sopenharmony_ci if (status != 0) { 23128c2ecf20Sopenharmony_ci netdev_err(dev, "failed to set number of slices\n"); 23138c2ecf20Sopenharmony_ci goto abort_with_nothing; 23148c2ecf20Sopenharmony_ci } 23158c2ecf20Sopenharmony_ci /* setup the indirection table */ 23168c2ecf20Sopenharmony_ci cmd.data0 = mgp->num_slices; 23178c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_RSS_TABLE_SIZE, 23188c2ecf20Sopenharmony_ci &cmd, 0); 23198c2ecf20Sopenharmony_ci 23208c2ecf20Sopenharmony_ci status |= myri10ge_send_cmd(mgp, 23218c2ecf20Sopenharmony_ci MXGEFW_CMD_GET_RSS_TABLE_OFFSET, 23228c2ecf20Sopenharmony_ci &cmd, 0); 23238c2ecf20Sopenharmony_ci if (status != 0) { 23248c2ecf20Sopenharmony_ci netdev_err(dev, "failed to setup rss tables\n"); 23258c2ecf20Sopenharmony_ci goto abort_with_nothing; 23268c2ecf20Sopenharmony_ci } 23278c2ecf20Sopenharmony_ci 23288c2ecf20Sopenharmony_ci /* just enable an identity mapping */ 23298c2ecf20Sopenharmony_ci itable = mgp->sram + cmd.data0; 23308c2ecf20Sopenharmony_ci for (i = 0; i < mgp->num_slices; i++) 23318c2ecf20Sopenharmony_ci __raw_writeb(i, &itable[i]); 23328c2ecf20Sopenharmony_ci 23338c2ecf20Sopenharmony_ci cmd.data0 = 1; 23348c2ecf20Sopenharmony_ci cmd.data1 = myri10ge_rss_hash; 23358c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_RSS_ENABLE, 23368c2ecf20Sopenharmony_ci &cmd, 0); 23378c2ecf20Sopenharmony_ci if (status != 0) { 23388c2ecf20Sopenharmony_ci netdev_err(dev, "failed to enable slices\n"); 23398c2ecf20Sopenharmony_ci goto abort_with_nothing; 23408c2ecf20Sopenharmony_ci } 23418c2ecf20Sopenharmony_ci } 23428c2ecf20Sopenharmony_ci 23438c2ecf20Sopenharmony_ci status = myri10ge_request_irq(mgp); 23448c2ecf20Sopenharmony_ci if (status != 0) 23458c2ecf20Sopenharmony_ci goto abort_with_nothing; 23468c2ecf20Sopenharmony_ci 23478c2ecf20Sopenharmony_ci /* decide what small buffer size to use. For good TCP rx 23488c2ecf20Sopenharmony_ci * performance, it is important to not receive 1514 byte 23498c2ecf20Sopenharmony_ci * frames into jumbo buffers, as it confuses the socket buffer 23508c2ecf20Sopenharmony_ci * accounting code, leading to drops and erratic performance. 23518c2ecf20Sopenharmony_ci */ 23528c2ecf20Sopenharmony_ci 23538c2ecf20Sopenharmony_ci if (dev->mtu <= ETH_DATA_LEN) 23548c2ecf20Sopenharmony_ci /* enough for a TCP header */ 23558c2ecf20Sopenharmony_ci mgp->small_bytes = (128 > SMP_CACHE_BYTES) 23568c2ecf20Sopenharmony_ci ? (128 - MXGEFW_PAD) 23578c2ecf20Sopenharmony_ci : (SMP_CACHE_BYTES - MXGEFW_PAD); 23588c2ecf20Sopenharmony_ci else 23598c2ecf20Sopenharmony_ci /* enough for a vlan encapsulated ETH_DATA_LEN frame */ 23608c2ecf20Sopenharmony_ci mgp->small_bytes = VLAN_ETH_FRAME_LEN; 23618c2ecf20Sopenharmony_ci 23628c2ecf20Sopenharmony_ci /* Override the small buffer size? */ 23638c2ecf20Sopenharmony_ci if (myri10ge_small_bytes >= 0) 23648c2ecf20Sopenharmony_ci mgp->small_bytes = myri10ge_small_bytes; 23658c2ecf20Sopenharmony_ci 23668c2ecf20Sopenharmony_ci /* Firmware needs the big buff size as a power of 2. Lie and 23678c2ecf20Sopenharmony_ci * tell him the buffer is larger, because we only use 1 23688c2ecf20Sopenharmony_ci * buffer/pkt, and the mtu will prevent overruns. 23698c2ecf20Sopenharmony_ci */ 23708c2ecf20Sopenharmony_ci big_pow2 = dev->mtu + ETH_HLEN + VLAN_HLEN + MXGEFW_PAD; 23718c2ecf20Sopenharmony_ci if (big_pow2 < MYRI10GE_ALLOC_SIZE / 2) { 23728c2ecf20Sopenharmony_ci while (!is_power_of_2(big_pow2)) 23738c2ecf20Sopenharmony_ci big_pow2++; 23748c2ecf20Sopenharmony_ci mgp->big_bytes = dev->mtu + ETH_HLEN + VLAN_HLEN + MXGEFW_PAD; 23758c2ecf20Sopenharmony_ci } else { 23768c2ecf20Sopenharmony_ci big_pow2 = MYRI10GE_ALLOC_SIZE; 23778c2ecf20Sopenharmony_ci mgp->big_bytes = big_pow2; 23788c2ecf20Sopenharmony_ci } 23798c2ecf20Sopenharmony_ci 23808c2ecf20Sopenharmony_ci /* setup the per-slice data structures */ 23818c2ecf20Sopenharmony_ci for (slice = 0; slice < mgp->num_slices; slice++) { 23828c2ecf20Sopenharmony_ci ss = &mgp->ss[slice]; 23838c2ecf20Sopenharmony_ci 23848c2ecf20Sopenharmony_ci status = myri10ge_get_txrx(mgp, slice); 23858c2ecf20Sopenharmony_ci if (status != 0) { 23868c2ecf20Sopenharmony_ci netdev_err(dev, "failed to get ring sizes or locations\n"); 23878c2ecf20Sopenharmony_ci goto abort_with_rings; 23888c2ecf20Sopenharmony_ci } 23898c2ecf20Sopenharmony_ci status = myri10ge_allocate_rings(ss); 23908c2ecf20Sopenharmony_ci if (status != 0) 23918c2ecf20Sopenharmony_ci goto abort_with_rings; 23928c2ecf20Sopenharmony_ci 23938c2ecf20Sopenharmony_ci /* only firmware which supports multiple TX queues 23948c2ecf20Sopenharmony_ci * supports setting up the tx stats on non-zero 23958c2ecf20Sopenharmony_ci * slices */ 23968c2ecf20Sopenharmony_ci if (slice == 0 || mgp->dev->real_num_tx_queues > 1) 23978c2ecf20Sopenharmony_ci status = myri10ge_set_stats(mgp, slice); 23988c2ecf20Sopenharmony_ci if (status) { 23998c2ecf20Sopenharmony_ci netdev_err(dev, "Couldn't set stats DMA\n"); 24008c2ecf20Sopenharmony_ci goto abort_with_rings; 24018c2ecf20Sopenharmony_ci } 24028c2ecf20Sopenharmony_ci 24038c2ecf20Sopenharmony_ci /* must happen prior to any irq */ 24048c2ecf20Sopenharmony_ci napi_enable(&(ss)->napi); 24058c2ecf20Sopenharmony_ci } 24068c2ecf20Sopenharmony_ci 24078c2ecf20Sopenharmony_ci /* now give firmware buffers sizes, and MTU */ 24088c2ecf20Sopenharmony_ci cmd.data0 = dev->mtu + ETH_HLEN + VLAN_HLEN; 24098c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_MTU, &cmd, 0); 24108c2ecf20Sopenharmony_ci cmd.data0 = mgp->small_bytes; 24118c2ecf20Sopenharmony_ci status |= 24128c2ecf20Sopenharmony_ci myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE, &cmd, 0); 24138c2ecf20Sopenharmony_ci cmd.data0 = big_pow2; 24148c2ecf20Sopenharmony_ci status |= 24158c2ecf20Sopenharmony_ci myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd, 0); 24168c2ecf20Sopenharmony_ci if (status) { 24178c2ecf20Sopenharmony_ci netdev_err(dev, "Couldn't set buffer sizes\n"); 24188c2ecf20Sopenharmony_ci goto abort_with_rings; 24198c2ecf20Sopenharmony_ci } 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_ci /* 24228c2ecf20Sopenharmony_ci * Set Linux style TSO mode; this is needed only on newer 24238c2ecf20Sopenharmony_ci * firmware versions. Older versions default to Linux 24248c2ecf20Sopenharmony_ci * style TSO 24258c2ecf20Sopenharmony_ci */ 24268c2ecf20Sopenharmony_ci cmd.data0 = 0; 24278c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_TSO_MODE, &cmd, 0); 24288c2ecf20Sopenharmony_ci if (status && status != -ENOSYS) { 24298c2ecf20Sopenharmony_ci netdev_err(dev, "Couldn't set TSO mode\n"); 24308c2ecf20Sopenharmony_ci goto abort_with_rings; 24318c2ecf20Sopenharmony_ci } 24328c2ecf20Sopenharmony_ci 24338c2ecf20Sopenharmony_ci mgp->link_state = ~0U; 24348c2ecf20Sopenharmony_ci mgp->rdma_tags_available = 15; 24358c2ecf20Sopenharmony_ci 24368c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, MXGEFW_CMD_ETHERNET_UP, &cmd, 0); 24378c2ecf20Sopenharmony_ci if (status) { 24388c2ecf20Sopenharmony_ci netdev_err(dev, "Couldn't bring up link\n"); 24398c2ecf20Sopenharmony_ci goto abort_with_rings; 24408c2ecf20Sopenharmony_ci } 24418c2ecf20Sopenharmony_ci 24428c2ecf20Sopenharmony_ci mgp->running = MYRI10GE_ETH_RUNNING; 24438c2ecf20Sopenharmony_ci mgp->watchdog_timer.expires = jiffies + myri10ge_watchdog_timeout * HZ; 24448c2ecf20Sopenharmony_ci add_timer(&mgp->watchdog_timer); 24458c2ecf20Sopenharmony_ci netif_tx_wake_all_queues(dev); 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_ci return 0; 24488c2ecf20Sopenharmony_ci 24498c2ecf20Sopenharmony_ciabort_with_rings: 24508c2ecf20Sopenharmony_ci while (slice) { 24518c2ecf20Sopenharmony_ci slice--; 24528c2ecf20Sopenharmony_ci napi_disable(&mgp->ss[slice].napi); 24538c2ecf20Sopenharmony_ci } 24548c2ecf20Sopenharmony_ci for (i = 0; i < mgp->num_slices; i++) 24558c2ecf20Sopenharmony_ci myri10ge_free_rings(&mgp->ss[i]); 24568c2ecf20Sopenharmony_ci 24578c2ecf20Sopenharmony_ci myri10ge_free_irq(mgp); 24588c2ecf20Sopenharmony_ci 24598c2ecf20Sopenharmony_ciabort_with_nothing: 24608c2ecf20Sopenharmony_ci mgp->running = MYRI10GE_ETH_STOPPED; 24618c2ecf20Sopenharmony_ci return -ENOMEM; 24628c2ecf20Sopenharmony_ci} 24638c2ecf20Sopenharmony_ci 24648c2ecf20Sopenharmony_cistatic int myri10ge_close(struct net_device *dev) 24658c2ecf20Sopenharmony_ci{ 24668c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = netdev_priv(dev); 24678c2ecf20Sopenharmony_ci struct myri10ge_cmd cmd; 24688c2ecf20Sopenharmony_ci int status, old_down_cnt; 24698c2ecf20Sopenharmony_ci int i; 24708c2ecf20Sopenharmony_ci 24718c2ecf20Sopenharmony_ci if (mgp->running != MYRI10GE_ETH_RUNNING) 24728c2ecf20Sopenharmony_ci return 0; 24738c2ecf20Sopenharmony_ci 24748c2ecf20Sopenharmony_ci if (mgp->ss[0].tx.req_bytes == NULL) 24758c2ecf20Sopenharmony_ci return 0; 24768c2ecf20Sopenharmony_ci 24778c2ecf20Sopenharmony_ci del_timer_sync(&mgp->watchdog_timer); 24788c2ecf20Sopenharmony_ci mgp->running = MYRI10GE_ETH_STOPPING; 24798c2ecf20Sopenharmony_ci for (i = 0; i < mgp->num_slices; i++) 24808c2ecf20Sopenharmony_ci napi_disable(&mgp->ss[i].napi); 24818c2ecf20Sopenharmony_ci 24828c2ecf20Sopenharmony_ci netif_carrier_off(dev); 24838c2ecf20Sopenharmony_ci 24848c2ecf20Sopenharmony_ci netif_tx_stop_all_queues(dev); 24858c2ecf20Sopenharmony_ci if (mgp->rebooted == 0) { 24868c2ecf20Sopenharmony_ci old_down_cnt = mgp->down_cnt; 24878c2ecf20Sopenharmony_ci mb(); 24888c2ecf20Sopenharmony_ci status = 24898c2ecf20Sopenharmony_ci myri10ge_send_cmd(mgp, MXGEFW_CMD_ETHERNET_DOWN, &cmd, 0); 24908c2ecf20Sopenharmony_ci if (status) 24918c2ecf20Sopenharmony_ci netdev_err(dev, "Couldn't bring down link\n"); 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_ci wait_event_timeout(mgp->down_wq, old_down_cnt != mgp->down_cnt, 24948c2ecf20Sopenharmony_ci HZ); 24958c2ecf20Sopenharmony_ci if (old_down_cnt == mgp->down_cnt) 24968c2ecf20Sopenharmony_ci netdev_err(dev, "never got down irq\n"); 24978c2ecf20Sopenharmony_ci } 24988c2ecf20Sopenharmony_ci netif_tx_disable(dev); 24998c2ecf20Sopenharmony_ci myri10ge_free_irq(mgp); 25008c2ecf20Sopenharmony_ci for (i = 0; i < mgp->num_slices; i++) 25018c2ecf20Sopenharmony_ci myri10ge_free_rings(&mgp->ss[i]); 25028c2ecf20Sopenharmony_ci 25038c2ecf20Sopenharmony_ci mgp->running = MYRI10GE_ETH_STOPPED; 25048c2ecf20Sopenharmony_ci return 0; 25058c2ecf20Sopenharmony_ci} 25068c2ecf20Sopenharmony_ci 25078c2ecf20Sopenharmony_ci/* copy an array of struct mcp_kreq_ether_send's to the mcp. Copy 25088c2ecf20Sopenharmony_ci * backwards one at a time and handle ring wraps */ 25098c2ecf20Sopenharmony_ci 25108c2ecf20Sopenharmony_cistatic inline void 25118c2ecf20Sopenharmony_cimyri10ge_submit_req_backwards(struct myri10ge_tx_buf *tx, 25128c2ecf20Sopenharmony_ci struct mcp_kreq_ether_send *src, int cnt) 25138c2ecf20Sopenharmony_ci{ 25148c2ecf20Sopenharmony_ci int idx, starting_slot; 25158c2ecf20Sopenharmony_ci starting_slot = tx->req; 25168c2ecf20Sopenharmony_ci while (cnt > 1) { 25178c2ecf20Sopenharmony_ci cnt--; 25188c2ecf20Sopenharmony_ci idx = (starting_slot + cnt) & tx->mask; 25198c2ecf20Sopenharmony_ci myri10ge_pio_copy(&tx->lanai[idx], &src[cnt], sizeof(*src)); 25208c2ecf20Sopenharmony_ci mb(); 25218c2ecf20Sopenharmony_ci } 25228c2ecf20Sopenharmony_ci} 25238c2ecf20Sopenharmony_ci 25248c2ecf20Sopenharmony_ci/* 25258c2ecf20Sopenharmony_ci * copy an array of struct mcp_kreq_ether_send's to the mcp. Copy 25268c2ecf20Sopenharmony_ci * at most 32 bytes at a time, so as to avoid involving the software 25278c2ecf20Sopenharmony_ci * pio handler in the nic. We re-write the first segment's flags 25288c2ecf20Sopenharmony_ci * to mark them valid only after writing the entire chain. 25298c2ecf20Sopenharmony_ci */ 25308c2ecf20Sopenharmony_ci 25318c2ecf20Sopenharmony_cistatic inline void 25328c2ecf20Sopenharmony_cimyri10ge_submit_req(struct myri10ge_tx_buf *tx, struct mcp_kreq_ether_send *src, 25338c2ecf20Sopenharmony_ci int cnt) 25348c2ecf20Sopenharmony_ci{ 25358c2ecf20Sopenharmony_ci int idx, i; 25368c2ecf20Sopenharmony_ci struct mcp_kreq_ether_send __iomem *dstp, *dst; 25378c2ecf20Sopenharmony_ci struct mcp_kreq_ether_send *srcp; 25388c2ecf20Sopenharmony_ci u8 last_flags; 25398c2ecf20Sopenharmony_ci 25408c2ecf20Sopenharmony_ci idx = tx->req & tx->mask; 25418c2ecf20Sopenharmony_ci 25428c2ecf20Sopenharmony_ci last_flags = src->flags; 25438c2ecf20Sopenharmony_ci src->flags = 0; 25448c2ecf20Sopenharmony_ci mb(); 25458c2ecf20Sopenharmony_ci dst = dstp = &tx->lanai[idx]; 25468c2ecf20Sopenharmony_ci srcp = src; 25478c2ecf20Sopenharmony_ci 25488c2ecf20Sopenharmony_ci if ((idx + cnt) < tx->mask) { 25498c2ecf20Sopenharmony_ci for (i = 0; i < (cnt - 1); i += 2) { 25508c2ecf20Sopenharmony_ci myri10ge_pio_copy(dstp, srcp, 2 * sizeof(*src)); 25518c2ecf20Sopenharmony_ci mb(); /* force write every 32 bytes */ 25528c2ecf20Sopenharmony_ci srcp += 2; 25538c2ecf20Sopenharmony_ci dstp += 2; 25548c2ecf20Sopenharmony_ci } 25558c2ecf20Sopenharmony_ci } else { 25568c2ecf20Sopenharmony_ci /* submit all but the first request, and ensure 25578c2ecf20Sopenharmony_ci * that it is submitted below */ 25588c2ecf20Sopenharmony_ci myri10ge_submit_req_backwards(tx, src, cnt); 25598c2ecf20Sopenharmony_ci i = 0; 25608c2ecf20Sopenharmony_ci } 25618c2ecf20Sopenharmony_ci if (i < cnt) { 25628c2ecf20Sopenharmony_ci /* submit the first request */ 25638c2ecf20Sopenharmony_ci myri10ge_pio_copy(dstp, srcp, sizeof(*src)); 25648c2ecf20Sopenharmony_ci mb(); /* barrier before setting valid flag */ 25658c2ecf20Sopenharmony_ci } 25668c2ecf20Sopenharmony_ci 25678c2ecf20Sopenharmony_ci /* re-write the last 32-bits with the valid flags */ 25688c2ecf20Sopenharmony_ci src->flags = last_flags; 25698c2ecf20Sopenharmony_ci put_be32(*((__be32 *) src + 3), (__be32 __iomem *) dst + 3); 25708c2ecf20Sopenharmony_ci tx->req += cnt; 25718c2ecf20Sopenharmony_ci mb(); 25728c2ecf20Sopenharmony_ci} 25738c2ecf20Sopenharmony_ci 25748c2ecf20Sopenharmony_cistatic void myri10ge_unmap_tx_dma(struct myri10ge_priv *mgp, 25758c2ecf20Sopenharmony_ci struct myri10ge_tx_buf *tx, int idx) 25768c2ecf20Sopenharmony_ci{ 25778c2ecf20Sopenharmony_ci unsigned int len; 25788c2ecf20Sopenharmony_ci int last_idx; 25798c2ecf20Sopenharmony_ci 25808c2ecf20Sopenharmony_ci /* Free any DMA resources we've alloced and clear out the skb slot */ 25818c2ecf20Sopenharmony_ci last_idx = (idx + 1) & tx->mask; 25828c2ecf20Sopenharmony_ci idx = tx->req & tx->mask; 25838c2ecf20Sopenharmony_ci do { 25848c2ecf20Sopenharmony_ci len = dma_unmap_len(&tx->info[idx], len); 25858c2ecf20Sopenharmony_ci if (len) { 25868c2ecf20Sopenharmony_ci if (tx->info[idx].skb != NULL) 25878c2ecf20Sopenharmony_ci pci_unmap_single(mgp->pdev, 25888c2ecf20Sopenharmony_ci dma_unmap_addr(&tx->info[idx], 25898c2ecf20Sopenharmony_ci bus), len, 25908c2ecf20Sopenharmony_ci PCI_DMA_TODEVICE); 25918c2ecf20Sopenharmony_ci else 25928c2ecf20Sopenharmony_ci pci_unmap_page(mgp->pdev, 25938c2ecf20Sopenharmony_ci dma_unmap_addr(&tx->info[idx], 25948c2ecf20Sopenharmony_ci bus), len, 25958c2ecf20Sopenharmony_ci PCI_DMA_TODEVICE); 25968c2ecf20Sopenharmony_ci dma_unmap_len_set(&tx->info[idx], len, 0); 25978c2ecf20Sopenharmony_ci tx->info[idx].skb = NULL; 25988c2ecf20Sopenharmony_ci } 25998c2ecf20Sopenharmony_ci idx = (idx + 1) & tx->mask; 26008c2ecf20Sopenharmony_ci } while (idx != last_idx); 26018c2ecf20Sopenharmony_ci} 26028c2ecf20Sopenharmony_ci 26038c2ecf20Sopenharmony_ci/* 26048c2ecf20Sopenharmony_ci * Transmit a packet. We need to split the packet so that a single 26058c2ecf20Sopenharmony_ci * segment does not cross myri10ge->tx_boundary, so this makes segment 26068c2ecf20Sopenharmony_ci * counting tricky. So rather than try to count segments up front, we 26078c2ecf20Sopenharmony_ci * just give up if there are too few segments to hold a reasonably 26088c2ecf20Sopenharmony_ci * fragmented packet currently available. If we run 26098c2ecf20Sopenharmony_ci * out of segments while preparing a packet for DMA, we just linearize 26108c2ecf20Sopenharmony_ci * it and try again. 26118c2ecf20Sopenharmony_ci */ 26128c2ecf20Sopenharmony_ci 26138c2ecf20Sopenharmony_cistatic netdev_tx_t myri10ge_xmit(struct sk_buff *skb, 26148c2ecf20Sopenharmony_ci struct net_device *dev) 26158c2ecf20Sopenharmony_ci{ 26168c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = netdev_priv(dev); 26178c2ecf20Sopenharmony_ci struct myri10ge_slice_state *ss; 26188c2ecf20Sopenharmony_ci struct mcp_kreq_ether_send *req; 26198c2ecf20Sopenharmony_ci struct myri10ge_tx_buf *tx; 26208c2ecf20Sopenharmony_ci skb_frag_t *frag; 26218c2ecf20Sopenharmony_ci struct netdev_queue *netdev_queue; 26228c2ecf20Sopenharmony_ci dma_addr_t bus; 26238c2ecf20Sopenharmony_ci u32 low; 26248c2ecf20Sopenharmony_ci __be32 high_swapped; 26258c2ecf20Sopenharmony_ci unsigned int len; 26268c2ecf20Sopenharmony_ci int idx, avail, frag_cnt, frag_idx, count, mss, max_segments; 26278c2ecf20Sopenharmony_ci u16 pseudo_hdr_offset, cksum_offset, queue; 26288c2ecf20Sopenharmony_ci int cum_len, seglen, boundary, rdma_count; 26298c2ecf20Sopenharmony_ci u8 flags, odd_flag; 26308c2ecf20Sopenharmony_ci 26318c2ecf20Sopenharmony_ci queue = skb_get_queue_mapping(skb); 26328c2ecf20Sopenharmony_ci ss = &mgp->ss[queue]; 26338c2ecf20Sopenharmony_ci netdev_queue = netdev_get_tx_queue(mgp->dev, queue); 26348c2ecf20Sopenharmony_ci tx = &ss->tx; 26358c2ecf20Sopenharmony_ci 26368c2ecf20Sopenharmony_ciagain: 26378c2ecf20Sopenharmony_ci req = tx->req_list; 26388c2ecf20Sopenharmony_ci avail = tx->mask - 1 - (tx->req - tx->done); 26398c2ecf20Sopenharmony_ci 26408c2ecf20Sopenharmony_ci mss = 0; 26418c2ecf20Sopenharmony_ci max_segments = MXGEFW_MAX_SEND_DESC; 26428c2ecf20Sopenharmony_ci 26438c2ecf20Sopenharmony_ci if (skb_is_gso(skb)) { 26448c2ecf20Sopenharmony_ci mss = skb_shinfo(skb)->gso_size; 26458c2ecf20Sopenharmony_ci max_segments = MYRI10GE_MAX_SEND_DESC_TSO; 26468c2ecf20Sopenharmony_ci } 26478c2ecf20Sopenharmony_ci 26488c2ecf20Sopenharmony_ci if ((unlikely(avail < max_segments))) { 26498c2ecf20Sopenharmony_ci /* we are out of transmit resources */ 26508c2ecf20Sopenharmony_ci tx->stop_queue++; 26518c2ecf20Sopenharmony_ci netif_tx_stop_queue(netdev_queue); 26528c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 26538c2ecf20Sopenharmony_ci } 26548c2ecf20Sopenharmony_ci 26558c2ecf20Sopenharmony_ci /* Setup checksum offloading, if needed */ 26568c2ecf20Sopenharmony_ci cksum_offset = 0; 26578c2ecf20Sopenharmony_ci pseudo_hdr_offset = 0; 26588c2ecf20Sopenharmony_ci odd_flag = 0; 26598c2ecf20Sopenharmony_ci flags = (MXGEFW_FLAGS_NO_TSO | MXGEFW_FLAGS_FIRST); 26608c2ecf20Sopenharmony_ci if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { 26618c2ecf20Sopenharmony_ci cksum_offset = skb_checksum_start_offset(skb); 26628c2ecf20Sopenharmony_ci pseudo_hdr_offset = cksum_offset + skb->csum_offset; 26638c2ecf20Sopenharmony_ci /* If the headers are excessively large, then we must 26648c2ecf20Sopenharmony_ci * fall back to a software checksum */ 26658c2ecf20Sopenharmony_ci if (unlikely(!mss && (cksum_offset > 255 || 26668c2ecf20Sopenharmony_ci pseudo_hdr_offset > 127))) { 26678c2ecf20Sopenharmony_ci if (skb_checksum_help(skb)) 26688c2ecf20Sopenharmony_ci goto drop; 26698c2ecf20Sopenharmony_ci cksum_offset = 0; 26708c2ecf20Sopenharmony_ci pseudo_hdr_offset = 0; 26718c2ecf20Sopenharmony_ci } else { 26728c2ecf20Sopenharmony_ci odd_flag = MXGEFW_FLAGS_ALIGN_ODD; 26738c2ecf20Sopenharmony_ci flags |= MXGEFW_FLAGS_CKSUM; 26748c2ecf20Sopenharmony_ci } 26758c2ecf20Sopenharmony_ci } 26768c2ecf20Sopenharmony_ci 26778c2ecf20Sopenharmony_ci cum_len = 0; 26788c2ecf20Sopenharmony_ci 26798c2ecf20Sopenharmony_ci if (mss) { /* TSO */ 26808c2ecf20Sopenharmony_ci /* this removes any CKSUM flag from before */ 26818c2ecf20Sopenharmony_ci flags = (MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST); 26828c2ecf20Sopenharmony_ci 26838c2ecf20Sopenharmony_ci /* negative cum_len signifies to the 26848c2ecf20Sopenharmony_ci * send loop that we are still in the 26858c2ecf20Sopenharmony_ci * header portion of the TSO packet. 26868c2ecf20Sopenharmony_ci * TSO header can be at most 1KB long */ 26878c2ecf20Sopenharmony_ci cum_len = -(skb_transport_offset(skb) + tcp_hdrlen(skb)); 26888c2ecf20Sopenharmony_ci 26898c2ecf20Sopenharmony_ci /* for IPv6 TSO, the checksum offset stores the 26908c2ecf20Sopenharmony_ci * TCP header length, to save the firmware from 26918c2ecf20Sopenharmony_ci * the need to parse the headers */ 26928c2ecf20Sopenharmony_ci if (skb_is_gso_v6(skb)) { 26938c2ecf20Sopenharmony_ci cksum_offset = tcp_hdrlen(skb); 26948c2ecf20Sopenharmony_ci /* Can only handle headers <= max_tso6 long */ 26958c2ecf20Sopenharmony_ci if (unlikely(-cum_len > mgp->max_tso6)) 26968c2ecf20Sopenharmony_ci return myri10ge_sw_tso(skb, dev); 26978c2ecf20Sopenharmony_ci } 26988c2ecf20Sopenharmony_ci /* for TSO, pseudo_hdr_offset holds mss. 26998c2ecf20Sopenharmony_ci * The firmware figures out where to put 27008c2ecf20Sopenharmony_ci * the checksum by parsing the header. */ 27018c2ecf20Sopenharmony_ci pseudo_hdr_offset = mss; 27028c2ecf20Sopenharmony_ci } else 27038c2ecf20Sopenharmony_ci /* Mark small packets, and pad out tiny packets */ 27048c2ecf20Sopenharmony_ci if (skb->len <= MXGEFW_SEND_SMALL_SIZE) { 27058c2ecf20Sopenharmony_ci flags |= MXGEFW_FLAGS_SMALL; 27068c2ecf20Sopenharmony_ci 27078c2ecf20Sopenharmony_ci /* pad frames to at least ETH_ZLEN bytes */ 27088c2ecf20Sopenharmony_ci if (eth_skb_pad(skb)) { 27098c2ecf20Sopenharmony_ci /* The packet is gone, so we must 27108c2ecf20Sopenharmony_ci * return 0 */ 27118c2ecf20Sopenharmony_ci ss->stats.tx_dropped += 1; 27128c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 27138c2ecf20Sopenharmony_ci } 27148c2ecf20Sopenharmony_ci } 27158c2ecf20Sopenharmony_ci 27168c2ecf20Sopenharmony_ci /* map the skb for DMA */ 27178c2ecf20Sopenharmony_ci len = skb_headlen(skb); 27188c2ecf20Sopenharmony_ci bus = pci_map_single(mgp->pdev, skb->data, len, PCI_DMA_TODEVICE); 27198c2ecf20Sopenharmony_ci if (unlikely(pci_dma_mapping_error(mgp->pdev, bus))) 27208c2ecf20Sopenharmony_ci goto drop; 27218c2ecf20Sopenharmony_ci 27228c2ecf20Sopenharmony_ci idx = tx->req & tx->mask; 27238c2ecf20Sopenharmony_ci tx->info[idx].skb = skb; 27248c2ecf20Sopenharmony_ci dma_unmap_addr_set(&tx->info[idx], bus, bus); 27258c2ecf20Sopenharmony_ci dma_unmap_len_set(&tx->info[idx], len, len); 27268c2ecf20Sopenharmony_ci 27278c2ecf20Sopenharmony_ci frag_cnt = skb_shinfo(skb)->nr_frags; 27288c2ecf20Sopenharmony_ci frag_idx = 0; 27298c2ecf20Sopenharmony_ci count = 0; 27308c2ecf20Sopenharmony_ci rdma_count = 0; 27318c2ecf20Sopenharmony_ci 27328c2ecf20Sopenharmony_ci /* "rdma_count" is the number of RDMAs belonging to the 27338c2ecf20Sopenharmony_ci * current packet BEFORE the current send request. For 27348c2ecf20Sopenharmony_ci * non-TSO packets, this is equal to "count". 27358c2ecf20Sopenharmony_ci * For TSO packets, rdma_count needs to be reset 27368c2ecf20Sopenharmony_ci * to 0 after a segment cut. 27378c2ecf20Sopenharmony_ci * 27388c2ecf20Sopenharmony_ci * The rdma_count field of the send request is 27398c2ecf20Sopenharmony_ci * the number of RDMAs of the packet starting at 27408c2ecf20Sopenharmony_ci * that request. For TSO send requests with one ore more cuts 27418c2ecf20Sopenharmony_ci * in the middle, this is the number of RDMAs starting 27428c2ecf20Sopenharmony_ci * after the last cut in the request. All previous 27438c2ecf20Sopenharmony_ci * segments before the last cut implicitly have 1 RDMA. 27448c2ecf20Sopenharmony_ci * 27458c2ecf20Sopenharmony_ci * Since the number of RDMAs is not known beforehand, 27468c2ecf20Sopenharmony_ci * it must be filled-in retroactively - after each 27478c2ecf20Sopenharmony_ci * segmentation cut or at the end of the entire packet. 27488c2ecf20Sopenharmony_ci */ 27498c2ecf20Sopenharmony_ci 27508c2ecf20Sopenharmony_ci while (1) { 27518c2ecf20Sopenharmony_ci /* Break the SKB or Fragment up into pieces which 27528c2ecf20Sopenharmony_ci * do not cross mgp->tx_boundary */ 27538c2ecf20Sopenharmony_ci low = MYRI10GE_LOWPART_TO_U32(bus); 27548c2ecf20Sopenharmony_ci high_swapped = htonl(MYRI10GE_HIGHPART_TO_U32(bus)); 27558c2ecf20Sopenharmony_ci while (len) { 27568c2ecf20Sopenharmony_ci u8 flags_next; 27578c2ecf20Sopenharmony_ci int cum_len_next; 27588c2ecf20Sopenharmony_ci 27598c2ecf20Sopenharmony_ci if (unlikely(count == max_segments)) 27608c2ecf20Sopenharmony_ci goto abort_linearize; 27618c2ecf20Sopenharmony_ci 27628c2ecf20Sopenharmony_ci boundary = 27638c2ecf20Sopenharmony_ci (low + mgp->tx_boundary) & ~(mgp->tx_boundary - 1); 27648c2ecf20Sopenharmony_ci seglen = boundary - low; 27658c2ecf20Sopenharmony_ci if (seglen > len) 27668c2ecf20Sopenharmony_ci seglen = len; 27678c2ecf20Sopenharmony_ci flags_next = flags & ~MXGEFW_FLAGS_FIRST; 27688c2ecf20Sopenharmony_ci cum_len_next = cum_len + seglen; 27698c2ecf20Sopenharmony_ci if (mss) { /* TSO */ 27708c2ecf20Sopenharmony_ci (req - rdma_count)->rdma_count = rdma_count + 1; 27718c2ecf20Sopenharmony_ci 27728c2ecf20Sopenharmony_ci if (likely(cum_len >= 0)) { /* payload */ 27738c2ecf20Sopenharmony_ci int next_is_first, chop; 27748c2ecf20Sopenharmony_ci 27758c2ecf20Sopenharmony_ci chop = (cum_len_next > mss); 27768c2ecf20Sopenharmony_ci cum_len_next = cum_len_next % mss; 27778c2ecf20Sopenharmony_ci next_is_first = (cum_len_next == 0); 27788c2ecf20Sopenharmony_ci flags |= chop * MXGEFW_FLAGS_TSO_CHOP; 27798c2ecf20Sopenharmony_ci flags_next |= next_is_first * 27808c2ecf20Sopenharmony_ci MXGEFW_FLAGS_FIRST; 27818c2ecf20Sopenharmony_ci rdma_count |= -(chop | next_is_first); 27828c2ecf20Sopenharmony_ci rdma_count += chop & ~next_is_first; 27838c2ecf20Sopenharmony_ci } else if (likely(cum_len_next >= 0)) { /* header ends */ 27848c2ecf20Sopenharmony_ci int small; 27858c2ecf20Sopenharmony_ci 27868c2ecf20Sopenharmony_ci rdma_count = -1; 27878c2ecf20Sopenharmony_ci cum_len_next = 0; 27888c2ecf20Sopenharmony_ci seglen = -cum_len; 27898c2ecf20Sopenharmony_ci small = (mss <= MXGEFW_SEND_SMALL_SIZE); 27908c2ecf20Sopenharmony_ci flags_next = MXGEFW_FLAGS_TSO_PLD | 27918c2ecf20Sopenharmony_ci MXGEFW_FLAGS_FIRST | 27928c2ecf20Sopenharmony_ci (small * MXGEFW_FLAGS_SMALL); 27938c2ecf20Sopenharmony_ci } 27948c2ecf20Sopenharmony_ci } 27958c2ecf20Sopenharmony_ci req->addr_high = high_swapped; 27968c2ecf20Sopenharmony_ci req->addr_low = htonl(low); 27978c2ecf20Sopenharmony_ci req->pseudo_hdr_offset = htons(pseudo_hdr_offset); 27988c2ecf20Sopenharmony_ci req->pad = 0; /* complete solid 16-byte block; does this matter? */ 27998c2ecf20Sopenharmony_ci req->rdma_count = 1; 28008c2ecf20Sopenharmony_ci req->length = htons(seglen); 28018c2ecf20Sopenharmony_ci req->cksum_offset = cksum_offset; 28028c2ecf20Sopenharmony_ci req->flags = flags | ((cum_len & 1) * odd_flag); 28038c2ecf20Sopenharmony_ci 28048c2ecf20Sopenharmony_ci low += seglen; 28058c2ecf20Sopenharmony_ci len -= seglen; 28068c2ecf20Sopenharmony_ci cum_len = cum_len_next; 28078c2ecf20Sopenharmony_ci flags = flags_next; 28088c2ecf20Sopenharmony_ci req++; 28098c2ecf20Sopenharmony_ci count++; 28108c2ecf20Sopenharmony_ci rdma_count++; 28118c2ecf20Sopenharmony_ci if (cksum_offset != 0 && !(mss && skb_is_gso_v6(skb))) { 28128c2ecf20Sopenharmony_ci if (unlikely(cksum_offset > seglen)) 28138c2ecf20Sopenharmony_ci cksum_offset -= seglen; 28148c2ecf20Sopenharmony_ci else 28158c2ecf20Sopenharmony_ci cksum_offset = 0; 28168c2ecf20Sopenharmony_ci } 28178c2ecf20Sopenharmony_ci } 28188c2ecf20Sopenharmony_ci if (frag_idx == frag_cnt) 28198c2ecf20Sopenharmony_ci break; 28208c2ecf20Sopenharmony_ci 28218c2ecf20Sopenharmony_ci /* map next fragment for DMA */ 28228c2ecf20Sopenharmony_ci frag = &skb_shinfo(skb)->frags[frag_idx]; 28238c2ecf20Sopenharmony_ci frag_idx++; 28248c2ecf20Sopenharmony_ci len = skb_frag_size(frag); 28258c2ecf20Sopenharmony_ci bus = skb_frag_dma_map(&mgp->pdev->dev, frag, 0, len, 28268c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 28278c2ecf20Sopenharmony_ci if (unlikely(pci_dma_mapping_error(mgp->pdev, bus))) { 28288c2ecf20Sopenharmony_ci myri10ge_unmap_tx_dma(mgp, tx, idx); 28298c2ecf20Sopenharmony_ci goto drop; 28308c2ecf20Sopenharmony_ci } 28318c2ecf20Sopenharmony_ci idx = (count + tx->req) & tx->mask; 28328c2ecf20Sopenharmony_ci dma_unmap_addr_set(&tx->info[idx], bus, bus); 28338c2ecf20Sopenharmony_ci dma_unmap_len_set(&tx->info[idx], len, len); 28348c2ecf20Sopenharmony_ci } 28358c2ecf20Sopenharmony_ci 28368c2ecf20Sopenharmony_ci (req - rdma_count)->rdma_count = rdma_count; 28378c2ecf20Sopenharmony_ci if (mss) 28388c2ecf20Sopenharmony_ci do { 28398c2ecf20Sopenharmony_ci req--; 28408c2ecf20Sopenharmony_ci req->flags |= MXGEFW_FLAGS_TSO_LAST; 28418c2ecf20Sopenharmony_ci } while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | 28428c2ecf20Sopenharmony_ci MXGEFW_FLAGS_FIRST))); 28438c2ecf20Sopenharmony_ci idx = ((count - 1) + tx->req) & tx->mask; 28448c2ecf20Sopenharmony_ci tx->info[idx].last = 1; 28458c2ecf20Sopenharmony_ci myri10ge_submit_req(tx, tx->req_list, count); 28468c2ecf20Sopenharmony_ci /* if using multiple tx queues, make sure NIC polls the 28478c2ecf20Sopenharmony_ci * current slice */ 28488c2ecf20Sopenharmony_ci if ((mgp->dev->real_num_tx_queues > 1) && tx->queue_active == 0) { 28498c2ecf20Sopenharmony_ci tx->queue_active = 1; 28508c2ecf20Sopenharmony_ci put_be32(htonl(1), tx->send_go); 28518c2ecf20Sopenharmony_ci mb(); 28528c2ecf20Sopenharmony_ci } 28538c2ecf20Sopenharmony_ci tx->pkt_start++; 28548c2ecf20Sopenharmony_ci if ((avail - count) < MXGEFW_MAX_SEND_DESC) { 28558c2ecf20Sopenharmony_ci tx->stop_queue++; 28568c2ecf20Sopenharmony_ci netif_tx_stop_queue(netdev_queue); 28578c2ecf20Sopenharmony_ci } 28588c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 28598c2ecf20Sopenharmony_ci 28608c2ecf20Sopenharmony_ciabort_linearize: 28618c2ecf20Sopenharmony_ci myri10ge_unmap_tx_dma(mgp, tx, idx); 28628c2ecf20Sopenharmony_ci 28638c2ecf20Sopenharmony_ci if (skb_is_gso(skb)) { 28648c2ecf20Sopenharmony_ci netdev_err(mgp->dev, "TSO but wanted to linearize?!?!?\n"); 28658c2ecf20Sopenharmony_ci goto drop; 28668c2ecf20Sopenharmony_ci } 28678c2ecf20Sopenharmony_ci 28688c2ecf20Sopenharmony_ci if (skb_linearize(skb)) 28698c2ecf20Sopenharmony_ci goto drop; 28708c2ecf20Sopenharmony_ci 28718c2ecf20Sopenharmony_ci tx->linearized++; 28728c2ecf20Sopenharmony_ci goto again; 28738c2ecf20Sopenharmony_ci 28748c2ecf20Sopenharmony_cidrop: 28758c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 28768c2ecf20Sopenharmony_ci ss->stats.tx_dropped += 1; 28778c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 28788c2ecf20Sopenharmony_ci 28798c2ecf20Sopenharmony_ci} 28808c2ecf20Sopenharmony_ci 28818c2ecf20Sopenharmony_cistatic netdev_tx_t myri10ge_sw_tso(struct sk_buff *skb, 28828c2ecf20Sopenharmony_ci struct net_device *dev) 28838c2ecf20Sopenharmony_ci{ 28848c2ecf20Sopenharmony_ci struct sk_buff *segs, *curr, *next; 28858c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = netdev_priv(dev); 28868c2ecf20Sopenharmony_ci struct myri10ge_slice_state *ss; 28878c2ecf20Sopenharmony_ci netdev_tx_t status; 28888c2ecf20Sopenharmony_ci 28898c2ecf20Sopenharmony_ci segs = skb_gso_segment(skb, dev->features & ~NETIF_F_TSO6); 28908c2ecf20Sopenharmony_ci if (IS_ERR(segs)) 28918c2ecf20Sopenharmony_ci goto drop; 28928c2ecf20Sopenharmony_ci 28938c2ecf20Sopenharmony_ci skb_list_walk_safe(segs, curr, next) { 28948c2ecf20Sopenharmony_ci skb_mark_not_on_list(curr); 28958c2ecf20Sopenharmony_ci status = myri10ge_xmit(curr, dev); 28968c2ecf20Sopenharmony_ci if (status != 0) { 28978c2ecf20Sopenharmony_ci dev_kfree_skb_any(curr); 28988c2ecf20Sopenharmony_ci skb_list_walk_safe(next, curr, next) { 28998c2ecf20Sopenharmony_ci curr->next = NULL; 29008c2ecf20Sopenharmony_ci dev_kfree_skb_any(curr); 29018c2ecf20Sopenharmony_ci } 29028c2ecf20Sopenharmony_ci goto drop; 29038c2ecf20Sopenharmony_ci } 29048c2ecf20Sopenharmony_ci } 29058c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 29068c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 29078c2ecf20Sopenharmony_ci 29088c2ecf20Sopenharmony_cidrop: 29098c2ecf20Sopenharmony_ci ss = &mgp->ss[skb_get_queue_mapping(skb)]; 29108c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 29118c2ecf20Sopenharmony_ci ss->stats.tx_dropped += 1; 29128c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 29138c2ecf20Sopenharmony_ci} 29148c2ecf20Sopenharmony_ci 29158c2ecf20Sopenharmony_cistatic void myri10ge_get_stats(struct net_device *dev, 29168c2ecf20Sopenharmony_ci struct rtnl_link_stats64 *stats) 29178c2ecf20Sopenharmony_ci{ 29188c2ecf20Sopenharmony_ci const struct myri10ge_priv *mgp = netdev_priv(dev); 29198c2ecf20Sopenharmony_ci const struct myri10ge_slice_netstats *slice_stats; 29208c2ecf20Sopenharmony_ci int i; 29218c2ecf20Sopenharmony_ci 29228c2ecf20Sopenharmony_ci for (i = 0; i < mgp->num_slices; i++) { 29238c2ecf20Sopenharmony_ci slice_stats = &mgp->ss[i].stats; 29248c2ecf20Sopenharmony_ci stats->rx_packets += slice_stats->rx_packets; 29258c2ecf20Sopenharmony_ci stats->tx_packets += slice_stats->tx_packets; 29268c2ecf20Sopenharmony_ci stats->rx_bytes += slice_stats->rx_bytes; 29278c2ecf20Sopenharmony_ci stats->tx_bytes += slice_stats->tx_bytes; 29288c2ecf20Sopenharmony_ci stats->rx_dropped += slice_stats->rx_dropped; 29298c2ecf20Sopenharmony_ci stats->tx_dropped += slice_stats->tx_dropped; 29308c2ecf20Sopenharmony_ci } 29318c2ecf20Sopenharmony_ci} 29328c2ecf20Sopenharmony_ci 29338c2ecf20Sopenharmony_cistatic void myri10ge_set_multicast_list(struct net_device *dev) 29348c2ecf20Sopenharmony_ci{ 29358c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = netdev_priv(dev); 29368c2ecf20Sopenharmony_ci struct myri10ge_cmd cmd; 29378c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 29388c2ecf20Sopenharmony_ci __be32 data[2] = { 0, 0 }; 29398c2ecf20Sopenharmony_ci int err; 29408c2ecf20Sopenharmony_ci 29418c2ecf20Sopenharmony_ci /* can be called from atomic contexts, 29428c2ecf20Sopenharmony_ci * pass 1 to force atomicity in myri10ge_send_cmd() */ 29438c2ecf20Sopenharmony_ci myri10ge_change_promisc(mgp, dev->flags & IFF_PROMISC, 1); 29448c2ecf20Sopenharmony_ci 29458c2ecf20Sopenharmony_ci /* This firmware is known to not support multicast */ 29468c2ecf20Sopenharmony_ci if (!mgp->fw_multicast_support) 29478c2ecf20Sopenharmony_ci return; 29488c2ecf20Sopenharmony_ci 29498c2ecf20Sopenharmony_ci /* Disable multicast filtering */ 29508c2ecf20Sopenharmony_ci 29518c2ecf20Sopenharmony_ci err = myri10ge_send_cmd(mgp, MXGEFW_ENABLE_ALLMULTI, &cmd, 1); 29528c2ecf20Sopenharmony_ci if (err != 0) { 29538c2ecf20Sopenharmony_ci netdev_err(dev, "Failed MXGEFW_ENABLE_ALLMULTI, error status: %d\n", 29548c2ecf20Sopenharmony_ci err); 29558c2ecf20Sopenharmony_ci goto abort; 29568c2ecf20Sopenharmony_ci } 29578c2ecf20Sopenharmony_ci 29588c2ecf20Sopenharmony_ci if ((dev->flags & IFF_ALLMULTI) || mgp->adopted_rx_filter_bug) { 29598c2ecf20Sopenharmony_ci /* request to disable multicast filtering, so quit here */ 29608c2ecf20Sopenharmony_ci return; 29618c2ecf20Sopenharmony_ci } 29628c2ecf20Sopenharmony_ci 29638c2ecf20Sopenharmony_ci /* Flush the filters */ 29648c2ecf20Sopenharmony_ci 29658c2ecf20Sopenharmony_ci err = myri10ge_send_cmd(mgp, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, 29668c2ecf20Sopenharmony_ci &cmd, 1); 29678c2ecf20Sopenharmony_ci if (err != 0) { 29688c2ecf20Sopenharmony_ci netdev_err(dev, "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, error status: %d\n", 29698c2ecf20Sopenharmony_ci err); 29708c2ecf20Sopenharmony_ci goto abort; 29718c2ecf20Sopenharmony_ci } 29728c2ecf20Sopenharmony_ci 29738c2ecf20Sopenharmony_ci /* Walk the multicast list, and add each address */ 29748c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 29758c2ecf20Sopenharmony_ci memcpy(data, &ha->addr, ETH_ALEN); 29768c2ecf20Sopenharmony_ci cmd.data0 = ntohl(data[0]); 29778c2ecf20Sopenharmony_ci cmd.data1 = ntohl(data[1]); 29788c2ecf20Sopenharmony_ci err = myri10ge_send_cmd(mgp, MXGEFW_JOIN_MULTICAST_GROUP, 29798c2ecf20Sopenharmony_ci &cmd, 1); 29808c2ecf20Sopenharmony_ci 29818c2ecf20Sopenharmony_ci if (err != 0) { 29828c2ecf20Sopenharmony_ci netdev_err(dev, "Failed MXGEFW_JOIN_MULTICAST_GROUP, error status:%d %pM\n", 29838c2ecf20Sopenharmony_ci err, ha->addr); 29848c2ecf20Sopenharmony_ci goto abort; 29858c2ecf20Sopenharmony_ci } 29868c2ecf20Sopenharmony_ci } 29878c2ecf20Sopenharmony_ci /* Enable multicast filtering */ 29888c2ecf20Sopenharmony_ci err = myri10ge_send_cmd(mgp, MXGEFW_DISABLE_ALLMULTI, &cmd, 1); 29898c2ecf20Sopenharmony_ci if (err != 0) { 29908c2ecf20Sopenharmony_ci netdev_err(dev, "Failed MXGEFW_DISABLE_ALLMULTI, error status: %d\n", 29918c2ecf20Sopenharmony_ci err); 29928c2ecf20Sopenharmony_ci goto abort; 29938c2ecf20Sopenharmony_ci } 29948c2ecf20Sopenharmony_ci 29958c2ecf20Sopenharmony_ci return; 29968c2ecf20Sopenharmony_ci 29978c2ecf20Sopenharmony_ciabort: 29988c2ecf20Sopenharmony_ci return; 29998c2ecf20Sopenharmony_ci} 30008c2ecf20Sopenharmony_ci 30018c2ecf20Sopenharmony_cistatic int myri10ge_set_mac_address(struct net_device *dev, void *addr) 30028c2ecf20Sopenharmony_ci{ 30038c2ecf20Sopenharmony_ci struct sockaddr *sa = addr; 30048c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = netdev_priv(dev); 30058c2ecf20Sopenharmony_ci int status; 30068c2ecf20Sopenharmony_ci 30078c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(sa->sa_data)) 30088c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 30098c2ecf20Sopenharmony_ci 30108c2ecf20Sopenharmony_ci status = myri10ge_update_mac_address(mgp, sa->sa_data); 30118c2ecf20Sopenharmony_ci if (status != 0) { 30128c2ecf20Sopenharmony_ci netdev_err(dev, "changing mac address failed with %d\n", 30138c2ecf20Sopenharmony_ci status); 30148c2ecf20Sopenharmony_ci return status; 30158c2ecf20Sopenharmony_ci } 30168c2ecf20Sopenharmony_ci 30178c2ecf20Sopenharmony_ci /* change the dev structure */ 30188c2ecf20Sopenharmony_ci memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN); 30198c2ecf20Sopenharmony_ci return 0; 30208c2ecf20Sopenharmony_ci} 30218c2ecf20Sopenharmony_ci 30228c2ecf20Sopenharmony_cistatic int myri10ge_change_mtu(struct net_device *dev, int new_mtu) 30238c2ecf20Sopenharmony_ci{ 30248c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = netdev_priv(dev); 30258c2ecf20Sopenharmony_ci 30268c2ecf20Sopenharmony_ci netdev_info(dev, "changing mtu from %d to %d\n", dev->mtu, new_mtu); 30278c2ecf20Sopenharmony_ci if (mgp->running) { 30288c2ecf20Sopenharmony_ci /* if we change the mtu on an active device, we must 30298c2ecf20Sopenharmony_ci * reset the device so the firmware sees the change */ 30308c2ecf20Sopenharmony_ci myri10ge_close(dev); 30318c2ecf20Sopenharmony_ci dev->mtu = new_mtu; 30328c2ecf20Sopenharmony_ci myri10ge_open(dev); 30338c2ecf20Sopenharmony_ci } else 30348c2ecf20Sopenharmony_ci dev->mtu = new_mtu; 30358c2ecf20Sopenharmony_ci 30368c2ecf20Sopenharmony_ci return 0; 30378c2ecf20Sopenharmony_ci} 30388c2ecf20Sopenharmony_ci 30398c2ecf20Sopenharmony_ci/* 30408c2ecf20Sopenharmony_ci * Enable ECRC to align PCI-E Completion packets on an 8-byte boundary. 30418c2ecf20Sopenharmony_ci * Only do it if the bridge is a root port since we don't want to disturb 30428c2ecf20Sopenharmony_ci * any other device, except if forced with myri10ge_ecrc_enable > 1. 30438c2ecf20Sopenharmony_ci */ 30448c2ecf20Sopenharmony_ci 30458c2ecf20Sopenharmony_cistatic void myri10ge_enable_ecrc(struct myri10ge_priv *mgp) 30468c2ecf20Sopenharmony_ci{ 30478c2ecf20Sopenharmony_ci struct pci_dev *bridge = mgp->pdev->bus->self; 30488c2ecf20Sopenharmony_ci struct device *dev = &mgp->pdev->dev; 30498c2ecf20Sopenharmony_ci int cap; 30508c2ecf20Sopenharmony_ci unsigned err_cap; 30518c2ecf20Sopenharmony_ci int ret; 30528c2ecf20Sopenharmony_ci 30538c2ecf20Sopenharmony_ci if (!myri10ge_ecrc_enable || !bridge) 30548c2ecf20Sopenharmony_ci return; 30558c2ecf20Sopenharmony_ci 30568c2ecf20Sopenharmony_ci /* check that the bridge is a root port */ 30578c2ecf20Sopenharmony_ci if (pci_pcie_type(bridge) != PCI_EXP_TYPE_ROOT_PORT) { 30588c2ecf20Sopenharmony_ci if (myri10ge_ecrc_enable > 1) { 30598c2ecf20Sopenharmony_ci struct pci_dev *prev_bridge, *old_bridge = bridge; 30608c2ecf20Sopenharmony_ci 30618c2ecf20Sopenharmony_ci /* Walk the hierarchy up to the root port 30628c2ecf20Sopenharmony_ci * where ECRC has to be enabled */ 30638c2ecf20Sopenharmony_ci do { 30648c2ecf20Sopenharmony_ci prev_bridge = bridge; 30658c2ecf20Sopenharmony_ci bridge = bridge->bus->self; 30668c2ecf20Sopenharmony_ci if (!bridge || prev_bridge == bridge) { 30678c2ecf20Sopenharmony_ci dev_err(dev, 30688c2ecf20Sopenharmony_ci "Failed to find root port" 30698c2ecf20Sopenharmony_ci " to force ECRC\n"); 30708c2ecf20Sopenharmony_ci return; 30718c2ecf20Sopenharmony_ci } 30728c2ecf20Sopenharmony_ci } while (pci_pcie_type(bridge) != 30738c2ecf20Sopenharmony_ci PCI_EXP_TYPE_ROOT_PORT); 30748c2ecf20Sopenharmony_ci 30758c2ecf20Sopenharmony_ci dev_info(dev, 30768c2ecf20Sopenharmony_ci "Forcing ECRC on non-root port %s" 30778c2ecf20Sopenharmony_ci " (enabling on root port %s)\n", 30788c2ecf20Sopenharmony_ci pci_name(old_bridge), pci_name(bridge)); 30798c2ecf20Sopenharmony_ci } else { 30808c2ecf20Sopenharmony_ci dev_err(dev, 30818c2ecf20Sopenharmony_ci "Not enabling ECRC on non-root port %s\n", 30828c2ecf20Sopenharmony_ci pci_name(bridge)); 30838c2ecf20Sopenharmony_ci return; 30848c2ecf20Sopenharmony_ci } 30858c2ecf20Sopenharmony_ci } 30868c2ecf20Sopenharmony_ci 30878c2ecf20Sopenharmony_ci cap = pci_find_ext_capability(bridge, PCI_EXT_CAP_ID_ERR); 30888c2ecf20Sopenharmony_ci if (!cap) 30898c2ecf20Sopenharmony_ci return; 30908c2ecf20Sopenharmony_ci 30918c2ecf20Sopenharmony_ci ret = pci_read_config_dword(bridge, cap + PCI_ERR_CAP, &err_cap); 30928c2ecf20Sopenharmony_ci if (ret) { 30938c2ecf20Sopenharmony_ci dev_err(dev, "failed reading ext-conf-space of %s\n", 30948c2ecf20Sopenharmony_ci pci_name(bridge)); 30958c2ecf20Sopenharmony_ci dev_err(dev, "\t pci=nommconf in use? " 30968c2ecf20Sopenharmony_ci "or buggy/incomplete/absent ACPI MCFG attr?\n"); 30978c2ecf20Sopenharmony_ci return; 30988c2ecf20Sopenharmony_ci } 30998c2ecf20Sopenharmony_ci if (!(err_cap & PCI_ERR_CAP_ECRC_GENC)) 31008c2ecf20Sopenharmony_ci return; 31018c2ecf20Sopenharmony_ci 31028c2ecf20Sopenharmony_ci err_cap |= PCI_ERR_CAP_ECRC_GENE; 31038c2ecf20Sopenharmony_ci pci_write_config_dword(bridge, cap + PCI_ERR_CAP, err_cap); 31048c2ecf20Sopenharmony_ci dev_info(dev, "Enabled ECRC on upstream bridge %s\n", pci_name(bridge)); 31058c2ecf20Sopenharmony_ci} 31068c2ecf20Sopenharmony_ci 31078c2ecf20Sopenharmony_ci/* 31088c2ecf20Sopenharmony_ci * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput 31098c2ecf20Sopenharmony_ci * when the PCI-E Completion packets are aligned on an 8-byte 31108c2ecf20Sopenharmony_ci * boundary. Some PCI-E chip sets always align Completion packets; on 31118c2ecf20Sopenharmony_ci * the ones that do not, the alignment can be enforced by enabling 31128c2ecf20Sopenharmony_ci * ECRC generation (if supported). 31138c2ecf20Sopenharmony_ci * 31148c2ecf20Sopenharmony_ci * When PCI-E Completion packets are not aligned, it is actually more 31158c2ecf20Sopenharmony_ci * efficient to limit Read-DMA transactions to 2KB, rather than 4KB. 31168c2ecf20Sopenharmony_ci * 31178c2ecf20Sopenharmony_ci * If the driver can neither enable ECRC nor verify that it has 31188c2ecf20Sopenharmony_ci * already been enabled, then it must use a firmware image which works 31198c2ecf20Sopenharmony_ci * around unaligned completion packets (myri10ge_rss_ethp_z8e.dat), and it 31208c2ecf20Sopenharmony_ci * should also ensure that it never gives the device a Read-DMA which is 31218c2ecf20Sopenharmony_ci * larger than 2KB by setting the tx_boundary to 2KB. If ECRC is 31228c2ecf20Sopenharmony_ci * enabled, then the driver should use the aligned (myri10ge_rss_eth_z8e.dat) 31238c2ecf20Sopenharmony_ci * firmware image, and set tx_boundary to 4KB. 31248c2ecf20Sopenharmony_ci */ 31258c2ecf20Sopenharmony_ci 31268c2ecf20Sopenharmony_cistatic void myri10ge_firmware_probe(struct myri10ge_priv *mgp) 31278c2ecf20Sopenharmony_ci{ 31288c2ecf20Sopenharmony_ci struct pci_dev *pdev = mgp->pdev; 31298c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 31308c2ecf20Sopenharmony_ci int status; 31318c2ecf20Sopenharmony_ci 31328c2ecf20Sopenharmony_ci mgp->tx_boundary = 4096; 31338c2ecf20Sopenharmony_ci /* 31348c2ecf20Sopenharmony_ci * Verify the max read request size was set to 4KB 31358c2ecf20Sopenharmony_ci * before trying the test with 4KB. 31368c2ecf20Sopenharmony_ci */ 31378c2ecf20Sopenharmony_ci status = pcie_get_readrq(pdev); 31388c2ecf20Sopenharmony_ci if (status < 0) { 31398c2ecf20Sopenharmony_ci dev_err(dev, "Couldn't read max read req size: %d\n", status); 31408c2ecf20Sopenharmony_ci goto abort; 31418c2ecf20Sopenharmony_ci } 31428c2ecf20Sopenharmony_ci if (status != 4096) { 31438c2ecf20Sopenharmony_ci dev_warn(dev, "Max Read Request size != 4096 (%d)\n", status); 31448c2ecf20Sopenharmony_ci mgp->tx_boundary = 2048; 31458c2ecf20Sopenharmony_ci } 31468c2ecf20Sopenharmony_ci /* 31478c2ecf20Sopenharmony_ci * load the optimized firmware (which assumes aligned PCIe 31488c2ecf20Sopenharmony_ci * completions) in order to see if it works on this host. 31498c2ecf20Sopenharmony_ci */ 31508c2ecf20Sopenharmony_ci set_fw_name(mgp, myri10ge_fw_aligned, false); 31518c2ecf20Sopenharmony_ci status = myri10ge_load_firmware(mgp, 1); 31528c2ecf20Sopenharmony_ci if (status != 0) { 31538c2ecf20Sopenharmony_ci goto abort; 31548c2ecf20Sopenharmony_ci } 31558c2ecf20Sopenharmony_ci 31568c2ecf20Sopenharmony_ci /* 31578c2ecf20Sopenharmony_ci * Enable ECRC if possible 31588c2ecf20Sopenharmony_ci */ 31598c2ecf20Sopenharmony_ci myri10ge_enable_ecrc(mgp); 31608c2ecf20Sopenharmony_ci 31618c2ecf20Sopenharmony_ci /* 31628c2ecf20Sopenharmony_ci * Run a DMA test which watches for unaligned completions and 31638c2ecf20Sopenharmony_ci * aborts on the first one seen. 31648c2ecf20Sopenharmony_ci */ 31658c2ecf20Sopenharmony_ci 31668c2ecf20Sopenharmony_ci status = myri10ge_dma_test(mgp, MXGEFW_CMD_UNALIGNED_TEST); 31678c2ecf20Sopenharmony_ci if (status == 0) 31688c2ecf20Sopenharmony_ci return; /* keep the aligned firmware */ 31698c2ecf20Sopenharmony_ci 31708c2ecf20Sopenharmony_ci if (status != -E2BIG) 31718c2ecf20Sopenharmony_ci dev_warn(dev, "DMA test failed: %d\n", status); 31728c2ecf20Sopenharmony_ci if (status == -ENOSYS) 31738c2ecf20Sopenharmony_ci dev_warn(dev, "Falling back to ethp! " 31748c2ecf20Sopenharmony_ci "Please install up to date fw\n"); 31758c2ecf20Sopenharmony_ciabort: 31768c2ecf20Sopenharmony_ci /* fall back to using the unaligned firmware */ 31778c2ecf20Sopenharmony_ci mgp->tx_boundary = 2048; 31788c2ecf20Sopenharmony_ci set_fw_name(mgp, myri10ge_fw_unaligned, false); 31798c2ecf20Sopenharmony_ci} 31808c2ecf20Sopenharmony_ci 31818c2ecf20Sopenharmony_cistatic void myri10ge_select_firmware(struct myri10ge_priv *mgp) 31828c2ecf20Sopenharmony_ci{ 31838c2ecf20Sopenharmony_ci int overridden = 0; 31848c2ecf20Sopenharmony_ci 31858c2ecf20Sopenharmony_ci if (myri10ge_force_firmware == 0) { 31868c2ecf20Sopenharmony_ci int link_width; 31878c2ecf20Sopenharmony_ci u16 lnk; 31888c2ecf20Sopenharmony_ci 31898c2ecf20Sopenharmony_ci pcie_capability_read_word(mgp->pdev, PCI_EXP_LNKSTA, &lnk); 31908c2ecf20Sopenharmony_ci link_width = (lnk >> 4) & 0x3f; 31918c2ecf20Sopenharmony_ci 31928c2ecf20Sopenharmony_ci /* Check to see if Link is less than 8 or if the 31938c2ecf20Sopenharmony_ci * upstream bridge is known to provide aligned 31948c2ecf20Sopenharmony_ci * completions */ 31958c2ecf20Sopenharmony_ci if (link_width < 8) { 31968c2ecf20Sopenharmony_ci dev_info(&mgp->pdev->dev, "PCIE x%d Link\n", 31978c2ecf20Sopenharmony_ci link_width); 31988c2ecf20Sopenharmony_ci mgp->tx_boundary = 4096; 31998c2ecf20Sopenharmony_ci set_fw_name(mgp, myri10ge_fw_aligned, false); 32008c2ecf20Sopenharmony_ci } else { 32018c2ecf20Sopenharmony_ci myri10ge_firmware_probe(mgp); 32028c2ecf20Sopenharmony_ci } 32038c2ecf20Sopenharmony_ci } else { 32048c2ecf20Sopenharmony_ci if (myri10ge_force_firmware == 1) { 32058c2ecf20Sopenharmony_ci dev_info(&mgp->pdev->dev, 32068c2ecf20Sopenharmony_ci "Assuming aligned completions (forced)\n"); 32078c2ecf20Sopenharmony_ci mgp->tx_boundary = 4096; 32088c2ecf20Sopenharmony_ci set_fw_name(mgp, myri10ge_fw_aligned, false); 32098c2ecf20Sopenharmony_ci } else { 32108c2ecf20Sopenharmony_ci dev_info(&mgp->pdev->dev, 32118c2ecf20Sopenharmony_ci "Assuming unaligned completions (forced)\n"); 32128c2ecf20Sopenharmony_ci mgp->tx_boundary = 2048; 32138c2ecf20Sopenharmony_ci set_fw_name(mgp, myri10ge_fw_unaligned, false); 32148c2ecf20Sopenharmony_ci } 32158c2ecf20Sopenharmony_ci } 32168c2ecf20Sopenharmony_ci 32178c2ecf20Sopenharmony_ci kernel_param_lock(THIS_MODULE); 32188c2ecf20Sopenharmony_ci if (myri10ge_fw_name != NULL) { 32198c2ecf20Sopenharmony_ci char *fw_name = kstrdup(myri10ge_fw_name, GFP_KERNEL); 32208c2ecf20Sopenharmony_ci if (fw_name) { 32218c2ecf20Sopenharmony_ci overridden = 1; 32228c2ecf20Sopenharmony_ci set_fw_name(mgp, fw_name, true); 32238c2ecf20Sopenharmony_ci } 32248c2ecf20Sopenharmony_ci } 32258c2ecf20Sopenharmony_ci kernel_param_unlock(THIS_MODULE); 32268c2ecf20Sopenharmony_ci 32278c2ecf20Sopenharmony_ci if (mgp->board_number < MYRI10GE_MAX_BOARDS && 32288c2ecf20Sopenharmony_ci myri10ge_fw_names[mgp->board_number] != NULL && 32298c2ecf20Sopenharmony_ci strlen(myri10ge_fw_names[mgp->board_number])) { 32308c2ecf20Sopenharmony_ci set_fw_name(mgp, myri10ge_fw_names[mgp->board_number], false); 32318c2ecf20Sopenharmony_ci overridden = 1; 32328c2ecf20Sopenharmony_ci } 32338c2ecf20Sopenharmony_ci if (overridden) 32348c2ecf20Sopenharmony_ci dev_info(&mgp->pdev->dev, "overriding firmware to %s\n", 32358c2ecf20Sopenharmony_ci mgp->fw_name); 32368c2ecf20Sopenharmony_ci} 32378c2ecf20Sopenharmony_ci 32388c2ecf20Sopenharmony_cistatic void myri10ge_mask_surprise_down(struct pci_dev *pdev) 32398c2ecf20Sopenharmony_ci{ 32408c2ecf20Sopenharmony_ci struct pci_dev *bridge = pdev->bus->self; 32418c2ecf20Sopenharmony_ci int cap; 32428c2ecf20Sopenharmony_ci u32 mask; 32438c2ecf20Sopenharmony_ci 32448c2ecf20Sopenharmony_ci if (bridge == NULL) 32458c2ecf20Sopenharmony_ci return; 32468c2ecf20Sopenharmony_ci 32478c2ecf20Sopenharmony_ci cap = pci_find_ext_capability(bridge, PCI_EXT_CAP_ID_ERR); 32488c2ecf20Sopenharmony_ci if (cap) { 32498c2ecf20Sopenharmony_ci /* a sram parity error can cause a surprise link 32508c2ecf20Sopenharmony_ci * down; since we expect and can recover from sram 32518c2ecf20Sopenharmony_ci * parity errors, mask surprise link down events */ 32528c2ecf20Sopenharmony_ci pci_read_config_dword(bridge, cap + PCI_ERR_UNCOR_MASK, &mask); 32538c2ecf20Sopenharmony_ci mask |= 0x20; 32548c2ecf20Sopenharmony_ci pci_write_config_dword(bridge, cap + PCI_ERR_UNCOR_MASK, mask); 32558c2ecf20Sopenharmony_ci } 32568c2ecf20Sopenharmony_ci} 32578c2ecf20Sopenharmony_ci 32588c2ecf20Sopenharmony_cistatic int __maybe_unused myri10ge_suspend(struct device *dev) 32598c2ecf20Sopenharmony_ci{ 32608c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp; 32618c2ecf20Sopenharmony_ci struct net_device *netdev; 32628c2ecf20Sopenharmony_ci 32638c2ecf20Sopenharmony_ci mgp = dev_get_drvdata(dev); 32648c2ecf20Sopenharmony_ci if (mgp == NULL) 32658c2ecf20Sopenharmony_ci return -EINVAL; 32668c2ecf20Sopenharmony_ci netdev = mgp->dev; 32678c2ecf20Sopenharmony_ci 32688c2ecf20Sopenharmony_ci netif_device_detach(netdev); 32698c2ecf20Sopenharmony_ci if (netif_running(netdev)) { 32708c2ecf20Sopenharmony_ci netdev_info(netdev, "closing\n"); 32718c2ecf20Sopenharmony_ci rtnl_lock(); 32728c2ecf20Sopenharmony_ci myri10ge_close(netdev); 32738c2ecf20Sopenharmony_ci rtnl_unlock(); 32748c2ecf20Sopenharmony_ci } 32758c2ecf20Sopenharmony_ci myri10ge_dummy_rdma(mgp, 0); 32768c2ecf20Sopenharmony_ci 32778c2ecf20Sopenharmony_ci return 0; 32788c2ecf20Sopenharmony_ci} 32798c2ecf20Sopenharmony_ci 32808c2ecf20Sopenharmony_cistatic int __maybe_unused myri10ge_resume(struct device *dev) 32818c2ecf20Sopenharmony_ci{ 32828c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 32838c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp; 32848c2ecf20Sopenharmony_ci struct net_device *netdev; 32858c2ecf20Sopenharmony_ci int status; 32868c2ecf20Sopenharmony_ci u16 vendor; 32878c2ecf20Sopenharmony_ci 32888c2ecf20Sopenharmony_ci mgp = pci_get_drvdata(pdev); 32898c2ecf20Sopenharmony_ci if (mgp == NULL) 32908c2ecf20Sopenharmony_ci return -EINVAL; 32918c2ecf20Sopenharmony_ci netdev = mgp->dev; 32928c2ecf20Sopenharmony_ci msleep(5); /* give card time to respond */ 32938c2ecf20Sopenharmony_ci pci_read_config_word(mgp->pdev, PCI_VENDOR_ID, &vendor); 32948c2ecf20Sopenharmony_ci if (vendor == 0xffff) { 32958c2ecf20Sopenharmony_ci netdev_err(mgp->dev, "device disappeared!\n"); 32968c2ecf20Sopenharmony_ci return -EIO; 32978c2ecf20Sopenharmony_ci } 32988c2ecf20Sopenharmony_ci 32998c2ecf20Sopenharmony_ci myri10ge_reset(mgp); 33008c2ecf20Sopenharmony_ci myri10ge_dummy_rdma(mgp, 1); 33018c2ecf20Sopenharmony_ci 33028c2ecf20Sopenharmony_ci if (netif_running(netdev)) { 33038c2ecf20Sopenharmony_ci rtnl_lock(); 33048c2ecf20Sopenharmony_ci status = myri10ge_open(netdev); 33058c2ecf20Sopenharmony_ci rtnl_unlock(); 33068c2ecf20Sopenharmony_ci if (status != 0) 33078c2ecf20Sopenharmony_ci goto abort_with_enabled; 33088c2ecf20Sopenharmony_ci 33098c2ecf20Sopenharmony_ci } 33108c2ecf20Sopenharmony_ci netif_device_attach(netdev); 33118c2ecf20Sopenharmony_ci 33128c2ecf20Sopenharmony_ci return 0; 33138c2ecf20Sopenharmony_ci 33148c2ecf20Sopenharmony_ciabort_with_enabled: 33158c2ecf20Sopenharmony_ci return -EIO; 33168c2ecf20Sopenharmony_ci} 33178c2ecf20Sopenharmony_ci 33188c2ecf20Sopenharmony_cistatic u32 myri10ge_read_reboot(struct myri10ge_priv *mgp) 33198c2ecf20Sopenharmony_ci{ 33208c2ecf20Sopenharmony_ci struct pci_dev *pdev = mgp->pdev; 33218c2ecf20Sopenharmony_ci int vs = mgp->vendor_specific_offset; 33228c2ecf20Sopenharmony_ci u32 reboot; 33238c2ecf20Sopenharmony_ci 33248c2ecf20Sopenharmony_ci /*enter read32 mode */ 33258c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, vs + 0x10, 0x3); 33268c2ecf20Sopenharmony_ci 33278c2ecf20Sopenharmony_ci /*read REBOOT_STATUS (0xfffffff0) */ 33288c2ecf20Sopenharmony_ci pci_write_config_dword(pdev, vs + 0x18, 0xfffffff0); 33298c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, vs + 0x14, &reboot); 33308c2ecf20Sopenharmony_ci return reboot; 33318c2ecf20Sopenharmony_ci} 33328c2ecf20Sopenharmony_ci 33338c2ecf20Sopenharmony_cistatic void 33348c2ecf20Sopenharmony_cimyri10ge_check_slice(struct myri10ge_slice_state *ss, int *reset_needed, 33358c2ecf20Sopenharmony_ci int *busy_slice_cnt, u32 rx_pause_cnt) 33368c2ecf20Sopenharmony_ci{ 33378c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = ss->mgp; 33388c2ecf20Sopenharmony_ci int slice = ss - mgp->ss; 33398c2ecf20Sopenharmony_ci 33408c2ecf20Sopenharmony_ci if (ss->tx.req != ss->tx.done && 33418c2ecf20Sopenharmony_ci ss->tx.done == ss->watchdog_tx_done && 33428c2ecf20Sopenharmony_ci ss->watchdog_tx_req != ss->watchdog_tx_done) { 33438c2ecf20Sopenharmony_ci /* nic seems like it might be stuck.. */ 33448c2ecf20Sopenharmony_ci if (rx_pause_cnt != mgp->watchdog_pause) { 33458c2ecf20Sopenharmony_ci if (net_ratelimit()) 33468c2ecf20Sopenharmony_ci netdev_warn(mgp->dev, "slice %d: TX paused, " 33478c2ecf20Sopenharmony_ci "check link partner\n", slice); 33488c2ecf20Sopenharmony_ci } else { 33498c2ecf20Sopenharmony_ci netdev_warn(mgp->dev, 33508c2ecf20Sopenharmony_ci "slice %d: TX stuck %d %d %d %d %d %d\n", 33518c2ecf20Sopenharmony_ci slice, ss->tx.queue_active, ss->tx.req, 33528c2ecf20Sopenharmony_ci ss->tx.done, ss->tx.pkt_start, 33538c2ecf20Sopenharmony_ci ss->tx.pkt_done, 33548c2ecf20Sopenharmony_ci (int)ntohl(mgp->ss[slice].fw_stats-> 33558c2ecf20Sopenharmony_ci send_done_count)); 33568c2ecf20Sopenharmony_ci *reset_needed = 1; 33578c2ecf20Sopenharmony_ci ss->stuck = 1; 33588c2ecf20Sopenharmony_ci } 33598c2ecf20Sopenharmony_ci } 33608c2ecf20Sopenharmony_ci if (ss->watchdog_tx_done != ss->tx.done || 33618c2ecf20Sopenharmony_ci ss->watchdog_rx_done != ss->rx_done.cnt) { 33628c2ecf20Sopenharmony_ci *busy_slice_cnt += 1; 33638c2ecf20Sopenharmony_ci } 33648c2ecf20Sopenharmony_ci ss->watchdog_tx_done = ss->tx.done; 33658c2ecf20Sopenharmony_ci ss->watchdog_tx_req = ss->tx.req; 33668c2ecf20Sopenharmony_ci ss->watchdog_rx_done = ss->rx_done.cnt; 33678c2ecf20Sopenharmony_ci} 33688c2ecf20Sopenharmony_ci 33698c2ecf20Sopenharmony_ci/* 33708c2ecf20Sopenharmony_ci * This watchdog is used to check whether the board has suffered 33718c2ecf20Sopenharmony_ci * from a parity error and needs to be recovered. 33728c2ecf20Sopenharmony_ci */ 33738c2ecf20Sopenharmony_cistatic void myri10ge_watchdog(struct work_struct *work) 33748c2ecf20Sopenharmony_ci{ 33758c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp = 33768c2ecf20Sopenharmony_ci container_of(work, struct myri10ge_priv, watchdog_work); 33778c2ecf20Sopenharmony_ci struct myri10ge_slice_state *ss; 33788c2ecf20Sopenharmony_ci u32 reboot, rx_pause_cnt; 33798c2ecf20Sopenharmony_ci int status, rebooted; 33808c2ecf20Sopenharmony_ci int i; 33818c2ecf20Sopenharmony_ci int reset_needed = 0; 33828c2ecf20Sopenharmony_ci int busy_slice_cnt = 0; 33838c2ecf20Sopenharmony_ci u16 cmd, vendor; 33848c2ecf20Sopenharmony_ci 33858c2ecf20Sopenharmony_ci mgp->watchdog_resets++; 33868c2ecf20Sopenharmony_ci pci_read_config_word(mgp->pdev, PCI_COMMAND, &cmd); 33878c2ecf20Sopenharmony_ci rebooted = 0; 33888c2ecf20Sopenharmony_ci if ((cmd & PCI_COMMAND_MASTER) == 0) { 33898c2ecf20Sopenharmony_ci /* Bus master DMA disabled? Check to see 33908c2ecf20Sopenharmony_ci * if the card rebooted due to a parity error 33918c2ecf20Sopenharmony_ci * For now, just report it */ 33928c2ecf20Sopenharmony_ci reboot = myri10ge_read_reboot(mgp); 33938c2ecf20Sopenharmony_ci netdev_err(mgp->dev, "NIC rebooted (0x%x),%s resetting\n", 33948c2ecf20Sopenharmony_ci reboot, myri10ge_reset_recover ? "" : " not"); 33958c2ecf20Sopenharmony_ci if (myri10ge_reset_recover == 0) 33968c2ecf20Sopenharmony_ci return; 33978c2ecf20Sopenharmony_ci rtnl_lock(); 33988c2ecf20Sopenharmony_ci mgp->rebooted = 1; 33998c2ecf20Sopenharmony_ci rebooted = 1; 34008c2ecf20Sopenharmony_ci myri10ge_close(mgp->dev); 34018c2ecf20Sopenharmony_ci myri10ge_reset_recover--; 34028c2ecf20Sopenharmony_ci mgp->rebooted = 0; 34038c2ecf20Sopenharmony_ci /* 34048c2ecf20Sopenharmony_ci * A rebooted nic will come back with config space as 34058c2ecf20Sopenharmony_ci * it was after power was applied to PCIe bus. 34068c2ecf20Sopenharmony_ci * Attempt to restore config space which was saved 34078c2ecf20Sopenharmony_ci * when the driver was loaded, or the last time the 34088c2ecf20Sopenharmony_ci * nic was resumed from power saving mode. 34098c2ecf20Sopenharmony_ci */ 34108c2ecf20Sopenharmony_ci pci_restore_state(mgp->pdev); 34118c2ecf20Sopenharmony_ci 34128c2ecf20Sopenharmony_ci /* save state again for accounting reasons */ 34138c2ecf20Sopenharmony_ci pci_save_state(mgp->pdev); 34148c2ecf20Sopenharmony_ci 34158c2ecf20Sopenharmony_ci } else { 34168c2ecf20Sopenharmony_ci /* if we get back -1's from our slot, perhaps somebody 34178c2ecf20Sopenharmony_ci * powered off our card. Don't try to reset it in 34188c2ecf20Sopenharmony_ci * this case */ 34198c2ecf20Sopenharmony_ci if (cmd == 0xffff) { 34208c2ecf20Sopenharmony_ci pci_read_config_word(mgp->pdev, PCI_VENDOR_ID, &vendor); 34218c2ecf20Sopenharmony_ci if (vendor == 0xffff) { 34228c2ecf20Sopenharmony_ci netdev_err(mgp->dev, "device disappeared!\n"); 34238c2ecf20Sopenharmony_ci return; 34248c2ecf20Sopenharmony_ci } 34258c2ecf20Sopenharmony_ci } 34268c2ecf20Sopenharmony_ci /* Perhaps it is a software error. See if stuck slice 34278c2ecf20Sopenharmony_ci * has recovered, reset if not */ 34288c2ecf20Sopenharmony_ci rx_pause_cnt = ntohl(mgp->ss[0].fw_stats->dropped_pause); 34298c2ecf20Sopenharmony_ci for (i = 0; i < mgp->num_slices; i++) { 34308c2ecf20Sopenharmony_ci ss = mgp->ss; 34318c2ecf20Sopenharmony_ci if (ss->stuck) { 34328c2ecf20Sopenharmony_ci myri10ge_check_slice(ss, &reset_needed, 34338c2ecf20Sopenharmony_ci &busy_slice_cnt, 34348c2ecf20Sopenharmony_ci rx_pause_cnt); 34358c2ecf20Sopenharmony_ci ss->stuck = 0; 34368c2ecf20Sopenharmony_ci } 34378c2ecf20Sopenharmony_ci } 34388c2ecf20Sopenharmony_ci if (!reset_needed) { 34398c2ecf20Sopenharmony_ci netdev_dbg(mgp->dev, "not resetting\n"); 34408c2ecf20Sopenharmony_ci return; 34418c2ecf20Sopenharmony_ci } 34428c2ecf20Sopenharmony_ci 34438c2ecf20Sopenharmony_ci netdev_err(mgp->dev, "device timeout, resetting\n"); 34448c2ecf20Sopenharmony_ci } 34458c2ecf20Sopenharmony_ci 34468c2ecf20Sopenharmony_ci if (!rebooted) { 34478c2ecf20Sopenharmony_ci rtnl_lock(); 34488c2ecf20Sopenharmony_ci myri10ge_close(mgp->dev); 34498c2ecf20Sopenharmony_ci } 34508c2ecf20Sopenharmony_ci status = myri10ge_load_firmware(mgp, 1); 34518c2ecf20Sopenharmony_ci if (status != 0) 34528c2ecf20Sopenharmony_ci netdev_err(mgp->dev, "failed to load firmware\n"); 34538c2ecf20Sopenharmony_ci else 34548c2ecf20Sopenharmony_ci myri10ge_open(mgp->dev); 34558c2ecf20Sopenharmony_ci rtnl_unlock(); 34568c2ecf20Sopenharmony_ci} 34578c2ecf20Sopenharmony_ci 34588c2ecf20Sopenharmony_ci/* 34598c2ecf20Sopenharmony_ci * We use our own timer routine rather than relying upon 34608c2ecf20Sopenharmony_ci * netdev->tx_timeout because we have a very large hardware transmit 34618c2ecf20Sopenharmony_ci * queue. Due to the large queue, the netdev->tx_timeout function 34628c2ecf20Sopenharmony_ci * cannot detect a NIC with a parity error in a timely fashion if the 34638c2ecf20Sopenharmony_ci * NIC is lightly loaded. 34648c2ecf20Sopenharmony_ci */ 34658c2ecf20Sopenharmony_cistatic void myri10ge_watchdog_timer(struct timer_list *t) 34668c2ecf20Sopenharmony_ci{ 34678c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp; 34688c2ecf20Sopenharmony_ci struct myri10ge_slice_state *ss; 34698c2ecf20Sopenharmony_ci int i, reset_needed, busy_slice_cnt; 34708c2ecf20Sopenharmony_ci u32 rx_pause_cnt; 34718c2ecf20Sopenharmony_ci u16 cmd; 34728c2ecf20Sopenharmony_ci 34738c2ecf20Sopenharmony_ci mgp = from_timer(mgp, t, watchdog_timer); 34748c2ecf20Sopenharmony_ci 34758c2ecf20Sopenharmony_ci rx_pause_cnt = ntohl(mgp->ss[0].fw_stats->dropped_pause); 34768c2ecf20Sopenharmony_ci busy_slice_cnt = 0; 34778c2ecf20Sopenharmony_ci for (i = 0, reset_needed = 0; 34788c2ecf20Sopenharmony_ci i < mgp->num_slices && reset_needed == 0; ++i) { 34798c2ecf20Sopenharmony_ci 34808c2ecf20Sopenharmony_ci ss = &mgp->ss[i]; 34818c2ecf20Sopenharmony_ci if (ss->rx_small.watchdog_needed) { 34828c2ecf20Sopenharmony_ci myri10ge_alloc_rx_pages(mgp, &ss->rx_small, 34838c2ecf20Sopenharmony_ci mgp->small_bytes + MXGEFW_PAD, 34848c2ecf20Sopenharmony_ci 1); 34858c2ecf20Sopenharmony_ci if (ss->rx_small.fill_cnt - ss->rx_small.cnt >= 34868c2ecf20Sopenharmony_ci myri10ge_fill_thresh) 34878c2ecf20Sopenharmony_ci ss->rx_small.watchdog_needed = 0; 34888c2ecf20Sopenharmony_ci } 34898c2ecf20Sopenharmony_ci if (ss->rx_big.watchdog_needed) { 34908c2ecf20Sopenharmony_ci myri10ge_alloc_rx_pages(mgp, &ss->rx_big, 34918c2ecf20Sopenharmony_ci mgp->big_bytes, 1); 34928c2ecf20Sopenharmony_ci if (ss->rx_big.fill_cnt - ss->rx_big.cnt >= 34938c2ecf20Sopenharmony_ci myri10ge_fill_thresh) 34948c2ecf20Sopenharmony_ci ss->rx_big.watchdog_needed = 0; 34958c2ecf20Sopenharmony_ci } 34968c2ecf20Sopenharmony_ci myri10ge_check_slice(ss, &reset_needed, &busy_slice_cnt, 34978c2ecf20Sopenharmony_ci rx_pause_cnt); 34988c2ecf20Sopenharmony_ci } 34998c2ecf20Sopenharmony_ci /* if we've sent or received no traffic, poll the NIC to 35008c2ecf20Sopenharmony_ci * ensure it is still there. Otherwise, we risk not noticing 35018c2ecf20Sopenharmony_ci * an error in a timely fashion */ 35028c2ecf20Sopenharmony_ci if (busy_slice_cnt == 0) { 35038c2ecf20Sopenharmony_ci pci_read_config_word(mgp->pdev, PCI_COMMAND, &cmd); 35048c2ecf20Sopenharmony_ci if ((cmd & PCI_COMMAND_MASTER) == 0) { 35058c2ecf20Sopenharmony_ci reset_needed = 1; 35068c2ecf20Sopenharmony_ci } 35078c2ecf20Sopenharmony_ci } 35088c2ecf20Sopenharmony_ci mgp->watchdog_pause = rx_pause_cnt; 35098c2ecf20Sopenharmony_ci 35108c2ecf20Sopenharmony_ci if (reset_needed) { 35118c2ecf20Sopenharmony_ci schedule_work(&mgp->watchdog_work); 35128c2ecf20Sopenharmony_ci } else { 35138c2ecf20Sopenharmony_ci /* rearm timer */ 35148c2ecf20Sopenharmony_ci mod_timer(&mgp->watchdog_timer, 35158c2ecf20Sopenharmony_ci jiffies + myri10ge_watchdog_timeout * HZ); 35168c2ecf20Sopenharmony_ci } 35178c2ecf20Sopenharmony_ci} 35188c2ecf20Sopenharmony_ci 35198c2ecf20Sopenharmony_cistatic void myri10ge_free_slices(struct myri10ge_priv *mgp) 35208c2ecf20Sopenharmony_ci{ 35218c2ecf20Sopenharmony_ci struct myri10ge_slice_state *ss; 35228c2ecf20Sopenharmony_ci struct pci_dev *pdev = mgp->pdev; 35238c2ecf20Sopenharmony_ci size_t bytes; 35248c2ecf20Sopenharmony_ci int i; 35258c2ecf20Sopenharmony_ci 35268c2ecf20Sopenharmony_ci if (mgp->ss == NULL) 35278c2ecf20Sopenharmony_ci return; 35288c2ecf20Sopenharmony_ci 35298c2ecf20Sopenharmony_ci for (i = 0; i < mgp->num_slices; i++) { 35308c2ecf20Sopenharmony_ci ss = &mgp->ss[i]; 35318c2ecf20Sopenharmony_ci if (ss->rx_done.entry != NULL) { 35328c2ecf20Sopenharmony_ci bytes = mgp->max_intr_slots * 35338c2ecf20Sopenharmony_ci sizeof(*ss->rx_done.entry); 35348c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, bytes, 35358c2ecf20Sopenharmony_ci ss->rx_done.entry, ss->rx_done.bus); 35368c2ecf20Sopenharmony_ci ss->rx_done.entry = NULL; 35378c2ecf20Sopenharmony_ci } 35388c2ecf20Sopenharmony_ci if (ss->fw_stats != NULL) { 35398c2ecf20Sopenharmony_ci bytes = sizeof(*ss->fw_stats); 35408c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, bytes, 35418c2ecf20Sopenharmony_ci ss->fw_stats, ss->fw_stats_bus); 35428c2ecf20Sopenharmony_ci ss->fw_stats = NULL; 35438c2ecf20Sopenharmony_ci } 35448c2ecf20Sopenharmony_ci __netif_napi_del(&ss->napi); 35458c2ecf20Sopenharmony_ci } 35468c2ecf20Sopenharmony_ci /* Wait till napi structs are no longer used, and then free ss. */ 35478c2ecf20Sopenharmony_ci synchronize_net(); 35488c2ecf20Sopenharmony_ci kfree(mgp->ss); 35498c2ecf20Sopenharmony_ci mgp->ss = NULL; 35508c2ecf20Sopenharmony_ci} 35518c2ecf20Sopenharmony_ci 35528c2ecf20Sopenharmony_cistatic int myri10ge_alloc_slices(struct myri10ge_priv *mgp) 35538c2ecf20Sopenharmony_ci{ 35548c2ecf20Sopenharmony_ci struct myri10ge_slice_state *ss; 35558c2ecf20Sopenharmony_ci struct pci_dev *pdev = mgp->pdev; 35568c2ecf20Sopenharmony_ci size_t bytes; 35578c2ecf20Sopenharmony_ci int i; 35588c2ecf20Sopenharmony_ci 35598c2ecf20Sopenharmony_ci bytes = sizeof(*mgp->ss) * mgp->num_slices; 35608c2ecf20Sopenharmony_ci mgp->ss = kzalloc(bytes, GFP_KERNEL); 35618c2ecf20Sopenharmony_ci if (mgp->ss == NULL) { 35628c2ecf20Sopenharmony_ci return -ENOMEM; 35638c2ecf20Sopenharmony_ci } 35648c2ecf20Sopenharmony_ci 35658c2ecf20Sopenharmony_ci for (i = 0; i < mgp->num_slices; i++) { 35668c2ecf20Sopenharmony_ci ss = &mgp->ss[i]; 35678c2ecf20Sopenharmony_ci bytes = mgp->max_intr_slots * sizeof(*ss->rx_done.entry); 35688c2ecf20Sopenharmony_ci ss->rx_done.entry = dma_alloc_coherent(&pdev->dev, bytes, 35698c2ecf20Sopenharmony_ci &ss->rx_done.bus, 35708c2ecf20Sopenharmony_ci GFP_KERNEL); 35718c2ecf20Sopenharmony_ci if (ss->rx_done.entry == NULL) 35728c2ecf20Sopenharmony_ci goto abort; 35738c2ecf20Sopenharmony_ci bytes = sizeof(*ss->fw_stats); 35748c2ecf20Sopenharmony_ci ss->fw_stats = dma_alloc_coherent(&pdev->dev, bytes, 35758c2ecf20Sopenharmony_ci &ss->fw_stats_bus, 35768c2ecf20Sopenharmony_ci GFP_KERNEL); 35778c2ecf20Sopenharmony_ci if (ss->fw_stats == NULL) 35788c2ecf20Sopenharmony_ci goto abort; 35798c2ecf20Sopenharmony_ci ss->mgp = mgp; 35808c2ecf20Sopenharmony_ci ss->dev = mgp->dev; 35818c2ecf20Sopenharmony_ci netif_napi_add(ss->dev, &ss->napi, myri10ge_poll, 35828c2ecf20Sopenharmony_ci myri10ge_napi_weight); 35838c2ecf20Sopenharmony_ci } 35848c2ecf20Sopenharmony_ci return 0; 35858c2ecf20Sopenharmony_ciabort: 35868c2ecf20Sopenharmony_ci myri10ge_free_slices(mgp); 35878c2ecf20Sopenharmony_ci return -ENOMEM; 35888c2ecf20Sopenharmony_ci} 35898c2ecf20Sopenharmony_ci 35908c2ecf20Sopenharmony_ci/* 35918c2ecf20Sopenharmony_ci * This function determines the number of slices supported. 35928c2ecf20Sopenharmony_ci * The number slices is the minimum of the number of CPUS, 35938c2ecf20Sopenharmony_ci * the number of MSI-X irqs supported, the number of slices 35948c2ecf20Sopenharmony_ci * supported by the firmware 35958c2ecf20Sopenharmony_ci */ 35968c2ecf20Sopenharmony_cistatic void myri10ge_probe_slices(struct myri10ge_priv *mgp) 35978c2ecf20Sopenharmony_ci{ 35988c2ecf20Sopenharmony_ci struct myri10ge_cmd cmd; 35998c2ecf20Sopenharmony_ci struct pci_dev *pdev = mgp->pdev; 36008c2ecf20Sopenharmony_ci char *old_fw; 36018c2ecf20Sopenharmony_ci bool old_allocated; 36028c2ecf20Sopenharmony_ci int i, status, ncpus; 36038c2ecf20Sopenharmony_ci 36048c2ecf20Sopenharmony_ci mgp->num_slices = 1; 36058c2ecf20Sopenharmony_ci ncpus = netif_get_num_default_rss_queues(); 36068c2ecf20Sopenharmony_ci 36078c2ecf20Sopenharmony_ci if (myri10ge_max_slices == 1 || !pdev->msix_cap || 36088c2ecf20Sopenharmony_ci (myri10ge_max_slices == -1 && ncpus < 2)) 36098c2ecf20Sopenharmony_ci return; 36108c2ecf20Sopenharmony_ci 36118c2ecf20Sopenharmony_ci /* try to load the slice aware rss firmware */ 36128c2ecf20Sopenharmony_ci old_fw = mgp->fw_name; 36138c2ecf20Sopenharmony_ci old_allocated = mgp->fw_name_allocated; 36148c2ecf20Sopenharmony_ci /* don't free old_fw if we override it. */ 36158c2ecf20Sopenharmony_ci mgp->fw_name_allocated = false; 36168c2ecf20Sopenharmony_ci 36178c2ecf20Sopenharmony_ci if (myri10ge_fw_name != NULL) { 36188c2ecf20Sopenharmony_ci dev_info(&mgp->pdev->dev, "overriding rss firmware to %s\n", 36198c2ecf20Sopenharmony_ci myri10ge_fw_name); 36208c2ecf20Sopenharmony_ci set_fw_name(mgp, myri10ge_fw_name, false); 36218c2ecf20Sopenharmony_ci } else if (old_fw == myri10ge_fw_aligned) 36228c2ecf20Sopenharmony_ci set_fw_name(mgp, myri10ge_fw_rss_aligned, false); 36238c2ecf20Sopenharmony_ci else 36248c2ecf20Sopenharmony_ci set_fw_name(mgp, myri10ge_fw_rss_unaligned, false); 36258c2ecf20Sopenharmony_ci status = myri10ge_load_firmware(mgp, 0); 36268c2ecf20Sopenharmony_ci if (status != 0) { 36278c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Rss firmware not found\n"); 36288c2ecf20Sopenharmony_ci if (old_allocated) 36298c2ecf20Sopenharmony_ci kfree(old_fw); 36308c2ecf20Sopenharmony_ci return; 36318c2ecf20Sopenharmony_ci } 36328c2ecf20Sopenharmony_ci 36338c2ecf20Sopenharmony_ci /* hit the board with a reset to ensure it is alive */ 36348c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 36358c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, MXGEFW_CMD_RESET, &cmd, 0); 36368c2ecf20Sopenharmony_ci if (status != 0) { 36378c2ecf20Sopenharmony_ci dev_err(&mgp->pdev->dev, "failed reset\n"); 36388c2ecf20Sopenharmony_ci goto abort_with_fw; 36398c2ecf20Sopenharmony_ci } 36408c2ecf20Sopenharmony_ci 36418c2ecf20Sopenharmony_ci mgp->max_intr_slots = cmd.data0 / sizeof(struct mcp_slot); 36428c2ecf20Sopenharmony_ci 36438c2ecf20Sopenharmony_ci /* tell it the size of the interrupt queues */ 36448c2ecf20Sopenharmony_ci cmd.data0 = mgp->max_intr_slots * sizeof(struct mcp_slot); 36458c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd, 0); 36468c2ecf20Sopenharmony_ci if (status != 0) { 36478c2ecf20Sopenharmony_ci dev_err(&mgp->pdev->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n"); 36488c2ecf20Sopenharmony_ci goto abort_with_fw; 36498c2ecf20Sopenharmony_ci } 36508c2ecf20Sopenharmony_ci 36518c2ecf20Sopenharmony_ci /* ask the maximum number of slices it supports */ 36528c2ecf20Sopenharmony_ci status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd, 0); 36538c2ecf20Sopenharmony_ci if (status != 0) 36548c2ecf20Sopenharmony_ci goto abort_with_fw; 36558c2ecf20Sopenharmony_ci else 36568c2ecf20Sopenharmony_ci mgp->num_slices = cmd.data0; 36578c2ecf20Sopenharmony_ci 36588c2ecf20Sopenharmony_ci /* Only allow multiple slices if MSI-X is usable */ 36598c2ecf20Sopenharmony_ci if (!myri10ge_msi) { 36608c2ecf20Sopenharmony_ci goto abort_with_fw; 36618c2ecf20Sopenharmony_ci } 36628c2ecf20Sopenharmony_ci 36638c2ecf20Sopenharmony_ci /* if the admin did not specify a limit to how many 36648c2ecf20Sopenharmony_ci * slices we should use, cap it automatically to the 36658c2ecf20Sopenharmony_ci * number of CPUs currently online */ 36668c2ecf20Sopenharmony_ci if (myri10ge_max_slices == -1) 36678c2ecf20Sopenharmony_ci myri10ge_max_slices = ncpus; 36688c2ecf20Sopenharmony_ci 36698c2ecf20Sopenharmony_ci if (mgp->num_slices > myri10ge_max_slices) 36708c2ecf20Sopenharmony_ci mgp->num_slices = myri10ge_max_slices; 36718c2ecf20Sopenharmony_ci 36728c2ecf20Sopenharmony_ci /* Now try to allocate as many MSI-X vectors as we have 36738c2ecf20Sopenharmony_ci * slices. We give up on MSI-X if we can only get a single 36748c2ecf20Sopenharmony_ci * vector. */ 36758c2ecf20Sopenharmony_ci 36768c2ecf20Sopenharmony_ci mgp->msix_vectors = kcalloc(mgp->num_slices, sizeof(*mgp->msix_vectors), 36778c2ecf20Sopenharmony_ci GFP_KERNEL); 36788c2ecf20Sopenharmony_ci if (mgp->msix_vectors == NULL) 36798c2ecf20Sopenharmony_ci goto no_msix; 36808c2ecf20Sopenharmony_ci for (i = 0; i < mgp->num_slices; i++) { 36818c2ecf20Sopenharmony_ci mgp->msix_vectors[i].entry = i; 36828c2ecf20Sopenharmony_ci } 36838c2ecf20Sopenharmony_ci 36848c2ecf20Sopenharmony_ci while (mgp->num_slices > 1) { 36858c2ecf20Sopenharmony_ci mgp->num_slices = rounddown_pow_of_two(mgp->num_slices); 36868c2ecf20Sopenharmony_ci if (mgp->num_slices == 1) 36878c2ecf20Sopenharmony_ci goto no_msix; 36888c2ecf20Sopenharmony_ci status = pci_enable_msix_range(pdev, 36898c2ecf20Sopenharmony_ci mgp->msix_vectors, 36908c2ecf20Sopenharmony_ci mgp->num_slices, 36918c2ecf20Sopenharmony_ci mgp->num_slices); 36928c2ecf20Sopenharmony_ci if (status < 0) 36938c2ecf20Sopenharmony_ci goto no_msix; 36948c2ecf20Sopenharmony_ci 36958c2ecf20Sopenharmony_ci pci_disable_msix(pdev); 36968c2ecf20Sopenharmony_ci 36978c2ecf20Sopenharmony_ci if (status == mgp->num_slices) { 36988c2ecf20Sopenharmony_ci if (old_allocated) 36998c2ecf20Sopenharmony_ci kfree(old_fw); 37008c2ecf20Sopenharmony_ci return; 37018c2ecf20Sopenharmony_ci } else { 37028c2ecf20Sopenharmony_ci mgp->num_slices = status; 37038c2ecf20Sopenharmony_ci } 37048c2ecf20Sopenharmony_ci } 37058c2ecf20Sopenharmony_ci 37068c2ecf20Sopenharmony_cino_msix: 37078c2ecf20Sopenharmony_ci if (mgp->msix_vectors != NULL) { 37088c2ecf20Sopenharmony_ci kfree(mgp->msix_vectors); 37098c2ecf20Sopenharmony_ci mgp->msix_vectors = NULL; 37108c2ecf20Sopenharmony_ci } 37118c2ecf20Sopenharmony_ci 37128c2ecf20Sopenharmony_ciabort_with_fw: 37138c2ecf20Sopenharmony_ci mgp->num_slices = 1; 37148c2ecf20Sopenharmony_ci set_fw_name(mgp, old_fw, old_allocated); 37158c2ecf20Sopenharmony_ci myri10ge_load_firmware(mgp, 0); 37168c2ecf20Sopenharmony_ci} 37178c2ecf20Sopenharmony_ci 37188c2ecf20Sopenharmony_cistatic const struct net_device_ops myri10ge_netdev_ops = { 37198c2ecf20Sopenharmony_ci .ndo_open = myri10ge_open, 37208c2ecf20Sopenharmony_ci .ndo_stop = myri10ge_close, 37218c2ecf20Sopenharmony_ci .ndo_start_xmit = myri10ge_xmit, 37228c2ecf20Sopenharmony_ci .ndo_get_stats64 = myri10ge_get_stats, 37238c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 37248c2ecf20Sopenharmony_ci .ndo_change_mtu = myri10ge_change_mtu, 37258c2ecf20Sopenharmony_ci .ndo_set_rx_mode = myri10ge_set_multicast_list, 37268c2ecf20Sopenharmony_ci .ndo_set_mac_address = myri10ge_set_mac_address, 37278c2ecf20Sopenharmony_ci}; 37288c2ecf20Sopenharmony_ci 37298c2ecf20Sopenharmony_cistatic int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 37308c2ecf20Sopenharmony_ci{ 37318c2ecf20Sopenharmony_ci struct net_device *netdev; 37328c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp; 37338c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 37348c2ecf20Sopenharmony_ci int i; 37358c2ecf20Sopenharmony_ci int status = -ENXIO; 37368c2ecf20Sopenharmony_ci int dac_enabled; 37378c2ecf20Sopenharmony_ci unsigned hdr_offset, ss_offset; 37388c2ecf20Sopenharmony_ci static int board_number; 37398c2ecf20Sopenharmony_ci 37408c2ecf20Sopenharmony_ci netdev = alloc_etherdev_mq(sizeof(*mgp), MYRI10GE_MAX_SLICES); 37418c2ecf20Sopenharmony_ci if (netdev == NULL) 37428c2ecf20Sopenharmony_ci return -ENOMEM; 37438c2ecf20Sopenharmony_ci 37448c2ecf20Sopenharmony_ci SET_NETDEV_DEV(netdev, &pdev->dev); 37458c2ecf20Sopenharmony_ci 37468c2ecf20Sopenharmony_ci mgp = netdev_priv(netdev); 37478c2ecf20Sopenharmony_ci mgp->dev = netdev; 37488c2ecf20Sopenharmony_ci mgp->pdev = pdev; 37498c2ecf20Sopenharmony_ci mgp->pause = myri10ge_flow_control; 37508c2ecf20Sopenharmony_ci mgp->intr_coal_delay = myri10ge_intr_coal_delay; 37518c2ecf20Sopenharmony_ci mgp->msg_enable = netif_msg_init(myri10ge_debug, MYRI10GE_MSG_DEFAULT); 37528c2ecf20Sopenharmony_ci mgp->board_number = board_number; 37538c2ecf20Sopenharmony_ci init_waitqueue_head(&mgp->down_wq); 37548c2ecf20Sopenharmony_ci 37558c2ecf20Sopenharmony_ci if (pci_enable_device(pdev)) { 37568c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "pci_enable_device call failed\n"); 37578c2ecf20Sopenharmony_ci status = -ENODEV; 37588c2ecf20Sopenharmony_ci goto abort_with_netdev; 37598c2ecf20Sopenharmony_ci } 37608c2ecf20Sopenharmony_ci 37618c2ecf20Sopenharmony_ci /* Find the vendor-specific cap so we can check 37628c2ecf20Sopenharmony_ci * the reboot register later on */ 37638c2ecf20Sopenharmony_ci mgp->vendor_specific_offset 37648c2ecf20Sopenharmony_ci = pci_find_capability(pdev, PCI_CAP_ID_VNDR); 37658c2ecf20Sopenharmony_ci 37668c2ecf20Sopenharmony_ci /* Set our max read request to 4KB */ 37678c2ecf20Sopenharmony_ci status = pcie_set_readrq(pdev, 4096); 37688c2ecf20Sopenharmony_ci if (status != 0) { 37698c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Error %d writing PCI_EXP_DEVCTL\n", 37708c2ecf20Sopenharmony_ci status); 37718c2ecf20Sopenharmony_ci goto abort_with_enabled; 37728c2ecf20Sopenharmony_ci } 37738c2ecf20Sopenharmony_ci 37748c2ecf20Sopenharmony_ci myri10ge_mask_surprise_down(pdev); 37758c2ecf20Sopenharmony_ci pci_set_master(pdev); 37768c2ecf20Sopenharmony_ci dac_enabled = 1; 37778c2ecf20Sopenharmony_ci status = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); 37788c2ecf20Sopenharmony_ci if (status != 0) { 37798c2ecf20Sopenharmony_ci dac_enabled = 0; 37808c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 37818c2ecf20Sopenharmony_ci "64-bit pci address mask was refused, " 37828c2ecf20Sopenharmony_ci "trying 32-bit\n"); 37838c2ecf20Sopenharmony_ci status = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); 37848c2ecf20Sopenharmony_ci } 37858c2ecf20Sopenharmony_ci if (status != 0) { 37868c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Error %d setting DMA mask\n", status); 37878c2ecf20Sopenharmony_ci goto abort_with_enabled; 37888c2ecf20Sopenharmony_ci } 37898c2ecf20Sopenharmony_ci (void)pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); 37908c2ecf20Sopenharmony_ci mgp->cmd = dma_alloc_coherent(&pdev->dev, sizeof(*mgp->cmd), 37918c2ecf20Sopenharmony_ci &mgp->cmd_bus, GFP_KERNEL); 37928c2ecf20Sopenharmony_ci if (!mgp->cmd) { 37938c2ecf20Sopenharmony_ci status = -ENOMEM; 37948c2ecf20Sopenharmony_ci goto abort_with_enabled; 37958c2ecf20Sopenharmony_ci } 37968c2ecf20Sopenharmony_ci 37978c2ecf20Sopenharmony_ci mgp->board_span = pci_resource_len(pdev, 0); 37988c2ecf20Sopenharmony_ci mgp->iomem_base = pci_resource_start(pdev, 0); 37998c2ecf20Sopenharmony_ci mgp->wc_cookie = arch_phys_wc_add(mgp->iomem_base, mgp->board_span); 38008c2ecf20Sopenharmony_ci mgp->sram = ioremap_wc(mgp->iomem_base, mgp->board_span); 38018c2ecf20Sopenharmony_ci if (mgp->sram == NULL) { 38028c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "ioremap failed for %ld bytes at 0x%lx\n", 38038c2ecf20Sopenharmony_ci mgp->board_span, mgp->iomem_base); 38048c2ecf20Sopenharmony_ci status = -ENXIO; 38058c2ecf20Sopenharmony_ci goto abort_with_mtrr; 38068c2ecf20Sopenharmony_ci } 38078c2ecf20Sopenharmony_ci hdr_offset = 38088c2ecf20Sopenharmony_ci swab32(readl(mgp->sram + MCP_HEADER_PTR_OFFSET)) & 0xffffc; 38098c2ecf20Sopenharmony_ci ss_offset = hdr_offset + offsetof(struct mcp_gen_header, string_specs); 38108c2ecf20Sopenharmony_ci mgp->sram_size = swab32(readl(mgp->sram + ss_offset)); 38118c2ecf20Sopenharmony_ci if (mgp->sram_size > mgp->board_span || 38128c2ecf20Sopenharmony_ci mgp->sram_size <= MYRI10GE_FW_OFFSET) { 38138c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 38148c2ecf20Sopenharmony_ci "invalid sram_size %dB or board span %ldB\n", 38158c2ecf20Sopenharmony_ci mgp->sram_size, mgp->board_span); 38168c2ecf20Sopenharmony_ci status = -EINVAL; 38178c2ecf20Sopenharmony_ci goto abort_with_ioremap; 38188c2ecf20Sopenharmony_ci } 38198c2ecf20Sopenharmony_ci memcpy_fromio(mgp->eeprom_strings, 38208c2ecf20Sopenharmony_ci mgp->sram + mgp->sram_size, MYRI10GE_EEPROM_STRINGS_SIZE); 38218c2ecf20Sopenharmony_ci memset(mgp->eeprom_strings + MYRI10GE_EEPROM_STRINGS_SIZE - 2, 0, 2); 38228c2ecf20Sopenharmony_ci status = myri10ge_read_mac_addr(mgp); 38238c2ecf20Sopenharmony_ci if (status) 38248c2ecf20Sopenharmony_ci goto abort_with_ioremap; 38258c2ecf20Sopenharmony_ci 38268c2ecf20Sopenharmony_ci for (i = 0; i < ETH_ALEN; i++) 38278c2ecf20Sopenharmony_ci netdev->dev_addr[i] = mgp->mac_addr[i]; 38288c2ecf20Sopenharmony_ci 38298c2ecf20Sopenharmony_ci myri10ge_select_firmware(mgp); 38308c2ecf20Sopenharmony_ci 38318c2ecf20Sopenharmony_ci status = myri10ge_load_firmware(mgp, 1); 38328c2ecf20Sopenharmony_ci if (status != 0) { 38338c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to load firmware\n"); 38348c2ecf20Sopenharmony_ci goto abort_with_ioremap; 38358c2ecf20Sopenharmony_ci } 38368c2ecf20Sopenharmony_ci myri10ge_probe_slices(mgp); 38378c2ecf20Sopenharmony_ci status = myri10ge_alloc_slices(mgp); 38388c2ecf20Sopenharmony_ci if (status != 0) { 38398c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to alloc slice state\n"); 38408c2ecf20Sopenharmony_ci goto abort_with_firmware; 38418c2ecf20Sopenharmony_ci } 38428c2ecf20Sopenharmony_ci netif_set_real_num_tx_queues(netdev, mgp->num_slices); 38438c2ecf20Sopenharmony_ci netif_set_real_num_rx_queues(netdev, mgp->num_slices); 38448c2ecf20Sopenharmony_ci status = myri10ge_reset(mgp); 38458c2ecf20Sopenharmony_ci if (status != 0) { 38468c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed reset\n"); 38478c2ecf20Sopenharmony_ci goto abort_with_slices; 38488c2ecf20Sopenharmony_ci } 38498c2ecf20Sopenharmony_ci#ifdef CONFIG_MYRI10GE_DCA 38508c2ecf20Sopenharmony_ci myri10ge_setup_dca(mgp); 38518c2ecf20Sopenharmony_ci#endif 38528c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, mgp); 38538c2ecf20Sopenharmony_ci 38548c2ecf20Sopenharmony_ci /* MTU range: 68 - 9000 */ 38558c2ecf20Sopenharmony_ci netdev->min_mtu = ETH_MIN_MTU; 38568c2ecf20Sopenharmony_ci netdev->max_mtu = MYRI10GE_MAX_ETHER_MTU - ETH_HLEN; 38578c2ecf20Sopenharmony_ci 38588c2ecf20Sopenharmony_ci if (myri10ge_initial_mtu > netdev->max_mtu) 38598c2ecf20Sopenharmony_ci myri10ge_initial_mtu = netdev->max_mtu; 38608c2ecf20Sopenharmony_ci if (myri10ge_initial_mtu < netdev->min_mtu) 38618c2ecf20Sopenharmony_ci myri10ge_initial_mtu = netdev->min_mtu; 38628c2ecf20Sopenharmony_ci 38638c2ecf20Sopenharmony_ci netdev->mtu = myri10ge_initial_mtu; 38648c2ecf20Sopenharmony_ci 38658c2ecf20Sopenharmony_ci netdev->netdev_ops = &myri10ge_netdev_ops; 38668c2ecf20Sopenharmony_ci netdev->hw_features = mgp->features | NETIF_F_RXCSUM; 38678c2ecf20Sopenharmony_ci 38688c2ecf20Sopenharmony_ci /* fake NETIF_F_HW_VLAN_CTAG_RX for good GRO performance */ 38698c2ecf20Sopenharmony_ci netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX; 38708c2ecf20Sopenharmony_ci 38718c2ecf20Sopenharmony_ci netdev->features = netdev->hw_features; 38728c2ecf20Sopenharmony_ci 38738c2ecf20Sopenharmony_ci if (dac_enabled) 38748c2ecf20Sopenharmony_ci netdev->features |= NETIF_F_HIGHDMA; 38758c2ecf20Sopenharmony_ci 38768c2ecf20Sopenharmony_ci netdev->vlan_features |= mgp->features; 38778c2ecf20Sopenharmony_ci if (mgp->fw_ver_tiny < 37) 38788c2ecf20Sopenharmony_ci netdev->vlan_features &= ~NETIF_F_TSO6; 38798c2ecf20Sopenharmony_ci if (mgp->fw_ver_tiny < 32) 38808c2ecf20Sopenharmony_ci netdev->vlan_features &= ~NETIF_F_TSO; 38818c2ecf20Sopenharmony_ci 38828c2ecf20Sopenharmony_ci /* make sure we can get an irq, and that MSI can be 38838c2ecf20Sopenharmony_ci * setup (if available). */ 38848c2ecf20Sopenharmony_ci status = myri10ge_request_irq(mgp); 38858c2ecf20Sopenharmony_ci if (status != 0) 38868c2ecf20Sopenharmony_ci goto abort_with_slices; 38878c2ecf20Sopenharmony_ci myri10ge_free_irq(mgp); 38888c2ecf20Sopenharmony_ci 38898c2ecf20Sopenharmony_ci /* Save configuration space to be restored if the 38908c2ecf20Sopenharmony_ci * nic resets due to a parity error */ 38918c2ecf20Sopenharmony_ci pci_save_state(pdev); 38928c2ecf20Sopenharmony_ci 38938c2ecf20Sopenharmony_ci /* Setup the watchdog timer */ 38948c2ecf20Sopenharmony_ci timer_setup(&mgp->watchdog_timer, myri10ge_watchdog_timer, 0); 38958c2ecf20Sopenharmony_ci 38968c2ecf20Sopenharmony_ci netdev->ethtool_ops = &myri10ge_ethtool_ops; 38978c2ecf20Sopenharmony_ci INIT_WORK(&mgp->watchdog_work, myri10ge_watchdog); 38988c2ecf20Sopenharmony_ci status = register_netdev(netdev); 38998c2ecf20Sopenharmony_ci if (status != 0) { 39008c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "register_netdev failed: %d\n", status); 39018c2ecf20Sopenharmony_ci goto abort_with_state; 39028c2ecf20Sopenharmony_ci } 39038c2ecf20Sopenharmony_ci if (mgp->msix_enabled) 39048c2ecf20Sopenharmony_ci dev_info(dev, "%d MSI-X IRQs, tx bndry %d, fw %s, MTRR %s, WC Enabled\n", 39058c2ecf20Sopenharmony_ci mgp->num_slices, mgp->tx_boundary, mgp->fw_name, 39068c2ecf20Sopenharmony_ci (mgp->wc_cookie > 0 ? "Enabled" : "Disabled")); 39078c2ecf20Sopenharmony_ci else 39088c2ecf20Sopenharmony_ci dev_info(dev, "%s IRQ %d, tx bndry %d, fw %s, MTRR %s, WC Enabled\n", 39098c2ecf20Sopenharmony_ci mgp->msi_enabled ? "MSI" : "xPIC", 39108c2ecf20Sopenharmony_ci pdev->irq, mgp->tx_boundary, mgp->fw_name, 39118c2ecf20Sopenharmony_ci (mgp->wc_cookie > 0 ? "Enabled" : "Disabled")); 39128c2ecf20Sopenharmony_ci 39138c2ecf20Sopenharmony_ci board_number++; 39148c2ecf20Sopenharmony_ci return 0; 39158c2ecf20Sopenharmony_ci 39168c2ecf20Sopenharmony_ciabort_with_state: 39178c2ecf20Sopenharmony_ci pci_restore_state(pdev); 39188c2ecf20Sopenharmony_ci 39198c2ecf20Sopenharmony_ciabort_with_slices: 39208c2ecf20Sopenharmony_ci myri10ge_free_slices(mgp); 39218c2ecf20Sopenharmony_ci 39228c2ecf20Sopenharmony_ciabort_with_firmware: 39238c2ecf20Sopenharmony_ci kfree(mgp->msix_vectors); 39248c2ecf20Sopenharmony_ci myri10ge_dummy_rdma(mgp, 0); 39258c2ecf20Sopenharmony_ci 39268c2ecf20Sopenharmony_ciabort_with_ioremap: 39278c2ecf20Sopenharmony_ci if (mgp->mac_addr_string != NULL) 39288c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 39298c2ecf20Sopenharmony_ci "myri10ge_probe() failed: MAC=%s, SN=%ld\n", 39308c2ecf20Sopenharmony_ci mgp->mac_addr_string, mgp->serial_number); 39318c2ecf20Sopenharmony_ci iounmap(mgp->sram); 39328c2ecf20Sopenharmony_ci 39338c2ecf20Sopenharmony_ciabort_with_mtrr: 39348c2ecf20Sopenharmony_ci arch_phys_wc_del(mgp->wc_cookie); 39358c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, sizeof(*mgp->cmd), 39368c2ecf20Sopenharmony_ci mgp->cmd, mgp->cmd_bus); 39378c2ecf20Sopenharmony_ci 39388c2ecf20Sopenharmony_ciabort_with_enabled: 39398c2ecf20Sopenharmony_ci pci_disable_device(pdev); 39408c2ecf20Sopenharmony_ci 39418c2ecf20Sopenharmony_ciabort_with_netdev: 39428c2ecf20Sopenharmony_ci set_fw_name(mgp, NULL, false); 39438c2ecf20Sopenharmony_ci free_netdev(netdev); 39448c2ecf20Sopenharmony_ci return status; 39458c2ecf20Sopenharmony_ci} 39468c2ecf20Sopenharmony_ci 39478c2ecf20Sopenharmony_ci/* 39488c2ecf20Sopenharmony_ci * myri10ge_remove 39498c2ecf20Sopenharmony_ci * 39508c2ecf20Sopenharmony_ci * Does what is necessary to shutdown one Myrinet device. Called 39518c2ecf20Sopenharmony_ci * once for each Myrinet card by the kernel when a module is 39528c2ecf20Sopenharmony_ci * unloaded. 39538c2ecf20Sopenharmony_ci */ 39548c2ecf20Sopenharmony_cistatic void myri10ge_remove(struct pci_dev *pdev) 39558c2ecf20Sopenharmony_ci{ 39568c2ecf20Sopenharmony_ci struct myri10ge_priv *mgp; 39578c2ecf20Sopenharmony_ci struct net_device *netdev; 39588c2ecf20Sopenharmony_ci 39598c2ecf20Sopenharmony_ci mgp = pci_get_drvdata(pdev); 39608c2ecf20Sopenharmony_ci if (mgp == NULL) 39618c2ecf20Sopenharmony_ci return; 39628c2ecf20Sopenharmony_ci 39638c2ecf20Sopenharmony_ci cancel_work_sync(&mgp->watchdog_work); 39648c2ecf20Sopenharmony_ci netdev = mgp->dev; 39658c2ecf20Sopenharmony_ci unregister_netdev(netdev); 39668c2ecf20Sopenharmony_ci 39678c2ecf20Sopenharmony_ci#ifdef CONFIG_MYRI10GE_DCA 39688c2ecf20Sopenharmony_ci myri10ge_teardown_dca(mgp); 39698c2ecf20Sopenharmony_ci#endif 39708c2ecf20Sopenharmony_ci myri10ge_dummy_rdma(mgp, 0); 39718c2ecf20Sopenharmony_ci 39728c2ecf20Sopenharmony_ci /* avoid a memory leak */ 39738c2ecf20Sopenharmony_ci pci_restore_state(pdev); 39748c2ecf20Sopenharmony_ci 39758c2ecf20Sopenharmony_ci iounmap(mgp->sram); 39768c2ecf20Sopenharmony_ci arch_phys_wc_del(mgp->wc_cookie); 39778c2ecf20Sopenharmony_ci myri10ge_free_slices(mgp); 39788c2ecf20Sopenharmony_ci kfree(mgp->msix_vectors); 39798c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, sizeof(*mgp->cmd), 39808c2ecf20Sopenharmony_ci mgp->cmd, mgp->cmd_bus); 39818c2ecf20Sopenharmony_ci 39828c2ecf20Sopenharmony_ci set_fw_name(mgp, NULL, false); 39838c2ecf20Sopenharmony_ci free_netdev(netdev); 39848c2ecf20Sopenharmony_ci pci_disable_device(pdev); 39858c2ecf20Sopenharmony_ci} 39868c2ecf20Sopenharmony_ci 39878c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_MYRICOM_MYRI10GE_Z8E 0x0008 39888c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_MYRICOM_MYRI10GE_Z8E_9 0x0009 39898c2ecf20Sopenharmony_ci 39908c2ecf20Sopenharmony_cistatic const struct pci_device_id myri10ge_pci_tbl[] = { 39918c2ecf20Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_MYRICOM, PCI_DEVICE_ID_MYRICOM_MYRI10GE_Z8E)}, 39928c2ecf20Sopenharmony_ci {PCI_DEVICE 39938c2ecf20Sopenharmony_ci (PCI_VENDOR_ID_MYRICOM, PCI_DEVICE_ID_MYRICOM_MYRI10GE_Z8E_9)}, 39948c2ecf20Sopenharmony_ci {0}, 39958c2ecf20Sopenharmony_ci}; 39968c2ecf20Sopenharmony_ci 39978c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, myri10ge_pci_tbl); 39988c2ecf20Sopenharmony_ci 39998c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(myri10ge_pm_ops, myri10ge_suspend, myri10ge_resume); 40008c2ecf20Sopenharmony_ci 40018c2ecf20Sopenharmony_cistatic struct pci_driver myri10ge_driver = { 40028c2ecf20Sopenharmony_ci .name = "myri10ge", 40038c2ecf20Sopenharmony_ci .probe = myri10ge_probe, 40048c2ecf20Sopenharmony_ci .remove = myri10ge_remove, 40058c2ecf20Sopenharmony_ci .id_table = myri10ge_pci_tbl, 40068c2ecf20Sopenharmony_ci .driver.pm = &myri10ge_pm_ops, 40078c2ecf20Sopenharmony_ci}; 40088c2ecf20Sopenharmony_ci 40098c2ecf20Sopenharmony_ci#ifdef CONFIG_MYRI10GE_DCA 40108c2ecf20Sopenharmony_cistatic int 40118c2ecf20Sopenharmony_cimyri10ge_notify_dca(struct notifier_block *nb, unsigned long event, void *p) 40128c2ecf20Sopenharmony_ci{ 40138c2ecf20Sopenharmony_ci int err = driver_for_each_device(&myri10ge_driver.driver, 40148c2ecf20Sopenharmony_ci NULL, &event, 40158c2ecf20Sopenharmony_ci myri10ge_notify_dca_device); 40168c2ecf20Sopenharmony_ci 40178c2ecf20Sopenharmony_ci if (err) 40188c2ecf20Sopenharmony_ci return NOTIFY_BAD; 40198c2ecf20Sopenharmony_ci return NOTIFY_DONE; 40208c2ecf20Sopenharmony_ci} 40218c2ecf20Sopenharmony_ci 40228c2ecf20Sopenharmony_cistatic struct notifier_block myri10ge_dca_notifier = { 40238c2ecf20Sopenharmony_ci .notifier_call = myri10ge_notify_dca, 40248c2ecf20Sopenharmony_ci .next = NULL, 40258c2ecf20Sopenharmony_ci .priority = 0, 40268c2ecf20Sopenharmony_ci}; 40278c2ecf20Sopenharmony_ci#endif /* CONFIG_MYRI10GE_DCA */ 40288c2ecf20Sopenharmony_ci 40298c2ecf20Sopenharmony_cistatic __init int myri10ge_init_module(void) 40308c2ecf20Sopenharmony_ci{ 40318c2ecf20Sopenharmony_ci pr_info("Version %s\n", MYRI10GE_VERSION_STR); 40328c2ecf20Sopenharmony_ci 40338c2ecf20Sopenharmony_ci if (myri10ge_rss_hash > MXGEFW_RSS_HASH_TYPE_MAX) { 40348c2ecf20Sopenharmony_ci pr_err("Illegal rssh hash type %d, defaulting to source port\n", 40358c2ecf20Sopenharmony_ci myri10ge_rss_hash); 40368c2ecf20Sopenharmony_ci myri10ge_rss_hash = MXGEFW_RSS_HASH_TYPE_SRC_PORT; 40378c2ecf20Sopenharmony_ci } 40388c2ecf20Sopenharmony_ci#ifdef CONFIG_MYRI10GE_DCA 40398c2ecf20Sopenharmony_ci dca_register_notify(&myri10ge_dca_notifier); 40408c2ecf20Sopenharmony_ci#endif 40418c2ecf20Sopenharmony_ci if (myri10ge_max_slices > MYRI10GE_MAX_SLICES) 40428c2ecf20Sopenharmony_ci myri10ge_max_slices = MYRI10GE_MAX_SLICES; 40438c2ecf20Sopenharmony_ci 40448c2ecf20Sopenharmony_ci return pci_register_driver(&myri10ge_driver); 40458c2ecf20Sopenharmony_ci} 40468c2ecf20Sopenharmony_ci 40478c2ecf20Sopenharmony_cimodule_init(myri10ge_init_module); 40488c2ecf20Sopenharmony_ci 40498c2ecf20Sopenharmony_cistatic __exit void myri10ge_cleanup_module(void) 40508c2ecf20Sopenharmony_ci{ 40518c2ecf20Sopenharmony_ci#ifdef CONFIG_MYRI10GE_DCA 40528c2ecf20Sopenharmony_ci dca_unregister_notify(&myri10ge_dca_notifier); 40538c2ecf20Sopenharmony_ci#endif 40548c2ecf20Sopenharmony_ci pci_unregister_driver(&myri10ge_driver); 40558c2ecf20Sopenharmony_ci} 40568c2ecf20Sopenharmony_ci 40578c2ecf20Sopenharmony_cimodule_exit(myri10ge_cleanup_module); 4058