162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
262306a36Sopenharmony_ci/* Copyright (c) 2015-2018 Mellanox Technologies. All rights reserved */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/kernel.h>
562306a36Sopenharmony_ci#include <linux/module.h>
662306a36Sopenharmony_ci#include <linux/export.h>
762306a36Sopenharmony_ci#include <linux/err.h>
862306a36Sopenharmony_ci#include <linux/device.h>
962306a36Sopenharmony_ci#include <linux/pci.h>
1062306a36Sopenharmony_ci#include <linux/interrupt.h>
1162306a36Sopenharmony_ci#include <linux/wait.h>
1262306a36Sopenharmony_ci#include <linux/types.h>
1362306a36Sopenharmony_ci#include <linux/skbuff.h>
1462306a36Sopenharmony_ci#include <linux/if_vlan.h>
1562306a36Sopenharmony_ci#include <linux/log2.h>
1662306a36Sopenharmony_ci#include <linux/string.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "pci_hw.h"
1962306a36Sopenharmony_ci#include "pci.h"
2062306a36Sopenharmony_ci#include "core.h"
2162306a36Sopenharmony_ci#include "cmd.h"
2262306a36Sopenharmony_ci#include "port.h"
2362306a36Sopenharmony_ci#include "resources.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define mlxsw_pci_write32(mlxsw_pci, reg, val) \
2662306a36Sopenharmony_ci	iowrite32be(val, (mlxsw_pci)->hw_addr + (MLXSW_PCI_ ## reg))
2762306a36Sopenharmony_ci#define mlxsw_pci_read32(mlxsw_pci, reg) \
2862306a36Sopenharmony_ci	ioread32be((mlxsw_pci)->hw_addr + (MLXSW_PCI_ ## reg))
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cienum mlxsw_pci_queue_type {
3162306a36Sopenharmony_ci	MLXSW_PCI_QUEUE_TYPE_SDQ,
3262306a36Sopenharmony_ci	MLXSW_PCI_QUEUE_TYPE_RDQ,
3362306a36Sopenharmony_ci	MLXSW_PCI_QUEUE_TYPE_CQ,
3462306a36Sopenharmony_ci	MLXSW_PCI_QUEUE_TYPE_EQ,
3562306a36Sopenharmony_ci};
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define MLXSW_PCI_QUEUE_TYPE_COUNT	4
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic const u16 mlxsw_pci_doorbell_type_offset[] = {
4062306a36Sopenharmony_ci	MLXSW_PCI_DOORBELL_SDQ_OFFSET,	/* for type MLXSW_PCI_QUEUE_TYPE_SDQ */
4162306a36Sopenharmony_ci	MLXSW_PCI_DOORBELL_RDQ_OFFSET,	/* for type MLXSW_PCI_QUEUE_TYPE_RDQ */
4262306a36Sopenharmony_ci	MLXSW_PCI_DOORBELL_CQ_OFFSET,	/* for type MLXSW_PCI_QUEUE_TYPE_CQ */
4362306a36Sopenharmony_ci	MLXSW_PCI_DOORBELL_EQ_OFFSET,	/* for type MLXSW_PCI_QUEUE_TYPE_EQ */
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic const u16 mlxsw_pci_doorbell_arm_type_offset[] = {
4762306a36Sopenharmony_ci	0, /* unused */
4862306a36Sopenharmony_ci	0, /* unused */
4962306a36Sopenharmony_ci	MLXSW_PCI_DOORBELL_ARM_CQ_OFFSET, /* for type MLXSW_PCI_QUEUE_TYPE_CQ */
5062306a36Sopenharmony_ci	MLXSW_PCI_DOORBELL_ARM_EQ_OFFSET, /* for type MLXSW_PCI_QUEUE_TYPE_EQ */
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistruct mlxsw_pci_mem_item {
5462306a36Sopenharmony_ci	char *buf;
5562306a36Sopenharmony_ci	dma_addr_t mapaddr;
5662306a36Sopenharmony_ci	size_t size;
5762306a36Sopenharmony_ci};
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistruct mlxsw_pci_queue_elem_info {
6062306a36Sopenharmony_ci	char *elem; /* pointer to actual dma mapped element mem chunk */
6162306a36Sopenharmony_ci	union {
6262306a36Sopenharmony_ci		struct {
6362306a36Sopenharmony_ci			struct sk_buff *skb;
6462306a36Sopenharmony_ci		} sdq;
6562306a36Sopenharmony_ci		struct {
6662306a36Sopenharmony_ci			struct sk_buff *skb;
6762306a36Sopenharmony_ci		} rdq;
6862306a36Sopenharmony_ci	} u;
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistruct mlxsw_pci_queue {
7262306a36Sopenharmony_ci	spinlock_t lock; /* for queue accesses */
7362306a36Sopenharmony_ci	struct mlxsw_pci_mem_item mem_item;
7462306a36Sopenharmony_ci	struct mlxsw_pci_queue_elem_info *elem_info;
7562306a36Sopenharmony_ci	u16 producer_counter;
7662306a36Sopenharmony_ci	u16 consumer_counter;
7762306a36Sopenharmony_ci	u16 count; /* number of elements in queue */
7862306a36Sopenharmony_ci	u8 num; /* queue number */
7962306a36Sopenharmony_ci	u8 elem_size; /* size of one element */
8062306a36Sopenharmony_ci	enum mlxsw_pci_queue_type type;
8162306a36Sopenharmony_ci	struct tasklet_struct tasklet; /* queue processing tasklet */
8262306a36Sopenharmony_ci	struct mlxsw_pci *pci;
8362306a36Sopenharmony_ci	union {
8462306a36Sopenharmony_ci		struct {
8562306a36Sopenharmony_ci			u32 comp_sdq_count;
8662306a36Sopenharmony_ci			u32 comp_rdq_count;
8762306a36Sopenharmony_ci			enum mlxsw_pci_cqe_v v;
8862306a36Sopenharmony_ci		} cq;
8962306a36Sopenharmony_ci		struct {
9062306a36Sopenharmony_ci			u32 ev_cmd_count;
9162306a36Sopenharmony_ci			u32 ev_comp_count;
9262306a36Sopenharmony_ci			u32 ev_other_count;
9362306a36Sopenharmony_ci		} eq;
9462306a36Sopenharmony_ci	} u;
9562306a36Sopenharmony_ci};
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistruct mlxsw_pci_queue_type_group {
9862306a36Sopenharmony_ci	struct mlxsw_pci_queue *q;
9962306a36Sopenharmony_ci	u8 count; /* number of queues in group */
10062306a36Sopenharmony_ci};
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistruct mlxsw_pci {
10362306a36Sopenharmony_ci	struct pci_dev *pdev;
10462306a36Sopenharmony_ci	u8 __iomem *hw_addr;
10562306a36Sopenharmony_ci	u64 free_running_clock_offset;
10662306a36Sopenharmony_ci	u64 utc_sec_offset;
10762306a36Sopenharmony_ci	u64 utc_nsec_offset;
10862306a36Sopenharmony_ci	struct mlxsw_pci_queue_type_group queues[MLXSW_PCI_QUEUE_TYPE_COUNT];
10962306a36Sopenharmony_ci	u32 doorbell_offset;
11062306a36Sopenharmony_ci	struct mlxsw_core *core;
11162306a36Sopenharmony_ci	struct {
11262306a36Sopenharmony_ci		struct mlxsw_pci_mem_item *items;
11362306a36Sopenharmony_ci		unsigned int count;
11462306a36Sopenharmony_ci	} fw_area;
11562306a36Sopenharmony_ci	struct {
11662306a36Sopenharmony_ci		struct mlxsw_pci_mem_item out_mbox;
11762306a36Sopenharmony_ci		struct mlxsw_pci_mem_item in_mbox;
11862306a36Sopenharmony_ci		struct mutex lock; /* Lock access to command registers */
11962306a36Sopenharmony_ci		bool nopoll;
12062306a36Sopenharmony_ci		wait_queue_head_t wait;
12162306a36Sopenharmony_ci		bool wait_done;
12262306a36Sopenharmony_ci		struct {
12362306a36Sopenharmony_ci			u8 status;
12462306a36Sopenharmony_ci			u64 out_param;
12562306a36Sopenharmony_ci		} comp;
12662306a36Sopenharmony_ci	} cmd;
12762306a36Sopenharmony_ci	struct mlxsw_bus_info bus_info;
12862306a36Sopenharmony_ci	const struct pci_device_id *id;
12962306a36Sopenharmony_ci	enum mlxsw_pci_cqe_v max_cqe_ver; /* Maximal supported CQE version */
13062306a36Sopenharmony_ci	u8 num_sdq_cqs; /* Number of CQs used for SDQs */
13162306a36Sopenharmony_ci};
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic void mlxsw_pci_queue_tasklet_schedule(struct mlxsw_pci_queue *q)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	tasklet_schedule(&q->tasklet);
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic char *__mlxsw_pci_queue_elem_get(struct mlxsw_pci_queue *q,
13962306a36Sopenharmony_ci					size_t elem_size, int elem_index)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	return q->mem_item.buf + (elem_size * elem_index);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic struct mlxsw_pci_queue_elem_info *
14562306a36Sopenharmony_cimlxsw_pci_queue_elem_info_get(struct mlxsw_pci_queue *q, int elem_index)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	return &q->elem_info[elem_index];
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic struct mlxsw_pci_queue_elem_info *
15162306a36Sopenharmony_cimlxsw_pci_queue_elem_info_producer_get(struct mlxsw_pci_queue *q)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	int index = q->producer_counter & (q->count - 1);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	if ((u16) (q->producer_counter - q->consumer_counter) == q->count)
15662306a36Sopenharmony_ci		return NULL;
15762306a36Sopenharmony_ci	return mlxsw_pci_queue_elem_info_get(q, index);
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic struct mlxsw_pci_queue_elem_info *
16162306a36Sopenharmony_cimlxsw_pci_queue_elem_info_consumer_get(struct mlxsw_pci_queue *q)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	int index = q->consumer_counter & (q->count - 1);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	return mlxsw_pci_queue_elem_info_get(q, index);
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic char *mlxsw_pci_queue_elem_get(struct mlxsw_pci_queue *q, int elem_index)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	return mlxsw_pci_queue_elem_info_get(q, elem_index)->elem;
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic bool mlxsw_pci_elem_hw_owned(struct mlxsw_pci_queue *q, bool owner_bit)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	return owner_bit != !!(q->consumer_counter & q->count);
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic struct mlxsw_pci_queue_type_group *
17962306a36Sopenharmony_cimlxsw_pci_queue_type_group_get(struct mlxsw_pci *mlxsw_pci,
18062306a36Sopenharmony_ci			       enum mlxsw_pci_queue_type q_type)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	return &mlxsw_pci->queues[q_type];
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic u8 __mlxsw_pci_queue_count(struct mlxsw_pci *mlxsw_pci,
18662306a36Sopenharmony_ci				  enum mlxsw_pci_queue_type q_type)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	struct mlxsw_pci_queue_type_group *queue_group;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	queue_group = mlxsw_pci_queue_type_group_get(mlxsw_pci, q_type);
19162306a36Sopenharmony_ci	return queue_group->count;
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic u8 mlxsw_pci_sdq_count(struct mlxsw_pci *mlxsw_pci)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	return __mlxsw_pci_queue_count(mlxsw_pci, MLXSW_PCI_QUEUE_TYPE_SDQ);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic u8 mlxsw_pci_cq_count(struct mlxsw_pci *mlxsw_pci)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	return __mlxsw_pci_queue_count(mlxsw_pci, MLXSW_PCI_QUEUE_TYPE_CQ);
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic struct mlxsw_pci_queue *
20562306a36Sopenharmony_ci__mlxsw_pci_queue_get(struct mlxsw_pci *mlxsw_pci,
20662306a36Sopenharmony_ci		      enum mlxsw_pci_queue_type q_type, u8 q_num)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	return &mlxsw_pci->queues[q_type].q[q_num];
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic struct mlxsw_pci_queue *mlxsw_pci_sdq_get(struct mlxsw_pci *mlxsw_pci,
21262306a36Sopenharmony_ci						 u8 q_num)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	return __mlxsw_pci_queue_get(mlxsw_pci,
21562306a36Sopenharmony_ci				     MLXSW_PCI_QUEUE_TYPE_SDQ, q_num);
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic struct mlxsw_pci_queue *mlxsw_pci_rdq_get(struct mlxsw_pci *mlxsw_pci,
21962306a36Sopenharmony_ci						 u8 q_num)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	return __mlxsw_pci_queue_get(mlxsw_pci,
22262306a36Sopenharmony_ci				     MLXSW_PCI_QUEUE_TYPE_RDQ, q_num);
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic struct mlxsw_pci_queue *mlxsw_pci_cq_get(struct mlxsw_pci *mlxsw_pci,
22662306a36Sopenharmony_ci						u8 q_num)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	return __mlxsw_pci_queue_get(mlxsw_pci, MLXSW_PCI_QUEUE_TYPE_CQ, q_num);
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic struct mlxsw_pci_queue *mlxsw_pci_eq_get(struct mlxsw_pci *mlxsw_pci,
23262306a36Sopenharmony_ci						u8 q_num)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	return __mlxsw_pci_queue_get(mlxsw_pci, MLXSW_PCI_QUEUE_TYPE_EQ, q_num);
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic void __mlxsw_pci_queue_doorbell_set(struct mlxsw_pci *mlxsw_pci,
23862306a36Sopenharmony_ci					   struct mlxsw_pci_queue *q,
23962306a36Sopenharmony_ci					   u16 val)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	mlxsw_pci_write32(mlxsw_pci,
24262306a36Sopenharmony_ci			  DOORBELL(mlxsw_pci->doorbell_offset,
24362306a36Sopenharmony_ci				   mlxsw_pci_doorbell_type_offset[q->type],
24462306a36Sopenharmony_ci				   q->num), val);
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic void __mlxsw_pci_queue_doorbell_arm_set(struct mlxsw_pci *mlxsw_pci,
24862306a36Sopenharmony_ci					       struct mlxsw_pci_queue *q,
24962306a36Sopenharmony_ci					       u16 val)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	mlxsw_pci_write32(mlxsw_pci,
25262306a36Sopenharmony_ci			  DOORBELL(mlxsw_pci->doorbell_offset,
25362306a36Sopenharmony_ci				   mlxsw_pci_doorbell_arm_type_offset[q->type],
25462306a36Sopenharmony_ci				   q->num), val);
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic void mlxsw_pci_queue_doorbell_producer_ring(struct mlxsw_pci *mlxsw_pci,
25862306a36Sopenharmony_ci						   struct mlxsw_pci_queue *q)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	wmb(); /* ensure all writes are done before we ring a bell */
26162306a36Sopenharmony_ci	__mlxsw_pci_queue_doorbell_set(mlxsw_pci, q, q->producer_counter);
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic void mlxsw_pci_queue_doorbell_consumer_ring(struct mlxsw_pci *mlxsw_pci,
26562306a36Sopenharmony_ci						   struct mlxsw_pci_queue *q)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	wmb(); /* ensure all writes are done before we ring a bell */
26862306a36Sopenharmony_ci	__mlxsw_pci_queue_doorbell_set(mlxsw_pci, q,
26962306a36Sopenharmony_ci				       q->consumer_counter + q->count);
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic void
27362306a36Sopenharmony_cimlxsw_pci_queue_doorbell_arm_consumer_ring(struct mlxsw_pci *mlxsw_pci,
27462306a36Sopenharmony_ci					   struct mlxsw_pci_queue *q)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	wmb(); /* ensure all writes are done before we ring a bell */
27762306a36Sopenharmony_ci	__mlxsw_pci_queue_doorbell_arm_set(mlxsw_pci, q, q->consumer_counter);
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic dma_addr_t __mlxsw_pci_queue_page_get(struct mlxsw_pci_queue *q,
28162306a36Sopenharmony_ci					     int page_index)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	return q->mem_item.mapaddr + MLXSW_PCI_PAGE_SIZE * page_index;
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic int mlxsw_pci_sdq_init(struct mlxsw_pci *mlxsw_pci, char *mbox,
28762306a36Sopenharmony_ci			      struct mlxsw_pci_queue *q)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	int tclass;
29062306a36Sopenharmony_ci	int lp;
29162306a36Sopenharmony_ci	int i;
29262306a36Sopenharmony_ci	int err;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	q->producer_counter = 0;
29562306a36Sopenharmony_ci	q->consumer_counter = 0;
29662306a36Sopenharmony_ci	tclass = q->num == MLXSW_PCI_SDQ_EMAD_INDEX ? MLXSW_PCI_SDQ_EMAD_TC :
29762306a36Sopenharmony_ci						      MLXSW_PCI_SDQ_CTL_TC;
29862306a36Sopenharmony_ci	lp = q->num == MLXSW_PCI_SDQ_EMAD_INDEX ? MLXSW_CMD_MBOX_SW2HW_DQ_SDQ_LP_IGNORE_WQE :
29962306a36Sopenharmony_ci						  MLXSW_CMD_MBOX_SW2HW_DQ_SDQ_LP_WQE;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	/* Set CQ of same number of this SDQ. */
30262306a36Sopenharmony_ci	mlxsw_cmd_mbox_sw2hw_dq_cq_set(mbox, q->num);
30362306a36Sopenharmony_ci	mlxsw_cmd_mbox_sw2hw_dq_sdq_lp_set(mbox, lp);
30462306a36Sopenharmony_ci	mlxsw_cmd_mbox_sw2hw_dq_sdq_tclass_set(mbox, tclass);
30562306a36Sopenharmony_ci	mlxsw_cmd_mbox_sw2hw_dq_log2_dq_sz_set(mbox, 3); /* 8 pages */
30662306a36Sopenharmony_ci	for (i = 0; i < MLXSW_PCI_AQ_PAGES; i++) {
30762306a36Sopenharmony_ci		dma_addr_t mapaddr = __mlxsw_pci_queue_page_get(q, i);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci		mlxsw_cmd_mbox_sw2hw_dq_pa_set(mbox, i, mapaddr);
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	err = mlxsw_cmd_sw2hw_sdq(mlxsw_pci->core, mbox, q->num);
31362306a36Sopenharmony_ci	if (err)
31462306a36Sopenharmony_ci		return err;
31562306a36Sopenharmony_ci	mlxsw_pci_queue_doorbell_producer_ring(mlxsw_pci, q);
31662306a36Sopenharmony_ci	return 0;
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic void mlxsw_pci_sdq_fini(struct mlxsw_pci *mlxsw_pci,
32062306a36Sopenharmony_ci			       struct mlxsw_pci_queue *q)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	mlxsw_cmd_hw2sw_sdq(mlxsw_pci->core, q->num);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic int mlxsw_pci_wqe_frag_map(struct mlxsw_pci *mlxsw_pci, char *wqe,
32662306a36Sopenharmony_ci				  int index, char *frag_data, size_t frag_len,
32762306a36Sopenharmony_ci				  int direction)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	struct pci_dev *pdev = mlxsw_pci->pdev;
33062306a36Sopenharmony_ci	dma_addr_t mapaddr;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	mapaddr = dma_map_single(&pdev->dev, frag_data, frag_len, direction);
33362306a36Sopenharmony_ci	if (unlikely(dma_mapping_error(&pdev->dev, mapaddr))) {
33462306a36Sopenharmony_ci		dev_err_ratelimited(&pdev->dev, "failed to dma map tx frag\n");
33562306a36Sopenharmony_ci		return -EIO;
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci	mlxsw_pci_wqe_address_set(wqe, index, mapaddr);
33862306a36Sopenharmony_ci	mlxsw_pci_wqe_byte_count_set(wqe, index, frag_len);
33962306a36Sopenharmony_ci	return 0;
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic void mlxsw_pci_wqe_frag_unmap(struct mlxsw_pci *mlxsw_pci, char *wqe,
34362306a36Sopenharmony_ci				     int index, int direction)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	struct pci_dev *pdev = mlxsw_pci->pdev;
34662306a36Sopenharmony_ci	size_t frag_len = mlxsw_pci_wqe_byte_count_get(wqe, index);
34762306a36Sopenharmony_ci	dma_addr_t mapaddr = mlxsw_pci_wqe_address_get(wqe, index);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	if (!frag_len)
35062306a36Sopenharmony_ci		return;
35162306a36Sopenharmony_ci	dma_unmap_single(&pdev->dev, mapaddr, frag_len, direction);
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic int mlxsw_pci_rdq_skb_alloc(struct mlxsw_pci *mlxsw_pci,
35562306a36Sopenharmony_ci				   struct mlxsw_pci_queue_elem_info *elem_info)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	size_t buf_len = MLXSW_PORT_MAX_MTU;
35862306a36Sopenharmony_ci	char *wqe = elem_info->elem;
35962306a36Sopenharmony_ci	struct sk_buff *skb;
36062306a36Sopenharmony_ci	int err;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	skb = netdev_alloc_skb_ip_align(NULL, buf_len);
36362306a36Sopenharmony_ci	if (!skb)
36462306a36Sopenharmony_ci		return -ENOMEM;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	err = mlxsw_pci_wqe_frag_map(mlxsw_pci, wqe, 0, skb->data,
36762306a36Sopenharmony_ci				     buf_len, DMA_FROM_DEVICE);
36862306a36Sopenharmony_ci	if (err)
36962306a36Sopenharmony_ci		goto err_frag_map;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	elem_info->u.rdq.skb = skb;
37262306a36Sopenharmony_ci	return 0;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cierr_frag_map:
37562306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
37662306a36Sopenharmony_ci	return err;
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic void mlxsw_pci_rdq_skb_free(struct mlxsw_pci *mlxsw_pci,
38062306a36Sopenharmony_ci				   struct mlxsw_pci_queue_elem_info *elem_info)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	struct sk_buff *skb;
38362306a36Sopenharmony_ci	char *wqe;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	skb = elem_info->u.rdq.skb;
38662306a36Sopenharmony_ci	wqe = elem_info->elem;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	mlxsw_pci_wqe_frag_unmap(mlxsw_pci, wqe, 0, DMA_FROM_DEVICE);
38962306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cistatic int mlxsw_pci_rdq_init(struct mlxsw_pci *mlxsw_pci, char *mbox,
39362306a36Sopenharmony_ci			      struct mlxsw_pci_queue *q)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	struct mlxsw_pci_queue_elem_info *elem_info;
39662306a36Sopenharmony_ci	u8 sdq_count = mlxsw_pci_sdq_count(mlxsw_pci);
39762306a36Sopenharmony_ci	int i;
39862306a36Sopenharmony_ci	int err;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	q->producer_counter = 0;
40162306a36Sopenharmony_ci	q->consumer_counter = 0;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	/* Set CQ of same number of this RDQ with base
40462306a36Sopenharmony_ci	 * above SDQ count as the lower ones are assigned to SDQs.
40562306a36Sopenharmony_ci	 */
40662306a36Sopenharmony_ci	mlxsw_cmd_mbox_sw2hw_dq_cq_set(mbox, sdq_count + q->num);
40762306a36Sopenharmony_ci	mlxsw_cmd_mbox_sw2hw_dq_log2_dq_sz_set(mbox, 3); /* 8 pages */
40862306a36Sopenharmony_ci	for (i = 0; i < MLXSW_PCI_AQ_PAGES; i++) {
40962306a36Sopenharmony_ci		dma_addr_t mapaddr = __mlxsw_pci_queue_page_get(q, i);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci		mlxsw_cmd_mbox_sw2hw_dq_pa_set(mbox, i, mapaddr);
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	err = mlxsw_cmd_sw2hw_rdq(mlxsw_pci->core, mbox, q->num);
41562306a36Sopenharmony_ci	if (err)
41662306a36Sopenharmony_ci		return err;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	mlxsw_pci_queue_doorbell_producer_ring(mlxsw_pci, q);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	for (i = 0; i < q->count; i++) {
42162306a36Sopenharmony_ci		elem_info = mlxsw_pci_queue_elem_info_producer_get(q);
42262306a36Sopenharmony_ci		BUG_ON(!elem_info);
42362306a36Sopenharmony_ci		err = mlxsw_pci_rdq_skb_alloc(mlxsw_pci, elem_info);
42462306a36Sopenharmony_ci		if (err)
42562306a36Sopenharmony_ci			goto rollback;
42662306a36Sopenharmony_ci		/* Everything is set up, ring doorbell to pass elem to HW */
42762306a36Sopenharmony_ci		q->producer_counter++;
42862306a36Sopenharmony_ci		mlxsw_pci_queue_doorbell_producer_ring(mlxsw_pci, q);
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	return 0;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cirollback:
43462306a36Sopenharmony_ci	for (i--; i >= 0; i--) {
43562306a36Sopenharmony_ci		elem_info = mlxsw_pci_queue_elem_info_get(q, i);
43662306a36Sopenharmony_ci		mlxsw_pci_rdq_skb_free(mlxsw_pci, elem_info);
43762306a36Sopenharmony_ci	}
43862306a36Sopenharmony_ci	mlxsw_cmd_hw2sw_rdq(mlxsw_pci->core, q->num);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	return err;
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistatic void mlxsw_pci_rdq_fini(struct mlxsw_pci *mlxsw_pci,
44462306a36Sopenharmony_ci			       struct mlxsw_pci_queue *q)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	struct mlxsw_pci_queue_elem_info *elem_info;
44762306a36Sopenharmony_ci	int i;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	mlxsw_cmd_hw2sw_rdq(mlxsw_pci->core, q->num);
45062306a36Sopenharmony_ci	for (i = 0; i < q->count; i++) {
45162306a36Sopenharmony_ci		elem_info = mlxsw_pci_queue_elem_info_get(q, i);
45262306a36Sopenharmony_ci		mlxsw_pci_rdq_skb_free(mlxsw_pci, elem_info);
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_cistatic void mlxsw_pci_cq_pre_init(struct mlxsw_pci *mlxsw_pci,
45762306a36Sopenharmony_ci				  struct mlxsw_pci_queue *q)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	q->u.cq.v = mlxsw_pci->max_cqe_ver;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	if (q->u.cq.v == MLXSW_PCI_CQE_V2 &&
46262306a36Sopenharmony_ci	    q->num < mlxsw_pci->num_sdq_cqs &&
46362306a36Sopenharmony_ci	    !mlxsw_core_sdq_supports_cqe_v2(mlxsw_pci->core))
46462306a36Sopenharmony_ci		q->u.cq.v = MLXSW_PCI_CQE_V1;
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cistatic int mlxsw_pci_cq_init(struct mlxsw_pci *mlxsw_pci, char *mbox,
46862306a36Sopenharmony_ci			     struct mlxsw_pci_queue *q)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	int i;
47162306a36Sopenharmony_ci	int err;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	q->consumer_counter = 0;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	for (i = 0; i < q->count; i++) {
47662306a36Sopenharmony_ci		char *elem = mlxsw_pci_queue_elem_get(q, i);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci		mlxsw_pci_cqe_owner_set(q->u.cq.v, elem, 1);
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	if (q->u.cq.v == MLXSW_PCI_CQE_V1)
48262306a36Sopenharmony_ci		mlxsw_cmd_mbox_sw2hw_cq_cqe_ver_set(mbox,
48362306a36Sopenharmony_ci				MLXSW_CMD_MBOX_SW2HW_CQ_CQE_VER_1);
48462306a36Sopenharmony_ci	else if (q->u.cq.v == MLXSW_PCI_CQE_V2)
48562306a36Sopenharmony_ci		mlxsw_cmd_mbox_sw2hw_cq_cqe_ver_set(mbox,
48662306a36Sopenharmony_ci				MLXSW_CMD_MBOX_SW2HW_CQ_CQE_VER_2);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	mlxsw_cmd_mbox_sw2hw_cq_c_eqn_set(mbox, MLXSW_PCI_EQ_COMP_NUM);
48962306a36Sopenharmony_ci	mlxsw_cmd_mbox_sw2hw_cq_st_set(mbox, 0);
49062306a36Sopenharmony_ci	mlxsw_cmd_mbox_sw2hw_cq_log_cq_size_set(mbox, ilog2(q->count));
49162306a36Sopenharmony_ci	for (i = 0; i < MLXSW_PCI_AQ_PAGES; i++) {
49262306a36Sopenharmony_ci		dma_addr_t mapaddr = __mlxsw_pci_queue_page_get(q, i);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci		mlxsw_cmd_mbox_sw2hw_cq_pa_set(mbox, i, mapaddr);
49562306a36Sopenharmony_ci	}
49662306a36Sopenharmony_ci	err = mlxsw_cmd_sw2hw_cq(mlxsw_pci->core, mbox, q->num);
49762306a36Sopenharmony_ci	if (err)
49862306a36Sopenharmony_ci		return err;
49962306a36Sopenharmony_ci	mlxsw_pci_queue_doorbell_consumer_ring(mlxsw_pci, q);
50062306a36Sopenharmony_ci	mlxsw_pci_queue_doorbell_arm_consumer_ring(mlxsw_pci, q);
50162306a36Sopenharmony_ci	return 0;
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic void mlxsw_pci_cq_fini(struct mlxsw_pci *mlxsw_pci,
50562306a36Sopenharmony_ci			      struct mlxsw_pci_queue *q)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	mlxsw_cmd_hw2sw_cq(mlxsw_pci->core, q->num);
50862306a36Sopenharmony_ci}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_cistatic unsigned int mlxsw_pci_read32_off(struct mlxsw_pci *mlxsw_pci,
51162306a36Sopenharmony_ci					 ptrdiff_t off)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	return ioread32be(mlxsw_pci->hw_addr + off);
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cistatic void mlxsw_pci_skb_cb_ts_set(struct mlxsw_pci *mlxsw_pci,
51762306a36Sopenharmony_ci				    struct sk_buff *skb,
51862306a36Sopenharmony_ci				    enum mlxsw_pci_cqe_v cqe_v, char *cqe)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	u8 ts_type;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	if (cqe_v != MLXSW_PCI_CQE_V2)
52362306a36Sopenharmony_ci		return;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	ts_type = mlxsw_pci_cqe2_time_stamp_type_get(cqe);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	if (ts_type != MLXSW_PCI_CQE_TIME_STAMP_TYPE_UTC &&
52862306a36Sopenharmony_ci	    ts_type != MLXSW_PCI_CQE_TIME_STAMP_TYPE_MIRROR_UTC)
52962306a36Sopenharmony_ci		return;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	mlxsw_skb_cb(skb)->cqe_ts.sec = mlxsw_pci_cqe2_time_stamp_sec_get(cqe);
53262306a36Sopenharmony_ci	mlxsw_skb_cb(skb)->cqe_ts.nsec =
53362306a36Sopenharmony_ci		mlxsw_pci_cqe2_time_stamp_nsec_get(cqe);
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_cistatic void mlxsw_pci_cqe_sdq_handle(struct mlxsw_pci *mlxsw_pci,
53762306a36Sopenharmony_ci				     struct mlxsw_pci_queue *q,
53862306a36Sopenharmony_ci				     u16 consumer_counter_limit,
53962306a36Sopenharmony_ci				     enum mlxsw_pci_cqe_v cqe_v,
54062306a36Sopenharmony_ci				     char *cqe)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	struct pci_dev *pdev = mlxsw_pci->pdev;
54362306a36Sopenharmony_ci	struct mlxsw_pci_queue_elem_info *elem_info;
54462306a36Sopenharmony_ci	struct mlxsw_tx_info tx_info;
54562306a36Sopenharmony_ci	char *wqe;
54662306a36Sopenharmony_ci	struct sk_buff *skb;
54762306a36Sopenharmony_ci	int i;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	spin_lock(&q->lock);
55062306a36Sopenharmony_ci	elem_info = mlxsw_pci_queue_elem_info_consumer_get(q);
55162306a36Sopenharmony_ci	tx_info = mlxsw_skb_cb(elem_info->u.sdq.skb)->tx_info;
55262306a36Sopenharmony_ci	skb = elem_info->u.sdq.skb;
55362306a36Sopenharmony_ci	wqe = elem_info->elem;
55462306a36Sopenharmony_ci	for (i = 0; i < MLXSW_PCI_WQE_SG_ENTRIES; i++)
55562306a36Sopenharmony_ci		mlxsw_pci_wqe_frag_unmap(mlxsw_pci, wqe, i, DMA_TO_DEVICE);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	if (unlikely(!tx_info.is_emad &&
55862306a36Sopenharmony_ci		     skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
55962306a36Sopenharmony_ci		mlxsw_pci_skb_cb_ts_set(mlxsw_pci, skb, cqe_v, cqe);
56062306a36Sopenharmony_ci		mlxsw_core_ptp_transmitted(mlxsw_pci->core, skb,
56162306a36Sopenharmony_ci					   tx_info.local_port);
56262306a36Sopenharmony_ci		skb = NULL;
56362306a36Sopenharmony_ci	}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	if (skb)
56662306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
56762306a36Sopenharmony_ci	elem_info->u.sdq.skb = NULL;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	if (q->consumer_counter++ != consumer_counter_limit)
57062306a36Sopenharmony_ci		dev_dbg_ratelimited(&pdev->dev, "Consumer counter does not match limit in SDQ\n");
57162306a36Sopenharmony_ci	spin_unlock(&q->lock);
57262306a36Sopenharmony_ci}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_cistatic void mlxsw_pci_cqe_rdq_md_tx_port_init(struct sk_buff *skb,
57562306a36Sopenharmony_ci					      const char *cqe)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	struct mlxsw_skb_cb *cb = mlxsw_skb_cb(skb);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	if (mlxsw_pci_cqe2_tx_lag_get(cqe)) {
58062306a36Sopenharmony_ci		cb->rx_md_info.tx_port_is_lag = true;
58162306a36Sopenharmony_ci		cb->rx_md_info.tx_lag_id = mlxsw_pci_cqe2_tx_lag_id_get(cqe);
58262306a36Sopenharmony_ci		cb->rx_md_info.tx_lag_port_index =
58362306a36Sopenharmony_ci			mlxsw_pci_cqe2_tx_lag_subport_get(cqe);
58462306a36Sopenharmony_ci	} else {
58562306a36Sopenharmony_ci		cb->rx_md_info.tx_port_is_lag = false;
58662306a36Sopenharmony_ci		cb->rx_md_info.tx_sys_port =
58762306a36Sopenharmony_ci			mlxsw_pci_cqe2_tx_system_port_get(cqe);
58862306a36Sopenharmony_ci	}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	if (cb->rx_md_info.tx_sys_port != MLXSW_PCI_CQE2_TX_PORT_MULTI_PORT &&
59162306a36Sopenharmony_ci	    cb->rx_md_info.tx_sys_port != MLXSW_PCI_CQE2_TX_PORT_INVALID)
59262306a36Sopenharmony_ci		cb->rx_md_info.tx_port_valid = 1;
59362306a36Sopenharmony_ci	else
59462306a36Sopenharmony_ci		cb->rx_md_info.tx_port_valid = 0;
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_cistatic void mlxsw_pci_cqe_rdq_md_init(struct sk_buff *skb, const char *cqe)
59862306a36Sopenharmony_ci{
59962306a36Sopenharmony_ci	struct mlxsw_skb_cb *cb = mlxsw_skb_cb(skb);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	cb->rx_md_info.tx_congestion = mlxsw_pci_cqe2_mirror_cong_get(cqe);
60262306a36Sopenharmony_ci	if (cb->rx_md_info.tx_congestion != MLXSW_PCI_CQE2_MIRROR_CONG_INVALID)
60362306a36Sopenharmony_ci		cb->rx_md_info.tx_congestion_valid = 1;
60462306a36Sopenharmony_ci	else
60562306a36Sopenharmony_ci		cb->rx_md_info.tx_congestion_valid = 0;
60662306a36Sopenharmony_ci	cb->rx_md_info.tx_congestion <<= MLXSW_PCI_CQE2_MIRROR_CONG_SHIFT;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	cb->rx_md_info.latency = mlxsw_pci_cqe2_mirror_latency_get(cqe);
60962306a36Sopenharmony_ci	if (cb->rx_md_info.latency != MLXSW_PCI_CQE2_MIRROR_LATENCY_INVALID)
61062306a36Sopenharmony_ci		cb->rx_md_info.latency_valid = 1;
61162306a36Sopenharmony_ci	else
61262306a36Sopenharmony_ci		cb->rx_md_info.latency_valid = 0;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	cb->rx_md_info.tx_tc = mlxsw_pci_cqe2_mirror_tclass_get(cqe);
61562306a36Sopenharmony_ci	if (cb->rx_md_info.tx_tc != MLXSW_PCI_CQE2_MIRROR_TCLASS_INVALID)
61662306a36Sopenharmony_ci		cb->rx_md_info.tx_tc_valid = 1;
61762306a36Sopenharmony_ci	else
61862306a36Sopenharmony_ci		cb->rx_md_info.tx_tc_valid = 0;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	mlxsw_pci_cqe_rdq_md_tx_port_init(skb, cqe);
62162306a36Sopenharmony_ci}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_cistatic void mlxsw_pci_cqe_rdq_handle(struct mlxsw_pci *mlxsw_pci,
62462306a36Sopenharmony_ci				     struct mlxsw_pci_queue *q,
62562306a36Sopenharmony_ci				     u16 consumer_counter_limit,
62662306a36Sopenharmony_ci				     enum mlxsw_pci_cqe_v cqe_v, char *cqe)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci	struct pci_dev *pdev = mlxsw_pci->pdev;
62962306a36Sopenharmony_ci	struct mlxsw_pci_queue_elem_info *elem_info;
63062306a36Sopenharmony_ci	struct mlxsw_rx_info rx_info = {};
63162306a36Sopenharmony_ci	char wqe[MLXSW_PCI_WQE_SIZE];
63262306a36Sopenharmony_ci	struct sk_buff *skb;
63362306a36Sopenharmony_ci	u16 byte_count;
63462306a36Sopenharmony_ci	int err;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	elem_info = mlxsw_pci_queue_elem_info_consumer_get(q);
63762306a36Sopenharmony_ci	skb = elem_info->u.rdq.skb;
63862306a36Sopenharmony_ci	memcpy(wqe, elem_info->elem, MLXSW_PCI_WQE_SIZE);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	if (q->consumer_counter++ != consumer_counter_limit)
64162306a36Sopenharmony_ci		dev_dbg_ratelimited(&pdev->dev, "Consumer counter does not match limit in RDQ\n");
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	err = mlxsw_pci_rdq_skb_alloc(mlxsw_pci, elem_info);
64462306a36Sopenharmony_ci	if (err) {
64562306a36Sopenharmony_ci		dev_err_ratelimited(&pdev->dev, "Failed to alloc skb for RDQ\n");
64662306a36Sopenharmony_ci		goto out;
64762306a36Sopenharmony_ci	}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	mlxsw_pci_wqe_frag_unmap(mlxsw_pci, wqe, 0, DMA_FROM_DEVICE);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	if (mlxsw_pci_cqe_lag_get(cqe_v, cqe)) {
65262306a36Sopenharmony_ci		rx_info.is_lag = true;
65362306a36Sopenharmony_ci		rx_info.u.lag_id = mlxsw_pci_cqe_lag_id_get(cqe_v, cqe);
65462306a36Sopenharmony_ci		rx_info.lag_port_index =
65562306a36Sopenharmony_ci			mlxsw_pci_cqe_lag_subport_get(cqe_v, cqe);
65662306a36Sopenharmony_ci	} else {
65762306a36Sopenharmony_ci		rx_info.is_lag = false;
65862306a36Sopenharmony_ci		rx_info.u.sys_port = mlxsw_pci_cqe_system_port_get(cqe);
65962306a36Sopenharmony_ci	}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	rx_info.trap_id = mlxsw_pci_cqe_trap_id_get(cqe);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	if (rx_info.trap_id == MLXSW_TRAP_ID_DISCARD_INGRESS_ACL ||
66462306a36Sopenharmony_ci	    rx_info.trap_id == MLXSW_TRAP_ID_DISCARD_EGRESS_ACL) {
66562306a36Sopenharmony_ci		u32 cookie_index = 0;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci		if (mlxsw_pci->max_cqe_ver >= MLXSW_PCI_CQE_V2)
66862306a36Sopenharmony_ci			cookie_index = mlxsw_pci_cqe2_user_def_val_orig_pkt_len_get(cqe);
66962306a36Sopenharmony_ci		mlxsw_skb_cb(skb)->rx_md_info.cookie_index = cookie_index;
67062306a36Sopenharmony_ci	} else if (rx_info.trap_id >= MLXSW_TRAP_ID_MIRROR_SESSION0 &&
67162306a36Sopenharmony_ci		   rx_info.trap_id <= MLXSW_TRAP_ID_MIRROR_SESSION7 &&
67262306a36Sopenharmony_ci		   mlxsw_pci->max_cqe_ver >= MLXSW_PCI_CQE_V2) {
67362306a36Sopenharmony_ci		rx_info.mirror_reason = mlxsw_pci_cqe2_mirror_reason_get(cqe);
67462306a36Sopenharmony_ci		mlxsw_pci_cqe_rdq_md_init(skb, cqe);
67562306a36Sopenharmony_ci	} else if (rx_info.trap_id == MLXSW_TRAP_ID_PKT_SAMPLE &&
67662306a36Sopenharmony_ci		   mlxsw_pci->max_cqe_ver >= MLXSW_PCI_CQE_V2) {
67762306a36Sopenharmony_ci		mlxsw_pci_cqe_rdq_md_tx_port_init(skb, cqe);
67862306a36Sopenharmony_ci	}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	mlxsw_pci_skb_cb_ts_set(mlxsw_pci, skb, cqe_v, cqe);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	byte_count = mlxsw_pci_cqe_byte_count_get(cqe);
68362306a36Sopenharmony_ci	if (mlxsw_pci_cqe_crc_get(cqe_v, cqe))
68462306a36Sopenharmony_ci		byte_count -= ETH_FCS_LEN;
68562306a36Sopenharmony_ci	skb_put(skb, byte_count);
68662306a36Sopenharmony_ci	mlxsw_core_skb_receive(mlxsw_pci->core, skb, &rx_info);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ciout:
68962306a36Sopenharmony_ci	/* Everything is set up, ring doorbell to pass elem to HW */
69062306a36Sopenharmony_ci	q->producer_counter++;
69162306a36Sopenharmony_ci	mlxsw_pci_queue_doorbell_producer_ring(mlxsw_pci, q);
69262306a36Sopenharmony_ci	return;
69362306a36Sopenharmony_ci}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_cistatic char *mlxsw_pci_cq_sw_cqe_get(struct mlxsw_pci_queue *q)
69662306a36Sopenharmony_ci{
69762306a36Sopenharmony_ci	struct mlxsw_pci_queue_elem_info *elem_info;
69862306a36Sopenharmony_ci	char *elem;
69962306a36Sopenharmony_ci	bool owner_bit;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	elem_info = mlxsw_pci_queue_elem_info_consumer_get(q);
70262306a36Sopenharmony_ci	elem = elem_info->elem;
70362306a36Sopenharmony_ci	owner_bit = mlxsw_pci_cqe_owner_get(q->u.cq.v, elem);
70462306a36Sopenharmony_ci	if (mlxsw_pci_elem_hw_owned(q, owner_bit))
70562306a36Sopenharmony_ci		return NULL;
70662306a36Sopenharmony_ci	q->consumer_counter++;
70762306a36Sopenharmony_ci	rmb(); /* make sure we read owned bit before the rest of elem */
70862306a36Sopenharmony_ci	return elem;
70962306a36Sopenharmony_ci}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_cistatic void mlxsw_pci_cq_tasklet(struct tasklet_struct *t)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	struct mlxsw_pci_queue *q = from_tasklet(q, t, tasklet);
71462306a36Sopenharmony_ci	struct mlxsw_pci *mlxsw_pci = q->pci;
71562306a36Sopenharmony_ci	char *cqe;
71662306a36Sopenharmony_ci	int items = 0;
71762306a36Sopenharmony_ci	int credits = q->count >> 1;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	while ((cqe = mlxsw_pci_cq_sw_cqe_get(q))) {
72062306a36Sopenharmony_ci		u16 wqe_counter = mlxsw_pci_cqe_wqe_counter_get(cqe);
72162306a36Sopenharmony_ci		u8 sendq = mlxsw_pci_cqe_sr_get(q->u.cq.v, cqe);
72262306a36Sopenharmony_ci		u8 dqn = mlxsw_pci_cqe_dqn_get(q->u.cq.v, cqe);
72362306a36Sopenharmony_ci		char ncqe[MLXSW_PCI_CQE_SIZE_MAX];
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci		memcpy(ncqe, cqe, q->elem_size);
72662306a36Sopenharmony_ci		mlxsw_pci_queue_doorbell_consumer_ring(mlxsw_pci, q);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci		if (sendq) {
72962306a36Sopenharmony_ci			struct mlxsw_pci_queue *sdq;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci			sdq = mlxsw_pci_sdq_get(mlxsw_pci, dqn);
73262306a36Sopenharmony_ci			mlxsw_pci_cqe_sdq_handle(mlxsw_pci, sdq,
73362306a36Sopenharmony_ci						 wqe_counter, q->u.cq.v, ncqe);
73462306a36Sopenharmony_ci			q->u.cq.comp_sdq_count++;
73562306a36Sopenharmony_ci		} else {
73662306a36Sopenharmony_ci			struct mlxsw_pci_queue *rdq;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci			rdq = mlxsw_pci_rdq_get(mlxsw_pci, dqn);
73962306a36Sopenharmony_ci			mlxsw_pci_cqe_rdq_handle(mlxsw_pci, rdq,
74062306a36Sopenharmony_ci						 wqe_counter, q->u.cq.v, ncqe);
74162306a36Sopenharmony_ci			q->u.cq.comp_rdq_count++;
74262306a36Sopenharmony_ci		}
74362306a36Sopenharmony_ci		if (++items == credits)
74462306a36Sopenharmony_ci			break;
74562306a36Sopenharmony_ci	}
74662306a36Sopenharmony_ci	if (items)
74762306a36Sopenharmony_ci		mlxsw_pci_queue_doorbell_arm_consumer_ring(mlxsw_pci, q);
74862306a36Sopenharmony_ci}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_cistatic u16 mlxsw_pci_cq_elem_count(const struct mlxsw_pci_queue *q)
75162306a36Sopenharmony_ci{
75262306a36Sopenharmony_ci	return q->u.cq.v == MLXSW_PCI_CQE_V2 ? MLXSW_PCI_CQE2_COUNT :
75362306a36Sopenharmony_ci					       MLXSW_PCI_CQE01_COUNT;
75462306a36Sopenharmony_ci}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_cistatic u8 mlxsw_pci_cq_elem_size(const struct mlxsw_pci_queue *q)
75762306a36Sopenharmony_ci{
75862306a36Sopenharmony_ci	return q->u.cq.v == MLXSW_PCI_CQE_V2 ? MLXSW_PCI_CQE2_SIZE :
75962306a36Sopenharmony_ci					       MLXSW_PCI_CQE01_SIZE;
76062306a36Sopenharmony_ci}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_cistatic int mlxsw_pci_eq_init(struct mlxsw_pci *mlxsw_pci, char *mbox,
76362306a36Sopenharmony_ci			     struct mlxsw_pci_queue *q)
76462306a36Sopenharmony_ci{
76562306a36Sopenharmony_ci	int i;
76662306a36Sopenharmony_ci	int err;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	q->consumer_counter = 0;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	for (i = 0; i < q->count; i++) {
77162306a36Sopenharmony_ci		char *elem = mlxsw_pci_queue_elem_get(q, i);
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci		mlxsw_pci_eqe_owner_set(elem, 1);
77462306a36Sopenharmony_ci	}
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	mlxsw_cmd_mbox_sw2hw_eq_int_msix_set(mbox, 1); /* MSI-X used */
77762306a36Sopenharmony_ci	mlxsw_cmd_mbox_sw2hw_eq_st_set(mbox, 1); /* armed */
77862306a36Sopenharmony_ci	mlxsw_cmd_mbox_sw2hw_eq_log_eq_size_set(mbox, ilog2(q->count));
77962306a36Sopenharmony_ci	for (i = 0; i < MLXSW_PCI_AQ_PAGES; i++) {
78062306a36Sopenharmony_ci		dma_addr_t mapaddr = __mlxsw_pci_queue_page_get(q, i);
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci		mlxsw_cmd_mbox_sw2hw_eq_pa_set(mbox, i, mapaddr);
78362306a36Sopenharmony_ci	}
78462306a36Sopenharmony_ci	err = mlxsw_cmd_sw2hw_eq(mlxsw_pci->core, mbox, q->num);
78562306a36Sopenharmony_ci	if (err)
78662306a36Sopenharmony_ci		return err;
78762306a36Sopenharmony_ci	mlxsw_pci_queue_doorbell_consumer_ring(mlxsw_pci, q);
78862306a36Sopenharmony_ci	mlxsw_pci_queue_doorbell_arm_consumer_ring(mlxsw_pci, q);
78962306a36Sopenharmony_ci	return 0;
79062306a36Sopenharmony_ci}
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_cistatic void mlxsw_pci_eq_fini(struct mlxsw_pci *mlxsw_pci,
79362306a36Sopenharmony_ci			      struct mlxsw_pci_queue *q)
79462306a36Sopenharmony_ci{
79562306a36Sopenharmony_ci	mlxsw_cmd_hw2sw_eq(mlxsw_pci->core, q->num);
79662306a36Sopenharmony_ci}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_cistatic void mlxsw_pci_eq_cmd_event(struct mlxsw_pci *mlxsw_pci, char *eqe)
79962306a36Sopenharmony_ci{
80062306a36Sopenharmony_ci	mlxsw_pci->cmd.comp.status = mlxsw_pci_eqe_cmd_status_get(eqe);
80162306a36Sopenharmony_ci	mlxsw_pci->cmd.comp.out_param =
80262306a36Sopenharmony_ci		((u64) mlxsw_pci_eqe_cmd_out_param_h_get(eqe)) << 32 |
80362306a36Sopenharmony_ci		mlxsw_pci_eqe_cmd_out_param_l_get(eqe);
80462306a36Sopenharmony_ci	mlxsw_pci->cmd.wait_done = true;
80562306a36Sopenharmony_ci	wake_up(&mlxsw_pci->cmd.wait);
80662306a36Sopenharmony_ci}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_cistatic char *mlxsw_pci_eq_sw_eqe_get(struct mlxsw_pci_queue *q)
80962306a36Sopenharmony_ci{
81062306a36Sopenharmony_ci	struct mlxsw_pci_queue_elem_info *elem_info;
81162306a36Sopenharmony_ci	char *elem;
81262306a36Sopenharmony_ci	bool owner_bit;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	elem_info = mlxsw_pci_queue_elem_info_consumer_get(q);
81562306a36Sopenharmony_ci	elem = elem_info->elem;
81662306a36Sopenharmony_ci	owner_bit = mlxsw_pci_eqe_owner_get(elem);
81762306a36Sopenharmony_ci	if (mlxsw_pci_elem_hw_owned(q, owner_bit))
81862306a36Sopenharmony_ci		return NULL;
81962306a36Sopenharmony_ci	q->consumer_counter++;
82062306a36Sopenharmony_ci	rmb(); /* make sure we read owned bit before the rest of elem */
82162306a36Sopenharmony_ci	return elem;
82262306a36Sopenharmony_ci}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_cistatic void mlxsw_pci_eq_tasklet(struct tasklet_struct *t)
82562306a36Sopenharmony_ci{
82662306a36Sopenharmony_ci	struct mlxsw_pci_queue *q = from_tasklet(q, t, tasklet);
82762306a36Sopenharmony_ci	struct mlxsw_pci *mlxsw_pci = q->pci;
82862306a36Sopenharmony_ci	u8 cq_count = mlxsw_pci_cq_count(mlxsw_pci);
82962306a36Sopenharmony_ci	unsigned long active_cqns[BITS_TO_LONGS(MLXSW_PCI_CQS_MAX)];
83062306a36Sopenharmony_ci	char *eqe;
83162306a36Sopenharmony_ci	u8 cqn;
83262306a36Sopenharmony_ci	bool cq_handle = false;
83362306a36Sopenharmony_ci	int items = 0;
83462306a36Sopenharmony_ci	int credits = q->count >> 1;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	memset(&active_cqns, 0, sizeof(active_cqns));
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	while ((eqe = mlxsw_pci_eq_sw_eqe_get(q))) {
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci		/* Command interface completion events are always received on
84162306a36Sopenharmony_ci		 * queue MLXSW_PCI_EQ_ASYNC_NUM (EQ0) and completion events
84262306a36Sopenharmony_ci		 * are mapped to queue MLXSW_PCI_EQ_COMP_NUM (EQ1).
84362306a36Sopenharmony_ci		 */
84462306a36Sopenharmony_ci		switch (q->num) {
84562306a36Sopenharmony_ci		case MLXSW_PCI_EQ_ASYNC_NUM:
84662306a36Sopenharmony_ci			mlxsw_pci_eq_cmd_event(mlxsw_pci, eqe);
84762306a36Sopenharmony_ci			q->u.eq.ev_cmd_count++;
84862306a36Sopenharmony_ci			break;
84962306a36Sopenharmony_ci		case MLXSW_PCI_EQ_COMP_NUM:
85062306a36Sopenharmony_ci			cqn = mlxsw_pci_eqe_cqn_get(eqe);
85162306a36Sopenharmony_ci			set_bit(cqn, active_cqns);
85262306a36Sopenharmony_ci			cq_handle = true;
85362306a36Sopenharmony_ci			q->u.eq.ev_comp_count++;
85462306a36Sopenharmony_ci			break;
85562306a36Sopenharmony_ci		default:
85662306a36Sopenharmony_ci			q->u.eq.ev_other_count++;
85762306a36Sopenharmony_ci		}
85862306a36Sopenharmony_ci		if (++items == credits)
85962306a36Sopenharmony_ci			break;
86062306a36Sopenharmony_ci	}
86162306a36Sopenharmony_ci	if (items) {
86262306a36Sopenharmony_ci		mlxsw_pci_queue_doorbell_consumer_ring(mlxsw_pci, q);
86362306a36Sopenharmony_ci		mlxsw_pci_queue_doorbell_arm_consumer_ring(mlxsw_pci, q);
86462306a36Sopenharmony_ci	}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	if (!cq_handle)
86762306a36Sopenharmony_ci		return;
86862306a36Sopenharmony_ci	for_each_set_bit(cqn, active_cqns, cq_count) {
86962306a36Sopenharmony_ci		q = mlxsw_pci_cq_get(mlxsw_pci, cqn);
87062306a36Sopenharmony_ci		mlxsw_pci_queue_tasklet_schedule(q);
87162306a36Sopenharmony_ci	}
87262306a36Sopenharmony_ci}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_cistruct mlxsw_pci_queue_ops {
87562306a36Sopenharmony_ci	const char *name;
87662306a36Sopenharmony_ci	enum mlxsw_pci_queue_type type;
87762306a36Sopenharmony_ci	void (*pre_init)(struct mlxsw_pci *mlxsw_pci,
87862306a36Sopenharmony_ci			 struct mlxsw_pci_queue *q);
87962306a36Sopenharmony_ci	int (*init)(struct mlxsw_pci *mlxsw_pci, char *mbox,
88062306a36Sopenharmony_ci		    struct mlxsw_pci_queue *q);
88162306a36Sopenharmony_ci	void (*fini)(struct mlxsw_pci *mlxsw_pci,
88262306a36Sopenharmony_ci		     struct mlxsw_pci_queue *q);
88362306a36Sopenharmony_ci	void (*tasklet)(struct tasklet_struct *t);
88462306a36Sopenharmony_ci	u16 (*elem_count_f)(const struct mlxsw_pci_queue *q);
88562306a36Sopenharmony_ci	u8 (*elem_size_f)(const struct mlxsw_pci_queue *q);
88662306a36Sopenharmony_ci	u16 elem_count;
88762306a36Sopenharmony_ci	u8 elem_size;
88862306a36Sopenharmony_ci};
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_cistatic const struct mlxsw_pci_queue_ops mlxsw_pci_sdq_ops = {
89162306a36Sopenharmony_ci	.type		= MLXSW_PCI_QUEUE_TYPE_SDQ,
89262306a36Sopenharmony_ci	.init		= mlxsw_pci_sdq_init,
89362306a36Sopenharmony_ci	.fini		= mlxsw_pci_sdq_fini,
89462306a36Sopenharmony_ci	.elem_count	= MLXSW_PCI_WQE_COUNT,
89562306a36Sopenharmony_ci	.elem_size	= MLXSW_PCI_WQE_SIZE,
89662306a36Sopenharmony_ci};
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_cistatic const struct mlxsw_pci_queue_ops mlxsw_pci_rdq_ops = {
89962306a36Sopenharmony_ci	.type		= MLXSW_PCI_QUEUE_TYPE_RDQ,
90062306a36Sopenharmony_ci	.init		= mlxsw_pci_rdq_init,
90162306a36Sopenharmony_ci	.fini		= mlxsw_pci_rdq_fini,
90262306a36Sopenharmony_ci	.elem_count	= MLXSW_PCI_WQE_COUNT,
90362306a36Sopenharmony_ci	.elem_size	= MLXSW_PCI_WQE_SIZE
90462306a36Sopenharmony_ci};
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_cistatic const struct mlxsw_pci_queue_ops mlxsw_pci_cq_ops = {
90762306a36Sopenharmony_ci	.type		= MLXSW_PCI_QUEUE_TYPE_CQ,
90862306a36Sopenharmony_ci	.pre_init	= mlxsw_pci_cq_pre_init,
90962306a36Sopenharmony_ci	.init		= mlxsw_pci_cq_init,
91062306a36Sopenharmony_ci	.fini		= mlxsw_pci_cq_fini,
91162306a36Sopenharmony_ci	.tasklet	= mlxsw_pci_cq_tasklet,
91262306a36Sopenharmony_ci	.elem_count_f	= mlxsw_pci_cq_elem_count,
91362306a36Sopenharmony_ci	.elem_size_f	= mlxsw_pci_cq_elem_size
91462306a36Sopenharmony_ci};
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_cistatic const struct mlxsw_pci_queue_ops mlxsw_pci_eq_ops = {
91762306a36Sopenharmony_ci	.type		= MLXSW_PCI_QUEUE_TYPE_EQ,
91862306a36Sopenharmony_ci	.init		= mlxsw_pci_eq_init,
91962306a36Sopenharmony_ci	.fini		= mlxsw_pci_eq_fini,
92062306a36Sopenharmony_ci	.tasklet	= mlxsw_pci_eq_tasklet,
92162306a36Sopenharmony_ci	.elem_count	= MLXSW_PCI_EQE_COUNT,
92262306a36Sopenharmony_ci	.elem_size	= MLXSW_PCI_EQE_SIZE
92362306a36Sopenharmony_ci};
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_cistatic int mlxsw_pci_queue_init(struct mlxsw_pci *mlxsw_pci, char *mbox,
92662306a36Sopenharmony_ci				const struct mlxsw_pci_queue_ops *q_ops,
92762306a36Sopenharmony_ci				struct mlxsw_pci_queue *q, u8 q_num)
92862306a36Sopenharmony_ci{
92962306a36Sopenharmony_ci	struct mlxsw_pci_mem_item *mem_item = &q->mem_item;
93062306a36Sopenharmony_ci	int i;
93162306a36Sopenharmony_ci	int err;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	q->num = q_num;
93462306a36Sopenharmony_ci	if (q_ops->pre_init)
93562306a36Sopenharmony_ci		q_ops->pre_init(mlxsw_pci, q);
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	spin_lock_init(&q->lock);
93862306a36Sopenharmony_ci	q->count = q_ops->elem_count_f ? q_ops->elem_count_f(q) :
93962306a36Sopenharmony_ci					 q_ops->elem_count;
94062306a36Sopenharmony_ci	q->elem_size = q_ops->elem_size_f ? q_ops->elem_size_f(q) :
94162306a36Sopenharmony_ci					    q_ops->elem_size;
94262306a36Sopenharmony_ci	q->type = q_ops->type;
94362306a36Sopenharmony_ci	q->pci = mlxsw_pci;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	if (q_ops->tasklet)
94662306a36Sopenharmony_ci		tasklet_setup(&q->tasklet, q_ops->tasklet);
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	mem_item->size = MLXSW_PCI_AQ_SIZE;
94962306a36Sopenharmony_ci	mem_item->buf = dma_alloc_coherent(&mlxsw_pci->pdev->dev,
95062306a36Sopenharmony_ci					   mem_item->size, &mem_item->mapaddr,
95162306a36Sopenharmony_ci					   GFP_KERNEL);
95262306a36Sopenharmony_ci	if (!mem_item->buf)
95362306a36Sopenharmony_ci		return -ENOMEM;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	q->elem_info = kcalloc(q->count, sizeof(*q->elem_info), GFP_KERNEL);
95662306a36Sopenharmony_ci	if (!q->elem_info) {
95762306a36Sopenharmony_ci		err = -ENOMEM;
95862306a36Sopenharmony_ci		goto err_elem_info_alloc;
95962306a36Sopenharmony_ci	}
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	/* Initialize dma mapped elements info elem_info for
96262306a36Sopenharmony_ci	 * future easy access.
96362306a36Sopenharmony_ci	 */
96462306a36Sopenharmony_ci	for (i = 0; i < q->count; i++) {
96562306a36Sopenharmony_ci		struct mlxsw_pci_queue_elem_info *elem_info;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci		elem_info = mlxsw_pci_queue_elem_info_get(q, i);
96862306a36Sopenharmony_ci		elem_info->elem =
96962306a36Sopenharmony_ci			__mlxsw_pci_queue_elem_get(q, q->elem_size, i);
97062306a36Sopenharmony_ci	}
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	mlxsw_cmd_mbox_zero(mbox);
97362306a36Sopenharmony_ci	err = q_ops->init(mlxsw_pci, mbox, q);
97462306a36Sopenharmony_ci	if (err)
97562306a36Sopenharmony_ci		goto err_q_ops_init;
97662306a36Sopenharmony_ci	return 0;
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_cierr_q_ops_init:
97962306a36Sopenharmony_ci	kfree(q->elem_info);
98062306a36Sopenharmony_cierr_elem_info_alloc:
98162306a36Sopenharmony_ci	dma_free_coherent(&mlxsw_pci->pdev->dev, mem_item->size,
98262306a36Sopenharmony_ci			  mem_item->buf, mem_item->mapaddr);
98362306a36Sopenharmony_ci	return err;
98462306a36Sopenharmony_ci}
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_cistatic void mlxsw_pci_queue_fini(struct mlxsw_pci *mlxsw_pci,
98762306a36Sopenharmony_ci				 const struct mlxsw_pci_queue_ops *q_ops,
98862306a36Sopenharmony_ci				 struct mlxsw_pci_queue *q)
98962306a36Sopenharmony_ci{
99062306a36Sopenharmony_ci	struct mlxsw_pci_mem_item *mem_item = &q->mem_item;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	q_ops->fini(mlxsw_pci, q);
99362306a36Sopenharmony_ci	kfree(q->elem_info);
99462306a36Sopenharmony_ci	dma_free_coherent(&mlxsw_pci->pdev->dev, mem_item->size,
99562306a36Sopenharmony_ci			  mem_item->buf, mem_item->mapaddr);
99662306a36Sopenharmony_ci}
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_cistatic int mlxsw_pci_queue_group_init(struct mlxsw_pci *mlxsw_pci, char *mbox,
99962306a36Sopenharmony_ci				      const struct mlxsw_pci_queue_ops *q_ops,
100062306a36Sopenharmony_ci				      u8 num_qs)
100162306a36Sopenharmony_ci{
100262306a36Sopenharmony_ci	struct mlxsw_pci_queue_type_group *queue_group;
100362306a36Sopenharmony_ci	int i;
100462306a36Sopenharmony_ci	int err;
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	queue_group = mlxsw_pci_queue_type_group_get(mlxsw_pci, q_ops->type);
100762306a36Sopenharmony_ci	queue_group->q = kcalloc(num_qs, sizeof(*queue_group->q), GFP_KERNEL);
100862306a36Sopenharmony_ci	if (!queue_group->q)
100962306a36Sopenharmony_ci		return -ENOMEM;
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	for (i = 0; i < num_qs; i++) {
101262306a36Sopenharmony_ci		err = mlxsw_pci_queue_init(mlxsw_pci, mbox, q_ops,
101362306a36Sopenharmony_ci					   &queue_group->q[i], i);
101462306a36Sopenharmony_ci		if (err)
101562306a36Sopenharmony_ci			goto err_queue_init;
101662306a36Sopenharmony_ci	}
101762306a36Sopenharmony_ci	queue_group->count = num_qs;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	return 0;
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_cierr_queue_init:
102262306a36Sopenharmony_ci	for (i--; i >= 0; i--)
102362306a36Sopenharmony_ci		mlxsw_pci_queue_fini(mlxsw_pci, q_ops, &queue_group->q[i]);
102462306a36Sopenharmony_ci	kfree(queue_group->q);
102562306a36Sopenharmony_ci	return err;
102662306a36Sopenharmony_ci}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_cistatic void mlxsw_pci_queue_group_fini(struct mlxsw_pci *mlxsw_pci,
102962306a36Sopenharmony_ci				       const struct mlxsw_pci_queue_ops *q_ops)
103062306a36Sopenharmony_ci{
103162306a36Sopenharmony_ci	struct mlxsw_pci_queue_type_group *queue_group;
103262306a36Sopenharmony_ci	int i;
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	queue_group = mlxsw_pci_queue_type_group_get(mlxsw_pci, q_ops->type);
103562306a36Sopenharmony_ci	for (i = 0; i < queue_group->count; i++)
103662306a36Sopenharmony_ci		mlxsw_pci_queue_fini(mlxsw_pci, q_ops, &queue_group->q[i]);
103762306a36Sopenharmony_ci	kfree(queue_group->q);
103862306a36Sopenharmony_ci}
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_cistatic int mlxsw_pci_aqs_init(struct mlxsw_pci *mlxsw_pci, char *mbox)
104162306a36Sopenharmony_ci{
104262306a36Sopenharmony_ci	struct pci_dev *pdev = mlxsw_pci->pdev;
104362306a36Sopenharmony_ci	u8 num_sdqs;
104462306a36Sopenharmony_ci	u8 sdq_log2sz;
104562306a36Sopenharmony_ci	u8 num_rdqs;
104662306a36Sopenharmony_ci	u8 rdq_log2sz;
104762306a36Sopenharmony_ci	u8 num_cqs;
104862306a36Sopenharmony_ci	u8 cq_log2sz;
104962306a36Sopenharmony_ci	u8 cqv2_log2sz;
105062306a36Sopenharmony_ci	u8 num_eqs;
105162306a36Sopenharmony_ci	u8 eq_log2sz;
105262306a36Sopenharmony_ci	int err;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	mlxsw_cmd_mbox_zero(mbox);
105562306a36Sopenharmony_ci	err = mlxsw_cmd_query_aq_cap(mlxsw_pci->core, mbox);
105662306a36Sopenharmony_ci	if (err)
105762306a36Sopenharmony_ci		return err;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	num_sdqs = mlxsw_cmd_mbox_query_aq_cap_max_num_sdqs_get(mbox);
106062306a36Sopenharmony_ci	sdq_log2sz = mlxsw_cmd_mbox_query_aq_cap_log_max_sdq_sz_get(mbox);
106162306a36Sopenharmony_ci	num_rdqs = mlxsw_cmd_mbox_query_aq_cap_max_num_rdqs_get(mbox);
106262306a36Sopenharmony_ci	rdq_log2sz = mlxsw_cmd_mbox_query_aq_cap_log_max_rdq_sz_get(mbox);
106362306a36Sopenharmony_ci	num_cqs = mlxsw_cmd_mbox_query_aq_cap_max_num_cqs_get(mbox);
106462306a36Sopenharmony_ci	cq_log2sz = mlxsw_cmd_mbox_query_aq_cap_log_max_cq_sz_get(mbox);
106562306a36Sopenharmony_ci	cqv2_log2sz = mlxsw_cmd_mbox_query_aq_cap_log_max_cqv2_sz_get(mbox);
106662306a36Sopenharmony_ci	num_eqs = mlxsw_cmd_mbox_query_aq_cap_max_num_eqs_get(mbox);
106762306a36Sopenharmony_ci	eq_log2sz = mlxsw_cmd_mbox_query_aq_cap_log_max_eq_sz_get(mbox);
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	if (num_sdqs + num_rdqs > num_cqs ||
107062306a36Sopenharmony_ci	    num_sdqs < MLXSW_PCI_SDQS_MIN ||
107162306a36Sopenharmony_ci	    num_cqs > MLXSW_PCI_CQS_MAX || num_eqs != MLXSW_PCI_EQS_COUNT) {
107262306a36Sopenharmony_ci		dev_err(&pdev->dev, "Unsupported number of queues\n");
107362306a36Sopenharmony_ci		return -EINVAL;
107462306a36Sopenharmony_ci	}
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	if ((1 << sdq_log2sz != MLXSW_PCI_WQE_COUNT) ||
107762306a36Sopenharmony_ci	    (1 << rdq_log2sz != MLXSW_PCI_WQE_COUNT) ||
107862306a36Sopenharmony_ci	    (1 << cq_log2sz != MLXSW_PCI_CQE01_COUNT) ||
107962306a36Sopenharmony_ci	    (mlxsw_pci->max_cqe_ver == MLXSW_PCI_CQE_V2 &&
108062306a36Sopenharmony_ci	     (1 << cqv2_log2sz != MLXSW_PCI_CQE2_COUNT)) ||
108162306a36Sopenharmony_ci	    (1 << eq_log2sz != MLXSW_PCI_EQE_COUNT)) {
108262306a36Sopenharmony_ci		dev_err(&pdev->dev, "Unsupported number of async queue descriptors\n");
108362306a36Sopenharmony_ci		return -EINVAL;
108462306a36Sopenharmony_ci	}
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	mlxsw_pci->num_sdq_cqs = num_sdqs;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	err = mlxsw_pci_queue_group_init(mlxsw_pci, mbox, &mlxsw_pci_eq_ops,
108962306a36Sopenharmony_ci					 num_eqs);
109062306a36Sopenharmony_ci	if (err) {
109162306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to initialize event queues\n");
109262306a36Sopenharmony_ci		return err;
109362306a36Sopenharmony_ci	}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	err = mlxsw_pci_queue_group_init(mlxsw_pci, mbox, &mlxsw_pci_cq_ops,
109662306a36Sopenharmony_ci					 num_cqs);
109762306a36Sopenharmony_ci	if (err) {
109862306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to initialize completion queues\n");
109962306a36Sopenharmony_ci		goto err_cqs_init;
110062306a36Sopenharmony_ci	}
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	err = mlxsw_pci_queue_group_init(mlxsw_pci, mbox, &mlxsw_pci_sdq_ops,
110362306a36Sopenharmony_ci					 num_sdqs);
110462306a36Sopenharmony_ci	if (err) {
110562306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to initialize send descriptor queues\n");
110662306a36Sopenharmony_ci		goto err_sdqs_init;
110762306a36Sopenharmony_ci	}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	err = mlxsw_pci_queue_group_init(mlxsw_pci, mbox, &mlxsw_pci_rdq_ops,
111062306a36Sopenharmony_ci					 num_rdqs);
111162306a36Sopenharmony_ci	if (err) {
111262306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to initialize receive descriptor queues\n");
111362306a36Sopenharmony_ci		goto err_rdqs_init;
111462306a36Sopenharmony_ci	}
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	/* We have to poll in command interface until queues are initialized */
111762306a36Sopenharmony_ci	mlxsw_pci->cmd.nopoll = true;
111862306a36Sopenharmony_ci	return 0;
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_cierr_rdqs_init:
112162306a36Sopenharmony_ci	mlxsw_pci_queue_group_fini(mlxsw_pci, &mlxsw_pci_sdq_ops);
112262306a36Sopenharmony_cierr_sdqs_init:
112362306a36Sopenharmony_ci	mlxsw_pci_queue_group_fini(mlxsw_pci, &mlxsw_pci_cq_ops);
112462306a36Sopenharmony_cierr_cqs_init:
112562306a36Sopenharmony_ci	mlxsw_pci_queue_group_fini(mlxsw_pci, &mlxsw_pci_eq_ops);
112662306a36Sopenharmony_ci	return err;
112762306a36Sopenharmony_ci}
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_cistatic void mlxsw_pci_aqs_fini(struct mlxsw_pci *mlxsw_pci)
113062306a36Sopenharmony_ci{
113162306a36Sopenharmony_ci	mlxsw_pci->cmd.nopoll = false;
113262306a36Sopenharmony_ci	mlxsw_pci_queue_group_fini(mlxsw_pci, &mlxsw_pci_rdq_ops);
113362306a36Sopenharmony_ci	mlxsw_pci_queue_group_fini(mlxsw_pci, &mlxsw_pci_sdq_ops);
113462306a36Sopenharmony_ci	mlxsw_pci_queue_group_fini(mlxsw_pci, &mlxsw_pci_cq_ops);
113562306a36Sopenharmony_ci	mlxsw_pci_queue_group_fini(mlxsw_pci, &mlxsw_pci_eq_ops);
113662306a36Sopenharmony_ci}
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_cistatic void
113962306a36Sopenharmony_cimlxsw_pci_config_profile_swid_config(struct mlxsw_pci *mlxsw_pci,
114062306a36Sopenharmony_ci				     char *mbox, int index,
114162306a36Sopenharmony_ci				     const struct mlxsw_swid_config *swid)
114262306a36Sopenharmony_ci{
114362306a36Sopenharmony_ci	u8 mask = 0;
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	if (swid->used_type) {
114662306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_swid_config_type_set(
114762306a36Sopenharmony_ci			mbox, index, swid->type);
114862306a36Sopenharmony_ci		mask |= 1;
114962306a36Sopenharmony_ci	}
115062306a36Sopenharmony_ci	if (swid->used_properties) {
115162306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_swid_config_properties_set(
115262306a36Sopenharmony_ci			mbox, index, swid->properties);
115362306a36Sopenharmony_ci		mask |= 2;
115462306a36Sopenharmony_ci	}
115562306a36Sopenharmony_ci	mlxsw_cmd_mbox_config_profile_swid_config_mask_set(mbox, index, mask);
115662306a36Sopenharmony_ci}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_cistatic int
115962306a36Sopenharmony_cimlxsw_pci_profile_get_kvd_sizes(const struct mlxsw_pci *mlxsw_pci,
116062306a36Sopenharmony_ci				const struct mlxsw_config_profile *profile,
116162306a36Sopenharmony_ci				struct mlxsw_res *res)
116262306a36Sopenharmony_ci{
116362306a36Sopenharmony_ci	u64 single_size, double_size, linear_size;
116462306a36Sopenharmony_ci	int err;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	err = mlxsw_core_kvd_sizes_get(mlxsw_pci->core, profile,
116762306a36Sopenharmony_ci				       &single_size, &double_size,
116862306a36Sopenharmony_ci				       &linear_size);
116962306a36Sopenharmony_ci	if (err)
117062306a36Sopenharmony_ci		return err;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	MLXSW_RES_SET(res, KVD_SINGLE_SIZE, single_size);
117362306a36Sopenharmony_ci	MLXSW_RES_SET(res, KVD_DOUBLE_SIZE, double_size);
117462306a36Sopenharmony_ci	MLXSW_RES_SET(res, KVD_LINEAR_SIZE, linear_size);
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	return 0;
117762306a36Sopenharmony_ci}
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_cistatic int mlxsw_pci_config_profile(struct mlxsw_pci *mlxsw_pci, char *mbox,
118062306a36Sopenharmony_ci				    const struct mlxsw_config_profile *profile,
118162306a36Sopenharmony_ci				    struct mlxsw_res *res)
118262306a36Sopenharmony_ci{
118362306a36Sopenharmony_ci	int i;
118462306a36Sopenharmony_ci	int err;
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	mlxsw_cmd_mbox_zero(mbox);
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	if (profile->used_max_vepa_channels) {
118962306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_set_max_vepa_channels_set(
119062306a36Sopenharmony_ci			mbox, 1);
119162306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_max_vepa_channels_set(
119262306a36Sopenharmony_ci			mbox, profile->max_vepa_channels);
119362306a36Sopenharmony_ci	}
119462306a36Sopenharmony_ci	if (profile->used_max_lag) {
119562306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_set_max_lag_set(mbox, 1);
119662306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_max_lag_set(mbox,
119762306a36Sopenharmony_ci							  profile->max_lag);
119862306a36Sopenharmony_ci	}
119962306a36Sopenharmony_ci	if (profile->used_max_mid) {
120062306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_set_max_mid_set(
120162306a36Sopenharmony_ci			mbox, 1);
120262306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_max_mid_set(
120362306a36Sopenharmony_ci			mbox, profile->max_mid);
120462306a36Sopenharmony_ci	}
120562306a36Sopenharmony_ci	if (profile->used_max_pgt) {
120662306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_set_max_pgt_set(
120762306a36Sopenharmony_ci			mbox, 1);
120862306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_max_pgt_set(
120962306a36Sopenharmony_ci			mbox, profile->max_pgt);
121062306a36Sopenharmony_ci	}
121162306a36Sopenharmony_ci	if (profile->used_max_system_port) {
121262306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_set_max_system_port_set(
121362306a36Sopenharmony_ci			mbox, 1);
121462306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_max_system_port_set(
121562306a36Sopenharmony_ci			mbox, profile->max_system_port);
121662306a36Sopenharmony_ci	}
121762306a36Sopenharmony_ci	if (profile->used_max_vlan_groups) {
121862306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_set_max_vlan_groups_set(
121962306a36Sopenharmony_ci			mbox, 1);
122062306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_max_vlan_groups_set(
122162306a36Sopenharmony_ci			mbox, profile->max_vlan_groups);
122262306a36Sopenharmony_ci	}
122362306a36Sopenharmony_ci	if (profile->used_max_regions) {
122462306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_set_max_regions_set(
122562306a36Sopenharmony_ci			mbox, 1);
122662306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_max_regions_set(
122762306a36Sopenharmony_ci			mbox, profile->max_regions);
122862306a36Sopenharmony_ci	}
122962306a36Sopenharmony_ci	if (profile->used_flood_tables) {
123062306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_set_flood_tables_set(
123162306a36Sopenharmony_ci			mbox, 1);
123262306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_max_flood_tables_set(
123362306a36Sopenharmony_ci			mbox, profile->max_flood_tables);
123462306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_max_vid_flood_tables_set(
123562306a36Sopenharmony_ci			mbox, profile->max_vid_flood_tables);
123662306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_max_fid_offset_flood_tables_set(
123762306a36Sopenharmony_ci			mbox, profile->max_fid_offset_flood_tables);
123862306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_fid_offset_flood_table_size_set(
123962306a36Sopenharmony_ci			mbox, profile->fid_offset_flood_table_size);
124062306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_max_fid_flood_tables_set(
124162306a36Sopenharmony_ci			mbox, profile->max_fid_flood_tables);
124262306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_fid_flood_table_size_set(
124362306a36Sopenharmony_ci			mbox, profile->fid_flood_table_size);
124462306a36Sopenharmony_ci	}
124562306a36Sopenharmony_ci	if (profile->used_flood_mode) {
124662306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_set_flood_mode_set(
124762306a36Sopenharmony_ci			mbox, 1);
124862306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_flood_mode_set(
124962306a36Sopenharmony_ci			mbox, profile->flood_mode);
125062306a36Sopenharmony_ci	}
125162306a36Sopenharmony_ci	if (profile->used_max_ib_mc) {
125262306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_set_max_ib_mc_set(
125362306a36Sopenharmony_ci			mbox, 1);
125462306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_max_ib_mc_set(
125562306a36Sopenharmony_ci			mbox, profile->max_ib_mc);
125662306a36Sopenharmony_ci	}
125762306a36Sopenharmony_ci	if (profile->used_max_pkey) {
125862306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_set_max_pkey_set(
125962306a36Sopenharmony_ci			mbox, 1);
126062306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_max_pkey_set(
126162306a36Sopenharmony_ci			mbox, profile->max_pkey);
126262306a36Sopenharmony_ci	}
126362306a36Sopenharmony_ci	if (profile->used_ar_sec) {
126462306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_set_ar_sec_set(
126562306a36Sopenharmony_ci			mbox, 1);
126662306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_ar_sec_set(
126762306a36Sopenharmony_ci			mbox, profile->ar_sec);
126862306a36Sopenharmony_ci	}
126962306a36Sopenharmony_ci	if (profile->used_adaptive_routing_group_cap) {
127062306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_set_adaptive_routing_group_cap_set(
127162306a36Sopenharmony_ci			mbox, 1);
127262306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_adaptive_routing_group_cap_set(
127362306a36Sopenharmony_ci			mbox, profile->adaptive_routing_group_cap);
127462306a36Sopenharmony_ci	}
127562306a36Sopenharmony_ci	if (profile->used_ubridge) {
127662306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_set_ubridge_set(mbox, 1);
127762306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_ubridge_set(mbox,
127862306a36Sopenharmony_ci							  profile->ubridge);
127962306a36Sopenharmony_ci	}
128062306a36Sopenharmony_ci	if (profile->used_kvd_sizes && MLXSW_RES_VALID(res, KVD_SIZE)) {
128162306a36Sopenharmony_ci		err = mlxsw_pci_profile_get_kvd_sizes(mlxsw_pci, profile, res);
128262306a36Sopenharmony_ci		if (err)
128362306a36Sopenharmony_ci			return err;
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_set_kvd_linear_size_set(mbox, 1);
128662306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_kvd_linear_size_set(mbox,
128762306a36Sopenharmony_ci					MLXSW_RES_GET(res, KVD_LINEAR_SIZE));
128862306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_set_kvd_hash_single_size_set(mbox,
128962306a36Sopenharmony_ci									   1);
129062306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_kvd_hash_single_size_set(mbox,
129162306a36Sopenharmony_ci					MLXSW_RES_GET(res, KVD_SINGLE_SIZE));
129262306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_set_kvd_hash_double_size_set(
129362306a36Sopenharmony_ci								mbox, 1);
129462306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_kvd_hash_double_size_set(mbox,
129562306a36Sopenharmony_ci					MLXSW_RES_GET(res, KVD_DOUBLE_SIZE));
129662306a36Sopenharmony_ci	}
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	for (i = 0; i < MLXSW_CONFIG_PROFILE_SWID_COUNT; i++)
129962306a36Sopenharmony_ci		mlxsw_pci_config_profile_swid_config(mlxsw_pci, mbox, i,
130062306a36Sopenharmony_ci						     &profile->swid_config[i]);
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	if (mlxsw_pci->max_cqe_ver > MLXSW_PCI_CQE_V0) {
130362306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_set_cqe_version_set(mbox, 1);
130462306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_cqe_version_set(mbox, 1);
130562306a36Sopenharmony_ci	}
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	if (profile->used_cqe_time_stamp_type) {
130862306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_set_cqe_time_stamp_type_set(mbox,
130962306a36Sopenharmony_ci									  1);
131062306a36Sopenharmony_ci		mlxsw_cmd_mbox_config_profile_cqe_time_stamp_type_set(mbox,
131162306a36Sopenharmony_ci					profile->cqe_time_stamp_type);
131262306a36Sopenharmony_ci	}
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	return mlxsw_cmd_config_profile_set(mlxsw_pci->core, mbox);
131562306a36Sopenharmony_ci}
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_cistatic int mlxsw_pci_boardinfo(struct mlxsw_pci *mlxsw_pci, char *mbox)
131862306a36Sopenharmony_ci{
131962306a36Sopenharmony_ci	struct mlxsw_bus_info *bus_info = &mlxsw_pci->bus_info;
132062306a36Sopenharmony_ci	int err;
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	mlxsw_cmd_mbox_zero(mbox);
132362306a36Sopenharmony_ci	err = mlxsw_cmd_boardinfo(mlxsw_pci->core, mbox);
132462306a36Sopenharmony_ci	if (err)
132562306a36Sopenharmony_ci		return err;
132662306a36Sopenharmony_ci	mlxsw_cmd_mbox_boardinfo_vsd_memcpy_from(mbox, bus_info->vsd);
132762306a36Sopenharmony_ci	mlxsw_cmd_mbox_boardinfo_psid_memcpy_from(mbox, bus_info->psid);
132862306a36Sopenharmony_ci	return 0;
132962306a36Sopenharmony_ci}
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_cistatic int mlxsw_pci_fw_area_init(struct mlxsw_pci *mlxsw_pci, char *mbox,
133262306a36Sopenharmony_ci				  u16 num_pages)
133362306a36Sopenharmony_ci{
133462306a36Sopenharmony_ci	struct mlxsw_pci_mem_item *mem_item;
133562306a36Sopenharmony_ci	int nent = 0;
133662306a36Sopenharmony_ci	int i;
133762306a36Sopenharmony_ci	int err;
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	mlxsw_pci->fw_area.items = kcalloc(num_pages, sizeof(*mem_item),
134062306a36Sopenharmony_ci					   GFP_KERNEL);
134162306a36Sopenharmony_ci	if (!mlxsw_pci->fw_area.items)
134262306a36Sopenharmony_ci		return -ENOMEM;
134362306a36Sopenharmony_ci	mlxsw_pci->fw_area.count = num_pages;
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	mlxsw_cmd_mbox_zero(mbox);
134662306a36Sopenharmony_ci	for (i = 0; i < num_pages; i++) {
134762306a36Sopenharmony_ci		mem_item = &mlxsw_pci->fw_area.items[i];
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci		mem_item->size = MLXSW_PCI_PAGE_SIZE;
135062306a36Sopenharmony_ci		mem_item->buf = dma_alloc_coherent(&mlxsw_pci->pdev->dev,
135162306a36Sopenharmony_ci						   mem_item->size,
135262306a36Sopenharmony_ci						   &mem_item->mapaddr, GFP_KERNEL);
135362306a36Sopenharmony_ci		if (!mem_item->buf) {
135462306a36Sopenharmony_ci			err = -ENOMEM;
135562306a36Sopenharmony_ci			goto err_alloc;
135662306a36Sopenharmony_ci		}
135762306a36Sopenharmony_ci		mlxsw_cmd_mbox_map_fa_pa_set(mbox, nent, mem_item->mapaddr);
135862306a36Sopenharmony_ci		mlxsw_cmd_mbox_map_fa_log2size_set(mbox, nent, 0); /* 1 page */
135962306a36Sopenharmony_ci		if (++nent == MLXSW_CMD_MAP_FA_VPM_ENTRIES_MAX) {
136062306a36Sopenharmony_ci			err = mlxsw_cmd_map_fa(mlxsw_pci->core, mbox, nent);
136162306a36Sopenharmony_ci			if (err)
136262306a36Sopenharmony_ci				goto err_cmd_map_fa;
136362306a36Sopenharmony_ci			nent = 0;
136462306a36Sopenharmony_ci			mlxsw_cmd_mbox_zero(mbox);
136562306a36Sopenharmony_ci		}
136662306a36Sopenharmony_ci	}
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	if (nent) {
136962306a36Sopenharmony_ci		err = mlxsw_cmd_map_fa(mlxsw_pci->core, mbox, nent);
137062306a36Sopenharmony_ci		if (err)
137162306a36Sopenharmony_ci			goto err_cmd_map_fa;
137262306a36Sopenharmony_ci	}
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	return 0;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_cierr_cmd_map_fa:
137762306a36Sopenharmony_cierr_alloc:
137862306a36Sopenharmony_ci	for (i--; i >= 0; i--) {
137962306a36Sopenharmony_ci		mem_item = &mlxsw_pci->fw_area.items[i];
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci		dma_free_coherent(&mlxsw_pci->pdev->dev, mem_item->size,
138262306a36Sopenharmony_ci				  mem_item->buf, mem_item->mapaddr);
138362306a36Sopenharmony_ci	}
138462306a36Sopenharmony_ci	kfree(mlxsw_pci->fw_area.items);
138562306a36Sopenharmony_ci	return err;
138662306a36Sopenharmony_ci}
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_cistatic void mlxsw_pci_fw_area_fini(struct mlxsw_pci *mlxsw_pci)
138962306a36Sopenharmony_ci{
139062306a36Sopenharmony_ci	struct mlxsw_pci_mem_item *mem_item;
139162306a36Sopenharmony_ci	int i;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	mlxsw_cmd_unmap_fa(mlxsw_pci->core);
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	for (i = 0; i < mlxsw_pci->fw_area.count; i++) {
139662306a36Sopenharmony_ci		mem_item = &mlxsw_pci->fw_area.items[i];
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci		dma_free_coherent(&mlxsw_pci->pdev->dev, mem_item->size,
139962306a36Sopenharmony_ci				  mem_item->buf, mem_item->mapaddr);
140062306a36Sopenharmony_ci	}
140162306a36Sopenharmony_ci	kfree(mlxsw_pci->fw_area.items);
140262306a36Sopenharmony_ci}
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_cistatic irqreturn_t mlxsw_pci_eq_irq_handler(int irq, void *dev_id)
140562306a36Sopenharmony_ci{
140662306a36Sopenharmony_ci	struct mlxsw_pci *mlxsw_pci = dev_id;
140762306a36Sopenharmony_ci	struct mlxsw_pci_queue *q;
140862306a36Sopenharmony_ci	int i;
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	for (i = 0; i < MLXSW_PCI_EQS_COUNT; i++) {
141162306a36Sopenharmony_ci		q = mlxsw_pci_eq_get(mlxsw_pci, i);
141262306a36Sopenharmony_ci		mlxsw_pci_queue_tasklet_schedule(q);
141362306a36Sopenharmony_ci	}
141462306a36Sopenharmony_ci	return IRQ_HANDLED;
141562306a36Sopenharmony_ci}
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_cistatic int mlxsw_pci_mbox_alloc(struct mlxsw_pci *mlxsw_pci,
141862306a36Sopenharmony_ci				struct mlxsw_pci_mem_item *mbox)
141962306a36Sopenharmony_ci{
142062306a36Sopenharmony_ci	struct pci_dev *pdev = mlxsw_pci->pdev;
142162306a36Sopenharmony_ci	int err = 0;
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	mbox->size = MLXSW_CMD_MBOX_SIZE;
142462306a36Sopenharmony_ci	mbox->buf = dma_alloc_coherent(&pdev->dev, MLXSW_CMD_MBOX_SIZE,
142562306a36Sopenharmony_ci				       &mbox->mapaddr, GFP_KERNEL);
142662306a36Sopenharmony_ci	if (!mbox->buf) {
142762306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed allocating memory for mailbox\n");
142862306a36Sopenharmony_ci		err = -ENOMEM;
142962306a36Sopenharmony_ci	}
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	return err;
143262306a36Sopenharmony_ci}
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_cistatic void mlxsw_pci_mbox_free(struct mlxsw_pci *mlxsw_pci,
143562306a36Sopenharmony_ci				struct mlxsw_pci_mem_item *mbox)
143662306a36Sopenharmony_ci{
143762306a36Sopenharmony_ci	struct pci_dev *pdev = mlxsw_pci->pdev;
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	dma_free_coherent(&pdev->dev, MLXSW_CMD_MBOX_SIZE, mbox->buf,
144062306a36Sopenharmony_ci			  mbox->mapaddr);
144162306a36Sopenharmony_ci}
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_cistatic int mlxsw_pci_sys_ready_wait(struct mlxsw_pci *mlxsw_pci,
144462306a36Sopenharmony_ci				    const struct pci_device_id *id,
144562306a36Sopenharmony_ci				    u32 *p_sys_status)
144662306a36Sopenharmony_ci{
144762306a36Sopenharmony_ci	unsigned long end;
144862306a36Sopenharmony_ci	u32 val;
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	/* We must wait for the HW to become responsive. */
145162306a36Sopenharmony_ci	msleep(MLXSW_PCI_SW_RESET_WAIT_MSECS);
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	end = jiffies + msecs_to_jiffies(MLXSW_PCI_SW_RESET_TIMEOUT_MSECS);
145462306a36Sopenharmony_ci	do {
145562306a36Sopenharmony_ci		val = mlxsw_pci_read32(mlxsw_pci, FW_READY);
145662306a36Sopenharmony_ci		if ((val & MLXSW_PCI_FW_READY_MASK) == MLXSW_PCI_FW_READY_MAGIC)
145762306a36Sopenharmony_ci			return 0;
145862306a36Sopenharmony_ci		cond_resched();
145962306a36Sopenharmony_ci	} while (time_before(jiffies, end));
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	*p_sys_status = val & MLXSW_PCI_FW_READY_MASK;
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	return -EBUSY;
146462306a36Sopenharmony_ci}
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_cistatic int mlxsw_pci_sw_reset(struct mlxsw_pci *mlxsw_pci,
146762306a36Sopenharmony_ci			      const struct pci_device_id *id)
146862306a36Sopenharmony_ci{
146962306a36Sopenharmony_ci	struct pci_dev *pdev = mlxsw_pci->pdev;
147062306a36Sopenharmony_ci	char mrsr_pl[MLXSW_REG_MRSR_LEN];
147162306a36Sopenharmony_ci	u32 sys_status;
147262306a36Sopenharmony_ci	int err;
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci	err = mlxsw_pci_sys_ready_wait(mlxsw_pci, id, &sys_status);
147562306a36Sopenharmony_ci	if (err) {
147662306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to reach system ready status before reset. Status is 0x%x\n",
147762306a36Sopenharmony_ci			sys_status);
147862306a36Sopenharmony_ci		return err;
147962306a36Sopenharmony_ci	}
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	mlxsw_reg_mrsr_pack(mrsr_pl);
148262306a36Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_pci->core, MLXSW_REG(mrsr), mrsr_pl);
148362306a36Sopenharmony_ci	if (err)
148462306a36Sopenharmony_ci		return err;
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci	err = mlxsw_pci_sys_ready_wait(mlxsw_pci, id, &sys_status);
148762306a36Sopenharmony_ci	if (err) {
148862306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to reach system ready status after reset. Status is 0x%x\n",
148962306a36Sopenharmony_ci			sys_status);
149062306a36Sopenharmony_ci		return err;
149162306a36Sopenharmony_ci	}
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	return 0;
149462306a36Sopenharmony_ci}
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_cistatic int mlxsw_pci_alloc_irq_vectors(struct mlxsw_pci *mlxsw_pci)
149762306a36Sopenharmony_ci{
149862306a36Sopenharmony_ci	int err;
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	err = pci_alloc_irq_vectors(mlxsw_pci->pdev, 1, 1, PCI_IRQ_MSIX);
150162306a36Sopenharmony_ci	if (err < 0)
150262306a36Sopenharmony_ci		dev_err(&mlxsw_pci->pdev->dev, "MSI-X init failed\n");
150362306a36Sopenharmony_ci	return err;
150462306a36Sopenharmony_ci}
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_cistatic void mlxsw_pci_free_irq_vectors(struct mlxsw_pci *mlxsw_pci)
150762306a36Sopenharmony_ci{
150862306a36Sopenharmony_ci	pci_free_irq_vectors(mlxsw_pci->pdev);
150962306a36Sopenharmony_ci}
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_cistatic int mlxsw_pci_init(void *bus_priv, struct mlxsw_core *mlxsw_core,
151262306a36Sopenharmony_ci			  const struct mlxsw_config_profile *profile,
151362306a36Sopenharmony_ci			  struct mlxsw_res *res)
151462306a36Sopenharmony_ci{
151562306a36Sopenharmony_ci	struct mlxsw_pci *mlxsw_pci = bus_priv;
151662306a36Sopenharmony_ci	struct pci_dev *pdev = mlxsw_pci->pdev;
151762306a36Sopenharmony_ci	char *mbox;
151862306a36Sopenharmony_ci	u16 num_pages;
151962306a36Sopenharmony_ci	int err;
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci	mlxsw_pci->core = mlxsw_core;
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	mbox = mlxsw_cmd_mbox_alloc();
152462306a36Sopenharmony_ci	if (!mbox)
152562306a36Sopenharmony_ci		return -ENOMEM;
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci	err = mlxsw_pci_sw_reset(mlxsw_pci, mlxsw_pci->id);
152862306a36Sopenharmony_ci	if (err)
152962306a36Sopenharmony_ci		goto err_sw_reset;
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	err = mlxsw_pci_alloc_irq_vectors(mlxsw_pci);
153262306a36Sopenharmony_ci	if (err < 0) {
153362306a36Sopenharmony_ci		dev_err(&pdev->dev, "MSI-X init failed\n");
153462306a36Sopenharmony_ci		goto err_alloc_irq;
153562306a36Sopenharmony_ci	}
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	err = mlxsw_cmd_query_fw(mlxsw_core, mbox);
153862306a36Sopenharmony_ci	if (err)
153962306a36Sopenharmony_ci		goto err_query_fw;
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	mlxsw_pci->bus_info.fw_rev.major =
154262306a36Sopenharmony_ci		mlxsw_cmd_mbox_query_fw_fw_rev_major_get(mbox);
154362306a36Sopenharmony_ci	mlxsw_pci->bus_info.fw_rev.minor =
154462306a36Sopenharmony_ci		mlxsw_cmd_mbox_query_fw_fw_rev_minor_get(mbox);
154562306a36Sopenharmony_ci	mlxsw_pci->bus_info.fw_rev.subminor =
154662306a36Sopenharmony_ci		mlxsw_cmd_mbox_query_fw_fw_rev_subminor_get(mbox);
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	if (mlxsw_cmd_mbox_query_fw_cmd_interface_rev_get(mbox) != 1) {
154962306a36Sopenharmony_ci		dev_err(&pdev->dev, "Unsupported cmd interface revision ID queried from hw\n");
155062306a36Sopenharmony_ci		err = -EINVAL;
155162306a36Sopenharmony_ci		goto err_iface_rev;
155262306a36Sopenharmony_ci	}
155362306a36Sopenharmony_ci	if (mlxsw_cmd_mbox_query_fw_doorbell_page_bar_get(mbox) != 0) {
155462306a36Sopenharmony_ci		dev_err(&pdev->dev, "Unsupported doorbell page bar queried from hw\n");
155562306a36Sopenharmony_ci		err = -EINVAL;
155662306a36Sopenharmony_ci		goto err_doorbell_page_bar;
155762306a36Sopenharmony_ci	}
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	mlxsw_pci->doorbell_offset =
156062306a36Sopenharmony_ci		mlxsw_cmd_mbox_query_fw_doorbell_page_offset_get(mbox);
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	if (mlxsw_cmd_mbox_query_fw_fr_rn_clk_bar_get(mbox) != 0) {
156362306a36Sopenharmony_ci		dev_err(&pdev->dev, "Unsupported free running clock BAR queried from hw\n");
156462306a36Sopenharmony_ci		err = -EINVAL;
156562306a36Sopenharmony_ci		goto err_fr_rn_clk_bar;
156662306a36Sopenharmony_ci	}
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	mlxsw_pci->free_running_clock_offset =
156962306a36Sopenharmony_ci		mlxsw_cmd_mbox_query_fw_free_running_clock_offset_get(mbox);
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	if (mlxsw_cmd_mbox_query_fw_utc_sec_bar_get(mbox) != 0) {
157262306a36Sopenharmony_ci		dev_err(&pdev->dev, "Unsupported UTC sec BAR queried from hw\n");
157362306a36Sopenharmony_ci		err = -EINVAL;
157462306a36Sopenharmony_ci		goto err_utc_sec_bar;
157562306a36Sopenharmony_ci	}
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci	mlxsw_pci->utc_sec_offset =
157862306a36Sopenharmony_ci		mlxsw_cmd_mbox_query_fw_utc_sec_offset_get(mbox);
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci	if (mlxsw_cmd_mbox_query_fw_utc_nsec_bar_get(mbox) != 0) {
158162306a36Sopenharmony_ci		dev_err(&pdev->dev, "Unsupported UTC nsec BAR queried from hw\n");
158262306a36Sopenharmony_ci		err = -EINVAL;
158362306a36Sopenharmony_ci		goto err_utc_nsec_bar;
158462306a36Sopenharmony_ci	}
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci	mlxsw_pci->utc_nsec_offset =
158762306a36Sopenharmony_ci		mlxsw_cmd_mbox_query_fw_utc_nsec_offset_get(mbox);
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	num_pages = mlxsw_cmd_mbox_query_fw_fw_pages_get(mbox);
159062306a36Sopenharmony_ci	err = mlxsw_pci_fw_area_init(mlxsw_pci, mbox, num_pages);
159162306a36Sopenharmony_ci	if (err)
159262306a36Sopenharmony_ci		goto err_fw_area_init;
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci	err = mlxsw_pci_boardinfo(mlxsw_pci, mbox);
159562306a36Sopenharmony_ci	if (err)
159662306a36Sopenharmony_ci		goto err_boardinfo;
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci	err = mlxsw_core_resources_query(mlxsw_core, mbox, res);
159962306a36Sopenharmony_ci	if (err)
160062306a36Sopenharmony_ci		goto err_query_resources;
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	if (MLXSW_CORE_RES_VALID(mlxsw_core, CQE_V2) &&
160362306a36Sopenharmony_ci	    MLXSW_CORE_RES_GET(mlxsw_core, CQE_V2))
160462306a36Sopenharmony_ci		mlxsw_pci->max_cqe_ver = MLXSW_PCI_CQE_V2;
160562306a36Sopenharmony_ci	else if (MLXSW_CORE_RES_VALID(mlxsw_core, CQE_V1) &&
160662306a36Sopenharmony_ci		 MLXSW_CORE_RES_GET(mlxsw_core, CQE_V1))
160762306a36Sopenharmony_ci		mlxsw_pci->max_cqe_ver = MLXSW_PCI_CQE_V1;
160862306a36Sopenharmony_ci	else if ((MLXSW_CORE_RES_VALID(mlxsw_core, CQE_V0) &&
160962306a36Sopenharmony_ci		  MLXSW_CORE_RES_GET(mlxsw_core, CQE_V0)) ||
161062306a36Sopenharmony_ci		 !MLXSW_CORE_RES_VALID(mlxsw_core, CQE_V0)) {
161162306a36Sopenharmony_ci		mlxsw_pci->max_cqe_ver = MLXSW_PCI_CQE_V0;
161262306a36Sopenharmony_ci	} else {
161362306a36Sopenharmony_ci		dev_err(&pdev->dev, "Invalid supported CQE version combination reported\n");
161462306a36Sopenharmony_ci		goto err_cqe_v_check;
161562306a36Sopenharmony_ci	}
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci	err = mlxsw_pci_config_profile(mlxsw_pci, mbox, profile, res);
161862306a36Sopenharmony_ci	if (err)
161962306a36Sopenharmony_ci		goto err_config_profile;
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	/* Some resources depend on unified bridge model, which is configured
162262306a36Sopenharmony_ci	 * as part of config_profile. Query the resources again to get correct
162362306a36Sopenharmony_ci	 * values.
162462306a36Sopenharmony_ci	 */
162562306a36Sopenharmony_ci	err = mlxsw_core_resources_query(mlxsw_core, mbox, res);
162662306a36Sopenharmony_ci	if (err)
162762306a36Sopenharmony_ci		goto err_requery_resources;
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ci	err = mlxsw_pci_aqs_init(mlxsw_pci, mbox);
163062306a36Sopenharmony_ci	if (err)
163162306a36Sopenharmony_ci		goto err_aqs_init;
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	err = request_irq(pci_irq_vector(pdev, 0),
163462306a36Sopenharmony_ci			  mlxsw_pci_eq_irq_handler, 0,
163562306a36Sopenharmony_ci			  mlxsw_pci->bus_info.device_kind, mlxsw_pci);
163662306a36Sopenharmony_ci	if (err) {
163762306a36Sopenharmony_ci		dev_err(&pdev->dev, "IRQ request failed\n");
163862306a36Sopenharmony_ci		goto err_request_eq_irq;
163962306a36Sopenharmony_ci	}
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci	goto mbox_put;
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_cierr_request_eq_irq:
164462306a36Sopenharmony_ci	mlxsw_pci_aqs_fini(mlxsw_pci);
164562306a36Sopenharmony_cierr_aqs_init:
164662306a36Sopenharmony_cierr_requery_resources:
164762306a36Sopenharmony_cierr_config_profile:
164862306a36Sopenharmony_cierr_cqe_v_check:
164962306a36Sopenharmony_cierr_query_resources:
165062306a36Sopenharmony_cierr_boardinfo:
165162306a36Sopenharmony_ci	mlxsw_pci_fw_area_fini(mlxsw_pci);
165262306a36Sopenharmony_cierr_fw_area_init:
165362306a36Sopenharmony_cierr_utc_nsec_bar:
165462306a36Sopenharmony_cierr_utc_sec_bar:
165562306a36Sopenharmony_cierr_fr_rn_clk_bar:
165662306a36Sopenharmony_cierr_doorbell_page_bar:
165762306a36Sopenharmony_cierr_iface_rev:
165862306a36Sopenharmony_cierr_query_fw:
165962306a36Sopenharmony_ci	mlxsw_pci_free_irq_vectors(mlxsw_pci);
166062306a36Sopenharmony_cierr_alloc_irq:
166162306a36Sopenharmony_cierr_sw_reset:
166262306a36Sopenharmony_cimbox_put:
166362306a36Sopenharmony_ci	mlxsw_cmd_mbox_free(mbox);
166462306a36Sopenharmony_ci	return err;
166562306a36Sopenharmony_ci}
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_cistatic void mlxsw_pci_fini(void *bus_priv)
166862306a36Sopenharmony_ci{
166962306a36Sopenharmony_ci	struct mlxsw_pci *mlxsw_pci = bus_priv;
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci	free_irq(pci_irq_vector(mlxsw_pci->pdev, 0), mlxsw_pci);
167262306a36Sopenharmony_ci	mlxsw_pci_aqs_fini(mlxsw_pci);
167362306a36Sopenharmony_ci	mlxsw_pci_fw_area_fini(mlxsw_pci);
167462306a36Sopenharmony_ci	mlxsw_pci_free_irq_vectors(mlxsw_pci);
167562306a36Sopenharmony_ci}
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_cistatic struct mlxsw_pci_queue *
167862306a36Sopenharmony_cimlxsw_pci_sdq_pick(struct mlxsw_pci *mlxsw_pci,
167962306a36Sopenharmony_ci		   const struct mlxsw_tx_info *tx_info)
168062306a36Sopenharmony_ci{
168162306a36Sopenharmony_ci	u8 ctl_sdq_count = mlxsw_pci_sdq_count(mlxsw_pci) - 1;
168262306a36Sopenharmony_ci	u8 sdqn;
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci	if (tx_info->is_emad) {
168562306a36Sopenharmony_ci		sdqn = MLXSW_PCI_SDQ_EMAD_INDEX;
168662306a36Sopenharmony_ci	} else {
168762306a36Sopenharmony_ci		BUILD_BUG_ON(MLXSW_PCI_SDQ_EMAD_INDEX != 0);
168862306a36Sopenharmony_ci		sdqn = 1 + (tx_info->local_port % ctl_sdq_count);
168962306a36Sopenharmony_ci	}
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_ci	return mlxsw_pci_sdq_get(mlxsw_pci, sdqn);
169262306a36Sopenharmony_ci}
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_cistatic bool mlxsw_pci_skb_transmit_busy(void *bus_priv,
169562306a36Sopenharmony_ci					const struct mlxsw_tx_info *tx_info)
169662306a36Sopenharmony_ci{
169762306a36Sopenharmony_ci	struct mlxsw_pci *mlxsw_pci = bus_priv;
169862306a36Sopenharmony_ci	struct mlxsw_pci_queue *q = mlxsw_pci_sdq_pick(mlxsw_pci, tx_info);
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci	return !mlxsw_pci_queue_elem_info_producer_get(q);
170162306a36Sopenharmony_ci}
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_cistatic int mlxsw_pci_skb_transmit(void *bus_priv, struct sk_buff *skb,
170462306a36Sopenharmony_ci				  const struct mlxsw_tx_info *tx_info)
170562306a36Sopenharmony_ci{
170662306a36Sopenharmony_ci	struct mlxsw_pci *mlxsw_pci = bus_priv;
170762306a36Sopenharmony_ci	struct mlxsw_pci_queue *q;
170862306a36Sopenharmony_ci	struct mlxsw_pci_queue_elem_info *elem_info;
170962306a36Sopenharmony_ci	char *wqe;
171062306a36Sopenharmony_ci	int i;
171162306a36Sopenharmony_ci	int err;
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	if (skb_shinfo(skb)->nr_frags > MLXSW_PCI_WQE_SG_ENTRIES - 1) {
171462306a36Sopenharmony_ci		err = skb_linearize(skb);
171562306a36Sopenharmony_ci		if (err)
171662306a36Sopenharmony_ci			return err;
171762306a36Sopenharmony_ci	}
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ci	q = mlxsw_pci_sdq_pick(mlxsw_pci, tx_info);
172062306a36Sopenharmony_ci	spin_lock_bh(&q->lock);
172162306a36Sopenharmony_ci	elem_info = mlxsw_pci_queue_elem_info_producer_get(q);
172262306a36Sopenharmony_ci	if (!elem_info) {
172362306a36Sopenharmony_ci		/* queue is full */
172462306a36Sopenharmony_ci		err = -EAGAIN;
172562306a36Sopenharmony_ci		goto unlock;
172662306a36Sopenharmony_ci	}
172762306a36Sopenharmony_ci	mlxsw_skb_cb(skb)->tx_info = *tx_info;
172862306a36Sopenharmony_ci	elem_info->u.sdq.skb = skb;
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci	wqe = elem_info->elem;
173162306a36Sopenharmony_ci	mlxsw_pci_wqe_c_set(wqe, 1); /* always report completion */
173262306a36Sopenharmony_ci	mlxsw_pci_wqe_lp_set(wqe, 0);
173362306a36Sopenharmony_ci	mlxsw_pci_wqe_type_set(wqe, MLXSW_PCI_WQE_TYPE_ETHERNET);
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci	err = mlxsw_pci_wqe_frag_map(mlxsw_pci, wqe, 0, skb->data,
173662306a36Sopenharmony_ci				     skb_headlen(skb), DMA_TO_DEVICE);
173762306a36Sopenharmony_ci	if (err)
173862306a36Sopenharmony_ci		goto unlock;
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
174162306a36Sopenharmony_ci		const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci		err = mlxsw_pci_wqe_frag_map(mlxsw_pci, wqe, i + 1,
174462306a36Sopenharmony_ci					     skb_frag_address(frag),
174562306a36Sopenharmony_ci					     skb_frag_size(frag),
174662306a36Sopenharmony_ci					     DMA_TO_DEVICE);
174762306a36Sopenharmony_ci		if (err)
174862306a36Sopenharmony_ci			goto unmap_frags;
174962306a36Sopenharmony_ci	}
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci	if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))
175262306a36Sopenharmony_ci		skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci	/* Set unused sq entries byte count to zero. */
175562306a36Sopenharmony_ci	for (i++; i < MLXSW_PCI_WQE_SG_ENTRIES; i++)
175662306a36Sopenharmony_ci		mlxsw_pci_wqe_byte_count_set(wqe, i, 0);
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	/* Everything is set up, ring producer doorbell to get HW going */
175962306a36Sopenharmony_ci	q->producer_counter++;
176062306a36Sopenharmony_ci	mlxsw_pci_queue_doorbell_producer_ring(mlxsw_pci, q);
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci	goto unlock;
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ciunmap_frags:
176562306a36Sopenharmony_ci	for (; i >= 0; i--)
176662306a36Sopenharmony_ci		mlxsw_pci_wqe_frag_unmap(mlxsw_pci, wqe, i, DMA_TO_DEVICE);
176762306a36Sopenharmony_ciunlock:
176862306a36Sopenharmony_ci	spin_unlock_bh(&q->lock);
176962306a36Sopenharmony_ci	return err;
177062306a36Sopenharmony_ci}
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_cistatic int mlxsw_pci_cmd_exec(void *bus_priv, u16 opcode, u8 opcode_mod,
177362306a36Sopenharmony_ci			      u32 in_mod, bool out_mbox_direct,
177462306a36Sopenharmony_ci			      char *in_mbox, size_t in_mbox_size,
177562306a36Sopenharmony_ci			      char *out_mbox, size_t out_mbox_size,
177662306a36Sopenharmony_ci			      u8 *p_status)
177762306a36Sopenharmony_ci{
177862306a36Sopenharmony_ci	struct mlxsw_pci *mlxsw_pci = bus_priv;
177962306a36Sopenharmony_ci	dma_addr_t in_mapaddr = 0, out_mapaddr = 0;
178062306a36Sopenharmony_ci	bool evreq = mlxsw_pci->cmd.nopoll;
178162306a36Sopenharmony_ci	unsigned long timeout = msecs_to_jiffies(MLXSW_PCI_CIR_TIMEOUT_MSECS);
178262306a36Sopenharmony_ci	bool *p_wait_done = &mlxsw_pci->cmd.wait_done;
178362306a36Sopenharmony_ci	int err;
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci	*p_status = MLXSW_CMD_STATUS_OK;
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_ci	err = mutex_lock_interruptible(&mlxsw_pci->cmd.lock);
178862306a36Sopenharmony_ci	if (err)
178962306a36Sopenharmony_ci		return err;
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_ci	if (in_mbox) {
179262306a36Sopenharmony_ci		memcpy(mlxsw_pci->cmd.in_mbox.buf, in_mbox, in_mbox_size);
179362306a36Sopenharmony_ci		in_mapaddr = mlxsw_pci->cmd.in_mbox.mapaddr;
179462306a36Sopenharmony_ci	}
179562306a36Sopenharmony_ci	mlxsw_pci_write32(mlxsw_pci, CIR_IN_PARAM_HI, upper_32_bits(in_mapaddr));
179662306a36Sopenharmony_ci	mlxsw_pci_write32(mlxsw_pci, CIR_IN_PARAM_LO, lower_32_bits(in_mapaddr));
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci	if (out_mbox)
179962306a36Sopenharmony_ci		out_mapaddr = mlxsw_pci->cmd.out_mbox.mapaddr;
180062306a36Sopenharmony_ci	mlxsw_pci_write32(mlxsw_pci, CIR_OUT_PARAM_HI, upper_32_bits(out_mapaddr));
180162306a36Sopenharmony_ci	mlxsw_pci_write32(mlxsw_pci, CIR_OUT_PARAM_LO, lower_32_bits(out_mapaddr));
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci	mlxsw_pci_write32(mlxsw_pci, CIR_IN_MODIFIER, in_mod);
180462306a36Sopenharmony_ci	mlxsw_pci_write32(mlxsw_pci, CIR_TOKEN, 0);
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_ci	*p_wait_done = false;
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	wmb(); /* all needs to be written before we write control register */
180962306a36Sopenharmony_ci	mlxsw_pci_write32(mlxsw_pci, CIR_CTRL,
181062306a36Sopenharmony_ci			  MLXSW_PCI_CIR_CTRL_GO_BIT |
181162306a36Sopenharmony_ci			  (evreq ? MLXSW_PCI_CIR_CTRL_EVREQ_BIT : 0) |
181262306a36Sopenharmony_ci			  (opcode_mod << MLXSW_PCI_CIR_CTRL_OPCODE_MOD_SHIFT) |
181362306a36Sopenharmony_ci			  opcode);
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ci	if (!evreq) {
181662306a36Sopenharmony_ci		unsigned long end;
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci		end = jiffies + timeout;
181962306a36Sopenharmony_ci		do {
182062306a36Sopenharmony_ci			u32 ctrl = mlxsw_pci_read32(mlxsw_pci, CIR_CTRL);
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci			if (!(ctrl & MLXSW_PCI_CIR_CTRL_GO_BIT)) {
182362306a36Sopenharmony_ci				*p_wait_done = true;
182462306a36Sopenharmony_ci				*p_status = ctrl >> MLXSW_PCI_CIR_CTRL_STATUS_SHIFT;
182562306a36Sopenharmony_ci				break;
182662306a36Sopenharmony_ci			}
182762306a36Sopenharmony_ci			cond_resched();
182862306a36Sopenharmony_ci		} while (time_before(jiffies, end));
182962306a36Sopenharmony_ci	} else {
183062306a36Sopenharmony_ci		wait_event_timeout(mlxsw_pci->cmd.wait, *p_wait_done, timeout);
183162306a36Sopenharmony_ci		*p_status = mlxsw_pci->cmd.comp.status;
183262306a36Sopenharmony_ci	}
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci	err = 0;
183562306a36Sopenharmony_ci	if (*p_wait_done) {
183662306a36Sopenharmony_ci		if (*p_status)
183762306a36Sopenharmony_ci			err = -EIO;
183862306a36Sopenharmony_ci	} else {
183962306a36Sopenharmony_ci		err = -ETIMEDOUT;
184062306a36Sopenharmony_ci	}
184162306a36Sopenharmony_ci
184262306a36Sopenharmony_ci	if (!err && out_mbox && out_mbox_direct) {
184362306a36Sopenharmony_ci		/* Some commands don't use output param as address to mailbox
184462306a36Sopenharmony_ci		 * but they store output directly into registers. In that case,
184562306a36Sopenharmony_ci		 * copy registers into mbox buffer.
184662306a36Sopenharmony_ci		 */
184762306a36Sopenharmony_ci		__be32 tmp;
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci		if (!evreq) {
185062306a36Sopenharmony_ci			tmp = cpu_to_be32(mlxsw_pci_read32(mlxsw_pci,
185162306a36Sopenharmony_ci							   CIR_OUT_PARAM_HI));
185262306a36Sopenharmony_ci			memcpy(out_mbox, &tmp, sizeof(tmp));
185362306a36Sopenharmony_ci			tmp = cpu_to_be32(mlxsw_pci_read32(mlxsw_pci,
185462306a36Sopenharmony_ci							   CIR_OUT_PARAM_LO));
185562306a36Sopenharmony_ci			memcpy(out_mbox + sizeof(tmp), &tmp, sizeof(tmp));
185662306a36Sopenharmony_ci		}
185762306a36Sopenharmony_ci	} else if (!err && out_mbox) {
185862306a36Sopenharmony_ci		memcpy(out_mbox, mlxsw_pci->cmd.out_mbox.buf, out_mbox_size);
185962306a36Sopenharmony_ci	}
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_ci	mutex_unlock(&mlxsw_pci->cmd.lock);
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci	return err;
186462306a36Sopenharmony_ci}
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_cistatic u32 mlxsw_pci_read_frc_h(void *bus_priv)
186762306a36Sopenharmony_ci{
186862306a36Sopenharmony_ci	struct mlxsw_pci *mlxsw_pci = bus_priv;
186962306a36Sopenharmony_ci	u64 frc_offset_h;
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci	frc_offset_h = mlxsw_pci->free_running_clock_offset;
187262306a36Sopenharmony_ci	return mlxsw_pci_read32_off(mlxsw_pci, frc_offset_h);
187362306a36Sopenharmony_ci}
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_cistatic u32 mlxsw_pci_read_frc_l(void *bus_priv)
187662306a36Sopenharmony_ci{
187762306a36Sopenharmony_ci	struct mlxsw_pci *mlxsw_pci = bus_priv;
187862306a36Sopenharmony_ci	u64 frc_offset_l;
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci	frc_offset_l = mlxsw_pci->free_running_clock_offset + 4;
188162306a36Sopenharmony_ci	return mlxsw_pci_read32_off(mlxsw_pci, frc_offset_l);
188262306a36Sopenharmony_ci}
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_cistatic u32 mlxsw_pci_read_utc_sec(void *bus_priv)
188562306a36Sopenharmony_ci{
188662306a36Sopenharmony_ci	struct mlxsw_pci *mlxsw_pci = bus_priv;
188762306a36Sopenharmony_ci
188862306a36Sopenharmony_ci	return mlxsw_pci_read32_off(mlxsw_pci, mlxsw_pci->utc_sec_offset);
188962306a36Sopenharmony_ci}
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_cistatic u32 mlxsw_pci_read_utc_nsec(void *bus_priv)
189262306a36Sopenharmony_ci{
189362306a36Sopenharmony_ci	struct mlxsw_pci *mlxsw_pci = bus_priv;
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_ci	return mlxsw_pci_read32_off(mlxsw_pci, mlxsw_pci->utc_nsec_offset);
189662306a36Sopenharmony_ci}
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_cistatic const struct mlxsw_bus mlxsw_pci_bus = {
189962306a36Sopenharmony_ci	.kind			= "pci",
190062306a36Sopenharmony_ci	.init			= mlxsw_pci_init,
190162306a36Sopenharmony_ci	.fini			= mlxsw_pci_fini,
190262306a36Sopenharmony_ci	.skb_transmit_busy	= mlxsw_pci_skb_transmit_busy,
190362306a36Sopenharmony_ci	.skb_transmit		= mlxsw_pci_skb_transmit,
190462306a36Sopenharmony_ci	.cmd_exec		= mlxsw_pci_cmd_exec,
190562306a36Sopenharmony_ci	.read_frc_h		= mlxsw_pci_read_frc_h,
190662306a36Sopenharmony_ci	.read_frc_l		= mlxsw_pci_read_frc_l,
190762306a36Sopenharmony_ci	.read_utc_sec		= mlxsw_pci_read_utc_sec,
190862306a36Sopenharmony_ci	.read_utc_nsec		= mlxsw_pci_read_utc_nsec,
190962306a36Sopenharmony_ci	.features		= MLXSW_BUS_F_TXRX | MLXSW_BUS_F_RESET,
191062306a36Sopenharmony_ci};
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_cistatic int mlxsw_pci_cmd_init(struct mlxsw_pci *mlxsw_pci)
191362306a36Sopenharmony_ci{
191462306a36Sopenharmony_ci	int err;
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_ci	mutex_init(&mlxsw_pci->cmd.lock);
191762306a36Sopenharmony_ci	init_waitqueue_head(&mlxsw_pci->cmd.wait);
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci	err = mlxsw_pci_mbox_alloc(mlxsw_pci, &mlxsw_pci->cmd.in_mbox);
192062306a36Sopenharmony_ci	if (err)
192162306a36Sopenharmony_ci		goto err_in_mbox_alloc;
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_ci	err = mlxsw_pci_mbox_alloc(mlxsw_pci, &mlxsw_pci->cmd.out_mbox);
192462306a36Sopenharmony_ci	if (err)
192562306a36Sopenharmony_ci		goto err_out_mbox_alloc;
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci	return 0;
192862306a36Sopenharmony_ci
192962306a36Sopenharmony_cierr_out_mbox_alloc:
193062306a36Sopenharmony_ci	mlxsw_pci_mbox_free(mlxsw_pci, &mlxsw_pci->cmd.in_mbox);
193162306a36Sopenharmony_cierr_in_mbox_alloc:
193262306a36Sopenharmony_ci	mutex_destroy(&mlxsw_pci->cmd.lock);
193362306a36Sopenharmony_ci	return err;
193462306a36Sopenharmony_ci}
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_cistatic void mlxsw_pci_cmd_fini(struct mlxsw_pci *mlxsw_pci)
193762306a36Sopenharmony_ci{
193862306a36Sopenharmony_ci	mlxsw_pci_mbox_free(mlxsw_pci, &mlxsw_pci->cmd.out_mbox);
193962306a36Sopenharmony_ci	mlxsw_pci_mbox_free(mlxsw_pci, &mlxsw_pci->cmd.in_mbox);
194062306a36Sopenharmony_ci	mutex_destroy(&mlxsw_pci->cmd.lock);
194162306a36Sopenharmony_ci}
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_cistatic int mlxsw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
194462306a36Sopenharmony_ci{
194562306a36Sopenharmony_ci	const char *driver_name = dev_driver_string(&pdev->dev);
194662306a36Sopenharmony_ci	struct mlxsw_pci *mlxsw_pci;
194762306a36Sopenharmony_ci	int err;
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci	mlxsw_pci = kzalloc(sizeof(*mlxsw_pci), GFP_KERNEL);
195062306a36Sopenharmony_ci	if (!mlxsw_pci)
195162306a36Sopenharmony_ci		return -ENOMEM;
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci	err = pci_enable_device(pdev);
195462306a36Sopenharmony_ci	if (err) {
195562306a36Sopenharmony_ci		dev_err(&pdev->dev, "pci_enable_device failed\n");
195662306a36Sopenharmony_ci		goto err_pci_enable_device;
195762306a36Sopenharmony_ci	}
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci	err = pci_request_regions(pdev, driver_name);
196062306a36Sopenharmony_ci	if (err) {
196162306a36Sopenharmony_ci		dev_err(&pdev->dev, "pci_request_regions failed\n");
196262306a36Sopenharmony_ci		goto err_pci_request_regions;
196362306a36Sopenharmony_ci	}
196462306a36Sopenharmony_ci
196562306a36Sopenharmony_ci	err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
196662306a36Sopenharmony_ci	if (err) {
196762306a36Sopenharmony_ci		err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
196862306a36Sopenharmony_ci		if (err) {
196962306a36Sopenharmony_ci			dev_err(&pdev->dev, "dma_set_mask failed\n");
197062306a36Sopenharmony_ci			goto err_pci_set_dma_mask;
197162306a36Sopenharmony_ci		}
197262306a36Sopenharmony_ci	}
197362306a36Sopenharmony_ci
197462306a36Sopenharmony_ci	if (pci_resource_len(pdev, 0) < MLXSW_PCI_BAR0_SIZE) {
197562306a36Sopenharmony_ci		dev_err(&pdev->dev, "invalid PCI region size\n");
197662306a36Sopenharmony_ci		err = -EINVAL;
197762306a36Sopenharmony_ci		goto err_pci_resource_len_check;
197862306a36Sopenharmony_ci	}
197962306a36Sopenharmony_ci
198062306a36Sopenharmony_ci	mlxsw_pci->hw_addr = ioremap(pci_resource_start(pdev, 0),
198162306a36Sopenharmony_ci				     pci_resource_len(pdev, 0));
198262306a36Sopenharmony_ci	if (!mlxsw_pci->hw_addr) {
198362306a36Sopenharmony_ci		dev_err(&pdev->dev, "ioremap failed\n");
198462306a36Sopenharmony_ci		err = -EIO;
198562306a36Sopenharmony_ci		goto err_ioremap;
198662306a36Sopenharmony_ci	}
198762306a36Sopenharmony_ci	pci_set_master(pdev);
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_ci	mlxsw_pci->pdev = pdev;
199062306a36Sopenharmony_ci	pci_set_drvdata(pdev, mlxsw_pci);
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_ci	err = mlxsw_pci_cmd_init(mlxsw_pci);
199362306a36Sopenharmony_ci	if (err)
199462306a36Sopenharmony_ci		goto err_pci_cmd_init;
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	mlxsw_pci->bus_info.device_kind = driver_name;
199762306a36Sopenharmony_ci	mlxsw_pci->bus_info.device_name = pci_name(mlxsw_pci->pdev);
199862306a36Sopenharmony_ci	mlxsw_pci->bus_info.dev = &pdev->dev;
199962306a36Sopenharmony_ci	mlxsw_pci->bus_info.read_clock_capable = true;
200062306a36Sopenharmony_ci	mlxsw_pci->id = id;
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci	err = mlxsw_core_bus_device_register(&mlxsw_pci->bus_info,
200362306a36Sopenharmony_ci					     &mlxsw_pci_bus, mlxsw_pci, false,
200462306a36Sopenharmony_ci					     NULL, NULL);
200562306a36Sopenharmony_ci	if (err) {
200662306a36Sopenharmony_ci		dev_err(&pdev->dev, "cannot register bus device\n");
200762306a36Sopenharmony_ci		goto err_bus_device_register;
200862306a36Sopenharmony_ci	}
200962306a36Sopenharmony_ci
201062306a36Sopenharmony_ci	return 0;
201162306a36Sopenharmony_ci
201262306a36Sopenharmony_cierr_bus_device_register:
201362306a36Sopenharmony_ci	mlxsw_pci_cmd_fini(mlxsw_pci);
201462306a36Sopenharmony_cierr_pci_cmd_init:
201562306a36Sopenharmony_ci	iounmap(mlxsw_pci->hw_addr);
201662306a36Sopenharmony_cierr_ioremap:
201762306a36Sopenharmony_cierr_pci_resource_len_check:
201862306a36Sopenharmony_cierr_pci_set_dma_mask:
201962306a36Sopenharmony_ci	pci_release_regions(pdev);
202062306a36Sopenharmony_cierr_pci_request_regions:
202162306a36Sopenharmony_ci	pci_disable_device(pdev);
202262306a36Sopenharmony_cierr_pci_enable_device:
202362306a36Sopenharmony_ci	kfree(mlxsw_pci);
202462306a36Sopenharmony_ci	return err;
202562306a36Sopenharmony_ci}
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_cistatic void mlxsw_pci_remove(struct pci_dev *pdev)
202862306a36Sopenharmony_ci{
202962306a36Sopenharmony_ci	struct mlxsw_pci *mlxsw_pci = pci_get_drvdata(pdev);
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_ci	mlxsw_core_bus_device_unregister(mlxsw_pci->core, false);
203262306a36Sopenharmony_ci	mlxsw_pci_cmd_fini(mlxsw_pci);
203362306a36Sopenharmony_ci	iounmap(mlxsw_pci->hw_addr);
203462306a36Sopenharmony_ci	pci_release_regions(mlxsw_pci->pdev);
203562306a36Sopenharmony_ci	pci_disable_device(mlxsw_pci->pdev);
203662306a36Sopenharmony_ci	kfree(mlxsw_pci);
203762306a36Sopenharmony_ci}
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_ciint mlxsw_pci_driver_register(struct pci_driver *pci_driver)
204062306a36Sopenharmony_ci{
204162306a36Sopenharmony_ci	pci_driver->probe = mlxsw_pci_probe;
204262306a36Sopenharmony_ci	pci_driver->remove = mlxsw_pci_remove;
204362306a36Sopenharmony_ci	pci_driver->shutdown = mlxsw_pci_remove;
204462306a36Sopenharmony_ci	return pci_register_driver(pci_driver);
204562306a36Sopenharmony_ci}
204662306a36Sopenharmony_ciEXPORT_SYMBOL(mlxsw_pci_driver_register);
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_civoid mlxsw_pci_driver_unregister(struct pci_driver *pci_driver)
204962306a36Sopenharmony_ci{
205062306a36Sopenharmony_ci	pci_unregister_driver(pci_driver);
205162306a36Sopenharmony_ci}
205262306a36Sopenharmony_ciEXPORT_SYMBOL(mlxsw_pci_driver_unregister);
205362306a36Sopenharmony_ci
205462306a36Sopenharmony_cistatic int __init mlxsw_pci_module_init(void)
205562306a36Sopenharmony_ci{
205662306a36Sopenharmony_ci	return 0;
205762306a36Sopenharmony_ci}
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_cistatic void __exit mlxsw_pci_module_exit(void)
206062306a36Sopenharmony_ci{
206162306a36Sopenharmony_ci}
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_cimodule_init(mlxsw_pci_module_init);
206462306a36Sopenharmony_cimodule_exit(mlxsw_pci_module_exit);
206562306a36Sopenharmony_ci
206662306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
206762306a36Sopenharmony_ciMODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>");
206862306a36Sopenharmony_ciMODULE_DESCRIPTION("Mellanox switch PCI interface driver");
2069