162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright(c) 2013 - 2019 Intel Corporation. */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/module.h>
562306a36Sopenharmony_ci#include <linux/interrupt.h>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "fm10k.h"
862306a36Sopenharmony_ci
962306a36Sopenharmony_cistatic const struct fm10k_info *fm10k_info_tbl[] = {
1062306a36Sopenharmony_ci	[fm10k_device_pf] = &fm10k_pf_info,
1162306a36Sopenharmony_ci	[fm10k_device_vf] = &fm10k_vf_info,
1262306a36Sopenharmony_ci};
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/*
1562306a36Sopenharmony_ci * fm10k_pci_tbl - PCI Device ID Table
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci * Wildcard entries (PCI_ANY_ID) should come last
1862306a36Sopenharmony_ci * Last entry must be all 0s
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * { Vendor ID, Device ID, SubVendor ID, SubDevice ID,
2162306a36Sopenharmony_ci *   Class, Class Mask, private data (not used) }
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_cistatic const struct pci_device_id fm10k_pci_tbl[] = {
2462306a36Sopenharmony_ci	{ PCI_VDEVICE(INTEL, FM10K_DEV_ID_PF), fm10k_device_pf },
2562306a36Sopenharmony_ci	{ PCI_VDEVICE(INTEL, FM10K_DEV_ID_SDI_FM10420_QDA2), fm10k_device_pf },
2662306a36Sopenharmony_ci	{ PCI_VDEVICE(INTEL, FM10K_DEV_ID_SDI_FM10420_DA2), fm10k_device_pf },
2762306a36Sopenharmony_ci	{ PCI_VDEVICE(INTEL, FM10K_DEV_ID_VF), fm10k_device_vf },
2862306a36Sopenharmony_ci	/* required last entry */
2962306a36Sopenharmony_ci	{ 0, }
3062306a36Sopenharmony_ci};
3162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, fm10k_pci_tbl);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ciu16 fm10k_read_pci_cfg_word(struct fm10k_hw *hw, u32 reg)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	struct fm10k_intfc *interface = hw->back;
3662306a36Sopenharmony_ci	u16 value = 0;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	if (FM10K_REMOVED(hw->hw_addr))
3962306a36Sopenharmony_ci		return ~value;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	pci_read_config_word(interface->pdev, reg, &value);
4262306a36Sopenharmony_ci	if (value == 0xFFFF)
4362306a36Sopenharmony_ci		fm10k_write_flush(hw);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	return value;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ciu32 fm10k_read_reg(struct fm10k_hw *hw, int reg)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	u32 __iomem *hw_addr = READ_ONCE(hw->hw_addr);
5162306a36Sopenharmony_ci	u32 value = 0;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	if (FM10K_REMOVED(hw_addr))
5462306a36Sopenharmony_ci		return ~value;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	value = readl(&hw_addr[reg]);
5762306a36Sopenharmony_ci	if (!(~value) && (!reg || !(~readl(hw_addr)))) {
5862306a36Sopenharmony_ci		struct fm10k_intfc *interface = hw->back;
5962306a36Sopenharmony_ci		struct net_device *netdev = interface->netdev;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci		hw->hw_addr = NULL;
6262306a36Sopenharmony_ci		netif_device_detach(netdev);
6362306a36Sopenharmony_ci		netdev_err(netdev, "PCIe link lost, device now detached\n");
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	return value;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic int fm10k_hw_ready(struct fm10k_intfc *interface)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	fm10k_write_flush(hw);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	return FM10K_REMOVED(hw->hw_addr) ? -ENODEV : 0;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/**
7962306a36Sopenharmony_ci * fm10k_macvlan_schedule - Schedule MAC/VLAN queue task
8062306a36Sopenharmony_ci * @interface: fm10k private interface structure
8162306a36Sopenharmony_ci *
8262306a36Sopenharmony_ci * Schedule the MAC/VLAN queue monitor task. If the MAC/VLAN task cannot be
8362306a36Sopenharmony_ci * started immediately, request that it be restarted when possible.
8462306a36Sopenharmony_ci */
8562306a36Sopenharmony_civoid fm10k_macvlan_schedule(struct fm10k_intfc *interface)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	/* Avoid processing the MAC/VLAN queue when the service task is
8862306a36Sopenharmony_ci	 * disabled, or when we're resetting the device.
8962306a36Sopenharmony_ci	 */
9062306a36Sopenharmony_ci	if (!test_bit(__FM10K_MACVLAN_DISABLE, interface->state) &&
9162306a36Sopenharmony_ci	    !test_and_set_bit(__FM10K_MACVLAN_SCHED, interface->state)) {
9262306a36Sopenharmony_ci		clear_bit(__FM10K_MACVLAN_REQUEST, interface->state);
9362306a36Sopenharmony_ci		/* We delay the actual start of execution in order to allow
9462306a36Sopenharmony_ci		 * multiple MAC/VLAN updates to accumulate before handling
9562306a36Sopenharmony_ci		 * them, and to allow some time to let the mailbox drain
9662306a36Sopenharmony_ci		 * between runs.
9762306a36Sopenharmony_ci		 */
9862306a36Sopenharmony_ci		queue_delayed_work(fm10k_workqueue,
9962306a36Sopenharmony_ci				   &interface->macvlan_task, 10);
10062306a36Sopenharmony_ci	} else {
10162306a36Sopenharmony_ci		set_bit(__FM10K_MACVLAN_REQUEST, interface->state);
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/**
10662306a36Sopenharmony_ci * fm10k_stop_macvlan_task - Stop the MAC/VLAN queue monitor
10762306a36Sopenharmony_ci * @interface: fm10k private interface structure
10862306a36Sopenharmony_ci *
10962306a36Sopenharmony_ci * Wait until the MAC/VLAN queue task has stopped, and cancel any future
11062306a36Sopenharmony_ci * requests.
11162306a36Sopenharmony_ci */
11262306a36Sopenharmony_cistatic void fm10k_stop_macvlan_task(struct fm10k_intfc *interface)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	/* Disable the MAC/VLAN work item */
11562306a36Sopenharmony_ci	set_bit(__FM10K_MACVLAN_DISABLE, interface->state);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	/* Make sure we waited until any current invocations have stopped */
11862306a36Sopenharmony_ci	cancel_delayed_work_sync(&interface->macvlan_task);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/* We set the __FM10K_MACVLAN_SCHED bit when we schedule the task.
12162306a36Sopenharmony_ci	 * However, it may not be unset of the MAC/VLAN task never actually
12262306a36Sopenharmony_ci	 * got a chance to run. Since we've canceled the task here, and it
12362306a36Sopenharmony_ci	 * cannot be rescheuled right now, we need to ensure the scheduled bit
12462306a36Sopenharmony_ci	 * gets unset.
12562306a36Sopenharmony_ci	 */
12662306a36Sopenharmony_ci	clear_bit(__FM10K_MACVLAN_SCHED, interface->state);
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/**
13062306a36Sopenharmony_ci * fm10k_resume_macvlan_task - Restart the MAC/VLAN queue monitor
13162306a36Sopenharmony_ci * @interface: fm10k private interface structure
13262306a36Sopenharmony_ci *
13362306a36Sopenharmony_ci * Clear the __FM10K_MACVLAN_DISABLE bit and, if a request occurred, schedule
13462306a36Sopenharmony_ci * the MAC/VLAN work monitor.
13562306a36Sopenharmony_ci */
13662306a36Sopenharmony_cistatic void fm10k_resume_macvlan_task(struct fm10k_intfc *interface)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	/* Re-enable the MAC/VLAN work item */
13962306a36Sopenharmony_ci	clear_bit(__FM10K_MACVLAN_DISABLE, interface->state);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	/* We might have received a MAC/VLAN request while disabled. If so,
14262306a36Sopenharmony_ci	 * kick off the queue now.
14362306a36Sopenharmony_ci	 */
14462306a36Sopenharmony_ci	if (test_bit(__FM10K_MACVLAN_REQUEST, interface->state))
14562306a36Sopenharmony_ci		fm10k_macvlan_schedule(interface);
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_civoid fm10k_service_event_schedule(struct fm10k_intfc *interface)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	if (!test_bit(__FM10K_SERVICE_DISABLE, interface->state) &&
15162306a36Sopenharmony_ci	    !test_and_set_bit(__FM10K_SERVICE_SCHED, interface->state)) {
15262306a36Sopenharmony_ci		clear_bit(__FM10K_SERVICE_REQUEST, interface->state);
15362306a36Sopenharmony_ci		queue_work(fm10k_workqueue, &interface->service_task);
15462306a36Sopenharmony_ci	} else {
15562306a36Sopenharmony_ci		set_bit(__FM10K_SERVICE_REQUEST, interface->state);
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic void fm10k_service_event_complete(struct fm10k_intfc *interface)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	WARN_ON(!test_bit(__FM10K_SERVICE_SCHED, interface->state));
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	/* flush memory to make sure state is correct before next watchog */
16462306a36Sopenharmony_ci	smp_mb__before_atomic();
16562306a36Sopenharmony_ci	clear_bit(__FM10K_SERVICE_SCHED, interface->state);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/* If a service event was requested since we started, immediately
16862306a36Sopenharmony_ci	 * re-schedule now. This ensures we don't drop a request until the
16962306a36Sopenharmony_ci	 * next timer event.
17062306a36Sopenharmony_ci	 */
17162306a36Sopenharmony_ci	if (test_bit(__FM10K_SERVICE_REQUEST, interface->state))
17262306a36Sopenharmony_ci		fm10k_service_event_schedule(interface);
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic void fm10k_stop_service_event(struct fm10k_intfc *interface)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	set_bit(__FM10K_SERVICE_DISABLE, interface->state);
17862306a36Sopenharmony_ci	cancel_work_sync(&interface->service_task);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/* It's possible that cancel_work_sync stopped the service task from
18162306a36Sopenharmony_ci	 * running before it could actually start. In this case the
18262306a36Sopenharmony_ci	 * __FM10K_SERVICE_SCHED bit will never be cleared. Since we know that
18362306a36Sopenharmony_ci	 * the service task cannot be running at this point, we need to clear
18462306a36Sopenharmony_ci	 * the scheduled bit, as otherwise the service task may never be
18562306a36Sopenharmony_ci	 * restarted.
18662306a36Sopenharmony_ci	 */
18762306a36Sopenharmony_ci	clear_bit(__FM10K_SERVICE_SCHED, interface->state);
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic void fm10k_start_service_event(struct fm10k_intfc *interface)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	clear_bit(__FM10K_SERVICE_DISABLE, interface->state);
19362306a36Sopenharmony_ci	fm10k_service_event_schedule(interface);
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci/**
19762306a36Sopenharmony_ci * fm10k_service_timer - Timer Call-back
19862306a36Sopenharmony_ci * @t: pointer to timer data
19962306a36Sopenharmony_ci **/
20062306a36Sopenharmony_cistatic void fm10k_service_timer(struct timer_list *t)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	struct fm10k_intfc *interface = from_timer(interface, t,
20362306a36Sopenharmony_ci						   service_timer);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	/* Reset the timer */
20662306a36Sopenharmony_ci	mod_timer(&interface->service_timer, (HZ * 2) + jiffies);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	fm10k_service_event_schedule(interface);
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci/**
21262306a36Sopenharmony_ci * fm10k_prepare_for_reset - Prepare the driver and device for a pending reset
21362306a36Sopenharmony_ci * @interface: fm10k private data structure
21462306a36Sopenharmony_ci *
21562306a36Sopenharmony_ci * This function prepares for a device reset by shutting as much down as we
21662306a36Sopenharmony_ci * can. It does nothing and returns false if __FM10K_RESETTING was already set
21762306a36Sopenharmony_ci * prior to calling this function. It returns true if it actually did work.
21862306a36Sopenharmony_ci */
21962306a36Sopenharmony_cistatic bool fm10k_prepare_for_reset(struct fm10k_intfc *interface)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	struct net_device *netdev = interface->netdev;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	/* put off any impending NetWatchDogTimeout */
22462306a36Sopenharmony_ci	netif_trans_update(netdev);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	/* Nothing to do if a reset is already in progress */
22762306a36Sopenharmony_ci	if (test_and_set_bit(__FM10K_RESETTING, interface->state))
22862306a36Sopenharmony_ci		return false;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	/* As the MAC/VLAN task will be accessing registers it must not be
23162306a36Sopenharmony_ci	 * running while we reset. Although the task will not be scheduled
23262306a36Sopenharmony_ci	 * once we start resetting it may already be running
23362306a36Sopenharmony_ci	 */
23462306a36Sopenharmony_ci	fm10k_stop_macvlan_task(interface);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	rtnl_lock();
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	fm10k_iov_suspend(interface->pdev);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (netif_running(netdev))
24162306a36Sopenharmony_ci		fm10k_close(netdev);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	fm10k_mbx_free_irq(interface);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	/* free interrupts */
24662306a36Sopenharmony_ci	fm10k_clear_queueing_scheme(interface);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	/* delay any future reset requests */
24962306a36Sopenharmony_ci	interface->last_reset = jiffies + (10 * HZ);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	rtnl_unlock();
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	return true;
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic int fm10k_handle_reset(struct fm10k_intfc *interface)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	struct net_device *netdev = interface->netdev;
25962306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
26062306a36Sopenharmony_ci	int err;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	WARN_ON(!test_bit(__FM10K_RESETTING, interface->state));
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	rtnl_lock();
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	pci_set_master(interface->pdev);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	/* reset and initialize the hardware so it is in a known state */
26962306a36Sopenharmony_ci	err = hw->mac.ops.reset_hw(hw);
27062306a36Sopenharmony_ci	if (err) {
27162306a36Sopenharmony_ci		dev_err(&interface->pdev->dev, "reset_hw failed: %d\n", err);
27262306a36Sopenharmony_ci		goto reinit_err;
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	err = hw->mac.ops.init_hw(hw);
27662306a36Sopenharmony_ci	if (err) {
27762306a36Sopenharmony_ci		dev_err(&interface->pdev->dev, "init_hw failed: %d\n", err);
27862306a36Sopenharmony_ci		goto reinit_err;
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	err = fm10k_init_queueing_scheme(interface);
28262306a36Sopenharmony_ci	if (err) {
28362306a36Sopenharmony_ci		dev_err(&interface->pdev->dev,
28462306a36Sopenharmony_ci			"init_queueing_scheme failed: %d\n", err);
28562306a36Sopenharmony_ci		goto reinit_err;
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	/* re-associate interrupts */
28962306a36Sopenharmony_ci	err = fm10k_mbx_request_irq(interface);
29062306a36Sopenharmony_ci	if (err)
29162306a36Sopenharmony_ci		goto err_mbx_irq;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	err = fm10k_hw_ready(interface);
29462306a36Sopenharmony_ci	if (err)
29562306a36Sopenharmony_ci		goto err_open;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	/* update hardware address for VFs if perm_addr has changed */
29862306a36Sopenharmony_ci	if (hw->mac.type == fm10k_mac_vf) {
29962306a36Sopenharmony_ci		if (is_valid_ether_addr(hw->mac.perm_addr)) {
30062306a36Sopenharmony_ci			ether_addr_copy(hw->mac.addr, hw->mac.perm_addr);
30162306a36Sopenharmony_ci			ether_addr_copy(netdev->perm_addr, hw->mac.perm_addr);
30262306a36Sopenharmony_ci			eth_hw_addr_set(netdev, hw->mac.perm_addr);
30362306a36Sopenharmony_ci			netdev->addr_assign_type &= ~NET_ADDR_RANDOM;
30462306a36Sopenharmony_ci		}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci		if (hw->mac.vlan_override)
30762306a36Sopenharmony_ci			netdev->features &= ~NETIF_F_HW_VLAN_CTAG_RX;
30862306a36Sopenharmony_ci		else
30962306a36Sopenharmony_ci			netdev->features |= NETIF_F_HW_VLAN_CTAG_RX;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	err = netif_running(netdev) ? fm10k_open(netdev) : 0;
31362306a36Sopenharmony_ci	if (err)
31462306a36Sopenharmony_ci		goto err_open;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	fm10k_iov_resume(interface->pdev);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	rtnl_unlock();
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	fm10k_resume_macvlan_task(interface);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	clear_bit(__FM10K_RESETTING, interface->state);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	return err;
32562306a36Sopenharmony_cierr_open:
32662306a36Sopenharmony_ci	fm10k_mbx_free_irq(interface);
32762306a36Sopenharmony_cierr_mbx_irq:
32862306a36Sopenharmony_ci	fm10k_clear_queueing_scheme(interface);
32962306a36Sopenharmony_cireinit_err:
33062306a36Sopenharmony_ci	netif_device_detach(netdev);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	rtnl_unlock();
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	clear_bit(__FM10K_RESETTING, interface->state);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	return err;
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic void fm10k_detach_subtask(struct fm10k_intfc *interface)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	struct net_device *netdev = interface->netdev;
34262306a36Sopenharmony_ci	u32 __iomem *hw_addr;
34362306a36Sopenharmony_ci	u32 value;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	/* do nothing if netdev is still present or hw_addr is set */
34662306a36Sopenharmony_ci	if (netif_device_present(netdev) || interface->hw.hw_addr)
34762306a36Sopenharmony_ci		return;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	/* We've lost the PCIe register space, and can no longer access the
35062306a36Sopenharmony_ci	 * device. Shut everything except the detach subtask down and prepare
35162306a36Sopenharmony_ci	 * to reset the device in case we recover. If we actually prepare for
35262306a36Sopenharmony_ci	 * reset, indicate that we're detached.
35362306a36Sopenharmony_ci	 */
35462306a36Sopenharmony_ci	if (fm10k_prepare_for_reset(interface))
35562306a36Sopenharmony_ci		set_bit(__FM10K_RESET_DETACHED, interface->state);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	/* check the real address space to see if we've recovered */
35862306a36Sopenharmony_ci	hw_addr = READ_ONCE(interface->uc_addr);
35962306a36Sopenharmony_ci	value = readl(hw_addr);
36062306a36Sopenharmony_ci	if (~value) {
36162306a36Sopenharmony_ci		int err;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci		/* Make sure the reset was initiated because we detached,
36462306a36Sopenharmony_ci		 * otherwise we might race with a different reset flow.
36562306a36Sopenharmony_ci		 */
36662306a36Sopenharmony_ci		if (!test_and_clear_bit(__FM10K_RESET_DETACHED,
36762306a36Sopenharmony_ci					interface->state))
36862306a36Sopenharmony_ci			return;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci		/* Restore the hardware address */
37162306a36Sopenharmony_ci		interface->hw.hw_addr = interface->uc_addr;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci		/* PCIe link has been restored, and the device is active
37462306a36Sopenharmony_ci		 * again. Restore everything and reset the device.
37562306a36Sopenharmony_ci		 */
37662306a36Sopenharmony_ci		err = fm10k_handle_reset(interface);
37762306a36Sopenharmony_ci		if (err) {
37862306a36Sopenharmony_ci			netdev_err(netdev, "Unable to reset device: %d\n", err);
37962306a36Sopenharmony_ci			interface->hw.hw_addr = NULL;
38062306a36Sopenharmony_ci			return;
38162306a36Sopenharmony_ci		}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci		/* Re-attach the netdev */
38462306a36Sopenharmony_ci		netif_device_attach(netdev);
38562306a36Sopenharmony_ci		netdev_warn(netdev, "PCIe link restored, device now attached\n");
38662306a36Sopenharmony_ci		return;
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_cistatic void fm10k_reset_subtask(struct fm10k_intfc *interface)
39162306a36Sopenharmony_ci{
39262306a36Sopenharmony_ci	int err;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	if (!test_and_clear_bit(FM10K_FLAG_RESET_REQUESTED,
39562306a36Sopenharmony_ci				interface->flags))
39662306a36Sopenharmony_ci		return;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	/* If another thread has already prepared to reset the device, we
39962306a36Sopenharmony_ci	 * should not attempt to handle a reset here, since we'd race with
40062306a36Sopenharmony_ci	 * that thread. This may happen if we suspend the device or if the
40162306a36Sopenharmony_ci	 * PCIe link is lost. In this case, we'll just ignore the RESET
40262306a36Sopenharmony_ci	 * request, as it will (eventually) be taken care of when the thread
40362306a36Sopenharmony_ci	 * which actually started the reset is finished.
40462306a36Sopenharmony_ci	 */
40562306a36Sopenharmony_ci	if (!fm10k_prepare_for_reset(interface))
40662306a36Sopenharmony_ci		return;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	netdev_err(interface->netdev, "Reset interface\n");
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	err = fm10k_handle_reset(interface);
41162306a36Sopenharmony_ci	if (err)
41262306a36Sopenharmony_ci		dev_err(&interface->pdev->dev,
41362306a36Sopenharmony_ci			"fm10k_handle_reset failed: %d\n", err);
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci/**
41762306a36Sopenharmony_ci * fm10k_configure_swpri_map - Configure Receive SWPRI to PC mapping
41862306a36Sopenharmony_ci * @interface: board private structure
41962306a36Sopenharmony_ci *
42062306a36Sopenharmony_ci * Configure the SWPRI to PC mapping for the port.
42162306a36Sopenharmony_ci **/
42262306a36Sopenharmony_cistatic void fm10k_configure_swpri_map(struct fm10k_intfc *interface)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	struct net_device *netdev = interface->netdev;
42562306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
42662306a36Sopenharmony_ci	int i;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	/* clear flag indicating update is needed */
42962306a36Sopenharmony_ci	clear_bit(FM10K_FLAG_SWPRI_CONFIG, interface->flags);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	/* these registers are only available on the PF */
43262306a36Sopenharmony_ci	if (hw->mac.type != fm10k_mac_pf)
43362306a36Sopenharmony_ci		return;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	/* configure SWPRI to PC map */
43662306a36Sopenharmony_ci	for (i = 0; i < FM10K_SWPRI_MAX; i++)
43762306a36Sopenharmony_ci		fm10k_write_reg(hw, FM10K_SWPRI_MAP(i),
43862306a36Sopenharmony_ci				netdev_get_prio_tc_map(netdev, i));
43962306a36Sopenharmony_ci}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci/**
44262306a36Sopenharmony_ci * fm10k_watchdog_update_host_state - Update the link status based on host.
44362306a36Sopenharmony_ci * @interface: board private structure
44462306a36Sopenharmony_ci **/
44562306a36Sopenharmony_cistatic void fm10k_watchdog_update_host_state(struct fm10k_intfc *interface)
44662306a36Sopenharmony_ci{
44762306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
44862306a36Sopenharmony_ci	s32 err;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	if (test_bit(__FM10K_LINK_DOWN, interface->state)) {
45162306a36Sopenharmony_ci		interface->host_ready = false;
45262306a36Sopenharmony_ci		if (time_is_after_jiffies(interface->link_down_event))
45362306a36Sopenharmony_ci			return;
45462306a36Sopenharmony_ci		clear_bit(__FM10K_LINK_DOWN, interface->state);
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	if (test_bit(FM10K_FLAG_SWPRI_CONFIG, interface->flags)) {
45862306a36Sopenharmony_ci		if (rtnl_trylock()) {
45962306a36Sopenharmony_ci			fm10k_configure_swpri_map(interface);
46062306a36Sopenharmony_ci			rtnl_unlock();
46162306a36Sopenharmony_ci		}
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	/* lock the mailbox for transmit and receive */
46562306a36Sopenharmony_ci	fm10k_mbx_lock(interface);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	err = hw->mac.ops.get_host_state(hw, &interface->host_ready);
46862306a36Sopenharmony_ci	if (err && time_is_before_jiffies(interface->last_reset))
46962306a36Sopenharmony_ci		set_bit(FM10K_FLAG_RESET_REQUESTED, interface->flags);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	/* free the lock */
47262306a36Sopenharmony_ci	fm10k_mbx_unlock(interface);
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci/**
47662306a36Sopenharmony_ci * fm10k_mbx_subtask - Process upstream and downstream mailboxes
47762306a36Sopenharmony_ci * @interface: board private structure
47862306a36Sopenharmony_ci *
47962306a36Sopenharmony_ci * This function will process both the upstream and downstream mailboxes.
48062306a36Sopenharmony_ci **/
48162306a36Sopenharmony_cistatic void fm10k_mbx_subtask(struct fm10k_intfc *interface)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci	/* If we're resetting, bail out */
48462306a36Sopenharmony_ci	if (test_bit(__FM10K_RESETTING, interface->state))
48562306a36Sopenharmony_ci		return;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	/* process upstream mailbox and update device state */
48862306a36Sopenharmony_ci	fm10k_watchdog_update_host_state(interface);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	/* process downstream mailboxes */
49162306a36Sopenharmony_ci	fm10k_iov_mbx(interface);
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci/**
49562306a36Sopenharmony_ci * fm10k_watchdog_host_is_ready - Update netdev status based on host ready
49662306a36Sopenharmony_ci * @interface: board private structure
49762306a36Sopenharmony_ci **/
49862306a36Sopenharmony_cistatic void fm10k_watchdog_host_is_ready(struct fm10k_intfc *interface)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	struct net_device *netdev = interface->netdev;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	/* only continue if link state is currently down */
50362306a36Sopenharmony_ci	if (netif_carrier_ok(netdev))
50462306a36Sopenharmony_ci		return;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	netif_info(interface, drv, netdev, "NIC Link is up\n");
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	netif_carrier_on(netdev);
50962306a36Sopenharmony_ci	netif_tx_wake_all_queues(netdev);
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci/**
51362306a36Sopenharmony_ci * fm10k_watchdog_host_not_ready - Update netdev status based on host not ready
51462306a36Sopenharmony_ci * @interface: board private structure
51562306a36Sopenharmony_ci **/
51662306a36Sopenharmony_cistatic void fm10k_watchdog_host_not_ready(struct fm10k_intfc *interface)
51762306a36Sopenharmony_ci{
51862306a36Sopenharmony_ci	struct net_device *netdev = interface->netdev;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	/* only continue if link state is currently up */
52162306a36Sopenharmony_ci	if (!netif_carrier_ok(netdev))
52262306a36Sopenharmony_ci		return;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	netif_info(interface, drv, netdev, "NIC Link is down\n");
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	netif_carrier_off(netdev);
52762306a36Sopenharmony_ci	netif_tx_stop_all_queues(netdev);
52862306a36Sopenharmony_ci}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci/**
53162306a36Sopenharmony_ci * fm10k_update_stats - Update the board statistics counters.
53262306a36Sopenharmony_ci * @interface: board private structure
53362306a36Sopenharmony_ci **/
53462306a36Sopenharmony_civoid fm10k_update_stats(struct fm10k_intfc *interface)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	struct net_device_stats *net_stats = &interface->netdev->stats;
53762306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
53862306a36Sopenharmony_ci	u64 hw_csum_tx_good = 0, hw_csum_rx_good = 0, rx_length_errors = 0;
53962306a36Sopenharmony_ci	u64 rx_switch_errors = 0, rx_drops = 0, rx_pp_errors = 0;
54062306a36Sopenharmony_ci	u64 rx_link_errors = 0;
54162306a36Sopenharmony_ci	u64 rx_errors = 0, rx_csum_errors = 0, tx_csum_errors = 0;
54262306a36Sopenharmony_ci	u64 restart_queue = 0, tx_busy = 0, alloc_failed = 0;
54362306a36Sopenharmony_ci	u64 rx_bytes_nic = 0, rx_pkts_nic = 0, rx_drops_nic = 0;
54462306a36Sopenharmony_ci	u64 tx_bytes_nic = 0, tx_pkts_nic = 0;
54562306a36Sopenharmony_ci	u64 bytes, pkts;
54662306a36Sopenharmony_ci	int i;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	/* ensure only one thread updates stats at a time */
54962306a36Sopenharmony_ci	if (test_and_set_bit(__FM10K_UPDATING_STATS, interface->state))
55062306a36Sopenharmony_ci		return;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	/* do not allow stats update via service task for next second */
55362306a36Sopenharmony_ci	interface->next_stats_update = jiffies + HZ;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	/* gather some stats to the interface struct that are per queue */
55662306a36Sopenharmony_ci	for (bytes = 0, pkts = 0, i = 0; i < interface->num_tx_queues; i++) {
55762306a36Sopenharmony_ci		struct fm10k_ring *tx_ring = READ_ONCE(interface->tx_ring[i]);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci		if (!tx_ring)
56062306a36Sopenharmony_ci			continue;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci		restart_queue += tx_ring->tx_stats.restart_queue;
56362306a36Sopenharmony_ci		tx_busy += tx_ring->tx_stats.tx_busy;
56462306a36Sopenharmony_ci		tx_csum_errors += tx_ring->tx_stats.csum_err;
56562306a36Sopenharmony_ci		bytes += tx_ring->stats.bytes;
56662306a36Sopenharmony_ci		pkts += tx_ring->stats.packets;
56762306a36Sopenharmony_ci		hw_csum_tx_good += tx_ring->tx_stats.csum_good;
56862306a36Sopenharmony_ci	}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	interface->restart_queue = restart_queue;
57162306a36Sopenharmony_ci	interface->tx_busy = tx_busy;
57262306a36Sopenharmony_ci	net_stats->tx_bytes = bytes;
57362306a36Sopenharmony_ci	net_stats->tx_packets = pkts;
57462306a36Sopenharmony_ci	interface->tx_csum_errors = tx_csum_errors;
57562306a36Sopenharmony_ci	interface->hw_csum_tx_good = hw_csum_tx_good;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	/* gather some stats to the interface struct that are per queue */
57862306a36Sopenharmony_ci	for (bytes = 0, pkts = 0, i = 0; i < interface->num_rx_queues; i++) {
57962306a36Sopenharmony_ci		struct fm10k_ring *rx_ring = READ_ONCE(interface->rx_ring[i]);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci		if (!rx_ring)
58262306a36Sopenharmony_ci			continue;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci		bytes += rx_ring->stats.bytes;
58562306a36Sopenharmony_ci		pkts += rx_ring->stats.packets;
58662306a36Sopenharmony_ci		alloc_failed += rx_ring->rx_stats.alloc_failed;
58762306a36Sopenharmony_ci		rx_csum_errors += rx_ring->rx_stats.csum_err;
58862306a36Sopenharmony_ci		rx_errors += rx_ring->rx_stats.errors;
58962306a36Sopenharmony_ci		hw_csum_rx_good += rx_ring->rx_stats.csum_good;
59062306a36Sopenharmony_ci		rx_switch_errors += rx_ring->rx_stats.switch_errors;
59162306a36Sopenharmony_ci		rx_drops += rx_ring->rx_stats.drops;
59262306a36Sopenharmony_ci		rx_pp_errors += rx_ring->rx_stats.pp_errors;
59362306a36Sopenharmony_ci		rx_link_errors += rx_ring->rx_stats.link_errors;
59462306a36Sopenharmony_ci		rx_length_errors += rx_ring->rx_stats.length_errors;
59562306a36Sopenharmony_ci	}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	net_stats->rx_bytes = bytes;
59862306a36Sopenharmony_ci	net_stats->rx_packets = pkts;
59962306a36Sopenharmony_ci	interface->alloc_failed = alloc_failed;
60062306a36Sopenharmony_ci	interface->rx_csum_errors = rx_csum_errors;
60162306a36Sopenharmony_ci	interface->hw_csum_rx_good = hw_csum_rx_good;
60262306a36Sopenharmony_ci	interface->rx_switch_errors = rx_switch_errors;
60362306a36Sopenharmony_ci	interface->rx_drops = rx_drops;
60462306a36Sopenharmony_ci	interface->rx_pp_errors = rx_pp_errors;
60562306a36Sopenharmony_ci	interface->rx_link_errors = rx_link_errors;
60662306a36Sopenharmony_ci	interface->rx_length_errors = rx_length_errors;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	hw->mac.ops.update_hw_stats(hw, &interface->stats);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	for (i = 0; i < hw->mac.max_queues; i++) {
61162306a36Sopenharmony_ci		struct fm10k_hw_stats_q *q = &interface->stats.q[i];
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci		tx_bytes_nic += q->tx_bytes.count;
61462306a36Sopenharmony_ci		tx_pkts_nic += q->tx_packets.count;
61562306a36Sopenharmony_ci		rx_bytes_nic += q->rx_bytes.count;
61662306a36Sopenharmony_ci		rx_pkts_nic += q->rx_packets.count;
61762306a36Sopenharmony_ci		rx_drops_nic += q->rx_drops.count;
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	interface->tx_bytes_nic = tx_bytes_nic;
62162306a36Sopenharmony_ci	interface->tx_packets_nic = tx_pkts_nic;
62262306a36Sopenharmony_ci	interface->rx_bytes_nic = rx_bytes_nic;
62362306a36Sopenharmony_ci	interface->rx_packets_nic = rx_pkts_nic;
62462306a36Sopenharmony_ci	interface->rx_drops_nic = rx_drops_nic;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	/* Fill out the OS statistics structure */
62762306a36Sopenharmony_ci	net_stats->rx_errors = rx_errors;
62862306a36Sopenharmony_ci	net_stats->rx_dropped = interface->stats.nodesc_drop.count;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	/* Update VF statistics */
63162306a36Sopenharmony_ci	fm10k_iov_update_stats(interface);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	clear_bit(__FM10K_UPDATING_STATS, interface->state);
63462306a36Sopenharmony_ci}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci/**
63762306a36Sopenharmony_ci * fm10k_watchdog_flush_tx - flush queues on host not ready
63862306a36Sopenharmony_ci * @interface: pointer to the device interface structure
63962306a36Sopenharmony_ci **/
64062306a36Sopenharmony_cistatic void fm10k_watchdog_flush_tx(struct fm10k_intfc *interface)
64162306a36Sopenharmony_ci{
64262306a36Sopenharmony_ci	int some_tx_pending = 0;
64362306a36Sopenharmony_ci	int i;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	/* nothing to do if carrier is up */
64662306a36Sopenharmony_ci	if (netif_carrier_ok(interface->netdev))
64762306a36Sopenharmony_ci		return;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	for (i = 0; i < interface->num_tx_queues; i++) {
65062306a36Sopenharmony_ci		struct fm10k_ring *tx_ring = interface->tx_ring[i];
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci		if (tx_ring->next_to_use != tx_ring->next_to_clean) {
65362306a36Sopenharmony_ci			some_tx_pending = 1;
65462306a36Sopenharmony_ci			break;
65562306a36Sopenharmony_ci		}
65662306a36Sopenharmony_ci	}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	/* We've lost link, so the controller stops DMA, but we've got
65962306a36Sopenharmony_ci	 * queued Tx work that's never going to get done, so reset
66062306a36Sopenharmony_ci	 * controller to flush Tx.
66162306a36Sopenharmony_ci	 */
66262306a36Sopenharmony_ci	if (some_tx_pending)
66362306a36Sopenharmony_ci		set_bit(FM10K_FLAG_RESET_REQUESTED, interface->flags);
66462306a36Sopenharmony_ci}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci/**
66762306a36Sopenharmony_ci * fm10k_watchdog_subtask - check and bring link up
66862306a36Sopenharmony_ci * @interface: pointer to the device interface structure
66962306a36Sopenharmony_ci **/
67062306a36Sopenharmony_cistatic void fm10k_watchdog_subtask(struct fm10k_intfc *interface)
67162306a36Sopenharmony_ci{
67262306a36Sopenharmony_ci	/* if interface is down do nothing */
67362306a36Sopenharmony_ci	if (test_bit(__FM10K_DOWN, interface->state) ||
67462306a36Sopenharmony_ci	    test_bit(__FM10K_RESETTING, interface->state))
67562306a36Sopenharmony_ci		return;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	if (interface->host_ready)
67862306a36Sopenharmony_ci		fm10k_watchdog_host_is_ready(interface);
67962306a36Sopenharmony_ci	else
68062306a36Sopenharmony_ci		fm10k_watchdog_host_not_ready(interface);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	/* update stats only once every second */
68362306a36Sopenharmony_ci	if (time_is_before_jiffies(interface->next_stats_update))
68462306a36Sopenharmony_ci		fm10k_update_stats(interface);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	/* flush any uncompleted work */
68762306a36Sopenharmony_ci	fm10k_watchdog_flush_tx(interface);
68862306a36Sopenharmony_ci}
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci/**
69162306a36Sopenharmony_ci * fm10k_check_hang_subtask - check for hung queues and dropped interrupts
69262306a36Sopenharmony_ci * @interface: pointer to the device interface structure
69362306a36Sopenharmony_ci *
69462306a36Sopenharmony_ci * This function serves two purposes.  First it strobes the interrupt lines
69562306a36Sopenharmony_ci * in order to make certain interrupts are occurring.  Secondly it sets the
69662306a36Sopenharmony_ci * bits needed to check for TX hangs.  As a result we should immediately
69762306a36Sopenharmony_ci * determine if a hang has occurred.
69862306a36Sopenharmony_ci */
69962306a36Sopenharmony_cistatic void fm10k_check_hang_subtask(struct fm10k_intfc *interface)
70062306a36Sopenharmony_ci{
70162306a36Sopenharmony_ci	/* If we're down or resetting, just bail */
70262306a36Sopenharmony_ci	if (test_bit(__FM10K_DOWN, interface->state) ||
70362306a36Sopenharmony_ci	    test_bit(__FM10K_RESETTING, interface->state))
70462306a36Sopenharmony_ci		return;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	/* rate limit tx hang checks to only once every 2 seconds */
70762306a36Sopenharmony_ci	if (time_is_after_eq_jiffies(interface->next_tx_hang_check))
70862306a36Sopenharmony_ci		return;
70962306a36Sopenharmony_ci	interface->next_tx_hang_check = jiffies + (2 * HZ);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	if (netif_carrier_ok(interface->netdev)) {
71262306a36Sopenharmony_ci		int i;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci		/* Force detection of hung controller */
71562306a36Sopenharmony_ci		for (i = 0; i < interface->num_tx_queues; i++)
71662306a36Sopenharmony_ci			set_check_for_tx_hang(interface->tx_ring[i]);
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci		/* Rearm all in-use q_vectors for immediate firing */
71962306a36Sopenharmony_ci		for (i = 0; i < interface->num_q_vectors; i++) {
72062306a36Sopenharmony_ci			struct fm10k_q_vector *qv = interface->q_vector[i];
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci			if (!qv->tx.count && !qv->rx.count)
72362306a36Sopenharmony_ci				continue;
72462306a36Sopenharmony_ci			writel(FM10K_ITR_ENABLE | FM10K_ITR_PENDING2, qv->itr);
72562306a36Sopenharmony_ci		}
72662306a36Sopenharmony_ci	}
72762306a36Sopenharmony_ci}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci/**
73062306a36Sopenharmony_ci * fm10k_service_task - manages and runs subtasks
73162306a36Sopenharmony_ci * @work: pointer to work_struct containing our data
73262306a36Sopenharmony_ci **/
73362306a36Sopenharmony_cistatic void fm10k_service_task(struct work_struct *work)
73462306a36Sopenharmony_ci{
73562306a36Sopenharmony_ci	struct fm10k_intfc *interface;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	interface = container_of(work, struct fm10k_intfc, service_task);
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	/* Check whether we're detached first */
74062306a36Sopenharmony_ci	fm10k_detach_subtask(interface);
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	/* tasks run even when interface is down */
74362306a36Sopenharmony_ci	fm10k_mbx_subtask(interface);
74462306a36Sopenharmony_ci	fm10k_reset_subtask(interface);
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	/* tasks only run when interface is up */
74762306a36Sopenharmony_ci	fm10k_watchdog_subtask(interface);
74862306a36Sopenharmony_ci	fm10k_check_hang_subtask(interface);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	/* release lock on service events to allow scheduling next event */
75162306a36Sopenharmony_ci	fm10k_service_event_complete(interface);
75262306a36Sopenharmony_ci}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci/**
75562306a36Sopenharmony_ci * fm10k_macvlan_task - send queued MAC/VLAN requests to switch manager
75662306a36Sopenharmony_ci * @work: pointer to work_struct containing our data
75762306a36Sopenharmony_ci *
75862306a36Sopenharmony_ci * This work item handles sending MAC/VLAN updates to the switch manager. When
75962306a36Sopenharmony_ci * the interface is up, it will attempt to queue mailbox messages to the
76062306a36Sopenharmony_ci * switch manager requesting updates for MAC/VLAN pairs. If the Tx fifo of the
76162306a36Sopenharmony_ci * mailbox is full, it will reschedule itself to try again in a short while.
76262306a36Sopenharmony_ci * This ensures that the driver does not overload the switch mailbox with too
76362306a36Sopenharmony_ci * many simultaneous requests, causing an unnecessary reset.
76462306a36Sopenharmony_ci **/
76562306a36Sopenharmony_cistatic void fm10k_macvlan_task(struct work_struct *work)
76662306a36Sopenharmony_ci{
76762306a36Sopenharmony_ci	struct fm10k_macvlan_request *item;
76862306a36Sopenharmony_ci	struct fm10k_intfc *interface;
76962306a36Sopenharmony_ci	struct delayed_work *dwork;
77062306a36Sopenharmony_ci	struct list_head *requests;
77162306a36Sopenharmony_ci	struct fm10k_hw *hw;
77262306a36Sopenharmony_ci	unsigned long flags;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	dwork = to_delayed_work(work);
77562306a36Sopenharmony_ci	interface = container_of(dwork, struct fm10k_intfc, macvlan_task);
77662306a36Sopenharmony_ci	hw = &interface->hw;
77762306a36Sopenharmony_ci	requests = &interface->macvlan_requests;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	do {
78062306a36Sopenharmony_ci		/* Pop the first item off the list */
78162306a36Sopenharmony_ci		spin_lock_irqsave(&interface->macvlan_lock, flags);
78262306a36Sopenharmony_ci		item = list_first_entry_or_null(requests,
78362306a36Sopenharmony_ci						struct fm10k_macvlan_request,
78462306a36Sopenharmony_ci						list);
78562306a36Sopenharmony_ci		if (item)
78662306a36Sopenharmony_ci			list_del_init(&item->list);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci		spin_unlock_irqrestore(&interface->macvlan_lock, flags);
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci		/* We have no more items to process */
79162306a36Sopenharmony_ci		if (!item)
79262306a36Sopenharmony_ci			goto done;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci		fm10k_mbx_lock(interface);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci		/* Check that we have plenty of space to send the message. We
79762306a36Sopenharmony_ci		 * want to ensure that the mailbox stays low enough to avoid a
79862306a36Sopenharmony_ci		 * change in the host state, otherwise we may see spurious
79962306a36Sopenharmony_ci		 * link up / link down notifications.
80062306a36Sopenharmony_ci		 */
80162306a36Sopenharmony_ci		if (!hw->mbx.ops.tx_ready(&hw->mbx, FM10K_VFMBX_MSG_MTU + 5)) {
80262306a36Sopenharmony_ci			hw->mbx.ops.process(hw, &hw->mbx);
80362306a36Sopenharmony_ci			set_bit(__FM10K_MACVLAN_REQUEST, interface->state);
80462306a36Sopenharmony_ci			fm10k_mbx_unlock(interface);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci			/* Put the request back on the list */
80762306a36Sopenharmony_ci			spin_lock_irqsave(&interface->macvlan_lock, flags);
80862306a36Sopenharmony_ci			list_add(&item->list, requests);
80962306a36Sopenharmony_ci			spin_unlock_irqrestore(&interface->macvlan_lock, flags);
81062306a36Sopenharmony_ci			break;
81162306a36Sopenharmony_ci		}
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci		switch (item->type) {
81462306a36Sopenharmony_ci		case FM10K_MC_MAC_REQUEST:
81562306a36Sopenharmony_ci			hw->mac.ops.update_mc_addr(hw,
81662306a36Sopenharmony_ci						   item->mac.glort,
81762306a36Sopenharmony_ci						   item->mac.addr,
81862306a36Sopenharmony_ci						   item->mac.vid,
81962306a36Sopenharmony_ci						   item->set);
82062306a36Sopenharmony_ci			break;
82162306a36Sopenharmony_ci		case FM10K_UC_MAC_REQUEST:
82262306a36Sopenharmony_ci			hw->mac.ops.update_uc_addr(hw,
82362306a36Sopenharmony_ci						   item->mac.glort,
82462306a36Sopenharmony_ci						   item->mac.addr,
82562306a36Sopenharmony_ci						   item->mac.vid,
82662306a36Sopenharmony_ci						   item->set,
82762306a36Sopenharmony_ci						   0);
82862306a36Sopenharmony_ci			break;
82962306a36Sopenharmony_ci		case FM10K_VLAN_REQUEST:
83062306a36Sopenharmony_ci			hw->mac.ops.update_vlan(hw,
83162306a36Sopenharmony_ci						item->vlan.vid,
83262306a36Sopenharmony_ci						item->vlan.vsi,
83362306a36Sopenharmony_ci						item->set);
83462306a36Sopenharmony_ci			break;
83562306a36Sopenharmony_ci		default:
83662306a36Sopenharmony_ci			break;
83762306a36Sopenharmony_ci		}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci		fm10k_mbx_unlock(interface);
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci		/* Free the item now that we've sent the update */
84262306a36Sopenharmony_ci		kfree(item);
84362306a36Sopenharmony_ci	} while (true);
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_cidone:
84662306a36Sopenharmony_ci	WARN_ON(!test_bit(__FM10K_MACVLAN_SCHED, interface->state));
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	/* flush memory to make sure state is correct */
84962306a36Sopenharmony_ci	smp_mb__before_atomic();
85062306a36Sopenharmony_ci	clear_bit(__FM10K_MACVLAN_SCHED, interface->state);
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	/* If a MAC/VLAN request was scheduled since we started, we should
85362306a36Sopenharmony_ci	 * re-schedule. However, there is no reason to re-schedule if there is
85462306a36Sopenharmony_ci	 * no work to do.
85562306a36Sopenharmony_ci	 */
85662306a36Sopenharmony_ci	if (test_bit(__FM10K_MACVLAN_REQUEST, interface->state))
85762306a36Sopenharmony_ci		fm10k_macvlan_schedule(interface);
85862306a36Sopenharmony_ci}
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci/**
86162306a36Sopenharmony_ci * fm10k_configure_tx_ring - Configure Tx ring after Reset
86262306a36Sopenharmony_ci * @interface: board private structure
86362306a36Sopenharmony_ci * @ring: structure containing ring specific data
86462306a36Sopenharmony_ci *
86562306a36Sopenharmony_ci * Configure the Tx descriptor ring after a reset.
86662306a36Sopenharmony_ci **/
86762306a36Sopenharmony_cistatic void fm10k_configure_tx_ring(struct fm10k_intfc *interface,
86862306a36Sopenharmony_ci				    struct fm10k_ring *ring)
86962306a36Sopenharmony_ci{
87062306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
87162306a36Sopenharmony_ci	u64 tdba = ring->dma;
87262306a36Sopenharmony_ci	u32 size = ring->count * sizeof(struct fm10k_tx_desc);
87362306a36Sopenharmony_ci	u32 txint = FM10K_INT_MAP_DISABLE;
87462306a36Sopenharmony_ci	u32 txdctl = BIT(FM10K_TXDCTL_MAX_TIME_SHIFT) | FM10K_TXDCTL_ENABLE;
87562306a36Sopenharmony_ci	u8 reg_idx = ring->reg_idx;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	/* disable queue to avoid issues while updating state */
87862306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_TXDCTL(reg_idx), 0);
87962306a36Sopenharmony_ci	fm10k_write_flush(hw);
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	/* possible poll here to verify ring resources have been cleaned */
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	/* set location and size for descriptor ring */
88462306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_TDBAL(reg_idx), tdba & DMA_BIT_MASK(32));
88562306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_TDBAH(reg_idx), tdba >> 32);
88662306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_TDLEN(reg_idx), size);
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	/* reset head and tail pointers */
88962306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_TDH(reg_idx), 0);
89062306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_TDT(reg_idx), 0);
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	/* store tail pointer */
89362306a36Sopenharmony_ci	ring->tail = &interface->uc_addr[FM10K_TDT(reg_idx)];
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	/* reset ntu and ntc to place SW in sync with hardware */
89662306a36Sopenharmony_ci	ring->next_to_clean = 0;
89762306a36Sopenharmony_ci	ring->next_to_use = 0;
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	/* Map interrupt */
90062306a36Sopenharmony_ci	if (ring->q_vector) {
90162306a36Sopenharmony_ci		txint = ring->q_vector->v_idx + NON_Q_VECTORS;
90262306a36Sopenharmony_ci		txint |= FM10K_INT_MAP_TIMER0;
90362306a36Sopenharmony_ci	}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_TXINT(reg_idx), txint);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	/* enable use of FTAG bit in Tx descriptor, register is RO for VF */
90862306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_PFVTCTL(reg_idx),
90962306a36Sopenharmony_ci			FM10K_PFVTCTL_FTAG_DESC_ENABLE);
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	/* Initialize XPS */
91262306a36Sopenharmony_ci	if (!test_and_set_bit(__FM10K_TX_XPS_INIT_DONE, ring->state) &&
91362306a36Sopenharmony_ci	    ring->q_vector)
91462306a36Sopenharmony_ci		netif_set_xps_queue(ring->netdev,
91562306a36Sopenharmony_ci				    &ring->q_vector->affinity_mask,
91662306a36Sopenharmony_ci				    ring->queue_index);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	/* enable queue */
91962306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_TXDCTL(reg_idx), txdctl);
92062306a36Sopenharmony_ci}
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci/**
92362306a36Sopenharmony_ci * fm10k_enable_tx_ring - Verify Tx ring is enabled after configuration
92462306a36Sopenharmony_ci * @interface: board private structure
92562306a36Sopenharmony_ci * @ring: structure containing ring specific data
92662306a36Sopenharmony_ci *
92762306a36Sopenharmony_ci * Verify the Tx descriptor ring is ready for transmit.
92862306a36Sopenharmony_ci **/
92962306a36Sopenharmony_cistatic void fm10k_enable_tx_ring(struct fm10k_intfc *interface,
93062306a36Sopenharmony_ci				 struct fm10k_ring *ring)
93162306a36Sopenharmony_ci{
93262306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
93362306a36Sopenharmony_ci	int wait_loop = 10;
93462306a36Sopenharmony_ci	u32 txdctl;
93562306a36Sopenharmony_ci	u8 reg_idx = ring->reg_idx;
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	/* if we are already enabled just exit */
93862306a36Sopenharmony_ci	if (fm10k_read_reg(hw, FM10K_TXDCTL(reg_idx)) & FM10K_TXDCTL_ENABLE)
93962306a36Sopenharmony_ci		return;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	/* poll to verify queue is enabled */
94262306a36Sopenharmony_ci	do {
94362306a36Sopenharmony_ci		usleep_range(1000, 2000);
94462306a36Sopenharmony_ci		txdctl = fm10k_read_reg(hw, FM10K_TXDCTL(reg_idx));
94562306a36Sopenharmony_ci	} while (!(txdctl & FM10K_TXDCTL_ENABLE) && --wait_loop);
94662306a36Sopenharmony_ci	if (!wait_loop)
94762306a36Sopenharmony_ci		netif_err(interface, drv, interface->netdev,
94862306a36Sopenharmony_ci			  "Could not enable Tx Queue %d\n", reg_idx);
94962306a36Sopenharmony_ci}
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci/**
95262306a36Sopenharmony_ci * fm10k_configure_tx - Configure Transmit Unit after Reset
95362306a36Sopenharmony_ci * @interface: board private structure
95462306a36Sopenharmony_ci *
95562306a36Sopenharmony_ci * Configure the Tx unit of the MAC after a reset.
95662306a36Sopenharmony_ci **/
95762306a36Sopenharmony_cistatic void fm10k_configure_tx(struct fm10k_intfc *interface)
95862306a36Sopenharmony_ci{
95962306a36Sopenharmony_ci	int i;
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	/* Setup the HW Tx Head and Tail descriptor pointers */
96262306a36Sopenharmony_ci	for (i = 0; i < interface->num_tx_queues; i++)
96362306a36Sopenharmony_ci		fm10k_configure_tx_ring(interface, interface->tx_ring[i]);
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	/* poll here to verify that Tx rings are now enabled */
96662306a36Sopenharmony_ci	for (i = 0; i < interface->num_tx_queues; i++)
96762306a36Sopenharmony_ci		fm10k_enable_tx_ring(interface, interface->tx_ring[i]);
96862306a36Sopenharmony_ci}
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci/**
97162306a36Sopenharmony_ci * fm10k_configure_rx_ring - Configure Rx ring after Reset
97262306a36Sopenharmony_ci * @interface: board private structure
97362306a36Sopenharmony_ci * @ring: structure containing ring specific data
97462306a36Sopenharmony_ci *
97562306a36Sopenharmony_ci * Configure the Rx descriptor ring after a reset.
97662306a36Sopenharmony_ci **/
97762306a36Sopenharmony_cistatic void fm10k_configure_rx_ring(struct fm10k_intfc *interface,
97862306a36Sopenharmony_ci				    struct fm10k_ring *ring)
97962306a36Sopenharmony_ci{
98062306a36Sopenharmony_ci	u64 rdba = ring->dma;
98162306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
98262306a36Sopenharmony_ci	u32 size = ring->count * sizeof(union fm10k_rx_desc);
98362306a36Sopenharmony_ci	u32 rxqctl, rxdctl = FM10K_RXDCTL_WRITE_BACK_MIN_DELAY;
98462306a36Sopenharmony_ci	u32 srrctl = FM10K_SRRCTL_BUFFER_CHAINING_EN;
98562306a36Sopenharmony_ci	u32 rxint = FM10K_INT_MAP_DISABLE;
98662306a36Sopenharmony_ci	u8 rx_pause = interface->rx_pause;
98762306a36Sopenharmony_ci	u8 reg_idx = ring->reg_idx;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	/* disable queue to avoid issues while updating state */
99062306a36Sopenharmony_ci	rxqctl = fm10k_read_reg(hw, FM10K_RXQCTL(reg_idx));
99162306a36Sopenharmony_ci	rxqctl &= ~FM10K_RXQCTL_ENABLE;
99262306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_RXQCTL(reg_idx), rxqctl);
99362306a36Sopenharmony_ci	fm10k_write_flush(hw);
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	/* possible poll here to verify ring resources have been cleaned */
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	/* set location and size for descriptor ring */
99862306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_RDBAL(reg_idx), rdba & DMA_BIT_MASK(32));
99962306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_RDBAH(reg_idx), rdba >> 32);
100062306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_RDLEN(reg_idx), size);
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	/* reset head and tail pointers */
100362306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_RDH(reg_idx), 0);
100462306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_RDT(reg_idx), 0);
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	/* store tail pointer */
100762306a36Sopenharmony_ci	ring->tail = &interface->uc_addr[FM10K_RDT(reg_idx)];
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	/* reset ntu and ntc to place SW in sync with hardware */
101062306a36Sopenharmony_ci	ring->next_to_clean = 0;
101162306a36Sopenharmony_ci	ring->next_to_use = 0;
101262306a36Sopenharmony_ci	ring->next_to_alloc = 0;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	/* Configure the Rx buffer size for one buff without split */
101562306a36Sopenharmony_ci	srrctl |= FM10K_RX_BUFSZ >> FM10K_SRRCTL_BSIZEPKT_SHIFT;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	/* Configure the Rx ring to suppress loopback packets */
101862306a36Sopenharmony_ci	srrctl |= FM10K_SRRCTL_LOOPBACK_SUPPRESS;
101962306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_SRRCTL(reg_idx), srrctl);
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	/* Enable drop on empty */
102262306a36Sopenharmony_ci#ifdef CONFIG_DCB
102362306a36Sopenharmony_ci	if (interface->pfc_en)
102462306a36Sopenharmony_ci		rx_pause = interface->pfc_en;
102562306a36Sopenharmony_ci#endif
102662306a36Sopenharmony_ci	if (!(rx_pause & BIT(ring->qos_pc)))
102762306a36Sopenharmony_ci		rxdctl |= FM10K_RXDCTL_DROP_ON_EMPTY;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_RXDCTL(reg_idx), rxdctl);
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	/* assign default VLAN to queue */
103262306a36Sopenharmony_ci	ring->vid = hw->mac.default_vid;
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	/* if we have an active VLAN, disable default VLAN ID */
103562306a36Sopenharmony_ci	if (test_bit(hw->mac.default_vid, interface->active_vlans))
103662306a36Sopenharmony_ci		ring->vid |= FM10K_VLAN_CLEAR;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	/* Map interrupt */
103962306a36Sopenharmony_ci	if (ring->q_vector) {
104062306a36Sopenharmony_ci		rxint = ring->q_vector->v_idx + NON_Q_VECTORS;
104162306a36Sopenharmony_ci		rxint |= FM10K_INT_MAP_TIMER1;
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_RXINT(reg_idx), rxint);
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	/* enable queue */
104762306a36Sopenharmony_ci	rxqctl = fm10k_read_reg(hw, FM10K_RXQCTL(reg_idx));
104862306a36Sopenharmony_ci	rxqctl |= FM10K_RXQCTL_ENABLE;
104962306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_RXQCTL(reg_idx), rxqctl);
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	/* place buffers on ring for receive data */
105262306a36Sopenharmony_ci	fm10k_alloc_rx_buffers(ring, fm10k_desc_unused(ring));
105362306a36Sopenharmony_ci}
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci/**
105662306a36Sopenharmony_ci * fm10k_update_rx_drop_en - Configures the drop enable bits for Rx rings
105762306a36Sopenharmony_ci * @interface: board private structure
105862306a36Sopenharmony_ci *
105962306a36Sopenharmony_ci * Configure the drop enable bits for the Rx rings.
106062306a36Sopenharmony_ci **/
106162306a36Sopenharmony_civoid fm10k_update_rx_drop_en(struct fm10k_intfc *interface)
106262306a36Sopenharmony_ci{
106362306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
106462306a36Sopenharmony_ci	u8 rx_pause = interface->rx_pause;
106562306a36Sopenharmony_ci	int i;
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci#ifdef CONFIG_DCB
106862306a36Sopenharmony_ci	if (interface->pfc_en)
106962306a36Sopenharmony_ci		rx_pause = interface->pfc_en;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci#endif
107262306a36Sopenharmony_ci	for (i = 0; i < interface->num_rx_queues; i++) {
107362306a36Sopenharmony_ci		struct fm10k_ring *ring = interface->rx_ring[i];
107462306a36Sopenharmony_ci		u32 rxdctl = FM10K_RXDCTL_WRITE_BACK_MIN_DELAY;
107562306a36Sopenharmony_ci		u8 reg_idx = ring->reg_idx;
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci		if (!(rx_pause & BIT(ring->qos_pc)))
107862306a36Sopenharmony_ci			rxdctl |= FM10K_RXDCTL_DROP_ON_EMPTY;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci		fm10k_write_reg(hw, FM10K_RXDCTL(reg_idx), rxdctl);
108162306a36Sopenharmony_ci	}
108262306a36Sopenharmony_ci}
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci/**
108562306a36Sopenharmony_ci * fm10k_configure_dglort - Configure Receive DGLORT after reset
108662306a36Sopenharmony_ci * @interface: board private structure
108762306a36Sopenharmony_ci *
108862306a36Sopenharmony_ci * Configure the DGLORT description and RSS tables.
108962306a36Sopenharmony_ci **/
109062306a36Sopenharmony_cistatic void fm10k_configure_dglort(struct fm10k_intfc *interface)
109162306a36Sopenharmony_ci{
109262306a36Sopenharmony_ci	struct fm10k_dglort_cfg dglort = { 0 };
109362306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
109462306a36Sopenharmony_ci	int i;
109562306a36Sopenharmony_ci	u32 mrqc;
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	/* Fill out hash function seeds */
109862306a36Sopenharmony_ci	for (i = 0; i < FM10K_RSSRK_SIZE; i++)
109962306a36Sopenharmony_ci		fm10k_write_reg(hw, FM10K_RSSRK(0, i), interface->rssrk[i]);
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	/* Write RETA table to hardware */
110262306a36Sopenharmony_ci	for (i = 0; i < FM10K_RETA_SIZE; i++)
110362306a36Sopenharmony_ci		fm10k_write_reg(hw, FM10K_RETA(0, i), interface->reta[i]);
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	/* Generate RSS hash based on packet types, TCP/UDP
110662306a36Sopenharmony_ci	 * port numbers and/or IPv4/v6 src and dst addresses
110762306a36Sopenharmony_ci	 */
110862306a36Sopenharmony_ci	mrqc = FM10K_MRQC_IPV4 |
110962306a36Sopenharmony_ci	       FM10K_MRQC_TCP_IPV4 |
111062306a36Sopenharmony_ci	       FM10K_MRQC_IPV6 |
111162306a36Sopenharmony_ci	       FM10K_MRQC_TCP_IPV6;
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	if (test_bit(FM10K_FLAG_RSS_FIELD_IPV4_UDP, interface->flags))
111462306a36Sopenharmony_ci		mrqc |= FM10K_MRQC_UDP_IPV4;
111562306a36Sopenharmony_ci	if (test_bit(FM10K_FLAG_RSS_FIELD_IPV6_UDP, interface->flags))
111662306a36Sopenharmony_ci		mrqc |= FM10K_MRQC_UDP_IPV6;
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_MRQC(0), mrqc);
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	/* configure default DGLORT mapping for RSS/DCB */
112162306a36Sopenharmony_ci	dglort.inner_rss = 1;
112262306a36Sopenharmony_ci	dglort.rss_l = fls(interface->ring_feature[RING_F_RSS].mask);
112362306a36Sopenharmony_ci	dglort.pc_l = fls(interface->ring_feature[RING_F_QOS].mask);
112462306a36Sopenharmony_ci	hw->mac.ops.configure_dglort_map(hw, &dglort);
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	/* assign GLORT per queue for queue mapped testing */
112762306a36Sopenharmony_ci	if (interface->glort_count > 64) {
112862306a36Sopenharmony_ci		memset(&dglort, 0, sizeof(dglort));
112962306a36Sopenharmony_ci		dglort.inner_rss = 1;
113062306a36Sopenharmony_ci		dglort.glort = interface->glort + 64;
113162306a36Sopenharmony_ci		dglort.idx = fm10k_dglort_pf_queue;
113262306a36Sopenharmony_ci		dglort.queue_l = fls(interface->num_rx_queues - 1);
113362306a36Sopenharmony_ci		hw->mac.ops.configure_dglort_map(hw, &dglort);
113462306a36Sopenharmony_ci	}
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	/* assign glort value for RSS/DCB specific to this interface */
113762306a36Sopenharmony_ci	memset(&dglort, 0, sizeof(dglort));
113862306a36Sopenharmony_ci	dglort.inner_rss = 1;
113962306a36Sopenharmony_ci	dglort.glort = interface->glort;
114062306a36Sopenharmony_ci	dglort.rss_l = fls(interface->ring_feature[RING_F_RSS].mask);
114162306a36Sopenharmony_ci	dglort.pc_l = fls(interface->ring_feature[RING_F_QOS].mask);
114262306a36Sopenharmony_ci	/* configure DGLORT mapping for RSS/DCB */
114362306a36Sopenharmony_ci	dglort.idx = fm10k_dglort_pf_rss;
114462306a36Sopenharmony_ci	if (interface->l2_accel)
114562306a36Sopenharmony_ci		dglort.shared_l = fls(interface->l2_accel->size);
114662306a36Sopenharmony_ci	hw->mac.ops.configure_dglort_map(hw, &dglort);
114762306a36Sopenharmony_ci}
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci/**
115062306a36Sopenharmony_ci * fm10k_configure_rx - Configure Receive Unit after Reset
115162306a36Sopenharmony_ci * @interface: board private structure
115262306a36Sopenharmony_ci *
115362306a36Sopenharmony_ci * Configure the Rx unit of the MAC after a reset.
115462306a36Sopenharmony_ci **/
115562306a36Sopenharmony_cistatic void fm10k_configure_rx(struct fm10k_intfc *interface)
115662306a36Sopenharmony_ci{
115762306a36Sopenharmony_ci	int i;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	/* Configure SWPRI to PC map */
116062306a36Sopenharmony_ci	fm10k_configure_swpri_map(interface);
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	/* Configure RSS and DGLORT map */
116362306a36Sopenharmony_ci	fm10k_configure_dglort(interface);
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	/* Setup the HW Rx Head and Tail descriptor pointers */
116662306a36Sopenharmony_ci	for (i = 0; i < interface->num_rx_queues; i++)
116762306a36Sopenharmony_ci		fm10k_configure_rx_ring(interface, interface->rx_ring[i]);
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	/* possible poll here to verify that Rx rings are now enabled */
117062306a36Sopenharmony_ci}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_cistatic void fm10k_napi_enable_all(struct fm10k_intfc *interface)
117362306a36Sopenharmony_ci{
117462306a36Sopenharmony_ci	struct fm10k_q_vector *q_vector;
117562306a36Sopenharmony_ci	int q_idx;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	for (q_idx = 0; q_idx < interface->num_q_vectors; q_idx++) {
117862306a36Sopenharmony_ci		q_vector = interface->q_vector[q_idx];
117962306a36Sopenharmony_ci		napi_enable(&q_vector->napi);
118062306a36Sopenharmony_ci	}
118162306a36Sopenharmony_ci}
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_cistatic irqreturn_t fm10k_msix_clean_rings(int __always_unused irq, void *data)
118462306a36Sopenharmony_ci{
118562306a36Sopenharmony_ci	struct fm10k_q_vector *q_vector = data;
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	if (q_vector->rx.count || q_vector->tx.count)
118862306a36Sopenharmony_ci		napi_schedule_irqoff(&q_vector->napi);
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	return IRQ_HANDLED;
119162306a36Sopenharmony_ci}
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_cistatic irqreturn_t fm10k_msix_mbx_vf(int __always_unused irq, void *data)
119462306a36Sopenharmony_ci{
119562306a36Sopenharmony_ci	struct fm10k_intfc *interface = data;
119662306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
119762306a36Sopenharmony_ci	struct fm10k_mbx_info *mbx = &hw->mbx;
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	/* re-enable mailbox interrupt and indicate 20us delay */
120062306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_VFITR(FM10K_MBX_VECTOR),
120162306a36Sopenharmony_ci			(FM10K_MBX_INT_DELAY >> hw->mac.itr_scale) |
120262306a36Sopenharmony_ci			FM10K_ITR_ENABLE);
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	/* service upstream mailbox */
120562306a36Sopenharmony_ci	if (fm10k_mbx_trylock(interface)) {
120662306a36Sopenharmony_ci		mbx->ops.process(hw, mbx);
120762306a36Sopenharmony_ci		fm10k_mbx_unlock(interface);
120862306a36Sopenharmony_ci	}
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	hw->mac.get_host_state = true;
121162306a36Sopenharmony_ci	fm10k_service_event_schedule(interface);
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	return IRQ_HANDLED;
121462306a36Sopenharmony_ci}
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci#define FM10K_ERR_MSG(type) case (type): error = #type; break
121762306a36Sopenharmony_cistatic void fm10k_handle_fault(struct fm10k_intfc *interface, int type,
121862306a36Sopenharmony_ci			       struct fm10k_fault *fault)
121962306a36Sopenharmony_ci{
122062306a36Sopenharmony_ci	struct pci_dev *pdev = interface->pdev;
122162306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
122262306a36Sopenharmony_ci	struct fm10k_iov_data *iov_data = interface->iov_data;
122362306a36Sopenharmony_ci	char *error;
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	switch (type) {
122662306a36Sopenharmony_ci	case FM10K_PCA_FAULT:
122762306a36Sopenharmony_ci		switch (fault->type) {
122862306a36Sopenharmony_ci		default:
122962306a36Sopenharmony_ci			error = "Unknown PCA error";
123062306a36Sopenharmony_ci			break;
123162306a36Sopenharmony_ci		FM10K_ERR_MSG(PCA_NO_FAULT);
123262306a36Sopenharmony_ci		FM10K_ERR_MSG(PCA_UNMAPPED_ADDR);
123362306a36Sopenharmony_ci		FM10K_ERR_MSG(PCA_BAD_QACCESS_PF);
123462306a36Sopenharmony_ci		FM10K_ERR_MSG(PCA_BAD_QACCESS_VF);
123562306a36Sopenharmony_ci		FM10K_ERR_MSG(PCA_MALICIOUS_REQ);
123662306a36Sopenharmony_ci		FM10K_ERR_MSG(PCA_POISONED_TLP);
123762306a36Sopenharmony_ci		FM10K_ERR_MSG(PCA_TLP_ABORT);
123862306a36Sopenharmony_ci		}
123962306a36Sopenharmony_ci		break;
124062306a36Sopenharmony_ci	case FM10K_THI_FAULT:
124162306a36Sopenharmony_ci		switch (fault->type) {
124262306a36Sopenharmony_ci		default:
124362306a36Sopenharmony_ci			error = "Unknown THI error";
124462306a36Sopenharmony_ci			break;
124562306a36Sopenharmony_ci		FM10K_ERR_MSG(THI_NO_FAULT);
124662306a36Sopenharmony_ci		FM10K_ERR_MSG(THI_MAL_DIS_Q_FAULT);
124762306a36Sopenharmony_ci		}
124862306a36Sopenharmony_ci		break;
124962306a36Sopenharmony_ci	case FM10K_FUM_FAULT:
125062306a36Sopenharmony_ci		switch (fault->type) {
125162306a36Sopenharmony_ci		default:
125262306a36Sopenharmony_ci			error = "Unknown FUM error";
125362306a36Sopenharmony_ci			break;
125462306a36Sopenharmony_ci		FM10K_ERR_MSG(FUM_NO_FAULT);
125562306a36Sopenharmony_ci		FM10K_ERR_MSG(FUM_UNMAPPED_ADDR);
125662306a36Sopenharmony_ci		FM10K_ERR_MSG(FUM_BAD_VF_QACCESS);
125762306a36Sopenharmony_ci		FM10K_ERR_MSG(FUM_ADD_DECODE_ERR);
125862306a36Sopenharmony_ci		FM10K_ERR_MSG(FUM_RO_ERROR);
125962306a36Sopenharmony_ci		FM10K_ERR_MSG(FUM_QPRC_CRC_ERROR);
126062306a36Sopenharmony_ci		FM10K_ERR_MSG(FUM_CSR_TIMEOUT);
126162306a36Sopenharmony_ci		FM10K_ERR_MSG(FUM_INVALID_TYPE);
126262306a36Sopenharmony_ci		FM10K_ERR_MSG(FUM_INVALID_LENGTH);
126362306a36Sopenharmony_ci		FM10K_ERR_MSG(FUM_INVALID_BE);
126462306a36Sopenharmony_ci		FM10K_ERR_MSG(FUM_INVALID_ALIGN);
126562306a36Sopenharmony_ci		}
126662306a36Sopenharmony_ci		break;
126762306a36Sopenharmony_ci	default:
126862306a36Sopenharmony_ci		error = "Undocumented fault";
126962306a36Sopenharmony_ci		break;
127062306a36Sopenharmony_ci	}
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	dev_warn(&pdev->dev,
127362306a36Sopenharmony_ci		 "%s Address: 0x%llx SpecInfo: 0x%x Func: %02x.%0x\n",
127462306a36Sopenharmony_ci		 error, fault->address, fault->specinfo,
127562306a36Sopenharmony_ci		 PCI_SLOT(fault->func), PCI_FUNC(fault->func));
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	/* For VF faults, clear out the respective LPORT, reset the queue
127862306a36Sopenharmony_ci	 * resources, and then reconnect to the mailbox. This allows the
127962306a36Sopenharmony_ci	 * VF in question to resume behavior. For transient faults that are
128062306a36Sopenharmony_ci	 * the result of non-malicious behavior this will log the fault and
128162306a36Sopenharmony_ci	 * allow the VF to resume functionality. Obviously for malicious VFs
128262306a36Sopenharmony_ci	 * they will be able to attempt malicious behavior again. In this
128362306a36Sopenharmony_ci	 * case, the system administrator will need to step in and manually
128462306a36Sopenharmony_ci	 * remove or disable the VF in question.
128562306a36Sopenharmony_ci	 */
128662306a36Sopenharmony_ci	if (fault->func && iov_data) {
128762306a36Sopenharmony_ci		int vf = fault->func - 1;
128862306a36Sopenharmony_ci		struct fm10k_vf_info *vf_info = &iov_data->vf_info[vf];
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci		hw->iov.ops.reset_lport(hw, vf_info);
129162306a36Sopenharmony_ci		hw->iov.ops.reset_resources(hw, vf_info);
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci		/* reset_lport disables the VF, so re-enable it */
129462306a36Sopenharmony_ci		hw->iov.ops.set_lport(hw, vf_info, vf,
129562306a36Sopenharmony_ci				      FM10K_VF_FLAG_MULTI_CAPABLE);
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci		/* reset_resources will disconnect from the mbx  */
129862306a36Sopenharmony_ci		vf_info->mbx.ops.connect(hw, &vf_info->mbx);
129962306a36Sopenharmony_ci	}
130062306a36Sopenharmony_ci}
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_cistatic void fm10k_report_fault(struct fm10k_intfc *interface, u32 eicr)
130362306a36Sopenharmony_ci{
130462306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
130562306a36Sopenharmony_ci	struct fm10k_fault fault = { 0 };
130662306a36Sopenharmony_ci	int type, err;
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	for (eicr &= FM10K_EICR_FAULT_MASK, type = FM10K_PCA_FAULT;
130962306a36Sopenharmony_ci	     eicr;
131062306a36Sopenharmony_ci	     eicr >>= 1, type += FM10K_FAULT_SIZE) {
131162306a36Sopenharmony_ci		/* only check if there is an error reported */
131262306a36Sopenharmony_ci		if (!(eicr & 0x1))
131362306a36Sopenharmony_ci			continue;
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci		/* retrieve fault info */
131662306a36Sopenharmony_ci		err = hw->mac.ops.get_fault(hw, type, &fault);
131762306a36Sopenharmony_ci		if (err) {
131862306a36Sopenharmony_ci			dev_err(&interface->pdev->dev,
131962306a36Sopenharmony_ci				"error reading fault\n");
132062306a36Sopenharmony_ci			continue;
132162306a36Sopenharmony_ci		}
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci		fm10k_handle_fault(interface, type, &fault);
132462306a36Sopenharmony_ci	}
132562306a36Sopenharmony_ci}
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_cistatic void fm10k_reset_drop_on_empty(struct fm10k_intfc *interface, u32 eicr)
132862306a36Sopenharmony_ci{
132962306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
133062306a36Sopenharmony_ci	const u32 rxdctl = FM10K_RXDCTL_WRITE_BACK_MIN_DELAY;
133162306a36Sopenharmony_ci	u32 maxholdq;
133262306a36Sopenharmony_ci	int q;
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	if (!(eicr & FM10K_EICR_MAXHOLDTIME))
133562306a36Sopenharmony_ci		return;
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	maxholdq = fm10k_read_reg(hw, FM10K_MAXHOLDQ(7));
133862306a36Sopenharmony_ci	if (maxholdq)
133962306a36Sopenharmony_ci		fm10k_write_reg(hw, FM10K_MAXHOLDQ(7), maxholdq);
134062306a36Sopenharmony_ci	for (q = 255;;) {
134162306a36Sopenharmony_ci		if (maxholdq & BIT(31)) {
134262306a36Sopenharmony_ci			if (q < FM10K_MAX_QUEUES_PF) {
134362306a36Sopenharmony_ci				interface->rx_overrun_pf++;
134462306a36Sopenharmony_ci				fm10k_write_reg(hw, FM10K_RXDCTL(q), rxdctl);
134562306a36Sopenharmony_ci			} else {
134662306a36Sopenharmony_ci				interface->rx_overrun_vf++;
134762306a36Sopenharmony_ci			}
134862306a36Sopenharmony_ci		}
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci		maxholdq *= 2;
135162306a36Sopenharmony_ci		if (!maxholdq)
135262306a36Sopenharmony_ci			q &= ~(32 - 1);
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci		if (!q)
135562306a36Sopenharmony_ci			break;
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci		if (q-- % 32)
135862306a36Sopenharmony_ci			continue;
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci		maxholdq = fm10k_read_reg(hw, FM10K_MAXHOLDQ(q / 32));
136162306a36Sopenharmony_ci		if (maxholdq)
136262306a36Sopenharmony_ci			fm10k_write_reg(hw, FM10K_MAXHOLDQ(q / 32), maxholdq);
136362306a36Sopenharmony_ci	}
136462306a36Sopenharmony_ci}
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_cistatic irqreturn_t fm10k_msix_mbx_pf(int __always_unused irq, void *data)
136762306a36Sopenharmony_ci{
136862306a36Sopenharmony_ci	struct fm10k_intfc *interface = data;
136962306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
137062306a36Sopenharmony_ci	struct fm10k_mbx_info *mbx = &hw->mbx;
137162306a36Sopenharmony_ci	u32 eicr;
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	/* unmask any set bits related to this interrupt */
137462306a36Sopenharmony_ci	eicr = fm10k_read_reg(hw, FM10K_EICR);
137562306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_EICR, eicr & (FM10K_EICR_MAILBOX |
137662306a36Sopenharmony_ci						FM10K_EICR_SWITCHREADY |
137762306a36Sopenharmony_ci						FM10K_EICR_SWITCHNOTREADY));
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	/* report any faults found to the message log */
138062306a36Sopenharmony_ci	fm10k_report_fault(interface, eicr);
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	/* reset any queues disabled due to receiver overrun */
138362306a36Sopenharmony_ci	fm10k_reset_drop_on_empty(interface, eicr);
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci	/* service mailboxes */
138662306a36Sopenharmony_ci	if (fm10k_mbx_trylock(interface)) {
138762306a36Sopenharmony_ci		s32 err = mbx->ops.process(hw, mbx);
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci		if (err == FM10K_ERR_RESET_REQUESTED)
139062306a36Sopenharmony_ci			set_bit(FM10K_FLAG_RESET_REQUESTED, interface->flags);
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci		/* handle VFLRE events */
139362306a36Sopenharmony_ci		fm10k_iov_event(interface);
139462306a36Sopenharmony_ci		fm10k_mbx_unlock(interface);
139562306a36Sopenharmony_ci	}
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	/* if switch toggled state we should reset GLORTs */
139862306a36Sopenharmony_ci	if (eicr & FM10K_EICR_SWITCHNOTREADY) {
139962306a36Sopenharmony_ci		/* force link down for at least 4 seconds */
140062306a36Sopenharmony_ci		interface->link_down_event = jiffies + (4 * HZ);
140162306a36Sopenharmony_ci		set_bit(__FM10K_LINK_DOWN, interface->state);
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci		/* reset dglort_map back to no config */
140462306a36Sopenharmony_ci		hw->mac.dglort_map = FM10K_DGLORTMAP_NONE;
140562306a36Sopenharmony_ci	}
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	/* we should validate host state after interrupt event */
140862306a36Sopenharmony_ci	hw->mac.get_host_state = true;
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	/* validate host state, and handle VF mailboxes in the service task */
141162306a36Sopenharmony_ci	fm10k_service_event_schedule(interface);
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	/* re-enable mailbox interrupt and indicate 20us delay */
141462306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_ITR(FM10K_MBX_VECTOR),
141562306a36Sopenharmony_ci			(FM10K_MBX_INT_DELAY >> hw->mac.itr_scale) |
141662306a36Sopenharmony_ci			FM10K_ITR_ENABLE);
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	return IRQ_HANDLED;
141962306a36Sopenharmony_ci}
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_civoid fm10k_mbx_free_irq(struct fm10k_intfc *interface)
142262306a36Sopenharmony_ci{
142362306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
142462306a36Sopenharmony_ci	struct msix_entry *entry;
142562306a36Sopenharmony_ci	int itr_reg;
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	/* no mailbox IRQ to free if MSI-X is not enabled */
142862306a36Sopenharmony_ci	if (!interface->msix_entries)
142962306a36Sopenharmony_ci		return;
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	entry = &interface->msix_entries[FM10K_MBX_VECTOR];
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	/* disconnect the mailbox */
143462306a36Sopenharmony_ci	hw->mbx.ops.disconnect(hw, &hw->mbx);
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	/* disable Mailbox cause */
143762306a36Sopenharmony_ci	if (hw->mac.type == fm10k_mac_pf) {
143862306a36Sopenharmony_ci		fm10k_write_reg(hw, FM10K_EIMR,
143962306a36Sopenharmony_ci				FM10K_EIMR_DISABLE(PCA_FAULT) |
144062306a36Sopenharmony_ci				FM10K_EIMR_DISABLE(FUM_FAULT) |
144162306a36Sopenharmony_ci				FM10K_EIMR_DISABLE(MAILBOX) |
144262306a36Sopenharmony_ci				FM10K_EIMR_DISABLE(SWITCHREADY) |
144362306a36Sopenharmony_ci				FM10K_EIMR_DISABLE(SWITCHNOTREADY) |
144462306a36Sopenharmony_ci				FM10K_EIMR_DISABLE(SRAMERROR) |
144562306a36Sopenharmony_ci				FM10K_EIMR_DISABLE(VFLR) |
144662306a36Sopenharmony_ci				FM10K_EIMR_DISABLE(MAXHOLDTIME));
144762306a36Sopenharmony_ci		itr_reg = FM10K_ITR(FM10K_MBX_VECTOR);
144862306a36Sopenharmony_ci	} else {
144962306a36Sopenharmony_ci		itr_reg = FM10K_VFITR(FM10K_MBX_VECTOR);
145062306a36Sopenharmony_ci	}
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	fm10k_write_reg(hw, itr_reg, FM10K_ITR_MASK_SET);
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	free_irq(entry->vector, interface);
145562306a36Sopenharmony_ci}
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_cistatic s32 fm10k_mbx_mac_addr(struct fm10k_hw *hw, u32 **results,
145862306a36Sopenharmony_ci			      struct fm10k_mbx_info *mbx)
145962306a36Sopenharmony_ci{
146062306a36Sopenharmony_ci	bool vlan_override = hw->mac.vlan_override;
146162306a36Sopenharmony_ci	u16 default_vid = hw->mac.default_vid;
146262306a36Sopenharmony_ci	struct fm10k_intfc *interface;
146362306a36Sopenharmony_ci	s32 err;
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	err = fm10k_msg_mac_vlan_vf(hw, results, mbx);
146662306a36Sopenharmony_ci	if (err)
146762306a36Sopenharmony_ci		return err;
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	interface = container_of(hw, struct fm10k_intfc, hw);
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	/* MAC was changed so we need reset */
147262306a36Sopenharmony_ci	if (is_valid_ether_addr(hw->mac.perm_addr) &&
147362306a36Sopenharmony_ci	    !ether_addr_equal(hw->mac.perm_addr, hw->mac.addr))
147462306a36Sopenharmony_ci		set_bit(FM10K_FLAG_RESET_REQUESTED, interface->flags);
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci	/* VLAN override was changed, or default VLAN changed */
147762306a36Sopenharmony_ci	if ((vlan_override != hw->mac.vlan_override) ||
147862306a36Sopenharmony_ci	    (default_vid != hw->mac.default_vid))
147962306a36Sopenharmony_ci		set_bit(FM10K_FLAG_RESET_REQUESTED, interface->flags);
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	return 0;
148262306a36Sopenharmony_ci}
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci/* generic error handler for mailbox issues */
148562306a36Sopenharmony_cistatic s32 fm10k_mbx_error(struct fm10k_hw *hw, u32 **results,
148662306a36Sopenharmony_ci			   struct fm10k_mbx_info __always_unused *mbx)
148762306a36Sopenharmony_ci{
148862306a36Sopenharmony_ci	struct fm10k_intfc *interface;
148962306a36Sopenharmony_ci	struct pci_dev *pdev;
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci	interface = container_of(hw, struct fm10k_intfc, hw);
149262306a36Sopenharmony_ci	pdev = interface->pdev;
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	dev_err(&pdev->dev, "Unknown message ID %u\n",
149562306a36Sopenharmony_ci		**results & FM10K_TLV_ID_MASK);
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci	return 0;
149862306a36Sopenharmony_ci}
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_cistatic const struct fm10k_msg_data vf_mbx_data[] = {
150162306a36Sopenharmony_ci	FM10K_TLV_MSG_TEST_HANDLER(fm10k_tlv_msg_test),
150262306a36Sopenharmony_ci	FM10K_VF_MSG_MAC_VLAN_HANDLER(fm10k_mbx_mac_addr),
150362306a36Sopenharmony_ci	FM10K_VF_MSG_LPORT_STATE_HANDLER(fm10k_msg_lport_state_vf),
150462306a36Sopenharmony_ci	FM10K_TLV_MSG_ERROR_HANDLER(fm10k_mbx_error),
150562306a36Sopenharmony_ci};
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_cistatic int fm10k_mbx_request_irq_vf(struct fm10k_intfc *interface)
150862306a36Sopenharmony_ci{
150962306a36Sopenharmony_ci	struct msix_entry *entry = &interface->msix_entries[FM10K_MBX_VECTOR];
151062306a36Sopenharmony_ci	struct net_device *dev = interface->netdev;
151162306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
151262306a36Sopenharmony_ci	int err;
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	/* Use timer0 for interrupt moderation on the mailbox */
151562306a36Sopenharmony_ci	u32 itr = entry->entry | FM10K_INT_MAP_TIMER0;
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	/* register mailbox handlers */
151862306a36Sopenharmony_ci	err = hw->mbx.ops.register_handlers(&hw->mbx, vf_mbx_data);
151962306a36Sopenharmony_ci	if (err)
152062306a36Sopenharmony_ci		return err;
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	/* request the IRQ */
152362306a36Sopenharmony_ci	err = request_irq(entry->vector, fm10k_msix_mbx_vf, 0,
152462306a36Sopenharmony_ci			  dev->name, interface);
152562306a36Sopenharmony_ci	if (err) {
152662306a36Sopenharmony_ci		netif_err(interface, probe, dev,
152762306a36Sopenharmony_ci			  "request_irq for msix_mbx failed: %d\n", err);
152862306a36Sopenharmony_ci		return err;
152962306a36Sopenharmony_ci	}
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	/* map all of the interrupt sources */
153262306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_VFINT_MAP, itr);
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	/* enable interrupt */
153562306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_VFITR(entry->entry), FM10K_ITR_ENABLE);
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	return 0;
153862306a36Sopenharmony_ci}
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_cistatic s32 fm10k_lport_map(struct fm10k_hw *hw, u32 **results,
154162306a36Sopenharmony_ci			   struct fm10k_mbx_info *mbx)
154262306a36Sopenharmony_ci{
154362306a36Sopenharmony_ci	struct fm10k_intfc *interface;
154462306a36Sopenharmony_ci	u32 dglort_map = hw->mac.dglort_map;
154562306a36Sopenharmony_ci	s32 err;
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	interface = container_of(hw, struct fm10k_intfc, hw);
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	err = fm10k_msg_err_pf(hw, results, mbx);
155062306a36Sopenharmony_ci	if (!err && hw->swapi.status) {
155162306a36Sopenharmony_ci		/* force link down for a reasonable delay */
155262306a36Sopenharmony_ci		interface->link_down_event = jiffies + (2 * HZ);
155362306a36Sopenharmony_ci		set_bit(__FM10K_LINK_DOWN, interface->state);
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci		/* reset dglort_map back to no config */
155662306a36Sopenharmony_ci		hw->mac.dglort_map = FM10K_DGLORTMAP_NONE;
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci		fm10k_service_event_schedule(interface);
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci		/* prevent overloading kernel message buffer */
156162306a36Sopenharmony_ci		if (interface->lport_map_failed)
156262306a36Sopenharmony_ci			return 0;
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci		interface->lport_map_failed = true;
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci		if (hw->swapi.status == FM10K_MSG_ERR_PEP_NOT_SCHEDULED)
156762306a36Sopenharmony_ci			dev_warn(&interface->pdev->dev,
156862306a36Sopenharmony_ci				 "cannot obtain link because the host interface is configured for a PCIe host interface bandwidth of zero\n");
156962306a36Sopenharmony_ci		dev_warn(&interface->pdev->dev,
157062306a36Sopenharmony_ci			 "request logical port map failed: %d\n",
157162306a36Sopenharmony_ci			 hw->swapi.status);
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci		return 0;
157462306a36Sopenharmony_ci	}
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	err = fm10k_msg_lport_map_pf(hw, results, mbx);
157762306a36Sopenharmony_ci	if (err)
157862306a36Sopenharmony_ci		return err;
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci	interface->lport_map_failed = false;
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci	/* we need to reset if port count was just updated */
158362306a36Sopenharmony_ci	if (dglort_map != hw->mac.dglort_map)
158462306a36Sopenharmony_ci		set_bit(FM10K_FLAG_RESET_REQUESTED, interface->flags);
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci	return 0;
158762306a36Sopenharmony_ci}
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_cistatic s32 fm10k_update_pvid(struct fm10k_hw *hw, u32 **results,
159062306a36Sopenharmony_ci			     struct fm10k_mbx_info __always_unused *mbx)
159162306a36Sopenharmony_ci{
159262306a36Sopenharmony_ci	struct fm10k_intfc *interface;
159362306a36Sopenharmony_ci	u16 glort, pvid;
159462306a36Sopenharmony_ci	u32 pvid_update;
159562306a36Sopenharmony_ci	s32 err;
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci	err = fm10k_tlv_attr_get_u32(results[FM10K_PF_ATTR_ID_UPDATE_PVID],
159862306a36Sopenharmony_ci				     &pvid_update);
159962306a36Sopenharmony_ci	if (err)
160062306a36Sopenharmony_ci		return err;
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	/* extract values from the pvid update */
160362306a36Sopenharmony_ci	glort = FM10K_MSG_HDR_FIELD_GET(pvid_update, UPDATE_PVID_GLORT);
160462306a36Sopenharmony_ci	pvid = FM10K_MSG_HDR_FIELD_GET(pvid_update, UPDATE_PVID_PVID);
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci	/* if glort is not valid return error */
160762306a36Sopenharmony_ci	if (!fm10k_glort_valid_pf(hw, glort))
160862306a36Sopenharmony_ci		return FM10K_ERR_PARAM;
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	/* verify VLAN ID is valid */
161162306a36Sopenharmony_ci	if (pvid >= FM10K_VLAN_TABLE_VID_MAX)
161262306a36Sopenharmony_ci		return FM10K_ERR_PARAM;
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	interface = container_of(hw, struct fm10k_intfc, hw);
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci	/* check to see if this belongs to one of the VFs */
161762306a36Sopenharmony_ci	err = fm10k_iov_update_pvid(interface, glort, pvid);
161862306a36Sopenharmony_ci	if (!err)
161962306a36Sopenharmony_ci		return 0;
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	/* we need to reset if default VLAN was just updated */
162262306a36Sopenharmony_ci	if (pvid != hw->mac.default_vid)
162362306a36Sopenharmony_ci		set_bit(FM10K_FLAG_RESET_REQUESTED, interface->flags);
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	hw->mac.default_vid = pvid;
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	return 0;
162862306a36Sopenharmony_ci}
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_cistatic const struct fm10k_msg_data pf_mbx_data[] = {
163162306a36Sopenharmony_ci	FM10K_PF_MSG_ERR_HANDLER(XCAST_MODES, fm10k_msg_err_pf),
163262306a36Sopenharmony_ci	FM10K_PF_MSG_ERR_HANDLER(UPDATE_MAC_FWD_RULE, fm10k_msg_err_pf),
163362306a36Sopenharmony_ci	FM10K_PF_MSG_LPORT_MAP_HANDLER(fm10k_lport_map),
163462306a36Sopenharmony_ci	FM10K_PF_MSG_ERR_HANDLER(LPORT_CREATE, fm10k_msg_err_pf),
163562306a36Sopenharmony_ci	FM10K_PF_MSG_ERR_HANDLER(LPORT_DELETE, fm10k_msg_err_pf),
163662306a36Sopenharmony_ci	FM10K_PF_MSG_UPDATE_PVID_HANDLER(fm10k_update_pvid),
163762306a36Sopenharmony_ci	FM10K_TLV_MSG_ERROR_HANDLER(fm10k_mbx_error),
163862306a36Sopenharmony_ci};
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_cistatic int fm10k_mbx_request_irq_pf(struct fm10k_intfc *interface)
164162306a36Sopenharmony_ci{
164262306a36Sopenharmony_ci	struct msix_entry *entry = &interface->msix_entries[FM10K_MBX_VECTOR];
164362306a36Sopenharmony_ci	struct net_device *dev = interface->netdev;
164462306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
164562306a36Sopenharmony_ci	int err;
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_ci	/* Use timer0 for interrupt moderation on the mailbox */
164862306a36Sopenharmony_ci	u32 mbx_itr = entry->entry | FM10K_INT_MAP_TIMER0;
164962306a36Sopenharmony_ci	u32 other_itr = entry->entry | FM10K_INT_MAP_IMMEDIATE;
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci	/* register mailbox handlers */
165262306a36Sopenharmony_ci	err = hw->mbx.ops.register_handlers(&hw->mbx, pf_mbx_data);
165362306a36Sopenharmony_ci	if (err)
165462306a36Sopenharmony_ci		return err;
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	/* request the IRQ */
165762306a36Sopenharmony_ci	err = request_irq(entry->vector, fm10k_msix_mbx_pf, 0,
165862306a36Sopenharmony_ci			  dev->name, interface);
165962306a36Sopenharmony_ci	if (err) {
166062306a36Sopenharmony_ci		netif_err(interface, probe, dev,
166162306a36Sopenharmony_ci			  "request_irq for msix_mbx failed: %d\n", err);
166262306a36Sopenharmony_ci		return err;
166362306a36Sopenharmony_ci	}
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_ci	/* Enable interrupts w/ no moderation for "other" interrupts */
166662306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_pcie_fault), other_itr);
166762306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_switch_up_down), other_itr);
166862306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_sram), other_itr);
166962306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_max_hold_time), other_itr);
167062306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_vflr), other_itr);
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	/* Enable interrupts w/ moderation for mailbox */
167362306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_mailbox), mbx_itr);
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	/* Enable individual interrupt causes */
167662306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_EIMR, FM10K_EIMR_ENABLE(PCA_FAULT) |
167762306a36Sopenharmony_ci					FM10K_EIMR_ENABLE(FUM_FAULT) |
167862306a36Sopenharmony_ci					FM10K_EIMR_ENABLE(MAILBOX) |
167962306a36Sopenharmony_ci					FM10K_EIMR_ENABLE(SWITCHREADY) |
168062306a36Sopenharmony_ci					FM10K_EIMR_ENABLE(SWITCHNOTREADY) |
168162306a36Sopenharmony_ci					FM10K_EIMR_ENABLE(SRAMERROR) |
168262306a36Sopenharmony_ci					FM10K_EIMR_ENABLE(VFLR) |
168362306a36Sopenharmony_ci					FM10K_EIMR_ENABLE(MAXHOLDTIME));
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	/* enable interrupt */
168662306a36Sopenharmony_ci	fm10k_write_reg(hw, FM10K_ITR(entry->entry), FM10K_ITR_ENABLE);
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci	return 0;
168962306a36Sopenharmony_ci}
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_ciint fm10k_mbx_request_irq(struct fm10k_intfc *interface)
169262306a36Sopenharmony_ci{
169362306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
169462306a36Sopenharmony_ci	int err;
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_ci	/* enable Mailbox cause */
169762306a36Sopenharmony_ci	if (hw->mac.type == fm10k_mac_pf)
169862306a36Sopenharmony_ci		err = fm10k_mbx_request_irq_pf(interface);
169962306a36Sopenharmony_ci	else
170062306a36Sopenharmony_ci		err = fm10k_mbx_request_irq_vf(interface);
170162306a36Sopenharmony_ci	if (err)
170262306a36Sopenharmony_ci		return err;
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci	/* connect mailbox */
170562306a36Sopenharmony_ci	err = hw->mbx.ops.connect(hw, &hw->mbx);
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci	/* if the mailbox failed to connect, then free IRQ */
170862306a36Sopenharmony_ci	if (err)
170962306a36Sopenharmony_ci		fm10k_mbx_free_irq(interface);
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci	return err;
171262306a36Sopenharmony_ci}
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci/**
171562306a36Sopenharmony_ci * fm10k_qv_free_irq - release interrupts associated with queue vectors
171662306a36Sopenharmony_ci * @interface: board private structure
171762306a36Sopenharmony_ci *
171862306a36Sopenharmony_ci * Release all interrupts associated with this interface
171962306a36Sopenharmony_ci **/
172062306a36Sopenharmony_civoid fm10k_qv_free_irq(struct fm10k_intfc *interface)
172162306a36Sopenharmony_ci{
172262306a36Sopenharmony_ci	int vector = interface->num_q_vectors;
172362306a36Sopenharmony_ci	struct msix_entry *entry;
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci	entry = &interface->msix_entries[NON_Q_VECTORS + vector];
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci	while (vector) {
172862306a36Sopenharmony_ci		struct fm10k_q_vector *q_vector;
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci		vector--;
173162306a36Sopenharmony_ci		entry--;
173262306a36Sopenharmony_ci		q_vector = interface->q_vector[vector];
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci		if (!q_vector->tx.count && !q_vector->rx.count)
173562306a36Sopenharmony_ci			continue;
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci		/* clear the affinity_mask in the IRQ descriptor */
173862306a36Sopenharmony_ci		irq_set_affinity_hint(entry->vector, NULL);
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci		/* disable interrupts */
174162306a36Sopenharmony_ci		writel(FM10K_ITR_MASK_SET, q_vector->itr);
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci		free_irq(entry->vector, q_vector);
174462306a36Sopenharmony_ci	}
174562306a36Sopenharmony_ci}
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci/**
174862306a36Sopenharmony_ci * fm10k_qv_request_irq - initialize interrupts for queue vectors
174962306a36Sopenharmony_ci * @interface: board private structure
175062306a36Sopenharmony_ci *
175162306a36Sopenharmony_ci * Attempts to configure interrupts using the best available
175262306a36Sopenharmony_ci * capabilities of the hardware and kernel.
175362306a36Sopenharmony_ci **/
175462306a36Sopenharmony_ciint fm10k_qv_request_irq(struct fm10k_intfc *interface)
175562306a36Sopenharmony_ci{
175662306a36Sopenharmony_ci	struct net_device *dev = interface->netdev;
175762306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
175862306a36Sopenharmony_ci	struct msix_entry *entry;
175962306a36Sopenharmony_ci	unsigned int ri = 0, ti = 0;
176062306a36Sopenharmony_ci	int vector, err;
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci	entry = &interface->msix_entries[NON_Q_VECTORS];
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci	for (vector = 0; vector < interface->num_q_vectors; vector++) {
176562306a36Sopenharmony_ci		struct fm10k_q_vector *q_vector = interface->q_vector[vector];
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci		/* name the vector */
176862306a36Sopenharmony_ci		if (q_vector->tx.count && q_vector->rx.count) {
176962306a36Sopenharmony_ci			snprintf(q_vector->name, sizeof(q_vector->name),
177062306a36Sopenharmony_ci				 "%s-TxRx-%u", dev->name, ri++);
177162306a36Sopenharmony_ci			ti++;
177262306a36Sopenharmony_ci		} else if (q_vector->rx.count) {
177362306a36Sopenharmony_ci			snprintf(q_vector->name, sizeof(q_vector->name),
177462306a36Sopenharmony_ci				 "%s-rx-%u", dev->name, ri++);
177562306a36Sopenharmony_ci		} else if (q_vector->tx.count) {
177662306a36Sopenharmony_ci			snprintf(q_vector->name, sizeof(q_vector->name),
177762306a36Sopenharmony_ci				 "%s-tx-%u", dev->name, ti++);
177862306a36Sopenharmony_ci		} else {
177962306a36Sopenharmony_ci			/* skip this unused q_vector */
178062306a36Sopenharmony_ci			continue;
178162306a36Sopenharmony_ci		}
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_ci		/* Assign ITR register to q_vector */
178462306a36Sopenharmony_ci		q_vector->itr = (hw->mac.type == fm10k_mac_pf) ?
178562306a36Sopenharmony_ci				&interface->uc_addr[FM10K_ITR(entry->entry)] :
178662306a36Sopenharmony_ci				&interface->uc_addr[FM10K_VFITR(entry->entry)];
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci		/* request the IRQ */
178962306a36Sopenharmony_ci		err = request_irq(entry->vector, &fm10k_msix_clean_rings, 0,
179062306a36Sopenharmony_ci				  q_vector->name, q_vector);
179162306a36Sopenharmony_ci		if (err) {
179262306a36Sopenharmony_ci			netif_err(interface, probe, dev,
179362306a36Sopenharmony_ci				  "request_irq failed for MSIX interrupt Error: %d\n",
179462306a36Sopenharmony_ci				  err);
179562306a36Sopenharmony_ci			goto err_out;
179662306a36Sopenharmony_ci		}
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci		/* assign the mask for this irq */
179962306a36Sopenharmony_ci		irq_set_affinity_hint(entry->vector, &q_vector->affinity_mask);
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci		/* Enable q_vector */
180262306a36Sopenharmony_ci		writel(FM10K_ITR_ENABLE, q_vector->itr);
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci		entry++;
180562306a36Sopenharmony_ci	}
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_ci	return 0;
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_cierr_out:
181062306a36Sopenharmony_ci	/* wind through the ring freeing all entries and vectors */
181162306a36Sopenharmony_ci	while (vector) {
181262306a36Sopenharmony_ci		struct fm10k_q_vector *q_vector;
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci		entry--;
181562306a36Sopenharmony_ci		vector--;
181662306a36Sopenharmony_ci		q_vector = interface->q_vector[vector];
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci		if (!q_vector->tx.count && !q_vector->rx.count)
181962306a36Sopenharmony_ci			continue;
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci		/* clear the affinity_mask in the IRQ descriptor */
182262306a36Sopenharmony_ci		irq_set_affinity_hint(entry->vector, NULL);
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci		/* disable interrupts */
182562306a36Sopenharmony_ci		writel(FM10K_ITR_MASK_SET, q_vector->itr);
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci		free_irq(entry->vector, q_vector);
182862306a36Sopenharmony_ci	}
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci	return err;
183162306a36Sopenharmony_ci}
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_civoid fm10k_up(struct fm10k_intfc *interface)
183462306a36Sopenharmony_ci{
183562306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	/* Enable Tx/Rx DMA */
183862306a36Sopenharmony_ci	hw->mac.ops.start_hw(hw);
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci	/* configure Tx descriptor rings */
184162306a36Sopenharmony_ci	fm10k_configure_tx(interface);
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci	/* configure Rx descriptor rings */
184462306a36Sopenharmony_ci	fm10k_configure_rx(interface);
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci	/* configure interrupts */
184762306a36Sopenharmony_ci	hw->mac.ops.update_int_moderator(hw);
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci	/* enable statistics capture again */
185062306a36Sopenharmony_ci	clear_bit(__FM10K_UPDATING_STATS, interface->state);
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	/* clear down bit to indicate we are ready to go */
185362306a36Sopenharmony_ci	clear_bit(__FM10K_DOWN, interface->state);
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci	/* enable polling cleanups */
185662306a36Sopenharmony_ci	fm10k_napi_enable_all(interface);
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci	/* re-establish Rx filters */
185962306a36Sopenharmony_ci	fm10k_restore_rx_state(interface);
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_ci	/* enable transmits */
186262306a36Sopenharmony_ci	netif_tx_start_all_queues(interface->netdev);
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci	/* kick off the service timer now */
186562306a36Sopenharmony_ci	hw->mac.get_host_state = true;
186662306a36Sopenharmony_ci	mod_timer(&interface->service_timer, jiffies);
186762306a36Sopenharmony_ci}
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_cistatic void fm10k_napi_disable_all(struct fm10k_intfc *interface)
187062306a36Sopenharmony_ci{
187162306a36Sopenharmony_ci	struct fm10k_q_vector *q_vector;
187262306a36Sopenharmony_ci	int q_idx;
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_ci	for (q_idx = 0; q_idx < interface->num_q_vectors; q_idx++) {
187562306a36Sopenharmony_ci		q_vector = interface->q_vector[q_idx];
187662306a36Sopenharmony_ci		napi_disable(&q_vector->napi);
187762306a36Sopenharmony_ci	}
187862306a36Sopenharmony_ci}
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_civoid fm10k_down(struct fm10k_intfc *interface)
188162306a36Sopenharmony_ci{
188262306a36Sopenharmony_ci	struct net_device *netdev = interface->netdev;
188362306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
188462306a36Sopenharmony_ci	int err, i = 0, count = 0;
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci	/* signal that we are down to the interrupt handler and service task */
188762306a36Sopenharmony_ci	if (test_and_set_bit(__FM10K_DOWN, interface->state))
188862306a36Sopenharmony_ci		return;
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ci	/* call carrier off first to avoid false dev_watchdog timeouts */
189162306a36Sopenharmony_ci	netif_carrier_off(netdev);
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci	/* disable transmits */
189462306a36Sopenharmony_ci	netif_tx_stop_all_queues(netdev);
189562306a36Sopenharmony_ci	netif_tx_disable(netdev);
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci	/* reset Rx filters */
189862306a36Sopenharmony_ci	fm10k_reset_rx_state(interface);
189962306a36Sopenharmony_ci
190062306a36Sopenharmony_ci	/* disable polling routines */
190162306a36Sopenharmony_ci	fm10k_napi_disable_all(interface);
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ci	/* capture stats one last time before stopping interface */
190462306a36Sopenharmony_ci	fm10k_update_stats(interface);
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci	/* prevent updating statistics while we're down */
190762306a36Sopenharmony_ci	while (test_and_set_bit(__FM10K_UPDATING_STATS, interface->state))
190862306a36Sopenharmony_ci		usleep_range(1000, 2000);
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci	/* skip waiting for TX DMA if we lost PCIe link */
191162306a36Sopenharmony_ci	if (FM10K_REMOVED(hw->hw_addr))
191262306a36Sopenharmony_ci		goto skip_tx_dma_drain;
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ci	/* In some rare circumstances it can take a while for Tx queues to
191562306a36Sopenharmony_ci	 * quiesce and be fully disabled. Attempt to .stop_hw() first, and
191662306a36Sopenharmony_ci	 * then if we get ERR_REQUESTS_PENDING, go ahead and wait in a loop
191762306a36Sopenharmony_ci	 * until the Tx queues have emptied, or until a number of retries. If
191862306a36Sopenharmony_ci	 * we fail to clear within the retry loop, we will issue a warning
191962306a36Sopenharmony_ci	 * indicating that Tx DMA is probably hung. Note this means we call
192062306a36Sopenharmony_ci	 * .stop_hw() twice but this shouldn't cause any problems.
192162306a36Sopenharmony_ci	 */
192262306a36Sopenharmony_ci	err = hw->mac.ops.stop_hw(hw);
192362306a36Sopenharmony_ci	if (err != FM10K_ERR_REQUESTS_PENDING)
192462306a36Sopenharmony_ci		goto skip_tx_dma_drain;
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci#define TX_DMA_DRAIN_RETRIES 25
192762306a36Sopenharmony_ci	for (count = 0; count < TX_DMA_DRAIN_RETRIES; count++) {
192862306a36Sopenharmony_ci		usleep_range(10000, 20000);
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci		/* start checking at the last ring to have pending Tx */
193162306a36Sopenharmony_ci		for (; i < interface->num_tx_queues; i++)
193262306a36Sopenharmony_ci			if (fm10k_get_tx_pending(interface->tx_ring[i], false))
193362306a36Sopenharmony_ci				break;
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci		/* if all the queues are drained, we can break now */
193662306a36Sopenharmony_ci		if (i == interface->num_tx_queues)
193762306a36Sopenharmony_ci			break;
193862306a36Sopenharmony_ci	}
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_ci	if (count >= TX_DMA_DRAIN_RETRIES)
194162306a36Sopenharmony_ci		dev_err(&interface->pdev->dev,
194262306a36Sopenharmony_ci			"Tx queues failed to drain after %d tries. Tx DMA is probably hung.\n",
194362306a36Sopenharmony_ci			count);
194462306a36Sopenharmony_ciskip_tx_dma_drain:
194562306a36Sopenharmony_ci	/* Disable DMA engine for Tx/Rx */
194662306a36Sopenharmony_ci	err = hw->mac.ops.stop_hw(hw);
194762306a36Sopenharmony_ci	if (err == FM10K_ERR_REQUESTS_PENDING)
194862306a36Sopenharmony_ci		dev_err(&interface->pdev->dev,
194962306a36Sopenharmony_ci			"due to pending requests hw was not shut down gracefully\n");
195062306a36Sopenharmony_ci	else if (err)
195162306a36Sopenharmony_ci		dev_err(&interface->pdev->dev, "stop_hw failed: %d\n", err);
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci	/* free any buffers still on the rings */
195462306a36Sopenharmony_ci	fm10k_clean_all_tx_rings(interface);
195562306a36Sopenharmony_ci	fm10k_clean_all_rx_rings(interface);
195662306a36Sopenharmony_ci}
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci/**
195962306a36Sopenharmony_ci * fm10k_sw_init - Initialize general software structures
196062306a36Sopenharmony_ci * @interface: host interface private structure to initialize
196162306a36Sopenharmony_ci * @ent: PCI device ID entry
196262306a36Sopenharmony_ci *
196362306a36Sopenharmony_ci * fm10k_sw_init initializes the interface private data structure.
196462306a36Sopenharmony_ci * Fields are initialized based on PCI device information and
196562306a36Sopenharmony_ci * OS network device settings (MTU size).
196662306a36Sopenharmony_ci **/
196762306a36Sopenharmony_cistatic int fm10k_sw_init(struct fm10k_intfc *interface,
196862306a36Sopenharmony_ci			 const struct pci_device_id *ent)
196962306a36Sopenharmony_ci{
197062306a36Sopenharmony_ci	const struct fm10k_info *fi = fm10k_info_tbl[ent->driver_data];
197162306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
197262306a36Sopenharmony_ci	struct pci_dev *pdev = interface->pdev;
197362306a36Sopenharmony_ci	struct net_device *netdev = interface->netdev;
197462306a36Sopenharmony_ci	u32 rss_key[FM10K_RSSRK_SIZE];
197562306a36Sopenharmony_ci	unsigned int rss;
197662306a36Sopenharmony_ci	int err;
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci	/* initialize back pointer */
197962306a36Sopenharmony_ci	hw->back = interface;
198062306a36Sopenharmony_ci	hw->hw_addr = interface->uc_addr;
198162306a36Sopenharmony_ci
198262306a36Sopenharmony_ci	/* PCI config space info */
198362306a36Sopenharmony_ci	hw->vendor_id = pdev->vendor;
198462306a36Sopenharmony_ci	hw->device_id = pdev->device;
198562306a36Sopenharmony_ci	hw->revision_id = pdev->revision;
198662306a36Sopenharmony_ci	hw->subsystem_vendor_id = pdev->subsystem_vendor;
198762306a36Sopenharmony_ci	hw->subsystem_device_id = pdev->subsystem_device;
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_ci	/* Setup hw api */
199062306a36Sopenharmony_ci	memcpy(&hw->mac.ops, fi->mac_ops, sizeof(hw->mac.ops));
199162306a36Sopenharmony_ci	hw->mac.type = fi->mac;
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_ci	/* Setup IOV handlers */
199462306a36Sopenharmony_ci	if (fi->iov_ops)
199562306a36Sopenharmony_ci		memcpy(&hw->iov.ops, fi->iov_ops, sizeof(hw->iov.ops));
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_ci	/* Set common capability flags and settings */
199862306a36Sopenharmony_ci	rss = min_t(int, FM10K_MAX_RSS_INDICES, num_online_cpus());
199962306a36Sopenharmony_ci	interface->ring_feature[RING_F_RSS].limit = rss;
200062306a36Sopenharmony_ci	fi->get_invariants(hw);
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci	/* pick up the PCIe bus settings for reporting later */
200362306a36Sopenharmony_ci	if (hw->mac.ops.get_bus_info)
200462306a36Sopenharmony_ci		hw->mac.ops.get_bus_info(hw);
200562306a36Sopenharmony_ci
200662306a36Sopenharmony_ci	/* limit the usable DMA range */
200762306a36Sopenharmony_ci	if (hw->mac.ops.set_dma_mask)
200862306a36Sopenharmony_ci		hw->mac.ops.set_dma_mask(hw, dma_get_mask(&pdev->dev));
200962306a36Sopenharmony_ci
201062306a36Sopenharmony_ci	/* update netdev with DMA restrictions */
201162306a36Sopenharmony_ci	if (dma_get_mask(&pdev->dev) > DMA_BIT_MASK(32)) {
201262306a36Sopenharmony_ci		netdev->features |= NETIF_F_HIGHDMA;
201362306a36Sopenharmony_ci		netdev->vlan_features |= NETIF_F_HIGHDMA;
201462306a36Sopenharmony_ci	}
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_ci	/* reset and initialize the hardware so it is in a known state */
201762306a36Sopenharmony_ci	err = hw->mac.ops.reset_hw(hw);
201862306a36Sopenharmony_ci	if (err) {
201962306a36Sopenharmony_ci		dev_err(&pdev->dev, "reset_hw failed: %d\n", err);
202062306a36Sopenharmony_ci		return err;
202162306a36Sopenharmony_ci	}
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci	err = hw->mac.ops.init_hw(hw);
202462306a36Sopenharmony_ci	if (err) {
202562306a36Sopenharmony_ci		dev_err(&pdev->dev, "init_hw failed: %d\n", err);
202662306a36Sopenharmony_ci		return err;
202762306a36Sopenharmony_ci	}
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci	/* initialize hardware statistics */
203062306a36Sopenharmony_ci	hw->mac.ops.update_hw_stats(hw, &interface->stats);
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ci	/* Set upper limit on IOV VFs that can be allocated */
203362306a36Sopenharmony_ci	pci_sriov_set_totalvfs(pdev, hw->iov.total_vfs);
203462306a36Sopenharmony_ci
203562306a36Sopenharmony_ci	/* Start with random Ethernet address */
203662306a36Sopenharmony_ci	eth_random_addr(hw->mac.addr);
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_ci	/* Initialize MAC address from hardware */
203962306a36Sopenharmony_ci	err = hw->mac.ops.read_mac_addr(hw);
204062306a36Sopenharmony_ci	if (err) {
204162306a36Sopenharmony_ci		dev_warn(&pdev->dev,
204262306a36Sopenharmony_ci			 "Failed to obtain MAC address defaulting to random\n");
204362306a36Sopenharmony_ci		/* tag address assignment as random */
204462306a36Sopenharmony_ci		netdev->addr_assign_type |= NET_ADDR_RANDOM;
204562306a36Sopenharmony_ci	}
204662306a36Sopenharmony_ci
204762306a36Sopenharmony_ci	eth_hw_addr_set(netdev, hw->mac.addr);
204862306a36Sopenharmony_ci	ether_addr_copy(netdev->perm_addr, hw->mac.addr);
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_ci	if (!is_valid_ether_addr(netdev->perm_addr)) {
205162306a36Sopenharmony_ci		dev_err(&pdev->dev, "Invalid MAC Address\n");
205262306a36Sopenharmony_ci		return -EIO;
205362306a36Sopenharmony_ci	}
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci	/* initialize DCBNL interface */
205662306a36Sopenharmony_ci	fm10k_dcbnl_set_ops(netdev);
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_ci	/* set default ring sizes */
205962306a36Sopenharmony_ci	interface->tx_ring_count = FM10K_DEFAULT_TXD;
206062306a36Sopenharmony_ci	interface->rx_ring_count = FM10K_DEFAULT_RXD;
206162306a36Sopenharmony_ci
206262306a36Sopenharmony_ci	/* set default interrupt moderation */
206362306a36Sopenharmony_ci	interface->tx_itr = FM10K_TX_ITR_DEFAULT;
206462306a36Sopenharmony_ci	interface->rx_itr = FM10K_ITR_ADAPTIVE | FM10K_RX_ITR_DEFAULT;
206562306a36Sopenharmony_ci
206662306a36Sopenharmony_ci	/* Initialize the MAC/VLAN queue */
206762306a36Sopenharmony_ci	INIT_LIST_HEAD(&interface->macvlan_requests);
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	netdev_rss_key_fill(rss_key, sizeof(rss_key));
207062306a36Sopenharmony_ci	memcpy(interface->rssrk, rss_key, sizeof(rss_key));
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_ci	/* Initialize the mailbox lock */
207362306a36Sopenharmony_ci	spin_lock_init(&interface->mbx_lock);
207462306a36Sopenharmony_ci	spin_lock_init(&interface->macvlan_lock);
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_ci	/* Start off interface as being down */
207762306a36Sopenharmony_ci	set_bit(__FM10K_DOWN, interface->state);
207862306a36Sopenharmony_ci	set_bit(__FM10K_UPDATING_STATS, interface->state);
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci	return 0;
208162306a36Sopenharmony_ci}
208262306a36Sopenharmony_ci
208362306a36Sopenharmony_ci/**
208462306a36Sopenharmony_ci * fm10k_probe - Device Initialization Routine
208562306a36Sopenharmony_ci * @pdev: PCI device information struct
208662306a36Sopenharmony_ci * @ent: entry in fm10k_pci_tbl
208762306a36Sopenharmony_ci *
208862306a36Sopenharmony_ci * Returns 0 on success, negative on failure
208962306a36Sopenharmony_ci *
209062306a36Sopenharmony_ci * fm10k_probe initializes an interface identified by a pci_dev structure.
209162306a36Sopenharmony_ci * The OS initialization, configuring of the interface private structure,
209262306a36Sopenharmony_ci * and a hardware reset occur.
209362306a36Sopenharmony_ci **/
209462306a36Sopenharmony_cistatic int fm10k_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
209562306a36Sopenharmony_ci{
209662306a36Sopenharmony_ci	struct net_device *netdev;
209762306a36Sopenharmony_ci	struct fm10k_intfc *interface;
209862306a36Sopenharmony_ci	int err;
209962306a36Sopenharmony_ci
210062306a36Sopenharmony_ci	if (pdev->error_state != pci_channel_io_normal) {
210162306a36Sopenharmony_ci		dev_err(&pdev->dev,
210262306a36Sopenharmony_ci			"PCI device still in an error state. Unable to load...\n");
210362306a36Sopenharmony_ci		return -EIO;
210462306a36Sopenharmony_ci	}
210562306a36Sopenharmony_ci
210662306a36Sopenharmony_ci	err = pci_enable_device_mem(pdev);
210762306a36Sopenharmony_ci	if (err) {
210862306a36Sopenharmony_ci		dev_err(&pdev->dev,
210962306a36Sopenharmony_ci			"PCI enable device failed: %d\n", err);
211062306a36Sopenharmony_ci		return err;
211162306a36Sopenharmony_ci	}
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci	err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(48));
211462306a36Sopenharmony_ci	if (err)
211562306a36Sopenharmony_ci		err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
211662306a36Sopenharmony_ci	if (err) {
211762306a36Sopenharmony_ci		dev_err(&pdev->dev,
211862306a36Sopenharmony_ci			"DMA configuration failed: %d\n", err);
211962306a36Sopenharmony_ci		goto err_dma;
212062306a36Sopenharmony_ci	}
212162306a36Sopenharmony_ci
212262306a36Sopenharmony_ci	err = pci_request_mem_regions(pdev, fm10k_driver_name);
212362306a36Sopenharmony_ci	if (err) {
212462306a36Sopenharmony_ci		dev_err(&pdev->dev,
212562306a36Sopenharmony_ci			"pci_request_selected_regions failed: %d\n", err);
212662306a36Sopenharmony_ci		goto err_pci_reg;
212762306a36Sopenharmony_ci	}
212862306a36Sopenharmony_ci
212962306a36Sopenharmony_ci	pci_set_master(pdev);
213062306a36Sopenharmony_ci	pci_save_state(pdev);
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_ci	netdev = fm10k_alloc_netdev(fm10k_info_tbl[ent->driver_data]);
213362306a36Sopenharmony_ci	if (!netdev) {
213462306a36Sopenharmony_ci		err = -ENOMEM;
213562306a36Sopenharmony_ci		goto err_alloc_netdev;
213662306a36Sopenharmony_ci	}
213762306a36Sopenharmony_ci
213862306a36Sopenharmony_ci	SET_NETDEV_DEV(netdev, &pdev->dev);
213962306a36Sopenharmony_ci
214062306a36Sopenharmony_ci	interface = netdev_priv(netdev);
214162306a36Sopenharmony_ci	pci_set_drvdata(pdev, interface);
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_ci	interface->netdev = netdev;
214462306a36Sopenharmony_ci	interface->pdev = pdev;
214562306a36Sopenharmony_ci
214662306a36Sopenharmony_ci	interface->uc_addr = ioremap(pci_resource_start(pdev, 0),
214762306a36Sopenharmony_ci				     FM10K_UC_ADDR_SIZE);
214862306a36Sopenharmony_ci	if (!interface->uc_addr) {
214962306a36Sopenharmony_ci		err = -EIO;
215062306a36Sopenharmony_ci		goto err_ioremap;
215162306a36Sopenharmony_ci	}
215262306a36Sopenharmony_ci
215362306a36Sopenharmony_ci	err = fm10k_sw_init(interface, ent);
215462306a36Sopenharmony_ci	if (err)
215562306a36Sopenharmony_ci		goto err_sw_init;
215662306a36Sopenharmony_ci
215762306a36Sopenharmony_ci	/* enable debugfs support */
215862306a36Sopenharmony_ci	fm10k_dbg_intfc_init(interface);
215962306a36Sopenharmony_ci
216062306a36Sopenharmony_ci	err = fm10k_init_queueing_scheme(interface);
216162306a36Sopenharmony_ci	if (err)
216262306a36Sopenharmony_ci		goto err_sw_init;
216362306a36Sopenharmony_ci
216462306a36Sopenharmony_ci	/* the mbx interrupt might attempt to schedule the service task, so we
216562306a36Sopenharmony_ci	 * must ensure it is disabled since we haven't yet requested the timer
216662306a36Sopenharmony_ci	 * or work item.
216762306a36Sopenharmony_ci	 */
216862306a36Sopenharmony_ci	set_bit(__FM10K_SERVICE_DISABLE, interface->state);
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_ci	err = fm10k_mbx_request_irq(interface);
217162306a36Sopenharmony_ci	if (err)
217262306a36Sopenharmony_ci		goto err_mbx_interrupt;
217362306a36Sopenharmony_ci
217462306a36Sopenharmony_ci	/* final check of hardware state before registering the interface */
217562306a36Sopenharmony_ci	err = fm10k_hw_ready(interface);
217662306a36Sopenharmony_ci	if (err)
217762306a36Sopenharmony_ci		goto err_register;
217862306a36Sopenharmony_ci
217962306a36Sopenharmony_ci	err = register_netdev(netdev);
218062306a36Sopenharmony_ci	if (err)
218162306a36Sopenharmony_ci		goto err_register;
218262306a36Sopenharmony_ci
218362306a36Sopenharmony_ci	/* carrier off reporting is important to ethtool even BEFORE open */
218462306a36Sopenharmony_ci	netif_carrier_off(netdev);
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_ci	/* stop all the transmit queues from transmitting until link is up */
218762306a36Sopenharmony_ci	netif_tx_stop_all_queues(netdev);
218862306a36Sopenharmony_ci
218962306a36Sopenharmony_ci	/* Initialize service timer and service task late in order to avoid
219062306a36Sopenharmony_ci	 * cleanup issues.
219162306a36Sopenharmony_ci	 */
219262306a36Sopenharmony_ci	timer_setup(&interface->service_timer, fm10k_service_timer, 0);
219362306a36Sopenharmony_ci	INIT_WORK(&interface->service_task, fm10k_service_task);
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci	/* Setup the MAC/VLAN queue */
219662306a36Sopenharmony_ci	INIT_DELAYED_WORK(&interface->macvlan_task, fm10k_macvlan_task);
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci	/* kick off service timer now, even when interface is down */
219962306a36Sopenharmony_ci	mod_timer(&interface->service_timer, (HZ * 2) + jiffies);
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_ci	/* print warning for non-optimal configurations */
220262306a36Sopenharmony_ci	pcie_print_link_status(interface->pdev);
220362306a36Sopenharmony_ci
220462306a36Sopenharmony_ci	/* report MAC address for logging */
220562306a36Sopenharmony_ci	dev_info(&pdev->dev, "%pM\n", netdev->dev_addr);
220662306a36Sopenharmony_ci
220762306a36Sopenharmony_ci	/* enable SR-IOV after registering netdev to enforce PF/VF ordering */
220862306a36Sopenharmony_ci	fm10k_iov_configure(pdev, 0);
220962306a36Sopenharmony_ci
221062306a36Sopenharmony_ci	/* clear the service task disable bit and kick off service task */
221162306a36Sopenharmony_ci	clear_bit(__FM10K_SERVICE_DISABLE, interface->state);
221262306a36Sopenharmony_ci	fm10k_service_event_schedule(interface);
221362306a36Sopenharmony_ci
221462306a36Sopenharmony_ci	return 0;
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_cierr_register:
221762306a36Sopenharmony_ci	fm10k_mbx_free_irq(interface);
221862306a36Sopenharmony_cierr_mbx_interrupt:
221962306a36Sopenharmony_ci	fm10k_clear_queueing_scheme(interface);
222062306a36Sopenharmony_cierr_sw_init:
222162306a36Sopenharmony_ci	if (interface->sw_addr)
222262306a36Sopenharmony_ci		iounmap(interface->sw_addr);
222362306a36Sopenharmony_ci	iounmap(interface->uc_addr);
222462306a36Sopenharmony_cierr_ioremap:
222562306a36Sopenharmony_ci	free_netdev(netdev);
222662306a36Sopenharmony_cierr_alloc_netdev:
222762306a36Sopenharmony_ci	pci_release_mem_regions(pdev);
222862306a36Sopenharmony_cierr_pci_reg:
222962306a36Sopenharmony_cierr_dma:
223062306a36Sopenharmony_ci	pci_disable_device(pdev);
223162306a36Sopenharmony_ci	return err;
223262306a36Sopenharmony_ci}
223362306a36Sopenharmony_ci
223462306a36Sopenharmony_ci/**
223562306a36Sopenharmony_ci * fm10k_remove - Device Removal Routine
223662306a36Sopenharmony_ci * @pdev: PCI device information struct
223762306a36Sopenharmony_ci *
223862306a36Sopenharmony_ci * fm10k_remove is called by the PCI subsystem to alert the driver
223962306a36Sopenharmony_ci * that it should release a PCI device.  The could be caused by a
224062306a36Sopenharmony_ci * Hot-Plug event, or because the driver is going to be removed from
224162306a36Sopenharmony_ci * memory.
224262306a36Sopenharmony_ci **/
224362306a36Sopenharmony_cistatic void fm10k_remove(struct pci_dev *pdev)
224462306a36Sopenharmony_ci{
224562306a36Sopenharmony_ci	struct fm10k_intfc *interface = pci_get_drvdata(pdev);
224662306a36Sopenharmony_ci	struct net_device *netdev = interface->netdev;
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_ci	del_timer_sync(&interface->service_timer);
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_ci	fm10k_stop_service_event(interface);
225162306a36Sopenharmony_ci	fm10k_stop_macvlan_task(interface);
225262306a36Sopenharmony_ci
225362306a36Sopenharmony_ci	/* Remove all pending MAC/VLAN requests */
225462306a36Sopenharmony_ci	fm10k_clear_macvlan_queue(interface, interface->glort, true);
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_ci	/* free netdev, this may bounce the interrupts due to setup_tc */
225762306a36Sopenharmony_ci	if (netdev->reg_state == NETREG_REGISTERED)
225862306a36Sopenharmony_ci		unregister_netdev(netdev);
225962306a36Sopenharmony_ci
226062306a36Sopenharmony_ci	/* release VFs */
226162306a36Sopenharmony_ci	fm10k_iov_disable(pdev);
226262306a36Sopenharmony_ci
226362306a36Sopenharmony_ci	/* disable mailbox interrupt */
226462306a36Sopenharmony_ci	fm10k_mbx_free_irq(interface);
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci	/* free interrupts */
226762306a36Sopenharmony_ci	fm10k_clear_queueing_scheme(interface);
226862306a36Sopenharmony_ci
226962306a36Sopenharmony_ci	/* remove any debugfs interfaces */
227062306a36Sopenharmony_ci	fm10k_dbg_intfc_exit(interface);
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci	if (interface->sw_addr)
227362306a36Sopenharmony_ci		iounmap(interface->sw_addr);
227462306a36Sopenharmony_ci	iounmap(interface->uc_addr);
227562306a36Sopenharmony_ci
227662306a36Sopenharmony_ci	free_netdev(netdev);
227762306a36Sopenharmony_ci
227862306a36Sopenharmony_ci	pci_release_mem_regions(pdev);
227962306a36Sopenharmony_ci
228062306a36Sopenharmony_ci	pci_disable_device(pdev);
228162306a36Sopenharmony_ci}
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_cistatic void fm10k_prepare_suspend(struct fm10k_intfc *interface)
228462306a36Sopenharmony_ci{
228562306a36Sopenharmony_ci	/* the watchdog task reads from registers, which might appear like
228662306a36Sopenharmony_ci	 * a surprise remove if the PCIe device is disabled while we're
228762306a36Sopenharmony_ci	 * stopped. We stop the watchdog task until after we resume software
228862306a36Sopenharmony_ci	 * activity.
228962306a36Sopenharmony_ci	 *
229062306a36Sopenharmony_ci	 * Note that the MAC/VLAN task will be stopped as part of preparing
229162306a36Sopenharmony_ci	 * for reset so we don't need to handle it here.
229262306a36Sopenharmony_ci	 */
229362306a36Sopenharmony_ci	fm10k_stop_service_event(interface);
229462306a36Sopenharmony_ci
229562306a36Sopenharmony_ci	if (fm10k_prepare_for_reset(interface))
229662306a36Sopenharmony_ci		set_bit(__FM10K_RESET_SUSPENDED, interface->state);
229762306a36Sopenharmony_ci}
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_cistatic int fm10k_handle_resume(struct fm10k_intfc *interface)
230062306a36Sopenharmony_ci{
230162306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
230262306a36Sopenharmony_ci	int err;
230362306a36Sopenharmony_ci
230462306a36Sopenharmony_ci	/* Even if we didn't properly prepare for reset in
230562306a36Sopenharmony_ci	 * fm10k_prepare_suspend, we'll attempt to resume anyways.
230662306a36Sopenharmony_ci	 */
230762306a36Sopenharmony_ci	if (!test_and_clear_bit(__FM10K_RESET_SUSPENDED, interface->state))
230862306a36Sopenharmony_ci		dev_warn(&interface->pdev->dev,
230962306a36Sopenharmony_ci			 "Device was shut down as part of suspend... Attempting to recover\n");
231062306a36Sopenharmony_ci
231162306a36Sopenharmony_ci	/* reset statistics starting values */
231262306a36Sopenharmony_ci	hw->mac.ops.rebind_hw_stats(hw, &interface->stats);
231362306a36Sopenharmony_ci
231462306a36Sopenharmony_ci	err = fm10k_handle_reset(interface);
231562306a36Sopenharmony_ci	if (err)
231662306a36Sopenharmony_ci		return err;
231762306a36Sopenharmony_ci
231862306a36Sopenharmony_ci	/* assume host is not ready, to prevent race with watchdog in case we
231962306a36Sopenharmony_ci	 * actually don't have connection to the switch
232062306a36Sopenharmony_ci	 */
232162306a36Sopenharmony_ci	interface->host_ready = false;
232262306a36Sopenharmony_ci	fm10k_watchdog_host_not_ready(interface);
232362306a36Sopenharmony_ci
232462306a36Sopenharmony_ci	/* force link to stay down for a second to prevent link flutter */
232562306a36Sopenharmony_ci	interface->link_down_event = jiffies + (HZ);
232662306a36Sopenharmony_ci	set_bit(__FM10K_LINK_DOWN, interface->state);
232762306a36Sopenharmony_ci
232862306a36Sopenharmony_ci	/* restart the service task */
232962306a36Sopenharmony_ci	fm10k_start_service_event(interface);
233062306a36Sopenharmony_ci
233162306a36Sopenharmony_ci	/* Restart the MAC/VLAN request queue in-case of outstanding events */
233262306a36Sopenharmony_ci	fm10k_macvlan_schedule(interface);
233362306a36Sopenharmony_ci
233462306a36Sopenharmony_ci	return 0;
233562306a36Sopenharmony_ci}
233662306a36Sopenharmony_ci
233762306a36Sopenharmony_ci/**
233862306a36Sopenharmony_ci * fm10k_resume - Generic PM resume hook
233962306a36Sopenharmony_ci * @dev: generic device structure
234062306a36Sopenharmony_ci *
234162306a36Sopenharmony_ci * Generic PM hook used when waking the device from a low power state after
234262306a36Sopenharmony_ci * suspend or hibernation. This function does not need to handle lower PCIe
234362306a36Sopenharmony_ci * device state as the stack takes care of that for us.
234462306a36Sopenharmony_ci **/
234562306a36Sopenharmony_cistatic int __maybe_unused fm10k_resume(struct device *dev)
234662306a36Sopenharmony_ci{
234762306a36Sopenharmony_ci	struct fm10k_intfc *interface = dev_get_drvdata(dev);
234862306a36Sopenharmony_ci	struct net_device *netdev = interface->netdev;
234962306a36Sopenharmony_ci	struct fm10k_hw *hw = &interface->hw;
235062306a36Sopenharmony_ci	int err;
235162306a36Sopenharmony_ci
235262306a36Sopenharmony_ci	/* refresh hw_addr in case it was dropped */
235362306a36Sopenharmony_ci	hw->hw_addr = interface->uc_addr;
235462306a36Sopenharmony_ci
235562306a36Sopenharmony_ci	err = fm10k_handle_resume(interface);
235662306a36Sopenharmony_ci	if (err)
235762306a36Sopenharmony_ci		return err;
235862306a36Sopenharmony_ci
235962306a36Sopenharmony_ci	netif_device_attach(netdev);
236062306a36Sopenharmony_ci
236162306a36Sopenharmony_ci	return 0;
236262306a36Sopenharmony_ci}
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_ci/**
236562306a36Sopenharmony_ci * fm10k_suspend - Generic PM suspend hook
236662306a36Sopenharmony_ci * @dev: generic device structure
236762306a36Sopenharmony_ci *
236862306a36Sopenharmony_ci * Generic PM hook used when setting the device into a low power state for
236962306a36Sopenharmony_ci * system suspend or hibernation. This function does not need to handle lower
237062306a36Sopenharmony_ci * PCIe device state as the stack takes care of that for us.
237162306a36Sopenharmony_ci **/
237262306a36Sopenharmony_cistatic int __maybe_unused fm10k_suspend(struct device *dev)
237362306a36Sopenharmony_ci{
237462306a36Sopenharmony_ci	struct fm10k_intfc *interface = dev_get_drvdata(dev);
237562306a36Sopenharmony_ci	struct net_device *netdev = interface->netdev;
237662306a36Sopenharmony_ci
237762306a36Sopenharmony_ci	netif_device_detach(netdev);
237862306a36Sopenharmony_ci
237962306a36Sopenharmony_ci	fm10k_prepare_suspend(interface);
238062306a36Sopenharmony_ci
238162306a36Sopenharmony_ci	return 0;
238262306a36Sopenharmony_ci}
238362306a36Sopenharmony_ci
238462306a36Sopenharmony_ci/**
238562306a36Sopenharmony_ci * fm10k_io_error_detected - called when PCI error is detected
238662306a36Sopenharmony_ci * @pdev: Pointer to PCI device
238762306a36Sopenharmony_ci * @state: The current pci connection state
238862306a36Sopenharmony_ci *
238962306a36Sopenharmony_ci * This function is called after a PCI bus error affecting
239062306a36Sopenharmony_ci * this device has been detected.
239162306a36Sopenharmony_ci */
239262306a36Sopenharmony_cistatic pci_ers_result_t fm10k_io_error_detected(struct pci_dev *pdev,
239362306a36Sopenharmony_ci						pci_channel_state_t state)
239462306a36Sopenharmony_ci{
239562306a36Sopenharmony_ci	struct fm10k_intfc *interface = pci_get_drvdata(pdev);
239662306a36Sopenharmony_ci	struct net_device *netdev = interface->netdev;
239762306a36Sopenharmony_ci
239862306a36Sopenharmony_ci	netif_device_detach(netdev);
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_ci	if (state == pci_channel_io_perm_failure)
240162306a36Sopenharmony_ci		return PCI_ERS_RESULT_DISCONNECT;
240262306a36Sopenharmony_ci
240362306a36Sopenharmony_ci	fm10k_prepare_suspend(interface);
240462306a36Sopenharmony_ci
240562306a36Sopenharmony_ci	/* Request a slot reset. */
240662306a36Sopenharmony_ci	return PCI_ERS_RESULT_NEED_RESET;
240762306a36Sopenharmony_ci}
240862306a36Sopenharmony_ci
240962306a36Sopenharmony_ci/**
241062306a36Sopenharmony_ci * fm10k_io_slot_reset - called after the pci bus has been reset.
241162306a36Sopenharmony_ci * @pdev: Pointer to PCI device
241262306a36Sopenharmony_ci *
241362306a36Sopenharmony_ci * Restart the card from scratch, as if from a cold-boot.
241462306a36Sopenharmony_ci */
241562306a36Sopenharmony_cistatic pci_ers_result_t fm10k_io_slot_reset(struct pci_dev *pdev)
241662306a36Sopenharmony_ci{
241762306a36Sopenharmony_ci	pci_ers_result_t result;
241862306a36Sopenharmony_ci
241962306a36Sopenharmony_ci	if (pci_reenable_device(pdev)) {
242062306a36Sopenharmony_ci		dev_err(&pdev->dev,
242162306a36Sopenharmony_ci			"Cannot re-enable PCI device after reset.\n");
242262306a36Sopenharmony_ci		result = PCI_ERS_RESULT_DISCONNECT;
242362306a36Sopenharmony_ci	} else {
242462306a36Sopenharmony_ci		pci_set_master(pdev);
242562306a36Sopenharmony_ci		pci_restore_state(pdev);
242662306a36Sopenharmony_ci
242762306a36Sopenharmony_ci		/* After second error pci->state_saved is false, this
242862306a36Sopenharmony_ci		 * resets it so EEH doesn't break.
242962306a36Sopenharmony_ci		 */
243062306a36Sopenharmony_ci		pci_save_state(pdev);
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_ci		pci_wake_from_d3(pdev, false);
243362306a36Sopenharmony_ci
243462306a36Sopenharmony_ci		result = PCI_ERS_RESULT_RECOVERED;
243562306a36Sopenharmony_ci	}
243662306a36Sopenharmony_ci
243762306a36Sopenharmony_ci	return result;
243862306a36Sopenharmony_ci}
243962306a36Sopenharmony_ci
244062306a36Sopenharmony_ci/**
244162306a36Sopenharmony_ci * fm10k_io_resume - called when traffic can start flowing again.
244262306a36Sopenharmony_ci * @pdev: Pointer to PCI device
244362306a36Sopenharmony_ci *
244462306a36Sopenharmony_ci * This callback is called when the error recovery driver tells us that
244562306a36Sopenharmony_ci * its OK to resume normal operation.
244662306a36Sopenharmony_ci */
244762306a36Sopenharmony_cistatic void fm10k_io_resume(struct pci_dev *pdev)
244862306a36Sopenharmony_ci{
244962306a36Sopenharmony_ci	struct fm10k_intfc *interface = pci_get_drvdata(pdev);
245062306a36Sopenharmony_ci	struct net_device *netdev = interface->netdev;
245162306a36Sopenharmony_ci	int err;
245262306a36Sopenharmony_ci
245362306a36Sopenharmony_ci	err = fm10k_handle_resume(interface);
245462306a36Sopenharmony_ci
245562306a36Sopenharmony_ci	if (err)
245662306a36Sopenharmony_ci		dev_warn(&pdev->dev,
245762306a36Sopenharmony_ci			 "%s failed: %d\n", __func__, err);
245862306a36Sopenharmony_ci	else
245962306a36Sopenharmony_ci		netif_device_attach(netdev);
246062306a36Sopenharmony_ci}
246162306a36Sopenharmony_ci
246262306a36Sopenharmony_ci/**
246362306a36Sopenharmony_ci * fm10k_io_reset_prepare - called when PCI function is about to be reset
246462306a36Sopenharmony_ci * @pdev: Pointer to PCI device
246562306a36Sopenharmony_ci *
246662306a36Sopenharmony_ci * This callback is called when the PCI function is about to be reset,
246762306a36Sopenharmony_ci * allowing the device driver to prepare for it.
246862306a36Sopenharmony_ci */
246962306a36Sopenharmony_cistatic void fm10k_io_reset_prepare(struct pci_dev *pdev)
247062306a36Sopenharmony_ci{
247162306a36Sopenharmony_ci	/* warn incase we have any active VF devices */
247262306a36Sopenharmony_ci	if (pci_num_vf(pdev))
247362306a36Sopenharmony_ci		dev_warn(&pdev->dev,
247462306a36Sopenharmony_ci			 "PCIe FLR may cause issues for any active VF devices\n");
247562306a36Sopenharmony_ci	fm10k_prepare_suspend(pci_get_drvdata(pdev));
247662306a36Sopenharmony_ci}
247762306a36Sopenharmony_ci
247862306a36Sopenharmony_ci/**
247962306a36Sopenharmony_ci * fm10k_io_reset_done - called when PCI function has finished resetting
248062306a36Sopenharmony_ci * @pdev: Pointer to PCI device
248162306a36Sopenharmony_ci *
248262306a36Sopenharmony_ci * This callback is called just after the PCI function is reset, such as via
248362306a36Sopenharmony_ci * /sys/class/net/<enpX>/device/reset or similar.
248462306a36Sopenharmony_ci */
248562306a36Sopenharmony_cistatic void fm10k_io_reset_done(struct pci_dev *pdev)
248662306a36Sopenharmony_ci{
248762306a36Sopenharmony_ci	struct fm10k_intfc *interface = pci_get_drvdata(pdev);
248862306a36Sopenharmony_ci	int err = fm10k_handle_resume(interface);
248962306a36Sopenharmony_ci
249062306a36Sopenharmony_ci	if (err) {
249162306a36Sopenharmony_ci		dev_warn(&pdev->dev,
249262306a36Sopenharmony_ci			 "%s failed: %d\n", __func__, err);
249362306a36Sopenharmony_ci		netif_device_detach(interface->netdev);
249462306a36Sopenharmony_ci	}
249562306a36Sopenharmony_ci}
249662306a36Sopenharmony_ci
249762306a36Sopenharmony_cistatic const struct pci_error_handlers fm10k_err_handler = {
249862306a36Sopenharmony_ci	.error_detected = fm10k_io_error_detected,
249962306a36Sopenharmony_ci	.slot_reset = fm10k_io_slot_reset,
250062306a36Sopenharmony_ci	.resume = fm10k_io_resume,
250162306a36Sopenharmony_ci	.reset_prepare = fm10k_io_reset_prepare,
250262306a36Sopenharmony_ci	.reset_done = fm10k_io_reset_done,
250362306a36Sopenharmony_ci};
250462306a36Sopenharmony_ci
250562306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(fm10k_pm_ops, fm10k_suspend, fm10k_resume);
250662306a36Sopenharmony_ci
250762306a36Sopenharmony_cistatic struct pci_driver fm10k_driver = {
250862306a36Sopenharmony_ci	.name			= fm10k_driver_name,
250962306a36Sopenharmony_ci	.id_table		= fm10k_pci_tbl,
251062306a36Sopenharmony_ci	.probe			= fm10k_probe,
251162306a36Sopenharmony_ci	.remove			= fm10k_remove,
251262306a36Sopenharmony_ci	.driver = {
251362306a36Sopenharmony_ci		.pm		= &fm10k_pm_ops,
251462306a36Sopenharmony_ci	},
251562306a36Sopenharmony_ci	.sriov_configure	= fm10k_iov_configure,
251662306a36Sopenharmony_ci	.err_handler		= &fm10k_err_handler
251762306a36Sopenharmony_ci};
251862306a36Sopenharmony_ci
251962306a36Sopenharmony_ci/**
252062306a36Sopenharmony_ci * fm10k_register_pci_driver - register driver interface
252162306a36Sopenharmony_ci *
252262306a36Sopenharmony_ci * This function is called on module load in order to register the driver.
252362306a36Sopenharmony_ci **/
252462306a36Sopenharmony_ciint fm10k_register_pci_driver(void)
252562306a36Sopenharmony_ci{
252662306a36Sopenharmony_ci	return pci_register_driver(&fm10k_driver);
252762306a36Sopenharmony_ci}
252862306a36Sopenharmony_ci
252962306a36Sopenharmony_ci/**
253062306a36Sopenharmony_ci * fm10k_unregister_pci_driver - unregister driver interface
253162306a36Sopenharmony_ci *
253262306a36Sopenharmony_ci * This function is called on module unload in order to remove the driver.
253362306a36Sopenharmony_ci **/
253462306a36Sopenharmony_civoid fm10k_unregister_pci_driver(void)
253562306a36Sopenharmony_ci{
253662306a36Sopenharmony_ci	pci_unregister_driver(&fm10k_driver);
253762306a36Sopenharmony_ci}
2538