1603 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/* 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 void42mlxsw_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 void52mlxsw_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 int166mlxsw_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 int189mlxsw_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 int210mlxsw_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 int251mlxsw_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 void451mlxsw_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 void462mlxsw_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 void476mlxsw_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 void490mlxsw_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 void508mlxsw_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 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 614unlock:615 mutex_unlock(&linecard->lock);616 return err;617}618 619static int620mlxsw_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 before632 * driver initialization. Due to a missing INI bundle file633 * or an outdated one, the queried card's type might not634 * be recognized by the driver. In this case, try to query635 * 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 658static 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 667static 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 685static 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 699static 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 706static 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 713static 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 768out:769 mutex_unlock(&linecard->lock);770 return err;771}772 773static 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 788static void mlxsw_linecards_irq_event_handler(struct mlxsw_core *mlxsw_core)789{790 struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);791 int i;792 793 /* Handle change of line card active state. */794 for (i = 0; i < linecards->count; i++) {795 struct mlxsw_linecard *linecard = mlxsw_linecard_get(linecards,796 i + 1);797 798 mlxsw_linecard_status_get_and_process(mlxsw_core, linecards,799 linecard);800 }801}802 803static const char * const mlxsw_linecard_status_event_type_name[] = {804 [MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION] = "provision",805 [MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION] = "unprovision",806};807 808static void mlxsw_linecard_status_event_to_work(struct work_struct *work)809{810 struct mlxsw_linecard *linecard =811 container_of(work, struct mlxsw_linecard,812 status_event_to_dw.work);813 814 mutex_lock(&linecard->lock);815 dev_err(linecard->linecards->bus_info->dev, "linecard %u: Timeout reached waiting on %s status event",816 linecard->slot_index,817 mlxsw_linecard_status_event_type_name[linecard->status_event_type_to]);818 mlxsw_linecard_provision_fail(linecard);819 mutex_unlock(&linecard->lock);820}821 822static int __mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard)823{824 dev_info(linecard->linecards->bus_info->dev, "linecard %u: Clearing FSM state error",825 linecard->slot_index);826 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,827 MLXSW_REG_MBCT_OP_CLEAR_ERRORS, false);828 return mlxsw_reg_write(linecard->linecards->mlxsw_core,829 MLXSW_REG(mbct), linecard->mbct_pl);830}831 832static int mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard,833 enum mlxsw_reg_mbct_fsm_state fsm_state)834{835 if (fsm_state != MLXSW_REG_MBCT_FSM_STATE_ERROR)836 return 0;837 return __mlxsw_linecard_fix_fsm_state(linecard);838}839 840static int841mlxsw_linecard_query_ini_status(struct mlxsw_linecard *linecard,842 enum mlxsw_reg_mbct_status *status,843 enum mlxsw_reg_mbct_fsm_state *fsm_state,844 struct netlink_ext_ack *extack)845{846 int err;847 848 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,849 MLXSW_REG_MBCT_OP_QUERY_STATUS, false);850 err = mlxsw_reg_query(linecard->linecards->mlxsw_core, MLXSW_REG(mbct),851 linecard->mbct_pl);852 if (err) {853 NL_SET_ERR_MSG_MOD(extack, "Failed to query linecard INI status");854 return err;855 }856 mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, status, fsm_state);857 return err;858}859 860static int861mlxsw_linecard_ini_transfer(struct mlxsw_core *mlxsw_core,862 struct mlxsw_linecard *linecard,863 const struct mlxsw_linecard_ini_file *ini_file,864 struct netlink_ext_ack *extack)865{866 enum mlxsw_reg_mbct_fsm_state fsm_state;867 enum mlxsw_reg_mbct_status status;868 size_t size_left;869 const u8 *data;870 int err;871 872 size_left = le16_to_cpu(ini_file->size);873 data = ini_file->data;874 while (size_left) {875 size_t data_size = MLXSW_REG_MBCT_DATA_LEN;876 bool is_last = false;877 878 if (size_left <= MLXSW_REG_MBCT_DATA_LEN) {879 data_size = size_left;880 is_last = true;881 }882 883 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,884 MLXSW_REG_MBCT_OP_DATA_TRANSFER, false);885 mlxsw_reg_mbct_dt_pack(linecard->mbct_pl, data_size,886 is_last, data);887 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct),888 linecard->mbct_pl);889 if (err) {890 NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI data transfer");891 return err;892 }893 mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL,894 &status, &fsm_state);895 if ((!is_last && status != MLXSW_REG_MBCT_STATUS_PART_DATA) ||896 (is_last && status != MLXSW_REG_MBCT_STATUS_LAST_DATA)) {897 NL_SET_ERR_MSG_MOD(extack, "Failed to transfer linecard INI data");898 mlxsw_linecard_fix_fsm_state(linecard, fsm_state);899 return -EINVAL;900 }901 size_left -= data_size;902 data += data_size;903 }904 905 return 0;906}907 908static int909mlxsw_linecard_ini_erase(struct mlxsw_core *mlxsw_core,910 struct mlxsw_linecard *linecard,911 struct netlink_ext_ack *extack)912{913 enum mlxsw_reg_mbct_fsm_state fsm_state;914 enum mlxsw_reg_mbct_status status;915 int err;916 917 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,918 MLXSW_REG_MBCT_OP_ERASE_INI_IMAGE, false);919 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct),920 linecard->mbct_pl);921 if (err) {922 NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI erase");923 return err;924 }925 mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state);926 switch (status) {927 case MLXSW_REG_MBCT_STATUS_ERASE_COMPLETE:928 break;929 default:930 /* Should not happen */931 fallthrough;932 case MLXSW_REG_MBCT_STATUS_ERASE_FAILED:933 NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI");934 goto fix_fsm_err_out;935 case MLXSW_REG_MBCT_STATUS_ERROR_INI_IN_USE:936 NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI while being used");937 goto fix_fsm_err_out;938 }939 return 0;940 941fix_fsm_err_out:942 mlxsw_linecard_fix_fsm_state(linecard, fsm_state);943 return -EINVAL;944}945 946static void mlxsw_linecard_bct_process(struct mlxsw_core *mlxsw_core,947 const char *mbct_pl)948{949 struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);950 enum mlxsw_reg_mbct_fsm_state fsm_state;951 enum mlxsw_reg_mbct_status status;952 struct mlxsw_linecard *linecard;953 u8 slot_index;954 955 mlxsw_reg_mbct_unpack(mbct_pl, &slot_index, &status, &fsm_state);956 if (WARN_ON(slot_index > linecards->count))957 return;958 linecard = mlxsw_linecard_get(linecards, slot_index);959 mutex_lock(&linecard->lock);960 if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) {961 dev_err(linecards->bus_info->dev, "linecard %u: Failed to activate INI",962 linecard->slot_index);963 goto fix_fsm_out;964 }965 mutex_unlock(&linecard->lock);966 return;967 968fix_fsm_out:969 mlxsw_linecard_fix_fsm_state(linecard, fsm_state);970 mlxsw_linecard_provision_fail(linecard);971 mutex_unlock(&linecard->lock);972}973 974static int975mlxsw_linecard_ini_activate(struct mlxsw_core *mlxsw_core,976 struct mlxsw_linecard *linecard,977 struct netlink_ext_ack *extack)978{979 enum mlxsw_reg_mbct_fsm_state fsm_state;980 enum mlxsw_reg_mbct_status status;981 int err;982 983 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,984 MLXSW_REG_MBCT_OP_ACTIVATE, true);985 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct), linecard->mbct_pl);986 if (err) {987 NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI activation");988 return err;989 }990 mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state);991 if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) {992 NL_SET_ERR_MSG_MOD(extack, "Failed to activate linecard INI");993 goto fix_fsm_err_out;994 }995 996 return 0;997 998fix_fsm_err_out:999 mlxsw_linecard_fix_fsm_state(linecard, fsm_state);1000 return -EINVAL;1001}1002 1003#define MLXSW_LINECARD_INI_WAIT_RETRIES 101004#define MLXSW_LINECARD_INI_WAIT_MS 5001005 1006static int1007mlxsw_linecard_ini_in_use_wait(struct mlxsw_core *mlxsw_core,1008 struct mlxsw_linecard *linecard,1009 struct netlink_ext_ack *extack)1010{1011 enum mlxsw_reg_mbct_fsm_state fsm_state;1012 enum mlxsw_reg_mbct_status status;1013 unsigned int ini_wait_retries = 0;1014 int err;1015 1016query_ini_status:1017 err = mlxsw_linecard_query_ini_status(linecard, &status,1018 &fsm_state, extack);1019 if (err)1020 return err;1021 1022 switch (fsm_state) {1023 case MLXSW_REG_MBCT_FSM_STATE_INI_IN_USE:1024 if (ini_wait_retries++ > MLXSW_LINECARD_INI_WAIT_RETRIES) {1025 NL_SET_ERR_MSG_MOD(extack, "Failed to wait for linecard INI to be unused");1026 return -EINVAL;1027 }1028 mdelay(MLXSW_LINECARD_INI_WAIT_MS);1029 goto query_ini_status;1030 default:1031 break;1032 }1033 return 0;1034}1035 1036static bool mlxsw_linecard_port_selector(void *priv, u16 local_port)1037{1038 struct mlxsw_linecard *linecard = priv;1039 struct mlxsw_core *mlxsw_core;1040 1041 mlxsw_core = linecard->linecards->mlxsw_core;1042 return linecard == mlxsw_core_port_linecard_get(mlxsw_core, local_port);1043}1044 1045static int mlxsw_linecard_provision(struct devlink_linecard *devlink_linecard,1046 void *priv, const char *type,1047 const void *type_priv,1048 struct netlink_ext_ack *extack)1049{1050 const struct mlxsw_linecard_ini_file *ini_file = type_priv;1051 struct mlxsw_linecard *linecard = priv;1052 struct mlxsw_core *mlxsw_core;1053 int err;1054 1055 mutex_lock(&linecard->lock);1056 1057 mlxsw_core = linecard->linecards->mlxsw_core;1058 1059 err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack);1060 if (err)1061 goto err_out;1062 1063 err = mlxsw_linecard_ini_transfer(mlxsw_core, linecard,1064 ini_file, extack);1065 if (err)1066 goto err_out;1067 1068 mlxsw_linecard_status_event_to_schedule(linecard,1069 MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION);1070 err = mlxsw_linecard_ini_activate(mlxsw_core, linecard, extack);1071 if (err)1072 goto err_out;1073 1074 goto out;1075 1076err_out:1077 mlxsw_linecard_provision_fail(linecard);1078out:1079 mutex_unlock(&linecard->lock);1080 return err;1081}1082 1083static int mlxsw_linecard_unprovision(struct devlink_linecard *devlink_linecard,1084 void *priv,1085 struct netlink_ext_ack *extack)1086{1087 struct mlxsw_linecard *linecard = priv;1088 struct mlxsw_core *mlxsw_core;1089 int err;1090 1091 mutex_lock(&linecard->lock);1092 1093 mlxsw_core = linecard->linecards->mlxsw_core;1094 1095 mlxsw_core_ports_remove_selected(mlxsw_core,1096 mlxsw_linecard_port_selector,1097 linecard);1098 1099 err = mlxsw_linecard_ini_in_use_wait(mlxsw_core, linecard, extack);1100 if (err)1101 goto err_out;1102 1103 mlxsw_linecard_status_event_to_schedule(linecard,1104 MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION);1105 err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack);1106 if (err)1107 goto err_out;1108 1109 goto out;1110 1111err_out:1112 mlxsw_linecard_provision_fail(linecard);1113out:1114 mutex_unlock(&linecard->lock);1115 return err;1116}1117 1118static bool mlxsw_linecard_same_provision(struct devlink_linecard *devlink_linecard,1119 void *priv, const char *type,1120 const void *type_priv)1121{1122 const struct mlxsw_linecard_ini_file *ini_file = type_priv;1123 struct mlxsw_linecard *linecard = priv;1124 bool ret;1125 1126 mutex_lock(&linecard->lock);1127 ret = linecard->hw_revision == be16_to_cpu(ini_file->format.hw_revision) &&1128 linecard->ini_version == be16_to_cpu(ini_file->format.ini_version);1129 mutex_unlock(&linecard->lock);1130 return ret;1131}1132 1133static unsigned int1134mlxsw_linecard_types_count(struct devlink_linecard *devlink_linecard,1135 void *priv)1136{1137 struct mlxsw_linecard *linecard = priv;1138 1139 return linecard->linecards->types_info ?1140 linecard->linecards->types_info->count : 0;1141}1142 1143static void mlxsw_linecard_types_get(struct devlink_linecard *devlink_linecard,1144 void *priv, unsigned int index,1145 const char **type, const void **type_priv)1146{1147 struct mlxsw_linecard_types_info *types_info;1148 struct mlxsw_linecard_ini_file *ini_file;1149 struct mlxsw_linecard *linecard = priv;1150 1151 types_info = linecard->linecards->types_info;1152 if (WARN_ON_ONCE(!types_info))1153 return;1154 ini_file = types_info->ini_files[index];1155 *type = ini_file->format.name;1156 *type_priv = ini_file;1157}1158 1159static const struct devlink_linecard_ops mlxsw_linecard_ops = {1160 .provision = mlxsw_linecard_provision,1161 .unprovision = mlxsw_linecard_unprovision,1162 .same_provision = mlxsw_linecard_same_provision,1163 .types_count = mlxsw_linecard_types_count,1164 .types_get = mlxsw_linecard_types_get,1165};1166 1167struct mlxsw_linecard_status_event {1168 struct mlxsw_core *mlxsw_core;1169 char mddq_pl[MLXSW_REG_MDDQ_LEN];1170 struct work_struct work;1171};1172 1173static void mlxsw_linecard_status_event_work(struct work_struct *work)1174{1175 struct mlxsw_linecard_status_event *event;1176 struct mlxsw_linecards *linecards;1177 struct mlxsw_core *mlxsw_core;1178 1179 event = container_of(work, struct mlxsw_linecard_status_event, work);1180 mlxsw_core = event->mlxsw_core;1181 linecards = mlxsw_core_linecards(mlxsw_core);1182 mlxsw_linecard_status_process(linecards, NULL, event->mddq_pl);1183 kfree(event);1184}1185 1186static void1187mlxsw_linecard_status_listener_func(const struct mlxsw_reg_info *reg,1188 char *mddq_pl, void *priv)1189{1190 struct mlxsw_linecard_status_event *event;1191 struct mlxsw_core *mlxsw_core = priv;1192 1193 event = kmalloc(sizeof(*event), GFP_ATOMIC);1194 if (!event)1195 return;1196 event->mlxsw_core = mlxsw_core;1197 memcpy(event->mddq_pl, mddq_pl, sizeof(event->mddq_pl));1198 INIT_WORK(&event->work, mlxsw_linecard_status_event_work);1199 mlxsw_core_schedule_work(&event->work);1200}1201 1202struct mlxsw_linecard_bct_event {1203 struct mlxsw_core *mlxsw_core;1204 char mbct_pl[MLXSW_REG_MBCT_LEN];1205 struct work_struct work;1206};1207 1208static void mlxsw_linecard_bct_event_work(struct work_struct *work)1209{1210 struct mlxsw_linecard_bct_event *event;1211 struct mlxsw_core *mlxsw_core;1212 1213 event = container_of(work, struct mlxsw_linecard_bct_event, work);1214 mlxsw_core = event->mlxsw_core;1215 mlxsw_linecard_bct_process(mlxsw_core, event->mbct_pl);1216 kfree(event);1217}1218 1219static void1220mlxsw_linecard_bct_listener_func(const struct mlxsw_reg_info *reg,1221 char *mbct_pl, void *priv)1222{1223 struct mlxsw_linecard_bct_event *event;1224 struct mlxsw_core *mlxsw_core = priv;1225 1226 event = kmalloc(sizeof(*event), GFP_ATOMIC);1227 if (!event)1228 return;1229 event->mlxsw_core = mlxsw_core;1230 memcpy(event->mbct_pl, mbct_pl, sizeof(event->mbct_pl));1231 INIT_WORK(&event->work, mlxsw_linecard_bct_event_work);1232 mlxsw_core_schedule_work(&event->work);1233}1234 1235static const struct mlxsw_listener mlxsw_linecard_listener[] = {1236 MLXSW_CORE_EVENTL(mlxsw_linecard_status_listener_func, DSDSC),1237 MLXSW_CORE_EVENTL(mlxsw_linecard_bct_listener_func, BCTOE),1238};1239 1240static int mlxsw_linecard_event_delivery_set(struct mlxsw_core *mlxsw_core,1241 struct mlxsw_linecard *linecard,1242 bool enable)1243{1244 char mddq_pl[MLXSW_REG_MDDQ_LEN];1245 1246 mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, enable);1247 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddq), mddq_pl);1248}1249 1250static int mlxsw_linecard_init(struct mlxsw_core *mlxsw_core,1251 struct mlxsw_linecards *linecards,1252 u8 slot_index)1253{1254 struct devlink_linecard *devlink_linecard;1255 struct mlxsw_linecard *linecard;1256 1257 linecard = mlxsw_linecard_get(linecards, slot_index);1258 linecard->slot_index = slot_index;1259 linecard->linecards = linecards;1260 mutex_init(&linecard->lock);1261 1262 devlink_linecard = devl_linecard_create(priv_to_devlink(mlxsw_core),1263 slot_index, &mlxsw_linecard_ops,1264 linecard);1265 if (IS_ERR(devlink_linecard))1266 return PTR_ERR(devlink_linecard);1267 1268 linecard->devlink_linecard = devlink_linecard;1269 INIT_DELAYED_WORK(&linecard->status_event_to_dw,1270 &mlxsw_linecard_status_event_to_work);1271 1272 return 0;1273}1274 1275static void mlxsw_linecard_fini(struct mlxsw_core *mlxsw_core,1276 struct mlxsw_linecards *linecards,1277 u8 slot_index)1278{1279 struct mlxsw_linecard *linecard;1280 1281 linecard = mlxsw_linecard_get(linecards, slot_index);1282 cancel_delayed_work_sync(&linecard->status_event_to_dw);1283 /* Make sure all scheduled events are processed */1284 mlxsw_core_flush_owq();1285 if (linecard->active)1286 mlxsw_linecard_active_clear(linecard);1287 mlxsw_linecard_bdev_del(linecard);1288 devl_linecard_destroy(linecard->devlink_linecard);1289 mutex_destroy(&linecard->lock);1290}1291 1292static int1293mlxsw_linecard_event_delivery_init(struct mlxsw_core *mlxsw_core,1294 struct mlxsw_linecards *linecards,1295 u8 slot_index)1296{1297 struct mlxsw_linecard *linecard;1298 int err;1299 1300 linecard = mlxsw_linecard_get(linecards, slot_index);1301 err = mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, true);1302 if (err)1303 return err;1304 1305 err = mlxsw_linecard_status_get_and_process(mlxsw_core, linecards,1306 linecard);1307 if (err)1308 goto err_status_get_and_process;1309 1310 return 0;1311 1312err_status_get_and_process:1313 mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false);1314 return err;1315}1316 1317static void1318mlxsw_linecard_event_delivery_fini(struct mlxsw_core *mlxsw_core,1319 struct mlxsw_linecards *linecards,1320 u8 slot_index)1321{1322 struct mlxsw_linecard *linecard;1323 1324 linecard = mlxsw_linecard_get(linecards, slot_index);1325 mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false);1326}1327 1328/* LINECARDS INI BUNDLE FILE1329 * +----------------------------------+1330 * | MAGIC ("NVLCINI+") |1331 * +----------------------------------+ +--------------------+1332 * | INI 0 +---> | __le16 size |1333 * +----------------------------------+ | __be16 hw_revision |1334 * | INI 1 | | __be16 ini_version |1335 * +----------------------------------+ | u8 __dontcare[3] |1336 * | ... | | u8 type |1337 * +----------------------------------+ | u8 name[20] |1338 * | INI N | | ... |1339 * +----------------------------------+ +--------------------+1340 */1341 1342#define MLXSW_LINECARDS_INI_BUNDLE_MAGIC "NVLCINI+"1343 1344static int1345mlxsw_linecard_types_file_validate(struct mlxsw_linecards *linecards,1346 struct mlxsw_linecard_types_info *types_info)1347{1348 size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC);1349 struct mlxsw_linecard_ini_file *ini_file;1350 size_t size = types_info->data_size;1351 const u8 *data = types_info->data;1352 unsigned int count = 0;1353 u16 ini_file_size;1354 1355 if (size < magic_size) {1356 dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file size, smaller than magic size\n");1357 return -EINVAL;1358 }1359 if (memcmp(data, MLXSW_LINECARDS_INI_BUNDLE_MAGIC, magic_size)) {1360 dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file magic pattern\n");1361 return -EINVAL;1362 }1363 1364 data += magic_size;1365 size -= magic_size;1366 1367 while (size > 0) {1368 if (size < sizeof(*ini_file)) {1369 dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI which is smaller than bare minimum\n");1370 return -EINVAL;1371 }1372 ini_file = (struct mlxsw_linecard_ini_file *) data;1373 ini_file_size = le16_to_cpu(ini_file->size);1374 if (ini_file_size + sizeof(__le16) > size) {1375 dev_warn(linecards->bus_info->dev, "Linecards INIs file appears to be truncated\n");1376 return -EINVAL;1377 }1378 if (ini_file_size % 4) {1379 dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI with invalid size\n");1380 return -EINVAL;1381 }1382 data += ini_file_size + sizeof(__le16);1383 size -= ini_file_size + sizeof(__le16);1384 count++;1385 }1386 if (!count) {1387 dev_warn(linecards->bus_info->dev, "Linecards INIs file does not contain any INI\n");1388 return -EINVAL;1389 }1390 types_info->count = count;1391 return 0;1392}1393 1394static void1395mlxsw_linecard_types_file_parse(struct mlxsw_linecard_types_info *types_info)1396{1397 size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC);1398 size_t size = types_info->data_size - magic_size;1399 const u8 *data = types_info->data + magic_size;1400 struct mlxsw_linecard_ini_file *ini_file;1401 unsigned int count = 0;1402 u16 ini_file_size;1403 int i;1404 1405 while (size) {1406 ini_file = (struct mlxsw_linecard_ini_file *) data;1407 ini_file_size = le16_to_cpu(ini_file->size);1408 for (i = 0; i < ini_file_size / 4; i++) {1409 u32 *val = &((u32 *) ini_file->data)[i];1410 1411 *val = swab32(*val);1412 }1413 types_info->ini_files[count] = ini_file;1414 data += ini_file_size + sizeof(__le16);1415 size -= ini_file_size + sizeof(__le16);1416 count++;1417 }1418}1419 1420#define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT \1421 "mellanox/lc_ini_bundle_%u_%u.bin"1422#define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN \1423 (sizeof(MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT) + 4)1424 1425static int mlxsw_linecard_types_init(struct mlxsw_core *mlxsw_core,1426 struct mlxsw_linecards *linecards)1427{1428 const struct mlxsw_fw_rev *rev = &linecards->bus_info->fw_rev;1429 char filename[MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN];1430 struct mlxsw_linecard_types_info *types_info;1431 const struct firmware *firmware;1432 int err;1433 1434 err = snprintf(filename, sizeof(filename),1435 MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT,1436 rev->minor, rev->subminor);1437 WARN_ON(err >= sizeof(filename));1438 1439 err = request_firmware_direct(&firmware, filename,1440 linecards->bus_info->dev);1441 if (err) {1442 dev_warn(linecards->bus_info->dev, "Could not request linecards INI file \"%s\", provisioning will not be possible\n",1443 filename);1444 return 0;1445 }1446 1447 types_info = kzalloc(sizeof(*types_info), GFP_KERNEL);1448 if (!types_info) {1449 release_firmware(firmware);1450 return -ENOMEM;1451 }1452 linecards->types_info = types_info;1453 1454 types_info->data_size = firmware->size;1455 types_info->data = vmalloc(types_info->data_size);1456 if (!types_info->data) {1457 err = -ENOMEM;1458 release_firmware(firmware);1459 goto err_data_alloc;1460 }1461 memcpy(types_info->data, firmware->data, types_info->data_size);1462 release_firmware(firmware);1463 1464 err = mlxsw_linecard_types_file_validate(linecards, types_info);1465 if (err) {1466 err = 0;1467 goto err_type_file_file_validate;1468 }1469 1470 types_info->ini_files = kmalloc_array(types_info->count,1471 sizeof(struct mlxsw_linecard_ini_file *),1472 GFP_KERNEL);1473 if (!types_info->ini_files) {1474 err = -ENOMEM;1475 goto err_ini_files_alloc;1476 }1477 1478 mlxsw_linecard_types_file_parse(types_info);1479 1480 return 0;1481 1482err_ini_files_alloc:1483err_type_file_file_validate:1484 vfree(types_info->data);1485err_data_alloc:1486 kfree(types_info);1487 linecards->types_info = NULL;1488 return err;1489}1490 1491static void mlxsw_linecard_types_fini(struct mlxsw_linecards *linecards)1492{1493 struct mlxsw_linecard_types_info *types_info = linecards->types_info;1494 1495 if (!types_info)1496 return;1497 kfree(types_info->ini_files);1498 vfree(types_info->data);1499 kfree(types_info);1500}1501 1502int mlxsw_linecards_init(struct mlxsw_core *mlxsw_core,1503 const struct mlxsw_bus_info *bus_info)1504{1505 char mgpir_pl[MLXSW_REG_MGPIR_LEN];1506 struct mlxsw_linecards *linecards;1507 u8 slot_count;1508 int err;1509 int i;1510 1511 mlxsw_reg_mgpir_pack(mgpir_pl, 0);1512 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl);1513 if (err)1514 return err;1515 1516 mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL,1517 NULL, &slot_count);1518 if (!slot_count)1519 return 0;1520 1521 linecards = vzalloc(struct_size(linecards, linecards, slot_count));1522 if (!linecards)1523 return -ENOMEM;1524 linecards->count = slot_count;1525 linecards->mlxsw_core = mlxsw_core;1526 linecards->bus_info = bus_info;1527 INIT_LIST_HEAD(&linecards->event_ops_list);1528 mutex_init(&linecards->event_ops_list_lock);1529 1530 err = mlxsw_linecard_types_init(mlxsw_core, linecards);1531 if (err)1532 goto err_types_init;1533 1534 err = mlxsw_core_traps_register(mlxsw_core, mlxsw_linecard_listener,1535 ARRAY_SIZE(mlxsw_linecard_listener),1536 mlxsw_core);1537 if (err)1538 goto err_traps_register;1539 1540 err = mlxsw_core_irq_event_handler_register(mlxsw_core,1541 mlxsw_linecards_irq_event_handler);1542 if (err)1543 goto err_irq_event_handler_register;1544 1545 mlxsw_core_linecards_set(mlxsw_core, linecards);1546 1547 for (i = 0; i < linecards->count; i++) {1548 err = mlxsw_linecard_init(mlxsw_core, linecards, i + 1);1549 if (err)1550 goto err_linecard_init;1551 }1552 1553 for (i = 0; i < linecards->count; i++) {1554 err = mlxsw_linecard_event_delivery_init(mlxsw_core, linecards,1555 i + 1);1556 if (err)1557 goto err_linecard_event_delivery_init;1558 }1559 1560 return 0;1561 1562err_linecard_event_delivery_init:1563 for (i--; i >= 0; i--)1564 mlxsw_linecard_event_delivery_fini(mlxsw_core, linecards, i + 1);1565 i = linecards->count;1566err_linecard_init:1567 for (i--; i >= 0; i--)1568 mlxsw_linecard_fini(mlxsw_core, linecards, i + 1);1569 mlxsw_core_irq_event_handler_unregister(mlxsw_core,1570 mlxsw_linecards_irq_event_handler);1571err_irq_event_handler_register:1572 mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener,1573 ARRAY_SIZE(mlxsw_linecard_listener),1574 mlxsw_core);1575err_traps_register:1576 mlxsw_linecard_types_fini(linecards);1577err_types_init:1578 vfree(linecards);1579 return err;1580}1581 1582void mlxsw_linecards_fini(struct mlxsw_core *mlxsw_core)1583{1584 struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);1585 int i;1586 1587 if (!linecards)1588 return;1589 for (i = 0; i < linecards->count; i++)1590 mlxsw_linecard_event_delivery_fini(mlxsw_core, linecards, i + 1);1591 for (i = 0; i < linecards->count; i++)1592 mlxsw_linecard_fini(mlxsw_core, linecards, i + 1);1593 mlxsw_core_irq_event_handler_unregister(mlxsw_core,1594 mlxsw_linecards_irq_event_handler);1595 mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener,1596 ARRAY_SIZE(mlxsw_linecard_listener),1597 mlxsw_core);1598 mlxsw_linecard_types_fini(linecards);1599 mutex_destroy(&linecards->event_ops_list_lock);1600 WARN_ON(!list_empty(&linecards->event_ops_list));1601 vfree(linecards);1602}1603