xref: /openbmc/linux/drivers/s390/net/qeth_l2_main.c (revision bb5ab541)
1ab9953ffSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
24a71df50SFrank Blaschka /*
3bbcfcdc8SFrank Blaschka  *    Copyright IBM Corp. 2007, 2009
44a71df50SFrank Blaschka  *    Author(s): Utz Bacher <utz.bacher@de.ibm.com>,
54a71df50SFrank Blaschka  *		 Frank Pavlic <fpavlic@de.ibm.com>,
64a71df50SFrank Blaschka  *		 Thomas Spatzier <tspat@de.ibm.com>,
74a71df50SFrank Blaschka  *		 Frank Blaschka <frank.blaschka@de.ibm.com>
84a71df50SFrank Blaschka  */
94a71df50SFrank Blaschka 
1074eacdb9SFrank Blaschka #define KMSG_COMPONENT "qeth"
1174eacdb9SFrank Blaschka #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
1274eacdb9SFrank Blaschka 
134a71df50SFrank Blaschka #include <linux/module.h>
144a71df50SFrank Blaschka #include <linux/moduleparam.h>
154a71df50SFrank Blaschka #include <linux/string.h>
164a71df50SFrank Blaschka #include <linux/errno.h>
174a71df50SFrank Blaschka #include <linux/kernel.h>
185a0e3ad6STejun Heo #include <linux/slab.h>
194a71df50SFrank Blaschka #include <linux/etherdevice.h>
20fa115adfSAlexandra Winter #include <linux/if_bridge.h>
21ccffad25SJiri Pirko #include <linux/list.h>
22fe5c8028SLakhvich Dmitriy #include <linux/hash.h>
23fe5c8028SLakhvich Dmitriy #include <linux/hashtable.h>
2410a6cfc0SAlexandra Winter #include <net/switchdev.h>
25a0138f59SAlexandra Winter #include <asm/chsc.h>
26fa115adfSAlexandra Winter #include <asm/css_chars.h>
27ec61bd2fSJulian Wiedmann #include <asm/setup.h>
284a71df50SFrank Blaschka #include "qeth_core.h"
29b4d72c08SEugene Crosser #include "qeth_l2.h"
304a71df50SFrank Blaschka 
31742d4d40SJulian Wiedmann static int qeth_l2_setdelmac_makerc(struct qeth_card *card, u16 retcode)
324a71df50SFrank Blaschka {
33efbbc1d5SEugene Crosser 	int rc;
344a71df50SFrank Blaschka 
35efbbc1d5SEugene Crosser 	if (retcode)
361aec42bcSThomas Richter 		QETH_CARD_TEXT_(card, 2, "err%04x", retcode);
37efbbc1d5SEugene Crosser 	switch (retcode) {
38efbbc1d5SEugene Crosser 	case IPA_RC_SUCCESS:
39efbbc1d5SEugene Crosser 		rc = 0;
40efbbc1d5SEugene Crosser 		break;
41efbbc1d5SEugene Crosser 	case IPA_RC_L2_UNSUPPORTED_CMD:
42ffb95251SEugene Crosser 		rc = -EOPNOTSUPP;
43efbbc1d5SEugene Crosser 		break;
44efbbc1d5SEugene Crosser 	case IPA_RC_L2_ADDR_TABLE_FULL:
45efbbc1d5SEugene Crosser 		rc = -ENOSPC;
46efbbc1d5SEugene Crosser 		break;
47efbbc1d5SEugene Crosser 	case IPA_RC_L2_DUP_MAC:
48efbbc1d5SEugene Crosser 	case IPA_RC_L2_DUP_LAYER3_MAC:
49bdb0cc12SJulian Wiedmann 		rc = -EADDRINUSE;
50efbbc1d5SEugene Crosser 		break;
51efbbc1d5SEugene Crosser 	case IPA_RC_L2_MAC_NOT_AUTH_BY_HYP:
52efbbc1d5SEugene Crosser 	case IPA_RC_L2_MAC_NOT_AUTH_BY_ADP:
53bdb0cc12SJulian Wiedmann 		rc = -EADDRNOTAVAIL;
54efbbc1d5SEugene Crosser 		break;
55efbbc1d5SEugene Crosser 	case IPA_RC_L2_MAC_NOT_FOUND:
56efbbc1d5SEugene Crosser 		rc = -ENOENT;
57efbbc1d5SEugene Crosser 		break;
58efbbc1d5SEugene Crosser 	default:
59efbbc1d5SEugene Crosser 		rc = -EIO;
60efbbc1d5SEugene Crosser 		break;
614a71df50SFrank Blaschka 	}
62efbbc1d5SEugene Crosser 	return rc;
634a71df50SFrank Blaschka }
644a71df50SFrank Blaschka 
65742d4d40SJulian Wiedmann static int qeth_l2_send_setdelmac_cb(struct qeth_card *card,
66742d4d40SJulian Wiedmann 				     struct qeth_reply *reply,
67742d4d40SJulian Wiedmann 				     unsigned long data)
68742d4d40SJulian Wiedmann {
69742d4d40SJulian Wiedmann 	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
70742d4d40SJulian Wiedmann 
71742d4d40SJulian Wiedmann 	return qeth_l2_setdelmac_makerc(card, cmd->hdr.return_code);
72742d4d40SJulian Wiedmann }
73742d4d40SJulian Wiedmann 
74ac988d78SJulian Wiedmann static int qeth_l2_send_setdelmac(struct qeth_card *card, __u8 *mac,
75ac988d78SJulian Wiedmann 			   enum qeth_ipa_cmds ipacmd)
76ac988d78SJulian Wiedmann {
77ac988d78SJulian Wiedmann 	struct qeth_ipa_cmd *cmd;
78ac988d78SJulian Wiedmann 	struct qeth_cmd_buffer *iob;
79ac988d78SJulian Wiedmann 
80ac988d78SJulian Wiedmann 	QETH_CARD_TEXT(card, 2, "L2sdmac");
81a59d121dSJulian Wiedmann 	iob = qeth_ipa_alloc_cmd(card, ipacmd, QETH_PROT_IPV4,
82a59d121dSJulian Wiedmann 				 IPA_DATA_SIZEOF(setdelmac));
83ac988d78SJulian Wiedmann 	if (!iob)
84ac988d78SJulian Wiedmann 		return -ENOMEM;
85ff5caa7aSJulian Wiedmann 	cmd = __ipa_cmd(iob);
8699f0b85dSJulian Wiedmann 	cmd->data.setdelmac.mac_length = ETH_ALEN;
8799f0b85dSJulian Wiedmann 	ether_addr_copy(cmd->data.setdelmac.mac, mac);
88742d4d40SJulian Wiedmann 	return qeth_send_ipa_cmd(card, iob, qeth_l2_send_setdelmac_cb, NULL);
89ac988d78SJulian Wiedmann }
90ac988d78SJulian Wiedmann 
91ac988d78SJulian Wiedmann static int qeth_l2_send_setmac(struct qeth_card *card, __u8 *mac)
92ac988d78SJulian Wiedmann {
93ac988d78SJulian Wiedmann 	int rc;
94ac988d78SJulian Wiedmann 
95ac988d78SJulian Wiedmann 	QETH_CARD_TEXT(card, 2, "L2Setmac");
96ac988d78SJulian Wiedmann 	rc = qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETVMAC);
97ac988d78SJulian Wiedmann 	if (rc == 0) {
98ac988d78SJulian Wiedmann 		dev_info(&card->gdev->dev,
99654e3d48SJulian Wiedmann 			 "MAC address %pM successfully registered\n", mac);
100ac988d78SJulian Wiedmann 	} else {
101ac988d78SJulian Wiedmann 		switch (rc) {
102bdb0cc12SJulian Wiedmann 		case -EADDRINUSE:
103ac988d78SJulian Wiedmann 			dev_warn(&card->gdev->dev,
104ac988d78SJulian Wiedmann 				"MAC address %pM already exists\n", mac);
105ac988d78SJulian Wiedmann 			break;
106bdb0cc12SJulian Wiedmann 		case -EADDRNOTAVAIL:
107ac988d78SJulian Wiedmann 			dev_warn(&card->gdev->dev,
108ac988d78SJulian Wiedmann 				"MAC address %pM is not authorized\n", mac);
109ac988d78SJulian Wiedmann 			break;
110ac988d78SJulian Wiedmann 		}
111ac988d78SJulian Wiedmann 	}
112ac988d78SJulian Wiedmann 	return rc;
113ac988d78SJulian Wiedmann }
114ac988d78SJulian Wiedmann 
1158174aa8aSJulian Wiedmann static int qeth_l2_write_mac(struct qeth_card *card, u8 *mac)
1164a71df50SFrank Blaschka {
1179d0a58fbSVasily Gorbik 	enum qeth_ipa_cmds cmd = is_multicast_ether_addr(mac) ?
1188174aa8aSJulian Wiedmann 					IPA_CMD_SETGMAC : IPA_CMD_SETVMAC;
119efbbc1d5SEugene Crosser 	int rc;
120efbbc1d5SEugene Crosser 
1218174aa8aSJulian Wiedmann 	QETH_CARD_TEXT(card, 2, "L2Wmac");
1228174aa8aSJulian Wiedmann 	rc = qeth_l2_send_setdelmac(card, mac, cmd);
123bdb0cc12SJulian Wiedmann 	if (rc == -EADDRINUSE)
124e19e5be8SJulian Wiedmann 		QETH_DBF_MESSAGE(2, "MAC already registered on device %x\n",
125e19e5be8SJulian Wiedmann 				 CARD_DEVID(card));
126efbbc1d5SEugene Crosser 	else if (rc)
127e19e5be8SJulian Wiedmann 		QETH_DBF_MESSAGE(2, "Failed to register MAC on device %x: %d\n",
128e19e5be8SJulian Wiedmann 				 CARD_DEVID(card), rc);
129efbbc1d5SEugene Crosser 	return rc;
1304a71df50SFrank Blaschka }
1314a71df50SFrank Blaschka 
1328174aa8aSJulian Wiedmann static int qeth_l2_remove_mac(struct qeth_card *card, u8 *mac)
1334a71df50SFrank Blaschka {
1349d0a58fbSVasily Gorbik 	enum qeth_ipa_cmds cmd = is_multicast_ether_addr(mac) ?
1358174aa8aSJulian Wiedmann 					IPA_CMD_DELGMAC : IPA_CMD_DELVMAC;
136efbbc1d5SEugene Crosser 	int rc;
137efbbc1d5SEugene Crosser 
1388174aa8aSJulian Wiedmann 	QETH_CARD_TEXT(card, 2, "L2Rmac");
1398174aa8aSJulian Wiedmann 	rc = qeth_l2_send_setdelmac(card, mac, cmd);
140efbbc1d5SEugene Crosser 	if (rc)
141e19e5be8SJulian Wiedmann 		QETH_DBF_MESSAGE(2, "Failed to delete MAC on device %u: %d\n",
142e19e5be8SJulian Wiedmann 				 CARD_DEVID(card), rc);
143efbbc1d5SEugene Crosser 	return rc;
1444a71df50SFrank Blaschka }
1454a71df50SFrank Blaschka 
146d0c74825SJulian Wiedmann static void qeth_l2_drain_rx_mode_cache(struct qeth_card *card)
1474a71df50SFrank Blaschka {
148fe5c8028SLakhvich Dmitriy 	struct qeth_mac *mac;
149fe5c8028SLakhvich Dmitriy 	struct hlist_node *tmp;
150fe5c8028SLakhvich Dmitriy 	int i;
1514a71df50SFrank Blaschka 
1520973292fSJulian Wiedmann 	hash_for_each_safe(card->rx_mode_addrs, i, tmp, mac, hnode) {
153fe5c8028SLakhvich Dmitriy 		hash_del(&mac->hnode);
154fe5c8028SLakhvich Dmitriy 		kfree(mac);
1554a71df50SFrank Blaschka 	}
1564a71df50SFrank Blaschka }
1574a71df50SFrank Blaschka 
158b0abc4f5SJulian Wiedmann static void qeth_l2_fill_header(struct qeth_qdio_out_q *queue,
159b0abc4f5SJulian Wiedmann 				struct qeth_hdr *hdr, struct sk_buff *skb,
160eca1d5c2SJulian Wiedmann 				int ipv, unsigned int data_len)
1614a71df50SFrank Blaschka {
162eca1d5c2SJulian Wiedmann 	int cast_type = qeth_get_ether_cast_type(skb);
1630aef8392SJulian Wiedmann 	struct vlan_ethhdr *veth = vlan_eth_hdr(skb);
1644a71df50SFrank Blaschka 
165ae79fe03SJulian Wiedmann 	hdr->hdr.l2.pkt_length = data_len;
1664a71df50SFrank Blaschka 
1670aef8392SJulian Wiedmann 	if (skb_is_gso(skb)) {
1680aef8392SJulian Wiedmann 		hdr->hdr.l2.id = QETH_HEADER_TYPE_L2_TSO;
1690aef8392SJulian Wiedmann 	} else {
1700aef8392SJulian Wiedmann 		hdr->hdr.l2.id = QETH_HEADER_TYPE_LAYER2;
171eeac0e20SJulian Wiedmann 		if (skb->ip_summed == CHECKSUM_PARTIAL)
172fc69660bSJulian Wiedmann 			qeth_tx_csum(skb, &hdr->hdr.l2.flags[1], ipv);
1730aef8392SJulian Wiedmann 	}
174fc69660bSJulian Wiedmann 
1754a71df50SFrank Blaschka 	/* set byte byte 3 to casting flags */
1764a71df50SFrank Blaschka 	if (cast_type == RTN_MULTICAST)
1774a71df50SFrank Blaschka 		hdr->hdr.l2.flags[2] |= QETH_LAYER2_FLAG_MULTICAST;
1784a71df50SFrank Blaschka 	else if (cast_type == RTN_BROADCAST)
1794a71df50SFrank Blaschka 		hdr->hdr.l2.flags[2] |= QETH_LAYER2_FLAG_BROADCAST;
1804a71df50SFrank Blaschka 	else
181ce73e10eSKlaus-Dieter Wacker 		hdr->hdr.l2.flags[2] |= QETH_LAYER2_FLAG_UNICAST;
1824a71df50SFrank Blaschka 
1834a71df50SFrank Blaschka 	/* VSWITCH relies on the VLAN
1844a71df50SFrank Blaschka 	 * information to be present in
1854a71df50SFrank Blaschka 	 * the QDIO header */
1864a71df50SFrank Blaschka 	if (veth->h_vlan_proto == __constant_htons(ETH_P_8021Q)) {
1874a71df50SFrank Blaschka 		hdr->hdr.l2.flags[2] |= QETH_LAYER2_FLAG_VLAN;
1884a71df50SFrank Blaschka 		hdr->hdr.l2.vlan_id = ntohs(veth->h_vlan_TCI);
1894a71df50SFrank Blaschka 	}
1904a71df50SFrank Blaschka }
1914a71df50SFrank Blaschka 
192742d4d40SJulian Wiedmann static int qeth_l2_setdelvlan_makerc(struct qeth_card *card, u16 retcode)
1934a71df50SFrank Blaschka {
1942aa48671SJulian Wiedmann 	if (retcode)
1952aa48671SJulian Wiedmann 		QETH_CARD_TEXT_(card, 2, "err%04x", retcode);
1962aa48671SJulian Wiedmann 
1972aa48671SJulian Wiedmann 	switch (retcode) {
1982aa48671SJulian Wiedmann 	case IPA_RC_SUCCESS:
1992aa48671SJulian Wiedmann 		return 0;
2002aa48671SJulian Wiedmann 	case IPA_RC_L2_INVALID_VLAN_ID:
2012aa48671SJulian Wiedmann 		return -EINVAL;
2022aa48671SJulian Wiedmann 	case IPA_RC_L2_DUP_VLAN_ID:
2032aa48671SJulian Wiedmann 		return -EEXIST;
2042aa48671SJulian Wiedmann 	case IPA_RC_L2_VLAN_ID_NOT_FOUND:
2052aa48671SJulian Wiedmann 		return -ENOENT;
2062aa48671SJulian Wiedmann 	case IPA_RC_L2_VLAN_ID_NOT_ALLOWED:
2072aa48671SJulian Wiedmann 		return -EPERM;
2082aa48671SJulian Wiedmann 	default:
2092aa48671SJulian Wiedmann 		return -EIO;
2102aa48671SJulian Wiedmann 	}
2112aa48671SJulian Wiedmann }
2122aa48671SJulian Wiedmann 
2132aa48671SJulian Wiedmann static int qeth_l2_send_setdelvlan_cb(struct qeth_card *card,
2142aa48671SJulian Wiedmann 				      struct qeth_reply *reply,
2152aa48671SJulian Wiedmann 				      unsigned long data)
2162aa48671SJulian Wiedmann {
2172aa48671SJulian Wiedmann 	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
2184a71df50SFrank Blaschka 
219847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 2, "L2sdvcb");
2204a71df50SFrank Blaschka 	if (cmd->hdr.return_code) {
221e19e5be8SJulian Wiedmann 		QETH_DBF_MESSAGE(2, "Error in processing VLAN %u on device %x: %#x.\n",
2222aa48671SJulian Wiedmann 				 cmd->data.setdelvlan.vlan_id,
223e19e5be8SJulian Wiedmann 				 CARD_DEVID(card), cmd->hdr.return_code);
224847a50fdSCarsten Otte 		QETH_CARD_TEXT_(card, 2, "L2VL%4x", cmd->hdr.command);
2254a71df50SFrank Blaschka 	}
226742d4d40SJulian Wiedmann 	return qeth_l2_setdelvlan_makerc(card, cmd->hdr.return_code);
2274a71df50SFrank Blaschka }
2284a71df50SFrank Blaschka 
2294a71df50SFrank Blaschka static int qeth_l2_send_setdelvlan(struct qeth_card *card, __u16 i,
2304a71df50SFrank Blaschka 				   enum qeth_ipa_cmds ipacmd)
2314a71df50SFrank Blaschka {
2324a71df50SFrank Blaschka 	struct qeth_ipa_cmd *cmd;
2334a71df50SFrank Blaschka 	struct qeth_cmd_buffer *iob;
2344a71df50SFrank Blaschka 
235847a50fdSCarsten Otte 	QETH_CARD_TEXT_(card, 4, "L2sdv%x", ipacmd);
236a59d121dSJulian Wiedmann 	iob = qeth_ipa_alloc_cmd(card, ipacmd, QETH_PROT_IPV4,
237a59d121dSJulian Wiedmann 				 IPA_DATA_SIZEOF(setdelvlan));
2381aec42bcSThomas Richter 	if (!iob)
2391aec42bcSThomas Richter 		return -ENOMEM;
240ff5caa7aSJulian Wiedmann 	cmd = __ipa_cmd(iob);
2414a71df50SFrank Blaschka 	cmd->data.setdelvlan.vlan_id = i;
242742d4d40SJulian Wiedmann 	return qeth_send_ipa_cmd(card, iob, qeth_l2_send_setdelvlan_cb, NULL);
2434a71df50SFrank Blaschka }
2444a71df50SFrank Blaschka 
24580d5c368SPatrick McHardy static int qeth_l2_vlan_rx_add_vid(struct net_device *dev,
24680d5c368SPatrick McHardy 				   __be16 proto, u16 vid)
2474a71df50SFrank Blaschka {
248509e2562SHeiko Carstens 	struct qeth_card *card = dev->ml_priv;
2494a71df50SFrank Blaschka 
250847a50fdSCarsten Otte 	QETH_CARD_TEXT_(card, 4, "aid:%d", vid);
25110651db7SUrsula Braun 	if (!vid)
2528e586137SJiri Pirko 		return 0;
253e6e771b3SJulian Wiedmann 
2545fc692a7SJulian Wiedmann 	return qeth_l2_send_setdelvlan(card, vid, IPA_CMD_SETVLAN);
2554a71df50SFrank Blaschka }
2564a71df50SFrank Blaschka 
25780d5c368SPatrick McHardy static int qeth_l2_vlan_rx_kill_vid(struct net_device *dev,
25880d5c368SPatrick McHardy 				    __be16 proto, u16 vid)
2594a71df50SFrank Blaschka {
260509e2562SHeiko Carstens 	struct qeth_card *card = dev->ml_priv;
2614a71df50SFrank Blaschka 
262847a50fdSCarsten Otte 	QETH_CARD_TEXT_(card, 4, "kid:%d", vid);
2635fc692a7SJulian Wiedmann 	if (!vid)
2645fc692a7SJulian Wiedmann 		return 0;
265e6e771b3SJulian Wiedmann 
2665fc692a7SJulian Wiedmann 	return qeth_l2_send_setdelvlan(card, vid, IPA_CMD_DELVLAN);
2674a71df50SFrank Blaschka }
2684a71df50SFrank Blaschka 
2699d6a569aSJulian Wiedmann static void qeth_l2_set_pnso_mode(struct qeth_card *card,
2709d6a569aSJulian Wiedmann 				  enum qeth_pnso_mode mode)
2719d6a569aSJulian Wiedmann {
2729d6a569aSJulian Wiedmann 	spin_lock_irq(get_ccwdev_lock(CARD_RDEV(card)));
2739d6a569aSJulian Wiedmann 	WRITE_ONCE(card->info.pnso_mode, mode);
2749d6a569aSJulian Wiedmann 	spin_unlock_irq(get_ccwdev_lock(CARD_RDEV(card)));
2759d6a569aSJulian Wiedmann 
2769d6a569aSJulian Wiedmann 	if (mode == QETH_PNSO_NONE)
2779d6a569aSJulian Wiedmann 		drain_workqueue(card->event_wq);
2789d6a569aSJulian Wiedmann }
2799d6a569aSJulian Wiedmann 
280817741a8SAlexandra Winter static void qeth_l2_dev2br_fdb_flush(struct qeth_card *card)
281817741a8SAlexandra Winter {
282817741a8SAlexandra Winter 	struct switchdev_notifier_fdb_info info;
283817741a8SAlexandra Winter 
284817741a8SAlexandra Winter 	QETH_CARD_TEXT(card, 2, "fdbflush");
285817741a8SAlexandra Winter 
286817741a8SAlexandra Winter 	info.addr = NULL;
287817741a8SAlexandra Winter 	/* flush all VLANs: */
288817741a8SAlexandra Winter 	info.vid = 0;
289817741a8SAlexandra Winter 	info.added_by_user = false;
290817741a8SAlexandra Winter 	info.offloaded = true;
291817741a8SAlexandra Winter 
292817741a8SAlexandra Winter 	call_switchdev_notifiers(SWITCHDEV_FDB_FLUSH_TO_BRIDGE,
293817741a8SAlexandra Winter 				 card->dev, &info.info, NULL);
294817741a8SAlexandra Winter }
295817741a8SAlexandra Winter 
2964a71df50SFrank Blaschka static int qeth_l2_request_initial_mac(struct qeth_card *card)
2974a71df50SFrank Blaschka {
2984a71df50SFrank Blaschka 	int rc = 0;
2994a71df50SFrank Blaschka 
30057a688aaSJulian Wiedmann 	QETH_CARD_TEXT(card, 2, "l2reqmac");
3014a71df50SFrank Blaschka 
302ec61bd2fSJulian Wiedmann 	if (MACHINE_IS_VM) {
303ec61bd2fSJulian Wiedmann 		rc = qeth_vm_request_mac(card);
304ec61bd2fSJulian Wiedmann 		if (!rc)
305ec61bd2fSJulian Wiedmann 			goto out;
306e19e5be8SJulian Wiedmann 		QETH_DBF_MESSAGE(2, "z/VM MAC Service failed on device %x: %#x\n",
307e19e5be8SJulian Wiedmann 				 CARD_DEVID(card), rc);
30857a688aaSJulian Wiedmann 		QETH_CARD_TEXT_(card, 2, "err%04x", rc);
309ec61bd2fSJulian Wiedmann 		/* fall back to alternative mechanism: */
310ec61bd2fSJulian Wiedmann 	}
311ec61bd2fSJulian Wiedmann 
312b144b99fSJulian Wiedmann 	if (!IS_OSN(card)) {
3134a71df50SFrank Blaschka 		rc = qeth_setadpparms_change_macaddr(card);
3144b7ae122SJulian Wiedmann 		if (!rc)
31521b1702aSJulian Wiedmann 			goto out;
316e19e5be8SJulian Wiedmann 		QETH_DBF_MESSAGE(2, "READ_MAC Assist failed on device %x: %#x\n",
317e19e5be8SJulian Wiedmann 				 CARD_DEVID(card), rc);
31857a688aaSJulian Wiedmann 		QETH_CARD_TEXT_(card, 2, "1err%04x", rc);
31921b1702aSJulian Wiedmann 		/* fall back once more: */
3204a71df50SFrank Blaschka 	}
32121b1702aSJulian Wiedmann 
32221b1702aSJulian Wiedmann 	/* some devices don't support a custom MAC address: */
323379ac99eSJulian Wiedmann 	if (IS_OSM(card) || IS_OSX(card))
32421b1702aSJulian Wiedmann 		return (rc) ? rc : -EADDRNOTAVAIL;
32521b1702aSJulian Wiedmann 	eth_hw_addr_random(card->dev);
32621b1702aSJulian Wiedmann 
327ec61bd2fSJulian Wiedmann out:
32857a688aaSJulian Wiedmann 	QETH_CARD_HEX(card, 2, card->dev->dev_addr, card->dev->addr_len);
3294a71df50SFrank Blaschka 	return 0;
3304a71df50SFrank Blaschka }
3314a71df50SFrank Blaschka 
332654e3d48SJulian Wiedmann static void qeth_l2_register_dev_addr(struct qeth_card *card)
333654e3d48SJulian Wiedmann {
334654e3d48SJulian Wiedmann 	if (!is_valid_ether_addr(card->dev->dev_addr))
335654e3d48SJulian Wiedmann 		qeth_l2_request_initial_mac(card);
336654e3d48SJulian Wiedmann 
337654e3d48SJulian Wiedmann 	if (!IS_OSN(card) && !qeth_l2_send_setmac(card, card->dev->dev_addr))
3389de15117SJulian Wiedmann 		card->info.dev_addr_is_registered = 1;
3399de15117SJulian Wiedmann 	else
3409de15117SJulian Wiedmann 		card->info.dev_addr_is_registered = 0;
341654e3d48SJulian Wiedmann }
342654e3d48SJulian Wiedmann 
343e22355eaSJulian Wiedmann static int qeth_l2_validate_addr(struct net_device *dev)
344e22355eaSJulian Wiedmann {
345e22355eaSJulian Wiedmann 	struct qeth_card *card = dev->ml_priv;
346e22355eaSJulian Wiedmann 
3479de15117SJulian Wiedmann 	if (card->info.dev_addr_is_registered)
348e22355eaSJulian Wiedmann 		return eth_validate_addr(dev);
349e22355eaSJulian Wiedmann 
350e22355eaSJulian Wiedmann 	QETH_CARD_TEXT(card, 4, "nomacadr");
351e22355eaSJulian Wiedmann 	return -EPERM;
352e22355eaSJulian Wiedmann }
353e22355eaSJulian Wiedmann 
3544a71df50SFrank Blaschka static int qeth_l2_set_mac_address(struct net_device *dev, void *p)
3554a71df50SFrank Blaschka {
3564a71df50SFrank Blaschka 	struct sockaddr *addr = p;
357509e2562SHeiko Carstens 	struct qeth_card *card = dev->ml_priv;
358bcacfcbcSJulian Wiedmann 	u8 old_addr[ETH_ALEN];
3594a71df50SFrank Blaschka 	int rc = 0;
3604a71df50SFrank Blaschka 
361847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 3, "setmac");
3624a71df50SFrank Blaschka 
3638024cc9eSJulian Wiedmann 	if (IS_OSM(card) || IS_OSX(card)) {
364847a50fdSCarsten Otte 		QETH_CARD_TEXT(card, 3, "setmcTYP");
3654a71df50SFrank Blaschka 		return -EOPNOTSUPP;
3664a71df50SFrank Blaschka 	}
36799f0b85dSJulian Wiedmann 	QETH_CARD_HEX(card, 3, addr->sa_data, ETH_ALEN);
368bcacfcbcSJulian Wiedmann 	if (!is_valid_ether_addr(addr->sa_data))
369bcacfcbcSJulian Wiedmann 		return -EADDRNOTAVAIL;
370bcacfcbcSJulian Wiedmann 
371bcacfcbcSJulian Wiedmann 	/* don't register the same address twice */
372bcacfcbcSJulian Wiedmann 	if (ether_addr_equal_64bits(dev->dev_addr, addr->sa_data) &&
3739de15117SJulian Wiedmann 	    card->info.dev_addr_is_registered)
374e6e771b3SJulian Wiedmann 		return 0;
375bcacfcbcSJulian Wiedmann 
376bcacfcbcSJulian Wiedmann 	/* add the new address, switch over, drop the old */
3774a71df50SFrank Blaschka 	rc = qeth_l2_send_setmac(card, addr->sa_data);
378bcacfcbcSJulian Wiedmann 	if (rc)
379e6e771b3SJulian Wiedmann 		return rc;
380bcacfcbcSJulian Wiedmann 	ether_addr_copy(old_addr, dev->dev_addr);
381bcacfcbcSJulian Wiedmann 	ether_addr_copy(dev->dev_addr, addr->sa_data);
382bcacfcbcSJulian Wiedmann 
3839de15117SJulian Wiedmann 	if (card->info.dev_addr_is_registered)
384bcacfcbcSJulian Wiedmann 		qeth_l2_remove_mac(card, old_addr);
3859de15117SJulian Wiedmann 	card->info.dev_addr_is_registered = 1;
386e6e771b3SJulian Wiedmann 	return 0;
3874a71df50SFrank Blaschka }
3884a71df50SFrank Blaschka 
38959b757a9SJulian Wiedmann static void qeth_l2_promisc_to_bridge(struct qeth_card *card, bool enable)
3900db587b0SEugene Crosser {
3910db587b0SEugene Crosser 	int role;
3920db587b0SEugene Crosser 	int rc;
3930db587b0SEugene Crosser 
3940db587b0SEugene Crosser 	QETH_CARD_TEXT(card, 3, "pmisc2br");
3950db587b0SEugene Crosser 
39659b757a9SJulian Wiedmann 	if (enable) {
3970db587b0SEugene Crosser 		if (card->options.sbp.reflect_promisc_primary)
3980db587b0SEugene Crosser 			role = QETH_SBP_ROLE_PRIMARY;
3990db587b0SEugene Crosser 		else
4000db587b0SEugene Crosser 			role = QETH_SBP_ROLE_SECONDARY;
4010db587b0SEugene Crosser 	} else
4020db587b0SEugene Crosser 		role = QETH_SBP_ROLE_NONE;
4030db587b0SEugene Crosser 
4040db587b0SEugene Crosser 	rc = qeth_bridgeport_setrole(card, role);
40559b757a9SJulian Wiedmann 	QETH_CARD_TEXT_(card, 2, "bpm%c%04x", enable ? '+' : '-', rc);
4060db587b0SEugene Crosser 	if (!rc) {
4070db587b0SEugene Crosser 		card->options.sbp.role = role;
40859b757a9SJulian Wiedmann 		card->info.promisc_mode = enable;
40959b757a9SJulian Wiedmann 	}
4100db587b0SEugene Crosser }
411fe5c8028SLakhvich Dmitriy 
41259b757a9SJulian Wiedmann static void qeth_l2_set_promisc_mode(struct qeth_card *card)
41359b757a9SJulian Wiedmann {
41459b757a9SJulian Wiedmann 	bool enable = card->dev->flags & IFF_PROMISC;
41559b757a9SJulian Wiedmann 
41659b757a9SJulian Wiedmann 	if (card->info.promisc_mode == enable)
41759b757a9SJulian Wiedmann 		return;
41859b757a9SJulian Wiedmann 
419c8183f54SJulian Wiedmann 	if (qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE)) {
42059b757a9SJulian Wiedmann 		qeth_setadp_promisc_mode(card, enable);
421c8183f54SJulian Wiedmann 	} else {
422c8183f54SJulian Wiedmann 		mutex_lock(&card->sbp_lock);
423c8183f54SJulian Wiedmann 		if (card->options.sbp.reflect_promisc)
42459b757a9SJulian Wiedmann 			qeth_l2_promisc_to_bridge(card, enable);
425c8183f54SJulian Wiedmann 		mutex_unlock(&card->sbp_lock);
426c8183f54SJulian Wiedmann 	}
427fe5c8028SLakhvich Dmitriy }
42859b757a9SJulian Wiedmann 
429fe5c8028SLakhvich Dmitriy /* New MAC address is added to the hash table and marked to be written on card
430fe5c8028SLakhvich Dmitriy  * only if there is not in the hash table storage already
431fe5c8028SLakhvich Dmitriy  *
432fe5c8028SLakhvich Dmitriy */
4334641b027SJulian Wiedmann static void qeth_l2_add_mac(struct qeth_card *card, struct netdev_hw_addr *ha)
434fe5c8028SLakhvich Dmitriy {
435cef6ff22SJulian Wiedmann 	u32 mac_hash = get_unaligned((u32 *)(&ha->addr[2]));
436fe5c8028SLakhvich Dmitriy 	struct qeth_mac *mac;
437fe5c8028SLakhvich Dmitriy 
4380973292fSJulian Wiedmann 	hash_for_each_possible(card->rx_mode_addrs, mac, hnode, mac_hash) {
43999f0b85dSJulian Wiedmann 		if (ether_addr_equal_64bits(ha->addr, mac->mac_addr)) {
4405f78e29cSLakhvich Dmitriy 			mac->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
441fe5c8028SLakhvich Dmitriy 			return;
442fe5c8028SLakhvich Dmitriy 		}
4430db587b0SEugene Crosser 	}
4440db587b0SEugene Crosser 
445fe5c8028SLakhvich Dmitriy 	mac = kzalloc(sizeof(struct qeth_mac), GFP_ATOMIC);
446fe5c8028SLakhvich Dmitriy 	if (!mac)
447fe5c8028SLakhvich Dmitriy 		return;
448fe5c8028SLakhvich Dmitriy 
44999f0b85dSJulian Wiedmann 	ether_addr_copy(mac->mac_addr, ha->addr);
4505f78e29cSLakhvich Dmitriy 	mac->disp_flag = QETH_DISP_ADDR_ADD;
451fe5c8028SLakhvich Dmitriy 
4520973292fSJulian Wiedmann 	hash_add(card->rx_mode_addrs, &mac->hnode, mac_hash);
453fe5c8028SLakhvich Dmitriy }
454fe5c8028SLakhvich Dmitriy 
455d0c74825SJulian Wiedmann static void qeth_l2_rx_mode_work(struct work_struct *work)
4564a71df50SFrank Blaschka {
457d0c74825SJulian Wiedmann 	struct qeth_card *card = container_of(work, struct qeth_card,
458d0c74825SJulian Wiedmann 					      rx_mode_work);
459d0c74825SJulian Wiedmann 	struct net_device *dev = card->dev;
460ccffad25SJiri Pirko 	struct netdev_hw_addr *ha;
461fe5c8028SLakhvich Dmitriy 	struct qeth_mac *mac;
462fe5c8028SLakhvich Dmitriy 	struct hlist_node *tmp;
463fe5c8028SLakhvich Dmitriy 	int i;
464fe5c8028SLakhvich Dmitriy 	int rc;
4654a71df50SFrank Blaschka 
466847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 3, "setmulti");
467fe5c8028SLakhvich Dmitriy 
468d0c74825SJulian Wiedmann 	netif_addr_lock_bh(dev);
46922bedad3SJiri Pirko 	netdev_for_each_mc_addr(ha, dev)
4704641b027SJulian Wiedmann 		qeth_l2_add_mac(card, ha);
47132e7bfc4SJiri Pirko 	netdev_for_each_uc_addr(ha, dev)
4724641b027SJulian Wiedmann 		qeth_l2_add_mac(card, ha);
473d0c74825SJulian Wiedmann 	netif_addr_unlock_bh(dev);
474fe5c8028SLakhvich Dmitriy 
4750973292fSJulian Wiedmann 	hash_for_each_safe(card->rx_mode_addrs, i, tmp, mac, hnode) {
47600c163f1SJulian Wiedmann 		switch (mac->disp_flag) {
47700c163f1SJulian Wiedmann 		case QETH_DISP_ADDR_DELETE:
4788174aa8aSJulian Wiedmann 			qeth_l2_remove_mac(card, mac->mac_addr);
479fe5c8028SLakhvich Dmitriy 			hash_del(&mac->hnode);
480fe5c8028SLakhvich Dmitriy 			kfree(mac);
48100c163f1SJulian Wiedmann 			break;
48200c163f1SJulian Wiedmann 		case QETH_DISP_ADDR_ADD:
4838174aa8aSJulian Wiedmann 			rc = qeth_l2_write_mac(card, mac->mac_addr);
484fe5c8028SLakhvich Dmitriy 			if (rc) {
485fe5c8028SLakhvich Dmitriy 				hash_del(&mac->hnode);
486fe5c8028SLakhvich Dmitriy 				kfree(mac);
48700c163f1SJulian Wiedmann 				break;
48800c163f1SJulian Wiedmann 			}
489df561f66SGustavo A. R. Silva 			fallthrough;
49000c163f1SJulian Wiedmann 		default:
49100c163f1SJulian Wiedmann 			/* for next call to set_rx_mode(): */
4925f78e29cSLakhvich Dmitriy 			mac->disp_flag = QETH_DISP_ADDR_DELETE;
49300c163f1SJulian Wiedmann 		}
494fe5c8028SLakhvich Dmitriy 	}
4957db2266aSFrank Blaschka 
49659b757a9SJulian Wiedmann 	qeth_l2_set_promisc_mode(card);
4974a71df50SFrank Blaschka }
4984a71df50SFrank Blaschka 
4997286384bSJulian Wiedmann static int qeth_l2_xmit_osn(struct qeth_card *card, struct sk_buff *skb,
5007286384bSJulian Wiedmann 			    struct qeth_qdio_out_q *queue)
5017286384bSJulian Wiedmann {
502b413ff8aSJulian Wiedmann 	gfp_t gfp = GFP_ATOMIC | (skb_pfmemalloc(skb) ? __GFP_MEMALLOC : 0);
50369d7ce80SJulian Wiedmann 	struct qeth_hdr *hdr = (struct qeth_hdr *)skb->data;
50469d7ce80SJulian Wiedmann 	addr_t end = (addr_t)(skb->data + sizeof(*hdr));
50569d7ce80SJulian Wiedmann 	addr_t start = (addr_t)skb->data;
50669d7ce80SJulian Wiedmann 	unsigned int elements = 0;
50769d7ce80SJulian Wiedmann 	unsigned int hd_len = 0;
50869d7ce80SJulian Wiedmann 	int rc;
5097286384bSJulian Wiedmann 
5107286384bSJulian Wiedmann 	if (skb->protocol == htons(ETH_P_IPV6))
5117286384bSJulian Wiedmann 		return -EPROTONOSUPPORT;
5127286384bSJulian Wiedmann 
51369d7ce80SJulian Wiedmann 	if (qeth_get_elements_for_range(start, end) > 1) {
51469d7ce80SJulian Wiedmann 		/* Misaligned HW header, move it to its own buffer element. */
515b413ff8aSJulian Wiedmann 		hdr = kmem_cache_alloc(qeth_core_header_cache, gfp);
51669d7ce80SJulian Wiedmann 		if (!hdr)
51769d7ce80SJulian Wiedmann 			return -ENOMEM;
51869d7ce80SJulian Wiedmann 		hd_len = sizeof(*hdr);
51969d7ce80SJulian Wiedmann 		skb_copy_from_linear_data(skb, (char *)hdr, hd_len);
52069d7ce80SJulian Wiedmann 		elements++;
52169d7ce80SJulian Wiedmann 	}
52269d7ce80SJulian Wiedmann 
52369d7ce80SJulian Wiedmann 	elements += qeth_count_elements(skb, hd_len);
5244e26c5feSJulian Wiedmann 	if (elements > queue->max_elements) {
52569d7ce80SJulian Wiedmann 		rc = -E2BIG;
52669d7ce80SJulian Wiedmann 		goto out;
52769d7ce80SJulian Wiedmann 	}
52869d7ce80SJulian Wiedmann 
52969d7ce80SJulian Wiedmann 	rc = qeth_do_send_packet(card, queue, skb, hdr, hd_len, hd_len,
53069d7ce80SJulian Wiedmann 				 elements);
53169d7ce80SJulian Wiedmann out:
53269d7ce80SJulian Wiedmann 	if (rc && hd_len)
53369d7ce80SJulian Wiedmann 		kmem_cache_free(qeth_core_header_cache, hdr);
53469d7ce80SJulian Wiedmann 	return rc;
5357286384bSJulian Wiedmann }
5367286384bSJulian Wiedmann 
537e38db6beSJulian Wiedmann static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb,
538e38db6beSJulian Wiedmann 					   struct net_device *dev)
5394a71df50SFrank Blaschka {
540509e2562SHeiko Carstens 	struct qeth_card *card = dev->ml_priv;
5413a18d754SJulian Wiedmann 	u16 txq = skb_get_queue_mapping(skb);
542290b8348SStefan Raspl 	struct qeth_qdio_out_q *queue;
5437286384bSJulian Wiedmann 	int rc;
5444a71df50SFrank Blaschka 
545eeac0e20SJulian Wiedmann 	if (!skb_is_gso(skb))
546eeac0e20SJulian Wiedmann 		qdisc_skb_cb(skb)->pkt_len = skb->len;
5473a18d754SJulian Wiedmann 	if (IS_IQD(card))
54854a50941SJulian Wiedmann 		txq = qeth_iqd_translate_txq(dev, txq);
54973dc2dafSJulian Wiedmann 	queue = card->qdio.out_qs[txq];
550b0abc4f5SJulian Wiedmann 
5515f89eca5SJulian Wiedmann 	if (IS_OSN(card))
5527286384bSJulian Wiedmann 		rc = qeth_l2_xmit_osn(card, skb, queue);
5535f89eca5SJulian Wiedmann 	else
55473dc2dafSJulian Wiedmann 		rc = qeth_xmit(card, skb, queue, qeth_get_ip_version(skb),
55558aa2491SJulian Wiedmann 			       qeth_l2_fill_header);
5564a71df50SFrank Blaschka 
557eeac0e20SJulian Wiedmann 	if (!rc)
5587286384bSJulian Wiedmann 		return NETDEV_TX_OK;
5594a71df50SFrank Blaschka 
560b0abc4f5SJulian Wiedmann 	QETH_TXQ_STAT_INC(queue, tx_dropped);
561104b4859SJulian Wiedmann 	kfree_skb(skb);
5624a71df50SFrank Blaschka 	return NETDEV_TX_OK;
5634a71df50SFrank Blaschka }
5644a71df50SFrank Blaschka 
5653a18d754SJulian Wiedmann static u16 qeth_l2_select_queue(struct net_device *dev, struct sk_buff *skb,
5663a18d754SJulian Wiedmann 				struct net_device *sb_dev)
5673a18d754SJulian Wiedmann {
56873dc2dafSJulian Wiedmann 	struct qeth_card *card = dev->ml_priv;
56973dc2dafSJulian Wiedmann 
57073dc2dafSJulian Wiedmann 	if (IS_IQD(card))
57173dc2dafSJulian Wiedmann 		return qeth_iqd_select_queue(dev, skb,
57258aa2491SJulian Wiedmann 					     qeth_get_ether_cast_type(skb),
5733a18d754SJulian Wiedmann 					     sb_dev);
5741c103cf8SJulian Wiedmann 
5751c103cf8SJulian Wiedmann 	return IS_VM_NIC(card) ? netdev_pick_tx(dev, skb, sb_dev) :
5761c103cf8SJulian Wiedmann 				 qeth_get_priority_queue(card, skb);
5773a18d754SJulian Wiedmann }
5783a18d754SJulian Wiedmann 
579d0c74825SJulian Wiedmann static void qeth_l2_set_rx_mode(struct net_device *dev)
580d0c74825SJulian Wiedmann {
581d0c74825SJulian Wiedmann 	struct qeth_card *card = dev->ml_priv;
582d0c74825SJulian Wiedmann 
583d0c74825SJulian Wiedmann 	schedule_work(&card->rx_mode_work);
584d0c74825SJulian Wiedmann }
585d0c74825SJulian Wiedmann 
586a0138f59SAlexandra Winter /**
587a0138f59SAlexandra Winter  *	qeth_l2_pnso() - perform network subchannel operation
588a0138f59SAlexandra Winter  *	@card: qeth_card structure pointer
5894fea49a7SAlexandra Winter  *	@oc: Operation Code
590a0138f59SAlexandra Winter  *	@cnc: Boolean Change-Notification Control
591a0138f59SAlexandra Winter  *	@cb: Callback function will be executed for each element
592a0138f59SAlexandra Winter  *		of the address list
593a0138f59SAlexandra Winter  *	@priv: Pointer to pass to the callback function.
594a0138f59SAlexandra Winter  *
595a0138f59SAlexandra Winter  *	Collects network information in a network address list and calls the
596a0138f59SAlexandra Winter  *	callback function for every entry in the list. If "change-notification-
597a0138f59SAlexandra Winter  *	control" is set, further changes in the address list will be reported
598a0138f59SAlexandra Winter  *	via the IPA command.
599a0138f59SAlexandra Winter  */
6004fea49a7SAlexandra Winter static int qeth_l2_pnso(struct qeth_card *card, u8 oc, int cnc,
601a0138f59SAlexandra Winter 			void (*cb)(void *priv, struct chsc_pnso_naid_l2 *entry),
602a0138f59SAlexandra Winter 			void *priv)
603a0138f59SAlexandra Winter {
604a0138f59SAlexandra Winter 	struct ccw_device *ddev = CARD_DDEV(card);
605a0138f59SAlexandra Winter 	struct chsc_pnso_area *rr;
606a0138f59SAlexandra Winter 	u32 prev_instance = 0;
607a0138f59SAlexandra Winter 	int isfirstblock = 1;
608a0138f59SAlexandra Winter 	int i, size, elems;
609a0138f59SAlexandra Winter 	int rc;
610a0138f59SAlexandra Winter 
611a0138f59SAlexandra Winter 	rr = (struct chsc_pnso_area *)get_zeroed_page(GFP_KERNEL);
612a0138f59SAlexandra Winter 	if (rr == NULL)
613a0138f59SAlexandra Winter 		return -ENOMEM;
614a0138f59SAlexandra Winter 	do {
6154fea49a7SAlexandra Winter 		QETH_CARD_TEXT(card, 2, "PNSO");
616a0138f59SAlexandra Winter 		/* on the first iteration, naihdr.resume_token will be zero */
6174fea49a7SAlexandra Winter 		rc = ccw_device_pnso(ddev, rr, oc, rr->naihdr.resume_token,
6184fea49a7SAlexandra Winter 				     cnc);
619a0138f59SAlexandra Winter 		if (rc)
620a0138f59SAlexandra Winter 			continue;
621a0138f59SAlexandra Winter 		if (cb == NULL)
622a0138f59SAlexandra Winter 			continue;
623a0138f59SAlexandra Winter 
624a0138f59SAlexandra Winter 		size = rr->naihdr.naids;
625a0138f59SAlexandra Winter 		if (size != sizeof(struct chsc_pnso_naid_l2)) {
626a0138f59SAlexandra Winter 			WARN_ON_ONCE(1);
627a0138f59SAlexandra Winter 			continue;
628a0138f59SAlexandra Winter 		}
629a0138f59SAlexandra Winter 
630a0138f59SAlexandra Winter 		elems = (rr->response.length - sizeof(struct chsc_header) -
631a0138f59SAlexandra Winter 			 sizeof(struct chsc_pnso_naihdr)) / size;
632a0138f59SAlexandra Winter 
633a0138f59SAlexandra Winter 		if (!isfirstblock && (rr->naihdr.instance != prev_instance)) {
634a0138f59SAlexandra Winter 			/* Inform the caller that they need to scrap */
635a0138f59SAlexandra Winter 			/* the data that was already reported via cb */
636a0138f59SAlexandra Winter 			rc = -EAGAIN;
637a0138f59SAlexandra Winter 			break;
638a0138f59SAlexandra Winter 		}
639a0138f59SAlexandra Winter 		isfirstblock = 0;
640a0138f59SAlexandra Winter 		prev_instance = rr->naihdr.instance;
641a0138f59SAlexandra Winter 		for (i = 0; i < elems; i++)
642a0138f59SAlexandra Winter 			(*cb)(priv, &rr->entries[i]);
643a0138f59SAlexandra Winter 	} while ((rc == -EBUSY) || (!rc && /* list stored */
644a0138f59SAlexandra Winter 		   /* resume token is non-zero => list incomplete */
645a0138f59SAlexandra Winter 		   (rr->naihdr.resume_token.t1 || rr->naihdr.resume_token.t2)));
646a0138f59SAlexandra Winter 
647a0138f59SAlexandra Winter 	if (rc)
648a0138f59SAlexandra Winter 		QETH_CARD_TEXT_(card, 2, "PNrp%04x", rr->response.code);
649a0138f59SAlexandra Winter 
650a0138f59SAlexandra Winter 	free_page((unsigned long)rr);
651a0138f59SAlexandra Winter 	return rc;
652a0138f59SAlexandra Winter }
653a0138f59SAlexandra Winter 
65410a6cfc0SAlexandra Winter static bool qeth_is_my_net_if_token(struct qeth_card *card,
65510a6cfc0SAlexandra Winter 				    struct net_if_token *token)
65610a6cfc0SAlexandra Winter {
65710a6cfc0SAlexandra Winter 	return ((card->info.ddev_devno == token->devnum) &&
65810a6cfc0SAlexandra Winter 		(card->info.cssid == token->cssid) &&
65910a6cfc0SAlexandra Winter 		(card->info.iid == token->iid) &&
66010a6cfc0SAlexandra Winter 		(card->info.ssid == token->ssid) &&
66110a6cfc0SAlexandra Winter 		(card->info.chpid == token->chpid) &&
66210a6cfc0SAlexandra Winter 		(card->info.chid == token->chid));
66310a6cfc0SAlexandra Winter }
66410a6cfc0SAlexandra Winter 
66510a6cfc0SAlexandra Winter /**
66610a6cfc0SAlexandra Winter  *	qeth_l2_dev2br_fdb_notify() - update fdb of master bridge
66710a6cfc0SAlexandra Winter  *	@card:	qeth_card structure pointer
66810a6cfc0SAlexandra Winter  *	@code:	event bitmask: high order bit 0x80 set to
66910a6cfc0SAlexandra Winter  *				1 - removal of an object
67010a6cfc0SAlexandra Winter  *				0 - addition of an object
67110a6cfc0SAlexandra Winter  *			       Object type(s):
67210a6cfc0SAlexandra Winter  *				0x01 - VLAN, 0x02 - MAC, 0x03 - VLAN and MAC
67310a6cfc0SAlexandra Winter  *	@token: "network token" structure identifying 'physical' location
67410a6cfc0SAlexandra Winter  *		of the target
67510a6cfc0SAlexandra Winter  *	@addr_lnid: structure with MAC address and VLAN ID of the target
67610a6cfc0SAlexandra Winter  */
67710a6cfc0SAlexandra Winter static void qeth_l2_dev2br_fdb_notify(struct qeth_card *card, u8 code,
67810a6cfc0SAlexandra Winter 				      struct net_if_token *token,
67910a6cfc0SAlexandra Winter 				      struct mac_addr_lnid *addr_lnid)
68010a6cfc0SAlexandra Winter {
68110a6cfc0SAlexandra Winter 	struct switchdev_notifier_fdb_info info;
68210a6cfc0SAlexandra Winter 	u8 ntfy_mac[ETH_ALEN];
68310a6cfc0SAlexandra Winter 
68410a6cfc0SAlexandra Winter 	ether_addr_copy(ntfy_mac, addr_lnid->mac);
68510a6cfc0SAlexandra Winter 	/* Ignore VLAN only changes */
68610a6cfc0SAlexandra Winter 	if (!(code & IPA_ADDR_CHANGE_CODE_MACADDR))
68710a6cfc0SAlexandra Winter 		return;
68810a6cfc0SAlexandra Winter 	/* Ignore mcast entries */
68910a6cfc0SAlexandra Winter 	if (is_multicast_ether_addr(ntfy_mac))
69010a6cfc0SAlexandra Winter 		return;
69110a6cfc0SAlexandra Winter 	/* Ignore my own addresses */
69210a6cfc0SAlexandra Winter 	if (qeth_is_my_net_if_token(card, token))
69310a6cfc0SAlexandra Winter 		return;
69410a6cfc0SAlexandra Winter 
69510a6cfc0SAlexandra Winter 	info.addr = ntfy_mac;
69610a6cfc0SAlexandra Winter 	/* don't report VLAN IDs */
69710a6cfc0SAlexandra Winter 	info.vid = 0;
69810a6cfc0SAlexandra Winter 	info.added_by_user = false;
69910a6cfc0SAlexandra Winter 	info.offloaded = true;
70010a6cfc0SAlexandra Winter 
70110a6cfc0SAlexandra Winter 	if (code & IPA_ADDR_CHANGE_CODE_REMOVAL) {
70210a6cfc0SAlexandra Winter 		call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE,
70310a6cfc0SAlexandra Winter 					 card->dev, &info.info, NULL);
70410a6cfc0SAlexandra Winter 		QETH_CARD_TEXT(card, 4, "andelmac");
70510a6cfc0SAlexandra Winter 		QETH_CARD_TEXT_(card, 4,
70610a6cfc0SAlexandra Winter 				"mc%012lx", ether_addr_to_u64(ntfy_mac));
70710a6cfc0SAlexandra Winter 	} else {
70810a6cfc0SAlexandra Winter 		call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE,
70910a6cfc0SAlexandra Winter 					 card->dev, &info.info, NULL);
71010a6cfc0SAlexandra Winter 		QETH_CARD_TEXT(card, 4, "anaddmac");
71110a6cfc0SAlexandra Winter 		QETH_CARD_TEXT_(card, 4,
71210a6cfc0SAlexandra Winter 				"mc%012lx", ether_addr_to_u64(ntfy_mac));
71310a6cfc0SAlexandra Winter 	}
71410a6cfc0SAlexandra Winter }
71510a6cfc0SAlexandra Winter 
716817741a8SAlexandra Winter static void qeth_l2_dev2br_an_set_cb(void *priv,
717817741a8SAlexandra Winter 				     struct chsc_pnso_naid_l2 *entry)
718817741a8SAlexandra Winter {
719817741a8SAlexandra Winter 	u8 code = IPA_ADDR_CHANGE_CODE_MACADDR;
720817741a8SAlexandra Winter 	struct qeth_card *card = priv;
721817741a8SAlexandra Winter 
722817741a8SAlexandra Winter 	if (entry->addr_lnid.lnid < VLAN_N_VID)
723817741a8SAlexandra Winter 		code |= IPA_ADDR_CHANGE_CODE_VLANID;
724817741a8SAlexandra Winter 	qeth_l2_dev2br_fdb_notify(card, code,
725817741a8SAlexandra Winter 				  (struct net_if_token *)&entry->nit,
726817741a8SAlexandra Winter 				  (struct mac_addr_lnid *)&entry->addr_lnid);
727817741a8SAlexandra Winter }
728817741a8SAlexandra Winter 
729817741a8SAlexandra Winter /**
730817741a8SAlexandra Winter  *	qeth_l2_dev2br_an_set() -
731817741a8SAlexandra Winter  *	Enable or disable 'dev to bridge network address notification'
732817741a8SAlexandra Winter  *	@card: qeth_card structure pointer
733817741a8SAlexandra Winter  *	@enable: Enable or disable 'dev to bridge network address notification'
734817741a8SAlexandra Winter  *
735817741a8SAlexandra Winter  *	Returns negative errno-compatible error indication or 0 on success.
736817741a8SAlexandra Winter  *
737817741a8SAlexandra Winter  *	On enable, emits a series of address notifications for all
738817741a8SAlexandra Winter  *	currently registered hosts.
739817741a8SAlexandra Winter  *
740817741a8SAlexandra Winter  *	Must be called under rtnl_lock
741817741a8SAlexandra Winter  */
742817741a8SAlexandra Winter static int qeth_l2_dev2br_an_set(struct qeth_card *card, bool enable)
743817741a8SAlexandra Winter {
744817741a8SAlexandra Winter 	int rc;
745817741a8SAlexandra Winter 
746817741a8SAlexandra Winter 	if (enable) {
747817741a8SAlexandra Winter 		QETH_CARD_TEXT(card, 2, "anseton");
748817741a8SAlexandra Winter 		rc = qeth_l2_pnso(card, PNSO_OC_NET_ADDR_INFO, 1,
749817741a8SAlexandra Winter 				  qeth_l2_dev2br_an_set_cb, card);
750817741a8SAlexandra Winter 		if (rc == -EAGAIN)
751817741a8SAlexandra Winter 			/* address notification enabled, but inconsistent
752817741a8SAlexandra Winter 			 * addresses reported -> disable address notification
753817741a8SAlexandra Winter 			 */
754817741a8SAlexandra Winter 			qeth_l2_pnso(card, PNSO_OC_NET_ADDR_INFO, 0,
755817741a8SAlexandra Winter 				     NULL, NULL);
756817741a8SAlexandra Winter 	} else {
757817741a8SAlexandra Winter 		QETH_CARD_TEXT(card, 2, "ansetoff");
758817741a8SAlexandra Winter 		rc = qeth_l2_pnso(card, PNSO_OC_NET_ADDR_INFO, 0, NULL, NULL);
759817741a8SAlexandra Winter 	}
760817741a8SAlexandra Winter 
761817741a8SAlexandra Winter 	return rc;
762817741a8SAlexandra Winter }
763817741a8SAlexandra Winter 
764780b6e7dSAlexandra Winter static int qeth_l2_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
765780b6e7dSAlexandra Winter 				  struct net_device *dev, u32 filter_mask,
766780b6e7dSAlexandra Winter 				  int nlflags)
767780b6e7dSAlexandra Winter {
768780b6e7dSAlexandra Winter 	struct qeth_priv *priv = netdev_priv(dev);
769780b6e7dSAlexandra Winter 	struct qeth_card *card = dev->ml_priv;
770780b6e7dSAlexandra Winter 	u16 mode = BRIDGE_MODE_UNDEF;
771780b6e7dSAlexandra Winter 
772780b6e7dSAlexandra Winter 	/* Do not even show qeth devs that cannot do bridge_setlink */
773780b6e7dSAlexandra Winter 	if (!priv->brport_hw_features || !netif_device_present(dev) ||
774780b6e7dSAlexandra Winter 	    qeth_bridgeport_is_in_use(card))
775780b6e7dSAlexandra Winter 		return -EOPNOTSUPP;
776780b6e7dSAlexandra Winter 
777780b6e7dSAlexandra Winter 	return ndo_dflt_bridge_getlink(skb, pid, seq, dev,
778780b6e7dSAlexandra Winter 				       mode, priv->brport_features,
779780b6e7dSAlexandra Winter 				       priv->brport_hw_features,
780780b6e7dSAlexandra Winter 				       nlflags, filter_mask, NULL);
781780b6e7dSAlexandra Winter }
782780b6e7dSAlexandra Winter 
783521c65b6SAlexandra Winter static const struct nla_policy qeth_brport_policy[IFLA_BRPORT_MAX + 1] = {
784521c65b6SAlexandra Winter 	[IFLA_BRPORT_LEARNING_SYNC]	= { .type = NLA_U8 },
785521c65b6SAlexandra Winter };
786521c65b6SAlexandra Winter 
787521c65b6SAlexandra Winter /**
788521c65b6SAlexandra Winter  *	qeth_l2_bridge_setlink() - set bridgeport attributes
789521c65b6SAlexandra Winter  *	@dev: netdevice
790521c65b6SAlexandra Winter  *	@nlh: netlink message header
791521c65b6SAlexandra Winter  *	@flags: bridge flags (here: BRIDGE_FLAGS_SELF)
792521c65b6SAlexandra Winter  *	@extack: extended ACK report struct
793521c65b6SAlexandra Winter  *
794521c65b6SAlexandra Winter  *	Called under rtnl_lock
795521c65b6SAlexandra Winter  */
796521c65b6SAlexandra Winter static int qeth_l2_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
797521c65b6SAlexandra Winter 				  u16 flags, struct netlink_ext_ack *extack)
798521c65b6SAlexandra Winter {
799521c65b6SAlexandra Winter 	struct qeth_priv *priv = netdev_priv(dev);
800521c65b6SAlexandra Winter 	struct nlattr *bp_tb[IFLA_BRPORT_MAX + 1];
801521c65b6SAlexandra Winter 	struct qeth_card *card = dev->ml_priv;
802521c65b6SAlexandra Winter 	struct nlattr *attr, *nested_attr;
803521c65b6SAlexandra Winter 	bool enable, has_protinfo = false;
804521c65b6SAlexandra Winter 	int rem1, rem2;
805521c65b6SAlexandra Winter 	int rc;
806521c65b6SAlexandra Winter 
807521c65b6SAlexandra Winter 	if (!netif_device_present(dev))
808521c65b6SAlexandra Winter 		return -ENODEV;
809521c65b6SAlexandra Winter 	if (!(priv->brport_hw_features))
810521c65b6SAlexandra Winter 		return -EOPNOTSUPP;
811521c65b6SAlexandra Winter 
812521c65b6SAlexandra Winter 	nlmsg_for_each_attr(attr, nlh, sizeof(struct ifinfomsg), rem1) {
813521c65b6SAlexandra Winter 		if (nla_type(attr) == IFLA_PROTINFO) {
814521c65b6SAlexandra Winter 			rc = nla_parse_nested(bp_tb, IFLA_BRPORT_MAX, attr,
815521c65b6SAlexandra Winter 					      qeth_brport_policy, extack);
816521c65b6SAlexandra Winter 			if (rc)
817521c65b6SAlexandra Winter 				return rc;
818521c65b6SAlexandra Winter 			has_protinfo = true;
819521c65b6SAlexandra Winter 		} else if (nla_type(attr) == IFLA_AF_SPEC) {
820521c65b6SAlexandra Winter 			nla_for_each_nested(nested_attr, attr, rem2) {
821521c65b6SAlexandra Winter 				if (nla_type(nested_attr) == IFLA_BRIDGE_FLAGS)
822521c65b6SAlexandra Winter 					continue;
823521c65b6SAlexandra Winter 				NL_SET_ERR_MSG_ATTR(extack, nested_attr,
824521c65b6SAlexandra Winter 						    "Unsupported attribute");
825521c65b6SAlexandra Winter 				return -EINVAL;
826521c65b6SAlexandra Winter 			}
827521c65b6SAlexandra Winter 		} else {
828521c65b6SAlexandra Winter 			NL_SET_ERR_MSG_ATTR(extack, attr, "Unsupported attribute");
829521c65b6SAlexandra Winter 			return -EINVAL;
830521c65b6SAlexandra Winter 		}
831521c65b6SAlexandra Winter 	}
832521c65b6SAlexandra Winter 	if (!has_protinfo)
833521c65b6SAlexandra Winter 		return 0;
834521c65b6SAlexandra Winter 	if (!bp_tb[IFLA_BRPORT_LEARNING_SYNC])
835521c65b6SAlexandra Winter 		return -EINVAL;
836521c65b6SAlexandra Winter 	enable = !!nla_get_u8(bp_tb[IFLA_BRPORT_LEARNING_SYNC]);
837521c65b6SAlexandra Winter 
838521c65b6SAlexandra Winter 	if (enable == !!(priv->brport_features & BR_LEARNING_SYNC))
839521c65b6SAlexandra Winter 		return 0;
840521c65b6SAlexandra Winter 
841521c65b6SAlexandra Winter 	mutex_lock(&card->sbp_lock);
842521c65b6SAlexandra Winter 	/* do not change anything if BridgePort is enabled */
843521c65b6SAlexandra Winter 	if (qeth_bridgeport_is_in_use(card)) {
844521c65b6SAlexandra Winter 		NL_SET_ERR_MSG(extack, "n/a (BridgePort)");
845521c65b6SAlexandra Winter 		rc = -EBUSY;
846521c65b6SAlexandra Winter 	} else if (enable) {
847521c65b6SAlexandra Winter 		qeth_l2_set_pnso_mode(card, QETH_PNSO_ADDR_INFO);
848521c65b6SAlexandra Winter 		rc = qeth_l2_dev2br_an_set(card, true);
849521c65b6SAlexandra Winter 		if (rc)
850521c65b6SAlexandra Winter 			qeth_l2_set_pnso_mode(card, QETH_PNSO_NONE);
851521c65b6SAlexandra Winter 		else
852521c65b6SAlexandra Winter 			priv->brport_features |= BR_LEARNING_SYNC;
853521c65b6SAlexandra Winter 	} else {
854521c65b6SAlexandra Winter 		rc = qeth_l2_dev2br_an_set(card, false);
855521c65b6SAlexandra Winter 		if (!rc) {
856521c65b6SAlexandra Winter 			qeth_l2_set_pnso_mode(card, QETH_PNSO_NONE);
857521c65b6SAlexandra Winter 			priv->brport_features ^= BR_LEARNING_SYNC;
858521c65b6SAlexandra Winter 			qeth_l2_dev2br_fdb_flush(card);
859521c65b6SAlexandra Winter 		}
860521c65b6SAlexandra Winter 	}
861521c65b6SAlexandra Winter 	mutex_unlock(&card->sbp_lock);
862521c65b6SAlexandra Winter 
863521c65b6SAlexandra Winter 	return rc;
864521c65b6SAlexandra Winter }
865521c65b6SAlexandra Winter 
8663d58cefdSFrank Blaschka static const struct net_device_ops qeth_l2_netdev_ops = {
867e22355eaSJulian Wiedmann 	.ndo_open		= qeth_open,
868e22355eaSJulian Wiedmann 	.ndo_stop		= qeth_stop,
869b0abc4f5SJulian Wiedmann 	.ndo_get_stats64	= qeth_get_stats64,
8708403b13cSFrank Blaschka 	.ndo_start_xmit		= qeth_l2_hard_start_xmit,
8716d69b1f1SJulian Wiedmann 	.ndo_features_check	= qeth_features_check,
8723a18d754SJulian Wiedmann 	.ndo_select_queue	= qeth_l2_select_queue,
873e22355eaSJulian Wiedmann 	.ndo_validate_addr	= qeth_l2_validate_addr,
874fe5c8028SLakhvich Dmitriy 	.ndo_set_rx_mode	= qeth_l2_set_rx_mode,
875942d6984SJulian Wiedmann 	.ndo_do_ioctl		= qeth_do_ioctl,
8768403b13cSFrank Blaschka 	.ndo_set_mac_address    = qeth_l2_set_mac_address,
8778403b13cSFrank Blaschka 	.ndo_vlan_rx_add_vid	= qeth_l2_vlan_rx_add_vid,
8788403b13cSFrank Blaschka 	.ndo_vlan_rx_kill_vid   = qeth_l2_vlan_rx_kill_vid,
8798403b13cSFrank Blaschka 	.ndo_tx_timeout	   	= qeth_tx_timeout,
8808f43fb00SThomas Richter 	.ndo_fix_features	= qeth_fix_features,
881780b6e7dSAlexandra Winter 	.ndo_set_features	= qeth_set_features,
882780b6e7dSAlexandra Winter 	.ndo_bridge_getlink	= qeth_l2_bridge_getlink,
883521c65b6SAlexandra Winter 	.ndo_bridge_setlink	= qeth_l2_bridge_setlink,
8848403b13cSFrank Blaschka };
8858403b13cSFrank Blaschka 
8868024cc9eSJulian Wiedmann static const struct net_device_ops qeth_osn_netdev_ops = {
8878024cc9eSJulian Wiedmann 	.ndo_open		= qeth_open,
8888024cc9eSJulian Wiedmann 	.ndo_stop		= qeth_stop,
8898024cc9eSJulian Wiedmann 	.ndo_get_stats64	= qeth_get_stats64,
8908024cc9eSJulian Wiedmann 	.ndo_start_xmit		= qeth_l2_hard_start_xmit,
8918024cc9eSJulian Wiedmann 	.ndo_validate_addr	= eth_validate_addr,
8928024cc9eSJulian Wiedmann 	.ndo_tx_timeout		= qeth_tx_timeout,
8938024cc9eSJulian Wiedmann };
8948024cc9eSJulian Wiedmann 
895cd652be5SJulian Wiedmann static int qeth_l2_setup_netdev(struct qeth_card *card)
8964a71df50SFrank Blaschka {
8978024cc9eSJulian Wiedmann 	if (IS_OSN(card)) {
8988024cc9eSJulian Wiedmann 		card->dev->netdev_ops = &qeth_osn_netdev_ops;
89925e2c341SJulian Wiedmann 		card->dev->flags |= IFF_NOARP;
9008024cc9eSJulian Wiedmann 		goto add_napi;
9018024cc9eSJulian Wiedmann 	}
9028024cc9eSJulian Wiedmann 
9035f89eca5SJulian Wiedmann 	card->dev->needed_headroom = sizeof(struct qeth_hdr);
9048024cc9eSJulian Wiedmann 	card->dev->netdev_ops = &qeth_l2_netdev_ops;
9058024cc9eSJulian Wiedmann 	card->dev->priv_flags |= IFF_UNICAST_FLT;
9069400c53fSJulian Wiedmann 
9075fc692a7SJulian Wiedmann 	if (IS_OSM(card)) {
9089400c53fSJulian Wiedmann 		card->dev->features |= NETIF_F_VLAN_CHALLENGED;
9095fc692a7SJulian Wiedmann 	} else {
9105fc692a7SJulian Wiedmann 		if (!IS_VM_NIC(card))
9115fc692a7SJulian Wiedmann 			card->dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER;
912f646968fSPatrick McHardy 		card->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
9135fc692a7SJulian Wiedmann 	}
9140f342945SJulian Wiedmann 
915379ac99eSJulian Wiedmann 	if (IS_OSD(card) && !IS_VM_NIC(card)) {
9166d69b1f1SJulian Wiedmann 		card->dev->features |= NETIF_F_SG;
9178f43fb00SThomas Richter 		/* OSA 3S and earlier has no RX/TX support */
9189bdc4411SHans Wippel 		if (qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM)) {
9198f43fb00SThomas Richter 			card->dev->hw_features |= NETIF_F_IP_CSUM;
9209bdc4411SHans Wippel 			card->dev->vlan_features |= NETIF_F_IP_CSUM;
9219bdc4411SHans Wippel 		}
9224d7def2aSThomas Richter 	}
923571f9dd8SKittipon Meesompop 	if (qeth_is_supported6(card, IPA_OUTBOUND_CHECKSUM_V6)) {
924571f9dd8SKittipon Meesompop 		card->dev->hw_features |= NETIF_F_IPV6_CSUM;
925571f9dd8SKittipon Meesompop 		card->dev->vlan_features |= NETIF_F_IPV6_CSUM;
926571f9dd8SKittipon Meesompop 	}
927d7e6ed97SKittipon Meesompop 	if (qeth_is_supported(card, IPA_INBOUND_CHECKSUM) ||
928d7e6ed97SKittipon Meesompop 	    qeth_is_supported6(card, IPA_INBOUND_CHECKSUM_V6)) {
929d7e6ed97SKittipon Meesompop 		card->dev->hw_features |= NETIF_F_RXCSUM;
930d7e6ed97SKittipon Meesompop 		card->dev->vlan_features |= NETIF_F_RXCSUM;
931d7e6ed97SKittipon Meesompop 	}
9320aef8392SJulian Wiedmann 	if (qeth_is_supported(card, IPA_OUTBOUND_TSO)) {
9330aef8392SJulian Wiedmann 		card->dev->hw_features |= NETIF_F_TSO;
9340aef8392SJulian Wiedmann 		card->dev->vlan_features |= NETIF_F_TSO;
9350aef8392SJulian Wiedmann 	}
9360aef8392SJulian Wiedmann 	if (qeth_is_supported6(card, IPA_OUTBOUND_TSO)) {
9370aef8392SJulian Wiedmann 		card->dev->hw_features |= NETIF_F_TSO6;
9380aef8392SJulian Wiedmann 		card->dev->vlan_features |= NETIF_F_TSO6;
9390aef8392SJulian Wiedmann 	}
9400aef8392SJulian Wiedmann 
9410aef8392SJulian Wiedmann 	if (card->dev->hw_features & (NETIF_F_TSO | NETIF_F_TSO6)) {
9420aef8392SJulian Wiedmann 		card->dev->needed_headroom = sizeof(struct qeth_hdr_tso);
943c619e9a6SJulian Wiedmann 		netif_keep_dst(card->dev);
9440aef8392SJulian Wiedmann 		netif_set_gso_max_size(card->dev,
9450aef8392SJulian Wiedmann 				       PAGE_SIZE * (QDIO_MAX_ELEMENTS_PER_BUFFER - 1));
9460aef8392SJulian Wiedmann 	}
9470d6f02d3SJulian Wiedmann 
9488024cc9eSJulian Wiedmann add_napi:
949d73ef324SJulian Wiedmann 	netif_napi_add(card->dev, &card->napi, qeth_poll, QETH_NAPI_WEIGHT);
950cd652be5SJulian Wiedmann 	return register_netdev(card->dev);
9514a71df50SFrank Blaschka }
9524a71df50SFrank Blaschka 
953521c10eaSJulian Wiedmann static void qeth_l2_trace_features(struct qeth_card *card)
954521c10eaSJulian Wiedmann {
955a45b3fafSHans Wippel 	/* Set BridgePort features */
956a45b3fafSHans Wippel 	QETH_CARD_TEXT(card, 2, "featuSBP");
957521c10eaSJulian Wiedmann 	QETH_CARD_HEX(card, 2, &card->options.sbp.supported_funcs,
958521c10eaSJulian Wiedmann 		      sizeof(card->options.sbp.supported_funcs));
959a45b3fafSHans Wippel 	/* VNIC Characteristics features */
960a45b3fafSHans Wippel 	QETH_CARD_TEXT(card, 2, "feaVNICC");
961a45b3fafSHans Wippel 	QETH_CARD_HEX(card, 2, &card->options.vnicc.sup_chars,
962a45b3fafSHans Wippel 		      sizeof(card->options.vnicc.sup_chars));
963521c10eaSJulian Wiedmann }
964521c10eaSJulian Wiedmann 
9658ca769e2SJulian Wiedmann static void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card)
9668ca769e2SJulian Wiedmann {
9678ca769e2SJulian Wiedmann 	if (!card->options.sbp.reflect_promisc &&
9688ca769e2SJulian Wiedmann 	    card->options.sbp.role != QETH_SBP_ROLE_NONE) {
9698ca769e2SJulian Wiedmann 		/* Conditional to avoid spurious error messages */
9708ca769e2SJulian Wiedmann 		qeth_bridgeport_setrole(card, card->options.sbp.role);
9718ca769e2SJulian Wiedmann 		/* Let the callback function refresh the stored role value. */
9728ca769e2SJulian Wiedmann 		qeth_bridgeport_query_ports(card, &card->options.sbp.role,
9738ca769e2SJulian Wiedmann 					    NULL);
9748ca769e2SJulian Wiedmann 	}
9758ca769e2SJulian Wiedmann 	if (card->options.sbp.hostnotification) {
9768ca769e2SJulian Wiedmann 		if (qeth_bridgeport_an_set(card, 1))
9778ca769e2SJulian Wiedmann 			card->options.sbp.hostnotification = 0;
9788ca769e2SJulian Wiedmann 	}
9798ca769e2SJulian Wiedmann }
9808ca769e2SJulian Wiedmann 
981fa115adfSAlexandra Winter /**
982fa115adfSAlexandra Winter  *	qeth_l2_detect_dev2br_support() -
983fa115adfSAlexandra Winter  *	Detect whether this card supports 'dev to bridge fdb network address
984fa115adfSAlexandra Winter  *	change notification' and thus can support the learning_sync bridgeport
985fa115adfSAlexandra Winter  *	attribute
986fa115adfSAlexandra Winter  *	@card: qeth_card structure pointer
987fa115adfSAlexandra Winter  *
988fa115adfSAlexandra Winter  *	This is a destructive test and must be called before dev2br or
989fa115adfSAlexandra Winter  *	bridgeport address notification is enabled!
990fa115adfSAlexandra Winter  */
991fa115adfSAlexandra Winter static void qeth_l2_detect_dev2br_support(struct qeth_card *card)
992fa115adfSAlexandra Winter {
993fa115adfSAlexandra Winter 	struct qeth_priv *priv = netdev_priv(card->dev);
994fa115adfSAlexandra Winter 	bool dev2br_supported;
995fa115adfSAlexandra Winter 	int rc;
996fa115adfSAlexandra Winter 
997fa115adfSAlexandra Winter 	QETH_CARD_TEXT(card, 2, "d2brsup");
998fa115adfSAlexandra Winter 	if (!IS_IQD(card))
999fa115adfSAlexandra Winter 		return;
1000fa115adfSAlexandra Winter 
1001fa115adfSAlexandra Winter 	/* dev2br requires valid cssid,iid,chid */
1002fa115adfSAlexandra Winter 	if (!card->info.ids_valid) {
1003fa115adfSAlexandra Winter 		dev2br_supported = false;
1004fa115adfSAlexandra Winter 	} else if (css_general_characteristics.enarf) {
1005fa115adfSAlexandra Winter 		dev2br_supported = true;
1006fa115adfSAlexandra Winter 	} else {
1007fa115adfSAlexandra Winter 		/* Old machines don't have the feature bit:
1008fa115adfSAlexandra Winter 		 * Probe by testing whether a disable succeeds
1009fa115adfSAlexandra Winter 		 */
1010fa115adfSAlexandra Winter 		rc = qeth_l2_pnso(card, PNSO_OC_NET_ADDR_INFO, 0, NULL, NULL);
1011fa115adfSAlexandra Winter 		dev2br_supported = !rc;
1012fa115adfSAlexandra Winter 	}
1013fa115adfSAlexandra Winter 	QETH_CARD_TEXT_(card, 2, "D2Bsup%02x", dev2br_supported);
1014fa115adfSAlexandra Winter 
1015fa115adfSAlexandra Winter 	if (dev2br_supported)
1016fa115adfSAlexandra Winter 		priv->brport_hw_features |= BR_LEARNING_SYNC;
1017fa115adfSAlexandra Winter 	else
1018fa115adfSAlexandra Winter 		priv->brport_hw_features &= ~BR_LEARNING_SYNC;
1019fa115adfSAlexandra Winter }
1020fa115adfSAlexandra Winter 
1021521c65b6SAlexandra Winter static void qeth_l2_enable_brport_features(struct qeth_card *card)
1022521c65b6SAlexandra Winter {
1023521c65b6SAlexandra Winter 	struct qeth_priv *priv = netdev_priv(card->dev);
1024521c65b6SAlexandra Winter 	int rc;
1025521c65b6SAlexandra Winter 
1026521c65b6SAlexandra Winter 	if (priv->brport_features & BR_LEARNING_SYNC) {
1027521c65b6SAlexandra Winter 		if (priv->brport_hw_features & BR_LEARNING_SYNC) {
1028521c65b6SAlexandra Winter 			qeth_l2_set_pnso_mode(card, QETH_PNSO_ADDR_INFO);
1029521c65b6SAlexandra Winter 			rc = qeth_l2_dev2br_an_set(card, true);
1030521c65b6SAlexandra Winter 			if (rc == -EAGAIN) {
1031521c65b6SAlexandra Winter 				/* Recoverable error, retry once */
1032521c65b6SAlexandra Winter 				qeth_l2_set_pnso_mode(card, QETH_PNSO_NONE);
1033521c65b6SAlexandra Winter 				qeth_l2_dev2br_fdb_flush(card);
1034521c65b6SAlexandra Winter 				qeth_l2_set_pnso_mode(card, QETH_PNSO_ADDR_INFO);
1035521c65b6SAlexandra Winter 				rc = qeth_l2_dev2br_an_set(card, true);
1036521c65b6SAlexandra Winter 			}
1037521c65b6SAlexandra Winter 			if (rc) {
1038521c65b6SAlexandra Winter 				netdev_err(card->dev,
1039521c65b6SAlexandra Winter 					   "failed to enable bridge learning_sync: %d\n",
1040521c65b6SAlexandra Winter 					   rc);
1041521c65b6SAlexandra Winter 				qeth_l2_set_pnso_mode(card, QETH_PNSO_NONE);
1042521c65b6SAlexandra Winter 				qeth_l2_dev2br_fdb_flush(card);
1043521c65b6SAlexandra Winter 				priv->brport_features ^= BR_LEARNING_SYNC;
1044521c65b6SAlexandra Winter 			}
1045521c65b6SAlexandra Winter 		} else {
1046521c65b6SAlexandra Winter 			dev_warn(&card->gdev->dev,
1047521c65b6SAlexandra Winter 				"bridge learning_sync not supported\n");
1048521c65b6SAlexandra Winter 			priv->brport_features ^= BR_LEARNING_SYNC;
1049521c65b6SAlexandra Winter 		}
1050521c65b6SAlexandra Winter 	}
1051521c65b6SAlexandra Winter }
1052521c65b6SAlexandra Winter 
10534e2b5aa5SJulian Wiedmann #ifdef CONFIG_QETH_OSN
10540ce37ec2SJulian Wiedmann static void qeth_osn_assist_cb(struct qeth_card *card,
105512fc286fSJulian Wiedmann 			       struct qeth_cmd_buffer *iob,
105612fc286fSJulian Wiedmann 			       unsigned int data_length)
10574a71df50SFrank Blaschka {
1058308946b0SJulian Wiedmann 	qeth_notify_cmd(iob, 0);
1059c3b2218dSJulian Wiedmann 	qeth_put_cmd(iob);
10604a71df50SFrank Blaschka }
10614a71df50SFrank Blaschka 
10624a71df50SFrank Blaschka int qeth_osn_assist(struct net_device *dev, void *data, int data_len)
10634a71df50SFrank Blaschka {
10644a71df50SFrank Blaschka 	struct qeth_cmd_buffer *iob;
10654a71df50SFrank Blaschka 	struct qeth_card *card;
10664a71df50SFrank Blaschka 
1067a59d121dSJulian Wiedmann 	if (data_len < 0)
1068a59d121dSJulian Wiedmann 		return -EINVAL;
10694a71df50SFrank Blaschka 	if (!dev)
10704a71df50SFrank Blaschka 		return -ENODEV;
1071509e2562SHeiko Carstens 	card = dev->ml_priv;
10724a71df50SFrank Blaschka 	if (!card)
10734a71df50SFrank Blaschka 		return -ENODEV;
1074847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 2, "osnsdmc");
1075c3521254SEugene Crosser 	if (!qeth_card_hw_is_reachable(card))
10764a71df50SFrank Blaschka 		return -ENODEV;
1077a59d121dSJulian Wiedmann 
1078a59d121dSJulian Wiedmann 	iob = qeth_alloc_cmd(&card->write, IPA_PDU_HEADER_SIZE + data_len, 1,
1079a59d121dSJulian Wiedmann 			     QETH_IPA_TIMEOUT);
10801273a800SJulian Wiedmann 	if (!iob)
10811273a800SJulian Wiedmann 		return -ENOMEM;
10821273a800SJulian Wiedmann 
10837f92316cSJulian Wiedmann 	qeth_prepare_ipa_cmd(card, iob, (u16) data_len, NULL);
10847f92316cSJulian Wiedmann 
1085ff5caa7aSJulian Wiedmann 	memcpy(__ipa_cmd(iob), data, data_len);
10860ce37ec2SJulian Wiedmann 	iob->callback = qeth_osn_assist_cb;
10870ce37ec2SJulian Wiedmann 	return qeth_send_ipa_cmd(card, iob, NULL, NULL);
10884a71df50SFrank Blaschka }
10894a71df50SFrank Blaschka EXPORT_SYMBOL(qeth_osn_assist);
10904a71df50SFrank Blaschka 
10914a71df50SFrank Blaschka int qeth_osn_register(unsigned char *read_dev_no, struct net_device **dev,
10924a71df50SFrank Blaschka 		  int (*assist_cb)(struct net_device *, void *),
10934a71df50SFrank Blaschka 		  int (*data_cb)(struct sk_buff *))
10944a71df50SFrank Blaschka {
10954a71df50SFrank Blaschka 	struct qeth_card *card;
1096d7d18da1SJulian Wiedmann 	char bus_id[16];
1097d7d18da1SJulian Wiedmann 	u16 devno;
10984a71df50SFrank Blaschka 
1099d7d18da1SJulian Wiedmann 	memcpy(&devno, read_dev_no, 2);
1100d7d18da1SJulian Wiedmann 	sprintf(bus_id, "0.0.%04x", devno);
1101d7d18da1SJulian Wiedmann 	card = qeth_get_card_by_busid(bus_id);
1102d7d18da1SJulian Wiedmann 	if (!card || !IS_OSN(card))
11034a71df50SFrank Blaschka 		return -ENODEV;
1104d7d18da1SJulian Wiedmann 	*dev = card->dev;
1105d7d18da1SJulian Wiedmann 
1106847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 2, "osnreg");
11074a71df50SFrank Blaschka 	if ((assist_cb == NULL) || (data_cb == NULL))
11084a71df50SFrank Blaschka 		return -EINVAL;
11094a71df50SFrank Blaschka 	card->osn_info.assist_cb = assist_cb;
11104a71df50SFrank Blaschka 	card->osn_info.data_cb = data_cb;
11114a71df50SFrank Blaschka 	return 0;
11124a71df50SFrank Blaschka }
11134a71df50SFrank Blaschka EXPORT_SYMBOL(qeth_osn_register);
11144a71df50SFrank Blaschka 
11154a71df50SFrank Blaschka void qeth_osn_deregister(struct net_device *dev)
11164a71df50SFrank Blaschka {
11174a71df50SFrank Blaschka 	struct qeth_card *card;
11184a71df50SFrank Blaschka 
11194a71df50SFrank Blaschka 	if (!dev)
11204a71df50SFrank Blaschka 		return;
1121509e2562SHeiko Carstens 	card = dev->ml_priv;
11224a71df50SFrank Blaschka 	if (!card)
11234a71df50SFrank Blaschka 		return;
1124847a50fdSCarsten Otte 	QETH_CARD_TEXT(card, 2, "osndereg");
11254a71df50SFrank Blaschka 	card->osn_info.assist_cb = NULL;
11264a71df50SFrank Blaschka 	card->osn_info.data_cb = NULL;
11274a71df50SFrank Blaschka 	return;
11284a71df50SFrank Blaschka }
11294a71df50SFrank Blaschka EXPORT_SYMBOL(qeth_osn_deregister);
11304e2b5aa5SJulian Wiedmann #endif
11314a71df50SFrank Blaschka 
1132b4d72c08SEugene Crosser /* SETBRIDGEPORT support, async notifications */
1133b4d72c08SEugene Crosser 
11349f48b9dbSEugene Crosser enum qeth_an_event_type {anev_reg_unreg, anev_abort, anev_reset};
11359f48b9dbSEugene Crosser 
11369f48b9dbSEugene Crosser /**
11379f48b9dbSEugene Crosser  * qeth_bridge_emit_host_event() - bridgeport address change notification
11389f48b9dbSEugene Crosser  * @card:  qeth_card structure pointer, for udev events.
11399f48b9dbSEugene Crosser  * @evtype:  "normal" register/unregister, or abort, or reset. For abort
11409f48b9dbSEugene Crosser  *	      and reset token and addr_lnid are unused and may be NULL.
11419f48b9dbSEugene Crosser  * @code:  event bitmask: high order bit 0x80 value 1 means removal of an
11429f48b9dbSEugene Crosser  *			  object, 0 - addition of an object.
11439f48b9dbSEugene Crosser  *			  0x01 - VLAN, 0x02 - MAC, 0x03 - VLAN and MAC.
11449f48b9dbSEugene Crosser  * @token: "network token" structure identifying physical address of the port.
11459f48b9dbSEugene Crosser  * @addr_lnid: pointer to structure with MAC address and VLAN ID.
11469f48b9dbSEugene Crosser  *
11479f48b9dbSEugene Crosser  * This function is called when registrations and deregistrations are
11489f48b9dbSEugene Crosser  * reported by the hardware, and also when notifications are enabled -
11499f48b9dbSEugene Crosser  * for all currently registered addresses.
11509f48b9dbSEugene Crosser  */
11519f48b9dbSEugene Crosser static void qeth_bridge_emit_host_event(struct qeth_card *card,
11529f48b9dbSEugene Crosser 					enum qeth_an_event_type evtype,
1153a0138f59SAlexandra Winter 					u8 code,
1154a0138f59SAlexandra Winter 					struct net_if_token *token,
1155a0138f59SAlexandra Winter 					struct mac_addr_lnid *addr_lnid)
11569f48b9dbSEugene Crosser {
11579f48b9dbSEugene Crosser 	char str[7][32];
11589f48b9dbSEugene Crosser 	char *env[8];
11599f48b9dbSEugene Crosser 	int i = 0;
11609f48b9dbSEugene Crosser 
11619f48b9dbSEugene Crosser 	switch (evtype) {
11629f48b9dbSEugene Crosser 	case anev_reg_unreg:
11639f48b9dbSEugene Crosser 		snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=%s",
11649f48b9dbSEugene Crosser 				(code & IPA_ADDR_CHANGE_CODE_REMOVAL)
11659f48b9dbSEugene Crosser 				? "deregister" : "register");
11669f48b9dbSEugene Crosser 		env[i] = str[i]; i++;
11679f48b9dbSEugene Crosser 		if (code & IPA_ADDR_CHANGE_CODE_VLANID) {
11689f48b9dbSEugene Crosser 			snprintf(str[i], sizeof(str[i]), "VLAN=%d",
11699f48b9dbSEugene Crosser 				addr_lnid->lnid);
11709f48b9dbSEugene Crosser 			env[i] = str[i]; i++;
11719f48b9dbSEugene Crosser 		}
11729f48b9dbSEugene Crosser 		if (code & IPA_ADDR_CHANGE_CODE_MACADDR) {
11739846e70bSEugene Crosser 			snprintf(str[i], sizeof(str[i]), "MAC=%pM",
11749846e70bSEugene Crosser 				addr_lnid->mac);
11759f48b9dbSEugene Crosser 			env[i] = str[i]; i++;
11769f48b9dbSEugene Crosser 		}
11779f48b9dbSEugene Crosser 		snprintf(str[i], sizeof(str[i]), "NTOK_BUSID=%x.%x.%04x",
11789f48b9dbSEugene Crosser 			token->cssid, token->ssid, token->devnum);
11799f48b9dbSEugene Crosser 		env[i] = str[i]; i++;
11809f48b9dbSEugene Crosser 		snprintf(str[i], sizeof(str[i]), "NTOK_IID=%02x", token->iid);
11819f48b9dbSEugene Crosser 		env[i] = str[i]; i++;
11829f48b9dbSEugene Crosser 		snprintf(str[i], sizeof(str[i]), "NTOK_CHPID=%02x",
11839f48b9dbSEugene Crosser 				token->chpid);
11849f48b9dbSEugene Crosser 		env[i] = str[i]; i++;
11859f48b9dbSEugene Crosser 		snprintf(str[i], sizeof(str[i]), "NTOK_CHID=%04x", token->chid);
11869f48b9dbSEugene Crosser 		env[i] = str[i]; i++;
11879f48b9dbSEugene Crosser 		break;
11889f48b9dbSEugene Crosser 	case anev_abort:
11899f48b9dbSEugene Crosser 		snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=abort");
11909f48b9dbSEugene Crosser 		env[i] = str[i]; i++;
11919f48b9dbSEugene Crosser 		break;
11929f48b9dbSEugene Crosser 	case anev_reset:
11939f48b9dbSEugene Crosser 		snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=reset");
11949f48b9dbSEugene Crosser 		env[i] = str[i]; i++;
11959f48b9dbSEugene Crosser 		break;
11969f48b9dbSEugene Crosser 	}
11979f48b9dbSEugene Crosser 	env[i] = NULL;
11989f48b9dbSEugene Crosser 	kobject_uevent_env(&card->gdev->dev.kobj, KOBJ_CHANGE, env);
11999f48b9dbSEugene Crosser }
12009f48b9dbSEugene Crosser 
1201b4d72c08SEugene Crosser struct qeth_bridge_state_data {
1202b4d72c08SEugene Crosser 	struct work_struct worker;
1203b4d72c08SEugene Crosser 	struct qeth_card *card;
120461c6f217SJulian Wiedmann 	u8 role;
120561c6f217SJulian Wiedmann 	u8 state;
1206b4d72c08SEugene Crosser };
1207b4d72c08SEugene Crosser 
1208b4d72c08SEugene Crosser static void qeth_bridge_state_change_worker(struct work_struct *work)
1209b4d72c08SEugene Crosser {
1210b4d72c08SEugene Crosser 	struct qeth_bridge_state_data *data =
1211b4d72c08SEugene Crosser 		container_of(work, struct qeth_bridge_state_data, worker);
1212b4d72c08SEugene Crosser 	char env_locrem[32];
1213b4d72c08SEugene Crosser 	char env_role[32];
1214b4d72c08SEugene Crosser 	char env_state[32];
1215b4d72c08SEugene Crosser 	char *env[] = {
1216b4d72c08SEugene Crosser 		env_locrem,
1217b4d72c08SEugene Crosser 		env_role,
1218b4d72c08SEugene Crosser 		env_state,
1219b4d72c08SEugene Crosser 		NULL
1220b4d72c08SEugene Crosser 	};
1221b4d72c08SEugene Crosser 
1222b4d72c08SEugene Crosser 	snprintf(env_locrem, sizeof(env_locrem), "BRIDGEPORT=statechange");
1223b4d72c08SEugene Crosser 	snprintf(env_role, sizeof(env_role), "ROLE=%s",
122461c6f217SJulian Wiedmann 		(data->role == QETH_SBP_ROLE_NONE) ? "none" :
122561c6f217SJulian Wiedmann 		(data->role == QETH_SBP_ROLE_PRIMARY) ? "primary" :
122661c6f217SJulian Wiedmann 		(data->role == QETH_SBP_ROLE_SECONDARY) ? "secondary" :
1227b4d72c08SEugene Crosser 		"<INVALID>");
1228b4d72c08SEugene Crosser 	snprintf(env_state, sizeof(env_state), "STATE=%s",
122961c6f217SJulian Wiedmann 		(data->state == QETH_SBP_STATE_INACTIVE) ? "inactive" :
123061c6f217SJulian Wiedmann 		(data->state == QETH_SBP_STATE_STANDBY) ? "standby" :
123161c6f217SJulian Wiedmann 		(data->state == QETH_SBP_STATE_ACTIVE) ? "active" :
1232b4d72c08SEugene Crosser 		"<INVALID>");
1233b4d72c08SEugene Crosser 	kobject_uevent_env(&data->card->gdev->dev.kobj,
1234b4d72c08SEugene Crosser 				KOBJ_CHANGE, env);
1235b4d72c08SEugene Crosser 	kfree(data);
1236b4d72c08SEugene Crosser }
1237b4d72c08SEugene Crosser 
1238c044dc21SEugene Crosser static void qeth_bridge_state_change(struct qeth_card *card,
1239c044dc21SEugene Crosser 					struct qeth_ipa_cmd *cmd)
1240b4d72c08SEugene Crosser {
124165b0494eSJulian Wiedmann 	struct qeth_sbp_port_data *qports = &cmd->data.sbp.data.port_data;
1242b4d72c08SEugene Crosser 	struct qeth_bridge_state_data *data;
1243b4d72c08SEugene Crosser 
1244b4d72c08SEugene Crosser 	QETH_CARD_TEXT(card, 2, "brstchng");
124502472e28SJulian Wiedmann 	if (qports->num_entries == 0) {
124602472e28SJulian Wiedmann 		QETH_CARD_TEXT(card, 2, "BPempty");
124702472e28SJulian Wiedmann 		return;
124802472e28SJulian Wiedmann 	}
1249b4d72c08SEugene Crosser 	if (qports->entry_length != sizeof(struct qeth_sbp_port_entry)) {
12508a593148SThomas Richter 		QETH_CARD_TEXT_(card, 2, "BPsz%04x", qports->entry_length);
1251b4d72c08SEugene Crosser 		return;
1252b4d72c08SEugene Crosser 	}
125361c6f217SJulian Wiedmann 
125461c6f217SJulian Wiedmann 	data = kzalloc(sizeof(*data), GFP_ATOMIC);
1255b4d72c08SEugene Crosser 	if (!data) {
1256b4d72c08SEugene Crosser 		QETH_CARD_TEXT(card, 2, "BPSalloc");
1257b4d72c08SEugene Crosser 		return;
1258b4d72c08SEugene Crosser 	}
1259b4d72c08SEugene Crosser 	INIT_WORK(&data->worker, qeth_bridge_state_change_worker);
1260b4d72c08SEugene Crosser 	data->card = card;
126161c6f217SJulian Wiedmann 	/* Information for the local port: */
126261c6f217SJulian Wiedmann 	data->role = qports->entry[0].role;
126361c6f217SJulian Wiedmann 	data->state = qports->entry[0].state;
126461c6f217SJulian Wiedmann 
1265c0a2e4d1SJulian Wiedmann 	queue_work(card->event_wq, &data->worker);
1266b4d72c08SEugene Crosser }
1267b4d72c08SEugene Crosser 
1268a0138f59SAlexandra Winter struct qeth_addr_change_data {
12699d6a569aSJulian Wiedmann 	struct delayed_work dwork;
12709f48b9dbSEugene Crosser 	struct qeth_card *card;
1271a0138f59SAlexandra Winter 	struct qeth_ipacmd_addr_change ac_event;
12729f48b9dbSEugene Crosser };
12739f48b9dbSEugene Crosser 
127410a6cfc0SAlexandra Winter static void qeth_l2_dev2br_worker(struct work_struct *work)
127510a6cfc0SAlexandra Winter {
127610a6cfc0SAlexandra Winter 	struct delayed_work *dwork = to_delayed_work(work);
127710a6cfc0SAlexandra Winter 	struct qeth_addr_change_data *data;
127810a6cfc0SAlexandra Winter 	struct qeth_card *card;
1279817741a8SAlexandra Winter 	struct qeth_priv *priv;
128010a6cfc0SAlexandra Winter 	unsigned int i;
1281817741a8SAlexandra Winter 	int rc;
128210a6cfc0SAlexandra Winter 
128310a6cfc0SAlexandra Winter 	data = container_of(dwork, struct qeth_addr_change_data, dwork);
128410a6cfc0SAlexandra Winter 	card = data->card;
1285817741a8SAlexandra Winter 	priv = netdev_priv(card->dev);
128610a6cfc0SAlexandra Winter 
128710a6cfc0SAlexandra Winter 	QETH_CARD_TEXT(card, 4, "dev2brew");
128810a6cfc0SAlexandra Winter 
128910a6cfc0SAlexandra Winter 	if (READ_ONCE(card->info.pnso_mode) == QETH_PNSO_NONE)
129010a6cfc0SAlexandra Winter 		goto free;
129110a6cfc0SAlexandra Winter 
129210a6cfc0SAlexandra Winter 	/* Potential re-config in progress, try again later: */
129310a6cfc0SAlexandra Winter 	if (!rtnl_trylock()) {
129410a6cfc0SAlexandra Winter 		queue_delayed_work(card->event_wq, dwork,
129510a6cfc0SAlexandra Winter 				   msecs_to_jiffies(100));
129610a6cfc0SAlexandra Winter 		return;
129710a6cfc0SAlexandra Winter 	}
1298817741a8SAlexandra Winter 	if (!netif_device_present(card->dev))
1299817741a8SAlexandra Winter 		goto out_unlock;
130010a6cfc0SAlexandra Winter 
130110a6cfc0SAlexandra Winter 	if (data->ac_event.lost_event_mask) {
130210a6cfc0SAlexandra Winter 		QETH_DBF_MESSAGE(3,
130310a6cfc0SAlexandra Winter 				 "Address change notification overflow on device %x\n",
130410a6cfc0SAlexandra Winter 				 CARD_DEVID(card));
1305817741a8SAlexandra Winter 		/* Card fdb and bridge fdb are out of sync, card has stopped
1306817741a8SAlexandra Winter 		 * notifications (no need to drain_workqueue). Purge all
1307817741a8SAlexandra Winter 		 * 'extern_learn' entries from the parent bridge and restart
1308817741a8SAlexandra Winter 		 * the notifications.
1309817741a8SAlexandra Winter 		 */
1310817741a8SAlexandra Winter 		qeth_l2_dev2br_fdb_flush(card);
1311817741a8SAlexandra Winter 		rc = qeth_l2_dev2br_an_set(card, true);
1312817741a8SAlexandra Winter 		if (rc) {
1313817741a8SAlexandra Winter 			/* TODO: if we want to retry after -EAGAIN, be
1314817741a8SAlexandra Winter 			 * aware there could be stale entries in the
1315817741a8SAlexandra Winter 			 * workqueue now, that need to be drained.
1316817741a8SAlexandra Winter 			 * For now we give up:
1317817741a8SAlexandra Winter 			 */
1318817741a8SAlexandra Winter 			netdev_err(card->dev,
1319817741a8SAlexandra Winter 				   "bridge learning_sync failed to recover: %d\n",
1320817741a8SAlexandra Winter 				   rc);
1321817741a8SAlexandra Winter 			WRITE_ONCE(card->info.pnso_mode,
1322817741a8SAlexandra Winter 				   QETH_PNSO_NONE);
1323817741a8SAlexandra Winter 			/* To remove fdb entries reported by an_set: */
1324817741a8SAlexandra Winter 			qeth_l2_dev2br_fdb_flush(card);
1325817741a8SAlexandra Winter 			priv->brport_features ^= BR_LEARNING_SYNC;
1326817741a8SAlexandra Winter 		} else {
1327817741a8SAlexandra Winter 			QETH_DBF_MESSAGE(3,
1328817741a8SAlexandra Winter 					 "Address Notification resynced on device %x\n",
1329817741a8SAlexandra Winter 					 CARD_DEVID(card));
1330817741a8SAlexandra Winter 		}
133110a6cfc0SAlexandra Winter 	} else {
133210a6cfc0SAlexandra Winter 		for (i = 0; i < data->ac_event.num_entries; i++) {
133310a6cfc0SAlexandra Winter 			struct qeth_ipacmd_addr_change_entry *entry =
133410a6cfc0SAlexandra Winter 					&data->ac_event.entry[i];
133510a6cfc0SAlexandra Winter 			qeth_l2_dev2br_fdb_notify(card,
133610a6cfc0SAlexandra Winter 						  entry->change_code,
133710a6cfc0SAlexandra Winter 						  &entry->token,
133810a6cfc0SAlexandra Winter 						  &entry->addr_lnid);
133910a6cfc0SAlexandra Winter 		}
134010a6cfc0SAlexandra Winter 	}
1341817741a8SAlexandra Winter 
1342817741a8SAlexandra Winter out_unlock:
134310a6cfc0SAlexandra Winter 	rtnl_unlock();
134410a6cfc0SAlexandra Winter 
134510a6cfc0SAlexandra Winter free:
134610a6cfc0SAlexandra Winter 	kfree(data);
134710a6cfc0SAlexandra Winter }
134810a6cfc0SAlexandra Winter 
1349a0138f59SAlexandra Winter static void qeth_addr_change_event_worker(struct work_struct *work)
13509f48b9dbSEugene Crosser {
13519d6a569aSJulian Wiedmann 	struct delayed_work *dwork = to_delayed_work(work);
13529d6a569aSJulian Wiedmann 	struct qeth_addr_change_data *data;
13539d6a569aSJulian Wiedmann 	struct qeth_card *card;
13549f48b9dbSEugene Crosser 	int i;
13559f48b9dbSEugene Crosser 
13569d6a569aSJulian Wiedmann 	data = container_of(dwork, struct qeth_addr_change_data, dwork);
13579d6a569aSJulian Wiedmann 	card = data->card;
13589d6a569aSJulian Wiedmann 
1359a0138f59SAlexandra Winter 	QETH_CARD_TEXT(data->card, 4, "adrchgew");
13609d6a569aSJulian Wiedmann 
13619d6a569aSJulian Wiedmann 	if (READ_ONCE(card->info.pnso_mode) == QETH_PNSO_NONE)
13629d6a569aSJulian Wiedmann 		goto free;
13639d6a569aSJulian Wiedmann 
1364a0138f59SAlexandra Winter 	if (data->ac_event.lost_event_mask) {
13659d6a569aSJulian Wiedmann 		/* Potential re-config in progress, try again later: */
13669d6a569aSJulian Wiedmann 		if (!mutex_trylock(&card->sbp_lock)) {
13679d6a569aSJulian Wiedmann 			queue_delayed_work(card->event_wq, dwork,
13689d6a569aSJulian Wiedmann 					   msecs_to_jiffies(100));
13699d6a569aSJulian Wiedmann 			return;
13709d6a569aSJulian Wiedmann 		}
13719d6a569aSJulian Wiedmann 
13729f48b9dbSEugene Crosser 		dev_info(&data->card->gdev->dev,
1373a0138f59SAlexandra Winter 			 "Address change notification stopped on %s (%s)\n",
13749f48b9dbSEugene Crosser 			 data->card->dev->name,
1375a0138f59SAlexandra Winter 			(data->ac_event.lost_event_mask == 0x01)
13769f48b9dbSEugene Crosser 			? "Overflow"
1377a0138f59SAlexandra Winter 			: (data->ac_event.lost_event_mask == 0x02)
13789f48b9dbSEugene Crosser 			? "Bridge port state change"
13799f48b9dbSEugene Crosser 			: "Unknown reason");
13809d6a569aSJulian Wiedmann 
13819f48b9dbSEugene Crosser 		data->card->options.sbp.hostnotification = 0;
13829d6a569aSJulian Wiedmann 		card->info.pnso_mode = QETH_PNSO_NONE;
1383c8183f54SJulian Wiedmann 		mutex_unlock(&data->card->sbp_lock);
13849f48b9dbSEugene Crosser 		qeth_bridge_emit_host_event(data->card, anev_abort,
13859f48b9dbSEugene Crosser 					    0, NULL, NULL);
13869f48b9dbSEugene Crosser 	} else
1387a0138f59SAlexandra Winter 		for (i = 0; i < data->ac_event.num_entries; i++) {
13889f48b9dbSEugene Crosser 			struct qeth_ipacmd_addr_change_entry *entry =
1389a0138f59SAlexandra Winter 					&data->ac_event.entry[i];
13909f48b9dbSEugene Crosser 			qeth_bridge_emit_host_event(data->card,
13919f48b9dbSEugene Crosser 						    anev_reg_unreg,
13929f48b9dbSEugene Crosser 						    entry->change_code,
1393a0138f59SAlexandra Winter 						    &entry->token,
1394a0138f59SAlexandra Winter 						    &entry->addr_lnid);
13959f48b9dbSEugene Crosser 		}
13969d6a569aSJulian Wiedmann 
13979d6a569aSJulian Wiedmann free:
13989f48b9dbSEugene Crosser 	kfree(data);
13999f48b9dbSEugene Crosser }
14009f48b9dbSEugene Crosser 
1401a0138f59SAlexandra Winter static void qeth_addr_change_event(struct qeth_card *card,
1402c044dc21SEugene Crosser 				   struct qeth_ipa_cmd *cmd)
14039f48b9dbSEugene Crosser {
14049f48b9dbSEugene Crosser 	struct qeth_ipacmd_addr_change *hostevs =
14059f48b9dbSEugene Crosser 		 &cmd->data.addrchange;
1406a0138f59SAlexandra Winter 	struct qeth_addr_change_data *data;
14079f48b9dbSEugene Crosser 	int extrasize;
14089f48b9dbSEugene Crosser 
14099d6a569aSJulian Wiedmann 	if (card->info.pnso_mode == QETH_PNSO_NONE)
14109d6a569aSJulian Wiedmann 		return;
14119d6a569aSJulian Wiedmann 
1412a0138f59SAlexandra Winter 	QETH_CARD_TEXT(card, 4, "adrchgev");
14139f48b9dbSEugene Crosser 	if (cmd->hdr.return_code != 0x0000) {
14149f48b9dbSEugene Crosser 		if (cmd->hdr.return_code == 0x0010) {
14159f48b9dbSEugene Crosser 			if (hostevs->lost_event_mask == 0x00)
14169f48b9dbSEugene Crosser 				hostevs->lost_event_mask = 0xff;
14179f48b9dbSEugene Crosser 		} else {
1418a0138f59SAlexandra Winter 			QETH_CARD_TEXT_(card, 2, "ACHN%04x",
14199f48b9dbSEugene Crosser 				cmd->hdr.return_code);
14209f48b9dbSEugene Crosser 			return;
14219f48b9dbSEugene Crosser 		}
14229f48b9dbSEugene Crosser 	}
14239f48b9dbSEugene Crosser 	extrasize = sizeof(struct qeth_ipacmd_addr_change_entry) *
14249f48b9dbSEugene Crosser 						hostevs->num_entries;
1425a0138f59SAlexandra Winter 	data = kzalloc(sizeof(struct qeth_addr_change_data) + extrasize,
14269f48b9dbSEugene Crosser 		       GFP_ATOMIC);
14279f48b9dbSEugene Crosser 	if (!data) {
1428a0138f59SAlexandra Winter 		QETH_CARD_TEXT(card, 2, "ACNalloc");
14299f48b9dbSEugene Crosser 		return;
14309f48b9dbSEugene Crosser 	}
143110a6cfc0SAlexandra Winter 	if (card->info.pnso_mode == QETH_PNSO_BRIDGEPORT)
14329d6a569aSJulian Wiedmann 		INIT_DELAYED_WORK(&data->dwork, qeth_addr_change_event_worker);
143310a6cfc0SAlexandra Winter 	else
143410a6cfc0SAlexandra Winter 		INIT_DELAYED_WORK(&data->dwork, qeth_l2_dev2br_worker);
14359f48b9dbSEugene Crosser 	data->card = card;
1436a0138f59SAlexandra Winter 	memcpy(&data->ac_event, hostevs,
14379f48b9dbSEugene Crosser 			sizeof(struct qeth_ipacmd_addr_change) + extrasize);
14389d6a569aSJulian Wiedmann 	queue_delayed_work(card->event_wq, &data->dwork, 0);
14399f48b9dbSEugene Crosser }
14409f48b9dbSEugene Crosser 
1441b4d72c08SEugene Crosser /* SETBRIDGEPORT support; sending commands */
1442b4d72c08SEugene Crosser 
1443b4d72c08SEugene Crosser struct _qeth_sbp_cbctl {
1444b4d72c08SEugene Crosser 	union {
1445b4d72c08SEugene Crosser 		u32 supported;
1446b4d72c08SEugene Crosser 		struct {
1447b4d72c08SEugene Crosser 			enum qeth_sbp_roles *role;
1448b4d72c08SEugene Crosser 			enum qeth_sbp_states *state;
1449b4d72c08SEugene Crosser 		} qports;
1450b4d72c08SEugene Crosser 	} data;
1451b4d72c08SEugene Crosser };
1452b4d72c08SEugene Crosser 
1453b4d72c08SEugene Crosser static int qeth_bridgeport_makerc(struct qeth_card *card,
14541709ff8dSJulian Wiedmann 				  struct qeth_ipa_cmd *cmd)
1455b4d72c08SEugene Crosser {
14561709ff8dSJulian Wiedmann 	struct qeth_ipacmd_setbridgeport *sbp = &cmd->data.sbp;
14571709ff8dSJulian Wiedmann 	enum qeth_ipa_sbp_cmd setcmd = sbp->hdr.command_code;
14581709ff8dSJulian Wiedmann 	u16 ipa_rc = cmd->hdr.return_code;
14591709ff8dSJulian Wiedmann 	u16 sbp_rc = sbp->hdr.return_code;
1460b4d72c08SEugene Crosser 	int rc;
1461b4d72c08SEugene Crosser 
14621709ff8dSJulian Wiedmann 	if (ipa_rc == IPA_RC_SUCCESS && sbp_rc == IPA_RC_SUCCESS)
14631709ff8dSJulian Wiedmann 		return 0;
14641709ff8dSJulian Wiedmann 
14651709ff8dSJulian Wiedmann 	if ((IS_IQD(card) && ipa_rc == IPA_RC_SUCCESS) ||
14661709ff8dSJulian Wiedmann 	    (!IS_IQD(card) && ipa_rc == sbp_rc)) {
14671709ff8dSJulian Wiedmann 		switch (sbp_rc) {
14682063a5f5SKittipon Meesompop 		case IPA_RC_SUCCESS:
1469b4d72c08SEugene Crosser 			rc = 0;
1470b4d72c08SEugene Crosser 			break;
14712063a5f5SKittipon Meesompop 		case IPA_RC_L2_UNSUPPORTED_CMD:
14722063a5f5SKittipon Meesompop 		case IPA_RC_UNSUPPORTED_COMMAND:
1473ffb95251SEugene Crosser 			rc = -EOPNOTSUPP;
1474b4d72c08SEugene Crosser 			break;
14752063a5f5SKittipon Meesompop 		case IPA_RC_SBP_OSA_NOT_CONFIGURED:
14762063a5f5SKittipon Meesompop 		case IPA_RC_SBP_IQD_NOT_CONFIGURED:
1477b4d72c08SEugene Crosser 			rc = -ENODEV; /* maybe not the best code here? */
1478b4d72c08SEugene Crosser 			dev_err(&card->gdev->dev,
14799c23f4daSEugene Crosser 	"The device is not configured as a Bridge Port\n");
1480b4d72c08SEugene Crosser 			break;
14812063a5f5SKittipon Meesompop 		case IPA_RC_SBP_OSA_OS_MISMATCH:
14822063a5f5SKittipon Meesompop 		case IPA_RC_SBP_IQD_OS_MISMATCH:
14831b05cf62SEugene Crosser 			rc = -EPERM;
14841b05cf62SEugene Crosser 			dev_err(&card->gdev->dev,
14851b05cf62SEugene Crosser 	"A Bridge Port is already configured by a different operating system\n");
14861b05cf62SEugene Crosser 			break;
14872063a5f5SKittipon Meesompop 		case IPA_RC_SBP_OSA_ANO_DEV_PRIMARY:
14882063a5f5SKittipon Meesompop 		case IPA_RC_SBP_IQD_ANO_DEV_PRIMARY:
1489b4d72c08SEugene Crosser 			switch (setcmd) {
1490b4d72c08SEugene Crosser 			case IPA_SBP_SET_PRIMARY_BRIDGE_PORT:
1491b4d72c08SEugene Crosser 				rc = -EEXIST;
1492b4d72c08SEugene Crosser 				dev_err(&card->gdev->dev,
14939c23f4daSEugene Crosser 	"The LAN already has a primary Bridge Port\n");
1494b4d72c08SEugene Crosser 				break;
1495b4d72c08SEugene Crosser 			case IPA_SBP_SET_SECONDARY_BRIDGE_PORT:
1496b4d72c08SEugene Crosser 				rc = -EBUSY;
1497b4d72c08SEugene Crosser 				dev_err(&card->gdev->dev,
14989c23f4daSEugene Crosser 	"The device is already a primary Bridge Port\n");
1499b4d72c08SEugene Crosser 				break;
1500b4d72c08SEugene Crosser 			default:
1501b4d72c08SEugene Crosser 				rc = -EIO;
1502b4d72c08SEugene Crosser 			}
1503b4d72c08SEugene Crosser 			break;
15042063a5f5SKittipon Meesompop 		case IPA_RC_SBP_OSA_CURRENT_SECOND:
15052063a5f5SKittipon Meesompop 		case IPA_RC_SBP_IQD_CURRENT_SECOND:
1506b4d72c08SEugene Crosser 			rc = -EBUSY;
1507b4d72c08SEugene Crosser 			dev_err(&card->gdev->dev,
15089c23f4daSEugene Crosser 	"The device is already a secondary Bridge Port\n");
1509b4d72c08SEugene Crosser 			break;
15102063a5f5SKittipon Meesompop 		case IPA_RC_SBP_OSA_LIMIT_SECOND:
15112063a5f5SKittipon Meesompop 		case IPA_RC_SBP_IQD_LIMIT_SECOND:
1512b4d72c08SEugene Crosser 			rc = -EEXIST;
1513b4d72c08SEugene Crosser 			dev_err(&card->gdev->dev,
15149c23f4daSEugene Crosser 	"The LAN cannot have more secondary Bridge Ports\n");
1515b4d72c08SEugene Crosser 			break;
15162063a5f5SKittipon Meesompop 		case IPA_RC_SBP_OSA_CURRENT_PRIMARY:
15172063a5f5SKittipon Meesompop 		case IPA_RC_SBP_IQD_CURRENT_PRIMARY:
1518b4d72c08SEugene Crosser 			rc = -EBUSY;
1519b4d72c08SEugene Crosser 			dev_err(&card->gdev->dev,
15209c23f4daSEugene Crosser 	"The device is already a primary Bridge Port\n");
1521b4d72c08SEugene Crosser 			break;
15222063a5f5SKittipon Meesompop 		case IPA_RC_SBP_OSA_NOT_AUTHD_BY_ZMAN:
15232063a5f5SKittipon Meesompop 		case IPA_RC_SBP_IQD_NOT_AUTHD_BY_ZMAN:
1524b4d72c08SEugene Crosser 			rc = -EACCES;
1525b4d72c08SEugene Crosser 			dev_err(&card->gdev->dev,
15269c23f4daSEugene Crosser 	"The device is not authorized to be a Bridge Port\n");
1527b4d72c08SEugene Crosser 			break;
1528b4d72c08SEugene Crosser 		default:
1529b4d72c08SEugene Crosser 			rc = -EIO;
1530b4d72c08SEugene Crosser 		}
15311709ff8dSJulian Wiedmann 	} else {
15321709ff8dSJulian Wiedmann 		switch (ipa_rc) {
1533b4d72c08SEugene Crosser 		case IPA_RC_NOTSUPP:
1534ffb95251SEugene Crosser 			rc = -EOPNOTSUPP;
1535b4d72c08SEugene Crosser 			break;
1536b4d72c08SEugene Crosser 		case IPA_RC_UNSUPPORTED_COMMAND:
1537ffb95251SEugene Crosser 			rc = -EOPNOTSUPP;
1538b4d72c08SEugene Crosser 			break;
1539b4d72c08SEugene Crosser 		default:
1540b4d72c08SEugene Crosser 			rc = -EIO;
1541b4d72c08SEugene Crosser 		}
15421709ff8dSJulian Wiedmann 	}
15439c23f4daSEugene Crosser 
1544b4d72c08SEugene Crosser 	if (rc) {
15451709ff8dSJulian Wiedmann 		QETH_CARD_TEXT_(card, 2, "SBPi%04x", ipa_rc);
15461709ff8dSJulian Wiedmann 		QETH_CARD_TEXT_(card, 2, "SBPc%04x", sbp_rc);
1547b4d72c08SEugene Crosser 	}
1548b4d72c08SEugene Crosser 	return rc;
1549b4d72c08SEugene Crosser }
1550b4d72c08SEugene Crosser 
1551d65626adSJulian Wiedmann static struct qeth_cmd_buffer *qeth_sbp_build_cmd(struct qeth_card *card,
1552d65626adSJulian Wiedmann 						  enum qeth_ipa_sbp_cmd sbp_cmd,
1553b9150461SJulian Wiedmann 						  unsigned int data_length)
15549c23f4daSEugene Crosser {
1555379ac99eSJulian Wiedmann 	enum qeth_ipa_cmds ipa_cmd = IS_IQD(card) ? IPA_CMD_SETBRIDGEPORT_IQD :
15569c23f4daSEugene Crosser 						    IPA_CMD_SETBRIDGEPORT_OSA;
1557b9150461SJulian Wiedmann 	struct qeth_ipacmd_sbp_hdr *hdr;
1558d65626adSJulian Wiedmann 	struct qeth_cmd_buffer *iob;
1559d65626adSJulian Wiedmann 
1560b9150461SJulian Wiedmann 	iob = qeth_ipa_alloc_cmd(card, ipa_cmd, QETH_PROT_NONE,
1561b9150461SJulian Wiedmann 				 data_length +
1562b9150461SJulian Wiedmann 				 offsetof(struct qeth_ipacmd_setbridgeport,
1563b9150461SJulian Wiedmann 					  data));
1564d65626adSJulian Wiedmann 	if (!iob)
1565d65626adSJulian Wiedmann 		return iob;
1566b9150461SJulian Wiedmann 
1567b9150461SJulian Wiedmann 	hdr = &__ipa_cmd(iob)->data.sbp.hdr;
1568b9150461SJulian Wiedmann 	hdr->cmdlength = sizeof(*hdr) + data_length;
1569b9150461SJulian Wiedmann 	hdr->command_code = sbp_cmd;
1570b9150461SJulian Wiedmann 	hdr->used_total = 1;
1571b9150461SJulian Wiedmann 	hdr->seq_no = 1;
1572d65626adSJulian Wiedmann 	return iob;
15739c23f4daSEugene Crosser }
15749c23f4daSEugene Crosser 
1575b4d72c08SEugene Crosser static int qeth_bridgeport_query_support_cb(struct qeth_card *card,
1576b4d72c08SEugene Crosser 	struct qeth_reply *reply, unsigned long data)
1577b4d72c08SEugene Crosser {
1578b4d72c08SEugene Crosser 	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
1579b4d72c08SEugene Crosser 	struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param;
15801709ff8dSJulian Wiedmann 	int rc;
15811709ff8dSJulian Wiedmann 
1582b4d72c08SEugene Crosser 	QETH_CARD_TEXT(card, 2, "brqsupcb");
15831709ff8dSJulian Wiedmann 	rc = qeth_bridgeport_makerc(card, cmd);
15841709ff8dSJulian Wiedmann 	if (rc)
15851709ff8dSJulian Wiedmann 		return rc;
15861709ff8dSJulian Wiedmann 
1587b4d72c08SEugene Crosser 	cbctl->data.supported =
1588b4d72c08SEugene Crosser 		cmd->data.sbp.data.query_cmds_supp.supported_cmds;
1589b4d72c08SEugene Crosser 	return 0;
1590b4d72c08SEugene Crosser }
1591b4d72c08SEugene Crosser 
1592b4d72c08SEugene Crosser /**
1593b4d72c08SEugene Crosser  * qeth_bridgeport_query_support() - store bitmask of supported subfunctions.
1594b4d72c08SEugene Crosser  * @card:			     qeth_card structure pointer.
1595b4d72c08SEugene Crosser  *
1596b4d72c08SEugene Crosser  * Sets bitmask of supported setbridgeport subfunctions in the qeth_card
1597b4d72c08SEugene Crosser  * strucutre: card->options.sbp.supported_funcs.
1598b4d72c08SEugene Crosser  */
1599c044dc21SEugene Crosser static void qeth_bridgeport_query_support(struct qeth_card *card)
1600b4d72c08SEugene Crosser {
1601b4d72c08SEugene Crosser 	struct qeth_cmd_buffer *iob;
1602b4d72c08SEugene Crosser 	struct _qeth_sbp_cbctl cbctl;
1603b4d72c08SEugene Crosser 
1604b4d72c08SEugene Crosser 	QETH_CARD_TEXT(card, 2, "brqsuppo");
1605d65626adSJulian Wiedmann 	iob = qeth_sbp_build_cmd(card, IPA_SBP_QUERY_COMMANDS_SUPPORTED,
1606b9150461SJulian Wiedmann 				 SBP_DATA_SIZEOF(query_cmds_supp));
16071aec42bcSThomas Richter 	if (!iob)
16081aec42bcSThomas Richter 		return;
16091709ff8dSJulian Wiedmann 
1610b4d72c08SEugene Crosser 	if (qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_support_cb,
16111709ff8dSJulian Wiedmann 			      &cbctl)) {
1612b4d72c08SEugene Crosser 		card->options.sbp.role = QETH_SBP_ROLE_NONE;
16131709ff8dSJulian Wiedmann 		card->options.sbp.supported_funcs = 0;
1614b4d72c08SEugene Crosser 		return;
1615b4d72c08SEugene Crosser 	}
1616b4d72c08SEugene Crosser 	card->options.sbp.supported_funcs = cbctl.data.supported;
1617b4d72c08SEugene Crosser }
1618b4d72c08SEugene Crosser 
1619b4d72c08SEugene Crosser static int qeth_bridgeport_query_ports_cb(struct qeth_card *card,
1620b4d72c08SEugene Crosser 	struct qeth_reply *reply, unsigned long data)
1621b4d72c08SEugene Crosser {
1622b4d72c08SEugene Crosser 	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
1623b4d72c08SEugene Crosser 	struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param;
162465b0494eSJulian Wiedmann 	struct qeth_sbp_port_data *qports;
16251709ff8dSJulian Wiedmann 	int rc;
1626b4d72c08SEugene Crosser 
1627b4d72c08SEugene Crosser 	QETH_CARD_TEXT(card, 2, "brqprtcb");
16281709ff8dSJulian Wiedmann 	rc = qeth_bridgeport_makerc(card, cmd);
16291709ff8dSJulian Wiedmann 	if (rc)
16301709ff8dSJulian Wiedmann 		return rc;
16311709ff8dSJulian Wiedmann 
163265b0494eSJulian Wiedmann 	qports = &cmd->data.sbp.data.port_data;
1633b4d72c08SEugene Crosser 	if (qports->entry_length != sizeof(struct qeth_sbp_port_entry)) {
1634b4d72c08SEugene Crosser 		QETH_CARD_TEXT_(card, 2, "SBPs%04x", qports->entry_length);
16351709ff8dSJulian Wiedmann 		return -EINVAL;
1636b4d72c08SEugene Crosser 	}
1637b4d72c08SEugene Crosser 	/* first entry contains the state of the local port */
1638b4d72c08SEugene Crosser 	if (qports->num_entries > 0) {
1639b4d72c08SEugene Crosser 		if (cbctl->data.qports.role)
1640b4d72c08SEugene Crosser 			*cbctl->data.qports.role = qports->entry[0].role;
1641b4d72c08SEugene Crosser 		if (cbctl->data.qports.state)
1642b4d72c08SEugene Crosser 			*cbctl->data.qports.state = qports->entry[0].state;
1643b4d72c08SEugene Crosser 	}
1644b4d72c08SEugene Crosser 	return 0;
1645b4d72c08SEugene Crosser }
1646b4d72c08SEugene Crosser 
1647b4d72c08SEugene Crosser /**
1648b4d72c08SEugene Crosser  * qeth_bridgeport_query_ports() - query local bridgeport status.
1649b4d72c08SEugene Crosser  * @card:			   qeth_card structure pointer.
1650b4d72c08SEugene Crosser  * @role:   Role of the port: 0-none, 1-primary, 2-secondary.
1651b4d72c08SEugene Crosser  * @state:  State of the port: 0-inactive, 1-standby, 2-active.
1652b4d72c08SEugene Crosser  *
1653b4d72c08SEugene Crosser  * Returns negative errno-compatible error indication or 0 on success.
1654b4d72c08SEugene Crosser  *
1655b4d72c08SEugene Crosser  * 'role' and 'state' are not updated in case of hardware operation failure.
1656b4d72c08SEugene Crosser  */
1657b4d72c08SEugene Crosser int qeth_bridgeport_query_ports(struct qeth_card *card,
1658b4d72c08SEugene Crosser 	enum qeth_sbp_roles *role, enum qeth_sbp_states *state)
1659b4d72c08SEugene Crosser {
1660b4d72c08SEugene Crosser 	struct qeth_cmd_buffer *iob;
1661b4d72c08SEugene Crosser 	struct _qeth_sbp_cbctl cbctl = {
1662b4d72c08SEugene Crosser 		.data = {
1663b4d72c08SEugene Crosser 			.qports = {
1664b4d72c08SEugene Crosser 				.role = role,
1665b4d72c08SEugene Crosser 				.state = state,
1666b4d72c08SEugene Crosser 			},
1667b4d72c08SEugene Crosser 		},
1668b4d72c08SEugene Crosser 	};
1669b4d72c08SEugene Crosser 
1670b4d72c08SEugene Crosser 	QETH_CARD_TEXT(card, 2, "brqports");
1671b4d72c08SEugene Crosser 	if (!(card->options.sbp.supported_funcs & IPA_SBP_QUERY_BRIDGE_PORTS))
1672b4d72c08SEugene Crosser 		return -EOPNOTSUPP;
1673d65626adSJulian Wiedmann 	iob = qeth_sbp_build_cmd(card, IPA_SBP_QUERY_BRIDGE_PORTS, 0);
16741aec42bcSThomas Richter 	if (!iob)
16751aec42bcSThomas Richter 		return -ENOMEM;
16761709ff8dSJulian Wiedmann 
16771709ff8dSJulian Wiedmann 	return qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_ports_cb,
16781709ff8dSJulian Wiedmann 				 &cbctl);
1679b4d72c08SEugene Crosser }
1680b4d72c08SEugene Crosser 
1681b4d72c08SEugene Crosser static int qeth_bridgeport_set_cb(struct qeth_card *card,
1682b4d72c08SEugene Crosser 	struct qeth_reply *reply, unsigned long data)
1683b4d72c08SEugene Crosser {
1684b4d72c08SEugene Crosser 	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *)data;
16851709ff8dSJulian Wiedmann 
1686b4d72c08SEugene Crosser 	QETH_CARD_TEXT(card, 2, "brsetrcb");
16871709ff8dSJulian Wiedmann 	return qeth_bridgeport_makerc(card, cmd);
1688b4d72c08SEugene Crosser }
1689b4d72c08SEugene Crosser 
1690b4d72c08SEugene Crosser /**
1691b4d72c08SEugene Crosser  * qeth_bridgeport_setrole() - Assign primary role to the port.
1692b4d72c08SEugene Crosser  * @card:		       qeth_card structure pointer.
1693b4d72c08SEugene Crosser  * @role:		       Role to assign.
1694b4d72c08SEugene Crosser  *
1695b4d72c08SEugene Crosser  * Returns negative errno-compatible error indication or 0 on success.
1696b4d72c08SEugene Crosser  */
1697b4d72c08SEugene Crosser int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role)
1698b4d72c08SEugene Crosser {
1699b4d72c08SEugene Crosser 	struct qeth_cmd_buffer *iob;
1700b4d72c08SEugene Crosser 	enum qeth_ipa_sbp_cmd setcmd;
1701b9150461SJulian Wiedmann 	unsigned int cmdlength = 0;
1702b4d72c08SEugene Crosser 
1703b4d72c08SEugene Crosser 	QETH_CARD_TEXT(card, 2, "brsetrol");
1704b4d72c08SEugene Crosser 	switch (role) {
1705b4d72c08SEugene Crosser 	case QETH_SBP_ROLE_NONE:
1706b4d72c08SEugene Crosser 		setcmd = IPA_SBP_RESET_BRIDGE_PORT_ROLE;
1707b4d72c08SEugene Crosser 		break;
1708b4d72c08SEugene Crosser 	case QETH_SBP_ROLE_PRIMARY:
1709b4d72c08SEugene Crosser 		setcmd = IPA_SBP_SET_PRIMARY_BRIDGE_PORT;
1710b9150461SJulian Wiedmann 		cmdlength = SBP_DATA_SIZEOF(set_primary);
1711b4d72c08SEugene Crosser 		break;
1712b4d72c08SEugene Crosser 	case QETH_SBP_ROLE_SECONDARY:
1713b4d72c08SEugene Crosser 		setcmd = IPA_SBP_SET_SECONDARY_BRIDGE_PORT;
1714b4d72c08SEugene Crosser 		break;
1715b4d72c08SEugene Crosser 	default:
1716b4d72c08SEugene Crosser 		return -EINVAL;
1717b4d72c08SEugene Crosser 	}
1718b4d72c08SEugene Crosser 	if (!(card->options.sbp.supported_funcs & setcmd))
1719b4d72c08SEugene Crosser 		return -EOPNOTSUPP;
1720d65626adSJulian Wiedmann 	iob = qeth_sbp_build_cmd(card, setcmd, cmdlength);
17211aec42bcSThomas Richter 	if (!iob)
17221aec42bcSThomas Richter 		return -ENOMEM;
17231709ff8dSJulian Wiedmann 
17241709ff8dSJulian Wiedmann 	return qeth_send_ipa_cmd(card, iob, qeth_bridgeport_set_cb, NULL);
1725b4d72c08SEugene Crosser }
1726b4d72c08SEugene Crosser 
17279f48b9dbSEugene Crosser static void qeth_bridgeport_an_set_cb(void *priv,
1728a0138f59SAlexandra Winter 				      struct chsc_pnso_naid_l2 *entry)
17299f48b9dbSEugene Crosser {
17309f48b9dbSEugene Crosser 	struct qeth_card *card = (struct qeth_card *)priv;
17319f48b9dbSEugene Crosser 	u8 code;
17329f48b9dbSEugene Crosser 
17339f48b9dbSEugene Crosser 	code = IPA_ADDR_CHANGE_CODE_MACADDR;
1734a0138f59SAlexandra Winter 	if (entry->addr_lnid.lnid < VLAN_N_VID)
17359f48b9dbSEugene Crosser 		code |= IPA_ADDR_CHANGE_CODE_VLANID;
17369f48b9dbSEugene Crosser 	qeth_bridge_emit_host_event(card, anev_reg_unreg, code,
1737a0138f59SAlexandra Winter 				    (struct net_if_token *)&entry->nit,
1738a0138f59SAlexandra Winter 				    (struct mac_addr_lnid *)&entry->addr_lnid);
17399f48b9dbSEugene Crosser }
17409f48b9dbSEugene Crosser 
17419f48b9dbSEugene Crosser /**
17429f48b9dbSEugene Crosser  * qeth_bridgeport_an_set() - Enable or disable bridgeport address notification
17439f48b9dbSEugene Crosser  * @card:		      qeth_card structure pointer.
17449f48b9dbSEugene Crosser  * @enable:		      0 - disable, non-zero - enable notifications
17459f48b9dbSEugene Crosser  *
17469f48b9dbSEugene Crosser  * Returns negative errno-compatible error indication or 0 on success.
17479f48b9dbSEugene Crosser  *
17489f48b9dbSEugene Crosser  * On enable, emits a series of address notifications udev events for all
17499f48b9dbSEugene Crosser  * currently registered hosts.
17509f48b9dbSEugene Crosser  */
17519f48b9dbSEugene Crosser int qeth_bridgeport_an_set(struct qeth_card *card, int enable)
17529f48b9dbSEugene Crosser {
17539f48b9dbSEugene Crosser 	int rc;
17549f48b9dbSEugene Crosser 
17559f48b9dbSEugene Crosser 	if (!card->options.sbp.supported_funcs)
17569f48b9dbSEugene Crosser 		return -EOPNOTSUPP;
17579f48b9dbSEugene Crosser 
17589f48b9dbSEugene Crosser 	if (enable) {
17599f48b9dbSEugene Crosser 		qeth_bridge_emit_host_event(card, anev_reset, 0, NULL, NULL);
17609d6a569aSJulian Wiedmann 		qeth_l2_set_pnso_mode(card, QETH_PNSO_BRIDGEPORT);
17614fea49a7SAlexandra Winter 		rc = qeth_l2_pnso(card, PNSO_OC_NET_BRIDGE_INFO, 1,
17624fea49a7SAlexandra Winter 				  qeth_bridgeport_an_set_cb, card);
17639d6a569aSJulian Wiedmann 		if (rc)
17649d6a569aSJulian Wiedmann 			qeth_l2_set_pnso_mode(card, QETH_PNSO_NONE);
17659d6a569aSJulian Wiedmann 	} else {
17664fea49a7SAlexandra Winter 		rc = qeth_l2_pnso(card, PNSO_OC_NET_BRIDGE_INFO, 0, NULL, NULL);
17679d6a569aSJulian Wiedmann 		qeth_l2_set_pnso_mode(card, QETH_PNSO_NONE);
17689d6a569aSJulian Wiedmann 	}
1769a0138f59SAlexandra Winter 	return rc;
17709f48b9dbSEugene Crosser }
17719f48b9dbSEugene Crosser 
1772a45b3fafSHans Wippel /* VNIC Characteristics support */
1773a45b3fafSHans Wippel 
1774a45b3fafSHans Wippel /* handle VNICC IPA command return codes; convert to error codes */
1775742d4d40SJulian Wiedmann static int qeth_l2_vnicc_makerc(struct qeth_card *card, u16 ipa_rc)
1776a45b3fafSHans Wippel {
1777a45b3fafSHans Wippel 	int rc;
1778a45b3fafSHans Wippel 
1779a45b3fafSHans Wippel 	switch (ipa_rc) {
1780a45b3fafSHans Wippel 	case IPA_RC_SUCCESS:
1781a45b3fafSHans Wippel 		return ipa_rc;
1782a45b3fafSHans Wippel 	case IPA_RC_L2_UNSUPPORTED_CMD:
1783a45b3fafSHans Wippel 	case IPA_RC_NOTSUPP:
1784a45b3fafSHans Wippel 		rc = -EOPNOTSUPP;
1785a45b3fafSHans Wippel 		break;
1786a45b3fafSHans Wippel 	case IPA_RC_VNICC_OOSEQ:
1787a45b3fafSHans Wippel 		rc = -EALREADY;
1788a45b3fafSHans Wippel 		break;
1789a45b3fafSHans Wippel 	case IPA_RC_VNICC_VNICBP:
1790a45b3fafSHans Wippel 		rc = -EBUSY;
1791a45b3fafSHans Wippel 		break;
1792a45b3fafSHans Wippel 	case IPA_RC_L2_ADDR_TABLE_FULL:
1793a45b3fafSHans Wippel 		rc = -ENOSPC;
1794a45b3fafSHans Wippel 		break;
1795a45b3fafSHans Wippel 	case IPA_RC_L2_MAC_NOT_AUTH_BY_ADP:
1796a45b3fafSHans Wippel 		rc = -EACCES;
1797a45b3fafSHans Wippel 		break;
1798a45b3fafSHans Wippel 	default:
1799a45b3fafSHans Wippel 		rc = -EIO;
1800a45b3fafSHans Wippel 	}
1801a45b3fafSHans Wippel 
1802a45b3fafSHans Wippel 	QETH_CARD_TEXT_(card, 2, "err%04x", ipa_rc);
1803a45b3fafSHans Wippel 	return rc;
1804a45b3fafSHans Wippel }
1805a45b3fafSHans Wippel 
1806a45b3fafSHans Wippel /* generic VNICC request call back */
1807a45b3fafSHans Wippel static int qeth_l2_vnicc_request_cb(struct qeth_card *card,
1808a45b3fafSHans Wippel 				    struct qeth_reply *reply,
1809a45b3fafSHans Wippel 				    unsigned long data)
1810a45b3fafSHans Wippel {
1811a45b3fafSHans Wippel 	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
1812a45b3fafSHans Wippel 	struct qeth_ipacmd_vnicc *rep = &cmd->data.vnicc;
18132744d811SJulian Wiedmann 	u32 sub_cmd = cmd->data.vnicc.hdr.sub_command;
1814a45b3fafSHans Wippel 
1815a45b3fafSHans Wippel 	QETH_CARD_TEXT(card, 2, "vniccrcb");
1816a45b3fafSHans Wippel 	if (cmd->hdr.return_code)
1817742d4d40SJulian Wiedmann 		return qeth_l2_vnicc_makerc(card, cmd->hdr.return_code);
1818a45b3fafSHans Wippel 	/* return results to caller */
18192cfb4810SJulian Wiedmann 	card->options.vnicc.sup_chars = rep->vnicc_cmds.supported;
18202cfb4810SJulian Wiedmann 	card->options.vnicc.cur_chars = rep->vnicc_cmds.enabled;
1821a45b3fafSHans Wippel 
18222744d811SJulian Wiedmann 	if (sub_cmd == IPA_VNICC_QUERY_CMDS)
182349f42f5dSJulian Wiedmann 		*(u32 *)reply->param = rep->data.query_cmds.sup_cmds;
18242744d811SJulian Wiedmann 	else if (sub_cmd == IPA_VNICC_GET_TIMEOUT)
182549f42f5dSJulian Wiedmann 		*(u32 *)reply->param = rep->data.getset_timeout.timeout;
1826349d13d5SHans Wippel 
1827a45b3fafSHans Wippel 	return 0;
1828a45b3fafSHans Wippel }
1829a45b3fafSHans Wippel 
18302cfb4810SJulian Wiedmann static struct qeth_cmd_buffer *qeth_l2_vnicc_build_cmd(struct qeth_card *card,
18312cfb4810SJulian Wiedmann 						       u32 vnicc_cmd,
18322cfb4810SJulian Wiedmann 						       unsigned int data_length)
1833a45b3fafSHans Wippel {
18342cfb4810SJulian Wiedmann 	struct qeth_ipacmd_vnicc_hdr *hdr;
1835a45b3fafSHans Wippel 	struct qeth_cmd_buffer *iob;
1836a45b3fafSHans Wippel 
18372cfb4810SJulian Wiedmann 	iob = qeth_ipa_alloc_cmd(card, IPA_CMD_VNICC, QETH_PROT_NONE,
18382cfb4810SJulian Wiedmann 				 data_length +
18392cfb4810SJulian Wiedmann 				 offsetof(struct qeth_ipacmd_vnicc, data));
1840a45b3fafSHans Wippel 	if (!iob)
18412cfb4810SJulian Wiedmann 		return NULL;
1842a45b3fafSHans Wippel 
18432cfb4810SJulian Wiedmann 	hdr = &__ipa_cmd(iob)->data.vnicc.hdr;
18442cfb4810SJulian Wiedmann 	hdr->data_length = sizeof(*hdr) + data_length;
18452cfb4810SJulian Wiedmann 	hdr->sub_command = vnicc_cmd;
18462cfb4810SJulian Wiedmann 	return iob;
1847a45b3fafSHans Wippel }
1848a45b3fafSHans Wippel 
1849a45b3fafSHans Wippel /* VNICC query VNIC characteristics request */
1850a45b3fafSHans Wippel static int qeth_l2_vnicc_query_chars(struct qeth_card *card)
1851a45b3fafSHans Wippel {
18522cfb4810SJulian Wiedmann 	struct qeth_cmd_buffer *iob;
18532cfb4810SJulian Wiedmann 
18542cfb4810SJulian Wiedmann 	QETH_CARD_TEXT(card, 2, "vniccqch");
18552cfb4810SJulian Wiedmann 	iob = qeth_l2_vnicc_build_cmd(card, IPA_VNICC_QUERY_CHARS, 0);
18562cfb4810SJulian Wiedmann 	if (!iob)
18572cfb4810SJulian Wiedmann 		return -ENOMEM;
1858a45b3fafSHans Wippel 
18592744d811SJulian Wiedmann 	return qeth_send_ipa_cmd(card, iob, qeth_l2_vnicc_request_cb, NULL);
1860a45b3fafSHans Wippel }
1861a45b3fafSHans Wippel 
1862caa1f0b1SHans Wippel /* VNICC query sub commands request */
1863caa1f0b1SHans Wippel static int qeth_l2_vnicc_query_cmds(struct qeth_card *card, u32 vnic_char,
1864caa1f0b1SHans Wippel 				    u32 *sup_cmds)
1865caa1f0b1SHans Wippel {
18662cfb4810SJulian Wiedmann 	struct qeth_cmd_buffer *iob;
18672cfb4810SJulian Wiedmann 
18682cfb4810SJulian Wiedmann 	QETH_CARD_TEXT(card, 2, "vniccqcm");
18692cfb4810SJulian Wiedmann 	iob = qeth_l2_vnicc_build_cmd(card, IPA_VNICC_QUERY_CMDS,
18702cfb4810SJulian Wiedmann 				      VNICC_DATA_SIZEOF(query_cmds));
18712cfb4810SJulian Wiedmann 	if (!iob)
18722cfb4810SJulian Wiedmann 		return -ENOMEM;
18732cfb4810SJulian Wiedmann 
18742cfb4810SJulian Wiedmann 	__ipa_cmd(iob)->data.vnicc.data.query_cmds.vnic_char = vnic_char;
1875caa1f0b1SHans Wippel 
187649f42f5dSJulian Wiedmann 	return qeth_send_ipa_cmd(card, iob, qeth_l2_vnicc_request_cb, sup_cmds);
1877caa1f0b1SHans Wippel }
1878caa1f0b1SHans Wippel 
1879caa1f0b1SHans Wippel /* VNICC enable/disable characteristic request */
1880caa1f0b1SHans Wippel static int qeth_l2_vnicc_set_char(struct qeth_card *card, u32 vnic_char,
1881caa1f0b1SHans Wippel 				      u32 cmd)
1882caa1f0b1SHans Wippel {
18832cfb4810SJulian Wiedmann 	struct qeth_cmd_buffer *iob;
18842cfb4810SJulian Wiedmann 
18852cfb4810SJulian Wiedmann 	QETH_CARD_TEXT(card, 2, "vniccedc");
18862cfb4810SJulian Wiedmann 	iob = qeth_l2_vnicc_build_cmd(card, cmd, VNICC_DATA_SIZEOF(set_char));
18872cfb4810SJulian Wiedmann 	if (!iob)
18882cfb4810SJulian Wiedmann 		return -ENOMEM;
18892cfb4810SJulian Wiedmann 
18902cfb4810SJulian Wiedmann 	__ipa_cmd(iob)->data.vnicc.data.set_char.vnic_char = vnic_char;
1891caa1f0b1SHans Wippel 
18922744d811SJulian Wiedmann 	return qeth_send_ipa_cmd(card, iob, qeth_l2_vnicc_request_cb, NULL);
1893caa1f0b1SHans Wippel }
1894caa1f0b1SHans Wippel 
1895349d13d5SHans Wippel /* VNICC get/set timeout for characteristic request */
1896349d13d5SHans Wippel static int qeth_l2_vnicc_getset_timeout(struct qeth_card *card, u32 vnicc,
1897349d13d5SHans Wippel 					u32 cmd, u32 *timeout)
1898349d13d5SHans Wippel {
18992cfb4810SJulian Wiedmann 	struct qeth_vnicc_getset_timeout *getset_timeout;
19002cfb4810SJulian Wiedmann 	struct qeth_cmd_buffer *iob;
19012cfb4810SJulian Wiedmann 
19022cfb4810SJulian Wiedmann 	QETH_CARD_TEXT(card, 2, "vniccgst");
19032cfb4810SJulian Wiedmann 	iob = qeth_l2_vnicc_build_cmd(card, cmd,
19042cfb4810SJulian Wiedmann 				      VNICC_DATA_SIZEOF(getset_timeout));
19052cfb4810SJulian Wiedmann 	if (!iob)
19062cfb4810SJulian Wiedmann 		return -ENOMEM;
19072cfb4810SJulian Wiedmann 
19082cfb4810SJulian Wiedmann 	getset_timeout = &__ipa_cmd(iob)->data.vnicc.data.getset_timeout;
19092cfb4810SJulian Wiedmann 	getset_timeout->vnic_char = vnicc;
19102cfb4810SJulian Wiedmann 
19112cfb4810SJulian Wiedmann 	if (cmd == IPA_VNICC_SET_TIMEOUT)
19122cfb4810SJulian Wiedmann 		getset_timeout->timeout = *timeout;
1913349d13d5SHans Wippel 
191449f42f5dSJulian Wiedmann 	return qeth_send_ipa_cmd(card, iob, qeth_l2_vnicc_request_cb, timeout);
1915349d13d5SHans Wippel }
1916349d13d5SHans Wippel 
191758fa3575SJulian Wiedmann /* recover user timeout setting */
191858fa3575SJulian Wiedmann static bool qeth_l2_vnicc_recover_timeout(struct qeth_card *card, u32 vnicc,
191958fa3575SJulian Wiedmann 					  u32 *timeout)
192058fa3575SJulian Wiedmann {
192158fa3575SJulian Wiedmann 	if (card->options.vnicc.sup_chars & vnicc &&
192258fa3575SJulian Wiedmann 	    card->options.vnicc.getset_timeout_sup & vnicc &&
192358fa3575SJulian Wiedmann 	    !qeth_l2_vnicc_getset_timeout(card, vnicc, IPA_VNICC_SET_TIMEOUT,
192458fa3575SJulian Wiedmann 					  timeout))
192558fa3575SJulian Wiedmann 		return false;
192658fa3575SJulian Wiedmann 	*timeout = QETH_VNICC_DEFAULT_TIMEOUT;
192758fa3575SJulian Wiedmann 	return true;
192858fa3575SJulian Wiedmann }
192958fa3575SJulian Wiedmann 
1930caa1f0b1SHans Wippel /* set current VNICC flag state; called from sysfs store function */
1931caa1f0b1SHans Wippel int qeth_l2_vnicc_set_state(struct qeth_card *card, u32 vnicc, bool state)
1932caa1f0b1SHans Wippel {
1933caa1f0b1SHans Wippel 	int rc = 0;
1934caa1f0b1SHans Wippel 	u32 cmd;
1935caa1f0b1SHans Wippel 
1936caa1f0b1SHans Wippel 	QETH_CARD_TEXT(card, 2, "vniccsch");
1937caa1f0b1SHans Wippel 
1938caa1f0b1SHans Wippel 	/* check if characteristic and enable/disable are supported */
1939caa1f0b1SHans Wippel 	if (!(card->options.vnicc.sup_chars & vnicc) ||
1940caa1f0b1SHans Wippel 	    !(card->options.vnicc.set_char_sup & vnicc))
1941caa1f0b1SHans Wippel 		return -EOPNOTSUPP;
1942caa1f0b1SHans Wippel 
19436f3846f0SAlexandra Winter 	if (qeth_bridgeport_is_in_use(card))
19446f3846f0SAlexandra Winter 		return -EBUSY;
19456f3846f0SAlexandra Winter 
1946caa1f0b1SHans Wippel 	/* set enable/disable command and store wanted characteristic */
1947caa1f0b1SHans Wippel 	if (state) {
1948caa1f0b1SHans Wippel 		cmd = IPA_VNICC_ENABLE;
1949caa1f0b1SHans Wippel 		card->options.vnicc.wanted_chars |= vnicc;
1950caa1f0b1SHans Wippel 	} else {
1951caa1f0b1SHans Wippel 		cmd = IPA_VNICC_DISABLE;
1952caa1f0b1SHans Wippel 		card->options.vnicc.wanted_chars &= ~vnicc;
1953caa1f0b1SHans Wippel 	}
1954caa1f0b1SHans Wippel 
1955caa1f0b1SHans Wippel 	/* do we need to do anything? */
1956caa1f0b1SHans Wippel 	if (card->options.vnicc.cur_chars == card->options.vnicc.wanted_chars)
1957caa1f0b1SHans Wippel 		return rc;
1958caa1f0b1SHans Wippel 
1959caa1f0b1SHans Wippel 	/* if card is not ready, simply stop here */
1960caa1f0b1SHans Wippel 	if (!qeth_card_hw_is_reachable(card)) {
1961caa1f0b1SHans Wippel 		if (state)
1962caa1f0b1SHans Wippel 			card->options.vnicc.cur_chars |= vnicc;
1963caa1f0b1SHans Wippel 		else
1964caa1f0b1SHans Wippel 			card->options.vnicc.cur_chars &= ~vnicc;
1965caa1f0b1SHans Wippel 		return rc;
1966caa1f0b1SHans Wippel 	}
1967caa1f0b1SHans Wippel 
1968caa1f0b1SHans Wippel 	rc = qeth_l2_vnicc_set_char(card, vnicc, cmd);
1969caa1f0b1SHans Wippel 	if (rc)
1970caa1f0b1SHans Wippel 		card->options.vnicc.wanted_chars =
1971caa1f0b1SHans Wippel 			card->options.vnicc.cur_chars;
1972349d13d5SHans Wippel 	else {
1973349d13d5SHans Wippel 		/* successful online VNICC change; handle special cases */
1974349d13d5SHans Wippel 		if (state && vnicc == QETH_VNICC_RX_BCAST)
1975caa1f0b1SHans Wippel 			card->options.vnicc.rx_bcast_enabled = true;
1976349d13d5SHans Wippel 		if (!state && vnicc == QETH_VNICC_LEARNING)
1977349d13d5SHans Wippel 			qeth_l2_vnicc_recover_timeout(card, vnicc,
1978349d13d5SHans Wippel 					&card->options.vnicc.learning_timeout);
1979349d13d5SHans Wippel 	}
1980caa1f0b1SHans Wippel 
1981caa1f0b1SHans Wippel 	return rc;
1982caa1f0b1SHans Wippel }
1983caa1f0b1SHans Wippel 
1984caa1f0b1SHans Wippel /* get current VNICC flag state; called from sysfs show function */
1985caa1f0b1SHans Wippel int qeth_l2_vnicc_get_state(struct qeth_card *card, u32 vnicc, bool *state)
1986caa1f0b1SHans Wippel {
1987caa1f0b1SHans Wippel 	int rc = 0;
1988caa1f0b1SHans Wippel 
1989caa1f0b1SHans Wippel 	QETH_CARD_TEXT(card, 2, "vniccgch");
1990caa1f0b1SHans Wippel 
1991caa1f0b1SHans Wippel 	/* check if characteristic is supported */
1992caa1f0b1SHans Wippel 	if (!(card->options.vnicc.sup_chars & vnicc))
1993caa1f0b1SHans Wippel 		return -EOPNOTSUPP;
1994caa1f0b1SHans Wippel 
19956f3846f0SAlexandra Winter 	if (qeth_bridgeport_is_in_use(card))
19966f3846f0SAlexandra Winter 		return -EBUSY;
19976f3846f0SAlexandra Winter 
1998caa1f0b1SHans Wippel 	/* if card is ready, query current VNICC state */
1999caa1f0b1SHans Wippel 	if (qeth_card_hw_is_reachable(card))
2000caa1f0b1SHans Wippel 		rc = qeth_l2_vnicc_query_chars(card);
2001caa1f0b1SHans Wippel 
2002caa1f0b1SHans Wippel 	*state = (card->options.vnicc.cur_chars & vnicc) ? true : false;
2003caa1f0b1SHans Wippel 	return rc;
2004caa1f0b1SHans Wippel }
2005caa1f0b1SHans Wippel 
2006349d13d5SHans Wippel /* set VNICC timeout; called from sysfs store function. Currently, only learning
2007349d13d5SHans Wippel  * supports timeout
2008349d13d5SHans Wippel  */
2009349d13d5SHans Wippel int qeth_l2_vnicc_set_timeout(struct qeth_card *card, u32 timeout)
2010349d13d5SHans Wippel {
2011349d13d5SHans Wippel 	int rc = 0;
2012349d13d5SHans Wippel 
2013349d13d5SHans Wippel 	QETH_CARD_TEXT(card, 2, "vniccsto");
2014349d13d5SHans Wippel 
2015349d13d5SHans Wippel 	/* check if characteristic and set_timeout are supported */
2016349d13d5SHans Wippel 	if (!(card->options.vnicc.sup_chars & QETH_VNICC_LEARNING) ||
2017349d13d5SHans Wippel 	    !(card->options.vnicc.getset_timeout_sup & QETH_VNICC_LEARNING))
2018349d13d5SHans Wippel 		return -EOPNOTSUPP;
2019349d13d5SHans Wippel 
20206f3846f0SAlexandra Winter 	if (qeth_bridgeport_is_in_use(card))
20216f3846f0SAlexandra Winter 		return -EBUSY;
20226f3846f0SAlexandra Winter 
2023349d13d5SHans Wippel 	/* do we need to do anything? */
2024349d13d5SHans Wippel 	if (card->options.vnicc.learning_timeout == timeout)
2025349d13d5SHans Wippel 		return rc;
2026349d13d5SHans Wippel 
2027349d13d5SHans Wippel 	/* if card is not ready, simply store the value internally and return */
2028349d13d5SHans Wippel 	if (!qeth_card_hw_is_reachable(card)) {
2029349d13d5SHans Wippel 		card->options.vnicc.learning_timeout = timeout;
2030349d13d5SHans Wippel 		return rc;
2031349d13d5SHans Wippel 	}
2032349d13d5SHans Wippel 
2033349d13d5SHans Wippel 	/* send timeout value to card; if successful, store value internally */
2034349d13d5SHans Wippel 	rc = qeth_l2_vnicc_getset_timeout(card, QETH_VNICC_LEARNING,
2035349d13d5SHans Wippel 					  IPA_VNICC_SET_TIMEOUT, &timeout);
2036349d13d5SHans Wippel 	if (!rc)
2037349d13d5SHans Wippel 		card->options.vnicc.learning_timeout = timeout;
2038349d13d5SHans Wippel 
2039349d13d5SHans Wippel 	return rc;
2040349d13d5SHans Wippel }
2041349d13d5SHans Wippel 
2042349d13d5SHans Wippel /* get current VNICC timeout; called from sysfs show function. Currently, only
2043349d13d5SHans Wippel  * learning supports timeout
2044349d13d5SHans Wippel  */
2045349d13d5SHans Wippel int qeth_l2_vnicc_get_timeout(struct qeth_card *card, u32 *timeout)
2046349d13d5SHans Wippel {
2047349d13d5SHans Wippel 	int rc = 0;
2048349d13d5SHans Wippel 
2049349d13d5SHans Wippel 	QETH_CARD_TEXT(card, 2, "vniccgto");
2050349d13d5SHans Wippel 
2051349d13d5SHans Wippel 	/* check if characteristic and get_timeout are supported */
2052349d13d5SHans Wippel 	if (!(card->options.vnicc.sup_chars & QETH_VNICC_LEARNING) ||
2053349d13d5SHans Wippel 	    !(card->options.vnicc.getset_timeout_sup & QETH_VNICC_LEARNING))
2054349d13d5SHans Wippel 		return -EOPNOTSUPP;
20556f3846f0SAlexandra Winter 
20566f3846f0SAlexandra Winter 	if (qeth_bridgeport_is_in_use(card))
20576f3846f0SAlexandra Winter 		return -EBUSY;
20586f3846f0SAlexandra Winter 
2059349d13d5SHans Wippel 	/* if card is ready, get timeout. Otherwise, just return stored value */
2060349d13d5SHans Wippel 	*timeout = card->options.vnicc.learning_timeout;
2061349d13d5SHans Wippel 	if (qeth_card_hw_is_reachable(card))
2062349d13d5SHans Wippel 		rc = qeth_l2_vnicc_getset_timeout(card, QETH_VNICC_LEARNING,
2063349d13d5SHans Wippel 						  IPA_VNICC_GET_TIMEOUT,
2064349d13d5SHans Wippel 						  timeout);
2065349d13d5SHans Wippel 
2066349d13d5SHans Wippel 	return rc;
2067349d13d5SHans Wippel }
2068349d13d5SHans Wippel 
2069caa1f0b1SHans Wippel /* check if VNICC is currently enabled */
2070817741a8SAlexandra Winter static bool _qeth_l2_vnicc_is_in_use(struct qeth_card *card)
2071caa1f0b1SHans Wippel {
2072e8a66d80SAlexandra Winter 	if (!card->options.vnicc.sup_chars)
2073caa1f0b1SHans Wippel 		return false;
2074caa1f0b1SHans Wippel 	/* default values are only OK if rx_bcast was not enabled by user
2075caa1f0b1SHans Wippel 	 * or the card is offline.
2076caa1f0b1SHans Wippel 	 */
2077caa1f0b1SHans Wippel 	if (card->options.vnicc.cur_chars == QETH_VNICC_DEFAULT) {
2078caa1f0b1SHans Wippel 		if (!card->options.vnicc.rx_bcast_enabled ||
2079caa1f0b1SHans Wippel 		    !qeth_card_hw_is_reachable(card))
2080caa1f0b1SHans Wippel 			return false;
2081caa1f0b1SHans Wippel 	}
2082caa1f0b1SHans Wippel 	return true;
2083caa1f0b1SHans Wippel }
2084caa1f0b1SHans Wippel 
2085817741a8SAlexandra Winter /**
2086817741a8SAlexandra Winter  *	qeth_bridgeport_allowed - are any qeth_bridgeport functions allowed?
2087817741a8SAlexandra Winter  *	@card: qeth_card structure pointer
2088817741a8SAlexandra Winter  *
2089817741a8SAlexandra Winter  *	qeth_bridgeport functionality is mutually exclusive with usage of the
2090817741a8SAlexandra Winter  *	VNIC Characteristics and dev2br address notifications
2091817741a8SAlexandra Winter  */
2092817741a8SAlexandra Winter bool qeth_bridgeport_allowed(struct qeth_card *card)
2093817741a8SAlexandra Winter {
2094817741a8SAlexandra Winter 	struct qeth_priv *priv = netdev_priv(card->dev);
2095817741a8SAlexandra Winter 
2096817741a8SAlexandra Winter 	return (!_qeth_l2_vnicc_is_in_use(card) &&
2097817741a8SAlexandra Winter 		!(priv->brport_features & BR_LEARNING_SYNC));
2098817741a8SAlexandra Winter }
2099817741a8SAlexandra Winter 
2100caa1f0b1SHans Wippel /* recover user characteristic setting */
2101caa1f0b1SHans Wippel static bool qeth_l2_vnicc_recover_char(struct qeth_card *card, u32 vnicc,
2102caa1f0b1SHans Wippel 				       bool enable)
2103caa1f0b1SHans Wippel {
2104caa1f0b1SHans Wippel 	u32 cmd = enable ? IPA_VNICC_ENABLE : IPA_VNICC_DISABLE;
2105caa1f0b1SHans Wippel 
2106caa1f0b1SHans Wippel 	if (card->options.vnicc.sup_chars & vnicc &&
2107caa1f0b1SHans Wippel 	    card->options.vnicc.set_char_sup & vnicc &&
2108caa1f0b1SHans Wippel 	    !qeth_l2_vnicc_set_char(card, vnicc, cmd))
2109caa1f0b1SHans Wippel 		return false;
2110caa1f0b1SHans Wippel 	card->options.vnicc.wanted_chars &= ~vnicc;
2111caa1f0b1SHans Wippel 	card->options.vnicc.wanted_chars |= QETH_VNICC_DEFAULT & vnicc;
2112caa1f0b1SHans Wippel 	return true;
2113caa1f0b1SHans Wippel }
2114caa1f0b1SHans Wippel 
2115a45b3fafSHans Wippel /* (re-)initialize VNICC */
2116a45b3fafSHans Wippel static void qeth_l2_vnicc_init(struct qeth_card *card)
2117a45b3fafSHans Wippel {
2118349d13d5SHans Wippel 	u32 *timeout = &card->options.vnicc.learning_timeout;
2119b528965bSAlexandra Winter 	bool enable, error = false;
2120caa1f0b1SHans Wippel 	unsigned int chars_len, i;
2121caa1f0b1SHans Wippel 	unsigned long chars_tmp;
2122caa1f0b1SHans Wippel 	u32 sup_cmds, vnicc;
2123caa1f0b1SHans Wippel 
2124a45b3fafSHans Wippel 	QETH_CARD_TEXT(card, 2, "vniccini");
2125caa1f0b1SHans Wippel 	/* reset rx_bcast */
2126caa1f0b1SHans Wippel 	card->options.vnicc.rx_bcast_enabled = 0;
2127a45b3fafSHans Wippel 	/* initial query and storage of VNIC characteristics */
2128a45b3fafSHans Wippel 	if (qeth_l2_vnicc_query_chars(card)) {
2129349d13d5SHans Wippel 		if (card->options.vnicc.wanted_chars != QETH_VNICC_DEFAULT ||
2130349d13d5SHans Wippel 		    *timeout != QETH_VNICC_DEFAULT_TIMEOUT)
2131caa1f0b1SHans Wippel 			dev_err(&card->gdev->dev, "Configuring the VNIC characteristics failed\n");
2132caa1f0b1SHans Wippel 		/* fail quietly if user didn't change the default config */
2133a45b3fafSHans Wippel 		card->options.vnicc.sup_chars = 0;
2134a45b3fafSHans Wippel 		card->options.vnicc.cur_chars = 0;
2135caa1f0b1SHans Wippel 		card->options.vnicc.wanted_chars = QETH_VNICC_DEFAULT;
2136caa1f0b1SHans Wippel 		return;
2137a45b3fafSHans Wippel 	}
2138caa1f0b1SHans Wippel 	/* get supported commands for each supported characteristic */
2139caa1f0b1SHans Wippel 	chars_tmp = card->options.vnicc.sup_chars;
2140caa1f0b1SHans Wippel 	chars_len = sizeof(card->options.vnicc.sup_chars) * BITS_PER_BYTE;
2141caa1f0b1SHans Wippel 	for_each_set_bit(i, &chars_tmp, chars_len) {
2142caa1f0b1SHans Wippel 		vnicc = BIT(i);
2143b528965bSAlexandra Winter 		if (qeth_l2_vnicc_query_cmds(card, vnicc, &sup_cmds)) {
2144b528965bSAlexandra Winter 			sup_cmds = 0;
2145b528965bSAlexandra Winter 			error = true;
2146b528965bSAlexandra Winter 		}
2147be40a86cSAlexandra Winter 		if ((sup_cmds & IPA_VNICC_SET_TIMEOUT) &&
2148be40a86cSAlexandra Winter 		    (sup_cmds & IPA_VNICC_GET_TIMEOUT))
2149be40a86cSAlexandra Winter 			card->options.vnicc.getset_timeout_sup |= vnicc;
2150be40a86cSAlexandra Winter 		else
2151349d13d5SHans Wippel 			card->options.vnicc.getset_timeout_sup &= ~vnicc;
2152be40a86cSAlexandra Winter 		if ((sup_cmds & IPA_VNICC_ENABLE) &&
2153be40a86cSAlexandra Winter 		    (sup_cmds & IPA_VNICC_DISABLE))
2154be40a86cSAlexandra Winter 			card->options.vnicc.set_char_sup |= vnicc;
2155be40a86cSAlexandra Winter 		else
2156caa1f0b1SHans Wippel 			card->options.vnicc.set_char_sup &= ~vnicc;
2157caa1f0b1SHans Wippel 	}
2158caa1f0b1SHans Wippel 	/* enforce assumed default values and recover settings, if changed  */
2159b528965bSAlexandra Winter 	error |= qeth_l2_vnicc_recover_timeout(card, QETH_VNICC_LEARNING,
2160349d13d5SHans Wippel 					       timeout);
2161d1b9ae18SAlexandra Winter 	/* Change chars, if necessary  */
2162d1b9ae18SAlexandra Winter 	chars_tmp = card->options.vnicc.wanted_chars ^
2163d1b9ae18SAlexandra Winter 		    card->options.vnicc.cur_chars;
2164caa1f0b1SHans Wippel 	chars_len = sizeof(card->options.vnicc.wanted_chars) * BITS_PER_BYTE;
2165caa1f0b1SHans Wippel 	for_each_set_bit(i, &chars_tmp, chars_len) {
2166caa1f0b1SHans Wippel 		vnicc = BIT(i);
2167caa1f0b1SHans Wippel 		enable = card->options.vnicc.wanted_chars & vnicc;
2168caa1f0b1SHans Wippel 		error |= qeth_l2_vnicc_recover_char(card, vnicc, enable);
2169caa1f0b1SHans Wippel 	}
2170caa1f0b1SHans Wippel 	if (error)
2171caa1f0b1SHans Wippel 		dev_err(&card->gdev->dev, "Configuring the VNIC characteristics failed\n");
2172caa1f0b1SHans Wippel }
2173caa1f0b1SHans Wippel 
2174caa1f0b1SHans Wippel /* configure default values of VNIC characteristics */
2175caa1f0b1SHans Wippel static void qeth_l2_vnicc_set_defaults(struct qeth_card *card)
2176caa1f0b1SHans Wippel {
2177caa1f0b1SHans Wippel 	/* characteristics values */
2178caa1f0b1SHans Wippel 	card->options.vnicc.sup_chars = QETH_VNICC_ALL;
2179caa1f0b1SHans Wippel 	card->options.vnicc.cur_chars = QETH_VNICC_DEFAULT;
2180349d13d5SHans Wippel 	card->options.vnicc.learning_timeout = QETH_VNICC_DEFAULT_TIMEOUT;
2181caa1f0b1SHans Wippel 	/* supported commands */
2182caa1f0b1SHans Wippel 	card->options.vnicc.set_char_sup = QETH_VNICC_ALL;
2183349d13d5SHans Wippel 	card->options.vnicc.getset_timeout_sup = QETH_VNICC_LEARNING;
2184caa1f0b1SHans Wippel 	/* settings wanted by users */
2185caa1f0b1SHans Wippel 	card->options.vnicc.wanted_chars = QETH_VNICC_DEFAULT;
2186a45b3fafSHans Wippel }
2187a45b3fafSHans Wippel 
218858fa3575SJulian Wiedmann static const struct device_type qeth_l2_devtype = {
218958fa3575SJulian Wiedmann 	.name = "qeth_layer2",
219058fa3575SJulian Wiedmann 	.groups = qeth_l2_attr_groups,
219158fa3575SJulian Wiedmann };
219258fa3575SJulian Wiedmann 
219358fa3575SJulian Wiedmann static int qeth_l2_probe_device(struct ccwgroup_device *gdev)
219458fa3575SJulian Wiedmann {
219558fa3575SJulian Wiedmann 	struct qeth_card *card = dev_get_drvdata(&gdev->dev);
219658fa3575SJulian Wiedmann 	int rc;
219758fa3575SJulian Wiedmann 
219858fa3575SJulian Wiedmann 	if (IS_OSN(card))
219958fa3575SJulian Wiedmann 		dev_notice(&gdev->dev, "OSN support will be dropped in 2021\n");
220058fa3575SJulian Wiedmann 
220158fa3575SJulian Wiedmann 	qeth_l2_vnicc_set_defaults(card);
220258fa3575SJulian Wiedmann 	mutex_init(&card->sbp_lock);
220358fa3575SJulian Wiedmann 
220458fa3575SJulian Wiedmann 	if (gdev->dev.type == &qeth_generic_devtype) {
220558fa3575SJulian Wiedmann 		rc = qeth_l2_create_device_attributes(&gdev->dev);
220658fa3575SJulian Wiedmann 		if (rc)
220758fa3575SJulian Wiedmann 			return rc;
220858fa3575SJulian Wiedmann 	}
220958fa3575SJulian Wiedmann 
221058fa3575SJulian Wiedmann 	INIT_WORK(&card->rx_mode_work, qeth_l2_rx_mode_work);
221158fa3575SJulian Wiedmann 	return 0;
221258fa3575SJulian Wiedmann }
221358fa3575SJulian Wiedmann 
221458fa3575SJulian Wiedmann static void qeth_l2_remove_device(struct ccwgroup_device *gdev)
221558fa3575SJulian Wiedmann {
221658fa3575SJulian Wiedmann 	struct qeth_card *card = dev_get_drvdata(&gdev->dev);
221758fa3575SJulian Wiedmann 
221858fa3575SJulian Wiedmann 	if (gdev->dev.type == &qeth_generic_devtype)
221958fa3575SJulian Wiedmann 		qeth_l2_remove_device_attributes(&gdev->dev);
222058fa3575SJulian Wiedmann 	qeth_set_allowed_threads(card, 0, 1);
222158fa3575SJulian Wiedmann 	wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0);
222258fa3575SJulian Wiedmann 
222358fa3575SJulian Wiedmann 	if (gdev->state == CCWGROUP_ONLINE)
222458fa3575SJulian Wiedmann 		qeth_set_offline(card, false);
222558fa3575SJulian Wiedmann 
222658fa3575SJulian Wiedmann 	cancel_work_sync(&card->close_dev_work);
222758fa3575SJulian Wiedmann 	if (card->dev->reg_state == NETREG_REGISTERED)
222858fa3575SJulian Wiedmann 		unregister_netdev(card->dev);
222958fa3575SJulian Wiedmann }
223058fa3575SJulian Wiedmann 
223158fa3575SJulian Wiedmann static int qeth_l2_set_online(struct qeth_card *card, bool carrier_ok)
223258fa3575SJulian Wiedmann {
223358fa3575SJulian Wiedmann 	struct net_device *dev = card->dev;
223458fa3575SJulian Wiedmann 	int rc = 0;
223558fa3575SJulian Wiedmann 
223658fa3575SJulian Wiedmann 	/* query before bridgeport_notification may be enabled */
223758fa3575SJulian Wiedmann 	qeth_l2_detect_dev2br_support(card);
223858fa3575SJulian Wiedmann 
223958fa3575SJulian Wiedmann 	mutex_lock(&card->sbp_lock);
224058fa3575SJulian Wiedmann 	qeth_bridgeport_query_support(card);
224158fa3575SJulian Wiedmann 	if (card->options.sbp.supported_funcs) {
224258fa3575SJulian Wiedmann 		qeth_l2_setup_bridgeport_attrs(card);
224358fa3575SJulian Wiedmann 		dev_info(&card->gdev->dev,
224458fa3575SJulian Wiedmann 			 "The device represents a Bridge Capable Port\n");
224558fa3575SJulian Wiedmann 	}
224658fa3575SJulian Wiedmann 	mutex_unlock(&card->sbp_lock);
224758fa3575SJulian Wiedmann 
224858fa3575SJulian Wiedmann 	qeth_l2_register_dev_addr(card);
224958fa3575SJulian Wiedmann 
225058fa3575SJulian Wiedmann 	/* for the rx_bcast characteristic, init VNICC after setmac */
225158fa3575SJulian Wiedmann 	qeth_l2_vnicc_init(card);
225258fa3575SJulian Wiedmann 
225358fa3575SJulian Wiedmann 	qeth_l2_trace_features(card);
225458fa3575SJulian Wiedmann 
225558fa3575SJulian Wiedmann 	/* softsetup */
225658fa3575SJulian Wiedmann 	QETH_CARD_TEXT(card, 2, "softsetp");
225758fa3575SJulian Wiedmann 
225858fa3575SJulian Wiedmann 	card->state = CARD_STATE_SOFTSETUP;
225958fa3575SJulian Wiedmann 
226058fa3575SJulian Wiedmann 	qeth_set_allowed_threads(card, 0xffffffff, 0);
226158fa3575SJulian Wiedmann 
226258fa3575SJulian Wiedmann 	if (dev->reg_state != NETREG_REGISTERED) {
226358fa3575SJulian Wiedmann 		rc = qeth_l2_setup_netdev(card);
226458fa3575SJulian Wiedmann 		if (rc)
226558fa3575SJulian Wiedmann 			goto err_setup;
226658fa3575SJulian Wiedmann 
226758fa3575SJulian Wiedmann 		if (carrier_ok)
226858fa3575SJulian Wiedmann 			netif_carrier_on(dev);
226958fa3575SJulian Wiedmann 	} else {
227058fa3575SJulian Wiedmann 		rtnl_lock();
2271bb5ab541SJulian Wiedmann 		rc = qeth_set_real_num_tx_queues(card,
2272bb5ab541SJulian Wiedmann 						 qeth_tx_actual_queues(card));
2273bb5ab541SJulian Wiedmann 		if (rc) {
2274bb5ab541SJulian Wiedmann 			rtnl_unlock();
2275bb5ab541SJulian Wiedmann 			goto err_set_queues;
2276bb5ab541SJulian Wiedmann 		}
2277bb5ab541SJulian Wiedmann 
227858fa3575SJulian Wiedmann 		if (carrier_ok)
227958fa3575SJulian Wiedmann 			netif_carrier_on(dev);
228058fa3575SJulian Wiedmann 		else
228158fa3575SJulian Wiedmann 			netif_carrier_off(dev);
228258fa3575SJulian Wiedmann 
228358fa3575SJulian Wiedmann 		netif_device_attach(dev);
228458fa3575SJulian Wiedmann 		qeth_enable_hw_features(dev);
228558fa3575SJulian Wiedmann 		qeth_l2_enable_brport_features(card);
228658fa3575SJulian Wiedmann 
228758fa3575SJulian Wiedmann 		if (card->info.open_when_online) {
228858fa3575SJulian Wiedmann 			card->info.open_when_online = 0;
228958fa3575SJulian Wiedmann 			dev_open(dev, NULL);
229058fa3575SJulian Wiedmann 		}
229158fa3575SJulian Wiedmann 		rtnl_unlock();
229258fa3575SJulian Wiedmann 	}
229358fa3575SJulian Wiedmann 	return 0;
229458fa3575SJulian Wiedmann 
2295bb5ab541SJulian Wiedmann err_set_queues:
229658fa3575SJulian Wiedmann err_setup:
229758fa3575SJulian Wiedmann 	qeth_set_allowed_threads(card, 0, 1);
229858fa3575SJulian Wiedmann 	card->state = CARD_STATE_DOWN;
229958fa3575SJulian Wiedmann 	return rc;
230058fa3575SJulian Wiedmann }
230158fa3575SJulian Wiedmann 
230258fa3575SJulian Wiedmann static void qeth_l2_set_offline(struct qeth_card *card)
230358fa3575SJulian Wiedmann {
230458fa3575SJulian Wiedmann 	struct qeth_priv *priv = netdev_priv(card->dev);
230558fa3575SJulian Wiedmann 
230658fa3575SJulian Wiedmann 	qeth_set_allowed_threads(card, 0, 1);
230758fa3575SJulian Wiedmann 	qeth_l2_drain_rx_mode_cache(card);
230858fa3575SJulian Wiedmann 
230958fa3575SJulian Wiedmann 	if (card->state == CARD_STATE_SOFTSETUP)
231058fa3575SJulian Wiedmann 		card->state = CARD_STATE_DOWN;
231158fa3575SJulian Wiedmann 
231258fa3575SJulian Wiedmann 	qeth_l2_set_pnso_mode(card, QETH_PNSO_NONE);
231358fa3575SJulian Wiedmann 	if (priv->brport_features & BR_LEARNING_SYNC) {
231458fa3575SJulian Wiedmann 		rtnl_lock();
231558fa3575SJulian Wiedmann 		qeth_l2_dev2br_fdb_flush(card);
231658fa3575SJulian Wiedmann 		rtnl_unlock();
231758fa3575SJulian Wiedmann 	}
231858fa3575SJulian Wiedmann }
231958fa3575SJulian Wiedmann 
232058fa3575SJulian Wiedmann /* Returns zero if the command is successfully "consumed" */
232158fa3575SJulian Wiedmann static int qeth_l2_control_event(struct qeth_card *card,
232258fa3575SJulian Wiedmann 				 struct qeth_ipa_cmd *cmd)
232358fa3575SJulian Wiedmann {
232458fa3575SJulian Wiedmann 	switch (cmd->hdr.command) {
232558fa3575SJulian Wiedmann 	case IPA_CMD_SETBRIDGEPORT_OSA:
232658fa3575SJulian Wiedmann 	case IPA_CMD_SETBRIDGEPORT_IQD:
232758fa3575SJulian Wiedmann 		if (cmd->data.sbp.hdr.command_code ==
232858fa3575SJulian Wiedmann 		    IPA_SBP_BRIDGE_PORT_STATE_CHANGE) {
232958fa3575SJulian Wiedmann 			qeth_bridge_state_change(card, cmd);
233058fa3575SJulian Wiedmann 			return 0;
233158fa3575SJulian Wiedmann 		}
233258fa3575SJulian Wiedmann 
233358fa3575SJulian Wiedmann 		return 1;
233458fa3575SJulian Wiedmann 	case IPA_CMD_ADDRESS_CHANGE_NOTIF:
233558fa3575SJulian Wiedmann 		qeth_addr_change_event(card, cmd);
233658fa3575SJulian Wiedmann 		return 0;
233758fa3575SJulian Wiedmann 	default:
233858fa3575SJulian Wiedmann 		return 1;
233958fa3575SJulian Wiedmann 	}
234058fa3575SJulian Wiedmann }
234158fa3575SJulian Wiedmann 
234258fa3575SJulian Wiedmann struct qeth_discipline qeth_l2_discipline = {
234358fa3575SJulian Wiedmann 	.devtype = &qeth_l2_devtype,
234458fa3575SJulian Wiedmann 	.setup = qeth_l2_probe_device,
234558fa3575SJulian Wiedmann 	.remove = qeth_l2_remove_device,
234658fa3575SJulian Wiedmann 	.set_online = qeth_l2_set_online,
234758fa3575SJulian Wiedmann 	.set_offline = qeth_l2_set_offline,
234858fa3575SJulian Wiedmann 	.do_ioctl = NULL,
234958fa3575SJulian Wiedmann 	.control_event_handler = qeth_l2_control_event,
235058fa3575SJulian Wiedmann };
235158fa3575SJulian Wiedmann EXPORT_SYMBOL_GPL(qeth_l2_discipline);
235258fa3575SJulian Wiedmann 
235358fa3575SJulian Wiedmann static int __init qeth_l2_init(void)
235458fa3575SJulian Wiedmann {
235558fa3575SJulian Wiedmann 	pr_info("register layer 2 discipline\n");
235658fa3575SJulian Wiedmann 	return 0;
235758fa3575SJulian Wiedmann }
235858fa3575SJulian Wiedmann 
235958fa3575SJulian Wiedmann static void __exit qeth_l2_exit(void)
236058fa3575SJulian Wiedmann {
236158fa3575SJulian Wiedmann 	pr_info("unregister layer 2 discipline\n");
236258fa3575SJulian Wiedmann }
236358fa3575SJulian Wiedmann 
23644a71df50SFrank Blaschka module_init(qeth_l2_init);
23654a71df50SFrank Blaschka module_exit(qeth_l2_exit);
23664a71df50SFrank Blaschka MODULE_AUTHOR("Frank Blaschka <frank.blaschka@de.ibm.com>");
23674a71df50SFrank Blaschka MODULE_DESCRIPTION("qeth layer 2 discipline");
23684a71df50SFrank Blaschka MODULE_LICENSE("GPL");
2369