0001
0002
0003
0004 #include <linux/kernel.h>
0005 #include <linux/module.h>
0006 #include <linux/err.h>
0007 #include <linux/types.h>
0008 #include <linux/string.h>
0009 #include <linux/workqueue.h>
0010 #include <linux/gfp.h>
0011 #include <linux/slab.h>
0012 #include <linux/list.h>
0013 #include <linux/vmalloc.h>
0014
0015 #include "core.h"
0016 #include "../mlxfw/mlxfw.h"
0017
0018 struct mlxsw_linecard_ini_file {
0019 __le16 size;
0020 union {
0021 u8 data[0];
0022 struct {
0023 __be16 hw_revision;
0024 __be16 ini_version;
0025 u8 __dontcare[3];
0026 u8 type;
0027 u8 name[20];
0028 } format;
0029 };
0030 };
0031
0032 struct mlxsw_linecard_types_info {
0033 struct mlxsw_linecard_ini_file **ini_files;
0034 unsigned int count;
0035 size_t data_size;
0036 char *data;
0037 };
0038
0039 #define MLXSW_LINECARD_STATUS_EVENT_TO (10 * MSEC_PER_SEC)
0040
0041 static void
0042 mlxsw_linecard_status_event_to_schedule(struct mlxsw_linecard *linecard,
0043 enum mlxsw_linecard_status_event_type status_event_type)
0044 {
0045 cancel_delayed_work_sync(&linecard->status_event_to_dw);
0046 linecard->status_event_type_to = status_event_type;
0047 mlxsw_core_schedule_dw(&linecard->status_event_to_dw,
0048 msecs_to_jiffies(MLXSW_LINECARD_STATUS_EVENT_TO));
0049 }
0050
0051 static void
0052 mlxsw_linecard_status_event_done(struct mlxsw_linecard *linecard,
0053 enum mlxsw_linecard_status_event_type status_event_type)
0054 {
0055 if (linecard->status_event_type_to == status_event_type)
0056 cancel_delayed_work_sync(&linecard->status_event_to_dw);
0057 }
0058
0059 static const char *
0060 mlxsw_linecard_types_lookup(struct mlxsw_linecards *linecards, u8 card_type)
0061 {
0062 struct mlxsw_linecard_types_info *types_info;
0063 struct mlxsw_linecard_ini_file *ini_file;
0064 int i;
0065
0066 types_info = linecards->types_info;
0067 if (!types_info)
0068 return NULL;
0069 for (i = 0; i < types_info->count; i++) {
0070 ini_file = linecards->types_info->ini_files[i];
0071 if (ini_file->format.type == card_type)
0072 return ini_file->format.name;
0073 }
0074 return NULL;
0075 }
0076
0077 static const char *mlxsw_linecard_type_name(struct mlxsw_linecard *linecard)
0078 {
0079 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
0080 char mddq_pl[MLXSW_REG_MDDQ_LEN];
0081 int err;
0082
0083 mlxsw_reg_mddq_slot_name_pack(mddq_pl, linecard->slot_index);
0084 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl);
0085 if (err)
0086 return ERR_PTR(err);
0087 mlxsw_reg_mddq_slot_name_unpack(mddq_pl, linecard->name);
0088 return linecard->name;
0089 }
0090
0091 struct mlxsw_linecard_device_fw_info {
0092 struct mlxfw_dev mlxfw_dev;
0093 struct mlxsw_core *mlxsw_core;
0094 struct mlxsw_linecard *linecard;
0095 };
0096
0097 static int mlxsw_linecard_device_fw_component_query(struct mlxfw_dev *mlxfw_dev,
0098 u16 component_index,
0099 u32 *p_max_size,
0100 u8 *p_align_bits,
0101 u16 *p_max_write_size)
0102 {
0103 struct mlxsw_linecard_device_fw_info *info =
0104 container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
0105 mlxfw_dev);
0106 struct mlxsw_linecard *linecard = info->linecard;
0107 struct mlxsw_core *mlxsw_core = info->mlxsw_core;
0108 char mddt_pl[MLXSW_REG_MDDT_LEN];
0109 char *mcqi_pl;
0110 int err;
0111
0112 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
0113 linecard->device.index,
0114 MLXSW_REG_MDDT_METHOD_QUERY,
0115 MLXSW_REG(mcqi), &mcqi_pl);
0116
0117 mlxsw_reg_mcqi_pack(mcqi_pl, component_index);
0118 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
0119 if (err)
0120 return err;
0121 mlxsw_reg_mcqi_unpack(mcqi_pl, p_max_size, p_align_bits,
0122 p_max_write_size);
0123
0124 *p_align_bits = max_t(u8, *p_align_bits, 2);
0125 *p_max_write_size = min_t(u16, *p_max_write_size,
0126 MLXSW_REG_MCDA_MAX_DATA_LEN);
0127 return 0;
0128 }
0129
0130 static int mlxsw_linecard_device_fw_fsm_lock(struct mlxfw_dev *mlxfw_dev,
0131 u32 *fwhandle)
0132 {
0133 struct mlxsw_linecard_device_fw_info *info =
0134 container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
0135 mlxfw_dev);
0136 struct mlxsw_linecard *linecard = info->linecard;
0137 struct mlxsw_core *mlxsw_core = info->mlxsw_core;
0138 char mddt_pl[MLXSW_REG_MDDT_LEN];
0139 u8 control_state;
0140 char *mcc_pl;
0141 int err;
0142
0143 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
0144 linecard->device.index,
0145 MLXSW_REG_MDDT_METHOD_QUERY,
0146 MLXSW_REG(mcc), &mcc_pl);
0147 mlxsw_reg_mcc_pack(mcc_pl, 0, 0, 0, 0);
0148 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
0149 if (err)
0150 return err;
0151
0152 mlxsw_reg_mcc_unpack(mcc_pl, fwhandle, NULL, &control_state);
0153 if (control_state != MLXFW_FSM_STATE_IDLE)
0154 return -EBUSY;
0155
0156 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
0157 linecard->device.index,
0158 MLXSW_REG_MDDT_METHOD_WRITE,
0159 MLXSW_REG(mcc), &mcc_pl);
0160 mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_LOCK_UPDATE_HANDLE,
0161 0, *fwhandle, 0);
0162 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
0163 }
0164
0165 static int
0166 mlxsw_linecard_device_fw_fsm_component_update(struct mlxfw_dev *mlxfw_dev,
0167 u32 fwhandle,
0168 u16 component_index,
0169 u32 component_size)
0170 {
0171 struct mlxsw_linecard_device_fw_info *info =
0172 container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
0173 mlxfw_dev);
0174 struct mlxsw_linecard *linecard = info->linecard;
0175 struct mlxsw_core *mlxsw_core = info->mlxsw_core;
0176 char mddt_pl[MLXSW_REG_MDDT_LEN];
0177 char *mcc_pl;
0178
0179 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
0180 linecard->device.index,
0181 MLXSW_REG_MDDT_METHOD_WRITE,
0182 MLXSW_REG(mcc), &mcc_pl);
0183 mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_UPDATE_COMPONENT,
0184 component_index, fwhandle, component_size);
0185 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
0186 }
0187
0188 static int
0189 mlxsw_linecard_device_fw_fsm_block_download(struct mlxfw_dev *mlxfw_dev,
0190 u32 fwhandle, u8 *data,
0191 u16 size, u32 offset)
0192 {
0193 struct mlxsw_linecard_device_fw_info *info =
0194 container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
0195 mlxfw_dev);
0196 struct mlxsw_linecard *linecard = info->linecard;
0197 struct mlxsw_core *mlxsw_core = info->mlxsw_core;
0198 char mddt_pl[MLXSW_REG_MDDT_LEN];
0199 char *mcda_pl;
0200
0201 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
0202 linecard->device.index,
0203 MLXSW_REG_MDDT_METHOD_WRITE,
0204 MLXSW_REG(mcda), &mcda_pl);
0205 mlxsw_reg_mcda_pack(mcda_pl, fwhandle, offset, size, data);
0206 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
0207 }
0208
0209 static int
0210 mlxsw_linecard_device_fw_fsm_component_verify(struct mlxfw_dev *mlxfw_dev,
0211 u32 fwhandle, u16 component_index)
0212 {
0213 struct mlxsw_linecard_device_fw_info *info =
0214 container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
0215 mlxfw_dev);
0216 struct mlxsw_linecard *linecard = info->linecard;
0217 struct mlxsw_core *mlxsw_core = info->mlxsw_core;
0218 char mddt_pl[MLXSW_REG_MDDT_LEN];
0219 char *mcc_pl;
0220
0221 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
0222 linecard->device.index,
0223 MLXSW_REG_MDDT_METHOD_WRITE,
0224 MLXSW_REG(mcc), &mcc_pl);
0225 mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_VERIFY_COMPONENT,
0226 component_index, fwhandle, 0);
0227 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
0228 }
0229
0230 static int mlxsw_linecard_device_fw_fsm_activate(struct mlxfw_dev *mlxfw_dev,
0231 u32 fwhandle)
0232 {
0233 struct mlxsw_linecard_device_fw_info *info =
0234 container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
0235 mlxfw_dev);
0236 struct mlxsw_linecard *linecard = info->linecard;
0237 struct mlxsw_core *mlxsw_core = info->mlxsw_core;
0238 char mddt_pl[MLXSW_REG_MDDT_LEN];
0239 char *mcc_pl;
0240
0241 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
0242 linecard->device.index,
0243 MLXSW_REG_MDDT_METHOD_WRITE,
0244 MLXSW_REG(mcc), &mcc_pl);
0245 mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_ACTIVATE,
0246 0, fwhandle, 0);
0247 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
0248 }
0249
0250 static int
0251 mlxsw_linecard_device_fw_fsm_query_state(struct mlxfw_dev *mlxfw_dev,
0252 u32 fwhandle,
0253 enum mlxfw_fsm_state *fsm_state,
0254 enum mlxfw_fsm_state_err *fsm_state_err)
0255 {
0256 struct mlxsw_linecard_device_fw_info *info =
0257 container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
0258 mlxfw_dev);
0259 struct mlxsw_linecard *linecard = info->linecard;
0260 struct mlxsw_core *mlxsw_core = info->mlxsw_core;
0261 char mddt_pl[MLXSW_REG_MDDT_LEN];
0262 u8 control_state;
0263 u8 error_code;
0264 char *mcc_pl;
0265 int err;
0266
0267 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
0268 linecard->device.index,
0269 MLXSW_REG_MDDT_METHOD_QUERY,
0270 MLXSW_REG(mcc), &mcc_pl);
0271 mlxsw_reg_mcc_pack(mcc_pl, 0, 0, fwhandle, 0);
0272 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
0273 if (err)
0274 return err;
0275
0276 mlxsw_reg_mcc_unpack(mcc_pl, NULL, &error_code, &control_state);
0277 *fsm_state = control_state;
0278 *fsm_state_err = min_t(enum mlxfw_fsm_state_err, error_code,
0279 MLXFW_FSM_STATE_ERR_MAX);
0280 return 0;
0281 }
0282
0283 static void mlxsw_linecard_device_fw_fsm_cancel(struct mlxfw_dev *mlxfw_dev,
0284 u32 fwhandle)
0285 {
0286 struct mlxsw_linecard_device_fw_info *info =
0287 container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
0288 mlxfw_dev);
0289 struct mlxsw_linecard *linecard = info->linecard;
0290 struct mlxsw_core *mlxsw_core = info->mlxsw_core;
0291 char mddt_pl[MLXSW_REG_MDDT_LEN];
0292 char *mcc_pl;
0293
0294 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
0295 linecard->device.index,
0296 MLXSW_REG_MDDT_METHOD_WRITE,
0297 MLXSW_REG(mcc), &mcc_pl);
0298 mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_CANCEL,
0299 0, fwhandle, 0);
0300 mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
0301 }
0302
0303 static void mlxsw_linecard_device_fw_fsm_release(struct mlxfw_dev *mlxfw_dev,
0304 u32 fwhandle)
0305 {
0306 struct mlxsw_linecard_device_fw_info *info =
0307 container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
0308 mlxfw_dev);
0309 struct mlxsw_linecard *linecard = info->linecard;
0310 struct mlxsw_core *mlxsw_core = info->mlxsw_core;
0311 char mddt_pl[MLXSW_REG_MDDT_LEN];
0312 char *mcc_pl;
0313
0314 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
0315 linecard->device.index,
0316 MLXSW_REG_MDDT_METHOD_WRITE,
0317 MLXSW_REG(mcc), &mcc_pl);
0318 mlxsw_reg_mcc_pack(mcc_pl,
0319 MLXSW_REG_MCC_INSTRUCTION_RELEASE_UPDATE_HANDLE,
0320 0, fwhandle, 0);
0321 mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
0322 }
0323
0324 static const struct mlxfw_dev_ops mlxsw_linecard_device_dev_ops = {
0325 .component_query = mlxsw_linecard_device_fw_component_query,
0326 .fsm_lock = mlxsw_linecard_device_fw_fsm_lock,
0327 .fsm_component_update = mlxsw_linecard_device_fw_fsm_component_update,
0328 .fsm_block_download = mlxsw_linecard_device_fw_fsm_block_download,
0329 .fsm_component_verify = mlxsw_linecard_device_fw_fsm_component_verify,
0330 .fsm_activate = mlxsw_linecard_device_fw_fsm_activate,
0331 .fsm_query_state = mlxsw_linecard_device_fw_fsm_query_state,
0332 .fsm_cancel = mlxsw_linecard_device_fw_fsm_cancel,
0333 .fsm_release = mlxsw_linecard_device_fw_fsm_release,
0334 };
0335
0336 int mlxsw_linecard_flash_update(struct devlink *linecard_devlink,
0337 struct mlxsw_linecard *linecard,
0338 const struct firmware *firmware,
0339 struct netlink_ext_ack *extack)
0340 {
0341 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
0342 struct mlxsw_linecard_device_fw_info info = {
0343 .mlxfw_dev = {
0344 .ops = &mlxsw_linecard_device_dev_ops,
0345 .psid = linecard->device.info.psid,
0346 .psid_size = strlen(linecard->device.info.psid),
0347 .devlink = linecard_devlink,
0348 },
0349 .mlxsw_core = mlxsw_core,
0350 .linecard = linecard,
0351 };
0352 int err;
0353
0354 mutex_lock(&linecard->lock);
0355 if (!linecard->active) {
0356 NL_SET_ERR_MSG_MOD(extack, "Only active line cards can be flashed");
0357 err = -EINVAL;
0358 goto unlock;
0359 }
0360 err = mlxsw_core_fw_flash(mlxsw_core, &info.mlxfw_dev,
0361 firmware, extack);
0362 unlock:
0363 mutex_unlock(&linecard->lock);
0364 return err;
0365 }
0366
0367 static int mlxsw_linecard_device_psid_get(struct mlxsw_linecard *linecard,
0368 u8 device_index, char *psid)
0369 {
0370 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
0371 char mddt_pl[MLXSW_REG_MDDT_LEN];
0372 char *mgir_pl;
0373 int err;
0374
0375 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, device_index,
0376 MLXSW_REG_MDDT_METHOD_QUERY,
0377 MLXSW_REG(mgir), &mgir_pl);
0378
0379 mlxsw_reg_mgir_pack(mgir_pl);
0380 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
0381 if (err)
0382 return err;
0383
0384 mlxsw_reg_mgir_fw_info_psid_memcpy_from(mgir_pl, psid);
0385 return 0;
0386 }
0387
0388 static int mlxsw_linecard_device_info_update(struct mlxsw_linecard *linecard)
0389 {
0390 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
0391 bool flashable_found = false;
0392 u8 msg_seq = 0;
0393
0394 do {
0395 struct mlxsw_linecard_device_info info;
0396 char mddq_pl[MLXSW_REG_MDDQ_LEN];
0397 bool flash_owner;
0398 bool data_valid;
0399 u8 device_index;
0400 int err;
0401
0402 mlxsw_reg_mddq_device_info_pack(mddq_pl, linecard->slot_index,
0403 msg_seq);
0404 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl);
0405 if (err)
0406 return err;
0407 mlxsw_reg_mddq_device_info_unpack(mddq_pl, &msg_seq,
0408 &data_valid, &flash_owner,
0409 &device_index,
0410 &info.fw_major,
0411 &info.fw_minor,
0412 &info.fw_sub_minor);
0413 if (!data_valid)
0414 break;
0415 if (!flash_owner)
0416 continue;
0417 if (flashable_found) {
0418 dev_warn_once(linecard->linecards->bus_info->dev, "linecard %u: More flashable devices present, exposing only the first one\n",
0419 linecard->slot_index);
0420 return 0;
0421 }
0422
0423 err = mlxsw_linecard_device_psid_get(linecard, device_index,
0424 info.psid);
0425 if (err)
0426 return err;
0427
0428 linecard->device.info = info;
0429 linecard->device.index = device_index;
0430 flashable_found = true;
0431 } while (msg_seq);
0432
0433 return 0;
0434 }
0435
0436 static void mlxsw_linecard_provision_fail(struct mlxsw_linecard *linecard)
0437 {
0438 linecard->provisioned = false;
0439 linecard->ready = false;
0440 linecard->active = false;
0441 devlink_linecard_provision_fail(linecard->devlink_linecard);
0442 }
0443
0444 struct mlxsw_linecards_event_ops_item {
0445 struct list_head list;
0446 const struct mlxsw_linecards_event_ops *event_ops;
0447 void *priv;
0448 };
0449
0450 static void
0451 mlxsw_linecard_event_op_call(struct mlxsw_linecard *linecard,
0452 mlxsw_linecards_event_op_t *op, void *priv)
0453 {
0454 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
0455
0456 if (!op)
0457 return;
0458 op(mlxsw_core, linecard->slot_index, priv);
0459 }
0460
0461 static void
0462 mlxsw_linecard_active_ops_call(struct mlxsw_linecard *linecard)
0463 {
0464 struct mlxsw_linecards *linecards = linecard->linecards;
0465 struct mlxsw_linecards_event_ops_item *item;
0466
0467 mutex_lock(&linecards->event_ops_list_lock);
0468 list_for_each_entry(item, &linecards->event_ops_list, list)
0469 mlxsw_linecard_event_op_call(linecard,
0470 item->event_ops->got_active,
0471 item->priv);
0472 mutex_unlock(&linecards->event_ops_list_lock);
0473 }
0474
0475 static void
0476 mlxsw_linecard_inactive_ops_call(struct mlxsw_linecard *linecard)
0477 {
0478 struct mlxsw_linecards *linecards = linecard->linecards;
0479 struct mlxsw_linecards_event_ops_item *item;
0480
0481 mutex_lock(&linecards->event_ops_list_lock);
0482 list_for_each_entry(item, &linecards->event_ops_list, list)
0483 mlxsw_linecard_event_op_call(linecard,
0484 item->event_ops->got_inactive,
0485 item->priv);
0486 mutex_unlock(&linecards->event_ops_list_lock);
0487 }
0488
0489 static void
0490 mlxsw_linecards_event_ops_register_call(struct mlxsw_linecards *linecards,
0491 const struct mlxsw_linecards_event_ops_item *item)
0492 {
0493 struct mlxsw_linecard *linecard;
0494 int i;
0495
0496 for (i = 0; i < linecards->count; i++) {
0497 linecard = mlxsw_linecard_get(linecards, i + 1);
0498 mutex_lock(&linecard->lock);
0499 if (linecard->active)
0500 mlxsw_linecard_event_op_call(linecard,
0501 item->event_ops->got_active,
0502 item->priv);
0503 mutex_unlock(&linecard->lock);
0504 }
0505 }
0506
0507 static void
0508 mlxsw_linecards_event_ops_unregister_call(struct mlxsw_linecards *linecards,
0509 const struct mlxsw_linecards_event_ops_item *item)
0510 {
0511 struct mlxsw_linecard *linecard;
0512 int i;
0513
0514 for (i = 0; i < linecards->count; i++) {
0515 linecard = mlxsw_linecard_get(linecards, i + 1);
0516 mutex_lock(&linecard->lock);
0517 if (linecard->active)
0518 mlxsw_linecard_event_op_call(linecard,
0519 item->event_ops->got_inactive,
0520 item->priv);
0521 mutex_unlock(&linecard->lock);
0522 }
0523 }
0524
0525 int mlxsw_linecards_event_ops_register(struct mlxsw_core *mlxsw_core,
0526 struct mlxsw_linecards_event_ops *ops,
0527 void *priv)
0528 {
0529 struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
0530 struct mlxsw_linecards_event_ops_item *item;
0531
0532 if (!linecards)
0533 return 0;
0534 item = kzalloc(sizeof(*item), GFP_KERNEL);
0535 if (!item)
0536 return -ENOMEM;
0537 item->event_ops = ops;
0538 item->priv = priv;
0539
0540 mutex_lock(&linecards->event_ops_list_lock);
0541 list_add_tail(&item->list, &linecards->event_ops_list);
0542 mutex_unlock(&linecards->event_ops_list_lock);
0543 mlxsw_linecards_event_ops_register_call(linecards, item);
0544 return 0;
0545 }
0546 EXPORT_SYMBOL(mlxsw_linecards_event_ops_register);
0547
0548 void mlxsw_linecards_event_ops_unregister(struct mlxsw_core *mlxsw_core,
0549 struct mlxsw_linecards_event_ops *ops,
0550 void *priv)
0551 {
0552 struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
0553 struct mlxsw_linecards_event_ops_item *item, *tmp;
0554 bool found = false;
0555
0556 if (!linecards)
0557 return;
0558 mutex_lock(&linecards->event_ops_list_lock);
0559 list_for_each_entry_safe(item, tmp, &linecards->event_ops_list, list) {
0560 if (item->event_ops == ops && item->priv == priv) {
0561 list_del(&item->list);
0562 found = true;
0563 break;
0564 }
0565 }
0566 mutex_unlock(&linecards->event_ops_list_lock);
0567
0568 if (!found)
0569 return;
0570 mlxsw_linecards_event_ops_unregister_call(linecards, item);
0571 kfree(item);
0572 }
0573 EXPORT_SYMBOL(mlxsw_linecards_event_ops_unregister);
0574
0575 int mlxsw_linecard_devlink_info_get(struct mlxsw_linecard *linecard,
0576 struct devlink_info_req *req,
0577 struct netlink_ext_ack *extack)
0578 {
0579 char buf[32];
0580 int err;
0581
0582 mutex_lock(&linecard->lock);
0583 if (WARN_ON(!linecard->provisioned)) {
0584 err = -EOPNOTSUPP;
0585 goto unlock;
0586 }
0587
0588 sprintf(buf, "%d", linecard->hw_revision);
0589 err = devlink_info_version_fixed_put(req, "hw.revision", buf);
0590 if (err)
0591 goto unlock;
0592
0593 sprintf(buf, "%d", linecard->ini_version);
0594 err = devlink_info_version_running_put(req, "ini.version", buf);
0595 if (err)
0596 goto unlock;
0597
0598 if (linecard->active) {
0599 struct mlxsw_linecard_device_info *info = &linecard->device.info;
0600
0601 err = devlink_info_version_fixed_put(req,
0602 DEVLINK_INFO_VERSION_GENERIC_FW_PSID,
0603 info->psid);
0604
0605 sprintf(buf, "%u.%u.%u", info->fw_major, info->fw_minor,
0606 info->fw_sub_minor);
0607 err = devlink_info_version_running_put(req,
0608 DEVLINK_INFO_VERSION_GENERIC_FW,
0609 buf);
0610 if (err)
0611 goto unlock;
0612 }
0613
0614 unlock:
0615 mutex_unlock(&linecard->lock);
0616 return err;
0617 }
0618
0619 static int
0620 mlxsw_linecard_provision_set(struct mlxsw_linecard *linecard, u8 card_type,
0621 u16 hw_revision, u16 ini_version)
0622 {
0623 struct mlxsw_linecards *linecards = linecard->linecards;
0624 const char *type;
0625 int err;
0626
0627 type = mlxsw_linecard_types_lookup(linecards, card_type);
0628 mlxsw_linecard_status_event_done(linecard,
0629 MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION);
0630 if (!type) {
0631
0632
0633
0634
0635
0636
0637 type = mlxsw_linecard_type_name(linecard);
0638 if (IS_ERR(type)) {
0639 mlxsw_linecard_provision_fail(linecard);
0640 return PTR_ERR(type);
0641 }
0642 }
0643 linecard->provisioned = true;
0644 linecard->hw_revision = hw_revision;
0645 linecard->ini_version = ini_version;
0646
0647 err = mlxsw_linecard_bdev_add(linecard);
0648 if (err) {
0649 linecard->provisioned = false;
0650 mlxsw_linecard_provision_fail(linecard);
0651 return err;
0652 }
0653
0654 devlink_linecard_provision_set(linecard->devlink_linecard, type);
0655 return 0;
0656 }
0657
0658 static void mlxsw_linecard_provision_clear(struct mlxsw_linecard *linecard)
0659 {
0660 mlxsw_linecard_status_event_done(linecard,
0661 MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION);
0662 mlxsw_linecard_bdev_del(linecard);
0663 linecard->provisioned = false;
0664 devlink_linecard_provision_clear(linecard->devlink_linecard);
0665 }
0666
0667 static int mlxsw_linecard_ready_set(struct mlxsw_linecard *linecard)
0668 {
0669 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
0670 char mddc_pl[MLXSW_REG_MDDC_LEN];
0671 int err;
0672
0673 err = mlxsw_linecard_device_info_update(linecard);
0674 if (err)
0675 return err;
0676
0677 mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, false, true);
0678 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl);
0679 if (err)
0680 return err;
0681 linecard->ready = true;
0682 return 0;
0683 }
0684
0685 static int mlxsw_linecard_ready_clear(struct mlxsw_linecard *linecard)
0686 {
0687 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
0688 char mddc_pl[MLXSW_REG_MDDC_LEN];
0689 int err;
0690
0691 mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, false, false);
0692 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl);
0693 if (err)
0694 return err;
0695 linecard->ready = false;
0696 return 0;
0697 }
0698
0699 static void mlxsw_linecard_active_set(struct mlxsw_linecard *linecard)
0700 {
0701 mlxsw_linecard_active_ops_call(linecard);
0702 linecard->active = true;
0703 devlink_linecard_activate(linecard->devlink_linecard);
0704 }
0705
0706 static void mlxsw_linecard_active_clear(struct mlxsw_linecard *linecard)
0707 {
0708 mlxsw_linecard_inactive_ops_call(linecard);
0709 linecard->active = false;
0710 devlink_linecard_deactivate(linecard->devlink_linecard);
0711 }
0712
0713 static int mlxsw_linecard_status_process(struct mlxsw_linecards *linecards,
0714 struct mlxsw_linecard *linecard,
0715 const char *mddq_pl)
0716 {
0717 enum mlxsw_reg_mddq_slot_info_ready ready;
0718 bool provisioned, sr_valid, active;
0719 u16 ini_version, hw_revision;
0720 u8 slot_index, card_type;
0721 int err = 0;
0722
0723 mlxsw_reg_mddq_slot_info_unpack(mddq_pl, &slot_index, &provisioned,
0724 &sr_valid, &ready, &active,
0725 &hw_revision, &ini_version,
0726 &card_type);
0727
0728 if (linecard) {
0729 if (WARN_ON(slot_index != linecard->slot_index))
0730 return -EINVAL;
0731 } else {
0732 if (WARN_ON(slot_index > linecards->count))
0733 return -EINVAL;
0734 linecard = mlxsw_linecard_get(linecards, slot_index);
0735 }
0736
0737 mutex_lock(&linecard->lock);
0738
0739 if (provisioned && linecard->provisioned != provisioned) {
0740 err = mlxsw_linecard_provision_set(linecard, card_type,
0741 hw_revision, ini_version);
0742 if (err)
0743 goto out;
0744 }
0745
0746 if (ready == MLXSW_REG_MDDQ_SLOT_INFO_READY_READY && !linecard->ready) {
0747 err = mlxsw_linecard_ready_set(linecard);
0748 if (err)
0749 goto out;
0750 }
0751
0752 if (active && linecard->active != active)
0753 mlxsw_linecard_active_set(linecard);
0754
0755 if (!active && linecard->active != active)
0756 mlxsw_linecard_active_clear(linecard);
0757
0758 if (ready != MLXSW_REG_MDDQ_SLOT_INFO_READY_READY &&
0759 linecard->ready) {
0760 err = mlxsw_linecard_ready_clear(linecard);
0761 if (err)
0762 goto out;
0763 }
0764
0765 if (!provisioned && linecard->provisioned != provisioned)
0766 mlxsw_linecard_provision_clear(linecard);
0767
0768 out:
0769 mutex_unlock(&linecard->lock);
0770 return err;
0771 }
0772
0773 static int mlxsw_linecard_status_get_and_process(struct mlxsw_core *mlxsw_core,
0774 struct mlxsw_linecards *linecards,
0775 struct mlxsw_linecard *linecard)
0776 {
0777 char mddq_pl[MLXSW_REG_MDDQ_LEN];
0778 int err;
0779
0780 mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, false);
0781 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl);
0782 if (err)
0783 return err;
0784
0785 return mlxsw_linecard_status_process(linecards, linecard, mddq_pl);
0786 }
0787
0788 static const char * const mlxsw_linecard_status_event_type_name[] = {
0789 [MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION] = "provision",
0790 [MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION] = "unprovision",
0791 };
0792
0793 static void mlxsw_linecard_status_event_to_work(struct work_struct *work)
0794 {
0795 struct mlxsw_linecard *linecard =
0796 container_of(work, struct mlxsw_linecard,
0797 status_event_to_dw.work);
0798
0799 mutex_lock(&linecard->lock);
0800 dev_err(linecard->linecards->bus_info->dev, "linecard %u: Timeout reached waiting on %s status event",
0801 linecard->slot_index,
0802 mlxsw_linecard_status_event_type_name[linecard->status_event_type_to]);
0803 mlxsw_linecard_provision_fail(linecard);
0804 mutex_unlock(&linecard->lock);
0805 }
0806
0807 static int __mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard)
0808 {
0809 dev_info(linecard->linecards->bus_info->dev, "linecard %u: Clearing FSM state error",
0810 linecard->slot_index);
0811 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
0812 MLXSW_REG_MBCT_OP_CLEAR_ERRORS, false);
0813 return mlxsw_reg_write(linecard->linecards->mlxsw_core,
0814 MLXSW_REG(mbct), linecard->mbct_pl);
0815 }
0816
0817 static int mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard,
0818 enum mlxsw_reg_mbct_fsm_state fsm_state)
0819 {
0820 if (fsm_state != MLXSW_REG_MBCT_FSM_STATE_ERROR)
0821 return 0;
0822 return __mlxsw_linecard_fix_fsm_state(linecard);
0823 }
0824
0825 static int
0826 mlxsw_linecard_query_ini_status(struct mlxsw_linecard *linecard,
0827 enum mlxsw_reg_mbct_status *status,
0828 enum mlxsw_reg_mbct_fsm_state *fsm_state,
0829 struct netlink_ext_ack *extack)
0830 {
0831 int err;
0832
0833 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
0834 MLXSW_REG_MBCT_OP_QUERY_STATUS, false);
0835 err = mlxsw_reg_query(linecard->linecards->mlxsw_core, MLXSW_REG(mbct),
0836 linecard->mbct_pl);
0837 if (err) {
0838 NL_SET_ERR_MSG_MOD(extack, "Failed to query linecard INI status");
0839 return err;
0840 }
0841 mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, status, fsm_state);
0842 return err;
0843 }
0844
0845 static int
0846 mlxsw_linecard_ini_transfer(struct mlxsw_core *mlxsw_core,
0847 struct mlxsw_linecard *linecard,
0848 const struct mlxsw_linecard_ini_file *ini_file,
0849 struct netlink_ext_ack *extack)
0850 {
0851 enum mlxsw_reg_mbct_fsm_state fsm_state;
0852 enum mlxsw_reg_mbct_status status;
0853 size_t size_left;
0854 const u8 *data;
0855 int err;
0856
0857 size_left = le16_to_cpu(ini_file->size);
0858 data = ini_file->data;
0859 while (size_left) {
0860 size_t data_size = MLXSW_REG_MBCT_DATA_LEN;
0861 bool is_last = false;
0862
0863 if (size_left <= MLXSW_REG_MBCT_DATA_LEN) {
0864 data_size = size_left;
0865 is_last = true;
0866 }
0867
0868 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
0869 MLXSW_REG_MBCT_OP_DATA_TRANSFER, false);
0870 mlxsw_reg_mbct_dt_pack(linecard->mbct_pl, data_size,
0871 is_last, data);
0872 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct),
0873 linecard->mbct_pl);
0874 if (err) {
0875 NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI data transfer");
0876 return err;
0877 }
0878 mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL,
0879 &status, &fsm_state);
0880 if ((!is_last && status != MLXSW_REG_MBCT_STATUS_PART_DATA) ||
0881 (is_last && status != MLXSW_REG_MBCT_STATUS_LAST_DATA)) {
0882 NL_SET_ERR_MSG_MOD(extack, "Failed to transfer linecard INI data");
0883 mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
0884 return -EINVAL;
0885 }
0886 size_left -= data_size;
0887 data += data_size;
0888 }
0889
0890 return 0;
0891 }
0892
0893 static int
0894 mlxsw_linecard_ini_erase(struct mlxsw_core *mlxsw_core,
0895 struct mlxsw_linecard *linecard,
0896 struct netlink_ext_ack *extack)
0897 {
0898 enum mlxsw_reg_mbct_fsm_state fsm_state;
0899 enum mlxsw_reg_mbct_status status;
0900 int err;
0901
0902 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
0903 MLXSW_REG_MBCT_OP_ERASE_INI_IMAGE, false);
0904 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct),
0905 linecard->mbct_pl);
0906 if (err) {
0907 NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI erase");
0908 return err;
0909 }
0910 mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state);
0911 switch (status) {
0912 case MLXSW_REG_MBCT_STATUS_ERASE_COMPLETE:
0913 break;
0914 default:
0915
0916 fallthrough;
0917 case MLXSW_REG_MBCT_STATUS_ERASE_FAILED:
0918 NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI");
0919 goto fix_fsm_err_out;
0920 case MLXSW_REG_MBCT_STATUS_ERROR_INI_IN_USE:
0921 NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI while being used");
0922 goto fix_fsm_err_out;
0923 }
0924 return 0;
0925
0926 fix_fsm_err_out:
0927 mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
0928 return -EINVAL;
0929 }
0930
0931 static void mlxsw_linecard_bct_process(struct mlxsw_core *mlxsw_core,
0932 const char *mbct_pl)
0933 {
0934 struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
0935 enum mlxsw_reg_mbct_fsm_state fsm_state;
0936 enum mlxsw_reg_mbct_status status;
0937 struct mlxsw_linecard *linecard;
0938 u8 slot_index;
0939
0940 mlxsw_reg_mbct_unpack(mbct_pl, &slot_index, &status, &fsm_state);
0941 if (WARN_ON(slot_index > linecards->count))
0942 return;
0943 linecard = mlxsw_linecard_get(linecards, slot_index);
0944 mutex_lock(&linecard->lock);
0945 if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) {
0946 dev_err(linecards->bus_info->dev, "linecard %u: Failed to activate INI",
0947 linecard->slot_index);
0948 goto fix_fsm_out;
0949 }
0950 mutex_unlock(&linecard->lock);
0951 return;
0952
0953 fix_fsm_out:
0954 mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
0955 mlxsw_linecard_provision_fail(linecard);
0956 mutex_unlock(&linecard->lock);
0957 }
0958
0959 static int
0960 mlxsw_linecard_ini_activate(struct mlxsw_core *mlxsw_core,
0961 struct mlxsw_linecard *linecard,
0962 struct netlink_ext_ack *extack)
0963 {
0964 enum mlxsw_reg_mbct_fsm_state fsm_state;
0965 enum mlxsw_reg_mbct_status status;
0966 int err;
0967
0968 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
0969 MLXSW_REG_MBCT_OP_ACTIVATE, true);
0970 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct), linecard->mbct_pl);
0971 if (err) {
0972 NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI activation");
0973 return err;
0974 }
0975 mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state);
0976 if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) {
0977 NL_SET_ERR_MSG_MOD(extack, "Failed to activate linecard INI");
0978 goto fix_fsm_err_out;
0979 }
0980
0981 return 0;
0982
0983 fix_fsm_err_out:
0984 mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
0985 return -EINVAL;
0986 }
0987
0988 #define MLXSW_LINECARD_INI_WAIT_RETRIES 10
0989 #define MLXSW_LINECARD_INI_WAIT_MS 500
0990
0991 static int
0992 mlxsw_linecard_ini_in_use_wait(struct mlxsw_core *mlxsw_core,
0993 struct mlxsw_linecard *linecard,
0994 struct netlink_ext_ack *extack)
0995 {
0996 enum mlxsw_reg_mbct_fsm_state fsm_state;
0997 enum mlxsw_reg_mbct_status status;
0998 unsigned int ini_wait_retries = 0;
0999 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
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
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
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 }