18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
28c2ecf20Sopenharmony_ci/* QLogic qed NIC Driver
38c2ecf20Sopenharmony_ci * Copyright (c) 2015-2017  QLogic Corporation
48c2ecf20Sopenharmony_ci * Copyright (c) 2019-2020 Marvell International Ltd.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/types.h>
88c2ecf20Sopenharmony_ci#include <asm/byteorder.h>
98c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
108c2ecf20Sopenharmony_ci#include <linux/if_vlan.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/pci.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/stddef.h>
158c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
168c2ecf20Sopenharmony_ci#include <net/ipv6.h>
178c2ecf20Sopenharmony_ci#include <linux/bitops.h>
188c2ecf20Sopenharmony_ci#include <linux/delay.h>
198c2ecf20Sopenharmony_ci#include <linux/errno.h>
208c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
218c2ecf20Sopenharmony_ci#include <linux/io.h>
228c2ecf20Sopenharmony_ci#include <linux/list.h>
238c2ecf20Sopenharmony_ci#include <linux/mutex.h>
248c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
258c2ecf20Sopenharmony_ci#include <linux/string.h>
268c2ecf20Sopenharmony_ci#include <linux/qed/qed_ll2_if.h>
278c2ecf20Sopenharmony_ci#include "qed.h"
288c2ecf20Sopenharmony_ci#include "qed_cxt.h"
298c2ecf20Sopenharmony_ci#include "qed_dev_api.h"
308c2ecf20Sopenharmony_ci#include "qed_hsi.h"
318c2ecf20Sopenharmony_ci#include "qed_hw.h"
328c2ecf20Sopenharmony_ci#include "qed_int.h"
338c2ecf20Sopenharmony_ci#include "qed_ll2.h"
348c2ecf20Sopenharmony_ci#include "qed_mcp.h"
358c2ecf20Sopenharmony_ci#include "qed_ooo.h"
368c2ecf20Sopenharmony_ci#include "qed_reg_addr.h"
378c2ecf20Sopenharmony_ci#include "qed_sp.h"
388c2ecf20Sopenharmony_ci#include "qed_rdma.h"
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define QED_LL2_RX_REGISTERED(ll2)	((ll2)->rx_queue.b_cb_registered)
418c2ecf20Sopenharmony_ci#define QED_LL2_TX_REGISTERED(ll2)	((ll2)->tx_queue.b_cb_registered)
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define QED_LL2_TX_SIZE (256)
448c2ecf20Sopenharmony_ci#define QED_LL2_RX_SIZE (4096)
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistruct qed_cb_ll2_info {
478c2ecf20Sopenharmony_ci	int rx_cnt;
488c2ecf20Sopenharmony_ci	u32 rx_size;
498c2ecf20Sopenharmony_ci	u8 handle;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	/* Lock protecting LL2 buffer lists in sleepless context */
528c2ecf20Sopenharmony_ci	spinlock_t lock;
538c2ecf20Sopenharmony_ci	struct list_head list;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	const struct qed_ll2_cb_ops *cbs;
568c2ecf20Sopenharmony_ci	void *cb_cookie;
578c2ecf20Sopenharmony_ci};
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistruct qed_ll2_buffer {
608c2ecf20Sopenharmony_ci	struct list_head list;
618c2ecf20Sopenharmony_ci	void *data;
628c2ecf20Sopenharmony_ci	dma_addr_t phys_addr;
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic void qed_ll2b_complete_tx_packet(void *cxt,
668c2ecf20Sopenharmony_ci					u8 connection_handle,
678c2ecf20Sopenharmony_ci					void *cookie,
688c2ecf20Sopenharmony_ci					dma_addr_t first_frag_addr,
698c2ecf20Sopenharmony_ci					bool b_last_fragment,
708c2ecf20Sopenharmony_ci					bool b_last_packet)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	struct qed_hwfn *p_hwfn = cxt;
738c2ecf20Sopenharmony_ci	struct qed_dev *cdev = p_hwfn->cdev;
748c2ecf20Sopenharmony_ci	struct sk_buff *skb = cookie;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	/* All we need to do is release the mapping */
778c2ecf20Sopenharmony_ci	dma_unmap_single(&p_hwfn->cdev->pdev->dev, first_frag_addr,
788c2ecf20Sopenharmony_ci			 skb_headlen(skb), DMA_TO_DEVICE);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	if (cdev->ll2->cbs && cdev->ll2->cbs->tx_cb)
818c2ecf20Sopenharmony_ci		cdev->ll2->cbs->tx_cb(cdev->ll2->cb_cookie, skb,
828c2ecf20Sopenharmony_ci				      b_last_fragment);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	dev_kfree_skb_any(skb);
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int qed_ll2_alloc_buffer(struct qed_dev *cdev,
888c2ecf20Sopenharmony_ci				u8 **data, dma_addr_t *phys_addr)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	size_t size = cdev->ll2->rx_size + NET_SKB_PAD +
918c2ecf20Sopenharmony_ci		      SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	*data = kmalloc(size, GFP_ATOMIC);
948c2ecf20Sopenharmony_ci	if (!(*data)) {
958c2ecf20Sopenharmony_ci		DP_INFO(cdev, "Failed to allocate LL2 buffer data\n");
968c2ecf20Sopenharmony_ci		return -ENOMEM;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	*phys_addr = dma_map_single(&cdev->pdev->dev,
1008c2ecf20Sopenharmony_ci				    ((*data) + NET_SKB_PAD),
1018c2ecf20Sopenharmony_ci				    cdev->ll2->rx_size, DMA_FROM_DEVICE);
1028c2ecf20Sopenharmony_ci	if (dma_mapping_error(&cdev->pdev->dev, *phys_addr)) {
1038c2ecf20Sopenharmony_ci		DP_INFO(cdev, "Failed to map LL2 buffer data\n");
1048c2ecf20Sopenharmony_ci		kfree((*data));
1058c2ecf20Sopenharmony_ci		return -ENOMEM;
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	return 0;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic int qed_ll2_dealloc_buffer(struct qed_dev *cdev,
1128c2ecf20Sopenharmony_ci				 struct qed_ll2_buffer *buffer)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	spin_lock_bh(&cdev->ll2->lock);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	dma_unmap_single(&cdev->pdev->dev, buffer->phys_addr,
1178c2ecf20Sopenharmony_ci			 cdev->ll2->rx_size, DMA_FROM_DEVICE);
1188c2ecf20Sopenharmony_ci	kfree(buffer->data);
1198c2ecf20Sopenharmony_ci	list_del(&buffer->list);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	cdev->ll2->rx_cnt--;
1228c2ecf20Sopenharmony_ci	if (!cdev->ll2->rx_cnt)
1238c2ecf20Sopenharmony_ci		DP_INFO(cdev, "All LL2 entries were removed\n");
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	spin_unlock_bh(&cdev->ll2->lock);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	return 0;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic void qed_ll2_kill_buffers(struct qed_dev *cdev)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	struct qed_ll2_buffer *buffer, *tmp_buffer;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	list_for_each_entry_safe(buffer, tmp_buffer, &cdev->ll2->list, list)
1358c2ecf20Sopenharmony_ci		qed_ll2_dealloc_buffer(cdev, buffer);
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic void qed_ll2b_complete_rx_packet(void *cxt,
1398c2ecf20Sopenharmony_ci					struct qed_ll2_comp_rx_data *data)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	struct qed_hwfn *p_hwfn = cxt;
1428c2ecf20Sopenharmony_ci	struct qed_ll2_buffer *buffer = data->cookie;
1438c2ecf20Sopenharmony_ci	struct qed_dev *cdev = p_hwfn->cdev;
1448c2ecf20Sopenharmony_ci	dma_addr_t new_phys_addr;
1458c2ecf20Sopenharmony_ci	struct sk_buff *skb;
1468c2ecf20Sopenharmony_ci	bool reuse = false;
1478c2ecf20Sopenharmony_ci	int rc = -EINVAL;
1488c2ecf20Sopenharmony_ci	u8 *new_data;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	DP_VERBOSE(p_hwfn,
1518c2ecf20Sopenharmony_ci		   (NETIF_MSG_RX_STATUS | QED_MSG_STORAGE | NETIF_MSG_PKTDATA),
1528c2ecf20Sopenharmony_ci		   "Got an LL2 Rx completion: [Buffer at phys 0x%llx, offset 0x%02x] Length 0x%04x Parse_flags 0x%04x vlan 0x%04x Opaque data [0x%08x:0x%08x]\n",
1538c2ecf20Sopenharmony_ci		   (u64)data->rx_buf_addr,
1548c2ecf20Sopenharmony_ci		   data->u.placement_offset,
1558c2ecf20Sopenharmony_ci		   data->length.packet_length,
1568c2ecf20Sopenharmony_ci		   data->parse_flags,
1578c2ecf20Sopenharmony_ci		   data->vlan, data->opaque_data_0, data->opaque_data_1);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if ((cdev->dp_module & NETIF_MSG_PKTDATA) && buffer->data) {
1608c2ecf20Sopenharmony_ci		print_hex_dump(KERN_INFO, "",
1618c2ecf20Sopenharmony_ci			       DUMP_PREFIX_OFFSET, 16, 1,
1628c2ecf20Sopenharmony_ci			       buffer->data, data->length.packet_length, false);
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	/* Determine if data is valid */
1668c2ecf20Sopenharmony_ci	if (data->length.packet_length < ETH_HLEN)
1678c2ecf20Sopenharmony_ci		reuse = true;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	/* Allocate a replacement for buffer; Reuse upon failure */
1708c2ecf20Sopenharmony_ci	if (!reuse)
1718c2ecf20Sopenharmony_ci		rc = qed_ll2_alloc_buffer(p_hwfn->cdev, &new_data,
1728c2ecf20Sopenharmony_ci					  &new_phys_addr);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	/* If need to reuse or there's no replacement buffer, repost this */
1758c2ecf20Sopenharmony_ci	if (rc)
1768c2ecf20Sopenharmony_ci		goto out_post;
1778c2ecf20Sopenharmony_ci	dma_unmap_single(&cdev->pdev->dev, buffer->phys_addr,
1788c2ecf20Sopenharmony_ci			 cdev->ll2->rx_size, DMA_FROM_DEVICE);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	skb = build_skb(buffer->data, 0);
1818c2ecf20Sopenharmony_ci	if (!skb) {
1828c2ecf20Sopenharmony_ci		DP_INFO(cdev, "Failed to build SKB\n");
1838c2ecf20Sopenharmony_ci		kfree(buffer->data);
1848c2ecf20Sopenharmony_ci		goto out_post1;
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	data->u.placement_offset += NET_SKB_PAD;
1888c2ecf20Sopenharmony_ci	skb_reserve(skb, data->u.placement_offset);
1898c2ecf20Sopenharmony_ci	skb_put(skb, data->length.packet_length);
1908c2ecf20Sopenharmony_ci	skb_checksum_none_assert(skb);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	/* Get parital ethernet information instead of eth_type_trans(),
1938c2ecf20Sopenharmony_ci	 * Since we don't have an associated net_device.
1948c2ecf20Sopenharmony_ci	 */
1958c2ecf20Sopenharmony_ci	skb_reset_mac_header(skb);
1968c2ecf20Sopenharmony_ci	skb->protocol = eth_hdr(skb)->h_proto;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	/* Pass SKB onward */
1998c2ecf20Sopenharmony_ci	if (cdev->ll2->cbs && cdev->ll2->cbs->rx_cb) {
2008c2ecf20Sopenharmony_ci		if (data->vlan)
2018c2ecf20Sopenharmony_ci			__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
2028c2ecf20Sopenharmony_ci					       data->vlan);
2038c2ecf20Sopenharmony_ci		cdev->ll2->cbs->rx_cb(cdev->ll2->cb_cookie, skb,
2048c2ecf20Sopenharmony_ci				      data->opaque_data_0,
2058c2ecf20Sopenharmony_ci				      data->opaque_data_1);
2068c2ecf20Sopenharmony_ci	} else {
2078c2ecf20Sopenharmony_ci		DP_VERBOSE(p_hwfn, (NETIF_MSG_RX_STATUS | NETIF_MSG_PKTDATA |
2088c2ecf20Sopenharmony_ci				    QED_MSG_LL2 | QED_MSG_STORAGE),
2098c2ecf20Sopenharmony_ci			   "Dropping the packet\n");
2108c2ecf20Sopenharmony_ci		kfree(buffer->data);
2118c2ecf20Sopenharmony_ci	}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ciout_post1:
2148c2ecf20Sopenharmony_ci	/* Update Buffer information and update FW producer */
2158c2ecf20Sopenharmony_ci	buffer->data = new_data;
2168c2ecf20Sopenharmony_ci	buffer->phys_addr = new_phys_addr;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ciout_post:
2198c2ecf20Sopenharmony_ci	rc = qed_ll2_post_rx_buffer(p_hwfn, cdev->ll2->handle,
2208c2ecf20Sopenharmony_ci				    buffer->phys_addr, 0, buffer, 1);
2218c2ecf20Sopenharmony_ci	if (rc)
2228c2ecf20Sopenharmony_ci		qed_ll2_dealloc_buffer(cdev, buffer);
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic struct qed_ll2_info *__qed_ll2_handle_sanity(struct qed_hwfn *p_hwfn,
2268c2ecf20Sopenharmony_ci						    u8 connection_handle,
2278c2ecf20Sopenharmony_ci						    bool b_lock,
2288c2ecf20Sopenharmony_ci						    bool b_only_active)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	struct qed_ll2_info *p_ll2_conn, *p_ret = NULL;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	if (connection_handle >= QED_MAX_NUM_OF_LL2_CONNECTIONS)
2338c2ecf20Sopenharmony_ci		return NULL;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	if (!p_hwfn->p_ll2_info)
2368c2ecf20Sopenharmony_ci		return NULL;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	p_ll2_conn = &p_hwfn->p_ll2_info[connection_handle];
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (b_only_active) {
2418c2ecf20Sopenharmony_ci		if (b_lock)
2428c2ecf20Sopenharmony_ci			mutex_lock(&p_ll2_conn->mutex);
2438c2ecf20Sopenharmony_ci		if (p_ll2_conn->b_active)
2448c2ecf20Sopenharmony_ci			p_ret = p_ll2_conn;
2458c2ecf20Sopenharmony_ci		if (b_lock)
2468c2ecf20Sopenharmony_ci			mutex_unlock(&p_ll2_conn->mutex);
2478c2ecf20Sopenharmony_ci	} else {
2488c2ecf20Sopenharmony_ci		p_ret = p_ll2_conn;
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	return p_ret;
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic struct qed_ll2_info *qed_ll2_handle_sanity(struct qed_hwfn *p_hwfn,
2558c2ecf20Sopenharmony_ci						  u8 connection_handle)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	return __qed_ll2_handle_sanity(p_hwfn, connection_handle, false, true);
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic struct qed_ll2_info *qed_ll2_handle_sanity_lock(struct qed_hwfn *p_hwfn,
2618c2ecf20Sopenharmony_ci						       u8 connection_handle)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	return __qed_ll2_handle_sanity(p_hwfn, connection_handle, true, true);
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic struct qed_ll2_info *qed_ll2_handle_sanity_inactive(struct qed_hwfn
2678c2ecf20Sopenharmony_ci							   *p_hwfn,
2688c2ecf20Sopenharmony_ci							   u8 connection_handle)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	return __qed_ll2_handle_sanity(p_hwfn, connection_handle, false, false);
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic void qed_ll2_txq_flush(struct qed_hwfn *p_hwfn, u8 connection_handle)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	bool b_last_packet = false, b_last_frag = false;
2768c2ecf20Sopenharmony_ci	struct qed_ll2_tx_packet *p_pkt = NULL;
2778c2ecf20Sopenharmony_ci	struct qed_ll2_info *p_ll2_conn;
2788c2ecf20Sopenharmony_ci	struct qed_ll2_tx_queue *p_tx;
2798c2ecf20Sopenharmony_ci	unsigned long flags = 0;
2808c2ecf20Sopenharmony_ci	dma_addr_t tx_frag;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	p_ll2_conn = qed_ll2_handle_sanity_inactive(p_hwfn, connection_handle);
2838c2ecf20Sopenharmony_ci	if (!p_ll2_conn)
2848c2ecf20Sopenharmony_ci		return;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	p_tx = &p_ll2_conn->tx_queue;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	spin_lock_irqsave(&p_tx->lock, flags);
2898c2ecf20Sopenharmony_ci	while (!list_empty(&p_tx->active_descq)) {
2908c2ecf20Sopenharmony_ci		p_pkt = list_first_entry(&p_tx->active_descq,
2918c2ecf20Sopenharmony_ci					 struct qed_ll2_tx_packet, list_entry);
2928c2ecf20Sopenharmony_ci		if (!p_pkt)
2938c2ecf20Sopenharmony_ci			break;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci		list_del(&p_pkt->list_entry);
2968c2ecf20Sopenharmony_ci		b_last_packet = list_empty(&p_tx->active_descq);
2978c2ecf20Sopenharmony_ci		list_add_tail(&p_pkt->list_entry, &p_tx->free_descq);
2988c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&p_tx->lock, flags);
2998c2ecf20Sopenharmony_ci		if (p_ll2_conn->input.conn_type == QED_LL2_TYPE_OOO) {
3008c2ecf20Sopenharmony_ci			struct qed_ooo_buffer *p_buffer;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci			p_buffer = (struct qed_ooo_buffer *)p_pkt->cookie;
3038c2ecf20Sopenharmony_ci			qed_ooo_put_free_buffer(p_hwfn, p_hwfn->p_ooo_info,
3048c2ecf20Sopenharmony_ci						p_buffer);
3058c2ecf20Sopenharmony_ci		} else {
3068c2ecf20Sopenharmony_ci			p_tx->cur_completing_packet = *p_pkt;
3078c2ecf20Sopenharmony_ci			p_tx->cur_completing_bd_idx = 1;
3088c2ecf20Sopenharmony_ci			b_last_frag =
3098c2ecf20Sopenharmony_ci				p_tx->cur_completing_bd_idx == p_pkt->bd_used;
3108c2ecf20Sopenharmony_ci			tx_frag = p_pkt->bds_set[0].tx_frag;
3118c2ecf20Sopenharmony_ci			p_ll2_conn->cbs.tx_release_cb(p_ll2_conn->cbs.cookie,
3128c2ecf20Sopenharmony_ci						      p_ll2_conn->my_id,
3138c2ecf20Sopenharmony_ci						      p_pkt->cookie,
3148c2ecf20Sopenharmony_ci						      tx_frag,
3158c2ecf20Sopenharmony_ci						      b_last_frag,
3168c2ecf20Sopenharmony_ci						      b_last_packet);
3178c2ecf20Sopenharmony_ci		}
3188c2ecf20Sopenharmony_ci		spin_lock_irqsave(&p_tx->lock, flags);
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&p_tx->lock, flags);
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic int qed_ll2_txq_completion(struct qed_hwfn *p_hwfn, void *p_cookie)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	struct qed_ll2_info *p_ll2_conn = p_cookie;
3268c2ecf20Sopenharmony_ci	struct qed_ll2_tx_queue *p_tx = &p_ll2_conn->tx_queue;
3278c2ecf20Sopenharmony_ci	u16 new_idx = 0, num_bds = 0, num_bds_in_packet = 0;
3288c2ecf20Sopenharmony_ci	struct qed_ll2_tx_packet *p_pkt;
3298c2ecf20Sopenharmony_ci	bool b_last_frag = false;
3308c2ecf20Sopenharmony_ci	unsigned long flags;
3318c2ecf20Sopenharmony_ci	int rc = -EINVAL;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	if (!p_ll2_conn)
3348c2ecf20Sopenharmony_ci		return rc;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	spin_lock_irqsave(&p_tx->lock, flags);
3378c2ecf20Sopenharmony_ci	if (p_tx->b_completing_packet) {
3388c2ecf20Sopenharmony_ci		rc = -EBUSY;
3398c2ecf20Sopenharmony_ci		goto out;
3408c2ecf20Sopenharmony_ci	}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	new_idx = le16_to_cpu(*p_tx->p_fw_cons);
3438c2ecf20Sopenharmony_ci	num_bds = ((s16)new_idx - (s16)p_tx->bds_idx);
3448c2ecf20Sopenharmony_ci	while (num_bds) {
3458c2ecf20Sopenharmony_ci		if (list_empty(&p_tx->active_descq))
3468c2ecf20Sopenharmony_ci			goto out;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci		p_pkt = list_first_entry(&p_tx->active_descq,
3498c2ecf20Sopenharmony_ci					 struct qed_ll2_tx_packet, list_entry);
3508c2ecf20Sopenharmony_ci		if (!p_pkt)
3518c2ecf20Sopenharmony_ci			goto out;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci		p_tx->b_completing_packet = true;
3548c2ecf20Sopenharmony_ci		p_tx->cur_completing_packet = *p_pkt;
3558c2ecf20Sopenharmony_ci		num_bds_in_packet = p_pkt->bd_used;
3568c2ecf20Sopenharmony_ci		list_del(&p_pkt->list_entry);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci		if (num_bds < num_bds_in_packet) {
3598c2ecf20Sopenharmony_ci			DP_NOTICE(p_hwfn,
3608c2ecf20Sopenharmony_ci				  "Rest of BDs does not cover whole packet\n");
3618c2ecf20Sopenharmony_ci			goto out;
3628c2ecf20Sopenharmony_ci		}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci		num_bds -= num_bds_in_packet;
3658c2ecf20Sopenharmony_ci		p_tx->bds_idx += num_bds_in_packet;
3668c2ecf20Sopenharmony_ci		while (num_bds_in_packet--)
3678c2ecf20Sopenharmony_ci			qed_chain_consume(&p_tx->txq_chain);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci		p_tx->cur_completing_bd_idx = 1;
3708c2ecf20Sopenharmony_ci		b_last_frag = p_tx->cur_completing_bd_idx == p_pkt->bd_used;
3718c2ecf20Sopenharmony_ci		list_add_tail(&p_pkt->list_entry, &p_tx->free_descq);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&p_tx->lock, flags);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci		p_ll2_conn->cbs.tx_comp_cb(p_ll2_conn->cbs.cookie,
3768c2ecf20Sopenharmony_ci					   p_ll2_conn->my_id,
3778c2ecf20Sopenharmony_ci					   p_pkt->cookie,
3788c2ecf20Sopenharmony_ci					   p_pkt->bds_set[0].tx_frag,
3798c2ecf20Sopenharmony_ci					   b_last_frag, !num_bds);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci		spin_lock_irqsave(&p_tx->lock, flags);
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	p_tx->b_completing_packet = false;
3858c2ecf20Sopenharmony_ci	rc = 0;
3868c2ecf20Sopenharmony_ciout:
3878c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&p_tx->lock, flags);
3888c2ecf20Sopenharmony_ci	return rc;
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic void qed_ll2_rxq_parse_gsi(struct qed_hwfn *p_hwfn,
3928c2ecf20Sopenharmony_ci				  union core_rx_cqe_union *p_cqe,
3938c2ecf20Sopenharmony_ci				  struct qed_ll2_comp_rx_data *data)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	data->parse_flags = le16_to_cpu(p_cqe->rx_cqe_gsi.parse_flags.flags);
3968c2ecf20Sopenharmony_ci	data->length.data_length = le16_to_cpu(p_cqe->rx_cqe_gsi.data_length);
3978c2ecf20Sopenharmony_ci	data->vlan = le16_to_cpu(p_cqe->rx_cqe_gsi.vlan);
3988c2ecf20Sopenharmony_ci	data->opaque_data_0 = le32_to_cpu(p_cqe->rx_cqe_gsi.src_mac_addrhi);
3998c2ecf20Sopenharmony_ci	data->opaque_data_1 = le16_to_cpu(p_cqe->rx_cqe_gsi.src_mac_addrlo);
4008c2ecf20Sopenharmony_ci	data->u.data_length_error = p_cqe->rx_cqe_gsi.data_length_error;
4018c2ecf20Sopenharmony_ci	data->qp_id = le16_to_cpu(p_cqe->rx_cqe_gsi.qp_id);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	data->src_qp = le32_to_cpu(p_cqe->rx_cqe_gsi.src_qp);
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cistatic void qed_ll2_rxq_parse_reg(struct qed_hwfn *p_hwfn,
4078c2ecf20Sopenharmony_ci				  union core_rx_cqe_union *p_cqe,
4088c2ecf20Sopenharmony_ci				  struct qed_ll2_comp_rx_data *data)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	data->parse_flags = le16_to_cpu(p_cqe->rx_cqe_fp.parse_flags.flags);
4118c2ecf20Sopenharmony_ci	data->err_flags = le16_to_cpu(p_cqe->rx_cqe_fp.err_flags.flags);
4128c2ecf20Sopenharmony_ci	data->length.packet_length =
4138c2ecf20Sopenharmony_ci	    le16_to_cpu(p_cqe->rx_cqe_fp.packet_length);
4148c2ecf20Sopenharmony_ci	data->vlan = le16_to_cpu(p_cqe->rx_cqe_fp.vlan);
4158c2ecf20Sopenharmony_ci	data->opaque_data_0 = le32_to_cpu(p_cqe->rx_cqe_fp.opaque_data.data[0]);
4168c2ecf20Sopenharmony_ci	data->opaque_data_1 = le32_to_cpu(p_cqe->rx_cqe_fp.opaque_data.data[1]);
4178c2ecf20Sopenharmony_ci	data->u.placement_offset = p_cqe->rx_cqe_fp.placement_offset;
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cistatic int
4218c2ecf20Sopenharmony_ciqed_ll2_handle_slowpath(struct qed_hwfn *p_hwfn,
4228c2ecf20Sopenharmony_ci			struct qed_ll2_info *p_ll2_conn,
4238c2ecf20Sopenharmony_ci			union core_rx_cqe_union *p_cqe,
4248c2ecf20Sopenharmony_ci			unsigned long *p_lock_flags)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci	struct qed_ll2_rx_queue *p_rx = &p_ll2_conn->rx_queue;
4278c2ecf20Sopenharmony_ci	struct core_rx_slow_path_cqe *sp_cqe;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	sp_cqe = &p_cqe->rx_cqe_sp;
4308c2ecf20Sopenharmony_ci	if (sp_cqe->ramrod_cmd_id != CORE_RAMROD_RX_QUEUE_FLUSH) {
4318c2ecf20Sopenharmony_ci		DP_NOTICE(p_hwfn,
4328c2ecf20Sopenharmony_ci			  "LL2 - unexpected Rx CQE slowpath ramrod_cmd_id:%d\n",
4338c2ecf20Sopenharmony_ci			  sp_cqe->ramrod_cmd_id);
4348c2ecf20Sopenharmony_ci		return -EINVAL;
4358c2ecf20Sopenharmony_ci	}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	if (!p_ll2_conn->cbs.slowpath_cb) {
4388c2ecf20Sopenharmony_ci		DP_NOTICE(p_hwfn,
4398c2ecf20Sopenharmony_ci			  "LL2 - received RX_QUEUE_FLUSH but no callback was provided\n");
4408c2ecf20Sopenharmony_ci		return -EINVAL;
4418c2ecf20Sopenharmony_ci	}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&p_rx->lock, *p_lock_flags);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	p_ll2_conn->cbs.slowpath_cb(p_ll2_conn->cbs.cookie,
4468c2ecf20Sopenharmony_ci				    p_ll2_conn->my_id,
4478c2ecf20Sopenharmony_ci				    le32_to_cpu(sp_cqe->opaque_data.data[0]),
4488c2ecf20Sopenharmony_ci				    le32_to_cpu(sp_cqe->opaque_data.data[1]));
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	spin_lock_irqsave(&p_rx->lock, *p_lock_flags);
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	return 0;
4538c2ecf20Sopenharmony_ci}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_cistatic int
4568c2ecf20Sopenharmony_ciqed_ll2_rxq_handle_completion(struct qed_hwfn *p_hwfn,
4578c2ecf20Sopenharmony_ci			      struct qed_ll2_info *p_ll2_conn,
4588c2ecf20Sopenharmony_ci			      union core_rx_cqe_union *p_cqe,
4598c2ecf20Sopenharmony_ci			      unsigned long *p_lock_flags, bool b_last_cqe)
4608c2ecf20Sopenharmony_ci{
4618c2ecf20Sopenharmony_ci	struct qed_ll2_rx_queue *p_rx = &p_ll2_conn->rx_queue;
4628c2ecf20Sopenharmony_ci	struct qed_ll2_rx_packet *p_pkt = NULL;
4638c2ecf20Sopenharmony_ci	struct qed_ll2_comp_rx_data data;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	if (!list_empty(&p_rx->active_descq))
4668c2ecf20Sopenharmony_ci		p_pkt = list_first_entry(&p_rx->active_descq,
4678c2ecf20Sopenharmony_ci					 struct qed_ll2_rx_packet, list_entry);
4688c2ecf20Sopenharmony_ci	if (!p_pkt) {
4698c2ecf20Sopenharmony_ci		DP_NOTICE(p_hwfn,
4708c2ecf20Sopenharmony_ci			  "[%d] LL2 Rx completion but active_descq is empty\n",
4718c2ecf20Sopenharmony_ci			  p_ll2_conn->input.conn_type);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci		return -EIO;
4748c2ecf20Sopenharmony_ci	}
4758c2ecf20Sopenharmony_ci	list_del(&p_pkt->list_entry);
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	if (p_cqe->rx_cqe_sp.type == CORE_RX_CQE_TYPE_REGULAR)
4788c2ecf20Sopenharmony_ci		qed_ll2_rxq_parse_reg(p_hwfn, p_cqe, &data);
4798c2ecf20Sopenharmony_ci	else
4808c2ecf20Sopenharmony_ci		qed_ll2_rxq_parse_gsi(p_hwfn, p_cqe, &data);
4818c2ecf20Sopenharmony_ci	if (qed_chain_consume(&p_rx->rxq_chain) != p_pkt->rxq_bd)
4828c2ecf20Sopenharmony_ci		DP_NOTICE(p_hwfn,
4838c2ecf20Sopenharmony_ci			  "Mismatch between active_descq and the LL2 Rx chain\n");
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	list_add_tail(&p_pkt->list_entry, &p_rx->free_descq);
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	data.connection_handle = p_ll2_conn->my_id;
4888c2ecf20Sopenharmony_ci	data.cookie = p_pkt->cookie;
4898c2ecf20Sopenharmony_ci	data.rx_buf_addr = p_pkt->rx_buf_addr;
4908c2ecf20Sopenharmony_ci	data.b_last_packet = b_last_cqe;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&p_rx->lock, *p_lock_flags);
4938c2ecf20Sopenharmony_ci	p_ll2_conn->cbs.rx_comp_cb(p_ll2_conn->cbs.cookie, &data);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	spin_lock_irqsave(&p_rx->lock, *p_lock_flags);
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	return 0;
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_cistatic int qed_ll2_rxq_completion(struct qed_hwfn *p_hwfn, void *cookie)
5018c2ecf20Sopenharmony_ci{
5028c2ecf20Sopenharmony_ci	struct qed_ll2_info *p_ll2_conn = (struct qed_ll2_info *)cookie;
5038c2ecf20Sopenharmony_ci	struct qed_ll2_rx_queue *p_rx = &p_ll2_conn->rx_queue;
5048c2ecf20Sopenharmony_ci	union core_rx_cqe_union *cqe = NULL;
5058c2ecf20Sopenharmony_ci	u16 cq_new_idx = 0, cq_old_idx = 0;
5068c2ecf20Sopenharmony_ci	unsigned long flags = 0;
5078c2ecf20Sopenharmony_ci	int rc = 0;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	if (!p_ll2_conn)
5108c2ecf20Sopenharmony_ci		return rc;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	spin_lock_irqsave(&p_rx->lock, flags);
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	if (!QED_LL2_RX_REGISTERED(p_ll2_conn)) {
5158c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&p_rx->lock, flags);
5168c2ecf20Sopenharmony_ci		return 0;
5178c2ecf20Sopenharmony_ci	}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	cq_new_idx = le16_to_cpu(*p_rx->p_fw_cons);
5208c2ecf20Sopenharmony_ci	cq_old_idx = qed_chain_get_cons_idx(&p_rx->rcq_chain);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	while (cq_new_idx != cq_old_idx) {
5238c2ecf20Sopenharmony_ci		bool b_last_cqe = (cq_new_idx == cq_old_idx);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci		cqe =
5268c2ecf20Sopenharmony_ci		    (union core_rx_cqe_union *)
5278c2ecf20Sopenharmony_ci		    qed_chain_consume(&p_rx->rcq_chain);
5288c2ecf20Sopenharmony_ci		cq_old_idx = qed_chain_get_cons_idx(&p_rx->rcq_chain);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci		DP_VERBOSE(p_hwfn,
5318c2ecf20Sopenharmony_ci			   QED_MSG_LL2,
5328c2ecf20Sopenharmony_ci			   "LL2 [sw. cons %04x, fw. at %04x] - Got Packet of type %02x\n",
5338c2ecf20Sopenharmony_ci			   cq_old_idx, cq_new_idx, cqe->rx_cqe_sp.type);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci		switch (cqe->rx_cqe_sp.type) {
5368c2ecf20Sopenharmony_ci		case CORE_RX_CQE_TYPE_SLOW_PATH:
5378c2ecf20Sopenharmony_ci			rc = qed_ll2_handle_slowpath(p_hwfn, p_ll2_conn,
5388c2ecf20Sopenharmony_ci						     cqe, &flags);
5398c2ecf20Sopenharmony_ci			break;
5408c2ecf20Sopenharmony_ci		case CORE_RX_CQE_TYPE_GSI_OFFLOAD:
5418c2ecf20Sopenharmony_ci		case CORE_RX_CQE_TYPE_REGULAR:
5428c2ecf20Sopenharmony_ci			rc = qed_ll2_rxq_handle_completion(p_hwfn, p_ll2_conn,
5438c2ecf20Sopenharmony_ci							   cqe, &flags,
5448c2ecf20Sopenharmony_ci							   b_last_cqe);
5458c2ecf20Sopenharmony_ci			break;
5468c2ecf20Sopenharmony_ci		default:
5478c2ecf20Sopenharmony_ci			rc = -EIO;
5488c2ecf20Sopenharmony_ci		}
5498c2ecf20Sopenharmony_ci	}
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&p_rx->lock, flags);
5528c2ecf20Sopenharmony_ci	return rc;
5538c2ecf20Sopenharmony_ci}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_cistatic void qed_ll2_rxq_flush(struct qed_hwfn *p_hwfn, u8 connection_handle)
5568c2ecf20Sopenharmony_ci{
5578c2ecf20Sopenharmony_ci	struct qed_ll2_info *p_ll2_conn = NULL;
5588c2ecf20Sopenharmony_ci	struct qed_ll2_rx_packet *p_pkt = NULL;
5598c2ecf20Sopenharmony_ci	struct qed_ll2_rx_queue *p_rx;
5608c2ecf20Sopenharmony_ci	unsigned long flags = 0;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	p_ll2_conn = qed_ll2_handle_sanity_inactive(p_hwfn, connection_handle);
5638c2ecf20Sopenharmony_ci	if (!p_ll2_conn)
5648c2ecf20Sopenharmony_ci		return;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	p_rx = &p_ll2_conn->rx_queue;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	spin_lock_irqsave(&p_rx->lock, flags);
5698c2ecf20Sopenharmony_ci	while (!list_empty(&p_rx->active_descq)) {
5708c2ecf20Sopenharmony_ci		p_pkt = list_first_entry(&p_rx->active_descq,
5718c2ecf20Sopenharmony_ci					 struct qed_ll2_rx_packet, list_entry);
5728c2ecf20Sopenharmony_ci		if (!p_pkt)
5738c2ecf20Sopenharmony_ci			break;
5748c2ecf20Sopenharmony_ci		list_move_tail(&p_pkt->list_entry, &p_rx->free_descq);
5758c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&p_rx->lock, flags);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci		if (p_ll2_conn->input.conn_type == QED_LL2_TYPE_OOO) {
5788c2ecf20Sopenharmony_ci			struct qed_ooo_buffer *p_buffer;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci			p_buffer = (struct qed_ooo_buffer *)p_pkt->cookie;
5818c2ecf20Sopenharmony_ci			qed_ooo_put_free_buffer(p_hwfn, p_hwfn->p_ooo_info,
5828c2ecf20Sopenharmony_ci						p_buffer);
5838c2ecf20Sopenharmony_ci		} else {
5848c2ecf20Sopenharmony_ci			dma_addr_t rx_buf_addr = p_pkt->rx_buf_addr;
5858c2ecf20Sopenharmony_ci			void *cookie = p_pkt->cookie;
5868c2ecf20Sopenharmony_ci			bool b_last;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci			b_last = list_empty(&p_rx->active_descq);
5898c2ecf20Sopenharmony_ci			p_ll2_conn->cbs.rx_release_cb(p_ll2_conn->cbs.cookie,
5908c2ecf20Sopenharmony_ci						      p_ll2_conn->my_id,
5918c2ecf20Sopenharmony_ci						      cookie,
5928c2ecf20Sopenharmony_ci						      rx_buf_addr, b_last);
5938c2ecf20Sopenharmony_ci		}
5948c2ecf20Sopenharmony_ci		spin_lock_irqsave(&p_rx->lock, flags);
5958c2ecf20Sopenharmony_ci	}
5968c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&p_rx->lock, flags);
5978c2ecf20Sopenharmony_ci}
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_cistatic bool
6008c2ecf20Sopenharmony_ciqed_ll2_lb_rxq_handler_slowpath(struct qed_hwfn *p_hwfn,
6018c2ecf20Sopenharmony_ci				struct core_rx_slow_path_cqe *p_cqe)
6028c2ecf20Sopenharmony_ci{
6038c2ecf20Sopenharmony_ci	struct ooo_opaque *iscsi_ooo;
6048c2ecf20Sopenharmony_ci	u32 cid;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	if (p_cqe->ramrod_cmd_id != CORE_RAMROD_RX_QUEUE_FLUSH)
6078c2ecf20Sopenharmony_ci		return false;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	iscsi_ooo = (struct ooo_opaque *)&p_cqe->opaque_data;
6108c2ecf20Sopenharmony_ci	if (iscsi_ooo->ooo_opcode != TCP_EVENT_DELETE_ISLES)
6118c2ecf20Sopenharmony_ci		return false;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	/* Need to make a flush */
6148c2ecf20Sopenharmony_ci	cid = le32_to_cpu(iscsi_ooo->cid);
6158c2ecf20Sopenharmony_ci	qed_ooo_release_connection_isles(p_hwfn, p_hwfn->p_ooo_info, cid);
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	return true;
6188c2ecf20Sopenharmony_ci}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_cistatic int qed_ll2_lb_rxq_handler(struct qed_hwfn *p_hwfn,
6218c2ecf20Sopenharmony_ci				  struct qed_ll2_info *p_ll2_conn)
6228c2ecf20Sopenharmony_ci{
6238c2ecf20Sopenharmony_ci	struct qed_ll2_rx_queue *p_rx = &p_ll2_conn->rx_queue;
6248c2ecf20Sopenharmony_ci	u16 packet_length = 0, parse_flags = 0, vlan = 0;
6258c2ecf20Sopenharmony_ci	struct qed_ll2_rx_packet *p_pkt = NULL;
6268c2ecf20Sopenharmony_ci	u32 num_ooo_add_to_peninsula = 0, cid;
6278c2ecf20Sopenharmony_ci	union core_rx_cqe_union *cqe = NULL;
6288c2ecf20Sopenharmony_ci	u16 cq_new_idx = 0, cq_old_idx = 0;
6298c2ecf20Sopenharmony_ci	struct qed_ooo_buffer *p_buffer;
6308c2ecf20Sopenharmony_ci	struct ooo_opaque *iscsi_ooo;
6318c2ecf20Sopenharmony_ci	u8 placement_offset = 0;
6328c2ecf20Sopenharmony_ci	u8 cqe_type;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	cq_new_idx = le16_to_cpu(*p_rx->p_fw_cons);
6358c2ecf20Sopenharmony_ci	cq_old_idx = qed_chain_get_cons_idx(&p_rx->rcq_chain);
6368c2ecf20Sopenharmony_ci	if (cq_new_idx == cq_old_idx)
6378c2ecf20Sopenharmony_ci		return 0;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	while (cq_new_idx != cq_old_idx) {
6408c2ecf20Sopenharmony_ci		struct core_rx_fast_path_cqe *p_cqe_fp;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci		cqe = qed_chain_consume(&p_rx->rcq_chain);
6438c2ecf20Sopenharmony_ci		cq_old_idx = qed_chain_get_cons_idx(&p_rx->rcq_chain);
6448c2ecf20Sopenharmony_ci		cqe_type = cqe->rx_cqe_sp.type;
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci		if (cqe_type == CORE_RX_CQE_TYPE_SLOW_PATH)
6478c2ecf20Sopenharmony_ci			if (qed_ll2_lb_rxq_handler_slowpath(p_hwfn,
6488c2ecf20Sopenharmony_ci							    &cqe->rx_cqe_sp))
6498c2ecf20Sopenharmony_ci				continue;
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci		if (cqe_type != CORE_RX_CQE_TYPE_REGULAR) {
6528c2ecf20Sopenharmony_ci			DP_NOTICE(p_hwfn,
6538c2ecf20Sopenharmony_ci				  "Got a non-regular LB LL2 completion [type 0x%02x]\n",
6548c2ecf20Sopenharmony_ci				  cqe_type);
6558c2ecf20Sopenharmony_ci			return -EINVAL;
6568c2ecf20Sopenharmony_ci		}
6578c2ecf20Sopenharmony_ci		p_cqe_fp = &cqe->rx_cqe_fp;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci		placement_offset = p_cqe_fp->placement_offset;
6608c2ecf20Sopenharmony_ci		parse_flags = le16_to_cpu(p_cqe_fp->parse_flags.flags);
6618c2ecf20Sopenharmony_ci		packet_length = le16_to_cpu(p_cqe_fp->packet_length);
6628c2ecf20Sopenharmony_ci		vlan = le16_to_cpu(p_cqe_fp->vlan);
6638c2ecf20Sopenharmony_ci		iscsi_ooo = (struct ooo_opaque *)&p_cqe_fp->opaque_data;
6648c2ecf20Sopenharmony_ci		qed_ooo_save_history_entry(p_hwfn, p_hwfn->p_ooo_info,
6658c2ecf20Sopenharmony_ci					   iscsi_ooo);
6668c2ecf20Sopenharmony_ci		cid = le32_to_cpu(iscsi_ooo->cid);
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci		/* Process delete isle first */
6698c2ecf20Sopenharmony_ci		if (iscsi_ooo->drop_size)
6708c2ecf20Sopenharmony_ci			qed_ooo_delete_isles(p_hwfn, p_hwfn->p_ooo_info, cid,
6718c2ecf20Sopenharmony_ci					     iscsi_ooo->drop_isle,
6728c2ecf20Sopenharmony_ci					     iscsi_ooo->drop_size);
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci		if (iscsi_ooo->ooo_opcode == TCP_EVENT_NOP)
6758c2ecf20Sopenharmony_ci			continue;
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci		/* Now process create/add/join isles */
6788c2ecf20Sopenharmony_ci		if (list_empty(&p_rx->active_descq)) {
6798c2ecf20Sopenharmony_ci			DP_NOTICE(p_hwfn,
6808c2ecf20Sopenharmony_ci				  "LL2 OOO RX chain has no submitted buffers\n"
6818c2ecf20Sopenharmony_ci				  );
6828c2ecf20Sopenharmony_ci			return -EIO;
6838c2ecf20Sopenharmony_ci		}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci		p_pkt = list_first_entry(&p_rx->active_descq,
6868c2ecf20Sopenharmony_ci					 struct qed_ll2_rx_packet, list_entry);
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci		if ((iscsi_ooo->ooo_opcode == TCP_EVENT_ADD_NEW_ISLE) ||
6898c2ecf20Sopenharmony_ci		    (iscsi_ooo->ooo_opcode == TCP_EVENT_ADD_ISLE_RIGHT) ||
6908c2ecf20Sopenharmony_ci		    (iscsi_ooo->ooo_opcode == TCP_EVENT_ADD_ISLE_LEFT) ||
6918c2ecf20Sopenharmony_ci		    (iscsi_ooo->ooo_opcode == TCP_EVENT_ADD_PEN) ||
6928c2ecf20Sopenharmony_ci		    (iscsi_ooo->ooo_opcode == TCP_EVENT_JOIN)) {
6938c2ecf20Sopenharmony_ci			if (!p_pkt) {
6948c2ecf20Sopenharmony_ci				DP_NOTICE(p_hwfn,
6958c2ecf20Sopenharmony_ci					  "LL2 OOO RX packet is not valid\n");
6968c2ecf20Sopenharmony_ci				return -EIO;
6978c2ecf20Sopenharmony_ci			}
6988c2ecf20Sopenharmony_ci			list_del(&p_pkt->list_entry);
6998c2ecf20Sopenharmony_ci			p_buffer = (struct qed_ooo_buffer *)p_pkt->cookie;
7008c2ecf20Sopenharmony_ci			p_buffer->packet_length = packet_length;
7018c2ecf20Sopenharmony_ci			p_buffer->parse_flags = parse_flags;
7028c2ecf20Sopenharmony_ci			p_buffer->vlan = vlan;
7038c2ecf20Sopenharmony_ci			p_buffer->placement_offset = placement_offset;
7048c2ecf20Sopenharmony_ci			qed_chain_consume(&p_rx->rxq_chain);
7058c2ecf20Sopenharmony_ci			list_add_tail(&p_pkt->list_entry, &p_rx->free_descq);
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci			switch (iscsi_ooo->ooo_opcode) {
7088c2ecf20Sopenharmony_ci			case TCP_EVENT_ADD_NEW_ISLE:
7098c2ecf20Sopenharmony_ci				qed_ooo_add_new_isle(p_hwfn,
7108c2ecf20Sopenharmony_ci						     p_hwfn->p_ooo_info,
7118c2ecf20Sopenharmony_ci						     cid,
7128c2ecf20Sopenharmony_ci						     iscsi_ooo->ooo_isle,
7138c2ecf20Sopenharmony_ci						     p_buffer);
7148c2ecf20Sopenharmony_ci				break;
7158c2ecf20Sopenharmony_ci			case TCP_EVENT_ADD_ISLE_RIGHT:
7168c2ecf20Sopenharmony_ci				qed_ooo_add_new_buffer(p_hwfn,
7178c2ecf20Sopenharmony_ci						       p_hwfn->p_ooo_info,
7188c2ecf20Sopenharmony_ci						       cid,
7198c2ecf20Sopenharmony_ci						       iscsi_ooo->ooo_isle,
7208c2ecf20Sopenharmony_ci						       p_buffer,
7218c2ecf20Sopenharmony_ci						       QED_OOO_RIGHT_BUF);
7228c2ecf20Sopenharmony_ci				break;
7238c2ecf20Sopenharmony_ci			case TCP_EVENT_ADD_ISLE_LEFT:
7248c2ecf20Sopenharmony_ci				qed_ooo_add_new_buffer(p_hwfn,
7258c2ecf20Sopenharmony_ci						       p_hwfn->p_ooo_info,
7268c2ecf20Sopenharmony_ci						       cid,
7278c2ecf20Sopenharmony_ci						       iscsi_ooo->ooo_isle,
7288c2ecf20Sopenharmony_ci						       p_buffer,
7298c2ecf20Sopenharmony_ci						       QED_OOO_LEFT_BUF);
7308c2ecf20Sopenharmony_ci				break;
7318c2ecf20Sopenharmony_ci			case TCP_EVENT_JOIN:
7328c2ecf20Sopenharmony_ci				qed_ooo_add_new_buffer(p_hwfn,
7338c2ecf20Sopenharmony_ci						       p_hwfn->p_ooo_info,
7348c2ecf20Sopenharmony_ci						       cid,
7358c2ecf20Sopenharmony_ci						       iscsi_ooo->ooo_isle +
7368c2ecf20Sopenharmony_ci						       1,
7378c2ecf20Sopenharmony_ci						       p_buffer,
7388c2ecf20Sopenharmony_ci						       QED_OOO_LEFT_BUF);
7398c2ecf20Sopenharmony_ci				qed_ooo_join_isles(p_hwfn,
7408c2ecf20Sopenharmony_ci						   p_hwfn->p_ooo_info,
7418c2ecf20Sopenharmony_ci						   cid, iscsi_ooo->ooo_isle);
7428c2ecf20Sopenharmony_ci				break;
7438c2ecf20Sopenharmony_ci			case TCP_EVENT_ADD_PEN:
7448c2ecf20Sopenharmony_ci				num_ooo_add_to_peninsula++;
7458c2ecf20Sopenharmony_ci				qed_ooo_put_ready_buffer(p_hwfn,
7468c2ecf20Sopenharmony_ci							 p_hwfn->p_ooo_info,
7478c2ecf20Sopenharmony_ci							 p_buffer, true);
7488c2ecf20Sopenharmony_ci				break;
7498c2ecf20Sopenharmony_ci			}
7508c2ecf20Sopenharmony_ci		} else {
7518c2ecf20Sopenharmony_ci			DP_NOTICE(p_hwfn,
7528c2ecf20Sopenharmony_ci				  "Unexpected event (%d) TX OOO completion\n",
7538c2ecf20Sopenharmony_ci				  iscsi_ooo->ooo_opcode);
7548c2ecf20Sopenharmony_ci		}
7558c2ecf20Sopenharmony_ci	}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	return 0;
7588c2ecf20Sopenharmony_ci}
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_cistatic void
7618c2ecf20Sopenharmony_ciqed_ooo_submit_tx_buffers(struct qed_hwfn *p_hwfn,
7628c2ecf20Sopenharmony_ci			  struct qed_ll2_info *p_ll2_conn)
7638c2ecf20Sopenharmony_ci{
7648c2ecf20Sopenharmony_ci	struct qed_ll2_tx_pkt_info tx_pkt;
7658c2ecf20Sopenharmony_ci	struct qed_ooo_buffer *p_buffer;
7668c2ecf20Sopenharmony_ci	u16 l4_hdr_offset_w;
7678c2ecf20Sopenharmony_ci	dma_addr_t first_frag;
7688c2ecf20Sopenharmony_ci	u8 bd_flags;
7698c2ecf20Sopenharmony_ci	int rc;
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	/* Submit Tx buffers here */
7728c2ecf20Sopenharmony_ci	while ((p_buffer = qed_ooo_get_ready_buffer(p_hwfn,
7738c2ecf20Sopenharmony_ci						    p_hwfn->p_ooo_info))) {
7748c2ecf20Sopenharmony_ci		l4_hdr_offset_w = 0;
7758c2ecf20Sopenharmony_ci		bd_flags = 0;
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci		first_frag = p_buffer->rx_buffer_phys_addr +
7788c2ecf20Sopenharmony_ci			     p_buffer->placement_offset;
7798c2ecf20Sopenharmony_ci		SET_FIELD(bd_flags, CORE_TX_BD_DATA_FORCE_VLAN_MODE, 1);
7808c2ecf20Sopenharmony_ci		SET_FIELD(bd_flags, CORE_TX_BD_DATA_L4_PROTOCOL, 1);
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci		memset(&tx_pkt, 0, sizeof(tx_pkt));
7838c2ecf20Sopenharmony_ci		tx_pkt.num_of_bds = 1;
7848c2ecf20Sopenharmony_ci		tx_pkt.vlan = p_buffer->vlan;
7858c2ecf20Sopenharmony_ci		tx_pkt.bd_flags = bd_flags;
7868c2ecf20Sopenharmony_ci		tx_pkt.l4_hdr_offset_w = l4_hdr_offset_w;
7878c2ecf20Sopenharmony_ci		switch (p_ll2_conn->tx_dest) {
7888c2ecf20Sopenharmony_ci		case CORE_TX_DEST_NW:
7898c2ecf20Sopenharmony_ci			tx_pkt.tx_dest = QED_LL2_TX_DEST_NW;
7908c2ecf20Sopenharmony_ci			break;
7918c2ecf20Sopenharmony_ci		case CORE_TX_DEST_LB:
7928c2ecf20Sopenharmony_ci			tx_pkt.tx_dest = QED_LL2_TX_DEST_LB;
7938c2ecf20Sopenharmony_ci			break;
7948c2ecf20Sopenharmony_ci		case CORE_TX_DEST_DROP:
7958c2ecf20Sopenharmony_ci		default:
7968c2ecf20Sopenharmony_ci			tx_pkt.tx_dest = QED_LL2_TX_DEST_DROP;
7978c2ecf20Sopenharmony_ci			break;
7988c2ecf20Sopenharmony_ci		}
7998c2ecf20Sopenharmony_ci		tx_pkt.first_frag = first_frag;
8008c2ecf20Sopenharmony_ci		tx_pkt.first_frag_len = p_buffer->packet_length;
8018c2ecf20Sopenharmony_ci		tx_pkt.cookie = p_buffer;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci		rc = qed_ll2_prepare_tx_packet(p_hwfn, p_ll2_conn->my_id,
8048c2ecf20Sopenharmony_ci					       &tx_pkt, true);
8058c2ecf20Sopenharmony_ci		if (rc) {
8068c2ecf20Sopenharmony_ci			qed_ooo_put_ready_buffer(p_hwfn, p_hwfn->p_ooo_info,
8078c2ecf20Sopenharmony_ci						 p_buffer, false);
8088c2ecf20Sopenharmony_ci			break;
8098c2ecf20Sopenharmony_ci		}
8108c2ecf20Sopenharmony_ci	}
8118c2ecf20Sopenharmony_ci}
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_cistatic void
8148c2ecf20Sopenharmony_ciqed_ooo_submit_rx_buffers(struct qed_hwfn *p_hwfn,
8158c2ecf20Sopenharmony_ci			  struct qed_ll2_info *p_ll2_conn)
8168c2ecf20Sopenharmony_ci{
8178c2ecf20Sopenharmony_ci	struct qed_ooo_buffer *p_buffer;
8188c2ecf20Sopenharmony_ci	int rc;
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	while ((p_buffer = qed_ooo_get_free_buffer(p_hwfn,
8218c2ecf20Sopenharmony_ci						   p_hwfn->p_ooo_info))) {
8228c2ecf20Sopenharmony_ci		rc = qed_ll2_post_rx_buffer(p_hwfn,
8238c2ecf20Sopenharmony_ci					    p_ll2_conn->my_id,
8248c2ecf20Sopenharmony_ci					    p_buffer->rx_buffer_phys_addr,
8258c2ecf20Sopenharmony_ci					    0, p_buffer, true);
8268c2ecf20Sopenharmony_ci		if (rc) {
8278c2ecf20Sopenharmony_ci			qed_ooo_put_free_buffer(p_hwfn,
8288c2ecf20Sopenharmony_ci						p_hwfn->p_ooo_info, p_buffer);
8298c2ecf20Sopenharmony_ci			break;
8308c2ecf20Sopenharmony_ci		}
8318c2ecf20Sopenharmony_ci	}
8328c2ecf20Sopenharmony_ci}
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_cistatic int qed_ll2_lb_rxq_completion(struct qed_hwfn *p_hwfn, void *p_cookie)
8358c2ecf20Sopenharmony_ci{
8368c2ecf20Sopenharmony_ci	struct qed_ll2_info *p_ll2_conn = (struct qed_ll2_info *)p_cookie;
8378c2ecf20Sopenharmony_ci	int rc;
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	if (!p_ll2_conn)
8408c2ecf20Sopenharmony_ci		return 0;
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	if (!QED_LL2_RX_REGISTERED(p_ll2_conn))
8438c2ecf20Sopenharmony_ci		return 0;
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	rc = qed_ll2_lb_rxq_handler(p_hwfn, p_ll2_conn);
8468c2ecf20Sopenharmony_ci	if (rc)
8478c2ecf20Sopenharmony_ci		return rc;
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	qed_ooo_submit_rx_buffers(p_hwfn, p_ll2_conn);
8508c2ecf20Sopenharmony_ci	qed_ooo_submit_tx_buffers(p_hwfn, p_ll2_conn);
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	return 0;
8538c2ecf20Sopenharmony_ci}
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_cistatic int qed_ll2_lb_txq_completion(struct qed_hwfn *p_hwfn, void *p_cookie)
8568c2ecf20Sopenharmony_ci{
8578c2ecf20Sopenharmony_ci	struct qed_ll2_info *p_ll2_conn = (struct qed_ll2_info *)p_cookie;
8588c2ecf20Sopenharmony_ci	struct qed_ll2_tx_queue *p_tx = &p_ll2_conn->tx_queue;
8598c2ecf20Sopenharmony_ci	struct qed_ll2_tx_packet *p_pkt = NULL;
8608c2ecf20Sopenharmony_ci	struct qed_ooo_buffer *p_buffer;
8618c2ecf20Sopenharmony_ci	bool b_dont_submit_rx = false;
8628c2ecf20Sopenharmony_ci	u16 new_idx = 0, num_bds = 0;
8638c2ecf20Sopenharmony_ci	int rc;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	if (!p_ll2_conn)
8668c2ecf20Sopenharmony_ci		return 0;
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	if (!QED_LL2_TX_REGISTERED(p_ll2_conn))
8698c2ecf20Sopenharmony_ci		return 0;
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	new_idx = le16_to_cpu(*p_tx->p_fw_cons);
8728c2ecf20Sopenharmony_ci	num_bds = ((s16)new_idx - (s16)p_tx->bds_idx);
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	if (!num_bds)
8758c2ecf20Sopenharmony_ci		return 0;
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	while (num_bds) {
8788c2ecf20Sopenharmony_ci		if (list_empty(&p_tx->active_descq))
8798c2ecf20Sopenharmony_ci			return -EINVAL;
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci		p_pkt = list_first_entry(&p_tx->active_descq,
8828c2ecf20Sopenharmony_ci					 struct qed_ll2_tx_packet, list_entry);
8838c2ecf20Sopenharmony_ci		if (!p_pkt)
8848c2ecf20Sopenharmony_ci			return -EINVAL;
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci		if (p_pkt->bd_used != 1) {
8878c2ecf20Sopenharmony_ci			DP_NOTICE(p_hwfn,
8888c2ecf20Sopenharmony_ci				  "Unexpectedly many BDs(%d) in TX OOO completion\n",
8898c2ecf20Sopenharmony_ci				  p_pkt->bd_used);
8908c2ecf20Sopenharmony_ci			return -EINVAL;
8918c2ecf20Sopenharmony_ci		}
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci		list_del(&p_pkt->list_entry);
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci		num_bds--;
8968c2ecf20Sopenharmony_ci		p_tx->bds_idx++;
8978c2ecf20Sopenharmony_ci		qed_chain_consume(&p_tx->txq_chain);
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci		p_buffer = (struct qed_ooo_buffer *)p_pkt->cookie;
9008c2ecf20Sopenharmony_ci		list_add_tail(&p_pkt->list_entry, &p_tx->free_descq);
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci		if (b_dont_submit_rx) {
9038c2ecf20Sopenharmony_ci			qed_ooo_put_free_buffer(p_hwfn, p_hwfn->p_ooo_info,
9048c2ecf20Sopenharmony_ci						p_buffer);
9058c2ecf20Sopenharmony_ci			continue;
9068c2ecf20Sopenharmony_ci		}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci		rc = qed_ll2_post_rx_buffer(p_hwfn, p_ll2_conn->my_id,
9098c2ecf20Sopenharmony_ci					    p_buffer->rx_buffer_phys_addr, 0,
9108c2ecf20Sopenharmony_ci					    p_buffer, true);
9118c2ecf20Sopenharmony_ci		if (rc != 0) {
9128c2ecf20Sopenharmony_ci			qed_ooo_put_free_buffer(p_hwfn,
9138c2ecf20Sopenharmony_ci						p_hwfn->p_ooo_info, p_buffer);
9148c2ecf20Sopenharmony_ci			b_dont_submit_rx = true;
9158c2ecf20Sopenharmony_ci		}
9168c2ecf20Sopenharmony_ci	}
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci	qed_ooo_submit_tx_buffers(p_hwfn, p_ll2_conn);
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	return 0;
9218c2ecf20Sopenharmony_ci}
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_cistatic void qed_ll2_stop_ooo(struct qed_hwfn *p_hwfn)
9248c2ecf20Sopenharmony_ci{
9258c2ecf20Sopenharmony_ci	u8 *handle = &p_hwfn->pf_params.iscsi_pf_params.ll2_ooo_queue_id;
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	DP_VERBOSE(p_hwfn, (QED_MSG_STORAGE | QED_MSG_LL2),
9288c2ecf20Sopenharmony_ci		   "Stopping LL2 OOO queue [%02x]\n", *handle);
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	qed_ll2_terminate_connection(p_hwfn, *handle);
9318c2ecf20Sopenharmony_ci	qed_ll2_release_connection(p_hwfn, *handle);
9328c2ecf20Sopenharmony_ci	*handle = QED_LL2_UNUSED_HANDLE;
9338c2ecf20Sopenharmony_ci}
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_cistatic int qed_sp_ll2_rx_queue_start(struct qed_hwfn *p_hwfn,
9368c2ecf20Sopenharmony_ci				     struct qed_ll2_info *p_ll2_conn,
9378c2ecf20Sopenharmony_ci				     u8 action_on_error)
9388c2ecf20Sopenharmony_ci{
9398c2ecf20Sopenharmony_ci	enum qed_ll2_conn_type conn_type = p_ll2_conn->input.conn_type;
9408c2ecf20Sopenharmony_ci	struct qed_ll2_rx_queue *p_rx = &p_ll2_conn->rx_queue;
9418c2ecf20Sopenharmony_ci	struct core_rx_start_ramrod_data *p_ramrod = NULL;
9428c2ecf20Sopenharmony_ci	struct qed_spq_entry *p_ent = NULL;
9438c2ecf20Sopenharmony_ci	struct qed_sp_init_data init_data;
9448c2ecf20Sopenharmony_ci	u16 cqe_pbl_size;
9458c2ecf20Sopenharmony_ci	int rc = 0;
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	/* Get SPQ entry */
9488c2ecf20Sopenharmony_ci	memset(&init_data, 0, sizeof(init_data));
9498c2ecf20Sopenharmony_ci	init_data.cid = p_ll2_conn->cid;
9508c2ecf20Sopenharmony_ci	init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
9518c2ecf20Sopenharmony_ci	init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	rc = qed_sp_init_request(p_hwfn, &p_ent,
9548c2ecf20Sopenharmony_ci				 CORE_RAMROD_RX_QUEUE_START,
9558c2ecf20Sopenharmony_ci				 PROTOCOLID_CORE, &init_data);
9568c2ecf20Sopenharmony_ci	if (rc)
9578c2ecf20Sopenharmony_ci		return rc;
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	p_ramrod = &p_ent->ramrod.core_rx_queue_start;
9608c2ecf20Sopenharmony_ci	memset(p_ramrod, 0, sizeof(*p_ramrod));
9618c2ecf20Sopenharmony_ci	p_ramrod->sb_id = cpu_to_le16(qed_int_get_sp_sb_id(p_hwfn));
9628c2ecf20Sopenharmony_ci	p_ramrod->sb_index = p_rx->rx_sb_index;
9638c2ecf20Sopenharmony_ci	p_ramrod->complete_event_flg = 1;
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	p_ramrod->mtu = cpu_to_le16(p_ll2_conn->input.mtu);
9668c2ecf20Sopenharmony_ci	DMA_REGPAIR_LE(p_ramrod->bd_base, p_rx->rxq_chain.p_phys_addr);
9678c2ecf20Sopenharmony_ci	cqe_pbl_size = (u16)qed_chain_get_page_cnt(&p_rx->rcq_chain);
9688c2ecf20Sopenharmony_ci	p_ramrod->num_of_pbl_pages = cpu_to_le16(cqe_pbl_size);
9698c2ecf20Sopenharmony_ci	DMA_REGPAIR_LE(p_ramrod->cqe_pbl_addr,
9708c2ecf20Sopenharmony_ci		       qed_chain_get_pbl_phys(&p_rx->rcq_chain));
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	p_ramrod->drop_ttl0_flg = p_ll2_conn->input.rx_drop_ttl0_flg;
9738c2ecf20Sopenharmony_ci	p_ramrod->inner_vlan_stripping_en =
9748c2ecf20Sopenharmony_ci		p_ll2_conn->input.rx_vlan_removal_en;
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	if (test_bit(QED_MF_UFP_SPECIFIC, &p_hwfn->cdev->mf_bits) &&
9778c2ecf20Sopenharmony_ci	    p_ll2_conn->input.conn_type == QED_LL2_TYPE_FCOE)
9788c2ecf20Sopenharmony_ci		p_ramrod->report_outer_vlan = 1;
9798c2ecf20Sopenharmony_ci	p_ramrod->queue_id = p_ll2_conn->queue_id;
9808c2ecf20Sopenharmony_ci	p_ramrod->main_func_queue = p_ll2_conn->main_func_queue ? 1 : 0;
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	if (test_bit(QED_MF_LL2_NON_UNICAST, &p_hwfn->cdev->mf_bits) &&
9838c2ecf20Sopenharmony_ci	    p_ramrod->main_func_queue && conn_type != QED_LL2_TYPE_ROCE &&
9848c2ecf20Sopenharmony_ci	    conn_type != QED_LL2_TYPE_IWARP) {
9858c2ecf20Sopenharmony_ci		p_ramrod->mf_si_bcast_accept_all = 1;
9868c2ecf20Sopenharmony_ci		p_ramrod->mf_si_mcast_accept_all = 1;
9878c2ecf20Sopenharmony_ci	} else {
9888c2ecf20Sopenharmony_ci		p_ramrod->mf_si_bcast_accept_all = 0;
9898c2ecf20Sopenharmony_ci		p_ramrod->mf_si_mcast_accept_all = 0;
9908c2ecf20Sopenharmony_ci	}
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci	p_ramrod->action_on_error.error_type = action_on_error;
9938c2ecf20Sopenharmony_ci	p_ramrod->gsi_offload_flag = p_ll2_conn->input.gsi_enable;
9948c2ecf20Sopenharmony_ci	p_ramrod->zero_prod_flg = 1;
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	return qed_spq_post(p_hwfn, p_ent, NULL);
9978c2ecf20Sopenharmony_ci}
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_cistatic int qed_sp_ll2_tx_queue_start(struct qed_hwfn *p_hwfn,
10008c2ecf20Sopenharmony_ci				     struct qed_ll2_info *p_ll2_conn)
10018c2ecf20Sopenharmony_ci{
10028c2ecf20Sopenharmony_ci	enum qed_ll2_conn_type conn_type = p_ll2_conn->input.conn_type;
10038c2ecf20Sopenharmony_ci	struct qed_ll2_tx_queue *p_tx = &p_ll2_conn->tx_queue;
10048c2ecf20Sopenharmony_ci	struct core_tx_start_ramrod_data *p_ramrod = NULL;
10058c2ecf20Sopenharmony_ci	struct qed_spq_entry *p_ent = NULL;
10068c2ecf20Sopenharmony_ci	struct qed_sp_init_data init_data;
10078c2ecf20Sopenharmony_ci	u16 pq_id = 0, pbl_size;
10088c2ecf20Sopenharmony_ci	int rc = -EINVAL;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	if (!QED_LL2_TX_REGISTERED(p_ll2_conn))
10118c2ecf20Sopenharmony_ci		return 0;
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	if (p_ll2_conn->input.conn_type == QED_LL2_TYPE_OOO)
10148c2ecf20Sopenharmony_ci		p_ll2_conn->tx_stats_en = 0;
10158c2ecf20Sopenharmony_ci	else
10168c2ecf20Sopenharmony_ci		p_ll2_conn->tx_stats_en = 1;
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	/* Get SPQ entry */
10198c2ecf20Sopenharmony_ci	memset(&init_data, 0, sizeof(init_data));
10208c2ecf20Sopenharmony_ci	init_data.cid = p_ll2_conn->cid;
10218c2ecf20Sopenharmony_ci	init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
10228c2ecf20Sopenharmony_ci	init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	rc = qed_sp_init_request(p_hwfn, &p_ent,
10258c2ecf20Sopenharmony_ci				 CORE_RAMROD_TX_QUEUE_START,
10268c2ecf20Sopenharmony_ci				 PROTOCOLID_CORE, &init_data);
10278c2ecf20Sopenharmony_ci	if (rc)
10288c2ecf20Sopenharmony_ci		return rc;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	p_ramrod = &p_ent->ramrod.core_tx_queue_start;
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	p_ramrod->sb_id = cpu_to_le16(qed_int_get_sp_sb_id(p_hwfn));
10338c2ecf20Sopenharmony_ci	p_ramrod->sb_index = p_tx->tx_sb_index;
10348c2ecf20Sopenharmony_ci	p_ramrod->mtu = cpu_to_le16(p_ll2_conn->input.mtu);
10358c2ecf20Sopenharmony_ci	p_ramrod->stats_en = p_ll2_conn->tx_stats_en;
10368c2ecf20Sopenharmony_ci	p_ramrod->stats_id = p_ll2_conn->tx_stats_id;
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	DMA_REGPAIR_LE(p_ramrod->pbl_base_addr,
10398c2ecf20Sopenharmony_ci		       qed_chain_get_pbl_phys(&p_tx->txq_chain));
10408c2ecf20Sopenharmony_ci	pbl_size = qed_chain_get_page_cnt(&p_tx->txq_chain);
10418c2ecf20Sopenharmony_ci	p_ramrod->pbl_size = cpu_to_le16(pbl_size);
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci	switch (p_ll2_conn->input.tx_tc) {
10448c2ecf20Sopenharmony_ci	case PURE_LB_TC:
10458c2ecf20Sopenharmony_ci		pq_id = qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_LB);
10468c2ecf20Sopenharmony_ci		break;
10478c2ecf20Sopenharmony_ci	case PKT_LB_TC:
10488c2ecf20Sopenharmony_ci		pq_id = qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_OOO);
10498c2ecf20Sopenharmony_ci		break;
10508c2ecf20Sopenharmony_ci	default:
10518c2ecf20Sopenharmony_ci		pq_id = qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_OFLD);
10528c2ecf20Sopenharmony_ci		break;
10538c2ecf20Sopenharmony_ci	}
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	p_ramrod->qm_pq_id = cpu_to_le16(pq_id);
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	switch (conn_type) {
10588c2ecf20Sopenharmony_ci	case QED_LL2_TYPE_FCOE:
10598c2ecf20Sopenharmony_ci		p_ramrod->conn_type = PROTOCOLID_FCOE;
10608c2ecf20Sopenharmony_ci		break;
10618c2ecf20Sopenharmony_ci	case QED_LL2_TYPE_ISCSI:
10628c2ecf20Sopenharmony_ci		p_ramrod->conn_type = PROTOCOLID_ISCSI;
10638c2ecf20Sopenharmony_ci		break;
10648c2ecf20Sopenharmony_ci	case QED_LL2_TYPE_ROCE:
10658c2ecf20Sopenharmony_ci		p_ramrod->conn_type = PROTOCOLID_ROCE;
10668c2ecf20Sopenharmony_ci		break;
10678c2ecf20Sopenharmony_ci	case QED_LL2_TYPE_IWARP:
10688c2ecf20Sopenharmony_ci		p_ramrod->conn_type = PROTOCOLID_IWARP;
10698c2ecf20Sopenharmony_ci		break;
10708c2ecf20Sopenharmony_ci	case QED_LL2_TYPE_OOO:
10718c2ecf20Sopenharmony_ci		if (p_hwfn->hw_info.personality == QED_PCI_ISCSI)
10728c2ecf20Sopenharmony_ci			p_ramrod->conn_type = PROTOCOLID_ISCSI;
10738c2ecf20Sopenharmony_ci		else
10748c2ecf20Sopenharmony_ci			p_ramrod->conn_type = PROTOCOLID_IWARP;
10758c2ecf20Sopenharmony_ci		break;
10768c2ecf20Sopenharmony_ci	default:
10778c2ecf20Sopenharmony_ci		p_ramrod->conn_type = PROTOCOLID_ETH;
10788c2ecf20Sopenharmony_ci		DP_NOTICE(p_hwfn, "Unknown connection type: %d\n", conn_type);
10798c2ecf20Sopenharmony_ci	}
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	p_ramrod->gsi_offload_flag = p_ll2_conn->input.gsi_enable;
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	rc = qed_spq_post(p_hwfn, p_ent, NULL);
10848c2ecf20Sopenharmony_ci	if (rc)
10858c2ecf20Sopenharmony_ci		return rc;
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	rc = qed_db_recovery_add(p_hwfn->cdev, p_tx->doorbell_addr,
10888c2ecf20Sopenharmony_ci				 &p_tx->db_msg, DB_REC_WIDTH_32B,
10898c2ecf20Sopenharmony_ci				 DB_REC_KERNEL);
10908c2ecf20Sopenharmony_ci	return rc;
10918c2ecf20Sopenharmony_ci}
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_cistatic int qed_sp_ll2_rx_queue_stop(struct qed_hwfn *p_hwfn,
10948c2ecf20Sopenharmony_ci				    struct qed_ll2_info *p_ll2_conn)
10958c2ecf20Sopenharmony_ci{
10968c2ecf20Sopenharmony_ci	struct core_rx_stop_ramrod_data *p_ramrod = NULL;
10978c2ecf20Sopenharmony_ci	struct qed_spq_entry *p_ent = NULL;
10988c2ecf20Sopenharmony_ci	struct qed_sp_init_data init_data;
10998c2ecf20Sopenharmony_ci	int rc = -EINVAL;
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	/* Get SPQ entry */
11028c2ecf20Sopenharmony_ci	memset(&init_data, 0, sizeof(init_data));
11038c2ecf20Sopenharmony_ci	init_data.cid = p_ll2_conn->cid;
11048c2ecf20Sopenharmony_ci	init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
11058c2ecf20Sopenharmony_ci	init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	rc = qed_sp_init_request(p_hwfn, &p_ent,
11088c2ecf20Sopenharmony_ci				 CORE_RAMROD_RX_QUEUE_STOP,
11098c2ecf20Sopenharmony_ci				 PROTOCOLID_CORE, &init_data);
11108c2ecf20Sopenharmony_ci	if (rc)
11118c2ecf20Sopenharmony_ci		return rc;
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	p_ramrod = &p_ent->ramrod.core_rx_queue_stop;
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	p_ramrod->complete_event_flg = 1;
11168c2ecf20Sopenharmony_ci	p_ramrod->queue_id = p_ll2_conn->queue_id;
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	return qed_spq_post(p_hwfn, p_ent, NULL);
11198c2ecf20Sopenharmony_ci}
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_cistatic int qed_sp_ll2_tx_queue_stop(struct qed_hwfn *p_hwfn,
11228c2ecf20Sopenharmony_ci				    struct qed_ll2_info *p_ll2_conn)
11238c2ecf20Sopenharmony_ci{
11248c2ecf20Sopenharmony_ci	struct qed_ll2_tx_queue *p_tx = &p_ll2_conn->tx_queue;
11258c2ecf20Sopenharmony_ci	struct qed_spq_entry *p_ent = NULL;
11268c2ecf20Sopenharmony_ci	struct qed_sp_init_data init_data;
11278c2ecf20Sopenharmony_ci	int rc = -EINVAL;
11288c2ecf20Sopenharmony_ci	qed_db_recovery_del(p_hwfn->cdev, p_tx->doorbell_addr, &p_tx->db_msg);
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	/* Get SPQ entry */
11318c2ecf20Sopenharmony_ci	memset(&init_data, 0, sizeof(init_data));
11328c2ecf20Sopenharmony_ci	init_data.cid = p_ll2_conn->cid;
11338c2ecf20Sopenharmony_ci	init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
11348c2ecf20Sopenharmony_ci	init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	rc = qed_sp_init_request(p_hwfn, &p_ent,
11378c2ecf20Sopenharmony_ci				 CORE_RAMROD_TX_QUEUE_STOP,
11388c2ecf20Sopenharmony_ci				 PROTOCOLID_CORE, &init_data);
11398c2ecf20Sopenharmony_ci	if (rc)
11408c2ecf20Sopenharmony_ci		return rc;
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	return qed_spq_post(p_hwfn, p_ent, NULL);
11438c2ecf20Sopenharmony_ci}
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_cistatic int
11468c2ecf20Sopenharmony_ciqed_ll2_acquire_connection_rx(struct qed_hwfn *p_hwfn,
11478c2ecf20Sopenharmony_ci			      struct qed_ll2_info *p_ll2_info)
11488c2ecf20Sopenharmony_ci{
11498c2ecf20Sopenharmony_ci	struct qed_chain_init_params params = {
11508c2ecf20Sopenharmony_ci		.intended_use	= QED_CHAIN_USE_TO_CONSUME_PRODUCE,
11518c2ecf20Sopenharmony_ci		.cnt_type	= QED_CHAIN_CNT_TYPE_U16,
11528c2ecf20Sopenharmony_ci		.num_elems	= p_ll2_info->input.rx_num_desc,
11538c2ecf20Sopenharmony_ci	};
11548c2ecf20Sopenharmony_ci	struct qed_dev *cdev = p_hwfn->cdev;
11558c2ecf20Sopenharmony_ci	struct qed_ll2_rx_packet *p_descq;
11568c2ecf20Sopenharmony_ci	u32 capacity;
11578c2ecf20Sopenharmony_ci	int rc = 0;
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	if (!p_ll2_info->input.rx_num_desc)
11608c2ecf20Sopenharmony_ci		goto out;
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	params.mode = QED_CHAIN_MODE_NEXT_PTR;
11638c2ecf20Sopenharmony_ci	params.elem_size = sizeof(struct core_rx_bd);
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci	rc = qed_chain_alloc(cdev, &p_ll2_info->rx_queue.rxq_chain, &params);
11668c2ecf20Sopenharmony_ci	if (rc) {
11678c2ecf20Sopenharmony_ci		DP_NOTICE(p_hwfn, "Failed to allocate ll2 rxq chain\n");
11688c2ecf20Sopenharmony_ci		goto out;
11698c2ecf20Sopenharmony_ci	}
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	capacity = qed_chain_get_capacity(&p_ll2_info->rx_queue.rxq_chain);
11728c2ecf20Sopenharmony_ci	p_descq = kcalloc(capacity, sizeof(struct qed_ll2_rx_packet),
11738c2ecf20Sopenharmony_ci			  GFP_KERNEL);
11748c2ecf20Sopenharmony_ci	if (!p_descq) {
11758c2ecf20Sopenharmony_ci		rc = -ENOMEM;
11768c2ecf20Sopenharmony_ci		DP_NOTICE(p_hwfn, "Failed to allocate ll2 Rx desc\n");
11778c2ecf20Sopenharmony_ci		goto out;
11788c2ecf20Sopenharmony_ci	}
11798c2ecf20Sopenharmony_ci	p_ll2_info->rx_queue.descq_array = p_descq;
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	params.mode = QED_CHAIN_MODE_PBL;
11828c2ecf20Sopenharmony_ci	params.elem_size = sizeof(struct core_rx_fast_path_cqe);
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	rc = qed_chain_alloc(cdev, &p_ll2_info->rx_queue.rcq_chain, &params);
11858c2ecf20Sopenharmony_ci	if (rc) {
11868c2ecf20Sopenharmony_ci		DP_NOTICE(p_hwfn, "Failed to allocate ll2 rcq chain\n");
11878c2ecf20Sopenharmony_ci		goto out;
11888c2ecf20Sopenharmony_ci	}
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci	DP_VERBOSE(p_hwfn, QED_MSG_LL2,
11918c2ecf20Sopenharmony_ci		   "Allocated LL2 Rxq [Type %08x] with 0x%08x buffers\n",
11928c2ecf20Sopenharmony_ci		   p_ll2_info->input.conn_type, p_ll2_info->input.rx_num_desc);
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ciout:
11958c2ecf20Sopenharmony_ci	return rc;
11968c2ecf20Sopenharmony_ci}
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_cistatic int qed_ll2_acquire_connection_tx(struct qed_hwfn *p_hwfn,
11998c2ecf20Sopenharmony_ci					 struct qed_ll2_info *p_ll2_info)
12008c2ecf20Sopenharmony_ci{
12018c2ecf20Sopenharmony_ci	struct qed_chain_init_params params = {
12028c2ecf20Sopenharmony_ci		.mode		= QED_CHAIN_MODE_PBL,
12038c2ecf20Sopenharmony_ci		.intended_use	= QED_CHAIN_USE_TO_CONSUME_PRODUCE,
12048c2ecf20Sopenharmony_ci		.cnt_type	= QED_CHAIN_CNT_TYPE_U16,
12058c2ecf20Sopenharmony_ci		.num_elems	= p_ll2_info->input.tx_num_desc,
12068c2ecf20Sopenharmony_ci		.elem_size	= sizeof(struct core_tx_bd),
12078c2ecf20Sopenharmony_ci	};
12088c2ecf20Sopenharmony_ci	struct qed_ll2_tx_packet *p_descq;
12098c2ecf20Sopenharmony_ci	size_t desc_size;
12108c2ecf20Sopenharmony_ci	u32 capacity;
12118c2ecf20Sopenharmony_ci	int rc = 0;
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci	if (!p_ll2_info->input.tx_num_desc)
12148c2ecf20Sopenharmony_ci		goto out;
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	rc = qed_chain_alloc(p_hwfn->cdev, &p_ll2_info->tx_queue.txq_chain,
12178c2ecf20Sopenharmony_ci			     &params);
12188c2ecf20Sopenharmony_ci	if (rc)
12198c2ecf20Sopenharmony_ci		goto out;
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci	capacity = qed_chain_get_capacity(&p_ll2_info->tx_queue.txq_chain);
12228c2ecf20Sopenharmony_ci	/* All bds_set elements are flexibily added. */
12238c2ecf20Sopenharmony_ci	desc_size = struct_size(p_descq, bds_set,
12248c2ecf20Sopenharmony_ci				p_ll2_info->input.tx_max_bds_per_packet);
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	p_descq = kcalloc(capacity, desc_size, GFP_KERNEL);
12278c2ecf20Sopenharmony_ci	if (!p_descq) {
12288c2ecf20Sopenharmony_ci		rc = -ENOMEM;
12298c2ecf20Sopenharmony_ci		goto out;
12308c2ecf20Sopenharmony_ci	}
12318c2ecf20Sopenharmony_ci	p_ll2_info->tx_queue.descq_mem = p_descq;
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci	DP_VERBOSE(p_hwfn, QED_MSG_LL2,
12348c2ecf20Sopenharmony_ci		   "Allocated LL2 Txq [Type %08x] with 0x%08x buffers\n",
12358c2ecf20Sopenharmony_ci		   p_ll2_info->input.conn_type, p_ll2_info->input.tx_num_desc);
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ciout:
12388c2ecf20Sopenharmony_ci	if (rc)
12398c2ecf20Sopenharmony_ci		DP_NOTICE(p_hwfn,
12408c2ecf20Sopenharmony_ci			  "Can't allocate memory for Tx LL2 with 0x%08x buffers\n",
12418c2ecf20Sopenharmony_ci			  p_ll2_info->input.tx_num_desc);
12428c2ecf20Sopenharmony_ci	return rc;
12438c2ecf20Sopenharmony_ci}
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_cistatic int
12468c2ecf20Sopenharmony_ciqed_ll2_acquire_connection_ooo(struct qed_hwfn *p_hwfn,
12478c2ecf20Sopenharmony_ci			       struct qed_ll2_info *p_ll2_info, u16 mtu)
12488c2ecf20Sopenharmony_ci{
12498c2ecf20Sopenharmony_ci	struct qed_ooo_buffer *p_buf = NULL;
12508c2ecf20Sopenharmony_ci	void *p_virt;
12518c2ecf20Sopenharmony_ci	u16 buf_idx;
12528c2ecf20Sopenharmony_ci	int rc = 0;
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci	if (p_ll2_info->input.conn_type != QED_LL2_TYPE_OOO)
12558c2ecf20Sopenharmony_ci		return rc;
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci	/* Correct number of requested OOO buffers if needed */
12588c2ecf20Sopenharmony_ci	if (!p_ll2_info->input.rx_num_ooo_buffers) {
12598c2ecf20Sopenharmony_ci		u16 num_desc = p_ll2_info->input.rx_num_desc;
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci		if (!num_desc)
12628c2ecf20Sopenharmony_ci			return -EINVAL;
12638c2ecf20Sopenharmony_ci		p_ll2_info->input.rx_num_ooo_buffers = num_desc * 2;
12648c2ecf20Sopenharmony_ci	}
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci	for (buf_idx = 0; buf_idx < p_ll2_info->input.rx_num_ooo_buffers;
12678c2ecf20Sopenharmony_ci	     buf_idx++) {
12688c2ecf20Sopenharmony_ci		p_buf = kzalloc(sizeof(*p_buf), GFP_KERNEL);
12698c2ecf20Sopenharmony_ci		if (!p_buf) {
12708c2ecf20Sopenharmony_ci			rc = -ENOMEM;
12718c2ecf20Sopenharmony_ci			goto out;
12728c2ecf20Sopenharmony_ci		}
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ci		p_buf->rx_buffer_size = mtu + 26 + ETH_CACHE_LINE_SIZE;
12758c2ecf20Sopenharmony_ci		p_buf->rx_buffer_size = (p_buf->rx_buffer_size +
12768c2ecf20Sopenharmony_ci					 ETH_CACHE_LINE_SIZE - 1) &
12778c2ecf20Sopenharmony_ci					~(ETH_CACHE_LINE_SIZE - 1);
12788c2ecf20Sopenharmony_ci		p_virt = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
12798c2ecf20Sopenharmony_ci					    p_buf->rx_buffer_size,
12808c2ecf20Sopenharmony_ci					    &p_buf->rx_buffer_phys_addr,
12818c2ecf20Sopenharmony_ci					    GFP_KERNEL);
12828c2ecf20Sopenharmony_ci		if (!p_virt) {
12838c2ecf20Sopenharmony_ci			kfree(p_buf);
12848c2ecf20Sopenharmony_ci			rc = -ENOMEM;
12858c2ecf20Sopenharmony_ci			goto out;
12868c2ecf20Sopenharmony_ci		}
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_ci		p_buf->rx_buffer_virt_addr = p_virt;
12898c2ecf20Sopenharmony_ci		qed_ooo_put_free_buffer(p_hwfn, p_hwfn->p_ooo_info, p_buf);
12908c2ecf20Sopenharmony_ci	}
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci	DP_VERBOSE(p_hwfn, QED_MSG_LL2,
12938c2ecf20Sopenharmony_ci		   "Allocated [%04x] LL2 OOO buffers [each of size 0x%08x]\n",
12948c2ecf20Sopenharmony_ci		   p_ll2_info->input.rx_num_ooo_buffers, p_buf->rx_buffer_size);
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_ciout:
12978c2ecf20Sopenharmony_ci	return rc;
12988c2ecf20Sopenharmony_ci}
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_cistatic int
13018c2ecf20Sopenharmony_ciqed_ll2_set_cbs(struct qed_ll2_info *p_ll2_info, const struct qed_ll2_cbs *cbs)
13028c2ecf20Sopenharmony_ci{
13038c2ecf20Sopenharmony_ci	if (!cbs || (!cbs->rx_comp_cb ||
13048c2ecf20Sopenharmony_ci		     !cbs->rx_release_cb ||
13058c2ecf20Sopenharmony_ci		     !cbs->tx_comp_cb || !cbs->tx_release_cb || !cbs->cookie))
13068c2ecf20Sopenharmony_ci		return -EINVAL;
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci	p_ll2_info->cbs.rx_comp_cb = cbs->rx_comp_cb;
13098c2ecf20Sopenharmony_ci	p_ll2_info->cbs.rx_release_cb = cbs->rx_release_cb;
13108c2ecf20Sopenharmony_ci	p_ll2_info->cbs.tx_comp_cb = cbs->tx_comp_cb;
13118c2ecf20Sopenharmony_ci	p_ll2_info->cbs.tx_release_cb = cbs->tx_release_cb;
13128c2ecf20Sopenharmony_ci	p_ll2_info->cbs.slowpath_cb = cbs->slowpath_cb;
13138c2ecf20Sopenharmony_ci	p_ll2_info->cbs.cookie = cbs->cookie;
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci	return 0;
13168c2ecf20Sopenharmony_ci}
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_cistatic void _qed_ll2_calc_allowed_conns(struct qed_hwfn *p_hwfn,
13198c2ecf20Sopenharmony_ci					struct qed_ll2_acquire_data *data,
13208c2ecf20Sopenharmony_ci					u8 *start_idx, u8 *last_idx)
13218c2ecf20Sopenharmony_ci{
13228c2ecf20Sopenharmony_ci	/* LL2 queues handles will be split as follows:
13238c2ecf20Sopenharmony_ci	 * First will be the legacy queues, and then the ctx based.
13248c2ecf20Sopenharmony_ci	 */
13258c2ecf20Sopenharmony_ci	if (data->input.rx_conn_type == QED_LL2_RX_TYPE_LEGACY) {
13268c2ecf20Sopenharmony_ci		*start_idx = QED_LL2_LEGACY_CONN_BASE_PF;
13278c2ecf20Sopenharmony_ci		*last_idx = *start_idx +
13288c2ecf20Sopenharmony_ci			QED_MAX_NUM_OF_LEGACY_LL2_CONNS_PF;
13298c2ecf20Sopenharmony_ci	} else {
13308c2ecf20Sopenharmony_ci		/* QED_LL2_RX_TYPE_CTX */
13318c2ecf20Sopenharmony_ci		*start_idx = QED_LL2_CTX_CONN_BASE_PF;
13328c2ecf20Sopenharmony_ci		*last_idx = *start_idx +
13338c2ecf20Sopenharmony_ci			QED_MAX_NUM_OF_CTX_LL2_CONNS_PF;
13348c2ecf20Sopenharmony_ci	}
13358c2ecf20Sopenharmony_ci}
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_cistatic enum core_error_handle
13388c2ecf20Sopenharmony_ciqed_ll2_get_error_choice(enum qed_ll2_error_handle err)
13398c2ecf20Sopenharmony_ci{
13408c2ecf20Sopenharmony_ci	switch (err) {
13418c2ecf20Sopenharmony_ci	case QED_LL2_DROP_PACKET:
13428c2ecf20Sopenharmony_ci		return LL2_DROP_PACKET;
13438c2ecf20Sopenharmony_ci	case QED_LL2_DO_NOTHING:
13448c2ecf20Sopenharmony_ci		return LL2_DO_NOTHING;
13458c2ecf20Sopenharmony_ci	case QED_LL2_ASSERT:
13468c2ecf20Sopenharmony_ci		return LL2_ASSERT;
13478c2ecf20Sopenharmony_ci	default:
13488c2ecf20Sopenharmony_ci		return LL2_DO_NOTHING;
13498c2ecf20Sopenharmony_ci	}
13508c2ecf20Sopenharmony_ci}
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ciint qed_ll2_acquire_connection(void *cxt, struct qed_ll2_acquire_data *data)
13538c2ecf20Sopenharmony_ci{
13548c2ecf20Sopenharmony_ci	struct qed_hwfn *p_hwfn = cxt;
13558c2ecf20Sopenharmony_ci	qed_int_comp_cb_t comp_rx_cb, comp_tx_cb;
13568c2ecf20Sopenharmony_ci	struct qed_ll2_info *p_ll2_info = NULL;
13578c2ecf20Sopenharmony_ci	u8 i, first_idx, last_idx, *p_tx_max;
13588c2ecf20Sopenharmony_ci	int rc;
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ci	if (!data->p_connection_handle || !p_hwfn->p_ll2_info)
13618c2ecf20Sopenharmony_ci		return -EINVAL;
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	_qed_ll2_calc_allowed_conns(p_hwfn, data, &first_idx, &last_idx);
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci	/* Find a free connection to be used */
13668c2ecf20Sopenharmony_ci	for (i = first_idx; i < last_idx; i++) {
13678c2ecf20Sopenharmony_ci		mutex_lock(&p_hwfn->p_ll2_info[i].mutex);
13688c2ecf20Sopenharmony_ci		if (p_hwfn->p_ll2_info[i].b_active) {
13698c2ecf20Sopenharmony_ci			mutex_unlock(&p_hwfn->p_ll2_info[i].mutex);
13708c2ecf20Sopenharmony_ci			continue;
13718c2ecf20Sopenharmony_ci		}
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_ci		p_hwfn->p_ll2_info[i].b_active = true;
13748c2ecf20Sopenharmony_ci		p_ll2_info = &p_hwfn->p_ll2_info[i];
13758c2ecf20Sopenharmony_ci		mutex_unlock(&p_hwfn->p_ll2_info[i].mutex);
13768c2ecf20Sopenharmony_ci		break;
13778c2ecf20Sopenharmony_ci	}
13788c2ecf20Sopenharmony_ci	if (!p_ll2_info)
13798c2ecf20Sopenharmony_ci		return -EBUSY;
13808c2ecf20Sopenharmony_ci
13818c2ecf20Sopenharmony_ci	memcpy(&p_ll2_info->input, &data->input, sizeof(p_ll2_info->input));
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci	switch (data->input.tx_dest) {
13848c2ecf20Sopenharmony_ci	case QED_LL2_TX_DEST_NW:
13858c2ecf20Sopenharmony_ci		p_ll2_info->tx_dest = CORE_TX_DEST_NW;
13868c2ecf20Sopenharmony_ci		break;
13878c2ecf20Sopenharmony_ci	case QED_LL2_TX_DEST_LB:
13888c2ecf20Sopenharmony_ci		p_ll2_info->tx_dest = CORE_TX_DEST_LB;
13898c2ecf20Sopenharmony_ci		break;
13908c2ecf20Sopenharmony_ci	case QED_LL2_TX_DEST_DROP:
13918c2ecf20Sopenharmony_ci		p_ll2_info->tx_dest = CORE_TX_DEST_DROP;
13928c2ecf20Sopenharmony_ci		break;
13938c2ecf20Sopenharmony_ci	default:
13948c2ecf20Sopenharmony_ci		return -EINVAL;
13958c2ecf20Sopenharmony_ci	}
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci	if (data->input.conn_type == QED_LL2_TYPE_OOO ||
13988c2ecf20Sopenharmony_ci	    data->input.secondary_queue)
13998c2ecf20Sopenharmony_ci		p_ll2_info->main_func_queue = false;
14008c2ecf20Sopenharmony_ci	else
14018c2ecf20Sopenharmony_ci		p_ll2_info->main_func_queue = true;
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci	/* Correct maximum number of Tx BDs */
14048c2ecf20Sopenharmony_ci	p_tx_max = &p_ll2_info->input.tx_max_bds_per_packet;
14058c2ecf20Sopenharmony_ci	if (*p_tx_max == 0)
14068c2ecf20Sopenharmony_ci		*p_tx_max = CORE_LL2_TX_MAX_BDS_PER_PACKET;
14078c2ecf20Sopenharmony_ci	else
14088c2ecf20Sopenharmony_ci		*p_tx_max = min_t(u8, *p_tx_max,
14098c2ecf20Sopenharmony_ci				  CORE_LL2_TX_MAX_BDS_PER_PACKET);
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_ci	rc = qed_ll2_set_cbs(p_ll2_info, data->cbs);
14128c2ecf20Sopenharmony_ci	if (rc) {
14138c2ecf20Sopenharmony_ci		DP_NOTICE(p_hwfn, "Invalid callback functions\n");
14148c2ecf20Sopenharmony_ci		goto q_allocate_fail;
14158c2ecf20Sopenharmony_ci	}
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci	rc = qed_ll2_acquire_connection_rx(p_hwfn, p_ll2_info);
14188c2ecf20Sopenharmony_ci	if (rc)
14198c2ecf20Sopenharmony_ci		goto q_allocate_fail;
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci	rc = qed_ll2_acquire_connection_tx(p_hwfn, p_ll2_info);
14228c2ecf20Sopenharmony_ci	if (rc)
14238c2ecf20Sopenharmony_ci		goto q_allocate_fail;
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci	rc = qed_ll2_acquire_connection_ooo(p_hwfn, p_ll2_info,
14268c2ecf20Sopenharmony_ci					    data->input.mtu);
14278c2ecf20Sopenharmony_ci	if (rc)
14288c2ecf20Sopenharmony_ci		goto q_allocate_fail;
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_ci	/* Register callbacks for the Rx/Tx queues */
14318c2ecf20Sopenharmony_ci	if (data->input.conn_type == QED_LL2_TYPE_OOO) {
14328c2ecf20Sopenharmony_ci		comp_rx_cb = qed_ll2_lb_rxq_completion;
14338c2ecf20Sopenharmony_ci		comp_tx_cb = qed_ll2_lb_txq_completion;
14348c2ecf20Sopenharmony_ci	} else {
14358c2ecf20Sopenharmony_ci		comp_rx_cb = qed_ll2_rxq_completion;
14368c2ecf20Sopenharmony_ci		comp_tx_cb = qed_ll2_txq_completion;
14378c2ecf20Sopenharmony_ci	}
14388c2ecf20Sopenharmony_ci
14398c2ecf20Sopenharmony_ci	if (data->input.rx_num_desc) {
14408c2ecf20Sopenharmony_ci		qed_int_register_cb(p_hwfn, comp_rx_cb,
14418c2ecf20Sopenharmony_ci				    &p_hwfn->p_ll2_info[i],
14428c2ecf20Sopenharmony_ci				    &p_ll2_info->rx_queue.rx_sb_index,
14438c2ecf20Sopenharmony_ci				    &p_ll2_info->rx_queue.p_fw_cons);
14448c2ecf20Sopenharmony_ci		p_ll2_info->rx_queue.b_cb_registered = true;
14458c2ecf20Sopenharmony_ci	}
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_ci	if (data->input.tx_num_desc) {
14488c2ecf20Sopenharmony_ci		qed_int_register_cb(p_hwfn,
14498c2ecf20Sopenharmony_ci				    comp_tx_cb,
14508c2ecf20Sopenharmony_ci				    &p_hwfn->p_ll2_info[i],
14518c2ecf20Sopenharmony_ci				    &p_ll2_info->tx_queue.tx_sb_index,
14528c2ecf20Sopenharmony_ci				    &p_ll2_info->tx_queue.p_fw_cons);
14538c2ecf20Sopenharmony_ci		p_ll2_info->tx_queue.b_cb_registered = true;
14548c2ecf20Sopenharmony_ci	}
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci	*data->p_connection_handle = i;
14578c2ecf20Sopenharmony_ci	return rc;
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_ciq_allocate_fail:
14608c2ecf20Sopenharmony_ci	qed_ll2_release_connection(p_hwfn, i);
14618c2ecf20Sopenharmony_ci	return -ENOMEM;
14628c2ecf20Sopenharmony_ci}
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_cistatic int qed_ll2_establish_connection_rx(struct qed_hwfn *p_hwfn,
14658c2ecf20Sopenharmony_ci					   struct qed_ll2_info *p_ll2_conn)
14668c2ecf20Sopenharmony_ci{
14678c2ecf20Sopenharmony_ci	enum qed_ll2_error_handle error_input;
14688c2ecf20Sopenharmony_ci	enum core_error_handle error_mode;
14698c2ecf20Sopenharmony_ci	u8 action_on_error = 0;
14708c2ecf20Sopenharmony_ci	int rc;
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_ci	if (!QED_LL2_RX_REGISTERED(p_ll2_conn))
14738c2ecf20Sopenharmony_ci		return 0;
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_ci	DIRECT_REG_WR(p_ll2_conn->rx_queue.set_prod_addr, 0x0);
14768c2ecf20Sopenharmony_ci	error_input = p_ll2_conn->input.ai_err_packet_too_big;
14778c2ecf20Sopenharmony_ci	error_mode = qed_ll2_get_error_choice(error_input);
14788c2ecf20Sopenharmony_ci	SET_FIELD(action_on_error,
14798c2ecf20Sopenharmony_ci		  CORE_RX_ACTION_ON_ERROR_PACKET_TOO_BIG, error_mode);
14808c2ecf20Sopenharmony_ci	error_input = p_ll2_conn->input.ai_err_no_buf;
14818c2ecf20Sopenharmony_ci	error_mode = qed_ll2_get_error_choice(error_input);
14828c2ecf20Sopenharmony_ci	SET_FIELD(action_on_error, CORE_RX_ACTION_ON_ERROR_NO_BUFF, error_mode);
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci	rc = qed_sp_ll2_rx_queue_start(p_hwfn, p_ll2_conn, action_on_error);
14858c2ecf20Sopenharmony_ci	if (rc)
14868c2ecf20Sopenharmony_ci		return rc;
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci	if (p_ll2_conn->rx_queue.ctx_based) {
14898c2ecf20Sopenharmony_ci		rc = qed_db_recovery_add(p_hwfn->cdev,
14908c2ecf20Sopenharmony_ci					 p_ll2_conn->rx_queue.set_prod_addr,
14918c2ecf20Sopenharmony_ci					 &p_ll2_conn->rx_queue.db_data,
14928c2ecf20Sopenharmony_ci					 DB_REC_WIDTH_64B, DB_REC_KERNEL);
14938c2ecf20Sopenharmony_ci	}
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_ci	return rc;
14968c2ecf20Sopenharmony_ci}
14978c2ecf20Sopenharmony_ci
14988c2ecf20Sopenharmony_cistatic void
14998c2ecf20Sopenharmony_ciqed_ll2_establish_connection_ooo(struct qed_hwfn *p_hwfn,
15008c2ecf20Sopenharmony_ci				 struct qed_ll2_info *p_ll2_conn)
15018c2ecf20Sopenharmony_ci{
15028c2ecf20Sopenharmony_ci	if (p_ll2_conn->input.conn_type != QED_LL2_TYPE_OOO)
15038c2ecf20Sopenharmony_ci		return;
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci	qed_ooo_release_all_isles(p_hwfn, p_hwfn->p_ooo_info);
15068c2ecf20Sopenharmony_ci	qed_ooo_submit_rx_buffers(p_hwfn, p_ll2_conn);
15078c2ecf20Sopenharmony_ci}
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_cistatic inline u8 qed_ll2_handle_to_queue_id(struct qed_hwfn *p_hwfn,
15108c2ecf20Sopenharmony_ci					    u8 handle,
15118c2ecf20Sopenharmony_ci					    u8 ll2_queue_type)
15128c2ecf20Sopenharmony_ci{
15138c2ecf20Sopenharmony_ci	u8 qid;
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci	if (ll2_queue_type == QED_LL2_RX_TYPE_LEGACY)
15168c2ecf20Sopenharmony_ci		return p_hwfn->hw_info.resc_start[QED_LL2_RAM_QUEUE] + handle;
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_ci	/* QED_LL2_RX_TYPE_CTX
15198c2ecf20Sopenharmony_ci	 * FW distinguishes between the legacy queues (ram based) and the
15208c2ecf20Sopenharmony_ci	 * ctx based queues by the queue_id.
15218c2ecf20Sopenharmony_ci	 * The first MAX_NUM_LL2_RX_RAM_QUEUES queues are legacy
15228c2ecf20Sopenharmony_ci	 * and the queue ids above that are ctx base.
15238c2ecf20Sopenharmony_ci	 */
15248c2ecf20Sopenharmony_ci	qid = p_hwfn->hw_info.resc_start[QED_LL2_CTX_QUEUE] +
15258c2ecf20Sopenharmony_ci	      MAX_NUM_LL2_RX_RAM_QUEUES;
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_ci	/* See comment on the acquire connection for how the ll2
15288c2ecf20Sopenharmony_ci	 * queues handles are divided.
15298c2ecf20Sopenharmony_ci	 */
15308c2ecf20Sopenharmony_ci	qid += (handle - QED_MAX_NUM_OF_LEGACY_LL2_CONNS_PF);
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci	return qid;
15338c2ecf20Sopenharmony_ci}
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ciint qed_ll2_establish_connection(void *cxt, u8 connection_handle)
15368c2ecf20Sopenharmony_ci{
15378c2ecf20Sopenharmony_ci	struct e4_core_conn_context *p_cxt;
15388c2ecf20Sopenharmony_ci	struct qed_ll2_tx_packet *p_pkt;
15398c2ecf20Sopenharmony_ci	struct qed_ll2_info *p_ll2_conn;
15408c2ecf20Sopenharmony_ci	struct qed_hwfn *p_hwfn = cxt;
15418c2ecf20Sopenharmony_ci	struct qed_ll2_rx_queue *p_rx;
15428c2ecf20Sopenharmony_ci	struct qed_ll2_tx_queue *p_tx;
15438c2ecf20Sopenharmony_ci	struct qed_cxt_info cxt_info;
15448c2ecf20Sopenharmony_ci	struct qed_ptt *p_ptt;
15458c2ecf20Sopenharmony_ci	int rc = -EINVAL;
15468c2ecf20Sopenharmony_ci	u32 i, capacity;
15478c2ecf20Sopenharmony_ci	size_t desc_size;
15488c2ecf20Sopenharmony_ci	u8 qid;
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_ci	p_ptt = qed_ptt_acquire(p_hwfn);
15518c2ecf20Sopenharmony_ci	if (!p_ptt)
15528c2ecf20Sopenharmony_ci		return -EAGAIN;
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	p_ll2_conn = qed_ll2_handle_sanity_lock(p_hwfn, connection_handle);
15558c2ecf20Sopenharmony_ci	if (!p_ll2_conn) {
15568c2ecf20Sopenharmony_ci		rc = -EINVAL;
15578c2ecf20Sopenharmony_ci		goto out;
15588c2ecf20Sopenharmony_ci	}
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	p_rx = &p_ll2_conn->rx_queue;
15618c2ecf20Sopenharmony_ci	p_tx = &p_ll2_conn->tx_queue;
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_ci	qed_chain_reset(&p_rx->rxq_chain);
15648c2ecf20Sopenharmony_ci	qed_chain_reset(&p_rx->rcq_chain);
15658c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&p_rx->active_descq);
15668c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&p_rx->free_descq);
15678c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&p_rx->posting_descq);
15688c2ecf20Sopenharmony_ci	spin_lock_init(&p_rx->lock);
15698c2ecf20Sopenharmony_ci	capacity = qed_chain_get_capacity(&p_rx->rxq_chain);
15708c2ecf20Sopenharmony_ci	for (i = 0; i < capacity; i++)
15718c2ecf20Sopenharmony_ci		list_add_tail(&p_rx->descq_array[i].list_entry,
15728c2ecf20Sopenharmony_ci			      &p_rx->free_descq);
15738c2ecf20Sopenharmony_ci	*p_rx->p_fw_cons = 0;
15748c2ecf20Sopenharmony_ci
15758c2ecf20Sopenharmony_ci	qed_chain_reset(&p_tx->txq_chain);
15768c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&p_tx->active_descq);
15778c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&p_tx->free_descq);
15788c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&p_tx->sending_descq);
15798c2ecf20Sopenharmony_ci	spin_lock_init(&p_tx->lock);
15808c2ecf20Sopenharmony_ci	capacity = qed_chain_get_capacity(&p_tx->txq_chain);
15818c2ecf20Sopenharmony_ci	/* All bds_set elements are flexibily added. */
15828c2ecf20Sopenharmony_ci	desc_size = struct_size(p_pkt, bds_set,
15838c2ecf20Sopenharmony_ci				p_ll2_conn->input.tx_max_bds_per_packet);
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_ci	for (i = 0; i < capacity; i++) {
15868c2ecf20Sopenharmony_ci		p_pkt = p_tx->descq_mem + desc_size * i;
15878c2ecf20Sopenharmony_ci		list_add_tail(&p_pkt->list_entry, &p_tx->free_descq);
15888c2ecf20Sopenharmony_ci	}
15898c2ecf20Sopenharmony_ci	p_tx->cur_completing_bd_idx = 0;
15908c2ecf20Sopenharmony_ci	p_tx->bds_idx = 0;
15918c2ecf20Sopenharmony_ci	p_tx->b_completing_packet = false;
15928c2ecf20Sopenharmony_ci	p_tx->cur_send_packet = NULL;
15938c2ecf20Sopenharmony_ci	p_tx->cur_send_frag_num = 0;
15948c2ecf20Sopenharmony_ci	p_tx->cur_completing_frag_num = 0;
15958c2ecf20Sopenharmony_ci	*p_tx->p_fw_cons = 0;
15968c2ecf20Sopenharmony_ci
15978c2ecf20Sopenharmony_ci	rc = qed_cxt_acquire_cid(p_hwfn, PROTOCOLID_CORE, &p_ll2_conn->cid);
15988c2ecf20Sopenharmony_ci	if (rc)
15998c2ecf20Sopenharmony_ci		goto out;
16008c2ecf20Sopenharmony_ci	cxt_info.iid = p_ll2_conn->cid;
16018c2ecf20Sopenharmony_ci	rc = qed_cxt_get_cid_info(p_hwfn, &cxt_info);
16028c2ecf20Sopenharmony_ci	if (rc) {
16038c2ecf20Sopenharmony_ci		DP_NOTICE(p_hwfn, "Cannot find context info for cid=%d\n",
16048c2ecf20Sopenharmony_ci			  p_ll2_conn->cid);
16058c2ecf20Sopenharmony_ci		goto out;
16068c2ecf20Sopenharmony_ci	}
16078c2ecf20Sopenharmony_ci
16088c2ecf20Sopenharmony_ci	p_cxt = cxt_info.p_cxt;
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_ci	memset(p_cxt, 0, sizeof(*p_cxt));
16118c2ecf20Sopenharmony_ci
16128c2ecf20Sopenharmony_ci	qid = qed_ll2_handle_to_queue_id(p_hwfn, connection_handle,
16138c2ecf20Sopenharmony_ci					 p_ll2_conn->input.rx_conn_type);
16148c2ecf20Sopenharmony_ci	p_ll2_conn->queue_id = qid;
16158c2ecf20Sopenharmony_ci	p_ll2_conn->tx_stats_id = qid;
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_ci	DP_VERBOSE(p_hwfn, QED_MSG_LL2,
16188c2ecf20Sopenharmony_ci		   "Establishing ll2 queue. PF %d ctx_based=%d abs qid=%d\n",
16198c2ecf20Sopenharmony_ci		   p_hwfn->rel_pf_id, p_ll2_conn->input.rx_conn_type, qid);
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ci	if (p_ll2_conn->input.rx_conn_type == QED_LL2_RX_TYPE_LEGACY) {
16228c2ecf20Sopenharmony_ci		p_rx->set_prod_addr = p_hwfn->regview +
16238c2ecf20Sopenharmony_ci		    GTT_BAR0_MAP_REG_TSDM_RAM + TSTORM_LL2_RX_PRODS_OFFSET(qid);
16248c2ecf20Sopenharmony_ci	} else {
16258c2ecf20Sopenharmony_ci		/* QED_LL2_RX_TYPE_CTX - using doorbell */
16268c2ecf20Sopenharmony_ci		p_rx->ctx_based = 1;
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci		p_rx->set_prod_addr = p_hwfn->doorbells +
16298c2ecf20Sopenharmony_ci			p_hwfn->dpi_start_offset +
16308c2ecf20Sopenharmony_ci			DB_ADDR_SHIFT(DQ_PWM_OFFSET_TCM_LL2_PROD_UPDATE);
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_ci		/* prepare db data */
16338c2ecf20Sopenharmony_ci		p_rx->db_data.icid = cpu_to_le16((u16)p_ll2_conn->cid);
16348c2ecf20Sopenharmony_ci		SET_FIELD(p_rx->db_data.params,
16358c2ecf20Sopenharmony_ci			  CORE_PWM_PROD_UPDATE_DATA_AGG_CMD, DB_AGG_CMD_SET);
16368c2ecf20Sopenharmony_ci		SET_FIELD(p_rx->db_data.params,
16378c2ecf20Sopenharmony_ci			  CORE_PWM_PROD_UPDATE_DATA_RESERVED1, 0);
16388c2ecf20Sopenharmony_ci	}
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_ci	p_tx->doorbell_addr = (u8 __iomem *)p_hwfn->doorbells +
16418c2ecf20Sopenharmony_ci					    qed_db_addr(p_ll2_conn->cid,
16428c2ecf20Sopenharmony_ci							DQ_DEMS_LEGACY);
16438c2ecf20Sopenharmony_ci	/* prepare db data */
16448c2ecf20Sopenharmony_ci	SET_FIELD(p_tx->db_msg.params, CORE_DB_DATA_DEST, DB_DEST_XCM);
16458c2ecf20Sopenharmony_ci	SET_FIELD(p_tx->db_msg.params, CORE_DB_DATA_AGG_CMD, DB_AGG_CMD_SET);
16468c2ecf20Sopenharmony_ci	SET_FIELD(p_tx->db_msg.params, CORE_DB_DATA_AGG_VAL_SEL,
16478c2ecf20Sopenharmony_ci		  DQ_XCM_CORE_TX_BD_PROD_CMD);
16488c2ecf20Sopenharmony_ci	p_tx->db_msg.agg_flags = DQ_XCM_CORE_DQ_CF_CMD;
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_ci	rc = qed_ll2_establish_connection_rx(p_hwfn, p_ll2_conn);
16518c2ecf20Sopenharmony_ci	if (rc)
16528c2ecf20Sopenharmony_ci		goto out;
16538c2ecf20Sopenharmony_ci
16548c2ecf20Sopenharmony_ci	rc = qed_sp_ll2_tx_queue_start(p_hwfn, p_ll2_conn);
16558c2ecf20Sopenharmony_ci	if (rc)
16568c2ecf20Sopenharmony_ci		goto out;
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_ci	if (!QED_IS_RDMA_PERSONALITY(p_hwfn))
16598c2ecf20Sopenharmony_ci		qed_wr(p_hwfn, p_ptt, PRS_REG_USE_LIGHT_L2, 1);
16608c2ecf20Sopenharmony_ci
16618c2ecf20Sopenharmony_ci	qed_ll2_establish_connection_ooo(p_hwfn, p_ll2_conn);
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci	if (p_ll2_conn->input.conn_type == QED_LL2_TYPE_FCOE) {
16648c2ecf20Sopenharmony_ci		if (!test_bit(QED_MF_UFP_SPECIFIC, &p_hwfn->cdev->mf_bits))
16658c2ecf20Sopenharmony_ci			qed_llh_add_protocol_filter(p_hwfn->cdev, 0,
16668c2ecf20Sopenharmony_ci						    QED_LLH_FILTER_ETHERTYPE,
16678c2ecf20Sopenharmony_ci						    ETH_P_FCOE, 0);
16688c2ecf20Sopenharmony_ci		qed_llh_add_protocol_filter(p_hwfn->cdev, 0,
16698c2ecf20Sopenharmony_ci					    QED_LLH_FILTER_ETHERTYPE,
16708c2ecf20Sopenharmony_ci					    ETH_P_FIP, 0);
16718c2ecf20Sopenharmony_ci	}
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_ciout:
16748c2ecf20Sopenharmony_ci	qed_ptt_release(p_hwfn, p_ptt);
16758c2ecf20Sopenharmony_ci	return rc;
16768c2ecf20Sopenharmony_ci}
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_cistatic void qed_ll2_post_rx_buffer_notify_fw(struct qed_hwfn *p_hwfn,
16798c2ecf20Sopenharmony_ci					     struct qed_ll2_rx_queue *p_rx,
16808c2ecf20Sopenharmony_ci					     struct qed_ll2_rx_packet *p_curp)
16818c2ecf20Sopenharmony_ci{
16828c2ecf20Sopenharmony_ci	struct qed_ll2_rx_packet *p_posting_packet = NULL;
16838c2ecf20Sopenharmony_ci	struct core_ll2_rx_prod rx_prod = { 0, 0 };
16848c2ecf20Sopenharmony_ci	bool b_notify_fw = false;
16858c2ecf20Sopenharmony_ci	u16 bd_prod, cq_prod;
16868c2ecf20Sopenharmony_ci
16878c2ecf20Sopenharmony_ci	/* This handles the flushing of already posted buffers */
16888c2ecf20Sopenharmony_ci	while (!list_empty(&p_rx->posting_descq)) {
16898c2ecf20Sopenharmony_ci		p_posting_packet = list_first_entry(&p_rx->posting_descq,
16908c2ecf20Sopenharmony_ci						    struct qed_ll2_rx_packet,
16918c2ecf20Sopenharmony_ci						    list_entry);
16928c2ecf20Sopenharmony_ci		list_move_tail(&p_posting_packet->list_entry,
16938c2ecf20Sopenharmony_ci			       &p_rx->active_descq);
16948c2ecf20Sopenharmony_ci		b_notify_fw = true;
16958c2ecf20Sopenharmony_ci	}
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_ci	/* This handles the supplied packet [if there is one] */
16988c2ecf20Sopenharmony_ci	if (p_curp) {
16998c2ecf20Sopenharmony_ci		list_add_tail(&p_curp->list_entry, &p_rx->active_descq);
17008c2ecf20Sopenharmony_ci		b_notify_fw = true;
17018c2ecf20Sopenharmony_ci	}
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_ci	if (!b_notify_fw)
17048c2ecf20Sopenharmony_ci		return;
17058c2ecf20Sopenharmony_ci
17068c2ecf20Sopenharmony_ci	bd_prod = qed_chain_get_prod_idx(&p_rx->rxq_chain);
17078c2ecf20Sopenharmony_ci	cq_prod = qed_chain_get_prod_idx(&p_rx->rcq_chain);
17088c2ecf20Sopenharmony_ci	if (p_rx->ctx_based) {
17098c2ecf20Sopenharmony_ci		/* update producer by giving a doorbell */
17108c2ecf20Sopenharmony_ci		p_rx->db_data.prod.bd_prod = cpu_to_le16(bd_prod);
17118c2ecf20Sopenharmony_ci		p_rx->db_data.prod.cqe_prod = cpu_to_le16(cq_prod);
17128c2ecf20Sopenharmony_ci		/* Make sure chain element is updated before ringing the
17138c2ecf20Sopenharmony_ci		 * doorbell
17148c2ecf20Sopenharmony_ci		 */
17158c2ecf20Sopenharmony_ci		dma_wmb();
17168c2ecf20Sopenharmony_ci		DIRECT_REG_WR64(p_rx->set_prod_addr,
17178c2ecf20Sopenharmony_ci				*((u64 *)&p_rx->db_data));
17188c2ecf20Sopenharmony_ci	} else {
17198c2ecf20Sopenharmony_ci		rx_prod.bd_prod = cpu_to_le16(bd_prod);
17208c2ecf20Sopenharmony_ci		rx_prod.cqe_prod = cpu_to_le16(cq_prod);
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_ci		/* Make sure chain element is updated before ringing the
17238c2ecf20Sopenharmony_ci		 * doorbell
17248c2ecf20Sopenharmony_ci		 */
17258c2ecf20Sopenharmony_ci		dma_wmb();
17268c2ecf20Sopenharmony_ci
17278c2ecf20Sopenharmony_ci		DIRECT_REG_WR(p_rx->set_prod_addr, *((u32 *)&rx_prod));
17288c2ecf20Sopenharmony_ci	}
17298c2ecf20Sopenharmony_ci}
17308c2ecf20Sopenharmony_ci
17318c2ecf20Sopenharmony_ciint qed_ll2_post_rx_buffer(void *cxt,
17328c2ecf20Sopenharmony_ci			   u8 connection_handle,
17338c2ecf20Sopenharmony_ci			   dma_addr_t addr,
17348c2ecf20Sopenharmony_ci			   u16 buf_len, void *cookie, u8 notify_fw)
17358c2ecf20Sopenharmony_ci{
17368c2ecf20Sopenharmony_ci	struct qed_hwfn *p_hwfn = cxt;
17378c2ecf20Sopenharmony_ci	struct core_rx_bd_with_buff_len *p_curb = NULL;
17388c2ecf20Sopenharmony_ci	struct qed_ll2_rx_packet *p_curp = NULL;
17398c2ecf20Sopenharmony_ci	struct qed_ll2_info *p_ll2_conn;
17408c2ecf20Sopenharmony_ci	struct qed_ll2_rx_queue *p_rx;
17418c2ecf20Sopenharmony_ci	unsigned long flags;
17428c2ecf20Sopenharmony_ci	void *p_data;
17438c2ecf20Sopenharmony_ci	int rc = 0;
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci	p_ll2_conn = qed_ll2_handle_sanity(p_hwfn, connection_handle);
17468c2ecf20Sopenharmony_ci	if (!p_ll2_conn)
17478c2ecf20Sopenharmony_ci		return -EINVAL;
17488c2ecf20Sopenharmony_ci	p_rx = &p_ll2_conn->rx_queue;
17498c2ecf20Sopenharmony_ci	if (!p_rx->set_prod_addr)
17508c2ecf20Sopenharmony_ci		return -EIO;
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci	spin_lock_irqsave(&p_rx->lock, flags);
17538c2ecf20Sopenharmony_ci	if (!list_empty(&p_rx->free_descq))
17548c2ecf20Sopenharmony_ci		p_curp = list_first_entry(&p_rx->free_descq,
17558c2ecf20Sopenharmony_ci					  struct qed_ll2_rx_packet, list_entry);
17568c2ecf20Sopenharmony_ci	if (p_curp) {
17578c2ecf20Sopenharmony_ci		if (qed_chain_get_elem_left(&p_rx->rxq_chain) &&
17588c2ecf20Sopenharmony_ci		    qed_chain_get_elem_left(&p_rx->rcq_chain)) {
17598c2ecf20Sopenharmony_ci			p_data = qed_chain_produce(&p_rx->rxq_chain);
17608c2ecf20Sopenharmony_ci			p_curb = (struct core_rx_bd_with_buff_len *)p_data;
17618c2ecf20Sopenharmony_ci			qed_chain_produce(&p_rx->rcq_chain);
17628c2ecf20Sopenharmony_ci		}
17638c2ecf20Sopenharmony_ci	}
17648c2ecf20Sopenharmony_ci
17658c2ecf20Sopenharmony_ci	/* If we're lacking entires, let's try to flush buffers to FW */
17668c2ecf20Sopenharmony_ci	if (!p_curp || !p_curb) {
17678c2ecf20Sopenharmony_ci		rc = -EBUSY;
17688c2ecf20Sopenharmony_ci		p_curp = NULL;
17698c2ecf20Sopenharmony_ci		goto out_notify;
17708c2ecf20Sopenharmony_ci	}
17718c2ecf20Sopenharmony_ci
17728c2ecf20Sopenharmony_ci	/* We have an Rx packet we can fill */
17738c2ecf20Sopenharmony_ci	DMA_REGPAIR_LE(p_curb->addr, addr);
17748c2ecf20Sopenharmony_ci	p_curb->buff_length = cpu_to_le16(buf_len);
17758c2ecf20Sopenharmony_ci	p_curp->rx_buf_addr = addr;
17768c2ecf20Sopenharmony_ci	p_curp->cookie = cookie;
17778c2ecf20Sopenharmony_ci	p_curp->rxq_bd = p_curb;
17788c2ecf20Sopenharmony_ci	p_curp->buf_length = buf_len;
17798c2ecf20Sopenharmony_ci	list_del(&p_curp->list_entry);
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_ci	/* Check if we only want to enqueue this packet without informing FW */
17828c2ecf20Sopenharmony_ci	if (!notify_fw) {
17838c2ecf20Sopenharmony_ci		list_add_tail(&p_curp->list_entry, &p_rx->posting_descq);
17848c2ecf20Sopenharmony_ci		goto out;
17858c2ecf20Sopenharmony_ci	}
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_ciout_notify:
17888c2ecf20Sopenharmony_ci	qed_ll2_post_rx_buffer_notify_fw(p_hwfn, p_rx, p_curp);
17898c2ecf20Sopenharmony_ciout:
17908c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&p_rx->lock, flags);
17918c2ecf20Sopenharmony_ci	return rc;
17928c2ecf20Sopenharmony_ci}
17938c2ecf20Sopenharmony_ci
17948c2ecf20Sopenharmony_cistatic void qed_ll2_prepare_tx_packet_set(struct qed_hwfn *p_hwfn,
17958c2ecf20Sopenharmony_ci					  struct qed_ll2_tx_queue *p_tx,
17968c2ecf20Sopenharmony_ci					  struct qed_ll2_tx_packet *p_curp,
17978c2ecf20Sopenharmony_ci					  struct qed_ll2_tx_pkt_info *pkt,
17988c2ecf20Sopenharmony_ci					  u8 notify_fw)
17998c2ecf20Sopenharmony_ci{
18008c2ecf20Sopenharmony_ci	list_del(&p_curp->list_entry);
18018c2ecf20Sopenharmony_ci	p_curp->cookie = pkt->cookie;
18028c2ecf20Sopenharmony_ci	p_curp->bd_used = pkt->num_of_bds;
18038c2ecf20Sopenharmony_ci	p_curp->notify_fw = notify_fw;
18048c2ecf20Sopenharmony_ci	p_tx->cur_send_packet = p_curp;
18058c2ecf20Sopenharmony_ci	p_tx->cur_send_frag_num = 0;
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_ci	p_curp->bds_set[p_tx->cur_send_frag_num].tx_frag = pkt->first_frag;
18088c2ecf20Sopenharmony_ci	p_curp->bds_set[p_tx->cur_send_frag_num].frag_len = pkt->first_frag_len;
18098c2ecf20Sopenharmony_ci	p_tx->cur_send_frag_num++;
18108c2ecf20Sopenharmony_ci}
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_cistatic void
18138c2ecf20Sopenharmony_ciqed_ll2_prepare_tx_packet_set_bd(struct qed_hwfn *p_hwfn,
18148c2ecf20Sopenharmony_ci				 struct qed_ll2_info *p_ll2,
18158c2ecf20Sopenharmony_ci				 struct qed_ll2_tx_packet *p_curp,
18168c2ecf20Sopenharmony_ci				 struct qed_ll2_tx_pkt_info *pkt)
18178c2ecf20Sopenharmony_ci{
18188c2ecf20Sopenharmony_ci	struct qed_chain *p_tx_chain = &p_ll2->tx_queue.txq_chain;
18198c2ecf20Sopenharmony_ci	u16 prod_idx = qed_chain_get_prod_idx(p_tx_chain);
18208c2ecf20Sopenharmony_ci	struct core_tx_bd *start_bd = NULL;
18218c2ecf20Sopenharmony_ci	enum core_roce_flavor_type roce_flavor;
18228c2ecf20Sopenharmony_ci	enum core_tx_dest tx_dest;
18238c2ecf20Sopenharmony_ci	u16 bd_data = 0, frag_idx;
18248c2ecf20Sopenharmony_ci	u16 bitfield1;
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci	roce_flavor = (pkt->qed_roce_flavor == QED_LL2_ROCE) ? CORE_ROCE
18278c2ecf20Sopenharmony_ci							     : CORE_RROCE;
18288c2ecf20Sopenharmony_ci
18298c2ecf20Sopenharmony_ci	switch (pkt->tx_dest) {
18308c2ecf20Sopenharmony_ci	case QED_LL2_TX_DEST_NW:
18318c2ecf20Sopenharmony_ci		tx_dest = CORE_TX_DEST_NW;
18328c2ecf20Sopenharmony_ci		break;
18338c2ecf20Sopenharmony_ci	case QED_LL2_TX_DEST_LB:
18348c2ecf20Sopenharmony_ci		tx_dest = CORE_TX_DEST_LB;
18358c2ecf20Sopenharmony_ci		break;
18368c2ecf20Sopenharmony_ci	case QED_LL2_TX_DEST_DROP:
18378c2ecf20Sopenharmony_ci		tx_dest = CORE_TX_DEST_DROP;
18388c2ecf20Sopenharmony_ci		break;
18398c2ecf20Sopenharmony_ci	default:
18408c2ecf20Sopenharmony_ci		tx_dest = CORE_TX_DEST_LB;
18418c2ecf20Sopenharmony_ci		break;
18428c2ecf20Sopenharmony_ci	}
18438c2ecf20Sopenharmony_ci
18448c2ecf20Sopenharmony_ci	start_bd = (struct core_tx_bd *)qed_chain_produce(p_tx_chain);
18458c2ecf20Sopenharmony_ci	if (QED_IS_IWARP_PERSONALITY(p_hwfn) &&
18468c2ecf20Sopenharmony_ci	    p_ll2->input.conn_type == QED_LL2_TYPE_OOO) {
18478c2ecf20Sopenharmony_ci		start_bd->nw_vlan_or_lb_echo =
18488c2ecf20Sopenharmony_ci		    cpu_to_le16(IWARP_LL2_IN_ORDER_TX_QUEUE);
18498c2ecf20Sopenharmony_ci	} else {
18508c2ecf20Sopenharmony_ci		start_bd->nw_vlan_or_lb_echo = cpu_to_le16(pkt->vlan);
18518c2ecf20Sopenharmony_ci		if (test_bit(QED_MF_UFP_SPECIFIC, &p_hwfn->cdev->mf_bits) &&
18528c2ecf20Sopenharmony_ci		    p_ll2->input.conn_type == QED_LL2_TYPE_FCOE)
18538c2ecf20Sopenharmony_ci			pkt->remove_stag = true;
18548c2ecf20Sopenharmony_ci	}
18558c2ecf20Sopenharmony_ci
18568c2ecf20Sopenharmony_ci	bitfield1 = le16_to_cpu(start_bd->bitfield1);
18578c2ecf20Sopenharmony_ci	SET_FIELD(bitfield1, CORE_TX_BD_L4_HDR_OFFSET_W, pkt->l4_hdr_offset_w);
18588c2ecf20Sopenharmony_ci	SET_FIELD(bitfield1, CORE_TX_BD_TX_DST, tx_dest);
18598c2ecf20Sopenharmony_ci	start_bd->bitfield1 = cpu_to_le16(bitfield1);
18608c2ecf20Sopenharmony_ci
18618c2ecf20Sopenharmony_ci	bd_data |= pkt->bd_flags;
18628c2ecf20Sopenharmony_ci	SET_FIELD(bd_data, CORE_TX_BD_DATA_START_BD, 0x1);
18638c2ecf20Sopenharmony_ci	SET_FIELD(bd_data, CORE_TX_BD_DATA_NBDS, pkt->num_of_bds);
18648c2ecf20Sopenharmony_ci	SET_FIELD(bd_data, CORE_TX_BD_DATA_ROCE_FLAV, roce_flavor);
18658c2ecf20Sopenharmony_ci	SET_FIELD(bd_data, CORE_TX_BD_DATA_IP_CSUM, !!(pkt->enable_ip_cksum));
18668c2ecf20Sopenharmony_ci	SET_FIELD(bd_data, CORE_TX_BD_DATA_L4_CSUM, !!(pkt->enable_l4_cksum));
18678c2ecf20Sopenharmony_ci	SET_FIELD(bd_data, CORE_TX_BD_DATA_IP_LEN, !!(pkt->calc_ip_len));
18688c2ecf20Sopenharmony_ci	SET_FIELD(bd_data, CORE_TX_BD_DATA_DISABLE_STAG_INSERTION,
18698c2ecf20Sopenharmony_ci		  !!(pkt->remove_stag));
18708c2ecf20Sopenharmony_ci
18718c2ecf20Sopenharmony_ci	start_bd->bd_data.as_bitfield = cpu_to_le16(bd_data);
18728c2ecf20Sopenharmony_ci	DMA_REGPAIR_LE(start_bd->addr, pkt->first_frag);
18738c2ecf20Sopenharmony_ci	start_bd->nbytes = cpu_to_le16(pkt->first_frag_len);
18748c2ecf20Sopenharmony_ci
18758c2ecf20Sopenharmony_ci	DP_VERBOSE(p_hwfn,
18768c2ecf20Sopenharmony_ci		   (NETIF_MSG_TX_QUEUED | QED_MSG_LL2),
18778c2ecf20Sopenharmony_ci		   "LL2 [q 0x%02x cid 0x%08x type 0x%08x] Tx Producer at [0x%04x] - set with a %04x bytes %02x BDs buffer at %08x:%08x\n",
18788c2ecf20Sopenharmony_ci		   p_ll2->queue_id,
18798c2ecf20Sopenharmony_ci		   p_ll2->cid,
18808c2ecf20Sopenharmony_ci		   p_ll2->input.conn_type,
18818c2ecf20Sopenharmony_ci		   prod_idx,
18828c2ecf20Sopenharmony_ci		   pkt->first_frag_len,
18838c2ecf20Sopenharmony_ci		   pkt->num_of_bds,
18848c2ecf20Sopenharmony_ci		   le32_to_cpu(start_bd->addr.hi),
18858c2ecf20Sopenharmony_ci		   le32_to_cpu(start_bd->addr.lo));
18868c2ecf20Sopenharmony_ci
18878c2ecf20Sopenharmony_ci	if (p_ll2->tx_queue.cur_send_frag_num == pkt->num_of_bds)
18888c2ecf20Sopenharmony_ci		return;
18898c2ecf20Sopenharmony_ci
18908c2ecf20Sopenharmony_ci	/* Need to provide the packet with additional BDs for frags */
18918c2ecf20Sopenharmony_ci	for (frag_idx = p_ll2->tx_queue.cur_send_frag_num;
18928c2ecf20Sopenharmony_ci	     frag_idx < pkt->num_of_bds; frag_idx++) {
18938c2ecf20Sopenharmony_ci		struct core_tx_bd **p_bd = &p_curp->bds_set[frag_idx].txq_bd;
18948c2ecf20Sopenharmony_ci
18958c2ecf20Sopenharmony_ci		*p_bd = (struct core_tx_bd *)qed_chain_produce(p_tx_chain);
18968c2ecf20Sopenharmony_ci		(*p_bd)->bd_data.as_bitfield = 0;
18978c2ecf20Sopenharmony_ci		(*p_bd)->bitfield1 = 0;
18988c2ecf20Sopenharmony_ci		p_curp->bds_set[frag_idx].tx_frag = 0;
18998c2ecf20Sopenharmony_ci		p_curp->bds_set[frag_idx].frag_len = 0;
19008c2ecf20Sopenharmony_ci	}
19018c2ecf20Sopenharmony_ci}
19028c2ecf20Sopenharmony_ci
19038c2ecf20Sopenharmony_ci/* This should be called while the Txq spinlock is being held */
19048c2ecf20Sopenharmony_cistatic void qed_ll2_tx_packet_notify(struct qed_hwfn *p_hwfn,
19058c2ecf20Sopenharmony_ci				     struct qed_ll2_info *p_ll2_conn)
19068c2ecf20Sopenharmony_ci{
19078c2ecf20Sopenharmony_ci	bool b_notify = p_ll2_conn->tx_queue.cur_send_packet->notify_fw;
19088c2ecf20Sopenharmony_ci	struct qed_ll2_tx_queue *p_tx = &p_ll2_conn->tx_queue;
19098c2ecf20Sopenharmony_ci	struct qed_ll2_tx_packet *p_pkt = NULL;
19108c2ecf20Sopenharmony_ci	u16 bd_prod;
19118c2ecf20Sopenharmony_ci
19128c2ecf20Sopenharmony_ci	/* If there are missing BDs, don't do anything now */
19138c2ecf20Sopenharmony_ci	if (p_ll2_conn->tx_queue.cur_send_frag_num !=
19148c2ecf20Sopenharmony_ci	    p_ll2_conn->tx_queue.cur_send_packet->bd_used)
19158c2ecf20Sopenharmony_ci		return;
19168c2ecf20Sopenharmony_ci
19178c2ecf20Sopenharmony_ci	/* Push the current packet to the list and clean after it */
19188c2ecf20Sopenharmony_ci	list_add_tail(&p_ll2_conn->tx_queue.cur_send_packet->list_entry,
19198c2ecf20Sopenharmony_ci		      &p_ll2_conn->tx_queue.sending_descq);
19208c2ecf20Sopenharmony_ci	p_ll2_conn->tx_queue.cur_send_packet = NULL;
19218c2ecf20Sopenharmony_ci	p_ll2_conn->tx_queue.cur_send_frag_num = 0;
19228c2ecf20Sopenharmony_ci
19238c2ecf20Sopenharmony_ci	/* Notify FW of packet only if requested to */
19248c2ecf20Sopenharmony_ci	if (!b_notify)
19258c2ecf20Sopenharmony_ci		return;
19268c2ecf20Sopenharmony_ci
19278c2ecf20Sopenharmony_ci	bd_prod = qed_chain_get_prod_idx(&p_ll2_conn->tx_queue.txq_chain);
19288c2ecf20Sopenharmony_ci
19298c2ecf20Sopenharmony_ci	while (!list_empty(&p_tx->sending_descq)) {
19308c2ecf20Sopenharmony_ci		p_pkt = list_first_entry(&p_tx->sending_descq,
19318c2ecf20Sopenharmony_ci					 struct qed_ll2_tx_packet, list_entry);
19328c2ecf20Sopenharmony_ci		if (!p_pkt)
19338c2ecf20Sopenharmony_ci			break;
19348c2ecf20Sopenharmony_ci
19358c2ecf20Sopenharmony_ci		list_move_tail(&p_pkt->list_entry, &p_tx->active_descq);
19368c2ecf20Sopenharmony_ci	}
19378c2ecf20Sopenharmony_ci
19388c2ecf20Sopenharmony_ci	p_tx->db_msg.spq_prod = cpu_to_le16(bd_prod);
19398c2ecf20Sopenharmony_ci
19408c2ecf20Sopenharmony_ci	/* Make sure the BDs data is updated before ringing the doorbell */
19418c2ecf20Sopenharmony_ci	wmb();
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_ci	DIRECT_REG_WR(p_tx->doorbell_addr, *((u32 *)&p_tx->db_msg));
19448c2ecf20Sopenharmony_ci
19458c2ecf20Sopenharmony_ci	DP_VERBOSE(p_hwfn,
19468c2ecf20Sopenharmony_ci		   (NETIF_MSG_TX_QUEUED | QED_MSG_LL2),
19478c2ecf20Sopenharmony_ci		   "LL2 [q 0x%02x cid 0x%08x type 0x%08x] Doorbelled [producer 0x%04x]\n",
19488c2ecf20Sopenharmony_ci		   p_ll2_conn->queue_id,
19498c2ecf20Sopenharmony_ci		   p_ll2_conn->cid,
19508c2ecf20Sopenharmony_ci		   p_ll2_conn->input.conn_type, p_tx->db_msg.spq_prod);
19518c2ecf20Sopenharmony_ci}
19528c2ecf20Sopenharmony_ci
19538c2ecf20Sopenharmony_ciint qed_ll2_prepare_tx_packet(void *cxt,
19548c2ecf20Sopenharmony_ci			      u8 connection_handle,
19558c2ecf20Sopenharmony_ci			      struct qed_ll2_tx_pkt_info *pkt,
19568c2ecf20Sopenharmony_ci			      bool notify_fw)
19578c2ecf20Sopenharmony_ci{
19588c2ecf20Sopenharmony_ci	struct qed_hwfn *p_hwfn = cxt;
19598c2ecf20Sopenharmony_ci	struct qed_ll2_tx_packet *p_curp = NULL;
19608c2ecf20Sopenharmony_ci	struct qed_ll2_info *p_ll2_conn = NULL;
19618c2ecf20Sopenharmony_ci	struct qed_ll2_tx_queue *p_tx;
19628c2ecf20Sopenharmony_ci	struct qed_chain *p_tx_chain;
19638c2ecf20Sopenharmony_ci	unsigned long flags;
19648c2ecf20Sopenharmony_ci	int rc = 0;
19658c2ecf20Sopenharmony_ci
19668c2ecf20Sopenharmony_ci	p_ll2_conn = qed_ll2_handle_sanity(p_hwfn, connection_handle);
19678c2ecf20Sopenharmony_ci	if (!p_ll2_conn)
19688c2ecf20Sopenharmony_ci		return -EINVAL;
19698c2ecf20Sopenharmony_ci	p_tx = &p_ll2_conn->tx_queue;
19708c2ecf20Sopenharmony_ci	p_tx_chain = &p_tx->txq_chain;
19718c2ecf20Sopenharmony_ci
19728c2ecf20Sopenharmony_ci	if (pkt->num_of_bds > p_ll2_conn->input.tx_max_bds_per_packet)
19738c2ecf20Sopenharmony_ci		return -EIO;
19748c2ecf20Sopenharmony_ci
19758c2ecf20Sopenharmony_ci	spin_lock_irqsave(&p_tx->lock, flags);
19768c2ecf20Sopenharmony_ci	if (p_tx->cur_send_packet) {
19778c2ecf20Sopenharmony_ci		rc = -EEXIST;
19788c2ecf20Sopenharmony_ci		goto out;
19798c2ecf20Sopenharmony_ci	}
19808c2ecf20Sopenharmony_ci
19818c2ecf20Sopenharmony_ci	/* Get entry, but only if we have tx elements for it */
19828c2ecf20Sopenharmony_ci	if (!list_empty(&p_tx->free_descq))
19838c2ecf20Sopenharmony_ci		p_curp = list_first_entry(&p_tx->free_descq,
19848c2ecf20Sopenharmony_ci					  struct qed_ll2_tx_packet, list_entry);
19858c2ecf20Sopenharmony_ci	if (p_curp && qed_chain_get_elem_left(p_tx_chain) < pkt->num_of_bds)
19868c2ecf20Sopenharmony_ci		p_curp = NULL;
19878c2ecf20Sopenharmony_ci
19888c2ecf20Sopenharmony_ci	if (!p_curp) {
19898c2ecf20Sopenharmony_ci		rc = -EBUSY;
19908c2ecf20Sopenharmony_ci		goto out;
19918c2ecf20Sopenharmony_ci	}
19928c2ecf20Sopenharmony_ci
19938c2ecf20Sopenharmony_ci	/* Prepare packet and BD, and perhaps send a doorbell to FW */
19948c2ecf20Sopenharmony_ci	qed_ll2_prepare_tx_packet_set(p_hwfn, p_tx, p_curp, pkt, notify_fw);
19958c2ecf20Sopenharmony_ci
19968c2ecf20Sopenharmony_ci	qed_ll2_prepare_tx_packet_set_bd(p_hwfn, p_ll2_conn, p_curp, pkt);
19978c2ecf20Sopenharmony_ci
19988c2ecf20Sopenharmony_ci	qed_ll2_tx_packet_notify(p_hwfn, p_ll2_conn);
19998c2ecf20Sopenharmony_ci
20008c2ecf20Sopenharmony_ciout:
20018c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&p_tx->lock, flags);
20028c2ecf20Sopenharmony_ci	return rc;
20038c2ecf20Sopenharmony_ci}
20048c2ecf20Sopenharmony_ci
20058c2ecf20Sopenharmony_ciint qed_ll2_set_fragment_of_tx_packet(void *cxt,
20068c2ecf20Sopenharmony_ci				      u8 connection_handle,
20078c2ecf20Sopenharmony_ci				      dma_addr_t addr, u16 nbytes)
20088c2ecf20Sopenharmony_ci{
20098c2ecf20Sopenharmony_ci	struct qed_ll2_tx_packet *p_cur_send_packet = NULL;
20108c2ecf20Sopenharmony_ci	struct qed_hwfn *p_hwfn = cxt;
20118c2ecf20Sopenharmony_ci	struct qed_ll2_info *p_ll2_conn = NULL;
20128c2ecf20Sopenharmony_ci	u16 cur_send_frag_num = 0;
20138c2ecf20Sopenharmony_ci	struct core_tx_bd *p_bd;
20148c2ecf20Sopenharmony_ci	unsigned long flags;
20158c2ecf20Sopenharmony_ci
20168c2ecf20Sopenharmony_ci	p_ll2_conn = qed_ll2_handle_sanity(p_hwfn, connection_handle);
20178c2ecf20Sopenharmony_ci	if (!p_ll2_conn)
20188c2ecf20Sopenharmony_ci		return -EINVAL;
20198c2ecf20Sopenharmony_ci
20208c2ecf20Sopenharmony_ci	if (!p_ll2_conn->tx_queue.cur_send_packet)
20218c2ecf20Sopenharmony_ci		return -EINVAL;
20228c2ecf20Sopenharmony_ci
20238c2ecf20Sopenharmony_ci	p_cur_send_packet = p_ll2_conn->tx_queue.cur_send_packet;
20248c2ecf20Sopenharmony_ci	cur_send_frag_num = p_ll2_conn->tx_queue.cur_send_frag_num;
20258c2ecf20Sopenharmony_ci
20268c2ecf20Sopenharmony_ci	if (cur_send_frag_num >= p_cur_send_packet->bd_used)
20278c2ecf20Sopenharmony_ci		return -EINVAL;
20288c2ecf20Sopenharmony_ci
20298c2ecf20Sopenharmony_ci	/* Fill the BD information, and possibly notify FW */
20308c2ecf20Sopenharmony_ci	p_bd = p_cur_send_packet->bds_set[cur_send_frag_num].txq_bd;
20318c2ecf20Sopenharmony_ci	DMA_REGPAIR_LE(p_bd->addr, addr);
20328c2ecf20Sopenharmony_ci	p_bd->nbytes = cpu_to_le16(nbytes);
20338c2ecf20Sopenharmony_ci	p_cur_send_packet->bds_set[cur_send_frag_num].tx_frag = addr;
20348c2ecf20Sopenharmony_ci	p_cur_send_packet->bds_set[cur_send_frag_num].frag_len = nbytes;
20358c2ecf20Sopenharmony_ci
20368c2ecf20Sopenharmony_ci	p_ll2_conn->tx_queue.cur_send_frag_num++;
20378c2ecf20Sopenharmony_ci
20388c2ecf20Sopenharmony_ci	spin_lock_irqsave(&p_ll2_conn->tx_queue.lock, flags);
20398c2ecf20Sopenharmony_ci	qed_ll2_tx_packet_notify(p_hwfn, p_ll2_conn);
20408c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&p_ll2_conn->tx_queue.lock, flags);
20418c2ecf20Sopenharmony_ci
20428c2ecf20Sopenharmony_ci	return 0;
20438c2ecf20Sopenharmony_ci}
20448c2ecf20Sopenharmony_ci
20458c2ecf20Sopenharmony_ciint qed_ll2_terminate_connection(void *cxt, u8 connection_handle)
20468c2ecf20Sopenharmony_ci{
20478c2ecf20Sopenharmony_ci	struct qed_hwfn *p_hwfn = cxt;
20488c2ecf20Sopenharmony_ci	struct qed_ll2_info *p_ll2_conn = NULL;
20498c2ecf20Sopenharmony_ci	int rc = -EINVAL;
20508c2ecf20Sopenharmony_ci	struct qed_ptt *p_ptt;
20518c2ecf20Sopenharmony_ci
20528c2ecf20Sopenharmony_ci	p_ptt = qed_ptt_acquire(p_hwfn);
20538c2ecf20Sopenharmony_ci	if (!p_ptt)
20548c2ecf20Sopenharmony_ci		return -EAGAIN;
20558c2ecf20Sopenharmony_ci
20568c2ecf20Sopenharmony_ci	p_ll2_conn = qed_ll2_handle_sanity_lock(p_hwfn, connection_handle);
20578c2ecf20Sopenharmony_ci	if (!p_ll2_conn) {
20588c2ecf20Sopenharmony_ci		rc = -EINVAL;
20598c2ecf20Sopenharmony_ci		goto out;
20608c2ecf20Sopenharmony_ci	}
20618c2ecf20Sopenharmony_ci
20628c2ecf20Sopenharmony_ci	/* Stop Tx & Rx of connection, if needed */
20638c2ecf20Sopenharmony_ci	if (QED_LL2_TX_REGISTERED(p_ll2_conn)) {
20648c2ecf20Sopenharmony_ci		p_ll2_conn->tx_queue.b_cb_registered = false;
20658c2ecf20Sopenharmony_ci		smp_wmb(); /* Make sure this is seen by ll2_lb_rxq_completion */
20668c2ecf20Sopenharmony_ci		rc = qed_sp_ll2_tx_queue_stop(p_hwfn, p_ll2_conn);
20678c2ecf20Sopenharmony_ci		if (rc)
20688c2ecf20Sopenharmony_ci			goto out;
20698c2ecf20Sopenharmony_ci
20708c2ecf20Sopenharmony_ci		qed_ll2_txq_flush(p_hwfn, connection_handle);
20718c2ecf20Sopenharmony_ci		qed_int_unregister_cb(p_hwfn, p_ll2_conn->tx_queue.tx_sb_index);
20728c2ecf20Sopenharmony_ci	}
20738c2ecf20Sopenharmony_ci
20748c2ecf20Sopenharmony_ci	if (QED_LL2_RX_REGISTERED(p_ll2_conn)) {
20758c2ecf20Sopenharmony_ci		p_ll2_conn->rx_queue.b_cb_registered = false;
20768c2ecf20Sopenharmony_ci		smp_wmb(); /* Make sure this is seen by ll2_lb_rxq_completion */
20778c2ecf20Sopenharmony_ci
20788c2ecf20Sopenharmony_ci		if (p_ll2_conn->rx_queue.ctx_based)
20798c2ecf20Sopenharmony_ci			qed_db_recovery_del(p_hwfn->cdev,
20808c2ecf20Sopenharmony_ci					    p_ll2_conn->rx_queue.set_prod_addr,
20818c2ecf20Sopenharmony_ci					    &p_ll2_conn->rx_queue.db_data);
20828c2ecf20Sopenharmony_ci
20838c2ecf20Sopenharmony_ci		rc = qed_sp_ll2_rx_queue_stop(p_hwfn, p_ll2_conn);
20848c2ecf20Sopenharmony_ci		if (rc)
20858c2ecf20Sopenharmony_ci			goto out;
20868c2ecf20Sopenharmony_ci
20878c2ecf20Sopenharmony_ci		qed_ll2_rxq_flush(p_hwfn, connection_handle);
20888c2ecf20Sopenharmony_ci		qed_int_unregister_cb(p_hwfn, p_ll2_conn->rx_queue.rx_sb_index);
20898c2ecf20Sopenharmony_ci	}
20908c2ecf20Sopenharmony_ci
20918c2ecf20Sopenharmony_ci	if (p_ll2_conn->input.conn_type == QED_LL2_TYPE_OOO)
20928c2ecf20Sopenharmony_ci		qed_ooo_release_all_isles(p_hwfn, p_hwfn->p_ooo_info);
20938c2ecf20Sopenharmony_ci
20948c2ecf20Sopenharmony_ci	if (p_ll2_conn->input.conn_type == QED_LL2_TYPE_FCOE) {
20958c2ecf20Sopenharmony_ci		if (!test_bit(QED_MF_UFP_SPECIFIC, &p_hwfn->cdev->mf_bits))
20968c2ecf20Sopenharmony_ci			qed_llh_remove_protocol_filter(p_hwfn->cdev, 0,
20978c2ecf20Sopenharmony_ci						       QED_LLH_FILTER_ETHERTYPE,
20988c2ecf20Sopenharmony_ci						       ETH_P_FCOE, 0);
20998c2ecf20Sopenharmony_ci		qed_llh_remove_protocol_filter(p_hwfn->cdev, 0,
21008c2ecf20Sopenharmony_ci					       QED_LLH_FILTER_ETHERTYPE,
21018c2ecf20Sopenharmony_ci					       ETH_P_FIP, 0);
21028c2ecf20Sopenharmony_ci	}
21038c2ecf20Sopenharmony_ci
21048c2ecf20Sopenharmony_ciout:
21058c2ecf20Sopenharmony_ci	qed_ptt_release(p_hwfn, p_ptt);
21068c2ecf20Sopenharmony_ci	return rc;
21078c2ecf20Sopenharmony_ci}
21088c2ecf20Sopenharmony_ci
21098c2ecf20Sopenharmony_cistatic void qed_ll2_release_connection_ooo(struct qed_hwfn *p_hwfn,
21108c2ecf20Sopenharmony_ci					   struct qed_ll2_info *p_ll2_conn)
21118c2ecf20Sopenharmony_ci{
21128c2ecf20Sopenharmony_ci	struct qed_ooo_buffer *p_buffer;
21138c2ecf20Sopenharmony_ci
21148c2ecf20Sopenharmony_ci	if (p_ll2_conn->input.conn_type != QED_LL2_TYPE_OOO)
21158c2ecf20Sopenharmony_ci		return;
21168c2ecf20Sopenharmony_ci
21178c2ecf20Sopenharmony_ci	qed_ooo_release_all_isles(p_hwfn, p_hwfn->p_ooo_info);
21188c2ecf20Sopenharmony_ci	while ((p_buffer = qed_ooo_get_free_buffer(p_hwfn,
21198c2ecf20Sopenharmony_ci						   p_hwfn->p_ooo_info))) {
21208c2ecf20Sopenharmony_ci		dma_free_coherent(&p_hwfn->cdev->pdev->dev,
21218c2ecf20Sopenharmony_ci				  p_buffer->rx_buffer_size,
21228c2ecf20Sopenharmony_ci				  p_buffer->rx_buffer_virt_addr,
21238c2ecf20Sopenharmony_ci				  p_buffer->rx_buffer_phys_addr);
21248c2ecf20Sopenharmony_ci		kfree(p_buffer);
21258c2ecf20Sopenharmony_ci	}
21268c2ecf20Sopenharmony_ci}
21278c2ecf20Sopenharmony_ci
21288c2ecf20Sopenharmony_civoid qed_ll2_release_connection(void *cxt, u8 connection_handle)
21298c2ecf20Sopenharmony_ci{
21308c2ecf20Sopenharmony_ci	struct qed_hwfn *p_hwfn = cxt;
21318c2ecf20Sopenharmony_ci	struct qed_ll2_info *p_ll2_conn = NULL;
21328c2ecf20Sopenharmony_ci
21338c2ecf20Sopenharmony_ci	p_ll2_conn = qed_ll2_handle_sanity(p_hwfn, connection_handle);
21348c2ecf20Sopenharmony_ci	if (!p_ll2_conn)
21358c2ecf20Sopenharmony_ci		return;
21368c2ecf20Sopenharmony_ci
21378c2ecf20Sopenharmony_ci	kfree(p_ll2_conn->tx_queue.descq_mem);
21388c2ecf20Sopenharmony_ci	qed_chain_free(p_hwfn->cdev, &p_ll2_conn->tx_queue.txq_chain);
21398c2ecf20Sopenharmony_ci
21408c2ecf20Sopenharmony_ci	kfree(p_ll2_conn->rx_queue.descq_array);
21418c2ecf20Sopenharmony_ci	qed_chain_free(p_hwfn->cdev, &p_ll2_conn->rx_queue.rxq_chain);
21428c2ecf20Sopenharmony_ci	qed_chain_free(p_hwfn->cdev, &p_ll2_conn->rx_queue.rcq_chain);
21438c2ecf20Sopenharmony_ci
21448c2ecf20Sopenharmony_ci	qed_cxt_release_cid(p_hwfn, p_ll2_conn->cid);
21458c2ecf20Sopenharmony_ci
21468c2ecf20Sopenharmony_ci	qed_ll2_release_connection_ooo(p_hwfn, p_ll2_conn);
21478c2ecf20Sopenharmony_ci
21488c2ecf20Sopenharmony_ci	mutex_lock(&p_ll2_conn->mutex);
21498c2ecf20Sopenharmony_ci	p_ll2_conn->b_active = false;
21508c2ecf20Sopenharmony_ci	mutex_unlock(&p_ll2_conn->mutex);
21518c2ecf20Sopenharmony_ci}
21528c2ecf20Sopenharmony_ci
21538c2ecf20Sopenharmony_ciint qed_ll2_alloc(struct qed_hwfn *p_hwfn)
21548c2ecf20Sopenharmony_ci{
21558c2ecf20Sopenharmony_ci	struct qed_ll2_info *p_ll2_connections;
21568c2ecf20Sopenharmony_ci	u8 i;
21578c2ecf20Sopenharmony_ci
21588c2ecf20Sopenharmony_ci	/* Allocate LL2's set struct */
21598c2ecf20Sopenharmony_ci	p_ll2_connections = kcalloc(QED_MAX_NUM_OF_LL2_CONNECTIONS,
21608c2ecf20Sopenharmony_ci				    sizeof(struct qed_ll2_info), GFP_KERNEL);
21618c2ecf20Sopenharmony_ci	if (!p_ll2_connections) {
21628c2ecf20Sopenharmony_ci		DP_NOTICE(p_hwfn, "Failed to allocate `struct qed_ll2'\n");
21638c2ecf20Sopenharmony_ci		return -ENOMEM;
21648c2ecf20Sopenharmony_ci	}
21658c2ecf20Sopenharmony_ci
21668c2ecf20Sopenharmony_ci	for (i = 0; i < QED_MAX_NUM_OF_LL2_CONNECTIONS; i++)
21678c2ecf20Sopenharmony_ci		p_ll2_connections[i].my_id = i;
21688c2ecf20Sopenharmony_ci
21698c2ecf20Sopenharmony_ci	p_hwfn->p_ll2_info = p_ll2_connections;
21708c2ecf20Sopenharmony_ci	return 0;
21718c2ecf20Sopenharmony_ci}
21728c2ecf20Sopenharmony_ci
21738c2ecf20Sopenharmony_civoid qed_ll2_setup(struct qed_hwfn *p_hwfn)
21748c2ecf20Sopenharmony_ci{
21758c2ecf20Sopenharmony_ci	int i;
21768c2ecf20Sopenharmony_ci
21778c2ecf20Sopenharmony_ci	for (i = 0; i < QED_MAX_NUM_OF_LL2_CONNECTIONS; i++)
21788c2ecf20Sopenharmony_ci		mutex_init(&p_hwfn->p_ll2_info[i].mutex);
21798c2ecf20Sopenharmony_ci}
21808c2ecf20Sopenharmony_ci
21818c2ecf20Sopenharmony_civoid qed_ll2_free(struct qed_hwfn *p_hwfn)
21828c2ecf20Sopenharmony_ci{
21838c2ecf20Sopenharmony_ci	if (!p_hwfn->p_ll2_info)
21848c2ecf20Sopenharmony_ci		return;
21858c2ecf20Sopenharmony_ci
21868c2ecf20Sopenharmony_ci	kfree(p_hwfn->p_ll2_info);
21878c2ecf20Sopenharmony_ci	p_hwfn->p_ll2_info = NULL;
21888c2ecf20Sopenharmony_ci}
21898c2ecf20Sopenharmony_ci
21908c2ecf20Sopenharmony_cistatic void _qed_ll2_get_port_stats(struct qed_hwfn *p_hwfn,
21918c2ecf20Sopenharmony_ci				    struct qed_ptt *p_ptt,
21928c2ecf20Sopenharmony_ci				    struct qed_ll2_stats *p_stats)
21938c2ecf20Sopenharmony_ci{
21948c2ecf20Sopenharmony_ci	struct core_ll2_port_stats port_stats;
21958c2ecf20Sopenharmony_ci
21968c2ecf20Sopenharmony_ci	memset(&port_stats, 0, sizeof(port_stats));
21978c2ecf20Sopenharmony_ci	qed_memcpy_from(p_hwfn, p_ptt, &port_stats,
21988c2ecf20Sopenharmony_ci			BAR0_MAP_REG_TSDM_RAM +
21998c2ecf20Sopenharmony_ci			TSTORM_LL2_PORT_STAT_OFFSET(MFW_PORT(p_hwfn)),
22008c2ecf20Sopenharmony_ci			sizeof(port_stats));
22018c2ecf20Sopenharmony_ci
22028c2ecf20Sopenharmony_ci	p_stats->gsi_invalid_hdr += HILO_64_REGPAIR(port_stats.gsi_invalid_hdr);
22038c2ecf20Sopenharmony_ci	p_stats->gsi_invalid_pkt_length +=
22048c2ecf20Sopenharmony_ci	    HILO_64_REGPAIR(port_stats.gsi_invalid_pkt_length);
22058c2ecf20Sopenharmony_ci	p_stats->gsi_unsupported_pkt_typ +=
22068c2ecf20Sopenharmony_ci	    HILO_64_REGPAIR(port_stats.gsi_unsupported_pkt_typ);
22078c2ecf20Sopenharmony_ci	p_stats->gsi_crcchksm_error +=
22088c2ecf20Sopenharmony_ci	    HILO_64_REGPAIR(port_stats.gsi_crcchksm_error);
22098c2ecf20Sopenharmony_ci}
22108c2ecf20Sopenharmony_ci
22118c2ecf20Sopenharmony_cistatic void _qed_ll2_get_tstats(struct qed_hwfn *p_hwfn,
22128c2ecf20Sopenharmony_ci				struct qed_ptt *p_ptt,
22138c2ecf20Sopenharmony_ci				struct qed_ll2_info *p_ll2_conn,
22148c2ecf20Sopenharmony_ci				struct qed_ll2_stats *p_stats)
22158c2ecf20Sopenharmony_ci{
22168c2ecf20Sopenharmony_ci	struct core_ll2_tstorm_per_queue_stat tstats;
22178c2ecf20Sopenharmony_ci	u8 qid = p_ll2_conn->queue_id;
22188c2ecf20Sopenharmony_ci	u32 tstats_addr;
22198c2ecf20Sopenharmony_ci
22208c2ecf20Sopenharmony_ci	memset(&tstats, 0, sizeof(tstats));
22218c2ecf20Sopenharmony_ci	tstats_addr = BAR0_MAP_REG_TSDM_RAM +
22228c2ecf20Sopenharmony_ci		      CORE_LL2_TSTORM_PER_QUEUE_STAT_OFFSET(qid);
22238c2ecf20Sopenharmony_ci	qed_memcpy_from(p_hwfn, p_ptt, &tstats, tstats_addr, sizeof(tstats));
22248c2ecf20Sopenharmony_ci
22258c2ecf20Sopenharmony_ci	p_stats->packet_too_big_discard +=
22268c2ecf20Sopenharmony_ci			HILO_64_REGPAIR(tstats.packet_too_big_discard);
22278c2ecf20Sopenharmony_ci	p_stats->no_buff_discard += HILO_64_REGPAIR(tstats.no_buff_discard);
22288c2ecf20Sopenharmony_ci}
22298c2ecf20Sopenharmony_ci
22308c2ecf20Sopenharmony_cistatic void _qed_ll2_get_ustats(struct qed_hwfn *p_hwfn,
22318c2ecf20Sopenharmony_ci				struct qed_ptt *p_ptt,
22328c2ecf20Sopenharmony_ci				struct qed_ll2_info *p_ll2_conn,
22338c2ecf20Sopenharmony_ci				struct qed_ll2_stats *p_stats)
22348c2ecf20Sopenharmony_ci{
22358c2ecf20Sopenharmony_ci	struct core_ll2_ustorm_per_queue_stat ustats;
22368c2ecf20Sopenharmony_ci	u8 qid = p_ll2_conn->queue_id;
22378c2ecf20Sopenharmony_ci	u32 ustats_addr;
22388c2ecf20Sopenharmony_ci
22398c2ecf20Sopenharmony_ci	memset(&ustats, 0, sizeof(ustats));
22408c2ecf20Sopenharmony_ci	ustats_addr = BAR0_MAP_REG_USDM_RAM +
22418c2ecf20Sopenharmony_ci		      CORE_LL2_USTORM_PER_QUEUE_STAT_OFFSET(qid);
22428c2ecf20Sopenharmony_ci	qed_memcpy_from(p_hwfn, p_ptt, &ustats, ustats_addr, sizeof(ustats));
22438c2ecf20Sopenharmony_ci
22448c2ecf20Sopenharmony_ci	p_stats->rcv_ucast_bytes += HILO_64_REGPAIR(ustats.rcv_ucast_bytes);
22458c2ecf20Sopenharmony_ci	p_stats->rcv_mcast_bytes += HILO_64_REGPAIR(ustats.rcv_mcast_bytes);
22468c2ecf20Sopenharmony_ci	p_stats->rcv_bcast_bytes += HILO_64_REGPAIR(ustats.rcv_bcast_bytes);
22478c2ecf20Sopenharmony_ci	p_stats->rcv_ucast_pkts += HILO_64_REGPAIR(ustats.rcv_ucast_pkts);
22488c2ecf20Sopenharmony_ci	p_stats->rcv_mcast_pkts += HILO_64_REGPAIR(ustats.rcv_mcast_pkts);
22498c2ecf20Sopenharmony_ci	p_stats->rcv_bcast_pkts += HILO_64_REGPAIR(ustats.rcv_bcast_pkts);
22508c2ecf20Sopenharmony_ci}
22518c2ecf20Sopenharmony_ci
22528c2ecf20Sopenharmony_cistatic void _qed_ll2_get_pstats(struct qed_hwfn *p_hwfn,
22538c2ecf20Sopenharmony_ci				struct qed_ptt *p_ptt,
22548c2ecf20Sopenharmony_ci				struct qed_ll2_info *p_ll2_conn,
22558c2ecf20Sopenharmony_ci				struct qed_ll2_stats *p_stats)
22568c2ecf20Sopenharmony_ci{
22578c2ecf20Sopenharmony_ci	struct core_ll2_pstorm_per_queue_stat pstats;
22588c2ecf20Sopenharmony_ci	u8 stats_id = p_ll2_conn->tx_stats_id;
22598c2ecf20Sopenharmony_ci	u32 pstats_addr;
22608c2ecf20Sopenharmony_ci
22618c2ecf20Sopenharmony_ci	memset(&pstats, 0, sizeof(pstats));
22628c2ecf20Sopenharmony_ci	pstats_addr = BAR0_MAP_REG_PSDM_RAM +
22638c2ecf20Sopenharmony_ci		      CORE_LL2_PSTORM_PER_QUEUE_STAT_OFFSET(stats_id);
22648c2ecf20Sopenharmony_ci	qed_memcpy_from(p_hwfn, p_ptt, &pstats, pstats_addr, sizeof(pstats));
22658c2ecf20Sopenharmony_ci
22668c2ecf20Sopenharmony_ci	p_stats->sent_ucast_bytes += HILO_64_REGPAIR(pstats.sent_ucast_bytes);
22678c2ecf20Sopenharmony_ci	p_stats->sent_mcast_bytes += HILO_64_REGPAIR(pstats.sent_mcast_bytes);
22688c2ecf20Sopenharmony_ci	p_stats->sent_bcast_bytes += HILO_64_REGPAIR(pstats.sent_bcast_bytes);
22698c2ecf20Sopenharmony_ci	p_stats->sent_ucast_pkts += HILO_64_REGPAIR(pstats.sent_ucast_pkts);
22708c2ecf20Sopenharmony_ci	p_stats->sent_mcast_pkts += HILO_64_REGPAIR(pstats.sent_mcast_pkts);
22718c2ecf20Sopenharmony_ci	p_stats->sent_bcast_pkts += HILO_64_REGPAIR(pstats.sent_bcast_pkts);
22728c2ecf20Sopenharmony_ci}
22738c2ecf20Sopenharmony_ci
22748c2ecf20Sopenharmony_cistatic int __qed_ll2_get_stats(void *cxt, u8 connection_handle,
22758c2ecf20Sopenharmony_ci			       struct qed_ll2_stats *p_stats)
22768c2ecf20Sopenharmony_ci{
22778c2ecf20Sopenharmony_ci	struct qed_hwfn *p_hwfn = cxt;
22788c2ecf20Sopenharmony_ci	struct qed_ll2_info *p_ll2_conn = NULL;
22798c2ecf20Sopenharmony_ci	struct qed_ptt *p_ptt;
22808c2ecf20Sopenharmony_ci
22818c2ecf20Sopenharmony_ci	if ((connection_handle >= QED_MAX_NUM_OF_LL2_CONNECTIONS) ||
22828c2ecf20Sopenharmony_ci	    !p_hwfn->p_ll2_info)
22838c2ecf20Sopenharmony_ci		return -EINVAL;
22848c2ecf20Sopenharmony_ci
22858c2ecf20Sopenharmony_ci	p_ll2_conn = &p_hwfn->p_ll2_info[connection_handle];
22868c2ecf20Sopenharmony_ci
22878c2ecf20Sopenharmony_ci	p_ptt = qed_ptt_acquire(p_hwfn);
22888c2ecf20Sopenharmony_ci	if (!p_ptt) {
22898c2ecf20Sopenharmony_ci		DP_ERR(p_hwfn, "Failed to acquire ptt\n");
22908c2ecf20Sopenharmony_ci		return -EINVAL;
22918c2ecf20Sopenharmony_ci	}
22928c2ecf20Sopenharmony_ci
22938c2ecf20Sopenharmony_ci	if (p_ll2_conn->input.gsi_enable)
22948c2ecf20Sopenharmony_ci		_qed_ll2_get_port_stats(p_hwfn, p_ptt, p_stats);
22958c2ecf20Sopenharmony_ci
22968c2ecf20Sopenharmony_ci	_qed_ll2_get_tstats(p_hwfn, p_ptt, p_ll2_conn, p_stats);
22978c2ecf20Sopenharmony_ci
22988c2ecf20Sopenharmony_ci	_qed_ll2_get_ustats(p_hwfn, p_ptt, p_ll2_conn, p_stats);
22998c2ecf20Sopenharmony_ci
23008c2ecf20Sopenharmony_ci	if (p_ll2_conn->tx_stats_en)
23018c2ecf20Sopenharmony_ci		_qed_ll2_get_pstats(p_hwfn, p_ptt, p_ll2_conn, p_stats);
23028c2ecf20Sopenharmony_ci
23038c2ecf20Sopenharmony_ci	qed_ptt_release(p_hwfn, p_ptt);
23048c2ecf20Sopenharmony_ci
23058c2ecf20Sopenharmony_ci	return 0;
23068c2ecf20Sopenharmony_ci}
23078c2ecf20Sopenharmony_ci
23088c2ecf20Sopenharmony_ciint qed_ll2_get_stats(void *cxt,
23098c2ecf20Sopenharmony_ci		      u8 connection_handle, struct qed_ll2_stats *p_stats)
23108c2ecf20Sopenharmony_ci{
23118c2ecf20Sopenharmony_ci	memset(p_stats, 0, sizeof(*p_stats));
23128c2ecf20Sopenharmony_ci	return __qed_ll2_get_stats(cxt, connection_handle, p_stats);
23138c2ecf20Sopenharmony_ci}
23148c2ecf20Sopenharmony_ci
23158c2ecf20Sopenharmony_cistatic void qed_ll2b_release_rx_packet(void *cxt,
23168c2ecf20Sopenharmony_ci				       u8 connection_handle,
23178c2ecf20Sopenharmony_ci				       void *cookie,
23188c2ecf20Sopenharmony_ci				       dma_addr_t rx_buf_addr,
23198c2ecf20Sopenharmony_ci				       bool b_last_packet)
23208c2ecf20Sopenharmony_ci{
23218c2ecf20Sopenharmony_ci	struct qed_hwfn *p_hwfn = cxt;
23228c2ecf20Sopenharmony_ci
23238c2ecf20Sopenharmony_ci	qed_ll2_dealloc_buffer(p_hwfn->cdev, cookie);
23248c2ecf20Sopenharmony_ci}
23258c2ecf20Sopenharmony_ci
23268c2ecf20Sopenharmony_cistatic void qed_ll2_register_cb_ops(struct qed_dev *cdev,
23278c2ecf20Sopenharmony_ci				    const struct qed_ll2_cb_ops *ops,
23288c2ecf20Sopenharmony_ci				    void *cookie)
23298c2ecf20Sopenharmony_ci{
23308c2ecf20Sopenharmony_ci	cdev->ll2->cbs = ops;
23318c2ecf20Sopenharmony_ci	cdev->ll2->cb_cookie = cookie;
23328c2ecf20Sopenharmony_ci}
23338c2ecf20Sopenharmony_ci
23348c2ecf20Sopenharmony_cistatic struct qed_ll2_cbs ll2_cbs = {
23358c2ecf20Sopenharmony_ci	.rx_comp_cb = &qed_ll2b_complete_rx_packet,
23368c2ecf20Sopenharmony_ci	.rx_release_cb = &qed_ll2b_release_rx_packet,
23378c2ecf20Sopenharmony_ci	.tx_comp_cb = &qed_ll2b_complete_tx_packet,
23388c2ecf20Sopenharmony_ci	.tx_release_cb = &qed_ll2b_complete_tx_packet,
23398c2ecf20Sopenharmony_ci};
23408c2ecf20Sopenharmony_ci
23418c2ecf20Sopenharmony_cistatic void qed_ll2_set_conn_data(struct qed_hwfn *p_hwfn,
23428c2ecf20Sopenharmony_ci				  struct qed_ll2_acquire_data *data,
23438c2ecf20Sopenharmony_ci				  struct qed_ll2_params *params,
23448c2ecf20Sopenharmony_ci				  enum qed_ll2_conn_type conn_type,
23458c2ecf20Sopenharmony_ci				  u8 *handle, bool lb)
23468c2ecf20Sopenharmony_ci{
23478c2ecf20Sopenharmony_ci	memset(data, 0, sizeof(*data));
23488c2ecf20Sopenharmony_ci
23498c2ecf20Sopenharmony_ci	data->input.conn_type = conn_type;
23508c2ecf20Sopenharmony_ci	data->input.mtu = params->mtu;
23518c2ecf20Sopenharmony_ci	data->input.rx_num_desc = QED_LL2_RX_SIZE;
23528c2ecf20Sopenharmony_ci	data->input.rx_drop_ttl0_flg = params->drop_ttl0_packets;
23538c2ecf20Sopenharmony_ci	data->input.rx_vlan_removal_en = params->rx_vlan_stripping;
23548c2ecf20Sopenharmony_ci	data->input.tx_num_desc = QED_LL2_TX_SIZE;
23558c2ecf20Sopenharmony_ci	data->p_connection_handle = handle;
23568c2ecf20Sopenharmony_ci	data->cbs = &ll2_cbs;
23578c2ecf20Sopenharmony_ci	ll2_cbs.cookie = p_hwfn;
23588c2ecf20Sopenharmony_ci
23598c2ecf20Sopenharmony_ci	if (lb) {
23608c2ecf20Sopenharmony_ci		data->input.tx_tc = PKT_LB_TC;
23618c2ecf20Sopenharmony_ci		data->input.tx_dest = QED_LL2_TX_DEST_LB;
23628c2ecf20Sopenharmony_ci	} else {
23638c2ecf20Sopenharmony_ci		data->input.tx_tc = 0;
23648c2ecf20Sopenharmony_ci		data->input.tx_dest = QED_LL2_TX_DEST_NW;
23658c2ecf20Sopenharmony_ci	}
23668c2ecf20Sopenharmony_ci}
23678c2ecf20Sopenharmony_ci
23688c2ecf20Sopenharmony_cistatic int qed_ll2_start_ooo(struct qed_hwfn *p_hwfn,
23698c2ecf20Sopenharmony_ci			     struct qed_ll2_params *params)
23708c2ecf20Sopenharmony_ci{
23718c2ecf20Sopenharmony_ci	u8 *handle = &p_hwfn->pf_params.iscsi_pf_params.ll2_ooo_queue_id;
23728c2ecf20Sopenharmony_ci	struct qed_ll2_acquire_data data;
23738c2ecf20Sopenharmony_ci	int rc;
23748c2ecf20Sopenharmony_ci
23758c2ecf20Sopenharmony_ci	qed_ll2_set_conn_data(p_hwfn, &data, params,
23768c2ecf20Sopenharmony_ci			      QED_LL2_TYPE_OOO, handle, true);
23778c2ecf20Sopenharmony_ci
23788c2ecf20Sopenharmony_ci	rc = qed_ll2_acquire_connection(p_hwfn, &data);
23798c2ecf20Sopenharmony_ci	if (rc) {
23808c2ecf20Sopenharmony_ci		DP_INFO(p_hwfn, "Failed to acquire LL2 OOO connection\n");
23818c2ecf20Sopenharmony_ci		goto out;
23828c2ecf20Sopenharmony_ci	}
23838c2ecf20Sopenharmony_ci
23848c2ecf20Sopenharmony_ci	rc = qed_ll2_establish_connection(p_hwfn, *handle);
23858c2ecf20Sopenharmony_ci	if (rc) {
23868c2ecf20Sopenharmony_ci		DP_INFO(p_hwfn, "Failed to establish LL2 OOO connection\n");
23878c2ecf20Sopenharmony_ci		goto fail;
23888c2ecf20Sopenharmony_ci	}
23898c2ecf20Sopenharmony_ci
23908c2ecf20Sopenharmony_ci	return 0;
23918c2ecf20Sopenharmony_ci
23928c2ecf20Sopenharmony_cifail:
23938c2ecf20Sopenharmony_ci	qed_ll2_release_connection(p_hwfn, *handle);
23948c2ecf20Sopenharmony_ciout:
23958c2ecf20Sopenharmony_ci	*handle = QED_LL2_UNUSED_HANDLE;
23968c2ecf20Sopenharmony_ci	return rc;
23978c2ecf20Sopenharmony_ci}
23988c2ecf20Sopenharmony_ci
23998c2ecf20Sopenharmony_cistatic bool qed_ll2_is_storage_eng1(struct qed_dev *cdev)
24008c2ecf20Sopenharmony_ci{
24018c2ecf20Sopenharmony_ci	return (QED_IS_FCOE_PERSONALITY(QED_LEADING_HWFN(cdev)) ||
24028c2ecf20Sopenharmony_ci		QED_IS_ISCSI_PERSONALITY(QED_LEADING_HWFN(cdev))) &&
24038c2ecf20Sopenharmony_ci		(QED_AFFIN_HWFN(cdev) != QED_LEADING_HWFN(cdev));
24048c2ecf20Sopenharmony_ci}
24058c2ecf20Sopenharmony_ci
24068c2ecf20Sopenharmony_cistatic int __qed_ll2_stop(struct qed_hwfn *p_hwfn)
24078c2ecf20Sopenharmony_ci{
24088c2ecf20Sopenharmony_ci	struct qed_dev *cdev = p_hwfn->cdev;
24098c2ecf20Sopenharmony_ci	int rc;
24108c2ecf20Sopenharmony_ci
24118c2ecf20Sopenharmony_ci	rc = qed_ll2_terminate_connection(p_hwfn, cdev->ll2->handle);
24128c2ecf20Sopenharmony_ci	if (rc)
24138c2ecf20Sopenharmony_ci		DP_INFO(cdev, "Failed to terminate LL2 connection\n");
24148c2ecf20Sopenharmony_ci
24158c2ecf20Sopenharmony_ci	qed_ll2_release_connection(p_hwfn, cdev->ll2->handle);
24168c2ecf20Sopenharmony_ci
24178c2ecf20Sopenharmony_ci	return rc;
24188c2ecf20Sopenharmony_ci}
24198c2ecf20Sopenharmony_ci
24208c2ecf20Sopenharmony_cistatic int qed_ll2_stop(struct qed_dev *cdev)
24218c2ecf20Sopenharmony_ci{
24228c2ecf20Sopenharmony_ci	bool b_is_storage_eng1 = qed_ll2_is_storage_eng1(cdev);
24238c2ecf20Sopenharmony_ci	struct qed_hwfn *p_hwfn = QED_AFFIN_HWFN(cdev);
24248c2ecf20Sopenharmony_ci	int rc = 0, rc2 = 0;
24258c2ecf20Sopenharmony_ci
24268c2ecf20Sopenharmony_ci	if (cdev->ll2->handle == QED_LL2_UNUSED_HANDLE)
24278c2ecf20Sopenharmony_ci		return 0;
24288c2ecf20Sopenharmony_ci
24298c2ecf20Sopenharmony_ci	qed_llh_remove_mac_filter(cdev, 0, cdev->ll2_mac_address);
24308c2ecf20Sopenharmony_ci	eth_zero_addr(cdev->ll2_mac_address);
24318c2ecf20Sopenharmony_ci
24328c2ecf20Sopenharmony_ci	if (QED_IS_ISCSI_PERSONALITY(p_hwfn))
24338c2ecf20Sopenharmony_ci		qed_ll2_stop_ooo(p_hwfn);
24348c2ecf20Sopenharmony_ci
24358c2ecf20Sopenharmony_ci	/* In CMT mode, LL2 is always started on engine 0 for a storage PF */
24368c2ecf20Sopenharmony_ci	if (b_is_storage_eng1) {
24378c2ecf20Sopenharmony_ci		rc2 = __qed_ll2_stop(QED_LEADING_HWFN(cdev));
24388c2ecf20Sopenharmony_ci		if (rc2)
24398c2ecf20Sopenharmony_ci			DP_NOTICE(QED_LEADING_HWFN(cdev),
24408c2ecf20Sopenharmony_ci				  "Failed to stop LL2 on engine 0\n");
24418c2ecf20Sopenharmony_ci	}
24428c2ecf20Sopenharmony_ci
24438c2ecf20Sopenharmony_ci	rc = __qed_ll2_stop(p_hwfn);
24448c2ecf20Sopenharmony_ci	if (rc)
24458c2ecf20Sopenharmony_ci		DP_NOTICE(p_hwfn, "Failed to stop LL2\n");
24468c2ecf20Sopenharmony_ci
24478c2ecf20Sopenharmony_ci	qed_ll2_kill_buffers(cdev);
24488c2ecf20Sopenharmony_ci
24498c2ecf20Sopenharmony_ci	cdev->ll2->handle = QED_LL2_UNUSED_HANDLE;
24508c2ecf20Sopenharmony_ci
24518c2ecf20Sopenharmony_ci	return rc | rc2;
24528c2ecf20Sopenharmony_ci}
24538c2ecf20Sopenharmony_ci
24548c2ecf20Sopenharmony_cistatic int __qed_ll2_start(struct qed_hwfn *p_hwfn,
24558c2ecf20Sopenharmony_ci			   struct qed_ll2_params *params)
24568c2ecf20Sopenharmony_ci{
24578c2ecf20Sopenharmony_ci	struct qed_ll2_buffer *buffer, *tmp_buffer;
24588c2ecf20Sopenharmony_ci	struct qed_dev *cdev = p_hwfn->cdev;
24598c2ecf20Sopenharmony_ci	enum qed_ll2_conn_type conn_type;
24608c2ecf20Sopenharmony_ci	struct qed_ll2_acquire_data data;
24618c2ecf20Sopenharmony_ci	int rc, rx_cnt;
24628c2ecf20Sopenharmony_ci
24638c2ecf20Sopenharmony_ci	switch (p_hwfn->hw_info.personality) {
24648c2ecf20Sopenharmony_ci	case QED_PCI_FCOE:
24658c2ecf20Sopenharmony_ci		conn_type = QED_LL2_TYPE_FCOE;
24668c2ecf20Sopenharmony_ci		break;
24678c2ecf20Sopenharmony_ci	case QED_PCI_ISCSI:
24688c2ecf20Sopenharmony_ci		conn_type = QED_LL2_TYPE_ISCSI;
24698c2ecf20Sopenharmony_ci		break;
24708c2ecf20Sopenharmony_ci	case QED_PCI_ETH_ROCE:
24718c2ecf20Sopenharmony_ci		conn_type = QED_LL2_TYPE_ROCE;
24728c2ecf20Sopenharmony_ci		break;
24738c2ecf20Sopenharmony_ci	default:
24748c2ecf20Sopenharmony_ci
24758c2ecf20Sopenharmony_ci		conn_type = QED_LL2_TYPE_TEST;
24768c2ecf20Sopenharmony_ci	}
24778c2ecf20Sopenharmony_ci
24788c2ecf20Sopenharmony_ci	qed_ll2_set_conn_data(p_hwfn, &data, params, conn_type,
24798c2ecf20Sopenharmony_ci			      &cdev->ll2->handle, false);
24808c2ecf20Sopenharmony_ci
24818c2ecf20Sopenharmony_ci	rc = qed_ll2_acquire_connection(p_hwfn, &data);
24828c2ecf20Sopenharmony_ci	if (rc) {
24838c2ecf20Sopenharmony_ci		DP_INFO(p_hwfn, "Failed to acquire LL2 connection\n");
24848c2ecf20Sopenharmony_ci		return rc;
24858c2ecf20Sopenharmony_ci	}
24868c2ecf20Sopenharmony_ci
24878c2ecf20Sopenharmony_ci	rc = qed_ll2_establish_connection(p_hwfn, cdev->ll2->handle);
24888c2ecf20Sopenharmony_ci	if (rc) {
24898c2ecf20Sopenharmony_ci		DP_INFO(p_hwfn, "Failed to establish LL2 connection\n");
24908c2ecf20Sopenharmony_ci		goto release_conn;
24918c2ecf20Sopenharmony_ci	}
24928c2ecf20Sopenharmony_ci
24938c2ecf20Sopenharmony_ci	/* Post all Rx buffers to FW */
24948c2ecf20Sopenharmony_ci	spin_lock_bh(&cdev->ll2->lock);
24958c2ecf20Sopenharmony_ci	rx_cnt = cdev->ll2->rx_cnt;
24968c2ecf20Sopenharmony_ci	list_for_each_entry_safe(buffer, tmp_buffer, &cdev->ll2->list, list) {
24978c2ecf20Sopenharmony_ci		rc = qed_ll2_post_rx_buffer(p_hwfn,
24988c2ecf20Sopenharmony_ci					    cdev->ll2->handle,
24998c2ecf20Sopenharmony_ci					    buffer->phys_addr, 0, buffer, 1);
25008c2ecf20Sopenharmony_ci		if (rc) {
25018c2ecf20Sopenharmony_ci			DP_INFO(p_hwfn,
25028c2ecf20Sopenharmony_ci				"Failed to post an Rx buffer; Deleting it\n");
25038c2ecf20Sopenharmony_ci			dma_unmap_single(&cdev->pdev->dev, buffer->phys_addr,
25048c2ecf20Sopenharmony_ci					 cdev->ll2->rx_size, DMA_FROM_DEVICE);
25058c2ecf20Sopenharmony_ci			kfree(buffer->data);
25068c2ecf20Sopenharmony_ci			list_del(&buffer->list);
25078c2ecf20Sopenharmony_ci			kfree(buffer);
25088c2ecf20Sopenharmony_ci		} else {
25098c2ecf20Sopenharmony_ci			rx_cnt++;
25108c2ecf20Sopenharmony_ci		}
25118c2ecf20Sopenharmony_ci	}
25128c2ecf20Sopenharmony_ci	spin_unlock_bh(&cdev->ll2->lock);
25138c2ecf20Sopenharmony_ci
25148c2ecf20Sopenharmony_ci	if (rx_cnt == cdev->ll2->rx_cnt) {
25158c2ecf20Sopenharmony_ci		DP_NOTICE(p_hwfn, "Failed passing even a single Rx buffer\n");
25168c2ecf20Sopenharmony_ci		goto terminate_conn;
25178c2ecf20Sopenharmony_ci	}
25188c2ecf20Sopenharmony_ci	cdev->ll2->rx_cnt = rx_cnt;
25198c2ecf20Sopenharmony_ci
25208c2ecf20Sopenharmony_ci	return 0;
25218c2ecf20Sopenharmony_ci
25228c2ecf20Sopenharmony_citerminate_conn:
25238c2ecf20Sopenharmony_ci	qed_ll2_terminate_connection(p_hwfn, cdev->ll2->handle);
25248c2ecf20Sopenharmony_cirelease_conn:
25258c2ecf20Sopenharmony_ci	qed_ll2_release_connection(p_hwfn, cdev->ll2->handle);
25268c2ecf20Sopenharmony_ci	return rc;
25278c2ecf20Sopenharmony_ci}
25288c2ecf20Sopenharmony_ci
25298c2ecf20Sopenharmony_cistatic int qed_ll2_start(struct qed_dev *cdev, struct qed_ll2_params *params)
25308c2ecf20Sopenharmony_ci{
25318c2ecf20Sopenharmony_ci	bool b_is_storage_eng1 = qed_ll2_is_storage_eng1(cdev);
25328c2ecf20Sopenharmony_ci	struct qed_hwfn *p_hwfn = QED_AFFIN_HWFN(cdev);
25338c2ecf20Sopenharmony_ci	struct qed_ll2_buffer *buffer;
25348c2ecf20Sopenharmony_ci	int rx_num_desc, i, rc;
25358c2ecf20Sopenharmony_ci
25368c2ecf20Sopenharmony_ci	if (!is_valid_ether_addr(params->ll2_mac_address)) {
25378c2ecf20Sopenharmony_ci		DP_NOTICE(cdev, "Invalid Ethernet address\n");
25388c2ecf20Sopenharmony_ci		return -EINVAL;
25398c2ecf20Sopenharmony_ci	}
25408c2ecf20Sopenharmony_ci
25418c2ecf20Sopenharmony_ci	WARN_ON(!cdev->ll2->cbs);
25428c2ecf20Sopenharmony_ci
25438c2ecf20Sopenharmony_ci	/* Initialize LL2 locks & lists */
25448c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&cdev->ll2->list);
25458c2ecf20Sopenharmony_ci	spin_lock_init(&cdev->ll2->lock);
25468c2ecf20Sopenharmony_ci
25478c2ecf20Sopenharmony_ci	cdev->ll2->rx_size = PRM_DMA_PAD_BYTES_NUM + ETH_HLEN +
25488c2ecf20Sopenharmony_ci			     L1_CACHE_BYTES + params->mtu;
25498c2ecf20Sopenharmony_ci
25508c2ecf20Sopenharmony_ci	/* Allocate memory for LL2.
25518c2ecf20Sopenharmony_ci	 * In CMT mode, in case of a storage PF which is affintized to engine 1,
25528c2ecf20Sopenharmony_ci	 * LL2 is started also on engine 0 and thus we need twofold buffers.
25538c2ecf20Sopenharmony_ci	 */
25548c2ecf20Sopenharmony_ci	rx_num_desc = QED_LL2_RX_SIZE * (b_is_storage_eng1 ? 2 : 1);
25558c2ecf20Sopenharmony_ci	DP_INFO(cdev, "Allocating %d LL2 buffers of size %08x bytes\n",
25568c2ecf20Sopenharmony_ci		rx_num_desc, cdev->ll2->rx_size);
25578c2ecf20Sopenharmony_ci	for (i = 0; i < rx_num_desc; i++) {
25588c2ecf20Sopenharmony_ci		buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
25598c2ecf20Sopenharmony_ci		if (!buffer) {
25608c2ecf20Sopenharmony_ci			DP_INFO(cdev, "Failed to allocate LL2 buffers\n");
25618c2ecf20Sopenharmony_ci			rc = -ENOMEM;
25628c2ecf20Sopenharmony_ci			goto err0;
25638c2ecf20Sopenharmony_ci		}
25648c2ecf20Sopenharmony_ci
25658c2ecf20Sopenharmony_ci		rc = qed_ll2_alloc_buffer(cdev, (u8 **)&buffer->data,
25668c2ecf20Sopenharmony_ci					  &buffer->phys_addr);
25678c2ecf20Sopenharmony_ci		if (rc) {
25688c2ecf20Sopenharmony_ci			kfree(buffer);
25698c2ecf20Sopenharmony_ci			goto err0;
25708c2ecf20Sopenharmony_ci		}
25718c2ecf20Sopenharmony_ci
25728c2ecf20Sopenharmony_ci		list_add_tail(&buffer->list, &cdev->ll2->list);
25738c2ecf20Sopenharmony_ci	}
25748c2ecf20Sopenharmony_ci
25758c2ecf20Sopenharmony_ci	rc = __qed_ll2_start(p_hwfn, params);
25768c2ecf20Sopenharmony_ci	if (rc) {
25778c2ecf20Sopenharmony_ci		DP_NOTICE(cdev, "Failed to start LL2\n");
25788c2ecf20Sopenharmony_ci		goto err0;
25798c2ecf20Sopenharmony_ci	}
25808c2ecf20Sopenharmony_ci
25818c2ecf20Sopenharmony_ci	/* In CMT mode, always need to start LL2 on engine 0 for a storage PF,
25828c2ecf20Sopenharmony_ci	 * since broadcast/mutlicast packets are routed to engine 0.
25838c2ecf20Sopenharmony_ci	 */
25848c2ecf20Sopenharmony_ci	if (b_is_storage_eng1) {
25858c2ecf20Sopenharmony_ci		rc = __qed_ll2_start(QED_LEADING_HWFN(cdev), params);
25868c2ecf20Sopenharmony_ci		if (rc) {
25878c2ecf20Sopenharmony_ci			DP_NOTICE(QED_LEADING_HWFN(cdev),
25888c2ecf20Sopenharmony_ci				  "Failed to start LL2 on engine 0\n");
25898c2ecf20Sopenharmony_ci			goto err1;
25908c2ecf20Sopenharmony_ci		}
25918c2ecf20Sopenharmony_ci	}
25928c2ecf20Sopenharmony_ci
25938c2ecf20Sopenharmony_ci	if (QED_IS_ISCSI_PERSONALITY(p_hwfn)) {
25948c2ecf20Sopenharmony_ci		DP_VERBOSE(cdev, QED_MSG_STORAGE, "Starting OOO LL2 queue\n");
25958c2ecf20Sopenharmony_ci		rc = qed_ll2_start_ooo(p_hwfn, params);
25968c2ecf20Sopenharmony_ci		if (rc) {
25978c2ecf20Sopenharmony_ci			DP_NOTICE(cdev, "Failed to start OOO LL2\n");
25988c2ecf20Sopenharmony_ci			goto err2;
25998c2ecf20Sopenharmony_ci		}
26008c2ecf20Sopenharmony_ci	}
26018c2ecf20Sopenharmony_ci
26028c2ecf20Sopenharmony_ci	rc = qed_llh_add_mac_filter(cdev, 0, params->ll2_mac_address);
26038c2ecf20Sopenharmony_ci	if (rc) {
26048c2ecf20Sopenharmony_ci		DP_NOTICE(cdev, "Failed to add an LLH filter\n");
26058c2ecf20Sopenharmony_ci		goto err3;
26068c2ecf20Sopenharmony_ci	}
26078c2ecf20Sopenharmony_ci
26088c2ecf20Sopenharmony_ci	ether_addr_copy(cdev->ll2_mac_address, params->ll2_mac_address);
26098c2ecf20Sopenharmony_ci
26108c2ecf20Sopenharmony_ci	return 0;
26118c2ecf20Sopenharmony_ci
26128c2ecf20Sopenharmony_cierr3:
26138c2ecf20Sopenharmony_ci	if (QED_IS_ISCSI_PERSONALITY(p_hwfn))
26148c2ecf20Sopenharmony_ci		qed_ll2_stop_ooo(p_hwfn);
26158c2ecf20Sopenharmony_cierr2:
26168c2ecf20Sopenharmony_ci	if (b_is_storage_eng1)
26178c2ecf20Sopenharmony_ci		__qed_ll2_stop(QED_LEADING_HWFN(cdev));
26188c2ecf20Sopenharmony_cierr1:
26198c2ecf20Sopenharmony_ci	__qed_ll2_stop(p_hwfn);
26208c2ecf20Sopenharmony_cierr0:
26218c2ecf20Sopenharmony_ci	qed_ll2_kill_buffers(cdev);
26228c2ecf20Sopenharmony_ci	cdev->ll2->handle = QED_LL2_UNUSED_HANDLE;
26238c2ecf20Sopenharmony_ci	return rc;
26248c2ecf20Sopenharmony_ci}
26258c2ecf20Sopenharmony_ci
26268c2ecf20Sopenharmony_cistatic int qed_ll2_start_xmit(struct qed_dev *cdev, struct sk_buff *skb,
26278c2ecf20Sopenharmony_ci			      unsigned long xmit_flags)
26288c2ecf20Sopenharmony_ci{
26298c2ecf20Sopenharmony_ci	struct qed_hwfn *p_hwfn = QED_AFFIN_HWFN(cdev);
26308c2ecf20Sopenharmony_ci	struct qed_ll2_tx_pkt_info pkt;
26318c2ecf20Sopenharmony_ci	const skb_frag_t *frag;
26328c2ecf20Sopenharmony_ci	u8 flags = 0, nr_frags;
26338c2ecf20Sopenharmony_ci	int rc = -EINVAL, i;
26348c2ecf20Sopenharmony_ci	dma_addr_t mapping;
26358c2ecf20Sopenharmony_ci	u16 vlan = 0;
26368c2ecf20Sopenharmony_ci
26378c2ecf20Sopenharmony_ci	if (unlikely(skb->ip_summed != CHECKSUM_NONE)) {
26388c2ecf20Sopenharmony_ci		DP_INFO(cdev, "Cannot transmit a checksummed packet\n");
26398c2ecf20Sopenharmony_ci		return -EINVAL;
26408c2ecf20Sopenharmony_ci	}
26418c2ecf20Sopenharmony_ci
26428c2ecf20Sopenharmony_ci	/* Cache number of fragments from SKB since SKB may be freed by
26438c2ecf20Sopenharmony_ci	 * the completion routine after calling qed_ll2_prepare_tx_packet()
26448c2ecf20Sopenharmony_ci	 */
26458c2ecf20Sopenharmony_ci	nr_frags = skb_shinfo(skb)->nr_frags;
26468c2ecf20Sopenharmony_ci
26478c2ecf20Sopenharmony_ci	if (1 + nr_frags > CORE_LL2_TX_MAX_BDS_PER_PACKET) {
26488c2ecf20Sopenharmony_ci		DP_ERR(cdev, "Cannot transmit a packet with %d fragments\n",
26498c2ecf20Sopenharmony_ci		       1 + nr_frags);
26508c2ecf20Sopenharmony_ci		return -EINVAL;
26518c2ecf20Sopenharmony_ci	}
26528c2ecf20Sopenharmony_ci
26538c2ecf20Sopenharmony_ci	mapping = dma_map_single(&cdev->pdev->dev, skb->data,
26548c2ecf20Sopenharmony_ci				 skb->len, DMA_TO_DEVICE);
26558c2ecf20Sopenharmony_ci	if (unlikely(dma_mapping_error(&cdev->pdev->dev, mapping))) {
26568c2ecf20Sopenharmony_ci		DP_NOTICE(cdev, "SKB mapping failed\n");
26578c2ecf20Sopenharmony_ci		return -EINVAL;
26588c2ecf20Sopenharmony_ci	}
26598c2ecf20Sopenharmony_ci
26608c2ecf20Sopenharmony_ci	/* Request HW to calculate IP csum */
26618c2ecf20Sopenharmony_ci	if (!((vlan_get_protocol(skb) == htons(ETH_P_IPV6)) &&
26628c2ecf20Sopenharmony_ci	      ipv6_hdr(skb)->nexthdr == NEXTHDR_IPV6))
26638c2ecf20Sopenharmony_ci		flags |= BIT(CORE_TX_BD_DATA_IP_CSUM_SHIFT);
26648c2ecf20Sopenharmony_ci
26658c2ecf20Sopenharmony_ci	if (skb_vlan_tag_present(skb)) {
26668c2ecf20Sopenharmony_ci		vlan = skb_vlan_tag_get(skb);
26678c2ecf20Sopenharmony_ci		flags |= BIT(CORE_TX_BD_DATA_VLAN_INSERTION_SHIFT);
26688c2ecf20Sopenharmony_ci	}
26698c2ecf20Sopenharmony_ci
26708c2ecf20Sopenharmony_ci	memset(&pkt, 0, sizeof(pkt));
26718c2ecf20Sopenharmony_ci	pkt.num_of_bds = 1 + nr_frags;
26728c2ecf20Sopenharmony_ci	pkt.vlan = vlan;
26738c2ecf20Sopenharmony_ci	pkt.bd_flags = flags;
26748c2ecf20Sopenharmony_ci	pkt.tx_dest = QED_LL2_TX_DEST_NW;
26758c2ecf20Sopenharmony_ci	pkt.first_frag = mapping;
26768c2ecf20Sopenharmony_ci	pkt.first_frag_len = skb->len;
26778c2ecf20Sopenharmony_ci	pkt.cookie = skb;
26788c2ecf20Sopenharmony_ci	if (test_bit(QED_MF_UFP_SPECIFIC, &cdev->mf_bits) &&
26798c2ecf20Sopenharmony_ci	    test_bit(QED_LL2_XMIT_FLAGS_FIP_DISCOVERY, &xmit_flags))
26808c2ecf20Sopenharmony_ci		pkt.remove_stag = true;
26818c2ecf20Sopenharmony_ci
26828c2ecf20Sopenharmony_ci	/* qed_ll2_prepare_tx_packet() may actually send the packet if
26838c2ecf20Sopenharmony_ci	 * there are no fragments in the skb and subsequently the completion
26848c2ecf20Sopenharmony_ci	 * routine may run and free the SKB, so no dereferencing the SKB
26858c2ecf20Sopenharmony_ci	 * beyond this point unless skb has any fragments.
26868c2ecf20Sopenharmony_ci	 */
26878c2ecf20Sopenharmony_ci	rc = qed_ll2_prepare_tx_packet(p_hwfn, cdev->ll2->handle,
26888c2ecf20Sopenharmony_ci				       &pkt, 1);
26898c2ecf20Sopenharmony_ci	if (rc)
26908c2ecf20Sopenharmony_ci		goto err;
26918c2ecf20Sopenharmony_ci
26928c2ecf20Sopenharmony_ci	for (i = 0; i < nr_frags; i++) {
26938c2ecf20Sopenharmony_ci		frag = &skb_shinfo(skb)->frags[i];
26948c2ecf20Sopenharmony_ci
26958c2ecf20Sopenharmony_ci		mapping = skb_frag_dma_map(&cdev->pdev->dev, frag, 0,
26968c2ecf20Sopenharmony_ci					   skb_frag_size(frag), DMA_TO_DEVICE);
26978c2ecf20Sopenharmony_ci
26988c2ecf20Sopenharmony_ci		if (unlikely(dma_mapping_error(&cdev->pdev->dev, mapping))) {
26998c2ecf20Sopenharmony_ci			DP_NOTICE(cdev,
27008c2ecf20Sopenharmony_ci				  "Unable to map frag - dropping packet\n");
27018c2ecf20Sopenharmony_ci			rc = -ENOMEM;
27028c2ecf20Sopenharmony_ci			goto err;
27038c2ecf20Sopenharmony_ci		}
27048c2ecf20Sopenharmony_ci
27058c2ecf20Sopenharmony_ci		rc = qed_ll2_set_fragment_of_tx_packet(p_hwfn,
27068c2ecf20Sopenharmony_ci						       cdev->ll2->handle,
27078c2ecf20Sopenharmony_ci						       mapping,
27088c2ecf20Sopenharmony_ci						       skb_frag_size(frag));
27098c2ecf20Sopenharmony_ci
27108c2ecf20Sopenharmony_ci		/* if failed not much to do here, partial packet has been posted
27118c2ecf20Sopenharmony_ci		 * we can't free memory, will need to wait for completion
27128c2ecf20Sopenharmony_ci		 */
27138c2ecf20Sopenharmony_ci		if (rc)
27148c2ecf20Sopenharmony_ci			goto err2;
27158c2ecf20Sopenharmony_ci	}
27168c2ecf20Sopenharmony_ci
27178c2ecf20Sopenharmony_ci	return 0;
27188c2ecf20Sopenharmony_ci
27198c2ecf20Sopenharmony_cierr:
27208c2ecf20Sopenharmony_ci	dma_unmap_single(&cdev->pdev->dev, mapping, skb->len, DMA_TO_DEVICE);
27218c2ecf20Sopenharmony_cierr2:
27228c2ecf20Sopenharmony_ci	return rc;
27238c2ecf20Sopenharmony_ci}
27248c2ecf20Sopenharmony_ci
27258c2ecf20Sopenharmony_cistatic int qed_ll2_stats(struct qed_dev *cdev, struct qed_ll2_stats *stats)
27268c2ecf20Sopenharmony_ci{
27278c2ecf20Sopenharmony_ci	bool b_is_storage_eng1 = qed_ll2_is_storage_eng1(cdev);
27288c2ecf20Sopenharmony_ci	struct qed_hwfn *p_hwfn = QED_AFFIN_HWFN(cdev);
27298c2ecf20Sopenharmony_ci	int rc;
27308c2ecf20Sopenharmony_ci
27318c2ecf20Sopenharmony_ci	if (!cdev->ll2)
27328c2ecf20Sopenharmony_ci		return -EINVAL;
27338c2ecf20Sopenharmony_ci
27348c2ecf20Sopenharmony_ci	rc = qed_ll2_get_stats(p_hwfn, cdev->ll2->handle, stats);
27358c2ecf20Sopenharmony_ci	if (rc) {
27368c2ecf20Sopenharmony_ci		DP_NOTICE(p_hwfn, "Failed to get LL2 stats\n");
27378c2ecf20Sopenharmony_ci		return rc;
27388c2ecf20Sopenharmony_ci	}
27398c2ecf20Sopenharmony_ci
27408c2ecf20Sopenharmony_ci	/* In CMT mode, LL2 is always started on engine 0 for a storage PF */
27418c2ecf20Sopenharmony_ci	if (b_is_storage_eng1) {
27428c2ecf20Sopenharmony_ci		rc = __qed_ll2_get_stats(QED_LEADING_HWFN(cdev),
27438c2ecf20Sopenharmony_ci					 cdev->ll2->handle, stats);
27448c2ecf20Sopenharmony_ci		if (rc) {
27458c2ecf20Sopenharmony_ci			DP_NOTICE(QED_LEADING_HWFN(cdev),
27468c2ecf20Sopenharmony_ci				  "Failed to get LL2 stats on engine 0\n");
27478c2ecf20Sopenharmony_ci			return rc;
27488c2ecf20Sopenharmony_ci		}
27498c2ecf20Sopenharmony_ci	}
27508c2ecf20Sopenharmony_ci
27518c2ecf20Sopenharmony_ci	return 0;
27528c2ecf20Sopenharmony_ci}
27538c2ecf20Sopenharmony_ci
27548c2ecf20Sopenharmony_ciconst struct qed_ll2_ops qed_ll2_ops_pass = {
27558c2ecf20Sopenharmony_ci	.start = &qed_ll2_start,
27568c2ecf20Sopenharmony_ci	.stop = &qed_ll2_stop,
27578c2ecf20Sopenharmony_ci	.start_xmit = &qed_ll2_start_xmit,
27588c2ecf20Sopenharmony_ci	.register_cb_ops = &qed_ll2_register_cb_ops,
27598c2ecf20Sopenharmony_ci	.get_stats = &qed_ll2_stats,
27608c2ecf20Sopenharmony_ci};
27618c2ecf20Sopenharmony_ci
27628c2ecf20Sopenharmony_ciint qed_ll2_alloc_if(struct qed_dev *cdev)
27638c2ecf20Sopenharmony_ci{
27648c2ecf20Sopenharmony_ci	cdev->ll2 = kzalloc(sizeof(*cdev->ll2), GFP_KERNEL);
27658c2ecf20Sopenharmony_ci	return cdev->ll2 ? 0 : -ENOMEM;
27668c2ecf20Sopenharmony_ci}
27678c2ecf20Sopenharmony_ci
27688c2ecf20Sopenharmony_civoid qed_ll2_dealloc_if(struct qed_dev *cdev)
27698c2ecf20Sopenharmony_ci{
27708c2ecf20Sopenharmony_ci	kfree(cdev->ll2);
27718c2ecf20Sopenharmony_ci	cdev->ll2 = NULL;
27728c2ecf20Sopenharmony_ci}
2773