xref: /linux/drivers/net/ethernet/meta/fbnic/fbnic_fw.c (revision 9410645520e9b820069761f3450ef6661418e279)
1da3cde08SAlexander Duyck // SPDX-License-Identifier: GPL-2.0
2da3cde08SAlexander Duyck /* Copyright (c) Meta Platforms, Inc. and affiliates. */
3da3cde08SAlexander Duyck 
4da3cde08SAlexander Duyck #include <linux/bitfield.h>
520d2e88cSAlexander Duyck #include <linux/etherdevice.h>
6da3cde08SAlexander Duyck #include <linux/delay.h>
7da3cde08SAlexander Duyck #include <linux/dev_printk.h>
8da3cde08SAlexander Duyck #include <linux/dma-mapping.h>
9da3cde08SAlexander Duyck #include <linux/gfp.h>
10da3cde08SAlexander Duyck #include <linux/types.h>
11da3cde08SAlexander Duyck 
12da3cde08SAlexander Duyck #include "fbnic.h"
13da3cde08SAlexander Duyck #include "fbnic_tlv.h"
14da3cde08SAlexander Duyck 
__fbnic_mbx_wr_desc(struct fbnic_dev * fbd,int mbx_idx,int desc_idx,u64 desc)15da3cde08SAlexander Duyck static void __fbnic_mbx_wr_desc(struct fbnic_dev *fbd, int mbx_idx,
16da3cde08SAlexander Duyck 				int desc_idx, u64 desc)
17da3cde08SAlexander Duyck {
18da3cde08SAlexander Duyck 	u32 desc_offset = FBNIC_IPC_MBX(mbx_idx, desc_idx);
19da3cde08SAlexander Duyck 
20da3cde08SAlexander Duyck 	fw_wr32(fbd, desc_offset + 1, upper_32_bits(desc));
21da3cde08SAlexander Duyck 	fw_wrfl(fbd);
22da3cde08SAlexander Duyck 	fw_wr32(fbd, desc_offset, lower_32_bits(desc));
23da3cde08SAlexander Duyck }
24da3cde08SAlexander Duyck 
__fbnic_mbx_rd_desc(struct fbnic_dev * fbd,int mbx_idx,int desc_idx)25da3cde08SAlexander Duyck static u64 __fbnic_mbx_rd_desc(struct fbnic_dev *fbd, int mbx_idx, int desc_idx)
26da3cde08SAlexander Duyck {
27da3cde08SAlexander Duyck 	u32 desc_offset = FBNIC_IPC_MBX(mbx_idx, desc_idx);
28da3cde08SAlexander Duyck 	u64 desc;
29da3cde08SAlexander Duyck 
30da3cde08SAlexander Duyck 	desc = fw_rd32(fbd, desc_offset);
31da3cde08SAlexander Duyck 	desc |= (u64)fw_rd32(fbd, desc_offset + 1) << 32;
32da3cde08SAlexander Duyck 
33da3cde08SAlexander Duyck 	return desc;
34da3cde08SAlexander Duyck }
35da3cde08SAlexander Duyck 
fbnic_mbx_init_desc_ring(struct fbnic_dev * fbd,int mbx_idx)36da3cde08SAlexander Duyck static void fbnic_mbx_init_desc_ring(struct fbnic_dev *fbd, int mbx_idx)
37da3cde08SAlexander Duyck {
38da3cde08SAlexander Duyck 	int desc_idx;
39da3cde08SAlexander Duyck 
40da3cde08SAlexander Duyck 	/* Initialize first descriptor to all 0s. Doing this gives us a
41da3cde08SAlexander Duyck 	 * solid stop for the firmware to hit when it is done looping
42da3cde08SAlexander Duyck 	 * through the ring.
43da3cde08SAlexander Duyck 	 */
44da3cde08SAlexander Duyck 	__fbnic_mbx_wr_desc(fbd, mbx_idx, 0, 0);
45da3cde08SAlexander Duyck 
46da3cde08SAlexander Duyck 	fw_wrfl(fbd);
47da3cde08SAlexander Duyck 
48da3cde08SAlexander Duyck 	/* We then fill the rest of the ring starting at the end and moving
49da3cde08SAlexander Duyck 	 * back toward descriptor 0 with skip descriptors that have no
50da3cde08SAlexander Duyck 	 * length nor address, and tell the firmware that they can skip
51da3cde08SAlexander Duyck 	 * them and just move past them to the one we initialized to 0.
52da3cde08SAlexander Duyck 	 */
53da3cde08SAlexander Duyck 	for (desc_idx = FBNIC_IPC_MBX_DESC_LEN; --desc_idx;) {
54da3cde08SAlexander Duyck 		__fbnic_mbx_wr_desc(fbd, mbx_idx, desc_idx,
55da3cde08SAlexander Duyck 				    FBNIC_IPC_MBX_DESC_FW_CMPL |
56da3cde08SAlexander Duyck 				    FBNIC_IPC_MBX_DESC_HOST_CMPL);
57da3cde08SAlexander Duyck 		fw_wrfl(fbd);
58da3cde08SAlexander Duyck 	}
59da3cde08SAlexander Duyck }
60da3cde08SAlexander Duyck 
fbnic_mbx_init(struct fbnic_dev * fbd)61da3cde08SAlexander Duyck void fbnic_mbx_init(struct fbnic_dev *fbd)
62da3cde08SAlexander Duyck {
63da3cde08SAlexander Duyck 	int i;
64da3cde08SAlexander Duyck 
65da3cde08SAlexander Duyck 	/* Initialize lock to protect Tx ring */
66da3cde08SAlexander Duyck 	spin_lock_init(&fbd->fw_tx_lock);
67da3cde08SAlexander Duyck 
68da3cde08SAlexander Duyck 	/* Reinitialize mailbox memory */
69da3cde08SAlexander Duyck 	for (i = 0; i < FBNIC_IPC_MBX_INDICES; i++)
70da3cde08SAlexander Duyck 		memset(&fbd->mbx[i], 0, sizeof(struct fbnic_fw_mbx));
71da3cde08SAlexander Duyck 
72da3cde08SAlexander Duyck 	/* Do not auto-clear the FW mailbox interrupt, let SW clear it */
73da3cde08SAlexander Duyck 	wr32(fbd, FBNIC_INTR_SW_AC_MODE(0), ~(1u << FBNIC_FW_MSIX_ENTRY));
74da3cde08SAlexander Duyck 
75da3cde08SAlexander Duyck 	/* Clear any stale causes in vector 0 as that is used for doorbell */
76da3cde08SAlexander Duyck 	wr32(fbd, FBNIC_INTR_CLEAR(0), 1u << FBNIC_FW_MSIX_ENTRY);
77da3cde08SAlexander Duyck 
78da3cde08SAlexander Duyck 	for (i = 0; i < FBNIC_IPC_MBX_INDICES; i++)
79da3cde08SAlexander Duyck 		fbnic_mbx_init_desc_ring(fbd, i);
80da3cde08SAlexander Duyck }
81da3cde08SAlexander Duyck 
fbnic_mbx_map_msg(struct fbnic_dev * fbd,int mbx_idx,struct fbnic_tlv_msg * msg,u16 length,u8 eom)82da3cde08SAlexander Duyck static int fbnic_mbx_map_msg(struct fbnic_dev *fbd, int mbx_idx,
83da3cde08SAlexander Duyck 			     struct fbnic_tlv_msg *msg, u16 length, u8 eom)
84da3cde08SAlexander Duyck {
85da3cde08SAlexander Duyck 	struct fbnic_fw_mbx *mbx = &fbd->mbx[mbx_idx];
86da3cde08SAlexander Duyck 	u8 tail = mbx->tail;
87da3cde08SAlexander Duyck 	dma_addr_t addr;
88da3cde08SAlexander Duyck 	int direction;
89da3cde08SAlexander Duyck 
90da3cde08SAlexander Duyck 	if (!mbx->ready || !fbnic_fw_present(fbd))
91da3cde08SAlexander Duyck 		return -ENODEV;
92da3cde08SAlexander Duyck 
93da3cde08SAlexander Duyck 	direction = (mbx_idx == FBNIC_IPC_MBX_RX_IDX) ? DMA_FROM_DEVICE :
94da3cde08SAlexander Duyck 							DMA_TO_DEVICE;
95da3cde08SAlexander Duyck 
96da3cde08SAlexander Duyck 	if (mbx->head == ((tail + 1) % FBNIC_IPC_MBX_DESC_LEN))
97da3cde08SAlexander Duyck 		return -EBUSY;
98da3cde08SAlexander Duyck 
99da3cde08SAlexander Duyck 	addr = dma_map_single(fbd->dev, msg, PAGE_SIZE, direction);
100da3cde08SAlexander Duyck 	if (dma_mapping_error(fbd->dev, addr)) {
101da3cde08SAlexander Duyck 		free_page((unsigned long)msg);
102da3cde08SAlexander Duyck 
103da3cde08SAlexander Duyck 		return -ENOSPC;
104da3cde08SAlexander Duyck 	}
105da3cde08SAlexander Duyck 
106da3cde08SAlexander Duyck 	mbx->buf_info[tail].msg = msg;
107da3cde08SAlexander Duyck 	mbx->buf_info[tail].addr = addr;
108da3cde08SAlexander Duyck 
109da3cde08SAlexander Duyck 	mbx->tail = (tail + 1) % FBNIC_IPC_MBX_DESC_LEN;
110da3cde08SAlexander Duyck 
111da3cde08SAlexander Duyck 	fw_wr32(fbd, FBNIC_IPC_MBX(mbx_idx, mbx->tail), 0);
112da3cde08SAlexander Duyck 
113da3cde08SAlexander Duyck 	__fbnic_mbx_wr_desc(fbd, mbx_idx, tail,
114da3cde08SAlexander Duyck 			    FIELD_PREP(FBNIC_IPC_MBX_DESC_LEN_MASK, length) |
115da3cde08SAlexander Duyck 			    (addr & FBNIC_IPC_MBX_DESC_ADDR_MASK) |
116da3cde08SAlexander Duyck 			    (eom ? FBNIC_IPC_MBX_DESC_EOM : 0) |
117da3cde08SAlexander Duyck 			    FBNIC_IPC_MBX_DESC_HOST_CMPL);
118da3cde08SAlexander Duyck 
119da3cde08SAlexander Duyck 	return 0;
120da3cde08SAlexander Duyck }
121da3cde08SAlexander Duyck 
fbnic_mbx_unmap_and_free_msg(struct fbnic_dev * fbd,int mbx_idx,int desc_idx)122da3cde08SAlexander Duyck static void fbnic_mbx_unmap_and_free_msg(struct fbnic_dev *fbd, int mbx_idx,
123da3cde08SAlexander Duyck 					 int desc_idx)
124da3cde08SAlexander Duyck {
125da3cde08SAlexander Duyck 	struct fbnic_fw_mbx *mbx = &fbd->mbx[mbx_idx];
126da3cde08SAlexander Duyck 	int direction;
127da3cde08SAlexander Duyck 
128da3cde08SAlexander Duyck 	if (!mbx->buf_info[desc_idx].msg)
129da3cde08SAlexander Duyck 		return;
130da3cde08SAlexander Duyck 
131da3cde08SAlexander Duyck 	direction = (mbx_idx == FBNIC_IPC_MBX_RX_IDX) ? DMA_FROM_DEVICE :
132da3cde08SAlexander Duyck 							DMA_TO_DEVICE;
133da3cde08SAlexander Duyck 	dma_unmap_single(fbd->dev, mbx->buf_info[desc_idx].addr,
134da3cde08SAlexander Duyck 			 PAGE_SIZE, direction);
135da3cde08SAlexander Duyck 
136da3cde08SAlexander Duyck 	free_page((unsigned long)mbx->buf_info[desc_idx].msg);
137da3cde08SAlexander Duyck 	mbx->buf_info[desc_idx].msg = NULL;
138da3cde08SAlexander Duyck }
139da3cde08SAlexander Duyck 
fbnic_mbx_clean_desc_ring(struct fbnic_dev * fbd,int mbx_idx)140da3cde08SAlexander Duyck static void fbnic_mbx_clean_desc_ring(struct fbnic_dev *fbd, int mbx_idx)
141da3cde08SAlexander Duyck {
142da3cde08SAlexander Duyck 	int i;
143da3cde08SAlexander Duyck 
144da3cde08SAlexander Duyck 	fbnic_mbx_init_desc_ring(fbd, mbx_idx);
145da3cde08SAlexander Duyck 
146da3cde08SAlexander Duyck 	for (i = FBNIC_IPC_MBX_DESC_LEN; i--;)
147da3cde08SAlexander Duyck 		fbnic_mbx_unmap_and_free_msg(fbd, mbx_idx, i);
148da3cde08SAlexander Duyck }
149da3cde08SAlexander Duyck 
fbnic_mbx_clean(struct fbnic_dev * fbd)150da3cde08SAlexander Duyck void fbnic_mbx_clean(struct fbnic_dev *fbd)
151da3cde08SAlexander Duyck {
152da3cde08SAlexander Duyck 	int i;
153da3cde08SAlexander Duyck 
154da3cde08SAlexander Duyck 	for (i = 0; i < FBNIC_IPC_MBX_INDICES; i++)
155da3cde08SAlexander Duyck 		fbnic_mbx_clean_desc_ring(fbd, i);
156da3cde08SAlexander Duyck }
157da3cde08SAlexander Duyck 
158da3cde08SAlexander Duyck #define FBNIC_MBX_MAX_PAGE_SIZE	FIELD_MAX(FBNIC_IPC_MBX_DESC_LEN_MASK)
159da3cde08SAlexander Duyck #define FBNIC_RX_PAGE_SIZE	min_t(int, PAGE_SIZE, FBNIC_MBX_MAX_PAGE_SIZE)
160da3cde08SAlexander Duyck 
fbnic_mbx_alloc_rx_msgs(struct fbnic_dev * fbd)161da3cde08SAlexander Duyck static int fbnic_mbx_alloc_rx_msgs(struct fbnic_dev *fbd)
162da3cde08SAlexander Duyck {
163da3cde08SAlexander Duyck 	struct fbnic_fw_mbx *rx_mbx = &fbd->mbx[FBNIC_IPC_MBX_RX_IDX];
164da3cde08SAlexander Duyck 	u8 tail = rx_mbx->tail, head = rx_mbx->head, count;
165da3cde08SAlexander Duyck 	int err = 0;
166da3cde08SAlexander Duyck 
167da3cde08SAlexander Duyck 	/* Do nothing if mailbox is not ready, or we already have pages on
168da3cde08SAlexander Duyck 	 * the ring that can be used by the firmware
169da3cde08SAlexander Duyck 	 */
170da3cde08SAlexander Duyck 	if (!rx_mbx->ready)
171da3cde08SAlexander Duyck 		return -ENODEV;
172da3cde08SAlexander Duyck 
173da3cde08SAlexander Duyck 	/* Fill all but 1 unused descriptors in the Rx queue. */
174da3cde08SAlexander Duyck 	count = (head - tail - 1) % FBNIC_IPC_MBX_DESC_LEN;
175da3cde08SAlexander Duyck 	while (!err && count--) {
176da3cde08SAlexander Duyck 		struct fbnic_tlv_msg *msg;
177da3cde08SAlexander Duyck 
178da3cde08SAlexander Duyck 		msg = (struct fbnic_tlv_msg *)__get_free_page(GFP_ATOMIC |
179da3cde08SAlexander Duyck 							      __GFP_NOWARN);
180da3cde08SAlexander Duyck 		if (!msg) {
181da3cde08SAlexander Duyck 			err = -ENOMEM;
182da3cde08SAlexander Duyck 			break;
183da3cde08SAlexander Duyck 		}
184da3cde08SAlexander Duyck 
185da3cde08SAlexander Duyck 		err = fbnic_mbx_map_msg(fbd, FBNIC_IPC_MBX_RX_IDX, msg,
186da3cde08SAlexander Duyck 					FBNIC_RX_PAGE_SIZE, 0);
187da3cde08SAlexander Duyck 		if (err)
188da3cde08SAlexander Duyck 			free_page((unsigned long)msg);
189da3cde08SAlexander Duyck 	}
190da3cde08SAlexander Duyck 
191da3cde08SAlexander Duyck 	return err;
192da3cde08SAlexander Duyck }
193da3cde08SAlexander Duyck 
fbnic_mbx_map_tlv_msg(struct fbnic_dev * fbd,struct fbnic_tlv_msg * msg)19420d2e88cSAlexander Duyck static int fbnic_mbx_map_tlv_msg(struct fbnic_dev *fbd,
19520d2e88cSAlexander Duyck 				 struct fbnic_tlv_msg *msg)
19620d2e88cSAlexander Duyck {
19720d2e88cSAlexander Duyck 	unsigned long flags;
19820d2e88cSAlexander Duyck 	int err;
19920d2e88cSAlexander Duyck 
20020d2e88cSAlexander Duyck 	spin_lock_irqsave(&fbd->fw_tx_lock, flags);
20120d2e88cSAlexander Duyck 
20220d2e88cSAlexander Duyck 	err = fbnic_mbx_map_msg(fbd, FBNIC_IPC_MBX_TX_IDX, msg,
20320d2e88cSAlexander Duyck 				le16_to_cpu(msg->hdr.len) * sizeof(u32), 1);
20420d2e88cSAlexander Duyck 
20520d2e88cSAlexander Duyck 	spin_unlock_irqrestore(&fbd->fw_tx_lock, flags);
20620d2e88cSAlexander Duyck 
20720d2e88cSAlexander Duyck 	return err;
20820d2e88cSAlexander Duyck }
20920d2e88cSAlexander Duyck 
fbnic_mbx_process_tx_msgs(struct fbnic_dev * fbd)210da3cde08SAlexander Duyck static void fbnic_mbx_process_tx_msgs(struct fbnic_dev *fbd)
211da3cde08SAlexander Duyck {
212da3cde08SAlexander Duyck 	struct fbnic_fw_mbx *tx_mbx = &fbd->mbx[FBNIC_IPC_MBX_TX_IDX];
213da3cde08SAlexander Duyck 	u8 head = tx_mbx->head;
214da3cde08SAlexander Duyck 	u64 desc;
215da3cde08SAlexander Duyck 
216da3cde08SAlexander Duyck 	while (head != tx_mbx->tail) {
217da3cde08SAlexander Duyck 		desc = __fbnic_mbx_rd_desc(fbd, FBNIC_IPC_MBX_TX_IDX, head);
218da3cde08SAlexander Duyck 		if (!(desc & FBNIC_IPC_MBX_DESC_FW_CMPL))
219da3cde08SAlexander Duyck 			break;
220da3cde08SAlexander Duyck 
221da3cde08SAlexander Duyck 		fbnic_mbx_unmap_and_free_msg(fbd, FBNIC_IPC_MBX_TX_IDX, head);
222da3cde08SAlexander Duyck 
223da3cde08SAlexander Duyck 		head++;
224da3cde08SAlexander Duyck 		head %= FBNIC_IPC_MBX_DESC_LEN;
225da3cde08SAlexander Duyck 	}
226da3cde08SAlexander Duyck 
227da3cde08SAlexander Duyck 	/* Record head for next interrupt */
228da3cde08SAlexander Duyck 	tx_mbx->head = head;
229da3cde08SAlexander Duyck }
230da3cde08SAlexander Duyck 
23120d2e88cSAlexander Duyck /**
23220d2e88cSAlexander Duyck  * fbnic_fw_xmit_simple_msg - Transmit a simple single TLV message w/o data
23320d2e88cSAlexander Duyck  * @fbd: FBNIC device structure
23420d2e88cSAlexander Duyck  * @msg_type: ENUM value indicating message type to send
23520d2e88cSAlexander Duyck  *
23620d2e88cSAlexander Duyck  * Return:
23720d2e88cSAlexander Duyck  *   One the following values:
23820d2e88cSAlexander Duyck  *     -EOPNOTSUPP: Is not ASIC so mailbox is not supported
23920d2e88cSAlexander Duyck  *     -ENODEV: Device I/O error
24020d2e88cSAlexander Duyck  *     -ENOMEM: Failed to allocate message
24120d2e88cSAlexander Duyck  *     -EBUSY: No space in mailbox
24220d2e88cSAlexander Duyck  *     -ENOSPC: DMA mapping failed
24320d2e88cSAlexander Duyck  *
24420d2e88cSAlexander Duyck  * This function sends a single TLV header indicating the host wants to take
24520d2e88cSAlexander Duyck  * some action. However there are no other side effects which means that any
24620d2e88cSAlexander Duyck  * response will need to be caught via a completion if this action is
24720d2e88cSAlexander Duyck  * expected to kick off a resultant action.
24820d2e88cSAlexander Duyck  */
fbnic_fw_xmit_simple_msg(struct fbnic_dev * fbd,u32 msg_type)24920d2e88cSAlexander Duyck static int fbnic_fw_xmit_simple_msg(struct fbnic_dev *fbd, u32 msg_type)
25020d2e88cSAlexander Duyck {
25120d2e88cSAlexander Duyck 	struct fbnic_tlv_msg *msg;
25220d2e88cSAlexander Duyck 	int err = 0;
25320d2e88cSAlexander Duyck 
25420d2e88cSAlexander Duyck 	if (!fbnic_fw_present(fbd))
25520d2e88cSAlexander Duyck 		return -ENODEV;
25620d2e88cSAlexander Duyck 
25720d2e88cSAlexander Duyck 	msg = fbnic_tlv_msg_alloc(msg_type);
25820d2e88cSAlexander Duyck 	if (!msg)
25920d2e88cSAlexander Duyck 		return -ENOMEM;
26020d2e88cSAlexander Duyck 
26120d2e88cSAlexander Duyck 	err = fbnic_mbx_map_tlv_msg(fbd, msg);
26220d2e88cSAlexander Duyck 	if (err)
26320d2e88cSAlexander Duyck 		free_page((unsigned long)msg);
26420d2e88cSAlexander Duyck 
26520d2e88cSAlexander Duyck 	return err;
26620d2e88cSAlexander Duyck }
26720d2e88cSAlexander Duyck 
26820d2e88cSAlexander Duyck /**
26920d2e88cSAlexander Duyck  * fbnic_fw_xmit_cap_msg - Allocate and populate a FW capabilities message
27020d2e88cSAlexander Duyck  * @fbd: FBNIC device structure
27120d2e88cSAlexander Duyck  *
27220d2e88cSAlexander Duyck  * Return: NULL on failure to allocate, error pointer on error, or pointer
27320d2e88cSAlexander Duyck  * to new TLV test message.
27420d2e88cSAlexander Duyck  *
27520d2e88cSAlexander Duyck  * Sends a single TLV header indicating the host wants the firmware to
27620d2e88cSAlexander Duyck  * confirm the capabilities and version.
27720d2e88cSAlexander Duyck  **/
fbnic_fw_xmit_cap_msg(struct fbnic_dev * fbd)27820d2e88cSAlexander Duyck static int fbnic_fw_xmit_cap_msg(struct fbnic_dev *fbd)
27920d2e88cSAlexander Duyck {
28020d2e88cSAlexander Duyck 	int err = fbnic_fw_xmit_simple_msg(fbd, FBNIC_TLV_MSG_ID_HOST_CAP_REQ);
28120d2e88cSAlexander Duyck 
28220d2e88cSAlexander Duyck 	/* Return 0 if we are not calling this on ASIC */
28320d2e88cSAlexander Duyck 	return (err == -EOPNOTSUPP) ? 0 : err;
28420d2e88cSAlexander Duyck }
28520d2e88cSAlexander Duyck 
fbnic_mbx_postinit_desc_ring(struct fbnic_dev * fbd,int mbx_idx)286da3cde08SAlexander Duyck static void fbnic_mbx_postinit_desc_ring(struct fbnic_dev *fbd, int mbx_idx)
287da3cde08SAlexander Duyck {
288da3cde08SAlexander Duyck 	struct fbnic_fw_mbx *mbx = &fbd->mbx[mbx_idx];
289da3cde08SAlexander Duyck 
290da3cde08SAlexander Duyck 	/* This is a one time init, so just exit if it is completed */
291da3cde08SAlexander Duyck 	if (mbx->ready)
292da3cde08SAlexander Duyck 		return;
293da3cde08SAlexander Duyck 
294da3cde08SAlexander Duyck 	mbx->ready = true;
295da3cde08SAlexander Duyck 
296da3cde08SAlexander Duyck 	switch (mbx_idx) {
297da3cde08SAlexander Duyck 	case FBNIC_IPC_MBX_RX_IDX:
298da3cde08SAlexander Duyck 		/* Make sure we have a page for the FW to write to */
299da3cde08SAlexander Duyck 		fbnic_mbx_alloc_rx_msgs(fbd);
300da3cde08SAlexander Duyck 		break;
30120d2e88cSAlexander Duyck 	case FBNIC_IPC_MBX_TX_IDX:
30220d2e88cSAlexander Duyck 		/* Force version to 1 if we successfully requested an update
30320d2e88cSAlexander Duyck 		 * from the firmware. This should be overwritten once we get
30420d2e88cSAlexander Duyck 		 * the actual version from the firmware in the capabilities
30520d2e88cSAlexander Duyck 		 * request message.
30620d2e88cSAlexander Duyck 		 */
30720d2e88cSAlexander Duyck 		if (!fbnic_fw_xmit_cap_msg(fbd) &&
30820d2e88cSAlexander Duyck 		    !fbd->fw_cap.running.mgmt.version)
30920d2e88cSAlexander Duyck 			fbd->fw_cap.running.mgmt.version = 1;
31020d2e88cSAlexander Duyck 		break;
311da3cde08SAlexander Duyck 	}
312da3cde08SAlexander Duyck }
313da3cde08SAlexander Duyck 
fbnic_mbx_postinit(struct fbnic_dev * fbd)314da3cde08SAlexander Duyck static void fbnic_mbx_postinit(struct fbnic_dev *fbd)
315da3cde08SAlexander Duyck {
316da3cde08SAlexander Duyck 	int i;
317da3cde08SAlexander Duyck 
318da3cde08SAlexander Duyck 	/* We only need to do this on the first interrupt following init.
319da3cde08SAlexander Duyck 	 * this primes the mailbox so that we will have cleared all the
320da3cde08SAlexander Duyck 	 * skip descriptors.
321da3cde08SAlexander Duyck 	 */
322da3cde08SAlexander Duyck 	if (!(rd32(fbd, FBNIC_INTR_STATUS(0)) & (1u << FBNIC_FW_MSIX_ENTRY)))
323da3cde08SAlexander Duyck 		return;
324da3cde08SAlexander Duyck 
325da3cde08SAlexander Duyck 	wr32(fbd, FBNIC_INTR_CLEAR(0), 1u << FBNIC_FW_MSIX_ENTRY);
326da3cde08SAlexander Duyck 
327da3cde08SAlexander Duyck 	for (i = 0; i < FBNIC_IPC_MBX_INDICES; i++)
328da3cde08SAlexander Duyck 		fbnic_mbx_postinit_desc_ring(fbd, i);
329da3cde08SAlexander Duyck }
330da3cde08SAlexander Duyck 
33120d2e88cSAlexander Duyck /**
33220d2e88cSAlexander Duyck  * fbnic_fw_xmit_ownership_msg - Create and transmit a host ownership message
33320d2e88cSAlexander Duyck  * to FW mailbox
33420d2e88cSAlexander Duyck  *
33520d2e88cSAlexander Duyck  * @fbd: FBNIC device structure
33620d2e88cSAlexander Duyck  * @take_ownership: take/release the ownership
33720d2e88cSAlexander Duyck  *
33820d2e88cSAlexander Duyck  * Return: zero on success, negative value on failure
33920d2e88cSAlexander Duyck  *
34020d2e88cSAlexander Duyck  * Notifies the firmware that the driver either takes ownership of the NIC
34120d2e88cSAlexander Duyck  * (when @take_ownership is true) or releases it.
34220d2e88cSAlexander Duyck  */
fbnic_fw_xmit_ownership_msg(struct fbnic_dev * fbd,bool take_ownership)34320d2e88cSAlexander Duyck int fbnic_fw_xmit_ownership_msg(struct fbnic_dev *fbd, bool take_ownership)
34420d2e88cSAlexander Duyck {
34520d2e88cSAlexander Duyck 	unsigned long req_time = jiffies;
34620d2e88cSAlexander Duyck 	struct fbnic_tlv_msg *msg;
34720d2e88cSAlexander Duyck 	int err = 0;
34820d2e88cSAlexander Duyck 
34920d2e88cSAlexander Duyck 	if (!fbnic_fw_present(fbd))
35020d2e88cSAlexander Duyck 		return -ENODEV;
35120d2e88cSAlexander Duyck 
35220d2e88cSAlexander Duyck 	msg = fbnic_tlv_msg_alloc(FBNIC_TLV_MSG_ID_OWNERSHIP_REQ);
35320d2e88cSAlexander Duyck 	if (!msg)
35420d2e88cSAlexander Duyck 		return -ENOMEM;
35520d2e88cSAlexander Duyck 
35620d2e88cSAlexander Duyck 	if (take_ownership) {
35720d2e88cSAlexander Duyck 		err = fbnic_tlv_attr_put_flag(msg, FBNIC_FW_OWNERSHIP_FLAG);
35820d2e88cSAlexander Duyck 		if (err)
35920d2e88cSAlexander Duyck 			goto free_message;
36020d2e88cSAlexander Duyck 	}
36120d2e88cSAlexander Duyck 
36220d2e88cSAlexander Duyck 	err = fbnic_mbx_map_tlv_msg(fbd, msg);
36320d2e88cSAlexander Duyck 	if (err)
36420d2e88cSAlexander Duyck 		goto free_message;
36520d2e88cSAlexander Duyck 
36620d2e88cSAlexander Duyck 	/* Initialize heartbeat, set last response to 1 second in the past
36720d2e88cSAlexander Duyck 	 * so that we will trigger a timeout if the firmware doesn't respond
36820d2e88cSAlexander Duyck 	 */
36920d2e88cSAlexander Duyck 	fbd->last_heartbeat_response = req_time - HZ;
37020d2e88cSAlexander Duyck 
37120d2e88cSAlexander Duyck 	fbd->last_heartbeat_request = req_time;
37220d2e88cSAlexander Duyck 
37320d2e88cSAlexander Duyck 	/* Set heartbeat detection based on if we are taking ownership */
37420d2e88cSAlexander Duyck 	fbd->fw_heartbeat_enabled = take_ownership;
37520d2e88cSAlexander Duyck 
37620d2e88cSAlexander Duyck 	return err;
37720d2e88cSAlexander Duyck 
37820d2e88cSAlexander Duyck free_message:
37920d2e88cSAlexander Duyck 	free_page((unsigned long)msg);
38020d2e88cSAlexander Duyck 	return err;
38120d2e88cSAlexander Duyck }
38220d2e88cSAlexander Duyck 
38320d2e88cSAlexander Duyck static const struct fbnic_tlv_index fbnic_fw_cap_resp_index[] = {
38420d2e88cSAlexander Duyck 	FBNIC_TLV_ATTR_U32(FBNIC_FW_CAP_RESP_VERSION),
38520d2e88cSAlexander Duyck 	FBNIC_TLV_ATTR_FLAG(FBNIC_FW_CAP_RESP_BMC_PRESENT),
38620d2e88cSAlexander Duyck 	FBNIC_TLV_ATTR_MAC_ADDR(FBNIC_FW_CAP_RESP_BMC_MAC_ADDR),
38720d2e88cSAlexander Duyck 	FBNIC_TLV_ATTR_ARRAY(FBNIC_FW_CAP_RESP_BMC_MAC_ARRAY),
38820d2e88cSAlexander Duyck 	FBNIC_TLV_ATTR_U32(FBNIC_FW_CAP_RESP_STORED_VERSION),
38920d2e88cSAlexander Duyck 	FBNIC_TLV_ATTR_U32(FBNIC_FW_CAP_RESP_ACTIVE_FW_SLOT),
39020d2e88cSAlexander Duyck 	FBNIC_TLV_ATTR_STRING(FBNIC_FW_CAP_RESP_VERSION_COMMIT_STR,
39120d2e88cSAlexander Duyck 			      FBNIC_FW_CAP_RESP_COMMIT_MAX_SIZE),
39220d2e88cSAlexander Duyck 	FBNIC_TLV_ATTR_U32(FBNIC_FW_CAP_RESP_BMC_ALL_MULTI),
39320d2e88cSAlexander Duyck 	FBNIC_TLV_ATTR_U32(FBNIC_FW_CAP_RESP_FW_LINK_SPEED),
39420d2e88cSAlexander Duyck 	FBNIC_TLV_ATTR_U32(FBNIC_FW_CAP_RESP_FW_LINK_FEC),
39520d2e88cSAlexander Duyck 	FBNIC_TLV_ATTR_STRING(FBNIC_FW_CAP_RESP_STORED_COMMIT_STR,
39620d2e88cSAlexander Duyck 			      FBNIC_FW_CAP_RESP_COMMIT_MAX_SIZE),
39720d2e88cSAlexander Duyck 	FBNIC_TLV_ATTR_U32(FBNIC_FW_CAP_RESP_CMRT_VERSION),
39820d2e88cSAlexander Duyck 	FBNIC_TLV_ATTR_U32(FBNIC_FW_CAP_RESP_STORED_CMRT_VERSION),
39920d2e88cSAlexander Duyck 	FBNIC_TLV_ATTR_STRING(FBNIC_FW_CAP_RESP_CMRT_COMMIT_STR,
40020d2e88cSAlexander Duyck 			      FBNIC_FW_CAP_RESP_COMMIT_MAX_SIZE),
40120d2e88cSAlexander Duyck 	FBNIC_TLV_ATTR_STRING(FBNIC_FW_CAP_RESP_STORED_CMRT_COMMIT_STR,
40220d2e88cSAlexander Duyck 			      FBNIC_FW_CAP_RESP_COMMIT_MAX_SIZE),
40320d2e88cSAlexander Duyck 	FBNIC_TLV_ATTR_U32(FBNIC_FW_CAP_RESP_UEFI_VERSION),
40420d2e88cSAlexander Duyck 	FBNIC_TLV_ATTR_STRING(FBNIC_FW_CAP_RESP_UEFI_COMMIT_STR,
40520d2e88cSAlexander Duyck 			      FBNIC_FW_CAP_RESP_COMMIT_MAX_SIZE),
40620d2e88cSAlexander Duyck 	FBNIC_TLV_ATTR_LAST
40720d2e88cSAlexander Duyck };
40820d2e88cSAlexander Duyck 
fbnic_fw_parse_bmc_addrs(u8 bmc_mac_addr[][ETH_ALEN],struct fbnic_tlv_msg * attr,int len)40920d2e88cSAlexander Duyck static int fbnic_fw_parse_bmc_addrs(u8 bmc_mac_addr[][ETH_ALEN],
41020d2e88cSAlexander Duyck 				    struct fbnic_tlv_msg *attr, int len)
41120d2e88cSAlexander Duyck {
41220d2e88cSAlexander Duyck 	int attr_len = le16_to_cpu(attr->hdr.len) / sizeof(u32) - 1;
41320d2e88cSAlexander Duyck 	struct fbnic_tlv_msg *mac_results[8];
41420d2e88cSAlexander Duyck 	int err, i = 0;
41520d2e88cSAlexander Duyck 
41620d2e88cSAlexander Duyck 	/* Make sure we have enough room to process all the MAC addresses */
41720d2e88cSAlexander Duyck 	if (len > 8)
41820d2e88cSAlexander Duyck 		return -ENOSPC;
41920d2e88cSAlexander Duyck 
42020d2e88cSAlexander Duyck 	/* Parse the array */
42120d2e88cSAlexander Duyck 	err = fbnic_tlv_attr_parse_array(&attr[1], attr_len, mac_results,
42220d2e88cSAlexander Duyck 					 fbnic_fw_cap_resp_index,
42320d2e88cSAlexander Duyck 					 FBNIC_FW_CAP_RESP_BMC_MAC_ADDR, len);
42420d2e88cSAlexander Duyck 	if (err)
42520d2e88cSAlexander Duyck 		return err;
42620d2e88cSAlexander Duyck 
42720d2e88cSAlexander Duyck 	/* Copy results into MAC addr array */
42820d2e88cSAlexander Duyck 	for (i = 0; i < len && mac_results[i]; i++)
42920d2e88cSAlexander Duyck 		fbnic_tlv_attr_addr_copy(bmc_mac_addr[i], mac_results[i]);
43020d2e88cSAlexander Duyck 
43120d2e88cSAlexander Duyck 	/* Zero remaining unused addresses */
43220d2e88cSAlexander Duyck 	while (i < len)
43320d2e88cSAlexander Duyck 		eth_zero_addr(bmc_mac_addr[i++]);
43420d2e88cSAlexander Duyck 
43520d2e88cSAlexander Duyck 	return 0;
43620d2e88cSAlexander Duyck }
43720d2e88cSAlexander Duyck 
fbnic_fw_parse_cap_resp(void * opaque,struct fbnic_tlv_msg ** results)43820d2e88cSAlexander Duyck static int fbnic_fw_parse_cap_resp(void *opaque, struct fbnic_tlv_msg **results)
43920d2e88cSAlexander Duyck {
44020d2e88cSAlexander Duyck 	u32 active_slot = 0, all_multi = 0;
44120d2e88cSAlexander Duyck 	struct fbnic_dev *fbd = opaque;
44220d2e88cSAlexander Duyck 	u32 speed = 0, fec = 0;
44320d2e88cSAlexander Duyck 	size_t commit_size = 0;
44420d2e88cSAlexander Duyck 	bool bmc_present;
44520d2e88cSAlexander Duyck 	int err;
44620d2e88cSAlexander Duyck 
44720d2e88cSAlexander Duyck 	get_unsigned_result(FBNIC_FW_CAP_RESP_VERSION,
44820d2e88cSAlexander Duyck 			    fbd->fw_cap.running.mgmt.version);
44920d2e88cSAlexander Duyck 
45020d2e88cSAlexander Duyck 	if (!fbd->fw_cap.running.mgmt.version)
45120d2e88cSAlexander Duyck 		return -EINVAL;
45220d2e88cSAlexander Duyck 
45320d2e88cSAlexander Duyck 	if (fbd->fw_cap.running.mgmt.version < MIN_FW_VERSION_CODE) {
45420d2e88cSAlexander Duyck 		char running_ver[FBNIC_FW_VER_MAX_SIZE];
45520d2e88cSAlexander Duyck 
45620d2e88cSAlexander Duyck 		fbnic_mk_fw_ver_str(fbd->fw_cap.running.mgmt.version,
45720d2e88cSAlexander Duyck 				    running_ver);
45820d2e88cSAlexander Duyck 		dev_err(fbd->dev, "Device firmware version(%s) is older than minimum required version(%02d.%02d.%02d)\n",
45920d2e88cSAlexander Duyck 			running_ver,
46020d2e88cSAlexander Duyck 			MIN_FW_MAJOR_VERSION,
46120d2e88cSAlexander Duyck 			MIN_FW_MINOR_VERSION,
46220d2e88cSAlexander Duyck 			MIN_FW_BUILD_VERSION);
46320d2e88cSAlexander Duyck 		/* Disable TX mailbox to prevent card use until firmware is
46420d2e88cSAlexander Duyck 		 * updated.
46520d2e88cSAlexander Duyck 		 */
46620d2e88cSAlexander Duyck 		fbd->mbx[FBNIC_IPC_MBX_TX_IDX].ready = false;
46720d2e88cSAlexander Duyck 		return -EINVAL;
46820d2e88cSAlexander Duyck 	}
46920d2e88cSAlexander Duyck 
47020d2e88cSAlexander Duyck 	get_string_result(FBNIC_FW_CAP_RESP_VERSION_COMMIT_STR, commit_size,
47120d2e88cSAlexander Duyck 			  fbd->fw_cap.running.mgmt.commit,
47220d2e88cSAlexander Duyck 			  FBNIC_FW_CAP_RESP_COMMIT_MAX_SIZE);
47320d2e88cSAlexander Duyck 	if (!commit_size)
47420d2e88cSAlexander Duyck 		dev_warn(fbd->dev, "Firmware did not send mgmt commit!\n");
47520d2e88cSAlexander Duyck 
47620d2e88cSAlexander Duyck 	get_unsigned_result(FBNIC_FW_CAP_RESP_STORED_VERSION,
47720d2e88cSAlexander Duyck 			    fbd->fw_cap.stored.mgmt.version);
47820d2e88cSAlexander Duyck 	get_string_result(FBNIC_FW_CAP_RESP_STORED_COMMIT_STR, commit_size,
47920d2e88cSAlexander Duyck 			  fbd->fw_cap.stored.mgmt.commit,
48020d2e88cSAlexander Duyck 			  FBNIC_FW_CAP_RESP_COMMIT_MAX_SIZE);
48120d2e88cSAlexander Duyck 
48220d2e88cSAlexander Duyck 	get_unsigned_result(FBNIC_FW_CAP_RESP_CMRT_VERSION,
48320d2e88cSAlexander Duyck 			    fbd->fw_cap.running.bootloader.version);
48420d2e88cSAlexander Duyck 	get_string_result(FBNIC_FW_CAP_RESP_CMRT_COMMIT_STR, commit_size,
48520d2e88cSAlexander Duyck 			  fbd->fw_cap.running.bootloader.commit,
48620d2e88cSAlexander Duyck 			  FBNIC_FW_CAP_RESP_COMMIT_MAX_SIZE);
48720d2e88cSAlexander Duyck 
48820d2e88cSAlexander Duyck 	get_unsigned_result(FBNIC_FW_CAP_RESP_STORED_CMRT_VERSION,
48920d2e88cSAlexander Duyck 			    fbd->fw_cap.stored.bootloader.version);
49020d2e88cSAlexander Duyck 	get_string_result(FBNIC_FW_CAP_RESP_STORED_CMRT_COMMIT_STR, commit_size,
49120d2e88cSAlexander Duyck 			  fbd->fw_cap.stored.bootloader.commit,
49220d2e88cSAlexander Duyck 			  FBNIC_FW_CAP_RESP_COMMIT_MAX_SIZE);
49320d2e88cSAlexander Duyck 
49420d2e88cSAlexander Duyck 	get_unsigned_result(FBNIC_FW_CAP_RESP_UEFI_VERSION,
49520d2e88cSAlexander Duyck 			    fbd->fw_cap.stored.undi.version);
49620d2e88cSAlexander Duyck 	get_string_result(FBNIC_FW_CAP_RESP_UEFI_COMMIT_STR, commit_size,
49720d2e88cSAlexander Duyck 			  fbd->fw_cap.stored.undi.commit,
49820d2e88cSAlexander Duyck 			  FBNIC_FW_CAP_RESP_COMMIT_MAX_SIZE);
49920d2e88cSAlexander Duyck 
50020d2e88cSAlexander Duyck 	get_unsigned_result(FBNIC_FW_CAP_RESP_ACTIVE_FW_SLOT, active_slot);
50120d2e88cSAlexander Duyck 	fbd->fw_cap.active_slot = active_slot;
50220d2e88cSAlexander Duyck 
50320d2e88cSAlexander Duyck 	get_unsigned_result(FBNIC_FW_CAP_RESP_FW_LINK_SPEED, speed);
50420d2e88cSAlexander Duyck 	get_unsigned_result(FBNIC_FW_CAP_RESP_FW_LINK_FEC, fec);
50520d2e88cSAlexander Duyck 	fbd->fw_cap.link_speed = speed;
50620d2e88cSAlexander Duyck 	fbd->fw_cap.link_fec = fec;
50720d2e88cSAlexander Duyck 
50820d2e88cSAlexander Duyck 	bmc_present = !!results[FBNIC_FW_CAP_RESP_BMC_PRESENT];
50920d2e88cSAlexander Duyck 	if (bmc_present) {
51020d2e88cSAlexander Duyck 		struct fbnic_tlv_msg *attr;
51120d2e88cSAlexander Duyck 
51220d2e88cSAlexander Duyck 		attr = results[FBNIC_FW_CAP_RESP_BMC_MAC_ARRAY];
51320d2e88cSAlexander Duyck 		if (!attr)
51420d2e88cSAlexander Duyck 			return -EINVAL;
51520d2e88cSAlexander Duyck 
51620d2e88cSAlexander Duyck 		err = fbnic_fw_parse_bmc_addrs(fbd->fw_cap.bmc_mac_addr,
51720d2e88cSAlexander Duyck 					       attr, 4);
51820d2e88cSAlexander Duyck 		if (err)
51920d2e88cSAlexander Duyck 			return err;
52020d2e88cSAlexander Duyck 
52120d2e88cSAlexander Duyck 		get_unsigned_result(FBNIC_FW_CAP_RESP_BMC_ALL_MULTI, all_multi);
52220d2e88cSAlexander Duyck 	} else {
52320d2e88cSAlexander Duyck 		memset(fbd->fw_cap.bmc_mac_addr, 0,
52420d2e88cSAlexander Duyck 		       sizeof(fbd->fw_cap.bmc_mac_addr));
52520d2e88cSAlexander Duyck 	}
52620d2e88cSAlexander Duyck 
52720d2e88cSAlexander Duyck 	fbd->fw_cap.bmc_present = bmc_present;
52820d2e88cSAlexander Duyck 
52920d2e88cSAlexander Duyck 	if (results[FBNIC_FW_CAP_RESP_BMC_ALL_MULTI] || !bmc_present)
53020d2e88cSAlexander Duyck 		fbd->fw_cap.all_multi = all_multi;
53120d2e88cSAlexander Duyck 
53220d2e88cSAlexander Duyck 	return 0;
53320d2e88cSAlexander Duyck }
53420d2e88cSAlexander Duyck 
53520d2e88cSAlexander Duyck static const struct fbnic_tlv_index fbnic_ownership_resp_index[] = {
53620d2e88cSAlexander Duyck 	FBNIC_TLV_ATTR_LAST
53720d2e88cSAlexander Duyck };
53820d2e88cSAlexander Duyck 
fbnic_fw_parse_ownership_resp(void * opaque,struct fbnic_tlv_msg ** results)53920d2e88cSAlexander Duyck static int fbnic_fw_parse_ownership_resp(void *opaque,
54020d2e88cSAlexander Duyck 					 struct fbnic_tlv_msg **results)
54120d2e88cSAlexander Duyck {
54220d2e88cSAlexander Duyck 	struct fbnic_dev *fbd = (struct fbnic_dev *)opaque;
54320d2e88cSAlexander Duyck 
54420d2e88cSAlexander Duyck 	/* Count the ownership response as a heartbeat reply */
54520d2e88cSAlexander Duyck 	fbd->last_heartbeat_response = jiffies;
54620d2e88cSAlexander Duyck 
54720d2e88cSAlexander Duyck 	return 0;
54820d2e88cSAlexander Duyck }
54920d2e88cSAlexander Duyck 
55020d2e88cSAlexander Duyck static const struct fbnic_tlv_index fbnic_heartbeat_resp_index[] = {
55120d2e88cSAlexander Duyck 	FBNIC_TLV_ATTR_LAST
55220d2e88cSAlexander Duyck };
55320d2e88cSAlexander Duyck 
fbnic_fw_parse_heartbeat_resp(void * opaque,struct fbnic_tlv_msg ** results)55420d2e88cSAlexander Duyck static int fbnic_fw_parse_heartbeat_resp(void *opaque,
55520d2e88cSAlexander Duyck 					 struct fbnic_tlv_msg **results)
55620d2e88cSAlexander Duyck {
55720d2e88cSAlexander Duyck 	struct fbnic_dev *fbd = (struct fbnic_dev *)opaque;
55820d2e88cSAlexander Duyck 
55920d2e88cSAlexander Duyck 	fbd->last_heartbeat_response = jiffies;
56020d2e88cSAlexander Duyck 
56120d2e88cSAlexander Duyck 	return 0;
56220d2e88cSAlexander Duyck }
56320d2e88cSAlexander Duyck 
fbnic_fw_xmit_heartbeat_message(struct fbnic_dev * fbd)56420d2e88cSAlexander Duyck static int fbnic_fw_xmit_heartbeat_message(struct fbnic_dev *fbd)
56520d2e88cSAlexander Duyck {
56620d2e88cSAlexander Duyck 	unsigned long req_time = jiffies;
56720d2e88cSAlexander Duyck 	struct fbnic_tlv_msg *msg;
56820d2e88cSAlexander Duyck 	int err = 0;
56920d2e88cSAlexander Duyck 
57020d2e88cSAlexander Duyck 	if (!fbnic_fw_present(fbd))
57120d2e88cSAlexander Duyck 		return -ENODEV;
57220d2e88cSAlexander Duyck 
57320d2e88cSAlexander Duyck 	msg = fbnic_tlv_msg_alloc(FBNIC_TLV_MSG_ID_HEARTBEAT_REQ);
57420d2e88cSAlexander Duyck 	if (!msg)
57520d2e88cSAlexander Duyck 		return -ENOMEM;
57620d2e88cSAlexander Duyck 
57720d2e88cSAlexander Duyck 	err = fbnic_mbx_map_tlv_msg(fbd, msg);
57820d2e88cSAlexander Duyck 	if (err)
57920d2e88cSAlexander Duyck 		goto free_message;
58020d2e88cSAlexander Duyck 
58120d2e88cSAlexander Duyck 	fbd->last_heartbeat_request = req_time;
58220d2e88cSAlexander Duyck 
58320d2e88cSAlexander Duyck 	return err;
58420d2e88cSAlexander Duyck 
58520d2e88cSAlexander Duyck free_message:
58620d2e88cSAlexander Duyck 	free_page((unsigned long)msg);
58720d2e88cSAlexander Duyck 	return err;
58820d2e88cSAlexander Duyck }
58920d2e88cSAlexander Duyck 
fbnic_fw_heartbeat_current(struct fbnic_dev * fbd)59020d2e88cSAlexander Duyck static bool fbnic_fw_heartbeat_current(struct fbnic_dev *fbd)
59120d2e88cSAlexander Duyck {
59220d2e88cSAlexander Duyck 	unsigned long last_response = fbd->last_heartbeat_response;
59320d2e88cSAlexander Duyck 	unsigned long last_request = fbd->last_heartbeat_request;
59420d2e88cSAlexander Duyck 
59520d2e88cSAlexander Duyck 	return !time_before(last_response, last_request);
59620d2e88cSAlexander Duyck }
59720d2e88cSAlexander Duyck 
fbnic_fw_init_heartbeat(struct fbnic_dev * fbd,bool poll)59820d2e88cSAlexander Duyck int fbnic_fw_init_heartbeat(struct fbnic_dev *fbd, bool poll)
59920d2e88cSAlexander Duyck {
60020d2e88cSAlexander Duyck 	int err = -ETIMEDOUT;
60120d2e88cSAlexander Duyck 	int attempts = 50;
60220d2e88cSAlexander Duyck 
60320d2e88cSAlexander Duyck 	if (!fbnic_fw_present(fbd))
60420d2e88cSAlexander Duyck 		return -ENODEV;
60520d2e88cSAlexander Duyck 
60620d2e88cSAlexander Duyck 	while (attempts--) {
60720d2e88cSAlexander Duyck 		msleep(200);
60820d2e88cSAlexander Duyck 		if (poll)
60920d2e88cSAlexander Duyck 			fbnic_mbx_poll(fbd);
61020d2e88cSAlexander Duyck 
61120d2e88cSAlexander Duyck 		if (!fbnic_fw_heartbeat_current(fbd))
61220d2e88cSAlexander Duyck 			continue;
61320d2e88cSAlexander Duyck 
61420d2e88cSAlexander Duyck 		/* Place new message on mailbox to elicit a response */
61520d2e88cSAlexander Duyck 		err = fbnic_fw_xmit_heartbeat_message(fbd);
61620d2e88cSAlexander Duyck 		if (err)
61720d2e88cSAlexander Duyck 			dev_warn(fbd->dev,
61820d2e88cSAlexander Duyck 				 "Failed to send heartbeat message: %d\n",
61920d2e88cSAlexander Duyck 				 err);
62020d2e88cSAlexander Duyck 		break;
62120d2e88cSAlexander Duyck 	}
62220d2e88cSAlexander Duyck 
62320d2e88cSAlexander Duyck 	return err;
62420d2e88cSAlexander Duyck }
62520d2e88cSAlexander Duyck 
fbnic_fw_check_heartbeat(struct fbnic_dev * fbd)62620d2e88cSAlexander Duyck void fbnic_fw_check_heartbeat(struct fbnic_dev *fbd)
62720d2e88cSAlexander Duyck {
62820d2e88cSAlexander Duyck 	unsigned long last_request = fbd->last_heartbeat_request;
62920d2e88cSAlexander Duyck 	int err;
63020d2e88cSAlexander Duyck 
63120d2e88cSAlexander Duyck 	/* Do not check heartbeat or send another request until current
63220d2e88cSAlexander Duyck 	 * period has expired. Otherwise we might start spamming requests.
63320d2e88cSAlexander Duyck 	 */
63420d2e88cSAlexander Duyck 	if (time_is_after_jiffies(last_request + FW_HEARTBEAT_PERIOD))
63520d2e88cSAlexander Duyck 		return;
63620d2e88cSAlexander Duyck 
63720d2e88cSAlexander Duyck 	/* We already reported no mailbox. Wait for it to come back */
63820d2e88cSAlexander Duyck 	if (!fbd->fw_heartbeat_enabled)
63920d2e88cSAlexander Duyck 		return;
64020d2e88cSAlexander Duyck 
64120d2e88cSAlexander Duyck 	/* Was the last heartbeat response long time ago? */
64220d2e88cSAlexander Duyck 	if (!fbnic_fw_heartbeat_current(fbd)) {
64320d2e88cSAlexander Duyck 		dev_warn(fbd->dev,
64420d2e88cSAlexander Duyck 			 "Firmware did not respond to heartbeat message\n");
64520d2e88cSAlexander Duyck 		fbd->fw_heartbeat_enabled = false;
64620d2e88cSAlexander Duyck 	}
64720d2e88cSAlexander Duyck 
64820d2e88cSAlexander Duyck 	/* Place new message on mailbox to elicit a response */
64920d2e88cSAlexander Duyck 	err = fbnic_fw_xmit_heartbeat_message(fbd);
65020d2e88cSAlexander Duyck 	if (err)
65120d2e88cSAlexander Duyck 		dev_warn(fbd->dev, "Failed to send heartbeat message\n");
65220d2e88cSAlexander Duyck }
65320d2e88cSAlexander Duyck 
654da3cde08SAlexander Duyck static const struct fbnic_tlv_parser fbnic_fw_tlv_parser[] = {
65520d2e88cSAlexander Duyck 	FBNIC_TLV_PARSER(FW_CAP_RESP, fbnic_fw_cap_resp_index,
65620d2e88cSAlexander Duyck 			 fbnic_fw_parse_cap_resp),
65720d2e88cSAlexander Duyck 	FBNIC_TLV_PARSER(OWNERSHIP_RESP, fbnic_ownership_resp_index,
65820d2e88cSAlexander Duyck 			 fbnic_fw_parse_ownership_resp),
65920d2e88cSAlexander Duyck 	FBNIC_TLV_PARSER(HEARTBEAT_RESP, fbnic_heartbeat_resp_index,
66020d2e88cSAlexander Duyck 			 fbnic_fw_parse_heartbeat_resp),
661da3cde08SAlexander Duyck 	FBNIC_TLV_MSG_ERROR
662da3cde08SAlexander Duyck };
663da3cde08SAlexander Duyck 
fbnic_mbx_process_rx_msgs(struct fbnic_dev * fbd)664da3cde08SAlexander Duyck static void fbnic_mbx_process_rx_msgs(struct fbnic_dev *fbd)
665da3cde08SAlexander Duyck {
666da3cde08SAlexander Duyck 	struct fbnic_fw_mbx *rx_mbx = &fbd->mbx[FBNIC_IPC_MBX_RX_IDX];
667da3cde08SAlexander Duyck 	u8 head = rx_mbx->head;
668da3cde08SAlexander Duyck 	u64 desc, length;
669da3cde08SAlexander Duyck 
670da3cde08SAlexander Duyck 	while (head != rx_mbx->tail) {
671da3cde08SAlexander Duyck 		struct fbnic_tlv_msg *msg;
672da3cde08SAlexander Duyck 		int err;
673da3cde08SAlexander Duyck 
674da3cde08SAlexander Duyck 		desc = __fbnic_mbx_rd_desc(fbd, FBNIC_IPC_MBX_RX_IDX, head);
675da3cde08SAlexander Duyck 		if (!(desc & FBNIC_IPC_MBX_DESC_FW_CMPL))
676da3cde08SAlexander Duyck 			break;
677da3cde08SAlexander Duyck 
678da3cde08SAlexander Duyck 		dma_unmap_single(fbd->dev, rx_mbx->buf_info[head].addr,
679da3cde08SAlexander Duyck 				 PAGE_SIZE, DMA_FROM_DEVICE);
680da3cde08SAlexander Duyck 
681da3cde08SAlexander Duyck 		msg = rx_mbx->buf_info[head].msg;
682da3cde08SAlexander Duyck 
683da3cde08SAlexander Duyck 		length = FIELD_GET(FBNIC_IPC_MBX_DESC_LEN_MASK, desc);
684da3cde08SAlexander Duyck 
685da3cde08SAlexander Duyck 		/* Ignore NULL mailbox descriptors */
686da3cde08SAlexander Duyck 		if (!length)
687da3cde08SAlexander Duyck 			goto next_page;
688da3cde08SAlexander Duyck 
689da3cde08SAlexander Duyck 		/* Report descriptors with length greater than page size */
690da3cde08SAlexander Duyck 		if (length > PAGE_SIZE) {
691da3cde08SAlexander Duyck 			dev_warn(fbd->dev,
692da3cde08SAlexander Duyck 				 "Invalid mailbox descriptor length: %lld\n",
693da3cde08SAlexander Duyck 				 length);
694da3cde08SAlexander Duyck 			goto next_page;
695da3cde08SAlexander Duyck 		}
696da3cde08SAlexander Duyck 
697da3cde08SAlexander Duyck 		if (le16_to_cpu(msg->hdr.len) * sizeof(u32) > length)
698da3cde08SAlexander Duyck 			dev_warn(fbd->dev, "Mailbox message length mismatch\n");
699da3cde08SAlexander Duyck 
700da3cde08SAlexander Duyck 		/* If parsing fails dump contents of message to dmesg */
701da3cde08SAlexander Duyck 		err = fbnic_tlv_msg_parse(fbd, msg, fbnic_fw_tlv_parser);
702da3cde08SAlexander Duyck 		if (err) {
703da3cde08SAlexander Duyck 			dev_warn(fbd->dev, "Unable to process message: %d\n",
704da3cde08SAlexander Duyck 				 err);
705da3cde08SAlexander Duyck 			print_hex_dump(KERN_WARNING, "fbnic:",
706da3cde08SAlexander Duyck 				       DUMP_PREFIX_OFFSET, 16, 2,
707da3cde08SAlexander Duyck 				       msg, length, true);
708da3cde08SAlexander Duyck 		}
709da3cde08SAlexander Duyck 
710da3cde08SAlexander Duyck 		dev_dbg(fbd->dev, "Parsed msg type %d\n", msg->hdr.type);
711da3cde08SAlexander Duyck next_page:
712da3cde08SAlexander Duyck 
713da3cde08SAlexander Duyck 		free_page((unsigned long)rx_mbx->buf_info[head].msg);
714da3cde08SAlexander Duyck 		rx_mbx->buf_info[head].msg = NULL;
715da3cde08SAlexander Duyck 
716da3cde08SAlexander Duyck 		head++;
717da3cde08SAlexander Duyck 		head %= FBNIC_IPC_MBX_DESC_LEN;
718da3cde08SAlexander Duyck 	}
719da3cde08SAlexander Duyck 
720da3cde08SAlexander Duyck 	/* Record head for next interrupt */
721da3cde08SAlexander Duyck 	rx_mbx->head = head;
722da3cde08SAlexander Duyck 
723da3cde08SAlexander Duyck 	/* Make sure we have at least one page for the FW to write to */
724da3cde08SAlexander Duyck 	fbnic_mbx_alloc_rx_msgs(fbd);
725da3cde08SAlexander Duyck }
726da3cde08SAlexander Duyck 
fbnic_mbx_poll(struct fbnic_dev * fbd)727da3cde08SAlexander Duyck void fbnic_mbx_poll(struct fbnic_dev *fbd)
728da3cde08SAlexander Duyck {
729da3cde08SAlexander Duyck 	fbnic_mbx_postinit(fbd);
730da3cde08SAlexander Duyck 
731da3cde08SAlexander Duyck 	fbnic_mbx_process_tx_msgs(fbd);
732da3cde08SAlexander Duyck 	fbnic_mbx_process_rx_msgs(fbd);
733da3cde08SAlexander Duyck }
734da3cde08SAlexander Duyck 
fbnic_mbx_poll_tx_ready(struct fbnic_dev * fbd)735da3cde08SAlexander Duyck int fbnic_mbx_poll_tx_ready(struct fbnic_dev *fbd)
736da3cde08SAlexander Duyck {
737da3cde08SAlexander Duyck 	struct fbnic_fw_mbx *tx_mbx;
738da3cde08SAlexander Duyck 	int attempts = 50;
739da3cde08SAlexander Duyck 
740da3cde08SAlexander Duyck 	/* Immediate fail if BAR4 isn't there */
741da3cde08SAlexander Duyck 	if (!fbnic_fw_present(fbd))
742da3cde08SAlexander Duyck 		return -ENODEV;
743da3cde08SAlexander Duyck 
744da3cde08SAlexander Duyck 	tx_mbx = &fbd->mbx[FBNIC_IPC_MBX_TX_IDX];
745da3cde08SAlexander Duyck 	while (!tx_mbx->ready && --attempts) {
746da3cde08SAlexander Duyck 		/* Force the firmware to trigger an interrupt response to
747da3cde08SAlexander Duyck 		 * avoid the mailbox getting stuck closed if the interrupt
748da3cde08SAlexander Duyck 		 * is reset.
749da3cde08SAlexander Duyck 		 */
750da3cde08SAlexander Duyck 		fbnic_mbx_init_desc_ring(fbd, FBNIC_IPC_MBX_TX_IDX);
751da3cde08SAlexander Duyck 
752da3cde08SAlexander Duyck 		msleep(200);
753da3cde08SAlexander Duyck 
754da3cde08SAlexander Duyck 		fbnic_mbx_poll(fbd);
755da3cde08SAlexander Duyck 	}
756da3cde08SAlexander Duyck 
757da3cde08SAlexander Duyck 	return attempts ? 0 : -ETIMEDOUT;
758da3cde08SAlexander Duyck }
759da3cde08SAlexander Duyck 
fbnic_mbx_flush_tx(struct fbnic_dev * fbd)760da3cde08SAlexander Duyck void fbnic_mbx_flush_tx(struct fbnic_dev *fbd)
761da3cde08SAlexander Duyck {
762da3cde08SAlexander Duyck 	struct fbnic_fw_mbx *tx_mbx;
763da3cde08SAlexander Duyck 	int attempts = 50;
764da3cde08SAlexander Duyck 	u8 count = 0;
765da3cde08SAlexander Duyck 
766da3cde08SAlexander Duyck 	/* Nothing to do if there is no mailbox */
767da3cde08SAlexander Duyck 	if (!fbnic_fw_present(fbd))
768da3cde08SAlexander Duyck 		return;
769da3cde08SAlexander Duyck 
770da3cde08SAlexander Duyck 	/* Record current Rx stats */
771da3cde08SAlexander Duyck 	tx_mbx = &fbd->mbx[FBNIC_IPC_MBX_TX_IDX];
772da3cde08SAlexander Duyck 
773da3cde08SAlexander Duyck 	/* Nothing to do if mailbox never got to ready */
774da3cde08SAlexander Duyck 	if (!tx_mbx->ready)
775da3cde08SAlexander Duyck 		return;
776da3cde08SAlexander Duyck 
777da3cde08SAlexander Duyck 	/* Give firmware time to process packet,
778da3cde08SAlexander Duyck 	 * we will wait up to 10 seconds which is 50 waits of 200ms.
779da3cde08SAlexander Duyck 	 */
780da3cde08SAlexander Duyck 	do {
781da3cde08SAlexander Duyck 		u8 head = tx_mbx->head;
782da3cde08SAlexander Duyck 
783da3cde08SAlexander Duyck 		if (head == tx_mbx->tail)
784da3cde08SAlexander Duyck 			break;
785da3cde08SAlexander Duyck 
786da3cde08SAlexander Duyck 		msleep(200);
787da3cde08SAlexander Duyck 		fbnic_mbx_process_tx_msgs(fbd);
788da3cde08SAlexander Duyck 
789da3cde08SAlexander Duyck 		count += (tx_mbx->head - head) % FBNIC_IPC_MBX_DESC_LEN;
790da3cde08SAlexander Duyck 	} while (count < FBNIC_IPC_MBX_DESC_LEN && --attempts);
791da3cde08SAlexander Duyck }
792*bd2557a5SMohsin Bashir 
fbnic_get_fw_ver_commit_str(struct fbnic_dev * fbd,char * fw_version,const size_t str_sz)793*bd2557a5SMohsin Bashir void fbnic_get_fw_ver_commit_str(struct fbnic_dev *fbd, char *fw_version,
794*bd2557a5SMohsin Bashir 				 const size_t str_sz)
795*bd2557a5SMohsin Bashir {
796*bd2557a5SMohsin Bashir 	struct fbnic_fw_ver *mgmt = &fbd->fw_cap.running.mgmt;
797*bd2557a5SMohsin Bashir 	const char *delim = "";
798*bd2557a5SMohsin Bashir 
799*bd2557a5SMohsin Bashir 	if (mgmt->commit[0])
800*bd2557a5SMohsin Bashir 		delim = "_";
801*bd2557a5SMohsin Bashir 
802*bd2557a5SMohsin Bashir 	fbnic_mk_full_fw_ver_str(mgmt->version, delim, mgmt->commit,
803*bd2557a5SMohsin Bashir 				 fw_version, str_sz);
804*bd2557a5SMohsin Bashir }
805