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