xref: /openbmc/linux/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1d6fce514SSteen Hegelund // SPDX-License-Identifier: GPL-2.0+
2d6fce514SSteen Hegelund /* Microchip Sparx5 Switch driver
3d6fce514SSteen Hegelund  *
4d6fce514SSteen Hegelund  * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
5d6fce514SSteen Hegelund  */
6d6fce514SSteen Hegelund 
7d6fce514SSteen Hegelund #include <linux/if_bridge.h>
8d6fce514SSteen Hegelund #include <net/switchdev.h>
9d6fce514SSteen Hegelund 
10d6fce514SSteen Hegelund #include "sparx5_main_regs.h"
11d6fce514SSteen Hegelund #include "sparx5_main.h"
12d6fce514SSteen Hegelund 
13d6fce514SSteen Hegelund static struct workqueue_struct *sparx5_owq;
14d6fce514SSteen Hegelund 
15d6fce514SSteen Hegelund struct sparx5_switchdev_event_work {
16d6fce514SSteen Hegelund 	struct work_struct work;
17d6fce514SSteen Hegelund 	struct switchdev_notifier_fdb_info fdb_info;
18d6fce514SSteen Hegelund 	struct net_device *dev;
199f01cfbfSCasper Andersson 	struct sparx5 *sparx5;
20d6fce514SSteen Hegelund 	unsigned long event;
21d6fce514SSteen Hegelund };
22d6fce514SSteen Hegelund 
sparx5_port_attr_pre_bridge_flags(struct sparx5_port * port,struct switchdev_brport_flags flags)2306388a03SCasper Andersson static int sparx5_port_attr_pre_bridge_flags(struct sparx5_port *port,
2406388a03SCasper Andersson 					     struct switchdev_brport_flags flags)
2506388a03SCasper Andersson {
2606388a03SCasper Andersson 	if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD))
2706388a03SCasper Andersson 		return -EINVAL;
2806388a03SCasper Andersson 
2906388a03SCasper Andersson 	return 0;
3006388a03SCasper Andersson }
3106388a03SCasper Andersson 
sparx5_port_update_mcast_ip_flood(struct sparx5_port * port,bool flood_flag)32*04e551d6SCasper Andersson static void sparx5_port_update_mcast_ip_flood(struct sparx5_port *port, bool flood_flag)
33*04e551d6SCasper Andersson {
34*04e551d6SCasper Andersson 	bool should_flood = flood_flag || port->is_mrouter;
35*04e551d6SCasper Andersson 	int pgid;
36*04e551d6SCasper Andersson 
37*04e551d6SCasper Andersson 	for (pgid = PGID_IPV4_MC_DATA; pgid <= PGID_IPV6_MC_CTRL; pgid++)
38*04e551d6SCasper Andersson 		sparx5_pgid_update_mask(port, pgid, should_flood);
39*04e551d6SCasper Andersson }
40*04e551d6SCasper Andersson 
sparx5_port_attr_bridge_flags(struct sparx5_port * port,struct switchdev_brport_flags flags)41d6fce514SSteen Hegelund static void sparx5_port_attr_bridge_flags(struct sparx5_port *port,
42d6fce514SSteen Hegelund 					  struct switchdev_brport_flags flags)
43d6fce514SSteen Hegelund {
44*04e551d6SCasper Andersson 	if (flags.mask & BR_MCAST_FLOOD) {
45*04e551d6SCasper Andersson 		sparx5_pgid_update_mask(port, PGID_MC_FLOOD, !!(flags.val & BR_MCAST_FLOOD));
46*04e551d6SCasper Andersson 		sparx5_port_update_mcast_ip_flood(port, !!(flags.val & BR_MCAST_FLOOD));
47*04e551d6SCasper Andersson 	}
4806388a03SCasper Andersson 
4906388a03SCasper Andersson 	if (flags.mask & BR_FLOOD)
5006388a03SCasper Andersson 		sparx5_pgid_update_mask(port, PGID_UC_FLOOD, !!(flags.val & BR_FLOOD));
5106388a03SCasper Andersson 	if (flags.mask & BR_BCAST_FLOOD)
5206388a03SCasper Andersson 		sparx5_pgid_update_mask(port, PGID_BCAST, !!(flags.val & BR_BCAST_FLOOD));
53d6fce514SSteen Hegelund }
54d6fce514SSteen Hegelund 
sparx5_attr_stp_state_set(struct sparx5_port * port,u8 state)55d6fce514SSteen Hegelund static void sparx5_attr_stp_state_set(struct sparx5_port *port,
56d6fce514SSteen Hegelund 				      u8 state)
57d6fce514SSteen Hegelund {
58d6fce514SSteen Hegelund 	struct sparx5 *sparx5 = port->sparx5;
59d6fce514SSteen Hegelund 
60d6fce514SSteen Hegelund 	if (!test_bit(port->portno, sparx5->bridge_mask)) {
61d6fce514SSteen Hegelund 		netdev_err(port->ndev,
62d6fce514SSteen Hegelund 			   "Controlling non-bridged port %d?\n", port->portno);
63d6fce514SSteen Hegelund 		return;
64d6fce514SSteen Hegelund 	}
65d6fce514SSteen Hegelund 
66d6fce514SSteen Hegelund 	switch (state) {
67d6fce514SSteen Hegelund 	case BR_STATE_FORWARDING:
68d6fce514SSteen Hegelund 		set_bit(port->portno, sparx5->bridge_fwd_mask);
69d6fce514SSteen Hegelund 		fallthrough;
70d6fce514SSteen Hegelund 	case BR_STATE_LEARNING:
71d6fce514SSteen Hegelund 		set_bit(port->portno, sparx5->bridge_lrn_mask);
72d6fce514SSteen Hegelund 		break;
73d6fce514SSteen Hegelund 
74d6fce514SSteen Hegelund 	default:
75d6fce514SSteen Hegelund 		/* All other states treated as blocking */
76d6fce514SSteen Hegelund 		clear_bit(port->portno, sparx5->bridge_fwd_mask);
77d6fce514SSteen Hegelund 		clear_bit(port->portno, sparx5->bridge_lrn_mask);
78d6fce514SSteen Hegelund 		break;
79d6fce514SSteen Hegelund 	}
80d6fce514SSteen Hegelund 
81d6fce514SSteen Hegelund 	/* apply the bridge_fwd_mask to all the ports */
82d6fce514SSteen Hegelund 	sparx5_update_fwd(sparx5);
83d6fce514SSteen Hegelund }
84d6fce514SSteen Hegelund 
sparx5_port_attr_ageing_set(struct sparx5_port * port,unsigned long ageing_clock_t)85d6fce514SSteen Hegelund static void sparx5_port_attr_ageing_set(struct sparx5_port *port,
86d6fce514SSteen Hegelund 					unsigned long ageing_clock_t)
87d6fce514SSteen Hegelund {
88d6fce514SSteen Hegelund 	unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t);
89d6fce514SSteen Hegelund 	u32 ageing_time = jiffies_to_msecs(ageing_jiffies);
90d6fce514SSteen Hegelund 
91d6fce514SSteen Hegelund 	sparx5_set_ageing(port->sparx5, ageing_time);
92d6fce514SSteen Hegelund }
93d6fce514SSteen Hegelund 
sparx5_port_attr_mrouter_set(struct sparx5_port * port,struct net_device * orig_dev,bool enable)94*04e551d6SCasper Andersson static void sparx5_port_attr_mrouter_set(struct sparx5_port *port,
95*04e551d6SCasper Andersson 					 struct net_device *orig_dev,
96*04e551d6SCasper Andersson 					 bool enable)
97*04e551d6SCasper Andersson {
98*04e551d6SCasper Andersson 	struct sparx5 *sparx5 = port->sparx5;
99*04e551d6SCasper Andersson 	struct sparx5_mdb_entry *e;
100*04e551d6SCasper Andersson 	bool flood_flag;
101*04e551d6SCasper Andersson 
102*04e551d6SCasper Andersson 	if ((enable && port->is_mrouter) || (!enable && !port->is_mrouter))
103*04e551d6SCasper Andersson 		return;
104*04e551d6SCasper Andersson 
105*04e551d6SCasper Andersson 	/* Add/del mrouter port on all active mdb entries in HW.
106*04e551d6SCasper Andersson 	 * Don't change entry port mask, since that represents
107*04e551d6SCasper Andersson 	 * ports that actually joined that group.
108*04e551d6SCasper Andersson 	 */
109*04e551d6SCasper Andersson 	mutex_lock(&sparx5->mdb_lock);
110*04e551d6SCasper Andersson 	list_for_each_entry(e, &sparx5->mdb_entries, list) {
111*04e551d6SCasper Andersson 		if (!test_bit(port->portno, e->port_mask) &&
112*04e551d6SCasper Andersson 		    ether_addr_is_ip_mcast(e->addr))
113*04e551d6SCasper Andersson 			sparx5_pgid_update_mask(port, e->pgid_idx, enable);
114*04e551d6SCasper Andersson 	}
115*04e551d6SCasper Andersson 	mutex_unlock(&sparx5->mdb_lock);
116*04e551d6SCasper Andersson 
117*04e551d6SCasper Andersson 	/* Enable/disable flooding depending on if port is mrouter port
118*04e551d6SCasper Andersson 	 * or if mcast flood is enabled.
119*04e551d6SCasper Andersson 	 */
120*04e551d6SCasper Andersson 	port->is_mrouter = enable;
121*04e551d6SCasper Andersson 	flood_flag = br_port_flag_is_set(port->ndev, BR_MCAST_FLOOD);
122*04e551d6SCasper Andersson 	sparx5_port_update_mcast_ip_flood(port, flood_flag);
123*04e551d6SCasper Andersson }
124*04e551d6SCasper Andersson 
sparx5_port_attr_set(struct net_device * dev,const void * ctx,const struct switchdev_attr * attr,struct netlink_ext_ack * extack)12569bfac96SVladimir Oltean static int sparx5_port_attr_set(struct net_device *dev, const void *ctx,
126d6fce514SSteen Hegelund 				const struct switchdev_attr *attr,
127d6fce514SSteen Hegelund 				struct netlink_ext_ack *extack)
128d6fce514SSteen Hegelund {
129d6fce514SSteen Hegelund 	struct sparx5_port *port = netdev_priv(dev);
130d6fce514SSteen Hegelund 
131d6fce514SSteen Hegelund 	switch (attr->id) {
13206388a03SCasper Andersson 	case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
13306388a03SCasper Andersson 		return sparx5_port_attr_pre_bridge_flags(port,
13406388a03SCasper Andersson 							 attr->u.brport_flags);
135d6fce514SSteen Hegelund 	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
136d6fce514SSteen Hegelund 		sparx5_port_attr_bridge_flags(port, attr->u.brport_flags);
137d6fce514SSteen Hegelund 		break;
138d6fce514SSteen Hegelund 	case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
139d6fce514SSteen Hegelund 		sparx5_attr_stp_state_set(port, attr->u.stp_state);
140d6fce514SSteen Hegelund 		break;
141d6fce514SSteen Hegelund 	case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
142d6fce514SSteen Hegelund 		sparx5_port_attr_ageing_set(port, attr->u.ageing_time);
143d6fce514SSteen Hegelund 		break;
144d6fce514SSteen Hegelund 	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
145e6980b57SCasper Andersson 		/* Used PVID 1 when default_pvid is 0, to avoid
146e6980b57SCasper Andersson 		 * collision with non-bridged ports.
147e6980b57SCasper Andersson 		 */
148e6980b57SCasper Andersson 		if (port->pvid == 0)
149e6980b57SCasper Andersson 			port->pvid = 1;
150d6fce514SSteen Hegelund 		port->vlan_aware = attr->u.vlan_filtering;
151d6fce514SSteen Hegelund 		sparx5_vlan_port_apply(port->sparx5, port);
152d6fce514SSteen Hegelund 		break;
153*04e551d6SCasper Andersson 	case SWITCHDEV_ATTR_ID_PORT_MROUTER:
154*04e551d6SCasper Andersson 		sparx5_port_attr_mrouter_set(port,
155*04e551d6SCasper Andersson 					     attr->orig_dev,
156*04e551d6SCasper Andersson 					     attr->u.mrouter);
157*04e551d6SCasper Andersson 		break;
158d6fce514SSteen Hegelund 	default:
159d6fce514SSteen Hegelund 		return -EOPNOTSUPP;
160d6fce514SSteen Hegelund 	}
161d6fce514SSteen Hegelund 
162d6fce514SSteen Hegelund 	return 0;
163d6fce514SSteen Hegelund }
164d6fce514SSteen Hegelund 
sparx5_port_bridge_join(struct sparx5_port * port,struct net_device * bridge,struct netlink_ext_ack * extack)165d6fce514SSteen Hegelund static int sparx5_port_bridge_join(struct sparx5_port *port,
1662f5dc00fSVladimir Oltean 				   struct net_device *bridge,
1672f5dc00fSVladimir Oltean 				   struct netlink_ext_ack *extack)
168d6fce514SSteen Hegelund {
169d6fce514SSteen Hegelund 	struct sparx5 *sparx5 = port->sparx5;
1702f5dc00fSVladimir Oltean 	struct net_device *ndev = port->ndev;
1712f5dc00fSVladimir Oltean 	int err;
172d6fce514SSteen Hegelund 
173d6fce514SSteen Hegelund 	if (bitmap_empty(sparx5->bridge_mask, SPX5_PORTS))
174d6fce514SSteen Hegelund 		/* First bridged port */
175d6fce514SSteen Hegelund 		sparx5->hw_bridge_dev = bridge;
176d6fce514SSteen Hegelund 	else
177d6fce514SSteen Hegelund 		if (sparx5->hw_bridge_dev != bridge)
178d6fce514SSteen Hegelund 			/* This is adding the port to a second bridge, this is
179d6fce514SSteen Hegelund 			 * unsupported
180d6fce514SSteen Hegelund 			 */
181d6fce514SSteen Hegelund 			return -ENODEV;
182d6fce514SSteen Hegelund 
183d6fce514SSteen Hegelund 	set_bit(port->portno, sparx5->bridge_mask);
184d6fce514SSteen Hegelund 
1854e51bf44SVladimir Oltean 	err = switchdev_bridge_port_offload(ndev, ndev, NULL, NULL, NULL,
18647211192STobias Waldekranz 					    false, extack);
1872f5dc00fSVladimir Oltean 	if (err)
1882f5dc00fSVladimir Oltean 		goto err_switchdev_offload;
1892f5dc00fSVladimir Oltean 
190e6980b57SCasper Andersson 	/* Remove standalone port entry */
191e6980b57SCasper Andersson 	sparx5_mact_forget(sparx5, ndev->dev_addr, 0);
192e6980b57SCasper Andersson 
193d6fce514SSteen Hegelund 	/* Port enters in bridge mode therefor don't need to copy to CPU
194d6fce514SSteen Hegelund 	 * frames for multicast in case the bridge is not requesting them
195d6fce514SSteen Hegelund 	 */
1962f5dc00fSVladimir Oltean 	__dev_mc_unsync(ndev, sparx5_mc_unsync);
197d6fce514SSteen Hegelund 
198d6fce514SSteen Hegelund 	return 0;
1992f5dc00fSVladimir Oltean 
2002f5dc00fSVladimir Oltean err_switchdev_offload:
2012f5dc00fSVladimir Oltean 	clear_bit(port->portno, sparx5->bridge_mask);
2022f5dc00fSVladimir Oltean 	return err;
203d6fce514SSteen Hegelund }
204d6fce514SSteen Hegelund 
sparx5_port_bridge_leave(struct sparx5_port * port,struct net_device * bridge)205d6fce514SSteen Hegelund static void sparx5_port_bridge_leave(struct sparx5_port *port,
206d6fce514SSteen Hegelund 				     struct net_device *bridge)
207d6fce514SSteen Hegelund {
208d6fce514SSteen Hegelund 	struct sparx5 *sparx5 = port->sparx5;
209d6fce514SSteen Hegelund 
2104e51bf44SVladimir Oltean 	switchdev_bridge_port_unoffload(port->ndev, NULL, NULL, NULL);
2112f5dc00fSVladimir Oltean 
212d6fce514SSteen Hegelund 	clear_bit(port->portno, sparx5->bridge_mask);
213d6fce514SSteen Hegelund 	if (bitmap_empty(sparx5->bridge_mask, SPX5_PORTS))
214d6fce514SSteen Hegelund 		sparx5->hw_bridge_dev = NULL;
215d6fce514SSteen Hegelund 
216d6fce514SSteen Hegelund 	/* Clear bridge vlan settings before updating the port settings */
217d6fce514SSteen Hegelund 	port->vlan_aware = 0;
218d6fce514SSteen Hegelund 	port->pvid = NULL_VID;
219d6fce514SSteen Hegelund 	port->vid = NULL_VID;
220d6fce514SSteen Hegelund 
221e6980b57SCasper Andersson 	/* Forward frames to CPU */
222e6980b57SCasper Andersson 	sparx5_mact_learn(sparx5, PGID_CPU, port->ndev->dev_addr, 0);
223e6980b57SCasper Andersson 
224d6fce514SSteen Hegelund 	/* Port enters in host more therefore restore mc list */
225d6fce514SSteen Hegelund 	__dev_mc_sync(port->ndev, sparx5_mc_sync, sparx5_mc_unsync);
226d6fce514SSteen Hegelund }
227d6fce514SSteen Hegelund 
sparx5_port_changeupper(struct net_device * dev,struct netdev_notifier_changeupper_info * info)228d6fce514SSteen Hegelund static int sparx5_port_changeupper(struct net_device *dev,
229d6fce514SSteen Hegelund 				   struct netdev_notifier_changeupper_info *info)
230d6fce514SSteen Hegelund {
231d6fce514SSteen Hegelund 	struct sparx5_port *port = netdev_priv(dev);
2322f5dc00fSVladimir Oltean 	struct netlink_ext_ack *extack;
233d6fce514SSteen Hegelund 	int err = 0;
234d6fce514SSteen Hegelund 
2352f5dc00fSVladimir Oltean 	extack = netdev_notifier_info_to_extack(&info->info);
2362f5dc00fSVladimir Oltean 
237d6fce514SSteen Hegelund 	if (netif_is_bridge_master(info->upper_dev)) {
238d6fce514SSteen Hegelund 		if (info->linking)
2392f5dc00fSVladimir Oltean 			err = sparx5_port_bridge_join(port, info->upper_dev,
2402f5dc00fSVladimir Oltean 						      extack);
241d6fce514SSteen Hegelund 		else
242d6fce514SSteen Hegelund 			sparx5_port_bridge_leave(port, info->upper_dev);
243d6fce514SSteen Hegelund 
244d6fce514SSteen Hegelund 		sparx5_vlan_port_apply(port->sparx5, port);
245d6fce514SSteen Hegelund 	}
246d6fce514SSteen Hegelund 
247d6fce514SSteen Hegelund 	return err;
248d6fce514SSteen Hegelund }
249d6fce514SSteen Hegelund 
sparx5_port_add_addr(struct net_device * dev,bool up)250d6fce514SSteen Hegelund static int sparx5_port_add_addr(struct net_device *dev, bool up)
251d6fce514SSteen Hegelund {
252d6fce514SSteen Hegelund 	struct sparx5_port *port = netdev_priv(dev);
253d6fce514SSteen Hegelund 	struct sparx5 *sparx5 = port->sparx5;
254d6fce514SSteen Hegelund 	u16 vid = port->pvid;
255d6fce514SSteen Hegelund 
256d6fce514SSteen Hegelund 	if (up)
257d6fce514SSteen Hegelund 		sparx5_mact_learn(sparx5, PGID_CPU, port->ndev->dev_addr, vid);
258d6fce514SSteen Hegelund 	else
259d6fce514SSteen Hegelund 		sparx5_mact_forget(sparx5, port->ndev->dev_addr, vid);
260d6fce514SSteen Hegelund 
261d6fce514SSteen Hegelund 	return 0;
262d6fce514SSteen Hegelund }
263d6fce514SSteen Hegelund 
sparx5_netdevice_port_event(struct net_device * dev,struct notifier_block * nb,unsigned long event,void * ptr)264d6fce514SSteen Hegelund static int sparx5_netdevice_port_event(struct net_device *dev,
265d6fce514SSteen Hegelund 				       struct notifier_block *nb,
266d6fce514SSteen Hegelund 				       unsigned long event, void *ptr)
267d6fce514SSteen Hegelund {
268d6fce514SSteen Hegelund 	int err = 0;
269d6fce514SSteen Hegelund 
270d6fce514SSteen Hegelund 	if (!sparx5_netdevice_check(dev))
271d6fce514SSteen Hegelund 		return 0;
272d6fce514SSteen Hegelund 
273d6fce514SSteen Hegelund 	switch (event) {
274d6fce514SSteen Hegelund 	case NETDEV_CHANGEUPPER:
275d6fce514SSteen Hegelund 		err = sparx5_port_changeupper(dev, ptr);
276d6fce514SSteen Hegelund 		break;
277d6fce514SSteen Hegelund 	case NETDEV_PRE_UP:
278d6fce514SSteen Hegelund 		err = sparx5_port_add_addr(dev, true);
279d6fce514SSteen Hegelund 		break;
280d6fce514SSteen Hegelund 	case NETDEV_DOWN:
281d6fce514SSteen Hegelund 		err = sparx5_port_add_addr(dev, false);
282d6fce514SSteen Hegelund 		break;
283d6fce514SSteen Hegelund 	}
284d6fce514SSteen Hegelund 
285d6fce514SSteen Hegelund 	return err;
286d6fce514SSteen Hegelund }
287d6fce514SSteen Hegelund 
sparx5_netdevice_event(struct notifier_block * nb,unsigned long event,void * ptr)288d6fce514SSteen Hegelund static int sparx5_netdevice_event(struct notifier_block *nb,
289d6fce514SSteen Hegelund 				  unsigned long event, void *ptr)
290d6fce514SSteen Hegelund {
291d6fce514SSteen Hegelund 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
292d6fce514SSteen Hegelund 	int ret = 0;
293d6fce514SSteen Hegelund 
294d6fce514SSteen Hegelund 	ret = sparx5_netdevice_port_event(dev, nb, event, ptr);
295d6fce514SSteen Hegelund 
296d6fce514SSteen Hegelund 	return notifier_from_errno(ret);
297d6fce514SSteen Hegelund }
298d6fce514SSteen Hegelund 
sparx5_switchdev_bridge_fdb_event_work(struct work_struct * work)299d6fce514SSteen Hegelund static void sparx5_switchdev_bridge_fdb_event_work(struct work_struct *work)
300d6fce514SSteen Hegelund {
301d6fce514SSteen Hegelund 	struct sparx5_switchdev_event_work *switchdev_work =
302d6fce514SSteen Hegelund 		container_of(work, struct sparx5_switchdev_event_work, work);
303d6fce514SSteen Hegelund 	struct net_device *dev = switchdev_work->dev;
304d6fce514SSteen Hegelund 	struct switchdev_notifier_fdb_info *fdb_info;
305d6fce514SSteen Hegelund 	struct sparx5_port *port;
306d6fce514SSteen Hegelund 	struct sparx5 *sparx5;
3079f01cfbfSCasper Andersson 	bool host_addr;
308e6980b57SCasper Andersson 	u16 vid;
309d6fce514SSteen Hegelund 
310d6fce514SSteen Hegelund 	rtnl_lock();
3119f01cfbfSCasper Andersson 	if (!sparx5_netdevice_check(dev)) {
3129f01cfbfSCasper Andersson 		host_addr = true;
3139f01cfbfSCasper Andersson 		sparx5 = switchdev_work->sparx5;
3149f01cfbfSCasper Andersson 	} else {
3159f01cfbfSCasper Andersson 		host_addr = false;
3169f01cfbfSCasper Andersson 		sparx5 = switchdev_work->sparx5;
317d6fce514SSteen Hegelund 		port = netdev_priv(dev);
3189f01cfbfSCasper Andersson 	}
319d6fce514SSteen Hegelund 
320d6fce514SSteen Hegelund 	fdb_info = &switchdev_work->fdb_info;
321d6fce514SSteen Hegelund 
322e6980b57SCasper Andersson 	/* Used PVID 1 when default_pvid is 0, to avoid
323e6980b57SCasper Andersson 	 * collision with non-bridged ports.
324e6980b57SCasper Andersson 	 */
325e6980b57SCasper Andersson 	if (fdb_info->vid == 0)
326e6980b57SCasper Andersson 		vid = 1;
327e6980b57SCasper Andersson 	else
328e6980b57SCasper Andersson 		vid = fdb_info->vid;
329e6980b57SCasper Andersson 
330d6fce514SSteen Hegelund 	switch (switchdev_work->event) {
331d6fce514SSteen Hegelund 	case SWITCHDEV_FDB_ADD_TO_DEVICE:
3329f01cfbfSCasper Andersson 		if (host_addr)
3339f01cfbfSCasper Andersson 			sparx5_add_mact_entry(sparx5, dev, PGID_CPU,
334e6980b57SCasper Andersson 					      fdb_info->addr, vid);
3359f01cfbfSCasper Andersson 		else
3369f01cfbfSCasper Andersson 			sparx5_add_mact_entry(sparx5, port->ndev, port->portno,
337e6980b57SCasper Andersson 					      fdb_info->addr, vid);
338d6fce514SSteen Hegelund 		break;
339d6fce514SSteen Hegelund 	case SWITCHDEV_FDB_DEL_TO_DEVICE:
340e6980b57SCasper Andersson 		sparx5_del_mact_entry(sparx5, fdb_info->addr, vid);
341d6fce514SSteen Hegelund 		break;
342d6fce514SSteen Hegelund 	}
343d6fce514SSteen Hegelund 
344d6fce514SSteen Hegelund 	rtnl_unlock();
345d6fce514SSteen Hegelund 	kfree(switchdev_work->fdb_info.addr);
346d6fce514SSteen Hegelund 	kfree(switchdev_work);
347d6fce514SSteen Hegelund 	dev_put(dev);
348d6fce514SSteen Hegelund }
349d6fce514SSteen Hegelund 
sparx5_schedule_work(struct work_struct * work)350d6fce514SSteen Hegelund static void sparx5_schedule_work(struct work_struct *work)
351d6fce514SSteen Hegelund {
352d6fce514SSteen Hegelund 	queue_work(sparx5_owq, work);
353d6fce514SSteen Hegelund }
354d6fce514SSteen Hegelund 
sparx5_switchdev_event(struct notifier_block * nb,unsigned long event,void * ptr)3559f01cfbfSCasper Andersson static int sparx5_switchdev_event(struct notifier_block *nb,
356d6fce514SSteen Hegelund 				  unsigned long event, void *ptr)
357d6fce514SSteen Hegelund {
358d6fce514SSteen Hegelund 	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
359d6fce514SSteen Hegelund 	struct sparx5_switchdev_event_work *switchdev_work;
360d6fce514SSteen Hegelund 	struct switchdev_notifier_fdb_info *fdb_info;
361d6fce514SSteen Hegelund 	struct switchdev_notifier_info *info = ptr;
3629f01cfbfSCasper Andersson 	struct sparx5 *spx5;
363d6fce514SSteen Hegelund 	int err;
364d6fce514SSteen Hegelund 
3659f01cfbfSCasper Andersson 	spx5 = container_of(nb, struct sparx5, switchdev_nb);
3669f01cfbfSCasper Andersson 
367d6fce514SSteen Hegelund 	switch (event) {
368d6fce514SSteen Hegelund 	case SWITCHDEV_PORT_ATTR_SET:
369d6fce514SSteen Hegelund 		err = switchdev_handle_port_attr_set(dev, ptr,
370d6fce514SSteen Hegelund 						     sparx5_netdevice_check,
371d6fce514SSteen Hegelund 						     sparx5_port_attr_set);
372d6fce514SSteen Hegelund 		return notifier_from_errno(err);
373d6fce514SSteen Hegelund 	case SWITCHDEV_FDB_ADD_TO_DEVICE:
374d6fce514SSteen Hegelund 		fallthrough;
375d6fce514SSteen Hegelund 	case SWITCHDEV_FDB_DEL_TO_DEVICE:
376d6fce514SSteen Hegelund 		switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
377d6fce514SSteen Hegelund 		if (!switchdev_work)
378d6fce514SSteen Hegelund 			return NOTIFY_BAD;
379d6fce514SSteen Hegelund 
380d6fce514SSteen Hegelund 		switchdev_work->dev = dev;
381d6fce514SSteen Hegelund 		switchdev_work->event = event;
3829f01cfbfSCasper Andersson 		switchdev_work->sparx5 = spx5;
383d6fce514SSteen Hegelund 
384d6fce514SSteen Hegelund 		fdb_info = container_of(info,
385d6fce514SSteen Hegelund 					struct switchdev_notifier_fdb_info,
386d6fce514SSteen Hegelund 					info);
387d6fce514SSteen Hegelund 		INIT_WORK(&switchdev_work->work,
388d6fce514SSteen Hegelund 			  sparx5_switchdev_bridge_fdb_event_work);
389d6fce514SSteen Hegelund 		memcpy(&switchdev_work->fdb_info, ptr,
390d6fce514SSteen Hegelund 		       sizeof(switchdev_work->fdb_info));
391d6fce514SSteen Hegelund 		switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
392d6fce514SSteen Hegelund 		if (!switchdev_work->fdb_info.addr)
393d6fce514SSteen Hegelund 			goto err_addr_alloc;
394d6fce514SSteen Hegelund 
395d6fce514SSteen Hegelund 		ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
396d6fce514SSteen Hegelund 				fdb_info->addr);
397d6fce514SSteen Hegelund 		dev_hold(dev);
398d6fce514SSteen Hegelund 
399d6fce514SSteen Hegelund 		sparx5_schedule_work(&switchdev_work->work);
400d6fce514SSteen Hegelund 		break;
401d6fce514SSteen Hegelund 	}
402d6fce514SSteen Hegelund 
403d6fce514SSteen Hegelund 	return NOTIFY_DONE;
404d6fce514SSteen Hegelund err_addr_alloc:
405d6fce514SSteen Hegelund 	kfree(switchdev_work);
406d6fce514SSteen Hegelund 	return NOTIFY_BAD;
407d6fce514SSteen Hegelund }
408d6fce514SSteen Hegelund 
sparx5_handle_port_vlan_add(struct net_device * dev,struct notifier_block * nb,const struct switchdev_obj_port_vlan * v)409d6fce514SSteen Hegelund static int sparx5_handle_port_vlan_add(struct net_device *dev,
410d6fce514SSteen Hegelund 				       struct notifier_block *nb,
411d6fce514SSteen Hegelund 				       const struct switchdev_obj_port_vlan *v)
412d6fce514SSteen Hegelund {
413d6fce514SSteen Hegelund 	struct sparx5_port *port = netdev_priv(dev);
414d6fce514SSteen Hegelund 
415d6fce514SSteen Hegelund 	if (netif_is_bridge_master(dev)) {
416d6fce514SSteen Hegelund 		struct sparx5 *sparx5 =
417d6fce514SSteen Hegelund 			container_of(nb, struct sparx5,
418d6fce514SSteen Hegelund 				     switchdev_blocking_nb);
419d6fce514SSteen Hegelund 
4209f01cfbfSCasper Andersson 		/* Flood broadcast to CPU */
4219f01cfbfSCasper Andersson 		sparx5_mact_learn(sparx5, PGID_BCAST, dev->broadcast,
4229f01cfbfSCasper Andersson 				  v->vid);
423d6fce514SSteen Hegelund 		return 0;
424d6fce514SSteen Hegelund 	}
425d6fce514SSteen Hegelund 
426d6fce514SSteen Hegelund 	if (!sparx5_netdevice_check(dev))
427d6fce514SSteen Hegelund 		return -EOPNOTSUPP;
428d6fce514SSteen Hegelund 
429d6fce514SSteen Hegelund 	return sparx5_vlan_vid_add(port, v->vid,
430d6fce514SSteen Hegelund 				  v->flags & BRIDGE_VLAN_INFO_PVID,
431d6fce514SSteen Hegelund 				  v->flags & BRIDGE_VLAN_INFO_UNTAGGED);
432d6fce514SSteen Hegelund }
433d6fce514SSteen Hegelund 
sparx5_alloc_mdb_entry(struct sparx5 * sparx5,const unsigned char * addr,u16 vid,struct sparx5_mdb_entry ** entry_out)434c8a3ea43SCasper Andersson static int sparx5_alloc_mdb_entry(struct sparx5 *sparx5,
435c8a3ea43SCasper Andersson 				  const unsigned char *addr,
436c8a3ea43SCasper Andersson 				  u16 vid,
437c8a3ea43SCasper Andersson 				  struct sparx5_mdb_entry **entry_out)
438c8a3ea43SCasper Andersson {
439c8a3ea43SCasper Andersson 	struct sparx5_mdb_entry *entry;
440c8a3ea43SCasper Andersson 	u16 pgid_idx;
441c8a3ea43SCasper Andersson 	int err;
442c8a3ea43SCasper Andersson 
443c8a3ea43SCasper Andersson 	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
444c8a3ea43SCasper Andersson 	if (!entry)
445c8a3ea43SCasper Andersson 		return -ENOMEM;
446c8a3ea43SCasper Andersson 
447c8a3ea43SCasper Andersson 	err = sparx5_pgid_alloc_mcast(sparx5, &pgid_idx);
448c8a3ea43SCasper Andersson 	if (err) {
449c8a3ea43SCasper Andersson 		kfree(entry);
450c8a3ea43SCasper Andersson 		return err;
451c8a3ea43SCasper Andersson 	}
452c8a3ea43SCasper Andersson 
453c8a3ea43SCasper Andersson 	memcpy(entry->addr, addr, ETH_ALEN);
454c8a3ea43SCasper Andersson 	entry->vid = vid;
455c8a3ea43SCasper Andersson 	entry->pgid_idx = pgid_idx;
456c8a3ea43SCasper Andersson 
457c8a3ea43SCasper Andersson 	mutex_lock(&sparx5->mdb_lock);
458c8a3ea43SCasper Andersson 	list_add_tail(&entry->list, &sparx5->mdb_entries);
459c8a3ea43SCasper Andersson 	mutex_unlock(&sparx5->mdb_lock);
460c8a3ea43SCasper Andersson 
461c8a3ea43SCasper Andersson 	*entry_out = entry;
462c8a3ea43SCasper Andersson 	return 0;
463c8a3ea43SCasper Andersson }
464c8a3ea43SCasper Andersson 
sparx5_free_mdb_entry(struct sparx5 * sparx5,const unsigned char * addr,u16 vid)465c8a3ea43SCasper Andersson static void sparx5_free_mdb_entry(struct sparx5 *sparx5,
466c8a3ea43SCasper Andersson 				  const unsigned char *addr,
467c8a3ea43SCasper Andersson 				  u16 vid)
468c8a3ea43SCasper Andersson {
469c8a3ea43SCasper Andersson 	struct sparx5_mdb_entry *entry, *tmp;
470c8a3ea43SCasper Andersson 
471c8a3ea43SCasper Andersson 	mutex_lock(&sparx5->mdb_lock);
472c8a3ea43SCasper Andersson 	list_for_each_entry_safe(entry, tmp, &sparx5->mdb_entries, list) {
473c8a3ea43SCasper Andersson 		if ((vid == 0 || entry->vid == vid) &&
474c8a3ea43SCasper Andersson 		    ether_addr_equal(addr, entry->addr)) {
475c8a3ea43SCasper Andersson 			list_del(&entry->list);
476c8a3ea43SCasper Andersson 
477c8a3ea43SCasper Andersson 			sparx5_pgid_free(sparx5, entry->pgid_idx);
478c8a3ea43SCasper Andersson 			kfree(entry);
479c8a3ea43SCasper Andersson 			goto out;
480c8a3ea43SCasper Andersson 		}
481c8a3ea43SCasper Andersson 	}
482c8a3ea43SCasper Andersson 
483c8a3ea43SCasper Andersson out:
484c8a3ea43SCasper Andersson 	mutex_unlock(&sparx5->mdb_lock);
485c8a3ea43SCasper Andersson }
486c8a3ea43SCasper Andersson 
sparx5_mdb_get_entry(struct sparx5 * sparx5,const unsigned char * addr,u16 vid)487c8a3ea43SCasper Andersson static struct sparx5_mdb_entry *sparx5_mdb_get_entry(struct sparx5 *sparx5,
488c8a3ea43SCasper Andersson 						     const unsigned char *addr,
489c8a3ea43SCasper Andersson 						     u16 vid)
490c8a3ea43SCasper Andersson {
491c8a3ea43SCasper Andersson 	struct sparx5_mdb_entry *e, *found = NULL;
492c8a3ea43SCasper Andersson 
493c8a3ea43SCasper Andersson 	mutex_lock(&sparx5->mdb_lock);
494c8a3ea43SCasper Andersson 	list_for_each_entry(e, &sparx5->mdb_entries, list) {
495c8a3ea43SCasper Andersson 		if (ether_addr_equal(e->addr, addr) && e->vid == vid) {
496c8a3ea43SCasper Andersson 			found = e;
497c8a3ea43SCasper Andersson 			goto out;
498c8a3ea43SCasper Andersson 		}
499c8a3ea43SCasper Andersson 	}
500c8a3ea43SCasper Andersson 
501c8a3ea43SCasper Andersson out:
502c8a3ea43SCasper Andersson 	mutex_unlock(&sparx5->mdb_lock);
503c8a3ea43SCasper Andersson 	return found;
504c8a3ea43SCasper Andersson }
505c8a3ea43SCasper Andersson 
sparx5_cpu_copy_ena(struct sparx5 * spx5,u16 pgid,bool enable)506c8a3ea43SCasper Andersson static void sparx5_cpu_copy_ena(struct sparx5 *spx5, u16 pgid, bool enable)
507c8a3ea43SCasper Andersson {
508c8a3ea43SCasper Andersson 	spx5_rmw(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_SET(enable),
509c8a3ea43SCasper Andersson 		 ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA, spx5,
510c8a3ea43SCasper Andersson 		 ANA_AC_PGID_MISC_CFG(pgid));
511c8a3ea43SCasper Andersson }
512c8a3ea43SCasper Andersson 
sparx5_handle_port_mdb_add(struct net_device * dev,struct notifier_block * nb,const struct switchdev_obj_port_mdb * v)5133bacfccdSCasper Andersson static int sparx5_handle_port_mdb_add(struct net_device *dev,
5143bacfccdSCasper Andersson 				      struct notifier_block *nb,
5153bacfccdSCasper Andersson 				      const struct switchdev_obj_port_mdb *v)
5163bacfccdSCasper Andersson {
5173bacfccdSCasper Andersson 	struct sparx5_port *port = netdev_priv(dev);
5183bacfccdSCasper Andersson 	struct sparx5 *spx5 = port->sparx5;
519c8a3ea43SCasper Andersson 	struct sparx5_mdb_entry *entry;
520*04e551d6SCasper Andersson 	bool is_host, is_new;
521*04e551d6SCasper Andersson 	int err, i;
522c8a3ea43SCasper Andersson 	u16 vid;
5233bacfccdSCasper Andersson 
5249c5de246SCasper Andersson 	if (!sparx5_netdevice_check(dev))
5259c5de246SCasper Andersson 		return -EOPNOTSUPP;
5269c5de246SCasper Andersson 
527fbb89d02SCasper Andersson 	is_host = netif_is_bridge_master(v->obj.orig_dev);
5281c1ed5a4SCasper Andersson 
5293bacfccdSCasper Andersson 	/* When VLAN unaware the vlan value is not parsed and we receive vid 0.
5303bacfccdSCasper Andersson 	 * Fall back to bridge vid 1.
5313bacfccdSCasper Andersson 	 */
5323bacfccdSCasper Andersson 	if (!br_vlan_enabled(spx5->hw_bridge_dev))
5333bacfccdSCasper Andersson 		vid = 1;
5343bacfccdSCasper Andersson 	else
5353bacfccdSCasper Andersson 		vid = v->vid;
5363bacfccdSCasper Andersson 
537*04e551d6SCasper Andersson 	is_new = false;
538c8a3ea43SCasper Andersson 	entry = sparx5_mdb_get_entry(spx5, v->addr, vid);
539c8a3ea43SCasper Andersson 	if (!entry) {
540c8a3ea43SCasper Andersson 		err = sparx5_alloc_mdb_entry(spx5, v->addr, vid, &entry);
541*04e551d6SCasper Andersson 		is_new = true;
542c8a3ea43SCasper Andersson 		if (err)
5433bacfccdSCasper Andersson 			return err;
5443bacfccdSCasper Andersson 	}
545fbb89d02SCasper Andersson 
546c8a3ea43SCasper Andersson 	mutex_lock(&spx5->mdb_lock);
547*04e551d6SCasper Andersson 
548*04e551d6SCasper Andersson 	/* Add any mrouter ports to the new entry */
549*04e551d6SCasper Andersson 	if (is_new && ether_addr_is_ip_mcast(v->addr))
550*04e551d6SCasper Andersson 		for (i = 0; i < SPX5_PORTS; i++)
551*04e551d6SCasper Andersson 			if (spx5->ports[i] && spx5->ports[i]->is_mrouter)
552*04e551d6SCasper Andersson 				sparx5_pgid_update_mask(spx5->ports[i],
553*04e551d6SCasper Andersson 							entry->pgid_idx,
554*04e551d6SCasper Andersson 							true);
555*04e551d6SCasper Andersson 
556c8a3ea43SCasper Andersson 	if (is_host && !entry->cpu_copy) {
557c8a3ea43SCasper Andersson 		sparx5_cpu_copy_ena(spx5, entry->pgid_idx, true);
558c8a3ea43SCasper Andersson 		entry->cpu_copy = true;
559c8a3ea43SCasper Andersson 	} else if (!is_host) {
560c8a3ea43SCasper Andersson 		sparx5_pgid_update_mask(port, entry->pgid_idx, true);
561c8a3ea43SCasper Andersson 		set_bit(port->portno, entry->port_mask);
5623bacfccdSCasper Andersson 	}
563c8a3ea43SCasper Andersson 	mutex_unlock(&spx5->mdb_lock);
5643bacfccdSCasper Andersson 
565c8a3ea43SCasper Andersson 	sparx5_mact_learn(spx5, entry->pgid_idx, entry->addr, entry->vid);
5663bacfccdSCasper Andersson 
5673bacfccdSCasper Andersson 	return 0;
5683bacfccdSCasper Andersson }
5693bacfccdSCasper Andersson 
sparx5_handle_port_mdb_del(struct net_device * dev,struct notifier_block * nb,const struct switchdev_obj_port_mdb * v)5703bacfccdSCasper Andersson static int sparx5_handle_port_mdb_del(struct net_device *dev,
5713bacfccdSCasper Andersson 				      struct notifier_block *nb,
5723bacfccdSCasper Andersson 				      const struct switchdev_obj_port_mdb *v)
5733bacfccdSCasper Andersson {
5743bacfccdSCasper Andersson 	struct sparx5_port *port = netdev_priv(dev);
5753bacfccdSCasper Andersson 	struct sparx5 *spx5 = port->sparx5;
576c8a3ea43SCasper Andersson 	struct sparx5_mdb_entry *entry;
577c8a3ea43SCasper Andersson 	bool is_host;
578c8a3ea43SCasper Andersson 	u16 vid;
5791c1ed5a4SCasper Andersson 
5809c5de246SCasper Andersson 	if (!sparx5_netdevice_check(dev))
5819c5de246SCasper Andersson 		return -EOPNOTSUPP;
5829c5de246SCasper Andersson 
583c8a3ea43SCasper Andersson 	is_host = netif_is_bridge_master(v->obj.orig_dev);
584c8a3ea43SCasper Andersson 
5853bacfccdSCasper Andersson 	if (!br_vlan_enabled(spx5->hw_bridge_dev))
5863bacfccdSCasper Andersson 		vid = 1;
5873bacfccdSCasper Andersson 	else
5883bacfccdSCasper Andersson 		vid = v->vid;
5893bacfccdSCasper Andersson 
590c8a3ea43SCasper Andersson 	entry = sparx5_mdb_get_entry(spx5, v->addr, vid);
591c8a3ea43SCasper Andersson 	if (!entry)
592c8a3ea43SCasper Andersson 		return 0;
5933bacfccdSCasper Andersson 
594c8a3ea43SCasper Andersson 	mutex_lock(&spx5->mdb_lock);
595c8a3ea43SCasper Andersson 	if (is_host && entry->cpu_copy) {
596c8a3ea43SCasper Andersson 		sparx5_cpu_copy_ena(spx5, entry->pgid_idx, false);
597c8a3ea43SCasper Andersson 		entry->cpu_copy = false;
598c8a3ea43SCasper Andersson 	} else if (!is_host) {
599c8a3ea43SCasper Andersson 		clear_bit(port->portno, entry->port_mask);
600*04e551d6SCasper Andersson 
601*04e551d6SCasper Andersson 		/* Port not mrouter port or addr is L2 mcast, remove port from mask. */
602*04e551d6SCasper Andersson 		if (!port->is_mrouter || !ether_addr_is_ip_mcast(v->addr))
603c8a3ea43SCasper Andersson 			sparx5_pgid_update_mask(port, entry->pgid_idx, false);
6043bacfccdSCasper Andersson 	}
605c8a3ea43SCasper Andersson 	mutex_unlock(&spx5->mdb_lock);
6063bacfccdSCasper Andersson 
607c8a3ea43SCasper Andersson 	if (bitmap_empty(entry->port_mask, SPX5_PORTS) && !entry->cpu_copy) {
608*04e551d6SCasper Andersson 		 /* Clear pgid in case mrouter ports exists
609*04e551d6SCasper Andersson 		  * that are not part of the group.
610*04e551d6SCasper Andersson 		  */
611*04e551d6SCasper Andersson 		sparx5_pgid_clear(spx5, entry->pgid_idx);
612c8a3ea43SCasper Andersson 		sparx5_mact_forget(spx5, entry->addr, entry->vid);
613c8a3ea43SCasper Andersson 		sparx5_free_mdb_entry(spx5, entry->addr, entry->vid);
614c8a3ea43SCasper Andersson 	}
6153bacfccdSCasper Andersson 	return 0;
6163bacfccdSCasper Andersson }
6173bacfccdSCasper Andersson 
sparx5_handle_port_obj_add(struct net_device * dev,struct notifier_block * nb,struct switchdev_notifier_port_obj_info * info)618d6fce514SSteen Hegelund static int sparx5_handle_port_obj_add(struct net_device *dev,
619d6fce514SSteen Hegelund 				      struct notifier_block *nb,
620d6fce514SSteen Hegelund 				      struct switchdev_notifier_port_obj_info *info)
621d6fce514SSteen Hegelund {
622d6fce514SSteen Hegelund 	const struct switchdev_obj *obj = info->obj;
623d6fce514SSteen Hegelund 	int err;
624d6fce514SSteen Hegelund 
625d6fce514SSteen Hegelund 	switch (obj->id) {
626d6fce514SSteen Hegelund 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
627d6fce514SSteen Hegelund 		err = sparx5_handle_port_vlan_add(dev, nb,
628d6fce514SSteen Hegelund 						  SWITCHDEV_OBJ_PORT_VLAN(obj));
629d6fce514SSteen Hegelund 		break;
6303bacfccdSCasper Andersson 	case SWITCHDEV_OBJ_ID_PORT_MDB:
6311c1ed5a4SCasper Andersson 	case SWITCHDEV_OBJ_ID_HOST_MDB:
6323bacfccdSCasper Andersson 		err = sparx5_handle_port_mdb_add(dev, nb,
6333bacfccdSCasper Andersson 						 SWITCHDEV_OBJ_PORT_MDB(obj));
6343bacfccdSCasper Andersson 		break;
635d6fce514SSteen Hegelund 	default:
636d6fce514SSteen Hegelund 		err = -EOPNOTSUPP;
637d6fce514SSteen Hegelund 		break;
638d6fce514SSteen Hegelund 	}
639d6fce514SSteen Hegelund 
640d6fce514SSteen Hegelund 	info->handled = true;
641d6fce514SSteen Hegelund 	return err;
642d6fce514SSteen Hegelund }
643d6fce514SSteen Hegelund 
sparx5_handle_port_vlan_del(struct net_device * dev,struct notifier_block * nb,u16 vid)644d6fce514SSteen Hegelund static int sparx5_handle_port_vlan_del(struct net_device *dev,
645d6fce514SSteen Hegelund 				       struct notifier_block *nb,
646d6fce514SSteen Hegelund 				       u16 vid)
647d6fce514SSteen Hegelund {
648d6fce514SSteen Hegelund 	struct sparx5_port *port = netdev_priv(dev);
649d6fce514SSteen Hegelund 	int ret;
650d6fce514SSteen Hegelund 
651d6fce514SSteen Hegelund 	/* Master bridge? */
652d6fce514SSteen Hegelund 	if (netif_is_bridge_master(dev)) {
653d6fce514SSteen Hegelund 		struct sparx5 *sparx5 =
654d6fce514SSteen Hegelund 			container_of(nb, struct sparx5,
655d6fce514SSteen Hegelund 				     switchdev_blocking_nb);
656d6fce514SSteen Hegelund 
6579f01cfbfSCasper Andersson 		sparx5_mact_forget(sparx5, dev->broadcast, vid);
658d6fce514SSteen Hegelund 		return 0;
659d6fce514SSteen Hegelund 	}
660d6fce514SSteen Hegelund 
661d6fce514SSteen Hegelund 	if (!sparx5_netdevice_check(dev))
662d6fce514SSteen Hegelund 		return -EOPNOTSUPP;
663d6fce514SSteen Hegelund 
664d6fce514SSteen Hegelund 	ret = sparx5_vlan_vid_del(port, vid);
665d6fce514SSteen Hegelund 	if (ret)
666d6fce514SSteen Hegelund 		return ret;
667d6fce514SSteen Hegelund 
668d6fce514SSteen Hegelund 	return 0;
669d6fce514SSteen Hegelund }
670d6fce514SSteen Hegelund 
sparx5_handle_port_obj_del(struct net_device * dev,struct notifier_block * nb,struct switchdev_notifier_port_obj_info * info)671d6fce514SSteen Hegelund static int sparx5_handle_port_obj_del(struct net_device *dev,
672d6fce514SSteen Hegelund 				      struct notifier_block *nb,
673d6fce514SSteen Hegelund 				      struct switchdev_notifier_port_obj_info *info)
674d6fce514SSteen Hegelund {
675d6fce514SSteen Hegelund 	const struct switchdev_obj *obj = info->obj;
676d6fce514SSteen Hegelund 	int err;
677d6fce514SSteen Hegelund 
678d6fce514SSteen Hegelund 	switch (obj->id) {
679d6fce514SSteen Hegelund 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
680d6fce514SSteen Hegelund 		err = sparx5_handle_port_vlan_del(dev, nb,
681d6fce514SSteen Hegelund 						  SWITCHDEV_OBJ_PORT_VLAN(obj)->vid);
682d6fce514SSteen Hegelund 		break;
6833bacfccdSCasper Andersson 	case SWITCHDEV_OBJ_ID_PORT_MDB:
6841c1ed5a4SCasper Andersson 	case SWITCHDEV_OBJ_ID_HOST_MDB:
6853bacfccdSCasper Andersson 		err = sparx5_handle_port_mdb_del(dev, nb,
6863bacfccdSCasper Andersson 						 SWITCHDEV_OBJ_PORT_MDB(obj));
6873bacfccdSCasper Andersson 		break;
688d6fce514SSteen Hegelund 	default:
689d6fce514SSteen Hegelund 		err = -EOPNOTSUPP;
690d6fce514SSteen Hegelund 		break;
691d6fce514SSteen Hegelund 	}
692d6fce514SSteen Hegelund 
693d6fce514SSteen Hegelund 	info->handled = true;
694d6fce514SSteen Hegelund 	return err;
695d6fce514SSteen Hegelund }
696d6fce514SSteen Hegelund 
sparx5_switchdev_blocking_event(struct notifier_block * nb,unsigned long event,void * ptr)697d6fce514SSteen Hegelund static int sparx5_switchdev_blocking_event(struct notifier_block *nb,
698d6fce514SSteen Hegelund 					   unsigned long event,
699d6fce514SSteen Hegelund 					   void *ptr)
700d6fce514SSteen Hegelund {
701d6fce514SSteen Hegelund 	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
702d6fce514SSteen Hegelund 	int err;
703d6fce514SSteen Hegelund 
704d6fce514SSteen Hegelund 	switch (event) {
705d6fce514SSteen Hegelund 	case SWITCHDEV_PORT_OBJ_ADD:
706d6fce514SSteen Hegelund 		err = sparx5_handle_port_obj_add(dev, nb, ptr);
707d6fce514SSteen Hegelund 		return notifier_from_errno(err);
708d6fce514SSteen Hegelund 	case SWITCHDEV_PORT_OBJ_DEL:
709d6fce514SSteen Hegelund 		err = sparx5_handle_port_obj_del(dev, nb, ptr);
710d6fce514SSteen Hegelund 		return notifier_from_errno(err);
711d6fce514SSteen Hegelund 	case SWITCHDEV_PORT_ATTR_SET:
712d6fce514SSteen Hegelund 		err = switchdev_handle_port_attr_set(dev, ptr,
713d6fce514SSteen Hegelund 						     sparx5_netdevice_check,
714d6fce514SSteen Hegelund 						     sparx5_port_attr_set);
715d6fce514SSteen Hegelund 		return notifier_from_errno(err);
716d6fce514SSteen Hegelund 	}
717d6fce514SSteen Hegelund 
718d6fce514SSteen Hegelund 	return NOTIFY_DONE;
719d6fce514SSteen Hegelund }
720d6fce514SSteen Hegelund 
sparx5_register_notifier_blocks(struct sparx5 * s5)721d6fce514SSteen Hegelund int sparx5_register_notifier_blocks(struct sparx5 *s5)
722d6fce514SSteen Hegelund {
723d6fce514SSteen Hegelund 	int err;
724d6fce514SSteen Hegelund 
725d6fce514SSteen Hegelund 	s5->netdevice_nb.notifier_call = sparx5_netdevice_event;
726d6fce514SSteen Hegelund 	err = register_netdevice_notifier(&s5->netdevice_nb);
727d6fce514SSteen Hegelund 	if (err)
728d6fce514SSteen Hegelund 		return err;
729d6fce514SSteen Hegelund 
730d6fce514SSteen Hegelund 	s5->switchdev_nb.notifier_call = sparx5_switchdev_event;
731d6fce514SSteen Hegelund 	err = register_switchdev_notifier(&s5->switchdev_nb);
732d6fce514SSteen Hegelund 	if (err)
733d6fce514SSteen Hegelund 		goto err_switchdev_nb;
734d6fce514SSteen Hegelund 
735d6fce514SSteen Hegelund 	s5->switchdev_blocking_nb.notifier_call = sparx5_switchdev_blocking_event;
736d6fce514SSteen Hegelund 	err = register_switchdev_blocking_notifier(&s5->switchdev_blocking_nb);
737d6fce514SSteen Hegelund 	if (err)
738d6fce514SSteen Hegelund 		goto err_switchdev_blocking_nb;
739d6fce514SSteen Hegelund 
740d6fce514SSteen Hegelund 	sparx5_owq = alloc_ordered_workqueue("sparx5_order", 0);
74183300c69SYang Yingliang 	if (!sparx5_owq) {
74283300c69SYang Yingliang 		err = -ENOMEM;
743d6fce514SSteen Hegelund 		goto err_switchdev_blocking_nb;
74483300c69SYang Yingliang 	}
745d6fce514SSteen Hegelund 
746d6fce514SSteen Hegelund 	return 0;
747d6fce514SSteen Hegelund 
748d6fce514SSteen Hegelund err_switchdev_blocking_nb:
749d6fce514SSteen Hegelund 	unregister_switchdev_notifier(&s5->switchdev_nb);
750d6fce514SSteen Hegelund err_switchdev_nb:
751d6fce514SSteen Hegelund 	unregister_netdevice_notifier(&s5->netdevice_nb);
752d6fce514SSteen Hegelund 
753d6fce514SSteen Hegelund 	return err;
754d6fce514SSteen Hegelund }
755d6fce514SSteen Hegelund 
sparx5_unregister_notifier_blocks(struct sparx5 * s5)756d6fce514SSteen Hegelund void sparx5_unregister_notifier_blocks(struct sparx5 *s5)
757d6fce514SSteen Hegelund {
758d6fce514SSteen Hegelund 	destroy_workqueue(sparx5_owq);
759d6fce514SSteen Hegelund 
760d6fce514SSteen Hegelund 	unregister_switchdev_blocking_notifier(&s5->switchdev_blocking_nb);
761d6fce514SSteen Hegelund 	unregister_switchdev_notifier(&s5->switchdev_nb);
762d6fce514SSteen Hegelund 	unregister_netdevice_notifier(&s5->netdevice_nb);
763d6fce514SSteen Hegelund }
764