File Index
Files and directories
Sign in
FILE

core_linecards.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/mellanox/mlxsw/core_linecards.c C
   1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
   2/* 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 void
  42mlxsw_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 void
  52mlxsw_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 int
 166mlxsw_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 int
 189mlxsw_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 int
 210mlxsw_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 int
 251mlxsw_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 void
 451mlxsw_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 void
 462mlxsw_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 void
 476mlxsw_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 void
 490mlxsw_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 void
 508mlxsw_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		if (err)
 605			goto unlock;
 606
 607		sprintf(buf, "%u.%u.%u", info->fw_major, info->fw_minor,
 608			info->fw_sub_minor);
 609		err = devlink_info_version_running_put(req,
 610						       DEVLINK_INFO_VERSION_GENERIC_FW,
 611						       buf);
 612		if (err)
 613			goto unlock;
 614	}
 615
 616unlock:
 617	mutex_unlock(&linecard->lock);
 618	return err;
 619}
 620
 621static int
 622mlxsw_linecard_provision_set(struct mlxsw_linecard *linecard, u8 card_type,
 623			     u16 hw_revision, u16 ini_version)
 624{
 625	struct mlxsw_linecards *linecards = linecard->linecards;
 626	const char *type;
 627	int err;
 628
 629	type = mlxsw_linecard_types_lookup(linecards, card_type);
 630	mlxsw_linecard_status_event_done(linecard,
 631					 MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION);
 632	if (!type) {
 633		/* It is possible for a line card to be provisioned before
 634		 * driver initialization. Due to a missing INI bundle file
 635		 * or an outdated one, the queried card's type might not
 636		 * be recognized by the driver. In this case, try to query
 637		 * the card's name from the device.
 638		 */
 639		type = mlxsw_linecard_type_name(linecard);
 640		if (IS_ERR(type)) {
 641			mlxsw_linecard_provision_fail(linecard);
 642			return PTR_ERR(type);
 643		}
 644	}
 645	linecard->provisioned = true;
 646	linecard->hw_revision = hw_revision;
 647	linecard->ini_version = ini_version;
 648
 649	err = mlxsw_linecard_bdev_add(linecard);
 650	if (err) {
 651		linecard->provisioned = false;
 652		mlxsw_linecard_provision_fail(linecard);
 653		return err;
 654	}
 655
 656	devlink_linecard_provision_set(linecard->devlink_linecard, type);
 657	return 0;
 658}
 659
 660static void mlxsw_linecard_provision_clear(struct mlxsw_linecard *linecard)
 661{
 662	mlxsw_linecard_status_event_done(linecard,
 663					 MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION);
 664	mlxsw_linecard_bdev_del(linecard);
 665	linecard->provisioned = false;
 666	devlink_linecard_provision_clear(linecard->devlink_linecard);
 667}
 668
 669static int mlxsw_linecard_ready_set(struct mlxsw_linecard *linecard)
 670{
 671	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
 672	char mddc_pl[MLXSW_REG_MDDC_LEN];
 673	int err;
 674
 675	err = mlxsw_linecard_device_info_update(linecard);
 676	if (err)
 677		return err;
 678
 679	mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, false, true);
 680	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl);
 681	if (err)
 682		return err;
 683	linecard->ready = true;
 684	return 0;
 685}
 686
 687static int mlxsw_linecard_ready_clear(struct mlxsw_linecard *linecard)
 688{
 689	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
 690	char mddc_pl[MLXSW_REG_MDDC_LEN];
 691	int err;
 692
 693	mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, false, false);
 694	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl);
 695	if (err)
 696		return err;
 697	linecard->ready = false;
 698	return 0;
 699}
 700
 701static void mlxsw_linecard_active_set(struct mlxsw_linecard *linecard)
 702{
 703	mlxsw_linecard_active_ops_call(linecard);
 704	linecard->active = true;
 705	devlink_linecard_activate(linecard->devlink_linecard);
 706}
 707
 708static void mlxsw_linecard_active_clear(struct mlxsw_linecard *linecard)
 709{
 710	mlxsw_linecard_inactive_ops_call(linecard);
 711	linecard->active = false;
 712	devlink_linecard_deactivate(linecard->devlink_linecard);
 713}
 714
 715static int mlxsw_linecard_status_process(struct mlxsw_linecards *linecards,
 716					 struct mlxsw_linecard *linecard,
 717					 const char *mddq_pl)
 718{
 719	enum mlxsw_reg_mddq_slot_info_ready ready;
 720	bool provisioned, sr_valid, active;
 721	u16 ini_version, hw_revision;
 722	u8 slot_index, card_type;
 723	int err = 0;
 724
 725	mlxsw_reg_mddq_slot_info_unpack(mddq_pl, &slot_index, &provisioned,
 726					&sr_valid, &ready, &active,
 727					&hw_revision, &ini_version,
 728					&card_type);
 729
 730	if (linecard) {
 731		if (WARN_ON(slot_index != linecard->slot_index))
 732			return -EINVAL;
 733	} else {
 734		if (WARN_ON(slot_index > linecards->count))
 735			return -EINVAL;
 736		linecard = mlxsw_linecard_get(linecards, slot_index);
 737	}
 738
 739	mutex_lock(&linecard->lock);
 740
 741	if (provisioned && linecard->provisioned != provisioned) {
 742		err = mlxsw_linecard_provision_set(linecard, card_type,
 743						   hw_revision, ini_version);
 744		if (err)
 745			goto out;
 746	}
 747
 748	if (ready == MLXSW_REG_MDDQ_SLOT_INFO_READY_READY && !linecard->ready) {
 749		err = mlxsw_linecard_ready_set(linecard);
 750		if (err)
 751			goto out;
 752	}
 753
 754	if (active && linecard->active != active)
 755		mlxsw_linecard_active_set(linecard);
 756
 757	if (!active && linecard->active != active)
 758		mlxsw_linecard_active_clear(linecard);
 759
 760	if (ready != MLXSW_REG_MDDQ_SLOT_INFO_READY_READY &&
 761	    linecard->ready) {
 762		err = mlxsw_linecard_ready_clear(linecard);
 763		if (err)
 764			goto out;
 765	}
 766
 767	if (!provisioned && linecard->provisioned != provisioned)
 768		mlxsw_linecard_provision_clear(linecard);
 769
 770out:
 771	mutex_unlock(&linecard->lock);
 772	return err;
 773}
 774
 775static int mlxsw_linecard_status_get_and_process(struct mlxsw_core *mlxsw_core,
 776						 struct mlxsw_linecards *linecards,
 777						 struct mlxsw_linecard *linecard)
 778{
 779	char mddq_pl[MLXSW_REG_MDDQ_LEN];
 780	int err;
 781
 782	mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, false);
 783	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl);
 784	if (err)
 785		return err;
 786
 787	return mlxsw_linecard_status_process(linecards, linecard, mddq_pl);
 788}
 789
 790static void mlxsw_linecards_irq_event_handler(struct mlxsw_core *mlxsw_core)
 791{
 792	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
 793	int i;
 794
 795	/* Handle change of line card active state. */
 796	for (i = 0; i < linecards->count; i++) {
 797		struct mlxsw_linecard *linecard = mlxsw_linecard_get(linecards,
 798								     i + 1);
 799
 800		mlxsw_linecard_status_get_and_process(mlxsw_core, linecards,
 801						      linecard);
 802	}
 803}
 804
 805static const char * const mlxsw_linecard_status_event_type_name[] = {
 806	[MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION] = "provision",
 807	[MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION] = "unprovision",
 808};
 809
 810static void mlxsw_linecard_status_event_to_work(struct work_struct *work)
 811{
 812	struct mlxsw_linecard *linecard =
 813		container_of(work, struct mlxsw_linecard,
 814			     status_event_to_dw.work);
 815
 816	mutex_lock(&linecard->lock);
 817	dev_err(linecard->linecards->bus_info->dev, "linecard %u: Timeout reached waiting on %s status event",
 818		linecard->slot_index,
 819		mlxsw_linecard_status_event_type_name[linecard->status_event_type_to]);
 820	mlxsw_linecard_provision_fail(linecard);
 821	mutex_unlock(&linecard->lock);
 822}
 823
 824static int __mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard)
 825{
 826	dev_info(linecard->linecards->bus_info->dev, "linecard %u: Clearing FSM state error",
 827		 linecard->slot_index);
 828	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
 829			    MLXSW_REG_MBCT_OP_CLEAR_ERRORS, false);
 830	return mlxsw_reg_write(linecard->linecards->mlxsw_core,
 831			       MLXSW_REG(mbct), linecard->mbct_pl);
 832}
 833
 834static int mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard,
 835					enum mlxsw_reg_mbct_fsm_state fsm_state)
 836{
 837	if (fsm_state != MLXSW_REG_MBCT_FSM_STATE_ERROR)
 838		return 0;
 839	return __mlxsw_linecard_fix_fsm_state(linecard);
 840}
 841
 842static int
 843mlxsw_linecard_query_ini_status(struct mlxsw_linecard *linecard,
 844				enum mlxsw_reg_mbct_status *status,
 845				enum mlxsw_reg_mbct_fsm_state *fsm_state,
 846				struct netlink_ext_ack *extack)
 847{
 848	int err;
 849
 850	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
 851			    MLXSW_REG_MBCT_OP_QUERY_STATUS, false);
 852	err = mlxsw_reg_query(linecard->linecards->mlxsw_core, MLXSW_REG(mbct),
 853			      linecard->mbct_pl);
 854	if (err) {
 855		NL_SET_ERR_MSG_MOD(extack, "Failed to query linecard INI status");
 856		return err;
 857	}
 858	mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, status, fsm_state);
 859	return err;
 860}
 861
 862static int
 863mlxsw_linecard_ini_transfer(struct mlxsw_core *mlxsw_core,
 864			    struct mlxsw_linecard *linecard,
 865			    const struct mlxsw_linecard_ini_file *ini_file,
 866			    struct netlink_ext_ack *extack)
 867{
 868	enum mlxsw_reg_mbct_fsm_state fsm_state;
 869	enum mlxsw_reg_mbct_status status;
 870	size_t size_left;
 871	const u8 *data;
 872	int err;
 873
 874	size_left = le16_to_cpu(ini_file->size);
 875	data = ini_file->data;
 876	while (size_left) {
 877		size_t data_size = MLXSW_REG_MBCT_DATA_LEN;
 878		bool is_last = false;
 879
 880		if (size_left <= MLXSW_REG_MBCT_DATA_LEN) {
 881			data_size = size_left;
 882			is_last = true;
 883		}
 884
 885		mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
 886				    MLXSW_REG_MBCT_OP_DATA_TRANSFER, false);
 887		mlxsw_reg_mbct_dt_pack(linecard->mbct_pl, data_size,
 888				       is_last, data);
 889		err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct),
 890				      linecard->mbct_pl);
 891		if (err) {
 892			NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI data transfer");
 893			return err;
 894		}
 895		mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL,
 896				      &status, &fsm_state);
 897		if ((!is_last && status != MLXSW_REG_MBCT_STATUS_PART_DATA) ||
 898		    (is_last && status != MLXSW_REG_MBCT_STATUS_LAST_DATA)) {
 899			NL_SET_ERR_MSG_MOD(extack, "Failed to transfer linecard INI data");
 900			mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
 901			return -EINVAL;
 902		}
 903		size_left -= data_size;
 904		data += data_size;
 905	}
 906
 907	return 0;
 908}
 909
 910static int
 911mlxsw_linecard_ini_erase(struct mlxsw_core *mlxsw_core,
 912			 struct mlxsw_linecard *linecard,
 913			 struct netlink_ext_ack *extack)
 914{
 915	enum mlxsw_reg_mbct_fsm_state fsm_state;
 916	enum mlxsw_reg_mbct_status status;
 917	int err;
 918
 919	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
 920			    MLXSW_REG_MBCT_OP_ERASE_INI_IMAGE, false);
 921	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct),
 922			      linecard->mbct_pl);
 923	if (err) {
 924		NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI erase");
 925		return err;
 926	}
 927	mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state);
 928	switch (status) {
 929	case MLXSW_REG_MBCT_STATUS_ERASE_COMPLETE:
 930		break;
 931	default:
 932		/* Should not happen */
 933		fallthrough;
 934	case MLXSW_REG_MBCT_STATUS_ERASE_FAILED:
 935		NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI");
 936		goto fix_fsm_err_out;
 937	case MLXSW_REG_MBCT_STATUS_ERROR_INI_IN_USE:
 938		NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI while being used");
 939		goto fix_fsm_err_out;
 940	}
 941	return 0;
 942
 943fix_fsm_err_out:
 944	mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
 945	return -EINVAL;
 946}
 947
 948static void mlxsw_linecard_bct_process(struct mlxsw_core *mlxsw_core,
 949				       const char *mbct_pl)
 950{
 951	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
 952	enum mlxsw_reg_mbct_fsm_state fsm_state;
 953	enum mlxsw_reg_mbct_status status;
 954	struct mlxsw_linecard *linecard;
 955	u8 slot_index;
 956
 957	mlxsw_reg_mbct_unpack(mbct_pl, &slot_index, &status, &fsm_state);
 958	if (WARN_ON(slot_index > linecards->count))
 959		return;
 960	linecard = mlxsw_linecard_get(linecards, slot_index);
 961	mutex_lock(&linecard->lock);
 962	if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) {
 963		dev_err(linecards->bus_info->dev, "linecard %u: Failed to activate INI",
 964			linecard->slot_index);
 965		goto fix_fsm_out;
 966	}
 967	mutex_unlock(&linecard->lock);
 968	return;
 969
 970fix_fsm_out:
 971	mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
 972	mlxsw_linecard_provision_fail(linecard);
 973	mutex_unlock(&linecard->lock);
 974}
 975
 976static int
 977mlxsw_linecard_ini_activate(struct mlxsw_core *mlxsw_core,
 978			    struct mlxsw_linecard *linecard,
 979			    struct netlink_ext_ack *extack)
 980{
 981	enum mlxsw_reg_mbct_fsm_state fsm_state;
 982	enum mlxsw_reg_mbct_status status;
 983	int err;
 984
 985	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
 986			    MLXSW_REG_MBCT_OP_ACTIVATE, true);
 987	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct), linecard->mbct_pl);
 988	if (err) {
 989		NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI activation");
 990		return err;
 991	}
 992	mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state);
 993	if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) {
 994		NL_SET_ERR_MSG_MOD(extack, "Failed to activate linecard INI");
 995		goto fix_fsm_err_out;
 996	}
 997
 998	return 0;
 999
