linux/drivers/net/ethernet/mellanox/mlxsw/minimal.c
<<
>>
Prefs
   1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
   2/* Copyright (c) 2016-2019 Mellanox Technologies. All rights reserved */
   3
   4#include <linux/netdevice.h>
   5#include <linux/etherdevice.h>
   6#include <linux/ethtool.h>
   7#include <linux/i2c.h>
   8#include <linux/kernel.h>
   9#include <linux/module.h>
  10#include <linux/mod_devicetable.h>
  11#include <linux/types.h>
  12
  13#include "core.h"
  14#include "core_env.h"
  15#include "i2c.h"
  16
  17static const char mlxsw_m_driver_name[] = "mlxsw_minimal";
  18
  19#define MLXSW_M_FWREV_MINOR     2000
  20#define MLXSW_M_FWREV_SUBMINOR  1886
  21
  22static const struct mlxsw_fw_rev mlxsw_m_fw_rev = {
  23        .minor = MLXSW_M_FWREV_MINOR,
  24        .subminor = MLXSW_M_FWREV_SUBMINOR,
  25};
  26
  27struct mlxsw_m_port;
  28
  29struct mlxsw_m {
  30        struct mlxsw_m_port **ports;
  31        int *module_to_port;
  32        struct mlxsw_core *core;
  33        const struct mlxsw_bus_info *bus_info;
  34        u8 base_mac[ETH_ALEN];
  35        u8 max_ports;
  36};
  37
  38struct mlxsw_m_port {
  39        struct net_device *dev;
  40        struct mlxsw_m *mlxsw_m;
  41        u8 local_port;
  42        u8 module;
  43};
  44
  45static int mlxsw_m_base_mac_get(struct mlxsw_m *mlxsw_m)
  46{
  47        char spad_pl[MLXSW_REG_SPAD_LEN] = {0};
  48        int err;
  49
  50        err = mlxsw_reg_query(mlxsw_m->core, MLXSW_REG(spad), spad_pl);
  51        if (err)
  52                return err;
  53        mlxsw_reg_spad_base_mac_memcpy_from(spad_pl, mlxsw_m->base_mac);
  54        return 0;
  55}
  56
  57static int mlxsw_m_port_dummy_open_stop(struct net_device *dev)
  58{
  59        return 0;
  60}
  61
  62static struct devlink_port *
  63mlxsw_m_port_get_devlink_port(struct net_device *dev)
  64{
  65        struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev);
  66        struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m;
  67
  68        return mlxsw_core_port_devlink_port_get(mlxsw_m->core,
  69                                                mlxsw_m_port->local_port);
  70}
  71
  72static const struct net_device_ops mlxsw_m_port_netdev_ops = {
  73        .ndo_open               = mlxsw_m_port_dummy_open_stop,
  74        .ndo_stop               = mlxsw_m_port_dummy_open_stop,
  75        .ndo_get_devlink_port   = mlxsw_m_port_get_devlink_port,
  76};
  77
  78static void mlxsw_m_module_get_drvinfo(struct net_device *dev,
  79                                       struct ethtool_drvinfo *drvinfo)
  80{
  81        struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev);
  82        struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m;
  83
  84        strlcpy(drvinfo->driver, mlxsw_m->bus_info->device_kind,
  85                sizeof(drvinfo->driver));
  86        snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
  87                 "%d.%d.%d",
  88                 mlxsw_m->bus_info->fw_rev.major,
  89                 mlxsw_m->bus_info->fw_rev.minor,
  90                 mlxsw_m->bus_info->fw_rev.subminor);
  91        strlcpy(drvinfo->bus_info, mlxsw_m->bus_info->device_name,
  92                sizeof(drvinfo->bus_info));
  93}
  94
  95static int mlxsw_m_get_module_info(struct net_device *netdev,
  96                                   struct ethtool_modinfo *modinfo)
  97{
  98        struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev);
  99        struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core;
 100
 101        return mlxsw_env_get_module_info(core, mlxsw_m_port->module, modinfo);
 102}
 103
 104static int
 105mlxsw_m_get_module_eeprom(struct net_device *netdev, struct ethtool_eeprom *ee,
 106                          u8 *data)
 107{
 108        struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev);
 109        struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core;
 110
 111        return mlxsw_env_get_module_eeprom(netdev, core, mlxsw_m_port->module,
 112                                           ee, data);
 113}
 114
 115static int
 116mlxsw_m_get_module_eeprom_by_page(struct net_device *netdev,
 117                                  const struct ethtool_module_eeprom *page,
 118                                  struct netlink_ext_ack *extack)
 119{
 120        struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev);
 121        struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core;
 122
 123        return mlxsw_env_get_module_eeprom_by_page(core, mlxsw_m_port->module,
 124                                                   page, extack);
 125}
 126
 127static const struct ethtool_ops mlxsw_m_port_ethtool_ops = {
 128        .get_drvinfo            = mlxsw_m_module_get_drvinfo,
 129        .get_module_info        = mlxsw_m_get_module_info,
 130        .get_module_eeprom      = mlxsw_m_get_module_eeprom,
 131        .get_module_eeprom_by_page = mlxsw_m_get_module_eeprom_by_page,
 132};
 133
 134static int
 135mlxsw_m_port_module_info_get(struct mlxsw_m *mlxsw_m, u8 local_port,
 136                             u8 *p_module, u8 *p_width)
 137{
 138        char pmlp_pl[MLXSW_REG_PMLP_LEN];
 139        int err;
 140
 141        mlxsw_reg_pmlp_pack(pmlp_pl, local_port);
 142        err = mlxsw_reg_query(mlxsw_m->core, MLXSW_REG(pmlp), pmlp_pl);
 143        if (err)
 144                return err;
 145        *p_module = mlxsw_reg_pmlp_module_get(pmlp_pl, 0);
 146        *p_width = mlxsw_reg_pmlp_width_get(pmlp_pl);
 147
 148        return 0;
 149}
 150
 151static int
 152mlxsw_m_port_dev_addr_get(struct mlxsw_m_port *mlxsw_m_port)
 153{
 154        struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m;
 155        struct net_device *dev = mlxsw_m_port->dev;
 156        char ppad_pl[MLXSW_REG_PPAD_LEN];
 157        int err;
 158
 159        mlxsw_reg_ppad_pack(ppad_pl, false, 0);
 160        err = mlxsw_reg_query(mlxsw_m->core, MLXSW_REG(ppad), ppad_pl);
 161        if (err)
 162                return err;
 163        mlxsw_reg_ppad_mac_memcpy_from(ppad_pl, dev->dev_addr);
 164        /* The last byte value in base mac address is guaranteed
 165         * to be such it does not overflow when adding local_port
 166         * value.
 167         */
 168        dev->dev_addr[ETH_ALEN - 1] += mlxsw_m_port->module + 1;
 169        return 0;
 170}
 171
 172static int
 173mlxsw_m_port_create(struct mlxsw_m *mlxsw_m, u8 local_port, u8 module)
 174{
 175        struct mlxsw_m_port *mlxsw_m_port;
 176        struct net_device *dev;
 177        int err;
 178
 179        err = mlxsw_core_port_init(mlxsw_m->core, local_port,
 180                                   module + 1, false, 0, false,
 181                                   0, mlxsw_m->base_mac,
 182                                   sizeof(mlxsw_m->base_mac));
 183        if (err) {
 184                dev_err(mlxsw_m->bus_info->dev, "Port %d: Failed to init core port\n",
 185                        local_port);
 186                return err;
 187        }
 188
 189        dev = alloc_etherdev(sizeof(struct mlxsw_m_port));
 190        if (!dev) {
 191                err = -ENOMEM;
 192                goto err_alloc_etherdev;
 193        }
 194
 195        SET_NETDEV_DEV(dev, mlxsw_m->bus_info->dev);
 196        dev_net_set(dev, mlxsw_core_net(mlxsw_m->core));
 197        mlxsw_m_port = netdev_priv(dev);
 198        mlxsw_m_port->dev = dev;
 199        mlxsw_m_port->mlxsw_m = mlxsw_m;
 200        mlxsw_m_port->local_port = local_port;
 201        mlxsw_m_port->module = module;
 202
 203        dev->netdev_ops = &mlxsw_m_port_netdev_ops;
 204        dev->ethtool_ops = &mlxsw_m_port_ethtool_ops;
 205
 206        err = mlxsw_m_port_dev_addr_get(mlxsw_m_port);
 207        if (err) {
 208                dev_err(mlxsw_m->bus_info->dev, "Port %d: Unable to get port mac address\n",
 209                        mlxsw_m_port->local_port);
 210                goto err_dev_addr_get;
 211        }
 212
 213        netif_carrier_off(dev);
 214        mlxsw_m->ports[local_port] = mlxsw_m_port;
 215        err = register_netdev(dev);
 216        if (err) {
 217                dev_err(mlxsw_m->bus_info->dev, "Port %d: Failed to register netdev\n",
 218                        mlxsw_m_port->local_port);
 219                goto err_register_netdev;
 220        }
 221
 222        mlxsw_core_port_eth_set(mlxsw_m->core, mlxsw_m_port->local_port,
 223                                mlxsw_m_port, dev);
 224
 225        return 0;
 226
 227err_register_netdev:
 228        mlxsw_m->ports[local_port] = NULL;
 229err_dev_addr_get:
 230        free_netdev(dev);
 231err_alloc_etherdev:
 232        mlxsw_core_port_fini(mlxsw_m->core, local_port);
 233        return err;
 234}
 235
 236static void mlxsw_m_port_remove(struct mlxsw_m *mlxsw_m, u8 local_port)
 237{
 238        struct mlxsw_m_port *mlxsw_m_port = mlxsw_m->ports[local_port];
 239
 240        mlxsw_core_port_clear(mlxsw_m->core, local_port, mlxsw_m);
 241        unregister_netdev(mlxsw_m_port->dev); /* This calls ndo_stop */
 242        mlxsw_m->ports[local_port] = NULL;
 243        free_netdev(mlxsw_m_port->dev);
 244        mlxsw_core_port_fini(mlxsw_m->core, local_port);
 245}
 246
 247static int mlxsw_m_port_module_map(struct mlxsw_m *mlxsw_m, u8 local_port,
 248                                   u8 *last_module)
 249{
 250        unsigned int max_ports = mlxsw_core_max_ports(mlxsw_m->core);
 251        u8 module, width;
 252        int err;
 253
 254        /* Fill out to local port mapping array */
 255        err = mlxsw_m_port_module_info_get(mlxsw_m, local_port, &module,
 256                                           &width);
 257        if (err)
 258                return err;
 259
 260        if (!width)
 261                return 0;
 262        /* Skip, if port belongs to the cluster */
 263        if (module == *last_module)
 264                return 0;
 265        *last_module = module;
 266
 267        if (WARN_ON_ONCE(module >= max_ports))
 268                return -EINVAL;
 269        mlxsw_m->module_to_port[module] = ++mlxsw_m->max_ports;
 270
 271        return 0;
 272}
 273
 274static void mlxsw_m_port_module_unmap(struct mlxsw_m *mlxsw_m, u8 module)
 275{
 276        mlxsw_m->module_to_port[module] = -1;
 277}
 278
 279static int mlxsw_m_ports_create(struct mlxsw_m *mlxsw_m)
 280{
 281        unsigned int max_ports = mlxsw_core_max_ports(mlxsw_m->core);
 282        u8 last_module = max_ports;
 283        int i;
 284        int err;
 285
 286        mlxsw_m->ports = kcalloc(max_ports, sizeof(*mlxsw_m->ports),
 287                                 GFP_KERNEL);
 288        if (!mlxsw_m->ports)
 289                return -ENOMEM;
 290
 291        mlxsw_m->module_to_port = kmalloc_array(max_ports, sizeof(int),
 292                                                GFP_KERNEL);
 293        if (!mlxsw_m->module_to_port) {
 294                err = -ENOMEM;
 295                goto err_module_to_port_alloc;
 296        }
 297
 298        /* Invalidate the entries of module to local port mapping array */
 299        for (i = 0; i < max_ports; i++)
 300                mlxsw_m->module_to_port[i] = -1;
 301
 302        /* Fill out module to local port mapping array */
 303        for (i = 1; i < max_ports; i++) {
 304                err = mlxsw_m_port_module_map(mlxsw_m, i, &last_module);
 305                if (err)
 306                        goto err_module_to_port_map;
 307        }
 308
 309        /* Create port objects for each valid entry */
 310        for (i = 0; i < mlxsw_m->max_ports; i++) {
 311                if (mlxsw_m->module_to_port[i] > 0 &&
 312                    !mlxsw_core_port_is_xm(mlxsw_m->core, i)) {
 313                        err = mlxsw_m_port_create(mlxsw_m,
 314                                                  mlxsw_m->module_to_port[i],
 315                                                  i);
 316                        if (err)
 317                                goto err_module_to_port_create;
 318                }
 319        }
 320
 321        return 0;
 322
 323err_module_to_port_create:
 324        for (i--; i >= 0; i--) {
 325                if (mlxsw_m->module_to_port[i] > 0)
 326                        mlxsw_m_port_remove(mlxsw_m,
 327                                            mlxsw_m->module_to_port[i]);
 328        }
 329        i = max_ports;
 330err_module_to_port_map:
 331        for (i--; i > 0; i--)
 332                mlxsw_m_port_module_unmap(mlxsw_m, i);
 333        kfree(mlxsw_m->module_to_port);
 334err_module_to_port_alloc:
 335        kfree(mlxsw_m->ports);
 336        return err;
 337}
 338
 339static void mlxsw_m_ports_remove(struct mlxsw_m *mlxsw_m)
 340{
 341        int i;
 342
 343        for (i = 0; i < mlxsw_m->max_ports; i++) {
 344                if (mlxsw_m->module_to_port[i] > 0) {
 345                        mlxsw_m_port_remove(mlxsw_m,
 346                                            mlxsw_m->module_to_port[i]);
 347                        mlxsw_m_port_module_unmap(mlxsw_m, i);
 348                }
 349        }
 350
 351        kfree(mlxsw_m->module_to_port);
 352        kfree(mlxsw_m->ports);
 353}
 354
 355static int mlxsw_m_fw_rev_validate(struct mlxsw_m *mlxsw_m)
 356{
 357        const struct mlxsw_fw_rev *rev = &mlxsw_m->bus_info->fw_rev;
 358
 359        /* Validate driver and FW are compatible.
 360         * Do not check major version, since it defines chip type, while
 361         * driver is supposed to support any type.
 362         */
 363        if (mlxsw_core_fw_rev_minor_subminor_validate(rev, &mlxsw_m_fw_rev))
 364                return 0;
 365
 366        dev_err(mlxsw_m->bus_info->dev, "The firmware version %d.%d.%d is incompatible with the driver (required >= %d.%d.%d)\n",
 367                rev->major, rev->minor, rev->subminor, rev->major,
 368                mlxsw_m_fw_rev.minor, mlxsw_m_fw_rev.subminor);
 369
 370        return -EINVAL;
 371}
 372
 373static int mlxsw_m_init(struct mlxsw_core *mlxsw_core,
 374                        const struct mlxsw_bus_info *mlxsw_bus_info,
 375                        struct netlink_ext_ack *extack)
 376{
 377        struct mlxsw_m *mlxsw_m = mlxsw_core_driver_priv(mlxsw_core);
 378        int err;
 379
 380        mlxsw_m->core = mlxsw_core;
 381        mlxsw_m->bus_info = mlxsw_bus_info;
 382
 383        err = mlxsw_m_fw_rev_validate(mlxsw_m);
 384        if (err)
 385                return err;
 386
 387        err = mlxsw_m_base_mac_get(mlxsw_m);
 388        if (err) {
 389                dev_err(mlxsw_m->bus_info->dev, "Failed to get base mac\n");
 390                return err;
 391        }
 392
 393        err = mlxsw_m_ports_create(mlxsw_m);
 394        if (err) {
 395                dev_err(mlxsw_m->bus_info->dev, "Failed to create ports\n");
 396                return err;
 397        }
 398
 399        return 0;
 400}
 401
 402static void mlxsw_m_fini(struct mlxsw_core *mlxsw_core)
 403{
 404        struct mlxsw_m *mlxsw_m = mlxsw_core_driver_priv(mlxsw_core);
 405
 406        mlxsw_m_ports_remove(mlxsw_m);
 407}
 408
 409static const struct mlxsw_config_profile mlxsw_m_config_profile;
 410
 411static struct mlxsw_driver mlxsw_m_driver = {
 412        .kind                   = mlxsw_m_driver_name,
 413        .priv_size              = sizeof(struct mlxsw_m),
 414        .init                   = mlxsw_m_init,
 415        .fini                   = mlxsw_m_fini,
 416        .profile                = &mlxsw_m_config_profile,
 417        .res_query_enabled      = true,
 418};
 419
 420static const struct i2c_device_id mlxsw_m_i2c_id[] = {
 421        { "mlxsw_minimal", 0},
 422        { },
 423};
 424
 425static struct i2c_driver mlxsw_m_i2c_driver = {
 426        .driver.name = "mlxsw_minimal",
 427        .class = I2C_CLASS_HWMON,
 428        .id_table = mlxsw_m_i2c_id,
 429};
 430
 431static int __init mlxsw_m_module_init(void)
 432{
 433        int err;
 434
 435        err = mlxsw_core_driver_register(&mlxsw_m_driver);
 436        if (err)
 437                return err;
 438
 439        err = mlxsw_i2c_driver_register(&mlxsw_m_i2c_driver);
 440        if (err)
 441                goto err_i2c_driver_register;
 442
 443        return 0;
 444
 445err_i2c_driver_register:
 446        mlxsw_core_driver_unregister(&mlxsw_m_driver);
 447
 448        return err;
 449}
 450
 451static void __exit mlxsw_m_module_exit(void)
 452{
 453        mlxsw_i2c_driver_unregister(&mlxsw_m_i2c_driver);
 454        mlxsw_core_driver_unregister(&mlxsw_m_driver);
 455}
 456
 457module_init(mlxsw_m_module_init);
 458module_exit(mlxsw_m_module_exit);
 459
 460MODULE_LICENSE("Dual BSD/GPL");
 461MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
 462MODULE_DESCRIPTION("Mellanox minimal driver");
 463MODULE_DEVICE_TABLE(i2c, mlxsw_m_i2c_id);
 464