]>
Commit | Line | Data |
---|---|---|
9c90eea3 VO |
1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) |
2 | /* Microsemi Ocelot Switch driver | |
f59fd9ca VO |
3 | * |
4 | * This contains glue logic between the switchdev driver operations and the | |
5 | * mscc_ocelot_switch_lib. | |
9c90eea3 VO |
6 | * |
7 | * Copyright (c) 2017, 2019 Microsemi Corporation | |
f59fd9ca | 8 | * Copyright 2020-2021 NXP Semiconductors |
9c90eea3 VO |
9 | */ |
10 | ||
11 | #include <linux/if_bridge.h> | |
12 | #include "ocelot.h" | |
3c83654f | 13 | #include "ocelot_vcap.h" |
9c90eea3 | 14 | |
f59fd9ca VO |
15 | static struct ocelot *devlink_port_to_ocelot(struct devlink_port *dlp) |
16 | { | |
17 | return devlink_priv(dlp->devlink); | |
18 | } | |
19 | ||
20 | static int devlink_port_to_port(struct devlink_port *dlp) | |
21 | { | |
22 | struct ocelot *ocelot = devlink_port_to_ocelot(dlp); | |
23 | ||
24 | return dlp - ocelot->devlink_ports; | |
25 | } | |
26 | ||
27 | static int ocelot_devlink_sb_pool_get(struct devlink *dl, | |
28 | unsigned int sb_index, u16 pool_index, | |
29 | struct devlink_sb_pool_info *pool_info) | |
30 | { | |
31 | struct ocelot *ocelot = devlink_priv(dl); | |
32 | ||
33 | return ocelot_sb_pool_get(ocelot, sb_index, pool_index, pool_info); | |
34 | } | |
35 | ||
36 | static int ocelot_devlink_sb_pool_set(struct devlink *dl, unsigned int sb_index, | |
37 | u16 pool_index, u32 size, | |
38 | enum devlink_sb_threshold_type threshold_type, | |
39 | struct netlink_ext_ack *extack) | |
40 | { | |
41 | struct ocelot *ocelot = devlink_priv(dl); | |
42 | ||
43 | return ocelot_sb_pool_set(ocelot, sb_index, pool_index, size, | |
44 | threshold_type, extack); | |
45 | } | |
46 | ||
47 | static int ocelot_devlink_sb_port_pool_get(struct devlink_port *dlp, | |
48 | unsigned int sb_index, u16 pool_index, | |
49 | u32 *p_threshold) | |
50 | { | |
51 | struct ocelot *ocelot = devlink_port_to_ocelot(dlp); | |
52 | int port = devlink_port_to_port(dlp); | |
53 | ||
54 | return ocelot_sb_port_pool_get(ocelot, port, sb_index, pool_index, | |
55 | p_threshold); | |
56 | } | |
57 | ||
58 | static int ocelot_devlink_sb_port_pool_set(struct devlink_port *dlp, | |
59 | unsigned int sb_index, u16 pool_index, | |
60 | u32 threshold, | |
61 | struct netlink_ext_ack *extack) | |
62 | { | |
63 | struct ocelot *ocelot = devlink_port_to_ocelot(dlp); | |
64 | int port = devlink_port_to_port(dlp); | |
65 | ||
66 | return ocelot_sb_port_pool_set(ocelot, port, sb_index, pool_index, | |
67 | threshold, extack); | |
68 | } | |
69 | ||
70 | static int | |
71 | ocelot_devlink_sb_tc_pool_bind_get(struct devlink_port *dlp, | |
72 | unsigned int sb_index, u16 tc_index, | |
73 | enum devlink_sb_pool_type pool_type, | |
74 | u16 *p_pool_index, u32 *p_threshold) | |
75 | { | |
76 | struct ocelot *ocelot = devlink_port_to_ocelot(dlp); | |
77 | int port = devlink_port_to_port(dlp); | |
78 | ||
79 | return ocelot_sb_tc_pool_bind_get(ocelot, port, sb_index, tc_index, | |
80 | pool_type, p_pool_index, | |
81 | p_threshold); | |
82 | } | |
83 | ||
84 | static int | |
85 | ocelot_devlink_sb_tc_pool_bind_set(struct devlink_port *dlp, | |
86 | unsigned int sb_index, u16 tc_index, | |
87 | enum devlink_sb_pool_type pool_type, | |
88 | u16 pool_index, u32 threshold, | |
89 | struct netlink_ext_ack *extack) | |
90 | { | |
91 | struct ocelot *ocelot = devlink_port_to_ocelot(dlp); | |
92 | int port = devlink_port_to_port(dlp); | |
93 | ||
94 | return ocelot_sb_tc_pool_bind_set(ocelot, port, sb_index, tc_index, | |
95 | pool_type, pool_index, threshold, | |
96 | extack); | |
97 | } | |
98 | ||
99 | static int ocelot_devlink_sb_occ_snapshot(struct devlink *dl, | |
100 | unsigned int sb_index) | |
101 | { | |
102 | struct ocelot *ocelot = devlink_priv(dl); | |
103 | ||
104 | return ocelot_sb_occ_snapshot(ocelot, sb_index); | |
105 | } | |
106 | ||
107 | static int ocelot_devlink_sb_occ_max_clear(struct devlink *dl, | |
108 | unsigned int sb_index) | |
109 | { | |
110 | struct ocelot *ocelot = devlink_priv(dl); | |
111 | ||
112 | return ocelot_sb_occ_max_clear(ocelot, sb_index); | |
113 | } | |
114 | ||
115 | static int ocelot_devlink_sb_occ_port_pool_get(struct devlink_port *dlp, | |
116 | unsigned int sb_index, | |
117 | u16 pool_index, u32 *p_cur, | |
118 | u32 *p_max) | |
119 | { | |
120 | struct ocelot *ocelot = devlink_port_to_ocelot(dlp); | |
121 | int port = devlink_port_to_port(dlp); | |
122 | ||
123 | return ocelot_sb_occ_port_pool_get(ocelot, port, sb_index, pool_index, | |
124 | p_cur, p_max); | |
125 | } | |
126 | ||
127 | static int | |
128 | ocelot_devlink_sb_occ_tc_port_bind_get(struct devlink_port *dlp, | |
129 | unsigned int sb_index, u16 tc_index, | |
130 | enum devlink_sb_pool_type pool_type, | |
131 | u32 *p_cur, u32 *p_max) | |
132 | { | |
133 | struct ocelot *ocelot = devlink_port_to_ocelot(dlp); | |
134 | int port = devlink_port_to_port(dlp); | |
135 | ||
136 | return ocelot_sb_occ_tc_port_bind_get(ocelot, port, sb_index, | |
137 | tc_index, pool_type, | |
138 | p_cur, p_max); | |
139 | } | |
140 | ||
6c30384e | 141 | const struct devlink_ops ocelot_devlink_ops = { |
f59fd9ca VO |
142 | .sb_pool_get = ocelot_devlink_sb_pool_get, |
143 | .sb_pool_set = ocelot_devlink_sb_pool_set, | |
144 | .sb_port_pool_get = ocelot_devlink_sb_port_pool_get, | |
145 | .sb_port_pool_set = ocelot_devlink_sb_port_pool_set, | |
146 | .sb_tc_pool_bind_get = ocelot_devlink_sb_tc_pool_bind_get, | |
147 | .sb_tc_pool_bind_set = ocelot_devlink_sb_tc_pool_bind_set, | |
148 | .sb_occ_snapshot = ocelot_devlink_sb_occ_snapshot, | |
149 | .sb_occ_max_clear = ocelot_devlink_sb_occ_max_clear, | |
150 | .sb_occ_port_pool_get = ocelot_devlink_sb_occ_port_pool_get, | |
151 | .sb_occ_tc_port_bind_get = ocelot_devlink_sb_occ_tc_port_bind_get, | |
6c30384e VO |
152 | }; |
153 | ||
154 | int ocelot_port_devlink_init(struct ocelot *ocelot, int port, | |
155 | enum devlink_port_flavour flavour) | |
156 | { | |
157 | struct devlink_port *dlp = &ocelot->devlink_ports[port]; | |
158 | int id_len = sizeof(ocelot->base_mac); | |
159 | struct devlink *dl = ocelot->devlink; | |
160 | struct devlink_port_attrs attrs = {}; | |
161 | ||
162 | memcpy(attrs.switch_id.id, &ocelot->base_mac, id_len); | |
163 | attrs.switch_id.id_len = id_len; | |
164 | attrs.phys.port_number = port; | |
165 | attrs.flavour = flavour; | |
166 | ||
167 | devlink_port_attrs_set(dlp, &attrs); | |
168 | ||
169 | return devlink_port_register(dl, dlp, port); | |
170 | } | |
171 | ||
172 | void ocelot_port_devlink_teardown(struct ocelot *ocelot, int port) | |
173 | { | |
174 | struct devlink_port *dlp = &ocelot->devlink_ports[port]; | |
175 | ||
176 | devlink_port_unregister(dlp); | |
177 | } | |
178 | ||
179 | static struct devlink_port *ocelot_get_devlink_port(struct net_device *dev) | |
180 | { | |
181 | struct ocelot_port_private *priv = netdev_priv(dev); | |
182 | struct ocelot *ocelot = priv->port.ocelot; | |
183 | int port = priv->chip_port; | |
184 | ||
185 | return &ocelot->devlink_ports[port]; | |
186 | } | |
187 | ||
9c90eea3 VO |
188 | int ocelot_setup_tc_cls_flower(struct ocelot_port_private *priv, |
189 | struct flow_cls_offload *f, | |
190 | bool ingress) | |
191 | { | |
192 | struct ocelot *ocelot = priv->port.ocelot; | |
193 | int port = priv->chip_port; | |
194 | ||
195 | if (!ingress) | |
196 | return -EOPNOTSUPP; | |
197 | ||
198 | switch (f->command) { | |
199 | case FLOW_CLS_REPLACE: | |
200 | return ocelot_cls_flower_replace(ocelot, port, f, ingress); | |
201 | case FLOW_CLS_DESTROY: | |
202 | return ocelot_cls_flower_destroy(ocelot, port, f, ingress); | |
203 | case FLOW_CLS_STATS: | |
204 | return ocelot_cls_flower_stats(ocelot, port, f, ingress); | |
205 | default: | |
206 | return -EOPNOTSUPP; | |
207 | } | |
208 | } | |
209 | ||
210 | static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv, | |
211 | struct tc_cls_matchall_offload *f, | |
212 | bool ingress) | |
213 | { | |
214 | struct netlink_ext_ack *extack = f->common.extack; | |
215 | struct ocelot *ocelot = priv->port.ocelot; | |
216 | struct ocelot_policer pol = { 0 }; | |
217 | struct flow_action_entry *action; | |
218 | int port = priv->chip_port; | |
219 | int err; | |
220 | ||
221 | if (!ingress) { | |
222 | NL_SET_ERR_MSG_MOD(extack, "Only ingress is supported"); | |
223 | return -EOPNOTSUPP; | |
224 | } | |
225 | ||
226 | switch (f->command) { | |
227 | case TC_CLSMATCHALL_REPLACE: | |
228 | if (!flow_offload_has_one_action(&f->rule->action)) { | |
229 | NL_SET_ERR_MSG_MOD(extack, | |
230 | "Only one action is supported"); | |
231 | return -EOPNOTSUPP; | |
232 | } | |
233 | ||
234 | if (priv->tc.block_shared) { | |
235 | NL_SET_ERR_MSG_MOD(extack, | |
236 | "Rate limit is not supported on shared blocks"); | |
237 | return -EOPNOTSUPP; | |
238 | } | |
239 | ||
240 | action = &f->rule->action.entries[0]; | |
241 | ||
242 | if (action->id != FLOW_ACTION_POLICE) { | |
243 | NL_SET_ERR_MSG_MOD(extack, "Unsupported action"); | |
244 | return -EOPNOTSUPP; | |
245 | } | |
246 | ||
247 | if (priv->tc.police_id && priv->tc.police_id != f->cookie) { | |
248 | NL_SET_ERR_MSG_MOD(extack, | |
249 | "Only one policer per port is supported"); | |
250 | return -EEXIST; | |
251 | } | |
252 | ||
253 | pol.rate = (u32)div_u64(action->police.rate_bytes_ps, 1000) * 8; | |
5f035af7 | 254 | pol.burst = action->police.burst; |
9c90eea3 VO |
255 | |
256 | err = ocelot_port_policer_add(ocelot, port, &pol); | |
257 | if (err) { | |
258 | NL_SET_ERR_MSG_MOD(extack, "Could not add policer"); | |
259 | return err; | |
260 | } | |
261 | ||
262 | priv->tc.police_id = f->cookie; | |
263 | priv->tc.offload_cnt++; | |
264 | return 0; | |
265 | case TC_CLSMATCHALL_DESTROY: | |
266 | if (priv->tc.police_id != f->cookie) | |
267 | return -ENOENT; | |
268 | ||
269 | err = ocelot_port_policer_del(ocelot, port); | |
270 | if (err) { | |
271 | NL_SET_ERR_MSG_MOD(extack, | |
272 | "Could not delete policer"); | |
273 | return err; | |
274 | } | |
275 | priv->tc.police_id = 0; | |
276 | priv->tc.offload_cnt--; | |
277 | return 0; | |
278 | case TC_CLSMATCHALL_STATS: | |
279 | default: | |
280 | return -EOPNOTSUPP; | |
281 | } | |
282 | } | |
283 | ||
284 | static int ocelot_setup_tc_block_cb(enum tc_setup_type type, | |
285 | void *type_data, | |
286 | void *cb_priv, bool ingress) | |
287 | { | |
288 | struct ocelot_port_private *priv = cb_priv; | |
289 | ||
290 | if (!tc_cls_can_offload_and_chain0(priv->dev, type_data)) | |
291 | return -EOPNOTSUPP; | |
292 | ||
293 | switch (type) { | |
294 | case TC_SETUP_CLSMATCHALL: | |
295 | return ocelot_setup_tc_cls_matchall(priv, type_data, ingress); | |
296 | case TC_SETUP_CLSFLOWER: | |
297 | return ocelot_setup_tc_cls_flower(priv, type_data, ingress); | |
298 | default: | |
299 | return -EOPNOTSUPP; | |
300 | } | |
301 | } | |
302 | ||
303 | static int ocelot_setup_tc_block_cb_ig(enum tc_setup_type type, | |
304 | void *type_data, | |
305 | void *cb_priv) | |
306 | { | |
307 | return ocelot_setup_tc_block_cb(type, type_data, | |
308 | cb_priv, true); | |
309 | } | |
310 | ||
311 | static int ocelot_setup_tc_block_cb_eg(enum tc_setup_type type, | |
312 | void *type_data, | |
313 | void *cb_priv) | |
314 | { | |
315 | return ocelot_setup_tc_block_cb(type, type_data, | |
316 | cb_priv, false); | |
317 | } | |
318 | ||
319 | static LIST_HEAD(ocelot_block_cb_list); | |
320 | ||
321 | static int ocelot_setup_tc_block(struct ocelot_port_private *priv, | |
322 | struct flow_block_offload *f) | |
323 | { | |
324 | struct flow_block_cb *block_cb; | |
325 | flow_setup_cb_t *cb; | |
326 | ||
327 | if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) { | |
328 | cb = ocelot_setup_tc_block_cb_ig; | |
329 | priv->tc.block_shared = f->block_shared; | |
330 | } else if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) { | |
331 | cb = ocelot_setup_tc_block_cb_eg; | |
332 | } else { | |
333 | return -EOPNOTSUPP; | |
334 | } | |
335 | ||
336 | f->driver_block_list = &ocelot_block_cb_list; | |
337 | ||
338 | switch (f->command) { | |
339 | case FLOW_BLOCK_BIND: | |
340 | if (flow_block_cb_is_busy(cb, priv, &ocelot_block_cb_list)) | |
341 | return -EBUSY; | |
342 | ||
343 | block_cb = flow_block_cb_alloc(cb, priv, priv, NULL); | |
344 | if (IS_ERR(block_cb)) | |
345 | return PTR_ERR(block_cb); | |
346 | ||
347 | flow_block_cb_add(block_cb, f); | |
348 | list_add_tail(&block_cb->driver_list, f->driver_block_list); | |
349 | return 0; | |
350 | case FLOW_BLOCK_UNBIND: | |
351 | block_cb = flow_block_cb_lookup(f->block, cb, priv); | |
352 | if (!block_cb) | |
353 | return -ENOENT; | |
354 | ||
355 | flow_block_cb_remove(block_cb, f); | |
356 | list_del(&block_cb->driver_list); | |
357 | return 0; | |
358 | default: | |
359 | return -EOPNOTSUPP; | |
360 | } | |
361 | } | |
362 | ||
363 | static int ocelot_setup_tc(struct net_device *dev, enum tc_setup_type type, | |
364 | void *type_data) | |
365 | { | |
366 | struct ocelot_port_private *priv = netdev_priv(dev); | |
367 | ||
368 | switch (type) { | |
369 | case TC_SETUP_BLOCK: | |
370 | return ocelot_setup_tc_block(priv, type_data); | |
371 | default: | |
372 | return -EOPNOTSUPP; | |
373 | } | |
374 | return 0; | |
375 | } | |
376 | ||
377 | static void ocelot_port_adjust_link(struct net_device *dev) | |
378 | { | |
379 | struct ocelot_port_private *priv = netdev_priv(dev); | |
380 | struct ocelot *ocelot = priv->port.ocelot; | |
381 | int port = priv->chip_port; | |
382 | ||
383 | ocelot_adjust_link(ocelot, port, dev->phydev); | |
384 | } | |
385 | ||
2f0402fe VO |
386 | static int ocelot_vlan_vid_prepare(struct net_device *dev, u16 vid, bool pvid, |
387 | bool untagged) | |
388 | { | |
389 | struct ocelot_port_private *priv = netdev_priv(dev); | |
390 | struct ocelot_port *ocelot_port = &priv->port; | |
391 | struct ocelot *ocelot = ocelot_port->ocelot; | |
392 | int port = priv->chip_port; | |
393 | ||
394 | return ocelot_vlan_prepare(ocelot, port, vid, pvid, untagged); | |
395 | } | |
396 | ||
9c90eea3 VO |
397 | static int ocelot_vlan_vid_add(struct net_device *dev, u16 vid, bool pvid, |
398 | bool untagged) | |
399 | { | |
400 | struct ocelot_port_private *priv = netdev_priv(dev); | |
401 | struct ocelot_port *ocelot_port = &priv->port; | |
402 | struct ocelot *ocelot = ocelot_port->ocelot; | |
403 | int port = priv->chip_port; | |
404 | int ret; | |
405 | ||
406 | ret = ocelot_vlan_add(ocelot, port, vid, pvid, untagged); | |
407 | if (ret) | |
408 | return ret; | |
409 | ||
410 | /* Add the port MAC address to with the right VLAN information */ | |
411 | ocelot_mact_learn(ocelot, PGID_CPU, dev->dev_addr, vid, | |
412 | ENTRYTYPE_LOCKED); | |
413 | ||
414 | return 0; | |
415 | } | |
416 | ||
417 | static int ocelot_vlan_vid_del(struct net_device *dev, u16 vid) | |
418 | { | |
419 | struct ocelot_port_private *priv = netdev_priv(dev); | |
420 | struct ocelot *ocelot = priv->port.ocelot; | |
421 | int port = priv->chip_port; | |
422 | int ret; | |
423 | ||
424 | /* 8021q removes VID 0 on module unload for all interfaces | |
425 | * with VLAN filtering feature. We need to keep it to receive | |
426 | * untagged traffic. | |
427 | */ | |
428 | if (vid == 0) | |
429 | return 0; | |
430 | ||
431 | ret = ocelot_vlan_del(ocelot, port, vid); | |
432 | if (ret) | |
433 | return ret; | |
434 | ||
435 | /* Del the port MAC address to with the right VLAN information */ | |
436 | ocelot_mact_forget(ocelot, dev->dev_addr, vid); | |
437 | ||
438 | return 0; | |
439 | } | |
440 | ||
441 | static int ocelot_port_open(struct net_device *dev) | |
442 | { | |
443 | struct ocelot_port_private *priv = netdev_priv(dev); | |
444 | struct ocelot_port *ocelot_port = &priv->port; | |
445 | struct ocelot *ocelot = ocelot_port->ocelot; | |
446 | int port = priv->chip_port; | |
447 | int err; | |
448 | ||
449 | if (priv->serdes) { | |
450 | err = phy_set_mode_ext(priv->serdes, PHY_MODE_ETHERNET, | |
451 | ocelot_port->phy_mode); | |
452 | if (err) { | |
453 | netdev_err(dev, "Could not set mode of SerDes\n"); | |
454 | return err; | |
455 | } | |
456 | } | |
457 | ||
458 | err = phy_connect_direct(dev, priv->phy, &ocelot_port_adjust_link, | |
459 | ocelot_port->phy_mode); | |
460 | if (err) { | |
461 | netdev_err(dev, "Could not attach to PHY\n"); | |
462 | return err; | |
463 | } | |
464 | ||
465 | dev->phydev = priv->phy; | |
466 | ||
467 | phy_attached_info(priv->phy); | |
468 | phy_start(priv->phy); | |
469 | ||
470 | ocelot_port_enable(ocelot, port, priv->phy); | |
471 | ||
472 | return 0; | |
473 | } | |
474 | ||
475 | static int ocelot_port_stop(struct net_device *dev) | |
476 | { | |
477 | struct ocelot_port_private *priv = netdev_priv(dev); | |
478 | struct ocelot *ocelot = priv->port.ocelot; | |
479 | int port = priv->chip_port; | |
480 | ||
481 | phy_disconnect(priv->phy); | |
482 | ||
483 | dev->phydev = NULL; | |
484 | ||
485 | ocelot_port_disable(ocelot, port); | |
486 | ||
487 | return 0; | |
488 | } | |
489 | ||
490 | /* Generate the IFH for frame injection | |
491 | * | |
492 | * The IFH is a 128bit-value | |
493 | * bit 127: bypass the analyzer processing | |
494 | * bit 56-67: destination mask | |
495 | * bit 28-29: pop_cnt: 3 disables all rewriting of the frame | |
496 | * bit 20-27: cpu extraction queue mask | |
497 | * bit 16: tag type 0: C-tag, 1: S-tag | |
498 | * bit 0-11: VID | |
499 | */ | |
500 | static int ocelot_gen_ifh(u32 *ifh, struct frame_info *info) | |
501 | { | |
502 | ifh[0] = IFH_INJ_BYPASS | ((0x1ff & info->rew_op) << 21); | |
503 | ifh[1] = (0xf00 & info->port) >> 8; | |
504 | ifh[2] = (0xff & info->port) << 24; | |
505 | ifh[3] = (info->tag_type << 16) | info->vid; | |
506 | ||
507 | return 0; | |
508 | } | |
509 | ||
510 | static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev) | |
511 | { | |
512 | struct ocelot_port_private *priv = netdev_priv(dev); | |
513 | struct skb_shared_info *shinfo = skb_shinfo(skb); | |
514 | struct ocelot_port *ocelot_port = &priv->port; | |
515 | struct ocelot *ocelot = ocelot_port->ocelot; | |
516 | u32 val, ifh[OCELOT_TAG_LEN / 4]; | |
517 | struct frame_info info = {}; | |
518 | u8 grp = 0; /* Send everything on CPU group 0 */ | |
519 | unsigned int i, count, last; | |
520 | int port = priv->chip_port; | |
521 | ||
522 | val = ocelot_read(ocelot, QS_INJ_STATUS); | |
523 | if (!(val & QS_INJ_STATUS_FIFO_RDY(BIT(grp))) || | |
524 | (val & QS_INJ_STATUS_WMARK_REACHED(BIT(grp)))) | |
525 | return NETDEV_TX_BUSY; | |
526 | ||
527 | ocelot_write_rix(ocelot, QS_INJ_CTRL_GAP_SIZE(1) | | |
528 | QS_INJ_CTRL_SOF, QS_INJ_CTRL, grp); | |
529 | ||
530 | info.port = BIT(port); | |
531 | info.tag_type = IFH_TAG_TYPE_C; | |
532 | info.vid = skb_vlan_tag_get(skb); | |
533 | ||
534 | /* Check if timestamping is needed */ | |
e2f9a8fe VO |
535 | if (ocelot->ptp && (shinfo->tx_flags & SKBTX_HW_TSTAMP)) { |
536 | info.rew_op = ocelot_port->ptp_cmd; | |
537 | ||
538 | if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) { | |
539 | struct sk_buff *clone; | |
540 | ||
541 | clone = skb_clone_sk(skb); | |
542 | if (!clone) { | |
543 | kfree_skb(skb); | |
544 | return NETDEV_TX_OK; | |
545 | } | |
546 | ||
547 | ocelot_port_add_txtstamp_skb(ocelot, port, clone); | |
548 | ||
549 | info.rew_op |= clone->cb[0] << 3; | |
550 | } | |
551 | } | |
9dda66ac | 552 | |
9c90eea3 VO |
553 | if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) { |
554 | info.rew_op = ocelot_port->ptp_cmd; | |
6565243c VO |
555 | if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) |
556 | info.rew_op |= skb->cb[0] << 3; | |
9c90eea3 VO |
557 | } |
558 | ||
559 | ocelot_gen_ifh(ifh, &info); | |
560 | ||
561 | for (i = 0; i < OCELOT_TAG_LEN / 4; i++) | |
562 | ocelot_write_rix(ocelot, (__force u32)cpu_to_be32(ifh[i]), | |
563 | QS_INJ_WR, grp); | |
564 | ||
565 | count = (skb->len + 3) / 4; | |
566 | last = skb->len % 4; | |
567 | for (i = 0; i < count; i++) | |
568 | ocelot_write_rix(ocelot, ((u32 *)skb->data)[i], QS_INJ_WR, grp); | |
569 | ||
570 | /* Add padding */ | |
571 | while (i < (OCELOT_BUFFER_CELL_SZ / 4)) { | |
572 | ocelot_write_rix(ocelot, 0, QS_INJ_WR, grp); | |
573 | i++; | |
574 | } | |
575 | ||
576 | /* Indicate EOF and valid bytes in last word */ | |
577 | ocelot_write_rix(ocelot, QS_INJ_CTRL_GAP_SIZE(1) | | |
578 | QS_INJ_CTRL_VLD_BYTES(skb->len < OCELOT_BUFFER_CELL_SZ ? 0 : last) | | |
579 | QS_INJ_CTRL_EOF, | |
580 | QS_INJ_CTRL, grp); | |
581 | ||
582 | /* Add dummy CRC */ | |
583 | ocelot_write_rix(ocelot, 0, QS_INJ_WR, grp); | |
584 | skb_tx_timestamp(skb); | |
585 | ||
586 | dev->stats.tx_packets++; | |
587 | dev->stats.tx_bytes += skb->len; | |
588 | ||
e2f9a8fe | 589 | kfree_skb(skb); |
9c90eea3 | 590 | |
9c90eea3 VO |
591 | return NETDEV_TX_OK; |
592 | } | |
593 | ||
ca0b272b VO |
594 | enum ocelot_action_type { |
595 | OCELOT_MACT_LEARN, | |
596 | OCELOT_MACT_FORGET, | |
597 | }; | |
598 | ||
599 | struct ocelot_mact_work_ctx { | |
600 | struct work_struct work; | |
601 | struct ocelot *ocelot; | |
602 | enum ocelot_action_type type; | |
603 | union { | |
604 | /* OCELOT_MACT_LEARN */ | |
605 | struct { | |
606 | unsigned char addr[ETH_ALEN]; | |
607 | u16 vid; | |
608 | enum macaccess_entry_type entry_type; | |
609 | int pgid; | |
610 | } learn; | |
611 | /* OCELOT_MACT_FORGET */ | |
612 | struct { | |
613 | unsigned char addr[ETH_ALEN]; | |
614 | u16 vid; | |
615 | } forget; | |
616 | }; | |
617 | }; | |
618 | ||
619 | #define ocelot_work_to_ctx(x) \ | |
620 | container_of((x), struct ocelot_mact_work_ctx, work) | |
621 | ||
622 | static void ocelot_mact_work(struct work_struct *work) | |
623 | { | |
624 | struct ocelot_mact_work_ctx *w = ocelot_work_to_ctx(work); | |
625 | struct ocelot *ocelot = w->ocelot; | |
626 | ||
627 | switch (w->type) { | |
628 | case OCELOT_MACT_LEARN: | |
629 | ocelot_mact_learn(ocelot, w->learn.pgid, w->learn.addr, | |
630 | w->learn.vid, w->learn.entry_type); | |
631 | break; | |
632 | case OCELOT_MACT_FORGET: | |
633 | ocelot_mact_forget(ocelot, w->forget.addr, w->forget.vid); | |
634 | break; | |
635 | default: | |
636 | break; | |
20efd2c7 | 637 | } |
ca0b272b VO |
638 | |
639 | kfree(w); | |
640 | } | |
641 | ||
642 | static int ocelot_enqueue_mact_action(struct ocelot *ocelot, | |
643 | const struct ocelot_mact_work_ctx *ctx) | |
644 | { | |
645 | struct ocelot_mact_work_ctx *w = kmemdup(ctx, sizeof(*w), GFP_ATOMIC); | |
646 | ||
647 | if (!w) | |
648 | return -ENOMEM; | |
649 | ||
650 | w->ocelot = ocelot; | |
651 | INIT_WORK(&w->work, ocelot_mact_work); | |
652 | queue_work(ocelot->owq, &w->work); | |
653 | ||
654 | return 0; | |
655 | } | |
656 | ||
9c90eea3 VO |
657 | static int ocelot_mc_unsync(struct net_device *dev, const unsigned char *addr) |
658 | { | |
659 | struct ocelot_port_private *priv = netdev_priv(dev); | |
660 | struct ocelot_port *ocelot_port = &priv->port; | |
661 | struct ocelot *ocelot = ocelot_port->ocelot; | |
ca0b272b VO |
662 | struct ocelot_mact_work_ctx w; |
663 | ||
664 | ether_addr_copy(w.forget.addr, addr); | |
665 | w.forget.vid = ocelot_port->pvid_vlan.vid; | |
666 | w.type = OCELOT_MACT_FORGET; | |
9c90eea3 | 667 | |
ca0b272b | 668 | return ocelot_enqueue_mact_action(ocelot, &w); |
9c90eea3 VO |
669 | } |
670 | ||
671 | static int ocelot_mc_sync(struct net_device *dev, const unsigned char *addr) | |
672 | { | |
673 | struct ocelot_port_private *priv = netdev_priv(dev); | |
674 | struct ocelot_port *ocelot_port = &priv->port; | |
675 | struct ocelot *ocelot = ocelot_port->ocelot; | |
ca0b272b VO |
676 | struct ocelot_mact_work_ctx w; |
677 | ||
678 | ether_addr_copy(w.learn.addr, addr); | |
679 | w.learn.vid = ocelot_port->pvid_vlan.vid; | |
680 | w.learn.pgid = PGID_CPU; | |
681 | w.learn.entry_type = ENTRYTYPE_LOCKED; | |
682 | w.type = OCELOT_MACT_LEARN; | |
9c90eea3 | 683 | |
ca0b272b | 684 | return ocelot_enqueue_mact_action(ocelot, &w); |
9c90eea3 VO |
685 | } |
686 | ||
687 | static void ocelot_set_rx_mode(struct net_device *dev) | |
688 | { | |
689 | struct ocelot_port_private *priv = netdev_priv(dev); | |
690 | struct ocelot *ocelot = priv->port.ocelot; | |
691 | u32 val; | |
692 | int i; | |
693 | ||
694 | /* This doesn't handle promiscuous mode because the bridge core is | |
695 | * setting IFF_PROMISC on all slave interfaces and all frames would be | |
696 | * forwarded to the CPU port. | |
697 | */ | |
698 | val = GENMASK(ocelot->num_phys_ports - 1, 0); | |
96b029b0 | 699 | for_each_nonreserved_multicast_dest_pgid(ocelot, i) |
9c90eea3 VO |
700 | ocelot_write_rix(ocelot, val, ANA_PGID_PGID, i); |
701 | ||
702 | __dev_mc_sync(dev, ocelot_mc_sync, ocelot_mc_unsync); | |
703 | } | |
704 | ||
9c90eea3 VO |
705 | static int ocelot_port_set_mac_address(struct net_device *dev, void *p) |
706 | { | |
707 | struct ocelot_port_private *priv = netdev_priv(dev); | |
708 | struct ocelot_port *ocelot_port = &priv->port; | |
709 | struct ocelot *ocelot = ocelot_port->ocelot; | |
710 | const struct sockaddr *addr = p; | |
711 | ||
712 | /* Learn the new net device MAC address in the mac table. */ | |
c3e58a75 VO |
713 | ocelot_mact_learn(ocelot, PGID_CPU, addr->sa_data, |
714 | ocelot_port->pvid_vlan.vid, ENTRYTYPE_LOCKED); | |
9c90eea3 | 715 | /* Then forget the previous one. */ |
c3e58a75 | 716 | ocelot_mact_forget(ocelot, dev->dev_addr, ocelot_port->pvid_vlan.vid); |
9c90eea3 VO |
717 | |
718 | ether_addr_copy(dev->dev_addr, addr->sa_data); | |
719 | return 0; | |
720 | } | |
721 | ||
722 | static void ocelot_get_stats64(struct net_device *dev, | |
723 | struct rtnl_link_stats64 *stats) | |
724 | { | |
725 | struct ocelot_port_private *priv = netdev_priv(dev); | |
726 | struct ocelot *ocelot = priv->port.ocelot; | |
727 | int port = priv->chip_port; | |
728 | ||
729 | /* Configure the port to read the stats from */ | |
730 | ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port), | |
731 | SYS_STAT_CFG); | |
732 | ||
733 | /* Get Rx stats */ | |
734 | stats->rx_bytes = ocelot_read(ocelot, SYS_COUNT_RX_OCTETS); | |
735 | stats->rx_packets = ocelot_read(ocelot, SYS_COUNT_RX_SHORTS) + | |
736 | ocelot_read(ocelot, SYS_COUNT_RX_FRAGMENTS) + | |
737 | ocelot_read(ocelot, SYS_COUNT_RX_JABBERS) + | |
738 | ocelot_read(ocelot, SYS_COUNT_RX_LONGS) + | |
739 | ocelot_read(ocelot, SYS_COUNT_RX_64) + | |
740 | ocelot_read(ocelot, SYS_COUNT_RX_65_127) + | |
741 | ocelot_read(ocelot, SYS_COUNT_RX_128_255) + | |
742 | ocelot_read(ocelot, SYS_COUNT_RX_256_1023) + | |
743 | ocelot_read(ocelot, SYS_COUNT_RX_1024_1526) + | |
744 | ocelot_read(ocelot, SYS_COUNT_RX_1527_MAX); | |
745 | stats->multicast = ocelot_read(ocelot, SYS_COUNT_RX_MULTICAST); | |
746 | stats->rx_dropped = dev->stats.rx_dropped; | |
747 | ||
748 | /* Get Tx stats */ | |
749 | stats->tx_bytes = ocelot_read(ocelot, SYS_COUNT_TX_OCTETS); | |
750 | stats->tx_packets = ocelot_read(ocelot, SYS_COUNT_TX_64) + | |
751 | ocelot_read(ocelot, SYS_COUNT_TX_65_127) + | |
752 | ocelot_read(ocelot, SYS_COUNT_TX_128_511) + | |
753 | ocelot_read(ocelot, SYS_COUNT_TX_512_1023) + | |
754 | ocelot_read(ocelot, SYS_COUNT_TX_1024_1526) + | |
755 | ocelot_read(ocelot, SYS_COUNT_TX_1527_MAX); | |
756 | stats->tx_dropped = ocelot_read(ocelot, SYS_COUNT_TX_DROPS) + | |
757 | ocelot_read(ocelot, SYS_COUNT_TX_AGING); | |
758 | stats->collisions = ocelot_read(ocelot, SYS_COUNT_TX_COLLISION); | |
759 | } | |
760 | ||
761 | static int ocelot_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], | |
762 | struct net_device *dev, | |
763 | const unsigned char *addr, | |
764 | u16 vid, u16 flags, | |
765 | struct netlink_ext_ack *extack) | |
766 | { | |
767 | struct ocelot_port_private *priv = netdev_priv(dev); | |
768 | struct ocelot *ocelot = priv->port.ocelot; | |
769 | int port = priv->chip_port; | |
770 | ||
771 | return ocelot_fdb_add(ocelot, port, addr, vid); | |
772 | } | |
773 | ||
774 | static int ocelot_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], | |
775 | struct net_device *dev, | |
776 | const unsigned char *addr, u16 vid) | |
777 | { | |
778 | struct ocelot_port_private *priv = netdev_priv(dev); | |
779 | struct ocelot *ocelot = priv->port.ocelot; | |
780 | int port = priv->chip_port; | |
781 | ||
782 | return ocelot_fdb_del(ocelot, port, addr, vid); | |
783 | } | |
784 | ||
785 | static int ocelot_port_fdb_dump(struct sk_buff *skb, | |
786 | struct netlink_callback *cb, | |
787 | struct net_device *dev, | |
788 | struct net_device *filter_dev, int *idx) | |
789 | { | |
790 | struct ocelot_port_private *priv = netdev_priv(dev); | |
791 | struct ocelot *ocelot = priv->port.ocelot; | |
792 | struct ocelot_dump_ctx dump = { | |
793 | .dev = dev, | |
794 | .skb = skb, | |
795 | .cb = cb, | |
796 | .idx = *idx, | |
797 | }; | |
798 | int port = priv->chip_port; | |
799 | int ret; | |
800 | ||
801 | ret = ocelot_fdb_dump(ocelot, port, ocelot_port_fdb_do_dump, &dump); | |
802 | ||
803 | *idx = dump.idx; | |
804 | ||
805 | return ret; | |
806 | } | |
807 | ||
808 | static int ocelot_vlan_rx_add_vid(struct net_device *dev, __be16 proto, | |
809 | u16 vid) | |
810 | { | |
811 | return ocelot_vlan_vid_add(dev, vid, false, false); | |
812 | } | |
813 | ||
814 | static int ocelot_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, | |
815 | u16 vid) | |
816 | { | |
817 | return ocelot_vlan_vid_del(dev, vid); | |
818 | } | |
819 | ||
820 | static void ocelot_vlan_mode(struct ocelot *ocelot, int port, | |
821 | netdev_features_t features) | |
822 | { | |
823 | u32 val; | |
824 | ||
825 | /* Filtering */ | |
826 | val = ocelot_read(ocelot, ANA_VLANMASK); | |
827 | if (features & NETIF_F_HW_VLAN_CTAG_FILTER) | |
828 | val |= BIT(port); | |
829 | else | |
830 | val &= ~BIT(port); | |
831 | ocelot_write(ocelot, val, ANA_VLANMASK); | |
832 | } | |
833 | ||
834 | static int ocelot_set_features(struct net_device *dev, | |
835 | netdev_features_t features) | |
836 | { | |
837 | netdev_features_t changed = dev->features ^ features; | |
838 | struct ocelot_port_private *priv = netdev_priv(dev); | |
839 | struct ocelot *ocelot = priv->port.ocelot; | |
840 | int port = priv->chip_port; | |
841 | ||
842 | if ((dev->features & NETIF_F_HW_TC) > (features & NETIF_F_HW_TC) && | |
843 | priv->tc.offload_cnt) { | |
844 | netdev_err(dev, | |
845 | "Cannot disable HW TC offload while offloads active\n"); | |
846 | return -EBUSY; | |
847 | } | |
848 | ||
849 | if (changed & NETIF_F_HW_VLAN_CTAG_FILTER) | |
850 | ocelot_vlan_mode(ocelot, port, features); | |
851 | ||
852 | return 0; | |
853 | } | |
854 | ||
9c90eea3 VO |
855 | static int ocelot_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) |
856 | { | |
857 | struct ocelot_port_private *priv = netdev_priv(dev); | |
858 | struct ocelot *ocelot = priv->port.ocelot; | |
859 | int port = priv->chip_port; | |
860 | ||
861 | /* If the attached PHY device isn't capable of timestamping operations, | |
862 | * use our own (when possible). | |
863 | */ | |
864 | if (!phy_has_hwtstamp(dev->phydev) && ocelot->ptp) { | |
865 | switch (cmd) { | |
866 | case SIOCSHWTSTAMP: | |
867 | return ocelot_hwstamp_set(ocelot, port, ifr); | |
868 | case SIOCGHWTSTAMP: | |
869 | return ocelot_hwstamp_get(ocelot, port, ifr); | |
870 | } | |
871 | } | |
872 | ||
873 | return phy_mii_ioctl(dev->phydev, ifr, cmd); | |
874 | } | |
875 | ||
876 | static const struct net_device_ops ocelot_port_netdev_ops = { | |
877 | .ndo_open = ocelot_port_open, | |
878 | .ndo_stop = ocelot_port_stop, | |
879 | .ndo_start_xmit = ocelot_port_xmit, | |
880 | .ndo_set_rx_mode = ocelot_set_rx_mode, | |
9c90eea3 VO |
881 | .ndo_set_mac_address = ocelot_port_set_mac_address, |
882 | .ndo_get_stats64 = ocelot_get_stats64, | |
883 | .ndo_fdb_add = ocelot_port_fdb_add, | |
884 | .ndo_fdb_del = ocelot_port_fdb_del, | |
885 | .ndo_fdb_dump = ocelot_port_fdb_dump, | |
886 | .ndo_vlan_rx_add_vid = ocelot_vlan_rx_add_vid, | |
887 | .ndo_vlan_rx_kill_vid = ocelot_vlan_rx_kill_vid, | |
888 | .ndo_set_features = ocelot_set_features, | |
9c90eea3 VO |
889 | .ndo_setup_tc = ocelot_setup_tc, |
890 | .ndo_do_ioctl = ocelot_ioctl, | |
6c30384e | 891 | .ndo_get_devlink_port = ocelot_get_devlink_port, |
9c90eea3 VO |
892 | }; |
893 | ||
319e4dd1 VO |
894 | struct net_device *ocelot_port_to_netdev(struct ocelot *ocelot, int port) |
895 | { | |
896 | struct ocelot_port *ocelot_port = ocelot->ports[port]; | |
897 | struct ocelot_port_private *priv; | |
898 | ||
899 | if (!ocelot_port) | |
900 | return NULL; | |
901 | ||
902 | priv = container_of(ocelot_port, struct ocelot_port_private, port); | |
903 | ||
904 | return priv->dev; | |
905 | } | |
906 | ||
7e38b03f VO |
907 | /* Checks if the net_device instance given to us originates from our driver */ |
908 | static bool ocelot_netdevice_dev_check(const struct net_device *dev) | |
319e4dd1 VO |
909 | { |
910 | return dev->netdev_ops == &ocelot_port_netdev_ops; | |
911 | } | |
912 | ||
913 | int ocelot_netdev_to_port(struct net_device *dev) | |
914 | { | |
915 | struct ocelot_port_private *priv; | |
916 | ||
7e38b03f | 917 | if (!dev || !ocelot_netdevice_dev_check(dev)) |
319e4dd1 VO |
918 | return -EINVAL; |
919 | ||
920 | priv = netdev_priv(dev); | |
921 | ||
922 | return priv->chip_port; | |
923 | } | |
924 | ||
9c90eea3 VO |
925 | static void ocelot_port_get_strings(struct net_device *netdev, u32 sset, |
926 | u8 *data) | |
927 | { | |
928 | struct ocelot_port_private *priv = netdev_priv(netdev); | |
929 | struct ocelot *ocelot = priv->port.ocelot; | |
930 | int port = priv->chip_port; | |
931 | ||
932 | ocelot_get_strings(ocelot, port, sset, data); | |
933 | } | |
934 | ||
935 | static void ocelot_port_get_ethtool_stats(struct net_device *dev, | |
936 | struct ethtool_stats *stats, | |
937 | u64 *data) | |
938 | { | |
939 | struct ocelot_port_private *priv = netdev_priv(dev); | |
940 | struct ocelot *ocelot = priv->port.ocelot; | |
941 | int port = priv->chip_port; | |
942 | ||
943 | ocelot_get_ethtool_stats(ocelot, port, data); | |
944 | } | |
945 | ||
946 | static int ocelot_port_get_sset_count(struct net_device *dev, int sset) | |
947 | { | |
948 | struct ocelot_port_private *priv = netdev_priv(dev); | |
949 | struct ocelot *ocelot = priv->port.ocelot; | |
950 | int port = priv->chip_port; | |
951 | ||
952 | return ocelot_get_sset_count(ocelot, port, sset); | |
953 | } | |
954 | ||
955 | static int ocelot_port_get_ts_info(struct net_device *dev, | |
956 | struct ethtool_ts_info *info) | |
957 | { | |
958 | struct ocelot_port_private *priv = netdev_priv(dev); | |
959 | struct ocelot *ocelot = priv->port.ocelot; | |
960 | int port = priv->chip_port; | |
961 | ||
962 | if (!ocelot->ptp) | |
963 | return ethtool_op_get_ts_info(dev, info); | |
964 | ||
965 | return ocelot_get_ts_info(ocelot, port, info); | |
966 | } | |
967 | ||
968 | static const struct ethtool_ops ocelot_ethtool_ops = { | |
969 | .get_strings = ocelot_port_get_strings, | |
970 | .get_ethtool_stats = ocelot_port_get_ethtool_stats, | |
971 | .get_sset_count = ocelot_port_get_sset_count, | |
972 | .get_link_ksettings = phy_ethtool_get_link_ksettings, | |
973 | .set_link_ksettings = phy_ethtool_set_link_ksettings, | |
974 | .get_ts_info = ocelot_port_get_ts_info, | |
975 | }; | |
976 | ||
977 | static void ocelot_port_attr_stp_state_set(struct ocelot *ocelot, int port, | |
9c90eea3 VO |
978 | u8 state) |
979 | { | |
9c90eea3 VO |
980 | ocelot_bridge_stp_state_set(ocelot, port, state); |
981 | } | |
982 | ||
983 | static void ocelot_port_attr_ageing_set(struct ocelot *ocelot, int port, | |
984 | unsigned long ageing_clock_t) | |
985 | { | |
986 | unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t); | |
987 | u32 ageing_time = jiffies_to_msecs(ageing_jiffies); | |
988 | ||
989 | ocelot_set_ageing_time(ocelot, ageing_time); | |
990 | } | |
991 | ||
992 | static void ocelot_port_attr_mc_set(struct ocelot *ocelot, int port, bool mc) | |
993 | { | |
994 | u32 cpu_fwd_mcast = ANA_PORT_CPU_FWD_CFG_CPU_IGMP_REDIR_ENA | | |
995 | ANA_PORT_CPU_FWD_CFG_CPU_MLD_REDIR_ENA | | |
996 | ANA_PORT_CPU_FWD_CFG_CPU_IPMC_CTRL_COPY_ENA; | |
997 | u32 val = 0; | |
998 | ||
999 | if (mc) | |
1000 | val = cpu_fwd_mcast; | |
1001 | ||
1002 | ocelot_rmw_gix(ocelot, val, cpu_fwd_mcast, | |
1003 | ANA_PORT_CPU_FWD_CFG, port); | |
1004 | } | |
1005 | ||
1006 | static int ocelot_port_attr_set(struct net_device *dev, | |
bae33f2b | 1007 | const struct switchdev_attr *attr) |
9c90eea3 VO |
1008 | { |
1009 | struct ocelot_port_private *priv = netdev_priv(dev); | |
1010 | struct ocelot *ocelot = priv->port.ocelot; | |
1011 | int port = priv->chip_port; | |
1012 | int err = 0; | |
1013 | ||
1014 | switch (attr->id) { | |
1015 | case SWITCHDEV_ATTR_ID_PORT_STP_STATE: | |
bae33f2b | 1016 | ocelot_port_attr_stp_state_set(ocelot, port, attr->u.stp_state); |
9c90eea3 VO |
1017 | break; |
1018 | case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: | |
1019 | ocelot_port_attr_ageing_set(ocelot, port, attr->u.ageing_time); | |
1020 | break; | |
1021 | case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING: | |
bae33f2b | 1022 | ocelot_port_vlan_filtering(ocelot, port, attr->u.vlan_filtering); |
9c90eea3 VO |
1023 | break; |
1024 | case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED: | |
1025 | ocelot_port_attr_mc_set(ocelot, port, !attr->u.mc_disabled); | |
1026 | break; | |
1027 | default: | |
1028 | err = -EOPNOTSUPP; | |
1029 | break; | |
1030 | } | |
1031 | ||
1032 | return err; | |
1033 | } | |
1034 | ||
1035 | static int ocelot_port_obj_add_vlan(struct net_device *dev, | |
ffb68fc5 | 1036 | const struct switchdev_obj_port_vlan *vlan) |
9c90eea3 | 1037 | { |
b7a9e0da VO |
1038 | bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; |
1039 | bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; | |
9c90eea3 | 1040 | int ret; |
9c90eea3 | 1041 | |
ffb68fc5 VO |
1042 | ret = ocelot_vlan_vid_prepare(dev, vlan->vid, pvid, untagged); |
1043 | if (ret) | |
1044 | return ret; | |
9c90eea3 | 1045 | |
ffb68fc5 | 1046 | return ocelot_vlan_vid_add(dev, vlan->vid, pvid, untagged); |
9c90eea3 VO |
1047 | } |
1048 | ||
209edf95 | 1049 | static int ocelot_port_obj_add_mdb(struct net_device *dev, |
ffb68fc5 | 1050 | const struct switchdev_obj_port_mdb *mdb) |
209edf95 VO |
1051 | { |
1052 | struct ocelot_port_private *priv = netdev_priv(dev); | |
1053 | struct ocelot_port *ocelot_port = &priv->port; | |
1054 | struct ocelot *ocelot = ocelot_port->ocelot; | |
1055 | int port = priv->chip_port; | |
1056 | ||
209edf95 VO |
1057 | return ocelot_port_mdb_add(ocelot, port, mdb); |
1058 | } | |
1059 | ||
1060 | static int ocelot_port_obj_del_mdb(struct net_device *dev, | |
1061 | const struct switchdev_obj_port_mdb *mdb) | |
1062 | { | |
1063 | struct ocelot_port_private *priv = netdev_priv(dev); | |
1064 | struct ocelot_port *ocelot_port = &priv->port; | |
1065 | struct ocelot *ocelot = ocelot_port->ocelot; | |
1066 | int port = priv->chip_port; | |
1067 | ||
1068 | return ocelot_port_mdb_del(ocelot, port, mdb); | |
1069 | } | |
1070 | ||
9c90eea3 VO |
1071 | static int ocelot_port_obj_add(struct net_device *dev, |
1072 | const struct switchdev_obj *obj, | |
9c90eea3 VO |
1073 | struct netlink_ext_ack *extack) |
1074 | { | |
1075 | int ret = 0; | |
1076 | ||
1077 | switch (obj->id) { | |
1078 | case SWITCHDEV_OBJ_ID_PORT_VLAN: | |
1079 | ret = ocelot_port_obj_add_vlan(dev, | |
ffb68fc5 | 1080 | SWITCHDEV_OBJ_PORT_VLAN(obj)); |
9c90eea3 VO |
1081 | break; |
1082 | case SWITCHDEV_OBJ_ID_PORT_MDB: | |
ffb68fc5 | 1083 | ret = ocelot_port_obj_add_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj)); |
9c90eea3 VO |
1084 | break; |
1085 | default: | |
1086 | return -EOPNOTSUPP; | |
1087 | } | |
1088 | ||
1089 | return ret; | |
1090 | } | |
1091 | ||
1092 | static int ocelot_port_obj_del(struct net_device *dev, | |
1093 | const struct switchdev_obj *obj) | |
1094 | { | |
1095 | int ret = 0; | |
1096 | ||
1097 | switch (obj->id) { | |
1098 | case SWITCHDEV_OBJ_ID_PORT_VLAN: | |
b7a9e0da VO |
1099 | ret = ocelot_vlan_vid_del(dev, |
1100 | SWITCHDEV_OBJ_PORT_VLAN(obj)->vid); | |
9c90eea3 VO |
1101 | break; |
1102 | case SWITCHDEV_OBJ_ID_PORT_MDB: | |
1103 | ret = ocelot_port_obj_del_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj)); | |
1104 | break; | |
1105 | default: | |
1106 | return -EOPNOTSUPP; | |
1107 | } | |
1108 | ||
1109 | return ret; | |
1110 | } | |
1111 | ||
9c90eea3 VO |
1112 | static int ocelot_netdevice_port_event(struct net_device *dev, |
1113 | unsigned long event, | |
1114 | struct netdev_notifier_changeupper_info *info) | |
1115 | { | |
1116 | struct ocelot_port_private *priv = netdev_priv(dev); | |
1117 | struct ocelot_port *ocelot_port = &priv->port; | |
1118 | struct ocelot *ocelot = ocelot_port->ocelot; | |
1119 | int port = priv->chip_port; | |
1120 | int err = 0; | |
1121 | ||
1122 | switch (event) { | |
1123 | case NETDEV_CHANGEUPPER: | |
1124 | if (netif_is_bridge_master(info->upper_dev)) { | |
1125 | if (info->linking) { | |
1126 | err = ocelot_port_bridge_join(ocelot, port, | |
1127 | info->upper_dev); | |
1128 | } else { | |
1129 | err = ocelot_port_bridge_leave(ocelot, port, | |
1130 | info->upper_dev); | |
1131 | } | |
1132 | } | |
1133 | if (netif_is_lag_master(info->upper_dev)) { | |
1134 | if (info->linking) | |
1135 | err = ocelot_port_lag_join(ocelot, port, | |
1136 | info->upper_dev); | |
1137 | else | |
1138 | ocelot_port_lag_leave(ocelot, port, | |
1139 | info->upper_dev); | |
1140 | } | |
1141 | break; | |
1142 | default: | |
1143 | break; | |
1144 | } | |
1145 | ||
1146 | return err; | |
1147 | } | |
1148 | ||
1149 | static int ocelot_netdevice_event(struct notifier_block *unused, | |
1150 | unsigned long event, void *ptr) | |
1151 | { | |
1152 | struct netdev_notifier_changeupper_info *info = ptr; | |
1153 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); | |
1154 | int ret = 0; | |
1155 | ||
9c90eea3 | 1156 | if (event == NETDEV_PRECHANGEUPPER && |
79267ae2 | 1157 | ocelot_netdevice_dev_check(dev) && |
9c90eea3 VO |
1158 | netif_is_lag_master(info->upper_dev)) { |
1159 | struct netdev_lag_upper_info *lag_upper_info = info->upper_info; | |
1160 | struct netlink_ext_ack *extack; | |
1161 | ||
1162 | if (lag_upper_info && | |
1163 | lag_upper_info->tx_type != NETDEV_LAG_TX_TYPE_HASH) { | |
1164 | extack = netdev_notifier_info_to_extack(&info->info); | |
1165 | NL_SET_ERR_MSG_MOD(extack, "LAG device using unsupported Tx type"); | |
1166 | ||
1167 | ret = -EINVAL; | |
1168 | goto notify; | |
1169 | } | |
1170 | } | |
1171 | ||
1172 | if (netif_is_lag_master(dev)) { | |
1173 | struct net_device *slave; | |
1174 | struct list_head *iter; | |
1175 | ||
1176 | netdev_for_each_lower_dev(dev, slave, iter) { | |
1177 | ret = ocelot_netdevice_port_event(slave, event, info); | |
1178 | if (ret) | |
1179 | goto notify; | |
1180 | } | |
1181 | } else { | |
1182 | ret = ocelot_netdevice_port_event(dev, event, info); | |
1183 | } | |
1184 | ||
1185 | notify: | |
1186 | return notifier_from_errno(ret); | |
1187 | } | |
1188 | ||
1189 | struct notifier_block ocelot_netdevice_nb __read_mostly = { | |
1190 | .notifier_call = ocelot_netdevice_event, | |
1191 | }; | |
9c90eea3 VO |
1192 | |
1193 | static int ocelot_switchdev_event(struct notifier_block *unused, | |
1194 | unsigned long event, void *ptr) | |
1195 | { | |
1196 | struct net_device *dev = switchdev_notifier_info_to_dev(ptr); | |
1197 | int err; | |
1198 | ||
1199 | switch (event) { | |
1200 | case SWITCHDEV_PORT_ATTR_SET: | |
1201 | err = switchdev_handle_port_attr_set(dev, ptr, | |
1202 | ocelot_netdevice_dev_check, | |
1203 | ocelot_port_attr_set); | |
1204 | return notifier_from_errno(err); | |
1205 | } | |
1206 | ||
1207 | return NOTIFY_DONE; | |
1208 | } | |
1209 | ||
1210 | struct notifier_block ocelot_switchdev_nb __read_mostly = { | |
1211 | .notifier_call = ocelot_switchdev_event, | |
1212 | }; | |
9c90eea3 VO |
1213 | |
1214 | static int ocelot_switchdev_blocking_event(struct notifier_block *unused, | |
1215 | unsigned long event, void *ptr) | |
1216 | { | |
1217 | struct net_device *dev = switchdev_notifier_info_to_dev(ptr); | |
1218 | int err; | |
1219 | ||
1220 | switch (event) { | |
1221 | /* Blocking events. */ | |
1222 | case SWITCHDEV_PORT_OBJ_ADD: | |
1223 | err = switchdev_handle_port_obj_add(dev, ptr, | |
1224 | ocelot_netdevice_dev_check, | |
1225 | ocelot_port_obj_add); | |
1226 | return notifier_from_errno(err); | |
1227 | case SWITCHDEV_PORT_OBJ_DEL: | |
1228 | err = switchdev_handle_port_obj_del(dev, ptr, | |
1229 | ocelot_netdevice_dev_check, | |
1230 | ocelot_port_obj_del); | |
1231 | return notifier_from_errno(err); | |
1232 | case SWITCHDEV_PORT_ATTR_SET: | |
1233 | err = switchdev_handle_port_attr_set(dev, ptr, | |
1234 | ocelot_netdevice_dev_check, | |
1235 | ocelot_port_attr_set); | |
1236 | return notifier_from_errno(err); | |
1237 | } | |
1238 | ||
1239 | return NOTIFY_DONE; | |
1240 | } | |
1241 | ||
1242 | struct notifier_block ocelot_switchdev_blocking_nb __read_mostly = { | |
1243 | .notifier_call = ocelot_switchdev_blocking_event, | |
1244 | }; | |
9c90eea3 | 1245 | |
91c724cf | 1246 | int ocelot_probe_port(struct ocelot *ocelot, int port, struct regmap *target, |
9c90eea3 VO |
1247 | struct phy_device *phy) |
1248 | { | |
1249 | struct ocelot_port_private *priv; | |
1250 | struct ocelot_port *ocelot_port; | |
1251 | struct net_device *dev; | |
1252 | int err; | |
1253 | ||
1254 | dev = alloc_etherdev(sizeof(struct ocelot_port_private)); | |
1255 | if (!dev) | |
1256 | return -ENOMEM; | |
1257 | SET_NETDEV_DEV(dev, ocelot->dev); | |
1258 | priv = netdev_priv(dev); | |
1259 | priv->dev = dev; | |
1260 | priv->phy = phy; | |
1261 | priv->chip_port = port; | |
1262 | ocelot_port = &priv->port; | |
1263 | ocelot_port->ocelot = ocelot; | |
91c724cf | 1264 | ocelot_port->target = target; |
9c90eea3 VO |
1265 | ocelot->ports[port] = ocelot_port; |
1266 | ||
1267 | dev->netdev_ops = &ocelot_port_netdev_ops; | |
1268 | dev->ethtool_ops = &ocelot_ethtool_ops; | |
1269 | ||
1270 | dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXFCS | | |
1271 | NETIF_F_HW_TC; | |
1272 | dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_TC; | |
1273 | ||
1274 | memcpy(dev->dev_addr, ocelot->base_mac, ETH_ALEN); | |
1275 | dev->dev_addr[ETH_ALEN - 1] += port; | |
c3e58a75 VO |
1276 | ocelot_mact_learn(ocelot, PGID_CPU, dev->dev_addr, |
1277 | ocelot_port->pvid_vlan.vid, ENTRYTYPE_LOCKED); | |
9c90eea3 VO |
1278 | |
1279 | ocelot_init_port(ocelot, port); | |
1280 | ||
1281 | err = register_netdev(dev); | |
1282 | if (err) { | |
1283 | dev_err(ocelot->dev, "register_netdev failed\n"); | |
1284 | free_netdev(dev); | |
1285 | } | |
1286 | ||
1287 | return err; | |
1288 | } |