111ce2ba3SJiri Pirko /*
211ce2ba3SJiri Pirko  * drivers/net/ethernet/rocker/rocker.c - Rocker switch device driver
311ce2ba3SJiri Pirko  * Copyright (c) 2014-2016 Jiri Pirko <jiri@mellanox.com>
411ce2ba3SJiri Pirko  * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com>
511ce2ba3SJiri Pirko  *
611ce2ba3SJiri Pirko  * This program is free software; you can redistribute it and/or modify
711ce2ba3SJiri Pirko  * it under the terms of the GNU General Public License as published by
811ce2ba3SJiri Pirko  * the Free Software Foundation; either version 2 of the License, or
911ce2ba3SJiri Pirko  * (at your option) any later version.
1011ce2ba3SJiri Pirko  */
1111ce2ba3SJiri Pirko 
1211ce2ba3SJiri Pirko #include <linux/kernel.h>
1311ce2ba3SJiri Pirko #include <linux/module.h>
1411ce2ba3SJiri Pirko #include <linux/pci.h>
1511ce2ba3SJiri Pirko #include <linux/interrupt.h>
1611ce2ba3SJiri Pirko #include <linux/sched.h>
1711ce2ba3SJiri Pirko #include <linux/wait.h>
1811ce2ba3SJiri Pirko #include <linux/spinlock.h>
1911ce2ba3SJiri Pirko #include <linux/hashtable.h>
2011ce2ba3SJiri Pirko #include <linux/crc32.h>
2111ce2ba3SJiri Pirko #include <linux/sort.h>
2211ce2ba3SJiri Pirko #include <linux/random.h>
2311ce2ba3SJiri Pirko #include <linux/netdevice.h>
2411ce2ba3SJiri Pirko #include <linux/inetdevice.h>
2511ce2ba3SJiri Pirko #include <linux/skbuff.h>
2611ce2ba3SJiri Pirko #include <linux/socket.h>
2711ce2ba3SJiri Pirko #include <linux/etherdevice.h>
2811ce2ba3SJiri Pirko #include <linux/ethtool.h>
2911ce2ba3SJiri Pirko #include <linux/if_ether.h>
3011ce2ba3SJiri Pirko #include <linux/if_vlan.h>
3111ce2ba3SJiri Pirko #include <linux/if_bridge.h>
3211ce2ba3SJiri Pirko #include <linux/bitops.h>
3311ce2ba3SJiri Pirko #include <linux/ctype.h>
3411ce2ba3SJiri Pirko #include <net/switchdev.h>
3511ce2ba3SJiri Pirko #include <net/rtnetlink.h>
3611ce2ba3SJiri Pirko #include <net/ip_fib.h>
3711ce2ba3SJiri Pirko #include <net/netevent.h>
3811ce2ba3SJiri Pirko #include <net/arp.h>
3911ce2ba3SJiri Pirko #include <linux/io-64-nonatomic-lo-hi.h>
4011ce2ba3SJiri Pirko #include <generated/utsrelease.h>
4111ce2ba3SJiri Pirko 
4211ce2ba3SJiri Pirko #include "rocker_hw.h"
43de152192SJiri Pirko #include "rocker.h"
44de152192SJiri Pirko #include "rocker_tlv.h"
4511ce2ba3SJiri Pirko 
4611ce2ba3SJiri Pirko static const char rocker_driver_name[] = "rocker";
4711ce2ba3SJiri Pirko 
4811ce2ba3SJiri Pirko static const struct pci_device_id rocker_pci_id_table[] = {
4911ce2ba3SJiri Pirko 	{PCI_VDEVICE(REDHAT, PCI_DEVICE_ID_REDHAT_ROCKER), 0},
5011ce2ba3SJiri Pirko 	{0, }
5111ce2ba3SJiri Pirko };
5211ce2ba3SJiri Pirko 
5311ce2ba3SJiri Pirko struct rocker_flow_tbl_key {
5411ce2ba3SJiri Pirko 	u32 priority;
5511ce2ba3SJiri Pirko 	enum rocker_of_dpa_table_id tbl_id;
5611ce2ba3SJiri Pirko 	union {
5711ce2ba3SJiri Pirko 		struct {
5811ce2ba3SJiri Pirko 			u32 in_pport;
5911ce2ba3SJiri Pirko 			u32 in_pport_mask;
6011ce2ba3SJiri Pirko 			enum rocker_of_dpa_table_id goto_tbl;
6111ce2ba3SJiri Pirko 		} ig_port;
6211ce2ba3SJiri Pirko 		struct {
6311ce2ba3SJiri Pirko 			u32 in_pport;
6411ce2ba3SJiri Pirko 			__be16 vlan_id;
6511ce2ba3SJiri Pirko 			__be16 vlan_id_mask;
6611ce2ba3SJiri Pirko 			enum rocker_of_dpa_table_id goto_tbl;
6711ce2ba3SJiri Pirko 			bool untagged;
6811ce2ba3SJiri Pirko 			__be16 new_vlan_id;
6911ce2ba3SJiri Pirko 		} vlan;
7011ce2ba3SJiri Pirko 		struct {
7111ce2ba3SJiri Pirko 			u32 in_pport;
7211ce2ba3SJiri Pirko 			u32 in_pport_mask;
7311ce2ba3SJiri Pirko 			__be16 eth_type;
7411ce2ba3SJiri Pirko 			u8 eth_dst[ETH_ALEN];
7511ce2ba3SJiri Pirko 			u8 eth_dst_mask[ETH_ALEN];
7611ce2ba3SJiri Pirko 			__be16 vlan_id;
7711ce2ba3SJiri Pirko 			__be16 vlan_id_mask;
7811ce2ba3SJiri Pirko 			enum rocker_of_dpa_table_id goto_tbl;
7911ce2ba3SJiri Pirko 			bool copy_to_cpu;
8011ce2ba3SJiri Pirko 		} term_mac;
8111ce2ba3SJiri Pirko 		struct {
8211ce2ba3SJiri Pirko 			__be16 eth_type;
8311ce2ba3SJiri Pirko 			__be32 dst4;
8411ce2ba3SJiri Pirko 			__be32 dst4_mask;
8511ce2ba3SJiri Pirko 			enum rocker_of_dpa_table_id goto_tbl;
8611ce2ba3SJiri Pirko 			u32 group_id;
8711ce2ba3SJiri Pirko 		} ucast_routing;
8811ce2ba3SJiri Pirko 		struct {
8911ce2ba3SJiri Pirko 			u8 eth_dst[ETH_ALEN];
9011ce2ba3SJiri Pirko 			u8 eth_dst_mask[ETH_ALEN];
9111ce2ba3SJiri Pirko 			int has_eth_dst;
9211ce2ba3SJiri Pirko 			int has_eth_dst_mask;
9311ce2ba3SJiri Pirko 			__be16 vlan_id;
9411ce2ba3SJiri Pirko 			u32 tunnel_id;
9511ce2ba3SJiri Pirko 			enum rocker_of_dpa_table_id goto_tbl;
9611ce2ba3SJiri Pirko 			u32 group_id;
9711ce2ba3SJiri Pirko 			bool copy_to_cpu;
9811ce2ba3SJiri Pirko 		} bridge;
9911ce2ba3SJiri Pirko 		struct {
10011ce2ba3SJiri Pirko 			u32 in_pport;
10111ce2ba3SJiri Pirko 			u32 in_pport_mask;
10211ce2ba3SJiri Pirko 			u8 eth_src[ETH_ALEN];
10311ce2ba3SJiri Pirko 			u8 eth_src_mask[ETH_ALEN];
10411ce2ba3SJiri Pirko 			u8 eth_dst[ETH_ALEN];
10511ce2ba3SJiri Pirko 			u8 eth_dst_mask[ETH_ALEN];
10611ce2ba3SJiri Pirko 			__be16 eth_type;
10711ce2ba3SJiri Pirko 			__be16 vlan_id;
10811ce2ba3SJiri Pirko 			__be16 vlan_id_mask;
10911ce2ba3SJiri Pirko 			u8 ip_proto;
11011ce2ba3SJiri Pirko 			u8 ip_proto_mask;
11111ce2ba3SJiri Pirko 			u8 ip_tos;
11211ce2ba3SJiri Pirko 			u8 ip_tos_mask;
11311ce2ba3SJiri Pirko 			u32 group_id;
11411ce2ba3SJiri Pirko 		} acl;
11511ce2ba3SJiri Pirko 	};
11611ce2ba3SJiri Pirko };
11711ce2ba3SJiri Pirko 
11811ce2ba3SJiri Pirko struct rocker_flow_tbl_entry {
11911ce2ba3SJiri Pirko 	struct hlist_node entry;
12011ce2ba3SJiri Pirko 	u32 cmd;
12111ce2ba3SJiri Pirko 	u64 cookie;
12211ce2ba3SJiri Pirko 	struct rocker_flow_tbl_key key;
12311ce2ba3SJiri Pirko 	size_t key_len;
12411ce2ba3SJiri Pirko 	u32 key_crc32; /* key */
12511ce2ba3SJiri Pirko };
12611ce2ba3SJiri Pirko 
12711ce2ba3SJiri Pirko struct rocker_group_tbl_entry {
12811ce2ba3SJiri Pirko 	struct hlist_node entry;
12911ce2ba3SJiri Pirko 	u32 cmd;
13011ce2ba3SJiri Pirko 	u32 group_id; /* key */
13111ce2ba3SJiri Pirko 	u16 group_count;
13211ce2ba3SJiri Pirko 	u32 *group_ids;
13311ce2ba3SJiri Pirko 	union {
13411ce2ba3SJiri Pirko 		struct {
13511ce2ba3SJiri Pirko 			u8 pop_vlan;
13611ce2ba3SJiri Pirko 		} l2_interface;
13711ce2ba3SJiri Pirko 		struct {
13811ce2ba3SJiri Pirko 			u8 eth_src[ETH_ALEN];
13911ce2ba3SJiri Pirko 			u8 eth_dst[ETH_ALEN];
14011ce2ba3SJiri Pirko 			__be16 vlan_id;
14111ce2ba3SJiri Pirko 			u32 group_id;
14211ce2ba3SJiri Pirko 		} l2_rewrite;
14311ce2ba3SJiri Pirko 		struct {
14411ce2ba3SJiri Pirko 			u8 eth_src[ETH_ALEN];
14511ce2ba3SJiri Pirko 			u8 eth_dst[ETH_ALEN];
14611ce2ba3SJiri Pirko 			__be16 vlan_id;
14711ce2ba3SJiri Pirko 			bool ttl_check;
14811ce2ba3SJiri Pirko 			u32 group_id;
14911ce2ba3SJiri Pirko 		} l3_unicast;
15011ce2ba3SJiri Pirko 	};
15111ce2ba3SJiri Pirko };
15211ce2ba3SJiri Pirko 
15311ce2ba3SJiri Pirko struct rocker_fdb_tbl_entry {
15411ce2ba3SJiri Pirko 	struct hlist_node entry;
15511ce2ba3SJiri Pirko 	u32 key_crc32; /* key */
15611ce2ba3SJiri Pirko 	bool learned;
15711ce2ba3SJiri Pirko 	unsigned long touched;
15811ce2ba3SJiri Pirko 	struct rocker_fdb_tbl_key {
15911ce2ba3SJiri Pirko 		struct rocker_port *rocker_port;
16011ce2ba3SJiri Pirko 		u8 addr[ETH_ALEN];
16111ce2ba3SJiri Pirko 		__be16 vlan_id;
16211ce2ba3SJiri Pirko 	} key;
16311ce2ba3SJiri Pirko };
16411ce2ba3SJiri Pirko 
16511ce2ba3SJiri Pirko struct rocker_internal_vlan_tbl_entry {
16611ce2ba3SJiri Pirko 	struct hlist_node entry;
16711ce2ba3SJiri Pirko 	int ifindex; /* key */
16811ce2ba3SJiri Pirko 	u32 ref_count;
16911ce2ba3SJiri Pirko 	__be16 vlan_id;
17011ce2ba3SJiri Pirko };
17111ce2ba3SJiri Pirko 
17211ce2ba3SJiri Pirko struct rocker_neigh_tbl_entry {
17311ce2ba3SJiri Pirko 	struct hlist_node entry;
17411ce2ba3SJiri Pirko 	__be32 ip_addr; /* key */
17511ce2ba3SJiri Pirko 	struct net_device *dev;
17611ce2ba3SJiri Pirko 	u32 ref_count;
17711ce2ba3SJiri Pirko 	u32 index;
17811ce2ba3SJiri Pirko 	u8 eth_dst[ETH_ALEN];
17911ce2ba3SJiri Pirko 	bool ttl_check;
18011ce2ba3SJiri Pirko };
18111ce2ba3SJiri Pirko 
18211ce2ba3SJiri Pirko static const u8 zero_mac[ETH_ALEN]   = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
18311ce2ba3SJiri Pirko static const u8 ff_mac[ETH_ALEN]     = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
18411ce2ba3SJiri Pirko static const u8 ll_mac[ETH_ALEN]     = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
18511ce2ba3SJiri Pirko static const u8 ll_mask[ETH_ALEN]    = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0 };
18611ce2ba3SJiri Pirko static const u8 mcast_mac[ETH_ALEN]  = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };
18711ce2ba3SJiri Pirko static const u8 ipv4_mcast[ETH_ALEN] = { 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00 };
18811ce2ba3SJiri Pirko static const u8 ipv4_mask[ETH_ALEN]  = { 0xff, 0xff, 0xff, 0x80, 0x00, 0x00 };
18911ce2ba3SJiri Pirko static const u8 ipv6_mcast[ETH_ALEN] = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x00 };
19011ce2ba3SJiri Pirko static const u8 ipv6_mask[ETH_ALEN]  = { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 };
19111ce2ba3SJiri Pirko 
19211ce2ba3SJiri Pirko /* Rocker priority levels for flow table entries.  Higher
19311ce2ba3SJiri Pirko  * priority match takes precedence over lower priority match.
19411ce2ba3SJiri Pirko  */
19511ce2ba3SJiri Pirko 
19611ce2ba3SJiri Pirko enum {
19711ce2ba3SJiri Pirko 	ROCKER_PRIORITY_UNKNOWN = 0,
19811ce2ba3SJiri Pirko 	ROCKER_PRIORITY_IG_PORT = 1,
19911ce2ba3SJiri Pirko 	ROCKER_PRIORITY_VLAN = 1,
20011ce2ba3SJiri Pirko 	ROCKER_PRIORITY_TERM_MAC_UCAST = 0,
20111ce2ba3SJiri Pirko 	ROCKER_PRIORITY_TERM_MAC_MCAST = 1,
20211ce2ba3SJiri Pirko 	ROCKER_PRIORITY_BRIDGING_VLAN_DFLT_EXACT = 1,
20311ce2ba3SJiri Pirko 	ROCKER_PRIORITY_BRIDGING_VLAN_DFLT_WILD = 2,
20411ce2ba3SJiri Pirko 	ROCKER_PRIORITY_BRIDGING_VLAN = 3,
20511ce2ba3SJiri Pirko 	ROCKER_PRIORITY_BRIDGING_TENANT_DFLT_EXACT = 1,
20611ce2ba3SJiri Pirko 	ROCKER_PRIORITY_BRIDGING_TENANT_DFLT_WILD = 2,
20711ce2ba3SJiri Pirko 	ROCKER_PRIORITY_BRIDGING_TENANT = 3,
20811ce2ba3SJiri Pirko 	ROCKER_PRIORITY_ACL_CTRL = 3,
20911ce2ba3SJiri Pirko 	ROCKER_PRIORITY_ACL_NORMAL = 2,
21011ce2ba3SJiri Pirko 	ROCKER_PRIORITY_ACL_DFLT = 1,
21111ce2ba3SJiri Pirko };
21211ce2ba3SJiri Pirko 
21311ce2ba3SJiri Pirko static bool rocker_vlan_id_is_internal(__be16 vlan_id)
21411ce2ba3SJiri Pirko {
21511ce2ba3SJiri Pirko 	u16 start = ROCKER_INTERNAL_VLAN_ID_BASE;
21611ce2ba3SJiri Pirko 	u16 end = 0xffe;
21711ce2ba3SJiri Pirko 	u16 _vlan_id = ntohs(vlan_id);
21811ce2ba3SJiri Pirko 
21911ce2ba3SJiri Pirko 	return (_vlan_id >= start && _vlan_id <= end);
22011ce2ba3SJiri Pirko }
22111ce2ba3SJiri Pirko 
22211ce2ba3SJiri Pirko static __be16 rocker_port_vid_to_vlan(const struct rocker_port *rocker_port,
22311ce2ba3SJiri Pirko 				      u16 vid, bool *pop_vlan)
22411ce2ba3SJiri Pirko {
22511ce2ba3SJiri Pirko 	__be16 vlan_id;
22611ce2ba3SJiri Pirko 
22711ce2ba3SJiri Pirko 	if (pop_vlan)
22811ce2ba3SJiri Pirko 		*pop_vlan = false;
22911ce2ba3SJiri Pirko 	vlan_id = htons(vid);
23011ce2ba3SJiri Pirko 	if (!vlan_id) {
23111ce2ba3SJiri Pirko 		vlan_id = rocker_port->internal_vlan_id;
23211ce2ba3SJiri Pirko 		if (pop_vlan)
23311ce2ba3SJiri Pirko 			*pop_vlan = true;
23411ce2ba3SJiri Pirko 	}
23511ce2ba3SJiri Pirko 
23611ce2ba3SJiri Pirko 	return vlan_id;
23711ce2ba3SJiri Pirko }
23811ce2ba3SJiri Pirko 
23911ce2ba3SJiri Pirko static u16 rocker_port_vlan_to_vid(const struct rocker_port *rocker_port,
24011ce2ba3SJiri Pirko 				   __be16 vlan_id)
24111ce2ba3SJiri Pirko {
24211ce2ba3SJiri Pirko 	if (rocker_vlan_id_is_internal(vlan_id))
24311ce2ba3SJiri Pirko 		return 0;
24411ce2ba3SJiri Pirko 
24511ce2ba3SJiri Pirko 	return ntohs(vlan_id);
24611ce2ba3SJiri Pirko }
24711ce2ba3SJiri Pirko 
24811ce2ba3SJiri Pirko static bool rocker_port_is_bridged(const struct rocker_port *rocker_port)
24911ce2ba3SJiri Pirko {
25011ce2ba3SJiri Pirko 	return rocker_port->bridge_dev &&
25111ce2ba3SJiri Pirko 	       netif_is_bridge_master(rocker_port->bridge_dev);
25211ce2ba3SJiri Pirko }
25311ce2ba3SJiri Pirko 
25411ce2ba3SJiri Pirko static bool rocker_port_is_ovsed(const struct rocker_port *rocker_port)
25511ce2ba3SJiri Pirko {
25611ce2ba3SJiri Pirko 	return rocker_port->bridge_dev &&
25711ce2ba3SJiri Pirko 	       netif_is_ovs_master(rocker_port->bridge_dev);
25811ce2ba3SJiri Pirko }
25911ce2ba3SJiri Pirko 
26011ce2ba3SJiri Pirko #define ROCKER_OP_FLAG_REMOVE		BIT(0)
26111ce2ba3SJiri Pirko #define ROCKER_OP_FLAG_NOWAIT		BIT(1)
26211ce2ba3SJiri Pirko #define ROCKER_OP_FLAG_LEARNED		BIT(2)
26311ce2ba3SJiri Pirko #define ROCKER_OP_FLAG_REFRESH		BIT(3)
26411ce2ba3SJiri Pirko 
26511ce2ba3SJiri Pirko static void *__rocker_mem_alloc(struct switchdev_trans *trans, int flags,
26611ce2ba3SJiri Pirko 				size_t size)
26711ce2ba3SJiri Pirko {
26811ce2ba3SJiri Pirko 	struct switchdev_trans_item *elem = NULL;
26911ce2ba3SJiri Pirko 	gfp_t gfp_flags = (flags & ROCKER_OP_FLAG_NOWAIT) ?
27011ce2ba3SJiri Pirko 			  GFP_ATOMIC : GFP_KERNEL;
27111ce2ba3SJiri Pirko 
27211ce2ba3SJiri Pirko 	/* If in transaction prepare phase, allocate the memory
27311ce2ba3SJiri Pirko 	 * and enqueue it on a transaction.  If in transaction
27411ce2ba3SJiri Pirko 	 * commit phase, dequeue the memory from the transaction
27511ce2ba3SJiri Pirko 	 * rather than re-allocating the memory.  The idea is the
27611ce2ba3SJiri Pirko 	 * driver code paths for prepare and commit are identical
27711ce2ba3SJiri Pirko 	 * so the memory allocated in the prepare phase is the
27811ce2ba3SJiri Pirko 	 * memory used in the commit phase.
27911ce2ba3SJiri Pirko 	 */
28011ce2ba3SJiri Pirko 
28111ce2ba3SJiri Pirko 	if (!trans) {
28211ce2ba3SJiri Pirko 		elem = kzalloc(size + sizeof(*elem), gfp_flags);
28311ce2ba3SJiri Pirko 	} else if (switchdev_trans_ph_prepare(trans)) {
28411ce2ba3SJiri Pirko 		elem = kzalloc(size + sizeof(*elem), gfp_flags);
28511ce2ba3SJiri Pirko 		if (!elem)
28611ce2ba3SJiri Pirko 			return NULL;
28711ce2ba3SJiri Pirko 		switchdev_trans_item_enqueue(trans, elem, kfree, elem);
28811ce2ba3SJiri Pirko 	} else {
28911ce2ba3SJiri Pirko 		elem = switchdev_trans_item_dequeue(trans);
29011ce2ba3SJiri Pirko 	}
29111ce2ba3SJiri Pirko 
29211ce2ba3SJiri Pirko 	return elem ? elem + 1 : NULL;
29311ce2ba3SJiri Pirko }
29411ce2ba3SJiri Pirko 
29511ce2ba3SJiri Pirko static void *rocker_kzalloc(struct switchdev_trans *trans, int flags,
29611ce2ba3SJiri Pirko 			    size_t size)
29711ce2ba3SJiri Pirko {
29811ce2ba3SJiri Pirko 	return __rocker_mem_alloc(trans, flags, size);
29911ce2ba3SJiri Pirko }
30011ce2ba3SJiri Pirko 
30111ce2ba3SJiri Pirko static void *rocker_kcalloc(struct switchdev_trans *trans, int flags,
30211ce2ba3SJiri Pirko 			    size_t n, size_t size)
30311ce2ba3SJiri Pirko {
30411ce2ba3SJiri Pirko 	return __rocker_mem_alloc(trans, flags, n * size);
30511ce2ba3SJiri Pirko }
30611ce2ba3SJiri Pirko 
30711ce2ba3SJiri Pirko static void rocker_kfree(struct switchdev_trans *trans, const void *mem)
30811ce2ba3SJiri Pirko {
30911ce2ba3SJiri Pirko 	struct switchdev_trans_item *elem;
31011ce2ba3SJiri Pirko 
31111ce2ba3SJiri Pirko 	/* Frees are ignored if in transaction prepare phase.  The
31211ce2ba3SJiri Pirko 	 * memory remains on the per-port list until freed in the
31311ce2ba3SJiri Pirko 	 * commit phase.
31411ce2ba3SJiri Pirko 	 */
31511ce2ba3SJiri Pirko 
31611ce2ba3SJiri Pirko 	if (switchdev_trans_ph_prepare(trans))
31711ce2ba3SJiri Pirko 		return;
31811ce2ba3SJiri Pirko 
31911ce2ba3SJiri Pirko 	elem = (struct switchdev_trans_item *) mem - 1;
32011ce2ba3SJiri Pirko 	kfree(elem);
32111ce2ba3SJiri Pirko }
32211ce2ba3SJiri Pirko 
32311ce2ba3SJiri Pirko struct rocker_wait {
32411ce2ba3SJiri Pirko 	wait_queue_head_t wait;
32511ce2ba3SJiri Pirko 	bool done;
32611ce2ba3SJiri Pirko 	bool nowait;
32711ce2ba3SJiri Pirko };
32811ce2ba3SJiri Pirko 
32911ce2ba3SJiri Pirko static void rocker_wait_reset(struct rocker_wait *wait)
33011ce2ba3SJiri Pirko {
33111ce2ba3SJiri Pirko 	wait->done = false;
33211ce2ba3SJiri Pirko 	wait->nowait = false;
33311ce2ba3SJiri Pirko }
33411ce2ba3SJiri Pirko 
33511ce2ba3SJiri Pirko static void rocker_wait_init(struct rocker_wait *wait)
33611ce2ba3SJiri Pirko {
33711ce2ba3SJiri Pirko 	init_waitqueue_head(&wait->wait);
33811ce2ba3SJiri Pirko 	rocker_wait_reset(wait);
33911ce2ba3SJiri Pirko }
34011ce2ba3SJiri Pirko 
34111ce2ba3SJiri Pirko static struct rocker_wait *rocker_wait_create(struct rocker_port *rocker_port,
34211ce2ba3SJiri Pirko 					      struct switchdev_trans *trans,
34311ce2ba3SJiri Pirko 					      int flags)
34411ce2ba3SJiri Pirko {
34511ce2ba3SJiri Pirko 	struct rocker_wait *wait;
34611ce2ba3SJiri Pirko 
34711ce2ba3SJiri Pirko 	wait = rocker_kzalloc(trans, flags, sizeof(*wait));
34811ce2ba3SJiri Pirko 	if (!wait)
34911ce2ba3SJiri Pirko 		return NULL;
35011ce2ba3SJiri Pirko 	rocker_wait_init(wait);
35111ce2ba3SJiri Pirko 	return wait;
35211ce2ba3SJiri Pirko }
35311ce2ba3SJiri Pirko 
35411ce2ba3SJiri Pirko static void rocker_wait_destroy(struct switchdev_trans *trans,
35511ce2ba3SJiri Pirko 				struct rocker_wait *wait)
35611ce2ba3SJiri Pirko {
35711ce2ba3SJiri Pirko 	rocker_kfree(trans, wait);
35811ce2ba3SJiri Pirko }
35911ce2ba3SJiri Pirko 
36011ce2ba3SJiri Pirko static bool rocker_wait_event_timeout(struct rocker_wait *wait,
36111ce2ba3SJiri Pirko 				      unsigned long timeout)
36211ce2ba3SJiri Pirko {
36311ce2ba3SJiri Pirko 	wait_event_timeout(wait->wait, wait->done, HZ / 10);
36411ce2ba3SJiri Pirko 	if (!wait->done)
36511ce2ba3SJiri Pirko 		return false;
36611ce2ba3SJiri Pirko 	return true;
36711ce2ba3SJiri Pirko }
36811ce2ba3SJiri Pirko 
36911ce2ba3SJiri Pirko static void rocker_wait_wake_up(struct rocker_wait *wait)
37011ce2ba3SJiri Pirko {
37111ce2ba3SJiri Pirko 	wait->done = true;
37211ce2ba3SJiri Pirko 	wake_up(&wait->wait);
37311ce2ba3SJiri Pirko }
37411ce2ba3SJiri Pirko 
37511ce2ba3SJiri Pirko static u32 rocker_msix_vector(const struct rocker *rocker, unsigned int vector)
37611ce2ba3SJiri Pirko {
37711ce2ba3SJiri Pirko 	return rocker->msix_entries[vector].vector;
37811ce2ba3SJiri Pirko }
37911ce2ba3SJiri Pirko 
38011ce2ba3SJiri Pirko static u32 rocker_msix_tx_vector(const struct rocker_port *rocker_port)
38111ce2ba3SJiri Pirko {
38211ce2ba3SJiri Pirko 	return rocker_msix_vector(rocker_port->rocker,
38311ce2ba3SJiri Pirko 				  ROCKER_MSIX_VEC_TX(rocker_port->port_number));
38411ce2ba3SJiri Pirko }
38511ce2ba3SJiri Pirko 
38611ce2ba3SJiri Pirko static u32 rocker_msix_rx_vector(const struct rocker_port *rocker_port)
38711ce2ba3SJiri Pirko {
38811ce2ba3SJiri Pirko 	return rocker_msix_vector(rocker_port->rocker,
38911ce2ba3SJiri Pirko 				  ROCKER_MSIX_VEC_RX(rocker_port->port_number));
39011ce2ba3SJiri Pirko }
39111ce2ba3SJiri Pirko 
39211ce2ba3SJiri Pirko #define rocker_write32(rocker, reg, val)	\
39311ce2ba3SJiri Pirko 	writel((val), (rocker)->hw_addr + (ROCKER_ ## reg))
39411ce2ba3SJiri Pirko #define rocker_read32(rocker, reg)	\
39511ce2ba3SJiri Pirko 	readl((rocker)->hw_addr + (ROCKER_ ## reg))
39611ce2ba3SJiri Pirko #define rocker_write64(rocker, reg, val)	\
39711ce2ba3SJiri Pirko 	writeq((val), (rocker)->hw_addr + (ROCKER_ ## reg))
39811ce2ba3SJiri Pirko #define rocker_read64(rocker, reg)	\
39911ce2ba3SJiri Pirko 	readq((rocker)->hw_addr + (ROCKER_ ## reg))
40011ce2ba3SJiri Pirko 
40111ce2ba3SJiri Pirko /*****************************
40211ce2ba3SJiri Pirko  * HW basic testing functions
40311ce2ba3SJiri Pirko  *****************************/
40411ce2ba3SJiri Pirko 
40511ce2ba3SJiri Pirko static int rocker_reg_test(const struct rocker *rocker)
40611ce2ba3SJiri Pirko {
40711ce2ba3SJiri Pirko 	const struct pci_dev *pdev = rocker->pdev;
40811ce2ba3SJiri Pirko 	u64 test_reg;
40911ce2ba3SJiri Pirko 	u64 rnd;
41011ce2ba3SJiri Pirko 
41111ce2ba3SJiri Pirko 	rnd = prandom_u32();
41211ce2ba3SJiri Pirko 	rnd >>= 1;
41311ce2ba3SJiri Pirko 	rocker_write32(rocker, TEST_REG, rnd);
41411ce2ba3SJiri Pirko 	test_reg = rocker_read32(rocker, TEST_REG);
41511ce2ba3SJiri Pirko 	if (test_reg != rnd * 2) {
41611ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "unexpected 32bit register value %08llx, expected %08llx\n",
41711ce2ba3SJiri Pirko 			test_reg, rnd * 2);
41811ce2ba3SJiri Pirko 		return -EIO;
41911ce2ba3SJiri Pirko 	}
42011ce2ba3SJiri Pirko 
42111ce2ba3SJiri Pirko 	rnd = prandom_u32();
42211ce2ba3SJiri Pirko 	rnd <<= 31;
42311ce2ba3SJiri Pirko 	rnd |= prandom_u32();
42411ce2ba3SJiri Pirko 	rocker_write64(rocker, TEST_REG64, rnd);
42511ce2ba3SJiri Pirko 	test_reg = rocker_read64(rocker, TEST_REG64);
42611ce2ba3SJiri Pirko 	if (test_reg != rnd * 2) {
42711ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "unexpected 64bit register value %16llx, expected %16llx\n",
42811ce2ba3SJiri Pirko 			test_reg, rnd * 2);
42911ce2ba3SJiri Pirko 		return -EIO;
43011ce2ba3SJiri Pirko 	}
43111ce2ba3SJiri Pirko 
43211ce2ba3SJiri Pirko 	return 0;
43311ce2ba3SJiri Pirko }
43411ce2ba3SJiri Pirko 
43511ce2ba3SJiri Pirko static int rocker_dma_test_one(const struct rocker *rocker,
43611ce2ba3SJiri Pirko 			       struct rocker_wait *wait, u32 test_type,
43711ce2ba3SJiri Pirko 			       dma_addr_t dma_handle, const unsigned char *buf,
43811ce2ba3SJiri Pirko 			       const unsigned char *expect, size_t size)
43911ce2ba3SJiri Pirko {
44011ce2ba3SJiri Pirko 	const struct pci_dev *pdev = rocker->pdev;
44111ce2ba3SJiri Pirko 	int i;
44211ce2ba3SJiri Pirko 
44311ce2ba3SJiri Pirko 	rocker_wait_reset(wait);
44411ce2ba3SJiri Pirko 	rocker_write32(rocker, TEST_DMA_CTRL, test_type);
44511ce2ba3SJiri Pirko 
44611ce2ba3SJiri Pirko 	if (!rocker_wait_event_timeout(wait, HZ / 10)) {
44711ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "no interrupt received within a timeout\n");
44811ce2ba3SJiri Pirko 		return -EIO;
44911ce2ba3SJiri Pirko 	}
45011ce2ba3SJiri Pirko 
45111ce2ba3SJiri Pirko 	for (i = 0; i < size; i++) {
45211ce2ba3SJiri Pirko 		if (buf[i] != expect[i]) {
45311ce2ba3SJiri Pirko 			dev_err(&pdev->dev, "unexpected memory content %02x at byte %x\n, %02x expected",
45411ce2ba3SJiri Pirko 				buf[i], i, expect[i]);
45511ce2ba3SJiri Pirko 			return -EIO;
45611ce2ba3SJiri Pirko 		}
45711ce2ba3SJiri Pirko 	}
45811ce2ba3SJiri Pirko 	return 0;
45911ce2ba3SJiri Pirko }
46011ce2ba3SJiri Pirko 
46111ce2ba3SJiri Pirko #define ROCKER_TEST_DMA_BUF_SIZE (PAGE_SIZE * 4)
46211ce2ba3SJiri Pirko #define ROCKER_TEST_DMA_FILL_PATTERN 0x96
46311ce2ba3SJiri Pirko 
46411ce2ba3SJiri Pirko static int rocker_dma_test_offset(const struct rocker *rocker,
46511ce2ba3SJiri Pirko 				  struct rocker_wait *wait, int offset)
46611ce2ba3SJiri Pirko {
46711ce2ba3SJiri Pirko 	struct pci_dev *pdev = rocker->pdev;
46811ce2ba3SJiri Pirko 	unsigned char *alloc;
46911ce2ba3SJiri Pirko 	unsigned char *buf;
47011ce2ba3SJiri Pirko 	unsigned char *expect;
47111ce2ba3SJiri Pirko 	dma_addr_t dma_handle;
47211ce2ba3SJiri Pirko 	int i;
47311ce2ba3SJiri Pirko 	int err;
47411ce2ba3SJiri Pirko 
47511ce2ba3SJiri Pirko 	alloc = kzalloc(ROCKER_TEST_DMA_BUF_SIZE * 2 + offset,
47611ce2ba3SJiri Pirko 			GFP_KERNEL | GFP_DMA);
47711ce2ba3SJiri Pirko 	if (!alloc)
47811ce2ba3SJiri Pirko 		return -ENOMEM;
47911ce2ba3SJiri Pirko 	buf = alloc + offset;
48011ce2ba3SJiri Pirko 	expect = buf + ROCKER_TEST_DMA_BUF_SIZE;
48111ce2ba3SJiri Pirko 
48211ce2ba3SJiri Pirko 	dma_handle = pci_map_single(pdev, buf, ROCKER_TEST_DMA_BUF_SIZE,
48311ce2ba3SJiri Pirko 				    PCI_DMA_BIDIRECTIONAL);
48411ce2ba3SJiri Pirko 	if (pci_dma_mapping_error(pdev, dma_handle)) {
48511ce2ba3SJiri Pirko 		err = -EIO;
48611ce2ba3SJiri Pirko 		goto free_alloc;
48711ce2ba3SJiri Pirko 	}
48811ce2ba3SJiri Pirko 
48911ce2ba3SJiri Pirko 	rocker_write64(rocker, TEST_DMA_ADDR, dma_handle);
49011ce2ba3SJiri Pirko 	rocker_write32(rocker, TEST_DMA_SIZE, ROCKER_TEST_DMA_BUF_SIZE);
49111ce2ba3SJiri Pirko 
49211ce2ba3SJiri Pirko 	memset(expect, ROCKER_TEST_DMA_FILL_PATTERN, ROCKER_TEST_DMA_BUF_SIZE);
49311ce2ba3SJiri Pirko 	err = rocker_dma_test_one(rocker, wait, ROCKER_TEST_DMA_CTRL_FILL,
49411ce2ba3SJiri Pirko 				  dma_handle, buf, expect,
49511ce2ba3SJiri Pirko 				  ROCKER_TEST_DMA_BUF_SIZE);
49611ce2ba3SJiri Pirko 	if (err)
49711ce2ba3SJiri Pirko 		goto unmap;
49811ce2ba3SJiri Pirko 
49911ce2ba3SJiri Pirko 	memset(expect, 0, ROCKER_TEST_DMA_BUF_SIZE);
50011ce2ba3SJiri Pirko 	err = rocker_dma_test_one(rocker, wait, ROCKER_TEST_DMA_CTRL_CLEAR,
50111ce2ba3SJiri Pirko 				  dma_handle, buf, expect,
50211ce2ba3SJiri Pirko 				  ROCKER_TEST_DMA_BUF_SIZE);
50311ce2ba3SJiri Pirko 	if (err)
50411ce2ba3SJiri Pirko 		goto unmap;
50511ce2ba3SJiri Pirko 
50611ce2ba3SJiri Pirko 	prandom_bytes(buf, ROCKER_TEST_DMA_BUF_SIZE);
50711ce2ba3SJiri Pirko 	for (i = 0; i < ROCKER_TEST_DMA_BUF_SIZE; i++)
50811ce2ba3SJiri Pirko 		expect[i] = ~buf[i];
50911ce2ba3SJiri Pirko 	err = rocker_dma_test_one(rocker, wait, ROCKER_TEST_DMA_CTRL_INVERT,
51011ce2ba3SJiri Pirko 				  dma_handle, buf, expect,
51111ce2ba3SJiri Pirko 				  ROCKER_TEST_DMA_BUF_SIZE);
51211ce2ba3SJiri Pirko 	if (err)
51311ce2ba3SJiri Pirko 		goto unmap;
51411ce2ba3SJiri Pirko 
51511ce2ba3SJiri Pirko unmap:
51611ce2ba3SJiri Pirko 	pci_unmap_single(pdev, dma_handle, ROCKER_TEST_DMA_BUF_SIZE,
51711ce2ba3SJiri Pirko 			 PCI_DMA_BIDIRECTIONAL);
51811ce2ba3SJiri Pirko free_alloc:
51911ce2ba3SJiri Pirko 	kfree(alloc);
52011ce2ba3SJiri Pirko 
52111ce2ba3SJiri Pirko 	return err;
52211ce2ba3SJiri Pirko }
52311ce2ba3SJiri Pirko 
52411ce2ba3SJiri Pirko static int rocker_dma_test(const struct rocker *rocker,
52511ce2ba3SJiri Pirko 			   struct rocker_wait *wait)
52611ce2ba3SJiri Pirko {
52711ce2ba3SJiri Pirko 	int i;
52811ce2ba3SJiri Pirko 	int err;
52911ce2ba3SJiri Pirko 
53011ce2ba3SJiri Pirko 	for (i = 0; i < 8; i++) {
53111ce2ba3SJiri Pirko 		err = rocker_dma_test_offset(rocker, wait, i);
53211ce2ba3SJiri Pirko 		if (err)
53311ce2ba3SJiri Pirko 			return err;
53411ce2ba3SJiri Pirko 	}
53511ce2ba3SJiri Pirko 	return 0;
53611ce2ba3SJiri Pirko }
53711ce2ba3SJiri Pirko 
53811ce2ba3SJiri Pirko static irqreturn_t rocker_test_irq_handler(int irq, void *dev_id)
53911ce2ba3SJiri Pirko {
54011ce2ba3SJiri Pirko 	struct rocker_wait *wait = dev_id;
54111ce2ba3SJiri Pirko 
54211ce2ba3SJiri Pirko 	rocker_wait_wake_up(wait);
54311ce2ba3SJiri Pirko 
54411ce2ba3SJiri Pirko 	return IRQ_HANDLED;
54511ce2ba3SJiri Pirko }
54611ce2ba3SJiri Pirko 
54711ce2ba3SJiri Pirko static int rocker_basic_hw_test(const struct rocker *rocker)
54811ce2ba3SJiri Pirko {
54911ce2ba3SJiri Pirko 	const struct pci_dev *pdev = rocker->pdev;
55011ce2ba3SJiri Pirko 	struct rocker_wait wait;
55111ce2ba3SJiri Pirko 	int err;
55211ce2ba3SJiri Pirko 
55311ce2ba3SJiri Pirko 	err = rocker_reg_test(rocker);
55411ce2ba3SJiri Pirko 	if (err) {
55511ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "reg test failed\n");
55611ce2ba3SJiri Pirko 		return err;
55711ce2ba3SJiri Pirko 	}
55811ce2ba3SJiri Pirko 
55911ce2ba3SJiri Pirko 	err = request_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_TEST),
56011ce2ba3SJiri Pirko 			  rocker_test_irq_handler, 0,
56111ce2ba3SJiri Pirko 			  rocker_driver_name, &wait);
56211ce2ba3SJiri Pirko 	if (err) {
56311ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "cannot assign test irq\n");
56411ce2ba3SJiri Pirko 		return err;
56511ce2ba3SJiri Pirko 	}
56611ce2ba3SJiri Pirko 
56711ce2ba3SJiri Pirko 	rocker_wait_init(&wait);
56811ce2ba3SJiri Pirko 	rocker_write32(rocker, TEST_IRQ, ROCKER_MSIX_VEC_TEST);
56911ce2ba3SJiri Pirko 
57011ce2ba3SJiri Pirko 	if (!rocker_wait_event_timeout(&wait, HZ / 10)) {
57111ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "no interrupt received within a timeout\n");
57211ce2ba3SJiri Pirko 		err = -EIO;
57311ce2ba3SJiri Pirko 		goto free_irq;
57411ce2ba3SJiri Pirko 	}
57511ce2ba3SJiri Pirko 
57611ce2ba3SJiri Pirko 	err = rocker_dma_test(rocker, &wait);
57711ce2ba3SJiri Pirko 	if (err)
57811ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "dma test failed\n");
57911ce2ba3SJiri Pirko 
58011ce2ba3SJiri Pirko free_irq:
58111ce2ba3SJiri Pirko 	free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_TEST), &wait);
58211ce2ba3SJiri Pirko 	return err;
58311ce2ba3SJiri Pirko }
58411ce2ba3SJiri Pirko 
58511ce2ba3SJiri Pirko /******************************************
58611ce2ba3SJiri Pirko  * DMA rings and descriptors manipulations
58711ce2ba3SJiri Pirko  ******************************************/
58811ce2ba3SJiri Pirko 
58911ce2ba3SJiri Pirko static u32 __pos_inc(u32 pos, size_t limit)
59011ce2ba3SJiri Pirko {
59111ce2ba3SJiri Pirko 	return ++pos == limit ? 0 : pos;
59211ce2ba3SJiri Pirko }
59311ce2ba3SJiri Pirko 
59411ce2ba3SJiri Pirko static int rocker_desc_err(const struct rocker_desc_info *desc_info)
59511ce2ba3SJiri Pirko {
59611ce2ba3SJiri Pirko 	int err = desc_info->desc->comp_err & ~ROCKER_DMA_DESC_COMP_ERR_GEN;
59711ce2ba3SJiri Pirko 
59811ce2ba3SJiri Pirko 	switch (err) {
59911ce2ba3SJiri Pirko 	case ROCKER_OK:
60011ce2ba3SJiri Pirko 		return 0;
60111ce2ba3SJiri Pirko 	case -ROCKER_ENOENT:
60211ce2ba3SJiri Pirko 		return -ENOENT;
60311ce2ba3SJiri Pirko 	case -ROCKER_ENXIO:
60411ce2ba3SJiri Pirko 		return -ENXIO;
60511ce2ba3SJiri Pirko 	case -ROCKER_ENOMEM:
60611ce2ba3SJiri Pirko 		return -ENOMEM;
60711ce2ba3SJiri Pirko 	case -ROCKER_EEXIST:
60811ce2ba3SJiri Pirko 		return -EEXIST;
60911ce2ba3SJiri Pirko 	case -ROCKER_EINVAL:
61011ce2ba3SJiri Pirko 		return -EINVAL;
61111ce2ba3SJiri Pirko 	case -ROCKER_EMSGSIZE:
61211ce2ba3SJiri Pirko 		return -EMSGSIZE;
61311ce2ba3SJiri Pirko 	case -ROCKER_ENOTSUP:
61411ce2ba3SJiri Pirko 		return -EOPNOTSUPP;
61511ce2ba3SJiri Pirko 	case -ROCKER_ENOBUFS:
61611ce2ba3SJiri Pirko 		return -ENOBUFS;
61711ce2ba3SJiri Pirko 	}
61811ce2ba3SJiri Pirko 
61911ce2ba3SJiri Pirko 	return -EINVAL;
62011ce2ba3SJiri Pirko }
62111ce2ba3SJiri Pirko 
62211ce2ba3SJiri Pirko static void rocker_desc_gen_clear(const struct rocker_desc_info *desc_info)
62311ce2ba3SJiri Pirko {
62411ce2ba3SJiri Pirko 	desc_info->desc->comp_err &= ~ROCKER_DMA_DESC_COMP_ERR_GEN;
62511ce2ba3SJiri Pirko }
62611ce2ba3SJiri Pirko 
62711ce2ba3SJiri Pirko static bool rocker_desc_gen(const struct rocker_desc_info *desc_info)
62811ce2ba3SJiri Pirko {
62911ce2ba3SJiri Pirko 	u32 comp_err = desc_info->desc->comp_err;
63011ce2ba3SJiri Pirko 
63111ce2ba3SJiri Pirko 	return comp_err & ROCKER_DMA_DESC_COMP_ERR_GEN ? true : false;
63211ce2ba3SJiri Pirko }
63311ce2ba3SJiri Pirko 
63411ce2ba3SJiri Pirko static void *
63511ce2ba3SJiri Pirko rocker_desc_cookie_ptr_get(const struct rocker_desc_info *desc_info)
63611ce2ba3SJiri Pirko {
63711ce2ba3SJiri Pirko 	return (void *)(uintptr_t)desc_info->desc->cookie;
63811ce2ba3SJiri Pirko }
63911ce2ba3SJiri Pirko 
64011ce2ba3SJiri Pirko static void rocker_desc_cookie_ptr_set(const struct rocker_desc_info *desc_info,
64111ce2ba3SJiri Pirko 				       void *ptr)
64211ce2ba3SJiri Pirko {
64311ce2ba3SJiri Pirko 	desc_info->desc->cookie = (uintptr_t) ptr;
64411ce2ba3SJiri Pirko }
64511ce2ba3SJiri Pirko 
64611ce2ba3SJiri Pirko static struct rocker_desc_info *
64711ce2ba3SJiri Pirko rocker_desc_head_get(const struct rocker_dma_ring_info *info)
64811ce2ba3SJiri Pirko {
64911ce2ba3SJiri Pirko 	static struct rocker_desc_info *desc_info;
65011ce2ba3SJiri Pirko 	u32 head = __pos_inc(info->head, info->size);
65111ce2ba3SJiri Pirko 
65211ce2ba3SJiri Pirko 	desc_info = &info->desc_info[info->head];
65311ce2ba3SJiri Pirko 	if (head == info->tail)
65411ce2ba3SJiri Pirko 		return NULL; /* ring full */
65511ce2ba3SJiri Pirko 	desc_info->tlv_size = 0;
65611ce2ba3SJiri Pirko 	return desc_info;
65711ce2ba3SJiri Pirko }
65811ce2ba3SJiri Pirko 
65911ce2ba3SJiri Pirko static void rocker_desc_commit(const struct rocker_desc_info *desc_info)
66011ce2ba3SJiri Pirko {
66111ce2ba3SJiri Pirko 	desc_info->desc->buf_size = desc_info->data_size;
66211ce2ba3SJiri Pirko 	desc_info->desc->tlv_size = desc_info->tlv_size;
66311ce2ba3SJiri Pirko }
66411ce2ba3SJiri Pirko 
66511ce2ba3SJiri Pirko static void rocker_desc_head_set(const struct rocker *rocker,
66611ce2ba3SJiri Pirko 				 struct rocker_dma_ring_info *info,
66711ce2ba3SJiri Pirko 				 const struct rocker_desc_info *desc_info)
66811ce2ba3SJiri Pirko {
66911ce2ba3SJiri Pirko 	u32 head = __pos_inc(info->head, info->size);
67011ce2ba3SJiri Pirko 
67111ce2ba3SJiri Pirko 	BUG_ON(head == info->tail);
67211ce2ba3SJiri Pirko 	rocker_desc_commit(desc_info);
67311ce2ba3SJiri Pirko 	info->head = head;
67411ce2ba3SJiri Pirko 	rocker_write32(rocker, DMA_DESC_HEAD(info->type), head);
67511ce2ba3SJiri Pirko }
67611ce2ba3SJiri Pirko 
67711ce2ba3SJiri Pirko static struct rocker_desc_info *
67811ce2ba3SJiri Pirko rocker_desc_tail_get(struct rocker_dma_ring_info *info)
67911ce2ba3SJiri Pirko {
68011ce2ba3SJiri Pirko 	static struct rocker_desc_info *desc_info;
68111ce2ba3SJiri Pirko 
68211ce2ba3SJiri Pirko 	if (info->tail == info->head)
68311ce2ba3SJiri Pirko 		return NULL; /* nothing to be done between head and tail */
68411ce2ba3SJiri Pirko 	desc_info = &info->desc_info[info->tail];
68511ce2ba3SJiri Pirko 	if (!rocker_desc_gen(desc_info))
68611ce2ba3SJiri Pirko 		return NULL; /* gen bit not set, desc is not ready yet */
68711ce2ba3SJiri Pirko 	info->tail = __pos_inc(info->tail, info->size);
68811ce2ba3SJiri Pirko 	desc_info->tlv_size = desc_info->desc->tlv_size;
68911ce2ba3SJiri Pirko 	return desc_info;
69011ce2ba3SJiri Pirko }
69111ce2ba3SJiri Pirko 
69211ce2ba3SJiri Pirko static void rocker_dma_ring_credits_set(const struct rocker *rocker,
69311ce2ba3SJiri Pirko 					const struct rocker_dma_ring_info *info,
69411ce2ba3SJiri Pirko 					u32 credits)
69511ce2ba3SJiri Pirko {
69611ce2ba3SJiri Pirko 	if (credits)
69711ce2ba3SJiri Pirko 		rocker_write32(rocker, DMA_DESC_CREDITS(info->type), credits);
69811ce2ba3SJiri Pirko }
69911ce2ba3SJiri Pirko 
70011ce2ba3SJiri Pirko static unsigned long rocker_dma_ring_size_fix(size_t size)
70111ce2ba3SJiri Pirko {
70211ce2ba3SJiri Pirko 	return max(ROCKER_DMA_SIZE_MIN,
70311ce2ba3SJiri Pirko 		   min(roundup_pow_of_two(size), ROCKER_DMA_SIZE_MAX));
70411ce2ba3SJiri Pirko }
70511ce2ba3SJiri Pirko 
70611ce2ba3SJiri Pirko static int rocker_dma_ring_create(const struct rocker *rocker,
70711ce2ba3SJiri Pirko 				  unsigned int type,
70811ce2ba3SJiri Pirko 				  size_t size,
70911ce2ba3SJiri Pirko 				  struct rocker_dma_ring_info *info)
71011ce2ba3SJiri Pirko {
71111ce2ba3SJiri Pirko 	int i;
71211ce2ba3SJiri Pirko 
71311ce2ba3SJiri Pirko 	BUG_ON(size != rocker_dma_ring_size_fix(size));
71411ce2ba3SJiri Pirko 	info->size = size;
71511ce2ba3SJiri Pirko 	info->type = type;
71611ce2ba3SJiri Pirko 	info->head = 0;
71711ce2ba3SJiri Pirko 	info->tail = 0;
71811ce2ba3SJiri Pirko 	info->desc_info = kcalloc(info->size, sizeof(*info->desc_info),
71911ce2ba3SJiri Pirko 				  GFP_KERNEL);
72011ce2ba3SJiri Pirko 	if (!info->desc_info)
72111ce2ba3SJiri Pirko 		return -ENOMEM;
72211ce2ba3SJiri Pirko 
72311ce2ba3SJiri Pirko 	info->desc = pci_alloc_consistent(rocker->pdev,
72411ce2ba3SJiri Pirko 					  info->size * sizeof(*info->desc),
72511ce2ba3SJiri Pirko 					  &info->mapaddr);
72611ce2ba3SJiri Pirko 	if (!info->desc) {
72711ce2ba3SJiri Pirko 		kfree(info->desc_info);
72811ce2ba3SJiri Pirko 		return -ENOMEM;
72911ce2ba3SJiri Pirko 	}
73011ce2ba3SJiri Pirko 
73111ce2ba3SJiri Pirko 	for (i = 0; i < info->size; i++)
73211ce2ba3SJiri Pirko 		info->desc_info[i].desc = &info->desc[i];
73311ce2ba3SJiri Pirko 
73411ce2ba3SJiri Pirko 	rocker_write32(rocker, DMA_DESC_CTRL(info->type),
73511ce2ba3SJiri Pirko 		       ROCKER_DMA_DESC_CTRL_RESET);
73611ce2ba3SJiri Pirko 	rocker_write64(rocker, DMA_DESC_ADDR(info->type), info->mapaddr);
73711ce2ba3SJiri Pirko 	rocker_write32(rocker, DMA_DESC_SIZE(info->type), info->size);
73811ce2ba3SJiri Pirko 
73911ce2ba3SJiri Pirko 	return 0;
74011ce2ba3SJiri Pirko }
74111ce2ba3SJiri Pirko 
74211ce2ba3SJiri Pirko static void rocker_dma_ring_destroy(const struct rocker *rocker,
74311ce2ba3SJiri Pirko 				    const struct rocker_dma_ring_info *info)
74411ce2ba3SJiri Pirko {
74511ce2ba3SJiri Pirko 	rocker_write64(rocker, DMA_DESC_ADDR(info->type), 0);
74611ce2ba3SJiri Pirko 
74711ce2ba3SJiri Pirko 	pci_free_consistent(rocker->pdev,
74811ce2ba3SJiri Pirko 			    info->size * sizeof(struct rocker_desc),
74911ce2ba3SJiri Pirko 			    info->desc, info->mapaddr);
75011ce2ba3SJiri Pirko 	kfree(info->desc_info);
75111ce2ba3SJiri Pirko }
75211ce2ba3SJiri Pirko 
75311ce2ba3SJiri Pirko static void rocker_dma_ring_pass_to_producer(const struct rocker *rocker,
75411ce2ba3SJiri Pirko 					     struct rocker_dma_ring_info *info)
75511ce2ba3SJiri Pirko {
75611ce2ba3SJiri Pirko 	int i;
75711ce2ba3SJiri Pirko 
75811ce2ba3SJiri Pirko 	BUG_ON(info->head || info->tail);
75911ce2ba3SJiri Pirko 
76011ce2ba3SJiri Pirko 	/* When ring is consumer, we need to advance head for each desc.
76111ce2ba3SJiri Pirko 	 * That tells hw that the desc is ready to be used by it.
76211ce2ba3SJiri Pirko 	 */
76311ce2ba3SJiri Pirko 	for (i = 0; i < info->size - 1; i++)
76411ce2ba3SJiri Pirko 		rocker_desc_head_set(rocker, info, &info->desc_info[i]);
76511ce2ba3SJiri Pirko 	rocker_desc_commit(&info->desc_info[i]);
76611ce2ba3SJiri Pirko }
76711ce2ba3SJiri Pirko 
76811ce2ba3SJiri Pirko static int rocker_dma_ring_bufs_alloc(const struct rocker *rocker,
76911ce2ba3SJiri Pirko 				      const struct rocker_dma_ring_info *info,
77011ce2ba3SJiri Pirko 				      int direction, size_t buf_size)
77111ce2ba3SJiri Pirko {
77211ce2ba3SJiri Pirko 	struct pci_dev *pdev = rocker->pdev;
77311ce2ba3SJiri Pirko 	int i;
77411ce2ba3SJiri Pirko 	int err;
77511ce2ba3SJiri Pirko 
77611ce2ba3SJiri Pirko 	for (i = 0; i < info->size; i++) {
77711ce2ba3SJiri Pirko 		struct rocker_desc_info *desc_info = &info->desc_info[i];
77811ce2ba3SJiri Pirko 		struct rocker_desc *desc = &info->desc[i];
77911ce2ba3SJiri Pirko 		dma_addr_t dma_handle;
78011ce2ba3SJiri Pirko 		char *buf;
78111ce2ba3SJiri Pirko 
78211ce2ba3SJiri Pirko 		buf = kzalloc(buf_size, GFP_KERNEL | GFP_DMA);
78311ce2ba3SJiri Pirko 		if (!buf) {
78411ce2ba3SJiri Pirko 			err = -ENOMEM;
78511ce2ba3SJiri Pirko 			goto rollback;
78611ce2ba3SJiri Pirko 		}
78711ce2ba3SJiri Pirko 
78811ce2ba3SJiri Pirko 		dma_handle = pci_map_single(pdev, buf, buf_size, direction);
78911ce2ba3SJiri Pirko 		if (pci_dma_mapping_error(pdev, dma_handle)) {
79011ce2ba3SJiri Pirko 			kfree(buf);
79111ce2ba3SJiri Pirko 			err = -EIO;
79211ce2ba3SJiri Pirko 			goto rollback;
79311ce2ba3SJiri Pirko 		}
79411ce2ba3SJiri Pirko 
79511ce2ba3SJiri Pirko 		desc_info->data = buf;
79611ce2ba3SJiri Pirko 		desc_info->data_size = buf_size;
79711ce2ba3SJiri Pirko 		dma_unmap_addr_set(desc_info, mapaddr, dma_handle);
79811ce2ba3SJiri Pirko 
79911ce2ba3SJiri Pirko 		desc->buf_addr = dma_handle;
80011ce2ba3SJiri Pirko 		desc->buf_size = buf_size;
80111ce2ba3SJiri Pirko 	}
80211ce2ba3SJiri Pirko 	return 0;
80311ce2ba3SJiri Pirko 
80411ce2ba3SJiri Pirko rollback:
80511ce2ba3SJiri Pirko 	for (i--; i >= 0; i--) {
80611ce2ba3SJiri Pirko 		const struct rocker_desc_info *desc_info = &info->desc_info[i];
80711ce2ba3SJiri Pirko 
80811ce2ba3SJiri Pirko 		pci_unmap_single(pdev, dma_unmap_addr(desc_info, mapaddr),
80911ce2ba3SJiri Pirko 				 desc_info->data_size, direction);
81011ce2ba3SJiri Pirko 		kfree(desc_info->data);
81111ce2ba3SJiri Pirko 	}
81211ce2ba3SJiri Pirko 	return err;
81311ce2ba3SJiri Pirko }
81411ce2ba3SJiri Pirko 
81511ce2ba3SJiri Pirko static void rocker_dma_ring_bufs_free(const struct rocker *rocker,
81611ce2ba3SJiri Pirko 				      const struct rocker_dma_ring_info *info,
81711ce2ba3SJiri Pirko 				      int direction)
81811ce2ba3SJiri Pirko {
81911ce2ba3SJiri Pirko 	struct pci_dev *pdev = rocker->pdev;
82011ce2ba3SJiri Pirko 	int i;
82111ce2ba3SJiri Pirko 
82211ce2ba3SJiri Pirko 	for (i = 0; i < info->size; i++) {
82311ce2ba3SJiri Pirko 		const struct rocker_desc_info *desc_info = &info->desc_info[i];
82411ce2ba3SJiri Pirko 		struct rocker_desc *desc = &info->desc[i];
82511ce2ba3SJiri Pirko 
82611ce2ba3SJiri Pirko 		desc->buf_addr = 0;
82711ce2ba3SJiri Pirko 		desc->buf_size = 0;
82811ce2ba3SJiri Pirko 		pci_unmap_single(pdev, dma_unmap_addr(desc_info, mapaddr),
82911ce2ba3SJiri Pirko 				 desc_info->data_size, direction);
83011ce2ba3SJiri Pirko 		kfree(desc_info->data);
83111ce2ba3SJiri Pirko 	}
83211ce2ba3SJiri Pirko }
83311ce2ba3SJiri Pirko 
83411ce2ba3SJiri Pirko static int rocker_dma_rings_init(struct rocker *rocker)
83511ce2ba3SJiri Pirko {
83611ce2ba3SJiri Pirko 	const struct pci_dev *pdev = rocker->pdev;
83711ce2ba3SJiri Pirko 	int err;
83811ce2ba3SJiri Pirko 
83911ce2ba3SJiri Pirko 	err = rocker_dma_ring_create(rocker, ROCKER_DMA_CMD,
84011ce2ba3SJiri Pirko 				     ROCKER_DMA_CMD_DEFAULT_SIZE,
84111ce2ba3SJiri Pirko 				     &rocker->cmd_ring);
84211ce2ba3SJiri Pirko 	if (err) {
84311ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "failed to create command dma ring\n");
84411ce2ba3SJiri Pirko 		return err;
84511ce2ba3SJiri Pirko 	}
84611ce2ba3SJiri Pirko 
84711ce2ba3SJiri Pirko 	spin_lock_init(&rocker->cmd_ring_lock);
84811ce2ba3SJiri Pirko 
84911ce2ba3SJiri Pirko 	err = rocker_dma_ring_bufs_alloc(rocker, &rocker->cmd_ring,
85011ce2ba3SJiri Pirko 					 PCI_DMA_BIDIRECTIONAL, PAGE_SIZE);
85111ce2ba3SJiri Pirko 	if (err) {
85211ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "failed to alloc command dma ring buffers\n");
85311ce2ba3SJiri Pirko 		goto err_dma_cmd_ring_bufs_alloc;
85411ce2ba3SJiri Pirko 	}
85511ce2ba3SJiri Pirko 
85611ce2ba3SJiri Pirko 	err = rocker_dma_ring_create(rocker, ROCKER_DMA_EVENT,
85711ce2ba3SJiri Pirko 				     ROCKER_DMA_EVENT_DEFAULT_SIZE,
85811ce2ba3SJiri Pirko 				     &rocker->event_ring);
85911ce2ba3SJiri Pirko 	if (err) {
86011ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "failed to create event dma ring\n");
86111ce2ba3SJiri Pirko 		goto err_dma_event_ring_create;
86211ce2ba3SJiri Pirko 	}
86311ce2ba3SJiri Pirko 
86411ce2ba3SJiri Pirko 	err = rocker_dma_ring_bufs_alloc(rocker, &rocker->event_ring,
86511ce2ba3SJiri Pirko 					 PCI_DMA_FROMDEVICE, PAGE_SIZE);
86611ce2ba3SJiri Pirko 	if (err) {
86711ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "failed to alloc event dma ring buffers\n");
86811ce2ba3SJiri Pirko 		goto err_dma_event_ring_bufs_alloc;
86911ce2ba3SJiri Pirko 	}
87011ce2ba3SJiri Pirko 	rocker_dma_ring_pass_to_producer(rocker, &rocker->event_ring);
87111ce2ba3SJiri Pirko 	return 0;
87211ce2ba3SJiri Pirko 
87311ce2ba3SJiri Pirko err_dma_event_ring_bufs_alloc:
87411ce2ba3SJiri Pirko 	rocker_dma_ring_destroy(rocker, &rocker->event_ring);
87511ce2ba3SJiri Pirko err_dma_event_ring_create:
87611ce2ba3SJiri Pirko 	rocker_dma_ring_bufs_free(rocker, &rocker->cmd_ring,
87711ce2ba3SJiri Pirko 				  PCI_DMA_BIDIRECTIONAL);
87811ce2ba3SJiri Pirko err_dma_cmd_ring_bufs_alloc:
87911ce2ba3SJiri Pirko 	rocker_dma_ring_destroy(rocker, &rocker->cmd_ring);
88011ce2ba3SJiri Pirko 	return err;
88111ce2ba3SJiri Pirko }
88211ce2ba3SJiri Pirko 
88311ce2ba3SJiri Pirko static void rocker_dma_rings_fini(struct rocker *rocker)
88411ce2ba3SJiri Pirko {
88511ce2ba3SJiri Pirko 	rocker_dma_ring_bufs_free(rocker, &rocker->event_ring,
88611ce2ba3SJiri Pirko 				  PCI_DMA_BIDIRECTIONAL);
88711ce2ba3SJiri Pirko 	rocker_dma_ring_destroy(rocker, &rocker->event_ring);
88811ce2ba3SJiri Pirko 	rocker_dma_ring_bufs_free(rocker, &rocker->cmd_ring,
88911ce2ba3SJiri Pirko 				  PCI_DMA_BIDIRECTIONAL);
89011ce2ba3SJiri Pirko 	rocker_dma_ring_destroy(rocker, &rocker->cmd_ring);
89111ce2ba3SJiri Pirko }
89211ce2ba3SJiri Pirko 
89311ce2ba3SJiri Pirko static int rocker_dma_rx_ring_skb_map(const struct rocker_port *rocker_port,
89411ce2ba3SJiri Pirko 				      struct rocker_desc_info *desc_info,
89511ce2ba3SJiri Pirko 				      struct sk_buff *skb, size_t buf_len)
89611ce2ba3SJiri Pirko {
89711ce2ba3SJiri Pirko 	const struct rocker *rocker = rocker_port->rocker;
89811ce2ba3SJiri Pirko 	struct pci_dev *pdev = rocker->pdev;
89911ce2ba3SJiri Pirko 	dma_addr_t dma_handle;
90011ce2ba3SJiri Pirko 
90111ce2ba3SJiri Pirko 	dma_handle = pci_map_single(pdev, skb->data, buf_len,
90211ce2ba3SJiri Pirko 				    PCI_DMA_FROMDEVICE);
90311ce2ba3SJiri Pirko 	if (pci_dma_mapping_error(pdev, dma_handle))
90411ce2ba3SJiri Pirko 		return -EIO;
90511ce2ba3SJiri Pirko 	if (rocker_tlv_put_u64(desc_info, ROCKER_TLV_RX_FRAG_ADDR, dma_handle))
90611ce2ba3SJiri Pirko 		goto tlv_put_failure;
90711ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_RX_FRAG_MAX_LEN, buf_len))
90811ce2ba3SJiri Pirko 		goto tlv_put_failure;
90911ce2ba3SJiri Pirko 	return 0;
91011ce2ba3SJiri Pirko 
91111ce2ba3SJiri Pirko tlv_put_failure:
91211ce2ba3SJiri Pirko 	pci_unmap_single(pdev, dma_handle, buf_len, PCI_DMA_FROMDEVICE);
91311ce2ba3SJiri Pirko 	desc_info->tlv_size = 0;
91411ce2ba3SJiri Pirko 	return -EMSGSIZE;
91511ce2ba3SJiri Pirko }
91611ce2ba3SJiri Pirko 
91711ce2ba3SJiri Pirko static size_t rocker_port_rx_buf_len(const struct rocker_port *rocker_port)
91811ce2ba3SJiri Pirko {
91911ce2ba3SJiri Pirko 	return rocker_port->dev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
92011ce2ba3SJiri Pirko }
92111ce2ba3SJiri Pirko 
92211ce2ba3SJiri Pirko static int rocker_dma_rx_ring_skb_alloc(const struct rocker_port *rocker_port,
92311ce2ba3SJiri Pirko 					struct rocker_desc_info *desc_info)
92411ce2ba3SJiri Pirko {
92511ce2ba3SJiri Pirko 	struct net_device *dev = rocker_port->dev;
92611ce2ba3SJiri Pirko 	struct sk_buff *skb;
92711ce2ba3SJiri Pirko 	size_t buf_len = rocker_port_rx_buf_len(rocker_port);
92811ce2ba3SJiri Pirko 	int err;
92911ce2ba3SJiri Pirko 
93011ce2ba3SJiri Pirko 	/* Ensure that hw will see tlv_size zero in case of an error.
93111ce2ba3SJiri Pirko 	 * That tells hw to use another descriptor.
93211ce2ba3SJiri Pirko 	 */
93311ce2ba3SJiri Pirko 	rocker_desc_cookie_ptr_set(desc_info, NULL);
93411ce2ba3SJiri Pirko 	desc_info->tlv_size = 0;
93511ce2ba3SJiri Pirko 
93611ce2ba3SJiri Pirko 	skb = netdev_alloc_skb_ip_align(dev, buf_len);
93711ce2ba3SJiri Pirko 	if (!skb)
93811ce2ba3SJiri Pirko 		return -ENOMEM;
93911ce2ba3SJiri Pirko 	err = rocker_dma_rx_ring_skb_map(rocker_port, desc_info, skb, buf_len);
94011ce2ba3SJiri Pirko 	if (err) {
94111ce2ba3SJiri Pirko 		dev_kfree_skb_any(skb);
94211ce2ba3SJiri Pirko 		return err;
94311ce2ba3SJiri Pirko 	}
94411ce2ba3SJiri Pirko 	rocker_desc_cookie_ptr_set(desc_info, skb);
94511ce2ba3SJiri Pirko 	return 0;
94611ce2ba3SJiri Pirko }
94711ce2ba3SJiri Pirko 
94811ce2ba3SJiri Pirko static void rocker_dma_rx_ring_skb_unmap(const struct rocker *rocker,
94911ce2ba3SJiri Pirko 					 const struct rocker_tlv **attrs)
95011ce2ba3SJiri Pirko {
95111ce2ba3SJiri Pirko 	struct pci_dev *pdev = rocker->pdev;
95211ce2ba3SJiri Pirko 	dma_addr_t dma_handle;
95311ce2ba3SJiri Pirko 	size_t len;
95411ce2ba3SJiri Pirko 
95511ce2ba3SJiri Pirko 	if (!attrs[ROCKER_TLV_RX_FRAG_ADDR] ||
95611ce2ba3SJiri Pirko 	    !attrs[ROCKER_TLV_RX_FRAG_MAX_LEN])
95711ce2ba3SJiri Pirko 		return;
95811ce2ba3SJiri Pirko 	dma_handle = rocker_tlv_get_u64(attrs[ROCKER_TLV_RX_FRAG_ADDR]);
95911ce2ba3SJiri Pirko 	len = rocker_tlv_get_u16(attrs[ROCKER_TLV_RX_FRAG_MAX_LEN]);
96011ce2ba3SJiri Pirko 	pci_unmap_single(pdev, dma_handle, len, PCI_DMA_FROMDEVICE);
96111ce2ba3SJiri Pirko }
96211ce2ba3SJiri Pirko 
96311ce2ba3SJiri Pirko static void rocker_dma_rx_ring_skb_free(const struct rocker *rocker,
96411ce2ba3SJiri Pirko 					const struct rocker_desc_info *desc_info)
96511ce2ba3SJiri Pirko {
96611ce2ba3SJiri Pirko 	const struct rocker_tlv *attrs[ROCKER_TLV_RX_MAX + 1];
96711ce2ba3SJiri Pirko 	struct sk_buff *skb = rocker_desc_cookie_ptr_get(desc_info);
96811ce2ba3SJiri Pirko 
96911ce2ba3SJiri Pirko 	if (!skb)
97011ce2ba3SJiri Pirko 		return;
97111ce2ba3SJiri Pirko 	rocker_tlv_parse_desc(attrs, ROCKER_TLV_RX_MAX, desc_info);
97211ce2ba3SJiri Pirko 	rocker_dma_rx_ring_skb_unmap(rocker, attrs);
97311ce2ba3SJiri Pirko 	dev_kfree_skb_any(skb);
97411ce2ba3SJiri Pirko }
97511ce2ba3SJiri Pirko 
97611ce2ba3SJiri Pirko static int rocker_dma_rx_ring_skbs_alloc(const struct rocker_port *rocker_port)
97711ce2ba3SJiri Pirko {
97811ce2ba3SJiri Pirko 	const struct rocker_dma_ring_info *rx_ring = &rocker_port->rx_ring;
97911ce2ba3SJiri Pirko 	const struct rocker *rocker = rocker_port->rocker;
98011ce2ba3SJiri Pirko 	int i;
98111ce2ba3SJiri Pirko 	int err;
98211ce2ba3SJiri Pirko 
98311ce2ba3SJiri Pirko 	for (i = 0; i < rx_ring->size; i++) {
98411ce2ba3SJiri Pirko 		err = rocker_dma_rx_ring_skb_alloc(rocker_port,
98511ce2ba3SJiri Pirko 						   &rx_ring->desc_info[i]);
98611ce2ba3SJiri Pirko 		if (err)
98711ce2ba3SJiri Pirko 			goto rollback;
98811ce2ba3SJiri Pirko 	}
98911ce2ba3SJiri Pirko 	return 0;
99011ce2ba3SJiri Pirko 
99111ce2ba3SJiri Pirko rollback:
99211ce2ba3SJiri Pirko 	for (i--; i >= 0; i--)
99311ce2ba3SJiri Pirko 		rocker_dma_rx_ring_skb_free(rocker, &rx_ring->desc_info[i]);
99411ce2ba3SJiri Pirko 	return err;
99511ce2ba3SJiri Pirko }
99611ce2ba3SJiri Pirko 
99711ce2ba3SJiri Pirko static void rocker_dma_rx_ring_skbs_free(const struct rocker_port *rocker_port)
99811ce2ba3SJiri Pirko {
99911ce2ba3SJiri Pirko 	const struct rocker_dma_ring_info *rx_ring = &rocker_port->rx_ring;
100011ce2ba3SJiri Pirko 	const struct rocker *rocker = rocker_port->rocker;
100111ce2ba3SJiri Pirko 	int i;
100211ce2ba3SJiri Pirko 
100311ce2ba3SJiri Pirko 	for (i = 0; i < rx_ring->size; i++)
100411ce2ba3SJiri Pirko 		rocker_dma_rx_ring_skb_free(rocker, &rx_ring->desc_info[i]);
100511ce2ba3SJiri Pirko }
100611ce2ba3SJiri Pirko 
100711ce2ba3SJiri Pirko static int rocker_port_dma_rings_init(struct rocker_port *rocker_port)
100811ce2ba3SJiri Pirko {
100911ce2ba3SJiri Pirko 	struct rocker *rocker = rocker_port->rocker;
101011ce2ba3SJiri Pirko 	int err;
101111ce2ba3SJiri Pirko 
101211ce2ba3SJiri Pirko 	err = rocker_dma_ring_create(rocker,
101311ce2ba3SJiri Pirko 				     ROCKER_DMA_TX(rocker_port->port_number),
101411ce2ba3SJiri Pirko 				     ROCKER_DMA_TX_DEFAULT_SIZE,
101511ce2ba3SJiri Pirko 				     &rocker_port->tx_ring);
101611ce2ba3SJiri Pirko 	if (err) {
101711ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev, "failed to create tx dma ring\n");
101811ce2ba3SJiri Pirko 		return err;
101911ce2ba3SJiri Pirko 	}
102011ce2ba3SJiri Pirko 
102111ce2ba3SJiri Pirko 	err = rocker_dma_ring_bufs_alloc(rocker, &rocker_port->tx_ring,
102211ce2ba3SJiri Pirko 					 PCI_DMA_TODEVICE,
102311ce2ba3SJiri Pirko 					 ROCKER_DMA_TX_DESC_SIZE);
102411ce2ba3SJiri Pirko 	if (err) {
102511ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev, "failed to alloc tx dma ring buffers\n");
102611ce2ba3SJiri Pirko 		goto err_dma_tx_ring_bufs_alloc;
102711ce2ba3SJiri Pirko 	}
102811ce2ba3SJiri Pirko 
102911ce2ba3SJiri Pirko 	err = rocker_dma_ring_create(rocker,
103011ce2ba3SJiri Pirko 				     ROCKER_DMA_RX(rocker_port->port_number),
103111ce2ba3SJiri Pirko 				     ROCKER_DMA_RX_DEFAULT_SIZE,
103211ce2ba3SJiri Pirko 				     &rocker_port->rx_ring);
103311ce2ba3SJiri Pirko 	if (err) {
103411ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev, "failed to create rx dma ring\n");
103511ce2ba3SJiri Pirko 		goto err_dma_rx_ring_create;
103611ce2ba3SJiri Pirko 	}
103711ce2ba3SJiri Pirko 
103811ce2ba3SJiri Pirko 	err = rocker_dma_ring_bufs_alloc(rocker, &rocker_port->rx_ring,
103911ce2ba3SJiri Pirko 					 PCI_DMA_BIDIRECTIONAL,
104011ce2ba3SJiri Pirko 					 ROCKER_DMA_RX_DESC_SIZE);
104111ce2ba3SJiri Pirko 	if (err) {
104211ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev, "failed to alloc rx dma ring buffers\n");
104311ce2ba3SJiri Pirko 		goto err_dma_rx_ring_bufs_alloc;
104411ce2ba3SJiri Pirko 	}
104511ce2ba3SJiri Pirko 
104611ce2ba3SJiri Pirko 	err = rocker_dma_rx_ring_skbs_alloc(rocker_port);
104711ce2ba3SJiri Pirko 	if (err) {
104811ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev, "failed to alloc rx dma ring skbs\n");
104911ce2ba3SJiri Pirko 		goto err_dma_rx_ring_skbs_alloc;
105011ce2ba3SJiri Pirko 	}
105111ce2ba3SJiri Pirko 	rocker_dma_ring_pass_to_producer(rocker, &rocker_port->rx_ring);
105211ce2ba3SJiri Pirko 
105311ce2ba3SJiri Pirko 	return 0;
105411ce2ba3SJiri Pirko 
105511ce2ba3SJiri Pirko err_dma_rx_ring_skbs_alloc:
105611ce2ba3SJiri Pirko 	rocker_dma_ring_bufs_free(rocker, &rocker_port->rx_ring,
105711ce2ba3SJiri Pirko 				  PCI_DMA_BIDIRECTIONAL);
105811ce2ba3SJiri Pirko err_dma_rx_ring_bufs_alloc:
105911ce2ba3SJiri Pirko 	rocker_dma_ring_destroy(rocker, &rocker_port->rx_ring);
106011ce2ba3SJiri Pirko err_dma_rx_ring_create:
106111ce2ba3SJiri Pirko 	rocker_dma_ring_bufs_free(rocker, &rocker_port->tx_ring,
106211ce2ba3SJiri Pirko 				  PCI_DMA_TODEVICE);
106311ce2ba3SJiri Pirko err_dma_tx_ring_bufs_alloc:
106411ce2ba3SJiri Pirko 	rocker_dma_ring_destroy(rocker, &rocker_port->tx_ring);
106511ce2ba3SJiri Pirko 	return err;
106611ce2ba3SJiri Pirko }
106711ce2ba3SJiri Pirko 
106811ce2ba3SJiri Pirko static void rocker_port_dma_rings_fini(struct rocker_port *rocker_port)
106911ce2ba3SJiri Pirko {
107011ce2ba3SJiri Pirko 	struct rocker *rocker = rocker_port->rocker;
107111ce2ba3SJiri Pirko 
107211ce2ba3SJiri Pirko 	rocker_dma_rx_ring_skbs_free(rocker_port);
107311ce2ba3SJiri Pirko 	rocker_dma_ring_bufs_free(rocker, &rocker_port->rx_ring,
107411ce2ba3SJiri Pirko 				  PCI_DMA_BIDIRECTIONAL);
107511ce2ba3SJiri Pirko 	rocker_dma_ring_destroy(rocker, &rocker_port->rx_ring);
107611ce2ba3SJiri Pirko 	rocker_dma_ring_bufs_free(rocker, &rocker_port->tx_ring,
107711ce2ba3SJiri Pirko 				  PCI_DMA_TODEVICE);
107811ce2ba3SJiri Pirko 	rocker_dma_ring_destroy(rocker, &rocker_port->tx_ring);
107911ce2ba3SJiri Pirko }
108011ce2ba3SJiri Pirko 
108111ce2ba3SJiri Pirko static void rocker_port_set_enable(const struct rocker_port *rocker_port,
108211ce2ba3SJiri Pirko 				   bool enable)
108311ce2ba3SJiri Pirko {
108411ce2ba3SJiri Pirko 	u64 val = rocker_read64(rocker_port->rocker, PORT_PHYS_ENABLE);
108511ce2ba3SJiri Pirko 
108611ce2ba3SJiri Pirko 	if (enable)
108711ce2ba3SJiri Pirko 		val |= 1ULL << rocker_port->pport;
108811ce2ba3SJiri Pirko 	else
108911ce2ba3SJiri Pirko 		val &= ~(1ULL << rocker_port->pport);
109011ce2ba3SJiri Pirko 	rocker_write64(rocker_port->rocker, PORT_PHYS_ENABLE, val);
109111ce2ba3SJiri Pirko }
109211ce2ba3SJiri Pirko 
109311ce2ba3SJiri Pirko /********************************
109411ce2ba3SJiri Pirko  * Interrupt handler and helpers
109511ce2ba3SJiri Pirko  ********************************/
109611ce2ba3SJiri Pirko 
109711ce2ba3SJiri Pirko static irqreturn_t rocker_cmd_irq_handler(int irq, void *dev_id)
109811ce2ba3SJiri Pirko {
109911ce2ba3SJiri Pirko 	struct rocker *rocker = dev_id;
110011ce2ba3SJiri Pirko 	const struct rocker_desc_info *desc_info;
110111ce2ba3SJiri Pirko 	struct rocker_wait *wait;
110211ce2ba3SJiri Pirko 	u32 credits = 0;
110311ce2ba3SJiri Pirko 
110411ce2ba3SJiri Pirko 	spin_lock(&rocker->cmd_ring_lock);
110511ce2ba3SJiri Pirko 	while ((desc_info = rocker_desc_tail_get(&rocker->cmd_ring))) {
110611ce2ba3SJiri Pirko 		wait = rocker_desc_cookie_ptr_get(desc_info);
110711ce2ba3SJiri Pirko 		if (wait->nowait) {
110811ce2ba3SJiri Pirko 			rocker_desc_gen_clear(desc_info);
110911ce2ba3SJiri Pirko 			rocker_wait_destroy(NULL, wait);
111011ce2ba3SJiri Pirko 		} else {
111111ce2ba3SJiri Pirko 			rocker_wait_wake_up(wait);
111211ce2ba3SJiri Pirko 		}
111311ce2ba3SJiri Pirko 		credits++;
111411ce2ba3SJiri Pirko 	}
111511ce2ba3SJiri Pirko 	spin_unlock(&rocker->cmd_ring_lock);
111611ce2ba3SJiri Pirko 	rocker_dma_ring_credits_set(rocker, &rocker->cmd_ring, credits);
111711ce2ba3SJiri Pirko 
111811ce2ba3SJiri Pirko 	return IRQ_HANDLED;
111911ce2ba3SJiri Pirko }
112011ce2ba3SJiri Pirko 
112111ce2ba3SJiri Pirko static void rocker_port_link_up(const struct rocker_port *rocker_port)
112211ce2ba3SJiri Pirko {
112311ce2ba3SJiri Pirko 	netif_carrier_on(rocker_port->dev);
112411ce2ba3SJiri Pirko 	netdev_info(rocker_port->dev, "Link is up\n");
112511ce2ba3SJiri Pirko }
112611ce2ba3SJiri Pirko 
112711ce2ba3SJiri Pirko static void rocker_port_link_down(const struct rocker_port *rocker_port)
112811ce2ba3SJiri Pirko {
112911ce2ba3SJiri Pirko 	netif_carrier_off(rocker_port->dev);
113011ce2ba3SJiri Pirko 	netdev_info(rocker_port->dev, "Link is down\n");
113111ce2ba3SJiri Pirko }
113211ce2ba3SJiri Pirko 
113311ce2ba3SJiri Pirko static int rocker_event_link_change(const struct rocker *rocker,
113411ce2ba3SJiri Pirko 				    const struct rocker_tlv *info)
113511ce2ba3SJiri Pirko {
113611ce2ba3SJiri Pirko 	const struct rocker_tlv *attrs[ROCKER_TLV_EVENT_LINK_CHANGED_MAX + 1];
113711ce2ba3SJiri Pirko 	unsigned int port_number;
113811ce2ba3SJiri Pirko 	bool link_up;
113911ce2ba3SJiri Pirko 	struct rocker_port *rocker_port;
114011ce2ba3SJiri Pirko 
114111ce2ba3SJiri Pirko 	rocker_tlv_parse_nested(attrs, ROCKER_TLV_EVENT_LINK_CHANGED_MAX, info);
114211ce2ba3SJiri Pirko 	if (!attrs[ROCKER_TLV_EVENT_LINK_CHANGED_PPORT] ||
114311ce2ba3SJiri Pirko 	    !attrs[ROCKER_TLV_EVENT_LINK_CHANGED_LINKUP])
114411ce2ba3SJiri Pirko 		return -EIO;
114511ce2ba3SJiri Pirko 	port_number =
114611ce2ba3SJiri Pirko 		rocker_tlv_get_u32(attrs[ROCKER_TLV_EVENT_LINK_CHANGED_PPORT]) - 1;
114711ce2ba3SJiri Pirko 	link_up = rocker_tlv_get_u8(attrs[ROCKER_TLV_EVENT_LINK_CHANGED_LINKUP]);
114811ce2ba3SJiri Pirko 
114911ce2ba3SJiri Pirko 	if (port_number >= rocker->port_count)
115011ce2ba3SJiri Pirko 		return -EINVAL;
115111ce2ba3SJiri Pirko 
115211ce2ba3SJiri Pirko 	rocker_port = rocker->ports[port_number];
115311ce2ba3SJiri Pirko 	if (netif_carrier_ok(rocker_port->dev) != link_up) {
115411ce2ba3SJiri Pirko 		if (link_up)
115511ce2ba3SJiri Pirko 			rocker_port_link_up(rocker_port);
115611ce2ba3SJiri Pirko 		else
115711ce2ba3SJiri Pirko 			rocker_port_link_down(rocker_port);
115811ce2ba3SJiri Pirko 	}
115911ce2ba3SJiri Pirko 
116011ce2ba3SJiri Pirko 	return 0;
116111ce2ba3SJiri Pirko }
116211ce2ba3SJiri Pirko 
116311ce2ba3SJiri Pirko static int rocker_port_fdb(struct rocker_port *rocker_port,
116411ce2ba3SJiri Pirko 			   struct switchdev_trans *trans,
116511ce2ba3SJiri Pirko 			   const unsigned char *addr,
116611ce2ba3SJiri Pirko 			   __be16 vlan_id, int flags);
1167e420114eSJiri Pirko static int rocker_world_port_ev_mac_vlan_seen(struct rocker_port *rocker_port,
1168e420114eSJiri Pirko 					      const unsigned char *addr,
1169e420114eSJiri Pirko 					      __be16 vlan_id);
117011ce2ba3SJiri Pirko 
117111ce2ba3SJiri Pirko static int rocker_event_mac_vlan_seen(const struct rocker *rocker,
117211ce2ba3SJiri Pirko 				      const struct rocker_tlv *info)
117311ce2ba3SJiri Pirko {
117411ce2ba3SJiri Pirko 	const struct rocker_tlv *attrs[ROCKER_TLV_EVENT_MAC_VLAN_MAX + 1];
117511ce2ba3SJiri Pirko 	unsigned int port_number;
117611ce2ba3SJiri Pirko 	struct rocker_port *rocker_port;
117711ce2ba3SJiri Pirko 	const unsigned char *addr;
117811ce2ba3SJiri Pirko 	int flags = ROCKER_OP_FLAG_NOWAIT | ROCKER_OP_FLAG_LEARNED;
117911ce2ba3SJiri Pirko 	__be16 vlan_id;
1180e420114eSJiri Pirko 	int err;
118111ce2ba3SJiri Pirko 
118211ce2ba3SJiri Pirko 	rocker_tlv_parse_nested(attrs, ROCKER_TLV_EVENT_MAC_VLAN_MAX, info);
118311ce2ba3SJiri Pirko 	if (!attrs[ROCKER_TLV_EVENT_MAC_VLAN_PPORT] ||
118411ce2ba3SJiri Pirko 	    !attrs[ROCKER_TLV_EVENT_MAC_VLAN_MAC] ||
118511ce2ba3SJiri Pirko 	    !attrs[ROCKER_TLV_EVENT_MAC_VLAN_VLAN_ID])
118611ce2ba3SJiri Pirko 		return -EIO;
118711ce2ba3SJiri Pirko 	port_number =
118811ce2ba3SJiri Pirko 		rocker_tlv_get_u32(attrs[ROCKER_TLV_EVENT_MAC_VLAN_PPORT]) - 1;
118911ce2ba3SJiri Pirko 	addr = rocker_tlv_data(attrs[ROCKER_TLV_EVENT_MAC_VLAN_MAC]);
119011ce2ba3SJiri Pirko 	vlan_id = rocker_tlv_get_be16(attrs[ROCKER_TLV_EVENT_MAC_VLAN_VLAN_ID]);
119111ce2ba3SJiri Pirko 
119211ce2ba3SJiri Pirko 	if (port_number >= rocker->port_count)
119311ce2ba3SJiri Pirko 		return -EINVAL;
119411ce2ba3SJiri Pirko 
119511ce2ba3SJiri Pirko 	rocker_port = rocker->ports[port_number];
119611ce2ba3SJiri Pirko 
1197e420114eSJiri Pirko 	err = rocker_world_port_ev_mac_vlan_seen(rocker_port, addr, vlan_id);
1198e420114eSJiri Pirko 	if (err)
1199e420114eSJiri Pirko 		return err;
1200e420114eSJiri Pirko 
120111ce2ba3SJiri Pirko 	if (rocker_port->stp_state != BR_STATE_LEARNING &&
120211ce2ba3SJiri Pirko 	    rocker_port->stp_state != BR_STATE_FORWARDING)
120311ce2ba3SJiri Pirko 		return 0;
120411ce2ba3SJiri Pirko 
120511ce2ba3SJiri Pirko 	return rocker_port_fdb(rocker_port, NULL, addr, vlan_id, flags);
120611ce2ba3SJiri Pirko }
120711ce2ba3SJiri Pirko 
120811ce2ba3SJiri Pirko static int rocker_event_process(const struct rocker *rocker,
120911ce2ba3SJiri Pirko 				const struct rocker_desc_info *desc_info)
121011ce2ba3SJiri Pirko {
121111ce2ba3SJiri Pirko 	const struct rocker_tlv *attrs[ROCKER_TLV_EVENT_MAX + 1];
121211ce2ba3SJiri Pirko 	const struct rocker_tlv *info;
121311ce2ba3SJiri Pirko 	u16 type;
121411ce2ba3SJiri Pirko 
121511ce2ba3SJiri Pirko 	rocker_tlv_parse_desc(attrs, ROCKER_TLV_EVENT_MAX, desc_info);
121611ce2ba3SJiri Pirko 	if (!attrs[ROCKER_TLV_EVENT_TYPE] ||
121711ce2ba3SJiri Pirko 	    !attrs[ROCKER_TLV_EVENT_INFO])
121811ce2ba3SJiri Pirko 		return -EIO;
121911ce2ba3SJiri Pirko 
122011ce2ba3SJiri Pirko 	type = rocker_tlv_get_u16(attrs[ROCKER_TLV_EVENT_TYPE]);
122111ce2ba3SJiri Pirko 	info = attrs[ROCKER_TLV_EVENT_INFO];
122211ce2ba3SJiri Pirko 
122311ce2ba3SJiri Pirko 	switch (type) {
122411ce2ba3SJiri Pirko 	case ROCKER_TLV_EVENT_TYPE_LINK_CHANGED:
122511ce2ba3SJiri Pirko 		return rocker_event_link_change(rocker, info);
122611ce2ba3SJiri Pirko 	case ROCKER_TLV_EVENT_TYPE_MAC_VLAN_SEEN:
122711ce2ba3SJiri Pirko 		return rocker_event_mac_vlan_seen(rocker, info);
122811ce2ba3SJiri Pirko 	}
122911ce2ba3SJiri Pirko 
123011ce2ba3SJiri Pirko 	return -EOPNOTSUPP;
123111ce2ba3SJiri Pirko }
123211ce2ba3SJiri Pirko 
123311ce2ba3SJiri Pirko static irqreturn_t rocker_event_irq_handler(int irq, void *dev_id)
123411ce2ba3SJiri Pirko {
123511ce2ba3SJiri Pirko 	struct rocker *rocker = dev_id;
123611ce2ba3SJiri Pirko 	const struct pci_dev *pdev = rocker->pdev;
123711ce2ba3SJiri Pirko 	const struct rocker_desc_info *desc_info;
123811ce2ba3SJiri Pirko 	u32 credits = 0;
123911ce2ba3SJiri Pirko 	int err;
124011ce2ba3SJiri Pirko 
124111ce2ba3SJiri Pirko 	while ((desc_info = rocker_desc_tail_get(&rocker->event_ring))) {
124211ce2ba3SJiri Pirko 		err = rocker_desc_err(desc_info);
124311ce2ba3SJiri Pirko 		if (err) {
124411ce2ba3SJiri Pirko 			dev_err(&pdev->dev, "event desc received with err %d\n",
124511ce2ba3SJiri Pirko 				err);
124611ce2ba3SJiri Pirko 		} else {
124711ce2ba3SJiri Pirko 			err = rocker_event_process(rocker, desc_info);
124811ce2ba3SJiri Pirko 			if (err)
124911ce2ba3SJiri Pirko 				dev_err(&pdev->dev, "event processing failed with err %d\n",
125011ce2ba3SJiri Pirko 					err);
125111ce2ba3SJiri Pirko 		}
125211ce2ba3SJiri Pirko 		rocker_desc_gen_clear(desc_info);
125311ce2ba3SJiri Pirko 		rocker_desc_head_set(rocker, &rocker->event_ring, desc_info);
125411ce2ba3SJiri Pirko 		credits++;
125511ce2ba3SJiri Pirko 	}
125611ce2ba3SJiri Pirko 	rocker_dma_ring_credits_set(rocker, &rocker->event_ring, credits);
125711ce2ba3SJiri Pirko 
125811ce2ba3SJiri Pirko 	return IRQ_HANDLED;
125911ce2ba3SJiri Pirko }
126011ce2ba3SJiri Pirko 
126111ce2ba3SJiri Pirko static irqreturn_t rocker_tx_irq_handler(int irq, void *dev_id)
126211ce2ba3SJiri Pirko {
126311ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = dev_id;
126411ce2ba3SJiri Pirko 
126511ce2ba3SJiri Pirko 	napi_schedule(&rocker_port->napi_tx);
126611ce2ba3SJiri Pirko 	return IRQ_HANDLED;
126711ce2ba3SJiri Pirko }
126811ce2ba3SJiri Pirko 
126911ce2ba3SJiri Pirko static irqreturn_t rocker_rx_irq_handler(int irq, void *dev_id)
127011ce2ba3SJiri Pirko {
127111ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = dev_id;
127211ce2ba3SJiri Pirko 
127311ce2ba3SJiri Pirko 	napi_schedule(&rocker_port->napi_rx);
127411ce2ba3SJiri Pirko 	return IRQ_HANDLED;
127511ce2ba3SJiri Pirko }
127611ce2ba3SJiri Pirko 
127711ce2ba3SJiri Pirko /********************
127811ce2ba3SJiri Pirko  * Command interface
127911ce2ba3SJiri Pirko  ********************/
128011ce2ba3SJiri Pirko 
128111ce2ba3SJiri Pirko typedef int (*rocker_cmd_prep_cb_t)(const struct rocker_port *rocker_port,
128211ce2ba3SJiri Pirko 				    struct rocker_desc_info *desc_info,
128311ce2ba3SJiri Pirko 				    void *priv);
128411ce2ba3SJiri Pirko 
128511ce2ba3SJiri Pirko typedef int (*rocker_cmd_proc_cb_t)(const struct rocker_port *rocker_port,
128611ce2ba3SJiri Pirko 				    const struct rocker_desc_info *desc_info,
128711ce2ba3SJiri Pirko 				    void *priv);
128811ce2ba3SJiri Pirko 
128911ce2ba3SJiri Pirko static int rocker_cmd_exec(struct rocker_port *rocker_port,
129011ce2ba3SJiri Pirko 			   struct switchdev_trans *trans, int flags,
129111ce2ba3SJiri Pirko 			   rocker_cmd_prep_cb_t prepare, void *prepare_priv,
129211ce2ba3SJiri Pirko 			   rocker_cmd_proc_cb_t process, void *process_priv)
129311ce2ba3SJiri Pirko {
129411ce2ba3SJiri Pirko 	struct rocker *rocker = rocker_port->rocker;
129511ce2ba3SJiri Pirko 	struct rocker_desc_info *desc_info;
129611ce2ba3SJiri Pirko 	struct rocker_wait *wait;
129711ce2ba3SJiri Pirko 	bool nowait = !!(flags & ROCKER_OP_FLAG_NOWAIT);
129811ce2ba3SJiri Pirko 	unsigned long lock_flags;
129911ce2ba3SJiri Pirko 	int err;
130011ce2ba3SJiri Pirko 
130111ce2ba3SJiri Pirko 	wait = rocker_wait_create(rocker_port, trans, flags);
130211ce2ba3SJiri Pirko 	if (!wait)
130311ce2ba3SJiri Pirko 		return -ENOMEM;
130411ce2ba3SJiri Pirko 	wait->nowait = nowait;
130511ce2ba3SJiri Pirko 
130611ce2ba3SJiri Pirko 	spin_lock_irqsave(&rocker->cmd_ring_lock, lock_flags);
130711ce2ba3SJiri Pirko 
130811ce2ba3SJiri Pirko 	desc_info = rocker_desc_head_get(&rocker->cmd_ring);
130911ce2ba3SJiri Pirko 	if (!desc_info) {
131011ce2ba3SJiri Pirko 		spin_unlock_irqrestore(&rocker->cmd_ring_lock, lock_flags);
131111ce2ba3SJiri Pirko 		err = -EAGAIN;
131211ce2ba3SJiri Pirko 		goto out;
131311ce2ba3SJiri Pirko 	}
131411ce2ba3SJiri Pirko 
131511ce2ba3SJiri Pirko 	err = prepare(rocker_port, desc_info, prepare_priv);
131611ce2ba3SJiri Pirko 	if (err) {
131711ce2ba3SJiri Pirko 		spin_unlock_irqrestore(&rocker->cmd_ring_lock, lock_flags);
131811ce2ba3SJiri Pirko 		goto out;
131911ce2ba3SJiri Pirko 	}
132011ce2ba3SJiri Pirko 
132111ce2ba3SJiri Pirko 	rocker_desc_cookie_ptr_set(desc_info, wait);
132211ce2ba3SJiri Pirko 
132311ce2ba3SJiri Pirko 	if (!switchdev_trans_ph_prepare(trans))
132411ce2ba3SJiri Pirko 		rocker_desc_head_set(rocker, &rocker->cmd_ring, desc_info);
132511ce2ba3SJiri Pirko 
132611ce2ba3SJiri Pirko 	spin_unlock_irqrestore(&rocker->cmd_ring_lock, lock_flags);
132711ce2ba3SJiri Pirko 
132811ce2ba3SJiri Pirko 	if (nowait)
132911ce2ba3SJiri Pirko 		return 0;
133011ce2ba3SJiri Pirko 
133111ce2ba3SJiri Pirko 	if (!switchdev_trans_ph_prepare(trans))
133211ce2ba3SJiri Pirko 		if (!rocker_wait_event_timeout(wait, HZ / 10))
133311ce2ba3SJiri Pirko 			return -EIO;
133411ce2ba3SJiri Pirko 
133511ce2ba3SJiri Pirko 	err = rocker_desc_err(desc_info);
133611ce2ba3SJiri Pirko 	if (err)
133711ce2ba3SJiri Pirko 		return err;
133811ce2ba3SJiri Pirko 
133911ce2ba3SJiri Pirko 	if (process)
134011ce2ba3SJiri Pirko 		err = process(rocker_port, desc_info, process_priv);
134111ce2ba3SJiri Pirko 
134211ce2ba3SJiri Pirko 	rocker_desc_gen_clear(desc_info);
134311ce2ba3SJiri Pirko out:
134411ce2ba3SJiri Pirko 	rocker_wait_destroy(trans, wait);
134511ce2ba3SJiri Pirko 	return err;
134611ce2ba3SJiri Pirko }
134711ce2ba3SJiri Pirko 
134811ce2ba3SJiri Pirko static int
134911ce2ba3SJiri Pirko rocker_cmd_get_port_settings_prep(const struct rocker_port *rocker_port,
135011ce2ba3SJiri Pirko 				  struct rocker_desc_info *desc_info,
135111ce2ba3SJiri Pirko 				  void *priv)
135211ce2ba3SJiri Pirko {
135311ce2ba3SJiri Pirko 	struct rocker_tlv *cmd_info;
135411ce2ba3SJiri Pirko 
135511ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE,
135611ce2ba3SJiri Pirko 			       ROCKER_TLV_CMD_TYPE_GET_PORT_SETTINGS))
135711ce2ba3SJiri Pirko 		return -EMSGSIZE;
135811ce2ba3SJiri Pirko 	cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO);
135911ce2ba3SJiri Pirko 	if (!cmd_info)
136011ce2ba3SJiri Pirko 		return -EMSGSIZE;
136111ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_PPORT,
136211ce2ba3SJiri Pirko 			       rocker_port->pport))
136311ce2ba3SJiri Pirko 		return -EMSGSIZE;
136411ce2ba3SJiri Pirko 	rocker_tlv_nest_end(desc_info, cmd_info);
136511ce2ba3SJiri Pirko 	return 0;
136611ce2ba3SJiri Pirko }
136711ce2ba3SJiri Pirko 
136811ce2ba3SJiri Pirko static int
136911ce2ba3SJiri Pirko rocker_cmd_get_port_settings_ethtool_proc(const struct rocker_port *rocker_port,
137011ce2ba3SJiri Pirko 					  const struct rocker_desc_info *desc_info,
137111ce2ba3SJiri Pirko 					  void *priv)
137211ce2ba3SJiri Pirko {
137311ce2ba3SJiri Pirko 	struct ethtool_cmd *ecmd = priv;
137411ce2ba3SJiri Pirko 	const struct rocker_tlv *attrs[ROCKER_TLV_CMD_MAX + 1];
137511ce2ba3SJiri Pirko 	const struct rocker_tlv *info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1];
137611ce2ba3SJiri Pirko 	u32 speed;
137711ce2ba3SJiri Pirko 	u8 duplex;
137811ce2ba3SJiri Pirko 	u8 autoneg;
137911ce2ba3SJiri Pirko 
138011ce2ba3SJiri Pirko 	rocker_tlv_parse_desc(attrs, ROCKER_TLV_CMD_MAX, desc_info);
138111ce2ba3SJiri Pirko 	if (!attrs[ROCKER_TLV_CMD_INFO])
138211ce2ba3SJiri Pirko 		return -EIO;
138311ce2ba3SJiri Pirko 
138411ce2ba3SJiri Pirko 	rocker_tlv_parse_nested(info_attrs, ROCKER_TLV_CMD_PORT_SETTINGS_MAX,
138511ce2ba3SJiri Pirko 				attrs[ROCKER_TLV_CMD_INFO]);
138611ce2ba3SJiri Pirko 	if (!info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_SPEED] ||
138711ce2ba3SJiri Pirko 	    !info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX] ||
138811ce2ba3SJiri Pirko 	    !info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG])
138911ce2ba3SJiri Pirko 		return -EIO;
139011ce2ba3SJiri Pirko 
139111ce2ba3SJiri Pirko 	speed = rocker_tlv_get_u32(info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_SPEED]);
139211ce2ba3SJiri Pirko 	duplex = rocker_tlv_get_u8(info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX]);
139311ce2ba3SJiri Pirko 	autoneg = rocker_tlv_get_u8(info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG]);
139411ce2ba3SJiri Pirko 
139511ce2ba3SJiri Pirko 	ecmd->transceiver = XCVR_INTERNAL;
139611ce2ba3SJiri Pirko 	ecmd->supported = SUPPORTED_TP;
139711ce2ba3SJiri Pirko 	ecmd->phy_address = 0xff;
139811ce2ba3SJiri Pirko 	ecmd->port = PORT_TP;
139911ce2ba3SJiri Pirko 	ethtool_cmd_speed_set(ecmd, speed);
140011ce2ba3SJiri Pirko 	ecmd->duplex = duplex ? DUPLEX_FULL : DUPLEX_HALF;
140111ce2ba3SJiri Pirko 	ecmd->autoneg = autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE;
140211ce2ba3SJiri Pirko 
140311ce2ba3SJiri Pirko 	return 0;
140411ce2ba3SJiri Pirko }
140511ce2ba3SJiri Pirko 
140611ce2ba3SJiri Pirko static int
140711ce2ba3SJiri Pirko rocker_cmd_get_port_settings_macaddr_proc(const struct rocker_port *rocker_port,
140811ce2ba3SJiri Pirko 					  const struct rocker_desc_info *desc_info,
140911ce2ba3SJiri Pirko 					  void *priv)
141011ce2ba3SJiri Pirko {
141111ce2ba3SJiri Pirko 	unsigned char *macaddr = priv;
141211ce2ba3SJiri Pirko 	const struct rocker_tlv *attrs[ROCKER_TLV_CMD_MAX + 1];
141311ce2ba3SJiri Pirko 	const struct rocker_tlv *info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1];
141411ce2ba3SJiri Pirko 	const struct rocker_tlv *attr;
141511ce2ba3SJiri Pirko 
141611ce2ba3SJiri Pirko 	rocker_tlv_parse_desc(attrs, ROCKER_TLV_CMD_MAX, desc_info);
141711ce2ba3SJiri Pirko 	if (!attrs[ROCKER_TLV_CMD_INFO])
141811ce2ba3SJiri Pirko 		return -EIO;
141911ce2ba3SJiri Pirko 
142011ce2ba3SJiri Pirko 	rocker_tlv_parse_nested(info_attrs, ROCKER_TLV_CMD_PORT_SETTINGS_MAX,
142111ce2ba3SJiri Pirko 				attrs[ROCKER_TLV_CMD_INFO]);
142211ce2ba3SJiri Pirko 	attr = info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR];
142311ce2ba3SJiri Pirko 	if (!attr)
142411ce2ba3SJiri Pirko 		return -EIO;
142511ce2ba3SJiri Pirko 
142611ce2ba3SJiri Pirko 	if (rocker_tlv_len(attr) != ETH_ALEN)
142711ce2ba3SJiri Pirko 		return -EINVAL;
142811ce2ba3SJiri Pirko 
142911ce2ba3SJiri Pirko 	ether_addr_copy(macaddr, rocker_tlv_data(attr));
143011ce2ba3SJiri Pirko 	return 0;
143111ce2ba3SJiri Pirko }
143211ce2ba3SJiri Pirko 
1433e1ba3deeSJiri Pirko static int
1434e1ba3deeSJiri Pirko rocker_cmd_get_port_settings_mode_proc(const struct rocker_port *rocker_port,
1435e1ba3deeSJiri Pirko 				       const struct rocker_desc_info *desc_info,
1436e1ba3deeSJiri Pirko 				       void *priv)
1437e1ba3deeSJiri Pirko {
1438e1ba3deeSJiri Pirko 	u8 *p_mode = priv;
1439e1ba3deeSJiri Pirko 	const struct rocker_tlv *attrs[ROCKER_TLV_CMD_MAX + 1];
1440e1ba3deeSJiri Pirko 	const struct rocker_tlv *info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1];
1441e1ba3deeSJiri Pirko 	const struct rocker_tlv *attr;
1442e1ba3deeSJiri Pirko 
1443e1ba3deeSJiri Pirko 	rocker_tlv_parse_desc(attrs, ROCKER_TLV_CMD_MAX, desc_info);
1444e1ba3deeSJiri Pirko 	if (!attrs[ROCKER_TLV_CMD_INFO])
1445e1ba3deeSJiri Pirko 		return -EIO;
1446e1ba3deeSJiri Pirko 
1447e1ba3deeSJiri Pirko 	rocker_tlv_parse_nested(info_attrs, ROCKER_TLV_CMD_PORT_SETTINGS_MAX,
1448e1ba3deeSJiri Pirko 				attrs[ROCKER_TLV_CMD_INFO]);
1449e1ba3deeSJiri Pirko 	attr = info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MODE];
1450e1ba3deeSJiri Pirko 	if (!attr)
1451e1ba3deeSJiri Pirko 		return -EIO;
1452e1ba3deeSJiri Pirko 
1453e1ba3deeSJiri Pirko 	*p_mode = rocker_tlv_get_u8(info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MODE]);
1454e1ba3deeSJiri Pirko 	return 0;
1455e1ba3deeSJiri Pirko }
1456e1ba3deeSJiri Pirko 
145711ce2ba3SJiri Pirko struct port_name {
145811ce2ba3SJiri Pirko 	char *buf;
145911ce2ba3SJiri Pirko 	size_t len;
146011ce2ba3SJiri Pirko };
146111ce2ba3SJiri Pirko 
146211ce2ba3SJiri Pirko static int
146311ce2ba3SJiri Pirko rocker_cmd_get_port_settings_phys_name_proc(const struct rocker_port *rocker_port,
146411ce2ba3SJiri Pirko 					    const struct rocker_desc_info *desc_info,
146511ce2ba3SJiri Pirko 					    void *priv)
146611ce2ba3SJiri Pirko {
146711ce2ba3SJiri Pirko 	const struct rocker_tlv *info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1];
146811ce2ba3SJiri Pirko 	const struct rocker_tlv *attrs[ROCKER_TLV_CMD_MAX + 1];
146911ce2ba3SJiri Pirko 	struct port_name *name = priv;
147011ce2ba3SJiri Pirko 	const struct rocker_tlv *attr;
147111ce2ba3SJiri Pirko 	size_t i, j, len;
147211ce2ba3SJiri Pirko 	const char *str;
147311ce2ba3SJiri Pirko 
147411ce2ba3SJiri Pirko 	rocker_tlv_parse_desc(attrs, ROCKER_TLV_CMD_MAX, desc_info);
147511ce2ba3SJiri Pirko 	if (!attrs[ROCKER_TLV_CMD_INFO])
147611ce2ba3SJiri Pirko 		return -EIO;
147711ce2ba3SJiri Pirko 
147811ce2ba3SJiri Pirko 	rocker_tlv_parse_nested(info_attrs, ROCKER_TLV_CMD_PORT_SETTINGS_MAX,
147911ce2ba3SJiri Pirko 				attrs[ROCKER_TLV_CMD_INFO]);
148011ce2ba3SJiri Pirko 	attr = info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_PHYS_NAME];
148111ce2ba3SJiri Pirko 	if (!attr)
148211ce2ba3SJiri Pirko 		return -EIO;
148311ce2ba3SJiri Pirko 
148411ce2ba3SJiri Pirko 	len = min_t(size_t, rocker_tlv_len(attr), name->len);
148511ce2ba3SJiri Pirko 	str = rocker_tlv_data(attr);
148611ce2ba3SJiri Pirko 
148711ce2ba3SJiri Pirko 	/* make sure name only contains alphanumeric characters */
148811ce2ba3SJiri Pirko 	for (i = j = 0; i < len; ++i) {
148911ce2ba3SJiri Pirko 		if (isalnum(str[i])) {
149011ce2ba3SJiri Pirko 			name->buf[j] = str[i];
149111ce2ba3SJiri Pirko 			j++;
149211ce2ba3SJiri Pirko 		}
149311ce2ba3SJiri Pirko 	}
149411ce2ba3SJiri Pirko 
149511ce2ba3SJiri Pirko 	if (j == 0)
149611ce2ba3SJiri Pirko 		return -EIO;
149711ce2ba3SJiri Pirko 
149811ce2ba3SJiri Pirko 	name->buf[j] = '\0';
149911ce2ba3SJiri Pirko 
150011ce2ba3SJiri Pirko 	return 0;
150111ce2ba3SJiri Pirko }
150211ce2ba3SJiri Pirko 
150311ce2ba3SJiri Pirko static int
150411ce2ba3SJiri Pirko rocker_cmd_set_port_settings_ethtool_prep(const struct rocker_port *rocker_port,
150511ce2ba3SJiri Pirko 					  struct rocker_desc_info *desc_info,
150611ce2ba3SJiri Pirko 					  void *priv)
150711ce2ba3SJiri Pirko {
150811ce2ba3SJiri Pirko 	struct ethtool_cmd *ecmd = priv;
150911ce2ba3SJiri Pirko 	struct rocker_tlv *cmd_info;
151011ce2ba3SJiri Pirko 
151111ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE,
151211ce2ba3SJiri Pirko 			       ROCKER_TLV_CMD_TYPE_SET_PORT_SETTINGS))
151311ce2ba3SJiri Pirko 		return -EMSGSIZE;
151411ce2ba3SJiri Pirko 	cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO);
151511ce2ba3SJiri Pirko 	if (!cmd_info)
151611ce2ba3SJiri Pirko 		return -EMSGSIZE;
151711ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_PPORT,
151811ce2ba3SJiri Pirko 			       rocker_port->pport))
151911ce2ba3SJiri Pirko 		return -EMSGSIZE;
152011ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_SPEED,
152111ce2ba3SJiri Pirko 			       ethtool_cmd_speed(ecmd)))
152211ce2ba3SJiri Pirko 		return -EMSGSIZE;
152311ce2ba3SJiri Pirko 	if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX,
152411ce2ba3SJiri Pirko 			      ecmd->duplex))
152511ce2ba3SJiri Pirko 		return -EMSGSIZE;
152611ce2ba3SJiri Pirko 	if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG,
152711ce2ba3SJiri Pirko 			      ecmd->autoneg))
152811ce2ba3SJiri Pirko 		return -EMSGSIZE;
152911ce2ba3SJiri Pirko 	rocker_tlv_nest_end(desc_info, cmd_info);
153011ce2ba3SJiri Pirko 	return 0;
153111ce2ba3SJiri Pirko }
153211ce2ba3SJiri Pirko 
153311ce2ba3SJiri Pirko static int
153411ce2ba3SJiri Pirko rocker_cmd_set_port_settings_macaddr_prep(const struct rocker_port *rocker_port,
153511ce2ba3SJiri Pirko 					  struct rocker_desc_info *desc_info,
153611ce2ba3SJiri Pirko 					  void *priv)
153711ce2ba3SJiri Pirko {
153811ce2ba3SJiri Pirko 	const unsigned char *macaddr = priv;
153911ce2ba3SJiri Pirko 	struct rocker_tlv *cmd_info;
154011ce2ba3SJiri Pirko 
154111ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE,
154211ce2ba3SJiri Pirko 			       ROCKER_TLV_CMD_TYPE_SET_PORT_SETTINGS))
154311ce2ba3SJiri Pirko 		return -EMSGSIZE;
154411ce2ba3SJiri Pirko 	cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO);
154511ce2ba3SJiri Pirko 	if (!cmd_info)
154611ce2ba3SJiri Pirko 		return -EMSGSIZE;
154711ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_PPORT,
154811ce2ba3SJiri Pirko 			       rocker_port->pport))
154911ce2ba3SJiri Pirko 		return -EMSGSIZE;
155011ce2ba3SJiri Pirko 	if (rocker_tlv_put(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR,
155111ce2ba3SJiri Pirko 			   ETH_ALEN, macaddr))
155211ce2ba3SJiri Pirko 		return -EMSGSIZE;
155311ce2ba3SJiri Pirko 	rocker_tlv_nest_end(desc_info, cmd_info);
155411ce2ba3SJiri Pirko 	return 0;
155511ce2ba3SJiri Pirko }
155611ce2ba3SJiri Pirko 
155711ce2ba3SJiri Pirko static int
155811ce2ba3SJiri Pirko rocker_cmd_set_port_settings_mtu_prep(const struct rocker_port *rocker_port,
155911ce2ba3SJiri Pirko 				      struct rocker_desc_info *desc_info,
156011ce2ba3SJiri Pirko 				      void *priv)
156111ce2ba3SJiri Pirko {
156211ce2ba3SJiri Pirko 	int mtu = *(int *)priv;
156311ce2ba3SJiri Pirko 	struct rocker_tlv *cmd_info;
156411ce2ba3SJiri Pirko 
156511ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE,
156611ce2ba3SJiri Pirko 			       ROCKER_TLV_CMD_TYPE_SET_PORT_SETTINGS))
156711ce2ba3SJiri Pirko 		return -EMSGSIZE;
156811ce2ba3SJiri Pirko 	cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO);
156911ce2ba3SJiri Pirko 	if (!cmd_info)
157011ce2ba3SJiri Pirko 		return -EMSGSIZE;
157111ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_PPORT,
157211ce2ba3SJiri Pirko 			       rocker_port->pport))
157311ce2ba3SJiri Pirko 		return -EMSGSIZE;
157411ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_MTU,
157511ce2ba3SJiri Pirko 			       mtu))
157611ce2ba3SJiri Pirko 		return -EMSGSIZE;
157711ce2ba3SJiri Pirko 	rocker_tlv_nest_end(desc_info, cmd_info);
157811ce2ba3SJiri Pirko 	return 0;
157911ce2ba3SJiri Pirko }
158011ce2ba3SJiri Pirko 
158111ce2ba3SJiri Pirko static int
158211ce2ba3SJiri Pirko rocker_cmd_set_port_learning_prep(const struct rocker_port *rocker_port,
158311ce2ba3SJiri Pirko 				  struct rocker_desc_info *desc_info,
158411ce2ba3SJiri Pirko 				  void *priv)
158511ce2ba3SJiri Pirko {
1586c1fe922eSJiri Pirko 	bool learning = *(bool *)priv;
158711ce2ba3SJiri Pirko 	struct rocker_tlv *cmd_info;
158811ce2ba3SJiri Pirko 
158911ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE,
159011ce2ba3SJiri Pirko 			       ROCKER_TLV_CMD_TYPE_SET_PORT_SETTINGS))
159111ce2ba3SJiri Pirko 		return -EMSGSIZE;
159211ce2ba3SJiri Pirko 	cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO);
159311ce2ba3SJiri Pirko 	if (!cmd_info)
159411ce2ba3SJiri Pirko 		return -EMSGSIZE;
159511ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_PPORT,
159611ce2ba3SJiri Pirko 			       rocker_port->pport))
159711ce2ba3SJiri Pirko 		return -EMSGSIZE;
159811ce2ba3SJiri Pirko 	if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING,
1599c1fe922eSJiri Pirko 			      learning))
160011ce2ba3SJiri Pirko 		return -EMSGSIZE;
160111ce2ba3SJiri Pirko 	rocker_tlv_nest_end(desc_info, cmd_info);
160211ce2ba3SJiri Pirko 	return 0;
160311ce2ba3SJiri Pirko }
160411ce2ba3SJiri Pirko 
160511ce2ba3SJiri Pirko static int rocker_cmd_get_port_settings_ethtool(struct rocker_port *rocker_port,
160611ce2ba3SJiri Pirko 						struct ethtool_cmd *ecmd)
160711ce2ba3SJiri Pirko {
160811ce2ba3SJiri Pirko 	return rocker_cmd_exec(rocker_port, NULL, 0,
160911ce2ba3SJiri Pirko 			       rocker_cmd_get_port_settings_prep, NULL,
161011ce2ba3SJiri Pirko 			       rocker_cmd_get_port_settings_ethtool_proc,
161111ce2ba3SJiri Pirko 			       ecmd);
161211ce2ba3SJiri Pirko }
161311ce2ba3SJiri Pirko 
161411ce2ba3SJiri Pirko static int rocker_cmd_get_port_settings_macaddr(struct rocker_port *rocker_port,
161511ce2ba3SJiri Pirko 						unsigned char *macaddr)
161611ce2ba3SJiri Pirko {
161711ce2ba3SJiri Pirko 	return rocker_cmd_exec(rocker_port, NULL, 0,
161811ce2ba3SJiri Pirko 			       rocker_cmd_get_port_settings_prep, NULL,
161911ce2ba3SJiri Pirko 			       rocker_cmd_get_port_settings_macaddr_proc,
162011ce2ba3SJiri Pirko 			       macaddr);
162111ce2ba3SJiri Pirko }
162211ce2ba3SJiri Pirko 
1623e1ba3deeSJiri Pirko static int rocker_cmd_get_port_settings_mode(struct rocker_port *rocker_port,
1624e1ba3deeSJiri Pirko 					     u8 *p_mode)
1625e1ba3deeSJiri Pirko {
1626e1ba3deeSJiri Pirko 	return rocker_cmd_exec(rocker_port, NULL, 0,
1627e1ba3deeSJiri Pirko 			       rocker_cmd_get_port_settings_prep, NULL,
1628e1ba3deeSJiri Pirko 			       rocker_cmd_get_port_settings_mode_proc, p_mode);
1629e1ba3deeSJiri Pirko }
1630e1ba3deeSJiri Pirko 
163111ce2ba3SJiri Pirko static int rocker_cmd_set_port_settings_ethtool(struct rocker_port *rocker_port,
163211ce2ba3SJiri Pirko 						struct ethtool_cmd *ecmd)
163311ce2ba3SJiri Pirko {
163411ce2ba3SJiri Pirko 	return rocker_cmd_exec(rocker_port, NULL, 0,
163511ce2ba3SJiri Pirko 			       rocker_cmd_set_port_settings_ethtool_prep,
163611ce2ba3SJiri Pirko 			       ecmd, NULL, NULL);
163711ce2ba3SJiri Pirko }
163811ce2ba3SJiri Pirko 
163911ce2ba3SJiri Pirko static int rocker_cmd_set_port_settings_macaddr(struct rocker_port *rocker_port,
164011ce2ba3SJiri Pirko 						unsigned char *macaddr)
164111ce2ba3SJiri Pirko {
164211ce2ba3SJiri Pirko 	return rocker_cmd_exec(rocker_port, NULL, 0,
164311ce2ba3SJiri Pirko 			       rocker_cmd_set_port_settings_macaddr_prep,
164411ce2ba3SJiri Pirko 			       macaddr, NULL, NULL);
164511ce2ba3SJiri Pirko }
164611ce2ba3SJiri Pirko 
164711ce2ba3SJiri Pirko static int rocker_cmd_set_port_settings_mtu(struct rocker_port *rocker_port,
164811ce2ba3SJiri Pirko 					    int mtu)
164911ce2ba3SJiri Pirko {
165011ce2ba3SJiri Pirko 	return rocker_cmd_exec(rocker_port, NULL, 0,
165111ce2ba3SJiri Pirko 			       rocker_cmd_set_port_settings_mtu_prep,
165211ce2ba3SJiri Pirko 			       &mtu, NULL, NULL);
165311ce2ba3SJiri Pirko }
165411ce2ba3SJiri Pirko 
165511ce2ba3SJiri Pirko static int rocker_port_set_learning(struct rocker_port *rocker_port,
1656c1fe922eSJiri Pirko 				    struct switchdev_trans *trans,
1657c1fe922eSJiri Pirko 				    bool learning)
165811ce2ba3SJiri Pirko {
165911ce2ba3SJiri Pirko 	return rocker_cmd_exec(rocker_port, trans, 0,
166011ce2ba3SJiri Pirko 			       rocker_cmd_set_port_learning_prep,
1661c1fe922eSJiri Pirko 			       &learning, NULL, NULL);
166211ce2ba3SJiri Pirko }
166311ce2ba3SJiri Pirko 
1664e420114eSJiri Pirko /**********************
1665e420114eSJiri Pirko  * Worlds manipulation
1666e420114eSJiri Pirko  **********************/
1667e420114eSJiri Pirko 
1668e420114eSJiri Pirko static struct rocker_world_ops *rocker_world_ops[] = {
1669e420114eSJiri Pirko 	&rocker_ofdpa_ops,
1670e420114eSJiri Pirko };
1671e420114eSJiri Pirko 
1672e420114eSJiri Pirko #define ROCKER_WORLD_OPS_LEN ARRAY_SIZE(rocker_world_ops)
1673e420114eSJiri Pirko 
1674e420114eSJiri Pirko static struct rocker_world_ops *rocker_world_ops_find(u8 mode)
1675e420114eSJiri Pirko {
1676e420114eSJiri Pirko 	int i;
1677e420114eSJiri Pirko 
1678e420114eSJiri Pirko 	for (i = 0; i < ROCKER_WORLD_OPS_LEN; i++)
1679e420114eSJiri Pirko 		if (rocker_world_ops[i]->mode == mode)
1680e420114eSJiri Pirko 			return rocker_world_ops[i];
1681e420114eSJiri Pirko 	return NULL;
1682e420114eSJiri Pirko }
1683e420114eSJiri Pirko 
1684e420114eSJiri Pirko static int rocker_world_init(struct rocker *rocker, u8 mode)
1685e420114eSJiri Pirko {
1686e420114eSJiri Pirko 	struct rocker_world_ops *wops;
1687e420114eSJiri Pirko 	int err;
1688e420114eSJiri Pirko 
1689e420114eSJiri Pirko 	wops = rocker_world_ops_find(mode);
1690e420114eSJiri Pirko 	if (!wops) {
1691e420114eSJiri Pirko 		dev_err(&rocker->pdev->dev, "port mode \"%d\" is not supported\n",
1692e420114eSJiri Pirko 			mode);
1693e420114eSJiri Pirko 		return -EINVAL;
1694e420114eSJiri Pirko 	}
1695e420114eSJiri Pirko 	rocker->wops = wops;
1696e420114eSJiri Pirko 	rocker->wpriv = kzalloc(wops->priv_size, GFP_KERNEL);
1697e420114eSJiri Pirko 	if (!rocker->wpriv)
1698e420114eSJiri Pirko 		return -ENOMEM;
1699e420114eSJiri Pirko 	if (!wops->init)
1700e420114eSJiri Pirko 		return 0;
1701e420114eSJiri Pirko 	err = wops->init(rocker);
1702e420114eSJiri Pirko 	if (err)
1703e420114eSJiri Pirko 		kfree(rocker->wpriv);
1704e420114eSJiri Pirko 	return err;
1705e420114eSJiri Pirko }
1706e420114eSJiri Pirko 
1707e420114eSJiri Pirko static void rocker_world_fini(struct rocker *rocker)
1708e420114eSJiri Pirko {
1709e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker->wops;
1710e420114eSJiri Pirko 
1711e420114eSJiri Pirko 	if (!wops || !wops->fini)
1712e420114eSJiri Pirko 		return;
1713e420114eSJiri Pirko 	wops->fini(rocker);
1714e420114eSJiri Pirko 	kfree(rocker->wpriv);
1715e420114eSJiri Pirko }
1716e420114eSJiri Pirko 
1717e420114eSJiri Pirko static int rocker_world_check_init(struct rocker_port *rocker_port)
1718e420114eSJiri Pirko {
1719e420114eSJiri Pirko 	struct rocker *rocker = rocker_port->rocker;
1720e420114eSJiri Pirko 	u8 mode;
1721e420114eSJiri Pirko 	int err;
1722e420114eSJiri Pirko 
1723e420114eSJiri Pirko 	err = rocker_cmd_get_port_settings_mode(rocker_port, &mode);
1724e420114eSJiri Pirko 	if (err) {
1725e420114eSJiri Pirko 		dev_err(&rocker->pdev->dev, "failed to get port mode\n");
1726e420114eSJiri Pirko 		return err;
1727e420114eSJiri Pirko 	}
1728e420114eSJiri Pirko 	if (rocker->wops) {
1729e420114eSJiri Pirko 		if (rocker->wops->mode != mode) {
1730e420114eSJiri Pirko 			dev_err(&rocker->pdev->dev, "hardware has ports in different worlds, which is not supported\n");
1731e420114eSJiri Pirko 			return err;
1732e420114eSJiri Pirko 		}
1733e420114eSJiri Pirko 		return 0;
1734e420114eSJiri Pirko 	}
1735e420114eSJiri Pirko 	return rocker_world_init(rocker, mode);
1736e420114eSJiri Pirko }
1737e420114eSJiri Pirko 
1738e420114eSJiri Pirko static int rocker_world_port_pre_init(struct rocker_port *rocker_port)
1739e420114eSJiri Pirko {
1740e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1741e420114eSJiri Pirko 	int err;
1742e420114eSJiri Pirko 
1743e420114eSJiri Pirko 	rocker_port->wpriv = kzalloc(wops->port_priv_size, GFP_KERNEL);
1744e420114eSJiri Pirko 	if (!rocker_port->wpriv)
1745e420114eSJiri Pirko 		return -ENOMEM;
1746e420114eSJiri Pirko 	if (!wops->port_pre_init)
1747e420114eSJiri Pirko 		return 0;
1748e420114eSJiri Pirko 	err = wops->port_pre_init(rocker_port);
1749e420114eSJiri Pirko 	if (err)
1750e420114eSJiri Pirko 		kfree(rocker_port->wpriv);
1751e420114eSJiri Pirko 	return 0;
1752e420114eSJiri Pirko }
1753e420114eSJiri Pirko 
1754e420114eSJiri Pirko static int rocker_world_port_init(struct rocker_port *rocker_port)
1755e420114eSJiri Pirko {
1756e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1757e420114eSJiri Pirko 
1758e420114eSJiri Pirko 	if (!wops->port_init)
1759e420114eSJiri Pirko 		return 0;
1760e420114eSJiri Pirko 	return wops->port_init(rocker_port);
1761e420114eSJiri Pirko }
1762e420114eSJiri Pirko 
1763e420114eSJiri Pirko static void rocker_world_port_fini(struct rocker_port *rocker_port)
1764e420114eSJiri Pirko {
1765e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1766e420114eSJiri Pirko 
1767e420114eSJiri Pirko 	if (!wops->port_fini)
1768e420114eSJiri Pirko 		return;
1769e420114eSJiri Pirko 	wops->port_fini(rocker_port);
1770e420114eSJiri Pirko }
1771e420114eSJiri Pirko 
1772e420114eSJiri Pirko static void rocker_world_port_post_fini(struct rocker_port *rocker_port)
1773e420114eSJiri Pirko {
1774e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1775e420114eSJiri Pirko 
1776e420114eSJiri Pirko 	if (!wops->port_post_fini)
1777e420114eSJiri Pirko 		return;
1778e420114eSJiri Pirko 	wops->port_post_fini(rocker_port);
1779e420114eSJiri Pirko 	kfree(rocker_port->wpriv);
1780e420114eSJiri Pirko }
1781e420114eSJiri Pirko 
1782e420114eSJiri Pirko static int rocker_world_port_open(struct rocker_port *rocker_port)
1783e420114eSJiri Pirko {
1784e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1785e420114eSJiri Pirko 
1786e420114eSJiri Pirko 	if (!wops->port_open)
1787e420114eSJiri Pirko 		return 0;
1788e420114eSJiri Pirko 	return wops->port_open(rocker_port);
1789e420114eSJiri Pirko }
1790e420114eSJiri Pirko 
1791e420114eSJiri Pirko static void rocker_world_port_stop(struct rocker_port *rocker_port)
1792e420114eSJiri Pirko {
1793e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1794e420114eSJiri Pirko 
1795e420114eSJiri Pirko 	if (!wops->port_stop)
1796e420114eSJiri Pirko 		return;
1797e420114eSJiri Pirko 	wops->port_stop(rocker_port);
1798e420114eSJiri Pirko }
1799e420114eSJiri Pirko 
1800e420114eSJiri Pirko static int rocker_world_port_attr_stp_state_set(struct rocker_port *rocker_port,
1801e420114eSJiri Pirko 						u8 state,
1802e420114eSJiri Pirko 						struct switchdev_trans *trans)
1803e420114eSJiri Pirko {
1804e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1805e420114eSJiri Pirko 
1806e420114eSJiri Pirko 	if (!wops->port_attr_stp_state_set)
1807e420114eSJiri Pirko 		return 0;
1808e420114eSJiri Pirko 	return wops->port_attr_stp_state_set(rocker_port, state, trans);
1809e420114eSJiri Pirko }
1810e420114eSJiri Pirko 
1811e420114eSJiri Pirko static int
1812e420114eSJiri Pirko rocker_world_port_attr_bridge_flags_set(struct rocker_port *rocker_port,
1813e420114eSJiri Pirko 					unsigned long brport_flags,
1814e420114eSJiri Pirko 					struct switchdev_trans *trans)
1815e420114eSJiri Pirko {
1816e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1817e420114eSJiri Pirko 
1818e420114eSJiri Pirko 	if (!wops->port_attr_bridge_flags_set)
1819e420114eSJiri Pirko 		return 0;
1820e420114eSJiri Pirko 	return wops->port_attr_bridge_flags_set(rocker_port, brport_flags,
1821e420114eSJiri Pirko 						trans);
1822e420114eSJiri Pirko }
1823e420114eSJiri Pirko 
1824e420114eSJiri Pirko static int
1825e420114eSJiri Pirko rocker_world_port_attr_bridge_flags_get(const struct rocker_port *rocker_port,
1826e420114eSJiri Pirko 					unsigned long *p_brport_flags)
1827e420114eSJiri Pirko {
1828e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1829e420114eSJiri Pirko 
1830e420114eSJiri Pirko 	if (!wops->port_attr_bridge_flags_get)
1831e420114eSJiri Pirko 		return 0;
1832e420114eSJiri Pirko 	return wops->port_attr_bridge_flags_get(rocker_port, p_brport_flags);
1833e420114eSJiri Pirko }
1834e420114eSJiri Pirko 
1835e420114eSJiri Pirko static int
1836e420114eSJiri Pirko rocker_world_port_attr_bridge_ageing_time_set(struct rocker_port *rocker_port,
1837e420114eSJiri Pirko 					      u32 ageing_time,
1838e420114eSJiri Pirko 					      struct switchdev_trans *trans)
1839e420114eSJiri Pirko 
1840e420114eSJiri Pirko {
1841e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1842e420114eSJiri Pirko 
1843e420114eSJiri Pirko 	if (!wops->port_attr_bridge_ageing_time_set)
1844e420114eSJiri Pirko 		return 0;
1845e420114eSJiri Pirko 	return wops->port_attr_bridge_ageing_time_set(rocker_port, ageing_time,
1846e420114eSJiri Pirko 						      trans);
1847e420114eSJiri Pirko }
1848e420114eSJiri Pirko 
1849e420114eSJiri Pirko static int
1850e420114eSJiri Pirko rocker_world_port_obj_vlan_add(struct rocker_port *rocker_port,
1851e420114eSJiri Pirko 			       const struct switchdev_obj_port_vlan *vlan,
1852e420114eSJiri Pirko 			       struct switchdev_trans *trans)
1853e420114eSJiri Pirko {
1854e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1855e420114eSJiri Pirko 
1856e420114eSJiri Pirko 	if (!wops->port_obj_vlan_add)
1857e420114eSJiri Pirko 		return 0;
1858e420114eSJiri Pirko 	return wops->port_obj_vlan_add(rocker_port, vlan, trans);
1859e420114eSJiri Pirko }
1860e420114eSJiri Pirko 
1861e420114eSJiri Pirko static int
1862e420114eSJiri Pirko rocker_world_port_obj_vlan_del(struct rocker_port *rocker_port,
1863e420114eSJiri Pirko 			       const struct switchdev_obj_port_vlan *vlan)
1864e420114eSJiri Pirko {
1865e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1866e420114eSJiri Pirko 
1867e420114eSJiri Pirko 	if (!wops->port_obj_vlan_del)
1868e420114eSJiri Pirko 		return 0;
1869e420114eSJiri Pirko 	return wops->port_obj_vlan_del(rocker_port, vlan);
1870e420114eSJiri Pirko }
1871e420114eSJiri Pirko 
1872e420114eSJiri Pirko static int
1873e420114eSJiri Pirko rocker_world_port_obj_vlan_dump(const struct rocker_port *rocker_port,
1874e420114eSJiri Pirko 				struct switchdev_obj_port_vlan *vlan,
1875e420114eSJiri Pirko 				switchdev_obj_dump_cb_t *cb)
1876e420114eSJiri Pirko {
1877e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1878e420114eSJiri Pirko 
1879e420114eSJiri Pirko 	if (!wops->port_obj_vlan_dump)
1880e420114eSJiri Pirko 		return 0;
1881e420114eSJiri Pirko 	return wops->port_obj_vlan_dump(rocker_port, vlan, cb);
1882e420114eSJiri Pirko }
1883e420114eSJiri Pirko 
1884e420114eSJiri Pirko static int
1885e420114eSJiri Pirko rocker_world_port_obj_fib4_add(struct rocker_port *rocker_port,
1886e420114eSJiri Pirko 			       const struct switchdev_obj_ipv4_fib *fib4,
1887e420114eSJiri Pirko 			       struct switchdev_trans *trans)
1888e420114eSJiri Pirko {
1889e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1890e420114eSJiri Pirko 
1891e420114eSJiri Pirko 	if (!wops->port_obj_fib4_add)
1892e420114eSJiri Pirko 		return 0;
1893e420114eSJiri Pirko 	return wops->port_obj_fib4_add(rocker_port, fib4, trans);
1894e420114eSJiri Pirko }
1895e420114eSJiri Pirko 
1896e420114eSJiri Pirko static int
1897e420114eSJiri Pirko rocker_world_port_obj_fib4_del(struct rocker_port *rocker_port,
1898e420114eSJiri Pirko 			       const struct switchdev_obj_ipv4_fib *fib4)
1899e420114eSJiri Pirko {
1900e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1901e420114eSJiri Pirko 
1902e420114eSJiri Pirko 	if (!wops->port_obj_fib4_del)
1903e420114eSJiri Pirko 		return 0;
1904e420114eSJiri Pirko 	return wops->port_obj_fib4_del(rocker_port, fib4);
1905e420114eSJiri Pirko }
1906e420114eSJiri Pirko 
1907e420114eSJiri Pirko static int
1908e420114eSJiri Pirko rocker_world_port_obj_fdb_add(struct rocker_port *rocker_port,
1909e420114eSJiri Pirko 			      const struct switchdev_obj_port_fdb *fdb,
1910e420114eSJiri Pirko 			      struct switchdev_trans *trans)
1911e420114eSJiri Pirko {
1912e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1913e420114eSJiri Pirko 
1914e420114eSJiri Pirko 	if (!wops->port_obj_fdb_add)
1915e420114eSJiri Pirko 		return 0;
1916e420114eSJiri Pirko 	return wops->port_obj_fdb_add(rocker_port, fdb, trans);
1917e420114eSJiri Pirko }
1918e420114eSJiri Pirko 
1919e420114eSJiri Pirko static int
1920e420114eSJiri Pirko rocker_world_port_obj_fdb_del(struct rocker_port *rocker_port,
1921e420114eSJiri Pirko 			      const struct switchdev_obj_port_fdb *fdb)
1922e420114eSJiri Pirko {
1923e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1924e420114eSJiri Pirko 
1925e420114eSJiri Pirko 	if (!wops->port_obj_fdb_del)
1926e420114eSJiri Pirko 		return 0;
1927e420114eSJiri Pirko 	return wops->port_obj_fdb_del(rocker_port, fdb);
1928e420114eSJiri Pirko }
1929e420114eSJiri Pirko 
1930e420114eSJiri Pirko static int
1931e420114eSJiri Pirko rocker_world_port_obj_fdb_dump(const struct rocker_port *rocker_port,
1932e420114eSJiri Pirko 			       struct switchdev_obj_port_fdb *fdb,
1933e420114eSJiri Pirko 			       switchdev_obj_dump_cb_t *cb)
1934e420114eSJiri Pirko {
1935e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1936e420114eSJiri Pirko 
1937e420114eSJiri Pirko 	if (!wops->port_obj_fdb_dump)
1938e420114eSJiri Pirko 		return 0;
1939e420114eSJiri Pirko 	return wops->port_obj_fdb_dump(rocker_port, fdb, cb);
1940e420114eSJiri Pirko }
1941e420114eSJiri Pirko 
1942e420114eSJiri Pirko static int rocker_world_port_master_linked(struct rocker_port *rocker_port,
1943e420114eSJiri Pirko 					   struct net_device *master)
1944e420114eSJiri Pirko {
1945e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1946e420114eSJiri Pirko 
1947e420114eSJiri Pirko 	if (!wops->port_master_linked)
1948e420114eSJiri Pirko 		return 0;
1949e420114eSJiri Pirko 	return wops->port_master_linked(rocker_port, master);
1950e420114eSJiri Pirko }
1951e420114eSJiri Pirko 
1952e420114eSJiri Pirko static int rocker_world_port_master_unlinked(struct rocker_port *rocker_port,
1953e420114eSJiri Pirko 					     struct net_device *master)
1954e420114eSJiri Pirko {
1955e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1956e420114eSJiri Pirko 
1957e420114eSJiri Pirko 	if (!wops->port_master_unlinked)
1958e420114eSJiri Pirko 		return 0;
1959e420114eSJiri Pirko 	return wops->port_master_unlinked(rocker_port, master);
1960e420114eSJiri Pirko }
1961e420114eSJiri Pirko 
1962e420114eSJiri Pirko static int rocker_world_port_neigh_update(struct rocker_port *rocker_port,
1963e420114eSJiri Pirko 					  struct neighbour *n)
1964e420114eSJiri Pirko {
1965e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1966e420114eSJiri Pirko 
1967e420114eSJiri Pirko 	if (!wops->port_neigh_update)
1968e420114eSJiri Pirko 		return 0;
1969e420114eSJiri Pirko 	return wops->port_neigh_update(rocker_port, n);
1970e420114eSJiri Pirko }
1971e420114eSJiri Pirko 
1972e420114eSJiri Pirko static int rocker_world_port_neigh_destroy(struct rocker_port *rocker_port,
1973e420114eSJiri Pirko 					   struct neighbour *n)
1974e420114eSJiri Pirko {
1975e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1976e420114eSJiri Pirko 
1977e420114eSJiri Pirko 	if (!wops->port_neigh_destroy)
1978e420114eSJiri Pirko 		return 0;
1979e420114eSJiri Pirko 	return wops->port_neigh_destroy(rocker_port, n);
1980e420114eSJiri Pirko }
1981e420114eSJiri Pirko 
1982e420114eSJiri Pirko static int rocker_world_port_ev_mac_vlan_seen(struct rocker_port *rocker_port,
1983e420114eSJiri Pirko 					      const unsigned char *addr,
1984e420114eSJiri Pirko 					      __be16 vlan_id)
1985e420114eSJiri Pirko {
1986e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1987e420114eSJiri Pirko 
1988e420114eSJiri Pirko 	if (!wops->port_ev_mac_vlan_seen)
1989e420114eSJiri Pirko 		return 0;
1990e420114eSJiri Pirko 	return wops->port_ev_mac_vlan_seen(rocker_port, addr, vlan_id);
1991e420114eSJiri Pirko }
1992e420114eSJiri Pirko 
199311ce2ba3SJiri Pirko static int
199411ce2ba3SJiri Pirko rocker_cmd_flow_tbl_add_ig_port(struct rocker_desc_info *desc_info,
199511ce2ba3SJiri Pirko 				const struct rocker_flow_tbl_entry *entry)
199611ce2ba3SJiri Pirko {
199711ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT,
199811ce2ba3SJiri Pirko 			       entry->key.ig_port.in_pport))
199911ce2ba3SJiri Pirko 		return -EMSGSIZE;
200011ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT_MASK,
200111ce2ba3SJiri Pirko 			       entry->key.ig_port.in_pport_mask))
200211ce2ba3SJiri Pirko 		return -EMSGSIZE;
200311ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_GOTO_TABLE_ID,
200411ce2ba3SJiri Pirko 			       entry->key.ig_port.goto_tbl))
200511ce2ba3SJiri Pirko 		return -EMSGSIZE;
200611ce2ba3SJiri Pirko 
200711ce2ba3SJiri Pirko 	return 0;
200811ce2ba3SJiri Pirko }
200911ce2ba3SJiri Pirko 
201011ce2ba3SJiri Pirko static int
201111ce2ba3SJiri Pirko rocker_cmd_flow_tbl_add_vlan(struct rocker_desc_info *desc_info,
201211ce2ba3SJiri Pirko 			     const struct rocker_flow_tbl_entry *entry)
201311ce2ba3SJiri Pirko {
201411ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT,
201511ce2ba3SJiri Pirko 			       entry->key.vlan.in_pport))
201611ce2ba3SJiri Pirko 		return -EMSGSIZE;
201711ce2ba3SJiri Pirko 	if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID,
201811ce2ba3SJiri Pirko 				entry->key.vlan.vlan_id))
201911ce2ba3SJiri Pirko 		return -EMSGSIZE;
202011ce2ba3SJiri Pirko 	if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID_MASK,
202111ce2ba3SJiri Pirko 				entry->key.vlan.vlan_id_mask))
202211ce2ba3SJiri Pirko 		return -EMSGSIZE;
202311ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_GOTO_TABLE_ID,
202411ce2ba3SJiri Pirko 			       entry->key.vlan.goto_tbl))
202511ce2ba3SJiri Pirko 		return -EMSGSIZE;
202611ce2ba3SJiri Pirko 	if (entry->key.vlan.untagged &&
202711ce2ba3SJiri Pirko 	    rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_NEW_VLAN_ID,
202811ce2ba3SJiri Pirko 				entry->key.vlan.new_vlan_id))
202911ce2ba3SJiri Pirko 		return -EMSGSIZE;
203011ce2ba3SJiri Pirko 
203111ce2ba3SJiri Pirko 	return 0;
203211ce2ba3SJiri Pirko }
203311ce2ba3SJiri Pirko 
203411ce2ba3SJiri Pirko static int
203511ce2ba3SJiri Pirko rocker_cmd_flow_tbl_add_term_mac(struct rocker_desc_info *desc_info,
203611ce2ba3SJiri Pirko 				 const struct rocker_flow_tbl_entry *entry)
203711ce2ba3SJiri Pirko {
203811ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT,
203911ce2ba3SJiri Pirko 			       entry->key.term_mac.in_pport))
204011ce2ba3SJiri Pirko 		return -EMSGSIZE;
204111ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT_MASK,
204211ce2ba3SJiri Pirko 			       entry->key.term_mac.in_pport_mask))
204311ce2ba3SJiri Pirko 		return -EMSGSIZE;
204411ce2ba3SJiri Pirko 	if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_ETHERTYPE,
204511ce2ba3SJiri Pirko 				entry->key.term_mac.eth_type))
204611ce2ba3SJiri Pirko 		return -EMSGSIZE;
204711ce2ba3SJiri Pirko 	if (rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC,
204811ce2ba3SJiri Pirko 			   ETH_ALEN, entry->key.term_mac.eth_dst))
204911ce2ba3SJiri Pirko 		return -EMSGSIZE;
205011ce2ba3SJiri Pirko 	if (rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC_MASK,
205111ce2ba3SJiri Pirko 			   ETH_ALEN, entry->key.term_mac.eth_dst_mask))
205211ce2ba3SJiri Pirko 		return -EMSGSIZE;
205311ce2ba3SJiri Pirko 	if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID,
205411ce2ba3SJiri Pirko 				entry->key.term_mac.vlan_id))
205511ce2ba3SJiri Pirko 		return -EMSGSIZE;
205611ce2ba3SJiri Pirko 	if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID_MASK,
205711ce2ba3SJiri Pirko 				entry->key.term_mac.vlan_id_mask))
205811ce2ba3SJiri Pirko 		return -EMSGSIZE;
205911ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_GOTO_TABLE_ID,
206011ce2ba3SJiri Pirko 			       entry->key.term_mac.goto_tbl))
206111ce2ba3SJiri Pirko 		return -EMSGSIZE;
206211ce2ba3SJiri Pirko 	if (entry->key.term_mac.copy_to_cpu &&
206311ce2ba3SJiri Pirko 	    rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_COPY_CPU_ACTION,
206411ce2ba3SJiri Pirko 			      entry->key.term_mac.copy_to_cpu))
206511ce2ba3SJiri Pirko 		return -EMSGSIZE;
206611ce2ba3SJiri Pirko 
206711ce2ba3SJiri Pirko 	return 0;
206811ce2ba3SJiri Pirko }
206911ce2ba3SJiri Pirko 
207011ce2ba3SJiri Pirko static int
207111ce2ba3SJiri Pirko rocker_cmd_flow_tbl_add_ucast_routing(struct rocker_desc_info *desc_info,
207211ce2ba3SJiri Pirko 				      const struct rocker_flow_tbl_entry *entry)
207311ce2ba3SJiri Pirko {
207411ce2ba3SJiri Pirko 	if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_ETHERTYPE,
207511ce2ba3SJiri Pirko 				entry->key.ucast_routing.eth_type))
207611ce2ba3SJiri Pirko 		return -EMSGSIZE;
207711ce2ba3SJiri Pirko 	if (rocker_tlv_put_be32(desc_info, ROCKER_TLV_OF_DPA_DST_IP,
207811ce2ba3SJiri Pirko 				entry->key.ucast_routing.dst4))
207911ce2ba3SJiri Pirko 		return -EMSGSIZE;
208011ce2ba3SJiri Pirko 	if (rocker_tlv_put_be32(desc_info, ROCKER_TLV_OF_DPA_DST_IP_MASK,
208111ce2ba3SJiri Pirko 				entry->key.ucast_routing.dst4_mask))
208211ce2ba3SJiri Pirko 		return -EMSGSIZE;
208311ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_GOTO_TABLE_ID,
208411ce2ba3SJiri Pirko 			       entry->key.ucast_routing.goto_tbl))
208511ce2ba3SJiri Pirko 		return -EMSGSIZE;
208611ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID,
208711ce2ba3SJiri Pirko 			       entry->key.ucast_routing.group_id))
208811ce2ba3SJiri Pirko 		return -EMSGSIZE;
208911ce2ba3SJiri Pirko 
209011ce2ba3SJiri Pirko 	return 0;
209111ce2ba3SJiri Pirko }
209211ce2ba3SJiri Pirko 
209311ce2ba3SJiri Pirko static int
209411ce2ba3SJiri Pirko rocker_cmd_flow_tbl_add_bridge(struct rocker_desc_info *desc_info,
209511ce2ba3SJiri Pirko 			       const struct rocker_flow_tbl_entry *entry)
209611ce2ba3SJiri Pirko {
209711ce2ba3SJiri Pirko 	if (entry->key.bridge.has_eth_dst &&
209811ce2ba3SJiri Pirko 	    rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC,
209911ce2ba3SJiri Pirko 			   ETH_ALEN, entry->key.bridge.eth_dst))
210011ce2ba3SJiri Pirko 		return -EMSGSIZE;
210111ce2ba3SJiri Pirko 	if (entry->key.bridge.has_eth_dst_mask &&
210211ce2ba3SJiri Pirko 	    rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC_MASK,
210311ce2ba3SJiri Pirko 			   ETH_ALEN, entry->key.bridge.eth_dst_mask))
210411ce2ba3SJiri Pirko 		return -EMSGSIZE;
210511ce2ba3SJiri Pirko 	if (entry->key.bridge.vlan_id &&
210611ce2ba3SJiri Pirko 	    rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID,
210711ce2ba3SJiri Pirko 				entry->key.bridge.vlan_id))
210811ce2ba3SJiri Pirko 		return -EMSGSIZE;
210911ce2ba3SJiri Pirko 	if (entry->key.bridge.tunnel_id &&
211011ce2ba3SJiri Pirko 	    rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_TUNNEL_ID,
211111ce2ba3SJiri Pirko 			       entry->key.bridge.tunnel_id))
211211ce2ba3SJiri Pirko 		return -EMSGSIZE;
211311ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_GOTO_TABLE_ID,
211411ce2ba3SJiri Pirko 			       entry->key.bridge.goto_tbl))
211511ce2ba3SJiri Pirko 		return -EMSGSIZE;
211611ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID,
211711ce2ba3SJiri Pirko 			       entry->key.bridge.group_id))
211811ce2ba3SJiri Pirko 		return -EMSGSIZE;
211911ce2ba3SJiri Pirko 	if (entry->key.bridge.copy_to_cpu &&
212011ce2ba3SJiri Pirko 	    rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_COPY_CPU_ACTION,
212111ce2ba3SJiri Pirko 			      entry->key.bridge.copy_to_cpu))
212211ce2ba3SJiri Pirko 		return -EMSGSIZE;
212311ce2ba3SJiri Pirko 
212411ce2ba3SJiri Pirko 	return 0;
212511ce2ba3SJiri Pirko }
212611ce2ba3SJiri Pirko 
212711ce2ba3SJiri Pirko static int
212811ce2ba3SJiri Pirko rocker_cmd_flow_tbl_add_acl(struct rocker_desc_info *desc_info,
212911ce2ba3SJiri Pirko 			    const struct rocker_flow_tbl_entry *entry)
213011ce2ba3SJiri Pirko {
213111ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT,
213211ce2ba3SJiri Pirko 			       entry->key.acl.in_pport))
213311ce2ba3SJiri Pirko 		return -EMSGSIZE;
213411ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT_MASK,
213511ce2ba3SJiri Pirko 			       entry->key.acl.in_pport_mask))
213611ce2ba3SJiri Pirko 		return -EMSGSIZE;
213711ce2ba3SJiri Pirko 	if (rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_SRC_MAC,
213811ce2ba3SJiri Pirko 			   ETH_ALEN, entry->key.acl.eth_src))
213911ce2ba3SJiri Pirko 		return -EMSGSIZE;
214011ce2ba3SJiri Pirko 	if (rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_SRC_MAC_MASK,
214111ce2ba3SJiri Pirko 			   ETH_ALEN, entry->key.acl.eth_src_mask))
214211ce2ba3SJiri Pirko 		return -EMSGSIZE;
214311ce2ba3SJiri Pirko 	if (rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC,
214411ce2ba3SJiri Pirko 			   ETH_ALEN, entry->key.acl.eth_dst))
214511ce2ba3SJiri Pirko 		return -EMSGSIZE;
214611ce2ba3SJiri Pirko 	if (rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC_MASK,
214711ce2ba3SJiri Pirko 			   ETH_ALEN, entry->key.acl.eth_dst_mask))
214811ce2ba3SJiri Pirko 		return -EMSGSIZE;
214911ce2ba3SJiri Pirko 	if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_ETHERTYPE,
215011ce2ba3SJiri Pirko 				entry->key.acl.eth_type))
215111ce2ba3SJiri Pirko 		return -EMSGSIZE;
215211ce2ba3SJiri Pirko 	if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID,
215311ce2ba3SJiri Pirko 				entry->key.acl.vlan_id))
215411ce2ba3SJiri Pirko 		return -EMSGSIZE;
215511ce2ba3SJiri Pirko 	if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID_MASK,
215611ce2ba3SJiri Pirko 				entry->key.acl.vlan_id_mask))
215711ce2ba3SJiri Pirko 		return -EMSGSIZE;
215811ce2ba3SJiri Pirko 
215911ce2ba3SJiri Pirko 	switch (ntohs(entry->key.acl.eth_type)) {
216011ce2ba3SJiri Pirko 	case ETH_P_IP:
216111ce2ba3SJiri Pirko 	case ETH_P_IPV6:
216211ce2ba3SJiri Pirko 		if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_IP_PROTO,
216311ce2ba3SJiri Pirko 				      entry->key.acl.ip_proto))
216411ce2ba3SJiri Pirko 			return -EMSGSIZE;
216511ce2ba3SJiri Pirko 		if (rocker_tlv_put_u8(desc_info,
216611ce2ba3SJiri Pirko 				      ROCKER_TLV_OF_DPA_IP_PROTO_MASK,
216711ce2ba3SJiri Pirko 				      entry->key.acl.ip_proto_mask))
216811ce2ba3SJiri Pirko 			return -EMSGSIZE;
216911ce2ba3SJiri Pirko 		if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_IP_DSCP,
217011ce2ba3SJiri Pirko 				      entry->key.acl.ip_tos & 0x3f))
217111ce2ba3SJiri Pirko 			return -EMSGSIZE;
217211ce2ba3SJiri Pirko 		if (rocker_tlv_put_u8(desc_info,
217311ce2ba3SJiri Pirko 				      ROCKER_TLV_OF_DPA_IP_DSCP_MASK,
217411ce2ba3SJiri Pirko 				      entry->key.acl.ip_tos_mask & 0x3f))
217511ce2ba3SJiri Pirko 			return -EMSGSIZE;
217611ce2ba3SJiri Pirko 		if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_IP_ECN,
217711ce2ba3SJiri Pirko 				      (entry->key.acl.ip_tos & 0xc0) >> 6))
217811ce2ba3SJiri Pirko 			return -EMSGSIZE;
217911ce2ba3SJiri Pirko 		if (rocker_tlv_put_u8(desc_info,
218011ce2ba3SJiri Pirko 				      ROCKER_TLV_OF_DPA_IP_ECN_MASK,
218111ce2ba3SJiri Pirko 				      (entry->key.acl.ip_tos_mask & 0xc0) >> 6))
218211ce2ba3SJiri Pirko 			return -EMSGSIZE;
218311ce2ba3SJiri Pirko 		break;
218411ce2ba3SJiri Pirko 	}
218511ce2ba3SJiri Pirko 
218611ce2ba3SJiri Pirko 	if (entry->key.acl.group_id != ROCKER_GROUP_NONE &&
218711ce2ba3SJiri Pirko 	    rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID,
218811ce2ba3SJiri Pirko 			       entry->key.acl.group_id))
218911ce2ba3SJiri Pirko 		return -EMSGSIZE;
219011ce2ba3SJiri Pirko 
219111ce2ba3SJiri Pirko 	return 0;
219211ce2ba3SJiri Pirko }
219311ce2ba3SJiri Pirko 
219411ce2ba3SJiri Pirko static int rocker_cmd_flow_tbl_add(const struct rocker_port *rocker_port,
219511ce2ba3SJiri Pirko 				   struct rocker_desc_info *desc_info,
219611ce2ba3SJiri Pirko 				   void *priv)
219711ce2ba3SJiri Pirko {
219811ce2ba3SJiri Pirko 	const struct rocker_flow_tbl_entry *entry = priv;
219911ce2ba3SJiri Pirko 	struct rocker_tlv *cmd_info;
220011ce2ba3SJiri Pirko 	int err = 0;
220111ce2ba3SJiri Pirko 
220211ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, entry->cmd))
220311ce2ba3SJiri Pirko 		return -EMSGSIZE;
220411ce2ba3SJiri Pirko 	cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO);
220511ce2ba3SJiri Pirko 	if (!cmd_info)
220611ce2ba3SJiri Pirko 		return -EMSGSIZE;
220711ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_TABLE_ID,
220811ce2ba3SJiri Pirko 			       entry->key.tbl_id))
220911ce2ba3SJiri Pirko 		return -EMSGSIZE;
221011ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_PRIORITY,
221111ce2ba3SJiri Pirko 			       entry->key.priority))
221211ce2ba3SJiri Pirko 		return -EMSGSIZE;
221311ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_HARDTIME, 0))
221411ce2ba3SJiri Pirko 		return -EMSGSIZE;
221511ce2ba3SJiri Pirko 	if (rocker_tlv_put_u64(desc_info, ROCKER_TLV_OF_DPA_COOKIE,
221611ce2ba3SJiri Pirko 			       entry->cookie))
221711ce2ba3SJiri Pirko 		return -EMSGSIZE;
221811ce2ba3SJiri Pirko 
221911ce2ba3SJiri Pirko 	switch (entry->key.tbl_id) {
222011ce2ba3SJiri Pirko 	case ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT:
222111ce2ba3SJiri Pirko 		err = rocker_cmd_flow_tbl_add_ig_port(desc_info, entry);
222211ce2ba3SJiri Pirko 		break;
222311ce2ba3SJiri Pirko 	case ROCKER_OF_DPA_TABLE_ID_VLAN:
222411ce2ba3SJiri Pirko 		err = rocker_cmd_flow_tbl_add_vlan(desc_info, entry);
222511ce2ba3SJiri Pirko 		break;
222611ce2ba3SJiri Pirko 	case ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC:
222711ce2ba3SJiri Pirko 		err = rocker_cmd_flow_tbl_add_term_mac(desc_info, entry);
222811ce2ba3SJiri Pirko 		break;
222911ce2ba3SJiri Pirko 	case ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING:
223011ce2ba3SJiri Pirko 		err = rocker_cmd_flow_tbl_add_ucast_routing(desc_info, entry);
223111ce2ba3SJiri Pirko 		break;
223211ce2ba3SJiri Pirko 	case ROCKER_OF_DPA_TABLE_ID_BRIDGING:
223311ce2ba3SJiri Pirko 		err = rocker_cmd_flow_tbl_add_bridge(desc_info, entry);
223411ce2ba3SJiri Pirko 		break;
223511ce2ba3SJiri Pirko 	case ROCKER_OF_DPA_TABLE_ID_ACL_POLICY:
223611ce2ba3SJiri Pirko 		err = rocker_cmd_flow_tbl_add_acl(desc_info, entry);
223711ce2ba3SJiri Pirko 		break;
223811ce2ba3SJiri Pirko 	default:
223911ce2ba3SJiri Pirko 		err = -ENOTSUPP;
224011ce2ba3SJiri Pirko 		break;
224111ce2ba3SJiri Pirko 	}
224211ce2ba3SJiri Pirko 
224311ce2ba3SJiri Pirko 	if (err)
224411ce2ba3SJiri Pirko 		return err;
224511ce2ba3SJiri Pirko 
224611ce2ba3SJiri Pirko 	rocker_tlv_nest_end(desc_info, cmd_info);
224711ce2ba3SJiri Pirko 
224811ce2ba3SJiri Pirko 	return 0;
224911ce2ba3SJiri Pirko }
225011ce2ba3SJiri Pirko 
225111ce2ba3SJiri Pirko static int rocker_cmd_flow_tbl_del(const struct rocker_port *rocker_port,
225211ce2ba3SJiri Pirko 				   struct rocker_desc_info *desc_info,
225311ce2ba3SJiri Pirko 				   void *priv)
225411ce2ba3SJiri Pirko {
225511ce2ba3SJiri Pirko 	const struct rocker_flow_tbl_entry *entry = priv;
225611ce2ba3SJiri Pirko 	struct rocker_tlv *cmd_info;
225711ce2ba3SJiri Pirko 
225811ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, entry->cmd))
225911ce2ba3SJiri Pirko 		return -EMSGSIZE;
226011ce2ba3SJiri Pirko 	cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO);
226111ce2ba3SJiri Pirko 	if (!cmd_info)
226211ce2ba3SJiri Pirko 		return -EMSGSIZE;
226311ce2ba3SJiri Pirko 	if (rocker_tlv_put_u64(desc_info, ROCKER_TLV_OF_DPA_COOKIE,
226411ce2ba3SJiri Pirko 			       entry->cookie))
226511ce2ba3SJiri Pirko 		return -EMSGSIZE;
226611ce2ba3SJiri Pirko 	rocker_tlv_nest_end(desc_info, cmd_info);
226711ce2ba3SJiri Pirko 
226811ce2ba3SJiri Pirko 	return 0;
226911ce2ba3SJiri Pirko }
227011ce2ba3SJiri Pirko 
227111ce2ba3SJiri Pirko static int
227211ce2ba3SJiri Pirko rocker_cmd_group_tbl_add_l2_interface(struct rocker_desc_info *desc_info,
227311ce2ba3SJiri Pirko 				      struct rocker_group_tbl_entry *entry)
227411ce2ba3SJiri Pirko {
227511ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_OUT_PPORT,
227611ce2ba3SJiri Pirko 			       ROCKER_GROUP_PORT_GET(entry->group_id)))
227711ce2ba3SJiri Pirko 		return -EMSGSIZE;
227811ce2ba3SJiri Pirko 	if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_POP_VLAN,
227911ce2ba3SJiri Pirko 			      entry->l2_interface.pop_vlan))
228011ce2ba3SJiri Pirko 		return -EMSGSIZE;
228111ce2ba3SJiri Pirko 
228211ce2ba3SJiri Pirko 	return 0;
228311ce2ba3SJiri Pirko }
228411ce2ba3SJiri Pirko 
228511ce2ba3SJiri Pirko static int
228611ce2ba3SJiri Pirko rocker_cmd_group_tbl_add_l2_rewrite(struct rocker_desc_info *desc_info,
228711ce2ba3SJiri Pirko 				    const struct rocker_group_tbl_entry *entry)
228811ce2ba3SJiri Pirko {
228911ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID_LOWER,
229011ce2ba3SJiri Pirko 			       entry->l2_rewrite.group_id))
229111ce2ba3SJiri Pirko 		return -EMSGSIZE;
229211ce2ba3SJiri Pirko 	if (!is_zero_ether_addr(entry->l2_rewrite.eth_src) &&
229311ce2ba3SJiri Pirko 	    rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_SRC_MAC,
229411ce2ba3SJiri Pirko 			   ETH_ALEN, entry->l2_rewrite.eth_src))
229511ce2ba3SJiri Pirko 		return -EMSGSIZE;
229611ce2ba3SJiri Pirko 	if (!is_zero_ether_addr(entry->l2_rewrite.eth_dst) &&
229711ce2ba3SJiri Pirko 	    rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC,
229811ce2ba3SJiri Pirko 			   ETH_ALEN, entry->l2_rewrite.eth_dst))
229911ce2ba3SJiri Pirko 		return -EMSGSIZE;
230011ce2ba3SJiri Pirko 	if (entry->l2_rewrite.vlan_id &&
230111ce2ba3SJiri Pirko 	    rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID,
230211ce2ba3SJiri Pirko 				entry->l2_rewrite.vlan_id))
230311ce2ba3SJiri Pirko 		return -EMSGSIZE;
230411ce2ba3SJiri Pirko 
230511ce2ba3SJiri Pirko 	return 0;
230611ce2ba3SJiri Pirko }
230711ce2ba3SJiri Pirko 
230811ce2ba3SJiri Pirko static int
230911ce2ba3SJiri Pirko rocker_cmd_group_tbl_add_group_ids(struct rocker_desc_info *desc_info,
231011ce2ba3SJiri Pirko 				   const struct rocker_group_tbl_entry *entry)
231111ce2ba3SJiri Pirko {
231211ce2ba3SJiri Pirko 	int i;
231311ce2ba3SJiri Pirko 	struct rocker_tlv *group_ids;
231411ce2ba3SJiri Pirko 
231511ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_GROUP_COUNT,
231611ce2ba3SJiri Pirko 			       entry->group_count))
231711ce2ba3SJiri Pirko 		return -EMSGSIZE;
231811ce2ba3SJiri Pirko 
231911ce2ba3SJiri Pirko 	group_ids = rocker_tlv_nest_start(desc_info,
232011ce2ba3SJiri Pirko 					  ROCKER_TLV_OF_DPA_GROUP_IDS);
232111ce2ba3SJiri Pirko 	if (!group_ids)
232211ce2ba3SJiri Pirko 		return -EMSGSIZE;
232311ce2ba3SJiri Pirko 
232411ce2ba3SJiri Pirko 	for (i = 0; i < entry->group_count; i++)
232511ce2ba3SJiri Pirko 		/* Note TLV array is 1-based */
232611ce2ba3SJiri Pirko 		if (rocker_tlv_put_u32(desc_info, i + 1, entry->group_ids[i]))
232711ce2ba3SJiri Pirko 			return -EMSGSIZE;
232811ce2ba3SJiri Pirko 
232911ce2ba3SJiri Pirko 	rocker_tlv_nest_end(desc_info, group_ids);
233011ce2ba3SJiri Pirko 
233111ce2ba3SJiri Pirko 	return 0;
233211ce2ba3SJiri Pirko }
233311ce2ba3SJiri Pirko 
233411ce2ba3SJiri Pirko static int
233511ce2ba3SJiri Pirko rocker_cmd_group_tbl_add_l3_unicast(struct rocker_desc_info *desc_info,
233611ce2ba3SJiri Pirko 				    const struct rocker_group_tbl_entry *entry)
233711ce2ba3SJiri Pirko {
233811ce2ba3SJiri Pirko 	if (!is_zero_ether_addr(entry->l3_unicast.eth_src) &&
233911ce2ba3SJiri Pirko 	    rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_SRC_MAC,
234011ce2ba3SJiri Pirko 			   ETH_ALEN, entry->l3_unicast.eth_src))
234111ce2ba3SJiri Pirko 		return -EMSGSIZE;
234211ce2ba3SJiri Pirko 	if (!is_zero_ether_addr(entry->l3_unicast.eth_dst) &&
234311ce2ba3SJiri Pirko 	    rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC,
234411ce2ba3SJiri Pirko 			   ETH_ALEN, entry->l3_unicast.eth_dst))
234511ce2ba3SJiri Pirko 		return -EMSGSIZE;
234611ce2ba3SJiri Pirko 	if (entry->l3_unicast.vlan_id &&
234711ce2ba3SJiri Pirko 	    rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID,
234811ce2ba3SJiri Pirko 				entry->l3_unicast.vlan_id))
234911ce2ba3SJiri Pirko 		return -EMSGSIZE;
235011ce2ba3SJiri Pirko 	if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_TTL_CHECK,
235111ce2ba3SJiri Pirko 			      entry->l3_unicast.ttl_check))
235211ce2ba3SJiri Pirko 		return -EMSGSIZE;
235311ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID_LOWER,
235411ce2ba3SJiri Pirko 			       entry->l3_unicast.group_id))
235511ce2ba3SJiri Pirko 		return -EMSGSIZE;
235611ce2ba3SJiri Pirko 
235711ce2ba3SJiri Pirko 	return 0;
235811ce2ba3SJiri Pirko }
235911ce2ba3SJiri Pirko 
236011ce2ba3SJiri Pirko static int rocker_cmd_group_tbl_add(const struct rocker_port *rocker_port,
236111ce2ba3SJiri Pirko 				    struct rocker_desc_info *desc_info,
236211ce2ba3SJiri Pirko 				    void *priv)
236311ce2ba3SJiri Pirko {
236411ce2ba3SJiri Pirko 	struct rocker_group_tbl_entry *entry = priv;
236511ce2ba3SJiri Pirko 	struct rocker_tlv *cmd_info;
236611ce2ba3SJiri Pirko 	int err = 0;
236711ce2ba3SJiri Pirko 
236811ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, entry->cmd))
236911ce2ba3SJiri Pirko 		return -EMSGSIZE;
237011ce2ba3SJiri Pirko 	cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO);
237111ce2ba3SJiri Pirko 	if (!cmd_info)
237211ce2ba3SJiri Pirko 		return -EMSGSIZE;
237311ce2ba3SJiri Pirko 
237411ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID,
237511ce2ba3SJiri Pirko 			       entry->group_id))
237611ce2ba3SJiri Pirko 		return -EMSGSIZE;
237711ce2ba3SJiri Pirko 
237811ce2ba3SJiri Pirko 	switch (ROCKER_GROUP_TYPE_GET(entry->group_id)) {
237911ce2ba3SJiri Pirko 	case ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE:
238011ce2ba3SJiri Pirko 		err = rocker_cmd_group_tbl_add_l2_interface(desc_info, entry);
238111ce2ba3SJiri Pirko 		break;
238211ce2ba3SJiri Pirko 	case ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE:
238311ce2ba3SJiri Pirko 		err = rocker_cmd_group_tbl_add_l2_rewrite(desc_info, entry);
238411ce2ba3SJiri Pirko 		break;
238511ce2ba3SJiri Pirko 	case ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD:
238611ce2ba3SJiri Pirko 	case ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST:
238711ce2ba3SJiri Pirko 		err = rocker_cmd_group_tbl_add_group_ids(desc_info, entry);
238811ce2ba3SJiri Pirko 		break;
238911ce2ba3SJiri Pirko 	case ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST:
239011ce2ba3SJiri Pirko 		err = rocker_cmd_group_tbl_add_l3_unicast(desc_info, entry);
239111ce2ba3SJiri Pirko 		break;
239211ce2ba3SJiri Pirko 	default:
239311ce2ba3SJiri Pirko 		err = -ENOTSUPP;
239411ce2ba3SJiri Pirko 		break;
239511ce2ba3SJiri Pirko 	}
239611ce2ba3SJiri Pirko 
239711ce2ba3SJiri Pirko 	if (err)
239811ce2ba3SJiri Pirko 		return err;
239911ce2ba3SJiri Pirko 
240011ce2ba3SJiri Pirko 	rocker_tlv_nest_end(desc_info, cmd_info);
240111ce2ba3SJiri Pirko 
240211ce2ba3SJiri Pirko 	return 0;
240311ce2ba3SJiri Pirko }
240411ce2ba3SJiri Pirko 
240511ce2ba3SJiri Pirko static int rocker_cmd_group_tbl_del(const struct rocker_port *rocker_port,
240611ce2ba3SJiri Pirko 				    struct rocker_desc_info *desc_info,
240711ce2ba3SJiri Pirko 				    void *priv)
240811ce2ba3SJiri Pirko {
240911ce2ba3SJiri Pirko 	const struct rocker_group_tbl_entry *entry = priv;
241011ce2ba3SJiri Pirko 	struct rocker_tlv *cmd_info;
241111ce2ba3SJiri Pirko 
241211ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, entry->cmd))
241311ce2ba3SJiri Pirko 		return -EMSGSIZE;
241411ce2ba3SJiri Pirko 	cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO);
241511ce2ba3SJiri Pirko 	if (!cmd_info)
241611ce2ba3SJiri Pirko 		return -EMSGSIZE;
241711ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID,
241811ce2ba3SJiri Pirko 			       entry->group_id))
241911ce2ba3SJiri Pirko 		return -EMSGSIZE;
242011ce2ba3SJiri Pirko 	rocker_tlv_nest_end(desc_info, cmd_info);
242111ce2ba3SJiri Pirko 
242211ce2ba3SJiri Pirko 	return 0;
242311ce2ba3SJiri Pirko }
242411ce2ba3SJiri Pirko 
242511ce2ba3SJiri Pirko /***************************************************
242611ce2ba3SJiri Pirko  * Flow, group, FDB, internal VLAN and neigh tables
242711ce2ba3SJiri Pirko  ***************************************************/
242811ce2ba3SJiri Pirko 
242911ce2ba3SJiri Pirko static int rocker_init_tbls(struct rocker *rocker)
243011ce2ba3SJiri Pirko {
243111ce2ba3SJiri Pirko 	hash_init(rocker->flow_tbl);
243211ce2ba3SJiri Pirko 	spin_lock_init(&rocker->flow_tbl_lock);
243311ce2ba3SJiri Pirko 
243411ce2ba3SJiri Pirko 	hash_init(rocker->group_tbl);
243511ce2ba3SJiri Pirko 	spin_lock_init(&rocker->group_tbl_lock);
243611ce2ba3SJiri Pirko 
243711ce2ba3SJiri Pirko 	hash_init(rocker->fdb_tbl);
243811ce2ba3SJiri Pirko 	spin_lock_init(&rocker->fdb_tbl_lock);
243911ce2ba3SJiri Pirko 
244011ce2ba3SJiri Pirko 	hash_init(rocker->internal_vlan_tbl);
244111ce2ba3SJiri Pirko 	spin_lock_init(&rocker->internal_vlan_tbl_lock);
244211ce2ba3SJiri Pirko 
244311ce2ba3SJiri Pirko 	hash_init(rocker->neigh_tbl);
244411ce2ba3SJiri Pirko 	spin_lock_init(&rocker->neigh_tbl_lock);
244511ce2ba3SJiri Pirko 
244611ce2ba3SJiri Pirko 	return 0;
244711ce2ba3SJiri Pirko }
244811ce2ba3SJiri Pirko 
244911ce2ba3SJiri Pirko static void rocker_free_tbls(struct rocker *rocker)
245011ce2ba3SJiri Pirko {
245111ce2ba3SJiri Pirko 	unsigned long flags;
245211ce2ba3SJiri Pirko 	struct rocker_flow_tbl_entry *flow_entry;
245311ce2ba3SJiri Pirko 	struct rocker_group_tbl_entry *group_entry;
245411ce2ba3SJiri Pirko 	struct rocker_fdb_tbl_entry *fdb_entry;
245511ce2ba3SJiri Pirko 	struct rocker_internal_vlan_tbl_entry *internal_vlan_entry;
245611ce2ba3SJiri Pirko 	struct rocker_neigh_tbl_entry *neigh_entry;
245711ce2ba3SJiri Pirko 	struct hlist_node *tmp;
245811ce2ba3SJiri Pirko 	int bkt;
245911ce2ba3SJiri Pirko 
246011ce2ba3SJiri Pirko 	spin_lock_irqsave(&rocker->flow_tbl_lock, flags);
246111ce2ba3SJiri Pirko 	hash_for_each_safe(rocker->flow_tbl, bkt, tmp, flow_entry, entry)
246211ce2ba3SJiri Pirko 		hash_del(&flow_entry->entry);
246311ce2ba3SJiri Pirko 	spin_unlock_irqrestore(&rocker->flow_tbl_lock, flags);
246411ce2ba3SJiri Pirko 
246511ce2ba3SJiri Pirko 	spin_lock_irqsave(&rocker->group_tbl_lock, flags);
246611ce2ba3SJiri Pirko 	hash_for_each_safe(rocker->group_tbl, bkt, tmp, group_entry, entry)
246711ce2ba3SJiri Pirko 		hash_del(&group_entry->entry);
246811ce2ba3SJiri Pirko 	spin_unlock_irqrestore(&rocker->group_tbl_lock, flags);
246911ce2ba3SJiri Pirko 
247011ce2ba3SJiri Pirko 	spin_lock_irqsave(&rocker->fdb_tbl_lock, flags);
247111ce2ba3SJiri Pirko 	hash_for_each_safe(rocker->fdb_tbl, bkt, tmp, fdb_entry, entry)
247211ce2ba3SJiri Pirko 		hash_del(&fdb_entry->entry);
247311ce2ba3SJiri Pirko 	spin_unlock_irqrestore(&rocker->fdb_tbl_lock, flags);
247411ce2ba3SJiri Pirko 
247511ce2ba3SJiri Pirko 	spin_lock_irqsave(&rocker->internal_vlan_tbl_lock, flags);
247611ce2ba3SJiri Pirko 	hash_for_each_safe(rocker->internal_vlan_tbl, bkt,
247711ce2ba3SJiri Pirko 			   tmp, internal_vlan_entry, entry)
247811ce2ba3SJiri Pirko 		hash_del(&internal_vlan_entry->entry);
247911ce2ba3SJiri Pirko 	spin_unlock_irqrestore(&rocker->internal_vlan_tbl_lock, flags);
248011ce2ba3SJiri Pirko 
248111ce2ba3SJiri Pirko 	spin_lock_irqsave(&rocker->neigh_tbl_lock, flags);
248211ce2ba3SJiri Pirko 	hash_for_each_safe(rocker->neigh_tbl, bkt, tmp, neigh_entry, entry)
248311ce2ba3SJiri Pirko 		hash_del(&neigh_entry->entry);
248411ce2ba3SJiri Pirko 	spin_unlock_irqrestore(&rocker->neigh_tbl_lock, flags);
248511ce2ba3SJiri Pirko }
248611ce2ba3SJiri Pirko 
248711ce2ba3SJiri Pirko static struct rocker_flow_tbl_entry *
248811ce2ba3SJiri Pirko rocker_flow_tbl_find(const struct rocker *rocker,
248911ce2ba3SJiri Pirko 		     const struct rocker_flow_tbl_entry *match)
249011ce2ba3SJiri Pirko {
249111ce2ba3SJiri Pirko 	struct rocker_flow_tbl_entry *found;
249211ce2ba3SJiri Pirko 	size_t key_len = match->key_len ? match->key_len : sizeof(found->key);
249311ce2ba3SJiri Pirko 
249411ce2ba3SJiri Pirko 	hash_for_each_possible(rocker->flow_tbl, found,
249511ce2ba3SJiri Pirko 			       entry, match->key_crc32) {
249611ce2ba3SJiri Pirko 		if (memcmp(&found->key, &match->key, key_len) == 0)
249711ce2ba3SJiri Pirko 			return found;
249811ce2ba3SJiri Pirko 	}
249911ce2ba3SJiri Pirko 
250011ce2ba3SJiri Pirko 	return NULL;
250111ce2ba3SJiri Pirko }
250211ce2ba3SJiri Pirko 
250311ce2ba3SJiri Pirko static int rocker_flow_tbl_add(struct rocker_port *rocker_port,
250411ce2ba3SJiri Pirko 			       struct switchdev_trans *trans, int flags,
250511ce2ba3SJiri Pirko 			       struct rocker_flow_tbl_entry *match)
250611ce2ba3SJiri Pirko {
250711ce2ba3SJiri Pirko 	struct rocker *rocker = rocker_port->rocker;
250811ce2ba3SJiri Pirko 	struct rocker_flow_tbl_entry *found;
250911ce2ba3SJiri Pirko 	size_t key_len = match->key_len ? match->key_len : sizeof(found->key);
251011ce2ba3SJiri Pirko 	unsigned long lock_flags;
251111ce2ba3SJiri Pirko 
251211ce2ba3SJiri Pirko 	match->key_crc32 = crc32(~0, &match->key, key_len);
251311ce2ba3SJiri Pirko 
251411ce2ba3SJiri Pirko 	spin_lock_irqsave(&rocker->flow_tbl_lock, lock_flags);
251511ce2ba3SJiri Pirko 
251611ce2ba3SJiri Pirko 	found = rocker_flow_tbl_find(rocker, match);
251711ce2ba3SJiri Pirko 
251811ce2ba3SJiri Pirko 	if (found) {
251911ce2ba3SJiri Pirko 		match->cookie = found->cookie;
252011ce2ba3SJiri Pirko 		if (!switchdev_trans_ph_prepare(trans))
252111ce2ba3SJiri Pirko 			hash_del(&found->entry);
252211ce2ba3SJiri Pirko 		rocker_kfree(trans, found);
252311ce2ba3SJiri Pirko 		found = match;
252411ce2ba3SJiri Pirko 		found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_MOD;
252511ce2ba3SJiri Pirko 	} else {
252611ce2ba3SJiri Pirko 		found = match;
252711ce2ba3SJiri Pirko 		found->cookie = rocker->flow_tbl_next_cookie++;
252811ce2ba3SJiri Pirko 		found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_ADD;
252911ce2ba3SJiri Pirko 	}
253011ce2ba3SJiri Pirko 
253111ce2ba3SJiri Pirko 	if (!switchdev_trans_ph_prepare(trans))
253211ce2ba3SJiri Pirko 		hash_add(rocker->flow_tbl, &found->entry, found->key_crc32);
253311ce2ba3SJiri Pirko 
253411ce2ba3SJiri Pirko 	spin_unlock_irqrestore(&rocker->flow_tbl_lock, lock_flags);
253511ce2ba3SJiri Pirko 
253611ce2ba3SJiri Pirko 	return rocker_cmd_exec(rocker_port, trans, flags,
253711ce2ba3SJiri Pirko 			       rocker_cmd_flow_tbl_add, found, NULL, NULL);
253811ce2ba3SJiri Pirko }
253911ce2ba3SJiri Pirko 
254011ce2ba3SJiri Pirko static int rocker_flow_tbl_del(struct rocker_port *rocker_port,
254111ce2ba3SJiri Pirko 			       struct switchdev_trans *trans, int flags,
254211ce2ba3SJiri Pirko 			       struct rocker_flow_tbl_entry *match)
254311ce2ba3SJiri Pirko {
254411ce2ba3SJiri Pirko 	struct rocker *rocker = rocker_port->rocker;
254511ce2ba3SJiri Pirko 	struct rocker_flow_tbl_entry *found;
254611ce2ba3SJiri Pirko 	size_t key_len = match->key_len ? match->key_len : sizeof(found->key);
254711ce2ba3SJiri Pirko 	unsigned long lock_flags;
254811ce2ba3SJiri Pirko 	int err = 0;
254911ce2ba3SJiri Pirko 
255011ce2ba3SJiri Pirko 	match->key_crc32 = crc32(~0, &match->key, key_len);
255111ce2ba3SJiri Pirko 
255211ce2ba3SJiri Pirko 	spin_lock_irqsave(&rocker->flow_tbl_lock, lock_flags);
255311ce2ba3SJiri Pirko 
255411ce2ba3SJiri Pirko 	found = rocker_flow_tbl_find(rocker, match);
255511ce2ba3SJiri Pirko 
255611ce2ba3SJiri Pirko 	if (found) {
255711ce2ba3SJiri Pirko 		if (!switchdev_trans_ph_prepare(trans))
255811ce2ba3SJiri Pirko 			hash_del(&found->entry);
255911ce2ba3SJiri Pirko 		found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_DEL;
256011ce2ba3SJiri Pirko 	}
256111ce2ba3SJiri Pirko 
256211ce2ba3SJiri Pirko 	spin_unlock_irqrestore(&rocker->flow_tbl_lock, lock_flags);
256311ce2ba3SJiri Pirko 
256411ce2ba3SJiri Pirko 	rocker_kfree(trans, match);
256511ce2ba3SJiri Pirko 
256611ce2ba3SJiri Pirko 	if (found) {
256711ce2ba3SJiri Pirko 		err = rocker_cmd_exec(rocker_port, trans, flags,
256811ce2ba3SJiri Pirko 				      rocker_cmd_flow_tbl_del,
256911ce2ba3SJiri Pirko 				      found, NULL, NULL);
257011ce2ba3SJiri Pirko 		rocker_kfree(trans, found);
257111ce2ba3SJiri Pirko 	}
257211ce2ba3SJiri Pirko 
257311ce2ba3SJiri Pirko 	return err;
257411ce2ba3SJiri Pirko }
257511ce2ba3SJiri Pirko 
257611ce2ba3SJiri Pirko static int rocker_flow_tbl_do(struct rocker_port *rocker_port,
257711ce2ba3SJiri Pirko 			      struct switchdev_trans *trans, int flags,
257811ce2ba3SJiri Pirko 			      struct rocker_flow_tbl_entry *entry)
257911ce2ba3SJiri Pirko {
258011ce2ba3SJiri Pirko 	if (flags & ROCKER_OP_FLAG_REMOVE)
258111ce2ba3SJiri Pirko 		return rocker_flow_tbl_del(rocker_port, trans, flags, entry);
258211ce2ba3SJiri Pirko 	else
258311ce2ba3SJiri Pirko 		return rocker_flow_tbl_add(rocker_port, trans, flags, entry);
258411ce2ba3SJiri Pirko }
258511ce2ba3SJiri Pirko 
258611ce2ba3SJiri Pirko static int rocker_flow_tbl_ig_port(struct rocker_port *rocker_port,
258711ce2ba3SJiri Pirko 				   struct switchdev_trans *trans, int flags,
258811ce2ba3SJiri Pirko 				   u32 in_pport, u32 in_pport_mask,
258911ce2ba3SJiri Pirko 				   enum rocker_of_dpa_table_id goto_tbl)
259011ce2ba3SJiri Pirko {
259111ce2ba3SJiri Pirko 	struct rocker_flow_tbl_entry *entry;
259211ce2ba3SJiri Pirko 
259311ce2ba3SJiri Pirko 	entry = rocker_kzalloc(trans, flags, sizeof(*entry));
259411ce2ba3SJiri Pirko 	if (!entry)
259511ce2ba3SJiri Pirko 		return -ENOMEM;
259611ce2ba3SJiri Pirko 
259711ce2ba3SJiri Pirko 	entry->key.priority = ROCKER_PRIORITY_IG_PORT;
259811ce2ba3SJiri Pirko 	entry->key.tbl_id = ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT;
259911ce2ba3SJiri Pirko 	entry->key.ig_port.in_pport = in_pport;
260011ce2ba3SJiri Pirko 	entry->key.ig_port.in_pport_mask = in_pport_mask;
260111ce2ba3SJiri Pirko 	entry->key.ig_port.goto_tbl = goto_tbl;
260211ce2ba3SJiri Pirko 
260311ce2ba3SJiri Pirko 	return rocker_flow_tbl_do(rocker_port, trans, flags, entry);
260411ce2ba3SJiri Pirko }
260511ce2ba3SJiri Pirko 
260611ce2ba3SJiri Pirko static int rocker_flow_tbl_vlan(struct rocker_port *rocker_port,
260711ce2ba3SJiri Pirko 				struct switchdev_trans *trans, int flags,
260811ce2ba3SJiri Pirko 				u32 in_pport, __be16 vlan_id,
260911ce2ba3SJiri Pirko 				__be16 vlan_id_mask,
261011ce2ba3SJiri Pirko 				enum rocker_of_dpa_table_id goto_tbl,
261111ce2ba3SJiri Pirko 				bool untagged, __be16 new_vlan_id)
261211ce2ba3SJiri Pirko {
261311ce2ba3SJiri Pirko 	struct rocker_flow_tbl_entry *entry;
261411ce2ba3SJiri Pirko 
261511ce2ba3SJiri Pirko 	entry = rocker_kzalloc(trans, flags, sizeof(*entry));
261611ce2ba3SJiri Pirko 	if (!entry)
261711ce2ba3SJiri Pirko 		return -ENOMEM;
261811ce2ba3SJiri Pirko 
261911ce2ba3SJiri Pirko 	entry->key.priority = ROCKER_PRIORITY_VLAN;
262011ce2ba3SJiri Pirko 	entry->key.tbl_id = ROCKER_OF_DPA_TABLE_ID_VLAN;
262111ce2ba3SJiri Pirko 	entry->key.vlan.in_pport = in_pport;
262211ce2ba3SJiri Pirko 	entry->key.vlan.vlan_id = vlan_id;
262311ce2ba3SJiri Pirko 	entry->key.vlan.vlan_id_mask = vlan_id_mask;
262411ce2ba3SJiri Pirko 	entry->key.vlan.goto_tbl = goto_tbl;
262511ce2ba3SJiri Pirko 
262611ce2ba3SJiri Pirko 	entry->key.vlan.untagged = untagged;
262711ce2ba3SJiri Pirko 	entry->key.vlan.new_vlan_id = new_vlan_id;
262811ce2ba3SJiri Pirko 
262911ce2ba3SJiri Pirko 	return rocker_flow_tbl_do(rocker_port, trans, flags, entry);
263011ce2ba3SJiri Pirko }
263111ce2ba3SJiri Pirko 
263211ce2ba3SJiri Pirko static int rocker_flow_tbl_term_mac(struct rocker_port *rocker_port,
263311ce2ba3SJiri Pirko 				    struct switchdev_trans *trans,
263411ce2ba3SJiri Pirko 				    u32 in_pport, u32 in_pport_mask,
263511ce2ba3SJiri Pirko 				    __be16 eth_type, const u8 *eth_dst,
263611ce2ba3SJiri Pirko 				    const u8 *eth_dst_mask, __be16 vlan_id,
263711ce2ba3SJiri Pirko 				    __be16 vlan_id_mask, bool copy_to_cpu,
263811ce2ba3SJiri Pirko 				    int flags)
263911ce2ba3SJiri Pirko {
264011ce2ba3SJiri Pirko 	struct rocker_flow_tbl_entry *entry;
264111ce2ba3SJiri Pirko 
264211ce2ba3SJiri Pirko 	entry = rocker_kzalloc(trans, flags, sizeof(*entry));
264311ce2ba3SJiri Pirko 	if (!entry)
264411ce2ba3SJiri Pirko 		return -ENOMEM;
264511ce2ba3SJiri Pirko 
264611ce2ba3SJiri Pirko 	if (is_multicast_ether_addr(eth_dst)) {
264711ce2ba3SJiri Pirko 		entry->key.priority = ROCKER_PRIORITY_TERM_MAC_MCAST;
264811ce2ba3SJiri Pirko 		entry->key.term_mac.goto_tbl =
264911ce2ba3SJiri Pirko 			 ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING;
265011ce2ba3SJiri Pirko 	} else {
265111ce2ba3SJiri Pirko 		entry->key.priority = ROCKER_PRIORITY_TERM_MAC_UCAST;
265211ce2ba3SJiri Pirko 		entry->key.term_mac.goto_tbl =
265311ce2ba3SJiri Pirko 			 ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING;
265411ce2ba3SJiri Pirko 	}
265511ce2ba3SJiri Pirko 
265611ce2ba3SJiri Pirko 	entry->key.tbl_id = ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC;
265711ce2ba3SJiri Pirko 	entry->key.term_mac.in_pport = in_pport;
265811ce2ba3SJiri Pirko 	entry->key.term_mac.in_pport_mask = in_pport_mask;
265911ce2ba3SJiri Pirko 	entry->key.term_mac.eth_type = eth_type;
266011ce2ba3SJiri Pirko 	ether_addr_copy(entry->key.term_mac.eth_dst, eth_dst);
266111ce2ba3SJiri Pirko 	ether_addr_copy(entry->key.term_mac.eth_dst_mask, eth_dst_mask);
266211ce2ba3SJiri Pirko 	entry->key.term_mac.vlan_id = vlan_id;
266311ce2ba3SJiri Pirko 	entry->key.term_mac.vlan_id_mask = vlan_id_mask;
266411ce2ba3SJiri Pirko 	entry->key.term_mac.copy_to_cpu = copy_to_cpu;
266511ce2ba3SJiri Pirko 
266611ce2ba3SJiri Pirko 	return rocker_flow_tbl_do(rocker_port, trans, flags, entry);
266711ce2ba3SJiri Pirko }
266811ce2ba3SJiri Pirko 
266911ce2ba3SJiri Pirko static int rocker_flow_tbl_bridge(struct rocker_port *rocker_port,
267011ce2ba3SJiri Pirko 				  struct switchdev_trans *trans, int flags,
267111ce2ba3SJiri Pirko 				  const u8 *eth_dst, const u8 *eth_dst_mask,
267211ce2ba3SJiri Pirko 				  __be16 vlan_id, u32 tunnel_id,
267311ce2ba3SJiri Pirko 				  enum rocker_of_dpa_table_id goto_tbl,
267411ce2ba3SJiri Pirko 				  u32 group_id, bool copy_to_cpu)
267511ce2ba3SJiri Pirko {
267611ce2ba3SJiri Pirko 	struct rocker_flow_tbl_entry *entry;
267711ce2ba3SJiri Pirko 	u32 priority;
267811ce2ba3SJiri Pirko 	bool vlan_bridging = !!vlan_id;
267911ce2ba3SJiri Pirko 	bool dflt = !eth_dst || (eth_dst && eth_dst_mask);
268011ce2ba3SJiri Pirko 	bool wild = false;
268111ce2ba3SJiri Pirko 
268211ce2ba3SJiri Pirko 	entry = rocker_kzalloc(trans, flags, sizeof(*entry));
268311ce2ba3SJiri Pirko 	if (!entry)
268411ce2ba3SJiri Pirko 		return -ENOMEM;
268511ce2ba3SJiri Pirko 
268611ce2ba3SJiri Pirko 	entry->key.tbl_id = ROCKER_OF_DPA_TABLE_ID_BRIDGING;
268711ce2ba3SJiri Pirko 
268811ce2ba3SJiri Pirko 	if (eth_dst) {
268911ce2ba3SJiri Pirko 		entry->key.bridge.has_eth_dst = 1;
269011ce2ba3SJiri Pirko 		ether_addr_copy(entry->key.bridge.eth_dst, eth_dst);
269111ce2ba3SJiri Pirko 	}
269211ce2ba3SJiri Pirko 	if (eth_dst_mask) {
269311ce2ba3SJiri Pirko 		entry->key.bridge.has_eth_dst_mask = 1;
269411ce2ba3SJiri Pirko 		ether_addr_copy(entry->key.bridge.eth_dst_mask, eth_dst_mask);
269511ce2ba3SJiri Pirko 		if (!ether_addr_equal(eth_dst_mask, ff_mac))
269611ce2ba3SJiri Pirko 			wild = true;
269711ce2ba3SJiri Pirko 	}
269811ce2ba3SJiri Pirko 
269911ce2ba3SJiri Pirko 	priority = ROCKER_PRIORITY_UNKNOWN;
270011ce2ba3SJiri Pirko 	if (vlan_bridging && dflt && wild)
270111ce2ba3SJiri Pirko 		priority = ROCKER_PRIORITY_BRIDGING_VLAN_DFLT_WILD;
270211ce2ba3SJiri Pirko 	else if (vlan_bridging && dflt && !wild)
270311ce2ba3SJiri Pirko 		priority = ROCKER_PRIORITY_BRIDGING_VLAN_DFLT_EXACT;
270411ce2ba3SJiri Pirko 	else if (vlan_bridging && !dflt)
270511ce2ba3SJiri Pirko 		priority = ROCKER_PRIORITY_BRIDGING_VLAN;
270611ce2ba3SJiri Pirko 	else if (!vlan_bridging && dflt && wild)
270711ce2ba3SJiri Pirko 		priority = ROCKER_PRIORITY_BRIDGING_TENANT_DFLT_WILD;
270811ce2ba3SJiri Pirko 	else if (!vlan_bridging && dflt && !wild)
270911ce2ba3SJiri Pirko 		priority = ROCKER_PRIORITY_BRIDGING_TENANT_DFLT_EXACT;
271011ce2ba3SJiri Pirko 	else if (!vlan_bridging && !dflt)
271111ce2ba3SJiri Pirko 		priority = ROCKER_PRIORITY_BRIDGING_TENANT;
271211ce2ba3SJiri Pirko 
271311ce2ba3SJiri Pirko 	entry->key.priority = priority;
271411ce2ba3SJiri Pirko 	entry->key.bridge.vlan_id = vlan_id;
271511ce2ba3SJiri Pirko 	entry->key.bridge.tunnel_id = tunnel_id;
271611ce2ba3SJiri Pirko 	entry->key.bridge.goto_tbl = goto_tbl;
271711ce2ba3SJiri Pirko 	entry->key.bridge.group_id = group_id;
271811ce2ba3SJiri Pirko 	entry->key.bridge.copy_to_cpu = copy_to_cpu;
271911ce2ba3SJiri Pirko 
272011ce2ba3SJiri Pirko 	return rocker_flow_tbl_do(rocker_port, trans, flags, entry);
272111ce2ba3SJiri Pirko }
272211ce2ba3SJiri Pirko 
272311ce2ba3SJiri Pirko static int rocker_flow_tbl_ucast4_routing(struct rocker_port *rocker_port,
272411ce2ba3SJiri Pirko 					  struct switchdev_trans *trans,
272511ce2ba3SJiri Pirko 					  __be16 eth_type, __be32 dst,
272611ce2ba3SJiri Pirko 					  __be32 dst_mask, u32 priority,
272711ce2ba3SJiri Pirko 					  enum rocker_of_dpa_table_id goto_tbl,
272811ce2ba3SJiri Pirko 					  u32 group_id, int flags)
272911ce2ba3SJiri Pirko {
273011ce2ba3SJiri Pirko 	struct rocker_flow_tbl_entry *entry;
273111ce2ba3SJiri Pirko 
273211ce2ba3SJiri Pirko 	entry = rocker_kzalloc(trans, flags, sizeof(*entry));
273311ce2ba3SJiri Pirko 	if (!entry)
273411ce2ba3SJiri Pirko 		return -ENOMEM;
273511ce2ba3SJiri Pirko 
273611ce2ba3SJiri Pirko 	entry->key.tbl_id = ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING;
273711ce2ba3SJiri Pirko 	entry->key.priority = priority;
273811ce2ba3SJiri Pirko 	entry->key.ucast_routing.eth_type = eth_type;
273911ce2ba3SJiri Pirko 	entry->key.ucast_routing.dst4 = dst;
274011ce2ba3SJiri Pirko 	entry->key.ucast_routing.dst4_mask = dst_mask;
274111ce2ba3SJiri Pirko 	entry->key.ucast_routing.goto_tbl = goto_tbl;
274211ce2ba3SJiri Pirko 	entry->key.ucast_routing.group_id = group_id;
274311ce2ba3SJiri Pirko 	entry->key_len = offsetof(struct rocker_flow_tbl_key,
274411ce2ba3SJiri Pirko 				  ucast_routing.group_id);
274511ce2ba3SJiri Pirko 
274611ce2ba3SJiri Pirko 	return rocker_flow_tbl_do(rocker_port, trans, flags, entry);
274711ce2ba3SJiri Pirko }
274811ce2ba3SJiri Pirko 
274911ce2ba3SJiri Pirko static int rocker_flow_tbl_acl(struct rocker_port *rocker_port,
275011ce2ba3SJiri Pirko 			       struct switchdev_trans *trans, int flags,
275111ce2ba3SJiri Pirko 			       u32 in_pport, u32 in_pport_mask,
275211ce2ba3SJiri Pirko 			       const u8 *eth_src, const u8 *eth_src_mask,
275311ce2ba3SJiri Pirko 			       const u8 *eth_dst, const u8 *eth_dst_mask,
275411ce2ba3SJiri Pirko 			       __be16 eth_type, __be16 vlan_id,
275511ce2ba3SJiri Pirko 			       __be16 vlan_id_mask, u8 ip_proto,
275611ce2ba3SJiri Pirko 			       u8 ip_proto_mask, u8 ip_tos, u8 ip_tos_mask,
275711ce2ba3SJiri Pirko 			       u32 group_id)
275811ce2ba3SJiri Pirko {
275911ce2ba3SJiri Pirko 	u32 priority;
276011ce2ba3SJiri Pirko 	struct rocker_flow_tbl_entry *entry;
276111ce2ba3SJiri Pirko 
276211ce2ba3SJiri Pirko 	entry = rocker_kzalloc(trans, flags, sizeof(*entry));
276311ce2ba3SJiri Pirko 	if (!entry)
276411ce2ba3SJiri Pirko 		return -ENOMEM;
276511ce2ba3SJiri Pirko 
276611ce2ba3SJiri Pirko 	priority = ROCKER_PRIORITY_ACL_NORMAL;
276711ce2ba3SJiri Pirko 	if (eth_dst && eth_dst_mask) {
276811ce2ba3SJiri Pirko 		if (ether_addr_equal(eth_dst_mask, mcast_mac))
276911ce2ba3SJiri Pirko 			priority = ROCKER_PRIORITY_ACL_DFLT;
277011ce2ba3SJiri Pirko 		else if (is_link_local_ether_addr(eth_dst))
277111ce2ba3SJiri Pirko 			priority = ROCKER_PRIORITY_ACL_CTRL;
277211ce2ba3SJiri Pirko 	}
277311ce2ba3SJiri Pirko 
277411ce2ba3SJiri Pirko 	entry->key.priority = priority;
277511ce2ba3SJiri Pirko 	entry->key.tbl_id = ROCKER_OF_DPA_TABLE_ID_ACL_POLICY;
277611ce2ba3SJiri Pirko 	entry->key.acl.in_pport = in_pport;
277711ce2ba3SJiri Pirko 	entry->key.acl.in_pport_mask = in_pport_mask;
277811ce2ba3SJiri Pirko 
277911ce2ba3SJiri Pirko 	if (eth_src)
278011ce2ba3SJiri Pirko 		ether_addr_copy(entry->key.acl.eth_src, eth_src);
278111ce2ba3SJiri Pirko 	if (eth_src_mask)
278211ce2ba3SJiri Pirko 		ether_addr_copy(entry->key.acl.eth_src_mask, eth_src_mask);
278311ce2ba3SJiri Pirko 	if (eth_dst)
278411ce2ba3SJiri Pirko 		ether_addr_copy(entry->key.acl.eth_dst, eth_dst);
278511ce2ba3SJiri Pirko 	if (eth_dst_mask)
278611ce2ba3SJiri Pirko 		ether_addr_copy(entry->key.acl.eth_dst_mask, eth_dst_mask);
278711ce2ba3SJiri Pirko 
278811ce2ba3SJiri Pirko 	entry->key.acl.eth_type = eth_type;
278911ce2ba3SJiri Pirko 	entry->key.acl.vlan_id = vlan_id;
279011ce2ba3SJiri Pirko 	entry->key.acl.vlan_id_mask = vlan_id_mask;
279111ce2ba3SJiri Pirko 	entry->key.acl.ip_proto = ip_proto;
279211ce2ba3SJiri Pirko 	entry->key.acl.ip_proto_mask = ip_proto_mask;
279311ce2ba3SJiri Pirko 	entry->key.acl.ip_tos = ip_tos;
279411ce2ba3SJiri Pirko 	entry->key.acl.ip_tos_mask = ip_tos_mask;
279511ce2ba3SJiri Pirko 	entry->key.acl.group_id = group_id;
279611ce2ba3SJiri Pirko 
279711ce2ba3SJiri Pirko 	return rocker_flow_tbl_do(rocker_port, trans, flags, entry);
279811ce2ba3SJiri Pirko }
279911ce2ba3SJiri Pirko 
280011ce2ba3SJiri Pirko static struct rocker_group_tbl_entry *
280111ce2ba3SJiri Pirko rocker_group_tbl_find(const struct rocker *rocker,
280211ce2ba3SJiri Pirko 		      const struct rocker_group_tbl_entry *match)
280311ce2ba3SJiri Pirko {
280411ce2ba3SJiri Pirko 	struct rocker_group_tbl_entry *found;
280511ce2ba3SJiri Pirko 
280611ce2ba3SJiri Pirko 	hash_for_each_possible(rocker->group_tbl, found,
280711ce2ba3SJiri Pirko 			       entry, match->group_id) {
280811ce2ba3SJiri Pirko 		if (found->group_id == match->group_id)
280911ce2ba3SJiri Pirko 			return found;
281011ce2ba3SJiri Pirko 	}
281111ce2ba3SJiri Pirko 
281211ce2ba3SJiri Pirko 	return NULL;
281311ce2ba3SJiri Pirko }
281411ce2ba3SJiri Pirko 
281511ce2ba3SJiri Pirko static void rocker_group_tbl_entry_free(struct switchdev_trans *trans,
281611ce2ba3SJiri Pirko 					struct rocker_group_tbl_entry *entry)
281711ce2ba3SJiri Pirko {
281811ce2ba3SJiri Pirko 	switch (ROCKER_GROUP_TYPE_GET(entry->group_id)) {
281911ce2ba3SJiri Pirko 	case ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD:
282011ce2ba3SJiri Pirko 	case ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST:
282111ce2ba3SJiri Pirko 		rocker_kfree(trans, entry->group_ids);
282211ce2ba3SJiri Pirko 		break;
282311ce2ba3SJiri Pirko 	default:
282411ce2ba3SJiri Pirko 		break;
282511ce2ba3SJiri Pirko 	}
282611ce2ba3SJiri Pirko 	rocker_kfree(trans, entry);
282711ce2ba3SJiri Pirko }
282811ce2ba3SJiri Pirko 
282911ce2ba3SJiri Pirko static int rocker_group_tbl_add(struct rocker_port *rocker_port,
283011ce2ba3SJiri Pirko 				struct switchdev_trans *trans, int flags,
283111ce2ba3SJiri Pirko 				struct rocker_group_tbl_entry *match)
283211ce2ba3SJiri Pirko {
283311ce2ba3SJiri Pirko 	struct rocker *rocker = rocker_port->rocker;
283411ce2ba3SJiri Pirko 	struct rocker_group_tbl_entry *found;
283511ce2ba3SJiri Pirko 	unsigned long lock_flags;
283611ce2ba3SJiri Pirko 
283711ce2ba3SJiri Pirko 	spin_lock_irqsave(&rocker->group_tbl_lock, lock_flags);
283811ce2ba3SJiri Pirko 
283911ce2ba3SJiri Pirko 	found = rocker_group_tbl_find(rocker, match);
284011ce2ba3SJiri Pirko 
284111ce2ba3SJiri Pirko 	if (found) {
284211ce2ba3SJiri Pirko 		if (!switchdev_trans_ph_prepare(trans))
284311ce2ba3SJiri Pirko 			hash_del(&found->entry);
284411ce2ba3SJiri Pirko 		rocker_group_tbl_entry_free(trans, found);
284511ce2ba3SJiri Pirko 		found = match;
284611ce2ba3SJiri Pirko 		found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_MOD;
284711ce2ba3SJiri Pirko 	} else {
284811ce2ba3SJiri Pirko 		found = match;
284911ce2ba3SJiri Pirko 		found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_ADD;
285011ce2ba3SJiri Pirko 	}
285111ce2ba3SJiri Pirko 
285211ce2ba3SJiri Pirko 	if (!switchdev_trans_ph_prepare(trans))
285311ce2ba3SJiri Pirko 		hash_add(rocker->group_tbl, &found->entry, found->group_id);
285411ce2ba3SJiri Pirko 
285511ce2ba3SJiri Pirko 	spin_unlock_irqrestore(&rocker->group_tbl_lock, lock_flags);
285611ce2ba3SJiri Pirko 
285711ce2ba3SJiri Pirko 	return rocker_cmd_exec(rocker_port, trans, flags,
285811ce2ba3SJiri Pirko 			       rocker_cmd_group_tbl_add, found, NULL, NULL);
285911ce2ba3SJiri Pirko }
286011ce2ba3SJiri Pirko 
286111ce2ba3SJiri Pirko static int rocker_group_tbl_del(struct rocker_port *rocker_port,
286211ce2ba3SJiri Pirko 				struct switchdev_trans *trans, int flags,
286311ce2ba3SJiri Pirko 				struct rocker_group_tbl_entry *match)
286411ce2ba3SJiri Pirko {
286511ce2ba3SJiri Pirko 	struct rocker *rocker = rocker_port->rocker;
286611ce2ba3SJiri Pirko 	struct rocker_group_tbl_entry *found;
286711ce2ba3SJiri Pirko 	unsigned long lock_flags;
286811ce2ba3SJiri Pirko 	int err = 0;
286911ce2ba3SJiri Pirko 
287011ce2ba3SJiri Pirko 	spin_lock_irqsave(&rocker->group_tbl_lock, lock_flags);
287111ce2ba3SJiri Pirko 
287211ce2ba3SJiri Pirko 	found = rocker_group_tbl_find(rocker, match);
287311ce2ba3SJiri Pirko 
287411ce2ba3SJiri Pirko 	if (found) {
287511ce2ba3SJiri Pirko 		if (!switchdev_trans_ph_prepare(trans))
287611ce2ba3SJiri Pirko 			hash_del(&found->entry);
287711ce2ba3SJiri Pirko 		found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_DEL;
287811ce2ba3SJiri Pirko 	}
287911ce2ba3SJiri Pirko 
288011ce2ba3SJiri Pirko 	spin_unlock_irqrestore(&rocker->group_tbl_lock, lock_flags);
288111ce2ba3SJiri Pirko 
288211ce2ba3SJiri Pirko 	rocker_group_tbl_entry_free(trans, match);
288311ce2ba3SJiri Pirko 
288411ce2ba3SJiri Pirko 	if (found) {
288511ce2ba3SJiri Pirko 		err = rocker_cmd_exec(rocker_port, trans, flags,
288611ce2ba3SJiri Pirko 				      rocker_cmd_group_tbl_del,
288711ce2ba3SJiri Pirko 				      found, NULL, NULL);
288811ce2ba3SJiri Pirko 		rocker_group_tbl_entry_free(trans, found);
288911ce2ba3SJiri Pirko 	}
289011ce2ba3SJiri Pirko 
289111ce2ba3SJiri Pirko 	return err;
289211ce2ba3SJiri Pirko }
289311ce2ba3SJiri Pirko 
289411ce2ba3SJiri Pirko static int rocker_group_tbl_do(struct rocker_port *rocker_port,
289511ce2ba3SJiri Pirko 			       struct switchdev_trans *trans, int flags,
289611ce2ba3SJiri Pirko 			       struct rocker_group_tbl_entry *entry)
289711ce2ba3SJiri Pirko {
289811ce2ba3SJiri Pirko 	if (flags & ROCKER_OP_FLAG_REMOVE)
289911ce2ba3SJiri Pirko 		return rocker_group_tbl_del(rocker_port, trans, flags, entry);
290011ce2ba3SJiri Pirko 	else
290111ce2ba3SJiri Pirko 		return rocker_group_tbl_add(rocker_port, trans, flags, entry);
290211ce2ba3SJiri Pirko }
290311ce2ba3SJiri Pirko 
290411ce2ba3SJiri Pirko static int rocker_group_l2_interface(struct rocker_port *rocker_port,
290511ce2ba3SJiri Pirko 				     struct switchdev_trans *trans, int flags,
290611ce2ba3SJiri Pirko 				     __be16 vlan_id, u32 out_pport,
290711ce2ba3SJiri Pirko 				     int pop_vlan)
290811ce2ba3SJiri Pirko {
290911ce2ba3SJiri Pirko 	struct rocker_group_tbl_entry *entry;
291011ce2ba3SJiri Pirko 
291111ce2ba3SJiri Pirko 	entry = rocker_kzalloc(trans, flags, sizeof(*entry));
291211ce2ba3SJiri Pirko 	if (!entry)
291311ce2ba3SJiri Pirko 		return -ENOMEM;
291411ce2ba3SJiri Pirko 
291511ce2ba3SJiri Pirko 	entry->group_id = ROCKER_GROUP_L2_INTERFACE(vlan_id, out_pport);
291611ce2ba3SJiri Pirko 	entry->l2_interface.pop_vlan = pop_vlan;
291711ce2ba3SJiri Pirko 
291811ce2ba3SJiri Pirko 	return rocker_group_tbl_do(rocker_port, trans, flags, entry);
291911ce2ba3SJiri Pirko }
292011ce2ba3SJiri Pirko 
292111ce2ba3SJiri Pirko static int rocker_group_l2_fan_out(struct rocker_port *rocker_port,
292211ce2ba3SJiri Pirko 				   struct switchdev_trans *trans,
292311ce2ba3SJiri Pirko 				   int flags, u8 group_count,
292411ce2ba3SJiri Pirko 				   const u32 *group_ids, u32 group_id)
292511ce2ba3SJiri Pirko {
292611ce2ba3SJiri Pirko 	struct rocker_group_tbl_entry *entry;
292711ce2ba3SJiri Pirko 
292811ce2ba3SJiri Pirko 	entry = rocker_kzalloc(trans, flags, sizeof(*entry));
292911ce2ba3SJiri Pirko 	if (!entry)
293011ce2ba3SJiri Pirko 		return -ENOMEM;
293111ce2ba3SJiri Pirko 
293211ce2ba3SJiri Pirko 	entry->group_id = group_id;
293311ce2ba3SJiri Pirko 	entry->group_count = group_count;
293411ce2ba3SJiri Pirko 
293511ce2ba3SJiri Pirko 	entry->group_ids = rocker_kcalloc(trans, flags,
293611ce2ba3SJiri Pirko 					  group_count, sizeof(u32));
293711ce2ba3SJiri Pirko 	if (!entry->group_ids) {
293811ce2ba3SJiri Pirko 		rocker_kfree(trans, entry);
293911ce2ba3SJiri Pirko 		return -ENOMEM;
294011ce2ba3SJiri Pirko 	}
294111ce2ba3SJiri Pirko 	memcpy(entry->group_ids, group_ids, group_count * sizeof(u32));
294211ce2ba3SJiri Pirko 
294311ce2ba3SJiri Pirko 	return rocker_group_tbl_do(rocker_port, trans, flags, entry);
294411ce2ba3SJiri Pirko }
294511ce2ba3SJiri Pirko 
294611ce2ba3SJiri Pirko static int rocker_group_l2_flood(struct rocker_port *rocker_port,
294711ce2ba3SJiri Pirko 				 struct switchdev_trans *trans, int flags,
294811ce2ba3SJiri Pirko 				 __be16 vlan_id, u8 group_count,
294911ce2ba3SJiri Pirko 				 const u32 *group_ids, u32 group_id)
295011ce2ba3SJiri Pirko {
295111ce2ba3SJiri Pirko 	return rocker_group_l2_fan_out(rocker_port, trans, flags,
295211ce2ba3SJiri Pirko 				       group_count, group_ids,
295311ce2ba3SJiri Pirko 				       group_id);
295411ce2ba3SJiri Pirko }
295511ce2ba3SJiri Pirko 
295611ce2ba3SJiri Pirko static int rocker_group_l3_unicast(struct rocker_port *rocker_port,
295711ce2ba3SJiri Pirko 				   struct switchdev_trans *trans, int flags,
295811ce2ba3SJiri Pirko 				   u32 index, const u8 *src_mac, const u8 *dst_mac,
295911ce2ba3SJiri Pirko 				   __be16 vlan_id, bool ttl_check, u32 pport)
296011ce2ba3SJiri Pirko {
296111ce2ba3SJiri Pirko 	struct rocker_group_tbl_entry *entry;
296211ce2ba3SJiri Pirko 
296311ce2ba3SJiri Pirko 	entry = rocker_kzalloc(trans, flags, sizeof(*entry));
296411ce2ba3SJiri Pirko 	if (!entry)
296511ce2ba3SJiri Pirko 		return -ENOMEM;
296611ce2ba3SJiri Pirko 
296711ce2ba3SJiri Pirko 	entry->group_id = ROCKER_GROUP_L3_UNICAST(index);
296811ce2ba3SJiri Pirko 	if (src_mac)
296911ce2ba3SJiri Pirko 		ether_addr_copy(entry->l3_unicast.eth_src, src_mac);
297011ce2ba3SJiri Pirko 	if (dst_mac)
297111ce2ba3SJiri Pirko 		ether_addr_copy(entry->l3_unicast.eth_dst, dst_mac);
297211ce2ba3SJiri Pirko 	entry->l3_unicast.vlan_id = vlan_id;
297311ce2ba3SJiri Pirko 	entry->l3_unicast.ttl_check = ttl_check;
297411ce2ba3SJiri Pirko 	entry->l3_unicast.group_id = ROCKER_GROUP_L2_INTERFACE(vlan_id, pport);
297511ce2ba3SJiri Pirko 
297611ce2ba3SJiri Pirko 	return rocker_group_tbl_do(rocker_port, trans, flags, entry);
297711ce2ba3SJiri Pirko }
297811ce2ba3SJiri Pirko 
297911ce2ba3SJiri Pirko static struct rocker_neigh_tbl_entry *
298011ce2ba3SJiri Pirko rocker_neigh_tbl_find(const struct rocker *rocker, __be32 ip_addr)
298111ce2ba3SJiri Pirko {
298211ce2ba3SJiri Pirko 	struct rocker_neigh_tbl_entry *found;
298311ce2ba3SJiri Pirko 
298411ce2ba3SJiri Pirko 	hash_for_each_possible(rocker->neigh_tbl, found,
298511ce2ba3SJiri Pirko 			       entry, be32_to_cpu(ip_addr))
298611ce2ba3SJiri Pirko 		if (found->ip_addr == ip_addr)
298711ce2ba3SJiri Pirko 			return found;
298811ce2ba3SJiri Pirko 
298911ce2ba3SJiri Pirko 	return NULL;
299011ce2ba3SJiri Pirko }
299111ce2ba3SJiri Pirko 
299211ce2ba3SJiri Pirko static void _rocker_neigh_add(struct rocker *rocker,
299311ce2ba3SJiri Pirko 			      struct switchdev_trans *trans,
299411ce2ba3SJiri Pirko 			      struct rocker_neigh_tbl_entry *entry)
299511ce2ba3SJiri Pirko {
299611ce2ba3SJiri Pirko 	if (!switchdev_trans_ph_commit(trans))
299711ce2ba3SJiri Pirko 		entry->index = rocker->neigh_tbl_next_index++;
299811ce2ba3SJiri Pirko 	if (switchdev_trans_ph_prepare(trans))
299911ce2ba3SJiri Pirko 		return;
300011ce2ba3SJiri Pirko 	entry->ref_count++;
300111ce2ba3SJiri Pirko 	hash_add(rocker->neigh_tbl, &entry->entry,
300211ce2ba3SJiri Pirko 		 be32_to_cpu(entry->ip_addr));
300311ce2ba3SJiri Pirko }
300411ce2ba3SJiri Pirko 
300511ce2ba3SJiri Pirko static void _rocker_neigh_del(struct switchdev_trans *trans,
300611ce2ba3SJiri Pirko 			      struct rocker_neigh_tbl_entry *entry)
300711ce2ba3SJiri Pirko {
300811ce2ba3SJiri Pirko 	if (switchdev_trans_ph_prepare(trans))
300911ce2ba3SJiri Pirko 		return;
301011ce2ba3SJiri Pirko 	if (--entry->ref_count == 0) {
301111ce2ba3SJiri Pirko 		hash_del(&entry->entry);
301211ce2ba3SJiri Pirko 		rocker_kfree(trans, entry);
301311ce2ba3SJiri Pirko 	}
301411ce2ba3SJiri Pirko }
301511ce2ba3SJiri Pirko 
301611ce2ba3SJiri Pirko static void _rocker_neigh_update(struct rocker_neigh_tbl_entry *entry,
301711ce2ba3SJiri Pirko 				 struct switchdev_trans *trans,
301811ce2ba3SJiri Pirko 				 const u8 *eth_dst, bool ttl_check)
301911ce2ba3SJiri Pirko {
302011ce2ba3SJiri Pirko 	if (eth_dst) {
302111ce2ba3SJiri Pirko 		ether_addr_copy(entry->eth_dst, eth_dst);
302211ce2ba3SJiri Pirko 		entry->ttl_check = ttl_check;
302311ce2ba3SJiri Pirko 	} else if (!switchdev_trans_ph_prepare(trans)) {
302411ce2ba3SJiri Pirko 		entry->ref_count++;
302511ce2ba3SJiri Pirko 	}
302611ce2ba3SJiri Pirko }
302711ce2ba3SJiri Pirko 
302811ce2ba3SJiri Pirko static int rocker_port_ipv4_neigh(struct rocker_port *rocker_port,
302911ce2ba3SJiri Pirko 				  struct switchdev_trans *trans,
303011ce2ba3SJiri Pirko 				  int flags, __be32 ip_addr, const u8 *eth_dst)
303111ce2ba3SJiri Pirko {
303211ce2ba3SJiri Pirko 	struct rocker *rocker = rocker_port->rocker;
303311ce2ba3SJiri Pirko 	struct rocker_neigh_tbl_entry *entry;
303411ce2ba3SJiri Pirko 	struct rocker_neigh_tbl_entry *found;
303511ce2ba3SJiri Pirko 	unsigned long lock_flags;
303611ce2ba3SJiri Pirko 	__be16 eth_type = htons(ETH_P_IP);
303711ce2ba3SJiri Pirko 	enum rocker_of_dpa_table_id goto_tbl =
303811ce2ba3SJiri Pirko 		ROCKER_OF_DPA_TABLE_ID_ACL_POLICY;
303911ce2ba3SJiri Pirko 	u32 group_id;
304011ce2ba3SJiri Pirko 	u32 priority = 0;
304111ce2ba3SJiri Pirko 	bool adding = !(flags & ROCKER_OP_FLAG_REMOVE);
304211ce2ba3SJiri Pirko 	bool updating;
304311ce2ba3SJiri Pirko 	bool removing;
304411ce2ba3SJiri Pirko 	int err = 0;
304511ce2ba3SJiri Pirko 
304611ce2ba3SJiri Pirko 	entry = rocker_kzalloc(trans, flags, sizeof(*entry));
304711ce2ba3SJiri Pirko 	if (!entry)
304811ce2ba3SJiri Pirko 		return -ENOMEM;
304911ce2ba3SJiri Pirko 
305011ce2ba3SJiri Pirko 	spin_lock_irqsave(&rocker->neigh_tbl_lock, lock_flags);
305111ce2ba3SJiri Pirko 
305211ce2ba3SJiri Pirko 	found = rocker_neigh_tbl_find(rocker, ip_addr);
305311ce2ba3SJiri Pirko 
305411ce2ba3SJiri Pirko 	updating = found && adding;
305511ce2ba3SJiri Pirko 	removing = found && !adding;
305611ce2ba3SJiri Pirko 	adding = !found && adding;
305711ce2ba3SJiri Pirko 
305811ce2ba3SJiri Pirko 	if (adding) {
305911ce2ba3SJiri Pirko 		entry->ip_addr = ip_addr;
306011ce2ba3SJiri Pirko 		entry->dev = rocker_port->dev;
306111ce2ba3SJiri Pirko 		ether_addr_copy(entry->eth_dst, eth_dst);
306211ce2ba3SJiri Pirko 		entry->ttl_check = true;
306311ce2ba3SJiri Pirko 		_rocker_neigh_add(rocker, trans, entry);
306411ce2ba3SJiri Pirko 	} else if (removing) {
306511ce2ba3SJiri Pirko 		memcpy(entry, found, sizeof(*entry));
306611ce2ba3SJiri Pirko 		_rocker_neigh_del(trans, found);
306711ce2ba3SJiri Pirko 	} else if (updating) {
306811ce2ba3SJiri Pirko 		_rocker_neigh_update(found, trans, eth_dst, true);
306911ce2ba3SJiri Pirko 		memcpy(entry, found, sizeof(*entry));
307011ce2ba3SJiri Pirko 	} else {
307111ce2ba3SJiri Pirko 		err = -ENOENT;
307211ce2ba3SJiri Pirko 	}
307311ce2ba3SJiri Pirko 
307411ce2ba3SJiri Pirko 	spin_unlock_irqrestore(&rocker->neigh_tbl_lock, lock_flags);
307511ce2ba3SJiri Pirko 
307611ce2ba3SJiri Pirko 	if (err)
307711ce2ba3SJiri Pirko 		goto err_out;
307811ce2ba3SJiri Pirko 
307911ce2ba3SJiri Pirko 	/* For each active neighbor, we have an L3 unicast group and
308011ce2ba3SJiri Pirko 	 * a /32 route to the neighbor, which uses the L3 unicast
308111ce2ba3SJiri Pirko 	 * group.  The L3 unicast group can also be referred to by
308211ce2ba3SJiri Pirko 	 * other routes' nexthops.
308311ce2ba3SJiri Pirko 	 */
308411ce2ba3SJiri Pirko 
308511ce2ba3SJiri Pirko 	err = rocker_group_l3_unicast(rocker_port, trans, flags,
308611ce2ba3SJiri Pirko 				      entry->index,
308711ce2ba3SJiri Pirko 				      rocker_port->dev->dev_addr,
308811ce2ba3SJiri Pirko 				      entry->eth_dst,
308911ce2ba3SJiri Pirko 				      rocker_port->internal_vlan_id,
309011ce2ba3SJiri Pirko 				      entry->ttl_check,
309111ce2ba3SJiri Pirko 				      rocker_port->pport);
309211ce2ba3SJiri Pirko 	if (err) {
309311ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev,
309411ce2ba3SJiri Pirko 			   "Error (%d) L3 unicast group index %d\n",
309511ce2ba3SJiri Pirko 			   err, entry->index);
309611ce2ba3SJiri Pirko 		goto err_out;
309711ce2ba3SJiri Pirko 	}
309811ce2ba3SJiri Pirko 
309911ce2ba3SJiri Pirko 	if (adding || removing) {
310011ce2ba3SJiri Pirko 		group_id = ROCKER_GROUP_L3_UNICAST(entry->index);
310111ce2ba3SJiri Pirko 		err = rocker_flow_tbl_ucast4_routing(rocker_port, trans,
310211ce2ba3SJiri Pirko 						     eth_type, ip_addr,
310311ce2ba3SJiri Pirko 						     inet_make_mask(32),
310411ce2ba3SJiri Pirko 						     priority, goto_tbl,
310511ce2ba3SJiri Pirko 						     group_id, flags);
310611ce2ba3SJiri Pirko 
310711ce2ba3SJiri Pirko 		if (err)
310811ce2ba3SJiri Pirko 			netdev_err(rocker_port->dev,
310911ce2ba3SJiri Pirko 				   "Error (%d) /32 unicast route %pI4 group 0x%08x\n",
311011ce2ba3SJiri Pirko 				   err, &entry->ip_addr, group_id);
311111ce2ba3SJiri Pirko 	}
311211ce2ba3SJiri Pirko 
311311ce2ba3SJiri Pirko err_out:
311411ce2ba3SJiri Pirko 	if (!adding)
311511ce2ba3SJiri Pirko 		rocker_kfree(trans, entry);
311611ce2ba3SJiri Pirko 
311711ce2ba3SJiri Pirko 	return err;
311811ce2ba3SJiri Pirko }
311911ce2ba3SJiri Pirko 
312011ce2ba3SJiri Pirko static int rocker_port_ipv4_resolve(struct rocker_port *rocker_port,
312111ce2ba3SJiri Pirko 				    struct switchdev_trans *trans,
312211ce2ba3SJiri Pirko 				    __be32 ip_addr)
312311ce2ba3SJiri Pirko {
312411ce2ba3SJiri Pirko 	struct net_device *dev = rocker_port->dev;
312511ce2ba3SJiri Pirko 	struct neighbour *n = __ipv4_neigh_lookup(dev, (__force u32)ip_addr);
312611ce2ba3SJiri Pirko 	int err = 0;
312711ce2ba3SJiri Pirko 
312811ce2ba3SJiri Pirko 	if (!n) {
312911ce2ba3SJiri Pirko 		n = neigh_create(&arp_tbl, &ip_addr, dev);
313011ce2ba3SJiri Pirko 		if (IS_ERR(n))
313111ce2ba3SJiri Pirko 			return IS_ERR(n);
313211ce2ba3SJiri Pirko 	}
313311ce2ba3SJiri Pirko 
313411ce2ba3SJiri Pirko 	/* If the neigh is already resolved, then go ahead and
313511ce2ba3SJiri Pirko 	 * install the entry, otherwise start the ARP process to
313611ce2ba3SJiri Pirko 	 * resolve the neigh.
313711ce2ba3SJiri Pirko 	 */
313811ce2ba3SJiri Pirko 
313911ce2ba3SJiri Pirko 	if (n->nud_state & NUD_VALID)
314011ce2ba3SJiri Pirko 		err = rocker_port_ipv4_neigh(rocker_port, trans, 0,
314111ce2ba3SJiri Pirko 					     ip_addr, n->ha);
314211ce2ba3SJiri Pirko 	else
314311ce2ba3SJiri Pirko 		neigh_event_send(n, NULL);
314411ce2ba3SJiri Pirko 
314511ce2ba3SJiri Pirko 	neigh_release(n);
314611ce2ba3SJiri Pirko 	return err;
314711ce2ba3SJiri Pirko }
314811ce2ba3SJiri Pirko 
314911ce2ba3SJiri Pirko static int rocker_port_ipv4_nh(struct rocker_port *rocker_port,
315011ce2ba3SJiri Pirko 			       struct switchdev_trans *trans, int flags,
315111ce2ba3SJiri Pirko 			       __be32 ip_addr, u32 *index)
315211ce2ba3SJiri Pirko {
315311ce2ba3SJiri Pirko 	struct rocker *rocker = rocker_port->rocker;
315411ce2ba3SJiri Pirko 	struct rocker_neigh_tbl_entry *entry;
315511ce2ba3SJiri Pirko 	struct rocker_neigh_tbl_entry *found;
315611ce2ba3SJiri Pirko 	unsigned long lock_flags;
315711ce2ba3SJiri Pirko 	bool adding = !(flags & ROCKER_OP_FLAG_REMOVE);
315811ce2ba3SJiri Pirko 	bool updating;
315911ce2ba3SJiri Pirko 	bool removing;
316011ce2ba3SJiri Pirko 	bool resolved = true;
316111ce2ba3SJiri Pirko 	int err = 0;
316211ce2ba3SJiri Pirko 
316311ce2ba3SJiri Pirko 	entry = rocker_kzalloc(trans, flags, sizeof(*entry));
316411ce2ba3SJiri Pirko 	if (!entry)
316511ce2ba3SJiri Pirko 		return -ENOMEM;
316611ce2ba3SJiri Pirko 
316711ce2ba3SJiri Pirko 	spin_lock_irqsave(&rocker->neigh_tbl_lock, lock_flags);
316811ce2ba3SJiri Pirko 
316911ce2ba3SJiri Pirko 	found = rocker_neigh_tbl_find(rocker, ip_addr);
317011ce2ba3SJiri Pirko 	if (found)
317111ce2ba3SJiri Pirko 		*index = found->index;
317211ce2ba3SJiri Pirko 
317311ce2ba3SJiri Pirko 	updating = found && adding;
317411ce2ba3SJiri Pirko 	removing = found && !adding;
317511ce2ba3SJiri Pirko 	adding = !found && adding;
317611ce2ba3SJiri Pirko 
317711ce2ba3SJiri Pirko 	if (adding) {
317811ce2ba3SJiri Pirko 		entry->ip_addr = ip_addr;
317911ce2ba3SJiri Pirko 		entry->dev = rocker_port->dev;
318011ce2ba3SJiri Pirko 		_rocker_neigh_add(rocker, trans, entry);
318111ce2ba3SJiri Pirko 		*index = entry->index;
318211ce2ba3SJiri Pirko 		resolved = false;
318311ce2ba3SJiri Pirko 	} else if (removing) {
318411ce2ba3SJiri Pirko 		_rocker_neigh_del(trans, found);
318511ce2ba3SJiri Pirko 	} else if (updating) {
318611ce2ba3SJiri Pirko 		_rocker_neigh_update(found, trans, NULL, false);
318711ce2ba3SJiri Pirko 		resolved = !is_zero_ether_addr(found->eth_dst);
318811ce2ba3SJiri Pirko 	} else {
318911ce2ba3SJiri Pirko 		err = -ENOENT;
319011ce2ba3SJiri Pirko 	}
319111ce2ba3SJiri Pirko 
319211ce2ba3SJiri Pirko 	spin_unlock_irqrestore(&rocker->neigh_tbl_lock, lock_flags);
319311ce2ba3SJiri Pirko 
319411ce2ba3SJiri Pirko 	if (!adding)
319511ce2ba3SJiri Pirko 		rocker_kfree(trans, entry);
319611ce2ba3SJiri Pirko 
319711ce2ba3SJiri Pirko 	if (err)
319811ce2ba3SJiri Pirko 		return err;
319911ce2ba3SJiri Pirko 
320011ce2ba3SJiri Pirko 	/* Resolved means neigh ip_addr is resolved to neigh mac. */
320111ce2ba3SJiri Pirko 
320211ce2ba3SJiri Pirko 	if (!resolved)
320311ce2ba3SJiri Pirko 		err = rocker_port_ipv4_resolve(rocker_port, trans, ip_addr);
320411ce2ba3SJiri Pirko 
320511ce2ba3SJiri Pirko 	return err;
320611ce2ba3SJiri Pirko }
320711ce2ba3SJiri Pirko 
320811ce2ba3SJiri Pirko static int rocker_port_vlan_flood_group(struct rocker_port *rocker_port,
320911ce2ba3SJiri Pirko 					struct switchdev_trans *trans,
321011ce2ba3SJiri Pirko 					int flags, __be16 vlan_id)
321111ce2ba3SJiri Pirko {
321211ce2ba3SJiri Pirko 	struct rocker_port *p;
321311ce2ba3SJiri Pirko 	const struct rocker *rocker = rocker_port->rocker;
321411ce2ba3SJiri Pirko 	u32 group_id = ROCKER_GROUP_L2_FLOOD(vlan_id, 0);
321511ce2ba3SJiri Pirko 	u32 *group_ids;
321611ce2ba3SJiri Pirko 	u8 group_count = 0;
321711ce2ba3SJiri Pirko 	int err = 0;
321811ce2ba3SJiri Pirko 	int i;
321911ce2ba3SJiri Pirko 
322011ce2ba3SJiri Pirko 	group_ids = rocker_kcalloc(trans, flags,
322111ce2ba3SJiri Pirko 				   rocker->port_count, sizeof(u32));
322211ce2ba3SJiri Pirko 	if (!group_ids)
322311ce2ba3SJiri Pirko 		return -ENOMEM;
322411ce2ba3SJiri Pirko 
322511ce2ba3SJiri Pirko 	/* Adjust the flood group for this VLAN.  The flood group
322611ce2ba3SJiri Pirko 	 * references an L2 interface group for each port in this
322711ce2ba3SJiri Pirko 	 * VLAN.
322811ce2ba3SJiri Pirko 	 */
322911ce2ba3SJiri Pirko 
323011ce2ba3SJiri Pirko 	for (i = 0; i < rocker->port_count; i++) {
323111ce2ba3SJiri Pirko 		p = rocker->ports[i];
323211ce2ba3SJiri Pirko 		if (!p)
323311ce2ba3SJiri Pirko 			continue;
323411ce2ba3SJiri Pirko 		if (!rocker_port_is_bridged(p))
323511ce2ba3SJiri Pirko 			continue;
323611ce2ba3SJiri Pirko 		if (test_bit(ntohs(vlan_id), p->vlan_bitmap)) {
323711ce2ba3SJiri Pirko 			group_ids[group_count++] =
323811ce2ba3SJiri Pirko 				ROCKER_GROUP_L2_INTERFACE(vlan_id, p->pport);
323911ce2ba3SJiri Pirko 		}
324011ce2ba3SJiri Pirko 	}
324111ce2ba3SJiri Pirko 
324211ce2ba3SJiri Pirko 	/* If there are no bridged ports in this VLAN, we're done */
324311ce2ba3SJiri Pirko 	if (group_count == 0)
324411ce2ba3SJiri Pirko 		goto no_ports_in_vlan;
324511ce2ba3SJiri Pirko 
324611ce2ba3SJiri Pirko 	err = rocker_group_l2_flood(rocker_port, trans, flags, vlan_id,
324711ce2ba3SJiri Pirko 				    group_count, group_ids, group_id);
324811ce2ba3SJiri Pirko 	if (err)
324911ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev,
325011ce2ba3SJiri Pirko 			   "Error (%d) port VLAN l2 flood group\n", err);
325111ce2ba3SJiri Pirko 
325211ce2ba3SJiri Pirko no_ports_in_vlan:
325311ce2ba3SJiri Pirko 	rocker_kfree(trans, group_ids);
325411ce2ba3SJiri Pirko 	return err;
325511ce2ba3SJiri Pirko }
325611ce2ba3SJiri Pirko 
325711ce2ba3SJiri Pirko static int rocker_port_vlan_l2_groups(struct rocker_port *rocker_port,
325811ce2ba3SJiri Pirko 				      struct switchdev_trans *trans, int flags,
325911ce2ba3SJiri Pirko 				      __be16 vlan_id, bool pop_vlan)
326011ce2ba3SJiri Pirko {
326111ce2ba3SJiri Pirko 	const struct rocker *rocker = rocker_port->rocker;
326211ce2ba3SJiri Pirko 	struct rocker_port *p;
326311ce2ba3SJiri Pirko 	bool adding = !(flags & ROCKER_OP_FLAG_REMOVE);
326411ce2ba3SJiri Pirko 	u32 out_pport;
326511ce2ba3SJiri Pirko 	int ref = 0;
326611ce2ba3SJiri Pirko 	int err;
326711ce2ba3SJiri Pirko 	int i;
326811ce2ba3SJiri Pirko 
326911ce2ba3SJiri Pirko 	/* An L2 interface group for this port in this VLAN, but
327011ce2ba3SJiri Pirko 	 * only when port STP state is LEARNING|FORWARDING.
327111ce2ba3SJiri Pirko 	 */
327211ce2ba3SJiri Pirko 
327311ce2ba3SJiri Pirko 	if (rocker_port->stp_state == BR_STATE_LEARNING ||
327411ce2ba3SJiri Pirko 	    rocker_port->stp_state == BR_STATE_FORWARDING) {
327511ce2ba3SJiri Pirko 		out_pport = rocker_port->pport;
327611ce2ba3SJiri Pirko 		err = rocker_group_l2_interface(rocker_port, trans, flags,
327711ce2ba3SJiri Pirko 						vlan_id, out_pport, pop_vlan);
327811ce2ba3SJiri Pirko 		if (err) {
327911ce2ba3SJiri Pirko 			netdev_err(rocker_port->dev,
328011ce2ba3SJiri Pirko 				   "Error (%d) port VLAN l2 group for pport %d\n",
328111ce2ba3SJiri Pirko 				   err, out_pport);
328211ce2ba3SJiri Pirko 			return err;
328311ce2ba3SJiri Pirko 		}
328411ce2ba3SJiri Pirko 	}
328511ce2ba3SJiri Pirko 
328611ce2ba3SJiri Pirko 	/* An L2 interface group for this VLAN to CPU port.
328711ce2ba3SJiri Pirko 	 * Add when first port joins this VLAN and destroy when
328811ce2ba3SJiri Pirko 	 * last port leaves this VLAN.
328911ce2ba3SJiri Pirko 	 */
329011ce2ba3SJiri Pirko 
329111ce2ba3SJiri Pirko 	for (i = 0; i < rocker->port_count; i++) {
329211ce2ba3SJiri Pirko 		p = rocker->ports[i];
329311ce2ba3SJiri Pirko 		if (p && test_bit(ntohs(vlan_id), p->vlan_bitmap))
329411ce2ba3SJiri Pirko 			ref++;
329511ce2ba3SJiri Pirko 	}
329611ce2ba3SJiri Pirko 
329711ce2ba3SJiri Pirko 	if ((!adding || ref != 1) && (adding || ref != 0))
329811ce2ba3SJiri Pirko 		return 0;
329911ce2ba3SJiri Pirko 
330011ce2ba3SJiri Pirko 	out_pport = 0;
330111ce2ba3SJiri Pirko 	err = rocker_group_l2_interface(rocker_port, trans, flags,
330211ce2ba3SJiri Pirko 					vlan_id, out_pport, pop_vlan);
330311ce2ba3SJiri Pirko 	if (err) {
330411ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev,
330511ce2ba3SJiri Pirko 			   "Error (%d) port VLAN l2 group for CPU port\n", err);
330611ce2ba3SJiri Pirko 		return err;
330711ce2ba3SJiri Pirko 	}
330811ce2ba3SJiri Pirko 
330911ce2ba3SJiri Pirko 	return 0;
331011ce2ba3SJiri Pirko }
331111ce2ba3SJiri Pirko 
331211ce2ba3SJiri Pirko static struct rocker_ctrl {
331311ce2ba3SJiri Pirko 	const u8 *eth_dst;
331411ce2ba3SJiri Pirko 	const u8 *eth_dst_mask;
331511ce2ba3SJiri Pirko 	__be16 eth_type;
331611ce2ba3SJiri Pirko 	bool acl;
331711ce2ba3SJiri Pirko 	bool bridge;
331811ce2ba3SJiri Pirko 	bool term;
331911ce2ba3SJiri Pirko 	bool copy_to_cpu;
332011ce2ba3SJiri Pirko } rocker_ctrls[] = {
332111ce2ba3SJiri Pirko 	[ROCKER_CTRL_LINK_LOCAL_MCAST] = {
332211ce2ba3SJiri Pirko 		/* pass link local multicast pkts up to CPU for filtering */
332311ce2ba3SJiri Pirko 		.eth_dst = ll_mac,
332411ce2ba3SJiri Pirko 		.eth_dst_mask = ll_mask,
332511ce2ba3SJiri Pirko 		.acl = true,
332611ce2ba3SJiri Pirko 	},
332711ce2ba3SJiri Pirko 	[ROCKER_CTRL_LOCAL_ARP] = {
332811ce2ba3SJiri Pirko 		/* pass local ARP pkts up to CPU */
332911ce2ba3SJiri Pirko 		.eth_dst = zero_mac,
333011ce2ba3SJiri Pirko 		.eth_dst_mask = zero_mac,
333111ce2ba3SJiri Pirko 		.eth_type = htons(ETH_P_ARP),
333211ce2ba3SJiri Pirko 		.acl = true,
333311ce2ba3SJiri Pirko 	},
333411ce2ba3SJiri Pirko 	[ROCKER_CTRL_IPV4_MCAST] = {
333511ce2ba3SJiri Pirko 		/* pass IPv4 mcast pkts up to CPU, RFC 1112 */
333611ce2ba3SJiri Pirko 		.eth_dst = ipv4_mcast,
333711ce2ba3SJiri Pirko 		.eth_dst_mask = ipv4_mask,
333811ce2ba3SJiri Pirko 		.eth_type = htons(ETH_P_IP),
333911ce2ba3SJiri Pirko 		.term  = true,
334011ce2ba3SJiri Pirko 		.copy_to_cpu = true,
334111ce2ba3SJiri Pirko 	},
334211ce2ba3SJiri Pirko 	[ROCKER_CTRL_IPV6_MCAST] = {
334311ce2ba3SJiri Pirko 		/* pass IPv6 mcast pkts up to CPU, RFC 2464 */
334411ce2ba3SJiri Pirko 		.eth_dst = ipv6_mcast,
334511ce2ba3SJiri Pirko 		.eth_dst_mask = ipv6_mask,
334611ce2ba3SJiri Pirko 		.eth_type = htons(ETH_P_IPV6),
334711ce2ba3SJiri Pirko 		.term  = true,
334811ce2ba3SJiri Pirko 		.copy_to_cpu = true,
334911ce2ba3SJiri Pirko 	},
335011ce2ba3SJiri Pirko 	[ROCKER_CTRL_DFLT_BRIDGING] = {
335111ce2ba3SJiri Pirko 		/* flood any pkts on vlan */
335211ce2ba3SJiri Pirko 		.bridge = true,
335311ce2ba3SJiri Pirko 		.copy_to_cpu = true,
335411ce2ba3SJiri Pirko 	},
335511ce2ba3SJiri Pirko 	[ROCKER_CTRL_DFLT_OVS] = {
335611ce2ba3SJiri Pirko 		/* pass all pkts up to CPU */
335711ce2ba3SJiri Pirko 		.eth_dst = zero_mac,
335811ce2ba3SJiri Pirko 		.eth_dst_mask = zero_mac,
335911ce2ba3SJiri Pirko 		.acl = true,
336011ce2ba3SJiri Pirko 	},
336111ce2ba3SJiri Pirko };
336211ce2ba3SJiri Pirko 
336311ce2ba3SJiri Pirko static int rocker_port_ctrl_vlan_acl(struct rocker_port *rocker_port,
336411ce2ba3SJiri Pirko 				     struct switchdev_trans *trans, int flags,
336511ce2ba3SJiri Pirko 				     const struct rocker_ctrl *ctrl, __be16 vlan_id)
336611ce2ba3SJiri Pirko {
336711ce2ba3SJiri Pirko 	u32 in_pport = rocker_port->pport;
336811ce2ba3SJiri Pirko 	u32 in_pport_mask = 0xffffffff;
336911ce2ba3SJiri Pirko 	u32 out_pport = 0;
337011ce2ba3SJiri Pirko 	const u8 *eth_src = NULL;
337111ce2ba3SJiri Pirko 	const u8 *eth_src_mask = NULL;
337211ce2ba3SJiri Pirko 	__be16 vlan_id_mask = htons(0xffff);
337311ce2ba3SJiri Pirko 	u8 ip_proto = 0;
337411ce2ba3SJiri Pirko 	u8 ip_proto_mask = 0;
337511ce2ba3SJiri Pirko 	u8 ip_tos = 0;
337611ce2ba3SJiri Pirko 	u8 ip_tos_mask = 0;
337711ce2ba3SJiri Pirko 	u32 group_id = ROCKER_GROUP_L2_INTERFACE(vlan_id, out_pport);
337811ce2ba3SJiri Pirko 	int err;
337911ce2ba3SJiri Pirko 
338011ce2ba3SJiri Pirko 	err = rocker_flow_tbl_acl(rocker_port, trans, flags,
338111ce2ba3SJiri Pirko 				  in_pport, in_pport_mask,
338211ce2ba3SJiri Pirko 				  eth_src, eth_src_mask,
338311ce2ba3SJiri Pirko 				  ctrl->eth_dst, ctrl->eth_dst_mask,
338411ce2ba3SJiri Pirko 				  ctrl->eth_type,
338511ce2ba3SJiri Pirko 				  vlan_id, vlan_id_mask,
338611ce2ba3SJiri Pirko 				  ip_proto, ip_proto_mask,
338711ce2ba3SJiri Pirko 				  ip_tos, ip_tos_mask,
338811ce2ba3SJiri Pirko 				  group_id);
338911ce2ba3SJiri Pirko 
339011ce2ba3SJiri Pirko 	if (err)
339111ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev, "Error (%d) ctrl ACL\n", err);
339211ce2ba3SJiri Pirko 
339311ce2ba3SJiri Pirko 	return err;
339411ce2ba3SJiri Pirko }
339511ce2ba3SJiri Pirko 
339611ce2ba3SJiri Pirko static int rocker_port_ctrl_vlan_bridge(struct rocker_port *rocker_port,
339711ce2ba3SJiri Pirko 					struct switchdev_trans *trans,
339811ce2ba3SJiri Pirko 					int flags,
339911ce2ba3SJiri Pirko 					const struct rocker_ctrl *ctrl,
340011ce2ba3SJiri Pirko 					__be16 vlan_id)
340111ce2ba3SJiri Pirko {
340211ce2ba3SJiri Pirko 	enum rocker_of_dpa_table_id goto_tbl =
340311ce2ba3SJiri Pirko 		ROCKER_OF_DPA_TABLE_ID_ACL_POLICY;
340411ce2ba3SJiri Pirko 	u32 group_id = ROCKER_GROUP_L2_FLOOD(vlan_id, 0);
340511ce2ba3SJiri Pirko 	u32 tunnel_id = 0;
340611ce2ba3SJiri Pirko 	int err;
340711ce2ba3SJiri Pirko 
340811ce2ba3SJiri Pirko 	if (!rocker_port_is_bridged(rocker_port))
340911ce2ba3SJiri Pirko 		return 0;
341011ce2ba3SJiri Pirko 
341111ce2ba3SJiri Pirko 	err = rocker_flow_tbl_bridge(rocker_port, trans, flags,
341211ce2ba3SJiri Pirko 				     ctrl->eth_dst, ctrl->eth_dst_mask,
341311ce2ba3SJiri Pirko 				     vlan_id, tunnel_id,
341411ce2ba3SJiri Pirko 				     goto_tbl, group_id, ctrl->copy_to_cpu);
341511ce2ba3SJiri Pirko 
341611ce2ba3SJiri Pirko 	if (err)
341711ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev, "Error (%d) ctrl FLOOD\n", err);
341811ce2ba3SJiri Pirko 
341911ce2ba3SJiri Pirko 	return err;
342011ce2ba3SJiri Pirko }
342111ce2ba3SJiri Pirko 
342211ce2ba3SJiri Pirko static int rocker_port_ctrl_vlan_term(struct rocker_port *rocker_port,
342311ce2ba3SJiri Pirko 				      struct switchdev_trans *trans, int flags,
342411ce2ba3SJiri Pirko 				      const struct rocker_ctrl *ctrl, __be16 vlan_id)
342511ce2ba3SJiri Pirko {
342611ce2ba3SJiri Pirko 	u32 in_pport_mask = 0xffffffff;
342711ce2ba3SJiri Pirko 	__be16 vlan_id_mask = htons(0xffff);
342811ce2ba3SJiri Pirko 	int err;
342911ce2ba3SJiri Pirko 
343011ce2ba3SJiri Pirko 	if (ntohs(vlan_id) == 0)
343111ce2ba3SJiri Pirko 		vlan_id = rocker_port->internal_vlan_id;
343211ce2ba3SJiri Pirko 
343311ce2ba3SJiri Pirko 	err = rocker_flow_tbl_term_mac(rocker_port, trans,
343411ce2ba3SJiri Pirko 				       rocker_port->pport, in_pport_mask,
343511ce2ba3SJiri Pirko 				       ctrl->eth_type, ctrl->eth_dst,
343611ce2ba3SJiri Pirko 				       ctrl->eth_dst_mask, vlan_id,
343711ce2ba3SJiri Pirko 				       vlan_id_mask, ctrl->copy_to_cpu,
343811ce2ba3SJiri Pirko 				       flags);
343911ce2ba3SJiri Pirko 
344011ce2ba3SJiri Pirko 	if (err)
344111ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev, "Error (%d) ctrl term\n", err);
344211ce2ba3SJiri Pirko 
344311ce2ba3SJiri Pirko 	return err;
344411ce2ba3SJiri Pirko }
344511ce2ba3SJiri Pirko 
344611ce2ba3SJiri Pirko static int rocker_port_ctrl_vlan(struct rocker_port *rocker_port,
344711ce2ba3SJiri Pirko 				 struct switchdev_trans *trans, int flags,
344811ce2ba3SJiri Pirko 				 const struct rocker_ctrl *ctrl, __be16 vlan_id)
344911ce2ba3SJiri Pirko {
345011ce2ba3SJiri Pirko 	if (ctrl->acl)
345111ce2ba3SJiri Pirko 		return rocker_port_ctrl_vlan_acl(rocker_port, trans, flags,
345211ce2ba3SJiri Pirko 						 ctrl, vlan_id);
345311ce2ba3SJiri Pirko 	if (ctrl->bridge)
345411ce2ba3SJiri Pirko 		return rocker_port_ctrl_vlan_bridge(rocker_port, trans, flags,
345511ce2ba3SJiri Pirko 						    ctrl, vlan_id);
345611ce2ba3SJiri Pirko 
345711ce2ba3SJiri Pirko 	if (ctrl->term)
345811ce2ba3SJiri Pirko 		return rocker_port_ctrl_vlan_term(rocker_port, trans, flags,
345911ce2ba3SJiri Pirko 						  ctrl, vlan_id);
346011ce2ba3SJiri Pirko 
346111ce2ba3SJiri Pirko 	return -EOPNOTSUPP;
346211ce2ba3SJiri Pirko }
346311ce2ba3SJiri Pirko 
346411ce2ba3SJiri Pirko static int rocker_port_ctrl_vlan_add(struct rocker_port *rocker_port,
346511ce2ba3SJiri Pirko 				     struct switchdev_trans *trans, int flags,
346611ce2ba3SJiri Pirko 				     __be16 vlan_id)
346711ce2ba3SJiri Pirko {
346811ce2ba3SJiri Pirko 	int err = 0;
346911ce2ba3SJiri Pirko 	int i;
347011ce2ba3SJiri Pirko 
347111ce2ba3SJiri Pirko 	for (i = 0; i < ROCKER_CTRL_MAX; i++) {
347211ce2ba3SJiri Pirko 		if (rocker_port->ctrls[i]) {
347311ce2ba3SJiri Pirko 			err = rocker_port_ctrl_vlan(rocker_port, trans, flags,
347411ce2ba3SJiri Pirko 						    &rocker_ctrls[i], vlan_id);
347511ce2ba3SJiri Pirko 			if (err)
347611ce2ba3SJiri Pirko 				return err;
347711ce2ba3SJiri Pirko 		}
347811ce2ba3SJiri Pirko 	}
347911ce2ba3SJiri Pirko 
348011ce2ba3SJiri Pirko 	return err;
348111ce2ba3SJiri Pirko }
348211ce2ba3SJiri Pirko 
348311ce2ba3SJiri Pirko static int rocker_port_ctrl(struct rocker_port *rocker_port,
348411ce2ba3SJiri Pirko 			    struct switchdev_trans *trans, int flags,
348511ce2ba3SJiri Pirko 			    const struct rocker_ctrl *ctrl)
348611ce2ba3SJiri Pirko {
348711ce2ba3SJiri Pirko 	u16 vid;
348811ce2ba3SJiri Pirko 	int err = 0;
348911ce2ba3SJiri Pirko 
349011ce2ba3SJiri Pirko 	for (vid = 1; vid < VLAN_N_VID; vid++) {
349111ce2ba3SJiri Pirko 		if (!test_bit(vid, rocker_port->vlan_bitmap))
349211ce2ba3SJiri Pirko 			continue;
349311ce2ba3SJiri Pirko 		err = rocker_port_ctrl_vlan(rocker_port, trans, flags,
349411ce2ba3SJiri Pirko 					    ctrl, htons(vid));
349511ce2ba3SJiri Pirko 		if (err)
349611ce2ba3SJiri Pirko 			break;
349711ce2ba3SJiri Pirko 	}
349811ce2ba3SJiri Pirko 
349911ce2ba3SJiri Pirko 	return err;
350011ce2ba3SJiri Pirko }
350111ce2ba3SJiri Pirko 
350211ce2ba3SJiri Pirko static int rocker_port_vlan(struct rocker_port *rocker_port,
350311ce2ba3SJiri Pirko 			    struct switchdev_trans *trans, int flags, u16 vid)
350411ce2ba3SJiri Pirko {
350511ce2ba3SJiri Pirko 	enum rocker_of_dpa_table_id goto_tbl =
350611ce2ba3SJiri Pirko 		ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC;
350711ce2ba3SJiri Pirko 	u32 in_pport = rocker_port->pport;
350811ce2ba3SJiri Pirko 	__be16 vlan_id = htons(vid);
350911ce2ba3SJiri Pirko 	__be16 vlan_id_mask = htons(0xffff);
351011ce2ba3SJiri Pirko 	__be16 internal_vlan_id;
351111ce2ba3SJiri Pirko 	bool untagged;
351211ce2ba3SJiri Pirko 	bool adding = !(flags & ROCKER_OP_FLAG_REMOVE);
351311ce2ba3SJiri Pirko 	int err;
351411ce2ba3SJiri Pirko 
351511ce2ba3SJiri Pirko 	internal_vlan_id = rocker_port_vid_to_vlan(rocker_port, vid, &untagged);
351611ce2ba3SJiri Pirko 
351711ce2ba3SJiri Pirko 	if (adding && test_bit(ntohs(internal_vlan_id),
351811ce2ba3SJiri Pirko 			       rocker_port->vlan_bitmap))
351911ce2ba3SJiri Pirko 		return 0; /* already added */
352011ce2ba3SJiri Pirko 	else if (!adding && !test_bit(ntohs(internal_vlan_id),
352111ce2ba3SJiri Pirko 				      rocker_port->vlan_bitmap))
352211ce2ba3SJiri Pirko 		return 0; /* already removed */
352311ce2ba3SJiri Pirko 
352411ce2ba3SJiri Pirko 	change_bit(ntohs(internal_vlan_id), rocker_port->vlan_bitmap);
352511ce2ba3SJiri Pirko 
352611ce2ba3SJiri Pirko 	if (adding) {
352711ce2ba3SJiri Pirko 		err = rocker_port_ctrl_vlan_add(rocker_port, trans, flags,
352811ce2ba3SJiri Pirko 						internal_vlan_id);
352911ce2ba3SJiri Pirko 		if (err) {
353011ce2ba3SJiri Pirko 			netdev_err(rocker_port->dev,
353111ce2ba3SJiri Pirko 				   "Error (%d) port ctrl vlan add\n", err);
353211ce2ba3SJiri Pirko 			goto err_out;
353311ce2ba3SJiri Pirko 		}
353411ce2ba3SJiri Pirko 	}
353511ce2ba3SJiri Pirko 
353611ce2ba3SJiri Pirko 	err = rocker_port_vlan_l2_groups(rocker_port, trans, flags,
353711ce2ba3SJiri Pirko 					 internal_vlan_id, untagged);
353811ce2ba3SJiri Pirko 	if (err) {
353911ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev,
354011ce2ba3SJiri Pirko 			   "Error (%d) port VLAN l2 groups\n", err);
354111ce2ba3SJiri Pirko 		goto err_out;
354211ce2ba3SJiri Pirko 	}
354311ce2ba3SJiri Pirko 
354411ce2ba3SJiri Pirko 	err = rocker_port_vlan_flood_group(rocker_port, trans, flags,
354511ce2ba3SJiri Pirko 					   internal_vlan_id);
354611ce2ba3SJiri Pirko 	if (err) {
354711ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev,
354811ce2ba3SJiri Pirko 			   "Error (%d) port VLAN l2 flood group\n", err);
354911ce2ba3SJiri Pirko 		goto err_out;
355011ce2ba3SJiri Pirko 	}
355111ce2ba3SJiri Pirko 
355211ce2ba3SJiri Pirko 	err = rocker_flow_tbl_vlan(rocker_port, trans, flags,
355311ce2ba3SJiri Pirko 				   in_pport, vlan_id, vlan_id_mask,
355411ce2ba3SJiri Pirko 				   goto_tbl, untagged, internal_vlan_id);
355511ce2ba3SJiri Pirko 	if (err)
355611ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev,
355711ce2ba3SJiri Pirko 			   "Error (%d) port VLAN table\n", err);
355811ce2ba3SJiri Pirko 
355911ce2ba3SJiri Pirko err_out:
356011ce2ba3SJiri Pirko 	if (switchdev_trans_ph_prepare(trans))
356111ce2ba3SJiri Pirko 		change_bit(ntohs(internal_vlan_id), rocker_port->vlan_bitmap);
356211ce2ba3SJiri Pirko 
356311ce2ba3SJiri Pirko 	return err;
356411ce2ba3SJiri Pirko }
356511ce2ba3SJiri Pirko 
356611ce2ba3SJiri Pirko static int rocker_port_ig_tbl(struct rocker_port *rocker_port,
356711ce2ba3SJiri Pirko 			      struct switchdev_trans *trans, int flags)
356811ce2ba3SJiri Pirko {
356911ce2ba3SJiri Pirko 	enum rocker_of_dpa_table_id goto_tbl;
357011ce2ba3SJiri Pirko 	u32 in_pport;
357111ce2ba3SJiri Pirko 	u32 in_pport_mask;
357211ce2ba3SJiri Pirko 	int err;
357311ce2ba3SJiri Pirko 
357411ce2ba3SJiri Pirko 	/* Normal Ethernet Frames.  Matches pkts from any local physical
357511ce2ba3SJiri Pirko 	 * ports.  Goto VLAN tbl.
357611ce2ba3SJiri Pirko 	 */
357711ce2ba3SJiri Pirko 
357811ce2ba3SJiri Pirko 	in_pport = 0;
357911ce2ba3SJiri Pirko 	in_pport_mask = 0xffff0000;
358011ce2ba3SJiri Pirko 	goto_tbl = ROCKER_OF_DPA_TABLE_ID_VLAN;
358111ce2ba3SJiri Pirko 
358211ce2ba3SJiri Pirko 	err = rocker_flow_tbl_ig_port(rocker_port, trans, flags,
358311ce2ba3SJiri Pirko 				      in_pport, in_pport_mask,
358411ce2ba3SJiri Pirko 				      goto_tbl);
358511ce2ba3SJiri Pirko 	if (err)
358611ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev,
358711ce2ba3SJiri Pirko 			   "Error (%d) ingress port table entry\n", err);
358811ce2ba3SJiri Pirko 
358911ce2ba3SJiri Pirko 	return err;
359011ce2ba3SJiri Pirko }
359111ce2ba3SJiri Pirko 
359211ce2ba3SJiri Pirko struct rocker_fdb_learn_work {
359311ce2ba3SJiri Pirko 	struct work_struct work;
359411ce2ba3SJiri Pirko 	struct rocker_port *rocker_port;
359511ce2ba3SJiri Pirko 	struct switchdev_trans *trans;
359611ce2ba3SJiri Pirko 	int flags;
359711ce2ba3SJiri Pirko 	u8 addr[ETH_ALEN];
359811ce2ba3SJiri Pirko 	u16 vid;
359911ce2ba3SJiri Pirko };
360011ce2ba3SJiri Pirko 
360111ce2ba3SJiri Pirko static void rocker_port_fdb_learn_work(struct work_struct *work)
360211ce2ba3SJiri Pirko {
360311ce2ba3SJiri Pirko 	const struct rocker_fdb_learn_work *lw =
360411ce2ba3SJiri Pirko 		container_of(work, struct rocker_fdb_learn_work, work);
360511ce2ba3SJiri Pirko 	bool removing = (lw->flags & ROCKER_OP_FLAG_REMOVE);
360611ce2ba3SJiri Pirko 	bool learned = (lw->flags & ROCKER_OP_FLAG_LEARNED);
360711ce2ba3SJiri Pirko 	struct switchdev_notifier_fdb_info info;
360811ce2ba3SJiri Pirko 
360911ce2ba3SJiri Pirko 	info.addr = lw->addr;
361011ce2ba3SJiri Pirko 	info.vid = lw->vid;
361111ce2ba3SJiri Pirko 
361211ce2ba3SJiri Pirko 	rtnl_lock();
361311ce2ba3SJiri Pirko 	if (learned && removing)
361411ce2ba3SJiri Pirko 		call_switchdev_notifiers(SWITCHDEV_FDB_DEL,
361511ce2ba3SJiri Pirko 					 lw->rocker_port->dev, &info.info);
361611ce2ba3SJiri Pirko 	else if (learned && !removing)
361711ce2ba3SJiri Pirko 		call_switchdev_notifiers(SWITCHDEV_FDB_ADD,
361811ce2ba3SJiri Pirko 					 lw->rocker_port->dev, &info.info);
361911ce2ba3SJiri Pirko 	rtnl_unlock();
362011ce2ba3SJiri Pirko 
362111ce2ba3SJiri Pirko 	rocker_kfree(lw->trans, work);
362211ce2ba3SJiri Pirko }
362311ce2ba3SJiri Pirko 
362411ce2ba3SJiri Pirko static int rocker_port_fdb_learn(struct rocker_port *rocker_port,
362511ce2ba3SJiri Pirko 				 struct switchdev_trans *trans, int flags,
362611ce2ba3SJiri Pirko 				 const u8 *addr, __be16 vlan_id)
362711ce2ba3SJiri Pirko {
362811ce2ba3SJiri Pirko 	struct rocker_fdb_learn_work *lw;
362911ce2ba3SJiri Pirko 	enum rocker_of_dpa_table_id goto_tbl =
363011ce2ba3SJiri Pirko 		ROCKER_OF_DPA_TABLE_ID_ACL_POLICY;
363111ce2ba3SJiri Pirko 	u32 out_pport = rocker_port->pport;
363211ce2ba3SJiri Pirko 	u32 tunnel_id = 0;
363311ce2ba3SJiri Pirko 	u32 group_id = ROCKER_GROUP_NONE;
363411ce2ba3SJiri Pirko 	bool syncing = !!(rocker_port->brport_flags & BR_LEARNING_SYNC);
363511ce2ba3SJiri Pirko 	bool copy_to_cpu = false;
363611ce2ba3SJiri Pirko 	int err;
363711ce2ba3SJiri Pirko 
363811ce2ba3SJiri Pirko 	if (rocker_port_is_bridged(rocker_port))
363911ce2ba3SJiri Pirko 		group_id = ROCKER_GROUP_L2_INTERFACE(vlan_id, out_pport);
364011ce2ba3SJiri Pirko 
364111ce2ba3SJiri Pirko 	if (!(flags & ROCKER_OP_FLAG_REFRESH)) {
364211ce2ba3SJiri Pirko 		err = rocker_flow_tbl_bridge(rocker_port, trans, flags, addr,
364311ce2ba3SJiri Pirko 					     NULL, vlan_id, tunnel_id, goto_tbl,
364411ce2ba3SJiri Pirko 					     group_id, copy_to_cpu);
364511ce2ba3SJiri Pirko 		if (err)
364611ce2ba3SJiri Pirko 			return err;
364711ce2ba3SJiri Pirko 	}
364811ce2ba3SJiri Pirko 
364911ce2ba3SJiri Pirko 	if (!syncing)
365011ce2ba3SJiri Pirko 		return 0;
365111ce2ba3SJiri Pirko 
365211ce2ba3SJiri Pirko 	if (!rocker_port_is_bridged(rocker_port))
365311ce2ba3SJiri Pirko 		return 0;
365411ce2ba3SJiri Pirko 
365511ce2ba3SJiri Pirko 	lw = rocker_kzalloc(trans, flags, sizeof(*lw));
365611ce2ba3SJiri Pirko 	if (!lw)
365711ce2ba3SJiri Pirko 		return -ENOMEM;
365811ce2ba3SJiri Pirko 
365911ce2ba3SJiri Pirko 	INIT_WORK(&lw->work, rocker_port_fdb_learn_work);
366011ce2ba3SJiri Pirko 
366111ce2ba3SJiri Pirko 	lw->rocker_port = rocker_port;
366211ce2ba3SJiri Pirko 	lw->trans = trans;
366311ce2ba3SJiri Pirko 	lw->flags = flags;
366411ce2ba3SJiri Pirko 	ether_addr_copy(lw->addr, addr);
366511ce2ba3SJiri Pirko 	lw->vid = rocker_port_vlan_to_vid(rocker_port, vlan_id);
366611ce2ba3SJiri Pirko 
366711ce2ba3SJiri Pirko 	if (switchdev_trans_ph_prepare(trans))
366811ce2ba3SJiri Pirko 		rocker_kfree(trans, lw);
366911ce2ba3SJiri Pirko 	else
367011ce2ba3SJiri Pirko 		schedule_work(&lw->work);
367111ce2ba3SJiri Pirko 
367211ce2ba3SJiri Pirko 	return 0;
367311ce2ba3SJiri Pirko }
367411ce2ba3SJiri Pirko 
367511ce2ba3SJiri Pirko static struct rocker_fdb_tbl_entry *
367611ce2ba3SJiri Pirko rocker_fdb_tbl_find(const struct rocker *rocker,
367711ce2ba3SJiri Pirko 		    const struct rocker_fdb_tbl_entry *match)
367811ce2ba3SJiri Pirko {
367911ce2ba3SJiri Pirko 	struct rocker_fdb_tbl_entry *found;
368011ce2ba3SJiri Pirko 
368111ce2ba3SJiri Pirko 	hash_for_each_possible(rocker->fdb_tbl, found, entry, match->key_crc32)
368211ce2ba3SJiri Pirko 		if (memcmp(&found->key, &match->key, sizeof(found->key)) == 0)
368311ce2ba3SJiri Pirko 			return found;
368411ce2ba3SJiri Pirko 
368511ce2ba3SJiri Pirko 	return NULL;
368611ce2ba3SJiri Pirko }
368711ce2ba3SJiri Pirko 
368811ce2ba3SJiri Pirko static int rocker_port_fdb(struct rocker_port *rocker_port,
368911ce2ba3SJiri Pirko 			   struct switchdev_trans *trans,
369011ce2ba3SJiri Pirko 			   const unsigned char *addr,
369111ce2ba3SJiri Pirko 			   __be16 vlan_id, int flags)
369211ce2ba3SJiri Pirko {
369311ce2ba3SJiri Pirko 	struct rocker *rocker = rocker_port->rocker;
369411ce2ba3SJiri Pirko 	struct rocker_fdb_tbl_entry *fdb;
369511ce2ba3SJiri Pirko 	struct rocker_fdb_tbl_entry *found;
369611ce2ba3SJiri Pirko 	bool removing = (flags & ROCKER_OP_FLAG_REMOVE);
369711ce2ba3SJiri Pirko 	unsigned long lock_flags;
369811ce2ba3SJiri Pirko 
369911ce2ba3SJiri Pirko 	fdb = rocker_kzalloc(trans, flags, sizeof(*fdb));
370011ce2ba3SJiri Pirko 	if (!fdb)
370111ce2ba3SJiri Pirko 		return -ENOMEM;
370211ce2ba3SJiri Pirko 
370311ce2ba3SJiri Pirko 	fdb->learned = (flags & ROCKER_OP_FLAG_LEARNED);
370411ce2ba3SJiri Pirko 	fdb->touched = jiffies;
370511ce2ba3SJiri Pirko 	fdb->key.rocker_port = rocker_port;
370611ce2ba3SJiri Pirko 	ether_addr_copy(fdb->key.addr, addr);
370711ce2ba3SJiri Pirko 	fdb->key.vlan_id = vlan_id;
370811ce2ba3SJiri Pirko 	fdb->key_crc32 = crc32(~0, &fdb->key, sizeof(fdb->key));
370911ce2ba3SJiri Pirko 
371011ce2ba3SJiri Pirko 	spin_lock_irqsave(&rocker->fdb_tbl_lock, lock_flags);
371111ce2ba3SJiri Pirko 
371211ce2ba3SJiri Pirko 	found = rocker_fdb_tbl_find(rocker, fdb);
371311ce2ba3SJiri Pirko 
371411ce2ba3SJiri Pirko 	if (found) {
371511ce2ba3SJiri Pirko 		found->touched = jiffies;
371611ce2ba3SJiri Pirko 		if (removing) {
371711ce2ba3SJiri Pirko 			rocker_kfree(trans, fdb);
371811ce2ba3SJiri Pirko 			if (!switchdev_trans_ph_prepare(trans))
371911ce2ba3SJiri Pirko 				hash_del(&found->entry);
372011ce2ba3SJiri Pirko 		}
372111ce2ba3SJiri Pirko 	} else if (!removing) {
372211ce2ba3SJiri Pirko 		if (!switchdev_trans_ph_prepare(trans))
372311ce2ba3SJiri Pirko 			hash_add(rocker->fdb_tbl, &fdb->entry,
372411ce2ba3SJiri Pirko 				 fdb->key_crc32);
372511ce2ba3SJiri Pirko 	}
372611ce2ba3SJiri Pirko 
372711ce2ba3SJiri Pirko 	spin_unlock_irqrestore(&rocker->fdb_tbl_lock, lock_flags);
372811ce2ba3SJiri Pirko 
372911ce2ba3SJiri Pirko 	/* Check if adding and already exists, or removing and can't find */
373011ce2ba3SJiri Pirko 	if (!found != !removing) {
373111ce2ba3SJiri Pirko 		rocker_kfree(trans, fdb);
373211ce2ba3SJiri Pirko 		if (!found && removing)
373311ce2ba3SJiri Pirko 			return 0;
373411ce2ba3SJiri Pirko 		/* Refreshing existing to update aging timers */
373511ce2ba3SJiri Pirko 		flags |= ROCKER_OP_FLAG_REFRESH;
373611ce2ba3SJiri Pirko 	}
373711ce2ba3SJiri Pirko 
373811ce2ba3SJiri Pirko 	return rocker_port_fdb_learn(rocker_port, trans, flags, addr, vlan_id);
373911ce2ba3SJiri Pirko }
374011ce2ba3SJiri Pirko 
374111ce2ba3SJiri Pirko static int rocker_port_fdb_flush(struct rocker_port *rocker_port,
374211ce2ba3SJiri Pirko 				 struct switchdev_trans *trans, int flags)
374311ce2ba3SJiri Pirko {
374411ce2ba3SJiri Pirko 	struct rocker *rocker = rocker_port->rocker;
374511ce2ba3SJiri Pirko 	struct rocker_fdb_tbl_entry *found;
374611ce2ba3SJiri Pirko 	unsigned long lock_flags;
374711ce2ba3SJiri Pirko 	struct hlist_node *tmp;
374811ce2ba3SJiri Pirko 	int bkt;
374911ce2ba3SJiri Pirko 	int err = 0;
375011ce2ba3SJiri Pirko 
375111ce2ba3SJiri Pirko 	if (rocker_port->stp_state == BR_STATE_LEARNING ||
375211ce2ba3SJiri Pirko 	    rocker_port->stp_state == BR_STATE_FORWARDING)
375311ce2ba3SJiri Pirko 		return 0;
375411ce2ba3SJiri Pirko 
375511ce2ba3SJiri Pirko 	flags |= ROCKER_OP_FLAG_NOWAIT | ROCKER_OP_FLAG_REMOVE;
375611ce2ba3SJiri Pirko 
375711ce2ba3SJiri Pirko 	spin_lock_irqsave(&rocker->fdb_tbl_lock, lock_flags);
375811ce2ba3SJiri Pirko 
375911ce2ba3SJiri Pirko 	hash_for_each_safe(rocker->fdb_tbl, bkt, tmp, found, entry) {
376011ce2ba3SJiri Pirko 		if (found->key.rocker_port != rocker_port)
376111ce2ba3SJiri Pirko 			continue;
376211ce2ba3SJiri Pirko 		if (!found->learned)
376311ce2ba3SJiri Pirko 			continue;
376411ce2ba3SJiri Pirko 		err = rocker_port_fdb_learn(rocker_port, trans, flags,
376511ce2ba3SJiri Pirko 					    found->key.addr,
376611ce2ba3SJiri Pirko 					    found->key.vlan_id);
376711ce2ba3SJiri Pirko 		if (err)
376811ce2ba3SJiri Pirko 			goto err_out;
376911ce2ba3SJiri Pirko 		if (!switchdev_trans_ph_prepare(trans))
377011ce2ba3SJiri Pirko 			hash_del(&found->entry);
377111ce2ba3SJiri Pirko 	}
377211ce2ba3SJiri Pirko 
377311ce2ba3SJiri Pirko err_out:
377411ce2ba3SJiri Pirko 	spin_unlock_irqrestore(&rocker->fdb_tbl_lock, lock_flags);
377511ce2ba3SJiri Pirko 
377611ce2ba3SJiri Pirko 	return err;
377711ce2ba3SJiri Pirko }
377811ce2ba3SJiri Pirko 
377911ce2ba3SJiri Pirko static void rocker_fdb_cleanup(unsigned long data)
378011ce2ba3SJiri Pirko {
378111ce2ba3SJiri Pirko 	struct rocker *rocker = (struct rocker *)data;
378211ce2ba3SJiri Pirko 	struct rocker_port *rocker_port;
378311ce2ba3SJiri Pirko 	struct rocker_fdb_tbl_entry *entry;
378411ce2ba3SJiri Pirko 	struct hlist_node *tmp;
378511ce2ba3SJiri Pirko 	unsigned long next_timer = jiffies + BR_MIN_AGEING_TIME;
378611ce2ba3SJiri Pirko 	unsigned long expires;
378711ce2ba3SJiri Pirko 	unsigned long lock_flags;
378811ce2ba3SJiri Pirko 	int flags = ROCKER_OP_FLAG_NOWAIT | ROCKER_OP_FLAG_REMOVE |
378911ce2ba3SJiri Pirko 		    ROCKER_OP_FLAG_LEARNED;
379011ce2ba3SJiri Pirko 	int bkt;
379111ce2ba3SJiri Pirko 
379211ce2ba3SJiri Pirko 	spin_lock_irqsave(&rocker->fdb_tbl_lock, lock_flags);
379311ce2ba3SJiri Pirko 
379411ce2ba3SJiri Pirko 	hash_for_each_safe(rocker->fdb_tbl, bkt, tmp, entry, entry) {
379511ce2ba3SJiri Pirko 		if (!entry->learned)
379611ce2ba3SJiri Pirko 			continue;
379711ce2ba3SJiri Pirko 		rocker_port = entry->key.rocker_port;
379811ce2ba3SJiri Pirko 		expires = entry->touched + rocker_port->ageing_time;
379911ce2ba3SJiri Pirko 		if (time_before_eq(expires, jiffies)) {
380011ce2ba3SJiri Pirko 			rocker_port_fdb_learn(rocker_port, NULL,
380111ce2ba3SJiri Pirko 					      flags, entry->key.addr,
380211ce2ba3SJiri Pirko 					      entry->key.vlan_id);
380311ce2ba3SJiri Pirko 			hash_del(&entry->entry);
380411ce2ba3SJiri Pirko 		} else if (time_before(expires, next_timer)) {
380511ce2ba3SJiri Pirko 			next_timer = expires;
380611ce2ba3SJiri Pirko 		}
380711ce2ba3SJiri Pirko 	}
380811ce2ba3SJiri Pirko 
380911ce2ba3SJiri Pirko 	spin_unlock_irqrestore(&rocker->fdb_tbl_lock, lock_flags);
381011ce2ba3SJiri Pirko 
381111ce2ba3SJiri Pirko 	mod_timer(&rocker->fdb_cleanup_timer, round_jiffies_up(next_timer));
381211ce2ba3SJiri Pirko }
381311ce2ba3SJiri Pirko 
381411ce2ba3SJiri Pirko static int rocker_port_router_mac(struct rocker_port *rocker_port,
381511ce2ba3SJiri Pirko 				  struct switchdev_trans *trans, int flags,
381611ce2ba3SJiri Pirko 				  __be16 vlan_id)
381711ce2ba3SJiri Pirko {
381811ce2ba3SJiri Pirko 	u32 in_pport_mask = 0xffffffff;
381911ce2ba3SJiri Pirko 	__be16 eth_type;
382011ce2ba3SJiri Pirko 	const u8 *dst_mac_mask = ff_mac;
382111ce2ba3SJiri Pirko 	__be16 vlan_id_mask = htons(0xffff);
382211ce2ba3SJiri Pirko 	bool copy_to_cpu = false;
382311ce2ba3SJiri Pirko 	int err;
382411ce2ba3SJiri Pirko 
382511ce2ba3SJiri Pirko 	if (ntohs(vlan_id) == 0)
382611ce2ba3SJiri Pirko 		vlan_id = rocker_port->internal_vlan_id;
382711ce2ba3SJiri Pirko 
382811ce2ba3SJiri Pirko 	eth_type = htons(ETH_P_IP);
382911ce2ba3SJiri Pirko 	err = rocker_flow_tbl_term_mac(rocker_port, trans,
383011ce2ba3SJiri Pirko 				       rocker_port->pport, in_pport_mask,
383111ce2ba3SJiri Pirko 				       eth_type, rocker_port->dev->dev_addr,
383211ce2ba3SJiri Pirko 				       dst_mac_mask, vlan_id, vlan_id_mask,
383311ce2ba3SJiri Pirko 				       copy_to_cpu, flags);
383411ce2ba3SJiri Pirko 	if (err)
383511ce2ba3SJiri Pirko 		return err;
383611ce2ba3SJiri Pirko 
383711ce2ba3SJiri Pirko 	eth_type = htons(ETH_P_IPV6);
383811ce2ba3SJiri Pirko 	err = rocker_flow_tbl_term_mac(rocker_port, trans,
383911ce2ba3SJiri Pirko 				       rocker_port->pport, in_pport_mask,
384011ce2ba3SJiri Pirko 				       eth_type, rocker_port->dev->dev_addr,
384111ce2ba3SJiri Pirko 				       dst_mac_mask, vlan_id, vlan_id_mask,
384211ce2ba3SJiri Pirko 				       copy_to_cpu, flags);
384311ce2ba3SJiri Pirko 
384411ce2ba3SJiri Pirko 	return err;
384511ce2ba3SJiri Pirko }
384611ce2ba3SJiri Pirko 
384711ce2ba3SJiri Pirko static int rocker_port_fwding(struct rocker_port *rocker_port,
384811ce2ba3SJiri Pirko 			      struct switchdev_trans *trans, int flags)
384911ce2ba3SJiri Pirko {
385011ce2ba3SJiri Pirko 	bool pop_vlan;
385111ce2ba3SJiri Pirko 	u32 out_pport;
385211ce2ba3SJiri Pirko 	__be16 vlan_id;
385311ce2ba3SJiri Pirko 	u16 vid;
385411ce2ba3SJiri Pirko 	int err;
385511ce2ba3SJiri Pirko 
385611ce2ba3SJiri Pirko 	/* Port will be forwarding-enabled if its STP state is LEARNING
385711ce2ba3SJiri Pirko 	 * or FORWARDING.  Traffic from CPU can still egress, regardless of
385811ce2ba3SJiri Pirko 	 * port STP state.  Use L2 interface group on port VLANs as a way
385911ce2ba3SJiri Pirko 	 * to toggle port forwarding: if forwarding is disabled, L2
386011ce2ba3SJiri Pirko 	 * interface group will not exist.
386111ce2ba3SJiri Pirko 	 */
386211ce2ba3SJiri Pirko 
386311ce2ba3SJiri Pirko 	if (rocker_port->stp_state != BR_STATE_LEARNING &&
386411ce2ba3SJiri Pirko 	    rocker_port->stp_state != BR_STATE_FORWARDING)
386511ce2ba3SJiri Pirko 		flags |= ROCKER_OP_FLAG_REMOVE;
386611ce2ba3SJiri Pirko 
386711ce2ba3SJiri Pirko 	out_pport = rocker_port->pport;
386811ce2ba3SJiri Pirko 	for (vid = 1; vid < VLAN_N_VID; vid++) {
386911ce2ba3SJiri Pirko 		if (!test_bit(vid, rocker_port->vlan_bitmap))
387011ce2ba3SJiri Pirko 			continue;
387111ce2ba3SJiri Pirko 		vlan_id = htons(vid);
387211ce2ba3SJiri Pirko 		pop_vlan = rocker_vlan_id_is_internal(vlan_id);
387311ce2ba3SJiri Pirko 		err = rocker_group_l2_interface(rocker_port, trans, flags,
387411ce2ba3SJiri Pirko 						vlan_id, out_pport, pop_vlan);
387511ce2ba3SJiri Pirko 		if (err) {
387611ce2ba3SJiri Pirko 			netdev_err(rocker_port->dev,
387711ce2ba3SJiri Pirko 				   "Error (%d) port VLAN l2 group for pport %d\n",
387811ce2ba3SJiri Pirko 				   err, out_pport);
387911ce2ba3SJiri Pirko 			return err;
388011ce2ba3SJiri Pirko 		}
388111ce2ba3SJiri Pirko 	}
388211ce2ba3SJiri Pirko 
388311ce2ba3SJiri Pirko 	return 0;
388411ce2ba3SJiri Pirko }
388511ce2ba3SJiri Pirko 
388611ce2ba3SJiri Pirko static int rocker_port_stp_update(struct rocker_port *rocker_port,
388711ce2ba3SJiri Pirko 				  struct switchdev_trans *trans, int flags,
388811ce2ba3SJiri Pirko 				  u8 state)
388911ce2ba3SJiri Pirko {
389011ce2ba3SJiri Pirko 	bool want[ROCKER_CTRL_MAX] = { 0, };
389111ce2ba3SJiri Pirko 	bool prev_ctrls[ROCKER_CTRL_MAX];
389211ce2ba3SJiri Pirko 	u8 uninitialized_var(prev_state);
389311ce2ba3SJiri Pirko 	int err;
389411ce2ba3SJiri Pirko 	int i;
389511ce2ba3SJiri Pirko 
389611ce2ba3SJiri Pirko 	if (switchdev_trans_ph_prepare(trans)) {
389711ce2ba3SJiri Pirko 		memcpy(prev_ctrls, rocker_port->ctrls, sizeof(prev_ctrls));
389811ce2ba3SJiri Pirko 		prev_state = rocker_port->stp_state;
389911ce2ba3SJiri Pirko 	}
390011ce2ba3SJiri Pirko 
390111ce2ba3SJiri Pirko 	if (rocker_port->stp_state == state)
390211ce2ba3SJiri Pirko 		return 0;
390311ce2ba3SJiri Pirko 
390411ce2ba3SJiri Pirko 	rocker_port->stp_state = state;
390511ce2ba3SJiri Pirko 
390611ce2ba3SJiri Pirko 	switch (state) {
390711ce2ba3SJiri Pirko 	case BR_STATE_DISABLED:
390811ce2ba3SJiri Pirko 		/* port is completely disabled */
390911ce2ba3SJiri Pirko 		break;
391011ce2ba3SJiri Pirko 	case BR_STATE_LISTENING:
391111ce2ba3SJiri Pirko 	case BR_STATE_BLOCKING:
391211ce2ba3SJiri Pirko 		want[ROCKER_CTRL_LINK_LOCAL_MCAST] = true;
391311ce2ba3SJiri Pirko 		break;
391411ce2ba3SJiri Pirko 	case BR_STATE_LEARNING:
391511ce2ba3SJiri Pirko 	case BR_STATE_FORWARDING:
391611ce2ba3SJiri Pirko 		if (!rocker_port_is_ovsed(rocker_port))
391711ce2ba3SJiri Pirko 			want[ROCKER_CTRL_LINK_LOCAL_MCAST] = true;
391811ce2ba3SJiri Pirko 		want[ROCKER_CTRL_IPV4_MCAST] = true;
391911ce2ba3SJiri Pirko 		want[ROCKER_CTRL_IPV6_MCAST] = true;
392011ce2ba3SJiri Pirko 		if (rocker_port_is_bridged(rocker_port))
392111ce2ba3SJiri Pirko 			want[ROCKER_CTRL_DFLT_BRIDGING] = true;
392211ce2ba3SJiri Pirko 		else if (rocker_port_is_ovsed(rocker_port))
392311ce2ba3SJiri Pirko 			want[ROCKER_CTRL_DFLT_OVS] = true;
392411ce2ba3SJiri Pirko 		else
392511ce2ba3SJiri Pirko 			want[ROCKER_CTRL_LOCAL_ARP] = true;
392611ce2ba3SJiri Pirko 		break;
392711ce2ba3SJiri Pirko 	}
392811ce2ba3SJiri Pirko 
392911ce2ba3SJiri Pirko 	for (i = 0; i < ROCKER_CTRL_MAX; i++) {
393011ce2ba3SJiri Pirko 		if (want[i] != rocker_port->ctrls[i]) {
393111ce2ba3SJiri Pirko 			int ctrl_flags = flags |
393211ce2ba3SJiri Pirko 					 (want[i] ? 0 : ROCKER_OP_FLAG_REMOVE);
393311ce2ba3SJiri Pirko 			err = rocker_port_ctrl(rocker_port, trans, ctrl_flags,
393411ce2ba3SJiri Pirko 					       &rocker_ctrls[i]);
393511ce2ba3SJiri Pirko 			if (err)
393611ce2ba3SJiri Pirko 				goto err_out;
393711ce2ba3SJiri Pirko 			rocker_port->ctrls[i] = want[i];
393811ce2ba3SJiri Pirko 		}
393911ce2ba3SJiri Pirko 	}
394011ce2ba3SJiri Pirko 
394111ce2ba3SJiri Pirko 	err = rocker_port_fdb_flush(rocker_port, trans, flags);
394211ce2ba3SJiri Pirko 	if (err)
394311ce2ba3SJiri Pirko 		goto err_out;
394411ce2ba3SJiri Pirko 
394511ce2ba3SJiri Pirko 	err = rocker_port_fwding(rocker_port, trans, flags);
394611ce2ba3SJiri Pirko 
394711ce2ba3SJiri Pirko err_out:
394811ce2ba3SJiri Pirko 	if (switchdev_trans_ph_prepare(trans)) {
394911ce2ba3SJiri Pirko 		memcpy(rocker_port->ctrls, prev_ctrls, sizeof(prev_ctrls));
395011ce2ba3SJiri Pirko 		rocker_port->stp_state = prev_state;
395111ce2ba3SJiri Pirko 	}
395211ce2ba3SJiri Pirko 
395311ce2ba3SJiri Pirko 	return err;
395411ce2ba3SJiri Pirko }
395511ce2ba3SJiri Pirko 
395611ce2ba3SJiri Pirko static int rocker_port_fwd_enable(struct rocker_port *rocker_port,
395711ce2ba3SJiri Pirko 				  struct switchdev_trans *trans, int flags)
395811ce2ba3SJiri Pirko {
395911ce2ba3SJiri Pirko 	if (rocker_port_is_bridged(rocker_port))
396011ce2ba3SJiri Pirko 		/* bridge STP will enable port */
396111ce2ba3SJiri Pirko 		return 0;
396211ce2ba3SJiri Pirko 
396311ce2ba3SJiri Pirko 	/* port is not bridged, so simulate going to FORWARDING state */
396411ce2ba3SJiri Pirko 	return rocker_port_stp_update(rocker_port, trans, flags,
396511ce2ba3SJiri Pirko 				      BR_STATE_FORWARDING);
396611ce2ba3SJiri Pirko }
396711ce2ba3SJiri Pirko 
396811ce2ba3SJiri Pirko static int rocker_port_fwd_disable(struct rocker_port *rocker_port,
396911ce2ba3SJiri Pirko 				   struct switchdev_trans *trans, int flags)
397011ce2ba3SJiri Pirko {
397111ce2ba3SJiri Pirko 	if (rocker_port_is_bridged(rocker_port))
397211ce2ba3SJiri Pirko 		/* bridge STP will disable port */
397311ce2ba3SJiri Pirko 		return 0;
397411ce2ba3SJiri Pirko 
397511ce2ba3SJiri Pirko 	/* port is not bridged, so simulate going to DISABLED state */
397611ce2ba3SJiri Pirko 	return rocker_port_stp_update(rocker_port, trans, flags,
397711ce2ba3SJiri Pirko 				      BR_STATE_DISABLED);
397811ce2ba3SJiri Pirko }
397911ce2ba3SJiri Pirko 
398011ce2ba3SJiri Pirko static struct rocker_internal_vlan_tbl_entry *
398111ce2ba3SJiri Pirko rocker_internal_vlan_tbl_find(const struct rocker *rocker, int ifindex)
398211ce2ba3SJiri Pirko {
398311ce2ba3SJiri Pirko 	struct rocker_internal_vlan_tbl_entry *found;
398411ce2ba3SJiri Pirko 
398511ce2ba3SJiri Pirko 	hash_for_each_possible(rocker->internal_vlan_tbl, found,
398611ce2ba3SJiri Pirko 			       entry, ifindex) {
398711ce2ba3SJiri Pirko 		if (found->ifindex == ifindex)
398811ce2ba3SJiri Pirko 			return found;
398911ce2ba3SJiri Pirko 	}
399011ce2ba3SJiri Pirko 
399111ce2ba3SJiri Pirko 	return NULL;
399211ce2ba3SJiri Pirko }
399311ce2ba3SJiri Pirko 
399411ce2ba3SJiri Pirko static __be16 rocker_port_internal_vlan_id_get(struct rocker_port *rocker_port,
399511ce2ba3SJiri Pirko 					       int ifindex)
399611ce2ba3SJiri Pirko {
399711ce2ba3SJiri Pirko 	struct rocker *rocker = rocker_port->rocker;
399811ce2ba3SJiri Pirko 	struct rocker_internal_vlan_tbl_entry *entry;
399911ce2ba3SJiri Pirko 	struct rocker_internal_vlan_tbl_entry *found;
400011ce2ba3SJiri Pirko 	unsigned long lock_flags;
400111ce2ba3SJiri Pirko 	int i;
400211ce2ba3SJiri Pirko 
400311ce2ba3SJiri Pirko 	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
400411ce2ba3SJiri Pirko 	if (!entry)
400511ce2ba3SJiri Pirko 		return 0;
400611ce2ba3SJiri Pirko 
400711ce2ba3SJiri Pirko 	entry->ifindex = ifindex;
400811ce2ba3SJiri Pirko 
400911ce2ba3SJiri Pirko 	spin_lock_irqsave(&rocker->internal_vlan_tbl_lock, lock_flags);
401011ce2ba3SJiri Pirko 
401111ce2ba3SJiri Pirko 	found = rocker_internal_vlan_tbl_find(rocker, ifindex);
401211ce2ba3SJiri Pirko 	if (found) {
401311ce2ba3SJiri Pirko 		kfree(entry);
401411ce2ba3SJiri Pirko 		goto found;
401511ce2ba3SJiri Pirko 	}
401611ce2ba3SJiri Pirko 
401711ce2ba3SJiri Pirko 	found = entry;
401811ce2ba3SJiri Pirko 	hash_add(rocker->internal_vlan_tbl, &found->entry, found->ifindex);
401911ce2ba3SJiri Pirko 
402011ce2ba3SJiri Pirko 	for (i = 0; i < ROCKER_N_INTERNAL_VLANS; i++) {
402111ce2ba3SJiri Pirko 		if (test_and_set_bit(i, rocker->internal_vlan_bitmap))
402211ce2ba3SJiri Pirko 			continue;
402311ce2ba3SJiri Pirko 		found->vlan_id = htons(ROCKER_INTERNAL_VLAN_ID_BASE + i);
402411ce2ba3SJiri Pirko 		goto found;
402511ce2ba3SJiri Pirko 	}
402611ce2ba3SJiri Pirko 
402711ce2ba3SJiri Pirko 	netdev_err(rocker_port->dev, "Out of internal VLAN IDs\n");
402811ce2ba3SJiri Pirko 
402911ce2ba3SJiri Pirko found:
403011ce2ba3SJiri Pirko 	found->ref_count++;
403111ce2ba3SJiri Pirko 	spin_unlock_irqrestore(&rocker->internal_vlan_tbl_lock, lock_flags);
403211ce2ba3SJiri Pirko 
403311ce2ba3SJiri Pirko 	return found->vlan_id;
403411ce2ba3SJiri Pirko }
403511ce2ba3SJiri Pirko 
403611ce2ba3SJiri Pirko static void
403711ce2ba3SJiri Pirko rocker_port_internal_vlan_id_put(const struct rocker_port *rocker_port,
403811ce2ba3SJiri Pirko 				 int ifindex)
403911ce2ba3SJiri Pirko {
404011ce2ba3SJiri Pirko 	struct rocker *rocker = rocker_port->rocker;
404111ce2ba3SJiri Pirko 	struct rocker_internal_vlan_tbl_entry *found;
404211ce2ba3SJiri Pirko 	unsigned long lock_flags;
404311ce2ba3SJiri Pirko 	unsigned long bit;
404411ce2ba3SJiri Pirko 
404511ce2ba3SJiri Pirko 	spin_lock_irqsave(&rocker->internal_vlan_tbl_lock, lock_flags);
404611ce2ba3SJiri Pirko 
404711ce2ba3SJiri Pirko 	found = rocker_internal_vlan_tbl_find(rocker, ifindex);
404811ce2ba3SJiri Pirko 	if (!found) {
404911ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev,
405011ce2ba3SJiri Pirko 			   "ifindex (%d) not found in internal VLAN tbl\n",
405111ce2ba3SJiri Pirko 			   ifindex);
405211ce2ba3SJiri Pirko 		goto not_found;
405311ce2ba3SJiri Pirko 	}
405411ce2ba3SJiri Pirko 
405511ce2ba3SJiri Pirko 	if (--found->ref_count <= 0) {
405611ce2ba3SJiri Pirko 		bit = ntohs(found->vlan_id) - ROCKER_INTERNAL_VLAN_ID_BASE;
405711ce2ba3SJiri Pirko 		clear_bit(bit, rocker->internal_vlan_bitmap);
405811ce2ba3SJiri Pirko 		hash_del(&found->entry);
405911ce2ba3SJiri Pirko 		kfree(found);
406011ce2ba3SJiri Pirko 	}
406111ce2ba3SJiri Pirko 
406211ce2ba3SJiri Pirko not_found:
406311ce2ba3SJiri Pirko 	spin_unlock_irqrestore(&rocker->internal_vlan_tbl_lock, lock_flags);
406411ce2ba3SJiri Pirko }
406511ce2ba3SJiri Pirko 
406611ce2ba3SJiri Pirko static int rocker_port_fib_ipv4(struct rocker_port *rocker_port,
406711ce2ba3SJiri Pirko 				struct switchdev_trans *trans, __be32 dst,
406811ce2ba3SJiri Pirko 				int dst_len, const struct fib_info *fi,
406911ce2ba3SJiri Pirko 				u32 tb_id, int flags)
407011ce2ba3SJiri Pirko {
407111ce2ba3SJiri Pirko 	const struct fib_nh *nh;
407211ce2ba3SJiri Pirko 	__be16 eth_type = htons(ETH_P_IP);
407311ce2ba3SJiri Pirko 	__be32 dst_mask = inet_make_mask(dst_len);
407411ce2ba3SJiri Pirko 	__be16 internal_vlan_id = rocker_port->internal_vlan_id;
407511ce2ba3SJiri Pirko 	u32 priority = fi->fib_priority;
407611ce2ba3SJiri Pirko 	enum rocker_of_dpa_table_id goto_tbl =
407711ce2ba3SJiri Pirko 		ROCKER_OF_DPA_TABLE_ID_ACL_POLICY;
407811ce2ba3SJiri Pirko 	u32 group_id;
407911ce2ba3SJiri Pirko 	bool nh_on_port;
408011ce2ba3SJiri Pirko 	bool has_gw;
408111ce2ba3SJiri Pirko 	u32 index;
408211ce2ba3SJiri Pirko 	int err;
408311ce2ba3SJiri Pirko 
408411ce2ba3SJiri Pirko 	/* XXX support ECMP */
408511ce2ba3SJiri Pirko 
408611ce2ba3SJiri Pirko 	nh = fi->fib_nh;
408711ce2ba3SJiri Pirko 	nh_on_port = (fi->fib_dev == rocker_port->dev);
408811ce2ba3SJiri Pirko 	has_gw = !!nh->nh_gw;
408911ce2ba3SJiri Pirko 
409011ce2ba3SJiri Pirko 	if (has_gw && nh_on_port) {
409111ce2ba3SJiri Pirko 		err = rocker_port_ipv4_nh(rocker_port, trans, flags,
409211ce2ba3SJiri Pirko 					  nh->nh_gw, &index);
409311ce2ba3SJiri Pirko 		if (err)
409411ce2ba3SJiri Pirko 			return err;
409511ce2ba3SJiri Pirko 
409611ce2ba3SJiri Pirko 		group_id = ROCKER_GROUP_L3_UNICAST(index);
409711ce2ba3SJiri Pirko 	} else {
409811ce2ba3SJiri Pirko 		/* Send to CPU for processing */
409911ce2ba3SJiri Pirko 		group_id = ROCKER_GROUP_L2_INTERFACE(internal_vlan_id, 0);
410011ce2ba3SJiri Pirko 	}
410111ce2ba3SJiri Pirko 
410211ce2ba3SJiri Pirko 	err = rocker_flow_tbl_ucast4_routing(rocker_port, trans, eth_type, dst,
410311ce2ba3SJiri Pirko 					     dst_mask, priority, goto_tbl,
410411ce2ba3SJiri Pirko 					     group_id, flags);
410511ce2ba3SJiri Pirko 	if (err)
410611ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev, "Error (%d) IPv4 route %pI4\n",
410711ce2ba3SJiri Pirko 			   err, &dst);
410811ce2ba3SJiri Pirko 
410911ce2ba3SJiri Pirko 	return err;
411011ce2ba3SJiri Pirko }
411111ce2ba3SJiri Pirko 
411211ce2ba3SJiri Pirko /*****************
411311ce2ba3SJiri Pirko  * Net device ops
411411ce2ba3SJiri Pirko  *****************/
411511ce2ba3SJiri Pirko 
411611ce2ba3SJiri Pirko static int rocker_port_open(struct net_device *dev)
411711ce2ba3SJiri Pirko {
411811ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(dev);
411911ce2ba3SJiri Pirko 	int err;
412011ce2ba3SJiri Pirko 
412111ce2ba3SJiri Pirko 	err = rocker_port_dma_rings_init(rocker_port);
412211ce2ba3SJiri Pirko 	if (err)
412311ce2ba3SJiri Pirko 		return err;
412411ce2ba3SJiri Pirko 
412511ce2ba3SJiri Pirko 	err = request_irq(rocker_msix_tx_vector(rocker_port),
412611ce2ba3SJiri Pirko 			  rocker_tx_irq_handler, 0,
412711ce2ba3SJiri Pirko 			  rocker_driver_name, rocker_port);
412811ce2ba3SJiri Pirko 	if (err) {
412911ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev, "cannot assign tx irq\n");
413011ce2ba3SJiri Pirko 		goto err_request_tx_irq;
413111ce2ba3SJiri Pirko 	}
413211ce2ba3SJiri Pirko 
413311ce2ba3SJiri Pirko 	err = request_irq(rocker_msix_rx_vector(rocker_port),
413411ce2ba3SJiri Pirko 			  rocker_rx_irq_handler, 0,
413511ce2ba3SJiri Pirko 			  rocker_driver_name, rocker_port);
413611ce2ba3SJiri Pirko 	if (err) {
413711ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev, "cannot assign rx irq\n");
413811ce2ba3SJiri Pirko 		goto err_request_rx_irq;
413911ce2ba3SJiri Pirko 	}
414011ce2ba3SJiri Pirko 
4141e420114eSJiri Pirko 	err = rocker_world_port_open(rocker_port);
4142e420114eSJiri Pirko 	if (err) {
4143e420114eSJiri Pirko 		netdev_err(rocker_port->dev, "cannot open port in world\n");
4144e420114eSJiri Pirko 		goto err_world_port_open;
4145e420114eSJiri Pirko 	}
4146e420114eSJiri Pirko 
414711ce2ba3SJiri Pirko 	err = rocker_port_fwd_enable(rocker_port, NULL, 0);
414811ce2ba3SJiri Pirko 	if (err)
414911ce2ba3SJiri Pirko 		goto err_fwd_enable;
415011ce2ba3SJiri Pirko 
415111ce2ba3SJiri Pirko 	napi_enable(&rocker_port->napi_tx);
415211ce2ba3SJiri Pirko 	napi_enable(&rocker_port->napi_rx);
415311ce2ba3SJiri Pirko 	if (!dev->proto_down)
415411ce2ba3SJiri Pirko 		rocker_port_set_enable(rocker_port, true);
415511ce2ba3SJiri Pirko 	netif_start_queue(dev);
415611ce2ba3SJiri Pirko 	return 0;
415711ce2ba3SJiri Pirko 
415811ce2ba3SJiri Pirko err_fwd_enable:
4159e420114eSJiri Pirko err_world_port_open:
416011ce2ba3SJiri Pirko 	free_irq(rocker_msix_rx_vector(rocker_port), rocker_port);
416111ce2ba3SJiri Pirko err_request_rx_irq:
416211ce2ba3SJiri Pirko 	free_irq(rocker_msix_tx_vector(rocker_port), rocker_port);
416311ce2ba3SJiri Pirko err_request_tx_irq:
416411ce2ba3SJiri Pirko 	rocker_port_dma_rings_fini(rocker_port);
416511ce2ba3SJiri Pirko 	return err;
416611ce2ba3SJiri Pirko }
416711ce2ba3SJiri Pirko 
416811ce2ba3SJiri Pirko static int rocker_port_stop(struct net_device *dev)
416911ce2ba3SJiri Pirko {
417011ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(dev);
417111ce2ba3SJiri Pirko 
417211ce2ba3SJiri Pirko 	netif_stop_queue(dev);
417311ce2ba3SJiri Pirko 	rocker_port_set_enable(rocker_port, false);
417411ce2ba3SJiri Pirko 	napi_disable(&rocker_port->napi_rx);
417511ce2ba3SJiri Pirko 	napi_disable(&rocker_port->napi_tx);
4176e420114eSJiri Pirko 	rocker_world_port_stop(rocker_port);
417711ce2ba3SJiri Pirko 	rocker_port_fwd_disable(rocker_port, NULL,
417811ce2ba3SJiri Pirko 				ROCKER_OP_FLAG_NOWAIT);
417911ce2ba3SJiri Pirko 	free_irq(rocker_msix_rx_vector(rocker_port), rocker_port);
418011ce2ba3SJiri Pirko 	free_irq(rocker_msix_tx_vector(rocker_port), rocker_port);
418111ce2ba3SJiri Pirko 	rocker_port_dma_rings_fini(rocker_port);
418211ce2ba3SJiri Pirko 
418311ce2ba3SJiri Pirko 	return 0;
418411ce2ba3SJiri Pirko }
418511ce2ba3SJiri Pirko 
418611ce2ba3SJiri Pirko static void rocker_tx_desc_frags_unmap(const struct rocker_port *rocker_port,
418711ce2ba3SJiri Pirko 				       const struct rocker_desc_info *desc_info)
418811ce2ba3SJiri Pirko {
418911ce2ba3SJiri Pirko 	const struct rocker *rocker = rocker_port->rocker;
419011ce2ba3SJiri Pirko 	struct pci_dev *pdev = rocker->pdev;
419111ce2ba3SJiri Pirko 	const struct rocker_tlv *attrs[ROCKER_TLV_TX_MAX + 1];
419211ce2ba3SJiri Pirko 	struct rocker_tlv *attr;
419311ce2ba3SJiri Pirko 	int rem;
419411ce2ba3SJiri Pirko 
419511ce2ba3SJiri Pirko 	rocker_tlv_parse_desc(attrs, ROCKER_TLV_TX_MAX, desc_info);
419611ce2ba3SJiri Pirko 	if (!attrs[ROCKER_TLV_TX_FRAGS])
419711ce2ba3SJiri Pirko 		return;
419811ce2ba3SJiri Pirko 	rocker_tlv_for_each_nested(attr, attrs[ROCKER_TLV_TX_FRAGS], rem) {
419911ce2ba3SJiri Pirko 		const struct rocker_tlv *frag_attrs[ROCKER_TLV_TX_FRAG_ATTR_MAX + 1];
420011ce2ba3SJiri Pirko 		dma_addr_t dma_handle;
420111ce2ba3SJiri Pirko 		size_t len;
420211ce2ba3SJiri Pirko 
420311ce2ba3SJiri Pirko 		if (rocker_tlv_type(attr) != ROCKER_TLV_TX_FRAG)
420411ce2ba3SJiri Pirko 			continue;
420511ce2ba3SJiri Pirko 		rocker_tlv_parse_nested(frag_attrs, ROCKER_TLV_TX_FRAG_ATTR_MAX,
420611ce2ba3SJiri Pirko 					attr);
420711ce2ba3SJiri Pirko 		if (!frag_attrs[ROCKER_TLV_TX_FRAG_ATTR_ADDR] ||
420811ce2ba3SJiri Pirko 		    !frag_attrs[ROCKER_TLV_TX_FRAG_ATTR_LEN])
420911ce2ba3SJiri Pirko 			continue;
421011ce2ba3SJiri Pirko 		dma_handle = rocker_tlv_get_u64(frag_attrs[ROCKER_TLV_TX_FRAG_ATTR_ADDR]);
421111ce2ba3SJiri Pirko 		len = rocker_tlv_get_u16(frag_attrs[ROCKER_TLV_TX_FRAG_ATTR_LEN]);
421211ce2ba3SJiri Pirko 		pci_unmap_single(pdev, dma_handle, len, DMA_TO_DEVICE);
421311ce2ba3SJiri Pirko 	}
421411ce2ba3SJiri Pirko }
421511ce2ba3SJiri Pirko 
421611ce2ba3SJiri Pirko static int rocker_tx_desc_frag_map_put(const struct rocker_port *rocker_port,
421711ce2ba3SJiri Pirko 				       struct rocker_desc_info *desc_info,
421811ce2ba3SJiri Pirko 				       char *buf, size_t buf_len)
421911ce2ba3SJiri Pirko {
422011ce2ba3SJiri Pirko 	const struct rocker *rocker = rocker_port->rocker;
422111ce2ba3SJiri Pirko 	struct pci_dev *pdev = rocker->pdev;
422211ce2ba3SJiri Pirko 	dma_addr_t dma_handle;
422311ce2ba3SJiri Pirko 	struct rocker_tlv *frag;
422411ce2ba3SJiri Pirko 
422511ce2ba3SJiri Pirko 	dma_handle = pci_map_single(pdev, buf, buf_len, DMA_TO_DEVICE);
422611ce2ba3SJiri Pirko 	if (unlikely(pci_dma_mapping_error(pdev, dma_handle))) {
422711ce2ba3SJiri Pirko 		if (net_ratelimit())
422811ce2ba3SJiri Pirko 			netdev_err(rocker_port->dev, "failed to dma map tx frag\n");
422911ce2ba3SJiri Pirko 		return -EIO;
423011ce2ba3SJiri Pirko 	}
423111ce2ba3SJiri Pirko 	frag = rocker_tlv_nest_start(desc_info, ROCKER_TLV_TX_FRAG);
423211ce2ba3SJiri Pirko 	if (!frag)
423311ce2ba3SJiri Pirko 		goto unmap_frag;
423411ce2ba3SJiri Pirko 	if (rocker_tlv_put_u64(desc_info, ROCKER_TLV_TX_FRAG_ATTR_ADDR,
423511ce2ba3SJiri Pirko 			       dma_handle))
423611ce2ba3SJiri Pirko 		goto nest_cancel;
423711ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_TX_FRAG_ATTR_LEN,
423811ce2ba3SJiri Pirko 			       buf_len))
423911ce2ba3SJiri Pirko 		goto nest_cancel;
424011ce2ba3SJiri Pirko 	rocker_tlv_nest_end(desc_info, frag);
424111ce2ba3SJiri Pirko 	return 0;
424211ce2ba3SJiri Pirko 
424311ce2ba3SJiri Pirko nest_cancel:
424411ce2ba3SJiri Pirko 	rocker_tlv_nest_cancel(desc_info, frag);
424511ce2ba3SJiri Pirko unmap_frag:
424611ce2ba3SJiri Pirko 	pci_unmap_single(pdev, dma_handle, buf_len, DMA_TO_DEVICE);
424711ce2ba3SJiri Pirko 	return -EMSGSIZE;
424811ce2ba3SJiri Pirko }
424911ce2ba3SJiri Pirko 
425011ce2ba3SJiri Pirko static netdev_tx_t rocker_port_xmit(struct sk_buff *skb, struct net_device *dev)
425111ce2ba3SJiri Pirko {
425211ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(dev);
425311ce2ba3SJiri Pirko 	struct rocker *rocker = rocker_port->rocker;
425411ce2ba3SJiri Pirko 	struct rocker_desc_info *desc_info;
425511ce2ba3SJiri Pirko 	struct rocker_tlv *frags;
425611ce2ba3SJiri Pirko 	int i;
425711ce2ba3SJiri Pirko 	int err;
425811ce2ba3SJiri Pirko 
425911ce2ba3SJiri Pirko 	desc_info = rocker_desc_head_get(&rocker_port->tx_ring);
426011ce2ba3SJiri Pirko 	if (unlikely(!desc_info)) {
426111ce2ba3SJiri Pirko 		if (net_ratelimit())
426211ce2ba3SJiri Pirko 			netdev_err(dev, "tx ring full when queue awake\n");
426311ce2ba3SJiri Pirko 		return NETDEV_TX_BUSY;
426411ce2ba3SJiri Pirko 	}
426511ce2ba3SJiri Pirko 
426611ce2ba3SJiri Pirko 	rocker_desc_cookie_ptr_set(desc_info, skb);
426711ce2ba3SJiri Pirko 
426811ce2ba3SJiri Pirko 	frags = rocker_tlv_nest_start(desc_info, ROCKER_TLV_TX_FRAGS);
426911ce2ba3SJiri Pirko 	if (!frags)
427011ce2ba3SJiri Pirko 		goto out;
427111ce2ba3SJiri Pirko 	err = rocker_tx_desc_frag_map_put(rocker_port, desc_info,
427211ce2ba3SJiri Pirko 					  skb->data, skb_headlen(skb));
427311ce2ba3SJiri Pirko 	if (err)
427411ce2ba3SJiri Pirko 		goto nest_cancel;
427511ce2ba3SJiri Pirko 	if (skb_shinfo(skb)->nr_frags > ROCKER_TX_FRAGS_MAX) {
427611ce2ba3SJiri Pirko 		err = skb_linearize(skb);
427711ce2ba3SJiri Pirko 		if (err)
427811ce2ba3SJiri Pirko 			goto unmap_frags;
427911ce2ba3SJiri Pirko 	}
428011ce2ba3SJiri Pirko 
428111ce2ba3SJiri Pirko 	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
428211ce2ba3SJiri Pirko 		const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
428311ce2ba3SJiri Pirko 
428411ce2ba3SJiri Pirko 		err = rocker_tx_desc_frag_map_put(rocker_port, desc_info,
428511ce2ba3SJiri Pirko 						  skb_frag_address(frag),
428611ce2ba3SJiri Pirko 						  skb_frag_size(frag));
428711ce2ba3SJiri Pirko 		if (err)
428811ce2ba3SJiri Pirko 			goto unmap_frags;
428911ce2ba3SJiri Pirko 	}
429011ce2ba3SJiri Pirko 	rocker_tlv_nest_end(desc_info, frags);
429111ce2ba3SJiri Pirko 
429211ce2ba3SJiri Pirko 	rocker_desc_gen_clear(desc_info);
429311ce2ba3SJiri Pirko 	rocker_desc_head_set(rocker, &rocker_port->tx_ring, desc_info);
429411ce2ba3SJiri Pirko 
429511ce2ba3SJiri Pirko 	desc_info = rocker_desc_head_get(&rocker_port->tx_ring);
429611ce2ba3SJiri Pirko 	if (!desc_info)
429711ce2ba3SJiri Pirko 		netif_stop_queue(dev);
429811ce2ba3SJiri Pirko 
429911ce2ba3SJiri Pirko 	return NETDEV_TX_OK;
430011ce2ba3SJiri Pirko 
430111ce2ba3SJiri Pirko unmap_frags:
430211ce2ba3SJiri Pirko 	rocker_tx_desc_frags_unmap(rocker_port, desc_info);
430311ce2ba3SJiri Pirko nest_cancel:
430411ce2ba3SJiri Pirko 	rocker_tlv_nest_cancel(desc_info, frags);
430511ce2ba3SJiri Pirko out:
430611ce2ba3SJiri Pirko 	dev_kfree_skb(skb);
430711ce2ba3SJiri Pirko 	dev->stats.tx_dropped++;
430811ce2ba3SJiri Pirko 
430911ce2ba3SJiri Pirko 	return NETDEV_TX_OK;
431011ce2ba3SJiri Pirko }
431111ce2ba3SJiri Pirko 
431211ce2ba3SJiri Pirko static int rocker_port_set_mac_address(struct net_device *dev, void *p)
431311ce2ba3SJiri Pirko {
431411ce2ba3SJiri Pirko 	struct sockaddr *addr = p;
431511ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(dev);
431611ce2ba3SJiri Pirko 	int err;
431711ce2ba3SJiri Pirko 
431811ce2ba3SJiri Pirko 	if (!is_valid_ether_addr(addr->sa_data))
431911ce2ba3SJiri Pirko 		return -EADDRNOTAVAIL;
432011ce2ba3SJiri Pirko 
432111ce2ba3SJiri Pirko 	err = rocker_cmd_set_port_settings_macaddr(rocker_port, addr->sa_data);
432211ce2ba3SJiri Pirko 	if (err)
432311ce2ba3SJiri Pirko 		return err;
432411ce2ba3SJiri Pirko 	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
432511ce2ba3SJiri Pirko 	return 0;
432611ce2ba3SJiri Pirko }
432711ce2ba3SJiri Pirko 
432811ce2ba3SJiri Pirko static int rocker_port_change_mtu(struct net_device *dev, int new_mtu)
432911ce2ba3SJiri Pirko {
433011ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(dev);
433111ce2ba3SJiri Pirko 	int running = netif_running(dev);
433211ce2ba3SJiri Pirko 	int err;
433311ce2ba3SJiri Pirko 
433411ce2ba3SJiri Pirko #define ROCKER_PORT_MIN_MTU	68
433511ce2ba3SJiri Pirko #define ROCKER_PORT_MAX_MTU	9000
433611ce2ba3SJiri Pirko 
433711ce2ba3SJiri Pirko 	if (new_mtu < ROCKER_PORT_MIN_MTU || new_mtu > ROCKER_PORT_MAX_MTU)
433811ce2ba3SJiri Pirko 		return -EINVAL;
433911ce2ba3SJiri Pirko 
434011ce2ba3SJiri Pirko 	if (running)
434111ce2ba3SJiri Pirko 		rocker_port_stop(dev);
434211ce2ba3SJiri Pirko 
434311ce2ba3SJiri Pirko 	netdev_info(dev, "MTU change from %d to %d\n", dev->mtu, new_mtu);
434411ce2ba3SJiri Pirko 	dev->mtu = new_mtu;
434511ce2ba3SJiri Pirko 
434611ce2ba3SJiri Pirko 	err = rocker_cmd_set_port_settings_mtu(rocker_port, new_mtu);
434711ce2ba3SJiri Pirko 	if (err)
434811ce2ba3SJiri Pirko 		return err;
434911ce2ba3SJiri Pirko 
435011ce2ba3SJiri Pirko 	if (running)
435111ce2ba3SJiri Pirko 		err = rocker_port_open(dev);
435211ce2ba3SJiri Pirko 
435311ce2ba3SJiri Pirko 	return err;
435411ce2ba3SJiri Pirko }
435511ce2ba3SJiri Pirko 
435611ce2ba3SJiri Pirko static int rocker_port_get_phys_port_name(struct net_device *dev,
435711ce2ba3SJiri Pirko 					  char *buf, size_t len)
435811ce2ba3SJiri Pirko {
435911ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(dev);
436011ce2ba3SJiri Pirko 	struct port_name name = { .buf = buf, .len = len };
436111ce2ba3SJiri Pirko 	int err;
436211ce2ba3SJiri Pirko 
436311ce2ba3SJiri Pirko 	err = rocker_cmd_exec(rocker_port, NULL, 0,
436411ce2ba3SJiri Pirko 			      rocker_cmd_get_port_settings_prep, NULL,
436511ce2ba3SJiri Pirko 			      rocker_cmd_get_port_settings_phys_name_proc,
436611ce2ba3SJiri Pirko 			      &name);
436711ce2ba3SJiri Pirko 
436811ce2ba3SJiri Pirko 	return err ? -EOPNOTSUPP : 0;
436911ce2ba3SJiri Pirko }
437011ce2ba3SJiri Pirko 
437111ce2ba3SJiri Pirko static int rocker_port_change_proto_down(struct net_device *dev,
437211ce2ba3SJiri Pirko 					 bool proto_down)
437311ce2ba3SJiri Pirko {
437411ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(dev);
437511ce2ba3SJiri Pirko 
437611ce2ba3SJiri Pirko 	if (rocker_port->dev->flags & IFF_UP)
437711ce2ba3SJiri Pirko 		rocker_port_set_enable(rocker_port, !proto_down);
437811ce2ba3SJiri Pirko 	rocker_port->dev->proto_down = proto_down;
437911ce2ba3SJiri Pirko 	return 0;
438011ce2ba3SJiri Pirko }
438111ce2ba3SJiri Pirko 
438211ce2ba3SJiri Pirko static void rocker_port_neigh_destroy(struct neighbour *n)
438311ce2ba3SJiri Pirko {
438411ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(n->dev);
438511ce2ba3SJiri Pirko 	int flags = ROCKER_OP_FLAG_REMOVE | ROCKER_OP_FLAG_NOWAIT;
438611ce2ba3SJiri Pirko 	__be32 ip_addr = *(__be32 *)n->primary_key;
4387e420114eSJiri Pirko 	int err;
438811ce2ba3SJiri Pirko 
438911ce2ba3SJiri Pirko 	rocker_port_ipv4_neigh(rocker_port, NULL,
439011ce2ba3SJiri Pirko 			       flags, ip_addr, n->ha);
4391e420114eSJiri Pirko 	err = rocker_world_port_neigh_destroy(rocker_port, n);
4392e420114eSJiri Pirko 	if (err)
4393e420114eSJiri Pirko 		netdev_warn(rocker_port->dev, "failed to handle neigh destroy (err %d)\n",
4394e420114eSJiri Pirko 			    err);
439511ce2ba3SJiri Pirko }
439611ce2ba3SJiri Pirko 
439711ce2ba3SJiri Pirko static const struct net_device_ops rocker_port_netdev_ops = {
439811ce2ba3SJiri Pirko 	.ndo_open			= rocker_port_open,
439911ce2ba3SJiri Pirko 	.ndo_stop			= rocker_port_stop,
440011ce2ba3SJiri Pirko 	.ndo_start_xmit			= rocker_port_xmit,
440111ce2ba3SJiri Pirko 	.ndo_set_mac_address		= rocker_port_set_mac_address,
440211ce2ba3SJiri Pirko 	.ndo_change_mtu			= rocker_port_change_mtu,
440311ce2ba3SJiri Pirko 	.ndo_bridge_getlink		= switchdev_port_bridge_getlink,
440411ce2ba3SJiri Pirko 	.ndo_bridge_setlink		= switchdev_port_bridge_setlink,
440511ce2ba3SJiri Pirko 	.ndo_bridge_dellink		= switchdev_port_bridge_dellink,
440611ce2ba3SJiri Pirko 	.ndo_fdb_add			= switchdev_port_fdb_add,
440711ce2ba3SJiri Pirko 	.ndo_fdb_del			= switchdev_port_fdb_del,
440811ce2ba3SJiri Pirko 	.ndo_fdb_dump			= switchdev_port_fdb_dump,
440911ce2ba3SJiri Pirko 	.ndo_get_phys_port_name		= rocker_port_get_phys_port_name,
441011ce2ba3SJiri Pirko 	.ndo_change_proto_down		= rocker_port_change_proto_down,
441111ce2ba3SJiri Pirko 	.ndo_neigh_destroy		= rocker_port_neigh_destroy,
441211ce2ba3SJiri Pirko };
441311ce2ba3SJiri Pirko 
441411ce2ba3SJiri Pirko /********************
441511ce2ba3SJiri Pirko  * swdev interface
441611ce2ba3SJiri Pirko  ********************/
441711ce2ba3SJiri Pirko 
441811ce2ba3SJiri Pirko static int rocker_port_attr_get(struct net_device *dev,
441911ce2ba3SJiri Pirko 				struct switchdev_attr *attr)
442011ce2ba3SJiri Pirko {
442111ce2ba3SJiri Pirko 	const struct rocker_port *rocker_port = netdev_priv(dev);
442211ce2ba3SJiri Pirko 	const struct rocker *rocker = rocker_port->rocker;
4423e420114eSJiri Pirko 	int err = 0;
442411ce2ba3SJiri Pirko 
442511ce2ba3SJiri Pirko 	switch (attr->id) {
442611ce2ba3SJiri Pirko 	case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
442711ce2ba3SJiri Pirko 		attr->u.ppid.id_len = sizeof(rocker->hw.id);
442811ce2ba3SJiri Pirko 		memcpy(&attr->u.ppid.id, &rocker->hw.id, attr->u.ppid.id_len);
442911ce2ba3SJiri Pirko 		break;
443011ce2ba3SJiri Pirko 	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
443111ce2ba3SJiri Pirko 		attr->u.brport_flags = rocker_port->brport_flags;
4432e420114eSJiri Pirko 		err = rocker_world_port_attr_bridge_flags_get(rocker_port,
4433e420114eSJiri Pirko 							      &attr->u.brport_flags);
443411ce2ba3SJiri Pirko 		break;
443511ce2ba3SJiri Pirko 	default:
443611ce2ba3SJiri Pirko 		return -EOPNOTSUPP;
443711ce2ba3SJiri Pirko 	}
443811ce2ba3SJiri Pirko 
4439e420114eSJiri Pirko 	return err;
444011ce2ba3SJiri Pirko }
444111ce2ba3SJiri Pirko 
444211ce2ba3SJiri Pirko static int rocker_port_brport_flags_set(struct rocker_port *rocker_port,
444311ce2ba3SJiri Pirko 					struct switchdev_trans *trans,
444411ce2ba3SJiri Pirko 					unsigned long brport_flags)
444511ce2ba3SJiri Pirko {
444611ce2ba3SJiri Pirko 	unsigned long orig_flags;
444711ce2ba3SJiri Pirko 	int err = 0;
444811ce2ba3SJiri Pirko 
444911ce2ba3SJiri Pirko 	orig_flags = rocker_port->brport_flags;
445011ce2ba3SJiri Pirko 	rocker_port->brport_flags = brport_flags;
445111ce2ba3SJiri Pirko 	if ((orig_flags ^ rocker_port->brport_flags) & BR_LEARNING)
4452c1fe922eSJiri Pirko 		err = rocker_port_set_learning(rocker_port, trans,
4453c1fe922eSJiri Pirko 					       !!(rocker_port->brport_flags & BR_LEARNING));
445411ce2ba3SJiri Pirko 
445511ce2ba3SJiri Pirko 	if (switchdev_trans_ph_prepare(trans))
445611ce2ba3SJiri Pirko 		rocker_port->brport_flags = orig_flags;
445711ce2ba3SJiri Pirko 
445811ce2ba3SJiri Pirko 	return err;
445911ce2ba3SJiri Pirko }
446011ce2ba3SJiri Pirko 
446111ce2ba3SJiri Pirko static int rocker_port_bridge_ageing_time(struct rocker_port *rocker_port,
446211ce2ba3SJiri Pirko 					  struct switchdev_trans *trans,
446311ce2ba3SJiri Pirko 					  u32 ageing_time)
446411ce2ba3SJiri Pirko {
446511ce2ba3SJiri Pirko 	if (!switchdev_trans_ph_prepare(trans)) {
446611ce2ba3SJiri Pirko 		rocker_port->ageing_time = clock_t_to_jiffies(ageing_time);
446711ce2ba3SJiri Pirko 		mod_timer(&rocker_port->rocker->fdb_cleanup_timer, jiffies);
446811ce2ba3SJiri Pirko 	}
446911ce2ba3SJiri Pirko 
447011ce2ba3SJiri Pirko 	return 0;
447111ce2ba3SJiri Pirko }
447211ce2ba3SJiri Pirko 
447311ce2ba3SJiri Pirko static int rocker_port_attr_set(struct net_device *dev,
447411ce2ba3SJiri Pirko 				const struct switchdev_attr *attr,
447511ce2ba3SJiri Pirko 				struct switchdev_trans *trans)
447611ce2ba3SJiri Pirko {
447711ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(dev);
447811ce2ba3SJiri Pirko 	int err = 0;
447911ce2ba3SJiri Pirko 
448011ce2ba3SJiri Pirko 	switch (attr->id) {
448111ce2ba3SJiri Pirko 	case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
448211ce2ba3SJiri Pirko 		err = rocker_port_stp_update(rocker_port, trans, 0,
448311ce2ba3SJiri Pirko 					     attr->u.stp_state);
4484e420114eSJiri Pirko 		if (err)
4485e420114eSJiri Pirko 			break;
4486e420114eSJiri Pirko 		err = rocker_world_port_attr_stp_state_set(rocker_port,
4487e420114eSJiri Pirko 							   attr->u.stp_state,
4488e420114eSJiri Pirko 							   trans);
448911ce2ba3SJiri Pirko 		break;
449011ce2ba3SJiri Pirko 	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
449111ce2ba3SJiri Pirko 		err = rocker_port_brport_flags_set(rocker_port, trans,
449211ce2ba3SJiri Pirko 						   attr->u.brport_flags);
4493e420114eSJiri Pirko 		if (err)
4494e420114eSJiri Pirko 			break;
4495e420114eSJiri Pirko 		err = rocker_world_port_attr_bridge_flags_set(rocker_port,
4496e420114eSJiri Pirko 							      attr->u.brport_flags,
4497e420114eSJiri Pirko 							      trans);
449811ce2ba3SJiri Pirko 		break;
449911ce2ba3SJiri Pirko 	case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
450011ce2ba3SJiri Pirko 		err = rocker_port_bridge_ageing_time(rocker_port, trans,
450111ce2ba3SJiri Pirko 						     attr->u.ageing_time);
4502e420114eSJiri Pirko 		if (err)
4503e420114eSJiri Pirko 			break;
4504e420114eSJiri Pirko 		err = rocker_world_port_attr_bridge_ageing_time_set(rocker_port,
4505e420114eSJiri Pirko 								    attr->u.ageing_time,
4506e420114eSJiri Pirko 								    trans);
450711ce2ba3SJiri Pirko 		break;
450811ce2ba3SJiri Pirko 	default:
450911ce2ba3SJiri Pirko 		err = -EOPNOTSUPP;
451011ce2ba3SJiri Pirko 		break;
451111ce2ba3SJiri Pirko 	}
451211ce2ba3SJiri Pirko 
451311ce2ba3SJiri Pirko 	return err;
451411ce2ba3SJiri Pirko }
451511ce2ba3SJiri Pirko 
451611ce2ba3SJiri Pirko static int rocker_port_vlan_add(struct rocker_port *rocker_port,
451711ce2ba3SJiri Pirko 				struct switchdev_trans *trans,
451811ce2ba3SJiri Pirko 				u16 vid, u16 flags)
451911ce2ba3SJiri Pirko {
452011ce2ba3SJiri Pirko 	int err;
452111ce2ba3SJiri Pirko 
452211ce2ba3SJiri Pirko 	/* XXX deal with flags for PVID and untagged */
452311ce2ba3SJiri Pirko 
452411ce2ba3SJiri Pirko 	err = rocker_port_vlan(rocker_port, trans, 0, vid);
452511ce2ba3SJiri Pirko 	if (err)
452611ce2ba3SJiri Pirko 		return err;
452711ce2ba3SJiri Pirko 
452811ce2ba3SJiri Pirko 	err = rocker_port_router_mac(rocker_port, trans, 0, htons(vid));
452911ce2ba3SJiri Pirko 	if (err)
453011ce2ba3SJiri Pirko 		rocker_port_vlan(rocker_port, trans,
453111ce2ba3SJiri Pirko 				 ROCKER_OP_FLAG_REMOVE, vid);
453211ce2ba3SJiri Pirko 
453311ce2ba3SJiri Pirko 	return err;
453411ce2ba3SJiri Pirko }
453511ce2ba3SJiri Pirko 
453611ce2ba3SJiri Pirko static int rocker_port_vlans_add(struct rocker_port *rocker_port,
453711ce2ba3SJiri Pirko 				 struct switchdev_trans *trans,
453811ce2ba3SJiri Pirko 				 const struct switchdev_obj_port_vlan *vlan)
453911ce2ba3SJiri Pirko {
454011ce2ba3SJiri Pirko 	u16 vid;
454111ce2ba3SJiri Pirko 	int err;
454211ce2ba3SJiri Pirko 
454311ce2ba3SJiri Pirko 	for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
454411ce2ba3SJiri Pirko 		err = rocker_port_vlan_add(rocker_port, trans,
454511ce2ba3SJiri Pirko 					   vid, vlan->flags);
454611ce2ba3SJiri Pirko 		if (err)
454711ce2ba3SJiri Pirko 			return err;
454811ce2ba3SJiri Pirko 	}
454911ce2ba3SJiri Pirko 
455011ce2ba3SJiri Pirko 	return 0;
455111ce2ba3SJiri Pirko }
455211ce2ba3SJiri Pirko 
455311ce2ba3SJiri Pirko static int rocker_port_fdb_add(struct rocker_port *rocker_port,
455411ce2ba3SJiri Pirko 			       struct switchdev_trans *trans,
455511ce2ba3SJiri Pirko 			       const struct switchdev_obj_port_fdb *fdb)
455611ce2ba3SJiri Pirko {
455711ce2ba3SJiri Pirko 	__be16 vlan_id = rocker_port_vid_to_vlan(rocker_port, fdb->vid, NULL);
455811ce2ba3SJiri Pirko 	int flags = 0;
455911ce2ba3SJiri Pirko 
456011ce2ba3SJiri Pirko 	if (!rocker_port_is_bridged(rocker_port))
456111ce2ba3SJiri Pirko 		return -EINVAL;
456211ce2ba3SJiri Pirko 
456311ce2ba3SJiri Pirko 	return rocker_port_fdb(rocker_port, trans, fdb->addr, vlan_id, flags);
456411ce2ba3SJiri Pirko }
456511ce2ba3SJiri Pirko 
456611ce2ba3SJiri Pirko static int rocker_port_obj_add(struct net_device *dev,
456711ce2ba3SJiri Pirko 			       const struct switchdev_obj *obj,
456811ce2ba3SJiri Pirko 			       struct switchdev_trans *trans)
456911ce2ba3SJiri Pirko {
457011ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(dev);
457111ce2ba3SJiri Pirko 	const struct switchdev_obj_ipv4_fib *fib4;
457211ce2ba3SJiri Pirko 	int err = 0;
457311ce2ba3SJiri Pirko 
457411ce2ba3SJiri Pirko 	switch (obj->id) {
457511ce2ba3SJiri Pirko 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
457611ce2ba3SJiri Pirko 		err = rocker_port_vlans_add(rocker_port, trans,
457711ce2ba3SJiri Pirko 					    SWITCHDEV_OBJ_PORT_VLAN(obj));
4578e420114eSJiri Pirko 		if (err)
4579e420114eSJiri Pirko 			break;
4580e420114eSJiri Pirko 		err = rocker_world_port_obj_vlan_add(rocker_port,
4581e420114eSJiri Pirko 						     SWITCHDEV_OBJ_PORT_VLAN(obj),
4582e420114eSJiri Pirko 						     trans);
458311ce2ba3SJiri Pirko 		break;
458411ce2ba3SJiri Pirko 	case SWITCHDEV_OBJ_ID_IPV4_FIB:
458511ce2ba3SJiri Pirko 		fib4 = SWITCHDEV_OBJ_IPV4_FIB(obj);
458611ce2ba3SJiri Pirko 		err = rocker_port_fib_ipv4(rocker_port, trans,
458711ce2ba3SJiri Pirko 					   htonl(fib4->dst), fib4->dst_len,
458811ce2ba3SJiri Pirko 					   &fib4->fi, fib4->tb_id, 0);
4589e420114eSJiri Pirko 		if (err)
4590e420114eSJiri Pirko 			break;
4591e420114eSJiri Pirko 		err = rocker_world_port_obj_fib4_add(rocker_port,
4592e420114eSJiri Pirko 						     SWITCHDEV_OBJ_IPV4_FIB(obj),
4593e420114eSJiri Pirko 						     trans);
459411ce2ba3SJiri Pirko 		break;
459511ce2ba3SJiri Pirko 	case SWITCHDEV_OBJ_ID_PORT_FDB:
459611ce2ba3SJiri Pirko 		err = rocker_port_fdb_add(rocker_port, trans,
459711ce2ba3SJiri Pirko 					  SWITCHDEV_OBJ_PORT_FDB(obj));
4598e420114eSJiri Pirko 		if (err)
4599e420114eSJiri Pirko 			break;
4600e420114eSJiri Pirko 		err = rocker_world_port_obj_fdb_add(rocker_port,
4601e420114eSJiri Pirko 						    SWITCHDEV_OBJ_PORT_FDB(obj),
4602e420114eSJiri Pirko 						    trans);
460311ce2ba3SJiri Pirko 		break;
460411ce2ba3SJiri Pirko 	default:
460511ce2ba3SJiri Pirko 		err = -EOPNOTSUPP;
460611ce2ba3SJiri Pirko 		break;
460711ce2ba3SJiri Pirko 	}
460811ce2ba3SJiri Pirko 
460911ce2ba3SJiri Pirko 	return err;
461011ce2ba3SJiri Pirko }
461111ce2ba3SJiri Pirko 
461211ce2ba3SJiri Pirko static int rocker_port_vlan_del(struct rocker_port *rocker_port,
461311ce2ba3SJiri Pirko 				u16 vid, u16 flags)
461411ce2ba3SJiri Pirko {
461511ce2ba3SJiri Pirko 	int err;
461611ce2ba3SJiri Pirko 
461711ce2ba3SJiri Pirko 	err = rocker_port_router_mac(rocker_port, NULL,
461811ce2ba3SJiri Pirko 				     ROCKER_OP_FLAG_REMOVE, htons(vid));
461911ce2ba3SJiri Pirko 	if (err)
462011ce2ba3SJiri Pirko 		return err;
462111ce2ba3SJiri Pirko 
462211ce2ba3SJiri Pirko 	return rocker_port_vlan(rocker_port, NULL,
462311ce2ba3SJiri Pirko 				ROCKER_OP_FLAG_REMOVE, vid);
462411ce2ba3SJiri Pirko }
462511ce2ba3SJiri Pirko 
462611ce2ba3SJiri Pirko static int rocker_port_vlans_del(struct rocker_port *rocker_port,
462711ce2ba3SJiri Pirko 				 const struct switchdev_obj_port_vlan *vlan)
462811ce2ba3SJiri Pirko {
462911ce2ba3SJiri Pirko 	u16 vid;
463011ce2ba3SJiri Pirko 	int err;
463111ce2ba3SJiri Pirko 
463211ce2ba3SJiri Pirko 	for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
463311ce2ba3SJiri Pirko 		err = rocker_port_vlan_del(rocker_port, vid, vlan->flags);
463411ce2ba3SJiri Pirko 		if (err)
463511ce2ba3SJiri Pirko 			return err;
463611ce2ba3SJiri Pirko 	}
463711ce2ba3SJiri Pirko 
463811ce2ba3SJiri Pirko 	return 0;
463911ce2ba3SJiri Pirko }
464011ce2ba3SJiri Pirko 
464111ce2ba3SJiri Pirko static int rocker_port_fdb_del(struct rocker_port *rocker_port,
464211ce2ba3SJiri Pirko 			       struct switchdev_trans *trans,
464311ce2ba3SJiri Pirko 			       const struct switchdev_obj_port_fdb *fdb)
464411ce2ba3SJiri Pirko {
464511ce2ba3SJiri Pirko 	__be16 vlan_id = rocker_port_vid_to_vlan(rocker_port, fdb->vid, NULL);
464611ce2ba3SJiri Pirko 	int flags = ROCKER_OP_FLAG_REMOVE;
464711ce2ba3SJiri Pirko 
464811ce2ba3SJiri Pirko 	if (!rocker_port_is_bridged(rocker_port))
464911ce2ba3SJiri Pirko 		return -EINVAL;
465011ce2ba3SJiri Pirko 
465111ce2ba3SJiri Pirko 	return rocker_port_fdb(rocker_port, trans, fdb->addr, vlan_id, flags);
465211ce2ba3SJiri Pirko }
465311ce2ba3SJiri Pirko 
465411ce2ba3SJiri Pirko static int rocker_port_obj_del(struct net_device *dev,
465511ce2ba3SJiri Pirko 			       const struct switchdev_obj *obj)
465611ce2ba3SJiri Pirko {
465711ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(dev);
465811ce2ba3SJiri Pirko 	const struct switchdev_obj_ipv4_fib *fib4;
465911ce2ba3SJiri Pirko 	int err = 0;
466011ce2ba3SJiri Pirko 
466111ce2ba3SJiri Pirko 	switch (obj->id) {
466211ce2ba3SJiri Pirko 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
466311ce2ba3SJiri Pirko 		err = rocker_port_vlans_del(rocker_port,
466411ce2ba3SJiri Pirko 					    SWITCHDEV_OBJ_PORT_VLAN(obj));
4665e420114eSJiri Pirko 		if (err)
4666e420114eSJiri Pirko 			break;
4667e420114eSJiri Pirko 		err = rocker_world_port_obj_vlan_del(rocker_port,
4668e420114eSJiri Pirko 						     SWITCHDEV_OBJ_PORT_VLAN(obj));
466911ce2ba3SJiri Pirko 		break;
467011ce2ba3SJiri Pirko 	case SWITCHDEV_OBJ_ID_IPV4_FIB:
467111ce2ba3SJiri Pirko 		fib4 = SWITCHDEV_OBJ_IPV4_FIB(obj);
467211ce2ba3SJiri Pirko 		err = rocker_port_fib_ipv4(rocker_port, NULL,
467311ce2ba3SJiri Pirko 					   htonl(fib4->dst), fib4->dst_len,
467411ce2ba3SJiri Pirko 					   &fib4->fi, fib4->tb_id,
467511ce2ba3SJiri Pirko 					   ROCKER_OP_FLAG_REMOVE);
4676e420114eSJiri Pirko 		if (err)
4677e420114eSJiri Pirko 			break;
4678e420114eSJiri Pirko 		err = rocker_world_port_obj_fib4_del(rocker_port,
4679e420114eSJiri Pirko 						     SWITCHDEV_OBJ_IPV4_FIB(obj));
468011ce2ba3SJiri Pirko 		break;
468111ce2ba3SJiri Pirko 	case SWITCHDEV_OBJ_ID_PORT_FDB:
468211ce2ba3SJiri Pirko 		err = rocker_port_fdb_del(rocker_port, NULL,
468311ce2ba3SJiri Pirko 					  SWITCHDEV_OBJ_PORT_FDB(obj));
4684e420114eSJiri Pirko 		if (err)
4685e420114eSJiri Pirko 			break;
4686e420114eSJiri Pirko 		err = rocker_world_port_obj_fdb_del(rocker_port,
4687e420114eSJiri Pirko 						    SWITCHDEV_OBJ_PORT_FDB(obj));
468811ce2ba3SJiri Pirko 		break;
468911ce2ba3SJiri Pirko 	default:
469011ce2ba3SJiri Pirko 		err = -EOPNOTSUPP;
469111ce2ba3SJiri Pirko 		break;
469211ce2ba3SJiri Pirko 	}
469311ce2ba3SJiri Pirko 
469411ce2ba3SJiri Pirko 	return err;
469511ce2ba3SJiri Pirko }
469611ce2ba3SJiri Pirko 
469711ce2ba3SJiri Pirko static int rocker_port_fdb_dump(const struct rocker_port *rocker_port,
469811ce2ba3SJiri Pirko 				struct switchdev_obj_port_fdb *fdb,
469911ce2ba3SJiri Pirko 				switchdev_obj_dump_cb_t *cb)
470011ce2ba3SJiri Pirko {
470111ce2ba3SJiri Pirko 	struct rocker *rocker = rocker_port->rocker;
470211ce2ba3SJiri Pirko 	struct rocker_fdb_tbl_entry *found;
470311ce2ba3SJiri Pirko 	struct hlist_node *tmp;
470411ce2ba3SJiri Pirko 	unsigned long lock_flags;
470511ce2ba3SJiri Pirko 	int bkt;
470611ce2ba3SJiri Pirko 	int err = 0;
470711ce2ba3SJiri Pirko 
470811ce2ba3SJiri Pirko 	spin_lock_irqsave(&rocker->fdb_tbl_lock, lock_flags);
470911ce2ba3SJiri Pirko 	hash_for_each_safe(rocker->fdb_tbl, bkt, tmp, found, entry) {
471011ce2ba3SJiri Pirko 		if (found->key.rocker_port != rocker_port)
471111ce2ba3SJiri Pirko 			continue;
471211ce2ba3SJiri Pirko 		ether_addr_copy(fdb->addr, found->key.addr);
471311ce2ba3SJiri Pirko 		fdb->ndm_state = NUD_REACHABLE;
471411ce2ba3SJiri Pirko 		fdb->vid = rocker_port_vlan_to_vid(rocker_port,
471511ce2ba3SJiri Pirko 						   found->key.vlan_id);
471611ce2ba3SJiri Pirko 		err = cb(&fdb->obj);
471711ce2ba3SJiri Pirko 		if (err)
471811ce2ba3SJiri Pirko 			break;
471911ce2ba3SJiri Pirko 	}
472011ce2ba3SJiri Pirko 	spin_unlock_irqrestore(&rocker->fdb_tbl_lock, lock_flags);
472111ce2ba3SJiri Pirko 
472211ce2ba3SJiri Pirko 	return err;
472311ce2ba3SJiri Pirko }
472411ce2ba3SJiri Pirko 
472511ce2ba3SJiri Pirko static int rocker_port_vlan_dump(const struct rocker_port *rocker_port,
472611ce2ba3SJiri Pirko 				 struct switchdev_obj_port_vlan *vlan,
472711ce2ba3SJiri Pirko 				 switchdev_obj_dump_cb_t *cb)
472811ce2ba3SJiri Pirko {
472911ce2ba3SJiri Pirko 	u16 vid;
473011ce2ba3SJiri Pirko 	int err = 0;
473111ce2ba3SJiri Pirko 
473211ce2ba3SJiri Pirko 	for (vid = 1; vid < VLAN_N_VID; vid++) {
473311ce2ba3SJiri Pirko 		if (!test_bit(vid, rocker_port->vlan_bitmap))
473411ce2ba3SJiri Pirko 			continue;
473511ce2ba3SJiri Pirko 		vlan->flags = 0;
473611ce2ba3SJiri Pirko 		if (rocker_vlan_id_is_internal(htons(vid)))
473711ce2ba3SJiri Pirko 			vlan->flags |= BRIDGE_VLAN_INFO_PVID;
473811ce2ba3SJiri Pirko 		vlan->vid_begin = vid;
473911ce2ba3SJiri Pirko 		vlan->vid_end = vid;
474011ce2ba3SJiri Pirko 		err = cb(&vlan->obj);
474111ce2ba3SJiri Pirko 		if (err)
474211ce2ba3SJiri Pirko 			break;
474311ce2ba3SJiri Pirko 	}
474411ce2ba3SJiri Pirko 
474511ce2ba3SJiri Pirko 	return err;
474611ce2ba3SJiri Pirko }
474711ce2ba3SJiri Pirko 
474811ce2ba3SJiri Pirko static int rocker_port_obj_dump(struct net_device *dev,
474911ce2ba3SJiri Pirko 				struct switchdev_obj *obj,
475011ce2ba3SJiri Pirko 				switchdev_obj_dump_cb_t *cb)
475111ce2ba3SJiri Pirko {
475211ce2ba3SJiri Pirko 	const struct rocker_port *rocker_port = netdev_priv(dev);
475311ce2ba3SJiri Pirko 	int err = 0;
475411ce2ba3SJiri Pirko 
475511ce2ba3SJiri Pirko 	switch (obj->id) {
475611ce2ba3SJiri Pirko 	case SWITCHDEV_OBJ_ID_PORT_FDB:
475711ce2ba3SJiri Pirko 		err = rocker_port_fdb_dump(rocker_port,
475811ce2ba3SJiri Pirko 					   SWITCHDEV_OBJ_PORT_FDB(obj), cb);
4759e420114eSJiri Pirko 		if (err)
4760e420114eSJiri Pirko 			break;
4761e420114eSJiri Pirko 		err = rocker_world_port_obj_fdb_dump(rocker_port,
4762e420114eSJiri Pirko 						     SWITCHDEV_OBJ_PORT_FDB(obj),
4763e420114eSJiri Pirko 						     cb);
476411ce2ba3SJiri Pirko 		break;
476511ce2ba3SJiri Pirko 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
476611ce2ba3SJiri Pirko 		err = rocker_port_vlan_dump(rocker_port,
476711ce2ba3SJiri Pirko 					    SWITCHDEV_OBJ_PORT_VLAN(obj), cb);
4768e420114eSJiri Pirko 		if (err)
4769e420114eSJiri Pirko 			break;
4770e420114eSJiri Pirko 		err = rocker_world_port_obj_vlan_dump(rocker_port,
4771e420114eSJiri Pirko 						      SWITCHDEV_OBJ_PORT_VLAN(obj),
4772e420114eSJiri Pirko 						      cb);
477311ce2ba3SJiri Pirko 		break;
477411ce2ba3SJiri Pirko 	default:
477511ce2ba3SJiri Pirko 		err = -EOPNOTSUPP;
477611ce2ba3SJiri Pirko 		break;
477711ce2ba3SJiri Pirko 	}
477811ce2ba3SJiri Pirko 
477911ce2ba3SJiri Pirko 	return err;
478011ce2ba3SJiri Pirko }
478111ce2ba3SJiri Pirko 
478211ce2ba3SJiri Pirko static const struct switchdev_ops rocker_port_switchdev_ops = {
478311ce2ba3SJiri Pirko 	.switchdev_port_attr_get	= rocker_port_attr_get,
478411ce2ba3SJiri Pirko 	.switchdev_port_attr_set	= rocker_port_attr_set,
478511ce2ba3SJiri Pirko 	.switchdev_port_obj_add		= rocker_port_obj_add,
478611ce2ba3SJiri Pirko 	.switchdev_port_obj_del		= rocker_port_obj_del,
478711ce2ba3SJiri Pirko 	.switchdev_port_obj_dump	= rocker_port_obj_dump,
478811ce2ba3SJiri Pirko };
478911ce2ba3SJiri Pirko 
479011ce2ba3SJiri Pirko /********************
479111ce2ba3SJiri Pirko  * ethtool interface
479211ce2ba3SJiri Pirko  ********************/
479311ce2ba3SJiri Pirko 
479411ce2ba3SJiri Pirko static int rocker_port_get_settings(struct net_device *dev,
479511ce2ba3SJiri Pirko 				    struct ethtool_cmd *ecmd)
479611ce2ba3SJiri Pirko {
479711ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(dev);
479811ce2ba3SJiri Pirko 
479911ce2ba3SJiri Pirko 	return rocker_cmd_get_port_settings_ethtool(rocker_port, ecmd);
480011ce2ba3SJiri Pirko }
480111ce2ba3SJiri Pirko 
480211ce2ba3SJiri Pirko static int rocker_port_set_settings(struct net_device *dev,
480311ce2ba3SJiri Pirko 				    struct ethtool_cmd *ecmd)
480411ce2ba3SJiri Pirko {
480511ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(dev);
480611ce2ba3SJiri Pirko 
480711ce2ba3SJiri Pirko 	return rocker_cmd_set_port_settings_ethtool(rocker_port, ecmd);
480811ce2ba3SJiri Pirko }
480911ce2ba3SJiri Pirko 
481011ce2ba3SJiri Pirko static void rocker_port_get_drvinfo(struct net_device *dev,
481111ce2ba3SJiri Pirko 				    struct ethtool_drvinfo *drvinfo)
481211ce2ba3SJiri Pirko {
481311ce2ba3SJiri Pirko 	strlcpy(drvinfo->driver, rocker_driver_name, sizeof(drvinfo->driver));
481411ce2ba3SJiri Pirko 	strlcpy(drvinfo->version, UTS_RELEASE, sizeof(drvinfo->version));
481511ce2ba3SJiri Pirko }
481611ce2ba3SJiri Pirko 
481711ce2ba3SJiri Pirko static struct rocker_port_stats {
481811ce2ba3SJiri Pirko 	char str[ETH_GSTRING_LEN];
481911ce2ba3SJiri Pirko 	int type;
482011ce2ba3SJiri Pirko } rocker_port_stats[] = {
482111ce2ba3SJiri Pirko 	{ "rx_packets", ROCKER_TLV_CMD_PORT_STATS_RX_PKTS,    },
482211ce2ba3SJiri Pirko 	{ "rx_bytes",   ROCKER_TLV_CMD_PORT_STATS_RX_BYTES,   },
482311ce2ba3SJiri Pirko 	{ "rx_dropped", ROCKER_TLV_CMD_PORT_STATS_RX_DROPPED, },
482411ce2ba3SJiri Pirko 	{ "rx_errors",  ROCKER_TLV_CMD_PORT_STATS_RX_ERRORS,  },
482511ce2ba3SJiri Pirko 
482611ce2ba3SJiri Pirko 	{ "tx_packets", ROCKER_TLV_CMD_PORT_STATS_TX_PKTS,    },
482711ce2ba3SJiri Pirko 	{ "tx_bytes",   ROCKER_TLV_CMD_PORT_STATS_TX_BYTES,   },
482811ce2ba3SJiri Pirko 	{ "tx_dropped", ROCKER_TLV_CMD_PORT_STATS_TX_DROPPED, },
482911ce2ba3SJiri Pirko 	{ "tx_errors",  ROCKER_TLV_CMD_PORT_STATS_TX_ERRORS,  },
483011ce2ba3SJiri Pirko };
483111ce2ba3SJiri Pirko 
483211ce2ba3SJiri Pirko #define ROCKER_PORT_STATS_LEN  ARRAY_SIZE(rocker_port_stats)
483311ce2ba3SJiri Pirko 
483411ce2ba3SJiri Pirko static void rocker_port_get_strings(struct net_device *netdev, u32 stringset,
483511ce2ba3SJiri Pirko 				    u8 *data)
483611ce2ba3SJiri Pirko {
483711ce2ba3SJiri Pirko 	u8 *p = data;
483811ce2ba3SJiri Pirko 	int i;
483911ce2ba3SJiri Pirko 
484011ce2ba3SJiri Pirko 	switch (stringset) {
484111ce2ba3SJiri Pirko 	case ETH_SS_STATS:
484211ce2ba3SJiri Pirko 		for (i = 0; i < ARRAY_SIZE(rocker_port_stats); i++) {
484311ce2ba3SJiri Pirko 			memcpy(p, rocker_port_stats[i].str, ETH_GSTRING_LEN);
484411ce2ba3SJiri Pirko 			p += ETH_GSTRING_LEN;
484511ce2ba3SJiri Pirko 		}
484611ce2ba3SJiri Pirko 		break;
484711ce2ba3SJiri Pirko 	}
484811ce2ba3SJiri Pirko }
484911ce2ba3SJiri Pirko 
485011ce2ba3SJiri Pirko static int
485111ce2ba3SJiri Pirko rocker_cmd_get_port_stats_prep(const struct rocker_port *rocker_port,
485211ce2ba3SJiri Pirko 			       struct rocker_desc_info *desc_info,
485311ce2ba3SJiri Pirko 			       void *priv)
485411ce2ba3SJiri Pirko {
485511ce2ba3SJiri Pirko 	struct rocker_tlv *cmd_stats;
485611ce2ba3SJiri Pirko 
485711ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE,
485811ce2ba3SJiri Pirko 			       ROCKER_TLV_CMD_TYPE_GET_PORT_STATS))
485911ce2ba3SJiri Pirko 		return -EMSGSIZE;
486011ce2ba3SJiri Pirko 
486111ce2ba3SJiri Pirko 	cmd_stats = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO);
486211ce2ba3SJiri Pirko 	if (!cmd_stats)
486311ce2ba3SJiri Pirko 		return -EMSGSIZE;
486411ce2ba3SJiri Pirko 
486511ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_STATS_PPORT,
486611ce2ba3SJiri Pirko 			       rocker_port->pport))
486711ce2ba3SJiri Pirko 		return -EMSGSIZE;
486811ce2ba3SJiri Pirko 
486911ce2ba3SJiri Pirko 	rocker_tlv_nest_end(desc_info, cmd_stats);
487011ce2ba3SJiri Pirko 
487111ce2ba3SJiri Pirko 	return 0;
487211ce2ba3SJiri Pirko }
487311ce2ba3SJiri Pirko 
487411ce2ba3SJiri Pirko static int
487511ce2ba3SJiri Pirko rocker_cmd_get_port_stats_ethtool_proc(const struct rocker_port *rocker_port,
487611ce2ba3SJiri Pirko 				       const struct rocker_desc_info *desc_info,
487711ce2ba3SJiri Pirko 				       void *priv)
487811ce2ba3SJiri Pirko {
487911ce2ba3SJiri Pirko 	const struct rocker_tlv *attrs[ROCKER_TLV_CMD_MAX + 1];
488011ce2ba3SJiri Pirko 	const struct rocker_tlv *stats_attrs[ROCKER_TLV_CMD_PORT_STATS_MAX + 1];
488111ce2ba3SJiri Pirko 	const struct rocker_tlv *pattr;
488211ce2ba3SJiri Pirko 	u32 pport;
488311ce2ba3SJiri Pirko 	u64 *data = priv;
488411ce2ba3SJiri Pirko 	int i;
488511ce2ba3SJiri Pirko 
488611ce2ba3SJiri Pirko 	rocker_tlv_parse_desc(attrs, ROCKER_TLV_CMD_MAX, desc_info);
488711ce2ba3SJiri Pirko 
488811ce2ba3SJiri Pirko 	if (!attrs[ROCKER_TLV_CMD_INFO])
488911ce2ba3SJiri Pirko 		return -EIO;
489011ce2ba3SJiri Pirko 
489111ce2ba3SJiri Pirko 	rocker_tlv_parse_nested(stats_attrs, ROCKER_TLV_CMD_PORT_STATS_MAX,
489211ce2ba3SJiri Pirko 				attrs[ROCKER_TLV_CMD_INFO]);
489311ce2ba3SJiri Pirko 
489411ce2ba3SJiri Pirko 	if (!stats_attrs[ROCKER_TLV_CMD_PORT_STATS_PPORT])
489511ce2ba3SJiri Pirko 		return -EIO;
489611ce2ba3SJiri Pirko 
489711ce2ba3SJiri Pirko 	pport = rocker_tlv_get_u32(stats_attrs[ROCKER_TLV_CMD_PORT_STATS_PPORT]);
489811ce2ba3SJiri Pirko 	if (pport != rocker_port->pport)
489911ce2ba3SJiri Pirko 		return -EIO;
490011ce2ba3SJiri Pirko 
490111ce2ba3SJiri Pirko 	for (i = 0; i < ARRAY_SIZE(rocker_port_stats); i++) {
490211ce2ba3SJiri Pirko 		pattr = stats_attrs[rocker_port_stats[i].type];
490311ce2ba3SJiri Pirko 		if (!pattr)
490411ce2ba3SJiri Pirko 			continue;
490511ce2ba3SJiri Pirko 
490611ce2ba3SJiri Pirko 		data[i] = rocker_tlv_get_u64(pattr);
490711ce2ba3SJiri Pirko 	}
490811ce2ba3SJiri Pirko 
490911ce2ba3SJiri Pirko 	return 0;
491011ce2ba3SJiri Pirko }
491111ce2ba3SJiri Pirko 
491211ce2ba3SJiri Pirko static int rocker_cmd_get_port_stats_ethtool(struct rocker_port *rocker_port,
491311ce2ba3SJiri Pirko 					     void *priv)
491411ce2ba3SJiri Pirko {
491511ce2ba3SJiri Pirko 	return rocker_cmd_exec(rocker_port, NULL, 0,
491611ce2ba3SJiri Pirko 			       rocker_cmd_get_port_stats_prep, NULL,
491711ce2ba3SJiri Pirko 			       rocker_cmd_get_port_stats_ethtool_proc,
491811ce2ba3SJiri Pirko 			       priv);
491911ce2ba3SJiri Pirko }
492011ce2ba3SJiri Pirko 
492111ce2ba3SJiri Pirko static void rocker_port_get_stats(struct net_device *dev,
492211ce2ba3SJiri Pirko 				  struct ethtool_stats *stats, u64 *data)
492311ce2ba3SJiri Pirko {
492411ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(dev);
492511ce2ba3SJiri Pirko 
492611ce2ba3SJiri Pirko 	if (rocker_cmd_get_port_stats_ethtool(rocker_port, data) != 0) {
492711ce2ba3SJiri Pirko 		int i;
492811ce2ba3SJiri Pirko 
492911ce2ba3SJiri Pirko 		for (i = 0; i < ARRAY_SIZE(rocker_port_stats); ++i)
493011ce2ba3SJiri Pirko 			data[i] = 0;
493111ce2ba3SJiri Pirko 	}
493211ce2ba3SJiri Pirko }
493311ce2ba3SJiri Pirko 
493411ce2ba3SJiri Pirko static int rocker_port_get_sset_count(struct net_device *netdev, int sset)
493511ce2ba3SJiri Pirko {
493611ce2ba3SJiri Pirko 	switch (sset) {
493711ce2ba3SJiri Pirko 	case ETH_SS_STATS:
493811ce2ba3SJiri Pirko 		return ROCKER_PORT_STATS_LEN;
493911ce2ba3SJiri Pirko 	default:
494011ce2ba3SJiri Pirko 		return -EOPNOTSUPP;
494111ce2ba3SJiri Pirko 	}
494211ce2ba3SJiri Pirko }
494311ce2ba3SJiri Pirko 
494411ce2ba3SJiri Pirko static const struct ethtool_ops rocker_port_ethtool_ops = {
494511ce2ba3SJiri Pirko 	.get_settings		= rocker_port_get_settings,
494611ce2ba3SJiri Pirko 	.set_settings		= rocker_port_set_settings,
494711ce2ba3SJiri Pirko 	.get_drvinfo		= rocker_port_get_drvinfo,
494811ce2ba3SJiri Pirko 	.get_link		= ethtool_op_get_link,
494911ce2ba3SJiri Pirko 	.get_strings		= rocker_port_get_strings,
495011ce2ba3SJiri Pirko 	.get_ethtool_stats	= rocker_port_get_stats,
495111ce2ba3SJiri Pirko 	.get_sset_count		= rocker_port_get_sset_count,
495211ce2ba3SJiri Pirko };
495311ce2ba3SJiri Pirko 
495411ce2ba3SJiri Pirko /*****************
495511ce2ba3SJiri Pirko  * NAPI interface
495611ce2ba3SJiri Pirko  *****************/
495711ce2ba3SJiri Pirko 
495811ce2ba3SJiri Pirko static struct rocker_port *rocker_port_napi_tx_get(struct napi_struct *napi)
495911ce2ba3SJiri Pirko {
496011ce2ba3SJiri Pirko 	return container_of(napi, struct rocker_port, napi_tx);
496111ce2ba3SJiri Pirko }
496211ce2ba3SJiri Pirko 
496311ce2ba3SJiri Pirko static int rocker_port_poll_tx(struct napi_struct *napi, int budget)
496411ce2ba3SJiri Pirko {
496511ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = rocker_port_napi_tx_get(napi);
496611ce2ba3SJiri Pirko 	const struct rocker *rocker = rocker_port->rocker;
496711ce2ba3SJiri Pirko 	const struct rocker_desc_info *desc_info;
496811ce2ba3SJiri Pirko 	u32 credits = 0;
496911ce2ba3SJiri Pirko 	int err;
497011ce2ba3SJiri Pirko 
497111ce2ba3SJiri Pirko 	/* Cleanup tx descriptors */
497211ce2ba3SJiri Pirko 	while ((desc_info = rocker_desc_tail_get(&rocker_port->tx_ring))) {
497311ce2ba3SJiri Pirko 		struct sk_buff *skb;
497411ce2ba3SJiri Pirko 
497511ce2ba3SJiri Pirko 		err = rocker_desc_err(desc_info);
497611ce2ba3SJiri Pirko 		if (err && net_ratelimit())
497711ce2ba3SJiri Pirko 			netdev_err(rocker_port->dev, "tx desc received with err %d\n",
497811ce2ba3SJiri Pirko 				   err);
497911ce2ba3SJiri Pirko 		rocker_tx_desc_frags_unmap(rocker_port, desc_info);
498011ce2ba3SJiri Pirko 
498111ce2ba3SJiri Pirko 		skb = rocker_desc_cookie_ptr_get(desc_info);
498211ce2ba3SJiri Pirko 		if (err == 0) {
498311ce2ba3SJiri Pirko 			rocker_port->dev->stats.tx_packets++;
498411ce2ba3SJiri Pirko 			rocker_port->dev->stats.tx_bytes += skb->len;
498511ce2ba3SJiri Pirko 		} else {
498611ce2ba3SJiri Pirko 			rocker_port->dev->stats.tx_errors++;
498711ce2ba3SJiri Pirko 		}
498811ce2ba3SJiri Pirko 
498911ce2ba3SJiri Pirko 		dev_kfree_skb_any(skb);
499011ce2ba3SJiri Pirko 		credits++;
499111ce2ba3SJiri Pirko 	}
499211ce2ba3SJiri Pirko 
499311ce2ba3SJiri Pirko 	if (credits && netif_queue_stopped(rocker_port->dev))
499411ce2ba3SJiri Pirko 		netif_wake_queue(rocker_port->dev);
499511ce2ba3SJiri Pirko 
499611ce2ba3SJiri Pirko 	napi_complete(napi);
499711ce2ba3SJiri Pirko 	rocker_dma_ring_credits_set(rocker, &rocker_port->tx_ring, credits);
499811ce2ba3SJiri Pirko 
499911ce2ba3SJiri Pirko 	return 0;
500011ce2ba3SJiri Pirko }
500111ce2ba3SJiri Pirko 
500211ce2ba3SJiri Pirko static int rocker_port_rx_proc(const struct rocker *rocker,
500311ce2ba3SJiri Pirko 			       const struct rocker_port *rocker_port,
500411ce2ba3SJiri Pirko 			       struct rocker_desc_info *desc_info)
500511ce2ba3SJiri Pirko {
500611ce2ba3SJiri Pirko 	const struct rocker_tlv *attrs[ROCKER_TLV_RX_MAX + 1];
500711ce2ba3SJiri Pirko 	struct sk_buff *skb = rocker_desc_cookie_ptr_get(desc_info);
500811ce2ba3SJiri Pirko 	size_t rx_len;
500911ce2ba3SJiri Pirko 	u16 rx_flags = 0;
501011ce2ba3SJiri Pirko 
501111ce2ba3SJiri Pirko 	if (!skb)
501211ce2ba3SJiri Pirko 		return -ENOENT;
501311ce2ba3SJiri Pirko 
501411ce2ba3SJiri Pirko 	rocker_tlv_parse_desc(attrs, ROCKER_TLV_RX_MAX, desc_info);
501511ce2ba3SJiri Pirko 	if (!attrs[ROCKER_TLV_RX_FRAG_LEN])
501611ce2ba3SJiri Pirko 		return -EINVAL;
501711ce2ba3SJiri Pirko 	if (attrs[ROCKER_TLV_RX_FLAGS])
501811ce2ba3SJiri Pirko 		rx_flags = rocker_tlv_get_u16(attrs[ROCKER_TLV_RX_FLAGS]);
501911ce2ba3SJiri Pirko 
502011ce2ba3SJiri Pirko 	rocker_dma_rx_ring_skb_unmap(rocker, attrs);
502111ce2ba3SJiri Pirko 
502211ce2ba3SJiri Pirko 	rx_len = rocker_tlv_get_u16(attrs[ROCKER_TLV_RX_FRAG_LEN]);
502311ce2ba3SJiri Pirko 	skb_put(skb, rx_len);
502411ce2ba3SJiri Pirko 	skb->protocol = eth_type_trans(skb, rocker_port->dev);
502511ce2ba3SJiri Pirko 
502611ce2ba3SJiri Pirko 	if (rx_flags & ROCKER_RX_FLAGS_FWD_OFFLOAD)
502711ce2ba3SJiri Pirko 		skb->offload_fwd_mark = rocker_port->dev->offload_fwd_mark;
502811ce2ba3SJiri Pirko 
502911ce2ba3SJiri Pirko 	rocker_port->dev->stats.rx_packets++;
503011ce2ba3SJiri Pirko 	rocker_port->dev->stats.rx_bytes += skb->len;
503111ce2ba3SJiri Pirko 
503211ce2ba3SJiri Pirko 	netif_receive_skb(skb);
503311ce2ba3SJiri Pirko 
503411ce2ba3SJiri Pirko 	return rocker_dma_rx_ring_skb_alloc(rocker_port, desc_info);
503511ce2ba3SJiri Pirko }
503611ce2ba3SJiri Pirko 
503711ce2ba3SJiri Pirko static struct rocker_port *rocker_port_napi_rx_get(struct napi_struct *napi)
503811ce2ba3SJiri Pirko {
503911ce2ba3SJiri Pirko 	return container_of(napi, struct rocker_port, napi_rx);
504011ce2ba3SJiri Pirko }
504111ce2ba3SJiri Pirko 
504211ce2ba3SJiri Pirko static int rocker_port_poll_rx(struct napi_struct *napi, int budget)
504311ce2ba3SJiri Pirko {
504411ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = rocker_port_napi_rx_get(napi);
504511ce2ba3SJiri Pirko 	const struct rocker *rocker = rocker_port->rocker;
504611ce2ba3SJiri Pirko 	struct rocker_desc_info *desc_info;
504711ce2ba3SJiri Pirko 	u32 credits = 0;
504811ce2ba3SJiri Pirko 	int err;
504911ce2ba3SJiri Pirko 
505011ce2ba3SJiri Pirko 	/* Process rx descriptors */
505111ce2ba3SJiri Pirko 	while (credits < budget &&
505211ce2ba3SJiri Pirko 	       (desc_info = rocker_desc_tail_get(&rocker_port->rx_ring))) {
505311ce2ba3SJiri Pirko 		err = rocker_desc_err(desc_info);
505411ce2ba3SJiri Pirko 		if (err) {
505511ce2ba3SJiri Pirko 			if (net_ratelimit())
505611ce2ba3SJiri Pirko 				netdev_err(rocker_port->dev, "rx desc received with err %d\n",
505711ce2ba3SJiri Pirko 					   err);
505811ce2ba3SJiri Pirko 		} else {
505911ce2ba3SJiri Pirko 			err = rocker_port_rx_proc(rocker, rocker_port,
506011ce2ba3SJiri Pirko 						  desc_info);
506111ce2ba3SJiri Pirko 			if (err && net_ratelimit())
506211ce2ba3SJiri Pirko 				netdev_err(rocker_port->dev, "rx processing failed with err %d\n",
506311ce2ba3SJiri Pirko 					   err);
506411ce2ba3SJiri Pirko 		}
506511ce2ba3SJiri Pirko 		if (err)
506611ce2ba3SJiri Pirko 			rocker_port->dev->stats.rx_errors++;
506711ce2ba3SJiri Pirko 
506811ce2ba3SJiri Pirko 		rocker_desc_gen_clear(desc_info);
506911ce2ba3SJiri Pirko 		rocker_desc_head_set(rocker, &rocker_port->rx_ring, desc_info);
507011ce2ba3SJiri Pirko 		credits++;
507111ce2ba3SJiri Pirko 	}
507211ce2ba3SJiri Pirko 
507311ce2ba3SJiri Pirko 	if (credits < budget)
507411ce2ba3SJiri Pirko 		napi_complete(napi);
507511ce2ba3SJiri Pirko 
507611ce2ba3SJiri Pirko 	rocker_dma_ring_credits_set(rocker, &rocker_port->rx_ring, credits);
507711ce2ba3SJiri Pirko 
507811ce2ba3SJiri Pirko 	return credits;
507911ce2ba3SJiri Pirko }
508011ce2ba3SJiri Pirko 
508111ce2ba3SJiri Pirko /*****************
508211ce2ba3SJiri Pirko  * PCI driver ops
508311ce2ba3SJiri Pirko  *****************/
508411ce2ba3SJiri Pirko 
508511ce2ba3SJiri Pirko static void rocker_carrier_init(const struct rocker_port *rocker_port)
508611ce2ba3SJiri Pirko {
508711ce2ba3SJiri Pirko 	const struct rocker *rocker = rocker_port->rocker;
508811ce2ba3SJiri Pirko 	u64 link_status = rocker_read64(rocker, PORT_PHYS_LINK_STATUS);
508911ce2ba3SJiri Pirko 	bool link_up;
509011ce2ba3SJiri Pirko 
509111ce2ba3SJiri Pirko 	link_up = link_status & (1 << rocker_port->pport);
509211ce2ba3SJiri Pirko 	if (link_up)
509311ce2ba3SJiri Pirko 		netif_carrier_on(rocker_port->dev);
509411ce2ba3SJiri Pirko 	else
509511ce2ba3SJiri Pirko 		netif_carrier_off(rocker_port->dev);
509611ce2ba3SJiri Pirko }
509711ce2ba3SJiri Pirko 
5098e420114eSJiri Pirko static void rocker_remove_ports(struct rocker *rocker)
509911ce2ba3SJiri Pirko {
510011ce2ba3SJiri Pirko 	struct rocker_port *rocker_port;
510111ce2ba3SJiri Pirko 	int i;
510211ce2ba3SJiri Pirko 
510311ce2ba3SJiri Pirko 	for (i = 0; i < rocker->port_count; i++) {
510411ce2ba3SJiri Pirko 		rocker_port = rocker->ports[i];
510511ce2ba3SJiri Pirko 		if (!rocker_port)
510611ce2ba3SJiri Pirko 			continue;
510711ce2ba3SJiri Pirko 		rocker_port_ig_tbl(rocker_port, NULL, ROCKER_OP_FLAG_REMOVE);
5108e420114eSJiri Pirko 		rocker_world_port_fini(rocker_port);
510911ce2ba3SJiri Pirko 		unregister_netdev(rocker_port->dev);
5110e420114eSJiri Pirko 		rocker_world_port_post_fini(rocker_port);
511111ce2ba3SJiri Pirko 		free_netdev(rocker_port->dev);
511211ce2ba3SJiri Pirko 	}
5113e420114eSJiri Pirko 	rocker_world_fini(rocker);
511411ce2ba3SJiri Pirko 	kfree(rocker->ports);
511511ce2ba3SJiri Pirko }
511611ce2ba3SJiri Pirko 
511711ce2ba3SJiri Pirko static void rocker_port_dev_addr_init(struct rocker_port *rocker_port)
511811ce2ba3SJiri Pirko {
511911ce2ba3SJiri Pirko 	const struct rocker *rocker = rocker_port->rocker;
512011ce2ba3SJiri Pirko 	const struct pci_dev *pdev = rocker->pdev;
512111ce2ba3SJiri Pirko 	int err;
512211ce2ba3SJiri Pirko 
512311ce2ba3SJiri Pirko 	err = rocker_cmd_get_port_settings_macaddr(rocker_port,
512411ce2ba3SJiri Pirko 						   rocker_port->dev->dev_addr);
512511ce2ba3SJiri Pirko 	if (err) {
512611ce2ba3SJiri Pirko 		dev_warn(&pdev->dev, "failed to get mac address, using random\n");
512711ce2ba3SJiri Pirko 		eth_hw_addr_random(rocker_port->dev);
512811ce2ba3SJiri Pirko 	}
512911ce2ba3SJiri Pirko }
513011ce2ba3SJiri Pirko 
513111ce2ba3SJiri Pirko static int rocker_probe_port(struct rocker *rocker, unsigned int port_number)
513211ce2ba3SJiri Pirko {
513311ce2ba3SJiri Pirko 	const struct pci_dev *pdev = rocker->pdev;
513411ce2ba3SJiri Pirko 	struct rocker_port *rocker_port;
513511ce2ba3SJiri Pirko 	struct net_device *dev;
513611ce2ba3SJiri Pirko 	u16 untagged_vid = 0;
513711ce2ba3SJiri Pirko 	int err;
513811ce2ba3SJiri Pirko 
513911ce2ba3SJiri Pirko 	dev = alloc_etherdev(sizeof(struct rocker_port));
514011ce2ba3SJiri Pirko 	if (!dev)
514111ce2ba3SJiri Pirko 		return -ENOMEM;
514211ce2ba3SJiri Pirko 	rocker_port = netdev_priv(dev);
514311ce2ba3SJiri Pirko 	rocker_port->dev = dev;
514411ce2ba3SJiri Pirko 	rocker_port->rocker = rocker;
514511ce2ba3SJiri Pirko 	rocker_port->port_number = port_number;
514611ce2ba3SJiri Pirko 	rocker_port->pport = port_number + 1;
514711ce2ba3SJiri Pirko 	rocker_port->brport_flags = BR_LEARNING | BR_LEARNING_SYNC;
514811ce2ba3SJiri Pirko 	rocker_port->ageing_time = BR_DEFAULT_AGEING_TIME;
514911ce2ba3SJiri Pirko 
5150e420114eSJiri Pirko 	err = rocker_world_check_init(rocker_port);
5151e420114eSJiri Pirko 	if (err) {
5152e420114eSJiri Pirko 		dev_err(&pdev->dev, "world init failed\n");
5153e420114eSJiri Pirko 		goto err_world_check_init;
5154e420114eSJiri Pirko 	}
5155e420114eSJiri Pirko 
515611ce2ba3SJiri Pirko 	rocker_port_dev_addr_init(rocker_port);
515711ce2ba3SJiri Pirko 	dev->netdev_ops = &rocker_port_netdev_ops;
515811ce2ba3SJiri Pirko 	dev->ethtool_ops = &rocker_port_ethtool_ops;
515911ce2ba3SJiri Pirko 	dev->switchdev_ops = &rocker_port_switchdev_ops;
516011ce2ba3SJiri Pirko 	netif_tx_napi_add(dev, &rocker_port->napi_tx, rocker_port_poll_tx,
516111ce2ba3SJiri Pirko 			  NAPI_POLL_WEIGHT);
516211ce2ba3SJiri Pirko 	netif_napi_add(dev, &rocker_port->napi_rx, rocker_port_poll_rx,
516311ce2ba3SJiri Pirko 		       NAPI_POLL_WEIGHT);
516411ce2ba3SJiri Pirko 	rocker_carrier_init(rocker_port);
516511ce2ba3SJiri Pirko 
516611ce2ba3SJiri Pirko 	dev->features |= NETIF_F_NETNS_LOCAL | NETIF_F_SG;
516711ce2ba3SJiri Pirko 
5168e420114eSJiri Pirko 	err = rocker_world_port_pre_init(rocker_port);
5169e420114eSJiri Pirko 	if (err) {
5170e420114eSJiri Pirko 		dev_err(&pdev->dev, "port world pre-init failed\n");
5171e420114eSJiri Pirko 		goto err_world_port_pre_init;
5172e420114eSJiri Pirko 	}
517311ce2ba3SJiri Pirko 	err = register_netdev(dev);
517411ce2ba3SJiri Pirko 	if (err) {
517511ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "register_netdev failed\n");
517611ce2ba3SJiri Pirko 		goto err_register_netdev;
517711ce2ba3SJiri Pirko 	}
517811ce2ba3SJiri Pirko 	rocker->ports[port_number] = rocker_port;
517911ce2ba3SJiri Pirko 
5180e420114eSJiri Pirko 	err = rocker_world_port_init(rocker_port);
5181e420114eSJiri Pirko 	if (err) {
5182e420114eSJiri Pirko 		dev_err(&pdev->dev, "port world init failed\n");
5183e420114eSJiri Pirko 		goto err_world_port_init;
5184e420114eSJiri Pirko 	}
5185e420114eSJiri Pirko 
518611ce2ba3SJiri Pirko 	switchdev_port_fwd_mark_set(rocker_port->dev, NULL, false);
518711ce2ba3SJiri Pirko 
5188c1fe922eSJiri Pirko 	rocker_port_set_learning(rocker_port, NULL,
5189c1fe922eSJiri Pirko 				 !!(rocker_port->brport_flags & BR_LEARNING));
519011ce2ba3SJiri Pirko 
519111ce2ba3SJiri Pirko 	err = rocker_port_ig_tbl(rocker_port, NULL, 0);
519211ce2ba3SJiri Pirko 	if (err) {
519311ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev, "install ig port table failed\n");
519411ce2ba3SJiri Pirko 		goto err_port_ig_tbl;
519511ce2ba3SJiri Pirko 	}
519611ce2ba3SJiri Pirko 
519711ce2ba3SJiri Pirko 	rocker_port->internal_vlan_id =
519811ce2ba3SJiri Pirko 		rocker_port_internal_vlan_id_get(rocker_port, dev->ifindex);
519911ce2ba3SJiri Pirko 
520011ce2ba3SJiri Pirko 	err = rocker_port_vlan_add(rocker_port, NULL, untagged_vid, 0);
520111ce2ba3SJiri Pirko 	if (err) {
520211ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev, "install untagged VLAN failed\n");
520311ce2ba3SJiri Pirko 		goto err_untagged_vlan;
520411ce2ba3SJiri Pirko 	}
520511ce2ba3SJiri Pirko 
520611ce2ba3SJiri Pirko 	return 0;
520711ce2ba3SJiri Pirko 
520811ce2ba3SJiri Pirko err_untagged_vlan:
520911ce2ba3SJiri Pirko 	rocker_port_ig_tbl(rocker_port, NULL, ROCKER_OP_FLAG_REMOVE);
521011ce2ba3SJiri Pirko err_port_ig_tbl:
5211e420114eSJiri Pirko 	rocker_world_port_fini(rocker_port);
5212e420114eSJiri Pirko err_world_port_init:
521311ce2ba3SJiri Pirko 	rocker->ports[port_number] = NULL;
521411ce2ba3SJiri Pirko 	unregister_netdev(dev);
521511ce2ba3SJiri Pirko err_register_netdev:
5216e420114eSJiri Pirko 	rocker_world_port_post_fini(rocker_port);
5217e420114eSJiri Pirko err_world_port_pre_init:
5218e420114eSJiri Pirko err_world_check_init:
521911ce2ba3SJiri Pirko 	free_netdev(dev);
522011ce2ba3SJiri Pirko 	return err;
522111ce2ba3SJiri Pirko }
522211ce2ba3SJiri Pirko 
522311ce2ba3SJiri Pirko static int rocker_probe_ports(struct rocker *rocker)
522411ce2ba3SJiri Pirko {
522511ce2ba3SJiri Pirko 	int i;
522611ce2ba3SJiri Pirko 	size_t alloc_size;
522711ce2ba3SJiri Pirko 	int err;
522811ce2ba3SJiri Pirko 
522911ce2ba3SJiri Pirko 	alloc_size = sizeof(struct rocker_port *) * rocker->port_count;
523011ce2ba3SJiri Pirko 	rocker->ports = kzalloc(alloc_size, GFP_KERNEL);
523111ce2ba3SJiri Pirko 	if (!rocker->ports)
523211ce2ba3SJiri Pirko 		return -ENOMEM;
523311ce2ba3SJiri Pirko 	for (i = 0; i < rocker->port_count; i++) {
523411ce2ba3SJiri Pirko 		err = rocker_probe_port(rocker, i);
523511ce2ba3SJiri Pirko 		if (err)
523611ce2ba3SJiri Pirko 			goto remove_ports;
523711ce2ba3SJiri Pirko 	}
523811ce2ba3SJiri Pirko 	return 0;
523911ce2ba3SJiri Pirko 
524011ce2ba3SJiri Pirko remove_ports:
524111ce2ba3SJiri Pirko 	rocker_remove_ports(rocker);
524211ce2ba3SJiri Pirko 	return err;
524311ce2ba3SJiri Pirko }
524411ce2ba3SJiri Pirko 
524511ce2ba3SJiri Pirko static int rocker_msix_init(struct rocker *rocker)
524611ce2ba3SJiri Pirko {
524711ce2ba3SJiri Pirko 	struct pci_dev *pdev = rocker->pdev;
524811ce2ba3SJiri Pirko 	int msix_entries;
524911ce2ba3SJiri Pirko 	int i;
525011ce2ba3SJiri Pirko 	int err;
525111ce2ba3SJiri Pirko 
525211ce2ba3SJiri Pirko 	msix_entries = pci_msix_vec_count(pdev);
525311ce2ba3SJiri Pirko 	if (msix_entries < 0)
525411ce2ba3SJiri Pirko 		return msix_entries;
525511ce2ba3SJiri Pirko 
525611ce2ba3SJiri Pirko 	if (msix_entries != ROCKER_MSIX_VEC_COUNT(rocker->port_count))
525711ce2ba3SJiri Pirko 		return -EINVAL;
525811ce2ba3SJiri Pirko 
525911ce2ba3SJiri Pirko 	rocker->msix_entries = kmalloc_array(msix_entries,
526011ce2ba3SJiri Pirko 					     sizeof(struct msix_entry),
526111ce2ba3SJiri Pirko 					     GFP_KERNEL);
526211ce2ba3SJiri Pirko 	if (!rocker->msix_entries)
526311ce2ba3SJiri Pirko 		return -ENOMEM;
526411ce2ba3SJiri Pirko 
526511ce2ba3SJiri Pirko 	for (i = 0; i < msix_entries; i++)
526611ce2ba3SJiri Pirko 		rocker->msix_entries[i].entry = i;
526711ce2ba3SJiri Pirko 
526811ce2ba3SJiri Pirko 	err = pci_enable_msix_exact(pdev, rocker->msix_entries, msix_entries);
526911ce2ba3SJiri Pirko 	if (err < 0)
527011ce2ba3SJiri Pirko 		goto err_enable_msix;
527111ce2ba3SJiri Pirko 
527211ce2ba3SJiri Pirko 	return 0;
527311ce2ba3SJiri Pirko 
527411ce2ba3SJiri Pirko err_enable_msix:
527511ce2ba3SJiri Pirko 	kfree(rocker->msix_entries);
527611ce2ba3SJiri Pirko 	return err;
527711ce2ba3SJiri Pirko }
527811ce2ba3SJiri Pirko 
527911ce2ba3SJiri Pirko static void rocker_msix_fini(const struct rocker *rocker)
528011ce2ba3SJiri Pirko {
528111ce2ba3SJiri Pirko 	pci_disable_msix(rocker->pdev);
528211ce2ba3SJiri Pirko 	kfree(rocker->msix_entries);
528311ce2ba3SJiri Pirko }
528411ce2ba3SJiri Pirko 
528511ce2ba3SJiri Pirko static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id)
528611ce2ba3SJiri Pirko {
528711ce2ba3SJiri Pirko 	struct rocker *rocker;
528811ce2ba3SJiri Pirko 	int err;
528911ce2ba3SJiri Pirko 
529011ce2ba3SJiri Pirko 	rocker = kzalloc(sizeof(*rocker), GFP_KERNEL);
529111ce2ba3SJiri Pirko 	if (!rocker)
529211ce2ba3SJiri Pirko 		return -ENOMEM;
529311ce2ba3SJiri Pirko 
529411ce2ba3SJiri Pirko 	err = pci_enable_device(pdev);
529511ce2ba3SJiri Pirko 	if (err) {
529611ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "pci_enable_device failed\n");
529711ce2ba3SJiri Pirko 		goto err_pci_enable_device;
529811ce2ba3SJiri Pirko 	}
529911ce2ba3SJiri Pirko 
530011ce2ba3SJiri Pirko 	err = pci_request_regions(pdev, rocker_driver_name);
530111ce2ba3SJiri Pirko 	if (err) {
530211ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "pci_request_regions failed\n");
530311ce2ba3SJiri Pirko 		goto err_pci_request_regions;
530411ce2ba3SJiri Pirko 	}
530511ce2ba3SJiri Pirko 
530611ce2ba3SJiri Pirko 	err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
530711ce2ba3SJiri Pirko 	if (!err) {
530811ce2ba3SJiri Pirko 		err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
530911ce2ba3SJiri Pirko 		if (err) {
531011ce2ba3SJiri Pirko 			dev_err(&pdev->dev, "pci_set_consistent_dma_mask failed\n");
531111ce2ba3SJiri Pirko 			goto err_pci_set_dma_mask;
531211ce2ba3SJiri Pirko 		}
531311ce2ba3SJiri Pirko 	} else {
531411ce2ba3SJiri Pirko 		err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
531511ce2ba3SJiri Pirko 		if (err) {
531611ce2ba3SJiri Pirko 			dev_err(&pdev->dev, "pci_set_dma_mask failed\n");
531711ce2ba3SJiri Pirko 			goto err_pci_set_dma_mask;
531811ce2ba3SJiri Pirko 		}
531911ce2ba3SJiri Pirko 	}
532011ce2ba3SJiri Pirko 
532111ce2ba3SJiri Pirko 	if (pci_resource_len(pdev, 0) < ROCKER_PCI_BAR0_SIZE) {
532211ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "invalid PCI region size\n");
532311ce2ba3SJiri Pirko 		err = -EINVAL;
532411ce2ba3SJiri Pirko 		goto err_pci_resource_len_check;
532511ce2ba3SJiri Pirko 	}
532611ce2ba3SJiri Pirko 
532711ce2ba3SJiri Pirko 	rocker->hw_addr = ioremap(pci_resource_start(pdev, 0),
532811ce2ba3SJiri Pirko 				  pci_resource_len(pdev, 0));
532911ce2ba3SJiri Pirko 	if (!rocker->hw_addr) {
533011ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "ioremap failed\n");
533111ce2ba3SJiri Pirko 		err = -EIO;
533211ce2ba3SJiri Pirko 		goto err_ioremap;
533311ce2ba3SJiri Pirko 	}
533411ce2ba3SJiri Pirko 	pci_set_master(pdev);
533511ce2ba3SJiri Pirko 
533611ce2ba3SJiri Pirko 	rocker->pdev = pdev;
533711ce2ba3SJiri Pirko 	pci_set_drvdata(pdev, rocker);
533811ce2ba3SJiri Pirko 
533911ce2ba3SJiri Pirko 	rocker->port_count = rocker_read32(rocker, PORT_PHYS_COUNT);
534011ce2ba3SJiri Pirko 
534111ce2ba3SJiri Pirko 	err = rocker_msix_init(rocker);
534211ce2ba3SJiri Pirko 	if (err) {
534311ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "MSI-X init failed\n");
534411ce2ba3SJiri Pirko 		goto err_msix_init;
534511ce2ba3SJiri Pirko 	}
534611ce2ba3SJiri Pirko 
534711ce2ba3SJiri Pirko 	err = rocker_basic_hw_test(rocker);
534811ce2ba3SJiri Pirko 	if (err) {
534911ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "basic hw test failed\n");
535011ce2ba3SJiri Pirko 		goto err_basic_hw_test;
535111ce2ba3SJiri Pirko 	}
535211ce2ba3SJiri Pirko 
535311ce2ba3SJiri Pirko 	rocker_write32(rocker, CONTROL, ROCKER_CONTROL_RESET);
535411ce2ba3SJiri Pirko 
535511ce2ba3SJiri Pirko 	err = rocker_dma_rings_init(rocker);
535611ce2ba3SJiri Pirko 	if (err)
535711ce2ba3SJiri Pirko 		goto err_dma_rings_init;
535811ce2ba3SJiri Pirko 
535911ce2ba3SJiri Pirko 	err = request_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_CMD),
536011ce2ba3SJiri Pirko 			  rocker_cmd_irq_handler, 0,
536111ce2ba3SJiri Pirko 			  rocker_driver_name, rocker);
536211ce2ba3SJiri Pirko 	if (err) {
536311ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "cannot assign cmd irq\n");
536411ce2ba3SJiri Pirko 		goto err_request_cmd_irq;
536511ce2ba3SJiri Pirko 	}
536611ce2ba3SJiri Pirko 
536711ce2ba3SJiri Pirko 	err = request_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_EVENT),
536811ce2ba3SJiri Pirko 			  rocker_event_irq_handler, 0,
536911ce2ba3SJiri Pirko 			  rocker_driver_name, rocker);
537011ce2ba3SJiri Pirko 	if (err) {
537111ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "cannot assign event irq\n");
537211ce2ba3SJiri Pirko 		goto err_request_event_irq;
537311ce2ba3SJiri Pirko 	}
537411ce2ba3SJiri Pirko 
537511ce2ba3SJiri Pirko 	rocker->hw.id = rocker_read64(rocker, SWITCH_ID);
537611ce2ba3SJiri Pirko 
537711ce2ba3SJiri Pirko 	err = rocker_init_tbls(rocker);
537811ce2ba3SJiri Pirko 	if (err) {
537911ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "cannot init rocker tables\n");
538011ce2ba3SJiri Pirko 		goto err_init_tbls;
538111ce2ba3SJiri Pirko 	}
538211ce2ba3SJiri Pirko 
538311ce2ba3SJiri Pirko 	setup_timer(&rocker->fdb_cleanup_timer, rocker_fdb_cleanup,
538411ce2ba3SJiri Pirko 		    (unsigned long) rocker);
538511ce2ba3SJiri Pirko 	mod_timer(&rocker->fdb_cleanup_timer, jiffies);
538611ce2ba3SJiri Pirko 
538711ce2ba3SJiri Pirko 	err = rocker_probe_ports(rocker);
538811ce2ba3SJiri Pirko 	if (err) {
538911ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "failed to probe ports\n");
539011ce2ba3SJiri Pirko 		goto err_probe_ports;
539111ce2ba3SJiri Pirko 	}
539211ce2ba3SJiri Pirko 
539311ce2ba3SJiri Pirko 	dev_info(&pdev->dev, "Rocker switch with id %*phN\n",
539411ce2ba3SJiri Pirko 		 (int)sizeof(rocker->hw.id), &rocker->hw.id);
539511ce2ba3SJiri Pirko 
539611ce2ba3SJiri Pirko 	return 0;
539711ce2ba3SJiri Pirko 
539811ce2ba3SJiri Pirko err_probe_ports:
539911ce2ba3SJiri Pirko 	del_timer_sync(&rocker->fdb_cleanup_timer);
540011ce2ba3SJiri Pirko 	rocker_free_tbls(rocker);
540111ce2ba3SJiri Pirko err_init_tbls:
540211ce2ba3SJiri Pirko 	free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_EVENT), rocker);
540311ce2ba3SJiri Pirko err_request_event_irq:
540411ce2ba3SJiri Pirko 	free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_CMD), rocker);
540511ce2ba3SJiri Pirko err_request_cmd_irq:
540611ce2ba3SJiri Pirko 	rocker_dma_rings_fini(rocker);
540711ce2ba3SJiri Pirko err_dma_rings_init:
540811ce2ba3SJiri Pirko err_basic_hw_test:
540911ce2ba3SJiri Pirko 	rocker_msix_fini(rocker);
541011ce2ba3SJiri Pirko err_msix_init:
541111ce2ba3SJiri Pirko 	iounmap(rocker->hw_addr);
541211ce2ba3SJiri Pirko err_ioremap:
541311ce2ba3SJiri Pirko err_pci_resource_len_check:
541411ce2ba3SJiri Pirko err_pci_set_dma_mask:
541511ce2ba3SJiri Pirko 	pci_release_regions(pdev);
541611ce2ba3SJiri Pirko err_pci_request_regions:
541711ce2ba3SJiri Pirko 	pci_disable_device(pdev);
541811ce2ba3SJiri Pirko err_pci_enable_device:
541911ce2ba3SJiri Pirko 	kfree(rocker);
542011ce2ba3SJiri Pirko 	return err;
542111ce2ba3SJiri Pirko }
542211ce2ba3SJiri Pirko 
542311ce2ba3SJiri Pirko static void rocker_remove(struct pci_dev *pdev)
542411ce2ba3SJiri Pirko {
542511ce2ba3SJiri Pirko 	struct rocker *rocker = pci_get_drvdata(pdev);
542611ce2ba3SJiri Pirko 
542711ce2ba3SJiri Pirko 	del_timer_sync(&rocker->fdb_cleanup_timer);
542811ce2ba3SJiri Pirko 	rocker_free_tbls(rocker);
542911ce2ba3SJiri Pirko 	rocker_write32(rocker, CONTROL, ROCKER_CONTROL_RESET);
543011ce2ba3SJiri Pirko 	rocker_remove_ports(rocker);
543111ce2ba3SJiri Pirko 	free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_EVENT), rocker);
543211ce2ba3SJiri Pirko 	free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_CMD), rocker);
543311ce2ba3SJiri Pirko 	rocker_dma_rings_fini(rocker);
543411ce2ba3SJiri Pirko 	rocker_msix_fini(rocker);
543511ce2ba3SJiri Pirko 	iounmap(rocker->hw_addr);
543611ce2ba3SJiri Pirko 	pci_release_regions(rocker->pdev);
543711ce2ba3SJiri Pirko 	pci_disable_device(rocker->pdev);
543811ce2ba3SJiri Pirko 	kfree(rocker);
543911ce2ba3SJiri Pirko }
544011ce2ba3SJiri Pirko 
544111ce2ba3SJiri Pirko static struct pci_driver rocker_pci_driver = {
544211ce2ba3SJiri Pirko 	.name		= rocker_driver_name,
544311ce2ba3SJiri Pirko 	.id_table	= rocker_pci_id_table,
544411ce2ba3SJiri Pirko 	.probe		= rocker_probe,
544511ce2ba3SJiri Pirko 	.remove		= rocker_remove,
544611ce2ba3SJiri Pirko };
544711ce2ba3SJiri Pirko 
544811ce2ba3SJiri Pirko /************************************
544911ce2ba3SJiri Pirko  * Net device notifier event handler
545011ce2ba3SJiri Pirko  ************************************/
545111ce2ba3SJiri Pirko 
545211ce2ba3SJiri Pirko static bool rocker_port_dev_check(const struct net_device *dev)
545311ce2ba3SJiri Pirko {
545411ce2ba3SJiri Pirko 	return dev->netdev_ops == &rocker_port_netdev_ops;
545511ce2ba3SJiri Pirko }
545611ce2ba3SJiri Pirko 
545711ce2ba3SJiri Pirko static int rocker_port_bridge_join(struct rocker_port *rocker_port,
545811ce2ba3SJiri Pirko 				   struct net_device *bridge)
545911ce2ba3SJiri Pirko {
546011ce2ba3SJiri Pirko 	u16 untagged_vid = 0;
546111ce2ba3SJiri Pirko 	int err;
546211ce2ba3SJiri Pirko 
546311ce2ba3SJiri Pirko 	/* Port is joining bridge, so the internal VLAN for the
546411ce2ba3SJiri Pirko 	 * port is going to change to the bridge internal VLAN.
546511ce2ba3SJiri Pirko 	 * Let's remove untagged VLAN (vid=0) from port and
546611ce2ba3SJiri Pirko 	 * re-add once internal VLAN has changed.
546711ce2ba3SJiri Pirko 	 */
546811ce2ba3SJiri Pirko 
546911ce2ba3SJiri Pirko 	err = rocker_port_vlan_del(rocker_port, untagged_vid, 0);
547011ce2ba3SJiri Pirko 	if (err)
547111ce2ba3SJiri Pirko 		return err;
547211ce2ba3SJiri Pirko 
547311ce2ba3SJiri Pirko 	rocker_port_internal_vlan_id_put(rocker_port,
547411ce2ba3SJiri Pirko 					 rocker_port->dev->ifindex);
547511ce2ba3SJiri Pirko 	rocker_port->internal_vlan_id =
547611ce2ba3SJiri Pirko 		rocker_port_internal_vlan_id_get(rocker_port, bridge->ifindex);
547711ce2ba3SJiri Pirko 
547811ce2ba3SJiri Pirko 	rocker_port->bridge_dev = bridge;
547911ce2ba3SJiri Pirko 	switchdev_port_fwd_mark_set(rocker_port->dev, bridge, true);
548011ce2ba3SJiri Pirko 
548111ce2ba3SJiri Pirko 	return rocker_port_vlan_add(rocker_port, NULL, untagged_vid, 0);
548211ce2ba3SJiri Pirko }
548311ce2ba3SJiri Pirko 
548411ce2ba3SJiri Pirko static int rocker_port_bridge_leave(struct rocker_port *rocker_port)
548511ce2ba3SJiri Pirko {
548611ce2ba3SJiri Pirko 	u16 untagged_vid = 0;
548711ce2ba3SJiri Pirko 	int err;
548811ce2ba3SJiri Pirko 
548911ce2ba3SJiri Pirko 	err = rocker_port_vlan_del(rocker_port, untagged_vid, 0);
549011ce2ba3SJiri Pirko 	if (err)
549111ce2ba3SJiri Pirko 		return err;
549211ce2ba3SJiri Pirko 
549311ce2ba3SJiri Pirko 	rocker_port_internal_vlan_id_put(rocker_port,
549411ce2ba3SJiri Pirko 					 rocker_port->bridge_dev->ifindex);
549511ce2ba3SJiri Pirko 	rocker_port->internal_vlan_id =
549611ce2ba3SJiri Pirko 		rocker_port_internal_vlan_id_get(rocker_port,
549711ce2ba3SJiri Pirko 						 rocker_port->dev->ifindex);
549811ce2ba3SJiri Pirko 
549911ce2ba3SJiri Pirko 	switchdev_port_fwd_mark_set(rocker_port->dev, rocker_port->bridge_dev,
550011ce2ba3SJiri Pirko 				    false);
550111ce2ba3SJiri Pirko 	rocker_port->bridge_dev = NULL;
550211ce2ba3SJiri Pirko 
550311ce2ba3SJiri Pirko 	err = rocker_port_vlan_add(rocker_port, NULL, untagged_vid, 0);
550411ce2ba3SJiri Pirko 	if (err)
550511ce2ba3SJiri Pirko 		return err;
550611ce2ba3SJiri Pirko 
550711ce2ba3SJiri Pirko 	if (rocker_port->dev->flags & IFF_UP)
550811ce2ba3SJiri Pirko 		err = rocker_port_fwd_enable(rocker_port, NULL, 0);
550911ce2ba3SJiri Pirko 
551011ce2ba3SJiri Pirko 	return err;
551111ce2ba3SJiri Pirko }
551211ce2ba3SJiri Pirko 
551311ce2ba3SJiri Pirko static int rocker_port_ovs_changed(struct rocker_port *rocker_port,
551411ce2ba3SJiri Pirko 				   struct net_device *master)
551511ce2ba3SJiri Pirko {
551611ce2ba3SJiri Pirko 	int err;
551711ce2ba3SJiri Pirko 
551811ce2ba3SJiri Pirko 	rocker_port->bridge_dev = master;
551911ce2ba3SJiri Pirko 
552011ce2ba3SJiri Pirko 	err = rocker_port_fwd_disable(rocker_port, NULL, 0);
552111ce2ba3SJiri Pirko 	if (err)
552211ce2ba3SJiri Pirko 		return err;
552311ce2ba3SJiri Pirko 	err = rocker_port_fwd_enable(rocker_port, NULL, 0);
552411ce2ba3SJiri Pirko 
552511ce2ba3SJiri Pirko 	return err;
552611ce2ba3SJiri Pirko }
552711ce2ba3SJiri Pirko 
552811ce2ba3SJiri Pirko static int rocker_port_master_linked(struct rocker_port *rocker_port,
552911ce2ba3SJiri Pirko 				     struct net_device *master)
553011ce2ba3SJiri Pirko {
553111ce2ba3SJiri Pirko 	int err = 0;
553211ce2ba3SJiri Pirko 
553311ce2ba3SJiri Pirko 	if (netif_is_bridge_master(master))
553411ce2ba3SJiri Pirko 		err = rocker_port_bridge_join(rocker_port, master);
553511ce2ba3SJiri Pirko 	else if (netif_is_ovs_master(master))
553611ce2ba3SJiri Pirko 		err = rocker_port_ovs_changed(rocker_port, master);
553711ce2ba3SJiri Pirko 	return err;
553811ce2ba3SJiri Pirko }
553911ce2ba3SJiri Pirko 
554011ce2ba3SJiri Pirko static int rocker_port_master_unlinked(struct rocker_port *rocker_port)
554111ce2ba3SJiri Pirko {
554211ce2ba3SJiri Pirko 	int err = 0;
554311ce2ba3SJiri Pirko 
554411ce2ba3SJiri Pirko 	if (rocker_port_is_bridged(rocker_port))
554511ce2ba3SJiri Pirko 		err = rocker_port_bridge_leave(rocker_port);
554611ce2ba3SJiri Pirko 	else if (rocker_port_is_ovsed(rocker_port))
554711ce2ba3SJiri Pirko 		err = rocker_port_ovs_changed(rocker_port, NULL);
554811ce2ba3SJiri Pirko 	return err;
554911ce2ba3SJiri Pirko }
555011ce2ba3SJiri Pirko 
555111ce2ba3SJiri Pirko static int rocker_netdevice_event(struct notifier_block *unused,
555211ce2ba3SJiri Pirko 				  unsigned long event, void *ptr)
555311ce2ba3SJiri Pirko {
555411ce2ba3SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
555511ce2ba3SJiri Pirko 	struct netdev_notifier_changeupper_info *info;
555611ce2ba3SJiri Pirko 	struct rocker_port *rocker_port;
555711ce2ba3SJiri Pirko 	int err;
555811ce2ba3SJiri Pirko 
555911ce2ba3SJiri Pirko 	if (!rocker_port_dev_check(dev))
556011ce2ba3SJiri Pirko 		return NOTIFY_DONE;
556111ce2ba3SJiri Pirko 
556211ce2ba3SJiri Pirko 	switch (event) {
556311ce2ba3SJiri Pirko 	case NETDEV_CHANGEUPPER:
556411ce2ba3SJiri Pirko 		info = ptr;
556511ce2ba3SJiri Pirko 		if (!info->master)
556611ce2ba3SJiri Pirko 			goto out;
556711ce2ba3SJiri Pirko 		rocker_port = netdev_priv(dev);
556811ce2ba3SJiri Pirko 		if (info->linking) {
5569e420114eSJiri Pirko 			err = rocker_world_port_master_linked(rocker_port,
5570e420114eSJiri Pirko 							      info->upper_dev);
5571e420114eSJiri Pirko 			if (err)
5572e420114eSJiri Pirko 				netdev_warn(dev, "failed to reflect master linked (err %d)\n",
5573e420114eSJiri Pirko 					    err);
557411ce2ba3SJiri Pirko 			err = rocker_port_master_linked(rocker_port,
557511ce2ba3SJiri Pirko 							info->upper_dev);
557611ce2ba3SJiri Pirko 			if (err)
557711ce2ba3SJiri Pirko 				netdev_warn(dev, "failed to reflect master linked (err %d)\n",
557811ce2ba3SJiri Pirko 					    err);
557911ce2ba3SJiri Pirko 		} else {
5580e420114eSJiri Pirko 			err = rocker_world_port_master_unlinked(rocker_port,
5581e420114eSJiri Pirko 								info->upper_dev);
5582e420114eSJiri Pirko 			if (err)
5583e420114eSJiri Pirko 				netdev_warn(dev, "failed to reflect master unlinked (err %d)\n",
5584e420114eSJiri Pirko 					    err);
558511ce2ba3SJiri Pirko 			err = rocker_port_master_unlinked(rocker_port);
558611ce2ba3SJiri Pirko 			if (err)
558711ce2ba3SJiri Pirko 				netdev_warn(dev, "failed to reflect master unlinked (err %d)\n",
558811ce2ba3SJiri Pirko 					    err);
558911ce2ba3SJiri Pirko 		}
559011ce2ba3SJiri Pirko 		break;
559111ce2ba3SJiri Pirko 	}
559211ce2ba3SJiri Pirko out:
559311ce2ba3SJiri Pirko 	return NOTIFY_DONE;
559411ce2ba3SJiri Pirko }
559511ce2ba3SJiri Pirko 
559611ce2ba3SJiri Pirko static struct notifier_block rocker_netdevice_nb __read_mostly = {
559711ce2ba3SJiri Pirko 	.notifier_call = rocker_netdevice_event,
559811ce2ba3SJiri Pirko };
559911ce2ba3SJiri Pirko 
560011ce2ba3SJiri Pirko /************************************
560111ce2ba3SJiri Pirko  * Net event notifier event handler
560211ce2ba3SJiri Pirko  ************************************/
560311ce2ba3SJiri Pirko 
560411ce2ba3SJiri Pirko static int rocker_neigh_update(struct net_device *dev, struct neighbour *n)
560511ce2ba3SJiri Pirko {
560611ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(dev);
560711ce2ba3SJiri Pirko 	int flags = (n->nud_state & NUD_VALID ? 0 : ROCKER_OP_FLAG_REMOVE) |
560811ce2ba3SJiri Pirko 		    ROCKER_OP_FLAG_NOWAIT;
560911ce2ba3SJiri Pirko 	__be32 ip_addr = *(__be32 *)n->primary_key;
561011ce2ba3SJiri Pirko 
561111ce2ba3SJiri Pirko 	return rocker_port_ipv4_neigh(rocker_port, NULL, flags, ip_addr, n->ha);
561211ce2ba3SJiri Pirko }
561311ce2ba3SJiri Pirko 
561411ce2ba3SJiri Pirko static int rocker_netevent_event(struct notifier_block *unused,
561511ce2ba3SJiri Pirko 				 unsigned long event, void *ptr)
561611ce2ba3SJiri Pirko {
5617e420114eSJiri Pirko 	struct rocker_port *rocker_port;
561811ce2ba3SJiri Pirko 	struct net_device *dev;
561911ce2ba3SJiri Pirko 	struct neighbour *n = ptr;
562011ce2ba3SJiri Pirko 	int err;
562111ce2ba3SJiri Pirko 
562211ce2ba3SJiri Pirko 	switch (event) {
562311ce2ba3SJiri Pirko 	case NETEVENT_NEIGH_UPDATE:
562411ce2ba3SJiri Pirko 		if (n->tbl != &arp_tbl)
562511ce2ba3SJiri Pirko 			return NOTIFY_DONE;
562611ce2ba3SJiri Pirko 		dev = n->dev;
562711ce2ba3SJiri Pirko 		if (!rocker_port_dev_check(dev))
562811ce2ba3SJiri Pirko 			return NOTIFY_DONE;
5629e420114eSJiri Pirko 		rocker_port = netdev_priv(dev);
5630e420114eSJiri Pirko 		err = rocker_world_port_neigh_update(rocker_port, n);
5631e420114eSJiri Pirko 		if (err)
5632e420114eSJiri Pirko 			netdev_warn(dev, "failed to handle neigh update (err %d)\n",
5633e420114eSJiri Pirko 				    err);
563411ce2ba3SJiri Pirko 		err = rocker_neigh_update(dev, n);
563511ce2ba3SJiri Pirko 		if (err)
563611ce2ba3SJiri Pirko 			netdev_warn(dev,
563711ce2ba3SJiri Pirko 				    "failed to handle neigh update (err %d)\n",
563811ce2ba3SJiri Pirko 				    err);
563911ce2ba3SJiri Pirko 		break;
564011ce2ba3SJiri Pirko 	}
564111ce2ba3SJiri Pirko 
564211ce2ba3SJiri Pirko 	return NOTIFY_DONE;
564311ce2ba3SJiri Pirko }
564411ce2ba3SJiri Pirko 
564511ce2ba3SJiri Pirko static struct notifier_block rocker_netevent_nb __read_mostly = {
564611ce2ba3SJiri Pirko 	.notifier_call = rocker_netevent_event,
564711ce2ba3SJiri Pirko };
564811ce2ba3SJiri Pirko 
564911ce2ba3SJiri Pirko /***********************
565011ce2ba3SJiri Pirko  * Module init and exit
565111ce2ba3SJiri Pirko  ***********************/
565211ce2ba3SJiri Pirko 
565311ce2ba3SJiri Pirko static int __init rocker_module_init(void)
565411ce2ba3SJiri Pirko {
565511ce2ba3SJiri Pirko 	int err;
565611ce2ba3SJiri Pirko 
565711ce2ba3SJiri Pirko 	register_netdevice_notifier(&rocker_netdevice_nb);
565811ce2ba3SJiri Pirko 	register_netevent_notifier(&rocker_netevent_nb);
565911ce2ba3SJiri Pirko 	err = pci_register_driver(&rocker_pci_driver);
566011ce2ba3SJiri Pirko 	if (err)
566111ce2ba3SJiri Pirko 		goto err_pci_register_driver;
566211ce2ba3SJiri Pirko 	return 0;
566311ce2ba3SJiri Pirko 
566411ce2ba3SJiri Pirko err_pci_register_driver:
566511ce2ba3SJiri Pirko 	unregister_netevent_notifier(&rocker_netevent_nb);
566611ce2ba3SJiri Pirko 	unregister_netdevice_notifier(&rocker_netdevice_nb);
566711ce2ba3SJiri Pirko 	return err;
566811ce2ba3SJiri Pirko }
566911ce2ba3SJiri Pirko 
567011ce2ba3SJiri Pirko static void __exit rocker_module_exit(void)
567111ce2ba3SJiri Pirko {
567211ce2ba3SJiri Pirko 	unregister_netevent_notifier(&rocker_netevent_nb);
567311ce2ba3SJiri Pirko 	unregister_netdevice_notifier(&rocker_netdevice_nb);
567411ce2ba3SJiri Pirko 	pci_unregister_driver(&rocker_pci_driver);
567511ce2ba3SJiri Pirko }
567611ce2ba3SJiri Pirko 
567711ce2ba3SJiri Pirko module_init(rocker_module_init);
567811ce2ba3SJiri Pirko module_exit(rocker_module_exit);
567911ce2ba3SJiri Pirko 
568011ce2ba3SJiri Pirko MODULE_LICENSE("GPL v2");
568111ce2ba3SJiri Pirko MODULE_AUTHOR("Jiri Pirko <jiri@resnulli.us>");
568211ce2ba3SJiri Pirko MODULE_AUTHOR("Scott Feldman <sfeldma@gmail.com>");
568311ce2ba3SJiri Pirko MODULE_DESCRIPTION("Rocker switch device driver");
568411ce2ba3SJiri Pirko MODULE_DEVICE_TABLE(pci, rocker_pci_id_table);
5685