762 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/* 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 200020#define MLXSW_M_FWREV_SUBMINOR 188621 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_line_card {30 bool active;31 int module_to_port[];32};33 34struct mlxsw_m {35 struct mlxsw_m_port **ports;36 struct mlxsw_core *core;37 const struct mlxsw_bus_info *bus_info;38 u8 base_mac[ETH_ALEN];39 u8 max_ports;40 u8 max_modules_per_slot; /* Maximum number of modules per-slot. */41 u8 num_of_slots; /* Including the main board. */42 struct mlxsw_m_line_card **line_cards;43};44 45struct mlxsw_m_port {46 struct net_device *dev;47 struct mlxsw_m *mlxsw_m;48 u16 local_port;49 u8 slot_index;50 u8 module;51 u8 module_offset;52};53 54static int mlxsw_m_base_mac_get(struct mlxsw_m *mlxsw_m)55{56 char spad_pl[MLXSW_REG_SPAD_LEN] = {0};57 int err;58 59 err = mlxsw_reg_query(mlxsw_m->core, MLXSW_REG(spad), spad_pl);60 if (err)61 return err;62 mlxsw_reg_spad_base_mac_memcpy_from(spad_pl, mlxsw_m->base_mac);63 return 0;64}65 66static int mlxsw_m_port_open(struct net_device *dev)67{68 struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev);69 struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m;70 71 return mlxsw_env_module_port_up(mlxsw_m->core, 0,72 mlxsw_m_port->module);73}74 75static int mlxsw_m_port_stop(struct net_device *dev)76{77 struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev);78 struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m;79 80 mlxsw_env_module_port_down(mlxsw_m->core, 0, mlxsw_m_port->module);81 return 0;82}83 84static const struct net_device_ops mlxsw_m_port_netdev_ops = {85 .ndo_open = mlxsw_m_port_open,86 .ndo_stop = mlxsw_m_port_stop,87};88 89static void mlxsw_m_module_get_drvinfo(struct net_device *dev,90 struct ethtool_drvinfo *drvinfo)91{92 struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev);93 struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m;94 95 strscpy(drvinfo->driver, mlxsw_m->bus_info->device_kind,96 sizeof(drvinfo->driver));97 snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),98 "%d.%d.%d",99 mlxsw_m->bus_info->fw_rev.major,100 mlxsw_m->bus_info->fw_rev.minor,101 mlxsw_m->bus_info->fw_rev.subminor);102 strscpy(drvinfo->bus_info, mlxsw_m->bus_info->device_name,103 sizeof(drvinfo->bus_info));104}105 106static int mlxsw_m_get_module_info(struct net_device *netdev,107 struct ethtool_modinfo *modinfo)108{109 struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev);110 struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core;111 112 return mlxsw_env_get_module_info(netdev, core,113 mlxsw_m_port->slot_index,114 mlxsw_m_port->module, modinfo);115}116 117static int118mlxsw_m_get_module_eeprom(struct net_device *netdev, struct ethtool_eeprom *ee,119 u8 *data)120{121 struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev);122 struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core;123 124 return mlxsw_env_get_module_eeprom(netdev, core,125 mlxsw_m_port->slot_index,126 mlxsw_m_port->module, ee, data);127}128 129static int130mlxsw_m_get_module_eeprom_by_page(struct net_device *netdev,131 const struct ethtool_module_eeprom *page,132 struct netlink_ext_ack *extack)133{134 struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev);135 struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core;136 137 return mlxsw_env_get_module_eeprom_by_page(core,138 mlxsw_m_port->slot_index,139 mlxsw_m_port->module,140 page, extack);141}142 143static int144mlxsw_m_set_module_eeprom_by_page(struct net_device *netdev,145 const struct ethtool_module_eeprom *page,146 struct netlink_ext_ack *extack)147{148 struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev);149 struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core;150 151 return mlxsw_env_set_module_eeprom_by_page(core,152 mlxsw_m_port->slot_index,153 mlxsw_m_port->module,154 page, extack);155}156 157static int mlxsw_m_reset(struct net_device *netdev, u32 *flags)158{159 struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev);160 struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core;161 162 return mlxsw_env_reset_module(netdev, core, mlxsw_m_port->slot_index,163 mlxsw_m_port->module,164 flags);165}166 167static int168mlxsw_m_get_module_power_mode(struct net_device *netdev,169 struct ethtool_module_power_mode_params *params,170 struct netlink_ext_ack *extack)171{172 struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev);173 struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core;174 175 return mlxsw_env_get_module_power_mode(core, mlxsw_m_port->slot_index,176 mlxsw_m_port->module,177 params, extack);178}179 180static int181mlxsw_m_set_module_power_mode(struct net_device *netdev,182 const struct ethtool_module_power_mode_params *params,183 struct netlink_ext_ack *extack)184{185 struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev);186 struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core;187 188 return mlxsw_env_set_module_power_mode(core, mlxsw_m_port->slot_index,189 mlxsw_m_port->module,190 params->policy, extack);191}192 193static const struct ethtool_ops mlxsw_m_port_ethtool_ops = {194 .get_drvinfo = mlxsw_m_module_get_drvinfo,195 .get_module_info = mlxsw_m_get_module_info,196 .get_module_eeprom = mlxsw_m_get_module_eeprom,197 .get_module_eeprom_by_page = mlxsw_m_get_module_eeprom_by_page,198 .set_module_eeprom_by_page = mlxsw_m_set_module_eeprom_by_page,199 .reset = mlxsw_m_reset,200 .get_module_power_mode = mlxsw_m_get_module_power_mode,201 .set_module_power_mode = mlxsw_m_set_module_power_mode,202};203 204static int205mlxsw_m_port_module_info_get(struct mlxsw_m *mlxsw_m, u16 local_port,206 u8 *p_module, u8 *p_width, u8 *p_slot_index)207{208 char pmlp_pl[MLXSW_REG_PMLP_LEN];209 int err;210 211 mlxsw_reg_pmlp_pack(pmlp_pl, local_port);212 err = mlxsw_reg_query(mlxsw_m->core, MLXSW_REG(pmlp), pmlp_pl);213 if (err)214 return err;215 *p_module = mlxsw_reg_pmlp_module_get(pmlp_pl, 0);216 *p_width = mlxsw_reg_pmlp_width_get(pmlp_pl);217 *p_slot_index = mlxsw_reg_pmlp_slot_index_get(pmlp_pl, 0);218 219 return 0;220}221 222static int223mlxsw_m_port_dev_addr_get(struct mlxsw_m_port *mlxsw_m_port)224{225 struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m;226 char ppad_pl[MLXSW_REG_PPAD_LEN];227 u8 addr[ETH_ALEN];228 int err;229 230 mlxsw_reg_ppad_pack(ppad_pl, false, 0);231 err = mlxsw_reg_query(mlxsw_m->core, MLXSW_REG(ppad), ppad_pl);232 if (err)233 return err;234 mlxsw_reg_ppad_mac_memcpy_from(ppad_pl, addr);235 eth_hw_addr_gen(mlxsw_m_port->dev, addr, mlxsw_m_port->module + 1 +236 mlxsw_m_port->module_offset);237 return 0;238}239 240static bool mlxsw_m_port_created(struct mlxsw_m *mlxsw_m, u16 local_port)241{242 return mlxsw_m->ports[local_port];243}244 245static int246mlxsw_m_port_create(struct mlxsw_m *mlxsw_m, u16 local_port, u8 slot_index,247 u8 module)248{249 struct mlxsw_m_port *mlxsw_m_port;250 struct net_device *dev;251 int err;252 253 err = mlxsw_core_port_init(mlxsw_m->core, local_port, slot_index,254 module + 1, false, 0, false,255 0, mlxsw_m->base_mac,256 sizeof(mlxsw_m->base_mac));257 if (err) {258 dev_err(mlxsw_m->bus_info->dev, "Port %d: Failed to init core port\n",259 local_port);260 return err;261 }262 263 dev = alloc_etherdev(sizeof(struct mlxsw_m_port));264 if (!dev) {265 err = -ENOMEM;266 goto err_alloc_etherdev;267 }268 269 SET_NETDEV_DEV(dev, mlxsw_m->bus_info->dev);270 dev_net_set(dev, mlxsw_core_net(mlxsw_m->core));271 mlxsw_m_port = netdev_priv(dev);272 mlxsw_core_port_netdev_link(mlxsw_m->core, local_port,273 mlxsw_m_port, dev);274 mlxsw_m_port->dev = dev;275 mlxsw_m_port->mlxsw_m = mlxsw_m;276 mlxsw_m_port->local_port = local_port;277 mlxsw_m_port->module = module;278 mlxsw_m_port->slot_index = slot_index;279 /* Add module offset for line card. Offset for main board iz zero.280 * For line card in slot #n offset is calculated as (#n - 1)281 * multiplied by maximum modules number, which could be found on a line282 * card.283 */284 mlxsw_m_port->module_offset = mlxsw_m_port->slot_index ?285 (mlxsw_m_port->slot_index - 1) *286 mlxsw_m->max_modules_per_slot : 0;287 288 dev->netdev_ops = &mlxsw_m_port_netdev_ops;289 dev->ethtool_ops = &mlxsw_m_port_ethtool_ops;290 291 err = mlxsw_m_port_dev_addr_get(mlxsw_m_port);292 if (err) {293 dev_err(mlxsw_m->bus_info->dev, "Port %d: Unable to get port mac address\n",294 mlxsw_m_port->local_port);295 goto err_dev_addr_get;296 }297 298 netif_carrier_off(dev);299 mlxsw_m->ports[local_port] = mlxsw_m_port;300 err = register_netdev(dev);301 if (err) {302 dev_err(mlxsw_m->bus_info->dev, "Port %d: Failed to register netdev\n",303 mlxsw_m_port->local_port);304 goto err_register_netdev;305 }306 307 return 0;308 309err_register_netdev:310 mlxsw_m->ports[local_port] = NULL;311err_dev_addr_get:312 free_netdev(dev);313err_alloc_etherdev:314 mlxsw_core_port_fini(mlxsw_m->core, local_port);315 return err;316}317 318static void mlxsw_m_port_remove(struct mlxsw_m *mlxsw_m, u16 local_port)319{320 struct mlxsw_m_port *mlxsw_m_port = mlxsw_m->ports[local_port];321 322 unregister_netdev(mlxsw_m_port->dev); /* This calls ndo_stop */323 mlxsw_m->ports[local_port] = NULL;324 free_netdev(mlxsw_m_port->dev);325 mlxsw_core_port_fini(mlxsw_m->core, local_port);326}327 328static int*329mlxsw_m_port_mapping_get(struct mlxsw_m *mlxsw_m, u8 slot_index, u8 module)330{331 return &mlxsw_m->line_cards[slot_index]->module_to_port[module];332}333 334static int mlxsw_m_port_module_map(struct mlxsw_m *mlxsw_m, u16 local_port,335 u8 *last_module)336{337 unsigned int max_ports = mlxsw_core_max_ports(mlxsw_m->core);338 u8 module, width, slot_index;339 int *module_to_port;340 int err;341 342 /* Fill out to local port mapping array */343 err = mlxsw_m_port_module_info_get(mlxsw_m, local_port, &module,344 &width, &slot_index);345 if (err)346 return err;347 348 /* Skip if line card has been already configured */349 if (mlxsw_m->line_cards[slot_index]->active)350 return 0;351 if (!width)352 return 0;353 /* Skip, if port belongs to the cluster */354 if (module == *last_module)355 return 0;356 *last_module = module;357 358 if (WARN_ON_ONCE(module >= max_ports))359 return -EINVAL;360 mlxsw_env_module_port_map(mlxsw_m->core, slot_index, module);361 module_to_port = mlxsw_m_port_mapping_get(mlxsw_m, slot_index, module);362 *module_to_port = local_port;363 364 return 0;365}366 367static void368mlxsw_m_port_module_unmap(struct mlxsw_m *mlxsw_m, u8 slot_index, u8 module)369{370 int *module_to_port = mlxsw_m_port_mapping_get(mlxsw_m, slot_index,371 module);372 *module_to_port = -1;373 mlxsw_env_module_port_unmap(mlxsw_m->core, slot_index, module);374}375 376static int mlxsw_m_linecards_init(struct mlxsw_m *mlxsw_m)377{378 unsigned int max_ports = mlxsw_core_max_ports(mlxsw_m->core);379 char mgpir_pl[MLXSW_REG_MGPIR_LEN];380 u8 num_of_modules;381 int i, j, err;382 383 mlxsw_reg_mgpir_pack(mgpir_pl, 0);384 err = mlxsw_reg_query(mlxsw_m->core, MLXSW_REG(mgpir), mgpir_pl);385 if (err)386 return err;387 388 mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL, &num_of_modules,389 &mlxsw_m->num_of_slots);390 /* If the system is modular, get the maximum number of modules per-slot.391 * Otherwise, get the maximum number of modules on the main board.392 */393 if (mlxsw_m->num_of_slots)394 mlxsw_m->max_modules_per_slot =395 mlxsw_reg_mgpir_max_modules_per_slot_get(mgpir_pl);396 else397 mlxsw_m->max_modules_per_slot = num_of_modules;398 /* Add slot for main board. */399 mlxsw_m->num_of_slots += 1;400 401 mlxsw_m->ports = kcalloc(max_ports, sizeof(*mlxsw_m->ports),402 GFP_KERNEL);403 if (!mlxsw_m->ports)404 return -ENOMEM;405 406 mlxsw_m->line_cards = kcalloc(mlxsw_m->num_of_slots,407 sizeof(*mlxsw_m->line_cards),408 GFP_KERNEL);409 if (!mlxsw_m->line_cards) {410 err = -ENOMEM;411 goto err_kcalloc;412 }413 414 for (i = 0; i < mlxsw_m->num_of_slots; i++) {415 mlxsw_m->line_cards[i] =416 kzalloc(struct_size(mlxsw_m->line_cards[i],417 module_to_port,418 mlxsw_m->max_modules_per_slot),419 GFP_KERNEL);420 if (!mlxsw_m->line_cards[i]) {421 err = -ENOMEM;422 goto err_kmalloc_array;423 }424 425 /* Invalidate the entries of module to local port mapping array. */426 for (j = 0; j < mlxsw_m->max_modules_per_slot; j++)427 mlxsw_m->line_cards[i]->module_to_port[j] = -1;428 }429 430 return 0;431 432err_kmalloc_array:433 for (i--; i >= 0; i--)434 kfree(mlxsw_m->line_cards[i]);435 kfree(mlxsw_m->line_cards);436err_kcalloc:437 kfree(mlxsw_m->ports);438 return err;439}440 441static void mlxsw_m_linecards_fini(struct mlxsw_m *mlxsw_m)442{443 int i = mlxsw_m->num_of_slots;444 445 for (i--; i >= 0; i--)446 kfree(mlxsw_m->line_cards[i]);447 kfree(mlxsw_m->line_cards);448 kfree(mlxsw_m->ports);449}450 451static void452mlxsw_m_linecard_port_module_unmap(struct mlxsw_m *mlxsw_m, u8 slot_index)453{454 int i;455 456 for (i = mlxsw_m->max_modules_per_slot - 1; i >= 0; i--) {457 int *module_to_port;458 459 module_to_port = mlxsw_m_port_mapping_get(mlxsw_m, slot_index, i);460 if (*module_to_port > 0)461 mlxsw_m_port_module_unmap(mlxsw_m, slot_index, i);462 }463}464 465static int466mlxsw_m_linecard_ports_create(struct mlxsw_m *mlxsw_m, u8 slot_index)467{468 int *module_to_port;469 int i, err;470 471 for (i = 0; i < mlxsw_m->max_modules_per_slot; i++) {472 module_to_port = mlxsw_m_port_mapping_get(mlxsw_m, slot_index, i);473 if (*module_to_port > 0) {474 err = mlxsw_m_port_create(mlxsw_m, *module_to_port,475 slot_index, i);476 if (err)477 goto err_port_create;478 /* Mark slot as active */479 if (!mlxsw_m->line_cards[slot_index]->active)480 mlxsw_m->line_cards[slot_index]->active = true;481 }482 }483 return 0;484 485err_port_create:486 for (i--; i >= 0; i--) {487 module_to_port = mlxsw_m_port_mapping_get(mlxsw_m, slot_index, i);488 if (*module_to_port > 0 &&489 mlxsw_m_port_created(mlxsw_m, *module_to_port)) {490 mlxsw_m_port_remove(mlxsw_m, *module_to_port);491 /* Mark slot as inactive */492 if (mlxsw_m->line_cards[slot_index]->active)493 mlxsw_m->line_cards[slot_index]->active = false;494 }495 }496 return err;497}498 499static void500mlxsw_m_linecard_ports_remove(struct mlxsw_m *mlxsw_m, u8 slot_index)501{502 int i;503 504 for (i = 0; i < mlxsw_m->max_modules_per_slot; i++) {505 int *module_to_port = mlxsw_m_port_mapping_get(mlxsw_m,506 slot_index, i);507 508 if (*module_to_port > 0 &&509 mlxsw_m_port_created(mlxsw_m, *module_to_port)) {510 mlxsw_m_port_remove(mlxsw_m, *module_to_port);511 mlxsw_m_port_module_unmap(mlxsw_m, slot_index, i);512 }513 }514}515 516static int mlxsw_m_ports_module_map(struct mlxsw_m *mlxsw_m)517{518 unsigned int max_ports = mlxsw_core_max_ports(mlxsw_m->core);519 u8 last_module = max_ports;520 int i, err;521 522 for (i = 1; i < max_ports; i++) {523 err = mlxsw_m_port_module_map(mlxsw_m, i, &last_module);524 if (err)525 return err;526 }527 528 return 0;529}530 531static int mlxsw_m_ports_create(struct mlxsw_m *mlxsw_m)532{533 int err;534 535 /* Fill out module to local port mapping array */536 err = mlxsw_m_ports_module_map(mlxsw_m);537 if (err)538 goto err_ports_module_map;539 540 /* Create port objects for each valid entry */541 err = mlxsw_m_linecard_ports_create(mlxsw_m, 0);542 if (err)543 goto err_linecard_ports_create;544 545 return 0;546 547err_linecard_ports_create:548err_ports_module_map:549 mlxsw_m_linecard_port_module_unmap(mlxsw_m, 0);550 551 return err;552}553 554static void mlxsw_m_ports_remove(struct mlxsw_m *mlxsw_m)555{556 mlxsw_m_linecard_ports_remove(mlxsw_m, 0);557}558 559static void560mlxsw_m_ports_remove_selected(struct mlxsw_core *mlxsw_core,561 bool (*selector)(void *priv, u16 local_port),562 void *priv)563{564 struct mlxsw_m *mlxsw_m = mlxsw_core_driver_priv(mlxsw_core);565 struct mlxsw_linecard *linecard_priv = priv;566 struct mlxsw_m_line_card *linecard;567 568 linecard = mlxsw_m->line_cards[linecard_priv->slot_index];569 570 if (WARN_ON(!linecard->active))571 return;572 573 mlxsw_m_linecard_ports_remove(mlxsw_m, linecard_priv->slot_index);574 linecard->active = false;575}576 577static int mlxsw_m_fw_rev_validate(struct mlxsw_m *mlxsw_m)578{579 const struct mlxsw_fw_rev *rev = &mlxsw_m->bus_info->fw_rev;580 581 /* Validate driver and FW are compatible.582 * Do not check major version, since it defines chip type, while583 * driver is supposed to support any type.584 */585 if (mlxsw_core_fw_rev_minor_subminor_validate(rev, &mlxsw_m_fw_rev))586 return 0;587 588 dev_err(mlxsw_m->bus_info->dev, "The firmware version %d.%d.%d is incompatible with the driver (required >= %d.%d.%d)\n",589 rev->major, rev->minor, rev->subminor, rev->major,590 mlxsw_m_fw_rev.minor, mlxsw_m_fw_rev.subminor);591 592 return -EINVAL;593}594 595static void596mlxsw_m_got_active(struct mlxsw_core *mlxsw_core, u8 slot_index, void *priv)597{598 struct mlxsw_m_line_card *linecard;599 struct mlxsw_m *mlxsw_m = priv;600 int err;601 602 linecard = mlxsw_m->line_cards[slot_index];603 /* Skip if line card has been already configured during init */604 if (linecard->active)605 return;606 607 /* Fill out module to local port mapping array */608 err = mlxsw_m_ports_module_map(mlxsw_m);609 if (err)610 goto err_ports_module_map;611 612 /* Create port objects for each valid entry */613 err = mlxsw_m_linecard_ports_create(mlxsw_m, slot_index);614 if (err) {615 dev_err(mlxsw_m->bus_info->dev, "Failed to create port for line card at slot %d\n",616 slot_index);617 goto err_linecard_ports_create;618 }619 620 linecard->active = true;621 622 return;623 624err_linecard_ports_create:625err_ports_module_map:626 mlxsw_m_linecard_port_module_unmap(mlxsw_m, slot_index);627}628 629static void630mlxsw_m_got_inactive(struct mlxsw_core *mlxsw_core, u8 slot_index, void *priv)631{632 struct mlxsw_m_line_card *linecard;633 struct mlxsw_m *mlxsw_m = priv;634 635 linecard = mlxsw_m->line_cards[slot_index];636 637 if (WARN_ON(!linecard->active))638 return;639 640 mlxsw_m_linecard_ports_remove(mlxsw_m, slot_index);641 linecard->active = false;642}643 644static struct mlxsw_linecards_event_ops mlxsw_m_event_ops = {645 .got_active = mlxsw_m_got_active,646 .got_inactive = mlxsw_m_got_inactive,647};648 649static int mlxsw_m_init(struct mlxsw_core *mlxsw_core,650 const struct mlxsw_bus_info *mlxsw_bus_info,651 struct netlink_ext_ack *extack)652{653 struct mlxsw_m *mlxsw_m = mlxsw_core_driver_priv(mlxsw_core);654 int err;655 656 mlxsw_m->core = mlxsw_core;657 mlxsw_m->bus_info = mlxsw_bus_info;658 659 err = mlxsw_m_fw_rev_validate(mlxsw_m);660 if (err)661 return err;662 663 err = mlxsw_m_base_mac_get(mlxsw_m);664 if (err) {665 dev_err(mlxsw_m->bus_info->dev, "Failed to get base mac\n");666 return err;667 }668 669 err = mlxsw_m_linecards_init(mlxsw_m);670 if (err) {671 dev_err(mlxsw_m->bus_info->dev, "Failed to create line cards\n");672 return err;673 }674 675 err = mlxsw_linecards_event_ops_register(mlxsw_core,676 &mlxsw_m_event_ops, mlxsw_m);677 if (err) {678 dev_err(mlxsw_m->bus_info->dev, "Failed to register line cards operations\n");679 goto linecards_event_ops_register;680 }681 682 err = mlxsw_m_ports_create(mlxsw_m);683 if (err) {684 dev_err(mlxsw_m->bus_info->dev, "Failed to create ports\n");685 goto err_ports_create;686 }687 688 return 0;689 690err_ports_create:691 mlxsw_linecards_event_ops_unregister(mlxsw_core,692 &mlxsw_m_event_ops, mlxsw_m);693linecards_event_ops_register:694 mlxsw_m_linecards_fini(mlxsw_m);695 return err;696}697 698static void mlxsw_m_fini(struct mlxsw_core *mlxsw_core)699{700 struct mlxsw_m *mlxsw_m = mlxsw_core_driver_priv(mlxsw_core);701 702 mlxsw_m_ports_remove(mlxsw_m);703 mlxsw_linecards_event_ops_unregister(mlxsw_core,704 &mlxsw_m_event_ops, mlxsw_m);705 mlxsw_m_linecards_fini(mlxsw_m);706}707 708static const struct mlxsw_config_profile mlxsw_m_config_profile;709 710static struct mlxsw_driver mlxsw_m_driver = {711 .kind = mlxsw_m_driver_name,712 .priv_size = sizeof(struct mlxsw_m),713 .init = mlxsw_m_init,714 .fini = mlxsw_m_fini,715 .ports_remove_selected = mlxsw_m_ports_remove_selected,716 .profile = &mlxsw_m_config_profile,717};718 719static const struct i2c_device_id mlxsw_m_i2c_id[] = {720 { "mlxsw_minimal" },721 { }722};723 724static struct i2c_driver mlxsw_m_i2c_driver = {725 .driver.name = "mlxsw_minimal",726 .id_table = mlxsw_m_i2c_id,727};728 729static int __init mlxsw_m_module_init(void)730{731 int err;732 733 err = mlxsw_core_driver_register(&mlxsw_m_driver);734 if (err)735 return err;736 737 err = mlxsw_i2c_driver_register(&mlxsw_m_i2c_driver);738 if (err)739 goto err_i2c_driver_register;740 741 return 0;742 743err_i2c_driver_register:744 mlxsw_core_driver_unregister(&mlxsw_m_driver);745 746 return err;747}748 749static void __exit mlxsw_m_module_exit(void)750{751 mlxsw_i2c_driver_unregister(&mlxsw_m_i2c_driver);752 mlxsw_core_driver_unregister(&mlxsw_m_driver);753}754 755module_init(mlxsw_m_module_init);756module_exit(mlxsw_m_module_exit);757 758MODULE_LICENSE("Dual BSD/GPL");759MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");760MODULE_DESCRIPTION("Mellanox minimal driver");761MODULE_DEVICE_TABLE(i2c, mlxsw_m_i2c_id);762