Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
0002 /* Copyright (c) 2016-2019 Mellanox Technologies. All rights reserved */
0003 
0004 #include <linux/netdevice.h>
0005 #include <linux/etherdevice.h>
0006 #include <linux/ethtool.h>
0007 #include <linux/i2c.h>
0008 #include <linux/kernel.h>
0009 #include <linux/module.h>
0010 #include <linux/mod_devicetable.h>
0011 #include <linux/types.h>
0012 
0013 #include "core.h"
0014 #include "core_env.h"
0015 #include "i2c.h"
0016 
0017 static const char mlxsw_m_driver_name[] = "mlxsw_minimal";
0018 
0019 #define MLXSW_M_FWREV_MINOR 2000
0020 #define MLXSW_M_FWREV_SUBMINOR  1886
0021 
0022 static const struct mlxsw_fw_rev mlxsw_m_fw_rev = {
0023     .minor = MLXSW_M_FWREV_MINOR,
0024     .subminor = MLXSW_M_FWREV_SUBMINOR,
0025 };
0026 
0027 struct mlxsw_m_port;
0028 
0029 struct mlxsw_m {
0030     struct mlxsw_m_port **ports;
0031     int *module_to_port;
0032     struct mlxsw_core *core;
0033     const struct mlxsw_bus_info *bus_info;
0034     u8 base_mac[ETH_ALEN];
0035     u8 max_ports;
0036 };
0037 
0038 struct mlxsw_m_port {
0039     struct net_device *dev;
0040     struct mlxsw_m *mlxsw_m;
0041     u16 local_port;
0042     u8 module;
0043 };
0044 
0045 static int mlxsw_m_base_mac_get(struct mlxsw_m *mlxsw_m)
0046 {
0047     char spad_pl[MLXSW_REG_SPAD_LEN] = {0};
0048     int err;
0049 
0050     err = mlxsw_reg_query(mlxsw_m->core, MLXSW_REG(spad), spad_pl);
0051     if (err)
0052         return err;
0053     mlxsw_reg_spad_base_mac_memcpy_from(spad_pl, mlxsw_m->base_mac);
0054     return 0;
0055 }
0056 
0057 static int mlxsw_m_port_open(struct net_device *dev)
0058 {
0059     struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev);
0060     struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m;
0061 
0062     return mlxsw_env_module_port_up(mlxsw_m->core, 0,
0063                     mlxsw_m_port->module);
0064 }
0065 
0066 static int mlxsw_m_port_stop(struct net_device *dev)
0067 {
0068     struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev);
0069     struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m;
0070 
0071     mlxsw_env_module_port_down(mlxsw_m->core, 0, mlxsw_m_port->module);
0072     return 0;
0073 }
0074 
0075 static struct devlink_port *
0076 mlxsw_m_port_get_devlink_port(struct net_device *dev)
0077 {
0078     struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev);
0079     struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m;
0080 
0081     return mlxsw_core_port_devlink_port_get(mlxsw_m->core,
0082                         mlxsw_m_port->local_port);
0083 }
0084 
0085 static const struct net_device_ops mlxsw_m_port_netdev_ops = {
0086     .ndo_open       = mlxsw_m_port_open,
0087     .ndo_stop       = mlxsw_m_port_stop,
0088     .ndo_get_devlink_port   = mlxsw_m_port_get_devlink_port,
0089 };
0090 
0091 static void mlxsw_m_module_get_drvinfo(struct net_device *dev,
0092                        struct ethtool_drvinfo *drvinfo)
0093 {
0094     struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev);
0095     struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m;
0096 
0097     strlcpy(drvinfo->driver, mlxsw_m->bus_info->device_kind,
0098         sizeof(drvinfo->driver));
0099     snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
0100          "%d.%d.%d",
0101          mlxsw_m->bus_info->fw_rev.major,
0102          mlxsw_m->bus_info->fw_rev.minor,
0103          mlxsw_m->bus_info->fw_rev.subminor);
0104     strlcpy(drvinfo->bus_info, mlxsw_m->bus_info->device_name,
0105         sizeof(drvinfo->bus_info));
0106 }
0107 
0108 static int mlxsw_m_get_module_info(struct net_device *netdev,
0109                    struct ethtool_modinfo *modinfo)
0110 {
0111     struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev);
0112     struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core;
0113 
0114     return mlxsw_env_get_module_info(netdev, core, 0, mlxsw_m_port->module,
0115                      modinfo);
0116 }
0117 
0118 static int
0119 mlxsw_m_get_module_eeprom(struct net_device *netdev, struct ethtool_eeprom *ee,
0120               u8 *data)
0121 {
0122     struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev);
0123     struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core;
0124 
0125     return mlxsw_env_get_module_eeprom(netdev, core, 0,
0126                        mlxsw_m_port->module, ee, data);
0127 }
0128 
0129 static int
0130 mlxsw_m_get_module_eeprom_by_page(struct net_device *netdev,
0131                   const struct ethtool_module_eeprom *page,
0132                   struct netlink_ext_ack *extack)
0133 {
0134     struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev);
0135     struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core;
0136 
0137     return mlxsw_env_get_module_eeprom_by_page(core, 0,
0138                            mlxsw_m_port->module,
0139                            page, extack);
0140 }
0141 
0142 static int mlxsw_m_reset(struct net_device *netdev, u32 *flags)
0143 {
0144     struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev);
0145     struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core;
0146 
0147     return mlxsw_env_reset_module(netdev, core, 0, mlxsw_m_port->module,
0148                       flags);
0149 }
0150 
0151 static int
0152 mlxsw_m_get_module_power_mode(struct net_device *netdev,
0153                   struct ethtool_module_power_mode_params *params,
0154                   struct netlink_ext_ack *extack)
0155 {
0156     struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev);
0157     struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core;
0158 
0159     return mlxsw_env_get_module_power_mode(core, 0, mlxsw_m_port->module,
0160                            params, extack);
0161 }
0162 
0163 static int
0164 mlxsw_m_set_module_power_mode(struct net_device *netdev,
0165                   const struct ethtool_module_power_mode_params *params,
0166                   struct netlink_ext_ack *extack)
0167 {
0168     struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev);
0169     struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core;
0170 
0171     return mlxsw_env_set_module_power_mode(core, 0, mlxsw_m_port->module,
0172                            params->policy, extack);
0173 }
0174 
0175 static const struct ethtool_ops mlxsw_m_port_ethtool_ops = {
0176     .get_drvinfo        = mlxsw_m_module_get_drvinfo,
0177     .get_module_info    = mlxsw_m_get_module_info,
0178     .get_module_eeprom  = mlxsw_m_get_module_eeprom,
0179     .get_module_eeprom_by_page = mlxsw_m_get_module_eeprom_by_page,
0180     .reset          = mlxsw_m_reset,
0181     .get_module_power_mode  = mlxsw_m_get_module_power_mode,
0182     .set_module_power_mode  = mlxsw_m_set_module_power_mode,
0183 };
0184 
0185 static int
0186 mlxsw_m_port_module_info_get(struct mlxsw_m *mlxsw_m, u16 local_port,
0187                  u8 *p_module, u8 *p_width)
0188 {
0189     char pmlp_pl[MLXSW_REG_PMLP_LEN];
0190     int err;
0191 
0192     mlxsw_reg_pmlp_pack(pmlp_pl, local_port);
0193     err = mlxsw_reg_query(mlxsw_m->core, MLXSW_REG(pmlp), pmlp_pl);
0194     if (err)
0195         return err;
0196     *p_module = mlxsw_reg_pmlp_module_get(pmlp_pl, 0);
0197     *p_width = mlxsw_reg_pmlp_width_get(pmlp_pl);
0198 
0199     return 0;
0200 }
0201 
0202 static int
0203 mlxsw_m_port_dev_addr_get(struct mlxsw_m_port *mlxsw_m_port)
0204 {
0205     struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m;
0206     char ppad_pl[MLXSW_REG_PPAD_LEN];
0207     u8 addr[ETH_ALEN];
0208     int err;
0209 
0210     mlxsw_reg_ppad_pack(ppad_pl, false, 0);
0211     err = mlxsw_reg_query(mlxsw_m->core, MLXSW_REG(ppad), ppad_pl);
0212     if (err)
0213         return err;
0214     mlxsw_reg_ppad_mac_memcpy_from(ppad_pl, addr);
0215     eth_hw_addr_gen(mlxsw_m_port->dev, addr, mlxsw_m_port->module + 1);
0216     return 0;
0217 }
0218 
0219 static int
0220 mlxsw_m_port_create(struct mlxsw_m *mlxsw_m, u16 local_port, u8 module)
0221 {
0222     struct mlxsw_m_port *mlxsw_m_port;
0223     struct net_device *dev;
0224     int err;
0225 
0226     err = mlxsw_core_port_init(mlxsw_m->core, local_port, 0,
0227                    module + 1, false, 0, false,
0228                    0, mlxsw_m->base_mac,
0229                    sizeof(mlxsw_m->base_mac));
0230     if (err) {
0231         dev_err(mlxsw_m->bus_info->dev, "Port %d: Failed to init core port\n",
0232             local_port);
0233         return err;
0234     }
0235 
0236     dev = alloc_etherdev(sizeof(struct mlxsw_m_port));
0237     if (!dev) {
0238         err = -ENOMEM;
0239         goto err_alloc_etherdev;
0240     }
0241 
0242     SET_NETDEV_DEV(dev, mlxsw_m->bus_info->dev);
0243     dev_net_set(dev, mlxsw_core_net(mlxsw_m->core));
0244     mlxsw_m_port = netdev_priv(dev);
0245     mlxsw_m_port->dev = dev;
0246     mlxsw_m_port->mlxsw_m = mlxsw_m;
0247     mlxsw_m_port->local_port = local_port;
0248     mlxsw_m_port->module = module;
0249 
0250     dev->netdev_ops = &mlxsw_m_port_netdev_ops;
0251     dev->ethtool_ops = &mlxsw_m_port_ethtool_ops;
0252 
0253     err = mlxsw_m_port_dev_addr_get(mlxsw_m_port);
0254     if (err) {
0255         dev_err(mlxsw_m->bus_info->dev, "Port %d: Unable to get port mac address\n",
0256             mlxsw_m_port->local_port);
0257         goto err_dev_addr_get;
0258     }
0259 
0260     netif_carrier_off(dev);
0261     mlxsw_m->ports[local_port] = mlxsw_m_port;
0262     err = register_netdev(dev);
0263     if (err) {
0264         dev_err(mlxsw_m->bus_info->dev, "Port %d: Failed to register netdev\n",
0265             mlxsw_m_port->local_port);
0266         goto err_register_netdev;
0267     }
0268 
0269     mlxsw_core_port_eth_set(mlxsw_m->core, mlxsw_m_port->local_port,
0270                 mlxsw_m_port, dev);
0271 
0272     return 0;
0273 
0274 err_register_netdev:
0275     mlxsw_m->ports[local_port] = NULL;
0276 err_dev_addr_get:
0277     free_netdev(dev);
0278 err_alloc_etherdev:
0279     mlxsw_core_port_fini(mlxsw_m->core, local_port);
0280     return err;
0281 }
0282 
0283 static void mlxsw_m_port_remove(struct mlxsw_m *mlxsw_m, u16 local_port)
0284 {
0285     struct mlxsw_m_port *mlxsw_m_port = mlxsw_m->ports[local_port];
0286 
0287     mlxsw_core_port_clear(mlxsw_m->core, local_port, mlxsw_m);
0288     unregister_netdev(mlxsw_m_port->dev); /* This calls ndo_stop */
0289     mlxsw_m->ports[local_port] = NULL;
0290     free_netdev(mlxsw_m_port->dev);
0291     mlxsw_core_port_fini(mlxsw_m->core, local_port);
0292 }
0293 
0294 static int mlxsw_m_port_module_map(struct mlxsw_m *mlxsw_m, u16 local_port,
0295                    u8 *last_module)
0296 {
0297     unsigned int max_ports = mlxsw_core_max_ports(mlxsw_m->core);
0298     u8 module, width;
0299     int err;
0300 
0301     /* Fill out to local port mapping array */
0302     err = mlxsw_m_port_module_info_get(mlxsw_m, local_port, &module,
0303                        &width);
0304     if (err)
0305         return err;
0306 
0307     if (!width)
0308         return 0;
0309     /* Skip, if port belongs to the cluster */
0310     if (module == *last_module)
0311         return 0;
0312     *last_module = module;
0313 
0314     if (WARN_ON_ONCE(module >= max_ports))
0315         return -EINVAL;
0316     mlxsw_env_module_port_map(mlxsw_m->core, 0, module);
0317     mlxsw_m->module_to_port[module] = ++mlxsw_m->max_ports;
0318 
0319     return 0;
0320 }
0321 
0322 static void mlxsw_m_port_module_unmap(struct mlxsw_m *mlxsw_m, u8 module)
0323 {
0324     mlxsw_m->module_to_port[module] = -1;
0325     mlxsw_env_module_port_unmap(mlxsw_m->core, 0, module);
0326 }
0327 
0328 static int mlxsw_m_ports_create(struct mlxsw_m *mlxsw_m)
0329 {
0330     unsigned int max_ports = mlxsw_core_max_ports(mlxsw_m->core);
0331     u8 last_module = max_ports;
0332     int i;
0333     int err;
0334 
0335     mlxsw_m->ports = kcalloc(max_ports, sizeof(*mlxsw_m->ports),
0336                  GFP_KERNEL);
0337     if (!mlxsw_m->ports)
0338         return -ENOMEM;
0339 
0340     mlxsw_m->module_to_port = kmalloc_array(max_ports, sizeof(int),
0341                         GFP_KERNEL);
0342     if (!mlxsw_m->module_to_port) {
0343         err = -ENOMEM;
0344         goto err_module_to_port_alloc;
0345     }
0346 
0347     /* Invalidate the entries of module to local port mapping array */
0348     for (i = 0; i < max_ports; i++)
0349         mlxsw_m->module_to_port[i] = -1;
0350 
0351     /* Fill out module to local port mapping array */
0352     for (i = 1; i < max_ports; i++) {
0353         err = mlxsw_m_port_module_map(mlxsw_m, i, &last_module);
0354         if (err)
0355             goto err_module_to_port_map;
0356     }
0357 
0358     /* Create port objects for each valid entry */
0359     for (i = 0; i < mlxsw_m->max_ports; i++) {
0360         if (mlxsw_m->module_to_port[i] > 0) {
0361             err = mlxsw_m_port_create(mlxsw_m,
0362                           mlxsw_m->module_to_port[i],
0363                           i);
0364             if (err)
0365                 goto err_module_to_port_create;
0366         }
0367     }
0368 
0369     return 0;
0370 
0371 err_module_to_port_create:
0372     for (i--; i >= 0; i--) {
0373         if (mlxsw_m->module_to_port[i] > 0)
0374             mlxsw_m_port_remove(mlxsw_m,
0375                         mlxsw_m->module_to_port[i]);
0376     }
0377     i = max_ports;
0378 err_module_to_port_map:
0379     for (i--; i > 0; i--)
0380         mlxsw_m_port_module_unmap(mlxsw_m, i);
0381     kfree(mlxsw_m->module_to_port);
0382 err_module_to_port_alloc:
0383     kfree(mlxsw_m->ports);
0384     return err;
0385 }
0386 
0387 static void mlxsw_m_ports_remove(struct mlxsw_m *mlxsw_m)
0388 {
0389     int i;
0390 
0391     for (i = 0; i < mlxsw_m->max_ports; i++) {
0392         if (mlxsw_m->module_to_port[i] > 0) {
0393             mlxsw_m_port_remove(mlxsw_m,
0394                         mlxsw_m->module_to_port[i]);
0395             mlxsw_m_port_module_unmap(mlxsw_m, i);
0396         }
0397     }
0398 
0399     kfree(mlxsw_m->module_to_port);
0400     kfree(mlxsw_m->ports);
0401 }
0402 
0403 static int mlxsw_m_fw_rev_validate(struct mlxsw_m *mlxsw_m)
0404 {
0405     const struct mlxsw_fw_rev *rev = &mlxsw_m->bus_info->fw_rev;
0406 
0407     /* Validate driver and FW are compatible.
0408      * Do not check major version, since it defines chip type, while
0409      * driver is supposed to support any type.
0410      */
0411     if (mlxsw_core_fw_rev_minor_subminor_validate(rev, &mlxsw_m_fw_rev))
0412         return 0;
0413 
0414     dev_err(mlxsw_m->bus_info->dev, "The firmware version %d.%d.%d is incompatible with the driver (required >= %d.%d.%d)\n",
0415         rev->major, rev->minor, rev->subminor, rev->major,
0416         mlxsw_m_fw_rev.minor, mlxsw_m_fw_rev.subminor);
0417 
0418     return -EINVAL;
0419 }
0420 
0421 static int mlxsw_m_init(struct mlxsw_core *mlxsw_core,
0422             const struct mlxsw_bus_info *mlxsw_bus_info,
0423             struct netlink_ext_ack *extack)
0424 {
0425     struct mlxsw_m *mlxsw_m = mlxsw_core_driver_priv(mlxsw_core);
0426     int err;
0427 
0428     mlxsw_m->core = mlxsw_core;
0429     mlxsw_m->bus_info = mlxsw_bus_info;
0430 
0431     err = mlxsw_m_fw_rev_validate(mlxsw_m);
0432     if (err)
0433         return err;
0434 
0435     err = mlxsw_m_base_mac_get(mlxsw_m);
0436     if (err) {
0437         dev_err(mlxsw_m->bus_info->dev, "Failed to get base mac\n");
0438         return err;
0439     }
0440 
0441     err = mlxsw_m_ports_create(mlxsw_m);
0442     if (err) {
0443         dev_err(mlxsw_m->bus_info->dev, "Failed to create ports\n");
0444         return err;
0445     }
0446 
0447     return 0;
0448 }
0449 
0450 static void mlxsw_m_fini(struct mlxsw_core *mlxsw_core)
0451 {
0452     struct mlxsw_m *mlxsw_m = mlxsw_core_driver_priv(mlxsw_core);
0453 
0454     mlxsw_m_ports_remove(mlxsw_m);
0455 }
0456 
0457 static const struct mlxsw_config_profile mlxsw_m_config_profile;
0458 
0459 static struct mlxsw_driver mlxsw_m_driver = {
0460     .kind           = mlxsw_m_driver_name,
0461     .priv_size      = sizeof(struct mlxsw_m),
0462     .init           = mlxsw_m_init,
0463     .fini           = mlxsw_m_fini,
0464     .profile        = &mlxsw_m_config_profile,
0465 };
0466 
0467 static const struct i2c_device_id mlxsw_m_i2c_id[] = {
0468     { "mlxsw_minimal", 0},
0469     { },
0470 };
0471 
0472 static struct i2c_driver mlxsw_m_i2c_driver = {
0473     .driver.name = "mlxsw_minimal",
0474     .class = I2C_CLASS_HWMON,
0475     .id_table = mlxsw_m_i2c_id,
0476 };
0477 
0478 static int __init mlxsw_m_module_init(void)
0479 {
0480     int err;
0481 
0482     err = mlxsw_core_driver_register(&mlxsw_m_driver);
0483     if (err)
0484         return err;
0485 
0486     err = mlxsw_i2c_driver_register(&mlxsw_m_i2c_driver);
0487     if (err)
0488         goto err_i2c_driver_register;
0489 
0490     return 0;
0491 
0492 err_i2c_driver_register:
0493     mlxsw_core_driver_unregister(&mlxsw_m_driver);
0494 
0495     return err;
0496 }
0497 
0498 static void __exit mlxsw_m_module_exit(void)
0499 {
0500     mlxsw_i2c_driver_unregister(&mlxsw_m_i2c_driver);
0501     mlxsw_core_driver_unregister(&mlxsw_m_driver);
0502 }
0503 
0504 module_init(mlxsw_m_module_init);
0505 module_exit(mlxsw_m_module_exit);
0506 
0507 MODULE_LICENSE("Dual BSD/GPL");
0508 MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
0509 MODULE_DESCRIPTION("Mellanox minimal driver");
0510 MODULE_DEVICE_TABLE(i2c, mlxsw_m_i2c_id);