162306a36Sopenharmony_ci/*************************************************************************
262306a36Sopenharmony_ci * myri10ge.c: Myricom Myri-10G Ethernet driver.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2005 - 2011 Myricom, Inc.
562306a36Sopenharmony_ci * All rights reserved.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
862306a36Sopenharmony_ci * modification, are permitted provided that the following conditions
962306a36Sopenharmony_ci * are met:
1062306a36Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright
1162306a36Sopenharmony_ci *    notice, this list of conditions and the following disclaimer.
1262306a36Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright
1362306a36Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in the
1462306a36Sopenharmony_ci *    documentation and/or other materials provided with the distribution.
1562306a36Sopenharmony_ci * 3. Neither the name of Myricom, Inc. nor the names of its contributors
1662306a36Sopenharmony_ci *    may be used to endorse or promote products derived from this software
1762306a36Sopenharmony_ci *    without specific prior written permission.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
2062306a36Sopenharmony_ci * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2162306a36Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2262306a36Sopenharmony_ci * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
2362306a36Sopenharmony_ci * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2462306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2562306a36Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2662306a36Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2762306a36Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2862306a36Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2962306a36Sopenharmony_ci * POSSIBILITY OF SUCH DAMAGE.
3062306a36Sopenharmony_ci *
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci * If the eeprom on your board is not recent enough, you will need to get a
3362306a36Sopenharmony_ci * newer firmware image at:
3462306a36Sopenharmony_ci *   http://www.myri.com/scs/download-Myri10GE.html
3562306a36Sopenharmony_ci *
3662306a36Sopenharmony_ci * Contact Information:
3762306a36Sopenharmony_ci *   <help@myri.com>
3862306a36Sopenharmony_ci *   Myricom, Inc., 325N Santa Anita Avenue, Arcadia, CA 91006
3962306a36Sopenharmony_ci *************************************************************************/
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#include <linux/tcp.h>
4462306a36Sopenharmony_ci#include <linux/netdevice.h>
4562306a36Sopenharmony_ci#include <linux/skbuff.h>
4662306a36Sopenharmony_ci#include <linux/string.h>
4762306a36Sopenharmony_ci#include <linux/module.h>
4862306a36Sopenharmony_ci#include <linux/pci.h>
4962306a36Sopenharmony_ci#include <linux/dma-mapping.h>
5062306a36Sopenharmony_ci#include <linux/etherdevice.h>
5162306a36Sopenharmony_ci#include <linux/if_ether.h>
5262306a36Sopenharmony_ci#include <linux/if_vlan.h>
5362306a36Sopenharmony_ci#include <linux/dca.h>
5462306a36Sopenharmony_ci#include <linux/ip.h>
5562306a36Sopenharmony_ci#include <linux/inet.h>
5662306a36Sopenharmony_ci#include <linux/in.h>
5762306a36Sopenharmony_ci#include <linux/ethtool.h>
5862306a36Sopenharmony_ci#include <linux/firmware.h>
5962306a36Sopenharmony_ci#include <linux/delay.h>
6062306a36Sopenharmony_ci#include <linux/timer.h>
6162306a36Sopenharmony_ci#include <linux/vmalloc.h>
6262306a36Sopenharmony_ci#include <linux/crc32.h>
6362306a36Sopenharmony_ci#include <linux/moduleparam.h>
6462306a36Sopenharmony_ci#include <linux/io.h>
6562306a36Sopenharmony_ci#include <linux/log2.h>
6662306a36Sopenharmony_ci#include <linux/slab.h>
6762306a36Sopenharmony_ci#include <linux/prefetch.h>
6862306a36Sopenharmony_ci#include <net/checksum.h>
6962306a36Sopenharmony_ci#include <net/gso.h>
7062306a36Sopenharmony_ci#include <net/ip.h>
7162306a36Sopenharmony_ci#include <net/tcp.h>
7262306a36Sopenharmony_ci#include <asm/byteorder.h>
7362306a36Sopenharmony_ci#include <asm/processor.h>
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci#include "myri10ge_mcp.h"
7662306a36Sopenharmony_ci#include "myri10ge_mcp_gen_header.h"
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci#define MYRI10GE_VERSION_STR "1.5.3-1.534"
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ciMODULE_DESCRIPTION("Myricom 10G driver (10GbE)");
8162306a36Sopenharmony_ciMODULE_AUTHOR("Maintainer: help@myri.com");
8262306a36Sopenharmony_ciMODULE_VERSION(MYRI10GE_VERSION_STR);
8362306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci#define MYRI10GE_MAX_ETHER_MTU 9014
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci#define MYRI10GE_ETH_STOPPED 0
8862306a36Sopenharmony_ci#define MYRI10GE_ETH_STOPPING 1
8962306a36Sopenharmony_ci#define MYRI10GE_ETH_STARTING 2
9062306a36Sopenharmony_ci#define MYRI10GE_ETH_RUNNING 3
9162306a36Sopenharmony_ci#define MYRI10GE_ETH_OPEN_FAILED 4
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#define MYRI10GE_EEPROM_STRINGS_SIZE 256
9462306a36Sopenharmony_ci#define MYRI10GE_MAX_SEND_DESC_TSO ((65536 / 2048) * 2)
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci#define MYRI10GE_NO_CONFIRM_DATA htonl(0xffffffff)
9762306a36Sopenharmony_ci#define MYRI10GE_NO_RESPONSE_RESULT 0xffffffff
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci#define MYRI10GE_ALLOC_ORDER 0
10062306a36Sopenharmony_ci#define MYRI10GE_ALLOC_SIZE ((1 << MYRI10GE_ALLOC_ORDER) * PAGE_SIZE)
10162306a36Sopenharmony_ci#define MYRI10GE_MAX_FRAGS_PER_FRAME (MYRI10GE_MAX_ETHER_MTU/MYRI10GE_ALLOC_SIZE + 1)
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci#define MYRI10GE_MAX_SLICES 32
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistruct myri10ge_rx_buffer_state {
10662306a36Sopenharmony_ci	struct page *page;
10762306a36Sopenharmony_ci	int page_offset;
10862306a36Sopenharmony_ci	DEFINE_DMA_UNMAP_ADDR(bus);
10962306a36Sopenharmony_ci	DEFINE_DMA_UNMAP_LEN(len);
11062306a36Sopenharmony_ci};
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistruct myri10ge_tx_buffer_state {
11362306a36Sopenharmony_ci	struct sk_buff *skb;
11462306a36Sopenharmony_ci	int last;
11562306a36Sopenharmony_ci	DEFINE_DMA_UNMAP_ADDR(bus);
11662306a36Sopenharmony_ci	DEFINE_DMA_UNMAP_LEN(len);
11762306a36Sopenharmony_ci};
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistruct myri10ge_cmd {
12062306a36Sopenharmony_ci	u32 data0;
12162306a36Sopenharmony_ci	u32 data1;
12262306a36Sopenharmony_ci	u32 data2;
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistruct myri10ge_rx_buf {
12662306a36Sopenharmony_ci	struct mcp_kreq_ether_recv __iomem *lanai;	/* lanai ptr for recv ring */
12762306a36Sopenharmony_ci	struct mcp_kreq_ether_recv *shadow;	/* host shadow of recv ring */
12862306a36Sopenharmony_ci	struct myri10ge_rx_buffer_state *info;
12962306a36Sopenharmony_ci	struct page *page;
13062306a36Sopenharmony_ci	dma_addr_t bus;
13162306a36Sopenharmony_ci	int page_offset;
13262306a36Sopenharmony_ci	int cnt;
13362306a36Sopenharmony_ci	int fill_cnt;
13462306a36Sopenharmony_ci	int alloc_fail;
13562306a36Sopenharmony_ci	int mask;		/* number of rx slots -1 */
13662306a36Sopenharmony_ci	int watchdog_needed;
13762306a36Sopenharmony_ci};
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistruct myri10ge_tx_buf {
14062306a36Sopenharmony_ci	struct mcp_kreq_ether_send __iomem *lanai;	/* lanai ptr for sendq */
14162306a36Sopenharmony_ci	__be32 __iomem *send_go;	/* "go" doorbell ptr */
14262306a36Sopenharmony_ci	__be32 __iomem *send_stop;	/* "stop" doorbell ptr */
14362306a36Sopenharmony_ci	struct mcp_kreq_ether_send *req_list;	/* host shadow of sendq */
14462306a36Sopenharmony_ci	char *req_bytes;
14562306a36Sopenharmony_ci	struct myri10ge_tx_buffer_state *info;
14662306a36Sopenharmony_ci	int mask;		/* number of transmit slots -1  */
14762306a36Sopenharmony_ci	int req ____cacheline_aligned;	/* transmit slots submitted     */
14862306a36Sopenharmony_ci	int pkt_start;		/* packets started */
14962306a36Sopenharmony_ci	int stop_queue;
15062306a36Sopenharmony_ci	int linearized;
15162306a36Sopenharmony_ci	int done ____cacheline_aligned;	/* transmit slots completed     */
15262306a36Sopenharmony_ci	int pkt_done;		/* packets completed */
15362306a36Sopenharmony_ci	int wake_queue;
15462306a36Sopenharmony_ci	int queue_active;
15562306a36Sopenharmony_ci};
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistruct myri10ge_rx_done {
15862306a36Sopenharmony_ci	struct mcp_slot *entry;
15962306a36Sopenharmony_ci	dma_addr_t bus;
16062306a36Sopenharmony_ci	int cnt;
16162306a36Sopenharmony_ci	int idx;
16262306a36Sopenharmony_ci};
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistruct myri10ge_slice_netstats {
16562306a36Sopenharmony_ci	unsigned long rx_packets;
16662306a36Sopenharmony_ci	unsigned long tx_packets;
16762306a36Sopenharmony_ci	unsigned long rx_bytes;
16862306a36Sopenharmony_ci	unsigned long tx_bytes;
16962306a36Sopenharmony_ci	unsigned long rx_dropped;
17062306a36Sopenharmony_ci	unsigned long tx_dropped;
17162306a36Sopenharmony_ci};
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistruct myri10ge_slice_state {
17462306a36Sopenharmony_ci	struct myri10ge_tx_buf tx;	/* transmit ring        */
17562306a36Sopenharmony_ci	struct myri10ge_rx_buf rx_small;
17662306a36Sopenharmony_ci	struct myri10ge_rx_buf rx_big;
17762306a36Sopenharmony_ci	struct myri10ge_rx_done rx_done;
17862306a36Sopenharmony_ci	struct net_device *dev;
17962306a36Sopenharmony_ci	struct napi_struct napi;
18062306a36Sopenharmony_ci	struct myri10ge_priv *mgp;
18162306a36Sopenharmony_ci	struct myri10ge_slice_netstats stats;
18262306a36Sopenharmony_ci	__be32 __iomem *irq_claim;
18362306a36Sopenharmony_ci	struct mcp_irq_data *fw_stats;
18462306a36Sopenharmony_ci	dma_addr_t fw_stats_bus;
18562306a36Sopenharmony_ci	int watchdog_tx_done;
18662306a36Sopenharmony_ci	int watchdog_tx_req;
18762306a36Sopenharmony_ci	int watchdog_rx_done;
18862306a36Sopenharmony_ci	int stuck;
18962306a36Sopenharmony_ci#ifdef CONFIG_MYRI10GE_DCA
19062306a36Sopenharmony_ci	int cached_dca_tag;
19162306a36Sopenharmony_ci	int cpu;
19262306a36Sopenharmony_ci	__be32 __iomem *dca_tag;
19362306a36Sopenharmony_ci#endif
19462306a36Sopenharmony_ci	char irq_desc[32];
19562306a36Sopenharmony_ci};
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistruct myri10ge_priv {
19862306a36Sopenharmony_ci	struct myri10ge_slice_state *ss;
19962306a36Sopenharmony_ci	int tx_boundary;	/* boundary transmits cannot cross */
20062306a36Sopenharmony_ci	int num_slices;
20162306a36Sopenharmony_ci	int running;		/* running?             */
20262306a36Sopenharmony_ci	int small_bytes;
20362306a36Sopenharmony_ci	int big_bytes;
20462306a36Sopenharmony_ci	int max_intr_slots;
20562306a36Sopenharmony_ci	struct net_device *dev;
20662306a36Sopenharmony_ci	u8 __iomem *sram;
20762306a36Sopenharmony_ci	int sram_size;
20862306a36Sopenharmony_ci	unsigned long board_span;
20962306a36Sopenharmony_ci	unsigned long iomem_base;
21062306a36Sopenharmony_ci	__be32 __iomem *irq_deassert;
21162306a36Sopenharmony_ci	char *mac_addr_string;
21262306a36Sopenharmony_ci	struct mcp_cmd_response *cmd;
21362306a36Sopenharmony_ci	dma_addr_t cmd_bus;
21462306a36Sopenharmony_ci	struct pci_dev *pdev;
21562306a36Sopenharmony_ci	int msi_enabled;
21662306a36Sopenharmony_ci	int msix_enabled;
21762306a36Sopenharmony_ci	struct msix_entry *msix_vectors;
21862306a36Sopenharmony_ci#ifdef CONFIG_MYRI10GE_DCA
21962306a36Sopenharmony_ci	int dca_enabled;
22062306a36Sopenharmony_ci	int relaxed_order;
22162306a36Sopenharmony_ci#endif
22262306a36Sopenharmony_ci	u32 link_state;
22362306a36Sopenharmony_ci	unsigned int rdma_tags_available;
22462306a36Sopenharmony_ci	int intr_coal_delay;
22562306a36Sopenharmony_ci	__be32 __iomem *intr_coal_delay_ptr;
22662306a36Sopenharmony_ci	int wc_cookie;
22762306a36Sopenharmony_ci	int down_cnt;
22862306a36Sopenharmony_ci	wait_queue_head_t down_wq;
22962306a36Sopenharmony_ci	struct work_struct watchdog_work;
23062306a36Sopenharmony_ci	struct timer_list watchdog_timer;
23162306a36Sopenharmony_ci	int watchdog_resets;
23262306a36Sopenharmony_ci	int watchdog_pause;
23362306a36Sopenharmony_ci	int pause;
23462306a36Sopenharmony_ci	bool fw_name_allocated;
23562306a36Sopenharmony_ci	char *fw_name;
23662306a36Sopenharmony_ci	char eeprom_strings[MYRI10GE_EEPROM_STRINGS_SIZE];
23762306a36Sopenharmony_ci	char *product_code_string;
23862306a36Sopenharmony_ci	char fw_version[128];
23962306a36Sopenharmony_ci	int fw_ver_major;
24062306a36Sopenharmony_ci	int fw_ver_minor;
24162306a36Sopenharmony_ci	int fw_ver_tiny;
24262306a36Sopenharmony_ci	int adopted_rx_filter_bug;
24362306a36Sopenharmony_ci	u8 mac_addr[ETH_ALEN];		/* eeprom mac address */
24462306a36Sopenharmony_ci	unsigned long serial_number;
24562306a36Sopenharmony_ci	int vendor_specific_offset;
24662306a36Sopenharmony_ci	int fw_multicast_support;
24762306a36Sopenharmony_ci	u32 features;
24862306a36Sopenharmony_ci	u32 max_tso6;
24962306a36Sopenharmony_ci	u32 read_dma;
25062306a36Sopenharmony_ci	u32 write_dma;
25162306a36Sopenharmony_ci	u32 read_write_dma;
25262306a36Sopenharmony_ci	u32 link_changes;
25362306a36Sopenharmony_ci	u32 msg_enable;
25462306a36Sopenharmony_ci	unsigned int board_number;
25562306a36Sopenharmony_ci	int rebooted;
25662306a36Sopenharmony_ci};
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic char *myri10ge_fw_unaligned = "myri10ge_ethp_z8e.dat";
25962306a36Sopenharmony_cistatic char *myri10ge_fw_aligned = "myri10ge_eth_z8e.dat";
26062306a36Sopenharmony_cistatic char *myri10ge_fw_rss_unaligned = "myri10ge_rss_ethp_z8e.dat";
26162306a36Sopenharmony_cistatic char *myri10ge_fw_rss_aligned = "myri10ge_rss_eth_z8e.dat";
26262306a36Sopenharmony_ciMODULE_FIRMWARE("myri10ge_ethp_z8e.dat");
26362306a36Sopenharmony_ciMODULE_FIRMWARE("myri10ge_eth_z8e.dat");
26462306a36Sopenharmony_ciMODULE_FIRMWARE("myri10ge_rss_ethp_z8e.dat");
26562306a36Sopenharmony_ciMODULE_FIRMWARE("myri10ge_rss_eth_z8e.dat");
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci/* Careful: must be accessed under kernel_param_lock() */
26862306a36Sopenharmony_cistatic char *myri10ge_fw_name = NULL;
26962306a36Sopenharmony_cimodule_param(myri10ge_fw_name, charp, 0644);
27062306a36Sopenharmony_ciMODULE_PARM_DESC(myri10ge_fw_name, "Firmware image name");
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci#define MYRI10GE_MAX_BOARDS 8
27362306a36Sopenharmony_cistatic char *myri10ge_fw_names[MYRI10GE_MAX_BOARDS] =
27462306a36Sopenharmony_ci    {[0 ... (MYRI10GE_MAX_BOARDS - 1)] = NULL };
27562306a36Sopenharmony_cimodule_param_array_named(myri10ge_fw_names, myri10ge_fw_names, charp, NULL,
27662306a36Sopenharmony_ci			 0444);
27762306a36Sopenharmony_ciMODULE_PARM_DESC(myri10ge_fw_names, "Firmware image names per board");
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic int myri10ge_ecrc_enable = 1;
28062306a36Sopenharmony_cimodule_param(myri10ge_ecrc_enable, int, 0444);
28162306a36Sopenharmony_ciMODULE_PARM_DESC(myri10ge_ecrc_enable, "Enable Extended CRC on PCI-E");
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic int myri10ge_small_bytes = -1;	/* -1 == auto */
28462306a36Sopenharmony_cimodule_param(myri10ge_small_bytes, int, 0644);
28562306a36Sopenharmony_ciMODULE_PARM_DESC(myri10ge_small_bytes, "Threshold of small packets");
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic int myri10ge_msi = 1;	/* enable msi by default */
28862306a36Sopenharmony_cimodule_param(myri10ge_msi, int, 0644);
28962306a36Sopenharmony_ciMODULE_PARM_DESC(myri10ge_msi, "Enable Message Signalled Interrupts");
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic int myri10ge_intr_coal_delay = 75;
29262306a36Sopenharmony_cimodule_param(myri10ge_intr_coal_delay, int, 0444);
29362306a36Sopenharmony_ciMODULE_PARM_DESC(myri10ge_intr_coal_delay, "Interrupt coalescing delay");
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic int myri10ge_flow_control = 1;
29662306a36Sopenharmony_cimodule_param(myri10ge_flow_control, int, 0444);
29762306a36Sopenharmony_ciMODULE_PARM_DESC(myri10ge_flow_control, "Pause parameter");
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic int myri10ge_deassert_wait = 1;
30062306a36Sopenharmony_cimodule_param(myri10ge_deassert_wait, int, 0644);
30162306a36Sopenharmony_ciMODULE_PARM_DESC(myri10ge_deassert_wait,
30262306a36Sopenharmony_ci		 "Wait when deasserting legacy interrupts");
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic int myri10ge_force_firmware = 0;
30562306a36Sopenharmony_cimodule_param(myri10ge_force_firmware, int, 0444);
30662306a36Sopenharmony_ciMODULE_PARM_DESC(myri10ge_force_firmware,
30762306a36Sopenharmony_ci		 "Force firmware to assume aligned completions");
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic int myri10ge_initial_mtu = MYRI10GE_MAX_ETHER_MTU - ETH_HLEN;
31062306a36Sopenharmony_cimodule_param(myri10ge_initial_mtu, int, 0444);
31162306a36Sopenharmony_ciMODULE_PARM_DESC(myri10ge_initial_mtu, "Initial MTU");
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic int myri10ge_napi_weight = 64;
31462306a36Sopenharmony_cimodule_param(myri10ge_napi_weight, int, 0444);
31562306a36Sopenharmony_ciMODULE_PARM_DESC(myri10ge_napi_weight, "Set NAPI weight");
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic int myri10ge_watchdog_timeout = 1;
31862306a36Sopenharmony_cimodule_param(myri10ge_watchdog_timeout, int, 0444);
31962306a36Sopenharmony_ciMODULE_PARM_DESC(myri10ge_watchdog_timeout, "Set watchdog timeout");
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic int myri10ge_max_irq_loops = 1048576;
32262306a36Sopenharmony_cimodule_param(myri10ge_max_irq_loops, int, 0444);
32362306a36Sopenharmony_ciMODULE_PARM_DESC(myri10ge_max_irq_loops,
32462306a36Sopenharmony_ci		 "Set stuck legacy IRQ detection threshold");
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci#define MYRI10GE_MSG_DEFAULT NETIF_MSG_LINK
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic int myri10ge_debug = -1;	/* defaults above */
32962306a36Sopenharmony_cimodule_param(myri10ge_debug, int, 0);
33062306a36Sopenharmony_ciMODULE_PARM_DESC(myri10ge_debug, "Debug level (0=none,...,16=all)");
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic int myri10ge_fill_thresh = 256;
33362306a36Sopenharmony_cimodule_param(myri10ge_fill_thresh, int, 0644);
33462306a36Sopenharmony_ciMODULE_PARM_DESC(myri10ge_fill_thresh, "Number of empty rx slots allowed");
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic int myri10ge_reset_recover = 1;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic int myri10ge_max_slices = 1;
33962306a36Sopenharmony_cimodule_param(myri10ge_max_slices, int, 0444);
34062306a36Sopenharmony_ciMODULE_PARM_DESC(myri10ge_max_slices, "Max tx/rx queues");
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic int myri10ge_rss_hash = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT;
34362306a36Sopenharmony_cimodule_param(myri10ge_rss_hash, int, 0444);
34462306a36Sopenharmony_ciMODULE_PARM_DESC(myri10ge_rss_hash, "Type of RSS hashing to do");
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cistatic int myri10ge_dca = 1;
34762306a36Sopenharmony_cimodule_param(myri10ge_dca, int, 0444);
34862306a36Sopenharmony_ciMODULE_PARM_DESC(myri10ge_dca, "Enable DCA if possible");
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci#define MYRI10GE_FW_OFFSET 1024*1024
35162306a36Sopenharmony_ci#define MYRI10GE_HIGHPART_TO_U32(X) \
35262306a36Sopenharmony_ci(sizeof (X) == 8) ? ((u32)((u64)(X) >> 32)) : (0)
35362306a36Sopenharmony_ci#define MYRI10GE_LOWPART_TO_U32(X) ((u32)(X))
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci#define myri10ge_pio_copy(to,from,size) __iowrite64_copy(to,from,size/8)
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic void myri10ge_set_multicast_list(struct net_device *dev);
35862306a36Sopenharmony_cistatic netdev_tx_t myri10ge_sw_tso(struct sk_buff *skb,
35962306a36Sopenharmony_ci					 struct net_device *dev);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cistatic inline void put_be32(__be32 val, __be32 __iomem * p)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	__raw_writel((__force __u32) val, (__force void __iomem *)p);
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic void myri10ge_get_stats(struct net_device *dev,
36762306a36Sopenharmony_ci			       struct rtnl_link_stats64 *stats);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic void set_fw_name(struct myri10ge_priv *mgp, char *name, bool allocated)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	if (mgp->fw_name_allocated)
37262306a36Sopenharmony_ci		kfree(mgp->fw_name);
37362306a36Sopenharmony_ci	mgp->fw_name = name;
37462306a36Sopenharmony_ci	mgp->fw_name_allocated = allocated;
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic int
37862306a36Sopenharmony_cimyri10ge_send_cmd(struct myri10ge_priv *mgp, u32 cmd,
37962306a36Sopenharmony_ci		  struct myri10ge_cmd *data, int atomic)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	struct mcp_cmd *buf;
38262306a36Sopenharmony_ci	char buf_bytes[sizeof(*buf) + 8];
38362306a36Sopenharmony_ci	struct mcp_cmd_response *response = mgp->cmd;
38462306a36Sopenharmony_ci	char __iomem *cmd_addr = mgp->sram + MXGEFW_ETH_CMD;
38562306a36Sopenharmony_ci	u32 dma_low, dma_high, result, value;
38662306a36Sopenharmony_ci	int sleep_total = 0;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	/* ensure buf is aligned to 8 bytes */
38962306a36Sopenharmony_ci	buf = (struct mcp_cmd *)ALIGN((unsigned long)buf_bytes, 8);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	buf->data0 = htonl(data->data0);
39262306a36Sopenharmony_ci	buf->data1 = htonl(data->data1);
39362306a36Sopenharmony_ci	buf->data2 = htonl(data->data2);
39462306a36Sopenharmony_ci	buf->cmd = htonl(cmd);
39562306a36Sopenharmony_ci	dma_low = MYRI10GE_LOWPART_TO_U32(mgp->cmd_bus);
39662306a36Sopenharmony_ci	dma_high = MYRI10GE_HIGHPART_TO_U32(mgp->cmd_bus);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	buf->response_addr.low = htonl(dma_low);
39962306a36Sopenharmony_ci	buf->response_addr.high = htonl(dma_high);
40062306a36Sopenharmony_ci	response->result = htonl(MYRI10GE_NO_RESPONSE_RESULT);
40162306a36Sopenharmony_ci	mb();
40262306a36Sopenharmony_ci	myri10ge_pio_copy(cmd_addr, buf, sizeof(*buf));
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	/* wait up to 15ms. Longest command is the DMA benchmark,
40562306a36Sopenharmony_ci	 * which is capped at 5ms, but runs from a timeout handler
40662306a36Sopenharmony_ci	 * that runs every 7.8ms. So a 15ms timeout leaves us with
40762306a36Sopenharmony_ci	 * a 2.2ms margin
40862306a36Sopenharmony_ci	 */
40962306a36Sopenharmony_ci	if (atomic) {
41062306a36Sopenharmony_ci		/* if atomic is set, do not sleep,
41162306a36Sopenharmony_ci		 * and try to get the completion quickly
41262306a36Sopenharmony_ci		 * (1ms will be enough for those commands) */
41362306a36Sopenharmony_ci		for (sleep_total = 0;
41462306a36Sopenharmony_ci		     sleep_total < 1000 &&
41562306a36Sopenharmony_ci		     response->result == htonl(MYRI10GE_NO_RESPONSE_RESULT);
41662306a36Sopenharmony_ci		     sleep_total += 10) {
41762306a36Sopenharmony_ci			udelay(10);
41862306a36Sopenharmony_ci			mb();
41962306a36Sopenharmony_ci		}
42062306a36Sopenharmony_ci	} else {
42162306a36Sopenharmony_ci		/* use msleep for most command */
42262306a36Sopenharmony_ci		for (sleep_total = 0;
42362306a36Sopenharmony_ci		     sleep_total < 15 &&
42462306a36Sopenharmony_ci		     response->result == htonl(MYRI10GE_NO_RESPONSE_RESULT);
42562306a36Sopenharmony_ci		     sleep_total++)
42662306a36Sopenharmony_ci			msleep(1);
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	result = ntohl(response->result);
43062306a36Sopenharmony_ci	value = ntohl(response->data);
43162306a36Sopenharmony_ci	if (result != MYRI10GE_NO_RESPONSE_RESULT) {
43262306a36Sopenharmony_ci		if (result == 0) {
43362306a36Sopenharmony_ci			data->data0 = value;
43462306a36Sopenharmony_ci			return 0;
43562306a36Sopenharmony_ci		} else if (result == MXGEFW_CMD_UNKNOWN) {
43662306a36Sopenharmony_ci			return -ENOSYS;
43762306a36Sopenharmony_ci		} else if (result == MXGEFW_CMD_ERROR_UNALIGNED) {
43862306a36Sopenharmony_ci			return -E2BIG;
43962306a36Sopenharmony_ci		} else if (result == MXGEFW_CMD_ERROR_RANGE &&
44062306a36Sopenharmony_ci			   cmd == MXGEFW_CMD_ENABLE_RSS_QUEUES &&
44162306a36Sopenharmony_ci			   (data->
44262306a36Sopenharmony_ci			    data1 & MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES) !=
44362306a36Sopenharmony_ci			   0) {
44462306a36Sopenharmony_ci			return -ERANGE;
44562306a36Sopenharmony_ci		} else {
44662306a36Sopenharmony_ci			dev_err(&mgp->pdev->dev,
44762306a36Sopenharmony_ci				"command %d failed, result = %d\n",
44862306a36Sopenharmony_ci				cmd, result);
44962306a36Sopenharmony_ci			return -ENXIO;
45062306a36Sopenharmony_ci		}
45162306a36Sopenharmony_ci	}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	dev_err(&mgp->pdev->dev, "command %d timed out, result = %d\n",
45462306a36Sopenharmony_ci		cmd, result);
45562306a36Sopenharmony_ci	return -EAGAIN;
45662306a36Sopenharmony_ci}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci/*
45962306a36Sopenharmony_ci * The eeprom strings on the lanaiX have the format
46062306a36Sopenharmony_ci * SN=x\0
46162306a36Sopenharmony_ci * MAC=x:x:x:x:x:x\0
46262306a36Sopenharmony_ci * PT:ddd mmm xx xx:xx:xx xx\0
46362306a36Sopenharmony_ci * PV:ddd mmm xx xx:xx:xx xx\0
46462306a36Sopenharmony_ci */
46562306a36Sopenharmony_cistatic int myri10ge_read_mac_addr(struct myri10ge_priv *mgp)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	char *ptr, *limit;
46862306a36Sopenharmony_ci	int i;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	ptr = mgp->eeprom_strings;
47162306a36Sopenharmony_ci	limit = mgp->eeprom_strings + MYRI10GE_EEPROM_STRINGS_SIZE;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	while (*ptr != '\0' && ptr < limit) {
47462306a36Sopenharmony_ci		if (memcmp(ptr, "MAC=", 4) == 0) {
47562306a36Sopenharmony_ci			ptr += 4;
47662306a36Sopenharmony_ci			mgp->mac_addr_string = ptr;
47762306a36Sopenharmony_ci			for (i = 0; i < 6; i++) {
47862306a36Sopenharmony_ci				if ((ptr + 2) > limit)
47962306a36Sopenharmony_ci					goto abort;
48062306a36Sopenharmony_ci				mgp->mac_addr[i] =
48162306a36Sopenharmony_ci				    simple_strtoul(ptr, &ptr, 16);
48262306a36Sopenharmony_ci				ptr += 1;
48362306a36Sopenharmony_ci			}
48462306a36Sopenharmony_ci		}
48562306a36Sopenharmony_ci		if (memcmp(ptr, "PC=", 3) == 0) {
48662306a36Sopenharmony_ci			ptr += 3;
48762306a36Sopenharmony_ci			mgp->product_code_string = ptr;
48862306a36Sopenharmony_ci		}
48962306a36Sopenharmony_ci		if (memcmp((const void *)ptr, "SN=", 3) == 0) {
49062306a36Sopenharmony_ci			ptr += 3;
49162306a36Sopenharmony_ci			mgp->serial_number = simple_strtoul(ptr, &ptr, 10);
49262306a36Sopenharmony_ci		}
49362306a36Sopenharmony_ci		while (ptr < limit && *ptr++) ;
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	return 0;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ciabort:
49962306a36Sopenharmony_ci	dev_err(&mgp->pdev->dev, "failed to parse eeprom_strings\n");
50062306a36Sopenharmony_ci	return -ENXIO;
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci/*
50462306a36Sopenharmony_ci * Enable or disable periodic RDMAs from the host to make certain
50562306a36Sopenharmony_ci * chipsets resend dropped PCIe messages
50662306a36Sopenharmony_ci */
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_cistatic void myri10ge_dummy_rdma(struct myri10ge_priv *mgp, int enable)
50962306a36Sopenharmony_ci{
51062306a36Sopenharmony_ci	char __iomem *submit;
51162306a36Sopenharmony_ci	__be32 buf[16] __attribute__ ((__aligned__(8)));
51262306a36Sopenharmony_ci	u32 dma_low, dma_high;
51362306a36Sopenharmony_ci	int i;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	/* clear confirmation addr */
51662306a36Sopenharmony_ci	mgp->cmd->data = 0;
51762306a36Sopenharmony_ci	mb();
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	/* send a rdma command to the PCIe engine, and wait for the
52062306a36Sopenharmony_ci	 * response in the confirmation address.  The firmware should
52162306a36Sopenharmony_ci	 * write a -1 there to indicate it is alive and well
52262306a36Sopenharmony_ci	 */
52362306a36Sopenharmony_ci	dma_low = MYRI10GE_LOWPART_TO_U32(mgp->cmd_bus);
52462306a36Sopenharmony_ci	dma_high = MYRI10GE_HIGHPART_TO_U32(mgp->cmd_bus);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	buf[0] = htonl(dma_high);	/* confirm addr MSW */
52762306a36Sopenharmony_ci	buf[1] = htonl(dma_low);	/* confirm addr LSW */
52862306a36Sopenharmony_ci	buf[2] = MYRI10GE_NO_CONFIRM_DATA;	/* confirm data */
52962306a36Sopenharmony_ci	buf[3] = htonl(dma_high);	/* dummy addr MSW */
53062306a36Sopenharmony_ci	buf[4] = htonl(dma_low);	/* dummy addr LSW */
53162306a36Sopenharmony_ci	buf[5] = htonl(enable);	/* enable? */
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	submit = mgp->sram + MXGEFW_BOOT_DUMMY_RDMA;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	myri10ge_pio_copy(submit, &buf, sizeof(buf));
53662306a36Sopenharmony_ci	for (i = 0; mgp->cmd->data != MYRI10GE_NO_CONFIRM_DATA && i < 20; i++)
53762306a36Sopenharmony_ci		msleep(1);
53862306a36Sopenharmony_ci	if (mgp->cmd->data != MYRI10GE_NO_CONFIRM_DATA)
53962306a36Sopenharmony_ci		dev_err(&mgp->pdev->dev, "dummy rdma %s failed\n",
54062306a36Sopenharmony_ci			(enable ? "enable" : "disable"));
54162306a36Sopenharmony_ci}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_cistatic int
54462306a36Sopenharmony_cimyri10ge_validate_firmware(struct myri10ge_priv *mgp,
54562306a36Sopenharmony_ci			   struct mcp_gen_header *hdr)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	struct device *dev = &mgp->pdev->dev;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	/* check firmware type */
55062306a36Sopenharmony_ci	if (ntohl(hdr->mcp_type) != MCP_TYPE_ETH) {
55162306a36Sopenharmony_ci		dev_err(dev, "Bad firmware type: 0x%x\n", ntohl(hdr->mcp_type));
55262306a36Sopenharmony_ci		return -EINVAL;
55362306a36Sopenharmony_ci	}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	/* save firmware version for ethtool */
55662306a36Sopenharmony_ci	strscpy(mgp->fw_version, hdr->version, sizeof(mgp->fw_version));
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	sscanf(mgp->fw_version, "%d.%d.%d", &mgp->fw_ver_major,
55962306a36Sopenharmony_ci	       &mgp->fw_ver_minor, &mgp->fw_ver_tiny);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	if (!(mgp->fw_ver_major == MXGEFW_VERSION_MAJOR &&
56262306a36Sopenharmony_ci	      mgp->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
56362306a36Sopenharmony_ci		dev_err(dev, "Found firmware version %s\n", mgp->fw_version);
56462306a36Sopenharmony_ci		dev_err(dev, "Driver needs %d.%d\n", MXGEFW_VERSION_MAJOR,
56562306a36Sopenharmony_ci			MXGEFW_VERSION_MINOR);
56662306a36Sopenharmony_ci		return -EINVAL;
56762306a36Sopenharmony_ci	}
56862306a36Sopenharmony_ci	return 0;
56962306a36Sopenharmony_ci}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_cistatic int myri10ge_load_hotplug_firmware(struct myri10ge_priv *mgp, u32 * size)
57262306a36Sopenharmony_ci{
57362306a36Sopenharmony_ci	unsigned crc, reread_crc;
57462306a36Sopenharmony_ci	const struct firmware *fw;
57562306a36Sopenharmony_ci	struct device *dev = &mgp->pdev->dev;
57662306a36Sopenharmony_ci	unsigned char *fw_readback;
57762306a36Sopenharmony_ci	struct mcp_gen_header *hdr;
57862306a36Sopenharmony_ci	size_t hdr_offset;
57962306a36Sopenharmony_ci	int status;
58062306a36Sopenharmony_ci	unsigned i;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	if (request_firmware(&fw, mgp->fw_name, dev) < 0) {
58362306a36Sopenharmony_ci		dev_err(dev, "Unable to load %s firmware image via hotplug\n",
58462306a36Sopenharmony_ci			mgp->fw_name);
58562306a36Sopenharmony_ci		status = -EINVAL;
58662306a36Sopenharmony_ci		goto abort_with_nothing;
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	/* check size */
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	if (fw->size >= mgp->sram_size - MYRI10GE_FW_OFFSET ||
59262306a36Sopenharmony_ci	    fw->size < MCP_HEADER_PTR_OFFSET + 4) {
59362306a36Sopenharmony_ci		dev_err(dev, "Firmware size invalid:%d\n", (int)fw->size);
59462306a36Sopenharmony_ci		status = -EINVAL;
59562306a36Sopenharmony_ci		goto abort_with_fw;
59662306a36Sopenharmony_ci	}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	/* check id */
59962306a36Sopenharmony_ci	hdr_offset = ntohl(*(__be32 *) (fw->data + MCP_HEADER_PTR_OFFSET));
60062306a36Sopenharmony_ci	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw->size) {
60162306a36Sopenharmony_ci		dev_err(dev, "Bad firmware file\n");
60262306a36Sopenharmony_ci		status = -EINVAL;
60362306a36Sopenharmony_ci		goto abort_with_fw;
60462306a36Sopenharmony_ci	}
60562306a36Sopenharmony_ci	hdr = (void *)(fw->data + hdr_offset);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	status = myri10ge_validate_firmware(mgp, hdr);
60862306a36Sopenharmony_ci	if (status != 0)
60962306a36Sopenharmony_ci		goto abort_with_fw;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	crc = crc32(~0, fw->data, fw->size);
61262306a36Sopenharmony_ci	for (i = 0; i < fw->size; i += 256) {
61362306a36Sopenharmony_ci		myri10ge_pio_copy(mgp->sram + MYRI10GE_FW_OFFSET + i,
61462306a36Sopenharmony_ci				  fw->data + i,
61562306a36Sopenharmony_ci				  min(256U, (unsigned)(fw->size - i)));
61662306a36Sopenharmony_ci		mb();
61762306a36Sopenharmony_ci		readb(mgp->sram);
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci	fw_readback = vmalloc(fw->size);
62062306a36Sopenharmony_ci	if (!fw_readback) {
62162306a36Sopenharmony_ci		status = -ENOMEM;
62262306a36Sopenharmony_ci		goto abort_with_fw;
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci	/* corruption checking is good for parity recovery and buggy chipset */
62562306a36Sopenharmony_ci	memcpy_fromio(fw_readback, mgp->sram + MYRI10GE_FW_OFFSET, fw->size);
62662306a36Sopenharmony_ci	reread_crc = crc32(~0, fw_readback, fw->size);
62762306a36Sopenharmony_ci	vfree(fw_readback);
62862306a36Sopenharmony_ci	if (crc != reread_crc) {
62962306a36Sopenharmony_ci		dev_err(dev, "CRC failed(fw-len=%u), got 0x%x (expect 0x%x)\n",
63062306a36Sopenharmony_ci			(unsigned)fw->size, reread_crc, crc);
63162306a36Sopenharmony_ci		status = -EIO;
63262306a36Sopenharmony_ci		goto abort_with_fw;
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci	*size = (u32) fw->size;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ciabort_with_fw:
63762306a36Sopenharmony_ci	release_firmware(fw);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ciabort_with_nothing:
64062306a36Sopenharmony_ci	return status;
64162306a36Sopenharmony_ci}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_cistatic int myri10ge_adopt_running_firmware(struct myri10ge_priv *mgp)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	struct mcp_gen_header *hdr;
64662306a36Sopenharmony_ci	struct device *dev = &mgp->pdev->dev;
64762306a36Sopenharmony_ci	const size_t bytes = sizeof(struct mcp_gen_header);
64862306a36Sopenharmony_ci	size_t hdr_offset;
64962306a36Sopenharmony_ci	int status;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	/* find running firmware header */
65262306a36Sopenharmony_ci	hdr_offset = swab32(readl(mgp->sram + MCP_HEADER_PTR_OFFSET));
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > mgp->sram_size) {
65562306a36Sopenharmony_ci		dev_err(dev, "Running firmware has bad header offset (%d)\n",
65662306a36Sopenharmony_ci			(int)hdr_offset);
65762306a36Sopenharmony_ci		return -EIO;
65862306a36Sopenharmony_ci	}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	/* copy header of running firmware from SRAM to host memory to
66162306a36Sopenharmony_ci	 * validate firmware */
66262306a36Sopenharmony_ci	hdr = kmalloc(bytes, GFP_KERNEL);
66362306a36Sopenharmony_ci	if (hdr == NULL)
66462306a36Sopenharmony_ci		return -ENOMEM;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	memcpy_fromio(hdr, mgp->sram + hdr_offset, bytes);
66762306a36Sopenharmony_ci	status = myri10ge_validate_firmware(mgp, hdr);
66862306a36Sopenharmony_ci	kfree(hdr);
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	/* check to see if adopted firmware has bug where adopting
67162306a36Sopenharmony_ci	 * it will cause broadcasts to be filtered unless the NIC
67262306a36Sopenharmony_ci	 * is kept in ALLMULTI mode */
67362306a36Sopenharmony_ci	if (mgp->fw_ver_major == 1 && mgp->fw_ver_minor == 4 &&
67462306a36Sopenharmony_ci	    mgp->fw_ver_tiny >= 4 && mgp->fw_ver_tiny <= 11) {
67562306a36Sopenharmony_ci		mgp->adopted_rx_filter_bug = 1;
67662306a36Sopenharmony_ci		dev_warn(dev, "Adopting fw %d.%d.%d: "
67762306a36Sopenharmony_ci			 "working around rx filter bug\n",
67862306a36Sopenharmony_ci			 mgp->fw_ver_major, mgp->fw_ver_minor,
67962306a36Sopenharmony_ci			 mgp->fw_ver_tiny);
68062306a36Sopenharmony_ci	}
68162306a36Sopenharmony_ci	return status;
68262306a36Sopenharmony_ci}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_cistatic int myri10ge_get_firmware_capabilities(struct myri10ge_priv *mgp)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	struct myri10ge_cmd cmd;
68762306a36Sopenharmony_ci	int status;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	/* probe for IPv6 TSO support */
69062306a36Sopenharmony_ci	mgp->features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_TSO;
69162306a36Sopenharmony_ci	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_MAX_TSO6_HDR_SIZE,
69262306a36Sopenharmony_ci				   &cmd, 0);
69362306a36Sopenharmony_ci	if (status == 0) {
69462306a36Sopenharmony_ci		mgp->max_tso6 = cmd.data0;
69562306a36Sopenharmony_ci		mgp->features |= NETIF_F_TSO6;
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd, 0);
69962306a36Sopenharmony_ci	if (status != 0) {
70062306a36Sopenharmony_ci		dev_err(&mgp->pdev->dev,
70162306a36Sopenharmony_ci			"failed MXGEFW_CMD_GET_RX_RING_SIZE\n");
70262306a36Sopenharmony_ci		return -ENXIO;
70362306a36Sopenharmony_ci	}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	mgp->max_intr_slots = 2 * (cmd.data0 / sizeof(struct mcp_dma_addr));
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	return 0;
70862306a36Sopenharmony_ci}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_cistatic int myri10ge_load_firmware(struct myri10ge_priv *mgp, int adopt)
71162306a36Sopenharmony_ci{
71262306a36Sopenharmony_ci	char __iomem *submit;
71362306a36Sopenharmony_ci	__be32 buf[16] __attribute__ ((__aligned__(8)));
71462306a36Sopenharmony_ci	u32 dma_low, dma_high, size;
71562306a36Sopenharmony_ci	int status, i;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	size = 0;
71862306a36Sopenharmony_ci	status = myri10ge_load_hotplug_firmware(mgp, &size);
71962306a36Sopenharmony_ci	if (status) {
72062306a36Sopenharmony_ci		if (!adopt)
72162306a36Sopenharmony_ci			return status;
72262306a36Sopenharmony_ci		dev_warn(&mgp->pdev->dev, "hotplug firmware loading failed\n");
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci		/* Do not attempt to adopt firmware if there
72562306a36Sopenharmony_ci		 * was a bad crc */
72662306a36Sopenharmony_ci		if (status == -EIO)
72762306a36Sopenharmony_ci			return status;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci		status = myri10ge_adopt_running_firmware(mgp);
73062306a36Sopenharmony_ci		if (status != 0) {
73162306a36Sopenharmony_ci			dev_err(&mgp->pdev->dev,
73262306a36Sopenharmony_ci				"failed to adopt running firmware\n");
73362306a36Sopenharmony_ci			return status;
73462306a36Sopenharmony_ci		}
73562306a36Sopenharmony_ci		dev_info(&mgp->pdev->dev,
73662306a36Sopenharmony_ci			 "Successfully adopted running firmware\n");
73762306a36Sopenharmony_ci		if (mgp->tx_boundary == 4096) {
73862306a36Sopenharmony_ci			dev_warn(&mgp->pdev->dev,
73962306a36Sopenharmony_ci				 "Using firmware currently running on NIC"
74062306a36Sopenharmony_ci				 ".  For optimal\n");
74162306a36Sopenharmony_ci			dev_warn(&mgp->pdev->dev,
74262306a36Sopenharmony_ci				 "performance consider loading optimized "
74362306a36Sopenharmony_ci				 "firmware\n");
74462306a36Sopenharmony_ci			dev_warn(&mgp->pdev->dev, "via hotplug\n");
74562306a36Sopenharmony_ci		}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci		set_fw_name(mgp, "adopted", false);
74862306a36Sopenharmony_ci		mgp->tx_boundary = 2048;
74962306a36Sopenharmony_ci		myri10ge_dummy_rdma(mgp, 1);
75062306a36Sopenharmony_ci		status = myri10ge_get_firmware_capabilities(mgp);
75162306a36Sopenharmony_ci		return status;
75262306a36Sopenharmony_ci	}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	/* clear confirmation addr */
75562306a36Sopenharmony_ci	mgp->cmd->data = 0;
75662306a36Sopenharmony_ci	mb();
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	/* send a reload command to the bootstrap MCP, and wait for the
75962306a36Sopenharmony_ci	 *  response in the confirmation address.  The firmware should
76062306a36Sopenharmony_ci	 * write a -1 there to indicate it is alive and well
76162306a36Sopenharmony_ci	 */
76262306a36Sopenharmony_ci	dma_low = MYRI10GE_LOWPART_TO_U32(mgp->cmd_bus);
76362306a36Sopenharmony_ci	dma_high = MYRI10GE_HIGHPART_TO_U32(mgp->cmd_bus);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	buf[0] = htonl(dma_high);	/* confirm addr MSW */
76662306a36Sopenharmony_ci	buf[1] = htonl(dma_low);	/* confirm addr LSW */
76762306a36Sopenharmony_ci	buf[2] = MYRI10GE_NO_CONFIRM_DATA;	/* confirm data */
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	/* FIX: All newest firmware should un-protect the bottom of
77062306a36Sopenharmony_ci	 * the sram before handoff. However, the very first interfaces
77162306a36Sopenharmony_ci	 * do not. Therefore the handoff copy must skip the first 8 bytes
77262306a36Sopenharmony_ci	 */
77362306a36Sopenharmony_ci	buf[3] = htonl(MYRI10GE_FW_OFFSET + 8);	/* where the code starts */
77462306a36Sopenharmony_ci	buf[4] = htonl(size - 8);	/* length of code */
77562306a36Sopenharmony_ci	buf[5] = htonl(8);	/* where to copy to */
77662306a36Sopenharmony_ci	buf[6] = htonl(0);	/* where to jump to */
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	submit = mgp->sram + MXGEFW_BOOT_HANDOFF;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	myri10ge_pio_copy(submit, &buf, sizeof(buf));
78162306a36Sopenharmony_ci	mb();
78262306a36Sopenharmony_ci	msleep(1);
78362306a36Sopenharmony_ci	mb();
78462306a36Sopenharmony_ci	i = 0;
78562306a36Sopenharmony_ci	while (mgp->cmd->data != MYRI10GE_NO_CONFIRM_DATA && i < 9) {
78662306a36Sopenharmony_ci		msleep(1 << i);
78762306a36Sopenharmony_ci		i++;
78862306a36Sopenharmony_ci	}
78962306a36Sopenharmony_ci	if (mgp->cmd->data != MYRI10GE_NO_CONFIRM_DATA) {
79062306a36Sopenharmony_ci		dev_err(&mgp->pdev->dev, "handoff failed\n");
79162306a36Sopenharmony_ci		return -ENXIO;
79262306a36Sopenharmony_ci	}
79362306a36Sopenharmony_ci	myri10ge_dummy_rdma(mgp, 1);
79462306a36Sopenharmony_ci	status = myri10ge_get_firmware_capabilities(mgp);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	return status;
79762306a36Sopenharmony_ci}
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_cistatic int myri10ge_update_mac_address(struct myri10ge_priv *mgp,
80062306a36Sopenharmony_ci				       const u8 * addr)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	struct myri10ge_cmd cmd;
80362306a36Sopenharmony_ci	int status;
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
80662306a36Sopenharmony_ci		     | (addr[2] << 8) | addr[3]);
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	cmd.data1 = ((addr[4] << 8) | (addr[5]));
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	status = myri10ge_send_cmd(mgp, MXGEFW_SET_MAC_ADDRESS, &cmd, 0);
81162306a36Sopenharmony_ci	return status;
81262306a36Sopenharmony_ci}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_cistatic int myri10ge_change_pause(struct myri10ge_priv *mgp, int pause)
81562306a36Sopenharmony_ci{
81662306a36Sopenharmony_ci	struct myri10ge_cmd cmd;
81762306a36Sopenharmony_ci	int status, ctl;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	ctl = pause ? MXGEFW_ENABLE_FLOW_CONTROL : MXGEFW_DISABLE_FLOW_CONTROL;
82062306a36Sopenharmony_ci	status = myri10ge_send_cmd(mgp, ctl, &cmd, 0);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	if (status) {
82362306a36Sopenharmony_ci		netdev_err(mgp->dev, "Failed to set flow control mode\n");
82462306a36Sopenharmony_ci		return status;
82562306a36Sopenharmony_ci	}
82662306a36Sopenharmony_ci	mgp->pause = pause;
82762306a36Sopenharmony_ci	return 0;
82862306a36Sopenharmony_ci}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_cistatic void
83162306a36Sopenharmony_cimyri10ge_change_promisc(struct myri10ge_priv *mgp, int promisc, int atomic)
83262306a36Sopenharmony_ci{
83362306a36Sopenharmony_ci	struct myri10ge_cmd cmd;
83462306a36Sopenharmony_ci	int status, ctl;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	ctl = promisc ? MXGEFW_ENABLE_PROMISC : MXGEFW_DISABLE_PROMISC;
83762306a36Sopenharmony_ci	status = myri10ge_send_cmd(mgp, ctl, &cmd, atomic);
83862306a36Sopenharmony_ci	if (status)
83962306a36Sopenharmony_ci		netdev_err(mgp->dev, "Failed to set promisc mode\n");
84062306a36Sopenharmony_ci}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_cistatic int myri10ge_dma_test(struct myri10ge_priv *mgp, int test_type)
84362306a36Sopenharmony_ci{
84462306a36Sopenharmony_ci	struct myri10ge_cmd cmd;
84562306a36Sopenharmony_ci	int status;
84662306a36Sopenharmony_ci	u32 len;
84762306a36Sopenharmony_ci	struct page *dmatest_page;
84862306a36Sopenharmony_ci	dma_addr_t dmatest_bus;
84962306a36Sopenharmony_ci	char *test = " ";
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	dmatest_page = alloc_page(GFP_KERNEL);
85262306a36Sopenharmony_ci	if (!dmatest_page)
85362306a36Sopenharmony_ci		return -ENOMEM;
85462306a36Sopenharmony_ci	dmatest_bus = dma_map_page(&mgp->pdev->dev, dmatest_page, 0,
85562306a36Sopenharmony_ci				   PAGE_SIZE, DMA_BIDIRECTIONAL);
85662306a36Sopenharmony_ci	if (unlikely(dma_mapping_error(&mgp->pdev->dev, dmatest_bus))) {
85762306a36Sopenharmony_ci		__free_page(dmatest_page);
85862306a36Sopenharmony_ci		return -ENOMEM;
85962306a36Sopenharmony_ci	}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	/* Run a small DMA test.
86262306a36Sopenharmony_ci	 * The magic multipliers to the length tell the firmware
86362306a36Sopenharmony_ci	 * to do DMA read, write, or read+write tests.  The
86462306a36Sopenharmony_ci	 * results are returned in cmd.data0.  The upper 16
86562306a36Sopenharmony_ci	 * bits or the return is the number of transfers completed.
86662306a36Sopenharmony_ci	 * The lower 16 bits is the time in 0.5us ticks that the
86762306a36Sopenharmony_ci	 * transfers took to complete.
86862306a36Sopenharmony_ci	 */
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	len = mgp->tx_boundary;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	cmd.data0 = MYRI10GE_LOWPART_TO_U32(dmatest_bus);
87362306a36Sopenharmony_ci	cmd.data1 = MYRI10GE_HIGHPART_TO_U32(dmatest_bus);
87462306a36Sopenharmony_ci	cmd.data2 = len * 0x10000;
87562306a36Sopenharmony_ci	status = myri10ge_send_cmd(mgp, test_type, &cmd, 0);
87662306a36Sopenharmony_ci	if (status != 0) {
87762306a36Sopenharmony_ci		test = "read";
87862306a36Sopenharmony_ci		goto abort;
87962306a36Sopenharmony_ci	}
88062306a36Sopenharmony_ci	mgp->read_dma = ((cmd.data0 >> 16) * len * 2) / (cmd.data0 & 0xffff);
88162306a36Sopenharmony_ci	cmd.data0 = MYRI10GE_LOWPART_TO_U32(dmatest_bus);
88262306a36Sopenharmony_ci	cmd.data1 = MYRI10GE_HIGHPART_TO_U32(dmatest_bus);
88362306a36Sopenharmony_ci	cmd.data2 = len * 0x1;
88462306a36Sopenharmony_ci	status = myri10ge_send_cmd(mgp, test_type, &cmd, 0);
88562306a36Sopenharmony_ci	if (status != 0) {
88662306a36Sopenharmony_ci		test = "write";
88762306a36Sopenharmony_ci		goto abort;
88862306a36Sopenharmony_ci	}
88962306a36Sopenharmony_ci	mgp->write_dma = ((cmd.data0 >> 16) * len * 2) / (cmd.data0 & 0xffff);
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	cmd.data0 = MYRI10GE_LOWPART_TO_U32(dmatest_bus);
89262306a36Sopenharmony_ci	cmd.data1 = MYRI10GE_HIGHPART_TO_U32(dmatest_bus);
89362306a36Sopenharmony_ci	cmd.data2 = len * 0x10001;
89462306a36Sopenharmony_ci	status = myri10ge_send_cmd(mgp, test_type, &cmd, 0);
89562306a36Sopenharmony_ci	if (status != 0) {
89662306a36Sopenharmony_ci		test = "read/write";
89762306a36Sopenharmony_ci		goto abort;
89862306a36Sopenharmony_ci	}
89962306a36Sopenharmony_ci	mgp->read_write_dma = ((cmd.data0 >> 16) * len * 2 * 2) /
90062306a36Sopenharmony_ci	    (cmd.data0 & 0xffff);
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ciabort:
90362306a36Sopenharmony_ci	dma_unmap_page(&mgp->pdev->dev, dmatest_bus, PAGE_SIZE,
90462306a36Sopenharmony_ci		       DMA_BIDIRECTIONAL);
90562306a36Sopenharmony_ci	put_page(dmatest_page);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST)
90862306a36Sopenharmony_ci		dev_warn(&mgp->pdev->dev, "DMA %s benchmark failed: %d\n",
90962306a36Sopenharmony_ci			 test, status);
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	return status;
91262306a36Sopenharmony_ci}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_cistatic int myri10ge_reset(struct myri10ge_priv *mgp)
91562306a36Sopenharmony_ci{
91662306a36Sopenharmony_ci	struct myri10ge_cmd cmd;
91762306a36Sopenharmony_ci	struct myri10ge_slice_state *ss;
91862306a36Sopenharmony_ci	int i, status;
91962306a36Sopenharmony_ci	size_t bytes;
92062306a36Sopenharmony_ci#ifdef CONFIG_MYRI10GE_DCA
92162306a36Sopenharmony_ci	unsigned long dca_tag_off;
92262306a36Sopenharmony_ci#endif
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	/* try to send a reset command to the card to see if it
92562306a36Sopenharmony_ci	 * is alive */
92662306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
92762306a36Sopenharmony_ci	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_RESET, &cmd, 0);
92862306a36Sopenharmony_ci	if (status != 0) {
92962306a36Sopenharmony_ci		dev_err(&mgp->pdev->dev, "failed reset\n");
93062306a36Sopenharmony_ci		return -ENXIO;
93162306a36Sopenharmony_ci	}
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	(void)myri10ge_dma_test(mgp, MXGEFW_DMA_TEST);
93462306a36Sopenharmony_ci	/*
93562306a36Sopenharmony_ci	 * Use non-ndis mcp_slot (eg, 4 bytes total,
93662306a36Sopenharmony_ci	 * no toeplitz hash value returned.  Older firmware will
93762306a36Sopenharmony_ci	 * not understand this command, but will use the correct
93862306a36Sopenharmony_ci	 * sized mcp_slot, so we ignore error returns
93962306a36Sopenharmony_ci	 */
94062306a36Sopenharmony_ci	cmd.data0 = MXGEFW_RSS_MCP_SLOT_TYPE_MIN;
94162306a36Sopenharmony_ci	(void)myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_RSS_MCP_SLOT_TYPE, &cmd, 0);
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	/* Now exchange information about interrupts  */
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	bytes = mgp->max_intr_slots * sizeof(*mgp->ss[0].rx_done.entry);
94662306a36Sopenharmony_ci	cmd.data0 = (u32) bytes;
94762306a36Sopenharmony_ci	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd, 0);
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	/*
95062306a36Sopenharmony_ci	 * Even though we already know how many slices are supported
95162306a36Sopenharmony_ci	 * via myri10ge_probe_slices() MXGEFW_CMD_GET_MAX_RSS_QUEUES
95262306a36Sopenharmony_ci	 * has magic side effects, and must be called after a reset.
95362306a36Sopenharmony_ci	 * It must be called prior to calling any RSS related cmds,
95462306a36Sopenharmony_ci	 * including assigning an interrupt queue for anything but
95562306a36Sopenharmony_ci	 * slice 0.  It must also be called *after*
95662306a36Sopenharmony_ci	 * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by
95762306a36Sopenharmony_ci	 * the firmware to compute offsets.
95862306a36Sopenharmony_ci	 */
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	if (mgp->num_slices > 1) {
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci		/* ask the maximum number of slices it supports */
96362306a36Sopenharmony_ci		status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_MAX_RSS_QUEUES,
96462306a36Sopenharmony_ci					   &cmd, 0);
96562306a36Sopenharmony_ci		if (status != 0) {
96662306a36Sopenharmony_ci			dev_err(&mgp->pdev->dev,
96762306a36Sopenharmony_ci				"failed to get number of slices\n");
96862306a36Sopenharmony_ci		}
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci		/*
97162306a36Sopenharmony_ci		 * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior
97262306a36Sopenharmony_ci		 * to setting up the interrupt queue DMA
97362306a36Sopenharmony_ci		 */
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci		cmd.data0 = mgp->num_slices;
97662306a36Sopenharmony_ci		cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
97762306a36Sopenharmony_ci		if (mgp->dev->real_num_tx_queues > 1)
97862306a36Sopenharmony_ci			cmd.data1 |= MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES;
97962306a36Sopenharmony_ci		status = myri10ge_send_cmd(mgp, MXGEFW_CMD_ENABLE_RSS_QUEUES,
98062306a36Sopenharmony_ci					   &cmd, 0);
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci		/* Firmware older than 1.4.32 only supports multiple
98362306a36Sopenharmony_ci		 * RX queues, so if we get an error, first retry using a
98462306a36Sopenharmony_ci		 * single TX queue before giving up */
98562306a36Sopenharmony_ci		if (status != 0 && mgp->dev->real_num_tx_queues > 1) {
98662306a36Sopenharmony_ci			netif_set_real_num_tx_queues(mgp->dev, 1);
98762306a36Sopenharmony_ci			cmd.data0 = mgp->num_slices;
98862306a36Sopenharmony_ci			cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
98962306a36Sopenharmony_ci			status = myri10ge_send_cmd(mgp,
99062306a36Sopenharmony_ci						   MXGEFW_CMD_ENABLE_RSS_QUEUES,
99162306a36Sopenharmony_ci						   &cmd, 0);
99262306a36Sopenharmony_ci		}
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci		if (status != 0) {
99562306a36Sopenharmony_ci			dev_err(&mgp->pdev->dev,
99662306a36Sopenharmony_ci				"failed to set number of slices\n");
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci			return status;
99962306a36Sopenharmony_ci		}
100062306a36Sopenharmony_ci	}
100162306a36Sopenharmony_ci	for (i = 0; i < mgp->num_slices; i++) {
100262306a36Sopenharmony_ci		ss = &mgp->ss[i];
100362306a36Sopenharmony_ci		cmd.data0 = MYRI10GE_LOWPART_TO_U32(ss->rx_done.bus);
100462306a36Sopenharmony_ci		cmd.data1 = MYRI10GE_HIGHPART_TO_U32(ss->rx_done.bus);
100562306a36Sopenharmony_ci		cmd.data2 = i;
100662306a36Sopenharmony_ci		status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_INTRQ_DMA,
100762306a36Sopenharmony_ci					    &cmd, 0);
100862306a36Sopenharmony_ci	}
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	status |=
101162306a36Sopenharmony_ci	    myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd, 0);
101262306a36Sopenharmony_ci	for (i = 0; i < mgp->num_slices; i++) {
101362306a36Sopenharmony_ci		ss = &mgp->ss[i];
101462306a36Sopenharmony_ci		ss->irq_claim =
101562306a36Sopenharmony_ci		    (__iomem __be32 *) (mgp->sram + cmd.data0 + 8 * i);
101662306a36Sopenharmony_ci	}
101762306a36Sopenharmony_ci	status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
101862306a36Sopenharmony_ci				    &cmd, 0);
101962306a36Sopenharmony_ci	mgp->irq_deassert = (__iomem __be32 *) (mgp->sram + cmd.data0);
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	status |= myri10ge_send_cmd
102262306a36Sopenharmony_ci	    (mgp, MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd, 0);
102362306a36Sopenharmony_ci	mgp->intr_coal_delay_ptr = (__iomem __be32 *) (mgp->sram + cmd.data0);
102462306a36Sopenharmony_ci	if (status != 0) {
102562306a36Sopenharmony_ci		dev_err(&mgp->pdev->dev, "failed set interrupt parameters\n");
102662306a36Sopenharmony_ci		return status;
102762306a36Sopenharmony_ci	}
102862306a36Sopenharmony_ci	put_be32(htonl(mgp->intr_coal_delay), mgp->intr_coal_delay_ptr);
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci#ifdef CONFIG_MYRI10GE_DCA
103162306a36Sopenharmony_ci	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_DCA_OFFSET, &cmd, 0);
103262306a36Sopenharmony_ci	dca_tag_off = cmd.data0;
103362306a36Sopenharmony_ci	for (i = 0; i < mgp->num_slices; i++) {
103462306a36Sopenharmony_ci		ss = &mgp->ss[i];
103562306a36Sopenharmony_ci		if (status == 0) {
103662306a36Sopenharmony_ci			ss->dca_tag = (__iomem __be32 *)
103762306a36Sopenharmony_ci			    (mgp->sram + dca_tag_off + 4 * i);
103862306a36Sopenharmony_ci		} else {
103962306a36Sopenharmony_ci			ss->dca_tag = NULL;
104062306a36Sopenharmony_ci		}
104162306a36Sopenharmony_ci	}
104262306a36Sopenharmony_ci#endif				/* CONFIG_MYRI10GE_DCA */
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	/* reset mcp/driver shared state back to 0 */
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	mgp->link_changes = 0;
104762306a36Sopenharmony_ci	for (i = 0; i < mgp->num_slices; i++) {
104862306a36Sopenharmony_ci		ss = &mgp->ss[i];
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci		memset(ss->rx_done.entry, 0, bytes);
105162306a36Sopenharmony_ci		ss->tx.req = 0;
105262306a36Sopenharmony_ci		ss->tx.done = 0;
105362306a36Sopenharmony_ci		ss->tx.pkt_start = 0;
105462306a36Sopenharmony_ci		ss->tx.pkt_done = 0;
105562306a36Sopenharmony_ci		ss->rx_big.cnt = 0;
105662306a36Sopenharmony_ci		ss->rx_small.cnt = 0;
105762306a36Sopenharmony_ci		ss->rx_done.idx = 0;
105862306a36Sopenharmony_ci		ss->rx_done.cnt = 0;
105962306a36Sopenharmony_ci		ss->tx.wake_queue = 0;
106062306a36Sopenharmony_ci		ss->tx.stop_queue = 0;
106162306a36Sopenharmony_ci	}
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	status = myri10ge_update_mac_address(mgp, mgp->dev->dev_addr);
106462306a36Sopenharmony_ci	myri10ge_change_pause(mgp, mgp->pause);
106562306a36Sopenharmony_ci	myri10ge_set_multicast_list(mgp->dev);
106662306a36Sopenharmony_ci	return status;
106762306a36Sopenharmony_ci}
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci#ifdef CONFIG_MYRI10GE_DCA
107062306a36Sopenharmony_cistatic int myri10ge_toggle_relaxed(struct pci_dev *pdev, int on)
107162306a36Sopenharmony_ci{
107262306a36Sopenharmony_ci	int ret;
107362306a36Sopenharmony_ci	u16 ctl;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	pcie_capability_read_word(pdev, PCI_EXP_DEVCTL, &ctl);
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	ret = (ctl & PCI_EXP_DEVCTL_RELAX_EN) >> 4;
107862306a36Sopenharmony_ci	if (ret != on) {
107962306a36Sopenharmony_ci		ctl &= ~PCI_EXP_DEVCTL_RELAX_EN;
108062306a36Sopenharmony_ci		ctl |= (on << 4);
108162306a36Sopenharmony_ci		pcie_capability_write_word(pdev, PCI_EXP_DEVCTL, ctl);
108262306a36Sopenharmony_ci	}
108362306a36Sopenharmony_ci	return ret;
108462306a36Sopenharmony_ci}
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_cistatic void
108762306a36Sopenharmony_cimyri10ge_write_dca(struct myri10ge_slice_state *ss, int cpu, int tag)
108862306a36Sopenharmony_ci{
108962306a36Sopenharmony_ci	ss->cached_dca_tag = tag;
109062306a36Sopenharmony_ci	put_be32(htonl(tag), ss->dca_tag);
109162306a36Sopenharmony_ci}
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_cistatic inline void myri10ge_update_dca(struct myri10ge_slice_state *ss)
109462306a36Sopenharmony_ci{
109562306a36Sopenharmony_ci	int cpu = get_cpu();
109662306a36Sopenharmony_ci	int tag;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	if (cpu != ss->cpu) {
109962306a36Sopenharmony_ci		tag = dca3_get_tag(&ss->mgp->pdev->dev, cpu);
110062306a36Sopenharmony_ci		if (ss->cached_dca_tag != tag)
110162306a36Sopenharmony_ci			myri10ge_write_dca(ss, cpu, tag);
110262306a36Sopenharmony_ci		ss->cpu = cpu;
110362306a36Sopenharmony_ci	}
110462306a36Sopenharmony_ci	put_cpu();
110562306a36Sopenharmony_ci}
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_cistatic void myri10ge_setup_dca(struct myri10ge_priv *mgp)
110862306a36Sopenharmony_ci{
110962306a36Sopenharmony_ci	int err, i;
111062306a36Sopenharmony_ci	struct pci_dev *pdev = mgp->pdev;
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	if (mgp->ss[0].dca_tag == NULL || mgp->dca_enabled)
111362306a36Sopenharmony_ci		return;
111462306a36Sopenharmony_ci	if (!myri10ge_dca) {
111562306a36Sopenharmony_ci		dev_err(&pdev->dev, "dca disabled by administrator\n");
111662306a36Sopenharmony_ci		return;
111762306a36Sopenharmony_ci	}
111862306a36Sopenharmony_ci	err = dca_add_requester(&pdev->dev);
111962306a36Sopenharmony_ci	if (err) {
112062306a36Sopenharmony_ci		if (err != -ENODEV)
112162306a36Sopenharmony_ci			dev_err(&pdev->dev,
112262306a36Sopenharmony_ci				"dca_add_requester() failed, err=%d\n", err);
112362306a36Sopenharmony_ci		return;
112462306a36Sopenharmony_ci	}
112562306a36Sopenharmony_ci	mgp->relaxed_order = myri10ge_toggle_relaxed(pdev, 0);
112662306a36Sopenharmony_ci	mgp->dca_enabled = 1;
112762306a36Sopenharmony_ci	for (i = 0; i < mgp->num_slices; i++) {
112862306a36Sopenharmony_ci		mgp->ss[i].cpu = -1;
112962306a36Sopenharmony_ci		mgp->ss[i].cached_dca_tag = -1;
113062306a36Sopenharmony_ci		myri10ge_update_dca(&mgp->ss[i]);
113162306a36Sopenharmony_ci	}
113262306a36Sopenharmony_ci}
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_cistatic void myri10ge_teardown_dca(struct myri10ge_priv *mgp)
113562306a36Sopenharmony_ci{
113662306a36Sopenharmony_ci	struct pci_dev *pdev = mgp->pdev;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	if (!mgp->dca_enabled)
113962306a36Sopenharmony_ci		return;
114062306a36Sopenharmony_ci	mgp->dca_enabled = 0;
114162306a36Sopenharmony_ci	if (mgp->relaxed_order)
114262306a36Sopenharmony_ci		myri10ge_toggle_relaxed(pdev, 1);
114362306a36Sopenharmony_ci	dca_remove_requester(&pdev->dev);
114462306a36Sopenharmony_ci}
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_cistatic int myri10ge_notify_dca_device(struct device *dev, void *data)
114762306a36Sopenharmony_ci{
114862306a36Sopenharmony_ci	struct myri10ge_priv *mgp;
114962306a36Sopenharmony_ci	unsigned long event;
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	mgp = dev_get_drvdata(dev);
115262306a36Sopenharmony_ci	event = *(unsigned long *)data;
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	if (event == DCA_PROVIDER_ADD)
115562306a36Sopenharmony_ci		myri10ge_setup_dca(mgp);
115662306a36Sopenharmony_ci	else if (event == DCA_PROVIDER_REMOVE)
115762306a36Sopenharmony_ci		myri10ge_teardown_dca(mgp);
115862306a36Sopenharmony_ci	return 0;
115962306a36Sopenharmony_ci}
116062306a36Sopenharmony_ci#endif				/* CONFIG_MYRI10GE_DCA */
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_cistatic inline void
116362306a36Sopenharmony_cimyri10ge_submit_8rx(struct mcp_kreq_ether_recv __iomem * dst,
116462306a36Sopenharmony_ci		    struct mcp_kreq_ether_recv *src)
116562306a36Sopenharmony_ci{
116662306a36Sopenharmony_ci	__be32 low;
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	low = src->addr_low;
116962306a36Sopenharmony_ci	src->addr_low = htonl(DMA_BIT_MASK(32));
117062306a36Sopenharmony_ci	myri10ge_pio_copy(dst, src, 4 * sizeof(*src));
117162306a36Sopenharmony_ci	mb();
117262306a36Sopenharmony_ci	myri10ge_pio_copy(dst + 4, src + 4, 4 * sizeof(*src));
117362306a36Sopenharmony_ci	mb();
117462306a36Sopenharmony_ci	src->addr_low = low;
117562306a36Sopenharmony_ci	put_be32(low, &dst->addr_low);
117662306a36Sopenharmony_ci	mb();
117762306a36Sopenharmony_ci}
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_cistatic void
118062306a36Sopenharmony_cimyri10ge_alloc_rx_pages(struct myri10ge_priv *mgp, struct myri10ge_rx_buf *rx,
118162306a36Sopenharmony_ci			int bytes, int watchdog)
118262306a36Sopenharmony_ci{
118362306a36Sopenharmony_ci	struct page *page;
118462306a36Sopenharmony_ci	dma_addr_t bus;
118562306a36Sopenharmony_ci	int idx;
118662306a36Sopenharmony_ci#if MYRI10GE_ALLOC_SIZE > 4096
118762306a36Sopenharmony_ci	int end_offset;
118862306a36Sopenharmony_ci#endif
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	if (unlikely(rx->watchdog_needed && !watchdog))
119162306a36Sopenharmony_ci		return;
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	/* try to refill entire ring */
119462306a36Sopenharmony_ci	while (rx->fill_cnt != (rx->cnt + rx->mask + 1)) {
119562306a36Sopenharmony_ci		idx = rx->fill_cnt & rx->mask;
119662306a36Sopenharmony_ci		if (rx->page_offset + bytes <= MYRI10GE_ALLOC_SIZE) {
119762306a36Sopenharmony_ci			/* we can use part of previous page */
119862306a36Sopenharmony_ci			get_page(rx->page);
119962306a36Sopenharmony_ci		} else {
120062306a36Sopenharmony_ci			/* we need a new page */
120162306a36Sopenharmony_ci			page =
120262306a36Sopenharmony_ci			    alloc_pages(GFP_ATOMIC | __GFP_COMP,
120362306a36Sopenharmony_ci					MYRI10GE_ALLOC_ORDER);
120462306a36Sopenharmony_ci			if (unlikely(page == NULL)) {
120562306a36Sopenharmony_ci				if (rx->fill_cnt - rx->cnt < 16)
120662306a36Sopenharmony_ci					rx->watchdog_needed = 1;
120762306a36Sopenharmony_ci				return;
120862306a36Sopenharmony_ci			}
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci			bus = dma_map_page(&mgp->pdev->dev, page, 0,
121162306a36Sopenharmony_ci					   MYRI10GE_ALLOC_SIZE,
121262306a36Sopenharmony_ci					   DMA_FROM_DEVICE);
121362306a36Sopenharmony_ci			if (unlikely(dma_mapping_error(&mgp->pdev->dev, bus))) {
121462306a36Sopenharmony_ci				__free_pages(page, MYRI10GE_ALLOC_ORDER);
121562306a36Sopenharmony_ci				if (rx->fill_cnt - rx->cnt < 16)
121662306a36Sopenharmony_ci					rx->watchdog_needed = 1;
121762306a36Sopenharmony_ci				return;
121862306a36Sopenharmony_ci			}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci			rx->page = page;
122162306a36Sopenharmony_ci			rx->page_offset = 0;
122262306a36Sopenharmony_ci			rx->bus = bus;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci		}
122562306a36Sopenharmony_ci		rx->info[idx].page = rx->page;
122662306a36Sopenharmony_ci		rx->info[idx].page_offset = rx->page_offset;
122762306a36Sopenharmony_ci		/* note that this is the address of the start of the
122862306a36Sopenharmony_ci		 * page */
122962306a36Sopenharmony_ci		dma_unmap_addr_set(&rx->info[idx], bus, rx->bus);
123062306a36Sopenharmony_ci		rx->shadow[idx].addr_low =
123162306a36Sopenharmony_ci		    htonl(MYRI10GE_LOWPART_TO_U32(rx->bus) + rx->page_offset);
123262306a36Sopenharmony_ci		rx->shadow[idx].addr_high =
123362306a36Sopenharmony_ci		    htonl(MYRI10GE_HIGHPART_TO_U32(rx->bus));
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci		/* start next packet on a cacheline boundary */
123662306a36Sopenharmony_ci		rx->page_offset += SKB_DATA_ALIGN(bytes);
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci#if MYRI10GE_ALLOC_SIZE > 4096
123962306a36Sopenharmony_ci		/* don't cross a 4KB boundary */
124062306a36Sopenharmony_ci		end_offset = rx->page_offset + bytes - 1;
124162306a36Sopenharmony_ci		if ((unsigned)(rx->page_offset ^ end_offset) > 4095)
124262306a36Sopenharmony_ci			rx->page_offset = end_offset & ~4095;
124362306a36Sopenharmony_ci#endif
124462306a36Sopenharmony_ci		rx->fill_cnt++;
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci		/* copy 8 descriptors to the firmware at a time */
124762306a36Sopenharmony_ci		if ((idx & 7) == 7) {
124862306a36Sopenharmony_ci			myri10ge_submit_8rx(&rx->lanai[idx - 7],
124962306a36Sopenharmony_ci					    &rx->shadow[idx - 7]);
125062306a36Sopenharmony_ci		}
125162306a36Sopenharmony_ci	}
125262306a36Sopenharmony_ci}
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_cistatic inline void
125562306a36Sopenharmony_cimyri10ge_unmap_rx_page(struct pci_dev *pdev,
125662306a36Sopenharmony_ci		       struct myri10ge_rx_buffer_state *info, int bytes)
125762306a36Sopenharmony_ci{
125862306a36Sopenharmony_ci	/* unmap the recvd page if we're the only or last user of it */
125962306a36Sopenharmony_ci	if (bytes >= MYRI10GE_ALLOC_SIZE / 2 ||
126062306a36Sopenharmony_ci	    (info->page_offset + 2 * bytes) > MYRI10GE_ALLOC_SIZE) {
126162306a36Sopenharmony_ci		dma_unmap_page(&pdev->dev, (dma_unmap_addr(info, bus)
126262306a36Sopenharmony_ci					    & ~(MYRI10GE_ALLOC_SIZE - 1)),
126362306a36Sopenharmony_ci			       MYRI10GE_ALLOC_SIZE, DMA_FROM_DEVICE);
126462306a36Sopenharmony_ci	}
126562306a36Sopenharmony_ci}
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci/*
126862306a36Sopenharmony_ci * GRO does not support acceleration of tagged vlan frames, and
126962306a36Sopenharmony_ci * this NIC does not support vlan tag offload, so we must pop
127062306a36Sopenharmony_ci * the tag ourselves to be able to achieve GRO performance that
127162306a36Sopenharmony_ci * is comparable to LRO.
127262306a36Sopenharmony_ci */
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_cistatic inline void
127562306a36Sopenharmony_cimyri10ge_vlan_rx(struct net_device *dev, void *addr, struct sk_buff *skb)
127662306a36Sopenharmony_ci{
127762306a36Sopenharmony_ci	u8 *va;
127862306a36Sopenharmony_ci	struct vlan_ethhdr *veh;
127962306a36Sopenharmony_ci	skb_frag_t *frag;
128062306a36Sopenharmony_ci	__wsum vsum;
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	va = addr;
128362306a36Sopenharmony_ci	va += MXGEFW_PAD;
128462306a36Sopenharmony_ci	veh = (struct vlan_ethhdr *)va;
128562306a36Sopenharmony_ci	if ((dev->features & NETIF_F_HW_VLAN_CTAG_RX) ==
128662306a36Sopenharmony_ci	    NETIF_F_HW_VLAN_CTAG_RX &&
128762306a36Sopenharmony_ci	    veh->h_vlan_proto == htons(ETH_P_8021Q)) {
128862306a36Sopenharmony_ci		/* fixup csum if needed */
128962306a36Sopenharmony_ci		if (skb->ip_summed == CHECKSUM_COMPLETE) {
129062306a36Sopenharmony_ci			vsum = csum_partial(va + ETH_HLEN, VLAN_HLEN, 0);
129162306a36Sopenharmony_ci			skb->csum = csum_sub(skb->csum, vsum);
129262306a36Sopenharmony_ci		}
129362306a36Sopenharmony_ci		/* pop tag */
129462306a36Sopenharmony_ci		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ntohs(veh->h_vlan_TCI));
129562306a36Sopenharmony_ci		memmove(va + VLAN_HLEN, va, 2 * ETH_ALEN);
129662306a36Sopenharmony_ci		skb->len -= VLAN_HLEN;
129762306a36Sopenharmony_ci		skb->data_len -= VLAN_HLEN;
129862306a36Sopenharmony_ci		frag = skb_shinfo(skb)->frags;
129962306a36Sopenharmony_ci		skb_frag_off_add(frag, VLAN_HLEN);
130062306a36Sopenharmony_ci		skb_frag_size_sub(frag, VLAN_HLEN);
130162306a36Sopenharmony_ci	}
130262306a36Sopenharmony_ci}
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci#define MYRI10GE_HLEN 64 /* Bytes to copy from page to skb linear memory */
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_cistatic inline int
130762306a36Sopenharmony_cimyri10ge_rx_done(struct myri10ge_slice_state *ss, int len, __wsum csum)
130862306a36Sopenharmony_ci{
130962306a36Sopenharmony_ci	struct myri10ge_priv *mgp = ss->mgp;
131062306a36Sopenharmony_ci	struct sk_buff *skb;
131162306a36Sopenharmony_ci	skb_frag_t *rx_frags;
131262306a36Sopenharmony_ci	struct myri10ge_rx_buf *rx;
131362306a36Sopenharmony_ci	int i, idx, remainder, bytes;
131462306a36Sopenharmony_ci	struct pci_dev *pdev = mgp->pdev;
131562306a36Sopenharmony_ci	struct net_device *dev = mgp->dev;
131662306a36Sopenharmony_ci	u8 *va;
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	if (len <= mgp->small_bytes) {
131962306a36Sopenharmony_ci		rx = &ss->rx_small;
132062306a36Sopenharmony_ci		bytes = mgp->small_bytes;
132162306a36Sopenharmony_ci	} else {
132262306a36Sopenharmony_ci		rx = &ss->rx_big;
132362306a36Sopenharmony_ci		bytes = mgp->big_bytes;
132462306a36Sopenharmony_ci	}
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	len += MXGEFW_PAD;
132762306a36Sopenharmony_ci	idx = rx->cnt & rx->mask;
132862306a36Sopenharmony_ci	va = page_address(rx->info[idx].page) + rx->info[idx].page_offset;
132962306a36Sopenharmony_ci	prefetch(va);
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	skb = napi_get_frags(&ss->napi);
133262306a36Sopenharmony_ci	if (unlikely(skb == NULL)) {
133362306a36Sopenharmony_ci		ss->stats.rx_dropped++;
133462306a36Sopenharmony_ci		for (i = 0, remainder = len; remainder > 0; i++) {
133562306a36Sopenharmony_ci			myri10ge_unmap_rx_page(pdev, &rx->info[idx], bytes);
133662306a36Sopenharmony_ci			put_page(rx->info[idx].page);
133762306a36Sopenharmony_ci			rx->cnt++;
133862306a36Sopenharmony_ci			idx = rx->cnt & rx->mask;
133962306a36Sopenharmony_ci			remainder -= MYRI10GE_ALLOC_SIZE;
134062306a36Sopenharmony_ci		}
134162306a36Sopenharmony_ci		return 0;
134262306a36Sopenharmony_ci	}
134362306a36Sopenharmony_ci	rx_frags = skb_shinfo(skb)->frags;
134462306a36Sopenharmony_ci	/* Fill skb_frag_t(s) with data from our receive */
134562306a36Sopenharmony_ci	for (i = 0, remainder = len; remainder > 0; i++) {
134662306a36Sopenharmony_ci		myri10ge_unmap_rx_page(pdev, &rx->info[idx], bytes);
134762306a36Sopenharmony_ci		skb_fill_page_desc(skb, i, rx->info[idx].page,
134862306a36Sopenharmony_ci				   rx->info[idx].page_offset,
134962306a36Sopenharmony_ci				   remainder < MYRI10GE_ALLOC_SIZE ?
135062306a36Sopenharmony_ci				   remainder : MYRI10GE_ALLOC_SIZE);
135162306a36Sopenharmony_ci		rx->cnt++;
135262306a36Sopenharmony_ci		idx = rx->cnt & rx->mask;
135362306a36Sopenharmony_ci		remainder -= MYRI10GE_ALLOC_SIZE;
135462306a36Sopenharmony_ci	}
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	/* remove padding */
135762306a36Sopenharmony_ci	skb_frag_off_add(&rx_frags[0], MXGEFW_PAD);
135862306a36Sopenharmony_ci	skb_frag_size_sub(&rx_frags[0], MXGEFW_PAD);
135962306a36Sopenharmony_ci	len -= MXGEFW_PAD;
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	skb->len = len;
136262306a36Sopenharmony_ci	skb->data_len = len;
136362306a36Sopenharmony_ci	skb->truesize += len;
136462306a36Sopenharmony_ci	if (dev->features & NETIF_F_RXCSUM) {
136562306a36Sopenharmony_ci		skb->ip_summed = CHECKSUM_COMPLETE;
136662306a36Sopenharmony_ci		skb->csum = csum;
136762306a36Sopenharmony_ci	}
136862306a36Sopenharmony_ci	myri10ge_vlan_rx(mgp->dev, va, skb);
136962306a36Sopenharmony_ci	skb_record_rx_queue(skb, ss - &mgp->ss[0]);
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	napi_gro_frags(&ss->napi);
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	return 1;
137462306a36Sopenharmony_ci}
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_cistatic inline void
137762306a36Sopenharmony_cimyri10ge_tx_done(struct myri10ge_slice_state *ss, int mcp_index)
137862306a36Sopenharmony_ci{
137962306a36Sopenharmony_ci	struct pci_dev *pdev = ss->mgp->pdev;
138062306a36Sopenharmony_ci	struct myri10ge_tx_buf *tx = &ss->tx;
138162306a36Sopenharmony_ci	struct netdev_queue *dev_queue;
138262306a36Sopenharmony_ci	struct sk_buff *skb;
138362306a36Sopenharmony_ci	int idx, len;
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci	while (tx->pkt_done != mcp_index) {
138662306a36Sopenharmony_ci		idx = tx->done & tx->mask;
138762306a36Sopenharmony_ci		skb = tx->info[idx].skb;
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci		/* Mark as free */
139062306a36Sopenharmony_ci		tx->info[idx].skb = NULL;
139162306a36Sopenharmony_ci		if (tx->info[idx].last) {
139262306a36Sopenharmony_ci			tx->pkt_done++;
139362306a36Sopenharmony_ci			tx->info[idx].last = 0;
139462306a36Sopenharmony_ci		}
139562306a36Sopenharmony_ci		tx->done++;
139662306a36Sopenharmony_ci		len = dma_unmap_len(&tx->info[idx], len);
139762306a36Sopenharmony_ci		dma_unmap_len_set(&tx->info[idx], len, 0);
139862306a36Sopenharmony_ci		if (skb) {
139962306a36Sopenharmony_ci			ss->stats.tx_bytes += skb->len;
140062306a36Sopenharmony_ci			ss->stats.tx_packets++;
140162306a36Sopenharmony_ci			dev_consume_skb_irq(skb);
140262306a36Sopenharmony_ci			if (len)
140362306a36Sopenharmony_ci				dma_unmap_single(&pdev->dev,
140462306a36Sopenharmony_ci						 dma_unmap_addr(&tx->info[idx],
140562306a36Sopenharmony_ci								bus), len,
140662306a36Sopenharmony_ci						 DMA_TO_DEVICE);
140762306a36Sopenharmony_ci		} else {
140862306a36Sopenharmony_ci			if (len)
140962306a36Sopenharmony_ci				dma_unmap_page(&pdev->dev,
141062306a36Sopenharmony_ci					       dma_unmap_addr(&tx->info[idx],
141162306a36Sopenharmony_ci							      bus), len,
141262306a36Sopenharmony_ci					       DMA_TO_DEVICE);
141362306a36Sopenharmony_ci		}
141462306a36Sopenharmony_ci	}
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	dev_queue = netdev_get_tx_queue(ss->dev, ss - ss->mgp->ss);
141762306a36Sopenharmony_ci	/*
141862306a36Sopenharmony_ci	 * Make a minimal effort to prevent the NIC from polling an
141962306a36Sopenharmony_ci	 * idle tx queue.  If we can't get the lock we leave the queue
142062306a36Sopenharmony_ci	 * active. In this case, either a thread was about to start
142162306a36Sopenharmony_ci	 * using the queue anyway, or we lost a race and the NIC will
142262306a36Sopenharmony_ci	 * waste some of its resources polling an inactive queue for a
142362306a36Sopenharmony_ci	 * while.
142462306a36Sopenharmony_ci	 */
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci	if ((ss->mgp->dev->real_num_tx_queues > 1) &&
142762306a36Sopenharmony_ci	    __netif_tx_trylock(dev_queue)) {
142862306a36Sopenharmony_ci		if (tx->req == tx->done) {
142962306a36Sopenharmony_ci			tx->queue_active = 0;
143062306a36Sopenharmony_ci			put_be32(htonl(1), tx->send_stop);
143162306a36Sopenharmony_ci			mb();
143262306a36Sopenharmony_ci		}
143362306a36Sopenharmony_ci		__netif_tx_unlock(dev_queue);
143462306a36Sopenharmony_ci	}
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	/* start the queue if we've stopped it */
143762306a36Sopenharmony_ci	if (netif_tx_queue_stopped(dev_queue) &&
143862306a36Sopenharmony_ci	    tx->req - tx->done < (tx->mask >> 1) &&
143962306a36Sopenharmony_ci	    ss->mgp->running == MYRI10GE_ETH_RUNNING) {
144062306a36Sopenharmony_ci		tx->wake_queue++;
144162306a36Sopenharmony_ci		netif_tx_wake_queue(dev_queue);
144262306a36Sopenharmony_ci	}
144362306a36Sopenharmony_ci}
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_cistatic inline int
144662306a36Sopenharmony_cimyri10ge_clean_rx_done(struct myri10ge_slice_state *ss, int budget)
144762306a36Sopenharmony_ci{
144862306a36Sopenharmony_ci	struct myri10ge_rx_done *rx_done = &ss->rx_done;
144962306a36Sopenharmony_ci	struct myri10ge_priv *mgp = ss->mgp;
145062306a36Sopenharmony_ci	unsigned long rx_bytes = 0;
145162306a36Sopenharmony_ci	unsigned long rx_packets = 0;
145262306a36Sopenharmony_ci	unsigned long rx_ok;
145362306a36Sopenharmony_ci	int idx = rx_done->idx;
145462306a36Sopenharmony_ci	int cnt = rx_done->cnt;
145562306a36Sopenharmony_ci	int work_done = 0;
145662306a36Sopenharmony_ci	u16 length;
145762306a36Sopenharmony_ci	__wsum checksum;
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	while (rx_done->entry[idx].length != 0 && work_done < budget) {
146062306a36Sopenharmony_ci		length = ntohs(rx_done->entry[idx].length);
146162306a36Sopenharmony_ci		rx_done->entry[idx].length = 0;
146262306a36Sopenharmony_ci		checksum = csum_unfold(rx_done->entry[idx].checksum);
146362306a36Sopenharmony_ci		rx_ok = myri10ge_rx_done(ss, length, checksum);
146462306a36Sopenharmony_ci		rx_packets += rx_ok;
146562306a36Sopenharmony_ci		rx_bytes += rx_ok * (unsigned long)length;
146662306a36Sopenharmony_ci		cnt++;
146762306a36Sopenharmony_ci		idx = cnt & (mgp->max_intr_slots - 1);
146862306a36Sopenharmony_ci		work_done++;
146962306a36Sopenharmony_ci	}
147062306a36Sopenharmony_ci	rx_done->idx = idx;
147162306a36Sopenharmony_ci	rx_done->cnt = cnt;
147262306a36Sopenharmony_ci	ss->stats.rx_packets += rx_packets;
147362306a36Sopenharmony_ci	ss->stats.rx_bytes += rx_bytes;
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	/* restock receive rings if needed */
147662306a36Sopenharmony_ci	if (ss->rx_small.fill_cnt - ss->rx_small.cnt < myri10ge_fill_thresh)
147762306a36Sopenharmony_ci		myri10ge_alloc_rx_pages(mgp, &ss->rx_small,
147862306a36Sopenharmony_ci					mgp->small_bytes + MXGEFW_PAD, 0);
147962306a36Sopenharmony_ci	if (ss->rx_big.fill_cnt - ss->rx_big.cnt < myri10ge_fill_thresh)
148062306a36Sopenharmony_ci		myri10ge_alloc_rx_pages(mgp, &ss->rx_big, mgp->big_bytes, 0);
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci	return work_done;
148362306a36Sopenharmony_ci}
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_cistatic inline void myri10ge_check_statblock(struct myri10ge_priv *mgp)
148662306a36Sopenharmony_ci{
148762306a36Sopenharmony_ci	struct mcp_irq_data *stats = mgp->ss[0].fw_stats;
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	if (unlikely(stats->stats_updated)) {
149062306a36Sopenharmony_ci		unsigned link_up = ntohl(stats->link_up);
149162306a36Sopenharmony_ci		if (mgp->link_state != link_up) {
149262306a36Sopenharmony_ci			mgp->link_state = link_up;
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci			if (mgp->link_state == MXGEFW_LINK_UP) {
149562306a36Sopenharmony_ci				netif_info(mgp, link, mgp->dev, "link up\n");
149662306a36Sopenharmony_ci				netif_carrier_on(mgp->dev);
149762306a36Sopenharmony_ci				mgp->link_changes++;
149862306a36Sopenharmony_ci			} else {
149962306a36Sopenharmony_ci				netif_info(mgp, link, mgp->dev, "link %s\n",
150062306a36Sopenharmony_ci					   (link_up == MXGEFW_LINK_MYRINET ?
150162306a36Sopenharmony_ci					    "mismatch (Myrinet detected)" :
150262306a36Sopenharmony_ci					    "down"));
150362306a36Sopenharmony_ci				netif_carrier_off(mgp->dev);
150462306a36Sopenharmony_ci				mgp->link_changes++;
150562306a36Sopenharmony_ci			}
150662306a36Sopenharmony_ci		}
150762306a36Sopenharmony_ci		if (mgp->rdma_tags_available !=
150862306a36Sopenharmony_ci		    ntohl(stats->rdma_tags_available)) {
150962306a36Sopenharmony_ci			mgp->rdma_tags_available =
151062306a36Sopenharmony_ci			    ntohl(stats->rdma_tags_available);
151162306a36Sopenharmony_ci			netdev_warn(mgp->dev, "RDMA timed out! %d tags left\n",
151262306a36Sopenharmony_ci				    mgp->rdma_tags_available);
151362306a36Sopenharmony_ci		}
151462306a36Sopenharmony_ci		mgp->down_cnt += stats->link_down;
151562306a36Sopenharmony_ci		if (stats->link_down)
151662306a36Sopenharmony_ci			wake_up(&mgp->down_wq);
151762306a36Sopenharmony_ci	}
151862306a36Sopenharmony_ci}
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_cistatic int myri10ge_poll(struct napi_struct *napi, int budget)
152162306a36Sopenharmony_ci{
152262306a36Sopenharmony_ci	struct myri10ge_slice_state *ss =
152362306a36Sopenharmony_ci	    container_of(napi, struct myri10ge_slice_state, napi);
152462306a36Sopenharmony_ci	int work_done;
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci#ifdef CONFIG_MYRI10GE_DCA
152762306a36Sopenharmony_ci	if (ss->mgp->dca_enabled)
152862306a36Sopenharmony_ci		myri10ge_update_dca(ss);
152962306a36Sopenharmony_ci#endif
153062306a36Sopenharmony_ci	/* process as many rx events as NAPI will allow */
153162306a36Sopenharmony_ci	work_done = myri10ge_clean_rx_done(ss, budget);
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	if (work_done < budget) {
153462306a36Sopenharmony_ci		napi_complete_done(napi, work_done);
153562306a36Sopenharmony_ci		put_be32(htonl(3), ss->irq_claim);
153662306a36Sopenharmony_ci	}
153762306a36Sopenharmony_ci	return work_done;
153862306a36Sopenharmony_ci}
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_cistatic irqreturn_t myri10ge_intr(int irq, void *arg)
154162306a36Sopenharmony_ci{
154262306a36Sopenharmony_ci	struct myri10ge_slice_state *ss = arg;
154362306a36Sopenharmony_ci	struct myri10ge_priv *mgp = ss->mgp;
154462306a36Sopenharmony_ci	struct mcp_irq_data *stats = ss->fw_stats;
154562306a36Sopenharmony_ci	struct myri10ge_tx_buf *tx = &ss->tx;
154662306a36Sopenharmony_ci	u32 send_done_count;
154762306a36Sopenharmony_ci	int i;
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	/* an interrupt on a non-zero receive-only slice is implicitly
155062306a36Sopenharmony_ci	 * valid  since MSI-X irqs are not shared */
155162306a36Sopenharmony_ci	if ((mgp->dev->real_num_tx_queues == 1) && (ss != mgp->ss)) {
155262306a36Sopenharmony_ci		napi_schedule(&ss->napi);
155362306a36Sopenharmony_ci		return IRQ_HANDLED;
155462306a36Sopenharmony_ci	}
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	/* make sure it is our IRQ, and that the DMA has finished */
155762306a36Sopenharmony_ci	if (unlikely(!stats->valid))
155862306a36Sopenharmony_ci		return IRQ_NONE;
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	/* low bit indicates receives are present, so schedule
156162306a36Sopenharmony_ci	 * napi poll handler */
156262306a36Sopenharmony_ci	if (stats->valid & 1)
156362306a36Sopenharmony_ci		napi_schedule(&ss->napi);
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	if (!mgp->msi_enabled && !mgp->msix_enabled) {
156662306a36Sopenharmony_ci		put_be32(0, mgp->irq_deassert);
156762306a36Sopenharmony_ci		if (!myri10ge_deassert_wait)
156862306a36Sopenharmony_ci			stats->valid = 0;
156962306a36Sopenharmony_ci		mb();
157062306a36Sopenharmony_ci	} else
157162306a36Sopenharmony_ci		stats->valid = 0;
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	/* Wait for IRQ line to go low, if using INTx */
157462306a36Sopenharmony_ci	i = 0;
157562306a36Sopenharmony_ci	while (1) {
157662306a36Sopenharmony_ci		i++;
157762306a36Sopenharmony_ci		/* check for transmit completes and receives */
157862306a36Sopenharmony_ci		send_done_count = ntohl(stats->send_done_count);
157962306a36Sopenharmony_ci		if (send_done_count != tx->pkt_done)
158062306a36Sopenharmony_ci			myri10ge_tx_done(ss, (int)send_done_count);
158162306a36Sopenharmony_ci		if (unlikely(i > myri10ge_max_irq_loops)) {
158262306a36Sopenharmony_ci			netdev_warn(mgp->dev, "irq stuck?\n");
158362306a36Sopenharmony_ci			stats->valid = 0;
158462306a36Sopenharmony_ci			schedule_work(&mgp->watchdog_work);
158562306a36Sopenharmony_ci		}
158662306a36Sopenharmony_ci		if (likely(stats->valid == 0))
158762306a36Sopenharmony_ci			break;
158862306a36Sopenharmony_ci		cpu_relax();
158962306a36Sopenharmony_ci		barrier();
159062306a36Sopenharmony_ci	}
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	/* Only slice 0 updates stats */
159362306a36Sopenharmony_ci	if (ss == mgp->ss)
159462306a36Sopenharmony_ci		myri10ge_check_statblock(mgp);
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	put_be32(htonl(3), ss->irq_claim + 1);
159762306a36Sopenharmony_ci	return IRQ_HANDLED;
159862306a36Sopenharmony_ci}
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_cistatic int
160162306a36Sopenharmony_cimyri10ge_get_link_ksettings(struct net_device *netdev,
160262306a36Sopenharmony_ci			    struct ethtool_link_ksettings *cmd)
160362306a36Sopenharmony_ci{
160462306a36Sopenharmony_ci	struct myri10ge_priv *mgp = netdev_priv(netdev);
160562306a36Sopenharmony_ci	char *ptr;
160662306a36Sopenharmony_ci	int i;
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci	cmd->base.autoneg = AUTONEG_DISABLE;
160962306a36Sopenharmony_ci	cmd->base.speed = SPEED_10000;
161062306a36Sopenharmony_ci	cmd->base.duplex = DUPLEX_FULL;
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci	/*
161362306a36Sopenharmony_ci	 * parse the product code to deterimine the interface type
161462306a36Sopenharmony_ci	 * (CX4, XFP, Quad Ribbon Fiber) by looking at the character
161562306a36Sopenharmony_ci	 * after the 3rd dash in the driver's cached copy of the
161662306a36Sopenharmony_ci	 * EEPROM's product code string.
161762306a36Sopenharmony_ci	 */
161862306a36Sopenharmony_ci	ptr = mgp->product_code_string;
161962306a36Sopenharmony_ci	if (ptr == NULL) {
162062306a36Sopenharmony_ci		netdev_err(netdev, "Missing product code\n");
162162306a36Sopenharmony_ci		return 0;
162262306a36Sopenharmony_ci	}
162362306a36Sopenharmony_ci	for (i = 0; i < 3; i++, ptr++) {
162462306a36Sopenharmony_ci		ptr = strchr(ptr, '-');
162562306a36Sopenharmony_ci		if (ptr == NULL) {
162662306a36Sopenharmony_ci			netdev_err(netdev, "Invalid product code %s\n",
162762306a36Sopenharmony_ci				   mgp->product_code_string);
162862306a36Sopenharmony_ci			return 0;
162962306a36Sopenharmony_ci		}
163062306a36Sopenharmony_ci	}
163162306a36Sopenharmony_ci	if (*ptr == '2')
163262306a36Sopenharmony_ci		ptr++;
163362306a36Sopenharmony_ci	if (*ptr == 'R' || *ptr == 'Q' || *ptr == 'S') {
163462306a36Sopenharmony_ci		/* We've found either an XFP, quad ribbon fiber, or SFP+ */
163562306a36Sopenharmony_ci		cmd->base.port = PORT_FIBRE;
163662306a36Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE);
163762306a36Sopenharmony_ci		ethtool_link_ksettings_add_link_mode(cmd, advertising, FIBRE);
163862306a36Sopenharmony_ci	} else {
163962306a36Sopenharmony_ci		cmd->base.port = PORT_OTHER;
164062306a36Sopenharmony_ci	}
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	return 0;
164362306a36Sopenharmony_ci}
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_cistatic void
164662306a36Sopenharmony_cimyri10ge_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info)
164762306a36Sopenharmony_ci{
164862306a36Sopenharmony_ci	struct myri10ge_priv *mgp = netdev_priv(netdev);
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	strscpy(info->driver, "myri10ge", sizeof(info->driver));
165162306a36Sopenharmony_ci	strscpy(info->version, MYRI10GE_VERSION_STR, sizeof(info->version));
165262306a36Sopenharmony_ci	strscpy(info->fw_version, mgp->fw_version, sizeof(info->fw_version));
165362306a36Sopenharmony_ci	strscpy(info->bus_info, pci_name(mgp->pdev), sizeof(info->bus_info));
165462306a36Sopenharmony_ci}
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_cistatic int myri10ge_get_coalesce(struct net_device *netdev,
165762306a36Sopenharmony_ci				 struct ethtool_coalesce *coal,
165862306a36Sopenharmony_ci				 struct kernel_ethtool_coalesce *kernel_coal,
165962306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
166062306a36Sopenharmony_ci{
166162306a36Sopenharmony_ci	struct myri10ge_priv *mgp = netdev_priv(netdev);
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	coal->rx_coalesce_usecs = mgp->intr_coal_delay;
166462306a36Sopenharmony_ci	return 0;
166562306a36Sopenharmony_ci}
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_cistatic int myri10ge_set_coalesce(struct net_device *netdev,
166862306a36Sopenharmony_ci				 struct ethtool_coalesce *coal,
166962306a36Sopenharmony_ci				 struct kernel_ethtool_coalesce *kernel_coal,
167062306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
167162306a36Sopenharmony_ci{
167262306a36Sopenharmony_ci	struct myri10ge_priv *mgp = netdev_priv(netdev);
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci	mgp->intr_coal_delay = coal->rx_coalesce_usecs;
167562306a36Sopenharmony_ci	put_be32(htonl(mgp->intr_coal_delay), mgp->intr_coal_delay_ptr);
167662306a36Sopenharmony_ci	return 0;
167762306a36Sopenharmony_ci}
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_cistatic void
168062306a36Sopenharmony_cimyri10ge_get_pauseparam(struct net_device *netdev,
168162306a36Sopenharmony_ci			struct ethtool_pauseparam *pause)
168262306a36Sopenharmony_ci{
168362306a36Sopenharmony_ci	struct myri10ge_priv *mgp = netdev_priv(netdev);
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	pause->autoneg = 0;
168662306a36Sopenharmony_ci	pause->rx_pause = mgp->pause;
168762306a36Sopenharmony_ci	pause->tx_pause = mgp->pause;
168862306a36Sopenharmony_ci}
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_cistatic int
169162306a36Sopenharmony_cimyri10ge_set_pauseparam(struct net_device *netdev,
169262306a36Sopenharmony_ci			struct ethtool_pauseparam *pause)
169362306a36Sopenharmony_ci{
169462306a36Sopenharmony_ci	struct myri10ge_priv *mgp = netdev_priv(netdev);
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_ci	if (pause->tx_pause != mgp->pause)
169762306a36Sopenharmony_ci		return myri10ge_change_pause(mgp, pause->tx_pause);
169862306a36Sopenharmony_ci	if (pause->rx_pause != mgp->pause)
169962306a36Sopenharmony_ci		return myri10ge_change_pause(mgp, pause->rx_pause);
170062306a36Sopenharmony_ci	if (pause->autoneg != 0)
170162306a36Sopenharmony_ci		return -EINVAL;
170262306a36Sopenharmony_ci	return 0;
170362306a36Sopenharmony_ci}
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_cistatic void
170662306a36Sopenharmony_cimyri10ge_get_ringparam(struct net_device *netdev,
170762306a36Sopenharmony_ci		       struct ethtool_ringparam *ring,
170862306a36Sopenharmony_ci		       struct kernel_ethtool_ringparam *kernel_ring,
170962306a36Sopenharmony_ci		       struct netlink_ext_ack *extack)
171062306a36Sopenharmony_ci{
171162306a36Sopenharmony_ci	struct myri10ge_priv *mgp = netdev_priv(netdev);
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	ring->rx_mini_max_pending = mgp->ss[0].rx_small.mask + 1;
171462306a36Sopenharmony_ci	ring->rx_max_pending = mgp->ss[0].rx_big.mask + 1;
171562306a36Sopenharmony_ci	ring->rx_jumbo_max_pending = 0;
171662306a36Sopenharmony_ci	ring->tx_max_pending = mgp->ss[0].tx.mask + 1;
171762306a36Sopenharmony_ci	ring->rx_mini_pending = ring->rx_mini_max_pending;
171862306a36Sopenharmony_ci	ring->rx_pending = ring->rx_max_pending;
171962306a36Sopenharmony_ci	ring->rx_jumbo_pending = ring->rx_jumbo_max_pending;
172062306a36Sopenharmony_ci	ring->tx_pending = ring->tx_max_pending;
172162306a36Sopenharmony_ci}
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_cistatic const char myri10ge_gstrings_main_stats[][ETH_GSTRING_LEN] = {
172462306a36Sopenharmony_ci	"rx_packets", "tx_packets", "rx_bytes", "tx_bytes", "rx_errors",
172562306a36Sopenharmony_ci	"tx_errors", "rx_dropped", "tx_dropped", "multicast", "collisions",
172662306a36Sopenharmony_ci	"rx_length_errors", "rx_over_errors", "rx_crc_errors",
172762306a36Sopenharmony_ci	"rx_frame_errors", "rx_fifo_errors", "rx_missed_errors",
172862306a36Sopenharmony_ci	"tx_aborted_errors", "tx_carrier_errors", "tx_fifo_errors",
172962306a36Sopenharmony_ci	"tx_heartbeat_errors", "tx_window_errors",
173062306a36Sopenharmony_ci	/* device-specific stats */
173162306a36Sopenharmony_ci	"tx_boundary", "irq", "MSI", "MSIX",
173262306a36Sopenharmony_ci	"read_dma_bw_MBs", "write_dma_bw_MBs", "read_write_dma_bw_MBs",
173362306a36Sopenharmony_ci	"serial_number", "watchdog_resets",
173462306a36Sopenharmony_ci#ifdef CONFIG_MYRI10GE_DCA
173562306a36Sopenharmony_ci	"dca_capable_firmware", "dca_device_present",
173662306a36Sopenharmony_ci#endif
173762306a36Sopenharmony_ci	"link_changes", "link_up", "dropped_link_overflow",
173862306a36Sopenharmony_ci	"dropped_link_error_or_filtered",
173962306a36Sopenharmony_ci	"dropped_pause", "dropped_bad_phy", "dropped_bad_crc32",
174062306a36Sopenharmony_ci	"dropped_unicast_filtered", "dropped_multicast_filtered",
174162306a36Sopenharmony_ci	"dropped_runt", "dropped_overrun", "dropped_no_small_buffer",
174262306a36Sopenharmony_ci	"dropped_no_big_buffer"
174362306a36Sopenharmony_ci};
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_cistatic const char myri10ge_gstrings_slice_stats[][ETH_GSTRING_LEN] = {
174662306a36Sopenharmony_ci	"----------- slice ---------",
174762306a36Sopenharmony_ci	"tx_pkt_start", "tx_pkt_done", "tx_req", "tx_done",
174862306a36Sopenharmony_ci	"rx_small_cnt", "rx_big_cnt",
174962306a36Sopenharmony_ci	"wake_queue", "stop_queue", "tx_linearized",
175062306a36Sopenharmony_ci};
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci#define MYRI10GE_NET_STATS_LEN      21
175362306a36Sopenharmony_ci#define MYRI10GE_MAIN_STATS_LEN  ARRAY_SIZE(myri10ge_gstrings_main_stats)
175462306a36Sopenharmony_ci#define MYRI10GE_SLICE_STATS_LEN  ARRAY_SIZE(myri10ge_gstrings_slice_stats)
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_cistatic void
175762306a36Sopenharmony_cimyri10ge_get_strings(struct net_device *netdev, u32 stringset, u8 * data)
175862306a36Sopenharmony_ci{
175962306a36Sopenharmony_ci	struct myri10ge_priv *mgp = netdev_priv(netdev);
176062306a36Sopenharmony_ci	int i;
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci	switch (stringset) {
176362306a36Sopenharmony_ci	case ETH_SS_STATS:
176462306a36Sopenharmony_ci		memcpy(data, *myri10ge_gstrings_main_stats,
176562306a36Sopenharmony_ci		       sizeof(myri10ge_gstrings_main_stats));
176662306a36Sopenharmony_ci		data += sizeof(myri10ge_gstrings_main_stats);
176762306a36Sopenharmony_ci		for (i = 0; i < mgp->num_slices; i++) {
176862306a36Sopenharmony_ci			memcpy(data, *myri10ge_gstrings_slice_stats,
176962306a36Sopenharmony_ci			       sizeof(myri10ge_gstrings_slice_stats));
177062306a36Sopenharmony_ci			data += sizeof(myri10ge_gstrings_slice_stats);
177162306a36Sopenharmony_ci		}
177262306a36Sopenharmony_ci		break;
177362306a36Sopenharmony_ci	}
177462306a36Sopenharmony_ci}
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_cistatic int myri10ge_get_sset_count(struct net_device *netdev, int sset)
177762306a36Sopenharmony_ci{
177862306a36Sopenharmony_ci	struct myri10ge_priv *mgp = netdev_priv(netdev);
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci	switch (sset) {
178162306a36Sopenharmony_ci	case ETH_SS_STATS:
178262306a36Sopenharmony_ci		return MYRI10GE_MAIN_STATS_LEN +
178362306a36Sopenharmony_ci		    mgp->num_slices * MYRI10GE_SLICE_STATS_LEN;
178462306a36Sopenharmony_ci	default:
178562306a36Sopenharmony_ci		return -EOPNOTSUPP;
178662306a36Sopenharmony_ci	}
178762306a36Sopenharmony_ci}
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_cistatic void
179062306a36Sopenharmony_cimyri10ge_get_ethtool_stats(struct net_device *netdev,
179162306a36Sopenharmony_ci			   struct ethtool_stats *stats, u64 * data)
179262306a36Sopenharmony_ci{
179362306a36Sopenharmony_ci	struct myri10ge_priv *mgp = netdev_priv(netdev);
179462306a36Sopenharmony_ci	struct myri10ge_slice_state *ss;
179562306a36Sopenharmony_ci	struct rtnl_link_stats64 link_stats;
179662306a36Sopenharmony_ci	int slice;
179762306a36Sopenharmony_ci	int i;
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_ci	/* force stats update */
180062306a36Sopenharmony_ci	memset(&link_stats, 0, sizeof(link_stats));
180162306a36Sopenharmony_ci	(void)myri10ge_get_stats(netdev, &link_stats);
180262306a36Sopenharmony_ci	for (i = 0; i < MYRI10GE_NET_STATS_LEN; i++)
180362306a36Sopenharmony_ci		data[i] = ((u64 *)&link_stats)[i];
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci	data[i++] = (unsigned int)mgp->tx_boundary;
180662306a36Sopenharmony_ci	data[i++] = (unsigned int)mgp->pdev->irq;
180762306a36Sopenharmony_ci	data[i++] = (unsigned int)mgp->msi_enabled;
180862306a36Sopenharmony_ci	data[i++] = (unsigned int)mgp->msix_enabled;
180962306a36Sopenharmony_ci	data[i++] = (unsigned int)mgp->read_dma;
181062306a36Sopenharmony_ci	data[i++] = (unsigned int)mgp->write_dma;
181162306a36Sopenharmony_ci	data[i++] = (unsigned int)mgp->read_write_dma;
181262306a36Sopenharmony_ci	data[i++] = (unsigned int)mgp->serial_number;
181362306a36Sopenharmony_ci	data[i++] = (unsigned int)mgp->watchdog_resets;
181462306a36Sopenharmony_ci#ifdef CONFIG_MYRI10GE_DCA
181562306a36Sopenharmony_ci	data[i++] = (unsigned int)(mgp->ss[0].dca_tag != NULL);
181662306a36Sopenharmony_ci	data[i++] = (unsigned int)(mgp->dca_enabled);
181762306a36Sopenharmony_ci#endif
181862306a36Sopenharmony_ci	data[i++] = (unsigned int)mgp->link_changes;
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci	/* firmware stats are useful only in the first slice */
182162306a36Sopenharmony_ci	ss = &mgp->ss[0];
182262306a36Sopenharmony_ci	data[i++] = (unsigned int)ntohl(ss->fw_stats->link_up);
182362306a36Sopenharmony_ci	data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_link_overflow);
182462306a36Sopenharmony_ci	data[i++] =
182562306a36Sopenharmony_ci	    (unsigned int)ntohl(ss->fw_stats->dropped_link_error_or_filtered);
182662306a36Sopenharmony_ci	data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_pause);
182762306a36Sopenharmony_ci	data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_bad_phy);
182862306a36Sopenharmony_ci	data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_bad_crc32);
182962306a36Sopenharmony_ci	data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_unicast_filtered);
183062306a36Sopenharmony_ci	data[i++] =
183162306a36Sopenharmony_ci	    (unsigned int)ntohl(ss->fw_stats->dropped_multicast_filtered);
183262306a36Sopenharmony_ci	data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_runt);
183362306a36Sopenharmony_ci	data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_overrun);
183462306a36Sopenharmony_ci	data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_no_small_buffer);
183562306a36Sopenharmony_ci	data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_no_big_buffer);
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	for (slice = 0; slice < mgp->num_slices; slice++) {
183862306a36Sopenharmony_ci		ss = &mgp->ss[slice];
183962306a36Sopenharmony_ci		data[i++] = slice;
184062306a36Sopenharmony_ci		data[i++] = (unsigned int)ss->tx.pkt_start;
184162306a36Sopenharmony_ci		data[i++] = (unsigned int)ss->tx.pkt_done;
184262306a36Sopenharmony_ci		data[i++] = (unsigned int)ss->tx.req;
184362306a36Sopenharmony_ci		data[i++] = (unsigned int)ss->tx.done;
184462306a36Sopenharmony_ci		data[i++] = (unsigned int)ss->rx_small.cnt;
184562306a36Sopenharmony_ci		data[i++] = (unsigned int)ss->rx_big.cnt;
184662306a36Sopenharmony_ci		data[i++] = (unsigned int)ss->tx.wake_queue;
184762306a36Sopenharmony_ci		data[i++] = (unsigned int)ss->tx.stop_queue;
184862306a36Sopenharmony_ci		data[i++] = (unsigned int)ss->tx.linearized;
184962306a36Sopenharmony_ci	}
185062306a36Sopenharmony_ci}
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_cistatic void myri10ge_set_msglevel(struct net_device *netdev, u32 value)
185362306a36Sopenharmony_ci{
185462306a36Sopenharmony_ci	struct myri10ge_priv *mgp = netdev_priv(netdev);
185562306a36Sopenharmony_ci	mgp->msg_enable = value;
185662306a36Sopenharmony_ci}
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_cistatic u32 myri10ge_get_msglevel(struct net_device *netdev)
185962306a36Sopenharmony_ci{
186062306a36Sopenharmony_ci	struct myri10ge_priv *mgp = netdev_priv(netdev);
186162306a36Sopenharmony_ci	return mgp->msg_enable;
186262306a36Sopenharmony_ci}
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci/*
186562306a36Sopenharmony_ci * Use a low-level command to change the LED behavior. Rather than
186662306a36Sopenharmony_ci * blinking (which is the normal case), when identify is used, the
186762306a36Sopenharmony_ci * yellow LED turns solid.
186862306a36Sopenharmony_ci */
186962306a36Sopenharmony_cistatic int myri10ge_led(struct myri10ge_priv *mgp, int on)
187062306a36Sopenharmony_ci{
187162306a36Sopenharmony_ci	struct mcp_gen_header *hdr;
187262306a36Sopenharmony_ci	struct device *dev = &mgp->pdev->dev;
187362306a36Sopenharmony_ci	size_t hdr_off, pattern_off, hdr_len;
187462306a36Sopenharmony_ci	u32 pattern = 0xfffffffe;
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci	/* find running firmware header */
187762306a36Sopenharmony_ci	hdr_off = swab32(readl(mgp->sram + MCP_HEADER_PTR_OFFSET));
187862306a36Sopenharmony_ci	if ((hdr_off & 3) || hdr_off + sizeof(*hdr) > mgp->sram_size) {
187962306a36Sopenharmony_ci		dev_err(dev, "Running firmware has bad header offset (%d)\n",
188062306a36Sopenharmony_ci			(int)hdr_off);
188162306a36Sopenharmony_ci		return -EIO;
188262306a36Sopenharmony_ci	}
188362306a36Sopenharmony_ci	hdr_len = swab32(readl(mgp->sram + hdr_off +
188462306a36Sopenharmony_ci			       offsetof(struct mcp_gen_header, header_length)));
188562306a36Sopenharmony_ci	pattern_off = hdr_off + offsetof(struct mcp_gen_header, led_pattern);
188662306a36Sopenharmony_ci	if (pattern_off >= (hdr_len + hdr_off)) {
188762306a36Sopenharmony_ci		dev_info(dev, "Firmware does not support LED identification\n");
188862306a36Sopenharmony_ci		return -EINVAL;
188962306a36Sopenharmony_ci	}
189062306a36Sopenharmony_ci	if (!on)
189162306a36Sopenharmony_ci		pattern = swab32(readl(mgp->sram + pattern_off + 4));
189262306a36Sopenharmony_ci	writel(swab32(pattern), mgp->sram + pattern_off);
189362306a36Sopenharmony_ci	return 0;
189462306a36Sopenharmony_ci}
189562306a36Sopenharmony_ci
189662306a36Sopenharmony_cistatic int
189762306a36Sopenharmony_cimyri10ge_phys_id(struct net_device *netdev, enum ethtool_phys_id_state state)
189862306a36Sopenharmony_ci{
189962306a36Sopenharmony_ci	struct myri10ge_priv *mgp = netdev_priv(netdev);
190062306a36Sopenharmony_ci	int rc;
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ci	switch (state) {
190362306a36Sopenharmony_ci	case ETHTOOL_ID_ACTIVE:
190462306a36Sopenharmony_ci		rc = myri10ge_led(mgp, 1);
190562306a36Sopenharmony_ci		break;
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_ci	case ETHTOOL_ID_INACTIVE:
190862306a36Sopenharmony_ci		rc =  myri10ge_led(mgp, 0);
190962306a36Sopenharmony_ci		break;
191062306a36Sopenharmony_ci
191162306a36Sopenharmony_ci	default:
191262306a36Sopenharmony_ci		rc = -EINVAL;
191362306a36Sopenharmony_ci	}
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_ci	return rc;
191662306a36Sopenharmony_ci}
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_cistatic const struct ethtool_ops myri10ge_ethtool_ops = {
191962306a36Sopenharmony_ci	.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS,
192062306a36Sopenharmony_ci	.get_drvinfo = myri10ge_get_drvinfo,
192162306a36Sopenharmony_ci	.get_coalesce = myri10ge_get_coalesce,
192262306a36Sopenharmony_ci	.set_coalesce = myri10ge_set_coalesce,
192362306a36Sopenharmony_ci	.get_pauseparam = myri10ge_get_pauseparam,
192462306a36Sopenharmony_ci	.set_pauseparam = myri10ge_set_pauseparam,
192562306a36Sopenharmony_ci	.get_ringparam = myri10ge_get_ringparam,
192662306a36Sopenharmony_ci	.get_link = ethtool_op_get_link,
192762306a36Sopenharmony_ci	.get_strings = myri10ge_get_strings,
192862306a36Sopenharmony_ci	.get_sset_count = myri10ge_get_sset_count,
192962306a36Sopenharmony_ci	.get_ethtool_stats = myri10ge_get_ethtool_stats,
193062306a36Sopenharmony_ci	.set_msglevel = myri10ge_set_msglevel,
193162306a36Sopenharmony_ci	.get_msglevel = myri10ge_get_msglevel,
193262306a36Sopenharmony_ci	.set_phys_id = myri10ge_phys_id,
193362306a36Sopenharmony_ci	.get_link_ksettings = myri10ge_get_link_ksettings,
193462306a36Sopenharmony_ci};
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_cistatic int myri10ge_allocate_rings(struct myri10ge_slice_state *ss)
193762306a36Sopenharmony_ci{
193862306a36Sopenharmony_ci	struct myri10ge_priv *mgp = ss->mgp;
193962306a36Sopenharmony_ci	struct myri10ge_cmd cmd;
194062306a36Sopenharmony_ci	struct net_device *dev = mgp->dev;
194162306a36Sopenharmony_ci	int tx_ring_size, rx_ring_size;
194262306a36Sopenharmony_ci	int tx_ring_entries, rx_ring_entries;
194362306a36Sopenharmony_ci	int i, slice, status;
194462306a36Sopenharmony_ci	size_t bytes;
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_ci	/* get ring sizes */
194762306a36Sopenharmony_ci	slice = ss - mgp->ss;
194862306a36Sopenharmony_ci	cmd.data0 = slice;
194962306a36Sopenharmony_ci	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd, 0);
195062306a36Sopenharmony_ci	tx_ring_size = cmd.data0;
195162306a36Sopenharmony_ci	cmd.data0 = slice;
195262306a36Sopenharmony_ci	status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd, 0);
195362306a36Sopenharmony_ci	if (status != 0)
195462306a36Sopenharmony_ci		return status;
195562306a36Sopenharmony_ci	rx_ring_size = cmd.data0;
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_ci	tx_ring_entries = tx_ring_size / sizeof(struct mcp_kreq_ether_send);
195862306a36Sopenharmony_ci	rx_ring_entries = rx_ring_size / sizeof(struct mcp_dma_addr);
195962306a36Sopenharmony_ci	ss->tx.mask = tx_ring_entries - 1;
196062306a36Sopenharmony_ci	ss->rx_small.mask = ss->rx_big.mask = rx_ring_entries - 1;
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci	status = -ENOMEM;
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	/* allocate the host shadow rings */
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_ci	bytes = 8 + (MYRI10GE_MAX_SEND_DESC_TSO + 4)
196762306a36Sopenharmony_ci	    * sizeof(*ss->tx.req_list);
196862306a36Sopenharmony_ci	ss->tx.req_bytes = kzalloc(bytes, GFP_KERNEL);
196962306a36Sopenharmony_ci	if (ss->tx.req_bytes == NULL)
197062306a36Sopenharmony_ci		goto abort_with_nothing;
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_ci	/* ensure req_list entries are aligned to 8 bytes */
197362306a36Sopenharmony_ci	ss->tx.req_list = (struct mcp_kreq_ether_send *)
197462306a36Sopenharmony_ci	    ALIGN((unsigned long)ss->tx.req_bytes, 8);
197562306a36Sopenharmony_ci	ss->tx.queue_active = 0;
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_ci	bytes = rx_ring_entries * sizeof(*ss->rx_small.shadow);
197862306a36Sopenharmony_ci	ss->rx_small.shadow = kzalloc(bytes, GFP_KERNEL);
197962306a36Sopenharmony_ci	if (ss->rx_small.shadow == NULL)
198062306a36Sopenharmony_ci		goto abort_with_tx_req_bytes;
198162306a36Sopenharmony_ci
198262306a36Sopenharmony_ci	bytes = rx_ring_entries * sizeof(*ss->rx_big.shadow);
198362306a36Sopenharmony_ci	ss->rx_big.shadow = kzalloc(bytes, GFP_KERNEL);
198462306a36Sopenharmony_ci	if (ss->rx_big.shadow == NULL)
198562306a36Sopenharmony_ci		goto abort_with_rx_small_shadow;
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci	/* allocate the host info rings */
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_ci	bytes = tx_ring_entries * sizeof(*ss->tx.info);
199062306a36Sopenharmony_ci	ss->tx.info = kzalloc(bytes, GFP_KERNEL);
199162306a36Sopenharmony_ci	if (ss->tx.info == NULL)
199262306a36Sopenharmony_ci		goto abort_with_rx_big_shadow;
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci	bytes = rx_ring_entries * sizeof(*ss->rx_small.info);
199562306a36Sopenharmony_ci	ss->rx_small.info = kzalloc(bytes, GFP_KERNEL);
199662306a36Sopenharmony_ci	if (ss->rx_small.info == NULL)
199762306a36Sopenharmony_ci		goto abort_with_tx_info;
199862306a36Sopenharmony_ci
199962306a36Sopenharmony_ci	bytes = rx_ring_entries * sizeof(*ss->rx_big.info);
200062306a36Sopenharmony_ci	ss->rx_big.info = kzalloc(bytes, GFP_KERNEL);
200162306a36Sopenharmony_ci	if (ss->rx_big.info == NULL)
200262306a36Sopenharmony_ci		goto abort_with_rx_small_info;
200362306a36Sopenharmony_ci
200462306a36Sopenharmony_ci	/* Fill the receive rings */
200562306a36Sopenharmony_ci	ss->rx_big.cnt = 0;
200662306a36Sopenharmony_ci	ss->rx_small.cnt = 0;
200762306a36Sopenharmony_ci	ss->rx_big.fill_cnt = 0;
200862306a36Sopenharmony_ci	ss->rx_small.fill_cnt = 0;
200962306a36Sopenharmony_ci	ss->rx_small.page_offset = MYRI10GE_ALLOC_SIZE;
201062306a36Sopenharmony_ci	ss->rx_big.page_offset = MYRI10GE_ALLOC_SIZE;
201162306a36Sopenharmony_ci	ss->rx_small.watchdog_needed = 0;
201262306a36Sopenharmony_ci	ss->rx_big.watchdog_needed = 0;
201362306a36Sopenharmony_ci	if (mgp->small_bytes == 0) {
201462306a36Sopenharmony_ci		ss->rx_small.fill_cnt = ss->rx_small.mask + 1;
201562306a36Sopenharmony_ci	} else {
201662306a36Sopenharmony_ci		myri10ge_alloc_rx_pages(mgp, &ss->rx_small,
201762306a36Sopenharmony_ci					mgp->small_bytes + MXGEFW_PAD, 0);
201862306a36Sopenharmony_ci	}
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_ci	if (ss->rx_small.fill_cnt < ss->rx_small.mask + 1) {
202162306a36Sopenharmony_ci		netdev_err(dev, "slice-%d: alloced only %d small bufs\n",
202262306a36Sopenharmony_ci			   slice, ss->rx_small.fill_cnt);
202362306a36Sopenharmony_ci		goto abort_with_rx_small_ring;
202462306a36Sopenharmony_ci	}
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_ci	myri10ge_alloc_rx_pages(mgp, &ss->rx_big, mgp->big_bytes, 0);
202762306a36Sopenharmony_ci	if (ss->rx_big.fill_cnt < ss->rx_big.mask + 1) {
202862306a36Sopenharmony_ci		netdev_err(dev, "slice-%d: alloced only %d big bufs\n",
202962306a36Sopenharmony_ci			   slice, ss->rx_big.fill_cnt);
203062306a36Sopenharmony_ci		goto abort_with_rx_big_ring;
203162306a36Sopenharmony_ci	}
203262306a36Sopenharmony_ci
203362306a36Sopenharmony_ci	return 0;
203462306a36Sopenharmony_ci
203562306a36Sopenharmony_ciabort_with_rx_big_ring:
203662306a36Sopenharmony_ci	for (i = ss->rx_big.cnt; i < ss->rx_big.fill_cnt; i++) {
203762306a36Sopenharmony_ci		int idx = i & ss->rx_big.mask;
203862306a36Sopenharmony_ci		myri10ge_unmap_rx_page(mgp->pdev, &ss->rx_big.info[idx],
203962306a36Sopenharmony_ci				       mgp->big_bytes);
204062306a36Sopenharmony_ci		put_page(ss->rx_big.info[idx].page);
204162306a36Sopenharmony_ci	}
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_ciabort_with_rx_small_ring:
204462306a36Sopenharmony_ci	if (mgp->small_bytes == 0)
204562306a36Sopenharmony_ci		ss->rx_small.fill_cnt = ss->rx_small.cnt;
204662306a36Sopenharmony_ci	for (i = ss->rx_small.cnt; i < ss->rx_small.fill_cnt; i++) {
204762306a36Sopenharmony_ci		int idx = i & ss->rx_small.mask;
204862306a36Sopenharmony_ci		myri10ge_unmap_rx_page(mgp->pdev, &ss->rx_small.info[idx],
204962306a36Sopenharmony_ci				       mgp->small_bytes + MXGEFW_PAD);
205062306a36Sopenharmony_ci		put_page(ss->rx_small.info[idx].page);
205162306a36Sopenharmony_ci	}
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_ci	kfree(ss->rx_big.info);
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ciabort_with_rx_small_info:
205662306a36Sopenharmony_ci	kfree(ss->rx_small.info);
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_ciabort_with_tx_info:
205962306a36Sopenharmony_ci	kfree(ss->tx.info);
206062306a36Sopenharmony_ci
206162306a36Sopenharmony_ciabort_with_rx_big_shadow:
206262306a36Sopenharmony_ci	kfree(ss->rx_big.shadow);
206362306a36Sopenharmony_ci
206462306a36Sopenharmony_ciabort_with_rx_small_shadow:
206562306a36Sopenharmony_ci	kfree(ss->rx_small.shadow);
206662306a36Sopenharmony_ci
206762306a36Sopenharmony_ciabort_with_tx_req_bytes:
206862306a36Sopenharmony_ci	kfree(ss->tx.req_bytes);
206962306a36Sopenharmony_ci	ss->tx.req_bytes = NULL;
207062306a36Sopenharmony_ci	ss->tx.req_list = NULL;
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_ciabort_with_nothing:
207362306a36Sopenharmony_ci	return status;
207462306a36Sopenharmony_ci}
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_cistatic void myri10ge_free_rings(struct myri10ge_slice_state *ss)
207762306a36Sopenharmony_ci{
207862306a36Sopenharmony_ci	struct myri10ge_priv *mgp = ss->mgp;
207962306a36Sopenharmony_ci	struct sk_buff *skb;
208062306a36Sopenharmony_ci	struct myri10ge_tx_buf *tx;
208162306a36Sopenharmony_ci	int i, len, idx;
208262306a36Sopenharmony_ci
208362306a36Sopenharmony_ci	/* If not allocated, skip it */
208462306a36Sopenharmony_ci	if (ss->tx.req_list == NULL)
208562306a36Sopenharmony_ci		return;
208662306a36Sopenharmony_ci
208762306a36Sopenharmony_ci	for (i = ss->rx_big.cnt; i < ss->rx_big.fill_cnt; i++) {
208862306a36Sopenharmony_ci		idx = i & ss->rx_big.mask;
208962306a36Sopenharmony_ci		if (i == ss->rx_big.fill_cnt - 1)
209062306a36Sopenharmony_ci			ss->rx_big.info[idx].page_offset = MYRI10GE_ALLOC_SIZE;
209162306a36Sopenharmony_ci		myri10ge_unmap_rx_page(mgp->pdev, &ss->rx_big.info[idx],
209262306a36Sopenharmony_ci				       mgp->big_bytes);
209362306a36Sopenharmony_ci		put_page(ss->rx_big.info[idx].page);
209462306a36Sopenharmony_ci	}
209562306a36Sopenharmony_ci
209662306a36Sopenharmony_ci	if (mgp->small_bytes == 0)
209762306a36Sopenharmony_ci		ss->rx_small.fill_cnt = ss->rx_small.cnt;
209862306a36Sopenharmony_ci	for (i = ss->rx_small.cnt; i < ss->rx_small.fill_cnt; i++) {
209962306a36Sopenharmony_ci		idx = i & ss->rx_small.mask;
210062306a36Sopenharmony_ci		if (i == ss->rx_small.fill_cnt - 1)
210162306a36Sopenharmony_ci			ss->rx_small.info[idx].page_offset =
210262306a36Sopenharmony_ci			    MYRI10GE_ALLOC_SIZE;
210362306a36Sopenharmony_ci		myri10ge_unmap_rx_page(mgp->pdev, &ss->rx_small.info[idx],
210462306a36Sopenharmony_ci				       mgp->small_bytes + MXGEFW_PAD);
210562306a36Sopenharmony_ci		put_page(ss->rx_small.info[idx].page);
210662306a36Sopenharmony_ci	}
210762306a36Sopenharmony_ci	tx = &ss->tx;
210862306a36Sopenharmony_ci	while (tx->done != tx->req) {
210962306a36Sopenharmony_ci		idx = tx->done & tx->mask;
211062306a36Sopenharmony_ci		skb = tx->info[idx].skb;
211162306a36Sopenharmony_ci
211262306a36Sopenharmony_ci		/* Mark as free */
211362306a36Sopenharmony_ci		tx->info[idx].skb = NULL;
211462306a36Sopenharmony_ci		tx->done++;
211562306a36Sopenharmony_ci		len = dma_unmap_len(&tx->info[idx], len);
211662306a36Sopenharmony_ci		dma_unmap_len_set(&tx->info[idx], len, 0);
211762306a36Sopenharmony_ci		if (skb) {
211862306a36Sopenharmony_ci			ss->stats.tx_dropped++;
211962306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
212062306a36Sopenharmony_ci			if (len)
212162306a36Sopenharmony_ci				dma_unmap_single(&mgp->pdev->dev,
212262306a36Sopenharmony_ci						 dma_unmap_addr(&tx->info[idx],
212362306a36Sopenharmony_ci								bus), len,
212462306a36Sopenharmony_ci						 DMA_TO_DEVICE);
212562306a36Sopenharmony_ci		} else {
212662306a36Sopenharmony_ci			if (len)
212762306a36Sopenharmony_ci				dma_unmap_page(&mgp->pdev->dev,
212862306a36Sopenharmony_ci					       dma_unmap_addr(&tx->info[idx],
212962306a36Sopenharmony_ci							      bus), len,
213062306a36Sopenharmony_ci					       DMA_TO_DEVICE);
213162306a36Sopenharmony_ci		}
213262306a36Sopenharmony_ci	}
213362306a36Sopenharmony_ci	kfree(ss->rx_big.info);
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci	kfree(ss->rx_small.info);
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci	kfree(ss->tx.info);
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci	kfree(ss->rx_big.shadow);
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_ci	kfree(ss->rx_small.shadow);
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_ci	kfree(ss->tx.req_bytes);
214462306a36Sopenharmony_ci	ss->tx.req_bytes = NULL;
214562306a36Sopenharmony_ci	ss->tx.req_list = NULL;
214662306a36Sopenharmony_ci}
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_cistatic int myri10ge_request_irq(struct myri10ge_priv *mgp)
214962306a36Sopenharmony_ci{
215062306a36Sopenharmony_ci	struct pci_dev *pdev = mgp->pdev;
215162306a36Sopenharmony_ci	struct myri10ge_slice_state *ss;
215262306a36Sopenharmony_ci	struct net_device *netdev = mgp->dev;
215362306a36Sopenharmony_ci	int i;
215462306a36Sopenharmony_ci	int status;
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_ci	mgp->msi_enabled = 0;
215762306a36Sopenharmony_ci	mgp->msix_enabled = 0;
215862306a36Sopenharmony_ci	status = 0;
215962306a36Sopenharmony_ci	if (myri10ge_msi) {
216062306a36Sopenharmony_ci		if (mgp->num_slices > 1) {
216162306a36Sopenharmony_ci			status = pci_enable_msix_range(pdev, mgp->msix_vectors,
216262306a36Sopenharmony_ci					mgp->num_slices, mgp->num_slices);
216362306a36Sopenharmony_ci			if (status < 0) {
216462306a36Sopenharmony_ci				dev_err(&pdev->dev,
216562306a36Sopenharmony_ci					"Error %d setting up MSI-X\n", status);
216662306a36Sopenharmony_ci				return status;
216762306a36Sopenharmony_ci			}
216862306a36Sopenharmony_ci			mgp->msix_enabled = 1;
216962306a36Sopenharmony_ci		}
217062306a36Sopenharmony_ci		if (mgp->msix_enabled == 0) {
217162306a36Sopenharmony_ci			status = pci_enable_msi(pdev);
217262306a36Sopenharmony_ci			if (status != 0) {
217362306a36Sopenharmony_ci				dev_err(&pdev->dev,
217462306a36Sopenharmony_ci					"Error %d setting up MSI; falling back to xPIC\n",
217562306a36Sopenharmony_ci					status);
217662306a36Sopenharmony_ci			} else {
217762306a36Sopenharmony_ci				mgp->msi_enabled = 1;
217862306a36Sopenharmony_ci			}
217962306a36Sopenharmony_ci		}
218062306a36Sopenharmony_ci	}
218162306a36Sopenharmony_ci	if (mgp->msix_enabled) {
218262306a36Sopenharmony_ci		for (i = 0; i < mgp->num_slices; i++) {
218362306a36Sopenharmony_ci			ss = &mgp->ss[i];
218462306a36Sopenharmony_ci			snprintf(ss->irq_desc, sizeof(ss->irq_desc),
218562306a36Sopenharmony_ci				 "%s:slice-%d", netdev->name, i);
218662306a36Sopenharmony_ci			status = request_irq(mgp->msix_vectors[i].vector,
218762306a36Sopenharmony_ci					     myri10ge_intr, 0, ss->irq_desc,
218862306a36Sopenharmony_ci					     ss);
218962306a36Sopenharmony_ci			if (status != 0) {
219062306a36Sopenharmony_ci				dev_err(&pdev->dev,
219162306a36Sopenharmony_ci					"slice %d failed to allocate IRQ\n", i);
219262306a36Sopenharmony_ci				i--;
219362306a36Sopenharmony_ci				while (i >= 0) {
219462306a36Sopenharmony_ci					free_irq(mgp->msix_vectors[i].vector,
219562306a36Sopenharmony_ci						 &mgp->ss[i]);
219662306a36Sopenharmony_ci					i--;
219762306a36Sopenharmony_ci				}
219862306a36Sopenharmony_ci				pci_disable_msix(pdev);
219962306a36Sopenharmony_ci				return status;
220062306a36Sopenharmony_ci			}
220162306a36Sopenharmony_ci		}
220262306a36Sopenharmony_ci	} else {
220362306a36Sopenharmony_ci		status = request_irq(pdev->irq, myri10ge_intr, IRQF_SHARED,
220462306a36Sopenharmony_ci				     mgp->dev->name, &mgp->ss[0]);
220562306a36Sopenharmony_ci		if (status != 0) {
220662306a36Sopenharmony_ci			dev_err(&pdev->dev, "failed to allocate IRQ\n");
220762306a36Sopenharmony_ci			if (mgp->msi_enabled)
220862306a36Sopenharmony_ci				pci_disable_msi(pdev);
220962306a36Sopenharmony_ci		}
221062306a36Sopenharmony_ci	}
221162306a36Sopenharmony_ci	return status;
221262306a36Sopenharmony_ci}
221362306a36Sopenharmony_ci
221462306a36Sopenharmony_cistatic void myri10ge_free_irq(struct myri10ge_priv *mgp)
221562306a36Sopenharmony_ci{
221662306a36Sopenharmony_ci	struct pci_dev *pdev = mgp->pdev;
221762306a36Sopenharmony_ci	int i;
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_ci	if (mgp->msix_enabled) {
222062306a36Sopenharmony_ci		for (i = 0; i < mgp->num_slices; i++)
222162306a36Sopenharmony_ci			free_irq(mgp->msix_vectors[i].vector, &mgp->ss[i]);
222262306a36Sopenharmony_ci	} else {
222362306a36Sopenharmony_ci		free_irq(pdev->irq, &mgp->ss[0]);
222462306a36Sopenharmony_ci	}
222562306a36Sopenharmony_ci	if (mgp->msi_enabled)
222662306a36Sopenharmony_ci		pci_disable_msi(pdev);
222762306a36Sopenharmony_ci	if (mgp->msix_enabled)
222862306a36Sopenharmony_ci		pci_disable_msix(pdev);
222962306a36Sopenharmony_ci}
223062306a36Sopenharmony_ci
223162306a36Sopenharmony_cistatic int myri10ge_get_txrx(struct myri10ge_priv *mgp, int slice)
223262306a36Sopenharmony_ci{
223362306a36Sopenharmony_ci	struct myri10ge_cmd cmd;
223462306a36Sopenharmony_ci	struct myri10ge_slice_state *ss;
223562306a36Sopenharmony_ci	int status;
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_ci	ss = &mgp->ss[slice];
223862306a36Sopenharmony_ci	status = 0;
223962306a36Sopenharmony_ci	if (slice == 0 || (mgp->dev->real_num_tx_queues > 1)) {
224062306a36Sopenharmony_ci		cmd.data0 = slice;
224162306a36Sopenharmony_ci		status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_SEND_OFFSET,
224262306a36Sopenharmony_ci					   &cmd, 0);
224362306a36Sopenharmony_ci		ss->tx.lanai = (struct mcp_kreq_ether_send __iomem *)
224462306a36Sopenharmony_ci		    (mgp->sram + cmd.data0);
224562306a36Sopenharmony_ci	}
224662306a36Sopenharmony_ci	cmd.data0 = slice;
224762306a36Sopenharmony_ci	status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_SMALL_RX_OFFSET,
224862306a36Sopenharmony_ci				    &cmd, 0);
224962306a36Sopenharmony_ci	ss->rx_small.lanai = (struct mcp_kreq_ether_recv __iomem *)
225062306a36Sopenharmony_ci	    (mgp->sram + cmd.data0);
225162306a36Sopenharmony_ci
225262306a36Sopenharmony_ci	cmd.data0 = slice;
225362306a36Sopenharmony_ci	status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd, 0);
225462306a36Sopenharmony_ci	ss->rx_big.lanai = (struct mcp_kreq_ether_recv __iomem *)
225562306a36Sopenharmony_ci	    (mgp->sram + cmd.data0);
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_ci	ss->tx.send_go = (__iomem __be32 *)
225862306a36Sopenharmony_ci	    (mgp->sram + MXGEFW_ETH_SEND_GO + 64 * slice);
225962306a36Sopenharmony_ci	ss->tx.send_stop = (__iomem __be32 *)
226062306a36Sopenharmony_ci	    (mgp->sram + MXGEFW_ETH_SEND_STOP + 64 * slice);
226162306a36Sopenharmony_ci	return status;
226262306a36Sopenharmony_ci
226362306a36Sopenharmony_ci}
226462306a36Sopenharmony_ci
226562306a36Sopenharmony_cistatic int myri10ge_set_stats(struct myri10ge_priv *mgp, int slice)
226662306a36Sopenharmony_ci{
226762306a36Sopenharmony_ci	struct myri10ge_cmd cmd;
226862306a36Sopenharmony_ci	struct myri10ge_slice_state *ss;
226962306a36Sopenharmony_ci	int status;
227062306a36Sopenharmony_ci
227162306a36Sopenharmony_ci	ss = &mgp->ss[slice];
227262306a36Sopenharmony_ci	cmd.data0 = MYRI10GE_LOWPART_TO_U32(ss->fw_stats_bus);
227362306a36Sopenharmony_ci	cmd.data1 = MYRI10GE_HIGHPART_TO_U32(ss->fw_stats_bus);
227462306a36Sopenharmony_ci	cmd.data2 = sizeof(struct mcp_irq_data) | (slice << 16);
227562306a36Sopenharmony_ci	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd, 0);
227662306a36Sopenharmony_ci	if (status == -ENOSYS) {
227762306a36Sopenharmony_ci		dma_addr_t bus = ss->fw_stats_bus;
227862306a36Sopenharmony_ci		if (slice != 0)
227962306a36Sopenharmony_ci			return -EINVAL;
228062306a36Sopenharmony_ci		bus += offsetof(struct mcp_irq_data, send_done_count);
228162306a36Sopenharmony_ci		cmd.data0 = MYRI10GE_LOWPART_TO_U32(bus);
228262306a36Sopenharmony_ci		cmd.data1 = MYRI10GE_HIGHPART_TO_U32(bus);
228362306a36Sopenharmony_ci		status = myri10ge_send_cmd(mgp,
228462306a36Sopenharmony_ci					   MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
228562306a36Sopenharmony_ci					   &cmd, 0);
228662306a36Sopenharmony_ci		/* Firmware cannot support multicast without STATS_DMA_V2 */
228762306a36Sopenharmony_ci		mgp->fw_multicast_support = 0;
228862306a36Sopenharmony_ci	} else {
228962306a36Sopenharmony_ci		mgp->fw_multicast_support = 1;
229062306a36Sopenharmony_ci	}
229162306a36Sopenharmony_ci	return 0;
229262306a36Sopenharmony_ci}
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_cistatic int myri10ge_open(struct net_device *dev)
229562306a36Sopenharmony_ci{
229662306a36Sopenharmony_ci	struct myri10ge_slice_state *ss;
229762306a36Sopenharmony_ci	struct myri10ge_priv *mgp = netdev_priv(dev);
229862306a36Sopenharmony_ci	struct myri10ge_cmd cmd;
229962306a36Sopenharmony_ci	int i, status, big_pow2, slice;
230062306a36Sopenharmony_ci	u8 __iomem *itable;
230162306a36Sopenharmony_ci
230262306a36Sopenharmony_ci	if (mgp->running != MYRI10GE_ETH_STOPPED)
230362306a36Sopenharmony_ci		return -EBUSY;
230462306a36Sopenharmony_ci
230562306a36Sopenharmony_ci	mgp->running = MYRI10GE_ETH_STARTING;
230662306a36Sopenharmony_ci	status = myri10ge_reset(mgp);
230762306a36Sopenharmony_ci	if (status != 0) {
230862306a36Sopenharmony_ci		netdev_err(dev, "failed reset\n");
230962306a36Sopenharmony_ci		goto abort_with_nothing;
231062306a36Sopenharmony_ci	}
231162306a36Sopenharmony_ci
231262306a36Sopenharmony_ci	if (mgp->num_slices > 1) {
231362306a36Sopenharmony_ci		cmd.data0 = mgp->num_slices;
231462306a36Sopenharmony_ci		cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
231562306a36Sopenharmony_ci		if (mgp->dev->real_num_tx_queues > 1)
231662306a36Sopenharmony_ci			cmd.data1 |= MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES;
231762306a36Sopenharmony_ci		status = myri10ge_send_cmd(mgp, MXGEFW_CMD_ENABLE_RSS_QUEUES,
231862306a36Sopenharmony_ci					   &cmd, 0);
231962306a36Sopenharmony_ci		if (status != 0) {
232062306a36Sopenharmony_ci			netdev_err(dev, "failed to set number of slices\n");
232162306a36Sopenharmony_ci			goto abort_with_nothing;
232262306a36Sopenharmony_ci		}
232362306a36Sopenharmony_ci		/* setup the indirection table */
232462306a36Sopenharmony_ci		cmd.data0 = mgp->num_slices;
232562306a36Sopenharmony_ci		status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_RSS_TABLE_SIZE,
232662306a36Sopenharmony_ci					   &cmd, 0);
232762306a36Sopenharmony_ci
232862306a36Sopenharmony_ci		status |= myri10ge_send_cmd(mgp,
232962306a36Sopenharmony_ci					    MXGEFW_CMD_GET_RSS_TABLE_OFFSET,
233062306a36Sopenharmony_ci					    &cmd, 0);
233162306a36Sopenharmony_ci		if (status != 0) {
233262306a36Sopenharmony_ci			netdev_err(dev, "failed to setup rss tables\n");
233362306a36Sopenharmony_ci			goto abort_with_nothing;
233462306a36Sopenharmony_ci		}
233562306a36Sopenharmony_ci
233662306a36Sopenharmony_ci		/* just enable an identity mapping */
233762306a36Sopenharmony_ci		itable = mgp->sram + cmd.data0;
233862306a36Sopenharmony_ci		for (i = 0; i < mgp->num_slices; i++)
233962306a36Sopenharmony_ci			__raw_writeb(i, &itable[i]);
234062306a36Sopenharmony_ci
234162306a36Sopenharmony_ci		cmd.data0 = 1;
234262306a36Sopenharmony_ci		cmd.data1 = myri10ge_rss_hash;
234362306a36Sopenharmony_ci		status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_RSS_ENABLE,
234462306a36Sopenharmony_ci					   &cmd, 0);
234562306a36Sopenharmony_ci		if (status != 0) {
234662306a36Sopenharmony_ci			netdev_err(dev, "failed to enable slices\n");
234762306a36Sopenharmony_ci			goto abort_with_nothing;
234862306a36Sopenharmony_ci		}
234962306a36Sopenharmony_ci	}
235062306a36Sopenharmony_ci
235162306a36Sopenharmony_ci	status = myri10ge_request_irq(mgp);
235262306a36Sopenharmony_ci	if (status != 0)
235362306a36Sopenharmony_ci		goto abort_with_nothing;
235462306a36Sopenharmony_ci
235562306a36Sopenharmony_ci	/* decide what small buffer size to use.  For good TCP rx
235662306a36Sopenharmony_ci	 * performance, it is important to not receive 1514 byte
235762306a36Sopenharmony_ci	 * frames into jumbo buffers, as it confuses the socket buffer
235862306a36Sopenharmony_ci	 * accounting code, leading to drops and erratic performance.
235962306a36Sopenharmony_ci	 */
236062306a36Sopenharmony_ci
236162306a36Sopenharmony_ci	if (dev->mtu <= ETH_DATA_LEN)
236262306a36Sopenharmony_ci		/* enough for a TCP header */
236362306a36Sopenharmony_ci		mgp->small_bytes = (128 > SMP_CACHE_BYTES)
236462306a36Sopenharmony_ci		    ? (128 - MXGEFW_PAD)
236562306a36Sopenharmony_ci		    : (SMP_CACHE_BYTES - MXGEFW_PAD);
236662306a36Sopenharmony_ci	else
236762306a36Sopenharmony_ci		/* enough for a vlan encapsulated ETH_DATA_LEN frame */
236862306a36Sopenharmony_ci		mgp->small_bytes = VLAN_ETH_FRAME_LEN;
236962306a36Sopenharmony_ci
237062306a36Sopenharmony_ci	/* Override the small buffer size? */
237162306a36Sopenharmony_ci	if (myri10ge_small_bytes >= 0)
237262306a36Sopenharmony_ci		mgp->small_bytes = myri10ge_small_bytes;
237362306a36Sopenharmony_ci
237462306a36Sopenharmony_ci	/* Firmware needs the big buff size as a power of 2.  Lie and
237562306a36Sopenharmony_ci	 * tell him the buffer is larger, because we only use 1
237662306a36Sopenharmony_ci	 * buffer/pkt, and the mtu will prevent overruns.
237762306a36Sopenharmony_ci	 */
237862306a36Sopenharmony_ci	big_pow2 = dev->mtu + ETH_HLEN + VLAN_HLEN + MXGEFW_PAD;
237962306a36Sopenharmony_ci	if (big_pow2 < MYRI10GE_ALLOC_SIZE / 2) {
238062306a36Sopenharmony_ci		while (!is_power_of_2(big_pow2))
238162306a36Sopenharmony_ci			big_pow2++;
238262306a36Sopenharmony_ci		mgp->big_bytes = dev->mtu + ETH_HLEN + VLAN_HLEN + MXGEFW_PAD;
238362306a36Sopenharmony_ci	} else {
238462306a36Sopenharmony_ci		big_pow2 = MYRI10GE_ALLOC_SIZE;
238562306a36Sopenharmony_ci		mgp->big_bytes = big_pow2;
238662306a36Sopenharmony_ci	}
238762306a36Sopenharmony_ci
238862306a36Sopenharmony_ci	/* setup the per-slice data structures */
238962306a36Sopenharmony_ci	for (slice = 0; slice < mgp->num_slices; slice++) {
239062306a36Sopenharmony_ci		ss = &mgp->ss[slice];
239162306a36Sopenharmony_ci
239262306a36Sopenharmony_ci		status = myri10ge_get_txrx(mgp, slice);
239362306a36Sopenharmony_ci		if (status != 0) {
239462306a36Sopenharmony_ci			netdev_err(dev, "failed to get ring sizes or locations\n");
239562306a36Sopenharmony_ci			goto abort_with_rings;
239662306a36Sopenharmony_ci		}
239762306a36Sopenharmony_ci		status = myri10ge_allocate_rings(ss);
239862306a36Sopenharmony_ci		if (status != 0)
239962306a36Sopenharmony_ci			goto abort_with_rings;
240062306a36Sopenharmony_ci
240162306a36Sopenharmony_ci		/* only firmware which supports multiple TX queues
240262306a36Sopenharmony_ci		 * supports setting up the tx stats on non-zero
240362306a36Sopenharmony_ci		 * slices */
240462306a36Sopenharmony_ci		if (slice == 0 || mgp->dev->real_num_tx_queues > 1)
240562306a36Sopenharmony_ci			status = myri10ge_set_stats(mgp, slice);
240662306a36Sopenharmony_ci		if (status) {
240762306a36Sopenharmony_ci			netdev_err(dev, "Couldn't set stats DMA\n");
240862306a36Sopenharmony_ci			goto abort_with_rings;
240962306a36Sopenharmony_ci		}
241062306a36Sopenharmony_ci
241162306a36Sopenharmony_ci		/* must happen prior to any irq */
241262306a36Sopenharmony_ci		napi_enable(&(ss)->napi);
241362306a36Sopenharmony_ci	}
241462306a36Sopenharmony_ci
241562306a36Sopenharmony_ci	/* now give firmware buffers sizes, and MTU */
241662306a36Sopenharmony_ci	cmd.data0 = dev->mtu + ETH_HLEN + VLAN_HLEN;
241762306a36Sopenharmony_ci	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_MTU, &cmd, 0);
241862306a36Sopenharmony_ci	cmd.data0 = mgp->small_bytes;
241962306a36Sopenharmony_ci	status |=
242062306a36Sopenharmony_ci	    myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE, &cmd, 0);
242162306a36Sopenharmony_ci	cmd.data0 = big_pow2;
242262306a36Sopenharmony_ci	status |=
242362306a36Sopenharmony_ci	    myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd, 0);
242462306a36Sopenharmony_ci	if (status) {
242562306a36Sopenharmony_ci		netdev_err(dev, "Couldn't set buffer sizes\n");
242662306a36Sopenharmony_ci		goto abort_with_rings;
242762306a36Sopenharmony_ci	}
242862306a36Sopenharmony_ci
242962306a36Sopenharmony_ci	/*
243062306a36Sopenharmony_ci	 * Set Linux style TSO mode; this is needed only on newer
243162306a36Sopenharmony_ci	 *  firmware versions.  Older versions default to Linux
243262306a36Sopenharmony_ci	 *  style TSO
243362306a36Sopenharmony_ci	 */
243462306a36Sopenharmony_ci	cmd.data0 = 0;
243562306a36Sopenharmony_ci	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_TSO_MODE, &cmd, 0);
243662306a36Sopenharmony_ci	if (status && status != -ENOSYS) {
243762306a36Sopenharmony_ci		netdev_err(dev, "Couldn't set TSO mode\n");
243862306a36Sopenharmony_ci		goto abort_with_rings;
243962306a36Sopenharmony_ci	}
244062306a36Sopenharmony_ci
244162306a36Sopenharmony_ci	mgp->link_state = ~0U;
244262306a36Sopenharmony_ci	mgp->rdma_tags_available = 15;
244362306a36Sopenharmony_ci
244462306a36Sopenharmony_ci	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_ETHERNET_UP, &cmd, 0);
244562306a36Sopenharmony_ci	if (status) {
244662306a36Sopenharmony_ci		netdev_err(dev, "Couldn't bring up link\n");
244762306a36Sopenharmony_ci		goto abort_with_rings;
244862306a36Sopenharmony_ci	}
244962306a36Sopenharmony_ci
245062306a36Sopenharmony_ci	mgp->running = MYRI10GE_ETH_RUNNING;
245162306a36Sopenharmony_ci	mgp->watchdog_timer.expires = jiffies + myri10ge_watchdog_timeout * HZ;
245262306a36Sopenharmony_ci	add_timer(&mgp->watchdog_timer);
245362306a36Sopenharmony_ci	netif_tx_wake_all_queues(dev);
245462306a36Sopenharmony_ci
245562306a36Sopenharmony_ci	return 0;
245662306a36Sopenharmony_ci
245762306a36Sopenharmony_ciabort_with_rings:
245862306a36Sopenharmony_ci	while (slice) {
245962306a36Sopenharmony_ci		slice--;
246062306a36Sopenharmony_ci		napi_disable(&mgp->ss[slice].napi);
246162306a36Sopenharmony_ci	}
246262306a36Sopenharmony_ci	for (i = 0; i < mgp->num_slices; i++)
246362306a36Sopenharmony_ci		myri10ge_free_rings(&mgp->ss[i]);
246462306a36Sopenharmony_ci
246562306a36Sopenharmony_ci	myri10ge_free_irq(mgp);
246662306a36Sopenharmony_ci
246762306a36Sopenharmony_ciabort_with_nothing:
246862306a36Sopenharmony_ci	mgp->running = MYRI10GE_ETH_STOPPED;
246962306a36Sopenharmony_ci	return -ENOMEM;
247062306a36Sopenharmony_ci}
247162306a36Sopenharmony_ci
247262306a36Sopenharmony_cistatic int myri10ge_close(struct net_device *dev)
247362306a36Sopenharmony_ci{
247462306a36Sopenharmony_ci	struct myri10ge_priv *mgp = netdev_priv(dev);
247562306a36Sopenharmony_ci	struct myri10ge_cmd cmd;
247662306a36Sopenharmony_ci	int status, old_down_cnt;
247762306a36Sopenharmony_ci	int i;
247862306a36Sopenharmony_ci
247962306a36Sopenharmony_ci	if (mgp->running != MYRI10GE_ETH_RUNNING)
248062306a36Sopenharmony_ci		return 0;
248162306a36Sopenharmony_ci
248262306a36Sopenharmony_ci	if (mgp->ss[0].tx.req_bytes == NULL)
248362306a36Sopenharmony_ci		return 0;
248462306a36Sopenharmony_ci
248562306a36Sopenharmony_ci	del_timer_sync(&mgp->watchdog_timer);
248662306a36Sopenharmony_ci	mgp->running = MYRI10GE_ETH_STOPPING;
248762306a36Sopenharmony_ci	for (i = 0; i < mgp->num_slices; i++)
248862306a36Sopenharmony_ci		napi_disable(&mgp->ss[i].napi);
248962306a36Sopenharmony_ci
249062306a36Sopenharmony_ci	netif_carrier_off(dev);
249162306a36Sopenharmony_ci
249262306a36Sopenharmony_ci	netif_tx_stop_all_queues(dev);
249362306a36Sopenharmony_ci	if (mgp->rebooted == 0) {
249462306a36Sopenharmony_ci		old_down_cnt = mgp->down_cnt;
249562306a36Sopenharmony_ci		mb();
249662306a36Sopenharmony_ci		status =
249762306a36Sopenharmony_ci		    myri10ge_send_cmd(mgp, MXGEFW_CMD_ETHERNET_DOWN, &cmd, 0);
249862306a36Sopenharmony_ci		if (status)
249962306a36Sopenharmony_ci			netdev_err(dev, "Couldn't bring down link\n");
250062306a36Sopenharmony_ci
250162306a36Sopenharmony_ci		wait_event_timeout(mgp->down_wq, old_down_cnt != mgp->down_cnt,
250262306a36Sopenharmony_ci				   HZ);
250362306a36Sopenharmony_ci		if (old_down_cnt == mgp->down_cnt)
250462306a36Sopenharmony_ci			netdev_err(dev, "never got down irq\n");
250562306a36Sopenharmony_ci	}
250662306a36Sopenharmony_ci	netif_tx_disable(dev);
250762306a36Sopenharmony_ci	myri10ge_free_irq(mgp);
250862306a36Sopenharmony_ci	for (i = 0; i < mgp->num_slices; i++)
250962306a36Sopenharmony_ci		myri10ge_free_rings(&mgp->ss[i]);
251062306a36Sopenharmony_ci
251162306a36Sopenharmony_ci	mgp->running = MYRI10GE_ETH_STOPPED;
251262306a36Sopenharmony_ci	return 0;
251362306a36Sopenharmony_ci}
251462306a36Sopenharmony_ci
251562306a36Sopenharmony_ci/* copy an array of struct mcp_kreq_ether_send's to the mcp.  Copy
251662306a36Sopenharmony_ci * backwards one at a time and handle ring wraps */
251762306a36Sopenharmony_ci
251862306a36Sopenharmony_cistatic inline void
251962306a36Sopenharmony_cimyri10ge_submit_req_backwards(struct myri10ge_tx_buf *tx,
252062306a36Sopenharmony_ci			      struct mcp_kreq_ether_send *src, int cnt)
252162306a36Sopenharmony_ci{
252262306a36Sopenharmony_ci	int idx, starting_slot;
252362306a36Sopenharmony_ci	starting_slot = tx->req;
252462306a36Sopenharmony_ci	while (cnt > 1) {
252562306a36Sopenharmony_ci		cnt--;
252662306a36Sopenharmony_ci		idx = (starting_slot + cnt) & tx->mask;
252762306a36Sopenharmony_ci		myri10ge_pio_copy(&tx->lanai[idx], &src[cnt], sizeof(*src));
252862306a36Sopenharmony_ci		mb();
252962306a36Sopenharmony_ci	}
253062306a36Sopenharmony_ci}
253162306a36Sopenharmony_ci
253262306a36Sopenharmony_ci/*
253362306a36Sopenharmony_ci * copy an array of struct mcp_kreq_ether_send's to the mcp.  Copy
253462306a36Sopenharmony_ci * at most 32 bytes at a time, so as to avoid involving the software
253562306a36Sopenharmony_ci * pio handler in the nic.   We re-write the first segment's flags
253662306a36Sopenharmony_ci * to mark them valid only after writing the entire chain.
253762306a36Sopenharmony_ci */
253862306a36Sopenharmony_ci
253962306a36Sopenharmony_cistatic inline void
254062306a36Sopenharmony_cimyri10ge_submit_req(struct myri10ge_tx_buf *tx, struct mcp_kreq_ether_send *src,
254162306a36Sopenharmony_ci		    int cnt)
254262306a36Sopenharmony_ci{
254362306a36Sopenharmony_ci	int idx, i;
254462306a36Sopenharmony_ci	struct mcp_kreq_ether_send __iomem *dstp, *dst;
254562306a36Sopenharmony_ci	struct mcp_kreq_ether_send *srcp;
254662306a36Sopenharmony_ci	u8 last_flags;
254762306a36Sopenharmony_ci
254862306a36Sopenharmony_ci	idx = tx->req & tx->mask;
254962306a36Sopenharmony_ci
255062306a36Sopenharmony_ci	last_flags = src->flags;
255162306a36Sopenharmony_ci	src->flags = 0;
255262306a36Sopenharmony_ci	mb();
255362306a36Sopenharmony_ci	dst = dstp = &tx->lanai[idx];
255462306a36Sopenharmony_ci	srcp = src;
255562306a36Sopenharmony_ci
255662306a36Sopenharmony_ci	if ((idx + cnt) < tx->mask) {
255762306a36Sopenharmony_ci		for (i = 0; i < (cnt - 1); i += 2) {
255862306a36Sopenharmony_ci			myri10ge_pio_copy(dstp, srcp, 2 * sizeof(*src));
255962306a36Sopenharmony_ci			mb();	/* force write every 32 bytes */
256062306a36Sopenharmony_ci			srcp += 2;
256162306a36Sopenharmony_ci			dstp += 2;
256262306a36Sopenharmony_ci		}
256362306a36Sopenharmony_ci	} else {
256462306a36Sopenharmony_ci		/* submit all but the first request, and ensure
256562306a36Sopenharmony_ci		 * that it is submitted below */
256662306a36Sopenharmony_ci		myri10ge_submit_req_backwards(tx, src, cnt);
256762306a36Sopenharmony_ci		i = 0;
256862306a36Sopenharmony_ci	}
256962306a36Sopenharmony_ci	if (i < cnt) {
257062306a36Sopenharmony_ci		/* submit the first request */
257162306a36Sopenharmony_ci		myri10ge_pio_copy(dstp, srcp, sizeof(*src));
257262306a36Sopenharmony_ci		mb();		/* barrier before setting valid flag */
257362306a36Sopenharmony_ci	}
257462306a36Sopenharmony_ci
257562306a36Sopenharmony_ci	/* re-write the last 32-bits with the valid flags */
257662306a36Sopenharmony_ci	src->flags = last_flags;
257762306a36Sopenharmony_ci	put_be32(*((__be32 *) src + 3), (__be32 __iomem *) dst + 3);
257862306a36Sopenharmony_ci	tx->req += cnt;
257962306a36Sopenharmony_ci	mb();
258062306a36Sopenharmony_ci}
258162306a36Sopenharmony_ci
258262306a36Sopenharmony_cistatic void myri10ge_unmap_tx_dma(struct myri10ge_priv *mgp,
258362306a36Sopenharmony_ci				  struct myri10ge_tx_buf *tx, int idx)
258462306a36Sopenharmony_ci{
258562306a36Sopenharmony_ci	unsigned int len;
258662306a36Sopenharmony_ci	int last_idx;
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ci	/* Free any DMA resources we've alloced and clear out the skb slot */
258962306a36Sopenharmony_ci	last_idx = (idx + 1) & tx->mask;
259062306a36Sopenharmony_ci	idx = tx->req & tx->mask;
259162306a36Sopenharmony_ci	do {
259262306a36Sopenharmony_ci		len = dma_unmap_len(&tx->info[idx], len);
259362306a36Sopenharmony_ci		if (len) {
259462306a36Sopenharmony_ci			if (tx->info[idx].skb != NULL)
259562306a36Sopenharmony_ci				dma_unmap_single(&mgp->pdev->dev,
259662306a36Sopenharmony_ci						 dma_unmap_addr(&tx->info[idx],
259762306a36Sopenharmony_ci								bus), len,
259862306a36Sopenharmony_ci						 DMA_TO_DEVICE);
259962306a36Sopenharmony_ci			else
260062306a36Sopenharmony_ci				dma_unmap_page(&mgp->pdev->dev,
260162306a36Sopenharmony_ci					       dma_unmap_addr(&tx->info[idx],
260262306a36Sopenharmony_ci							      bus), len,
260362306a36Sopenharmony_ci					       DMA_TO_DEVICE);
260462306a36Sopenharmony_ci			dma_unmap_len_set(&tx->info[idx], len, 0);
260562306a36Sopenharmony_ci			tx->info[idx].skb = NULL;
260662306a36Sopenharmony_ci		}
260762306a36Sopenharmony_ci		idx = (idx + 1) & tx->mask;
260862306a36Sopenharmony_ci	} while (idx != last_idx);
260962306a36Sopenharmony_ci}
261062306a36Sopenharmony_ci
261162306a36Sopenharmony_ci/*
261262306a36Sopenharmony_ci * Transmit a packet.  We need to split the packet so that a single
261362306a36Sopenharmony_ci * segment does not cross myri10ge->tx_boundary, so this makes segment
261462306a36Sopenharmony_ci * counting tricky.  So rather than try to count segments up front, we
261562306a36Sopenharmony_ci * just give up if there are too few segments to hold a reasonably
261662306a36Sopenharmony_ci * fragmented packet currently available.  If we run
261762306a36Sopenharmony_ci * out of segments while preparing a packet for DMA, we just linearize
261862306a36Sopenharmony_ci * it and try again.
261962306a36Sopenharmony_ci */
262062306a36Sopenharmony_ci
262162306a36Sopenharmony_cistatic netdev_tx_t myri10ge_xmit(struct sk_buff *skb,
262262306a36Sopenharmony_ci				       struct net_device *dev)
262362306a36Sopenharmony_ci{
262462306a36Sopenharmony_ci	struct myri10ge_priv *mgp = netdev_priv(dev);
262562306a36Sopenharmony_ci	struct myri10ge_slice_state *ss;
262662306a36Sopenharmony_ci	struct mcp_kreq_ether_send *req;
262762306a36Sopenharmony_ci	struct myri10ge_tx_buf *tx;
262862306a36Sopenharmony_ci	skb_frag_t *frag;
262962306a36Sopenharmony_ci	struct netdev_queue *netdev_queue;
263062306a36Sopenharmony_ci	dma_addr_t bus;
263162306a36Sopenharmony_ci	u32 low;
263262306a36Sopenharmony_ci	__be32 high_swapped;
263362306a36Sopenharmony_ci	unsigned int len;
263462306a36Sopenharmony_ci	int idx, avail, frag_cnt, frag_idx, count, mss, max_segments;
263562306a36Sopenharmony_ci	u16 pseudo_hdr_offset, cksum_offset, queue;
263662306a36Sopenharmony_ci	int cum_len, seglen, boundary, rdma_count;
263762306a36Sopenharmony_ci	u8 flags, odd_flag;
263862306a36Sopenharmony_ci
263962306a36Sopenharmony_ci	queue = skb_get_queue_mapping(skb);
264062306a36Sopenharmony_ci	ss = &mgp->ss[queue];
264162306a36Sopenharmony_ci	netdev_queue = netdev_get_tx_queue(mgp->dev, queue);
264262306a36Sopenharmony_ci	tx = &ss->tx;
264362306a36Sopenharmony_ci
264462306a36Sopenharmony_ciagain:
264562306a36Sopenharmony_ci	req = tx->req_list;
264662306a36Sopenharmony_ci	avail = tx->mask - 1 - (tx->req - tx->done);
264762306a36Sopenharmony_ci
264862306a36Sopenharmony_ci	mss = 0;
264962306a36Sopenharmony_ci	max_segments = MXGEFW_MAX_SEND_DESC;
265062306a36Sopenharmony_ci
265162306a36Sopenharmony_ci	if (skb_is_gso(skb)) {
265262306a36Sopenharmony_ci		mss = skb_shinfo(skb)->gso_size;
265362306a36Sopenharmony_ci		max_segments = MYRI10GE_MAX_SEND_DESC_TSO;
265462306a36Sopenharmony_ci	}
265562306a36Sopenharmony_ci
265662306a36Sopenharmony_ci	if ((unlikely(avail < max_segments))) {
265762306a36Sopenharmony_ci		/* we are out of transmit resources */
265862306a36Sopenharmony_ci		tx->stop_queue++;
265962306a36Sopenharmony_ci		netif_tx_stop_queue(netdev_queue);
266062306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
266162306a36Sopenharmony_ci	}
266262306a36Sopenharmony_ci
266362306a36Sopenharmony_ci	/* Setup checksum offloading, if needed */
266462306a36Sopenharmony_ci	cksum_offset = 0;
266562306a36Sopenharmony_ci	pseudo_hdr_offset = 0;
266662306a36Sopenharmony_ci	odd_flag = 0;
266762306a36Sopenharmony_ci	flags = (MXGEFW_FLAGS_NO_TSO | MXGEFW_FLAGS_FIRST);
266862306a36Sopenharmony_ci	if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) {
266962306a36Sopenharmony_ci		cksum_offset = skb_checksum_start_offset(skb);
267062306a36Sopenharmony_ci		pseudo_hdr_offset = cksum_offset + skb->csum_offset;
267162306a36Sopenharmony_ci		/* If the headers are excessively large, then we must
267262306a36Sopenharmony_ci		 * fall back to a software checksum */
267362306a36Sopenharmony_ci		if (unlikely(!mss && (cksum_offset > 255 ||
267462306a36Sopenharmony_ci				      pseudo_hdr_offset > 127))) {
267562306a36Sopenharmony_ci			if (skb_checksum_help(skb))
267662306a36Sopenharmony_ci				goto drop;
267762306a36Sopenharmony_ci			cksum_offset = 0;
267862306a36Sopenharmony_ci			pseudo_hdr_offset = 0;
267962306a36Sopenharmony_ci		} else {
268062306a36Sopenharmony_ci			odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
268162306a36Sopenharmony_ci			flags |= MXGEFW_FLAGS_CKSUM;
268262306a36Sopenharmony_ci		}
268362306a36Sopenharmony_ci	}
268462306a36Sopenharmony_ci
268562306a36Sopenharmony_ci	cum_len = 0;
268662306a36Sopenharmony_ci
268762306a36Sopenharmony_ci	if (mss) {		/* TSO */
268862306a36Sopenharmony_ci		/* this removes any CKSUM flag from before */
268962306a36Sopenharmony_ci		flags = (MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST);
269062306a36Sopenharmony_ci
269162306a36Sopenharmony_ci		/* negative cum_len signifies to the
269262306a36Sopenharmony_ci		 * send loop that we are still in the
269362306a36Sopenharmony_ci		 * header portion of the TSO packet.
269462306a36Sopenharmony_ci		 * TSO header can be at most 1KB long */
269562306a36Sopenharmony_ci		cum_len = -skb_tcp_all_headers(skb);
269662306a36Sopenharmony_ci
269762306a36Sopenharmony_ci		/* for IPv6 TSO, the checksum offset stores the
269862306a36Sopenharmony_ci		 * TCP header length, to save the firmware from
269962306a36Sopenharmony_ci		 * the need to parse the headers */
270062306a36Sopenharmony_ci		if (skb_is_gso_v6(skb)) {
270162306a36Sopenharmony_ci			cksum_offset = tcp_hdrlen(skb);
270262306a36Sopenharmony_ci			/* Can only handle headers <= max_tso6 long */
270362306a36Sopenharmony_ci			if (unlikely(-cum_len > mgp->max_tso6))
270462306a36Sopenharmony_ci				return myri10ge_sw_tso(skb, dev);
270562306a36Sopenharmony_ci		}
270662306a36Sopenharmony_ci		/* for TSO, pseudo_hdr_offset holds mss.
270762306a36Sopenharmony_ci		 * The firmware figures out where to put
270862306a36Sopenharmony_ci		 * the checksum by parsing the header. */
270962306a36Sopenharmony_ci		pseudo_hdr_offset = mss;
271062306a36Sopenharmony_ci	} else
271162306a36Sopenharmony_ci		/* Mark small packets, and pad out tiny packets */
271262306a36Sopenharmony_ci	if (skb->len <= MXGEFW_SEND_SMALL_SIZE) {
271362306a36Sopenharmony_ci		flags |= MXGEFW_FLAGS_SMALL;
271462306a36Sopenharmony_ci
271562306a36Sopenharmony_ci		/* pad frames to at least ETH_ZLEN bytes */
271662306a36Sopenharmony_ci		if (eth_skb_pad(skb)) {
271762306a36Sopenharmony_ci			/* The packet is gone, so we must
271862306a36Sopenharmony_ci			 * return 0 */
271962306a36Sopenharmony_ci			ss->stats.tx_dropped += 1;
272062306a36Sopenharmony_ci			return NETDEV_TX_OK;
272162306a36Sopenharmony_ci		}
272262306a36Sopenharmony_ci	}
272362306a36Sopenharmony_ci
272462306a36Sopenharmony_ci	/* map the skb for DMA */
272562306a36Sopenharmony_ci	len = skb_headlen(skb);
272662306a36Sopenharmony_ci	bus = dma_map_single(&mgp->pdev->dev, skb->data, len, DMA_TO_DEVICE);
272762306a36Sopenharmony_ci	if (unlikely(dma_mapping_error(&mgp->pdev->dev, bus)))
272862306a36Sopenharmony_ci		goto drop;
272962306a36Sopenharmony_ci
273062306a36Sopenharmony_ci	idx = tx->req & tx->mask;
273162306a36Sopenharmony_ci	tx->info[idx].skb = skb;
273262306a36Sopenharmony_ci	dma_unmap_addr_set(&tx->info[idx], bus, bus);
273362306a36Sopenharmony_ci	dma_unmap_len_set(&tx->info[idx], len, len);
273462306a36Sopenharmony_ci
273562306a36Sopenharmony_ci	frag_cnt = skb_shinfo(skb)->nr_frags;
273662306a36Sopenharmony_ci	frag_idx = 0;
273762306a36Sopenharmony_ci	count = 0;
273862306a36Sopenharmony_ci	rdma_count = 0;
273962306a36Sopenharmony_ci
274062306a36Sopenharmony_ci	/* "rdma_count" is the number of RDMAs belonging to the
274162306a36Sopenharmony_ci	 * current packet BEFORE the current send request. For
274262306a36Sopenharmony_ci	 * non-TSO packets, this is equal to "count".
274362306a36Sopenharmony_ci	 * For TSO packets, rdma_count needs to be reset
274462306a36Sopenharmony_ci	 * to 0 after a segment cut.
274562306a36Sopenharmony_ci	 *
274662306a36Sopenharmony_ci	 * The rdma_count field of the send request is
274762306a36Sopenharmony_ci	 * the number of RDMAs of the packet starting at
274862306a36Sopenharmony_ci	 * that request. For TSO send requests with one ore more cuts
274962306a36Sopenharmony_ci	 * in the middle, this is the number of RDMAs starting
275062306a36Sopenharmony_ci	 * after the last cut in the request. All previous
275162306a36Sopenharmony_ci	 * segments before the last cut implicitly have 1 RDMA.
275262306a36Sopenharmony_ci	 *
275362306a36Sopenharmony_ci	 * Since the number of RDMAs is not known beforehand,
275462306a36Sopenharmony_ci	 * it must be filled-in retroactively - after each
275562306a36Sopenharmony_ci	 * segmentation cut or at the end of the entire packet.
275662306a36Sopenharmony_ci	 */
275762306a36Sopenharmony_ci
275862306a36Sopenharmony_ci	while (1) {
275962306a36Sopenharmony_ci		/* Break the SKB or Fragment up into pieces which
276062306a36Sopenharmony_ci		 * do not cross mgp->tx_boundary */
276162306a36Sopenharmony_ci		low = MYRI10GE_LOWPART_TO_U32(bus);
276262306a36Sopenharmony_ci		high_swapped = htonl(MYRI10GE_HIGHPART_TO_U32(bus));
276362306a36Sopenharmony_ci		while (len) {
276462306a36Sopenharmony_ci			u8 flags_next;
276562306a36Sopenharmony_ci			int cum_len_next;
276662306a36Sopenharmony_ci
276762306a36Sopenharmony_ci			if (unlikely(count == max_segments))
276862306a36Sopenharmony_ci				goto abort_linearize;
276962306a36Sopenharmony_ci
277062306a36Sopenharmony_ci			boundary =
277162306a36Sopenharmony_ci			    (low + mgp->tx_boundary) & ~(mgp->tx_boundary - 1);
277262306a36Sopenharmony_ci			seglen = boundary - low;
277362306a36Sopenharmony_ci			if (seglen > len)
277462306a36Sopenharmony_ci				seglen = len;
277562306a36Sopenharmony_ci			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
277662306a36Sopenharmony_ci			cum_len_next = cum_len + seglen;
277762306a36Sopenharmony_ci			if (mss) {	/* TSO */
277862306a36Sopenharmony_ci				(req - rdma_count)->rdma_count = rdma_count + 1;
277962306a36Sopenharmony_ci
278062306a36Sopenharmony_ci				if (likely(cum_len >= 0)) {	/* payload */
278162306a36Sopenharmony_ci					int next_is_first, chop;
278262306a36Sopenharmony_ci
278362306a36Sopenharmony_ci					chop = (cum_len_next > mss);
278462306a36Sopenharmony_ci					cum_len_next = cum_len_next % mss;
278562306a36Sopenharmony_ci					next_is_first = (cum_len_next == 0);
278662306a36Sopenharmony_ci					flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
278762306a36Sopenharmony_ci					flags_next |= next_is_first *
278862306a36Sopenharmony_ci					    MXGEFW_FLAGS_FIRST;
278962306a36Sopenharmony_ci					rdma_count |= -(chop | next_is_first);
279062306a36Sopenharmony_ci					rdma_count += chop & ~next_is_first;
279162306a36Sopenharmony_ci				} else if (likely(cum_len_next >= 0)) {	/* header ends */
279262306a36Sopenharmony_ci					int small;
279362306a36Sopenharmony_ci
279462306a36Sopenharmony_ci					rdma_count = -1;
279562306a36Sopenharmony_ci					cum_len_next = 0;
279662306a36Sopenharmony_ci					seglen = -cum_len;
279762306a36Sopenharmony_ci					small = (mss <= MXGEFW_SEND_SMALL_SIZE);
279862306a36Sopenharmony_ci					flags_next = MXGEFW_FLAGS_TSO_PLD |
279962306a36Sopenharmony_ci					    MXGEFW_FLAGS_FIRST |
280062306a36Sopenharmony_ci					    (small * MXGEFW_FLAGS_SMALL);
280162306a36Sopenharmony_ci				}
280262306a36Sopenharmony_ci			}
280362306a36Sopenharmony_ci			req->addr_high = high_swapped;
280462306a36Sopenharmony_ci			req->addr_low = htonl(low);
280562306a36Sopenharmony_ci			req->pseudo_hdr_offset = htons(pseudo_hdr_offset);
280662306a36Sopenharmony_ci			req->pad = 0;	/* complete solid 16-byte block; does this matter? */
280762306a36Sopenharmony_ci			req->rdma_count = 1;
280862306a36Sopenharmony_ci			req->length = htons(seglen);
280962306a36Sopenharmony_ci			req->cksum_offset = cksum_offset;
281062306a36Sopenharmony_ci			req->flags = flags | ((cum_len & 1) * odd_flag);
281162306a36Sopenharmony_ci
281262306a36Sopenharmony_ci			low += seglen;
281362306a36Sopenharmony_ci			len -= seglen;
281462306a36Sopenharmony_ci			cum_len = cum_len_next;
281562306a36Sopenharmony_ci			flags = flags_next;
281662306a36Sopenharmony_ci			req++;
281762306a36Sopenharmony_ci			count++;
281862306a36Sopenharmony_ci			rdma_count++;
281962306a36Sopenharmony_ci			if (cksum_offset != 0 && !(mss && skb_is_gso_v6(skb))) {
282062306a36Sopenharmony_ci				if (unlikely(cksum_offset > seglen))
282162306a36Sopenharmony_ci					cksum_offset -= seglen;
282262306a36Sopenharmony_ci				else
282362306a36Sopenharmony_ci					cksum_offset = 0;
282462306a36Sopenharmony_ci			}
282562306a36Sopenharmony_ci		}
282662306a36Sopenharmony_ci		if (frag_idx == frag_cnt)
282762306a36Sopenharmony_ci			break;
282862306a36Sopenharmony_ci
282962306a36Sopenharmony_ci		/* map next fragment for DMA */
283062306a36Sopenharmony_ci		frag = &skb_shinfo(skb)->frags[frag_idx];
283162306a36Sopenharmony_ci		frag_idx++;
283262306a36Sopenharmony_ci		len = skb_frag_size(frag);
283362306a36Sopenharmony_ci		bus = skb_frag_dma_map(&mgp->pdev->dev, frag, 0, len,
283462306a36Sopenharmony_ci				       DMA_TO_DEVICE);
283562306a36Sopenharmony_ci		if (unlikely(dma_mapping_error(&mgp->pdev->dev, bus))) {
283662306a36Sopenharmony_ci			myri10ge_unmap_tx_dma(mgp, tx, idx);
283762306a36Sopenharmony_ci			goto drop;
283862306a36Sopenharmony_ci		}
283962306a36Sopenharmony_ci		idx = (count + tx->req) & tx->mask;
284062306a36Sopenharmony_ci		dma_unmap_addr_set(&tx->info[idx], bus, bus);
284162306a36Sopenharmony_ci		dma_unmap_len_set(&tx->info[idx], len, len);
284262306a36Sopenharmony_ci	}
284362306a36Sopenharmony_ci
284462306a36Sopenharmony_ci	(req - rdma_count)->rdma_count = rdma_count;
284562306a36Sopenharmony_ci	if (mss)
284662306a36Sopenharmony_ci		do {
284762306a36Sopenharmony_ci			req--;
284862306a36Sopenharmony_ci			req->flags |= MXGEFW_FLAGS_TSO_LAST;
284962306a36Sopenharmony_ci		} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP |
285062306a36Sopenharmony_ci					 MXGEFW_FLAGS_FIRST)));
285162306a36Sopenharmony_ci	idx = ((count - 1) + tx->req) & tx->mask;
285262306a36Sopenharmony_ci	tx->info[idx].last = 1;
285362306a36Sopenharmony_ci	myri10ge_submit_req(tx, tx->req_list, count);
285462306a36Sopenharmony_ci	/* if using multiple tx queues, make sure NIC polls the
285562306a36Sopenharmony_ci	 * current slice */
285662306a36Sopenharmony_ci	if ((mgp->dev->real_num_tx_queues > 1) && tx->queue_active == 0) {
285762306a36Sopenharmony_ci		tx->queue_active = 1;
285862306a36Sopenharmony_ci		put_be32(htonl(1), tx->send_go);
285962306a36Sopenharmony_ci		mb();
286062306a36Sopenharmony_ci	}
286162306a36Sopenharmony_ci	tx->pkt_start++;
286262306a36Sopenharmony_ci	if ((avail - count) < MXGEFW_MAX_SEND_DESC) {
286362306a36Sopenharmony_ci		tx->stop_queue++;
286462306a36Sopenharmony_ci		netif_tx_stop_queue(netdev_queue);
286562306a36Sopenharmony_ci	}
286662306a36Sopenharmony_ci	return NETDEV_TX_OK;
286762306a36Sopenharmony_ci
286862306a36Sopenharmony_ciabort_linearize:
286962306a36Sopenharmony_ci	myri10ge_unmap_tx_dma(mgp, tx, idx);
287062306a36Sopenharmony_ci
287162306a36Sopenharmony_ci	if (skb_is_gso(skb)) {
287262306a36Sopenharmony_ci		netdev_err(mgp->dev, "TSO but wanted to linearize?!?!?\n");
287362306a36Sopenharmony_ci		goto drop;
287462306a36Sopenharmony_ci	}
287562306a36Sopenharmony_ci
287662306a36Sopenharmony_ci	if (skb_linearize(skb))
287762306a36Sopenharmony_ci		goto drop;
287862306a36Sopenharmony_ci
287962306a36Sopenharmony_ci	tx->linearized++;
288062306a36Sopenharmony_ci	goto again;
288162306a36Sopenharmony_ci
288262306a36Sopenharmony_cidrop:
288362306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
288462306a36Sopenharmony_ci	ss->stats.tx_dropped += 1;
288562306a36Sopenharmony_ci	return NETDEV_TX_OK;
288662306a36Sopenharmony_ci
288762306a36Sopenharmony_ci}
288862306a36Sopenharmony_ci
288962306a36Sopenharmony_cistatic netdev_tx_t myri10ge_sw_tso(struct sk_buff *skb,
289062306a36Sopenharmony_ci					 struct net_device *dev)
289162306a36Sopenharmony_ci{
289262306a36Sopenharmony_ci	struct sk_buff *segs, *curr, *next;
289362306a36Sopenharmony_ci	struct myri10ge_priv *mgp = netdev_priv(dev);
289462306a36Sopenharmony_ci	struct myri10ge_slice_state *ss;
289562306a36Sopenharmony_ci	netdev_tx_t status;
289662306a36Sopenharmony_ci
289762306a36Sopenharmony_ci	segs = skb_gso_segment(skb, dev->features & ~NETIF_F_TSO6);
289862306a36Sopenharmony_ci	if (IS_ERR(segs))
289962306a36Sopenharmony_ci		goto drop;
290062306a36Sopenharmony_ci
290162306a36Sopenharmony_ci	skb_list_walk_safe(segs, curr, next) {
290262306a36Sopenharmony_ci		skb_mark_not_on_list(curr);
290362306a36Sopenharmony_ci		status = myri10ge_xmit(curr, dev);
290462306a36Sopenharmony_ci		if (status != 0) {
290562306a36Sopenharmony_ci			dev_kfree_skb_any(curr);
290662306a36Sopenharmony_ci			skb_list_walk_safe(next, curr, next) {
290762306a36Sopenharmony_ci				curr->next = NULL;
290862306a36Sopenharmony_ci				dev_kfree_skb_any(curr);
290962306a36Sopenharmony_ci			}
291062306a36Sopenharmony_ci			goto drop;
291162306a36Sopenharmony_ci		}
291262306a36Sopenharmony_ci	}
291362306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
291462306a36Sopenharmony_ci	return NETDEV_TX_OK;
291562306a36Sopenharmony_ci
291662306a36Sopenharmony_cidrop:
291762306a36Sopenharmony_ci	ss = &mgp->ss[skb_get_queue_mapping(skb)];
291862306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
291962306a36Sopenharmony_ci	ss->stats.tx_dropped += 1;
292062306a36Sopenharmony_ci	return NETDEV_TX_OK;
292162306a36Sopenharmony_ci}
292262306a36Sopenharmony_ci
292362306a36Sopenharmony_cistatic void myri10ge_get_stats(struct net_device *dev,
292462306a36Sopenharmony_ci			       struct rtnl_link_stats64 *stats)
292562306a36Sopenharmony_ci{
292662306a36Sopenharmony_ci	const struct myri10ge_priv *mgp = netdev_priv(dev);
292762306a36Sopenharmony_ci	const struct myri10ge_slice_netstats *slice_stats;
292862306a36Sopenharmony_ci	int i;
292962306a36Sopenharmony_ci
293062306a36Sopenharmony_ci	for (i = 0; i < mgp->num_slices; i++) {
293162306a36Sopenharmony_ci		slice_stats = &mgp->ss[i].stats;
293262306a36Sopenharmony_ci		stats->rx_packets += slice_stats->rx_packets;
293362306a36Sopenharmony_ci		stats->tx_packets += slice_stats->tx_packets;
293462306a36Sopenharmony_ci		stats->rx_bytes += slice_stats->rx_bytes;
293562306a36Sopenharmony_ci		stats->tx_bytes += slice_stats->tx_bytes;
293662306a36Sopenharmony_ci		stats->rx_dropped += slice_stats->rx_dropped;
293762306a36Sopenharmony_ci		stats->tx_dropped += slice_stats->tx_dropped;
293862306a36Sopenharmony_ci	}
293962306a36Sopenharmony_ci}
294062306a36Sopenharmony_ci
294162306a36Sopenharmony_cistatic void myri10ge_set_multicast_list(struct net_device *dev)
294262306a36Sopenharmony_ci{
294362306a36Sopenharmony_ci	struct myri10ge_priv *mgp = netdev_priv(dev);
294462306a36Sopenharmony_ci	struct myri10ge_cmd cmd;
294562306a36Sopenharmony_ci	struct netdev_hw_addr *ha;
294662306a36Sopenharmony_ci	__be32 data[2] = { 0, 0 };
294762306a36Sopenharmony_ci	int err;
294862306a36Sopenharmony_ci
294962306a36Sopenharmony_ci	/* can be called from atomic contexts,
295062306a36Sopenharmony_ci	 * pass 1 to force atomicity in myri10ge_send_cmd() */
295162306a36Sopenharmony_ci	myri10ge_change_promisc(mgp, dev->flags & IFF_PROMISC, 1);
295262306a36Sopenharmony_ci
295362306a36Sopenharmony_ci	/* This firmware is known to not support multicast */
295462306a36Sopenharmony_ci	if (!mgp->fw_multicast_support)
295562306a36Sopenharmony_ci		return;
295662306a36Sopenharmony_ci
295762306a36Sopenharmony_ci	/* Disable multicast filtering */
295862306a36Sopenharmony_ci
295962306a36Sopenharmony_ci	err = myri10ge_send_cmd(mgp, MXGEFW_ENABLE_ALLMULTI, &cmd, 1);
296062306a36Sopenharmony_ci	if (err != 0) {
296162306a36Sopenharmony_ci		netdev_err(dev, "Failed MXGEFW_ENABLE_ALLMULTI, error status: %d\n",
296262306a36Sopenharmony_ci			   err);
296362306a36Sopenharmony_ci		goto abort;
296462306a36Sopenharmony_ci	}
296562306a36Sopenharmony_ci
296662306a36Sopenharmony_ci	if ((dev->flags & IFF_ALLMULTI) || mgp->adopted_rx_filter_bug) {
296762306a36Sopenharmony_ci		/* request to disable multicast filtering, so quit here */
296862306a36Sopenharmony_ci		return;
296962306a36Sopenharmony_ci	}
297062306a36Sopenharmony_ci
297162306a36Sopenharmony_ci	/* Flush the filters */
297262306a36Sopenharmony_ci
297362306a36Sopenharmony_ci	err = myri10ge_send_cmd(mgp, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS,
297462306a36Sopenharmony_ci				&cmd, 1);
297562306a36Sopenharmony_ci	if (err != 0) {
297662306a36Sopenharmony_ci		netdev_err(dev, "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, error status: %d\n",
297762306a36Sopenharmony_ci			   err);
297862306a36Sopenharmony_ci		goto abort;
297962306a36Sopenharmony_ci	}
298062306a36Sopenharmony_ci
298162306a36Sopenharmony_ci	/* Walk the multicast list, and add each address */
298262306a36Sopenharmony_ci	netdev_for_each_mc_addr(ha, dev) {
298362306a36Sopenharmony_ci		memcpy(data, &ha->addr, ETH_ALEN);
298462306a36Sopenharmony_ci		cmd.data0 = ntohl(data[0]);
298562306a36Sopenharmony_ci		cmd.data1 = ntohl(data[1]);
298662306a36Sopenharmony_ci		err = myri10ge_send_cmd(mgp, MXGEFW_JOIN_MULTICAST_GROUP,
298762306a36Sopenharmony_ci					&cmd, 1);
298862306a36Sopenharmony_ci
298962306a36Sopenharmony_ci		if (err != 0) {
299062306a36Sopenharmony_ci			netdev_err(dev, "Failed MXGEFW_JOIN_MULTICAST_GROUP, error status:%d %pM\n",
299162306a36Sopenharmony_ci				   err, ha->addr);
299262306a36Sopenharmony_ci			goto abort;
299362306a36Sopenharmony_ci		}
299462306a36Sopenharmony_ci	}
299562306a36Sopenharmony_ci	/* Enable multicast filtering */
299662306a36Sopenharmony_ci	err = myri10ge_send_cmd(mgp, MXGEFW_DISABLE_ALLMULTI, &cmd, 1);
299762306a36Sopenharmony_ci	if (err != 0) {
299862306a36Sopenharmony_ci		netdev_err(dev, "Failed MXGEFW_DISABLE_ALLMULTI, error status: %d\n",
299962306a36Sopenharmony_ci			   err);
300062306a36Sopenharmony_ci		goto abort;
300162306a36Sopenharmony_ci	}
300262306a36Sopenharmony_ci
300362306a36Sopenharmony_ci	return;
300462306a36Sopenharmony_ci
300562306a36Sopenharmony_ciabort:
300662306a36Sopenharmony_ci	return;
300762306a36Sopenharmony_ci}
300862306a36Sopenharmony_ci
300962306a36Sopenharmony_cistatic int myri10ge_set_mac_address(struct net_device *dev, void *addr)
301062306a36Sopenharmony_ci{
301162306a36Sopenharmony_ci	struct sockaddr *sa = addr;
301262306a36Sopenharmony_ci	struct myri10ge_priv *mgp = netdev_priv(dev);
301362306a36Sopenharmony_ci	int status;
301462306a36Sopenharmony_ci
301562306a36Sopenharmony_ci	if (!is_valid_ether_addr(sa->sa_data))
301662306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
301762306a36Sopenharmony_ci
301862306a36Sopenharmony_ci	status = myri10ge_update_mac_address(mgp, sa->sa_data);
301962306a36Sopenharmony_ci	if (status != 0) {
302062306a36Sopenharmony_ci		netdev_err(dev, "changing mac address failed with %d\n",
302162306a36Sopenharmony_ci			   status);
302262306a36Sopenharmony_ci		return status;
302362306a36Sopenharmony_ci	}
302462306a36Sopenharmony_ci
302562306a36Sopenharmony_ci	/* change the dev structure */
302662306a36Sopenharmony_ci	eth_hw_addr_set(dev, sa->sa_data);
302762306a36Sopenharmony_ci	return 0;
302862306a36Sopenharmony_ci}
302962306a36Sopenharmony_ci
303062306a36Sopenharmony_cistatic int myri10ge_change_mtu(struct net_device *dev, int new_mtu)
303162306a36Sopenharmony_ci{
303262306a36Sopenharmony_ci	struct myri10ge_priv *mgp = netdev_priv(dev);
303362306a36Sopenharmony_ci
303462306a36Sopenharmony_ci	netdev_info(dev, "changing mtu from %d to %d\n", dev->mtu, new_mtu);
303562306a36Sopenharmony_ci	if (mgp->running) {
303662306a36Sopenharmony_ci		/* if we change the mtu on an active device, we must
303762306a36Sopenharmony_ci		 * reset the device so the firmware sees the change */
303862306a36Sopenharmony_ci		myri10ge_close(dev);
303962306a36Sopenharmony_ci		dev->mtu = new_mtu;
304062306a36Sopenharmony_ci		myri10ge_open(dev);
304162306a36Sopenharmony_ci	} else
304262306a36Sopenharmony_ci		dev->mtu = new_mtu;
304362306a36Sopenharmony_ci
304462306a36Sopenharmony_ci	return 0;
304562306a36Sopenharmony_ci}
304662306a36Sopenharmony_ci
304762306a36Sopenharmony_ci/*
304862306a36Sopenharmony_ci * Enable ECRC to align PCI-E Completion packets on an 8-byte boundary.
304962306a36Sopenharmony_ci * Only do it if the bridge is a root port since we don't want to disturb
305062306a36Sopenharmony_ci * any other device, except if forced with myri10ge_ecrc_enable > 1.
305162306a36Sopenharmony_ci */
305262306a36Sopenharmony_ci
305362306a36Sopenharmony_cistatic void myri10ge_enable_ecrc(struct myri10ge_priv *mgp)
305462306a36Sopenharmony_ci{
305562306a36Sopenharmony_ci	struct pci_dev *bridge = mgp->pdev->bus->self;
305662306a36Sopenharmony_ci	struct device *dev = &mgp->pdev->dev;
305762306a36Sopenharmony_ci	int cap;
305862306a36Sopenharmony_ci	unsigned err_cap;
305962306a36Sopenharmony_ci	int ret;
306062306a36Sopenharmony_ci
306162306a36Sopenharmony_ci	if (!myri10ge_ecrc_enable || !bridge)
306262306a36Sopenharmony_ci		return;
306362306a36Sopenharmony_ci
306462306a36Sopenharmony_ci	/* check that the bridge is a root port */
306562306a36Sopenharmony_ci	if (pci_pcie_type(bridge) != PCI_EXP_TYPE_ROOT_PORT) {
306662306a36Sopenharmony_ci		if (myri10ge_ecrc_enable > 1) {
306762306a36Sopenharmony_ci			struct pci_dev *prev_bridge, *old_bridge = bridge;
306862306a36Sopenharmony_ci
306962306a36Sopenharmony_ci			/* Walk the hierarchy up to the root port
307062306a36Sopenharmony_ci			 * where ECRC has to be enabled */
307162306a36Sopenharmony_ci			do {
307262306a36Sopenharmony_ci				prev_bridge = bridge;
307362306a36Sopenharmony_ci				bridge = bridge->bus->self;
307462306a36Sopenharmony_ci				if (!bridge || prev_bridge == bridge) {
307562306a36Sopenharmony_ci					dev_err(dev,
307662306a36Sopenharmony_ci						"Failed to find root port"
307762306a36Sopenharmony_ci						" to force ECRC\n");
307862306a36Sopenharmony_ci					return;
307962306a36Sopenharmony_ci				}
308062306a36Sopenharmony_ci			} while (pci_pcie_type(bridge) !=
308162306a36Sopenharmony_ci				 PCI_EXP_TYPE_ROOT_PORT);
308262306a36Sopenharmony_ci
308362306a36Sopenharmony_ci			dev_info(dev,
308462306a36Sopenharmony_ci				 "Forcing ECRC on non-root port %s"
308562306a36Sopenharmony_ci				 " (enabling on root port %s)\n",
308662306a36Sopenharmony_ci				 pci_name(old_bridge), pci_name(bridge));
308762306a36Sopenharmony_ci		} else {
308862306a36Sopenharmony_ci			dev_err(dev,
308962306a36Sopenharmony_ci				"Not enabling ECRC on non-root port %s\n",
309062306a36Sopenharmony_ci				pci_name(bridge));
309162306a36Sopenharmony_ci			return;
309262306a36Sopenharmony_ci		}
309362306a36Sopenharmony_ci	}
309462306a36Sopenharmony_ci
309562306a36Sopenharmony_ci	cap = pci_find_ext_capability(bridge, PCI_EXT_CAP_ID_ERR);
309662306a36Sopenharmony_ci	if (!cap)
309762306a36Sopenharmony_ci		return;
309862306a36Sopenharmony_ci
309962306a36Sopenharmony_ci	ret = pci_read_config_dword(bridge, cap + PCI_ERR_CAP, &err_cap);
310062306a36Sopenharmony_ci	if (ret) {
310162306a36Sopenharmony_ci		dev_err(dev, "failed reading ext-conf-space of %s\n",
310262306a36Sopenharmony_ci			pci_name(bridge));
310362306a36Sopenharmony_ci		dev_err(dev, "\t pci=nommconf in use? "
310462306a36Sopenharmony_ci			"or buggy/incomplete/absent ACPI MCFG attr?\n");
310562306a36Sopenharmony_ci		return;
310662306a36Sopenharmony_ci	}
310762306a36Sopenharmony_ci	if (!(err_cap & PCI_ERR_CAP_ECRC_GENC))
310862306a36Sopenharmony_ci		return;
310962306a36Sopenharmony_ci
311062306a36Sopenharmony_ci	err_cap |= PCI_ERR_CAP_ECRC_GENE;
311162306a36Sopenharmony_ci	pci_write_config_dword(bridge, cap + PCI_ERR_CAP, err_cap);
311262306a36Sopenharmony_ci	dev_info(dev, "Enabled ECRC on upstream bridge %s\n", pci_name(bridge));
311362306a36Sopenharmony_ci}
311462306a36Sopenharmony_ci
311562306a36Sopenharmony_ci/*
311662306a36Sopenharmony_ci * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
311762306a36Sopenharmony_ci * when the PCI-E Completion packets are aligned on an 8-byte
311862306a36Sopenharmony_ci * boundary.  Some PCI-E chip sets always align Completion packets; on
311962306a36Sopenharmony_ci * the ones that do not, the alignment can be enforced by enabling
312062306a36Sopenharmony_ci * ECRC generation (if supported).
312162306a36Sopenharmony_ci *
312262306a36Sopenharmony_ci * When PCI-E Completion packets are not aligned, it is actually more
312362306a36Sopenharmony_ci * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
312462306a36Sopenharmony_ci *
312562306a36Sopenharmony_ci * If the driver can neither enable ECRC nor verify that it has
312662306a36Sopenharmony_ci * already been enabled, then it must use a firmware image which works
312762306a36Sopenharmony_ci * around unaligned completion packets (myri10ge_rss_ethp_z8e.dat), and it
312862306a36Sopenharmony_ci * should also ensure that it never gives the device a Read-DMA which is
312962306a36Sopenharmony_ci * larger than 2KB by setting the tx_boundary to 2KB.  If ECRC is
313062306a36Sopenharmony_ci * enabled, then the driver should use the aligned (myri10ge_rss_eth_z8e.dat)
313162306a36Sopenharmony_ci * firmware image, and set tx_boundary to 4KB.
313262306a36Sopenharmony_ci */
313362306a36Sopenharmony_ci
313462306a36Sopenharmony_cistatic void myri10ge_firmware_probe(struct myri10ge_priv *mgp)
313562306a36Sopenharmony_ci{
313662306a36Sopenharmony_ci	struct pci_dev *pdev = mgp->pdev;
313762306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
313862306a36Sopenharmony_ci	int status;
313962306a36Sopenharmony_ci
314062306a36Sopenharmony_ci	mgp->tx_boundary = 4096;
314162306a36Sopenharmony_ci	/*
314262306a36Sopenharmony_ci	 * Verify the max read request size was set to 4KB
314362306a36Sopenharmony_ci	 * before trying the test with 4KB.
314462306a36Sopenharmony_ci	 */
314562306a36Sopenharmony_ci	status = pcie_get_readrq(pdev);
314662306a36Sopenharmony_ci	if (status < 0) {
314762306a36Sopenharmony_ci		dev_err(dev, "Couldn't read max read req size: %d\n", status);
314862306a36Sopenharmony_ci		goto abort;
314962306a36Sopenharmony_ci	}
315062306a36Sopenharmony_ci	if (status != 4096) {
315162306a36Sopenharmony_ci		dev_warn(dev, "Max Read Request size != 4096 (%d)\n", status);
315262306a36Sopenharmony_ci		mgp->tx_boundary = 2048;
315362306a36Sopenharmony_ci	}
315462306a36Sopenharmony_ci	/*
315562306a36Sopenharmony_ci	 * load the optimized firmware (which assumes aligned PCIe
315662306a36Sopenharmony_ci	 * completions) in order to see if it works on this host.
315762306a36Sopenharmony_ci	 */
315862306a36Sopenharmony_ci	set_fw_name(mgp, myri10ge_fw_aligned, false);
315962306a36Sopenharmony_ci	status = myri10ge_load_firmware(mgp, 1);
316062306a36Sopenharmony_ci	if (status != 0) {
316162306a36Sopenharmony_ci		goto abort;
316262306a36Sopenharmony_ci	}
316362306a36Sopenharmony_ci
316462306a36Sopenharmony_ci	/*
316562306a36Sopenharmony_ci	 * Enable ECRC if possible
316662306a36Sopenharmony_ci	 */
316762306a36Sopenharmony_ci	myri10ge_enable_ecrc(mgp);
316862306a36Sopenharmony_ci
316962306a36Sopenharmony_ci	/*
317062306a36Sopenharmony_ci	 * Run a DMA test which watches for unaligned completions and
317162306a36Sopenharmony_ci	 * aborts on the first one seen.
317262306a36Sopenharmony_ci	 */
317362306a36Sopenharmony_ci
317462306a36Sopenharmony_ci	status = myri10ge_dma_test(mgp, MXGEFW_CMD_UNALIGNED_TEST);
317562306a36Sopenharmony_ci	if (status == 0)
317662306a36Sopenharmony_ci		return;		/* keep the aligned firmware */
317762306a36Sopenharmony_ci
317862306a36Sopenharmony_ci	if (status != -E2BIG)
317962306a36Sopenharmony_ci		dev_warn(dev, "DMA test failed: %d\n", status);
318062306a36Sopenharmony_ci	if (status == -ENOSYS)
318162306a36Sopenharmony_ci		dev_warn(dev, "Falling back to ethp! "
318262306a36Sopenharmony_ci			 "Please install up to date fw\n");
318362306a36Sopenharmony_ciabort:
318462306a36Sopenharmony_ci	/* fall back to using the unaligned firmware */
318562306a36Sopenharmony_ci	mgp->tx_boundary = 2048;
318662306a36Sopenharmony_ci	set_fw_name(mgp, myri10ge_fw_unaligned, false);
318762306a36Sopenharmony_ci}
318862306a36Sopenharmony_ci
318962306a36Sopenharmony_cistatic void myri10ge_select_firmware(struct myri10ge_priv *mgp)
319062306a36Sopenharmony_ci{
319162306a36Sopenharmony_ci	int overridden = 0;
319262306a36Sopenharmony_ci
319362306a36Sopenharmony_ci	if (myri10ge_force_firmware == 0) {
319462306a36Sopenharmony_ci		int link_width;
319562306a36Sopenharmony_ci		u16 lnk;
319662306a36Sopenharmony_ci
319762306a36Sopenharmony_ci		pcie_capability_read_word(mgp->pdev, PCI_EXP_LNKSTA, &lnk);
319862306a36Sopenharmony_ci		link_width = (lnk >> 4) & 0x3f;
319962306a36Sopenharmony_ci
320062306a36Sopenharmony_ci		/* Check to see if Link is less than 8 or if the
320162306a36Sopenharmony_ci		 * upstream bridge is known to provide aligned
320262306a36Sopenharmony_ci		 * completions */
320362306a36Sopenharmony_ci		if (link_width < 8) {
320462306a36Sopenharmony_ci			dev_info(&mgp->pdev->dev, "PCIE x%d Link\n",
320562306a36Sopenharmony_ci				 link_width);
320662306a36Sopenharmony_ci			mgp->tx_boundary = 4096;
320762306a36Sopenharmony_ci			set_fw_name(mgp, myri10ge_fw_aligned, false);
320862306a36Sopenharmony_ci		} else {
320962306a36Sopenharmony_ci			myri10ge_firmware_probe(mgp);
321062306a36Sopenharmony_ci		}
321162306a36Sopenharmony_ci	} else {
321262306a36Sopenharmony_ci		if (myri10ge_force_firmware == 1) {
321362306a36Sopenharmony_ci			dev_info(&mgp->pdev->dev,
321462306a36Sopenharmony_ci				 "Assuming aligned completions (forced)\n");
321562306a36Sopenharmony_ci			mgp->tx_boundary = 4096;
321662306a36Sopenharmony_ci			set_fw_name(mgp, myri10ge_fw_aligned, false);
321762306a36Sopenharmony_ci		} else {
321862306a36Sopenharmony_ci			dev_info(&mgp->pdev->dev,
321962306a36Sopenharmony_ci				 "Assuming unaligned completions (forced)\n");
322062306a36Sopenharmony_ci			mgp->tx_boundary = 2048;
322162306a36Sopenharmony_ci			set_fw_name(mgp, myri10ge_fw_unaligned, false);
322262306a36Sopenharmony_ci		}
322362306a36Sopenharmony_ci	}
322462306a36Sopenharmony_ci
322562306a36Sopenharmony_ci	kernel_param_lock(THIS_MODULE);
322662306a36Sopenharmony_ci	if (myri10ge_fw_name != NULL) {
322762306a36Sopenharmony_ci		char *fw_name = kstrdup(myri10ge_fw_name, GFP_KERNEL);
322862306a36Sopenharmony_ci		if (fw_name) {
322962306a36Sopenharmony_ci			overridden = 1;
323062306a36Sopenharmony_ci			set_fw_name(mgp, fw_name, true);
323162306a36Sopenharmony_ci		}
323262306a36Sopenharmony_ci	}
323362306a36Sopenharmony_ci	kernel_param_unlock(THIS_MODULE);
323462306a36Sopenharmony_ci
323562306a36Sopenharmony_ci	if (mgp->board_number < MYRI10GE_MAX_BOARDS &&
323662306a36Sopenharmony_ci	    myri10ge_fw_names[mgp->board_number] != NULL &&
323762306a36Sopenharmony_ci	    strlen(myri10ge_fw_names[mgp->board_number])) {
323862306a36Sopenharmony_ci		set_fw_name(mgp, myri10ge_fw_names[mgp->board_number], false);
323962306a36Sopenharmony_ci		overridden = 1;
324062306a36Sopenharmony_ci	}
324162306a36Sopenharmony_ci	if (overridden)
324262306a36Sopenharmony_ci		dev_info(&mgp->pdev->dev, "overriding firmware to %s\n",
324362306a36Sopenharmony_ci			 mgp->fw_name);
324462306a36Sopenharmony_ci}
324562306a36Sopenharmony_ci
324662306a36Sopenharmony_cistatic void myri10ge_mask_surprise_down(struct pci_dev *pdev)
324762306a36Sopenharmony_ci{
324862306a36Sopenharmony_ci	struct pci_dev *bridge = pdev->bus->self;
324962306a36Sopenharmony_ci	int cap;
325062306a36Sopenharmony_ci	u32 mask;
325162306a36Sopenharmony_ci
325262306a36Sopenharmony_ci	if (bridge == NULL)
325362306a36Sopenharmony_ci		return;
325462306a36Sopenharmony_ci
325562306a36Sopenharmony_ci	cap = pci_find_ext_capability(bridge, PCI_EXT_CAP_ID_ERR);
325662306a36Sopenharmony_ci	if (cap) {
325762306a36Sopenharmony_ci		/* a sram parity error can cause a surprise link
325862306a36Sopenharmony_ci		 * down; since we expect and can recover from sram
325962306a36Sopenharmony_ci		 * parity errors, mask surprise link down events */
326062306a36Sopenharmony_ci		pci_read_config_dword(bridge, cap + PCI_ERR_UNCOR_MASK, &mask);
326162306a36Sopenharmony_ci		mask |= 0x20;
326262306a36Sopenharmony_ci		pci_write_config_dword(bridge, cap + PCI_ERR_UNCOR_MASK, mask);
326362306a36Sopenharmony_ci	}
326462306a36Sopenharmony_ci}
326562306a36Sopenharmony_ci
326662306a36Sopenharmony_cistatic int __maybe_unused myri10ge_suspend(struct device *dev)
326762306a36Sopenharmony_ci{
326862306a36Sopenharmony_ci	struct myri10ge_priv *mgp;
326962306a36Sopenharmony_ci	struct net_device *netdev;
327062306a36Sopenharmony_ci
327162306a36Sopenharmony_ci	mgp = dev_get_drvdata(dev);
327262306a36Sopenharmony_ci	if (mgp == NULL)
327362306a36Sopenharmony_ci		return -EINVAL;
327462306a36Sopenharmony_ci	netdev = mgp->dev;
327562306a36Sopenharmony_ci
327662306a36Sopenharmony_ci	netif_device_detach(netdev);
327762306a36Sopenharmony_ci	if (netif_running(netdev)) {
327862306a36Sopenharmony_ci		netdev_info(netdev, "closing\n");
327962306a36Sopenharmony_ci		rtnl_lock();
328062306a36Sopenharmony_ci		myri10ge_close(netdev);
328162306a36Sopenharmony_ci		rtnl_unlock();
328262306a36Sopenharmony_ci	}
328362306a36Sopenharmony_ci	myri10ge_dummy_rdma(mgp, 0);
328462306a36Sopenharmony_ci
328562306a36Sopenharmony_ci	return 0;
328662306a36Sopenharmony_ci}
328762306a36Sopenharmony_ci
328862306a36Sopenharmony_cistatic int __maybe_unused myri10ge_resume(struct device *dev)
328962306a36Sopenharmony_ci{
329062306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
329162306a36Sopenharmony_ci	struct myri10ge_priv *mgp;
329262306a36Sopenharmony_ci	struct net_device *netdev;
329362306a36Sopenharmony_ci	int status;
329462306a36Sopenharmony_ci	u16 vendor;
329562306a36Sopenharmony_ci
329662306a36Sopenharmony_ci	mgp = pci_get_drvdata(pdev);
329762306a36Sopenharmony_ci	if (mgp == NULL)
329862306a36Sopenharmony_ci		return -EINVAL;
329962306a36Sopenharmony_ci	netdev = mgp->dev;
330062306a36Sopenharmony_ci	msleep(5);		/* give card time to respond */
330162306a36Sopenharmony_ci	pci_read_config_word(mgp->pdev, PCI_VENDOR_ID, &vendor);
330262306a36Sopenharmony_ci	if (vendor == 0xffff) {
330362306a36Sopenharmony_ci		netdev_err(mgp->dev, "device disappeared!\n");
330462306a36Sopenharmony_ci		return -EIO;
330562306a36Sopenharmony_ci	}
330662306a36Sopenharmony_ci
330762306a36Sopenharmony_ci	myri10ge_reset(mgp);
330862306a36Sopenharmony_ci	myri10ge_dummy_rdma(mgp, 1);
330962306a36Sopenharmony_ci
331062306a36Sopenharmony_ci	if (netif_running(netdev)) {
331162306a36Sopenharmony_ci		rtnl_lock();
331262306a36Sopenharmony_ci		status = myri10ge_open(netdev);
331362306a36Sopenharmony_ci		rtnl_unlock();
331462306a36Sopenharmony_ci		if (status != 0)
331562306a36Sopenharmony_ci			goto abort_with_enabled;
331662306a36Sopenharmony_ci
331762306a36Sopenharmony_ci	}
331862306a36Sopenharmony_ci	netif_device_attach(netdev);
331962306a36Sopenharmony_ci
332062306a36Sopenharmony_ci	return 0;
332162306a36Sopenharmony_ci
332262306a36Sopenharmony_ciabort_with_enabled:
332362306a36Sopenharmony_ci	return -EIO;
332462306a36Sopenharmony_ci}
332562306a36Sopenharmony_ci
332662306a36Sopenharmony_cistatic u32 myri10ge_read_reboot(struct myri10ge_priv *mgp)
332762306a36Sopenharmony_ci{
332862306a36Sopenharmony_ci	struct pci_dev *pdev = mgp->pdev;
332962306a36Sopenharmony_ci	int vs = mgp->vendor_specific_offset;
333062306a36Sopenharmony_ci	u32 reboot;
333162306a36Sopenharmony_ci
333262306a36Sopenharmony_ci	/*enter read32 mode */
333362306a36Sopenharmony_ci	pci_write_config_byte(pdev, vs + 0x10, 0x3);
333462306a36Sopenharmony_ci
333562306a36Sopenharmony_ci	/*read REBOOT_STATUS (0xfffffff0) */
333662306a36Sopenharmony_ci	pci_write_config_dword(pdev, vs + 0x18, 0xfffffff0);
333762306a36Sopenharmony_ci	pci_read_config_dword(pdev, vs + 0x14, &reboot);
333862306a36Sopenharmony_ci	return reboot;
333962306a36Sopenharmony_ci}
334062306a36Sopenharmony_ci
334162306a36Sopenharmony_cistatic void
334262306a36Sopenharmony_cimyri10ge_check_slice(struct myri10ge_slice_state *ss, int *reset_needed,
334362306a36Sopenharmony_ci		     int *busy_slice_cnt, u32 rx_pause_cnt)
334462306a36Sopenharmony_ci{
334562306a36Sopenharmony_ci	struct myri10ge_priv *mgp = ss->mgp;
334662306a36Sopenharmony_ci	int slice = ss - mgp->ss;
334762306a36Sopenharmony_ci
334862306a36Sopenharmony_ci	if (ss->tx.req != ss->tx.done &&
334962306a36Sopenharmony_ci	    ss->tx.done == ss->watchdog_tx_done &&
335062306a36Sopenharmony_ci	    ss->watchdog_tx_req != ss->watchdog_tx_done) {
335162306a36Sopenharmony_ci		/* nic seems like it might be stuck.. */
335262306a36Sopenharmony_ci		if (rx_pause_cnt != mgp->watchdog_pause) {
335362306a36Sopenharmony_ci			if (net_ratelimit())
335462306a36Sopenharmony_ci				netdev_warn(mgp->dev, "slice %d: TX paused, "
335562306a36Sopenharmony_ci					    "check link partner\n", slice);
335662306a36Sopenharmony_ci		} else {
335762306a36Sopenharmony_ci			netdev_warn(mgp->dev,
335862306a36Sopenharmony_ci				    "slice %d: TX stuck %d %d %d %d %d %d\n",
335962306a36Sopenharmony_ci				    slice, ss->tx.queue_active, ss->tx.req,
336062306a36Sopenharmony_ci				    ss->tx.done, ss->tx.pkt_start,
336162306a36Sopenharmony_ci				    ss->tx.pkt_done,
336262306a36Sopenharmony_ci				    (int)ntohl(mgp->ss[slice].fw_stats->
336362306a36Sopenharmony_ci					       send_done_count));
336462306a36Sopenharmony_ci			*reset_needed = 1;
336562306a36Sopenharmony_ci			ss->stuck = 1;
336662306a36Sopenharmony_ci		}
336762306a36Sopenharmony_ci	}
336862306a36Sopenharmony_ci	if (ss->watchdog_tx_done != ss->tx.done ||
336962306a36Sopenharmony_ci	    ss->watchdog_rx_done != ss->rx_done.cnt) {
337062306a36Sopenharmony_ci		*busy_slice_cnt += 1;
337162306a36Sopenharmony_ci	}
337262306a36Sopenharmony_ci	ss->watchdog_tx_done = ss->tx.done;
337362306a36Sopenharmony_ci	ss->watchdog_tx_req = ss->tx.req;
337462306a36Sopenharmony_ci	ss->watchdog_rx_done = ss->rx_done.cnt;
337562306a36Sopenharmony_ci}
337662306a36Sopenharmony_ci
337762306a36Sopenharmony_ci/*
337862306a36Sopenharmony_ci * This watchdog is used to check whether the board has suffered
337962306a36Sopenharmony_ci * from a parity error and needs to be recovered.
338062306a36Sopenharmony_ci */
338162306a36Sopenharmony_cistatic void myri10ge_watchdog(struct work_struct *work)
338262306a36Sopenharmony_ci{
338362306a36Sopenharmony_ci	struct myri10ge_priv *mgp =
338462306a36Sopenharmony_ci	    container_of(work, struct myri10ge_priv, watchdog_work);
338562306a36Sopenharmony_ci	struct myri10ge_slice_state *ss;
338662306a36Sopenharmony_ci	u32 reboot, rx_pause_cnt;
338762306a36Sopenharmony_ci	int status, rebooted;
338862306a36Sopenharmony_ci	int i;
338962306a36Sopenharmony_ci	int reset_needed = 0;
339062306a36Sopenharmony_ci	int busy_slice_cnt = 0;
339162306a36Sopenharmony_ci	u16 cmd, vendor;
339262306a36Sopenharmony_ci
339362306a36Sopenharmony_ci	mgp->watchdog_resets++;
339462306a36Sopenharmony_ci	pci_read_config_word(mgp->pdev, PCI_COMMAND, &cmd);
339562306a36Sopenharmony_ci	rebooted = 0;
339662306a36Sopenharmony_ci	if ((cmd & PCI_COMMAND_MASTER) == 0) {
339762306a36Sopenharmony_ci		/* Bus master DMA disabled?  Check to see
339862306a36Sopenharmony_ci		 * if the card rebooted due to a parity error
339962306a36Sopenharmony_ci		 * For now, just report it */
340062306a36Sopenharmony_ci		reboot = myri10ge_read_reboot(mgp);
340162306a36Sopenharmony_ci		netdev_err(mgp->dev, "NIC rebooted (0x%x),%s resetting\n",
340262306a36Sopenharmony_ci			   reboot, myri10ge_reset_recover ? "" : " not");
340362306a36Sopenharmony_ci		if (myri10ge_reset_recover == 0)
340462306a36Sopenharmony_ci			return;
340562306a36Sopenharmony_ci		rtnl_lock();
340662306a36Sopenharmony_ci		mgp->rebooted = 1;
340762306a36Sopenharmony_ci		rebooted = 1;
340862306a36Sopenharmony_ci		myri10ge_close(mgp->dev);
340962306a36Sopenharmony_ci		myri10ge_reset_recover--;
341062306a36Sopenharmony_ci		mgp->rebooted = 0;
341162306a36Sopenharmony_ci		/*
341262306a36Sopenharmony_ci		 * A rebooted nic will come back with config space as
341362306a36Sopenharmony_ci		 * it was after power was applied to PCIe bus.
341462306a36Sopenharmony_ci		 * Attempt to restore config space which was saved
341562306a36Sopenharmony_ci		 * when the driver was loaded, or the last time the
341662306a36Sopenharmony_ci		 * nic was resumed from power saving mode.
341762306a36Sopenharmony_ci		 */
341862306a36Sopenharmony_ci		pci_restore_state(mgp->pdev);
341962306a36Sopenharmony_ci
342062306a36Sopenharmony_ci		/* save state again for accounting reasons */
342162306a36Sopenharmony_ci		pci_save_state(mgp->pdev);
342262306a36Sopenharmony_ci
342362306a36Sopenharmony_ci	} else {
342462306a36Sopenharmony_ci		/* if we get back -1's from our slot, perhaps somebody
342562306a36Sopenharmony_ci		 * powered off our card.  Don't try to reset it in
342662306a36Sopenharmony_ci		 * this case */
342762306a36Sopenharmony_ci		if (cmd == 0xffff) {
342862306a36Sopenharmony_ci			pci_read_config_word(mgp->pdev, PCI_VENDOR_ID, &vendor);
342962306a36Sopenharmony_ci			if (vendor == 0xffff) {
343062306a36Sopenharmony_ci				netdev_err(mgp->dev, "device disappeared!\n");
343162306a36Sopenharmony_ci				return;
343262306a36Sopenharmony_ci			}
343362306a36Sopenharmony_ci		}
343462306a36Sopenharmony_ci		/* Perhaps it is a software error. See if stuck slice
343562306a36Sopenharmony_ci		 * has recovered, reset if not */
343662306a36Sopenharmony_ci		rx_pause_cnt = ntohl(mgp->ss[0].fw_stats->dropped_pause);
343762306a36Sopenharmony_ci		for (i = 0; i < mgp->num_slices; i++) {
343862306a36Sopenharmony_ci			ss = mgp->ss;
343962306a36Sopenharmony_ci			if (ss->stuck) {
344062306a36Sopenharmony_ci				myri10ge_check_slice(ss, &reset_needed,
344162306a36Sopenharmony_ci						     &busy_slice_cnt,
344262306a36Sopenharmony_ci						     rx_pause_cnt);
344362306a36Sopenharmony_ci				ss->stuck = 0;
344462306a36Sopenharmony_ci			}
344562306a36Sopenharmony_ci		}
344662306a36Sopenharmony_ci		if (!reset_needed) {
344762306a36Sopenharmony_ci			netdev_dbg(mgp->dev, "not resetting\n");
344862306a36Sopenharmony_ci			return;
344962306a36Sopenharmony_ci		}
345062306a36Sopenharmony_ci
345162306a36Sopenharmony_ci		netdev_err(mgp->dev, "device timeout, resetting\n");
345262306a36Sopenharmony_ci	}
345362306a36Sopenharmony_ci
345462306a36Sopenharmony_ci	if (!rebooted) {
345562306a36Sopenharmony_ci		rtnl_lock();
345662306a36Sopenharmony_ci		myri10ge_close(mgp->dev);
345762306a36Sopenharmony_ci	}
345862306a36Sopenharmony_ci	status = myri10ge_load_firmware(mgp, 1);
345962306a36Sopenharmony_ci	if (status != 0)
346062306a36Sopenharmony_ci		netdev_err(mgp->dev, "failed to load firmware\n");
346162306a36Sopenharmony_ci	else
346262306a36Sopenharmony_ci		myri10ge_open(mgp->dev);
346362306a36Sopenharmony_ci	rtnl_unlock();
346462306a36Sopenharmony_ci}
346562306a36Sopenharmony_ci
346662306a36Sopenharmony_ci/*
346762306a36Sopenharmony_ci * We use our own timer routine rather than relying upon
346862306a36Sopenharmony_ci * netdev->tx_timeout because we have a very large hardware transmit
346962306a36Sopenharmony_ci * queue.  Due to the large queue, the netdev->tx_timeout function
347062306a36Sopenharmony_ci * cannot detect a NIC with a parity error in a timely fashion if the
347162306a36Sopenharmony_ci * NIC is lightly loaded.
347262306a36Sopenharmony_ci */
347362306a36Sopenharmony_cistatic void myri10ge_watchdog_timer(struct timer_list *t)
347462306a36Sopenharmony_ci{
347562306a36Sopenharmony_ci	struct myri10ge_priv *mgp;
347662306a36Sopenharmony_ci	struct myri10ge_slice_state *ss;
347762306a36Sopenharmony_ci	int i, reset_needed, busy_slice_cnt;
347862306a36Sopenharmony_ci	u32 rx_pause_cnt;
347962306a36Sopenharmony_ci	u16 cmd;
348062306a36Sopenharmony_ci
348162306a36Sopenharmony_ci	mgp = from_timer(mgp, t, watchdog_timer);
348262306a36Sopenharmony_ci
348362306a36Sopenharmony_ci	rx_pause_cnt = ntohl(mgp->ss[0].fw_stats->dropped_pause);
348462306a36Sopenharmony_ci	busy_slice_cnt = 0;
348562306a36Sopenharmony_ci	for (i = 0, reset_needed = 0;
348662306a36Sopenharmony_ci	     i < mgp->num_slices && reset_needed == 0; ++i) {
348762306a36Sopenharmony_ci
348862306a36Sopenharmony_ci		ss = &mgp->ss[i];
348962306a36Sopenharmony_ci		if (ss->rx_small.watchdog_needed) {
349062306a36Sopenharmony_ci			myri10ge_alloc_rx_pages(mgp, &ss->rx_small,
349162306a36Sopenharmony_ci						mgp->small_bytes + MXGEFW_PAD,
349262306a36Sopenharmony_ci						1);
349362306a36Sopenharmony_ci			if (ss->rx_small.fill_cnt - ss->rx_small.cnt >=
349462306a36Sopenharmony_ci			    myri10ge_fill_thresh)
349562306a36Sopenharmony_ci				ss->rx_small.watchdog_needed = 0;
349662306a36Sopenharmony_ci		}
349762306a36Sopenharmony_ci		if (ss->rx_big.watchdog_needed) {
349862306a36Sopenharmony_ci			myri10ge_alloc_rx_pages(mgp, &ss->rx_big,
349962306a36Sopenharmony_ci						mgp->big_bytes, 1);
350062306a36Sopenharmony_ci			if (ss->rx_big.fill_cnt - ss->rx_big.cnt >=
350162306a36Sopenharmony_ci			    myri10ge_fill_thresh)
350262306a36Sopenharmony_ci				ss->rx_big.watchdog_needed = 0;
350362306a36Sopenharmony_ci		}
350462306a36Sopenharmony_ci		myri10ge_check_slice(ss, &reset_needed, &busy_slice_cnt,
350562306a36Sopenharmony_ci				     rx_pause_cnt);
350662306a36Sopenharmony_ci	}
350762306a36Sopenharmony_ci	/* if we've sent or received no traffic, poll the NIC to
350862306a36Sopenharmony_ci	 * ensure it is still there.  Otherwise, we risk not noticing
350962306a36Sopenharmony_ci	 * an error in a timely fashion */
351062306a36Sopenharmony_ci	if (busy_slice_cnt == 0) {
351162306a36Sopenharmony_ci		pci_read_config_word(mgp->pdev, PCI_COMMAND, &cmd);
351262306a36Sopenharmony_ci		if ((cmd & PCI_COMMAND_MASTER) == 0) {
351362306a36Sopenharmony_ci			reset_needed = 1;
351462306a36Sopenharmony_ci		}
351562306a36Sopenharmony_ci	}
351662306a36Sopenharmony_ci	mgp->watchdog_pause = rx_pause_cnt;
351762306a36Sopenharmony_ci
351862306a36Sopenharmony_ci	if (reset_needed) {
351962306a36Sopenharmony_ci		schedule_work(&mgp->watchdog_work);
352062306a36Sopenharmony_ci	} else {
352162306a36Sopenharmony_ci		/* rearm timer */
352262306a36Sopenharmony_ci		mod_timer(&mgp->watchdog_timer,
352362306a36Sopenharmony_ci			  jiffies + myri10ge_watchdog_timeout * HZ);
352462306a36Sopenharmony_ci	}
352562306a36Sopenharmony_ci}
352662306a36Sopenharmony_ci
352762306a36Sopenharmony_cistatic void myri10ge_free_slices(struct myri10ge_priv *mgp)
352862306a36Sopenharmony_ci{
352962306a36Sopenharmony_ci	struct myri10ge_slice_state *ss;
353062306a36Sopenharmony_ci	struct pci_dev *pdev = mgp->pdev;
353162306a36Sopenharmony_ci	size_t bytes;
353262306a36Sopenharmony_ci	int i;
353362306a36Sopenharmony_ci
353462306a36Sopenharmony_ci	if (mgp->ss == NULL)
353562306a36Sopenharmony_ci		return;
353662306a36Sopenharmony_ci
353762306a36Sopenharmony_ci	for (i = 0; i < mgp->num_slices; i++) {
353862306a36Sopenharmony_ci		ss = &mgp->ss[i];
353962306a36Sopenharmony_ci		if (ss->rx_done.entry != NULL) {
354062306a36Sopenharmony_ci			bytes = mgp->max_intr_slots *
354162306a36Sopenharmony_ci			    sizeof(*ss->rx_done.entry);
354262306a36Sopenharmony_ci			dma_free_coherent(&pdev->dev, bytes,
354362306a36Sopenharmony_ci					  ss->rx_done.entry, ss->rx_done.bus);
354462306a36Sopenharmony_ci			ss->rx_done.entry = NULL;
354562306a36Sopenharmony_ci		}
354662306a36Sopenharmony_ci		if (ss->fw_stats != NULL) {
354762306a36Sopenharmony_ci			bytes = sizeof(*ss->fw_stats);
354862306a36Sopenharmony_ci			dma_free_coherent(&pdev->dev, bytes,
354962306a36Sopenharmony_ci					  ss->fw_stats, ss->fw_stats_bus);
355062306a36Sopenharmony_ci			ss->fw_stats = NULL;
355162306a36Sopenharmony_ci		}
355262306a36Sopenharmony_ci		__netif_napi_del(&ss->napi);
355362306a36Sopenharmony_ci	}
355462306a36Sopenharmony_ci	/* Wait till napi structs are no longer used, and then free ss. */
355562306a36Sopenharmony_ci	synchronize_net();
355662306a36Sopenharmony_ci	kfree(mgp->ss);
355762306a36Sopenharmony_ci	mgp->ss = NULL;
355862306a36Sopenharmony_ci}
355962306a36Sopenharmony_ci
356062306a36Sopenharmony_cistatic int myri10ge_alloc_slices(struct myri10ge_priv *mgp)
356162306a36Sopenharmony_ci{
356262306a36Sopenharmony_ci	struct myri10ge_slice_state *ss;
356362306a36Sopenharmony_ci	struct pci_dev *pdev = mgp->pdev;
356462306a36Sopenharmony_ci	size_t bytes;
356562306a36Sopenharmony_ci	int i;
356662306a36Sopenharmony_ci
356762306a36Sopenharmony_ci	bytes = sizeof(*mgp->ss) * mgp->num_slices;
356862306a36Sopenharmony_ci	mgp->ss = kzalloc(bytes, GFP_KERNEL);
356962306a36Sopenharmony_ci	if (mgp->ss == NULL) {
357062306a36Sopenharmony_ci		return -ENOMEM;
357162306a36Sopenharmony_ci	}
357262306a36Sopenharmony_ci
357362306a36Sopenharmony_ci	for (i = 0; i < mgp->num_slices; i++) {
357462306a36Sopenharmony_ci		ss = &mgp->ss[i];
357562306a36Sopenharmony_ci		bytes = mgp->max_intr_slots * sizeof(*ss->rx_done.entry);
357662306a36Sopenharmony_ci		ss->rx_done.entry = dma_alloc_coherent(&pdev->dev, bytes,
357762306a36Sopenharmony_ci						       &ss->rx_done.bus,
357862306a36Sopenharmony_ci						       GFP_KERNEL);
357962306a36Sopenharmony_ci		if (ss->rx_done.entry == NULL)
358062306a36Sopenharmony_ci			goto abort;
358162306a36Sopenharmony_ci		bytes = sizeof(*ss->fw_stats);
358262306a36Sopenharmony_ci		ss->fw_stats = dma_alloc_coherent(&pdev->dev, bytes,
358362306a36Sopenharmony_ci						  &ss->fw_stats_bus,
358462306a36Sopenharmony_ci						  GFP_KERNEL);
358562306a36Sopenharmony_ci		if (ss->fw_stats == NULL)
358662306a36Sopenharmony_ci			goto abort;
358762306a36Sopenharmony_ci		ss->mgp = mgp;
358862306a36Sopenharmony_ci		ss->dev = mgp->dev;
358962306a36Sopenharmony_ci		netif_napi_add_weight(ss->dev, &ss->napi, myri10ge_poll,
359062306a36Sopenharmony_ci				      myri10ge_napi_weight);
359162306a36Sopenharmony_ci	}
359262306a36Sopenharmony_ci	return 0;
359362306a36Sopenharmony_ciabort:
359462306a36Sopenharmony_ci	myri10ge_free_slices(mgp);
359562306a36Sopenharmony_ci	return -ENOMEM;
359662306a36Sopenharmony_ci}
359762306a36Sopenharmony_ci
359862306a36Sopenharmony_ci/*
359962306a36Sopenharmony_ci * This function determines the number of slices supported.
360062306a36Sopenharmony_ci * The number slices is the minimum of the number of CPUS,
360162306a36Sopenharmony_ci * the number of MSI-X irqs supported, the number of slices
360262306a36Sopenharmony_ci * supported by the firmware
360362306a36Sopenharmony_ci */
360462306a36Sopenharmony_cistatic void myri10ge_probe_slices(struct myri10ge_priv *mgp)
360562306a36Sopenharmony_ci{
360662306a36Sopenharmony_ci	struct myri10ge_cmd cmd;
360762306a36Sopenharmony_ci	struct pci_dev *pdev = mgp->pdev;
360862306a36Sopenharmony_ci	char *old_fw;
360962306a36Sopenharmony_ci	bool old_allocated;
361062306a36Sopenharmony_ci	int i, status, ncpus;
361162306a36Sopenharmony_ci
361262306a36Sopenharmony_ci	mgp->num_slices = 1;
361362306a36Sopenharmony_ci	ncpus = netif_get_num_default_rss_queues();
361462306a36Sopenharmony_ci
361562306a36Sopenharmony_ci	if (myri10ge_max_slices == 1 || !pdev->msix_cap ||
361662306a36Sopenharmony_ci	    (myri10ge_max_slices == -1 && ncpus < 2))
361762306a36Sopenharmony_ci		return;
361862306a36Sopenharmony_ci
361962306a36Sopenharmony_ci	/* try to load the slice aware rss firmware */
362062306a36Sopenharmony_ci	old_fw = mgp->fw_name;
362162306a36Sopenharmony_ci	old_allocated = mgp->fw_name_allocated;
362262306a36Sopenharmony_ci	/* don't free old_fw if we override it. */
362362306a36Sopenharmony_ci	mgp->fw_name_allocated = false;
362462306a36Sopenharmony_ci
362562306a36Sopenharmony_ci	if (myri10ge_fw_name != NULL) {
362662306a36Sopenharmony_ci		dev_info(&mgp->pdev->dev, "overriding rss firmware to %s\n",
362762306a36Sopenharmony_ci			 myri10ge_fw_name);
362862306a36Sopenharmony_ci		set_fw_name(mgp, myri10ge_fw_name, false);
362962306a36Sopenharmony_ci	} else if (old_fw == myri10ge_fw_aligned)
363062306a36Sopenharmony_ci		set_fw_name(mgp, myri10ge_fw_rss_aligned, false);
363162306a36Sopenharmony_ci	else
363262306a36Sopenharmony_ci		set_fw_name(mgp, myri10ge_fw_rss_unaligned, false);
363362306a36Sopenharmony_ci	status = myri10ge_load_firmware(mgp, 0);
363462306a36Sopenharmony_ci	if (status != 0) {
363562306a36Sopenharmony_ci		dev_info(&pdev->dev, "Rss firmware not found\n");
363662306a36Sopenharmony_ci		if (old_allocated)
363762306a36Sopenharmony_ci			kfree(old_fw);
363862306a36Sopenharmony_ci		return;
363962306a36Sopenharmony_ci	}
364062306a36Sopenharmony_ci
364162306a36Sopenharmony_ci	/* hit the board with a reset to ensure it is alive */
364262306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
364362306a36Sopenharmony_ci	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_RESET, &cmd, 0);
364462306a36Sopenharmony_ci	if (status != 0) {
364562306a36Sopenharmony_ci		dev_err(&mgp->pdev->dev, "failed reset\n");
364662306a36Sopenharmony_ci		goto abort_with_fw;
364762306a36Sopenharmony_ci	}
364862306a36Sopenharmony_ci
364962306a36Sopenharmony_ci	mgp->max_intr_slots = cmd.data0 / sizeof(struct mcp_slot);
365062306a36Sopenharmony_ci
365162306a36Sopenharmony_ci	/* tell it the size of the interrupt queues */
365262306a36Sopenharmony_ci	cmd.data0 = mgp->max_intr_slots * sizeof(struct mcp_slot);
365362306a36Sopenharmony_ci	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd, 0);
365462306a36Sopenharmony_ci	if (status != 0) {
365562306a36Sopenharmony_ci		dev_err(&mgp->pdev->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n");
365662306a36Sopenharmony_ci		goto abort_with_fw;
365762306a36Sopenharmony_ci	}
365862306a36Sopenharmony_ci
365962306a36Sopenharmony_ci	/* ask the maximum number of slices it supports */
366062306a36Sopenharmony_ci	status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd, 0);
366162306a36Sopenharmony_ci	if (status != 0)
366262306a36Sopenharmony_ci		goto abort_with_fw;
366362306a36Sopenharmony_ci	else
366462306a36Sopenharmony_ci		mgp->num_slices = cmd.data0;
366562306a36Sopenharmony_ci
366662306a36Sopenharmony_ci	/* Only allow multiple slices if MSI-X is usable */
366762306a36Sopenharmony_ci	if (!myri10ge_msi) {
366862306a36Sopenharmony_ci		goto abort_with_fw;
366962306a36Sopenharmony_ci	}
367062306a36Sopenharmony_ci
367162306a36Sopenharmony_ci	/* if the admin did not specify a limit to how many
367262306a36Sopenharmony_ci	 * slices we should use, cap it automatically to the
367362306a36Sopenharmony_ci	 * number of CPUs currently online */
367462306a36Sopenharmony_ci	if (myri10ge_max_slices == -1)
367562306a36Sopenharmony_ci		myri10ge_max_slices = ncpus;
367662306a36Sopenharmony_ci
367762306a36Sopenharmony_ci	if (mgp->num_slices > myri10ge_max_slices)
367862306a36Sopenharmony_ci		mgp->num_slices = myri10ge_max_slices;
367962306a36Sopenharmony_ci
368062306a36Sopenharmony_ci	/* Now try to allocate as many MSI-X vectors as we have
368162306a36Sopenharmony_ci	 * slices. We give up on MSI-X if we can only get a single
368262306a36Sopenharmony_ci	 * vector. */
368362306a36Sopenharmony_ci
368462306a36Sopenharmony_ci	mgp->msix_vectors = kcalloc(mgp->num_slices, sizeof(*mgp->msix_vectors),
368562306a36Sopenharmony_ci				    GFP_KERNEL);
368662306a36Sopenharmony_ci	if (mgp->msix_vectors == NULL)
368762306a36Sopenharmony_ci		goto no_msix;
368862306a36Sopenharmony_ci	for (i = 0; i < mgp->num_slices; i++) {
368962306a36Sopenharmony_ci		mgp->msix_vectors[i].entry = i;
369062306a36Sopenharmony_ci	}
369162306a36Sopenharmony_ci
369262306a36Sopenharmony_ci	while (mgp->num_slices > 1) {
369362306a36Sopenharmony_ci		mgp->num_slices = rounddown_pow_of_two(mgp->num_slices);
369462306a36Sopenharmony_ci		if (mgp->num_slices == 1)
369562306a36Sopenharmony_ci			goto no_msix;
369662306a36Sopenharmony_ci		status = pci_enable_msix_range(pdev,
369762306a36Sopenharmony_ci					       mgp->msix_vectors,
369862306a36Sopenharmony_ci					       mgp->num_slices,
369962306a36Sopenharmony_ci					       mgp->num_slices);
370062306a36Sopenharmony_ci		if (status < 0)
370162306a36Sopenharmony_ci			goto no_msix;
370262306a36Sopenharmony_ci
370362306a36Sopenharmony_ci		pci_disable_msix(pdev);
370462306a36Sopenharmony_ci
370562306a36Sopenharmony_ci		if (status == mgp->num_slices) {
370662306a36Sopenharmony_ci			if (old_allocated)
370762306a36Sopenharmony_ci				kfree(old_fw);
370862306a36Sopenharmony_ci			return;
370962306a36Sopenharmony_ci		} else {
371062306a36Sopenharmony_ci			mgp->num_slices = status;
371162306a36Sopenharmony_ci		}
371262306a36Sopenharmony_ci	}
371362306a36Sopenharmony_ci
371462306a36Sopenharmony_cino_msix:
371562306a36Sopenharmony_ci	if (mgp->msix_vectors != NULL) {
371662306a36Sopenharmony_ci		kfree(mgp->msix_vectors);
371762306a36Sopenharmony_ci		mgp->msix_vectors = NULL;
371862306a36Sopenharmony_ci	}
371962306a36Sopenharmony_ci
372062306a36Sopenharmony_ciabort_with_fw:
372162306a36Sopenharmony_ci	mgp->num_slices = 1;
372262306a36Sopenharmony_ci	set_fw_name(mgp, old_fw, old_allocated);
372362306a36Sopenharmony_ci	myri10ge_load_firmware(mgp, 0);
372462306a36Sopenharmony_ci}
372562306a36Sopenharmony_ci
372662306a36Sopenharmony_cistatic const struct net_device_ops myri10ge_netdev_ops = {
372762306a36Sopenharmony_ci	.ndo_open		= myri10ge_open,
372862306a36Sopenharmony_ci	.ndo_stop		= myri10ge_close,
372962306a36Sopenharmony_ci	.ndo_start_xmit		= myri10ge_xmit,
373062306a36Sopenharmony_ci	.ndo_get_stats64	= myri10ge_get_stats,
373162306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
373262306a36Sopenharmony_ci	.ndo_change_mtu		= myri10ge_change_mtu,
373362306a36Sopenharmony_ci	.ndo_set_rx_mode	= myri10ge_set_multicast_list,
373462306a36Sopenharmony_ci	.ndo_set_mac_address	= myri10ge_set_mac_address,
373562306a36Sopenharmony_ci};
373662306a36Sopenharmony_ci
373762306a36Sopenharmony_cistatic int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
373862306a36Sopenharmony_ci{
373962306a36Sopenharmony_ci	struct net_device *netdev;
374062306a36Sopenharmony_ci	struct myri10ge_priv *mgp;
374162306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
374262306a36Sopenharmony_ci	int status = -ENXIO;
374362306a36Sopenharmony_ci	unsigned hdr_offset, ss_offset;
374462306a36Sopenharmony_ci	static int board_number;
374562306a36Sopenharmony_ci
374662306a36Sopenharmony_ci	netdev = alloc_etherdev_mq(sizeof(*mgp), MYRI10GE_MAX_SLICES);
374762306a36Sopenharmony_ci	if (netdev == NULL)
374862306a36Sopenharmony_ci		return -ENOMEM;
374962306a36Sopenharmony_ci
375062306a36Sopenharmony_ci	SET_NETDEV_DEV(netdev, &pdev->dev);
375162306a36Sopenharmony_ci
375262306a36Sopenharmony_ci	mgp = netdev_priv(netdev);
375362306a36Sopenharmony_ci	mgp->dev = netdev;
375462306a36Sopenharmony_ci	mgp->pdev = pdev;
375562306a36Sopenharmony_ci	mgp->pause = myri10ge_flow_control;
375662306a36Sopenharmony_ci	mgp->intr_coal_delay = myri10ge_intr_coal_delay;
375762306a36Sopenharmony_ci	mgp->msg_enable = netif_msg_init(myri10ge_debug, MYRI10GE_MSG_DEFAULT);
375862306a36Sopenharmony_ci	mgp->board_number = board_number;
375962306a36Sopenharmony_ci	init_waitqueue_head(&mgp->down_wq);
376062306a36Sopenharmony_ci
376162306a36Sopenharmony_ci	if (pci_enable_device(pdev)) {
376262306a36Sopenharmony_ci		dev_err(&pdev->dev, "pci_enable_device call failed\n");
376362306a36Sopenharmony_ci		status = -ENODEV;
376462306a36Sopenharmony_ci		goto abort_with_netdev;
376562306a36Sopenharmony_ci	}
376662306a36Sopenharmony_ci
376762306a36Sopenharmony_ci	/* Find the vendor-specific cap so we can check
376862306a36Sopenharmony_ci	 * the reboot register later on */
376962306a36Sopenharmony_ci	mgp->vendor_specific_offset
377062306a36Sopenharmony_ci	    = pci_find_capability(pdev, PCI_CAP_ID_VNDR);
377162306a36Sopenharmony_ci
377262306a36Sopenharmony_ci	/* Set our max read request to 4KB */
377362306a36Sopenharmony_ci	status = pcie_set_readrq(pdev, 4096);
377462306a36Sopenharmony_ci	if (status != 0) {
377562306a36Sopenharmony_ci		dev_err(&pdev->dev, "Error %d writing PCI_EXP_DEVCTL\n",
377662306a36Sopenharmony_ci			status);
377762306a36Sopenharmony_ci		goto abort_with_enabled;
377862306a36Sopenharmony_ci	}
377962306a36Sopenharmony_ci
378062306a36Sopenharmony_ci	myri10ge_mask_surprise_down(pdev);
378162306a36Sopenharmony_ci	pci_set_master(pdev);
378262306a36Sopenharmony_ci	status = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
378362306a36Sopenharmony_ci	if (status != 0) {
378462306a36Sopenharmony_ci		dev_err(&pdev->dev, "Error %d setting DMA mask\n", status);
378562306a36Sopenharmony_ci		goto abort_with_enabled;
378662306a36Sopenharmony_ci	}
378762306a36Sopenharmony_ci	mgp->cmd = dma_alloc_coherent(&pdev->dev, sizeof(*mgp->cmd),
378862306a36Sopenharmony_ci				      &mgp->cmd_bus, GFP_KERNEL);
378962306a36Sopenharmony_ci	if (!mgp->cmd) {
379062306a36Sopenharmony_ci		status = -ENOMEM;
379162306a36Sopenharmony_ci		goto abort_with_enabled;
379262306a36Sopenharmony_ci	}
379362306a36Sopenharmony_ci
379462306a36Sopenharmony_ci	mgp->board_span = pci_resource_len(pdev, 0);
379562306a36Sopenharmony_ci	mgp->iomem_base = pci_resource_start(pdev, 0);
379662306a36Sopenharmony_ci	mgp->wc_cookie = arch_phys_wc_add(mgp->iomem_base, mgp->board_span);
379762306a36Sopenharmony_ci	mgp->sram = ioremap_wc(mgp->iomem_base, mgp->board_span);
379862306a36Sopenharmony_ci	if (mgp->sram == NULL) {
379962306a36Sopenharmony_ci		dev_err(&pdev->dev, "ioremap failed for %ld bytes at 0x%lx\n",
380062306a36Sopenharmony_ci			mgp->board_span, mgp->iomem_base);
380162306a36Sopenharmony_ci		status = -ENXIO;
380262306a36Sopenharmony_ci		goto abort_with_mtrr;
380362306a36Sopenharmony_ci	}
380462306a36Sopenharmony_ci	hdr_offset =
380562306a36Sopenharmony_ci	    swab32(readl(mgp->sram + MCP_HEADER_PTR_OFFSET)) & 0xffffc;
380662306a36Sopenharmony_ci	ss_offset = hdr_offset + offsetof(struct mcp_gen_header, string_specs);
380762306a36Sopenharmony_ci	mgp->sram_size = swab32(readl(mgp->sram + ss_offset));
380862306a36Sopenharmony_ci	if (mgp->sram_size > mgp->board_span ||
380962306a36Sopenharmony_ci	    mgp->sram_size <= MYRI10GE_FW_OFFSET) {
381062306a36Sopenharmony_ci		dev_err(&pdev->dev,
381162306a36Sopenharmony_ci			"invalid sram_size %dB or board span %ldB\n",
381262306a36Sopenharmony_ci			mgp->sram_size, mgp->board_span);
381362306a36Sopenharmony_ci		status = -EINVAL;
381462306a36Sopenharmony_ci		goto abort_with_ioremap;
381562306a36Sopenharmony_ci	}
381662306a36Sopenharmony_ci	memcpy_fromio(mgp->eeprom_strings,
381762306a36Sopenharmony_ci		      mgp->sram + mgp->sram_size, MYRI10GE_EEPROM_STRINGS_SIZE);
381862306a36Sopenharmony_ci	memset(mgp->eeprom_strings + MYRI10GE_EEPROM_STRINGS_SIZE - 2, 0, 2);
381962306a36Sopenharmony_ci	status = myri10ge_read_mac_addr(mgp);
382062306a36Sopenharmony_ci	if (status)
382162306a36Sopenharmony_ci		goto abort_with_ioremap;
382262306a36Sopenharmony_ci
382362306a36Sopenharmony_ci	eth_hw_addr_set(netdev, mgp->mac_addr);
382462306a36Sopenharmony_ci
382562306a36Sopenharmony_ci	myri10ge_select_firmware(mgp);
382662306a36Sopenharmony_ci
382762306a36Sopenharmony_ci	status = myri10ge_load_firmware(mgp, 1);
382862306a36Sopenharmony_ci	if (status != 0) {
382962306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to load firmware\n");
383062306a36Sopenharmony_ci		goto abort_with_ioremap;
383162306a36Sopenharmony_ci	}
383262306a36Sopenharmony_ci	myri10ge_probe_slices(mgp);
383362306a36Sopenharmony_ci	status = myri10ge_alloc_slices(mgp);
383462306a36Sopenharmony_ci	if (status != 0) {
383562306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to alloc slice state\n");
383662306a36Sopenharmony_ci		goto abort_with_firmware;
383762306a36Sopenharmony_ci	}
383862306a36Sopenharmony_ci	netif_set_real_num_tx_queues(netdev, mgp->num_slices);
383962306a36Sopenharmony_ci	netif_set_real_num_rx_queues(netdev, mgp->num_slices);
384062306a36Sopenharmony_ci	status = myri10ge_reset(mgp);
384162306a36Sopenharmony_ci	if (status != 0) {
384262306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed reset\n");
384362306a36Sopenharmony_ci		goto abort_with_slices;
384462306a36Sopenharmony_ci	}
384562306a36Sopenharmony_ci#ifdef CONFIG_MYRI10GE_DCA
384662306a36Sopenharmony_ci	myri10ge_setup_dca(mgp);
384762306a36Sopenharmony_ci#endif
384862306a36Sopenharmony_ci	pci_set_drvdata(pdev, mgp);
384962306a36Sopenharmony_ci
385062306a36Sopenharmony_ci	/* MTU range: 68 - 9000 */
385162306a36Sopenharmony_ci	netdev->min_mtu = ETH_MIN_MTU;
385262306a36Sopenharmony_ci	netdev->max_mtu = MYRI10GE_MAX_ETHER_MTU - ETH_HLEN;
385362306a36Sopenharmony_ci
385462306a36Sopenharmony_ci	if (myri10ge_initial_mtu > netdev->max_mtu)
385562306a36Sopenharmony_ci		myri10ge_initial_mtu = netdev->max_mtu;
385662306a36Sopenharmony_ci	if (myri10ge_initial_mtu < netdev->min_mtu)
385762306a36Sopenharmony_ci		myri10ge_initial_mtu = netdev->min_mtu;
385862306a36Sopenharmony_ci
385962306a36Sopenharmony_ci	netdev->mtu = myri10ge_initial_mtu;
386062306a36Sopenharmony_ci
386162306a36Sopenharmony_ci	netdev->netdev_ops = &myri10ge_netdev_ops;
386262306a36Sopenharmony_ci	netdev->hw_features = mgp->features | NETIF_F_RXCSUM;
386362306a36Sopenharmony_ci
386462306a36Sopenharmony_ci	/* fake NETIF_F_HW_VLAN_CTAG_RX for good GRO performance */
386562306a36Sopenharmony_ci	netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX;
386662306a36Sopenharmony_ci
386762306a36Sopenharmony_ci	netdev->features = netdev->hw_features | NETIF_F_HIGHDMA;
386862306a36Sopenharmony_ci
386962306a36Sopenharmony_ci	netdev->vlan_features |= mgp->features;
387062306a36Sopenharmony_ci	if (mgp->fw_ver_tiny < 37)
387162306a36Sopenharmony_ci		netdev->vlan_features &= ~NETIF_F_TSO6;
387262306a36Sopenharmony_ci	if (mgp->fw_ver_tiny < 32)
387362306a36Sopenharmony_ci		netdev->vlan_features &= ~NETIF_F_TSO;
387462306a36Sopenharmony_ci
387562306a36Sopenharmony_ci	/* make sure we can get an irq, and that MSI can be
387662306a36Sopenharmony_ci	 * setup (if available). */
387762306a36Sopenharmony_ci	status = myri10ge_request_irq(mgp);
387862306a36Sopenharmony_ci	if (status != 0)
387962306a36Sopenharmony_ci		goto abort_with_slices;
388062306a36Sopenharmony_ci	myri10ge_free_irq(mgp);
388162306a36Sopenharmony_ci
388262306a36Sopenharmony_ci	/* Save configuration space to be restored if the
388362306a36Sopenharmony_ci	 * nic resets due to a parity error */
388462306a36Sopenharmony_ci	pci_save_state(pdev);
388562306a36Sopenharmony_ci
388662306a36Sopenharmony_ci	/* Setup the watchdog timer */
388762306a36Sopenharmony_ci	timer_setup(&mgp->watchdog_timer, myri10ge_watchdog_timer, 0);
388862306a36Sopenharmony_ci
388962306a36Sopenharmony_ci	netdev->ethtool_ops = &myri10ge_ethtool_ops;
389062306a36Sopenharmony_ci	INIT_WORK(&mgp->watchdog_work, myri10ge_watchdog);
389162306a36Sopenharmony_ci	status = register_netdev(netdev);
389262306a36Sopenharmony_ci	if (status != 0) {
389362306a36Sopenharmony_ci		dev_err(&pdev->dev, "register_netdev failed: %d\n", status);
389462306a36Sopenharmony_ci		goto abort_with_state;
389562306a36Sopenharmony_ci	}
389662306a36Sopenharmony_ci	if (mgp->msix_enabled)
389762306a36Sopenharmony_ci		dev_info(dev, "%d MSI-X IRQs, tx bndry %d, fw %s, MTRR %s, WC Enabled\n",
389862306a36Sopenharmony_ci			 mgp->num_slices, mgp->tx_boundary, mgp->fw_name,
389962306a36Sopenharmony_ci			 (mgp->wc_cookie > 0 ? "Enabled" : "Disabled"));
390062306a36Sopenharmony_ci	else
390162306a36Sopenharmony_ci		dev_info(dev, "%s IRQ %d, tx bndry %d, fw %s, MTRR %s, WC Enabled\n",
390262306a36Sopenharmony_ci			 mgp->msi_enabled ? "MSI" : "xPIC",
390362306a36Sopenharmony_ci			 pdev->irq, mgp->tx_boundary, mgp->fw_name,
390462306a36Sopenharmony_ci			 (mgp->wc_cookie > 0 ? "Enabled" : "Disabled"));
390562306a36Sopenharmony_ci
390662306a36Sopenharmony_ci	board_number++;
390762306a36Sopenharmony_ci	return 0;
390862306a36Sopenharmony_ci
390962306a36Sopenharmony_ciabort_with_state:
391062306a36Sopenharmony_ci	pci_restore_state(pdev);
391162306a36Sopenharmony_ci
391262306a36Sopenharmony_ciabort_with_slices:
391362306a36Sopenharmony_ci	myri10ge_free_slices(mgp);
391462306a36Sopenharmony_ci
391562306a36Sopenharmony_ciabort_with_firmware:
391662306a36Sopenharmony_ci	kfree(mgp->msix_vectors);
391762306a36Sopenharmony_ci	myri10ge_dummy_rdma(mgp, 0);
391862306a36Sopenharmony_ci
391962306a36Sopenharmony_ciabort_with_ioremap:
392062306a36Sopenharmony_ci	if (mgp->mac_addr_string != NULL)
392162306a36Sopenharmony_ci		dev_err(&pdev->dev,
392262306a36Sopenharmony_ci			"myri10ge_probe() failed: MAC=%s, SN=%ld\n",
392362306a36Sopenharmony_ci			mgp->mac_addr_string, mgp->serial_number);
392462306a36Sopenharmony_ci	iounmap(mgp->sram);
392562306a36Sopenharmony_ci
392662306a36Sopenharmony_ciabort_with_mtrr:
392762306a36Sopenharmony_ci	arch_phys_wc_del(mgp->wc_cookie);
392862306a36Sopenharmony_ci	dma_free_coherent(&pdev->dev, sizeof(*mgp->cmd),
392962306a36Sopenharmony_ci			  mgp->cmd, mgp->cmd_bus);
393062306a36Sopenharmony_ci
393162306a36Sopenharmony_ciabort_with_enabled:
393262306a36Sopenharmony_ci	pci_disable_device(pdev);
393362306a36Sopenharmony_ci
393462306a36Sopenharmony_ciabort_with_netdev:
393562306a36Sopenharmony_ci	set_fw_name(mgp, NULL, false);
393662306a36Sopenharmony_ci	free_netdev(netdev);
393762306a36Sopenharmony_ci	return status;
393862306a36Sopenharmony_ci}
393962306a36Sopenharmony_ci
394062306a36Sopenharmony_ci/*
394162306a36Sopenharmony_ci * myri10ge_remove
394262306a36Sopenharmony_ci *
394362306a36Sopenharmony_ci * Does what is necessary to shutdown one Myrinet device. Called
394462306a36Sopenharmony_ci *   once for each Myrinet card by the kernel when a module is
394562306a36Sopenharmony_ci *   unloaded.
394662306a36Sopenharmony_ci */
394762306a36Sopenharmony_cistatic void myri10ge_remove(struct pci_dev *pdev)
394862306a36Sopenharmony_ci{
394962306a36Sopenharmony_ci	struct myri10ge_priv *mgp;
395062306a36Sopenharmony_ci	struct net_device *netdev;
395162306a36Sopenharmony_ci
395262306a36Sopenharmony_ci	mgp = pci_get_drvdata(pdev);
395362306a36Sopenharmony_ci	if (mgp == NULL)
395462306a36Sopenharmony_ci		return;
395562306a36Sopenharmony_ci
395662306a36Sopenharmony_ci	cancel_work_sync(&mgp->watchdog_work);
395762306a36Sopenharmony_ci	netdev = mgp->dev;
395862306a36Sopenharmony_ci	unregister_netdev(netdev);
395962306a36Sopenharmony_ci
396062306a36Sopenharmony_ci#ifdef CONFIG_MYRI10GE_DCA
396162306a36Sopenharmony_ci	myri10ge_teardown_dca(mgp);
396262306a36Sopenharmony_ci#endif
396362306a36Sopenharmony_ci	myri10ge_dummy_rdma(mgp, 0);
396462306a36Sopenharmony_ci
396562306a36Sopenharmony_ci	/* avoid a memory leak */
396662306a36Sopenharmony_ci	pci_restore_state(pdev);
396762306a36Sopenharmony_ci
396862306a36Sopenharmony_ci	iounmap(mgp->sram);
396962306a36Sopenharmony_ci	arch_phys_wc_del(mgp->wc_cookie);
397062306a36Sopenharmony_ci	myri10ge_free_slices(mgp);
397162306a36Sopenharmony_ci	kfree(mgp->msix_vectors);
397262306a36Sopenharmony_ci	dma_free_coherent(&pdev->dev, sizeof(*mgp->cmd),
397362306a36Sopenharmony_ci			  mgp->cmd, mgp->cmd_bus);
397462306a36Sopenharmony_ci
397562306a36Sopenharmony_ci	set_fw_name(mgp, NULL, false);
397662306a36Sopenharmony_ci	free_netdev(netdev);
397762306a36Sopenharmony_ci	pci_disable_device(pdev);
397862306a36Sopenharmony_ci}
397962306a36Sopenharmony_ci
398062306a36Sopenharmony_ci#define PCI_DEVICE_ID_MYRICOM_MYRI10GE_Z8E 	0x0008
398162306a36Sopenharmony_ci#define PCI_DEVICE_ID_MYRICOM_MYRI10GE_Z8E_9	0x0009
398262306a36Sopenharmony_ci
398362306a36Sopenharmony_cistatic const struct pci_device_id myri10ge_pci_tbl[] = {
398462306a36Sopenharmony_ci	{PCI_DEVICE(PCI_VENDOR_ID_MYRICOM, PCI_DEVICE_ID_MYRICOM_MYRI10GE_Z8E)},
398562306a36Sopenharmony_ci	{PCI_DEVICE
398662306a36Sopenharmony_ci	 (PCI_VENDOR_ID_MYRICOM, PCI_DEVICE_ID_MYRICOM_MYRI10GE_Z8E_9)},
398762306a36Sopenharmony_ci	{0},
398862306a36Sopenharmony_ci};
398962306a36Sopenharmony_ci
399062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, myri10ge_pci_tbl);
399162306a36Sopenharmony_ci
399262306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(myri10ge_pm_ops, myri10ge_suspend, myri10ge_resume);
399362306a36Sopenharmony_ci
399462306a36Sopenharmony_cistatic struct pci_driver myri10ge_driver = {
399562306a36Sopenharmony_ci	.name = "myri10ge",
399662306a36Sopenharmony_ci	.probe = myri10ge_probe,
399762306a36Sopenharmony_ci	.remove = myri10ge_remove,
399862306a36Sopenharmony_ci	.id_table = myri10ge_pci_tbl,
399962306a36Sopenharmony_ci	.driver.pm = &myri10ge_pm_ops,
400062306a36Sopenharmony_ci};
400162306a36Sopenharmony_ci
400262306a36Sopenharmony_ci#ifdef CONFIG_MYRI10GE_DCA
400362306a36Sopenharmony_cistatic int
400462306a36Sopenharmony_cimyri10ge_notify_dca(struct notifier_block *nb, unsigned long event, void *p)
400562306a36Sopenharmony_ci{
400662306a36Sopenharmony_ci	int err = driver_for_each_device(&myri10ge_driver.driver,
400762306a36Sopenharmony_ci					 NULL, &event,
400862306a36Sopenharmony_ci					 myri10ge_notify_dca_device);
400962306a36Sopenharmony_ci
401062306a36Sopenharmony_ci	if (err)
401162306a36Sopenharmony_ci		return NOTIFY_BAD;
401262306a36Sopenharmony_ci	return NOTIFY_DONE;
401362306a36Sopenharmony_ci}
401462306a36Sopenharmony_ci
401562306a36Sopenharmony_cistatic struct notifier_block myri10ge_dca_notifier = {
401662306a36Sopenharmony_ci	.notifier_call = myri10ge_notify_dca,
401762306a36Sopenharmony_ci	.next = NULL,
401862306a36Sopenharmony_ci	.priority = 0,
401962306a36Sopenharmony_ci};
402062306a36Sopenharmony_ci#endif				/* CONFIG_MYRI10GE_DCA */
402162306a36Sopenharmony_ci
402262306a36Sopenharmony_cistatic __init int myri10ge_init_module(void)
402362306a36Sopenharmony_ci{
402462306a36Sopenharmony_ci	pr_info("Version %s\n", MYRI10GE_VERSION_STR);
402562306a36Sopenharmony_ci
402662306a36Sopenharmony_ci	if (myri10ge_rss_hash > MXGEFW_RSS_HASH_TYPE_MAX) {
402762306a36Sopenharmony_ci		pr_err("Illegal rssh hash type %d, defaulting to source port\n",
402862306a36Sopenharmony_ci		       myri10ge_rss_hash);
402962306a36Sopenharmony_ci		myri10ge_rss_hash = MXGEFW_RSS_HASH_TYPE_SRC_PORT;
403062306a36Sopenharmony_ci	}
403162306a36Sopenharmony_ci#ifdef CONFIG_MYRI10GE_DCA
403262306a36Sopenharmony_ci	dca_register_notify(&myri10ge_dca_notifier);
403362306a36Sopenharmony_ci#endif
403462306a36Sopenharmony_ci	if (myri10ge_max_slices > MYRI10GE_MAX_SLICES)
403562306a36Sopenharmony_ci		myri10ge_max_slices = MYRI10GE_MAX_SLICES;
403662306a36Sopenharmony_ci
403762306a36Sopenharmony_ci	return pci_register_driver(&myri10ge_driver);
403862306a36Sopenharmony_ci}
403962306a36Sopenharmony_ci
404062306a36Sopenharmony_cimodule_init(myri10ge_init_module);
404162306a36Sopenharmony_ci
404262306a36Sopenharmony_cistatic __exit void myri10ge_cleanup_module(void)
404362306a36Sopenharmony_ci{
404462306a36Sopenharmony_ci#ifdef CONFIG_MYRI10GE_DCA
404562306a36Sopenharmony_ci	dca_unregister_notify(&myri10ge_dca_notifier);
404662306a36Sopenharmony_ci#endif
404762306a36Sopenharmony_ci	pci_unregister_driver(&myri10ge_driver);
404862306a36Sopenharmony_ci}
404962306a36Sopenharmony_ci
405062306a36Sopenharmony_cimodule_exit(myri10ge_cleanup_module);
4051