Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/net/ethernet/mellanox/mlxsw/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 45 kB image not shown  

Quelle  core_linecards.c   Sprache: C

 
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/* Copyright (c) 2022 NVIDIA Corporation and Mellanox Technologies. All rights reserved */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/workqueue.h>
#include <linux/gfp.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/vmalloc.h>

#include "core.h"
#include "../mlxfw/mlxfw.h"

struct mlxsw_linecard_ini_file {
 __le16 size;
 union {
  u8 data[0];
  struct {
   __be16 hw_revision;
   __be16 ini_version;
   u8 __dontcare[3];
   u8 type;
   u8 name[20];
  } format;
 };
};

struct mlxsw_linecard_types_info {
 struct mlxsw_linecard_ini_file **ini_files;
 unsigned int count;
 size_t data_size;
 char *data;
};

#define MLXSW_LINECARD_STATUS_EVENT_TO (10 * MSEC_PER_SEC)

static void
mlxsw_linecard_status_event_to_schedule(struct mlxsw_linecard *linecard,
     enum mlxsw_linecard_status_event_type status_event_type)
{
 cancel_delayed_work_sync(&linecard->status_event_to_dw);
 linecard->status_event_type_to = status_event_type;
 mlxsw_core_schedule_dw(&linecard->status_event_to_dw,
          msecs_to_jiffies(MLXSW_LINECARD_STATUS_EVENT_TO));
}

static void
mlxsw_linecard_status_event_done(struct mlxsw_linecard *linecard,
     enum mlxsw_linecard_status_event_type status_event_type)
{
 if (linecard->status_event_type_to == status_event_type)
  cancel_delayed_work_sync(&linecard->status_event_to_dw);
}

static const char *
mlxsw_linecard_types_lookup(struct mlxsw_linecards *linecards, u8 card_type)
{
 struct mlxsw_linecard_types_info *types_info;
 struct mlxsw_linecard_ini_file *ini_file;
 int i;

 types_info = linecards->types_info;
 if (!types_info)
  return NULL;
 for (i = 0; i < types_info->count; i++) {
  ini_file = linecards->types_info->ini_files[i];
  if (ini_file->format.type == card_type)
   return ini_file->format.name;
 }
 return NULL;
}

static const char *mlxsw_linecard_type_name(struct mlxsw_linecard *linecard)
{
 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
 char mddq_pl[MLXSW_REG_MDDQ_LEN];
 int err;

 mlxsw_reg_mddq_slot_name_pack(mddq_pl, linecard->slot_index);
 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl);
 if (err)
  return ERR_PTR(err);
 mlxsw_reg_mddq_slot_name_unpack(mddq_pl, linecard->name);
 return linecard->name;
}

struct mlxsw_linecard_device_fw_info {
 struct mlxfw_dev mlxfw_dev;
 struct mlxsw_core *mlxsw_core;
 struct mlxsw_linecard *linecard;
};

static int mlxsw_linecard_device_fw_component_query(struct mlxfw_dev *mlxfw_dev,
          u16 component_index,
          u32 *p_max_size,
          u8 *p_align_bits,
          u16 *p_max_write_size)
{
 struct mlxsw_linecard_device_fw_info *info =
  container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
        mlxfw_dev);
 struct mlxsw_linecard *linecard = info->linecard;
 struct mlxsw_core *mlxsw_core = info->mlxsw_core;
 char mddt_pl[MLXSW_REG_MDDT_LEN];
 char *mcqi_pl;
 int err;

 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
       linecard->device.index,
       MLXSW_REG_MDDT_METHOD_QUERY,
       MLXSW_REG(mcqi), &mcqi_pl);

 mlxsw_reg_mcqi_pack(mcqi_pl, component_index);
 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
 if (err)
  return err;
 mlxsw_reg_mcqi_unpack(mcqi_pl, p_max_size, p_align_bits,
         p_max_write_size);

 *p_align_bits = max_t(u8, *p_align_bits, 2);
 *p_max_write_size = min_t(u16, *p_max_write_size,
      MLXSW_REG_MCDA_MAX_DATA_LEN);
 return 0;
}

static int mlxsw_linecard_device_fw_fsm_lock(struct mlxfw_dev *mlxfw_dev,
          u32 *fwhandle)
{
 struct mlxsw_linecard_device_fw_info *info =
  container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
        mlxfw_dev);
 struct mlxsw_linecard *linecard = info->linecard;
 struct mlxsw_core *mlxsw_core = info->mlxsw_core;
 char mddt_pl[MLXSW_REG_MDDT_LEN];
 u8 control_state;
 char *mcc_pl;
 int err;

 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
       linecard->device.index,
       MLXSW_REG_MDDT_METHOD_QUERY,
       MLXSW_REG(mcc), &mcc_pl);
 mlxsw_reg_mcc_pack(mcc_pl, 0, 0, 0, 0);
 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
 if (err)
  return err;

 mlxsw_reg_mcc_unpack(mcc_pl, fwhandle, NULL, &control_state);
 if (control_state != MLXFW_FSM_STATE_IDLE)
  return -EBUSY;

 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
       linecard->device.index,
       MLXSW_REG_MDDT_METHOD_WRITE,
       MLXSW_REG(mcc), &mcc_pl);
 mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_LOCK_UPDATE_HANDLE,
      0, *fwhandle, 0);
 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
}

static int
mlxsw_linecard_device_fw_fsm_component_update(struct mlxfw_dev *mlxfw_dev,
           u32 fwhandle,
           u16 component_index,
           u32 component_size)
{
 struct mlxsw_linecard_device_fw_info *info =
  container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
        mlxfw_dev);
 struct mlxsw_linecard *linecard = info->linecard;
 struct mlxsw_core *mlxsw_core = info->mlxsw_core;
 char mddt_pl[MLXSW_REG_MDDT_LEN];
 char *mcc_pl;

 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
       linecard->device.index,
       MLXSW_REG_MDDT_METHOD_WRITE,
       MLXSW_REG(mcc), &mcc_pl);
 mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_UPDATE_COMPONENT,
      component_index, fwhandle, component_size);
 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
}

static int
mlxsw_linecard_device_fw_fsm_block_download(struct mlxfw_dev *mlxfw_dev,
         u32 fwhandle, u8 *data,
         u16 size, u32 offset)
{
 struct mlxsw_linecard_device_fw_info *info =
  container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
        mlxfw_dev);
 struct mlxsw_linecard *linecard = info->linecard;
 struct mlxsw_core *mlxsw_core = info->mlxsw_core;
 char mddt_pl[MLXSW_REG_MDDT_LEN];
 char *mcda_pl;

 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
       linecard->device.index,
       MLXSW_REG_MDDT_METHOD_WRITE,
       MLXSW_REG(mcda), &mcda_pl);
 mlxsw_reg_mcda_pack(mcda_pl, fwhandle, offset, size, data);
 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
}

