162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  linux/drivers/net/ethernet/ibm/ehea/ehea_main.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  eHEA ethernet device driver for IBM eServer System p
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  (C) Copyright IBM Corp. 2006
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *  Authors:
1062306a36Sopenharmony_ci *	 Christoph Raisch <raisch@de.ibm.com>
1162306a36Sopenharmony_ci *	 Jan-Bernd Themann <themann@de.ibm.com>
1262306a36Sopenharmony_ci *	 Thomas Klein <tklein@de.ibm.com>
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/device.h>
1862306a36Sopenharmony_ci#include <linux/in.h>
1962306a36Sopenharmony_ci#include <linux/ip.h>
2062306a36Sopenharmony_ci#include <linux/tcp.h>
2162306a36Sopenharmony_ci#include <linux/udp.h>
2262306a36Sopenharmony_ci#include <linux/if.h>
2362306a36Sopenharmony_ci#include <linux/list.h>
2462306a36Sopenharmony_ci#include <linux/slab.h>
2562306a36Sopenharmony_ci#include <linux/if_ether.h>
2662306a36Sopenharmony_ci#include <linux/notifier.h>
2762306a36Sopenharmony_ci#include <linux/reboot.h>
2862306a36Sopenharmony_ci#include <linux/memory.h>
2962306a36Sopenharmony_ci#include <asm/kexec.h>
3062306a36Sopenharmony_ci#include <linux/mutex.h>
3162306a36Sopenharmony_ci#include <linux/prefetch.h>
3262306a36Sopenharmony_ci#include <linux/of.h>
3362306a36Sopenharmony_ci#include <linux/of_device.h>
3462306a36Sopenharmony_ci#include <linux/platform_device.h>
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#include <net/ip.h>
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#include "ehea.h"
3962306a36Sopenharmony_ci#include "ehea_qmr.h"
4062306a36Sopenharmony_ci#include "ehea_phyp.h"
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
4462306a36Sopenharmony_ciMODULE_AUTHOR("Christoph Raisch <raisch@de.ibm.com>");
4562306a36Sopenharmony_ciMODULE_DESCRIPTION("IBM eServer HEA Driver");
4662306a36Sopenharmony_ciMODULE_VERSION(DRV_VERSION);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic int msg_level = -1;
5062306a36Sopenharmony_cistatic int rq1_entries = EHEA_DEF_ENTRIES_RQ1;
5162306a36Sopenharmony_cistatic int rq2_entries = EHEA_DEF_ENTRIES_RQ2;
5262306a36Sopenharmony_cistatic int rq3_entries = EHEA_DEF_ENTRIES_RQ3;
5362306a36Sopenharmony_cistatic int sq_entries = EHEA_DEF_ENTRIES_SQ;
5462306a36Sopenharmony_cistatic int use_mcs = 1;
5562306a36Sopenharmony_cistatic int prop_carrier_state;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cimodule_param(msg_level, int, 0);
5862306a36Sopenharmony_cimodule_param(rq1_entries, int, 0);
5962306a36Sopenharmony_cimodule_param(rq2_entries, int, 0);
6062306a36Sopenharmony_cimodule_param(rq3_entries, int, 0);
6162306a36Sopenharmony_cimodule_param(sq_entries, int, 0);
6262306a36Sopenharmony_cimodule_param(prop_carrier_state, int, 0);
6362306a36Sopenharmony_cimodule_param(use_mcs, int, 0);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ciMODULE_PARM_DESC(msg_level, "msg_level");
6662306a36Sopenharmony_ciMODULE_PARM_DESC(prop_carrier_state, "Propagate carrier state of physical "
6762306a36Sopenharmony_ci		 "port to stack. 1:yes, 0:no.  Default = 0 ");
6862306a36Sopenharmony_ciMODULE_PARM_DESC(rq3_entries, "Number of entries for Receive Queue 3 "
6962306a36Sopenharmony_ci		 "[2^x - 1], x = [7..14]. Default = "
7062306a36Sopenharmony_ci		 __MODULE_STRING(EHEA_DEF_ENTRIES_RQ3) ")");
7162306a36Sopenharmony_ciMODULE_PARM_DESC(rq2_entries, "Number of entries for Receive Queue 2 "
7262306a36Sopenharmony_ci		 "[2^x - 1], x = [7..14]. Default = "
7362306a36Sopenharmony_ci		 __MODULE_STRING(EHEA_DEF_ENTRIES_RQ2) ")");
7462306a36Sopenharmony_ciMODULE_PARM_DESC(rq1_entries, "Number of entries for Receive Queue 1 "
7562306a36Sopenharmony_ci		 "[2^x - 1], x = [7..14]. Default = "
7662306a36Sopenharmony_ci		 __MODULE_STRING(EHEA_DEF_ENTRIES_RQ1) ")");
7762306a36Sopenharmony_ciMODULE_PARM_DESC(sq_entries, " Number of entries for the Send Queue  "
7862306a36Sopenharmony_ci		 "[2^x - 1], x = [7..14]. Default = "
7962306a36Sopenharmony_ci		 __MODULE_STRING(EHEA_DEF_ENTRIES_SQ) ")");
8062306a36Sopenharmony_ciMODULE_PARM_DESC(use_mcs, " Multiple receive queues, 1: enable, 0: disable, "
8162306a36Sopenharmony_ci		 "Default = 1");
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic int port_name_cnt;
8462306a36Sopenharmony_cistatic LIST_HEAD(adapter_list);
8562306a36Sopenharmony_cistatic unsigned long ehea_driver_flags;
8662306a36Sopenharmony_cistatic DEFINE_MUTEX(dlpar_mem_lock);
8762306a36Sopenharmony_cistatic struct ehea_fw_handle_array ehea_fw_handles;
8862306a36Sopenharmony_cistatic struct ehea_bcmc_reg_array ehea_bcmc_regs;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic int ehea_probe_adapter(struct platform_device *dev);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic int ehea_remove(struct platform_device *dev);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic const struct of_device_id ehea_module_device_table[] = {
9662306a36Sopenharmony_ci	{
9762306a36Sopenharmony_ci		.name = "lhea",
9862306a36Sopenharmony_ci		.compatible = "IBM,lhea",
9962306a36Sopenharmony_ci	},
10062306a36Sopenharmony_ci	{
10162306a36Sopenharmony_ci		.type = "network",
10262306a36Sopenharmony_ci		.compatible = "IBM,lhea-ethernet",
10362306a36Sopenharmony_ci	},
10462306a36Sopenharmony_ci	{},
10562306a36Sopenharmony_ci};
10662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ehea_module_device_table);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic const struct of_device_id ehea_device_table[] = {
10962306a36Sopenharmony_ci	{
11062306a36Sopenharmony_ci		.name = "lhea",
11162306a36Sopenharmony_ci		.compatible = "IBM,lhea",
11262306a36Sopenharmony_ci	},
11362306a36Sopenharmony_ci	{},
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ehea_device_table);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic struct platform_driver ehea_driver = {
11862306a36Sopenharmony_ci	.driver = {
11962306a36Sopenharmony_ci		.name = "ehea",
12062306a36Sopenharmony_ci		.owner = THIS_MODULE,
12162306a36Sopenharmony_ci		.of_match_table = ehea_device_table,
12262306a36Sopenharmony_ci	},
12362306a36Sopenharmony_ci	.probe = ehea_probe_adapter,
12462306a36Sopenharmony_ci	.remove = ehea_remove,
12562306a36Sopenharmony_ci};
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_civoid ehea_dump(void *adr, int len, char *msg)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	int x;
13062306a36Sopenharmony_ci	unsigned char *deb = adr;
13162306a36Sopenharmony_ci	for (x = 0; x < len; x += 16) {
13262306a36Sopenharmony_ci		pr_info("%s adr=%p ofs=%04x %016llx %016llx\n",
13362306a36Sopenharmony_ci			msg, deb, x, *((u64 *)&deb[0]), *((u64 *)&deb[8]));
13462306a36Sopenharmony_ci		deb += 16;
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic void ehea_schedule_port_reset(struct ehea_port *port)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	if (!test_bit(__EHEA_DISABLE_PORT_RESET, &port->flags))
14162306a36Sopenharmony_ci		schedule_work(&port->reset_task);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic void ehea_update_firmware_handles(void)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct ehea_fw_handle_entry *arr = NULL;
14762306a36Sopenharmony_ci	struct ehea_adapter *adapter;
14862306a36Sopenharmony_ci	int num_adapters = 0;
14962306a36Sopenharmony_ci	int num_ports = 0;
15062306a36Sopenharmony_ci	int num_portres = 0;
15162306a36Sopenharmony_ci	int i = 0;
15262306a36Sopenharmony_ci	int num_fw_handles, k, l;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	/* Determine number of handles */
15562306a36Sopenharmony_ci	mutex_lock(&ehea_fw_handles.lock);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	list_for_each_entry(adapter, &adapter_list, list) {
15862306a36Sopenharmony_ci		num_adapters++;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci		for (k = 0; k < EHEA_MAX_PORTS; k++) {
16162306a36Sopenharmony_ci			struct ehea_port *port = adapter->port[k];
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci			if (!port || (port->state != EHEA_PORT_UP))
16462306a36Sopenharmony_ci				continue;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci			num_ports++;
16762306a36Sopenharmony_ci			num_portres += port->num_def_qps;
16862306a36Sopenharmony_ci		}
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	num_fw_handles = num_adapters * EHEA_NUM_ADAPTER_FW_HANDLES +
17262306a36Sopenharmony_ci			 num_ports * EHEA_NUM_PORT_FW_HANDLES +
17362306a36Sopenharmony_ci			 num_portres * EHEA_NUM_PORTRES_FW_HANDLES;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (num_fw_handles) {
17662306a36Sopenharmony_ci		arr = kcalloc(num_fw_handles, sizeof(*arr), GFP_KERNEL);
17762306a36Sopenharmony_ci		if (!arr)
17862306a36Sopenharmony_ci			goto out;  /* Keep the existing array */
17962306a36Sopenharmony_ci	} else
18062306a36Sopenharmony_ci		goto out_update;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	list_for_each_entry(adapter, &adapter_list, list) {
18362306a36Sopenharmony_ci		if (num_adapters == 0)
18462306a36Sopenharmony_ci			break;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci		for (k = 0; k < EHEA_MAX_PORTS; k++) {
18762306a36Sopenharmony_ci			struct ehea_port *port = adapter->port[k];
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci			if (!port || (port->state != EHEA_PORT_UP) ||
19062306a36Sopenharmony_ci			    (num_ports == 0))
19162306a36Sopenharmony_ci				continue;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci			for (l = 0; l < port->num_def_qps; l++) {
19462306a36Sopenharmony_ci				struct ehea_port_res *pr = &port->port_res[l];
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci				arr[i].adh = adapter->handle;
19762306a36Sopenharmony_ci				arr[i++].fwh = pr->qp->fw_handle;
19862306a36Sopenharmony_ci				arr[i].adh = adapter->handle;
19962306a36Sopenharmony_ci				arr[i++].fwh = pr->send_cq->fw_handle;
20062306a36Sopenharmony_ci				arr[i].adh = adapter->handle;
20162306a36Sopenharmony_ci				arr[i++].fwh = pr->recv_cq->fw_handle;
20262306a36Sopenharmony_ci				arr[i].adh = adapter->handle;
20362306a36Sopenharmony_ci				arr[i++].fwh = pr->eq->fw_handle;
20462306a36Sopenharmony_ci				arr[i].adh = adapter->handle;
20562306a36Sopenharmony_ci				arr[i++].fwh = pr->send_mr.handle;
20662306a36Sopenharmony_ci				arr[i].adh = adapter->handle;
20762306a36Sopenharmony_ci				arr[i++].fwh = pr->recv_mr.handle;
20862306a36Sopenharmony_ci			}
20962306a36Sopenharmony_ci			arr[i].adh = adapter->handle;
21062306a36Sopenharmony_ci			arr[i++].fwh = port->qp_eq->fw_handle;
21162306a36Sopenharmony_ci			num_ports--;
21262306a36Sopenharmony_ci		}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci		arr[i].adh = adapter->handle;
21562306a36Sopenharmony_ci		arr[i++].fwh = adapter->neq->fw_handle;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci		if (adapter->mr.handle) {
21862306a36Sopenharmony_ci			arr[i].adh = adapter->handle;
21962306a36Sopenharmony_ci			arr[i++].fwh = adapter->mr.handle;
22062306a36Sopenharmony_ci		}
22162306a36Sopenharmony_ci		num_adapters--;
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ciout_update:
22562306a36Sopenharmony_ci	kfree(ehea_fw_handles.arr);
22662306a36Sopenharmony_ci	ehea_fw_handles.arr = arr;
22762306a36Sopenharmony_ci	ehea_fw_handles.num_entries = i;
22862306a36Sopenharmony_ciout:
22962306a36Sopenharmony_ci	mutex_unlock(&ehea_fw_handles.lock);
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic void ehea_update_bcmc_registrations(void)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	unsigned long flags;
23562306a36Sopenharmony_ci	struct ehea_bcmc_reg_entry *arr = NULL;
23662306a36Sopenharmony_ci	struct ehea_adapter *adapter;
23762306a36Sopenharmony_ci	struct ehea_mc_list *mc_entry;
23862306a36Sopenharmony_ci	int num_registrations = 0;
23962306a36Sopenharmony_ci	int i = 0;
24062306a36Sopenharmony_ci	int k;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	spin_lock_irqsave(&ehea_bcmc_regs.lock, flags);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/* Determine number of registrations */
24562306a36Sopenharmony_ci	list_for_each_entry(adapter, &adapter_list, list)
24662306a36Sopenharmony_ci		for (k = 0; k < EHEA_MAX_PORTS; k++) {
24762306a36Sopenharmony_ci			struct ehea_port *port = adapter->port[k];
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci			if (!port || (port->state != EHEA_PORT_UP))
25062306a36Sopenharmony_ci				continue;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci			num_registrations += 2;	/* Broadcast registrations */
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci			list_for_each_entry(mc_entry, &port->mc_list->list,list)
25562306a36Sopenharmony_ci				num_registrations += 2;
25662306a36Sopenharmony_ci		}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	if (num_registrations) {
25962306a36Sopenharmony_ci		arr = kcalloc(num_registrations, sizeof(*arr), GFP_ATOMIC);
26062306a36Sopenharmony_ci		if (!arr)
26162306a36Sopenharmony_ci			goto out;  /* Keep the existing array */
26262306a36Sopenharmony_ci	} else
26362306a36Sopenharmony_ci		goto out_update;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	list_for_each_entry(adapter, &adapter_list, list) {
26662306a36Sopenharmony_ci		for (k = 0; k < EHEA_MAX_PORTS; k++) {
26762306a36Sopenharmony_ci			struct ehea_port *port = adapter->port[k];
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci			if (!port || (port->state != EHEA_PORT_UP))
27062306a36Sopenharmony_ci				continue;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci			if (num_registrations == 0)
27362306a36Sopenharmony_ci				goto out_update;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci			arr[i].adh = adapter->handle;
27662306a36Sopenharmony_ci			arr[i].port_id = port->logical_port_id;
27762306a36Sopenharmony_ci			arr[i].reg_type = EHEA_BCMC_BROADCAST |
27862306a36Sopenharmony_ci					  EHEA_BCMC_UNTAGGED;
27962306a36Sopenharmony_ci			arr[i++].macaddr = port->mac_addr;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci			arr[i].adh = adapter->handle;
28262306a36Sopenharmony_ci			arr[i].port_id = port->logical_port_id;
28362306a36Sopenharmony_ci			arr[i].reg_type = EHEA_BCMC_BROADCAST |
28462306a36Sopenharmony_ci					  EHEA_BCMC_VLANID_ALL;
28562306a36Sopenharmony_ci			arr[i++].macaddr = port->mac_addr;
28662306a36Sopenharmony_ci			num_registrations -= 2;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci			list_for_each_entry(mc_entry,
28962306a36Sopenharmony_ci					    &port->mc_list->list, list) {
29062306a36Sopenharmony_ci				if (num_registrations == 0)
29162306a36Sopenharmony_ci					goto out_update;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci				arr[i].adh = adapter->handle;
29462306a36Sopenharmony_ci				arr[i].port_id = port->logical_port_id;
29562306a36Sopenharmony_ci				arr[i].reg_type = EHEA_BCMC_MULTICAST |
29662306a36Sopenharmony_ci						  EHEA_BCMC_UNTAGGED;
29762306a36Sopenharmony_ci				if (mc_entry->macaddr == 0)
29862306a36Sopenharmony_ci					arr[i].reg_type |= EHEA_BCMC_SCOPE_ALL;
29962306a36Sopenharmony_ci				arr[i++].macaddr = mc_entry->macaddr;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci				arr[i].adh = adapter->handle;
30262306a36Sopenharmony_ci				arr[i].port_id = port->logical_port_id;
30362306a36Sopenharmony_ci				arr[i].reg_type = EHEA_BCMC_MULTICAST |
30462306a36Sopenharmony_ci						  EHEA_BCMC_VLANID_ALL;
30562306a36Sopenharmony_ci				if (mc_entry->macaddr == 0)
30662306a36Sopenharmony_ci					arr[i].reg_type |= EHEA_BCMC_SCOPE_ALL;
30762306a36Sopenharmony_ci				arr[i++].macaddr = mc_entry->macaddr;
30862306a36Sopenharmony_ci				num_registrations -= 2;
30962306a36Sopenharmony_ci			}
31062306a36Sopenharmony_ci		}
31162306a36Sopenharmony_ci	}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ciout_update:
31462306a36Sopenharmony_ci	kfree(ehea_bcmc_regs.arr);
31562306a36Sopenharmony_ci	ehea_bcmc_regs.arr = arr;
31662306a36Sopenharmony_ci	ehea_bcmc_regs.num_entries = i;
31762306a36Sopenharmony_ciout:
31862306a36Sopenharmony_ci	spin_unlock_irqrestore(&ehea_bcmc_regs.lock, flags);
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic void ehea_get_stats64(struct net_device *dev,
32262306a36Sopenharmony_ci			     struct rtnl_link_stats64 *stats)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
32562306a36Sopenharmony_ci	u64 rx_packets = 0, tx_packets = 0, rx_bytes = 0, tx_bytes = 0;
32662306a36Sopenharmony_ci	int i;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	for (i = 0; i < port->num_def_qps; i++) {
32962306a36Sopenharmony_ci		rx_packets += port->port_res[i].rx_packets;
33062306a36Sopenharmony_ci		rx_bytes   += port->port_res[i].rx_bytes;
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	for (i = 0; i < port->num_def_qps; i++) {
33462306a36Sopenharmony_ci		tx_packets += port->port_res[i].tx_packets;
33562306a36Sopenharmony_ci		tx_bytes   += port->port_res[i].tx_bytes;
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	stats->tx_packets = tx_packets;
33962306a36Sopenharmony_ci	stats->rx_bytes = rx_bytes;
34062306a36Sopenharmony_ci	stats->tx_bytes = tx_bytes;
34162306a36Sopenharmony_ci	stats->rx_packets = rx_packets;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	stats->multicast = port->stats.multicast;
34462306a36Sopenharmony_ci	stats->rx_errors = port->stats.rx_errors;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic void ehea_update_stats(struct work_struct *work)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	struct ehea_port *port =
35062306a36Sopenharmony_ci		container_of(work, struct ehea_port, stats_work.work);
35162306a36Sopenharmony_ci	struct net_device *dev = port->netdev;
35262306a36Sopenharmony_ci	struct rtnl_link_stats64 *stats = &port->stats;
35362306a36Sopenharmony_ci	struct hcp_ehea_port_cb2 *cb2;
35462306a36Sopenharmony_ci	u64 hret;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	cb2 = (void *)get_zeroed_page(GFP_KERNEL);
35762306a36Sopenharmony_ci	if (!cb2) {
35862306a36Sopenharmony_ci		netdev_err(dev, "No mem for cb2. Some interface statistics were not updated\n");
35962306a36Sopenharmony_ci		goto resched;
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	hret = ehea_h_query_ehea_port(port->adapter->handle,
36362306a36Sopenharmony_ci				      port->logical_port_id,
36462306a36Sopenharmony_ci				      H_PORT_CB2, H_PORT_CB2_ALL, cb2);
36562306a36Sopenharmony_ci	if (hret != H_SUCCESS) {
36662306a36Sopenharmony_ci		netdev_err(dev, "query_ehea_port failed\n");
36762306a36Sopenharmony_ci		goto out_herr;
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	if (netif_msg_hw(port))
37162306a36Sopenharmony_ci		ehea_dump(cb2, sizeof(*cb2), "net_device_stats");
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	stats->multicast = cb2->rxmcp;
37462306a36Sopenharmony_ci	stats->rx_errors = cb2->rxuerr;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ciout_herr:
37762306a36Sopenharmony_ci	free_page((unsigned long)cb2);
37862306a36Sopenharmony_ciresched:
37962306a36Sopenharmony_ci	schedule_delayed_work(&port->stats_work,
38062306a36Sopenharmony_ci			      round_jiffies_relative(msecs_to_jiffies(1000)));
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic void ehea_refill_rq1(struct ehea_port_res *pr, int index, int nr_of_wqes)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	struct sk_buff **skb_arr_rq1 = pr->rq1_skba.arr;
38662306a36Sopenharmony_ci	struct net_device *dev = pr->port->netdev;
38762306a36Sopenharmony_ci	int max_index_mask = pr->rq1_skba.len - 1;
38862306a36Sopenharmony_ci	int fill_wqes = pr->rq1_skba.os_skbs + nr_of_wqes;
38962306a36Sopenharmony_ci	int adder = 0;
39062306a36Sopenharmony_ci	int i;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	pr->rq1_skba.os_skbs = 0;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	if (unlikely(test_bit(__EHEA_STOP_XFER, &ehea_driver_flags))) {
39562306a36Sopenharmony_ci		if (nr_of_wqes > 0)
39662306a36Sopenharmony_ci			pr->rq1_skba.index = index;
39762306a36Sopenharmony_ci		pr->rq1_skba.os_skbs = fill_wqes;
39862306a36Sopenharmony_ci		return;
39962306a36Sopenharmony_ci	}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	for (i = 0; i < fill_wqes; i++) {
40262306a36Sopenharmony_ci		if (!skb_arr_rq1[index]) {
40362306a36Sopenharmony_ci			skb_arr_rq1[index] = netdev_alloc_skb(dev,
40462306a36Sopenharmony_ci							      EHEA_L_PKT_SIZE);
40562306a36Sopenharmony_ci			if (!skb_arr_rq1[index]) {
40662306a36Sopenharmony_ci				pr->rq1_skba.os_skbs = fill_wqes - i;
40762306a36Sopenharmony_ci				break;
40862306a36Sopenharmony_ci			}
40962306a36Sopenharmony_ci		}
41062306a36Sopenharmony_ci		index--;
41162306a36Sopenharmony_ci		index &= max_index_mask;
41262306a36Sopenharmony_ci		adder++;
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	if (adder == 0)
41662306a36Sopenharmony_ci		return;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	/* Ring doorbell */
41962306a36Sopenharmony_ci	ehea_update_rq1a(pr->qp, adder);
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic void ehea_init_fill_rq1(struct ehea_port_res *pr, int nr_rq1a)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	struct sk_buff **skb_arr_rq1 = pr->rq1_skba.arr;
42562306a36Sopenharmony_ci	struct net_device *dev = pr->port->netdev;
42662306a36Sopenharmony_ci	int i;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	if (nr_rq1a > pr->rq1_skba.len) {
42962306a36Sopenharmony_ci		netdev_err(dev, "NR_RQ1A bigger than skb array len\n");
43062306a36Sopenharmony_ci		return;
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	for (i = 0; i < nr_rq1a; i++) {
43462306a36Sopenharmony_ci		skb_arr_rq1[i] = netdev_alloc_skb(dev, EHEA_L_PKT_SIZE);
43562306a36Sopenharmony_ci		if (!skb_arr_rq1[i])
43662306a36Sopenharmony_ci			break;
43762306a36Sopenharmony_ci	}
43862306a36Sopenharmony_ci	/* Ring doorbell */
43962306a36Sopenharmony_ci	ehea_update_rq1a(pr->qp, i - 1);
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic int ehea_refill_rq_def(struct ehea_port_res *pr,
44362306a36Sopenharmony_ci			      struct ehea_q_skb_arr *q_skba, int rq_nr,
44462306a36Sopenharmony_ci			      int num_wqes, int wqe_type, int packet_size)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	struct net_device *dev = pr->port->netdev;
44762306a36Sopenharmony_ci	struct ehea_qp *qp = pr->qp;
44862306a36Sopenharmony_ci	struct sk_buff **skb_arr = q_skba->arr;
44962306a36Sopenharmony_ci	struct ehea_rwqe *rwqe;
45062306a36Sopenharmony_ci	int i, index, max_index_mask, fill_wqes;
45162306a36Sopenharmony_ci	int adder = 0;
45262306a36Sopenharmony_ci	int ret = 0;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	fill_wqes = q_skba->os_skbs + num_wqes;
45562306a36Sopenharmony_ci	q_skba->os_skbs = 0;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	if (unlikely(test_bit(__EHEA_STOP_XFER, &ehea_driver_flags))) {
45862306a36Sopenharmony_ci		q_skba->os_skbs = fill_wqes;
45962306a36Sopenharmony_ci		return ret;
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	index = q_skba->index;
46362306a36Sopenharmony_ci	max_index_mask = q_skba->len - 1;
46462306a36Sopenharmony_ci	for (i = 0; i < fill_wqes; i++) {
46562306a36Sopenharmony_ci		u64 tmp_addr;
46662306a36Sopenharmony_ci		struct sk_buff *skb;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci		skb = netdev_alloc_skb_ip_align(dev, packet_size);
46962306a36Sopenharmony_ci		if (!skb) {
47062306a36Sopenharmony_ci			q_skba->os_skbs = fill_wqes - i;
47162306a36Sopenharmony_ci			if (q_skba->os_skbs == q_skba->len - 2) {
47262306a36Sopenharmony_ci				netdev_info(pr->port->netdev,
47362306a36Sopenharmony_ci					    "rq%i ran dry - no mem for skb\n",
47462306a36Sopenharmony_ci					    rq_nr);
47562306a36Sopenharmony_ci				ret = -ENOMEM;
47662306a36Sopenharmony_ci			}
47762306a36Sopenharmony_ci			break;
47862306a36Sopenharmony_ci		}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci		skb_arr[index] = skb;
48162306a36Sopenharmony_ci		tmp_addr = ehea_map_vaddr(skb->data);
48262306a36Sopenharmony_ci		if (tmp_addr == -1) {
48362306a36Sopenharmony_ci			dev_consume_skb_any(skb);
48462306a36Sopenharmony_ci			q_skba->os_skbs = fill_wqes - i;
48562306a36Sopenharmony_ci			ret = 0;
48662306a36Sopenharmony_ci			break;
48762306a36Sopenharmony_ci		}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci		rwqe = ehea_get_next_rwqe(qp, rq_nr);
49062306a36Sopenharmony_ci		rwqe->wr_id = EHEA_BMASK_SET(EHEA_WR_ID_TYPE, wqe_type)
49162306a36Sopenharmony_ci			    | EHEA_BMASK_SET(EHEA_WR_ID_INDEX, index);
49262306a36Sopenharmony_ci		rwqe->sg_list[0].l_key = pr->recv_mr.lkey;
49362306a36Sopenharmony_ci		rwqe->sg_list[0].vaddr = tmp_addr;
49462306a36Sopenharmony_ci		rwqe->sg_list[0].len = packet_size;
49562306a36Sopenharmony_ci		rwqe->data_segments = 1;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci		index++;
49862306a36Sopenharmony_ci		index &= max_index_mask;
49962306a36Sopenharmony_ci		adder++;
50062306a36Sopenharmony_ci	}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	q_skba->index = index;
50362306a36Sopenharmony_ci	if (adder == 0)
50462306a36Sopenharmony_ci		goto out;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	/* Ring doorbell */
50762306a36Sopenharmony_ci	iosync();
50862306a36Sopenharmony_ci	if (rq_nr == 2)
50962306a36Sopenharmony_ci		ehea_update_rq2a(pr->qp, adder);
51062306a36Sopenharmony_ci	else
51162306a36Sopenharmony_ci		ehea_update_rq3a(pr->qp, adder);
51262306a36Sopenharmony_ciout:
51362306a36Sopenharmony_ci	return ret;
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_cistatic int ehea_refill_rq2(struct ehea_port_res *pr, int nr_of_wqes)
51862306a36Sopenharmony_ci{
51962306a36Sopenharmony_ci	return ehea_refill_rq_def(pr, &pr->rq2_skba, 2,
52062306a36Sopenharmony_ci				  nr_of_wqes, EHEA_RWQE2_TYPE,
52162306a36Sopenharmony_ci				  EHEA_RQ2_PKT_SIZE);
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_cistatic int ehea_refill_rq3(struct ehea_port_res *pr, int nr_of_wqes)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	return ehea_refill_rq_def(pr, &pr->rq3_skba, 3,
52862306a36Sopenharmony_ci				  nr_of_wqes, EHEA_RWQE3_TYPE,
52962306a36Sopenharmony_ci				  EHEA_MAX_PACKET_SIZE);
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic inline int ehea_check_cqe(struct ehea_cqe *cqe, int *rq_num)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	*rq_num = (cqe->type & EHEA_CQE_TYPE_RQ) >> 5;
53562306a36Sopenharmony_ci	if ((cqe->status & EHEA_CQE_STAT_ERR_MASK) == 0)
53662306a36Sopenharmony_ci		return 0;
53762306a36Sopenharmony_ci	if (((cqe->status & EHEA_CQE_STAT_ERR_TCP) != 0) &&
53862306a36Sopenharmony_ci	    (cqe->header_length == 0))
53962306a36Sopenharmony_ci		return 0;
54062306a36Sopenharmony_ci	return -EINVAL;
54162306a36Sopenharmony_ci}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_cistatic inline void ehea_fill_skb(struct net_device *dev,
54462306a36Sopenharmony_ci				 struct sk_buff *skb, struct ehea_cqe *cqe,
54562306a36Sopenharmony_ci				 struct ehea_port_res *pr)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	int length = cqe->num_bytes_transfered - 4;	/*remove CRC */
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	skb_put(skb, length);
55062306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, dev);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	/* The packet was not an IPV4 packet so a complemented checksum was
55362306a36Sopenharmony_ci	   calculated. The value is found in the Internet Checksum field. */
55462306a36Sopenharmony_ci	if (cqe->status & EHEA_CQE_BLIND_CKSUM) {
55562306a36Sopenharmony_ci		skb->ip_summed = CHECKSUM_COMPLETE;
55662306a36Sopenharmony_ci		skb->csum = csum_unfold(~cqe->inet_checksum_value);
55762306a36Sopenharmony_ci	} else
55862306a36Sopenharmony_ci		skb->ip_summed = CHECKSUM_UNNECESSARY;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	skb_record_rx_queue(skb, pr - &pr->port->port_res[0]);
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_cistatic inline struct sk_buff *get_skb_by_index(struct sk_buff **skb_array,
56462306a36Sopenharmony_ci					       int arr_len,
56562306a36Sopenharmony_ci					       struct ehea_cqe *cqe)
56662306a36Sopenharmony_ci{
56762306a36Sopenharmony_ci	int skb_index = EHEA_BMASK_GET(EHEA_WR_ID_INDEX, cqe->wr_id);
56862306a36Sopenharmony_ci	struct sk_buff *skb;
56962306a36Sopenharmony_ci	void *pref;
57062306a36Sopenharmony_ci	int x;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	x = skb_index + 1;
57362306a36Sopenharmony_ci	x &= (arr_len - 1);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	pref = skb_array[x];
57662306a36Sopenharmony_ci	if (pref) {
57762306a36Sopenharmony_ci		prefetchw(pref);
57862306a36Sopenharmony_ci		prefetchw(pref + EHEA_CACHE_LINE);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci		pref = (skb_array[x]->data);
58162306a36Sopenharmony_ci		prefetch(pref);
58262306a36Sopenharmony_ci		prefetch(pref + EHEA_CACHE_LINE);
58362306a36Sopenharmony_ci		prefetch(pref + EHEA_CACHE_LINE * 2);
58462306a36Sopenharmony_ci		prefetch(pref + EHEA_CACHE_LINE * 3);
58562306a36Sopenharmony_ci	}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	skb = skb_array[skb_index];
58862306a36Sopenharmony_ci	skb_array[skb_index] = NULL;
58962306a36Sopenharmony_ci	return skb;
59062306a36Sopenharmony_ci}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_cistatic inline struct sk_buff *get_skb_by_index_ll(struct sk_buff **skb_array,
59362306a36Sopenharmony_ci						  int arr_len, int wqe_index)
59462306a36Sopenharmony_ci{
59562306a36Sopenharmony_ci	struct sk_buff *skb;
59662306a36Sopenharmony_ci	void *pref;
59762306a36Sopenharmony_ci	int x;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	x = wqe_index + 1;
60062306a36Sopenharmony_ci	x &= (arr_len - 1);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	pref = skb_array[x];
60362306a36Sopenharmony_ci	if (pref) {
60462306a36Sopenharmony_ci		prefetchw(pref);
60562306a36Sopenharmony_ci		prefetchw(pref + EHEA_CACHE_LINE);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci		pref = (skb_array[x]->data);
60862306a36Sopenharmony_ci		prefetchw(pref);
60962306a36Sopenharmony_ci		prefetchw(pref + EHEA_CACHE_LINE);
61062306a36Sopenharmony_ci	}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	skb = skb_array[wqe_index];
61362306a36Sopenharmony_ci	skb_array[wqe_index] = NULL;
61462306a36Sopenharmony_ci	return skb;
61562306a36Sopenharmony_ci}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_cistatic int ehea_treat_poll_error(struct ehea_port_res *pr, int rq,
61862306a36Sopenharmony_ci				 struct ehea_cqe *cqe, int *processed_rq2,
61962306a36Sopenharmony_ci				 int *processed_rq3)
62062306a36Sopenharmony_ci{
62162306a36Sopenharmony_ci	struct sk_buff *skb;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	if (cqe->status & EHEA_CQE_STAT_ERR_TCP)
62462306a36Sopenharmony_ci		pr->p_stats.err_tcp_cksum++;
62562306a36Sopenharmony_ci	if (cqe->status & EHEA_CQE_STAT_ERR_IP)
62662306a36Sopenharmony_ci		pr->p_stats.err_ip_cksum++;
62762306a36Sopenharmony_ci	if (cqe->status & EHEA_CQE_STAT_ERR_CRC)
62862306a36Sopenharmony_ci		pr->p_stats.err_frame_crc++;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	if (rq == 2) {
63162306a36Sopenharmony_ci		*processed_rq2 += 1;
63262306a36Sopenharmony_ci		skb = get_skb_by_index(pr->rq2_skba.arr, pr->rq2_skba.len, cqe);
63362306a36Sopenharmony_ci		dev_kfree_skb(skb);
63462306a36Sopenharmony_ci	} else if (rq == 3) {
63562306a36Sopenharmony_ci		*processed_rq3 += 1;
63662306a36Sopenharmony_ci		skb = get_skb_by_index(pr->rq3_skba.arr, pr->rq3_skba.len, cqe);
63762306a36Sopenharmony_ci		dev_kfree_skb(skb);
63862306a36Sopenharmony_ci	}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	if (cqe->status & EHEA_CQE_STAT_FAT_ERR_MASK) {
64162306a36Sopenharmony_ci		if (netif_msg_rx_err(pr->port)) {
64262306a36Sopenharmony_ci			pr_err("Critical receive error for QP %d. Resetting port.\n",
64362306a36Sopenharmony_ci			       pr->qp->init_attr.qp_nr);
64462306a36Sopenharmony_ci			ehea_dump(cqe, sizeof(*cqe), "CQE");
64562306a36Sopenharmony_ci		}
64662306a36Sopenharmony_ci		ehea_schedule_port_reset(pr->port);
64762306a36Sopenharmony_ci		return 1;
64862306a36Sopenharmony_ci	}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	return 0;
65162306a36Sopenharmony_ci}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_cistatic int ehea_proc_rwqes(struct net_device *dev,
65462306a36Sopenharmony_ci			   struct ehea_port_res *pr,
65562306a36Sopenharmony_ci			   int budget)
65662306a36Sopenharmony_ci{
65762306a36Sopenharmony_ci	struct ehea_port *port = pr->port;
65862306a36Sopenharmony_ci	struct ehea_qp *qp = pr->qp;
65962306a36Sopenharmony_ci	struct ehea_cqe *cqe;
66062306a36Sopenharmony_ci	struct sk_buff *skb;
66162306a36Sopenharmony_ci	struct sk_buff **skb_arr_rq1 = pr->rq1_skba.arr;
66262306a36Sopenharmony_ci	struct sk_buff **skb_arr_rq2 = pr->rq2_skba.arr;
66362306a36Sopenharmony_ci	struct sk_buff **skb_arr_rq3 = pr->rq3_skba.arr;
66462306a36Sopenharmony_ci	int skb_arr_rq1_len = pr->rq1_skba.len;
66562306a36Sopenharmony_ci	int skb_arr_rq2_len = pr->rq2_skba.len;
66662306a36Sopenharmony_ci	int skb_arr_rq3_len = pr->rq3_skba.len;
66762306a36Sopenharmony_ci	int processed, processed_rq1, processed_rq2, processed_rq3;
66862306a36Sopenharmony_ci	u64 processed_bytes = 0;
66962306a36Sopenharmony_ci	int wqe_index, last_wqe_index, rq, port_reset;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	processed = processed_rq1 = processed_rq2 = processed_rq3 = 0;
67262306a36Sopenharmony_ci	last_wqe_index = 0;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	cqe = ehea_poll_rq1(qp, &wqe_index);
67562306a36Sopenharmony_ci	while ((processed < budget) && cqe) {
67662306a36Sopenharmony_ci		ehea_inc_rq1(qp);
67762306a36Sopenharmony_ci		processed_rq1++;
67862306a36Sopenharmony_ci		processed++;
67962306a36Sopenharmony_ci		if (netif_msg_rx_status(port))
68062306a36Sopenharmony_ci			ehea_dump(cqe, sizeof(*cqe), "CQE");
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci		last_wqe_index = wqe_index;
68362306a36Sopenharmony_ci		rmb();
68462306a36Sopenharmony_ci		if (!ehea_check_cqe(cqe, &rq)) {
68562306a36Sopenharmony_ci			if (rq == 1) {
68662306a36Sopenharmony_ci				/* LL RQ1 */
68762306a36Sopenharmony_ci				skb = get_skb_by_index_ll(skb_arr_rq1,
68862306a36Sopenharmony_ci							  skb_arr_rq1_len,
68962306a36Sopenharmony_ci							  wqe_index);
69062306a36Sopenharmony_ci				if (unlikely(!skb)) {
69162306a36Sopenharmony_ci					netif_info(port, rx_err, dev,
69262306a36Sopenharmony_ci						  "LL rq1: skb=NULL\n");
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci					skb = netdev_alloc_skb(dev,
69562306a36Sopenharmony_ci							       EHEA_L_PKT_SIZE);
69662306a36Sopenharmony_ci					if (!skb)
69762306a36Sopenharmony_ci						break;
69862306a36Sopenharmony_ci				}
69962306a36Sopenharmony_ci				skb_copy_to_linear_data(skb, ((char *)cqe) + 64,
70062306a36Sopenharmony_ci						 cqe->num_bytes_transfered - 4);
70162306a36Sopenharmony_ci				ehea_fill_skb(dev, skb, cqe, pr);
70262306a36Sopenharmony_ci			} else if (rq == 2) {
70362306a36Sopenharmony_ci				/* RQ2 */
70462306a36Sopenharmony_ci				skb = get_skb_by_index(skb_arr_rq2,
70562306a36Sopenharmony_ci						       skb_arr_rq2_len, cqe);
70662306a36Sopenharmony_ci				if (unlikely(!skb)) {
70762306a36Sopenharmony_ci					netif_err(port, rx_err, dev,
70862306a36Sopenharmony_ci						  "rq2: skb=NULL\n");
70962306a36Sopenharmony_ci					break;
71062306a36Sopenharmony_ci				}
71162306a36Sopenharmony_ci				ehea_fill_skb(dev, skb, cqe, pr);
71262306a36Sopenharmony_ci				processed_rq2++;
71362306a36Sopenharmony_ci			} else {
71462306a36Sopenharmony_ci				/* RQ3 */
71562306a36Sopenharmony_ci				skb = get_skb_by_index(skb_arr_rq3,
71662306a36Sopenharmony_ci						       skb_arr_rq3_len, cqe);
71762306a36Sopenharmony_ci				if (unlikely(!skb)) {
71862306a36Sopenharmony_ci					netif_err(port, rx_err, dev,
71962306a36Sopenharmony_ci						  "rq3: skb=NULL\n");
72062306a36Sopenharmony_ci					break;
72162306a36Sopenharmony_ci				}
72262306a36Sopenharmony_ci				ehea_fill_skb(dev, skb, cqe, pr);
72362306a36Sopenharmony_ci				processed_rq3++;
72462306a36Sopenharmony_ci			}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci			processed_bytes += skb->len;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci			if (cqe->status & EHEA_CQE_VLAN_TAG_XTRACT)
72962306a36Sopenharmony_ci				__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
73062306a36Sopenharmony_ci						       cqe->vlan_tag);
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci			napi_gro_receive(&pr->napi, skb);
73362306a36Sopenharmony_ci		} else {
73462306a36Sopenharmony_ci			pr->p_stats.poll_receive_errors++;
73562306a36Sopenharmony_ci			port_reset = ehea_treat_poll_error(pr, rq, cqe,
73662306a36Sopenharmony_ci							   &processed_rq2,
73762306a36Sopenharmony_ci							   &processed_rq3);
73862306a36Sopenharmony_ci			if (port_reset)
73962306a36Sopenharmony_ci				break;
74062306a36Sopenharmony_ci		}
74162306a36Sopenharmony_ci		cqe = ehea_poll_rq1(qp, &wqe_index);
74262306a36Sopenharmony_ci	}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	pr->rx_packets += processed;
74562306a36Sopenharmony_ci	pr->rx_bytes += processed_bytes;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	ehea_refill_rq1(pr, last_wqe_index, processed_rq1);
74862306a36Sopenharmony_ci	ehea_refill_rq2(pr, processed_rq2);
74962306a36Sopenharmony_ci	ehea_refill_rq3(pr, processed_rq3);
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	return processed;
75262306a36Sopenharmony_ci}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci#define SWQE_RESTART_CHECK 0xdeadbeaff00d0000ull
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_cistatic void reset_sq_restart_flag(struct ehea_port *port)
75762306a36Sopenharmony_ci{
75862306a36Sopenharmony_ci	int i;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	for (i = 0; i < port->num_def_qps; i++) {
76162306a36Sopenharmony_ci		struct ehea_port_res *pr = &port->port_res[i];
76262306a36Sopenharmony_ci		pr->sq_restart_flag = 0;
76362306a36Sopenharmony_ci	}
76462306a36Sopenharmony_ci	wake_up(&port->restart_wq);
76562306a36Sopenharmony_ci}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_cistatic void check_sqs(struct ehea_port *port)
76862306a36Sopenharmony_ci{
76962306a36Sopenharmony_ci	struct ehea_swqe *swqe;
77062306a36Sopenharmony_ci	int swqe_index;
77162306a36Sopenharmony_ci	int i;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	for (i = 0; i < port->num_def_qps; i++) {
77462306a36Sopenharmony_ci		struct ehea_port_res *pr = &port->port_res[i];
77562306a36Sopenharmony_ci		int ret;
77662306a36Sopenharmony_ci		swqe = ehea_get_swqe(pr->qp, &swqe_index);
77762306a36Sopenharmony_ci		memset(swqe, 0, SWQE_HEADER_SIZE);
77862306a36Sopenharmony_ci		atomic_dec(&pr->swqe_avail);
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci		swqe->tx_control |= EHEA_SWQE_PURGE;
78162306a36Sopenharmony_ci		swqe->wr_id = SWQE_RESTART_CHECK;
78262306a36Sopenharmony_ci		swqe->tx_control |= EHEA_SWQE_SIGNALLED_COMPLETION;
78362306a36Sopenharmony_ci		swqe->tx_control |= EHEA_SWQE_IMM_DATA_PRESENT;
78462306a36Sopenharmony_ci		swqe->immediate_data_length = 80;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci		ehea_post_swqe(pr->qp, swqe);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci		ret = wait_event_timeout(port->restart_wq,
78962306a36Sopenharmony_ci					 pr->sq_restart_flag == 0,
79062306a36Sopenharmony_ci					 msecs_to_jiffies(100));
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci		if (!ret) {
79362306a36Sopenharmony_ci			pr_err("HW/SW queues out of sync\n");
79462306a36Sopenharmony_ci			ehea_schedule_port_reset(pr->port);
79562306a36Sopenharmony_ci			return;
79662306a36Sopenharmony_ci		}
79762306a36Sopenharmony_ci	}
79862306a36Sopenharmony_ci}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_cistatic struct ehea_cqe *ehea_proc_cqes(struct ehea_port_res *pr, int my_quota)
80262306a36Sopenharmony_ci{
80362306a36Sopenharmony_ci	struct sk_buff *skb;
80462306a36Sopenharmony_ci	struct ehea_cq *send_cq = pr->send_cq;
80562306a36Sopenharmony_ci	struct ehea_cqe *cqe;
80662306a36Sopenharmony_ci	int quota = my_quota;
80762306a36Sopenharmony_ci	int cqe_counter = 0;
80862306a36Sopenharmony_ci	int swqe_av = 0;
80962306a36Sopenharmony_ci	int index;
81062306a36Sopenharmony_ci	struct netdev_queue *txq = netdev_get_tx_queue(pr->port->netdev,
81162306a36Sopenharmony_ci						pr - &pr->port->port_res[0]);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	cqe = ehea_poll_cq(send_cq);
81462306a36Sopenharmony_ci	while (cqe && (quota > 0)) {
81562306a36Sopenharmony_ci		ehea_inc_cq(send_cq);
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci		cqe_counter++;
81862306a36Sopenharmony_ci		rmb();
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci		if (cqe->wr_id == SWQE_RESTART_CHECK) {
82162306a36Sopenharmony_ci			pr->sq_restart_flag = 1;
82262306a36Sopenharmony_ci			swqe_av++;
82362306a36Sopenharmony_ci			break;
82462306a36Sopenharmony_ci		}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci		if (cqe->status & EHEA_CQE_STAT_ERR_MASK) {
82762306a36Sopenharmony_ci			pr_err("Bad send completion status=0x%04X\n",
82862306a36Sopenharmony_ci			       cqe->status);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci			if (netif_msg_tx_err(pr->port))
83162306a36Sopenharmony_ci				ehea_dump(cqe, sizeof(*cqe), "Send CQE");
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci			if (cqe->status & EHEA_CQE_STAT_RESET_MASK) {
83462306a36Sopenharmony_ci				pr_err("Resetting port\n");
83562306a36Sopenharmony_ci				ehea_schedule_port_reset(pr->port);
83662306a36Sopenharmony_ci				break;
83762306a36Sopenharmony_ci			}
83862306a36Sopenharmony_ci		}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci		if (netif_msg_tx_done(pr->port))
84162306a36Sopenharmony_ci			ehea_dump(cqe, sizeof(*cqe), "CQE");
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci		if (likely(EHEA_BMASK_GET(EHEA_WR_ID_TYPE, cqe->wr_id)
84462306a36Sopenharmony_ci			   == EHEA_SWQE2_TYPE)) {
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci			index = EHEA_BMASK_GET(EHEA_WR_ID_INDEX, cqe->wr_id);
84762306a36Sopenharmony_ci			skb = pr->sq_skba.arr[index];
84862306a36Sopenharmony_ci			dev_consume_skb_any(skb);
84962306a36Sopenharmony_ci			pr->sq_skba.arr[index] = NULL;
85062306a36Sopenharmony_ci		}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci		swqe_av += EHEA_BMASK_GET(EHEA_WR_ID_REFILL, cqe->wr_id);
85362306a36Sopenharmony_ci		quota--;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci		cqe = ehea_poll_cq(send_cq);
85662306a36Sopenharmony_ci	}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	ehea_update_feca(send_cq, cqe_counter);
85962306a36Sopenharmony_ci	atomic_add(swqe_av, &pr->swqe_avail);
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	if (unlikely(netif_tx_queue_stopped(txq) &&
86262306a36Sopenharmony_ci		     (atomic_read(&pr->swqe_avail) >= pr->swqe_refill_th))) {
86362306a36Sopenharmony_ci		__netif_tx_lock(txq, smp_processor_id());
86462306a36Sopenharmony_ci		if (netif_tx_queue_stopped(txq) &&
86562306a36Sopenharmony_ci		    (atomic_read(&pr->swqe_avail) >= pr->swqe_refill_th))
86662306a36Sopenharmony_ci			netif_tx_wake_queue(txq);
86762306a36Sopenharmony_ci		__netif_tx_unlock(txq);
86862306a36Sopenharmony_ci	}
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	wake_up(&pr->port->swqe_avail_wq);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	return cqe;
87362306a36Sopenharmony_ci}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci#define EHEA_POLL_MAX_CQES 65535
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_cistatic int ehea_poll(struct napi_struct *napi, int budget)
87862306a36Sopenharmony_ci{
87962306a36Sopenharmony_ci	struct ehea_port_res *pr = container_of(napi, struct ehea_port_res,
88062306a36Sopenharmony_ci						napi);
88162306a36Sopenharmony_ci	struct net_device *dev = pr->port->netdev;
88262306a36Sopenharmony_ci	struct ehea_cqe *cqe;
88362306a36Sopenharmony_ci	struct ehea_cqe *cqe_skb = NULL;
88462306a36Sopenharmony_ci	int wqe_index;
88562306a36Sopenharmony_ci	int rx = 0;
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	cqe_skb = ehea_proc_cqes(pr, EHEA_POLL_MAX_CQES);
88862306a36Sopenharmony_ci	rx += ehea_proc_rwqes(dev, pr, budget - rx);
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	while (rx != budget) {
89162306a36Sopenharmony_ci		napi_complete(napi);
89262306a36Sopenharmony_ci		ehea_reset_cq_ep(pr->recv_cq);
89362306a36Sopenharmony_ci		ehea_reset_cq_ep(pr->send_cq);
89462306a36Sopenharmony_ci		ehea_reset_cq_n1(pr->recv_cq);
89562306a36Sopenharmony_ci		ehea_reset_cq_n1(pr->send_cq);
89662306a36Sopenharmony_ci		rmb();
89762306a36Sopenharmony_ci		cqe = ehea_poll_rq1(pr->qp, &wqe_index);
89862306a36Sopenharmony_ci		cqe_skb = ehea_poll_cq(pr->send_cq);
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci		if (!cqe && !cqe_skb)
90162306a36Sopenharmony_ci			return rx;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci		if (!napi_reschedule(napi))
90462306a36Sopenharmony_ci			return rx;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci		cqe_skb = ehea_proc_cqes(pr, EHEA_POLL_MAX_CQES);
90762306a36Sopenharmony_ci		rx += ehea_proc_rwqes(dev, pr, budget - rx);
90862306a36Sopenharmony_ci	}
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	return rx;
91162306a36Sopenharmony_ci}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_cistatic irqreturn_t ehea_recv_irq_handler(int irq, void *param)
91462306a36Sopenharmony_ci{
91562306a36Sopenharmony_ci	struct ehea_port_res *pr = param;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	napi_schedule(&pr->napi);
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	return IRQ_HANDLED;
92062306a36Sopenharmony_ci}
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_cistatic irqreturn_t ehea_qp_aff_irq_handler(int irq, void *param)
92362306a36Sopenharmony_ci{
92462306a36Sopenharmony_ci	struct ehea_port *port = param;
92562306a36Sopenharmony_ci	struct ehea_eqe *eqe;
92662306a36Sopenharmony_ci	struct ehea_qp *qp;
92762306a36Sopenharmony_ci	u32 qp_token;
92862306a36Sopenharmony_ci	u64 resource_type, aer, aerr;
92962306a36Sopenharmony_ci	int reset_port = 0;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	eqe = ehea_poll_eq(port->qp_eq);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	while (eqe) {
93462306a36Sopenharmony_ci		qp_token = EHEA_BMASK_GET(EHEA_EQE_QP_TOKEN, eqe->entry);
93562306a36Sopenharmony_ci		pr_err("QP aff_err: entry=0x%llx, token=0x%x\n",
93662306a36Sopenharmony_ci		       eqe->entry, qp_token);
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci		qp = port->port_res[qp_token].qp;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci		resource_type = ehea_error_data(port->adapter, qp->fw_handle,
94162306a36Sopenharmony_ci						&aer, &aerr);
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci		if (resource_type == EHEA_AER_RESTYPE_QP) {
94462306a36Sopenharmony_ci			if ((aer & EHEA_AER_RESET_MASK) ||
94562306a36Sopenharmony_ci			    (aerr & EHEA_AERR_RESET_MASK))
94662306a36Sopenharmony_ci				 reset_port = 1;
94762306a36Sopenharmony_ci		} else
94862306a36Sopenharmony_ci			reset_port = 1;   /* Reset in case of CQ or EQ error */
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci		eqe = ehea_poll_eq(port->qp_eq);
95162306a36Sopenharmony_ci	}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	if (reset_port) {
95462306a36Sopenharmony_ci		pr_err("Resetting port\n");
95562306a36Sopenharmony_ci		ehea_schedule_port_reset(port);
95662306a36Sopenharmony_ci	}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	return IRQ_HANDLED;
95962306a36Sopenharmony_ci}
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_cistatic struct ehea_port *ehea_get_port(struct ehea_adapter *adapter,
96262306a36Sopenharmony_ci				       int logical_port)
96362306a36Sopenharmony_ci{
96462306a36Sopenharmony_ci	int i;
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	for (i = 0; i < EHEA_MAX_PORTS; i++)
96762306a36Sopenharmony_ci		if (adapter->port[i])
96862306a36Sopenharmony_ci			if (adapter->port[i]->logical_port_id == logical_port)
96962306a36Sopenharmony_ci				return adapter->port[i];
97062306a36Sopenharmony_ci	return NULL;
97162306a36Sopenharmony_ci}
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ciint ehea_sense_port_attr(struct ehea_port *port)
97462306a36Sopenharmony_ci{
97562306a36Sopenharmony_ci	int ret;
97662306a36Sopenharmony_ci	u64 hret;
97762306a36Sopenharmony_ci	struct hcp_ehea_port_cb0 *cb0;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	/* may be called via ehea_neq_tasklet() */
98062306a36Sopenharmony_ci	cb0 = (void *)get_zeroed_page(GFP_ATOMIC);
98162306a36Sopenharmony_ci	if (!cb0) {
98262306a36Sopenharmony_ci		pr_err("no mem for cb0\n");
98362306a36Sopenharmony_ci		ret = -ENOMEM;
98462306a36Sopenharmony_ci		goto out;
98562306a36Sopenharmony_ci	}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	hret = ehea_h_query_ehea_port(port->adapter->handle,
98862306a36Sopenharmony_ci				      port->logical_port_id, H_PORT_CB0,
98962306a36Sopenharmony_ci				      EHEA_BMASK_SET(H_PORT_CB0_ALL, 0xFFFF),
99062306a36Sopenharmony_ci				      cb0);
99162306a36Sopenharmony_ci	if (hret != H_SUCCESS) {
99262306a36Sopenharmony_ci		ret = -EIO;
99362306a36Sopenharmony_ci		goto out_free;
99462306a36Sopenharmony_ci	}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	/* MAC address */
99762306a36Sopenharmony_ci	port->mac_addr = cb0->port_mac_addr << 16;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	if (!is_valid_ether_addr((u8 *)&port->mac_addr)) {
100062306a36Sopenharmony_ci		ret = -EADDRNOTAVAIL;
100162306a36Sopenharmony_ci		goto out_free;
100262306a36Sopenharmony_ci	}
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	/* Port speed */
100562306a36Sopenharmony_ci	switch (cb0->port_speed) {
100662306a36Sopenharmony_ci	case H_SPEED_10M_H:
100762306a36Sopenharmony_ci		port->port_speed = EHEA_SPEED_10M;
100862306a36Sopenharmony_ci		port->full_duplex = 0;
100962306a36Sopenharmony_ci		break;
101062306a36Sopenharmony_ci	case H_SPEED_10M_F:
101162306a36Sopenharmony_ci		port->port_speed = EHEA_SPEED_10M;
101262306a36Sopenharmony_ci		port->full_duplex = 1;
101362306a36Sopenharmony_ci		break;
101462306a36Sopenharmony_ci	case H_SPEED_100M_H:
101562306a36Sopenharmony_ci		port->port_speed = EHEA_SPEED_100M;
101662306a36Sopenharmony_ci		port->full_duplex = 0;
101762306a36Sopenharmony_ci		break;
101862306a36Sopenharmony_ci	case H_SPEED_100M_F:
101962306a36Sopenharmony_ci		port->port_speed = EHEA_SPEED_100M;
102062306a36Sopenharmony_ci		port->full_duplex = 1;
102162306a36Sopenharmony_ci		break;
102262306a36Sopenharmony_ci	case H_SPEED_1G_F:
102362306a36Sopenharmony_ci		port->port_speed = EHEA_SPEED_1G;
102462306a36Sopenharmony_ci		port->full_duplex = 1;
102562306a36Sopenharmony_ci		break;
102662306a36Sopenharmony_ci	case H_SPEED_10G_F:
102762306a36Sopenharmony_ci		port->port_speed = EHEA_SPEED_10G;
102862306a36Sopenharmony_ci		port->full_duplex = 1;
102962306a36Sopenharmony_ci		break;
103062306a36Sopenharmony_ci	default:
103162306a36Sopenharmony_ci		port->port_speed = 0;
103262306a36Sopenharmony_ci		port->full_duplex = 0;
103362306a36Sopenharmony_ci		break;
103462306a36Sopenharmony_ci	}
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	port->autoneg = 1;
103762306a36Sopenharmony_ci	port->num_mcs = cb0->num_default_qps;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	/* Number of default QPs */
104062306a36Sopenharmony_ci	if (use_mcs)
104162306a36Sopenharmony_ci		port->num_def_qps = cb0->num_default_qps;
104262306a36Sopenharmony_ci	else
104362306a36Sopenharmony_ci		port->num_def_qps = 1;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	if (!port->num_def_qps) {
104662306a36Sopenharmony_ci		ret = -EINVAL;
104762306a36Sopenharmony_ci		goto out_free;
104862306a36Sopenharmony_ci	}
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	ret = 0;
105162306a36Sopenharmony_ciout_free:
105262306a36Sopenharmony_ci	if (ret || netif_msg_probe(port))
105362306a36Sopenharmony_ci		ehea_dump(cb0, sizeof(*cb0), "ehea_sense_port_attr");
105462306a36Sopenharmony_ci	free_page((unsigned long)cb0);
105562306a36Sopenharmony_ciout:
105662306a36Sopenharmony_ci	return ret;
105762306a36Sopenharmony_ci}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ciint ehea_set_portspeed(struct ehea_port *port, u32 port_speed)
106062306a36Sopenharmony_ci{
106162306a36Sopenharmony_ci	struct hcp_ehea_port_cb4 *cb4;
106262306a36Sopenharmony_ci	u64 hret;
106362306a36Sopenharmony_ci	int ret = 0;
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	cb4 = (void *)get_zeroed_page(GFP_KERNEL);
106662306a36Sopenharmony_ci	if (!cb4) {
106762306a36Sopenharmony_ci		pr_err("no mem for cb4\n");
106862306a36Sopenharmony_ci		ret = -ENOMEM;
106962306a36Sopenharmony_ci		goto out;
107062306a36Sopenharmony_ci	}
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	cb4->port_speed = port_speed;
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	netif_carrier_off(port->netdev);
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	hret = ehea_h_modify_ehea_port(port->adapter->handle,
107762306a36Sopenharmony_ci				       port->logical_port_id,
107862306a36Sopenharmony_ci				       H_PORT_CB4, H_PORT_CB4_SPEED, cb4);
107962306a36Sopenharmony_ci	if (hret == H_SUCCESS) {
108062306a36Sopenharmony_ci		port->autoneg = port_speed == EHEA_SPEED_AUTONEG ? 1 : 0;
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci		hret = ehea_h_query_ehea_port(port->adapter->handle,
108362306a36Sopenharmony_ci					      port->logical_port_id,
108462306a36Sopenharmony_ci					      H_PORT_CB4, H_PORT_CB4_SPEED,
108562306a36Sopenharmony_ci					      cb4);
108662306a36Sopenharmony_ci		if (hret == H_SUCCESS) {
108762306a36Sopenharmony_ci			switch (cb4->port_speed) {
108862306a36Sopenharmony_ci			case H_SPEED_10M_H:
108962306a36Sopenharmony_ci				port->port_speed = EHEA_SPEED_10M;
109062306a36Sopenharmony_ci				port->full_duplex = 0;
109162306a36Sopenharmony_ci				break;
109262306a36Sopenharmony_ci			case H_SPEED_10M_F:
109362306a36Sopenharmony_ci				port->port_speed = EHEA_SPEED_10M;
109462306a36Sopenharmony_ci				port->full_duplex = 1;
109562306a36Sopenharmony_ci				break;
109662306a36Sopenharmony_ci			case H_SPEED_100M_H:
109762306a36Sopenharmony_ci				port->port_speed = EHEA_SPEED_100M;
109862306a36Sopenharmony_ci				port->full_duplex = 0;
109962306a36Sopenharmony_ci				break;
110062306a36Sopenharmony_ci			case H_SPEED_100M_F:
110162306a36Sopenharmony_ci				port->port_speed = EHEA_SPEED_100M;
110262306a36Sopenharmony_ci				port->full_duplex = 1;
110362306a36Sopenharmony_ci				break;
110462306a36Sopenharmony_ci			case H_SPEED_1G_F:
110562306a36Sopenharmony_ci				port->port_speed = EHEA_SPEED_1G;
110662306a36Sopenharmony_ci				port->full_duplex = 1;
110762306a36Sopenharmony_ci				break;
110862306a36Sopenharmony_ci			case H_SPEED_10G_F:
110962306a36Sopenharmony_ci				port->port_speed = EHEA_SPEED_10G;
111062306a36Sopenharmony_ci				port->full_duplex = 1;
111162306a36Sopenharmony_ci				break;
111262306a36Sopenharmony_ci			default:
111362306a36Sopenharmony_ci				port->port_speed = 0;
111462306a36Sopenharmony_ci				port->full_duplex = 0;
111562306a36Sopenharmony_ci				break;
111662306a36Sopenharmony_ci			}
111762306a36Sopenharmony_ci		} else {
111862306a36Sopenharmony_ci			pr_err("Failed sensing port speed\n");
111962306a36Sopenharmony_ci			ret = -EIO;
112062306a36Sopenharmony_ci		}
112162306a36Sopenharmony_ci	} else {
112262306a36Sopenharmony_ci		if (hret == H_AUTHORITY) {
112362306a36Sopenharmony_ci			pr_info("Hypervisor denied setting port speed\n");
112462306a36Sopenharmony_ci			ret = -EPERM;
112562306a36Sopenharmony_ci		} else {
112662306a36Sopenharmony_ci			ret = -EIO;
112762306a36Sopenharmony_ci			pr_err("Failed setting port speed\n");
112862306a36Sopenharmony_ci		}
112962306a36Sopenharmony_ci	}
113062306a36Sopenharmony_ci	if (!prop_carrier_state || (port->phy_link == EHEA_PHY_LINK_UP))
113162306a36Sopenharmony_ci		netif_carrier_on(port->netdev);
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	free_page((unsigned long)cb4);
113462306a36Sopenharmony_ciout:
113562306a36Sopenharmony_ci	return ret;
113662306a36Sopenharmony_ci}
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_cistatic void ehea_parse_eqe(struct ehea_adapter *adapter, u64 eqe)
113962306a36Sopenharmony_ci{
114062306a36Sopenharmony_ci	int ret;
114162306a36Sopenharmony_ci	u8 ec;
114262306a36Sopenharmony_ci	u8 portnum;
114362306a36Sopenharmony_ci	struct ehea_port *port;
114462306a36Sopenharmony_ci	struct net_device *dev;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	ec = EHEA_BMASK_GET(NEQE_EVENT_CODE, eqe);
114762306a36Sopenharmony_ci	portnum = EHEA_BMASK_GET(NEQE_PORTNUM, eqe);
114862306a36Sopenharmony_ci	port = ehea_get_port(adapter, portnum);
114962306a36Sopenharmony_ci	if (!port) {
115062306a36Sopenharmony_ci		netdev_err(NULL, "unknown portnum %x\n", portnum);
115162306a36Sopenharmony_ci		return;
115262306a36Sopenharmony_ci	}
115362306a36Sopenharmony_ci	dev = port->netdev;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	switch (ec) {
115662306a36Sopenharmony_ci	case EHEA_EC_PORTSTATE_CHG:	/* port state change */
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci		if (EHEA_BMASK_GET(NEQE_PORT_UP, eqe)) {
115962306a36Sopenharmony_ci			if (!netif_carrier_ok(dev)) {
116062306a36Sopenharmony_ci				ret = ehea_sense_port_attr(port);
116162306a36Sopenharmony_ci				if (ret) {
116262306a36Sopenharmony_ci					netdev_err(dev, "failed resensing port attributes\n");
116362306a36Sopenharmony_ci					break;
116462306a36Sopenharmony_ci				}
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci				netif_info(port, link, dev,
116762306a36Sopenharmony_ci					   "Logical port up: %dMbps %s Duplex\n",
116862306a36Sopenharmony_ci					   port->port_speed,
116962306a36Sopenharmony_ci					   port->full_duplex == 1 ?
117062306a36Sopenharmony_ci					   "Full" : "Half");
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci				netif_carrier_on(dev);
117362306a36Sopenharmony_ci				netif_wake_queue(dev);
117462306a36Sopenharmony_ci			}
117562306a36Sopenharmony_ci		} else
117662306a36Sopenharmony_ci			if (netif_carrier_ok(dev)) {
117762306a36Sopenharmony_ci				netif_info(port, link, dev,
117862306a36Sopenharmony_ci					   "Logical port down\n");
117962306a36Sopenharmony_ci				netif_carrier_off(dev);
118062306a36Sopenharmony_ci				netif_tx_disable(dev);
118162306a36Sopenharmony_ci			}
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci		if (EHEA_BMASK_GET(NEQE_EXTSWITCH_PORT_UP, eqe)) {
118462306a36Sopenharmony_ci			port->phy_link = EHEA_PHY_LINK_UP;
118562306a36Sopenharmony_ci			netif_info(port, link, dev,
118662306a36Sopenharmony_ci				   "Physical port up\n");
118762306a36Sopenharmony_ci			if (prop_carrier_state)
118862306a36Sopenharmony_ci				netif_carrier_on(dev);
118962306a36Sopenharmony_ci		} else {
119062306a36Sopenharmony_ci			port->phy_link = EHEA_PHY_LINK_DOWN;
119162306a36Sopenharmony_ci			netif_info(port, link, dev,
119262306a36Sopenharmony_ci				   "Physical port down\n");
119362306a36Sopenharmony_ci			if (prop_carrier_state)
119462306a36Sopenharmony_ci				netif_carrier_off(dev);
119562306a36Sopenharmony_ci		}
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci		if (EHEA_BMASK_GET(NEQE_EXTSWITCH_PRIMARY, eqe))
119862306a36Sopenharmony_ci			netdev_info(dev,
119962306a36Sopenharmony_ci				    "External switch port is primary port\n");
120062306a36Sopenharmony_ci		else
120162306a36Sopenharmony_ci			netdev_info(dev,
120262306a36Sopenharmony_ci				    "External switch port is backup port\n");
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci		break;
120562306a36Sopenharmony_ci	case EHEA_EC_ADAPTER_MALFUNC:
120662306a36Sopenharmony_ci		netdev_err(dev, "Adapter malfunction\n");
120762306a36Sopenharmony_ci		break;
120862306a36Sopenharmony_ci	case EHEA_EC_PORT_MALFUNC:
120962306a36Sopenharmony_ci		netdev_info(dev, "Port malfunction\n");
121062306a36Sopenharmony_ci		netif_carrier_off(dev);
121162306a36Sopenharmony_ci		netif_tx_disable(dev);
121262306a36Sopenharmony_ci		break;
121362306a36Sopenharmony_ci	default:
121462306a36Sopenharmony_ci		netdev_err(dev, "unknown event code %x, eqe=0x%llX\n", ec, eqe);
121562306a36Sopenharmony_ci		break;
121662306a36Sopenharmony_ci	}
121762306a36Sopenharmony_ci}
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_cistatic void ehea_neq_tasklet(struct tasklet_struct *t)
122062306a36Sopenharmony_ci{
122162306a36Sopenharmony_ci	struct ehea_adapter *adapter = from_tasklet(adapter, t, neq_tasklet);
122262306a36Sopenharmony_ci	struct ehea_eqe *eqe;
122362306a36Sopenharmony_ci	u64 event_mask;
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	eqe = ehea_poll_eq(adapter->neq);
122662306a36Sopenharmony_ci	pr_debug("eqe=%p\n", eqe);
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	while (eqe) {
122962306a36Sopenharmony_ci		pr_debug("*eqe=%lx\n", (unsigned long) eqe->entry);
123062306a36Sopenharmony_ci		ehea_parse_eqe(adapter, eqe->entry);
123162306a36Sopenharmony_ci		eqe = ehea_poll_eq(adapter->neq);
123262306a36Sopenharmony_ci		pr_debug("next eqe=%p\n", eqe);
123362306a36Sopenharmony_ci	}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	event_mask = EHEA_BMASK_SET(NELR_PORTSTATE_CHG, 1)
123662306a36Sopenharmony_ci		   | EHEA_BMASK_SET(NELR_ADAPTER_MALFUNC, 1)
123762306a36Sopenharmony_ci		   | EHEA_BMASK_SET(NELR_PORT_MALFUNC, 1);
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	ehea_h_reset_events(adapter->handle,
124062306a36Sopenharmony_ci			    adapter->neq->fw_handle, event_mask);
124162306a36Sopenharmony_ci}
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_cistatic irqreturn_t ehea_interrupt_neq(int irq, void *param)
124462306a36Sopenharmony_ci{
124562306a36Sopenharmony_ci	struct ehea_adapter *adapter = param;
124662306a36Sopenharmony_ci	tasklet_hi_schedule(&adapter->neq_tasklet);
124762306a36Sopenharmony_ci	return IRQ_HANDLED;
124862306a36Sopenharmony_ci}
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_cistatic int ehea_fill_port_res(struct ehea_port_res *pr)
125262306a36Sopenharmony_ci{
125362306a36Sopenharmony_ci	int ret;
125462306a36Sopenharmony_ci	struct ehea_qp_init_attr *init_attr = &pr->qp->init_attr;
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	ehea_init_fill_rq1(pr, pr->rq1_skba.len);
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	ret = ehea_refill_rq2(pr, init_attr->act_nr_rwqes_rq2 - 1);
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	ret |= ehea_refill_rq3(pr, init_attr->act_nr_rwqes_rq3 - 1);
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	return ret;
126362306a36Sopenharmony_ci}
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_cistatic int ehea_reg_interrupts(struct net_device *dev)
126662306a36Sopenharmony_ci{
126762306a36Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
126862306a36Sopenharmony_ci	struct ehea_port_res *pr;
126962306a36Sopenharmony_ci	int i, ret;
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	snprintf(port->int_aff_name, EHEA_IRQ_NAME_SIZE - 1, "%s-aff",
127362306a36Sopenharmony_ci		 dev->name);
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci	ret = ibmebus_request_irq(port->qp_eq->attr.ist1,
127662306a36Sopenharmony_ci				  ehea_qp_aff_irq_handler,
127762306a36Sopenharmony_ci				  0, port->int_aff_name, port);
127862306a36Sopenharmony_ci	if (ret) {
127962306a36Sopenharmony_ci		netdev_err(dev, "failed registering irq for qp_aff_irq_handler:ist=%X\n",
128062306a36Sopenharmony_ci			   port->qp_eq->attr.ist1);
128162306a36Sopenharmony_ci		goto out_free_qpeq;
128262306a36Sopenharmony_ci	}
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	netif_info(port, ifup, dev,
128562306a36Sopenharmony_ci		   "irq_handle 0x%X for function qp_aff_irq_handler registered\n",
128662306a36Sopenharmony_ci		   port->qp_eq->attr.ist1);
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	for (i = 0; i < port->num_def_qps; i++) {
129062306a36Sopenharmony_ci		pr = &port->port_res[i];
129162306a36Sopenharmony_ci		snprintf(pr->int_send_name, EHEA_IRQ_NAME_SIZE - 1,
129262306a36Sopenharmony_ci			 "%s-queue%d", dev->name, i);
129362306a36Sopenharmony_ci		ret = ibmebus_request_irq(pr->eq->attr.ist1,
129462306a36Sopenharmony_ci					  ehea_recv_irq_handler,
129562306a36Sopenharmony_ci					  0, pr->int_send_name, pr);
129662306a36Sopenharmony_ci		if (ret) {
129762306a36Sopenharmony_ci			netdev_err(dev, "failed registering irq for ehea_queue port_res_nr:%d, ist=%X\n",
129862306a36Sopenharmony_ci				   i, pr->eq->attr.ist1);
129962306a36Sopenharmony_ci			goto out_free_req;
130062306a36Sopenharmony_ci		}
130162306a36Sopenharmony_ci		netif_info(port, ifup, dev,
130262306a36Sopenharmony_ci			   "irq_handle 0x%X for function ehea_queue_int %d registered\n",
130362306a36Sopenharmony_ci			   pr->eq->attr.ist1, i);
130462306a36Sopenharmony_ci	}
130562306a36Sopenharmony_ciout:
130662306a36Sopenharmony_ci	return ret;
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ciout_free_req:
131062306a36Sopenharmony_ci	while (--i >= 0) {
131162306a36Sopenharmony_ci		u32 ist = port->port_res[i].eq->attr.ist1;
131262306a36Sopenharmony_ci		ibmebus_free_irq(ist, &port->port_res[i]);
131362306a36Sopenharmony_ci	}
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ciout_free_qpeq:
131662306a36Sopenharmony_ci	ibmebus_free_irq(port->qp_eq->attr.ist1, port);
131762306a36Sopenharmony_ci	i = port->num_def_qps;
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	goto out;
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci}
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_cistatic void ehea_free_interrupts(struct net_device *dev)
132462306a36Sopenharmony_ci{
132562306a36Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
132662306a36Sopenharmony_ci	struct ehea_port_res *pr;
132762306a36Sopenharmony_ci	int i;
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	/* send */
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	for (i = 0; i < port->num_def_qps; i++) {
133262306a36Sopenharmony_ci		pr = &port->port_res[i];
133362306a36Sopenharmony_ci		ibmebus_free_irq(pr->eq->attr.ist1, pr);
133462306a36Sopenharmony_ci		netif_info(port, intr, dev,
133562306a36Sopenharmony_ci			   "free send irq for res %d with handle 0x%X\n",
133662306a36Sopenharmony_ci			   i, pr->eq->attr.ist1);
133762306a36Sopenharmony_ci	}
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	/* associated events */
134062306a36Sopenharmony_ci	ibmebus_free_irq(port->qp_eq->attr.ist1, port);
134162306a36Sopenharmony_ci	netif_info(port, intr, dev,
134262306a36Sopenharmony_ci		   "associated event interrupt for handle 0x%X freed\n",
134362306a36Sopenharmony_ci		   port->qp_eq->attr.ist1);
134462306a36Sopenharmony_ci}
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_cistatic int ehea_configure_port(struct ehea_port *port)
134762306a36Sopenharmony_ci{
134862306a36Sopenharmony_ci	int ret, i;
134962306a36Sopenharmony_ci	u64 hret, mask;
135062306a36Sopenharmony_ci	struct hcp_ehea_port_cb0 *cb0;
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	ret = -ENOMEM;
135362306a36Sopenharmony_ci	cb0 = (void *)get_zeroed_page(GFP_KERNEL);
135462306a36Sopenharmony_ci	if (!cb0)
135562306a36Sopenharmony_ci		goto out;
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	cb0->port_rc = EHEA_BMASK_SET(PXLY_RC_VALID, 1)
135862306a36Sopenharmony_ci		     | EHEA_BMASK_SET(PXLY_RC_IP_CHKSUM, 1)
135962306a36Sopenharmony_ci		     | EHEA_BMASK_SET(PXLY_RC_TCP_UDP_CHKSUM, 1)
136062306a36Sopenharmony_ci		     | EHEA_BMASK_SET(PXLY_RC_VLAN_XTRACT, 1)
136162306a36Sopenharmony_ci		     | EHEA_BMASK_SET(PXLY_RC_VLAN_TAG_FILTER,
136262306a36Sopenharmony_ci				      PXLY_RC_VLAN_FILTER)
136362306a36Sopenharmony_ci		     | EHEA_BMASK_SET(PXLY_RC_JUMBO_FRAME, 1);
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci	for (i = 0; i < port->num_mcs; i++)
136662306a36Sopenharmony_ci		if (use_mcs)
136762306a36Sopenharmony_ci			cb0->default_qpn_arr[i] =
136862306a36Sopenharmony_ci				port->port_res[i].qp->init_attr.qp_nr;
136962306a36Sopenharmony_ci		else
137062306a36Sopenharmony_ci			cb0->default_qpn_arr[i] =
137162306a36Sopenharmony_ci				port->port_res[0].qp->init_attr.qp_nr;
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	if (netif_msg_ifup(port))
137462306a36Sopenharmony_ci		ehea_dump(cb0, sizeof(*cb0), "ehea_configure_port");
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	mask = EHEA_BMASK_SET(H_PORT_CB0_PRC, 1)
137762306a36Sopenharmony_ci	     | EHEA_BMASK_SET(H_PORT_CB0_DEFQPNARRAY, 1);
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	hret = ehea_h_modify_ehea_port(port->adapter->handle,
138062306a36Sopenharmony_ci				       port->logical_port_id,
138162306a36Sopenharmony_ci				       H_PORT_CB0, mask, cb0);
138262306a36Sopenharmony_ci	ret = -EIO;
138362306a36Sopenharmony_ci	if (hret != H_SUCCESS)
138462306a36Sopenharmony_ci		goto out_free;
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	ret = 0;
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ciout_free:
138962306a36Sopenharmony_ci	free_page((unsigned long)cb0);
139062306a36Sopenharmony_ciout:
139162306a36Sopenharmony_ci	return ret;
139262306a36Sopenharmony_ci}
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_cistatic int ehea_gen_smrs(struct ehea_port_res *pr)
139562306a36Sopenharmony_ci{
139662306a36Sopenharmony_ci	int ret;
139762306a36Sopenharmony_ci	struct ehea_adapter *adapter = pr->port->adapter;
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	ret = ehea_gen_smr(adapter, &adapter->mr, &pr->send_mr);
140062306a36Sopenharmony_ci	if (ret)
140162306a36Sopenharmony_ci		goto out;
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	ret = ehea_gen_smr(adapter, &adapter->mr, &pr->recv_mr);
140462306a36Sopenharmony_ci	if (ret)
140562306a36Sopenharmony_ci		goto out_free;
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	return 0;
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ciout_free:
141062306a36Sopenharmony_ci	ehea_rem_mr(&pr->send_mr);
141162306a36Sopenharmony_ciout:
141262306a36Sopenharmony_ci	pr_err("Generating SMRS failed\n");
141362306a36Sopenharmony_ci	return -EIO;
141462306a36Sopenharmony_ci}
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_cistatic int ehea_rem_smrs(struct ehea_port_res *pr)
141762306a36Sopenharmony_ci{
141862306a36Sopenharmony_ci	if ((ehea_rem_mr(&pr->send_mr)) ||
141962306a36Sopenharmony_ci	    (ehea_rem_mr(&pr->recv_mr)))
142062306a36Sopenharmony_ci		return -EIO;
142162306a36Sopenharmony_ci	else
142262306a36Sopenharmony_ci		return 0;
142362306a36Sopenharmony_ci}
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_cistatic int ehea_init_q_skba(struct ehea_q_skb_arr *q_skba, int max_q_entries)
142662306a36Sopenharmony_ci{
142762306a36Sopenharmony_ci	int arr_size = sizeof(void *) * max_q_entries;
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	q_skba->arr = vzalloc(arr_size);
143062306a36Sopenharmony_ci	if (!q_skba->arr)
143162306a36Sopenharmony_ci		return -ENOMEM;
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	q_skba->len = max_q_entries;
143462306a36Sopenharmony_ci	q_skba->index = 0;
143562306a36Sopenharmony_ci	q_skba->os_skbs = 0;
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	return 0;
143862306a36Sopenharmony_ci}
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_cistatic int ehea_init_port_res(struct ehea_port *port, struct ehea_port_res *pr,
144162306a36Sopenharmony_ci			      struct port_res_cfg *pr_cfg, int queue_token)
144262306a36Sopenharmony_ci{
144362306a36Sopenharmony_ci	struct ehea_adapter *adapter = port->adapter;
144462306a36Sopenharmony_ci	enum ehea_eq_type eq_type = EHEA_EQ;
144562306a36Sopenharmony_ci	struct ehea_qp_init_attr *init_attr = NULL;
144662306a36Sopenharmony_ci	int ret = -EIO;
144762306a36Sopenharmony_ci	u64 tx_bytes, rx_bytes, tx_packets, rx_packets;
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	tx_bytes = pr->tx_bytes;
145062306a36Sopenharmony_ci	tx_packets = pr->tx_packets;
145162306a36Sopenharmony_ci	rx_bytes = pr->rx_bytes;
145262306a36Sopenharmony_ci	rx_packets = pr->rx_packets;
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	memset(pr, 0, sizeof(struct ehea_port_res));
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	pr->tx_bytes = tx_bytes;
145762306a36Sopenharmony_ci	pr->tx_packets = tx_packets;
145862306a36Sopenharmony_ci	pr->rx_bytes = rx_bytes;
145962306a36Sopenharmony_ci	pr->rx_packets = rx_packets;
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	pr->port = port;
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	pr->eq = ehea_create_eq(adapter, eq_type, EHEA_MAX_ENTRIES_EQ, 0);
146462306a36Sopenharmony_ci	if (!pr->eq) {
146562306a36Sopenharmony_ci		pr_err("create_eq failed (eq)\n");
146662306a36Sopenharmony_ci		goto out_free;
146762306a36Sopenharmony_ci	}
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	pr->recv_cq = ehea_create_cq(adapter, pr_cfg->max_entries_rcq,
147062306a36Sopenharmony_ci				     pr->eq->fw_handle,
147162306a36Sopenharmony_ci				     port->logical_port_id);
147262306a36Sopenharmony_ci	if (!pr->recv_cq) {
147362306a36Sopenharmony_ci		pr_err("create_cq failed (cq_recv)\n");
147462306a36Sopenharmony_ci		goto out_free;
147562306a36Sopenharmony_ci	}
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	pr->send_cq = ehea_create_cq(adapter, pr_cfg->max_entries_scq,
147862306a36Sopenharmony_ci				     pr->eq->fw_handle,
147962306a36Sopenharmony_ci				     port->logical_port_id);
148062306a36Sopenharmony_ci	if (!pr->send_cq) {
148162306a36Sopenharmony_ci		pr_err("create_cq failed (cq_send)\n");
148262306a36Sopenharmony_ci		goto out_free;
148362306a36Sopenharmony_ci	}
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	if (netif_msg_ifup(port))
148662306a36Sopenharmony_ci		pr_info("Send CQ: act_nr_cqes=%d, Recv CQ: act_nr_cqes=%d\n",
148762306a36Sopenharmony_ci			pr->send_cq->attr.act_nr_of_cqes,
148862306a36Sopenharmony_ci			pr->recv_cq->attr.act_nr_of_cqes);
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci	init_attr = kzalloc(sizeof(*init_attr), GFP_KERNEL);
149162306a36Sopenharmony_ci	if (!init_attr) {
149262306a36Sopenharmony_ci		ret = -ENOMEM;
149362306a36Sopenharmony_ci		pr_err("no mem for ehea_qp_init_attr\n");
149462306a36Sopenharmony_ci		goto out_free;
149562306a36Sopenharmony_ci	}
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci	init_attr->low_lat_rq1 = 1;
149862306a36Sopenharmony_ci	init_attr->signalingtype = 1;	/* generate CQE if specified in WQE */
149962306a36Sopenharmony_ci	init_attr->rq_count = 3;
150062306a36Sopenharmony_ci	init_attr->qp_token = queue_token;
150162306a36Sopenharmony_ci	init_attr->max_nr_send_wqes = pr_cfg->max_entries_sq;
150262306a36Sopenharmony_ci	init_attr->max_nr_rwqes_rq1 = pr_cfg->max_entries_rq1;
150362306a36Sopenharmony_ci	init_attr->max_nr_rwqes_rq2 = pr_cfg->max_entries_rq2;
150462306a36Sopenharmony_ci	init_attr->max_nr_rwqes_rq3 = pr_cfg->max_entries_rq3;
150562306a36Sopenharmony_ci	init_attr->wqe_size_enc_sq = EHEA_SG_SQ;
150662306a36Sopenharmony_ci	init_attr->wqe_size_enc_rq1 = EHEA_SG_RQ1;
150762306a36Sopenharmony_ci	init_attr->wqe_size_enc_rq2 = EHEA_SG_RQ2;
150862306a36Sopenharmony_ci	init_attr->wqe_size_enc_rq3 = EHEA_SG_RQ3;
150962306a36Sopenharmony_ci	init_attr->rq2_threshold = EHEA_RQ2_THRESHOLD;
151062306a36Sopenharmony_ci	init_attr->rq3_threshold = EHEA_RQ3_THRESHOLD;
151162306a36Sopenharmony_ci	init_attr->port_nr = port->logical_port_id;
151262306a36Sopenharmony_ci	init_attr->send_cq_handle = pr->send_cq->fw_handle;
151362306a36Sopenharmony_ci	init_attr->recv_cq_handle = pr->recv_cq->fw_handle;
151462306a36Sopenharmony_ci	init_attr->aff_eq_handle = port->qp_eq->fw_handle;
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	pr->qp = ehea_create_qp(adapter, adapter->pd, init_attr);
151762306a36Sopenharmony_ci	if (!pr->qp) {
151862306a36Sopenharmony_ci		pr_err("create_qp failed\n");
151962306a36Sopenharmony_ci		ret = -EIO;
152062306a36Sopenharmony_ci		goto out_free;
152162306a36Sopenharmony_ci	}
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	if (netif_msg_ifup(port))
152462306a36Sopenharmony_ci		pr_info("QP: qp_nr=%d\n act_nr_snd_wqe=%d\n nr_rwqe_rq1=%d\n nr_rwqe_rq2=%d\n nr_rwqe_rq3=%d\n",
152562306a36Sopenharmony_ci			init_attr->qp_nr,
152662306a36Sopenharmony_ci			init_attr->act_nr_send_wqes,
152762306a36Sopenharmony_ci			init_attr->act_nr_rwqes_rq1,
152862306a36Sopenharmony_ci			init_attr->act_nr_rwqes_rq2,
152962306a36Sopenharmony_ci			init_attr->act_nr_rwqes_rq3);
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	pr->sq_skba_size = init_attr->act_nr_send_wqes + 1;
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	ret = ehea_init_q_skba(&pr->sq_skba, pr->sq_skba_size);
153462306a36Sopenharmony_ci	ret |= ehea_init_q_skba(&pr->rq1_skba, init_attr->act_nr_rwqes_rq1 + 1);
153562306a36Sopenharmony_ci	ret |= ehea_init_q_skba(&pr->rq2_skba, init_attr->act_nr_rwqes_rq2 + 1);
153662306a36Sopenharmony_ci	ret |= ehea_init_q_skba(&pr->rq3_skba, init_attr->act_nr_rwqes_rq3 + 1);
153762306a36Sopenharmony_ci	if (ret)
153862306a36Sopenharmony_ci		goto out_free;
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	pr->swqe_refill_th = init_attr->act_nr_send_wqes / 10;
154162306a36Sopenharmony_ci	if (ehea_gen_smrs(pr) != 0) {
154262306a36Sopenharmony_ci		ret = -EIO;
154362306a36Sopenharmony_ci		goto out_free;
154462306a36Sopenharmony_ci	}
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	atomic_set(&pr->swqe_avail, init_attr->act_nr_send_wqes - 1);
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	kfree(init_attr);
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	netif_napi_add(pr->port->netdev, &pr->napi, ehea_poll);
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	ret = 0;
155362306a36Sopenharmony_ci	goto out;
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ciout_free:
155662306a36Sopenharmony_ci	kfree(init_attr);
155762306a36Sopenharmony_ci	vfree(pr->sq_skba.arr);
155862306a36Sopenharmony_ci	vfree(pr->rq1_skba.arr);
155962306a36Sopenharmony_ci	vfree(pr->rq2_skba.arr);
156062306a36Sopenharmony_ci	vfree(pr->rq3_skba.arr);
156162306a36Sopenharmony_ci	ehea_destroy_qp(pr->qp);
156262306a36Sopenharmony_ci	ehea_destroy_cq(pr->send_cq);
156362306a36Sopenharmony_ci	ehea_destroy_cq(pr->recv_cq);
156462306a36Sopenharmony_ci	ehea_destroy_eq(pr->eq);
156562306a36Sopenharmony_ciout:
156662306a36Sopenharmony_ci	return ret;
156762306a36Sopenharmony_ci}
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_cistatic int ehea_clean_portres(struct ehea_port *port, struct ehea_port_res *pr)
157062306a36Sopenharmony_ci{
157162306a36Sopenharmony_ci	int ret, i;
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	if (pr->qp)
157462306a36Sopenharmony_ci		netif_napi_del(&pr->napi);
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	ret = ehea_destroy_qp(pr->qp);
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	if (!ret) {
157962306a36Sopenharmony_ci		ehea_destroy_cq(pr->send_cq);
158062306a36Sopenharmony_ci		ehea_destroy_cq(pr->recv_cq);
158162306a36Sopenharmony_ci		ehea_destroy_eq(pr->eq);
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci		for (i = 0; i < pr->rq1_skba.len; i++)
158462306a36Sopenharmony_ci			dev_kfree_skb(pr->rq1_skba.arr[i]);
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci		for (i = 0; i < pr->rq2_skba.len; i++)
158762306a36Sopenharmony_ci			dev_kfree_skb(pr->rq2_skba.arr[i]);
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci		for (i = 0; i < pr->rq3_skba.len; i++)
159062306a36Sopenharmony_ci			dev_kfree_skb(pr->rq3_skba.arr[i]);
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci		for (i = 0; i < pr->sq_skba.len; i++)
159362306a36Sopenharmony_ci			dev_kfree_skb(pr->sq_skba.arr[i]);
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci		vfree(pr->rq1_skba.arr);
159662306a36Sopenharmony_ci		vfree(pr->rq2_skba.arr);
159762306a36Sopenharmony_ci		vfree(pr->rq3_skba.arr);
159862306a36Sopenharmony_ci		vfree(pr->sq_skba.arr);
159962306a36Sopenharmony_ci		ret = ehea_rem_smrs(pr);
160062306a36Sopenharmony_ci	}
160162306a36Sopenharmony_ci	return ret;
160262306a36Sopenharmony_ci}
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_cistatic void write_swqe2_immediate(struct sk_buff *skb, struct ehea_swqe *swqe,
160562306a36Sopenharmony_ci				  u32 lkey)
160662306a36Sopenharmony_ci{
160762306a36Sopenharmony_ci	int skb_data_size = skb_headlen(skb);
160862306a36Sopenharmony_ci	u8 *imm_data = &swqe->u.immdata_desc.immediate_data[0];
160962306a36Sopenharmony_ci	struct ehea_vsgentry *sg1entry = &swqe->u.immdata_desc.sg_entry;
161062306a36Sopenharmony_ci	unsigned int immediate_len = SWQE2_MAX_IMM;
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci	swqe->descriptors = 0;
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	if (skb_is_gso(skb)) {
161562306a36Sopenharmony_ci		swqe->tx_control |= EHEA_SWQE_TSO;
161662306a36Sopenharmony_ci		swqe->mss = skb_shinfo(skb)->gso_size;
161762306a36Sopenharmony_ci		/*
161862306a36Sopenharmony_ci		 * For TSO packets we only copy the headers into the
161962306a36Sopenharmony_ci		 * immediate area.
162062306a36Sopenharmony_ci		 */
162162306a36Sopenharmony_ci		immediate_len = skb_tcp_all_headers(skb);
162262306a36Sopenharmony_ci	}
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci	if (skb_is_gso(skb) || skb_data_size >= SWQE2_MAX_IMM) {
162562306a36Sopenharmony_ci		skb_copy_from_linear_data(skb, imm_data, immediate_len);
162662306a36Sopenharmony_ci		swqe->immediate_data_length = immediate_len;
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci		if (skb_data_size > immediate_len) {
162962306a36Sopenharmony_ci			sg1entry->l_key = lkey;
163062306a36Sopenharmony_ci			sg1entry->len = skb_data_size - immediate_len;
163162306a36Sopenharmony_ci			sg1entry->vaddr =
163262306a36Sopenharmony_ci				ehea_map_vaddr(skb->data + immediate_len);
163362306a36Sopenharmony_ci			swqe->descriptors++;
163462306a36Sopenharmony_ci		}
163562306a36Sopenharmony_ci	} else {
163662306a36Sopenharmony_ci		skb_copy_from_linear_data(skb, imm_data, skb_data_size);
163762306a36Sopenharmony_ci		swqe->immediate_data_length = skb_data_size;
163862306a36Sopenharmony_ci	}
163962306a36Sopenharmony_ci}
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_cistatic inline void write_swqe2_data(struct sk_buff *skb, struct net_device *dev,
164262306a36Sopenharmony_ci				    struct ehea_swqe *swqe, u32 lkey)
164362306a36Sopenharmony_ci{
164462306a36Sopenharmony_ci	struct ehea_vsgentry *sg_list, *sg1entry, *sgentry;
164562306a36Sopenharmony_ci	skb_frag_t *frag;
164662306a36Sopenharmony_ci	int nfrags, sg1entry_contains_frag_data, i;
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci	nfrags = skb_shinfo(skb)->nr_frags;
164962306a36Sopenharmony_ci	sg1entry = &swqe->u.immdata_desc.sg_entry;
165062306a36Sopenharmony_ci	sg_list = (struct ehea_vsgentry *)&swqe->u.immdata_desc.sg_list;
165162306a36Sopenharmony_ci	sg1entry_contains_frag_data = 0;
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci	write_swqe2_immediate(skb, swqe, lkey);
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci	/* write descriptors */
165662306a36Sopenharmony_ci	if (nfrags > 0) {
165762306a36Sopenharmony_ci		if (swqe->descriptors == 0) {
165862306a36Sopenharmony_ci			/* sg1entry not yet used */
165962306a36Sopenharmony_ci			frag = &skb_shinfo(skb)->frags[0];
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci			/* copy sg1entry data */
166262306a36Sopenharmony_ci			sg1entry->l_key = lkey;
166362306a36Sopenharmony_ci			sg1entry->len = skb_frag_size(frag);
166462306a36Sopenharmony_ci			sg1entry->vaddr =
166562306a36Sopenharmony_ci				ehea_map_vaddr(skb_frag_address(frag));
166662306a36Sopenharmony_ci			swqe->descriptors++;
166762306a36Sopenharmony_ci			sg1entry_contains_frag_data = 1;
166862306a36Sopenharmony_ci		}
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci		for (i = sg1entry_contains_frag_data; i < nfrags; i++) {
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci			frag = &skb_shinfo(skb)->frags[i];
167362306a36Sopenharmony_ci			sgentry = &sg_list[i - sg1entry_contains_frag_data];
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci			sgentry->l_key = lkey;
167662306a36Sopenharmony_ci			sgentry->len = skb_frag_size(frag);
167762306a36Sopenharmony_ci			sgentry->vaddr = ehea_map_vaddr(skb_frag_address(frag));
167862306a36Sopenharmony_ci			swqe->descriptors++;
167962306a36Sopenharmony_ci		}
168062306a36Sopenharmony_ci	}
168162306a36Sopenharmony_ci}
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_cistatic int ehea_broadcast_reg_helper(struct ehea_port *port, u32 hcallid)
168462306a36Sopenharmony_ci{
168562306a36Sopenharmony_ci	int ret = 0;
168662306a36Sopenharmony_ci	u64 hret;
168762306a36Sopenharmony_ci	u8 reg_type;
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci	/* De/Register untagged packets */
169062306a36Sopenharmony_ci	reg_type = EHEA_BCMC_BROADCAST | EHEA_BCMC_UNTAGGED;
169162306a36Sopenharmony_ci	hret = ehea_h_reg_dereg_bcmc(port->adapter->handle,
169262306a36Sopenharmony_ci				     port->logical_port_id,
169362306a36Sopenharmony_ci				     reg_type, port->mac_addr, 0, hcallid);
169462306a36Sopenharmony_ci	if (hret != H_SUCCESS) {
169562306a36Sopenharmony_ci		pr_err("%sregistering bc address failed (tagged)\n",
169662306a36Sopenharmony_ci		       hcallid == H_REG_BCMC ? "" : "de");
169762306a36Sopenharmony_ci		ret = -EIO;
169862306a36Sopenharmony_ci		goto out_herr;
169962306a36Sopenharmony_ci	}
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci	/* De/Register VLAN packets */
170262306a36Sopenharmony_ci	reg_type = EHEA_BCMC_BROADCAST | EHEA_BCMC_VLANID_ALL;
170362306a36Sopenharmony_ci	hret = ehea_h_reg_dereg_bcmc(port->adapter->handle,
170462306a36Sopenharmony_ci				     port->logical_port_id,
170562306a36Sopenharmony_ci				     reg_type, port->mac_addr, 0, hcallid);
170662306a36Sopenharmony_ci	if (hret != H_SUCCESS) {
170762306a36Sopenharmony_ci		pr_err("%sregistering bc address failed (vlan)\n",
170862306a36Sopenharmony_ci		       hcallid == H_REG_BCMC ? "" : "de");
170962306a36Sopenharmony_ci		ret = -EIO;
171062306a36Sopenharmony_ci	}
171162306a36Sopenharmony_ciout_herr:
171262306a36Sopenharmony_ci	return ret;
171362306a36Sopenharmony_ci}
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_cistatic int ehea_set_mac_addr(struct net_device *dev, void *sa)
171662306a36Sopenharmony_ci{
171762306a36Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
171862306a36Sopenharmony_ci	struct sockaddr *mac_addr = sa;
171962306a36Sopenharmony_ci	struct hcp_ehea_port_cb0 *cb0;
172062306a36Sopenharmony_ci	int ret;
172162306a36Sopenharmony_ci	u64 hret;
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_ci	if (!is_valid_ether_addr(mac_addr->sa_data)) {
172462306a36Sopenharmony_ci		ret = -EADDRNOTAVAIL;
172562306a36Sopenharmony_ci		goto out;
172662306a36Sopenharmony_ci	}
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	cb0 = (void *)get_zeroed_page(GFP_KERNEL);
172962306a36Sopenharmony_ci	if (!cb0) {
173062306a36Sopenharmony_ci		pr_err("no mem for cb0\n");
173162306a36Sopenharmony_ci		ret = -ENOMEM;
173262306a36Sopenharmony_ci		goto out;
173362306a36Sopenharmony_ci	}
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci	memcpy(&(cb0->port_mac_addr), &(mac_addr->sa_data[0]), ETH_ALEN);
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	cb0->port_mac_addr = cb0->port_mac_addr >> 16;
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci	hret = ehea_h_modify_ehea_port(port->adapter->handle,
174062306a36Sopenharmony_ci				       port->logical_port_id, H_PORT_CB0,
174162306a36Sopenharmony_ci				       EHEA_BMASK_SET(H_PORT_CB0_MAC, 1), cb0);
174262306a36Sopenharmony_ci	if (hret != H_SUCCESS) {
174362306a36Sopenharmony_ci		ret = -EIO;
174462306a36Sopenharmony_ci		goto out_free;
174562306a36Sopenharmony_ci	}
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci	eth_hw_addr_set(dev, mac_addr->sa_data);
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci	/* Deregister old MAC in pHYP */
175062306a36Sopenharmony_ci	if (port->state == EHEA_PORT_UP) {
175162306a36Sopenharmony_ci		ret = ehea_broadcast_reg_helper(port, H_DEREG_BCMC);
175262306a36Sopenharmony_ci		if (ret)
175362306a36Sopenharmony_ci			goto out_upregs;
175462306a36Sopenharmony_ci	}
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci	port->mac_addr = cb0->port_mac_addr << 16;
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	/* Register new MAC in pHYP */
175962306a36Sopenharmony_ci	if (port->state == EHEA_PORT_UP) {
176062306a36Sopenharmony_ci		ret = ehea_broadcast_reg_helper(port, H_REG_BCMC);
176162306a36Sopenharmony_ci		if (ret)
176262306a36Sopenharmony_ci			goto out_upregs;
176362306a36Sopenharmony_ci	}
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci	ret = 0;
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ciout_upregs:
176862306a36Sopenharmony_ci	ehea_update_bcmc_registrations();
176962306a36Sopenharmony_ciout_free:
177062306a36Sopenharmony_ci	free_page((unsigned long)cb0);
177162306a36Sopenharmony_ciout:
177262306a36Sopenharmony_ci	return ret;
177362306a36Sopenharmony_ci}
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_cistatic void ehea_promiscuous_error(u64 hret, int enable)
177662306a36Sopenharmony_ci{
177762306a36Sopenharmony_ci	if (hret == H_AUTHORITY)
177862306a36Sopenharmony_ci		pr_info("Hypervisor denied %sabling promiscuous mode\n",
177962306a36Sopenharmony_ci			enable == 1 ? "en" : "dis");
178062306a36Sopenharmony_ci	else
178162306a36Sopenharmony_ci		pr_err("failed %sabling promiscuous mode\n",
178262306a36Sopenharmony_ci		       enable == 1 ? "en" : "dis");
178362306a36Sopenharmony_ci}
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_cistatic void ehea_promiscuous(struct net_device *dev, int enable)
178662306a36Sopenharmony_ci{
178762306a36Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
178862306a36Sopenharmony_ci	struct hcp_ehea_port_cb7 *cb7;
178962306a36Sopenharmony_ci	u64 hret;
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_ci	if (enable == port->promisc)
179262306a36Sopenharmony_ci		return;
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci	cb7 = (void *)get_zeroed_page(GFP_ATOMIC);
179562306a36Sopenharmony_ci	if (!cb7) {
179662306a36Sopenharmony_ci		pr_err("no mem for cb7\n");
179762306a36Sopenharmony_ci		goto out;
179862306a36Sopenharmony_ci	}
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_ci	/* Modify Pxs_DUCQPN in CB7 */
180162306a36Sopenharmony_ci	cb7->def_uc_qpn = enable == 1 ? port->port_res[0].qp->fw_handle : 0;
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci	hret = ehea_h_modify_ehea_port(port->adapter->handle,
180462306a36Sopenharmony_ci				       port->logical_port_id,
180562306a36Sopenharmony_ci				       H_PORT_CB7, H_PORT_CB7_DUCQPN, cb7);
180662306a36Sopenharmony_ci	if (hret) {
180762306a36Sopenharmony_ci		ehea_promiscuous_error(hret, enable);
180862306a36Sopenharmony_ci		goto out;
180962306a36Sopenharmony_ci	}
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci	port->promisc = enable;
181262306a36Sopenharmony_ciout:
181362306a36Sopenharmony_ci	free_page((unsigned long)cb7);
181462306a36Sopenharmony_ci}
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_cistatic u64 ehea_multicast_reg_helper(struct ehea_port *port, u64 mc_mac_addr,
181762306a36Sopenharmony_ci				     u32 hcallid)
181862306a36Sopenharmony_ci{
181962306a36Sopenharmony_ci	u64 hret;
182062306a36Sopenharmony_ci	u8 reg_type;
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci	reg_type = EHEA_BCMC_MULTICAST | EHEA_BCMC_UNTAGGED;
182362306a36Sopenharmony_ci	if (mc_mac_addr == 0)
182462306a36Sopenharmony_ci		reg_type |= EHEA_BCMC_SCOPE_ALL;
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci	hret = ehea_h_reg_dereg_bcmc(port->adapter->handle,
182762306a36Sopenharmony_ci				     port->logical_port_id,
182862306a36Sopenharmony_ci				     reg_type, mc_mac_addr, 0, hcallid);
182962306a36Sopenharmony_ci	if (hret)
183062306a36Sopenharmony_ci		goto out;
183162306a36Sopenharmony_ci
183262306a36Sopenharmony_ci	reg_type = EHEA_BCMC_MULTICAST | EHEA_BCMC_VLANID_ALL;
183362306a36Sopenharmony_ci	if (mc_mac_addr == 0)
183462306a36Sopenharmony_ci		reg_type |= EHEA_BCMC_SCOPE_ALL;
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_ci	hret = ehea_h_reg_dereg_bcmc(port->adapter->handle,
183762306a36Sopenharmony_ci				     port->logical_port_id,
183862306a36Sopenharmony_ci				     reg_type, mc_mac_addr, 0, hcallid);
183962306a36Sopenharmony_ciout:
184062306a36Sopenharmony_ci	return hret;
184162306a36Sopenharmony_ci}
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_cistatic int ehea_drop_multicast_list(struct net_device *dev)
184462306a36Sopenharmony_ci{
184562306a36Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
184662306a36Sopenharmony_ci	struct ehea_mc_list *mc_entry = port->mc_list;
184762306a36Sopenharmony_ci	struct list_head *pos;
184862306a36Sopenharmony_ci	struct list_head *temp;
184962306a36Sopenharmony_ci	int ret = 0;
185062306a36Sopenharmony_ci	u64 hret;
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	list_for_each_safe(pos, temp, &(port->mc_list->list)) {
185362306a36Sopenharmony_ci		mc_entry = list_entry(pos, struct ehea_mc_list, list);
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci		hret = ehea_multicast_reg_helper(port, mc_entry->macaddr,
185662306a36Sopenharmony_ci						 H_DEREG_BCMC);
185762306a36Sopenharmony_ci		if (hret) {
185862306a36Sopenharmony_ci			pr_err("failed deregistering mcast MAC\n");
185962306a36Sopenharmony_ci			ret = -EIO;
186062306a36Sopenharmony_ci		}
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci		list_del(pos);
186362306a36Sopenharmony_ci		kfree(mc_entry);
186462306a36Sopenharmony_ci	}
186562306a36Sopenharmony_ci	return ret;
186662306a36Sopenharmony_ci}
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_cistatic void ehea_allmulti(struct net_device *dev, int enable)
186962306a36Sopenharmony_ci{
187062306a36Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
187162306a36Sopenharmony_ci	u64 hret;
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci	if (!port->allmulti) {
187462306a36Sopenharmony_ci		if (enable) {
187562306a36Sopenharmony_ci			/* Enable ALLMULTI */
187662306a36Sopenharmony_ci			ehea_drop_multicast_list(dev);
187762306a36Sopenharmony_ci			hret = ehea_multicast_reg_helper(port, 0, H_REG_BCMC);
187862306a36Sopenharmony_ci			if (!hret)
187962306a36Sopenharmony_ci				port->allmulti = 1;
188062306a36Sopenharmony_ci			else
188162306a36Sopenharmony_ci				netdev_err(dev,
188262306a36Sopenharmony_ci					   "failed enabling IFF_ALLMULTI\n");
188362306a36Sopenharmony_ci		}
188462306a36Sopenharmony_ci	} else {
188562306a36Sopenharmony_ci		if (!enable) {
188662306a36Sopenharmony_ci			/* Disable ALLMULTI */
188762306a36Sopenharmony_ci			hret = ehea_multicast_reg_helper(port, 0, H_DEREG_BCMC);
188862306a36Sopenharmony_ci			if (!hret)
188962306a36Sopenharmony_ci				port->allmulti = 0;
189062306a36Sopenharmony_ci			else
189162306a36Sopenharmony_ci				netdev_err(dev,
189262306a36Sopenharmony_ci					   "failed disabling IFF_ALLMULTI\n");
189362306a36Sopenharmony_ci		}
189462306a36Sopenharmony_ci	}
189562306a36Sopenharmony_ci}
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_cistatic void ehea_add_multicast_entry(struct ehea_port *port, u8 *mc_mac_addr)
189862306a36Sopenharmony_ci{
189962306a36Sopenharmony_ci	struct ehea_mc_list *ehea_mcl_entry;
190062306a36Sopenharmony_ci	u64 hret;
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ci	ehea_mcl_entry = kzalloc(sizeof(*ehea_mcl_entry), GFP_ATOMIC);
190362306a36Sopenharmony_ci	if (!ehea_mcl_entry)
190462306a36Sopenharmony_ci		return;
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci	INIT_LIST_HEAD(&ehea_mcl_entry->list);
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci	memcpy(&ehea_mcl_entry->macaddr, mc_mac_addr, ETH_ALEN);
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci	hret = ehea_multicast_reg_helper(port, ehea_mcl_entry->macaddr,
191162306a36Sopenharmony_ci					 H_REG_BCMC);
191262306a36Sopenharmony_ci	if (!hret)
191362306a36Sopenharmony_ci		list_add(&ehea_mcl_entry->list, &port->mc_list->list);
191462306a36Sopenharmony_ci	else {
191562306a36Sopenharmony_ci		pr_err("failed registering mcast MAC\n");
191662306a36Sopenharmony_ci		kfree(ehea_mcl_entry);
191762306a36Sopenharmony_ci	}
191862306a36Sopenharmony_ci}
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_cistatic void ehea_set_multicast_list(struct net_device *dev)
192162306a36Sopenharmony_ci{
192262306a36Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
192362306a36Sopenharmony_ci	struct netdev_hw_addr *ha;
192462306a36Sopenharmony_ci	int ret;
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci	ehea_promiscuous(dev, !!(dev->flags & IFF_PROMISC));
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_ci	if (dev->flags & IFF_ALLMULTI) {
192962306a36Sopenharmony_ci		ehea_allmulti(dev, 1);
193062306a36Sopenharmony_ci		goto out;
193162306a36Sopenharmony_ci	}
193262306a36Sopenharmony_ci	ehea_allmulti(dev, 0);
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_ci	if (!netdev_mc_empty(dev)) {
193562306a36Sopenharmony_ci		ret = ehea_drop_multicast_list(dev);
193662306a36Sopenharmony_ci		if (ret) {
193762306a36Sopenharmony_ci			/* Dropping the current multicast list failed.
193862306a36Sopenharmony_ci			 * Enabling ALL_MULTI is the best we can do.
193962306a36Sopenharmony_ci			 */
194062306a36Sopenharmony_ci			ehea_allmulti(dev, 1);
194162306a36Sopenharmony_ci		}
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci		if (netdev_mc_count(dev) > port->adapter->max_mc_mac) {
194462306a36Sopenharmony_ci			pr_info("Mcast registration limit reached (0x%llx). Use ALLMULTI!\n",
194562306a36Sopenharmony_ci				port->adapter->max_mc_mac);
194662306a36Sopenharmony_ci			goto out;
194762306a36Sopenharmony_ci		}
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci		netdev_for_each_mc_addr(ha, dev)
195062306a36Sopenharmony_ci			ehea_add_multicast_entry(port, ha->addr);
195162306a36Sopenharmony_ci
195262306a36Sopenharmony_ci	}
195362306a36Sopenharmony_ciout:
195462306a36Sopenharmony_ci	ehea_update_bcmc_registrations();
195562306a36Sopenharmony_ci}
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_cistatic void xmit_common(struct sk_buff *skb, struct ehea_swqe *swqe)
195862306a36Sopenharmony_ci{
195962306a36Sopenharmony_ci	swqe->tx_control |= EHEA_SWQE_IMM_DATA_PRESENT | EHEA_SWQE_CRC;
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_ci	if (vlan_get_protocol(skb) != htons(ETH_P_IP))
196262306a36Sopenharmony_ci		return;
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	if (skb->ip_summed == CHECKSUM_PARTIAL)
196562306a36Sopenharmony_ci		swqe->tx_control |= EHEA_SWQE_IP_CHECKSUM;
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci	swqe->ip_start = skb_network_offset(skb);
196862306a36Sopenharmony_ci	swqe->ip_end = swqe->ip_start + ip_hdrlen(skb) - 1;
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_ci	switch (ip_hdr(skb)->protocol) {
197162306a36Sopenharmony_ci	case IPPROTO_UDP:
197262306a36Sopenharmony_ci		if (skb->ip_summed == CHECKSUM_PARTIAL)
197362306a36Sopenharmony_ci			swqe->tx_control |= EHEA_SWQE_TCP_CHECKSUM;
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_ci		swqe->tcp_offset = swqe->ip_end + 1 +
197662306a36Sopenharmony_ci				   offsetof(struct udphdr, check);
197762306a36Sopenharmony_ci		break;
197862306a36Sopenharmony_ci
197962306a36Sopenharmony_ci	case IPPROTO_TCP:
198062306a36Sopenharmony_ci		if (skb->ip_summed == CHECKSUM_PARTIAL)
198162306a36Sopenharmony_ci			swqe->tx_control |= EHEA_SWQE_TCP_CHECKSUM;
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci		swqe->tcp_offset = swqe->ip_end + 1 +
198462306a36Sopenharmony_ci				   offsetof(struct tcphdr, check);
198562306a36Sopenharmony_ci		break;
198662306a36Sopenharmony_ci	}
198762306a36Sopenharmony_ci}
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_cistatic void ehea_xmit2(struct sk_buff *skb, struct net_device *dev,
199062306a36Sopenharmony_ci		       struct ehea_swqe *swqe, u32 lkey)
199162306a36Sopenharmony_ci{
199262306a36Sopenharmony_ci	swqe->tx_control |= EHEA_SWQE_DESCRIPTORS_PRESENT;
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci	xmit_common(skb, swqe);
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	write_swqe2_data(skb, dev, swqe, lkey);
199762306a36Sopenharmony_ci}
199862306a36Sopenharmony_ci
199962306a36Sopenharmony_cistatic void ehea_xmit3(struct sk_buff *skb, struct net_device *dev,
200062306a36Sopenharmony_ci		       struct ehea_swqe *swqe)
200162306a36Sopenharmony_ci{
200262306a36Sopenharmony_ci	u8 *imm_data = &swqe->u.immdata_nodesc.immediate_data[0];
200362306a36Sopenharmony_ci
200462306a36Sopenharmony_ci	xmit_common(skb, swqe);
200562306a36Sopenharmony_ci
200662306a36Sopenharmony_ci	if (!skb->data_len)
200762306a36Sopenharmony_ci		skb_copy_from_linear_data(skb, imm_data, skb->len);
200862306a36Sopenharmony_ci	else
200962306a36Sopenharmony_ci		skb_copy_bits(skb, 0, imm_data, skb->len);
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_ci	swqe->immediate_data_length = skb->len;
201262306a36Sopenharmony_ci	dev_consume_skb_any(skb);
201362306a36Sopenharmony_ci}
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_cistatic netdev_tx_t ehea_start_xmit(struct sk_buff *skb, struct net_device *dev)
201662306a36Sopenharmony_ci{
201762306a36Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
201862306a36Sopenharmony_ci	struct ehea_swqe *swqe;
201962306a36Sopenharmony_ci	u32 lkey;
202062306a36Sopenharmony_ci	int swqe_index;
202162306a36Sopenharmony_ci	struct ehea_port_res *pr;
202262306a36Sopenharmony_ci	struct netdev_queue *txq;
202362306a36Sopenharmony_ci
202462306a36Sopenharmony_ci	pr = &port->port_res[skb_get_queue_mapping(skb)];
202562306a36Sopenharmony_ci	txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb));
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_ci	swqe = ehea_get_swqe(pr->qp, &swqe_index);
202862306a36Sopenharmony_ci	memset(swqe, 0, SWQE_HEADER_SIZE);
202962306a36Sopenharmony_ci	atomic_dec(&pr->swqe_avail);
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_ci	if (skb_vlan_tag_present(skb)) {
203262306a36Sopenharmony_ci		swqe->tx_control |= EHEA_SWQE_VLAN_INSERT;
203362306a36Sopenharmony_ci		swqe->vlan_tag = skb_vlan_tag_get(skb);
203462306a36Sopenharmony_ci	}
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci	pr->tx_packets++;
203762306a36Sopenharmony_ci	pr->tx_bytes += skb->len;
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_ci	if (skb->len <= SWQE3_MAX_IMM) {
204062306a36Sopenharmony_ci		u32 sig_iv = port->sig_comp_iv;
204162306a36Sopenharmony_ci		u32 swqe_num = pr->swqe_id_counter;
204262306a36Sopenharmony_ci		ehea_xmit3(skb, dev, swqe);
204362306a36Sopenharmony_ci		swqe->wr_id = EHEA_BMASK_SET(EHEA_WR_ID_TYPE, EHEA_SWQE3_TYPE)
204462306a36Sopenharmony_ci			| EHEA_BMASK_SET(EHEA_WR_ID_COUNT, swqe_num);
204562306a36Sopenharmony_ci		if (pr->swqe_ll_count >= (sig_iv - 1)) {
204662306a36Sopenharmony_ci			swqe->wr_id |= EHEA_BMASK_SET(EHEA_WR_ID_REFILL,
204762306a36Sopenharmony_ci						      sig_iv);
204862306a36Sopenharmony_ci			swqe->tx_control |= EHEA_SWQE_SIGNALLED_COMPLETION;
204962306a36Sopenharmony_ci			pr->swqe_ll_count = 0;
205062306a36Sopenharmony_ci		} else
205162306a36Sopenharmony_ci			pr->swqe_ll_count += 1;
205262306a36Sopenharmony_ci	} else {
205362306a36Sopenharmony_ci		swqe->wr_id =
205462306a36Sopenharmony_ci			EHEA_BMASK_SET(EHEA_WR_ID_TYPE, EHEA_SWQE2_TYPE)
205562306a36Sopenharmony_ci		      | EHEA_BMASK_SET(EHEA_WR_ID_COUNT, pr->swqe_id_counter)
205662306a36Sopenharmony_ci		      | EHEA_BMASK_SET(EHEA_WR_ID_REFILL, 1)
205762306a36Sopenharmony_ci		      | EHEA_BMASK_SET(EHEA_WR_ID_INDEX, pr->sq_skba.index);
205862306a36Sopenharmony_ci		pr->sq_skba.arr[pr->sq_skba.index] = skb;
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_ci		pr->sq_skba.index++;
206162306a36Sopenharmony_ci		pr->sq_skba.index &= (pr->sq_skba.len - 1);
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci		lkey = pr->send_mr.lkey;
206462306a36Sopenharmony_ci		ehea_xmit2(skb, dev, swqe, lkey);
206562306a36Sopenharmony_ci		swqe->tx_control |= EHEA_SWQE_SIGNALLED_COMPLETION;
206662306a36Sopenharmony_ci	}
206762306a36Sopenharmony_ci	pr->swqe_id_counter += 1;
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	netif_info(port, tx_queued, dev,
207062306a36Sopenharmony_ci		   "post swqe on QP %d\n", pr->qp->init_attr.qp_nr);
207162306a36Sopenharmony_ci	if (netif_msg_tx_queued(port))
207262306a36Sopenharmony_ci		ehea_dump(swqe, 512, "swqe");
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci	if (unlikely(test_bit(__EHEA_STOP_XFER, &ehea_driver_flags))) {
207562306a36Sopenharmony_ci		netif_tx_stop_queue(txq);
207662306a36Sopenharmony_ci		swqe->tx_control |= EHEA_SWQE_PURGE;
207762306a36Sopenharmony_ci	}
207862306a36Sopenharmony_ci
207962306a36Sopenharmony_ci	ehea_post_swqe(pr->qp, swqe);
208062306a36Sopenharmony_ci
208162306a36Sopenharmony_ci	if (unlikely(atomic_read(&pr->swqe_avail) <= 1)) {
208262306a36Sopenharmony_ci		pr->p_stats.queue_stopped++;
208362306a36Sopenharmony_ci		netif_tx_stop_queue(txq);
208462306a36Sopenharmony_ci	}
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_ci	return NETDEV_TX_OK;
208762306a36Sopenharmony_ci}
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_cistatic int ehea_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
209062306a36Sopenharmony_ci{
209162306a36Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
209262306a36Sopenharmony_ci	struct ehea_adapter *adapter = port->adapter;
209362306a36Sopenharmony_ci	struct hcp_ehea_port_cb1 *cb1;
209462306a36Sopenharmony_ci	int index;
209562306a36Sopenharmony_ci	u64 hret;
209662306a36Sopenharmony_ci	int err = 0;
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_ci	cb1 = (void *)get_zeroed_page(GFP_KERNEL);
209962306a36Sopenharmony_ci	if (!cb1) {
210062306a36Sopenharmony_ci		pr_err("no mem for cb1\n");
210162306a36Sopenharmony_ci		err = -ENOMEM;
210262306a36Sopenharmony_ci		goto out;
210362306a36Sopenharmony_ci	}
210462306a36Sopenharmony_ci
210562306a36Sopenharmony_ci	hret = ehea_h_query_ehea_port(adapter->handle, port->logical_port_id,
210662306a36Sopenharmony_ci				      H_PORT_CB1, H_PORT_CB1_ALL, cb1);
210762306a36Sopenharmony_ci	if (hret != H_SUCCESS) {
210862306a36Sopenharmony_ci		pr_err("query_ehea_port failed\n");
210962306a36Sopenharmony_ci		err = -EINVAL;
211062306a36Sopenharmony_ci		goto out;
211162306a36Sopenharmony_ci	}
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci	index = (vid / 64);
211462306a36Sopenharmony_ci	cb1->vlan_filter[index] |= ((u64)(0x8000000000000000 >> (vid & 0x3F)));
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci	hret = ehea_h_modify_ehea_port(adapter->handle, port->logical_port_id,
211762306a36Sopenharmony_ci				       H_PORT_CB1, H_PORT_CB1_ALL, cb1);
211862306a36Sopenharmony_ci	if (hret != H_SUCCESS) {
211962306a36Sopenharmony_ci		pr_err("modify_ehea_port failed\n");
212062306a36Sopenharmony_ci		err = -EINVAL;
212162306a36Sopenharmony_ci	}
212262306a36Sopenharmony_ciout:
212362306a36Sopenharmony_ci	free_page((unsigned long)cb1);
212462306a36Sopenharmony_ci	return err;
212562306a36Sopenharmony_ci}
212662306a36Sopenharmony_ci
212762306a36Sopenharmony_cistatic int ehea_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
212862306a36Sopenharmony_ci{
212962306a36Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
213062306a36Sopenharmony_ci	struct ehea_adapter *adapter = port->adapter;
213162306a36Sopenharmony_ci	struct hcp_ehea_port_cb1 *cb1;
213262306a36Sopenharmony_ci	int index;
213362306a36Sopenharmony_ci	u64 hret;
213462306a36Sopenharmony_ci	int err = 0;
213562306a36Sopenharmony_ci
213662306a36Sopenharmony_ci	cb1 = (void *)get_zeroed_page(GFP_KERNEL);
213762306a36Sopenharmony_ci	if (!cb1) {
213862306a36Sopenharmony_ci		pr_err("no mem for cb1\n");
213962306a36Sopenharmony_ci		err = -ENOMEM;
214062306a36Sopenharmony_ci		goto out;
214162306a36Sopenharmony_ci	}
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_ci	hret = ehea_h_query_ehea_port(adapter->handle, port->logical_port_id,
214462306a36Sopenharmony_ci				      H_PORT_CB1, H_PORT_CB1_ALL, cb1);
214562306a36Sopenharmony_ci	if (hret != H_SUCCESS) {
214662306a36Sopenharmony_ci		pr_err("query_ehea_port failed\n");
214762306a36Sopenharmony_ci		err = -EINVAL;
214862306a36Sopenharmony_ci		goto out;
214962306a36Sopenharmony_ci	}
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_ci	index = (vid / 64);
215262306a36Sopenharmony_ci	cb1->vlan_filter[index] &= ~((u64)(0x8000000000000000 >> (vid & 0x3F)));
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_ci	hret = ehea_h_modify_ehea_port(adapter->handle, port->logical_port_id,
215562306a36Sopenharmony_ci				       H_PORT_CB1, H_PORT_CB1_ALL, cb1);
215662306a36Sopenharmony_ci	if (hret != H_SUCCESS) {
215762306a36Sopenharmony_ci		pr_err("modify_ehea_port failed\n");
215862306a36Sopenharmony_ci		err = -EINVAL;
215962306a36Sopenharmony_ci	}
216062306a36Sopenharmony_ciout:
216162306a36Sopenharmony_ci	free_page((unsigned long)cb1);
216262306a36Sopenharmony_ci	return err;
216362306a36Sopenharmony_ci}
216462306a36Sopenharmony_ci
216562306a36Sopenharmony_cistatic int ehea_activate_qp(struct ehea_adapter *adapter, struct ehea_qp *qp)
216662306a36Sopenharmony_ci{
216762306a36Sopenharmony_ci	int ret = -EIO;
216862306a36Sopenharmony_ci	u64 hret;
216962306a36Sopenharmony_ci	u16 dummy16 = 0;
217062306a36Sopenharmony_ci	u64 dummy64 = 0;
217162306a36Sopenharmony_ci	struct hcp_modify_qp_cb0 *cb0;
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_ci	cb0 = (void *)get_zeroed_page(GFP_KERNEL);
217462306a36Sopenharmony_ci	if (!cb0) {
217562306a36Sopenharmony_ci		ret = -ENOMEM;
217662306a36Sopenharmony_ci		goto out;
217762306a36Sopenharmony_ci	}
217862306a36Sopenharmony_ci
217962306a36Sopenharmony_ci	hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
218062306a36Sopenharmony_ci				    EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF), cb0);
218162306a36Sopenharmony_ci	if (hret != H_SUCCESS) {
218262306a36Sopenharmony_ci		pr_err("query_ehea_qp failed (1)\n");
218362306a36Sopenharmony_ci		goto out;
218462306a36Sopenharmony_ci	}
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_ci	cb0->qp_ctl_reg = H_QP_CR_STATE_INITIALIZED;
218762306a36Sopenharmony_ci	hret = ehea_h_modify_ehea_qp(adapter->handle, 0, qp->fw_handle,
218862306a36Sopenharmony_ci				     EHEA_BMASK_SET(H_QPCB0_QP_CTL_REG, 1), cb0,
218962306a36Sopenharmony_ci				     &dummy64, &dummy64, &dummy16, &dummy16);
219062306a36Sopenharmony_ci	if (hret != H_SUCCESS) {
219162306a36Sopenharmony_ci		pr_err("modify_ehea_qp failed (1)\n");
219262306a36Sopenharmony_ci		goto out;
219362306a36Sopenharmony_ci	}
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci	hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
219662306a36Sopenharmony_ci				    EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF), cb0);
219762306a36Sopenharmony_ci	if (hret != H_SUCCESS) {
219862306a36Sopenharmony_ci		pr_err("query_ehea_qp failed (2)\n");
219962306a36Sopenharmony_ci		goto out;
220062306a36Sopenharmony_ci	}
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_ci	cb0->qp_ctl_reg = H_QP_CR_ENABLED | H_QP_CR_STATE_INITIALIZED;
220362306a36Sopenharmony_ci	hret = ehea_h_modify_ehea_qp(adapter->handle, 0, qp->fw_handle,
220462306a36Sopenharmony_ci				     EHEA_BMASK_SET(H_QPCB0_QP_CTL_REG, 1), cb0,
220562306a36Sopenharmony_ci				     &dummy64, &dummy64, &dummy16, &dummy16);
220662306a36Sopenharmony_ci	if (hret != H_SUCCESS) {
220762306a36Sopenharmony_ci		pr_err("modify_ehea_qp failed (2)\n");
220862306a36Sopenharmony_ci		goto out;
220962306a36Sopenharmony_ci	}
221062306a36Sopenharmony_ci
221162306a36Sopenharmony_ci	hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
221262306a36Sopenharmony_ci				    EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF), cb0);
221362306a36Sopenharmony_ci	if (hret != H_SUCCESS) {
221462306a36Sopenharmony_ci		pr_err("query_ehea_qp failed (3)\n");
221562306a36Sopenharmony_ci		goto out;
221662306a36Sopenharmony_ci	}
221762306a36Sopenharmony_ci
221862306a36Sopenharmony_ci	cb0->qp_ctl_reg = H_QP_CR_ENABLED | H_QP_CR_STATE_RDY2SND;
221962306a36Sopenharmony_ci	hret = ehea_h_modify_ehea_qp(adapter->handle, 0, qp->fw_handle,
222062306a36Sopenharmony_ci				     EHEA_BMASK_SET(H_QPCB0_QP_CTL_REG, 1), cb0,
222162306a36Sopenharmony_ci				     &dummy64, &dummy64, &dummy16, &dummy16);
222262306a36Sopenharmony_ci	if (hret != H_SUCCESS) {
222362306a36Sopenharmony_ci		pr_err("modify_ehea_qp failed (3)\n");
222462306a36Sopenharmony_ci		goto out;
222562306a36Sopenharmony_ci	}
222662306a36Sopenharmony_ci
222762306a36Sopenharmony_ci	hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
222862306a36Sopenharmony_ci				    EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF), cb0);
222962306a36Sopenharmony_ci	if (hret != H_SUCCESS) {
223062306a36Sopenharmony_ci		pr_err("query_ehea_qp failed (4)\n");
223162306a36Sopenharmony_ci		goto out;
223262306a36Sopenharmony_ci	}
223362306a36Sopenharmony_ci
223462306a36Sopenharmony_ci	ret = 0;
223562306a36Sopenharmony_ciout:
223662306a36Sopenharmony_ci	free_page((unsigned long)cb0);
223762306a36Sopenharmony_ci	return ret;
223862306a36Sopenharmony_ci}
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_cistatic int ehea_port_res_setup(struct ehea_port *port, int def_qps)
224162306a36Sopenharmony_ci{
224262306a36Sopenharmony_ci	int ret, i;
224362306a36Sopenharmony_ci	struct port_res_cfg pr_cfg, pr_cfg_small_rx;
224462306a36Sopenharmony_ci	enum ehea_eq_type eq_type = EHEA_EQ;
224562306a36Sopenharmony_ci
224662306a36Sopenharmony_ci	port->qp_eq = ehea_create_eq(port->adapter, eq_type,
224762306a36Sopenharmony_ci				   EHEA_MAX_ENTRIES_EQ, 1);
224862306a36Sopenharmony_ci	if (!port->qp_eq) {
224962306a36Sopenharmony_ci		ret = -EINVAL;
225062306a36Sopenharmony_ci		pr_err("ehea_create_eq failed (qp_eq)\n");
225162306a36Sopenharmony_ci		goto out_kill_eq;
225262306a36Sopenharmony_ci	}
225362306a36Sopenharmony_ci
225462306a36Sopenharmony_ci	pr_cfg.max_entries_rcq = rq1_entries + rq2_entries + rq3_entries;
225562306a36Sopenharmony_ci	pr_cfg.max_entries_scq = sq_entries * 2;
225662306a36Sopenharmony_ci	pr_cfg.max_entries_sq = sq_entries;
225762306a36Sopenharmony_ci	pr_cfg.max_entries_rq1 = rq1_entries;
225862306a36Sopenharmony_ci	pr_cfg.max_entries_rq2 = rq2_entries;
225962306a36Sopenharmony_ci	pr_cfg.max_entries_rq3 = rq3_entries;
226062306a36Sopenharmony_ci
226162306a36Sopenharmony_ci	pr_cfg_small_rx.max_entries_rcq = 1;
226262306a36Sopenharmony_ci	pr_cfg_small_rx.max_entries_scq = sq_entries;
226362306a36Sopenharmony_ci	pr_cfg_small_rx.max_entries_sq = sq_entries;
226462306a36Sopenharmony_ci	pr_cfg_small_rx.max_entries_rq1 = 1;
226562306a36Sopenharmony_ci	pr_cfg_small_rx.max_entries_rq2 = 1;
226662306a36Sopenharmony_ci	pr_cfg_small_rx.max_entries_rq3 = 1;
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci	for (i = 0; i < def_qps; i++) {
226962306a36Sopenharmony_ci		ret = ehea_init_port_res(port, &port->port_res[i], &pr_cfg, i);
227062306a36Sopenharmony_ci		if (ret)
227162306a36Sopenharmony_ci			goto out_clean_pr;
227262306a36Sopenharmony_ci	}
227362306a36Sopenharmony_ci	for (i = def_qps; i < def_qps; i++) {
227462306a36Sopenharmony_ci		ret = ehea_init_port_res(port, &port->port_res[i],
227562306a36Sopenharmony_ci					 &pr_cfg_small_rx, i);
227662306a36Sopenharmony_ci		if (ret)
227762306a36Sopenharmony_ci			goto out_clean_pr;
227862306a36Sopenharmony_ci	}
227962306a36Sopenharmony_ci
228062306a36Sopenharmony_ci	return 0;
228162306a36Sopenharmony_ci
228262306a36Sopenharmony_ciout_clean_pr:
228362306a36Sopenharmony_ci	while (--i >= 0)
228462306a36Sopenharmony_ci		ehea_clean_portres(port, &port->port_res[i]);
228562306a36Sopenharmony_ci
228662306a36Sopenharmony_ciout_kill_eq:
228762306a36Sopenharmony_ci	ehea_destroy_eq(port->qp_eq);
228862306a36Sopenharmony_ci	return ret;
228962306a36Sopenharmony_ci}
229062306a36Sopenharmony_ci
229162306a36Sopenharmony_cistatic int ehea_clean_all_portres(struct ehea_port *port)
229262306a36Sopenharmony_ci{
229362306a36Sopenharmony_ci	int ret = 0;
229462306a36Sopenharmony_ci	int i;
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_ci	for (i = 0; i < port->num_def_qps; i++)
229762306a36Sopenharmony_ci		ret |= ehea_clean_portres(port, &port->port_res[i]);
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_ci	ret |= ehea_destroy_eq(port->qp_eq);
230062306a36Sopenharmony_ci
230162306a36Sopenharmony_ci	return ret;
230262306a36Sopenharmony_ci}
230362306a36Sopenharmony_ci
230462306a36Sopenharmony_cistatic void ehea_remove_adapter_mr(struct ehea_adapter *adapter)
230562306a36Sopenharmony_ci{
230662306a36Sopenharmony_ci	if (adapter->active_ports)
230762306a36Sopenharmony_ci		return;
230862306a36Sopenharmony_ci
230962306a36Sopenharmony_ci	ehea_rem_mr(&adapter->mr);
231062306a36Sopenharmony_ci}
231162306a36Sopenharmony_ci
231262306a36Sopenharmony_cistatic int ehea_add_adapter_mr(struct ehea_adapter *adapter)
231362306a36Sopenharmony_ci{
231462306a36Sopenharmony_ci	if (adapter->active_ports)
231562306a36Sopenharmony_ci		return 0;
231662306a36Sopenharmony_ci
231762306a36Sopenharmony_ci	return ehea_reg_kernel_mr(adapter, &adapter->mr);
231862306a36Sopenharmony_ci}
231962306a36Sopenharmony_ci
232062306a36Sopenharmony_cistatic int ehea_up(struct net_device *dev)
232162306a36Sopenharmony_ci{
232262306a36Sopenharmony_ci	int ret, i;
232362306a36Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
232462306a36Sopenharmony_ci
232562306a36Sopenharmony_ci	if (port->state == EHEA_PORT_UP)
232662306a36Sopenharmony_ci		return 0;
232762306a36Sopenharmony_ci
232862306a36Sopenharmony_ci	ret = ehea_port_res_setup(port, port->num_def_qps);
232962306a36Sopenharmony_ci	if (ret) {
233062306a36Sopenharmony_ci		netdev_err(dev, "port_res_failed\n");
233162306a36Sopenharmony_ci		goto out;
233262306a36Sopenharmony_ci	}
233362306a36Sopenharmony_ci
233462306a36Sopenharmony_ci	/* Set default QP for this port */
233562306a36Sopenharmony_ci	ret = ehea_configure_port(port);
233662306a36Sopenharmony_ci	if (ret) {
233762306a36Sopenharmony_ci		netdev_err(dev, "ehea_configure_port failed. ret:%d\n", ret);
233862306a36Sopenharmony_ci		goto out_clean_pr;
233962306a36Sopenharmony_ci	}
234062306a36Sopenharmony_ci
234162306a36Sopenharmony_ci	ret = ehea_reg_interrupts(dev);
234262306a36Sopenharmony_ci	if (ret) {
234362306a36Sopenharmony_ci		netdev_err(dev, "reg_interrupts failed. ret:%d\n", ret);
234462306a36Sopenharmony_ci		goto out_clean_pr;
234562306a36Sopenharmony_ci	}
234662306a36Sopenharmony_ci
234762306a36Sopenharmony_ci	for (i = 0; i < port->num_def_qps; i++) {
234862306a36Sopenharmony_ci		ret = ehea_activate_qp(port->adapter, port->port_res[i].qp);
234962306a36Sopenharmony_ci		if (ret) {
235062306a36Sopenharmony_ci			netdev_err(dev, "activate_qp failed\n");
235162306a36Sopenharmony_ci			goto out_free_irqs;
235262306a36Sopenharmony_ci		}
235362306a36Sopenharmony_ci	}
235462306a36Sopenharmony_ci
235562306a36Sopenharmony_ci	for (i = 0; i < port->num_def_qps; i++) {
235662306a36Sopenharmony_ci		ret = ehea_fill_port_res(&port->port_res[i]);
235762306a36Sopenharmony_ci		if (ret) {
235862306a36Sopenharmony_ci			netdev_err(dev, "out_free_irqs\n");
235962306a36Sopenharmony_ci			goto out_free_irqs;
236062306a36Sopenharmony_ci		}
236162306a36Sopenharmony_ci	}
236262306a36Sopenharmony_ci
236362306a36Sopenharmony_ci	ret = ehea_broadcast_reg_helper(port, H_REG_BCMC);
236462306a36Sopenharmony_ci	if (ret) {
236562306a36Sopenharmony_ci		ret = -EIO;
236662306a36Sopenharmony_ci		goto out_free_irqs;
236762306a36Sopenharmony_ci	}
236862306a36Sopenharmony_ci
236962306a36Sopenharmony_ci	port->state = EHEA_PORT_UP;
237062306a36Sopenharmony_ci
237162306a36Sopenharmony_ci	ret = 0;
237262306a36Sopenharmony_ci	goto out;
237362306a36Sopenharmony_ci
237462306a36Sopenharmony_ciout_free_irqs:
237562306a36Sopenharmony_ci	ehea_free_interrupts(dev);
237662306a36Sopenharmony_ci
237762306a36Sopenharmony_ciout_clean_pr:
237862306a36Sopenharmony_ci	ehea_clean_all_portres(port);
237962306a36Sopenharmony_ciout:
238062306a36Sopenharmony_ci	if (ret)
238162306a36Sopenharmony_ci		netdev_info(dev, "Failed starting. ret=%i\n", ret);
238262306a36Sopenharmony_ci
238362306a36Sopenharmony_ci	ehea_update_bcmc_registrations();
238462306a36Sopenharmony_ci	ehea_update_firmware_handles();
238562306a36Sopenharmony_ci
238662306a36Sopenharmony_ci	return ret;
238762306a36Sopenharmony_ci}
238862306a36Sopenharmony_ci
238962306a36Sopenharmony_cistatic void port_napi_disable(struct ehea_port *port)
239062306a36Sopenharmony_ci{
239162306a36Sopenharmony_ci	int i;
239262306a36Sopenharmony_ci
239362306a36Sopenharmony_ci	for (i = 0; i < port->num_def_qps; i++)
239462306a36Sopenharmony_ci		napi_disable(&port->port_res[i].napi);
239562306a36Sopenharmony_ci}
239662306a36Sopenharmony_ci
239762306a36Sopenharmony_cistatic void port_napi_enable(struct ehea_port *port)
239862306a36Sopenharmony_ci{
239962306a36Sopenharmony_ci	int i;
240062306a36Sopenharmony_ci
240162306a36Sopenharmony_ci	for (i = 0; i < port->num_def_qps; i++)
240262306a36Sopenharmony_ci		napi_enable(&port->port_res[i].napi);
240362306a36Sopenharmony_ci}
240462306a36Sopenharmony_ci
240562306a36Sopenharmony_cistatic int ehea_open(struct net_device *dev)
240662306a36Sopenharmony_ci{
240762306a36Sopenharmony_ci	int ret;
240862306a36Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
240962306a36Sopenharmony_ci
241062306a36Sopenharmony_ci	mutex_lock(&port->port_lock);
241162306a36Sopenharmony_ci
241262306a36Sopenharmony_ci	netif_info(port, ifup, dev, "enabling port\n");
241362306a36Sopenharmony_ci
241462306a36Sopenharmony_ci	netif_carrier_off(dev);
241562306a36Sopenharmony_ci
241662306a36Sopenharmony_ci	ret = ehea_up(dev);
241762306a36Sopenharmony_ci	if (!ret) {
241862306a36Sopenharmony_ci		port_napi_enable(port);
241962306a36Sopenharmony_ci		netif_tx_start_all_queues(dev);
242062306a36Sopenharmony_ci	}
242162306a36Sopenharmony_ci
242262306a36Sopenharmony_ci	mutex_unlock(&port->port_lock);
242362306a36Sopenharmony_ci	schedule_delayed_work(&port->stats_work,
242462306a36Sopenharmony_ci			      round_jiffies_relative(msecs_to_jiffies(1000)));
242562306a36Sopenharmony_ci
242662306a36Sopenharmony_ci	return ret;
242762306a36Sopenharmony_ci}
242862306a36Sopenharmony_ci
242962306a36Sopenharmony_cistatic int ehea_down(struct net_device *dev)
243062306a36Sopenharmony_ci{
243162306a36Sopenharmony_ci	int ret;
243262306a36Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
243362306a36Sopenharmony_ci
243462306a36Sopenharmony_ci	if (port->state == EHEA_PORT_DOWN)
243562306a36Sopenharmony_ci		return 0;
243662306a36Sopenharmony_ci
243762306a36Sopenharmony_ci	ehea_drop_multicast_list(dev);
243862306a36Sopenharmony_ci	ehea_allmulti(dev, 0);
243962306a36Sopenharmony_ci	ehea_broadcast_reg_helper(port, H_DEREG_BCMC);
244062306a36Sopenharmony_ci
244162306a36Sopenharmony_ci	ehea_free_interrupts(dev);
244262306a36Sopenharmony_ci
244362306a36Sopenharmony_ci	port->state = EHEA_PORT_DOWN;
244462306a36Sopenharmony_ci
244562306a36Sopenharmony_ci	ehea_update_bcmc_registrations();
244662306a36Sopenharmony_ci
244762306a36Sopenharmony_ci	ret = ehea_clean_all_portres(port);
244862306a36Sopenharmony_ci	if (ret)
244962306a36Sopenharmony_ci		netdev_info(dev, "Failed freeing resources. ret=%i\n", ret);
245062306a36Sopenharmony_ci
245162306a36Sopenharmony_ci	ehea_update_firmware_handles();
245262306a36Sopenharmony_ci
245362306a36Sopenharmony_ci	return ret;
245462306a36Sopenharmony_ci}
245562306a36Sopenharmony_ci
245662306a36Sopenharmony_cistatic int ehea_stop(struct net_device *dev)
245762306a36Sopenharmony_ci{
245862306a36Sopenharmony_ci	int ret;
245962306a36Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
246062306a36Sopenharmony_ci
246162306a36Sopenharmony_ci	netif_info(port, ifdown, dev, "disabling port\n");
246262306a36Sopenharmony_ci
246362306a36Sopenharmony_ci	set_bit(__EHEA_DISABLE_PORT_RESET, &port->flags);
246462306a36Sopenharmony_ci	cancel_work_sync(&port->reset_task);
246562306a36Sopenharmony_ci	cancel_delayed_work_sync(&port->stats_work);
246662306a36Sopenharmony_ci	mutex_lock(&port->port_lock);
246762306a36Sopenharmony_ci	netif_tx_stop_all_queues(dev);
246862306a36Sopenharmony_ci	port_napi_disable(port);
246962306a36Sopenharmony_ci	ret = ehea_down(dev);
247062306a36Sopenharmony_ci	mutex_unlock(&port->port_lock);
247162306a36Sopenharmony_ci	clear_bit(__EHEA_DISABLE_PORT_RESET, &port->flags);
247262306a36Sopenharmony_ci	return ret;
247362306a36Sopenharmony_ci}
247462306a36Sopenharmony_ci
247562306a36Sopenharmony_cistatic void ehea_purge_sq(struct ehea_qp *orig_qp)
247662306a36Sopenharmony_ci{
247762306a36Sopenharmony_ci	struct ehea_qp qp = *orig_qp;
247862306a36Sopenharmony_ci	struct ehea_qp_init_attr *init_attr = &qp.init_attr;
247962306a36Sopenharmony_ci	struct ehea_swqe *swqe;
248062306a36Sopenharmony_ci	int wqe_index;
248162306a36Sopenharmony_ci	int i;
248262306a36Sopenharmony_ci
248362306a36Sopenharmony_ci	for (i = 0; i < init_attr->act_nr_send_wqes; i++) {
248462306a36Sopenharmony_ci		swqe = ehea_get_swqe(&qp, &wqe_index);
248562306a36Sopenharmony_ci		swqe->tx_control |= EHEA_SWQE_PURGE;
248662306a36Sopenharmony_ci	}
248762306a36Sopenharmony_ci}
248862306a36Sopenharmony_ci
248962306a36Sopenharmony_cistatic void ehea_flush_sq(struct ehea_port *port)
249062306a36Sopenharmony_ci{
249162306a36Sopenharmony_ci	int i;
249262306a36Sopenharmony_ci
249362306a36Sopenharmony_ci	for (i = 0; i < port->num_def_qps; i++) {
249462306a36Sopenharmony_ci		struct ehea_port_res *pr = &port->port_res[i];
249562306a36Sopenharmony_ci		int swqe_max = pr->sq_skba_size - 2 - pr->swqe_ll_count;
249662306a36Sopenharmony_ci		int ret;
249762306a36Sopenharmony_ci
249862306a36Sopenharmony_ci		ret = wait_event_timeout(port->swqe_avail_wq,
249962306a36Sopenharmony_ci			 atomic_read(&pr->swqe_avail) >= swqe_max,
250062306a36Sopenharmony_ci			 msecs_to_jiffies(100));
250162306a36Sopenharmony_ci
250262306a36Sopenharmony_ci		if (!ret) {
250362306a36Sopenharmony_ci			pr_err("WARNING: sq not flushed completely\n");
250462306a36Sopenharmony_ci			break;
250562306a36Sopenharmony_ci		}
250662306a36Sopenharmony_ci	}
250762306a36Sopenharmony_ci}
250862306a36Sopenharmony_ci
250962306a36Sopenharmony_cistatic int ehea_stop_qps(struct net_device *dev)
251062306a36Sopenharmony_ci{
251162306a36Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
251262306a36Sopenharmony_ci	struct ehea_adapter *adapter = port->adapter;
251362306a36Sopenharmony_ci	struct hcp_modify_qp_cb0 *cb0;
251462306a36Sopenharmony_ci	int ret = -EIO;
251562306a36Sopenharmony_ci	int dret;
251662306a36Sopenharmony_ci	int i;
251762306a36Sopenharmony_ci	u64 hret;
251862306a36Sopenharmony_ci	u64 dummy64 = 0;
251962306a36Sopenharmony_ci	u16 dummy16 = 0;
252062306a36Sopenharmony_ci
252162306a36Sopenharmony_ci	cb0 = (void *)get_zeroed_page(GFP_KERNEL);
252262306a36Sopenharmony_ci	if (!cb0) {
252362306a36Sopenharmony_ci		ret = -ENOMEM;
252462306a36Sopenharmony_ci		goto out;
252562306a36Sopenharmony_ci	}
252662306a36Sopenharmony_ci
252762306a36Sopenharmony_ci	for (i = 0; i < (port->num_def_qps); i++) {
252862306a36Sopenharmony_ci		struct ehea_port_res *pr =  &port->port_res[i];
252962306a36Sopenharmony_ci		struct ehea_qp *qp = pr->qp;
253062306a36Sopenharmony_ci
253162306a36Sopenharmony_ci		/* Purge send queue */
253262306a36Sopenharmony_ci		ehea_purge_sq(qp);
253362306a36Sopenharmony_ci
253462306a36Sopenharmony_ci		/* Disable queue pair */
253562306a36Sopenharmony_ci		hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
253662306a36Sopenharmony_ci					    EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF),
253762306a36Sopenharmony_ci					    cb0);
253862306a36Sopenharmony_ci		if (hret != H_SUCCESS) {
253962306a36Sopenharmony_ci			pr_err("query_ehea_qp failed (1)\n");
254062306a36Sopenharmony_ci			goto out;
254162306a36Sopenharmony_ci		}
254262306a36Sopenharmony_ci
254362306a36Sopenharmony_ci		cb0->qp_ctl_reg = (cb0->qp_ctl_reg & H_QP_CR_RES_STATE) << 8;
254462306a36Sopenharmony_ci		cb0->qp_ctl_reg &= ~H_QP_CR_ENABLED;
254562306a36Sopenharmony_ci
254662306a36Sopenharmony_ci		hret = ehea_h_modify_ehea_qp(adapter->handle, 0, qp->fw_handle,
254762306a36Sopenharmony_ci					     EHEA_BMASK_SET(H_QPCB0_QP_CTL_REG,
254862306a36Sopenharmony_ci							    1), cb0, &dummy64,
254962306a36Sopenharmony_ci					     &dummy64, &dummy16, &dummy16);
255062306a36Sopenharmony_ci		if (hret != H_SUCCESS) {
255162306a36Sopenharmony_ci			pr_err("modify_ehea_qp failed (1)\n");
255262306a36Sopenharmony_ci			goto out;
255362306a36Sopenharmony_ci		}
255462306a36Sopenharmony_ci
255562306a36Sopenharmony_ci		hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
255662306a36Sopenharmony_ci					    EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF),
255762306a36Sopenharmony_ci					    cb0);
255862306a36Sopenharmony_ci		if (hret != H_SUCCESS) {
255962306a36Sopenharmony_ci			pr_err("query_ehea_qp failed (2)\n");
256062306a36Sopenharmony_ci			goto out;
256162306a36Sopenharmony_ci		}
256262306a36Sopenharmony_ci
256362306a36Sopenharmony_ci		/* deregister shared memory regions */
256462306a36Sopenharmony_ci		dret = ehea_rem_smrs(pr);
256562306a36Sopenharmony_ci		if (dret) {
256662306a36Sopenharmony_ci			pr_err("unreg shared memory region failed\n");
256762306a36Sopenharmony_ci			goto out;
256862306a36Sopenharmony_ci		}
256962306a36Sopenharmony_ci	}
257062306a36Sopenharmony_ci
257162306a36Sopenharmony_ci	ret = 0;
257262306a36Sopenharmony_ciout:
257362306a36Sopenharmony_ci	free_page((unsigned long)cb0);
257462306a36Sopenharmony_ci
257562306a36Sopenharmony_ci	return ret;
257662306a36Sopenharmony_ci}
257762306a36Sopenharmony_ci
257862306a36Sopenharmony_cistatic void ehea_update_rqs(struct ehea_qp *orig_qp, struct ehea_port_res *pr)
257962306a36Sopenharmony_ci{
258062306a36Sopenharmony_ci	struct ehea_qp qp = *orig_qp;
258162306a36Sopenharmony_ci	struct ehea_qp_init_attr *init_attr = &qp.init_attr;
258262306a36Sopenharmony_ci	struct ehea_rwqe *rwqe;
258362306a36Sopenharmony_ci	struct sk_buff **skba_rq2 = pr->rq2_skba.arr;
258462306a36Sopenharmony_ci	struct sk_buff **skba_rq3 = pr->rq3_skba.arr;
258562306a36Sopenharmony_ci	struct sk_buff *skb;
258662306a36Sopenharmony_ci	u32 lkey = pr->recv_mr.lkey;
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ci
258962306a36Sopenharmony_ci	int i;
259062306a36Sopenharmony_ci	int index;
259162306a36Sopenharmony_ci
259262306a36Sopenharmony_ci	for (i = 0; i < init_attr->act_nr_rwqes_rq2 + 1; i++) {
259362306a36Sopenharmony_ci		rwqe = ehea_get_next_rwqe(&qp, 2);
259462306a36Sopenharmony_ci		rwqe->sg_list[0].l_key = lkey;
259562306a36Sopenharmony_ci		index = EHEA_BMASK_GET(EHEA_WR_ID_INDEX, rwqe->wr_id);
259662306a36Sopenharmony_ci		skb = skba_rq2[index];
259762306a36Sopenharmony_ci		if (skb)
259862306a36Sopenharmony_ci			rwqe->sg_list[0].vaddr = ehea_map_vaddr(skb->data);
259962306a36Sopenharmony_ci	}
260062306a36Sopenharmony_ci
260162306a36Sopenharmony_ci	for (i = 0; i < init_attr->act_nr_rwqes_rq3 + 1; i++) {
260262306a36Sopenharmony_ci		rwqe = ehea_get_next_rwqe(&qp, 3);
260362306a36Sopenharmony_ci		rwqe->sg_list[0].l_key = lkey;
260462306a36Sopenharmony_ci		index = EHEA_BMASK_GET(EHEA_WR_ID_INDEX, rwqe->wr_id);
260562306a36Sopenharmony_ci		skb = skba_rq3[index];
260662306a36Sopenharmony_ci		if (skb)
260762306a36Sopenharmony_ci			rwqe->sg_list[0].vaddr = ehea_map_vaddr(skb->data);
260862306a36Sopenharmony_ci	}
260962306a36Sopenharmony_ci}
261062306a36Sopenharmony_ci
261162306a36Sopenharmony_cistatic int ehea_restart_qps(struct net_device *dev)
261262306a36Sopenharmony_ci{
261362306a36Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
261462306a36Sopenharmony_ci	struct ehea_adapter *adapter = port->adapter;
261562306a36Sopenharmony_ci	int ret = 0;
261662306a36Sopenharmony_ci	int i;
261762306a36Sopenharmony_ci
261862306a36Sopenharmony_ci	struct hcp_modify_qp_cb0 *cb0;
261962306a36Sopenharmony_ci	u64 hret;
262062306a36Sopenharmony_ci	u64 dummy64 = 0;
262162306a36Sopenharmony_ci	u16 dummy16 = 0;
262262306a36Sopenharmony_ci
262362306a36Sopenharmony_ci	cb0 = (void *)get_zeroed_page(GFP_KERNEL);
262462306a36Sopenharmony_ci	if (!cb0)
262562306a36Sopenharmony_ci		return -ENOMEM;
262662306a36Sopenharmony_ci
262762306a36Sopenharmony_ci	for (i = 0; i < (port->num_def_qps); i++) {
262862306a36Sopenharmony_ci		struct ehea_port_res *pr =  &port->port_res[i];
262962306a36Sopenharmony_ci		struct ehea_qp *qp = pr->qp;
263062306a36Sopenharmony_ci
263162306a36Sopenharmony_ci		ret = ehea_gen_smrs(pr);
263262306a36Sopenharmony_ci		if (ret) {
263362306a36Sopenharmony_ci			netdev_err(dev, "creation of shared memory regions failed\n");
263462306a36Sopenharmony_ci			goto out;
263562306a36Sopenharmony_ci		}
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_ci		ehea_update_rqs(qp, pr);
263862306a36Sopenharmony_ci
263962306a36Sopenharmony_ci		/* Enable queue pair */
264062306a36Sopenharmony_ci		hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
264162306a36Sopenharmony_ci					    EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF),
264262306a36Sopenharmony_ci					    cb0);
264362306a36Sopenharmony_ci		if (hret != H_SUCCESS) {
264462306a36Sopenharmony_ci			netdev_err(dev, "query_ehea_qp failed (1)\n");
264562306a36Sopenharmony_ci			ret = -EFAULT;
264662306a36Sopenharmony_ci			goto out;
264762306a36Sopenharmony_ci		}
264862306a36Sopenharmony_ci
264962306a36Sopenharmony_ci		cb0->qp_ctl_reg = (cb0->qp_ctl_reg & H_QP_CR_RES_STATE) << 8;
265062306a36Sopenharmony_ci		cb0->qp_ctl_reg |= H_QP_CR_ENABLED;
265162306a36Sopenharmony_ci
265262306a36Sopenharmony_ci		hret = ehea_h_modify_ehea_qp(adapter->handle, 0, qp->fw_handle,
265362306a36Sopenharmony_ci					     EHEA_BMASK_SET(H_QPCB0_QP_CTL_REG,
265462306a36Sopenharmony_ci							    1), cb0, &dummy64,
265562306a36Sopenharmony_ci					     &dummy64, &dummy16, &dummy16);
265662306a36Sopenharmony_ci		if (hret != H_SUCCESS) {
265762306a36Sopenharmony_ci			netdev_err(dev, "modify_ehea_qp failed (1)\n");
265862306a36Sopenharmony_ci			ret = -EFAULT;
265962306a36Sopenharmony_ci			goto out;
266062306a36Sopenharmony_ci		}
266162306a36Sopenharmony_ci
266262306a36Sopenharmony_ci		hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
266362306a36Sopenharmony_ci					    EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF),
266462306a36Sopenharmony_ci					    cb0);
266562306a36Sopenharmony_ci		if (hret != H_SUCCESS) {
266662306a36Sopenharmony_ci			netdev_err(dev, "query_ehea_qp failed (2)\n");
266762306a36Sopenharmony_ci			ret = -EFAULT;
266862306a36Sopenharmony_ci			goto out;
266962306a36Sopenharmony_ci		}
267062306a36Sopenharmony_ci
267162306a36Sopenharmony_ci		/* refill entire queue */
267262306a36Sopenharmony_ci		ehea_refill_rq1(pr, pr->rq1_skba.index, 0);
267362306a36Sopenharmony_ci		ehea_refill_rq2(pr, 0);
267462306a36Sopenharmony_ci		ehea_refill_rq3(pr, 0);
267562306a36Sopenharmony_ci	}
267662306a36Sopenharmony_ciout:
267762306a36Sopenharmony_ci	free_page((unsigned long)cb0);
267862306a36Sopenharmony_ci
267962306a36Sopenharmony_ci	return ret;
268062306a36Sopenharmony_ci}
268162306a36Sopenharmony_ci
268262306a36Sopenharmony_cistatic void ehea_reset_port(struct work_struct *work)
268362306a36Sopenharmony_ci{
268462306a36Sopenharmony_ci	int ret;
268562306a36Sopenharmony_ci	struct ehea_port *port =
268662306a36Sopenharmony_ci		container_of(work, struct ehea_port, reset_task);
268762306a36Sopenharmony_ci	struct net_device *dev = port->netdev;
268862306a36Sopenharmony_ci
268962306a36Sopenharmony_ci	mutex_lock(&dlpar_mem_lock);
269062306a36Sopenharmony_ci	port->resets++;
269162306a36Sopenharmony_ci	mutex_lock(&port->port_lock);
269262306a36Sopenharmony_ci	netif_tx_disable(dev);
269362306a36Sopenharmony_ci
269462306a36Sopenharmony_ci	port_napi_disable(port);
269562306a36Sopenharmony_ci
269662306a36Sopenharmony_ci	ehea_down(dev);
269762306a36Sopenharmony_ci
269862306a36Sopenharmony_ci	ret = ehea_up(dev);
269962306a36Sopenharmony_ci	if (ret)
270062306a36Sopenharmony_ci		goto out;
270162306a36Sopenharmony_ci
270262306a36Sopenharmony_ci	ehea_set_multicast_list(dev);
270362306a36Sopenharmony_ci
270462306a36Sopenharmony_ci	netif_info(port, timer, dev, "reset successful\n");
270562306a36Sopenharmony_ci
270662306a36Sopenharmony_ci	port_napi_enable(port);
270762306a36Sopenharmony_ci
270862306a36Sopenharmony_ci	netif_tx_wake_all_queues(dev);
270962306a36Sopenharmony_ciout:
271062306a36Sopenharmony_ci	mutex_unlock(&port->port_lock);
271162306a36Sopenharmony_ci	mutex_unlock(&dlpar_mem_lock);
271262306a36Sopenharmony_ci}
271362306a36Sopenharmony_ci
271462306a36Sopenharmony_cistatic void ehea_rereg_mrs(void)
271562306a36Sopenharmony_ci{
271662306a36Sopenharmony_ci	int ret, i;
271762306a36Sopenharmony_ci	struct ehea_adapter *adapter;
271862306a36Sopenharmony_ci
271962306a36Sopenharmony_ci	pr_info("LPAR memory changed - re-initializing driver\n");
272062306a36Sopenharmony_ci
272162306a36Sopenharmony_ci	list_for_each_entry(adapter, &adapter_list, list)
272262306a36Sopenharmony_ci		if (adapter->active_ports) {
272362306a36Sopenharmony_ci			/* Shutdown all ports */
272462306a36Sopenharmony_ci			for (i = 0; i < EHEA_MAX_PORTS; i++) {
272562306a36Sopenharmony_ci				struct ehea_port *port = adapter->port[i];
272662306a36Sopenharmony_ci				struct net_device *dev;
272762306a36Sopenharmony_ci
272862306a36Sopenharmony_ci				if (!port)
272962306a36Sopenharmony_ci					continue;
273062306a36Sopenharmony_ci
273162306a36Sopenharmony_ci				dev = port->netdev;
273262306a36Sopenharmony_ci
273362306a36Sopenharmony_ci				if (dev->flags & IFF_UP) {
273462306a36Sopenharmony_ci					mutex_lock(&port->port_lock);
273562306a36Sopenharmony_ci					netif_tx_disable(dev);
273662306a36Sopenharmony_ci					ehea_flush_sq(port);
273762306a36Sopenharmony_ci					ret = ehea_stop_qps(dev);
273862306a36Sopenharmony_ci					if (ret) {
273962306a36Sopenharmony_ci						mutex_unlock(&port->port_lock);
274062306a36Sopenharmony_ci						goto out;
274162306a36Sopenharmony_ci					}
274262306a36Sopenharmony_ci					port_napi_disable(port);
274362306a36Sopenharmony_ci					mutex_unlock(&port->port_lock);
274462306a36Sopenharmony_ci				}
274562306a36Sopenharmony_ci				reset_sq_restart_flag(port);
274662306a36Sopenharmony_ci			}
274762306a36Sopenharmony_ci
274862306a36Sopenharmony_ci			/* Unregister old memory region */
274962306a36Sopenharmony_ci			ret = ehea_rem_mr(&adapter->mr);
275062306a36Sopenharmony_ci			if (ret) {
275162306a36Sopenharmony_ci				pr_err("unregister MR failed - driver inoperable!\n");
275262306a36Sopenharmony_ci				goto out;
275362306a36Sopenharmony_ci			}
275462306a36Sopenharmony_ci		}
275562306a36Sopenharmony_ci
275662306a36Sopenharmony_ci	clear_bit(__EHEA_STOP_XFER, &ehea_driver_flags);
275762306a36Sopenharmony_ci
275862306a36Sopenharmony_ci	list_for_each_entry(adapter, &adapter_list, list)
275962306a36Sopenharmony_ci		if (adapter->active_ports) {
276062306a36Sopenharmony_ci			/* Register new memory region */
276162306a36Sopenharmony_ci			ret = ehea_reg_kernel_mr(adapter, &adapter->mr);
276262306a36Sopenharmony_ci			if (ret) {
276362306a36Sopenharmony_ci				pr_err("register MR failed - driver inoperable!\n");
276462306a36Sopenharmony_ci				goto out;
276562306a36Sopenharmony_ci			}
276662306a36Sopenharmony_ci
276762306a36Sopenharmony_ci			/* Restart all ports */
276862306a36Sopenharmony_ci			for (i = 0; i < EHEA_MAX_PORTS; i++) {
276962306a36Sopenharmony_ci				struct ehea_port *port = adapter->port[i];
277062306a36Sopenharmony_ci
277162306a36Sopenharmony_ci				if (port) {
277262306a36Sopenharmony_ci					struct net_device *dev = port->netdev;
277362306a36Sopenharmony_ci
277462306a36Sopenharmony_ci					if (dev->flags & IFF_UP) {
277562306a36Sopenharmony_ci						mutex_lock(&port->port_lock);
277662306a36Sopenharmony_ci						ret = ehea_restart_qps(dev);
277762306a36Sopenharmony_ci						if (!ret) {
277862306a36Sopenharmony_ci							check_sqs(port);
277962306a36Sopenharmony_ci							port_napi_enable(port);
278062306a36Sopenharmony_ci							netif_tx_wake_all_queues(dev);
278162306a36Sopenharmony_ci						} else {
278262306a36Sopenharmony_ci							netdev_err(dev, "Unable to restart QPS\n");
278362306a36Sopenharmony_ci						}
278462306a36Sopenharmony_ci						mutex_unlock(&port->port_lock);
278562306a36Sopenharmony_ci					}
278662306a36Sopenharmony_ci				}
278762306a36Sopenharmony_ci			}
278862306a36Sopenharmony_ci		}
278962306a36Sopenharmony_ci	pr_info("re-initializing driver complete\n");
279062306a36Sopenharmony_ciout:
279162306a36Sopenharmony_ci	return;
279262306a36Sopenharmony_ci}
279362306a36Sopenharmony_ci
279462306a36Sopenharmony_cistatic void ehea_tx_watchdog(struct net_device *dev, unsigned int txqueue)
279562306a36Sopenharmony_ci{
279662306a36Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
279762306a36Sopenharmony_ci
279862306a36Sopenharmony_ci	if (netif_carrier_ok(dev) &&
279962306a36Sopenharmony_ci	    !test_bit(__EHEA_STOP_XFER, &ehea_driver_flags))
280062306a36Sopenharmony_ci		ehea_schedule_port_reset(port);
280162306a36Sopenharmony_ci}
280262306a36Sopenharmony_ci
280362306a36Sopenharmony_cistatic int ehea_sense_adapter_attr(struct ehea_adapter *adapter)
280462306a36Sopenharmony_ci{
280562306a36Sopenharmony_ci	struct hcp_query_ehea *cb;
280662306a36Sopenharmony_ci	u64 hret;
280762306a36Sopenharmony_ci	int ret;
280862306a36Sopenharmony_ci
280962306a36Sopenharmony_ci	cb = (void *)get_zeroed_page(GFP_KERNEL);
281062306a36Sopenharmony_ci	if (!cb) {
281162306a36Sopenharmony_ci		ret = -ENOMEM;
281262306a36Sopenharmony_ci		goto out;
281362306a36Sopenharmony_ci	}
281462306a36Sopenharmony_ci
281562306a36Sopenharmony_ci	hret = ehea_h_query_ehea(adapter->handle, cb);
281662306a36Sopenharmony_ci
281762306a36Sopenharmony_ci	if (hret != H_SUCCESS) {
281862306a36Sopenharmony_ci		ret = -EIO;
281962306a36Sopenharmony_ci		goto out_herr;
282062306a36Sopenharmony_ci	}
282162306a36Sopenharmony_ci
282262306a36Sopenharmony_ci	adapter->max_mc_mac = cb->max_mc_mac - 1;
282362306a36Sopenharmony_ci	ret = 0;
282462306a36Sopenharmony_ci
282562306a36Sopenharmony_ciout_herr:
282662306a36Sopenharmony_ci	free_page((unsigned long)cb);
282762306a36Sopenharmony_ciout:
282862306a36Sopenharmony_ci	return ret;
282962306a36Sopenharmony_ci}
283062306a36Sopenharmony_ci
283162306a36Sopenharmony_cistatic int ehea_get_jumboframe_status(struct ehea_port *port, int *jumbo)
283262306a36Sopenharmony_ci{
283362306a36Sopenharmony_ci	struct hcp_ehea_port_cb4 *cb4;
283462306a36Sopenharmony_ci	u64 hret;
283562306a36Sopenharmony_ci	int ret = 0;
283662306a36Sopenharmony_ci
283762306a36Sopenharmony_ci	*jumbo = 0;
283862306a36Sopenharmony_ci
283962306a36Sopenharmony_ci	/* (Try to) enable *jumbo frames */
284062306a36Sopenharmony_ci	cb4 = (void *)get_zeroed_page(GFP_KERNEL);
284162306a36Sopenharmony_ci	if (!cb4) {
284262306a36Sopenharmony_ci		pr_err("no mem for cb4\n");
284362306a36Sopenharmony_ci		ret = -ENOMEM;
284462306a36Sopenharmony_ci		goto out;
284562306a36Sopenharmony_ci	} else {
284662306a36Sopenharmony_ci		hret = ehea_h_query_ehea_port(port->adapter->handle,
284762306a36Sopenharmony_ci					      port->logical_port_id,
284862306a36Sopenharmony_ci					      H_PORT_CB4,
284962306a36Sopenharmony_ci					      H_PORT_CB4_JUMBO, cb4);
285062306a36Sopenharmony_ci		if (hret == H_SUCCESS) {
285162306a36Sopenharmony_ci			if (cb4->jumbo_frame)
285262306a36Sopenharmony_ci				*jumbo = 1;
285362306a36Sopenharmony_ci			else {
285462306a36Sopenharmony_ci				cb4->jumbo_frame = 1;
285562306a36Sopenharmony_ci				hret = ehea_h_modify_ehea_port(port->adapter->
285662306a36Sopenharmony_ci							       handle,
285762306a36Sopenharmony_ci							       port->
285862306a36Sopenharmony_ci							       logical_port_id,
285962306a36Sopenharmony_ci							       H_PORT_CB4,
286062306a36Sopenharmony_ci							       H_PORT_CB4_JUMBO,
286162306a36Sopenharmony_ci							       cb4);
286262306a36Sopenharmony_ci				if (hret == H_SUCCESS)
286362306a36Sopenharmony_ci					*jumbo = 1;
286462306a36Sopenharmony_ci			}
286562306a36Sopenharmony_ci		} else
286662306a36Sopenharmony_ci			ret = -EINVAL;
286762306a36Sopenharmony_ci
286862306a36Sopenharmony_ci		free_page((unsigned long)cb4);
286962306a36Sopenharmony_ci	}
287062306a36Sopenharmony_ciout:
287162306a36Sopenharmony_ci	return ret;
287262306a36Sopenharmony_ci}
287362306a36Sopenharmony_ci
287462306a36Sopenharmony_cistatic ssize_t log_port_id_show(struct device *dev,
287562306a36Sopenharmony_ci				struct device_attribute *attr, char *buf)
287662306a36Sopenharmony_ci{
287762306a36Sopenharmony_ci	struct ehea_port *port = container_of(dev, struct ehea_port, ofdev.dev);
287862306a36Sopenharmony_ci	return sprintf(buf, "%d", port->logical_port_id);
287962306a36Sopenharmony_ci}
288062306a36Sopenharmony_ci
288162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(log_port_id);
288262306a36Sopenharmony_ci
288362306a36Sopenharmony_cistatic void logical_port_release(struct device *dev)
288462306a36Sopenharmony_ci{
288562306a36Sopenharmony_ci	struct ehea_port *port = container_of(dev, struct ehea_port, ofdev.dev);
288662306a36Sopenharmony_ci	of_node_put(port->ofdev.dev.of_node);
288762306a36Sopenharmony_ci}
288862306a36Sopenharmony_ci
288962306a36Sopenharmony_cistatic struct device *ehea_register_port(struct ehea_port *port,
289062306a36Sopenharmony_ci					 struct device_node *dn)
289162306a36Sopenharmony_ci{
289262306a36Sopenharmony_ci	int ret;
289362306a36Sopenharmony_ci
289462306a36Sopenharmony_ci	port->ofdev.dev.of_node = of_node_get(dn);
289562306a36Sopenharmony_ci	port->ofdev.dev.parent = &port->adapter->ofdev->dev;
289662306a36Sopenharmony_ci	port->ofdev.dev.bus = &ibmebus_bus_type;
289762306a36Sopenharmony_ci
289862306a36Sopenharmony_ci	dev_set_name(&port->ofdev.dev, "port%d", port_name_cnt++);
289962306a36Sopenharmony_ci	port->ofdev.dev.release = logical_port_release;
290062306a36Sopenharmony_ci
290162306a36Sopenharmony_ci	ret = of_device_register(&port->ofdev);
290262306a36Sopenharmony_ci	if (ret) {
290362306a36Sopenharmony_ci		pr_err("failed to register device. ret=%d\n", ret);
290462306a36Sopenharmony_ci		put_device(&port->ofdev.dev);
290562306a36Sopenharmony_ci		goto out;
290662306a36Sopenharmony_ci	}
290762306a36Sopenharmony_ci
290862306a36Sopenharmony_ci	ret = device_create_file(&port->ofdev.dev, &dev_attr_log_port_id);
290962306a36Sopenharmony_ci	if (ret) {
291062306a36Sopenharmony_ci		pr_err("failed to register attributes, ret=%d\n", ret);
291162306a36Sopenharmony_ci		goto out_unreg_of_dev;
291262306a36Sopenharmony_ci	}
291362306a36Sopenharmony_ci
291462306a36Sopenharmony_ci	return &port->ofdev.dev;
291562306a36Sopenharmony_ci
291662306a36Sopenharmony_ciout_unreg_of_dev:
291762306a36Sopenharmony_ci	of_device_unregister(&port->ofdev);
291862306a36Sopenharmony_ciout:
291962306a36Sopenharmony_ci	return NULL;
292062306a36Sopenharmony_ci}
292162306a36Sopenharmony_ci
292262306a36Sopenharmony_cistatic void ehea_unregister_port(struct ehea_port *port)
292362306a36Sopenharmony_ci{
292462306a36Sopenharmony_ci	device_remove_file(&port->ofdev.dev, &dev_attr_log_port_id);
292562306a36Sopenharmony_ci	of_device_unregister(&port->ofdev);
292662306a36Sopenharmony_ci}
292762306a36Sopenharmony_ci
292862306a36Sopenharmony_cistatic const struct net_device_ops ehea_netdev_ops = {
292962306a36Sopenharmony_ci	.ndo_open		= ehea_open,
293062306a36Sopenharmony_ci	.ndo_stop		= ehea_stop,
293162306a36Sopenharmony_ci	.ndo_start_xmit		= ehea_start_xmit,
293262306a36Sopenharmony_ci	.ndo_get_stats64	= ehea_get_stats64,
293362306a36Sopenharmony_ci	.ndo_set_mac_address	= ehea_set_mac_addr,
293462306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
293562306a36Sopenharmony_ci	.ndo_set_rx_mode	= ehea_set_multicast_list,
293662306a36Sopenharmony_ci	.ndo_vlan_rx_add_vid	= ehea_vlan_rx_add_vid,
293762306a36Sopenharmony_ci	.ndo_vlan_rx_kill_vid	= ehea_vlan_rx_kill_vid,
293862306a36Sopenharmony_ci	.ndo_tx_timeout		= ehea_tx_watchdog,
293962306a36Sopenharmony_ci};
294062306a36Sopenharmony_ci
294162306a36Sopenharmony_cistatic struct ehea_port *ehea_setup_single_port(struct ehea_adapter *adapter,
294262306a36Sopenharmony_ci					 u32 logical_port_id,
294362306a36Sopenharmony_ci					 struct device_node *dn)
294462306a36Sopenharmony_ci{
294562306a36Sopenharmony_ci	int ret;
294662306a36Sopenharmony_ci	struct net_device *dev;
294762306a36Sopenharmony_ci	struct ehea_port *port;
294862306a36Sopenharmony_ci	struct device *port_dev;
294962306a36Sopenharmony_ci	int jumbo;
295062306a36Sopenharmony_ci
295162306a36Sopenharmony_ci	/* allocate memory for the port structures */
295262306a36Sopenharmony_ci	dev = alloc_etherdev_mq(sizeof(struct ehea_port), EHEA_MAX_PORT_RES);
295362306a36Sopenharmony_ci
295462306a36Sopenharmony_ci	if (!dev) {
295562306a36Sopenharmony_ci		ret = -ENOMEM;
295662306a36Sopenharmony_ci		goto out_err;
295762306a36Sopenharmony_ci	}
295862306a36Sopenharmony_ci
295962306a36Sopenharmony_ci	port = netdev_priv(dev);
296062306a36Sopenharmony_ci
296162306a36Sopenharmony_ci	mutex_init(&port->port_lock);
296262306a36Sopenharmony_ci	port->state = EHEA_PORT_DOWN;
296362306a36Sopenharmony_ci	port->sig_comp_iv = sq_entries / 10;
296462306a36Sopenharmony_ci
296562306a36Sopenharmony_ci	port->adapter = adapter;
296662306a36Sopenharmony_ci	port->netdev = dev;
296762306a36Sopenharmony_ci	port->logical_port_id = logical_port_id;
296862306a36Sopenharmony_ci
296962306a36Sopenharmony_ci	port->msg_enable = netif_msg_init(msg_level, EHEA_MSG_DEFAULT);
297062306a36Sopenharmony_ci
297162306a36Sopenharmony_ci	port->mc_list = kzalloc(sizeof(struct ehea_mc_list), GFP_KERNEL);
297262306a36Sopenharmony_ci	if (!port->mc_list) {
297362306a36Sopenharmony_ci		ret = -ENOMEM;
297462306a36Sopenharmony_ci		goto out_free_ethdev;
297562306a36Sopenharmony_ci	}
297662306a36Sopenharmony_ci
297762306a36Sopenharmony_ci	INIT_LIST_HEAD(&port->mc_list->list);
297862306a36Sopenharmony_ci
297962306a36Sopenharmony_ci	ret = ehea_sense_port_attr(port);
298062306a36Sopenharmony_ci	if (ret)
298162306a36Sopenharmony_ci		goto out_free_mc_list;
298262306a36Sopenharmony_ci
298362306a36Sopenharmony_ci	netif_set_real_num_rx_queues(dev, port->num_def_qps);
298462306a36Sopenharmony_ci	netif_set_real_num_tx_queues(dev, port->num_def_qps);
298562306a36Sopenharmony_ci
298662306a36Sopenharmony_ci	port_dev = ehea_register_port(port, dn);
298762306a36Sopenharmony_ci	if (!port_dev)
298862306a36Sopenharmony_ci		goto out_free_mc_list;
298962306a36Sopenharmony_ci
299062306a36Sopenharmony_ci	SET_NETDEV_DEV(dev, port_dev);
299162306a36Sopenharmony_ci
299262306a36Sopenharmony_ci	/* initialize net_device structure */
299362306a36Sopenharmony_ci	eth_hw_addr_set(dev, (u8 *)&port->mac_addr);
299462306a36Sopenharmony_ci
299562306a36Sopenharmony_ci	dev->netdev_ops = &ehea_netdev_ops;
299662306a36Sopenharmony_ci	ehea_set_ethtool_ops(dev);
299762306a36Sopenharmony_ci
299862306a36Sopenharmony_ci	dev->hw_features = NETIF_F_SG | NETIF_F_TSO |
299962306a36Sopenharmony_ci		      NETIF_F_IP_CSUM | NETIF_F_HW_VLAN_CTAG_TX;
300062306a36Sopenharmony_ci	dev->features = NETIF_F_SG | NETIF_F_TSO |
300162306a36Sopenharmony_ci		      NETIF_F_HIGHDMA | NETIF_F_IP_CSUM |
300262306a36Sopenharmony_ci		      NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX |
300362306a36Sopenharmony_ci		      NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXCSUM;
300462306a36Sopenharmony_ci	dev->vlan_features = NETIF_F_SG | NETIF_F_TSO | NETIF_F_HIGHDMA |
300562306a36Sopenharmony_ci			NETIF_F_IP_CSUM;
300662306a36Sopenharmony_ci	dev->watchdog_timeo = EHEA_WATCH_DOG_TIMEOUT;
300762306a36Sopenharmony_ci
300862306a36Sopenharmony_ci	/* MTU range: 68 - 9022 */
300962306a36Sopenharmony_ci	dev->min_mtu = ETH_MIN_MTU;
301062306a36Sopenharmony_ci	dev->max_mtu = EHEA_MAX_PACKET_SIZE;
301162306a36Sopenharmony_ci
301262306a36Sopenharmony_ci	INIT_WORK(&port->reset_task, ehea_reset_port);
301362306a36Sopenharmony_ci	INIT_DELAYED_WORK(&port->stats_work, ehea_update_stats);
301462306a36Sopenharmony_ci
301562306a36Sopenharmony_ci	init_waitqueue_head(&port->swqe_avail_wq);
301662306a36Sopenharmony_ci	init_waitqueue_head(&port->restart_wq);
301762306a36Sopenharmony_ci
301862306a36Sopenharmony_ci	ret = register_netdev(dev);
301962306a36Sopenharmony_ci	if (ret) {
302062306a36Sopenharmony_ci		pr_err("register_netdev failed. ret=%d\n", ret);
302162306a36Sopenharmony_ci		goto out_unreg_port;
302262306a36Sopenharmony_ci	}
302362306a36Sopenharmony_ci
302462306a36Sopenharmony_ci	ret = ehea_get_jumboframe_status(port, &jumbo);
302562306a36Sopenharmony_ci	if (ret)
302662306a36Sopenharmony_ci		netdev_err(dev, "failed determining jumbo frame status\n");
302762306a36Sopenharmony_ci
302862306a36Sopenharmony_ci	netdev_info(dev, "Jumbo frames are %sabled\n",
302962306a36Sopenharmony_ci		    jumbo == 1 ? "en" : "dis");
303062306a36Sopenharmony_ci
303162306a36Sopenharmony_ci	adapter->active_ports++;
303262306a36Sopenharmony_ci
303362306a36Sopenharmony_ci	return port;
303462306a36Sopenharmony_ci
303562306a36Sopenharmony_ciout_unreg_port:
303662306a36Sopenharmony_ci	ehea_unregister_port(port);
303762306a36Sopenharmony_ci
303862306a36Sopenharmony_ciout_free_mc_list:
303962306a36Sopenharmony_ci	kfree(port->mc_list);
304062306a36Sopenharmony_ci
304162306a36Sopenharmony_ciout_free_ethdev:
304262306a36Sopenharmony_ci	free_netdev(dev);
304362306a36Sopenharmony_ci
304462306a36Sopenharmony_ciout_err:
304562306a36Sopenharmony_ci	pr_err("setting up logical port with id=%d failed, ret=%d\n",
304662306a36Sopenharmony_ci	       logical_port_id, ret);
304762306a36Sopenharmony_ci	return NULL;
304862306a36Sopenharmony_ci}
304962306a36Sopenharmony_ci
305062306a36Sopenharmony_cistatic void ehea_shutdown_single_port(struct ehea_port *port)
305162306a36Sopenharmony_ci{
305262306a36Sopenharmony_ci	struct ehea_adapter *adapter = port->adapter;
305362306a36Sopenharmony_ci
305462306a36Sopenharmony_ci	cancel_work_sync(&port->reset_task);
305562306a36Sopenharmony_ci	cancel_delayed_work_sync(&port->stats_work);
305662306a36Sopenharmony_ci	unregister_netdev(port->netdev);
305762306a36Sopenharmony_ci	ehea_unregister_port(port);
305862306a36Sopenharmony_ci	kfree(port->mc_list);
305962306a36Sopenharmony_ci	free_netdev(port->netdev);
306062306a36Sopenharmony_ci	adapter->active_ports--;
306162306a36Sopenharmony_ci}
306262306a36Sopenharmony_ci
306362306a36Sopenharmony_cistatic int ehea_setup_ports(struct ehea_adapter *adapter)
306462306a36Sopenharmony_ci{
306562306a36Sopenharmony_ci	struct device_node *lhea_dn;
306662306a36Sopenharmony_ci	struct device_node *eth_dn = NULL;
306762306a36Sopenharmony_ci
306862306a36Sopenharmony_ci	const u32 *dn_log_port_id;
306962306a36Sopenharmony_ci	int i = 0;
307062306a36Sopenharmony_ci
307162306a36Sopenharmony_ci	lhea_dn = adapter->ofdev->dev.of_node;
307262306a36Sopenharmony_ci	while ((eth_dn = of_get_next_child(lhea_dn, eth_dn))) {
307362306a36Sopenharmony_ci
307462306a36Sopenharmony_ci		dn_log_port_id = of_get_property(eth_dn, "ibm,hea-port-no",
307562306a36Sopenharmony_ci						 NULL);
307662306a36Sopenharmony_ci		if (!dn_log_port_id) {
307762306a36Sopenharmony_ci			pr_err("bad device node: eth_dn name=%pOF\n", eth_dn);
307862306a36Sopenharmony_ci			continue;
307962306a36Sopenharmony_ci		}
308062306a36Sopenharmony_ci
308162306a36Sopenharmony_ci		if (ehea_add_adapter_mr(adapter)) {
308262306a36Sopenharmony_ci			pr_err("creating MR failed\n");
308362306a36Sopenharmony_ci			of_node_put(eth_dn);
308462306a36Sopenharmony_ci			return -EIO;
308562306a36Sopenharmony_ci		}
308662306a36Sopenharmony_ci
308762306a36Sopenharmony_ci		adapter->port[i] = ehea_setup_single_port(adapter,
308862306a36Sopenharmony_ci							  *dn_log_port_id,
308962306a36Sopenharmony_ci							  eth_dn);
309062306a36Sopenharmony_ci		if (adapter->port[i])
309162306a36Sopenharmony_ci			netdev_info(adapter->port[i]->netdev,
309262306a36Sopenharmony_ci				    "logical port id #%d\n", *dn_log_port_id);
309362306a36Sopenharmony_ci		else
309462306a36Sopenharmony_ci			ehea_remove_adapter_mr(adapter);
309562306a36Sopenharmony_ci
309662306a36Sopenharmony_ci		i++;
309762306a36Sopenharmony_ci	}
309862306a36Sopenharmony_ci	return 0;
309962306a36Sopenharmony_ci}
310062306a36Sopenharmony_ci
310162306a36Sopenharmony_cistatic struct device_node *ehea_get_eth_dn(struct ehea_adapter *adapter,
310262306a36Sopenharmony_ci					   u32 logical_port_id)
310362306a36Sopenharmony_ci{
310462306a36Sopenharmony_ci	struct device_node *lhea_dn;
310562306a36Sopenharmony_ci	struct device_node *eth_dn = NULL;
310662306a36Sopenharmony_ci	const u32 *dn_log_port_id;
310762306a36Sopenharmony_ci
310862306a36Sopenharmony_ci	lhea_dn = adapter->ofdev->dev.of_node;
310962306a36Sopenharmony_ci	while ((eth_dn = of_get_next_child(lhea_dn, eth_dn))) {
311062306a36Sopenharmony_ci
311162306a36Sopenharmony_ci		dn_log_port_id = of_get_property(eth_dn, "ibm,hea-port-no",
311262306a36Sopenharmony_ci						 NULL);
311362306a36Sopenharmony_ci		if (dn_log_port_id)
311462306a36Sopenharmony_ci			if (*dn_log_port_id == logical_port_id)
311562306a36Sopenharmony_ci				return eth_dn;
311662306a36Sopenharmony_ci	}
311762306a36Sopenharmony_ci
311862306a36Sopenharmony_ci	return NULL;
311962306a36Sopenharmony_ci}
312062306a36Sopenharmony_ci
312162306a36Sopenharmony_cistatic ssize_t probe_port_store(struct device *dev,
312262306a36Sopenharmony_ci			       struct device_attribute *attr,
312362306a36Sopenharmony_ci			       const char *buf, size_t count)
312462306a36Sopenharmony_ci{
312562306a36Sopenharmony_ci	struct ehea_adapter *adapter = dev_get_drvdata(dev);
312662306a36Sopenharmony_ci	struct ehea_port *port;
312762306a36Sopenharmony_ci	struct device_node *eth_dn = NULL;
312862306a36Sopenharmony_ci	int i;
312962306a36Sopenharmony_ci
313062306a36Sopenharmony_ci	u32 logical_port_id;
313162306a36Sopenharmony_ci
313262306a36Sopenharmony_ci	sscanf(buf, "%d", &logical_port_id);
313362306a36Sopenharmony_ci
313462306a36Sopenharmony_ci	port = ehea_get_port(adapter, logical_port_id);
313562306a36Sopenharmony_ci
313662306a36Sopenharmony_ci	if (port) {
313762306a36Sopenharmony_ci		netdev_info(port->netdev, "adding port with logical port id=%d failed: port already configured\n",
313862306a36Sopenharmony_ci			    logical_port_id);
313962306a36Sopenharmony_ci		return -EINVAL;
314062306a36Sopenharmony_ci	}
314162306a36Sopenharmony_ci
314262306a36Sopenharmony_ci	eth_dn = ehea_get_eth_dn(adapter, logical_port_id);
314362306a36Sopenharmony_ci
314462306a36Sopenharmony_ci	if (!eth_dn) {
314562306a36Sopenharmony_ci		pr_info("no logical port with id %d found\n", logical_port_id);
314662306a36Sopenharmony_ci		return -EINVAL;
314762306a36Sopenharmony_ci	}
314862306a36Sopenharmony_ci
314962306a36Sopenharmony_ci	if (ehea_add_adapter_mr(adapter)) {
315062306a36Sopenharmony_ci		pr_err("creating MR failed\n");
315162306a36Sopenharmony_ci		of_node_put(eth_dn);
315262306a36Sopenharmony_ci		return -EIO;
315362306a36Sopenharmony_ci	}
315462306a36Sopenharmony_ci
315562306a36Sopenharmony_ci	port = ehea_setup_single_port(adapter, logical_port_id, eth_dn);
315662306a36Sopenharmony_ci
315762306a36Sopenharmony_ci	of_node_put(eth_dn);
315862306a36Sopenharmony_ci
315962306a36Sopenharmony_ci	if (port) {
316062306a36Sopenharmony_ci		for (i = 0; i < EHEA_MAX_PORTS; i++)
316162306a36Sopenharmony_ci			if (!adapter->port[i]) {
316262306a36Sopenharmony_ci				adapter->port[i] = port;
316362306a36Sopenharmony_ci				break;
316462306a36Sopenharmony_ci			}
316562306a36Sopenharmony_ci
316662306a36Sopenharmony_ci		netdev_info(port->netdev, "added: (logical port id=%d)\n",
316762306a36Sopenharmony_ci			    logical_port_id);
316862306a36Sopenharmony_ci	} else {
316962306a36Sopenharmony_ci		ehea_remove_adapter_mr(adapter);
317062306a36Sopenharmony_ci		return -EIO;
317162306a36Sopenharmony_ci	}
317262306a36Sopenharmony_ci
317362306a36Sopenharmony_ci	return (ssize_t) count;
317462306a36Sopenharmony_ci}
317562306a36Sopenharmony_ci
317662306a36Sopenharmony_cistatic ssize_t remove_port_store(struct device *dev,
317762306a36Sopenharmony_ci				 struct device_attribute *attr,
317862306a36Sopenharmony_ci				 const char *buf, size_t count)
317962306a36Sopenharmony_ci{
318062306a36Sopenharmony_ci	struct ehea_adapter *adapter = dev_get_drvdata(dev);
318162306a36Sopenharmony_ci	struct ehea_port *port;
318262306a36Sopenharmony_ci	int i;
318362306a36Sopenharmony_ci	u32 logical_port_id;
318462306a36Sopenharmony_ci
318562306a36Sopenharmony_ci	sscanf(buf, "%d", &logical_port_id);
318662306a36Sopenharmony_ci
318762306a36Sopenharmony_ci	port = ehea_get_port(adapter, logical_port_id);
318862306a36Sopenharmony_ci
318962306a36Sopenharmony_ci	if (port) {
319062306a36Sopenharmony_ci		netdev_info(port->netdev, "removed: (logical port id=%d)\n",
319162306a36Sopenharmony_ci			    logical_port_id);
319262306a36Sopenharmony_ci
319362306a36Sopenharmony_ci		ehea_shutdown_single_port(port);
319462306a36Sopenharmony_ci
319562306a36Sopenharmony_ci		for (i = 0; i < EHEA_MAX_PORTS; i++)
319662306a36Sopenharmony_ci			if (adapter->port[i] == port) {
319762306a36Sopenharmony_ci				adapter->port[i] = NULL;
319862306a36Sopenharmony_ci				break;
319962306a36Sopenharmony_ci			}
320062306a36Sopenharmony_ci	} else {
320162306a36Sopenharmony_ci		pr_err("removing port with logical port id=%d failed. port not configured.\n",
320262306a36Sopenharmony_ci		       logical_port_id);
320362306a36Sopenharmony_ci		return -EINVAL;
320462306a36Sopenharmony_ci	}
320562306a36Sopenharmony_ci
320662306a36Sopenharmony_ci	ehea_remove_adapter_mr(adapter);
320762306a36Sopenharmony_ci
320862306a36Sopenharmony_ci	return (ssize_t) count;
320962306a36Sopenharmony_ci}
321062306a36Sopenharmony_ci
321162306a36Sopenharmony_cistatic DEVICE_ATTR_WO(probe_port);
321262306a36Sopenharmony_cistatic DEVICE_ATTR_WO(remove_port);
321362306a36Sopenharmony_ci
321462306a36Sopenharmony_cistatic int ehea_create_device_sysfs(struct platform_device *dev)
321562306a36Sopenharmony_ci{
321662306a36Sopenharmony_ci	int ret = device_create_file(&dev->dev, &dev_attr_probe_port);
321762306a36Sopenharmony_ci	if (ret)
321862306a36Sopenharmony_ci		goto out;
321962306a36Sopenharmony_ci
322062306a36Sopenharmony_ci	ret = device_create_file(&dev->dev, &dev_attr_remove_port);
322162306a36Sopenharmony_ciout:
322262306a36Sopenharmony_ci	return ret;
322362306a36Sopenharmony_ci}
322462306a36Sopenharmony_ci
322562306a36Sopenharmony_cistatic void ehea_remove_device_sysfs(struct platform_device *dev)
322662306a36Sopenharmony_ci{
322762306a36Sopenharmony_ci	device_remove_file(&dev->dev, &dev_attr_probe_port);
322862306a36Sopenharmony_ci	device_remove_file(&dev->dev, &dev_attr_remove_port);
322962306a36Sopenharmony_ci}
323062306a36Sopenharmony_ci
323162306a36Sopenharmony_cistatic int ehea_reboot_notifier(struct notifier_block *nb,
323262306a36Sopenharmony_ci				unsigned long action, void *unused)
323362306a36Sopenharmony_ci{
323462306a36Sopenharmony_ci	if (action == SYS_RESTART) {
323562306a36Sopenharmony_ci		pr_info("Reboot: freeing all eHEA resources\n");
323662306a36Sopenharmony_ci		ibmebus_unregister_driver(&ehea_driver);
323762306a36Sopenharmony_ci	}
323862306a36Sopenharmony_ci	return NOTIFY_DONE;
323962306a36Sopenharmony_ci}
324062306a36Sopenharmony_ci
324162306a36Sopenharmony_cistatic struct notifier_block ehea_reboot_nb = {
324262306a36Sopenharmony_ci	.notifier_call = ehea_reboot_notifier,
324362306a36Sopenharmony_ci};
324462306a36Sopenharmony_ci
324562306a36Sopenharmony_cistatic int ehea_mem_notifier(struct notifier_block *nb,
324662306a36Sopenharmony_ci			     unsigned long action, void *data)
324762306a36Sopenharmony_ci{
324862306a36Sopenharmony_ci	int ret = NOTIFY_BAD;
324962306a36Sopenharmony_ci	struct memory_notify *arg = data;
325062306a36Sopenharmony_ci
325162306a36Sopenharmony_ci	mutex_lock(&dlpar_mem_lock);
325262306a36Sopenharmony_ci
325362306a36Sopenharmony_ci	switch (action) {
325462306a36Sopenharmony_ci	case MEM_CANCEL_OFFLINE:
325562306a36Sopenharmony_ci		pr_info("memory offlining canceled");
325662306a36Sopenharmony_ci		fallthrough;	/* re-add canceled memory block */
325762306a36Sopenharmony_ci
325862306a36Sopenharmony_ci	case MEM_ONLINE:
325962306a36Sopenharmony_ci		pr_info("memory is going online");
326062306a36Sopenharmony_ci		set_bit(__EHEA_STOP_XFER, &ehea_driver_flags);
326162306a36Sopenharmony_ci		if (ehea_add_sect_bmap(arg->start_pfn, arg->nr_pages))
326262306a36Sopenharmony_ci			goto out_unlock;
326362306a36Sopenharmony_ci		ehea_rereg_mrs();
326462306a36Sopenharmony_ci		break;
326562306a36Sopenharmony_ci
326662306a36Sopenharmony_ci	case MEM_GOING_OFFLINE:
326762306a36Sopenharmony_ci		pr_info("memory is going offline");
326862306a36Sopenharmony_ci		set_bit(__EHEA_STOP_XFER, &ehea_driver_flags);
326962306a36Sopenharmony_ci		if (ehea_rem_sect_bmap(arg->start_pfn, arg->nr_pages))
327062306a36Sopenharmony_ci			goto out_unlock;
327162306a36Sopenharmony_ci		ehea_rereg_mrs();
327262306a36Sopenharmony_ci		break;
327362306a36Sopenharmony_ci
327462306a36Sopenharmony_ci	default:
327562306a36Sopenharmony_ci		break;
327662306a36Sopenharmony_ci	}
327762306a36Sopenharmony_ci
327862306a36Sopenharmony_ci	ehea_update_firmware_handles();
327962306a36Sopenharmony_ci	ret = NOTIFY_OK;
328062306a36Sopenharmony_ci
328162306a36Sopenharmony_ciout_unlock:
328262306a36Sopenharmony_ci	mutex_unlock(&dlpar_mem_lock);
328362306a36Sopenharmony_ci	return ret;
328462306a36Sopenharmony_ci}
328562306a36Sopenharmony_ci
328662306a36Sopenharmony_cistatic struct notifier_block ehea_mem_nb = {
328762306a36Sopenharmony_ci	.notifier_call = ehea_mem_notifier,
328862306a36Sopenharmony_ci};
328962306a36Sopenharmony_ci
329062306a36Sopenharmony_cistatic void ehea_crash_handler(void)
329162306a36Sopenharmony_ci{
329262306a36Sopenharmony_ci	int i;
329362306a36Sopenharmony_ci
329462306a36Sopenharmony_ci	if (ehea_fw_handles.arr)
329562306a36Sopenharmony_ci		for (i = 0; i < ehea_fw_handles.num_entries; i++)
329662306a36Sopenharmony_ci			ehea_h_free_resource(ehea_fw_handles.arr[i].adh,
329762306a36Sopenharmony_ci					     ehea_fw_handles.arr[i].fwh,
329862306a36Sopenharmony_ci					     FORCE_FREE);
329962306a36Sopenharmony_ci
330062306a36Sopenharmony_ci	if (ehea_bcmc_regs.arr)
330162306a36Sopenharmony_ci		for (i = 0; i < ehea_bcmc_regs.num_entries; i++)
330262306a36Sopenharmony_ci			ehea_h_reg_dereg_bcmc(ehea_bcmc_regs.arr[i].adh,
330362306a36Sopenharmony_ci					      ehea_bcmc_regs.arr[i].port_id,
330462306a36Sopenharmony_ci					      ehea_bcmc_regs.arr[i].reg_type,
330562306a36Sopenharmony_ci					      ehea_bcmc_regs.arr[i].macaddr,
330662306a36Sopenharmony_ci					      0, H_DEREG_BCMC);
330762306a36Sopenharmony_ci}
330862306a36Sopenharmony_ci
330962306a36Sopenharmony_cistatic atomic_t ehea_memory_hooks_registered;
331062306a36Sopenharmony_ci
331162306a36Sopenharmony_ci/* Register memory hooks on probe of first adapter */
331262306a36Sopenharmony_cistatic int ehea_register_memory_hooks(void)
331362306a36Sopenharmony_ci{
331462306a36Sopenharmony_ci	int ret = 0;
331562306a36Sopenharmony_ci
331662306a36Sopenharmony_ci	if (atomic_inc_return(&ehea_memory_hooks_registered) > 1)
331762306a36Sopenharmony_ci		return 0;
331862306a36Sopenharmony_ci
331962306a36Sopenharmony_ci	ret = ehea_create_busmap();
332062306a36Sopenharmony_ci	if (ret) {
332162306a36Sopenharmony_ci		pr_info("ehea_create_busmap failed\n");
332262306a36Sopenharmony_ci		goto out;
332362306a36Sopenharmony_ci	}
332462306a36Sopenharmony_ci
332562306a36Sopenharmony_ci	ret = register_reboot_notifier(&ehea_reboot_nb);
332662306a36Sopenharmony_ci	if (ret) {
332762306a36Sopenharmony_ci		pr_info("register_reboot_notifier failed\n");
332862306a36Sopenharmony_ci		goto out;
332962306a36Sopenharmony_ci	}
333062306a36Sopenharmony_ci
333162306a36Sopenharmony_ci	ret = register_memory_notifier(&ehea_mem_nb);
333262306a36Sopenharmony_ci	if (ret) {
333362306a36Sopenharmony_ci		pr_info("register_memory_notifier failed\n");
333462306a36Sopenharmony_ci		goto out2;
333562306a36Sopenharmony_ci	}
333662306a36Sopenharmony_ci
333762306a36Sopenharmony_ci	ret = crash_shutdown_register(ehea_crash_handler);
333862306a36Sopenharmony_ci	if (ret) {
333962306a36Sopenharmony_ci		pr_info("crash_shutdown_register failed\n");
334062306a36Sopenharmony_ci		goto out3;
334162306a36Sopenharmony_ci	}
334262306a36Sopenharmony_ci
334362306a36Sopenharmony_ci	return 0;
334462306a36Sopenharmony_ci
334562306a36Sopenharmony_ciout3:
334662306a36Sopenharmony_ci	unregister_memory_notifier(&ehea_mem_nb);
334762306a36Sopenharmony_ciout2:
334862306a36Sopenharmony_ci	unregister_reboot_notifier(&ehea_reboot_nb);
334962306a36Sopenharmony_ciout:
335062306a36Sopenharmony_ci	atomic_dec(&ehea_memory_hooks_registered);
335162306a36Sopenharmony_ci	return ret;
335262306a36Sopenharmony_ci}
335362306a36Sopenharmony_ci
335462306a36Sopenharmony_cistatic void ehea_unregister_memory_hooks(void)
335562306a36Sopenharmony_ci{
335662306a36Sopenharmony_ci	/* Only remove the hooks if we've registered them */
335762306a36Sopenharmony_ci	if (atomic_read(&ehea_memory_hooks_registered) == 0)
335862306a36Sopenharmony_ci		return;
335962306a36Sopenharmony_ci
336062306a36Sopenharmony_ci	unregister_reboot_notifier(&ehea_reboot_nb);
336162306a36Sopenharmony_ci	if (crash_shutdown_unregister(ehea_crash_handler))
336262306a36Sopenharmony_ci		pr_info("failed unregistering crash handler\n");
336362306a36Sopenharmony_ci	unregister_memory_notifier(&ehea_mem_nb);
336462306a36Sopenharmony_ci}
336562306a36Sopenharmony_ci
336662306a36Sopenharmony_cistatic int ehea_probe_adapter(struct platform_device *dev)
336762306a36Sopenharmony_ci{
336862306a36Sopenharmony_ci	struct ehea_adapter *adapter;
336962306a36Sopenharmony_ci	const u64 *adapter_handle;
337062306a36Sopenharmony_ci	int ret;
337162306a36Sopenharmony_ci	int i;
337262306a36Sopenharmony_ci
337362306a36Sopenharmony_ci	ret = ehea_register_memory_hooks();
337462306a36Sopenharmony_ci	if (ret)
337562306a36Sopenharmony_ci		return ret;
337662306a36Sopenharmony_ci
337762306a36Sopenharmony_ci	if (!dev || !dev->dev.of_node) {
337862306a36Sopenharmony_ci		pr_err("Invalid ibmebus device probed\n");
337962306a36Sopenharmony_ci		return -EINVAL;
338062306a36Sopenharmony_ci	}
338162306a36Sopenharmony_ci
338262306a36Sopenharmony_ci	adapter = devm_kzalloc(&dev->dev, sizeof(*adapter), GFP_KERNEL);
338362306a36Sopenharmony_ci	if (!adapter) {
338462306a36Sopenharmony_ci		ret = -ENOMEM;
338562306a36Sopenharmony_ci		dev_err(&dev->dev, "no mem for ehea_adapter\n");
338662306a36Sopenharmony_ci		goto out;
338762306a36Sopenharmony_ci	}
338862306a36Sopenharmony_ci
338962306a36Sopenharmony_ci	list_add(&adapter->list, &adapter_list);
339062306a36Sopenharmony_ci
339162306a36Sopenharmony_ci	adapter->ofdev = dev;
339262306a36Sopenharmony_ci
339362306a36Sopenharmony_ci	adapter_handle = of_get_property(dev->dev.of_node, "ibm,hea-handle",
339462306a36Sopenharmony_ci					 NULL);
339562306a36Sopenharmony_ci	if (adapter_handle)
339662306a36Sopenharmony_ci		adapter->handle = *adapter_handle;
339762306a36Sopenharmony_ci
339862306a36Sopenharmony_ci	if (!adapter->handle) {
339962306a36Sopenharmony_ci		dev_err(&dev->dev, "failed getting handle for adapter"
340062306a36Sopenharmony_ci			" '%pOF'\n", dev->dev.of_node);
340162306a36Sopenharmony_ci		ret = -ENODEV;
340262306a36Sopenharmony_ci		goto out_free_ad;
340362306a36Sopenharmony_ci	}
340462306a36Sopenharmony_ci
340562306a36Sopenharmony_ci	adapter->pd = EHEA_PD_ID;
340662306a36Sopenharmony_ci
340762306a36Sopenharmony_ci	platform_set_drvdata(dev, adapter);
340862306a36Sopenharmony_ci
340962306a36Sopenharmony_ci
341062306a36Sopenharmony_ci	/* initialize adapter and ports */
341162306a36Sopenharmony_ci	/* get adapter properties */
341262306a36Sopenharmony_ci	ret = ehea_sense_adapter_attr(adapter);
341362306a36Sopenharmony_ci	if (ret) {
341462306a36Sopenharmony_ci		dev_err(&dev->dev, "sense_adapter_attr failed: %d\n", ret);
341562306a36Sopenharmony_ci		goto out_free_ad;
341662306a36Sopenharmony_ci	}
341762306a36Sopenharmony_ci
341862306a36Sopenharmony_ci	adapter->neq = ehea_create_eq(adapter,
341962306a36Sopenharmony_ci				      EHEA_NEQ, EHEA_MAX_ENTRIES_EQ, 1);
342062306a36Sopenharmony_ci	if (!adapter->neq) {
342162306a36Sopenharmony_ci		ret = -EIO;
342262306a36Sopenharmony_ci		dev_err(&dev->dev, "NEQ creation failed\n");
342362306a36Sopenharmony_ci		goto out_free_ad;
342462306a36Sopenharmony_ci	}
342562306a36Sopenharmony_ci
342662306a36Sopenharmony_ci	tasklet_setup(&adapter->neq_tasklet, ehea_neq_tasklet);
342762306a36Sopenharmony_ci
342862306a36Sopenharmony_ci	ret = ehea_create_device_sysfs(dev);
342962306a36Sopenharmony_ci	if (ret)
343062306a36Sopenharmony_ci		goto out_kill_eq;
343162306a36Sopenharmony_ci
343262306a36Sopenharmony_ci	ret = ehea_setup_ports(adapter);
343362306a36Sopenharmony_ci	if (ret) {
343462306a36Sopenharmony_ci		dev_err(&dev->dev, "setup_ports failed\n");
343562306a36Sopenharmony_ci		goto out_rem_dev_sysfs;
343662306a36Sopenharmony_ci	}
343762306a36Sopenharmony_ci
343862306a36Sopenharmony_ci	ret = ibmebus_request_irq(adapter->neq->attr.ist1,
343962306a36Sopenharmony_ci				  ehea_interrupt_neq, 0,
344062306a36Sopenharmony_ci				  "ehea_neq", adapter);
344162306a36Sopenharmony_ci	if (ret) {
344262306a36Sopenharmony_ci		dev_err(&dev->dev, "requesting NEQ IRQ failed\n");
344362306a36Sopenharmony_ci		goto out_shutdown_ports;
344462306a36Sopenharmony_ci	}
344562306a36Sopenharmony_ci
344662306a36Sopenharmony_ci	/* Handle any events that might be pending. */
344762306a36Sopenharmony_ci	tasklet_hi_schedule(&adapter->neq_tasklet);
344862306a36Sopenharmony_ci
344962306a36Sopenharmony_ci	ret = 0;
345062306a36Sopenharmony_ci	goto out;
345162306a36Sopenharmony_ci
345262306a36Sopenharmony_ciout_shutdown_ports:
345362306a36Sopenharmony_ci	for (i = 0; i < EHEA_MAX_PORTS; i++)
345462306a36Sopenharmony_ci		if (adapter->port[i]) {
345562306a36Sopenharmony_ci			ehea_shutdown_single_port(adapter->port[i]);
345662306a36Sopenharmony_ci			adapter->port[i] = NULL;
345762306a36Sopenharmony_ci		}
345862306a36Sopenharmony_ci
345962306a36Sopenharmony_ciout_rem_dev_sysfs:
346062306a36Sopenharmony_ci	ehea_remove_device_sysfs(dev);
346162306a36Sopenharmony_ci
346262306a36Sopenharmony_ciout_kill_eq:
346362306a36Sopenharmony_ci	ehea_destroy_eq(adapter->neq);
346462306a36Sopenharmony_ci
346562306a36Sopenharmony_ciout_free_ad:
346662306a36Sopenharmony_ci	list_del(&adapter->list);
346762306a36Sopenharmony_ci
346862306a36Sopenharmony_ciout:
346962306a36Sopenharmony_ci	ehea_update_firmware_handles();
347062306a36Sopenharmony_ci
347162306a36Sopenharmony_ci	return ret;
347262306a36Sopenharmony_ci}
347362306a36Sopenharmony_ci
347462306a36Sopenharmony_cistatic int ehea_remove(struct platform_device *dev)
347562306a36Sopenharmony_ci{
347662306a36Sopenharmony_ci	struct ehea_adapter *adapter = platform_get_drvdata(dev);
347762306a36Sopenharmony_ci	int i;
347862306a36Sopenharmony_ci
347962306a36Sopenharmony_ci	for (i = 0; i < EHEA_MAX_PORTS; i++)
348062306a36Sopenharmony_ci		if (adapter->port[i]) {
348162306a36Sopenharmony_ci			ehea_shutdown_single_port(adapter->port[i]);
348262306a36Sopenharmony_ci			adapter->port[i] = NULL;
348362306a36Sopenharmony_ci		}
348462306a36Sopenharmony_ci
348562306a36Sopenharmony_ci	ehea_remove_device_sysfs(dev);
348662306a36Sopenharmony_ci
348762306a36Sopenharmony_ci	ibmebus_free_irq(adapter->neq->attr.ist1, adapter);
348862306a36Sopenharmony_ci	tasklet_kill(&adapter->neq_tasklet);
348962306a36Sopenharmony_ci
349062306a36Sopenharmony_ci	ehea_destroy_eq(adapter->neq);
349162306a36Sopenharmony_ci	ehea_remove_adapter_mr(adapter);
349262306a36Sopenharmony_ci	list_del(&adapter->list);
349362306a36Sopenharmony_ci
349462306a36Sopenharmony_ci	ehea_update_firmware_handles();
349562306a36Sopenharmony_ci
349662306a36Sopenharmony_ci	return 0;
349762306a36Sopenharmony_ci}
349862306a36Sopenharmony_ci
349962306a36Sopenharmony_cistatic int check_module_parm(void)
350062306a36Sopenharmony_ci{
350162306a36Sopenharmony_ci	int ret = 0;
350262306a36Sopenharmony_ci
350362306a36Sopenharmony_ci	if ((rq1_entries < EHEA_MIN_ENTRIES_QP) ||
350462306a36Sopenharmony_ci	    (rq1_entries > EHEA_MAX_ENTRIES_RQ1)) {
350562306a36Sopenharmony_ci		pr_info("Bad parameter: rq1_entries\n");
350662306a36Sopenharmony_ci		ret = -EINVAL;
350762306a36Sopenharmony_ci	}
350862306a36Sopenharmony_ci	if ((rq2_entries < EHEA_MIN_ENTRIES_QP) ||
350962306a36Sopenharmony_ci	    (rq2_entries > EHEA_MAX_ENTRIES_RQ2)) {
351062306a36Sopenharmony_ci		pr_info("Bad parameter: rq2_entries\n");
351162306a36Sopenharmony_ci		ret = -EINVAL;
351262306a36Sopenharmony_ci	}
351362306a36Sopenharmony_ci	if ((rq3_entries < EHEA_MIN_ENTRIES_QP) ||
351462306a36Sopenharmony_ci	    (rq3_entries > EHEA_MAX_ENTRIES_RQ3)) {
351562306a36Sopenharmony_ci		pr_info("Bad parameter: rq3_entries\n");
351662306a36Sopenharmony_ci		ret = -EINVAL;
351762306a36Sopenharmony_ci	}
351862306a36Sopenharmony_ci	if ((sq_entries < EHEA_MIN_ENTRIES_QP) ||
351962306a36Sopenharmony_ci	    (sq_entries > EHEA_MAX_ENTRIES_SQ)) {
352062306a36Sopenharmony_ci		pr_info("Bad parameter: sq_entries\n");
352162306a36Sopenharmony_ci		ret = -EINVAL;
352262306a36Sopenharmony_ci	}
352362306a36Sopenharmony_ci
352462306a36Sopenharmony_ci	return ret;
352562306a36Sopenharmony_ci}
352662306a36Sopenharmony_ci
352762306a36Sopenharmony_cistatic ssize_t capabilities_show(struct device_driver *drv, char *buf)
352862306a36Sopenharmony_ci{
352962306a36Sopenharmony_ci	return sprintf(buf, "%d", EHEA_CAPABILITIES);
353062306a36Sopenharmony_ci}
353162306a36Sopenharmony_ci
353262306a36Sopenharmony_cistatic DRIVER_ATTR_RO(capabilities);
353362306a36Sopenharmony_ci
353462306a36Sopenharmony_cistatic int __init ehea_module_init(void)
353562306a36Sopenharmony_ci{
353662306a36Sopenharmony_ci	int ret;
353762306a36Sopenharmony_ci
353862306a36Sopenharmony_ci	pr_info("IBM eHEA ethernet device driver (Release %s)\n", DRV_VERSION);
353962306a36Sopenharmony_ci
354062306a36Sopenharmony_ci	memset(&ehea_fw_handles, 0, sizeof(ehea_fw_handles));
354162306a36Sopenharmony_ci	memset(&ehea_bcmc_regs, 0, sizeof(ehea_bcmc_regs));
354262306a36Sopenharmony_ci
354362306a36Sopenharmony_ci	mutex_init(&ehea_fw_handles.lock);
354462306a36Sopenharmony_ci	spin_lock_init(&ehea_bcmc_regs.lock);
354562306a36Sopenharmony_ci
354662306a36Sopenharmony_ci	ret = check_module_parm();
354762306a36Sopenharmony_ci	if (ret)
354862306a36Sopenharmony_ci		goto out;
354962306a36Sopenharmony_ci
355062306a36Sopenharmony_ci	ret = ibmebus_register_driver(&ehea_driver);
355162306a36Sopenharmony_ci	if (ret) {
355262306a36Sopenharmony_ci		pr_err("failed registering eHEA device driver on ebus\n");
355362306a36Sopenharmony_ci		goto out;
355462306a36Sopenharmony_ci	}
355562306a36Sopenharmony_ci
355662306a36Sopenharmony_ci	ret = driver_create_file(&ehea_driver.driver,
355762306a36Sopenharmony_ci				 &driver_attr_capabilities);
355862306a36Sopenharmony_ci	if (ret) {
355962306a36Sopenharmony_ci		pr_err("failed to register capabilities attribute, ret=%d\n",
356062306a36Sopenharmony_ci		       ret);
356162306a36Sopenharmony_ci		goto out2;
356262306a36Sopenharmony_ci	}
356362306a36Sopenharmony_ci
356462306a36Sopenharmony_ci	return ret;
356562306a36Sopenharmony_ci
356662306a36Sopenharmony_ciout2:
356762306a36Sopenharmony_ci	ibmebus_unregister_driver(&ehea_driver);
356862306a36Sopenharmony_ciout:
356962306a36Sopenharmony_ci	return ret;
357062306a36Sopenharmony_ci}
357162306a36Sopenharmony_ci
357262306a36Sopenharmony_cistatic void __exit ehea_module_exit(void)
357362306a36Sopenharmony_ci{
357462306a36Sopenharmony_ci	driver_remove_file(&ehea_driver.driver, &driver_attr_capabilities);
357562306a36Sopenharmony_ci	ibmebus_unregister_driver(&ehea_driver);
357662306a36Sopenharmony_ci	ehea_unregister_memory_hooks();
357762306a36Sopenharmony_ci	kfree(ehea_fw_handles.arr);
357862306a36Sopenharmony_ci	kfree(ehea_bcmc_regs.arr);
357962306a36Sopenharmony_ci	ehea_destroy_busmap();
358062306a36Sopenharmony_ci}
358162306a36Sopenharmony_ci
358262306a36Sopenharmony_cimodule_init(ehea_module_init);
358362306a36Sopenharmony_cimodule_exit(ehea_module_exit);
3584