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