static int
mlxsw_linecard_device_fw_fsm_component_verify(struct mlxfw_dev *mlxfw_dev,
           u32 fwhandle, u16 component_index)
{
 struct mlxsw_linecard_device_fw_info *info =
  container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
        mlxfw_dev);
 struct mlxsw_linecard *linecard = info->linecard;
 struct mlxsw_core *mlxsw_core = info->mlxsw_core;
 char mddt_pl[MLXSW_REG_MDDT_LEN];
 char *mcc_pl;

 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
       linecard->device.index,
       MLXSW_REG_MDDT_METHOD_WRITE,
       MLXSW_REG(mcc), &mcc_pl);
 mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_VERIFY_COMPONENT,
      component_index, fwhandle, 0);
 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
}

static int mlxsw_linecard_device_fw_fsm_activate(struct mlxfw_dev *mlxfw_dev,
       u32 fwhandle)
{
 struct mlxsw_linecard_device_fw_info *info =
  container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
        mlxfw_dev);
 struct mlxsw_linecard *linecard = info->linecard;
 struct mlxsw_core *mlxsw_core = info->mlxsw_core;
 char mddt_pl[MLXSW_REG_MDDT_LEN];
 char *mcc_pl;

 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
       linecard->device.index,
       MLXSW_REG_MDDT_METHOD_WRITE,
       MLXSW_REG(mcc), &mcc_pl);
 mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_ACTIVATE,
      0, fwhandle, 0);
 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
}

static int
mlxsw_linecard_device_fw_fsm_query_state(struct mlxfw_dev *mlxfw_dev,
      u32 fwhandle,
      enum mlxfw_fsm_state *fsm_state,
      enum mlxfw_fsm_state_err *fsm_state_err)
{
 struct mlxsw_linecard_device_fw_info *info =
  container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
        mlxfw_dev);
 struct mlxsw_linecard *linecard = info->linecard;
 struct mlxsw_core *mlxsw_core = info->mlxsw_core;
 char mddt_pl[MLXSW_REG_MDDT_LEN];
 u8 control_state;
 u8 error_code;
 char *mcc_pl;
 int err;

 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
       linecard->device.index,
       MLXSW_REG_MDDT_METHOD_QUERY,
       MLXSW_REG(mcc), &mcc_pl);
 mlxsw_reg_mcc_pack(mcc_pl, 0, 0, fwhandle, 0);
 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
 if (err)
  return err;

 mlxsw_reg_mcc_unpack(mcc_pl, NULL, &error_code, &control_state);
 *fsm_state = control_state;
 *fsm_state_err = min_t(enum mlxfw_fsm_state_err, error_code,
          MLXFW_FSM_STATE_ERR_MAX);
 return 0;
}

static void mlxsw_linecard_device_fw_fsm_cancel(struct mlxfw_dev *mlxfw_dev,
      u32 fwhandle)
{
 struct mlxsw_linecard_device_fw_info *info =
  container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
        mlxfw_dev);
 struct mlxsw_linecard *linecard = info->linecard;
 struct mlxsw_core *mlxsw_core = info->mlxsw_core;
 char mddt_pl[MLXSW_REG_MDDT_LEN];
 char *mcc_pl;

 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
       linecard->device.index,
       MLXSW_REG_MDDT_METHOD_WRITE,
       MLXSW_REG(mcc), &mcc_pl);
 mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_CANCEL,
      0, fwhandle, 0);
 mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
}

static void mlxsw_linecard_device_fw_fsm_release(struct mlxfw_dev *mlxfw_dev,
       u32 fwhandle)
{
 struct mlxsw_linecard_device_fw_info *info =
  container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
        mlxfw_dev);
 struct mlxsw_linecard *linecard = info->linecard;
 struct mlxsw_core *mlxsw_core = info->mlxsw_core;
 char mddt_pl[MLXSW_REG_MDDT_LEN];
 char *mcc_pl;

 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
       linecard->device.index,
       MLXSW_REG_MDDT_METHOD_WRITE,
       MLXSW_REG(mcc), &mcc_pl);
 mlxsw_reg_mcc_pack(mcc_pl,
      MLXSW_REG_MCC_INSTRUCTION_RELEASE_UPDATE_HANDLE,
      0, fwhandle, 0);
 mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
}

static const struct mlxfw_dev_ops mlxsw_linecard_device_dev_ops = {
 .component_query = mlxsw_linecard_device_fw_component_query,
 .fsm_lock  = mlxsw_linecard_device_fw_fsm_lock,
 .fsm_component_update = mlxsw_linecard_device_fw_fsm_component_update,
 .fsm_block_download = mlxsw_linecard_device_fw_fsm_block_download,
 .fsm_component_verify = mlxsw_linecard_device_fw_fsm_component_verify,
 .fsm_activate  = mlxsw_linecard_device_fw_fsm_activate,
 .fsm_query_state = mlxsw_linecard_device_fw_fsm_query_state,
 .fsm_cancel  = mlxsw_linecard_device_fw_fsm_cancel,
 .fsm_release  = mlxsw_linecard_device_fw_fsm_release,
};

int mlxsw_linecard_flash_update(struct devlink *linecard_devlink,
    struct mlxsw_linecard *linecard,
    const struct firmware *firmware,
    struct netlink_ext_ack *extack)
{
 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
 struct mlxsw_linecard_device_fw_info info = {
  .mlxfw_dev = {
   .ops = &mlxsw_linecard_device_dev_ops,
   .psid = linecard->device.info.psid,
   .psid_size = strlen(linecard->device.info.psid),
   .devlink = linecard_devlink,
  },
  .mlxsw_core = mlxsw_core,
  .linecard = linecard,
 };
 int err;

 mutex_lock(&linecard->lock);
 if (!linecard->active) {
  NL_SET_ERR_MSG_MOD(extack, "Only active line cards can be flashed");
  err = -EINVAL;
  goto unlock;
 }
 err = mlxsw_core_fw_flash(mlxsw_core, &info.mlxfw_dev,
      firmware, extack);
unlock:
 mutex_unlock(&linecard->lock);
 return err;
}

