xref: /linux/drivers/net/ethernet/ti/icssg/icssg_switchdev.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 /* Texas Instruments K3 ICSSG Ethernet Switchdev Driver
4  *
5  * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/
6  *
7  */
8 
9 #include <linux/etherdevice.h>
10 #include <linux/if_bridge.h>
11 #include <linux/netdevice.h>
12 #include <linux/workqueue.h>
13 #include <net/switchdev.h>
14 
15 #include "icssg_prueth.h"
16 #include "icssg_switchdev.h"
17 #include "icssg_mii_rt.h"
18 
19 struct prueth_switchdev_event_work {
20 	struct work_struct work;
21 	struct switchdev_notifier_fdb_info fdb_info;
22 	struct prueth_emac *emac;
23 	unsigned long event;
24 };
25 
prueth_switchdev_stp_state_set(struct prueth_emac * emac,u8 state)26 static int prueth_switchdev_stp_state_set(struct prueth_emac *emac,
27 					  u8 state)
28 {
29 	enum icssg_port_state_cmd emac_state;
30 	int ret = 0;
31 
32 	switch (state) {
33 	case BR_STATE_FORWARDING:
34 		emac_state = ICSSG_EMAC_PORT_FORWARD;
35 		break;
36 	case BR_STATE_DISABLED:
37 		emac_state = ICSSG_EMAC_PORT_DISABLE;
38 		break;
39 	case BR_STATE_LISTENING:
40 	case BR_STATE_BLOCKING:
41 		emac_state = ICSSG_EMAC_PORT_BLOCK;
42 		break;
43 	default:
44 		return -EOPNOTSUPP;
45 	}
46 
47 	icssg_set_port_state(emac, emac_state);
48 	netdev_dbg(emac->ndev, "STP state: %u\n", emac_state);
49 
50 	return ret;
51 }
52 
prueth_switchdev_attr_br_flags_set(struct prueth_emac * emac,struct net_device * orig_dev,struct switchdev_brport_flags brport_flags)53 static int prueth_switchdev_attr_br_flags_set(struct prueth_emac *emac,
54 					      struct net_device *orig_dev,
55 					      struct switchdev_brport_flags brport_flags)
56 {
57 	enum icssg_port_state_cmd emac_state;
58 
59 	if (brport_flags.mask & BR_MCAST_FLOOD)
60 		emac_state = ICSSG_EMAC_PORT_MC_FLOODING_ENABLE;
61 	else
62 		emac_state = ICSSG_EMAC_PORT_MC_FLOODING_DISABLE;
63 
64 	netdev_dbg(emac->ndev, "BR_MCAST_FLOOD: %d port %u\n",
65 		   emac_state, emac->port_id);
66 
67 	icssg_set_port_state(emac, emac_state);
68 
69 	return 0;
70 }
71 
prueth_switchdev_attr_br_flags_pre_set(struct net_device * netdev,struct switchdev_brport_flags brport_flags)72 static int prueth_switchdev_attr_br_flags_pre_set(struct net_device *netdev,
73 						  struct switchdev_brport_flags brport_flags)
74 {
75 	if (brport_flags.mask & ~(BR_LEARNING | BR_MCAST_FLOOD))
76 		return -EINVAL;
77 
78 	return 0;
79 }
80 
prueth_switchdev_attr_set(struct net_device * ndev,const void * ctx,const struct switchdev_attr * attr,struct netlink_ext_ack * extack)81 static int prueth_switchdev_attr_set(struct net_device *ndev, const void *ctx,
82 				     const struct switchdev_attr *attr,
83 				     struct netlink_ext_ack *extack)
84 {
85 	struct prueth_emac *emac = netdev_priv(ndev);
86 	int ret;
87 
88 	netdev_dbg(ndev, "attr: id %u port: %u\n", attr->id, emac->port_id);
89 
90 	switch (attr->id) {
91 	case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
92 		ret = prueth_switchdev_attr_br_flags_pre_set(ndev,
93 							     attr->u.brport_flags);
94 		break;
95 	case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
96 		ret = prueth_switchdev_stp_state_set(emac,
97 						     attr->u.stp_state);
98 		netdev_dbg(ndev, "stp state: %u\n", attr->u.stp_state);
99 		break;
100 	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
101 		ret = prueth_switchdev_attr_br_flags_set(emac, attr->orig_dev,
102 							 attr->u.brport_flags);
103 		break;
104 	default:
105 		ret = -EOPNOTSUPP;
106 		break;
107 	}
108 
109 	return ret;
110 }
111 
prueth_switchdev_fdb_offload_notify(struct net_device * ndev,struct switchdev_notifier_fdb_info * rcv)112 static void prueth_switchdev_fdb_offload_notify(struct net_device *ndev,
113 						struct switchdev_notifier_fdb_info *rcv)
114 {
115 	struct switchdev_notifier_fdb_info info;
116 
117 	memset(&info, 0, sizeof(info));
118 	info.addr = rcv->addr;
119 	info.vid = rcv->vid;
120 	info.offloaded = true;
121 	call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED,
122 				 ndev, &info.info, NULL);
123 }
124 
prueth_switchdev_event_work(struct work_struct * work)125 static void prueth_switchdev_event_work(struct work_struct *work)
126 {
127 	struct prueth_switchdev_event_work *switchdev_work =
128 		container_of(work, struct prueth_switchdev_event_work, work);
129 	struct prueth_emac *emac = switchdev_work->emac;
130 	struct switchdev_notifier_fdb_info *fdb;
131 	int port_id = emac->port_id;
132 	int ret;
133 
134 	rtnl_lock();
135 	switch (switchdev_work->event) {
136 	case SWITCHDEV_FDB_ADD_TO_DEVICE:
137 		fdb = &switchdev_work->fdb_info;
138 
139 		netdev_dbg(emac->ndev, "prueth_fdb_add: MACID = %pM vid = %u flags = %u %u -- port %d\n",
140 			   fdb->addr, fdb->vid, fdb->added_by_user,
141 			   fdb->offloaded, port_id);
142 
143 		if (!fdb->added_by_user)
144 			break;
145 		if (!ether_addr_equal(emac->mac_addr, fdb->addr))
146 			break;
147 
148 		ret = icssg_fdb_add_del(emac, fdb->addr, fdb->vid,
149 					BIT(port_id), true);
150 		if (!ret)
151 			prueth_switchdev_fdb_offload_notify(emac->ndev, fdb);
152 		break;
153 	case SWITCHDEV_FDB_DEL_TO_DEVICE:
154 		fdb = &switchdev_work->fdb_info;
155 
156 		netdev_dbg(emac->ndev, "prueth_fdb_del: MACID = %pM vid = %u flags = %u %u -- port %d\n",
157 			   fdb->addr, fdb->vid, fdb->added_by_user,
158 			   fdb->offloaded, port_id);
159 
160 		if (!fdb->added_by_user)
161 			break;
162 		if (!ether_addr_equal(emac->mac_addr, fdb->addr))
163 			break;
164 		icssg_fdb_add_del(emac, fdb->addr, fdb->vid,
165 				  BIT(port_id), false);
166 		break;
167 	default:
168 		break;
169 	}
170 	rtnl_unlock();
171 
172 	kfree(switchdev_work->fdb_info.addr);
173 	kfree(switchdev_work);
174 	dev_put(emac->ndev);
175 }
176 
prueth_switchdev_event(struct notifier_block * unused,unsigned long event,void * ptr)177 static int prueth_switchdev_event(struct notifier_block *unused,
178 				  unsigned long event, void *ptr)
179 {
180 	struct net_device *ndev = switchdev_notifier_info_to_dev(ptr);
181 	struct prueth_switchdev_event_work *switchdev_work;
182 	struct switchdev_notifier_fdb_info *fdb_info = ptr;
183 	struct prueth_emac *emac = netdev_priv(ndev);
184 	int err;
185 
186 	if (!prueth_dev_check(ndev))
187 		return NOTIFY_DONE;
188 
189 	if (event == SWITCHDEV_PORT_ATTR_SET) {
190 		err = switchdev_handle_port_attr_set(ndev, ptr,
191 						     prueth_dev_check,
192 						     prueth_switchdev_attr_set);
193 		return notifier_from_errno(err);
194 	}
195 
196 	switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
197 	if (WARN_ON(!switchdev_work))
198 		return NOTIFY_BAD;
199 
200 	INIT_WORK(&switchdev_work->work, prueth_switchdev_event_work);
201 	switchdev_work->emac = emac;
202 	switchdev_work->event = event;
203 
204 	switch (event) {
205 	case SWITCHDEV_FDB_ADD_TO_DEVICE:
206 	case SWITCHDEV_FDB_DEL_TO_DEVICE:
207 		memcpy(&switchdev_work->fdb_info, ptr,
208 		       sizeof(switchdev_work->fdb_info));
209 		switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
210 		if (!switchdev_work->fdb_info.addr)
211 			goto err_addr_alloc;
212 		ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
213 				fdb_info->addr);
214 		dev_hold(ndev);
215 		break;
216 	default:
217 		kfree(switchdev_work);
218 		return NOTIFY_DONE;
219 	}
220 
221 	queue_work(system_long_wq, &switchdev_work->work);
222 
223 	return NOTIFY_DONE;
224 
225 err_addr_alloc:
226 	kfree(switchdev_work);
227 	return NOTIFY_BAD;
228 }
229 
prueth_switchdev_vlan_add(struct prueth_emac * emac,bool untag,bool pvid,u8 vid,struct net_device * orig_dev)230 static int prueth_switchdev_vlan_add(struct prueth_emac *emac, bool untag, bool pvid,
231 				     u8 vid, struct net_device *orig_dev)
232 {
233 	bool cpu_port = netif_is_bridge_master(orig_dev);
234 	int untag_mask = 0;
235 	int port_mask;
236 	int ret = 0;
237 
238 	if (cpu_port)
239 		port_mask = BIT(PRUETH_PORT_HOST);
240 	else
241 		port_mask = BIT(emac->port_id);
242 
243 	if (untag)
244 		untag_mask = port_mask;
245 
246 	icssg_vtbl_modify(emac, vid, port_mask, untag_mask, true);
247 
248 	netdev_dbg(emac->ndev, "VID add vid:%u port_mask:%X untag_mask %X PVID %d\n",
249 		   vid, port_mask, untag_mask, pvid);
250 
251 	if (!pvid)
252 		return ret;
253 
254 	icssg_set_pvid(emac->prueth, vid, emac->port_id);
255 
256 	return ret;
257 }
258 
prueth_switchdev_vlan_del(struct prueth_emac * emac,u16 vid,struct net_device * orig_dev)259 static int prueth_switchdev_vlan_del(struct prueth_emac *emac, u16 vid,
260 				     struct net_device *orig_dev)
261 {
262 	bool cpu_port = netif_is_bridge_master(orig_dev);
263 	int port_mask;
264 	int ret = 0;
265 
266 	if (cpu_port)
267 		port_mask = BIT(PRUETH_PORT_HOST);
268 	else
269 		port_mask = BIT(emac->port_id);
270 
271 	icssg_vtbl_modify(emac, vid, port_mask, 0, false);
272 
273 	if (cpu_port)
274 		icssg_fdb_add_del(emac, emac->mac_addr, vid,
275 				  BIT(PRUETH_PORT_HOST), false);
276 
277 	if (vid == icssg_get_pvid(emac))
278 		icssg_set_pvid(emac->prueth, 0, emac->port_id);
279 
280 	netdev_dbg(emac->ndev, "VID del vid:%u port_mask:%X\n",
281 		   vid, port_mask);
282 
283 	return ret;
284 }
285 
prueth_switchdev_vlans_add(struct prueth_emac * emac,const struct switchdev_obj_port_vlan * vlan)286 static int prueth_switchdev_vlans_add(struct prueth_emac *emac,
287 				      const struct switchdev_obj_port_vlan *vlan)
288 {
289 	bool untag = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
290 	struct net_device *orig_dev = vlan->obj.orig_dev;
291 	bool cpu_port = netif_is_bridge_master(orig_dev);
292 	bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
293 
294 	netdev_dbg(emac->ndev, "VID add vid:%u flags:%X\n",
295 		   vlan->vid, vlan->flags);
296 
297 	if (cpu_port && !(vlan->flags & BRIDGE_VLAN_INFO_BRENTRY))
298 		return 0;
299 
300 	if (vlan->vid > 0xff)
301 		return 0;
302 
303 	return prueth_switchdev_vlan_add(emac, untag, pvid, vlan->vid,
304 					 orig_dev);
305 }
306 
prueth_switchdev_vlans_del(struct prueth_emac * emac,const struct switchdev_obj_port_vlan * vlan)307 static int prueth_switchdev_vlans_del(struct prueth_emac *emac,
308 				      const struct switchdev_obj_port_vlan *vlan)
309 {
310 	if (vlan->vid > 0xff)
311 		return 0;
312 
313 	return prueth_switchdev_vlan_del(emac, vlan->vid,
314 					 vlan->obj.orig_dev);
315 }
316 
prueth_switchdev_mdb_add(struct prueth_emac * emac,struct switchdev_obj_port_mdb * mdb)317 static int prueth_switchdev_mdb_add(struct prueth_emac *emac,
318 				    struct switchdev_obj_port_mdb *mdb)
319 {
320 	struct net_device *orig_dev = mdb->obj.orig_dev;
321 	u8 port_mask, fid_c2;
322 	bool cpu_port;
323 	int err;
324 
325 	cpu_port = netif_is_bridge_master(orig_dev);
326 
327 	if (cpu_port)
328 		port_mask = BIT(PRUETH_PORT_HOST);
329 	else
330 		port_mask = BIT(emac->port_id);
331 
332 	fid_c2 = icssg_fdb_lookup(emac, mdb->addr, mdb->vid);
333 
334 	err = icssg_fdb_add_del(emac, mdb->addr, mdb->vid, fid_c2 | port_mask, true);
335 	netdev_dbg(emac->ndev, "MDB add vid %u:%pM  ports: %X\n",
336 		   mdb->vid, mdb->addr, port_mask);
337 
338 	return err;
339 }
340 
prueth_switchdev_mdb_del(struct prueth_emac * emac,struct switchdev_obj_port_mdb * mdb)341 static int prueth_switchdev_mdb_del(struct prueth_emac *emac,
342 				    struct switchdev_obj_port_mdb *mdb)
343 {
344 	struct net_device *orig_dev = mdb->obj.orig_dev;
345 	int del_mask, ret, fid_c2;
346 	bool cpu_port;
347 
348 	cpu_port = netif_is_bridge_master(orig_dev);
349 
350 	if (cpu_port)
351 		del_mask = BIT(PRUETH_PORT_HOST);
352 	else
353 		del_mask = BIT(emac->port_id);
354 
355 	fid_c2 = icssg_fdb_lookup(emac, mdb->addr, mdb->vid);
356 
357 	if (fid_c2 & ~del_mask)
358 		ret = icssg_fdb_add_del(emac, mdb->addr, mdb->vid, fid_c2 & ~del_mask, true);
359 	else
360 		ret = icssg_fdb_add_del(emac, mdb->addr, mdb->vid, 0, false);
361 
362 	netdev_dbg(emac->ndev, "MDB del vid %u:%pM  ports: %X\n",
363 		   mdb->vid, mdb->addr, del_mask);
364 
365 	return ret;
366 }
367 
prueth_switchdev_obj_add(struct net_device * ndev,const void * ctx,const struct switchdev_obj * obj,struct netlink_ext_ack * extack)368 static int prueth_switchdev_obj_add(struct net_device *ndev, const void *ctx,
369 				    const struct switchdev_obj *obj,
370 				    struct netlink_ext_ack *extack)
371 {
372 	struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
373 	struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
374 	struct prueth_emac *emac = netdev_priv(ndev);
375 	int err = 0;
376 
377 	netdev_dbg(ndev, "obj_add: id %u port: %u\n", obj->id, emac->port_id);
378 
379 	switch (obj->id) {
380 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
381 		err = prueth_switchdev_vlans_add(emac, vlan);
382 		break;
383 	case SWITCHDEV_OBJ_ID_PORT_MDB:
384 	case SWITCHDEV_OBJ_ID_HOST_MDB:
385 		err = prueth_switchdev_mdb_add(emac, mdb);
386 		break;
387 	default:
388 		err = -EOPNOTSUPP;
389 		break;
390 	}
391 
392 	return err;
393 }
394 
prueth_switchdev_obj_del(struct net_device * ndev,const void * ctx,const struct switchdev_obj * obj)395 static int prueth_switchdev_obj_del(struct net_device *ndev, const void *ctx,
396 				    const struct switchdev_obj *obj)
397 {
398 	struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
399 	struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
400 	struct prueth_emac *emac = netdev_priv(ndev);
401 	int err = 0;
402 
403 	netdev_dbg(ndev, "obj_del: id %u port: %u\n", obj->id, emac->port_id);
404 
405 	switch (obj->id) {
406 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
407 		err = prueth_switchdev_vlans_del(emac, vlan);
408 		break;
409 	case SWITCHDEV_OBJ_ID_PORT_MDB:
410 	case SWITCHDEV_OBJ_ID_HOST_MDB:
411 		err = prueth_switchdev_mdb_del(emac, mdb);
412 		break;
413 	default:
414 		err = -EOPNOTSUPP;
415 		break;
416 	}
417 
418 	return err;
419 }
420 
prueth_switchdev_blocking_event(struct notifier_block * unused,unsigned long event,void * ptr)421 static int prueth_switchdev_blocking_event(struct notifier_block *unused,
422 					   unsigned long event, void *ptr)
423 {
424 	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
425 	int err;
426 
427 	switch (event) {
428 	case SWITCHDEV_PORT_OBJ_ADD:
429 		err = switchdev_handle_port_obj_add(dev, ptr,
430 						    prueth_dev_check,
431 						    prueth_switchdev_obj_add);
432 		return notifier_from_errno(err);
433 	case SWITCHDEV_PORT_OBJ_DEL:
434 		err = switchdev_handle_port_obj_del(dev, ptr,
435 						    prueth_dev_check,
436 						    prueth_switchdev_obj_del);
437 		return notifier_from_errno(err);
438 	case SWITCHDEV_PORT_ATTR_SET:
439 		err = switchdev_handle_port_attr_set(dev, ptr,
440 						     prueth_dev_check,
441 						     prueth_switchdev_attr_set);
442 		return notifier_from_errno(err);
443 	default:
444 		break;
445 	}
446 
447 	return NOTIFY_DONE;
448 }
449 
prueth_switchdev_register_notifiers(struct prueth * prueth)450 int prueth_switchdev_register_notifiers(struct prueth *prueth)
451 {
452 	int ret = 0;
453 
454 	prueth->prueth_switchdev_nb.notifier_call = &prueth_switchdev_event;
455 	ret = register_switchdev_notifier(&prueth->prueth_switchdev_nb);
456 	if (ret) {
457 		dev_err(prueth->dev, "register switchdev notifier fail ret:%d\n",
458 			ret);
459 		return ret;
460 	}
461 
462 	prueth->prueth_switchdev_bl_nb.notifier_call = &prueth_switchdev_blocking_event;
463 	ret = register_switchdev_blocking_notifier(&prueth->prueth_switchdev_bl_nb);
464 	if (ret) {
465 		dev_err(prueth->dev, "register switchdev blocking notifier ret:%d\n",
466 			ret);
467 		unregister_switchdev_notifier(&prueth->prueth_switchdev_nb);
468 	}
469 
470 	return ret;
471 }
472 
prueth_switchdev_unregister_notifiers(struct prueth * prueth)473 void prueth_switchdev_unregister_notifiers(struct prueth *prueth)
474 {
475 	unregister_switchdev_blocking_notifier(&prueth->prueth_switchdev_bl_nb);
476 	unregister_switchdev_notifier(&prueth->prueth_switchdev_nb);
477 }
478