Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'mlxsw-introduce-modular-system-support-by-minimal-driver'

Petr Machata says:

====================
mlxsw: Introduce modular system support by minimal driver

Vadim Pasternak writes:

This patchset adds line cards support in mlxsw_minimal, which is used
for monitoring purposes on BMC systems. The BMC is connected to the
ASIC over I2C bus, unlike the host CPU that is connected to the ASIC
via PCI bus.

The BMC system needs to be notified whenever line cards become active
or inactive, so that, for example, netdevs will be registered /
unregistered by mlxsw_minimal. However, traps cannot be generated
towards the BMC over the I2C bus. To overcome that, the I2C bus driver
(i.e., mlxsw_i2c) registers an handler for an IRQ that is fired upon
specific system wide changes, like line card activation and
deactivation.

The generated event is handled by mlxsw_core, which checks whether
anything changed in the state of available line cards. If a line card
becomes active or inactive, interested parties such as mlxsw_minimal
are notified via their registered line card event callback.

Patch set overview:

Patches #1 is preparations.

Patches #2-#3 extend mlxsw_core with an infrastructure to handle the
previously mentioned system events.

Patch #4 extends the I2C bus driver to register an handler for the IRQ
fired upon specific system wide changes.

Patches #5-#8 gradually add line cards support in mlxsw_minimal by
dynamically registering / unregistering netdevs for ports found on
line cards, whenever a line card becomes active / inactive.
====================

