FILE
core_linecards.c
File preview with syntax highlighting.
linux-6.19.14/drivers/net/ethernet/mellanox/mlxsw/core_linecards.c
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#include "../mlxfw/mlxfw.h" 17 18struct mlxsw_linecard_ini_file { 19 __le16 size; 20 union { 21 u8 data[0]; 22 struct { 23 __be16 hw_revision; 24 __be16 ini_version; 25 u8 __dontcare[3]; 26 u8 type; 27 u8 name[20]; 28 } format; 29 }; 30}; 31 32struct mlxsw_linecard_types_info { 33 struct mlxsw_linecard_ini_file **ini_files; 34 unsigned int count; 35 size_t data_size; 36 char *data; 37}; 38 39#define MLXSW_LINECARD_STATUS_EVENT_TO (10 * MSEC_PER_SEC) 40 41static void 42mlxsw_linecard_status_event_to_schedule(struct mlxsw_linecard *linecard, 43 enum mlxsw_linecard_status_event_type status_event_type) 44{ 45 cancel_delayed_work_sync(&linecard->status_event_to_dw); 46 linecard->status_event_type_to = status_event_type; 47 mlxsw_core_schedule_dw(&linecard->status_event_to_dw, 48 msecs_to_jiffies(MLXSW_LINECARD_STATUS_EVENT_TO)); 49} 50 51static void 52mlxsw_linecard_status_event_done(struct mlxsw_linecard *linecard, 53 enum mlxsw_linecard_status_event_type status_event_type) 54{ 55 if (linecard->status_event_type_to == status_event_type) 56 cancel_delayed_work_sync(&linecard->status_event_to_dw); 57} 58 59static const char * 60mlxsw_linecard_types_lookup(struct mlxsw_linecards *linecards, u8 card_type) 61{ 62 struct mlxsw_linecard_types_info *types_info; 63 struct mlxsw_linecard_ini_file *ini_file; 64 int i; 65 66 types_info = linecards->types_info; 67 if (!types_info) 68 return NULL; 69 for (i = 0; i < types_info->count; i++) { 70 ini_file = linecards->types_info->ini_files[i]; 71 if (ini_file->format.type == card_type) 72 return ini_file->format.name; 73 } 74 return NULL; 75} 76 77static const char *mlxsw_linecard_type_name(struct mlxsw_linecard *linecard) 78{ 79 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core; 80 char mddq_pl[MLXSW_REG_MDDQ_LEN]; 81 int err; 82 83 mlxsw_reg_mddq_slot_name_pack(mddq_pl, linecard->slot_index); 84 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl); 85 if (err) 86 return ERR_PTR(err); 87 mlxsw_reg_mddq_slot_name_unpack(mddq_pl, linecard->name); 88 return linecard->name; 89} 90 91struct mlxsw_linecard_device_fw_info { 92 struct mlxfw_dev mlxfw_dev; 93 struct mlxsw_core *mlxsw_core; 94 struct mlxsw_linecard *linecard; 95}; 96 97static int mlxsw_linecard_device_fw_component_query(struct mlxfw_dev *mlxfw_dev, 98 u16 component_index, 99 u32 *p_max_size, 100 u8 *p_align_bits, 101 u16 *p_max_write_size) 102{ 103 struct mlxsw_linecard_device_fw_info *info = 104 container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, 105 mlxfw_dev); 106 struct mlxsw_linecard *linecard = info->linecard; 107 struct mlxsw_core *mlxsw_core = info->mlxsw_core; 108 char mddt_pl[MLXSW_REG_MDDT_LEN]; 109 char *mcqi_pl; 110 int err; 111 112 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, 113 linecard->device.index, 114 MLXSW_REG_MDDT_METHOD_QUERY, 115 MLXSW_REG(mcqi), &mcqi_pl); 116 117 mlxsw_reg_mcqi_pack(mcqi_pl, component_index); 118 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl); 119 if (err) 120 return err; 121 mlxsw_reg_mcqi_unpack(mcqi_pl, p_max_size, p_align_bits, 122 p_max_write_size); 123 124 *p_align_bits = max_t(u8, *p_align_bits, 2); 125 *p_max_write_size = min_t(u16, *p_max_write_size, 126 MLXSW_REG_MCDA_MAX_DATA_LEN); 127 return 0; 128} 129 130static int mlxsw_linecard_device_fw_fsm_lock(struct mlxfw_dev *mlxfw_dev, 131 u32 *fwhandle) 132{ 133 struct mlxsw_linecard_device_fw_info *info = 134 container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, 135 mlxfw_dev); 136 struct mlxsw_linecard *linecard = info->linecard; 137 struct mlxsw_core *mlxsw_core = info->mlxsw_core; 138 char mddt_pl[MLXSW_REG_MDDT_LEN]; 139 u8 control_state; 140 char *mcc_pl; 141 int err; 142 143 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, 144 linecard->device.index, 145 MLXSW_REG_MDDT_METHOD_QUERY, 146 MLXSW_REG(mcc), &mcc_pl); 147 mlxsw_reg_mcc_pack(mcc_pl, 0, 0, 0, 0); 148 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl); 149 if (err) 150 return err; 151 152 mlxsw_reg_mcc_unpack(mcc_pl, fwhandle, NULL, &control_state); 153 if (control_state != MLXFW_FSM_STATE_IDLE) 154 return -EBUSY; 155 156 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, 157 linecard->device.index, 158 MLXSW_REG_MDDT_METHOD_WRITE, 159 MLXSW_REG(mcc), &mcc_pl); 160 mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_LOCK_UPDATE_HANDLE, 161 0, *fwhandle, 0); 162 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl); 163} 164 165static int 166mlxsw_linecard_device_fw_fsm_component_update(struct mlxfw_dev *mlxfw_dev, 167 u32 fwhandle, 168 u16 component_index, 169 u32 component_size) 170{ 171 struct mlxsw_linecard_device_fw_info *info = 172 container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, 173 mlxfw_dev); 174 struct mlxsw_linecard *linecard = info->linecard; 175 struct mlxsw_core *mlxsw_core = info->mlxsw_core; 176 char mddt_pl[MLXSW_REG_MDDT_LEN]; 177 char *mcc_pl; 178 179 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, 180 linecard->device.index, 181 MLXSW_REG_MDDT_METHOD_WRITE, 182 MLXSW_REG(mcc), &mcc_pl); 183 mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_UPDATE_COMPONENT, 184 component_index, fwhandle, component_size); 185 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl); 186} 187 188static int 189mlxsw_linecard_device_fw_fsm_block_download(struct mlxfw_dev *mlxfw_dev, 190 u32 fwhandle, u8 *data, 191 u16 size, u32 offset) 192{ 193 struct mlxsw_linecard_device_fw_info *info = 194 container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, 195 mlxfw_dev); 196 struct mlxsw_linecard *linecard = info->linecard; 197 struct mlxsw_core *mlxsw_core = info->mlxsw_core; 198 char mddt_pl[MLXSW_REG_MDDT_LEN]; 199 char *mcda_pl; 200 201 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, 202 linecard->device.index, 203 MLXSW_REG_MDDT_METHOD_WRITE, 204 MLXSW_REG(mcda), &mcda_pl); 205 mlxsw_reg_mcda_pack(mcda_pl, fwhandle, offset, size, data); 206 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl); 207} 208 209static int 210mlxsw_linecard_device_fw_fsm_component_verify(struct mlxfw_dev *mlxfw_dev, 211 u32 fwhandle, u16 component_index) 212{ 213 struct mlxsw_linecard_device_fw_info *info = 214 container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, 215 mlxfw_dev); 216 struct mlxsw_linecard *linecard = info->linecard; 217 struct mlxsw_core *mlxsw_core = info->mlxsw_core; 218 char mddt_pl[MLXSW_REG_MDDT_LEN]; 219 char *mcc_pl; 220 221 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, 222 linecard->device.index, 223 MLXSW_REG_MDDT_METHOD_WRITE, 224 MLXSW_REG(mcc), &mcc_pl); 225 mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_VERIFY_COMPONENT, 226 component_index, fwhandle, 0); 227 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl); 228} 229 230static int mlxsw_linecard_device_fw_fsm_activate(struct mlxfw_dev *mlxfw_dev, 231 u32 fwhandle) 232{ 233 struct mlxsw_linecard_device_fw_info *info = 234 container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, 235 mlxfw_dev); 236 struct mlxsw_linecard *linecard = info->linecard; 237 struct mlxsw_core *mlxsw_core = info->mlxsw_core; 238 char mddt_pl[MLXSW_REG_MDDT_LEN]; 239 char *mcc_pl; 240 241 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, 242 linecard->device.index, 243 MLXSW_REG_MDDT_METHOD_WRITE, 244 MLXSW_REG(mcc), &mcc_pl); 245 mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_ACTIVATE, 246 0, fwhandle, 0); 247 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl); 248} 249 250static int 251mlxsw_linecard_device_fw_fsm_query_state(struct mlxfw_dev *mlxfw_dev, 252 u32 fwhandle, 253 enum mlxfw_fsm_state *fsm_state, 254 enum mlxfw_fsm_state_err *fsm_state_err) 255{ 256 struct mlxsw_linecard_device_fw_info *info = 257 container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, 258 mlxfw_dev); 259 struct mlxsw_linecard *linecard = info->linecard; 260 struct mlxsw_core *mlxsw_core = info->mlxsw_core; 261 char mddt_pl[MLXSW_REG_MDDT_LEN]; 262 u8 control_state; 263 u8 error_code; 264 char *mcc_pl; 265 int err; 266 267 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, 268 linecard->device.index, 269 MLXSW_REG_MDDT_METHOD_QUERY, 270 MLXSW_REG(mcc), &mcc_pl); 271 mlxsw_reg_mcc_pack(mcc_pl, 0, 0, fwhandle, 0); 272 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl); 273 if (err) 274 return err; 275 276 mlxsw_reg_mcc_unpack(mcc_pl, NULL, &error_code, &control_state); 277 *fsm_state = control_state; 278 *fsm_state_err = min_t(enum mlxfw_fsm_state_err, error_code, 279 MLXFW_FSM_STATE_ERR_MAX); 280 return 0; 281} 282 283static void mlxsw_linecard_device_fw_fsm_cancel(struct mlxfw_dev *mlxfw_dev, 284 u32 fwhandle) 285{ 286 struct mlxsw_linecard_device_fw_info *info = 287 container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, 288 mlxfw_dev); 289 struct mlxsw_linecard *linecard = info->linecard; 290 struct mlxsw_core *mlxsw_core = info->mlxsw_core; 291 char mddt_pl[MLXSW_REG_MDDT_LEN]; 292 char *mcc_pl; 293 294 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, 295 linecard->device.index, 296 MLXSW_REG_MDDT_METHOD_WRITE, 297 MLXSW_REG(mcc), &mcc_pl); 298 mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_CANCEL, 299 0, fwhandle, 0); 300 mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl); 301} 302 303static void mlxsw_linecard_device_fw_fsm_release(struct mlxfw_dev *mlxfw_dev, 304 u32 fwhandle) 305{ 306 struct mlxsw_linecard_device_fw_info *info = 307 container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, 308 mlxfw_dev); 309 struct mlxsw_linecard *linecard = info->linecard; 310 struct mlxsw_core *mlxsw_core = info->mlxsw_core; 311 char mddt_pl[MLXSW_REG_MDDT_LEN]; 312 char *mcc_pl; 313 314 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, 315 linecard->device.index, 316 MLXSW_REG_MDDT_METHOD_WRITE, 317 MLXSW_REG(mcc), &mcc_pl); 318 mlxsw_reg_mcc_pack(mcc_pl, 319 MLXSW_REG_MCC_INSTRUCTION_RELEASE_UPDATE_HANDLE, 320 0, fwhandle, 0); 321 mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl); 322} 323 324static const struct mlxfw_dev_ops mlxsw_linecard_device_dev_ops = { 325 .component_query = mlxsw_linecard_device_fw_component_query, 326 .fsm_lock = mlxsw_linecard_device_fw_fsm_lock, 327 .fsm_component_update = mlxsw_linecard_device_fw_fsm_component_update, 328 .fsm_block_download = mlxsw_linecard_device_fw_fsm_block_download, 329 .fsm_component_verify = mlxsw_linecard_device_fw_fsm_component_verify, 330 .fsm_activate = mlxsw_linecard_device_fw_fsm_activate, 331 .fsm_query_state = mlxsw_linecard_device_fw_fsm_query_state, 332 .fsm_cancel = mlxsw_linecard_device_fw_fsm_cancel, 333 .fsm_release = mlxsw_linecard_device_fw_fsm_release, 334}; 335 336int mlxsw_linecard_flash_update(struct devlink *linecard_devlink, 337 struct mlxsw_linecard *linecard, 338 const struct firmware *firmware, 339 struct netlink_ext_ack *extack) 340{ 341 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core; 342 struct mlxsw_linecard_device_fw_info info = { 343 .mlxfw_dev = { 344 .ops = &mlxsw_linecard_device_dev_ops, 345 .psid = linecard->device.info.psid, 346 .psid_size = strlen(linecard->device.info.psid), 347 .devlink = linecard_devlink, 348 }, 349 .mlxsw_core = mlxsw_core, 350 .linecard = linecard, 351 }; 352 int err; 353 354 mutex_lock(&linecard->lock); 355 if (!linecard->active) { 356 NL_SET_ERR_MSG_MOD(extack, "Only active line cards can be flashed"); 357 err = -EINVAL; 358 goto unlock; 359 } 360 err = mlxsw_core_fw_flash(mlxsw_core, &info.mlxfw_dev, 361 firmware, extack); 362unlock: 363 mutex_unlock(&linecard->lock); 364 return err; 365} 366 367static int mlxsw_linecard_device_psid_get(struct mlxsw_linecard *linecard, 368 u8 device_index, char *psid) 369{ 370 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core; 371 char mddt_pl[MLXSW_REG_MDDT_LEN]; 372 char *mgir_pl; 373 int err; 374 375 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, device_index, 376 MLXSW_REG_MDDT_METHOD_QUERY, 377 MLXSW_REG(mgir), &mgir_pl); 378 379 mlxsw_reg_mgir_pack(mgir_pl); 380 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl); 381 if (err) 382 return err; 383 384 mlxsw_reg_mgir_fw_info_psid_memcpy_from(mgir_pl, psid); 385 return 0; 386} 387 388static int mlxsw_linecard_device_info_update(struct mlxsw_linecard *linecard) 389{ 390 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core; 391 bool flashable_found = false; 392 u8 msg_seq = 0; 393 394 do { 395 struct mlxsw_linecard_device_info info; 396 char mddq_pl[MLXSW_REG_MDDQ_LEN]; 397 bool flash_owner; 398 bool data_valid; 399 u8 device_index; 400 int err; 401 402 mlxsw_reg_mddq_device_info_pack(mddq_pl, linecard->slot_index, 403 msg_seq); 404 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl); 405 if (err) 406 return err; 407 mlxsw_reg_mddq_device_info_unpack(mddq_pl, &msg_seq, 408 &data_valid, &flash_owner, 409 &device_index, 410 &info.fw_major, 411 &info.fw_minor, 412 &info.fw_sub_minor); 413 if (!data_valid) 414 break; 415 if (!flash_owner) /* We care only about flashable ones. */ 416 continue; 417 if (flashable_found) { 418 dev_warn_once(linecard->linecards->bus_info->dev, "linecard %u: More flashable devices present, exposing only the first one\n", 419 linecard->slot_index); 420 return 0; 421 } 422 423 err = mlxsw_linecard_device_psid_get(linecard, device_index, 424 info.psid); 425 if (err) 426 return err; 427 428 linecard->device.info = info; 429 linecard->device.index = device_index; 430 flashable_found = true; 431 } while (msg_seq); 432 433 return 0; 434} 435 436static void mlxsw_linecard_provision_fail(struct mlxsw_linecard *linecard) 437{ 438 linecard->provisioned = false; 439 linecard->ready = false; 440 linecard->active = false; 441 devlink_linecard_provision_fail(linecard->devlink_linecard); 442} 443 444struct mlxsw_linecards_event_ops_item { 445 struct list_head list; 446 const struct mlxsw_linecards_event_ops *event_ops; 447 void *priv; 448}; 449 450static void 451mlxsw_linecard_event_op_call(struct mlxsw_linecard *linecard, 452 mlxsw_linecards_event_op_t *op, void *priv) 453{ 454 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core; 455 456 if (!op) 457 return; 458 op(mlxsw_core, linecard->slot_index, priv); 459} 460 461static void 462mlxsw_linecard_active_ops_call(struct mlxsw_linecard *linecard) 463{ 464 struct mlxsw_linecards *linecards = linecard->linecards; 465 struct mlxsw_linecards_event_ops_item *item; 466 467 mutex_lock(&linecards->event_ops_list_lock); 468 list_for_each_entry(item, &linecards->event_ops_list, list) 469 mlxsw_linecard_event_op_call(linecard, 470 item->event_ops->got_active, 471 item->priv); 472 mutex_unlock(&linecards->event_ops_list_lock); 473} 474 475static void 476mlxsw_linecard_inactive_ops_call(struct mlxsw_linecard *linecard) 477{ 478 struct mlxsw_linecards *linecards = linecard->linecards; 479 struct mlxsw_linecards_event_ops_item *item; 480 481 mutex_lock(&linecards->event_ops_list_lock); 482 list_for_each_entry(item, &linecards->event_ops_list, list) 483 mlxsw_linecard_event_op_call(linecard, 484 item->event_ops->got_inactive, 485 item->priv); 486 mutex_unlock(&linecards->event_ops_list_lock); 487} 488 489static void 490mlxsw_linecards_event_ops_register_call(struct mlxsw_linecards *linecards, 491 const struct mlxsw_linecards_event_ops_item *item) 492{ 493 struct mlxsw_linecard *linecard; 494 int i; 495 496 for (i = 0; i < linecards->count; i++) { 497 linecard = mlxsw_linecard_get(linecards, i + 1); 498 mutex_lock(&linecard->lock); 499 if (linecard->active) 500 mlxsw_linecard_event_op_call(linecard, 501 item->event_ops->got_active, 502 item->priv); 503 mutex_unlock(&linecard->lock); 504 } 505} 506 507static void 508mlxsw_linecards_event_ops_unregister_call(struct mlxsw_linecards *linecards, 509 const struct mlxsw_linecards_event_ops_item *item) 510{ 511 struct mlxsw_linecard *linecard; 512 int i; 513 514 for (i = 0; i < linecards->count; i++) { 515 linecard = mlxsw_linecard_get(linecards, i + 1); 516 mutex_lock(&linecard->lock); 517 if (linecard->active) 518 mlxsw_linecard_event_op_call(linecard, 519 item->event_ops->got_inactive, 520 item->priv); 521 mutex_unlock(&linecard->lock); 522 } 523} 524 525int mlxsw_linecards_event_ops_register(struct mlxsw_core *mlxsw_core, 526 struct mlxsw_linecards_event_ops *ops, 527 void *priv) 528{ 529 struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core); 530 struct mlxsw_linecards_event_ops_item *item; 531 532 if (!linecards) 533 return 0; 534 item = kzalloc(sizeof(*item), GFP_KERNEL); 535 if (!item) 536 return -ENOMEM; 537 item->event_ops = ops; 538 item->priv = priv; 539 540 mutex_lock(&linecards->event_ops_list_lock); 541 list_add_tail(&item->list, &linecards->event_ops_list); 542 mutex_unlock(&linecards->event_ops_list_lock); 543 mlxsw_linecards_event_ops_register_call(linecards, item); 544 return 0; 545} 546EXPORT_SYMBOL(mlxsw_linecards_event_ops_register); 547 548void mlxsw_linecards_event_ops_unregister(struct mlxsw_core *mlxsw_core, 549 struct mlxsw_linecards_event_ops *ops, 550 void *priv) 551{ 552 struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core); 553 struct mlxsw_linecards_event_ops_item *item, *tmp; 554 bool found = false; 555 556 if (!linecards) 557 return; 558 mutex_lock(&linecards->event_ops_list_lock); 559 list_for_each_entry_safe(item, tmp, &linecards->event_ops_list, list) { 560 if (item->event_ops == ops && item->priv == priv) { 561 list_del(&item->list); 562 found = true; 563 break; 564 } 565 } 566 mutex_unlock(&linecards->event_ops_list_lock); 567 568 if (!found) 569 return; 570 mlxsw_linecards_event_ops_unregister_call(linecards, item); 571 kfree(item); 572} 573EXPORT_SYMBOL(mlxsw_linecards_event_ops_unregister); 574 575int mlxsw_linecard_devlink_info_get(struct mlxsw_linecard *linecard, 576 struct devlink_info_req *req, 577 struct netlink_ext_ack *extack) 578{ 579 char buf[32]; 580 int err; 581 582 mutex_lock(&linecard->lock); 583 if (WARN_ON(!linecard->provisioned)) { 584 err = -EOPNOTSUPP; 585 goto unlock; 586 } 587 588 sprintf(buf, "%d", linecard->hw_revision); 589 err = devlink_info_version_fixed_put(req, "hw.revision", buf); 590 if (err) 591 goto unlock; 592 593 sprintf(buf, "%d", linecard->ini_version); 594 err = devlink_info_version_running_put(req, "ini.version", buf); 595 if (err) 596 goto unlock; 597 598 if (linecard->active) { 599 struct mlxsw_linecard_device_info *info = &linecard->device.info; 600 601 err = devlink_info_version_fixed_put(req, 602 DEVLINK_INFO_VERSION_GENERIC_FW_PSID, 603 info->psid); 604 if (err) 605 goto unlock; 606 607 sprintf(buf, "%u.%u.%u", info->fw_major, info->fw_minor, 608 info->fw_sub_minor); 609 err = devlink_info_version_running_put(req, 610 DEVLINK_INFO_VERSION_GENERIC_FW, 611 buf); 612 if (err) 613 goto unlock; 614 } 615 616unlock: 617 mutex_unlock(&linecard->lock); 618 return err; 619} 620 621static int 622mlxsw_linecard_provision_set(struct mlxsw_linecard *linecard, u8 card_type, 623 u16 hw_revision, u16 ini_version) 624{ 625 struct mlxsw_linecards *linecards = linecard->linecards; 626 const char *type; 627 int err; 628 629 type = mlxsw_linecard_types_lookup(linecards, card_type); 630 mlxsw_linecard_status_event_done(linecard, 631 MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION); 632 if (!type) { 633 /* It is possible for a line card to be provisioned before 634 * driver initialization. Due to a missing INI bundle file 635 * or an outdated one, the queried card's type might not 636 * be recognized by the driver. In this case, try to query 637 * the card's name from the device. 638 */ 639 type = mlxsw_linecard_type_name(linecard); 640 if (IS_ERR(type)) { 641 mlxsw_linecard_provision_fail(linecard); 642 return PTR_ERR(type); 643 } 644 } 645 linecard->provisioned = true; 646 linecard->hw_revision = hw_revision; 647 linecard->ini_version = ini_version; 648 649 err = mlxsw_linecard_bdev_add(linecard); 650 if (err) { 651 linecard->provisioned = false; 652 mlxsw_linecard_provision_fail(linecard); 653 return err; 654 } 655 656 devlink_linecard_provision_set(linecard->devlink_linecard, type); 657 return 0; 658} 659 660static void mlxsw_linecard_provision_clear(struct mlxsw_linecard *linecard) 661{ 662 mlxsw_linecard_status_event_done(linecard, 663 MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION); 664 mlxsw_linecard_bdev_del(linecard); 665 linecard->provisioned = false; 666 devlink_linecard_provision_clear(linecard->devlink_linecard); 667} 668 669static int mlxsw_linecard_ready_set(struct mlxsw_linecard *linecard) 670{ 671 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core; 672 char mddc_pl[MLXSW_REG_MDDC_LEN]; 673 int err; 674 675 err = mlxsw_linecard_device_info_update(linecard); 676 if (err) 677 return err; 678 679 mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, false, true); 680 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl); 681 if (err) 682 return err; 683 linecard->ready = true; 684 return 0; 685} 686 687static int mlxsw_linecard_ready_clear(struct mlxsw_linecard *linecard) 688{ 689 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core; 690 char mddc_pl[MLXSW_REG_MDDC_LEN]; 691 int err; 692 693 mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, false, false); 694 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl); 695 if (err) 696 return err; 697 linecard->ready = false; 698 return 0; 699} 700 701static void mlxsw_linecard_active_set(struct mlxsw_linecard *linecard) 702{ 703 mlxsw_linecard_active_ops_call(linecard); 704 linecard->active = true; 705 devlink_linecard_activate(linecard->devlink_linecard); 706} 707 708static void mlxsw_linecard_active_clear(struct mlxsw_linecard *linecard) 709{ 710 mlxsw_linecard_inactive_ops_call(linecard); 711 linecard->active = false; 712 devlink_linecard_deactivate(linecard->devlink_linecard); 713} 714 715static int mlxsw_linecard_status_process(struct mlxsw_linecards *linecards, 716 struct mlxsw_linecard *linecard, 717 const char *mddq_pl) 718{ 719 enum mlxsw_reg_mddq_slot_info_ready ready; 720 bool provisioned, sr_valid, active; 721 u16 ini_version, hw_revision; 722 u8 slot_index, card_type; 723 int err = 0; 724 725 mlxsw_reg_mddq_slot_info_unpack(mddq_pl, &slot_index, &provisioned, 726 &sr_valid, &ready, &active, 727 &hw_revision, &ini_version, 728 &card_type); 729 730 if (linecard) { 731 if (WARN_ON(slot_index != linecard->slot_index)) 732 return -EINVAL; 733 } else { 734 if (WARN_ON(slot_index > linecards->count)) 735 return -EINVAL; 736 linecard = mlxsw_linecard_get(linecards, slot_index); 737 } 738 739 mutex_lock(&linecard->lock); 740 741 if (provisioned && linecard->provisioned != provisioned) { 742 err = mlxsw_linecard_provision_set(linecard, card_type, 743 hw_revision, ini_version); 744 if (err) 745 goto out; 746 } 747 748 if (ready == MLXSW_REG_MDDQ_SLOT_INFO_READY_READY && !linecard->ready) { 749 err = mlxsw_linecard_ready_set(linecard); 750 if (err) 751 goto out; 752 } 753 754 if (active && linecard->active != active) 755 mlxsw_linecard_active_set(linecard); 756 757 if (!active && linecard->active != active) 758 mlxsw_linecard_active_clear(linecard); 759 760 if (ready != MLXSW_REG_MDDQ_SLOT_INFO_READY_READY && 761 linecard->ready) { 762 err = mlxsw_linecard_ready_clear(linecard); 763 if (err) 764 goto out; 765 } 766 767 if (!provisioned && linecard->provisioned != provisioned) 768 mlxsw_linecard_provision_clear(linecard); 769 770out: 771 mutex_unlock(&linecard->lock); 772 return err; 773} 774 775static int mlxsw_linecard_status_get_and_process(struct mlxsw_core *mlxsw_core, 776 struct mlxsw_linecards *linecards, 777 struct mlxsw_linecard *linecard) 778{ 779 char mddq_pl[MLXSW_REG_MDDQ_LEN]; 780 int err; 781 782 mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, false); 783 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl); 784 if (err) 785 return err; 786 787 return mlxsw_linecard_status_process(linecards, linecard, mddq_pl); 788} 789 790static void mlxsw_linecards_irq_event_handler(struct mlxsw_core *mlxsw_core) 791{ 792 struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core); 793 int i; 794 795 /* Handle change of line card active state. */ 796 for (i = 0; i < linecards->count; i++) { 797 struct mlxsw_linecard *linecard = mlxsw_linecard_get(linecards, 798 i + 1); 799 800 mlxsw_linecard_status_get_and_process(mlxsw_core, linecards, 801 linecard); 802 } 803} 804 805static const char * const mlxsw_linecard_status_event_type_name[] = { 806 [MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION] = "provision", 807 [MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION] = "unprovision", 808}; 809 810static void mlxsw_linecard_status_event_to_work(struct work_struct *work) 811{ 812 struct mlxsw_linecard *linecard = 813 container_of(work, struct mlxsw_linecard, 814 status_event_to_dw.work); 815 816 mutex_lock(&linecard->lock); 817 dev_err(linecard->linecards->bus_info->dev, "linecard %u: Timeout reached waiting on %s status event", 818 linecard->slot_index, 819 mlxsw_linecard_status_event_type_name[linecard->status_event_type_to]); 820 mlxsw_linecard_provision_fail(linecard); 821 mutex_unlock(&linecard->lock); 822} 823 824static int __mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard) 825{ 826 dev_info(linecard->linecards->bus_info->dev, "linecard %u: Clearing FSM state error", 827 linecard->slot_index); 828 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index, 829 MLXSW_REG_MBCT_OP_CLEAR_ERRORS, false); 830 return mlxsw_reg_write(linecard->linecards->mlxsw_core, 831 MLXSW_REG(mbct), linecard->mbct_pl); 832} 833 834static int mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard, 835 enum mlxsw_reg_mbct_fsm_state fsm_state) 836{ 837 if (fsm_state != MLXSW_REG_MBCT_FSM_STATE_ERROR) 838 return 0; 839 return __mlxsw_linecard_fix_fsm_state(linecard); 840} 841 842static int 843mlxsw_linecard_query_ini_status(struct mlxsw_linecard *linecard, 844 enum mlxsw_reg_mbct_status *status, 845 enum mlxsw_reg_mbct_fsm_state *fsm_state, 846 struct netlink_ext_ack *extack) 847{ 848 int err; 849 850 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index, 851 MLXSW_REG_MBCT_OP_QUERY_STATUS, false); 852 err = mlxsw_reg_query(linecard->linecards->mlxsw_core, MLXSW_REG(mbct), 853 linecard->mbct_pl); 854 if (err) { 855 NL_SET_ERR_MSG_MOD(extack, "Failed to query linecard INI status"); 856 return err; 857 } 858 mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, status, fsm_state); 859 return err; 860} 861 862static int 863mlxsw_linecard_ini_transfer(struct mlxsw_core *mlxsw_core, 864 struct mlxsw_linecard *linecard, 865 const struct mlxsw_linecard_ini_file *ini_file, 866 struct netlink_ext_ack *extack) 867{ 868 enum mlxsw_reg_mbct_fsm_state fsm_state; 869 enum mlxsw_reg_mbct_status status; 870 size_t size_left; 871 const u8 *data; 872 int err; 873 874 size_left = le16_to_cpu(ini_file->size); 875 data = ini_file->data; 876 while (size_left) { 877 size_t data_size = MLXSW_REG_MBCT_DATA_LEN; 878 bool is_last = false; 879 880 if (size_left <= MLXSW_REG_MBCT_DATA_LEN) { 881 data_size = size_left; 882 is_last = true; 883 } 884 885 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index, 886 MLXSW_REG_MBCT_OP_DATA_TRANSFER, false); 887 mlxsw_reg_mbct_dt_pack(linecard->mbct_pl, data_size, 888 is_last, data); 889 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct), 890 linecard->mbct_pl); 891 if (err) { 892 NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI data transfer"); 893 return err; 894 } 895 mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, 896 &status, &fsm_state); 897 if ((!is_last && status != MLXSW_REG_MBCT_STATUS_PART_DATA) || 898 (is_last && status != MLXSW_REG_MBCT_STATUS_LAST_DATA)) { 899 NL_SET_ERR_MSG_MOD(extack, "Failed to transfer linecard INI data"); 900 mlxsw_linecard_fix_fsm_state(linecard, fsm_state); 901 return -EINVAL; 902 } 903 size_left -= data_size; 904 data += data_size; 905 } 906 907 return 0; 908} 909 910static int 911mlxsw_linecard_ini_erase(struct mlxsw_core *mlxsw_core, 912 struct mlxsw_linecard *linecard, 913 struct netlink_ext_ack *extack) 914{ 915 enum mlxsw_reg_mbct_fsm_state fsm_state; 916 enum mlxsw_reg_mbct_status status; 917 int err; 918 919 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index, 920 MLXSW_REG_MBCT_OP_ERASE_INI_IMAGE, false); 921 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct), 922 linecard->mbct_pl); 923 if (err) { 924 NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI erase"); 925 return err; 926 } 927 mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state); 928 switch (status) { 929 case MLXSW_REG_MBCT_STATUS_ERASE_COMPLETE: 930 break; 931 default: 932 /* Should not happen */ 933 fallthrough; 934 case MLXSW_REG_MBCT_STATUS_ERASE_FAILED: 935 NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI"); 936 goto fix_fsm_err_out; 937 case MLXSW_REG_MBCT_STATUS_ERROR_INI_IN_USE: 938 NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI while being used"); 939 goto fix_fsm_err_out; 940 } 941 return 0; 942 943fix_fsm_err_out: 944 mlxsw_linecard_fix_fsm_state(linecard, fsm_state); 945 return -EINVAL; 946} 947 948static void mlxsw_linecard_bct_process(struct mlxsw_core *mlxsw_core, 949 const char *mbct_pl) 950{ 951 struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core); 952 enum mlxsw_reg_mbct_fsm_state fsm_state; 953 enum mlxsw_reg_mbct_status status; 954 struct mlxsw_linecard *linecard; 955 u8 slot_index; 956 957 mlxsw_reg_mbct_unpack(mbct_pl, &slot_index, &status, &fsm_state); 958 if (WARN_ON(slot_index > linecards->count)) 959 return; 960 linecard = mlxsw_linecard_get(linecards, slot_index); 961 mutex_lock(&linecard->lock); 962 if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) { 963 dev_err(linecards->bus_info->dev, "linecard %u: Failed to activate INI", 964 linecard->slot_index); 965 goto fix_fsm_out; 966 } 967 mutex_unlock(&linecard->lock); 968 return; 969 970fix_fsm_out: 971 mlxsw_linecard_fix_fsm_state(linecard, fsm_state); 972 mlxsw_linecard_provision_fail(linecard); 973 mutex_unlock(&linecard->lock); 974} 975 976static int 977mlxsw_linecard_ini_activate(struct mlxsw_core *mlxsw_core, 978 struct mlxsw_linecard *linecard, 979 struct netlink_ext_ack *extack) 980{ 981 enum mlxsw_reg_mbct_fsm_state fsm_state; 982 enum mlxsw_reg_mbct_status status; 983 int err; 984 985 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index, 986 MLXSW_REG_MBCT_OP_ACTIVATE, true); 987 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct), linecard->mbct_pl); 988 if (err) { 989 NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI activation"); 990 return err; 991 } 992 mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state); 993 if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) { 994 NL_SET_ERR_MSG_MOD(extack, "Failed to activate linecard INI"); 995 goto fix_fsm_err_out; 996 } 997 998 return 0; 999 1000fix_fsm_err_out: 1001 mlxsw_linecard_fix_fsm_state(linecard, fsm_state); 1002 return -EINVAL; 1003} 1004 1005#define MLXSW_LINECARD_INI_WAIT_RETRIES 10 1006#define MLXSW_LINECARD_INI_WAIT_MS 500 1007 1008static int 1009mlxsw_linecard_ini_in_use_wait(struct mlxsw_core *mlxsw_core, 1010 struct mlxsw_linecard *linecard, 1011 struct netlink_ext_ack *extack) 1012{ 1013 enum mlxsw_reg_mbct_fsm_state fsm_state; 1014 enum mlxsw_reg_mbct_status status; 1015 unsigned int ini_wait_retries = 0; 1016 int err; 1017 1018query_ini_status: 1019 err = mlxsw_linecard_query_ini_status(linecard, &status, 1020 &fsm_state, extack); 1021 if (err) 1022 return err; 1023 1024 switch (fsm_state) { 1025 case MLXSW_REG_MBCT_FSM_STATE_INI_IN_USE: 1026 if (ini_wait_retries++ > MLXSW_LINECARD_INI_WAIT_RETRIES) { 1027 NL_SET_ERR_MSG_MOD(extack, "Failed to wait for linecard INI to be unused"); 1028 return -EINVAL; 1029 } 1030 mdelay(MLXSW_LINECARD_INI_WAIT_MS); 1031 goto query_ini_status; 1032 default: 1033 break; 1034 } 1035 return 0; 1036} 1037 1038static bool mlxsw_linecard_port_selector(void *priv, u16 local_port) 1039{ 1040 struct mlxsw_linecard *linecard = priv; 1041 struct mlxsw_core *mlxsw_core; 1042 1043 mlxsw_core = linecard->linecards->mlxsw_core; 1044 return linecard == mlxsw_core_port_linecard_get(mlxsw_core, local_port); 1045} 1046 1047static int mlxsw_linecard_provision(struct devlink_linecard *devlink_linecard, 1048 void *priv, const char *type, 1049 const void *type_priv, 1050 struct netlink_ext_ack *extack) 1051{ 1052 const struct mlxsw_linecard_ini_file *ini_file = type_priv; 1053 struct mlxsw_linecard *linecard = priv; 1054 struct mlxsw_core *mlxsw_core; 1055 int err; 1056 1057 mutex_lock(&linecard->lock); 1058 1059 mlxsw_core = linecard->linecards->mlxsw_core; 1060 1061 err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack); 1062 if (err) 1063 goto err_out; 1064 1065 err = mlxsw_linecard_ini_transfer(mlxsw_core, linecard, 1066 ini_file, extack); 1067 if (err) 1068 goto err_out; 1069 1070 mlxsw_linecard_status_event_to_schedule(linecard, 1071 MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION); 1072 err = mlxsw_linecard_ini_activate(mlxsw_core, linecard, extack); 1073 if (err) 1074 goto err_out; 1075 1076 goto out; 1077 1078err_out: 1079 mlxsw_linecard_provision_fail(linecard); 1080out: 1081 mutex_unlock(&linecard->lock); 1082 return err; 1083} 1084 1085static int mlxsw_linecard_unprovision(struct devlink_linecard *devlink_linecard, 1086 void *priv, 1087 struct netlink_ext_ack *extack) 1088{ 1089 struct mlxsw_linecard *linecard = priv; 1090 struct mlxsw_core *mlxsw_core; 1091 int err; 1092 1093 mutex_lock(&linecard->lock); 1094 1095 mlxsw_core = linecard->linecards->mlxsw_core; 1096 1097 mlxsw_core_ports_remove_selected(mlxsw_core, 1098 mlxsw_linecard_port_selector, 1099 linecard); 1100 1101 err = mlxsw_linecard_ini_in_use_wait(mlxsw_core, linecard, extack); 1102 if (err) 1103 goto err_out; 1104 1105 mlxsw_linecard_status_event_to_schedule(linecard, 1106 MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION); 1107 err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack); 1108 if (err) 1109 goto err_out; 1110 1111 goto out; 1112 1113err_out: 1114 mlxsw_linecard_provision_fail(linecard); 1115out: 1116 mutex_unlock(&linecard->lock); 1117 return err; 1118} 1119 1120static bool mlxsw_linecard_same_provision(struct devlink_linecard *devlink_linecard, 1121 void *priv, const char *type, 1122 const void *type_priv) 1123{ 1124 const struct mlxsw_linecard_ini_file *ini_file = type_priv; 1125 struct mlxsw_linecard *linecard = priv; 1126 bool ret; 1127 1128 mutex_lock(&linecard->lock); 1129 ret = linecard->hw_revision == be16_to_cpu(ini_file->format.hw_revision) && 1130 linecard->ini_version == be16_to_cpu(ini_file->format.ini_version); 1131 mutex_unlock(&linecard->lock); 1132 return ret; 1133} 1134 1135static unsigned int 1136mlxsw_linecard_types_count(struct devlink_linecard *devlink_linecard, 1137 void *priv) 1138{ 1139 struct mlxsw_linecard *linecard = priv; 1140 1141 return linecard->linecards->types_info ? 1142 linecard->linecards->types_info->count : 0; 1143} 1144 1145static void mlxsw_linecard_types_get(struct devlink_linecard *devlink_linecard, 1146 void *priv, unsigned int index, 1147 const char **type, const void **type_priv) 1148{ 1149 struct mlxsw_linecard_types_info *types_info; 1150 struct mlxsw_linecard_ini_file *ini_file; 1151 struct mlxsw_linecard *linecard = priv; 1152 1153 types_info = linecard->linecards->types_info; 1154 if (WARN_ON_ONCE(!types_info)) 1155 return; 1156 ini_file = types_info->ini_files[index]; 1157 *type = ini_file->format.name; 1158 *type_priv = ini_file; 1159} 1160 1161static const struct devlink_linecard_ops mlxsw_linecard_ops = { 1162 .provision = mlxsw_linecard_provision, 1163 .unprovision = mlxsw_linecard_unprovision, 1164 .same_provision = mlxsw_linecard_same_provision, 1165 .types_count = mlxsw_linecard_types_count, 1166 .types_get = mlxsw_linecard_types_get, 1167}; 1168 1169struct mlxsw_linecard_status_event { 1170 struct mlxsw_core *mlxsw_core; 1171 char mddq_pl[MLXSW_REG_MDDQ_LEN]; 1172 struct work_struct work; 1173}; 1174 1175static void mlxsw_linecard_status_event_work(struct work_struct *work) 1176{ 1177 struct mlxsw_linecard_status_event *event; 1178 struct mlxsw_linecards *linecards; 1179 struct mlxsw_core *mlxsw_core; 1180 1181 event = container_of(work, struct mlxsw_linecard_status_event, work); 1182 mlxsw_core = event->mlxsw_core; 1183 linecards = mlxsw_core_linecards(mlxsw_core); 1184 mlxsw_linecard_status_process(linecards, NULL, event->mddq_pl); 1185 kfree(event); 1186} 1187 1188static void 1189mlxsw_linecard_status_listener_func(const struct mlxsw_reg_info *reg, 1190 char *mddq_pl, void *priv) 1191{ 1192 struct mlxsw_linecard_status_event *event; 1193 struct mlxsw_core *mlxsw_core = priv; 1194 1195 event = kmalloc(sizeof(*event), GFP_ATOMIC); 1196 if (!event) 1197 return; 1198 event->mlxsw_core = mlxsw_core; 1199 memcpy(event->mddq_pl, mddq_pl, sizeof(event->mddq_pl)); 1200 INIT_WORK(&event->work, mlxsw_linecard_status_event_work); 1201 mlxsw_core_schedule_work(&event->work); 1202} 1203 1204struct mlxsw_linecard_bct_event { 1205 struct mlxsw_core *mlxsw_core; 1206 char mbct_pl[MLXSW_REG_MBCT_LEN]; 1207 struct work_struct work; 1208}; 1209 1210static void mlxsw_linecard_bct_event_work(struct work_struct *work) 1211{ 1212 struct mlxsw_linecard_bct_event *event; 1213 struct mlxsw_core *mlxsw_core; 1214 1215 event = container_of(work, struct mlxsw_linecard_bct_event, work); 1216 mlxsw_core = event->mlxsw_core; 1217 mlxsw_linecard_bct_process(mlxsw_core, event->mbct_pl); 1218 kfree(event); 1219} 1220 1221static void 1222mlxsw_linecard_bct_listener_func(const struct mlxsw_reg_info *reg, 1223 char *mbct_pl, void *priv) 1224{ 1225 struct mlxsw_linecard_bct_event *event; 1226 struct mlxsw_core *mlxsw_core = priv; 1227 1228 event = kmalloc(sizeof(*event), GFP_ATOMIC); 1229 if (!event) 1230 return; 1231 event->mlxsw_core = mlxsw_core; 1232 memcpy(event->mbct_pl, mbct_pl, sizeof(event->mbct_pl)); 1233 INIT_WORK(&event->work, mlxsw_linecard_bct_event_work); 1234 mlxsw_core_schedule_work(&event->work); 1235} 1236 1237static const struct mlxsw_listener mlxsw_linecard_listener[] = { 1238 MLXSW_CORE_EVENTL(mlxsw_linecard_status_listener_func, DSDSC), 1239 MLXSW_CORE_EVENTL(mlxsw_linecard_bct_listener_func, BCTOE), 1240}; 1241 1242static int mlxsw_linecard_event_delivery_set(struct mlxsw_core *mlxsw_core, 1243 struct mlxsw_linecard *linecard, 1244 bool enable) 1245{ 1246 char mddq_pl[MLXSW_REG_MDDQ_LEN]; 1247 1248 mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, enable); 1249 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddq), mddq_pl); 1250} 1251 1252static int mlxsw_linecard_init(struct mlxsw_core *mlxsw_core, 1253 struct mlxsw_linecards *linecards, 1254 u8 slot_index) 1255{ 1256 struct devlink_linecard *devlink_linecard; 1257 struct mlxsw_linecard *linecard; 1258 1259 linecard = mlxsw_linecard_get(linecards, slot_index); 1260 linecard->slot_index = slot_index; 1261 linecard->linecards = linecards; 1262 mutex_init(&linecard->lock); 1263 1264 devlink_linecard = devl_linecard_create(priv_to_devlink(mlxsw_core), 1265 slot_index, &mlxsw_linecard_ops, 1266 linecard); 1267 if (IS_ERR(devlink_linecard)) 1268 return PTR_ERR(devlink_linecard); 1269 1270 linecard->devlink_linecard = devlink_linecard; 1271 INIT_DELAYED_WORK(&linecard->status_event_to_dw, 1272 &mlxsw_linecard_status_event_to_work); 1273 1274 return 0; 1275} 1276 1277static void mlxsw_linecard_fini(struct mlxsw_core *mlxsw_core, 1278 struct mlxsw_linecards *linecards, 1279 u8 slot_index) 1280{ 1281 struct mlxsw_linecard *linecard; 1282 1283 linecard = mlxsw_linecard_get(linecards, slot_index); 1284 cancel_delayed_work_sync(&linecard->status_event_to_dw); 1285 /* Make sure all scheduled events are processed */ 1286 mlxsw_core_flush_owq(); 1287 if (linecard->active) 1288 mlxsw_linecard_active_clear(linecard); 1289 mlxsw_linecard_bdev_del(linecard); 1290 devl_linecard_destroy(linecard->devlink_linecard); 1291 mutex_destroy(&linecard->lock); 1292} 1293 1294static int 1295mlxsw_linecard_event_delivery_init(struct mlxsw_core *mlxsw_core, 1296 struct mlxsw_linecards *linecards, 1297 u8 slot_index) 1298{ 1299 struct mlxsw_linecard *linecard; 1300 int err; 1301 1302 linecard = mlxsw_linecard_get(linecards, slot_index); 1303 err = mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, true); 1304 if (err) 1305 return err; 1306 1307 err = mlxsw_linecard_status_get_and_process(mlxsw_core, linecards, 1308 linecard); 1309 if (err) 1310 goto err_status_get_and_process; 1311 1312 return 0; 1313 1314err_status_get_and_process: 1315 mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false); 1316 return err; 1317} 1318 1319static void 1320mlxsw_linecard_event_delivery_fini(struct mlxsw_core *mlxsw_core, 1321 struct mlxsw_linecards *linecards, 1322 u8 slot_index) 1323{ 1324 struct mlxsw_linecard *linecard; 1325 1326 linecard = mlxsw_linecard_get(linecards, slot_index); 1327 mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false); 1328} 1329 1330/* LINECARDS INI BUNDLE FILE 1331 * +----------------------------------+ 1332 * | MAGIC ("NVLCINI+") | 1333 * +----------------------------------+ +--------------------+ 1334 * | INI 0 +---> | __le16 size | 1335 * +----------------------------------+ | __be16 hw_revision | 1336 * | INI 1 | | __be16 ini_version | 1337 * +----------------------------------+ | u8 __dontcare[3] | 1338 * | ... | | u8 type | 1339 * +----------------------------------+ | u8 name[20] | 1340 * | INI N | | ... | 1341 * +----------------------------------+ +--------------------+ 1342 */ 1343 1344#define MLXSW_LINECARDS_INI_BUNDLE_MAGIC "NVLCINI+" 1345 1346static int 1347mlxsw_linecard_types_file_validate(struct mlxsw_linecards *linecards, 1348 struct mlxsw_linecard_types_info *types_info) 1349{ 1350 size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC); 1351 struct mlxsw_linecard_ini_file *ini_file; 1352 size_t size = types_info->data_size; 1353 const u8 *data = types_info->data; 1354 unsigned int count = 0; 1355 u16 ini_file_size; 1356 1357 if (size < magic_size) { 1358 dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file size, smaller than magic size\n"); 1359 return -EINVAL; 1360 } 1361 if (memcmp(data, MLXSW_LINECARDS_INI_BUNDLE_MAGIC, magic_size)) { 1362 dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file magic pattern\n"); 1363 return -EINVAL; 1364 } 1365 1366 data += magic_size; 1367 size -= magic_size; 1368 1369 while (size > 0) { 1370 if (size < sizeof(*ini_file)) { 1371 dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI which is smaller than bare minimum\n"); 1372 return -EINVAL; 1373 } 1374 ini_file = (struct mlxsw_linecard_ini_file *) data; 1375 ini_file_size = le16_to_cpu(ini_file->size); 1376 if (ini_file_size + sizeof(__le16) > size) { 1377 dev_warn(linecards->bus_info->dev, "Linecards INIs file appears to be truncated\n"); 1378 return -EINVAL; 1379 } 1380 if (ini_file_size % 4) { 1381 dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI with invalid size\n"); 1382 return -EINVAL; 1383 } 1384 data += ini_file_size + sizeof(__le16); 1385 size -= ini_file_size + sizeof(__le16); 1386 count++; 1387 } 1388 if (!count) { 1389 dev_warn(linecards->bus_info->dev, "Linecards INIs file does not contain any INI\n"); 1390 return -EINVAL; 1391 } 1392 types_info->count = count; 1393 return 0; 1394} 1395 1396static void 1397mlxsw_linecard_types_file_parse(struct mlxsw_linecard_types_info *types_info) 1398{ 1399 size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC); 1400 size_t size = types_info->data_size - magic_size; 1401 const u8 *data = types_info->data + magic_size; 1402 struct mlxsw_linecard_ini_file *ini_file; 1403 unsigned int count = 0; 1404 u16 ini_file_size; 1405 int i; 1406 1407 while (size) { 1408 ini_file = (struct mlxsw_linecard_ini_file *) data; 1409 ini_file_size = le16_to_cpu(ini_file->size); 1410 for (i = 0; i < ini_file_size / 4; i++) { 1411 u32 *val = &((u32 *) ini_file->data)[i]; 1412 1413 *val = swab32(*val); 1414 } 1415 types_info->ini_files[count] = ini_file; 1416 data += ini_file_size + sizeof(__le16); 1417 size -= ini_file_size + sizeof(__le16); 1418 count++; 1419 } 1420} 1421 1422#define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT \ 1423 "mellanox/lc_ini_bundle_%u_%u.bin" 1424#define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN \ 1425 (sizeof(MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT) + 4) 1426 1427static int mlxsw_linecard_types_init(struct mlxsw_core *mlxsw_core, 1428 struct mlxsw_linecards *linecards) 1429{ 1430 const struct mlxsw_fw_rev *rev = &linecards->bus_info->fw_rev; 1431 char filename[MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN]; 1432 struct mlxsw_linecard_types_info *types_info; 1433 const struct firmware *firmware; 1434 int err; 1435 1436 err = snprintf(filename, sizeof(filename), 1437 MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT, 1438 rev->minor, rev->subminor); 1439 WARN_ON(err >= sizeof(filename)); 1440 1441 err = request_firmware_direct(&firmware, filename, 1442 linecards->bus_info->dev); 1443 if (err) { 1444 dev_warn(linecards->bus_info->dev, "Could not request linecards INI file \"%s\", provisioning will not be possible\n", 1445 filename); 1446 return 0; 1447 } 1448 1449 types_info = kzalloc(sizeof(*types_info), GFP_KERNEL); 1450 if (!types_info) { 1451 release_firmware(firmware); 1452 return -ENOMEM; 1453 } 1454 linecards->types_info = types_info; 1455 1456 types_info->data_size = firmware->size; 1457 types_info->data = vmalloc(types_info->data_size); 1458 if (!types_info->data) { 1459 err = -ENOMEM; 1460 release_firmware(firmware); 1461 goto err_data_alloc; 1462 } 1463 memcpy(types_info->data, firmware->data, types_info->data_size); 1464 release_firmware(firmware); 1465 1466 err = mlxsw_linecard_types_file_validate(linecards, types_info); 1467 if (err) { 1468 err = 0; 1469 goto err_type_file_file_validate; 1470 } 1471 1472 types_info->ini_files = kmalloc_array(types_info->count, 1473 sizeof(struct mlxsw_linecard_ini_file *), 1474 GFP_KERNEL); 1475 if (!types_info->ini_files) { 1476 err = -ENOMEM; 1477 goto err_ini_files_alloc; 1478 } 1479 1480 mlxsw_linecard_types_file_parse(types_info); 1481 1482 return 0; 1483 1484err_ini_files_alloc: 1485err_type_file_file_validate: 1486 vfree(types_info->data); 1487err_data_alloc: 1488 kfree(types_info); 1489 linecards->types_info = NULL; 1490 return err; 1491} 1492 1493static void mlxsw_linecard_types_fini(struct mlxsw_linecards *linecards) 1494{ 1495 struct mlxsw_linecard_types_info *types_info = linecards->types_info; 1496 1497 if (!types_info) 1498 return; 1499 kfree(types_info->ini_files); 1500 vfree(types_info->data); 1501 kfree(types_info); 1502} 1503 1504int mlxsw_linecards_init(struct mlxsw_core *mlxsw_core, 1505 const struct mlxsw_bus_info *bus_info) 1506{ 1507 char mgpir_pl[MLXSW_REG_MGPIR_LEN]; 1508 struct mlxsw_linecards *linecards; 1509 u8 slot_count; 1510 int err; 1511 int i; 1512 1513 mlxsw_reg_mgpir_pack(mgpir_pl, 0); 1514 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl); 1515 if (err) 1516 return err; 1517 1518 mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL, 1519 NULL, &slot_count); 1520 if (!slot_count) 1521 return 0; 1522 1523 linecards = vzalloc(struct_size(linecards, linecards, slot_count)); 1524 if (!linecards) 1525 return -ENOMEM; 1526 linecards->count = slot_count; 1527 linecards->mlxsw_core = mlxsw_core; 1528 linecards->bus_info = bus_info; 1529 INIT_LIST_HEAD(&linecards->event_ops_list); 1530 mutex_init(&linecards->event_ops_list_lock); 1531 1532 err = mlxsw_linecard_types_init(mlxsw_core, linecards); 1533 if (err) 1534 goto err_types_init; 1535 1536 err = mlxsw_core_traps_register(mlxsw_core, mlxsw_linecard_listener, 1537 ARRAY_SIZE(mlxsw_linecard_listener), 1538 mlxsw_core); 1539 if (err) 1540 goto err_traps_register; 1541 1542 err = mlxsw_core_irq_event_handler_register(mlxsw_core, 1543 mlxsw_linecards_irq_event_handler); 1544 if (err) 1545 goto err_irq_event_handler_register; 1546 1547 mlxsw_core_linecards_set(mlxsw_core, linecards); 1548 1549 for (i = 0; i < linecards->count; i++) { 1550 err = mlxsw_linecard_init(mlxsw_core, linecards, i + 1); 1551 if (err) 1552 goto err_linecard_init; 1553 } 1554 1555 for (i = 0; i < linecards->count; i++) { 1556 err = mlxsw_linecard_event_delivery_init(mlxsw_core, linecards, 1557 i + 1); 1558 if (err) 1559 goto err_linecard_event_delivery_init; 1560 } 1561 1562 return 0; 1563 1564err_linecard_event_delivery_init: 1565 for (i--; i >= 0; i--) 1566 mlxsw_linecard_event_delivery_fini(mlxsw_core, linecards, i + 1); 1567 i = linecards->count; 1568err_linecard_init: 1569 for (i--; i >= 0; i--) 1570 mlxsw_linecard_fini(mlxsw_core, linecards, i + 1); 1571 mlxsw_core_irq_event_handler_unregister(mlxsw_core, 1572 mlxsw_linecards_irq_event_handler); 1573err_irq_event_handler_register: 1574 mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener, 1575 ARRAY_SIZE(mlxsw_linecard_listener), 1576 mlxsw_core); 1577err_traps_register: 1578 mlxsw_linecard_types_fini(linecards); 1579err_types_init: 1580 vfree(linecards); 1581 return err; 1582} 1583 1584void mlxsw_linecards_fini(struct mlxsw_core *mlxsw_core) 1585{ 1586 struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core); 1587 int i; 1588 1589 if (!linecards) 1590 return; 1591 for (i = 0; i < linecards->count; i++) 1592 mlxsw_linecard_event_delivery_fini(mlxsw_core, linecards, i + 1); 1593 for (i = 0; i < linecards->count; i++) 1594 mlxsw_linecard_fini(mlxsw_core, linecards, i + 1); 1595 mlxsw_core_irq_event_handler_unregister(mlxsw_core, 1596 mlxsw_linecards_irq_event_handler); 1597 mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener, 1598 ARRAY_SIZE(mlxsw_linecard_listener), 1599 mlxsw_core); 1600 mlxsw_linecard_types_fini(linecards); 1601 mutex_destroy(&linecards->event_ops_list_lock); 1602 WARN_ON(!list_empty(&linecards->event_ops_list)); 1603 vfree(linecards); 1604}
~/
Read-only file preview.