1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Texas Instruments switchdev Driver |
4 | * |
5 | * Copyright (C) 2019 Texas Instruments |
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 "cpsw.h" |
16 | #include "cpsw_ale.h" |
17 | #include "cpsw_priv.h" |
18 | #include "cpsw_switchdev.h" |
19 | |
20 | struct cpsw_switchdev_event_work { |
21 | struct work_struct work; |
22 | struct switchdev_notifier_fdb_info fdb_info; |
23 | struct cpsw_priv *priv; |
24 | unsigned long event; |
25 | }; |
26 | |
27 | static int cpsw_port_stp_state_set(struct cpsw_priv *priv, u8 state) |
28 | { |
29 | struct cpsw_common *cpsw = priv->cpsw; |
30 | u8 cpsw_state; |
31 | int ret = 0; |
32 | |
33 | switch (state) { |
34 | case BR_STATE_FORWARDING: |
35 | cpsw_state = ALE_PORT_STATE_FORWARD; |
36 | break; |
37 | case BR_STATE_LEARNING: |
38 | cpsw_state = ALE_PORT_STATE_LEARN; |
39 | break; |
40 | case BR_STATE_DISABLED: |
41 | cpsw_state = ALE_PORT_STATE_DISABLE; |
42 | break; |
43 | case BR_STATE_LISTENING: |
44 | case BR_STATE_BLOCKING: |
45 | cpsw_state = ALE_PORT_STATE_BLOCK; |
46 | break; |
47 | default: |
48 | return -EOPNOTSUPP; |
49 | } |
50 | |
51 | ret = cpsw_ale_control_set(ale: cpsw->ale, port: priv->emac_port, |
52 | control: ALE_PORT_STATE, value: cpsw_state); |
53 | dev_dbg(priv->dev, "ale state: %u\n" , cpsw_state); |
54 | |
55 | return ret; |
56 | } |
57 | |
58 | static int cpsw_port_attr_br_flags_set(struct cpsw_priv *priv, |
59 | struct net_device *orig_dev, |
60 | struct switchdev_brport_flags flags) |
61 | { |
62 | struct cpsw_common *cpsw = priv->cpsw; |
63 | |
64 | if (flags.mask & BR_MCAST_FLOOD) { |
65 | bool unreg_mcast_add = false; |
66 | |
67 | if (flags.val & BR_MCAST_FLOOD) |
68 | unreg_mcast_add = true; |
69 | |
70 | dev_dbg(priv->dev, "BR_MCAST_FLOOD: %d port %u\n" , |
71 | unreg_mcast_add, priv->emac_port); |
72 | |
73 | cpsw_ale_set_unreg_mcast(ale: cpsw->ale, BIT(priv->emac_port), |
74 | add: unreg_mcast_add); |
75 | } |
76 | |
77 | return 0; |
78 | } |
79 | |
80 | static int cpsw_port_attr_br_flags_pre_set(struct net_device *netdev, |
81 | struct switchdev_brport_flags flags) |
82 | { |
83 | if (flags.mask & ~(BR_LEARNING | BR_MCAST_FLOOD)) |
84 | return -EINVAL; |
85 | |
86 | return 0; |
87 | } |
88 | |
89 | static int cpsw_port_attr_set(struct net_device *ndev, const void *ctx, |
90 | const struct switchdev_attr *attr, |
91 | struct netlink_ext_ack *extack) |
92 | { |
93 | struct cpsw_priv *priv = netdev_priv(dev: ndev); |
94 | int ret; |
95 | |
96 | dev_dbg(priv->dev, "attr: id %u port: %u\n" , attr->id, priv->emac_port); |
97 | |
98 | switch (attr->id) { |
99 | case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: |
100 | ret = cpsw_port_attr_br_flags_pre_set(netdev: ndev, |
101 | flags: attr->u.brport_flags); |
102 | break; |
103 | case SWITCHDEV_ATTR_ID_PORT_STP_STATE: |
104 | ret = cpsw_port_stp_state_set(priv, state: attr->u.stp_state); |
105 | dev_dbg(priv->dev, "stp state: %u\n" , attr->u.stp_state); |
106 | break; |
107 | case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: |
108 | ret = cpsw_port_attr_br_flags_set(priv, orig_dev: attr->orig_dev, |
109 | flags: attr->u.brport_flags); |
110 | break; |
111 | default: |
112 | ret = -EOPNOTSUPP; |
113 | break; |
114 | } |
115 | |
116 | return ret; |
117 | } |
118 | |
119 | static u16 cpsw_get_pvid(struct cpsw_priv *priv) |
120 | { |
121 | struct cpsw_common *cpsw = priv->cpsw; |
122 | u32 __iomem *port_vlan_reg; |
123 | u32 pvid; |
124 | |
125 | if (priv->emac_port) { |
126 | int reg = CPSW2_PORT_VLAN; |
127 | |
128 | if (cpsw->version == CPSW_VERSION_1) |
129 | reg = CPSW1_PORT_VLAN; |
130 | pvid = slave_read(slave: cpsw->slaves + (priv->emac_port - 1), offset: reg); |
131 | } else { |
132 | port_vlan_reg = &cpsw->host_port_regs->port_vlan; |
133 | pvid = readl(addr: port_vlan_reg); |
134 | } |
135 | |
136 | pvid = pvid & 0xfff; |
137 | |
138 | return pvid; |
139 | } |
140 | |
141 | static void cpsw_set_pvid(struct cpsw_priv *priv, u16 vid, bool cfi, u32 cos) |
142 | { |
143 | struct cpsw_common *cpsw = priv->cpsw; |
144 | void __iomem *port_vlan_reg; |
145 | u32 pvid; |
146 | |
147 | pvid = vid; |
148 | pvid |= cfi ? BIT(12) : 0; |
149 | pvid |= (cos & 0x7) << 13; |
150 | |
151 | if (priv->emac_port) { |
152 | int reg = CPSW2_PORT_VLAN; |
153 | |
154 | if (cpsw->version == CPSW_VERSION_1) |
155 | reg = CPSW1_PORT_VLAN; |
156 | /* no barrier */ |
157 | slave_write(slave: cpsw->slaves + (priv->emac_port - 1), val: pvid, offset: reg); |
158 | } else { |
159 | /* CPU port */ |
160 | port_vlan_reg = &cpsw->host_port_regs->port_vlan; |
161 | writel(val: pvid, addr: port_vlan_reg); |
162 | } |
163 | } |
164 | |
165 | static int cpsw_port_vlan_add(struct cpsw_priv *priv, bool untag, bool pvid, |
166 | u16 vid, struct net_device *orig_dev) |
167 | { |
168 | bool cpu_port = netif_is_bridge_master(dev: orig_dev); |
169 | struct cpsw_common *cpsw = priv->cpsw; |
170 | int unreg_mcast_mask = 0; |
171 | int reg_mcast_mask = 0; |
172 | int untag_mask = 0; |
173 | int port_mask; |
174 | int ret = 0; |
175 | u32 flags; |
176 | |
177 | if (cpu_port) { |
178 | port_mask = BIT(HOST_PORT_NUM); |
179 | flags = orig_dev->flags; |
180 | unreg_mcast_mask = port_mask; |
181 | } else { |
182 | port_mask = BIT(priv->emac_port); |
183 | flags = priv->ndev->flags; |
184 | } |
185 | |
186 | if (flags & IFF_MULTICAST) |
187 | reg_mcast_mask = port_mask; |
188 | |
189 | if (untag) |
190 | untag_mask = port_mask; |
191 | |
192 | ret = cpsw_ale_vlan_add_modify(ale: cpsw->ale, vid, port_mask, untag_mask, |
193 | reg_mcast: reg_mcast_mask, unreg_mcast: unreg_mcast_mask); |
194 | if (ret) { |
195 | dev_err(priv->dev, "Unable to add vlan\n" ); |
196 | return ret; |
197 | } |
198 | |
199 | if (cpu_port) |
200 | cpsw_ale_add_ucast(ale: cpsw->ale, addr: priv->mac_addr, |
201 | HOST_PORT_NUM, ALE_VLAN, vid); |
202 | if (!pvid) |
203 | return ret; |
204 | |
205 | cpsw_set_pvid(priv, vid, cfi: 0, cos: 0); |
206 | |
207 | dev_dbg(priv->dev, "VID add: %s: vid:%u ports:%X\n" , |
208 | priv->ndev->name, vid, port_mask); |
209 | return ret; |
210 | } |
211 | |
212 | static int cpsw_port_vlan_del(struct cpsw_priv *priv, u16 vid, |
213 | struct net_device *orig_dev) |
214 | { |
215 | bool cpu_port = netif_is_bridge_master(dev: orig_dev); |
216 | struct cpsw_common *cpsw = priv->cpsw; |
217 | int port_mask; |
218 | int ret = 0; |
219 | |
220 | if (cpu_port) |
221 | port_mask = BIT(HOST_PORT_NUM); |
222 | else |
223 | port_mask = BIT(priv->emac_port); |
224 | |
225 | ret = cpsw_ale_vlan_del_modify(ale: cpsw->ale, vid, port_mask); |
226 | if (ret != 0) |
227 | return ret; |
228 | |
229 | /* We don't care for the return value here, error is returned only if |
230 | * the unicast entry is not present |
231 | */ |
232 | if (cpu_port) |
233 | cpsw_ale_del_ucast(ale: cpsw->ale, addr: priv->mac_addr, |
234 | HOST_PORT_NUM, ALE_VLAN, vid); |
235 | |
236 | if (vid == cpsw_get_pvid(priv)) |
237 | cpsw_set_pvid(priv, vid: 0, cfi: 0, cos: 0); |
238 | |
239 | /* We don't care for the return value here, error is returned only if |
240 | * the multicast entry is not present |
241 | */ |
242 | cpsw_ale_del_mcast(ale: cpsw->ale, addr: priv->ndev->broadcast, |
243 | port_mask, ALE_VLAN, vid); |
244 | dev_dbg(priv->dev, "VID del: %s: vid:%u ports:%X\n" , |
245 | priv->ndev->name, vid, port_mask); |
246 | |
247 | return ret; |
248 | } |
249 | |
250 | static int cpsw_port_vlans_add(struct cpsw_priv *priv, |
251 | const struct switchdev_obj_port_vlan *vlan) |
252 | { |
253 | bool untag = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; |
254 | struct net_device *orig_dev = vlan->obj.orig_dev; |
255 | bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; |
256 | |
257 | dev_dbg(priv->dev, "VID add: %s: vid:%u flags:%X\n" , |
258 | priv->ndev->name, vlan->vid, vlan->flags); |
259 | |
260 | return cpsw_port_vlan_add(priv, untag, pvid, vid: vlan->vid, orig_dev); |
261 | } |
262 | |
263 | static int cpsw_port_mdb_add(struct cpsw_priv *priv, |
264 | struct switchdev_obj_port_mdb *mdb) |
265 | |
266 | { |
267 | struct net_device *orig_dev = mdb->obj.orig_dev; |
268 | bool cpu_port = netif_is_bridge_master(dev: orig_dev); |
269 | struct cpsw_common *cpsw = priv->cpsw; |
270 | int port_mask; |
271 | int err; |
272 | |
273 | if (cpu_port) |
274 | port_mask = BIT(HOST_PORT_NUM); |
275 | else |
276 | port_mask = BIT(priv->emac_port); |
277 | |
278 | err = cpsw_ale_add_mcast(ale: cpsw->ale, addr: mdb->addr, port_mask, |
279 | ALE_VLAN, vid: mdb->vid, mcast_state: 0); |
280 | dev_dbg(priv->dev, "MDB add: %s: vid %u:%pM ports: %X\n" , |
281 | priv->ndev->name, mdb->vid, mdb->addr, port_mask); |
282 | |
283 | return err; |
284 | } |
285 | |
286 | static int cpsw_port_mdb_del(struct cpsw_priv *priv, |
287 | struct switchdev_obj_port_mdb *mdb) |
288 | |
289 | { |
290 | struct net_device *orig_dev = mdb->obj.orig_dev; |
291 | bool cpu_port = netif_is_bridge_master(dev: orig_dev); |
292 | struct cpsw_common *cpsw = priv->cpsw; |
293 | int del_mask; |
294 | int err; |
295 | |
296 | if (cpu_port) |
297 | del_mask = BIT(HOST_PORT_NUM); |
298 | else |
299 | del_mask = BIT(priv->emac_port); |
300 | |
301 | err = cpsw_ale_del_mcast(ale: cpsw->ale, addr: mdb->addr, port_mask: del_mask, |
302 | ALE_VLAN, vid: mdb->vid); |
303 | dev_dbg(priv->dev, "MDB del: %s: vid %u:%pM ports: %X\n" , |
304 | priv->ndev->name, mdb->vid, mdb->addr, del_mask); |
305 | |
306 | return err; |
307 | } |
308 | |
309 | static int cpsw_port_obj_add(struct net_device *ndev, const void *ctx, |
310 | const struct switchdev_obj *obj, |
311 | struct netlink_ext_ack *extack) |
312 | { |
313 | struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); |
314 | struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj); |
315 | struct cpsw_priv *priv = netdev_priv(dev: ndev); |
316 | int err = 0; |
317 | |
318 | dev_dbg(priv->dev, "obj_add: id %u port: %u\n" , |
319 | obj->id, priv->emac_port); |
320 | |
321 | switch (obj->id) { |
322 | case SWITCHDEV_OBJ_ID_PORT_VLAN: |
323 | err = cpsw_port_vlans_add(priv, vlan); |
324 | break; |
325 | case SWITCHDEV_OBJ_ID_PORT_MDB: |
326 | case SWITCHDEV_OBJ_ID_HOST_MDB: |
327 | err = cpsw_port_mdb_add(priv, mdb); |
328 | break; |
329 | default: |
330 | err = -EOPNOTSUPP; |
331 | break; |
332 | } |
333 | |
334 | return err; |
335 | } |
336 | |
337 | static int cpsw_port_obj_del(struct net_device *ndev, const void *ctx, |
338 | const struct switchdev_obj *obj) |
339 | { |
340 | struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); |
341 | struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj); |
342 | struct cpsw_priv *priv = netdev_priv(dev: ndev); |
343 | int err = 0; |
344 | |
345 | dev_dbg(priv->dev, "obj_del: id %u port: %u\n" , |
346 | obj->id, priv->emac_port); |
347 | |
348 | switch (obj->id) { |
349 | case SWITCHDEV_OBJ_ID_PORT_VLAN: |
350 | err = cpsw_port_vlan_del(priv, vid: vlan->vid, orig_dev: vlan->obj.orig_dev); |
351 | break; |
352 | case SWITCHDEV_OBJ_ID_PORT_MDB: |
353 | case SWITCHDEV_OBJ_ID_HOST_MDB: |
354 | err = cpsw_port_mdb_del(priv, mdb); |
355 | break; |
356 | default: |
357 | err = -EOPNOTSUPP; |
358 | break; |
359 | } |
360 | |
361 | return err; |
362 | } |
363 | |
364 | static void cpsw_fdb_offload_notify(struct net_device *ndev, |
365 | struct switchdev_notifier_fdb_info *rcv) |
366 | { |
367 | struct switchdev_notifier_fdb_info info = {}; |
368 | |
369 | info.addr = rcv->addr; |
370 | info.vid = rcv->vid; |
371 | info.offloaded = true; |
372 | call_switchdev_notifiers(val: SWITCHDEV_FDB_OFFLOADED, |
373 | dev: ndev, info: &info.info, NULL); |
374 | } |
375 | |
376 | static void cpsw_switchdev_event_work(struct work_struct *work) |
377 | { |
378 | struct cpsw_switchdev_event_work *switchdev_work = |
379 | container_of(work, struct cpsw_switchdev_event_work, work); |
380 | struct cpsw_priv *priv = switchdev_work->priv; |
381 | struct switchdev_notifier_fdb_info *fdb; |
382 | struct cpsw_common *cpsw = priv->cpsw; |
383 | int port = priv->emac_port; |
384 | |
385 | rtnl_lock(); |
386 | switch (switchdev_work->event) { |
387 | case SWITCHDEV_FDB_ADD_TO_DEVICE: |
388 | fdb = &switchdev_work->fdb_info; |
389 | |
390 | dev_dbg(cpsw->dev, "cpsw_fdb_add: MACID = %pM vid = %u flags = %u %u -- port %d\n" , |
391 | fdb->addr, fdb->vid, fdb->added_by_user, |
392 | fdb->offloaded, port); |
393 | |
394 | if (!fdb->added_by_user || fdb->is_local) |
395 | break; |
396 | if (memcmp(p: priv->mac_addr, q: (u8 *)fdb->addr, ETH_ALEN) == 0) |
397 | port = HOST_PORT_NUM; |
398 | |
399 | cpsw_ale_add_ucast(ale: cpsw->ale, addr: (u8 *)fdb->addr, port, |
400 | flags: fdb->vid ? ALE_VLAN : 0, vid: fdb->vid); |
401 | cpsw_fdb_offload_notify(ndev: priv->ndev, rcv: fdb); |
402 | break; |
403 | case SWITCHDEV_FDB_DEL_TO_DEVICE: |
404 | fdb = &switchdev_work->fdb_info; |
405 | |
406 | dev_dbg(cpsw->dev, "cpsw_fdb_del: MACID = %pM vid = %u flags = %u %u -- port %d\n" , |
407 | fdb->addr, fdb->vid, fdb->added_by_user, |
408 | fdb->offloaded, port); |
409 | |
410 | if (!fdb->added_by_user || fdb->is_local) |
411 | break; |
412 | if (memcmp(p: priv->mac_addr, q: (u8 *)fdb->addr, ETH_ALEN) == 0) |
413 | port = HOST_PORT_NUM; |
414 | |
415 | cpsw_ale_del_ucast(ale: cpsw->ale, addr: (u8 *)fdb->addr, port, |
416 | flags: fdb->vid ? ALE_VLAN : 0, vid: fdb->vid); |
417 | break; |
418 | default: |
419 | break; |
420 | } |
421 | rtnl_unlock(); |
422 | |
423 | kfree(objp: switchdev_work->fdb_info.addr); |
424 | kfree(objp: switchdev_work); |
425 | dev_put(dev: priv->ndev); |
426 | } |
427 | |
428 | /* called under rcu_read_lock() */ |
429 | static int cpsw_switchdev_event(struct notifier_block *unused, |
430 | unsigned long event, void *ptr) |
431 | { |
432 | struct net_device *ndev = switchdev_notifier_info_to_dev(info: ptr); |
433 | struct switchdev_notifier_fdb_info *fdb_info = ptr; |
434 | struct cpsw_switchdev_event_work *switchdev_work; |
435 | struct cpsw_priv *priv = netdev_priv(dev: ndev); |
436 | int err; |
437 | |
438 | if (event == SWITCHDEV_PORT_ATTR_SET) { |
439 | err = switchdev_handle_port_attr_set(dev: ndev, port_attr_info: ptr, |
440 | check_cb: cpsw_port_dev_check, |
441 | set_cb: cpsw_port_attr_set); |
442 | return notifier_from_errno(err); |
443 | } |
444 | |
445 | if (!cpsw_port_dev_check(dev: ndev)) |
446 | return NOTIFY_DONE; |
447 | |
448 | switchdev_work = kzalloc(size: sizeof(*switchdev_work), GFP_ATOMIC); |
449 | if (WARN_ON(!switchdev_work)) |
450 | return NOTIFY_BAD; |
451 | |
452 | INIT_WORK(&switchdev_work->work, cpsw_switchdev_event_work); |
453 | switchdev_work->priv = priv; |
454 | switchdev_work->event = event; |
455 | |
456 | switch (event) { |
457 | case SWITCHDEV_FDB_ADD_TO_DEVICE: |
458 | case SWITCHDEV_FDB_DEL_TO_DEVICE: |
459 | memcpy(&switchdev_work->fdb_info, ptr, |
460 | sizeof(switchdev_work->fdb_info)); |
461 | switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); |
462 | if (!switchdev_work->fdb_info.addr) |
463 | goto err_addr_alloc; |
464 | ether_addr_copy(dst: (u8 *)switchdev_work->fdb_info.addr, |
465 | src: fdb_info->addr); |
466 | dev_hold(dev: ndev); |
467 | break; |
468 | default: |
469 | kfree(objp: switchdev_work); |
470 | return NOTIFY_DONE; |
471 | } |
472 | |
473 | queue_work(wq: system_long_wq, work: &switchdev_work->work); |
474 | |
475 | return NOTIFY_DONE; |
476 | |
477 | err_addr_alloc: |
478 | kfree(objp: switchdev_work); |
479 | return NOTIFY_BAD; |
480 | } |
481 | |
482 | static struct notifier_block cpsw_switchdev_notifier = { |
483 | .notifier_call = cpsw_switchdev_event, |
484 | }; |
485 | |
486 | static int cpsw_switchdev_blocking_event(struct notifier_block *unused, |
487 | unsigned long event, void *ptr) |
488 | { |
489 | struct net_device *dev = switchdev_notifier_info_to_dev(info: ptr); |
490 | int err; |
491 | |
492 | switch (event) { |
493 | case SWITCHDEV_PORT_OBJ_ADD: |
494 | err = switchdev_handle_port_obj_add(dev, port_obj_info: ptr, |
495 | check_cb: cpsw_port_dev_check, |
496 | add_cb: cpsw_port_obj_add); |
497 | return notifier_from_errno(err); |
498 | case SWITCHDEV_PORT_OBJ_DEL: |
499 | err = switchdev_handle_port_obj_del(dev, port_obj_info: ptr, |
500 | check_cb: cpsw_port_dev_check, |
501 | del_cb: cpsw_port_obj_del); |
502 | return notifier_from_errno(err); |
503 | case SWITCHDEV_PORT_ATTR_SET: |
504 | err = switchdev_handle_port_attr_set(dev, port_attr_info: ptr, |
505 | check_cb: cpsw_port_dev_check, |
506 | set_cb: cpsw_port_attr_set); |
507 | return notifier_from_errno(err); |
508 | default: |
509 | break; |
510 | } |
511 | |
512 | return NOTIFY_DONE; |
513 | } |
514 | |
515 | static struct notifier_block cpsw_switchdev_bl_notifier = { |
516 | .notifier_call = cpsw_switchdev_blocking_event, |
517 | }; |
518 | |
519 | int cpsw_switchdev_register_notifiers(struct cpsw_common *cpsw) |
520 | { |
521 | int ret = 0; |
522 | |
523 | ret = register_switchdev_notifier(nb: &cpsw_switchdev_notifier); |
524 | if (ret) { |
525 | dev_err(cpsw->dev, "register switchdev notifier fail ret:%d\n" , |
526 | ret); |
527 | return ret; |
528 | } |
529 | |
530 | ret = register_switchdev_blocking_notifier(nb: &cpsw_switchdev_bl_notifier); |
531 | if (ret) { |
532 | dev_err(cpsw->dev, "register switchdev blocking notifier ret:%d\n" , |
533 | ret); |
534 | unregister_switchdev_notifier(nb: &cpsw_switchdev_notifier); |
535 | } |
536 | |
537 | return ret; |
538 | } |
539 | |
540 | void cpsw_switchdev_unregister_notifiers(struct cpsw_common *cpsw) |
541 | { |
542 | unregister_switchdev_blocking_notifier(nb: &cpsw_switchdev_bl_notifier); |
543 | unregister_switchdev_notifier(nb: &cpsw_switchdev_notifier); |
544 | } |
545 | |