18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  linux/drivers/net/ethernet/ibm/ehea/ehea_main.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  eHEA ethernet device driver for IBM eServer System p
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  (C) Copyright IBM Corp. 2006
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci *  Authors:
108c2ecf20Sopenharmony_ci *	 Christoph Raisch <raisch@de.ibm.com>
118c2ecf20Sopenharmony_ci *	 Jan-Bernd Themann <themann@de.ibm.com>
128c2ecf20Sopenharmony_ci *	 Thomas Klein <tklein@de.ibm.com>
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/device.h>
188c2ecf20Sopenharmony_ci#include <linux/in.h>
198c2ecf20Sopenharmony_ci#include <linux/ip.h>
208c2ecf20Sopenharmony_ci#include <linux/tcp.h>
218c2ecf20Sopenharmony_ci#include <linux/udp.h>
228c2ecf20Sopenharmony_ci#include <linux/if.h>
238c2ecf20Sopenharmony_ci#include <linux/list.h>
248c2ecf20Sopenharmony_ci#include <linux/slab.h>
258c2ecf20Sopenharmony_ci#include <linux/if_ether.h>
268c2ecf20Sopenharmony_ci#include <linux/notifier.h>
278c2ecf20Sopenharmony_ci#include <linux/reboot.h>
288c2ecf20Sopenharmony_ci#include <linux/memory.h>
298c2ecf20Sopenharmony_ci#include <asm/kexec.h>
308c2ecf20Sopenharmony_ci#include <linux/mutex.h>
318c2ecf20Sopenharmony_ci#include <linux/prefetch.h>
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include <net/ip.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#include "ehea.h"
368c2ecf20Sopenharmony_ci#include "ehea_qmr.h"
378c2ecf20Sopenharmony_ci#include "ehea_phyp.h"
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
418c2ecf20Sopenharmony_ciMODULE_AUTHOR("Christoph Raisch <raisch@de.ibm.com>");
428c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IBM eServer HEA Driver");
438c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic int msg_level = -1;
478c2ecf20Sopenharmony_cistatic int rq1_entries = EHEA_DEF_ENTRIES_RQ1;
488c2ecf20Sopenharmony_cistatic int rq2_entries = EHEA_DEF_ENTRIES_RQ2;
498c2ecf20Sopenharmony_cistatic int rq3_entries = EHEA_DEF_ENTRIES_RQ3;
508c2ecf20Sopenharmony_cistatic int sq_entries = EHEA_DEF_ENTRIES_SQ;
518c2ecf20Sopenharmony_cistatic int use_mcs = 1;
528c2ecf20Sopenharmony_cistatic int prop_carrier_state;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cimodule_param(msg_level, int, 0);
558c2ecf20Sopenharmony_cimodule_param(rq1_entries, int, 0);
568c2ecf20Sopenharmony_cimodule_param(rq2_entries, int, 0);
578c2ecf20Sopenharmony_cimodule_param(rq3_entries, int, 0);
588c2ecf20Sopenharmony_cimodule_param(sq_entries, int, 0);
598c2ecf20Sopenharmony_cimodule_param(prop_carrier_state, int, 0);
608c2ecf20Sopenharmony_cimodule_param(use_mcs, int, 0);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ciMODULE_PARM_DESC(msg_level, "msg_level");
638c2ecf20Sopenharmony_ciMODULE_PARM_DESC(prop_carrier_state, "Propagate carrier state of physical "
648c2ecf20Sopenharmony_ci		 "port to stack. 1:yes, 0:no.  Default = 0 ");
658c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rq3_entries, "Number of entries for Receive Queue 3 "
668c2ecf20Sopenharmony_ci		 "[2^x - 1], x = [7..14]. Default = "
678c2ecf20Sopenharmony_ci		 __MODULE_STRING(EHEA_DEF_ENTRIES_RQ3) ")");
688c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rq2_entries, "Number of entries for Receive Queue 2 "
698c2ecf20Sopenharmony_ci		 "[2^x - 1], x = [7..14]. Default = "
708c2ecf20Sopenharmony_ci		 __MODULE_STRING(EHEA_DEF_ENTRIES_RQ2) ")");
718c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rq1_entries, "Number of entries for Receive Queue 1 "
728c2ecf20Sopenharmony_ci		 "[2^x - 1], x = [7..14]. Default = "
738c2ecf20Sopenharmony_ci		 __MODULE_STRING(EHEA_DEF_ENTRIES_RQ1) ")");
748c2ecf20Sopenharmony_ciMODULE_PARM_DESC(sq_entries, " Number of entries for the Send Queue  "
758c2ecf20Sopenharmony_ci		 "[2^x - 1], x = [7..14]. Default = "
768c2ecf20Sopenharmony_ci		 __MODULE_STRING(EHEA_DEF_ENTRIES_SQ) ")");
778c2ecf20Sopenharmony_ciMODULE_PARM_DESC(use_mcs, " Multiple receive queues, 1: enable, 0: disable, "
788c2ecf20Sopenharmony_ci		 "Default = 1");
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic int port_name_cnt;
818c2ecf20Sopenharmony_cistatic LIST_HEAD(adapter_list);
828c2ecf20Sopenharmony_cistatic unsigned long ehea_driver_flags;
838c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(dlpar_mem_lock);
848c2ecf20Sopenharmony_cistatic struct ehea_fw_handle_array ehea_fw_handles;
858c2ecf20Sopenharmony_cistatic struct ehea_bcmc_reg_array ehea_bcmc_regs;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic int ehea_probe_adapter(struct platform_device *dev);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic int ehea_remove(struct platform_device *dev);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic const struct of_device_id ehea_module_device_table[] = {
938c2ecf20Sopenharmony_ci	{
948c2ecf20Sopenharmony_ci		.name = "lhea",
958c2ecf20Sopenharmony_ci		.compatible = "IBM,lhea",
968c2ecf20Sopenharmony_ci	},
978c2ecf20Sopenharmony_ci	{
988c2ecf20Sopenharmony_ci		.type = "network",
998c2ecf20Sopenharmony_ci		.compatible = "IBM,lhea-ethernet",
1008c2ecf20Sopenharmony_ci	},
1018c2ecf20Sopenharmony_ci	{},
1028c2ecf20Sopenharmony_ci};
1038c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ehea_module_device_table);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic const struct of_device_id ehea_device_table[] = {
1068c2ecf20Sopenharmony_ci	{
1078c2ecf20Sopenharmony_ci		.name = "lhea",
1088c2ecf20Sopenharmony_ci		.compatible = "IBM,lhea",
1098c2ecf20Sopenharmony_ci	},
1108c2ecf20Sopenharmony_ci	{},
1118c2ecf20Sopenharmony_ci};
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic struct platform_driver ehea_driver = {
1148c2ecf20Sopenharmony_ci	.driver = {
1158c2ecf20Sopenharmony_ci		.name = "ehea",
1168c2ecf20Sopenharmony_ci		.owner = THIS_MODULE,
1178c2ecf20Sopenharmony_ci		.of_match_table = ehea_device_table,
1188c2ecf20Sopenharmony_ci	},
1198c2ecf20Sopenharmony_ci	.probe = ehea_probe_adapter,
1208c2ecf20Sopenharmony_ci	.remove = ehea_remove,
1218c2ecf20Sopenharmony_ci};
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_civoid ehea_dump(void *adr, int len, char *msg)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	int x;
1268c2ecf20Sopenharmony_ci	unsigned char *deb = adr;
1278c2ecf20Sopenharmony_ci	for (x = 0; x < len; x += 16) {
1288c2ecf20Sopenharmony_ci		pr_info("%s adr=%p ofs=%04x %016llx %016llx\n",
1298c2ecf20Sopenharmony_ci			msg, deb, x, *((u64 *)&deb[0]), *((u64 *)&deb[8]));
1308c2ecf20Sopenharmony_ci		deb += 16;
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic void ehea_schedule_port_reset(struct ehea_port *port)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	if (!test_bit(__EHEA_DISABLE_PORT_RESET, &port->flags))
1378c2ecf20Sopenharmony_ci		schedule_work(&port->reset_task);
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic void ehea_update_firmware_handles(void)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	struct ehea_fw_handle_entry *arr = NULL;
1438c2ecf20Sopenharmony_ci	struct ehea_adapter *adapter;
1448c2ecf20Sopenharmony_ci	int num_adapters = 0;
1458c2ecf20Sopenharmony_ci	int num_ports = 0;
1468c2ecf20Sopenharmony_ci	int num_portres = 0;
1478c2ecf20Sopenharmony_ci	int i = 0;
1488c2ecf20Sopenharmony_ci	int num_fw_handles, k, l;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	/* Determine number of handles */
1518c2ecf20Sopenharmony_ci	mutex_lock(&ehea_fw_handles.lock);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	list_for_each_entry(adapter, &adapter_list, list) {
1548c2ecf20Sopenharmony_ci		num_adapters++;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci		for (k = 0; k < EHEA_MAX_PORTS; k++) {
1578c2ecf20Sopenharmony_ci			struct ehea_port *port = adapter->port[k];
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci			if (!port || (port->state != EHEA_PORT_UP))
1608c2ecf20Sopenharmony_ci				continue;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci			num_ports++;
1638c2ecf20Sopenharmony_ci			num_portres += port->num_def_qps;
1648c2ecf20Sopenharmony_ci		}
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	num_fw_handles = num_adapters * EHEA_NUM_ADAPTER_FW_HANDLES +
1688c2ecf20Sopenharmony_ci			 num_ports * EHEA_NUM_PORT_FW_HANDLES +
1698c2ecf20Sopenharmony_ci			 num_portres * EHEA_NUM_PORTRES_FW_HANDLES;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	if (num_fw_handles) {
1728c2ecf20Sopenharmony_ci		arr = kcalloc(num_fw_handles, sizeof(*arr), GFP_KERNEL);
1738c2ecf20Sopenharmony_ci		if (!arr)
1748c2ecf20Sopenharmony_ci			goto out;  /* Keep the existing array */
1758c2ecf20Sopenharmony_ci	} else
1768c2ecf20Sopenharmony_ci		goto out_update;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	list_for_each_entry(adapter, &adapter_list, list) {
1798c2ecf20Sopenharmony_ci		if (num_adapters == 0)
1808c2ecf20Sopenharmony_ci			break;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci		for (k = 0; k < EHEA_MAX_PORTS; k++) {
1838c2ecf20Sopenharmony_ci			struct ehea_port *port = adapter->port[k];
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci			if (!port || (port->state != EHEA_PORT_UP) ||
1868c2ecf20Sopenharmony_ci			    (num_ports == 0))
1878c2ecf20Sopenharmony_ci				continue;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci			for (l = 0; l < port->num_def_qps; l++) {
1908c2ecf20Sopenharmony_ci				struct ehea_port_res *pr = &port->port_res[l];
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci				arr[i].adh = adapter->handle;
1938c2ecf20Sopenharmony_ci				arr[i++].fwh = pr->qp->fw_handle;
1948c2ecf20Sopenharmony_ci				arr[i].adh = adapter->handle;
1958c2ecf20Sopenharmony_ci				arr[i++].fwh = pr->send_cq->fw_handle;
1968c2ecf20Sopenharmony_ci				arr[i].adh = adapter->handle;
1978c2ecf20Sopenharmony_ci				arr[i++].fwh = pr->recv_cq->fw_handle;
1988c2ecf20Sopenharmony_ci				arr[i].adh = adapter->handle;
1998c2ecf20Sopenharmony_ci				arr[i++].fwh = pr->eq->fw_handle;
2008c2ecf20Sopenharmony_ci				arr[i].adh = adapter->handle;
2018c2ecf20Sopenharmony_ci				arr[i++].fwh = pr->send_mr.handle;
2028c2ecf20Sopenharmony_ci				arr[i].adh = adapter->handle;
2038c2ecf20Sopenharmony_ci				arr[i++].fwh = pr->recv_mr.handle;
2048c2ecf20Sopenharmony_ci			}
2058c2ecf20Sopenharmony_ci			arr[i].adh = adapter->handle;
2068c2ecf20Sopenharmony_ci			arr[i++].fwh = port->qp_eq->fw_handle;
2078c2ecf20Sopenharmony_ci			num_ports--;
2088c2ecf20Sopenharmony_ci		}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci		arr[i].adh = adapter->handle;
2118c2ecf20Sopenharmony_ci		arr[i++].fwh = adapter->neq->fw_handle;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci		if (adapter->mr.handle) {
2148c2ecf20Sopenharmony_ci			arr[i].adh = adapter->handle;
2158c2ecf20Sopenharmony_ci			arr[i++].fwh = adapter->mr.handle;
2168c2ecf20Sopenharmony_ci		}
2178c2ecf20Sopenharmony_ci		num_adapters--;
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ciout_update:
2218c2ecf20Sopenharmony_ci	kfree(ehea_fw_handles.arr);
2228c2ecf20Sopenharmony_ci	ehea_fw_handles.arr = arr;
2238c2ecf20Sopenharmony_ci	ehea_fw_handles.num_entries = i;
2248c2ecf20Sopenharmony_ciout:
2258c2ecf20Sopenharmony_ci	mutex_unlock(&ehea_fw_handles.lock);
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic void ehea_update_bcmc_registrations(void)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	unsigned long flags;
2318c2ecf20Sopenharmony_ci	struct ehea_bcmc_reg_entry *arr = NULL;
2328c2ecf20Sopenharmony_ci	struct ehea_adapter *adapter;
2338c2ecf20Sopenharmony_ci	struct ehea_mc_list *mc_entry;
2348c2ecf20Sopenharmony_ci	int num_registrations = 0;
2358c2ecf20Sopenharmony_ci	int i = 0;
2368c2ecf20Sopenharmony_ci	int k;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ehea_bcmc_regs.lock, flags);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	/* Determine number of registrations */
2418c2ecf20Sopenharmony_ci	list_for_each_entry(adapter, &adapter_list, list)
2428c2ecf20Sopenharmony_ci		for (k = 0; k < EHEA_MAX_PORTS; k++) {
2438c2ecf20Sopenharmony_ci			struct ehea_port *port = adapter->port[k];
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci			if (!port || (port->state != EHEA_PORT_UP))
2468c2ecf20Sopenharmony_ci				continue;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci			num_registrations += 2;	/* Broadcast registrations */
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci			list_for_each_entry(mc_entry, &port->mc_list->list,list)
2518c2ecf20Sopenharmony_ci				num_registrations += 2;
2528c2ecf20Sopenharmony_ci		}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	if (num_registrations) {
2558c2ecf20Sopenharmony_ci		arr = kcalloc(num_registrations, sizeof(*arr), GFP_ATOMIC);
2568c2ecf20Sopenharmony_ci		if (!arr)
2578c2ecf20Sopenharmony_ci			goto out;  /* Keep the existing array */
2588c2ecf20Sopenharmony_ci	} else
2598c2ecf20Sopenharmony_ci		goto out_update;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	list_for_each_entry(adapter, &adapter_list, list) {
2628c2ecf20Sopenharmony_ci		for (k = 0; k < EHEA_MAX_PORTS; k++) {
2638c2ecf20Sopenharmony_ci			struct ehea_port *port = adapter->port[k];
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci			if (!port || (port->state != EHEA_PORT_UP))
2668c2ecf20Sopenharmony_ci				continue;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci			if (num_registrations == 0)
2698c2ecf20Sopenharmony_ci				goto out_update;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci			arr[i].adh = adapter->handle;
2728c2ecf20Sopenharmony_ci			arr[i].port_id = port->logical_port_id;
2738c2ecf20Sopenharmony_ci			arr[i].reg_type = EHEA_BCMC_BROADCAST |
2748c2ecf20Sopenharmony_ci					  EHEA_BCMC_UNTAGGED;
2758c2ecf20Sopenharmony_ci			arr[i++].macaddr = port->mac_addr;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci			arr[i].adh = adapter->handle;
2788c2ecf20Sopenharmony_ci			arr[i].port_id = port->logical_port_id;
2798c2ecf20Sopenharmony_ci			arr[i].reg_type = EHEA_BCMC_BROADCAST |
2808c2ecf20Sopenharmony_ci					  EHEA_BCMC_VLANID_ALL;
2818c2ecf20Sopenharmony_ci			arr[i++].macaddr = port->mac_addr;
2828c2ecf20Sopenharmony_ci			num_registrations -= 2;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci			list_for_each_entry(mc_entry,
2858c2ecf20Sopenharmony_ci					    &port->mc_list->list, list) {
2868c2ecf20Sopenharmony_ci				if (num_registrations == 0)
2878c2ecf20Sopenharmony_ci					goto out_update;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci				arr[i].adh = adapter->handle;
2908c2ecf20Sopenharmony_ci				arr[i].port_id = port->logical_port_id;
2918c2ecf20Sopenharmony_ci				arr[i].reg_type = EHEA_BCMC_MULTICAST |
2928c2ecf20Sopenharmony_ci						  EHEA_BCMC_UNTAGGED;
2938c2ecf20Sopenharmony_ci				if (mc_entry->macaddr == 0)
2948c2ecf20Sopenharmony_ci					arr[i].reg_type |= EHEA_BCMC_SCOPE_ALL;
2958c2ecf20Sopenharmony_ci				arr[i++].macaddr = mc_entry->macaddr;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci				arr[i].adh = adapter->handle;
2988c2ecf20Sopenharmony_ci				arr[i].port_id = port->logical_port_id;
2998c2ecf20Sopenharmony_ci				arr[i].reg_type = EHEA_BCMC_MULTICAST |
3008c2ecf20Sopenharmony_ci						  EHEA_BCMC_VLANID_ALL;
3018c2ecf20Sopenharmony_ci				if (mc_entry->macaddr == 0)
3028c2ecf20Sopenharmony_ci					arr[i].reg_type |= EHEA_BCMC_SCOPE_ALL;
3038c2ecf20Sopenharmony_ci				arr[i++].macaddr = mc_entry->macaddr;
3048c2ecf20Sopenharmony_ci				num_registrations -= 2;
3058c2ecf20Sopenharmony_ci			}
3068c2ecf20Sopenharmony_ci		}
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ciout_update:
3108c2ecf20Sopenharmony_ci	kfree(ehea_bcmc_regs.arr);
3118c2ecf20Sopenharmony_ci	ehea_bcmc_regs.arr = arr;
3128c2ecf20Sopenharmony_ci	ehea_bcmc_regs.num_entries = i;
3138c2ecf20Sopenharmony_ciout:
3148c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ehea_bcmc_regs.lock, flags);
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic void ehea_get_stats64(struct net_device *dev,
3188c2ecf20Sopenharmony_ci			     struct rtnl_link_stats64 *stats)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
3218c2ecf20Sopenharmony_ci	u64 rx_packets = 0, tx_packets = 0, rx_bytes = 0, tx_bytes = 0;
3228c2ecf20Sopenharmony_ci	int i;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	for (i = 0; i < port->num_def_qps; i++) {
3258c2ecf20Sopenharmony_ci		rx_packets += port->port_res[i].rx_packets;
3268c2ecf20Sopenharmony_ci		rx_bytes   += port->port_res[i].rx_bytes;
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	for (i = 0; i < port->num_def_qps; i++) {
3308c2ecf20Sopenharmony_ci		tx_packets += port->port_res[i].tx_packets;
3318c2ecf20Sopenharmony_ci		tx_bytes   += port->port_res[i].tx_bytes;
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	stats->tx_packets = tx_packets;
3358c2ecf20Sopenharmony_ci	stats->rx_bytes = rx_bytes;
3368c2ecf20Sopenharmony_ci	stats->tx_bytes = tx_bytes;
3378c2ecf20Sopenharmony_ci	stats->rx_packets = rx_packets;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	stats->multicast = port->stats.multicast;
3408c2ecf20Sopenharmony_ci	stats->rx_errors = port->stats.rx_errors;
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic void ehea_update_stats(struct work_struct *work)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	struct ehea_port *port =
3468c2ecf20Sopenharmony_ci		container_of(work, struct ehea_port, stats_work.work);
3478c2ecf20Sopenharmony_ci	struct net_device *dev = port->netdev;
3488c2ecf20Sopenharmony_ci	struct rtnl_link_stats64 *stats = &port->stats;
3498c2ecf20Sopenharmony_ci	struct hcp_ehea_port_cb2 *cb2;
3508c2ecf20Sopenharmony_ci	u64 hret;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	cb2 = (void *)get_zeroed_page(GFP_KERNEL);
3538c2ecf20Sopenharmony_ci	if (!cb2) {
3548c2ecf20Sopenharmony_ci		netdev_err(dev, "No mem for cb2. Some interface statistics were not updated\n");
3558c2ecf20Sopenharmony_ci		goto resched;
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	hret = ehea_h_query_ehea_port(port->adapter->handle,
3598c2ecf20Sopenharmony_ci				      port->logical_port_id,
3608c2ecf20Sopenharmony_ci				      H_PORT_CB2, H_PORT_CB2_ALL, cb2);
3618c2ecf20Sopenharmony_ci	if (hret != H_SUCCESS) {
3628c2ecf20Sopenharmony_ci		netdev_err(dev, "query_ehea_port failed\n");
3638c2ecf20Sopenharmony_ci		goto out_herr;
3648c2ecf20Sopenharmony_ci	}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	if (netif_msg_hw(port))
3678c2ecf20Sopenharmony_ci		ehea_dump(cb2, sizeof(*cb2), "net_device_stats");
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	stats->multicast = cb2->rxmcp;
3708c2ecf20Sopenharmony_ci	stats->rx_errors = cb2->rxuerr;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ciout_herr:
3738c2ecf20Sopenharmony_ci	free_page((unsigned long)cb2);
3748c2ecf20Sopenharmony_ciresched:
3758c2ecf20Sopenharmony_ci	schedule_delayed_work(&port->stats_work,
3768c2ecf20Sopenharmony_ci			      round_jiffies_relative(msecs_to_jiffies(1000)));
3778c2ecf20Sopenharmony_ci}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic void ehea_refill_rq1(struct ehea_port_res *pr, int index, int nr_of_wqes)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	struct sk_buff **skb_arr_rq1 = pr->rq1_skba.arr;
3828c2ecf20Sopenharmony_ci	struct net_device *dev = pr->port->netdev;
3838c2ecf20Sopenharmony_ci	int max_index_mask = pr->rq1_skba.len - 1;
3848c2ecf20Sopenharmony_ci	int fill_wqes = pr->rq1_skba.os_skbs + nr_of_wqes;
3858c2ecf20Sopenharmony_ci	int adder = 0;
3868c2ecf20Sopenharmony_ci	int i;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	pr->rq1_skba.os_skbs = 0;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	if (unlikely(test_bit(__EHEA_STOP_XFER, &ehea_driver_flags))) {
3918c2ecf20Sopenharmony_ci		if (nr_of_wqes > 0)
3928c2ecf20Sopenharmony_ci			pr->rq1_skba.index = index;
3938c2ecf20Sopenharmony_ci		pr->rq1_skba.os_skbs = fill_wqes;
3948c2ecf20Sopenharmony_ci		return;
3958c2ecf20Sopenharmony_ci	}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	for (i = 0; i < fill_wqes; i++) {
3988c2ecf20Sopenharmony_ci		if (!skb_arr_rq1[index]) {
3998c2ecf20Sopenharmony_ci			skb_arr_rq1[index] = netdev_alloc_skb(dev,
4008c2ecf20Sopenharmony_ci							      EHEA_L_PKT_SIZE);
4018c2ecf20Sopenharmony_ci			if (!skb_arr_rq1[index]) {
4028c2ecf20Sopenharmony_ci				pr->rq1_skba.os_skbs = fill_wqes - i;
4038c2ecf20Sopenharmony_ci				break;
4048c2ecf20Sopenharmony_ci			}
4058c2ecf20Sopenharmony_ci		}
4068c2ecf20Sopenharmony_ci		index--;
4078c2ecf20Sopenharmony_ci		index &= max_index_mask;
4088c2ecf20Sopenharmony_ci		adder++;
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	if (adder == 0)
4128c2ecf20Sopenharmony_ci		return;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	/* Ring doorbell */
4158c2ecf20Sopenharmony_ci	ehea_update_rq1a(pr->qp, adder);
4168c2ecf20Sopenharmony_ci}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_cistatic void ehea_init_fill_rq1(struct ehea_port_res *pr, int nr_rq1a)
4198c2ecf20Sopenharmony_ci{
4208c2ecf20Sopenharmony_ci	struct sk_buff **skb_arr_rq1 = pr->rq1_skba.arr;
4218c2ecf20Sopenharmony_ci	struct net_device *dev = pr->port->netdev;
4228c2ecf20Sopenharmony_ci	int i;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	if (nr_rq1a > pr->rq1_skba.len) {
4258c2ecf20Sopenharmony_ci		netdev_err(dev, "NR_RQ1A bigger than skb array len\n");
4268c2ecf20Sopenharmony_ci		return;
4278c2ecf20Sopenharmony_ci	}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	for (i = 0; i < nr_rq1a; i++) {
4308c2ecf20Sopenharmony_ci		skb_arr_rq1[i] = netdev_alloc_skb(dev, EHEA_L_PKT_SIZE);
4318c2ecf20Sopenharmony_ci		if (!skb_arr_rq1[i])
4328c2ecf20Sopenharmony_ci			break;
4338c2ecf20Sopenharmony_ci	}
4348c2ecf20Sopenharmony_ci	/* Ring doorbell */
4358c2ecf20Sopenharmony_ci	ehea_update_rq1a(pr->qp, i - 1);
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_cistatic int ehea_refill_rq_def(struct ehea_port_res *pr,
4398c2ecf20Sopenharmony_ci			      struct ehea_q_skb_arr *q_skba, int rq_nr,
4408c2ecf20Sopenharmony_ci			      int num_wqes, int wqe_type, int packet_size)
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	struct net_device *dev = pr->port->netdev;
4438c2ecf20Sopenharmony_ci	struct ehea_qp *qp = pr->qp;
4448c2ecf20Sopenharmony_ci	struct sk_buff **skb_arr = q_skba->arr;
4458c2ecf20Sopenharmony_ci	struct ehea_rwqe *rwqe;
4468c2ecf20Sopenharmony_ci	int i, index, max_index_mask, fill_wqes;
4478c2ecf20Sopenharmony_ci	int adder = 0;
4488c2ecf20Sopenharmony_ci	int ret = 0;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	fill_wqes = q_skba->os_skbs + num_wqes;
4518c2ecf20Sopenharmony_ci	q_skba->os_skbs = 0;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	if (unlikely(test_bit(__EHEA_STOP_XFER, &ehea_driver_flags))) {
4548c2ecf20Sopenharmony_ci		q_skba->os_skbs = fill_wqes;
4558c2ecf20Sopenharmony_ci		return ret;
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	index = q_skba->index;
4598c2ecf20Sopenharmony_ci	max_index_mask = q_skba->len - 1;
4608c2ecf20Sopenharmony_ci	for (i = 0; i < fill_wqes; i++) {
4618c2ecf20Sopenharmony_ci		u64 tmp_addr;
4628c2ecf20Sopenharmony_ci		struct sk_buff *skb;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci		skb = netdev_alloc_skb_ip_align(dev, packet_size);
4658c2ecf20Sopenharmony_ci		if (!skb) {
4668c2ecf20Sopenharmony_ci			q_skba->os_skbs = fill_wqes - i;
4678c2ecf20Sopenharmony_ci			if (q_skba->os_skbs == q_skba->len - 2) {
4688c2ecf20Sopenharmony_ci				netdev_info(pr->port->netdev,
4698c2ecf20Sopenharmony_ci					    "rq%i ran dry - no mem for skb\n",
4708c2ecf20Sopenharmony_ci					    rq_nr);
4718c2ecf20Sopenharmony_ci				ret = -ENOMEM;
4728c2ecf20Sopenharmony_ci			}
4738c2ecf20Sopenharmony_ci			break;
4748c2ecf20Sopenharmony_ci		}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci		skb_arr[index] = skb;
4778c2ecf20Sopenharmony_ci		tmp_addr = ehea_map_vaddr(skb->data);
4788c2ecf20Sopenharmony_ci		if (tmp_addr == -1) {
4798c2ecf20Sopenharmony_ci			dev_consume_skb_any(skb);
4808c2ecf20Sopenharmony_ci			q_skba->os_skbs = fill_wqes - i;
4818c2ecf20Sopenharmony_ci			ret = 0;
4828c2ecf20Sopenharmony_ci			break;
4838c2ecf20Sopenharmony_ci		}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci		rwqe = ehea_get_next_rwqe(qp, rq_nr);
4868c2ecf20Sopenharmony_ci		rwqe->wr_id = EHEA_BMASK_SET(EHEA_WR_ID_TYPE, wqe_type)
4878c2ecf20Sopenharmony_ci			    | EHEA_BMASK_SET(EHEA_WR_ID_INDEX, index);
4888c2ecf20Sopenharmony_ci		rwqe->sg_list[0].l_key = pr->recv_mr.lkey;
4898c2ecf20Sopenharmony_ci		rwqe->sg_list[0].vaddr = tmp_addr;
4908c2ecf20Sopenharmony_ci		rwqe->sg_list[0].len = packet_size;
4918c2ecf20Sopenharmony_ci		rwqe->data_segments = 1;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci		index++;
4948c2ecf20Sopenharmony_ci		index &= max_index_mask;
4958c2ecf20Sopenharmony_ci		adder++;
4968c2ecf20Sopenharmony_ci	}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	q_skba->index = index;
4998c2ecf20Sopenharmony_ci	if (adder == 0)
5008c2ecf20Sopenharmony_ci		goto out;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	/* Ring doorbell */
5038c2ecf20Sopenharmony_ci	iosync();
5048c2ecf20Sopenharmony_ci	if (rq_nr == 2)
5058c2ecf20Sopenharmony_ci		ehea_update_rq2a(pr->qp, adder);
5068c2ecf20Sopenharmony_ci	else
5078c2ecf20Sopenharmony_ci		ehea_update_rq3a(pr->qp, adder);
5088c2ecf20Sopenharmony_ciout:
5098c2ecf20Sopenharmony_ci	return ret;
5108c2ecf20Sopenharmony_ci}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_cistatic int ehea_refill_rq2(struct ehea_port_res *pr, int nr_of_wqes)
5148c2ecf20Sopenharmony_ci{
5158c2ecf20Sopenharmony_ci	return ehea_refill_rq_def(pr, &pr->rq2_skba, 2,
5168c2ecf20Sopenharmony_ci				  nr_of_wqes, EHEA_RWQE2_TYPE,
5178c2ecf20Sopenharmony_ci				  EHEA_RQ2_PKT_SIZE);
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_cistatic int ehea_refill_rq3(struct ehea_port_res *pr, int nr_of_wqes)
5228c2ecf20Sopenharmony_ci{
5238c2ecf20Sopenharmony_ci	return ehea_refill_rq_def(pr, &pr->rq3_skba, 3,
5248c2ecf20Sopenharmony_ci				  nr_of_wqes, EHEA_RWQE3_TYPE,
5258c2ecf20Sopenharmony_ci				  EHEA_MAX_PACKET_SIZE);
5268c2ecf20Sopenharmony_ci}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_cistatic inline int ehea_check_cqe(struct ehea_cqe *cqe, int *rq_num)
5298c2ecf20Sopenharmony_ci{
5308c2ecf20Sopenharmony_ci	*rq_num = (cqe->type & EHEA_CQE_TYPE_RQ) >> 5;
5318c2ecf20Sopenharmony_ci	if ((cqe->status & EHEA_CQE_STAT_ERR_MASK) == 0)
5328c2ecf20Sopenharmony_ci		return 0;
5338c2ecf20Sopenharmony_ci	if (((cqe->status & EHEA_CQE_STAT_ERR_TCP) != 0) &&
5348c2ecf20Sopenharmony_ci	    (cqe->header_length == 0))
5358c2ecf20Sopenharmony_ci		return 0;
5368c2ecf20Sopenharmony_ci	return -EINVAL;
5378c2ecf20Sopenharmony_ci}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_cistatic inline void ehea_fill_skb(struct net_device *dev,
5408c2ecf20Sopenharmony_ci				 struct sk_buff *skb, struct ehea_cqe *cqe,
5418c2ecf20Sopenharmony_ci				 struct ehea_port_res *pr)
5428c2ecf20Sopenharmony_ci{
5438c2ecf20Sopenharmony_ci	int length = cqe->num_bytes_transfered - 4;	/*remove CRC */
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	skb_put(skb, length);
5468c2ecf20Sopenharmony_ci	skb->protocol = eth_type_trans(skb, dev);
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	/* The packet was not an IPV4 packet so a complemented checksum was
5498c2ecf20Sopenharmony_ci	   calculated. The value is found in the Internet Checksum field. */
5508c2ecf20Sopenharmony_ci	if (cqe->status & EHEA_CQE_BLIND_CKSUM) {
5518c2ecf20Sopenharmony_ci		skb->ip_summed = CHECKSUM_COMPLETE;
5528c2ecf20Sopenharmony_ci		skb->csum = csum_unfold(~cqe->inet_checksum_value);
5538c2ecf20Sopenharmony_ci	} else
5548c2ecf20Sopenharmony_ci		skb->ip_summed = CHECKSUM_UNNECESSARY;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	skb_record_rx_queue(skb, pr - &pr->port->port_res[0]);
5578c2ecf20Sopenharmony_ci}
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_cistatic inline struct sk_buff *get_skb_by_index(struct sk_buff **skb_array,
5608c2ecf20Sopenharmony_ci					       int arr_len,
5618c2ecf20Sopenharmony_ci					       struct ehea_cqe *cqe)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	int skb_index = EHEA_BMASK_GET(EHEA_WR_ID_INDEX, cqe->wr_id);
5648c2ecf20Sopenharmony_ci	struct sk_buff *skb;
5658c2ecf20Sopenharmony_ci	void *pref;
5668c2ecf20Sopenharmony_ci	int x;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	x = skb_index + 1;
5698c2ecf20Sopenharmony_ci	x &= (arr_len - 1);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	pref = skb_array[x];
5728c2ecf20Sopenharmony_ci	if (pref) {
5738c2ecf20Sopenharmony_ci		prefetchw(pref);
5748c2ecf20Sopenharmony_ci		prefetchw(pref + EHEA_CACHE_LINE);
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci		pref = (skb_array[x]->data);
5778c2ecf20Sopenharmony_ci		prefetch(pref);
5788c2ecf20Sopenharmony_ci		prefetch(pref + EHEA_CACHE_LINE);
5798c2ecf20Sopenharmony_ci		prefetch(pref + EHEA_CACHE_LINE * 2);
5808c2ecf20Sopenharmony_ci		prefetch(pref + EHEA_CACHE_LINE * 3);
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	skb = skb_array[skb_index];
5848c2ecf20Sopenharmony_ci	skb_array[skb_index] = NULL;
5858c2ecf20Sopenharmony_ci	return skb;
5868c2ecf20Sopenharmony_ci}
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_cistatic inline struct sk_buff *get_skb_by_index_ll(struct sk_buff **skb_array,
5898c2ecf20Sopenharmony_ci						  int arr_len, int wqe_index)
5908c2ecf20Sopenharmony_ci{
5918c2ecf20Sopenharmony_ci	struct sk_buff *skb;
5928c2ecf20Sopenharmony_ci	void *pref;
5938c2ecf20Sopenharmony_ci	int x;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	x = wqe_index + 1;
5968c2ecf20Sopenharmony_ci	x &= (arr_len - 1);
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	pref = skb_array[x];
5998c2ecf20Sopenharmony_ci	if (pref) {
6008c2ecf20Sopenharmony_ci		prefetchw(pref);
6018c2ecf20Sopenharmony_ci		prefetchw(pref + EHEA_CACHE_LINE);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci		pref = (skb_array[x]->data);
6048c2ecf20Sopenharmony_ci		prefetchw(pref);
6058c2ecf20Sopenharmony_ci		prefetchw(pref + EHEA_CACHE_LINE);
6068c2ecf20Sopenharmony_ci	}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	skb = skb_array[wqe_index];
6098c2ecf20Sopenharmony_ci	skb_array[wqe_index] = NULL;
6108c2ecf20Sopenharmony_ci	return skb;
6118c2ecf20Sopenharmony_ci}
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_cistatic int ehea_treat_poll_error(struct ehea_port_res *pr, int rq,
6148c2ecf20Sopenharmony_ci				 struct ehea_cqe *cqe, int *processed_rq2,
6158c2ecf20Sopenharmony_ci				 int *processed_rq3)
6168c2ecf20Sopenharmony_ci{
6178c2ecf20Sopenharmony_ci	struct sk_buff *skb;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	if (cqe->status & EHEA_CQE_STAT_ERR_TCP)
6208c2ecf20Sopenharmony_ci		pr->p_stats.err_tcp_cksum++;
6218c2ecf20Sopenharmony_ci	if (cqe->status & EHEA_CQE_STAT_ERR_IP)
6228c2ecf20Sopenharmony_ci		pr->p_stats.err_ip_cksum++;
6238c2ecf20Sopenharmony_ci	if (cqe->status & EHEA_CQE_STAT_ERR_CRC)
6248c2ecf20Sopenharmony_ci		pr->p_stats.err_frame_crc++;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	if (rq == 2) {
6278c2ecf20Sopenharmony_ci		*processed_rq2 += 1;
6288c2ecf20Sopenharmony_ci		skb = get_skb_by_index(pr->rq2_skba.arr, pr->rq2_skba.len, cqe);
6298c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
6308c2ecf20Sopenharmony_ci	} else if (rq == 3) {
6318c2ecf20Sopenharmony_ci		*processed_rq3 += 1;
6328c2ecf20Sopenharmony_ci		skb = get_skb_by_index(pr->rq3_skba.arr, pr->rq3_skba.len, cqe);
6338c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
6348c2ecf20Sopenharmony_ci	}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	if (cqe->status & EHEA_CQE_STAT_FAT_ERR_MASK) {
6378c2ecf20Sopenharmony_ci		if (netif_msg_rx_err(pr->port)) {
6388c2ecf20Sopenharmony_ci			pr_err("Critical receive error for QP %d. Resetting port.\n",
6398c2ecf20Sopenharmony_ci			       pr->qp->init_attr.qp_nr);
6408c2ecf20Sopenharmony_ci			ehea_dump(cqe, sizeof(*cqe), "CQE");
6418c2ecf20Sopenharmony_ci		}
6428c2ecf20Sopenharmony_ci		ehea_schedule_port_reset(pr->port);
6438c2ecf20Sopenharmony_ci		return 1;
6448c2ecf20Sopenharmony_ci	}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	return 0;
6478c2ecf20Sopenharmony_ci}
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_cistatic int ehea_proc_rwqes(struct net_device *dev,
6508c2ecf20Sopenharmony_ci			   struct ehea_port_res *pr,
6518c2ecf20Sopenharmony_ci			   int budget)
6528c2ecf20Sopenharmony_ci{
6538c2ecf20Sopenharmony_ci	struct ehea_port *port = pr->port;
6548c2ecf20Sopenharmony_ci	struct ehea_qp *qp = pr->qp;
6558c2ecf20Sopenharmony_ci	struct ehea_cqe *cqe;
6568c2ecf20Sopenharmony_ci	struct sk_buff *skb;
6578c2ecf20Sopenharmony_ci	struct sk_buff **skb_arr_rq1 = pr->rq1_skba.arr;
6588c2ecf20Sopenharmony_ci	struct sk_buff **skb_arr_rq2 = pr->rq2_skba.arr;
6598c2ecf20Sopenharmony_ci	struct sk_buff **skb_arr_rq3 = pr->rq3_skba.arr;
6608c2ecf20Sopenharmony_ci	int skb_arr_rq1_len = pr->rq1_skba.len;
6618c2ecf20Sopenharmony_ci	int skb_arr_rq2_len = pr->rq2_skba.len;
6628c2ecf20Sopenharmony_ci	int skb_arr_rq3_len = pr->rq3_skba.len;
6638c2ecf20Sopenharmony_ci	int processed, processed_rq1, processed_rq2, processed_rq3;
6648c2ecf20Sopenharmony_ci	u64 processed_bytes = 0;
6658c2ecf20Sopenharmony_ci	int wqe_index, last_wqe_index, rq, port_reset;
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	processed = processed_rq1 = processed_rq2 = processed_rq3 = 0;
6688c2ecf20Sopenharmony_ci	last_wqe_index = 0;
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	cqe = ehea_poll_rq1(qp, &wqe_index);
6718c2ecf20Sopenharmony_ci	while ((processed < budget) && cqe) {
6728c2ecf20Sopenharmony_ci		ehea_inc_rq1(qp);
6738c2ecf20Sopenharmony_ci		processed_rq1++;
6748c2ecf20Sopenharmony_ci		processed++;
6758c2ecf20Sopenharmony_ci		if (netif_msg_rx_status(port))
6768c2ecf20Sopenharmony_ci			ehea_dump(cqe, sizeof(*cqe), "CQE");
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci		last_wqe_index = wqe_index;
6798c2ecf20Sopenharmony_ci		rmb();
6808c2ecf20Sopenharmony_ci		if (!ehea_check_cqe(cqe, &rq)) {
6818c2ecf20Sopenharmony_ci			if (rq == 1) {
6828c2ecf20Sopenharmony_ci				/* LL RQ1 */
6838c2ecf20Sopenharmony_ci				skb = get_skb_by_index_ll(skb_arr_rq1,
6848c2ecf20Sopenharmony_ci							  skb_arr_rq1_len,
6858c2ecf20Sopenharmony_ci							  wqe_index);
6868c2ecf20Sopenharmony_ci				if (unlikely(!skb)) {
6878c2ecf20Sopenharmony_ci					netif_info(port, rx_err, dev,
6888c2ecf20Sopenharmony_ci						  "LL rq1: skb=NULL\n");
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci					skb = netdev_alloc_skb(dev,
6918c2ecf20Sopenharmony_ci							       EHEA_L_PKT_SIZE);
6928c2ecf20Sopenharmony_ci					if (!skb)
6938c2ecf20Sopenharmony_ci						break;
6948c2ecf20Sopenharmony_ci				}
6958c2ecf20Sopenharmony_ci				skb_copy_to_linear_data(skb, ((char *)cqe) + 64,
6968c2ecf20Sopenharmony_ci						 cqe->num_bytes_transfered - 4);
6978c2ecf20Sopenharmony_ci				ehea_fill_skb(dev, skb, cqe, pr);
6988c2ecf20Sopenharmony_ci			} else if (rq == 2) {
6998c2ecf20Sopenharmony_ci				/* RQ2 */
7008c2ecf20Sopenharmony_ci				skb = get_skb_by_index(skb_arr_rq2,
7018c2ecf20Sopenharmony_ci						       skb_arr_rq2_len, cqe);
7028c2ecf20Sopenharmony_ci				if (unlikely(!skb)) {
7038c2ecf20Sopenharmony_ci					netif_err(port, rx_err, dev,
7048c2ecf20Sopenharmony_ci						  "rq2: skb=NULL\n");
7058c2ecf20Sopenharmony_ci					break;
7068c2ecf20Sopenharmony_ci				}
7078c2ecf20Sopenharmony_ci				ehea_fill_skb(dev, skb, cqe, pr);
7088c2ecf20Sopenharmony_ci				processed_rq2++;
7098c2ecf20Sopenharmony_ci			} else {
7108c2ecf20Sopenharmony_ci				/* RQ3 */
7118c2ecf20Sopenharmony_ci				skb = get_skb_by_index(skb_arr_rq3,
7128c2ecf20Sopenharmony_ci						       skb_arr_rq3_len, cqe);
7138c2ecf20Sopenharmony_ci				if (unlikely(!skb)) {
7148c2ecf20Sopenharmony_ci					netif_err(port, rx_err, dev,
7158c2ecf20Sopenharmony_ci						  "rq3: skb=NULL\n");
7168c2ecf20Sopenharmony_ci					break;
7178c2ecf20Sopenharmony_ci				}
7188c2ecf20Sopenharmony_ci				ehea_fill_skb(dev, skb, cqe, pr);
7198c2ecf20Sopenharmony_ci				processed_rq3++;
7208c2ecf20Sopenharmony_ci			}
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci			processed_bytes += skb->len;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci			if (cqe->status & EHEA_CQE_VLAN_TAG_XTRACT)
7258c2ecf20Sopenharmony_ci				__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
7268c2ecf20Sopenharmony_ci						       cqe->vlan_tag);
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci			napi_gro_receive(&pr->napi, skb);
7298c2ecf20Sopenharmony_ci		} else {
7308c2ecf20Sopenharmony_ci			pr->p_stats.poll_receive_errors++;
7318c2ecf20Sopenharmony_ci			port_reset = ehea_treat_poll_error(pr, rq, cqe,
7328c2ecf20Sopenharmony_ci							   &processed_rq2,
7338c2ecf20Sopenharmony_ci							   &processed_rq3);
7348c2ecf20Sopenharmony_ci			if (port_reset)
7358c2ecf20Sopenharmony_ci				break;
7368c2ecf20Sopenharmony_ci		}
7378c2ecf20Sopenharmony_ci		cqe = ehea_poll_rq1(qp, &wqe_index);
7388c2ecf20Sopenharmony_ci	}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	pr->rx_packets += processed;
7418c2ecf20Sopenharmony_ci	pr->rx_bytes += processed_bytes;
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	ehea_refill_rq1(pr, last_wqe_index, processed_rq1);
7448c2ecf20Sopenharmony_ci	ehea_refill_rq2(pr, processed_rq2);
7458c2ecf20Sopenharmony_ci	ehea_refill_rq3(pr, processed_rq3);
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	return processed;
7488c2ecf20Sopenharmony_ci}
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci#define SWQE_RESTART_CHECK 0xdeadbeaff00d0000ull
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_cistatic void reset_sq_restart_flag(struct ehea_port *port)
7538c2ecf20Sopenharmony_ci{
7548c2ecf20Sopenharmony_ci	int i;
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	for (i = 0; i < port->num_def_qps; i++) {
7578c2ecf20Sopenharmony_ci		struct ehea_port_res *pr = &port->port_res[i];
7588c2ecf20Sopenharmony_ci		pr->sq_restart_flag = 0;
7598c2ecf20Sopenharmony_ci	}
7608c2ecf20Sopenharmony_ci	wake_up(&port->restart_wq);
7618c2ecf20Sopenharmony_ci}
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_cistatic void check_sqs(struct ehea_port *port)
7648c2ecf20Sopenharmony_ci{
7658c2ecf20Sopenharmony_ci	struct ehea_swqe *swqe;
7668c2ecf20Sopenharmony_ci	int swqe_index;
7678c2ecf20Sopenharmony_ci	int i;
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	for (i = 0; i < port->num_def_qps; i++) {
7708c2ecf20Sopenharmony_ci		struct ehea_port_res *pr = &port->port_res[i];
7718c2ecf20Sopenharmony_ci		int ret;
7728c2ecf20Sopenharmony_ci		swqe = ehea_get_swqe(pr->qp, &swqe_index);
7738c2ecf20Sopenharmony_ci		memset(swqe, 0, SWQE_HEADER_SIZE);
7748c2ecf20Sopenharmony_ci		atomic_dec(&pr->swqe_avail);
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci		swqe->tx_control |= EHEA_SWQE_PURGE;
7778c2ecf20Sopenharmony_ci		swqe->wr_id = SWQE_RESTART_CHECK;
7788c2ecf20Sopenharmony_ci		swqe->tx_control |= EHEA_SWQE_SIGNALLED_COMPLETION;
7798c2ecf20Sopenharmony_ci		swqe->tx_control |= EHEA_SWQE_IMM_DATA_PRESENT;
7808c2ecf20Sopenharmony_ci		swqe->immediate_data_length = 80;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci		ehea_post_swqe(pr->qp, swqe);
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci		ret = wait_event_timeout(port->restart_wq,
7858c2ecf20Sopenharmony_ci					 pr->sq_restart_flag == 0,
7868c2ecf20Sopenharmony_ci					 msecs_to_jiffies(100));
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci		if (!ret) {
7898c2ecf20Sopenharmony_ci			pr_err("HW/SW queues out of sync\n");
7908c2ecf20Sopenharmony_ci			ehea_schedule_port_reset(pr->port);
7918c2ecf20Sopenharmony_ci			return;
7928c2ecf20Sopenharmony_ci		}
7938c2ecf20Sopenharmony_ci	}
7948c2ecf20Sopenharmony_ci}
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_cistatic struct ehea_cqe *ehea_proc_cqes(struct ehea_port_res *pr, int my_quota)
7988c2ecf20Sopenharmony_ci{
7998c2ecf20Sopenharmony_ci	struct sk_buff *skb;
8008c2ecf20Sopenharmony_ci	struct ehea_cq *send_cq = pr->send_cq;
8018c2ecf20Sopenharmony_ci	struct ehea_cqe *cqe;
8028c2ecf20Sopenharmony_ci	int quota = my_quota;
8038c2ecf20Sopenharmony_ci	int cqe_counter = 0;
8048c2ecf20Sopenharmony_ci	int swqe_av = 0;
8058c2ecf20Sopenharmony_ci	int index;
8068c2ecf20Sopenharmony_ci	struct netdev_queue *txq = netdev_get_tx_queue(pr->port->netdev,
8078c2ecf20Sopenharmony_ci						pr - &pr->port->port_res[0]);
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	cqe = ehea_poll_cq(send_cq);
8108c2ecf20Sopenharmony_ci	while (cqe && (quota > 0)) {
8118c2ecf20Sopenharmony_ci		ehea_inc_cq(send_cq);
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci		cqe_counter++;
8148c2ecf20Sopenharmony_ci		rmb();
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci		if (cqe->wr_id == SWQE_RESTART_CHECK) {
8178c2ecf20Sopenharmony_ci			pr->sq_restart_flag = 1;
8188c2ecf20Sopenharmony_ci			swqe_av++;
8198c2ecf20Sopenharmony_ci			break;
8208c2ecf20Sopenharmony_ci		}
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci		if (cqe->status & EHEA_CQE_STAT_ERR_MASK) {
8238c2ecf20Sopenharmony_ci			pr_err("Bad send completion status=0x%04X\n",
8248c2ecf20Sopenharmony_ci			       cqe->status);
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci			if (netif_msg_tx_err(pr->port))
8278c2ecf20Sopenharmony_ci				ehea_dump(cqe, sizeof(*cqe), "Send CQE");
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci			if (cqe->status & EHEA_CQE_STAT_RESET_MASK) {
8308c2ecf20Sopenharmony_ci				pr_err("Resetting port\n");
8318c2ecf20Sopenharmony_ci				ehea_schedule_port_reset(pr->port);
8328c2ecf20Sopenharmony_ci				break;
8338c2ecf20Sopenharmony_ci			}
8348c2ecf20Sopenharmony_ci		}
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci		if (netif_msg_tx_done(pr->port))
8378c2ecf20Sopenharmony_ci			ehea_dump(cqe, sizeof(*cqe), "CQE");
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci		if (likely(EHEA_BMASK_GET(EHEA_WR_ID_TYPE, cqe->wr_id)
8408c2ecf20Sopenharmony_ci			   == EHEA_SWQE2_TYPE)) {
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci			index = EHEA_BMASK_GET(EHEA_WR_ID_INDEX, cqe->wr_id);
8438c2ecf20Sopenharmony_ci			skb = pr->sq_skba.arr[index];
8448c2ecf20Sopenharmony_ci			dev_consume_skb_any(skb);
8458c2ecf20Sopenharmony_ci			pr->sq_skba.arr[index] = NULL;
8468c2ecf20Sopenharmony_ci		}
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci		swqe_av += EHEA_BMASK_GET(EHEA_WR_ID_REFILL, cqe->wr_id);
8498c2ecf20Sopenharmony_ci		quota--;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci		cqe = ehea_poll_cq(send_cq);
8528c2ecf20Sopenharmony_ci	}
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	ehea_update_feca(send_cq, cqe_counter);
8558c2ecf20Sopenharmony_ci	atomic_add(swqe_av, &pr->swqe_avail);
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	if (unlikely(netif_tx_queue_stopped(txq) &&
8588c2ecf20Sopenharmony_ci		     (atomic_read(&pr->swqe_avail) >= pr->swqe_refill_th))) {
8598c2ecf20Sopenharmony_ci		__netif_tx_lock(txq, smp_processor_id());
8608c2ecf20Sopenharmony_ci		if (netif_tx_queue_stopped(txq) &&
8618c2ecf20Sopenharmony_ci		    (atomic_read(&pr->swqe_avail) >= pr->swqe_refill_th))
8628c2ecf20Sopenharmony_ci			netif_tx_wake_queue(txq);
8638c2ecf20Sopenharmony_ci		__netif_tx_unlock(txq);
8648c2ecf20Sopenharmony_ci	}
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	wake_up(&pr->port->swqe_avail_wq);
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	return cqe;
8698c2ecf20Sopenharmony_ci}
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci#define EHEA_POLL_MAX_CQES 65535
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_cistatic int ehea_poll(struct napi_struct *napi, int budget)
8748c2ecf20Sopenharmony_ci{
8758c2ecf20Sopenharmony_ci	struct ehea_port_res *pr = container_of(napi, struct ehea_port_res,
8768c2ecf20Sopenharmony_ci						napi);
8778c2ecf20Sopenharmony_ci	struct net_device *dev = pr->port->netdev;
8788c2ecf20Sopenharmony_ci	struct ehea_cqe *cqe;
8798c2ecf20Sopenharmony_ci	struct ehea_cqe *cqe_skb = NULL;
8808c2ecf20Sopenharmony_ci	int wqe_index;
8818c2ecf20Sopenharmony_ci	int rx = 0;
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	cqe_skb = ehea_proc_cqes(pr, EHEA_POLL_MAX_CQES);
8848c2ecf20Sopenharmony_ci	rx += ehea_proc_rwqes(dev, pr, budget - rx);
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	while (rx != budget) {
8878c2ecf20Sopenharmony_ci		napi_complete(napi);
8888c2ecf20Sopenharmony_ci		ehea_reset_cq_ep(pr->recv_cq);
8898c2ecf20Sopenharmony_ci		ehea_reset_cq_ep(pr->send_cq);
8908c2ecf20Sopenharmony_ci		ehea_reset_cq_n1(pr->recv_cq);
8918c2ecf20Sopenharmony_ci		ehea_reset_cq_n1(pr->send_cq);
8928c2ecf20Sopenharmony_ci		rmb();
8938c2ecf20Sopenharmony_ci		cqe = ehea_poll_rq1(pr->qp, &wqe_index);
8948c2ecf20Sopenharmony_ci		cqe_skb = ehea_poll_cq(pr->send_cq);
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci		if (!cqe && !cqe_skb)
8978c2ecf20Sopenharmony_ci			return rx;
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci		if (!napi_reschedule(napi))
9008c2ecf20Sopenharmony_ci			return rx;
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci		cqe_skb = ehea_proc_cqes(pr, EHEA_POLL_MAX_CQES);
9038c2ecf20Sopenharmony_ci		rx += ehea_proc_rwqes(dev, pr, budget - rx);
9048c2ecf20Sopenharmony_ci	}
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	return rx;
9078c2ecf20Sopenharmony_ci}
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_cistatic irqreturn_t ehea_recv_irq_handler(int irq, void *param)
9108c2ecf20Sopenharmony_ci{
9118c2ecf20Sopenharmony_ci	struct ehea_port_res *pr = param;
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	napi_schedule(&pr->napi);
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
9168c2ecf20Sopenharmony_ci}
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_cistatic irqreturn_t ehea_qp_aff_irq_handler(int irq, void *param)
9198c2ecf20Sopenharmony_ci{
9208c2ecf20Sopenharmony_ci	struct ehea_port *port = param;
9218c2ecf20Sopenharmony_ci	struct ehea_eqe *eqe;
9228c2ecf20Sopenharmony_ci	struct ehea_qp *qp;
9238c2ecf20Sopenharmony_ci	u32 qp_token;
9248c2ecf20Sopenharmony_ci	u64 resource_type, aer, aerr;
9258c2ecf20Sopenharmony_ci	int reset_port = 0;
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	eqe = ehea_poll_eq(port->qp_eq);
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	while (eqe) {
9308c2ecf20Sopenharmony_ci		qp_token = EHEA_BMASK_GET(EHEA_EQE_QP_TOKEN, eqe->entry);
9318c2ecf20Sopenharmony_ci		pr_err("QP aff_err: entry=0x%llx, token=0x%x\n",
9328c2ecf20Sopenharmony_ci		       eqe->entry, qp_token);
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci		qp = port->port_res[qp_token].qp;
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci		resource_type = ehea_error_data(port->adapter, qp->fw_handle,
9378c2ecf20Sopenharmony_ci						&aer, &aerr);
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci		if (resource_type == EHEA_AER_RESTYPE_QP) {
9408c2ecf20Sopenharmony_ci			if ((aer & EHEA_AER_RESET_MASK) ||
9418c2ecf20Sopenharmony_ci			    (aerr & EHEA_AERR_RESET_MASK))
9428c2ecf20Sopenharmony_ci				 reset_port = 1;
9438c2ecf20Sopenharmony_ci		} else
9448c2ecf20Sopenharmony_ci			reset_port = 1;   /* Reset in case of CQ or EQ error */
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci		eqe = ehea_poll_eq(port->qp_eq);
9478c2ecf20Sopenharmony_ci	}
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	if (reset_port) {
9508c2ecf20Sopenharmony_ci		pr_err("Resetting port\n");
9518c2ecf20Sopenharmony_ci		ehea_schedule_port_reset(port);
9528c2ecf20Sopenharmony_ci	}
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
9558c2ecf20Sopenharmony_ci}
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_cistatic struct ehea_port *ehea_get_port(struct ehea_adapter *adapter,
9588c2ecf20Sopenharmony_ci				       int logical_port)
9598c2ecf20Sopenharmony_ci{
9608c2ecf20Sopenharmony_ci	int i;
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	for (i = 0; i < EHEA_MAX_PORTS; i++)
9638c2ecf20Sopenharmony_ci		if (adapter->port[i])
9648c2ecf20Sopenharmony_ci			if (adapter->port[i]->logical_port_id == logical_port)
9658c2ecf20Sopenharmony_ci				return adapter->port[i];
9668c2ecf20Sopenharmony_ci	return NULL;
9678c2ecf20Sopenharmony_ci}
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ciint ehea_sense_port_attr(struct ehea_port *port)
9708c2ecf20Sopenharmony_ci{
9718c2ecf20Sopenharmony_ci	int ret;
9728c2ecf20Sopenharmony_ci	u64 hret;
9738c2ecf20Sopenharmony_ci	struct hcp_ehea_port_cb0 *cb0;
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci	/* may be called via ehea_neq_tasklet() */
9768c2ecf20Sopenharmony_ci	cb0 = (void *)get_zeroed_page(GFP_ATOMIC);
9778c2ecf20Sopenharmony_ci	if (!cb0) {
9788c2ecf20Sopenharmony_ci		pr_err("no mem for cb0\n");
9798c2ecf20Sopenharmony_ci		ret = -ENOMEM;
9808c2ecf20Sopenharmony_ci		goto out;
9818c2ecf20Sopenharmony_ci	}
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	hret = ehea_h_query_ehea_port(port->adapter->handle,
9848c2ecf20Sopenharmony_ci				      port->logical_port_id, H_PORT_CB0,
9858c2ecf20Sopenharmony_ci				      EHEA_BMASK_SET(H_PORT_CB0_ALL, 0xFFFF),
9868c2ecf20Sopenharmony_ci				      cb0);
9878c2ecf20Sopenharmony_ci	if (hret != H_SUCCESS) {
9888c2ecf20Sopenharmony_ci		ret = -EIO;
9898c2ecf20Sopenharmony_ci		goto out_free;
9908c2ecf20Sopenharmony_ci	}
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci	/* MAC address */
9938c2ecf20Sopenharmony_ci	port->mac_addr = cb0->port_mac_addr << 16;
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	if (!is_valid_ether_addr((u8 *)&port->mac_addr)) {
9968c2ecf20Sopenharmony_ci		ret = -EADDRNOTAVAIL;
9978c2ecf20Sopenharmony_ci		goto out_free;
9988c2ecf20Sopenharmony_ci	}
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci	/* Port speed */
10018c2ecf20Sopenharmony_ci	switch (cb0->port_speed) {
10028c2ecf20Sopenharmony_ci	case H_SPEED_10M_H:
10038c2ecf20Sopenharmony_ci		port->port_speed = EHEA_SPEED_10M;
10048c2ecf20Sopenharmony_ci		port->full_duplex = 0;
10058c2ecf20Sopenharmony_ci		break;
10068c2ecf20Sopenharmony_ci	case H_SPEED_10M_F:
10078c2ecf20Sopenharmony_ci		port->port_speed = EHEA_SPEED_10M;
10088c2ecf20Sopenharmony_ci		port->full_duplex = 1;
10098c2ecf20Sopenharmony_ci		break;
10108c2ecf20Sopenharmony_ci	case H_SPEED_100M_H:
10118c2ecf20Sopenharmony_ci		port->port_speed = EHEA_SPEED_100M;
10128c2ecf20Sopenharmony_ci		port->full_duplex = 0;
10138c2ecf20Sopenharmony_ci		break;
10148c2ecf20Sopenharmony_ci	case H_SPEED_100M_F:
10158c2ecf20Sopenharmony_ci		port->port_speed = EHEA_SPEED_100M;
10168c2ecf20Sopenharmony_ci		port->full_duplex = 1;
10178c2ecf20Sopenharmony_ci		break;
10188c2ecf20Sopenharmony_ci	case H_SPEED_1G_F:
10198c2ecf20Sopenharmony_ci		port->port_speed = EHEA_SPEED_1G;
10208c2ecf20Sopenharmony_ci		port->full_duplex = 1;
10218c2ecf20Sopenharmony_ci		break;
10228c2ecf20Sopenharmony_ci	case H_SPEED_10G_F:
10238c2ecf20Sopenharmony_ci		port->port_speed = EHEA_SPEED_10G;
10248c2ecf20Sopenharmony_ci		port->full_duplex = 1;
10258c2ecf20Sopenharmony_ci		break;
10268c2ecf20Sopenharmony_ci	default:
10278c2ecf20Sopenharmony_ci		port->port_speed = 0;
10288c2ecf20Sopenharmony_ci		port->full_duplex = 0;
10298c2ecf20Sopenharmony_ci		break;
10308c2ecf20Sopenharmony_ci	}
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	port->autoneg = 1;
10338c2ecf20Sopenharmony_ci	port->num_mcs = cb0->num_default_qps;
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	/* Number of default QPs */
10368c2ecf20Sopenharmony_ci	if (use_mcs)
10378c2ecf20Sopenharmony_ci		port->num_def_qps = cb0->num_default_qps;
10388c2ecf20Sopenharmony_ci	else
10398c2ecf20Sopenharmony_ci		port->num_def_qps = 1;
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	if (!port->num_def_qps) {
10428c2ecf20Sopenharmony_ci		ret = -EINVAL;
10438c2ecf20Sopenharmony_ci		goto out_free;
10448c2ecf20Sopenharmony_ci	}
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	ret = 0;
10478c2ecf20Sopenharmony_ciout_free:
10488c2ecf20Sopenharmony_ci	if (ret || netif_msg_probe(port))
10498c2ecf20Sopenharmony_ci		ehea_dump(cb0, sizeof(*cb0), "ehea_sense_port_attr");
10508c2ecf20Sopenharmony_ci	free_page((unsigned long)cb0);
10518c2ecf20Sopenharmony_ciout:
10528c2ecf20Sopenharmony_ci	return ret;
10538c2ecf20Sopenharmony_ci}
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ciint ehea_set_portspeed(struct ehea_port *port, u32 port_speed)
10568c2ecf20Sopenharmony_ci{
10578c2ecf20Sopenharmony_ci	struct hcp_ehea_port_cb4 *cb4;
10588c2ecf20Sopenharmony_ci	u64 hret;
10598c2ecf20Sopenharmony_ci	int ret = 0;
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	cb4 = (void *)get_zeroed_page(GFP_KERNEL);
10628c2ecf20Sopenharmony_ci	if (!cb4) {
10638c2ecf20Sopenharmony_ci		pr_err("no mem for cb4\n");
10648c2ecf20Sopenharmony_ci		ret = -ENOMEM;
10658c2ecf20Sopenharmony_ci		goto out;
10668c2ecf20Sopenharmony_ci	}
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	cb4->port_speed = port_speed;
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	netif_carrier_off(port->netdev);
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	hret = ehea_h_modify_ehea_port(port->adapter->handle,
10738c2ecf20Sopenharmony_ci				       port->logical_port_id,
10748c2ecf20Sopenharmony_ci				       H_PORT_CB4, H_PORT_CB4_SPEED, cb4);
10758c2ecf20Sopenharmony_ci	if (hret == H_SUCCESS) {
10768c2ecf20Sopenharmony_ci		port->autoneg = port_speed == EHEA_SPEED_AUTONEG ? 1 : 0;
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci		hret = ehea_h_query_ehea_port(port->adapter->handle,
10798c2ecf20Sopenharmony_ci					      port->logical_port_id,
10808c2ecf20Sopenharmony_ci					      H_PORT_CB4, H_PORT_CB4_SPEED,
10818c2ecf20Sopenharmony_ci					      cb4);
10828c2ecf20Sopenharmony_ci		if (hret == H_SUCCESS) {
10838c2ecf20Sopenharmony_ci			switch (cb4->port_speed) {
10848c2ecf20Sopenharmony_ci			case H_SPEED_10M_H:
10858c2ecf20Sopenharmony_ci				port->port_speed = EHEA_SPEED_10M;
10868c2ecf20Sopenharmony_ci				port->full_duplex = 0;
10878c2ecf20Sopenharmony_ci				break;
10888c2ecf20Sopenharmony_ci			case H_SPEED_10M_F:
10898c2ecf20Sopenharmony_ci				port->port_speed = EHEA_SPEED_10M;
10908c2ecf20Sopenharmony_ci				port->full_duplex = 1;
10918c2ecf20Sopenharmony_ci				break;
10928c2ecf20Sopenharmony_ci			case H_SPEED_100M_H:
10938c2ecf20Sopenharmony_ci				port->port_speed = EHEA_SPEED_100M;
10948c2ecf20Sopenharmony_ci				port->full_duplex = 0;
10958c2ecf20Sopenharmony_ci				break;
10968c2ecf20Sopenharmony_ci			case H_SPEED_100M_F:
10978c2ecf20Sopenharmony_ci				port->port_speed = EHEA_SPEED_100M;
10988c2ecf20Sopenharmony_ci				port->full_duplex = 1;
10998c2ecf20Sopenharmony_ci				break;
11008c2ecf20Sopenharmony_ci			case H_SPEED_1G_F:
11018c2ecf20Sopenharmony_ci				port->port_speed = EHEA_SPEED_1G;
11028c2ecf20Sopenharmony_ci				port->full_duplex = 1;
11038c2ecf20Sopenharmony_ci				break;
11048c2ecf20Sopenharmony_ci			case H_SPEED_10G_F:
11058c2ecf20Sopenharmony_ci				port->port_speed = EHEA_SPEED_10G;
11068c2ecf20Sopenharmony_ci				port->full_duplex = 1;
11078c2ecf20Sopenharmony_ci				break;
11088c2ecf20Sopenharmony_ci			default:
11098c2ecf20Sopenharmony_ci				port->port_speed = 0;
11108c2ecf20Sopenharmony_ci				port->full_duplex = 0;
11118c2ecf20Sopenharmony_ci				break;
11128c2ecf20Sopenharmony_ci			}
11138c2ecf20Sopenharmony_ci		} else {
11148c2ecf20Sopenharmony_ci			pr_err("Failed sensing port speed\n");
11158c2ecf20Sopenharmony_ci			ret = -EIO;
11168c2ecf20Sopenharmony_ci		}
11178c2ecf20Sopenharmony_ci	} else {
11188c2ecf20Sopenharmony_ci		if (hret == H_AUTHORITY) {
11198c2ecf20Sopenharmony_ci			pr_info("Hypervisor denied setting port speed\n");
11208c2ecf20Sopenharmony_ci			ret = -EPERM;
11218c2ecf20Sopenharmony_ci		} else {
11228c2ecf20Sopenharmony_ci			ret = -EIO;
11238c2ecf20Sopenharmony_ci			pr_err("Failed setting port speed\n");
11248c2ecf20Sopenharmony_ci		}
11258c2ecf20Sopenharmony_ci	}
11268c2ecf20Sopenharmony_ci	if (!prop_carrier_state || (port->phy_link == EHEA_PHY_LINK_UP))
11278c2ecf20Sopenharmony_ci		netif_carrier_on(port->netdev);
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci	free_page((unsigned long)cb4);
11308c2ecf20Sopenharmony_ciout:
11318c2ecf20Sopenharmony_ci	return ret;
11328c2ecf20Sopenharmony_ci}
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_cistatic void ehea_parse_eqe(struct ehea_adapter *adapter, u64 eqe)
11358c2ecf20Sopenharmony_ci{
11368c2ecf20Sopenharmony_ci	int ret;
11378c2ecf20Sopenharmony_ci	u8 ec;
11388c2ecf20Sopenharmony_ci	u8 portnum;
11398c2ecf20Sopenharmony_ci	struct ehea_port *port;
11408c2ecf20Sopenharmony_ci	struct net_device *dev;
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	ec = EHEA_BMASK_GET(NEQE_EVENT_CODE, eqe);
11438c2ecf20Sopenharmony_ci	portnum = EHEA_BMASK_GET(NEQE_PORTNUM, eqe);
11448c2ecf20Sopenharmony_ci	port = ehea_get_port(adapter, portnum);
11458c2ecf20Sopenharmony_ci	if (!port) {
11468c2ecf20Sopenharmony_ci		netdev_err(NULL, "unknown portnum %x\n", portnum);
11478c2ecf20Sopenharmony_ci		return;
11488c2ecf20Sopenharmony_ci	}
11498c2ecf20Sopenharmony_ci	dev = port->netdev;
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci	switch (ec) {
11528c2ecf20Sopenharmony_ci	case EHEA_EC_PORTSTATE_CHG:	/* port state change */
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci		if (EHEA_BMASK_GET(NEQE_PORT_UP, eqe)) {
11558c2ecf20Sopenharmony_ci			if (!netif_carrier_ok(dev)) {
11568c2ecf20Sopenharmony_ci				ret = ehea_sense_port_attr(port);
11578c2ecf20Sopenharmony_ci				if (ret) {
11588c2ecf20Sopenharmony_ci					netdev_err(dev, "failed resensing port attributes\n");
11598c2ecf20Sopenharmony_ci					break;
11608c2ecf20Sopenharmony_ci				}
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci				netif_info(port, link, dev,
11638c2ecf20Sopenharmony_ci					   "Logical port up: %dMbps %s Duplex\n",
11648c2ecf20Sopenharmony_ci					   port->port_speed,
11658c2ecf20Sopenharmony_ci					   port->full_duplex == 1 ?
11668c2ecf20Sopenharmony_ci					   "Full" : "Half");
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci				netif_carrier_on(dev);
11698c2ecf20Sopenharmony_ci				netif_wake_queue(dev);
11708c2ecf20Sopenharmony_ci			}
11718c2ecf20Sopenharmony_ci		} else
11728c2ecf20Sopenharmony_ci			if (netif_carrier_ok(dev)) {
11738c2ecf20Sopenharmony_ci				netif_info(port, link, dev,
11748c2ecf20Sopenharmony_ci					   "Logical port down\n");
11758c2ecf20Sopenharmony_ci				netif_carrier_off(dev);
11768c2ecf20Sopenharmony_ci				netif_tx_disable(dev);
11778c2ecf20Sopenharmony_ci			}
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci		if (EHEA_BMASK_GET(NEQE_EXTSWITCH_PORT_UP, eqe)) {
11808c2ecf20Sopenharmony_ci			port->phy_link = EHEA_PHY_LINK_UP;
11818c2ecf20Sopenharmony_ci			netif_info(port, link, dev,
11828c2ecf20Sopenharmony_ci				   "Physical port up\n");
11838c2ecf20Sopenharmony_ci			if (prop_carrier_state)
11848c2ecf20Sopenharmony_ci				netif_carrier_on(dev);
11858c2ecf20Sopenharmony_ci		} else {
11868c2ecf20Sopenharmony_ci			port->phy_link = EHEA_PHY_LINK_DOWN;
11878c2ecf20Sopenharmony_ci			netif_info(port, link, dev,
11888c2ecf20Sopenharmony_ci				   "Physical port down\n");
11898c2ecf20Sopenharmony_ci			if (prop_carrier_state)
11908c2ecf20Sopenharmony_ci				netif_carrier_off(dev);
11918c2ecf20Sopenharmony_ci		}
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci		if (EHEA_BMASK_GET(NEQE_EXTSWITCH_PRIMARY, eqe))
11948c2ecf20Sopenharmony_ci			netdev_info(dev,
11958c2ecf20Sopenharmony_ci				    "External switch port is primary port\n");
11968c2ecf20Sopenharmony_ci		else
11978c2ecf20Sopenharmony_ci			netdev_info(dev,
11988c2ecf20Sopenharmony_ci				    "External switch port is backup port\n");
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci		break;
12018c2ecf20Sopenharmony_ci	case EHEA_EC_ADAPTER_MALFUNC:
12028c2ecf20Sopenharmony_ci		netdev_err(dev, "Adapter malfunction\n");
12038c2ecf20Sopenharmony_ci		break;
12048c2ecf20Sopenharmony_ci	case EHEA_EC_PORT_MALFUNC:
12058c2ecf20Sopenharmony_ci		netdev_info(dev, "Port malfunction\n");
12068c2ecf20Sopenharmony_ci		netif_carrier_off(dev);
12078c2ecf20Sopenharmony_ci		netif_tx_disable(dev);
12088c2ecf20Sopenharmony_ci		break;
12098c2ecf20Sopenharmony_ci	default:
12108c2ecf20Sopenharmony_ci		netdev_err(dev, "unknown event code %x, eqe=0x%llX\n", ec, eqe);
12118c2ecf20Sopenharmony_ci		break;
12128c2ecf20Sopenharmony_ci	}
12138c2ecf20Sopenharmony_ci}
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_cistatic void ehea_neq_tasklet(struct tasklet_struct *t)
12168c2ecf20Sopenharmony_ci{
12178c2ecf20Sopenharmony_ci	struct ehea_adapter *adapter = from_tasklet(adapter, t, neq_tasklet);
12188c2ecf20Sopenharmony_ci	struct ehea_eqe *eqe;
12198c2ecf20Sopenharmony_ci	u64 event_mask;
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci	eqe = ehea_poll_eq(adapter->neq);
12228c2ecf20Sopenharmony_ci	pr_debug("eqe=%p\n", eqe);
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	while (eqe) {
12258c2ecf20Sopenharmony_ci		pr_debug("*eqe=%lx\n", (unsigned long) eqe->entry);
12268c2ecf20Sopenharmony_ci		ehea_parse_eqe(adapter, eqe->entry);
12278c2ecf20Sopenharmony_ci		eqe = ehea_poll_eq(adapter->neq);
12288c2ecf20Sopenharmony_ci		pr_debug("next eqe=%p\n", eqe);
12298c2ecf20Sopenharmony_ci	}
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	event_mask = EHEA_BMASK_SET(NELR_PORTSTATE_CHG, 1)
12328c2ecf20Sopenharmony_ci		   | EHEA_BMASK_SET(NELR_ADAPTER_MALFUNC, 1)
12338c2ecf20Sopenharmony_ci		   | EHEA_BMASK_SET(NELR_PORT_MALFUNC, 1);
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	ehea_h_reset_events(adapter->handle,
12368c2ecf20Sopenharmony_ci			    adapter->neq->fw_handle, event_mask);
12378c2ecf20Sopenharmony_ci}
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_cistatic irqreturn_t ehea_interrupt_neq(int irq, void *param)
12408c2ecf20Sopenharmony_ci{
12418c2ecf20Sopenharmony_ci	struct ehea_adapter *adapter = param;
12428c2ecf20Sopenharmony_ci	tasklet_hi_schedule(&adapter->neq_tasklet);
12438c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
12448c2ecf20Sopenharmony_ci}
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_cistatic int ehea_fill_port_res(struct ehea_port_res *pr)
12488c2ecf20Sopenharmony_ci{
12498c2ecf20Sopenharmony_ci	int ret;
12508c2ecf20Sopenharmony_ci	struct ehea_qp_init_attr *init_attr = &pr->qp->init_attr;
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	ehea_init_fill_rq1(pr, pr->rq1_skba.len);
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci	ret = ehea_refill_rq2(pr, init_attr->act_nr_rwqes_rq2 - 1);
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	ret |= ehea_refill_rq3(pr, init_attr->act_nr_rwqes_rq3 - 1);
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci	return ret;
12598c2ecf20Sopenharmony_ci}
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_cistatic int ehea_reg_interrupts(struct net_device *dev)
12628c2ecf20Sopenharmony_ci{
12638c2ecf20Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
12648c2ecf20Sopenharmony_ci	struct ehea_port_res *pr;
12658c2ecf20Sopenharmony_ci	int i, ret;
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_ci	snprintf(port->int_aff_name, EHEA_IRQ_NAME_SIZE - 1, "%s-aff",
12698c2ecf20Sopenharmony_ci		 dev->name);
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	ret = ibmebus_request_irq(port->qp_eq->attr.ist1,
12728c2ecf20Sopenharmony_ci				  ehea_qp_aff_irq_handler,
12738c2ecf20Sopenharmony_ci				  0, port->int_aff_name, port);
12748c2ecf20Sopenharmony_ci	if (ret) {
12758c2ecf20Sopenharmony_ci		netdev_err(dev, "failed registering irq for qp_aff_irq_handler:ist=%X\n",
12768c2ecf20Sopenharmony_ci			   port->qp_eq->attr.ist1);
12778c2ecf20Sopenharmony_ci		goto out_free_qpeq;
12788c2ecf20Sopenharmony_ci	}
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	netif_info(port, ifup, dev,
12818c2ecf20Sopenharmony_ci		   "irq_handle 0x%X for function qp_aff_irq_handler registered\n",
12828c2ecf20Sopenharmony_ci		   port->qp_eq->attr.ist1);
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci	for (i = 0; i < port->num_def_qps; i++) {
12868c2ecf20Sopenharmony_ci		pr = &port->port_res[i];
12878c2ecf20Sopenharmony_ci		snprintf(pr->int_send_name, EHEA_IRQ_NAME_SIZE - 1,
12888c2ecf20Sopenharmony_ci			 "%s-queue%d", dev->name, i);
12898c2ecf20Sopenharmony_ci		ret = ibmebus_request_irq(pr->eq->attr.ist1,
12908c2ecf20Sopenharmony_ci					  ehea_recv_irq_handler,
12918c2ecf20Sopenharmony_ci					  0, pr->int_send_name, pr);
12928c2ecf20Sopenharmony_ci		if (ret) {
12938c2ecf20Sopenharmony_ci			netdev_err(dev, "failed registering irq for ehea_queue port_res_nr:%d, ist=%X\n",
12948c2ecf20Sopenharmony_ci				   i, pr->eq->attr.ist1);
12958c2ecf20Sopenharmony_ci			goto out_free_req;
12968c2ecf20Sopenharmony_ci		}
12978c2ecf20Sopenharmony_ci		netif_info(port, ifup, dev,
12988c2ecf20Sopenharmony_ci			   "irq_handle 0x%X for function ehea_queue_int %d registered\n",
12998c2ecf20Sopenharmony_ci			   pr->eq->attr.ist1, i);
13008c2ecf20Sopenharmony_ci	}
13018c2ecf20Sopenharmony_ciout:
13028c2ecf20Sopenharmony_ci	return ret;
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ciout_free_req:
13068c2ecf20Sopenharmony_ci	while (--i >= 0) {
13078c2ecf20Sopenharmony_ci		u32 ist = port->port_res[i].eq->attr.ist1;
13088c2ecf20Sopenharmony_ci		ibmebus_free_irq(ist, &port->port_res[i]);
13098c2ecf20Sopenharmony_ci	}
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_ciout_free_qpeq:
13128c2ecf20Sopenharmony_ci	ibmebus_free_irq(port->qp_eq->attr.ist1, port);
13138c2ecf20Sopenharmony_ci	i = port->num_def_qps;
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci	goto out;
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci}
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_cistatic void ehea_free_interrupts(struct net_device *dev)
13208c2ecf20Sopenharmony_ci{
13218c2ecf20Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
13228c2ecf20Sopenharmony_ci	struct ehea_port_res *pr;
13238c2ecf20Sopenharmony_ci	int i;
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci	/* send */
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ci	for (i = 0; i < port->num_def_qps; i++) {
13288c2ecf20Sopenharmony_ci		pr = &port->port_res[i];
13298c2ecf20Sopenharmony_ci		ibmebus_free_irq(pr->eq->attr.ist1, pr);
13308c2ecf20Sopenharmony_ci		netif_info(port, intr, dev,
13318c2ecf20Sopenharmony_ci			   "free send irq for res %d with handle 0x%X\n",
13328c2ecf20Sopenharmony_ci			   i, pr->eq->attr.ist1);
13338c2ecf20Sopenharmony_ci	}
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_ci	/* associated events */
13368c2ecf20Sopenharmony_ci	ibmebus_free_irq(port->qp_eq->attr.ist1, port);
13378c2ecf20Sopenharmony_ci	netif_info(port, intr, dev,
13388c2ecf20Sopenharmony_ci		   "associated event interrupt for handle 0x%X freed\n",
13398c2ecf20Sopenharmony_ci		   port->qp_eq->attr.ist1);
13408c2ecf20Sopenharmony_ci}
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_cistatic int ehea_configure_port(struct ehea_port *port)
13438c2ecf20Sopenharmony_ci{
13448c2ecf20Sopenharmony_ci	int ret, i;
13458c2ecf20Sopenharmony_ci	u64 hret, mask;
13468c2ecf20Sopenharmony_ci	struct hcp_ehea_port_cb0 *cb0;
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci	ret = -ENOMEM;
13498c2ecf20Sopenharmony_ci	cb0 = (void *)get_zeroed_page(GFP_KERNEL);
13508c2ecf20Sopenharmony_ci	if (!cb0)
13518c2ecf20Sopenharmony_ci		goto out;
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci	cb0->port_rc = EHEA_BMASK_SET(PXLY_RC_VALID, 1)
13548c2ecf20Sopenharmony_ci		     | EHEA_BMASK_SET(PXLY_RC_IP_CHKSUM, 1)
13558c2ecf20Sopenharmony_ci		     | EHEA_BMASK_SET(PXLY_RC_TCP_UDP_CHKSUM, 1)
13568c2ecf20Sopenharmony_ci		     | EHEA_BMASK_SET(PXLY_RC_VLAN_XTRACT, 1)
13578c2ecf20Sopenharmony_ci		     | EHEA_BMASK_SET(PXLY_RC_VLAN_TAG_FILTER,
13588c2ecf20Sopenharmony_ci				      PXLY_RC_VLAN_FILTER)
13598c2ecf20Sopenharmony_ci		     | EHEA_BMASK_SET(PXLY_RC_JUMBO_FRAME, 1);
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci	for (i = 0; i < port->num_mcs; i++)
13628c2ecf20Sopenharmony_ci		if (use_mcs)
13638c2ecf20Sopenharmony_ci			cb0->default_qpn_arr[i] =
13648c2ecf20Sopenharmony_ci				port->port_res[i].qp->init_attr.qp_nr;
13658c2ecf20Sopenharmony_ci		else
13668c2ecf20Sopenharmony_ci			cb0->default_qpn_arr[i] =
13678c2ecf20Sopenharmony_ci				port->port_res[0].qp->init_attr.qp_nr;
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci	if (netif_msg_ifup(port))
13708c2ecf20Sopenharmony_ci		ehea_dump(cb0, sizeof(*cb0), "ehea_configure_port");
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci	mask = EHEA_BMASK_SET(H_PORT_CB0_PRC, 1)
13738c2ecf20Sopenharmony_ci	     | EHEA_BMASK_SET(H_PORT_CB0_DEFQPNARRAY, 1);
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci	hret = ehea_h_modify_ehea_port(port->adapter->handle,
13768c2ecf20Sopenharmony_ci				       port->logical_port_id,
13778c2ecf20Sopenharmony_ci				       H_PORT_CB0, mask, cb0);
13788c2ecf20Sopenharmony_ci	ret = -EIO;
13798c2ecf20Sopenharmony_ci	if (hret != H_SUCCESS)
13808c2ecf20Sopenharmony_ci		goto out_free;
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	ret = 0;
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ciout_free:
13858c2ecf20Sopenharmony_ci	free_page((unsigned long)cb0);
13868c2ecf20Sopenharmony_ciout:
13878c2ecf20Sopenharmony_ci	return ret;
13888c2ecf20Sopenharmony_ci}
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_cistatic int ehea_gen_smrs(struct ehea_port_res *pr)
13918c2ecf20Sopenharmony_ci{
13928c2ecf20Sopenharmony_ci	int ret;
13938c2ecf20Sopenharmony_ci	struct ehea_adapter *adapter = pr->port->adapter;
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ci	ret = ehea_gen_smr(adapter, &adapter->mr, &pr->send_mr);
13968c2ecf20Sopenharmony_ci	if (ret)
13978c2ecf20Sopenharmony_ci		goto out;
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	ret = ehea_gen_smr(adapter, &adapter->mr, &pr->recv_mr);
14008c2ecf20Sopenharmony_ci	if (ret)
14018c2ecf20Sopenharmony_ci		goto out_free;
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci	return 0;
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ciout_free:
14068c2ecf20Sopenharmony_ci	ehea_rem_mr(&pr->send_mr);
14078c2ecf20Sopenharmony_ciout:
14088c2ecf20Sopenharmony_ci	pr_err("Generating SMRS failed\n");
14098c2ecf20Sopenharmony_ci	return -EIO;
14108c2ecf20Sopenharmony_ci}
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_cistatic int ehea_rem_smrs(struct ehea_port_res *pr)
14138c2ecf20Sopenharmony_ci{
14148c2ecf20Sopenharmony_ci	if ((ehea_rem_mr(&pr->send_mr)) ||
14158c2ecf20Sopenharmony_ci	    (ehea_rem_mr(&pr->recv_mr)))
14168c2ecf20Sopenharmony_ci		return -EIO;
14178c2ecf20Sopenharmony_ci	else
14188c2ecf20Sopenharmony_ci		return 0;
14198c2ecf20Sopenharmony_ci}
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_cistatic int ehea_init_q_skba(struct ehea_q_skb_arr *q_skba, int max_q_entries)
14228c2ecf20Sopenharmony_ci{
14238c2ecf20Sopenharmony_ci	int arr_size = sizeof(void *) * max_q_entries;
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci	q_skba->arr = vzalloc(arr_size);
14268c2ecf20Sopenharmony_ci	if (!q_skba->arr)
14278c2ecf20Sopenharmony_ci		return -ENOMEM;
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci	q_skba->len = max_q_entries;
14308c2ecf20Sopenharmony_ci	q_skba->index = 0;
14318c2ecf20Sopenharmony_ci	q_skba->os_skbs = 0;
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci	return 0;
14348c2ecf20Sopenharmony_ci}
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_cistatic int ehea_init_port_res(struct ehea_port *port, struct ehea_port_res *pr,
14378c2ecf20Sopenharmony_ci			      struct port_res_cfg *pr_cfg, int queue_token)
14388c2ecf20Sopenharmony_ci{
14398c2ecf20Sopenharmony_ci	struct ehea_adapter *adapter = port->adapter;
14408c2ecf20Sopenharmony_ci	enum ehea_eq_type eq_type = EHEA_EQ;
14418c2ecf20Sopenharmony_ci	struct ehea_qp_init_attr *init_attr = NULL;
14428c2ecf20Sopenharmony_ci	int ret = -EIO;
14438c2ecf20Sopenharmony_ci	u64 tx_bytes, rx_bytes, tx_packets, rx_packets;
14448c2ecf20Sopenharmony_ci
14458c2ecf20Sopenharmony_ci	tx_bytes = pr->tx_bytes;
14468c2ecf20Sopenharmony_ci	tx_packets = pr->tx_packets;
14478c2ecf20Sopenharmony_ci	rx_bytes = pr->rx_bytes;
14488c2ecf20Sopenharmony_ci	rx_packets = pr->rx_packets;
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci	memset(pr, 0, sizeof(struct ehea_port_res));
14518c2ecf20Sopenharmony_ci
14528c2ecf20Sopenharmony_ci	pr->tx_bytes = tx_bytes;
14538c2ecf20Sopenharmony_ci	pr->tx_packets = tx_packets;
14548c2ecf20Sopenharmony_ci	pr->rx_bytes = rx_bytes;
14558c2ecf20Sopenharmony_ci	pr->rx_packets = rx_packets;
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_ci	pr->port = port;
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_ci	pr->eq = ehea_create_eq(adapter, eq_type, EHEA_MAX_ENTRIES_EQ, 0);
14608c2ecf20Sopenharmony_ci	if (!pr->eq) {
14618c2ecf20Sopenharmony_ci		pr_err("create_eq failed (eq)\n");
14628c2ecf20Sopenharmony_ci		goto out_free;
14638c2ecf20Sopenharmony_ci	}
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ci	pr->recv_cq = ehea_create_cq(adapter, pr_cfg->max_entries_rcq,
14668c2ecf20Sopenharmony_ci				     pr->eq->fw_handle,
14678c2ecf20Sopenharmony_ci				     port->logical_port_id);
14688c2ecf20Sopenharmony_ci	if (!pr->recv_cq) {
14698c2ecf20Sopenharmony_ci		pr_err("create_cq failed (cq_recv)\n");
14708c2ecf20Sopenharmony_ci		goto out_free;
14718c2ecf20Sopenharmony_ci	}
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci	pr->send_cq = ehea_create_cq(adapter, pr_cfg->max_entries_scq,
14748c2ecf20Sopenharmony_ci				     pr->eq->fw_handle,
14758c2ecf20Sopenharmony_ci				     port->logical_port_id);
14768c2ecf20Sopenharmony_ci	if (!pr->send_cq) {
14778c2ecf20Sopenharmony_ci		pr_err("create_cq failed (cq_send)\n");
14788c2ecf20Sopenharmony_ci		goto out_free;
14798c2ecf20Sopenharmony_ci	}
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_ci	if (netif_msg_ifup(port))
14828c2ecf20Sopenharmony_ci		pr_info("Send CQ: act_nr_cqes=%d, Recv CQ: act_nr_cqes=%d\n",
14838c2ecf20Sopenharmony_ci			pr->send_cq->attr.act_nr_of_cqes,
14848c2ecf20Sopenharmony_ci			pr->recv_cq->attr.act_nr_of_cqes);
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci	init_attr = kzalloc(sizeof(*init_attr), GFP_KERNEL);
14878c2ecf20Sopenharmony_ci	if (!init_attr) {
14888c2ecf20Sopenharmony_ci		ret = -ENOMEM;
14898c2ecf20Sopenharmony_ci		pr_err("no mem for ehea_qp_init_attr\n");
14908c2ecf20Sopenharmony_ci		goto out_free;
14918c2ecf20Sopenharmony_ci	}
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci	init_attr->low_lat_rq1 = 1;
14948c2ecf20Sopenharmony_ci	init_attr->signalingtype = 1;	/* generate CQE if specified in WQE */
14958c2ecf20Sopenharmony_ci	init_attr->rq_count = 3;
14968c2ecf20Sopenharmony_ci	init_attr->qp_token = queue_token;
14978c2ecf20Sopenharmony_ci	init_attr->max_nr_send_wqes = pr_cfg->max_entries_sq;
14988c2ecf20Sopenharmony_ci	init_attr->max_nr_rwqes_rq1 = pr_cfg->max_entries_rq1;
14998c2ecf20Sopenharmony_ci	init_attr->max_nr_rwqes_rq2 = pr_cfg->max_entries_rq2;
15008c2ecf20Sopenharmony_ci	init_attr->max_nr_rwqes_rq3 = pr_cfg->max_entries_rq3;
15018c2ecf20Sopenharmony_ci	init_attr->wqe_size_enc_sq = EHEA_SG_SQ;
15028c2ecf20Sopenharmony_ci	init_attr->wqe_size_enc_rq1 = EHEA_SG_RQ1;
15038c2ecf20Sopenharmony_ci	init_attr->wqe_size_enc_rq2 = EHEA_SG_RQ2;
15048c2ecf20Sopenharmony_ci	init_attr->wqe_size_enc_rq3 = EHEA_SG_RQ3;
15058c2ecf20Sopenharmony_ci	init_attr->rq2_threshold = EHEA_RQ2_THRESHOLD;
15068c2ecf20Sopenharmony_ci	init_attr->rq3_threshold = EHEA_RQ3_THRESHOLD;
15078c2ecf20Sopenharmony_ci	init_attr->port_nr = port->logical_port_id;
15088c2ecf20Sopenharmony_ci	init_attr->send_cq_handle = pr->send_cq->fw_handle;
15098c2ecf20Sopenharmony_ci	init_attr->recv_cq_handle = pr->recv_cq->fw_handle;
15108c2ecf20Sopenharmony_ci	init_attr->aff_eq_handle = port->qp_eq->fw_handle;
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci	pr->qp = ehea_create_qp(adapter, adapter->pd, init_attr);
15138c2ecf20Sopenharmony_ci	if (!pr->qp) {
15148c2ecf20Sopenharmony_ci		pr_err("create_qp failed\n");
15158c2ecf20Sopenharmony_ci		ret = -EIO;
15168c2ecf20Sopenharmony_ci		goto out_free;
15178c2ecf20Sopenharmony_ci	}
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci	if (netif_msg_ifup(port))
15208c2ecf20Sopenharmony_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",
15218c2ecf20Sopenharmony_ci			init_attr->qp_nr,
15228c2ecf20Sopenharmony_ci			init_attr->act_nr_send_wqes,
15238c2ecf20Sopenharmony_ci			init_attr->act_nr_rwqes_rq1,
15248c2ecf20Sopenharmony_ci			init_attr->act_nr_rwqes_rq2,
15258c2ecf20Sopenharmony_ci			init_attr->act_nr_rwqes_rq3);
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_ci	pr->sq_skba_size = init_attr->act_nr_send_wqes + 1;
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_ci	ret = ehea_init_q_skba(&pr->sq_skba, pr->sq_skba_size);
15308c2ecf20Sopenharmony_ci	ret |= ehea_init_q_skba(&pr->rq1_skba, init_attr->act_nr_rwqes_rq1 + 1);
15318c2ecf20Sopenharmony_ci	ret |= ehea_init_q_skba(&pr->rq2_skba, init_attr->act_nr_rwqes_rq2 + 1);
15328c2ecf20Sopenharmony_ci	ret |= ehea_init_q_skba(&pr->rq3_skba, init_attr->act_nr_rwqes_rq3 + 1);
15338c2ecf20Sopenharmony_ci	if (ret)
15348c2ecf20Sopenharmony_ci		goto out_free;
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci	pr->swqe_refill_th = init_attr->act_nr_send_wqes / 10;
15378c2ecf20Sopenharmony_ci	if (ehea_gen_smrs(pr) != 0) {
15388c2ecf20Sopenharmony_ci		ret = -EIO;
15398c2ecf20Sopenharmony_ci		goto out_free;
15408c2ecf20Sopenharmony_ci	}
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ci	atomic_set(&pr->swqe_avail, init_attr->act_nr_send_wqes - 1);
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci	kfree(init_attr);
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci	netif_napi_add(pr->port->netdev, &pr->napi, ehea_poll, 64);
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_ci	ret = 0;
15498c2ecf20Sopenharmony_ci	goto out;
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_ciout_free:
15528c2ecf20Sopenharmony_ci	kfree(init_attr);
15538c2ecf20Sopenharmony_ci	vfree(pr->sq_skba.arr);
15548c2ecf20Sopenharmony_ci	vfree(pr->rq1_skba.arr);
15558c2ecf20Sopenharmony_ci	vfree(pr->rq2_skba.arr);
15568c2ecf20Sopenharmony_ci	vfree(pr->rq3_skba.arr);
15578c2ecf20Sopenharmony_ci	ehea_destroy_qp(pr->qp);
15588c2ecf20Sopenharmony_ci	ehea_destroy_cq(pr->send_cq);
15598c2ecf20Sopenharmony_ci	ehea_destroy_cq(pr->recv_cq);
15608c2ecf20Sopenharmony_ci	ehea_destroy_eq(pr->eq);
15618c2ecf20Sopenharmony_ciout:
15628c2ecf20Sopenharmony_ci	return ret;
15638c2ecf20Sopenharmony_ci}
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_cistatic int ehea_clean_portres(struct ehea_port *port, struct ehea_port_res *pr)
15668c2ecf20Sopenharmony_ci{
15678c2ecf20Sopenharmony_ci	int ret, i;
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_ci	if (pr->qp)
15708c2ecf20Sopenharmony_ci		netif_napi_del(&pr->napi);
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci	ret = ehea_destroy_qp(pr->qp);
15738c2ecf20Sopenharmony_ci
15748c2ecf20Sopenharmony_ci	if (!ret) {
15758c2ecf20Sopenharmony_ci		ehea_destroy_cq(pr->send_cq);
15768c2ecf20Sopenharmony_ci		ehea_destroy_cq(pr->recv_cq);
15778c2ecf20Sopenharmony_ci		ehea_destroy_eq(pr->eq);
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_ci		for (i = 0; i < pr->rq1_skba.len; i++)
15808c2ecf20Sopenharmony_ci			dev_kfree_skb(pr->rq1_skba.arr[i]);
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_ci		for (i = 0; i < pr->rq2_skba.len; i++)
15838c2ecf20Sopenharmony_ci			dev_kfree_skb(pr->rq2_skba.arr[i]);
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_ci		for (i = 0; i < pr->rq3_skba.len; i++)
15868c2ecf20Sopenharmony_ci			dev_kfree_skb(pr->rq3_skba.arr[i]);
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci		for (i = 0; i < pr->sq_skba.len; i++)
15898c2ecf20Sopenharmony_ci			dev_kfree_skb(pr->sq_skba.arr[i]);
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci		vfree(pr->rq1_skba.arr);
15928c2ecf20Sopenharmony_ci		vfree(pr->rq2_skba.arr);
15938c2ecf20Sopenharmony_ci		vfree(pr->rq3_skba.arr);
15948c2ecf20Sopenharmony_ci		vfree(pr->sq_skba.arr);
15958c2ecf20Sopenharmony_ci		ret = ehea_rem_smrs(pr);
15968c2ecf20Sopenharmony_ci	}
15978c2ecf20Sopenharmony_ci	return ret;
15988c2ecf20Sopenharmony_ci}
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_cistatic void write_swqe2_immediate(struct sk_buff *skb, struct ehea_swqe *swqe,
16018c2ecf20Sopenharmony_ci				  u32 lkey)
16028c2ecf20Sopenharmony_ci{
16038c2ecf20Sopenharmony_ci	int skb_data_size = skb_headlen(skb);
16048c2ecf20Sopenharmony_ci	u8 *imm_data = &swqe->u.immdata_desc.immediate_data[0];
16058c2ecf20Sopenharmony_ci	struct ehea_vsgentry *sg1entry = &swqe->u.immdata_desc.sg_entry;
16068c2ecf20Sopenharmony_ci	unsigned int immediate_len = SWQE2_MAX_IMM;
16078c2ecf20Sopenharmony_ci
16088c2ecf20Sopenharmony_ci	swqe->descriptors = 0;
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_ci	if (skb_is_gso(skb)) {
16118c2ecf20Sopenharmony_ci		swqe->tx_control |= EHEA_SWQE_TSO;
16128c2ecf20Sopenharmony_ci		swqe->mss = skb_shinfo(skb)->gso_size;
16138c2ecf20Sopenharmony_ci		/*
16148c2ecf20Sopenharmony_ci		 * For TSO packets we only copy the headers into the
16158c2ecf20Sopenharmony_ci		 * immediate area.
16168c2ecf20Sopenharmony_ci		 */
16178c2ecf20Sopenharmony_ci		immediate_len = ETH_HLEN + ip_hdrlen(skb) + tcp_hdrlen(skb);
16188c2ecf20Sopenharmony_ci	}
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_ci	if (skb_is_gso(skb) || skb_data_size >= SWQE2_MAX_IMM) {
16218c2ecf20Sopenharmony_ci		skb_copy_from_linear_data(skb, imm_data, immediate_len);
16228c2ecf20Sopenharmony_ci		swqe->immediate_data_length = immediate_len;
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_ci		if (skb_data_size > immediate_len) {
16258c2ecf20Sopenharmony_ci			sg1entry->l_key = lkey;
16268c2ecf20Sopenharmony_ci			sg1entry->len = skb_data_size - immediate_len;
16278c2ecf20Sopenharmony_ci			sg1entry->vaddr =
16288c2ecf20Sopenharmony_ci				ehea_map_vaddr(skb->data + immediate_len);
16298c2ecf20Sopenharmony_ci			swqe->descriptors++;
16308c2ecf20Sopenharmony_ci		}
16318c2ecf20Sopenharmony_ci	} else {
16328c2ecf20Sopenharmony_ci		skb_copy_from_linear_data(skb, imm_data, skb_data_size);
16338c2ecf20Sopenharmony_ci		swqe->immediate_data_length = skb_data_size;
16348c2ecf20Sopenharmony_ci	}
16358c2ecf20Sopenharmony_ci}
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_cistatic inline void write_swqe2_data(struct sk_buff *skb, struct net_device *dev,
16388c2ecf20Sopenharmony_ci				    struct ehea_swqe *swqe, u32 lkey)
16398c2ecf20Sopenharmony_ci{
16408c2ecf20Sopenharmony_ci	struct ehea_vsgentry *sg_list, *sg1entry, *sgentry;
16418c2ecf20Sopenharmony_ci	skb_frag_t *frag;
16428c2ecf20Sopenharmony_ci	int nfrags, sg1entry_contains_frag_data, i;
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_ci	nfrags = skb_shinfo(skb)->nr_frags;
16458c2ecf20Sopenharmony_ci	sg1entry = &swqe->u.immdata_desc.sg_entry;
16468c2ecf20Sopenharmony_ci	sg_list = (struct ehea_vsgentry *)&swqe->u.immdata_desc.sg_list;
16478c2ecf20Sopenharmony_ci	sg1entry_contains_frag_data = 0;
16488c2ecf20Sopenharmony_ci
16498c2ecf20Sopenharmony_ci	write_swqe2_immediate(skb, swqe, lkey);
16508c2ecf20Sopenharmony_ci
16518c2ecf20Sopenharmony_ci	/* write descriptors */
16528c2ecf20Sopenharmony_ci	if (nfrags > 0) {
16538c2ecf20Sopenharmony_ci		if (swqe->descriptors == 0) {
16548c2ecf20Sopenharmony_ci			/* sg1entry not yet used */
16558c2ecf20Sopenharmony_ci			frag = &skb_shinfo(skb)->frags[0];
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci			/* copy sg1entry data */
16588c2ecf20Sopenharmony_ci			sg1entry->l_key = lkey;
16598c2ecf20Sopenharmony_ci			sg1entry->len = skb_frag_size(frag);
16608c2ecf20Sopenharmony_ci			sg1entry->vaddr =
16618c2ecf20Sopenharmony_ci				ehea_map_vaddr(skb_frag_address(frag));
16628c2ecf20Sopenharmony_ci			swqe->descriptors++;
16638c2ecf20Sopenharmony_ci			sg1entry_contains_frag_data = 1;
16648c2ecf20Sopenharmony_ci		}
16658c2ecf20Sopenharmony_ci
16668c2ecf20Sopenharmony_ci		for (i = sg1entry_contains_frag_data; i < nfrags; i++) {
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ci			frag = &skb_shinfo(skb)->frags[i];
16698c2ecf20Sopenharmony_ci			sgentry = &sg_list[i - sg1entry_contains_frag_data];
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_ci			sgentry->l_key = lkey;
16728c2ecf20Sopenharmony_ci			sgentry->len = skb_frag_size(frag);
16738c2ecf20Sopenharmony_ci			sgentry->vaddr = ehea_map_vaddr(skb_frag_address(frag));
16748c2ecf20Sopenharmony_ci			swqe->descriptors++;
16758c2ecf20Sopenharmony_ci		}
16768c2ecf20Sopenharmony_ci	}
16778c2ecf20Sopenharmony_ci}
16788c2ecf20Sopenharmony_ci
16798c2ecf20Sopenharmony_cistatic int ehea_broadcast_reg_helper(struct ehea_port *port, u32 hcallid)
16808c2ecf20Sopenharmony_ci{
16818c2ecf20Sopenharmony_ci	int ret = 0;
16828c2ecf20Sopenharmony_ci	u64 hret;
16838c2ecf20Sopenharmony_ci	u8 reg_type;
16848c2ecf20Sopenharmony_ci
16858c2ecf20Sopenharmony_ci	/* De/Register untagged packets */
16868c2ecf20Sopenharmony_ci	reg_type = EHEA_BCMC_BROADCAST | EHEA_BCMC_UNTAGGED;
16878c2ecf20Sopenharmony_ci	hret = ehea_h_reg_dereg_bcmc(port->adapter->handle,
16888c2ecf20Sopenharmony_ci				     port->logical_port_id,
16898c2ecf20Sopenharmony_ci				     reg_type, port->mac_addr, 0, hcallid);
16908c2ecf20Sopenharmony_ci	if (hret != H_SUCCESS) {
16918c2ecf20Sopenharmony_ci		pr_err("%sregistering bc address failed (tagged)\n",
16928c2ecf20Sopenharmony_ci		       hcallid == H_REG_BCMC ? "" : "de");
16938c2ecf20Sopenharmony_ci		ret = -EIO;
16948c2ecf20Sopenharmony_ci		goto out_herr;
16958c2ecf20Sopenharmony_ci	}
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_ci	/* De/Register VLAN packets */
16988c2ecf20Sopenharmony_ci	reg_type = EHEA_BCMC_BROADCAST | EHEA_BCMC_VLANID_ALL;
16998c2ecf20Sopenharmony_ci	hret = ehea_h_reg_dereg_bcmc(port->adapter->handle,
17008c2ecf20Sopenharmony_ci				     port->logical_port_id,
17018c2ecf20Sopenharmony_ci				     reg_type, port->mac_addr, 0, hcallid);
17028c2ecf20Sopenharmony_ci	if (hret != H_SUCCESS) {
17038c2ecf20Sopenharmony_ci		pr_err("%sregistering bc address failed (vlan)\n",
17048c2ecf20Sopenharmony_ci		       hcallid == H_REG_BCMC ? "" : "de");
17058c2ecf20Sopenharmony_ci		ret = -EIO;
17068c2ecf20Sopenharmony_ci	}
17078c2ecf20Sopenharmony_ciout_herr:
17088c2ecf20Sopenharmony_ci	return ret;
17098c2ecf20Sopenharmony_ci}
17108c2ecf20Sopenharmony_ci
17118c2ecf20Sopenharmony_cistatic int ehea_set_mac_addr(struct net_device *dev, void *sa)
17128c2ecf20Sopenharmony_ci{
17138c2ecf20Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
17148c2ecf20Sopenharmony_ci	struct sockaddr *mac_addr = sa;
17158c2ecf20Sopenharmony_ci	struct hcp_ehea_port_cb0 *cb0;
17168c2ecf20Sopenharmony_ci	int ret;
17178c2ecf20Sopenharmony_ci	u64 hret;
17188c2ecf20Sopenharmony_ci
17198c2ecf20Sopenharmony_ci	if (!is_valid_ether_addr(mac_addr->sa_data)) {
17208c2ecf20Sopenharmony_ci		ret = -EADDRNOTAVAIL;
17218c2ecf20Sopenharmony_ci		goto out;
17228c2ecf20Sopenharmony_ci	}
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_ci	cb0 = (void *)get_zeroed_page(GFP_KERNEL);
17258c2ecf20Sopenharmony_ci	if (!cb0) {
17268c2ecf20Sopenharmony_ci		pr_err("no mem for cb0\n");
17278c2ecf20Sopenharmony_ci		ret = -ENOMEM;
17288c2ecf20Sopenharmony_ci		goto out;
17298c2ecf20Sopenharmony_ci	}
17308c2ecf20Sopenharmony_ci
17318c2ecf20Sopenharmony_ci	memcpy(&(cb0->port_mac_addr), &(mac_addr->sa_data[0]), ETH_ALEN);
17328c2ecf20Sopenharmony_ci
17338c2ecf20Sopenharmony_ci	cb0->port_mac_addr = cb0->port_mac_addr >> 16;
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_ci	hret = ehea_h_modify_ehea_port(port->adapter->handle,
17368c2ecf20Sopenharmony_ci				       port->logical_port_id, H_PORT_CB0,
17378c2ecf20Sopenharmony_ci				       EHEA_BMASK_SET(H_PORT_CB0_MAC, 1), cb0);
17388c2ecf20Sopenharmony_ci	if (hret != H_SUCCESS) {
17398c2ecf20Sopenharmony_ci		ret = -EIO;
17408c2ecf20Sopenharmony_ci		goto out_free;
17418c2ecf20Sopenharmony_ci	}
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ci	memcpy(dev->dev_addr, mac_addr->sa_data, dev->addr_len);
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci	/* Deregister old MAC in pHYP */
17468c2ecf20Sopenharmony_ci	if (port->state == EHEA_PORT_UP) {
17478c2ecf20Sopenharmony_ci		ret = ehea_broadcast_reg_helper(port, H_DEREG_BCMC);
17488c2ecf20Sopenharmony_ci		if (ret)
17498c2ecf20Sopenharmony_ci			goto out_upregs;
17508c2ecf20Sopenharmony_ci	}
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci	port->mac_addr = cb0->port_mac_addr << 16;
17538c2ecf20Sopenharmony_ci
17548c2ecf20Sopenharmony_ci	/* Register new MAC in pHYP */
17558c2ecf20Sopenharmony_ci	if (port->state == EHEA_PORT_UP) {
17568c2ecf20Sopenharmony_ci		ret = ehea_broadcast_reg_helper(port, H_REG_BCMC);
17578c2ecf20Sopenharmony_ci		if (ret)
17588c2ecf20Sopenharmony_ci			goto out_upregs;
17598c2ecf20Sopenharmony_ci	}
17608c2ecf20Sopenharmony_ci
17618c2ecf20Sopenharmony_ci	ret = 0;
17628c2ecf20Sopenharmony_ci
17638c2ecf20Sopenharmony_ciout_upregs:
17648c2ecf20Sopenharmony_ci	ehea_update_bcmc_registrations();
17658c2ecf20Sopenharmony_ciout_free:
17668c2ecf20Sopenharmony_ci	free_page((unsigned long)cb0);
17678c2ecf20Sopenharmony_ciout:
17688c2ecf20Sopenharmony_ci	return ret;
17698c2ecf20Sopenharmony_ci}
17708c2ecf20Sopenharmony_ci
17718c2ecf20Sopenharmony_cistatic void ehea_promiscuous_error(u64 hret, int enable)
17728c2ecf20Sopenharmony_ci{
17738c2ecf20Sopenharmony_ci	if (hret == H_AUTHORITY)
17748c2ecf20Sopenharmony_ci		pr_info("Hypervisor denied %sabling promiscuous mode\n",
17758c2ecf20Sopenharmony_ci			enable == 1 ? "en" : "dis");
17768c2ecf20Sopenharmony_ci	else
17778c2ecf20Sopenharmony_ci		pr_err("failed %sabling promiscuous mode\n",
17788c2ecf20Sopenharmony_ci		       enable == 1 ? "en" : "dis");
17798c2ecf20Sopenharmony_ci}
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_cistatic void ehea_promiscuous(struct net_device *dev, int enable)
17828c2ecf20Sopenharmony_ci{
17838c2ecf20Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
17848c2ecf20Sopenharmony_ci	struct hcp_ehea_port_cb7 *cb7;
17858c2ecf20Sopenharmony_ci	u64 hret;
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_ci	if (enable == port->promisc)
17888c2ecf20Sopenharmony_ci		return;
17898c2ecf20Sopenharmony_ci
17908c2ecf20Sopenharmony_ci	cb7 = (void *)get_zeroed_page(GFP_ATOMIC);
17918c2ecf20Sopenharmony_ci	if (!cb7) {
17928c2ecf20Sopenharmony_ci		pr_err("no mem for cb7\n");
17938c2ecf20Sopenharmony_ci		goto out;
17948c2ecf20Sopenharmony_ci	}
17958c2ecf20Sopenharmony_ci
17968c2ecf20Sopenharmony_ci	/* Modify Pxs_DUCQPN in CB7 */
17978c2ecf20Sopenharmony_ci	cb7->def_uc_qpn = enable == 1 ? port->port_res[0].qp->fw_handle : 0;
17988c2ecf20Sopenharmony_ci
17998c2ecf20Sopenharmony_ci	hret = ehea_h_modify_ehea_port(port->adapter->handle,
18008c2ecf20Sopenharmony_ci				       port->logical_port_id,
18018c2ecf20Sopenharmony_ci				       H_PORT_CB7, H_PORT_CB7_DUCQPN, cb7);
18028c2ecf20Sopenharmony_ci	if (hret) {
18038c2ecf20Sopenharmony_ci		ehea_promiscuous_error(hret, enable);
18048c2ecf20Sopenharmony_ci		goto out;
18058c2ecf20Sopenharmony_ci	}
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_ci	port->promisc = enable;
18088c2ecf20Sopenharmony_ciout:
18098c2ecf20Sopenharmony_ci	free_page((unsigned long)cb7);
18108c2ecf20Sopenharmony_ci}
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_cistatic u64 ehea_multicast_reg_helper(struct ehea_port *port, u64 mc_mac_addr,
18138c2ecf20Sopenharmony_ci				     u32 hcallid)
18148c2ecf20Sopenharmony_ci{
18158c2ecf20Sopenharmony_ci	u64 hret;
18168c2ecf20Sopenharmony_ci	u8 reg_type;
18178c2ecf20Sopenharmony_ci
18188c2ecf20Sopenharmony_ci	reg_type = EHEA_BCMC_MULTICAST | EHEA_BCMC_UNTAGGED;
18198c2ecf20Sopenharmony_ci	if (mc_mac_addr == 0)
18208c2ecf20Sopenharmony_ci		reg_type |= EHEA_BCMC_SCOPE_ALL;
18218c2ecf20Sopenharmony_ci
18228c2ecf20Sopenharmony_ci	hret = ehea_h_reg_dereg_bcmc(port->adapter->handle,
18238c2ecf20Sopenharmony_ci				     port->logical_port_id,
18248c2ecf20Sopenharmony_ci				     reg_type, mc_mac_addr, 0, hcallid);
18258c2ecf20Sopenharmony_ci	if (hret)
18268c2ecf20Sopenharmony_ci		goto out;
18278c2ecf20Sopenharmony_ci
18288c2ecf20Sopenharmony_ci	reg_type = EHEA_BCMC_MULTICAST | EHEA_BCMC_VLANID_ALL;
18298c2ecf20Sopenharmony_ci	if (mc_mac_addr == 0)
18308c2ecf20Sopenharmony_ci		reg_type |= EHEA_BCMC_SCOPE_ALL;
18318c2ecf20Sopenharmony_ci
18328c2ecf20Sopenharmony_ci	hret = ehea_h_reg_dereg_bcmc(port->adapter->handle,
18338c2ecf20Sopenharmony_ci				     port->logical_port_id,
18348c2ecf20Sopenharmony_ci				     reg_type, mc_mac_addr, 0, hcallid);
18358c2ecf20Sopenharmony_ciout:
18368c2ecf20Sopenharmony_ci	return hret;
18378c2ecf20Sopenharmony_ci}
18388c2ecf20Sopenharmony_ci
18398c2ecf20Sopenharmony_cistatic int ehea_drop_multicast_list(struct net_device *dev)
18408c2ecf20Sopenharmony_ci{
18418c2ecf20Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
18428c2ecf20Sopenharmony_ci	struct ehea_mc_list *mc_entry = port->mc_list;
18438c2ecf20Sopenharmony_ci	struct list_head *pos;
18448c2ecf20Sopenharmony_ci	struct list_head *temp;
18458c2ecf20Sopenharmony_ci	int ret = 0;
18468c2ecf20Sopenharmony_ci	u64 hret;
18478c2ecf20Sopenharmony_ci
18488c2ecf20Sopenharmony_ci	list_for_each_safe(pos, temp, &(port->mc_list->list)) {
18498c2ecf20Sopenharmony_ci		mc_entry = list_entry(pos, struct ehea_mc_list, list);
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ci		hret = ehea_multicast_reg_helper(port, mc_entry->macaddr,
18528c2ecf20Sopenharmony_ci						 H_DEREG_BCMC);
18538c2ecf20Sopenharmony_ci		if (hret) {
18548c2ecf20Sopenharmony_ci			pr_err("failed deregistering mcast MAC\n");
18558c2ecf20Sopenharmony_ci			ret = -EIO;
18568c2ecf20Sopenharmony_ci		}
18578c2ecf20Sopenharmony_ci
18588c2ecf20Sopenharmony_ci		list_del(pos);
18598c2ecf20Sopenharmony_ci		kfree(mc_entry);
18608c2ecf20Sopenharmony_ci	}
18618c2ecf20Sopenharmony_ci	return ret;
18628c2ecf20Sopenharmony_ci}
18638c2ecf20Sopenharmony_ci
18648c2ecf20Sopenharmony_cistatic void ehea_allmulti(struct net_device *dev, int enable)
18658c2ecf20Sopenharmony_ci{
18668c2ecf20Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
18678c2ecf20Sopenharmony_ci	u64 hret;
18688c2ecf20Sopenharmony_ci
18698c2ecf20Sopenharmony_ci	if (!port->allmulti) {
18708c2ecf20Sopenharmony_ci		if (enable) {
18718c2ecf20Sopenharmony_ci			/* Enable ALLMULTI */
18728c2ecf20Sopenharmony_ci			ehea_drop_multicast_list(dev);
18738c2ecf20Sopenharmony_ci			hret = ehea_multicast_reg_helper(port, 0, H_REG_BCMC);
18748c2ecf20Sopenharmony_ci			if (!hret)
18758c2ecf20Sopenharmony_ci				port->allmulti = 1;
18768c2ecf20Sopenharmony_ci			else
18778c2ecf20Sopenharmony_ci				netdev_err(dev,
18788c2ecf20Sopenharmony_ci					   "failed enabling IFF_ALLMULTI\n");
18798c2ecf20Sopenharmony_ci		}
18808c2ecf20Sopenharmony_ci	} else {
18818c2ecf20Sopenharmony_ci		if (!enable) {
18828c2ecf20Sopenharmony_ci			/* Disable ALLMULTI */
18838c2ecf20Sopenharmony_ci			hret = ehea_multicast_reg_helper(port, 0, H_DEREG_BCMC);
18848c2ecf20Sopenharmony_ci			if (!hret)
18858c2ecf20Sopenharmony_ci				port->allmulti = 0;
18868c2ecf20Sopenharmony_ci			else
18878c2ecf20Sopenharmony_ci				netdev_err(dev,
18888c2ecf20Sopenharmony_ci					   "failed disabling IFF_ALLMULTI\n");
18898c2ecf20Sopenharmony_ci		}
18908c2ecf20Sopenharmony_ci	}
18918c2ecf20Sopenharmony_ci}
18928c2ecf20Sopenharmony_ci
18938c2ecf20Sopenharmony_cistatic void ehea_add_multicast_entry(struct ehea_port *port, u8 *mc_mac_addr)
18948c2ecf20Sopenharmony_ci{
18958c2ecf20Sopenharmony_ci	struct ehea_mc_list *ehea_mcl_entry;
18968c2ecf20Sopenharmony_ci	u64 hret;
18978c2ecf20Sopenharmony_ci
18988c2ecf20Sopenharmony_ci	ehea_mcl_entry = kzalloc(sizeof(*ehea_mcl_entry), GFP_ATOMIC);
18998c2ecf20Sopenharmony_ci	if (!ehea_mcl_entry)
19008c2ecf20Sopenharmony_ci		return;
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ehea_mcl_entry->list);
19038c2ecf20Sopenharmony_ci
19048c2ecf20Sopenharmony_ci	memcpy(&ehea_mcl_entry->macaddr, mc_mac_addr, ETH_ALEN);
19058c2ecf20Sopenharmony_ci
19068c2ecf20Sopenharmony_ci	hret = ehea_multicast_reg_helper(port, ehea_mcl_entry->macaddr,
19078c2ecf20Sopenharmony_ci					 H_REG_BCMC);
19088c2ecf20Sopenharmony_ci	if (!hret)
19098c2ecf20Sopenharmony_ci		list_add(&ehea_mcl_entry->list, &port->mc_list->list);
19108c2ecf20Sopenharmony_ci	else {
19118c2ecf20Sopenharmony_ci		pr_err("failed registering mcast MAC\n");
19128c2ecf20Sopenharmony_ci		kfree(ehea_mcl_entry);
19138c2ecf20Sopenharmony_ci	}
19148c2ecf20Sopenharmony_ci}
19158c2ecf20Sopenharmony_ci
19168c2ecf20Sopenharmony_cistatic void ehea_set_multicast_list(struct net_device *dev)
19178c2ecf20Sopenharmony_ci{
19188c2ecf20Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
19198c2ecf20Sopenharmony_ci	struct netdev_hw_addr *ha;
19208c2ecf20Sopenharmony_ci	int ret;
19218c2ecf20Sopenharmony_ci
19228c2ecf20Sopenharmony_ci	ehea_promiscuous(dev, !!(dev->flags & IFF_PROMISC));
19238c2ecf20Sopenharmony_ci
19248c2ecf20Sopenharmony_ci	if (dev->flags & IFF_ALLMULTI) {
19258c2ecf20Sopenharmony_ci		ehea_allmulti(dev, 1);
19268c2ecf20Sopenharmony_ci		goto out;
19278c2ecf20Sopenharmony_ci	}
19288c2ecf20Sopenharmony_ci	ehea_allmulti(dev, 0);
19298c2ecf20Sopenharmony_ci
19308c2ecf20Sopenharmony_ci	if (!netdev_mc_empty(dev)) {
19318c2ecf20Sopenharmony_ci		ret = ehea_drop_multicast_list(dev);
19328c2ecf20Sopenharmony_ci		if (ret) {
19338c2ecf20Sopenharmony_ci			/* Dropping the current multicast list failed.
19348c2ecf20Sopenharmony_ci			 * Enabling ALL_MULTI is the best we can do.
19358c2ecf20Sopenharmony_ci			 */
19368c2ecf20Sopenharmony_ci			ehea_allmulti(dev, 1);
19378c2ecf20Sopenharmony_ci		}
19388c2ecf20Sopenharmony_ci
19398c2ecf20Sopenharmony_ci		if (netdev_mc_count(dev) > port->adapter->max_mc_mac) {
19408c2ecf20Sopenharmony_ci			pr_info("Mcast registration limit reached (0x%llx). Use ALLMULTI!\n",
19418c2ecf20Sopenharmony_ci				port->adapter->max_mc_mac);
19428c2ecf20Sopenharmony_ci			goto out;
19438c2ecf20Sopenharmony_ci		}
19448c2ecf20Sopenharmony_ci
19458c2ecf20Sopenharmony_ci		netdev_for_each_mc_addr(ha, dev)
19468c2ecf20Sopenharmony_ci			ehea_add_multicast_entry(port, ha->addr);
19478c2ecf20Sopenharmony_ci
19488c2ecf20Sopenharmony_ci	}
19498c2ecf20Sopenharmony_ciout:
19508c2ecf20Sopenharmony_ci	ehea_update_bcmc_registrations();
19518c2ecf20Sopenharmony_ci}
19528c2ecf20Sopenharmony_ci
19538c2ecf20Sopenharmony_cistatic void xmit_common(struct sk_buff *skb, struct ehea_swqe *swqe)
19548c2ecf20Sopenharmony_ci{
19558c2ecf20Sopenharmony_ci	swqe->tx_control |= EHEA_SWQE_IMM_DATA_PRESENT | EHEA_SWQE_CRC;
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_ci	if (vlan_get_protocol(skb) != htons(ETH_P_IP))
19588c2ecf20Sopenharmony_ci		return;
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_ci	if (skb->ip_summed == CHECKSUM_PARTIAL)
19618c2ecf20Sopenharmony_ci		swqe->tx_control |= EHEA_SWQE_IP_CHECKSUM;
19628c2ecf20Sopenharmony_ci
19638c2ecf20Sopenharmony_ci	swqe->ip_start = skb_network_offset(skb);
19648c2ecf20Sopenharmony_ci	swqe->ip_end = swqe->ip_start + ip_hdrlen(skb) - 1;
19658c2ecf20Sopenharmony_ci
19668c2ecf20Sopenharmony_ci	switch (ip_hdr(skb)->protocol) {
19678c2ecf20Sopenharmony_ci	case IPPROTO_UDP:
19688c2ecf20Sopenharmony_ci		if (skb->ip_summed == CHECKSUM_PARTIAL)
19698c2ecf20Sopenharmony_ci			swqe->tx_control |= EHEA_SWQE_TCP_CHECKSUM;
19708c2ecf20Sopenharmony_ci
19718c2ecf20Sopenharmony_ci		swqe->tcp_offset = swqe->ip_end + 1 +
19728c2ecf20Sopenharmony_ci				   offsetof(struct udphdr, check);
19738c2ecf20Sopenharmony_ci		break;
19748c2ecf20Sopenharmony_ci
19758c2ecf20Sopenharmony_ci	case IPPROTO_TCP:
19768c2ecf20Sopenharmony_ci		if (skb->ip_summed == CHECKSUM_PARTIAL)
19778c2ecf20Sopenharmony_ci			swqe->tx_control |= EHEA_SWQE_TCP_CHECKSUM;
19788c2ecf20Sopenharmony_ci
19798c2ecf20Sopenharmony_ci		swqe->tcp_offset = swqe->ip_end + 1 +
19808c2ecf20Sopenharmony_ci				   offsetof(struct tcphdr, check);
19818c2ecf20Sopenharmony_ci		break;
19828c2ecf20Sopenharmony_ci	}
19838c2ecf20Sopenharmony_ci}
19848c2ecf20Sopenharmony_ci
19858c2ecf20Sopenharmony_cistatic void ehea_xmit2(struct sk_buff *skb, struct net_device *dev,
19868c2ecf20Sopenharmony_ci		       struct ehea_swqe *swqe, u32 lkey)
19878c2ecf20Sopenharmony_ci{
19888c2ecf20Sopenharmony_ci	swqe->tx_control |= EHEA_SWQE_DESCRIPTORS_PRESENT;
19898c2ecf20Sopenharmony_ci
19908c2ecf20Sopenharmony_ci	xmit_common(skb, swqe);
19918c2ecf20Sopenharmony_ci
19928c2ecf20Sopenharmony_ci	write_swqe2_data(skb, dev, swqe, lkey);
19938c2ecf20Sopenharmony_ci}
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_cistatic void ehea_xmit3(struct sk_buff *skb, struct net_device *dev,
19968c2ecf20Sopenharmony_ci		       struct ehea_swqe *swqe)
19978c2ecf20Sopenharmony_ci{
19988c2ecf20Sopenharmony_ci	u8 *imm_data = &swqe->u.immdata_nodesc.immediate_data[0];
19998c2ecf20Sopenharmony_ci
20008c2ecf20Sopenharmony_ci	xmit_common(skb, swqe);
20018c2ecf20Sopenharmony_ci
20028c2ecf20Sopenharmony_ci	if (!skb->data_len)
20038c2ecf20Sopenharmony_ci		skb_copy_from_linear_data(skb, imm_data, skb->len);
20048c2ecf20Sopenharmony_ci	else
20058c2ecf20Sopenharmony_ci		skb_copy_bits(skb, 0, imm_data, skb->len);
20068c2ecf20Sopenharmony_ci
20078c2ecf20Sopenharmony_ci	swqe->immediate_data_length = skb->len;
20088c2ecf20Sopenharmony_ci	dev_consume_skb_any(skb);
20098c2ecf20Sopenharmony_ci}
20108c2ecf20Sopenharmony_ci
20118c2ecf20Sopenharmony_cistatic netdev_tx_t ehea_start_xmit(struct sk_buff *skb, struct net_device *dev)
20128c2ecf20Sopenharmony_ci{
20138c2ecf20Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
20148c2ecf20Sopenharmony_ci	struct ehea_swqe *swqe;
20158c2ecf20Sopenharmony_ci	u32 lkey;
20168c2ecf20Sopenharmony_ci	int swqe_index;
20178c2ecf20Sopenharmony_ci	struct ehea_port_res *pr;
20188c2ecf20Sopenharmony_ci	struct netdev_queue *txq;
20198c2ecf20Sopenharmony_ci
20208c2ecf20Sopenharmony_ci	pr = &port->port_res[skb_get_queue_mapping(skb)];
20218c2ecf20Sopenharmony_ci	txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb));
20228c2ecf20Sopenharmony_ci
20238c2ecf20Sopenharmony_ci	swqe = ehea_get_swqe(pr->qp, &swqe_index);
20248c2ecf20Sopenharmony_ci	memset(swqe, 0, SWQE_HEADER_SIZE);
20258c2ecf20Sopenharmony_ci	atomic_dec(&pr->swqe_avail);
20268c2ecf20Sopenharmony_ci
20278c2ecf20Sopenharmony_ci	if (skb_vlan_tag_present(skb)) {
20288c2ecf20Sopenharmony_ci		swqe->tx_control |= EHEA_SWQE_VLAN_INSERT;
20298c2ecf20Sopenharmony_ci		swqe->vlan_tag = skb_vlan_tag_get(skb);
20308c2ecf20Sopenharmony_ci	}
20318c2ecf20Sopenharmony_ci
20328c2ecf20Sopenharmony_ci	pr->tx_packets++;
20338c2ecf20Sopenharmony_ci	pr->tx_bytes += skb->len;
20348c2ecf20Sopenharmony_ci
20358c2ecf20Sopenharmony_ci	if (skb->len <= SWQE3_MAX_IMM) {
20368c2ecf20Sopenharmony_ci		u32 sig_iv = port->sig_comp_iv;
20378c2ecf20Sopenharmony_ci		u32 swqe_num = pr->swqe_id_counter;
20388c2ecf20Sopenharmony_ci		ehea_xmit3(skb, dev, swqe);
20398c2ecf20Sopenharmony_ci		swqe->wr_id = EHEA_BMASK_SET(EHEA_WR_ID_TYPE, EHEA_SWQE3_TYPE)
20408c2ecf20Sopenharmony_ci			| EHEA_BMASK_SET(EHEA_WR_ID_COUNT, swqe_num);
20418c2ecf20Sopenharmony_ci		if (pr->swqe_ll_count >= (sig_iv - 1)) {
20428c2ecf20Sopenharmony_ci			swqe->wr_id |= EHEA_BMASK_SET(EHEA_WR_ID_REFILL,
20438c2ecf20Sopenharmony_ci						      sig_iv);
20448c2ecf20Sopenharmony_ci			swqe->tx_control |= EHEA_SWQE_SIGNALLED_COMPLETION;
20458c2ecf20Sopenharmony_ci			pr->swqe_ll_count = 0;
20468c2ecf20Sopenharmony_ci		} else
20478c2ecf20Sopenharmony_ci			pr->swqe_ll_count += 1;
20488c2ecf20Sopenharmony_ci	} else {
20498c2ecf20Sopenharmony_ci		swqe->wr_id =
20508c2ecf20Sopenharmony_ci			EHEA_BMASK_SET(EHEA_WR_ID_TYPE, EHEA_SWQE2_TYPE)
20518c2ecf20Sopenharmony_ci		      | EHEA_BMASK_SET(EHEA_WR_ID_COUNT, pr->swqe_id_counter)
20528c2ecf20Sopenharmony_ci		      | EHEA_BMASK_SET(EHEA_WR_ID_REFILL, 1)
20538c2ecf20Sopenharmony_ci		      | EHEA_BMASK_SET(EHEA_WR_ID_INDEX, pr->sq_skba.index);
20548c2ecf20Sopenharmony_ci		pr->sq_skba.arr[pr->sq_skba.index] = skb;
20558c2ecf20Sopenharmony_ci
20568c2ecf20Sopenharmony_ci		pr->sq_skba.index++;
20578c2ecf20Sopenharmony_ci		pr->sq_skba.index &= (pr->sq_skba.len - 1);
20588c2ecf20Sopenharmony_ci
20598c2ecf20Sopenharmony_ci		lkey = pr->send_mr.lkey;
20608c2ecf20Sopenharmony_ci		ehea_xmit2(skb, dev, swqe, lkey);
20618c2ecf20Sopenharmony_ci		swqe->tx_control |= EHEA_SWQE_SIGNALLED_COMPLETION;
20628c2ecf20Sopenharmony_ci	}
20638c2ecf20Sopenharmony_ci	pr->swqe_id_counter += 1;
20648c2ecf20Sopenharmony_ci
20658c2ecf20Sopenharmony_ci	netif_info(port, tx_queued, dev,
20668c2ecf20Sopenharmony_ci		   "post swqe on QP %d\n", pr->qp->init_attr.qp_nr);
20678c2ecf20Sopenharmony_ci	if (netif_msg_tx_queued(port))
20688c2ecf20Sopenharmony_ci		ehea_dump(swqe, 512, "swqe");
20698c2ecf20Sopenharmony_ci
20708c2ecf20Sopenharmony_ci	if (unlikely(test_bit(__EHEA_STOP_XFER, &ehea_driver_flags))) {
20718c2ecf20Sopenharmony_ci		netif_tx_stop_queue(txq);
20728c2ecf20Sopenharmony_ci		swqe->tx_control |= EHEA_SWQE_PURGE;
20738c2ecf20Sopenharmony_ci	}
20748c2ecf20Sopenharmony_ci
20758c2ecf20Sopenharmony_ci	ehea_post_swqe(pr->qp, swqe);
20768c2ecf20Sopenharmony_ci
20778c2ecf20Sopenharmony_ci	if (unlikely(atomic_read(&pr->swqe_avail) <= 1)) {
20788c2ecf20Sopenharmony_ci		pr->p_stats.queue_stopped++;
20798c2ecf20Sopenharmony_ci		netif_tx_stop_queue(txq);
20808c2ecf20Sopenharmony_ci	}
20818c2ecf20Sopenharmony_ci
20828c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
20838c2ecf20Sopenharmony_ci}
20848c2ecf20Sopenharmony_ci
20858c2ecf20Sopenharmony_cistatic int ehea_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
20868c2ecf20Sopenharmony_ci{
20878c2ecf20Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
20888c2ecf20Sopenharmony_ci	struct ehea_adapter *adapter = port->adapter;
20898c2ecf20Sopenharmony_ci	struct hcp_ehea_port_cb1 *cb1;
20908c2ecf20Sopenharmony_ci	int index;
20918c2ecf20Sopenharmony_ci	u64 hret;
20928c2ecf20Sopenharmony_ci	int err = 0;
20938c2ecf20Sopenharmony_ci
20948c2ecf20Sopenharmony_ci	cb1 = (void *)get_zeroed_page(GFP_KERNEL);
20958c2ecf20Sopenharmony_ci	if (!cb1) {
20968c2ecf20Sopenharmony_ci		pr_err("no mem for cb1\n");
20978c2ecf20Sopenharmony_ci		err = -ENOMEM;
20988c2ecf20Sopenharmony_ci		goto out;
20998c2ecf20Sopenharmony_ci	}
21008c2ecf20Sopenharmony_ci
21018c2ecf20Sopenharmony_ci	hret = ehea_h_query_ehea_port(adapter->handle, port->logical_port_id,
21028c2ecf20Sopenharmony_ci				      H_PORT_CB1, H_PORT_CB1_ALL, cb1);
21038c2ecf20Sopenharmony_ci	if (hret != H_SUCCESS) {
21048c2ecf20Sopenharmony_ci		pr_err("query_ehea_port failed\n");
21058c2ecf20Sopenharmony_ci		err = -EINVAL;
21068c2ecf20Sopenharmony_ci		goto out;
21078c2ecf20Sopenharmony_ci	}
21088c2ecf20Sopenharmony_ci
21098c2ecf20Sopenharmony_ci	index = (vid / 64);
21108c2ecf20Sopenharmony_ci	cb1->vlan_filter[index] |= ((u64)(0x8000000000000000 >> (vid & 0x3F)));
21118c2ecf20Sopenharmony_ci
21128c2ecf20Sopenharmony_ci	hret = ehea_h_modify_ehea_port(adapter->handle, port->logical_port_id,
21138c2ecf20Sopenharmony_ci				       H_PORT_CB1, H_PORT_CB1_ALL, cb1);
21148c2ecf20Sopenharmony_ci	if (hret != H_SUCCESS) {
21158c2ecf20Sopenharmony_ci		pr_err("modify_ehea_port failed\n");
21168c2ecf20Sopenharmony_ci		err = -EINVAL;
21178c2ecf20Sopenharmony_ci	}
21188c2ecf20Sopenharmony_ciout:
21198c2ecf20Sopenharmony_ci	free_page((unsigned long)cb1);
21208c2ecf20Sopenharmony_ci	return err;
21218c2ecf20Sopenharmony_ci}
21228c2ecf20Sopenharmony_ci
21238c2ecf20Sopenharmony_cistatic int ehea_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
21248c2ecf20Sopenharmony_ci{
21258c2ecf20Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
21268c2ecf20Sopenharmony_ci	struct ehea_adapter *adapter = port->adapter;
21278c2ecf20Sopenharmony_ci	struct hcp_ehea_port_cb1 *cb1;
21288c2ecf20Sopenharmony_ci	int index;
21298c2ecf20Sopenharmony_ci	u64 hret;
21308c2ecf20Sopenharmony_ci	int err = 0;
21318c2ecf20Sopenharmony_ci
21328c2ecf20Sopenharmony_ci	cb1 = (void *)get_zeroed_page(GFP_KERNEL);
21338c2ecf20Sopenharmony_ci	if (!cb1) {
21348c2ecf20Sopenharmony_ci		pr_err("no mem for cb1\n");
21358c2ecf20Sopenharmony_ci		err = -ENOMEM;
21368c2ecf20Sopenharmony_ci		goto out;
21378c2ecf20Sopenharmony_ci	}
21388c2ecf20Sopenharmony_ci
21398c2ecf20Sopenharmony_ci	hret = ehea_h_query_ehea_port(adapter->handle, port->logical_port_id,
21408c2ecf20Sopenharmony_ci				      H_PORT_CB1, H_PORT_CB1_ALL, cb1);
21418c2ecf20Sopenharmony_ci	if (hret != H_SUCCESS) {
21428c2ecf20Sopenharmony_ci		pr_err("query_ehea_port failed\n");
21438c2ecf20Sopenharmony_ci		err = -EINVAL;
21448c2ecf20Sopenharmony_ci		goto out;
21458c2ecf20Sopenharmony_ci	}
21468c2ecf20Sopenharmony_ci
21478c2ecf20Sopenharmony_ci	index = (vid / 64);
21488c2ecf20Sopenharmony_ci	cb1->vlan_filter[index] &= ~((u64)(0x8000000000000000 >> (vid & 0x3F)));
21498c2ecf20Sopenharmony_ci
21508c2ecf20Sopenharmony_ci	hret = ehea_h_modify_ehea_port(adapter->handle, port->logical_port_id,
21518c2ecf20Sopenharmony_ci				       H_PORT_CB1, H_PORT_CB1_ALL, cb1);
21528c2ecf20Sopenharmony_ci	if (hret != H_SUCCESS) {
21538c2ecf20Sopenharmony_ci		pr_err("modify_ehea_port failed\n");
21548c2ecf20Sopenharmony_ci		err = -EINVAL;
21558c2ecf20Sopenharmony_ci	}
21568c2ecf20Sopenharmony_ciout:
21578c2ecf20Sopenharmony_ci	free_page((unsigned long)cb1);
21588c2ecf20Sopenharmony_ci	return err;
21598c2ecf20Sopenharmony_ci}
21608c2ecf20Sopenharmony_ci
21618c2ecf20Sopenharmony_cistatic int ehea_activate_qp(struct ehea_adapter *adapter, struct ehea_qp *qp)
21628c2ecf20Sopenharmony_ci{
21638c2ecf20Sopenharmony_ci	int ret = -EIO;
21648c2ecf20Sopenharmony_ci	u64 hret;
21658c2ecf20Sopenharmony_ci	u16 dummy16 = 0;
21668c2ecf20Sopenharmony_ci	u64 dummy64 = 0;
21678c2ecf20Sopenharmony_ci	struct hcp_modify_qp_cb0 *cb0;
21688c2ecf20Sopenharmony_ci
21698c2ecf20Sopenharmony_ci	cb0 = (void *)get_zeroed_page(GFP_KERNEL);
21708c2ecf20Sopenharmony_ci	if (!cb0) {
21718c2ecf20Sopenharmony_ci		ret = -ENOMEM;
21728c2ecf20Sopenharmony_ci		goto out;
21738c2ecf20Sopenharmony_ci	}
21748c2ecf20Sopenharmony_ci
21758c2ecf20Sopenharmony_ci	hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
21768c2ecf20Sopenharmony_ci				    EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF), cb0);
21778c2ecf20Sopenharmony_ci	if (hret != H_SUCCESS) {
21788c2ecf20Sopenharmony_ci		pr_err("query_ehea_qp failed (1)\n");
21798c2ecf20Sopenharmony_ci		goto out;
21808c2ecf20Sopenharmony_ci	}
21818c2ecf20Sopenharmony_ci
21828c2ecf20Sopenharmony_ci	cb0->qp_ctl_reg = H_QP_CR_STATE_INITIALIZED;
21838c2ecf20Sopenharmony_ci	hret = ehea_h_modify_ehea_qp(adapter->handle, 0, qp->fw_handle,
21848c2ecf20Sopenharmony_ci				     EHEA_BMASK_SET(H_QPCB0_QP_CTL_REG, 1), cb0,
21858c2ecf20Sopenharmony_ci				     &dummy64, &dummy64, &dummy16, &dummy16);
21868c2ecf20Sopenharmony_ci	if (hret != H_SUCCESS) {
21878c2ecf20Sopenharmony_ci		pr_err("modify_ehea_qp failed (1)\n");
21888c2ecf20Sopenharmony_ci		goto out;
21898c2ecf20Sopenharmony_ci	}
21908c2ecf20Sopenharmony_ci
21918c2ecf20Sopenharmony_ci	hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
21928c2ecf20Sopenharmony_ci				    EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF), cb0);
21938c2ecf20Sopenharmony_ci	if (hret != H_SUCCESS) {
21948c2ecf20Sopenharmony_ci		pr_err("query_ehea_qp failed (2)\n");
21958c2ecf20Sopenharmony_ci		goto out;
21968c2ecf20Sopenharmony_ci	}
21978c2ecf20Sopenharmony_ci
21988c2ecf20Sopenharmony_ci	cb0->qp_ctl_reg = H_QP_CR_ENABLED | H_QP_CR_STATE_INITIALIZED;
21998c2ecf20Sopenharmony_ci	hret = ehea_h_modify_ehea_qp(adapter->handle, 0, qp->fw_handle,
22008c2ecf20Sopenharmony_ci				     EHEA_BMASK_SET(H_QPCB0_QP_CTL_REG, 1), cb0,
22018c2ecf20Sopenharmony_ci				     &dummy64, &dummy64, &dummy16, &dummy16);
22028c2ecf20Sopenharmony_ci	if (hret != H_SUCCESS) {
22038c2ecf20Sopenharmony_ci		pr_err("modify_ehea_qp failed (2)\n");
22048c2ecf20Sopenharmony_ci		goto out;
22058c2ecf20Sopenharmony_ci	}
22068c2ecf20Sopenharmony_ci
22078c2ecf20Sopenharmony_ci	hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
22088c2ecf20Sopenharmony_ci				    EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF), cb0);
22098c2ecf20Sopenharmony_ci	if (hret != H_SUCCESS) {
22108c2ecf20Sopenharmony_ci		pr_err("query_ehea_qp failed (3)\n");
22118c2ecf20Sopenharmony_ci		goto out;
22128c2ecf20Sopenharmony_ci	}
22138c2ecf20Sopenharmony_ci
22148c2ecf20Sopenharmony_ci	cb0->qp_ctl_reg = H_QP_CR_ENABLED | H_QP_CR_STATE_RDY2SND;
22158c2ecf20Sopenharmony_ci	hret = ehea_h_modify_ehea_qp(adapter->handle, 0, qp->fw_handle,
22168c2ecf20Sopenharmony_ci				     EHEA_BMASK_SET(H_QPCB0_QP_CTL_REG, 1), cb0,
22178c2ecf20Sopenharmony_ci				     &dummy64, &dummy64, &dummy16, &dummy16);
22188c2ecf20Sopenharmony_ci	if (hret != H_SUCCESS) {
22198c2ecf20Sopenharmony_ci		pr_err("modify_ehea_qp failed (3)\n");
22208c2ecf20Sopenharmony_ci		goto out;
22218c2ecf20Sopenharmony_ci	}
22228c2ecf20Sopenharmony_ci
22238c2ecf20Sopenharmony_ci	hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
22248c2ecf20Sopenharmony_ci				    EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF), cb0);
22258c2ecf20Sopenharmony_ci	if (hret != H_SUCCESS) {
22268c2ecf20Sopenharmony_ci		pr_err("query_ehea_qp failed (4)\n");
22278c2ecf20Sopenharmony_ci		goto out;
22288c2ecf20Sopenharmony_ci	}
22298c2ecf20Sopenharmony_ci
22308c2ecf20Sopenharmony_ci	ret = 0;
22318c2ecf20Sopenharmony_ciout:
22328c2ecf20Sopenharmony_ci	free_page((unsigned long)cb0);
22338c2ecf20Sopenharmony_ci	return ret;
22348c2ecf20Sopenharmony_ci}
22358c2ecf20Sopenharmony_ci
22368c2ecf20Sopenharmony_cistatic int ehea_port_res_setup(struct ehea_port *port, int def_qps)
22378c2ecf20Sopenharmony_ci{
22388c2ecf20Sopenharmony_ci	int ret, i;
22398c2ecf20Sopenharmony_ci	struct port_res_cfg pr_cfg, pr_cfg_small_rx;
22408c2ecf20Sopenharmony_ci	enum ehea_eq_type eq_type = EHEA_EQ;
22418c2ecf20Sopenharmony_ci
22428c2ecf20Sopenharmony_ci	port->qp_eq = ehea_create_eq(port->adapter, eq_type,
22438c2ecf20Sopenharmony_ci				   EHEA_MAX_ENTRIES_EQ, 1);
22448c2ecf20Sopenharmony_ci	if (!port->qp_eq) {
22458c2ecf20Sopenharmony_ci		ret = -EINVAL;
22468c2ecf20Sopenharmony_ci		pr_err("ehea_create_eq failed (qp_eq)\n");
22478c2ecf20Sopenharmony_ci		goto out_kill_eq;
22488c2ecf20Sopenharmony_ci	}
22498c2ecf20Sopenharmony_ci
22508c2ecf20Sopenharmony_ci	pr_cfg.max_entries_rcq = rq1_entries + rq2_entries + rq3_entries;
22518c2ecf20Sopenharmony_ci	pr_cfg.max_entries_scq = sq_entries * 2;
22528c2ecf20Sopenharmony_ci	pr_cfg.max_entries_sq = sq_entries;
22538c2ecf20Sopenharmony_ci	pr_cfg.max_entries_rq1 = rq1_entries;
22548c2ecf20Sopenharmony_ci	pr_cfg.max_entries_rq2 = rq2_entries;
22558c2ecf20Sopenharmony_ci	pr_cfg.max_entries_rq3 = rq3_entries;
22568c2ecf20Sopenharmony_ci
22578c2ecf20Sopenharmony_ci	pr_cfg_small_rx.max_entries_rcq = 1;
22588c2ecf20Sopenharmony_ci	pr_cfg_small_rx.max_entries_scq = sq_entries;
22598c2ecf20Sopenharmony_ci	pr_cfg_small_rx.max_entries_sq = sq_entries;
22608c2ecf20Sopenharmony_ci	pr_cfg_small_rx.max_entries_rq1 = 1;
22618c2ecf20Sopenharmony_ci	pr_cfg_small_rx.max_entries_rq2 = 1;
22628c2ecf20Sopenharmony_ci	pr_cfg_small_rx.max_entries_rq3 = 1;
22638c2ecf20Sopenharmony_ci
22648c2ecf20Sopenharmony_ci	for (i = 0; i < def_qps; i++) {
22658c2ecf20Sopenharmony_ci		ret = ehea_init_port_res(port, &port->port_res[i], &pr_cfg, i);
22668c2ecf20Sopenharmony_ci		if (ret)
22678c2ecf20Sopenharmony_ci			goto out_clean_pr;
22688c2ecf20Sopenharmony_ci	}
22698c2ecf20Sopenharmony_ci	for (i = def_qps; i < def_qps; i++) {
22708c2ecf20Sopenharmony_ci		ret = ehea_init_port_res(port, &port->port_res[i],
22718c2ecf20Sopenharmony_ci					 &pr_cfg_small_rx, i);
22728c2ecf20Sopenharmony_ci		if (ret)
22738c2ecf20Sopenharmony_ci			goto out_clean_pr;
22748c2ecf20Sopenharmony_ci	}
22758c2ecf20Sopenharmony_ci
22768c2ecf20Sopenharmony_ci	return 0;
22778c2ecf20Sopenharmony_ci
22788c2ecf20Sopenharmony_ciout_clean_pr:
22798c2ecf20Sopenharmony_ci	while (--i >= 0)
22808c2ecf20Sopenharmony_ci		ehea_clean_portres(port, &port->port_res[i]);
22818c2ecf20Sopenharmony_ci
22828c2ecf20Sopenharmony_ciout_kill_eq:
22838c2ecf20Sopenharmony_ci	ehea_destroy_eq(port->qp_eq);
22848c2ecf20Sopenharmony_ci	return ret;
22858c2ecf20Sopenharmony_ci}
22868c2ecf20Sopenharmony_ci
22878c2ecf20Sopenharmony_cistatic int ehea_clean_all_portres(struct ehea_port *port)
22888c2ecf20Sopenharmony_ci{
22898c2ecf20Sopenharmony_ci	int ret = 0;
22908c2ecf20Sopenharmony_ci	int i;
22918c2ecf20Sopenharmony_ci
22928c2ecf20Sopenharmony_ci	for (i = 0; i < port->num_def_qps; i++)
22938c2ecf20Sopenharmony_ci		ret |= ehea_clean_portres(port, &port->port_res[i]);
22948c2ecf20Sopenharmony_ci
22958c2ecf20Sopenharmony_ci	ret |= ehea_destroy_eq(port->qp_eq);
22968c2ecf20Sopenharmony_ci
22978c2ecf20Sopenharmony_ci	return ret;
22988c2ecf20Sopenharmony_ci}
22998c2ecf20Sopenharmony_ci
23008c2ecf20Sopenharmony_cistatic void ehea_remove_adapter_mr(struct ehea_adapter *adapter)
23018c2ecf20Sopenharmony_ci{
23028c2ecf20Sopenharmony_ci	if (adapter->active_ports)
23038c2ecf20Sopenharmony_ci		return;
23048c2ecf20Sopenharmony_ci
23058c2ecf20Sopenharmony_ci	ehea_rem_mr(&adapter->mr);
23068c2ecf20Sopenharmony_ci}
23078c2ecf20Sopenharmony_ci
23088c2ecf20Sopenharmony_cistatic int ehea_add_adapter_mr(struct ehea_adapter *adapter)
23098c2ecf20Sopenharmony_ci{
23108c2ecf20Sopenharmony_ci	if (adapter->active_ports)
23118c2ecf20Sopenharmony_ci		return 0;
23128c2ecf20Sopenharmony_ci
23138c2ecf20Sopenharmony_ci	return ehea_reg_kernel_mr(adapter, &adapter->mr);
23148c2ecf20Sopenharmony_ci}
23158c2ecf20Sopenharmony_ci
23168c2ecf20Sopenharmony_cistatic int ehea_up(struct net_device *dev)
23178c2ecf20Sopenharmony_ci{
23188c2ecf20Sopenharmony_ci	int ret, i;
23198c2ecf20Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
23208c2ecf20Sopenharmony_ci
23218c2ecf20Sopenharmony_ci	if (port->state == EHEA_PORT_UP)
23228c2ecf20Sopenharmony_ci		return 0;
23238c2ecf20Sopenharmony_ci
23248c2ecf20Sopenharmony_ci	ret = ehea_port_res_setup(port, port->num_def_qps);
23258c2ecf20Sopenharmony_ci	if (ret) {
23268c2ecf20Sopenharmony_ci		netdev_err(dev, "port_res_failed\n");
23278c2ecf20Sopenharmony_ci		goto out;
23288c2ecf20Sopenharmony_ci	}
23298c2ecf20Sopenharmony_ci
23308c2ecf20Sopenharmony_ci	/* Set default QP for this port */
23318c2ecf20Sopenharmony_ci	ret = ehea_configure_port(port);
23328c2ecf20Sopenharmony_ci	if (ret) {
23338c2ecf20Sopenharmony_ci		netdev_err(dev, "ehea_configure_port failed. ret:%d\n", ret);
23348c2ecf20Sopenharmony_ci		goto out_clean_pr;
23358c2ecf20Sopenharmony_ci	}
23368c2ecf20Sopenharmony_ci
23378c2ecf20Sopenharmony_ci	ret = ehea_reg_interrupts(dev);
23388c2ecf20Sopenharmony_ci	if (ret) {
23398c2ecf20Sopenharmony_ci		netdev_err(dev, "reg_interrupts failed. ret:%d\n", ret);
23408c2ecf20Sopenharmony_ci		goto out_clean_pr;
23418c2ecf20Sopenharmony_ci	}
23428c2ecf20Sopenharmony_ci
23438c2ecf20Sopenharmony_ci	for (i = 0; i < port->num_def_qps; i++) {
23448c2ecf20Sopenharmony_ci		ret = ehea_activate_qp(port->adapter, port->port_res[i].qp);
23458c2ecf20Sopenharmony_ci		if (ret) {
23468c2ecf20Sopenharmony_ci			netdev_err(dev, "activate_qp failed\n");
23478c2ecf20Sopenharmony_ci			goto out_free_irqs;
23488c2ecf20Sopenharmony_ci		}
23498c2ecf20Sopenharmony_ci	}
23508c2ecf20Sopenharmony_ci
23518c2ecf20Sopenharmony_ci	for (i = 0; i < port->num_def_qps; i++) {
23528c2ecf20Sopenharmony_ci		ret = ehea_fill_port_res(&port->port_res[i]);
23538c2ecf20Sopenharmony_ci		if (ret) {
23548c2ecf20Sopenharmony_ci			netdev_err(dev, "out_free_irqs\n");
23558c2ecf20Sopenharmony_ci			goto out_free_irqs;
23568c2ecf20Sopenharmony_ci		}
23578c2ecf20Sopenharmony_ci	}
23588c2ecf20Sopenharmony_ci
23598c2ecf20Sopenharmony_ci	ret = ehea_broadcast_reg_helper(port, H_REG_BCMC);
23608c2ecf20Sopenharmony_ci	if (ret) {
23618c2ecf20Sopenharmony_ci		ret = -EIO;
23628c2ecf20Sopenharmony_ci		goto out_free_irqs;
23638c2ecf20Sopenharmony_ci	}
23648c2ecf20Sopenharmony_ci
23658c2ecf20Sopenharmony_ci	port->state = EHEA_PORT_UP;
23668c2ecf20Sopenharmony_ci
23678c2ecf20Sopenharmony_ci	ret = 0;
23688c2ecf20Sopenharmony_ci	goto out;
23698c2ecf20Sopenharmony_ci
23708c2ecf20Sopenharmony_ciout_free_irqs:
23718c2ecf20Sopenharmony_ci	ehea_free_interrupts(dev);
23728c2ecf20Sopenharmony_ci
23738c2ecf20Sopenharmony_ciout_clean_pr:
23748c2ecf20Sopenharmony_ci	ehea_clean_all_portres(port);
23758c2ecf20Sopenharmony_ciout:
23768c2ecf20Sopenharmony_ci	if (ret)
23778c2ecf20Sopenharmony_ci		netdev_info(dev, "Failed starting. ret=%i\n", ret);
23788c2ecf20Sopenharmony_ci
23798c2ecf20Sopenharmony_ci	ehea_update_bcmc_registrations();
23808c2ecf20Sopenharmony_ci	ehea_update_firmware_handles();
23818c2ecf20Sopenharmony_ci
23828c2ecf20Sopenharmony_ci	return ret;
23838c2ecf20Sopenharmony_ci}
23848c2ecf20Sopenharmony_ci
23858c2ecf20Sopenharmony_cistatic void port_napi_disable(struct ehea_port *port)
23868c2ecf20Sopenharmony_ci{
23878c2ecf20Sopenharmony_ci	int i;
23888c2ecf20Sopenharmony_ci
23898c2ecf20Sopenharmony_ci	for (i = 0; i < port->num_def_qps; i++)
23908c2ecf20Sopenharmony_ci		napi_disable(&port->port_res[i].napi);
23918c2ecf20Sopenharmony_ci}
23928c2ecf20Sopenharmony_ci
23938c2ecf20Sopenharmony_cistatic void port_napi_enable(struct ehea_port *port)
23948c2ecf20Sopenharmony_ci{
23958c2ecf20Sopenharmony_ci	int i;
23968c2ecf20Sopenharmony_ci
23978c2ecf20Sopenharmony_ci	for (i = 0; i < port->num_def_qps; i++)
23988c2ecf20Sopenharmony_ci		napi_enable(&port->port_res[i].napi);
23998c2ecf20Sopenharmony_ci}
24008c2ecf20Sopenharmony_ci
24018c2ecf20Sopenharmony_cistatic int ehea_open(struct net_device *dev)
24028c2ecf20Sopenharmony_ci{
24038c2ecf20Sopenharmony_ci	int ret;
24048c2ecf20Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
24058c2ecf20Sopenharmony_ci
24068c2ecf20Sopenharmony_ci	mutex_lock(&port->port_lock);
24078c2ecf20Sopenharmony_ci
24088c2ecf20Sopenharmony_ci	netif_info(port, ifup, dev, "enabling port\n");
24098c2ecf20Sopenharmony_ci
24108c2ecf20Sopenharmony_ci	netif_carrier_off(dev);
24118c2ecf20Sopenharmony_ci
24128c2ecf20Sopenharmony_ci	ret = ehea_up(dev);
24138c2ecf20Sopenharmony_ci	if (!ret) {
24148c2ecf20Sopenharmony_ci		port_napi_enable(port);
24158c2ecf20Sopenharmony_ci		netif_tx_start_all_queues(dev);
24168c2ecf20Sopenharmony_ci	}
24178c2ecf20Sopenharmony_ci
24188c2ecf20Sopenharmony_ci	mutex_unlock(&port->port_lock);
24198c2ecf20Sopenharmony_ci	schedule_delayed_work(&port->stats_work,
24208c2ecf20Sopenharmony_ci			      round_jiffies_relative(msecs_to_jiffies(1000)));
24218c2ecf20Sopenharmony_ci
24228c2ecf20Sopenharmony_ci	return ret;
24238c2ecf20Sopenharmony_ci}
24248c2ecf20Sopenharmony_ci
24258c2ecf20Sopenharmony_cistatic int ehea_down(struct net_device *dev)
24268c2ecf20Sopenharmony_ci{
24278c2ecf20Sopenharmony_ci	int ret;
24288c2ecf20Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
24298c2ecf20Sopenharmony_ci
24308c2ecf20Sopenharmony_ci	if (port->state == EHEA_PORT_DOWN)
24318c2ecf20Sopenharmony_ci		return 0;
24328c2ecf20Sopenharmony_ci
24338c2ecf20Sopenharmony_ci	ehea_drop_multicast_list(dev);
24348c2ecf20Sopenharmony_ci	ehea_allmulti(dev, 0);
24358c2ecf20Sopenharmony_ci	ehea_broadcast_reg_helper(port, H_DEREG_BCMC);
24368c2ecf20Sopenharmony_ci
24378c2ecf20Sopenharmony_ci	ehea_free_interrupts(dev);
24388c2ecf20Sopenharmony_ci
24398c2ecf20Sopenharmony_ci	port->state = EHEA_PORT_DOWN;
24408c2ecf20Sopenharmony_ci
24418c2ecf20Sopenharmony_ci	ehea_update_bcmc_registrations();
24428c2ecf20Sopenharmony_ci
24438c2ecf20Sopenharmony_ci	ret = ehea_clean_all_portres(port);
24448c2ecf20Sopenharmony_ci	if (ret)
24458c2ecf20Sopenharmony_ci		netdev_info(dev, "Failed freeing resources. ret=%i\n", ret);
24468c2ecf20Sopenharmony_ci
24478c2ecf20Sopenharmony_ci	ehea_update_firmware_handles();
24488c2ecf20Sopenharmony_ci
24498c2ecf20Sopenharmony_ci	return ret;
24508c2ecf20Sopenharmony_ci}
24518c2ecf20Sopenharmony_ci
24528c2ecf20Sopenharmony_cistatic int ehea_stop(struct net_device *dev)
24538c2ecf20Sopenharmony_ci{
24548c2ecf20Sopenharmony_ci	int ret;
24558c2ecf20Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
24568c2ecf20Sopenharmony_ci
24578c2ecf20Sopenharmony_ci	netif_info(port, ifdown, dev, "disabling port\n");
24588c2ecf20Sopenharmony_ci
24598c2ecf20Sopenharmony_ci	set_bit(__EHEA_DISABLE_PORT_RESET, &port->flags);
24608c2ecf20Sopenharmony_ci	cancel_work_sync(&port->reset_task);
24618c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&port->stats_work);
24628c2ecf20Sopenharmony_ci	mutex_lock(&port->port_lock);
24638c2ecf20Sopenharmony_ci	netif_tx_stop_all_queues(dev);
24648c2ecf20Sopenharmony_ci	port_napi_disable(port);
24658c2ecf20Sopenharmony_ci	ret = ehea_down(dev);
24668c2ecf20Sopenharmony_ci	mutex_unlock(&port->port_lock);
24678c2ecf20Sopenharmony_ci	clear_bit(__EHEA_DISABLE_PORT_RESET, &port->flags);
24688c2ecf20Sopenharmony_ci	return ret;
24698c2ecf20Sopenharmony_ci}
24708c2ecf20Sopenharmony_ci
24718c2ecf20Sopenharmony_cistatic void ehea_purge_sq(struct ehea_qp *orig_qp)
24728c2ecf20Sopenharmony_ci{
24738c2ecf20Sopenharmony_ci	struct ehea_qp qp = *orig_qp;
24748c2ecf20Sopenharmony_ci	struct ehea_qp_init_attr *init_attr = &qp.init_attr;
24758c2ecf20Sopenharmony_ci	struct ehea_swqe *swqe;
24768c2ecf20Sopenharmony_ci	int wqe_index;
24778c2ecf20Sopenharmony_ci	int i;
24788c2ecf20Sopenharmony_ci
24798c2ecf20Sopenharmony_ci	for (i = 0; i < init_attr->act_nr_send_wqes; i++) {
24808c2ecf20Sopenharmony_ci		swqe = ehea_get_swqe(&qp, &wqe_index);
24818c2ecf20Sopenharmony_ci		swqe->tx_control |= EHEA_SWQE_PURGE;
24828c2ecf20Sopenharmony_ci	}
24838c2ecf20Sopenharmony_ci}
24848c2ecf20Sopenharmony_ci
24858c2ecf20Sopenharmony_cistatic void ehea_flush_sq(struct ehea_port *port)
24868c2ecf20Sopenharmony_ci{
24878c2ecf20Sopenharmony_ci	int i;
24888c2ecf20Sopenharmony_ci
24898c2ecf20Sopenharmony_ci	for (i = 0; i < port->num_def_qps; i++) {
24908c2ecf20Sopenharmony_ci		struct ehea_port_res *pr = &port->port_res[i];
24918c2ecf20Sopenharmony_ci		int swqe_max = pr->sq_skba_size - 2 - pr->swqe_ll_count;
24928c2ecf20Sopenharmony_ci		int ret;
24938c2ecf20Sopenharmony_ci
24948c2ecf20Sopenharmony_ci		ret = wait_event_timeout(port->swqe_avail_wq,
24958c2ecf20Sopenharmony_ci			 atomic_read(&pr->swqe_avail) >= swqe_max,
24968c2ecf20Sopenharmony_ci			 msecs_to_jiffies(100));
24978c2ecf20Sopenharmony_ci
24988c2ecf20Sopenharmony_ci		if (!ret) {
24998c2ecf20Sopenharmony_ci			pr_err("WARNING: sq not flushed completely\n");
25008c2ecf20Sopenharmony_ci			break;
25018c2ecf20Sopenharmony_ci		}
25028c2ecf20Sopenharmony_ci	}
25038c2ecf20Sopenharmony_ci}
25048c2ecf20Sopenharmony_ci
25058c2ecf20Sopenharmony_cistatic int ehea_stop_qps(struct net_device *dev)
25068c2ecf20Sopenharmony_ci{
25078c2ecf20Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
25088c2ecf20Sopenharmony_ci	struct ehea_adapter *adapter = port->adapter;
25098c2ecf20Sopenharmony_ci	struct hcp_modify_qp_cb0 *cb0;
25108c2ecf20Sopenharmony_ci	int ret = -EIO;
25118c2ecf20Sopenharmony_ci	int dret;
25128c2ecf20Sopenharmony_ci	int i;
25138c2ecf20Sopenharmony_ci	u64 hret;
25148c2ecf20Sopenharmony_ci	u64 dummy64 = 0;
25158c2ecf20Sopenharmony_ci	u16 dummy16 = 0;
25168c2ecf20Sopenharmony_ci
25178c2ecf20Sopenharmony_ci	cb0 = (void *)get_zeroed_page(GFP_KERNEL);
25188c2ecf20Sopenharmony_ci	if (!cb0) {
25198c2ecf20Sopenharmony_ci		ret = -ENOMEM;
25208c2ecf20Sopenharmony_ci		goto out;
25218c2ecf20Sopenharmony_ci	}
25228c2ecf20Sopenharmony_ci
25238c2ecf20Sopenharmony_ci	for (i = 0; i < (port->num_def_qps); i++) {
25248c2ecf20Sopenharmony_ci		struct ehea_port_res *pr =  &port->port_res[i];
25258c2ecf20Sopenharmony_ci		struct ehea_qp *qp = pr->qp;
25268c2ecf20Sopenharmony_ci
25278c2ecf20Sopenharmony_ci		/* Purge send queue */
25288c2ecf20Sopenharmony_ci		ehea_purge_sq(qp);
25298c2ecf20Sopenharmony_ci
25308c2ecf20Sopenharmony_ci		/* Disable queue pair */
25318c2ecf20Sopenharmony_ci		hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
25328c2ecf20Sopenharmony_ci					    EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF),
25338c2ecf20Sopenharmony_ci					    cb0);
25348c2ecf20Sopenharmony_ci		if (hret != H_SUCCESS) {
25358c2ecf20Sopenharmony_ci			pr_err("query_ehea_qp failed (1)\n");
25368c2ecf20Sopenharmony_ci			goto out;
25378c2ecf20Sopenharmony_ci		}
25388c2ecf20Sopenharmony_ci
25398c2ecf20Sopenharmony_ci		cb0->qp_ctl_reg = (cb0->qp_ctl_reg & H_QP_CR_RES_STATE) << 8;
25408c2ecf20Sopenharmony_ci		cb0->qp_ctl_reg &= ~H_QP_CR_ENABLED;
25418c2ecf20Sopenharmony_ci
25428c2ecf20Sopenharmony_ci		hret = ehea_h_modify_ehea_qp(adapter->handle, 0, qp->fw_handle,
25438c2ecf20Sopenharmony_ci					     EHEA_BMASK_SET(H_QPCB0_QP_CTL_REG,
25448c2ecf20Sopenharmony_ci							    1), cb0, &dummy64,
25458c2ecf20Sopenharmony_ci					     &dummy64, &dummy16, &dummy16);
25468c2ecf20Sopenharmony_ci		if (hret != H_SUCCESS) {
25478c2ecf20Sopenharmony_ci			pr_err("modify_ehea_qp failed (1)\n");
25488c2ecf20Sopenharmony_ci			goto out;
25498c2ecf20Sopenharmony_ci		}
25508c2ecf20Sopenharmony_ci
25518c2ecf20Sopenharmony_ci		hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
25528c2ecf20Sopenharmony_ci					    EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF),
25538c2ecf20Sopenharmony_ci					    cb0);
25548c2ecf20Sopenharmony_ci		if (hret != H_SUCCESS) {
25558c2ecf20Sopenharmony_ci			pr_err("query_ehea_qp failed (2)\n");
25568c2ecf20Sopenharmony_ci			goto out;
25578c2ecf20Sopenharmony_ci		}
25588c2ecf20Sopenharmony_ci
25598c2ecf20Sopenharmony_ci		/* deregister shared memory regions */
25608c2ecf20Sopenharmony_ci		dret = ehea_rem_smrs(pr);
25618c2ecf20Sopenharmony_ci		if (dret) {
25628c2ecf20Sopenharmony_ci			pr_err("unreg shared memory region failed\n");
25638c2ecf20Sopenharmony_ci			goto out;
25648c2ecf20Sopenharmony_ci		}
25658c2ecf20Sopenharmony_ci	}
25668c2ecf20Sopenharmony_ci
25678c2ecf20Sopenharmony_ci	ret = 0;
25688c2ecf20Sopenharmony_ciout:
25698c2ecf20Sopenharmony_ci	free_page((unsigned long)cb0);
25708c2ecf20Sopenharmony_ci
25718c2ecf20Sopenharmony_ci	return ret;
25728c2ecf20Sopenharmony_ci}
25738c2ecf20Sopenharmony_ci
25748c2ecf20Sopenharmony_cistatic void ehea_update_rqs(struct ehea_qp *orig_qp, struct ehea_port_res *pr)
25758c2ecf20Sopenharmony_ci{
25768c2ecf20Sopenharmony_ci	struct ehea_qp qp = *orig_qp;
25778c2ecf20Sopenharmony_ci	struct ehea_qp_init_attr *init_attr = &qp.init_attr;
25788c2ecf20Sopenharmony_ci	struct ehea_rwqe *rwqe;
25798c2ecf20Sopenharmony_ci	struct sk_buff **skba_rq2 = pr->rq2_skba.arr;
25808c2ecf20Sopenharmony_ci	struct sk_buff **skba_rq3 = pr->rq3_skba.arr;
25818c2ecf20Sopenharmony_ci	struct sk_buff *skb;
25828c2ecf20Sopenharmony_ci	u32 lkey = pr->recv_mr.lkey;
25838c2ecf20Sopenharmony_ci
25848c2ecf20Sopenharmony_ci
25858c2ecf20Sopenharmony_ci	int i;
25868c2ecf20Sopenharmony_ci	int index;
25878c2ecf20Sopenharmony_ci
25888c2ecf20Sopenharmony_ci	for (i = 0; i < init_attr->act_nr_rwqes_rq2 + 1; i++) {
25898c2ecf20Sopenharmony_ci		rwqe = ehea_get_next_rwqe(&qp, 2);
25908c2ecf20Sopenharmony_ci		rwqe->sg_list[0].l_key = lkey;
25918c2ecf20Sopenharmony_ci		index = EHEA_BMASK_GET(EHEA_WR_ID_INDEX, rwqe->wr_id);
25928c2ecf20Sopenharmony_ci		skb = skba_rq2[index];
25938c2ecf20Sopenharmony_ci		if (skb)
25948c2ecf20Sopenharmony_ci			rwqe->sg_list[0].vaddr = ehea_map_vaddr(skb->data);
25958c2ecf20Sopenharmony_ci	}
25968c2ecf20Sopenharmony_ci
25978c2ecf20Sopenharmony_ci	for (i = 0; i < init_attr->act_nr_rwqes_rq3 + 1; i++) {
25988c2ecf20Sopenharmony_ci		rwqe = ehea_get_next_rwqe(&qp, 3);
25998c2ecf20Sopenharmony_ci		rwqe->sg_list[0].l_key = lkey;
26008c2ecf20Sopenharmony_ci		index = EHEA_BMASK_GET(EHEA_WR_ID_INDEX, rwqe->wr_id);
26018c2ecf20Sopenharmony_ci		skb = skba_rq3[index];
26028c2ecf20Sopenharmony_ci		if (skb)
26038c2ecf20Sopenharmony_ci			rwqe->sg_list[0].vaddr = ehea_map_vaddr(skb->data);
26048c2ecf20Sopenharmony_ci	}
26058c2ecf20Sopenharmony_ci}
26068c2ecf20Sopenharmony_ci
26078c2ecf20Sopenharmony_cistatic int ehea_restart_qps(struct net_device *dev)
26088c2ecf20Sopenharmony_ci{
26098c2ecf20Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
26108c2ecf20Sopenharmony_ci	struct ehea_adapter *adapter = port->adapter;
26118c2ecf20Sopenharmony_ci	int ret = 0;
26128c2ecf20Sopenharmony_ci	int i;
26138c2ecf20Sopenharmony_ci
26148c2ecf20Sopenharmony_ci	struct hcp_modify_qp_cb0 *cb0;
26158c2ecf20Sopenharmony_ci	u64 hret;
26168c2ecf20Sopenharmony_ci	u64 dummy64 = 0;
26178c2ecf20Sopenharmony_ci	u16 dummy16 = 0;
26188c2ecf20Sopenharmony_ci
26198c2ecf20Sopenharmony_ci	cb0 = (void *)get_zeroed_page(GFP_KERNEL);
26208c2ecf20Sopenharmony_ci	if (!cb0)
26218c2ecf20Sopenharmony_ci		return -ENOMEM;
26228c2ecf20Sopenharmony_ci
26238c2ecf20Sopenharmony_ci	for (i = 0; i < (port->num_def_qps); i++) {
26248c2ecf20Sopenharmony_ci		struct ehea_port_res *pr =  &port->port_res[i];
26258c2ecf20Sopenharmony_ci		struct ehea_qp *qp = pr->qp;
26268c2ecf20Sopenharmony_ci
26278c2ecf20Sopenharmony_ci		ret = ehea_gen_smrs(pr);
26288c2ecf20Sopenharmony_ci		if (ret) {
26298c2ecf20Sopenharmony_ci			netdev_err(dev, "creation of shared memory regions failed\n");
26308c2ecf20Sopenharmony_ci			goto out;
26318c2ecf20Sopenharmony_ci		}
26328c2ecf20Sopenharmony_ci
26338c2ecf20Sopenharmony_ci		ehea_update_rqs(qp, pr);
26348c2ecf20Sopenharmony_ci
26358c2ecf20Sopenharmony_ci		/* Enable queue pair */
26368c2ecf20Sopenharmony_ci		hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
26378c2ecf20Sopenharmony_ci					    EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF),
26388c2ecf20Sopenharmony_ci					    cb0);
26398c2ecf20Sopenharmony_ci		if (hret != H_SUCCESS) {
26408c2ecf20Sopenharmony_ci			netdev_err(dev, "query_ehea_qp failed (1)\n");
26418c2ecf20Sopenharmony_ci			ret = -EFAULT;
26428c2ecf20Sopenharmony_ci			goto out;
26438c2ecf20Sopenharmony_ci		}
26448c2ecf20Sopenharmony_ci
26458c2ecf20Sopenharmony_ci		cb0->qp_ctl_reg = (cb0->qp_ctl_reg & H_QP_CR_RES_STATE) << 8;
26468c2ecf20Sopenharmony_ci		cb0->qp_ctl_reg |= H_QP_CR_ENABLED;
26478c2ecf20Sopenharmony_ci
26488c2ecf20Sopenharmony_ci		hret = ehea_h_modify_ehea_qp(adapter->handle, 0, qp->fw_handle,
26498c2ecf20Sopenharmony_ci					     EHEA_BMASK_SET(H_QPCB0_QP_CTL_REG,
26508c2ecf20Sopenharmony_ci							    1), cb0, &dummy64,
26518c2ecf20Sopenharmony_ci					     &dummy64, &dummy16, &dummy16);
26528c2ecf20Sopenharmony_ci		if (hret != H_SUCCESS) {
26538c2ecf20Sopenharmony_ci			netdev_err(dev, "modify_ehea_qp failed (1)\n");
26548c2ecf20Sopenharmony_ci			ret = -EFAULT;
26558c2ecf20Sopenharmony_ci			goto out;
26568c2ecf20Sopenharmony_ci		}
26578c2ecf20Sopenharmony_ci
26588c2ecf20Sopenharmony_ci		hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
26598c2ecf20Sopenharmony_ci					    EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF),
26608c2ecf20Sopenharmony_ci					    cb0);
26618c2ecf20Sopenharmony_ci		if (hret != H_SUCCESS) {
26628c2ecf20Sopenharmony_ci			netdev_err(dev, "query_ehea_qp failed (2)\n");
26638c2ecf20Sopenharmony_ci			ret = -EFAULT;
26648c2ecf20Sopenharmony_ci			goto out;
26658c2ecf20Sopenharmony_ci		}
26668c2ecf20Sopenharmony_ci
26678c2ecf20Sopenharmony_ci		/* refill entire queue */
26688c2ecf20Sopenharmony_ci		ehea_refill_rq1(pr, pr->rq1_skba.index, 0);
26698c2ecf20Sopenharmony_ci		ehea_refill_rq2(pr, 0);
26708c2ecf20Sopenharmony_ci		ehea_refill_rq3(pr, 0);
26718c2ecf20Sopenharmony_ci	}
26728c2ecf20Sopenharmony_ciout:
26738c2ecf20Sopenharmony_ci	free_page((unsigned long)cb0);
26748c2ecf20Sopenharmony_ci
26758c2ecf20Sopenharmony_ci	return ret;
26768c2ecf20Sopenharmony_ci}
26778c2ecf20Sopenharmony_ci
26788c2ecf20Sopenharmony_cistatic void ehea_reset_port(struct work_struct *work)
26798c2ecf20Sopenharmony_ci{
26808c2ecf20Sopenharmony_ci	int ret;
26818c2ecf20Sopenharmony_ci	struct ehea_port *port =
26828c2ecf20Sopenharmony_ci		container_of(work, struct ehea_port, reset_task);
26838c2ecf20Sopenharmony_ci	struct net_device *dev = port->netdev;
26848c2ecf20Sopenharmony_ci
26858c2ecf20Sopenharmony_ci	mutex_lock(&dlpar_mem_lock);
26868c2ecf20Sopenharmony_ci	port->resets++;
26878c2ecf20Sopenharmony_ci	mutex_lock(&port->port_lock);
26888c2ecf20Sopenharmony_ci	netif_tx_disable(dev);
26898c2ecf20Sopenharmony_ci
26908c2ecf20Sopenharmony_ci	port_napi_disable(port);
26918c2ecf20Sopenharmony_ci
26928c2ecf20Sopenharmony_ci	ehea_down(dev);
26938c2ecf20Sopenharmony_ci
26948c2ecf20Sopenharmony_ci	ret = ehea_up(dev);
26958c2ecf20Sopenharmony_ci	if (ret)
26968c2ecf20Sopenharmony_ci		goto out;
26978c2ecf20Sopenharmony_ci
26988c2ecf20Sopenharmony_ci	ehea_set_multicast_list(dev);
26998c2ecf20Sopenharmony_ci
27008c2ecf20Sopenharmony_ci	netif_info(port, timer, dev, "reset successful\n");
27018c2ecf20Sopenharmony_ci
27028c2ecf20Sopenharmony_ci	port_napi_enable(port);
27038c2ecf20Sopenharmony_ci
27048c2ecf20Sopenharmony_ci	netif_tx_wake_all_queues(dev);
27058c2ecf20Sopenharmony_ciout:
27068c2ecf20Sopenharmony_ci	mutex_unlock(&port->port_lock);
27078c2ecf20Sopenharmony_ci	mutex_unlock(&dlpar_mem_lock);
27088c2ecf20Sopenharmony_ci}
27098c2ecf20Sopenharmony_ci
27108c2ecf20Sopenharmony_cistatic void ehea_rereg_mrs(void)
27118c2ecf20Sopenharmony_ci{
27128c2ecf20Sopenharmony_ci	int ret, i;
27138c2ecf20Sopenharmony_ci	struct ehea_adapter *adapter;
27148c2ecf20Sopenharmony_ci
27158c2ecf20Sopenharmony_ci	pr_info("LPAR memory changed - re-initializing driver\n");
27168c2ecf20Sopenharmony_ci
27178c2ecf20Sopenharmony_ci	list_for_each_entry(adapter, &adapter_list, list)
27188c2ecf20Sopenharmony_ci		if (adapter->active_ports) {
27198c2ecf20Sopenharmony_ci			/* Shutdown all ports */
27208c2ecf20Sopenharmony_ci			for (i = 0; i < EHEA_MAX_PORTS; i++) {
27218c2ecf20Sopenharmony_ci				struct ehea_port *port = adapter->port[i];
27228c2ecf20Sopenharmony_ci				struct net_device *dev;
27238c2ecf20Sopenharmony_ci
27248c2ecf20Sopenharmony_ci				if (!port)
27258c2ecf20Sopenharmony_ci					continue;
27268c2ecf20Sopenharmony_ci
27278c2ecf20Sopenharmony_ci				dev = port->netdev;
27288c2ecf20Sopenharmony_ci
27298c2ecf20Sopenharmony_ci				if (dev->flags & IFF_UP) {
27308c2ecf20Sopenharmony_ci					mutex_lock(&port->port_lock);
27318c2ecf20Sopenharmony_ci					netif_tx_disable(dev);
27328c2ecf20Sopenharmony_ci					ehea_flush_sq(port);
27338c2ecf20Sopenharmony_ci					ret = ehea_stop_qps(dev);
27348c2ecf20Sopenharmony_ci					if (ret) {
27358c2ecf20Sopenharmony_ci						mutex_unlock(&port->port_lock);
27368c2ecf20Sopenharmony_ci						goto out;
27378c2ecf20Sopenharmony_ci					}
27388c2ecf20Sopenharmony_ci					port_napi_disable(port);
27398c2ecf20Sopenharmony_ci					mutex_unlock(&port->port_lock);
27408c2ecf20Sopenharmony_ci				}
27418c2ecf20Sopenharmony_ci				reset_sq_restart_flag(port);
27428c2ecf20Sopenharmony_ci			}
27438c2ecf20Sopenharmony_ci
27448c2ecf20Sopenharmony_ci			/* Unregister old memory region */
27458c2ecf20Sopenharmony_ci			ret = ehea_rem_mr(&adapter->mr);
27468c2ecf20Sopenharmony_ci			if (ret) {
27478c2ecf20Sopenharmony_ci				pr_err("unregister MR failed - driver inoperable!\n");
27488c2ecf20Sopenharmony_ci				goto out;
27498c2ecf20Sopenharmony_ci			}
27508c2ecf20Sopenharmony_ci		}
27518c2ecf20Sopenharmony_ci
27528c2ecf20Sopenharmony_ci	clear_bit(__EHEA_STOP_XFER, &ehea_driver_flags);
27538c2ecf20Sopenharmony_ci
27548c2ecf20Sopenharmony_ci	list_for_each_entry(adapter, &adapter_list, list)
27558c2ecf20Sopenharmony_ci		if (adapter->active_ports) {
27568c2ecf20Sopenharmony_ci			/* Register new memory region */
27578c2ecf20Sopenharmony_ci			ret = ehea_reg_kernel_mr(adapter, &adapter->mr);
27588c2ecf20Sopenharmony_ci			if (ret) {
27598c2ecf20Sopenharmony_ci				pr_err("register MR failed - driver inoperable!\n");
27608c2ecf20Sopenharmony_ci				goto out;
27618c2ecf20Sopenharmony_ci			}
27628c2ecf20Sopenharmony_ci
27638c2ecf20Sopenharmony_ci			/* Restart all ports */
27648c2ecf20Sopenharmony_ci			for (i = 0; i < EHEA_MAX_PORTS; i++) {
27658c2ecf20Sopenharmony_ci				struct ehea_port *port = adapter->port[i];
27668c2ecf20Sopenharmony_ci
27678c2ecf20Sopenharmony_ci				if (port) {
27688c2ecf20Sopenharmony_ci					struct net_device *dev = port->netdev;
27698c2ecf20Sopenharmony_ci
27708c2ecf20Sopenharmony_ci					if (dev->flags & IFF_UP) {
27718c2ecf20Sopenharmony_ci						mutex_lock(&port->port_lock);
27728c2ecf20Sopenharmony_ci						ret = ehea_restart_qps(dev);
27738c2ecf20Sopenharmony_ci						if (!ret) {
27748c2ecf20Sopenharmony_ci							check_sqs(port);
27758c2ecf20Sopenharmony_ci							port_napi_enable(port);
27768c2ecf20Sopenharmony_ci							netif_tx_wake_all_queues(dev);
27778c2ecf20Sopenharmony_ci						} else {
27788c2ecf20Sopenharmony_ci							netdev_err(dev, "Unable to restart QPS\n");
27798c2ecf20Sopenharmony_ci						}
27808c2ecf20Sopenharmony_ci						mutex_unlock(&port->port_lock);
27818c2ecf20Sopenharmony_ci					}
27828c2ecf20Sopenharmony_ci				}
27838c2ecf20Sopenharmony_ci			}
27848c2ecf20Sopenharmony_ci		}
27858c2ecf20Sopenharmony_ci	pr_info("re-initializing driver complete\n");
27868c2ecf20Sopenharmony_ciout:
27878c2ecf20Sopenharmony_ci	return;
27888c2ecf20Sopenharmony_ci}
27898c2ecf20Sopenharmony_ci
27908c2ecf20Sopenharmony_cistatic void ehea_tx_watchdog(struct net_device *dev, unsigned int txqueue)
27918c2ecf20Sopenharmony_ci{
27928c2ecf20Sopenharmony_ci	struct ehea_port *port = netdev_priv(dev);
27938c2ecf20Sopenharmony_ci
27948c2ecf20Sopenharmony_ci	if (netif_carrier_ok(dev) &&
27958c2ecf20Sopenharmony_ci	    !test_bit(__EHEA_STOP_XFER, &ehea_driver_flags))
27968c2ecf20Sopenharmony_ci		ehea_schedule_port_reset(port);
27978c2ecf20Sopenharmony_ci}
27988c2ecf20Sopenharmony_ci
27998c2ecf20Sopenharmony_cistatic int ehea_sense_adapter_attr(struct ehea_adapter *adapter)
28008c2ecf20Sopenharmony_ci{
28018c2ecf20Sopenharmony_ci	struct hcp_query_ehea *cb;
28028c2ecf20Sopenharmony_ci	u64 hret;
28038c2ecf20Sopenharmony_ci	int ret;
28048c2ecf20Sopenharmony_ci
28058c2ecf20Sopenharmony_ci	cb = (void *)get_zeroed_page(GFP_KERNEL);
28068c2ecf20Sopenharmony_ci	if (!cb) {
28078c2ecf20Sopenharmony_ci		ret = -ENOMEM;
28088c2ecf20Sopenharmony_ci		goto out;
28098c2ecf20Sopenharmony_ci	}
28108c2ecf20Sopenharmony_ci
28118c2ecf20Sopenharmony_ci	hret = ehea_h_query_ehea(adapter->handle, cb);
28128c2ecf20Sopenharmony_ci
28138c2ecf20Sopenharmony_ci	if (hret != H_SUCCESS) {
28148c2ecf20Sopenharmony_ci		ret = -EIO;
28158c2ecf20Sopenharmony_ci		goto out_herr;
28168c2ecf20Sopenharmony_ci	}
28178c2ecf20Sopenharmony_ci
28188c2ecf20Sopenharmony_ci	adapter->max_mc_mac = cb->max_mc_mac - 1;
28198c2ecf20Sopenharmony_ci	ret = 0;
28208c2ecf20Sopenharmony_ci
28218c2ecf20Sopenharmony_ciout_herr:
28228c2ecf20Sopenharmony_ci	free_page((unsigned long)cb);
28238c2ecf20Sopenharmony_ciout:
28248c2ecf20Sopenharmony_ci	return ret;
28258c2ecf20Sopenharmony_ci}
28268c2ecf20Sopenharmony_ci
28278c2ecf20Sopenharmony_cistatic int ehea_get_jumboframe_status(struct ehea_port *port, int *jumbo)
28288c2ecf20Sopenharmony_ci{
28298c2ecf20Sopenharmony_ci	struct hcp_ehea_port_cb4 *cb4;
28308c2ecf20Sopenharmony_ci	u64 hret;
28318c2ecf20Sopenharmony_ci	int ret = 0;
28328c2ecf20Sopenharmony_ci
28338c2ecf20Sopenharmony_ci	*jumbo = 0;
28348c2ecf20Sopenharmony_ci
28358c2ecf20Sopenharmony_ci	/* (Try to) enable *jumbo frames */
28368c2ecf20Sopenharmony_ci	cb4 = (void *)get_zeroed_page(GFP_KERNEL);
28378c2ecf20Sopenharmony_ci	if (!cb4) {
28388c2ecf20Sopenharmony_ci		pr_err("no mem for cb4\n");
28398c2ecf20Sopenharmony_ci		ret = -ENOMEM;
28408c2ecf20Sopenharmony_ci		goto out;
28418c2ecf20Sopenharmony_ci	} else {
28428c2ecf20Sopenharmony_ci		hret = ehea_h_query_ehea_port(port->adapter->handle,
28438c2ecf20Sopenharmony_ci					      port->logical_port_id,
28448c2ecf20Sopenharmony_ci					      H_PORT_CB4,
28458c2ecf20Sopenharmony_ci					      H_PORT_CB4_JUMBO, cb4);
28468c2ecf20Sopenharmony_ci		if (hret == H_SUCCESS) {
28478c2ecf20Sopenharmony_ci			if (cb4->jumbo_frame)
28488c2ecf20Sopenharmony_ci				*jumbo = 1;
28498c2ecf20Sopenharmony_ci			else {
28508c2ecf20Sopenharmony_ci				cb4->jumbo_frame = 1;
28518c2ecf20Sopenharmony_ci				hret = ehea_h_modify_ehea_port(port->adapter->
28528c2ecf20Sopenharmony_ci							       handle,
28538c2ecf20Sopenharmony_ci							       port->
28548c2ecf20Sopenharmony_ci							       logical_port_id,
28558c2ecf20Sopenharmony_ci							       H_PORT_CB4,
28568c2ecf20Sopenharmony_ci							       H_PORT_CB4_JUMBO,
28578c2ecf20Sopenharmony_ci							       cb4);
28588c2ecf20Sopenharmony_ci				if (hret == H_SUCCESS)
28598c2ecf20Sopenharmony_ci					*jumbo = 1;
28608c2ecf20Sopenharmony_ci			}
28618c2ecf20Sopenharmony_ci		} else
28628c2ecf20Sopenharmony_ci			ret = -EINVAL;
28638c2ecf20Sopenharmony_ci
28648c2ecf20Sopenharmony_ci		free_page((unsigned long)cb4);
28658c2ecf20Sopenharmony_ci	}
28668c2ecf20Sopenharmony_ciout:
28678c2ecf20Sopenharmony_ci	return ret;
28688c2ecf20Sopenharmony_ci}
28698c2ecf20Sopenharmony_ci
28708c2ecf20Sopenharmony_cistatic ssize_t ehea_show_port_id(struct device *dev,
28718c2ecf20Sopenharmony_ci				 struct device_attribute *attr, char *buf)
28728c2ecf20Sopenharmony_ci{
28738c2ecf20Sopenharmony_ci	struct ehea_port *port = container_of(dev, struct ehea_port, ofdev.dev);
28748c2ecf20Sopenharmony_ci	return sprintf(buf, "%d", port->logical_port_id);
28758c2ecf20Sopenharmony_ci}
28768c2ecf20Sopenharmony_ci
28778c2ecf20Sopenharmony_cistatic DEVICE_ATTR(log_port_id, 0444, ehea_show_port_id, NULL);
28788c2ecf20Sopenharmony_ci
28798c2ecf20Sopenharmony_cistatic void logical_port_release(struct device *dev)
28808c2ecf20Sopenharmony_ci{
28818c2ecf20Sopenharmony_ci	struct ehea_port *port = container_of(dev, struct ehea_port, ofdev.dev);
28828c2ecf20Sopenharmony_ci	of_node_put(port->ofdev.dev.of_node);
28838c2ecf20Sopenharmony_ci}
28848c2ecf20Sopenharmony_ci
28858c2ecf20Sopenharmony_cistatic struct device *ehea_register_port(struct ehea_port *port,
28868c2ecf20Sopenharmony_ci					 struct device_node *dn)
28878c2ecf20Sopenharmony_ci{
28888c2ecf20Sopenharmony_ci	int ret;
28898c2ecf20Sopenharmony_ci
28908c2ecf20Sopenharmony_ci	port->ofdev.dev.of_node = of_node_get(dn);
28918c2ecf20Sopenharmony_ci	port->ofdev.dev.parent = &port->adapter->ofdev->dev;
28928c2ecf20Sopenharmony_ci	port->ofdev.dev.bus = &ibmebus_bus_type;
28938c2ecf20Sopenharmony_ci
28948c2ecf20Sopenharmony_ci	dev_set_name(&port->ofdev.dev, "port%d", port_name_cnt++);
28958c2ecf20Sopenharmony_ci	port->ofdev.dev.release = logical_port_release;
28968c2ecf20Sopenharmony_ci
28978c2ecf20Sopenharmony_ci	ret = of_device_register(&port->ofdev);
28988c2ecf20Sopenharmony_ci	if (ret) {
28998c2ecf20Sopenharmony_ci		pr_err("failed to register device. ret=%d\n", ret);
29008c2ecf20Sopenharmony_ci		put_device(&port->ofdev.dev);
29018c2ecf20Sopenharmony_ci		goto out;
29028c2ecf20Sopenharmony_ci	}
29038c2ecf20Sopenharmony_ci
29048c2ecf20Sopenharmony_ci	ret = device_create_file(&port->ofdev.dev, &dev_attr_log_port_id);
29058c2ecf20Sopenharmony_ci	if (ret) {
29068c2ecf20Sopenharmony_ci		pr_err("failed to register attributes, ret=%d\n", ret);
29078c2ecf20Sopenharmony_ci		goto out_unreg_of_dev;
29088c2ecf20Sopenharmony_ci	}
29098c2ecf20Sopenharmony_ci
29108c2ecf20Sopenharmony_ci	return &port->ofdev.dev;
29118c2ecf20Sopenharmony_ci
29128c2ecf20Sopenharmony_ciout_unreg_of_dev:
29138c2ecf20Sopenharmony_ci	of_device_unregister(&port->ofdev);
29148c2ecf20Sopenharmony_ciout:
29158c2ecf20Sopenharmony_ci	return NULL;
29168c2ecf20Sopenharmony_ci}
29178c2ecf20Sopenharmony_ci
29188c2ecf20Sopenharmony_cistatic void ehea_unregister_port(struct ehea_port *port)
29198c2ecf20Sopenharmony_ci{
29208c2ecf20Sopenharmony_ci	device_remove_file(&port->ofdev.dev, &dev_attr_log_port_id);
29218c2ecf20Sopenharmony_ci	of_device_unregister(&port->ofdev);
29228c2ecf20Sopenharmony_ci}
29238c2ecf20Sopenharmony_ci
29248c2ecf20Sopenharmony_cistatic const struct net_device_ops ehea_netdev_ops = {
29258c2ecf20Sopenharmony_ci	.ndo_open		= ehea_open,
29268c2ecf20Sopenharmony_ci	.ndo_stop		= ehea_stop,
29278c2ecf20Sopenharmony_ci	.ndo_start_xmit		= ehea_start_xmit,
29288c2ecf20Sopenharmony_ci	.ndo_get_stats64	= ehea_get_stats64,
29298c2ecf20Sopenharmony_ci	.ndo_set_mac_address	= ehea_set_mac_addr,
29308c2ecf20Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
29318c2ecf20Sopenharmony_ci	.ndo_set_rx_mode	= ehea_set_multicast_list,
29328c2ecf20Sopenharmony_ci	.ndo_vlan_rx_add_vid	= ehea_vlan_rx_add_vid,
29338c2ecf20Sopenharmony_ci	.ndo_vlan_rx_kill_vid	= ehea_vlan_rx_kill_vid,
29348c2ecf20Sopenharmony_ci	.ndo_tx_timeout		= ehea_tx_watchdog,
29358c2ecf20Sopenharmony_ci};
29368c2ecf20Sopenharmony_ci
29378c2ecf20Sopenharmony_cistatic struct ehea_port *ehea_setup_single_port(struct ehea_adapter *adapter,
29388c2ecf20Sopenharmony_ci					 u32 logical_port_id,
29398c2ecf20Sopenharmony_ci					 struct device_node *dn)
29408c2ecf20Sopenharmony_ci{
29418c2ecf20Sopenharmony_ci	int ret;
29428c2ecf20Sopenharmony_ci	struct net_device *dev;
29438c2ecf20Sopenharmony_ci	struct ehea_port *port;
29448c2ecf20Sopenharmony_ci	struct device *port_dev;
29458c2ecf20Sopenharmony_ci	int jumbo;
29468c2ecf20Sopenharmony_ci
29478c2ecf20Sopenharmony_ci	/* allocate memory for the port structures */
29488c2ecf20Sopenharmony_ci	dev = alloc_etherdev_mq(sizeof(struct ehea_port), EHEA_MAX_PORT_RES);
29498c2ecf20Sopenharmony_ci
29508c2ecf20Sopenharmony_ci	if (!dev) {
29518c2ecf20Sopenharmony_ci		ret = -ENOMEM;
29528c2ecf20Sopenharmony_ci		goto out_err;
29538c2ecf20Sopenharmony_ci	}
29548c2ecf20Sopenharmony_ci
29558c2ecf20Sopenharmony_ci	port = netdev_priv(dev);
29568c2ecf20Sopenharmony_ci
29578c2ecf20Sopenharmony_ci	mutex_init(&port->port_lock);
29588c2ecf20Sopenharmony_ci	port->state = EHEA_PORT_DOWN;
29598c2ecf20Sopenharmony_ci	port->sig_comp_iv = sq_entries / 10;
29608c2ecf20Sopenharmony_ci
29618c2ecf20Sopenharmony_ci	port->adapter = adapter;
29628c2ecf20Sopenharmony_ci	port->netdev = dev;
29638c2ecf20Sopenharmony_ci	port->logical_port_id = logical_port_id;
29648c2ecf20Sopenharmony_ci
29658c2ecf20Sopenharmony_ci	port->msg_enable = netif_msg_init(msg_level, EHEA_MSG_DEFAULT);
29668c2ecf20Sopenharmony_ci
29678c2ecf20Sopenharmony_ci	port->mc_list = kzalloc(sizeof(struct ehea_mc_list), GFP_KERNEL);
29688c2ecf20Sopenharmony_ci	if (!port->mc_list) {
29698c2ecf20Sopenharmony_ci		ret = -ENOMEM;
29708c2ecf20Sopenharmony_ci		goto out_free_ethdev;
29718c2ecf20Sopenharmony_ci	}
29728c2ecf20Sopenharmony_ci
29738c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&port->mc_list->list);
29748c2ecf20Sopenharmony_ci
29758c2ecf20Sopenharmony_ci	ret = ehea_sense_port_attr(port);
29768c2ecf20Sopenharmony_ci	if (ret)
29778c2ecf20Sopenharmony_ci		goto out_free_mc_list;
29788c2ecf20Sopenharmony_ci
29798c2ecf20Sopenharmony_ci	netif_set_real_num_rx_queues(dev, port->num_def_qps);
29808c2ecf20Sopenharmony_ci	netif_set_real_num_tx_queues(dev, port->num_def_qps);
29818c2ecf20Sopenharmony_ci
29828c2ecf20Sopenharmony_ci	port_dev = ehea_register_port(port, dn);
29838c2ecf20Sopenharmony_ci	if (!port_dev)
29848c2ecf20Sopenharmony_ci		goto out_free_mc_list;
29858c2ecf20Sopenharmony_ci
29868c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(dev, port_dev);
29878c2ecf20Sopenharmony_ci
29888c2ecf20Sopenharmony_ci	/* initialize net_device structure */
29898c2ecf20Sopenharmony_ci	memcpy(dev->dev_addr, &port->mac_addr, ETH_ALEN);
29908c2ecf20Sopenharmony_ci
29918c2ecf20Sopenharmony_ci	dev->netdev_ops = &ehea_netdev_ops;
29928c2ecf20Sopenharmony_ci	ehea_set_ethtool_ops(dev);
29938c2ecf20Sopenharmony_ci
29948c2ecf20Sopenharmony_ci	dev->hw_features = NETIF_F_SG | NETIF_F_TSO |
29958c2ecf20Sopenharmony_ci		      NETIF_F_IP_CSUM | NETIF_F_HW_VLAN_CTAG_TX;
29968c2ecf20Sopenharmony_ci	dev->features = NETIF_F_SG | NETIF_F_TSO |
29978c2ecf20Sopenharmony_ci		      NETIF_F_HIGHDMA | NETIF_F_IP_CSUM |
29988c2ecf20Sopenharmony_ci		      NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX |
29998c2ecf20Sopenharmony_ci		      NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXCSUM;
30008c2ecf20Sopenharmony_ci	dev->vlan_features = NETIF_F_SG | NETIF_F_TSO | NETIF_F_HIGHDMA |
30018c2ecf20Sopenharmony_ci			NETIF_F_IP_CSUM;
30028c2ecf20Sopenharmony_ci	dev->watchdog_timeo = EHEA_WATCH_DOG_TIMEOUT;
30038c2ecf20Sopenharmony_ci
30048c2ecf20Sopenharmony_ci	/* MTU range: 68 - 9022 */
30058c2ecf20Sopenharmony_ci	dev->min_mtu = ETH_MIN_MTU;
30068c2ecf20Sopenharmony_ci	dev->max_mtu = EHEA_MAX_PACKET_SIZE;
30078c2ecf20Sopenharmony_ci
30088c2ecf20Sopenharmony_ci	INIT_WORK(&port->reset_task, ehea_reset_port);
30098c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&port->stats_work, ehea_update_stats);
30108c2ecf20Sopenharmony_ci
30118c2ecf20Sopenharmony_ci	init_waitqueue_head(&port->swqe_avail_wq);
30128c2ecf20Sopenharmony_ci	init_waitqueue_head(&port->restart_wq);
30138c2ecf20Sopenharmony_ci
30148c2ecf20Sopenharmony_ci	ret = register_netdev(dev);
30158c2ecf20Sopenharmony_ci	if (ret) {
30168c2ecf20Sopenharmony_ci		pr_err("register_netdev failed. ret=%d\n", ret);
30178c2ecf20Sopenharmony_ci		goto out_unreg_port;
30188c2ecf20Sopenharmony_ci	}
30198c2ecf20Sopenharmony_ci
30208c2ecf20Sopenharmony_ci	ret = ehea_get_jumboframe_status(port, &jumbo);
30218c2ecf20Sopenharmony_ci	if (ret)
30228c2ecf20Sopenharmony_ci		netdev_err(dev, "failed determining jumbo frame status\n");
30238c2ecf20Sopenharmony_ci
30248c2ecf20Sopenharmony_ci	netdev_info(dev, "Jumbo frames are %sabled\n",
30258c2ecf20Sopenharmony_ci		    jumbo == 1 ? "en" : "dis");
30268c2ecf20Sopenharmony_ci
30278c2ecf20Sopenharmony_ci	adapter->active_ports++;
30288c2ecf20Sopenharmony_ci
30298c2ecf20Sopenharmony_ci	return port;
30308c2ecf20Sopenharmony_ci
30318c2ecf20Sopenharmony_ciout_unreg_port:
30328c2ecf20Sopenharmony_ci	ehea_unregister_port(port);
30338c2ecf20Sopenharmony_ci
30348c2ecf20Sopenharmony_ciout_free_mc_list:
30358c2ecf20Sopenharmony_ci	kfree(port->mc_list);
30368c2ecf20Sopenharmony_ci
30378c2ecf20Sopenharmony_ciout_free_ethdev:
30388c2ecf20Sopenharmony_ci	free_netdev(dev);
30398c2ecf20Sopenharmony_ci
30408c2ecf20Sopenharmony_ciout_err:
30418c2ecf20Sopenharmony_ci	pr_err("setting up logical port with id=%d failed, ret=%d\n",
30428c2ecf20Sopenharmony_ci	       logical_port_id, ret);
30438c2ecf20Sopenharmony_ci	return NULL;
30448c2ecf20Sopenharmony_ci}
30458c2ecf20Sopenharmony_ci
30468c2ecf20Sopenharmony_cistatic void ehea_shutdown_single_port(struct ehea_port *port)
30478c2ecf20Sopenharmony_ci{
30488c2ecf20Sopenharmony_ci	struct ehea_adapter *adapter = port->adapter;
30498c2ecf20Sopenharmony_ci
30508c2ecf20Sopenharmony_ci	cancel_work_sync(&port->reset_task);
30518c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&port->stats_work);
30528c2ecf20Sopenharmony_ci	unregister_netdev(port->netdev);
30538c2ecf20Sopenharmony_ci	ehea_unregister_port(port);
30548c2ecf20Sopenharmony_ci	kfree(port->mc_list);
30558c2ecf20Sopenharmony_ci	free_netdev(port->netdev);
30568c2ecf20Sopenharmony_ci	adapter->active_ports--;
30578c2ecf20Sopenharmony_ci}
30588c2ecf20Sopenharmony_ci
30598c2ecf20Sopenharmony_cistatic int ehea_setup_ports(struct ehea_adapter *adapter)
30608c2ecf20Sopenharmony_ci{
30618c2ecf20Sopenharmony_ci	struct device_node *lhea_dn;
30628c2ecf20Sopenharmony_ci	struct device_node *eth_dn = NULL;
30638c2ecf20Sopenharmony_ci
30648c2ecf20Sopenharmony_ci	const u32 *dn_log_port_id;
30658c2ecf20Sopenharmony_ci	int i = 0;
30668c2ecf20Sopenharmony_ci
30678c2ecf20Sopenharmony_ci	lhea_dn = adapter->ofdev->dev.of_node;
30688c2ecf20Sopenharmony_ci	while ((eth_dn = of_get_next_child(lhea_dn, eth_dn))) {
30698c2ecf20Sopenharmony_ci
30708c2ecf20Sopenharmony_ci		dn_log_port_id = of_get_property(eth_dn, "ibm,hea-port-no",
30718c2ecf20Sopenharmony_ci						 NULL);
30728c2ecf20Sopenharmony_ci		if (!dn_log_port_id) {
30738c2ecf20Sopenharmony_ci			pr_err("bad device node: eth_dn name=%pOF\n", eth_dn);
30748c2ecf20Sopenharmony_ci			continue;
30758c2ecf20Sopenharmony_ci		}
30768c2ecf20Sopenharmony_ci
30778c2ecf20Sopenharmony_ci		if (ehea_add_adapter_mr(adapter)) {
30788c2ecf20Sopenharmony_ci			pr_err("creating MR failed\n");
30798c2ecf20Sopenharmony_ci			of_node_put(eth_dn);
30808c2ecf20Sopenharmony_ci			return -EIO;
30818c2ecf20Sopenharmony_ci		}
30828c2ecf20Sopenharmony_ci
30838c2ecf20Sopenharmony_ci		adapter->port[i] = ehea_setup_single_port(adapter,
30848c2ecf20Sopenharmony_ci							  *dn_log_port_id,
30858c2ecf20Sopenharmony_ci							  eth_dn);
30868c2ecf20Sopenharmony_ci		if (adapter->port[i])
30878c2ecf20Sopenharmony_ci			netdev_info(adapter->port[i]->netdev,
30888c2ecf20Sopenharmony_ci				    "logical port id #%d\n", *dn_log_port_id);
30898c2ecf20Sopenharmony_ci		else
30908c2ecf20Sopenharmony_ci			ehea_remove_adapter_mr(adapter);
30918c2ecf20Sopenharmony_ci
30928c2ecf20Sopenharmony_ci		i++;
30938c2ecf20Sopenharmony_ci	}
30948c2ecf20Sopenharmony_ci	return 0;
30958c2ecf20Sopenharmony_ci}
30968c2ecf20Sopenharmony_ci
30978c2ecf20Sopenharmony_cistatic struct device_node *ehea_get_eth_dn(struct ehea_adapter *adapter,
30988c2ecf20Sopenharmony_ci					   u32 logical_port_id)
30998c2ecf20Sopenharmony_ci{
31008c2ecf20Sopenharmony_ci	struct device_node *lhea_dn;
31018c2ecf20Sopenharmony_ci	struct device_node *eth_dn = NULL;
31028c2ecf20Sopenharmony_ci	const u32 *dn_log_port_id;
31038c2ecf20Sopenharmony_ci
31048c2ecf20Sopenharmony_ci	lhea_dn = adapter->ofdev->dev.of_node;
31058c2ecf20Sopenharmony_ci	while ((eth_dn = of_get_next_child(lhea_dn, eth_dn))) {
31068c2ecf20Sopenharmony_ci
31078c2ecf20Sopenharmony_ci		dn_log_port_id = of_get_property(eth_dn, "ibm,hea-port-no",
31088c2ecf20Sopenharmony_ci						 NULL);
31098c2ecf20Sopenharmony_ci		if (dn_log_port_id)
31108c2ecf20Sopenharmony_ci			if (*dn_log_port_id == logical_port_id)
31118c2ecf20Sopenharmony_ci				return eth_dn;
31128c2ecf20Sopenharmony_ci	}
31138c2ecf20Sopenharmony_ci
31148c2ecf20Sopenharmony_ci	return NULL;
31158c2ecf20Sopenharmony_ci}
31168c2ecf20Sopenharmony_ci
31178c2ecf20Sopenharmony_cistatic ssize_t ehea_probe_port(struct device *dev,
31188c2ecf20Sopenharmony_ci			       struct device_attribute *attr,
31198c2ecf20Sopenharmony_ci			       const char *buf, size_t count)
31208c2ecf20Sopenharmony_ci{
31218c2ecf20Sopenharmony_ci	struct ehea_adapter *adapter = dev_get_drvdata(dev);
31228c2ecf20Sopenharmony_ci	struct ehea_port *port;
31238c2ecf20Sopenharmony_ci	struct device_node *eth_dn = NULL;
31248c2ecf20Sopenharmony_ci	int i;
31258c2ecf20Sopenharmony_ci
31268c2ecf20Sopenharmony_ci	u32 logical_port_id;
31278c2ecf20Sopenharmony_ci
31288c2ecf20Sopenharmony_ci	sscanf(buf, "%d", &logical_port_id);
31298c2ecf20Sopenharmony_ci
31308c2ecf20Sopenharmony_ci	port = ehea_get_port(adapter, logical_port_id);
31318c2ecf20Sopenharmony_ci
31328c2ecf20Sopenharmony_ci	if (port) {
31338c2ecf20Sopenharmony_ci		netdev_info(port->netdev, "adding port with logical port id=%d failed: port already configured\n",
31348c2ecf20Sopenharmony_ci			    logical_port_id);
31358c2ecf20Sopenharmony_ci		return -EINVAL;
31368c2ecf20Sopenharmony_ci	}
31378c2ecf20Sopenharmony_ci
31388c2ecf20Sopenharmony_ci	eth_dn = ehea_get_eth_dn(adapter, logical_port_id);
31398c2ecf20Sopenharmony_ci
31408c2ecf20Sopenharmony_ci	if (!eth_dn) {
31418c2ecf20Sopenharmony_ci		pr_info("no logical port with id %d found\n", logical_port_id);
31428c2ecf20Sopenharmony_ci		return -EINVAL;
31438c2ecf20Sopenharmony_ci	}
31448c2ecf20Sopenharmony_ci
31458c2ecf20Sopenharmony_ci	if (ehea_add_adapter_mr(adapter)) {
31468c2ecf20Sopenharmony_ci		pr_err("creating MR failed\n");
31478c2ecf20Sopenharmony_ci		of_node_put(eth_dn);
31488c2ecf20Sopenharmony_ci		return -EIO;
31498c2ecf20Sopenharmony_ci	}
31508c2ecf20Sopenharmony_ci
31518c2ecf20Sopenharmony_ci	port = ehea_setup_single_port(adapter, logical_port_id, eth_dn);
31528c2ecf20Sopenharmony_ci
31538c2ecf20Sopenharmony_ci	of_node_put(eth_dn);
31548c2ecf20Sopenharmony_ci
31558c2ecf20Sopenharmony_ci	if (port) {
31568c2ecf20Sopenharmony_ci		for (i = 0; i < EHEA_MAX_PORTS; i++)
31578c2ecf20Sopenharmony_ci			if (!adapter->port[i]) {
31588c2ecf20Sopenharmony_ci				adapter->port[i] = port;
31598c2ecf20Sopenharmony_ci				break;
31608c2ecf20Sopenharmony_ci			}
31618c2ecf20Sopenharmony_ci
31628c2ecf20Sopenharmony_ci		netdev_info(port->netdev, "added: (logical port id=%d)\n",
31638c2ecf20Sopenharmony_ci			    logical_port_id);
31648c2ecf20Sopenharmony_ci	} else {
31658c2ecf20Sopenharmony_ci		ehea_remove_adapter_mr(adapter);
31668c2ecf20Sopenharmony_ci		return -EIO;
31678c2ecf20Sopenharmony_ci	}
31688c2ecf20Sopenharmony_ci
31698c2ecf20Sopenharmony_ci	return (ssize_t) count;
31708c2ecf20Sopenharmony_ci}
31718c2ecf20Sopenharmony_ci
31728c2ecf20Sopenharmony_cistatic ssize_t ehea_remove_port(struct device *dev,
31738c2ecf20Sopenharmony_ci				struct device_attribute *attr,
31748c2ecf20Sopenharmony_ci				const char *buf, size_t count)
31758c2ecf20Sopenharmony_ci{
31768c2ecf20Sopenharmony_ci	struct ehea_adapter *adapter = dev_get_drvdata(dev);
31778c2ecf20Sopenharmony_ci	struct ehea_port *port;
31788c2ecf20Sopenharmony_ci	int i;
31798c2ecf20Sopenharmony_ci	u32 logical_port_id;
31808c2ecf20Sopenharmony_ci
31818c2ecf20Sopenharmony_ci	sscanf(buf, "%d", &logical_port_id);
31828c2ecf20Sopenharmony_ci
31838c2ecf20Sopenharmony_ci	port = ehea_get_port(adapter, logical_port_id);
31848c2ecf20Sopenharmony_ci
31858c2ecf20Sopenharmony_ci	if (port) {
31868c2ecf20Sopenharmony_ci		netdev_info(port->netdev, "removed: (logical port id=%d)\n",
31878c2ecf20Sopenharmony_ci			    logical_port_id);
31888c2ecf20Sopenharmony_ci
31898c2ecf20Sopenharmony_ci		ehea_shutdown_single_port(port);
31908c2ecf20Sopenharmony_ci
31918c2ecf20Sopenharmony_ci		for (i = 0; i < EHEA_MAX_PORTS; i++)
31928c2ecf20Sopenharmony_ci			if (adapter->port[i] == port) {
31938c2ecf20Sopenharmony_ci				adapter->port[i] = NULL;
31948c2ecf20Sopenharmony_ci				break;
31958c2ecf20Sopenharmony_ci			}
31968c2ecf20Sopenharmony_ci	} else {
31978c2ecf20Sopenharmony_ci		pr_err("removing port with logical port id=%d failed. port not configured.\n",
31988c2ecf20Sopenharmony_ci		       logical_port_id);
31998c2ecf20Sopenharmony_ci		return -EINVAL;
32008c2ecf20Sopenharmony_ci	}
32018c2ecf20Sopenharmony_ci
32028c2ecf20Sopenharmony_ci	ehea_remove_adapter_mr(adapter);
32038c2ecf20Sopenharmony_ci
32048c2ecf20Sopenharmony_ci	return (ssize_t) count;
32058c2ecf20Sopenharmony_ci}
32068c2ecf20Sopenharmony_ci
32078c2ecf20Sopenharmony_cistatic DEVICE_ATTR(probe_port, 0200, NULL, ehea_probe_port);
32088c2ecf20Sopenharmony_cistatic DEVICE_ATTR(remove_port, 0200, NULL, ehea_remove_port);
32098c2ecf20Sopenharmony_ci
32108c2ecf20Sopenharmony_cistatic int ehea_create_device_sysfs(struct platform_device *dev)
32118c2ecf20Sopenharmony_ci{
32128c2ecf20Sopenharmony_ci	int ret = device_create_file(&dev->dev, &dev_attr_probe_port);
32138c2ecf20Sopenharmony_ci	if (ret)
32148c2ecf20Sopenharmony_ci		goto out;
32158c2ecf20Sopenharmony_ci
32168c2ecf20Sopenharmony_ci	ret = device_create_file(&dev->dev, &dev_attr_remove_port);
32178c2ecf20Sopenharmony_ciout:
32188c2ecf20Sopenharmony_ci	return ret;
32198c2ecf20Sopenharmony_ci}
32208c2ecf20Sopenharmony_ci
32218c2ecf20Sopenharmony_cistatic void ehea_remove_device_sysfs(struct platform_device *dev)
32228c2ecf20Sopenharmony_ci{
32238c2ecf20Sopenharmony_ci	device_remove_file(&dev->dev, &dev_attr_probe_port);
32248c2ecf20Sopenharmony_ci	device_remove_file(&dev->dev, &dev_attr_remove_port);
32258c2ecf20Sopenharmony_ci}
32268c2ecf20Sopenharmony_ci
32278c2ecf20Sopenharmony_cistatic int ehea_reboot_notifier(struct notifier_block *nb,
32288c2ecf20Sopenharmony_ci				unsigned long action, void *unused)
32298c2ecf20Sopenharmony_ci{
32308c2ecf20Sopenharmony_ci	if (action == SYS_RESTART) {
32318c2ecf20Sopenharmony_ci		pr_info("Reboot: freeing all eHEA resources\n");
32328c2ecf20Sopenharmony_ci		ibmebus_unregister_driver(&ehea_driver);
32338c2ecf20Sopenharmony_ci	}
32348c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
32358c2ecf20Sopenharmony_ci}
32368c2ecf20Sopenharmony_ci
32378c2ecf20Sopenharmony_cistatic struct notifier_block ehea_reboot_nb = {
32388c2ecf20Sopenharmony_ci	.notifier_call = ehea_reboot_notifier,
32398c2ecf20Sopenharmony_ci};
32408c2ecf20Sopenharmony_ci
32418c2ecf20Sopenharmony_cistatic int ehea_mem_notifier(struct notifier_block *nb,
32428c2ecf20Sopenharmony_ci			     unsigned long action, void *data)
32438c2ecf20Sopenharmony_ci{
32448c2ecf20Sopenharmony_ci	int ret = NOTIFY_BAD;
32458c2ecf20Sopenharmony_ci	struct memory_notify *arg = data;
32468c2ecf20Sopenharmony_ci
32478c2ecf20Sopenharmony_ci	mutex_lock(&dlpar_mem_lock);
32488c2ecf20Sopenharmony_ci
32498c2ecf20Sopenharmony_ci	switch (action) {
32508c2ecf20Sopenharmony_ci	case MEM_CANCEL_OFFLINE:
32518c2ecf20Sopenharmony_ci		pr_info("memory offlining canceled");
32528c2ecf20Sopenharmony_ci		fallthrough;	/* re-add canceled memory block */
32538c2ecf20Sopenharmony_ci
32548c2ecf20Sopenharmony_ci	case MEM_ONLINE:
32558c2ecf20Sopenharmony_ci		pr_info("memory is going online");
32568c2ecf20Sopenharmony_ci		set_bit(__EHEA_STOP_XFER, &ehea_driver_flags);
32578c2ecf20Sopenharmony_ci		if (ehea_add_sect_bmap(arg->start_pfn, arg->nr_pages))
32588c2ecf20Sopenharmony_ci			goto out_unlock;
32598c2ecf20Sopenharmony_ci		ehea_rereg_mrs();
32608c2ecf20Sopenharmony_ci		break;
32618c2ecf20Sopenharmony_ci
32628c2ecf20Sopenharmony_ci	case MEM_GOING_OFFLINE:
32638c2ecf20Sopenharmony_ci		pr_info("memory is going offline");
32648c2ecf20Sopenharmony_ci		set_bit(__EHEA_STOP_XFER, &ehea_driver_flags);
32658c2ecf20Sopenharmony_ci		if (ehea_rem_sect_bmap(arg->start_pfn, arg->nr_pages))
32668c2ecf20Sopenharmony_ci			goto out_unlock;
32678c2ecf20Sopenharmony_ci		ehea_rereg_mrs();
32688c2ecf20Sopenharmony_ci		break;
32698c2ecf20Sopenharmony_ci
32708c2ecf20Sopenharmony_ci	default:
32718c2ecf20Sopenharmony_ci		break;
32728c2ecf20Sopenharmony_ci	}
32738c2ecf20Sopenharmony_ci
32748c2ecf20Sopenharmony_ci	ehea_update_firmware_handles();
32758c2ecf20Sopenharmony_ci	ret = NOTIFY_OK;
32768c2ecf20Sopenharmony_ci
32778c2ecf20Sopenharmony_ciout_unlock:
32788c2ecf20Sopenharmony_ci	mutex_unlock(&dlpar_mem_lock);
32798c2ecf20Sopenharmony_ci	return ret;
32808c2ecf20Sopenharmony_ci}
32818c2ecf20Sopenharmony_ci
32828c2ecf20Sopenharmony_cistatic struct notifier_block ehea_mem_nb = {
32838c2ecf20Sopenharmony_ci	.notifier_call = ehea_mem_notifier,
32848c2ecf20Sopenharmony_ci};
32858c2ecf20Sopenharmony_ci
32868c2ecf20Sopenharmony_cistatic void ehea_crash_handler(void)
32878c2ecf20Sopenharmony_ci{
32888c2ecf20Sopenharmony_ci	int i;
32898c2ecf20Sopenharmony_ci
32908c2ecf20Sopenharmony_ci	if (ehea_fw_handles.arr)
32918c2ecf20Sopenharmony_ci		for (i = 0; i < ehea_fw_handles.num_entries; i++)
32928c2ecf20Sopenharmony_ci			ehea_h_free_resource(ehea_fw_handles.arr[i].adh,
32938c2ecf20Sopenharmony_ci					     ehea_fw_handles.arr[i].fwh,
32948c2ecf20Sopenharmony_ci					     FORCE_FREE);
32958c2ecf20Sopenharmony_ci
32968c2ecf20Sopenharmony_ci	if (ehea_bcmc_regs.arr)
32978c2ecf20Sopenharmony_ci		for (i = 0; i < ehea_bcmc_regs.num_entries; i++)
32988c2ecf20Sopenharmony_ci			ehea_h_reg_dereg_bcmc(ehea_bcmc_regs.arr[i].adh,
32998c2ecf20Sopenharmony_ci					      ehea_bcmc_regs.arr[i].port_id,
33008c2ecf20Sopenharmony_ci					      ehea_bcmc_regs.arr[i].reg_type,
33018c2ecf20Sopenharmony_ci					      ehea_bcmc_regs.arr[i].macaddr,
33028c2ecf20Sopenharmony_ci					      0, H_DEREG_BCMC);
33038c2ecf20Sopenharmony_ci}
33048c2ecf20Sopenharmony_ci
33058c2ecf20Sopenharmony_cistatic atomic_t ehea_memory_hooks_registered;
33068c2ecf20Sopenharmony_ci
33078c2ecf20Sopenharmony_ci/* Register memory hooks on probe of first adapter */
33088c2ecf20Sopenharmony_cistatic int ehea_register_memory_hooks(void)
33098c2ecf20Sopenharmony_ci{
33108c2ecf20Sopenharmony_ci	int ret = 0;
33118c2ecf20Sopenharmony_ci
33128c2ecf20Sopenharmony_ci	if (atomic_inc_return(&ehea_memory_hooks_registered) > 1)
33138c2ecf20Sopenharmony_ci		return 0;
33148c2ecf20Sopenharmony_ci
33158c2ecf20Sopenharmony_ci	ret = ehea_create_busmap();
33168c2ecf20Sopenharmony_ci	if (ret) {
33178c2ecf20Sopenharmony_ci		pr_info("ehea_create_busmap failed\n");
33188c2ecf20Sopenharmony_ci		goto out;
33198c2ecf20Sopenharmony_ci	}
33208c2ecf20Sopenharmony_ci
33218c2ecf20Sopenharmony_ci	ret = register_reboot_notifier(&ehea_reboot_nb);
33228c2ecf20Sopenharmony_ci	if (ret) {
33238c2ecf20Sopenharmony_ci		pr_info("register_reboot_notifier failed\n");
33248c2ecf20Sopenharmony_ci		goto out;
33258c2ecf20Sopenharmony_ci	}
33268c2ecf20Sopenharmony_ci
33278c2ecf20Sopenharmony_ci	ret = register_memory_notifier(&ehea_mem_nb);
33288c2ecf20Sopenharmony_ci	if (ret) {
33298c2ecf20Sopenharmony_ci		pr_info("register_memory_notifier failed\n");
33308c2ecf20Sopenharmony_ci		goto out2;
33318c2ecf20Sopenharmony_ci	}
33328c2ecf20Sopenharmony_ci
33338c2ecf20Sopenharmony_ci	ret = crash_shutdown_register(ehea_crash_handler);
33348c2ecf20Sopenharmony_ci	if (ret) {
33358c2ecf20Sopenharmony_ci		pr_info("crash_shutdown_register failed\n");
33368c2ecf20Sopenharmony_ci		goto out3;
33378c2ecf20Sopenharmony_ci	}
33388c2ecf20Sopenharmony_ci
33398c2ecf20Sopenharmony_ci	return 0;
33408c2ecf20Sopenharmony_ci
33418c2ecf20Sopenharmony_ciout3:
33428c2ecf20Sopenharmony_ci	unregister_memory_notifier(&ehea_mem_nb);
33438c2ecf20Sopenharmony_ciout2:
33448c2ecf20Sopenharmony_ci	unregister_reboot_notifier(&ehea_reboot_nb);
33458c2ecf20Sopenharmony_ciout:
33468c2ecf20Sopenharmony_ci	atomic_dec(&ehea_memory_hooks_registered);
33478c2ecf20Sopenharmony_ci	return ret;
33488c2ecf20Sopenharmony_ci}
33498c2ecf20Sopenharmony_ci
33508c2ecf20Sopenharmony_cistatic void ehea_unregister_memory_hooks(void)
33518c2ecf20Sopenharmony_ci{
33528c2ecf20Sopenharmony_ci	/* Only remove the hooks if we've registered them */
33538c2ecf20Sopenharmony_ci	if (atomic_read(&ehea_memory_hooks_registered) == 0)
33548c2ecf20Sopenharmony_ci		return;
33558c2ecf20Sopenharmony_ci
33568c2ecf20Sopenharmony_ci	unregister_reboot_notifier(&ehea_reboot_nb);
33578c2ecf20Sopenharmony_ci	if (crash_shutdown_unregister(ehea_crash_handler))
33588c2ecf20Sopenharmony_ci		pr_info("failed unregistering crash handler\n");
33598c2ecf20Sopenharmony_ci	unregister_memory_notifier(&ehea_mem_nb);
33608c2ecf20Sopenharmony_ci}
33618c2ecf20Sopenharmony_ci
33628c2ecf20Sopenharmony_cistatic int ehea_probe_adapter(struct platform_device *dev)
33638c2ecf20Sopenharmony_ci{
33648c2ecf20Sopenharmony_ci	struct ehea_adapter *adapter;
33658c2ecf20Sopenharmony_ci	const u64 *adapter_handle;
33668c2ecf20Sopenharmony_ci	int ret;
33678c2ecf20Sopenharmony_ci	int i;
33688c2ecf20Sopenharmony_ci
33698c2ecf20Sopenharmony_ci	ret = ehea_register_memory_hooks();
33708c2ecf20Sopenharmony_ci	if (ret)
33718c2ecf20Sopenharmony_ci		return ret;
33728c2ecf20Sopenharmony_ci
33738c2ecf20Sopenharmony_ci	if (!dev || !dev->dev.of_node) {
33748c2ecf20Sopenharmony_ci		pr_err("Invalid ibmebus device probed\n");
33758c2ecf20Sopenharmony_ci		return -EINVAL;
33768c2ecf20Sopenharmony_ci	}
33778c2ecf20Sopenharmony_ci
33788c2ecf20Sopenharmony_ci	adapter = devm_kzalloc(&dev->dev, sizeof(*adapter), GFP_KERNEL);
33798c2ecf20Sopenharmony_ci	if (!adapter) {
33808c2ecf20Sopenharmony_ci		ret = -ENOMEM;
33818c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "no mem for ehea_adapter\n");
33828c2ecf20Sopenharmony_ci		goto out;
33838c2ecf20Sopenharmony_ci	}
33848c2ecf20Sopenharmony_ci
33858c2ecf20Sopenharmony_ci	list_add(&adapter->list, &adapter_list);
33868c2ecf20Sopenharmony_ci
33878c2ecf20Sopenharmony_ci	adapter->ofdev = dev;
33888c2ecf20Sopenharmony_ci
33898c2ecf20Sopenharmony_ci	adapter_handle = of_get_property(dev->dev.of_node, "ibm,hea-handle",
33908c2ecf20Sopenharmony_ci					 NULL);
33918c2ecf20Sopenharmony_ci	if (adapter_handle)
33928c2ecf20Sopenharmony_ci		adapter->handle = *adapter_handle;
33938c2ecf20Sopenharmony_ci
33948c2ecf20Sopenharmony_ci	if (!adapter->handle) {
33958c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "failed getting handle for adapter"
33968c2ecf20Sopenharmony_ci			" '%pOF'\n", dev->dev.of_node);
33978c2ecf20Sopenharmony_ci		ret = -ENODEV;
33988c2ecf20Sopenharmony_ci		goto out_free_ad;
33998c2ecf20Sopenharmony_ci	}
34008c2ecf20Sopenharmony_ci
34018c2ecf20Sopenharmony_ci	adapter->pd = EHEA_PD_ID;
34028c2ecf20Sopenharmony_ci
34038c2ecf20Sopenharmony_ci	platform_set_drvdata(dev, adapter);
34048c2ecf20Sopenharmony_ci
34058c2ecf20Sopenharmony_ci
34068c2ecf20Sopenharmony_ci	/* initialize adapter and ports */
34078c2ecf20Sopenharmony_ci	/* get adapter properties */
34088c2ecf20Sopenharmony_ci	ret = ehea_sense_adapter_attr(adapter);
34098c2ecf20Sopenharmony_ci	if (ret) {
34108c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "sense_adapter_attr failed: %d\n", ret);
34118c2ecf20Sopenharmony_ci		goto out_free_ad;
34128c2ecf20Sopenharmony_ci	}
34138c2ecf20Sopenharmony_ci
34148c2ecf20Sopenharmony_ci	adapter->neq = ehea_create_eq(adapter,
34158c2ecf20Sopenharmony_ci				      EHEA_NEQ, EHEA_MAX_ENTRIES_EQ, 1);
34168c2ecf20Sopenharmony_ci	if (!adapter->neq) {
34178c2ecf20Sopenharmony_ci		ret = -EIO;
34188c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "NEQ creation failed\n");
34198c2ecf20Sopenharmony_ci		goto out_free_ad;
34208c2ecf20Sopenharmony_ci	}
34218c2ecf20Sopenharmony_ci
34228c2ecf20Sopenharmony_ci	tasklet_setup(&adapter->neq_tasklet, ehea_neq_tasklet);
34238c2ecf20Sopenharmony_ci
34248c2ecf20Sopenharmony_ci	ret = ehea_create_device_sysfs(dev);
34258c2ecf20Sopenharmony_ci	if (ret)
34268c2ecf20Sopenharmony_ci		goto out_kill_eq;
34278c2ecf20Sopenharmony_ci
34288c2ecf20Sopenharmony_ci	ret = ehea_setup_ports(adapter);
34298c2ecf20Sopenharmony_ci	if (ret) {
34308c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "setup_ports failed\n");
34318c2ecf20Sopenharmony_ci		goto out_rem_dev_sysfs;
34328c2ecf20Sopenharmony_ci	}
34338c2ecf20Sopenharmony_ci
34348c2ecf20Sopenharmony_ci	ret = ibmebus_request_irq(adapter->neq->attr.ist1,
34358c2ecf20Sopenharmony_ci				  ehea_interrupt_neq, 0,
34368c2ecf20Sopenharmony_ci				  "ehea_neq", adapter);
34378c2ecf20Sopenharmony_ci	if (ret) {
34388c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "requesting NEQ IRQ failed\n");
34398c2ecf20Sopenharmony_ci		goto out_shutdown_ports;
34408c2ecf20Sopenharmony_ci	}
34418c2ecf20Sopenharmony_ci
34428c2ecf20Sopenharmony_ci	/* Handle any events that might be pending. */
34438c2ecf20Sopenharmony_ci	tasklet_hi_schedule(&adapter->neq_tasklet);
34448c2ecf20Sopenharmony_ci
34458c2ecf20Sopenharmony_ci	ret = 0;
34468c2ecf20Sopenharmony_ci	goto out;
34478c2ecf20Sopenharmony_ci
34488c2ecf20Sopenharmony_ciout_shutdown_ports:
34498c2ecf20Sopenharmony_ci	for (i = 0; i < EHEA_MAX_PORTS; i++)
34508c2ecf20Sopenharmony_ci		if (adapter->port[i]) {
34518c2ecf20Sopenharmony_ci			ehea_shutdown_single_port(adapter->port[i]);
34528c2ecf20Sopenharmony_ci			adapter->port[i] = NULL;
34538c2ecf20Sopenharmony_ci		}
34548c2ecf20Sopenharmony_ci
34558c2ecf20Sopenharmony_ciout_rem_dev_sysfs:
34568c2ecf20Sopenharmony_ci	ehea_remove_device_sysfs(dev);
34578c2ecf20Sopenharmony_ci
34588c2ecf20Sopenharmony_ciout_kill_eq:
34598c2ecf20Sopenharmony_ci	ehea_destroy_eq(adapter->neq);
34608c2ecf20Sopenharmony_ci
34618c2ecf20Sopenharmony_ciout_free_ad:
34628c2ecf20Sopenharmony_ci	list_del(&adapter->list);
34638c2ecf20Sopenharmony_ci
34648c2ecf20Sopenharmony_ciout:
34658c2ecf20Sopenharmony_ci	ehea_update_firmware_handles();
34668c2ecf20Sopenharmony_ci
34678c2ecf20Sopenharmony_ci	return ret;
34688c2ecf20Sopenharmony_ci}
34698c2ecf20Sopenharmony_ci
34708c2ecf20Sopenharmony_cistatic int ehea_remove(struct platform_device *dev)
34718c2ecf20Sopenharmony_ci{
34728c2ecf20Sopenharmony_ci	struct ehea_adapter *adapter = platform_get_drvdata(dev);
34738c2ecf20Sopenharmony_ci	int i;
34748c2ecf20Sopenharmony_ci
34758c2ecf20Sopenharmony_ci	for (i = 0; i < EHEA_MAX_PORTS; i++)
34768c2ecf20Sopenharmony_ci		if (adapter->port[i]) {
34778c2ecf20Sopenharmony_ci			ehea_shutdown_single_port(adapter->port[i]);
34788c2ecf20Sopenharmony_ci			adapter->port[i] = NULL;
34798c2ecf20Sopenharmony_ci		}
34808c2ecf20Sopenharmony_ci
34818c2ecf20Sopenharmony_ci	ehea_remove_device_sysfs(dev);
34828c2ecf20Sopenharmony_ci
34838c2ecf20Sopenharmony_ci	ibmebus_free_irq(adapter->neq->attr.ist1, adapter);
34848c2ecf20Sopenharmony_ci	tasklet_kill(&adapter->neq_tasklet);
34858c2ecf20Sopenharmony_ci
34868c2ecf20Sopenharmony_ci	ehea_destroy_eq(adapter->neq);
34878c2ecf20Sopenharmony_ci	ehea_remove_adapter_mr(adapter);
34888c2ecf20Sopenharmony_ci	list_del(&adapter->list);
34898c2ecf20Sopenharmony_ci
34908c2ecf20Sopenharmony_ci	ehea_update_firmware_handles();
34918c2ecf20Sopenharmony_ci
34928c2ecf20Sopenharmony_ci	return 0;
34938c2ecf20Sopenharmony_ci}
34948c2ecf20Sopenharmony_ci
34958c2ecf20Sopenharmony_cistatic int check_module_parm(void)
34968c2ecf20Sopenharmony_ci{
34978c2ecf20Sopenharmony_ci	int ret = 0;
34988c2ecf20Sopenharmony_ci
34998c2ecf20Sopenharmony_ci	if ((rq1_entries < EHEA_MIN_ENTRIES_QP) ||
35008c2ecf20Sopenharmony_ci	    (rq1_entries > EHEA_MAX_ENTRIES_RQ1)) {
35018c2ecf20Sopenharmony_ci		pr_info("Bad parameter: rq1_entries\n");
35028c2ecf20Sopenharmony_ci		ret = -EINVAL;
35038c2ecf20Sopenharmony_ci	}
35048c2ecf20Sopenharmony_ci	if ((rq2_entries < EHEA_MIN_ENTRIES_QP) ||
35058c2ecf20Sopenharmony_ci	    (rq2_entries > EHEA_MAX_ENTRIES_RQ2)) {
35068c2ecf20Sopenharmony_ci		pr_info("Bad parameter: rq2_entries\n");
35078c2ecf20Sopenharmony_ci		ret = -EINVAL;
35088c2ecf20Sopenharmony_ci	}
35098c2ecf20Sopenharmony_ci	if ((rq3_entries < EHEA_MIN_ENTRIES_QP) ||
35108c2ecf20Sopenharmony_ci	    (rq3_entries > EHEA_MAX_ENTRIES_RQ3)) {
35118c2ecf20Sopenharmony_ci		pr_info("Bad parameter: rq3_entries\n");
35128c2ecf20Sopenharmony_ci		ret = -EINVAL;
35138c2ecf20Sopenharmony_ci	}
35148c2ecf20Sopenharmony_ci	if ((sq_entries < EHEA_MIN_ENTRIES_QP) ||
35158c2ecf20Sopenharmony_ci	    (sq_entries > EHEA_MAX_ENTRIES_SQ)) {
35168c2ecf20Sopenharmony_ci		pr_info("Bad parameter: sq_entries\n");
35178c2ecf20Sopenharmony_ci		ret = -EINVAL;
35188c2ecf20Sopenharmony_ci	}
35198c2ecf20Sopenharmony_ci
35208c2ecf20Sopenharmony_ci	return ret;
35218c2ecf20Sopenharmony_ci}
35228c2ecf20Sopenharmony_ci
35238c2ecf20Sopenharmony_cistatic ssize_t capabilities_show(struct device_driver *drv, char *buf)
35248c2ecf20Sopenharmony_ci{
35258c2ecf20Sopenharmony_ci	return sprintf(buf, "%d", EHEA_CAPABILITIES);
35268c2ecf20Sopenharmony_ci}
35278c2ecf20Sopenharmony_ci
35288c2ecf20Sopenharmony_cistatic DRIVER_ATTR_RO(capabilities);
35298c2ecf20Sopenharmony_ci
35308c2ecf20Sopenharmony_cistatic int __init ehea_module_init(void)
35318c2ecf20Sopenharmony_ci{
35328c2ecf20Sopenharmony_ci	int ret;
35338c2ecf20Sopenharmony_ci
35348c2ecf20Sopenharmony_ci	pr_info("IBM eHEA ethernet device driver (Release %s)\n", DRV_VERSION);
35358c2ecf20Sopenharmony_ci
35368c2ecf20Sopenharmony_ci	memset(&ehea_fw_handles, 0, sizeof(ehea_fw_handles));
35378c2ecf20Sopenharmony_ci	memset(&ehea_bcmc_regs, 0, sizeof(ehea_bcmc_regs));
35388c2ecf20Sopenharmony_ci
35398c2ecf20Sopenharmony_ci	mutex_init(&ehea_fw_handles.lock);
35408c2ecf20Sopenharmony_ci	spin_lock_init(&ehea_bcmc_regs.lock);
35418c2ecf20Sopenharmony_ci
35428c2ecf20Sopenharmony_ci	ret = check_module_parm();
35438c2ecf20Sopenharmony_ci	if (ret)
35448c2ecf20Sopenharmony_ci		goto out;
35458c2ecf20Sopenharmony_ci
35468c2ecf20Sopenharmony_ci	ret = ibmebus_register_driver(&ehea_driver);
35478c2ecf20Sopenharmony_ci	if (ret) {
35488c2ecf20Sopenharmony_ci		pr_err("failed registering eHEA device driver on ebus\n");
35498c2ecf20Sopenharmony_ci		goto out;
35508c2ecf20Sopenharmony_ci	}
35518c2ecf20Sopenharmony_ci
35528c2ecf20Sopenharmony_ci	ret = driver_create_file(&ehea_driver.driver,
35538c2ecf20Sopenharmony_ci				 &driver_attr_capabilities);
35548c2ecf20Sopenharmony_ci	if (ret) {
35558c2ecf20Sopenharmony_ci		pr_err("failed to register capabilities attribute, ret=%d\n",
35568c2ecf20Sopenharmony_ci		       ret);
35578c2ecf20Sopenharmony_ci		goto out2;
35588c2ecf20Sopenharmony_ci	}
35598c2ecf20Sopenharmony_ci
35608c2ecf20Sopenharmony_ci	return ret;
35618c2ecf20Sopenharmony_ci
35628c2ecf20Sopenharmony_ciout2:
35638c2ecf20Sopenharmony_ci	ibmebus_unregister_driver(&ehea_driver);
35648c2ecf20Sopenharmony_ciout:
35658c2ecf20Sopenharmony_ci	return ret;
35668c2ecf20Sopenharmony_ci}
35678c2ecf20Sopenharmony_ci
35688c2ecf20Sopenharmony_cistatic void __exit ehea_module_exit(void)
35698c2ecf20Sopenharmony_ci{
35708c2ecf20Sopenharmony_ci	driver_remove_file(&ehea_driver.driver, &driver_attr_capabilities);
35718c2ecf20Sopenharmony_ci	ibmebus_unregister_driver(&ehea_driver);
35728c2ecf20Sopenharmony_ci	ehea_unregister_memory_hooks();
35738c2ecf20Sopenharmony_ci	kfree(ehea_fw_handles.arr);
35748c2ecf20Sopenharmony_ci	kfree(ehea_bcmc_regs.arr);
35758c2ecf20Sopenharmony_ci	ehea_destroy_busmap();
35768c2ecf20Sopenharmony_ci}
35778c2ecf20Sopenharmony_ci
35788c2ecf20Sopenharmony_cimodule_init(ehea_module_init);
35798c2ecf20Sopenharmony_cimodule_exit(ehea_module_exit);
3580