static int mlxsw_linecard_device_psid_get(struct mlxsw_linecard *linecard,
       u8 device_index, char *psid)
{
 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
 char mddt_pl[MLXSW_REG_MDDT_LEN];
 char *mgir_pl;
 int err;

 mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, device_index,
       MLXSW_REG_MDDT_METHOD_QUERY,
       MLXSW_REG(mgir), &mgir_pl);

 mlxsw_reg_mgir_pack(mgir_pl);
 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
 if (err)
  return err;

 mlxsw_reg_mgir_fw_info_psid_memcpy_from(mgir_pl, psid);
 return 0;
}

static int mlxsw_linecard_device_info_update(struct mlxsw_linecard *linecard)
{
 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
 bool flashable_found = false;
 u8 msg_seq = 0;

 do {
  struct mlxsw_linecard_device_info info;
  char mddq_pl[MLXSW_REG_MDDQ_LEN];
  bool flash_owner;
  bool data_valid;
  u8 device_index;
  int err;

  mlxsw_reg_mddq_device_info_pack(mddq_pl, linecard->slot_index,
      msg_seq);
  err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl);
  if (err)
   return err;
  mlxsw_reg_mddq_device_info_unpack(mddq_pl, &msg_seq,
        &data_valid, &flash_owner,
        &device_index,
        &info.fw_major,
        &info.fw_minor,
        &info.fw_sub_minor);
  if (!data_valid)
   break;
  if (!flash_owner) /* We care only about flashable ones. */
   continue;
  if (flashable_found) {
   dev_warn_once(linecard->linecards->bus_info->dev, "linecard %u: More flashable devices present, exposing only the first one\n",
          linecard->slot_index);
   return 0;
  }

  err = mlxsw_linecard_device_psid_get(linecard, device_index,
           info.psid);
  if (err)
   return err;

  linecard->device.info = info;
  linecard->device.index = device_index;
  flashable_found = true;
 } while (msg_seq);

 return 0;
}

static void mlxsw_linecard_provision_fail(struct mlxsw_linecard *linecard)
{
 linecard->provisioned = false;
 linecard->ready = false;
 linecard->active = false;
 devlink_linecard_provision_fail(linecard->devlink_linecard);
}

struct mlxsw_linecards_event_ops_item {
 struct list_head list;
 const struct mlxsw_linecards_event_ops *event_ops;
 void *priv;
};

static void
mlxsw_linecard_event_op_call(struct mlxsw_linecard *linecard,
        mlxsw_linecards_event_op_t *op, void *priv)
{
 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;

 if (!op)
  return;
 op(mlxsw_core, linecard->slot_index, priv);
}

static void
mlxsw_linecard_active_ops_call(struct mlxsw_linecard *linecard)
{
 struct mlxsw_linecards *linecards = linecard->linecards;
 struct mlxsw_linecards_event_ops_item *item;

 mutex_lock(&linecards->event_ops_list_lock);
 list_for_each_entry(item, &linecards->event_ops_list, list)
  mlxsw_linecard_event_op_call(linecard,
          item->event_ops->got_active,
          item->priv);
 mutex_unlock(&linecards->event_ops_list_lock);
}

static void
mlxsw_linecard_inactive_ops_call(struct mlxsw_linecard *linecard)
{
 struct mlxsw_linecards *linecards = linecard->linecards;
 struct mlxsw_linecards_event_ops_item *item;

 mutex_lock(&linecards->event_ops_list_lock);
 list_for_each_entry(item, &linecards->event_ops_list, list)
  mlxsw_linecard_event_op_call(linecard,
          item->event_ops->got_inactive,
          item->priv);
 mutex_unlock(&linecards->event_ops_list_lock);
}

static void
mlxsw_linecards_event_ops_register_call(struct mlxsw_linecards *linecards,
     const struct mlxsw_linecards_event_ops_item *item)
{
 struct mlxsw_linecard *linecard;
 int i;

 for (i = 0; i < linecards->count; i++) {
  linecard = mlxsw_linecard_get(linecards, i + 1);
  mutex_lock(&linecard->lock);
  if (linecard->active)
   mlxsw_linecard_event_op_call(linecard,
           item->event_ops->got_active,
           item->priv);
  mutex_unlock(&linecard->lock);
 }
}

static void
mlxsw_linecards_event_ops_unregister_call(struct mlxsw_linecards *linecards,
       const struct mlxsw_linecards_event_ops_item *item)
{
 struct mlxsw_linecard *linecard;
 int i;

 for (i = 0; i < linecards->count; i++) {
  linecard = mlxsw_linecard_get(linecards, i + 1);
  mutex_lock(&linecard->lock);
  if (linecard->active)
   mlxsw_linecard_event_op_call(linecard,
           item->event_ops->got_inactive,
           item->priv);
  mutex_unlock(&linecard->lock);
 }
}

int mlxsw_linecards_event_ops_register(struct mlxsw_core *mlxsw_core,
           struct mlxsw_linecards_event_ops *ops,
           void *priv)
{
 struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
 struct mlxsw_linecards_event_ops_item *item;

 if (!linecards)
  return 0;
 item = kzalloc(sizeof(*item), GFP_KERNEL);
 if (!item)
  return -ENOMEM;
 item->event_ops = ops;
 item->priv = priv;

 mutex_lock(&linecards->event_ops_list_lock);
 list_add_tail(&item->list, &linecards->event_ops_list);
 mutex_unlock(&linecards->event_ops_list_lock);
 mlxsw_linecards_event_ops_register_call(linecards, item);
 return 0;
}
EXPORT_SYMBOL(mlxsw_linecards_event_ops_register);

void mlxsw_linecards_event_ops_unregister(struct mlxsw_core *mlxsw_core,
       struct mlxsw_linecards_event_ops *ops,
       void *priv)
{
 struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
 struct mlxsw_linecards_event_ops_item *item, *tmp;
 bool found = false;

 if (!linecards)
  return;
 mutex_lock(&linecards->event_ops_list_lock);
 list_for_each_entry_safe(item, tmp, &linecards->event_ops_list, list) {
  if (item->event_ops == ops && item->priv == priv) {
   list_del(&item->list);
   found = true;
   break;
  }
 }
 mutex_unlock(&linecards->event_ops_list_lock);

 if (!found)
  return;
 mlxsw_linecards_event_ops_unregister_call(linecards, item);
 kfree(item);
}
EXPORT_SYMBOL(mlxsw_linecards_event_ops_unregister);

int mlxsw_linecard_devlink_info_get(struct mlxsw_linecard *linecard,
        struct devlink_info_req *req,
        struct netlink_ext_ack *extack)
{
 char buf[32];
 int err;

 mutex_lock(&linecard->lock);
 if (WARN_ON(!linecard->provisioned)) {
  err = -EOPNOTSUPP;
  goto unlock;
 }

