brintos

brintos / linux-shallow public Read only

0
0
Text · 45.6 KiB · b032d5a Raw
1603 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/* Copyright (c) 2022 NVIDIA Corporation and Mellanox Technologies. All rights reserved */3 4#include <linux/kernel.h>5#include <linux/module.h>6#include <linux/err.h>7#include <linux/types.h>8#include <linux/string.h>9#include <linux/workqueue.h>10#include <linux/gfp.h>11#include <linux/slab.h>12#include <linux/list.h>13#include <linux/vmalloc.h>14 15#include "core.h"16#include "../mlxfw/mlxfw.h"17 18struct mlxsw_linecard_ini_file {19	__le16 size;20	union {21		u8 data[0];22		struct {23			__be16 hw_revision;24			__be16 ini_version;25			u8 __dontcare[3];26			u8 type;27			u8 name[20];28		} format;29	};30};31 32struct mlxsw_linecard_types_info {33	struct mlxsw_linecard_ini_file **ini_files;34	unsigned int count;35	size_t data_size;36	char *data;37};38 39#define MLXSW_LINECARD_STATUS_EVENT_TO (10 * MSEC_PER_SEC)40 41static void42mlxsw_linecard_status_event_to_schedule(struct mlxsw_linecard *linecard,43					enum mlxsw_linecard_status_event_type status_event_type)44{45	cancel_delayed_work_sync(&linecard->status_event_to_dw);46	linecard->status_event_type_to = status_event_type;47	mlxsw_core_schedule_dw(&linecard->status_event_to_dw,48			       msecs_to_jiffies(MLXSW_LINECARD_STATUS_EVENT_TO));49}50 51static void52mlxsw_linecard_status_event_done(struct mlxsw_linecard *linecard,53				 enum mlxsw_linecard_status_event_type status_event_type)54{55	if (linecard->status_event_type_to == status_event_type)56		cancel_delayed_work_sync(&linecard->status_event_to_dw);57}58 59static const char *60mlxsw_linecard_types_lookup(struct mlxsw_linecards *linecards, u8 card_type)61{62	struct mlxsw_linecard_types_info *types_info;63	struct mlxsw_linecard_ini_file *ini_file;64	int i;65 66	types_info = linecards->types_info;67	if (!types_info)68		return NULL;69	for (i = 0; i < types_info->count; i++) {70		ini_file = linecards->types_info->ini_files[i];71		if (ini_file->format.type == card_type)72			return ini_file->format.name;73	}74	return NULL;75}76 77static const char *mlxsw_linecard_type_name(struct mlxsw_linecard *linecard)78{79	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;80	char mddq_pl[MLXSW_REG_MDDQ_LEN];81	int err;82 83	mlxsw_reg_mddq_slot_name_pack(mddq_pl, linecard->slot_index);84	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl);85	if (err)86		return ERR_PTR(err);87	mlxsw_reg_mddq_slot_name_unpack(mddq_pl, linecard->name);88	return linecard->name;89}90 91struct mlxsw_linecard_device_fw_info {92	struct mlxfw_dev mlxfw_dev;93	struct mlxsw_core *mlxsw_core;94	struct mlxsw_linecard *linecard;95};96 97static int mlxsw_linecard_device_fw_component_query(struct mlxfw_dev *mlxfw_dev,98						    u16 component_index,99						    u32 *p_max_size,100						    u8 *p_align_bits,101						    u16 *p_max_write_size)102{103	struct mlxsw_linecard_device_fw_info *info =104		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,105			     mlxfw_dev);106	struct mlxsw_linecard *linecard = info->linecard;107	struct mlxsw_core *mlxsw_core = info->mlxsw_core;108	char mddt_pl[MLXSW_REG_MDDT_LEN];109	char *mcqi_pl;110	int err;111 112	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,113			    linecard->device.index,114			    MLXSW_REG_MDDT_METHOD_QUERY,115			    MLXSW_REG(mcqi), &mcqi_pl);116 117	mlxsw_reg_mcqi_pack(mcqi_pl, component_index);118	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl);119	if (err)120		return err;121	mlxsw_reg_mcqi_unpack(mcqi_pl, p_max_size, p_align_bits,122			      p_max_write_size);123 124	*p_align_bits = max_t(u8, *p_align_bits, 2);125	*p_max_write_size = min_t(u16, *p_max_write_size,126				  MLXSW_REG_MCDA_MAX_DATA_LEN);127	return 0;128}129 130static int mlxsw_linecard_device_fw_fsm_lock(struct mlxfw_dev *mlxfw_dev,131					     u32 *fwhandle)132{133	struct mlxsw_linecard_device_fw_info *info =134		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,135			     mlxfw_dev);136	struct mlxsw_linecard *linecard = info->linecard;137	struct mlxsw_core *mlxsw_core = info->mlxsw_core;138	char mddt_pl[MLXSW_REG_MDDT_LEN];139	u8 control_state;140	char *mcc_pl;141	int err;142 143	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,144			    linecard->device.index,145			    MLXSW_REG_MDDT_METHOD_QUERY,146			    MLXSW_REG(mcc), &mcc_pl);147	mlxsw_reg_mcc_pack(mcc_pl, 0, 0, 0, 0);148	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl);149	if (err)150		return err;151 152	mlxsw_reg_mcc_unpack(mcc_pl, fwhandle, NULL, &control_state);153	if (control_state != MLXFW_FSM_STATE_IDLE)154		return -EBUSY;155 156	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,157			    linecard->device.index,158			    MLXSW_REG_MDDT_METHOD_WRITE,159			    MLXSW_REG(mcc), &mcc_pl);160	mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_LOCK_UPDATE_HANDLE,161			   0, *fwhandle, 0);162	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);163}164 165static int166mlxsw_linecard_device_fw_fsm_component_update(struct mlxfw_dev *mlxfw_dev,167					      u32 fwhandle,168					      u16 component_index,169					      u32 component_size)170{171	struct mlxsw_linecard_device_fw_info *info =172		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,173			     mlxfw_dev);174	struct mlxsw_linecard *linecard = info->linecard;175	struct mlxsw_core *mlxsw_core = info->mlxsw_core;176	char mddt_pl[MLXSW_REG_MDDT_LEN];177	char *mcc_pl;178 179	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,180			    linecard->device.index,181			    MLXSW_REG_MDDT_METHOD_WRITE,182			    MLXSW_REG(mcc), &mcc_pl);183	mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_UPDATE_COMPONENT,184			   component_index, fwhandle, component_size);185	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);186}187 188static int189mlxsw_linecard_device_fw_fsm_block_download(struct mlxfw_dev *mlxfw_dev,190					    u32 fwhandle, u8 *data,191					    u16 size, u32 offset)192{193	struct mlxsw_linecard_device_fw_info *info =194		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,195			     mlxfw_dev);196	struct mlxsw_linecard *linecard = info->linecard;197	struct mlxsw_core *mlxsw_core = info->mlxsw_core;198	char mddt_pl[MLXSW_REG_MDDT_LEN];199	char *mcda_pl;200 201	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,202			    linecard->device.index,203			    MLXSW_REG_MDDT_METHOD_WRITE,204			    MLXSW_REG(mcda), &mcda_pl);205	mlxsw_reg_mcda_pack(mcda_pl, fwhandle, offset, size, data);206	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);207}208 209static int210mlxsw_linecard_device_fw_fsm_component_verify(struct mlxfw_dev *mlxfw_dev,211					      u32 fwhandle, u16 component_index)212{213	struct mlxsw_linecard_device_fw_info *info =214		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,215			     mlxfw_dev);216	struct mlxsw_linecard *linecard = info->linecard;217	struct mlxsw_core *mlxsw_core = info->mlxsw_core;218	char mddt_pl[MLXSW_REG_MDDT_LEN];219	char *mcc_pl;220 221	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,222			    linecard->device.index,223			    MLXSW_REG_MDDT_METHOD_WRITE,224			    MLXSW_REG(mcc), &mcc_pl);225	mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_VERIFY_COMPONENT,226			   component_index, fwhandle, 0);227	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);228}229 230static int mlxsw_linecard_device_fw_fsm_activate(struct mlxfw_dev *mlxfw_dev,231						 u32 fwhandle)232{233	struct mlxsw_linecard_device_fw_info *info =234		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,235			     mlxfw_dev);236	struct mlxsw_linecard *linecard = info->linecard;237	struct mlxsw_core *mlxsw_core = info->mlxsw_core;238	char mddt_pl[MLXSW_REG_MDDT_LEN];239	char *mcc_pl;240 241	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,242			    linecard->device.index,243			    MLXSW_REG_MDDT_METHOD_WRITE,244			    MLXSW_REG(mcc), &mcc_pl);245	mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_ACTIVATE,246			   0, fwhandle, 0);247	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);248}249 250static int251mlxsw_linecard_device_fw_fsm_query_state(struct mlxfw_dev *mlxfw_dev,252					 u32 fwhandle,253					 enum mlxfw_fsm_state *fsm_state,254					 enum mlxfw_fsm_state_err *fsm_state_err)255{256	struct mlxsw_linecard_device_fw_info *info =257		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,258			     mlxfw_dev);259	struct mlxsw_linecard *linecard = info->linecard;260	struct mlxsw_core *mlxsw_core = info->mlxsw_core;261	char mddt_pl[MLXSW_REG_MDDT_LEN];262	u8 control_state;263	u8 error_code;264	char *mcc_pl;265	int err;266 267	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,268			    linecard->device.index,269			    MLXSW_REG_MDDT_METHOD_QUERY,270			    MLXSW_REG(mcc), &mcc_pl);271	mlxsw_reg_mcc_pack(mcc_pl, 0, 0, fwhandle, 0);272	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl);273	if (err)274		return err;275 276	mlxsw_reg_mcc_unpack(mcc_pl, NULL, &error_code, &control_state);277	*fsm_state = control_state;278	*fsm_state_err = min_t(enum mlxfw_fsm_state_err, error_code,279			       MLXFW_FSM_STATE_ERR_MAX);280	return 0;281}282 283static void mlxsw_linecard_device_fw_fsm_cancel(struct mlxfw_dev *mlxfw_dev,284						u32 fwhandle)285{286	struct mlxsw_linecard_device_fw_info *info =287		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,288			     mlxfw_dev);289	struct mlxsw_linecard *linecard = info->linecard;290	struct mlxsw_core *mlxsw_core = info->mlxsw_core;291	char mddt_pl[MLXSW_REG_MDDT_LEN];292	char *mcc_pl;293 294	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,295			    linecard->device.index,296			    MLXSW_REG_MDDT_METHOD_WRITE,297			    MLXSW_REG(mcc), &mcc_pl);298	mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_CANCEL,299			   0, fwhandle, 0);300	mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);301}302 303static void mlxsw_linecard_device_fw_fsm_release(struct mlxfw_dev *mlxfw_dev,304						 u32 fwhandle)305{306	struct mlxsw_linecard_device_fw_info *info =307		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,308			     mlxfw_dev);309	struct mlxsw_linecard *linecard = info->linecard;310	struct mlxsw_core *mlxsw_core = info->mlxsw_core;311	char mddt_pl[MLXSW_REG_MDDT_LEN];312	char *mcc_pl;313 314	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,315			    linecard->device.index,316			    MLXSW_REG_MDDT_METHOD_WRITE,317			    MLXSW_REG(mcc), &mcc_pl);318	mlxsw_reg_mcc_pack(mcc_pl,319			   MLXSW_REG_MCC_INSTRUCTION_RELEASE_UPDATE_HANDLE,320			   0, fwhandle, 0);321	mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);322}323 324static const struct mlxfw_dev_ops mlxsw_linecard_device_dev_ops = {325	.component_query	= mlxsw_linecard_device_fw_component_query,326	.fsm_lock		= mlxsw_linecard_device_fw_fsm_lock,327	.fsm_component_update	= mlxsw_linecard_device_fw_fsm_component_update,328	.fsm_block_download	= mlxsw_linecard_device_fw_fsm_block_download,329	.fsm_component_verify	= mlxsw_linecard_device_fw_fsm_component_verify,330	.fsm_activate		= mlxsw_linecard_device_fw_fsm_activate,331	.fsm_query_state	= mlxsw_linecard_device_fw_fsm_query_state,332	.fsm_cancel		= mlxsw_linecard_device_fw_fsm_cancel,333	.fsm_release		= mlxsw_linecard_device_fw_fsm_release,334};335 336int mlxsw_linecard_flash_update(struct devlink *linecard_devlink,337				struct mlxsw_linecard *linecard,338				const struct firmware *firmware,339				struct netlink_ext_ack *extack)340{341	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;342	struct mlxsw_linecard_device_fw_info info = {343		.mlxfw_dev = {344			.ops = &mlxsw_linecard_device_dev_ops,345			.psid = linecard->device.info.psid,346			.psid_size = strlen(linecard->device.info.psid),347			.devlink = linecard_devlink,348		},349		.mlxsw_core = mlxsw_core,350		.linecard = linecard,351	};352	int err;353 354	mutex_lock(&linecard->lock);355	if (!linecard->active) {356		NL_SET_ERR_MSG_MOD(extack, "Only active line cards can be flashed");357		err = -EINVAL;358		goto unlock;359	}360	err = mlxsw_core_fw_flash(mlxsw_core, &info.mlxfw_dev,361				  firmware, extack);362unlock:363	mutex_unlock(&linecard->lock);364	return err;365}366 367static int mlxsw_linecard_device_psid_get(struct mlxsw_linecard *linecard,368					  u8 device_index, char *psid)369{370	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;371	char mddt_pl[MLXSW_REG_MDDT_LEN];372	char *mgir_pl;373	int err;374 375	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, device_index,376			    MLXSW_REG_MDDT_METHOD_QUERY,377			    MLXSW_REG(mgir), &mgir_pl);378 379	mlxsw_reg_mgir_pack(mgir_pl);380	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl);381	if (err)382		return err;383 384	mlxsw_reg_mgir_fw_info_psid_memcpy_from(mgir_pl, psid);385	return 0;386}387 388static int mlxsw_linecard_device_info_update(struct mlxsw_linecard *linecard)389{390	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;391	bool flashable_found = false;392	u8 msg_seq = 0;393 394	do {395		struct mlxsw_linecard_device_info info;396		char mddq_pl[MLXSW_REG_MDDQ_LEN];397		bool flash_owner;398		bool data_valid;399		u8 device_index;400		int err;401 402		mlxsw_reg_mddq_device_info_pack(mddq_pl, linecard->slot_index,403						msg_seq);404		err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl);405		if (err)406			return err;407		mlxsw_reg_mddq_device_info_unpack(mddq_pl, &msg_seq,408						  &data_valid, &flash_owner,409						  &device_index,410						  &info.fw_major,411						  &info.fw_minor,412						  &info.fw_sub_minor);413		if (!data_valid)414			break;415		if (!flash_owner) /* We care only about flashable ones. */416			continue;417		if (flashable_found) {418			dev_warn_once(linecard->linecards->bus_info->dev, "linecard %u: More flashable devices present, exposing only the first one\n",419				      linecard->slot_index);420			return 0;421		}422 423		err = mlxsw_linecard_device_psid_get(linecard, device_index,424						     info.psid);425		if (err)426			return err;427 428		linecard->device.info = info;429		linecard->device.index = device_index;430		flashable_found = true;431	} while (msg_seq);432 433	return 0;434}435 436static void mlxsw_linecard_provision_fail(struct mlxsw_linecard *linecard)437{438	linecard->provisioned = false;439	linecard->ready = false;440	linecard->active = false;441	devlink_linecard_provision_fail(linecard->devlink_linecard);442}443 444struct mlxsw_linecards_event_ops_item {445	struct list_head list;446	const struct mlxsw_linecards_event_ops *event_ops;447	void *priv;448};449 450static void451mlxsw_linecard_event_op_call(struct mlxsw_linecard *linecard,452			     mlxsw_linecards_event_op_t *op, void *priv)453{454	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;455 456	if (!op)457		return;458	op(mlxsw_core, linecard->slot_index, priv);459}460 461static void462mlxsw_linecard_active_ops_call(struct mlxsw_linecard *linecard)463{464	struct mlxsw_linecards *linecards = linecard->linecards;465	struct mlxsw_linecards_event_ops_item *item;466 467	mutex_lock(&linecards->event_ops_list_lock);468	list_for_each_entry(item, &linecards->event_ops_list, list)469		mlxsw_linecard_event_op_call(linecard,470					     item->event_ops->got_active,471					     item->priv);472	mutex_unlock(&linecards->event_ops_list_lock);473}474 475static void476mlxsw_linecard_inactive_ops_call(struct mlxsw_linecard *linecard)477{478	struct mlxsw_linecards *linecards = linecard->linecards;479	struct mlxsw_linecards_event_ops_item *item;480 481	mutex_lock(&linecards->event_ops_list_lock);482	list_for_each_entry(item, &linecards->event_ops_list, list)483		mlxsw_linecard_event_op_call(linecard,484					     item->event_ops->got_inactive,485					     item->priv);486	mutex_unlock(&linecards->event_ops_list_lock);487}488 489static void490mlxsw_linecards_event_ops_register_call(struct mlxsw_linecards *linecards,491					const struct mlxsw_linecards_event_ops_item *item)492{493	struct mlxsw_linecard *linecard;494	int i;495 496	for (i = 0; i < linecards->count; i++) {497		linecard = mlxsw_linecard_get(linecards, i + 1);498		mutex_lock(&linecard->lock);499		if (linecard->active)500			mlxsw_linecard_event_op_call(linecard,501						     item->event_ops->got_active,502						     item->priv);503		mutex_unlock(&linecard->lock);504	}505}506 507static void508mlxsw_linecards_event_ops_unregister_call(struct mlxsw_linecards *linecards,509					  const struct mlxsw_linecards_event_ops_item *item)510{511	struct mlxsw_linecard *linecard;512	int i;513 514	for (i = 0; i < linecards->count; i++) {515		linecard = mlxsw_linecard_get(linecards, i + 1);516		mutex_lock(&linecard->lock);517		if (linecard->active)518			mlxsw_linecard_event_op_call(linecard,519						     item->event_ops->got_inactive,520						     item->priv);521		mutex_unlock(&linecard->lock);522	}523}524 525int mlxsw_linecards_event_ops_register(struct mlxsw_core *mlxsw_core,526				       struct mlxsw_linecards_event_ops *ops,527				       void *priv)528{529	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);530	struct mlxsw_linecards_event_ops_item *item;531 532	if (!linecards)533		return 0;534	item = kzalloc(sizeof(*item), GFP_KERNEL);535	if (!item)536		return -ENOMEM;537	item->event_ops = ops;538	item->priv = priv;539 540	mutex_lock(&linecards->event_ops_list_lock);541	list_add_tail(&item->list, &linecards->event_ops_list);542	mutex_unlock(&linecards->event_ops_list_lock);543	mlxsw_linecards_event_ops_register_call(linecards, item);544	return 0;545}546EXPORT_SYMBOL(mlxsw_linecards_event_ops_register);547 548void mlxsw_linecards_event_ops_unregister(struct mlxsw_core *mlxsw_core,549					  struct mlxsw_linecards_event_ops *ops,550					  void *priv)551{552	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);553	struct mlxsw_linecards_event_ops_item *item, *tmp;554	bool found = false;555 556	if (!linecards)557		return;558	mutex_lock(&linecards->event_ops_list_lock);559	list_for_each_entry_safe(item, tmp, &linecards->event_ops_list, list) {560		if (item->event_ops == ops && item->priv == priv) {561			list_del(&item->list);562			found = true;563			break;564		}565	}566	mutex_unlock(&linecards->event_ops_list_lock);567 568	if (!found)569		return;570	mlxsw_linecards_event_ops_unregister_call(linecards, item);571	kfree(item);572}573EXPORT_SYMBOL(mlxsw_linecards_event_ops_unregister);574 575int mlxsw_linecard_devlink_info_get(struct mlxsw_linecard *linecard,576				    struct devlink_info_req *req,577				    struct netlink_ext_ack *extack)578{579	char buf[32];580	int err;581 582	mutex_lock(&linecard->lock);583	if (WARN_ON(!linecard->provisioned)) {584		err = -EOPNOTSUPP;585		goto unlock;586	}587 588	sprintf(buf, "%d", linecard->hw_revision);589	err = devlink_info_version_fixed_put(req, "hw.revision", buf);590	if (err)591		goto unlock;592 593	sprintf(buf, "%d", linecard->ini_version);594	err = devlink_info_version_running_put(req, "ini.version", buf);595	if (err)596		goto unlock;597 598	if (linecard->active) {599		struct mlxsw_linecard_device_info *info = &linecard->device.info;600 601		err = devlink_info_version_fixed_put(req,602						     DEVLINK_INFO_VERSION_GENERIC_FW_PSID,603						     info->psid);604 605		sprintf(buf, "%u.%u.%u", info->fw_major, info->fw_minor,606			info->fw_sub_minor);607		err = devlink_info_version_running_put(req,608						       DEVLINK_INFO_VERSION_GENERIC_FW,609						       buf);610		if (err)611			goto unlock;612	}613 614unlock:615	mutex_unlock(&linecard->lock);616	return err;617}618 619static int620mlxsw_linecard_provision_set(struct mlxsw_linecard *linecard, u8 card_type,621			     u16 hw_revision, u16 ini_version)622{623	struct mlxsw_linecards *linecards = linecard->linecards;624	const char *type;625	int err;626 627	type = mlxsw_linecard_types_lookup(linecards, card_type);628	mlxsw_linecard_status_event_done(linecard,629					 MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION);630	if (!type) {631		/* It is possible for a line card to be provisioned before632		 * driver initialization. Due to a missing INI bundle file633		 * or an outdated one, the queried card's type might not634		 * be recognized by the driver. In this case, try to query635		 * the card's name from the device.636		 */637		type = mlxsw_linecard_type_name(linecard);638		if (IS_ERR(type)) {639			mlxsw_linecard_provision_fail(linecard);640			return PTR_ERR(type);641		}642	}643	linecard->provisioned = true;644	linecard->hw_revision = hw_revision;645	linecard->ini_version = ini_version;646 647	err = mlxsw_linecard_bdev_add(linecard);648	if (err) {649		linecard->provisioned = false;650		mlxsw_linecard_provision_fail(linecard);651		return err;652	}653 654	devlink_linecard_provision_set(linecard->devlink_linecard, type);655	return 0;656}657 658static void mlxsw_linecard_provision_clear(struct mlxsw_linecard *linecard)659{660	mlxsw_linecard_status_event_done(linecard,661					 MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION);662	mlxsw_linecard_bdev_del(linecard);663	linecard->provisioned = false;664	devlink_linecard_provision_clear(linecard->devlink_linecard);665}666 667static int mlxsw_linecard_ready_set(struct mlxsw_linecard *linecard)668{669	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;670	char mddc_pl[MLXSW_REG_MDDC_LEN];671	int err;672 673	err = mlxsw_linecard_device_info_update(linecard);674	if (err)675		return err;676 677	mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, false, true);678	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl);679	if (err)680		return err;681	linecard->ready = true;682	return 0;683}684 685static int mlxsw_linecard_ready_clear(struct mlxsw_linecard *linecard)686{687	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;688	char mddc_pl[MLXSW_REG_MDDC_LEN];689	int err;690 691	mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, false, false);692	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl);693	if (err)694		return err;695	linecard->ready = false;696	return 0;697}698 699static void mlxsw_linecard_active_set(struct mlxsw_linecard *linecard)700{701	mlxsw_linecard_active_ops_call(linecard);702	linecard->active = true;703	devlink_linecard_activate(linecard->devlink_linecard);704}705 706static void mlxsw_linecard_active_clear(struct mlxsw_linecard *linecard)707{708	mlxsw_linecard_inactive_ops_call(linecard);709	linecard->active = false;710	devlink_linecard_deactivate(linecard->devlink_linecard);711}712 713static int mlxsw_linecard_status_process(struct mlxsw_linecards *linecards,714					 struct mlxsw_linecard *linecard,715					 const char *mddq_pl)716{717	enum mlxsw_reg_mddq_slot_info_ready ready;718	bool provisioned, sr_valid, active;719	u16 ini_version, hw_revision;720	u8 slot_index, card_type;721	int err = 0;722 723	mlxsw_reg_mddq_slot_info_unpack(mddq_pl, &slot_index, &provisioned,724					&sr_valid, &ready, &active,725					&hw_revision, &ini_version,726					&card_type);727 728	if (linecard) {729		if (WARN_ON(slot_index != linecard->slot_index))730			return -EINVAL;731	} else {732		if (WARN_ON(slot_index > linecards->count))733			return -EINVAL;734		linecard = mlxsw_linecard_get(linecards, slot_index);735	}736 737	mutex_lock(&linecard->lock);738 739	if (provisioned && linecard->provisioned != provisioned) {740		err = mlxsw_linecard_provision_set(linecard, card_type,741						   hw_revision, ini_version);742		if (err)743			goto out;744	}745 746	if (ready == MLXSW_REG_MDDQ_SLOT_INFO_READY_READY && !linecard->ready) {747		err = mlxsw_linecard_ready_set(linecard);748		if (err)749			goto out;750	}751 752	if (active && linecard->active != active)753		mlxsw_linecard_active_set(linecard);754 755	if (!active && linecard->active != active)756		mlxsw_linecard_active_clear(linecard);757 758	if (ready != MLXSW_REG_MDDQ_SLOT_INFO_READY_READY &&759	    linecard->ready) {760		err = mlxsw_linecard_ready_clear(linecard);761		if (err)762			goto out;763	}764 765	if (!provisioned && linecard->provisioned != provisioned)766		mlxsw_linecard_provision_clear(linecard);767 768out:769	mutex_unlock(&linecard->lock);770	return err;771}772 773static int mlxsw_linecard_status_get_and_process(struct mlxsw_core *mlxsw_core,774						 struct mlxsw_linecards *linecards,775						 struct mlxsw_linecard *linecard)776{777	char mddq_pl[MLXSW_REG_MDDQ_LEN];778	int err;779 780	mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, false);781	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl);782	if (err)783		return err;784 785	return mlxsw_linecard_status_process(linecards, linecard, mddq_pl);786}787 788static void mlxsw_linecards_irq_event_handler(struct mlxsw_core *mlxsw_core)789{790	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);791	int i;792 793	/* Handle change of line card active state. */794	for (i = 0; i < linecards->count; i++) {795		struct mlxsw_linecard *linecard = mlxsw_linecard_get(linecards,796								     i + 1);797 798		mlxsw_linecard_status_get_and_process(mlxsw_core, linecards,799						      linecard);800	}801}802 803static const char * const mlxsw_linecard_status_event_type_name[] = {804	[MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION] = "provision",805	[MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION] = "unprovision",806};807 808static void mlxsw_linecard_status_event_to_work(struct work_struct *work)809{810	struct mlxsw_linecard *linecard =811		container_of(work, struct mlxsw_linecard,812			     status_event_to_dw.work);813 814	mutex_lock(&linecard->lock);815	dev_err(linecard->linecards->bus_info->dev, "linecard %u: Timeout reached waiting on %s status event",816		linecard->slot_index,817		mlxsw_linecard_status_event_type_name[linecard->status_event_type_to]);818	mlxsw_linecard_provision_fail(linecard);819	mutex_unlock(&linecard->lock);820}821 822static int __mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard)823{824	dev_info(linecard->linecards->bus_info->dev, "linecard %u: Clearing FSM state error",825		 linecard->slot_index);826	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,827			    MLXSW_REG_MBCT_OP_CLEAR_ERRORS, false);828	return mlxsw_reg_write(linecard->linecards->mlxsw_core,829			       MLXSW_REG(mbct), linecard->mbct_pl);830}831 832static int mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard,833					enum mlxsw_reg_mbct_fsm_state fsm_state)834{835	if (fsm_state != MLXSW_REG_MBCT_FSM_STATE_ERROR)836		return 0;837	return __mlxsw_linecard_fix_fsm_state(linecard);838}839 840static int841mlxsw_linecard_query_ini_status(struct mlxsw_linecard *linecard,842				enum mlxsw_reg_mbct_status *status,843				enum mlxsw_reg_mbct_fsm_state *fsm_state,844				struct netlink_ext_ack *extack)845{846	int err;847 848	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,849			    MLXSW_REG_MBCT_OP_QUERY_STATUS, false);850	err = mlxsw_reg_query(linecard->linecards->mlxsw_core, MLXSW_REG(mbct),851			      linecard->mbct_pl);852	if (err) {853		NL_SET_ERR_MSG_MOD(extack, "Failed to query linecard INI status");854		return err;855	}856	mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, status, fsm_state);857	return err;858}859 860static int861mlxsw_linecard_ini_transfer(struct mlxsw_core *mlxsw_core,862			    struct mlxsw_linecard *linecard,863			    const struct mlxsw_linecard_ini_file *ini_file,864			    struct netlink_ext_ack *extack)865{866	enum mlxsw_reg_mbct_fsm_state fsm_state;867	enum mlxsw_reg_mbct_status status;868	size_t size_left;869	const u8 *data;870	int err;871 872	size_left = le16_to_cpu(ini_file->size);873	data = ini_file->data;874	while (size_left) {875		size_t data_size = MLXSW_REG_MBCT_DATA_LEN;876		bool is_last = false;877 878		if (size_left <= MLXSW_REG_MBCT_DATA_LEN) {879			data_size = size_left;880			is_last = true;881		}882 883		mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,884				    MLXSW_REG_MBCT_OP_DATA_TRANSFER, false);885		mlxsw_reg_mbct_dt_pack(linecard->mbct_pl, data_size,886				       is_last, data);887		err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct),888				      linecard->mbct_pl);889		if (err) {890			NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI data transfer");891			return err;892		}893		mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL,894				      &status, &fsm_state);895		if ((!is_last && status != MLXSW_REG_MBCT_STATUS_PART_DATA) ||896		    (is_last && status != MLXSW_REG_MBCT_STATUS_LAST_DATA)) {897			NL_SET_ERR_MSG_MOD(extack, "Failed to transfer linecard INI data");898			mlxsw_linecard_fix_fsm_state(linecard, fsm_state);899			return -EINVAL;900		}901		size_left -= data_size;902		data += data_size;903	}904 905	return 0;906}907 908static int909mlxsw_linecard_ini_erase(struct mlxsw_core *mlxsw_core,910			 struct mlxsw_linecard *linecard,911			 struct netlink_ext_ack *extack)912{913	enum mlxsw_reg_mbct_fsm_state fsm_state;914	enum mlxsw_reg_mbct_status status;915	int err;916 917	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,918			    MLXSW_REG_MBCT_OP_ERASE_INI_IMAGE, false);919	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct),920			      linecard->mbct_pl);921	if (err) {922		NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI erase");923		return err;924	}925	mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state);926	switch (status) {927	case MLXSW_REG_MBCT_STATUS_ERASE_COMPLETE:928		break;929	default:930		/* Should not happen */931		fallthrough;932	case MLXSW_REG_MBCT_STATUS_ERASE_FAILED:933		NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI");934		goto fix_fsm_err_out;935	case MLXSW_REG_MBCT_STATUS_ERROR_INI_IN_USE:936		NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI while being used");937		goto fix_fsm_err_out;938	}939	return 0;940 941fix_fsm_err_out:942	mlxsw_linecard_fix_fsm_state(linecard, fsm_state);943	return -EINVAL;944}945 946static void mlxsw_linecard_bct_process(struct mlxsw_core *mlxsw_core,947				       const char *mbct_pl)948{949	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);950	enum mlxsw_reg_mbct_fsm_state fsm_state;951	enum mlxsw_reg_mbct_status status;952	struct mlxsw_linecard *linecard;953	u8 slot_index;954 955	mlxsw_reg_mbct_unpack(mbct_pl, &slot_index, &status, &fsm_state);956	if (WARN_ON(slot_index > linecards->count))957		return;958	linecard = mlxsw_linecard_get(linecards, slot_index);959	mutex_lock(&linecard->lock);960	if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) {961		dev_err(linecards->bus_info->dev, "linecard %u: Failed to activate INI",962			linecard->slot_index);963		goto fix_fsm_out;964	}965	mutex_unlock(&linecard->lock);966	return;967 968fix_fsm_out:969	mlxsw_linecard_fix_fsm_state(linecard, fsm_state);970	mlxsw_linecard_provision_fail(linecard);971	mutex_unlock(&linecard->lock);972}973 974static int975mlxsw_linecard_ini_activate(struct mlxsw_core *mlxsw_core,976			    struct mlxsw_linecard *linecard,977			    struct netlink_ext_ack *extack)978{979	enum mlxsw_reg_mbct_fsm_state fsm_state;980	enum mlxsw_reg_mbct_status status;981	int err;982 983	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,984			    MLXSW_REG_MBCT_OP_ACTIVATE, true);985	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct), linecard->mbct_pl);986	if (err) {987		NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI activation");988		return err;989	}990	mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state);991	if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) {992		NL_SET_ERR_MSG_MOD(extack, "Failed to activate linecard INI");993		goto fix_fsm_err_out;994	}995 996	return 0;997 998fix_fsm_err_out:999	mlxsw_linecard_fix_fsm_state(linecard, fsm_state);1000	return -EINVAL;1001}1002 1003#define MLXSW_LINECARD_INI_WAIT_RETRIES 101004#define MLXSW_LINECARD_INI_WAIT_MS 5001005 1006static int1007mlxsw_linecard_ini_in_use_wait(struct mlxsw_core *mlxsw_core,1008			       struct mlxsw_linecard *linecard,1009			       struct netlink_ext_ack *extack)1010{1011	enum mlxsw_reg_mbct_fsm_state fsm_state;1012	enum mlxsw_reg_mbct_status status;1013	unsigned int ini_wait_retries = 0;1014	int err;1015 1016query_ini_status:1017	err = mlxsw_linecard_query_ini_status(linecard, &status,1018					      &fsm_state, extack);1019	if (err)1020		return err;1021 1022	switch (fsm_state) {1023	case MLXSW_REG_MBCT_FSM_STATE_INI_IN_USE:1024		if (ini_wait_retries++ > MLXSW_LINECARD_INI_WAIT_RETRIES) {1025			NL_SET_ERR_MSG_MOD(extack, "Failed to wait for linecard INI to be unused");1026			return -EINVAL;1027		}1028		mdelay(MLXSW_LINECARD_INI_WAIT_MS);1029		goto query_ini_status;1030	default:1031		break;1032	}1033	return 0;1034}1035 1036static bool mlxsw_linecard_port_selector(void *priv, u16 local_port)1037{1038	struct mlxsw_linecard *linecard = priv;1039	struct mlxsw_core *mlxsw_core;1040 1041	mlxsw_core = linecard->linecards->mlxsw_core;1042	return linecard == mlxsw_core_port_linecard_get(mlxsw_core, local_port);1043}1044 1045static int mlxsw_linecard_provision(struct devlink_linecard *devlink_linecard,1046				    void *priv, const char *type,1047				    const void *type_priv,1048				    struct netlink_ext_ack *extack)1049{1050	const struct mlxsw_linecard_ini_file *ini_file = type_priv;1051	struct mlxsw_linecard *linecard = priv;1052	struct mlxsw_core *mlxsw_core;1053	int err;1054 1055	mutex_lock(&linecard->lock);1056 1057	mlxsw_core = linecard->linecards->mlxsw_core;1058 1059	err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack);1060	if (err)1061		goto err_out;1062 1063	err = mlxsw_linecard_ini_transfer(mlxsw_core, linecard,1064					  ini_file, extack);1065	if (err)1066		goto err_out;1067 1068	mlxsw_linecard_status_event_to_schedule(linecard,1069						MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION);1070	err = mlxsw_linecard_ini_activate(mlxsw_core, linecard, extack);1071	if (err)1072		goto err_out;1073 1074	goto out;1075 1076err_out:1077	mlxsw_linecard_provision_fail(linecard);1078out:1079	mutex_unlock(&linecard->lock);1080	return err;1081}1082 1083static int mlxsw_linecard_unprovision(struct devlink_linecard *devlink_linecard,1084				      void *priv,1085				      struct netlink_ext_ack *extack)1086{1087	struct mlxsw_linecard *linecard = priv;1088	struct mlxsw_core *mlxsw_core;1089	int err;1090 1091	mutex_lock(&linecard->lock);1092 1093	mlxsw_core = linecard->linecards->mlxsw_core;1094 1095	mlxsw_core_ports_remove_selected(mlxsw_core,1096					 mlxsw_linecard_port_selector,1097					 linecard);1098 1099	err = mlxsw_linecard_ini_in_use_wait(mlxsw_core, linecard, extack);1100	if (err)1101		goto err_out;1102 1103	mlxsw_linecard_status_event_to_schedule(linecard,1104						MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION);1105	err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack);1106	if (err)1107		goto err_out;1108 1109	goto out;1110 1111err_out:1112	mlxsw_linecard_provision_fail(linecard);1113out:1114	mutex_unlock(&linecard->lock);1115	return err;1116}1117 1118static bool mlxsw_linecard_same_provision(struct devlink_linecard *devlink_linecard,1119					  void *priv, const char *type,1120					  const void *type_priv)1121{1122	const struct mlxsw_linecard_ini_file *ini_file = type_priv;1123	struct mlxsw_linecard *linecard = priv;1124	bool ret;1125 1126	mutex_lock(&linecard->lock);1127	ret = linecard->hw_revision == be16_to_cpu(ini_file->format.hw_revision) &&1128	      linecard->ini_version == be16_to_cpu(ini_file->format.ini_version);1129	mutex_unlock(&linecard->lock);1130	return ret;1131}1132 1133static unsigned int1134mlxsw_linecard_types_count(struct devlink_linecard *devlink_linecard,1135			   void *priv)1136{1137	struct mlxsw_linecard *linecard = priv;1138 1139	return linecard->linecards->types_info ?1140	       linecard->linecards->types_info->count : 0;1141}1142 1143static void mlxsw_linecard_types_get(struct devlink_linecard *devlink_linecard,1144				     void *priv, unsigned int index,1145				     const char **type, const void **type_priv)1146{1147	struct mlxsw_linecard_types_info *types_info;1148	struct mlxsw_linecard_ini_file *ini_file;1149	struct mlxsw_linecard *linecard = priv;1150 1151	types_info = linecard->linecards->types_info;1152	if (WARN_ON_ONCE(!types_info))1153		return;1154	ini_file = types_info->ini_files[index];1155	*type = ini_file->format.name;1156	*type_priv = ini_file;1157}1158 1159static const struct devlink_linecard_ops mlxsw_linecard_ops = {1160	.provision = mlxsw_linecard_provision,1161	.unprovision = mlxsw_linecard_unprovision,1162	.same_provision = mlxsw_linecard_same_provision,1163	.types_count = mlxsw_linecard_types_count,1164	.types_get = mlxsw_linecard_types_get,1165};1166 1167struct mlxsw_linecard_status_event {1168	struct mlxsw_core *mlxsw_core;1169	char mddq_pl[MLXSW_REG_MDDQ_LEN];1170	struct work_struct work;1171};1172 1173static void mlxsw_linecard_status_event_work(struct work_struct *work)1174{1175	struct mlxsw_linecard_status_event *event;1176	struct mlxsw_linecards *linecards;1177	struct mlxsw_core *mlxsw_core;1178 1179	event = container_of(work, struct mlxsw_linecard_status_event, work);1180	mlxsw_core = event->mlxsw_core;1181	linecards = mlxsw_core_linecards(mlxsw_core);1182	mlxsw_linecard_status_process(linecards, NULL, event->mddq_pl);1183	kfree(event);1184}1185 1186static void1187mlxsw_linecard_status_listener_func(const struct mlxsw_reg_info *reg,1188				    char *mddq_pl, void *priv)1189{1190	struct mlxsw_linecard_status_event *event;1191	struct mlxsw_core *mlxsw_core = priv;1192 1193	event = kmalloc(sizeof(*event), GFP_ATOMIC);1194	if (!event)1195		return;1196	event->mlxsw_core = mlxsw_core;1197	memcpy(event->mddq_pl, mddq_pl, sizeof(event->mddq_pl));1198	INIT_WORK(&event->work, mlxsw_linecard_status_event_work);1199	mlxsw_core_schedule_work(&event->work);1200}1201 1202struct mlxsw_linecard_bct_event {1203	struct mlxsw_core *mlxsw_core;1204	char mbct_pl[MLXSW_REG_MBCT_LEN];1205	struct work_struct work;1206};1207 1208static void mlxsw_linecard_bct_event_work(struct work_struct *work)1209{1210	struct mlxsw_linecard_bct_event *event;1211	struct mlxsw_core *mlxsw_core;1212 1213	event = container_of(work, struct mlxsw_linecard_bct_event, work);1214	mlxsw_core = event->mlxsw_core;1215	mlxsw_linecard_bct_process(mlxsw_core, event->mbct_pl);1216	kfree(event);1217}1218 1219static void1220mlxsw_linecard_bct_listener_func(const struct mlxsw_reg_info *reg,1221				 char *mbct_pl, void *priv)1222{1223	struct mlxsw_linecard_bct_event *event;1224	struct mlxsw_core *mlxsw_core = priv;1225 1226	event = kmalloc(sizeof(*event), GFP_ATOMIC);1227	if (!event)1228		return;1229	event->mlxsw_core = mlxsw_core;1230	memcpy(event->mbct_pl, mbct_pl, sizeof(event->mbct_pl));1231	INIT_WORK(&event->work, mlxsw_linecard_bct_event_work);1232	mlxsw_core_schedule_work(&event->work);1233}1234 1235static const struct mlxsw_listener mlxsw_linecard_listener[] = {1236	MLXSW_CORE_EVENTL(mlxsw_linecard_status_listener_func, DSDSC),1237	MLXSW_CORE_EVENTL(mlxsw_linecard_bct_listener_func, BCTOE),1238};1239 1240static int mlxsw_linecard_event_delivery_set(struct mlxsw_core *mlxsw_core,1241					     struct mlxsw_linecard *linecard,1242					     bool enable)1243{1244	char mddq_pl[MLXSW_REG_MDDQ_LEN];1245 1246	mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, enable);1247	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddq), mddq_pl);1248}1249 1250static int mlxsw_linecard_init(struct mlxsw_core *mlxsw_core,1251			       struct mlxsw_linecards *linecards,1252			       u8 slot_index)1253{1254	struct devlink_linecard *devlink_linecard;1255	struct mlxsw_linecard *linecard;1256 1257	linecard = mlxsw_linecard_get(linecards, slot_index);1258	linecard->slot_index = slot_index;1259	linecard->linecards = linecards;1260	mutex_init(&linecard->lock);1261 1262	devlink_linecard = devl_linecard_create(priv_to_devlink(mlxsw_core),1263						slot_index, &mlxsw_linecard_ops,1264						linecard);1265	if (IS_ERR(devlink_linecard))1266		return PTR_ERR(devlink_linecard);1267 1268	linecard->devlink_linecard = devlink_linecard;1269	INIT_DELAYED_WORK(&linecard->status_event_to_dw,1270			  &mlxsw_linecard_status_event_to_work);1271 1272	return 0;1273}1274 1275static void mlxsw_linecard_fini(struct mlxsw_core *mlxsw_core,1276				struct mlxsw_linecards *linecards,1277				u8 slot_index)1278{1279	struct mlxsw_linecard *linecard;1280 1281	linecard = mlxsw_linecard_get(linecards, slot_index);1282	cancel_delayed_work_sync(&linecard->status_event_to_dw);1283	/* Make sure all scheduled events are processed */1284	mlxsw_core_flush_owq();1285	if (linecard->active)1286		mlxsw_linecard_active_clear(linecard);1287	mlxsw_linecard_bdev_del(linecard);1288	devl_linecard_destroy(linecard->devlink_linecard);1289	mutex_destroy(&linecard->lock);1290}1291 1292static int1293mlxsw_linecard_event_delivery_init(struct mlxsw_core *mlxsw_core,1294				   struct mlxsw_linecards *linecards,1295				   u8 slot_index)1296{1297	struct mlxsw_linecard *linecard;1298	int err;1299 1300	linecard = mlxsw_linecard_get(linecards, slot_index);1301	err = mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, true);1302	if (err)1303		return err;1304 1305	err = mlxsw_linecard_status_get_and_process(mlxsw_core, linecards,1306						    linecard);1307	if (err)1308		goto err_status_get_and_process;1309 1310	return 0;1311 1312err_status_get_and_process:1313	mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false);1314	return err;1315}1316 1317static void1318mlxsw_linecard_event_delivery_fini(struct mlxsw_core *mlxsw_core,1319				   struct mlxsw_linecards *linecards,1320				   u8 slot_index)1321{1322	struct mlxsw_linecard *linecard;1323 1324	linecard = mlxsw_linecard_get(linecards, slot_index);1325	mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false);1326}1327 1328/*       LINECARDS INI BUNDLE FILE1329 *  +----------------------------------+1330 *  |        MAGIC ("NVLCINI+")        |1331 *  +----------------------------------+     +--------------------+1332 *  |  INI 0                           +---> | __le16 size        |1333 *  +----------------------------------+     | __be16 hw_revision |1334 *  |  INI 1                           |     | __be16 ini_version |1335 *  +----------------------------------+     | u8 __dontcare[3]   |1336 *  |  ...                             |     | u8 type            |1337 *  +----------------------------------+     | u8 name[20]        |1338 *  |  INI N                           |     | ...                |1339 *  +----------------------------------+     +--------------------+1340 */1341 1342#define MLXSW_LINECARDS_INI_BUNDLE_MAGIC "NVLCINI+"1343 1344static int1345mlxsw_linecard_types_file_validate(struct mlxsw_linecards *linecards,1346				   struct mlxsw_linecard_types_info *types_info)1347{1348	size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC);1349	struct mlxsw_linecard_ini_file *ini_file;1350	size_t size = types_info->data_size;1351	const u8 *data = types_info->data;1352	unsigned int count = 0;1353	u16 ini_file_size;1354 1355	if (size < magic_size) {1356		dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file size, smaller than magic size\n");1357		return -EINVAL;1358	}1359	if (memcmp(data, MLXSW_LINECARDS_INI_BUNDLE_MAGIC, magic_size)) {1360		dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file magic pattern\n");1361		return -EINVAL;1362	}1363 1364	data += magic_size;1365	size -= magic_size;1366 1367	while (size > 0) {1368		if (size < sizeof(*ini_file)) {1369			dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI which is smaller than bare minimum\n");1370			return -EINVAL;1371		}1372		ini_file = (struct mlxsw_linecard_ini_file *) data;1373		ini_file_size = le16_to_cpu(ini_file->size);1374		if (ini_file_size + sizeof(__le16) > size) {1375			dev_warn(linecards->bus_info->dev, "Linecards INIs file appears to be truncated\n");1376			return -EINVAL;1377		}1378		if (ini_file_size % 4) {1379			dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI with invalid size\n");1380			return -EINVAL;1381		}1382		data += ini_file_size + sizeof(__le16);1383		size -= ini_file_size + sizeof(__le16);1384		count++;1385	}1386	if (!count) {1387		dev_warn(linecards->bus_info->dev, "Linecards INIs file does not contain any INI\n");1388		return -EINVAL;1389	}1390	types_info->count = count;1391	return 0;1392}1393 1394static void1395mlxsw_linecard_types_file_parse(struct mlxsw_linecard_types_info *types_info)1396{1397	size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC);1398	size_t size = types_info->data_size - magic_size;1399	const u8 *data = types_info->data + magic_size;1400	struct mlxsw_linecard_ini_file *ini_file;1401	unsigned int count = 0;1402	u16 ini_file_size;1403	int i;1404 1405	while (size) {1406		ini_file = (struct mlxsw_linecard_ini_file *) data;1407		ini_file_size = le16_to_cpu(ini_file->size);1408		for (i = 0; i < ini_file_size / 4; i++) {1409			u32 *val = &((u32 *) ini_file->data)[i];1410 1411			*val = swab32(*val);1412		}1413		types_info->ini_files[count] = ini_file;1414		data += ini_file_size + sizeof(__le16);1415		size -= ini_file_size + sizeof(__le16);1416		count++;1417	}1418}1419 1420#define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT \1421	"mellanox/lc_ini_bundle_%u_%u.bin"1422#define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN \1423	(sizeof(MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT) + 4)1424 1425static int mlxsw_linecard_types_init(struct mlxsw_core *mlxsw_core,1426				     struct mlxsw_linecards *linecards)1427{1428	const struct mlxsw_fw_rev *rev = &linecards->bus_info->fw_rev;1429	char filename[MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN];1430	struct mlxsw_linecard_types_info *types_info;1431	const struct firmware *firmware;1432	int err;1433 1434	err = snprintf(filename, sizeof(filename),1435		       MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT,1436		       rev->minor, rev->subminor);1437	WARN_ON(err >= sizeof(filename));1438 1439	err = request_firmware_direct(&firmware, filename,1440				      linecards->bus_info->dev);1441	if (err) {1442		dev_warn(linecards->bus_info->dev, "Could not request linecards INI file \"%s\", provisioning will not be possible\n",1443			 filename);1444		return 0;1445	}1446 1447	types_info = kzalloc(sizeof(*types_info), GFP_KERNEL);1448	if (!types_info) {1449		release_firmware(firmware);1450		return -ENOMEM;1451	}1452	linecards->types_info = types_info;1453 1454	types_info->data_size = firmware->size;1455	types_info->data = vmalloc(types_info->data_size);1456	if (!types_info->data) {1457		err = -ENOMEM;1458		release_firmware(firmware);1459		goto err_data_alloc;1460	}1461	memcpy(types_info->data, firmware->data, types_info->data_size);1462	release_firmware(firmware);1463 1464	err = mlxsw_linecard_types_file_validate(linecards, types_info);1465	if (err) {1466		err = 0;1467		goto err_type_file_file_validate;1468	}1469 1470	types_info->ini_files = kmalloc_array(types_info->count,1471					      sizeof(struct mlxsw_linecard_ini_file *),1472					      GFP_KERNEL);1473	if (!types_info->ini_files) {1474		err = -ENOMEM;1475		goto err_ini_files_alloc;1476	}1477 1478	mlxsw_linecard_types_file_parse(types_info);1479 1480	return 0;1481 1482err_ini_files_alloc:1483err_type_file_file_validate:1484	vfree(types_info->data);1485err_data_alloc:1486	kfree(types_info);1487	linecards->types_info = NULL;1488	return err;1489}1490 1491static void mlxsw_linecard_types_fini(struct mlxsw_linecards *linecards)1492{1493	struct mlxsw_linecard_types_info *types_info = linecards->types_info;1494 1495	if (!types_info)1496		return;1497	kfree(types_info->ini_files);1498	vfree(types_info->data);1499	kfree(types_info);1500}1501 1502int mlxsw_linecards_init(struct mlxsw_core *mlxsw_core,1503			 const struct mlxsw_bus_info *bus_info)1504{1505	char mgpir_pl[MLXSW_REG_MGPIR_LEN];1506	struct mlxsw_linecards *linecards;1507	u8 slot_count;1508	int err;1509	int i;1510 1511	mlxsw_reg_mgpir_pack(mgpir_pl, 0);1512	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl);1513	if (err)1514		return err;1515 1516	mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL,1517			       NULL, &slot_count);1518	if (!slot_count)1519		return 0;1520 1521	linecards = vzalloc(struct_size(linecards, linecards, slot_count));1522	if (!linecards)1523		return -ENOMEM;1524	linecards->count = slot_count;1525	linecards->mlxsw_core = mlxsw_core;1526	linecards->bus_info = bus_info;1527	INIT_LIST_HEAD(&linecards->event_ops_list);1528	mutex_init(&linecards->event_ops_list_lock);1529 1530	err = mlxsw_linecard_types_init(mlxsw_core, linecards);1531	if (err)1532		goto err_types_init;1533 1534	err = mlxsw_core_traps_register(mlxsw_core, mlxsw_linecard_listener,1535					ARRAY_SIZE(mlxsw_linecard_listener),1536					mlxsw_core);1537	if (err)1538		goto err_traps_register;1539 1540	err = mlxsw_core_irq_event_handler_register(mlxsw_core,1541						    mlxsw_linecards_irq_event_handler);1542	if (err)1543		goto err_irq_event_handler_register;1544 1545	mlxsw_core_linecards_set(mlxsw_core, linecards);1546 1547	for (i = 0; i < linecards->count; i++) {1548		err = mlxsw_linecard_init(mlxsw_core, linecards, i + 1);1549		if (err)1550			goto err_linecard_init;1551	}1552 1553	for (i = 0; i < linecards->count; i++) {1554		err = mlxsw_linecard_event_delivery_init(mlxsw_core, linecards,1555							 i + 1);1556		if (err)1557			goto err_linecard_event_delivery_init;1558	}1559 1560	return 0;1561 1562err_linecard_event_delivery_init:1563	for (i--; i >= 0; i--)1564		mlxsw_linecard_event_delivery_fini(mlxsw_core, linecards, i + 1);1565	i = linecards->count;1566err_linecard_init:1567	for (i--; i >= 0; i--)1568		mlxsw_linecard_fini(mlxsw_core, linecards, i + 1);1569	mlxsw_core_irq_event_handler_unregister(mlxsw_core,1570						mlxsw_linecards_irq_event_handler);1571err_irq_event_handler_register:1572	mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener,1573				    ARRAY_SIZE(mlxsw_linecard_listener),1574				    mlxsw_core);1575err_traps_register:1576	mlxsw_linecard_types_fini(linecards);1577err_types_init:1578	vfree(linecards);1579	return err;1580}1581 1582void mlxsw_linecards_fini(struct mlxsw_core *mlxsw_core)1583{1584	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);1585	int i;1586 1587	if (!linecards)1588		return;1589	for (i = 0; i < linecards->count; i++)1590		mlxsw_linecard_event_delivery_fini(mlxsw_core, linecards, i + 1);1591	for (i = 0; i < linecards->count; i++)1592		mlxsw_linecard_fini(mlxsw_core, linecards, i + 1);1593	mlxsw_core_irq_event_handler_unregister(mlxsw_core,1594						mlxsw_linecards_irq_event_handler);1595	mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener,1596				    ARRAY_SIZE(mlxsw_linecard_listener),1597				    mlxsw_core);1598	mlxsw_linecard_types_fini(linecards);1599	mutex_destroy(&linecards->event_ops_list_lock);1600	WARN_ON(!list_empty(&linecards->event_ops_list));1601	vfree(linecards);1602}1603