1000fix_fsm_err_out:
1001	mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
1002	return -EINVAL;
1003}
1004
1005#define MLXSW_LINECARD_INI_WAIT_RETRIES 10
1006#define MLXSW_LINECARD_INI_WAIT_MS 500
1007
1008static int
1009mlxsw_linecard_ini_in_use_wait(struct mlxsw_core *mlxsw_core,
1010			       struct mlxsw_linecard *linecard,
1011			       struct netlink_ext_ack *extack)
1012{
1013	enum mlxsw_reg_mbct_fsm_state fsm_state;
1014	enum mlxsw_reg_mbct_status status;
1015	unsigned int ini_wait_retries = 0;
1016	int err;
1017
1018query_ini_status:
1019	err = mlxsw_linecard_query_ini_status(linecard, &status,
1020					      &fsm_state, extack);
1021	if (err)
1022		return err;
1023
1024	switch (fsm_state) {
1025	case MLXSW_REG_MBCT_FSM_STATE_INI_IN_USE:
1026		if (ini_wait_retries++ > MLXSW_LINECARD_INI_WAIT_RETRIES) {
1027			NL_SET_ERR_MSG_MOD(extack, "Failed to wait for linecard INI to be unused");
1028			return -EINVAL;
1029		}
1030		mdelay(MLXSW_LINECARD_INI_WAIT_MS);
1031		goto query_ini_status;
1032	default:
1033		break;
1034	}
1035	return 0;
1036}
1037
1038static bool mlxsw_linecard_port_selector(void *priv, u16 local_port)
1039{
1040	struct mlxsw_linecard *linecard = priv;
1041	struct mlxsw_core *mlxsw_core;
1042
1043	mlxsw_core = linecard->linecards->mlxsw_core;
1044	return linecard == mlxsw_core_port_linecard_get(mlxsw_core, local_port);
1045}
1046
1047static int mlxsw_linecard_provision(struct devlink_linecard *devlink_linecard,
1048				    void *priv, const char *type,
1049				    const void *type_priv,
1050				    struct netlink_ext_ack *extack)
1051{
1052	const struct mlxsw_linecard_ini_file *ini_file = type_priv;
1053	struct mlxsw_linecard *linecard = priv;
1054	struct mlxsw_core *mlxsw_core;
1055	int err;
1056
1057	mutex_lock(&linecard->lock);
1058
1059	mlxsw_core = linecard->linecards->mlxsw_core;
1060
1061	err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack);
1062	if (err)
1063		goto err_out;
1064
1065	err = mlxsw_linecard_ini_transfer(mlxsw_core, linecard,
1066					  ini_file, extack);
1067	if (err)
1068		goto err_out;
1069
1070	mlxsw_linecard_status_event_to_schedule(linecard,
1071						MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION);
1072	err = mlxsw_linecard_ini_activate(mlxsw_core, linecard, extack);
1073	if (err)
1074		goto err_out;
1075
1076	goto out;
1077
1078err_out:
1079	mlxsw_linecard_provision_fail(linecard);
1080out:
1081	mutex_unlock(&linecard->lock);
1082	return err;
1083}
1084
1085static int mlxsw_linecard_unprovision(struct devlink_linecard *devlink_linecard,
1086				      void *priv,
1087				      struct netlink_ext_ack *extack)
1088{
1089	struct mlxsw_linecard *linecard = priv;
1090	struct mlxsw_core *mlxsw_core;
1091	int err;
1092
1093	mutex_lock(&linecard->lock);
1094
1095	mlxsw_core = linecard->linecards->mlxsw_core;
1096
1097	mlxsw_core_ports_remove_selected(mlxsw_core,
1098					 mlxsw_linecard_port_selector,
1099					 linecard);
1100
1101	err = mlxsw_linecard_ini_in_use_wait(mlxsw_core, linecard, extack);
1102	if (err)
1103		goto err_out;
1104
1105	mlxsw_linecard_status_event_to_schedule(linecard,
1106						MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION);
1107	err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack);
1108	if (err)
1109		goto err_out;
1110
1111	goto out;
1112
1113err_out:
1114	mlxsw_linecard_provision_fail(linecard);
1115out:
1116	mutex_unlock(&linecard->lock);
1117	return err;
1118}
1119
1120static bool mlxsw_linecard_same_provision(struct devlink_linecard *devlink_linecard,
1121					  void *priv, const char *type,
1122					  const void *type_priv)
1123{
1124	const struct mlxsw_linecard_ini_file *ini_file = type_priv;
1125	struct mlxsw_linecard *linecard = priv;
1126	bool ret;
1127
1128	mutex_lock(&linecard->lock);
1129	ret = linecard->hw_revision == be16_to_cpu(ini_file->format.hw_revision) &&
1130	      linecard->ini_version == be16_to_cpu(ini_file->format.ini_version);
1131	mutex_unlock(&linecard->lock);
1132	return ret;
1133}
1134
1135static unsigned int
1136mlxsw_linecard_types_count(struct devlink_linecard *devlink_linecard,
1137			   void *priv)
1138{
1139	struct mlxsw_linecard *linecard = priv;
1140
1141	return linecard->linecards->types_info ?
1142	       linecard->linecards->types_info->count : 0;
1143}
1144
1145static void mlxsw_linecard_types_get(struct devlink_linecard *devlink_linecard,
1146				     void *priv, unsigned int index,
1147				     const char **type, const void **type_priv)
1148{
1149	struct mlxsw_linecard_types_info *types_info;
1150	struct mlxsw_linecard_ini_file *ini_file;
1151	struct mlxsw_linecard *linecard = priv;
1152
1153	types_info = linecard->linecards->types_info;
1154	if (WARN_ON_ONCE(!types_info))
1155		return;
1156	ini_file = types_info->ini_files[index];
1157	*type = ini_file->format.name;
1158	*type_priv = ini_file;
1159}
1160
1161static const struct devlink_linecard_ops mlxsw_linecard_ops = {
1162	.provision = mlxsw_linecard_provision,
1163	.unprovision = mlxsw_linecard_unprovision,
1164	.same_provision = mlxsw_linecard_same_provision,
1165	.types_count = mlxsw_linecard_types_count,
1166	.types_get = mlxsw_linecard_types_get,
1167};
1168
1169struct mlxsw_linecard_status_event {
1170	struct mlxsw_core *mlxsw_core;
1171	char mddq_pl[MLXSW_REG_MDDQ_LEN];
1172	struct work_struct work;
1173};
1174
1175static void mlxsw_linecard_status_event_work(struct work_struct *work)
1176{
1177	struct mlxsw_linecard_status_event *event;
1178	struct mlxsw_linecards *linecards;
1179	struct mlxsw_core *mlxsw_core;
1180
1181	event = container_of(work, struct mlxsw_linecard_status_event, work);
1182	mlxsw_core = event->mlxsw_core;
1183	linecards = mlxsw_core_linecards(mlxsw_core);
1184	mlxsw_linecard_status_process(linecards, NULL, event->mddq_pl);
1185	kfree(event);
1186}
1187
1188static void
1189mlxsw_linecard_status_listener_func(const struct mlxsw_reg_info *reg,
1190				    char *mddq_pl, void *priv)
1191{
1192	struct mlxsw_linecard_status_event *event;
1193	struct mlxsw_core *mlxsw_core = priv;
1194
1195	event = kmalloc(sizeof(*event), GFP_ATOMIC);
1196	if (!event)
1197		return;
1198	event->mlxsw_core = mlxsw_core;
1199	memcpy(event->mddq_pl, mddq_pl, sizeof(event->mddq_pl));
1200	INIT_WORK(&event->work, mlxsw_linecard_status_event_work);
1201	mlxsw_core_schedule_work(&event->work);
1202}
1203
1204struct mlxsw_linecard_bct_event {
1205	struct mlxsw_core *mlxsw_core;
1206	char mbct_pl[MLXSW_REG_MBCT_LEN];
1207	struct work_struct work;
1208};
1209
1210static void mlxsw_linecard_bct_event_work(struct work_struct *work)
1211{
1212	struct mlxsw_linecard_bct_event *event;
1213	struct mlxsw_core *mlxsw_core;
1214
1215	event = container_of(work, struct mlxsw_linecard_bct_event, work);
1216	mlxsw_core = event->mlxsw_core;
1217	mlxsw_linecard_bct_process(mlxsw_core, event->mbct_pl);
1218	kfree(event);
1219}
1220
1221static void
1222mlxsw_linecard_bct_listener_func(const struct mlxsw_reg_info *reg,
1223				 char *mbct_pl, void *priv)
1224{
1225	struct mlxsw_linecard_bct_event *event;
1226	struct mlxsw_core *mlxsw_core = priv;
1227
1228	event = kmalloc(sizeof(*event), GFP_ATOMIC);
1229	if (!event)
1230		return;
1231	event->mlxsw_core = mlxsw_core;
1232	memcpy(event->mbct_pl, mbct_pl, sizeof(event->mbct_pl));
1233	INIT_WORK(&event->work, mlxsw_linecard_bct_event_work);
1234	mlxsw_core_schedule_work(&event->work);
1235}
1236
1237static const struct mlxsw_listener mlxsw_linecard_listener[] = {
1238	MLXSW_CORE_EVENTL(mlxsw_linecard_status_listener_func, DSDSC),
1239	MLXSW_CORE_EVENTL(mlxsw_linecard_bct_listener_func, BCTOE),
1240};
1241
1242static int mlxsw_linecard_event_delivery_set(struct mlxsw_core *mlxsw_core,
1243					     struct mlxsw_linecard *linecard,
1244					     bool enable)
1245{
1246	char mddq_pl[MLXSW_REG_MDDQ_LEN];
1247
1248	mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, enable);
1249	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddq), mddq_pl);
1250}
1251
1252static int mlxsw_linecard_init(struct mlxsw_core *mlxsw_core,
1253			       struct mlxsw_linecards *linecards,
1254			       u8 slot_index)
1255{
1256	struct devlink_linecard *devlink_linecard;
1257	struct mlxsw_linecard *linecard;
1258
1259	linecard = mlxsw_linecard_get(linecards, slot_index);
1260	linecard->slot_index = slot_index;
1261	linecard->linecards = linecards;
1262	mutex_init(&linecard->lock);
1263
1264	devlink_linecard = devl_linecard_create(priv_to_devlink(mlxsw_core),
1265						slot_index, &mlxsw_linecard_ops,
1266						linecard);
1267	if (IS_ERR(devlink_linecard))
1268		return PTR_ERR(devlink_linecard);
1269
1270	linecard->devlink_linecard = devlink_linecard;
1271	INIT_DELAYED_WORK(&linecard->status_event_to_dw,
1272			  &mlxsw_linecard_status_event_to_work);
1273
1274	return 0;
1275}
1276
1277static void mlxsw_linecard_fini(struct mlxsw_core *mlxsw_core,
1278				struct mlxsw_linecards *linecards,
1279				u8 slot_index)
1280{
1281	struct mlxsw_linecard *linecard;
1282
1283	linecard = mlxsw_linecard_get(linecards, slot_index);
1284	cancel_delayed_work_sync(&linecard->status_event_to_dw);
1285	/* Make sure all scheduled events are processed */
1286	mlxsw_core_flush_owq();
1287	if (linecard->active)
1288		mlxsw_linecard_active_clear(linecard);
1289	mlxsw_linecard_bdev_del(linecard);
1290	devl_linecard_destroy(linecard->devlink_linecard);
1291	mutex_destroy(&linecard->lock);
1292}
1293
1294static int
1295mlxsw_linecard_event_delivery_init(struct mlxsw_core *mlxsw_core,
1296				   struct mlxsw_linecards *linecards,
1297				   u8 slot_index)
1298{
1299	struct mlxsw_linecard *linecard;
1300	int err;
1301
1302	linecard = mlxsw_linecard_get(linecards, slot_index);
1303	err = mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, true);
1304	if (err)
1305		return err;
1306
1307	err = mlxsw_linecard_status_get_and_process(mlxsw_core, linecards,
1308						    linecard);
1309	if (err)
1310		goto err_status_get_and_process;
1311
1312	return 0;
1313
1314err_status_get_and_process:
1315	mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false);
1316	return err;
1317}
1318
1319static void
1320mlxsw_linecard_event_delivery_fini(struct mlxsw_core *mlxsw_core,
1321				   struct mlxsw_linecards *linecards,
1322				   u8 slot_index)
1323{
1324	struct mlxsw_linecard *linecard;
1325
1326	linecard = mlxsw_linecard_get(linecards, slot_index);
1327	mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false);
1328}
1329
1330/*       LINECARDS INI BUNDLE FILE
1331 *  +----------------------------------+
1332 *  |        MAGIC ("NVLCINI+")        |
1333 *  +----------------------------------+     +--------------------+
1334 *  |  INI 0                           +---> | __le16 size        |
1335 *  +----------------------------------+     | __be16 hw_revision |
1336 *  |  INI 1                           |     | __be16 ini_version |
1337 *  +----------------------------------+     | u8 __dontcare[3]   |
1338 *  |  ...                             |     | u8 type            |
1339 *  +----------------------------------+     | u8 name[20]        |
1340 *  |  INI N                           |     | ...                |
1341 *  +----------------------------------+     +--------------------+
1342 */
1343
1344#define MLXSW_LINECARDS_INI_BUNDLE_MAGIC "NVLCINI+"
1345
1346static int
1347mlxsw_linecard_types_file_validate(struct mlxsw_linecards *linecards,
1348				   struct mlxsw_linecard_types_info *types_info)
1349{
1350	size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC);
1351	struct mlxsw_linecard_ini_file *ini_file;
1352	size_t size = types_info->data_size;
1353	const u8 *data = types_info->data;
1354	unsigned int count = 0;
1355	u16 ini_file_size;
1356
1357	if (size < magic_size) {
1358		dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file size, smaller than magic size\n");
1359		return -EINVAL;
1360	}
1361	if (memcmp(data, MLXSW_LINECARDS_INI_BUNDLE_MAGIC, magic_size)) {
1362		dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file magic pattern\n");
1363		return -EINVAL;
1364	}
1365
1366	data += magic_size;
1367	size -= magic_size;
1368
1369	while (size > 0) {
1370		if (size < sizeof(*ini_file)) {
1371			dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI which is smaller than bare minimum\n");
1372			return -EINVAL;
1373		}
1374		ini_file = (struct mlxsw_linecard_ini_file *) data;
1375		ini_file_size = le16_to_cpu(ini_file->size);
1376		if (ini_file_size + sizeof(__le16) > size) {
1377			dev_warn(linecards->bus_info->dev, "Linecards INIs file appears to be truncated\n");
1378			return -EINVAL;
1379		}
1380		if (ini_file_size % 4) {
1381			dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI with invalid size\n");
1382			return -EINVAL;
1383		}
1384		data += ini_file_size + sizeof(__le16);
1385		size -= ini_file_size + sizeof(__le16);
1386		count++;
1387	}
1388	if (!count) {
1389		dev_warn(linecards->bus_info->dev, "Linecards INIs file does not contain any INI\n");
1390		return -EINVAL;
1391	}
1392	types_info->count = count;
1393	return 0;
1394}
1395
1396static void
1397mlxsw_linecard_types_file_parse(struct mlxsw_linecard_types_info *types_info)
1398{
1399	size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC);
1400	size_t size = types_info->data_size - magic_size;
1401	const u8 *data = types_info->data + magic_size;
1402	struct mlxsw_linecard_ini_file *ini_file;
1403	unsigned int count = 0;
1404	u16 ini_file_size;
1405	int i;
1406
1407	while (size) {
1408		ini_file = (struct mlxsw_linecard_ini_file *) data;
1409		ini_file_size = le16_to_cpu(ini_file->size);
1410		for (i = 0; i < ini_file_size / 4; i++) {
1411			u32 *val = &((u32 *) ini_file->data)[i];
1412
1413			*val = swab32(*val);
1414		}
1415		types_info->ini_files[count] = ini_file;
1416		data += ini_file_size + sizeof(__le16);
1417		size -= ini_file_size + sizeof(__le16);
1418		count++;
1419	}
1420}
1421
1422#define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT \
1423	"mellanox/lc_ini_bundle_%u_%u.bin"
1424#define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN \
1425	(sizeof(MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT) + 4)
1426
1427static int mlxsw_linecard_types_init(struct mlxsw_core *mlxsw_core,
1428				     struct mlxsw_linecards *linecards)
1429{
1430	const struct mlxsw_fw_rev *rev = &linecards->bus_info->fw_rev;
1431	char filename[MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN];
1432	struct mlxsw_linecard_types_info *types_info;
1433	const struct firmware *firmware;
1434	int err;
1435
1436	err = snprintf(filename, sizeof(filename),
1437		       MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT,
1438		       rev->minor, rev->subminor);
1439	WARN_ON(err >= sizeof(filename));
1440
1441	err = request_firmware_direct(&firmware, filename,
1442				      linecards->bus_info->dev);
1443	if (err) {
1444		dev_warn(linecards->bus_info->dev, "Could not request linecards INI file \"%s\", provisioning will not be possible\n",
1445			 filename);
1446		return 0;
1447	}
1448
1449	types_info = kzalloc(sizeof(*types_info), GFP_KERNEL);
1450	if (!types_info) {
1451		release_firmware(firmware);
1452		return -ENOMEM;
1453	}
1454	linecards->types_info = types_info;
1455
1456	types_info->data_size = firmware->size;
1457	types_info->data = vmalloc(types_info->data_size);
1458	if (!types_info->data) {
1459		err = -ENOMEM;
1460		release_firmware(firmware);
1461		goto err_data_alloc;
1462	}
1463	memcpy(types_info->data, firmware->data, types_info->data_size);
1464	release_firmware(firmware);
1465
1466	err = mlxsw_linecard_types_file_validate(linecards, types_info);
1467	if (err) {
1468		err = 0;
1469		goto err_type_file_file_validate;
1470	}
1471
1472	types_info->ini_files = kmalloc_array(types_info->count,
1473					      sizeof(struct mlxsw_linecard_ini_file *),
1474					      GFP_KERNEL);
1475	if (!types_info->ini_files) {
1476		err = -ENOMEM;
1477		goto err_ini_files_alloc;
1478	}
1479
1480	mlxsw_linecard_types_file_parse(types_info);
1481
1482	return 0;
1483
1484err_ini_files_alloc:
1485err_type_file_file_validate:
1486	vfree(types_info->data);
1487err_data_alloc:
1488	kfree(types_info);
1489	linecards->types_info = NULL;
1490	return err;
1491}
1492
1493static void mlxsw_linecard_types_fini(struct mlxsw_linecards *linecards)
1494{
1495	struct mlxsw_linecard_types_info *types_info = linecards->types_info;
1496
1497	if (!types_info)
1498		return;
1499	kfree(types_info->ini_files);
1500	vfree(types_info->data);
1501	kfree(types_info);
1502}
1503
1504int mlxsw_linecards_init(struct mlxsw_core *mlxsw_core,
1505			 const struct mlxsw_bus_info *bus_info)
1506{
1507	char mgpir_pl[MLXSW_REG_MGPIR_LEN];
1508	struct mlxsw_linecards *linecards;
1509	u8 slot_count;
1510	int err;
1511	int i;
1512
1513	mlxsw_reg_mgpir_pack(mgpir_pl, 0);
1514	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl);
1515	if (err)
1516		return err;
1517
1518	mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL,
1519			       NULL, &slot_count);
1520	if (!slot_count)
1521		return 0;
1522
1523	linecards = vzalloc(struct_size(linecards, linecards, slot_count));
1524	if (!linecards)
1525		return -ENOMEM;
1526	linecards->count = slot_count;
1527	linecards->mlxsw_core = mlxsw_core;
1528	linecards->bus_info = bus_info;
1529	INIT_LIST_HEAD(&linecards->event_ops_list);
1530	mutex_init(&linecards->event_ops_list_lock);
1531
1532	err = mlxsw_linecard_types_init(mlxsw_core, linecards);
1533	if (err)
1534		goto err_types_init;
1535
1536	err = mlxsw_core_traps_register(mlxsw_core, mlxsw_linecard_listener,
1537					ARRAY_SIZE(mlxsw_linecard_listener),
1538					mlxsw_core);
1539	if (err)
1540		goto err_traps_register;
1541
1542	err = mlxsw_core_irq_event_handler_register(mlxsw_core,
1543						    mlxsw_linecards_irq_event_handler);
1544	if (err)
1545		goto err_irq_event_handler_register;
1546
1547	mlxsw_core_linecards_set(mlxsw_core, linecards);
1548
1549	for (i = 0; i < linecards->count; i++) {
1550		err = mlxsw_linecard_init(mlxsw_core, linecards, i + 1);
1551		if (err)
1552			goto err_linecard_init;
1553	}
1554
1555	for (i = 0; i < linecards->count; i++) {
1556		err = mlxsw_linecard_event_delivery_init(mlxsw_core, linecards,
1557							 i + 1);
1558		if (err)
1559			goto err_linecard_event_delivery_init;
1560	}
1561
1562	return 0;
1563
1564err_linecard_event_delivery_init:
1565	for (i--; i >= 0; i--)
1566		mlxsw_linecard_event_delivery_fini(mlxsw_core, linecards, i + 1);
1567	i = linecards->count;
1568err_linecard_init:
1569	for (i--; i >= 0; i--)
1570		mlxsw_linecard_fini(mlxsw_core, linecards, i + 1);
1571	mlxsw_core_irq_event_handler_unregister(mlxsw_core,
1572						mlxsw_linecards_irq_event_handler);
1573err_irq_event_handler_register:
1574	mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener,
1575				    ARRAY_SIZE(mlxsw_linecard_listener),
1576				    mlxsw_core);
1577err_traps_register:
1578	mlxsw_linecard_types_fini(linecards);
1579err_types_init:
1580	vfree(linecards);
1581	return err;
1582}
1583
1584void mlxsw_linecards_fini(struct mlxsw_core *mlxsw_core)
1585{
1586	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
1587	int i;
1588
1589	if (!linecards)
1590		return;
1591	for (i = 0; i < linecards->count; i++)
1592		mlxsw_linecard_event_delivery_fini(mlxsw_core, linecards, i + 1);
1593	for (i = 0; i < linecards->count; i++)
1594		mlxsw_linecard_fini(mlxsw_core, linecards, i + 1);
1595	mlxsw_core_irq_event_handler_unregister(mlxsw_core,
1596						mlxsw_linecards_irq_event_handler);
1597	mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener,
1598				    ARRAY_SIZE(mlxsw_linecard_listener),
1599				    mlxsw_core);
1600	mlxsw_linecard_types_fini(linecards);
1601	mutex_destroy(&linecards->event_ops_list_lock);
1602	WARN_ON(!list_empty(&linecards->event_ops_list));
1603	vfree(linecards);
1604}
~/ Read-only file preview.