 sprintf(buf, "%d", linecard->hw_revision);
 err = devlink_info_version_fixed_put(req, "hw.revision", buf);
 if (err)
  goto unlock;

 sprintf(buf, "%d", linecard->ini_version);
 err = devlink_info_version_running_put(req, "ini.version", buf);
 if (err)
  goto unlock;

 if (linecard->active) {
  struct mlxsw_linecard_device_info *info = &linecard->device.info;

  err = devlink_info_version_fixed_put(req,
           DEVLINK_INFO_VERSION_GENERIC_FW_PSID,
           info->psid);

  sprintf(buf, "%u.%u.%u", info->fw_major, info->fw_minor,
   info->fw_sub_minor);
  err = devlink_info_version_running_put(req,
             DEVLINK_INFO_VERSION_GENERIC_FW,
             buf);
  if (err)
   goto unlock;
 }

unlock:
 mutex_unlock(&linecard->lock);
 return err;
}

static int
mlxsw_linecard_provision_set(struct mlxsw_linecard *linecard, u8 card_type,
        u16 hw_revision, u16 ini_version)
{
 struct mlxsw_linecards *linecards = linecard->linecards;
 const char *type;
 int err;

 type = mlxsw_linecard_types_lookup(linecards, card_type);
 mlxsw_linecard_status_event_done(linecard,
      MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION);
 if (!type) {
  /* It is possible for a line card to be provisioned before
 * driver initialization. Due to a missing INI bundle file
 * or an outdated one, the queried card's type might not
 * be recognized by the driver. In this case, try to query
 * the card's name from the device.
 */

  type = mlxsw_linecard_type_name(linecard);
  if (IS_ERR(type)) {
   mlxsw_linecard_provision_fail(linecard);
   return PTR_ERR(type);
  }
 }
 linecard->provisioned = true;
 linecard->hw_revision = hw_revision;
 linecard->ini_version = ini_version;

 err = mlxsw_linecard_bdev_add(linecard);
 if (err) {
  linecard->provisioned = false;
  mlxsw_linecard_provision_fail(linecard);
  return err;
 }

 devlink_linecard_provision_set(linecard->devlink_linecard, type);
 return 0;
}

static void mlxsw_linecard_provision_clear(struct mlxsw_linecard *linecard)
{
 mlxsw_linecard_status_event_done(linecard,
      MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION);
 mlxsw_linecard_bdev_del(linecard);
 linecard->provisioned = false;
 devlink_linecard_provision_clear(linecard->devlink_linecard);
}

static int mlxsw_linecard_ready_set(struct mlxsw_linecard *linecard)
{
 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
 char mddc_pl[MLXSW_REG_MDDC_LEN];
 int err;

 err = mlxsw_linecard_device_info_update(linecard);
 if (err)
  return err;

 mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, falsetrue);
 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl);
 if (err)
  return err;
 linecard->ready = true;
 return 0;
}

static int mlxsw_linecard_ready_clear(struct mlxsw_linecard *linecard)
{
 struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
 char mddc_pl[MLXSW_REG_MDDC_LEN];
 int err;

 mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, falsefalse);
 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl);
 if (err)
  return err;
 linecard->ready = false;
 return 0;
}

static void mlxsw_linecard_active_set(struct mlxsw_linecard *linecard)
{
 mlxsw_linecard_active_ops_call(linecard);
 linecard->active = true;
 devlink_linecard_activate(linecard->devlink_linecard);
}

static void mlxsw_linecard_active_clear(struct mlxsw_linecard *linecard)
{
 mlxsw_linecard_inactive_ops_call(linecard);
 linecard->active = false;
 devlink_linecard_deactivate(linecard->devlink_linecard);
}

static int mlxsw_linecard_status_process(struct mlxsw_linecards *linecards,
      struct mlxsw_linecard *linecard,
      const char *mddq_pl)
{
 enum mlxsw_reg_mddq_slot_info_ready ready;
 bool provisioned, sr_valid, active;
 u16 ini_version, hw_revision;
 u8 slot_index, card_type;
 int err = 0;

 mlxsw_reg_mddq_slot_info_unpack(mddq_pl, &slot_index, &provisioned,
     &sr_valid, &ready, &active,
     &hw_revision, &ini_version,
     &card_type);

 if (linecard) {
  if (WARN_ON(slot_index != linecard->slot_index))
   return -EINVAL;
 } else {
  if (WARN_ON(slot_index > linecards->count))
   return -EINVAL;
  linecard = mlxsw_linecard_get(linecards, slot_index);
 }

 mutex_lock(&linecard->lock);

 if (provisioned && linecard->provisioned != provisioned) {
  err = mlxsw_linecard_provision_set(linecard, card_type,
         hw_revision, ini_version);
  if (err)
   goto out;
 }

 if (ready == MLXSW_REG_MDDQ_SLOT_INFO_READY_READY && !linecard->ready) {
  err = mlxsw_linecard_ready_set(linecard);
  if (err)
   goto out;
 }

 if (active && linecard->active != active)
  mlxsw_linecard_active_set(linecard);

 if (!active && linecard->active != active)
  mlxsw_linecard_active_clear(linecard);

 if (ready != MLXSW_REG_MDDQ_SLOT_INFO_READY_READY &&
     linecard->ready) {
  err = mlxsw_linecard_ready_clear(linecard);
  if (err)
   goto out;
 }

 if (!provisioned && linecard->provisioned != provisioned)
  mlxsw_linecard_provision_clear(linecard);

out:
 mutex_unlock(&linecard->lock);
 return err;
}

static int mlxsw_linecard_status_get_and_process(struct mlxsw_core *mlxsw_core,
       struct mlxsw_linecards *linecards,
       struct mlxsw_linecard *linecard)
{
 char mddq_pl[MLXSW_REG_MDDQ_LEN];
 int err;

 mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, false);
 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl);
 if (err)
  return err;

 return mlxsw_linecard_status_process(linecards, linecard, mddq_pl);
}

static void mlxsw_linecards_irq_event_handler(struct mlxsw_core *mlxsw_core)
{
 struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
 int i;

 /* Handle change of line card active state. */
 for (i = 0; i < linecards->count; i++) {
  struct mlxsw_linecard *linecard = mlxsw_linecard_get(linecards,
             i + 1);

  mlxsw_linecard_status_get_and_process(mlxsw_core, linecards,
            linecard);
 }
}

static const char * const mlxsw_linecard_status_event_type_name[] = {
 [MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION] = "provision",
 [MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION] = "unprovision",
};

