linux/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
<<
>>
Prefs
   1/*
   2 * drivers/net/ethernet/mellanox/mlxsw/switchx2.c
   3 * Copyright (c) 2015 Mellanox Technologies. All rights reserved.
   4 * Copyright (c) 2015 Jiri Pirko <jiri@mellanox.com>
   5 * Copyright (c) 2015 Ido Schimmel <idosch@mellanox.com>
   6 * Copyright (c) 2015 Elad Raz <eladr@mellanox.com>
   7 *
   8 * Redistribution and use in source and binary forms, with or without
   9 * modification, are permitted provided that the following conditions are met:
  10 *
  11 * 1. Redistributions of source code must retain the above copyright
  12 *    notice, this list of conditions and the following disclaimer.
  13 * 2. Redistributions in binary form must reproduce the above copyright
  14 *    notice, this list of conditions and the following disclaimer in the
  15 *    documentation and/or other materials provided with the distribution.
  16 * 3. Neither the names of the copyright holders nor the names of its
  17 *    contributors may be used to endorse or promote products derived from
  18 *    this software without specific prior written permission.
  19 *
  20 * Alternatively, this software may be distributed under the terms of the
  21 * GNU General Public License ("GPL") version 2 as published by the Free
  22 * Software Foundation.
  23 *
  24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  34 * POSSIBILITY OF SUCH DAMAGE.
  35 */
  36
  37#include <linux/kernel.h>
  38#include <linux/module.h>
  39#include <linux/types.h>
  40#include <linux/netdevice.h>
  41#include <linux/etherdevice.h>
  42#include <linux/slab.h>
  43#include <linux/device.h>
  44#include <linux/skbuff.h>
  45#include <linux/if_vlan.h>
  46#include <net/switchdev.h>
  47#include <generated/utsrelease.h>
  48
  49#include "core.h"
  50#include "reg.h"
  51#include "port.h"
  52#include "trap.h"
  53#include "txheader.h"
  54
  55static const char mlxsw_sx_driver_name[] = "mlxsw_switchx2";
  56static const char mlxsw_sx_driver_version[] = "1.0";
  57
  58struct mlxsw_sx_port;
  59
  60struct mlxsw_sx {
  61        struct mlxsw_sx_port **ports;
  62        struct mlxsw_core *core;
  63        const struct mlxsw_bus_info *bus_info;
  64        u8 hw_id[ETH_ALEN];
  65};
  66
  67struct mlxsw_sx_port_pcpu_stats {
  68        u64                     rx_packets;
  69        u64                     rx_bytes;
  70        u64                     tx_packets;
  71        u64                     tx_bytes;
  72        struct u64_stats_sync   syncp;
  73        u32                     tx_dropped;
  74};
  75
  76struct mlxsw_sx_port {
  77        struct mlxsw_core_port core_port; /* must be first */
  78        struct net_device *dev;
  79        struct mlxsw_sx_port_pcpu_stats __percpu *pcpu_stats;
  80        struct mlxsw_sx *mlxsw_sx;
  81        u8 local_port;
  82};
  83
  84/* tx_hdr_version
  85 * Tx header version.
  86 * Must be set to 0.
  87 */
  88MLXSW_ITEM32(tx, hdr, version, 0x00, 28, 4);
  89
  90/* tx_hdr_ctl
  91 * Packet control type.
  92 * 0 - Ethernet control (e.g. EMADs, LACP)
  93 * 1 - Ethernet data
  94 */
  95MLXSW_ITEM32(tx, hdr, ctl, 0x00, 26, 2);
  96
  97/* tx_hdr_proto
  98 * Packet protocol type. Must be set to 1 (Ethernet).
  99 */
 100MLXSW_ITEM32(tx, hdr, proto, 0x00, 21, 3);
 101
 102/* tx_hdr_etclass
 103 * Egress TClass to be used on the egress device on the egress port.
 104 * The MSB is specified in the 'ctclass3' field.
 105 * Range is 0-15, where 15 is the highest priority.
 106 */
 107MLXSW_ITEM32(tx, hdr, etclass, 0x00, 18, 3);
 108
 109/* tx_hdr_swid
 110 * Switch partition ID.
 111 */
 112MLXSW_ITEM32(tx, hdr, swid, 0x00, 12, 3);
 113
 114/* tx_hdr_port_mid
 115 * Destination local port for unicast packets.
 116 * Destination multicast ID for multicast packets.
 117 *
 118 * Control packets are directed to a specific egress port, while data
 119 * packets are transmitted through the CPU port (0) into the switch partition,
 120 * where forwarding rules are applied.
 121 */
 122MLXSW_ITEM32(tx, hdr, port_mid, 0x04, 16, 16);
 123
 124/* tx_hdr_ctclass3
 125 * See field 'etclass'.
 126 */
 127MLXSW_ITEM32(tx, hdr, ctclass3, 0x04, 14, 1);
 128
 129/* tx_hdr_rdq
 130 * RDQ for control packets sent to remote CPU.
 131 * Must be set to 0x1F for EMADs, otherwise 0.
 132 */
 133MLXSW_ITEM32(tx, hdr, rdq, 0x04, 9, 5);
 134
 135/* tx_hdr_cpu_sig
 136 * Signature control for packets going to CPU. Must be set to 0.
 137 */
 138MLXSW_ITEM32(tx, hdr, cpu_sig, 0x04, 0, 9);
 139
 140/* tx_hdr_sig
 141 * Stacking protocl signature. Must be set to 0xE0E0.
 142 */
 143MLXSW_ITEM32(tx, hdr, sig, 0x0C, 16, 16);
 144
 145/* tx_hdr_stclass
 146 * Stacking TClass.
 147 */
 148MLXSW_ITEM32(tx, hdr, stclass, 0x0C, 13, 3);
 149
 150/* tx_hdr_emad
 151 * EMAD bit. Must be set for EMADs.
 152 */
 153MLXSW_ITEM32(tx, hdr, emad, 0x0C, 5, 1);
 154
 155/* tx_hdr_type
 156 * 0 - Data packets
 157 * 6 - Control packets
 158 */
 159MLXSW_ITEM32(tx, hdr, type, 0x0C, 0, 4);
 160
 161static void mlxsw_sx_txhdr_construct(struct sk_buff *skb,
 162                                     const struct mlxsw_tx_info *tx_info)
 163{
 164        char *txhdr = skb_push(skb, MLXSW_TXHDR_LEN);
 165        bool is_emad = tx_info->is_emad;
 166
 167        memset(txhdr, 0, MLXSW_TXHDR_LEN);
 168
 169        /* We currently set default values for the egress tclass (QoS). */
 170        mlxsw_tx_hdr_version_set(txhdr, MLXSW_TXHDR_VERSION_0);
 171        mlxsw_tx_hdr_ctl_set(txhdr, MLXSW_TXHDR_ETH_CTL);
 172        mlxsw_tx_hdr_proto_set(txhdr, MLXSW_TXHDR_PROTO_ETH);
 173        mlxsw_tx_hdr_etclass_set(txhdr, is_emad ? MLXSW_TXHDR_ETCLASS_6 :
 174                                                  MLXSW_TXHDR_ETCLASS_5);
 175        mlxsw_tx_hdr_swid_set(txhdr, 0);
 176        mlxsw_tx_hdr_port_mid_set(txhdr, tx_info->local_port);
 177        mlxsw_tx_hdr_ctclass3_set(txhdr, MLXSW_TXHDR_CTCLASS3);
 178        mlxsw_tx_hdr_rdq_set(txhdr, is_emad ? MLXSW_TXHDR_RDQ_EMAD :
 179                                              MLXSW_TXHDR_RDQ_OTHER);
 180        mlxsw_tx_hdr_cpu_sig_set(txhdr, MLXSW_TXHDR_CPU_SIG);
 181        mlxsw_tx_hdr_sig_set(txhdr, MLXSW_TXHDR_SIG);
 182        mlxsw_tx_hdr_stclass_set(txhdr, MLXSW_TXHDR_STCLASS_NONE);
 183        mlxsw_tx_hdr_emad_set(txhdr, is_emad ? MLXSW_TXHDR_EMAD :
 184                                               MLXSW_TXHDR_NOT_EMAD);
 185        mlxsw_tx_hdr_type_set(txhdr, MLXSW_TXHDR_TYPE_CONTROL);
 186}
 187
 188static int mlxsw_sx_port_admin_status_set(struct mlxsw_sx_port *mlxsw_sx_port,
 189                                          bool is_up)
 190{
 191        struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
 192        char paos_pl[MLXSW_REG_PAOS_LEN];
 193
 194        mlxsw_reg_paos_pack(paos_pl, mlxsw_sx_port->local_port,
 195                            is_up ? MLXSW_PORT_ADMIN_STATUS_UP :
 196                            MLXSW_PORT_ADMIN_STATUS_DOWN);
 197        return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(paos), paos_pl);
 198}
 199
 200static int mlxsw_sx_port_oper_status_get(struct mlxsw_sx_port *mlxsw_sx_port,
 201                                         bool *p_is_up)
 202{
 203        struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
 204        char paos_pl[MLXSW_REG_PAOS_LEN];
 205        u8 oper_status;
 206        int err;
 207
 208        mlxsw_reg_paos_pack(paos_pl, mlxsw_sx_port->local_port, 0);
 209        err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(paos), paos_pl);
 210        if (err)
 211                return err;
 212        oper_status = mlxsw_reg_paos_oper_status_get(paos_pl);
 213        *p_is_up = oper_status == MLXSW_PORT_ADMIN_STATUS_UP ? true : false;
 214        return 0;
 215}
 216
 217static int mlxsw_sx_port_mtu_set(struct mlxsw_sx_port *mlxsw_sx_port, u16 mtu)
 218{
 219        struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
 220        char pmtu_pl[MLXSW_REG_PMTU_LEN];
 221        int max_mtu;
 222        int err;
 223
 224        mtu += MLXSW_TXHDR_LEN + ETH_HLEN;
 225        mlxsw_reg_pmtu_pack(pmtu_pl, mlxsw_sx_port->local_port, 0);
 226        err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(pmtu), pmtu_pl);
 227        if (err)
 228                return err;
 229        max_mtu = mlxsw_reg_pmtu_max_mtu_get(pmtu_pl);
 230
 231        if (mtu > max_mtu)
 232                return -EINVAL;
 233
 234        mlxsw_reg_pmtu_pack(pmtu_pl, mlxsw_sx_port->local_port, mtu);
 235        return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(pmtu), pmtu_pl);
 236}
 237
 238static int mlxsw_sx_port_swid_set(struct mlxsw_sx_port *mlxsw_sx_port, u8 swid)
 239{
 240        struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
 241        char pspa_pl[MLXSW_REG_PSPA_LEN];
 242
 243        mlxsw_reg_pspa_pack(pspa_pl, swid, mlxsw_sx_port->local_port);
 244        return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(pspa), pspa_pl);
 245}
 246
 247static int
 248mlxsw_sx_port_system_port_mapping_set(struct mlxsw_sx_port *mlxsw_sx_port)
 249{
 250        struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
 251        char sspr_pl[MLXSW_REG_SSPR_LEN];
 252
 253        mlxsw_reg_sspr_pack(sspr_pl, mlxsw_sx_port->local_port);
 254        return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sspr), sspr_pl);
 255}
 256
 257static int mlxsw_sx_port_module_check(struct mlxsw_sx_port *mlxsw_sx_port,
 258                                      bool *p_usable)
 259{
 260        struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
 261        char pmlp_pl[MLXSW_REG_PMLP_LEN];
 262        int err;
 263
 264        mlxsw_reg_pmlp_pack(pmlp_pl, mlxsw_sx_port->local_port);
 265        err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(pmlp), pmlp_pl);
 266        if (err)
 267                return err;
 268        *p_usable = mlxsw_reg_pmlp_width_get(pmlp_pl) ? true : false;
 269        return 0;
 270}
 271
 272static int mlxsw_sx_port_open(struct net_device *dev)
 273{
 274        struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
 275        int err;
 276
 277        err = mlxsw_sx_port_admin_status_set(mlxsw_sx_port, true);
 278        if (err)
 279                return err;
 280        netif_start_queue(dev);
 281        return 0;
 282}
 283
 284static int mlxsw_sx_port_stop(struct net_device *dev)
 285{
 286        struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
 287
 288        netif_stop_queue(dev);
 289        return mlxsw_sx_port_admin_status_set(mlxsw_sx_port, false);
 290}
 291
 292static netdev_tx_t mlxsw_sx_port_xmit(struct sk_buff *skb,
 293                                      struct net_device *dev)
 294{
 295        struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
 296        struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
 297        struct mlxsw_sx_port_pcpu_stats *pcpu_stats;
 298        const struct mlxsw_tx_info tx_info = {
 299                .local_port = mlxsw_sx_port->local_port,
 300                .is_emad = false,
 301        };
 302        u64 len;
 303        int err;
 304
 305        if (mlxsw_core_skb_transmit_busy(mlxsw_sx->core, &tx_info))
 306                return NETDEV_TX_BUSY;
 307
 308        if (unlikely(skb_headroom(skb) < MLXSW_TXHDR_LEN)) {
 309                struct sk_buff *skb_orig = skb;
 310
 311                skb = skb_realloc_headroom(skb, MLXSW_TXHDR_LEN);
 312                if (!skb) {
 313                        this_cpu_inc(mlxsw_sx_port->pcpu_stats->tx_dropped);
 314                        dev_kfree_skb_any(skb_orig);
 315                        return NETDEV_TX_OK;
 316                }
 317        }
 318        mlxsw_sx_txhdr_construct(skb, &tx_info);
 319        /* TX header is consumed by HW on the way so we shouldn't count its
 320         * bytes as being sent.
 321         */
 322        len = skb->len - MLXSW_TXHDR_LEN;
 323        /* Due to a race we might fail here because of a full queue. In that
 324         * unlikely case we simply drop the packet.
 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_set(mlxsw_sx_port, mtu);
 347        if (err)
 348                return err;
 349        dev->mtu = mtu;
 350        return 0;
 351}
 352
 353static struct rtnl_link_stats64 *
 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                /* tx_dropped is u32, updated without syncp protection. */
 379                tx_dropped      += p->tx_dropped;
 380        }
 381        stats->tx_dropped       = tx_dropped;
 382        return stats;
 383}
 384
 385static const struct net_device_ops mlxsw_sx_port_netdev_ops = {
 386        .ndo_open               = mlxsw_sx_port_open,
 387        .ndo_stop               = mlxsw_sx_port_stop,
 388        .ndo_start_xmit         = mlxsw_sx_port_xmit,
 389        .ndo_change_mtu         = mlxsw_sx_port_change_mtu,
 390        .ndo_get_stats64        = mlxsw_sx_port_get_stats64,
 391};
 392
 393static void mlxsw_sx_port_get_drvinfo(struct net_device *dev,
 394                                      struct ethtool_drvinfo *drvinfo)
 395{
 396        struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
 397        struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
 398
 399        strlcpy(drvinfo->driver, mlxsw_sx_driver_name, sizeof(drvinfo->driver));
 400        strlcpy(drvinfo->version, mlxsw_sx_driver_version,
 401                sizeof(drvinfo->version));
 402        snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
 403                 "%d.%d.%d",
 404                 mlxsw_sx->bus_info->fw_rev.major,
 405                 mlxsw_sx->bus_info->fw_rev.minor,
 406                 mlxsw_sx->bus_info->fw_rev.subminor);
 407        strlcpy(drvinfo->bus_info, mlxsw_sx->bus_info->device_name,
 408                sizeof(drvinfo->bus_info));
 409}
 410
 411struct mlxsw_sx_port_hw_stats {
 412        char str[ETH_GSTRING_LEN];
 413        u64 (*getter)(char *payload);
 414};
 415
 416static const struct mlxsw_sx_port_hw_stats mlxsw_sx_port_hw_stats[] = {
 417        {
 418                .str = "a_frames_transmitted_ok",
 419                .getter = mlxsw_reg_ppcnt_a_frames_transmitted_ok_get,
 420        },
 421        {
 422                .str = "a_frames_received_ok",
 423                .getter = mlxsw_reg_ppcnt_a_frames_received_ok_get,
 424        },
 425        {
 426                .str = "a_frame_check_sequence_errors",
 427                .getter = mlxsw_reg_ppcnt_a_frame_check_sequence_errors_get,
 428        },
 429        {
 430                .str = "a_alignment_errors",
 431                .getter = mlxsw_reg_ppcnt_a_alignment_errors_get,
 432        },
 433        {
 434                .str = "a_octets_transmitted_ok",
 435                .getter = mlxsw_reg_ppcnt_a_octets_transmitted_ok_get,
 436        },
 437        {
 438                .str = "a_octets_received_ok",
 439                .getter = mlxsw_reg_ppcnt_a_octets_received_ok_get,
 440        },
 441        {
 442                .str = "a_multicast_frames_xmitted_ok",
 443                .getter = mlxsw_reg_ppcnt_a_multicast_frames_xmitted_ok_get,
 444        },
 445        {
 446                .str = "a_broadcast_frames_xmitted_ok",
 447                .getter = mlxsw_reg_ppcnt_a_broadcast_frames_xmitted_ok_get,
 448        },
 449        {
 450                .str = "a_multicast_frames_received_ok",
 451                .getter = mlxsw_reg_ppcnt_a_multicast_frames_received_ok_get,
 452        },
 453        {
 454                .str = "a_broadcast_frames_received_ok",
 455                .getter = mlxsw_reg_ppcnt_a_broadcast_frames_received_ok_get,
 456        },
 457        {
 458                .str = "a_in_range_length_errors",
 459                .getter = mlxsw_reg_ppcnt_a_in_range_length_errors_get,
 460        },
 461        {
 462                .str = "a_out_of_range_length_field",
 463                .getter = mlxsw_reg_ppcnt_a_out_of_range_length_field_get,
 464        },
 465        {
 466                .str = "a_frame_too_long_errors",
 467                .getter = mlxsw_reg_ppcnt_a_frame_too_long_errors_get,
 468        },
 469        {
 470                .str = "a_symbol_error_during_carrier",
 471                .getter = mlxsw_reg_ppcnt_a_symbol_error_during_carrier_get,
 472        },
 473        {
 474                .str = "a_mac_control_frames_transmitted",
 475                .getter = mlxsw_reg_ppcnt_a_mac_control_frames_transmitted_get,
 476        },
 477        {
 478                .str = "a_mac_control_frames_received",
 479                .getter = mlxsw_reg_ppcnt_a_mac_control_frames_received_get,
 480        },
 481        {
 482                .str = "a_unsupported_opcodes_received",
 483                .getter = mlxsw_reg_ppcnt_a_unsupported_opcodes_received_get,
 484        },
 485        {
 486                .str = "a_pause_mac_ctrl_frames_received",
 487                .getter = mlxsw_reg_ppcnt_a_pause_mac_ctrl_frames_received_get,
 488        },
 489        {
 490                .str = "a_pause_mac_ctrl_frames_xmitted",
 491                .getter = mlxsw_reg_ppcnt_a_pause_mac_ctrl_frames_transmitted_get,
 492        },
 493};
 494
 495#define MLXSW_SX_PORT_HW_STATS_LEN ARRAY_SIZE(mlxsw_sx_port_hw_stats)
 496
 497static void mlxsw_sx_port_get_strings(struct net_device *dev,
 498                                      u32 stringset, u8 *data)
 499{
 500        u8 *p = data;
 501        int i;
 502
 503        switch (stringset) {
 504        case ETH_SS_STATS:
 505                for (i = 0; i < MLXSW_SX_PORT_HW_STATS_LEN; i++) {
 506                        memcpy(p, mlxsw_sx_port_hw_stats[i].str,
 507                               ETH_GSTRING_LEN);
 508                        p += ETH_GSTRING_LEN;
 509                }
 510                break;
 511        }
 512}
 513
 514static void mlxsw_sx_port_get_stats(struct net_device *dev,
 515                                    struct ethtool_stats *stats, u64 *data)
 516{
 517        struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
 518        struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
 519        char ppcnt_pl[MLXSW_REG_PPCNT_LEN];
 520        int i;
 521        int err;
 522
 523        mlxsw_reg_ppcnt_pack(ppcnt_pl, mlxsw_sx_port->local_port,
 524                             MLXSW_REG_PPCNT_IEEE_8023_CNT, 0);
 525        err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(ppcnt), ppcnt_pl);
 526        for (i = 0; i < MLXSW_SX_PORT_HW_STATS_LEN; i++)
 527                data[i] = !err ? mlxsw_sx_port_hw_stats[i].getter(ppcnt_pl) : 0;
 528}
 529
 530static int mlxsw_sx_port_get_sset_count(struct net_device *dev, int sset)
 531{
 532        switch (sset) {
 533        case ETH_SS_STATS:
 534                return MLXSW_SX_PORT_HW_STATS_LEN;
 535        default:
 536                return -EOPNOTSUPP;
 537        }
 538}
 539
 540struct mlxsw_sx_port_link_mode {
 541        u32 mask;
 542        u32 supported;
 543        u32 advertised;
 544        u32 speed;
 545};
 546
 547static const struct mlxsw_sx_port_link_mode mlxsw_sx_port_link_mode[] = {
 548        {
 549                .mask           = MLXSW_REG_PTYS_ETH_SPEED_100BASE_T,
 550                .supported      = SUPPORTED_100baseT_Full,
 551                .advertised     = ADVERTISED_100baseT_Full,
 552                .speed          = 100,
 553        },
 554        {
 555                .mask           = MLXSW_REG_PTYS_ETH_SPEED_100BASE_TX,
 556                .speed          = 100,
 557        },
 558        {
 559                .mask           = MLXSW_REG_PTYS_ETH_SPEED_SGMII |
 560                                  MLXSW_REG_PTYS_ETH_SPEED_1000BASE_KX,
 561                .supported      = SUPPORTED_1000baseKX_Full,
 562                .advertised     = ADVERTISED_1000baseKX_Full,
 563                .speed          = 1000,
 564        },
 565        {
 566                .mask           = MLXSW_REG_PTYS_ETH_SPEED_10GBASE_T,
 567                .supported      = SUPPORTED_10000baseT_Full,
 568                .advertised     = ADVERTISED_10000baseT_Full,
 569                .speed          = 10000,
 570        },
 571        {
 572                .mask           = MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CX4 |
 573                                  MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4,
 574                .supported      = SUPPORTED_10000baseKX4_Full,
 575                .advertised     = ADVERTISED_10000baseKX4_Full,
 576                .speed          = 10000,
 577        },
 578        {
 579                .mask           = MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR |
 580                                  MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR |
 581                                  MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR |
 582                                  MLXSW_REG_PTYS_ETH_SPEED_10GBASE_ER_LR,
 583                .supported      = SUPPORTED_10000baseKR_Full,
 584                .advertised     = ADVERTISED_10000baseKR_Full,
 585                .speed          = 10000,
 586        },
 587        {
 588                .mask           = MLXSW_REG_PTYS_ETH_SPEED_20GBASE_KR2,
 589                .supported      = SUPPORTED_20000baseKR2_Full,
 590                .advertised     = ADVERTISED_20000baseKR2_Full,
 591                .speed          = 20000,
 592        },
 593        {
 594                .mask           = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4,
 595                .supported      = SUPPORTED_40000baseCR4_Full,
 596                .advertised     = ADVERTISED_40000baseCR4_Full,
 597                .speed          = 40000,
 598        },
 599        {
 600                .mask           = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4,
 601                .supported      = SUPPORTED_40000baseKR4_Full,
 602                .advertised     = ADVERTISED_40000baseKR4_Full,
 603                .speed          = 40000,
 604        },
 605        {
 606                .mask           = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4,
 607                .supported      = SUPPORTED_40000baseSR4_Full,
 608                .advertised     = ADVERTISED_40000baseSR4_Full,
 609                .speed          = 40000,
 610        },
 611        {
 612                .mask           = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_LR4_ER4,
 613                .supported      = SUPPORTED_40000baseLR4_Full,
 614                .advertised     = ADVERTISED_40000baseLR4_Full,
 615                .speed          = 40000,
 616        },
 617        {
 618                .mask           = MLXSW_REG_PTYS_ETH_SPEED_25GBASE_CR |
 619                                  MLXSW_REG_PTYS_ETH_SPEED_25GBASE_KR |
 620                                  MLXSW_REG_PTYS_ETH_SPEED_25GBASE_SR,
 621                .speed          = 25000,
 622        },
 623        {
 624                .mask           = MLXSW_REG_PTYS_ETH_SPEED_50GBASE_KR4 |
 625                                  MLXSW_REG_PTYS_ETH_SPEED_50GBASE_CR2 |
 626                                  MLXSW_REG_PTYS_ETH_SPEED_50GBASE_KR2,
 627                .speed          = 50000,
 628        },
 629        {
 630                .mask           = MLXSW_REG_PTYS_ETH_SPEED_56GBASE_R4,
 631                .supported      = SUPPORTED_56000baseKR4_Full,
 632                .advertised     = ADVERTISED_56000baseKR4_Full,
 633                .speed          = 56000,
 634        },
 635        {
 636                .mask           = MLXSW_REG_PTYS_ETH_SPEED_100GBASE_CR4 |
 637                                  MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4 |
 638                                  MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4 |
 639                                  MLXSW_REG_PTYS_ETH_SPEED_100GBASE_LR4_ER4,
 640                .speed          = 100000,
 641        },
 642};
 643
 644#define MLXSW_SX_PORT_LINK_MODE_LEN ARRAY_SIZE(mlxsw_sx_port_link_mode)
 645
 646static u32 mlxsw_sx_from_ptys_supported_port(u32 ptys_eth_proto)
 647{
 648        if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR |
 649                              MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR |
 650                              MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4 |
 651                              MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4 |
 652                              MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4 |
 653                              MLXSW_REG_PTYS_ETH_SPEED_SGMII))
 654                return SUPPORTED_FIBRE;
 655
 656        if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR |
 657                              MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4 |
 658                              MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4 |
 659                              MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4 |
 660                              MLXSW_REG_PTYS_ETH_SPEED_1000BASE_KX))
 661                return SUPPORTED_Backplane;
 662        return 0;
 663}
 664
 665static u32 mlxsw_sx_from_ptys_supported_link(u32 ptys_eth_proto)
 666{
 667        u32 modes = 0;
 668        int i;
 669
 670        for (i = 0; i < MLXSW_SX_PORT_LINK_MODE_LEN; i++) {
 671                if (ptys_eth_proto & mlxsw_sx_port_link_mode[i].mask)
 672                        modes |= mlxsw_sx_port_link_mode[i].supported;
 673        }
 674        return modes;
 675}
 676
 677static u32 mlxsw_sx_from_ptys_advert_link(u32 ptys_eth_proto)
 678{
 679        u32 modes = 0;
 680        int i;
 681
 682        for (i = 0; i < MLXSW_SX_PORT_LINK_MODE_LEN; i++) {
 683                if (ptys_eth_proto & mlxsw_sx_port_link_mode[i].mask)
 684                        modes |= mlxsw_sx_port_link_mode[i].advertised;
 685        }
 686        return modes;
 687}
 688
 689static void mlxsw_sx_from_ptys_speed_duplex(bool carrier_ok, u32 ptys_eth_proto,
 690                                            struct ethtool_cmd *cmd)
 691{
 692        u32 speed = SPEED_UNKNOWN;
 693        u8 duplex = DUPLEX_UNKNOWN;
 694        int i;
 695
 696        if (!carrier_ok)
 697                goto out;
 698
 699        for (i = 0; i < MLXSW_SX_PORT_LINK_MODE_LEN; i++) {
 700                if (ptys_eth_proto & mlxsw_sx_port_link_mode[i].mask) {
 701                        speed = mlxsw_sx_port_link_mode[i].speed;
 702                        duplex = DUPLEX_FULL;
 703                        break;
 704                }
 705        }
 706out:
 707        ethtool_cmd_speed_set(cmd, speed);
 708        cmd->duplex = duplex;
 709}
 710
 711static u8 mlxsw_sx_port_connector_port(u32 ptys_eth_proto)
 712{
 713        if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR |
 714                              MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4 |
 715                              MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4 |
 716                              MLXSW_REG_PTYS_ETH_SPEED_SGMII))
 717                return PORT_FIBRE;
 718
 719        if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR |
 720                              MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4 |
 721                              MLXSW_REG_PTYS_ETH_SPEED_100GBASE_CR4))
 722                return PORT_DA;
 723
 724        if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR |
 725                              MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4 |
 726                              MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4 |
 727                              MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4))
 728                return PORT_NONE;
 729
 730        return PORT_OTHER;
 731}
 732
 733static int mlxsw_sx_port_get_settings(struct net_device *dev,
 734                                      struct ethtool_cmd *cmd)
 735{
 736        struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
 737        struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
 738        char ptys_pl[MLXSW_REG_PTYS_LEN];
 739        u32 eth_proto_cap;
 740        u32 eth_proto_admin;
 741        u32 eth_proto_oper;
 742        int err;
 743
 744        mlxsw_reg_ptys_pack(ptys_pl, mlxsw_sx_port->local_port, 0);
 745        err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(ptys), ptys_pl);
 746        if (err) {
 747                netdev_err(dev, "Failed to get proto");
 748                return err;
 749        }
 750        mlxsw_reg_ptys_unpack(ptys_pl, &eth_proto_cap,
 751                              &eth_proto_admin, &eth_proto_oper);
 752
 753        cmd->supported = mlxsw_sx_from_ptys_supported_port(eth_proto_cap) |
 754                         mlxsw_sx_from_ptys_supported_link(eth_proto_cap) |
 755                         SUPPORTED_Pause | SUPPORTED_Asym_Pause;
 756        cmd->advertising = mlxsw_sx_from_ptys_advert_link(eth_proto_admin);
 757        mlxsw_sx_from_ptys_speed_duplex(netif_carrier_ok(dev),
 758                                        eth_proto_oper, cmd);
 759
 760        eth_proto_oper = eth_proto_oper ? eth_proto_oper : eth_proto_cap;
 761        cmd->port = mlxsw_sx_port_connector_port(eth_proto_oper);
 762        cmd->lp_advertising = mlxsw_sx_from_ptys_advert_link(eth_proto_oper);
 763
 764        cmd->transceiver = XCVR_INTERNAL;
 765        return 0;
 766}
 767
 768static u32 mlxsw_sx_to_ptys_advert_link(u32 advertising)
 769{
 770        u32 ptys_proto = 0;
 771        int i;
 772
 773        for (i = 0; i < MLXSW_SX_PORT_LINK_MODE_LEN; i++) {
 774                if (advertising & mlxsw_sx_port_link_mode[i].advertised)
 775                        ptys_proto |= mlxsw_sx_port_link_mode[i].mask;
 776        }
 777        return ptys_proto;
 778}
 779
 780static u32 mlxsw_sx_to_ptys_speed(u32 speed)
 781{
 782        u32 ptys_proto = 0;
 783        int i;
 784
 785        for (i = 0; i < MLXSW_SX_PORT_LINK_MODE_LEN; i++) {
 786                if (speed == mlxsw_sx_port_link_mode[i].speed)
 787                        ptys_proto |= mlxsw_sx_port_link_mode[i].mask;
 788        }
 789        return ptys_proto;
 790}
 791
 792static int mlxsw_sx_port_set_settings(struct net_device *dev,
 793                                      struct ethtool_cmd *cmd)
 794{
 795        struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
 796        struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
 797        char ptys_pl[MLXSW_REG_PTYS_LEN];
 798        u32 speed;
 799        u32 eth_proto_new;
 800        u32 eth_proto_cap;
 801        u32 eth_proto_admin;
 802        bool is_up;
 803        int err;
 804
 805        speed = ethtool_cmd_speed(cmd);
 806
 807        eth_proto_new = cmd->autoneg == AUTONEG_ENABLE ?
 808                mlxsw_sx_to_ptys_advert_link(cmd->advertising) :
 809                mlxsw_sx_to_ptys_speed(speed);
 810
 811        mlxsw_reg_ptys_pack(ptys_pl, mlxsw_sx_port->local_port, 0);
 812        err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(ptys), ptys_pl);
 813        if (err) {
 814                netdev_err(dev, "Failed to get proto");
 815                return err;
 816        }
 817        mlxsw_reg_ptys_unpack(ptys_pl, &eth_proto_cap, &eth_proto_admin, NULL);
 818
 819        eth_proto_new = eth_proto_new & eth_proto_cap;
 820        if (!eth_proto_new) {
 821                netdev_err(dev, "Not supported proto admin requested");
 822                return -EINVAL;
 823        }
 824        if (eth_proto_new == eth_proto_admin)
 825                return 0;
 826
 827        mlxsw_reg_ptys_pack(ptys_pl, mlxsw_sx_port->local_port, eth_proto_new);
 828        err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(ptys), ptys_pl);
 829        if (err) {
 830                netdev_err(dev, "Failed to set proto admin");
 831                return err;
 832        }
 833
 834        err = mlxsw_sx_port_oper_status_get(mlxsw_sx_port, &is_up);
 835        if (err) {
 836                netdev_err(dev, "Failed to get oper status");
 837                return err;
 838        }
 839        if (!is_up)
 840                return 0;
 841
 842        err = mlxsw_sx_port_admin_status_set(mlxsw_sx_port, false);
 843        if (err) {
 844                netdev_err(dev, "Failed to set admin status");
 845                return err;
 846        }
 847
 848        err = mlxsw_sx_port_admin_status_set(mlxsw_sx_port, true);
 849        if (err) {
 850                netdev_err(dev, "Failed to set admin status");
 851                return err;
 852        }
 853
 854        return 0;
 855}
 856
 857static const struct ethtool_ops mlxsw_sx_port_ethtool_ops = {
 858        .get_drvinfo            = mlxsw_sx_port_get_drvinfo,
 859        .get_link               = ethtool_op_get_link,
 860        .get_strings            = mlxsw_sx_port_get_strings,
 861        .get_ethtool_stats      = mlxsw_sx_port_get_stats,
 862        .get_sset_count         = mlxsw_sx_port_get_sset_count,
 863        .get_settings           = mlxsw_sx_port_get_settings,
 864        .set_settings           = mlxsw_sx_port_set_settings,
 865};
 866
 867static int mlxsw_sx_port_attr_get(struct net_device *dev,
 868                                  struct switchdev_attr *attr)
 869{
 870        struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
 871        struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
 872
 873        switch (attr->id) {
 874        case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
 875                attr->u.ppid.id_len = sizeof(mlxsw_sx->hw_id);
 876                memcpy(&attr->u.ppid.id, &mlxsw_sx->hw_id, attr->u.ppid.id_len);
 877                break;
 878        default:
 879                return -EOPNOTSUPP;
 880        }
 881
 882        return 0;
 883}
 884
 885static const struct switchdev_ops mlxsw_sx_port_switchdev_ops = {
 886        .switchdev_port_attr_get        = mlxsw_sx_port_attr_get,
 887};
 888
 889static int mlxsw_sx_hw_id_get(struct mlxsw_sx *mlxsw_sx)
 890{
 891        char spad_pl[MLXSW_REG_SPAD_LEN];
 892        int err;
 893
 894        err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(spad), spad_pl);
 895        if (err)
 896                return err;
 897        mlxsw_reg_spad_base_mac_memcpy_from(spad_pl, mlxsw_sx->hw_id);
 898        return 0;
 899}
 900
 901static int mlxsw_sx_port_dev_addr_get(struct mlxsw_sx_port *mlxsw_sx_port)
 902{
 903        struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
 904        struct net_device *dev = mlxsw_sx_port->dev;
 905        char ppad_pl[MLXSW_REG_PPAD_LEN];
 906        int err;
 907
 908        mlxsw_reg_ppad_pack(ppad_pl, false, 0);
 909        err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(ppad), ppad_pl);
 910        if (err)
 911                return err;
 912        mlxsw_reg_ppad_mac_memcpy_from(ppad_pl, dev->dev_addr);
 913        /* The last byte value in base mac address is guaranteed
 914         * to be such it does not overflow when adding local_port
 915         * value.
 916         */
 917        dev->dev_addr[ETH_ALEN - 1] += mlxsw_sx_port->local_port;
 918        return 0;
 919}
 920
 921static int mlxsw_sx_port_stp_state_set(struct mlxsw_sx_port *mlxsw_sx_port,
 922                                       u16 vid, enum mlxsw_reg_spms_state state)
 923{
 924        struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
 925        char *spms_pl;
 926        int err;
 927
 928        spms_pl = kmalloc(MLXSW_REG_SPMS_LEN, GFP_KERNEL);
 929        if (!spms_pl)
 930                return -ENOMEM;
 931        mlxsw_reg_spms_pack(spms_pl, mlxsw_sx_port->local_port);
 932        mlxsw_reg_spms_vid_pack(spms_pl, vid, state);
 933        err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(spms), spms_pl);
 934        kfree(spms_pl);
 935        return err;
 936}
 937
 938static int mlxsw_sx_port_speed_set(struct mlxsw_sx_port *mlxsw_sx_port,
 939                                   u32 speed)
 940{
 941        struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
 942        char ptys_pl[MLXSW_REG_PTYS_LEN];
 943
 944        mlxsw_reg_ptys_pack(ptys_pl, mlxsw_sx_port->local_port, speed);
 945        return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(ptys), ptys_pl);
 946}
 947
 948static int
 949mlxsw_sx_port_mac_learning_mode_set(struct mlxsw_sx_port *mlxsw_sx_port,
 950                                    enum mlxsw_reg_spmlr_learn_mode mode)
 951{
 952        struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
 953        char spmlr_pl[MLXSW_REG_SPMLR_LEN];
 954
 955        mlxsw_reg_spmlr_pack(spmlr_pl, mlxsw_sx_port->local_port, mode);
 956        return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(spmlr), spmlr_pl);
 957}
 958
 959static int mlxsw_sx_port_create(struct mlxsw_sx *mlxsw_sx, u8 local_port)
 960{
 961        struct mlxsw_sx_port *mlxsw_sx_port;
 962        struct net_device *dev;
 963        bool usable;
 964        int err;
 965
 966        dev = alloc_etherdev(sizeof(struct mlxsw_sx_port));
 967        if (!dev)
 968                return -ENOMEM;
 969        mlxsw_sx_port = netdev_priv(dev);
 970        mlxsw_sx_port->dev = dev;
 971        mlxsw_sx_port->mlxsw_sx = mlxsw_sx;
 972        mlxsw_sx_port->local_port = local_port;
 973
 974        mlxsw_sx_port->pcpu_stats =
 975                netdev_alloc_pcpu_stats(struct mlxsw_sx_port_pcpu_stats);
 976        if (!mlxsw_sx_port->pcpu_stats) {
 977                err = -ENOMEM;
 978                goto err_alloc_stats;
 979        }
 980
 981        dev->netdev_ops = &mlxsw_sx_port_netdev_ops;
 982        dev->ethtool_ops = &mlxsw_sx_port_ethtool_ops;
 983        dev->switchdev_ops = &mlxsw_sx_port_switchdev_ops;
 984
 985        err = mlxsw_sx_port_dev_addr_get(mlxsw_sx_port);
 986        if (err) {
 987                dev_err(mlxsw_sx->bus_info->dev, "Port %d: Unable to get port mac address\n",
 988                        mlxsw_sx_port->local_port);
 989                goto err_dev_addr_get;
 990        }
 991
 992        netif_carrier_off(dev);
 993
 994        dev->features |= NETIF_F_NETNS_LOCAL | NETIF_F_LLTX | NETIF_F_SG |
 995                         NETIF_F_VLAN_CHALLENGED;
 996
 997        /* Each packet needs to have a Tx header (metadata) on top all other
 998         * headers.
 999         */
1000        dev->needed_headroom = MLXSW_TXHDR_LEN;
1001
1002        err = mlxsw_sx_port_module_check(mlxsw_sx_port, &usable);
1003        if (err) {
1004                dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to check module\n",
1005                        mlxsw_sx_port->local_port);
1006                goto err_port_module_check;
1007        }
1008
1009        if (!usable) {
1010                dev_dbg(mlxsw_sx->bus_info->dev, "Port %d: Not usable, skipping initialization\n",
1011                        mlxsw_sx_port->local_port);
1012                goto port_not_usable;
1013        }
1014
1015        err = mlxsw_sx_port_system_port_mapping_set(mlxsw_sx_port);
1016        if (err) {
1017                dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set system port mapping\n",
1018                        mlxsw_sx_port->local_port);
1019                goto err_port_system_port_mapping_set;
1020        }
1021
1022        err = mlxsw_sx_port_swid_set(mlxsw_sx_port, 0);
1023        if (err) {
1024                dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set SWID\n",
1025                        mlxsw_sx_port->local_port);
1026                goto err_port_swid_set;
1027        }
1028
1029        err = mlxsw_sx_port_speed_set(mlxsw_sx_port,
1030                                      MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4);
1031        if (err) {
1032                dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set speed\n",
1033                        mlxsw_sx_port->local_port);
1034                goto err_port_speed_set;
1035        }
1036
1037        err = mlxsw_sx_port_mtu_set(mlxsw_sx_port, ETH_DATA_LEN);
1038        if (err) {
1039                dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set MTU\n",
1040                        mlxsw_sx_port->local_port);
1041                goto err_port_mtu_set;
1042        }
1043
1044        err = mlxsw_sx_port_admin_status_set(mlxsw_sx_port, false);
1045        if (err)
1046                goto err_port_admin_status_set;
1047
1048        err = mlxsw_sx_port_stp_state_set(mlxsw_sx_port,
1049                                          MLXSW_PORT_DEFAULT_VID,
1050                                          MLXSW_REG_SPMS_STATE_FORWARDING);
1051        if (err) {
1052                dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set STP state\n",
1053                        mlxsw_sx_port->local_port);
1054                goto err_port_stp_state_set;
1055        }
1056
1057        err = mlxsw_sx_port_mac_learning_mode_set(mlxsw_sx_port,
1058                                                  MLXSW_REG_SPMLR_LEARN_MODE_DISABLE);
1059        if (err) {
1060                dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set MAC learning mode\n",
1061                        mlxsw_sx_port->local_port);
1062                goto err_port_mac_learning_mode_set;
1063        }
1064
1065        err = register_netdev(dev);
1066        if (err) {
1067                dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to register netdev\n",
1068                        mlxsw_sx_port->local_port);
1069                goto err_register_netdev;
1070        }
1071
1072        err = mlxsw_core_port_init(mlxsw_sx->core, &mlxsw_sx_port->core_port,
1073                                   mlxsw_sx_port->local_port, dev, false, 0);
1074        if (err) {
1075                dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to init core port\n",
1076                        mlxsw_sx_port->local_port);
1077                goto err_core_port_init;
1078        }
1079
1080        mlxsw_sx->ports[local_port] = mlxsw_sx_port;
1081        return 0;
1082
1083err_core_port_init:
1084        unregister_netdev(dev);
1085err_register_netdev:
1086err_port_mac_learning_mode_set:
1087err_port_stp_state_set:
1088err_port_admin_status_set:
1089err_port_mtu_set:
1090err_port_speed_set:
1091        mlxsw_sx_port_swid_set(mlxsw_sx_port, MLXSW_PORT_SWID_DISABLED_PORT);
1092err_port_swid_set:
1093err_port_system_port_mapping_set:
1094port_not_usable:
1095err_port_module_check:
1096err_dev_addr_get:
1097        free_percpu(mlxsw_sx_port->pcpu_stats);
1098err_alloc_stats:
1099        free_netdev(dev);
1100        return err;
1101}
1102
1103static void mlxsw_sx_port_remove(struct mlxsw_sx *mlxsw_sx, u8 local_port)
1104{
1105        struct mlxsw_sx_port *mlxsw_sx_port = mlxsw_sx->ports[local_port];
1106
1107        if (!mlxsw_sx_port)
1108                return;
1109        mlxsw_core_port_fini(&mlxsw_sx_port->core_port);
1110        unregister_netdev(mlxsw_sx_port->dev); /* This calls ndo_stop */
1111        mlxsw_sx_port_swid_set(mlxsw_sx_port, MLXSW_PORT_SWID_DISABLED_PORT);
1112        free_percpu(mlxsw_sx_port->pcpu_stats);
1113        free_netdev(mlxsw_sx_port->dev);
1114}
1115
1116static void mlxsw_sx_ports_remove(struct mlxsw_sx *mlxsw_sx)
1117{
1118        int i;
1119
1120        for (i = 1; i < MLXSW_PORT_MAX_PORTS; i++)
1121                mlxsw_sx_port_remove(mlxsw_sx, i);
1122        kfree(mlxsw_sx->ports);
1123}
1124
1125static int mlxsw_sx_ports_create(struct mlxsw_sx *mlxsw_sx)
1126{
1127        size_t alloc_size;
1128        int i;
1129        int err;
1130
1131        alloc_size = sizeof(struct mlxsw_sx_port *) * MLXSW_PORT_MAX_PORTS;
1132        mlxsw_sx->ports = kzalloc(alloc_size, GFP_KERNEL);
1133        if (!mlxsw_sx->ports)
1134                return -ENOMEM;
1135
1136        for (i = 1; i < MLXSW_PORT_MAX_PORTS; i++) {
1137                err = mlxsw_sx_port_create(mlxsw_sx, i);
1138                if (err)
1139                        goto err_port_create;
1140        }
1141        return 0;
1142
1143err_port_create:
1144        for (i--; i >= 1; i--)
1145                mlxsw_sx_port_remove(mlxsw_sx, i);
1146        kfree(mlxsw_sx->ports);
1147        return err;
1148}
1149
1150static void mlxsw_sx_pude_event_func(const struct mlxsw_reg_info *reg,
1151                                     char *pude_pl, void *priv)
1152{
1153        struct mlxsw_sx *mlxsw_sx = priv;
1154        struct mlxsw_sx_port *mlxsw_sx_port;
1155        enum mlxsw_reg_pude_oper_status status;
1156        u8 local_port;
1157
1158        local_port = mlxsw_reg_pude_local_port_get(pude_pl);
1159        mlxsw_sx_port = mlxsw_sx->ports[local_port];
1160        if (!mlxsw_sx_port) {
1161                dev_warn(mlxsw_sx->bus_info->dev, "Port %d: Link event received for non-existent port\n",
1162                         local_port);
1163                return;
1164        }
1165
1166        status = mlxsw_reg_pude_oper_status_get(pude_pl);
1167        if (status == MLXSW_PORT_OPER_STATUS_UP) {
1168                netdev_info(mlxsw_sx_port->dev, "link up\n");
1169                netif_carrier_on(mlxsw_sx_port->dev);
1170        } else {
1171                netdev_info(mlxsw_sx_port->dev, "link down\n");
1172                netif_carrier_off(mlxsw_sx_port->dev);
1173        }
1174}
1175
1176static struct mlxsw_event_listener mlxsw_sx_pude_event = {
1177        .func = mlxsw_sx_pude_event_func,
1178        .trap_id = MLXSW_TRAP_ID_PUDE,
1179};
1180
1181static int mlxsw_sx_event_register(struct mlxsw_sx *mlxsw_sx,
1182                                   enum mlxsw_event_trap_id trap_id)
1183{
1184        struct mlxsw_event_listener *el;
1185        char hpkt_pl[MLXSW_REG_HPKT_LEN];
1186        int err;
1187
1188        switch (trap_id) {
1189        case MLXSW_TRAP_ID_PUDE:
1190                el = &mlxsw_sx_pude_event;
1191                break;
1192        }
1193        err = mlxsw_core_event_listener_register(mlxsw_sx->core, el, mlxsw_sx);
1194        if (err)
1195                return err;
1196
1197        mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD, trap_id);
1198        err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(hpkt), hpkt_pl);
1199        if (err)
1200                goto err_event_trap_set;
1201
1202        return 0;
1203
1204err_event_trap_set:
1205        mlxsw_core_event_listener_unregister(mlxsw_sx->core, el, mlxsw_sx);
1206        return err;
1207}
1208
1209static void mlxsw_sx_event_unregister(struct mlxsw_sx *mlxsw_sx,
1210                                      enum mlxsw_event_trap_id trap_id)
1211{
1212        struct mlxsw_event_listener *el;
1213
1214        switch (trap_id) {
1215        case MLXSW_TRAP_ID_PUDE:
1216                el = &mlxsw_sx_pude_event;
1217                break;
1218        }
1219        mlxsw_core_event_listener_unregister(mlxsw_sx->core, el, mlxsw_sx);
1220}
1221
1222static void mlxsw_sx_rx_listener_func(struct sk_buff *skb, u8 local_port,
1223                                      void *priv)
1224{
1225        struct mlxsw_sx *mlxsw_sx = priv;
1226        struct mlxsw_sx_port *mlxsw_sx_port = mlxsw_sx->ports[local_port];
1227        struct mlxsw_sx_port_pcpu_stats *pcpu_stats;
1228
1229        if (unlikely(!mlxsw_sx_port)) {
1230                dev_warn_ratelimited(mlxsw_sx->bus_info->dev, "Port %d: skb received for non-existent port\n",
1231                                     local_port);
1232                return;
1233        }
1234
1235        skb->dev = mlxsw_sx_port->dev;
1236
1237        pcpu_stats = this_cpu_ptr(mlxsw_sx_port->pcpu_stats);
1238        u64_stats_update_begin(&pcpu_stats->syncp);
1239        pcpu_stats->rx_packets++;
1240        pcpu_stats->rx_bytes += skb->len;
1241        u64_stats_update_end(&pcpu_stats->syncp);
1242
1243        skb->protocol = eth_type_trans(skb, skb->dev);
1244        netif_receive_skb(skb);
1245}
1246
1247static const struct mlxsw_rx_listener mlxsw_sx_rx_listener[] = {
1248        {
1249                .func = mlxsw_sx_rx_listener_func,
1250                .local_port = MLXSW_PORT_DONT_CARE,
1251                .trap_id = MLXSW_TRAP_ID_FDB_MC,
1252        },
1253        /* Traps for specific L2 packet types, not trapped as FDB MC */
1254        {
1255                .func = mlxsw_sx_rx_listener_func,
1256                .local_port = MLXSW_PORT_DONT_CARE,
1257                .trap_id = MLXSW_TRAP_ID_STP,
1258        },
1259        {
1260                .func = mlxsw_sx_rx_listener_func,
1261                .local_port = MLXSW_PORT_DONT_CARE,
1262                .trap_id = MLXSW_TRAP_ID_LACP,
1263        },
1264        {
1265                .func = mlxsw_sx_rx_listener_func,
1266                .local_port = MLXSW_PORT_DONT_CARE,
1267                .trap_id = MLXSW_TRAP_ID_EAPOL,
1268        },
1269        {
1270                .func = mlxsw_sx_rx_listener_func,
1271                .local_port = MLXSW_PORT_DONT_CARE,
1272                .trap_id = MLXSW_TRAP_ID_LLDP,
1273        },
1274        {
1275                .func = mlxsw_sx_rx_listener_func,
1276                .local_port = MLXSW_PORT_DONT_CARE,
1277                .trap_id = MLXSW_TRAP_ID_MMRP,
1278        },
1279        {
1280                .func = mlxsw_sx_rx_listener_func,
1281                .local_port = MLXSW_PORT_DONT_CARE,
1282                .trap_id = MLXSW_TRAP_ID_MVRP,
1283        },
1284        {
1285                .func = mlxsw_sx_rx_listener_func,
1286                .local_port = MLXSW_PORT_DONT_CARE,
1287                .trap_id = MLXSW_TRAP_ID_RPVST,
1288        },
1289        {
1290                .func = mlxsw_sx_rx_listener_func,
1291                .local_port = MLXSW_PORT_DONT_CARE,
1292                .trap_id = MLXSW_TRAP_ID_DHCP,
1293        },
1294        {
1295                .func = mlxsw_sx_rx_listener_func,
1296                .local_port = MLXSW_PORT_DONT_CARE,
1297                .trap_id = MLXSW_TRAP_ID_IGMP_QUERY,
1298        },
1299        {
1300                .func = mlxsw_sx_rx_listener_func,
1301                .local_port = MLXSW_PORT_DONT_CARE,
1302                .trap_id = MLXSW_TRAP_ID_IGMP_V1_REPORT,
1303        },
1304        {
1305                .func = mlxsw_sx_rx_listener_func,
1306                .local_port = MLXSW_PORT_DONT_CARE,
1307                .trap_id = MLXSW_TRAP_ID_IGMP_V2_REPORT,
1308        },
1309        {
1310                .func = mlxsw_sx_rx_listener_func,
1311                .local_port = MLXSW_PORT_DONT_CARE,
1312                .trap_id = MLXSW_TRAP_ID_IGMP_V2_LEAVE,
1313        },
1314        {
1315                .func = mlxsw_sx_rx_listener_func,
1316                .local_port = MLXSW_PORT_DONT_CARE,
1317                .trap_id = MLXSW_TRAP_ID_IGMP_V3_REPORT,
1318        },
1319};
1320
1321static int mlxsw_sx_traps_init(struct mlxsw_sx *mlxsw_sx)
1322{
1323        char htgt_pl[MLXSW_REG_HTGT_LEN];
1324        char hpkt_pl[MLXSW_REG_HPKT_LEN];
1325        int i;
1326        int err;
1327
1328        mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_RX);
1329        err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(htgt), htgt_pl);
1330        if (err)
1331                return err;
1332
1333        mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_CTRL);
1334        err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(htgt), htgt_pl);
1335        if (err)
1336                return err;
1337
1338        for (i = 0; i < ARRAY_SIZE(mlxsw_sx_rx_listener); i++) {
1339                err = mlxsw_core_rx_listener_register(mlxsw_sx->core,
1340                                                      &mlxsw_sx_rx_listener[i],
1341                                                      mlxsw_sx);
1342                if (err)
1343                        goto err_rx_listener_register;
1344
1345                mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_TRAP_TO_CPU,
1346                                    mlxsw_sx_rx_listener[i].trap_id);
1347                err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(hpkt), hpkt_pl);
1348                if (err)
1349                        goto err_rx_trap_set;
1350        }
1351        return 0;
1352
1353err_rx_trap_set:
1354        mlxsw_core_rx_listener_unregister(mlxsw_sx->core,
1355                                          &mlxsw_sx_rx_listener[i],
1356                                          mlxsw_sx);
1357err_rx_listener_register:
1358        for (i--; i >= 0; i--) {
1359                mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD,
1360                                    mlxsw_sx_rx_listener[i].trap_id);
1361                mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(hpkt), hpkt_pl);
1362
1363                mlxsw_core_rx_listener_unregister(mlxsw_sx->core,
1364                                                  &mlxsw_sx_rx_listener[i],
1365                                                  mlxsw_sx);
1366        }
1367        return err;
1368}
1369
1370static void mlxsw_sx_traps_fini(struct mlxsw_sx *mlxsw_sx)
1371{
1372        char hpkt_pl[MLXSW_REG_HPKT_LEN];
1373        int i;
1374
1375        for (i = 0; i < ARRAY_SIZE(mlxsw_sx_rx_listener); i++) {
1376                mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD,
1377                                    mlxsw_sx_rx_listener[i].trap_id);
1378                mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(hpkt), hpkt_pl);
1379
1380                mlxsw_core_rx_listener_unregister(mlxsw_sx->core,
1381                                                  &mlxsw_sx_rx_listener[i],
1382                                                  mlxsw_sx);
1383        }
1384}
1385
1386static int mlxsw_sx_flood_init(struct mlxsw_sx *mlxsw_sx)
1387{
1388        char sfgc_pl[MLXSW_REG_SFGC_LEN];
1389        char sgcr_pl[MLXSW_REG_SGCR_LEN];
1390        char *sftr_pl;
1391        int err;
1392
1393        /* Configure a flooding table, which includes only CPU port. */
1394        sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL);
1395        if (!sftr_pl)
1396                return -ENOMEM;
1397        mlxsw_reg_sftr_pack(sftr_pl, 0, 0, MLXSW_REG_SFGC_TABLE_TYPE_SINGLE, 0,
1398                            MLXSW_PORT_CPU_PORT, true);
1399        err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sftr), sftr_pl);
1400        kfree(sftr_pl);
1401        if (err)
1402                return err;
1403
1404        /* Flood different packet types using the flooding table. */
1405        mlxsw_reg_sfgc_pack(sfgc_pl,
1406                            MLXSW_REG_SFGC_TYPE_UNKNOWN_UNICAST,
1407                            MLXSW_REG_SFGC_BRIDGE_TYPE_1Q_FID,
1408                            MLXSW_REG_SFGC_TABLE_TYPE_SINGLE,
1409                            0);
1410        err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sfgc), sfgc_pl);
1411        if (err)
1412                return err;
1413
1414        mlxsw_reg_sfgc_pack(sfgc_pl,
1415                            MLXSW_REG_SFGC_TYPE_BROADCAST,
1416                            MLXSW_REG_SFGC_BRIDGE_TYPE_1Q_FID,
1417                            MLXSW_REG_SFGC_TABLE_TYPE_SINGLE,
1418                            0);
1419        err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sfgc), sfgc_pl);
1420        if (err)
1421                return err;
1422
1423        mlxsw_reg_sfgc_pack(sfgc_pl,
1424                            MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_NON_IP,
1425                            MLXSW_REG_SFGC_BRIDGE_TYPE_1Q_FID,
1426                            MLXSW_REG_SFGC_TABLE_TYPE_SINGLE,
1427                            0);
1428        err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sfgc), sfgc_pl);
1429        if (err)
1430                return err;
1431
1432        mlxsw_reg_sfgc_pack(sfgc_pl,
1433                            MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV6,
1434                            MLXSW_REG_SFGC_BRIDGE_TYPE_1Q_FID,
1435                            MLXSW_REG_SFGC_TABLE_TYPE_SINGLE,
1436                            0);
1437        err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sfgc), sfgc_pl);
1438        if (err)
1439                return err;
1440
1441        mlxsw_reg_sfgc_pack(sfgc_pl,
1442                            MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV4,
1443                            MLXSW_REG_SFGC_BRIDGE_TYPE_1Q_FID,
1444                            MLXSW_REG_SFGC_TABLE_TYPE_SINGLE,
1445                            0);
1446        err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sfgc), sfgc_pl);
1447        if (err)
1448                return err;
1449
1450        mlxsw_reg_sgcr_pack(sgcr_pl, true);
1451        return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sgcr), sgcr_pl);
1452}
1453
1454static int mlxsw_sx_init(struct mlxsw_core *mlxsw_core,
1455                         const struct mlxsw_bus_info *mlxsw_bus_info)
1456{
1457        struct mlxsw_sx *mlxsw_sx = mlxsw_core_driver_priv(mlxsw_core);
1458        int err;
1459
1460        mlxsw_sx->core = mlxsw_core;
1461        mlxsw_sx->bus_info = mlxsw_bus_info;
1462
1463        err = mlxsw_sx_hw_id_get(mlxsw_sx);
1464        if (err) {
1465                dev_err(mlxsw_sx->bus_info->dev, "Failed to get switch HW ID\n");
1466                return err;
1467        }
1468
1469        err = mlxsw_sx_ports_create(mlxsw_sx);
1470        if (err) {
1471                dev_err(mlxsw_sx->bus_info->dev, "Failed to create ports\n");
1472                return err;
1473        }
1474
1475        err = mlxsw_sx_event_register(mlxsw_sx, MLXSW_TRAP_ID_PUDE);
1476        if (err) {
1477                dev_err(mlxsw_sx->bus_info->dev, "Failed to register for PUDE events\n");
1478                goto err_event_register;
1479        }
1480
1481        err = mlxsw_sx_traps_init(mlxsw_sx);
1482        if (err) {
1483                dev_err(mlxsw_sx->bus_info->dev, "Failed to set traps for RX\n");
1484                goto err_rx_listener_register;
1485        }
1486
1487        err = mlxsw_sx_flood_init(mlxsw_sx);
1488        if (err) {
1489                dev_err(mlxsw_sx->bus_info->dev, "Failed to initialize flood tables\n");
1490                goto err_flood_init;
1491        }
1492
1493        return 0;
1494
1495err_flood_init:
1496        mlxsw_sx_traps_fini(mlxsw_sx);
1497err_rx_listener_register:
1498        mlxsw_sx_event_unregister(mlxsw_sx, MLXSW_TRAP_ID_PUDE);
1499err_event_register:
1500        mlxsw_sx_ports_remove(mlxsw_sx);
1501        return err;
1502}
1503
1504static void mlxsw_sx_fini(struct mlxsw_core *mlxsw_core)
1505{
1506        struct mlxsw_sx *mlxsw_sx = mlxsw_core_driver_priv(mlxsw_core);
1507
1508        mlxsw_sx_traps_fini(mlxsw_sx);
1509        mlxsw_sx_event_unregister(mlxsw_sx, MLXSW_TRAP_ID_PUDE);
1510        mlxsw_sx_ports_remove(mlxsw_sx);
1511}
1512
1513static struct mlxsw_config_profile mlxsw_sx_config_profile = {
1514        .used_max_vepa_channels         = 1,
1515        .max_vepa_channels              = 0,
1516        .used_max_mid                   = 1,
1517        .max_mid                        = 7000,
1518        .used_max_pgt                   = 1,
1519        .max_pgt                        = 0,
1520        .used_max_system_port           = 1,
1521        .max_system_port                = 48000,
1522        .used_max_vlan_groups           = 1,
1523        .max_vlan_groups                = 127,
1524        .used_max_regions               = 1,
1525        .max_regions                    = 400,
1526        .used_flood_tables              = 1,
1527        .max_flood_tables               = 2,
1528        .max_vid_flood_tables           = 1,
1529        .used_flood_mode                = 1,
1530        .flood_mode                     = 3,
1531        .used_max_ib_mc                 = 1,
1532        .max_ib_mc                      = 0,
1533        .used_max_pkey                  = 1,
1534        .max_pkey                       = 0,
1535        .swid_config                    = {
1536                {
1537                        .used_type      = 1,
1538                        .type           = MLXSW_PORT_SWID_TYPE_ETH,
1539                }
1540        },
1541        .resource_query_enable          = 0,
1542};
1543
1544static struct mlxsw_driver mlxsw_sx_driver = {
1545        .kind                   = MLXSW_DEVICE_KIND_SWITCHX2,
1546        .owner                  = THIS_MODULE,
1547        .priv_size              = sizeof(struct mlxsw_sx),
1548        .init                   = mlxsw_sx_init,
1549        .fini                   = mlxsw_sx_fini,
1550        .txhdr_construct        = mlxsw_sx_txhdr_construct,
1551        .txhdr_len              = MLXSW_TXHDR_LEN,
1552        .profile                = &mlxsw_sx_config_profile,
1553};
1554
1555static int __init mlxsw_sx_module_init(void)
1556{
1557        return mlxsw_core_driver_register(&mlxsw_sx_driver);
1558}
1559
1560static void __exit mlxsw_sx_module_exit(void)
1561{
1562        mlxsw_core_driver_unregister(&mlxsw_sx_driver);
1563}
1564
1565module_init(mlxsw_sx_module_init);
1566module_exit(mlxsw_sx_module_exit);
1567
1568MODULE_LICENSE("Dual BSD/GPL");
1569MODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>");
1570MODULE_DESCRIPTION("Mellanox SwitchX-2 driver");
1571MODULE_MLXSW_DRIVER_ALIAS(MLXSW_DEVICE_KIND_SWITCHX2);
1572