cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

core_linecards.c (32814B)


      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
     17struct mlxsw_linecard_ini_file {
     18	__le16 size;
     19	union {
     20		u8 data[0];
     21		struct {
     22			__be16 hw_revision;
     23			__be16 ini_version;
     24			u8 __dontcare[3];
     25			u8 type;
     26			u8 name[20];
     27		} format;
     28	};
     29};
     30
     31struct mlxsw_linecard_types_info {
     32	struct mlxsw_linecard_ini_file **ini_files;
     33	unsigned int count;
     34	size_t data_size;
     35	char *data;
     36};
     37
     38#define MLXSW_LINECARD_STATUS_EVENT_TO (10 * MSEC_PER_SEC)
     39
     40static void
     41mlxsw_linecard_status_event_to_schedule(struct mlxsw_linecard *linecard,
     42					enum mlxsw_linecard_status_event_type status_event_type)
     43{
     44	cancel_delayed_work_sync(&linecard->status_event_to_dw);
     45	linecard->status_event_type_to = status_event_type;
     46	mlxsw_core_schedule_dw(&linecard->status_event_to_dw,
     47			       msecs_to_jiffies(MLXSW_LINECARD_STATUS_EVENT_TO));
     48}
     49
     50static void
     51mlxsw_linecard_status_event_done(struct mlxsw_linecard *linecard,
     52				 enum mlxsw_linecard_status_event_type status_event_type)
     53{
     54	if (linecard->status_event_type_to == status_event_type)
     55		cancel_delayed_work_sync(&linecard->status_event_to_dw);
     56}
     57
     58static const char *
     59mlxsw_linecard_types_lookup(struct mlxsw_linecards *linecards, u8 card_type)
     60{
     61	struct mlxsw_linecard_types_info *types_info;
     62	struct mlxsw_linecard_ini_file *ini_file;
     63	int i;
     64
     65	types_info = linecards->types_info;
     66	if (!types_info)
     67		return NULL;
     68	for (i = 0; i < types_info->count; i++) {
     69		ini_file = linecards->types_info->ini_files[i];
     70		if (ini_file->format.type == card_type)
     71			return ini_file->format.name;
     72	}
     73	return NULL;
     74}
     75
     76static const char *mlxsw_linecard_type_name(struct mlxsw_linecard *linecard)
     77{
     78	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
     79	char mddq_pl[MLXSW_REG_MDDQ_LEN];
     80	int err;
     81
     82	mlxsw_reg_mddq_slot_name_pack(mddq_pl, linecard->slot_index);
     83	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl);
     84	if (err)
     85		return ERR_PTR(err);
     86	mlxsw_reg_mddq_slot_name_unpack(mddq_pl, linecard->name);
     87	return linecard->name;
     88}
     89
     90static void mlxsw_linecard_provision_fail(struct mlxsw_linecard *linecard)
     91{
     92	linecard->provisioned = false;
     93	linecard->ready = false;
     94	linecard->active = false;
     95	devlink_linecard_provision_fail(linecard->devlink_linecard);
     96}
     97
     98struct mlxsw_linecards_event_ops_item {
     99	struct list_head list;
    100	const struct mlxsw_linecards_event_ops *event_ops;
    101	void *priv;
    102};
    103
    104static void
    105mlxsw_linecard_event_op_call(struct mlxsw_linecard *linecard,
    106			     mlxsw_linecards_event_op_t *op, void *priv)
    107{
    108	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
    109
    110	if (!op)
    111		return;
    112	op(mlxsw_core, linecard->slot_index, priv);
    113}
    114
    115static void
    116mlxsw_linecard_active_ops_call(struct mlxsw_linecard *linecard)
    117{
    118	struct mlxsw_linecards *linecards = linecard->linecards;
    119	struct mlxsw_linecards_event_ops_item *item;
    120
    121	mutex_lock(&linecards->event_ops_list_lock);
    122	list_for_each_entry(item, &linecards->event_ops_list, list)
    123		mlxsw_linecard_event_op_call(linecard,
    124					     item->event_ops->got_active,
    125					     item->priv);
    126	mutex_unlock(&linecards->event_ops_list_lock);
    127}
    128
    129static void
    130mlxsw_linecard_inactive_ops_call(struct mlxsw_linecard *linecard)
    131{
    132	struct mlxsw_linecards *linecards = linecard->linecards;
    133	struct mlxsw_linecards_event_ops_item *item;
    134
    135	mutex_lock(&linecards->event_ops_list_lock);
    136	list_for_each_entry(item, &linecards->event_ops_list, list)
    137		mlxsw_linecard_event_op_call(linecard,
    138					     item->event_ops->got_inactive,
    139					     item->priv);
    140	mutex_unlock(&linecards->event_ops_list_lock);
    141}
    142
    143static void
    144mlxsw_linecards_event_ops_register_call(struct mlxsw_linecards *linecards,
    145					const struct mlxsw_linecards_event_ops_item *item)
    146{
    147	struct mlxsw_linecard *linecard;
    148	int i;
    149
    150	for (i = 0; i < linecards->count; i++) {
    151		linecard = mlxsw_linecard_get(linecards, i + 1);
    152		mutex_lock(&linecard->lock);
    153		if (linecard->active)
    154			mlxsw_linecard_event_op_call(linecard,
    155						     item->event_ops->got_active,
    156						     item->priv);
    157		mutex_unlock(&linecard->lock);
    158	}
    159}
    160
    161static void
    162mlxsw_linecards_event_ops_unregister_call(struct mlxsw_linecards *linecards,
    163					  const struct mlxsw_linecards_event_ops_item *item)
    164{
    165	struct mlxsw_linecard *linecard;
    166	int i;
    167
    168	for (i = 0; i < linecards->count; i++) {
    169		linecard = mlxsw_linecard_get(linecards, i + 1);
    170		mutex_lock(&linecard->lock);
    171		if (linecard->active)
    172			mlxsw_linecard_event_op_call(linecard,
    173						     item->event_ops->got_inactive,
    174						     item->priv);
    175		mutex_unlock(&linecard->lock);
    176	}
    177}
    178
    179int mlxsw_linecards_event_ops_register(struct mlxsw_core *mlxsw_core,
    180				       struct mlxsw_linecards_event_ops *ops,
    181				       void *priv)
    182{
    183	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
    184	struct mlxsw_linecards_event_ops_item *item;
    185
    186	if (!linecards)
    187		return 0;
    188	item = kzalloc(sizeof(*item), GFP_KERNEL);
    189	if (!item)
    190		return -ENOMEM;
    191	item->event_ops = ops;
    192	item->priv = priv;
    193
    194	mutex_lock(&linecards->event_ops_list_lock);
    195	list_add_tail(&item->list, &linecards->event_ops_list);
    196	mutex_unlock(&linecards->event_ops_list_lock);
    197	mlxsw_linecards_event_ops_register_call(linecards, item);
    198	return 0;
    199}
    200EXPORT_SYMBOL(mlxsw_linecards_event_ops_register);
    201
    202void mlxsw_linecards_event_ops_unregister(struct mlxsw_core *mlxsw_core,
    203					  struct mlxsw_linecards_event_ops *ops,
    204					  void *priv)
    205{
    206	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
    207	struct mlxsw_linecards_event_ops_item *item, *tmp;
    208	bool found = false;
    209
    210	if (!linecards)
    211		return;
    212	mutex_lock(&linecards->event_ops_list_lock);
    213	list_for_each_entry_safe(item, tmp, &linecards->event_ops_list, list) {
    214		if (item->event_ops == ops && item->priv == priv) {
    215			list_del(&item->list);
    216			found = true;
    217			break;
    218		}
    219	}
    220	mutex_unlock(&linecards->event_ops_list_lock);
    221
    222	if (!found)
    223		return;
    224	mlxsw_linecards_event_ops_unregister_call(linecards, item);
    225	kfree(item);
    226}
    227EXPORT_SYMBOL(mlxsw_linecards_event_ops_unregister);
    228
    229static int
    230mlxsw_linecard_provision_set(struct mlxsw_linecard *linecard, u8 card_type,
    231			     u16 hw_revision, u16 ini_version)
    232{
    233	struct mlxsw_linecards *linecards = linecard->linecards;
    234	const char *type;
    235
    236	type = mlxsw_linecard_types_lookup(linecards, card_type);
    237	mlxsw_linecard_status_event_done(linecard,
    238					 MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION);
    239	if (!type) {
    240		/* It is possible for a line card to be provisioned before
    241		 * driver initialization. Due to a missing INI bundle file
    242		 * or an outdated one, the queried card's type might not
    243		 * be recognized by the driver. In this case, try to query
    244		 * the card's name from the device.
    245		 */
    246		type = mlxsw_linecard_type_name(linecard);
    247		if (IS_ERR(type)) {
    248			mlxsw_linecard_provision_fail(linecard);
    249			return PTR_ERR(type);
    250		}
    251	}
    252	linecard->provisioned = true;
    253	linecard->hw_revision = hw_revision;
    254	linecard->ini_version = ini_version;
    255	devlink_linecard_provision_set(linecard->devlink_linecard, type);
    256	return 0;
    257}
    258
    259static void mlxsw_linecard_provision_clear(struct mlxsw_linecard *linecard)
    260{
    261	mlxsw_linecard_status_event_done(linecard,
    262					 MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION);
    263	linecard->provisioned = false;
    264	devlink_linecard_provision_clear(linecard->devlink_linecard);
    265}
    266
    267static int mlxsw_linecard_ready_set(struct mlxsw_linecard *linecard)
    268{
    269	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
    270	char mddc_pl[MLXSW_REG_MDDC_LEN];
    271	int err;
    272
    273	mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, false, true);
    274	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl);
    275	if (err)
    276		return err;
    277	linecard->ready = true;
    278	return 0;
    279}
    280
    281static int mlxsw_linecard_ready_clear(struct mlxsw_linecard *linecard)
    282{
    283	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
    284	char mddc_pl[MLXSW_REG_MDDC_LEN];
    285	int err;
    286
    287	mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, false, false);
    288	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl);
    289	if (err)
    290		return err;
    291	linecard->ready = false;
    292	return 0;
    293}
    294
    295static void mlxsw_linecard_active_set(struct mlxsw_linecard *linecard)
    296{
    297	mlxsw_linecard_active_ops_call(linecard);
    298	linecard->active = true;
    299	devlink_linecard_activate(linecard->devlink_linecard);
    300}
    301
    302static void mlxsw_linecard_active_clear(struct mlxsw_linecard *linecard)
    303{
    304	mlxsw_linecard_inactive_ops_call(linecard);
    305	linecard->active = false;
    306	devlink_linecard_deactivate(linecard->devlink_linecard);
    307}
    308
    309static int mlxsw_linecard_status_process(struct mlxsw_linecards *linecards,
    310					 struct mlxsw_linecard *linecard,
    311					 const char *mddq_pl)
    312{
    313	enum mlxsw_reg_mddq_slot_info_ready ready;
    314	bool provisioned, sr_valid, active;
    315	u16 ini_version, hw_revision;
    316	u8 slot_index, card_type;
    317	int err = 0;
    318
    319	mlxsw_reg_mddq_slot_info_unpack(mddq_pl, &slot_index, &provisioned,
    320					&sr_valid, &ready, &active,
    321					&hw_revision, &ini_version,
    322					&card_type);
    323
    324	if (linecard) {
    325		if (WARN_ON(slot_index != linecard->slot_index))
    326			return -EINVAL;
    327	} else {
    328		if (WARN_ON(slot_index > linecards->count))
    329			return -EINVAL;
    330		linecard = mlxsw_linecard_get(linecards, slot_index);
    331	}
    332
    333	mutex_lock(&linecard->lock);
    334
    335	if (provisioned && linecard->provisioned != provisioned) {
    336		err = mlxsw_linecard_provision_set(linecard, card_type,
    337						   hw_revision, ini_version);
    338		if (err)
    339			goto out;
    340	}
    341
    342	if (ready == MLXSW_REG_MDDQ_SLOT_INFO_READY_READY && !linecard->ready) {
    343		err = mlxsw_linecard_ready_set(linecard);
    344		if (err)
    345			goto out;
    346	}
    347
    348	if (active && linecard->active != active)
    349		mlxsw_linecard_active_set(linecard);
    350
    351	if (!active && linecard->active != active)
    352		mlxsw_linecard_active_clear(linecard);
    353
    354	if (ready != MLXSW_REG_MDDQ_SLOT_INFO_READY_READY &&
    355	    linecard->ready) {
    356		err = mlxsw_linecard_ready_clear(linecard);
    357		if (err)
    358			goto out;
    359	}
    360
    361	if (!provisioned && linecard->provisioned != provisioned)
    362		mlxsw_linecard_provision_clear(linecard);
    363
    364out:
    365	mutex_unlock(&linecard->lock);
    366	return err;
    367}
    368
    369static int mlxsw_linecard_status_get_and_process(struct mlxsw_core *mlxsw_core,
    370						 struct mlxsw_linecards *linecards,
    371						 struct mlxsw_linecard *linecard)
    372{
    373	char mddq_pl[MLXSW_REG_MDDQ_LEN];
    374	int err;
    375
    376	mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, false);
    377	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl);
    378	if (err)
    379		return err;
    380
    381	return mlxsw_linecard_status_process(linecards, linecard, mddq_pl);
    382}
    383
    384static const char * const mlxsw_linecard_status_event_type_name[] = {
    385	[MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION] = "provision",
    386	[MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION] = "unprovision",
    387};
    388
    389static void mlxsw_linecard_status_event_to_work(struct work_struct *work)
    390{
    391	struct mlxsw_linecard *linecard =
    392		container_of(work, struct mlxsw_linecard,
    393			     status_event_to_dw.work);
    394
    395	mutex_lock(&linecard->lock);
    396	dev_err(linecard->linecards->bus_info->dev, "linecard %u: Timeout reached waiting on %s status event",
    397		linecard->slot_index,
    398		mlxsw_linecard_status_event_type_name[linecard->status_event_type_to]);
    399	mlxsw_linecard_provision_fail(linecard);
    400	mutex_unlock(&linecard->lock);
    401}
    402
    403static int __mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard)
    404{
    405	dev_info(linecard->linecards->bus_info->dev, "linecard %u: Clearing FSM state error",
    406		 linecard->slot_index);
    407	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
    408			    MLXSW_REG_MBCT_OP_CLEAR_ERRORS, false);
    409	return mlxsw_reg_write(linecard->linecards->mlxsw_core,
    410			       MLXSW_REG(mbct), linecard->mbct_pl);
    411}
    412
    413static int mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard,
    414					enum mlxsw_reg_mbct_fsm_state fsm_state)
    415{
    416	if (fsm_state != MLXSW_REG_MBCT_FSM_STATE_ERROR)
    417		return 0;
    418	return __mlxsw_linecard_fix_fsm_state(linecard);
    419}
    420
    421static int
    422mlxsw_linecard_query_ini_status(struct mlxsw_linecard *linecard,
    423				enum mlxsw_reg_mbct_status *status,
    424				enum mlxsw_reg_mbct_fsm_state *fsm_state,
    425				struct netlink_ext_ack *extack)
    426{
    427	int err;
    428
    429	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
    430			    MLXSW_REG_MBCT_OP_QUERY_STATUS, false);
    431	err = mlxsw_reg_query(linecard->linecards->mlxsw_core, MLXSW_REG(mbct),
    432			      linecard->mbct_pl);
    433	if (err) {
    434		NL_SET_ERR_MSG_MOD(extack, "Failed to query linecard INI status");
    435		return err;
    436	}
    437	mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, status, fsm_state);
    438	return err;
    439}
    440
    441static int
    442mlxsw_linecard_ini_transfer(struct mlxsw_core *mlxsw_core,
    443			    struct mlxsw_linecard *linecard,
    444			    const struct mlxsw_linecard_ini_file *ini_file,
    445			    struct netlink_ext_ack *extack)
    446{
    447	enum mlxsw_reg_mbct_fsm_state fsm_state;
    448	enum mlxsw_reg_mbct_status status;
    449	size_t size_left;
    450	const u8 *data;
    451	int err;
    452
    453	size_left = le16_to_cpu(ini_file->size);
    454	data = ini_file->data;
    455	while (size_left) {
    456		size_t data_size = MLXSW_REG_MBCT_DATA_LEN;
    457		bool is_last = false;
    458
    459		if (size_left <= MLXSW_REG_MBCT_DATA_LEN) {
    460			data_size = size_left;
    461			is_last = true;
    462		}
    463
    464		mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
    465				    MLXSW_REG_MBCT_OP_DATA_TRANSFER, false);
    466		mlxsw_reg_mbct_dt_pack(linecard->mbct_pl, data_size,
    467				       is_last, data);
    468		err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct),
    469				      linecard->mbct_pl);
    470		if (err) {
    471			NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI data transfer");
    472			return err;
    473		}
    474		mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL,
    475				      &status, &fsm_state);
    476		if ((!is_last && status != MLXSW_REG_MBCT_STATUS_PART_DATA) ||
    477		    (is_last && status != MLXSW_REG_MBCT_STATUS_LAST_DATA)) {
    478			NL_SET_ERR_MSG_MOD(extack, "Failed to transfer linecard INI data");
    479			mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
    480			return -EINVAL;
    481		}
    482		size_left -= data_size;
    483		data += data_size;
    484	}
    485
    486	return 0;
    487}
    488
    489static int
    490mlxsw_linecard_ini_erase(struct mlxsw_core *mlxsw_core,
    491			 struct mlxsw_linecard *linecard,
    492			 struct netlink_ext_ack *extack)
    493{
    494	enum mlxsw_reg_mbct_fsm_state fsm_state;
    495	enum mlxsw_reg_mbct_status status;
    496	int err;
    497
    498	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
    499			    MLXSW_REG_MBCT_OP_ERASE_INI_IMAGE, false);
    500	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct),
    501			      linecard->mbct_pl);
    502	if (err) {
    503		NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI erase");
    504		return err;
    505	}
    506	mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state);
    507	switch (status) {
    508	case MLXSW_REG_MBCT_STATUS_ERASE_COMPLETE:
    509		break;
    510	default:
    511		/* Should not happen */
    512		fallthrough;
    513	case MLXSW_REG_MBCT_STATUS_ERASE_FAILED:
    514		NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI");
    515		goto fix_fsm_err_out;
    516	case MLXSW_REG_MBCT_STATUS_ERROR_INI_IN_USE:
    517		NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI while being used");
    518		goto fix_fsm_err_out;
    519	}
    520	return 0;
    521
    522fix_fsm_err_out:
    523	mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
    524	return -EINVAL;
    525}
    526
    527static void mlxsw_linecard_bct_process(struct mlxsw_core *mlxsw_core,
    528				       const char *mbct_pl)
    529{
    530	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
    531	enum mlxsw_reg_mbct_fsm_state fsm_state;
    532	enum mlxsw_reg_mbct_status status;
    533	struct mlxsw_linecard *linecard;
    534	u8 slot_index;
    535
    536	mlxsw_reg_mbct_unpack(mbct_pl, &slot_index, &status, &fsm_state);
    537	if (WARN_ON(slot_index > linecards->count))
    538		return;
    539	linecard = mlxsw_linecard_get(linecards, slot_index);
    540	mutex_lock(&linecard->lock);
    541	if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) {
    542		dev_err(linecards->bus_info->dev, "linecard %u: Failed to activate INI",
    543			linecard->slot_index);
    544		goto fix_fsm_out;
    545	}
    546	mutex_unlock(&linecard->lock);
    547	return;
    548
    549fix_fsm_out:
    550	mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
    551	mlxsw_linecard_provision_fail(linecard);
    552	mutex_unlock(&linecard->lock);
    553}
    554
    555static int
    556mlxsw_linecard_ini_activate(struct mlxsw_core *mlxsw_core,
    557			    struct mlxsw_linecard *linecard,
    558			    struct netlink_ext_ack *extack)
    559{
    560	enum mlxsw_reg_mbct_fsm_state fsm_state;
    561	enum mlxsw_reg_mbct_status status;
    562	int err;
    563
    564	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
    565			    MLXSW_REG_MBCT_OP_ACTIVATE, true);
    566	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct), linecard->mbct_pl);
    567	if (err) {
    568		NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI activation");
    569		return err;
    570	}
    571	mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state);
    572	if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) {
    573		NL_SET_ERR_MSG_MOD(extack, "Failed to activate linecard INI");
    574		goto fix_fsm_err_out;
    575	}
    576
    577	return 0;
    578
    579fix_fsm_err_out:
    580	mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
    581	return -EINVAL;
    582}
    583
    584#define MLXSW_LINECARD_INI_WAIT_RETRIES 10
    585#define MLXSW_LINECARD_INI_WAIT_MS 500
    586
    587static int
    588mlxsw_linecard_ini_in_use_wait(struct mlxsw_core *mlxsw_core,
    589			       struct mlxsw_linecard *linecard,
    590			       struct netlink_ext_ack *extack)
    591{
    592	enum mlxsw_reg_mbct_fsm_state fsm_state;
    593	enum mlxsw_reg_mbct_status status;
    594	unsigned int ini_wait_retries = 0;
    595	int err;
    596
    597query_ini_status:
    598	err = mlxsw_linecard_query_ini_status(linecard, &status,
    599					      &fsm_state, extack);
    600	if (err)
    601		return err;
    602
    603	switch (fsm_state) {
    604	case MLXSW_REG_MBCT_FSM_STATE_INI_IN_USE:
    605		if (ini_wait_retries++ > MLXSW_LINECARD_INI_WAIT_RETRIES) {
    606			NL_SET_ERR_MSG_MOD(extack, "Failed to wait for linecard INI to be unused");
    607			return -EINVAL;
    608		}
    609		mdelay(MLXSW_LINECARD_INI_WAIT_MS);
    610		goto query_ini_status;
    611	default:
    612		break;
    613	}
    614	return 0;
    615}
    616
    617static bool mlxsw_linecard_port_selector(void *priv, u16 local_port)
    618{
    619	struct mlxsw_linecard *linecard = priv;
    620	struct mlxsw_core *mlxsw_core;
    621
    622	mlxsw_core = linecard->linecards->mlxsw_core;
    623	return linecard == mlxsw_core_port_linecard_get(mlxsw_core, local_port);
    624}
    625
    626static int mlxsw_linecard_provision(struct devlink_linecard *devlink_linecard,
    627				    void *priv, const char *type,
    628				    const void *type_priv,
    629				    struct netlink_ext_ack *extack)
    630{
    631	const struct mlxsw_linecard_ini_file *ini_file = type_priv;
    632	struct mlxsw_linecard *linecard = priv;
    633	struct mlxsw_core *mlxsw_core;
    634	int err;
    635
    636	mutex_lock(&linecard->lock);
    637
    638	mlxsw_core = linecard->linecards->mlxsw_core;
    639
    640	err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack);
    641	if (err)
    642		goto err_out;
    643
    644	err = mlxsw_linecard_ini_transfer(mlxsw_core, linecard,
    645					  ini_file, extack);
    646	if (err)
    647		goto err_out;
    648
    649	mlxsw_linecard_status_event_to_schedule(linecard,
    650						MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION);
    651	err = mlxsw_linecard_ini_activate(mlxsw_core, linecard, extack);
    652	if (err)
    653		goto err_out;
    654
    655	goto out;
    656
    657err_out:
    658	mlxsw_linecard_provision_fail(linecard);
    659out:
    660	mutex_unlock(&linecard->lock);
    661	return err;
    662}
    663
    664static int mlxsw_linecard_unprovision(struct devlink_linecard *devlink_linecard,
    665				      void *priv,
    666				      struct netlink_ext_ack *extack)
    667{
    668	struct mlxsw_linecard *linecard = priv;
    669	struct mlxsw_core *mlxsw_core;
    670	int err;
    671
    672	mutex_lock(&linecard->lock);
    673
    674	mlxsw_core = linecard->linecards->mlxsw_core;
    675
    676	mlxsw_core_ports_remove_selected(mlxsw_core,
    677					 mlxsw_linecard_port_selector,
    678					 linecard);
    679
    680	err = mlxsw_linecard_ini_in_use_wait(mlxsw_core, linecard, extack);
    681	if (err)
    682		goto err_out;
    683
    684	mlxsw_linecard_status_event_to_schedule(linecard,
    685						MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION);
    686	err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack);
    687	if (err)
    688		goto err_out;
    689
    690	goto out;
    691
    692err_out:
    693	mlxsw_linecard_provision_fail(linecard);
    694out:
    695	mutex_unlock(&linecard->lock);
    696	return err;
    697}
    698
    699static bool mlxsw_linecard_same_provision(struct devlink_linecard *devlink_linecard,
    700					  void *priv, const char *type,
    701					  const void *type_priv)
    702{
    703	const struct mlxsw_linecard_ini_file *ini_file = type_priv;
    704	struct mlxsw_linecard *linecard = priv;
    705	bool ret;
    706
    707	mutex_lock(&linecard->lock);
    708	ret = linecard->hw_revision == be16_to_cpu(ini_file->format.hw_revision) &&
    709	      linecard->ini_version == be16_to_cpu(ini_file->format.ini_version);
    710	mutex_unlock(&linecard->lock);
    711	return ret;
    712}
    713
    714static unsigned int
    715mlxsw_linecard_types_count(struct devlink_linecard *devlink_linecard,
    716			   void *priv)
    717{
    718	struct mlxsw_linecard *linecard = priv;
    719
    720	return linecard->linecards->types_info ?
    721	       linecard->linecards->types_info->count : 0;
    722}
    723
    724static void mlxsw_linecard_types_get(struct devlink_linecard *devlink_linecard,
    725				     void *priv, unsigned int index,
    726				     const char **type, const void **type_priv)
    727{
    728	struct mlxsw_linecard_types_info *types_info;
    729	struct mlxsw_linecard_ini_file *ini_file;
    730	struct mlxsw_linecard *linecard = priv;
    731
    732	types_info = linecard->linecards->types_info;
    733	if (WARN_ON_ONCE(!types_info))
    734		return;
    735	ini_file = types_info->ini_files[index];
    736	*type = ini_file->format.name;
    737	*type_priv = ini_file;
    738}
    739
    740static const struct devlink_linecard_ops mlxsw_linecard_ops = {
    741	.provision = mlxsw_linecard_provision,
    742	.unprovision = mlxsw_linecard_unprovision,
    743	.same_provision = mlxsw_linecard_same_provision,
    744	.types_count = mlxsw_linecard_types_count,
    745	.types_get = mlxsw_linecard_types_get,
    746};
    747
    748struct mlxsw_linecard_status_event {
    749	struct mlxsw_core *mlxsw_core;
    750	char mddq_pl[MLXSW_REG_MDDQ_LEN];
    751	struct work_struct work;
    752};
    753
    754static void mlxsw_linecard_status_event_work(struct work_struct *work)
    755{
    756	struct mlxsw_linecard_status_event *event;
    757	struct mlxsw_linecards *linecards;
    758	struct mlxsw_core *mlxsw_core;
    759
    760	event = container_of(work, struct mlxsw_linecard_status_event, work);
    761	mlxsw_core = event->mlxsw_core;
    762	linecards = mlxsw_core_linecards(mlxsw_core);
    763	mlxsw_linecard_status_process(linecards, NULL, event->mddq_pl);
    764	kfree(event);
    765}
    766
    767static void
    768mlxsw_linecard_status_listener_func(const struct mlxsw_reg_info *reg,
    769				    char *mddq_pl, void *priv)
    770{
    771	struct mlxsw_linecard_status_event *event;
    772	struct mlxsw_core *mlxsw_core = priv;
    773
    774	event = kmalloc(sizeof(*event), GFP_ATOMIC);
    775	if (!event)
    776		return;
    777	event->mlxsw_core = mlxsw_core;
    778	memcpy(event->mddq_pl, mddq_pl, sizeof(event->mddq_pl));
    779	INIT_WORK(&event->work, mlxsw_linecard_status_event_work);
    780	mlxsw_core_schedule_work(&event->work);
    781}
    782
    783struct mlxsw_linecard_bct_event {
    784	struct mlxsw_core *mlxsw_core;
    785	char mbct_pl[MLXSW_REG_MBCT_LEN];
    786	struct work_struct work;
    787};
    788
    789static void mlxsw_linecard_bct_event_work(struct work_struct *work)
    790{
    791	struct mlxsw_linecard_bct_event *event;
    792	struct mlxsw_core *mlxsw_core;
    793
    794	event = container_of(work, struct mlxsw_linecard_bct_event, work);
    795	mlxsw_core = event->mlxsw_core;
    796	mlxsw_linecard_bct_process(mlxsw_core, event->mbct_pl);
    797	kfree(event);
    798}
    799
    800static void
    801mlxsw_linecard_bct_listener_func(const struct mlxsw_reg_info *reg,
    802				 char *mbct_pl, void *priv)
    803{
    804	struct mlxsw_linecard_bct_event *event;
    805	struct mlxsw_core *mlxsw_core = priv;
    806
    807	event = kmalloc(sizeof(*event), GFP_ATOMIC);
    808	if (!event)
    809		return;
    810	event->mlxsw_core = mlxsw_core;
    811	memcpy(event->mbct_pl, mbct_pl, sizeof(event->mbct_pl));
    812	INIT_WORK(&event->work, mlxsw_linecard_bct_event_work);
    813	mlxsw_core_schedule_work(&event->work);
    814}
    815
    816static const struct mlxsw_listener mlxsw_linecard_listener[] = {
    817	MLXSW_CORE_EVENTL(mlxsw_linecard_status_listener_func, DSDSC),
    818	MLXSW_CORE_EVENTL(mlxsw_linecard_bct_listener_func, BCTOE),
    819};
    820
    821static int mlxsw_linecard_event_delivery_set(struct mlxsw_core *mlxsw_core,
    822					     struct mlxsw_linecard *linecard,
    823					     bool enable)
    824{
    825	char mddq_pl[MLXSW_REG_MDDQ_LEN];
    826
    827	mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, enable);
    828	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddq), mddq_pl);
    829}
    830
    831static int mlxsw_linecard_init(struct mlxsw_core *mlxsw_core,
    832			       struct mlxsw_linecards *linecards,
    833			       u8 slot_index)
    834{
    835	struct devlink_linecard *devlink_linecard;
    836	struct mlxsw_linecard *linecard;
    837	int err;
    838
    839	linecard = mlxsw_linecard_get(linecards, slot_index);
    840	linecard->slot_index = slot_index;
    841	linecard->linecards = linecards;
    842	mutex_init(&linecard->lock);
    843
    844	devlink_linecard = devlink_linecard_create(priv_to_devlink(mlxsw_core),
    845						   slot_index, &mlxsw_linecard_ops,
    846						   linecard);
    847	if (IS_ERR(devlink_linecard)) {
    848		err = PTR_ERR(devlink_linecard);
    849		goto err_devlink_linecard_create;
    850	}
    851	linecard->devlink_linecard = devlink_linecard;
    852	INIT_DELAYED_WORK(&linecard->status_event_to_dw,
    853			  &mlxsw_linecard_status_event_to_work);
    854
    855	err = mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, true);
    856	if (err)
    857		goto err_event_delivery_set;
    858
    859	err = mlxsw_linecard_status_get_and_process(mlxsw_core, linecards,
    860						    linecard);
    861	if (err)
    862		goto err_status_get_and_process;
    863
    864	return 0;
    865
    866err_status_get_and_process:
    867	mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false);
    868err_event_delivery_set:
    869	devlink_linecard_destroy(linecard->devlink_linecard);
    870err_devlink_linecard_create:
    871	mutex_destroy(&linecard->lock);
    872	return err;
    873}
    874
    875static void mlxsw_linecard_fini(struct mlxsw_core *mlxsw_core,
    876				struct mlxsw_linecards *linecards,
    877				u8 slot_index)
    878{
    879	struct mlxsw_linecard *linecard;
    880
    881	linecard = mlxsw_linecard_get(linecards, slot_index);
    882	mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false);
    883	cancel_delayed_work_sync(&linecard->status_event_to_dw);
    884	/* Make sure all scheduled events are processed */
    885	mlxsw_core_flush_owq();
    886	if (linecard->active)
    887		mlxsw_linecard_active_clear(linecard);
    888	devlink_linecard_destroy(linecard->devlink_linecard);
    889	mutex_destroy(&linecard->lock);
    890}
    891
    892/*       LINECARDS INI BUNDLE FILE
    893 *  +----------------------------------+
    894 *  |        MAGIC ("NVLCINI+")        |
    895 *  +----------------------------------+     +--------------------+
    896 *  |  INI 0                           +---> | __le16 size        |
    897 *  +----------------------------------+     | __be16 hw_revision |
    898 *  |  INI 1                           |     | __be16 ini_version |
    899 *  +----------------------------------+     | u8 __dontcare[3]   |
    900 *  |  ...                             |     | u8 type            |
    901 *  +----------------------------------+     | u8 name[20]        |
    902 *  |  INI N                           |     | ...                |
    903 *  +----------------------------------+     +--------------------+
    904 */
    905
    906#define MLXSW_LINECARDS_INI_BUNDLE_MAGIC "NVLCINI+"
    907
    908static int
    909mlxsw_linecard_types_file_validate(struct mlxsw_linecards *linecards,
    910				   struct mlxsw_linecard_types_info *types_info)
    911{
    912	size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC);
    913	struct mlxsw_linecard_ini_file *ini_file;
    914	size_t size = types_info->data_size;
    915	const u8 *data = types_info->data;
    916	unsigned int count = 0;
    917	u16 ini_file_size;
    918
    919	if (size < magic_size) {
    920		dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file size, smaller than magic size\n");
    921		return -EINVAL;
    922	}
    923	if (memcmp(data, MLXSW_LINECARDS_INI_BUNDLE_MAGIC, magic_size)) {
    924		dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file magic pattern\n");
    925		return -EINVAL;
    926	}
    927
    928	data += magic_size;
    929	size -= magic_size;
    930
    931	while (size > 0) {
    932		if (size < sizeof(*ini_file)) {
    933			dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI which is smaller than bare minimum\n");
    934			return -EINVAL;
    935		}
    936		ini_file = (struct mlxsw_linecard_ini_file *) data;
    937		ini_file_size = le16_to_cpu(ini_file->size);
    938		if (ini_file_size + sizeof(__le16) > size) {
    939			dev_warn(linecards->bus_info->dev, "Linecards INIs file appears to be truncated\n");
    940			return -EINVAL;
    941		}
    942		if (ini_file_size % 4) {
    943			dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI with invalid size\n");
    944			return -EINVAL;
    945		}
    946		data += ini_file_size + sizeof(__le16);
    947		size -= ini_file_size + sizeof(__le16);
    948		count++;
    949	}
    950	if (!count) {
    951		dev_warn(linecards->bus_info->dev, "Linecards INIs file does not contain any INI\n");
    952		return -EINVAL;
    953	}
    954	types_info->count = count;
    955	return 0;
    956}
    957
    958static void
    959mlxsw_linecard_types_file_parse(struct mlxsw_linecard_types_info *types_info)
    960{
    961	size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC);
    962	size_t size = types_info->data_size - magic_size;
    963	const u8 *data = types_info->data + magic_size;
    964	struct mlxsw_linecard_ini_file *ini_file;
    965	unsigned int count = 0;
    966	u16 ini_file_size;
    967	int i;
    968
    969	while (size) {
    970		ini_file = (struct mlxsw_linecard_ini_file *) data;
    971		ini_file_size = le16_to_cpu(ini_file->size);
    972		for (i = 0; i < ini_file_size / 4; i++) {
    973			u32 *val = &((u32 *) ini_file->data)[i];
    974
    975			*val = swab32(*val);
    976		}
    977		types_info->ini_files[count] = ini_file;
    978		data += ini_file_size + sizeof(__le16);
    979		size -= ini_file_size + sizeof(__le16);
    980		count++;
    981	}
    982}
    983
    984#define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT \
    985	"mellanox/lc_ini_bundle_%u_%u.bin"
    986#define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN \
    987	(sizeof(MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT) + 4)
    988
    989static int mlxsw_linecard_types_init(struct mlxsw_core *mlxsw_core,
    990				     struct mlxsw_linecards *linecards)
    991{
    992	const struct mlxsw_fw_rev *rev = &linecards->bus_info->fw_rev;
    993	char filename[MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN];
    994	struct mlxsw_linecard_types_info *types_info;
    995	const struct firmware *firmware;
    996	int err;
    997
    998	err = snprintf(filename, sizeof(filename),
    999		       MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT,
   1000		       rev->minor, rev->subminor);
   1001	WARN_ON(err >= sizeof(filename));
   1002
   1003	err = request_firmware_direct(&firmware, filename,
   1004				      linecards->bus_info->dev);
   1005	if (err) {
   1006		dev_warn(linecards->bus_info->dev, "Could not request linecards INI file \"%s\", provisioning will not be possible\n",
   1007			 filename);
   1008		return 0;
   1009	}
   1010
   1011	types_info = kzalloc(sizeof(*types_info), GFP_KERNEL);
   1012	if (!types_info) {
   1013		release_firmware(firmware);
   1014		return -ENOMEM;
   1015	}
   1016	linecards->types_info = types_info;
   1017
   1018	types_info->data_size = firmware->size;
   1019	types_info->data = vmalloc(types_info->data_size);
   1020	if (!types_info->data) {
   1021		err = -ENOMEM;
   1022		release_firmware(firmware);
   1023		goto err_data_alloc;
   1024	}
   1025	memcpy(types_info->data, firmware->data, types_info->data_size);
   1026	release_firmware(firmware);
   1027
   1028	err = mlxsw_linecard_types_file_validate(linecards, types_info);
   1029	if (err) {
   1030		err = 0;
   1031		goto err_type_file_file_validate;
   1032	}
   1033
   1034	types_info->ini_files = kmalloc_array(types_info->count,
   1035					      sizeof(struct mlxsw_linecard_ini_file *),
   1036					      GFP_KERNEL);
   1037	if (!types_info->ini_files) {
   1038		err = -ENOMEM;
   1039		goto err_ini_files_alloc;
   1040	}
   1041
   1042	mlxsw_linecard_types_file_parse(types_info);
   1043
   1044	return 0;
   1045
   1046err_ini_files_alloc:
   1047err_type_file_file_validate:
   1048	vfree(types_info->data);
   1049err_data_alloc:
   1050	kfree(types_info);
   1051	return err;
   1052}
   1053
   1054static void mlxsw_linecard_types_fini(struct mlxsw_linecards *linecards)
   1055{
   1056	struct mlxsw_linecard_types_info *types_info = linecards->types_info;
   1057
   1058	if (!types_info)
   1059		return;
   1060	kfree(types_info->ini_files);
   1061	vfree(types_info->data);
   1062	kfree(types_info);
   1063}
   1064
   1065int mlxsw_linecards_init(struct mlxsw_core *mlxsw_core,
   1066			 const struct mlxsw_bus_info *bus_info)
   1067{
   1068	char mgpir_pl[MLXSW_REG_MGPIR_LEN];
   1069	struct mlxsw_linecards *linecards;
   1070	u8 slot_count;
   1071	int err;
   1072	int i;
   1073
   1074	mlxsw_reg_mgpir_pack(mgpir_pl, 0);
   1075	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl);
   1076	if (err)
   1077		return err;
   1078
   1079	mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL,
   1080			       NULL, &slot_count);
   1081	if (!slot_count)
   1082		return 0;
   1083
   1084	linecards = vzalloc(struct_size(linecards, linecards, slot_count));
   1085	if (!linecards)
   1086		return -ENOMEM;
   1087	linecards->count = slot_count;
   1088	linecards->mlxsw_core = mlxsw_core;
   1089	linecards->bus_info = bus_info;
   1090	INIT_LIST_HEAD(&linecards->event_ops_list);
   1091	mutex_init(&linecards->event_ops_list_lock);
   1092
   1093	err = mlxsw_linecard_types_init(mlxsw_core, linecards);
   1094	if (err)
   1095		goto err_types_init;
   1096
   1097	err = mlxsw_core_traps_register(mlxsw_core, mlxsw_linecard_listener,
   1098					ARRAY_SIZE(mlxsw_linecard_listener),
   1099					mlxsw_core);
   1100	if (err)
   1101		goto err_traps_register;
   1102
   1103	mlxsw_core_linecards_set(mlxsw_core, linecards);
   1104
   1105	for (i = 0; i < linecards->count; i++) {
   1106		err = mlxsw_linecard_init(mlxsw_core, linecards, i + 1);
   1107		if (err)
   1108			goto err_linecard_init;
   1109	}
   1110
   1111	return 0;
   1112
   1113err_linecard_init:
   1114	for (i--; i >= 0; i--)
   1115		mlxsw_linecard_fini(mlxsw_core, linecards, i + 1);
   1116	mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener,
   1117				    ARRAY_SIZE(mlxsw_linecard_listener),
   1118				    mlxsw_core);
   1119err_traps_register:
   1120	mlxsw_linecard_types_fini(linecards);
   1121err_types_init:
   1122	vfree(linecards);
   1123	return err;
   1124}
   1125
   1126void mlxsw_linecards_fini(struct mlxsw_core *mlxsw_core)
   1127{
   1128	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
   1129	int i;
   1130
   1131	if (!linecards)
   1132		return;
   1133	for (i = 0; i < linecards->count; i++)
   1134		mlxsw_linecard_fini(mlxsw_core, linecards, i + 1);
   1135	mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener,
   1136				    ARRAY_SIZE(mlxsw_linecard_listener),
   1137				    mlxsw_core);
   1138	mlxsw_linecard_types_fini(linecards);
   1139	mutex_destroy(&linecards->event_ops_list_lock);
   1140	WARN_ON(!list_empty(&linecards->event_ops_list));
   1141	vfree(linecards);
   1142}