18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/if_bridge.h>
58c2ecf20Sopenharmony_ci#include <linux/if_vlan.h>
68c2ecf20Sopenharmony_ci#include <linux/kernel.h>
78c2ecf20Sopenharmony_ci#include <linux/module.h>
88c2ecf20Sopenharmony_ci#include <linux/notifier.h>
98c2ecf20Sopenharmony_ci#include <net/netevent.h>
108c2ecf20Sopenharmony_ci#include <net/switchdev.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "prestera.h"
138c2ecf20Sopenharmony_ci#include "prestera_hw.h"
148c2ecf20Sopenharmony_ci#include "prestera_switchdev.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define PRESTERA_VID_ALL (0xffff)
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define PRESTERA_DEFAULT_AGEING_TIME_MS 300000
198c2ecf20Sopenharmony_ci#define PRESTERA_MAX_AGEING_TIME_MS 1000000000
208c2ecf20Sopenharmony_ci#define PRESTERA_MIN_AGEING_TIME_MS 32000
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistruct prestera_fdb_event_work {
238c2ecf20Sopenharmony_ci	struct work_struct work;
248c2ecf20Sopenharmony_ci	struct switchdev_notifier_fdb_info fdb_info;
258c2ecf20Sopenharmony_ci	struct net_device *dev;
268c2ecf20Sopenharmony_ci	unsigned long event;
278c2ecf20Sopenharmony_ci};
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistruct prestera_switchdev {
308c2ecf20Sopenharmony_ci	struct prestera_switch *sw;
318c2ecf20Sopenharmony_ci	struct list_head bridge_list;
328c2ecf20Sopenharmony_ci	bool bridge_8021q_exists;
338c2ecf20Sopenharmony_ci	struct notifier_block swdev_nb_blk;
348c2ecf20Sopenharmony_ci	struct notifier_block swdev_nb;
358c2ecf20Sopenharmony_ci};
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistruct prestera_bridge {
388c2ecf20Sopenharmony_ci	struct list_head head;
398c2ecf20Sopenharmony_ci	struct net_device *dev;
408c2ecf20Sopenharmony_ci	struct prestera_switchdev *swdev;
418c2ecf20Sopenharmony_ci	struct list_head port_list;
428c2ecf20Sopenharmony_ci	bool vlan_enabled;
438c2ecf20Sopenharmony_ci	u16 bridge_id;
448c2ecf20Sopenharmony_ci};
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistruct prestera_bridge_port {
478c2ecf20Sopenharmony_ci	struct list_head head;
488c2ecf20Sopenharmony_ci	struct net_device *dev;
498c2ecf20Sopenharmony_ci	struct prestera_bridge *bridge;
508c2ecf20Sopenharmony_ci	struct list_head vlan_list;
518c2ecf20Sopenharmony_ci	refcount_t ref_count;
528c2ecf20Sopenharmony_ci	unsigned long flags;
538c2ecf20Sopenharmony_ci	u8 stp_state;
548c2ecf20Sopenharmony_ci};
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistruct prestera_bridge_vlan {
578c2ecf20Sopenharmony_ci	struct list_head head;
588c2ecf20Sopenharmony_ci	struct list_head port_vlan_list;
598c2ecf20Sopenharmony_ci	u16 vid;
608c2ecf20Sopenharmony_ci};
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistruct prestera_port_vlan {
638c2ecf20Sopenharmony_ci	struct list_head br_vlan_head;
648c2ecf20Sopenharmony_ci	struct list_head port_head;
658c2ecf20Sopenharmony_ci	struct prestera_port *port;
668c2ecf20Sopenharmony_ci	struct prestera_bridge_port *br_port;
678c2ecf20Sopenharmony_ci	u16 vid;
688c2ecf20Sopenharmony_ci};
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic struct workqueue_struct *swdev_wq;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic void prestera_bridge_port_put(struct prestera_bridge_port *br_port);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic int prestera_port_vid_stp_set(struct prestera_port *port, u16 vid,
758c2ecf20Sopenharmony_ci				     u8 state);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic struct prestera_bridge_vlan *
788c2ecf20Sopenharmony_ciprestera_bridge_vlan_create(struct prestera_bridge_port *br_port, u16 vid)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	struct prestera_bridge_vlan *br_vlan;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	br_vlan = kzalloc(sizeof(*br_vlan), GFP_KERNEL);
838c2ecf20Sopenharmony_ci	if (!br_vlan)
848c2ecf20Sopenharmony_ci		return NULL;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&br_vlan->port_vlan_list);
878c2ecf20Sopenharmony_ci	br_vlan->vid = vid;
888c2ecf20Sopenharmony_ci	list_add(&br_vlan->head, &br_port->vlan_list);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	return br_vlan;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic void prestera_bridge_vlan_destroy(struct prestera_bridge_vlan *br_vlan)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	list_del(&br_vlan->head);
968c2ecf20Sopenharmony_ci	WARN_ON(!list_empty(&br_vlan->port_vlan_list));
978c2ecf20Sopenharmony_ci	kfree(br_vlan);
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic struct prestera_bridge_vlan *
1018c2ecf20Sopenharmony_ciprestera_bridge_vlan_by_vid(struct prestera_bridge_port *br_port, u16 vid)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	struct prestera_bridge_vlan *br_vlan;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	list_for_each_entry(br_vlan, &br_port->vlan_list, head) {
1068c2ecf20Sopenharmony_ci		if (br_vlan->vid == vid)
1078c2ecf20Sopenharmony_ci			return br_vlan;
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	return NULL;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic int prestera_bridge_vlan_port_count(struct prestera_bridge *bridge,
1148c2ecf20Sopenharmony_ci					   u16 vid)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	struct prestera_bridge_port *br_port;
1178c2ecf20Sopenharmony_ci	struct prestera_bridge_vlan *br_vlan;
1188c2ecf20Sopenharmony_ci	int count = 0;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	list_for_each_entry(br_port, &bridge->port_list, head) {
1218c2ecf20Sopenharmony_ci		list_for_each_entry(br_vlan, &br_port->vlan_list, head) {
1228c2ecf20Sopenharmony_ci			if (br_vlan->vid == vid) {
1238c2ecf20Sopenharmony_ci				count += 1;
1248c2ecf20Sopenharmony_ci				break;
1258c2ecf20Sopenharmony_ci			}
1268c2ecf20Sopenharmony_ci		}
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	return count;
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic void prestera_bridge_vlan_put(struct prestera_bridge_vlan *br_vlan)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	if (list_empty(&br_vlan->port_vlan_list))
1358c2ecf20Sopenharmony_ci		prestera_bridge_vlan_destroy(br_vlan);
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic struct prestera_port_vlan *
1398c2ecf20Sopenharmony_ciprestera_port_vlan_by_vid(struct prestera_port *port, u16 vid)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	struct prestera_port_vlan *port_vlan;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	list_for_each_entry(port_vlan, &port->vlans_list, port_head) {
1448c2ecf20Sopenharmony_ci		if (port_vlan->vid == vid)
1458c2ecf20Sopenharmony_ci			return port_vlan;
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	return NULL;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic struct prestera_port_vlan *
1528c2ecf20Sopenharmony_ciprestera_port_vlan_create(struct prestera_port *port, u16 vid, bool untagged)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	struct prestera_port_vlan *port_vlan;
1558c2ecf20Sopenharmony_ci	int err;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	port_vlan = prestera_port_vlan_by_vid(port, vid);
1588c2ecf20Sopenharmony_ci	if (port_vlan)
1598c2ecf20Sopenharmony_ci		return ERR_PTR(-EEXIST);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	err = prestera_hw_vlan_port_set(port, vid, true, untagged);
1628c2ecf20Sopenharmony_ci	if (err)
1638c2ecf20Sopenharmony_ci		return ERR_PTR(err);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	port_vlan = kzalloc(sizeof(*port_vlan), GFP_KERNEL);
1668c2ecf20Sopenharmony_ci	if (!port_vlan) {
1678c2ecf20Sopenharmony_ci		err = -ENOMEM;
1688c2ecf20Sopenharmony_ci		goto err_port_vlan_alloc;
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	port_vlan->port = port;
1728c2ecf20Sopenharmony_ci	port_vlan->vid = vid;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	list_add(&port_vlan->port_head, &port->vlans_list);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	return port_vlan;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cierr_port_vlan_alloc:
1798c2ecf20Sopenharmony_ci	prestera_hw_vlan_port_set(port, vid, false, false);
1808c2ecf20Sopenharmony_ci	return ERR_PTR(err);
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic void
1848c2ecf20Sopenharmony_ciprestera_port_vlan_bridge_leave(struct prestera_port_vlan *port_vlan)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	u32 fdb_flush_mode = PRESTERA_FDB_FLUSH_MODE_DYNAMIC;
1878c2ecf20Sopenharmony_ci	struct prestera_port *port = port_vlan->port;
1888c2ecf20Sopenharmony_ci	struct prestera_bridge_vlan *br_vlan;
1898c2ecf20Sopenharmony_ci	struct prestera_bridge_port *br_port;
1908c2ecf20Sopenharmony_ci	bool last_port, last_vlan;
1918c2ecf20Sopenharmony_ci	u16 vid = port_vlan->vid;
1928c2ecf20Sopenharmony_ci	int port_count;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	br_port = port_vlan->br_port;
1958c2ecf20Sopenharmony_ci	port_count = prestera_bridge_vlan_port_count(br_port->bridge, vid);
1968c2ecf20Sopenharmony_ci	br_vlan = prestera_bridge_vlan_by_vid(br_port, vid);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	last_vlan = list_is_singular(&br_port->vlan_list);
1998c2ecf20Sopenharmony_ci	last_port = port_count == 1;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	if (last_vlan)
2028c2ecf20Sopenharmony_ci		prestera_hw_fdb_flush_port(port, fdb_flush_mode);
2038c2ecf20Sopenharmony_ci	else if (last_port)
2048c2ecf20Sopenharmony_ci		prestera_hw_fdb_flush_vlan(port->sw, vid, fdb_flush_mode);
2058c2ecf20Sopenharmony_ci	else
2068c2ecf20Sopenharmony_ci		prestera_hw_fdb_flush_port_vlan(port, vid, fdb_flush_mode);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	list_del(&port_vlan->br_vlan_head);
2098c2ecf20Sopenharmony_ci	prestera_bridge_vlan_put(br_vlan);
2108c2ecf20Sopenharmony_ci	prestera_bridge_port_put(br_port);
2118c2ecf20Sopenharmony_ci	port_vlan->br_port = NULL;
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic void prestera_port_vlan_destroy(struct prestera_port_vlan *port_vlan)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	struct prestera_port *port = port_vlan->port;
2178c2ecf20Sopenharmony_ci	u16 vid = port_vlan->vid;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if (port_vlan->br_port)
2208c2ecf20Sopenharmony_ci		prestera_port_vlan_bridge_leave(port_vlan);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	prestera_hw_vlan_port_set(port, vid, false, false);
2238c2ecf20Sopenharmony_ci	list_del(&port_vlan->port_head);
2248c2ecf20Sopenharmony_ci	kfree(port_vlan);
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic struct prestera_bridge *
2288c2ecf20Sopenharmony_ciprestera_bridge_create(struct prestera_switchdev *swdev, struct net_device *dev)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	bool vlan_enabled = br_vlan_enabled(dev);
2318c2ecf20Sopenharmony_ci	struct prestera_bridge *bridge;
2328c2ecf20Sopenharmony_ci	u16 bridge_id;
2338c2ecf20Sopenharmony_ci	int err;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	if (vlan_enabled && swdev->bridge_8021q_exists) {
2368c2ecf20Sopenharmony_ci		netdev_err(dev, "Only one VLAN-aware bridge is supported\n");
2378c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
2418c2ecf20Sopenharmony_ci	if (!bridge)
2428c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	if (vlan_enabled) {
2458c2ecf20Sopenharmony_ci		swdev->bridge_8021q_exists = true;
2468c2ecf20Sopenharmony_ci	} else {
2478c2ecf20Sopenharmony_ci		err = prestera_hw_bridge_create(swdev->sw, &bridge_id);
2488c2ecf20Sopenharmony_ci		if (err) {
2498c2ecf20Sopenharmony_ci			kfree(bridge);
2508c2ecf20Sopenharmony_ci			return ERR_PTR(err);
2518c2ecf20Sopenharmony_ci		}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci		bridge->bridge_id = bridge_id;
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	bridge->vlan_enabled = vlan_enabled;
2578c2ecf20Sopenharmony_ci	bridge->swdev = swdev;
2588c2ecf20Sopenharmony_ci	bridge->dev = dev;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&bridge->port_list);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	list_add(&bridge->head, &swdev->bridge_list);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	return bridge;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic void prestera_bridge_destroy(struct prestera_bridge *bridge)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	struct prestera_switchdev *swdev = bridge->swdev;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	list_del(&bridge->head);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	if (bridge->vlan_enabled)
2748c2ecf20Sopenharmony_ci		swdev->bridge_8021q_exists = false;
2758c2ecf20Sopenharmony_ci	else
2768c2ecf20Sopenharmony_ci		prestera_hw_bridge_delete(swdev->sw, bridge->bridge_id);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	WARN_ON(!list_empty(&bridge->port_list));
2798c2ecf20Sopenharmony_ci	kfree(bridge);
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic void prestera_bridge_put(struct prestera_bridge *bridge)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	if (list_empty(&bridge->port_list))
2858c2ecf20Sopenharmony_ci		prestera_bridge_destroy(bridge);
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistatic
2898c2ecf20Sopenharmony_cistruct prestera_bridge *prestera_bridge_by_dev(struct prestera_switchdev *swdev,
2908c2ecf20Sopenharmony_ci					       const struct net_device *dev)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	struct prestera_bridge *bridge;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	list_for_each_entry(bridge, &swdev->bridge_list, head)
2958c2ecf20Sopenharmony_ci		if (bridge->dev == dev)
2968c2ecf20Sopenharmony_ci			return bridge;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	return NULL;
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic struct prestera_bridge_port *
3028c2ecf20Sopenharmony_ci__prestera_bridge_port_by_dev(struct prestera_bridge *bridge,
3038c2ecf20Sopenharmony_ci			      struct net_device *dev)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	struct prestera_bridge_port *br_port;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	list_for_each_entry(br_port, &bridge->port_list, head) {
3088c2ecf20Sopenharmony_ci		if (br_port->dev == dev)
3098c2ecf20Sopenharmony_ci			return br_port;
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	return NULL;
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cistatic struct prestera_bridge_port *
3168c2ecf20Sopenharmony_ciprestera_bridge_port_by_dev(struct prestera_switchdev *swdev,
3178c2ecf20Sopenharmony_ci			    struct net_device *dev)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	struct net_device *br_dev = netdev_master_upper_dev_get(dev);
3208c2ecf20Sopenharmony_ci	struct prestera_bridge *bridge;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	if (!br_dev)
3238c2ecf20Sopenharmony_ci		return NULL;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	bridge = prestera_bridge_by_dev(swdev, br_dev);
3268c2ecf20Sopenharmony_ci	if (!bridge)
3278c2ecf20Sopenharmony_ci		return NULL;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	return __prestera_bridge_port_by_dev(bridge, dev);
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic struct prestera_bridge_port *
3338c2ecf20Sopenharmony_ciprestera_bridge_port_create(struct prestera_bridge *bridge,
3348c2ecf20Sopenharmony_ci			    struct net_device *dev)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	struct prestera_bridge_port *br_port;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	br_port = kzalloc(sizeof(*br_port), GFP_KERNEL);
3398c2ecf20Sopenharmony_ci	if (!br_port)
3408c2ecf20Sopenharmony_ci		return NULL;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	br_port->flags = BR_LEARNING | BR_FLOOD | BR_LEARNING_SYNC |
3438c2ecf20Sopenharmony_ci				BR_MCAST_FLOOD;
3448c2ecf20Sopenharmony_ci	br_port->stp_state = BR_STATE_DISABLED;
3458c2ecf20Sopenharmony_ci	refcount_set(&br_port->ref_count, 1);
3468c2ecf20Sopenharmony_ci	br_port->bridge = bridge;
3478c2ecf20Sopenharmony_ci	br_port->dev = dev;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&br_port->vlan_list);
3508c2ecf20Sopenharmony_ci	list_add(&br_port->head, &bridge->port_list);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	return br_port;
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic void
3568c2ecf20Sopenharmony_ciprestera_bridge_port_destroy(struct prestera_bridge_port *br_port)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	list_del(&br_port->head);
3598c2ecf20Sopenharmony_ci	WARN_ON(!list_empty(&br_port->vlan_list));
3608c2ecf20Sopenharmony_ci	kfree(br_port);
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cistatic void prestera_bridge_port_get(struct prestera_bridge_port *br_port)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	refcount_inc(&br_port->ref_count);
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic void prestera_bridge_port_put(struct prestera_bridge_port *br_port)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	struct prestera_bridge *bridge = br_port->bridge;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (refcount_dec_and_test(&br_port->ref_count)) {
3738c2ecf20Sopenharmony_ci		prestera_bridge_port_destroy(br_port);
3748c2ecf20Sopenharmony_ci		prestera_bridge_put(bridge);
3758c2ecf20Sopenharmony_ci	}
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_cistatic struct prestera_bridge_port *
3798c2ecf20Sopenharmony_ciprestera_bridge_port_add(struct prestera_bridge *bridge, struct net_device *dev)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	struct prestera_bridge_port *br_port;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	br_port = __prestera_bridge_port_by_dev(bridge, dev);
3848c2ecf20Sopenharmony_ci	if (br_port) {
3858c2ecf20Sopenharmony_ci		prestera_bridge_port_get(br_port);
3868c2ecf20Sopenharmony_ci		return br_port;
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	br_port = prestera_bridge_port_create(bridge, dev);
3908c2ecf20Sopenharmony_ci	if (!br_port)
3918c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	return br_port;
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistatic int
3978c2ecf20Sopenharmony_ciprestera_bridge_1d_port_join(struct prestera_bridge_port *br_port)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	struct prestera_port *port = netdev_priv(br_port->dev);
4008c2ecf20Sopenharmony_ci	struct prestera_bridge *bridge = br_port->bridge;
4018c2ecf20Sopenharmony_ci	int err;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	err = prestera_hw_bridge_port_add(port, bridge->bridge_id);
4048c2ecf20Sopenharmony_ci	if (err)
4058c2ecf20Sopenharmony_ci		return err;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	err = prestera_hw_port_flood_set(port, br_port->flags & BR_FLOOD);
4088c2ecf20Sopenharmony_ci	if (err)
4098c2ecf20Sopenharmony_ci		goto err_port_flood_set;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	err = prestera_hw_port_learning_set(port, br_port->flags & BR_LEARNING);
4128c2ecf20Sopenharmony_ci	if (err)
4138c2ecf20Sopenharmony_ci		goto err_port_learning_set;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	return 0;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cierr_port_learning_set:
4188c2ecf20Sopenharmony_ci	prestera_hw_port_flood_set(port, false);
4198c2ecf20Sopenharmony_cierr_port_flood_set:
4208c2ecf20Sopenharmony_ci	prestera_hw_bridge_port_delete(port, bridge->bridge_id);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	return err;
4238c2ecf20Sopenharmony_ci}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cistatic int prestera_port_bridge_join(struct prestera_port *port,
4268c2ecf20Sopenharmony_ci				     struct net_device *upper)
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	struct prestera_switchdev *swdev = port->sw->swdev;
4298c2ecf20Sopenharmony_ci	struct prestera_bridge_port *br_port;
4308c2ecf20Sopenharmony_ci	struct prestera_bridge *bridge;
4318c2ecf20Sopenharmony_ci	int err;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	bridge = prestera_bridge_by_dev(swdev, upper);
4348c2ecf20Sopenharmony_ci	if (!bridge) {
4358c2ecf20Sopenharmony_ci		bridge = prestera_bridge_create(swdev, upper);
4368c2ecf20Sopenharmony_ci		if (IS_ERR(bridge))
4378c2ecf20Sopenharmony_ci			return PTR_ERR(bridge);
4388c2ecf20Sopenharmony_ci	}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	br_port = prestera_bridge_port_add(bridge, port->dev);
4418c2ecf20Sopenharmony_ci	if (IS_ERR(br_port)) {
4428c2ecf20Sopenharmony_ci		prestera_bridge_put(bridge);
4438c2ecf20Sopenharmony_ci		return PTR_ERR(br_port);
4448c2ecf20Sopenharmony_ci	}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	if (bridge->vlan_enabled)
4478c2ecf20Sopenharmony_ci		return 0;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	err = prestera_bridge_1d_port_join(br_port);
4508c2ecf20Sopenharmony_ci	if (err)
4518c2ecf20Sopenharmony_ci		goto err_port_join;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	return 0;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_cierr_port_join:
4568c2ecf20Sopenharmony_ci	prestera_bridge_port_put(br_port);
4578c2ecf20Sopenharmony_ci	return err;
4588c2ecf20Sopenharmony_ci}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_cistatic void prestera_bridge_1q_port_leave(struct prestera_bridge_port *br_port)
4618c2ecf20Sopenharmony_ci{
4628c2ecf20Sopenharmony_ci	struct prestera_port *port = netdev_priv(br_port->dev);
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	prestera_hw_fdb_flush_port(port, PRESTERA_FDB_FLUSH_MODE_ALL);
4658c2ecf20Sopenharmony_ci	prestera_port_pvid_set(port, PRESTERA_DEFAULT_VID);
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cistatic void prestera_bridge_1d_port_leave(struct prestera_bridge_port *br_port)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	struct prestera_port *port = netdev_priv(br_port->dev);
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	prestera_hw_fdb_flush_port(port, PRESTERA_FDB_FLUSH_MODE_ALL);
4738c2ecf20Sopenharmony_ci	prestera_hw_bridge_port_delete(port, br_port->bridge->bridge_id);
4748c2ecf20Sopenharmony_ci}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_cistatic int prestera_port_vid_stp_set(struct prestera_port *port, u16 vid,
4778c2ecf20Sopenharmony_ci				     u8 state)
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	u8 hw_state = state;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	switch (state) {
4828c2ecf20Sopenharmony_ci	case BR_STATE_DISABLED:
4838c2ecf20Sopenharmony_ci		hw_state = PRESTERA_STP_DISABLED;
4848c2ecf20Sopenharmony_ci		break;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	case BR_STATE_BLOCKING:
4878c2ecf20Sopenharmony_ci	case BR_STATE_LISTENING:
4888c2ecf20Sopenharmony_ci		hw_state = PRESTERA_STP_BLOCK_LISTEN;
4898c2ecf20Sopenharmony_ci		break;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	case BR_STATE_LEARNING:
4928c2ecf20Sopenharmony_ci		hw_state = PRESTERA_STP_LEARN;
4938c2ecf20Sopenharmony_ci		break;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	case BR_STATE_FORWARDING:
4968c2ecf20Sopenharmony_ci		hw_state = PRESTERA_STP_FORWARD;
4978c2ecf20Sopenharmony_ci		break;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	default:
5008c2ecf20Sopenharmony_ci		return -EINVAL;
5018c2ecf20Sopenharmony_ci	}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	return prestera_hw_vlan_port_stp_set(port, vid, hw_state);
5048c2ecf20Sopenharmony_ci}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_cistatic void prestera_port_bridge_leave(struct prestera_port *port,
5078c2ecf20Sopenharmony_ci				       struct net_device *upper)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	struct prestera_switchdev *swdev = port->sw->swdev;
5108c2ecf20Sopenharmony_ci	struct prestera_bridge_port *br_port;
5118c2ecf20Sopenharmony_ci	struct prestera_bridge *bridge;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	bridge = prestera_bridge_by_dev(swdev, upper);
5148c2ecf20Sopenharmony_ci	if (!bridge)
5158c2ecf20Sopenharmony_ci		return;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	br_port = __prestera_bridge_port_by_dev(bridge, port->dev);
5188c2ecf20Sopenharmony_ci	if (!br_port)
5198c2ecf20Sopenharmony_ci		return;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	bridge = br_port->bridge;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	if (bridge->vlan_enabled)
5248c2ecf20Sopenharmony_ci		prestera_bridge_1q_port_leave(br_port);
5258c2ecf20Sopenharmony_ci	else
5268c2ecf20Sopenharmony_ci		prestera_bridge_1d_port_leave(br_port);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	prestera_hw_port_learning_set(port, false);
5298c2ecf20Sopenharmony_ci	prestera_hw_port_flood_set(port, false);
5308c2ecf20Sopenharmony_ci	prestera_port_vid_stp_set(port, PRESTERA_VID_ALL, BR_STATE_FORWARDING);
5318c2ecf20Sopenharmony_ci	prestera_bridge_port_put(br_port);
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ciint prestera_bridge_port_event(struct net_device *dev, unsigned long event,
5358c2ecf20Sopenharmony_ci			       void *ptr)
5368c2ecf20Sopenharmony_ci{
5378c2ecf20Sopenharmony_ci	struct netdev_notifier_changeupper_info *info = ptr;
5388c2ecf20Sopenharmony_ci	struct netlink_ext_ack *extack;
5398c2ecf20Sopenharmony_ci	struct prestera_port *port;
5408c2ecf20Sopenharmony_ci	struct net_device *upper;
5418c2ecf20Sopenharmony_ci	int err;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	extack = netdev_notifier_info_to_extack(&info->info);
5448c2ecf20Sopenharmony_ci	port = netdev_priv(dev);
5458c2ecf20Sopenharmony_ci	upper = info->upper_dev;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	switch (event) {
5488c2ecf20Sopenharmony_ci	case NETDEV_PRECHANGEUPPER:
5498c2ecf20Sopenharmony_ci		if (!netif_is_bridge_master(upper)) {
5508c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type");
5518c2ecf20Sopenharmony_ci			return -EINVAL;
5528c2ecf20Sopenharmony_ci		}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci		if (!info->linking)
5558c2ecf20Sopenharmony_ci			break;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci		if (netdev_has_any_upper_dev(upper)) {
5588c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Upper device is already enslaved");
5598c2ecf20Sopenharmony_ci			return -EINVAL;
5608c2ecf20Sopenharmony_ci		}
5618c2ecf20Sopenharmony_ci		break;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	case NETDEV_CHANGEUPPER:
5648c2ecf20Sopenharmony_ci		if (!netif_is_bridge_master(upper))
5658c2ecf20Sopenharmony_ci			break;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci		if (info->linking) {
5688c2ecf20Sopenharmony_ci			err = prestera_port_bridge_join(port, upper);
5698c2ecf20Sopenharmony_ci			if (err)
5708c2ecf20Sopenharmony_ci				return err;
5718c2ecf20Sopenharmony_ci		} else {
5728c2ecf20Sopenharmony_ci			prestera_port_bridge_leave(port, upper);
5738c2ecf20Sopenharmony_ci		}
5748c2ecf20Sopenharmony_ci		break;
5758c2ecf20Sopenharmony_ci	}
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	return 0;
5788c2ecf20Sopenharmony_ci}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_cistatic int prestera_port_attr_br_flags_set(struct prestera_port *port,
5818c2ecf20Sopenharmony_ci					   struct switchdev_trans *trans,
5828c2ecf20Sopenharmony_ci					   struct net_device *dev,
5838c2ecf20Sopenharmony_ci					   unsigned long flags)
5848c2ecf20Sopenharmony_ci{
5858c2ecf20Sopenharmony_ci	struct prestera_bridge_port *br_port;
5868c2ecf20Sopenharmony_ci	int err;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	if (switchdev_trans_ph_prepare(trans))
5898c2ecf20Sopenharmony_ci		return 0;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	br_port = prestera_bridge_port_by_dev(port->sw->swdev, dev);
5928c2ecf20Sopenharmony_ci	if (!br_port)
5938c2ecf20Sopenharmony_ci		return 0;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	err = prestera_hw_port_flood_set(port, flags & BR_FLOOD);
5968c2ecf20Sopenharmony_ci	if (err)
5978c2ecf20Sopenharmony_ci		return err;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	err = prestera_hw_port_learning_set(port, flags & BR_LEARNING);
6008c2ecf20Sopenharmony_ci	if (err)
6018c2ecf20Sopenharmony_ci		return err;
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	memcpy(&br_port->flags, &flags, sizeof(flags));
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	return 0;
6068c2ecf20Sopenharmony_ci}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_cistatic int prestera_port_attr_br_ageing_set(struct prestera_port *port,
6098c2ecf20Sopenharmony_ci					    struct switchdev_trans *trans,
6108c2ecf20Sopenharmony_ci					    unsigned long ageing_clock_t)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t);
6138c2ecf20Sopenharmony_ci	u32 ageing_time_ms = jiffies_to_msecs(ageing_jiffies);
6148c2ecf20Sopenharmony_ci	struct prestera_switch *sw = port->sw;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	if (switchdev_trans_ph_prepare(trans)) {
6178c2ecf20Sopenharmony_ci		if (ageing_time_ms < PRESTERA_MIN_AGEING_TIME_MS ||
6188c2ecf20Sopenharmony_ci		    ageing_time_ms > PRESTERA_MAX_AGEING_TIME_MS)
6198c2ecf20Sopenharmony_ci			return -ERANGE;
6208c2ecf20Sopenharmony_ci		else
6218c2ecf20Sopenharmony_ci			return 0;
6228c2ecf20Sopenharmony_ci	}
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	return prestera_hw_switch_ageing_set(sw, ageing_time_ms);
6258c2ecf20Sopenharmony_ci}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_cistatic int prestera_port_attr_br_vlan_set(struct prestera_port *port,
6288c2ecf20Sopenharmony_ci					  struct switchdev_trans *trans,
6298c2ecf20Sopenharmony_ci					  struct net_device *dev,
6308c2ecf20Sopenharmony_ci					  bool vlan_enabled)
6318c2ecf20Sopenharmony_ci{
6328c2ecf20Sopenharmony_ci	struct prestera_switch *sw = port->sw;
6338c2ecf20Sopenharmony_ci	struct prestera_bridge *bridge;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	if (!switchdev_trans_ph_prepare(trans))
6368c2ecf20Sopenharmony_ci		return 0;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	bridge = prestera_bridge_by_dev(sw->swdev, dev);
6398c2ecf20Sopenharmony_ci	if (WARN_ON(!bridge))
6408c2ecf20Sopenharmony_ci		return -EINVAL;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	if (bridge->vlan_enabled == vlan_enabled)
6438c2ecf20Sopenharmony_ci		return 0;
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	netdev_err(bridge->dev, "VLAN filtering can't be changed for existing bridge\n");
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	return -EINVAL;
6488c2ecf20Sopenharmony_ci}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_cistatic int prestera_port_bridge_vlan_stp_set(struct prestera_port *port,
6518c2ecf20Sopenharmony_ci					     struct prestera_bridge_vlan *br_vlan,
6528c2ecf20Sopenharmony_ci					     u8 state)
6538c2ecf20Sopenharmony_ci{
6548c2ecf20Sopenharmony_ci	struct prestera_port_vlan *port_vlan;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	list_for_each_entry(port_vlan, &br_vlan->port_vlan_list, br_vlan_head) {
6578c2ecf20Sopenharmony_ci		if (port_vlan->port != port)
6588c2ecf20Sopenharmony_ci			continue;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci		return prestera_port_vid_stp_set(port, br_vlan->vid, state);
6618c2ecf20Sopenharmony_ci	}
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	return 0;
6648c2ecf20Sopenharmony_ci}
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_cistatic int presterar_port_attr_stp_state_set(struct prestera_port *port,
6678c2ecf20Sopenharmony_ci					     struct switchdev_trans *trans,
6688c2ecf20Sopenharmony_ci					     struct net_device *dev,
6698c2ecf20Sopenharmony_ci					     u8 state)
6708c2ecf20Sopenharmony_ci{
6718c2ecf20Sopenharmony_ci	struct prestera_bridge_port *br_port;
6728c2ecf20Sopenharmony_ci	struct prestera_bridge_vlan *br_vlan;
6738c2ecf20Sopenharmony_ci	int err;
6748c2ecf20Sopenharmony_ci	u16 vid;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	if (switchdev_trans_ph_prepare(trans))
6778c2ecf20Sopenharmony_ci		return 0;
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	br_port = prestera_bridge_port_by_dev(port->sw->swdev, dev);
6808c2ecf20Sopenharmony_ci	if (!br_port)
6818c2ecf20Sopenharmony_ci		return 0;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	if (!br_port->bridge->vlan_enabled) {
6848c2ecf20Sopenharmony_ci		vid = br_port->bridge->bridge_id;
6858c2ecf20Sopenharmony_ci		err = prestera_port_vid_stp_set(port, vid, state);
6868c2ecf20Sopenharmony_ci		if (err)
6878c2ecf20Sopenharmony_ci			goto err_port_stp_set;
6888c2ecf20Sopenharmony_ci	} else {
6898c2ecf20Sopenharmony_ci		list_for_each_entry(br_vlan, &br_port->vlan_list, head) {
6908c2ecf20Sopenharmony_ci			err = prestera_port_bridge_vlan_stp_set(port, br_vlan,
6918c2ecf20Sopenharmony_ci								state);
6928c2ecf20Sopenharmony_ci			if (err)
6938c2ecf20Sopenharmony_ci				goto err_port_vlan_stp_set;
6948c2ecf20Sopenharmony_ci		}
6958c2ecf20Sopenharmony_ci	}
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	br_port->stp_state = state;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	return 0;
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_cierr_port_vlan_stp_set:
7028c2ecf20Sopenharmony_ci	list_for_each_entry_continue_reverse(br_vlan, &br_port->vlan_list, head)
7038c2ecf20Sopenharmony_ci		prestera_port_bridge_vlan_stp_set(port, br_vlan, br_port->stp_state);
7048c2ecf20Sopenharmony_ci	return err;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_cierr_port_stp_set:
7078c2ecf20Sopenharmony_ci	prestera_port_vid_stp_set(port, vid, br_port->stp_state);
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	return err;
7108c2ecf20Sopenharmony_ci}
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_cistatic int prestera_port_obj_attr_set(struct net_device *dev,
7138c2ecf20Sopenharmony_ci				      const struct switchdev_attr *attr,
7148c2ecf20Sopenharmony_ci				      struct switchdev_trans *trans)
7158c2ecf20Sopenharmony_ci{
7168c2ecf20Sopenharmony_ci	struct prestera_port *port = netdev_priv(dev);
7178c2ecf20Sopenharmony_ci	int err = 0;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	switch (attr->id) {
7208c2ecf20Sopenharmony_ci	case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
7218c2ecf20Sopenharmony_ci		err = presterar_port_attr_stp_state_set(port, trans,
7228c2ecf20Sopenharmony_ci							attr->orig_dev,
7238c2ecf20Sopenharmony_ci							attr->u.stp_state);
7248c2ecf20Sopenharmony_ci		break;
7258c2ecf20Sopenharmony_ci	case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
7268c2ecf20Sopenharmony_ci		if (attr->u.brport_flags &
7278c2ecf20Sopenharmony_ci		    ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD))
7288c2ecf20Sopenharmony_ci			err = -EINVAL;
7298c2ecf20Sopenharmony_ci		break;
7308c2ecf20Sopenharmony_ci	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
7318c2ecf20Sopenharmony_ci		err = prestera_port_attr_br_flags_set(port, trans,
7328c2ecf20Sopenharmony_ci						      attr->orig_dev,
7338c2ecf20Sopenharmony_ci						      attr->u.brport_flags);
7348c2ecf20Sopenharmony_ci		break;
7358c2ecf20Sopenharmony_ci	case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
7368c2ecf20Sopenharmony_ci		err = prestera_port_attr_br_ageing_set(port, trans,
7378c2ecf20Sopenharmony_ci						       attr->u.ageing_time);
7388c2ecf20Sopenharmony_ci		break;
7398c2ecf20Sopenharmony_ci	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
7408c2ecf20Sopenharmony_ci		err = prestera_port_attr_br_vlan_set(port, trans,
7418c2ecf20Sopenharmony_ci						     attr->orig_dev,
7428c2ecf20Sopenharmony_ci						     attr->u.vlan_filtering);
7438c2ecf20Sopenharmony_ci		break;
7448c2ecf20Sopenharmony_ci	default:
7458c2ecf20Sopenharmony_ci		err = -EOPNOTSUPP;
7468c2ecf20Sopenharmony_ci	}
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	return err;
7498c2ecf20Sopenharmony_ci}
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_cistatic void
7528c2ecf20Sopenharmony_ciprestera_fdb_offload_notify(struct prestera_port *port,
7538c2ecf20Sopenharmony_ci			    struct switchdev_notifier_fdb_info *info)
7548c2ecf20Sopenharmony_ci{
7558c2ecf20Sopenharmony_ci	struct switchdev_notifier_fdb_info send_info;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	send_info.addr = info->addr;
7588c2ecf20Sopenharmony_ci	send_info.vid = info->vid;
7598c2ecf20Sopenharmony_ci	send_info.offloaded = true;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, port->dev,
7628c2ecf20Sopenharmony_ci				 &send_info.info, NULL);
7638c2ecf20Sopenharmony_ci}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_cistatic int prestera_port_fdb_set(struct prestera_port *port,
7668c2ecf20Sopenharmony_ci				 struct switchdev_notifier_fdb_info *fdb_info,
7678c2ecf20Sopenharmony_ci				 bool adding)
7688c2ecf20Sopenharmony_ci{
7698c2ecf20Sopenharmony_ci	struct prestera_switch *sw = port->sw;
7708c2ecf20Sopenharmony_ci	struct prestera_bridge_port *br_port;
7718c2ecf20Sopenharmony_ci	struct prestera_bridge *bridge;
7728c2ecf20Sopenharmony_ci	int err;
7738c2ecf20Sopenharmony_ci	u16 vid;
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	br_port = prestera_bridge_port_by_dev(sw->swdev, port->dev);
7768c2ecf20Sopenharmony_ci	if (!br_port)
7778c2ecf20Sopenharmony_ci		return -EINVAL;
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	bridge = br_port->bridge;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	if (bridge->vlan_enabled)
7828c2ecf20Sopenharmony_ci		vid = fdb_info->vid;
7838c2ecf20Sopenharmony_ci	else
7848c2ecf20Sopenharmony_ci		vid = bridge->bridge_id;
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	if (adding)
7878c2ecf20Sopenharmony_ci		err = prestera_hw_fdb_add(port, fdb_info->addr, vid, false);
7888c2ecf20Sopenharmony_ci	else
7898c2ecf20Sopenharmony_ci		err = prestera_hw_fdb_del(port, fdb_info->addr, vid);
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	return err;
7928c2ecf20Sopenharmony_ci}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_cistatic void prestera_fdb_event_work(struct work_struct *work)
7958c2ecf20Sopenharmony_ci{
7968c2ecf20Sopenharmony_ci	struct switchdev_notifier_fdb_info *fdb_info;
7978c2ecf20Sopenharmony_ci	struct prestera_fdb_event_work *swdev_work;
7988c2ecf20Sopenharmony_ci	struct prestera_port *port;
7998c2ecf20Sopenharmony_ci	struct net_device *dev;
8008c2ecf20Sopenharmony_ci	int err;
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	swdev_work = container_of(work, struct prestera_fdb_event_work, work);
8038c2ecf20Sopenharmony_ci	dev = swdev_work->dev;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	rtnl_lock();
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	port = prestera_port_dev_lower_find(dev);
8088c2ecf20Sopenharmony_ci	if (!port)
8098c2ecf20Sopenharmony_ci		goto out_unlock;
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	switch (swdev_work->event) {
8128c2ecf20Sopenharmony_ci	case SWITCHDEV_FDB_ADD_TO_DEVICE:
8138c2ecf20Sopenharmony_ci		fdb_info = &swdev_work->fdb_info;
8148c2ecf20Sopenharmony_ci		if (!fdb_info->added_by_user)
8158c2ecf20Sopenharmony_ci			break;
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci		err = prestera_port_fdb_set(port, fdb_info, true);
8188c2ecf20Sopenharmony_ci		if (err)
8198c2ecf20Sopenharmony_ci			break;
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci		prestera_fdb_offload_notify(port, fdb_info);
8228c2ecf20Sopenharmony_ci		break;
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	case SWITCHDEV_FDB_DEL_TO_DEVICE:
8258c2ecf20Sopenharmony_ci		fdb_info = &swdev_work->fdb_info;
8268c2ecf20Sopenharmony_ci		prestera_port_fdb_set(port, fdb_info, false);
8278c2ecf20Sopenharmony_ci		break;
8288c2ecf20Sopenharmony_ci	}
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ciout_unlock:
8318c2ecf20Sopenharmony_ci	rtnl_unlock();
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	kfree(swdev_work->fdb_info.addr);
8348c2ecf20Sopenharmony_ci	kfree(swdev_work);
8358c2ecf20Sopenharmony_ci	dev_put(dev);
8368c2ecf20Sopenharmony_ci}
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_cistatic int prestera_switchdev_event(struct notifier_block *unused,
8398c2ecf20Sopenharmony_ci				    unsigned long event, void *ptr)
8408c2ecf20Sopenharmony_ci{
8418c2ecf20Sopenharmony_ci	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
8428c2ecf20Sopenharmony_ci	struct switchdev_notifier_fdb_info *fdb_info;
8438c2ecf20Sopenharmony_ci	struct switchdev_notifier_info *info = ptr;
8448c2ecf20Sopenharmony_ci	struct prestera_fdb_event_work *swdev_work;
8458c2ecf20Sopenharmony_ci	struct net_device *upper;
8468c2ecf20Sopenharmony_ci	int err;
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	if (event == SWITCHDEV_PORT_ATTR_SET) {
8498c2ecf20Sopenharmony_ci		err = switchdev_handle_port_attr_set(dev, ptr,
8508c2ecf20Sopenharmony_ci						     prestera_netdev_check,
8518c2ecf20Sopenharmony_ci						     prestera_port_obj_attr_set);
8528c2ecf20Sopenharmony_ci		return notifier_from_errno(err);
8538c2ecf20Sopenharmony_ci	}
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	if (!prestera_netdev_check(dev))
8568c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	upper = netdev_master_upper_dev_get_rcu(dev);
8598c2ecf20Sopenharmony_ci	if (!upper)
8608c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	if (!netif_is_bridge_master(upper))
8638c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	swdev_work = kzalloc(sizeof(*swdev_work), GFP_ATOMIC);
8668c2ecf20Sopenharmony_ci	if (!swdev_work)
8678c2ecf20Sopenharmony_ci		return NOTIFY_BAD;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	swdev_work->event = event;
8708c2ecf20Sopenharmony_ci	swdev_work->dev = dev;
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	switch (event) {
8738c2ecf20Sopenharmony_ci	case SWITCHDEV_FDB_ADD_TO_DEVICE:
8748c2ecf20Sopenharmony_ci	case SWITCHDEV_FDB_DEL_TO_DEVICE:
8758c2ecf20Sopenharmony_ci		fdb_info = container_of(info,
8768c2ecf20Sopenharmony_ci					struct switchdev_notifier_fdb_info,
8778c2ecf20Sopenharmony_ci					info);
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci		INIT_WORK(&swdev_work->work, prestera_fdb_event_work);
8808c2ecf20Sopenharmony_ci		memcpy(&swdev_work->fdb_info, ptr,
8818c2ecf20Sopenharmony_ci		       sizeof(swdev_work->fdb_info));
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci		swdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
8848c2ecf20Sopenharmony_ci		if (!swdev_work->fdb_info.addr)
8858c2ecf20Sopenharmony_ci			goto out_bad;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci		ether_addr_copy((u8 *)swdev_work->fdb_info.addr,
8888c2ecf20Sopenharmony_ci				fdb_info->addr);
8898c2ecf20Sopenharmony_ci		dev_hold(dev);
8908c2ecf20Sopenharmony_ci		break;
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	default:
8938c2ecf20Sopenharmony_ci		kfree(swdev_work);
8948c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
8958c2ecf20Sopenharmony_ci	}
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	queue_work(swdev_wq, &swdev_work->work);
8988c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ciout_bad:
9018c2ecf20Sopenharmony_ci	kfree(swdev_work);
9028c2ecf20Sopenharmony_ci	return NOTIFY_BAD;
9038c2ecf20Sopenharmony_ci}
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_cistatic int
9068c2ecf20Sopenharmony_ciprestera_port_vlan_bridge_join(struct prestera_port_vlan *port_vlan,
9078c2ecf20Sopenharmony_ci			       struct prestera_bridge_port *br_port)
9088c2ecf20Sopenharmony_ci{
9098c2ecf20Sopenharmony_ci	struct prestera_port *port = port_vlan->port;
9108c2ecf20Sopenharmony_ci	struct prestera_bridge_vlan *br_vlan;
9118c2ecf20Sopenharmony_ci	u16 vid = port_vlan->vid;
9128c2ecf20Sopenharmony_ci	int err;
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	if (port_vlan->br_port)
9158c2ecf20Sopenharmony_ci		return 0;
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	err = prestera_hw_port_flood_set(port, br_port->flags & BR_FLOOD);
9188c2ecf20Sopenharmony_ci	if (err)
9198c2ecf20Sopenharmony_ci		return err;
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	err = prestera_hw_port_learning_set(port, br_port->flags & BR_LEARNING);
9228c2ecf20Sopenharmony_ci	if (err)
9238c2ecf20Sopenharmony_ci		goto err_port_learning_set;
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	err = prestera_port_vid_stp_set(port, vid, br_port->stp_state);
9268c2ecf20Sopenharmony_ci	if (err)
9278c2ecf20Sopenharmony_ci		goto err_port_vid_stp_set;
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	br_vlan = prestera_bridge_vlan_by_vid(br_port, vid);
9308c2ecf20Sopenharmony_ci	if (!br_vlan) {
9318c2ecf20Sopenharmony_ci		br_vlan = prestera_bridge_vlan_create(br_port, vid);
9328c2ecf20Sopenharmony_ci		if (!br_vlan) {
9338c2ecf20Sopenharmony_ci			err = -ENOMEM;
9348c2ecf20Sopenharmony_ci			goto err_bridge_vlan_get;
9358c2ecf20Sopenharmony_ci		}
9368c2ecf20Sopenharmony_ci	}
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	list_add(&port_vlan->br_vlan_head, &br_vlan->port_vlan_list);
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	prestera_bridge_port_get(br_port);
9418c2ecf20Sopenharmony_ci	port_vlan->br_port = br_port;
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	return 0;
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_cierr_bridge_vlan_get:
9468c2ecf20Sopenharmony_ci	prestera_port_vid_stp_set(port, vid, BR_STATE_FORWARDING);
9478c2ecf20Sopenharmony_cierr_port_vid_stp_set:
9488c2ecf20Sopenharmony_ci	prestera_hw_port_learning_set(port, false);
9498c2ecf20Sopenharmony_cierr_port_learning_set:
9508c2ecf20Sopenharmony_ci	return err;
9518c2ecf20Sopenharmony_ci}
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_cistatic int
9548c2ecf20Sopenharmony_ciprestera_bridge_port_vlan_add(struct prestera_port *port,
9558c2ecf20Sopenharmony_ci			      struct prestera_bridge_port *br_port,
9568c2ecf20Sopenharmony_ci			      u16 vid, bool is_untagged, bool is_pvid,
9578c2ecf20Sopenharmony_ci			      struct netlink_ext_ack *extack)
9588c2ecf20Sopenharmony_ci{
9598c2ecf20Sopenharmony_ci	struct prestera_port_vlan *port_vlan;
9608c2ecf20Sopenharmony_ci	u16 old_pvid = port->pvid;
9618c2ecf20Sopenharmony_ci	u16 pvid;
9628c2ecf20Sopenharmony_ci	int err;
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	if (is_pvid)
9658c2ecf20Sopenharmony_ci		pvid = vid;
9668c2ecf20Sopenharmony_ci	else
9678c2ecf20Sopenharmony_ci		pvid = port->pvid == vid ? 0 : port->pvid;
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	port_vlan = prestera_port_vlan_by_vid(port, vid);
9708c2ecf20Sopenharmony_ci	if (port_vlan && port_vlan->br_port != br_port)
9718c2ecf20Sopenharmony_ci		return -EEXIST;
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	if (!port_vlan) {
9748c2ecf20Sopenharmony_ci		port_vlan = prestera_port_vlan_create(port, vid, is_untagged);
9758c2ecf20Sopenharmony_ci		if (IS_ERR(port_vlan))
9768c2ecf20Sopenharmony_ci			return PTR_ERR(port_vlan);
9778c2ecf20Sopenharmony_ci	} else {
9788c2ecf20Sopenharmony_ci		err = prestera_hw_vlan_port_set(port, vid, true, is_untagged);
9798c2ecf20Sopenharmony_ci		if (err)
9808c2ecf20Sopenharmony_ci			goto err_port_vlan_set;
9818c2ecf20Sopenharmony_ci	}
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	err = prestera_port_pvid_set(port, pvid);
9848c2ecf20Sopenharmony_ci	if (err)
9858c2ecf20Sopenharmony_ci		goto err_port_pvid_set;
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	err = prestera_port_vlan_bridge_join(port_vlan, br_port);
9888c2ecf20Sopenharmony_ci	if (err)
9898c2ecf20Sopenharmony_ci		goto err_port_vlan_bridge_join;
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	return 0;
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_cierr_port_vlan_bridge_join:
9948c2ecf20Sopenharmony_ci	prestera_port_pvid_set(port, old_pvid);
9958c2ecf20Sopenharmony_cierr_port_pvid_set:
9968c2ecf20Sopenharmony_ci	prestera_hw_vlan_port_set(port, vid, false, false);
9978c2ecf20Sopenharmony_cierr_port_vlan_set:
9988c2ecf20Sopenharmony_ci	prestera_port_vlan_destroy(port_vlan);
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci	return err;
10018c2ecf20Sopenharmony_ci}
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_cistatic void
10048c2ecf20Sopenharmony_ciprestera_bridge_port_vlan_del(struct prestera_port *port,
10058c2ecf20Sopenharmony_ci			      struct prestera_bridge_port *br_port, u16 vid)
10068c2ecf20Sopenharmony_ci{
10078c2ecf20Sopenharmony_ci	u16 pvid = port->pvid == vid ? 0 : port->pvid;
10088c2ecf20Sopenharmony_ci	struct prestera_port_vlan *port_vlan;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	port_vlan = prestera_port_vlan_by_vid(port, vid);
10118c2ecf20Sopenharmony_ci	if (WARN_ON(!port_vlan))
10128c2ecf20Sopenharmony_ci		return;
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	prestera_port_vlan_bridge_leave(port_vlan);
10158c2ecf20Sopenharmony_ci	prestera_port_pvid_set(port, pvid);
10168c2ecf20Sopenharmony_ci	prestera_port_vlan_destroy(port_vlan);
10178c2ecf20Sopenharmony_ci}
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_cistatic int prestera_port_vlans_add(struct prestera_port *port,
10208c2ecf20Sopenharmony_ci				   const struct switchdev_obj_port_vlan *vlan,
10218c2ecf20Sopenharmony_ci				   struct switchdev_trans *trans,
10228c2ecf20Sopenharmony_ci				   struct netlink_ext_ack *extack)
10238c2ecf20Sopenharmony_ci{
10248c2ecf20Sopenharmony_ci	bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
10258c2ecf20Sopenharmony_ci	bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
10268c2ecf20Sopenharmony_ci	struct net_device *dev = vlan->obj.orig_dev;
10278c2ecf20Sopenharmony_ci	struct prestera_bridge_port *br_port;
10288c2ecf20Sopenharmony_ci	struct prestera_switch *sw = port->sw;
10298c2ecf20Sopenharmony_ci	struct prestera_bridge *bridge;
10308c2ecf20Sopenharmony_ci	u16 vid;
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	if (netif_is_bridge_master(dev))
10338c2ecf20Sopenharmony_ci		return 0;
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	if (switchdev_trans_ph_commit(trans))
10368c2ecf20Sopenharmony_ci		return 0;
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	br_port = prestera_bridge_port_by_dev(sw->swdev, dev);
10398c2ecf20Sopenharmony_ci	if (WARN_ON(!br_port))
10408c2ecf20Sopenharmony_ci		return -EINVAL;
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci	bridge = br_port->bridge;
10438c2ecf20Sopenharmony_ci	if (!bridge->vlan_enabled)
10448c2ecf20Sopenharmony_ci		return 0;
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
10478c2ecf20Sopenharmony_ci		int err;
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci		err = prestera_bridge_port_vlan_add(port, br_port,
10508c2ecf20Sopenharmony_ci						    vid, flag_untagged,
10518c2ecf20Sopenharmony_ci						    flag_pvid, extack);
10528c2ecf20Sopenharmony_ci		if (err)
10538c2ecf20Sopenharmony_ci			return err;
10548c2ecf20Sopenharmony_ci	}
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	return 0;
10578c2ecf20Sopenharmony_ci}
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_cistatic int prestera_port_obj_add(struct net_device *dev,
10608c2ecf20Sopenharmony_ci				 const struct switchdev_obj *obj,
10618c2ecf20Sopenharmony_ci				 struct switchdev_trans *trans,
10628c2ecf20Sopenharmony_ci				 struct netlink_ext_ack *extack)
10638c2ecf20Sopenharmony_ci{
10648c2ecf20Sopenharmony_ci	struct prestera_port *port = netdev_priv(dev);
10658c2ecf20Sopenharmony_ci	const struct switchdev_obj_port_vlan *vlan;
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	switch (obj->id) {
10688c2ecf20Sopenharmony_ci	case SWITCHDEV_OBJ_ID_PORT_VLAN:
10698c2ecf20Sopenharmony_ci		vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
10708c2ecf20Sopenharmony_ci		return prestera_port_vlans_add(port, vlan, trans, extack);
10718c2ecf20Sopenharmony_ci	default:
10728c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
10738c2ecf20Sopenharmony_ci	}
10748c2ecf20Sopenharmony_ci}
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_cistatic int prestera_port_vlans_del(struct prestera_port *port,
10778c2ecf20Sopenharmony_ci				   const struct switchdev_obj_port_vlan *vlan)
10788c2ecf20Sopenharmony_ci{
10798c2ecf20Sopenharmony_ci	struct net_device *dev = vlan->obj.orig_dev;
10808c2ecf20Sopenharmony_ci	struct prestera_bridge_port *br_port;
10818c2ecf20Sopenharmony_ci	struct prestera_switch *sw = port->sw;
10828c2ecf20Sopenharmony_ci	u16 vid;
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	if (netif_is_bridge_master(dev))
10858c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	br_port = prestera_bridge_port_by_dev(sw->swdev, dev);
10888c2ecf20Sopenharmony_ci	if (WARN_ON(!br_port))
10898c2ecf20Sopenharmony_ci		return -EINVAL;
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	if (!br_port->bridge->vlan_enabled)
10928c2ecf20Sopenharmony_ci		return 0;
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci	for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++)
10958c2ecf20Sopenharmony_ci		prestera_bridge_port_vlan_del(port, br_port, vid);
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci	return 0;
10988c2ecf20Sopenharmony_ci}
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_cistatic int prestera_port_obj_del(struct net_device *dev,
11018c2ecf20Sopenharmony_ci				 const struct switchdev_obj *obj)
11028c2ecf20Sopenharmony_ci{
11038c2ecf20Sopenharmony_ci	struct prestera_port *port = netdev_priv(dev);
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	switch (obj->id) {
11068c2ecf20Sopenharmony_ci	case SWITCHDEV_OBJ_ID_PORT_VLAN:
11078c2ecf20Sopenharmony_ci		return prestera_port_vlans_del(port, SWITCHDEV_OBJ_PORT_VLAN(obj));
11088c2ecf20Sopenharmony_ci	default:
11098c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
11108c2ecf20Sopenharmony_ci	}
11118c2ecf20Sopenharmony_ci}
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_cistatic int prestera_switchdev_blk_event(struct notifier_block *unused,
11148c2ecf20Sopenharmony_ci					unsigned long event, void *ptr)
11158c2ecf20Sopenharmony_ci{
11168c2ecf20Sopenharmony_ci	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
11178c2ecf20Sopenharmony_ci	int err;
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci	switch (event) {
11208c2ecf20Sopenharmony_ci	case SWITCHDEV_PORT_OBJ_ADD:
11218c2ecf20Sopenharmony_ci		err = switchdev_handle_port_obj_add(dev, ptr,
11228c2ecf20Sopenharmony_ci						    prestera_netdev_check,
11238c2ecf20Sopenharmony_ci						    prestera_port_obj_add);
11248c2ecf20Sopenharmony_ci		break;
11258c2ecf20Sopenharmony_ci	case SWITCHDEV_PORT_OBJ_DEL:
11268c2ecf20Sopenharmony_ci		err = switchdev_handle_port_obj_del(dev, ptr,
11278c2ecf20Sopenharmony_ci						    prestera_netdev_check,
11288c2ecf20Sopenharmony_ci						    prestera_port_obj_del);
11298c2ecf20Sopenharmony_ci		break;
11308c2ecf20Sopenharmony_ci	case SWITCHDEV_PORT_ATTR_SET:
11318c2ecf20Sopenharmony_ci		err = switchdev_handle_port_attr_set(dev, ptr,
11328c2ecf20Sopenharmony_ci						     prestera_netdev_check,
11338c2ecf20Sopenharmony_ci						     prestera_port_obj_attr_set);
11348c2ecf20Sopenharmony_ci		break;
11358c2ecf20Sopenharmony_ci	default:
11368c2ecf20Sopenharmony_ci		err = -EOPNOTSUPP;
11378c2ecf20Sopenharmony_ci	}
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	return notifier_from_errno(err);
11408c2ecf20Sopenharmony_ci}
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_cistatic void prestera_fdb_event(struct prestera_switch *sw,
11438c2ecf20Sopenharmony_ci			       struct prestera_event *evt, void *arg)
11448c2ecf20Sopenharmony_ci{
11458c2ecf20Sopenharmony_ci	struct switchdev_notifier_fdb_info info;
11468c2ecf20Sopenharmony_ci	struct prestera_port *port;
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci	port = prestera_find_port(sw, evt->fdb_evt.port_id);
11498c2ecf20Sopenharmony_ci	if (!port)
11508c2ecf20Sopenharmony_ci		return;
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_ci	info.addr = evt->fdb_evt.data.mac;
11538c2ecf20Sopenharmony_ci	info.vid = evt->fdb_evt.vid;
11548c2ecf20Sopenharmony_ci	info.offloaded = true;
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	rtnl_lock();
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_ci	switch (evt->id) {
11598c2ecf20Sopenharmony_ci	case PRESTERA_FDB_EVENT_LEARNED:
11608c2ecf20Sopenharmony_ci		call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE,
11618c2ecf20Sopenharmony_ci					 port->dev, &info.info, NULL);
11628c2ecf20Sopenharmony_ci		break;
11638c2ecf20Sopenharmony_ci	case PRESTERA_FDB_EVENT_AGED:
11648c2ecf20Sopenharmony_ci		call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE,
11658c2ecf20Sopenharmony_ci					 port->dev, &info.info, NULL);
11668c2ecf20Sopenharmony_ci		break;
11678c2ecf20Sopenharmony_ci	}
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	rtnl_unlock();
11708c2ecf20Sopenharmony_ci}
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_cistatic int prestera_fdb_init(struct prestera_switch *sw)
11738c2ecf20Sopenharmony_ci{
11748c2ecf20Sopenharmony_ci	int err;
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci	err = prestera_hw_event_handler_register(sw, PRESTERA_EVENT_TYPE_FDB,
11778c2ecf20Sopenharmony_ci						 prestera_fdb_event, NULL);
11788c2ecf20Sopenharmony_ci	if (err)
11798c2ecf20Sopenharmony_ci		return err;
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	err = prestera_hw_switch_ageing_set(sw, PRESTERA_DEFAULT_AGEING_TIME_MS);
11828c2ecf20Sopenharmony_ci	if (err)
11838c2ecf20Sopenharmony_ci		goto err_ageing_set;
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci	return 0;
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_cierr_ageing_set:
11888c2ecf20Sopenharmony_ci	prestera_hw_event_handler_unregister(sw, PRESTERA_EVENT_TYPE_FDB,
11898c2ecf20Sopenharmony_ci					     prestera_fdb_event);
11908c2ecf20Sopenharmony_ci	return err;
11918c2ecf20Sopenharmony_ci}
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_cistatic void prestera_fdb_fini(struct prestera_switch *sw)
11948c2ecf20Sopenharmony_ci{
11958c2ecf20Sopenharmony_ci	prestera_hw_event_handler_unregister(sw, PRESTERA_EVENT_TYPE_FDB,
11968c2ecf20Sopenharmony_ci					     prestera_fdb_event);
11978c2ecf20Sopenharmony_ci}
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_cistatic int prestera_switchdev_handler_init(struct prestera_switchdev *swdev)
12008c2ecf20Sopenharmony_ci{
12018c2ecf20Sopenharmony_ci	int err;
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	swdev->swdev_nb.notifier_call = prestera_switchdev_event;
12048c2ecf20Sopenharmony_ci	err = register_switchdev_notifier(&swdev->swdev_nb);
12058c2ecf20Sopenharmony_ci	if (err)
12068c2ecf20Sopenharmony_ci		goto err_register_swdev_notifier;
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci	swdev->swdev_nb_blk.notifier_call = prestera_switchdev_blk_event;
12098c2ecf20Sopenharmony_ci	err = register_switchdev_blocking_notifier(&swdev->swdev_nb_blk);
12108c2ecf20Sopenharmony_ci	if (err)
12118c2ecf20Sopenharmony_ci		goto err_register_blk_swdev_notifier;
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci	return 0;
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_cierr_register_blk_swdev_notifier:
12168c2ecf20Sopenharmony_ci	unregister_switchdev_notifier(&swdev->swdev_nb);
12178c2ecf20Sopenharmony_cierr_register_swdev_notifier:
12188c2ecf20Sopenharmony_ci	destroy_workqueue(swdev_wq);
12198c2ecf20Sopenharmony_ci	return err;
12208c2ecf20Sopenharmony_ci}
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_cistatic void prestera_switchdev_handler_fini(struct prestera_switchdev *swdev)
12238c2ecf20Sopenharmony_ci{
12248c2ecf20Sopenharmony_ci	unregister_switchdev_blocking_notifier(&swdev->swdev_nb_blk);
12258c2ecf20Sopenharmony_ci	unregister_switchdev_notifier(&swdev->swdev_nb);
12268c2ecf20Sopenharmony_ci}
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ciint prestera_switchdev_init(struct prestera_switch *sw)
12298c2ecf20Sopenharmony_ci{
12308c2ecf20Sopenharmony_ci	struct prestera_switchdev *swdev;
12318c2ecf20Sopenharmony_ci	int err;
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci	swdev = kzalloc(sizeof(*swdev), GFP_KERNEL);
12348c2ecf20Sopenharmony_ci	if (!swdev)
12358c2ecf20Sopenharmony_ci		return -ENOMEM;
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci	sw->swdev = swdev;
12388c2ecf20Sopenharmony_ci	swdev->sw = sw;
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&swdev->bridge_list);
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	swdev_wq = alloc_ordered_workqueue("%s_ordered", 0, "prestera_br");
12438c2ecf20Sopenharmony_ci	if (!swdev_wq) {
12448c2ecf20Sopenharmony_ci		err = -ENOMEM;
12458c2ecf20Sopenharmony_ci		goto err_alloc_wq;
12468c2ecf20Sopenharmony_ci	}
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci	err = prestera_switchdev_handler_init(swdev);
12498c2ecf20Sopenharmony_ci	if (err)
12508c2ecf20Sopenharmony_ci		goto err_swdev_init;
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	err = prestera_fdb_init(sw);
12538c2ecf20Sopenharmony_ci	if (err)
12548c2ecf20Sopenharmony_ci		goto err_fdb_init;
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	return 0;
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_cierr_fdb_init:
12598c2ecf20Sopenharmony_cierr_swdev_init:
12608c2ecf20Sopenharmony_ci	destroy_workqueue(swdev_wq);
12618c2ecf20Sopenharmony_cierr_alloc_wq:
12628c2ecf20Sopenharmony_ci	kfree(swdev);
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci	return err;
12658c2ecf20Sopenharmony_ci}
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_civoid prestera_switchdev_fini(struct prestera_switch *sw)
12688c2ecf20Sopenharmony_ci{
12698c2ecf20Sopenharmony_ci	struct prestera_switchdev *swdev = sw->swdev;
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	prestera_fdb_fini(sw);
12728c2ecf20Sopenharmony_ci	prestera_switchdev_handler_fini(swdev);
12738c2ecf20Sopenharmony_ci	destroy_workqueue(swdev_wq);
12748c2ecf20Sopenharmony_ci	kfree(swdev);
12758c2ecf20Sopenharmony_ci}
1276