162306a36Sopenharmony_ci/**********************************************************************
262306a36Sopenharmony_ci * Author: Cavium, Inc.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Contact: support@cavium.com
562306a36Sopenharmony_ci *          Please include "LiquidIO" in the subject.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (c) 2003-2016 Cavium, Inc.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * This file is free software; you can redistribute it and/or modify
1062306a36Sopenharmony_ci * it under the terms of the GNU General Public License, Version 2, as
1162306a36Sopenharmony_ci * published by the Free Software Foundation.
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * This file is distributed in the hope that it will be useful, but
1462306a36Sopenharmony_ci * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
1562306a36Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
1662306a36Sopenharmony_ci * NONINFRINGEMENT.  See the GNU General Public License for more details.
1762306a36Sopenharmony_ci ***********************************************************************/
1862306a36Sopenharmony_ci#include <linux/module.h>
1962306a36Sopenharmony_ci#include <linux/interrupt.h>
2062306a36Sopenharmony_ci#include <linux/pci.h>
2162306a36Sopenharmony_ci#include <linux/firmware.h>
2262306a36Sopenharmony_ci#include <net/vxlan.h>
2362306a36Sopenharmony_ci#include <linux/kthread.h>
2462306a36Sopenharmony_ci#include "liquidio_common.h"
2562306a36Sopenharmony_ci#include "octeon_droq.h"
2662306a36Sopenharmony_ci#include "octeon_iq.h"
2762306a36Sopenharmony_ci#include "response_manager.h"
2862306a36Sopenharmony_ci#include "octeon_device.h"
2962306a36Sopenharmony_ci#include "octeon_nic.h"
3062306a36Sopenharmony_ci#include "octeon_main.h"
3162306a36Sopenharmony_ci#include "octeon_network.h"
3262306a36Sopenharmony_ci#include "cn66xx_regs.h"
3362306a36Sopenharmony_ci#include "cn66xx_device.h"
3462306a36Sopenharmony_ci#include "cn68xx_device.h"
3562306a36Sopenharmony_ci#include "cn23xx_pf_device.h"
3662306a36Sopenharmony_ci#include "liquidio_image.h"
3762306a36Sopenharmony_ci#include "lio_vf_rep.h"
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ciMODULE_AUTHOR("Cavium Networks, <support@cavium.com>");
4062306a36Sopenharmony_ciMODULE_DESCRIPTION("Cavium LiquidIO Intelligent Server Adapter Driver");
4162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
4262306a36Sopenharmony_ciMODULE_FIRMWARE(LIO_FW_DIR LIO_FW_BASE_NAME LIO_210SV_NAME
4362306a36Sopenharmony_ci		"_" LIO_FW_NAME_TYPE_NIC LIO_FW_NAME_SUFFIX);
4462306a36Sopenharmony_ciMODULE_FIRMWARE(LIO_FW_DIR LIO_FW_BASE_NAME LIO_210NV_NAME
4562306a36Sopenharmony_ci		"_" LIO_FW_NAME_TYPE_NIC LIO_FW_NAME_SUFFIX);
4662306a36Sopenharmony_ciMODULE_FIRMWARE(LIO_FW_DIR LIO_FW_BASE_NAME LIO_410NV_NAME
4762306a36Sopenharmony_ci		"_" LIO_FW_NAME_TYPE_NIC LIO_FW_NAME_SUFFIX);
4862306a36Sopenharmony_ciMODULE_FIRMWARE(LIO_FW_DIR LIO_FW_BASE_NAME LIO_23XX_NAME
4962306a36Sopenharmony_ci		"_" LIO_FW_NAME_TYPE_NIC LIO_FW_NAME_SUFFIX);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic int ddr_timeout = 10000;
5262306a36Sopenharmony_cimodule_param(ddr_timeout, int, 0644);
5362306a36Sopenharmony_ciMODULE_PARM_DESC(ddr_timeout,
5462306a36Sopenharmony_ci		 "Number of milliseconds to wait for DDR initialization. 0 waits for ddr_timeout to be set to non-zero value before starting to check");
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK)
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic int debug = -1;
5962306a36Sopenharmony_cimodule_param(debug, int, 0644);
6062306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "NETIF_MSG debug bits");
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic char fw_type[LIO_MAX_FW_TYPE_LEN] = LIO_FW_NAME_TYPE_AUTO;
6362306a36Sopenharmony_cimodule_param_string(fw_type, fw_type, sizeof(fw_type), 0444);
6462306a36Sopenharmony_ciMODULE_PARM_DESC(fw_type, "Type of firmware to be loaded (default is \"auto\"), which uses firmware in flash, if present, else loads \"nic\".");
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic u32 console_bitmask;
6762306a36Sopenharmony_cimodule_param(console_bitmask, int, 0644);
6862306a36Sopenharmony_ciMODULE_PARM_DESC(console_bitmask,
6962306a36Sopenharmony_ci		 "Bitmask indicating which consoles have debug output redirected to syslog.");
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/**
7262306a36Sopenharmony_ci * octeon_console_debug_enabled - determines if a given console has debug enabled.
7362306a36Sopenharmony_ci * @console: console to check
7462306a36Sopenharmony_ci * Return:  1 = enabled. 0 otherwise
7562306a36Sopenharmony_ci */
7662306a36Sopenharmony_cistatic int octeon_console_debug_enabled(u32 console)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	return (console_bitmask >> (console)) & 0x1;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci/* Polling interval for determining when NIC application is alive */
8262306a36Sopenharmony_ci#define LIQUIDIO_STARTER_POLL_INTERVAL_MS 100
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/* runtime link query interval */
8562306a36Sopenharmony_ci#define LIQUIDIO_LINK_QUERY_INTERVAL_MS         1000
8662306a36Sopenharmony_ci/* update localtime to octeon firmware every 60 seconds.
8762306a36Sopenharmony_ci * make firmware to use same time reference, so that it will be easy to
8862306a36Sopenharmony_ci * correlate firmware logged events/errors with host events, for debugging.
8962306a36Sopenharmony_ci */
9062306a36Sopenharmony_ci#define LIO_SYNC_OCTEON_TIME_INTERVAL_MS 60000
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci/* time to wait for possible in-flight requests in milliseconds */
9362306a36Sopenharmony_ci#define WAIT_INFLIGHT_REQUEST	msecs_to_jiffies(1000)
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistruct oct_link_status_resp {
9662306a36Sopenharmony_ci	u64 rh;
9762306a36Sopenharmony_ci	struct oct_link_info link_info;
9862306a36Sopenharmony_ci	u64 status;
9962306a36Sopenharmony_ci};
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistruct oct_timestamp_resp {
10262306a36Sopenharmony_ci	u64 rh;
10362306a36Sopenharmony_ci	u64 timestamp;
10462306a36Sopenharmony_ci	u64 status;
10562306a36Sopenharmony_ci};
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci#define OCT_TIMESTAMP_RESP_SIZE (sizeof(struct oct_timestamp_resp))
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ciunion tx_info {
11062306a36Sopenharmony_ci	u64 u64;
11162306a36Sopenharmony_ci	struct {
11262306a36Sopenharmony_ci#ifdef __BIG_ENDIAN_BITFIELD
11362306a36Sopenharmony_ci		u16 gso_size;
11462306a36Sopenharmony_ci		u16 gso_segs;
11562306a36Sopenharmony_ci		u32 reserved;
11662306a36Sopenharmony_ci#else
11762306a36Sopenharmony_ci		u32 reserved;
11862306a36Sopenharmony_ci		u16 gso_segs;
11962306a36Sopenharmony_ci		u16 gso_size;
12062306a36Sopenharmony_ci#endif
12162306a36Sopenharmony_ci	} s;
12262306a36Sopenharmony_ci};
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/* Octeon device properties to be used by the NIC module.
12562306a36Sopenharmony_ci * Each octeon device in the system will be represented
12662306a36Sopenharmony_ci * by this structure in the NIC module.
12762306a36Sopenharmony_ci */
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci#define OCTNIC_GSO_MAX_HEADER_SIZE 128
13062306a36Sopenharmony_ci#define OCTNIC_GSO_MAX_SIZE                                                    \
13162306a36Sopenharmony_ci	(CN23XX_DEFAULT_INPUT_JABBER - OCTNIC_GSO_MAX_HEADER_SIZE)
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistruct handshake {
13462306a36Sopenharmony_ci	struct completion init;
13562306a36Sopenharmony_ci	struct completion started;
13662306a36Sopenharmony_ci	struct pci_dev *pci_dev;
13762306a36Sopenharmony_ci	int init_ok;
13862306a36Sopenharmony_ci	int started_ok;
13962306a36Sopenharmony_ci};
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci#ifdef CONFIG_PCI_IOV
14262306a36Sopenharmony_cistatic int liquidio_enable_sriov(struct pci_dev *dev, int num_vfs);
14362306a36Sopenharmony_ci#endif
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic int octeon_dbg_console_print(struct octeon_device *oct, u32 console_num,
14662306a36Sopenharmony_ci				    char *prefix, char *suffix);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic int octeon_device_init(struct octeon_device *);
14962306a36Sopenharmony_cistatic int liquidio_stop(struct net_device *netdev);
15062306a36Sopenharmony_cistatic void liquidio_remove(struct pci_dev *pdev);
15162306a36Sopenharmony_cistatic int liquidio_probe(struct pci_dev *pdev,
15262306a36Sopenharmony_ci			  const struct pci_device_id *ent);
15362306a36Sopenharmony_cistatic int liquidio_set_vf_link_state(struct net_device *netdev, int vfidx,
15462306a36Sopenharmony_ci				      int linkstate);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic struct handshake handshake[MAX_OCTEON_DEVICES];
15762306a36Sopenharmony_cistatic struct completion first_stage;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic void octeon_droq_bh(struct tasklet_struct *t)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	int q_no;
16262306a36Sopenharmony_ci	int reschedule = 0;
16362306a36Sopenharmony_ci	struct octeon_device_priv *oct_priv = from_tasklet(oct_priv, t,
16462306a36Sopenharmony_ci							  droq_tasklet);
16562306a36Sopenharmony_ci	struct octeon_device *oct = oct_priv->dev;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	for (q_no = 0; q_no < MAX_OCTEON_OUTPUT_QUEUES(oct); q_no++) {
16862306a36Sopenharmony_ci		if (!(oct->io_qmask.oq & BIT_ULL(q_no)))
16962306a36Sopenharmony_ci			continue;
17062306a36Sopenharmony_ci		reschedule |= octeon_droq_process_packets(oct, oct->droq[q_no],
17162306a36Sopenharmony_ci							  MAX_PACKET_BUDGET);
17262306a36Sopenharmony_ci		lio_enable_irq(oct->droq[q_no], NULL);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci		if (OCTEON_CN23XX_PF(oct) && oct->msix_on) {
17562306a36Sopenharmony_ci			/* set time and cnt interrupt thresholds for this DROQ
17662306a36Sopenharmony_ci			 * for NAPI
17762306a36Sopenharmony_ci			 */
17862306a36Sopenharmony_ci			int adjusted_q_no = q_no + oct->sriov_info.pf_srn;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci			octeon_write_csr64(
18162306a36Sopenharmony_ci			    oct, CN23XX_SLI_OQ_PKT_INT_LEVELS(adjusted_q_no),
18262306a36Sopenharmony_ci			    0x5700000040ULL);
18362306a36Sopenharmony_ci			octeon_write_csr64(
18462306a36Sopenharmony_ci			    oct, CN23XX_SLI_OQ_PKTS_SENT(adjusted_q_no), 0);
18562306a36Sopenharmony_ci		}
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (reschedule)
18962306a36Sopenharmony_ci		tasklet_schedule(&oct_priv->droq_tasklet);
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic int lio_wait_for_oq_pkts(struct octeon_device *oct)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	struct octeon_device_priv *oct_priv = oct->priv;
19562306a36Sopenharmony_ci	int retry = 100, pkt_cnt = 0, pending_pkts = 0;
19662306a36Sopenharmony_ci	int i;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	do {
19962306a36Sopenharmony_ci		pending_pkts = 0;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci		for (i = 0; i < MAX_OCTEON_OUTPUT_QUEUES(oct); i++) {
20262306a36Sopenharmony_ci			if (!(oct->io_qmask.oq & BIT_ULL(i)))
20362306a36Sopenharmony_ci				continue;
20462306a36Sopenharmony_ci			pkt_cnt += octeon_droq_check_hw_for_pkts(oct->droq[i]);
20562306a36Sopenharmony_ci		}
20662306a36Sopenharmony_ci		if (pkt_cnt > 0) {
20762306a36Sopenharmony_ci			pending_pkts += pkt_cnt;
20862306a36Sopenharmony_ci			tasklet_schedule(&oct_priv->droq_tasklet);
20962306a36Sopenharmony_ci		}
21062306a36Sopenharmony_ci		pkt_cnt = 0;
21162306a36Sopenharmony_ci		schedule_timeout_uninterruptible(1);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	} while (retry-- && pending_pkts);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	return pkt_cnt;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci/**
21962306a36Sopenharmony_ci * force_io_queues_off - Forces all IO queues off on a given device
22062306a36Sopenharmony_ci * @oct: Pointer to Octeon device
22162306a36Sopenharmony_ci */
22262306a36Sopenharmony_cistatic void force_io_queues_off(struct octeon_device *oct)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	if ((oct->chip_id == OCTEON_CN66XX) ||
22562306a36Sopenharmony_ci	    (oct->chip_id == OCTEON_CN68XX)) {
22662306a36Sopenharmony_ci		/* Reset the Enable bits for Input Queues. */
22762306a36Sopenharmony_ci		octeon_write_csr(oct, CN6XXX_SLI_PKT_INSTR_ENB, 0);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci		/* Reset the Enable bits for Output Queues. */
23062306a36Sopenharmony_ci		octeon_write_csr(oct, CN6XXX_SLI_PKT_OUT_ENB, 0);
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci/**
23562306a36Sopenharmony_ci * pcierror_quiesce_device - Cause device to go quiet so it can be safely removed/reset/etc
23662306a36Sopenharmony_ci * @oct: Pointer to Octeon device
23762306a36Sopenharmony_ci */
23862306a36Sopenharmony_cistatic inline void pcierror_quiesce_device(struct octeon_device *oct)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	int i;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	/* Disable the input and output queues now. No more packets will
24362306a36Sopenharmony_ci	 * arrive from Octeon, but we should wait for all packet processing
24462306a36Sopenharmony_ci	 * to finish.
24562306a36Sopenharmony_ci	 */
24662306a36Sopenharmony_ci	force_io_queues_off(oct);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	/* To allow for in-flight requests */
24962306a36Sopenharmony_ci	schedule_timeout_uninterruptible(WAIT_INFLIGHT_REQUEST);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (wait_for_pending_requests(oct))
25262306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "There were pending requests\n");
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	/* Force all requests waiting to be fetched by OCTEON to complete. */
25562306a36Sopenharmony_ci	for (i = 0; i < MAX_OCTEON_INSTR_QUEUES(oct); i++) {
25662306a36Sopenharmony_ci		struct octeon_instr_queue *iq;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci		if (!(oct->io_qmask.iq & BIT_ULL(i)))
25962306a36Sopenharmony_ci			continue;
26062306a36Sopenharmony_ci		iq = oct->instr_queue[i];
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci		if (atomic_read(&iq->instr_pending)) {
26362306a36Sopenharmony_ci			spin_lock_bh(&iq->lock);
26462306a36Sopenharmony_ci			iq->fill_cnt = 0;
26562306a36Sopenharmony_ci			iq->octeon_read_index = iq->host_write_index;
26662306a36Sopenharmony_ci			iq->stats.instr_processed +=
26762306a36Sopenharmony_ci				atomic_read(&iq->instr_pending);
26862306a36Sopenharmony_ci			lio_process_iq_request_list(oct, iq, 0);
26962306a36Sopenharmony_ci			spin_unlock_bh(&iq->lock);
27062306a36Sopenharmony_ci		}
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	/* Force all pending ordered list requests to time out. */
27462306a36Sopenharmony_ci	lio_process_ordered_list(oct, 1);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	/* We do not need to wait for output queue packets to be processed. */
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci/**
28062306a36Sopenharmony_ci * cleanup_aer_uncorrect_error_status - Cleanup PCI AER uncorrectable error status
28162306a36Sopenharmony_ci * @dev: Pointer to PCI device
28262306a36Sopenharmony_ci */
28362306a36Sopenharmony_cistatic void cleanup_aer_uncorrect_error_status(struct pci_dev *dev)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	int pos = 0x100;
28662306a36Sopenharmony_ci	u32 status, mask;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	pr_info("%s :\n", __func__);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
29162306a36Sopenharmony_ci	pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask);
29262306a36Sopenharmony_ci	if (dev->error_state == pci_channel_io_normal)
29362306a36Sopenharmony_ci		status &= ~mask;        /* Clear corresponding nonfatal bits */
29462306a36Sopenharmony_ci	else
29562306a36Sopenharmony_ci		status &= mask;         /* Clear corresponding fatal bits */
29662306a36Sopenharmony_ci	pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status);
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci/**
30062306a36Sopenharmony_ci * stop_pci_io - Stop all PCI IO to a given device
30162306a36Sopenharmony_ci * @oct: Pointer to Octeon device
30262306a36Sopenharmony_ci */
30362306a36Sopenharmony_cistatic void stop_pci_io(struct octeon_device *oct)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	/* No more instructions will be forwarded. */
30662306a36Sopenharmony_ci	atomic_set(&oct->status, OCT_DEV_IN_RESET);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	pci_disable_device(oct->pci_dev);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	/* Disable interrupts  */
31162306a36Sopenharmony_ci	oct->fn_list.disable_interrupt(oct, OCTEON_ALL_INTR);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	pcierror_quiesce_device(oct);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	/* Release the interrupt line */
31662306a36Sopenharmony_ci	free_irq(oct->pci_dev->irq, oct);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	if (oct->flags & LIO_FLAG_MSI_ENABLED)
31962306a36Sopenharmony_ci		pci_disable_msi(oct->pci_dev);
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	dev_dbg(&oct->pci_dev->dev, "Device state is now %s\n",
32262306a36Sopenharmony_ci		lio_get_state_string(&oct->status));
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	/* making it a common function for all OCTEON models */
32562306a36Sopenharmony_ci	cleanup_aer_uncorrect_error_status(oct->pci_dev);
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci/**
32962306a36Sopenharmony_ci * liquidio_pcie_error_detected - called when PCI error is detected
33062306a36Sopenharmony_ci * @pdev: Pointer to PCI device
33162306a36Sopenharmony_ci * @state: The current pci connection state
33262306a36Sopenharmony_ci *
33362306a36Sopenharmony_ci * This function is called after a PCI bus error affecting
33462306a36Sopenharmony_ci * this device has been detected.
33562306a36Sopenharmony_ci */
33662306a36Sopenharmony_cistatic pci_ers_result_t liquidio_pcie_error_detected(struct pci_dev *pdev,
33762306a36Sopenharmony_ci						     pci_channel_state_t state)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	struct octeon_device *oct = pci_get_drvdata(pdev);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	/* Non-correctable Non-fatal errors */
34262306a36Sopenharmony_ci	if (state == pci_channel_io_normal) {
34362306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "Non-correctable non-fatal error reported:\n");
34462306a36Sopenharmony_ci		cleanup_aer_uncorrect_error_status(oct->pci_dev);
34562306a36Sopenharmony_ci		return PCI_ERS_RESULT_CAN_RECOVER;
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	/* Non-correctable Fatal errors */
34962306a36Sopenharmony_ci	dev_err(&oct->pci_dev->dev, "Non-correctable FATAL reported by PCI AER driver\n");
35062306a36Sopenharmony_ci	stop_pci_io(oct);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	/* Always return a DISCONNECT. There is no support for recovery but only
35362306a36Sopenharmony_ci	 * for a clean shutdown.
35462306a36Sopenharmony_ci	 */
35562306a36Sopenharmony_ci	return PCI_ERS_RESULT_DISCONNECT;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci/**
35962306a36Sopenharmony_ci * liquidio_pcie_mmio_enabled - mmio handler
36062306a36Sopenharmony_ci * @pdev: Pointer to PCI device
36162306a36Sopenharmony_ci */
36262306a36Sopenharmony_cistatic pci_ers_result_t liquidio_pcie_mmio_enabled(struct pci_dev __maybe_unused *pdev)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	/* We should never hit this since we never ask for a reset for a Fatal
36562306a36Sopenharmony_ci	 * Error. We always return DISCONNECT in io_error above.
36662306a36Sopenharmony_ci	 * But play safe and return RECOVERED for now.
36762306a36Sopenharmony_ci	 */
36862306a36Sopenharmony_ci	return PCI_ERS_RESULT_RECOVERED;
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci/**
37262306a36Sopenharmony_ci * liquidio_pcie_slot_reset - called after the pci bus has been reset.
37362306a36Sopenharmony_ci * @pdev: Pointer to PCI device
37462306a36Sopenharmony_ci *
37562306a36Sopenharmony_ci * Restart the card from scratch, as if from a cold-boot. Implementation
37662306a36Sopenharmony_ci * resembles the first-half of the octeon_resume routine.
37762306a36Sopenharmony_ci */
37862306a36Sopenharmony_cistatic pci_ers_result_t liquidio_pcie_slot_reset(struct pci_dev __maybe_unused *pdev)
37962306a36Sopenharmony_ci{
38062306a36Sopenharmony_ci	/* We should never hit this since we never ask for a reset for a Fatal
38162306a36Sopenharmony_ci	 * Error. We always return DISCONNECT in io_error above.
38262306a36Sopenharmony_ci	 * But play safe and return RECOVERED for now.
38362306a36Sopenharmony_ci	 */
38462306a36Sopenharmony_ci	return PCI_ERS_RESULT_RECOVERED;
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci/**
38862306a36Sopenharmony_ci * liquidio_pcie_resume - called when traffic can start flowing again.
38962306a36Sopenharmony_ci * @pdev: Pointer to PCI device
39062306a36Sopenharmony_ci *
39162306a36Sopenharmony_ci * This callback is called when the error recovery driver tells us that
39262306a36Sopenharmony_ci * its OK to resume normal operation. Implementation resembles the
39362306a36Sopenharmony_ci * second-half of the octeon_resume routine.
39462306a36Sopenharmony_ci */
39562306a36Sopenharmony_cistatic void liquidio_pcie_resume(struct pci_dev __maybe_unused *pdev)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	/* Nothing to be done here. */
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci#define liquidio_suspend NULL
40162306a36Sopenharmony_ci#define liquidio_resume NULL
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci/* For PCI-E Advanced Error Recovery (AER) Interface */
40462306a36Sopenharmony_cistatic const struct pci_error_handlers liquidio_err_handler = {
40562306a36Sopenharmony_ci	.error_detected = liquidio_pcie_error_detected,
40662306a36Sopenharmony_ci	.mmio_enabled	= liquidio_pcie_mmio_enabled,
40762306a36Sopenharmony_ci	.slot_reset	= liquidio_pcie_slot_reset,
40862306a36Sopenharmony_ci	.resume		= liquidio_pcie_resume,
40962306a36Sopenharmony_ci};
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_cistatic const struct pci_device_id liquidio_pci_tbl[] = {
41262306a36Sopenharmony_ci	{       /* 68xx */
41362306a36Sopenharmony_ci		PCI_VENDOR_ID_CAVIUM, 0x91, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0
41462306a36Sopenharmony_ci	},
41562306a36Sopenharmony_ci	{       /* 66xx */
41662306a36Sopenharmony_ci		PCI_VENDOR_ID_CAVIUM, 0x92, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0
41762306a36Sopenharmony_ci	},
41862306a36Sopenharmony_ci	{       /* 23xx pf */
41962306a36Sopenharmony_ci		PCI_VENDOR_ID_CAVIUM, 0x9702, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0
42062306a36Sopenharmony_ci	},
42162306a36Sopenharmony_ci	{
42262306a36Sopenharmony_ci		0, 0, 0, 0, 0, 0, 0
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci};
42562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, liquidio_pci_tbl);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(liquidio_pm_ops, liquidio_suspend, liquidio_resume);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_cistatic struct pci_driver liquidio_pci_driver = {
43062306a36Sopenharmony_ci	.name		= "LiquidIO",
43162306a36Sopenharmony_ci	.id_table	= liquidio_pci_tbl,
43262306a36Sopenharmony_ci	.probe		= liquidio_probe,
43362306a36Sopenharmony_ci	.remove		= liquidio_remove,
43462306a36Sopenharmony_ci	.err_handler	= &liquidio_err_handler,    /* For AER */
43562306a36Sopenharmony_ci	.driver.pm	= &liquidio_pm_ops,
43662306a36Sopenharmony_ci#ifdef CONFIG_PCI_IOV
43762306a36Sopenharmony_ci	.sriov_configure = liquidio_enable_sriov,
43862306a36Sopenharmony_ci#endif
43962306a36Sopenharmony_ci};
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci/**
44262306a36Sopenharmony_ci * liquidio_init_pci - register PCI driver
44362306a36Sopenharmony_ci */
44462306a36Sopenharmony_cistatic int liquidio_init_pci(void)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	return pci_register_driver(&liquidio_pci_driver);
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci/**
45062306a36Sopenharmony_ci * liquidio_deinit_pci - unregister PCI driver
45162306a36Sopenharmony_ci */
45262306a36Sopenharmony_cistatic void liquidio_deinit_pci(void)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	pci_unregister_driver(&liquidio_pci_driver);
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci/**
45862306a36Sopenharmony_ci * check_txq_status - Check Tx queue status, and take appropriate action
45962306a36Sopenharmony_ci * @lio: per-network private data
46062306a36Sopenharmony_ci * Return: 0 if full, number of queues woken up otherwise
46162306a36Sopenharmony_ci */
46262306a36Sopenharmony_cistatic inline int check_txq_status(struct lio *lio)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	int numqs = lio->netdev->real_num_tx_queues;
46562306a36Sopenharmony_ci	int ret_val = 0;
46662306a36Sopenharmony_ci	int q, iq;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	/* check each sub-queue state */
46962306a36Sopenharmony_ci	for (q = 0; q < numqs; q++) {
47062306a36Sopenharmony_ci		iq = lio->linfo.txpciq[q %
47162306a36Sopenharmony_ci			lio->oct_dev->num_iqs].s.q_no;
47262306a36Sopenharmony_ci		if (octnet_iq_is_full(lio->oct_dev, iq))
47362306a36Sopenharmony_ci			continue;
47462306a36Sopenharmony_ci		if (__netif_subqueue_stopped(lio->netdev, q)) {
47562306a36Sopenharmony_ci			netif_wake_subqueue(lio->netdev, q);
47662306a36Sopenharmony_ci			INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, iq,
47762306a36Sopenharmony_ci						  tx_restart, 1);
47862306a36Sopenharmony_ci			ret_val++;
47962306a36Sopenharmony_ci		}
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	return ret_val;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci/**
48662306a36Sopenharmony_ci * print_link_info -  Print link information
48762306a36Sopenharmony_ci * @netdev: network device
48862306a36Sopenharmony_ci */
48962306a36Sopenharmony_cistatic void print_link_info(struct net_device *netdev)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	if (!ifstate_check(lio, LIO_IFSTATE_RESETTING) &&
49462306a36Sopenharmony_ci	    ifstate_check(lio, LIO_IFSTATE_REGISTERED)) {
49562306a36Sopenharmony_ci		struct oct_link_info *linfo = &lio->linfo;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci		if (linfo->link.s.link_up) {
49862306a36Sopenharmony_ci			netif_info(lio, link, lio->netdev, "%d Mbps %s Duplex UP\n",
49962306a36Sopenharmony_ci				   linfo->link.s.speed,
50062306a36Sopenharmony_ci				   (linfo->link.s.duplex) ? "Full" : "Half");
50162306a36Sopenharmony_ci		} else {
50262306a36Sopenharmony_ci			netif_info(lio, link, lio->netdev, "Link Down\n");
50362306a36Sopenharmony_ci		}
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci/**
50862306a36Sopenharmony_ci * octnet_link_status_change - Routine to notify MTU change
50962306a36Sopenharmony_ci * @work: work_struct data structure
51062306a36Sopenharmony_ci */
51162306a36Sopenharmony_cistatic void octnet_link_status_change(struct work_struct *work)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	struct cavium_wk *wk = (struct cavium_wk *)work;
51462306a36Sopenharmony_ci	struct lio *lio = (struct lio *)wk->ctxptr;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	/* lio->linfo.link.s.mtu always contains max MTU of the lio interface.
51762306a36Sopenharmony_ci	 * this API is invoked only when new max-MTU of the interface is
51862306a36Sopenharmony_ci	 * less than current MTU.
51962306a36Sopenharmony_ci	 */
52062306a36Sopenharmony_ci	rtnl_lock();
52162306a36Sopenharmony_ci	dev_set_mtu(lio->netdev, lio->linfo.link.s.mtu);
52262306a36Sopenharmony_ci	rtnl_unlock();
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci/**
52662306a36Sopenharmony_ci * setup_link_status_change_wq - Sets up the mtu status change work
52762306a36Sopenharmony_ci * @netdev: network device
52862306a36Sopenharmony_ci */
52962306a36Sopenharmony_cistatic inline int setup_link_status_change_wq(struct net_device *netdev)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
53262306a36Sopenharmony_ci	struct octeon_device *oct = lio->oct_dev;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	lio->link_status_wq.wq = alloc_workqueue("link-status",
53562306a36Sopenharmony_ci						 WQ_MEM_RECLAIM, 0);
53662306a36Sopenharmony_ci	if (!lio->link_status_wq.wq) {
53762306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "unable to create cavium link status wq\n");
53862306a36Sopenharmony_ci		return -1;
53962306a36Sopenharmony_ci	}
54062306a36Sopenharmony_ci	INIT_DELAYED_WORK(&lio->link_status_wq.wk.work,
54162306a36Sopenharmony_ci			  octnet_link_status_change);
54262306a36Sopenharmony_ci	lio->link_status_wq.wk.ctxptr = lio;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	return 0;
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic inline void cleanup_link_status_change_wq(struct net_device *netdev)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	if (lio->link_status_wq.wq) {
55262306a36Sopenharmony_ci		cancel_delayed_work_sync(&lio->link_status_wq.wk.work);
55362306a36Sopenharmony_ci		destroy_workqueue(lio->link_status_wq.wq);
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci/**
55862306a36Sopenharmony_ci * update_link_status - Update link status
55962306a36Sopenharmony_ci * @netdev: network device
56062306a36Sopenharmony_ci * @ls: link status structure
56162306a36Sopenharmony_ci *
56262306a36Sopenharmony_ci * Called on receipt of a link status response from the core application to
56362306a36Sopenharmony_ci * update each interface's link status.
56462306a36Sopenharmony_ci */
56562306a36Sopenharmony_cistatic inline void update_link_status(struct net_device *netdev,
56662306a36Sopenharmony_ci				      union oct_link_status *ls)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
56962306a36Sopenharmony_ci	int changed = (lio->linfo.link.u64 != ls->u64);
57062306a36Sopenharmony_ci	int current_max_mtu = lio->linfo.link.s.mtu;
57162306a36Sopenharmony_ci	struct octeon_device *oct = lio->oct_dev;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	dev_dbg(&oct->pci_dev->dev, "%s: lio->linfo.link.u64=%llx, ls->u64=%llx\n",
57462306a36Sopenharmony_ci		__func__, lio->linfo.link.u64, ls->u64);
57562306a36Sopenharmony_ci	lio->linfo.link.u64 = ls->u64;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	if ((lio->intf_open) && (changed)) {
57862306a36Sopenharmony_ci		print_link_info(netdev);
57962306a36Sopenharmony_ci		lio->link_changes++;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci		if (lio->linfo.link.s.link_up) {
58262306a36Sopenharmony_ci			dev_dbg(&oct->pci_dev->dev, "%s: link_up", __func__);
58362306a36Sopenharmony_ci			netif_carrier_on(netdev);
58462306a36Sopenharmony_ci			wake_txqs(netdev);
58562306a36Sopenharmony_ci		} else {
58662306a36Sopenharmony_ci			dev_dbg(&oct->pci_dev->dev, "%s: link_off", __func__);
58762306a36Sopenharmony_ci			netif_carrier_off(netdev);
58862306a36Sopenharmony_ci			stop_txqs(netdev);
58962306a36Sopenharmony_ci		}
59062306a36Sopenharmony_ci		if (lio->linfo.link.s.mtu != current_max_mtu) {
59162306a36Sopenharmony_ci			netif_info(lio, probe, lio->netdev, "Max MTU changed from %d to %d\n",
59262306a36Sopenharmony_ci				   current_max_mtu, lio->linfo.link.s.mtu);
59362306a36Sopenharmony_ci			netdev->max_mtu = lio->linfo.link.s.mtu;
59462306a36Sopenharmony_ci		}
59562306a36Sopenharmony_ci		if (lio->linfo.link.s.mtu < netdev->mtu) {
59662306a36Sopenharmony_ci			dev_warn(&oct->pci_dev->dev,
59762306a36Sopenharmony_ci				 "Current MTU is higher than new max MTU; Reducing the current mtu from %d to %d\n",
59862306a36Sopenharmony_ci				     netdev->mtu, lio->linfo.link.s.mtu);
59962306a36Sopenharmony_ci			queue_delayed_work(lio->link_status_wq.wq,
60062306a36Sopenharmony_ci					   &lio->link_status_wq.wk.work, 0);
60162306a36Sopenharmony_ci		}
60262306a36Sopenharmony_ci	}
60362306a36Sopenharmony_ci}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci/**
60662306a36Sopenharmony_ci * lio_sync_octeon_time - send latest localtime to octeon firmware so that
60762306a36Sopenharmony_ci * firmware will correct it's time, in case there is a time skew
60862306a36Sopenharmony_ci *
60962306a36Sopenharmony_ci * @work: work scheduled to send time update to octeon firmware
61062306a36Sopenharmony_ci **/
61162306a36Sopenharmony_cistatic void lio_sync_octeon_time(struct work_struct *work)
61262306a36Sopenharmony_ci{
61362306a36Sopenharmony_ci	struct cavium_wk *wk = (struct cavium_wk *)work;
61462306a36Sopenharmony_ci	struct lio *lio = (struct lio *)wk->ctxptr;
61562306a36Sopenharmony_ci	struct octeon_device *oct = lio->oct_dev;
61662306a36Sopenharmony_ci	struct octeon_soft_command *sc;
61762306a36Sopenharmony_ci	struct timespec64 ts;
61862306a36Sopenharmony_ci	struct lio_time *lt;
61962306a36Sopenharmony_ci	int ret;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	sc = octeon_alloc_soft_command(oct, sizeof(struct lio_time), 16, 0);
62262306a36Sopenharmony_ci	if (!sc) {
62362306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev,
62462306a36Sopenharmony_ci			"Failed to sync time to octeon: soft command allocation failed\n");
62562306a36Sopenharmony_ci		return;
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	lt = (struct lio_time *)sc->virtdptr;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	/* Get time of the day */
63162306a36Sopenharmony_ci	ktime_get_real_ts64(&ts);
63262306a36Sopenharmony_ci	lt->sec = ts.tv_sec;
63362306a36Sopenharmony_ci	lt->nsec = ts.tv_nsec;
63462306a36Sopenharmony_ci	octeon_swap_8B_data((u64 *)lt, (sizeof(struct lio_time)) / 8);
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	sc->iq_no = lio->linfo.txpciq[0].s.q_no;
63762306a36Sopenharmony_ci	octeon_prepare_soft_command(oct, sc, OPCODE_NIC,
63862306a36Sopenharmony_ci				    OPCODE_NIC_SYNC_OCTEON_TIME, 0, 0, 0);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	init_completion(&sc->complete);
64162306a36Sopenharmony_ci	sc->sc_status = OCTEON_REQUEST_PENDING;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	ret = octeon_send_soft_command(oct, sc);
64462306a36Sopenharmony_ci	if (ret == IQ_SEND_FAILED) {
64562306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev,
64662306a36Sopenharmony_ci			"Failed to sync time to octeon: failed to send soft command\n");
64762306a36Sopenharmony_ci		octeon_free_soft_command(oct, sc);
64862306a36Sopenharmony_ci	} else {
64962306a36Sopenharmony_ci		WRITE_ONCE(sc->caller_is_done, true);
65062306a36Sopenharmony_ci	}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	queue_delayed_work(lio->sync_octeon_time_wq.wq,
65362306a36Sopenharmony_ci			   &lio->sync_octeon_time_wq.wk.work,
65462306a36Sopenharmony_ci			   msecs_to_jiffies(LIO_SYNC_OCTEON_TIME_INTERVAL_MS));
65562306a36Sopenharmony_ci}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci/**
65862306a36Sopenharmony_ci * setup_sync_octeon_time_wq - prepare work to periodically update local time to octeon firmware
65962306a36Sopenharmony_ci *
66062306a36Sopenharmony_ci * @netdev: network device which should send time update to firmware
66162306a36Sopenharmony_ci **/
66262306a36Sopenharmony_cistatic inline int setup_sync_octeon_time_wq(struct net_device *netdev)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
66562306a36Sopenharmony_ci	struct octeon_device *oct = lio->oct_dev;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	lio->sync_octeon_time_wq.wq =
66862306a36Sopenharmony_ci		alloc_workqueue("update-octeon-time", WQ_MEM_RECLAIM, 0);
66962306a36Sopenharmony_ci	if (!lio->sync_octeon_time_wq.wq) {
67062306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "Unable to create wq to update octeon time\n");
67162306a36Sopenharmony_ci		return -1;
67262306a36Sopenharmony_ci	}
67362306a36Sopenharmony_ci	INIT_DELAYED_WORK(&lio->sync_octeon_time_wq.wk.work,
67462306a36Sopenharmony_ci			  lio_sync_octeon_time);
67562306a36Sopenharmony_ci	lio->sync_octeon_time_wq.wk.ctxptr = lio;
67662306a36Sopenharmony_ci	queue_delayed_work(lio->sync_octeon_time_wq.wq,
67762306a36Sopenharmony_ci			   &lio->sync_octeon_time_wq.wk.work,
67862306a36Sopenharmony_ci			   msecs_to_jiffies(LIO_SYNC_OCTEON_TIME_INTERVAL_MS));
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	return 0;
68162306a36Sopenharmony_ci}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci/**
68462306a36Sopenharmony_ci * cleanup_sync_octeon_time_wq - destroy wq
68562306a36Sopenharmony_ci *
68662306a36Sopenharmony_ci * @netdev: network device which should send time update to firmware
68762306a36Sopenharmony_ci *
68862306a36Sopenharmony_ci * Stop scheduling and destroy the work created to periodically update local
68962306a36Sopenharmony_ci * time to octeon firmware.
69062306a36Sopenharmony_ci **/
69162306a36Sopenharmony_cistatic inline void cleanup_sync_octeon_time_wq(struct net_device *netdev)
69262306a36Sopenharmony_ci{
69362306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
69462306a36Sopenharmony_ci	struct cavium_wq *time_wq = &lio->sync_octeon_time_wq;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	if (time_wq->wq) {
69762306a36Sopenharmony_ci		cancel_delayed_work_sync(&time_wq->wk.work);
69862306a36Sopenharmony_ci		destroy_workqueue(time_wq->wq);
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_cistatic struct octeon_device *get_other_octeon_device(struct octeon_device *oct)
70362306a36Sopenharmony_ci{
70462306a36Sopenharmony_ci	struct octeon_device *other_oct;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	other_oct = lio_get_device(oct->octeon_id + 1);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	if (other_oct && other_oct->pci_dev) {
70962306a36Sopenharmony_ci		int oct_busnum, other_oct_busnum;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci		oct_busnum = oct->pci_dev->bus->number;
71262306a36Sopenharmony_ci		other_oct_busnum = other_oct->pci_dev->bus->number;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci		if (oct_busnum == other_oct_busnum) {
71562306a36Sopenharmony_ci			int oct_slot, other_oct_slot;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci			oct_slot = PCI_SLOT(oct->pci_dev->devfn);
71862306a36Sopenharmony_ci			other_oct_slot = PCI_SLOT(other_oct->pci_dev->devfn);
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci			if (oct_slot == other_oct_slot)
72162306a36Sopenharmony_ci				return other_oct;
72262306a36Sopenharmony_ci		}
72362306a36Sopenharmony_ci	}
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	return NULL;
72662306a36Sopenharmony_ci}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_cistatic void disable_all_vf_links(struct octeon_device *oct)
72962306a36Sopenharmony_ci{
73062306a36Sopenharmony_ci	struct net_device *netdev;
73162306a36Sopenharmony_ci	int max_vfs, vf, i;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	if (!oct)
73462306a36Sopenharmony_ci		return;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	max_vfs = oct->sriov_info.max_vfs;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	for (i = 0; i < oct->ifcount; i++) {
73962306a36Sopenharmony_ci		netdev = oct->props[i].netdev;
74062306a36Sopenharmony_ci		if (!netdev)
74162306a36Sopenharmony_ci			continue;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci		for (vf = 0; vf < max_vfs; vf++)
74462306a36Sopenharmony_ci			liquidio_set_vf_link_state(netdev, vf,
74562306a36Sopenharmony_ci						   IFLA_VF_LINK_STATE_DISABLE);
74662306a36Sopenharmony_ci	}
74762306a36Sopenharmony_ci}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_cistatic int liquidio_watchdog(void *param)
75062306a36Sopenharmony_ci{
75162306a36Sopenharmony_ci	bool err_msg_was_printed[LIO_MAX_CORES];
75262306a36Sopenharmony_ci	u16 mask_of_crashed_or_stuck_cores = 0;
75362306a36Sopenharmony_ci	bool all_vf_links_are_disabled = false;
75462306a36Sopenharmony_ci	struct octeon_device *oct = param;
75562306a36Sopenharmony_ci	struct octeon_device *other_oct;
75662306a36Sopenharmony_ci#ifdef CONFIG_MODULE_UNLOAD
75762306a36Sopenharmony_ci	long refcount, vfs_referencing_pf;
75862306a36Sopenharmony_ci	u64 vfs_mask1, vfs_mask2;
75962306a36Sopenharmony_ci#endif
76062306a36Sopenharmony_ci	int core;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	memset(err_msg_was_printed, 0, sizeof(err_msg_was_printed));
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	while (!kthread_should_stop()) {
76562306a36Sopenharmony_ci		/* sleep for a couple of seconds so that we don't hog the CPU */
76662306a36Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
76762306a36Sopenharmony_ci		schedule_timeout(msecs_to_jiffies(2000));
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci		mask_of_crashed_or_stuck_cores =
77062306a36Sopenharmony_ci		    (u16)octeon_read_csr64(oct, CN23XX_SLI_SCRATCH2);
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci		if (!mask_of_crashed_or_stuck_cores)
77362306a36Sopenharmony_ci			continue;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci		WRITE_ONCE(oct->cores_crashed, true);
77662306a36Sopenharmony_ci		other_oct = get_other_octeon_device(oct);
77762306a36Sopenharmony_ci		if (other_oct)
77862306a36Sopenharmony_ci			WRITE_ONCE(other_oct->cores_crashed, true);
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci		for (core = 0; core < LIO_MAX_CORES; core++) {
78162306a36Sopenharmony_ci			bool core_crashed_or_got_stuck;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci			core_crashed_or_got_stuck =
78462306a36Sopenharmony_ci						(mask_of_crashed_or_stuck_cores
78562306a36Sopenharmony_ci						 >> core) & 1;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci			if (core_crashed_or_got_stuck &&
78862306a36Sopenharmony_ci			    !err_msg_was_printed[core]) {
78962306a36Sopenharmony_ci				dev_err(&oct->pci_dev->dev,
79062306a36Sopenharmony_ci					"ERROR: Octeon core %d crashed or got stuck!  See oct-fwdump for details.\n",
79162306a36Sopenharmony_ci					core);
79262306a36Sopenharmony_ci				err_msg_was_printed[core] = true;
79362306a36Sopenharmony_ci			}
79462306a36Sopenharmony_ci		}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci		if (all_vf_links_are_disabled)
79762306a36Sopenharmony_ci			continue;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci		disable_all_vf_links(oct);
80062306a36Sopenharmony_ci		disable_all_vf_links(other_oct);
80162306a36Sopenharmony_ci		all_vf_links_are_disabled = true;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci#ifdef CONFIG_MODULE_UNLOAD
80462306a36Sopenharmony_ci		vfs_mask1 = READ_ONCE(oct->sriov_info.vf_drv_loaded_mask);
80562306a36Sopenharmony_ci		vfs_mask2 = READ_ONCE(other_oct->sriov_info.vf_drv_loaded_mask);
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci		vfs_referencing_pf  = hweight64(vfs_mask1);
80862306a36Sopenharmony_ci		vfs_referencing_pf += hweight64(vfs_mask2);
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci		refcount = module_refcount(THIS_MODULE);
81162306a36Sopenharmony_ci		if (refcount >= vfs_referencing_pf) {
81262306a36Sopenharmony_ci			while (vfs_referencing_pf) {
81362306a36Sopenharmony_ci				module_put(THIS_MODULE);
81462306a36Sopenharmony_ci				vfs_referencing_pf--;
81562306a36Sopenharmony_ci			}
81662306a36Sopenharmony_ci		}
81762306a36Sopenharmony_ci#endif
81862306a36Sopenharmony_ci	}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	return 0;
82162306a36Sopenharmony_ci}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci/**
82462306a36Sopenharmony_ci * liquidio_probe - PCI probe handler
82562306a36Sopenharmony_ci * @pdev: PCI device structure
82662306a36Sopenharmony_ci * @ent: unused
82762306a36Sopenharmony_ci */
82862306a36Sopenharmony_cistatic int
82962306a36Sopenharmony_ciliquidio_probe(struct pci_dev *pdev, const struct pci_device_id __maybe_unused *ent)
83062306a36Sopenharmony_ci{
83162306a36Sopenharmony_ci	struct octeon_device *oct_dev = NULL;
83262306a36Sopenharmony_ci	struct handshake *hs;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	oct_dev = octeon_allocate_device(pdev->device,
83562306a36Sopenharmony_ci					 sizeof(struct octeon_device_priv));
83662306a36Sopenharmony_ci	if (!oct_dev) {
83762306a36Sopenharmony_ci		dev_err(&pdev->dev, "Unable to allocate device\n");
83862306a36Sopenharmony_ci		return -ENOMEM;
83962306a36Sopenharmony_ci	}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	if (pdev->device == OCTEON_CN23XX_PF_VID)
84262306a36Sopenharmony_ci		oct_dev->msix_on = LIO_FLAG_MSIX_ENABLED;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	/* Enable PTP for 6XXX Device */
84562306a36Sopenharmony_ci	if (((pdev->device == OCTEON_CN66XX) ||
84662306a36Sopenharmony_ci	     (pdev->device == OCTEON_CN68XX)))
84762306a36Sopenharmony_ci		oct_dev->ptp_enable = true;
84862306a36Sopenharmony_ci	else
84962306a36Sopenharmony_ci		oct_dev->ptp_enable = false;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	dev_info(&pdev->dev, "Initializing device %x:%x.\n",
85262306a36Sopenharmony_ci		 (u32)pdev->vendor, (u32)pdev->device);
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	/* Assign octeon_device for this device to the private data area. */
85562306a36Sopenharmony_ci	pci_set_drvdata(pdev, oct_dev);
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	/* set linux specific device pointer */
85862306a36Sopenharmony_ci	oct_dev->pci_dev = (void *)pdev;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	oct_dev->subsystem_id = pdev->subsystem_vendor |
86162306a36Sopenharmony_ci		(pdev->subsystem_device << 16);
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	hs = &handshake[oct_dev->octeon_id];
86462306a36Sopenharmony_ci	init_completion(&hs->init);
86562306a36Sopenharmony_ci	init_completion(&hs->started);
86662306a36Sopenharmony_ci	hs->pci_dev = pdev;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	if (oct_dev->octeon_id == 0)
86962306a36Sopenharmony_ci		/* first LiquidIO NIC is detected */
87062306a36Sopenharmony_ci		complete(&first_stage);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	if (octeon_device_init(oct_dev)) {
87362306a36Sopenharmony_ci		complete(&hs->init);
87462306a36Sopenharmony_ci		liquidio_remove(pdev);
87562306a36Sopenharmony_ci		return -ENOMEM;
87662306a36Sopenharmony_ci	}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	if (OCTEON_CN23XX_PF(oct_dev)) {
87962306a36Sopenharmony_ci		u8 bus, device, function;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci		if (atomic_read(oct_dev->adapter_refcount) == 1) {
88262306a36Sopenharmony_ci			/* Each NIC gets one watchdog kernel thread.  The first
88362306a36Sopenharmony_ci			 * PF (of each NIC) that gets pci_driver->probe()'d
88462306a36Sopenharmony_ci			 * creates that thread.
88562306a36Sopenharmony_ci			 */
88662306a36Sopenharmony_ci			bus = pdev->bus->number;
88762306a36Sopenharmony_ci			device = PCI_SLOT(pdev->devfn);
88862306a36Sopenharmony_ci			function = PCI_FUNC(pdev->devfn);
88962306a36Sopenharmony_ci			oct_dev->watchdog_task = kthread_run(liquidio_watchdog,
89062306a36Sopenharmony_ci							     oct_dev,
89162306a36Sopenharmony_ci							     "liowd/%02hhx:%02hhx.%hhx",
89262306a36Sopenharmony_ci							     bus, device, function);
89362306a36Sopenharmony_ci			if (IS_ERR(oct_dev->watchdog_task)) {
89462306a36Sopenharmony_ci				oct_dev->watchdog_task = NULL;
89562306a36Sopenharmony_ci				dev_err(&oct_dev->pci_dev->dev,
89662306a36Sopenharmony_ci					"failed to create kernel_thread\n");
89762306a36Sopenharmony_ci				liquidio_remove(pdev);
89862306a36Sopenharmony_ci				return -1;
89962306a36Sopenharmony_ci			}
90062306a36Sopenharmony_ci		}
90162306a36Sopenharmony_ci	}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	oct_dev->rx_pause = 1;
90462306a36Sopenharmony_ci	oct_dev->tx_pause = 1;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	dev_dbg(&oct_dev->pci_dev->dev, "Device is ready\n");
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	return 0;
90962306a36Sopenharmony_ci}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_cistatic bool fw_type_is_auto(void)
91262306a36Sopenharmony_ci{
91362306a36Sopenharmony_ci	return strncmp(fw_type, LIO_FW_NAME_TYPE_AUTO,
91462306a36Sopenharmony_ci		       sizeof(LIO_FW_NAME_TYPE_AUTO)) == 0;
91562306a36Sopenharmony_ci}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci/**
91862306a36Sopenharmony_ci * octeon_pci_flr - PCI FLR for each Octeon device.
91962306a36Sopenharmony_ci * @oct: octeon device
92062306a36Sopenharmony_ci */
92162306a36Sopenharmony_cistatic void octeon_pci_flr(struct octeon_device *oct)
92262306a36Sopenharmony_ci{
92362306a36Sopenharmony_ci	int rc;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	pci_save_state(oct->pci_dev);
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	pci_cfg_access_lock(oct->pci_dev);
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	/* Quiesce the device completely */
93062306a36Sopenharmony_ci	pci_write_config_word(oct->pci_dev, PCI_COMMAND,
93162306a36Sopenharmony_ci			      PCI_COMMAND_INTX_DISABLE);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	rc = __pci_reset_function_locked(oct->pci_dev);
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	if (rc != 0)
93662306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "Error %d resetting PCI function %d\n",
93762306a36Sopenharmony_ci			rc, oct->pf_num);
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	pci_cfg_access_unlock(oct->pci_dev);
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	pci_restore_state(oct->pci_dev);
94262306a36Sopenharmony_ci}
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci/**
94562306a36Sopenharmony_ci * octeon_destroy_resources - Destroy resources associated with octeon device
94662306a36Sopenharmony_ci * @oct: octeon device
94762306a36Sopenharmony_ci */
94862306a36Sopenharmony_cistatic void octeon_destroy_resources(struct octeon_device *oct)
94962306a36Sopenharmony_ci{
95062306a36Sopenharmony_ci	int i, refcount;
95162306a36Sopenharmony_ci	struct msix_entry *msix_entries;
95262306a36Sopenharmony_ci	struct octeon_device_priv *oct_priv = oct->priv;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	struct handshake *hs;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	switch (atomic_read(&oct->status)) {
95762306a36Sopenharmony_ci	case OCT_DEV_RUNNING:
95862306a36Sopenharmony_ci	case OCT_DEV_CORE_OK:
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci		/* No more instructions will be forwarded. */
96162306a36Sopenharmony_ci		atomic_set(&oct->status, OCT_DEV_IN_RESET);
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci		oct->app_mode = CVM_DRV_INVALID_APP;
96462306a36Sopenharmony_ci		dev_dbg(&oct->pci_dev->dev, "Device state is now %s\n",
96562306a36Sopenharmony_ci			lio_get_state_string(&oct->status));
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci		schedule_timeout_uninterruptible(HZ / 10);
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci		fallthrough;
97062306a36Sopenharmony_ci	case OCT_DEV_HOST_OK:
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	case OCT_DEV_CONSOLE_INIT_DONE:
97362306a36Sopenharmony_ci		/* Remove any consoles */
97462306a36Sopenharmony_ci		octeon_remove_consoles(oct);
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci		fallthrough;
97762306a36Sopenharmony_ci	case OCT_DEV_IO_QUEUES_DONE:
97862306a36Sopenharmony_ci		if (lio_wait_for_instr_fetch(oct))
97962306a36Sopenharmony_ci			dev_err(&oct->pci_dev->dev, "IQ had pending instructions\n");
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci		if (wait_for_pending_requests(oct))
98262306a36Sopenharmony_ci			dev_err(&oct->pci_dev->dev, "There were pending requests\n");
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci		/* Disable the input and output queues now. No more packets will
98562306a36Sopenharmony_ci		 * arrive from Octeon, but we should wait for all packet
98662306a36Sopenharmony_ci		 * processing to finish.
98762306a36Sopenharmony_ci		 */
98862306a36Sopenharmony_ci		oct->fn_list.disable_io_queues(oct);
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci		if (lio_wait_for_oq_pkts(oct))
99162306a36Sopenharmony_ci			dev_err(&oct->pci_dev->dev, "OQ had pending packets\n");
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci		/* Force all requests waiting to be fetched by OCTEON to
99462306a36Sopenharmony_ci		 * complete.
99562306a36Sopenharmony_ci		 */
99662306a36Sopenharmony_ci		for (i = 0; i < MAX_OCTEON_INSTR_QUEUES(oct); i++) {
99762306a36Sopenharmony_ci			struct octeon_instr_queue *iq;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci			if (!(oct->io_qmask.iq & BIT_ULL(i)))
100062306a36Sopenharmony_ci				continue;
100162306a36Sopenharmony_ci			iq = oct->instr_queue[i];
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci			if (atomic_read(&iq->instr_pending)) {
100462306a36Sopenharmony_ci				spin_lock_bh(&iq->lock);
100562306a36Sopenharmony_ci				iq->fill_cnt = 0;
100662306a36Sopenharmony_ci				iq->octeon_read_index = iq->host_write_index;
100762306a36Sopenharmony_ci				iq->stats.instr_processed +=
100862306a36Sopenharmony_ci					atomic_read(&iq->instr_pending);
100962306a36Sopenharmony_ci				lio_process_iq_request_list(oct, iq, 0);
101062306a36Sopenharmony_ci				spin_unlock_bh(&iq->lock);
101162306a36Sopenharmony_ci			}
101262306a36Sopenharmony_ci		}
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci		lio_process_ordered_list(oct, 1);
101562306a36Sopenharmony_ci		octeon_free_sc_done_list(oct);
101662306a36Sopenharmony_ci		octeon_free_sc_zombie_list(oct);
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci		fallthrough;
101962306a36Sopenharmony_ci	case OCT_DEV_INTR_SET_DONE:
102062306a36Sopenharmony_ci		/* Disable interrupts  */
102162306a36Sopenharmony_ci		oct->fn_list.disable_interrupt(oct, OCTEON_ALL_INTR);
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci		if (oct->msix_on) {
102462306a36Sopenharmony_ci			msix_entries = (struct msix_entry *)oct->msix_entries;
102562306a36Sopenharmony_ci			for (i = 0; i < oct->num_msix_irqs - 1; i++) {
102662306a36Sopenharmony_ci				if (oct->ioq_vector[i].vector) {
102762306a36Sopenharmony_ci					/* clear the affinity_cpumask */
102862306a36Sopenharmony_ci					irq_set_affinity_hint(
102962306a36Sopenharmony_ci							msix_entries[i].vector,
103062306a36Sopenharmony_ci							NULL);
103162306a36Sopenharmony_ci					free_irq(msix_entries[i].vector,
103262306a36Sopenharmony_ci						 &oct->ioq_vector[i]);
103362306a36Sopenharmony_ci					oct->ioq_vector[i].vector = 0;
103462306a36Sopenharmony_ci				}
103562306a36Sopenharmony_ci			}
103662306a36Sopenharmony_ci			/* non-iov vector's argument is oct struct */
103762306a36Sopenharmony_ci			free_irq(msix_entries[i].vector, oct);
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci			pci_disable_msix(oct->pci_dev);
104062306a36Sopenharmony_ci			kfree(oct->msix_entries);
104162306a36Sopenharmony_ci			oct->msix_entries = NULL;
104262306a36Sopenharmony_ci		} else {
104362306a36Sopenharmony_ci			/* Release the interrupt line */
104462306a36Sopenharmony_ci			free_irq(oct->pci_dev->irq, oct);
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci			if (oct->flags & LIO_FLAG_MSI_ENABLED)
104762306a36Sopenharmony_ci				pci_disable_msi(oct->pci_dev);
104862306a36Sopenharmony_ci		}
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci		kfree(oct->irq_name_storage);
105162306a36Sopenharmony_ci		oct->irq_name_storage = NULL;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci		fallthrough;
105462306a36Sopenharmony_ci	case OCT_DEV_MSIX_ALLOC_VECTOR_DONE:
105562306a36Sopenharmony_ci		if (OCTEON_CN23XX_PF(oct))
105662306a36Sopenharmony_ci			octeon_free_ioq_vector(oct);
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci		fallthrough;
105962306a36Sopenharmony_ci	case OCT_DEV_MBOX_SETUP_DONE:
106062306a36Sopenharmony_ci		if (OCTEON_CN23XX_PF(oct))
106162306a36Sopenharmony_ci			oct->fn_list.free_mbox(oct);
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci		fallthrough;
106462306a36Sopenharmony_ci	case OCT_DEV_IN_RESET:
106562306a36Sopenharmony_ci	case OCT_DEV_DROQ_INIT_DONE:
106662306a36Sopenharmony_ci		/* Wait for any pending operations */
106762306a36Sopenharmony_ci		mdelay(100);
106862306a36Sopenharmony_ci		for (i = 0; i < MAX_OCTEON_OUTPUT_QUEUES(oct); i++) {
106962306a36Sopenharmony_ci			if (!(oct->io_qmask.oq & BIT_ULL(i)))
107062306a36Sopenharmony_ci				continue;
107162306a36Sopenharmony_ci			octeon_delete_droq(oct, i);
107262306a36Sopenharmony_ci		}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci		/* Force any pending handshakes to complete */
107562306a36Sopenharmony_ci		for (i = 0; i < MAX_OCTEON_DEVICES; i++) {
107662306a36Sopenharmony_ci			hs = &handshake[i];
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci			if (hs->pci_dev) {
107962306a36Sopenharmony_ci				handshake[oct->octeon_id].init_ok = 0;
108062306a36Sopenharmony_ci				complete(&handshake[oct->octeon_id].init);
108162306a36Sopenharmony_ci				handshake[oct->octeon_id].started_ok = 0;
108262306a36Sopenharmony_ci				complete(&handshake[oct->octeon_id].started);
108362306a36Sopenharmony_ci			}
108462306a36Sopenharmony_ci		}
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci		fallthrough;
108762306a36Sopenharmony_ci	case OCT_DEV_RESP_LIST_INIT_DONE:
108862306a36Sopenharmony_ci		octeon_delete_response_list(oct);
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci		fallthrough;
109162306a36Sopenharmony_ci	case OCT_DEV_INSTR_QUEUE_INIT_DONE:
109262306a36Sopenharmony_ci		for (i = 0; i < MAX_OCTEON_INSTR_QUEUES(oct); i++) {
109362306a36Sopenharmony_ci			if (!(oct->io_qmask.iq & BIT_ULL(i)))
109462306a36Sopenharmony_ci				continue;
109562306a36Sopenharmony_ci			octeon_delete_instr_queue(oct, i);
109662306a36Sopenharmony_ci		}
109762306a36Sopenharmony_ci#ifdef CONFIG_PCI_IOV
109862306a36Sopenharmony_ci		if (oct->sriov_info.sriov_enabled)
109962306a36Sopenharmony_ci			pci_disable_sriov(oct->pci_dev);
110062306a36Sopenharmony_ci#endif
110162306a36Sopenharmony_ci		fallthrough;
110262306a36Sopenharmony_ci	case OCT_DEV_SC_BUFF_POOL_INIT_DONE:
110362306a36Sopenharmony_ci		octeon_free_sc_buffer_pool(oct);
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci		fallthrough;
110662306a36Sopenharmony_ci	case OCT_DEV_DISPATCH_INIT_DONE:
110762306a36Sopenharmony_ci		octeon_delete_dispatch_list(oct);
110862306a36Sopenharmony_ci		cancel_delayed_work_sync(&oct->nic_poll_work.work);
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci		fallthrough;
111162306a36Sopenharmony_ci	case OCT_DEV_PCI_MAP_DONE:
111262306a36Sopenharmony_ci		refcount = octeon_deregister_device(oct);
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci		/* Soft reset the octeon device before exiting.
111562306a36Sopenharmony_ci		 * However, if fw was loaded from card (i.e. autoboot),
111662306a36Sopenharmony_ci		 * perform an FLR instead.
111762306a36Sopenharmony_ci		 * Implementation note: only soft-reset the device
111862306a36Sopenharmony_ci		 * if it is a CN6XXX OR the LAST CN23XX device.
111962306a36Sopenharmony_ci		 */
112062306a36Sopenharmony_ci		if (atomic_read(oct->adapter_fw_state) == FW_IS_PRELOADED)
112162306a36Sopenharmony_ci			octeon_pci_flr(oct);
112262306a36Sopenharmony_ci		else if (OCTEON_CN6XXX(oct) || !refcount)
112362306a36Sopenharmony_ci			oct->fn_list.soft_reset(oct);
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci		octeon_unmap_pci_barx(oct, 0);
112662306a36Sopenharmony_ci		octeon_unmap_pci_barx(oct, 1);
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci		fallthrough;
112962306a36Sopenharmony_ci	case OCT_DEV_PCI_ENABLE_DONE:
113062306a36Sopenharmony_ci		/* Disable the device, releasing the PCI INT */
113162306a36Sopenharmony_ci		pci_disable_device(oct->pci_dev);
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci		fallthrough;
113462306a36Sopenharmony_ci	case OCT_DEV_BEGIN_STATE:
113562306a36Sopenharmony_ci		/* Nothing to be done here either */
113662306a36Sopenharmony_ci		break;
113762306a36Sopenharmony_ci	}                       /* end switch (oct->status) */
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	tasklet_kill(&oct_priv->droq_tasklet);
114062306a36Sopenharmony_ci}
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci/**
114362306a36Sopenharmony_ci * send_rx_ctrl_cmd - Send Rx control command
114462306a36Sopenharmony_ci * @lio: per-network private data
114562306a36Sopenharmony_ci * @start_stop: whether to start or stop
114662306a36Sopenharmony_ci */
114762306a36Sopenharmony_cistatic int send_rx_ctrl_cmd(struct lio *lio, int start_stop)
114862306a36Sopenharmony_ci{
114962306a36Sopenharmony_ci	struct octeon_soft_command *sc;
115062306a36Sopenharmony_ci	union octnet_cmd *ncmd;
115162306a36Sopenharmony_ci	struct octeon_device *oct = (struct octeon_device *)lio->oct_dev;
115262306a36Sopenharmony_ci	int retval;
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	if (oct->props[lio->ifidx].rx_on == start_stop)
115562306a36Sopenharmony_ci		return 0;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	sc = (struct octeon_soft_command *)
115862306a36Sopenharmony_ci		octeon_alloc_soft_command(oct, OCTNET_CMD_SIZE,
115962306a36Sopenharmony_ci					  16, 0);
116062306a36Sopenharmony_ci	if (!sc) {
116162306a36Sopenharmony_ci		netif_info(lio, rx_err, lio->netdev,
116262306a36Sopenharmony_ci			   "Failed to allocate octeon_soft_command struct\n");
116362306a36Sopenharmony_ci		return -ENOMEM;
116462306a36Sopenharmony_ci	}
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	ncmd = (union octnet_cmd *)sc->virtdptr;
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	ncmd->u64 = 0;
116962306a36Sopenharmony_ci	ncmd->s.cmd = OCTNET_CMD_RX_CTL;
117062306a36Sopenharmony_ci	ncmd->s.param1 = start_stop;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	octeon_swap_8B_data((u64 *)ncmd, (OCTNET_CMD_SIZE >> 3));
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	sc->iq_no = lio->linfo.txpciq[0].s.q_no;
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	octeon_prepare_soft_command(oct, sc, OPCODE_NIC,
117762306a36Sopenharmony_ci				    OPCODE_NIC_CMD, 0, 0, 0);
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	init_completion(&sc->complete);
118062306a36Sopenharmony_ci	sc->sc_status = OCTEON_REQUEST_PENDING;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	retval = octeon_send_soft_command(oct, sc);
118362306a36Sopenharmony_ci	if (retval == IQ_SEND_FAILED) {
118462306a36Sopenharmony_ci		netif_info(lio, rx_err, lio->netdev, "Failed to send RX Control message\n");
118562306a36Sopenharmony_ci		octeon_free_soft_command(oct, sc);
118662306a36Sopenharmony_ci	} else {
118762306a36Sopenharmony_ci		/* Sleep on a wait queue till the cond flag indicates that the
118862306a36Sopenharmony_ci		 * response arrived or timed-out.
118962306a36Sopenharmony_ci		 */
119062306a36Sopenharmony_ci		retval = wait_for_sc_completion_timeout(oct, sc, 0);
119162306a36Sopenharmony_ci		if (retval)
119262306a36Sopenharmony_ci			return retval;
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci		oct->props[lio->ifidx].rx_on = start_stop;
119562306a36Sopenharmony_ci		WRITE_ONCE(sc->caller_is_done, true);
119662306a36Sopenharmony_ci	}
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	return retval;
119962306a36Sopenharmony_ci}
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci/**
120262306a36Sopenharmony_ci * liquidio_destroy_nic_device - Destroy NIC device interface
120362306a36Sopenharmony_ci * @oct: octeon device
120462306a36Sopenharmony_ci * @ifidx: which interface to destroy
120562306a36Sopenharmony_ci *
120662306a36Sopenharmony_ci * Cleanup associated with each interface for an Octeon device  when NIC
120762306a36Sopenharmony_ci * module is being unloaded or if initialization fails during load.
120862306a36Sopenharmony_ci */
120962306a36Sopenharmony_cistatic void liquidio_destroy_nic_device(struct octeon_device *oct, int ifidx)
121062306a36Sopenharmony_ci{
121162306a36Sopenharmony_ci	struct net_device *netdev = oct->props[ifidx].netdev;
121262306a36Sopenharmony_ci	struct octeon_device_priv *oct_priv = oct->priv;
121362306a36Sopenharmony_ci	struct napi_struct *napi, *n;
121462306a36Sopenharmony_ci	struct lio *lio;
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	if (!netdev) {
121762306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "%s No netdevice ptr for index %d\n",
121862306a36Sopenharmony_ci			__func__, ifidx);
121962306a36Sopenharmony_ci		return;
122062306a36Sopenharmony_ci	}
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	lio = GET_LIO(netdev);
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	dev_dbg(&oct->pci_dev->dev, "NIC device cleanup\n");
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	if (atomic_read(&lio->ifstate) & LIO_IFSTATE_RUNNING)
122762306a36Sopenharmony_ci		liquidio_stop(netdev);
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	if (oct->props[lio->ifidx].napi_enabled == 1) {
123062306a36Sopenharmony_ci		list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list)
123162306a36Sopenharmony_ci			napi_disable(napi);
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci		oct->props[lio->ifidx].napi_enabled = 0;
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci		if (OCTEON_CN23XX_PF(oct))
123662306a36Sopenharmony_ci			oct->droq[0]->ops.poll_mode = 0;
123762306a36Sopenharmony_ci	}
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	/* Delete NAPI */
124062306a36Sopenharmony_ci	list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list)
124162306a36Sopenharmony_ci		netif_napi_del(napi);
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	tasklet_enable(&oct_priv->droq_tasklet);
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	if (atomic_read(&lio->ifstate) & LIO_IFSTATE_REGISTERED)
124662306a36Sopenharmony_ci		unregister_netdev(netdev);
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	cleanup_sync_octeon_time_wq(netdev);
124962306a36Sopenharmony_ci	cleanup_link_status_change_wq(netdev);
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	cleanup_rx_oom_poll_fn(netdev);
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	lio_delete_glists(lio);
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	free_netdev(netdev);
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	oct->props[ifidx].gmxport = -1;
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	oct->props[ifidx].netdev = NULL;
126062306a36Sopenharmony_ci}
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci/**
126362306a36Sopenharmony_ci * liquidio_stop_nic_module - Stop complete NIC functionality
126462306a36Sopenharmony_ci * @oct: octeon device
126562306a36Sopenharmony_ci */
126662306a36Sopenharmony_cistatic int liquidio_stop_nic_module(struct octeon_device *oct)
126762306a36Sopenharmony_ci{
126862306a36Sopenharmony_ci	int i, j;
126962306a36Sopenharmony_ci	struct lio *lio;
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	dev_dbg(&oct->pci_dev->dev, "Stopping network interfaces\n");
127262306a36Sopenharmony_ci	device_lock(&oct->pci_dev->dev);
127362306a36Sopenharmony_ci	if (oct->devlink) {
127462306a36Sopenharmony_ci		devlink_unregister(oct->devlink);
127562306a36Sopenharmony_ci		devlink_free(oct->devlink);
127662306a36Sopenharmony_ci		oct->devlink = NULL;
127762306a36Sopenharmony_ci	}
127862306a36Sopenharmony_ci	device_unlock(&oct->pci_dev->dev);
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	if (!oct->ifcount) {
128162306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "Init for Octeon was not completed\n");
128262306a36Sopenharmony_ci		return 1;
128362306a36Sopenharmony_ci	}
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	spin_lock_bh(&oct->cmd_resp_wqlock);
128662306a36Sopenharmony_ci	oct->cmd_resp_state = OCT_DRV_OFFLINE;
128762306a36Sopenharmony_ci	spin_unlock_bh(&oct->cmd_resp_wqlock);
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	lio_vf_rep_destroy(oct);
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	for (i = 0; i < oct->ifcount; i++) {
129262306a36Sopenharmony_ci		lio = GET_LIO(oct->props[i].netdev);
129362306a36Sopenharmony_ci		for (j = 0; j < oct->num_oqs; j++)
129462306a36Sopenharmony_ci			octeon_unregister_droq_ops(oct,
129562306a36Sopenharmony_ci						   lio->linfo.rxpciq[j].s.q_no);
129662306a36Sopenharmony_ci	}
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	for (i = 0; i < oct->ifcount; i++)
129962306a36Sopenharmony_ci		liquidio_destroy_nic_device(oct, i);
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	dev_dbg(&oct->pci_dev->dev, "Network interfaces stopped\n");
130262306a36Sopenharmony_ci	return 0;
130362306a36Sopenharmony_ci}
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci/**
130662306a36Sopenharmony_ci * liquidio_remove - Cleans up resources at unload time
130762306a36Sopenharmony_ci * @pdev: PCI device structure
130862306a36Sopenharmony_ci */
130962306a36Sopenharmony_cistatic void liquidio_remove(struct pci_dev *pdev)
131062306a36Sopenharmony_ci{
131162306a36Sopenharmony_ci	struct octeon_device *oct_dev = pci_get_drvdata(pdev);
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	dev_dbg(&oct_dev->pci_dev->dev, "Stopping device\n");
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	if (oct_dev->watchdog_task)
131662306a36Sopenharmony_ci		kthread_stop(oct_dev->watchdog_task);
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	if (!oct_dev->octeon_id &&
131962306a36Sopenharmony_ci	    oct_dev->fw_info.app_cap_flags & LIQUIDIO_SWITCHDEV_CAP)
132062306a36Sopenharmony_ci		lio_vf_rep_modexit();
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	if (oct_dev->app_mode && (oct_dev->app_mode == CVM_DRV_NIC_APP))
132362306a36Sopenharmony_ci		liquidio_stop_nic_module(oct_dev);
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	/* Reset the octeon device and cleanup all memory allocated for
132662306a36Sopenharmony_ci	 * the octeon device by driver.
132762306a36Sopenharmony_ci	 */
132862306a36Sopenharmony_ci	octeon_destroy_resources(oct_dev);
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	dev_info(&oct_dev->pci_dev->dev, "Device removed\n");
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	/* This octeon device has been removed. Update the global
133362306a36Sopenharmony_ci	 * data structure to reflect this. Free the device structure.
133462306a36Sopenharmony_ci	 */
133562306a36Sopenharmony_ci	octeon_free_device_mem(oct_dev);
133662306a36Sopenharmony_ci}
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci/**
133962306a36Sopenharmony_ci * octeon_chip_specific_setup - Identify the Octeon device and to map the BAR address space
134062306a36Sopenharmony_ci * @oct: octeon device
134162306a36Sopenharmony_ci */
134262306a36Sopenharmony_cistatic int octeon_chip_specific_setup(struct octeon_device *oct)
134362306a36Sopenharmony_ci{
134462306a36Sopenharmony_ci	u32 dev_id, rev_id;
134562306a36Sopenharmony_ci	int ret = 1;
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	pci_read_config_dword(oct->pci_dev, 0, &dev_id);
134862306a36Sopenharmony_ci	pci_read_config_dword(oct->pci_dev, 8, &rev_id);
134962306a36Sopenharmony_ci	oct->rev_id = rev_id & 0xff;
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	switch (dev_id) {
135262306a36Sopenharmony_ci	case OCTEON_CN68XX_PCIID:
135362306a36Sopenharmony_ci		oct->chip_id = OCTEON_CN68XX;
135462306a36Sopenharmony_ci		ret = lio_setup_cn68xx_octeon_device(oct);
135562306a36Sopenharmony_ci		break;
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	case OCTEON_CN66XX_PCIID:
135862306a36Sopenharmony_ci		oct->chip_id = OCTEON_CN66XX;
135962306a36Sopenharmony_ci		ret = lio_setup_cn66xx_octeon_device(oct);
136062306a36Sopenharmony_ci		break;
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	case OCTEON_CN23XX_PCIID_PF:
136362306a36Sopenharmony_ci		oct->chip_id = OCTEON_CN23XX_PF_VID;
136462306a36Sopenharmony_ci		ret = setup_cn23xx_octeon_pf_device(oct);
136562306a36Sopenharmony_ci		if (ret)
136662306a36Sopenharmony_ci			break;
136762306a36Sopenharmony_ci#ifdef CONFIG_PCI_IOV
136862306a36Sopenharmony_ci		if (!ret)
136962306a36Sopenharmony_ci			pci_sriov_set_totalvfs(oct->pci_dev,
137062306a36Sopenharmony_ci					       oct->sriov_info.max_vfs);
137162306a36Sopenharmony_ci#endif
137262306a36Sopenharmony_ci		break;
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	default:
137562306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "Unknown device found (dev_id: %x)\n",
137662306a36Sopenharmony_ci			dev_id);
137762306a36Sopenharmony_ci	}
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	return ret;
138062306a36Sopenharmony_ci}
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci/**
138362306a36Sopenharmony_ci * octeon_pci_os_setup - PCI initialization for each Octeon device.
138462306a36Sopenharmony_ci * @oct: octeon device
138562306a36Sopenharmony_ci */
138662306a36Sopenharmony_cistatic int octeon_pci_os_setup(struct octeon_device *oct)
138762306a36Sopenharmony_ci{
138862306a36Sopenharmony_ci	/* setup PCI stuff first */
138962306a36Sopenharmony_ci	if (pci_enable_device(oct->pci_dev)) {
139062306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "pci_enable_device failed\n");
139162306a36Sopenharmony_ci		return 1;
139262306a36Sopenharmony_ci	}
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	if (dma_set_mask_and_coherent(&oct->pci_dev->dev, DMA_BIT_MASK(64))) {
139562306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "Unexpected DMA device capability\n");
139662306a36Sopenharmony_ci		pci_disable_device(oct->pci_dev);
139762306a36Sopenharmony_ci		return 1;
139862306a36Sopenharmony_ci	}
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	/* Enable PCI DMA Master. */
140162306a36Sopenharmony_ci	pci_set_master(oct->pci_dev);
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	return 0;
140462306a36Sopenharmony_ci}
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci/**
140762306a36Sopenharmony_ci * free_netbuf - Unmap and free network buffer
140862306a36Sopenharmony_ci * @buf: buffer
140962306a36Sopenharmony_ci */
141062306a36Sopenharmony_cistatic void free_netbuf(void *buf)
141162306a36Sopenharmony_ci{
141262306a36Sopenharmony_ci	struct sk_buff *skb;
141362306a36Sopenharmony_ci	struct octnet_buf_free_info *finfo;
141462306a36Sopenharmony_ci	struct lio *lio;
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	finfo = (struct octnet_buf_free_info *)buf;
141762306a36Sopenharmony_ci	skb = finfo->skb;
141862306a36Sopenharmony_ci	lio = finfo->lio;
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	dma_unmap_single(&lio->oct_dev->pci_dev->dev, finfo->dptr, skb->len,
142162306a36Sopenharmony_ci			 DMA_TO_DEVICE);
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	tx_buffer_free(skb);
142462306a36Sopenharmony_ci}
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci/**
142762306a36Sopenharmony_ci * free_netsgbuf - Unmap and free gather buffer
142862306a36Sopenharmony_ci * @buf: buffer
142962306a36Sopenharmony_ci */
143062306a36Sopenharmony_cistatic void free_netsgbuf(void *buf)
143162306a36Sopenharmony_ci{
143262306a36Sopenharmony_ci	struct octnet_buf_free_info *finfo;
143362306a36Sopenharmony_ci	struct sk_buff *skb;
143462306a36Sopenharmony_ci	struct lio *lio;
143562306a36Sopenharmony_ci	struct octnic_gather *g;
143662306a36Sopenharmony_ci	int i, frags, iq;
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	finfo = (struct octnet_buf_free_info *)buf;
143962306a36Sopenharmony_ci	skb = finfo->skb;
144062306a36Sopenharmony_ci	lio = finfo->lio;
144162306a36Sopenharmony_ci	g = finfo->g;
144262306a36Sopenharmony_ci	frags = skb_shinfo(skb)->nr_frags;
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	dma_unmap_single(&lio->oct_dev->pci_dev->dev,
144562306a36Sopenharmony_ci			 g->sg[0].ptr[0], (skb->len - skb->data_len),
144662306a36Sopenharmony_ci			 DMA_TO_DEVICE);
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci	i = 1;
144962306a36Sopenharmony_ci	while (frags--) {
145062306a36Sopenharmony_ci		skb_frag_t *frag = &skb_shinfo(skb)->frags[i - 1];
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci		dma_unmap_page(&lio->oct_dev->pci_dev->dev,
145362306a36Sopenharmony_ci			       g->sg[(i >> 2)].ptr[(i & 3)],
145462306a36Sopenharmony_ci			       skb_frag_size(frag), DMA_TO_DEVICE);
145562306a36Sopenharmony_ci		i++;
145662306a36Sopenharmony_ci	}
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	iq = skb_iq(lio->oct_dev, skb);
145962306a36Sopenharmony_ci	spin_lock(&lio->glist_lock[iq]);
146062306a36Sopenharmony_ci	list_add_tail(&g->list, &lio->glist[iq]);
146162306a36Sopenharmony_ci	spin_unlock(&lio->glist_lock[iq]);
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	tx_buffer_free(skb);
146462306a36Sopenharmony_ci}
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci/**
146762306a36Sopenharmony_ci * free_netsgbuf_with_resp - Unmap and free gather buffer with response
146862306a36Sopenharmony_ci * @buf: buffer
146962306a36Sopenharmony_ci */
147062306a36Sopenharmony_cistatic void free_netsgbuf_with_resp(void *buf)
147162306a36Sopenharmony_ci{
147262306a36Sopenharmony_ci	struct octeon_soft_command *sc;
147362306a36Sopenharmony_ci	struct octnet_buf_free_info *finfo;
147462306a36Sopenharmony_ci	struct sk_buff *skb;
147562306a36Sopenharmony_ci	struct lio *lio;
147662306a36Sopenharmony_ci	struct octnic_gather *g;
147762306a36Sopenharmony_ci	int i, frags, iq;
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci	sc = (struct octeon_soft_command *)buf;
148062306a36Sopenharmony_ci	skb = (struct sk_buff *)sc->callback_arg;
148162306a36Sopenharmony_ci	finfo = (struct octnet_buf_free_info *)&skb->cb;
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	lio = finfo->lio;
148462306a36Sopenharmony_ci	g = finfo->g;
148562306a36Sopenharmony_ci	frags = skb_shinfo(skb)->nr_frags;
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	dma_unmap_single(&lio->oct_dev->pci_dev->dev,
148862306a36Sopenharmony_ci			 g->sg[0].ptr[0], (skb->len - skb->data_len),
148962306a36Sopenharmony_ci			 DMA_TO_DEVICE);
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci	i = 1;
149262306a36Sopenharmony_ci	while (frags--) {
149362306a36Sopenharmony_ci		skb_frag_t *frag = &skb_shinfo(skb)->frags[i - 1];
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci		dma_unmap_page(&lio->oct_dev->pci_dev->dev,
149662306a36Sopenharmony_ci			       g->sg[(i >> 2)].ptr[(i & 3)],
149762306a36Sopenharmony_ci			       skb_frag_size(frag), DMA_TO_DEVICE);
149862306a36Sopenharmony_ci		i++;
149962306a36Sopenharmony_ci	}
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	iq = skb_iq(lio->oct_dev, skb);
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	spin_lock(&lio->glist_lock[iq]);
150462306a36Sopenharmony_ci	list_add_tail(&g->list, &lio->glist[iq]);
150562306a36Sopenharmony_ci	spin_unlock(&lio->glist_lock[iq]);
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	/* Don't free the skb yet */
150862306a36Sopenharmony_ci}
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci/**
151162306a36Sopenharmony_ci * liquidio_ptp_adjfine - Adjust ptp frequency
151262306a36Sopenharmony_ci * @ptp: PTP clock info
151362306a36Sopenharmony_ci * @scaled_ppm: how much to adjust by, in scaled parts-per-million
151462306a36Sopenharmony_ci *
151562306a36Sopenharmony_ci * Scaled parts per million is ppm with a 16-bit binary fractional field.
151662306a36Sopenharmony_ci */
151762306a36Sopenharmony_cistatic int liquidio_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
151862306a36Sopenharmony_ci{
151962306a36Sopenharmony_ci	struct lio *lio = container_of(ptp, struct lio, ptp_info);
152062306a36Sopenharmony_ci	struct octeon_device *oct = (struct octeon_device *)lio->oct_dev;
152162306a36Sopenharmony_ci	s32 ppb = scaled_ppm_to_ppb(scaled_ppm);
152262306a36Sopenharmony_ci	u64 comp, delta;
152362306a36Sopenharmony_ci	unsigned long flags;
152462306a36Sopenharmony_ci	bool neg_adj = false;
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	if (ppb < 0) {
152762306a36Sopenharmony_ci		neg_adj = true;
152862306a36Sopenharmony_ci		ppb = -ppb;
152962306a36Sopenharmony_ci	}
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	/* The hardware adds the clock compensation value to the
153262306a36Sopenharmony_ci	 * PTP clock on every coprocessor clock cycle, so we
153362306a36Sopenharmony_ci	 * compute the delta in terms of coprocessor clocks.
153462306a36Sopenharmony_ci	 */
153562306a36Sopenharmony_ci	delta = (u64)ppb << 32;
153662306a36Sopenharmony_ci	do_div(delta, oct->coproc_clock_rate);
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	spin_lock_irqsave(&lio->ptp_lock, flags);
153962306a36Sopenharmony_ci	comp = lio_pci_readq(oct, CN6XXX_MIO_PTP_CLOCK_COMP);
154062306a36Sopenharmony_ci	if (neg_adj)
154162306a36Sopenharmony_ci		comp -= delta;
154262306a36Sopenharmony_ci	else
154362306a36Sopenharmony_ci		comp += delta;
154462306a36Sopenharmony_ci	lio_pci_writeq(oct, comp, CN6XXX_MIO_PTP_CLOCK_COMP);
154562306a36Sopenharmony_ci	spin_unlock_irqrestore(&lio->ptp_lock, flags);
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	return 0;
154862306a36Sopenharmony_ci}
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci/**
155162306a36Sopenharmony_ci * liquidio_ptp_adjtime - Adjust ptp time
155262306a36Sopenharmony_ci * @ptp: PTP clock info
155362306a36Sopenharmony_ci * @delta: how much to adjust by, in nanosecs
155462306a36Sopenharmony_ci */
155562306a36Sopenharmony_cistatic int liquidio_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
155662306a36Sopenharmony_ci{
155762306a36Sopenharmony_ci	unsigned long flags;
155862306a36Sopenharmony_ci	struct lio *lio = container_of(ptp, struct lio, ptp_info);
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	spin_lock_irqsave(&lio->ptp_lock, flags);
156162306a36Sopenharmony_ci	lio->ptp_adjust += delta;
156262306a36Sopenharmony_ci	spin_unlock_irqrestore(&lio->ptp_lock, flags);
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci	return 0;
156562306a36Sopenharmony_ci}
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci/**
156862306a36Sopenharmony_ci * liquidio_ptp_gettime - Get hardware clock time, including any adjustment
156962306a36Sopenharmony_ci * @ptp: PTP clock info
157062306a36Sopenharmony_ci * @ts: timespec
157162306a36Sopenharmony_ci */
157262306a36Sopenharmony_cistatic int liquidio_ptp_gettime(struct ptp_clock_info *ptp,
157362306a36Sopenharmony_ci				struct timespec64 *ts)
157462306a36Sopenharmony_ci{
157562306a36Sopenharmony_ci	u64 ns;
157662306a36Sopenharmony_ci	unsigned long flags;
157762306a36Sopenharmony_ci	struct lio *lio = container_of(ptp, struct lio, ptp_info);
157862306a36Sopenharmony_ci	struct octeon_device *oct = (struct octeon_device *)lio->oct_dev;
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci	spin_lock_irqsave(&lio->ptp_lock, flags);
158162306a36Sopenharmony_ci	ns = lio_pci_readq(oct, CN6XXX_MIO_PTP_CLOCK_HI);
158262306a36Sopenharmony_ci	ns += lio->ptp_adjust;
158362306a36Sopenharmony_ci	spin_unlock_irqrestore(&lio->ptp_lock, flags);
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci	*ts = ns_to_timespec64(ns);
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	return 0;
158862306a36Sopenharmony_ci}
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci/**
159162306a36Sopenharmony_ci * liquidio_ptp_settime - Set hardware clock time. Reset adjustment
159262306a36Sopenharmony_ci * @ptp: PTP clock info
159362306a36Sopenharmony_ci * @ts: timespec
159462306a36Sopenharmony_ci */
159562306a36Sopenharmony_cistatic int liquidio_ptp_settime(struct ptp_clock_info *ptp,
159662306a36Sopenharmony_ci				const struct timespec64 *ts)
159762306a36Sopenharmony_ci{
159862306a36Sopenharmony_ci	u64 ns;
159962306a36Sopenharmony_ci	unsigned long flags;
160062306a36Sopenharmony_ci	struct lio *lio = container_of(ptp, struct lio, ptp_info);
160162306a36Sopenharmony_ci	struct octeon_device *oct = (struct octeon_device *)lio->oct_dev;
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci	ns = timespec64_to_ns(ts);
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci	spin_lock_irqsave(&lio->ptp_lock, flags);
160662306a36Sopenharmony_ci	lio_pci_writeq(oct, ns, CN6XXX_MIO_PTP_CLOCK_HI);
160762306a36Sopenharmony_ci	lio->ptp_adjust = 0;
160862306a36Sopenharmony_ci	spin_unlock_irqrestore(&lio->ptp_lock, flags);
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	return 0;
161162306a36Sopenharmony_ci}
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci/**
161462306a36Sopenharmony_ci * liquidio_ptp_enable - Check if PTP is enabled
161562306a36Sopenharmony_ci * @ptp: PTP clock info
161662306a36Sopenharmony_ci * @rq: request
161762306a36Sopenharmony_ci * @on: is it on
161862306a36Sopenharmony_ci */
161962306a36Sopenharmony_cistatic int
162062306a36Sopenharmony_ciliquidio_ptp_enable(struct ptp_clock_info __maybe_unused *ptp,
162162306a36Sopenharmony_ci		    struct ptp_clock_request __maybe_unused *rq,
162262306a36Sopenharmony_ci		    int __maybe_unused on)
162362306a36Sopenharmony_ci{
162462306a36Sopenharmony_ci	return -EOPNOTSUPP;
162562306a36Sopenharmony_ci}
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci/**
162862306a36Sopenharmony_ci * oct_ptp_open - Open PTP clock source
162962306a36Sopenharmony_ci * @netdev: network device
163062306a36Sopenharmony_ci */
163162306a36Sopenharmony_cistatic void oct_ptp_open(struct net_device *netdev)
163262306a36Sopenharmony_ci{
163362306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
163462306a36Sopenharmony_ci	struct octeon_device *oct = (struct octeon_device *)lio->oct_dev;
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	spin_lock_init(&lio->ptp_lock);
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci	snprintf(lio->ptp_info.name, 16, "%s", netdev->name);
163962306a36Sopenharmony_ci	lio->ptp_info.owner = THIS_MODULE;
164062306a36Sopenharmony_ci	lio->ptp_info.max_adj = 250000000;
164162306a36Sopenharmony_ci	lio->ptp_info.n_alarm = 0;
164262306a36Sopenharmony_ci	lio->ptp_info.n_ext_ts = 0;
164362306a36Sopenharmony_ci	lio->ptp_info.n_per_out = 0;
164462306a36Sopenharmony_ci	lio->ptp_info.pps = 0;
164562306a36Sopenharmony_ci	lio->ptp_info.adjfine = liquidio_ptp_adjfine;
164662306a36Sopenharmony_ci	lio->ptp_info.adjtime = liquidio_ptp_adjtime;
164762306a36Sopenharmony_ci	lio->ptp_info.gettime64 = liquidio_ptp_gettime;
164862306a36Sopenharmony_ci	lio->ptp_info.settime64 = liquidio_ptp_settime;
164962306a36Sopenharmony_ci	lio->ptp_info.enable = liquidio_ptp_enable;
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci	lio->ptp_adjust = 0;
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci	lio->ptp_clock = ptp_clock_register(&lio->ptp_info,
165462306a36Sopenharmony_ci					     &oct->pci_dev->dev);
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	if (IS_ERR(lio->ptp_clock))
165762306a36Sopenharmony_ci		lio->ptp_clock = NULL;
165862306a36Sopenharmony_ci}
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci/**
166162306a36Sopenharmony_ci * liquidio_ptp_init - Init PTP clock
166262306a36Sopenharmony_ci * @oct: octeon device
166362306a36Sopenharmony_ci */
166462306a36Sopenharmony_cistatic void liquidio_ptp_init(struct octeon_device *oct)
166562306a36Sopenharmony_ci{
166662306a36Sopenharmony_ci	u64 clock_comp, cfg;
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	clock_comp = (u64)NSEC_PER_SEC << 32;
166962306a36Sopenharmony_ci	do_div(clock_comp, oct->coproc_clock_rate);
167062306a36Sopenharmony_ci	lio_pci_writeq(oct, clock_comp, CN6XXX_MIO_PTP_CLOCK_COMP);
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	/* Enable */
167362306a36Sopenharmony_ci	cfg = lio_pci_readq(oct, CN6XXX_MIO_PTP_CLOCK_CFG);
167462306a36Sopenharmony_ci	lio_pci_writeq(oct, cfg | 0x01, CN6XXX_MIO_PTP_CLOCK_CFG);
167562306a36Sopenharmony_ci}
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_ci/**
167862306a36Sopenharmony_ci * load_firmware - Load firmware to device
167962306a36Sopenharmony_ci * @oct: octeon device
168062306a36Sopenharmony_ci *
168162306a36Sopenharmony_ci * Maps device to firmware filename, requests firmware, and downloads it
168262306a36Sopenharmony_ci */
168362306a36Sopenharmony_cistatic int load_firmware(struct octeon_device *oct)
168462306a36Sopenharmony_ci{
168562306a36Sopenharmony_ci	int ret = 0;
168662306a36Sopenharmony_ci	const struct firmware *fw;
168762306a36Sopenharmony_ci	char fw_name[LIO_MAX_FW_FILENAME_LEN];
168862306a36Sopenharmony_ci	char *tmp_fw_type;
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci	if (fw_type_is_auto()) {
169162306a36Sopenharmony_ci		tmp_fw_type = LIO_FW_NAME_TYPE_NIC;
169262306a36Sopenharmony_ci		strncpy(fw_type, tmp_fw_type, sizeof(fw_type));
169362306a36Sopenharmony_ci	} else {
169462306a36Sopenharmony_ci		tmp_fw_type = fw_type;
169562306a36Sopenharmony_ci	}
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	sprintf(fw_name, "%s%s%s_%s%s", LIO_FW_DIR, LIO_FW_BASE_NAME,
169862306a36Sopenharmony_ci		octeon_get_conf(oct)->card_name, tmp_fw_type,
169962306a36Sopenharmony_ci		LIO_FW_NAME_SUFFIX);
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci	ret = request_firmware(&fw, fw_name, &oct->pci_dev->dev);
170262306a36Sopenharmony_ci	if (ret) {
170362306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "Request firmware failed. Could not find file %s.\n",
170462306a36Sopenharmony_ci			fw_name);
170562306a36Sopenharmony_ci		release_firmware(fw);
170662306a36Sopenharmony_ci		return ret;
170762306a36Sopenharmony_ci	}
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci	ret = octeon_download_firmware(oct, fw->data, fw->size);
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci	release_firmware(fw);
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	return ret;
171462306a36Sopenharmony_ci}
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci/**
171762306a36Sopenharmony_ci * octnet_poll_check_txq_status - Poll routine for checking transmit queue status
171862306a36Sopenharmony_ci * @work: work_struct data structure
171962306a36Sopenharmony_ci */
172062306a36Sopenharmony_cistatic void octnet_poll_check_txq_status(struct work_struct *work)
172162306a36Sopenharmony_ci{
172262306a36Sopenharmony_ci	struct cavium_wk *wk = (struct cavium_wk *)work;
172362306a36Sopenharmony_ci	struct lio *lio = (struct lio *)wk->ctxptr;
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci	if (!ifstate_check(lio, LIO_IFSTATE_RUNNING))
172662306a36Sopenharmony_ci		return;
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	check_txq_status(lio);
172962306a36Sopenharmony_ci	queue_delayed_work(lio->txq_status_wq.wq,
173062306a36Sopenharmony_ci			   &lio->txq_status_wq.wk.work, msecs_to_jiffies(1));
173162306a36Sopenharmony_ci}
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci/**
173462306a36Sopenharmony_ci * setup_tx_poll_fn - Sets up the txq poll check
173562306a36Sopenharmony_ci * @netdev: network device
173662306a36Sopenharmony_ci */
173762306a36Sopenharmony_cistatic inline int setup_tx_poll_fn(struct net_device *netdev)
173862306a36Sopenharmony_ci{
173962306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
174062306a36Sopenharmony_ci	struct octeon_device *oct = lio->oct_dev;
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ci	lio->txq_status_wq.wq = alloc_workqueue("txq-status",
174362306a36Sopenharmony_ci						WQ_MEM_RECLAIM, 0);
174462306a36Sopenharmony_ci	if (!lio->txq_status_wq.wq) {
174562306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "unable to create cavium txq status wq\n");
174662306a36Sopenharmony_ci		return -1;
174762306a36Sopenharmony_ci	}
174862306a36Sopenharmony_ci	INIT_DELAYED_WORK(&lio->txq_status_wq.wk.work,
174962306a36Sopenharmony_ci			  octnet_poll_check_txq_status);
175062306a36Sopenharmony_ci	lio->txq_status_wq.wk.ctxptr = lio;
175162306a36Sopenharmony_ci	queue_delayed_work(lio->txq_status_wq.wq,
175262306a36Sopenharmony_ci			   &lio->txq_status_wq.wk.work, msecs_to_jiffies(1));
175362306a36Sopenharmony_ci	return 0;
175462306a36Sopenharmony_ci}
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_cistatic inline void cleanup_tx_poll_fn(struct net_device *netdev)
175762306a36Sopenharmony_ci{
175862306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	if (lio->txq_status_wq.wq) {
176162306a36Sopenharmony_ci		cancel_delayed_work_sync(&lio->txq_status_wq.wk.work);
176262306a36Sopenharmony_ci		destroy_workqueue(lio->txq_status_wq.wq);
176362306a36Sopenharmony_ci	}
176462306a36Sopenharmony_ci}
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci/**
176762306a36Sopenharmony_ci * liquidio_open - Net device open for LiquidIO
176862306a36Sopenharmony_ci * @netdev: network device
176962306a36Sopenharmony_ci */
177062306a36Sopenharmony_cistatic int liquidio_open(struct net_device *netdev)
177162306a36Sopenharmony_ci{
177262306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
177362306a36Sopenharmony_ci	struct octeon_device *oct = lio->oct_dev;
177462306a36Sopenharmony_ci	struct octeon_device_priv *oct_priv = oct->priv;
177562306a36Sopenharmony_ci	struct napi_struct *napi, *n;
177662306a36Sopenharmony_ci	int ret = 0;
177762306a36Sopenharmony_ci
177862306a36Sopenharmony_ci	if (oct->props[lio->ifidx].napi_enabled == 0) {
177962306a36Sopenharmony_ci		tasklet_disable(&oct_priv->droq_tasklet);
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci		list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list)
178262306a36Sopenharmony_ci			napi_enable(napi);
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci		oct->props[lio->ifidx].napi_enabled = 1;
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci		if (OCTEON_CN23XX_PF(oct))
178762306a36Sopenharmony_ci			oct->droq[0]->ops.poll_mode = 1;
178862306a36Sopenharmony_ci	}
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci	if (oct->ptp_enable)
179162306a36Sopenharmony_ci		oct_ptp_open(netdev);
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_ci	ifstate_set(lio, LIO_IFSTATE_RUNNING);
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci	if (!OCTEON_CN23XX_PF(oct) || !oct->msix_on) {
179662306a36Sopenharmony_ci		ret = setup_tx_poll_fn(netdev);
179762306a36Sopenharmony_ci		if (ret)
179862306a36Sopenharmony_ci			goto err_poll;
179962306a36Sopenharmony_ci	}
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci	netif_tx_start_all_queues(netdev);
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci	/* Ready for link status updates */
180462306a36Sopenharmony_ci	lio->intf_open = 1;
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_ci	netif_info(lio, ifup, lio->netdev, "Interface Open, ready for traffic\n");
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	/* tell Octeon to start forwarding packets to host */
180962306a36Sopenharmony_ci	ret = send_rx_ctrl_cmd(lio, 1);
181062306a36Sopenharmony_ci	if (ret)
181162306a36Sopenharmony_ci		goto err_rx_ctrl;
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci	/* start periodical statistics fetch */
181462306a36Sopenharmony_ci	INIT_DELAYED_WORK(&lio->stats_wk.work, lio_fetch_stats);
181562306a36Sopenharmony_ci	lio->stats_wk.ctxptr = lio;
181662306a36Sopenharmony_ci	schedule_delayed_work(&lio->stats_wk.work, msecs_to_jiffies
181762306a36Sopenharmony_ci					(LIQUIDIO_NDEV_STATS_POLL_TIME_MS));
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci	dev_info(&oct->pci_dev->dev, "%s interface is opened\n",
182062306a36Sopenharmony_ci		 netdev->name);
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci	return 0;
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_cierr_rx_ctrl:
182562306a36Sopenharmony_ci	if (!OCTEON_CN23XX_PF(oct) || !oct->msix_on)
182662306a36Sopenharmony_ci		cleanup_tx_poll_fn(netdev);
182762306a36Sopenharmony_cierr_poll:
182862306a36Sopenharmony_ci	if (lio->ptp_clock) {
182962306a36Sopenharmony_ci		ptp_clock_unregister(lio->ptp_clock);
183062306a36Sopenharmony_ci		lio->ptp_clock = NULL;
183162306a36Sopenharmony_ci	}
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci	if (oct->props[lio->ifidx].napi_enabled == 1) {
183462306a36Sopenharmony_ci		list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list)
183562306a36Sopenharmony_ci			napi_disable(napi);
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci		oct->props[lio->ifidx].napi_enabled = 0;
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ci		if (OCTEON_CN23XX_PF(oct))
184062306a36Sopenharmony_ci			oct->droq[0]->ops.poll_mode = 0;
184162306a36Sopenharmony_ci	}
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci	return ret;
184462306a36Sopenharmony_ci}
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci/**
184762306a36Sopenharmony_ci * liquidio_stop - Net device stop for LiquidIO
184862306a36Sopenharmony_ci * @netdev: network device
184962306a36Sopenharmony_ci */
185062306a36Sopenharmony_cistatic int liquidio_stop(struct net_device *netdev)
185162306a36Sopenharmony_ci{
185262306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
185362306a36Sopenharmony_ci	struct octeon_device *oct = lio->oct_dev;
185462306a36Sopenharmony_ci	struct octeon_device_priv *oct_priv = oct->priv;
185562306a36Sopenharmony_ci	struct napi_struct *napi, *n;
185662306a36Sopenharmony_ci	int ret = 0;
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci	ifstate_reset(lio, LIO_IFSTATE_RUNNING);
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci	/* Stop any link updates */
186162306a36Sopenharmony_ci	lio->intf_open = 0;
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci	stop_txqs(netdev);
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci	/* Inform that netif carrier is down */
186662306a36Sopenharmony_ci	netif_carrier_off(netdev);
186762306a36Sopenharmony_ci	netif_tx_disable(netdev);
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci	lio->linfo.link.s.link_up = 0;
187062306a36Sopenharmony_ci	lio->link_changes++;
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	/* Tell Octeon that nic interface is down. */
187362306a36Sopenharmony_ci	ret = send_rx_ctrl_cmd(lio, 0);
187462306a36Sopenharmony_ci	if (ret)
187562306a36Sopenharmony_ci		return ret;
187662306a36Sopenharmony_ci
187762306a36Sopenharmony_ci	if (OCTEON_CN23XX_PF(oct)) {
187862306a36Sopenharmony_ci		if (!oct->msix_on)
187962306a36Sopenharmony_ci			cleanup_tx_poll_fn(netdev);
188062306a36Sopenharmony_ci	} else {
188162306a36Sopenharmony_ci		cleanup_tx_poll_fn(netdev);
188262306a36Sopenharmony_ci	}
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_ci	cancel_delayed_work_sync(&lio->stats_wk.work);
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci	if (lio->ptp_clock) {
188762306a36Sopenharmony_ci		ptp_clock_unregister(lio->ptp_clock);
188862306a36Sopenharmony_ci		lio->ptp_clock = NULL;
188962306a36Sopenharmony_ci	}
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_ci	/* Wait for any pending Rx descriptors */
189262306a36Sopenharmony_ci	if (lio_wait_for_clean_oq(oct))
189362306a36Sopenharmony_ci		netif_info(lio, rx_err, lio->netdev,
189462306a36Sopenharmony_ci			   "Proceeding with stop interface after partial RX desc processing\n");
189562306a36Sopenharmony_ci
189662306a36Sopenharmony_ci	if (oct->props[lio->ifidx].napi_enabled == 1) {
189762306a36Sopenharmony_ci		list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list)
189862306a36Sopenharmony_ci			napi_disable(napi);
189962306a36Sopenharmony_ci
190062306a36Sopenharmony_ci		oct->props[lio->ifidx].napi_enabled = 0;
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ci		if (OCTEON_CN23XX_PF(oct))
190362306a36Sopenharmony_ci			oct->droq[0]->ops.poll_mode = 0;
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci		tasklet_enable(&oct_priv->droq_tasklet);
190662306a36Sopenharmony_ci	}
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci	dev_info(&oct->pci_dev->dev, "%s interface is stopped\n", netdev->name);
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci	return ret;
191162306a36Sopenharmony_ci}
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci/**
191462306a36Sopenharmony_ci * get_new_flags - Converts a mask based on net device flags
191562306a36Sopenharmony_ci * @netdev: network device
191662306a36Sopenharmony_ci *
191762306a36Sopenharmony_ci * This routine generates a octnet_ifflags mask from the net device flags
191862306a36Sopenharmony_ci * received from the OS.
191962306a36Sopenharmony_ci */
192062306a36Sopenharmony_cistatic inline enum octnet_ifflags get_new_flags(struct net_device *netdev)
192162306a36Sopenharmony_ci{
192262306a36Sopenharmony_ci	enum octnet_ifflags f = OCTNET_IFFLAG_UNICAST;
192362306a36Sopenharmony_ci
192462306a36Sopenharmony_ci	if (netdev->flags & IFF_PROMISC)
192562306a36Sopenharmony_ci		f |= OCTNET_IFFLAG_PROMISC;
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci	if (netdev->flags & IFF_ALLMULTI)
192862306a36Sopenharmony_ci		f |= OCTNET_IFFLAG_ALLMULTI;
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci	if (netdev->flags & IFF_MULTICAST) {
193162306a36Sopenharmony_ci		f |= OCTNET_IFFLAG_MULTICAST;
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ci		/* Accept all multicast addresses if there are more than we
193462306a36Sopenharmony_ci		 * can handle
193562306a36Sopenharmony_ci		 */
193662306a36Sopenharmony_ci		if (netdev_mc_count(netdev) > MAX_OCTEON_MULTICAST_ADDR)
193762306a36Sopenharmony_ci			f |= OCTNET_IFFLAG_ALLMULTI;
193862306a36Sopenharmony_ci	}
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_ci	if (netdev->flags & IFF_BROADCAST)
194162306a36Sopenharmony_ci		f |= OCTNET_IFFLAG_BROADCAST;
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci	return f;
194462306a36Sopenharmony_ci}
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_ci/**
194762306a36Sopenharmony_ci * liquidio_set_mcast_list - Net device set_multicast_list
194862306a36Sopenharmony_ci * @netdev: network device
194962306a36Sopenharmony_ci */
195062306a36Sopenharmony_cistatic void liquidio_set_mcast_list(struct net_device *netdev)
195162306a36Sopenharmony_ci{
195262306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
195362306a36Sopenharmony_ci	struct octeon_device *oct = lio->oct_dev;
195462306a36Sopenharmony_ci	struct octnic_ctrl_pkt nctrl;
195562306a36Sopenharmony_ci	struct netdev_hw_addr *ha;
195662306a36Sopenharmony_ci	u64 *mc;
195762306a36Sopenharmony_ci	int ret;
195862306a36Sopenharmony_ci	int mc_count = min(netdev_mc_count(netdev), MAX_OCTEON_MULTICAST_ADDR);
195962306a36Sopenharmony_ci
196062306a36Sopenharmony_ci	memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt));
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci	/* Create a ctrl pkt command to be sent to core app. */
196362306a36Sopenharmony_ci	nctrl.ncmd.u64 = 0;
196462306a36Sopenharmony_ci	nctrl.ncmd.s.cmd = OCTNET_CMD_SET_MULTI_LIST;
196562306a36Sopenharmony_ci	nctrl.ncmd.s.param1 = get_new_flags(netdev);
196662306a36Sopenharmony_ci	nctrl.ncmd.s.param2 = mc_count;
196762306a36Sopenharmony_ci	nctrl.ncmd.s.more = mc_count;
196862306a36Sopenharmony_ci	nctrl.iq_no = lio->linfo.txpciq[0].s.q_no;
196962306a36Sopenharmony_ci	nctrl.netpndev = (u64)netdev;
197062306a36Sopenharmony_ci	nctrl.cb_fn = liquidio_link_ctrl_cmd_completion;
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_ci	/* copy all the addresses into the udd */
197362306a36Sopenharmony_ci	mc = &nctrl.udd[0];
197462306a36Sopenharmony_ci	netdev_for_each_mc_addr(ha, netdev) {
197562306a36Sopenharmony_ci		*mc = 0;
197662306a36Sopenharmony_ci		memcpy(((u8 *)mc) + 2, ha->addr, ETH_ALEN);
197762306a36Sopenharmony_ci		/* no need to swap bytes */
197862306a36Sopenharmony_ci
197962306a36Sopenharmony_ci		if (++mc > &nctrl.udd[mc_count])
198062306a36Sopenharmony_ci			break;
198162306a36Sopenharmony_ci	}
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci	/* Apparently, any activity in this call from the kernel has to
198462306a36Sopenharmony_ci	 * be atomic. So we won't wait for response.
198562306a36Sopenharmony_ci	 */
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci	ret = octnet_send_nic_ctrl_pkt(lio->oct_dev, &nctrl);
198862306a36Sopenharmony_ci	if (ret) {
198962306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "DEVFLAGS change failed in core (ret: 0x%x)\n",
199062306a36Sopenharmony_ci			ret);
199162306a36Sopenharmony_ci	}
199262306a36Sopenharmony_ci}
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci/**
199562306a36Sopenharmony_ci * liquidio_set_mac - Net device set_mac_address
199662306a36Sopenharmony_ci * @netdev: network device
199762306a36Sopenharmony_ci * @p: pointer to sockaddr
199862306a36Sopenharmony_ci */
199962306a36Sopenharmony_cistatic int liquidio_set_mac(struct net_device *netdev, void *p)
200062306a36Sopenharmony_ci{
200162306a36Sopenharmony_ci	int ret = 0;
200262306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
200362306a36Sopenharmony_ci	struct octeon_device *oct = lio->oct_dev;
200462306a36Sopenharmony_ci	struct sockaddr *addr = (struct sockaddr *)p;
200562306a36Sopenharmony_ci	struct octnic_ctrl_pkt nctrl;
200662306a36Sopenharmony_ci
200762306a36Sopenharmony_ci	if (!is_valid_ether_addr(addr->sa_data))
200862306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
200962306a36Sopenharmony_ci
201062306a36Sopenharmony_ci	memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt));
201162306a36Sopenharmony_ci
201262306a36Sopenharmony_ci	nctrl.ncmd.u64 = 0;
201362306a36Sopenharmony_ci	nctrl.ncmd.s.cmd = OCTNET_CMD_CHANGE_MACADDR;
201462306a36Sopenharmony_ci	nctrl.ncmd.s.param1 = 0;
201562306a36Sopenharmony_ci	nctrl.ncmd.s.more = 1;
201662306a36Sopenharmony_ci	nctrl.iq_no = lio->linfo.txpciq[0].s.q_no;
201762306a36Sopenharmony_ci	nctrl.netpndev = (u64)netdev;
201862306a36Sopenharmony_ci
201962306a36Sopenharmony_ci	nctrl.udd[0] = 0;
202062306a36Sopenharmony_ci	/* The MAC Address is presented in network byte order. */
202162306a36Sopenharmony_ci	memcpy((u8 *)&nctrl.udd[0] + 2, addr->sa_data, ETH_ALEN);
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci	ret = octnet_send_nic_ctrl_pkt(lio->oct_dev, &nctrl);
202462306a36Sopenharmony_ci	if (ret < 0) {
202562306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "MAC Address change failed\n");
202662306a36Sopenharmony_ci		return -ENOMEM;
202762306a36Sopenharmony_ci	}
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci	if (nctrl.sc_status) {
203062306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev,
203162306a36Sopenharmony_ci			"%s: MAC Address change failed. sc return=%x\n",
203262306a36Sopenharmony_ci			 __func__, nctrl.sc_status);
203362306a36Sopenharmony_ci		return -EIO;
203462306a36Sopenharmony_ci	}
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci	eth_hw_addr_set(netdev, addr->sa_data);
203762306a36Sopenharmony_ci	memcpy(((u8 *)&lio->linfo.hw_addr) + 2, addr->sa_data, ETH_ALEN);
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_ci	return 0;
204062306a36Sopenharmony_ci}
204162306a36Sopenharmony_ci
204262306a36Sopenharmony_cistatic void
204362306a36Sopenharmony_ciliquidio_get_stats64(struct net_device *netdev,
204462306a36Sopenharmony_ci		     struct rtnl_link_stats64 *lstats)
204562306a36Sopenharmony_ci{
204662306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
204762306a36Sopenharmony_ci	struct octeon_device *oct;
204862306a36Sopenharmony_ci	u64 pkts = 0, drop = 0, bytes = 0;
204962306a36Sopenharmony_ci	struct oct_droq_stats *oq_stats;
205062306a36Sopenharmony_ci	struct oct_iq_stats *iq_stats;
205162306a36Sopenharmony_ci	int i, iq_no, oq_no;
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_ci	oct = lio->oct_dev;
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci	if (ifstate_check(lio, LIO_IFSTATE_RESETTING))
205662306a36Sopenharmony_ci		return;
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_ci	for (i = 0; i < oct->num_iqs; i++) {
205962306a36Sopenharmony_ci		iq_no = lio->linfo.txpciq[i].s.q_no;
206062306a36Sopenharmony_ci		iq_stats = &oct->instr_queue[iq_no]->stats;
206162306a36Sopenharmony_ci		pkts += iq_stats->tx_done;
206262306a36Sopenharmony_ci		drop += iq_stats->tx_dropped;
206362306a36Sopenharmony_ci		bytes += iq_stats->tx_tot_bytes;
206462306a36Sopenharmony_ci	}
206562306a36Sopenharmony_ci
206662306a36Sopenharmony_ci	lstats->tx_packets = pkts;
206762306a36Sopenharmony_ci	lstats->tx_bytes = bytes;
206862306a36Sopenharmony_ci	lstats->tx_dropped = drop;
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_ci	pkts = 0;
207162306a36Sopenharmony_ci	drop = 0;
207262306a36Sopenharmony_ci	bytes = 0;
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci	for (i = 0; i < oct->num_oqs; i++) {
207562306a36Sopenharmony_ci		oq_no = lio->linfo.rxpciq[i].s.q_no;
207662306a36Sopenharmony_ci		oq_stats = &oct->droq[oq_no]->stats;
207762306a36Sopenharmony_ci		pkts += oq_stats->rx_pkts_received;
207862306a36Sopenharmony_ci		drop += (oq_stats->rx_dropped +
207962306a36Sopenharmony_ci			 oq_stats->dropped_nodispatch +
208062306a36Sopenharmony_ci			 oq_stats->dropped_toomany +
208162306a36Sopenharmony_ci			 oq_stats->dropped_nomem);
208262306a36Sopenharmony_ci		bytes += oq_stats->rx_bytes_received;
208362306a36Sopenharmony_ci	}
208462306a36Sopenharmony_ci
208562306a36Sopenharmony_ci	lstats->rx_bytes = bytes;
208662306a36Sopenharmony_ci	lstats->rx_packets = pkts;
208762306a36Sopenharmony_ci	lstats->rx_dropped = drop;
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_ci	lstats->multicast = oct->link_stats.fromwire.fw_total_mcast;
209062306a36Sopenharmony_ci	lstats->collisions = oct->link_stats.fromhost.total_collisions;
209162306a36Sopenharmony_ci
209262306a36Sopenharmony_ci	/* detailed rx_errors: */
209362306a36Sopenharmony_ci	lstats->rx_length_errors = oct->link_stats.fromwire.l2_err;
209462306a36Sopenharmony_ci	/* recved pkt with crc error    */
209562306a36Sopenharmony_ci	lstats->rx_crc_errors = oct->link_stats.fromwire.fcs_err;
209662306a36Sopenharmony_ci	/* recv'd frame alignment error */
209762306a36Sopenharmony_ci	lstats->rx_frame_errors = oct->link_stats.fromwire.frame_err;
209862306a36Sopenharmony_ci	/* recv'r fifo overrun */
209962306a36Sopenharmony_ci	lstats->rx_fifo_errors = oct->link_stats.fromwire.fifo_err;
210062306a36Sopenharmony_ci
210162306a36Sopenharmony_ci	lstats->rx_errors = lstats->rx_length_errors + lstats->rx_crc_errors +
210262306a36Sopenharmony_ci		lstats->rx_frame_errors + lstats->rx_fifo_errors;
210362306a36Sopenharmony_ci
210462306a36Sopenharmony_ci	/* detailed tx_errors */
210562306a36Sopenharmony_ci	lstats->tx_aborted_errors = oct->link_stats.fromhost.fw_err_pko;
210662306a36Sopenharmony_ci	lstats->tx_carrier_errors = oct->link_stats.fromhost.fw_err_link;
210762306a36Sopenharmony_ci	lstats->tx_fifo_errors = oct->link_stats.fromhost.fifo_err;
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_ci	lstats->tx_errors = lstats->tx_aborted_errors +
211062306a36Sopenharmony_ci		lstats->tx_carrier_errors +
211162306a36Sopenharmony_ci		lstats->tx_fifo_errors;
211262306a36Sopenharmony_ci}
211362306a36Sopenharmony_ci
211462306a36Sopenharmony_ci/**
211562306a36Sopenharmony_ci * hwtstamp_ioctl - Handler for SIOCSHWTSTAMP ioctl
211662306a36Sopenharmony_ci * @netdev: network device
211762306a36Sopenharmony_ci * @ifr: interface request
211862306a36Sopenharmony_ci */
211962306a36Sopenharmony_cistatic int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr)
212062306a36Sopenharmony_ci{
212162306a36Sopenharmony_ci	struct hwtstamp_config conf;
212262306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
212362306a36Sopenharmony_ci
212462306a36Sopenharmony_ci	if (copy_from_user(&conf, ifr->ifr_data, sizeof(conf)))
212562306a36Sopenharmony_ci		return -EFAULT;
212662306a36Sopenharmony_ci
212762306a36Sopenharmony_ci	switch (conf.tx_type) {
212862306a36Sopenharmony_ci	case HWTSTAMP_TX_ON:
212962306a36Sopenharmony_ci	case HWTSTAMP_TX_OFF:
213062306a36Sopenharmony_ci		break;
213162306a36Sopenharmony_ci	default:
213262306a36Sopenharmony_ci		return -ERANGE;
213362306a36Sopenharmony_ci	}
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci	switch (conf.rx_filter) {
213662306a36Sopenharmony_ci	case HWTSTAMP_FILTER_NONE:
213762306a36Sopenharmony_ci		break;
213862306a36Sopenharmony_ci	case HWTSTAMP_FILTER_ALL:
213962306a36Sopenharmony_ci	case HWTSTAMP_FILTER_SOME:
214062306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
214162306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
214262306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
214362306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
214462306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
214562306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
214662306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
214762306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
214862306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
214962306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_EVENT:
215062306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_SYNC:
215162306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
215262306a36Sopenharmony_ci	case HWTSTAMP_FILTER_NTP_ALL:
215362306a36Sopenharmony_ci		conf.rx_filter = HWTSTAMP_FILTER_ALL;
215462306a36Sopenharmony_ci		break;
215562306a36Sopenharmony_ci	default:
215662306a36Sopenharmony_ci		return -ERANGE;
215762306a36Sopenharmony_ci	}
215862306a36Sopenharmony_ci
215962306a36Sopenharmony_ci	if (conf.rx_filter == HWTSTAMP_FILTER_ALL)
216062306a36Sopenharmony_ci		ifstate_set(lio, LIO_IFSTATE_RX_TIMESTAMP_ENABLED);
216162306a36Sopenharmony_ci
216262306a36Sopenharmony_ci	else
216362306a36Sopenharmony_ci		ifstate_reset(lio, LIO_IFSTATE_RX_TIMESTAMP_ENABLED);
216462306a36Sopenharmony_ci
216562306a36Sopenharmony_ci	return copy_to_user(ifr->ifr_data, &conf, sizeof(conf)) ? -EFAULT : 0;
216662306a36Sopenharmony_ci}
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_ci/**
216962306a36Sopenharmony_ci * liquidio_ioctl - ioctl handler
217062306a36Sopenharmony_ci * @netdev: network device
217162306a36Sopenharmony_ci * @ifr: interface request
217262306a36Sopenharmony_ci * @cmd: command
217362306a36Sopenharmony_ci */
217462306a36Sopenharmony_cistatic int liquidio_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
217562306a36Sopenharmony_ci{
217662306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_ci	switch (cmd) {
217962306a36Sopenharmony_ci	case SIOCSHWTSTAMP:
218062306a36Sopenharmony_ci		if (lio->oct_dev->ptp_enable)
218162306a36Sopenharmony_ci			return hwtstamp_ioctl(netdev, ifr);
218262306a36Sopenharmony_ci		fallthrough;
218362306a36Sopenharmony_ci	default:
218462306a36Sopenharmony_ci		return -EOPNOTSUPP;
218562306a36Sopenharmony_ci	}
218662306a36Sopenharmony_ci}
218762306a36Sopenharmony_ci
218862306a36Sopenharmony_ci/**
218962306a36Sopenharmony_ci * handle_timestamp - handle a Tx timestamp response
219062306a36Sopenharmony_ci * @oct: octeon device
219162306a36Sopenharmony_ci * @status: response status
219262306a36Sopenharmony_ci * @buf: pointer to skb
219362306a36Sopenharmony_ci */
219462306a36Sopenharmony_cistatic void handle_timestamp(struct octeon_device *oct,
219562306a36Sopenharmony_ci			     u32 status,
219662306a36Sopenharmony_ci			     void *buf)
219762306a36Sopenharmony_ci{
219862306a36Sopenharmony_ci	struct octnet_buf_free_info *finfo;
219962306a36Sopenharmony_ci	struct octeon_soft_command *sc;
220062306a36Sopenharmony_ci	struct oct_timestamp_resp *resp;
220162306a36Sopenharmony_ci	struct lio *lio;
220262306a36Sopenharmony_ci	struct sk_buff *skb = (struct sk_buff *)buf;
220362306a36Sopenharmony_ci
220462306a36Sopenharmony_ci	finfo = (struct octnet_buf_free_info *)skb->cb;
220562306a36Sopenharmony_ci	lio = finfo->lio;
220662306a36Sopenharmony_ci	sc = finfo->sc;
220762306a36Sopenharmony_ci	oct = lio->oct_dev;
220862306a36Sopenharmony_ci	resp = (struct oct_timestamp_resp *)sc->virtrptr;
220962306a36Sopenharmony_ci
221062306a36Sopenharmony_ci	if (status != OCTEON_REQUEST_DONE) {
221162306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "Tx timestamp instruction failed. Status: %llx\n",
221262306a36Sopenharmony_ci			CVM_CAST64(status));
221362306a36Sopenharmony_ci		resp->timestamp = 0;
221462306a36Sopenharmony_ci	}
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci	octeon_swap_8B_data(&resp->timestamp, 1);
221762306a36Sopenharmony_ci
221862306a36Sopenharmony_ci	if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) != 0)) {
221962306a36Sopenharmony_ci		struct skb_shared_hwtstamps ts;
222062306a36Sopenharmony_ci		u64 ns = resp->timestamp;
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_ci		netif_info(lio, tx_done, lio->netdev,
222362306a36Sopenharmony_ci			   "Got resulting SKBTX_HW_TSTAMP skb=%p ns=%016llu\n",
222462306a36Sopenharmony_ci			   skb, (unsigned long long)ns);
222562306a36Sopenharmony_ci		ts.hwtstamp = ns_to_ktime(ns + lio->ptp_adjust);
222662306a36Sopenharmony_ci		skb_tstamp_tx(skb, &ts);
222762306a36Sopenharmony_ci	}
222862306a36Sopenharmony_ci
222962306a36Sopenharmony_ci	octeon_free_soft_command(oct, sc);
223062306a36Sopenharmony_ci	tx_buffer_free(skb);
223162306a36Sopenharmony_ci}
223262306a36Sopenharmony_ci
223362306a36Sopenharmony_ci/**
223462306a36Sopenharmony_ci * send_nic_timestamp_pkt - Send a data packet that will be timestamped
223562306a36Sopenharmony_ci * @oct: octeon device
223662306a36Sopenharmony_ci * @ndata: pointer to network data
223762306a36Sopenharmony_ci * @finfo: pointer to private network data
223862306a36Sopenharmony_ci * @xmit_more: more is coming
223962306a36Sopenharmony_ci */
224062306a36Sopenharmony_cistatic inline int send_nic_timestamp_pkt(struct octeon_device *oct,
224162306a36Sopenharmony_ci					 struct octnic_data_pkt *ndata,
224262306a36Sopenharmony_ci					 struct octnet_buf_free_info *finfo,
224362306a36Sopenharmony_ci					 int xmit_more)
224462306a36Sopenharmony_ci{
224562306a36Sopenharmony_ci	int retval;
224662306a36Sopenharmony_ci	struct octeon_soft_command *sc;
224762306a36Sopenharmony_ci	struct lio *lio;
224862306a36Sopenharmony_ci	int ring_doorbell;
224962306a36Sopenharmony_ci	u32 len;
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_ci	lio = finfo->lio;
225262306a36Sopenharmony_ci
225362306a36Sopenharmony_ci	sc = octeon_alloc_soft_command_resp(oct, &ndata->cmd,
225462306a36Sopenharmony_ci					    sizeof(struct oct_timestamp_resp));
225562306a36Sopenharmony_ci	finfo->sc = sc;
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_ci	if (!sc) {
225862306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "No memory for timestamped data packet\n");
225962306a36Sopenharmony_ci		return IQ_SEND_FAILED;
226062306a36Sopenharmony_ci	}
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_ci	if (ndata->reqtype == REQTYPE_NORESP_NET)
226362306a36Sopenharmony_ci		ndata->reqtype = REQTYPE_RESP_NET;
226462306a36Sopenharmony_ci	else if (ndata->reqtype == REQTYPE_NORESP_NET_SG)
226562306a36Sopenharmony_ci		ndata->reqtype = REQTYPE_RESP_NET_SG;
226662306a36Sopenharmony_ci
226762306a36Sopenharmony_ci	sc->callback = handle_timestamp;
226862306a36Sopenharmony_ci	sc->callback_arg = finfo->skb;
226962306a36Sopenharmony_ci	sc->iq_no = ndata->q_no;
227062306a36Sopenharmony_ci
227162306a36Sopenharmony_ci	if (OCTEON_CN23XX_PF(oct))
227262306a36Sopenharmony_ci		len = (u32)((struct octeon_instr_ih3 *)
227362306a36Sopenharmony_ci			    (&sc->cmd.cmd3.ih3))->dlengsz;
227462306a36Sopenharmony_ci	else
227562306a36Sopenharmony_ci		len = (u32)((struct octeon_instr_ih2 *)
227662306a36Sopenharmony_ci			    (&sc->cmd.cmd2.ih2))->dlengsz;
227762306a36Sopenharmony_ci
227862306a36Sopenharmony_ci	ring_doorbell = !xmit_more;
227962306a36Sopenharmony_ci
228062306a36Sopenharmony_ci	retval = octeon_send_command(oct, sc->iq_no, ring_doorbell, &sc->cmd,
228162306a36Sopenharmony_ci				     sc, len, ndata->reqtype);
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_ci	if (retval == IQ_SEND_FAILED) {
228462306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "timestamp data packet failed status: %x\n",
228562306a36Sopenharmony_ci			retval);
228662306a36Sopenharmony_ci		octeon_free_soft_command(oct, sc);
228762306a36Sopenharmony_ci	} else {
228862306a36Sopenharmony_ci		netif_info(lio, tx_queued, lio->netdev, "Queued timestamp packet\n");
228962306a36Sopenharmony_ci	}
229062306a36Sopenharmony_ci
229162306a36Sopenharmony_ci	return retval;
229262306a36Sopenharmony_ci}
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_ci/**
229562306a36Sopenharmony_ci * liquidio_xmit - Transmit networks packets to the Octeon interface
229662306a36Sopenharmony_ci * @skb: skbuff struct to be passed to network layer.
229762306a36Sopenharmony_ci * @netdev: pointer to network device
229862306a36Sopenharmony_ci *
229962306a36Sopenharmony_ci * Return: whether the packet was transmitted to the device okay or not
230062306a36Sopenharmony_ci *             (NETDEV_TX_OK or NETDEV_TX_BUSY)
230162306a36Sopenharmony_ci */
230262306a36Sopenharmony_cistatic netdev_tx_t liquidio_xmit(struct sk_buff *skb, struct net_device *netdev)
230362306a36Sopenharmony_ci{
230462306a36Sopenharmony_ci	struct lio *lio;
230562306a36Sopenharmony_ci	struct octnet_buf_free_info *finfo;
230662306a36Sopenharmony_ci	union octnic_cmd_setup cmdsetup;
230762306a36Sopenharmony_ci	struct octnic_data_pkt ndata;
230862306a36Sopenharmony_ci	struct octeon_device *oct;
230962306a36Sopenharmony_ci	struct oct_iq_stats *stats;
231062306a36Sopenharmony_ci	struct octeon_instr_irh *irh;
231162306a36Sopenharmony_ci	union tx_info *tx_info;
231262306a36Sopenharmony_ci	int status = 0;
231362306a36Sopenharmony_ci	int q_idx = 0, iq_no = 0;
231462306a36Sopenharmony_ci	int j, xmit_more = 0;
231562306a36Sopenharmony_ci	u64 dptr = 0;
231662306a36Sopenharmony_ci	u32 tag = 0;
231762306a36Sopenharmony_ci
231862306a36Sopenharmony_ci	lio = GET_LIO(netdev);
231962306a36Sopenharmony_ci	oct = lio->oct_dev;
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ci	q_idx = skb_iq(oct, skb);
232262306a36Sopenharmony_ci	tag = q_idx;
232362306a36Sopenharmony_ci	iq_no = lio->linfo.txpciq[q_idx].s.q_no;
232462306a36Sopenharmony_ci
232562306a36Sopenharmony_ci	stats = &oct->instr_queue[iq_no]->stats;
232662306a36Sopenharmony_ci
232762306a36Sopenharmony_ci	/* Check for all conditions in which the current packet cannot be
232862306a36Sopenharmony_ci	 * transmitted.
232962306a36Sopenharmony_ci	 */
233062306a36Sopenharmony_ci	if (!(atomic_read(&lio->ifstate) & LIO_IFSTATE_RUNNING) ||
233162306a36Sopenharmony_ci	    (!lio->linfo.link.s.link_up) ||
233262306a36Sopenharmony_ci	    (skb->len <= 0)) {
233362306a36Sopenharmony_ci		netif_info(lio, tx_err, lio->netdev,
233462306a36Sopenharmony_ci			   "Transmit failed link_status : %d\n",
233562306a36Sopenharmony_ci			   lio->linfo.link.s.link_up);
233662306a36Sopenharmony_ci		goto lio_xmit_failed;
233762306a36Sopenharmony_ci	}
233862306a36Sopenharmony_ci
233962306a36Sopenharmony_ci	/* Use space in skb->cb to store info used to unmap and
234062306a36Sopenharmony_ci	 * free the buffers.
234162306a36Sopenharmony_ci	 */
234262306a36Sopenharmony_ci	finfo = (struct octnet_buf_free_info *)skb->cb;
234362306a36Sopenharmony_ci	finfo->lio = lio;
234462306a36Sopenharmony_ci	finfo->skb = skb;
234562306a36Sopenharmony_ci	finfo->sc = NULL;
234662306a36Sopenharmony_ci
234762306a36Sopenharmony_ci	/* Prepare the attributes for the data to be passed to OSI. */
234862306a36Sopenharmony_ci	memset(&ndata, 0, sizeof(struct octnic_data_pkt));
234962306a36Sopenharmony_ci
235062306a36Sopenharmony_ci	ndata.buf = (void *)finfo;
235162306a36Sopenharmony_ci
235262306a36Sopenharmony_ci	ndata.q_no = iq_no;
235362306a36Sopenharmony_ci
235462306a36Sopenharmony_ci	if (octnet_iq_is_full(oct, ndata.q_no)) {
235562306a36Sopenharmony_ci		/* defer sending if queue is full */
235662306a36Sopenharmony_ci		netif_info(lio, tx_err, lio->netdev, "Transmit failed iq:%d full\n",
235762306a36Sopenharmony_ci			   ndata.q_no);
235862306a36Sopenharmony_ci		stats->tx_iq_busy++;
235962306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
236062306a36Sopenharmony_ci	}
236162306a36Sopenharmony_ci
236262306a36Sopenharmony_ci	/* pr_info(" XMIT - valid Qs: %d, 1st Q no: %d, cpu:  %d, q_no:%d\n",
236362306a36Sopenharmony_ci	 *	lio->linfo.num_txpciq, lio->txq, cpu, ndata.q_no);
236462306a36Sopenharmony_ci	 */
236562306a36Sopenharmony_ci
236662306a36Sopenharmony_ci	ndata.datasize = skb->len;
236762306a36Sopenharmony_ci
236862306a36Sopenharmony_ci	cmdsetup.u64 = 0;
236962306a36Sopenharmony_ci	cmdsetup.s.iq_no = iq_no;
237062306a36Sopenharmony_ci
237162306a36Sopenharmony_ci	if (skb->ip_summed == CHECKSUM_PARTIAL) {
237262306a36Sopenharmony_ci		if (skb->encapsulation) {
237362306a36Sopenharmony_ci			cmdsetup.s.tnl_csum = 1;
237462306a36Sopenharmony_ci			stats->tx_vxlan++;
237562306a36Sopenharmony_ci		} else {
237662306a36Sopenharmony_ci			cmdsetup.s.transport_csum = 1;
237762306a36Sopenharmony_ci		}
237862306a36Sopenharmony_ci	}
237962306a36Sopenharmony_ci	if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
238062306a36Sopenharmony_ci		skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
238162306a36Sopenharmony_ci		cmdsetup.s.timestamp = 1;
238262306a36Sopenharmony_ci	}
238362306a36Sopenharmony_ci
238462306a36Sopenharmony_ci	if (skb_shinfo(skb)->nr_frags == 0) {
238562306a36Sopenharmony_ci		cmdsetup.s.u.datasize = skb->len;
238662306a36Sopenharmony_ci		octnet_prepare_pci_cmd(oct, &ndata.cmd, &cmdsetup, tag);
238762306a36Sopenharmony_ci
238862306a36Sopenharmony_ci		/* Offload checksum calculation for TCP/UDP packets */
238962306a36Sopenharmony_ci		dptr = dma_map_single(&oct->pci_dev->dev,
239062306a36Sopenharmony_ci				      skb->data,
239162306a36Sopenharmony_ci				      skb->len,
239262306a36Sopenharmony_ci				      DMA_TO_DEVICE);
239362306a36Sopenharmony_ci		if (dma_mapping_error(&oct->pci_dev->dev, dptr)) {
239462306a36Sopenharmony_ci			dev_err(&oct->pci_dev->dev, "%s DMA mapping error 1\n",
239562306a36Sopenharmony_ci				__func__);
239662306a36Sopenharmony_ci			stats->tx_dmamap_fail++;
239762306a36Sopenharmony_ci			return NETDEV_TX_BUSY;
239862306a36Sopenharmony_ci		}
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_ci		if (OCTEON_CN23XX_PF(oct))
240162306a36Sopenharmony_ci			ndata.cmd.cmd3.dptr = dptr;
240262306a36Sopenharmony_ci		else
240362306a36Sopenharmony_ci			ndata.cmd.cmd2.dptr = dptr;
240462306a36Sopenharmony_ci		finfo->dptr = dptr;
240562306a36Sopenharmony_ci		ndata.reqtype = REQTYPE_NORESP_NET;
240662306a36Sopenharmony_ci
240762306a36Sopenharmony_ci	} else {
240862306a36Sopenharmony_ci		int i, frags;
240962306a36Sopenharmony_ci		skb_frag_t *frag;
241062306a36Sopenharmony_ci		struct octnic_gather *g;
241162306a36Sopenharmony_ci
241262306a36Sopenharmony_ci		spin_lock(&lio->glist_lock[q_idx]);
241362306a36Sopenharmony_ci		g = (struct octnic_gather *)
241462306a36Sopenharmony_ci			lio_list_delete_head(&lio->glist[q_idx]);
241562306a36Sopenharmony_ci		spin_unlock(&lio->glist_lock[q_idx]);
241662306a36Sopenharmony_ci
241762306a36Sopenharmony_ci		if (!g) {
241862306a36Sopenharmony_ci			netif_info(lio, tx_err, lio->netdev,
241962306a36Sopenharmony_ci				   "Transmit scatter gather: glist null!\n");
242062306a36Sopenharmony_ci			goto lio_xmit_failed;
242162306a36Sopenharmony_ci		}
242262306a36Sopenharmony_ci
242362306a36Sopenharmony_ci		cmdsetup.s.gather = 1;
242462306a36Sopenharmony_ci		cmdsetup.s.u.gatherptrs = (skb_shinfo(skb)->nr_frags + 1);
242562306a36Sopenharmony_ci		octnet_prepare_pci_cmd(oct, &ndata.cmd, &cmdsetup, tag);
242662306a36Sopenharmony_ci
242762306a36Sopenharmony_ci		memset(g->sg, 0, g->sg_size);
242862306a36Sopenharmony_ci
242962306a36Sopenharmony_ci		g->sg[0].ptr[0] = dma_map_single(&oct->pci_dev->dev,
243062306a36Sopenharmony_ci						 skb->data,
243162306a36Sopenharmony_ci						 (skb->len - skb->data_len),
243262306a36Sopenharmony_ci						 DMA_TO_DEVICE);
243362306a36Sopenharmony_ci		if (dma_mapping_error(&oct->pci_dev->dev, g->sg[0].ptr[0])) {
243462306a36Sopenharmony_ci			dev_err(&oct->pci_dev->dev, "%s DMA mapping error 2\n",
243562306a36Sopenharmony_ci				__func__);
243662306a36Sopenharmony_ci			stats->tx_dmamap_fail++;
243762306a36Sopenharmony_ci			return NETDEV_TX_BUSY;
243862306a36Sopenharmony_ci		}
243962306a36Sopenharmony_ci		add_sg_size(&g->sg[0], (skb->len - skb->data_len), 0);
244062306a36Sopenharmony_ci
244162306a36Sopenharmony_ci		frags = skb_shinfo(skb)->nr_frags;
244262306a36Sopenharmony_ci		i = 1;
244362306a36Sopenharmony_ci		while (frags--) {
244462306a36Sopenharmony_ci			frag = &skb_shinfo(skb)->frags[i - 1];
244562306a36Sopenharmony_ci
244662306a36Sopenharmony_ci			g->sg[(i >> 2)].ptr[(i & 3)] =
244762306a36Sopenharmony_ci				skb_frag_dma_map(&oct->pci_dev->dev,
244862306a36Sopenharmony_ci					         frag, 0, skb_frag_size(frag),
244962306a36Sopenharmony_ci						 DMA_TO_DEVICE);
245062306a36Sopenharmony_ci
245162306a36Sopenharmony_ci			if (dma_mapping_error(&oct->pci_dev->dev,
245262306a36Sopenharmony_ci					      g->sg[i >> 2].ptr[i & 3])) {
245362306a36Sopenharmony_ci				dma_unmap_single(&oct->pci_dev->dev,
245462306a36Sopenharmony_ci						 g->sg[0].ptr[0],
245562306a36Sopenharmony_ci						 skb->len - skb->data_len,
245662306a36Sopenharmony_ci						 DMA_TO_DEVICE);
245762306a36Sopenharmony_ci				for (j = 1; j < i; j++) {
245862306a36Sopenharmony_ci					frag = &skb_shinfo(skb)->frags[j - 1];
245962306a36Sopenharmony_ci					dma_unmap_page(&oct->pci_dev->dev,
246062306a36Sopenharmony_ci						       g->sg[j >> 2].ptr[j & 3],
246162306a36Sopenharmony_ci						       skb_frag_size(frag),
246262306a36Sopenharmony_ci						       DMA_TO_DEVICE);
246362306a36Sopenharmony_ci				}
246462306a36Sopenharmony_ci				dev_err(&oct->pci_dev->dev, "%s DMA mapping error 3\n",
246562306a36Sopenharmony_ci					__func__);
246662306a36Sopenharmony_ci				return NETDEV_TX_BUSY;
246762306a36Sopenharmony_ci			}
246862306a36Sopenharmony_ci
246962306a36Sopenharmony_ci			add_sg_size(&g->sg[(i >> 2)], skb_frag_size(frag),
247062306a36Sopenharmony_ci				    (i & 3));
247162306a36Sopenharmony_ci			i++;
247262306a36Sopenharmony_ci		}
247362306a36Sopenharmony_ci
247462306a36Sopenharmony_ci		dptr = g->sg_dma_ptr;
247562306a36Sopenharmony_ci
247662306a36Sopenharmony_ci		if (OCTEON_CN23XX_PF(oct))
247762306a36Sopenharmony_ci			ndata.cmd.cmd3.dptr = dptr;
247862306a36Sopenharmony_ci		else
247962306a36Sopenharmony_ci			ndata.cmd.cmd2.dptr = dptr;
248062306a36Sopenharmony_ci		finfo->dptr = dptr;
248162306a36Sopenharmony_ci		finfo->g = g;
248262306a36Sopenharmony_ci
248362306a36Sopenharmony_ci		ndata.reqtype = REQTYPE_NORESP_NET_SG;
248462306a36Sopenharmony_ci	}
248562306a36Sopenharmony_ci
248662306a36Sopenharmony_ci	if (OCTEON_CN23XX_PF(oct)) {
248762306a36Sopenharmony_ci		irh = (struct octeon_instr_irh *)&ndata.cmd.cmd3.irh;
248862306a36Sopenharmony_ci		tx_info = (union tx_info *)&ndata.cmd.cmd3.ossp[0];
248962306a36Sopenharmony_ci	} else {
249062306a36Sopenharmony_ci		irh = (struct octeon_instr_irh *)&ndata.cmd.cmd2.irh;
249162306a36Sopenharmony_ci		tx_info = (union tx_info *)&ndata.cmd.cmd2.ossp[0];
249262306a36Sopenharmony_ci	}
249362306a36Sopenharmony_ci
249462306a36Sopenharmony_ci	if (skb_shinfo(skb)->gso_size) {
249562306a36Sopenharmony_ci		tx_info->s.gso_size = skb_shinfo(skb)->gso_size;
249662306a36Sopenharmony_ci		tx_info->s.gso_segs = skb_shinfo(skb)->gso_segs;
249762306a36Sopenharmony_ci		stats->tx_gso++;
249862306a36Sopenharmony_ci	}
249962306a36Sopenharmony_ci
250062306a36Sopenharmony_ci	/* HW insert VLAN tag */
250162306a36Sopenharmony_ci	if (skb_vlan_tag_present(skb)) {
250262306a36Sopenharmony_ci		irh->priority = skb_vlan_tag_get(skb) >> 13;
250362306a36Sopenharmony_ci		irh->vlan = skb_vlan_tag_get(skb) & 0xfff;
250462306a36Sopenharmony_ci	}
250562306a36Sopenharmony_ci
250662306a36Sopenharmony_ci	xmit_more = netdev_xmit_more();
250762306a36Sopenharmony_ci
250862306a36Sopenharmony_ci	if (unlikely(cmdsetup.s.timestamp))
250962306a36Sopenharmony_ci		status = send_nic_timestamp_pkt(oct, &ndata, finfo, xmit_more);
251062306a36Sopenharmony_ci	else
251162306a36Sopenharmony_ci		status = octnet_send_nic_data_pkt(oct, &ndata, xmit_more);
251262306a36Sopenharmony_ci	if (status == IQ_SEND_FAILED)
251362306a36Sopenharmony_ci		goto lio_xmit_failed;
251462306a36Sopenharmony_ci
251562306a36Sopenharmony_ci	netif_info(lio, tx_queued, lio->netdev, "Transmit queued successfully\n");
251662306a36Sopenharmony_ci
251762306a36Sopenharmony_ci	if (status == IQ_SEND_STOP)
251862306a36Sopenharmony_ci		netif_stop_subqueue(netdev, q_idx);
251962306a36Sopenharmony_ci
252062306a36Sopenharmony_ci	netif_trans_update(netdev);
252162306a36Sopenharmony_ci
252262306a36Sopenharmony_ci	if (tx_info->s.gso_segs)
252362306a36Sopenharmony_ci		stats->tx_done += tx_info->s.gso_segs;
252462306a36Sopenharmony_ci	else
252562306a36Sopenharmony_ci		stats->tx_done++;
252662306a36Sopenharmony_ci	stats->tx_tot_bytes += ndata.datasize;
252762306a36Sopenharmony_ci
252862306a36Sopenharmony_ci	return NETDEV_TX_OK;
252962306a36Sopenharmony_ci
253062306a36Sopenharmony_cilio_xmit_failed:
253162306a36Sopenharmony_ci	stats->tx_dropped++;
253262306a36Sopenharmony_ci	netif_info(lio, tx_err, lio->netdev, "IQ%d Transmit dropped:%llu\n",
253362306a36Sopenharmony_ci		   iq_no, stats->tx_dropped);
253462306a36Sopenharmony_ci	if (dptr)
253562306a36Sopenharmony_ci		dma_unmap_single(&oct->pci_dev->dev, dptr,
253662306a36Sopenharmony_ci				 ndata.datasize, DMA_TO_DEVICE);
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci	octeon_ring_doorbell_locked(oct, iq_no);
253962306a36Sopenharmony_ci
254062306a36Sopenharmony_ci	tx_buffer_free(skb);
254162306a36Sopenharmony_ci	return NETDEV_TX_OK;
254262306a36Sopenharmony_ci}
254362306a36Sopenharmony_ci
254462306a36Sopenharmony_ci/**
254562306a36Sopenharmony_ci * liquidio_tx_timeout - Network device Tx timeout
254662306a36Sopenharmony_ci * @netdev:    pointer to network device
254762306a36Sopenharmony_ci * @txqueue: index of the hung transmit queue
254862306a36Sopenharmony_ci */
254962306a36Sopenharmony_cistatic void liquidio_tx_timeout(struct net_device *netdev, unsigned int txqueue)
255062306a36Sopenharmony_ci{
255162306a36Sopenharmony_ci	struct lio *lio;
255262306a36Sopenharmony_ci
255362306a36Sopenharmony_ci	lio = GET_LIO(netdev);
255462306a36Sopenharmony_ci
255562306a36Sopenharmony_ci	netif_info(lio, tx_err, lio->netdev,
255662306a36Sopenharmony_ci		   "Transmit timeout tx_dropped:%ld, waking up queues now!!\n",
255762306a36Sopenharmony_ci		   netdev->stats.tx_dropped);
255862306a36Sopenharmony_ci	netif_trans_update(netdev);
255962306a36Sopenharmony_ci	wake_txqs(netdev);
256062306a36Sopenharmony_ci}
256162306a36Sopenharmony_ci
256262306a36Sopenharmony_cistatic int liquidio_vlan_rx_add_vid(struct net_device *netdev,
256362306a36Sopenharmony_ci				    __be16 proto __attribute__((unused)),
256462306a36Sopenharmony_ci				    u16 vid)
256562306a36Sopenharmony_ci{
256662306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
256762306a36Sopenharmony_ci	struct octeon_device *oct = lio->oct_dev;
256862306a36Sopenharmony_ci	struct octnic_ctrl_pkt nctrl;
256962306a36Sopenharmony_ci	int ret = 0;
257062306a36Sopenharmony_ci
257162306a36Sopenharmony_ci	memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt));
257262306a36Sopenharmony_ci
257362306a36Sopenharmony_ci	nctrl.ncmd.u64 = 0;
257462306a36Sopenharmony_ci	nctrl.ncmd.s.cmd = OCTNET_CMD_ADD_VLAN_FILTER;
257562306a36Sopenharmony_ci	nctrl.ncmd.s.param1 = vid;
257662306a36Sopenharmony_ci	nctrl.iq_no = lio->linfo.txpciq[0].s.q_no;
257762306a36Sopenharmony_ci	nctrl.netpndev = (u64)netdev;
257862306a36Sopenharmony_ci	nctrl.cb_fn = liquidio_link_ctrl_cmd_completion;
257962306a36Sopenharmony_ci
258062306a36Sopenharmony_ci	ret = octnet_send_nic_ctrl_pkt(lio->oct_dev, &nctrl);
258162306a36Sopenharmony_ci	if (ret) {
258262306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "Add VLAN filter failed in core (ret: 0x%x)\n",
258362306a36Sopenharmony_ci			ret);
258462306a36Sopenharmony_ci		if (ret > 0)
258562306a36Sopenharmony_ci			ret = -EIO;
258662306a36Sopenharmony_ci	}
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ci	return ret;
258962306a36Sopenharmony_ci}
259062306a36Sopenharmony_ci
259162306a36Sopenharmony_cistatic int liquidio_vlan_rx_kill_vid(struct net_device *netdev,
259262306a36Sopenharmony_ci				     __be16 proto __attribute__((unused)),
259362306a36Sopenharmony_ci				     u16 vid)
259462306a36Sopenharmony_ci{
259562306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
259662306a36Sopenharmony_ci	struct octeon_device *oct = lio->oct_dev;
259762306a36Sopenharmony_ci	struct octnic_ctrl_pkt nctrl;
259862306a36Sopenharmony_ci	int ret = 0;
259962306a36Sopenharmony_ci
260062306a36Sopenharmony_ci	memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt));
260162306a36Sopenharmony_ci
260262306a36Sopenharmony_ci	nctrl.ncmd.u64 = 0;
260362306a36Sopenharmony_ci	nctrl.ncmd.s.cmd = OCTNET_CMD_DEL_VLAN_FILTER;
260462306a36Sopenharmony_ci	nctrl.ncmd.s.param1 = vid;
260562306a36Sopenharmony_ci	nctrl.iq_no = lio->linfo.txpciq[0].s.q_no;
260662306a36Sopenharmony_ci	nctrl.netpndev = (u64)netdev;
260762306a36Sopenharmony_ci	nctrl.cb_fn = liquidio_link_ctrl_cmd_completion;
260862306a36Sopenharmony_ci
260962306a36Sopenharmony_ci	ret = octnet_send_nic_ctrl_pkt(lio->oct_dev, &nctrl);
261062306a36Sopenharmony_ci	if (ret) {
261162306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "Del VLAN filter failed in core (ret: 0x%x)\n",
261262306a36Sopenharmony_ci			ret);
261362306a36Sopenharmony_ci		if (ret > 0)
261462306a36Sopenharmony_ci			ret = -EIO;
261562306a36Sopenharmony_ci	}
261662306a36Sopenharmony_ci	return ret;
261762306a36Sopenharmony_ci}
261862306a36Sopenharmony_ci
261962306a36Sopenharmony_ci/**
262062306a36Sopenharmony_ci * liquidio_set_rxcsum_command - Sending command to enable/disable RX checksum offload
262162306a36Sopenharmony_ci * @netdev:                pointer to network device
262262306a36Sopenharmony_ci * @command:               OCTNET_CMD_TNL_RX_CSUM_CTL
262362306a36Sopenharmony_ci * @rx_cmd:                OCTNET_CMD_RXCSUM_ENABLE/OCTNET_CMD_RXCSUM_DISABLE
262462306a36Sopenharmony_ci * Returns:                SUCCESS or FAILURE
262562306a36Sopenharmony_ci */
262662306a36Sopenharmony_cistatic int liquidio_set_rxcsum_command(struct net_device *netdev, int command,
262762306a36Sopenharmony_ci				       u8 rx_cmd)
262862306a36Sopenharmony_ci{
262962306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
263062306a36Sopenharmony_ci	struct octeon_device *oct = lio->oct_dev;
263162306a36Sopenharmony_ci	struct octnic_ctrl_pkt nctrl;
263262306a36Sopenharmony_ci	int ret = 0;
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_ci	memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt));
263562306a36Sopenharmony_ci
263662306a36Sopenharmony_ci	nctrl.ncmd.u64 = 0;
263762306a36Sopenharmony_ci	nctrl.ncmd.s.cmd = command;
263862306a36Sopenharmony_ci	nctrl.ncmd.s.param1 = rx_cmd;
263962306a36Sopenharmony_ci	nctrl.iq_no = lio->linfo.txpciq[0].s.q_no;
264062306a36Sopenharmony_ci	nctrl.netpndev = (u64)netdev;
264162306a36Sopenharmony_ci	nctrl.cb_fn = liquidio_link_ctrl_cmd_completion;
264262306a36Sopenharmony_ci
264362306a36Sopenharmony_ci	ret = octnet_send_nic_ctrl_pkt(lio->oct_dev, &nctrl);
264462306a36Sopenharmony_ci	if (ret) {
264562306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev,
264662306a36Sopenharmony_ci			"DEVFLAGS RXCSUM change failed in core(ret:0x%x)\n",
264762306a36Sopenharmony_ci			ret);
264862306a36Sopenharmony_ci		if (ret > 0)
264962306a36Sopenharmony_ci			ret = -EIO;
265062306a36Sopenharmony_ci	}
265162306a36Sopenharmony_ci	return ret;
265262306a36Sopenharmony_ci}
265362306a36Sopenharmony_ci
265462306a36Sopenharmony_ci/**
265562306a36Sopenharmony_ci * liquidio_vxlan_port_command - Sending command to add/delete VxLAN UDP port to firmware
265662306a36Sopenharmony_ci * @netdev:                pointer to network device
265762306a36Sopenharmony_ci * @command:               OCTNET_CMD_VXLAN_PORT_CONFIG
265862306a36Sopenharmony_ci * @vxlan_port:            VxLAN port to be added or deleted
265962306a36Sopenharmony_ci * @vxlan_cmd_bit:         OCTNET_CMD_VXLAN_PORT_ADD,
266062306a36Sopenharmony_ci *                              OCTNET_CMD_VXLAN_PORT_DEL
266162306a36Sopenharmony_ci * Return:                     SUCCESS or FAILURE
266262306a36Sopenharmony_ci */
266362306a36Sopenharmony_cistatic int liquidio_vxlan_port_command(struct net_device *netdev, int command,
266462306a36Sopenharmony_ci				       u16 vxlan_port, u8 vxlan_cmd_bit)
266562306a36Sopenharmony_ci{
266662306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
266762306a36Sopenharmony_ci	struct octeon_device *oct = lio->oct_dev;
266862306a36Sopenharmony_ci	struct octnic_ctrl_pkt nctrl;
266962306a36Sopenharmony_ci	int ret = 0;
267062306a36Sopenharmony_ci
267162306a36Sopenharmony_ci	memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt));
267262306a36Sopenharmony_ci
267362306a36Sopenharmony_ci	nctrl.ncmd.u64 = 0;
267462306a36Sopenharmony_ci	nctrl.ncmd.s.cmd = command;
267562306a36Sopenharmony_ci	nctrl.ncmd.s.more = vxlan_cmd_bit;
267662306a36Sopenharmony_ci	nctrl.ncmd.s.param1 = vxlan_port;
267762306a36Sopenharmony_ci	nctrl.iq_no = lio->linfo.txpciq[0].s.q_no;
267862306a36Sopenharmony_ci	nctrl.netpndev = (u64)netdev;
267962306a36Sopenharmony_ci	nctrl.cb_fn = liquidio_link_ctrl_cmd_completion;
268062306a36Sopenharmony_ci
268162306a36Sopenharmony_ci	ret = octnet_send_nic_ctrl_pkt(lio->oct_dev, &nctrl);
268262306a36Sopenharmony_ci	if (ret) {
268362306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev,
268462306a36Sopenharmony_ci			"VxLAN port add/delete failed in core (ret:0x%x)\n",
268562306a36Sopenharmony_ci			ret);
268662306a36Sopenharmony_ci		if (ret > 0)
268762306a36Sopenharmony_ci			ret = -EIO;
268862306a36Sopenharmony_ci	}
268962306a36Sopenharmony_ci	return ret;
269062306a36Sopenharmony_ci}
269162306a36Sopenharmony_ci
269262306a36Sopenharmony_cistatic int liquidio_udp_tunnel_set_port(struct net_device *netdev,
269362306a36Sopenharmony_ci					unsigned int table, unsigned int entry,
269462306a36Sopenharmony_ci					struct udp_tunnel_info *ti)
269562306a36Sopenharmony_ci{
269662306a36Sopenharmony_ci	return liquidio_vxlan_port_command(netdev,
269762306a36Sopenharmony_ci					   OCTNET_CMD_VXLAN_PORT_CONFIG,
269862306a36Sopenharmony_ci					   htons(ti->port),
269962306a36Sopenharmony_ci					   OCTNET_CMD_VXLAN_PORT_ADD);
270062306a36Sopenharmony_ci}
270162306a36Sopenharmony_ci
270262306a36Sopenharmony_cistatic int liquidio_udp_tunnel_unset_port(struct net_device *netdev,
270362306a36Sopenharmony_ci					  unsigned int table,
270462306a36Sopenharmony_ci					  unsigned int entry,
270562306a36Sopenharmony_ci					  struct udp_tunnel_info *ti)
270662306a36Sopenharmony_ci{
270762306a36Sopenharmony_ci	return liquidio_vxlan_port_command(netdev,
270862306a36Sopenharmony_ci					   OCTNET_CMD_VXLAN_PORT_CONFIG,
270962306a36Sopenharmony_ci					   htons(ti->port),
271062306a36Sopenharmony_ci					   OCTNET_CMD_VXLAN_PORT_DEL);
271162306a36Sopenharmony_ci}
271262306a36Sopenharmony_ci
271362306a36Sopenharmony_cistatic const struct udp_tunnel_nic_info liquidio_udp_tunnels = {
271462306a36Sopenharmony_ci	.set_port	= liquidio_udp_tunnel_set_port,
271562306a36Sopenharmony_ci	.unset_port	= liquidio_udp_tunnel_unset_port,
271662306a36Sopenharmony_ci	.tables		= {
271762306a36Sopenharmony_ci		{ .n_entries = 1024, .tunnel_types = UDP_TUNNEL_TYPE_VXLAN, },
271862306a36Sopenharmony_ci	},
271962306a36Sopenharmony_ci};
272062306a36Sopenharmony_ci
272162306a36Sopenharmony_ci/**
272262306a36Sopenharmony_ci * liquidio_fix_features - Net device fix features
272362306a36Sopenharmony_ci * @netdev:  pointer to network device
272462306a36Sopenharmony_ci * @request: features requested
272562306a36Sopenharmony_ci * Return: updated features list
272662306a36Sopenharmony_ci */
272762306a36Sopenharmony_cistatic netdev_features_t liquidio_fix_features(struct net_device *netdev,
272862306a36Sopenharmony_ci					       netdev_features_t request)
272962306a36Sopenharmony_ci{
273062306a36Sopenharmony_ci	struct lio *lio = netdev_priv(netdev);
273162306a36Sopenharmony_ci
273262306a36Sopenharmony_ci	if ((request & NETIF_F_RXCSUM) &&
273362306a36Sopenharmony_ci	    !(lio->dev_capability & NETIF_F_RXCSUM))
273462306a36Sopenharmony_ci		request &= ~NETIF_F_RXCSUM;
273562306a36Sopenharmony_ci
273662306a36Sopenharmony_ci	if ((request & NETIF_F_HW_CSUM) &&
273762306a36Sopenharmony_ci	    !(lio->dev_capability & NETIF_F_HW_CSUM))
273862306a36Sopenharmony_ci		request &= ~NETIF_F_HW_CSUM;
273962306a36Sopenharmony_ci
274062306a36Sopenharmony_ci	if ((request & NETIF_F_TSO) && !(lio->dev_capability & NETIF_F_TSO))
274162306a36Sopenharmony_ci		request &= ~NETIF_F_TSO;
274262306a36Sopenharmony_ci
274362306a36Sopenharmony_ci	if ((request & NETIF_F_TSO6) && !(lio->dev_capability & NETIF_F_TSO6))
274462306a36Sopenharmony_ci		request &= ~NETIF_F_TSO6;
274562306a36Sopenharmony_ci
274662306a36Sopenharmony_ci	if ((request & NETIF_F_LRO) && !(lio->dev_capability & NETIF_F_LRO))
274762306a36Sopenharmony_ci		request &= ~NETIF_F_LRO;
274862306a36Sopenharmony_ci
274962306a36Sopenharmony_ci	/*Disable LRO if RXCSUM is off */
275062306a36Sopenharmony_ci	if (!(request & NETIF_F_RXCSUM) && (netdev->features & NETIF_F_LRO) &&
275162306a36Sopenharmony_ci	    (lio->dev_capability & NETIF_F_LRO))
275262306a36Sopenharmony_ci		request &= ~NETIF_F_LRO;
275362306a36Sopenharmony_ci
275462306a36Sopenharmony_ci	if ((request & NETIF_F_HW_VLAN_CTAG_FILTER) &&
275562306a36Sopenharmony_ci	    !(lio->dev_capability & NETIF_F_HW_VLAN_CTAG_FILTER))
275662306a36Sopenharmony_ci		request &= ~NETIF_F_HW_VLAN_CTAG_FILTER;
275762306a36Sopenharmony_ci
275862306a36Sopenharmony_ci	return request;
275962306a36Sopenharmony_ci}
276062306a36Sopenharmony_ci
276162306a36Sopenharmony_ci/**
276262306a36Sopenharmony_ci * liquidio_set_features - Net device set features
276362306a36Sopenharmony_ci * @netdev:  pointer to network device
276462306a36Sopenharmony_ci * @features: features to enable/disable
276562306a36Sopenharmony_ci */
276662306a36Sopenharmony_cistatic int liquidio_set_features(struct net_device *netdev,
276762306a36Sopenharmony_ci				 netdev_features_t features)
276862306a36Sopenharmony_ci{
276962306a36Sopenharmony_ci	struct lio *lio = netdev_priv(netdev);
277062306a36Sopenharmony_ci
277162306a36Sopenharmony_ci	if ((features & NETIF_F_LRO) &&
277262306a36Sopenharmony_ci	    (lio->dev_capability & NETIF_F_LRO) &&
277362306a36Sopenharmony_ci	    !(netdev->features & NETIF_F_LRO))
277462306a36Sopenharmony_ci		liquidio_set_feature(netdev, OCTNET_CMD_LRO_ENABLE,
277562306a36Sopenharmony_ci				     OCTNIC_LROIPV4 | OCTNIC_LROIPV6);
277662306a36Sopenharmony_ci	else if (!(features & NETIF_F_LRO) &&
277762306a36Sopenharmony_ci		 (lio->dev_capability & NETIF_F_LRO) &&
277862306a36Sopenharmony_ci		 (netdev->features & NETIF_F_LRO))
277962306a36Sopenharmony_ci		liquidio_set_feature(netdev, OCTNET_CMD_LRO_DISABLE,
278062306a36Sopenharmony_ci				     OCTNIC_LROIPV4 | OCTNIC_LROIPV6);
278162306a36Sopenharmony_ci
278262306a36Sopenharmony_ci	/* Sending command to firmware to enable/disable RX checksum
278362306a36Sopenharmony_ci	 * offload settings using ethtool
278462306a36Sopenharmony_ci	 */
278562306a36Sopenharmony_ci	if (!(netdev->features & NETIF_F_RXCSUM) &&
278662306a36Sopenharmony_ci	    (lio->enc_dev_capability & NETIF_F_RXCSUM) &&
278762306a36Sopenharmony_ci	    (features & NETIF_F_RXCSUM))
278862306a36Sopenharmony_ci		liquidio_set_rxcsum_command(netdev,
278962306a36Sopenharmony_ci					    OCTNET_CMD_TNL_RX_CSUM_CTL,
279062306a36Sopenharmony_ci					    OCTNET_CMD_RXCSUM_ENABLE);
279162306a36Sopenharmony_ci	else if ((netdev->features & NETIF_F_RXCSUM) &&
279262306a36Sopenharmony_ci		 (lio->enc_dev_capability & NETIF_F_RXCSUM) &&
279362306a36Sopenharmony_ci		 !(features & NETIF_F_RXCSUM))
279462306a36Sopenharmony_ci		liquidio_set_rxcsum_command(netdev, OCTNET_CMD_TNL_RX_CSUM_CTL,
279562306a36Sopenharmony_ci					    OCTNET_CMD_RXCSUM_DISABLE);
279662306a36Sopenharmony_ci
279762306a36Sopenharmony_ci	if ((features & NETIF_F_HW_VLAN_CTAG_FILTER) &&
279862306a36Sopenharmony_ci	    (lio->dev_capability & NETIF_F_HW_VLAN_CTAG_FILTER) &&
279962306a36Sopenharmony_ci	    !(netdev->features & NETIF_F_HW_VLAN_CTAG_FILTER))
280062306a36Sopenharmony_ci		liquidio_set_feature(netdev, OCTNET_CMD_VLAN_FILTER_CTL,
280162306a36Sopenharmony_ci				     OCTNET_CMD_VLAN_FILTER_ENABLE);
280262306a36Sopenharmony_ci	else if (!(features & NETIF_F_HW_VLAN_CTAG_FILTER) &&
280362306a36Sopenharmony_ci		 (lio->dev_capability & NETIF_F_HW_VLAN_CTAG_FILTER) &&
280462306a36Sopenharmony_ci		 (netdev->features & NETIF_F_HW_VLAN_CTAG_FILTER))
280562306a36Sopenharmony_ci		liquidio_set_feature(netdev, OCTNET_CMD_VLAN_FILTER_CTL,
280662306a36Sopenharmony_ci				     OCTNET_CMD_VLAN_FILTER_DISABLE);
280762306a36Sopenharmony_ci
280862306a36Sopenharmony_ci	return 0;
280962306a36Sopenharmony_ci}
281062306a36Sopenharmony_ci
281162306a36Sopenharmony_cistatic int __liquidio_set_vf_mac(struct net_device *netdev, int vfidx,
281262306a36Sopenharmony_ci				 u8 *mac, bool is_admin_assigned)
281362306a36Sopenharmony_ci{
281462306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
281562306a36Sopenharmony_ci	struct octeon_device *oct = lio->oct_dev;
281662306a36Sopenharmony_ci	struct octnic_ctrl_pkt nctrl;
281762306a36Sopenharmony_ci	int ret = 0;
281862306a36Sopenharmony_ci
281962306a36Sopenharmony_ci	if (!is_valid_ether_addr(mac))
282062306a36Sopenharmony_ci		return -EINVAL;
282162306a36Sopenharmony_ci
282262306a36Sopenharmony_ci	if (vfidx < 0 || vfidx >= oct->sriov_info.max_vfs)
282362306a36Sopenharmony_ci		return -EINVAL;
282462306a36Sopenharmony_ci
282562306a36Sopenharmony_ci	memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt));
282662306a36Sopenharmony_ci
282762306a36Sopenharmony_ci	nctrl.ncmd.u64 = 0;
282862306a36Sopenharmony_ci	nctrl.ncmd.s.cmd = OCTNET_CMD_CHANGE_MACADDR;
282962306a36Sopenharmony_ci	/* vfidx is 0 based, but vf_num (param1) is 1 based */
283062306a36Sopenharmony_ci	nctrl.ncmd.s.param1 = vfidx + 1;
283162306a36Sopenharmony_ci	nctrl.ncmd.s.more = 1;
283262306a36Sopenharmony_ci	nctrl.iq_no = lio->linfo.txpciq[0].s.q_no;
283362306a36Sopenharmony_ci	nctrl.netpndev = (u64)netdev;
283462306a36Sopenharmony_ci	if (is_admin_assigned) {
283562306a36Sopenharmony_ci		nctrl.ncmd.s.param2 = true;
283662306a36Sopenharmony_ci		nctrl.cb_fn = liquidio_link_ctrl_cmd_completion;
283762306a36Sopenharmony_ci	}
283862306a36Sopenharmony_ci
283962306a36Sopenharmony_ci	nctrl.udd[0] = 0;
284062306a36Sopenharmony_ci	/* The MAC Address is presented in network byte order. */
284162306a36Sopenharmony_ci	ether_addr_copy((u8 *)&nctrl.udd[0] + 2, mac);
284262306a36Sopenharmony_ci
284362306a36Sopenharmony_ci	oct->sriov_info.vf_macaddr[vfidx] = nctrl.udd[0];
284462306a36Sopenharmony_ci
284562306a36Sopenharmony_ci	ret = octnet_send_nic_ctrl_pkt(oct, &nctrl);
284662306a36Sopenharmony_ci	if (ret > 0)
284762306a36Sopenharmony_ci		ret = -EIO;
284862306a36Sopenharmony_ci
284962306a36Sopenharmony_ci	return ret;
285062306a36Sopenharmony_ci}
285162306a36Sopenharmony_ci
285262306a36Sopenharmony_cistatic int liquidio_set_vf_mac(struct net_device *netdev, int vfidx, u8 *mac)
285362306a36Sopenharmony_ci{
285462306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
285562306a36Sopenharmony_ci	struct octeon_device *oct = lio->oct_dev;
285662306a36Sopenharmony_ci	int retval;
285762306a36Sopenharmony_ci
285862306a36Sopenharmony_ci	if (vfidx < 0 || vfidx >= oct->sriov_info.num_vfs_alloced)
285962306a36Sopenharmony_ci		return -EINVAL;
286062306a36Sopenharmony_ci
286162306a36Sopenharmony_ci	retval = __liquidio_set_vf_mac(netdev, vfidx, mac, true);
286262306a36Sopenharmony_ci	if (!retval)
286362306a36Sopenharmony_ci		cn23xx_tell_vf_its_macaddr_changed(oct, vfidx, mac);
286462306a36Sopenharmony_ci
286562306a36Sopenharmony_ci	return retval;
286662306a36Sopenharmony_ci}
286762306a36Sopenharmony_ci
286862306a36Sopenharmony_cistatic int liquidio_set_vf_spoofchk(struct net_device *netdev, int vfidx,
286962306a36Sopenharmony_ci				    bool enable)
287062306a36Sopenharmony_ci{
287162306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
287262306a36Sopenharmony_ci	struct octeon_device *oct = lio->oct_dev;
287362306a36Sopenharmony_ci	struct octnic_ctrl_pkt nctrl;
287462306a36Sopenharmony_ci	int retval;
287562306a36Sopenharmony_ci
287662306a36Sopenharmony_ci	if (!(oct->fw_info.app_cap_flags & LIQUIDIO_SPOOFCHK_CAP)) {
287762306a36Sopenharmony_ci		netif_info(lio, drv, lio->netdev,
287862306a36Sopenharmony_ci			   "firmware does not support spoofchk\n");
287962306a36Sopenharmony_ci		return -EOPNOTSUPP;
288062306a36Sopenharmony_ci	}
288162306a36Sopenharmony_ci
288262306a36Sopenharmony_ci	if (vfidx < 0 || vfidx >= oct->sriov_info.num_vfs_alloced) {
288362306a36Sopenharmony_ci		netif_info(lio, drv, lio->netdev, "Invalid vfidx %d\n", vfidx);
288462306a36Sopenharmony_ci		return -EINVAL;
288562306a36Sopenharmony_ci	}
288662306a36Sopenharmony_ci
288762306a36Sopenharmony_ci	if (enable) {
288862306a36Sopenharmony_ci		if (oct->sriov_info.vf_spoofchk[vfidx])
288962306a36Sopenharmony_ci			return 0;
289062306a36Sopenharmony_ci	} else {
289162306a36Sopenharmony_ci		/* Clear */
289262306a36Sopenharmony_ci		if (!oct->sriov_info.vf_spoofchk[vfidx])
289362306a36Sopenharmony_ci			return 0;
289462306a36Sopenharmony_ci	}
289562306a36Sopenharmony_ci
289662306a36Sopenharmony_ci	memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt));
289762306a36Sopenharmony_ci	nctrl.ncmd.s.cmdgroup = OCTNET_CMD_GROUP1;
289862306a36Sopenharmony_ci	nctrl.ncmd.s.cmd = OCTNET_CMD_SET_VF_SPOOFCHK;
289962306a36Sopenharmony_ci	nctrl.ncmd.s.param1 =
290062306a36Sopenharmony_ci		vfidx + 1; /* vfidx is 0 based,
290162306a36Sopenharmony_ci			    * but vf_num (param1) is 1 based
290262306a36Sopenharmony_ci			    */
290362306a36Sopenharmony_ci	nctrl.ncmd.s.param2 = enable;
290462306a36Sopenharmony_ci	nctrl.ncmd.s.more = 0;
290562306a36Sopenharmony_ci	nctrl.iq_no = lio->linfo.txpciq[0].s.q_no;
290662306a36Sopenharmony_ci	nctrl.cb_fn = NULL;
290762306a36Sopenharmony_ci
290862306a36Sopenharmony_ci	retval = octnet_send_nic_ctrl_pkt(oct, &nctrl);
290962306a36Sopenharmony_ci
291062306a36Sopenharmony_ci	if (retval) {
291162306a36Sopenharmony_ci		netif_info(lio, drv, lio->netdev,
291262306a36Sopenharmony_ci			   "Failed to set VF %d spoofchk %s\n", vfidx,
291362306a36Sopenharmony_ci			enable ? "on" : "off");
291462306a36Sopenharmony_ci		return -1;
291562306a36Sopenharmony_ci	}
291662306a36Sopenharmony_ci
291762306a36Sopenharmony_ci	oct->sriov_info.vf_spoofchk[vfidx] = enable;
291862306a36Sopenharmony_ci	netif_info(lio, drv, lio->netdev, "VF %u spoofchk is %s\n", vfidx,
291962306a36Sopenharmony_ci		   enable ? "on" : "off");
292062306a36Sopenharmony_ci
292162306a36Sopenharmony_ci	return 0;
292262306a36Sopenharmony_ci}
292362306a36Sopenharmony_ci
292462306a36Sopenharmony_cistatic int liquidio_set_vf_vlan(struct net_device *netdev, int vfidx,
292562306a36Sopenharmony_ci				u16 vlan, u8 qos, __be16 vlan_proto)
292662306a36Sopenharmony_ci{
292762306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
292862306a36Sopenharmony_ci	struct octeon_device *oct = lio->oct_dev;
292962306a36Sopenharmony_ci	struct octnic_ctrl_pkt nctrl;
293062306a36Sopenharmony_ci	u16 vlantci;
293162306a36Sopenharmony_ci	int ret = 0;
293262306a36Sopenharmony_ci
293362306a36Sopenharmony_ci	if (vfidx < 0 || vfidx >= oct->sriov_info.num_vfs_alloced)
293462306a36Sopenharmony_ci		return -EINVAL;
293562306a36Sopenharmony_ci
293662306a36Sopenharmony_ci	if (vlan_proto != htons(ETH_P_8021Q))
293762306a36Sopenharmony_ci		return -EPROTONOSUPPORT;
293862306a36Sopenharmony_ci
293962306a36Sopenharmony_ci	if (vlan >= VLAN_N_VID || qos > 7)
294062306a36Sopenharmony_ci		return -EINVAL;
294162306a36Sopenharmony_ci
294262306a36Sopenharmony_ci	if (vlan)
294362306a36Sopenharmony_ci		vlantci = vlan | (u16)qos << VLAN_PRIO_SHIFT;
294462306a36Sopenharmony_ci	else
294562306a36Sopenharmony_ci		vlantci = 0;
294662306a36Sopenharmony_ci
294762306a36Sopenharmony_ci	if (oct->sriov_info.vf_vlantci[vfidx] == vlantci)
294862306a36Sopenharmony_ci		return 0;
294962306a36Sopenharmony_ci
295062306a36Sopenharmony_ci	memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt));
295162306a36Sopenharmony_ci
295262306a36Sopenharmony_ci	if (vlan)
295362306a36Sopenharmony_ci		nctrl.ncmd.s.cmd = OCTNET_CMD_ADD_VLAN_FILTER;
295462306a36Sopenharmony_ci	else
295562306a36Sopenharmony_ci		nctrl.ncmd.s.cmd = OCTNET_CMD_DEL_VLAN_FILTER;
295662306a36Sopenharmony_ci
295762306a36Sopenharmony_ci	nctrl.ncmd.s.param1 = vlantci;
295862306a36Sopenharmony_ci	nctrl.ncmd.s.param2 =
295962306a36Sopenharmony_ci	    vfidx + 1; /* vfidx is 0 based, but vf_num (param2) is 1 based */
296062306a36Sopenharmony_ci	nctrl.ncmd.s.more = 0;
296162306a36Sopenharmony_ci	nctrl.iq_no = lio->linfo.txpciq[0].s.q_no;
296262306a36Sopenharmony_ci	nctrl.cb_fn = NULL;
296362306a36Sopenharmony_ci
296462306a36Sopenharmony_ci	ret = octnet_send_nic_ctrl_pkt(oct, &nctrl);
296562306a36Sopenharmony_ci	if (ret) {
296662306a36Sopenharmony_ci		if (ret > 0)
296762306a36Sopenharmony_ci			ret = -EIO;
296862306a36Sopenharmony_ci		return ret;
296962306a36Sopenharmony_ci	}
297062306a36Sopenharmony_ci
297162306a36Sopenharmony_ci	oct->sriov_info.vf_vlantci[vfidx] = vlantci;
297262306a36Sopenharmony_ci
297362306a36Sopenharmony_ci	return ret;
297462306a36Sopenharmony_ci}
297562306a36Sopenharmony_ci
297662306a36Sopenharmony_cistatic int liquidio_get_vf_config(struct net_device *netdev, int vfidx,
297762306a36Sopenharmony_ci				  struct ifla_vf_info *ivi)
297862306a36Sopenharmony_ci{
297962306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
298062306a36Sopenharmony_ci	struct octeon_device *oct = lio->oct_dev;
298162306a36Sopenharmony_ci	u8 *macaddr;
298262306a36Sopenharmony_ci
298362306a36Sopenharmony_ci	if (vfidx < 0 || vfidx >= oct->sriov_info.num_vfs_alloced)
298462306a36Sopenharmony_ci		return -EINVAL;
298562306a36Sopenharmony_ci
298662306a36Sopenharmony_ci	memset(ivi, 0, sizeof(struct ifla_vf_info));
298762306a36Sopenharmony_ci
298862306a36Sopenharmony_ci	ivi->vf = vfidx;
298962306a36Sopenharmony_ci	macaddr = 2 + (u8 *)&oct->sriov_info.vf_macaddr[vfidx];
299062306a36Sopenharmony_ci	ether_addr_copy(&ivi->mac[0], macaddr);
299162306a36Sopenharmony_ci	ivi->vlan = oct->sriov_info.vf_vlantci[vfidx] & VLAN_VID_MASK;
299262306a36Sopenharmony_ci	ivi->qos = oct->sriov_info.vf_vlantci[vfidx] >> VLAN_PRIO_SHIFT;
299362306a36Sopenharmony_ci	if (oct->sriov_info.trusted_vf.active &&
299462306a36Sopenharmony_ci	    oct->sriov_info.trusted_vf.id == vfidx)
299562306a36Sopenharmony_ci		ivi->trusted = true;
299662306a36Sopenharmony_ci	else
299762306a36Sopenharmony_ci		ivi->trusted = false;
299862306a36Sopenharmony_ci	ivi->linkstate = oct->sriov_info.vf_linkstate[vfidx];
299962306a36Sopenharmony_ci	ivi->spoofchk = oct->sriov_info.vf_spoofchk[vfidx];
300062306a36Sopenharmony_ci	ivi->max_tx_rate = lio->linfo.link.s.speed;
300162306a36Sopenharmony_ci	ivi->min_tx_rate = 0;
300262306a36Sopenharmony_ci
300362306a36Sopenharmony_ci	return 0;
300462306a36Sopenharmony_ci}
300562306a36Sopenharmony_ci
300662306a36Sopenharmony_cistatic int liquidio_send_vf_trust_cmd(struct lio *lio, int vfidx, bool trusted)
300762306a36Sopenharmony_ci{
300862306a36Sopenharmony_ci	struct octeon_device *oct = lio->oct_dev;
300962306a36Sopenharmony_ci	struct octeon_soft_command *sc;
301062306a36Sopenharmony_ci	int retval;
301162306a36Sopenharmony_ci
301262306a36Sopenharmony_ci	sc = octeon_alloc_soft_command(oct, 0, 16, 0);
301362306a36Sopenharmony_ci	if (!sc)
301462306a36Sopenharmony_ci		return -ENOMEM;
301562306a36Sopenharmony_ci
301662306a36Sopenharmony_ci	sc->iq_no = lio->linfo.txpciq[0].s.q_no;
301762306a36Sopenharmony_ci
301862306a36Sopenharmony_ci	/* vfidx is 0 based, but vf_num (param1) is 1 based */
301962306a36Sopenharmony_ci	octeon_prepare_soft_command(oct, sc, OPCODE_NIC,
302062306a36Sopenharmony_ci				    OPCODE_NIC_SET_TRUSTED_VF, 0, vfidx + 1,
302162306a36Sopenharmony_ci				    trusted);
302262306a36Sopenharmony_ci
302362306a36Sopenharmony_ci	init_completion(&sc->complete);
302462306a36Sopenharmony_ci	sc->sc_status = OCTEON_REQUEST_PENDING;
302562306a36Sopenharmony_ci
302662306a36Sopenharmony_ci	retval = octeon_send_soft_command(oct, sc);
302762306a36Sopenharmony_ci	if (retval == IQ_SEND_FAILED) {
302862306a36Sopenharmony_ci		octeon_free_soft_command(oct, sc);
302962306a36Sopenharmony_ci		retval = -1;
303062306a36Sopenharmony_ci	} else {
303162306a36Sopenharmony_ci		/* Wait for response or timeout */
303262306a36Sopenharmony_ci		retval = wait_for_sc_completion_timeout(oct, sc, 0);
303362306a36Sopenharmony_ci		if (retval)
303462306a36Sopenharmony_ci			return (retval);
303562306a36Sopenharmony_ci
303662306a36Sopenharmony_ci		WRITE_ONCE(sc->caller_is_done, true);
303762306a36Sopenharmony_ci	}
303862306a36Sopenharmony_ci
303962306a36Sopenharmony_ci	return retval;
304062306a36Sopenharmony_ci}
304162306a36Sopenharmony_ci
304262306a36Sopenharmony_cistatic int liquidio_set_vf_trust(struct net_device *netdev, int vfidx,
304362306a36Sopenharmony_ci				 bool setting)
304462306a36Sopenharmony_ci{
304562306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
304662306a36Sopenharmony_ci	struct octeon_device *oct = lio->oct_dev;
304762306a36Sopenharmony_ci
304862306a36Sopenharmony_ci	if (strcmp(oct->fw_info.liquidio_firmware_version, "1.7.1") < 0) {
304962306a36Sopenharmony_ci		/* trusted vf is not supported by firmware older than 1.7.1 */
305062306a36Sopenharmony_ci		return -EOPNOTSUPP;
305162306a36Sopenharmony_ci	}
305262306a36Sopenharmony_ci
305362306a36Sopenharmony_ci	if (vfidx < 0 || vfidx >= oct->sriov_info.num_vfs_alloced) {
305462306a36Sopenharmony_ci		netif_info(lio, drv, lio->netdev, "Invalid vfidx %d\n", vfidx);
305562306a36Sopenharmony_ci		return -EINVAL;
305662306a36Sopenharmony_ci	}
305762306a36Sopenharmony_ci
305862306a36Sopenharmony_ci	if (setting) {
305962306a36Sopenharmony_ci		/* Set */
306062306a36Sopenharmony_ci
306162306a36Sopenharmony_ci		if (oct->sriov_info.trusted_vf.active &&
306262306a36Sopenharmony_ci		    oct->sriov_info.trusted_vf.id == vfidx)
306362306a36Sopenharmony_ci			return 0;
306462306a36Sopenharmony_ci
306562306a36Sopenharmony_ci		if (oct->sriov_info.trusted_vf.active) {
306662306a36Sopenharmony_ci			netif_info(lio, drv, lio->netdev, "More than one trusted VF is not allowed\n");
306762306a36Sopenharmony_ci			return -EPERM;
306862306a36Sopenharmony_ci		}
306962306a36Sopenharmony_ci	} else {
307062306a36Sopenharmony_ci		/* Clear */
307162306a36Sopenharmony_ci
307262306a36Sopenharmony_ci		if (!oct->sriov_info.trusted_vf.active)
307362306a36Sopenharmony_ci			return 0;
307462306a36Sopenharmony_ci	}
307562306a36Sopenharmony_ci
307662306a36Sopenharmony_ci	if (!liquidio_send_vf_trust_cmd(lio, vfidx, setting)) {
307762306a36Sopenharmony_ci		if (setting) {
307862306a36Sopenharmony_ci			oct->sriov_info.trusted_vf.id = vfidx;
307962306a36Sopenharmony_ci			oct->sriov_info.trusted_vf.active = true;
308062306a36Sopenharmony_ci		} else {
308162306a36Sopenharmony_ci			oct->sriov_info.trusted_vf.active = false;
308262306a36Sopenharmony_ci		}
308362306a36Sopenharmony_ci
308462306a36Sopenharmony_ci		netif_info(lio, drv, lio->netdev, "VF %u is %strusted\n", vfidx,
308562306a36Sopenharmony_ci			   setting ? "" : "not ");
308662306a36Sopenharmony_ci	} else {
308762306a36Sopenharmony_ci		netif_info(lio, drv, lio->netdev, "Failed to set VF trusted\n");
308862306a36Sopenharmony_ci		return -1;
308962306a36Sopenharmony_ci	}
309062306a36Sopenharmony_ci
309162306a36Sopenharmony_ci	return 0;
309262306a36Sopenharmony_ci}
309362306a36Sopenharmony_ci
309462306a36Sopenharmony_cistatic int liquidio_set_vf_link_state(struct net_device *netdev, int vfidx,
309562306a36Sopenharmony_ci				      int linkstate)
309662306a36Sopenharmony_ci{
309762306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
309862306a36Sopenharmony_ci	struct octeon_device *oct = lio->oct_dev;
309962306a36Sopenharmony_ci	struct octnic_ctrl_pkt nctrl;
310062306a36Sopenharmony_ci	int ret = 0;
310162306a36Sopenharmony_ci
310262306a36Sopenharmony_ci	if (vfidx < 0 || vfidx >= oct->sriov_info.num_vfs_alloced)
310362306a36Sopenharmony_ci		return -EINVAL;
310462306a36Sopenharmony_ci
310562306a36Sopenharmony_ci	if (oct->sriov_info.vf_linkstate[vfidx] == linkstate)
310662306a36Sopenharmony_ci		return 0;
310762306a36Sopenharmony_ci
310862306a36Sopenharmony_ci	memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt));
310962306a36Sopenharmony_ci	nctrl.ncmd.s.cmd = OCTNET_CMD_SET_VF_LINKSTATE;
311062306a36Sopenharmony_ci	nctrl.ncmd.s.param1 =
311162306a36Sopenharmony_ci	    vfidx + 1; /* vfidx is 0 based, but vf_num (param1) is 1 based */
311262306a36Sopenharmony_ci	nctrl.ncmd.s.param2 = linkstate;
311362306a36Sopenharmony_ci	nctrl.ncmd.s.more = 0;
311462306a36Sopenharmony_ci	nctrl.iq_no = lio->linfo.txpciq[0].s.q_no;
311562306a36Sopenharmony_ci	nctrl.cb_fn = NULL;
311662306a36Sopenharmony_ci
311762306a36Sopenharmony_ci	ret = octnet_send_nic_ctrl_pkt(oct, &nctrl);
311862306a36Sopenharmony_ci
311962306a36Sopenharmony_ci	if (!ret)
312062306a36Sopenharmony_ci		oct->sriov_info.vf_linkstate[vfidx] = linkstate;
312162306a36Sopenharmony_ci	else if (ret > 0)
312262306a36Sopenharmony_ci		ret = -EIO;
312362306a36Sopenharmony_ci
312462306a36Sopenharmony_ci	return ret;
312562306a36Sopenharmony_ci}
312662306a36Sopenharmony_ci
312762306a36Sopenharmony_cistatic int
312862306a36Sopenharmony_ciliquidio_eswitch_mode_get(struct devlink *devlink, u16 *mode)
312962306a36Sopenharmony_ci{
313062306a36Sopenharmony_ci	struct lio_devlink_priv *priv;
313162306a36Sopenharmony_ci	struct octeon_device *oct;
313262306a36Sopenharmony_ci
313362306a36Sopenharmony_ci	priv = devlink_priv(devlink);
313462306a36Sopenharmony_ci	oct = priv->oct;
313562306a36Sopenharmony_ci
313662306a36Sopenharmony_ci	*mode = oct->eswitch_mode;
313762306a36Sopenharmony_ci
313862306a36Sopenharmony_ci	return 0;
313962306a36Sopenharmony_ci}
314062306a36Sopenharmony_ci
314162306a36Sopenharmony_cistatic int
314262306a36Sopenharmony_ciliquidio_eswitch_mode_set(struct devlink *devlink, u16 mode,
314362306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
314462306a36Sopenharmony_ci{
314562306a36Sopenharmony_ci	struct lio_devlink_priv *priv;
314662306a36Sopenharmony_ci	struct octeon_device *oct;
314762306a36Sopenharmony_ci	int ret = 0;
314862306a36Sopenharmony_ci
314962306a36Sopenharmony_ci	priv = devlink_priv(devlink);
315062306a36Sopenharmony_ci	oct = priv->oct;
315162306a36Sopenharmony_ci
315262306a36Sopenharmony_ci	if (!(oct->fw_info.app_cap_flags & LIQUIDIO_SWITCHDEV_CAP))
315362306a36Sopenharmony_ci		return -EINVAL;
315462306a36Sopenharmony_ci
315562306a36Sopenharmony_ci	if (oct->eswitch_mode == mode)
315662306a36Sopenharmony_ci		return 0;
315762306a36Sopenharmony_ci
315862306a36Sopenharmony_ci	switch (mode) {
315962306a36Sopenharmony_ci	case DEVLINK_ESWITCH_MODE_SWITCHDEV:
316062306a36Sopenharmony_ci		oct->eswitch_mode = mode;
316162306a36Sopenharmony_ci		ret = lio_vf_rep_create(oct);
316262306a36Sopenharmony_ci		break;
316362306a36Sopenharmony_ci
316462306a36Sopenharmony_ci	case DEVLINK_ESWITCH_MODE_LEGACY:
316562306a36Sopenharmony_ci		lio_vf_rep_destroy(oct);
316662306a36Sopenharmony_ci		oct->eswitch_mode = mode;
316762306a36Sopenharmony_ci		break;
316862306a36Sopenharmony_ci
316962306a36Sopenharmony_ci	default:
317062306a36Sopenharmony_ci		ret = -EINVAL;
317162306a36Sopenharmony_ci	}
317262306a36Sopenharmony_ci
317362306a36Sopenharmony_ci	return ret;
317462306a36Sopenharmony_ci}
317562306a36Sopenharmony_ci
317662306a36Sopenharmony_cistatic const struct devlink_ops liquidio_devlink_ops = {
317762306a36Sopenharmony_ci	.eswitch_mode_get = liquidio_eswitch_mode_get,
317862306a36Sopenharmony_ci	.eswitch_mode_set = liquidio_eswitch_mode_set,
317962306a36Sopenharmony_ci};
318062306a36Sopenharmony_ci
318162306a36Sopenharmony_cistatic int
318262306a36Sopenharmony_ciliquidio_get_port_parent_id(struct net_device *dev,
318362306a36Sopenharmony_ci			    struct netdev_phys_item_id *ppid)
318462306a36Sopenharmony_ci{
318562306a36Sopenharmony_ci	struct lio *lio = GET_LIO(dev);
318662306a36Sopenharmony_ci	struct octeon_device *oct = lio->oct_dev;
318762306a36Sopenharmony_ci
318862306a36Sopenharmony_ci	if (oct->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV)
318962306a36Sopenharmony_ci		return -EOPNOTSUPP;
319062306a36Sopenharmony_ci
319162306a36Sopenharmony_ci	ppid->id_len = ETH_ALEN;
319262306a36Sopenharmony_ci	ether_addr_copy(ppid->id, (void *)&lio->linfo.hw_addr + 2);
319362306a36Sopenharmony_ci
319462306a36Sopenharmony_ci	return 0;
319562306a36Sopenharmony_ci}
319662306a36Sopenharmony_ci
319762306a36Sopenharmony_cistatic int liquidio_get_vf_stats(struct net_device *netdev, int vfidx,
319862306a36Sopenharmony_ci				 struct ifla_vf_stats *vf_stats)
319962306a36Sopenharmony_ci{
320062306a36Sopenharmony_ci	struct lio *lio = GET_LIO(netdev);
320162306a36Sopenharmony_ci	struct octeon_device *oct = lio->oct_dev;
320262306a36Sopenharmony_ci	struct oct_vf_stats stats;
320362306a36Sopenharmony_ci	int ret;
320462306a36Sopenharmony_ci
320562306a36Sopenharmony_ci	if (vfidx < 0 || vfidx >= oct->sriov_info.num_vfs_alloced)
320662306a36Sopenharmony_ci		return -EINVAL;
320762306a36Sopenharmony_ci
320862306a36Sopenharmony_ci	memset(&stats, 0, sizeof(struct oct_vf_stats));
320962306a36Sopenharmony_ci	ret = cn23xx_get_vf_stats(oct, vfidx, &stats);
321062306a36Sopenharmony_ci	if (!ret) {
321162306a36Sopenharmony_ci		vf_stats->rx_packets = stats.rx_packets;
321262306a36Sopenharmony_ci		vf_stats->tx_packets = stats.tx_packets;
321362306a36Sopenharmony_ci		vf_stats->rx_bytes = stats.rx_bytes;
321462306a36Sopenharmony_ci		vf_stats->tx_bytes = stats.tx_bytes;
321562306a36Sopenharmony_ci		vf_stats->broadcast = stats.broadcast;
321662306a36Sopenharmony_ci		vf_stats->multicast = stats.multicast;
321762306a36Sopenharmony_ci	}
321862306a36Sopenharmony_ci
321962306a36Sopenharmony_ci	return ret;
322062306a36Sopenharmony_ci}
322162306a36Sopenharmony_ci
322262306a36Sopenharmony_cistatic const struct net_device_ops lionetdevops = {
322362306a36Sopenharmony_ci	.ndo_open		= liquidio_open,
322462306a36Sopenharmony_ci	.ndo_stop		= liquidio_stop,
322562306a36Sopenharmony_ci	.ndo_start_xmit		= liquidio_xmit,
322662306a36Sopenharmony_ci	.ndo_get_stats64	= liquidio_get_stats64,
322762306a36Sopenharmony_ci	.ndo_set_mac_address	= liquidio_set_mac,
322862306a36Sopenharmony_ci	.ndo_set_rx_mode	= liquidio_set_mcast_list,
322962306a36Sopenharmony_ci	.ndo_tx_timeout		= liquidio_tx_timeout,
323062306a36Sopenharmony_ci
323162306a36Sopenharmony_ci	.ndo_vlan_rx_add_vid    = liquidio_vlan_rx_add_vid,
323262306a36Sopenharmony_ci	.ndo_vlan_rx_kill_vid   = liquidio_vlan_rx_kill_vid,
323362306a36Sopenharmony_ci	.ndo_change_mtu		= liquidio_change_mtu,
323462306a36Sopenharmony_ci	.ndo_eth_ioctl		= liquidio_ioctl,
323562306a36Sopenharmony_ci	.ndo_fix_features	= liquidio_fix_features,
323662306a36Sopenharmony_ci	.ndo_set_features	= liquidio_set_features,
323762306a36Sopenharmony_ci	.ndo_set_vf_mac		= liquidio_set_vf_mac,
323862306a36Sopenharmony_ci	.ndo_set_vf_vlan	= liquidio_set_vf_vlan,
323962306a36Sopenharmony_ci	.ndo_get_vf_config	= liquidio_get_vf_config,
324062306a36Sopenharmony_ci	.ndo_set_vf_spoofchk	= liquidio_set_vf_spoofchk,
324162306a36Sopenharmony_ci	.ndo_set_vf_trust	= liquidio_set_vf_trust,
324262306a36Sopenharmony_ci	.ndo_set_vf_link_state  = liquidio_set_vf_link_state,
324362306a36Sopenharmony_ci	.ndo_get_vf_stats	= liquidio_get_vf_stats,
324462306a36Sopenharmony_ci	.ndo_get_port_parent_id	= liquidio_get_port_parent_id,
324562306a36Sopenharmony_ci};
324662306a36Sopenharmony_ci
324762306a36Sopenharmony_ci/**
324862306a36Sopenharmony_ci * liquidio_init - Entry point for the liquidio module
324962306a36Sopenharmony_ci */
325062306a36Sopenharmony_cistatic int __init liquidio_init(void)
325162306a36Sopenharmony_ci{
325262306a36Sopenharmony_ci	int i;
325362306a36Sopenharmony_ci	struct handshake *hs;
325462306a36Sopenharmony_ci
325562306a36Sopenharmony_ci	init_completion(&first_stage);
325662306a36Sopenharmony_ci
325762306a36Sopenharmony_ci	octeon_init_device_list(OCTEON_CONFIG_TYPE_DEFAULT);
325862306a36Sopenharmony_ci
325962306a36Sopenharmony_ci	if (liquidio_init_pci())
326062306a36Sopenharmony_ci		return -EINVAL;
326162306a36Sopenharmony_ci
326262306a36Sopenharmony_ci	wait_for_completion_timeout(&first_stage, msecs_to_jiffies(1000));
326362306a36Sopenharmony_ci
326462306a36Sopenharmony_ci	for (i = 0; i < MAX_OCTEON_DEVICES; i++) {
326562306a36Sopenharmony_ci		hs = &handshake[i];
326662306a36Sopenharmony_ci		if (hs->pci_dev) {
326762306a36Sopenharmony_ci			wait_for_completion(&hs->init);
326862306a36Sopenharmony_ci			if (!hs->init_ok) {
326962306a36Sopenharmony_ci				/* init handshake failed */
327062306a36Sopenharmony_ci				dev_err(&hs->pci_dev->dev,
327162306a36Sopenharmony_ci					"Failed to init device\n");
327262306a36Sopenharmony_ci				liquidio_deinit_pci();
327362306a36Sopenharmony_ci				return -EIO;
327462306a36Sopenharmony_ci			}
327562306a36Sopenharmony_ci		}
327662306a36Sopenharmony_ci	}
327762306a36Sopenharmony_ci
327862306a36Sopenharmony_ci	for (i = 0; i < MAX_OCTEON_DEVICES; i++) {
327962306a36Sopenharmony_ci		hs = &handshake[i];
328062306a36Sopenharmony_ci		if (hs->pci_dev) {
328162306a36Sopenharmony_ci			wait_for_completion_timeout(&hs->started,
328262306a36Sopenharmony_ci						    msecs_to_jiffies(30000));
328362306a36Sopenharmony_ci			if (!hs->started_ok) {
328462306a36Sopenharmony_ci				/* starter handshake failed */
328562306a36Sopenharmony_ci				dev_err(&hs->pci_dev->dev,
328662306a36Sopenharmony_ci					"Firmware failed to start\n");
328762306a36Sopenharmony_ci				liquidio_deinit_pci();
328862306a36Sopenharmony_ci				return -EIO;
328962306a36Sopenharmony_ci			}
329062306a36Sopenharmony_ci		}
329162306a36Sopenharmony_ci	}
329262306a36Sopenharmony_ci
329362306a36Sopenharmony_ci	return 0;
329462306a36Sopenharmony_ci}
329562306a36Sopenharmony_ci
329662306a36Sopenharmony_cistatic int lio_nic_info(struct octeon_recv_info *recv_info, void *buf)
329762306a36Sopenharmony_ci{
329862306a36Sopenharmony_ci	struct octeon_device *oct = (struct octeon_device *)buf;
329962306a36Sopenharmony_ci	struct octeon_recv_pkt *recv_pkt = recv_info->recv_pkt;
330062306a36Sopenharmony_ci	int gmxport = 0;
330162306a36Sopenharmony_ci	union oct_link_status *ls;
330262306a36Sopenharmony_ci	int i;
330362306a36Sopenharmony_ci
330462306a36Sopenharmony_ci	if (recv_pkt->buffer_size[0] != (sizeof(*ls) + OCT_DROQ_INFO_SIZE)) {
330562306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "Malformed NIC_INFO, len=%d, ifidx=%d\n",
330662306a36Sopenharmony_ci			recv_pkt->buffer_size[0],
330762306a36Sopenharmony_ci			recv_pkt->rh.r_nic_info.gmxport);
330862306a36Sopenharmony_ci		goto nic_info_err;
330962306a36Sopenharmony_ci	}
331062306a36Sopenharmony_ci
331162306a36Sopenharmony_ci	gmxport = recv_pkt->rh.r_nic_info.gmxport;
331262306a36Sopenharmony_ci	ls = (union oct_link_status *)(get_rbd(recv_pkt->buffer_ptr[0]) +
331362306a36Sopenharmony_ci		OCT_DROQ_INFO_SIZE);
331462306a36Sopenharmony_ci
331562306a36Sopenharmony_ci	octeon_swap_8B_data((u64 *)ls, (sizeof(union oct_link_status)) >> 3);
331662306a36Sopenharmony_ci	for (i = 0; i < oct->ifcount; i++) {
331762306a36Sopenharmony_ci		if (oct->props[i].gmxport == gmxport) {
331862306a36Sopenharmony_ci			update_link_status(oct->props[i].netdev, ls);
331962306a36Sopenharmony_ci			break;
332062306a36Sopenharmony_ci		}
332162306a36Sopenharmony_ci	}
332262306a36Sopenharmony_ci
332362306a36Sopenharmony_cinic_info_err:
332462306a36Sopenharmony_ci	for (i = 0; i < recv_pkt->buffer_count; i++)
332562306a36Sopenharmony_ci		recv_buffer_free(recv_pkt->buffer_ptr[i]);
332662306a36Sopenharmony_ci	octeon_free_recv_info(recv_info);
332762306a36Sopenharmony_ci	return 0;
332862306a36Sopenharmony_ci}
332962306a36Sopenharmony_ci
333062306a36Sopenharmony_ci/**
333162306a36Sopenharmony_ci * setup_nic_devices - Setup network interfaces
333262306a36Sopenharmony_ci * @octeon_dev:  octeon device
333362306a36Sopenharmony_ci *
333462306a36Sopenharmony_ci * Called during init time for each device. It assumes the NIC
333562306a36Sopenharmony_ci * is already up and running.  The link information for each
333662306a36Sopenharmony_ci * interface is passed in link_info.
333762306a36Sopenharmony_ci */
333862306a36Sopenharmony_cistatic int setup_nic_devices(struct octeon_device *octeon_dev)
333962306a36Sopenharmony_ci{
334062306a36Sopenharmony_ci	struct lio *lio = NULL;
334162306a36Sopenharmony_ci	struct net_device *netdev;
334262306a36Sopenharmony_ci	u8 mac[6], i, j, *fw_ver, *micro_ver;
334362306a36Sopenharmony_ci	unsigned long micro;
334462306a36Sopenharmony_ci	u32 cur_ver;
334562306a36Sopenharmony_ci	struct octeon_soft_command *sc;
334662306a36Sopenharmony_ci	struct liquidio_if_cfg_resp *resp;
334762306a36Sopenharmony_ci	struct octdev_props *props;
334862306a36Sopenharmony_ci	int retval, num_iqueues, num_oqueues;
334962306a36Sopenharmony_ci	int max_num_queues = 0;
335062306a36Sopenharmony_ci	union oct_nic_if_cfg if_cfg;
335162306a36Sopenharmony_ci	unsigned int base_queue;
335262306a36Sopenharmony_ci	unsigned int gmx_port_id;
335362306a36Sopenharmony_ci	u32 resp_size, data_size;
335462306a36Sopenharmony_ci	u32 ifidx_or_pfnum;
335562306a36Sopenharmony_ci	struct lio_version *vdata;
335662306a36Sopenharmony_ci	struct devlink *devlink;
335762306a36Sopenharmony_ci	struct lio_devlink_priv *lio_devlink;
335862306a36Sopenharmony_ci
335962306a36Sopenharmony_ci	/* This is to handle link status changes */
336062306a36Sopenharmony_ci	octeon_register_dispatch_fn(octeon_dev, OPCODE_NIC,
336162306a36Sopenharmony_ci				    OPCODE_NIC_INFO,
336262306a36Sopenharmony_ci				    lio_nic_info, octeon_dev);
336362306a36Sopenharmony_ci
336462306a36Sopenharmony_ci	/* REQTYPE_RESP_NET and REQTYPE_SOFT_COMMAND do not have free functions.
336562306a36Sopenharmony_ci	 * They are handled directly.
336662306a36Sopenharmony_ci	 */
336762306a36Sopenharmony_ci	octeon_register_reqtype_free_fn(octeon_dev, REQTYPE_NORESP_NET,
336862306a36Sopenharmony_ci					free_netbuf);
336962306a36Sopenharmony_ci
337062306a36Sopenharmony_ci	octeon_register_reqtype_free_fn(octeon_dev, REQTYPE_NORESP_NET_SG,
337162306a36Sopenharmony_ci					free_netsgbuf);
337262306a36Sopenharmony_ci
337362306a36Sopenharmony_ci	octeon_register_reqtype_free_fn(octeon_dev, REQTYPE_RESP_NET_SG,
337462306a36Sopenharmony_ci					free_netsgbuf_with_resp);
337562306a36Sopenharmony_ci
337662306a36Sopenharmony_ci	for (i = 0; i < octeon_dev->ifcount; i++) {
337762306a36Sopenharmony_ci		resp_size = sizeof(struct liquidio_if_cfg_resp);
337862306a36Sopenharmony_ci		data_size = sizeof(struct lio_version);
337962306a36Sopenharmony_ci		sc = (struct octeon_soft_command *)
338062306a36Sopenharmony_ci			octeon_alloc_soft_command(octeon_dev, data_size,
338162306a36Sopenharmony_ci						  resp_size, 0);
338262306a36Sopenharmony_ci		resp = (struct liquidio_if_cfg_resp *)sc->virtrptr;
338362306a36Sopenharmony_ci		vdata = (struct lio_version *)sc->virtdptr;
338462306a36Sopenharmony_ci
338562306a36Sopenharmony_ci		*((u64 *)vdata) = 0;
338662306a36Sopenharmony_ci		vdata->major = cpu_to_be16(LIQUIDIO_BASE_MAJOR_VERSION);
338762306a36Sopenharmony_ci		vdata->minor = cpu_to_be16(LIQUIDIO_BASE_MINOR_VERSION);
338862306a36Sopenharmony_ci		vdata->micro = cpu_to_be16(LIQUIDIO_BASE_MICRO_VERSION);
338962306a36Sopenharmony_ci
339062306a36Sopenharmony_ci		if (OCTEON_CN23XX_PF(octeon_dev)) {
339162306a36Sopenharmony_ci			num_iqueues = octeon_dev->sriov_info.num_pf_rings;
339262306a36Sopenharmony_ci			num_oqueues = octeon_dev->sriov_info.num_pf_rings;
339362306a36Sopenharmony_ci			base_queue = octeon_dev->sriov_info.pf_srn;
339462306a36Sopenharmony_ci
339562306a36Sopenharmony_ci			gmx_port_id = octeon_dev->pf_num;
339662306a36Sopenharmony_ci			ifidx_or_pfnum = octeon_dev->pf_num;
339762306a36Sopenharmony_ci		} else {
339862306a36Sopenharmony_ci			num_iqueues = CFG_GET_NUM_TXQS_NIC_IF(
339962306a36Sopenharmony_ci						octeon_get_conf(octeon_dev), i);
340062306a36Sopenharmony_ci			num_oqueues = CFG_GET_NUM_RXQS_NIC_IF(
340162306a36Sopenharmony_ci						octeon_get_conf(octeon_dev), i);
340262306a36Sopenharmony_ci			base_queue = CFG_GET_BASE_QUE_NIC_IF(
340362306a36Sopenharmony_ci						octeon_get_conf(octeon_dev), i);
340462306a36Sopenharmony_ci			gmx_port_id = CFG_GET_GMXID_NIC_IF(
340562306a36Sopenharmony_ci						octeon_get_conf(octeon_dev), i);
340662306a36Sopenharmony_ci			ifidx_or_pfnum = i;
340762306a36Sopenharmony_ci		}
340862306a36Sopenharmony_ci
340962306a36Sopenharmony_ci		dev_dbg(&octeon_dev->pci_dev->dev,
341062306a36Sopenharmony_ci			"requesting config for interface %d, iqs %d, oqs %d\n",
341162306a36Sopenharmony_ci			ifidx_or_pfnum, num_iqueues, num_oqueues);
341262306a36Sopenharmony_ci
341362306a36Sopenharmony_ci		if_cfg.u64 = 0;
341462306a36Sopenharmony_ci		if_cfg.s.num_iqueues = num_iqueues;
341562306a36Sopenharmony_ci		if_cfg.s.num_oqueues = num_oqueues;
341662306a36Sopenharmony_ci		if_cfg.s.base_queue = base_queue;
341762306a36Sopenharmony_ci		if_cfg.s.gmx_port_id = gmx_port_id;
341862306a36Sopenharmony_ci
341962306a36Sopenharmony_ci		sc->iq_no = 0;
342062306a36Sopenharmony_ci
342162306a36Sopenharmony_ci		octeon_prepare_soft_command(octeon_dev, sc, OPCODE_NIC,
342262306a36Sopenharmony_ci					    OPCODE_NIC_IF_CFG, 0,
342362306a36Sopenharmony_ci					    if_cfg.u64, 0);
342462306a36Sopenharmony_ci
342562306a36Sopenharmony_ci		init_completion(&sc->complete);
342662306a36Sopenharmony_ci		sc->sc_status = OCTEON_REQUEST_PENDING;
342762306a36Sopenharmony_ci
342862306a36Sopenharmony_ci		retval = octeon_send_soft_command(octeon_dev, sc);
342962306a36Sopenharmony_ci		if (retval == IQ_SEND_FAILED) {
343062306a36Sopenharmony_ci			dev_err(&octeon_dev->pci_dev->dev,
343162306a36Sopenharmony_ci				"iq/oq config failed status: %x\n",
343262306a36Sopenharmony_ci				retval);
343362306a36Sopenharmony_ci			/* Soft instr is freed by driver in case of failure. */
343462306a36Sopenharmony_ci			octeon_free_soft_command(octeon_dev, sc);
343562306a36Sopenharmony_ci			return(-EIO);
343662306a36Sopenharmony_ci		}
343762306a36Sopenharmony_ci
343862306a36Sopenharmony_ci		/* Sleep on a wait queue till the cond flag indicates that the
343962306a36Sopenharmony_ci		 * response arrived or timed-out.
344062306a36Sopenharmony_ci		 */
344162306a36Sopenharmony_ci		retval = wait_for_sc_completion_timeout(octeon_dev, sc, 0);
344262306a36Sopenharmony_ci		if (retval)
344362306a36Sopenharmony_ci			return retval;
344462306a36Sopenharmony_ci
344562306a36Sopenharmony_ci		retval = resp->status;
344662306a36Sopenharmony_ci		if (retval) {
344762306a36Sopenharmony_ci			dev_err(&octeon_dev->pci_dev->dev, "iq/oq config failed\n");
344862306a36Sopenharmony_ci			WRITE_ONCE(sc->caller_is_done, true);
344962306a36Sopenharmony_ci			goto setup_nic_dev_done;
345062306a36Sopenharmony_ci		}
345162306a36Sopenharmony_ci		snprintf(octeon_dev->fw_info.liquidio_firmware_version,
345262306a36Sopenharmony_ci			 32, "%s",
345362306a36Sopenharmony_ci			 resp->cfg_info.liquidio_firmware_version);
345462306a36Sopenharmony_ci
345562306a36Sopenharmony_ci		/* Verify f/w version (in case of 'auto' loading from flash) */
345662306a36Sopenharmony_ci		fw_ver = octeon_dev->fw_info.liquidio_firmware_version;
345762306a36Sopenharmony_ci		if (memcmp(LIQUIDIO_BASE_VERSION,
345862306a36Sopenharmony_ci			   fw_ver,
345962306a36Sopenharmony_ci			   strlen(LIQUIDIO_BASE_VERSION))) {
346062306a36Sopenharmony_ci			dev_err(&octeon_dev->pci_dev->dev,
346162306a36Sopenharmony_ci				"Unmatched firmware version. Expected %s.x, got %s.\n",
346262306a36Sopenharmony_ci				LIQUIDIO_BASE_VERSION, fw_ver);
346362306a36Sopenharmony_ci			WRITE_ONCE(sc->caller_is_done, true);
346462306a36Sopenharmony_ci			goto setup_nic_dev_done;
346562306a36Sopenharmony_ci		} else if (atomic_read(octeon_dev->adapter_fw_state) ==
346662306a36Sopenharmony_ci			   FW_IS_PRELOADED) {
346762306a36Sopenharmony_ci			dev_info(&octeon_dev->pci_dev->dev,
346862306a36Sopenharmony_ci				 "Using auto-loaded firmware version %s.\n",
346962306a36Sopenharmony_ci				 fw_ver);
347062306a36Sopenharmony_ci		}
347162306a36Sopenharmony_ci
347262306a36Sopenharmony_ci		/* extract micro version field; point past '<maj>.<min>.' */
347362306a36Sopenharmony_ci		micro_ver = fw_ver + strlen(LIQUIDIO_BASE_VERSION) + 1;
347462306a36Sopenharmony_ci		if (kstrtoul(micro_ver, 10, &micro) != 0)
347562306a36Sopenharmony_ci			micro = 0;
347662306a36Sopenharmony_ci		octeon_dev->fw_info.ver.maj = LIQUIDIO_BASE_MAJOR_VERSION;
347762306a36Sopenharmony_ci		octeon_dev->fw_info.ver.min = LIQUIDIO_BASE_MINOR_VERSION;
347862306a36Sopenharmony_ci		octeon_dev->fw_info.ver.rev = micro;
347962306a36Sopenharmony_ci
348062306a36Sopenharmony_ci		octeon_swap_8B_data((u64 *)(&resp->cfg_info),
348162306a36Sopenharmony_ci				    (sizeof(struct liquidio_if_cfg_info)) >> 3);
348262306a36Sopenharmony_ci
348362306a36Sopenharmony_ci		num_iqueues = hweight64(resp->cfg_info.iqmask);
348462306a36Sopenharmony_ci		num_oqueues = hweight64(resp->cfg_info.oqmask);
348562306a36Sopenharmony_ci
348662306a36Sopenharmony_ci		if (!(num_iqueues) || !(num_oqueues)) {
348762306a36Sopenharmony_ci			dev_err(&octeon_dev->pci_dev->dev,
348862306a36Sopenharmony_ci				"Got bad iqueues (%016llx) or oqueues (%016llx) from firmware.\n",
348962306a36Sopenharmony_ci				resp->cfg_info.iqmask,
349062306a36Sopenharmony_ci				resp->cfg_info.oqmask);
349162306a36Sopenharmony_ci			WRITE_ONCE(sc->caller_is_done, true);
349262306a36Sopenharmony_ci			goto setup_nic_dev_done;
349362306a36Sopenharmony_ci		}
349462306a36Sopenharmony_ci
349562306a36Sopenharmony_ci		if (OCTEON_CN6XXX(octeon_dev)) {
349662306a36Sopenharmony_ci			max_num_queues = CFG_GET_IQ_MAX_Q(CHIP_CONF(octeon_dev,
349762306a36Sopenharmony_ci								    cn6xxx));
349862306a36Sopenharmony_ci		} else if (OCTEON_CN23XX_PF(octeon_dev)) {
349962306a36Sopenharmony_ci			max_num_queues = CFG_GET_IQ_MAX_Q(CHIP_CONF(octeon_dev,
350062306a36Sopenharmony_ci								    cn23xx_pf));
350162306a36Sopenharmony_ci		}
350262306a36Sopenharmony_ci
350362306a36Sopenharmony_ci		dev_dbg(&octeon_dev->pci_dev->dev,
350462306a36Sopenharmony_ci			"interface %d, iqmask %016llx, oqmask %016llx, numiqueues %d, numoqueues %d max_num_queues: %d\n",
350562306a36Sopenharmony_ci			i, resp->cfg_info.iqmask, resp->cfg_info.oqmask,
350662306a36Sopenharmony_ci			num_iqueues, num_oqueues, max_num_queues);
350762306a36Sopenharmony_ci		netdev = alloc_etherdev_mq(LIO_SIZE, max_num_queues);
350862306a36Sopenharmony_ci
350962306a36Sopenharmony_ci		if (!netdev) {
351062306a36Sopenharmony_ci			dev_err(&octeon_dev->pci_dev->dev, "Device allocation failed\n");
351162306a36Sopenharmony_ci			WRITE_ONCE(sc->caller_is_done, true);
351262306a36Sopenharmony_ci			goto setup_nic_dev_done;
351362306a36Sopenharmony_ci		}
351462306a36Sopenharmony_ci
351562306a36Sopenharmony_ci		SET_NETDEV_DEV(netdev, &octeon_dev->pci_dev->dev);
351662306a36Sopenharmony_ci
351762306a36Sopenharmony_ci		/* Associate the routines that will handle different
351862306a36Sopenharmony_ci		 * netdev tasks.
351962306a36Sopenharmony_ci		 */
352062306a36Sopenharmony_ci		netdev->netdev_ops = &lionetdevops;
352162306a36Sopenharmony_ci
352262306a36Sopenharmony_ci		retval = netif_set_real_num_rx_queues(netdev, num_oqueues);
352362306a36Sopenharmony_ci		if (retval) {
352462306a36Sopenharmony_ci			dev_err(&octeon_dev->pci_dev->dev,
352562306a36Sopenharmony_ci				"setting real number rx failed\n");
352662306a36Sopenharmony_ci			WRITE_ONCE(sc->caller_is_done, true);
352762306a36Sopenharmony_ci			goto setup_nic_dev_free;
352862306a36Sopenharmony_ci		}
352962306a36Sopenharmony_ci
353062306a36Sopenharmony_ci		retval = netif_set_real_num_tx_queues(netdev, num_iqueues);
353162306a36Sopenharmony_ci		if (retval) {
353262306a36Sopenharmony_ci			dev_err(&octeon_dev->pci_dev->dev,
353362306a36Sopenharmony_ci				"setting real number tx failed\n");
353462306a36Sopenharmony_ci			WRITE_ONCE(sc->caller_is_done, true);
353562306a36Sopenharmony_ci			goto setup_nic_dev_free;
353662306a36Sopenharmony_ci		}
353762306a36Sopenharmony_ci
353862306a36Sopenharmony_ci		lio = GET_LIO(netdev);
353962306a36Sopenharmony_ci
354062306a36Sopenharmony_ci		memset(lio, 0, sizeof(struct lio));
354162306a36Sopenharmony_ci
354262306a36Sopenharmony_ci		lio->ifidx = ifidx_or_pfnum;
354362306a36Sopenharmony_ci
354462306a36Sopenharmony_ci		props = &octeon_dev->props[i];
354562306a36Sopenharmony_ci		props->gmxport = resp->cfg_info.linfo.gmxport;
354662306a36Sopenharmony_ci		props->netdev = netdev;
354762306a36Sopenharmony_ci
354862306a36Sopenharmony_ci		lio->linfo.num_rxpciq = num_oqueues;
354962306a36Sopenharmony_ci		lio->linfo.num_txpciq = num_iqueues;
355062306a36Sopenharmony_ci		for (j = 0; j < num_oqueues; j++) {
355162306a36Sopenharmony_ci			lio->linfo.rxpciq[j].u64 =
355262306a36Sopenharmony_ci				resp->cfg_info.linfo.rxpciq[j].u64;
355362306a36Sopenharmony_ci		}
355462306a36Sopenharmony_ci		for (j = 0; j < num_iqueues; j++) {
355562306a36Sopenharmony_ci			lio->linfo.txpciq[j].u64 =
355662306a36Sopenharmony_ci				resp->cfg_info.linfo.txpciq[j].u64;
355762306a36Sopenharmony_ci		}
355862306a36Sopenharmony_ci		lio->linfo.hw_addr = resp->cfg_info.linfo.hw_addr;
355962306a36Sopenharmony_ci		lio->linfo.gmxport = resp->cfg_info.linfo.gmxport;
356062306a36Sopenharmony_ci		lio->linfo.link.u64 = resp->cfg_info.linfo.link.u64;
356162306a36Sopenharmony_ci
356262306a36Sopenharmony_ci		WRITE_ONCE(sc->caller_is_done, true);
356362306a36Sopenharmony_ci
356462306a36Sopenharmony_ci		lio->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE);
356562306a36Sopenharmony_ci
356662306a36Sopenharmony_ci		if (OCTEON_CN23XX_PF(octeon_dev) ||
356762306a36Sopenharmony_ci		    OCTEON_CN6XXX(octeon_dev)) {
356862306a36Sopenharmony_ci			lio->dev_capability = NETIF_F_HIGHDMA
356962306a36Sopenharmony_ci					      | NETIF_F_IP_CSUM
357062306a36Sopenharmony_ci					      | NETIF_F_IPV6_CSUM
357162306a36Sopenharmony_ci					      | NETIF_F_SG | NETIF_F_RXCSUM
357262306a36Sopenharmony_ci					      | NETIF_F_GRO
357362306a36Sopenharmony_ci					      | NETIF_F_TSO | NETIF_F_TSO6
357462306a36Sopenharmony_ci					      | NETIF_F_LRO;
357562306a36Sopenharmony_ci		}
357662306a36Sopenharmony_ci		netif_set_tso_max_size(netdev, OCTNIC_GSO_MAX_SIZE);
357762306a36Sopenharmony_ci
357862306a36Sopenharmony_ci		/*  Copy of transmit encapsulation capabilities:
357962306a36Sopenharmony_ci		 *  TSO, TSO6, Checksums for this device
358062306a36Sopenharmony_ci		 */
358162306a36Sopenharmony_ci		lio->enc_dev_capability = NETIF_F_IP_CSUM
358262306a36Sopenharmony_ci					  | NETIF_F_IPV6_CSUM
358362306a36Sopenharmony_ci					  | NETIF_F_GSO_UDP_TUNNEL
358462306a36Sopenharmony_ci					  | NETIF_F_HW_CSUM | NETIF_F_SG
358562306a36Sopenharmony_ci					  | NETIF_F_RXCSUM
358662306a36Sopenharmony_ci					  | NETIF_F_TSO | NETIF_F_TSO6
358762306a36Sopenharmony_ci					  | NETIF_F_LRO;
358862306a36Sopenharmony_ci
358962306a36Sopenharmony_ci		netdev->hw_enc_features = (lio->enc_dev_capability &
359062306a36Sopenharmony_ci					   ~NETIF_F_LRO);
359162306a36Sopenharmony_ci
359262306a36Sopenharmony_ci		netdev->udp_tunnel_nic_info = &liquidio_udp_tunnels;
359362306a36Sopenharmony_ci
359462306a36Sopenharmony_ci		lio->dev_capability |= NETIF_F_GSO_UDP_TUNNEL;
359562306a36Sopenharmony_ci
359662306a36Sopenharmony_ci		netdev->vlan_features = lio->dev_capability;
359762306a36Sopenharmony_ci		/* Add any unchangeable hw features */
359862306a36Sopenharmony_ci		lio->dev_capability |=  NETIF_F_HW_VLAN_CTAG_FILTER |
359962306a36Sopenharmony_ci					NETIF_F_HW_VLAN_CTAG_RX |
360062306a36Sopenharmony_ci					NETIF_F_HW_VLAN_CTAG_TX;
360162306a36Sopenharmony_ci
360262306a36Sopenharmony_ci		netdev->features = (lio->dev_capability & ~NETIF_F_LRO);
360362306a36Sopenharmony_ci
360462306a36Sopenharmony_ci		netdev->hw_features = lio->dev_capability;
360562306a36Sopenharmony_ci		/*HW_VLAN_RX and HW_VLAN_FILTER is always on*/
360662306a36Sopenharmony_ci		netdev->hw_features = netdev->hw_features &
360762306a36Sopenharmony_ci			~NETIF_F_HW_VLAN_CTAG_RX;
360862306a36Sopenharmony_ci
360962306a36Sopenharmony_ci		/* MTU range: 68 - 16000 */
361062306a36Sopenharmony_ci		netdev->min_mtu = LIO_MIN_MTU_SIZE;
361162306a36Sopenharmony_ci		netdev->max_mtu = LIO_MAX_MTU_SIZE;
361262306a36Sopenharmony_ci
361362306a36Sopenharmony_ci		/* Point to the  properties for octeon device to which this
361462306a36Sopenharmony_ci		 * interface belongs.
361562306a36Sopenharmony_ci		 */
361662306a36Sopenharmony_ci		lio->oct_dev = octeon_dev;
361762306a36Sopenharmony_ci		lio->octprops = props;
361862306a36Sopenharmony_ci		lio->netdev = netdev;
361962306a36Sopenharmony_ci
362062306a36Sopenharmony_ci		dev_dbg(&octeon_dev->pci_dev->dev,
362162306a36Sopenharmony_ci			"if%d gmx: %d hw_addr: 0x%llx\n", i,
362262306a36Sopenharmony_ci			lio->linfo.gmxport, CVM_CAST64(lio->linfo.hw_addr));
362362306a36Sopenharmony_ci
362462306a36Sopenharmony_ci		for (j = 0; j < octeon_dev->sriov_info.max_vfs; j++) {
362562306a36Sopenharmony_ci			u8 vfmac[ETH_ALEN];
362662306a36Sopenharmony_ci
362762306a36Sopenharmony_ci			eth_random_addr(vfmac);
362862306a36Sopenharmony_ci			if (__liquidio_set_vf_mac(netdev, j, vfmac, false)) {
362962306a36Sopenharmony_ci				dev_err(&octeon_dev->pci_dev->dev,
363062306a36Sopenharmony_ci					"Error setting VF%d MAC address\n",
363162306a36Sopenharmony_ci					j);
363262306a36Sopenharmony_ci				goto setup_nic_dev_free;
363362306a36Sopenharmony_ci			}
363462306a36Sopenharmony_ci		}
363562306a36Sopenharmony_ci
363662306a36Sopenharmony_ci		/* 64-bit swap required on LE machines */
363762306a36Sopenharmony_ci		octeon_swap_8B_data(&lio->linfo.hw_addr, 1);
363862306a36Sopenharmony_ci		for (j = 0; j < 6; j++)
363962306a36Sopenharmony_ci			mac[j] = *((u8 *)(((u8 *)&lio->linfo.hw_addr) + 2 + j));
364062306a36Sopenharmony_ci
364162306a36Sopenharmony_ci		/* Copy MAC Address to OS network device structure */
364262306a36Sopenharmony_ci
364362306a36Sopenharmony_ci		eth_hw_addr_set(netdev, mac);
364462306a36Sopenharmony_ci
364562306a36Sopenharmony_ci		/* By default all interfaces on a single Octeon uses the same
364662306a36Sopenharmony_ci		 * tx and rx queues
364762306a36Sopenharmony_ci		 */
364862306a36Sopenharmony_ci		lio->txq = lio->linfo.txpciq[0].s.q_no;
364962306a36Sopenharmony_ci		lio->rxq = lio->linfo.rxpciq[0].s.q_no;
365062306a36Sopenharmony_ci		if (liquidio_setup_io_queues(octeon_dev, i,
365162306a36Sopenharmony_ci					     lio->linfo.num_txpciq,
365262306a36Sopenharmony_ci					     lio->linfo.num_rxpciq)) {
365362306a36Sopenharmony_ci			dev_err(&octeon_dev->pci_dev->dev, "I/O queues creation failed\n");
365462306a36Sopenharmony_ci			goto setup_nic_dev_free;
365562306a36Sopenharmony_ci		}
365662306a36Sopenharmony_ci
365762306a36Sopenharmony_ci		ifstate_set(lio, LIO_IFSTATE_DROQ_OPS);
365862306a36Sopenharmony_ci
365962306a36Sopenharmony_ci		lio->tx_qsize = octeon_get_tx_qsize(octeon_dev, lio->txq);
366062306a36Sopenharmony_ci		lio->rx_qsize = octeon_get_rx_qsize(octeon_dev, lio->rxq);
366162306a36Sopenharmony_ci
366262306a36Sopenharmony_ci		if (lio_setup_glists(octeon_dev, lio, num_iqueues)) {
366362306a36Sopenharmony_ci			dev_err(&octeon_dev->pci_dev->dev,
366462306a36Sopenharmony_ci				"Gather list allocation failed\n");
366562306a36Sopenharmony_ci			goto setup_nic_dev_free;
366662306a36Sopenharmony_ci		}
366762306a36Sopenharmony_ci
366862306a36Sopenharmony_ci		/* Register ethtool support */
366962306a36Sopenharmony_ci		liquidio_set_ethtool_ops(netdev);
367062306a36Sopenharmony_ci		if (lio->oct_dev->chip_id == OCTEON_CN23XX_PF_VID)
367162306a36Sopenharmony_ci			octeon_dev->priv_flags = OCT_PRIV_FLAG_DEFAULT;
367262306a36Sopenharmony_ci		else
367362306a36Sopenharmony_ci			octeon_dev->priv_flags = 0x0;
367462306a36Sopenharmony_ci
367562306a36Sopenharmony_ci		if (netdev->features & NETIF_F_LRO)
367662306a36Sopenharmony_ci			liquidio_set_feature(netdev, OCTNET_CMD_LRO_ENABLE,
367762306a36Sopenharmony_ci					     OCTNIC_LROIPV4 | OCTNIC_LROIPV6);
367862306a36Sopenharmony_ci
367962306a36Sopenharmony_ci		liquidio_set_feature(netdev, OCTNET_CMD_VLAN_FILTER_CTL,
368062306a36Sopenharmony_ci				     OCTNET_CMD_VLAN_FILTER_ENABLE);
368162306a36Sopenharmony_ci
368262306a36Sopenharmony_ci		if ((debug != -1) && (debug & NETIF_MSG_HW))
368362306a36Sopenharmony_ci			liquidio_set_feature(netdev,
368462306a36Sopenharmony_ci					     OCTNET_CMD_VERBOSE_ENABLE, 0);
368562306a36Sopenharmony_ci
368662306a36Sopenharmony_ci		if (setup_link_status_change_wq(netdev))
368762306a36Sopenharmony_ci			goto setup_nic_dev_free;
368862306a36Sopenharmony_ci
368962306a36Sopenharmony_ci		if ((octeon_dev->fw_info.app_cap_flags &
369062306a36Sopenharmony_ci		     LIQUIDIO_TIME_SYNC_CAP) &&
369162306a36Sopenharmony_ci		    setup_sync_octeon_time_wq(netdev))
369262306a36Sopenharmony_ci			goto setup_nic_dev_free;
369362306a36Sopenharmony_ci
369462306a36Sopenharmony_ci		if (setup_rx_oom_poll_fn(netdev))
369562306a36Sopenharmony_ci			goto setup_nic_dev_free;
369662306a36Sopenharmony_ci
369762306a36Sopenharmony_ci		/* Register the network device with the OS */
369862306a36Sopenharmony_ci		if (register_netdev(netdev)) {
369962306a36Sopenharmony_ci			dev_err(&octeon_dev->pci_dev->dev, "Device registration failed\n");
370062306a36Sopenharmony_ci			goto setup_nic_dev_free;
370162306a36Sopenharmony_ci		}
370262306a36Sopenharmony_ci
370362306a36Sopenharmony_ci		dev_dbg(&octeon_dev->pci_dev->dev,
370462306a36Sopenharmony_ci			"Setup NIC ifidx:%d mac:%02x%02x%02x%02x%02x%02x\n",
370562306a36Sopenharmony_ci			i, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
370662306a36Sopenharmony_ci		netif_carrier_off(netdev);
370762306a36Sopenharmony_ci		lio->link_changes++;
370862306a36Sopenharmony_ci
370962306a36Sopenharmony_ci		ifstate_set(lio, LIO_IFSTATE_REGISTERED);
371062306a36Sopenharmony_ci
371162306a36Sopenharmony_ci		/* Sending command to firmware to enable Rx checksum offload
371262306a36Sopenharmony_ci		 * by default at the time of setup of Liquidio driver for
371362306a36Sopenharmony_ci		 * this device
371462306a36Sopenharmony_ci		 */
371562306a36Sopenharmony_ci		liquidio_set_rxcsum_command(netdev, OCTNET_CMD_TNL_RX_CSUM_CTL,
371662306a36Sopenharmony_ci					    OCTNET_CMD_RXCSUM_ENABLE);
371762306a36Sopenharmony_ci		liquidio_set_feature(netdev, OCTNET_CMD_TNL_TX_CSUM_CTL,
371862306a36Sopenharmony_ci				     OCTNET_CMD_TXCSUM_ENABLE);
371962306a36Sopenharmony_ci
372062306a36Sopenharmony_ci		dev_dbg(&octeon_dev->pci_dev->dev,
372162306a36Sopenharmony_ci			"NIC ifidx:%d Setup successful\n", i);
372262306a36Sopenharmony_ci
372362306a36Sopenharmony_ci		if (octeon_dev->subsystem_id ==
372462306a36Sopenharmony_ci			OCTEON_CN2350_25GB_SUBSYS_ID ||
372562306a36Sopenharmony_ci		    octeon_dev->subsystem_id ==
372662306a36Sopenharmony_ci			OCTEON_CN2360_25GB_SUBSYS_ID) {
372762306a36Sopenharmony_ci			cur_ver = OCT_FW_VER(octeon_dev->fw_info.ver.maj,
372862306a36Sopenharmony_ci					     octeon_dev->fw_info.ver.min,
372962306a36Sopenharmony_ci					     octeon_dev->fw_info.ver.rev);
373062306a36Sopenharmony_ci
373162306a36Sopenharmony_ci			/* speed control unsupported in f/w older than 1.7.2 */
373262306a36Sopenharmony_ci			if (cur_ver < OCT_FW_VER(1, 7, 2)) {
373362306a36Sopenharmony_ci				dev_info(&octeon_dev->pci_dev->dev,
373462306a36Sopenharmony_ci					 "speed setting not supported by f/w.");
373562306a36Sopenharmony_ci				octeon_dev->speed_setting = 25;
373662306a36Sopenharmony_ci				octeon_dev->no_speed_setting = 1;
373762306a36Sopenharmony_ci			} else {
373862306a36Sopenharmony_ci				liquidio_get_speed(lio);
373962306a36Sopenharmony_ci			}
374062306a36Sopenharmony_ci
374162306a36Sopenharmony_ci			if (octeon_dev->speed_setting == 0) {
374262306a36Sopenharmony_ci				octeon_dev->speed_setting = 25;
374362306a36Sopenharmony_ci				octeon_dev->no_speed_setting = 1;
374462306a36Sopenharmony_ci			}
374562306a36Sopenharmony_ci		} else {
374662306a36Sopenharmony_ci			octeon_dev->no_speed_setting = 1;
374762306a36Sopenharmony_ci			octeon_dev->speed_setting = 10;
374862306a36Sopenharmony_ci		}
374962306a36Sopenharmony_ci		octeon_dev->speed_boot = octeon_dev->speed_setting;
375062306a36Sopenharmony_ci
375162306a36Sopenharmony_ci		/* don't read FEC setting if unsupported by f/w (see above) */
375262306a36Sopenharmony_ci		if (octeon_dev->speed_boot == 25 &&
375362306a36Sopenharmony_ci		    !octeon_dev->no_speed_setting) {
375462306a36Sopenharmony_ci			liquidio_get_fec(lio);
375562306a36Sopenharmony_ci			octeon_dev->props[lio->ifidx].fec_boot =
375662306a36Sopenharmony_ci				octeon_dev->props[lio->ifidx].fec;
375762306a36Sopenharmony_ci		}
375862306a36Sopenharmony_ci	}
375962306a36Sopenharmony_ci
376062306a36Sopenharmony_ci	device_lock(&octeon_dev->pci_dev->dev);
376162306a36Sopenharmony_ci	devlink = devlink_alloc(&liquidio_devlink_ops,
376262306a36Sopenharmony_ci				sizeof(struct lio_devlink_priv),
376362306a36Sopenharmony_ci				&octeon_dev->pci_dev->dev);
376462306a36Sopenharmony_ci	if (!devlink) {
376562306a36Sopenharmony_ci		device_unlock(&octeon_dev->pci_dev->dev);
376662306a36Sopenharmony_ci		dev_err(&octeon_dev->pci_dev->dev, "devlink alloc failed\n");
376762306a36Sopenharmony_ci		goto setup_nic_dev_free;
376862306a36Sopenharmony_ci	}
376962306a36Sopenharmony_ci
377062306a36Sopenharmony_ci	lio_devlink = devlink_priv(devlink);
377162306a36Sopenharmony_ci	lio_devlink->oct = octeon_dev;
377262306a36Sopenharmony_ci
377362306a36Sopenharmony_ci	octeon_dev->devlink = devlink;
377462306a36Sopenharmony_ci	octeon_dev->eswitch_mode = DEVLINK_ESWITCH_MODE_LEGACY;
377562306a36Sopenharmony_ci	devlink_register(devlink);
377662306a36Sopenharmony_ci	device_unlock(&octeon_dev->pci_dev->dev);
377762306a36Sopenharmony_ci
377862306a36Sopenharmony_ci	return 0;
377962306a36Sopenharmony_ci
378062306a36Sopenharmony_cisetup_nic_dev_free:
378162306a36Sopenharmony_ci
378262306a36Sopenharmony_ci	while (i--) {
378362306a36Sopenharmony_ci		dev_err(&octeon_dev->pci_dev->dev,
378462306a36Sopenharmony_ci			"NIC ifidx:%d Setup failed\n", i);
378562306a36Sopenharmony_ci		liquidio_destroy_nic_device(octeon_dev, i);
378662306a36Sopenharmony_ci	}
378762306a36Sopenharmony_ci
378862306a36Sopenharmony_cisetup_nic_dev_done:
378962306a36Sopenharmony_ci
379062306a36Sopenharmony_ci	return -ENODEV;
379162306a36Sopenharmony_ci}
379262306a36Sopenharmony_ci
379362306a36Sopenharmony_ci#ifdef CONFIG_PCI_IOV
379462306a36Sopenharmony_cistatic int octeon_enable_sriov(struct octeon_device *oct)
379562306a36Sopenharmony_ci{
379662306a36Sopenharmony_ci	unsigned int num_vfs_alloced = oct->sriov_info.num_vfs_alloced;
379762306a36Sopenharmony_ci	struct pci_dev *vfdev;
379862306a36Sopenharmony_ci	int err;
379962306a36Sopenharmony_ci	u32 u;
380062306a36Sopenharmony_ci
380162306a36Sopenharmony_ci	if (OCTEON_CN23XX_PF(oct) && num_vfs_alloced) {
380262306a36Sopenharmony_ci		err = pci_enable_sriov(oct->pci_dev,
380362306a36Sopenharmony_ci				       oct->sriov_info.num_vfs_alloced);
380462306a36Sopenharmony_ci		if (err) {
380562306a36Sopenharmony_ci			dev_err(&oct->pci_dev->dev,
380662306a36Sopenharmony_ci				"OCTEON: Failed to enable PCI sriov: %d\n",
380762306a36Sopenharmony_ci				err);
380862306a36Sopenharmony_ci			oct->sriov_info.num_vfs_alloced = 0;
380962306a36Sopenharmony_ci			return err;
381062306a36Sopenharmony_ci		}
381162306a36Sopenharmony_ci		oct->sriov_info.sriov_enabled = 1;
381262306a36Sopenharmony_ci
381362306a36Sopenharmony_ci		/* init lookup table that maps DPI ring number to VF pci_dev
381462306a36Sopenharmony_ci		 * struct pointer
381562306a36Sopenharmony_ci		 */
381662306a36Sopenharmony_ci		u = 0;
381762306a36Sopenharmony_ci		vfdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
381862306a36Sopenharmony_ci				       OCTEON_CN23XX_VF_VID, NULL);
381962306a36Sopenharmony_ci		while (vfdev) {
382062306a36Sopenharmony_ci			if (vfdev->is_virtfn &&
382162306a36Sopenharmony_ci			    (vfdev->physfn == oct->pci_dev)) {
382262306a36Sopenharmony_ci				oct->sriov_info.dpiring_to_vfpcidev_lut[u] =
382362306a36Sopenharmony_ci					vfdev;
382462306a36Sopenharmony_ci				u += oct->sriov_info.rings_per_vf;
382562306a36Sopenharmony_ci			}
382662306a36Sopenharmony_ci			vfdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
382762306a36Sopenharmony_ci					       OCTEON_CN23XX_VF_VID, vfdev);
382862306a36Sopenharmony_ci		}
382962306a36Sopenharmony_ci	}
383062306a36Sopenharmony_ci
383162306a36Sopenharmony_ci	return num_vfs_alloced;
383262306a36Sopenharmony_ci}
383362306a36Sopenharmony_ci
383462306a36Sopenharmony_cistatic int lio_pci_sriov_disable(struct octeon_device *oct)
383562306a36Sopenharmony_ci{
383662306a36Sopenharmony_ci	int u;
383762306a36Sopenharmony_ci
383862306a36Sopenharmony_ci	if (pci_vfs_assigned(oct->pci_dev)) {
383962306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "VFs are still assigned to VMs.\n");
384062306a36Sopenharmony_ci		return -EPERM;
384162306a36Sopenharmony_ci	}
384262306a36Sopenharmony_ci
384362306a36Sopenharmony_ci	pci_disable_sriov(oct->pci_dev);
384462306a36Sopenharmony_ci
384562306a36Sopenharmony_ci	u = 0;
384662306a36Sopenharmony_ci	while (u < MAX_POSSIBLE_VFS) {
384762306a36Sopenharmony_ci		oct->sriov_info.dpiring_to_vfpcidev_lut[u] = NULL;
384862306a36Sopenharmony_ci		u += oct->sriov_info.rings_per_vf;
384962306a36Sopenharmony_ci	}
385062306a36Sopenharmony_ci
385162306a36Sopenharmony_ci	oct->sriov_info.num_vfs_alloced = 0;
385262306a36Sopenharmony_ci	dev_info(&oct->pci_dev->dev, "oct->pf_num:%d disabled VFs\n",
385362306a36Sopenharmony_ci		 oct->pf_num);
385462306a36Sopenharmony_ci
385562306a36Sopenharmony_ci	return 0;
385662306a36Sopenharmony_ci}
385762306a36Sopenharmony_ci
385862306a36Sopenharmony_cistatic int liquidio_enable_sriov(struct pci_dev *dev, int num_vfs)
385962306a36Sopenharmony_ci{
386062306a36Sopenharmony_ci	struct octeon_device *oct = pci_get_drvdata(dev);
386162306a36Sopenharmony_ci	int ret = 0;
386262306a36Sopenharmony_ci
386362306a36Sopenharmony_ci	if ((num_vfs == oct->sriov_info.num_vfs_alloced) &&
386462306a36Sopenharmony_ci	    (oct->sriov_info.sriov_enabled)) {
386562306a36Sopenharmony_ci		dev_info(&oct->pci_dev->dev, "oct->pf_num:%d already enabled num_vfs:%d\n",
386662306a36Sopenharmony_ci			 oct->pf_num, num_vfs);
386762306a36Sopenharmony_ci		return 0;
386862306a36Sopenharmony_ci	}
386962306a36Sopenharmony_ci
387062306a36Sopenharmony_ci	if (!num_vfs) {
387162306a36Sopenharmony_ci		lio_vf_rep_destroy(oct);
387262306a36Sopenharmony_ci		ret = lio_pci_sriov_disable(oct);
387362306a36Sopenharmony_ci	} else if (num_vfs > oct->sriov_info.max_vfs) {
387462306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev,
387562306a36Sopenharmony_ci			"OCTEON: Max allowed VFs:%d user requested:%d",
387662306a36Sopenharmony_ci			oct->sriov_info.max_vfs, num_vfs);
387762306a36Sopenharmony_ci		ret = -EPERM;
387862306a36Sopenharmony_ci	} else {
387962306a36Sopenharmony_ci		oct->sriov_info.num_vfs_alloced = num_vfs;
388062306a36Sopenharmony_ci		ret = octeon_enable_sriov(oct);
388162306a36Sopenharmony_ci		dev_info(&oct->pci_dev->dev, "oct->pf_num:%d num_vfs:%d\n",
388262306a36Sopenharmony_ci			 oct->pf_num, num_vfs);
388362306a36Sopenharmony_ci		ret = lio_vf_rep_create(oct);
388462306a36Sopenharmony_ci		if (ret)
388562306a36Sopenharmony_ci			dev_info(&oct->pci_dev->dev,
388662306a36Sopenharmony_ci				 "vf representor create failed");
388762306a36Sopenharmony_ci	}
388862306a36Sopenharmony_ci
388962306a36Sopenharmony_ci	return ret;
389062306a36Sopenharmony_ci}
389162306a36Sopenharmony_ci#endif
389262306a36Sopenharmony_ci
389362306a36Sopenharmony_ci/**
389462306a36Sopenharmony_ci * liquidio_init_nic_module - initialize the NIC
389562306a36Sopenharmony_ci * @oct: octeon device
389662306a36Sopenharmony_ci *
389762306a36Sopenharmony_ci * This initialization routine is called once the Octeon device application is
389862306a36Sopenharmony_ci * up and running
389962306a36Sopenharmony_ci */
390062306a36Sopenharmony_cistatic int liquidio_init_nic_module(struct octeon_device *oct)
390162306a36Sopenharmony_ci{
390262306a36Sopenharmony_ci	int i, retval = 0;
390362306a36Sopenharmony_ci	int num_nic_ports = CFG_GET_NUM_NIC_PORTS(octeon_get_conf(oct));
390462306a36Sopenharmony_ci
390562306a36Sopenharmony_ci	dev_dbg(&oct->pci_dev->dev, "Initializing network interfaces\n");
390662306a36Sopenharmony_ci
390762306a36Sopenharmony_ci	/* only default iq and oq were initialized
390862306a36Sopenharmony_ci	 * initialize the rest as well
390962306a36Sopenharmony_ci	 */
391062306a36Sopenharmony_ci	/* run port_config command for each port */
391162306a36Sopenharmony_ci	oct->ifcount = num_nic_ports;
391262306a36Sopenharmony_ci
391362306a36Sopenharmony_ci	memset(oct->props, 0, sizeof(struct octdev_props) * num_nic_ports);
391462306a36Sopenharmony_ci
391562306a36Sopenharmony_ci	for (i = 0; i < MAX_OCTEON_LINKS; i++)
391662306a36Sopenharmony_ci		oct->props[i].gmxport = -1;
391762306a36Sopenharmony_ci
391862306a36Sopenharmony_ci	retval = setup_nic_devices(oct);
391962306a36Sopenharmony_ci	if (retval) {
392062306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "Setup NIC devices failed\n");
392162306a36Sopenharmony_ci		goto octnet_init_failure;
392262306a36Sopenharmony_ci	}
392362306a36Sopenharmony_ci
392462306a36Sopenharmony_ci	/* Call vf_rep_modinit if the firmware is switchdev capable
392562306a36Sopenharmony_ci	 * and do it from the first liquidio function probed.
392662306a36Sopenharmony_ci	 */
392762306a36Sopenharmony_ci	if (!oct->octeon_id &&
392862306a36Sopenharmony_ci	    oct->fw_info.app_cap_flags & LIQUIDIO_SWITCHDEV_CAP) {
392962306a36Sopenharmony_ci		retval = lio_vf_rep_modinit();
393062306a36Sopenharmony_ci		if (retval) {
393162306a36Sopenharmony_ci			liquidio_stop_nic_module(oct);
393262306a36Sopenharmony_ci			goto octnet_init_failure;
393362306a36Sopenharmony_ci		}
393462306a36Sopenharmony_ci	}
393562306a36Sopenharmony_ci
393662306a36Sopenharmony_ci	liquidio_ptp_init(oct);
393762306a36Sopenharmony_ci
393862306a36Sopenharmony_ci	dev_dbg(&oct->pci_dev->dev, "Network interfaces ready\n");
393962306a36Sopenharmony_ci
394062306a36Sopenharmony_ci	return retval;
394162306a36Sopenharmony_ci
394262306a36Sopenharmony_cioctnet_init_failure:
394362306a36Sopenharmony_ci
394462306a36Sopenharmony_ci	oct->ifcount = 0;
394562306a36Sopenharmony_ci
394662306a36Sopenharmony_ci	return retval;
394762306a36Sopenharmony_ci}
394862306a36Sopenharmony_ci
394962306a36Sopenharmony_ci/**
395062306a36Sopenharmony_ci * nic_starter - finish init
395162306a36Sopenharmony_ci * @work:  work struct work_struct
395262306a36Sopenharmony_ci *
395362306a36Sopenharmony_ci * starter callback that invokes the remaining initialization work after the NIC is up and running.
395462306a36Sopenharmony_ci */
395562306a36Sopenharmony_cistatic void nic_starter(struct work_struct *work)
395662306a36Sopenharmony_ci{
395762306a36Sopenharmony_ci	struct octeon_device *oct;
395862306a36Sopenharmony_ci	struct cavium_wk *wk = (struct cavium_wk *)work;
395962306a36Sopenharmony_ci
396062306a36Sopenharmony_ci	oct = (struct octeon_device *)wk->ctxptr;
396162306a36Sopenharmony_ci
396262306a36Sopenharmony_ci	if (atomic_read(&oct->status) == OCT_DEV_RUNNING)
396362306a36Sopenharmony_ci		return;
396462306a36Sopenharmony_ci
396562306a36Sopenharmony_ci	/* If the status of the device is CORE_OK, the core
396662306a36Sopenharmony_ci	 * application has reported its application type. Call
396762306a36Sopenharmony_ci	 * any registered handlers now and move to the RUNNING
396862306a36Sopenharmony_ci	 * state.
396962306a36Sopenharmony_ci	 */
397062306a36Sopenharmony_ci	if (atomic_read(&oct->status) != OCT_DEV_CORE_OK) {
397162306a36Sopenharmony_ci		schedule_delayed_work(&oct->nic_poll_work.work,
397262306a36Sopenharmony_ci				      LIQUIDIO_STARTER_POLL_INTERVAL_MS);
397362306a36Sopenharmony_ci		return;
397462306a36Sopenharmony_ci	}
397562306a36Sopenharmony_ci
397662306a36Sopenharmony_ci	atomic_set(&oct->status, OCT_DEV_RUNNING);
397762306a36Sopenharmony_ci
397862306a36Sopenharmony_ci	if (oct->app_mode && oct->app_mode == CVM_DRV_NIC_APP) {
397962306a36Sopenharmony_ci		dev_dbg(&oct->pci_dev->dev, "Starting NIC module\n");
398062306a36Sopenharmony_ci
398162306a36Sopenharmony_ci		if (liquidio_init_nic_module(oct))
398262306a36Sopenharmony_ci			dev_err(&oct->pci_dev->dev, "NIC initialization failed\n");
398362306a36Sopenharmony_ci		else
398462306a36Sopenharmony_ci			handshake[oct->octeon_id].started_ok = 1;
398562306a36Sopenharmony_ci	} else {
398662306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev,
398762306a36Sopenharmony_ci			"Unexpected application running on NIC (%d). Check firmware.\n",
398862306a36Sopenharmony_ci			oct->app_mode);
398962306a36Sopenharmony_ci	}
399062306a36Sopenharmony_ci
399162306a36Sopenharmony_ci	complete(&handshake[oct->octeon_id].started);
399262306a36Sopenharmony_ci}
399362306a36Sopenharmony_ci
399462306a36Sopenharmony_cistatic int
399562306a36Sopenharmony_ciocteon_recv_vf_drv_notice(struct octeon_recv_info *recv_info, void *buf)
399662306a36Sopenharmony_ci{
399762306a36Sopenharmony_ci	struct octeon_device *oct = (struct octeon_device *)buf;
399862306a36Sopenharmony_ci	struct octeon_recv_pkt *recv_pkt = recv_info->recv_pkt;
399962306a36Sopenharmony_ci	int i, notice, vf_idx;
400062306a36Sopenharmony_ci	bool cores_crashed;
400162306a36Sopenharmony_ci	u64 *data, vf_num;
400262306a36Sopenharmony_ci
400362306a36Sopenharmony_ci	notice = recv_pkt->rh.r.ossp;
400462306a36Sopenharmony_ci	data = (u64 *)(get_rbd(recv_pkt->buffer_ptr[0]) + OCT_DROQ_INFO_SIZE);
400562306a36Sopenharmony_ci
400662306a36Sopenharmony_ci	/* the first 64-bit word of data is the vf_num */
400762306a36Sopenharmony_ci	vf_num = data[0];
400862306a36Sopenharmony_ci	octeon_swap_8B_data(&vf_num, 1);
400962306a36Sopenharmony_ci	vf_idx = (int)vf_num - 1;
401062306a36Sopenharmony_ci
401162306a36Sopenharmony_ci	cores_crashed = READ_ONCE(oct->cores_crashed);
401262306a36Sopenharmony_ci
401362306a36Sopenharmony_ci	if (notice == VF_DRV_LOADED) {
401462306a36Sopenharmony_ci		if (!(oct->sriov_info.vf_drv_loaded_mask & BIT_ULL(vf_idx))) {
401562306a36Sopenharmony_ci			oct->sriov_info.vf_drv_loaded_mask |= BIT_ULL(vf_idx);
401662306a36Sopenharmony_ci			dev_info(&oct->pci_dev->dev,
401762306a36Sopenharmony_ci				 "driver for VF%d was loaded\n", vf_idx);
401862306a36Sopenharmony_ci			if (!cores_crashed)
401962306a36Sopenharmony_ci				try_module_get(THIS_MODULE);
402062306a36Sopenharmony_ci		}
402162306a36Sopenharmony_ci	} else if (notice == VF_DRV_REMOVED) {
402262306a36Sopenharmony_ci		if (oct->sriov_info.vf_drv_loaded_mask & BIT_ULL(vf_idx)) {
402362306a36Sopenharmony_ci			oct->sriov_info.vf_drv_loaded_mask &= ~BIT_ULL(vf_idx);
402462306a36Sopenharmony_ci			dev_info(&oct->pci_dev->dev,
402562306a36Sopenharmony_ci				 "driver for VF%d was removed\n", vf_idx);
402662306a36Sopenharmony_ci			if (!cores_crashed)
402762306a36Sopenharmony_ci				module_put(THIS_MODULE);
402862306a36Sopenharmony_ci		}
402962306a36Sopenharmony_ci	} else if (notice == VF_DRV_MACADDR_CHANGED) {
403062306a36Sopenharmony_ci		u8 *b = (u8 *)&data[1];
403162306a36Sopenharmony_ci
403262306a36Sopenharmony_ci		oct->sriov_info.vf_macaddr[vf_idx] = data[1];
403362306a36Sopenharmony_ci		dev_info(&oct->pci_dev->dev,
403462306a36Sopenharmony_ci			 "VF driver changed VF%d's MAC address to %pM\n",
403562306a36Sopenharmony_ci			 vf_idx, b + 2);
403662306a36Sopenharmony_ci	}
403762306a36Sopenharmony_ci
403862306a36Sopenharmony_ci	for (i = 0; i < recv_pkt->buffer_count; i++)
403962306a36Sopenharmony_ci		recv_buffer_free(recv_pkt->buffer_ptr[i]);
404062306a36Sopenharmony_ci	octeon_free_recv_info(recv_info);
404162306a36Sopenharmony_ci
404262306a36Sopenharmony_ci	return 0;
404362306a36Sopenharmony_ci}
404462306a36Sopenharmony_ci
404562306a36Sopenharmony_ci/**
404662306a36Sopenharmony_ci * octeon_device_init - Device initialization for each Octeon device that is probed
404762306a36Sopenharmony_ci * @octeon_dev:  octeon device
404862306a36Sopenharmony_ci */
404962306a36Sopenharmony_cistatic int octeon_device_init(struct octeon_device *octeon_dev)
405062306a36Sopenharmony_ci{
405162306a36Sopenharmony_ci	int j, ret;
405262306a36Sopenharmony_ci	char bootcmd[] = "\n";
405362306a36Sopenharmony_ci	char *dbg_enb = NULL;
405462306a36Sopenharmony_ci	enum lio_fw_state fw_state;
405562306a36Sopenharmony_ci	struct octeon_device_priv *oct_priv = octeon_dev->priv;
405662306a36Sopenharmony_ci	atomic_set(&octeon_dev->status, OCT_DEV_BEGIN_STATE);
405762306a36Sopenharmony_ci
405862306a36Sopenharmony_ci	/* Enable access to the octeon device and make its DMA capability
405962306a36Sopenharmony_ci	 * known to the OS.
406062306a36Sopenharmony_ci	 */
406162306a36Sopenharmony_ci	if (octeon_pci_os_setup(octeon_dev))
406262306a36Sopenharmony_ci		return 1;
406362306a36Sopenharmony_ci
406462306a36Sopenharmony_ci	atomic_set(&octeon_dev->status, OCT_DEV_PCI_ENABLE_DONE);
406562306a36Sopenharmony_ci
406662306a36Sopenharmony_ci	/* Identify the Octeon type and map the BAR address space. */
406762306a36Sopenharmony_ci	if (octeon_chip_specific_setup(octeon_dev)) {
406862306a36Sopenharmony_ci		dev_err(&octeon_dev->pci_dev->dev, "Chip specific setup failed\n");
406962306a36Sopenharmony_ci		return 1;
407062306a36Sopenharmony_ci	}
407162306a36Sopenharmony_ci
407262306a36Sopenharmony_ci	atomic_set(&octeon_dev->status, OCT_DEV_PCI_MAP_DONE);
407362306a36Sopenharmony_ci
407462306a36Sopenharmony_ci	/* Only add a reference after setting status 'OCT_DEV_PCI_MAP_DONE',
407562306a36Sopenharmony_ci	 * since that is what is required for the reference to be removed
407662306a36Sopenharmony_ci	 * during de-initialization (see 'octeon_destroy_resources').
407762306a36Sopenharmony_ci	 */
407862306a36Sopenharmony_ci	octeon_register_device(octeon_dev, octeon_dev->pci_dev->bus->number,
407962306a36Sopenharmony_ci			       PCI_SLOT(octeon_dev->pci_dev->devfn),
408062306a36Sopenharmony_ci			       PCI_FUNC(octeon_dev->pci_dev->devfn),
408162306a36Sopenharmony_ci			       true);
408262306a36Sopenharmony_ci
408362306a36Sopenharmony_ci	octeon_dev->app_mode = CVM_DRV_INVALID_APP;
408462306a36Sopenharmony_ci
408562306a36Sopenharmony_ci	/* CN23XX supports preloaded firmware if the following is true:
408662306a36Sopenharmony_ci	 *
408762306a36Sopenharmony_ci	 * The adapter indicates that firmware is currently running AND
408862306a36Sopenharmony_ci	 * 'fw_type' is 'auto'.
408962306a36Sopenharmony_ci	 *
409062306a36Sopenharmony_ci	 * (default state is NEEDS_TO_BE_LOADED, override it if appropriate).
409162306a36Sopenharmony_ci	 */
409262306a36Sopenharmony_ci	if (OCTEON_CN23XX_PF(octeon_dev) &&
409362306a36Sopenharmony_ci	    cn23xx_fw_loaded(octeon_dev) && fw_type_is_auto()) {
409462306a36Sopenharmony_ci		atomic_cmpxchg(octeon_dev->adapter_fw_state,
409562306a36Sopenharmony_ci			       FW_NEEDS_TO_BE_LOADED, FW_IS_PRELOADED);
409662306a36Sopenharmony_ci	}
409762306a36Sopenharmony_ci
409862306a36Sopenharmony_ci	/* If loading firmware, only first device of adapter needs to do so. */
409962306a36Sopenharmony_ci	fw_state = atomic_cmpxchg(octeon_dev->adapter_fw_state,
410062306a36Sopenharmony_ci				  FW_NEEDS_TO_BE_LOADED,
410162306a36Sopenharmony_ci				  FW_IS_BEING_LOADED);
410262306a36Sopenharmony_ci
410362306a36Sopenharmony_ci	/* Here, [local variable] 'fw_state' is set to one of:
410462306a36Sopenharmony_ci	 *
410562306a36Sopenharmony_ci	 *   FW_IS_PRELOADED:       No firmware is to be loaded (see above)
410662306a36Sopenharmony_ci	 *   FW_NEEDS_TO_BE_LOADED: The driver's first instance will load
410762306a36Sopenharmony_ci	 *                          firmware to the adapter.
410862306a36Sopenharmony_ci	 *   FW_IS_BEING_LOADED:    The driver's second instance will not load
410962306a36Sopenharmony_ci	 *                          firmware to the adapter.
411062306a36Sopenharmony_ci	 */
411162306a36Sopenharmony_ci
411262306a36Sopenharmony_ci	/* Prior to f/w load, perform a soft reset of the Octeon device;
411362306a36Sopenharmony_ci	 * if error resetting, return w/error.
411462306a36Sopenharmony_ci	 */
411562306a36Sopenharmony_ci	if (fw_state == FW_NEEDS_TO_BE_LOADED)
411662306a36Sopenharmony_ci		if (octeon_dev->fn_list.soft_reset(octeon_dev))
411762306a36Sopenharmony_ci			return 1;
411862306a36Sopenharmony_ci
411962306a36Sopenharmony_ci	/* Initialize the dispatch mechanism used to push packets arriving on
412062306a36Sopenharmony_ci	 * Octeon Output queues.
412162306a36Sopenharmony_ci	 */
412262306a36Sopenharmony_ci	if (octeon_init_dispatch_list(octeon_dev))
412362306a36Sopenharmony_ci		return 1;
412462306a36Sopenharmony_ci
412562306a36Sopenharmony_ci	octeon_register_dispatch_fn(octeon_dev, OPCODE_NIC,
412662306a36Sopenharmony_ci				    OPCODE_NIC_CORE_DRV_ACTIVE,
412762306a36Sopenharmony_ci				    octeon_core_drv_init,
412862306a36Sopenharmony_ci				    octeon_dev);
412962306a36Sopenharmony_ci
413062306a36Sopenharmony_ci	octeon_register_dispatch_fn(octeon_dev, OPCODE_NIC,
413162306a36Sopenharmony_ci				    OPCODE_NIC_VF_DRV_NOTICE,
413262306a36Sopenharmony_ci				    octeon_recv_vf_drv_notice, octeon_dev);
413362306a36Sopenharmony_ci	INIT_DELAYED_WORK(&octeon_dev->nic_poll_work.work, nic_starter);
413462306a36Sopenharmony_ci	octeon_dev->nic_poll_work.ctxptr = (void *)octeon_dev;
413562306a36Sopenharmony_ci	schedule_delayed_work(&octeon_dev->nic_poll_work.work,
413662306a36Sopenharmony_ci			      LIQUIDIO_STARTER_POLL_INTERVAL_MS);
413762306a36Sopenharmony_ci
413862306a36Sopenharmony_ci	atomic_set(&octeon_dev->status, OCT_DEV_DISPATCH_INIT_DONE);
413962306a36Sopenharmony_ci
414062306a36Sopenharmony_ci	if (octeon_set_io_queues_off(octeon_dev)) {
414162306a36Sopenharmony_ci		dev_err(&octeon_dev->pci_dev->dev, "setting io queues off failed\n");
414262306a36Sopenharmony_ci		return 1;
414362306a36Sopenharmony_ci	}
414462306a36Sopenharmony_ci
414562306a36Sopenharmony_ci	if (OCTEON_CN23XX_PF(octeon_dev)) {
414662306a36Sopenharmony_ci		ret = octeon_dev->fn_list.setup_device_regs(octeon_dev);
414762306a36Sopenharmony_ci		if (ret) {
414862306a36Sopenharmony_ci			dev_err(&octeon_dev->pci_dev->dev, "OCTEON: Failed to configure device registers\n");
414962306a36Sopenharmony_ci			return ret;
415062306a36Sopenharmony_ci		}
415162306a36Sopenharmony_ci	}
415262306a36Sopenharmony_ci
415362306a36Sopenharmony_ci	/* Initialize soft command buffer pool
415462306a36Sopenharmony_ci	 */
415562306a36Sopenharmony_ci	if (octeon_setup_sc_buffer_pool(octeon_dev)) {
415662306a36Sopenharmony_ci		dev_err(&octeon_dev->pci_dev->dev, "sc buffer pool allocation failed\n");
415762306a36Sopenharmony_ci		return 1;
415862306a36Sopenharmony_ci	}
415962306a36Sopenharmony_ci	atomic_set(&octeon_dev->status, OCT_DEV_SC_BUFF_POOL_INIT_DONE);
416062306a36Sopenharmony_ci
416162306a36Sopenharmony_ci	/*  Setup the data structures that manage this Octeon's Input queues. */
416262306a36Sopenharmony_ci	if (octeon_setup_instr_queues(octeon_dev)) {
416362306a36Sopenharmony_ci		dev_err(&octeon_dev->pci_dev->dev,
416462306a36Sopenharmony_ci			"instruction queue initialization failed\n");
416562306a36Sopenharmony_ci		return 1;
416662306a36Sopenharmony_ci	}
416762306a36Sopenharmony_ci	atomic_set(&octeon_dev->status, OCT_DEV_INSTR_QUEUE_INIT_DONE);
416862306a36Sopenharmony_ci
416962306a36Sopenharmony_ci	/* Initialize lists to manage the requests of different types that
417062306a36Sopenharmony_ci	 * arrive from user & kernel applications for this octeon device.
417162306a36Sopenharmony_ci	 */
417262306a36Sopenharmony_ci	if (octeon_setup_response_list(octeon_dev)) {
417362306a36Sopenharmony_ci		dev_err(&octeon_dev->pci_dev->dev, "Response list allocation failed\n");
417462306a36Sopenharmony_ci		return 1;
417562306a36Sopenharmony_ci	}
417662306a36Sopenharmony_ci	atomic_set(&octeon_dev->status, OCT_DEV_RESP_LIST_INIT_DONE);
417762306a36Sopenharmony_ci
417862306a36Sopenharmony_ci	if (octeon_setup_output_queues(octeon_dev)) {
417962306a36Sopenharmony_ci		dev_err(&octeon_dev->pci_dev->dev, "Output queue initialization failed\n");
418062306a36Sopenharmony_ci		return 1;
418162306a36Sopenharmony_ci	}
418262306a36Sopenharmony_ci
418362306a36Sopenharmony_ci	atomic_set(&octeon_dev->status, OCT_DEV_DROQ_INIT_DONE);
418462306a36Sopenharmony_ci
418562306a36Sopenharmony_ci	if (OCTEON_CN23XX_PF(octeon_dev)) {
418662306a36Sopenharmony_ci		if (octeon_dev->fn_list.setup_mbox(octeon_dev)) {
418762306a36Sopenharmony_ci			dev_err(&octeon_dev->pci_dev->dev, "OCTEON: Mailbox setup failed\n");
418862306a36Sopenharmony_ci			return 1;
418962306a36Sopenharmony_ci		}
419062306a36Sopenharmony_ci		atomic_set(&octeon_dev->status, OCT_DEV_MBOX_SETUP_DONE);
419162306a36Sopenharmony_ci
419262306a36Sopenharmony_ci		if (octeon_allocate_ioq_vector
419362306a36Sopenharmony_ci				(octeon_dev,
419462306a36Sopenharmony_ci				 octeon_dev->sriov_info.num_pf_rings)) {
419562306a36Sopenharmony_ci			dev_err(&octeon_dev->pci_dev->dev, "OCTEON: ioq vector allocation failed\n");
419662306a36Sopenharmony_ci			return 1;
419762306a36Sopenharmony_ci		}
419862306a36Sopenharmony_ci		atomic_set(&octeon_dev->status, OCT_DEV_MSIX_ALLOC_VECTOR_DONE);
419962306a36Sopenharmony_ci
420062306a36Sopenharmony_ci	} else {
420162306a36Sopenharmony_ci		/* The input and output queue registers were setup earlier (the
420262306a36Sopenharmony_ci		 * queues were not enabled). Any additional registers
420362306a36Sopenharmony_ci		 * that need to be programmed should be done now.
420462306a36Sopenharmony_ci		 */
420562306a36Sopenharmony_ci		ret = octeon_dev->fn_list.setup_device_regs(octeon_dev);
420662306a36Sopenharmony_ci		if (ret) {
420762306a36Sopenharmony_ci			dev_err(&octeon_dev->pci_dev->dev,
420862306a36Sopenharmony_ci				"Failed to configure device registers\n");
420962306a36Sopenharmony_ci			return ret;
421062306a36Sopenharmony_ci		}
421162306a36Sopenharmony_ci	}
421262306a36Sopenharmony_ci
421362306a36Sopenharmony_ci	/* Initialize the tasklet that handles output queue packet processing.*/
421462306a36Sopenharmony_ci	dev_dbg(&octeon_dev->pci_dev->dev, "Initializing droq tasklet\n");
421562306a36Sopenharmony_ci	tasklet_setup(&oct_priv->droq_tasklet, octeon_droq_bh);
421662306a36Sopenharmony_ci
421762306a36Sopenharmony_ci	/* Setup the interrupt handler and record the INT SUM register address
421862306a36Sopenharmony_ci	 */
421962306a36Sopenharmony_ci	if (octeon_setup_interrupt(octeon_dev,
422062306a36Sopenharmony_ci				   octeon_dev->sriov_info.num_pf_rings))
422162306a36Sopenharmony_ci		return 1;
422262306a36Sopenharmony_ci
422362306a36Sopenharmony_ci	/* Enable Octeon device interrupts */
422462306a36Sopenharmony_ci	octeon_dev->fn_list.enable_interrupt(octeon_dev, OCTEON_ALL_INTR);
422562306a36Sopenharmony_ci
422662306a36Sopenharmony_ci	atomic_set(&octeon_dev->status, OCT_DEV_INTR_SET_DONE);
422762306a36Sopenharmony_ci
422862306a36Sopenharmony_ci	/* Send Credit for Octeon Output queues. Credits are always sent BEFORE
422962306a36Sopenharmony_ci	 * the output queue is enabled.
423062306a36Sopenharmony_ci	 * This ensures that we'll receive the f/w CORE DRV_ACTIVE message in
423162306a36Sopenharmony_ci	 * case we've configured CN23XX_SLI_GBL_CONTROL[NOPTR_D] = 0.
423262306a36Sopenharmony_ci	 * Otherwise, it is possible that the DRV_ACTIVE message will be sent
423362306a36Sopenharmony_ci	 * before any credits have been issued, causing the ring to be reset
423462306a36Sopenharmony_ci	 * (and the f/w appear to never have started).
423562306a36Sopenharmony_ci	 */
423662306a36Sopenharmony_ci	for (j = 0; j < octeon_dev->num_oqs; j++)
423762306a36Sopenharmony_ci		writel(octeon_dev->droq[j]->max_count,
423862306a36Sopenharmony_ci		       octeon_dev->droq[j]->pkts_credit_reg);
423962306a36Sopenharmony_ci
424062306a36Sopenharmony_ci	/* Enable the input and output queues for this Octeon device */
424162306a36Sopenharmony_ci	ret = octeon_dev->fn_list.enable_io_queues(octeon_dev);
424262306a36Sopenharmony_ci	if (ret) {
424362306a36Sopenharmony_ci		dev_err(&octeon_dev->pci_dev->dev, "Failed to enable input/output queues");
424462306a36Sopenharmony_ci		return ret;
424562306a36Sopenharmony_ci	}
424662306a36Sopenharmony_ci
424762306a36Sopenharmony_ci	atomic_set(&octeon_dev->status, OCT_DEV_IO_QUEUES_DONE);
424862306a36Sopenharmony_ci
424962306a36Sopenharmony_ci	if (fw_state == FW_NEEDS_TO_BE_LOADED) {
425062306a36Sopenharmony_ci		dev_dbg(&octeon_dev->pci_dev->dev, "Waiting for DDR initialization...\n");
425162306a36Sopenharmony_ci		if (!ddr_timeout) {
425262306a36Sopenharmony_ci			dev_info(&octeon_dev->pci_dev->dev,
425362306a36Sopenharmony_ci				 "WAITING. Set ddr_timeout to non-zero value to proceed with initialization.\n");
425462306a36Sopenharmony_ci		}
425562306a36Sopenharmony_ci
425662306a36Sopenharmony_ci		schedule_timeout_uninterruptible(HZ * LIO_RESET_SECS);
425762306a36Sopenharmony_ci
425862306a36Sopenharmony_ci		/* Wait for the octeon to initialize DDR after the soft-reset.*/
425962306a36Sopenharmony_ci		while (!ddr_timeout) {
426062306a36Sopenharmony_ci			set_current_state(TASK_INTERRUPTIBLE);
426162306a36Sopenharmony_ci			if (schedule_timeout(HZ / 10)) {
426262306a36Sopenharmony_ci				/* user probably pressed Control-C */
426362306a36Sopenharmony_ci				return 1;
426462306a36Sopenharmony_ci			}
426562306a36Sopenharmony_ci		}
426662306a36Sopenharmony_ci		ret = octeon_wait_for_ddr_init(octeon_dev, &ddr_timeout);
426762306a36Sopenharmony_ci		if (ret) {
426862306a36Sopenharmony_ci			dev_err(&octeon_dev->pci_dev->dev,
426962306a36Sopenharmony_ci				"DDR not initialized. Please confirm that board is configured to boot from Flash, ret: %d\n",
427062306a36Sopenharmony_ci				ret);
427162306a36Sopenharmony_ci			return 1;
427262306a36Sopenharmony_ci		}
427362306a36Sopenharmony_ci
427462306a36Sopenharmony_ci		if (octeon_wait_for_bootloader(octeon_dev, 1000)) {
427562306a36Sopenharmony_ci			dev_err(&octeon_dev->pci_dev->dev, "Board not responding\n");
427662306a36Sopenharmony_ci			return 1;
427762306a36Sopenharmony_ci		}
427862306a36Sopenharmony_ci
427962306a36Sopenharmony_ci		/* Divert uboot to take commands from host instead. */
428062306a36Sopenharmony_ci		ret = octeon_console_send_cmd(octeon_dev, bootcmd, 50);
428162306a36Sopenharmony_ci
428262306a36Sopenharmony_ci		dev_dbg(&octeon_dev->pci_dev->dev, "Initializing consoles\n");
428362306a36Sopenharmony_ci		ret = octeon_init_consoles(octeon_dev);
428462306a36Sopenharmony_ci		if (ret) {
428562306a36Sopenharmony_ci			dev_err(&octeon_dev->pci_dev->dev, "Could not access board consoles\n");
428662306a36Sopenharmony_ci			return 1;
428762306a36Sopenharmony_ci		}
428862306a36Sopenharmony_ci		/* If console debug enabled, specify empty string to use default
428962306a36Sopenharmony_ci		 * enablement ELSE specify NULL string for 'disabled'.
429062306a36Sopenharmony_ci		 */
429162306a36Sopenharmony_ci		dbg_enb = octeon_console_debug_enabled(0) ? "" : NULL;
429262306a36Sopenharmony_ci		ret = octeon_add_console(octeon_dev, 0, dbg_enb);
429362306a36Sopenharmony_ci		if (ret) {
429462306a36Sopenharmony_ci			dev_err(&octeon_dev->pci_dev->dev, "Could not access board console\n");
429562306a36Sopenharmony_ci			return 1;
429662306a36Sopenharmony_ci		} else if (octeon_console_debug_enabled(0)) {
429762306a36Sopenharmony_ci			/* If console was added AND we're logging console output
429862306a36Sopenharmony_ci			 * then set our console print function.
429962306a36Sopenharmony_ci			 */
430062306a36Sopenharmony_ci			octeon_dev->console[0].print = octeon_dbg_console_print;
430162306a36Sopenharmony_ci		}
430262306a36Sopenharmony_ci
430362306a36Sopenharmony_ci		atomic_set(&octeon_dev->status, OCT_DEV_CONSOLE_INIT_DONE);
430462306a36Sopenharmony_ci
430562306a36Sopenharmony_ci		dev_dbg(&octeon_dev->pci_dev->dev, "Loading firmware\n");
430662306a36Sopenharmony_ci		ret = load_firmware(octeon_dev);
430762306a36Sopenharmony_ci		if (ret) {
430862306a36Sopenharmony_ci			dev_err(&octeon_dev->pci_dev->dev, "Could not load firmware to board\n");
430962306a36Sopenharmony_ci			return 1;
431062306a36Sopenharmony_ci		}
431162306a36Sopenharmony_ci
431262306a36Sopenharmony_ci		atomic_set(octeon_dev->adapter_fw_state, FW_HAS_BEEN_LOADED);
431362306a36Sopenharmony_ci	}
431462306a36Sopenharmony_ci
431562306a36Sopenharmony_ci	handshake[octeon_dev->octeon_id].init_ok = 1;
431662306a36Sopenharmony_ci	complete(&handshake[octeon_dev->octeon_id].init);
431762306a36Sopenharmony_ci
431862306a36Sopenharmony_ci	atomic_set(&octeon_dev->status, OCT_DEV_HOST_OK);
431962306a36Sopenharmony_ci	oct_priv->dev = octeon_dev;
432062306a36Sopenharmony_ci
432162306a36Sopenharmony_ci	return 0;
432262306a36Sopenharmony_ci}
432362306a36Sopenharmony_ci
432462306a36Sopenharmony_ci/**
432562306a36Sopenharmony_ci * octeon_dbg_console_print - Debug console print function
432662306a36Sopenharmony_ci * @oct:  octeon device
432762306a36Sopenharmony_ci * @console_num: console number
432862306a36Sopenharmony_ci * @prefix:      first portion of line to display
432962306a36Sopenharmony_ci * @suffix:      second portion of line to display
433062306a36Sopenharmony_ci *
433162306a36Sopenharmony_ci * The OCTEON debug console outputs entire lines (excluding '\n').
433262306a36Sopenharmony_ci * Normally, the line will be passed in the 'prefix' parameter.
433362306a36Sopenharmony_ci * However, due to buffering, it is possible for a line to be split into two
433462306a36Sopenharmony_ci * parts, in which case they will be passed as the 'prefix' parameter and
433562306a36Sopenharmony_ci * 'suffix' parameter.
433662306a36Sopenharmony_ci */
433762306a36Sopenharmony_cistatic int octeon_dbg_console_print(struct octeon_device *oct, u32 console_num,
433862306a36Sopenharmony_ci				    char *prefix, char *suffix)
433962306a36Sopenharmony_ci{
434062306a36Sopenharmony_ci	if (prefix && suffix)
434162306a36Sopenharmony_ci		dev_info(&oct->pci_dev->dev, "%u: %s%s\n", console_num, prefix,
434262306a36Sopenharmony_ci			 suffix);
434362306a36Sopenharmony_ci	else if (prefix)
434462306a36Sopenharmony_ci		dev_info(&oct->pci_dev->dev, "%u: %s\n", console_num, prefix);
434562306a36Sopenharmony_ci	else if (suffix)
434662306a36Sopenharmony_ci		dev_info(&oct->pci_dev->dev, "%u: %s\n", console_num, suffix);
434762306a36Sopenharmony_ci
434862306a36Sopenharmony_ci	return 0;
434962306a36Sopenharmony_ci}
435062306a36Sopenharmony_ci
435162306a36Sopenharmony_ci/**
435262306a36Sopenharmony_ci * liquidio_exit - Exits the module
435362306a36Sopenharmony_ci */
435462306a36Sopenharmony_cistatic void __exit liquidio_exit(void)
435562306a36Sopenharmony_ci{
435662306a36Sopenharmony_ci	liquidio_deinit_pci();
435762306a36Sopenharmony_ci
435862306a36Sopenharmony_ci	pr_info("LiquidIO network module is now unloaded\n");
435962306a36Sopenharmony_ci}
436062306a36Sopenharmony_ci
436162306a36Sopenharmony_cimodule_init(liquidio_init);
436262306a36Sopenharmony_cimodule_exit(liquidio_exit);
4363