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 18 struct 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 32 struct 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 41 static void 42 mlxsw_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 51 static void 52 mlxsw_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 59 static const char * 60 mlxsw_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 77 static 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 91 struct mlxsw_linecard_device_fw_info { 92 struct mlxfw_dev mlxfw_dev; 93 struct mlxsw_core *mlxsw_core; 94 struct mlxsw_linecard *linecard; 95 }; 96 97 static 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 130 static 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 165 static int 166 mlxsw_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 188 static int 189 mlxsw_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 209 static int 210 mlxsw_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 230 static 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 250 static int 251 mlxsw_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 283 static 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 303 static 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 324 static 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 336 int 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); 362 unlock: 363 mutex_unlock(&linecard->lock); 364 return err; 365 } 366 367 static 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 388 static 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 436 static 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 444 struct mlxsw_linecards_event_ops_item { 445 struct list_head list; 446 const struct mlxsw_linecards_event_ops *event_ops; 447 void *priv; 448 }; 449 450 static void 451 mlxsw_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 461 static void 462 mlxsw_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 475 static void 476 mlxsw_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 489 static void 490 mlxsw_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 507 static void 508 mlxsw_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 525 int 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 } 546 EXPORT_SYMBOL(mlxsw_linecards_event_ops_register); 547 548 void 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 } 573 EXPORT_SYMBOL(mlxsw_linecards_event_ops_unregister); 574 575 int 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 605 sprintf(buf, "%u.%u.%u", info->fw_major, info->fw_minor, 606 info->fw_sub_minor); 607 err = devlink_info_version_running_put(req, 608 DEVLINK_INFO_VERSION_GENERIC_FW, 609 buf); 610 if (err) 611 goto unlock; 612 } 613 614 unlock: 615 mutex_unlock(&linecard->lock); 616 return err; 617 } 618 619 static int 620 mlxsw_linecard_provision_set(struct mlxsw_linecard *linecard, u8 card_type, 621 u16 hw_revision, u16 ini_version) 622 { 623 struct mlxsw_linecards *linecards = linecard->linecards; 624 const char *type; 625 int err; 626 627 type = mlxsw_linecard_types_lookup(linecards, card_type); 628 mlxsw_linecard_status_event_done(linecard, 629 MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION); 630 if (!type) { 631 /* It is possible for a line card to be provisioned before 632 * driver initialization. Due to a missing INI bundle file 633 * or an outdated one, the queried card's type might not 634 * be recognized by the driver. In this case, try to query 635 * the card's name from the device. 636 */ 637 type = mlxsw_linecard_type_name(linecard); 638 if (IS_ERR(type)) { 639 mlxsw_linecard_provision_fail(linecard); 640 return PTR_ERR(type); 641 } 642 } 643 linecard->provisioned = true; 644 linecard->hw_revision = hw_revision; 645 linecard->ini_version = ini_version; 646 647 err = mlxsw_linecard_bdev_add(linecard); 648 if (err) { 649 linecard->provisioned = false; 650 mlxsw_linecard_provision_fail(linecard); 651 return err; 652 } 653 654 devlink_linecard_provision_set(linecard->devlink_linecard, type); 655 return 0; 656 } 657 658 static void mlxsw_linecard_provision_clear(struct mlxsw_linecard *linecard) 659 { 660 mlxsw_linecard_status_event_done(linecard, 661 MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION); 662 mlxsw_linecard_bdev_del(linecard); 663 linecard->provisioned = false; 664 devlink_linecard_provision_clear(linecard->devlink_linecard); 665 } 666 667 static int mlxsw_linecard_ready_set(struct mlxsw_linecard *linecard) 668 { 669 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core; 670 char mddc_pl[MLXSW_REG_MDDC_LEN]; 671 int err; 672 673 err = mlxsw_linecard_device_info_update(linecard); 674 if (err) 675 return err; 676 677 mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, false, true); 678 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl); 679 if (err) 680 return err; 681 linecard->ready = true; 682 return 0; 683 } 684 685 static int mlxsw_linecard_ready_clear(struct mlxsw_linecard *linecard) 686 { 687 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core; 688 char mddc_pl[MLXSW_REG_MDDC_LEN]; 689 int err; 690 691 mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, false, false); 692 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl); 693 if (err) 694 return err; 695 linecard->ready = false; 696 return 0; 697 } 698 699 static void mlxsw_linecard_active_set(struct mlxsw_linecard *linecard) 700 { 701 mlxsw_linecard_active_ops_call(linecard); 702 linecard->active = true; 703 devlink_linecard_activate(linecard->devlink_linecard); 704 } 705 706 static void mlxsw_linecard_active_clear(struct mlxsw_linecard *linecard) 707 { 708 mlxsw_linecard_inactive_ops_call(linecard); 709 linecard->active = false; 710 devlink_linecard_deactivate(linecard->devlink_linecard); 711 } 712 713 static int mlxsw_linecard_status_process(struct mlxsw_linecards *linecards, 714 struct mlxsw_linecard *linecard, 715 const char *mddq_pl) 716 { 717 enum mlxsw_reg_mddq_slot_info_ready ready; 718 bool provisioned, sr_valid, active; 719 u16 ini_version, hw_revision; 720 u8 slot_index, card_type; 721 int err = 0; 722 723 mlxsw_reg_mddq_slot_info_unpack(mddq_pl, &slot_index, &provisioned, 724 &sr_valid, &ready, &active, 725 &hw_revision, &ini_version, 726 &card_type); 727 728 if (linecard) { 729 if (WARN_ON(slot_index != linecard->slot_index)) 730 return -EINVAL; 731 } else { 732 if (WARN_ON(slot_index > linecards->count)) 733 return -EINVAL; 734 linecard = mlxsw_linecard_get(linecards, slot_index); 735 } 736 737 mutex_lock(&linecard->lock); 738 739 if (provisioned && linecard->provisioned != provisioned) { 740 err = mlxsw_linecard_provision_set(linecard, card_type, 741 hw_revision, ini_version); 742 if (err) 743 goto out; 744 } 745 746 if (ready == MLXSW_REG_MDDQ_SLOT_INFO_READY_READY && !linecard->ready) { 747 err = mlxsw_linecard_ready_set(linecard); 748 if (err) 749 goto out; 750 } 751 752 if (active && linecard->active != active) 753 mlxsw_linecard_active_set(linecard); 754 755 if (!active && linecard->active != active) 756 mlxsw_linecard_active_clear(linecard); 757 758 if (ready != MLXSW_REG_MDDQ_SLOT_INFO_READY_READY && 759 linecard->ready) { 760 err = mlxsw_linecard_ready_clear(linecard); 761 if (err) 762 goto out; 763 } 764 765 if (!provisioned && linecard->provisioned != provisioned) 766 mlxsw_linecard_provision_clear(linecard); 767 768 out: 769 mutex_unlock(&linecard->lock); 770 return err; 771 } 772 773 static int mlxsw_linecard_status_get_and_process(struct mlxsw_core *mlxsw_core, 774 struct mlxsw_linecards *linecards, 775 struct mlxsw_linecard *linecard) 776 { 777 char mddq_pl[MLXSW_REG_MDDQ_LEN]; 778 int err; 779 780 mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, false); 781 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl); 782 if (err) 783 return err; 784 785 return mlxsw_linecard_status_process(linecards, linecard, mddq_pl); 786 } 787 788 static const char * const mlxsw_linecard_status_event_type_name[] = { 789 [MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION] = "provision", 790 [MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION] = "unprovision", 791 }; 792 793 static void mlxsw_linecard_status_event_to_work(struct work_struct *work) 794 { 795 struct mlxsw_linecard *linecard = 796 container_of(work, struct mlxsw_linecard, 797 status_event_to_dw.work); 798 799 mutex_lock(&linecard->lock); 800 dev_err(linecard->linecards->bus_info->dev, "linecard %u: Timeout reached waiting on %s status event", 801 linecard->slot_index, 802 mlxsw_linecard_status_event_type_name[linecard->status_event_type_to]); 803 mlxsw_linecard_provision_fail(linecard); 804 mutex_unlock(&linecard->lock); 805 } 806 807 static int __mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard) 808 { 809 dev_info(linecard->linecards->bus_info->dev, "linecard %u: Clearing FSM state error", 810 linecard->slot_index); 811 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index, 812 MLXSW_REG_MBCT_OP_CLEAR_ERRORS, false); 813 return mlxsw_reg_write(linecard->linecards->mlxsw_core, 814 MLXSW_REG(mbct), linecard->mbct_pl); 815 } 816 817 static int mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard, 818 enum mlxsw_reg_mbct_fsm_state fsm_state) 819 { 820 if (fsm_state != MLXSW_REG_MBCT_FSM_STATE_ERROR) 821 return 0; 822 return __mlxsw_linecard_fix_fsm_state(linecard); 823 } 824 825 static int 826 mlxsw_linecard_query_ini_status(struct mlxsw_linecard *linecard, 827 enum mlxsw_reg_mbct_status *status, 828 enum mlxsw_reg_mbct_fsm_state *fsm_state, 829 struct netlink_ext_ack *extack) 830 { 831 int err; 832 833 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index, 834 MLXSW_REG_MBCT_OP_QUERY_STATUS, false); 835 err = mlxsw_reg_query(linecard->linecards->mlxsw_core, MLXSW_REG(mbct), 836 linecard->mbct_pl); 837 if (err) { 838 NL_SET_ERR_MSG_MOD(extack, "Failed to query linecard INI status"); 839 return err; 840 } 841 mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, status, fsm_state); 842 return err; 843 } 844 845 static int 846 mlxsw_linecard_ini_transfer(struct mlxsw_core *mlxsw_core, 847 struct mlxsw_linecard *linecard, 848 const struct mlxsw_linecard_ini_file *ini_file, 849 struct netlink_ext_ack *extack) 850 { 851 enum mlxsw_reg_mbct_fsm_state fsm_state; 852 enum mlxsw_reg_mbct_status status; 853 size_t size_left; 854 const u8 *data; 855 int err; 856 857 size_left = le16_to_cpu(ini_file->size); 858 data = ini_file->data; 859 while (size_left) { 860 size_t data_size = MLXSW_REG_MBCT_DATA_LEN; 861 bool is_last = false; 862 863 if (size_left <= MLXSW_REG_MBCT_DATA_LEN) { 864 data_size = size_left; 865 is_last = true; 866 } 867 868 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index, 869 MLXSW_REG_MBCT_OP_DATA_TRANSFER, false); 870 mlxsw_reg_mbct_dt_pack(linecard->mbct_pl, data_size, 871 is_last, data); 872 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct), 873 linecard->mbct_pl); 874 if (err) { 875 NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI data transfer"); 876 return err; 877 } 878 mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, 879 &status, &fsm_state); 880 if ((!is_last && status != MLXSW_REG_MBCT_STATUS_PART_DATA) || 881 (is_last && status != MLXSW_REG_MBCT_STATUS_LAST_DATA)) { 882 NL_SET_ERR_MSG_MOD(extack, "Failed to transfer linecard INI data"); 883 mlxsw_linecard_fix_fsm_state(linecard, fsm_state); 884 return -EINVAL; 885 } 886 size_left -= data_size; 887 data += data_size; 888 } 889 890 return 0; 891 } 892 893 static int 894 mlxsw_linecard_ini_erase(struct mlxsw_core *mlxsw_core, 895 struct mlxsw_linecard *linecard, 896 struct netlink_ext_ack *extack) 897 { 898 enum mlxsw_reg_mbct_fsm_state fsm_state; 899 enum mlxsw_reg_mbct_status status; 900 int err; 901 902 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index, 903 MLXSW_REG_MBCT_OP_ERASE_INI_IMAGE, false); 904 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct), 905 linecard->mbct_pl); 906 if (err) { 907 NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI erase"); 908 return err; 909 } 910 mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state); 911 switch (status) { 912 case MLXSW_REG_MBCT_STATUS_ERASE_COMPLETE: 913 break; 914 default: 915 /* Should not happen */ 916 fallthrough; 917 case MLXSW_REG_MBCT_STATUS_ERASE_FAILED: 918 NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI"); 919 goto fix_fsm_err_out; 920 case MLXSW_REG_MBCT_STATUS_ERROR_INI_IN_USE: 921 NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI while being used"); 922 goto fix_fsm_err_out; 923 } 924 return 0; 925 926 fix_fsm_err_out: 927 mlxsw_linecard_fix_fsm_state(linecard, fsm_state); 928 return -EINVAL; 929 } 930 931 static void mlxsw_linecard_bct_process(struct mlxsw_core *mlxsw_core, 932 const char *mbct_pl) 933 { 934 struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core); 935 enum mlxsw_reg_mbct_fsm_state fsm_state; 936 enum mlxsw_reg_mbct_status status; 937 struct mlxsw_linecard *linecard; 938 u8 slot_index; 939 940 mlxsw_reg_mbct_unpack(mbct_pl, &slot_index, &status, &fsm_state); 941 if (WARN_ON(slot_index > linecards->count)) 942 return; 943 linecard = mlxsw_linecard_get(linecards, slot_index); 944 mutex_lock(&linecard->lock); 945 if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) { 946 dev_err(linecards->bus_info->dev, "linecard %u: Failed to activate INI", 947 linecard->slot_index); 948 goto fix_fsm_out; 949 } 950 mutex_unlock(&linecard->lock); 951 return; 952 953 fix_fsm_out: 954 mlxsw_linecard_fix_fsm_state(linecard, fsm_state); 955 mlxsw_linecard_provision_fail(linecard); 956 mutex_unlock(&linecard->lock); 957 } 958 959 static int 960 mlxsw_linecard_ini_activate(struct mlxsw_core *mlxsw_core, 961 struct mlxsw_linecard *linecard, 962 struct netlink_ext_ack *extack) 963 { 964 enum mlxsw_reg_mbct_fsm_state fsm_state; 965 enum mlxsw_reg_mbct_status status; 966 int err; 967 968 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index, 969 MLXSW_REG_MBCT_OP_ACTIVATE, true); 970 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct), linecard->mbct_pl); 971 if (err) { 972 NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI activation"); 973 return err; 974 } 975 mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state); 976 if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) { 977 NL_SET_ERR_MSG_MOD(extack, "Failed to activate linecard INI"); 978 goto fix_fsm_err_out; 979 } 980 981 return 0; 982 983 fix_fsm_err_out: 984 mlxsw_linecard_fix_fsm_state(linecard, fsm_state); 985 return -EINVAL; 986 } 987 988 #define MLXSW_LINECARD_INI_WAIT_RETRIES 10 989 #define MLXSW_LINECARD_INI_WAIT_MS 500 990 991 static int 992 mlxsw_linecard_ini_in_use_wait(struct mlxsw_core *mlxsw_core, 993 struct mlxsw_linecard *linecard, 994 struct netlink_ext_ack *extack) 995 { 996 enum mlxsw_reg_mbct_fsm_state fsm_state; 997 enum mlxsw_reg_mbct_status status; 998 unsigned int ini_wait_retries = 0; 999 int err; 1000 1001 query_ini_status: 1002 err = mlxsw_linecard_query_ini_status(linecard, &status, 1003 &fsm_state, extack); 1004 if (err) 1005 return err; 1006 1007 switch (fsm_state) { 1008 case MLXSW_REG_MBCT_FSM_STATE_INI_IN_USE: 1009 if (ini_wait_retries++ > MLXSW_LINECARD_INI_WAIT_RETRIES) { 1010 NL_SET_ERR_MSG_MOD(extack, "Failed to wait for linecard INI to be unused"); 1011 return -EINVAL; 1012 } 1013 mdelay(MLXSW_LINECARD_INI_WAIT_MS); 1014 goto query_ini_status; 1015 default: 1016 break; 1017 } 1018 return 0; 1019 } 1020 1021 static bool mlxsw_linecard_port_selector(void *priv, u16 local_port) 1022 { 1023 struct mlxsw_linecard *linecard = priv; 1024 struct mlxsw_core *mlxsw_core; 1025 1026 mlxsw_core = linecard->linecards->mlxsw_core; 1027 return linecard == mlxsw_core_port_linecard_get(mlxsw_core, local_port); 1028 } 1029 1030 static int mlxsw_linecard_provision(struct devlink_linecard *devlink_linecard, 1031 void *priv, const char *type, 1032 const void *type_priv, 1033 struct netlink_ext_ack *extack) 1034 { 1035 const struct mlxsw_linecard_ini_file *ini_file = type_priv; 1036 struct mlxsw_linecard *linecard = priv; 1037 struct mlxsw_core *mlxsw_core; 1038 int err; 1039 1040 mutex_lock(&linecard->lock); 1041 1042 mlxsw_core = linecard->linecards->mlxsw_core; 1043 1044 err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack); 1045 if (err) 1046 goto err_out; 1047 1048 err = mlxsw_linecard_ini_transfer(mlxsw_core, linecard, 1049 ini_file, extack); 1050 if (err) 1051 goto err_out; 1052 1053 mlxsw_linecard_status_event_to_schedule(linecard, 1054 MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION); 1055 err = mlxsw_linecard_ini_activate(mlxsw_core, linecard, extack); 1056 if (err) 1057 goto err_out; 1058 1059 goto out; 1060 1061 err_out: 1062 mlxsw_linecard_provision_fail(linecard); 1063 out: 1064 mutex_unlock(&linecard->lock); 1065 return err; 1066 } 1067 1068 static int mlxsw_linecard_unprovision(struct devlink_linecard *devlink_linecard, 1069 void *priv, 1070 struct netlink_ext_ack *extack) 1071 { 1072 struct mlxsw_linecard *linecard = priv; 1073 struct mlxsw_core *mlxsw_core; 1074 int err; 1075 1076 mutex_lock(&linecard->lock); 1077 1078 mlxsw_core = linecard->linecards->mlxsw_core; 1079 1080 mlxsw_core_ports_remove_selected(mlxsw_core, 1081 mlxsw_linecard_port_selector, 1082 linecard); 1083 1084 err = mlxsw_linecard_ini_in_use_wait(mlxsw_core, linecard, extack); 1085 if (err) 1086 goto err_out; 1087 1088 mlxsw_linecard_status_event_to_schedule(linecard, 1089 MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION); 1090 err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack); 1091 if (err) 1092 goto err_out; 1093 1094 goto out; 1095 1096 err_out: 1097 mlxsw_linecard_provision_fail(linecard); 1098 out: 1099 mutex_unlock(&linecard->lock); 1100 return err; 1101 } 1102 1103 static bool mlxsw_linecard_same_provision(struct devlink_linecard *devlink_linecard, 1104 void *priv, const char *type, 1105 const void *type_priv) 1106 { 1107 const struct mlxsw_linecard_ini_file *ini_file = type_priv; 1108 struct mlxsw_linecard *linecard = priv; 1109 bool ret; 1110 1111 mutex_lock(&linecard->lock); 1112 ret = linecard->hw_revision == be16_to_cpu(ini_file->format.hw_revision) && 1113 linecard->ini_version == be16_to_cpu(ini_file->format.ini_version); 1114 mutex_unlock(&linecard->lock); 1115 return ret; 1116 } 1117 1118 static unsigned int 1119 mlxsw_linecard_types_count(struct devlink_linecard *devlink_linecard, 1120 void *priv) 1121 { 1122 struct mlxsw_linecard *linecard = priv; 1123 1124 return linecard->linecards->types_info ? 1125 linecard->linecards->types_info->count : 0; 1126 } 1127 1128 static void mlxsw_linecard_types_get(struct devlink_linecard *devlink_linecard, 1129 void *priv, unsigned int index, 1130 const char **type, const void **type_priv) 1131 { 1132 struct mlxsw_linecard_types_info *types_info; 1133 struct mlxsw_linecard_ini_file *ini_file; 1134 struct mlxsw_linecard *linecard = priv; 1135 1136 types_info = linecard->linecards->types_info; 1137 if (WARN_ON_ONCE(!types_info)) 1138 return; 1139 ini_file = types_info->ini_files[index]; 1140 *type = ini_file->format.name; 1141 *type_priv = ini_file; 1142 } 1143 1144 static const struct devlink_linecard_ops mlxsw_linecard_ops = { 1145 .provision = mlxsw_linecard_provision, 1146 .unprovision = mlxsw_linecard_unprovision, 1147 .same_provision = mlxsw_linecard_same_provision, 1148 .types_count = mlxsw_linecard_types_count, 1149 .types_get = mlxsw_linecard_types_get, 1150 }; 1151 1152 struct mlxsw_linecard_status_event { 1153 struct mlxsw_core *mlxsw_core; 1154 char mddq_pl[MLXSW_REG_MDDQ_LEN]; 1155 struct work_struct work; 1156 }; 1157 1158 static void mlxsw_linecard_status_event_work(struct work_struct *work) 1159 { 1160 struct mlxsw_linecard_status_event *event; 1161 struct mlxsw_linecards *linecards; 1162 struct mlxsw_core *mlxsw_core; 1163 1164 event = container_of(work, struct mlxsw_linecard_status_event, work); 1165 mlxsw_core = event->mlxsw_core; 1166 linecards = mlxsw_core_linecards(mlxsw_core); 1167 mlxsw_linecard_status_process(linecards, NULL, event->mddq_pl); 1168 kfree(event); 1169 } 1170 1171 static void 1172 mlxsw_linecard_status_listener_func(const struct mlxsw_reg_info *reg, 1173 char *mddq_pl, void *priv) 1174 { 1175 struct mlxsw_linecard_status_event *event; 1176 struct mlxsw_core *mlxsw_core = priv; 1177 1178 event = kmalloc(sizeof(*event), GFP_ATOMIC); 1179 if (!event) 1180 return; 1181 event->mlxsw_core = mlxsw_core; 1182 memcpy(event->mddq_pl, mddq_pl, sizeof(event->mddq_pl)); 1183 INIT_WORK(&event->work, mlxsw_linecard_status_event_work); 1184 mlxsw_core_schedule_work(&event->work); 1185 } 1186 1187 struct mlxsw_linecard_bct_event { 1188 struct mlxsw_core *mlxsw_core; 1189 char mbct_pl[MLXSW_REG_MBCT_LEN]; 1190 struct work_struct work; 1191 }; 1192 1193 static void mlxsw_linecard_bct_event_work(struct work_struct *work) 1194 { 1195 struct mlxsw_linecard_bct_event *event; 1196 struct mlxsw_core *mlxsw_core; 1197 1198 event = container_of(work, struct mlxsw_linecard_bct_event, work); 1199 mlxsw_core = event->mlxsw_core; 1200 mlxsw_linecard_bct_process(mlxsw_core, event->mbct_pl); 1201 kfree(event); 1202 } 1203 1204 static void 1205 mlxsw_linecard_bct_listener_func(const struct mlxsw_reg_info *reg, 1206 char *mbct_pl, void *priv) 1207 { 1208 struct mlxsw_linecard_bct_event *event; 1209 struct mlxsw_core *mlxsw_core = priv; 1210 1211 event = kmalloc(sizeof(*event), GFP_ATOMIC); 1212 if (!event) 1213 return; 1214 event->mlxsw_core = mlxsw_core; 1215 memcpy(event->mbct_pl, mbct_pl, sizeof(event->mbct_pl)); 1216 INIT_WORK(&event->work, mlxsw_linecard_bct_event_work); 1217 mlxsw_core_schedule_work(&event->work); 1218 } 1219 1220 static const struct mlxsw_listener mlxsw_linecard_listener[] = { 1221 MLXSW_CORE_EVENTL(mlxsw_linecard_status_listener_func, DSDSC), 1222 MLXSW_CORE_EVENTL(mlxsw_linecard_bct_listener_func, BCTOE), 1223 }; 1224 1225 static int mlxsw_linecard_event_delivery_set(struct mlxsw_core *mlxsw_core, 1226 struct mlxsw_linecard *linecard, 1227 bool enable) 1228 { 1229 char mddq_pl[MLXSW_REG_MDDQ_LEN]; 1230 1231 mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, enable); 1232 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddq), mddq_pl); 1233 } 1234 1235 static int mlxsw_linecard_init(struct mlxsw_core *mlxsw_core, 1236 struct mlxsw_linecards *linecards, 1237 u8 slot_index) 1238 { 1239 struct devlink_linecard *devlink_linecard; 1240 struct mlxsw_linecard *linecard; 1241 int err; 1242 1243 linecard = mlxsw_linecard_get(linecards, slot_index); 1244 linecard->slot_index = slot_index; 1245 linecard->linecards = linecards; 1246 mutex_init(&linecard->lock); 1247 1248 devlink_linecard = devlink_linecard_create(priv_to_devlink(mlxsw_core), 1249 slot_index, &mlxsw_linecard_ops, 1250 linecard); 1251 if (IS_ERR(devlink_linecard)) { 1252 err = PTR_ERR(devlink_linecard); 1253 goto err_devlink_linecard_create; 1254 } 1255 linecard->devlink_linecard = devlink_linecard; 1256 INIT_DELAYED_WORK(&linecard->status_event_to_dw, 1257 &mlxsw_linecard_status_event_to_work); 1258 1259 err = mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, true); 1260 if (err) 1261 goto err_event_delivery_set; 1262 1263 err = mlxsw_linecard_status_get_and_process(mlxsw_core, linecards, 1264 linecard); 1265 if (err) 1266 goto err_status_get_and_process; 1267 1268 return 0; 1269 1270 err_status_get_and_process: 1271 mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false); 1272 err_event_delivery_set: 1273 devlink_linecard_destroy(linecard->devlink_linecard); 1274 err_devlink_linecard_create: 1275 mutex_destroy(&linecard->lock); 1276 return err; 1277 } 1278 1279 static void mlxsw_linecard_fini(struct mlxsw_core *mlxsw_core, 1280 struct mlxsw_linecards *linecards, 1281 u8 slot_index) 1282 { 1283 struct mlxsw_linecard *linecard; 1284 1285 linecard = mlxsw_linecard_get(linecards, slot_index); 1286 mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false); 1287 cancel_delayed_work_sync(&linecard->status_event_to_dw); 1288 /* Make sure all scheduled events are processed */ 1289 mlxsw_core_flush_owq(); 1290 if (linecard->active) 1291 mlxsw_linecard_active_clear(linecard); 1292 mlxsw_linecard_bdev_del(linecard); 1293 devlink_linecard_destroy(linecard->devlink_linecard); 1294 mutex_destroy(&linecard->lock); 1295 } 1296 1297 /* LINECARDS INI BUNDLE FILE 1298 * +----------------------------------+ 1299 * | MAGIC ("NVLCINI+") | 1300 * +----------------------------------+ +--------------------+ 1301 * | INI 0 +---> | __le16 size | 1302 * +----------------------------------+ | __be16 hw_revision | 1303 * | INI 1 | | __be16 ini_version | 1304 * +----------------------------------+ | u8 __dontcare[3] | 1305 * | ... | | u8 type | 1306 * +----------------------------------+ | u8 name[20] | 1307 * | INI N | | ... | 1308 * +----------------------------------+ +--------------------+ 1309 */ 1310 1311 #define MLXSW_LINECARDS_INI_BUNDLE_MAGIC "NVLCINI+" 1312 1313 static int 1314 mlxsw_linecard_types_file_validate(struct mlxsw_linecards *linecards, 1315 struct mlxsw_linecard_types_info *types_info) 1316 { 1317 size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC); 1318 struct mlxsw_linecard_ini_file *ini_file; 1319 size_t size = types_info->data_size; 1320 const u8 *data = types_info->data; 1321 unsigned int count = 0; 1322 u16 ini_file_size; 1323 1324 if (size < magic_size) { 1325 dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file size, smaller than magic size\n"); 1326 return -EINVAL; 1327 } 1328 if (memcmp(data, MLXSW_LINECARDS_INI_BUNDLE_MAGIC, magic_size)) { 1329 dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file magic pattern\n"); 1330 return -EINVAL; 1331 } 1332 1333 data += magic_size; 1334 size -= magic_size; 1335 1336 while (size > 0) { 1337 if (size < sizeof(*ini_file)) { 1338 dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI which is smaller than bare minimum\n"); 1339 return -EINVAL; 1340 } 1341 ini_file = (struct mlxsw_linecard_ini_file *) data; 1342 ini_file_size = le16_to_cpu(ini_file->size); 1343 if (ini_file_size + sizeof(__le16) > size) { 1344 dev_warn(linecards->bus_info->dev, "Linecards INIs file appears to be truncated\n"); 1345 return -EINVAL; 1346 } 1347 if (ini_file_size % 4) { 1348 dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI with invalid size\n"); 1349 return -EINVAL; 1350 } 1351 data += ini_file_size + sizeof(__le16); 1352 size -= ini_file_size + sizeof(__le16); 1353 count++; 1354 } 1355 if (!count) { 1356 dev_warn(linecards->bus_info->dev, "Linecards INIs file does not contain any INI\n"); 1357 return -EINVAL; 1358 } 1359 types_info->count = count; 1360 return 0; 1361 } 1362 1363 static void 1364 mlxsw_linecard_types_file_parse(struct mlxsw_linecard_types_info *types_info) 1365 { 1366 size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC); 1367 size_t size = types_info->data_size - magic_size; 1368 const u8 *data = types_info->data + magic_size; 1369 struct mlxsw_linecard_ini_file *ini_file; 1370 unsigned int count = 0; 1371 u16 ini_file_size; 1372 int i; 1373 1374 while (size) { 1375 ini_file = (struct mlxsw_linecard_ini_file *) data; 1376 ini_file_size = le16_to_cpu(ini_file->size); 1377 for (i = 0; i < ini_file_size / 4; i++) { 1378 u32 *val = &((u32 *) ini_file->data)[i]; 1379 1380 *val = swab32(*val); 1381 } 1382 types_info->ini_files[count] = ini_file; 1383 data += ini_file_size + sizeof(__le16); 1384 size -= ini_file_size + sizeof(__le16); 1385 count++; 1386 } 1387 } 1388 1389 #define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT \ 1390 "mellanox/lc_ini_bundle_%u_%u.bin" 1391 #define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN \ 1392 (sizeof(MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT) + 4) 1393 1394 static int mlxsw_linecard_types_init(struct mlxsw_core *mlxsw_core, 1395 struct mlxsw_linecards *linecards) 1396 { 1397 const struct mlxsw_fw_rev *rev = &linecards->bus_info->fw_rev; 1398 char filename[MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN]; 1399 struct mlxsw_linecard_types_info *types_info; 1400 const struct firmware *firmware; 1401 int err; 1402 1403 err = snprintf(filename, sizeof(filename), 1404 MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT, 1405 rev->minor, rev->subminor); 1406 WARN_ON(err >= sizeof(filename)); 1407 1408 err = request_firmware_direct(&firmware, filename, 1409 linecards->bus_info->dev); 1410 if (err) { 1411 dev_warn(linecards->bus_info->dev, "Could not request linecards INI file \"%s\", provisioning will not be possible\n", 1412 filename); 1413 return 0; 1414 } 1415 1416 types_info = kzalloc(sizeof(*types_info), GFP_KERNEL); 1417 if (!types_info) { 1418 release_firmware(firmware); 1419 return -ENOMEM; 1420 } 1421 linecards->types_info = types_info; 1422 1423 types_info->data_size = firmware->size; 1424 types_info->data = vmalloc(types_info->data_size); 1425 if (!types_info->data) { 1426 err = -ENOMEM; 1427 release_firmware(firmware); 1428 goto err_data_alloc; 1429 } 1430 memcpy(types_info->data, firmware->data, types_info->data_size); 1431 release_firmware(firmware); 1432 1433 err = mlxsw_linecard_types_file_validate(linecards, types_info); 1434 if (err) { 1435 err = 0; 1436 goto err_type_file_file_validate; 1437 } 1438 1439 types_info->ini_files = kmalloc_array(types_info->count, 1440 sizeof(struct mlxsw_linecard_ini_file *), 1441 GFP_KERNEL); 1442 if (!types_info->ini_files) { 1443 err = -ENOMEM; 1444 goto err_ini_files_alloc; 1445 } 1446 1447 mlxsw_linecard_types_file_parse(types_info); 1448 1449 return 0; 1450 1451 err_ini_files_alloc: 1452 err_type_file_file_validate: 1453 vfree(types_info->data); 1454 err_data_alloc: 1455 kfree(types_info); 1456 return err; 1457 } 1458 1459 static void mlxsw_linecard_types_fini(struct mlxsw_linecards *linecards) 1460 { 1461 struct mlxsw_linecard_types_info *types_info = linecards->types_info; 1462 1463 if (!types_info) 1464 return; 1465 kfree(types_info->ini_files); 1466 vfree(types_info->data); 1467 kfree(types_info); 1468 } 1469 1470 int mlxsw_linecards_init(struct mlxsw_core *mlxsw_core, 1471 const struct mlxsw_bus_info *bus_info) 1472 { 1473 char mgpir_pl[MLXSW_REG_MGPIR_LEN]; 1474 struct mlxsw_linecards *linecards; 1475 u8 slot_count; 1476 int err; 1477 int i; 1478 1479 mlxsw_reg_mgpir_pack(mgpir_pl, 0); 1480 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl); 1481 if (err) 1482 return err; 1483 1484 mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL, 1485 NULL, &slot_count); 1486 if (!slot_count) 1487 return 0; 1488 1489 linecards = vzalloc(struct_size(linecards, linecards, slot_count)); 1490 if (!linecards) 1491 return -ENOMEM; 1492 linecards->count = slot_count; 1493 linecards->mlxsw_core = mlxsw_core; 1494 linecards->bus_info = bus_info; 1495 INIT_LIST_HEAD(&linecards->event_ops_list); 1496 mutex_init(&linecards->event_ops_list_lock); 1497 1498 err = mlxsw_linecard_types_init(mlxsw_core, linecards); 1499 if (err) 1500 goto err_types_init; 1501 1502 err = mlxsw_core_traps_register(mlxsw_core, mlxsw_linecard_listener, 1503 ARRAY_SIZE(mlxsw_linecard_listener), 1504 mlxsw_core); 1505 if (err) 1506 goto err_traps_register; 1507 1508 mlxsw_core_linecards_set(mlxsw_core, linecards); 1509 1510 for (i = 0; i < linecards->count; i++) { 1511 err = mlxsw_linecard_init(mlxsw_core, linecards, i + 1); 1512 if (err) 1513 goto err_linecard_init; 1514 } 1515 1516 return 0; 1517 1518 err_linecard_init: 1519 for (i--; i >= 0; i--) 1520 mlxsw_linecard_fini(mlxsw_core, linecards, i + 1); 1521 mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener, 1522 ARRAY_SIZE(mlxsw_linecard_listener), 1523 mlxsw_core); 1524 err_traps_register: 1525 mlxsw_linecard_types_fini(linecards); 1526 err_types_init: 1527 vfree(linecards); 1528 return err; 1529 } 1530 1531 void mlxsw_linecards_fini(struct mlxsw_core *mlxsw_core) 1532 { 1533 struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core); 1534 int i; 1535 1536 if (!linecards) 1537 return; 1538 for (i = 0; i < linecards->count; i++) 1539 mlxsw_linecard_fini(mlxsw_core, linecards, i + 1); 1540 mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener, 1541 ARRAY_SIZE(mlxsw_linecard_listener), 1542 mlxsw_core); 1543 mlxsw_linecard_types_fini(linecards); 1544 mutex_destroy(&linecards->event_ops_list_lock); 1545 WARN_ON(!list_empty(&linecards->event_ops_list)); 1546 vfree(linecards); 1547 } 1548