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 17 static const char mlxsw_m_driver_name[] = "mlxsw_minimal"; 18 19 struct mlxsw_m_port; 20 21 struct mlxsw_m { 22 struct mlxsw_m_port **ports; 23 int *module_to_port; 24 struct mlxsw_core *core; 25 const struct mlxsw_bus_info *bus_info; 26 u8 base_mac[ETH_ALEN]; 27 u8 max_ports; 28 }; 29 30 struct mlxsw_m_port { 31 struct net_device *dev; 32 struct mlxsw_m *mlxsw_m; 33 u8 local_port; 34 u8 module; 35 }; 36 37 static int mlxsw_m_port_dummy_open_stop(struct net_device *dev) 38 { 39 return 0; 40 } 41 42 static int 43 mlxsw_m_port_get_phys_port_name(struct net_device *dev, char *name, size_t len) 44 { 45 struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev); 46 struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; 47 u8 local_port = mlxsw_m_port->local_port; 48 49 return mlxsw_core_port_get_phys_port_name(core, local_port, name, len); 50 } 51 52 static int mlxsw_m_port_get_port_parent_id(struct net_device *dev, 53 struct netdev_phys_item_id *ppid) 54 { 55 struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev); 56 struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m; 57 58 ppid->id_len = sizeof(mlxsw_m->base_mac); 59 memcpy(&ppid->id, &mlxsw_m->base_mac, ppid->id_len); 60 61 return 0; 62 } 63 64 static const struct net_device_ops mlxsw_m_port_netdev_ops = { 65 .ndo_open = mlxsw_m_port_dummy_open_stop, 66 .ndo_stop = mlxsw_m_port_dummy_open_stop, 67 .ndo_get_phys_port_name = mlxsw_m_port_get_phys_port_name, 68 .ndo_get_port_parent_id = mlxsw_m_port_get_port_parent_id, 69 }; 70 71 static int mlxsw_m_get_module_info(struct net_device *netdev, 72 struct ethtool_modinfo *modinfo) 73 { 74 struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev); 75 struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; 76 77 return mlxsw_env_get_module_info(core, mlxsw_m_port->module, modinfo); 78 } 79 80 static int 81 mlxsw_m_get_module_eeprom(struct net_device *netdev, struct ethtool_eeprom *ee, 82 u8 *data) 83 { 84 struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev); 85 struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; 86 87 return mlxsw_env_get_module_eeprom(netdev, core, mlxsw_m_port->module, 88 ee, data); 89 } 90 91 static const struct ethtool_ops mlxsw_m_port_ethtool_ops = { 92 .get_module_info = mlxsw_m_get_module_info, 93 .get_module_eeprom = mlxsw_m_get_module_eeprom, 94 }; 95 96 static int 97 mlxsw_m_port_module_info_get(struct mlxsw_m *mlxsw_m, u8 local_port, 98 u8 *p_module, u8 *p_width) 99 { 100 char pmlp_pl[MLXSW_REG_PMLP_LEN]; 101 int err; 102 103 mlxsw_reg_pmlp_pack(pmlp_pl, local_port); 104 err = mlxsw_reg_query(mlxsw_m->core, MLXSW_REG(pmlp), pmlp_pl); 105 if (err) 106 return err; 107 *p_module = mlxsw_reg_pmlp_module_get(pmlp_pl, 0); 108 *p_width = mlxsw_reg_pmlp_width_get(pmlp_pl); 109 110 return 0; 111 } 112 113 static int 114 mlxsw_m_port_dev_addr_get(struct mlxsw_m_port *mlxsw_m_port) 115 { 116 struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m; 117 struct net_device *dev = mlxsw_m_port->dev; 118 char ppad_pl[MLXSW_REG_PPAD_LEN]; 119 int err; 120 121 mlxsw_reg_ppad_pack(ppad_pl, false, 0); 122 err = mlxsw_reg_query(mlxsw_m->core, MLXSW_REG(ppad), ppad_pl); 123 if (err) 124 return err; 125 mlxsw_reg_ppad_mac_memcpy_from(ppad_pl, dev->dev_addr); 126 /* The last byte value in base mac address is guaranteed 127 * to be such it does not overflow when adding local_port 128 * value. 129 */ 130 dev->dev_addr[ETH_ALEN - 1] += mlxsw_m_port->module + 1; 131 return 0; 132 } 133 134 static int 135 mlxsw_m_port_create(struct mlxsw_m *mlxsw_m, u8 local_port, u8 module) 136 { 137 struct mlxsw_m_port *mlxsw_m_port; 138 struct net_device *dev; 139 int err; 140 141 err = mlxsw_core_port_init(mlxsw_m->core, local_port); 142 if (err) { 143 dev_err(mlxsw_m->bus_info->dev, "Port %d: Failed to init core port\n", 144 local_port); 145 return err; 146 } 147 148 dev = alloc_etherdev(sizeof(struct mlxsw_m_port)); 149 if (!dev) { 150 err = -ENOMEM; 151 goto err_alloc_etherdev; 152 } 153 154 SET_NETDEV_DEV(dev, mlxsw_m->bus_info->dev); 155 mlxsw_m_port = netdev_priv(dev); 156 mlxsw_m_port->dev = dev; 157 mlxsw_m_port->mlxsw_m = mlxsw_m; 158 mlxsw_m_port->local_port = local_port; 159 mlxsw_m_port->module = module; 160 161 dev->netdev_ops = &mlxsw_m_port_netdev_ops; 162 dev->ethtool_ops = &mlxsw_m_port_ethtool_ops; 163 164 err = mlxsw_m_port_dev_addr_get(mlxsw_m_port); 165 if (err) { 166 dev_err(mlxsw_m->bus_info->dev, "Port %d: Unable to get port mac address\n", 167 mlxsw_m_port->local_port); 168 goto err_dev_addr_get; 169 } 170 171 netif_carrier_off(dev); 172 mlxsw_m->ports[local_port] = mlxsw_m_port; 173 err = register_netdev(dev); 174 if (err) { 175 dev_err(mlxsw_m->bus_info->dev, "Port %d: Failed to register netdev\n", 176 mlxsw_m_port->local_port); 177 goto err_register_netdev; 178 } 179 180 mlxsw_core_port_eth_set(mlxsw_m->core, mlxsw_m_port->local_port, 181 mlxsw_m_port, dev, module + 1, false, 0); 182 183 return 0; 184 185 err_register_netdev: 186 mlxsw_m->ports[local_port] = NULL; 187 free_netdev(dev); 188 err_dev_addr_get: 189 err_alloc_etherdev: 190 mlxsw_core_port_fini(mlxsw_m->core, local_port); 191 return err; 192 } 193 194 static void mlxsw_m_port_remove(struct mlxsw_m *mlxsw_m, u8 local_port) 195 { 196 struct mlxsw_m_port *mlxsw_m_port = mlxsw_m->ports[local_port]; 197 198 mlxsw_core_port_clear(mlxsw_m->core, local_port, mlxsw_m); 199 unregister_netdev(mlxsw_m_port->dev); /* This calls ndo_stop */ 200 mlxsw_m->ports[local_port] = NULL; 201 free_netdev(mlxsw_m_port->dev); 202 mlxsw_core_port_fini(mlxsw_m->core, local_port); 203 } 204 205 static int mlxsw_m_port_module_map(struct mlxsw_m *mlxsw_m, u8 local_port, 206 u8 *last_module) 207 { 208 u8 module, width; 209 int err; 210 211 /* Fill out to local port mapping array */ 212 err = mlxsw_m_port_module_info_get(mlxsw_m, local_port, &module, 213 &width); 214 if (err) 215 return err; 216 217 if (!width) 218 return 0; 219 /* Skip, if port belongs to the cluster */ 220 if (module == *last_module) 221 return 0; 222 *last_module = module; 223 mlxsw_m->module_to_port[module] = ++mlxsw_m->max_ports; 224 225 return 0; 226 } 227 228 static void mlxsw_m_port_module_unmap(struct mlxsw_m *mlxsw_m, u8 module) 229 { 230 mlxsw_m->module_to_port[module] = -1; 231 } 232 233 static int mlxsw_m_ports_create(struct mlxsw_m *mlxsw_m) 234 { 235 unsigned int max_ports = mlxsw_core_max_ports(mlxsw_m->core); 236 u8 last_module = max_ports; 237 int i; 238 int err; 239 240 mlxsw_m->ports = kcalloc(max_ports, sizeof(*mlxsw_m->ports), 241 GFP_KERNEL); 242 if (!mlxsw_m->ports) 243 return -ENOMEM; 244 245 mlxsw_m->module_to_port = kmalloc_array(max_ports, sizeof(int), 246 GFP_KERNEL); 247 if (!mlxsw_m->module_to_port) { 248 err = -ENOMEM; 249 goto err_module_to_port_alloc; 250 } 251 252 /* Invalidate the entries of module to local port mapping array */ 253 for (i = 0; i < max_ports; i++) 254 mlxsw_m->module_to_port[i] = -1; 255 256 /* Fill out module to local port mapping array */ 257 for (i = 1; i < max_ports; i++) { 258 err = mlxsw_m_port_module_map(mlxsw_m, i, &last_module); 259 if (err) 260 goto err_module_to_port_map; 261 } 262 263 /* Create port objects for each valid entry */ 264 for (i = 0; i < mlxsw_m->max_ports; i++) { 265 if (mlxsw_m->module_to_port[i] > 0) { 266 err = mlxsw_m_port_create(mlxsw_m, 267 mlxsw_m->module_to_port[i], 268 i); 269 if (err) 270 goto err_module_to_port_create; 271 } 272 } 273 274 return 0; 275 276 err_module_to_port_create: 277 for (i--; i >= 0; i--) { 278 if (mlxsw_m->module_to_port[i] > 0) 279 mlxsw_m_port_remove(mlxsw_m, 280 mlxsw_m->module_to_port[i]); 281 } 282 i = max_ports; 283 err_module_to_port_map: 284 for (i--; i > 0; i--) 285 mlxsw_m_port_module_unmap(mlxsw_m, i); 286 kfree(mlxsw_m->module_to_port); 287 err_module_to_port_alloc: 288 kfree(mlxsw_m->ports); 289 return err; 290 } 291 292 static void mlxsw_m_ports_remove(struct mlxsw_m *mlxsw_m) 293 { 294 int i; 295 296 for (i = 0; i < mlxsw_m->max_ports; i++) { 297 if (mlxsw_m->module_to_port[i] > 0) { 298 mlxsw_m_port_remove(mlxsw_m, 299 mlxsw_m->module_to_port[i]); 300 mlxsw_m_port_module_unmap(mlxsw_m, i); 301 } 302 } 303 304 kfree(mlxsw_m->module_to_port); 305 kfree(mlxsw_m->ports); 306 } 307 308 static int mlxsw_m_init(struct mlxsw_core *mlxsw_core, 309 const struct mlxsw_bus_info *mlxsw_bus_info) 310 { 311 struct mlxsw_m *mlxsw_m = mlxsw_core_driver_priv(mlxsw_core); 312 int err; 313 314 mlxsw_m->core = mlxsw_core; 315 mlxsw_m->bus_info = mlxsw_bus_info; 316 317 err = mlxsw_m_ports_create(mlxsw_m); 318 if (err) { 319 dev_err(mlxsw_m->bus_info->dev, "Failed to create ports\n"); 320 return err; 321 } 322 323 return 0; 324 } 325 326 static void mlxsw_m_fini(struct mlxsw_core *mlxsw_core) 327 { 328 struct mlxsw_m *mlxsw_m = mlxsw_core_driver_priv(mlxsw_core); 329 330 mlxsw_m_ports_remove(mlxsw_m); 331 } 332 333 static const struct mlxsw_config_profile mlxsw_m_config_profile; 334 335 static struct mlxsw_driver mlxsw_m_driver = { 336 .kind = mlxsw_m_driver_name, 337 .priv_size = sizeof(struct mlxsw_m), 338 .init = mlxsw_m_init, 339 .fini = mlxsw_m_fini, 340 .profile = &mlxsw_m_config_profile, 341 .res_query_enabled = true, 342 }; 343 344 static const struct i2c_device_id mlxsw_m_i2c_id[] = { 345 { "mlxsw_minimal", 0}, 346 { }, 347 }; 348 349 static struct i2c_driver mlxsw_m_i2c_driver = { 350 .driver.name = "mlxsw_minimal", 351 .class = I2C_CLASS_HWMON, 352 .id_table = mlxsw_m_i2c_id, 353 }; 354 355 static int __init mlxsw_m_module_init(void) 356 { 357 int err; 358 359 err = mlxsw_core_driver_register(&mlxsw_m_driver); 360 if (err) 361 return err; 362 363 err = mlxsw_i2c_driver_register(&mlxsw_m_i2c_driver); 364 if (err) 365 goto err_i2c_driver_register; 366 367 return 0; 368 369 err_i2c_driver_register: 370 mlxsw_core_driver_unregister(&mlxsw_m_driver); 371 372 return err; 373 } 374 375 static void __exit mlxsw_m_module_exit(void) 376 { 377 mlxsw_i2c_driver_unregister(&mlxsw_m_i2c_driver); 378 mlxsw_core_driver_unregister(&mlxsw_m_driver); 379 } 380 381 module_init(mlxsw_m_module_init); 382 module_exit(mlxsw_m_module_exit); 383 384 MODULE_LICENSE("Dual BSD/GPL"); 385 MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>"); 386 MODULE_DESCRIPTION("Mellanox minimal driver"); 387 MODULE_DEVICE_TABLE(i2c, mlxsw_m_i2c_id); 388