Link: https://lore.kernel.org/r/cover.1661093502.git.petrm@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+554 -94
+68
drivers/net/ethernet/mellanox/mlxsw/core.c
··· 70 70 struct workqueue_struct *emad_wq; 71 71 struct list_head rx_listener_list; 72 72 struct list_head event_listener_list; 73 + struct list_head irq_event_handler_list; 74 + struct mutex irq_event_handler_lock; /* Locks access to handlers list */ 73 75 struct { 74 76 atomic64_t tid; 75 77 struct list_head trans_list; ··· 2092 2090 devlink_health_reporter_destroy(mlxsw_core->health.fw_fatal); 2093 2091 } 2094 2092 2093 + static void mlxsw_core_irq_event_handler_init(struct mlxsw_core *mlxsw_core) 2094 + { 2095 + INIT_LIST_HEAD(&mlxsw_core->irq_event_handler_list); 2096 + mutex_init(&mlxsw_core->irq_event_handler_lock); 2097 + } 2098 + 2099 + static void mlxsw_core_irq_event_handler_fini(struct mlxsw_core *mlxsw_core) 2100 + { 2101 + mutex_destroy(&mlxsw_core->irq_event_handler_lock); 2102 + WARN_ON(!list_empty(&mlxsw_core->irq_event_handler_list)); 2103 + } 2104 + 2095 2105 static int 2096 2106 __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, 2097 2107 const struct mlxsw_bus *mlxsw_bus, ··· 2139 2125 mlxsw_core->bus = mlxsw_bus; 2140 2126 mlxsw_core->bus_priv = bus_priv; 2141 2127 mlxsw_core->bus_info = mlxsw_bus_info; 2128 + mlxsw_core_irq_event_handler_init(mlxsw_core); 2142 2129 2143 2130 err = mlxsw_bus->init(bus_priv, mlxsw_core, mlxsw_driver->profile, 2144 2131 &mlxsw_core->res); ··· 2248 2233 err_register_resources: 2249 2234 mlxsw_bus->fini(bus_priv); 2250 2235 err_bus_init: 2236 + mlxsw_core_irq_event_handler_fini(mlxsw_core); 2251 2237 if (!reload) { 2252 2238 devl_unlock(devlink); 2253 2239 devlink_free(devlink); ··· 2318 2302 if (!reload) 2319 2303 devl_resources_unregister(devlink); 2320 2304 mlxsw_core->bus->fini(mlxsw_core->bus_priv); 2305 + mlxsw_core_irq_event_handler_fini(mlxsw_core); 2321 2306 if (!reload) { 2322 2307 devl_unlock(devlink); 2323 2308 devlink_free(devlink); ··· 2788 2771 return sum_err; 2789 2772 } 2790 2773 EXPORT_SYMBOL(mlxsw_reg_trans_bulk_wait); 2774 + 2775 + struct mlxsw_core_irq_event_handler_item { 2776 + struct list_head list; 2777 + void (*cb)(struct mlxsw_core *mlxsw_core); 2778 + }; 2779 + 2780 + int mlxsw_core_irq_event_handler_register(struct mlxsw_core *mlxsw_core, 2781 + mlxsw_irq_event_cb_t cb) 2782 + { 2783 + struct mlxsw_core_irq_event_handler_item *item; 2784 + 2785 + item = kzalloc(sizeof(*item), GFP_KERNEL); 2786 + if (!item) 2787 + return -ENOMEM; 2788 + item->cb = cb; 2789 + mutex_lock(&mlxsw_core->irq_event_handler_lock); 2790 + list_add_tail(&item->list, &mlxsw_core->irq_event_handler_list); 2791 + mutex_unlock(&mlxsw_core->irq_event_handler_lock); 2792 + return 0; 2793 + } 2794 + EXPORT_SYMBOL(mlxsw_core_irq_event_handler_register); 2795 + 2796 + void mlxsw_core_irq_event_handler_unregister(struct mlxsw_core *mlxsw_core, 2797 + mlxsw_irq_event_cb_t cb) 2798 + { 2799 + struct mlxsw_core_irq_event_handler_item *item, *tmp; 2800 + 2801 + mutex_lock(&mlxsw_core->irq_event_handler_lock); 2802 + list_for_each_entry_safe(item, tmp, 2803 + &mlxsw_core->irq_event_handler_list, list) { 2804 + if (item->cb == cb) { 2805 + list_del(&item->list); 2806 + kfree(item); 2807 + } 2808 + } 2809 + mutex_unlock(&mlxsw_core->irq_event_handler_lock); 2810 + } 2811 + EXPORT_SYMBOL(mlxsw_core_irq_event_handler_unregister); 2812 + 2813 + void mlxsw_core_irq_event_handlers_call(struct mlxsw_core *mlxsw_core) 2814 + { 2815 + struct mlxsw_core_irq_event_handler_item *item; 2816 + 2817 + mutex_lock(&mlxsw_core->irq_event_handler_lock); 2818 + list_for_each_entry(item, &mlxsw_core->irq_event_handler_list, list) { 2819 + if (item->cb) 2820 + item->cb(mlxsw_core); 2821 + } 2822 + mutex_unlock(&mlxsw_core->irq_event_handler_lock); 2823 + } 2824 + EXPORT_SYMBOL(mlxsw_core_irq_event_handlers_call); 2791 2825 2792 2826 static int mlxsw_core_reg_access_cmd(struct mlxsw_core *mlxsw_core, 2793 2827 const struct mlxsw_reg_info *reg,
+8
drivers/net/ethernet/mellanox/mlxsw/core.h
··· 215 215 mlxsw_reg_trans_cb_t *cb, unsigned long cb_priv); 216 216 int mlxsw_reg_trans_bulk_wait(struct list_head *bulk_list); 217 217 218 + typedef void mlxsw_irq_event_cb_t(struct mlxsw_core *mlxsw_core); 219 + 220 + int mlxsw_core_irq_event_handler_register(struct mlxsw_core *mlxsw_core, 221 + mlxsw_irq_event_cb_t cb); 222 + void mlxsw_core_irq_event_handler_unregister(struct mlxsw_core *mlxsw_core, 223 + mlxsw_irq_event_cb_t cb); 224 + void mlxsw_core_irq_event_handlers_call(struct mlxsw_core *mlxsw_core); 225 + 218 226 int mlxsw_reg_query(struct mlxsw_core *mlxsw_core, 219 227 const struct mlxsw_reg_info *reg, char *payload); 220 228 int mlxsw_reg_write(struct mlxsw_core *mlxsw_core,
+75 -21
drivers/net/ethernet/mellanox/mlxsw/core_linecards.c
··· 785 785 return mlxsw_linecard_status_process(linecards, linecard, mddq_pl); 786 786 } 787 787 788 + static void mlxsw_linecards_irq_event_handler(struct mlxsw_core *mlxsw_core) 789 + { 790 + struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core); 791 + int i; 792 + 793 + /* Handle change of line card active state. */ 794 + for (i = 0; i < linecards->count; i++) { 795 + struct mlxsw_linecard *linecard = mlxsw_linecard_get(linecards, 796 + i + 1); 797 + 798 + mlxsw_linecard_status_get_and_process(mlxsw_core, linecards, 799 + linecard); 800 + } 801 + } 802 + 788 803 static const char * const mlxsw_linecard_status_event_type_name[] = { 789 804 [MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION] = "provision", 790 805 [MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION] = "unprovision", ··· 1253 1238 { 1254 1239 struct devlink_linecard *devlink_linecard; 1255 1240 struct mlxsw_linecard *linecard; 1256 - int err; 1257 1241 1258 1242 linecard = mlxsw_linecard_get(linecards, slot_index); 1259 1243 linecard->slot_index = slot_index; ··· 1262 1248 devlink_linecard = devlink_linecard_create(priv_to_devlink(mlxsw_core), 1263 1249 slot_index, &mlxsw_linecard_ops, 1264 1250 linecard); 1265 - if (IS_ERR(devlink_linecard)) { 1266 - err = PTR_ERR(devlink_linecard); 1267 - goto err_devlink_linecard_create; 1268 - } 1251 + if (IS_ERR(devlink_linecard)) 1252 + return PTR_ERR(devlink_linecard); 1253 + 1269 1254 linecard->devlink_linecard = devlink_linecard; 1270 1255 INIT_DELAYED_WORK(&linecard->status_event_to_dw, 1271 1256 &mlxsw_linecard_status_event_to_work); 1272 1257 1258 + return 0; 1259 + } 1260 + 1261 + static void mlxsw_linecard_fini(struct mlxsw_core *mlxsw_core, 1262 + struct mlxsw_linecards *linecards, 1263 + u8 slot_index) 1264 + { 1265 + struct mlxsw_linecard *linecard; 1266 + 1267 + linecard = mlxsw_linecard_get(linecards, slot_index); 1268 + cancel_delayed_work_sync(&linecard->status_event_to_dw); 1269 + /* Make sure all scheduled events are processed */ 1270 + mlxsw_core_flush_owq(); 1271 + if (linecard->active) 1272 + mlxsw_linecard_active_clear(linecard); 1273 + mlxsw_linecard_bdev_del(linecard); 1274 + devlink_linecard_destroy(linecard->devlink_linecard); 1275 + mutex_destroy(&linecard->lock); 1276 + } 1277 + 1278 + static int 1279 + mlxsw_linecard_event_delivery_init(struct mlxsw_core *mlxsw_core, 1280 + struct mlxsw_linecards *linecards, 1281 + u8 slot_index) 1282 + { 1283 + struct mlxsw_linecard *linecard; 1284 + int err; 1285 + 1286 + linecard = mlxsw_linecard_get(linecards, slot_index); 1273 1287 err = mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, true); 1274 1288 if (err) 1275 - goto err_event_delivery_set; 1289 + return err; 1276 1290 1277 1291 err = mlxsw_linecard_status_get_and_process(mlxsw_core, linecards, 1278 1292 linecard); ··· 1311 1269 1312 1270 err_status_get_and_process: 1313 1271 mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false); 1314 - err_event_delivery_set: 1315 - devlink_linecard_destroy(linecard->devlink_linecard); 1316 - err_devlink_linecard_create: 1317 - mutex_destroy(&linecard->lock); 1318 1272 return err; 1319 1273 } 1320 1274 1321 - static void mlxsw_linecard_fini(struct mlxsw_core *mlxsw_core, 1322 - struct mlxsw_linecards *linecards, 1323 - u8 slot_index) 1275 + static void 1276 + mlxsw_linecard_event_delivery_fini(struct mlxsw_core *mlxsw_core, 1277 + struct mlxsw_linecards *linecards, 1278 + u8 slot_index) 1324 1279 { 1325 1280 struct mlxsw_linecard *linecard; 1326 1281 1327 1282 linecard = mlxsw_linecard_get(linecards, slot_index); 1328 1283 mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false); 1329 - cancel_delayed_work_sync(&linecard->status_event_to_dw); 1330 - /* Make sure all scheduled events are processed */ 1331 - mlxsw_core_flush_owq(); 1332 - if (linecard->active) 1333 - mlxsw_linecard_active_clear(linecard); 1334 - mlxsw_linecard_bdev_del(linecard); 1335 - devlink_linecard_destroy(linecard->devlink_linecard); 1336 - mutex_destroy(&linecard->lock); 1337 1284 } 1338 1285 1339 1286 /* LINECARDS INI BUNDLE FILE ··· 1536 1505 if (err) 1537 1506 goto err_traps_register; 1538 1507 1508 + err = mlxsw_core_irq_event_handler_register(mlxsw_core, 1509 + mlxsw_linecards_irq_event_handler); 1510 + if (err) 1511 + goto err_irq_event_handler_register; 1512 + 1539 1513 mlxsw_core_linecards_set(mlxsw_core, linecards); 1540 1514 1541 1515 for (i = 0; i < linecards->count; i++) { ··· 1549 1513 goto err_linecard_init; 1550 1514 } 1551 1515 1516 + for (i = 0; i < linecards->count; i++) { 1517 + err = mlxsw_linecard_event_delivery_init(mlxsw_core, linecards, 1518 + i + 1); 1519 + if (err) 1520 + goto err_linecard_event_delivery_init; 1521 + } 1522 + 1552 1523 return 0; 1553 1524 1525 + err_linecard_event_delivery_init: 1526 + for (i--; i >= 0; i--) 1527 + mlxsw_linecard_event_delivery_fini(mlxsw_core, linecards, i + 1); 1528 + i = linecards->count; 1554 1529 err_linecard_init: 1555 1530 for (i--; i >= 0; i--) 1556 1531 mlxsw_linecard_fini(mlxsw_core, linecards, i + 1); 1532 + mlxsw_core_irq_event_handler_unregister(mlxsw_core, 1533 + mlxsw_linecards_irq_event_handler); 1534 + err_irq_event_handler_register: 1557 1535 mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener, 1558 1536 ARRAY_SIZE(mlxsw_linecard_listener), 1559 1537 mlxsw_core); ··· 1586 1536 if (!linecards) 1587 1537 return; 1588 1538 for (i = 0; i < linecards->count; i++) 1539 + mlxsw_linecard_event_delivery_fini(mlxsw_core, linecards, i + 1); 1540 + for (i = 0; i < linecards->count; i++) 1589 1541 mlxsw_linecard_fini(mlxsw_core, linecards, i + 1); 1542 + mlxsw_core_irq_event_handler_unregister(mlxsw_core, 1543 + mlxsw_linecards_irq_event_handler); 1590 1544 mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener, 1591 1545 ARRAY_SIZE(mlxsw_linecard_listener), 1592 1546 mlxsw_core);
+86 -1
drivers/net/ethernet/mellanox/mlxsw/i2c.c
··· 9 9 #include <linux/mutex.h> 10 10 #include <linux/module.h> 11 11 #include <linux/mod_devicetable.h> 12 + #include <linux/platform_data/mlxreg.h> 12 13 #include <linux/slab.h> 13 14 14 15 #include "cmd.h" ··· 52 51 #define MLXSW_I2C_TIMEOUT_MSECS 5000 53 52 #define MLXSW_I2C_MAX_DATA_SIZE 256 54 53 54 + /* Driver can be initialized by kernel platform driver or from the user 55 + * space. In the first case IRQ line number is passed through the platform 56 + * data, otherwise default IRQ line is to be used. Default IRQ is relevant 57 + * only for specific I2C slave address, allowing 3.4 MHz I2C path to the chip 58 + * (special hardware feature for I2C acceleration). 59 + */ 60 + #define MLXSW_I2C_DEFAULT_IRQ 17 61 + #define MLXSW_FAST_I2C_SLAVE 0x37 62 + 55 63 /** 56 64 * struct mlxsw_i2c - device private data: 57 65 * @cmd: command attributes; ··· 73 63 * @core: switch core pointer; 74 64 * @bus_info: bus info block; 75 65 * @block_size: maximum block size allowed to pass to under layer; 66 + * @pdata: device platform data; 67 + * @irq_work: interrupts work item; 68 + * @irq: IRQ line number; 76 69 */ 77 70 struct mlxsw_i2c { 78 71 struct { ··· 89 76 struct mlxsw_core *core; 90 77 struct mlxsw_bus_info bus_info; 91 78 u16 block_size; 79 + struct mlxreg_core_hotplug_platform_data *pdata; 80 + struct work_struct irq_work; 81 + int irq; 92 82 }; 93 83 94 84 #define MLXSW_I2C_READ_MSG(_client, _addr_buf, _buf, _len) { \ ··· 562 546 mlxsw_i2c->core = NULL; 563 547 } 564 548 549 + static void mlxsw_i2c_work_handler(struct work_struct *work) 550 + { 551 + struct mlxsw_i2c *mlxsw_i2c; 552 + 553 + mlxsw_i2c = container_of(work, struct mlxsw_i2c, irq_work); 554 + mlxsw_core_irq_event_handlers_call(mlxsw_i2c->core); 555 + } 556 + 557 + static irqreturn_t mlxsw_i2c_irq_handler(int irq, void *dev) 558 + { 559 + struct mlxsw_i2c *mlxsw_i2c = dev; 560 + 561 + mlxsw_core_schedule_work(&mlxsw_i2c->irq_work); 562 + 563 + /* Interrupt handler shares IRQ line with 'main' interrupt handler. 564 + * Return here IRQ_NONE, while main handler will return IRQ_HANDLED. 565 + */ 566 + return IRQ_NONE; 567 + } 568 + 569 + static int mlxsw_i2c_irq_init(struct mlxsw_i2c *mlxsw_i2c, u8 addr) 570 + { 571 + int err; 572 + 573 + /* Initialize interrupt handler if system hotplug driver is reachable, 574 + * otherwise interrupt line is not enabled and interrupts will not be 575 + * raised to CPU. Also request_irq() call will be not valid. 576 + */ 577 + if (!IS_REACHABLE(CONFIG_MLXREG_HOTPLUG)) 578 + return 0; 579 + 580 + /* Set default interrupt line. */ 581 + if (mlxsw_i2c->pdata && mlxsw_i2c->pdata->irq) 582 + mlxsw_i2c->irq = mlxsw_i2c->pdata->irq; 583 + else if (addr == MLXSW_FAST_I2C_SLAVE) 584 + mlxsw_i2c->irq = MLXSW_I2C_DEFAULT_IRQ; 585 + 586 + if (!mlxsw_i2c->irq) 587 + return 0; 588 + 589 + INIT_WORK(&mlxsw_i2c->irq_work, mlxsw_i2c_work_handler); 590 + err = request_irq(mlxsw_i2c->irq, mlxsw_i2c_irq_handler, 591 + IRQF_TRIGGER_FALLING | IRQF_SHARED, "mlxsw-i2c", 592 + mlxsw_i2c); 593 + if (err) { 594 + dev_err(mlxsw_i2c->bus_info.dev, "Failed to request irq: %d\n", 595 + err); 596 + return err; 597 + } 598 + 599 + return 0; 600 + } 601 + 602 + static void mlxsw_i2c_irq_fini(struct mlxsw_i2c *mlxsw_i2c) 603 + { 604 + if (!IS_REACHABLE(CONFIG_MLXREG_HOTPLUG) || !mlxsw_i2c->irq) 605 + return; 606 + cancel_work_sync(&mlxsw_i2c->irq_work); 607 + free_irq(mlxsw_i2c->irq, mlxsw_i2c); 608 + } 609 + 565 610 static const struct mlxsw_bus mlxsw_i2c_bus = { 566 611 .kind = "i2c", 567 612 .init = mlxsw_i2c_init, ··· 715 638 mlxsw_i2c->bus_info.dev = &client->dev; 716 639 mlxsw_i2c->bus_info.low_frequency = true; 717 640 mlxsw_i2c->dev = &client->dev; 641 + mlxsw_i2c->pdata = client->dev.platform_data; 642 + 643 + err = mlxsw_i2c_irq_init(mlxsw_i2c, client->addr); 644 + if (err) 645 + goto errout; 718 646 719 647 err = mlxsw_core_bus_device_register(&mlxsw_i2c->bus_info, 720 648 &mlxsw_i2c_bus, mlxsw_i2c, false, 721 649 NULL, NULL); 722 650 if (err) { 723 651 dev_err(&client->dev, "Fail to register core bus\n"); 724 - return err; 652 + goto err_bus_device_register; 725 653 } 726 654 727 655 return 0; 728 656 657 + err_bus_device_register: 658 + mlxsw_i2c_irq_fini(mlxsw_i2c); 729 659 errout: 730 660 mutex_destroy(&mlxsw_i2c->cmd.lock); 731 661 i2c_set_clientdata(client, NULL); ··· 745 661 struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client); 746 662 747 663 mlxsw_core_bus_device_unregister(mlxsw_i2c->core, false); 664 + mlxsw_i2c_irq_fini(mlxsw_i2c); 748 665 mutex_destroy(&mlxsw_i2c->cmd.lock); 749 666 750 667 return 0;
+317 -72
drivers/net/ethernet/mellanox/mlxsw/minimal.c
··· 26 26 27 27 struct mlxsw_m_port; 28 28 29 + struct mlxsw_m_line_card { 30 + bool active; 31 + int module_to_port[]; 32 + }; 33 + 29 34 struct mlxsw_m { 30 35 struct mlxsw_m_port **ports; 31 - int *module_to_port; 32 36 struct mlxsw_core *core; 33 37 const struct mlxsw_bus_info *bus_info; 34 38 u8 base_mac[ETH_ALEN]; 35 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; 36 43 }; 37 44 38 45 struct mlxsw_m_port { 39 46 struct net_device *dev; 40 47 struct mlxsw_m *mlxsw_m; 41 48 u16 local_port; 49 + u8 slot_index; 42 50 u8 module; 51 + u8 module_offset; 43 52 }; 44 53 45 54 static int mlxsw_m_base_mac_get(struct mlxsw_m *mlxsw_m) ··· 120 111 struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev); 121 112 struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; 122 113 123 - return mlxsw_env_get_module_info(netdev, core, 0, mlxsw_m_port->module, 124 - modinfo); 114 + return mlxsw_env_get_module_info(netdev, core, 115 + mlxsw_m_port->slot_index, 116 + mlxsw_m_port->module, modinfo); 125 117 } 126 118 127 119 static int ··· 132 122 struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev); 133 123 struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; 134 124 135 - return mlxsw_env_get_module_eeprom(netdev, core, 0, 125 + return mlxsw_env_get_module_eeprom(netdev, core, 126 + mlxsw_m_port->slot_index, 136 127 mlxsw_m_port->module, ee, data); 137 128 } 138 129 ··· 145 134 struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev); 146 135 struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; 147 136 148 - return mlxsw_env_get_module_eeprom_by_page(core, 0, 137 + return mlxsw_env_get_module_eeprom_by_page(core, 138 + mlxsw_m_port->slot_index, 149 139 mlxsw_m_port->module, 150 140 page, extack); 151 141 } ··· 156 144 struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev); 157 145 struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; 158 146 159 - return mlxsw_env_reset_module(netdev, core, 0, mlxsw_m_port->module, 147 + return mlxsw_env_reset_module(netdev, core, mlxsw_m_port->slot_index, 148 + mlxsw_m_port->module, 160 149 flags); 161 150 } 162 151 ··· 169 156 struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev); 170 157 struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; 171 158 172 - return mlxsw_env_get_module_power_mode(core, 0, mlxsw_m_port->module, 159 + return mlxsw_env_get_module_power_mode(core, mlxsw_m_port->slot_index, 160 + mlxsw_m_port->module, 173 161 params, extack); 174 162 } 175 163 ··· 182 168 struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev); 183 169 struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; 184 170 185 - return mlxsw_env_set_module_power_mode(core, 0, mlxsw_m_port->module, 171 + return mlxsw_env_set_module_power_mode(core, mlxsw_m_port->slot_index, 172 + mlxsw_m_port->module, 186 173 params->policy, extack); 187 174 } 188 175 ··· 199 184 200 185 static int 201 186 mlxsw_m_port_module_info_get(struct mlxsw_m *mlxsw_m, u16 local_port, 202 - u8 *p_module, u8 *p_width) 187 + u8 *p_module, u8 *p_width, u8 *p_slot_index) 203 188 { 204 189 char pmlp_pl[MLXSW_REG_PMLP_LEN]; 205 190 int err; ··· 210 195 return err; 211 196 *p_module = mlxsw_reg_pmlp_module_get(pmlp_pl, 0); 212 197 *p_width = mlxsw_reg_pmlp_width_get(pmlp_pl); 198 + *p_slot_index = mlxsw_reg_pmlp_slot_index_get(pmlp_pl, 0); 213 199 214 200 return 0; 215 201 } ··· 228 212 if (err) 229 213 return err; 230 214 mlxsw_reg_ppad_mac_memcpy_from(ppad_pl, addr); 231 - eth_hw_addr_gen(mlxsw_m_port->dev, addr, mlxsw_m_port->module + 1); 215 + eth_hw_addr_gen(mlxsw_m_port->dev, addr, mlxsw_m_port->module + 1 + 216 + mlxsw_m_port->module_offset); 232 217 return 0; 233 218 } 234 219 220 + static bool mlxsw_m_port_created(struct mlxsw_m *mlxsw_m, u16 local_port) 221 + { 222 + return mlxsw_m->ports[local_port]; 223 + } 224 + 235 225 static int 236 - mlxsw_m_port_create(struct mlxsw_m *mlxsw_m, u16 local_port, u8 module) 226 + mlxsw_m_port_create(struct mlxsw_m *mlxsw_m, u16 local_port, u8 slot_index, 227 + u8 module) 237 228 { 238 229 struct mlxsw_m_port *mlxsw_m_port; 239 230 struct net_device *dev; 240 231 int err; 241 232 242 - err = mlxsw_core_port_init(mlxsw_m->core, local_port, 0, 233 + err = mlxsw_core_port_init(mlxsw_m->core, local_port, slot_index, 243 234 module + 1, false, 0, false, 244 235 0, mlxsw_m->base_mac, 245 236 sizeof(mlxsw_m->base_mac)); ··· 269 246 mlxsw_m_port->mlxsw_m = mlxsw_m; 270 247 mlxsw_m_port->local_port = local_port; 271 248 mlxsw_m_port->module = module; 249 + mlxsw_m_port->slot_index = slot_index; 250 + /* Add module offset for line card. Offset for main board iz zero. 251 + * For line card in slot #n offset is calculated as (#n - 1) 252 + * multiplied by maximum modules number, which could be found on a line 253 + * card. 254 + */ 255 + mlxsw_m_port->module_offset = mlxsw_m_port->slot_index ? 256 + (mlxsw_m_port->slot_index - 1) * 257 + mlxsw_m->max_modules_per_slot : 0; 272 258 273 259 dev->netdev_ops = &mlxsw_m_port_netdev_ops; 274 260 dev->ethtool_ops = &mlxsw_m_port_ethtool_ops; ··· 323 291 mlxsw_core_port_fini(mlxsw_m->core, local_port); 324 292 } 325 293 294 + static int* 295 + mlxsw_m_port_mapping_get(struct mlxsw_m *mlxsw_m, u8 slot_index, u8 module) 296 + { 297 + return &mlxsw_m->line_cards[slot_index]->module_to_port[module]; 298 + } 299 + 326 300 static int mlxsw_m_port_module_map(struct mlxsw_m *mlxsw_m, u16 local_port, 327 301 u8 *last_module) 328 302 { 329 303 unsigned int max_ports = mlxsw_core_max_ports(mlxsw_m->core); 330 - u8 module, width; 304 + u8 module, width, slot_index; 305 + int *module_to_port; 331 306 int err; 332 307 333 308 /* Fill out to local port mapping array */ 334 309 err = mlxsw_m_port_module_info_get(mlxsw_m, local_port, &module, 335 - &width); 310 + &width, &slot_index); 336 311 if (err) 337 312 return err; 338 313 314 + /* Skip if line card has been already configured */ 315 + if (mlxsw_m->line_cards[slot_index]->active) 316 + return 0; 339 317 if (!width) 340 318 return 0; 341 319 /* Skip, if port belongs to the cluster */ ··· 355 313 356 314 if (WARN_ON_ONCE(module >= max_ports)) 357 315 return -EINVAL; 358 - mlxsw_env_module_port_map(mlxsw_m->core, 0, module); 359 - mlxsw_m->module_to_port[module] = ++mlxsw_m->max_ports; 316 + mlxsw_env_module_port_map(mlxsw_m->core, slot_index, module); 317 + module_to_port = mlxsw_m_port_mapping_get(mlxsw_m, slot_index, module); 318 + *module_to_port = local_port; 360 319 361 320 return 0; 362 321 } 363 322 364 - static void mlxsw_m_port_module_unmap(struct mlxsw_m *mlxsw_m, u8 module) 323 + static void 324 + mlxsw_m_port_module_unmap(struct mlxsw_m *mlxsw_m, u8 slot_index, u8 module) 365 325 { 366 - mlxsw_m->module_to_port[module] = -1; 367 - mlxsw_env_module_port_unmap(mlxsw_m->core, 0, module); 326 + int *module_to_port = mlxsw_m_port_mapping_get(mlxsw_m, slot_index, 327 + module); 328 + *module_to_port = -1; 329 + mlxsw_env_module_port_unmap(mlxsw_m->core, slot_index, module); 368 330 } 369 331 370 - static int mlxsw_m_ports_create(struct mlxsw_m *mlxsw_m) 332 + static int mlxsw_m_linecards_init(struct mlxsw_m *mlxsw_m) 371 333 { 372 334 unsigned int max_ports = mlxsw_core_max_ports(mlxsw_m->core); 373 - u8 last_module = max_ports; 374 - int i; 375 - int err; 335 + char mgpir_pl[MLXSW_REG_MGPIR_LEN]; 336 + u8 num_of_modules; 337 + int i, j, err; 338 + 339 + mlxsw_reg_mgpir_pack(mgpir_pl, 0); 340 + err = mlxsw_reg_query(mlxsw_m->core, MLXSW_REG(mgpir), mgpir_pl); 341 + if (err) 342 + return err; 343 + 344 + mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL, &num_of_modules, 345 + &mlxsw_m->num_of_slots); 346 + /* If the system is modular, get the maximum number of modules per-slot. 347 + * Otherwise, get the maximum number of modules on the main board. 348 + */ 349 + if (mlxsw_m->num_of_slots) 350 + mlxsw_m->max_modules_per_slot = 351 + mlxsw_reg_mgpir_max_modules_per_slot_get(mgpir_pl); 352 + else 353 + mlxsw_m->max_modules_per_slot = num_of_modules; 354 + /* Add slot for main board. */ 355 + mlxsw_m->num_of_slots += 1; 376 356 377 357 mlxsw_m->ports = kcalloc(max_ports, sizeof(*mlxsw_m->ports), 378 358 GFP_KERNEL); 379 359 if (!mlxsw_m->ports) 380 360 return -ENOMEM; 381 361 382 - mlxsw_m->module_to_port = kmalloc_array(max_ports, sizeof(int), 383 - GFP_KERNEL); 384 - if (!mlxsw_m->module_to_port) { 385 - err = -ENOMEM; 386 - goto err_module_to_port_alloc; 387 - } 362 + mlxsw_m->line_cards = kcalloc(mlxsw_m->num_of_slots, 363 + sizeof(*mlxsw_m->line_cards), 364 + GFP_KERNEL); 365 + if (!mlxsw_m->line_cards) 366 + goto err_kcalloc; 388 367 389 - /* Invalidate the entries of module to local port mapping array */ 390 - for (i = 0; i < max_ports; i++) 391 - mlxsw_m->module_to_port[i] = -1; 368 + for (i = 0; i < mlxsw_m->num_of_slots; i++) { 369 + mlxsw_m->line_cards[i] = 370 + kzalloc(struct_size(mlxsw_m->line_cards[i], 371 + module_to_port, 372 + mlxsw_m->max_modules_per_slot), 373 + GFP_KERNEL); 374 + if (!mlxsw_m->line_cards[i]) 375 + goto err_kmalloc_array; 392 376 393 - /* Fill out module to local port mapping array */ 394 - for (i = 1; i < max_ports; i++) { 395 - err = mlxsw_m_port_module_map(mlxsw_m, i, &last_module); 396 - if (err) 397 - goto err_module_to_port_map; 398 - } 399 - 400 - /* Create port objects for each valid entry */ 401 - for (i = 0; i < mlxsw_m->max_ports; i++) { 402 - if (mlxsw_m->module_to_port[i] > 0) { 403 - err = mlxsw_m_port_create(mlxsw_m, 404 - mlxsw_m->module_to_port[i], 405 - i); 406 - if (err) 407 - goto err_module_to_port_create; 408 - } 377 + /* Invalidate the entries of module to local port mapping array. */ 378 + for (j = 0; j < mlxsw_m->max_modules_per_slot; j++) 379 + mlxsw_m->line_cards[i]->module_to_port[j] = -1; 409 380 } 410 381 411 382 return 0; 412 383 413 - err_module_to_port_create: 414 - for (i--; i >= 0; i--) { 415 - if (mlxsw_m->module_to_port[i] > 0) 416 - mlxsw_m_port_remove(mlxsw_m, 417 - mlxsw_m->module_to_port[i]); 418 - } 419 - i = max_ports; 420 - err_module_to_port_map: 421 - for (i--; i > 0; i--) 422 - mlxsw_m_port_module_unmap(mlxsw_m, i); 423 - kfree(mlxsw_m->module_to_port); 424 - err_module_to_port_alloc: 384 + err_kmalloc_array: 385 + for (i--; i >= 0; i--) 386 + kfree(mlxsw_m->line_cards[i]); 387 + err_kcalloc: 425 388 kfree(mlxsw_m->ports); 389 + return err; 390 + } 391 + 392 + static void mlxsw_m_linecards_fini(struct mlxsw_m *mlxsw_m) 393 + { 394 + int i = mlxsw_m->num_of_slots; 395 + 396 + for (i--; i >= 0; i--) 397 + kfree(mlxsw_m->line_cards[i]); 398 + kfree(mlxsw_m->line_cards); 399 + kfree(mlxsw_m->ports); 400 + } 401 + 402 + static void 403 + mlxsw_m_linecard_port_module_unmap(struct mlxsw_m *mlxsw_m, u8 slot_index) 404 + { 405 + int i; 406 + 407 + for (i = mlxsw_m->max_modules_per_slot - 1; i >= 0; i--) { 408 + int *module_to_port; 409 + 410 + module_to_port = mlxsw_m_port_mapping_get(mlxsw_m, slot_index, i); 411 + if (*module_to_port > 0) 412 + mlxsw_m_port_module_unmap(mlxsw_m, slot_index, i); 413 + } 414 + } 415 + 416 + static int 417 + mlxsw_m_linecard_ports_create(struct mlxsw_m *mlxsw_m, u8 slot_index) 418 + { 419 + int *module_to_port; 420 + int i, err; 421 + 422 + for (i = 0; i < mlxsw_m->max_modules_per_slot; i++) { 423 + module_to_port = mlxsw_m_port_mapping_get(mlxsw_m, slot_index, i); 424 + if (*module_to_port > 0) { 425 + err = mlxsw_m_port_create(mlxsw_m, *module_to_port, 426 + slot_index, i); 427 + if (err) 428 + goto err_port_create; 429 + /* Mark slot as active */ 430 + if (!mlxsw_m->line_cards[slot_index]->active) 431 + mlxsw_m->line_cards[slot_index]->active = true; 432 + } 433 + } 434 + return 0; 435 + 436 + err_port_create: 437 + for (i--; i >= 0; i--) { 438 + module_to_port = mlxsw_m_port_mapping_get(mlxsw_m, slot_index, i); 439 + if (*module_to_port > 0 && 440 + mlxsw_m_port_created(mlxsw_m, *module_to_port)) { 441 + mlxsw_m_port_remove(mlxsw_m, *module_to_port); 442 + /* Mark slot as inactive */ 443 + if (mlxsw_m->line_cards[slot_index]->active) 444 + mlxsw_m->line_cards[slot_index]->active = false; 445 + } 446 + } 447 + return err; 448 + } 449 + 450 + static void 451 + mlxsw_m_linecard_ports_remove(struct mlxsw_m *mlxsw_m, u8 slot_index) 452 + { 453 + int i; 454 + 455 + for (i = 0; i < mlxsw_m->max_modules_per_slot; i++) { 456 + int *module_to_port = mlxsw_m_port_mapping_get(mlxsw_m, 457 + slot_index, i); 458 + 459 + if (*module_to_port > 0 && 460 + mlxsw_m_port_created(mlxsw_m, *module_to_port)) { 461 + mlxsw_m_port_remove(mlxsw_m, *module_to_port); 462 + mlxsw_m_port_module_unmap(mlxsw_m, slot_index, i); 463 + } 464 + } 465 + } 466 + 467 + static int mlxsw_m_ports_module_map(struct mlxsw_m *mlxsw_m) 468 + { 469 + unsigned int max_ports = mlxsw_core_max_ports(mlxsw_m->core); 470 + u8 last_module = max_ports; 471 + int i, err; 472 + 473 + for (i = 1; i < max_ports; i++) { 474 + err = mlxsw_m_port_module_map(mlxsw_m, i, &last_module); 475 + if (err) 476 + return err; 477 + } 478 + 479 + return 0; 480 + } 481 + 482 + static int mlxsw_m_ports_create(struct mlxsw_m *mlxsw_m) 483 + { 484 + int err; 485 + 486 + /* Fill out module to local port mapping array */ 487 + err = mlxsw_m_ports_module_map(mlxsw_m); 488 + if (err) 489 + goto err_ports_module_map; 490 + 491 + /* Create port objects for each valid entry */ 492 + err = mlxsw_m_linecard_ports_create(mlxsw_m, 0); 493 + if (err) 494 + goto err_linecard_ports_create; 495 + 496 + return 0; 497 + 498 + err_linecard_ports_create: 499 + err_ports_module_map: 500 + mlxsw_m_linecard_port_module_unmap(mlxsw_m, 0); 501 + 426 502 return err; 427 503 } 428 504 429 505 static void mlxsw_m_ports_remove(struct mlxsw_m *mlxsw_m) 430 506 { 431 - int i; 507 + mlxsw_m_linecard_ports_remove(mlxsw_m, 0); 508 + } 432 509 433 - for (i = 0; i < mlxsw_m->max_ports; i++) { 434 - if (mlxsw_m->module_to_port[i] > 0) { 435 - mlxsw_m_port_remove(mlxsw_m, 436 - mlxsw_m->module_to_port[i]); 437 - mlxsw_m_port_module_unmap(mlxsw_m, i); 438 - } 439 - } 510 + static void 511 + mlxsw_m_ports_remove_selected(struct mlxsw_core *mlxsw_core, 512 + bool (*selector)(void *priv, u16 local_port), 513 + void *priv) 514 + { 515 + struct mlxsw_m *mlxsw_m = mlxsw_core_driver_priv(mlxsw_core); 516 + struct mlxsw_linecard *linecard_priv = priv; 517 + struct mlxsw_m_line_card *linecard; 440 518 441 - kfree(mlxsw_m->module_to_port); 442 - kfree(mlxsw_m->ports); 519 + linecard = mlxsw_m->line_cards[linecard_priv->slot_index]; 520 + 521 + if (WARN_ON(!linecard->active)) 522 + return; 523 + 524 + mlxsw_m_linecard_ports_remove(mlxsw_m, linecard_priv->slot_index); 525 + linecard->active = false; 443 526 } 444 527 445 528 static int mlxsw_m_fw_rev_validate(struct mlxsw_m *mlxsw_m) ··· 584 417 585 418 return -EINVAL; 586 419 } 420 + 421 + static void 422 + mlxsw_m_got_active(struct mlxsw_core *mlxsw_core, u8 slot_index, void *priv) 423 + { 424 + struct mlxsw_m_line_card *linecard; 425 + struct mlxsw_m *mlxsw_m = priv; 426 + int err; 427 + 428 + linecard = mlxsw_m->line_cards[slot_index]; 429 + /* Skip if line card has been already configured during init */ 430 + if (linecard->active) 431 + return; 432 + 433 + /* Fill out module to local port mapping array */ 434 + err = mlxsw_m_ports_module_map(mlxsw_m); 435 + if (err) 436 + goto err_ports_module_map; 437 + 438 + /* Create port objects for each valid entry */ 439 + err = mlxsw_m_linecard_ports_create(mlxsw_m, slot_index); 440 + if (err) { 441 + dev_err(mlxsw_m->bus_info->dev, "Failed to create port for line card at slot %d\n", 442 + slot_index); 443 + goto err_linecard_ports_create; 444 + } 445 + 446 + linecard->active = true; 447 + 448 + return; 449 + 450 + err_linecard_ports_create: 451 + err_ports_module_map: 452 + mlxsw_m_linecard_port_module_unmap(mlxsw_m, slot_index); 453 + } 454 + 455 + static void 456 + mlxsw_m_got_inactive(struct mlxsw_core *mlxsw_core, u8 slot_index, void *priv) 457 + { 458 + struct mlxsw_m_line_card *linecard; 459 + struct mlxsw_m *mlxsw_m = priv; 460 + 461 + linecard = mlxsw_m->line_cards[slot_index]; 462 + 463 + if (WARN_ON(!linecard->active)) 464 + return; 465 + 466 + mlxsw_m_linecard_ports_remove(mlxsw_m, slot_index); 467 + linecard->active = false; 468 + } 469 + 470 + static struct mlxsw_linecards_event_ops mlxsw_m_event_ops = { 471 + .got_active = mlxsw_m_got_active, 472 + .got_inactive = mlxsw_m_got_inactive, 473 + }; 587 474 588 475 static int mlxsw_m_init(struct mlxsw_core *mlxsw_core, 589 476 const struct mlxsw_bus_info *mlxsw_bus_info, ··· 659 438 return err; 660 439 } 661 440 662 - err = mlxsw_m_ports_create(mlxsw_m); 441 + err = mlxsw_m_linecards_init(mlxsw_m); 663 442 if (err) { 664 - dev_err(mlxsw_m->bus_info->dev, "Failed to create ports\n"); 443 + dev_err(mlxsw_m->bus_info->dev, "Failed to create line cards\n"); 665 444 return err; 666 445 } 667 446 447 + err = mlxsw_linecards_event_ops_register(mlxsw_core, 448 + &mlxsw_m_event_ops, mlxsw_m); 449 + if (err) { 450 + dev_err(mlxsw_m->bus_info->dev, "Failed to register line cards operations\n"); 451 + goto linecards_event_ops_register; 452 + } 453 + 454 + err = mlxsw_m_ports_create(mlxsw_m); 455 + if (err) { 456 + dev_err(mlxsw_m->bus_info->dev, "Failed to create ports\n"); 457 + goto err_ports_create; 458 + } 459 + 668 460 return 0; 461 + 462 + err_ports_create: 463 + mlxsw_linecards_event_ops_unregister(mlxsw_core, 464 + &mlxsw_m_event_ops, mlxsw_m); 465 + linecards_event_ops_register: 466 + mlxsw_m_linecards_fini(mlxsw_m); 467 + return err; 669 468 } 670 469 671 470 static void mlxsw_m_fini(struct mlxsw_core *mlxsw_core) ··· 693 452 struct mlxsw_m *mlxsw_m = mlxsw_core_driver_priv(mlxsw_core); 694 453 695 454 mlxsw_m_ports_remove(mlxsw_m); 455 + mlxsw_linecards_event_ops_unregister(mlxsw_core, 456 + &mlxsw_m_event_ops, mlxsw_m); 457 + mlxsw_m_linecards_fini(mlxsw_m); 696 458 } 697 459 698 460 static const struct mlxsw_config_profile mlxsw_m_config_profile; ··· 705 461 .priv_size = sizeof(struct mlxsw_m), 706 462 .init = mlxsw_m_init, 707 463 .fini = mlxsw_m_fini, 464 + .ports_remove_selected = mlxsw_m_ports_remove_selected, 708 465 .profile = &mlxsw_m_config_profile, 709 466 }; 710 467