static void mlxsw_linecard_status_event_to_work(struct work_struct *work)
{
 struct mlxsw_linecard *linecard =
  container_of(work, struct mlxsw_linecard,
        status_event_to_dw.work);

 mutex_lock(&linecard->lock);
 dev_err(linecard->linecards->bus_info->dev, "linecard %u: Timeout reached waiting on %s status event",
  linecard->slot_index,
  mlxsw_linecard_status_event_type_name[linecard->status_event_type_to]);
 mlxsw_linecard_provision_fail(linecard);
 mutex_unlock(&linecard->lock);
}

static int __mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard)
{
 dev_info(linecard->linecards->bus_info->dev, "linecard %u: Clearing FSM state error",
   linecard->slot_index);
 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
       MLXSW_REG_MBCT_OP_CLEAR_ERRORS, false);
 return mlxsw_reg_write(linecard->linecards->mlxsw_core,
          MLXSW_REG(mbct), linecard->mbct_pl);
}

static int mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard,
     enum mlxsw_reg_mbct_fsm_state fsm_state)
{
 if (fsm_state != MLXSW_REG_MBCT_FSM_STATE_ERROR)
  return 0;
 return __mlxsw_linecard_fix_fsm_state(linecard);
}

static int
mlxsw_linecard_query_ini_status(struct mlxsw_linecard *linecard,
    enum mlxsw_reg_mbct_status *status,
    enum mlxsw_reg_mbct_fsm_state *fsm_state,
    struct netlink_ext_ack *extack)
{
 int err;

 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
       MLXSW_REG_MBCT_OP_QUERY_STATUS, false);
 err = mlxsw_reg_query(linecard->linecards->mlxsw_core, MLXSW_REG(mbct),
         linecard->mbct_pl);
 if (err) {
  NL_SET_ERR_MSG_MOD(extack, "Failed to query linecard INI status");
  return err;
 }
 mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, status, fsm_state);
 return err;
}

static int
mlxsw_linecard_ini_transfer(struct mlxsw_core *mlxsw_core,
       struct mlxsw_linecard *linecard,
       const struct mlxsw_linecard_ini_file *ini_file,
       struct netlink_ext_ack *extack)
{
 enum mlxsw_reg_mbct_fsm_state fsm_state;
 enum mlxsw_reg_mbct_status status;
 size_t size_left;
 const u8 *data;
 int err;

 size_left = le16_to_cpu(ini_file->size);
 data = ini_file->data;
 while (size_left) {
  size_t data_size = MLXSW_REG_MBCT_DATA_LEN;
  bool is_last = false;

  if (size_left <= MLXSW_REG_MBCT_DATA_LEN) {
   data_size = size_left;
   is_last = true;
  }

  mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
        MLXSW_REG_MBCT_OP_DATA_TRANSFER, false);
  mlxsw_reg_mbct_dt_pack(linecard->mbct_pl, data_size,
           is_last, data);
  err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct),
          linecard->mbct_pl);
  if (err) {
   NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI data transfer");
   return err;
  }
  mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL,
          &status, &fsm_state);
  if ((!is_last && status != MLXSW_REG_MBCT_STATUS_PART_DATA) ||
      (is_last && status != MLXSW_REG_MBCT_STATUS_LAST_DATA)) {
   NL_SET_ERR_MSG_MOD(extack, "Failed to transfer linecard INI data");
   mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
   return -EINVAL;
  }
  size_left -= data_size;
  data += data_size;
 }

 return 0;
}

static int
mlxsw_linecard_ini_erase(struct mlxsw_core *mlxsw_core,
    struct mlxsw_linecard *linecard,
    struct netlink_ext_ack *extack)
{
 enum mlxsw_reg_mbct_fsm_state fsm_state;
 enum mlxsw_reg_mbct_status status;
 int err;

 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
       MLXSW_REG_MBCT_OP_ERASE_INI_IMAGE, false);
 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct),
         linecard->mbct_pl);
 if (err) {
  NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI erase");
  return err;
 }
 mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state);
 switch (status) {
 case MLXSW_REG_MBCT_STATUS_ERASE_COMPLETE:
  break;
 default:
  /* Should not happen */
  fallthrough;
 case MLXSW_REG_MBCT_STATUS_ERASE_FAILED:
  NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI");
  goto fix_fsm_err_out;
 case MLXSW_REG_MBCT_STATUS_ERROR_INI_IN_USE:
  NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI while being used");
  goto fix_fsm_err_out;
 }
 return 0;

fix_fsm_err_out:
 mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
 return -EINVAL;
}

static void mlxsw_linecard_bct_process(struct mlxsw_core *mlxsw_core,
           const char *mbct_pl)
{
 struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
 enum mlxsw_reg_mbct_fsm_state fsm_state;
 enum mlxsw_reg_mbct_status status;
 struct mlxsw_linecard *linecard;
 u8 slot_index;

 mlxsw_reg_mbct_unpack(mbct_pl, &slot_index, &status, &fsm_state);
 if (WARN_ON(slot_index > linecards->count))
  return;
 linecard = mlxsw_linecard_get(linecards, slot_index);
 mutex_lock(&linecard->lock);
 if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) {
  dev_err(linecards->bus_info->dev, "linecard %u: Failed to activate INI",
   linecard->slot_index);
  goto fix_fsm_out;
 }
 mutex_unlock(&linecard->lock);
 return;

fix_fsm_out:
 mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
 mlxsw_linecard_provision_fail(linecard);
 mutex_unlock(&linecard->lock);
}

static int
mlxsw_linecard_ini_activate(struct mlxsw_core *mlxsw_core,
       struct mlxsw_linecard *linecard,
       struct netlink_ext_ack *extack)
{
 enum mlxsw_reg_mbct_fsm_state fsm_state;
 enum mlxsw_reg_mbct_status status;
 int err;

 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
       MLXSW_REG_MBCT_OP_ACTIVATE, true);
 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct), linecard->mbct_pl);
 if (err) {
  NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI activation");
  return err;
 }
 mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state);
 if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) {
  NL_SET_ERR_MSG_MOD(extack, "Failed to activate linecard INI");
  goto fix_fsm_err_out;
 }

 return 0;

fix_fsm_err_out:
 mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
 return -EINVAL;
}

#define MLXSW_LINECARD_INI_WAIT_RETRIES 10
#define MLXSW_LINECARD_INI_WAIT_MS 500

