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