1
2
3
4#include <linux/kernel.h>
5#include <linux/module.h>
6#include <linux/types.h>
7#include <linux/pci.h>
8#include <linux/netdevice.h>
9#include <linux/etherdevice.h>
10#include <linux/slab.h>
11#include <linux/device.h>
12#include <linux/skbuff.h>
13#include <linux/if_vlan.h>
14
15#include "pci.h"
16#include "core.h"
17#include "reg.h"
18#include "port.h"
19#include "trap.h"
20#include "txheader.h"
21#include "ib.h"
22
23static const char mlxsw_sx_driver_name[] = "mlxsw_switchx2";
24static const char mlxsw_sx_driver_version[] = "1.0";
25
26struct mlxsw_sx_port;
27
28struct mlxsw_sx {
29 struct mlxsw_sx_port **ports;
30 struct mlxsw_core *core;
31 const struct mlxsw_bus_info *bus_info;
32 u8 hw_id[ETH_ALEN];
33};
34
35struct mlxsw_sx_port_pcpu_stats {
36 u64 rx_packets;
37 u64 rx_bytes;
38 u64 tx_packets;
39 u64 tx_bytes;
40 struct u64_stats_sync syncp;
41 u32 tx_dropped;
42};
43
44struct mlxsw_sx_port {
45 struct net_device *dev;
46 struct mlxsw_sx_port_pcpu_stats __percpu *pcpu_stats;
47 struct mlxsw_sx *mlxsw_sx;
48 u8 local_port;
49 struct {
50 u8 module;
51 } mapping;
52};
53
54
55
56
57
58MLXSW_ITEM32(tx, hdr, version, 0x00, 28, 4);
59
60
61
62
63
64
65MLXSW_ITEM32(tx, hdr, ctl, 0x00, 26, 2);
66
67
68
69
70MLXSW_ITEM32(tx, hdr, proto, 0x00, 21, 3);
71
72
73
74
75
76
77MLXSW_ITEM32(tx, hdr, etclass, 0x00, 18, 3);
78
79
80
81
82MLXSW_ITEM32(tx, hdr, swid, 0x00, 12, 3);
83
84
85
86
87
88
89
90
91
92MLXSW_ITEM32(tx, hdr, port_mid, 0x04, 16, 16);
93
94
95
96
97MLXSW_ITEM32(tx, hdr, ctclass3, 0x04, 14, 1);
98
99
100
101
102
103MLXSW_ITEM32(tx, hdr, rdq, 0x04, 9, 5);
104
105
106
107
108MLXSW_ITEM32(tx, hdr, cpu_sig, 0x04, 0, 9);
109
110
111
112
113MLXSW_ITEM32(tx, hdr, sig, 0x0C, 16, 16);
114
115
116
117
118MLXSW_ITEM32(tx, hdr, stclass, 0x0C, 13, 3);
119
120
121
122
123MLXSW_ITEM32(tx, hdr, emad, 0x0C, 5, 1);
124
125
126
127
128
129MLXSW_ITEM32(tx, hdr, type, 0x0C, 0, 4);
130
131static void mlxsw_sx_txhdr_construct(struct sk_buff *skb,
132 const struct mlxsw_tx_info *tx_info)
133{
134 char *txhdr = skb_push(skb, MLXSW_TXHDR_LEN);
135 bool is_emad = tx_info->is_emad;
136
137 memset(txhdr, 0, MLXSW_TXHDR_LEN);
138
139
140 mlxsw_tx_hdr_version_set(txhdr, MLXSW_TXHDR_VERSION_0);
141 mlxsw_tx_hdr_ctl_set(txhdr, MLXSW_TXHDR_ETH_CTL);
142 mlxsw_tx_hdr_proto_set(txhdr, MLXSW_TXHDR_PROTO_ETH);
143 mlxsw_tx_hdr_etclass_set(txhdr, is_emad ? MLXSW_TXHDR_ETCLASS_6 :
144 MLXSW_TXHDR_ETCLASS_5);
145 mlxsw_tx_hdr_swid_set(txhdr, 0);
146 mlxsw_tx_hdr_port_mid_set(txhdr, tx_info->local_port);
147 mlxsw_tx_hdr_ctclass3_set(txhdr, MLXSW_TXHDR_CTCLASS3);
148 mlxsw_tx_hdr_rdq_set(txhdr, is_emad ? MLXSW_TXHDR_RDQ_EMAD :
149 MLXSW_TXHDR_RDQ_OTHER);
150 mlxsw_tx_hdr_cpu_sig_set(txhdr, MLXSW_TXHDR_CPU_SIG);
151 mlxsw_tx_hdr_sig_set(txhdr, MLXSW_TXHDR_SIG);
152 mlxsw_tx_hdr_stclass_set(txhdr, MLXSW_TXHDR_STCLASS_NONE);
153 mlxsw_tx_hdr_emad_set(txhdr, is_emad ? MLXSW_TXHDR_EMAD :
154 MLXSW_TXHDR_NOT_EMAD);
155 mlxsw_tx_hdr_type_set(txhdr, MLXSW_TXHDR_TYPE_CONTROL);
156}
157
158static int mlxsw_sx_port_admin_status_set(struct mlxsw_sx_port *mlxsw_sx_port,
159 bool is_up)
160{
161 struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
162 char paos_pl[MLXSW_REG_PAOS_LEN];
163
164 mlxsw_reg_paos_pack(paos_pl, mlxsw_sx_port->local_port,
165 is_up ? MLXSW_PORT_ADMIN_STATUS_UP :
166 MLXSW_PORT_ADMIN_STATUS_DOWN);
167 return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(paos), paos_pl);
168}
169
170static int mlxsw_sx_port_oper_status_get(struct mlxsw_sx_port *mlxsw_sx_port,
171 bool *p_is_up)
172{
173 struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
174 char paos_pl[MLXSW_REG_PAOS_LEN];
175 u8 oper_status;
176 int err;
177
178 mlxsw_reg_paos_pack(paos_pl, mlxsw_sx_port->local_port, 0);
179 err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(paos), paos_pl);
180 if (err)
181 return err;
182 oper_status = mlxsw_reg_paos_oper_status_get(paos_pl);
183 *p_is_up = oper_status == MLXSW_PORT_ADMIN_STATUS_UP ? true : false;
184 return 0;
185}
186
187static int __mlxsw_sx_port_mtu_set(struct mlxsw_sx_port *mlxsw_sx_port,
188 u16 mtu)
189{
190 struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
191 char pmtu_pl[MLXSW_REG_PMTU_LEN];
192 int max_mtu;
193 int err;
194
195 mlxsw_reg_pmtu_pack(pmtu_pl, mlxsw_sx_port->local_port, 0);
196 err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(pmtu), pmtu_pl);
197 if (err)
198 return err;
199 max_mtu = mlxsw_reg_pmtu_max_mtu_get(pmtu_pl);
200
201 if (mtu > max_mtu)
202 return -EINVAL;
203
204 mlxsw_reg_pmtu_pack(pmtu_pl, mlxsw_sx_port->local_port, mtu);
205 return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(pmtu), pmtu_pl);
206}
207
208static int mlxsw_sx_port_mtu_eth_set(struct mlxsw_sx_port *mlxsw_sx_port,
209 u16 mtu)
210{
211 mtu += MLXSW_TXHDR_LEN + ETH_HLEN;
212 return __mlxsw_sx_port_mtu_set(mlxsw_sx_port, mtu);
213}
214
215static int mlxsw_sx_port_mtu_ib_set(struct mlxsw_sx_port *mlxsw_sx_port,
216 u16 mtu)
217{
218 return __mlxsw_sx_port_mtu_set(mlxsw_sx_port, mtu);
219}
220
221static int mlxsw_sx_port_ib_port_set(struct mlxsw_sx_port *mlxsw_sx_port,
222 u8 ib_port)
223{
224 struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
225 char plib_pl[MLXSW_REG_PLIB_LEN] = {0};
226 int err;
227
228 mlxsw_reg_plib_local_port_set(plib_pl, mlxsw_sx_port->local_port);
229 mlxsw_reg_plib_ib_port_set(plib_pl, ib_port);
230 err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(plib), plib_pl);
231 return err;
232}
233
234static int mlxsw_sx_port_swid_set(struct mlxsw_sx_port *mlxsw_sx_port, u8 swid)
235{
236 struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
237 char pspa_pl[MLXSW_REG_PSPA_LEN];
238
239 mlxsw_reg_pspa_pack(pspa_pl, swid, mlxsw_sx_port->local_port);
240 return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(pspa), pspa_pl);
241}
242
243static int
244mlxsw_sx_port_system_port_mapping_set(struct mlxsw_sx_port *mlxsw_sx_port)
245{
246 struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
247 char sspr_pl[MLXSW_REG_SSPR_LEN];
248
249 mlxsw_reg_sspr_pack(sspr_pl, mlxsw_sx_port->local_port);
250 return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sspr), sspr_pl);
251}
252
253static int mlxsw_sx_port_module_info_get(struct mlxsw_sx *mlxsw_sx,
254 u8 local_port, u8 *p_module,
255 u8 *p_width)
256{
257 char pmlp_pl[MLXSW_REG_PMLP_LEN];
258 int err;
259
260 mlxsw_reg_pmlp_pack(pmlp_pl, local_port);
261 err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(pmlp), pmlp_pl);
262 if (err)
263 return err;
264 *p_module = mlxsw_reg_pmlp_module_get(pmlp_pl, 0);
265 *p_width = mlxsw_reg_pmlp_width_get(pmlp_pl);
266 return 0;
267}
268
269static int mlxsw_sx_port_open(struct net_device *dev)
270{
271 struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
272 int err;
273
274 err = mlxsw_sx_port_admin_status_set(mlxsw_sx_port, true);
275 if (err)
276 return err;
277 netif_start_queue(dev);
278 return 0;
279}
280
281static int mlxsw_sx_port_stop(struct net_device *dev)
282{
283 struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
284
285 netif_stop_queue(dev);
286 return mlxsw_sx_port_admin_status_set(mlxsw_sx_port, false);
287}
288
289static netdev_tx_t mlxsw_sx_port_xmit(struct sk_buff *skb,
290 struct net_device *dev)
291{
292 struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
293 struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
294 struct mlxsw_sx_port_pcpu_stats *pcpu_stats;
295 const struct mlxsw_tx_info tx_info = {
296 .local_port = mlxsw_sx_port->local_port,
297 .is_emad = false,
298 };
299 u64 len;
300 int err;
301
302 memset(skb->cb, 0, sizeof(struct mlxsw_skb_cb));
303
304 if (mlxsw_core_skb_transmit_busy(mlxsw_sx->core, &tx_info))
305 return NETDEV_TX_BUSY;
306
307 if (unlikely(skb_headroom(skb) < MLXSW_TXHDR_LEN)) {
308 struct sk_buff *skb_orig = skb;
309
310 skb = skb_realloc_headroom(skb, MLXSW_TXHDR_LEN);
311 if (!skb) {
312 this_cpu_inc(mlxsw_sx_port->pcpu_stats->tx_dropped);
313 dev_kfree_skb_any(skb_orig);
314 return NETDEV_TX_OK;
315 }
316 dev_consume_skb_any(skb_orig);
317 }
318 mlxsw_sx_txhdr_construct(skb, &tx_info);
319
320
321
322 len = skb->len - MLXSW_TXHDR_LEN;
323
324
325
326 err = mlxsw_core_skb_transmit(mlxsw_sx->core, skb, &tx_info);
327
328 if (!err) {
329 pcpu_stats = this_cpu_ptr(mlxsw_sx_port->pcpu_stats);
330 u64_stats_update_begin(&pcpu_stats->syncp);
331 pcpu_stats->tx_packets++;
332 pcpu_stats->tx_bytes += len;
333 u64_stats_update_end(&pcpu_stats->syncp);
334 } else {
335 this_cpu_inc(mlxsw_sx_port->pcpu_stats->tx_dropped);
336 dev_kfree_skb_any(skb);
337 }
338 return NETDEV_TX_OK;
339}
340
341static int mlxsw_sx_port_change_mtu(struct net_device *dev, int mtu)
342{
343 struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
344 int err;
345
346 err = mlxsw_sx_port_mtu_eth_set(mlxsw_sx_port, mtu);
347 if (err)
348 return err;
349 dev->mtu = mtu;
350 return 0;
351}
352
353static void
354mlxsw_sx_port_get_stats64(struct net_device *dev,
355 struct rtnl_link_stats64 *stats)
356{
357 struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
358 struct mlxsw_sx_port_pcpu_stats *p;
359 u64 rx_packets, rx_bytes, tx_packets, tx_bytes;
360 u32 tx_dropped = 0;
361 unsigned int start;
362 int i;
363
364 for_each_possible_cpu(i) {
365 p = per_cpu_ptr(mlxsw_sx_port->pcpu_stats, i);
366 do {
367 start = u64_stats_fetch_begin_irq(&p->syncp);
368 rx_packets = p->rx_packets;
369 rx_bytes = p->rx_bytes;
370 tx_packets = p->tx_packets;
371 tx_bytes = p->tx_bytes;
372 } while (u64_stats_fetch_retry_irq(&p->syncp, start));
373
374 stats->rx_packets += rx_packets;
375 stats->rx_bytes += rx_bytes;
376 stats->tx_packets += tx_packets;
377 stats->tx_bytes += tx_bytes;
378
379 tx_dropped += p->tx_dropped;
380 }
381 stats->tx_dropped = tx_dropped;
382}
383
384static struct devlink_port *
385mlxsw_sx_port_get_devlink_port(struct net_device *dev)
386{
387 struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
388 struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
389
390 return mlxsw_core_port_devlink_port_get(mlxsw_sx->core,
391 mlxsw_sx_port->local_port);
392}
393
394static const struct net_device_ops mlxsw_sx_port_netdev_ops = {
395 .ndo_open = mlxsw_sx_port_open,
396 .ndo_stop = mlxsw_sx_port_stop,
397 .ndo_start_xmit = mlxsw_sx_port_xmit,
398 .ndo_change_mtu = mlxsw_sx_port_change_mtu,
399 .ndo_get_stats64 = mlxsw_sx_port_get_stats64,
400 .ndo_get_devlink_port = mlxsw_sx_port_get_devlink_port,
401};
402
403static void mlxsw_sx_port_get_drvinfo(struct net_device *dev,
404 struct ethtool_drvinfo *drvinfo)
405{
406 struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
407 struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
408
409 strlcpy(drvinfo->driver, mlxsw_sx_driver_name, sizeof(drvinfo->driver));
410 strlcpy(drvinfo->version, mlxsw_sx_driver_version,
411 sizeof(drvinfo->version));
412 snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
413 "%d.%d.%d",
414 mlxsw_sx->bus_info->fw_rev.major,
415 mlxsw_sx->bus_info->fw_rev.minor,
416 mlxsw_sx->bus_info->fw_rev.subminor);
417 strlcpy(drvinfo->bus_info, mlxsw_sx->bus_info->device_name,
418 sizeof(drvinfo->bus_info));
419}
420
421struct mlxsw_sx_port_hw_stats {
422 char str[ETH_GSTRING_LEN];
423 u64 (*getter)(const char *payload);
424};
425
426static const struct mlxsw_sx_port_hw_stats mlxsw_sx_port_hw_stats[] = {
427 {
428 .str = "a_frames_transmitted_ok",
429 .getter = mlxsw_reg_ppcnt_a_frames_transmitted_ok_get,
430 },
431 {
432 .str = "a_frames_received_ok",
433 .getter = mlxsw_reg_ppcnt_a_frames_received_ok_get,
434 },
435 {
436 .str = "a_frame_check_sequence_errors",
437 .getter = mlxsw_reg_ppcnt_a_frame_check_sequence_errors_get,
438 },
439 {
440 .str = "a_alignment_errors",
441 .getter = mlxsw_reg_ppcnt_a_alignment_errors_get,
442 },
443 {
444 .str = "a_octets_transmitted_ok",
445 .getter = mlxsw_reg_ppcnt_a_octets_transmitted_ok_get,
446 },
447 {
448 .str = "a_octets_received_ok",
449 .getter = mlxsw_reg_ppcnt_a_octets_received_ok_get,
450 },
451 {
452 .str = "a_multicast_frames_xmitted_ok",
453 .getter = mlxsw_reg_ppcnt_a_multicast_frames_xmitted_ok_get,
454 },
455 {
456 .str = "a_broadcast_frames_xmitted_ok",
457 .getter = mlxsw_reg_ppcnt_a_broadcast_frames_xmitted_ok_get,
458 },
459 {
460 .str = "a_multicast_frames_received_ok",
461 .getter = mlxsw_reg_ppcnt_a_multicast_frames_received_ok_get,
462 },
463 {
464 .str = "a_broadcast_frames_received_ok",
465 .getter = mlxsw_reg_ppcnt_a_broadcast_frames_received_ok_get,
466 },
467 {
468 .str = "a_in_range_length_errors",
469 .getter = mlxsw_reg_ppcnt_a_in_range_length_errors_get,
470 },
471 {
472 .str = "a_out_of_range_length_field",
473 .getter = mlxsw_reg_ppcnt_a_out_of_range_length_field_get,
474 },
475 {
476 .str = "a_frame_too_long_errors",
477 .getter = mlxsw_reg_ppcnt_a_frame_too_long_errors_get,
478 },
479 {
480 .str = "a_symbol_error_during_carrier",
481 .getter = mlxsw_reg_ppcnt_a_symbol_error_during_carrier_get,
482 },
483 {
484 .str = "a_mac_control_frames_transmitted",
485 .getter = mlxsw_reg_ppcnt_a_mac_control_frames_transmitted_get,
486 },
487 {
488 .str = "a_mac_control_frames_received",
489 .getter = mlxsw_reg_ppcnt_a_mac_control_frames_received_get,
490 },
491 {
492 .str = "a_unsupported_opcodes_received",
493 .getter = mlxsw_reg_ppcnt_a_unsupported_opcodes_received_get,
494 },
495 {
496 .str = "a_pause_mac_ctrl_frames_received",
497 .getter = mlxsw_reg_ppcnt_a_pause_mac_ctrl_frames_received_get,
498 },
499 {
500 .str = "a_pause_mac_ctrl_frames_xmitted",
501 .getter = mlxsw_reg_ppcnt_a_pause_mac_ctrl_frames_transmitted_get,
502 },
503};
504
505#define MLXSW_SX_PORT_HW_STATS_LEN ARRAY_SIZE(mlxsw_sx_port_hw_stats)
506
507static void mlxsw_sx_port_get_strings(struct net_device *dev,
508 u32 stringset, u8 *data)
509{
510 u8 *p = data;
511 int i;
512
513 switch (stringset) {
514 case ETH_SS_STATS:
515 for (i = 0; i < MLXSW_SX_PORT_HW_STATS_LEN; i++) {
516 memcpy(p, mlxsw_sx_port_hw_stats[i].str,
517 ETH_GSTRING_LEN);
518 p += ETH_GSTRING_LEN;
519 }
520 break;
521 }
522}
523
524static void mlxsw_sx_port_get_stats(struct net_device *dev,
525 struct ethtool_stats *stats, u64 *data)
526{
527 struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
528 struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
529 char ppcnt_pl[MLXSW_REG_PPCNT_LEN];
530 int i;
531 int err;
532
533 mlxsw_reg_ppcnt_pack(ppcnt_pl, mlxsw_sx_port->local_port,
534 MLXSW_REG_PPCNT_IEEE_8023_CNT, 0);
535 err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(ppcnt), ppcnt_pl);
536 for (i = 0; i < MLXSW_SX_PORT_HW_STATS_LEN; i++)
537 data[i] = !err ? mlxsw_sx_port_hw_stats[i].getter(ppcnt_pl) : 0;
538}
539
540static int mlxsw_sx_port_get_sset_count(struct net_device *dev, int sset)
541{
542 switch (sset) {
543 case ETH_SS_STATS:
544 return MLXSW_SX_PORT_HW_STATS_LEN;
545 default:
546 return -EOPNOTSUPP;
547 }
548}
549
550struct mlxsw_sx_port_link_mode {
551 u32 mask;
552 u32 supported;
553 u32 advertised;
554 u32 speed;
555};
556
557static const struct mlxsw_sx_port_link_mode mlxsw_sx_port_link_mode[] = {
558 {
559 .mask = MLXSW_REG_PTYS_ETH_SPEED_100BASE_T,
560 .supported = SUPPORTED_100baseT_Full,
561 .advertised = ADVERTISED_100baseT_Full,
562 .speed = 100,
563 },
564 {
565 .mask = MLXSW_REG_PTYS_ETH_SPEED_100BASE_TX,
566 .speed = 100,
567 },
568 {
569 .mask = MLXSW_REG_PTYS_ETH_SPEED_SGMII |
570 MLXSW_REG_PTYS_ETH_SPEED_1000BASE_KX,
571 .supported = SUPPORTED_1000baseKX_Full,
572 .advertised = ADVERTISED_1000baseKX_Full,
573 .speed = 1000,
574 },
575 {
576 .mask = MLXSW_REG_PTYS_ETH_SPEED_10GBASE_T,
577 .supported = SUPPORTED_10000baseT_Full,
578 .advertised = ADVERTISED_10000baseT_Full,
579 .speed = 10000,
580 },
581 {
582 .mask = MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CX4 |
583 MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4,
584 .supported = SUPPORTED_10000baseKX4_Full,
585 .advertised = ADVERTISED_10000baseKX4_Full,
586 .speed = 10000,
587 },
588 {
589 .mask = MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR |
590 MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR |
591 MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR |
592 MLXSW_REG_PTYS_ETH_SPEED_10GBASE_ER_LR,
593 .supported = SUPPORTED_10000baseKR_Full,
594 .advertised = ADVERTISED_10000baseKR_Full,
595 .speed = 10000,
596 },
597 {
598 .mask = MLXSW_REG_PTYS_ETH_SPEED_20GBASE_KR2,
599 .supported = SUPPORTED_20000baseKR2_Full,
600 .advertised = ADVERTISED_20000baseKR2_Full,
601 .speed = 20000,
602 },
603 {
604 .mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4,
605 .supported = SUPPORTED_40000baseCR4_Full,
606 .advertised = ADVERTISED_40000baseCR4_Full,
607 .speed = 40000,
608 },
609 {
610 .mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4,
611 .supported = SUPPORTED_40000baseKR4_Full,
612 .advertised = ADVERTISED_40000baseKR4_Full,
613 .speed = 40000,
614 },
615 {
616 .mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4,
617 .supported = SUPPORTED_40000baseSR4_Full,
618 .advertised = ADVERTISED_40000baseSR4_Full,
619 .speed = 40000,
620 },
621 {
622 .mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_LR4_ER4,
623 .supported = SUPPORTED_40000baseLR4_Full,
624 .advertised = ADVERTISED_40000baseLR4_Full,
625 .speed = 40000,
626 },
627 {
628 .mask = MLXSW_REG_PTYS_ETH_SPEED_25GBASE_CR |
629 MLXSW_REG_PTYS_ETH_SPEED_25GBASE_KR |
630 MLXSW_REG_PTYS_ETH_SPEED_25GBASE_SR,
631 .speed = 25000,
632 },
633 {
634 .mask = MLXSW_REG_PTYS_ETH_SPEED_50GBASE_KR4 |
635 MLXSW_REG_PTYS_ETH_SPEED_50GBASE_CR2 |
636 MLXSW_REG_PTYS_ETH_SPEED_50GBASE_KR2,
637 .speed = 50000,
638 },
639 {
640 .mask = MLXSW_REG_PTYS_ETH_SPEED_56GBASE_R4,
641 .supported = SUPPORTED_56000baseKR4_Full,
642 .advertised = ADVERTISED_56000baseKR4_Full,
643 .speed = 56000,
644 },
645 {
646 .mask = MLXSW_REG_PTYS_ETH_SPEED_100GBASE_CR4 |
647 MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4 |
648 MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4 |
649 MLXSW_REG_PTYS_ETH_SPEED_100GBASE_LR4_ER4,
650 .speed = 100000,
651 },
652};
653
654#define MLXSW_SX_PORT_LINK_MODE_LEN ARRAY_SIZE(mlxsw_sx_port_link_mode)
655#define MLXSW_SX_PORT_BASE_SPEED 10000
656
657static u32 mlxsw_sx_from_ptys_supported_port(u32 ptys_eth_proto)
658{
659 if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR |
660 MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR |
661 MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4 |
662 MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4 |
663 MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4 |
664 MLXSW_REG_PTYS_ETH_SPEED_SGMII))
665 return SUPPORTED_FIBRE;
666
667 if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR |
668 MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4 |
669 MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4 |
670 MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4 |
671 MLXSW_REG_PTYS_ETH_SPEED_1000BASE_KX))
672 return SUPPORTED_Backplane;
673 return 0;
674}
675
676static u32 mlxsw_sx_from_ptys_supported_link(u32 ptys_eth_proto)
677{
678 u32 modes = 0;
679 int i;
680
681 for (i = 0; i < MLXSW_SX_PORT_LINK_MODE_LEN; i++) {
682 if (ptys_eth_proto & mlxsw_sx_port_link_mode[i].mask)
683 modes |= mlxsw_sx_port_link_mode[i].supported;
684 }
685 return modes;
686}
687
688static u32 mlxsw_sx_from_ptys_advert_link(u32 ptys_eth_proto)
689{
690 u32 modes = 0;
691 int i;
692
693 for (i = 0; i < MLXSW_SX_PORT_LINK_MODE_LEN; i++) {
694 if (ptys_eth_proto & mlxsw_sx_port_link_mode[i].mask)
695 modes |= mlxsw_sx_port_link_mode[i].advertised;
696 }
697 return modes;
698}
699
700static void mlxsw_sx_from_ptys_speed_duplex(bool carrier_ok, u32 ptys_eth_proto,
701 struct ethtool_link_ksettings *cmd)
702{
703 u32 speed = SPEED_UNKNOWN;
704 u8 duplex = DUPLEX_UNKNOWN;
705 int i;
706
707 if (!carrier_ok)
708 goto out;
709
710 for (i = 0; i < MLXSW_SX_PORT_LINK_MODE_LEN; i++) {
711 if (ptys_eth_proto & mlxsw_sx_port_link_mode[i].mask) {
712 speed = mlxsw_sx_port_link_mode[i].speed;
713 duplex = DUPLEX_FULL;
714 break;
715 }
716 }
717out:
718 cmd->base.speed = speed;
719 cmd->base.duplex = duplex;
720}
721
722static u8 mlxsw_sx_port_connector_port(u32 ptys_eth_proto)
723{
724 if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR |
725 MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4 |
726 MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4 |
727 MLXSW_REG_PTYS_ETH_SPEED_SGMII))
728 return PORT_FIBRE;
729
730 if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR |
731 MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4 |
732 MLXSW_REG_PTYS_ETH_SPEED_100GBASE_CR4))
733 return PORT_DA;
734
735 if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR |
736 MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4 |
737 MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4 |
738 MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4))
739 return PORT_NONE;
740
741 return PORT_OTHER;
742}
743
744static int
745mlxsw_sx_port_get_link_ksettings(struct net_device *dev,
746 struct ethtool_link_ksettings *cmd)
747{
748 struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
749 struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
750 char ptys_pl[MLXSW_REG_PTYS_LEN];
751 u32 eth_proto_cap;
752 u32 eth_proto_admin;
753 u32 eth_proto_oper;
754 u32 supported, advertising, lp_advertising;
755 int err;
756
757 mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sx_port->local_port, 0, false);
758 err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(ptys), ptys_pl);
759 if (err) {
760 netdev_err(dev, "Failed to get proto");
761 return err;
762 }
763 mlxsw_reg_ptys_eth_unpack(ptys_pl, ð_proto_cap,
764 ð_proto_admin, ð_proto_oper);
765
766 supported = mlxsw_sx_from_ptys_supported_port(eth_proto_cap) |
767 mlxsw_sx_from_ptys_supported_link(eth_proto_cap) |
768 SUPPORTED_Pause | SUPPORTED_Asym_Pause;
769 advertising = mlxsw_sx_from_ptys_advert_link(eth_proto_admin);
770 mlxsw_sx_from_ptys_speed_duplex(netif_carrier_ok(dev),
771 eth_proto_oper, cmd);
772
773 eth_proto_oper = eth_proto_oper ? eth_proto_oper : eth_proto_cap;
774 cmd->base.port = mlxsw_sx_port_connector_port(eth_proto_oper);
775 lp_advertising = mlxsw_sx_from_ptys_advert_link(eth_proto_oper);
776
777 ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
778 supported);
779 ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
780 advertising);
781 ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.lp_advertising,
782 lp_advertising);
783
784 return 0;
785}
786
787static u32 mlxsw_sx_to_ptys_advert_link(u32 advertising)
788{
789 u32 ptys_proto = 0;
790 int i;
791
792 for (i = 0; i < MLXSW_SX_PORT_LINK_MODE_LEN; i++) {
793 if (advertising & mlxsw_sx_port_link_mode[i].advertised)
794 ptys_proto |= mlxsw_sx_port_link_mode[i].mask;
795 }
796 return ptys_proto;
797}
798
799static u32 mlxsw_sx_to_ptys_speed(u32 speed)
800{
801 u32 ptys_proto = 0;
802 int i;
803
804 for (i = 0; i < MLXSW_SX_PORT_LINK_MODE_LEN; i++) {
805 if (speed == mlxsw_sx_port_link_mode[i].speed)
806 ptys_proto |= mlxsw_sx_port_link_mode[i].mask;
807 }
808 return ptys_proto;
809}
810
811static u32 mlxsw_sx_to_ptys_upper_speed(u32 upper_speed)
812{
813 u32 ptys_proto = 0;
814 int i;
815
816 for (i = 0; i < MLXSW_SX_PORT_LINK_MODE_LEN; i++) {
817 if (mlxsw_sx_port_link_mode[i].speed <= upper_speed)
818 ptys_proto |= mlxsw_sx_port_link_mode[i].mask;
819 }
820 return ptys_proto;
821}
822
823static int
824mlxsw_sx_port_set_link_ksettings(struct net_device *dev,
825 const struct ethtool_link_ksettings *cmd)
826{
827 struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
828 struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
829 char ptys_pl[MLXSW_REG_PTYS_LEN];
830 u32 speed;
831 u32 eth_proto_new;
832 u32 eth_proto_cap;
833 u32 eth_proto_admin;
834 u32 advertising;
835 bool is_up;
836 int err;
837
838 speed = cmd->base.speed;
839
840 ethtool_convert_link_mode_to_legacy_u32(&advertising,
841 cmd->link_modes.advertising);
842
843 eth_proto_new = cmd->base.autoneg == AUTONEG_ENABLE ?
844 mlxsw_sx_to_ptys_advert_link(advertising) :
845 mlxsw_sx_to_ptys_speed(speed);
846
847 mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sx_port->local_port, 0, false);
848 err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(ptys), ptys_pl);
849 if (err) {
850 netdev_err(dev, "Failed to get proto");
851 return err;
852 }
853 mlxsw_reg_ptys_eth_unpack(ptys_pl, ð_proto_cap, ð_proto_admin,
854 NULL);
855
856 eth_proto_new = eth_proto_new & eth_proto_cap;
857 if (!eth_proto_new) {
858 netdev_err(dev, "Not supported proto admin requested");
859 return -EINVAL;
860 }
861 if (eth_proto_new == eth_proto_admin)
862 return 0;
863
864 mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sx_port->local_port,
865 eth_proto_new, true);
866 err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(ptys), ptys_pl);
867 if (err) {
868 netdev_err(dev, "Failed to set proto admin");
869 return err;
870 }
871
872 err = mlxsw_sx_port_oper_status_get(mlxsw_sx_port, &is_up);
873 if (err) {
874 netdev_err(dev, "Failed to get oper status");
875 return err;
876 }
877 if (!is_up)
878 return 0;
879
880 err = mlxsw_sx_port_admin_status_set(mlxsw_sx_port, false);
881 if (err) {
882 netdev_err(dev, "Failed to set admin status");
883 return err;
884 }
885
886 err = mlxsw_sx_port_admin_status_set(mlxsw_sx_port, true);
887 if (err) {
888 netdev_err(dev, "Failed to set admin status");
889 return err;
890 }
891
892 return 0;
893}
894
895static const struct ethtool_ops mlxsw_sx_port_ethtool_ops = {
896 .get_drvinfo = mlxsw_sx_port_get_drvinfo,
897 .get_link = ethtool_op_get_link,
898 .get_strings = mlxsw_sx_port_get_strings,
899 .get_ethtool_stats = mlxsw_sx_port_get_stats,
900 .get_sset_count = mlxsw_sx_port_get_sset_count,
901 .get_link_ksettings = mlxsw_sx_port_get_link_ksettings,
902 .set_link_ksettings = mlxsw_sx_port_set_link_ksettings,
903};
904
905static int mlxsw_sx_hw_id_get(struct mlxsw_sx *mlxsw_sx)
906{
907 char spad_pl[MLXSW_REG_SPAD_LEN] = {0};
908 int err;
909
910 err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(spad), spad_pl);
911 if (err)
912 return err;
913 mlxsw_reg_spad_base_mac_memcpy_from(spad_pl, mlxsw_sx->hw_id);
914 return 0;
915}
916
917static int mlxsw_sx_port_dev_addr_get(struct mlxsw_sx_port *mlxsw_sx_port)
918{
919 struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
920 struct net_device *dev = mlxsw_sx_port->dev;
921 char ppad_pl[MLXSW_REG_PPAD_LEN];
922 int err;
923
924 mlxsw_reg_ppad_pack(ppad_pl, false, 0);
925 err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(ppad), ppad_pl);
926 if (err)
927 return err;
928 mlxsw_reg_ppad_mac_memcpy_from(ppad_pl, dev->dev_addr);
929
930
931
932
933 dev->dev_addr[ETH_ALEN - 1] += mlxsw_sx_port->local_port;
934 return 0;
935}
936
937static int mlxsw_sx_port_stp_state_set(struct mlxsw_sx_port *mlxsw_sx_port,
938 u16 vid, enum mlxsw_reg_spms_state state)
939{
940 struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
941 char *spms_pl;
942 int err;
943
944 spms_pl = kmalloc(MLXSW_REG_SPMS_LEN, GFP_KERNEL);
945 if (!spms_pl)
946 return -ENOMEM;
947 mlxsw_reg_spms_pack(spms_pl, mlxsw_sx_port->local_port);
948 mlxsw_reg_spms_vid_pack(spms_pl, vid, state);
949 err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(spms), spms_pl);
950 kfree(spms_pl);
951 return err;
952}
953
954static int mlxsw_sx_port_ib_speed_set(struct mlxsw_sx_port *mlxsw_sx_port,
955 u16 speed, u16 width)
956{
957 struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
958 char ptys_pl[MLXSW_REG_PTYS_LEN];
959
960 mlxsw_reg_ptys_ib_pack(ptys_pl, mlxsw_sx_port->local_port, speed,
961 width);
962 return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(ptys), ptys_pl);
963}
964
965static int
966mlxsw_sx_port_speed_by_width_set(struct mlxsw_sx_port *mlxsw_sx_port, u8 width)
967{
968 struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
969 u32 upper_speed = MLXSW_SX_PORT_BASE_SPEED * width;
970 char ptys_pl[MLXSW_REG_PTYS_LEN];
971 u32 eth_proto_admin;
972
973 eth_proto_admin = mlxsw_sx_to_ptys_upper_speed(upper_speed);
974 mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sx_port->local_port,
975 eth_proto_admin, true);
976 return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(ptys), ptys_pl);
977}
978
979static int
980mlxsw_sx_port_mac_learning_mode_set(struct mlxsw_sx_port *mlxsw_sx_port,
981 enum mlxsw_reg_spmlr_learn_mode mode)
982{
983 struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
984 char spmlr_pl[MLXSW_REG_SPMLR_LEN];
985
986 mlxsw_reg_spmlr_pack(spmlr_pl, mlxsw_sx_port->local_port, mode);
987 return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(spmlr), spmlr_pl);
988}
989
990static int __mlxsw_sx_port_eth_create(struct mlxsw_sx *mlxsw_sx, u8 local_port,
991 u8 module, u8 width)
992{
993 struct mlxsw_sx_port *mlxsw_sx_port;
994 struct net_device *dev;
995 int err;
996
997 dev = alloc_etherdev(sizeof(struct mlxsw_sx_port));
998 if (!dev)
999 return -ENOMEM;
1000 SET_NETDEV_DEV(dev, mlxsw_sx->bus_info->dev);
1001 mlxsw_sx_port = netdev_priv(dev);
1002 mlxsw_sx_port->dev = dev;
1003 mlxsw_sx_port->mlxsw_sx = mlxsw_sx;
1004 mlxsw_sx_port->local_port = local_port;
1005 mlxsw_sx_port->mapping.module = module;
1006
1007 mlxsw_sx_port->pcpu_stats =
1008 netdev_alloc_pcpu_stats(struct mlxsw_sx_port_pcpu_stats);
1009 if (!mlxsw_sx_port->pcpu_stats) {
1010 err = -ENOMEM;
1011 goto err_alloc_stats;
1012 }
1013
1014 dev->netdev_ops = &mlxsw_sx_port_netdev_ops;
1015 dev->ethtool_ops = &mlxsw_sx_port_ethtool_ops;
1016
1017 err = mlxsw_sx_port_dev_addr_get(mlxsw_sx_port);
1018 if (err) {
1019 dev_err(mlxsw_sx->bus_info->dev, "Port %d: Unable to get port mac address\n",
1020 mlxsw_sx_port->local_port);
1021 goto err_dev_addr_get;
1022 }
1023
1024 netif_carrier_off(dev);
1025
1026 dev->features |= NETIF_F_NETNS_LOCAL | NETIF_F_LLTX | NETIF_F_SG |
1027 NETIF_F_VLAN_CHALLENGED;
1028
1029 dev->min_mtu = 0;
1030 dev->max_mtu = ETH_MAX_MTU;
1031
1032
1033
1034
1035 dev->needed_headroom = MLXSW_TXHDR_LEN;
1036
1037 err = mlxsw_sx_port_system_port_mapping_set(mlxsw_sx_port);
1038 if (err) {
1039 dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set system port mapping\n",
1040 mlxsw_sx_port->local_port);
1041 goto err_port_system_port_mapping_set;
1042 }
1043
1044 err = mlxsw_sx_port_swid_set(mlxsw_sx_port, 0);
1045 if (err) {
1046 dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set SWID\n",
1047 mlxsw_sx_port->local_port);
1048 goto err_port_swid_set;
1049 }
1050
1051 err = mlxsw_sx_port_speed_by_width_set(mlxsw_sx_port, width);
1052 if (err) {
1053 dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set speed\n",
1054 mlxsw_sx_port->local_port);
1055 goto err_port_speed_set;
1056 }
1057
1058 err = mlxsw_sx_port_mtu_eth_set(mlxsw_sx_port, ETH_DATA_LEN);
1059 if (err) {
1060 dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set MTU\n",
1061 mlxsw_sx_port->local_port);
1062 goto err_port_mtu_set;
1063 }
1064
1065 err = mlxsw_sx_port_admin_status_set(mlxsw_sx_port, false);
1066 if (err)
1067 goto err_port_admin_status_set;
1068
1069 err = mlxsw_sx_port_stp_state_set(mlxsw_sx_port,
1070 MLXSW_PORT_DEFAULT_VID,
1071 MLXSW_REG_SPMS_STATE_FORWARDING);
1072 if (err) {
1073 dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set STP state\n",
1074 mlxsw_sx_port->local_port);
1075 goto err_port_stp_state_set;
1076 }
1077
1078 err = mlxsw_sx_port_mac_learning_mode_set(mlxsw_sx_port,
1079 MLXSW_REG_SPMLR_LEARN_MODE_DISABLE);
1080 if (err) {
1081 dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set MAC learning mode\n",
1082 mlxsw_sx_port->local_port);
1083 goto err_port_mac_learning_mode_set;
1084 }
1085
1086 err = register_netdev(dev);
1087 if (err) {
1088 dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to register netdev\n",
1089 mlxsw_sx_port->local_port);
1090 goto err_register_netdev;
1091 }
1092
1093 mlxsw_core_port_eth_set(mlxsw_sx->core, mlxsw_sx_port->local_port,
1094 mlxsw_sx_port, dev);
1095 mlxsw_sx->ports[local_port] = mlxsw_sx_port;
1096 return 0;
1097
1098err_register_netdev:
1099err_port_mac_learning_mode_set:
1100err_port_stp_state_set:
1101err_port_admin_status_set:
1102err_port_mtu_set:
1103err_port_speed_set:
1104 mlxsw_sx_port_swid_set(mlxsw_sx_port, MLXSW_PORT_SWID_DISABLED_PORT);
1105err_port_swid_set:
1106err_port_system_port_mapping_set:
1107err_dev_addr_get:
1108 free_percpu(mlxsw_sx_port->pcpu_stats);
1109err_alloc_stats:
1110 free_netdev(dev);
1111 return err;
1112}
1113
1114static int mlxsw_sx_port_eth_create(struct mlxsw_sx *mlxsw_sx, u8 local_port,
1115 u8 module, u8 width)
1116{
1117 int err;
1118
1119 err = mlxsw_core_port_init(mlxsw_sx->core, local_port,
1120 module + 1, false, 0,
1121 mlxsw_sx->hw_id, sizeof(mlxsw_sx->hw_id));
1122 if (err) {
1123 dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to init core port\n",
1124 local_port);
1125 return err;
1126 }
1127 err = __mlxsw_sx_port_eth_create(mlxsw_sx, local_port, module, width);
1128 if (err)
1129 goto err_port_create;
1130
1131 return 0;
1132
1133err_port_create:
1134 mlxsw_core_port_fini(mlxsw_sx->core, local_port);
1135 return err;
1136}
1137
1138static void __mlxsw_sx_port_eth_remove(struct mlxsw_sx *mlxsw_sx, u8 local_port)
1139{
1140 struct mlxsw_sx_port *mlxsw_sx_port = mlxsw_sx->ports[local_port];
1141
1142 mlxsw_core_port_clear(mlxsw_sx->core, local_port, mlxsw_sx);
1143 unregister_netdev(mlxsw_sx_port->dev);
1144 mlxsw_sx->ports[local_port] = NULL;
1145 mlxsw_sx_port_swid_set(mlxsw_sx_port, MLXSW_PORT_SWID_DISABLED_PORT);
1146 free_percpu(mlxsw_sx_port->pcpu_stats);
1147 free_netdev(mlxsw_sx_port->dev);
1148}
1149
1150static bool mlxsw_sx_port_created(struct mlxsw_sx *mlxsw_sx, u8 local_port)
1151{
1152 return mlxsw_sx->ports[local_port] != NULL;
1153}
1154
1155static int __mlxsw_sx_port_ib_create(struct mlxsw_sx *mlxsw_sx, u8 local_port,
1156 u8 module, u8 width)
1157{
1158 struct mlxsw_sx_port *mlxsw_sx_port;
1159 int err;
1160
1161 mlxsw_sx_port = kzalloc(sizeof(*mlxsw_sx_port), GFP_KERNEL);
1162 if (!mlxsw_sx_port)
1163 return -ENOMEM;
1164 mlxsw_sx_port->mlxsw_sx = mlxsw_sx;
1165 mlxsw_sx_port->local_port = local_port;
1166 mlxsw_sx_port->mapping.module = module;
1167
1168 err = mlxsw_sx_port_system_port_mapping_set(mlxsw_sx_port);
1169 if (err) {
1170 dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set system port mapping\n",
1171 mlxsw_sx_port->local_port);
1172 goto err_port_system_port_mapping_set;
1173 }
1174
1175
1176 err = mlxsw_sx_port_swid_set(mlxsw_sx_port, 1);
1177 if (err) {
1178 dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set SWID\n",
1179 mlxsw_sx_port->local_port);
1180 goto err_port_swid_set;
1181 }
1182
1183
1184 err = mlxsw_sx_port_ib_port_set(mlxsw_sx_port, module + 1);
1185 if (err) {
1186 dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set IB port\n",
1187 mlxsw_sx_port->local_port);
1188 goto err_port_ib_set;
1189 }
1190
1191
1192
1193
1194 err = mlxsw_sx_port_ib_speed_set(mlxsw_sx_port,
1195 MLXSW_REG_PTYS_IB_SPEED_EDR - 1,
1196 BIT(3) - 1);
1197 if (err) {
1198 dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set speed\n",
1199 mlxsw_sx_port->local_port);
1200 goto err_port_speed_set;
1201 }
1202
1203
1204
1205
1206 err = mlxsw_sx_port_mtu_ib_set(mlxsw_sx_port, MLXSW_IB_DEFAULT_MTU);
1207 if (err) {
1208 dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set MTU\n",
1209 mlxsw_sx_port->local_port);
1210 goto err_port_mtu_set;
1211 }
1212
1213 err = mlxsw_sx_port_admin_status_set(mlxsw_sx_port, true);
1214 if (err) {
1215 dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to change admin state to UP\n",
1216 mlxsw_sx_port->local_port);
1217 goto err_port_admin_set;
1218 }
1219
1220 mlxsw_core_port_ib_set(mlxsw_sx->core, mlxsw_sx_port->local_port,
1221 mlxsw_sx_port);
1222 mlxsw_sx->ports[local_port] = mlxsw_sx_port;
1223 return 0;
1224
1225err_port_admin_set:
1226err_port_mtu_set:
1227err_port_speed_set:
1228err_port_ib_set:
1229 mlxsw_sx_port_swid_set(mlxsw_sx_port, MLXSW_PORT_SWID_DISABLED_PORT);
1230err_port_swid_set:
1231err_port_system_port_mapping_set:
1232 kfree(mlxsw_sx_port);
1233 return err;
1234}
1235
1236static void __mlxsw_sx_port_ib_remove(struct mlxsw_sx *mlxsw_sx, u8 local_port)
1237{
1238 struct mlxsw_sx_port *mlxsw_sx_port = mlxsw_sx->ports[local_port];
1239
1240 mlxsw_core_port_clear(mlxsw_sx->core, local_port, mlxsw_sx);
1241 mlxsw_sx->ports[local_port] = NULL;
1242 mlxsw_sx_port_admin_status_set(mlxsw_sx_port, false);
1243 mlxsw_sx_port_swid_set(mlxsw_sx_port, MLXSW_PORT_SWID_DISABLED_PORT);
1244 kfree(mlxsw_sx_port);
1245}
1246
1247static void __mlxsw_sx_port_remove(struct mlxsw_sx *mlxsw_sx, u8 local_port)
1248{
1249 enum devlink_port_type port_type =
1250 mlxsw_core_port_type_get(mlxsw_sx->core, local_port);
1251
1252 if (port_type == DEVLINK_PORT_TYPE_ETH)
1253 __mlxsw_sx_port_eth_remove(mlxsw_sx, local_port);
1254 else if (port_type == DEVLINK_PORT_TYPE_IB)
1255 __mlxsw_sx_port_ib_remove(mlxsw_sx, local_port);
1256}
1257
1258static void mlxsw_sx_port_remove(struct mlxsw_sx *mlxsw_sx, u8 local_port)
1259{
1260 __mlxsw_sx_port_remove(mlxsw_sx, local_port);
1261 mlxsw_core_port_fini(mlxsw_sx->core, local_port);
1262}
1263
1264static void mlxsw_sx_ports_remove(struct mlxsw_sx *mlxsw_sx)
1265{
1266 int i;
1267
1268 for (i = 1; i < mlxsw_core_max_ports(mlxsw_sx->core); i++)
1269 if (mlxsw_sx_port_created(mlxsw_sx, i))
1270 mlxsw_sx_port_remove(mlxsw_sx, i);
1271 kfree(mlxsw_sx->ports);
1272}
1273
1274static int mlxsw_sx_ports_create(struct mlxsw_sx *mlxsw_sx)
1275{
1276 unsigned int max_ports = mlxsw_core_max_ports(mlxsw_sx->core);
1277 size_t alloc_size;
1278 u8 module, width;
1279 int i;
1280 int err;
1281
1282 alloc_size = sizeof(struct mlxsw_sx_port *) * max_ports;
1283 mlxsw_sx->ports = kzalloc(alloc_size, GFP_KERNEL);
1284 if (!mlxsw_sx->ports)
1285 return -ENOMEM;
1286
1287 for (i = 1; i < max_ports; i++) {
1288 err = mlxsw_sx_port_module_info_get(mlxsw_sx, i, &module,
1289 &width);
1290 if (err)
1291 goto err_port_module_info_get;
1292 if (!width)
1293 continue;
1294 err = mlxsw_sx_port_eth_create(mlxsw_sx, i, module, width);
1295 if (err)
1296 goto err_port_create;
1297 }
1298 return 0;
1299
1300err_port_create:
1301err_port_module_info_get:
1302 for (i--; i >= 1; i--)
1303 if (mlxsw_sx_port_created(mlxsw_sx, i))
1304 mlxsw_sx_port_remove(mlxsw_sx, i);
1305 kfree(mlxsw_sx->ports);
1306 return err;
1307}
1308
1309static void mlxsw_sx_pude_eth_event_func(struct mlxsw_sx_port *mlxsw_sx_port,
1310 enum mlxsw_reg_pude_oper_status status)
1311{
1312 if (status == MLXSW_PORT_OPER_STATUS_UP) {
1313 netdev_info(mlxsw_sx_port->dev, "link up\n");
1314 netif_carrier_on(mlxsw_sx_port->dev);
1315 } else {
1316 netdev_info(mlxsw_sx_port->dev, "link down\n");
1317 netif_carrier_off(mlxsw_sx_port->dev);
1318 }
1319}
1320
1321static void mlxsw_sx_pude_ib_event_func(struct mlxsw_sx_port *mlxsw_sx_port,
1322 enum mlxsw_reg_pude_oper_status status)
1323{
1324 if (status == MLXSW_PORT_OPER_STATUS_UP)
1325 pr_info("ib link for port %d - up\n",
1326 mlxsw_sx_port->mapping.module + 1);
1327 else
1328 pr_info("ib link for port %d - down\n",
1329 mlxsw_sx_port->mapping.module + 1);
1330}
1331
1332static void mlxsw_sx_pude_event_func(const struct mlxsw_reg_info *reg,
1333 char *pude_pl, void *priv)
1334{
1335 struct mlxsw_sx *mlxsw_sx = priv;
1336 struct mlxsw_sx_port *mlxsw_sx_port;
1337 enum mlxsw_reg_pude_oper_status status;
1338 enum devlink_port_type port_type;
1339 u8 local_port;
1340
1341 local_port = mlxsw_reg_pude_local_port_get(pude_pl);
1342 mlxsw_sx_port = mlxsw_sx->ports[local_port];
1343 if (!mlxsw_sx_port) {
1344 dev_warn(mlxsw_sx->bus_info->dev, "Port %d: Link event received for non-existent port\n",
1345 local_port);
1346 return;
1347 }
1348
1349 status = mlxsw_reg_pude_oper_status_get(pude_pl);
1350 port_type = mlxsw_core_port_type_get(mlxsw_sx->core, local_port);
1351 if (port_type == DEVLINK_PORT_TYPE_ETH)
1352 mlxsw_sx_pude_eth_event_func(mlxsw_sx_port, status);
1353 else if (port_type == DEVLINK_PORT_TYPE_IB)
1354 mlxsw_sx_pude_ib_event_func(mlxsw_sx_port, status);
1355}
1356
1357static void mlxsw_sx_rx_listener_func(struct sk_buff *skb, u8 local_port,
1358 void *priv)
1359{
1360 struct mlxsw_sx *mlxsw_sx = priv;
1361 struct mlxsw_sx_port *mlxsw_sx_port = mlxsw_sx->ports[local_port];
1362 struct mlxsw_sx_port_pcpu_stats *pcpu_stats;
1363
1364 if (unlikely(!mlxsw_sx_port)) {
1365 dev_warn_ratelimited(mlxsw_sx->bus_info->dev, "Port %d: skb received for non-existent port\n",
1366 local_port);
1367 return;
1368 }
1369
1370 skb->dev = mlxsw_sx_port->dev;
1371
1372 pcpu_stats = this_cpu_ptr(mlxsw_sx_port->pcpu_stats);
1373 u64_stats_update_begin(&pcpu_stats->syncp);
1374 pcpu_stats->rx_packets++;
1375 pcpu_stats->rx_bytes += skb->len;
1376 u64_stats_update_end(&pcpu_stats->syncp);
1377
1378 skb->protocol = eth_type_trans(skb, skb->dev);
1379 netif_receive_skb(skb);
1380}
1381
1382static int mlxsw_sx_port_type_set(struct mlxsw_core *mlxsw_core, u8 local_port,
1383 enum devlink_port_type new_type)
1384{
1385 struct mlxsw_sx *mlxsw_sx = mlxsw_core_driver_priv(mlxsw_core);
1386 u8 module, width;
1387 int err;
1388
1389 if (new_type == DEVLINK_PORT_TYPE_AUTO)
1390 return -EOPNOTSUPP;
1391
1392 __mlxsw_sx_port_remove(mlxsw_sx, local_port);
1393 err = mlxsw_sx_port_module_info_get(mlxsw_sx, local_port, &module,
1394 &width);
1395 if (err)
1396 goto err_port_module_info_get;
1397
1398 if (new_type == DEVLINK_PORT_TYPE_ETH)
1399 err = __mlxsw_sx_port_eth_create(mlxsw_sx, local_port, module,
1400 width);
1401 else if (new_type == DEVLINK_PORT_TYPE_IB)
1402 err = __mlxsw_sx_port_ib_create(mlxsw_sx, local_port, module,
1403 width);
1404
1405err_port_module_info_get:
1406 return err;
1407}
1408
1409#define MLXSW_SX_RXL(_trap_id) \
1410 MLXSW_RXL(mlxsw_sx_rx_listener_func, _trap_id, TRAP_TO_CPU, \
1411 false, SX2_RX, FORWARD)
1412
1413static const struct mlxsw_listener mlxsw_sx_listener[] = {
1414 MLXSW_EVENTL(mlxsw_sx_pude_event_func, PUDE, EMAD),
1415 MLXSW_SX_RXL(FDB_MC),
1416 MLXSW_SX_RXL(STP),
1417 MLXSW_SX_RXL(LACP),
1418 MLXSW_SX_RXL(EAPOL),
1419 MLXSW_SX_RXL(LLDP),
1420 MLXSW_SX_RXL(MMRP),
1421 MLXSW_SX_RXL(MVRP),
1422 MLXSW_SX_RXL(RPVST),
1423 MLXSW_SX_RXL(DHCP),
1424 MLXSW_SX_RXL(IGMP_QUERY),
1425 MLXSW_SX_RXL(IGMP_V1_REPORT),
1426 MLXSW_SX_RXL(IGMP_V2_REPORT),
1427 MLXSW_SX_RXL(IGMP_V2_LEAVE),
1428 MLXSW_SX_RXL(IGMP_V3_REPORT),
1429};
1430
1431static int mlxsw_sx_traps_init(struct mlxsw_sx *mlxsw_sx)
1432{
1433 char htgt_pl[MLXSW_REG_HTGT_LEN];
1434 int i;
1435 int err;
1436
1437 mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_SX2_RX,
1438 MLXSW_REG_HTGT_INVALID_POLICER,
1439 MLXSW_REG_HTGT_DEFAULT_PRIORITY,
1440 MLXSW_REG_HTGT_DEFAULT_TC);
1441 mlxsw_reg_htgt_local_path_rdq_set(htgt_pl,
1442 MLXSW_REG_HTGT_LOCAL_PATH_RDQ_SX2_RX);
1443
1444 err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(htgt), htgt_pl);
1445 if (err)
1446 return err;
1447
1448 mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_SX2_CTRL,
1449 MLXSW_REG_HTGT_INVALID_POLICER,
1450 MLXSW_REG_HTGT_DEFAULT_PRIORITY,
1451 MLXSW_REG_HTGT_DEFAULT_TC);
1452 mlxsw_reg_htgt_local_path_rdq_set(htgt_pl,
1453 MLXSW_REG_HTGT_LOCAL_PATH_RDQ_SX2_CTRL);
1454
1455 err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(htgt), htgt_pl);
1456 if (err)
1457 return err;
1458
1459 for (i = 0; i < ARRAY_SIZE(mlxsw_sx_listener); i++) {
1460 err = mlxsw_core_trap_register(mlxsw_sx->core,
1461 &mlxsw_sx_listener[i],
1462 mlxsw_sx);
1463 if (err)
1464 goto err_listener_register;
1465
1466 }
1467 return 0;
1468
1469err_listener_register:
1470 for (i--; i >= 0; i--) {
1471 mlxsw_core_trap_unregister(mlxsw_sx->core,
1472 &mlxsw_sx_listener[i],
1473 mlxsw_sx);
1474 }
1475 return err;
1476}
1477
1478static void mlxsw_sx_traps_fini(struct mlxsw_sx *mlxsw_sx)
1479{
1480 int i;
1481
1482 for (i = 0; i < ARRAY_SIZE(mlxsw_sx_listener); i++) {
1483 mlxsw_core_trap_unregister(mlxsw_sx->core,
1484 &mlxsw_sx_listener[i],
1485 mlxsw_sx);
1486 }
1487}
1488
1489static int mlxsw_sx_flood_init(struct mlxsw_sx *mlxsw_sx)
1490{
1491 char sfgc_pl[MLXSW_REG_SFGC_LEN];
1492 char sgcr_pl[MLXSW_REG_SGCR_LEN];
1493 char *sftr_pl;
1494 int err;
1495
1496
1497 sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL);
1498 if (!sftr_pl)
1499 return -ENOMEM;
1500 mlxsw_reg_sftr_pack(sftr_pl, 0, 0, MLXSW_REG_SFGC_TABLE_TYPE_SINGLE, 0,
1501 MLXSW_PORT_CPU_PORT, true);
1502 err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sftr), sftr_pl);
1503 kfree(sftr_pl);
1504 if (err)
1505 return err;
1506
1507
1508 mlxsw_reg_sfgc_pack(sfgc_pl,
1509 MLXSW_REG_SFGC_TYPE_UNKNOWN_UNICAST,
1510 MLXSW_REG_SFGC_BRIDGE_TYPE_1Q_FID,
1511 MLXSW_REG_SFGC_TABLE_TYPE_SINGLE,
1512 0);
1513 err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sfgc), sfgc_pl);
1514 if (err)
1515 return err;
1516
1517 mlxsw_reg_sfgc_pack(sfgc_pl,
1518 MLXSW_REG_SFGC_TYPE_BROADCAST,
1519 MLXSW_REG_SFGC_BRIDGE_TYPE_1Q_FID,
1520 MLXSW_REG_SFGC_TABLE_TYPE_SINGLE,
1521 0);
1522 err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sfgc), sfgc_pl);
1523 if (err)
1524 return err;
1525
1526 mlxsw_reg_sfgc_pack(sfgc_pl,
1527 MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_NON_IP,
1528 MLXSW_REG_SFGC_BRIDGE_TYPE_1Q_FID,
1529 MLXSW_REG_SFGC_TABLE_TYPE_SINGLE,
1530 0);
1531 err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sfgc), sfgc_pl);
1532 if (err)
1533 return err;
1534
1535 mlxsw_reg_sfgc_pack(sfgc_pl,
1536 MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV6,
1537 MLXSW_REG_SFGC_BRIDGE_TYPE_1Q_FID,
1538 MLXSW_REG_SFGC_TABLE_TYPE_SINGLE,
1539 0);
1540 err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sfgc), sfgc_pl);
1541 if (err)
1542 return err;
1543
1544 mlxsw_reg_sfgc_pack(sfgc_pl,
1545 MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV4,
1546 MLXSW_REG_SFGC_BRIDGE_TYPE_1Q_FID,
1547 MLXSW_REG_SFGC_TABLE_TYPE_SINGLE,
1548 0);
1549 err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sfgc), sfgc_pl);
1550 if (err)
1551 return err;
1552
1553 mlxsw_reg_sgcr_pack(sgcr_pl, true);
1554 return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sgcr), sgcr_pl);
1555}
1556
1557static int mlxsw_sx_basic_trap_groups_set(struct mlxsw_core *mlxsw_core)
1558{
1559 char htgt_pl[MLXSW_REG_HTGT_LEN];
1560
1561 mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_EMAD,
1562 MLXSW_REG_HTGT_INVALID_POLICER,
1563 MLXSW_REG_HTGT_DEFAULT_PRIORITY,
1564 MLXSW_REG_HTGT_DEFAULT_TC);
1565 mlxsw_reg_htgt_swid_set(htgt_pl, MLXSW_PORT_SWID_ALL_SWIDS);
1566 mlxsw_reg_htgt_local_path_rdq_set(htgt_pl,
1567 MLXSW_REG_HTGT_LOCAL_PATH_RDQ_SX2_EMAD);
1568 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl);
1569}
1570
1571static int mlxsw_sx_init(struct mlxsw_core *mlxsw_core,
1572 const struct mlxsw_bus_info *mlxsw_bus_info)
1573{
1574 struct mlxsw_sx *mlxsw_sx = mlxsw_core_driver_priv(mlxsw_core);
1575 int err;
1576
1577 mlxsw_sx->core = mlxsw_core;
1578 mlxsw_sx->bus_info = mlxsw_bus_info;
1579
1580 err = mlxsw_sx_hw_id_get(mlxsw_sx);
1581 if (err) {
1582 dev_err(mlxsw_sx->bus_info->dev, "Failed to get switch HW ID\n");
1583 return err;
1584 }
1585
1586 err = mlxsw_sx_ports_create(mlxsw_sx);
1587 if (err) {
1588 dev_err(mlxsw_sx->bus_info->dev, "Failed to create ports\n");
1589 return err;
1590 }
1591
1592 err = mlxsw_sx_traps_init(mlxsw_sx);
1593 if (err) {
1594 dev_err(mlxsw_sx->bus_info->dev, "Failed to set traps\n");
1595 goto err_listener_register;
1596 }
1597
1598 err = mlxsw_sx_flood_init(mlxsw_sx);
1599 if (err) {
1600 dev_err(mlxsw_sx->bus_info->dev, "Failed to initialize flood tables\n");
1601 goto err_flood_init;
1602 }
1603
1604 return 0;
1605
1606err_flood_init:
1607 mlxsw_sx_traps_fini(mlxsw_sx);
1608err_listener_register:
1609 mlxsw_sx_ports_remove(mlxsw_sx);
1610 return err;
1611}
1612
1613static void mlxsw_sx_fini(struct mlxsw_core *mlxsw_core)
1614{
1615 struct mlxsw_sx *mlxsw_sx = mlxsw_core_driver_priv(mlxsw_core);
1616
1617 mlxsw_sx_traps_fini(mlxsw_sx);
1618 mlxsw_sx_ports_remove(mlxsw_sx);
1619}
1620
1621static const struct mlxsw_config_profile mlxsw_sx_config_profile = {
1622 .used_max_vepa_channels = 1,
1623 .max_vepa_channels = 0,
1624 .used_max_mid = 1,
1625 .max_mid = 7000,
1626 .used_max_pgt = 1,
1627 .max_pgt = 0,
1628 .used_max_system_port = 1,
1629 .max_system_port = 48000,
1630 .used_max_vlan_groups = 1,
1631 .max_vlan_groups = 127,
1632 .used_max_regions = 1,
1633 .max_regions = 400,
1634 .used_flood_tables = 1,
1635 .max_flood_tables = 2,
1636 .max_vid_flood_tables = 1,
1637 .used_flood_mode = 1,
1638 .flood_mode = 3,
1639 .used_max_ib_mc = 1,
1640 .max_ib_mc = 6,
1641 .used_max_pkey = 1,
1642 .max_pkey = 0,
1643 .swid_config = {
1644 {
1645 .used_type = 1,
1646 .type = MLXSW_PORT_SWID_TYPE_ETH,
1647 },
1648 {
1649 .used_type = 1,
1650 .type = MLXSW_PORT_SWID_TYPE_IB,
1651 }
1652 },
1653};
1654
1655static struct mlxsw_driver mlxsw_sx_driver = {
1656 .kind = mlxsw_sx_driver_name,
1657 .priv_size = sizeof(struct mlxsw_sx),
1658 .init = mlxsw_sx_init,
1659 .fini = mlxsw_sx_fini,
1660 .basic_trap_groups_set = mlxsw_sx_basic_trap_groups_set,
1661 .txhdr_construct = mlxsw_sx_txhdr_construct,
1662 .txhdr_len = MLXSW_TXHDR_LEN,
1663 .profile = &mlxsw_sx_config_profile,
1664 .port_type_set = mlxsw_sx_port_type_set,
1665};
1666
1667static const struct pci_device_id mlxsw_sx_pci_id_table[] = {
1668 {PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SWITCHX2), 0},
1669 {0, },
1670};
1671
1672static struct pci_driver mlxsw_sx_pci_driver = {
1673 .name = mlxsw_sx_driver_name,
1674 .id_table = mlxsw_sx_pci_id_table,
1675};
1676
1677static int __init mlxsw_sx_module_init(void)
1678{
1679 int err;
1680
1681 err = mlxsw_core_driver_register(&mlxsw_sx_driver);
1682 if (err)
1683 return err;
1684
1685 err = mlxsw_pci_driver_register(&mlxsw_sx_pci_driver);
1686 if (err)
1687 goto err_pci_driver_register;
1688
1689 return 0;
1690
1691err_pci_driver_register:
1692 mlxsw_core_driver_unregister(&mlxsw_sx_driver);
1693 return err;
1694}
1695
1696static void __exit mlxsw_sx_module_exit(void)
1697{
1698 mlxsw_pci_driver_unregister(&mlxsw_sx_pci_driver);
1699 mlxsw_core_driver_unregister(&mlxsw_sx_driver);
1700}
1701
1702module_init(mlxsw_sx_module_init);
1703module_exit(mlxsw_sx_module_exit);
1704
1705MODULE_LICENSE("Dual BSD/GPL");
1706MODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>");
1707MODULE_DESCRIPTION("Mellanox SwitchX-2 driver");
1708MODULE_DEVICE_TABLE(pci, mlxsw_sx_pci_id_table);
1709