static int
mlxsw_linecard_ini_in_use_wait(struct mlxsw_core *mlxsw_core,
          struct mlxsw_linecard *linecard,
          struct netlink_ext_ack *extack)
{
 enum mlxsw_reg_mbct_fsm_state fsm_state;
 enum mlxsw_reg_mbct_status status;
 unsigned int ini_wait_retries = 0;
 int err;

query_ini_status:
 err = mlxsw_linecard_query_ini_status(linecard, &status,
           &fsm_state, extack);
 if (err)
  return err;

 switch (fsm_state) {
 case MLXSW_REG_MBCT_FSM_STATE_INI_IN_USE:
  if (ini_wait_retries++ > MLXSW_LINECARD_INI_WAIT_RETRIES) {
   NL_SET_ERR_MSG_MOD(extack, "Failed to wait for linecard INI to be unused");
   return -EINVAL;
  }
  mdelay(MLXSW_LINECARD_INI_WAIT_MS);
  goto query_ini_status;
 default:
  break;
 }
 return 0;
}

static bool mlxsw_linecard_port_selector(void *priv, u16 local_port)
{
 struct mlxsw_linecard *linecard = priv;
 struct mlxsw_core *mlxsw_core;

 mlxsw_core = linecard->linecards->mlxsw_core;
 return linecard == mlxsw_core_port_linecard_get(mlxsw_core, local_port);
}

static int mlxsw_linecard_provision(struct devlink_linecard *devlink_linecard,
        void *priv, const char *type,
        const void *type_priv,
        struct netlink_ext_ack *extack)
{
 const struct mlxsw_linecard_ini_file *ini_file = type_priv;
 struct mlxsw_linecard *linecard = priv;
 struct mlxsw_core *mlxsw_core;
 int err;

 mutex_lock(&linecard->lock);

 mlxsw_core = linecard->linecards->mlxsw_core;

 err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack);
 if (err)
  goto err_out;

 err = mlxsw_linecard_ini_transfer(mlxsw_core, linecard,
       ini_file, extack);
 if (err)
  goto err_out;

 mlxsw_linecard_status_event_to_schedule(linecard,
      MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION);
 err = mlxsw_linecard_ini_activate(mlxsw_core, linecard, extack);
 if (err)
  goto err_out;

 goto out;

err_out:
 mlxsw_linecard_provision_fail(linecard);
out:
 mutex_unlock(&linecard->lock);
 return err;
}

static int mlxsw_linecard_unprovision(struct devlink_linecard *devlink_linecard,
          void *priv,
          struct netlink_ext_ack *extack)
{
 struct mlxsw_linecard *linecard = priv;
 struct mlxsw_core *mlxsw_core;
 int err;

 mutex_lock(&linecard->lock);

 mlxsw_core = linecard->linecards->mlxsw_core;

 mlxsw_core_ports_remove_selected(mlxsw_core,
      mlxsw_linecard_port_selector,
      linecard);

 err = mlxsw_linecard_ini_in_use_wait(mlxsw_core, linecard, extack);
 if (err)
  goto err_out;

 mlxsw_linecard_status_event_to_schedule(linecard,
      MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION);
 err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack);
 if (err)
  goto err_out;

 goto out;

err_out:
 mlxsw_linecard_provision_fail(linecard);
out:
 mutex_unlock(&linecard->lock);
 return err;
}

static bool mlxsw_linecard_same_provision(struct devlink_linecard *devlink_linecard,
       void *priv, const char *type,
       const void *type_priv)
{
 const struct mlxsw_linecard_ini_file *ini_file = type_priv;
 struct mlxsw_linecard *linecard = priv;
 bool ret;

 mutex_lock(&linecard->lock);
 ret = linecard->hw_revision == be16_to_cpu(ini_file->format.hw_revision) &&
       linecard->ini_version == be16_to_cpu(ini_file->format.ini_version);
 mutex_unlock(&linecard->lock);
 return ret;
}

static unsigned int
mlxsw_linecard_types_count(struct devlink_linecard *devlink_linecard,
      void *priv)
{
 struct mlxsw_linecard *linecard = priv;

 return linecard->linecards->types_info ?
        linecard->linecards->types_info->count : 0;
}

static void mlxsw_linecard_types_get(struct devlink_linecard *devlink_linecard,
         void *priv, unsigned int index,
         const char **type, const void **type_priv)
{
 struct mlxsw_linecard_types_info *types_info;
 struct mlxsw_linecard_ini_file *ini_file;
 struct mlxsw_linecard *linecard = priv;

 types_info = linecard->linecards->types_info;
 if (WARN_ON_ONCE(!types_info))
  return;
 ini_file = types_info->ini_files[index];
 *type = ini_file->format.name;
 *type_priv = ini_file;
}

static const struct devlink_linecard_ops mlxsw_linecard_ops = {
 .provision = mlxsw_linecard_provision,
 .unprovision = mlxsw_linecard_unprovision,
 .same_provision = mlxsw_linecard_same_provision,
 .types_count = mlxsw_linecard_types_count,
 .types_get = mlxsw_linecard_types_get,
};

struct mlxsw_linecard_status_event {
 struct mlxsw_core *mlxsw_core;
 char mddq_pl[MLXSW_REG_MDDQ_LEN];
 struct work_struct work;
};

static void mlxsw_linecard_status_event_work(struct work_struct *work)
{
 struct mlxsw_linecard_status_event *event;
 struct mlxsw_linecards *linecards;
 struct mlxsw_core *mlxsw_core;

 event = container_of(work, struct mlxsw_linecard_status_event, work);
 mlxsw_core = event->mlxsw_core;
 linecards = mlxsw_core_linecards(mlxsw_core);
 mlxsw_linecard_status_process(linecards, NULL, event->mddq_pl);
 kfree(event);
}

static void
mlxsw_linecard_status_listener_func(const struct mlxsw_reg_info *reg,
        char *mddq_pl, void *priv)
{
 struct mlxsw_linecard_status_event *event;
 struct mlxsw_core *mlxsw_core = priv;

 event = kmalloc(sizeof(*event), GFP_ATOMIC);
 if (!event)
  return;
 event->mlxsw_core = mlxsw_core;
 memcpy(event->mddq_pl, mddq_pl, sizeof(event->mddq_pl));
 INIT_WORK(&event->work, mlxsw_linecard_status_event_work);
 mlxsw_core_schedule_work(&event->work);
}

struct mlxsw_linecard_bct_event {
 struct mlxsw_core *mlxsw_core;
 char mbct_pl[MLXSW_REG_MBCT_LEN];
 struct work_struct work;
};

