core_linecards.c (32814B)
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 2/* Copyright (c) 2022 NVIDIA Corporation and Mellanox Technologies. All rights reserved */ 3 4#include <linux/kernel.h> 5#include <linux/module.h> 6#include <linux/err.h> 7#include <linux/types.h> 8#include <linux/string.h> 9#include <linux/workqueue.h> 10#include <linux/gfp.h> 11#include <linux/slab.h> 12#include <linux/list.h> 13#include <linux/vmalloc.h> 14 15#include "core.h" 16 17struct mlxsw_linecard_ini_file { 18 __le16 size; 19 union { 20 u8 data[0]; 21 struct { 22 __be16 hw_revision; 23 __be16 ini_version; 24 u8 __dontcare[3]; 25 u8 type; 26 u8 name[20]; 27 } format; 28 }; 29}; 30 31struct mlxsw_linecard_types_info { 32 struct mlxsw_linecard_ini_file **ini_files; 33 unsigned int count; 34 size_t data_size; 35 char *data; 36}; 37 38#define MLXSW_LINECARD_STATUS_EVENT_TO (10 * MSEC_PER_SEC) 39 40static void 41mlxsw_linecard_status_event_to_schedule(struct mlxsw_linecard *linecard, 42 enum mlxsw_linecard_status_event_type status_event_type) 43{ 44 cancel_delayed_work_sync(&linecard->status_event_to_dw); 45 linecard->status_event_type_to = status_event_type; 46 mlxsw_core_schedule_dw(&linecard->status_event_to_dw, 47 msecs_to_jiffies(MLXSW_LINECARD_STATUS_EVENT_TO)); 48} 49 50static void 51mlxsw_linecard_status_event_done(struct mlxsw_linecard *linecard, 52 enum mlxsw_linecard_status_event_type status_event_type) 53{ 54 if (linecard->status_event_type_to == status_event_type) 55 cancel_delayed_work_sync(&linecard->status_event_to_dw); 56} 57 58static const char * 59mlxsw_linecard_types_lookup(struct mlxsw_linecards *linecards, u8 card_type) 60{ 61 struct mlxsw_linecard_types_info *types_info; 62 struct mlxsw_linecard_ini_file *ini_file; 63 int i; 64 65 types_info = linecards->types_info; 66 if (!types_info) 67 return NULL; 68 for (i = 0; i < types_info->count; i++) { 69 ini_file = linecards->types_info->ini_files[i]; 70 if (ini_file->format.type == card_type) 71 return ini_file->format.name; 72 } 73 return NULL; 74} 75 76static const char *mlxsw_linecard_type_name(struct mlxsw_linecard *linecard) 77{ 78 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core; 79 char mddq_pl[MLXSW_REG_MDDQ_LEN]; 80 int err; 81 82 mlxsw_reg_mddq_slot_name_pack(mddq_pl, linecard->slot_index); 83 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl); 84 if (err) 85 return ERR_PTR(err); 86 mlxsw_reg_mddq_slot_name_unpack(mddq_pl, linecard->name); 87 return linecard->name; 88} 89 90static void mlxsw_linecard_provision_fail(struct mlxsw_linecard *linecard) 91{ 92 linecard->provisioned = false; 93 linecard->ready = false; 94 linecard->active = false; 95 devlink_linecard_provision_fail(linecard->devlink_linecard); 96} 97 98struct mlxsw_linecards_event_ops_item { 99 struct list_head list; 100 const struct mlxsw_linecards_event_ops *event_ops; 101 void *priv; 102}; 103 104static void 105mlxsw_linecard_event_op_call(struct mlxsw_linecard *linecard, 106 mlxsw_linecards_event_op_t *op, void *priv) 107{ 108 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core; 109 110 if (!op) 111 return; 112 op(mlxsw_core, linecard->slot_index, priv); 113} 114 115static void 116mlxsw_linecard_active_ops_call(struct mlxsw_linecard *linecard) 117{ 118 struct mlxsw_linecards *linecards = linecard->linecards; 119 struct mlxsw_linecards_event_ops_item *item; 120 121 mutex_lock(&linecards->event_ops_list_lock); 122 list_for_each_entry(item, &linecards->event_ops_list, list) 123 mlxsw_linecard_event_op_call(linecard, 124 item->event_ops->got_active, 125 item->priv); 126 mutex_unlock(&linecards->event_ops_list_lock); 127} 128 129static void 130mlxsw_linecard_inactive_ops_call(struct mlxsw_linecard *linecard) 131{ 132 struct mlxsw_linecards *linecards = linecard->linecards; 133 struct mlxsw_linecards_event_ops_item *item; 134 135 mutex_lock(&linecards->event_ops_list_lock); 136 list_for_each_entry(item, &linecards->event_ops_list, list) 137 mlxsw_linecard_event_op_call(linecard, 138 item->event_ops->got_inactive, 139 item->priv); 140 mutex_unlock(&linecards->event_ops_list_lock); 141} 142 143static void 144mlxsw_linecards_event_ops_register_call(struct mlxsw_linecards *linecards, 145 const struct mlxsw_linecards_event_ops_item *item) 146{ 147 struct mlxsw_linecard *linecard; 148 int i; 149 150 for (i = 0; i < linecards->count; i++) { 151 linecard = mlxsw_linecard_get(linecards, i + 1); 152 mutex_lock(&linecard->lock); 153 if (linecard->active) 154 mlxsw_linecard_event_op_call(linecard, 155 item->event_ops->got_active, 156 item->priv); 157 mutex_unlock(&linecard->lock); 158 } 159} 160 161static void 162mlxsw_linecards_event_ops_unregister_call(struct mlxsw_linecards *linecards, 163 const struct mlxsw_linecards_event_ops_item *item) 164{ 165 struct mlxsw_linecard *linecard; 166 int i; 167 168 for (i = 0; i < linecards->count; i++) { 169 linecard = mlxsw_linecard_get(linecards, i + 1); 170 mutex_lock(&linecard->lock); 171 if (linecard->active) 172 mlxsw_linecard_event_op_call(linecard, 173 item->event_ops->got_inactive, 174 item->priv); 175 mutex_unlock(&linecard->lock); 176 } 177} 178 179int mlxsw_linecards_event_ops_register(struct mlxsw_core *mlxsw_core, 180 struct mlxsw_linecards_event_ops *ops, 181 void *priv) 182{ 183 struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core); 184 struct mlxsw_linecards_event_ops_item *item; 185 186 if (!linecards) 187 return 0; 188 item = kzalloc(sizeof(*item), GFP_KERNEL); 189 if (!item) 190 return -ENOMEM; 191 item->event_ops = ops; 192 item->priv = priv; 193 194 mutex_lock(&linecards->event_ops_list_lock); 195 list_add_tail(&item->list, &linecards->event_ops_list); 196 mutex_unlock(&linecards->event_ops_list_lock); 197 mlxsw_linecards_event_ops_register_call(linecards, item); 198 return 0; 199} 200EXPORT_SYMBOL(mlxsw_linecards_event_ops_register); 201 202void mlxsw_linecards_event_ops_unregister(struct mlxsw_core *mlxsw_core, 203 struct mlxsw_linecards_event_ops *ops, 204 void *priv) 205{ 206 struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core); 207 struct mlxsw_linecards_event_ops_item *item, *tmp; 208 bool found = false; 209 210 if (!linecards) 211 return; 212 mutex_lock(&linecards->event_ops_list_lock); 213 list_for_each_entry_safe(item, tmp, &linecards->event_ops_list, list) { 214 if (item->event_ops == ops && item->priv == priv) { 215 list_del(&item->list); 216 found = true; 217 break; 218 } 219 } 220 mutex_unlock(&linecards->event_ops_list_lock); 221 222 if (!found) 223 return; 224 mlxsw_linecards_event_ops_unregister_call(linecards, item); 225 kfree(item); 226} 227EXPORT_SYMBOL(mlxsw_linecards_event_ops_unregister); 228 229static int 230mlxsw_linecard_provision_set(struct mlxsw_linecard *linecard, u8 card_type, 231 u16 hw_revision, u16 ini_version) 232{ 233 struct mlxsw_linecards *linecards = linecard->linecards; 234 const char *type; 235 236 type = mlxsw_linecard_types_lookup(linecards, card_type); 237 mlxsw_linecard_status_event_done(linecard, 238 MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION); 239 if (!type) { 240 /* It is possible for a line card to be provisioned before 241 * driver initialization. Due to a missing INI bundle file 242 * or an outdated one, the queried card's type might not 243 * be recognized by the driver. In this case, try to query 244 * the card's name from the device. 245 */ 246 type = mlxsw_linecard_type_name(linecard); 247 if (IS_ERR(type)) { 248 mlxsw_linecard_provision_fail(linecard); 249 return PTR_ERR(type); 250 } 251 } 252 linecard->provisioned = true; 253 linecard->hw_revision = hw_revision; 254 linecard->ini_version = ini_version; 255 devlink_linecard_provision_set(linecard->devlink_linecard, type); 256 return 0; 257} 258 259static void mlxsw_linecard_provision_clear(struct mlxsw_linecard *linecard) 260{ 261 mlxsw_linecard_status_event_done(linecard, 262 MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION); 263 linecard->provisioned = false; 264 devlink_linecard_provision_clear(linecard->devlink_linecard); 265} 266 267static int mlxsw_linecard_ready_set(struct mlxsw_linecard *linecard) 268{ 269 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core; 270 char mddc_pl[MLXSW_REG_MDDC_LEN]; 271 int err; 272 273 mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, false, true); 274 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl); 275 if (err) 276 return err; 277 linecard->ready = true; 278 return 0; 279} 280 281static int mlxsw_linecard_ready_clear(struct mlxsw_linecard *linecard) 282{ 283 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core; 284 char mddc_pl[MLXSW_REG_MDDC_LEN]; 285 int err; 286 287 mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, false, false); 288 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl); 289 if (err) 290 return err; 291 linecard->ready = false; 292 return 0; 293} 294 295static void mlxsw_linecard_active_set(struct mlxsw_linecard *linecard) 296{ 297 mlxsw_linecard_active_ops_call(linecard); 298 linecard->active = true; 299 devlink_linecard_activate(linecard->devlink_linecard); 300} 301 302static void mlxsw_linecard_active_clear(struct mlxsw_linecard *linecard) 303{ 304 mlxsw_linecard_inactive_ops_call(linecard); 305 linecard->active = false; 306 devlink_linecard_deactivate(linecard->devlink_linecard); 307} 308 309static int mlxsw_linecard_status_process(struct mlxsw_linecards *linecards, 310 struct mlxsw_linecard *linecard, 311 const char *mddq_pl) 312{ 313 enum mlxsw_reg_mddq_slot_info_ready ready; 314 bool provisioned, sr_valid, active; 315 u16 ini_version, hw_revision; 316 u8 slot_index, card_type; 317 int err = 0; 318 319 mlxsw_reg_mddq_slot_info_unpack(mddq_pl, &slot_index, &provisioned, 320 &sr_valid, &ready, &active, 321 &hw_revision, &ini_version, 322 &card_type); 323 324 if (linecard) { 325 if (WARN_ON(slot_index != linecard->slot_index)) 326 return -EINVAL; 327 } else { 328 if (WARN_ON(slot_index > linecards->count)) 329 return -EINVAL; 330 linecard = mlxsw_linecard_get(linecards, slot_index); 331 } 332 333 mutex_lock(&linecard->lock); 334 335 if (provisioned && linecard->provisioned != provisioned) { 336 err = mlxsw_linecard_provision_set(linecard, card_type, 337 hw_revision, ini_version); 338 if (err) 339 goto out; 340 } 341 342 if (ready == MLXSW_REG_MDDQ_SLOT_INFO_READY_READY && !linecard->ready) { 343 err = mlxsw_linecard_ready_set(linecard); 344 if (err) 345 goto out; 346 } 347 348 if (active && linecard->active != active) 349 mlxsw_linecard_active_set(linecard); 350 351 if (!active && linecard->active != active) 352 mlxsw_linecard_active_clear(linecard); 353 354 if (ready != MLXSW_REG_MDDQ_SLOT_INFO_READY_READY && 355 linecard->ready) { 356 err = mlxsw_linecard_ready_clear(linecard); 357 if (err) 358 goto out; 359 } 360 361 if (!provisioned && linecard->provisioned != provisioned) 362 mlxsw_linecard_provision_clear(linecard); 363 364out: 365 mutex_unlock(&linecard->lock); 366 return err; 367} 368 369static int mlxsw_linecard_status_get_and_process(struct mlxsw_core *mlxsw_core, 370 struct mlxsw_linecards *linecards, 371 struct mlxsw_linecard *linecard) 372{ 373 char mddq_pl[MLXSW_REG_MDDQ_LEN]; 374 int err; 375 376 mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, false); 377 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl); 378 if (err) 379 return err; 380 381 return mlxsw_linecard_status_process(linecards, linecard, mddq_pl); 382} 383 384static const char * const mlxsw_linecard_status_event_type_name[] = { 385 [MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION] = "provision", 386 [MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION] = "unprovision", 387}; 388 389static void mlxsw_linecard_status_event_to_work(struct work_struct *work) 390{ 391 struct mlxsw_linecard *linecard = 392 container_of(work, struct mlxsw_linecard, 393 status_event_to_dw.work); 394 395 mutex_lock(&linecard->lock); 396 dev_err(linecard->linecards->bus_info->dev, "linecard %u: Timeout reached waiting on %s status event", 397 linecard->slot_index, 398 mlxsw_linecard_status_event_type_name[linecard->status_event_type_to]); 399 mlxsw_linecard_provision_fail(linecard); 400 mutex_unlock(&linecard->lock); 401} 402 403static int __mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard) 404{ 405 dev_info(linecard->linecards->bus_info->dev, "linecard %u: Clearing FSM state error", 406 linecard->slot_index); 407 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index, 408 MLXSW_REG_MBCT_OP_CLEAR_ERRORS, false); 409 return mlxsw_reg_write(linecard->linecards->mlxsw_core, 410 MLXSW_REG(mbct), linecard->mbct_pl); 411} 412 413static int mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard, 414 enum mlxsw_reg_mbct_fsm_state fsm_state) 415{ 416 if (fsm_state != MLXSW_REG_MBCT_FSM_STATE_ERROR) 417 return 0; 418 return __mlxsw_linecard_fix_fsm_state(linecard); 419} 420 421static int 422mlxsw_linecard_query_ini_status(struct mlxsw_linecard *linecard, 423 enum mlxsw_reg_mbct_status *status, 424 enum mlxsw_reg_mbct_fsm_state *fsm_state, 425 struct netlink_ext_ack *extack) 426{ 427 int err; 428 429 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index, 430 MLXSW_REG_MBCT_OP_QUERY_STATUS, false); 431 err = mlxsw_reg_query(linecard->linecards->mlxsw_core, MLXSW_REG(mbct), 432 linecard->mbct_pl); 433 if (err) { 434 NL_SET_ERR_MSG_MOD(extack, "Failed to query linecard INI status"); 435 return err; 436 } 437 mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, status, fsm_state); 438 return err; 439} 440 441static int 442mlxsw_linecard_ini_transfer(struct mlxsw_core *mlxsw_core, 443 struct mlxsw_linecard *linecard, 444 const struct mlxsw_linecard_ini_file *ini_file, 445 struct netlink_ext_ack *extack) 446{ 447 enum mlxsw_reg_mbct_fsm_state fsm_state; 448 enum mlxsw_reg_mbct_status status; 449 size_t size_left; 450 const u8 *data; 451 int err; 452 453 size_left = le16_to_cpu(ini_file->size); 454 data = ini_file->data; 455 while (size_left) { 456 size_t data_size = MLXSW_REG_MBCT_DATA_LEN; 457 bool is_last = false; 458 459 if (size_left <= MLXSW_REG_MBCT_DATA_LEN) { 460 data_size = size_left; 461 is_last = true; 462 } 463 464 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index, 465 MLXSW_REG_MBCT_OP_DATA_TRANSFER, false); 466 mlxsw_reg_mbct_dt_pack(linecard->mbct_pl, data_size, 467 is_last, data); 468 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct), 469 linecard->mbct_pl); 470 if (err) { 471 NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI data transfer"); 472 return err; 473 } 474 mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, 475 &status, &fsm_state); 476 if ((!is_last && status != MLXSW_REG_MBCT_STATUS_PART_DATA) || 477 (is_last && status != MLXSW_REG_MBCT_STATUS_LAST_DATA)) { 478 NL_SET_ERR_MSG_MOD(extack, "Failed to transfer linecard INI data"); 479 mlxsw_linecard_fix_fsm_state(linecard, fsm_state); 480 return -EINVAL; 481 } 482 size_left -= data_size; 483 data += data_size; 484 } 485 486 return 0; 487} 488 489static int 490mlxsw_linecard_ini_erase(struct mlxsw_core *mlxsw_core, 491 struct mlxsw_linecard *linecard, 492 struct netlink_ext_ack *extack) 493{ 494 enum mlxsw_reg_mbct_fsm_state fsm_state; 495 enum mlxsw_reg_mbct_status status; 496 int err; 497 498 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index, 499 MLXSW_REG_MBCT_OP_ERASE_INI_IMAGE, false); 500 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct), 501 linecard->mbct_pl); 502 if (err) { 503 NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI erase"); 504 return err; 505 } 506 mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state); 507 switch (status) { 508 case MLXSW_REG_MBCT_STATUS_ERASE_COMPLETE: 509 break; 510 default: 511 /* Should not happen */ 512 fallthrough; 513 case MLXSW_REG_MBCT_STATUS_ERASE_FAILED: 514 NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI"); 515 goto fix_fsm_err_out; 516 case MLXSW_REG_MBCT_STATUS_ERROR_INI_IN_USE: 517 NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI while being used"); 518 goto fix_fsm_err_out; 519 } 520 return 0; 521 522fix_fsm_err_out: 523 mlxsw_linecard_fix_fsm_state(linecard, fsm_state); 524 return -EINVAL; 525} 526 527static void mlxsw_linecard_bct_process(struct mlxsw_core *mlxsw_core, 528 const char *mbct_pl) 529{ 530 struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core); 531 enum mlxsw_reg_mbct_fsm_state fsm_state; 532 enum mlxsw_reg_mbct_status status; 533 struct mlxsw_linecard *linecard; 534 u8 slot_index; 535 536 mlxsw_reg_mbct_unpack(mbct_pl, &slot_index, &status, &fsm_state); 537 if (WARN_ON(slot_index > linecards->count)) 538 return; 539 linecard = mlxsw_linecard_get(linecards, slot_index); 540 mutex_lock(&linecard->lock); 541 if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) { 542 dev_err(linecards->bus_info->dev, "linecard %u: Failed to activate INI", 543 linecard->slot_index); 544 goto fix_fsm_out; 545 } 546 mutex_unlock(&linecard->lock); 547 return; 548 549fix_fsm_out: 550 mlxsw_linecard_fix_fsm_state(linecard, fsm_state); 551 mlxsw_linecard_provision_fail(linecard); 552 mutex_unlock(&linecard->lock); 553} 554 555static int 556mlxsw_linecard_ini_activate(struct mlxsw_core *mlxsw_core, 557 struct mlxsw_linecard *linecard, 558 struct netlink_ext_ack *extack) 559{ 560 enum mlxsw_reg_mbct_fsm_state fsm_state; 561 enum mlxsw_reg_mbct_status status; 562 int err; 563 564 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index, 565 MLXSW_REG_MBCT_OP_ACTIVATE, true); 566 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct), linecard->mbct_pl); 567 if (err) { 568 NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI activation"); 569 return err; 570 } 571 mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state); 572 if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) { 573 NL_SET_ERR_MSG_MOD(extack, "Failed to activate linecard INI"); 574 goto fix_fsm_err_out; 575 } 576 577 return 0; 578 579fix_fsm_err_out: 580 mlxsw_linecard_fix_fsm_state(linecard, fsm_state); 581 return -EINVAL; 582} 583 584#define MLXSW_LINECARD_INI_WAIT_RETRIES 10 585#define MLXSW_LINECARD_INI_WAIT_MS 500 586 587static int 588mlxsw_linecard_ini_in_use_wait(struct mlxsw_core *mlxsw_core, 589 struct mlxsw_linecard *linecard, 590 struct netlink_ext_ack *extack) 591{ 592 enum mlxsw_reg_mbct_fsm_state fsm_state; 593 enum mlxsw_reg_mbct_status status; 594 unsigned int ini_wait_retries = 0; 595 int err; 596 597query_ini_status: 598 err = mlxsw_linecard_query_ini_status(linecard, &status, 599 &fsm_state, extack); 600 if (err) 601 return err; 602 603 switch (fsm_state) { 604 case MLXSW_REG_MBCT_FSM_STATE_INI_IN_USE: 605 if (ini_wait_retries++ > MLXSW_LINECARD_INI_WAIT_RETRIES) { 606 NL_SET_ERR_MSG_MOD(extack, "Failed to wait for linecard INI to be unused"); 607 return -EINVAL; 608 } 609 mdelay(MLXSW_LINECARD_INI_WAIT_MS); 610 goto query_ini_status; 611 default: 612 break; 613 } 614 return 0; 615} 616 617static bool mlxsw_linecard_port_selector(void *priv, u16 local_port) 618{ 619 struct mlxsw_linecard *linecard = priv; 620 struct mlxsw_core *mlxsw_core; 621 622 mlxsw_core = linecard->linecards->mlxsw_core; 623 return linecard == mlxsw_core_port_linecard_get(mlxsw_core, local_port); 624} 625 626static int mlxsw_linecard_provision(struct devlink_linecard *devlink_linecard, 627 void *priv, const char *type, 628 const void *type_priv, 629 struct netlink_ext_ack *extack) 630{ 631 const struct mlxsw_linecard_ini_file *ini_file = type_priv; 632 struct mlxsw_linecard *linecard = priv; 633 struct mlxsw_core *mlxsw_core; 634 int err; 635 636 mutex_lock(&linecard->lock); 637 638 mlxsw_core = linecard->linecards->mlxsw_core; 639 640 err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack); 641 if (err) 642 goto err_out; 643 644 err = mlxsw_linecard_ini_transfer(mlxsw_core, linecard, 645 ini_file, extack); 646 if (err) 647 goto err_out; 648 649 mlxsw_linecard_status_event_to_schedule(linecard, 650 MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION); 651 err = mlxsw_linecard_ini_activate(mlxsw_core, linecard, extack); 652 if (err) 653 goto err_out; 654 655 goto out; 656 657err_out: 658 mlxsw_linecard_provision_fail(linecard); 659out: 660 mutex_unlock(&linecard->lock); 661 return err; 662} 663 664static int mlxsw_linecard_unprovision(struct devlink_linecard *devlink_linecard, 665 void *priv, 666 struct netlink_ext_ack *extack) 667{ 668 struct mlxsw_linecard *linecard = priv; 669 struct mlxsw_core *mlxsw_core; 670 int err; 671 672 mutex_lock(&linecard->lock); 673 674 mlxsw_core = linecard->linecards->mlxsw_core; 675 676 mlxsw_core_ports_remove_selected(mlxsw_core, 677 mlxsw_linecard_port_selector, 678 linecard); 679 680 err = mlxsw_linecard_ini_in_use_wait(mlxsw_core, linecard, extack); 681 if (err) 682 goto err_out; 683 684 mlxsw_linecard_status_event_to_schedule(linecard, 685 MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION); 686 err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack); 687 if (err) 688 goto err_out; 689 690 goto out; 691 692err_out: 693 mlxsw_linecard_provision_fail(linecard); 694out: 695 mutex_unlock(&linecard->lock); 696 return err; 697} 698 699static bool mlxsw_linecard_same_provision(struct devlink_linecard *devlink_linecard, 700 void *priv, const char *type, 701 const void *type_priv) 702{ 703 const struct mlxsw_linecard_ini_file *ini_file = type_priv; 704 struct mlxsw_linecard *linecard = priv; 705 bool ret; 706 707 mutex_lock(&linecard->lock); 708 ret = linecard->hw_revision == be16_to_cpu(ini_file->format.hw_revision) && 709 linecard->ini_version == be16_to_cpu(ini_file->format.ini_version); 710 mutex_unlock(&linecard->lock); 711 return ret; 712} 713 714static unsigned int 715mlxsw_linecard_types_count(struct devlink_linecard *devlink_linecard, 716 void *priv) 717{ 718 struct mlxsw_linecard *linecard = priv; 719 720 return linecard->linecards->types_info ? 721 linecard->linecards->types_info->count : 0; 722} 723 724static void mlxsw_linecard_types_get(struct devlink_linecard *devlink_linecard, 725 void *priv, unsigned int index, 726 const char **type, const void **type_priv) 727{ 728 struct mlxsw_linecard_types_info *types_info; 729 struct mlxsw_linecard_ini_file *ini_file; 730 struct mlxsw_linecard *linecard = priv; 731 732 types_info = linecard->linecards->types_info; 733 if (WARN_ON_ONCE(!types_info)) 734 return; 735 ini_file = types_info->ini_files[index]; 736 *type = ini_file->format.name; 737 *type_priv = ini_file; 738} 739 740static const struct devlink_linecard_ops mlxsw_linecard_ops = { 741 .provision = mlxsw_linecard_provision, 742 .unprovision = mlxsw_linecard_unprovision, 743 .same_provision = mlxsw_linecard_same_provision, 744 .types_count = mlxsw_linecard_types_count, 745 .types_get = mlxsw_linecard_types_get, 746}; 747 748struct mlxsw_linecard_status_event { 749 struct mlxsw_core *mlxsw_core; 750 char mddq_pl[MLXSW_REG_MDDQ_LEN]; 751 struct work_struct work; 752}; 753 754static void mlxsw_linecard_status_event_work(struct work_struct *work) 755{ 756 struct mlxsw_linecard_status_event *event; 757 struct mlxsw_linecards *linecards; 758 struct mlxsw_core *mlxsw_core; 759 760 event = container_of(work, struct mlxsw_linecard_status_event, work); 761 mlxsw_core = event->mlxsw_core; 762 linecards = mlxsw_core_linecards(mlxsw_core); 763 mlxsw_linecard_status_process(linecards, NULL, event->mddq_pl); 764 kfree(event); 765} 766 767static void 768mlxsw_linecard_status_listener_func(const struct mlxsw_reg_info *reg, 769 char *mddq_pl, void *priv) 770{ 771 struct mlxsw_linecard_status_event *event; 772 struct mlxsw_core *mlxsw_core = priv; 773 774 event = kmalloc(sizeof(*event), GFP_ATOMIC); 775 if (!event) 776 return; 777 event->mlxsw_core = mlxsw_core; 778 memcpy(event->mddq_pl, mddq_pl, sizeof(event->mddq_pl)); 779 INIT_WORK(&event->work, mlxsw_linecard_status_event_work); 780 mlxsw_core_schedule_work(&event->work); 781} 782 783struct mlxsw_linecard_bct_event { 784 struct mlxsw_core *mlxsw_core; 785 char mbct_pl[MLXSW_REG_MBCT_LEN]; 786 struct work_struct work; 787}; 788 789static void mlxsw_linecard_bct_event_work(struct work_struct *work) 790{ 791 struct mlxsw_linecard_bct_event *event; 792 struct mlxsw_core *mlxsw_core; 793 794 event = container_of(work, struct mlxsw_linecard_bct_event, work); 795 mlxsw_core = event->mlxsw_core; 796 mlxsw_linecard_bct_process(mlxsw_core, event->mbct_pl); 797 kfree(event); 798} 799 800static void 801mlxsw_linecard_bct_listener_func(const struct mlxsw_reg_info *reg, 802 char *mbct_pl, void *priv) 803{ 804 struct mlxsw_linecard_bct_event *event; 805 struct mlxsw_core *mlxsw_core = priv; 806 807 event = kmalloc(sizeof(*event), GFP_ATOMIC); 808 if (!event) 809 return; 810 event->mlxsw_core = mlxsw_core; 811 memcpy(event->mbct_pl, mbct_pl, sizeof(event->mbct_pl)); 812 INIT_WORK(&event->work, mlxsw_linecard_bct_event_work); 813 mlxsw_core_schedule_work(&event->work); 814} 815 816static const struct mlxsw_listener mlxsw_linecard_listener[] = { 817 MLXSW_CORE_EVENTL(mlxsw_linecard_status_listener_func, DSDSC), 818 MLXSW_CORE_EVENTL(mlxsw_linecard_bct_listener_func, BCTOE), 819}; 820 821static int mlxsw_linecard_event_delivery_set(struct mlxsw_core *mlxsw_core, 822 struct mlxsw_linecard *linecard, 823 bool enable) 824{ 825 char mddq_pl[MLXSW_REG_MDDQ_LEN]; 826 827 mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, enable); 828 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddq), mddq_pl); 829} 830 831static int mlxsw_linecard_init(struct mlxsw_core *mlxsw_core, 832 struct mlxsw_linecards *linecards, 833 u8 slot_index) 834{ 835 struct devlink_linecard *devlink_linecard; 836 struct mlxsw_linecard *linecard; 837 int err; 838 839 linecard = mlxsw_linecard_get(linecards, slot_index); 840 linecard->slot_index = slot_index; 841 linecard->linecards = linecards; 842 mutex_init(&linecard->lock); 843 844 devlink_linecard = devlink_linecard_create(priv_to_devlink(mlxsw_core), 845 slot_index, &mlxsw_linecard_ops, 846 linecard); 847 if (IS_ERR(devlink_linecard)) { 848 err = PTR_ERR(devlink_linecard); 849 goto err_devlink_linecard_create; 850 } 851 linecard->devlink_linecard = devlink_linecard; 852 INIT_DELAYED_WORK(&linecard->status_event_to_dw, 853 &mlxsw_linecard_status_event_to_work); 854 855 err = mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, true); 856 if (err) 857 goto err_event_delivery_set; 858 859 err = mlxsw_linecard_status_get_and_process(mlxsw_core, linecards, 860 linecard); 861 if (err) 862 goto err_status_get_and_process; 863 864 return 0; 865 866err_status_get_and_process: 867 mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false); 868err_event_delivery_set: 869 devlink_linecard_destroy(linecard->devlink_linecard); 870err_devlink_linecard_create: 871 mutex_destroy(&linecard->lock); 872 return err; 873} 874 875static void mlxsw_linecard_fini(struct mlxsw_core *mlxsw_core, 876 struct mlxsw_linecards *linecards, 877 u8 slot_index) 878{ 879 struct mlxsw_linecard *linecard; 880 881 linecard = mlxsw_linecard_get(linecards, slot_index); 882 mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false); 883 cancel_delayed_work_sync(&linecard->status_event_to_dw); 884 /* Make sure all scheduled events are processed */ 885 mlxsw_core_flush_owq(); 886 if (linecard->active) 887 mlxsw_linecard_active_clear(linecard); 888 devlink_linecard_destroy(linecard->devlink_linecard); 889 mutex_destroy(&linecard->lock); 890} 891 892/* LINECARDS INI BUNDLE FILE 893 * +----------------------------------+ 894 * | MAGIC ("NVLCINI+") | 895 * +----------------------------------+ +--------------------+ 896 * | INI 0 +---> | __le16 size | 897 * +----------------------------------+ | __be16 hw_revision | 898 * | INI 1 | | __be16 ini_version | 899 * +----------------------------------+ | u8 __dontcare[3] | 900 * | ... | | u8 type | 901 * +----------------------------------+ | u8 name[20] | 902 * | INI N | | ... | 903 * +----------------------------------+ +--------------------+ 904 */ 905 906#define MLXSW_LINECARDS_INI_BUNDLE_MAGIC "NVLCINI+" 907 908static int 909mlxsw_linecard_types_file_validate(struct mlxsw_linecards *linecards, 910 struct mlxsw_linecard_types_info *types_info) 911{ 912 size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC); 913 struct mlxsw_linecard_ini_file *ini_file; 914 size_t size = types_info->data_size; 915 const u8 *data = types_info->data; 916 unsigned int count = 0; 917 u16 ini_file_size; 918 919 if (size < magic_size) { 920 dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file size, smaller than magic size\n"); 921 return -EINVAL; 922 } 923 if (memcmp(data, MLXSW_LINECARDS_INI_BUNDLE_MAGIC, magic_size)) { 924 dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file magic pattern\n"); 925 return -EINVAL; 926 } 927 928 data += magic_size; 929 size -= magic_size; 930 931 while (size > 0) { 932 if (size < sizeof(*ini_file)) { 933 dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI which is smaller than bare minimum\n"); 934 return -EINVAL; 935 } 936 ini_file = (struct mlxsw_linecard_ini_file *) data; 937 ini_file_size = le16_to_cpu(ini_file->size); 938 if (ini_file_size + sizeof(__le16) > size) { 939 dev_warn(linecards->bus_info->dev, "Linecards INIs file appears to be truncated\n"); 940 return -EINVAL; 941 } 942 if (ini_file_size % 4) { 943 dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI with invalid size\n"); 944 return -EINVAL; 945 } 946 data += ini_file_size + sizeof(__le16); 947 size -= ini_file_size + sizeof(__le16); 948 count++; 949 } 950 if (!count) { 951 dev_warn(linecards->bus_info->dev, "Linecards INIs file does not contain any INI\n"); 952 return -EINVAL; 953 } 954 types_info->count = count; 955 return 0; 956} 957 958static void 959mlxsw_linecard_types_file_parse(struct mlxsw_linecard_types_info *types_info) 960{ 961 size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC); 962 size_t size = types_info->data_size - magic_size; 963 const u8 *data = types_info->data + magic_size; 964 struct mlxsw_linecard_ini_file *ini_file; 965 unsigned int count = 0; 966 u16 ini_file_size; 967 int i; 968 969 while (size) { 970 ini_file = (struct mlxsw_linecard_ini_file *) data; 971 ini_file_size = le16_to_cpu(ini_file->size); 972 for (i = 0; i < ini_file_size / 4; i++) { 973 u32 *val = &((u32 *) ini_file->data)[i]; 974 975 *val = swab32(*val); 976 } 977 types_info->ini_files[count] = ini_file; 978 data += ini_file_size + sizeof(__le16); 979 size -= ini_file_size + sizeof(__le16); 980 count++; 981 } 982} 983 984#define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT \ 985 "mellanox/lc_ini_bundle_%u_%u.bin" 986#define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN \ 987 (sizeof(MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT) + 4) 988 989static int mlxsw_linecard_types_init(struct mlxsw_core *mlxsw_core, 990 struct mlxsw_linecards *linecards) 991{ 992 const struct mlxsw_fw_rev *rev = &linecards->bus_info->fw_rev; 993 char filename[MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN]; 994 struct mlxsw_linecard_types_info *types_info; 995 const struct firmware *firmware; 996 int err; 997 998 err = snprintf(filename, sizeof(filename), 999 MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT, 1000 rev->minor, rev->subminor); 1001 WARN_ON(err >= sizeof(filename)); 1002 1003 err = request_firmware_direct(&firmware, filename, 1004 linecards->bus_info->dev); 1005 if (err) { 1006 dev_warn(linecards->bus_info->dev, "Could not request linecards INI file \"%s\", provisioning will not be possible\n", 1007 filename); 1008 return 0; 1009 } 1010 1011 types_info = kzalloc(sizeof(*types_info), GFP_KERNEL); 1012 if (!types_info) { 1013 release_firmware(firmware); 1014 return -ENOMEM; 1015 } 1016 linecards->types_info = types_info; 1017 1018 types_info->data_size = firmware->size; 1019 types_info->data = vmalloc(types_info->data_size); 1020 if (!types_info->data) { 1021 err = -ENOMEM; 1022 release_firmware(firmware); 1023 goto err_data_alloc; 1024 } 1025 memcpy(types_info->data, firmware->data, types_info->data_size); 1026 release_firmware(firmware); 1027 1028 err = mlxsw_linecard_types_file_validate(linecards, types_info); 1029 if (err) { 1030 err = 0; 1031 goto err_type_file_file_validate; 1032 } 1033 1034 types_info->ini_files = kmalloc_array(types_info->count, 1035 sizeof(struct mlxsw_linecard_ini_file *), 1036 GFP_KERNEL); 1037 if (!types_info->ini_files) { 1038 err = -ENOMEM; 1039 goto err_ini_files_alloc; 1040 } 1041 1042 mlxsw_linecard_types_file_parse(types_info); 1043 1044 return 0; 1045 1046err_ini_files_alloc: 1047err_type_file_file_validate: 1048 vfree(types_info->data); 1049err_data_alloc: 1050 kfree(types_info); 1051 return err; 1052} 1053 1054static void mlxsw_linecard_types_fini(struct mlxsw_linecards *linecards) 1055{ 1056 struct mlxsw_linecard_types_info *types_info = linecards->types_info; 1057 1058 if (!types_info) 1059 return; 1060 kfree(types_info->ini_files); 1061 vfree(types_info->data); 1062 kfree(types_info); 1063} 1064 1065int mlxsw_linecards_init(struct mlxsw_core *mlxsw_core, 1066 const struct mlxsw_bus_info *bus_info) 1067{ 1068 char mgpir_pl[MLXSW_REG_MGPIR_LEN]; 1069 struct mlxsw_linecards *linecards; 1070 u8 slot_count; 1071 int err; 1072 int i; 1073 1074 mlxsw_reg_mgpir_pack(mgpir_pl, 0); 1075 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl); 1076 if (err) 1077 return err; 1078 1079 mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL, 1080 NULL, &slot_count); 1081 if (!slot_count) 1082 return 0; 1083 1084 linecards = vzalloc(struct_size(linecards, linecards, slot_count)); 1085 if (!linecards) 1086 return -ENOMEM; 1087 linecards->count = slot_count; 1088 linecards->mlxsw_core = mlxsw_core; 1089 linecards->bus_info = bus_info; 1090 INIT_LIST_HEAD(&linecards->event_ops_list); 1091 mutex_init(&linecards->event_ops_list_lock); 1092 1093 err = mlxsw_linecard_types_init(mlxsw_core, linecards); 1094 if (err) 1095 goto err_types_init; 1096 1097 err = mlxsw_core_traps_register(mlxsw_core, mlxsw_linecard_listener, 1098 ARRAY_SIZE(mlxsw_linecard_listener), 1099 mlxsw_core); 1100 if (err) 1101 goto err_traps_register; 1102 1103 mlxsw_core_linecards_set(mlxsw_core, linecards); 1104 1105 for (i = 0; i < linecards->count; i++) { 1106 err = mlxsw_linecard_init(mlxsw_core, linecards, i + 1); 1107 if (err) 1108 goto err_linecard_init; 1109 } 1110 1111 return 0; 1112 1113err_linecard_init: 1114 for (i--; i >= 0; i--) 1115 mlxsw_linecard_fini(mlxsw_core, linecards, i + 1); 1116 mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener, 1117 ARRAY_SIZE(mlxsw_linecard_listener), 1118 mlxsw_core); 1119err_traps_register: 1120 mlxsw_linecard_types_fini(linecards); 1121err_types_init: 1122 vfree(linecards); 1123 return err; 1124} 1125 1126void mlxsw_linecards_fini(struct mlxsw_core *mlxsw_core) 1127{ 1128 struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core); 1129 int i; 1130 1131 if (!linecards) 1132 return; 1133 for (i = 0; i < linecards->count; i++) 1134 mlxsw_linecard_fini(mlxsw_core, linecards, i + 1); 1135 mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener, 1136 ARRAY_SIZE(mlxsw_linecard_listener), 1137 mlxsw_core); 1138 mlxsw_linecard_types_fini(linecards); 1139 mutex_destroy(&linecards->event_ops_list_lock); 1140 WARN_ON(!list_empty(&linecards->event_ops_list)); 1141 vfree(linecards); 1142}