mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	mlxsw: core_linecards: Add line card objects and implement provisioning
Introduce objects for line cards and an infrastructure around that. Use devlink_linecard_create/destroy() to register the line card with devlink core. Implement provisioning ops with a list of supported line cards. Signed-off-by: Jiri Pirko <jiri@nvidia.com> Signed-off-by: Ido Schimmel <idosch@nvidia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									5bade5aa4a
								
							
						
					
					
						commit
						b217127e5e
					
				
					 6 changed files with 1006 additions and 1 deletions
				
			
		| 
						 | 
				
			
			@ -1,7 +1,8 @@
 | 
			
		|||
# SPDX-License-Identifier: GPL-2.0
 | 
			
		||||
obj-$(CONFIG_MLXSW_CORE)	+= mlxsw_core.o
 | 
			
		||||
mlxsw_core-objs			:= core.o core_acl_flex_keys.o \
 | 
			
		||||
				   core_acl_flex_actions.o core_env.o
 | 
			
		||||
				   core_acl_flex_actions.o core_env.o \
 | 
			
		||||
				   core_linecards.o
 | 
			
		||||
mlxsw_core-$(CONFIG_MLXSW_CORE_HWMON) += core_hwmon.o
 | 
			
		||||
mlxsw_core-$(CONFIG_MLXSW_CORE_THERMAL) += core_thermal.o
 | 
			
		||||