static void mlxsw_linecard_bct_event_work(struct work_struct *work)
{
 struct mlxsw_linecard_bct_event *event;
 struct mlxsw_core *mlxsw_core;

 event = container_of(work, struct mlxsw_linecard_bct_event, work);
 mlxsw_core = event->mlxsw_core;
 mlxsw_linecard_bct_process(mlxsw_core, event->mbct_pl);
 kfree(event);
}

static void
mlxsw_linecard_bct_listener_func(const struct mlxsw_reg_info *reg,
     char *mbct_pl, void *priv)
{
 struct mlxsw_linecard_bct_event *event;
 struct mlxsw_core *mlxsw_core = priv;

 event = kmalloc(sizeof(*event), GFP_ATOMIC);
 if (!event)
  return;
 event->mlxsw_core = mlxsw_core;
 memcpy(event->mbct_pl, mbct_pl, sizeof(event->mbct_pl));
 INIT_WORK(&event->work, mlxsw_linecard_bct_event_work);
 mlxsw_core_schedule_work(&event->work);
}

static const struct mlxsw_listener mlxsw_linecard_listener[] = {
 MLXSW_CORE_EVENTL(mlxsw_linecard_status_listener_func, DSDSC),
 MLXSW_CORE_EVENTL(mlxsw_linecard_bct_listener_func, BCTOE),
};

static int mlxsw_linecard_event_delivery_set(struct mlxsw_core *mlxsw_core,
          struct mlxsw_linecard *linecard,
          bool enable)
{
 char mddq_pl[MLXSW_REG_MDDQ_LEN];

 mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, enable);
 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddq), mddq_pl);
}

static int mlxsw_linecard_init(struct mlxsw_core *mlxsw_core,
          struct mlxsw_linecards *linecards,
          u8 slot_index)
{
 struct devlink_linecard *devlink_linecard;
 struct mlxsw_linecard *linecard;

 linecard = mlxsw_linecard_get(linecards, slot_index);
 linecard->slot_index = slot_index;
 linecard->linecards = linecards;
 mutex_init(&linecard->lock);

 devlink_linecard = devl_linecard_create(priv_to_devlink(mlxsw_core),
      slot_index, &mlxsw_linecard_ops,
      linecard);
 if (IS_ERR(devlink_linecard))
  return PTR_ERR(devlink_linecard);

 linecard->devlink_linecard = devlink_linecard;
 INIT_DELAYED_WORK(&linecard->status_event_to_dw,
     &mlxsw_linecard_status_event_to_work);

 return 0;
}

static void mlxsw_linecard_fini(struct mlxsw_core *mlxsw_core,
    struct mlxsw_linecards *linecards,
    u8 slot_index)
{
 struct mlxsw_linecard *linecard;

 linecard = mlxsw_linecard_get(linecards, slot_index);
 cancel_delayed_work_sync(&linecard->status_event_to_dw);
 /* Make sure all scheduled events are processed */
 mlxsw_core_flush_owq();
 if (linecard->active)
  mlxsw_linecard_active_clear(linecard);
 mlxsw_linecard_bdev_del(linecard);
 devl_linecard_destroy(linecard->devlink_linecard);
 mutex_destroy(&linecard->lock);
}

static int
mlxsw_linecard_event_delivery_init(struct mlxsw_core *mlxsw_core,
       struct mlxsw_linecards *linecards,
       u8 slot_index)
{
 struct mlxsw_linecard *linecard;
 int err;

 linecard = mlxsw_linecard_get(linecards, slot_index);
 err = mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, true);
 if (err)
  return err;

 err = mlxsw_linecard_status_get_and_process(mlxsw_core, linecards,
          linecard);
 if (err)
  goto err_status_get_and_process;

 return 0;

err_status_get_and_process:
 mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false);
 return err;
}

static void
mlxsw_linecard_event_delivery_fini(struct mlxsw_core *mlxsw_core,
       struct mlxsw_linecards *linecards,
       u8 slot_index)
{
 struct mlxsw_linecard *linecard;

 linecard = mlxsw_linecard_get(linecards, slot_index);
 mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false);
}

/*       LINECARDS INI BUNDLE FILE
 *  +----------------------------------+
 *  |        MAGIC ("NVLCINI+")        |
 *  +----------------------------------+     +--------------------+
 *  |  INI 0                           +---> | __le16 size        |
 *  +----------------------------------+     | __be16 hw_revision |
 *  |  INI 1                           |     | __be16 ini_version |
 *  +----------------------------------+     | u8 __dontcare[3]   |
 *  |  ...                             |     | u8 type            |
 *  +----------------------------------+     | u8 name[20]        |
 *  |  INI N                           |     | ...                |
 *  +----------------------------------+     +--------------------+
 */


#define MLXSW_LINECARDS_INI_BUNDLE_MAGIC "NVLCINI+"

static int
mlxsw_linecard_types_file_validate(struct mlxsw_linecards *linecards,
       struct mlxsw_linecard_types_info *types_info)
{
 size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC);
 struct mlxsw_linecard_ini_file *ini_file;
 size_t size = types_info->data_size;
 const u8 *data = types_info->data;
 unsigned int count = 0;
 u16 ini_file_size;

 if (size < magic_size) {
  dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file size, smaller than magic size\n");
  return -EINVAL;
 }
 if (memcmp(data, MLXSW_LINECARDS_INI_BUNDLE_MAGIC, magic_size)) {
  dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file magic pattern\n");
  return -EINVAL;
 }

 data += magic_size;
 size -= magic_size;

 while (size > 0) {
  if (size < sizeof(*ini_file)) {
   dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI which is smaller than bare minimum\n");
   return -EINVAL;
  }
  ini_file = (struct mlxsw_linecard_ini_file *) data;
  ini_file_size = le16_to_cpu(ini_file->size);
  if (ini_file_size + sizeof(__le16) > size) {
   dev_warn(linecards->bus_info->dev, "Linecards INIs file appears to be truncated\n");
   return -EINVAL;
  }
  if (ini_file_size % 4) {
   dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI with invalid size\n");
   return -EINVAL;
  }
  data += ini_file_size + sizeof(__le16);
  size -= ini_file_size + sizeof(__le16);
  count++;
 }
 if (!count) {
  dev_warn(linecards->bus_info->dev, "Linecards INIs file does not contain any INI\n");
  return -EINVAL;
 }
 types_info->count = count;
 return 0;
}

