18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Handling of a single switch chip, part of a switch fabric
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2017 Savoir-faire Linux Inc.
68c2ecf20Sopenharmony_ci *	Vivien Didelot <vivien.didelot@savoirfairelinux.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/if_bridge.h>
108c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
118c2ecf20Sopenharmony_ci#include <linux/notifier.h>
128c2ecf20Sopenharmony_ci#include <linux/if_vlan.h>
138c2ecf20Sopenharmony_ci#include <net/switchdev.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include "dsa_priv.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic unsigned int dsa_switch_fastest_ageing_time(struct dsa_switch *ds,
188c2ecf20Sopenharmony_ci						   unsigned int ageing_time)
198c2ecf20Sopenharmony_ci{
208c2ecf20Sopenharmony_ci	int i;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	for (i = 0; i < ds->num_ports; ++i) {
238c2ecf20Sopenharmony_ci		struct dsa_port *dp = dsa_to_port(ds, i);
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci		if (dp->ageing_time && dp->ageing_time < ageing_time)
268c2ecf20Sopenharmony_ci			ageing_time = dp->ageing_time;
278c2ecf20Sopenharmony_ci	}
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	return ageing_time;
308c2ecf20Sopenharmony_ci}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic int dsa_switch_ageing_time(struct dsa_switch *ds,
338c2ecf20Sopenharmony_ci				  struct dsa_notifier_ageing_time_info *info)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	unsigned int ageing_time = info->ageing_time;
368c2ecf20Sopenharmony_ci	struct switchdev_trans *trans = info->trans;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	if (switchdev_trans_ph_prepare(trans)) {
398c2ecf20Sopenharmony_ci		if (ds->ageing_time_min && ageing_time < ds->ageing_time_min)
408c2ecf20Sopenharmony_ci			return -ERANGE;
418c2ecf20Sopenharmony_ci		if (ds->ageing_time_max && ageing_time > ds->ageing_time_max)
428c2ecf20Sopenharmony_ci			return -ERANGE;
438c2ecf20Sopenharmony_ci		return 0;
448c2ecf20Sopenharmony_ci	}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	/* Program the fastest ageing time in case of multiple bridges */
478c2ecf20Sopenharmony_ci	ageing_time = dsa_switch_fastest_ageing_time(ds, ageing_time);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	if (ds->ops->set_ageing_time)
508c2ecf20Sopenharmony_ci		return ds->ops->set_ageing_time(ds, ageing_time);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	return 0;
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic bool dsa_switch_mtu_match(struct dsa_switch *ds, int port,
568c2ecf20Sopenharmony_ci				 struct dsa_notifier_mtu_info *info)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	if (ds->index == info->sw_index)
598c2ecf20Sopenharmony_ci		return (port == info->port) || dsa_is_dsa_port(ds, port);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	if (!info->propagate_upstream)
628c2ecf20Sopenharmony_ci		return false;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port))
658c2ecf20Sopenharmony_ci		return true;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	return false;
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic int dsa_switch_mtu(struct dsa_switch *ds,
718c2ecf20Sopenharmony_ci			  struct dsa_notifier_mtu_info *info)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	int port, ret;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (!ds->ops->port_change_mtu)
768c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	for (port = 0; port < ds->num_ports; port++) {
798c2ecf20Sopenharmony_ci		if (dsa_switch_mtu_match(ds, port, info)) {
808c2ecf20Sopenharmony_ci			ret = ds->ops->port_change_mtu(ds, port, info->mtu);
818c2ecf20Sopenharmony_ci			if (ret)
828c2ecf20Sopenharmony_ci				return ret;
838c2ecf20Sopenharmony_ci		}
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	return 0;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic int dsa_switch_bridge_join(struct dsa_switch *ds,
908c2ecf20Sopenharmony_ci				  struct dsa_notifier_bridge_info *info)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	struct dsa_switch_tree *dst = ds->dst;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	if (dst->index == info->tree_index && ds->index == info->sw_index &&
958c2ecf20Sopenharmony_ci	    ds->ops->port_bridge_join)
968c2ecf20Sopenharmony_ci		return ds->ops->port_bridge_join(ds, info->port, info->br);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	if ((dst->index != info->tree_index || ds->index != info->sw_index) &&
998c2ecf20Sopenharmony_ci	    ds->ops->crosschip_bridge_join)
1008c2ecf20Sopenharmony_ci		return ds->ops->crosschip_bridge_join(ds, info->tree_index,
1018c2ecf20Sopenharmony_ci						      info->sw_index,
1028c2ecf20Sopenharmony_ci						      info->port, info->br);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	return 0;
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic int dsa_switch_bridge_leave(struct dsa_switch *ds,
1088c2ecf20Sopenharmony_ci				   struct dsa_notifier_bridge_info *info)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	bool unset_vlan_filtering = br_vlan_enabled(info->br);
1118c2ecf20Sopenharmony_ci	struct dsa_switch_tree *dst = ds->dst;
1128c2ecf20Sopenharmony_ci	int err, i;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	if (dst->index == info->tree_index && ds->index == info->sw_index &&
1158c2ecf20Sopenharmony_ci	    ds->ops->port_bridge_leave)
1168c2ecf20Sopenharmony_ci		ds->ops->port_bridge_leave(ds, info->port, info->br);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if ((dst->index != info->tree_index || ds->index != info->sw_index) &&
1198c2ecf20Sopenharmony_ci	    ds->ops->crosschip_bridge_leave)
1208c2ecf20Sopenharmony_ci		ds->ops->crosschip_bridge_leave(ds, info->tree_index,
1218c2ecf20Sopenharmony_ci						info->sw_index, info->port,
1228c2ecf20Sopenharmony_ci						info->br);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	/* If the bridge was vlan_filtering, the bridge core doesn't trigger an
1258c2ecf20Sopenharmony_ci	 * event for changing vlan_filtering setting upon slave ports leaving
1268c2ecf20Sopenharmony_ci	 * it. That is a good thing, because that lets us handle it and also
1278c2ecf20Sopenharmony_ci	 * handle the case where the switch's vlan_filtering setting is global
1288c2ecf20Sopenharmony_ci	 * (not per port). When that happens, the correct moment to trigger the
1298c2ecf20Sopenharmony_ci	 * vlan_filtering callback is only when the last port left this bridge.
1308c2ecf20Sopenharmony_ci	 */
1318c2ecf20Sopenharmony_ci	if (unset_vlan_filtering && ds->vlan_filtering_is_global) {
1328c2ecf20Sopenharmony_ci		for (i = 0; i < ds->num_ports; i++) {
1338c2ecf20Sopenharmony_ci			if (i == info->port)
1348c2ecf20Sopenharmony_ci				continue;
1358c2ecf20Sopenharmony_ci			if (dsa_to_port(ds, i)->bridge_dev == info->br) {
1368c2ecf20Sopenharmony_ci				unset_vlan_filtering = false;
1378c2ecf20Sopenharmony_ci				break;
1388c2ecf20Sopenharmony_ci			}
1398c2ecf20Sopenharmony_ci		}
1408c2ecf20Sopenharmony_ci	}
1418c2ecf20Sopenharmony_ci	if (unset_vlan_filtering) {
1428c2ecf20Sopenharmony_ci		struct switchdev_trans trans;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci		trans.ph_prepare = true;
1458c2ecf20Sopenharmony_ci		err = dsa_port_vlan_filtering(dsa_to_port(ds, info->port),
1468c2ecf20Sopenharmony_ci					      false, &trans);
1478c2ecf20Sopenharmony_ci		if (err && err != EOPNOTSUPP)
1488c2ecf20Sopenharmony_ci			return err;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci		trans.ph_prepare = false;
1518c2ecf20Sopenharmony_ci		err = dsa_port_vlan_filtering(dsa_to_port(ds, info->port),
1528c2ecf20Sopenharmony_ci					      false, &trans);
1538c2ecf20Sopenharmony_ci		if (err && err != EOPNOTSUPP)
1548c2ecf20Sopenharmony_ci			return err;
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci	return 0;
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic int dsa_switch_fdb_add(struct dsa_switch *ds,
1608c2ecf20Sopenharmony_ci			      struct dsa_notifier_fdb_info *info)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	int port = dsa_towards_port(ds, info->sw_index, info->port);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	if (!ds->ops->port_fdb_add)
1658c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	return ds->ops->port_fdb_add(ds, port, info->addr, info->vid);
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic int dsa_switch_fdb_del(struct dsa_switch *ds,
1718c2ecf20Sopenharmony_ci			      struct dsa_notifier_fdb_info *info)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	int port = dsa_towards_port(ds, info->sw_index, info->port);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	if (!ds->ops->port_fdb_del)
1768c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	return ds->ops->port_fdb_del(ds, port, info->addr, info->vid);
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic bool dsa_switch_mdb_match(struct dsa_switch *ds, int port,
1828c2ecf20Sopenharmony_ci				 struct dsa_notifier_mdb_info *info)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	if (ds->index == info->sw_index && port == info->port)
1858c2ecf20Sopenharmony_ci		return true;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	if (dsa_is_dsa_port(ds, port))
1888c2ecf20Sopenharmony_ci		return true;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	return false;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic int dsa_switch_mdb_prepare(struct dsa_switch *ds,
1948c2ecf20Sopenharmony_ci				  struct dsa_notifier_mdb_info *info)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	int port, err;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (!ds->ops->port_mdb_prepare || !ds->ops->port_mdb_add)
1998c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	for (port = 0; port < ds->num_ports; port++) {
2028c2ecf20Sopenharmony_ci		if (dsa_switch_mdb_match(ds, port, info)) {
2038c2ecf20Sopenharmony_ci			err = ds->ops->port_mdb_prepare(ds, port, info->mdb);
2048c2ecf20Sopenharmony_ci			if (err)
2058c2ecf20Sopenharmony_ci				return err;
2068c2ecf20Sopenharmony_ci		}
2078c2ecf20Sopenharmony_ci	}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	return 0;
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic int dsa_switch_mdb_add(struct dsa_switch *ds,
2138c2ecf20Sopenharmony_ci			      struct dsa_notifier_mdb_info *info)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	int port;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (switchdev_trans_ph_prepare(info->trans))
2188c2ecf20Sopenharmony_ci		return dsa_switch_mdb_prepare(ds, info);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	if (!ds->ops->port_mdb_add)
2218c2ecf20Sopenharmony_ci		return 0;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	for (port = 0; port < ds->num_ports; port++)
2248c2ecf20Sopenharmony_ci		if (dsa_switch_mdb_match(ds, port, info))
2258c2ecf20Sopenharmony_ci			ds->ops->port_mdb_add(ds, port, info->mdb);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	return 0;
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic int dsa_switch_mdb_del(struct dsa_switch *ds,
2318c2ecf20Sopenharmony_ci			      struct dsa_notifier_mdb_info *info)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	if (!ds->ops->port_mdb_del)
2348c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	if (ds->index == info->sw_index)
2378c2ecf20Sopenharmony_ci		return ds->ops->port_mdb_del(ds, info->port, info->mdb);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	return 0;
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic bool dsa_switch_vlan_match(struct dsa_switch *ds, int port,
2438c2ecf20Sopenharmony_ci				  struct dsa_notifier_vlan_info *info)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	if (ds->index == info->sw_index && port == info->port)
2468c2ecf20Sopenharmony_ci		return true;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	if (dsa_is_dsa_port(ds, port))
2498c2ecf20Sopenharmony_ci		return true;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	return false;
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic int dsa_switch_vlan_prepare(struct dsa_switch *ds,
2558c2ecf20Sopenharmony_ci				   struct dsa_notifier_vlan_info *info)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	int port, err;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	if (!ds->ops->port_vlan_prepare || !ds->ops->port_vlan_add)
2608c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	for (port = 0; port < ds->num_ports; port++) {
2638c2ecf20Sopenharmony_ci		if (dsa_switch_vlan_match(ds, port, info)) {
2648c2ecf20Sopenharmony_ci			err = ds->ops->port_vlan_prepare(ds, port, info->vlan);
2658c2ecf20Sopenharmony_ci			if (err)
2668c2ecf20Sopenharmony_ci				return err;
2678c2ecf20Sopenharmony_ci		}
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	return 0;
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic int dsa_switch_vlan_add(struct dsa_switch *ds,
2748c2ecf20Sopenharmony_ci			       struct dsa_notifier_vlan_info *info)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	int port;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	if (switchdev_trans_ph_prepare(info->trans))
2798c2ecf20Sopenharmony_ci		return dsa_switch_vlan_prepare(ds, info);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	if (!ds->ops->port_vlan_add)
2828c2ecf20Sopenharmony_ci		return 0;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	for (port = 0; port < ds->num_ports; port++)
2858c2ecf20Sopenharmony_ci		if (dsa_switch_vlan_match(ds, port, info))
2868c2ecf20Sopenharmony_ci			ds->ops->port_vlan_add(ds, port, info->vlan);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	return 0;
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic int dsa_switch_vlan_del(struct dsa_switch *ds,
2928c2ecf20Sopenharmony_ci			       struct dsa_notifier_vlan_info *info)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	if (!ds->ops->port_vlan_del)
2958c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	if (ds->index == info->sw_index)
2988c2ecf20Sopenharmony_ci		return ds->ops->port_vlan_del(ds, info->port, info->vlan);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	/* Do not deprogram the DSA links as they may be used as conduit
3018c2ecf20Sopenharmony_ci	 * for other VLAN members in the fabric.
3028c2ecf20Sopenharmony_ci	 */
3038c2ecf20Sopenharmony_ci	return 0;
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic int dsa_switch_event(struct notifier_block *nb,
3078c2ecf20Sopenharmony_ci			    unsigned long event, void *info)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	struct dsa_switch *ds = container_of(nb, struct dsa_switch, nb);
3108c2ecf20Sopenharmony_ci	int err;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	switch (event) {
3138c2ecf20Sopenharmony_ci	case DSA_NOTIFIER_AGEING_TIME:
3148c2ecf20Sopenharmony_ci		err = dsa_switch_ageing_time(ds, info);
3158c2ecf20Sopenharmony_ci		break;
3168c2ecf20Sopenharmony_ci	case DSA_NOTIFIER_BRIDGE_JOIN:
3178c2ecf20Sopenharmony_ci		err = dsa_switch_bridge_join(ds, info);
3188c2ecf20Sopenharmony_ci		break;
3198c2ecf20Sopenharmony_ci	case DSA_NOTIFIER_BRIDGE_LEAVE:
3208c2ecf20Sopenharmony_ci		err = dsa_switch_bridge_leave(ds, info);
3218c2ecf20Sopenharmony_ci		break;
3228c2ecf20Sopenharmony_ci	case DSA_NOTIFIER_FDB_ADD:
3238c2ecf20Sopenharmony_ci		err = dsa_switch_fdb_add(ds, info);
3248c2ecf20Sopenharmony_ci		break;
3258c2ecf20Sopenharmony_ci	case DSA_NOTIFIER_FDB_DEL:
3268c2ecf20Sopenharmony_ci		err = dsa_switch_fdb_del(ds, info);
3278c2ecf20Sopenharmony_ci		break;
3288c2ecf20Sopenharmony_ci	case DSA_NOTIFIER_MDB_ADD:
3298c2ecf20Sopenharmony_ci		err = dsa_switch_mdb_add(ds, info);
3308c2ecf20Sopenharmony_ci		break;
3318c2ecf20Sopenharmony_ci	case DSA_NOTIFIER_MDB_DEL:
3328c2ecf20Sopenharmony_ci		err = dsa_switch_mdb_del(ds, info);
3338c2ecf20Sopenharmony_ci		break;
3348c2ecf20Sopenharmony_ci	case DSA_NOTIFIER_VLAN_ADD:
3358c2ecf20Sopenharmony_ci		err = dsa_switch_vlan_add(ds, info);
3368c2ecf20Sopenharmony_ci		break;
3378c2ecf20Sopenharmony_ci	case DSA_NOTIFIER_VLAN_DEL:
3388c2ecf20Sopenharmony_ci		err = dsa_switch_vlan_del(ds, info);
3398c2ecf20Sopenharmony_ci		break;
3408c2ecf20Sopenharmony_ci	case DSA_NOTIFIER_MTU:
3418c2ecf20Sopenharmony_ci		err = dsa_switch_mtu(ds, info);
3428c2ecf20Sopenharmony_ci		break;
3438c2ecf20Sopenharmony_ci	default:
3448c2ecf20Sopenharmony_ci		err = -EOPNOTSUPP;
3458c2ecf20Sopenharmony_ci		break;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	/* Non-switchdev operations cannot be rolled back. If a DSA driver
3498c2ecf20Sopenharmony_ci	 * returns an error during the chained call, switch chips may be in an
3508c2ecf20Sopenharmony_ci	 * inconsistent state.
3518c2ecf20Sopenharmony_ci	 */
3528c2ecf20Sopenharmony_ci	if (err)
3538c2ecf20Sopenharmony_ci		dev_dbg(ds->dev, "breaking chain for DSA event %lu (%d)\n",
3548c2ecf20Sopenharmony_ci			event, err);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	return notifier_from_errno(err);
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ciint dsa_switch_register_notifier(struct dsa_switch *ds)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	ds->nb.notifier_call = dsa_switch_event;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	return raw_notifier_chain_register(&ds->dst->nh, &ds->nb);
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_civoid dsa_switch_unregister_notifier(struct dsa_switch *ds)
3678c2ecf20Sopenharmony_ci{
3688c2ecf20Sopenharmony_ci	int err;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	err = raw_notifier_chain_unregister(&ds->dst->nh, &ds->nb);
3718c2ecf20Sopenharmony_ci	if (err)
3728c2ecf20Sopenharmony_ci		dev_err(ds->dev, "failed to unregister notifier (%d)\n", err);
3738c2ecf20Sopenharmony_ci}
374