0001
0002
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);
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
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
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
0348 for (i = 0; i < max_ports; i++)
0349 mlxsw_m->module_to_port[i] = -1;
0350
0351
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
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
0408
0409
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);