static void
mlxsw_linecard_types_file_parse(struct mlxsw_linecard_types_info *types_info)
{
 size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC);
 size_t size = types_info->data_size - magic_size;
 const u8 *data = types_info->data + magic_size;
 struct mlxsw_linecard_ini_file *ini_file;
 unsigned int count = 0;
 u16 ini_file_size;
 int i;

 while (size) {
  ini_file = (struct mlxsw_linecard_ini_file *) data;
  ini_file_size = le16_to_cpu(ini_file->size);
  for (i = 0; i < ini_file_size / 4; i++) {
   u32 *val = &((u32 *) ini_file->data)[i];

   *val = swab32(*val);
  }
  types_info->ini_files[count] = ini_file;
  data += ini_file_size + sizeof(__le16);
  size -= ini_file_size + sizeof(__le16);
  count++;
 }
}

#define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT \
 "mellanox/lc_ini_bundle_%u_%u.bin"
#define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN \
 (sizeof(MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT) + 4)

static int mlxsw_linecard_types_init(struct mlxsw_core *mlxsw_core,
         struct mlxsw_linecards *linecards)
{
 const struct mlxsw_fw_rev *rev = &linecards->bus_info->fw_rev;
 char filename[MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN];
 struct mlxsw_linecard_types_info *types_info;
 const struct firmware *firmware;
 int err;

 err = snprintf(filename, sizeof(filename),
         MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT,
         rev->minor, rev->subminor);
 WARN_ON(err >= sizeof(filename));

 err = request_firmware_direct(&firmware, filename,
          linecards->bus_info->dev);
 if (err) {
  dev_warn(linecards->bus_info->dev, "Could not request linecards INI file \"%s\", provisioning will not be possible\n",
    filename);
  return 0;
 }

 types_info = kzalloc(sizeof(*types_info), GFP_KERNEL);
 if (!types_info) {
  release_firmware(firmware);
  return -ENOMEM;
 }
 linecards->types_info = types_info;

 types_info->data_size = firmware->size;
 types_info->data = vmalloc(types_info->data_size);
 if (!types_info->data) {
  err = -ENOMEM;
  release_firmware(firmware);
  goto err_data_alloc;
 }
 memcpy(types_info->data, firmware->data, types_info->data_size);
 release_firmware(firmware);

 err = mlxsw_linecard_types_file_validate(linecards, types_info);
 if (err) {
  err = 0;
  goto err_type_file_file_validate;
 }

 types_info->ini_files = kmalloc_array(types_info->count,
           sizeof(struct mlxsw_linecard_ini_file *),
           GFP_KERNEL);
 if (!types_info->ini_files) {
  err = -ENOMEM;
  goto err_ini_files_alloc;
 }

 mlxsw_linecard_types_file_parse(types_info);

 return 0;

err_ini_files_alloc:
err_type_file_file_validate:
 vfree(types_info->data);
err_data_alloc:
 kfree(types_info);
 linecards->types_info = NULL;
 return err;
}

static void mlxsw_linecard_types_fini(struct mlxsw_linecards *linecards)
{
 struct mlxsw_linecard_types_info *types_info = linecards->types_info;

 if (!types_info)
  return;
 kfree(types_info->ini_files);
 vfree(types_info->data);
 kfree(types_info);
}

int mlxsw_linecards_init(struct mlxsw_core *mlxsw_core,
    const struct mlxsw_bus_info *bus_info)
{
 char mgpir_pl[MLXSW_REG_MGPIR_LEN];
 struct mlxsw_linecards *linecards;
 u8 slot_count;
 int err;
 int i;

 mlxsw_reg_mgpir_pack(mgpir_pl, 0);
 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl);
 if (err)
  return err;

 mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL,
          NULL, &slot_count);
 if (!slot_count)
  return 0;

 linecards = vzalloc(struct_size(linecards, linecards, slot_count));
 if (!linecards)
  return -ENOMEM;
 linecards->count = slot_count;
 linecards->mlxsw_core = mlxsw_core;
 linecards->bus_info = bus_info;
 INIT_LIST_HEAD(&linecards->event_ops_list);
 mutex_init(&linecards->event_ops_list_lock);

 err = mlxsw_linecard_types_init(mlxsw_core, linecards);
 if (err)
  goto err_types_init;

 err = mlxsw_core_traps_register(mlxsw_core, mlxsw_linecard_listener,
     ARRAY_SIZE(mlxsw_linecard_listener),
     mlxsw_core);
 if (err)
  goto err_traps_register;

 err = mlxsw_core_irq_event_handler_register(mlxsw_core,
          mlxsw_linecards_irq_event_handler);
 if (err)
  goto err_irq_event_handler_register;

 mlxsw_core_linecards_set(mlxsw_core, linecards);

 for (i = 0; i < linecards->count; i++) {
  err = mlxsw_linecard_init(mlxsw_core, linecards, i + 1);
  if (err)
   goto err_linecard_init;
 }

 for (i = 0; i < linecards->count; i++) {
  err = mlxsw_linecard_event_delivery_init(mlxsw_core, linecards,
        i + 1);
  if (err)
   goto err_linecard_event_delivery_init;
 }

 return 0;

err_linecard_event_delivery_init:
 for (i--; i >= 0; i--)
  mlxsw_linecard_event_delivery_fini(mlxsw_core, linecards, i + 1);
 i = linecards->count;
err_linecard_init:
 for (i--; i >= 0; i--)
  mlxsw_linecard_fini(mlxsw_core, linecards, i + 1);
 mlxsw_core_irq_event_handler_unregister(mlxsw_core,
      mlxsw_linecards_irq_event_handler);
err_irq_event_handler_register:
 mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener,
        ARRAY_SIZE(mlxsw_linecard_listener),
        mlxsw_core);
err_traps_register:
 mlxsw_linecard_types_fini(linecards);
err_types_init:
 vfree(linecards);
 return err;
}

void mlxsw_linecards_fini(struct mlxsw_core *mlxsw_core)
{
 struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
 int i;

 if (!linecards)
  return;
 for (i = 0; i < linecards->count; i++)
  mlxsw_linecard_event_delivery_fini(mlxsw_core, linecards, i + 1);
 for (i = 0; i < linecards->count; i++)
  mlxsw_linecard_fini(mlxsw_core, linecards, i + 1);
 mlxsw_core_irq_event_handler_unregister(mlxsw_core,
      mlxsw_linecards_irq_event_handler);
 mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener,
        ARRAY_SIZE(mlxsw_linecard_listener),
        mlxsw_core);
 mlxsw_linecard_types_fini(linecards);
 mutex_destroy(&linecards->event_ops_list_lock);
 WARN_ON(!list_empty(&linecards->event_ops_list));
 vfree(linecards);
}

Messung V0.5
C=98 H=93 G=95

¤ Dauer der Verarbeitung: 0.17 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.