Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
0002 /* Copyright (c) 2022 NVIDIA Corporation and Mellanox Technologies. All rights reserved */
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) /* We care only about flashable ones. */
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         /* It is possible for a line card to be provisioned before
0632          * driver initialization. Due to a missing INI bundle file
0633          * or an outdated one, the queried card's type might not
0634          * be recognized by the driver. In this case, try to query
0635          * the card's name from the device.
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         /* Should not happen */
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     /* Make sure all scheduled events are processed */
1289     mlxsw_core_flush_owq();
1290     if (linecard->active)
1291         mlxsw_linecard_active_clear(linecard);
1292     mlxsw_linecard_bdev_del(linecard);
1293     devlink_linecard_destroy(linecard->devlink_linecard);
1294     mutex_destroy(&linecard->lock);
1295 }
1296 
1297 /*       LINECARDS INI BUNDLE FILE
1298  *  +----------------------------------+
1299  *  |        MAGIC ("NVLCINI+")        |
1300  *  +----------------------------------+     +--------------------+
1301  *  |  INI 0                           +---> | __le16 size        |
1302  *  +----------------------------------+     | __be16 hw_revision |
1303  *  |  INI 1                           |     | __be16 ini_version |
1304  *  +----------------------------------+     | u8 __dontcare[3]   |
1305  *  |  ...                             |     | u8 type            |
1306  *  +----------------------------------+     | u8 name[20]        |
1307  *  |  INI N                           |     | ...                |
1308  *  +----------------------------------+     +--------------------+
1309  */
1310 
1311 #define MLXSW_LINECARDS_INI_BUNDLE_MAGIC "NVLCINI+"
1312 
1313 static int
1314 mlxsw_linecard_types_file_validate(struct mlxsw_linecards *linecards,
1315                    struct mlxsw_linecard_types_info *types_info)
1316 {
1317     size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC);
1318     struct mlxsw_linecard_ini_file *ini_file;
1319     size_t size = types_info->data_size;
1320     const u8 *data = types_info->data;
1321     unsigned int count = 0;
1322     u16 ini_file_size;
1323 
1324     if (size < magic_size) {
1325         dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file size, smaller than magic size\n");
1326         return -EINVAL;
1327     }
1328     if (memcmp(data, MLXSW_LINECARDS_INI_BUNDLE_MAGIC, magic_size)) {
1329         dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file magic pattern\n");
1330         return -EINVAL;
1331     }
1332 
1333     data += magic_size;
1334     size -= magic_size;
1335 
1336     while (size > 0) {
1337         if (size < sizeof(*ini_file)) {
1338             dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI which is smaller than bare minimum\n");
1339             return -EINVAL;
1340         }
1341         ini_file = (struct mlxsw_linecard_ini_file *) data;
1342         ini_file_size = le16_to_cpu(ini_file->size);
1343         if (ini_file_size + sizeof(__le16) > size) {
1344             dev_warn(linecards->bus_info->dev, "Linecards INIs file appears to be truncated\n");
1345             return -EINVAL;
1346         }
1347         if (ini_file_size % 4) {
1348             dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI with invalid size\n");
1349             return -EINVAL;
1350         }
1351         data += ini_file_size + sizeof(__le16);
1352         size -= ini_file_size + sizeof(__le16);
1353         count++;
1354     }
1355     if (!count) {
1356         dev_warn(linecards->bus_info->dev, "Linecards INIs file does not contain any INI\n");
1357         return -EINVAL;
1358     }
1359     types_info->count = count;
1360     return 0;
1361 }
1362 
1363 static void
1364 mlxsw_linecard_types_file_parse(struct mlxsw_linecard_types_info *types_info)
1365 {
1366     size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC);
1367     size_t size = types_info->data_size - magic_size;
1368     const u8 *data = types_info->data + magic_size;
1369     struct mlxsw_linecard_ini_file *ini_file;
1370     unsigned int count = 0;
1371     u16 ini_file_size;
1372     int i;
1373 
1374     while (size) {
1375         ini_file = (struct mlxsw_linecard_ini_file *) data;
1376         ini_file_size = le16_to_cpu(ini_file->size);
1377         for (i = 0; i < ini_file_size / 4; i++) {
1378             u32 *val = &((u32 *) ini_file->data)[i];
1379 
1380             *val = swab32(*val);
1381         }
1382         types_info->ini_files[count] = ini_file;
1383         data += ini_file_size + sizeof(__le16);
1384         size -= ini_file_size + sizeof(__le16);
1385         count++;
1386     }
1387 }
1388 
1389 #define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT \
1390     "mellanox/lc_ini_bundle_%u_%u.bin"
1391 #define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN \
1392     (sizeof(MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT) + 4)
1393 
1394 static int mlxsw_linecard_types_init(struct mlxsw_core *mlxsw_core,
1395                      struct mlxsw_linecards *linecards)
1396 {
1397     const struct mlxsw_fw_rev *rev = &linecards->bus_info->fw_rev;
1398     char filename[MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN];
1399     struct mlxsw_linecard_types_info *types_info;
1400     const struct firmware *firmware;
1401     int err;
1402 
1403     err = snprintf(filename, sizeof(filename),
1404                MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT,
1405                rev->minor, rev->subminor);
1406     WARN_ON(err >= sizeof(filename));
1407 
1408     err = request_firmware_direct(&firmware, filename,
1409                       linecards->bus_info->dev);
1410     if (err) {
1411         dev_warn(linecards->bus_info->dev, "Could not request linecards INI file \"%s\", provisioning will not be possible\n",
1412              filename);
1413         return 0;
1414     }
1415 
1416     types_info = kzalloc(sizeof(*types_info), GFP_KERNEL);
1417     if (!types_info) {
1418         release_firmware(firmware);
1419         return -ENOMEM;
1420     }
1421     linecards->types_info = types_info;
1422 
1423     types_info->data_size = firmware->size;
1424     types_info->data = vmalloc(types_info->data_size);
1425     if (!types_info->data) {
1426         err = -ENOMEM;
1427         release_firmware(firmware);
1428         goto err_data_alloc;
1429     }
1430     memcpy(types_info->data, firmware->data, types_info->data_size);
1431     release_firmware(firmware);
1432 
1433     err = mlxsw_linecard_types_file_validate(linecards, types_info);
1434     if (err) {
1435         err = 0;
1436         goto err_type_file_file_validate;
1437     }
1438 
1439     types_info->ini_files = kmalloc_array(types_info->count,
1440                           sizeof(struct mlxsw_linecard_ini_file *),
1441                           GFP_KERNEL);
1442     if (!types_info->ini_files) {
1443         err = -ENOMEM;
1444         goto err_ini_files_alloc;
1445     }
1446 
1447     mlxsw_linecard_types_file_parse(types_info);
1448 
1449     return 0;
1450 
1451 err_ini_files_alloc:
1452 err_type_file_file_validate:
1453     vfree(types_info->data);
1454 err_data_alloc:
1455     kfree(types_info);
1456     return err;
1457 }
1458 
1459 static void mlxsw_linecard_types_fini(struct mlxsw_linecards *linecards)
1460 {
1461     struct mlxsw_linecard_types_info *types_info = linecards->types_info;
1462 
1463     if (!types_info)
1464         return;
1465     kfree(types_info->ini_files);
1466     vfree(types_info->data);
1467     kfree(types_info);
1468 }
1469 
1470 int mlxsw_linecards_init(struct mlxsw_core *mlxsw_core,
1471              const struct mlxsw_bus_info *bus_info)
1472 {
1473     char mgpir_pl[MLXSW_REG_MGPIR_LEN];
1474     struct mlxsw_linecards *linecards;
1475     u8 slot_count;
1476     int err;
1477     int i;
1478 
1479     mlxsw_reg_mgpir_pack(mgpir_pl, 0);
1480     err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl);
1481     if (err)
1482         return err;
1483 
1484     mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL,
1485                    NULL, &slot_count);
1486     if (!slot_count)
1487         return 0;
1488 
1489     linecards = vzalloc(struct_size(linecards, linecards, slot_count));
1490     if (!linecards)
1491         return -ENOMEM;
1492     linecards->count = slot_count;
1493     linecards->mlxsw_core = mlxsw_core;
1494     linecards->bus_info = bus_info;
1495     INIT_LIST_HEAD(&linecards->event_ops_list);
1496     mutex_init(&linecards->event_ops_list_lock);
1497 
1498     err = mlxsw_linecard_types_init(mlxsw_core, linecards);
1499     if (err)
1500         goto err_types_init;
1501 
1502     err = mlxsw_core_traps_register(mlxsw_core, mlxsw_linecard_listener,
1503                     ARRAY_SIZE(mlxsw_linecard_listener),
1504                     mlxsw_core);
1505     if (err)
1506         goto err_traps_register;
1507 
1508     mlxsw_core_linecards_set(mlxsw_core, linecards);
1509 
1510     for (i = 0; i < linecards->count; i++) {
1511         err = mlxsw_linecard_init(mlxsw_core, linecards, i + 1);
1512         if (err)
1513             goto err_linecard_init;
1514     }
1515 
1516     return 0;
1517 
1518 err_linecard_init:
1519     for (i--; i >= 0; i--)
1520         mlxsw_linecard_fini(mlxsw_core, linecards, i + 1);
1521     mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener,
1522                     ARRAY_SIZE(mlxsw_linecard_listener),
1523                     mlxsw_core);
1524 err_traps_register:
1525     mlxsw_linecard_types_fini(linecards);
1526 err_types_init:
1527     vfree(linecards);
1528     return err;
1529 }
1530 
1531 void mlxsw_linecards_fini(struct mlxsw_core *mlxsw_core)
1532 {
1533     struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
1534     int i;
1535 
1536     if (!linecards)
1537         return;
1538     for (i = 0; i < linecards->count; i++)
1539         mlxsw_linecard_fini(mlxsw_core, linecards, i + 1);
1540     mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener,
1541                     ARRAY_SIZE(mlxsw_linecard_listener),
1542                     mlxsw_core);
1543     mlxsw_linecard_types_fini(linecards);
1544     mutex_destroy(&linecards->event_ops_list_lock);
1545     WARN_ON(!list_empty(&linecards->event_ops_list));
1546     vfree(linecards);
1547 }