162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * drivers/net/ethernet/rocker/rocker_ofdpa.c - Rocker switch OF-DPA-like
462306a36Sopenharmony_ci *					        implementation
562306a36Sopenharmony_ci * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com>
662306a36Sopenharmony_ci * Copyright (c) 2014-2016 Jiri Pirko <jiri@mellanox.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/types.h>
1162306a36Sopenharmony_ci#include <linux/spinlock.h>
1262306a36Sopenharmony_ci#include <linux/hashtable.h>
1362306a36Sopenharmony_ci#include <linux/crc32.h>
1462306a36Sopenharmony_ci#include <linux/netdevice.h>
1562306a36Sopenharmony_ci#include <linux/inetdevice.h>
1662306a36Sopenharmony_ci#include <linux/if_vlan.h>
1762306a36Sopenharmony_ci#include <linux/if_bridge.h>
1862306a36Sopenharmony_ci#include <net/neighbour.h>
1962306a36Sopenharmony_ci#include <net/switchdev.h>
2062306a36Sopenharmony_ci#include <net/ip_fib.h>
2162306a36Sopenharmony_ci#include <net/nexthop.h>
2262306a36Sopenharmony_ci#include <net/arp.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "rocker.h"
2562306a36Sopenharmony_ci#include "rocker_tlv.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistruct ofdpa_flow_tbl_key {
2862306a36Sopenharmony_ci	u32 priority;
2962306a36Sopenharmony_ci	enum rocker_of_dpa_table_id tbl_id;
3062306a36Sopenharmony_ci	union {
3162306a36Sopenharmony_ci		struct {
3262306a36Sopenharmony_ci			u32 in_pport;
3362306a36Sopenharmony_ci			u32 in_pport_mask;
3462306a36Sopenharmony_ci			enum rocker_of_dpa_table_id goto_tbl;
3562306a36Sopenharmony_ci		} ig_port;
3662306a36Sopenharmony_ci		struct {
3762306a36Sopenharmony_ci			u32 in_pport;
3862306a36Sopenharmony_ci			__be16 vlan_id;
3962306a36Sopenharmony_ci			__be16 vlan_id_mask;
4062306a36Sopenharmony_ci			enum rocker_of_dpa_table_id goto_tbl;
4162306a36Sopenharmony_ci			bool untagged;
4262306a36Sopenharmony_ci			__be16 new_vlan_id;
4362306a36Sopenharmony_ci		} vlan;
4462306a36Sopenharmony_ci		struct {
4562306a36Sopenharmony_ci			u32 in_pport;
4662306a36Sopenharmony_ci			u32 in_pport_mask;
4762306a36Sopenharmony_ci			__be16 eth_type;
4862306a36Sopenharmony_ci			u8 eth_dst[ETH_ALEN];
4962306a36Sopenharmony_ci			u8 eth_dst_mask[ETH_ALEN];
5062306a36Sopenharmony_ci			__be16 vlan_id;
5162306a36Sopenharmony_ci			__be16 vlan_id_mask;
5262306a36Sopenharmony_ci			enum rocker_of_dpa_table_id goto_tbl;
5362306a36Sopenharmony_ci			bool copy_to_cpu;
5462306a36Sopenharmony_ci		} term_mac;
5562306a36Sopenharmony_ci		struct {
5662306a36Sopenharmony_ci			__be16 eth_type;
5762306a36Sopenharmony_ci			__be32 dst4;
5862306a36Sopenharmony_ci			__be32 dst4_mask;
5962306a36Sopenharmony_ci			enum rocker_of_dpa_table_id goto_tbl;
6062306a36Sopenharmony_ci			u32 group_id;
6162306a36Sopenharmony_ci		} ucast_routing;
6262306a36Sopenharmony_ci		struct {
6362306a36Sopenharmony_ci			u8 eth_dst[ETH_ALEN];
6462306a36Sopenharmony_ci			u8 eth_dst_mask[ETH_ALEN];
6562306a36Sopenharmony_ci			int has_eth_dst;
6662306a36Sopenharmony_ci			int has_eth_dst_mask;
6762306a36Sopenharmony_ci			__be16 vlan_id;
6862306a36Sopenharmony_ci			u32 tunnel_id;
6962306a36Sopenharmony_ci			enum rocker_of_dpa_table_id goto_tbl;
7062306a36Sopenharmony_ci			u32 group_id;
7162306a36Sopenharmony_ci			bool copy_to_cpu;
7262306a36Sopenharmony_ci		} bridge;
7362306a36Sopenharmony_ci		struct {
7462306a36Sopenharmony_ci			u32 in_pport;
7562306a36Sopenharmony_ci			u32 in_pport_mask;
7662306a36Sopenharmony_ci			u8 eth_src[ETH_ALEN];
7762306a36Sopenharmony_ci			u8 eth_src_mask[ETH_ALEN];
7862306a36Sopenharmony_ci			u8 eth_dst[ETH_ALEN];
7962306a36Sopenharmony_ci			u8 eth_dst_mask[ETH_ALEN];
8062306a36Sopenharmony_ci			__be16 eth_type;
8162306a36Sopenharmony_ci			__be16 vlan_id;
8262306a36Sopenharmony_ci			__be16 vlan_id_mask;
8362306a36Sopenharmony_ci			u8 ip_proto;
8462306a36Sopenharmony_ci			u8 ip_proto_mask;
8562306a36Sopenharmony_ci			u8 ip_tos;
8662306a36Sopenharmony_ci			u8 ip_tos_mask;
8762306a36Sopenharmony_ci			u32 group_id;
8862306a36Sopenharmony_ci		} acl;
8962306a36Sopenharmony_ci	};
9062306a36Sopenharmony_ci};
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistruct ofdpa_flow_tbl_entry {
9362306a36Sopenharmony_ci	struct hlist_node entry;
9462306a36Sopenharmony_ci	u32 cmd;
9562306a36Sopenharmony_ci	u64 cookie;
9662306a36Sopenharmony_ci	struct ofdpa_flow_tbl_key key;
9762306a36Sopenharmony_ci	size_t key_len;
9862306a36Sopenharmony_ci	u32 key_crc32; /* key */
9962306a36Sopenharmony_ci	struct fib_info *fi;
10062306a36Sopenharmony_ci};
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistruct ofdpa_group_tbl_entry {
10362306a36Sopenharmony_ci	struct hlist_node entry;
10462306a36Sopenharmony_ci	u32 cmd;
10562306a36Sopenharmony_ci	u32 group_id; /* key */
10662306a36Sopenharmony_ci	u16 group_count;
10762306a36Sopenharmony_ci	u32 *group_ids;
10862306a36Sopenharmony_ci	union {
10962306a36Sopenharmony_ci		struct {
11062306a36Sopenharmony_ci			u8 pop_vlan;
11162306a36Sopenharmony_ci		} l2_interface;
11262306a36Sopenharmony_ci		struct {
11362306a36Sopenharmony_ci			u8 eth_src[ETH_ALEN];
11462306a36Sopenharmony_ci			u8 eth_dst[ETH_ALEN];
11562306a36Sopenharmony_ci			__be16 vlan_id;
11662306a36Sopenharmony_ci			u32 group_id;
11762306a36Sopenharmony_ci		} l2_rewrite;
11862306a36Sopenharmony_ci		struct {
11962306a36Sopenharmony_ci			u8 eth_src[ETH_ALEN];
12062306a36Sopenharmony_ci			u8 eth_dst[ETH_ALEN];
12162306a36Sopenharmony_ci			__be16 vlan_id;
12262306a36Sopenharmony_ci			bool ttl_check;
12362306a36Sopenharmony_ci			u32 group_id;
12462306a36Sopenharmony_ci		} l3_unicast;
12562306a36Sopenharmony_ci	};
12662306a36Sopenharmony_ci};
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistruct ofdpa_fdb_tbl_entry {
12962306a36Sopenharmony_ci	struct hlist_node entry;
13062306a36Sopenharmony_ci	u32 key_crc32; /* key */
13162306a36Sopenharmony_ci	bool learned;
13262306a36Sopenharmony_ci	unsigned long touched;
13362306a36Sopenharmony_ci	struct ofdpa_fdb_tbl_key {
13462306a36Sopenharmony_ci		struct ofdpa_port *ofdpa_port;
13562306a36Sopenharmony_ci		u8 addr[ETH_ALEN];
13662306a36Sopenharmony_ci		__be16 vlan_id;
13762306a36Sopenharmony_ci	} key;
13862306a36Sopenharmony_ci};
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistruct ofdpa_internal_vlan_tbl_entry {
14162306a36Sopenharmony_ci	struct hlist_node entry;
14262306a36Sopenharmony_ci	int ifindex; /* key */
14362306a36Sopenharmony_ci	u32 ref_count;
14462306a36Sopenharmony_ci	__be16 vlan_id;
14562306a36Sopenharmony_ci};
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistruct ofdpa_neigh_tbl_entry {
14862306a36Sopenharmony_ci	struct hlist_node entry;
14962306a36Sopenharmony_ci	__be32 ip_addr; /* key */
15062306a36Sopenharmony_ci	struct net_device *dev;
15162306a36Sopenharmony_ci	u32 ref_count;
15262306a36Sopenharmony_ci	u32 index;
15362306a36Sopenharmony_ci	u8 eth_dst[ETH_ALEN];
15462306a36Sopenharmony_ci	bool ttl_check;
15562306a36Sopenharmony_ci};
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cienum {
15862306a36Sopenharmony_ci	OFDPA_CTRL_LINK_LOCAL_MCAST,
15962306a36Sopenharmony_ci	OFDPA_CTRL_LOCAL_ARP,
16062306a36Sopenharmony_ci	OFDPA_CTRL_IPV4_MCAST,
16162306a36Sopenharmony_ci	OFDPA_CTRL_IPV6_MCAST,
16262306a36Sopenharmony_ci	OFDPA_CTRL_DFLT_BRIDGING,
16362306a36Sopenharmony_ci	OFDPA_CTRL_DFLT_OVS,
16462306a36Sopenharmony_ci	OFDPA_CTRL_MAX,
16562306a36Sopenharmony_ci};
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci#define OFDPA_INTERNAL_VLAN_ID_BASE	0x0f00
16862306a36Sopenharmony_ci#define OFDPA_N_INTERNAL_VLANS		255
16962306a36Sopenharmony_ci#define OFDPA_VLAN_BITMAP_LEN		BITS_TO_LONGS(VLAN_N_VID)
17062306a36Sopenharmony_ci#define OFDPA_INTERNAL_VLAN_BITMAP_LEN	BITS_TO_LONGS(OFDPA_N_INTERNAL_VLANS)
17162306a36Sopenharmony_ci#define OFDPA_UNTAGGED_VID 0
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistruct ofdpa {
17462306a36Sopenharmony_ci	struct rocker *rocker;
17562306a36Sopenharmony_ci	DECLARE_HASHTABLE(flow_tbl, 16);
17662306a36Sopenharmony_ci	spinlock_t flow_tbl_lock;		/* for flow tbl accesses */
17762306a36Sopenharmony_ci	u64 flow_tbl_next_cookie;
17862306a36Sopenharmony_ci	DECLARE_HASHTABLE(group_tbl, 16);
17962306a36Sopenharmony_ci	spinlock_t group_tbl_lock;		/* for group tbl accesses */
18062306a36Sopenharmony_ci	struct timer_list fdb_cleanup_timer;
18162306a36Sopenharmony_ci	DECLARE_HASHTABLE(fdb_tbl, 16);
18262306a36Sopenharmony_ci	spinlock_t fdb_tbl_lock;		/* for fdb tbl accesses */
18362306a36Sopenharmony_ci	unsigned long internal_vlan_bitmap[OFDPA_INTERNAL_VLAN_BITMAP_LEN];
18462306a36Sopenharmony_ci	DECLARE_HASHTABLE(internal_vlan_tbl, 8);
18562306a36Sopenharmony_ci	spinlock_t internal_vlan_tbl_lock;	/* for vlan tbl accesses */
18662306a36Sopenharmony_ci	DECLARE_HASHTABLE(neigh_tbl, 16);
18762306a36Sopenharmony_ci	spinlock_t neigh_tbl_lock;		/* for neigh tbl accesses */
18862306a36Sopenharmony_ci	u32 neigh_tbl_next_index;
18962306a36Sopenharmony_ci	unsigned long ageing_time;
19062306a36Sopenharmony_ci	bool fib_aborted;
19162306a36Sopenharmony_ci};
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistruct ofdpa_port {
19462306a36Sopenharmony_ci	struct ofdpa *ofdpa;
19562306a36Sopenharmony_ci	struct rocker_port *rocker_port;
19662306a36Sopenharmony_ci	struct net_device *dev;
19762306a36Sopenharmony_ci	u32 pport;
19862306a36Sopenharmony_ci	struct net_device *bridge_dev;
19962306a36Sopenharmony_ci	__be16 internal_vlan_id;
20062306a36Sopenharmony_ci	int stp_state;
20162306a36Sopenharmony_ci	u32 brport_flags;
20262306a36Sopenharmony_ci	unsigned long ageing_time;
20362306a36Sopenharmony_ci	bool ctrls[OFDPA_CTRL_MAX];
20462306a36Sopenharmony_ci	unsigned long vlan_bitmap[OFDPA_VLAN_BITMAP_LEN];
20562306a36Sopenharmony_ci};
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic const u8 zero_mac[ETH_ALEN]   = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
20862306a36Sopenharmony_cistatic const u8 ff_mac[ETH_ALEN]     = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
20962306a36Sopenharmony_cistatic const u8 ll_mac[ETH_ALEN]     = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
21062306a36Sopenharmony_cistatic const u8 ll_mask[ETH_ALEN]    = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0 };
21162306a36Sopenharmony_cistatic const u8 mcast_mac[ETH_ALEN]  = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };
21262306a36Sopenharmony_cistatic const u8 ipv4_mcast[ETH_ALEN] = { 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00 };
21362306a36Sopenharmony_cistatic const u8 ipv4_mask[ETH_ALEN]  = { 0xff, 0xff, 0xff, 0x80, 0x00, 0x00 };
21462306a36Sopenharmony_cistatic const u8 ipv6_mcast[ETH_ALEN] = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x00 };
21562306a36Sopenharmony_cistatic const u8 ipv6_mask[ETH_ALEN]  = { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 };
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci/* Rocker priority levels for flow table entries.  Higher
21862306a36Sopenharmony_ci * priority match takes precedence over lower priority match.
21962306a36Sopenharmony_ci */
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cienum {
22262306a36Sopenharmony_ci	OFDPA_PRIORITY_UNKNOWN = 0,
22362306a36Sopenharmony_ci	OFDPA_PRIORITY_IG_PORT = 1,
22462306a36Sopenharmony_ci	OFDPA_PRIORITY_VLAN = 1,
22562306a36Sopenharmony_ci	OFDPA_PRIORITY_TERM_MAC_UCAST = 0,
22662306a36Sopenharmony_ci	OFDPA_PRIORITY_TERM_MAC_MCAST = 1,
22762306a36Sopenharmony_ci	OFDPA_PRIORITY_BRIDGING_VLAN_DFLT_EXACT = 1,
22862306a36Sopenharmony_ci	OFDPA_PRIORITY_BRIDGING_VLAN_DFLT_WILD = 2,
22962306a36Sopenharmony_ci	OFDPA_PRIORITY_BRIDGING_VLAN = 3,
23062306a36Sopenharmony_ci	OFDPA_PRIORITY_BRIDGING_TENANT_DFLT_EXACT = 1,
23162306a36Sopenharmony_ci	OFDPA_PRIORITY_BRIDGING_TENANT_DFLT_WILD = 2,
23262306a36Sopenharmony_ci	OFDPA_PRIORITY_BRIDGING_TENANT = 3,
23362306a36Sopenharmony_ci	OFDPA_PRIORITY_ACL_CTRL = 3,
23462306a36Sopenharmony_ci	OFDPA_PRIORITY_ACL_NORMAL = 2,
23562306a36Sopenharmony_ci	OFDPA_PRIORITY_ACL_DFLT = 1,
23662306a36Sopenharmony_ci};
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic bool ofdpa_vlan_id_is_internal(__be16 vlan_id)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	u16 start = OFDPA_INTERNAL_VLAN_ID_BASE;
24162306a36Sopenharmony_ci	u16 end = 0xffe;
24262306a36Sopenharmony_ci	u16 _vlan_id = ntohs(vlan_id);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	return (_vlan_id >= start && _vlan_id <= end);
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic __be16 ofdpa_port_vid_to_vlan(const struct ofdpa_port *ofdpa_port,
24862306a36Sopenharmony_ci				     u16 vid, bool *pop_vlan)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	__be16 vlan_id;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	if (pop_vlan)
25362306a36Sopenharmony_ci		*pop_vlan = false;
25462306a36Sopenharmony_ci	vlan_id = htons(vid);
25562306a36Sopenharmony_ci	if (!vlan_id) {
25662306a36Sopenharmony_ci		vlan_id = ofdpa_port->internal_vlan_id;
25762306a36Sopenharmony_ci		if (pop_vlan)
25862306a36Sopenharmony_ci			*pop_vlan = true;
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	return vlan_id;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic u16 ofdpa_port_vlan_to_vid(const struct ofdpa_port *ofdpa_port,
26562306a36Sopenharmony_ci				  __be16 vlan_id)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	if (ofdpa_vlan_id_is_internal(vlan_id))
26862306a36Sopenharmony_ci		return 0;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return ntohs(vlan_id);
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic bool ofdpa_port_is_slave(const struct ofdpa_port *ofdpa_port,
27462306a36Sopenharmony_ci				const char *kind)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	return ofdpa_port->bridge_dev &&
27762306a36Sopenharmony_ci		!strcmp(ofdpa_port->bridge_dev->rtnl_link_ops->kind, kind);
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic bool ofdpa_port_is_bridged(const struct ofdpa_port *ofdpa_port)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	return ofdpa_port_is_slave(ofdpa_port, "bridge");
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic bool ofdpa_port_is_ovsed(const struct ofdpa_port *ofdpa_port)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	return ofdpa_port_is_slave(ofdpa_port, "openvswitch");
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci#define OFDPA_OP_FLAG_REMOVE		BIT(0)
29162306a36Sopenharmony_ci#define OFDPA_OP_FLAG_NOWAIT		BIT(1)
29262306a36Sopenharmony_ci#define OFDPA_OP_FLAG_LEARNED		BIT(2)
29362306a36Sopenharmony_ci#define OFDPA_OP_FLAG_REFRESH		BIT(3)
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic bool ofdpa_flags_nowait(int flags)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	return flags & OFDPA_OP_FLAG_NOWAIT;
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci/*************************************************************
30162306a36Sopenharmony_ci * Flow, group, FDB, internal VLAN and neigh command prepares
30262306a36Sopenharmony_ci *************************************************************/
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic int
30562306a36Sopenharmony_ciofdpa_cmd_flow_tbl_add_ig_port(struct rocker_desc_info *desc_info,
30662306a36Sopenharmony_ci			       const struct ofdpa_flow_tbl_entry *entry)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT,
30962306a36Sopenharmony_ci			       entry->key.ig_port.in_pport))
31062306a36Sopenharmony_ci		return -EMSGSIZE;
31162306a36Sopenharmony_ci	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT_MASK,
31262306a36Sopenharmony_ci			       entry->key.ig_port.in_pport_mask))
31362306a36Sopenharmony_ci		return -EMSGSIZE;
31462306a36Sopenharmony_ci	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_GOTO_TABLE_ID,
31562306a36Sopenharmony_ci			       entry->key.ig_port.goto_tbl))
31662306a36Sopenharmony_ci		return -EMSGSIZE;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	return 0;
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic int
32262306a36Sopenharmony_ciofdpa_cmd_flow_tbl_add_vlan(struct rocker_desc_info *desc_info,
32362306a36Sopenharmony_ci			    const struct ofdpa_flow_tbl_entry *entry)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT,
32662306a36Sopenharmony_ci			       entry->key.vlan.in_pport))
32762306a36Sopenharmony_ci		return -EMSGSIZE;
32862306a36Sopenharmony_ci	if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID,
32962306a36Sopenharmony_ci				entry->key.vlan.vlan_id))
33062306a36Sopenharmony_ci		return -EMSGSIZE;
33162306a36Sopenharmony_ci	if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID_MASK,
33262306a36Sopenharmony_ci				entry->key.vlan.vlan_id_mask))
33362306a36Sopenharmony_ci		return -EMSGSIZE;
33462306a36Sopenharmony_ci	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_GOTO_TABLE_ID,
33562306a36Sopenharmony_ci			       entry->key.vlan.goto_tbl))
33662306a36Sopenharmony_ci		return -EMSGSIZE;
33762306a36Sopenharmony_ci	if (entry->key.vlan.untagged &&
33862306a36Sopenharmony_ci	    rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_NEW_VLAN_ID,
33962306a36Sopenharmony_ci				entry->key.vlan.new_vlan_id))
34062306a36Sopenharmony_ci		return -EMSGSIZE;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	return 0;
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic int
34662306a36Sopenharmony_ciofdpa_cmd_flow_tbl_add_term_mac(struct rocker_desc_info *desc_info,
34762306a36Sopenharmony_ci				const struct ofdpa_flow_tbl_entry *entry)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT,
35062306a36Sopenharmony_ci			       entry->key.term_mac.in_pport))
35162306a36Sopenharmony_ci		return -EMSGSIZE;
35262306a36Sopenharmony_ci	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT_MASK,
35362306a36Sopenharmony_ci			       entry->key.term_mac.in_pport_mask))
35462306a36Sopenharmony_ci		return -EMSGSIZE;
35562306a36Sopenharmony_ci	if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_ETHERTYPE,
35662306a36Sopenharmony_ci				entry->key.term_mac.eth_type))
35762306a36Sopenharmony_ci		return -EMSGSIZE;
35862306a36Sopenharmony_ci	if (rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC,
35962306a36Sopenharmony_ci			   ETH_ALEN, entry->key.term_mac.eth_dst))
36062306a36Sopenharmony_ci		return -EMSGSIZE;
36162306a36Sopenharmony_ci	if (rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC_MASK,
36262306a36Sopenharmony_ci			   ETH_ALEN, entry->key.term_mac.eth_dst_mask))
36362306a36Sopenharmony_ci		return -EMSGSIZE;
36462306a36Sopenharmony_ci	if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID,
36562306a36Sopenharmony_ci				entry->key.term_mac.vlan_id))
36662306a36Sopenharmony_ci		return -EMSGSIZE;
36762306a36Sopenharmony_ci	if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID_MASK,
36862306a36Sopenharmony_ci				entry->key.term_mac.vlan_id_mask))
36962306a36Sopenharmony_ci		return -EMSGSIZE;
37062306a36Sopenharmony_ci	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_GOTO_TABLE_ID,
37162306a36Sopenharmony_ci			       entry->key.term_mac.goto_tbl))
37262306a36Sopenharmony_ci		return -EMSGSIZE;
37362306a36Sopenharmony_ci	if (entry->key.term_mac.copy_to_cpu &&
37462306a36Sopenharmony_ci	    rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_COPY_CPU_ACTION,
37562306a36Sopenharmony_ci			      entry->key.term_mac.copy_to_cpu))
37662306a36Sopenharmony_ci		return -EMSGSIZE;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	return 0;
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic int
38262306a36Sopenharmony_ciofdpa_cmd_flow_tbl_add_ucast_routing(struct rocker_desc_info *desc_info,
38362306a36Sopenharmony_ci				     const struct ofdpa_flow_tbl_entry *entry)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_ETHERTYPE,
38662306a36Sopenharmony_ci				entry->key.ucast_routing.eth_type))
38762306a36Sopenharmony_ci		return -EMSGSIZE;
38862306a36Sopenharmony_ci	if (rocker_tlv_put_be32(desc_info, ROCKER_TLV_OF_DPA_DST_IP,
38962306a36Sopenharmony_ci				entry->key.ucast_routing.dst4))
39062306a36Sopenharmony_ci		return -EMSGSIZE;
39162306a36Sopenharmony_ci	if (rocker_tlv_put_be32(desc_info, ROCKER_TLV_OF_DPA_DST_IP_MASK,
39262306a36Sopenharmony_ci				entry->key.ucast_routing.dst4_mask))
39362306a36Sopenharmony_ci		return -EMSGSIZE;
39462306a36Sopenharmony_ci	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_GOTO_TABLE_ID,
39562306a36Sopenharmony_ci			       entry->key.ucast_routing.goto_tbl))
39662306a36Sopenharmony_ci		return -EMSGSIZE;
39762306a36Sopenharmony_ci	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID,
39862306a36Sopenharmony_ci			       entry->key.ucast_routing.group_id))
39962306a36Sopenharmony_ci		return -EMSGSIZE;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	return 0;
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic int
40562306a36Sopenharmony_ciofdpa_cmd_flow_tbl_add_bridge(struct rocker_desc_info *desc_info,
40662306a36Sopenharmony_ci			      const struct ofdpa_flow_tbl_entry *entry)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	if (entry->key.bridge.has_eth_dst &&
40962306a36Sopenharmony_ci	    rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC,
41062306a36Sopenharmony_ci			   ETH_ALEN, entry->key.bridge.eth_dst))
41162306a36Sopenharmony_ci		return -EMSGSIZE;
41262306a36Sopenharmony_ci	if (entry->key.bridge.has_eth_dst_mask &&
41362306a36Sopenharmony_ci	    rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC_MASK,
41462306a36Sopenharmony_ci			   ETH_ALEN, entry->key.bridge.eth_dst_mask))
41562306a36Sopenharmony_ci		return -EMSGSIZE;
41662306a36Sopenharmony_ci	if (entry->key.bridge.vlan_id &&
41762306a36Sopenharmony_ci	    rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID,
41862306a36Sopenharmony_ci				entry->key.bridge.vlan_id))
41962306a36Sopenharmony_ci		return -EMSGSIZE;
42062306a36Sopenharmony_ci	if (entry->key.bridge.tunnel_id &&
42162306a36Sopenharmony_ci	    rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_TUNNEL_ID,
42262306a36Sopenharmony_ci			       entry->key.bridge.tunnel_id))
42362306a36Sopenharmony_ci		return -EMSGSIZE;
42462306a36Sopenharmony_ci	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_GOTO_TABLE_ID,
42562306a36Sopenharmony_ci			       entry->key.bridge.goto_tbl))
42662306a36Sopenharmony_ci		return -EMSGSIZE;
42762306a36Sopenharmony_ci	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID,
42862306a36Sopenharmony_ci			       entry->key.bridge.group_id))
42962306a36Sopenharmony_ci		return -EMSGSIZE;
43062306a36Sopenharmony_ci	if (entry->key.bridge.copy_to_cpu &&
43162306a36Sopenharmony_ci	    rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_COPY_CPU_ACTION,
43262306a36Sopenharmony_ci			      entry->key.bridge.copy_to_cpu))
43362306a36Sopenharmony_ci		return -EMSGSIZE;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	return 0;
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_cistatic int
43962306a36Sopenharmony_ciofdpa_cmd_flow_tbl_add_acl(struct rocker_desc_info *desc_info,
44062306a36Sopenharmony_ci			   const struct ofdpa_flow_tbl_entry *entry)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT,
44362306a36Sopenharmony_ci			       entry->key.acl.in_pport))
44462306a36Sopenharmony_ci		return -EMSGSIZE;
44562306a36Sopenharmony_ci	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT_MASK,
44662306a36Sopenharmony_ci			       entry->key.acl.in_pport_mask))
44762306a36Sopenharmony_ci		return -EMSGSIZE;
44862306a36Sopenharmony_ci	if (rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_SRC_MAC,
44962306a36Sopenharmony_ci			   ETH_ALEN, entry->key.acl.eth_src))
45062306a36Sopenharmony_ci		return -EMSGSIZE;
45162306a36Sopenharmony_ci	if (rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_SRC_MAC_MASK,
45262306a36Sopenharmony_ci			   ETH_ALEN, entry->key.acl.eth_src_mask))
45362306a36Sopenharmony_ci		return -EMSGSIZE;
45462306a36Sopenharmony_ci	if (rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC,
45562306a36Sopenharmony_ci			   ETH_ALEN, entry->key.acl.eth_dst))
45662306a36Sopenharmony_ci		return -EMSGSIZE;
45762306a36Sopenharmony_ci	if (rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC_MASK,
45862306a36Sopenharmony_ci			   ETH_ALEN, entry->key.acl.eth_dst_mask))
45962306a36Sopenharmony_ci		return -EMSGSIZE;
46062306a36Sopenharmony_ci	if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_ETHERTYPE,
46162306a36Sopenharmony_ci				entry->key.acl.eth_type))
46262306a36Sopenharmony_ci		return -EMSGSIZE;
46362306a36Sopenharmony_ci	if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID,
46462306a36Sopenharmony_ci				entry->key.acl.vlan_id))
46562306a36Sopenharmony_ci		return -EMSGSIZE;
46662306a36Sopenharmony_ci	if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID_MASK,
46762306a36Sopenharmony_ci				entry->key.acl.vlan_id_mask))
46862306a36Sopenharmony_ci		return -EMSGSIZE;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	switch (ntohs(entry->key.acl.eth_type)) {
47162306a36Sopenharmony_ci	case ETH_P_IP:
47262306a36Sopenharmony_ci	case ETH_P_IPV6:
47362306a36Sopenharmony_ci		if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_IP_PROTO,
47462306a36Sopenharmony_ci				      entry->key.acl.ip_proto))
47562306a36Sopenharmony_ci			return -EMSGSIZE;
47662306a36Sopenharmony_ci		if (rocker_tlv_put_u8(desc_info,
47762306a36Sopenharmony_ci				      ROCKER_TLV_OF_DPA_IP_PROTO_MASK,
47862306a36Sopenharmony_ci				      entry->key.acl.ip_proto_mask))
47962306a36Sopenharmony_ci			return -EMSGSIZE;
48062306a36Sopenharmony_ci		if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_IP_DSCP,
48162306a36Sopenharmony_ci				      entry->key.acl.ip_tos & 0x3f))
48262306a36Sopenharmony_ci			return -EMSGSIZE;
48362306a36Sopenharmony_ci		if (rocker_tlv_put_u8(desc_info,
48462306a36Sopenharmony_ci				      ROCKER_TLV_OF_DPA_IP_DSCP_MASK,
48562306a36Sopenharmony_ci				      entry->key.acl.ip_tos_mask & 0x3f))
48662306a36Sopenharmony_ci			return -EMSGSIZE;
48762306a36Sopenharmony_ci		if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_IP_ECN,
48862306a36Sopenharmony_ci				      (entry->key.acl.ip_tos & 0xc0) >> 6))
48962306a36Sopenharmony_ci			return -EMSGSIZE;
49062306a36Sopenharmony_ci		if (rocker_tlv_put_u8(desc_info,
49162306a36Sopenharmony_ci				      ROCKER_TLV_OF_DPA_IP_ECN_MASK,
49262306a36Sopenharmony_ci				      (entry->key.acl.ip_tos_mask & 0xc0) >> 6))
49362306a36Sopenharmony_ci			return -EMSGSIZE;
49462306a36Sopenharmony_ci		break;
49562306a36Sopenharmony_ci	}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	if (entry->key.acl.group_id != ROCKER_GROUP_NONE &&
49862306a36Sopenharmony_ci	    rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID,
49962306a36Sopenharmony_ci			       entry->key.acl.group_id))
50062306a36Sopenharmony_ci		return -EMSGSIZE;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	return 0;
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_cistatic int ofdpa_cmd_flow_tbl_add(const struct rocker_port *rocker_port,
50662306a36Sopenharmony_ci				  struct rocker_desc_info *desc_info,
50762306a36Sopenharmony_ci				  void *priv)
50862306a36Sopenharmony_ci{
50962306a36Sopenharmony_ci	const struct ofdpa_flow_tbl_entry *entry = priv;
51062306a36Sopenharmony_ci	struct rocker_tlv *cmd_info;
51162306a36Sopenharmony_ci	int err = 0;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, entry->cmd))
51462306a36Sopenharmony_ci		return -EMSGSIZE;
51562306a36Sopenharmony_ci	cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO);
51662306a36Sopenharmony_ci	if (!cmd_info)
51762306a36Sopenharmony_ci		return -EMSGSIZE;
51862306a36Sopenharmony_ci	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_TABLE_ID,
51962306a36Sopenharmony_ci			       entry->key.tbl_id))
52062306a36Sopenharmony_ci		return -EMSGSIZE;
52162306a36Sopenharmony_ci	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_PRIORITY,
52262306a36Sopenharmony_ci			       entry->key.priority))
52362306a36Sopenharmony_ci		return -EMSGSIZE;
52462306a36Sopenharmony_ci	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_HARDTIME, 0))
52562306a36Sopenharmony_ci		return -EMSGSIZE;
52662306a36Sopenharmony_ci	if (rocker_tlv_put_u64(desc_info, ROCKER_TLV_OF_DPA_COOKIE,
52762306a36Sopenharmony_ci			       entry->cookie))
52862306a36Sopenharmony_ci		return -EMSGSIZE;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	switch (entry->key.tbl_id) {
53162306a36Sopenharmony_ci	case ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT:
53262306a36Sopenharmony_ci		err = ofdpa_cmd_flow_tbl_add_ig_port(desc_info, entry);
53362306a36Sopenharmony_ci		break;
53462306a36Sopenharmony_ci	case ROCKER_OF_DPA_TABLE_ID_VLAN:
53562306a36Sopenharmony_ci		err = ofdpa_cmd_flow_tbl_add_vlan(desc_info, entry);
53662306a36Sopenharmony_ci		break;
53762306a36Sopenharmony_ci	case ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC:
53862306a36Sopenharmony_ci		err = ofdpa_cmd_flow_tbl_add_term_mac(desc_info, entry);
53962306a36Sopenharmony_ci		break;
54062306a36Sopenharmony_ci	case ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING:
54162306a36Sopenharmony_ci		err = ofdpa_cmd_flow_tbl_add_ucast_routing(desc_info, entry);
54262306a36Sopenharmony_ci		break;
54362306a36Sopenharmony_ci	case ROCKER_OF_DPA_TABLE_ID_BRIDGING:
54462306a36Sopenharmony_ci		err = ofdpa_cmd_flow_tbl_add_bridge(desc_info, entry);
54562306a36Sopenharmony_ci		break;
54662306a36Sopenharmony_ci	case ROCKER_OF_DPA_TABLE_ID_ACL_POLICY:
54762306a36Sopenharmony_ci		err = ofdpa_cmd_flow_tbl_add_acl(desc_info, entry);
54862306a36Sopenharmony_ci		break;
54962306a36Sopenharmony_ci	default:
55062306a36Sopenharmony_ci		err = -ENOTSUPP;
55162306a36Sopenharmony_ci		break;
55262306a36Sopenharmony_ci	}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	if (err)
55562306a36Sopenharmony_ci		return err;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	rocker_tlv_nest_end(desc_info, cmd_info);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	return 0;
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic int ofdpa_cmd_flow_tbl_del(const struct rocker_port *rocker_port,
56362306a36Sopenharmony_ci				  struct rocker_desc_info *desc_info,
56462306a36Sopenharmony_ci				  void *priv)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	const struct ofdpa_flow_tbl_entry *entry = priv;
56762306a36Sopenharmony_ci	struct rocker_tlv *cmd_info;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, entry->cmd))
57062306a36Sopenharmony_ci		return -EMSGSIZE;
57162306a36Sopenharmony_ci	cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO);
57262306a36Sopenharmony_ci	if (!cmd_info)
57362306a36Sopenharmony_ci		return -EMSGSIZE;
57462306a36Sopenharmony_ci	if (rocker_tlv_put_u64(desc_info, ROCKER_TLV_OF_DPA_COOKIE,
57562306a36Sopenharmony_ci			       entry->cookie))
57662306a36Sopenharmony_ci		return -EMSGSIZE;
57762306a36Sopenharmony_ci	rocker_tlv_nest_end(desc_info, cmd_info);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	return 0;
58062306a36Sopenharmony_ci}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic int
58362306a36Sopenharmony_ciofdpa_cmd_group_tbl_add_l2_interface(struct rocker_desc_info *desc_info,
58462306a36Sopenharmony_ci				     struct ofdpa_group_tbl_entry *entry)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_OUT_PPORT,
58762306a36Sopenharmony_ci			       ROCKER_GROUP_PORT_GET(entry->group_id)))
58862306a36Sopenharmony_ci		return -EMSGSIZE;
58962306a36Sopenharmony_ci	if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_POP_VLAN,
59062306a36Sopenharmony_ci			      entry->l2_interface.pop_vlan))
59162306a36Sopenharmony_ci		return -EMSGSIZE;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	return 0;
59462306a36Sopenharmony_ci}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_cistatic int
59762306a36Sopenharmony_ciofdpa_cmd_group_tbl_add_l2_rewrite(struct rocker_desc_info *desc_info,
59862306a36Sopenharmony_ci				   const struct ofdpa_group_tbl_entry *entry)
59962306a36Sopenharmony_ci{
60062306a36Sopenharmony_ci	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID_LOWER,
60162306a36Sopenharmony_ci			       entry->l2_rewrite.group_id))
60262306a36Sopenharmony_ci		return -EMSGSIZE;
60362306a36Sopenharmony_ci	if (!is_zero_ether_addr(entry->l2_rewrite.eth_src) &&
60462306a36Sopenharmony_ci	    rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_SRC_MAC,
60562306a36Sopenharmony_ci			   ETH_ALEN, entry->l2_rewrite.eth_src))
60662306a36Sopenharmony_ci		return -EMSGSIZE;
60762306a36Sopenharmony_ci	if (!is_zero_ether_addr(entry->l2_rewrite.eth_dst) &&
60862306a36Sopenharmony_ci	    rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC,
60962306a36Sopenharmony_ci			   ETH_ALEN, entry->l2_rewrite.eth_dst))
61062306a36Sopenharmony_ci		return -EMSGSIZE;
61162306a36Sopenharmony_ci	if (entry->l2_rewrite.vlan_id &&
61262306a36Sopenharmony_ci	    rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID,
61362306a36Sopenharmony_ci				entry->l2_rewrite.vlan_id))
61462306a36Sopenharmony_ci		return -EMSGSIZE;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	return 0;
61762306a36Sopenharmony_ci}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_cistatic int
62062306a36Sopenharmony_ciofdpa_cmd_group_tbl_add_group_ids(struct rocker_desc_info *desc_info,
62162306a36Sopenharmony_ci				  const struct ofdpa_group_tbl_entry *entry)
62262306a36Sopenharmony_ci{
62362306a36Sopenharmony_ci	int i;
62462306a36Sopenharmony_ci	struct rocker_tlv *group_ids;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_GROUP_COUNT,
62762306a36Sopenharmony_ci			       entry->group_count))
62862306a36Sopenharmony_ci		return -EMSGSIZE;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	group_ids = rocker_tlv_nest_start(desc_info,
63162306a36Sopenharmony_ci					  ROCKER_TLV_OF_DPA_GROUP_IDS);
63262306a36Sopenharmony_ci	if (!group_ids)
63362306a36Sopenharmony_ci		return -EMSGSIZE;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	for (i = 0; i < entry->group_count; i++)
63662306a36Sopenharmony_ci		/* Note TLV array is 1-based */
63762306a36Sopenharmony_ci		if (rocker_tlv_put_u32(desc_info, i + 1, entry->group_ids[i]))
63862306a36Sopenharmony_ci			return -EMSGSIZE;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	rocker_tlv_nest_end(desc_info, group_ids);
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	return 0;
64362306a36Sopenharmony_ci}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_cistatic int
64662306a36Sopenharmony_ciofdpa_cmd_group_tbl_add_l3_unicast(struct rocker_desc_info *desc_info,
64762306a36Sopenharmony_ci				   const struct ofdpa_group_tbl_entry *entry)
64862306a36Sopenharmony_ci{
64962306a36Sopenharmony_ci	if (!is_zero_ether_addr(entry->l3_unicast.eth_src) &&
65062306a36Sopenharmony_ci	    rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_SRC_MAC,
65162306a36Sopenharmony_ci			   ETH_ALEN, entry->l3_unicast.eth_src))
65262306a36Sopenharmony_ci		return -EMSGSIZE;
65362306a36Sopenharmony_ci	if (!is_zero_ether_addr(entry->l3_unicast.eth_dst) &&
65462306a36Sopenharmony_ci	    rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC,
65562306a36Sopenharmony_ci			   ETH_ALEN, entry->l3_unicast.eth_dst))
65662306a36Sopenharmony_ci		return -EMSGSIZE;
65762306a36Sopenharmony_ci	if (entry->l3_unicast.vlan_id &&
65862306a36Sopenharmony_ci	    rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID,
65962306a36Sopenharmony_ci				entry->l3_unicast.vlan_id))
66062306a36Sopenharmony_ci		return -EMSGSIZE;
66162306a36Sopenharmony_ci	if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_TTL_CHECK,
66262306a36Sopenharmony_ci			      entry->l3_unicast.ttl_check))
66362306a36Sopenharmony_ci		return -EMSGSIZE;
66462306a36Sopenharmony_ci	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID_LOWER,
66562306a36Sopenharmony_ci			       entry->l3_unicast.group_id))
66662306a36Sopenharmony_ci		return -EMSGSIZE;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	return 0;
66962306a36Sopenharmony_ci}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_cistatic int ofdpa_cmd_group_tbl_add(const struct rocker_port *rocker_port,
67262306a36Sopenharmony_ci				   struct rocker_desc_info *desc_info,
67362306a36Sopenharmony_ci				   void *priv)
67462306a36Sopenharmony_ci{
67562306a36Sopenharmony_ci	struct ofdpa_group_tbl_entry *entry = priv;
67662306a36Sopenharmony_ci	struct rocker_tlv *cmd_info;
67762306a36Sopenharmony_ci	int err = 0;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, entry->cmd))
68062306a36Sopenharmony_ci		return -EMSGSIZE;
68162306a36Sopenharmony_ci	cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO);
68262306a36Sopenharmony_ci	if (!cmd_info)
68362306a36Sopenharmony_ci		return -EMSGSIZE;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID,
68662306a36Sopenharmony_ci			       entry->group_id))
68762306a36Sopenharmony_ci		return -EMSGSIZE;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	switch (ROCKER_GROUP_TYPE_GET(entry->group_id)) {
69062306a36Sopenharmony_ci	case ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE:
69162306a36Sopenharmony_ci		err = ofdpa_cmd_group_tbl_add_l2_interface(desc_info, entry);
69262306a36Sopenharmony_ci		break;
69362306a36Sopenharmony_ci	case ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE:
69462306a36Sopenharmony_ci		err = ofdpa_cmd_group_tbl_add_l2_rewrite(desc_info, entry);
69562306a36Sopenharmony_ci		break;
69662306a36Sopenharmony_ci	case ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD:
69762306a36Sopenharmony_ci	case ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST:
69862306a36Sopenharmony_ci		err = ofdpa_cmd_group_tbl_add_group_ids(desc_info, entry);
69962306a36Sopenharmony_ci		break;
70062306a36Sopenharmony_ci	case ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST:
70162306a36Sopenharmony_ci		err = ofdpa_cmd_group_tbl_add_l3_unicast(desc_info, entry);
70262306a36Sopenharmony_ci		break;
70362306a36Sopenharmony_ci	default:
70462306a36Sopenharmony_ci		err = -ENOTSUPP;
70562306a36Sopenharmony_ci		break;
70662306a36Sopenharmony_ci	}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	if (err)
70962306a36Sopenharmony_ci		return err;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	rocker_tlv_nest_end(desc_info, cmd_info);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	return 0;
71462306a36Sopenharmony_ci}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_cistatic int ofdpa_cmd_group_tbl_del(const struct rocker_port *rocker_port,
71762306a36Sopenharmony_ci				   struct rocker_desc_info *desc_info,
71862306a36Sopenharmony_ci				   void *priv)
71962306a36Sopenharmony_ci{
72062306a36Sopenharmony_ci	const struct ofdpa_group_tbl_entry *entry = priv;
72162306a36Sopenharmony_ci	struct rocker_tlv *cmd_info;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, entry->cmd))
72462306a36Sopenharmony_ci		return -EMSGSIZE;
72562306a36Sopenharmony_ci	cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO);
72662306a36Sopenharmony_ci	if (!cmd_info)
72762306a36Sopenharmony_ci		return -EMSGSIZE;
72862306a36Sopenharmony_ci	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID,
72962306a36Sopenharmony_ci			       entry->group_id))
73062306a36Sopenharmony_ci		return -EMSGSIZE;
73162306a36Sopenharmony_ci	rocker_tlv_nest_end(desc_info, cmd_info);
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	return 0;
73462306a36Sopenharmony_ci}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci/***************************************************
73762306a36Sopenharmony_ci * Flow, group, FDB, internal VLAN and neigh tables
73862306a36Sopenharmony_ci ***************************************************/
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_cistatic struct ofdpa_flow_tbl_entry *
74162306a36Sopenharmony_ciofdpa_flow_tbl_find(const struct ofdpa *ofdpa,
74262306a36Sopenharmony_ci		    const struct ofdpa_flow_tbl_entry *match)
74362306a36Sopenharmony_ci{
74462306a36Sopenharmony_ci	struct ofdpa_flow_tbl_entry *found;
74562306a36Sopenharmony_ci	size_t key_len = match->key_len ? match->key_len : sizeof(found->key);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	hash_for_each_possible(ofdpa->flow_tbl, found,
74862306a36Sopenharmony_ci			       entry, match->key_crc32) {
74962306a36Sopenharmony_ci		if (memcmp(&found->key, &match->key, key_len) == 0)
75062306a36Sopenharmony_ci			return found;
75162306a36Sopenharmony_ci	}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	return NULL;
75462306a36Sopenharmony_ci}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_cistatic int ofdpa_flow_tbl_add(struct ofdpa_port *ofdpa_port,
75762306a36Sopenharmony_ci			      int flags, struct ofdpa_flow_tbl_entry *match)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	struct ofdpa *ofdpa = ofdpa_port->ofdpa;
76062306a36Sopenharmony_ci	struct ofdpa_flow_tbl_entry *found;
76162306a36Sopenharmony_ci	size_t key_len = match->key_len ? match->key_len : sizeof(found->key);
76262306a36Sopenharmony_ci	unsigned long lock_flags;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	match->key_crc32 = crc32(~0, &match->key, key_len);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	spin_lock_irqsave(&ofdpa->flow_tbl_lock, lock_flags);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	found = ofdpa_flow_tbl_find(ofdpa, match);
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	if (found) {
77162306a36Sopenharmony_ci		match->cookie = found->cookie;
77262306a36Sopenharmony_ci		hash_del(&found->entry);
77362306a36Sopenharmony_ci		kfree(found);
77462306a36Sopenharmony_ci		found = match;
77562306a36Sopenharmony_ci		found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_MOD;
77662306a36Sopenharmony_ci	} else {
77762306a36Sopenharmony_ci		found = match;
77862306a36Sopenharmony_ci		found->cookie = ofdpa->flow_tbl_next_cookie++;
77962306a36Sopenharmony_ci		found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_ADD;
78062306a36Sopenharmony_ci	}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	hash_add(ofdpa->flow_tbl, &found->entry, found->key_crc32);
78362306a36Sopenharmony_ci	spin_unlock_irqrestore(&ofdpa->flow_tbl_lock, lock_flags);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	return rocker_cmd_exec(ofdpa_port->rocker_port,
78662306a36Sopenharmony_ci			       ofdpa_flags_nowait(flags),
78762306a36Sopenharmony_ci			       ofdpa_cmd_flow_tbl_add,
78862306a36Sopenharmony_ci			       found, NULL, NULL);
78962306a36Sopenharmony_ci}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_cistatic int ofdpa_flow_tbl_del(struct ofdpa_port *ofdpa_port,
79262306a36Sopenharmony_ci			      int flags, struct ofdpa_flow_tbl_entry *match)
79362306a36Sopenharmony_ci{
79462306a36Sopenharmony_ci	struct ofdpa *ofdpa = ofdpa_port->ofdpa;
79562306a36Sopenharmony_ci	struct ofdpa_flow_tbl_entry *found;
79662306a36Sopenharmony_ci	size_t key_len = match->key_len ? match->key_len : sizeof(found->key);
79762306a36Sopenharmony_ci	unsigned long lock_flags;
79862306a36Sopenharmony_ci	int err = 0;
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	match->key_crc32 = crc32(~0, &match->key, key_len);
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	spin_lock_irqsave(&ofdpa->flow_tbl_lock, lock_flags);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	found = ofdpa_flow_tbl_find(ofdpa, match);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	if (found) {
80762306a36Sopenharmony_ci		hash_del(&found->entry);
80862306a36Sopenharmony_ci		found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_DEL;
80962306a36Sopenharmony_ci	}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	spin_unlock_irqrestore(&ofdpa->flow_tbl_lock, lock_flags);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	kfree(match);
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	if (found) {
81662306a36Sopenharmony_ci		err = rocker_cmd_exec(ofdpa_port->rocker_port,
81762306a36Sopenharmony_ci				      ofdpa_flags_nowait(flags),
81862306a36Sopenharmony_ci				      ofdpa_cmd_flow_tbl_del,
81962306a36Sopenharmony_ci				      found, NULL, NULL);
82062306a36Sopenharmony_ci		kfree(found);
82162306a36Sopenharmony_ci	}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	return err;
82462306a36Sopenharmony_ci}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_cistatic int ofdpa_flow_tbl_do(struct ofdpa_port *ofdpa_port, int flags,
82762306a36Sopenharmony_ci			     struct ofdpa_flow_tbl_entry *entry)
82862306a36Sopenharmony_ci{
82962306a36Sopenharmony_ci	if (flags & OFDPA_OP_FLAG_REMOVE)
83062306a36Sopenharmony_ci		return ofdpa_flow_tbl_del(ofdpa_port, flags, entry);
83162306a36Sopenharmony_ci	else
83262306a36Sopenharmony_ci		return ofdpa_flow_tbl_add(ofdpa_port, flags, entry);
83362306a36Sopenharmony_ci}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_cistatic int ofdpa_flow_tbl_ig_port(struct ofdpa_port *ofdpa_port, int flags,
83662306a36Sopenharmony_ci				  u32 in_pport, u32 in_pport_mask,
83762306a36Sopenharmony_ci				  enum rocker_of_dpa_table_id goto_tbl)
83862306a36Sopenharmony_ci{
83962306a36Sopenharmony_ci	struct ofdpa_flow_tbl_entry *entry;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
84262306a36Sopenharmony_ci	if (!entry)
84362306a36Sopenharmony_ci		return -ENOMEM;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	entry->key.priority = OFDPA_PRIORITY_IG_PORT;
84662306a36Sopenharmony_ci	entry->key.tbl_id = ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT;
84762306a36Sopenharmony_ci	entry->key.ig_port.in_pport = in_pport;
84862306a36Sopenharmony_ci	entry->key.ig_port.in_pport_mask = in_pport_mask;
84962306a36Sopenharmony_ci	entry->key.ig_port.goto_tbl = goto_tbl;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	return ofdpa_flow_tbl_do(ofdpa_port, flags, entry);
85262306a36Sopenharmony_ci}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_cistatic int ofdpa_flow_tbl_vlan(struct ofdpa_port *ofdpa_port,
85562306a36Sopenharmony_ci			       int flags,
85662306a36Sopenharmony_ci			       u32 in_pport, __be16 vlan_id,
85762306a36Sopenharmony_ci			       __be16 vlan_id_mask,
85862306a36Sopenharmony_ci			       enum rocker_of_dpa_table_id goto_tbl,
85962306a36Sopenharmony_ci			       bool untagged, __be16 new_vlan_id)
86062306a36Sopenharmony_ci{
86162306a36Sopenharmony_ci	struct ofdpa_flow_tbl_entry *entry;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
86462306a36Sopenharmony_ci	if (!entry)
86562306a36Sopenharmony_ci		return -ENOMEM;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	entry->key.priority = OFDPA_PRIORITY_VLAN;
86862306a36Sopenharmony_ci	entry->key.tbl_id = ROCKER_OF_DPA_TABLE_ID_VLAN;
86962306a36Sopenharmony_ci	entry->key.vlan.in_pport = in_pport;
87062306a36Sopenharmony_ci	entry->key.vlan.vlan_id = vlan_id;
87162306a36Sopenharmony_ci	entry->key.vlan.vlan_id_mask = vlan_id_mask;
87262306a36Sopenharmony_ci	entry->key.vlan.goto_tbl = goto_tbl;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	entry->key.vlan.untagged = untagged;
87562306a36Sopenharmony_ci	entry->key.vlan.new_vlan_id = new_vlan_id;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	return ofdpa_flow_tbl_do(ofdpa_port, flags, entry);
87862306a36Sopenharmony_ci}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_cistatic int ofdpa_flow_tbl_term_mac(struct ofdpa_port *ofdpa_port,
88162306a36Sopenharmony_ci				   u32 in_pport, u32 in_pport_mask,
88262306a36Sopenharmony_ci				   __be16 eth_type, const u8 *eth_dst,
88362306a36Sopenharmony_ci				   const u8 *eth_dst_mask, __be16 vlan_id,
88462306a36Sopenharmony_ci				   __be16 vlan_id_mask, bool copy_to_cpu,
88562306a36Sopenharmony_ci				   int flags)
88662306a36Sopenharmony_ci{
88762306a36Sopenharmony_ci	struct ofdpa_flow_tbl_entry *entry;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
89062306a36Sopenharmony_ci	if (!entry)
89162306a36Sopenharmony_ci		return -ENOMEM;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	if (is_multicast_ether_addr(eth_dst)) {
89462306a36Sopenharmony_ci		entry->key.priority = OFDPA_PRIORITY_TERM_MAC_MCAST;
89562306a36Sopenharmony_ci		entry->key.term_mac.goto_tbl =
89662306a36Sopenharmony_ci			 ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING;
89762306a36Sopenharmony_ci	} else {
89862306a36Sopenharmony_ci		entry->key.priority = OFDPA_PRIORITY_TERM_MAC_UCAST;
89962306a36Sopenharmony_ci		entry->key.term_mac.goto_tbl =
90062306a36Sopenharmony_ci			 ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING;
90162306a36Sopenharmony_ci	}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	entry->key.tbl_id = ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC;
90462306a36Sopenharmony_ci	entry->key.term_mac.in_pport = in_pport;
90562306a36Sopenharmony_ci	entry->key.term_mac.in_pport_mask = in_pport_mask;
90662306a36Sopenharmony_ci	entry->key.term_mac.eth_type = eth_type;
90762306a36Sopenharmony_ci	ether_addr_copy(entry->key.term_mac.eth_dst, eth_dst);
90862306a36Sopenharmony_ci	ether_addr_copy(entry->key.term_mac.eth_dst_mask, eth_dst_mask);
90962306a36Sopenharmony_ci	entry->key.term_mac.vlan_id = vlan_id;
91062306a36Sopenharmony_ci	entry->key.term_mac.vlan_id_mask = vlan_id_mask;
91162306a36Sopenharmony_ci	entry->key.term_mac.copy_to_cpu = copy_to_cpu;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	return ofdpa_flow_tbl_do(ofdpa_port, flags, entry);
91462306a36Sopenharmony_ci}
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_cistatic int ofdpa_flow_tbl_bridge(struct ofdpa_port *ofdpa_port,
91762306a36Sopenharmony_ci				 int flags, const u8 *eth_dst,
91862306a36Sopenharmony_ci				 const u8 *eth_dst_mask,  __be16 vlan_id,
91962306a36Sopenharmony_ci				 u32 tunnel_id,
92062306a36Sopenharmony_ci				 enum rocker_of_dpa_table_id goto_tbl,
92162306a36Sopenharmony_ci				 u32 group_id, bool copy_to_cpu)
92262306a36Sopenharmony_ci{
92362306a36Sopenharmony_ci	struct ofdpa_flow_tbl_entry *entry;
92462306a36Sopenharmony_ci	u32 priority;
92562306a36Sopenharmony_ci	bool vlan_bridging = !!vlan_id;
92662306a36Sopenharmony_ci	bool dflt = !eth_dst || eth_dst_mask;
92762306a36Sopenharmony_ci	bool wild = false;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
93062306a36Sopenharmony_ci	if (!entry)
93162306a36Sopenharmony_ci		return -ENOMEM;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	entry->key.tbl_id = ROCKER_OF_DPA_TABLE_ID_BRIDGING;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	if (eth_dst) {
93662306a36Sopenharmony_ci		entry->key.bridge.has_eth_dst = 1;
93762306a36Sopenharmony_ci		ether_addr_copy(entry->key.bridge.eth_dst, eth_dst);
93862306a36Sopenharmony_ci	}
93962306a36Sopenharmony_ci	if (eth_dst_mask) {
94062306a36Sopenharmony_ci		entry->key.bridge.has_eth_dst_mask = 1;
94162306a36Sopenharmony_ci		ether_addr_copy(entry->key.bridge.eth_dst_mask, eth_dst_mask);
94262306a36Sopenharmony_ci		if (!ether_addr_equal(eth_dst_mask, ff_mac))
94362306a36Sopenharmony_ci			wild = true;
94462306a36Sopenharmony_ci	}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	priority = OFDPA_PRIORITY_UNKNOWN;
94762306a36Sopenharmony_ci	if (vlan_bridging && dflt && wild)
94862306a36Sopenharmony_ci		priority = OFDPA_PRIORITY_BRIDGING_VLAN_DFLT_WILD;
94962306a36Sopenharmony_ci	else if (vlan_bridging && dflt && !wild)
95062306a36Sopenharmony_ci		priority = OFDPA_PRIORITY_BRIDGING_VLAN_DFLT_EXACT;
95162306a36Sopenharmony_ci	else if (vlan_bridging && !dflt)
95262306a36Sopenharmony_ci		priority = OFDPA_PRIORITY_BRIDGING_VLAN;
95362306a36Sopenharmony_ci	else if (!vlan_bridging && dflt && wild)
95462306a36Sopenharmony_ci		priority = OFDPA_PRIORITY_BRIDGING_TENANT_DFLT_WILD;
95562306a36Sopenharmony_ci	else if (!vlan_bridging && dflt && !wild)
95662306a36Sopenharmony_ci		priority = OFDPA_PRIORITY_BRIDGING_TENANT_DFLT_EXACT;
95762306a36Sopenharmony_ci	else if (!vlan_bridging && !dflt)
95862306a36Sopenharmony_ci		priority = OFDPA_PRIORITY_BRIDGING_TENANT;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	entry->key.priority = priority;
96162306a36Sopenharmony_ci	entry->key.bridge.vlan_id = vlan_id;
96262306a36Sopenharmony_ci	entry->key.bridge.tunnel_id = tunnel_id;
96362306a36Sopenharmony_ci	entry->key.bridge.goto_tbl = goto_tbl;
96462306a36Sopenharmony_ci	entry->key.bridge.group_id = group_id;
96562306a36Sopenharmony_ci	entry->key.bridge.copy_to_cpu = copy_to_cpu;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	return ofdpa_flow_tbl_do(ofdpa_port, flags, entry);
96862306a36Sopenharmony_ci}
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_cistatic int ofdpa_flow_tbl_ucast4_routing(struct ofdpa_port *ofdpa_port,
97162306a36Sopenharmony_ci					 __be16 eth_type, __be32 dst,
97262306a36Sopenharmony_ci					 __be32 dst_mask, u32 priority,
97362306a36Sopenharmony_ci					 enum rocker_of_dpa_table_id goto_tbl,
97462306a36Sopenharmony_ci					 u32 group_id, struct fib_info *fi,
97562306a36Sopenharmony_ci					 int flags)
97662306a36Sopenharmony_ci{
97762306a36Sopenharmony_ci	struct ofdpa_flow_tbl_entry *entry;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
98062306a36Sopenharmony_ci	if (!entry)
98162306a36Sopenharmony_ci		return -ENOMEM;
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	entry->key.tbl_id = ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING;
98462306a36Sopenharmony_ci	entry->key.priority = priority;
98562306a36Sopenharmony_ci	entry->key.ucast_routing.eth_type = eth_type;
98662306a36Sopenharmony_ci	entry->key.ucast_routing.dst4 = dst;
98762306a36Sopenharmony_ci	entry->key.ucast_routing.dst4_mask = dst_mask;
98862306a36Sopenharmony_ci	entry->key.ucast_routing.goto_tbl = goto_tbl;
98962306a36Sopenharmony_ci	entry->key.ucast_routing.group_id = group_id;
99062306a36Sopenharmony_ci	entry->key_len = offsetof(struct ofdpa_flow_tbl_key,
99162306a36Sopenharmony_ci				  ucast_routing.group_id);
99262306a36Sopenharmony_ci	entry->fi = fi;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	return ofdpa_flow_tbl_do(ofdpa_port, flags, entry);
99562306a36Sopenharmony_ci}
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_cistatic int ofdpa_flow_tbl_acl(struct ofdpa_port *ofdpa_port, int flags,
99862306a36Sopenharmony_ci			      u32 in_pport, u32 in_pport_mask,
99962306a36Sopenharmony_ci			      const u8 *eth_src, const u8 *eth_src_mask,
100062306a36Sopenharmony_ci			      const u8 *eth_dst, const u8 *eth_dst_mask,
100162306a36Sopenharmony_ci			      __be16 eth_type, __be16 vlan_id,
100262306a36Sopenharmony_ci			      __be16 vlan_id_mask, u8 ip_proto,
100362306a36Sopenharmony_ci			      u8 ip_proto_mask, u8 ip_tos, u8 ip_tos_mask,
100462306a36Sopenharmony_ci			      u32 group_id)
100562306a36Sopenharmony_ci{
100662306a36Sopenharmony_ci	u32 priority;
100762306a36Sopenharmony_ci	struct ofdpa_flow_tbl_entry *entry;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
101062306a36Sopenharmony_ci	if (!entry)
101162306a36Sopenharmony_ci		return -ENOMEM;
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	priority = OFDPA_PRIORITY_ACL_NORMAL;
101462306a36Sopenharmony_ci	if (eth_dst && eth_dst_mask) {
101562306a36Sopenharmony_ci		if (ether_addr_equal(eth_dst_mask, mcast_mac))
101662306a36Sopenharmony_ci			priority = OFDPA_PRIORITY_ACL_DFLT;
101762306a36Sopenharmony_ci		else if (is_link_local_ether_addr(eth_dst))
101862306a36Sopenharmony_ci			priority = OFDPA_PRIORITY_ACL_CTRL;
101962306a36Sopenharmony_ci	}
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	entry->key.priority = priority;
102262306a36Sopenharmony_ci	entry->key.tbl_id = ROCKER_OF_DPA_TABLE_ID_ACL_POLICY;
102362306a36Sopenharmony_ci	entry->key.acl.in_pport = in_pport;
102462306a36Sopenharmony_ci	entry->key.acl.in_pport_mask = in_pport_mask;
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	if (eth_src)
102762306a36Sopenharmony_ci		ether_addr_copy(entry->key.acl.eth_src, eth_src);
102862306a36Sopenharmony_ci	if (eth_src_mask)
102962306a36Sopenharmony_ci		ether_addr_copy(entry->key.acl.eth_src_mask, eth_src_mask);
103062306a36Sopenharmony_ci	if (eth_dst)
103162306a36Sopenharmony_ci		ether_addr_copy(entry->key.acl.eth_dst, eth_dst);
103262306a36Sopenharmony_ci	if (eth_dst_mask)
103362306a36Sopenharmony_ci		ether_addr_copy(entry->key.acl.eth_dst_mask, eth_dst_mask);
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	entry->key.acl.eth_type = eth_type;
103662306a36Sopenharmony_ci	entry->key.acl.vlan_id = vlan_id;
103762306a36Sopenharmony_ci	entry->key.acl.vlan_id_mask = vlan_id_mask;
103862306a36Sopenharmony_ci	entry->key.acl.ip_proto = ip_proto;
103962306a36Sopenharmony_ci	entry->key.acl.ip_proto_mask = ip_proto_mask;
104062306a36Sopenharmony_ci	entry->key.acl.ip_tos = ip_tos;
104162306a36Sopenharmony_ci	entry->key.acl.ip_tos_mask = ip_tos_mask;
104262306a36Sopenharmony_ci	entry->key.acl.group_id = group_id;
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	return ofdpa_flow_tbl_do(ofdpa_port, flags, entry);
104562306a36Sopenharmony_ci}
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_cistatic struct ofdpa_group_tbl_entry *
104862306a36Sopenharmony_ciofdpa_group_tbl_find(const struct ofdpa *ofdpa,
104962306a36Sopenharmony_ci		     const struct ofdpa_group_tbl_entry *match)
105062306a36Sopenharmony_ci{
105162306a36Sopenharmony_ci	struct ofdpa_group_tbl_entry *found;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	hash_for_each_possible(ofdpa->group_tbl, found,
105462306a36Sopenharmony_ci			       entry, match->group_id) {
105562306a36Sopenharmony_ci		if (found->group_id == match->group_id)
105662306a36Sopenharmony_ci			return found;
105762306a36Sopenharmony_ci	}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	return NULL;
106062306a36Sopenharmony_ci}
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_cistatic void ofdpa_group_tbl_entry_free(struct ofdpa_group_tbl_entry *entry)
106362306a36Sopenharmony_ci{
106462306a36Sopenharmony_ci	switch (ROCKER_GROUP_TYPE_GET(entry->group_id)) {
106562306a36Sopenharmony_ci	case ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD:
106662306a36Sopenharmony_ci	case ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST:
106762306a36Sopenharmony_ci		kfree(entry->group_ids);
106862306a36Sopenharmony_ci		break;
106962306a36Sopenharmony_ci	default:
107062306a36Sopenharmony_ci		break;
107162306a36Sopenharmony_ci	}
107262306a36Sopenharmony_ci	kfree(entry);
107362306a36Sopenharmony_ci}
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_cistatic int ofdpa_group_tbl_add(struct ofdpa_port *ofdpa_port, int flags,
107662306a36Sopenharmony_ci			       struct ofdpa_group_tbl_entry *match)
107762306a36Sopenharmony_ci{
107862306a36Sopenharmony_ci	struct ofdpa *ofdpa = ofdpa_port->ofdpa;
107962306a36Sopenharmony_ci	struct ofdpa_group_tbl_entry *found;
108062306a36Sopenharmony_ci	unsigned long lock_flags;
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	spin_lock_irqsave(&ofdpa->group_tbl_lock, lock_flags);
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	found = ofdpa_group_tbl_find(ofdpa, match);
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	if (found) {
108762306a36Sopenharmony_ci		hash_del(&found->entry);
108862306a36Sopenharmony_ci		ofdpa_group_tbl_entry_free(found);
108962306a36Sopenharmony_ci		found = match;
109062306a36Sopenharmony_ci		found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_MOD;
109162306a36Sopenharmony_ci	} else {
109262306a36Sopenharmony_ci		found = match;
109362306a36Sopenharmony_ci		found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_ADD;
109462306a36Sopenharmony_ci	}
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	hash_add(ofdpa->group_tbl, &found->entry, found->group_id);
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	spin_unlock_irqrestore(&ofdpa->group_tbl_lock, lock_flags);
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	return rocker_cmd_exec(ofdpa_port->rocker_port,
110162306a36Sopenharmony_ci			       ofdpa_flags_nowait(flags),
110262306a36Sopenharmony_ci			       ofdpa_cmd_group_tbl_add,
110362306a36Sopenharmony_ci			       found, NULL, NULL);
110462306a36Sopenharmony_ci}
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_cistatic int ofdpa_group_tbl_del(struct ofdpa_port *ofdpa_port, int flags,
110762306a36Sopenharmony_ci			       struct ofdpa_group_tbl_entry *match)
110862306a36Sopenharmony_ci{
110962306a36Sopenharmony_ci	struct ofdpa *ofdpa = ofdpa_port->ofdpa;
111062306a36Sopenharmony_ci	struct ofdpa_group_tbl_entry *found;
111162306a36Sopenharmony_ci	unsigned long lock_flags;
111262306a36Sopenharmony_ci	int err = 0;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	spin_lock_irqsave(&ofdpa->group_tbl_lock, lock_flags);
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	found = ofdpa_group_tbl_find(ofdpa, match);
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	if (found) {
111962306a36Sopenharmony_ci		hash_del(&found->entry);
112062306a36Sopenharmony_ci		found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_DEL;
112162306a36Sopenharmony_ci	}
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	spin_unlock_irqrestore(&ofdpa->group_tbl_lock, lock_flags);
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	ofdpa_group_tbl_entry_free(match);
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	if (found) {
112862306a36Sopenharmony_ci		err = rocker_cmd_exec(ofdpa_port->rocker_port,
112962306a36Sopenharmony_ci				      ofdpa_flags_nowait(flags),
113062306a36Sopenharmony_ci				      ofdpa_cmd_group_tbl_del,
113162306a36Sopenharmony_ci				      found, NULL, NULL);
113262306a36Sopenharmony_ci		ofdpa_group_tbl_entry_free(found);
113362306a36Sopenharmony_ci	}
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	return err;
113662306a36Sopenharmony_ci}
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_cistatic int ofdpa_group_tbl_do(struct ofdpa_port *ofdpa_port, int flags,
113962306a36Sopenharmony_ci			      struct ofdpa_group_tbl_entry *entry)
114062306a36Sopenharmony_ci{
114162306a36Sopenharmony_ci	if (flags & OFDPA_OP_FLAG_REMOVE)
114262306a36Sopenharmony_ci		return ofdpa_group_tbl_del(ofdpa_port, flags, entry);
114362306a36Sopenharmony_ci	else
114462306a36Sopenharmony_ci		return ofdpa_group_tbl_add(ofdpa_port, flags, entry);
114562306a36Sopenharmony_ci}
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_cistatic int ofdpa_group_l2_interface(struct ofdpa_port *ofdpa_port,
114862306a36Sopenharmony_ci				    int flags, __be16 vlan_id,
114962306a36Sopenharmony_ci				    u32 out_pport, int pop_vlan)
115062306a36Sopenharmony_ci{
115162306a36Sopenharmony_ci	struct ofdpa_group_tbl_entry *entry;
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
115462306a36Sopenharmony_ci	if (!entry)
115562306a36Sopenharmony_ci		return -ENOMEM;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	entry->group_id = ROCKER_GROUP_L2_INTERFACE(vlan_id, out_pport);
115862306a36Sopenharmony_ci	entry->l2_interface.pop_vlan = pop_vlan;
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	return ofdpa_group_tbl_do(ofdpa_port, flags, entry);
116162306a36Sopenharmony_ci}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_cistatic int ofdpa_group_l2_fan_out(struct ofdpa_port *ofdpa_port,
116462306a36Sopenharmony_ci				  int flags, u8 group_count,
116562306a36Sopenharmony_ci				  const u32 *group_ids, u32 group_id)
116662306a36Sopenharmony_ci{
116762306a36Sopenharmony_ci	struct ofdpa_group_tbl_entry *entry;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
117062306a36Sopenharmony_ci	if (!entry)
117162306a36Sopenharmony_ci		return -ENOMEM;
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	entry->group_id = group_id;
117462306a36Sopenharmony_ci	entry->group_count = group_count;
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	entry->group_ids = kcalloc(group_count, sizeof(u32), GFP_KERNEL);
117762306a36Sopenharmony_ci	if (!entry->group_ids) {
117862306a36Sopenharmony_ci		kfree(entry);
117962306a36Sopenharmony_ci		return -ENOMEM;
118062306a36Sopenharmony_ci	}
118162306a36Sopenharmony_ci	memcpy(entry->group_ids, group_ids, group_count * sizeof(u32));
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	return ofdpa_group_tbl_do(ofdpa_port, flags, entry);
118462306a36Sopenharmony_ci}
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_cistatic int ofdpa_group_l2_flood(struct ofdpa_port *ofdpa_port,
118762306a36Sopenharmony_ci				int flags, __be16 vlan_id,
118862306a36Sopenharmony_ci				u8 group_count,	const u32 *group_ids,
118962306a36Sopenharmony_ci				u32 group_id)
119062306a36Sopenharmony_ci{
119162306a36Sopenharmony_ci	return ofdpa_group_l2_fan_out(ofdpa_port, flags,
119262306a36Sopenharmony_ci				      group_count, group_ids,
119362306a36Sopenharmony_ci				      group_id);
119462306a36Sopenharmony_ci}
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_cistatic int ofdpa_group_l3_unicast(struct ofdpa_port *ofdpa_port, int flags,
119762306a36Sopenharmony_ci				  u32 index, const u8 *src_mac, const u8 *dst_mac,
119862306a36Sopenharmony_ci				  __be16 vlan_id, bool ttl_check, u32 pport)
119962306a36Sopenharmony_ci{
120062306a36Sopenharmony_ci	struct ofdpa_group_tbl_entry *entry;
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
120362306a36Sopenharmony_ci	if (!entry)
120462306a36Sopenharmony_ci		return -ENOMEM;
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	entry->group_id = ROCKER_GROUP_L3_UNICAST(index);
120762306a36Sopenharmony_ci	if (src_mac)
120862306a36Sopenharmony_ci		ether_addr_copy(entry->l3_unicast.eth_src, src_mac);
120962306a36Sopenharmony_ci	if (dst_mac)
121062306a36Sopenharmony_ci		ether_addr_copy(entry->l3_unicast.eth_dst, dst_mac);
121162306a36Sopenharmony_ci	entry->l3_unicast.vlan_id = vlan_id;
121262306a36Sopenharmony_ci	entry->l3_unicast.ttl_check = ttl_check;
121362306a36Sopenharmony_ci	entry->l3_unicast.group_id = ROCKER_GROUP_L2_INTERFACE(vlan_id, pport);
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	return ofdpa_group_tbl_do(ofdpa_port, flags, entry);
121662306a36Sopenharmony_ci}
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_cistatic struct ofdpa_neigh_tbl_entry *
121962306a36Sopenharmony_ciofdpa_neigh_tbl_find(const struct ofdpa *ofdpa, __be32 ip_addr)
122062306a36Sopenharmony_ci{
122162306a36Sopenharmony_ci	struct ofdpa_neigh_tbl_entry *found;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	hash_for_each_possible(ofdpa->neigh_tbl, found,
122462306a36Sopenharmony_ci			       entry, be32_to_cpu(ip_addr))
122562306a36Sopenharmony_ci		if (found->ip_addr == ip_addr)
122662306a36Sopenharmony_ci			return found;
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	return NULL;
122962306a36Sopenharmony_ci}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_cistatic void ofdpa_neigh_add(struct ofdpa *ofdpa,
123262306a36Sopenharmony_ci			    struct ofdpa_neigh_tbl_entry *entry)
123362306a36Sopenharmony_ci{
123462306a36Sopenharmony_ci	entry->index = ofdpa->neigh_tbl_next_index++;
123562306a36Sopenharmony_ci	entry->ref_count++;
123662306a36Sopenharmony_ci	hash_add(ofdpa->neigh_tbl, &entry->entry,
123762306a36Sopenharmony_ci		 be32_to_cpu(entry->ip_addr));
123862306a36Sopenharmony_ci}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_cistatic void ofdpa_neigh_del(struct ofdpa_neigh_tbl_entry *entry)
124162306a36Sopenharmony_ci{
124262306a36Sopenharmony_ci	if (--entry->ref_count == 0) {
124362306a36Sopenharmony_ci		hash_del(&entry->entry);
124462306a36Sopenharmony_ci		kfree(entry);
124562306a36Sopenharmony_ci	}
124662306a36Sopenharmony_ci}
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_cistatic void ofdpa_neigh_update(struct ofdpa_neigh_tbl_entry *entry,
124962306a36Sopenharmony_ci			       const u8 *eth_dst, bool ttl_check)
125062306a36Sopenharmony_ci{
125162306a36Sopenharmony_ci	if (eth_dst) {
125262306a36Sopenharmony_ci		ether_addr_copy(entry->eth_dst, eth_dst);
125362306a36Sopenharmony_ci		entry->ttl_check = ttl_check;
125462306a36Sopenharmony_ci	} else {
125562306a36Sopenharmony_ci		entry->ref_count++;
125662306a36Sopenharmony_ci	}
125762306a36Sopenharmony_ci}
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_cistatic int ofdpa_port_ipv4_neigh(struct ofdpa_port *ofdpa_port,
126062306a36Sopenharmony_ci				 int flags, __be32 ip_addr, const u8 *eth_dst)
126162306a36Sopenharmony_ci{
126262306a36Sopenharmony_ci	struct ofdpa *ofdpa = ofdpa_port->ofdpa;
126362306a36Sopenharmony_ci	struct ofdpa_neigh_tbl_entry *entry;
126462306a36Sopenharmony_ci	struct ofdpa_neigh_tbl_entry *found;
126562306a36Sopenharmony_ci	unsigned long lock_flags;
126662306a36Sopenharmony_ci	__be16 eth_type = htons(ETH_P_IP);
126762306a36Sopenharmony_ci	enum rocker_of_dpa_table_id goto_tbl =
126862306a36Sopenharmony_ci			ROCKER_OF_DPA_TABLE_ID_ACL_POLICY;
126962306a36Sopenharmony_ci	u32 group_id;
127062306a36Sopenharmony_ci	u32 priority = 0;
127162306a36Sopenharmony_ci	bool adding = !(flags & OFDPA_OP_FLAG_REMOVE);
127262306a36Sopenharmony_ci	bool updating;
127362306a36Sopenharmony_ci	bool removing;
127462306a36Sopenharmony_ci	int err = 0;
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
127762306a36Sopenharmony_ci	if (!entry)
127862306a36Sopenharmony_ci		return -ENOMEM;
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	spin_lock_irqsave(&ofdpa->neigh_tbl_lock, lock_flags);
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	found = ofdpa_neigh_tbl_find(ofdpa, ip_addr);
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	updating = found && adding;
128562306a36Sopenharmony_ci	removing = found && !adding;
128662306a36Sopenharmony_ci	adding = !found && adding;
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	if (adding) {
128962306a36Sopenharmony_ci		entry->ip_addr = ip_addr;
129062306a36Sopenharmony_ci		entry->dev = ofdpa_port->dev;
129162306a36Sopenharmony_ci		ether_addr_copy(entry->eth_dst, eth_dst);
129262306a36Sopenharmony_ci		entry->ttl_check = true;
129362306a36Sopenharmony_ci		ofdpa_neigh_add(ofdpa, entry);
129462306a36Sopenharmony_ci	} else if (removing) {
129562306a36Sopenharmony_ci		memcpy(entry, found, sizeof(*entry));
129662306a36Sopenharmony_ci		ofdpa_neigh_del(found);
129762306a36Sopenharmony_ci	} else if (updating) {
129862306a36Sopenharmony_ci		ofdpa_neigh_update(found, eth_dst, true);
129962306a36Sopenharmony_ci		memcpy(entry, found, sizeof(*entry));
130062306a36Sopenharmony_ci	} else {
130162306a36Sopenharmony_ci		err = -ENOENT;
130262306a36Sopenharmony_ci	}
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	spin_unlock_irqrestore(&ofdpa->neigh_tbl_lock, lock_flags);
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	if (err)
130762306a36Sopenharmony_ci		goto err_out;
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	/* For each active neighbor, we have an L3 unicast group and
131062306a36Sopenharmony_ci	 * a /32 route to the neighbor, which uses the L3 unicast
131162306a36Sopenharmony_ci	 * group.  The L3 unicast group can also be referred to by
131262306a36Sopenharmony_ci	 * other routes' nexthops.
131362306a36Sopenharmony_ci	 */
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	err = ofdpa_group_l3_unicast(ofdpa_port, flags,
131662306a36Sopenharmony_ci				     entry->index,
131762306a36Sopenharmony_ci				     ofdpa_port->dev->dev_addr,
131862306a36Sopenharmony_ci				     entry->eth_dst,
131962306a36Sopenharmony_ci				     ofdpa_port->internal_vlan_id,
132062306a36Sopenharmony_ci				     entry->ttl_check,
132162306a36Sopenharmony_ci				     ofdpa_port->pport);
132262306a36Sopenharmony_ci	if (err) {
132362306a36Sopenharmony_ci		netdev_err(ofdpa_port->dev, "Error (%d) L3 unicast group index %d\n",
132462306a36Sopenharmony_ci			   err, entry->index);
132562306a36Sopenharmony_ci		goto err_out;
132662306a36Sopenharmony_ci	}
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	if (adding || removing) {
132962306a36Sopenharmony_ci		group_id = ROCKER_GROUP_L3_UNICAST(entry->index);
133062306a36Sopenharmony_ci		err = ofdpa_flow_tbl_ucast4_routing(ofdpa_port,
133162306a36Sopenharmony_ci						    eth_type, ip_addr,
133262306a36Sopenharmony_ci						    inet_make_mask(32),
133362306a36Sopenharmony_ci						    priority, goto_tbl,
133462306a36Sopenharmony_ci						    group_id, NULL, flags);
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci		if (err)
133762306a36Sopenharmony_ci			netdev_err(ofdpa_port->dev, "Error (%d) /32 unicast route %pI4 group 0x%08x\n",
133862306a36Sopenharmony_ci				   err, &entry->ip_addr, group_id);
133962306a36Sopenharmony_ci	}
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_cierr_out:
134262306a36Sopenharmony_ci	if (!adding)
134362306a36Sopenharmony_ci		kfree(entry);
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	return err;
134662306a36Sopenharmony_ci}
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_cistatic int ofdpa_port_ipv4_resolve(struct ofdpa_port *ofdpa_port,
134962306a36Sopenharmony_ci				   __be32 ip_addr)
135062306a36Sopenharmony_ci{
135162306a36Sopenharmony_ci	struct net_device *dev = ofdpa_port->dev;
135262306a36Sopenharmony_ci	struct neighbour *n = __ipv4_neigh_lookup(dev, (__force u32)ip_addr);
135362306a36Sopenharmony_ci	int err = 0;
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	if (!n) {
135662306a36Sopenharmony_ci		n = neigh_create(&arp_tbl, &ip_addr, dev);
135762306a36Sopenharmony_ci		if (IS_ERR(n))
135862306a36Sopenharmony_ci			return PTR_ERR(n);
135962306a36Sopenharmony_ci	}
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	/* If the neigh is already resolved, then go ahead and
136262306a36Sopenharmony_ci	 * install the entry, otherwise start the ARP process to
136362306a36Sopenharmony_ci	 * resolve the neigh.
136462306a36Sopenharmony_ci	 */
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	if (n->nud_state & NUD_VALID)
136762306a36Sopenharmony_ci		err = ofdpa_port_ipv4_neigh(ofdpa_port, 0,
136862306a36Sopenharmony_ci					    ip_addr, n->ha);
136962306a36Sopenharmony_ci	else
137062306a36Sopenharmony_ci		neigh_event_send(n, NULL);
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	neigh_release(n);
137362306a36Sopenharmony_ci	return err;
137462306a36Sopenharmony_ci}
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_cistatic int ofdpa_port_ipv4_nh(struct ofdpa_port *ofdpa_port,
137762306a36Sopenharmony_ci			      int flags, __be32 ip_addr, u32 *index)
137862306a36Sopenharmony_ci{
137962306a36Sopenharmony_ci	struct ofdpa *ofdpa = ofdpa_port->ofdpa;
138062306a36Sopenharmony_ci	struct ofdpa_neigh_tbl_entry *entry;
138162306a36Sopenharmony_ci	struct ofdpa_neigh_tbl_entry *found;
138262306a36Sopenharmony_ci	unsigned long lock_flags;
138362306a36Sopenharmony_ci	bool adding = !(flags & OFDPA_OP_FLAG_REMOVE);
138462306a36Sopenharmony_ci	bool updating;
138562306a36Sopenharmony_ci	bool removing;
138662306a36Sopenharmony_ci	bool resolved = true;
138762306a36Sopenharmony_ci	int err = 0;
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
139062306a36Sopenharmony_ci	if (!entry)
139162306a36Sopenharmony_ci		return -ENOMEM;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	spin_lock_irqsave(&ofdpa->neigh_tbl_lock, lock_flags);
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	found = ofdpa_neigh_tbl_find(ofdpa, ip_addr);
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	updating = found && adding;
139862306a36Sopenharmony_ci	removing = found && !adding;
139962306a36Sopenharmony_ci	adding = !found && adding;
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	if (adding) {
140262306a36Sopenharmony_ci		entry->ip_addr = ip_addr;
140362306a36Sopenharmony_ci		entry->dev = ofdpa_port->dev;
140462306a36Sopenharmony_ci		ofdpa_neigh_add(ofdpa, entry);
140562306a36Sopenharmony_ci		*index = entry->index;
140662306a36Sopenharmony_ci		resolved = false;
140762306a36Sopenharmony_ci	} else if (removing) {
140862306a36Sopenharmony_ci		*index = found->index;
140962306a36Sopenharmony_ci		ofdpa_neigh_del(found);
141062306a36Sopenharmony_ci	} else if (updating) {
141162306a36Sopenharmony_ci		ofdpa_neigh_update(found, NULL, false);
141262306a36Sopenharmony_ci		resolved = !is_zero_ether_addr(found->eth_dst);
141362306a36Sopenharmony_ci		*index = found->index;
141462306a36Sopenharmony_ci	} else {
141562306a36Sopenharmony_ci		err = -ENOENT;
141662306a36Sopenharmony_ci	}
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	spin_unlock_irqrestore(&ofdpa->neigh_tbl_lock, lock_flags);
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	if (!adding)
142162306a36Sopenharmony_ci		kfree(entry);
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	if (err)
142462306a36Sopenharmony_ci		return err;
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci	/* Resolved means neigh ip_addr is resolved to neigh mac. */
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	if (!resolved)
142962306a36Sopenharmony_ci		err = ofdpa_port_ipv4_resolve(ofdpa_port, ip_addr);
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	return err;
143262306a36Sopenharmony_ci}
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_cistatic struct ofdpa_port *ofdpa_port_get(const struct ofdpa *ofdpa,
143562306a36Sopenharmony_ci					 int port_index)
143662306a36Sopenharmony_ci{
143762306a36Sopenharmony_ci	struct rocker_port *rocker_port;
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	rocker_port = ofdpa->rocker->ports[port_index];
144062306a36Sopenharmony_ci	return rocker_port ? rocker_port->wpriv : NULL;
144162306a36Sopenharmony_ci}
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_cistatic int ofdpa_port_vlan_flood_group(struct ofdpa_port *ofdpa_port,
144462306a36Sopenharmony_ci				       int flags, __be16 vlan_id)
144562306a36Sopenharmony_ci{
144662306a36Sopenharmony_ci	struct ofdpa_port *p;
144762306a36Sopenharmony_ci	const struct ofdpa *ofdpa = ofdpa_port->ofdpa;
144862306a36Sopenharmony_ci	unsigned int port_count = ofdpa->rocker->port_count;
144962306a36Sopenharmony_ci	u32 group_id = ROCKER_GROUP_L2_FLOOD(vlan_id, 0);
145062306a36Sopenharmony_ci	u32 *group_ids;
145162306a36Sopenharmony_ci	u8 group_count = 0;
145262306a36Sopenharmony_ci	int err = 0;
145362306a36Sopenharmony_ci	int i;
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	group_ids = kcalloc(port_count, sizeof(u32), GFP_KERNEL);
145662306a36Sopenharmony_ci	if (!group_ids)
145762306a36Sopenharmony_ci		return -ENOMEM;
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	/* Adjust the flood group for this VLAN.  The flood group
146062306a36Sopenharmony_ci	 * references an L2 interface group for each port in this
146162306a36Sopenharmony_ci	 * VLAN.
146262306a36Sopenharmony_ci	 */
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	for (i = 0; i < port_count; i++) {
146562306a36Sopenharmony_ci		p = ofdpa_port_get(ofdpa, i);
146662306a36Sopenharmony_ci		if (!p)
146762306a36Sopenharmony_ci			continue;
146862306a36Sopenharmony_ci		if (!ofdpa_port_is_bridged(p))
146962306a36Sopenharmony_ci			continue;
147062306a36Sopenharmony_ci		if (test_bit(ntohs(vlan_id), p->vlan_bitmap)) {
147162306a36Sopenharmony_ci			group_ids[group_count++] =
147262306a36Sopenharmony_ci				ROCKER_GROUP_L2_INTERFACE(vlan_id, p->pport);
147362306a36Sopenharmony_ci		}
147462306a36Sopenharmony_ci	}
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci	/* If there are no bridged ports in this VLAN, we're done */
147762306a36Sopenharmony_ci	if (group_count == 0)
147862306a36Sopenharmony_ci		goto no_ports_in_vlan;
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	err = ofdpa_group_l2_flood(ofdpa_port, flags, vlan_id,
148162306a36Sopenharmony_ci				   group_count, group_ids, group_id);
148262306a36Sopenharmony_ci	if (err)
148362306a36Sopenharmony_ci		netdev_err(ofdpa_port->dev, "Error (%d) port VLAN l2 flood group\n", err);
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_cino_ports_in_vlan:
148662306a36Sopenharmony_ci	kfree(group_ids);
148762306a36Sopenharmony_ci	return err;
148862306a36Sopenharmony_ci}
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_cistatic int ofdpa_port_vlan_l2_groups(struct ofdpa_port *ofdpa_port, int flags,
149162306a36Sopenharmony_ci				     __be16 vlan_id, bool pop_vlan)
149262306a36Sopenharmony_ci{
149362306a36Sopenharmony_ci	const struct ofdpa *ofdpa = ofdpa_port->ofdpa;
149462306a36Sopenharmony_ci	unsigned int port_count = ofdpa->rocker->port_count;
149562306a36Sopenharmony_ci	struct ofdpa_port *p;
149662306a36Sopenharmony_ci	bool adding = !(flags & OFDPA_OP_FLAG_REMOVE);
149762306a36Sopenharmony_ci	u32 out_pport;
149862306a36Sopenharmony_ci	int ref = 0;
149962306a36Sopenharmony_ci	int err;
150062306a36Sopenharmony_ci	int i;
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci	/* An L2 interface group for this port in this VLAN, but
150362306a36Sopenharmony_ci	 * only when port STP state is LEARNING|FORWARDING.
150462306a36Sopenharmony_ci	 */
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	if (ofdpa_port->stp_state == BR_STATE_LEARNING ||
150762306a36Sopenharmony_ci	    ofdpa_port->stp_state == BR_STATE_FORWARDING) {
150862306a36Sopenharmony_ci		out_pport = ofdpa_port->pport;
150962306a36Sopenharmony_ci		err = ofdpa_group_l2_interface(ofdpa_port, flags,
151062306a36Sopenharmony_ci					       vlan_id, out_pport, pop_vlan);
151162306a36Sopenharmony_ci		if (err) {
151262306a36Sopenharmony_ci			netdev_err(ofdpa_port->dev, "Error (%d) port VLAN l2 group for pport %d\n",
151362306a36Sopenharmony_ci				   err, out_pport);
151462306a36Sopenharmony_ci			return err;
151562306a36Sopenharmony_ci		}
151662306a36Sopenharmony_ci	}
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	/* An L2 interface group for this VLAN to CPU port.
151962306a36Sopenharmony_ci	 * Add when first port joins this VLAN and destroy when
152062306a36Sopenharmony_ci	 * last port leaves this VLAN.
152162306a36Sopenharmony_ci	 */
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	for (i = 0; i < port_count; i++) {
152462306a36Sopenharmony_ci		p = ofdpa_port_get(ofdpa, i);
152562306a36Sopenharmony_ci		if (p && test_bit(ntohs(vlan_id), p->vlan_bitmap))
152662306a36Sopenharmony_ci			ref++;
152762306a36Sopenharmony_ci	}
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	if ((!adding || ref != 1) && (adding || ref != 0))
153062306a36Sopenharmony_ci		return 0;
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci	out_pport = 0;
153362306a36Sopenharmony_ci	err = ofdpa_group_l2_interface(ofdpa_port, flags,
153462306a36Sopenharmony_ci				       vlan_id, out_pport, pop_vlan);
153562306a36Sopenharmony_ci	if (err) {
153662306a36Sopenharmony_ci		netdev_err(ofdpa_port->dev, "Error (%d) port VLAN l2 group for CPU port\n", err);
153762306a36Sopenharmony_ci		return err;
153862306a36Sopenharmony_ci	}
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	return 0;
154162306a36Sopenharmony_ci}
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_cistatic struct ofdpa_ctrl {
154462306a36Sopenharmony_ci	const u8 *eth_dst;
154562306a36Sopenharmony_ci	const u8 *eth_dst_mask;
154662306a36Sopenharmony_ci	__be16 eth_type;
154762306a36Sopenharmony_ci	bool acl;
154862306a36Sopenharmony_ci	bool bridge;
154962306a36Sopenharmony_ci	bool term;
155062306a36Sopenharmony_ci	bool copy_to_cpu;
155162306a36Sopenharmony_ci} ofdpa_ctrls[] = {
155262306a36Sopenharmony_ci	[OFDPA_CTRL_LINK_LOCAL_MCAST] = {
155362306a36Sopenharmony_ci		/* pass link local multicast pkts up to CPU for filtering */
155462306a36Sopenharmony_ci		.eth_dst = ll_mac,
155562306a36Sopenharmony_ci		.eth_dst_mask = ll_mask,
155662306a36Sopenharmony_ci		.acl = true,
155762306a36Sopenharmony_ci	},
155862306a36Sopenharmony_ci	[OFDPA_CTRL_LOCAL_ARP] = {
155962306a36Sopenharmony_ci		/* pass local ARP pkts up to CPU */
156062306a36Sopenharmony_ci		.eth_dst = zero_mac,
156162306a36Sopenharmony_ci		.eth_dst_mask = zero_mac,
156262306a36Sopenharmony_ci		.eth_type = htons(ETH_P_ARP),
156362306a36Sopenharmony_ci		.acl = true,
156462306a36Sopenharmony_ci	},
156562306a36Sopenharmony_ci	[OFDPA_CTRL_IPV4_MCAST] = {
156662306a36Sopenharmony_ci		/* pass IPv4 mcast pkts up to CPU, RFC 1112 */
156762306a36Sopenharmony_ci		.eth_dst = ipv4_mcast,
156862306a36Sopenharmony_ci		.eth_dst_mask = ipv4_mask,
156962306a36Sopenharmony_ci		.eth_type = htons(ETH_P_IP),
157062306a36Sopenharmony_ci		.term  = true,
157162306a36Sopenharmony_ci		.copy_to_cpu = true,
157262306a36Sopenharmony_ci	},
157362306a36Sopenharmony_ci	[OFDPA_CTRL_IPV6_MCAST] = {
157462306a36Sopenharmony_ci		/* pass IPv6 mcast pkts up to CPU, RFC 2464 */
157562306a36Sopenharmony_ci		.eth_dst = ipv6_mcast,
157662306a36Sopenharmony_ci		.eth_dst_mask = ipv6_mask,
157762306a36Sopenharmony_ci		.eth_type = htons(ETH_P_IPV6),
157862306a36Sopenharmony_ci		.term  = true,
157962306a36Sopenharmony_ci		.copy_to_cpu = true,
158062306a36Sopenharmony_ci	},
158162306a36Sopenharmony_ci	[OFDPA_CTRL_DFLT_BRIDGING] = {
158262306a36Sopenharmony_ci		/* flood any pkts on vlan */
158362306a36Sopenharmony_ci		.bridge = true,
158462306a36Sopenharmony_ci		.copy_to_cpu = true,
158562306a36Sopenharmony_ci	},
158662306a36Sopenharmony_ci	[OFDPA_CTRL_DFLT_OVS] = {
158762306a36Sopenharmony_ci		/* pass all pkts up to CPU */
158862306a36Sopenharmony_ci		.eth_dst = zero_mac,
158962306a36Sopenharmony_ci		.eth_dst_mask = zero_mac,
159062306a36Sopenharmony_ci		.acl = true,
159162306a36Sopenharmony_ci	},
159262306a36Sopenharmony_ci};
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_cistatic int ofdpa_port_ctrl_vlan_acl(struct ofdpa_port *ofdpa_port, int flags,
159562306a36Sopenharmony_ci				    const struct ofdpa_ctrl *ctrl, __be16 vlan_id)
159662306a36Sopenharmony_ci{
159762306a36Sopenharmony_ci	u32 in_pport = ofdpa_port->pport;
159862306a36Sopenharmony_ci	u32 in_pport_mask = 0xffffffff;
159962306a36Sopenharmony_ci	u32 out_pport = 0;
160062306a36Sopenharmony_ci	const u8 *eth_src = NULL;
160162306a36Sopenharmony_ci	const u8 *eth_src_mask = NULL;
160262306a36Sopenharmony_ci	__be16 vlan_id_mask = htons(0xffff);
160362306a36Sopenharmony_ci	u8 ip_proto = 0;
160462306a36Sopenharmony_ci	u8 ip_proto_mask = 0;
160562306a36Sopenharmony_ci	u8 ip_tos = 0;
160662306a36Sopenharmony_ci	u8 ip_tos_mask = 0;
160762306a36Sopenharmony_ci	u32 group_id = ROCKER_GROUP_L2_INTERFACE(vlan_id, out_pport);
160862306a36Sopenharmony_ci	int err;
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	err = ofdpa_flow_tbl_acl(ofdpa_port, flags,
161162306a36Sopenharmony_ci				 in_pport, in_pport_mask,
161262306a36Sopenharmony_ci				 eth_src, eth_src_mask,
161362306a36Sopenharmony_ci				 ctrl->eth_dst, ctrl->eth_dst_mask,
161462306a36Sopenharmony_ci				 ctrl->eth_type,
161562306a36Sopenharmony_ci				 vlan_id, vlan_id_mask,
161662306a36Sopenharmony_ci				 ip_proto, ip_proto_mask,
161762306a36Sopenharmony_ci				 ip_tos, ip_tos_mask,
161862306a36Sopenharmony_ci				 group_id);
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	if (err)
162162306a36Sopenharmony_ci		netdev_err(ofdpa_port->dev, "Error (%d) ctrl ACL\n", err);
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	return err;
162462306a36Sopenharmony_ci}
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_cistatic int ofdpa_port_ctrl_vlan_bridge(struct ofdpa_port *ofdpa_port,
162762306a36Sopenharmony_ci				       int flags, const struct ofdpa_ctrl *ctrl,
162862306a36Sopenharmony_ci				       __be16 vlan_id)
162962306a36Sopenharmony_ci{
163062306a36Sopenharmony_ci	enum rocker_of_dpa_table_id goto_tbl =
163162306a36Sopenharmony_ci			ROCKER_OF_DPA_TABLE_ID_ACL_POLICY;
163262306a36Sopenharmony_ci	u32 group_id = ROCKER_GROUP_L2_FLOOD(vlan_id, 0);
163362306a36Sopenharmony_ci	u32 tunnel_id = 0;
163462306a36Sopenharmony_ci	int err;
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	if (!ofdpa_port_is_bridged(ofdpa_port))
163762306a36Sopenharmony_ci		return 0;
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	err = ofdpa_flow_tbl_bridge(ofdpa_port, flags,
164062306a36Sopenharmony_ci				    ctrl->eth_dst, ctrl->eth_dst_mask,
164162306a36Sopenharmony_ci				    vlan_id, tunnel_id,
164262306a36Sopenharmony_ci				    goto_tbl, group_id, ctrl->copy_to_cpu);
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci	if (err)
164562306a36Sopenharmony_ci		netdev_err(ofdpa_port->dev, "Error (%d) ctrl FLOOD\n", err);
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_ci	return err;
164862306a36Sopenharmony_ci}
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_cistatic int ofdpa_port_ctrl_vlan_term(struct ofdpa_port *ofdpa_port, int flags,
165162306a36Sopenharmony_ci				     const struct ofdpa_ctrl *ctrl, __be16 vlan_id)
165262306a36Sopenharmony_ci{
165362306a36Sopenharmony_ci	u32 in_pport_mask = 0xffffffff;
165462306a36Sopenharmony_ci	__be16 vlan_id_mask = htons(0xffff);
165562306a36Sopenharmony_ci	int err;
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_ci	if (ntohs(vlan_id) == 0)
165862306a36Sopenharmony_ci		vlan_id = ofdpa_port->internal_vlan_id;
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci	err = ofdpa_flow_tbl_term_mac(ofdpa_port, ofdpa_port->pport, in_pport_mask,
166162306a36Sopenharmony_ci				      ctrl->eth_type, ctrl->eth_dst,
166262306a36Sopenharmony_ci				      ctrl->eth_dst_mask, vlan_id,
166362306a36Sopenharmony_ci				      vlan_id_mask, ctrl->copy_to_cpu,
166462306a36Sopenharmony_ci				      flags);
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci	if (err)
166762306a36Sopenharmony_ci		netdev_err(ofdpa_port->dev, "Error (%d) ctrl term\n", err);
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci	return err;
167062306a36Sopenharmony_ci}
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_cistatic int ofdpa_port_ctrl_vlan(struct ofdpa_port *ofdpa_port, int flags,
167362306a36Sopenharmony_ci				const struct ofdpa_ctrl *ctrl, __be16 vlan_id)
167462306a36Sopenharmony_ci{
167562306a36Sopenharmony_ci	if (ctrl->acl)
167662306a36Sopenharmony_ci		return ofdpa_port_ctrl_vlan_acl(ofdpa_port, flags,
167762306a36Sopenharmony_ci						ctrl, vlan_id);
167862306a36Sopenharmony_ci	if (ctrl->bridge)
167962306a36Sopenharmony_ci		return ofdpa_port_ctrl_vlan_bridge(ofdpa_port, flags,
168062306a36Sopenharmony_ci						   ctrl, vlan_id);
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci	if (ctrl->term)
168362306a36Sopenharmony_ci		return ofdpa_port_ctrl_vlan_term(ofdpa_port, flags,
168462306a36Sopenharmony_ci						 ctrl, vlan_id);
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci	return -EOPNOTSUPP;
168762306a36Sopenharmony_ci}
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_cistatic int ofdpa_port_ctrl_vlan_add(struct ofdpa_port *ofdpa_port, int flags,
169062306a36Sopenharmony_ci				    __be16 vlan_id)
169162306a36Sopenharmony_ci{
169262306a36Sopenharmony_ci	int err = 0;
169362306a36Sopenharmony_ci	int i;
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci	for (i = 0; i < OFDPA_CTRL_MAX; i++) {
169662306a36Sopenharmony_ci		if (ofdpa_port->ctrls[i]) {
169762306a36Sopenharmony_ci			err = ofdpa_port_ctrl_vlan(ofdpa_port, flags,
169862306a36Sopenharmony_ci						   &ofdpa_ctrls[i], vlan_id);
169962306a36Sopenharmony_ci			if (err)
170062306a36Sopenharmony_ci				return err;
170162306a36Sopenharmony_ci		}
170262306a36Sopenharmony_ci	}
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci	return err;
170562306a36Sopenharmony_ci}
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_cistatic int ofdpa_port_ctrl(struct ofdpa_port *ofdpa_port, int flags,
170862306a36Sopenharmony_ci			   const struct ofdpa_ctrl *ctrl)
170962306a36Sopenharmony_ci{
171062306a36Sopenharmony_ci	u16 vid;
171162306a36Sopenharmony_ci	int err = 0;
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	for (vid = 1; vid < VLAN_N_VID; vid++) {
171462306a36Sopenharmony_ci		if (!test_bit(vid, ofdpa_port->vlan_bitmap))
171562306a36Sopenharmony_ci			continue;
171662306a36Sopenharmony_ci		err = ofdpa_port_ctrl_vlan(ofdpa_port, flags,
171762306a36Sopenharmony_ci					   ctrl, htons(vid));
171862306a36Sopenharmony_ci		if (err)
171962306a36Sopenharmony_ci			break;
172062306a36Sopenharmony_ci	}
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci	return err;
172362306a36Sopenharmony_ci}
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_cistatic int ofdpa_port_vlan(struct ofdpa_port *ofdpa_port, int flags,
172662306a36Sopenharmony_ci			   u16 vid)
172762306a36Sopenharmony_ci{
172862306a36Sopenharmony_ci	enum rocker_of_dpa_table_id goto_tbl =
172962306a36Sopenharmony_ci			ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC;
173062306a36Sopenharmony_ci	u32 in_pport = ofdpa_port->pport;
173162306a36Sopenharmony_ci	__be16 vlan_id = htons(vid);
173262306a36Sopenharmony_ci	__be16 vlan_id_mask = htons(0xffff);
173362306a36Sopenharmony_ci	__be16 internal_vlan_id;
173462306a36Sopenharmony_ci	bool untagged;
173562306a36Sopenharmony_ci	bool adding = !(flags & OFDPA_OP_FLAG_REMOVE);
173662306a36Sopenharmony_ci	int err;
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci	internal_vlan_id = ofdpa_port_vid_to_vlan(ofdpa_port, vid, &untagged);
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	if (adding &&
174162306a36Sopenharmony_ci	    test_bit(ntohs(internal_vlan_id), ofdpa_port->vlan_bitmap))
174262306a36Sopenharmony_ci		return 0; /* already added */
174362306a36Sopenharmony_ci	else if (!adding &&
174462306a36Sopenharmony_ci		 !test_bit(ntohs(internal_vlan_id), ofdpa_port->vlan_bitmap))
174562306a36Sopenharmony_ci		return 0; /* already removed */
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci	change_bit(ntohs(internal_vlan_id), ofdpa_port->vlan_bitmap);
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci	if (adding) {
175062306a36Sopenharmony_ci		err = ofdpa_port_ctrl_vlan_add(ofdpa_port, flags,
175162306a36Sopenharmony_ci					       internal_vlan_id);
175262306a36Sopenharmony_ci		if (err) {
175362306a36Sopenharmony_ci			netdev_err(ofdpa_port->dev, "Error (%d) port ctrl vlan add\n", err);
175462306a36Sopenharmony_ci			goto err_vlan_add;
175562306a36Sopenharmony_ci		}
175662306a36Sopenharmony_ci	}
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	err = ofdpa_port_vlan_l2_groups(ofdpa_port, flags,
175962306a36Sopenharmony_ci					internal_vlan_id, untagged);
176062306a36Sopenharmony_ci	if (err) {
176162306a36Sopenharmony_ci		netdev_err(ofdpa_port->dev, "Error (%d) port VLAN l2 groups\n", err);
176262306a36Sopenharmony_ci		goto err_vlan_l2_groups;
176362306a36Sopenharmony_ci	}
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci	err = ofdpa_port_vlan_flood_group(ofdpa_port, flags,
176662306a36Sopenharmony_ci					  internal_vlan_id);
176762306a36Sopenharmony_ci	if (err) {
176862306a36Sopenharmony_ci		netdev_err(ofdpa_port->dev, "Error (%d) port VLAN l2 flood group\n", err);
176962306a36Sopenharmony_ci		goto err_flood_group;
177062306a36Sopenharmony_ci	}
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci	err = ofdpa_flow_tbl_vlan(ofdpa_port, flags,
177362306a36Sopenharmony_ci				  in_pport, vlan_id, vlan_id_mask,
177462306a36Sopenharmony_ci				  goto_tbl, untagged, internal_vlan_id);
177562306a36Sopenharmony_ci	if (err)
177662306a36Sopenharmony_ci		netdev_err(ofdpa_port->dev, "Error (%d) port VLAN table\n", err);
177762306a36Sopenharmony_ci
177862306a36Sopenharmony_ci	return 0;
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_cierr_vlan_add:
178162306a36Sopenharmony_cierr_vlan_l2_groups:
178262306a36Sopenharmony_cierr_flood_group:
178362306a36Sopenharmony_ci	change_bit(ntohs(internal_vlan_id), ofdpa_port->vlan_bitmap);
178462306a36Sopenharmony_ci	return err;
178562306a36Sopenharmony_ci}
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_cistatic int ofdpa_port_ig_tbl(struct ofdpa_port *ofdpa_port, int flags)
178862306a36Sopenharmony_ci{
178962306a36Sopenharmony_ci	enum rocker_of_dpa_table_id goto_tbl;
179062306a36Sopenharmony_ci	u32 in_pport;
179162306a36Sopenharmony_ci	u32 in_pport_mask;
179262306a36Sopenharmony_ci	int err;
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci	/* Normal Ethernet Frames.  Matches pkts from any local physical
179562306a36Sopenharmony_ci	 * ports.  Goto VLAN tbl.
179662306a36Sopenharmony_ci	 */
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci	in_pport = 0;
179962306a36Sopenharmony_ci	in_pport_mask = 0xffff0000;
180062306a36Sopenharmony_ci	goto_tbl = ROCKER_OF_DPA_TABLE_ID_VLAN;
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ci	err = ofdpa_flow_tbl_ig_port(ofdpa_port, flags,
180362306a36Sopenharmony_ci				     in_pport, in_pport_mask,
180462306a36Sopenharmony_ci				     goto_tbl);
180562306a36Sopenharmony_ci	if (err)
180662306a36Sopenharmony_ci		netdev_err(ofdpa_port->dev, "Error (%d) ingress port table entry\n", err);
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	return err;
180962306a36Sopenharmony_ci}
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_cistruct ofdpa_fdb_learn_work {
181262306a36Sopenharmony_ci	struct work_struct work;
181362306a36Sopenharmony_ci	struct ofdpa_port *ofdpa_port;
181462306a36Sopenharmony_ci	int flags;
181562306a36Sopenharmony_ci	u8 addr[ETH_ALEN];
181662306a36Sopenharmony_ci	u16 vid;
181762306a36Sopenharmony_ci};
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_cistatic void ofdpa_port_fdb_learn_work(struct work_struct *work)
182062306a36Sopenharmony_ci{
182162306a36Sopenharmony_ci	const struct ofdpa_fdb_learn_work *lw =
182262306a36Sopenharmony_ci		container_of(work, struct ofdpa_fdb_learn_work, work);
182362306a36Sopenharmony_ci	bool removing = (lw->flags & OFDPA_OP_FLAG_REMOVE);
182462306a36Sopenharmony_ci	struct switchdev_notifier_fdb_info info = {};
182562306a36Sopenharmony_ci	enum switchdev_notifier_type event;
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci	info.addr = lw->addr;
182862306a36Sopenharmony_ci	info.vid = lw->vid;
182962306a36Sopenharmony_ci	info.offloaded = !removing;
183062306a36Sopenharmony_ci	event = removing ? SWITCHDEV_FDB_DEL_TO_BRIDGE :
183162306a36Sopenharmony_ci			   SWITCHDEV_FDB_ADD_TO_BRIDGE;
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci	rtnl_lock();
183462306a36Sopenharmony_ci	call_switchdev_notifiers(event, lw->ofdpa_port->dev, &info.info, NULL);
183562306a36Sopenharmony_ci	rtnl_unlock();
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	kfree(work);
183862306a36Sopenharmony_ci}
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_cistatic int ofdpa_port_fdb_learn(struct ofdpa_port *ofdpa_port,
184162306a36Sopenharmony_ci				int flags, const u8 *addr, __be16 vlan_id)
184262306a36Sopenharmony_ci{
184362306a36Sopenharmony_ci	struct ofdpa_fdb_learn_work *lw;
184462306a36Sopenharmony_ci	enum rocker_of_dpa_table_id goto_tbl =
184562306a36Sopenharmony_ci			ROCKER_OF_DPA_TABLE_ID_ACL_POLICY;
184662306a36Sopenharmony_ci	u32 out_pport = ofdpa_port->pport;
184762306a36Sopenharmony_ci	u32 tunnel_id = 0;
184862306a36Sopenharmony_ci	u32 group_id = ROCKER_GROUP_NONE;
184962306a36Sopenharmony_ci	bool copy_to_cpu = false;
185062306a36Sopenharmony_ci	int err;
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	if (ofdpa_port_is_bridged(ofdpa_port))
185362306a36Sopenharmony_ci		group_id = ROCKER_GROUP_L2_INTERFACE(vlan_id, out_pport);
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci	if (!(flags & OFDPA_OP_FLAG_REFRESH)) {
185662306a36Sopenharmony_ci		err = ofdpa_flow_tbl_bridge(ofdpa_port, flags, addr,
185762306a36Sopenharmony_ci					    NULL, vlan_id, tunnel_id, goto_tbl,
185862306a36Sopenharmony_ci					    group_id, copy_to_cpu);
185962306a36Sopenharmony_ci		if (err)
186062306a36Sopenharmony_ci			return err;
186162306a36Sopenharmony_ci	}
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci	if (!ofdpa_port_is_bridged(ofdpa_port))
186462306a36Sopenharmony_ci		return 0;
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci	if (!(flags & OFDPA_OP_FLAG_LEARNED))
186762306a36Sopenharmony_ci		return 0;
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci	lw = kzalloc(sizeof(*lw), GFP_ATOMIC);
187062306a36Sopenharmony_ci	if (!lw)
187162306a36Sopenharmony_ci		return -ENOMEM;
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci	INIT_WORK(&lw->work, ofdpa_port_fdb_learn_work);
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_ci	lw->ofdpa_port = ofdpa_port;
187662306a36Sopenharmony_ci	lw->flags = flags;
187762306a36Sopenharmony_ci	ether_addr_copy(lw->addr, addr);
187862306a36Sopenharmony_ci	lw->vid = ofdpa_port_vlan_to_vid(ofdpa_port, vlan_id);
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci	schedule_work(&lw->work);
188162306a36Sopenharmony_ci	return 0;
188262306a36Sopenharmony_ci}
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_cistatic struct ofdpa_fdb_tbl_entry *
188562306a36Sopenharmony_ciofdpa_fdb_tbl_find(const struct ofdpa *ofdpa,
188662306a36Sopenharmony_ci		   const struct ofdpa_fdb_tbl_entry *match)
188762306a36Sopenharmony_ci{
188862306a36Sopenharmony_ci	struct ofdpa_fdb_tbl_entry *found;
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ci	hash_for_each_possible(ofdpa->fdb_tbl, found, entry, match->key_crc32)
189162306a36Sopenharmony_ci		if (memcmp(&found->key, &match->key, sizeof(found->key)) == 0)
189262306a36Sopenharmony_ci			return found;
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_ci	return NULL;
189562306a36Sopenharmony_ci}
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_cistatic int ofdpa_port_fdb(struct ofdpa_port *ofdpa_port,
189862306a36Sopenharmony_ci			  const unsigned char *addr,
189962306a36Sopenharmony_ci			  __be16 vlan_id, int flags)
190062306a36Sopenharmony_ci{
190162306a36Sopenharmony_ci	struct ofdpa *ofdpa = ofdpa_port->ofdpa;
190262306a36Sopenharmony_ci	struct ofdpa_fdb_tbl_entry *fdb;
190362306a36Sopenharmony_ci	struct ofdpa_fdb_tbl_entry *found;
190462306a36Sopenharmony_ci	bool removing = (flags & OFDPA_OP_FLAG_REMOVE);
190562306a36Sopenharmony_ci	unsigned long lock_flags;
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_ci	fdb = kzalloc(sizeof(*fdb), GFP_KERNEL);
190862306a36Sopenharmony_ci	if (!fdb)
190962306a36Sopenharmony_ci		return -ENOMEM;
191062306a36Sopenharmony_ci
191162306a36Sopenharmony_ci	fdb->learned = (flags & OFDPA_OP_FLAG_LEARNED);
191262306a36Sopenharmony_ci	fdb->touched = jiffies;
191362306a36Sopenharmony_ci	fdb->key.ofdpa_port = ofdpa_port;
191462306a36Sopenharmony_ci	ether_addr_copy(fdb->key.addr, addr);
191562306a36Sopenharmony_ci	fdb->key.vlan_id = vlan_id;
191662306a36Sopenharmony_ci	fdb->key_crc32 = crc32(~0, &fdb->key, sizeof(fdb->key));
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci	spin_lock_irqsave(&ofdpa->fdb_tbl_lock, lock_flags);
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_ci	found = ofdpa_fdb_tbl_find(ofdpa, fdb);
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_ci	if (found) {
192362306a36Sopenharmony_ci		found->touched = jiffies;
192462306a36Sopenharmony_ci		if (removing) {
192562306a36Sopenharmony_ci			kfree(fdb);
192662306a36Sopenharmony_ci			hash_del(&found->entry);
192762306a36Sopenharmony_ci		}
192862306a36Sopenharmony_ci	} else if (!removing) {
192962306a36Sopenharmony_ci		hash_add(ofdpa->fdb_tbl, &fdb->entry,
193062306a36Sopenharmony_ci			 fdb->key_crc32);
193162306a36Sopenharmony_ci	}
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ci	spin_unlock_irqrestore(&ofdpa->fdb_tbl_lock, lock_flags);
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci	/* Check if adding and already exists, or removing and can't find */
193662306a36Sopenharmony_ci	if (!found != !removing) {
193762306a36Sopenharmony_ci		kfree(fdb);
193862306a36Sopenharmony_ci		if (!found && removing)
193962306a36Sopenharmony_ci			return 0;
194062306a36Sopenharmony_ci		/* Refreshing existing to update aging timers */
194162306a36Sopenharmony_ci		flags |= OFDPA_OP_FLAG_REFRESH;
194262306a36Sopenharmony_ci	}
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_ci	return ofdpa_port_fdb_learn(ofdpa_port, flags, addr, vlan_id);
194562306a36Sopenharmony_ci}
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_cistatic int ofdpa_port_fdb_flush(struct ofdpa_port *ofdpa_port, int flags)
194862306a36Sopenharmony_ci{
194962306a36Sopenharmony_ci	struct ofdpa *ofdpa = ofdpa_port->ofdpa;
195062306a36Sopenharmony_ci	struct ofdpa_fdb_tbl_entry *found;
195162306a36Sopenharmony_ci	unsigned long lock_flags;
195262306a36Sopenharmony_ci	struct hlist_node *tmp;
195362306a36Sopenharmony_ci	int bkt;
195462306a36Sopenharmony_ci	int err = 0;
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_ci	if (ofdpa_port->stp_state == BR_STATE_LEARNING ||
195762306a36Sopenharmony_ci	    ofdpa_port->stp_state == BR_STATE_FORWARDING)
195862306a36Sopenharmony_ci		return 0;
195962306a36Sopenharmony_ci
196062306a36Sopenharmony_ci	flags |= OFDPA_OP_FLAG_NOWAIT | OFDPA_OP_FLAG_REMOVE;
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci	spin_lock_irqsave(&ofdpa->fdb_tbl_lock, lock_flags);
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	hash_for_each_safe(ofdpa->fdb_tbl, bkt, tmp, found, entry) {
196562306a36Sopenharmony_ci		if (found->key.ofdpa_port != ofdpa_port)
196662306a36Sopenharmony_ci			continue;
196762306a36Sopenharmony_ci		if (!found->learned)
196862306a36Sopenharmony_ci			continue;
196962306a36Sopenharmony_ci		err = ofdpa_port_fdb_learn(ofdpa_port, flags,
197062306a36Sopenharmony_ci					   found->key.addr,
197162306a36Sopenharmony_ci					   found->key.vlan_id);
197262306a36Sopenharmony_ci		if (err)
197362306a36Sopenharmony_ci			goto err_out;
197462306a36Sopenharmony_ci		hash_del(&found->entry);
197562306a36Sopenharmony_ci	}
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_cierr_out:
197862306a36Sopenharmony_ci	spin_unlock_irqrestore(&ofdpa->fdb_tbl_lock, lock_flags);
197962306a36Sopenharmony_ci
198062306a36Sopenharmony_ci	return err;
198162306a36Sopenharmony_ci}
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_cistatic void ofdpa_fdb_cleanup(struct timer_list *t)
198462306a36Sopenharmony_ci{
198562306a36Sopenharmony_ci	struct ofdpa *ofdpa = from_timer(ofdpa, t, fdb_cleanup_timer);
198662306a36Sopenharmony_ci	struct ofdpa_port *ofdpa_port;
198762306a36Sopenharmony_ci	struct ofdpa_fdb_tbl_entry *entry;
198862306a36Sopenharmony_ci	struct hlist_node *tmp;
198962306a36Sopenharmony_ci	unsigned long next_timer = jiffies + ofdpa->ageing_time;
199062306a36Sopenharmony_ci	unsigned long expires;
199162306a36Sopenharmony_ci	unsigned long lock_flags;
199262306a36Sopenharmony_ci	int flags = OFDPA_OP_FLAG_NOWAIT | OFDPA_OP_FLAG_REMOVE |
199362306a36Sopenharmony_ci		    OFDPA_OP_FLAG_LEARNED;
199462306a36Sopenharmony_ci	int bkt;
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	spin_lock_irqsave(&ofdpa->fdb_tbl_lock, lock_flags);
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_ci	hash_for_each_safe(ofdpa->fdb_tbl, bkt, tmp, entry, entry) {
199962306a36Sopenharmony_ci		if (!entry->learned)
200062306a36Sopenharmony_ci			continue;
200162306a36Sopenharmony_ci		ofdpa_port = entry->key.ofdpa_port;
200262306a36Sopenharmony_ci		expires = entry->touched + ofdpa_port->ageing_time;
200362306a36Sopenharmony_ci		if (time_before_eq(expires, jiffies)) {
200462306a36Sopenharmony_ci			ofdpa_port_fdb_learn(ofdpa_port, flags,
200562306a36Sopenharmony_ci					     entry->key.addr,
200662306a36Sopenharmony_ci					     entry->key.vlan_id);
200762306a36Sopenharmony_ci			hash_del(&entry->entry);
200862306a36Sopenharmony_ci		} else if (time_before(expires, next_timer)) {
200962306a36Sopenharmony_ci			next_timer = expires;
201062306a36Sopenharmony_ci		}
201162306a36Sopenharmony_ci	}
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_ci	spin_unlock_irqrestore(&ofdpa->fdb_tbl_lock, lock_flags);
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_ci	mod_timer(&ofdpa->fdb_cleanup_timer, round_jiffies_up(next_timer));
201662306a36Sopenharmony_ci}
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_cistatic int ofdpa_port_router_mac(struct ofdpa_port *ofdpa_port,
201962306a36Sopenharmony_ci				 int flags, __be16 vlan_id)
202062306a36Sopenharmony_ci{
202162306a36Sopenharmony_ci	u32 in_pport_mask = 0xffffffff;
202262306a36Sopenharmony_ci	__be16 eth_type;
202362306a36Sopenharmony_ci	const u8 *dst_mac_mask = ff_mac;
202462306a36Sopenharmony_ci	__be16 vlan_id_mask = htons(0xffff);
202562306a36Sopenharmony_ci	bool copy_to_cpu = false;
202662306a36Sopenharmony_ci	int err;
202762306a36Sopenharmony_ci
202862306a36Sopenharmony_ci	if (ntohs(vlan_id) == 0)
202962306a36Sopenharmony_ci		vlan_id = ofdpa_port->internal_vlan_id;
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_ci	eth_type = htons(ETH_P_IP);
203262306a36Sopenharmony_ci	err = ofdpa_flow_tbl_term_mac(ofdpa_port, ofdpa_port->pport,
203362306a36Sopenharmony_ci				      in_pport_mask, eth_type,
203462306a36Sopenharmony_ci				      ofdpa_port->dev->dev_addr,
203562306a36Sopenharmony_ci				      dst_mac_mask, vlan_id, vlan_id_mask,
203662306a36Sopenharmony_ci				      copy_to_cpu, flags);
203762306a36Sopenharmony_ci	if (err)
203862306a36Sopenharmony_ci		return err;
203962306a36Sopenharmony_ci
204062306a36Sopenharmony_ci	eth_type = htons(ETH_P_IPV6);
204162306a36Sopenharmony_ci	err = ofdpa_flow_tbl_term_mac(ofdpa_port, ofdpa_port->pport,
204262306a36Sopenharmony_ci				      in_pport_mask, eth_type,
204362306a36Sopenharmony_ci				      ofdpa_port->dev->dev_addr,
204462306a36Sopenharmony_ci				      dst_mac_mask, vlan_id, vlan_id_mask,
204562306a36Sopenharmony_ci				      copy_to_cpu, flags);
204662306a36Sopenharmony_ci
204762306a36Sopenharmony_ci	return err;
204862306a36Sopenharmony_ci}
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_cistatic int ofdpa_port_fwding(struct ofdpa_port *ofdpa_port, int flags)
205162306a36Sopenharmony_ci{
205262306a36Sopenharmony_ci	bool pop_vlan;
205362306a36Sopenharmony_ci	u32 out_pport;
205462306a36Sopenharmony_ci	__be16 vlan_id;
205562306a36Sopenharmony_ci	u16 vid;
205662306a36Sopenharmony_ci	int err;
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_ci	/* Port will be forwarding-enabled if its STP state is LEARNING
205962306a36Sopenharmony_ci	 * or FORWARDING.  Traffic from CPU can still egress, regardless of
206062306a36Sopenharmony_ci	 * port STP state.  Use L2 interface group on port VLANs as a way
206162306a36Sopenharmony_ci	 * to toggle port forwarding: if forwarding is disabled, L2
206262306a36Sopenharmony_ci	 * interface group will not exist.
206362306a36Sopenharmony_ci	 */
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_ci	if (ofdpa_port->stp_state != BR_STATE_LEARNING &&
206662306a36Sopenharmony_ci	    ofdpa_port->stp_state != BR_STATE_FORWARDING)
206762306a36Sopenharmony_ci		flags |= OFDPA_OP_FLAG_REMOVE;
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	out_pport = ofdpa_port->pport;
207062306a36Sopenharmony_ci	for (vid = 1; vid < VLAN_N_VID; vid++) {
207162306a36Sopenharmony_ci		if (!test_bit(vid, ofdpa_port->vlan_bitmap))
207262306a36Sopenharmony_ci			continue;
207362306a36Sopenharmony_ci		vlan_id = htons(vid);
207462306a36Sopenharmony_ci		pop_vlan = ofdpa_vlan_id_is_internal(vlan_id);
207562306a36Sopenharmony_ci		err = ofdpa_group_l2_interface(ofdpa_port, flags,
207662306a36Sopenharmony_ci					       vlan_id, out_pport, pop_vlan);
207762306a36Sopenharmony_ci		if (err) {
207862306a36Sopenharmony_ci			netdev_err(ofdpa_port->dev, "Error (%d) port VLAN l2 group for pport %d\n",
207962306a36Sopenharmony_ci				   err, out_pport);
208062306a36Sopenharmony_ci			return err;
208162306a36Sopenharmony_ci		}
208262306a36Sopenharmony_ci	}
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci	return 0;
208562306a36Sopenharmony_ci}
208662306a36Sopenharmony_ci
208762306a36Sopenharmony_cistatic int ofdpa_port_stp_update(struct ofdpa_port *ofdpa_port,
208862306a36Sopenharmony_ci				 int flags, u8 state)
208962306a36Sopenharmony_ci{
209062306a36Sopenharmony_ci	bool want[OFDPA_CTRL_MAX] = { 0, };
209162306a36Sopenharmony_ci	bool prev_ctrls[OFDPA_CTRL_MAX];
209262306a36Sopenharmony_ci	u8 prev_state;
209362306a36Sopenharmony_ci	int err;
209462306a36Sopenharmony_ci	int i;
209562306a36Sopenharmony_ci
209662306a36Sopenharmony_ci	memcpy(prev_ctrls, ofdpa_port->ctrls, sizeof(prev_ctrls));
209762306a36Sopenharmony_ci	prev_state = ofdpa_port->stp_state;
209862306a36Sopenharmony_ci
209962306a36Sopenharmony_ci	if (ofdpa_port->stp_state == state)
210062306a36Sopenharmony_ci		return 0;
210162306a36Sopenharmony_ci
210262306a36Sopenharmony_ci	ofdpa_port->stp_state = state;
210362306a36Sopenharmony_ci
210462306a36Sopenharmony_ci	switch (state) {
210562306a36Sopenharmony_ci	case BR_STATE_DISABLED:
210662306a36Sopenharmony_ci		/* port is completely disabled */
210762306a36Sopenharmony_ci		break;
210862306a36Sopenharmony_ci	case BR_STATE_LISTENING:
210962306a36Sopenharmony_ci	case BR_STATE_BLOCKING:
211062306a36Sopenharmony_ci		want[OFDPA_CTRL_LINK_LOCAL_MCAST] = true;
211162306a36Sopenharmony_ci		break;
211262306a36Sopenharmony_ci	case BR_STATE_LEARNING:
211362306a36Sopenharmony_ci	case BR_STATE_FORWARDING:
211462306a36Sopenharmony_ci		if (!ofdpa_port_is_ovsed(ofdpa_port))
211562306a36Sopenharmony_ci			want[OFDPA_CTRL_LINK_LOCAL_MCAST] = true;
211662306a36Sopenharmony_ci		want[OFDPA_CTRL_IPV4_MCAST] = true;
211762306a36Sopenharmony_ci		want[OFDPA_CTRL_IPV6_MCAST] = true;
211862306a36Sopenharmony_ci		if (ofdpa_port_is_bridged(ofdpa_port))
211962306a36Sopenharmony_ci			want[OFDPA_CTRL_DFLT_BRIDGING] = true;
212062306a36Sopenharmony_ci		else if (ofdpa_port_is_ovsed(ofdpa_port))
212162306a36Sopenharmony_ci			want[OFDPA_CTRL_DFLT_OVS] = true;
212262306a36Sopenharmony_ci		else
212362306a36Sopenharmony_ci			want[OFDPA_CTRL_LOCAL_ARP] = true;
212462306a36Sopenharmony_ci		break;
212562306a36Sopenharmony_ci	}
212662306a36Sopenharmony_ci
212762306a36Sopenharmony_ci	for (i = 0; i < OFDPA_CTRL_MAX; i++) {
212862306a36Sopenharmony_ci		if (want[i] != ofdpa_port->ctrls[i]) {
212962306a36Sopenharmony_ci			int ctrl_flags = flags |
213062306a36Sopenharmony_ci					 (want[i] ? 0 : OFDPA_OP_FLAG_REMOVE);
213162306a36Sopenharmony_ci			err = ofdpa_port_ctrl(ofdpa_port, ctrl_flags,
213262306a36Sopenharmony_ci					      &ofdpa_ctrls[i]);
213362306a36Sopenharmony_ci			if (err)
213462306a36Sopenharmony_ci				goto err_port_ctrl;
213562306a36Sopenharmony_ci			ofdpa_port->ctrls[i] = want[i];
213662306a36Sopenharmony_ci		}
213762306a36Sopenharmony_ci	}
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci	err = ofdpa_port_fdb_flush(ofdpa_port, flags);
214062306a36Sopenharmony_ci	if (err)
214162306a36Sopenharmony_ci		goto err_fdb_flush;
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_ci	err = ofdpa_port_fwding(ofdpa_port, flags);
214462306a36Sopenharmony_ci	if (err)
214562306a36Sopenharmony_ci		goto err_port_fwding;
214662306a36Sopenharmony_ci
214762306a36Sopenharmony_ci	return 0;
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_cierr_port_ctrl:
215062306a36Sopenharmony_cierr_fdb_flush:
215162306a36Sopenharmony_cierr_port_fwding:
215262306a36Sopenharmony_ci	memcpy(ofdpa_port->ctrls, prev_ctrls, sizeof(prev_ctrls));
215362306a36Sopenharmony_ci	ofdpa_port->stp_state = prev_state;
215462306a36Sopenharmony_ci	return err;
215562306a36Sopenharmony_ci}
215662306a36Sopenharmony_ci
215762306a36Sopenharmony_cistatic int ofdpa_port_fwd_enable(struct ofdpa_port *ofdpa_port, int flags)
215862306a36Sopenharmony_ci{
215962306a36Sopenharmony_ci	if (ofdpa_port_is_bridged(ofdpa_port))
216062306a36Sopenharmony_ci		/* bridge STP will enable port */
216162306a36Sopenharmony_ci		return 0;
216262306a36Sopenharmony_ci
216362306a36Sopenharmony_ci	/* port is not bridged, so simulate going to FORWARDING state */
216462306a36Sopenharmony_ci	return ofdpa_port_stp_update(ofdpa_port, flags,
216562306a36Sopenharmony_ci				     BR_STATE_FORWARDING);
216662306a36Sopenharmony_ci}
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_cistatic int ofdpa_port_fwd_disable(struct ofdpa_port *ofdpa_port, int flags)
216962306a36Sopenharmony_ci{
217062306a36Sopenharmony_ci	if (ofdpa_port_is_bridged(ofdpa_port))
217162306a36Sopenharmony_ci		/* bridge STP will disable port */
217262306a36Sopenharmony_ci		return 0;
217362306a36Sopenharmony_ci
217462306a36Sopenharmony_ci	/* port is not bridged, so simulate going to DISABLED state */
217562306a36Sopenharmony_ci	return ofdpa_port_stp_update(ofdpa_port, flags,
217662306a36Sopenharmony_ci				     BR_STATE_DISABLED);
217762306a36Sopenharmony_ci}
217862306a36Sopenharmony_ci
217962306a36Sopenharmony_cistatic int ofdpa_port_vlan_add(struct ofdpa_port *ofdpa_port,
218062306a36Sopenharmony_ci			       u16 vid, u16 flags)
218162306a36Sopenharmony_ci{
218262306a36Sopenharmony_ci	int err;
218362306a36Sopenharmony_ci
218462306a36Sopenharmony_ci	/* XXX deal with flags for PVID and untagged */
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_ci	err = ofdpa_port_vlan(ofdpa_port, 0, vid);
218762306a36Sopenharmony_ci	if (err)
218862306a36Sopenharmony_ci		return err;
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci	err = ofdpa_port_router_mac(ofdpa_port, 0, htons(vid));
219162306a36Sopenharmony_ci	if (err)
219262306a36Sopenharmony_ci		ofdpa_port_vlan(ofdpa_port,
219362306a36Sopenharmony_ci				OFDPA_OP_FLAG_REMOVE, vid);
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci	return err;
219662306a36Sopenharmony_ci}
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_cistatic int ofdpa_port_vlan_del(struct ofdpa_port *ofdpa_port,
219962306a36Sopenharmony_ci			       u16 vid, u16 flags)
220062306a36Sopenharmony_ci{
220162306a36Sopenharmony_ci	int err;
220262306a36Sopenharmony_ci
220362306a36Sopenharmony_ci	err = ofdpa_port_router_mac(ofdpa_port, OFDPA_OP_FLAG_REMOVE,
220462306a36Sopenharmony_ci				    htons(vid));
220562306a36Sopenharmony_ci	if (err)
220662306a36Sopenharmony_ci		return err;
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_ci	return ofdpa_port_vlan(ofdpa_port, OFDPA_OP_FLAG_REMOVE,
220962306a36Sopenharmony_ci			       vid);
221062306a36Sopenharmony_ci}
221162306a36Sopenharmony_ci
221262306a36Sopenharmony_cistatic struct ofdpa_internal_vlan_tbl_entry *
221362306a36Sopenharmony_ciofdpa_internal_vlan_tbl_find(const struct ofdpa *ofdpa, int ifindex)
221462306a36Sopenharmony_ci{
221562306a36Sopenharmony_ci	struct ofdpa_internal_vlan_tbl_entry *found;
221662306a36Sopenharmony_ci
221762306a36Sopenharmony_ci	hash_for_each_possible(ofdpa->internal_vlan_tbl, found,
221862306a36Sopenharmony_ci			       entry, ifindex) {
221962306a36Sopenharmony_ci		if (found->ifindex == ifindex)
222062306a36Sopenharmony_ci			return found;
222162306a36Sopenharmony_ci	}
222262306a36Sopenharmony_ci
222362306a36Sopenharmony_ci	return NULL;
222462306a36Sopenharmony_ci}
222562306a36Sopenharmony_ci
222662306a36Sopenharmony_cistatic __be16 ofdpa_port_internal_vlan_id_get(struct ofdpa_port *ofdpa_port,
222762306a36Sopenharmony_ci					      int ifindex)
222862306a36Sopenharmony_ci{
222962306a36Sopenharmony_ci	struct ofdpa *ofdpa = ofdpa_port->ofdpa;
223062306a36Sopenharmony_ci	struct ofdpa_internal_vlan_tbl_entry *entry;
223162306a36Sopenharmony_ci	struct ofdpa_internal_vlan_tbl_entry *found;
223262306a36Sopenharmony_ci	unsigned long lock_flags;
223362306a36Sopenharmony_ci	int i;
223462306a36Sopenharmony_ci
223562306a36Sopenharmony_ci	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
223662306a36Sopenharmony_ci	if (!entry)
223762306a36Sopenharmony_ci		return 0;
223862306a36Sopenharmony_ci
223962306a36Sopenharmony_ci	entry->ifindex = ifindex;
224062306a36Sopenharmony_ci
224162306a36Sopenharmony_ci	spin_lock_irqsave(&ofdpa->internal_vlan_tbl_lock, lock_flags);
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_ci	found = ofdpa_internal_vlan_tbl_find(ofdpa, ifindex);
224462306a36Sopenharmony_ci	if (found) {
224562306a36Sopenharmony_ci		kfree(entry);
224662306a36Sopenharmony_ci		goto found;
224762306a36Sopenharmony_ci	}
224862306a36Sopenharmony_ci
224962306a36Sopenharmony_ci	found = entry;
225062306a36Sopenharmony_ci	hash_add(ofdpa->internal_vlan_tbl, &found->entry, found->ifindex);
225162306a36Sopenharmony_ci
225262306a36Sopenharmony_ci	for (i = 0; i < OFDPA_N_INTERNAL_VLANS; i++) {
225362306a36Sopenharmony_ci		if (test_and_set_bit(i, ofdpa->internal_vlan_bitmap))
225462306a36Sopenharmony_ci			continue;
225562306a36Sopenharmony_ci		found->vlan_id = htons(OFDPA_INTERNAL_VLAN_ID_BASE + i);
225662306a36Sopenharmony_ci		goto found;
225762306a36Sopenharmony_ci	}
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci	netdev_err(ofdpa_port->dev, "Out of internal VLAN IDs\n");
226062306a36Sopenharmony_ci
226162306a36Sopenharmony_cifound:
226262306a36Sopenharmony_ci	found->ref_count++;
226362306a36Sopenharmony_ci	spin_unlock_irqrestore(&ofdpa->internal_vlan_tbl_lock, lock_flags);
226462306a36Sopenharmony_ci
226562306a36Sopenharmony_ci	return found->vlan_id;
226662306a36Sopenharmony_ci}
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_cistatic int ofdpa_port_fib_ipv4(struct ofdpa_port *ofdpa_port,  __be32 dst,
226962306a36Sopenharmony_ci			       int dst_len, struct fib_info *fi, u32 tb_id,
227062306a36Sopenharmony_ci			       int flags)
227162306a36Sopenharmony_ci{
227262306a36Sopenharmony_ci	const struct fib_nh *nh;
227362306a36Sopenharmony_ci	__be16 eth_type = htons(ETH_P_IP);
227462306a36Sopenharmony_ci	__be32 dst_mask = inet_make_mask(dst_len);
227562306a36Sopenharmony_ci	__be16 internal_vlan_id = ofdpa_port->internal_vlan_id;
227662306a36Sopenharmony_ci	u32 priority = fi->fib_priority;
227762306a36Sopenharmony_ci	enum rocker_of_dpa_table_id goto_tbl =
227862306a36Sopenharmony_ci		ROCKER_OF_DPA_TABLE_ID_ACL_POLICY;
227962306a36Sopenharmony_ci	u32 group_id;
228062306a36Sopenharmony_ci	bool nh_on_port;
228162306a36Sopenharmony_ci	bool has_gw;
228262306a36Sopenharmony_ci	u32 index;
228362306a36Sopenharmony_ci	int err;
228462306a36Sopenharmony_ci
228562306a36Sopenharmony_ci	/* XXX support ECMP */
228662306a36Sopenharmony_ci
228762306a36Sopenharmony_ci	nh = fib_info_nh(fi, 0);
228862306a36Sopenharmony_ci	nh_on_port = (nh->fib_nh_dev == ofdpa_port->dev);
228962306a36Sopenharmony_ci	has_gw = !!nh->fib_nh_gw4;
229062306a36Sopenharmony_ci
229162306a36Sopenharmony_ci	if (has_gw && nh_on_port) {
229262306a36Sopenharmony_ci		err = ofdpa_port_ipv4_nh(ofdpa_port, flags,
229362306a36Sopenharmony_ci					 nh->fib_nh_gw4, &index);
229462306a36Sopenharmony_ci		if (err)
229562306a36Sopenharmony_ci			return err;
229662306a36Sopenharmony_ci
229762306a36Sopenharmony_ci		group_id = ROCKER_GROUP_L3_UNICAST(index);
229862306a36Sopenharmony_ci	} else {
229962306a36Sopenharmony_ci		/* Send to CPU for processing */
230062306a36Sopenharmony_ci		group_id = ROCKER_GROUP_L2_INTERFACE(internal_vlan_id, 0);
230162306a36Sopenharmony_ci	}
230262306a36Sopenharmony_ci
230362306a36Sopenharmony_ci	err = ofdpa_flow_tbl_ucast4_routing(ofdpa_port, eth_type, dst,
230462306a36Sopenharmony_ci					    dst_mask, priority, goto_tbl,
230562306a36Sopenharmony_ci					    group_id, fi, flags);
230662306a36Sopenharmony_ci	if (err)
230762306a36Sopenharmony_ci		netdev_err(ofdpa_port->dev, "Error (%d) IPv4 route %pI4\n",
230862306a36Sopenharmony_ci			   err, &dst);
230962306a36Sopenharmony_ci
231062306a36Sopenharmony_ci	return err;
231162306a36Sopenharmony_ci}
231262306a36Sopenharmony_ci
231362306a36Sopenharmony_cistatic void
231462306a36Sopenharmony_ciofdpa_port_internal_vlan_id_put(const struct ofdpa_port *ofdpa_port,
231562306a36Sopenharmony_ci				int ifindex)
231662306a36Sopenharmony_ci{
231762306a36Sopenharmony_ci	struct ofdpa *ofdpa = ofdpa_port->ofdpa;
231862306a36Sopenharmony_ci	struct ofdpa_internal_vlan_tbl_entry *found;
231962306a36Sopenharmony_ci	unsigned long lock_flags;
232062306a36Sopenharmony_ci	unsigned long bit;
232162306a36Sopenharmony_ci
232262306a36Sopenharmony_ci	spin_lock_irqsave(&ofdpa->internal_vlan_tbl_lock, lock_flags);
232362306a36Sopenharmony_ci
232462306a36Sopenharmony_ci	found = ofdpa_internal_vlan_tbl_find(ofdpa, ifindex);
232562306a36Sopenharmony_ci	if (!found) {
232662306a36Sopenharmony_ci		netdev_err(ofdpa_port->dev,
232762306a36Sopenharmony_ci			   "ifindex (%d) not found in internal VLAN tbl\n",
232862306a36Sopenharmony_ci			   ifindex);
232962306a36Sopenharmony_ci		goto not_found;
233062306a36Sopenharmony_ci	}
233162306a36Sopenharmony_ci
233262306a36Sopenharmony_ci	if (--found->ref_count <= 0) {
233362306a36Sopenharmony_ci		bit = ntohs(found->vlan_id) - OFDPA_INTERNAL_VLAN_ID_BASE;
233462306a36Sopenharmony_ci		clear_bit(bit, ofdpa->internal_vlan_bitmap);
233562306a36Sopenharmony_ci		hash_del(&found->entry);
233662306a36Sopenharmony_ci		kfree(found);
233762306a36Sopenharmony_ci	}
233862306a36Sopenharmony_ci
233962306a36Sopenharmony_cinot_found:
234062306a36Sopenharmony_ci	spin_unlock_irqrestore(&ofdpa->internal_vlan_tbl_lock, lock_flags);
234162306a36Sopenharmony_ci}
234262306a36Sopenharmony_ci
234362306a36Sopenharmony_ci/**********************************
234462306a36Sopenharmony_ci * Rocker world ops implementation
234562306a36Sopenharmony_ci **********************************/
234662306a36Sopenharmony_ci
234762306a36Sopenharmony_cistatic int ofdpa_init(struct rocker *rocker)
234862306a36Sopenharmony_ci{
234962306a36Sopenharmony_ci	struct ofdpa *ofdpa = rocker->wpriv;
235062306a36Sopenharmony_ci
235162306a36Sopenharmony_ci	ofdpa->rocker = rocker;
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_ci	hash_init(ofdpa->flow_tbl);
235462306a36Sopenharmony_ci	spin_lock_init(&ofdpa->flow_tbl_lock);
235562306a36Sopenharmony_ci
235662306a36Sopenharmony_ci	hash_init(ofdpa->group_tbl);
235762306a36Sopenharmony_ci	spin_lock_init(&ofdpa->group_tbl_lock);
235862306a36Sopenharmony_ci
235962306a36Sopenharmony_ci	hash_init(ofdpa->fdb_tbl);
236062306a36Sopenharmony_ci	spin_lock_init(&ofdpa->fdb_tbl_lock);
236162306a36Sopenharmony_ci
236262306a36Sopenharmony_ci	hash_init(ofdpa->internal_vlan_tbl);
236362306a36Sopenharmony_ci	spin_lock_init(&ofdpa->internal_vlan_tbl_lock);
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci	hash_init(ofdpa->neigh_tbl);
236662306a36Sopenharmony_ci	spin_lock_init(&ofdpa->neigh_tbl_lock);
236762306a36Sopenharmony_ci
236862306a36Sopenharmony_ci	timer_setup(&ofdpa->fdb_cleanup_timer, ofdpa_fdb_cleanup, 0);
236962306a36Sopenharmony_ci	mod_timer(&ofdpa->fdb_cleanup_timer, jiffies);
237062306a36Sopenharmony_ci
237162306a36Sopenharmony_ci	ofdpa->ageing_time = BR_DEFAULT_AGEING_TIME;
237262306a36Sopenharmony_ci
237362306a36Sopenharmony_ci	return 0;
237462306a36Sopenharmony_ci}
237562306a36Sopenharmony_ci
237662306a36Sopenharmony_cistatic void ofdpa_fini(struct rocker *rocker)
237762306a36Sopenharmony_ci{
237862306a36Sopenharmony_ci	struct ofdpa *ofdpa = rocker->wpriv;
237962306a36Sopenharmony_ci
238062306a36Sopenharmony_ci	unsigned long flags;
238162306a36Sopenharmony_ci	struct ofdpa_flow_tbl_entry *flow_entry;
238262306a36Sopenharmony_ci	struct ofdpa_group_tbl_entry *group_entry;
238362306a36Sopenharmony_ci	struct ofdpa_fdb_tbl_entry *fdb_entry;
238462306a36Sopenharmony_ci	struct ofdpa_internal_vlan_tbl_entry *internal_vlan_entry;
238562306a36Sopenharmony_ci	struct ofdpa_neigh_tbl_entry *neigh_entry;
238662306a36Sopenharmony_ci	struct hlist_node *tmp;
238762306a36Sopenharmony_ci	int bkt;
238862306a36Sopenharmony_ci
238962306a36Sopenharmony_ci	del_timer_sync(&ofdpa->fdb_cleanup_timer);
239062306a36Sopenharmony_ci	flush_workqueue(rocker->rocker_owq);
239162306a36Sopenharmony_ci
239262306a36Sopenharmony_ci	spin_lock_irqsave(&ofdpa->flow_tbl_lock, flags);
239362306a36Sopenharmony_ci	hash_for_each_safe(ofdpa->flow_tbl, bkt, tmp, flow_entry, entry)
239462306a36Sopenharmony_ci		hash_del(&flow_entry->entry);
239562306a36Sopenharmony_ci	spin_unlock_irqrestore(&ofdpa->flow_tbl_lock, flags);
239662306a36Sopenharmony_ci
239762306a36Sopenharmony_ci	spin_lock_irqsave(&ofdpa->group_tbl_lock, flags);
239862306a36Sopenharmony_ci	hash_for_each_safe(ofdpa->group_tbl, bkt, tmp, group_entry, entry)
239962306a36Sopenharmony_ci		hash_del(&group_entry->entry);
240062306a36Sopenharmony_ci	spin_unlock_irqrestore(&ofdpa->group_tbl_lock, flags);
240162306a36Sopenharmony_ci
240262306a36Sopenharmony_ci	spin_lock_irqsave(&ofdpa->fdb_tbl_lock, flags);
240362306a36Sopenharmony_ci	hash_for_each_safe(ofdpa->fdb_tbl, bkt, tmp, fdb_entry, entry)
240462306a36Sopenharmony_ci		hash_del(&fdb_entry->entry);
240562306a36Sopenharmony_ci	spin_unlock_irqrestore(&ofdpa->fdb_tbl_lock, flags);
240662306a36Sopenharmony_ci
240762306a36Sopenharmony_ci	spin_lock_irqsave(&ofdpa->internal_vlan_tbl_lock, flags);
240862306a36Sopenharmony_ci	hash_for_each_safe(ofdpa->internal_vlan_tbl, bkt,
240962306a36Sopenharmony_ci			   tmp, internal_vlan_entry, entry)
241062306a36Sopenharmony_ci		hash_del(&internal_vlan_entry->entry);
241162306a36Sopenharmony_ci	spin_unlock_irqrestore(&ofdpa->internal_vlan_tbl_lock, flags);
241262306a36Sopenharmony_ci
241362306a36Sopenharmony_ci	spin_lock_irqsave(&ofdpa->neigh_tbl_lock, flags);
241462306a36Sopenharmony_ci	hash_for_each_safe(ofdpa->neigh_tbl, bkt, tmp, neigh_entry, entry)
241562306a36Sopenharmony_ci		hash_del(&neigh_entry->entry);
241662306a36Sopenharmony_ci	spin_unlock_irqrestore(&ofdpa->neigh_tbl_lock, flags);
241762306a36Sopenharmony_ci}
241862306a36Sopenharmony_ci
241962306a36Sopenharmony_cistatic int ofdpa_port_pre_init(struct rocker_port *rocker_port)
242062306a36Sopenharmony_ci{
242162306a36Sopenharmony_ci	struct ofdpa_port *ofdpa_port = rocker_port->wpriv;
242262306a36Sopenharmony_ci
242362306a36Sopenharmony_ci	ofdpa_port->ofdpa = rocker_port->rocker->wpriv;
242462306a36Sopenharmony_ci	ofdpa_port->rocker_port = rocker_port;
242562306a36Sopenharmony_ci	ofdpa_port->dev = rocker_port->dev;
242662306a36Sopenharmony_ci	ofdpa_port->pport = rocker_port->pport;
242762306a36Sopenharmony_ci	ofdpa_port->brport_flags = BR_LEARNING;
242862306a36Sopenharmony_ci	ofdpa_port->ageing_time = BR_DEFAULT_AGEING_TIME;
242962306a36Sopenharmony_ci	return 0;
243062306a36Sopenharmony_ci}
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_cistatic int ofdpa_port_init(struct rocker_port *rocker_port)
243362306a36Sopenharmony_ci{
243462306a36Sopenharmony_ci	struct ofdpa_port *ofdpa_port = rocker_port->wpriv;
243562306a36Sopenharmony_ci	int err;
243662306a36Sopenharmony_ci
243762306a36Sopenharmony_ci	rocker_port_set_learning(rocker_port,
243862306a36Sopenharmony_ci				 !!(ofdpa_port->brport_flags & BR_LEARNING));
243962306a36Sopenharmony_ci
244062306a36Sopenharmony_ci	err = ofdpa_port_ig_tbl(ofdpa_port, 0);
244162306a36Sopenharmony_ci	if (err) {
244262306a36Sopenharmony_ci		netdev_err(ofdpa_port->dev, "install ig port table failed\n");
244362306a36Sopenharmony_ci		return err;
244462306a36Sopenharmony_ci	}
244562306a36Sopenharmony_ci
244662306a36Sopenharmony_ci	ofdpa_port->internal_vlan_id =
244762306a36Sopenharmony_ci		ofdpa_port_internal_vlan_id_get(ofdpa_port,
244862306a36Sopenharmony_ci						ofdpa_port->dev->ifindex);
244962306a36Sopenharmony_ci
245062306a36Sopenharmony_ci	err = ofdpa_port_vlan_add(ofdpa_port, OFDPA_UNTAGGED_VID, 0);
245162306a36Sopenharmony_ci	if (err) {
245262306a36Sopenharmony_ci		netdev_err(ofdpa_port->dev, "install untagged VLAN failed\n");
245362306a36Sopenharmony_ci		goto err_untagged_vlan;
245462306a36Sopenharmony_ci	}
245562306a36Sopenharmony_ci	return 0;
245662306a36Sopenharmony_ci
245762306a36Sopenharmony_cierr_untagged_vlan:
245862306a36Sopenharmony_ci	ofdpa_port_ig_tbl(ofdpa_port, OFDPA_OP_FLAG_REMOVE);
245962306a36Sopenharmony_ci	return err;
246062306a36Sopenharmony_ci}
246162306a36Sopenharmony_ci
246262306a36Sopenharmony_cistatic void ofdpa_port_fini(struct rocker_port *rocker_port)
246362306a36Sopenharmony_ci{
246462306a36Sopenharmony_ci	struct ofdpa_port *ofdpa_port = rocker_port->wpriv;
246562306a36Sopenharmony_ci
246662306a36Sopenharmony_ci	ofdpa_port_ig_tbl(ofdpa_port, OFDPA_OP_FLAG_REMOVE);
246762306a36Sopenharmony_ci}
246862306a36Sopenharmony_ci
246962306a36Sopenharmony_cistatic int ofdpa_port_open(struct rocker_port *rocker_port)
247062306a36Sopenharmony_ci{
247162306a36Sopenharmony_ci	struct ofdpa_port *ofdpa_port = rocker_port->wpriv;
247262306a36Sopenharmony_ci
247362306a36Sopenharmony_ci	return ofdpa_port_fwd_enable(ofdpa_port, 0);
247462306a36Sopenharmony_ci}
247562306a36Sopenharmony_ci
247662306a36Sopenharmony_cistatic void ofdpa_port_stop(struct rocker_port *rocker_port)
247762306a36Sopenharmony_ci{
247862306a36Sopenharmony_ci	struct ofdpa_port *ofdpa_port = rocker_port->wpriv;
247962306a36Sopenharmony_ci
248062306a36Sopenharmony_ci	ofdpa_port_fwd_disable(ofdpa_port, OFDPA_OP_FLAG_NOWAIT);
248162306a36Sopenharmony_ci}
248262306a36Sopenharmony_ci
248362306a36Sopenharmony_cistatic int ofdpa_port_attr_stp_state_set(struct rocker_port *rocker_port,
248462306a36Sopenharmony_ci					 u8 state)
248562306a36Sopenharmony_ci{
248662306a36Sopenharmony_ci	struct ofdpa_port *ofdpa_port = rocker_port->wpriv;
248762306a36Sopenharmony_ci
248862306a36Sopenharmony_ci	return ofdpa_port_stp_update(ofdpa_port, 0, state);
248962306a36Sopenharmony_ci}
249062306a36Sopenharmony_ci
249162306a36Sopenharmony_cistatic int ofdpa_port_attr_bridge_flags_set(struct rocker_port *rocker_port,
249262306a36Sopenharmony_ci					    unsigned long brport_flags)
249362306a36Sopenharmony_ci{
249462306a36Sopenharmony_ci	struct ofdpa_port *ofdpa_port = rocker_port->wpriv;
249562306a36Sopenharmony_ci	unsigned long orig_flags;
249662306a36Sopenharmony_ci	int err = 0;
249762306a36Sopenharmony_ci
249862306a36Sopenharmony_ci	orig_flags = ofdpa_port->brport_flags;
249962306a36Sopenharmony_ci	ofdpa_port->brport_flags = brport_flags;
250062306a36Sopenharmony_ci
250162306a36Sopenharmony_ci	if ((orig_flags ^ ofdpa_port->brport_flags) & BR_LEARNING)
250262306a36Sopenharmony_ci		err = rocker_port_set_learning(ofdpa_port->rocker_port,
250362306a36Sopenharmony_ci					       !!(ofdpa_port->brport_flags & BR_LEARNING));
250462306a36Sopenharmony_ci
250562306a36Sopenharmony_ci	return err;
250662306a36Sopenharmony_ci}
250762306a36Sopenharmony_ci
250862306a36Sopenharmony_cistatic int
250962306a36Sopenharmony_ciofdpa_port_attr_bridge_flags_support_get(const struct rocker_port *
251062306a36Sopenharmony_ci					 rocker_port,
251162306a36Sopenharmony_ci					 unsigned long *
251262306a36Sopenharmony_ci					 p_brport_flags_support)
251362306a36Sopenharmony_ci{
251462306a36Sopenharmony_ci	*p_brport_flags_support = BR_LEARNING;
251562306a36Sopenharmony_ci	return 0;
251662306a36Sopenharmony_ci}
251762306a36Sopenharmony_ci
251862306a36Sopenharmony_cistatic int
251962306a36Sopenharmony_ciofdpa_port_attr_bridge_ageing_time_set(struct rocker_port *rocker_port,
252062306a36Sopenharmony_ci				       u32 ageing_time)
252162306a36Sopenharmony_ci{
252262306a36Sopenharmony_ci	struct ofdpa_port *ofdpa_port = rocker_port->wpriv;
252362306a36Sopenharmony_ci	struct ofdpa *ofdpa = ofdpa_port->ofdpa;
252462306a36Sopenharmony_ci
252562306a36Sopenharmony_ci	ofdpa_port->ageing_time = clock_t_to_jiffies(ageing_time);
252662306a36Sopenharmony_ci	if (ofdpa_port->ageing_time < ofdpa->ageing_time)
252762306a36Sopenharmony_ci		ofdpa->ageing_time = ofdpa_port->ageing_time;
252862306a36Sopenharmony_ci	mod_timer(&ofdpa_port->ofdpa->fdb_cleanup_timer, jiffies);
252962306a36Sopenharmony_ci
253062306a36Sopenharmony_ci	return 0;
253162306a36Sopenharmony_ci}
253262306a36Sopenharmony_ci
253362306a36Sopenharmony_cistatic int ofdpa_port_obj_vlan_add(struct rocker_port *rocker_port,
253462306a36Sopenharmony_ci				   const struct switchdev_obj_port_vlan *vlan)
253562306a36Sopenharmony_ci{
253662306a36Sopenharmony_ci	struct ofdpa_port *ofdpa_port = rocker_port->wpriv;
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci	return ofdpa_port_vlan_add(ofdpa_port, vlan->vid, vlan->flags);
253962306a36Sopenharmony_ci}
254062306a36Sopenharmony_ci
254162306a36Sopenharmony_cistatic int ofdpa_port_obj_vlan_del(struct rocker_port *rocker_port,
254262306a36Sopenharmony_ci				   const struct switchdev_obj_port_vlan *vlan)
254362306a36Sopenharmony_ci{
254462306a36Sopenharmony_ci	struct ofdpa_port *ofdpa_port = rocker_port->wpriv;
254562306a36Sopenharmony_ci
254662306a36Sopenharmony_ci	return ofdpa_port_vlan_del(ofdpa_port, vlan->vid, vlan->flags);
254762306a36Sopenharmony_ci}
254862306a36Sopenharmony_ci
254962306a36Sopenharmony_cistatic int ofdpa_port_obj_fdb_add(struct rocker_port *rocker_port,
255062306a36Sopenharmony_ci				  u16 vid, const unsigned char *addr)
255162306a36Sopenharmony_ci{
255262306a36Sopenharmony_ci	struct ofdpa_port *ofdpa_port = rocker_port->wpriv;
255362306a36Sopenharmony_ci	__be16 vlan_id = ofdpa_port_vid_to_vlan(ofdpa_port, vid, NULL);
255462306a36Sopenharmony_ci
255562306a36Sopenharmony_ci	if (!ofdpa_port_is_bridged(ofdpa_port))
255662306a36Sopenharmony_ci		return -EINVAL;
255762306a36Sopenharmony_ci
255862306a36Sopenharmony_ci	return ofdpa_port_fdb(ofdpa_port, addr, vlan_id, 0);
255962306a36Sopenharmony_ci}
256062306a36Sopenharmony_ci
256162306a36Sopenharmony_cistatic int ofdpa_port_obj_fdb_del(struct rocker_port *rocker_port,
256262306a36Sopenharmony_ci				  u16 vid, const unsigned char *addr)
256362306a36Sopenharmony_ci{
256462306a36Sopenharmony_ci	struct ofdpa_port *ofdpa_port = rocker_port->wpriv;
256562306a36Sopenharmony_ci	__be16 vlan_id = ofdpa_port_vid_to_vlan(ofdpa_port, vid, NULL);
256662306a36Sopenharmony_ci	int flags = OFDPA_OP_FLAG_REMOVE;
256762306a36Sopenharmony_ci
256862306a36Sopenharmony_ci	if (!ofdpa_port_is_bridged(ofdpa_port))
256962306a36Sopenharmony_ci		return -EINVAL;
257062306a36Sopenharmony_ci
257162306a36Sopenharmony_ci	return ofdpa_port_fdb(ofdpa_port, addr, vlan_id, flags);
257262306a36Sopenharmony_ci}
257362306a36Sopenharmony_ci
257462306a36Sopenharmony_cistatic int ofdpa_port_bridge_join(struct ofdpa_port *ofdpa_port,
257562306a36Sopenharmony_ci				  struct net_device *bridge,
257662306a36Sopenharmony_ci				  struct netlink_ext_ack *extack)
257762306a36Sopenharmony_ci{
257862306a36Sopenharmony_ci	struct net_device *dev = ofdpa_port->dev;
257962306a36Sopenharmony_ci	int err;
258062306a36Sopenharmony_ci
258162306a36Sopenharmony_ci	/* Port is joining bridge, so the internal VLAN for the
258262306a36Sopenharmony_ci	 * port is going to change to the bridge internal VLAN.
258362306a36Sopenharmony_ci	 * Let's remove untagged VLAN (vid=0) from port and
258462306a36Sopenharmony_ci	 * re-add once internal VLAN has changed.
258562306a36Sopenharmony_ci	 */
258662306a36Sopenharmony_ci
258762306a36Sopenharmony_ci	err = ofdpa_port_vlan_del(ofdpa_port, OFDPA_UNTAGGED_VID, 0);
258862306a36Sopenharmony_ci	if (err)
258962306a36Sopenharmony_ci		return err;
259062306a36Sopenharmony_ci
259162306a36Sopenharmony_ci	ofdpa_port_internal_vlan_id_put(ofdpa_port,
259262306a36Sopenharmony_ci					ofdpa_port->dev->ifindex);
259362306a36Sopenharmony_ci	ofdpa_port->internal_vlan_id =
259462306a36Sopenharmony_ci		ofdpa_port_internal_vlan_id_get(ofdpa_port, bridge->ifindex);
259562306a36Sopenharmony_ci
259662306a36Sopenharmony_ci	ofdpa_port->bridge_dev = bridge;
259762306a36Sopenharmony_ci
259862306a36Sopenharmony_ci	err = ofdpa_port_vlan_add(ofdpa_port, OFDPA_UNTAGGED_VID, 0);
259962306a36Sopenharmony_ci	if (err)
260062306a36Sopenharmony_ci		return err;
260162306a36Sopenharmony_ci
260262306a36Sopenharmony_ci	return switchdev_bridge_port_offload(dev, dev, NULL, NULL, NULL,
260362306a36Sopenharmony_ci					     false, extack);
260462306a36Sopenharmony_ci}
260562306a36Sopenharmony_ci
260662306a36Sopenharmony_cistatic int ofdpa_port_bridge_leave(struct ofdpa_port *ofdpa_port)
260762306a36Sopenharmony_ci{
260862306a36Sopenharmony_ci	struct net_device *dev = ofdpa_port->dev;
260962306a36Sopenharmony_ci	int err;
261062306a36Sopenharmony_ci
261162306a36Sopenharmony_ci	switchdev_bridge_port_unoffload(dev, NULL, NULL, NULL);
261262306a36Sopenharmony_ci
261362306a36Sopenharmony_ci	err = ofdpa_port_vlan_del(ofdpa_port, OFDPA_UNTAGGED_VID, 0);
261462306a36Sopenharmony_ci	if (err)
261562306a36Sopenharmony_ci		return err;
261662306a36Sopenharmony_ci
261762306a36Sopenharmony_ci	ofdpa_port_internal_vlan_id_put(ofdpa_port,
261862306a36Sopenharmony_ci					ofdpa_port->bridge_dev->ifindex);
261962306a36Sopenharmony_ci	ofdpa_port->internal_vlan_id =
262062306a36Sopenharmony_ci		ofdpa_port_internal_vlan_id_get(ofdpa_port,
262162306a36Sopenharmony_ci						ofdpa_port->dev->ifindex);
262262306a36Sopenharmony_ci
262362306a36Sopenharmony_ci	ofdpa_port->bridge_dev = NULL;
262462306a36Sopenharmony_ci
262562306a36Sopenharmony_ci	err = ofdpa_port_vlan_add(ofdpa_port, OFDPA_UNTAGGED_VID, 0);
262662306a36Sopenharmony_ci	if (err)
262762306a36Sopenharmony_ci		return err;
262862306a36Sopenharmony_ci
262962306a36Sopenharmony_ci	if (ofdpa_port->dev->flags & IFF_UP)
263062306a36Sopenharmony_ci		err = ofdpa_port_fwd_enable(ofdpa_port, 0);
263162306a36Sopenharmony_ci
263262306a36Sopenharmony_ci	return err;
263362306a36Sopenharmony_ci}
263462306a36Sopenharmony_ci
263562306a36Sopenharmony_cistatic int ofdpa_port_ovs_changed(struct ofdpa_port *ofdpa_port,
263662306a36Sopenharmony_ci				  struct net_device *master)
263762306a36Sopenharmony_ci{
263862306a36Sopenharmony_ci	int err;
263962306a36Sopenharmony_ci
264062306a36Sopenharmony_ci	ofdpa_port->bridge_dev = master;
264162306a36Sopenharmony_ci
264262306a36Sopenharmony_ci	err = ofdpa_port_fwd_disable(ofdpa_port, 0);
264362306a36Sopenharmony_ci	if (err)
264462306a36Sopenharmony_ci		return err;
264562306a36Sopenharmony_ci	err = ofdpa_port_fwd_enable(ofdpa_port, 0);
264662306a36Sopenharmony_ci
264762306a36Sopenharmony_ci	return err;
264862306a36Sopenharmony_ci}
264962306a36Sopenharmony_ci
265062306a36Sopenharmony_cistatic int ofdpa_port_master_linked(struct rocker_port *rocker_port,
265162306a36Sopenharmony_ci				    struct net_device *master,
265262306a36Sopenharmony_ci				    struct netlink_ext_ack *extack)
265362306a36Sopenharmony_ci{
265462306a36Sopenharmony_ci	struct ofdpa_port *ofdpa_port = rocker_port->wpriv;
265562306a36Sopenharmony_ci	int err = 0;
265662306a36Sopenharmony_ci
265762306a36Sopenharmony_ci	if (netif_is_bridge_master(master))
265862306a36Sopenharmony_ci		err = ofdpa_port_bridge_join(ofdpa_port, master, extack);
265962306a36Sopenharmony_ci	else if (netif_is_ovs_master(master))
266062306a36Sopenharmony_ci		err = ofdpa_port_ovs_changed(ofdpa_port, master);
266162306a36Sopenharmony_ci	return err;
266262306a36Sopenharmony_ci}
266362306a36Sopenharmony_ci
266462306a36Sopenharmony_cistatic int ofdpa_port_master_unlinked(struct rocker_port *rocker_port,
266562306a36Sopenharmony_ci				      struct net_device *master)
266662306a36Sopenharmony_ci{
266762306a36Sopenharmony_ci	struct ofdpa_port *ofdpa_port = rocker_port->wpriv;
266862306a36Sopenharmony_ci	int err = 0;
266962306a36Sopenharmony_ci
267062306a36Sopenharmony_ci	if (ofdpa_port_is_bridged(ofdpa_port))
267162306a36Sopenharmony_ci		err = ofdpa_port_bridge_leave(ofdpa_port);
267262306a36Sopenharmony_ci	else if (ofdpa_port_is_ovsed(ofdpa_port))
267362306a36Sopenharmony_ci		err = ofdpa_port_ovs_changed(ofdpa_port, NULL);
267462306a36Sopenharmony_ci	return err;
267562306a36Sopenharmony_ci}
267662306a36Sopenharmony_ci
267762306a36Sopenharmony_cistatic int ofdpa_port_neigh_update(struct rocker_port *rocker_port,
267862306a36Sopenharmony_ci				   struct neighbour *n)
267962306a36Sopenharmony_ci{
268062306a36Sopenharmony_ci	struct ofdpa_port *ofdpa_port = rocker_port->wpriv;
268162306a36Sopenharmony_ci	int flags = (n->nud_state & NUD_VALID ? 0 : OFDPA_OP_FLAG_REMOVE) |
268262306a36Sopenharmony_ci						    OFDPA_OP_FLAG_NOWAIT;
268362306a36Sopenharmony_ci	__be32 ip_addr = *(__be32 *) n->primary_key;
268462306a36Sopenharmony_ci
268562306a36Sopenharmony_ci	return ofdpa_port_ipv4_neigh(ofdpa_port, flags, ip_addr, n->ha);
268662306a36Sopenharmony_ci}
268762306a36Sopenharmony_ci
268862306a36Sopenharmony_cistatic int ofdpa_port_neigh_destroy(struct rocker_port *rocker_port,
268962306a36Sopenharmony_ci				    struct neighbour *n)
269062306a36Sopenharmony_ci{
269162306a36Sopenharmony_ci	struct ofdpa_port *ofdpa_port = rocker_port->wpriv;
269262306a36Sopenharmony_ci	int flags = OFDPA_OP_FLAG_REMOVE | OFDPA_OP_FLAG_NOWAIT;
269362306a36Sopenharmony_ci	__be32 ip_addr = *(__be32 *) n->primary_key;
269462306a36Sopenharmony_ci
269562306a36Sopenharmony_ci	return ofdpa_port_ipv4_neigh(ofdpa_port, flags, ip_addr, n->ha);
269662306a36Sopenharmony_ci}
269762306a36Sopenharmony_ci
269862306a36Sopenharmony_cistatic int ofdpa_port_ev_mac_vlan_seen(struct rocker_port *rocker_port,
269962306a36Sopenharmony_ci				       const unsigned char *addr,
270062306a36Sopenharmony_ci				       __be16 vlan_id)
270162306a36Sopenharmony_ci{
270262306a36Sopenharmony_ci	struct ofdpa_port *ofdpa_port = rocker_port->wpriv;
270362306a36Sopenharmony_ci	int flags = OFDPA_OP_FLAG_NOWAIT | OFDPA_OP_FLAG_LEARNED;
270462306a36Sopenharmony_ci
270562306a36Sopenharmony_ci	if (ofdpa_port->stp_state != BR_STATE_LEARNING &&
270662306a36Sopenharmony_ci	    ofdpa_port->stp_state != BR_STATE_FORWARDING)
270762306a36Sopenharmony_ci		return 0;
270862306a36Sopenharmony_ci
270962306a36Sopenharmony_ci	return ofdpa_port_fdb(ofdpa_port, addr, vlan_id, flags);
271062306a36Sopenharmony_ci}
271162306a36Sopenharmony_ci
271262306a36Sopenharmony_cistatic struct ofdpa_port *ofdpa_port_dev_lower_find(struct net_device *dev,
271362306a36Sopenharmony_ci						    struct rocker *rocker)
271462306a36Sopenharmony_ci{
271562306a36Sopenharmony_ci	struct rocker_port *rocker_port;
271662306a36Sopenharmony_ci
271762306a36Sopenharmony_ci	rocker_port = rocker_port_dev_lower_find(dev, rocker);
271862306a36Sopenharmony_ci	return rocker_port ? rocker_port->wpriv : NULL;
271962306a36Sopenharmony_ci}
272062306a36Sopenharmony_ci
272162306a36Sopenharmony_cistatic int ofdpa_fib4_add(struct rocker *rocker,
272262306a36Sopenharmony_ci			  const struct fib_entry_notifier_info *fen_info)
272362306a36Sopenharmony_ci{
272462306a36Sopenharmony_ci	struct ofdpa *ofdpa = rocker->wpriv;
272562306a36Sopenharmony_ci	struct ofdpa_port *ofdpa_port;
272662306a36Sopenharmony_ci	struct fib_nh *nh;
272762306a36Sopenharmony_ci	int err;
272862306a36Sopenharmony_ci
272962306a36Sopenharmony_ci	if (ofdpa->fib_aborted)
273062306a36Sopenharmony_ci		return 0;
273162306a36Sopenharmony_ci	nh = fib_info_nh(fen_info->fi, 0);
273262306a36Sopenharmony_ci	ofdpa_port = ofdpa_port_dev_lower_find(nh->fib_nh_dev, rocker);
273362306a36Sopenharmony_ci	if (!ofdpa_port)
273462306a36Sopenharmony_ci		return 0;
273562306a36Sopenharmony_ci	err = ofdpa_port_fib_ipv4(ofdpa_port, htonl(fen_info->dst),
273662306a36Sopenharmony_ci				  fen_info->dst_len, fen_info->fi,
273762306a36Sopenharmony_ci				  fen_info->tb_id, 0);
273862306a36Sopenharmony_ci	if (err)
273962306a36Sopenharmony_ci		return err;
274062306a36Sopenharmony_ci	nh->fib_nh_flags |= RTNH_F_OFFLOAD;
274162306a36Sopenharmony_ci	return 0;
274262306a36Sopenharmony_ci}
274362306a36Sopenharmony_ci
274462306a36Sopenharmony_cistatic int ofdpa_fib4_del(struct rocker *rocker,
274562306a36Sopenharmony_ci			  const struct fib_entry_notifier_info *fen_info)
274662306a36Sopenharmony_ci{
274762306a36Sopenharmony_ci	struct ofdpa *ofdpa = rocker->wpriv;
274862306a36Sopenharmony_ci	struct ofdpa_port *ofdpa_port;
274962306a36Sopenharmony_ci	struct fib_nh *nh;
275062306a36Sopenharmony_ci
275162306a36Sopenharmony_ci	if (ofdpa->fib_aborted)
275262306a36Sopenharmony_ci		return 0;
275362306a36Sopenharmony_ci	nh = fib_info_nh(fen_info->fi, 0);
275462306a36Sopenharmony_ci	ofdpa_port = ofdpa_port_dev_lower_find(nh->fib_nh_dev, rocker);
275562306a36Sopenharmony_ci	if (!ofdpa_port)
275662306a36Sopenharmony_ci		return 0;
275762306a36Sopenharmony_ci	nh->fib_nh_flags &= ~RTNH_F_OFFLOAD;
275862306a36Sopenharmony_ci	return ofdpa_port_fib_ipv4(ofdpa_port, htonl(fen_info->dst),
275962306a36Sopenharmony_ci				   fen_info->dst_len, fen_info->fi,
276062306a36Sopenharmony_ci				   fen_info->tb_id, OFDPA_OP_FLAG_REMOVE);
276162306a36Sopenharmony_ci}
276262306a36Sopenharmony_ci
276362306a36Sopenharmony_cistatic void ofdpa_fib4_abort(struct rocker *rocker)
276462306a36Sopenharmony_ci{
276562306a36Sopenharmony_ci	struct ofdpa *ofdpa = rocker->wpriv;
276662306a36Sopenharmony_ci	struct ofdpa_port *ofdpa_port;
276762306a36Sopenharmony_ci	struct ofdpa_flow_tbl_entry *flow_entry;
276862306a36Sopenharmony_ci	struct hlist_node *tmp;
276962306a36Sopenharmony_ci	unsigned long flags;
277062306a36Sopenharmony_ci	int bkt;
277162306a36Sopenharmony_ci
277262306a36Sopenharmony_ci	if (ofdpa->fib_aborted)
277362306a36Sopenharmony_ci		return;
277462306a36Sopenharmony_ci
277562306a36Sopenharmony_ci	spin_lock_irqsave(&ofdpa->flow_tbl_lock, flags);
277662306a36Sopenharmony_ci	hash_for_each_safe(ofdpa->flow_tbl, bkt, tmp, flow_entry, entry) {
277762306a36Sopenharmony_ci		struct fib_nh *nh;
277862306a36Sopenharmony_ci
277962306a36Sopenharmony_ci		if (flow_entry->key.tbl_id !=
278062306a36Sopenharmony_ci		    ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING)
278162306a36Sopenharmony_ci			continue;
278262306a36Sopenharmony_ci		nh = fib_info_nh(flow_entry->fi, 0);
278362306a36Sopenharmony_ci		ofdpa_port = ofdpa_port_dev_lower_find(nh->fib_nh_dev, rocker);
278462306a36Sopenharmony_ci		if (!ofdpa_port)
278562306a36Sopenharmony_ci			continue;
278662306a36Sopenharmony_ci		nh->fib_nh_flags &= ~RTNH_F_OFFLOAD;
278762306a36Sopenharmony_ci		ofdpa_flow_tbl_del(ofdpa_port,
278862306a36Sopenharmony_ci				   OFDPA_OP_FLAG_REMOVE | OFDPA_OP_FLAG_NOWAIT,
278962306a36Sopenharmony_ci				   flow_entry);
279062306a36Sopenharmony_ci	}
279162306a36Sopenharmony_ci	spin_unlock_irqrestore(&ofdpa->flow_tbl_lock, flags);
279262306a36Sopenharmony_ci	ofdpa->fib_aborted = true;
279362306a36Sopenharmony_ci}
279462306a36Sopenharmony_ci
279562306a36Sopenharmony_cistruct rocker_world_ops rocker_ofdpa_ops = {
279662306a36Sopenharmony_ci	.kind = "ofdpa",
279762306a36Sopenharmony_ci	.priv_size = sizeof(struct ofdpa),
279862306a36Sopenharmony_ci	.port_priv_size = sizeof(struct ofdpa_port),
279962306a36Sopenharmony_ci	.mode = ROCKER_PORT_MODE_OF_DPA,
280062306a36Sopenharmony_ci	.init = ofdpa_init,
280162306a36Sopenharmony_ci	.fini = ofdpa_fini,
280262306a36Sopenharmony_ci	.port_pre_init = ofdpa_port_pre_init,
280362306a36Sopenharmony_ci	.port_init = ofdpa_port_init,
280462306a36Sopenharmony_ci	.port_fini = ofdpa_port_fini,
280562306a36Sopenharmony_ci	.port_open = ofdpa_port_open,
280662306a36Sopenharmony_ci	.port_stop = ofdpa_port_stop,
280762306a36Sopenharmony_ci	.port_attr_stp_state_set = ofdpa_port_attr_stp_state_set,
280862306a36Sopenharmony_ci	.port_attr_bridge_flags_set = ofdpa_port_attr_bridge_flags_set,
280962306a36Sopenharmony_ci	.port_attr_bridge_flags_support_get = ofdpa_port_attr_bridge_flags_support_get,
281062306a36Sopenharmony_ci	.port_attr_bridge_ageing_time_set = ofdpa_port_attr_bridge_ageing_time_set,
281162306a36Sopenharmony_ci	.port_obj_vlan_add = ofdpa_port_obj_vlan_add,
281262306a36Sopenharmony_ci	.port_obj_vlan_del = ofdpa_port_obj_vlan_del,
281362306a36Sopenharmony_ci	.port_obj_fdb_add = ofdpa_port_obj_fdb_add,
281462306a36Sopenharmony_ci	.port_obj_fdb_del = ofdpa_port_obj_fdb_del,
281562306a36Sopenharmony_ci	.port_master_linked = ofdpa_port_master_linked,
281662306a36Sopenharmony_ci	.port_master_unlinked = ofdpa_port_master_unlinked,
281762306a36Sopenharmony_ci	.port_neigh_update = ofdpa_port_neigh_update,
281862306a36Sopenharmony_ci	.port_neigh_destroy = ofdpa_port_neigh_destroy,
281962306a36Sopenharmony_ci	.port_ev_mac_vlan_seen = ofdpa_port_ev_mac_vlan_seen,
282062306a36Sopenharmony_ci	.fib4_add = ofdpa_fib4_add,
282162306a36Sopenharmony_ci	.fib4_del = ofdpa_fib4_del,
282262306a36Sopenharmony_ci	.fib4_abort = ofdpa_fib4_abort,
282362306a36Sopenharmony_ci};
2824