obj-$(CONFIG_MLXSW_PCI)		+= mlxsw_pci.o
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -82,6 +82,7 @@ struct mlxsw_core {
 | 
			
		|||
	struct mlxsw_res res;
 | 
			
		||||
	struct mlxsw_hwmon *hwmon;
 | 
			
		||||
	struct mlxsw_thermal *thermal;
 | 
			
		||||
	struct mlxsw_linecards *linecards;
 | 
			
		||||
	struct mlxsw_core_port *ports;
 | 
			
		||||
	unsigned int max_ports;
 | 
			
		||||
	atomic_t active_ports_count;
 | 
			
		||||
| 
						 | 
				
			
			@ -94,6 +95,17 @@ struct mlxsw_core {
 | 
			
		|||
	/* driver_priv has to be always the last item */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mlxsw_linecards *mlxsw_core_linecards(struct mlxsw_core *mlxsw_core)
 | 
			
		||||
{
 | 
			
		||||
	return mlxsw_core->linecards;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mlxsw_core_linecards_set(struct mlxsw_core *mlxsw_core,
 | 
			
		||||
			      struct mlxsw_linecards *linecards)
 | 
			
		||||
{
 | 
			
		||||
	mlxsw_core->linecards = linecards;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define MLXSW_PORT_MAX_PORTS_DEFAULT	0x40
 | 
			
		||||
 | 
			
		||||
static u64 mlxsw_ports_occ_get(void *priv)
 | 
			
		||||
| 
						 | 
				
			
			@ -2145,6 +2157,10 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
 | 
			
		|||
	if (err)
 | 
			
		||||
		goto err_fw_rev_validate;
 | 
			
		||||
 | 
			
		||||
	err = mlxsw_linecards_init(mlxsw_core, mlxsw_bus_info);
 | 
			
		||||
	if (err)
 | 
			
		||||
		goto err_linecards_init;
 | 
			
		||||
 | 
			
		||||
	err = mlxsw_core_health_init(mlxsw_core);
 | 
			
		||||
	if (err)
 | 
			
		||||
		goto err_health_init;
 | 
			
		||||
| 
						 | 
				
			
			@ -2183,6 +2199,8 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
 | 
			
		|||
err_hwmon_init:
 | 
			
		||||
	mlxsw_core_health_fini(mlxsw_core);
 | 
			
		||||
err_health_init:
 | 
			
		||||
	mlxsw_linecards_fini(mlxsw_core);
 | 
			
		||||
err_linecards_init:
 | 
			
		||||
err_fw_rev_validate:
 | 
			
		||||
	if (!reload)
 | 
			
		||||
		mlxsw_core_params_unregister(mlxsw_core);
 | 
			
		||||
| 
						 | 
				
			
			@ -2255,6 +2273,7 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core,
 | 
			
		|||
	mlxsw_thermal_fini(mlxsw_core->thermal);
 | 
			
		||||
	mlxsw_hwmon_fini(mlxsw_core->hwmon);
 | 
			
		||||
	mlxsw_core_health_fini(mlxsw_core);
 | 
			
		||||
	mlxsw_linecards_fini(mlxsw_core);
 | 
			
		||||
	if (!reload)
 | 
			
		||||
		mlxsw_core_params_unregister(mlxsw_core);
 | 
			
		||||
	mlxsw_emad_fini(mlxsw_core);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,6 +35,11 @@ unsigned int mlxsw_core_max_ports(const struct mlxsw_core *mlxsw_core);
 | 
			
		|||
 | 
			
		||||
void *mlxsw_core_driver_priv(struct mlxsw_core *mlxsw_core);
 | 
			
		||||
 | 
			
		||||
struct mlxsw_linecards *mlxsw_core_linecards(struct mlxsw_core *mlxsw_core);
 | 
			
		||||
 | 
			
		||||
void mlxsw_core_linecards_set(struct mlxsw_core *mlxsw_core,
 | 
			
		||||
			      struct mlxsw_linecards *linecard);
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
mlxsw_core_fw_rev_minor_subminor_validate(const struct mlxsw_fw_rev *rev,
 | 
			
		||||
					  const struct mlxsw_fw_rev *req_rev);
 | 
			
		||||
| 
						 | 
				
			
			@ -543,4 +548,45 @@ static inline struct mlxsw_skb_cb *mlxsw_skb_cb(struct sk_buff *skb)
 | 
			
		|||
	return (struct mlxsw_skb_cb *) skb->cb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct mlxsw_linecards;
 | 
			
		||||
 | 
			
		||||
enum mlxsw_linecard_status_event_type {
 | 
			
		||||
	MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION,
 | 
			
		||||
	MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mlxsw_linecard {
 | 
			
		||||
	u8 slot_index;
 | 
			
		||||
	struct mlxsw_linecards *linecards;
 | 
			
		||||
	struct devlink_linecard *devlink_linecard;
 | 
			
		||||
	struct mutex lock; /* Locks accesses to the linecard structure */
 | 
			
		||||
	char name[MLXSW_REG_MDDQ_SLOT_ASCII_NAME_LEN];
 | 
			
		||||
	char mbct_pl[MLXSW_REG_MBCT_LEN]; /* Too big for stack */
 | 
			
		||||
	enum mlxsw_linecard_status_event_type status_event_type_to;
 | 
			
		||||
	struct delayed_work status_event_to_dw;
 | 
			
		||||
	u8 provisioned:1;
 | 
			
		||||
	u16 hw_revision;
 | 
			
		||||
	u16 ini_version;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mlxsw_linecard_types_info;
 | 
			
		||||
 | 
			
		||||
struct mlxsw_linecards {
 | 
			
		||||
	struct mlxsw_core *mlxsw_core;
 | 
			
		||||
	const struct mlxsw_bus_info *bus_info;
 | 
			
		||||
	u8 count;
 | 
			
		||||
	struct mlxsw_linecard_types_info *types_info;
 | 
			
		||||
	struct mlxsw_linecard linecards[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static inline struct mlxsw_linecard *
 | 
			
		||||
mlxsw_linecard_get(struct mlxsw_linecards *linecards, u8 slot_index)
 | 
			
		||||
{
 | 
			
		||||
	return &linecards->linecards[slot_index - 1];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int mlxsw_linecards_init(struct mlxsw_core *mlxsw_core,
 | 
			
		||||
			 const struct mlxsw_bus_info *bus_info);
 | 
			
		||||
void mlxsw_linecards_fini(struct mlxsw_core *mlxsw_core);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										929
									
								
								drivers/net/ethernet/mellanox/mlxsw/core_linecards.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										929
									
								
								drivers/net/ethernet/mellanox/mlxsw/core_linecards.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,929 @@
 | 
			
		|||
// 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"
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mlxsw_linecard_provision_fail(struct mlxsw_linecard *linecard)
 | 
			
		||||
{
 | 
			
		||||
	linecard->provisioned = false;
 | 
			
		||||
	devlink_linecard_provision_fail(linecard->devlink_linecard);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
	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);
 | 
			
		||||
	linecard->provisioned = false;
 | 
			
		||||
	devlink_linecard_provision_clear(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 (!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 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 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;
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	linecard = mlxsw_linecard_get(linecards, slot_index);
 | 
			
		||||
	linecard->slot_index = slot_index;
 | 
			
		||||
	linecard->linecards = linecards;
 | 
			
		||||
	mutex_init(&linecard->lock);
 | 
			
		||||
 | 
			
		||||
	devlink_linecard = devlink_linecard_create(priv_to_devlink(mlxsw_core),
 | 
			
		||||
						   slot_index, &mlxsw_linecard_ops,
 | 
			
		||||
						   linecard);
 | 
			
		||||
	if (IS_ERR(devlink_linecard)) {
 | 
			
		||||
		err = PTR_ERR(devlink_linecard);
 | 
			
		||||
		goto err_devlink_linecard_create;
 | 
			
		||||
	}
 | 
			
		||||
	linecard->devlink_linecard = devlink_linecard;
 | 
			
		||||
	INIT_DELAYED_WORK(&linecard->status_event_to_dw,
 | 
			
		||||
			  &mlxsw_linecard_status_event_to_work);
 | 
			
		||||
 | 
			
		||||
	err = mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, true);
 | 
			
		||||
	if (err)
 | 
			
		||||
		goto err_event_delivery_set;
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
err_event_delivery_set:
 | 
			
		||||
	devlink_linecard_destroy(linecard->devlink_linecard);
 | 
			
		||||
err_devlink_linecard_create:
 | 
			
		||||
	mutex_destroy(&linecard->lock);
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
	mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false);
 | 
			
		||||
	cancel_delayed_work_sync(&linecard->status_event_to_dw);
 | 
			
		||||
	/* Make sure all scheduled events are processed */
 | 
			
		||||
	mlxsw_core_flush_owq();
 | 
			
		||||
	devlink_linecard_destroy(linecard->devlink_linecard);
 | 
			
		||||
	mutex_destroy(&linecard->lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*       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);
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
err_linecard_init:
 | 
			
		||||
	for (i--; i >= 0; i--)
 | 
			
		||||
		mlxsw_linecard_fini(mlxsw_core, linecards, i + 1);
 | 
			
		||||
	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_fini(mlxsw_core, linecards, i + 1);
 | 
			
		||||
	mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener,
 | 
			
		||||
				    ARRAY_SIZE(mlxsw_linecard_listener),
 | 
			
		||||
				    mlxsw_core);
 | 
			
		||||
	mlxsw_linecard_types_fini(linecards);
 | 
			
		||||
	vfree(linecards);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -89,6 +89,11 @@ static const struct mlxsw_fw_rev mlxsw_sp3_fw_rev = {
 | 
			
		|||
	"." __stringify(MLXSW_SP_FWREV_MINOR) \
 | 
			
		||||
	"." __stringify(MLXSW_SP_FWREV_SUBMINOR) ".mfa2"
 | 
			
		||||
 | 
			
		||||
#define MLXSW_SP_LINECARDS_INI_BUNDLE_FILENAME \
 | 
			
		||||
	"mellanox/lc_ini_bundle_" \
 | 
			
		||||
	__stringify(MLXSW_SP_FWREV_MINOR) "_" \
 | 
			
		||||
	__stringify(MLXSW_SP_FWREV_SUBMINOR) ".bin"
 | 
			
		||||
 | 
			
		||||
static const char mlxsw_sp1_driver_name[] = "mlxsw_spectrum";
 | 
			
		||||
static const char mlxsw_sp2_driver_name[] = "mlxsw_spectrum2";
 | 
			
		||||
static const char mlxsw_sp3_driver_name[] = "mlxsw_spectrum3";
 | 
			
		||||
| 
						 | 
				
			
			@ -5159,3 +5164,4 @@ MODULE_DEVICE_TABLE(pci, mlxsw_sp4_pci_id_table);
 | 
			
		|||
MODULE_FIRMWARE(MLXSW_SP1_FW_FILENAME);
 | 
			
		||||
MODULE_FIRMWARE(MLXSW_SP2_FW_FILENAME);
 | 
			
		||||
MODULE_FIRMWARE(MLXSW_SP3_FW_FILENAME);
 | 
			
		||||
MODULE_FIRMWARE(MLXSW_SP_LINECARDS_INI_BUNDLE_FILENAME);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -133,6 +133,10 @@ enum mlxsw_event_trap_id {
 | 
			
		|||
	MLXSW_TRAP_ID_PTP_ING_FIFO = 0x2D,
 | 
			
		||||
	/* PTP Egress FIFO has a new entry */
 | 
			
		||||
	MLXSW_TRAP_ID_PTP_EGR_FIFO = 0x2E,
 | 
			
		||||
	/* Downstream Device Status Change */
 | 
			
		||||
	MLXSW_TRAP_ID_DSDSC = 0x321,
 | 
			
		||||
	/* Binary Code Transfer Operation Executed Event */
 | 
			
		||||
	MLXSW_TRAP_ID_BCTOE = 0x322,
 | 
			
		||||
	/* Port mapping change */
 | 
			
		||||
	MLXSW_TRAP_ID_PMLPE = 0x32E,
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue