162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
262306a36Sopenharmony_ci/* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/bitfield.h>
562306a36Sopenharmony_ci#include <linux/dmapool.h>
662306a36Sopenharmony_ci#include <linux/etherdevice.h>
762306a36Sopenharmony_ci#include <linux/if_vlan.h>
862306a36Sopenharmony_ci#include <linux/platform_device.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "prestera_dsa.h"
1162306a36Sopenharmony_ci#include "prestera.h"
1262306a36Sopenharmony_ci#include "prestera_hw.h"
1362306a36Sopenharmony_ci#include "prestera_rxtx.h"
1462306a36Sopenharmony_ci#include "prestera_devlink.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define PRESTERA_SDMA_WAIT_MUL		10
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistruct prestera_sdma_desc {
1962306a36Sopenharmony_ci	__le32 word1;
2062306a36Sopenharmony_ci	__le32 word2;
2162306a36Sopenharmony_ci	__le32 buff;
2262306a36Sopenharmony_ci	__le32 next;
2362306a36Sopenharmony_ci} __packed __aligned(16);
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define PRESTERA_SDMA_BUFF_SIZE_MAX	1544
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define PRESTERA_SDMA_RX_DESC_PKT_LEN(desc) \
2862306a36Sopenharmony_ci	((le32_to_cpu((desc)->word2) >> 16) & GENMASK(13, 0))
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define PRESTERA_SDMA_RX_DESC_OWNER(desc) \
3162306a36Sopenharmony_ci	((le32_to_cpu((desc)->word1) & BIT(31)) >> 31)
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define PRESTERA_SDMA_RX_DESC_IS_RCVD(desc) \
3462306a36Sopenharmony_ci	(PRESTERA_SDMA_RX_DESC_OWNER(desc) == PRESTERA_SDMA_RX_DESC_CPU_OWN)
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define PRESTERA_SDMA_RX_DESC_CPU_OWN	0
3762306a36Sopenharmony_ci#define PRESTERA_SDMA_RX_DESC_DMA_OWN	1
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define PRESTERA_SDMA_RX_QUEUE_NUM	8
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define PRESTERA_SDMA_RX_DESC_PER_Q	1000
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_PER_Q	1000
4462306a36Sopenharmony_ci#define PRESTERA_SDMA_TX_MAX_BURST	64
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_OWNER(desc) \
4762306a36Sopenharmony_ci	((le32_to_cpu((desc)->word1) & BIT(31)) >> 31)
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_CPU_OWN	0
5062306a36Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_DMA_OWN	1U
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_IS_SENT(desc) \
5362306a36Sopenharmony_ci	(PRESTERA_SDMA_TX_DESC_OWNER(desc) == PRESTERA_SDMA_TX_DESC_CPU_OWN)
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_LAST	BIT(20)
5662306a36Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_FIRST	BIT(21)
5762306a36Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_CALC_CRC	BIT(12)
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_SINGLE	\
6062306a36Sopenharmony_ci	(PRESTERA_SDMA_TX_DESC_FIRST | PRESTERA_SDMA_TX_DESC_LAST)
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_INIT	\
6362306a36Sopenharmony_ci	(PRESTERA_SDMA_TX_DESC_SINGLE | PRESTERA_SDMA_TX_DESC_CALC_CRC)
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#define PRESTERA_SDMA_RX_INTR_MASK_REG		0x2814
6662306a36Sopenharmony_ci#define PRESTERA_SDMA_RX_QUEUE_STATUS_REG	0x2680
6762306a36Sopenharmony_ci#define PRESTERA_SDMA_RX_QUEUE_DESC_REG(n)	(0x260C + (n) * 16)
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci#define PRESTERA_SDMA_TX_QUEUE_DESC_REG		0x26C0
7062306a36Sopenharmony_ci#define PRESTERA_SDMA_TX_QUEUE_START_REG	0x2868
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistruct prestera_sdma_buf {
7362306a36Sopenharmony_ci	struct prestera_sdma_desc *desc;
7462306a36Sopenharmony_ci	dma_addr_t desc_dma;
7562306a36Sopenharmony_ci	struct sk_buff *skb;
7662306a36Sopenharmony_ci	dma_addr_t buf_dma;
7762306a36Sopenharmony_ci	bool is_used;
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistruct prestera_rx_ring {
8162306a36Sopenharmony_ci	struct prestera_sdma_buf *bufs;
8262306a36Sopenharmony_ci	int next_rx;
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistruct prestera_tx_ring {
8662306a36Sopenharmony_ci	struct prestera_sdma_buf *bufs;
8762306a36Sopenharmony_ci	int next_tx;
8862306a36Sopenharmony_ci	int max_burst;
8962306a36Sopenharmony_ci	int burst;
9062306a36Sopenharmony_ci};
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistruct prestera_sdma {
9362306a36Sopenharmony_ci	struct prestera_rx_ring rx_ring[PRESTERA_SDMA_RX_QUEUE_NUM];
9462306a36Sopenharmony_ci	struct prestera_tx_ring tx_ring;
9562306a36Sopenharmony_ci	struct prestera_switch *sw;
9662306a36Sopenharmony_ci	struct dma_pool *desc_pool;
9762306a36Sopenharmony_ci	struct work_struct tx_work;
9862306a36Sopenharmony_ci	struct napi_struct rx_napi;
9962306a36Sopenharmony_ci	struct net_device napi_dev;
10062306a36Sopenharmony_ci	u32 map_addr;
10162306a36Sopenharmony_ci	u64 dma_mask;
10262306a36Sopenharmony_ci	/* protect SDMA with concurrent access from multiple CPUs */
10362306a36Sopenharmony_ci	spinlock_t tx_lock;
10462306a36Sopenharmony_ci};
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistruct prestera_rxtx {
10762306a36Sopenharmony_ci	struct prestera_sdma sdma;
10862306a36Sopenharmony_ci};
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic int prestera_sdma_buf_init(struct prestera_sdma *sdma,
11162306a36Sopenharmony_ci				  struct prestera_sdma_buf *buf)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	struct prestera_sdma_desc *desc;
11462306a36Sopenharmony_ci	dma_addr_t dma;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	desc = dma_pool_alloc(sdma->desc_pool, GFP_DMA | GFP_KERNEL, &dma);
11762306a36Sopenharmony_ci	if (!desc)
11862306a36Sopenharmony_ci		return -ENOMEM;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	buf->buf_dma = DMA_MAPPING_ERROR;
12162306a36Sopenharmony_ci	buf->desc_dma = dma;
12262306a36Sopenharmony_ci	buf->desc = desc;
12362306a36Sopenharmony_ci	buf->skb = NULL;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	return 0;
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic u32 prestera_sdma_map(struct prestera_sdma *sdma, dma_addr_t pa)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	return sdma->map_addr + pa;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic void prestera_sdma_rx_desc_init(struct prestera_sdma *sdma,
13462306a36Sopenharmony_ci				       struct prestera_sdma_desc *desc,
13562306a36Sopenharmony_ci				       dma_addr_t buf)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	u32 word = le32_to_cpu(desc->word2);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	u32p_replace_bits(&word, PRESTERA_SDMA_BUFF_SIZE_MAX, GENMASK(15, 0));
14062306a36Sopenharmony_ci	desc->word2 = cpu_to_le32(word);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	desc->buff = cpu_to_le32(prestera_sdma_map(sdma, buf));
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/* make sure buffer is set before reset the descriptor */
14562306a36Sopenharmony_ci	wmb();
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	desc->word1 = cpu_to_le32(0xA0000000);
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic void prestera_sdma_rx_desc_set_next(struct prestera_sdma *sdma,
15162306a36Sopenharmony_ci					   struct prestera_sdma_desc *desc,
15262306a36Sopenharmony_ci					   dma_addr_t next)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	desc->next = cpu_to_le32(prestera_sdma_map(sdma, next));
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic int prestera_sdma_rx_skb_alloc(struct prestera_sdma *sdma,
15862306a36Sopenharmony_ci				      struct prestera_sdma_buf *buf)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	struct device *dev = sdma->sw->dev->dev;
16162306a36Sopenharmony_ci	struct sk_buff *skb;
16262306a36Sopenharmony_ci	dma_addr_t dma;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	skb = alloc_skb(PRESTERA_SDMA_BUFF_SIZE_MAX, GFP_DMA | GFP_ATOMIC);
16562306a36Sopenharmony_ci	if (!skb)
16662306a36Sopenharmony_ci		return -ENOMEM;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	dma = dma_map_single(dev, skb->data, skb->len, DMA_FROM_DEVICE);
16962306a36Sopenharmony_ci	if (dma_mapping_error(dev, dma))
17062306a36Sopenharmony_ci		goto err_dma_map;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (buf->skb)
17362306a36Sopenharmony_ci		dma_unmap_single(dev, buf->buf_dma, buf->skb->len,
17462306a36Sopenharmony_ci				 DMA_FROM_DEVICE);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	buf->buf_dma = dma;
17762306a36Sopenharmony_ci	buf->skb = skb;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	return 0;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cierr_dma_map:
18262306a36Sopenharmony_ci	kfree_skb(skb);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	return -ENOMEM;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic struct sk_buff *prestera_sdma_rx_skb_get(struct prestera_sdma *sdma,
18862306a36Sopenharmony_ci						struct prestera_sdma_buf *buf)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	dma_addr_t buf_dma = buf->buf_dma;
19162306a36Sopenharmony_ci	struct sk_buff *skb = buf->skb;
19262306a36Sopenharmony_ci	u32 len = skb->len;
19362306a36Sopenharmony_ci	int err;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	err = prestera_sdma_rx_skb_alloc(sdma, buf);
19662306a36Sopenharmony_ci	if (err) {
19762306a36Sopenharmony_ci		buf->buf_dma = buf_dma;
19862306a36Sopenharmony_ci		buf->skb = skb;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci		skb = alloc_skb(skb->len, GFP_ATOMIC);
20162306a36Sopenharmony_ci		if (skb) {
20262306a36Sopenharmony_ci			skb_put(skb, len);
20362306a36Sopenharmony_ci			skb_copy_from_linear_data(buf->skb, skb->data, len);
20462306a36Sopenharmony_ci		}
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	prestera_sdma_rx_desc_init(sdma, buf->desc, buf->buf_dma);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	return skb;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic int prestera_rxtx_process_skb(struct prestera_sdma *sdma,
21362306a36Sopenharmony_ci				     struct sk_buff *skb)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	struct prestera_port *port;
21662306a36Sopenharmony_ci	struct prestera_dsa dsa;
21762306a36Sopenharmony_ci	u32 hw_port, dev_id;
21862306a36Sopenharmony_ci	u8 cpu_code;
21962306a36Sopenharmony_ci	int err;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	skb_pull(skb, ETH_HLEN);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	/* ethertype field is part of the dsa header */
22462306a36Sopenharmony_ci	err = prestera_dsa_parse(&dsa, skb->data - ETH_TLEN);
22562306a36Sopenharmony_ci	if (err)
22662306a36Sopenharmony_ci		return err;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	dev_id = dsa.hw_dev_num;
22962306a36Sopenharmony_ci	hw_port = dsa.port_num;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	port = prestera_port_find_by_hwid(sdma->sw, dev_id, hw_port);
23262306a36Sopenharmony_ci	if (unlikely(!port)) {
23362306a36Sopenharmony_ci		dev_warn_ratelimited(prestera_dev(sdma->sw), "received pkt for non-existent port(%u, %u)\n",
23462306a36Sopenharmony_ci				     dev_id, hw_port);
23562306a36Sopenharmony_ci		return -ENOENT;
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	if (unlikely(!pskb_may_pull(skb, PRESTERA_DSA_HLEN)))
23962306a36Sopenharmony_ci		return -EINVAL;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/* remove DSA tag and update checksum */
24262306a36Sopenharmony_ci	skb_pull_rcsum(skb, PRESTERA_DSA_HLEN);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	memmove(skb->data - ETH_HLEN, skb->data - ETH_HLEN - PRESTERA_DSA_HLEN,
24562306a36Sopenharmony_ci		ETH_ALEN * 2);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	skb_push(skb, ETH_HLEN);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, port->dev);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (dsa.vlan.is_tagged) {
25262306a36Sopenharmony_ci		u16 tci = dsa.vlan.vid & VLAN_VID_MASK;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci		tci |= dsa.vlan.vpt << VLAN_PRIO_SHIFT;
25562306a36Sopenharmony_ci		if (dsa.vlan.cfi_bit)
25662306a36Sopenharmony_ci			tci |= VLAN_CFI_MASK;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), tci);
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	cpu_code = dsa.cpu_code;
26262306a36Sopenharmony_ci	prestera_devlink_trap_report(port, skb, cpu_code);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	return 0;
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic int prestera_sdma_next_rx_buf_idx(int buf_idx)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	return (buf_idx + 1) % PRESTERA_SDMA_RX_DESC_PER_Q;
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic int prestera_sdma_rx_poll(struct napi_struct *napi, int budget)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	int qnum = PRESTERA_SDMA_RX_QUEUE_NUM;
27562306a36Sopenharmony_ci	unsigned int rxq_done_map = 0;
27662306a36Sopenharmony_ci	struct prestera_sdma *sdma;
27762306a36Sopenharmony_ci	struct list_head rx_list;
27862306a36Sopenharmony_ci	unsigned int qmask;
27962306a36Sopenharmony_ci	int pkts_done = 0;
28062306a36Sopenharmony_ci	int q;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	qnum = PRESTERA_SDMA_RX_QUEUE_NUM;
28362306a36Sopenharmony_ci	qmask = GENMASK(qnum - 1, 0);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	INIT_LIST_HEAD(&rx_list);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	sdma = container_of(napi, struct prestera_sdma, rx_napi);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	while (pkts_done < budget && rxq_done_map != qmask) {
29062306a36Sopenharmony_ci		for (q = 0; q < qnum && pkts_done < budget; q++) {
29162306a36Sopenharmony_ci			struct prestera_rx_ring *ring = &sdma->rx_ring[q];
29262306a36Sopenharmony_ci			struct prestera_sdma_desc *desc;
29362306a36Sopenharmony_ci			struct prestera_sdma_buf *buf;
29462306a36Sopenharmony_ci			int buf_idx = ring->next_rx;
29562306a36Sopenharmony_ci			struct sk_buff *skb;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci			buf = &ring->bufs[buf_idx];
29862306a36Sopenharmony_ci			desc = buf->desc;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci			if (PRESTERA_SDMA_RX_DESC_IS_RCVD(desc)) {
30162306a36Sopenharmony_ci				rxq_done_map &= ~BIT(q);
30262306a36Sopenharmony_ci			} else {
30362306a36Sopenharmony_ci				rxq_done_map |= BIT(q);
30462306a36Sopenharmony_ci				continue;
30562306a36Sopenharmony_ci			}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci			pkts_done++;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci			__skb_trim(buf->skb, PRESTERA_SDMA_RX_DESC_PKT_LEN(desc));
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci			skb = prestera_sdma_rx_skb_get(sdma, buf);
31262306a36Sopenharmony_ci			if (!skb)
31362306a36Sopenharmony_ci				goto rx_next_buf;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci			if (unlikely(prestera_rxtx_process_skb(sdma, skb)))
31662306a36Sopenharmony_ci				goto rx_next_buf;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci			list_add_tail(&skb->list, &rx_list);
31962306a36Sopenharmony_cirx_next_buf:
32062306a36Sopenharmony_ci			ring->next_rx = prestera_sdma_next_rx_buf_idx(buf_idx);
32162306a36Sopenharmony_ci		}
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	if (pkts_done < budget && napi_complete_done(napi, pkts_done))
32562306a36Sopenharmony_ci		prestera_write(sdma->sw, PRESTERA_SDMA_RX_INTR_MASK_REG,
32662306a36Sopenharmony_ci			       GENMASK(9, 2));
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	netif_receive_skb_list(&rx_list);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	return pkts_done;
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_cistatic void prestera_sdma_rx_fini(struct prestera_sdma *sdma)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	int qnum = PRESTERA_SDMA_RX_QUEUE_NUM;
33662306a36Sopenharmony_ci	int q, b;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	/* disable all rx queues */
33962306a36Sopenharmony_ci	prestera_write(sdma->sw, PRESTERA_SDMA_RX_QUEUE_STATUS_REG,
34062306a36Sopenharmony_ci		       GENMASK(15, 8));
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	for (q = 0; q < qnum; q++) {
34362306a36Sopenharmony_ci		struct prestera_rx_ring *ring = &sdma->rx_ring[q];
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci		if (!ring->bufs)
34662306a36Sopenharmony_ci			break;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci		for (b = 0; b < PRESTERA_SDMA_RX_DESC_PER_Q; b++) {
34962306a36Sopenharmony_ci			struct prestera_sdma_buf *buf = &ring->bufs[b];
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci			if (buf->desc_dma)
35262306a36Sopenharmony_ci				dma_pool_free(sdma->desc_pool, buf->desc,
35362306a36Sopenharmony_ci					      buf->desc_dma);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci			if (!buf->skb)
35662306a36Sopenharmony_ci				continue;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci			if (buf->buf_dma != DMA_MAPPING_ERROR)
35962306a36Sopenharmony_ci				dma_unmap_single(sdma->sw->dev->dev,
36062306a36Sopenharmony_ci						 buf->buf_dma, buf->skb->len,
36162306a36Sopenharmony_ci						 DMA_FROM_DEVICE);
36262306a36Sopenharmony_ci			kfree_skb(buf->skb);
36362306a36Sopenharmony_ci		}
36462306a36Sopenharmony_ci	}
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cistatic int prestera_sdma_rx_init(struct prestera_sdma *sdma)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	int bnum = PRESTERA_SDMA_RX_DESC_PER_Q;
37062306a36Sopenharmony_ci	int qnum = PRESTERA_SDMA_RX_QUEUE_NUM;
37162306a36Sopenharmony_ci	int err;
37262306a36Sopenharmony_ci	int q;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	/* disable all rx queues */
37562306a36Sopenharmony_ci	prestera_write(sdma->sw, PRESTERA_SDMA_RX_QUEUE_STATUS_REG,
37662306a36Sopenharmony_ci		       GENMASK(15, 8));
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	for (q = 0; q < qnum; q++) {
37962306a36Sopenharmony_ci		struct prestera_sdma_buf *head, *tail, *next, *prev;
38062306a36Sopenharmony_ci		struct prestera_rx_ring *ring = &sdma->rx_ring[q];
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci		ring->bufs = kmalloc_array(bnum, sizeof(*head), GFP_KERNEL);
38362306a36Sopenharmony_ci		if (!ring->bufs)
38462306a36Sopenharmony_ci			return -ENOMEM;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		ring->next_rx = 0;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci		tail = &ring->bufs[bnum - 1];
38962306a36Sopenharmony_ci		head = &ring->bufs[0];
39062306a36Sopenharmony_ci		next = head;
39162306a36Sopenharmony_ci		prev = next;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci		do {
39462306a36Sopenharmony_ci			err = prestera_sdma_buf_init(sdma, next);
39562306a36Sopenharmony_ci			if (err)
39662306a36Sopenharmony_ci				return err;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci			err = prestera_sdma_rx_skb_alloc(sdma, next);
39962306a36Sopenharmony_ci			if (err)
40062306a36Sopenharmony_ci				return err;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci			prestera_sdma_rx_desc_init(sdma, next->desc,
40362306a36Sopenharmony_ci						   next->buf_dma);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci			prestera_sdma_rx_desc_set_next(sdma, prev->desc,
40662306a36Sopenharmony_ci						       next->desc_dma);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci			prev = next;
40962306a36Sopenharmony_ci			next++;
41062306a36Sopenharmony_ci		} while (prev != tail);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci		/* join tail with head to make a circular list */
41362306a36Sopenharmony_ci		prestera_sdma_rx_desc_set_next(sdma, tail->desc, head->desc_dma);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		prestera_write(sdma->sw, PRESTERA_SDMA_RX_QUEUE_DESC_REG(q),
41662306a36Sopenharmony_ci			       prestera_sdma_map(sdma, head->desc_dma));
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	/* make sure all rx descs are filled before enabling all rx queues */
42062306a36Sopenharmony_ci	wmb();
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	prestera_write(sdma->sw, PRESTERA_SDMA_RX_QUEUE_STATUS_REG,
42362306a36Sopenharmony_ci		       GENMASK(7, 0));
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	return 0;
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic void prestera_sdma_tx_desc_init(struct prestera_sdma *sdma,
42962306a36Sopenharmony_ci				       struct prestera_sdma_desc *desc)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	desc->word1 = cpu_to_le32(PRESTERA_SDMA_TX_DESC_INIT);
43262306a36Sopenharmony_ci	desc->word2 = 0;
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic void prestera_sdma_tx_desc_set_next(struct prestera_sdma *sdma,
43662306a36Sopenharmony_ci					   struct prestera_sdma_desc *desc,
43762306a36Sopenharmony_ci					   dma_addr_t next)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	desc->next = cpu_to_le32(prestera_sdma_map(sdma, next));
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic void prestera_sdma_tx_desc_set_buf(struct prestera_sdma *sdma,
44362306a36Sopenharmony_ci					  struct prestera_sdma_desc *desc,
44462306a36Sopenharmony_ci					  dma_addr_t buf, size_t len)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	u32 word = le32_to_cpu(desc->word2);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	u32p_replace_bits(&word, len + ETH_FCS_LEN, GENMASK(30, 16));
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	desc->buff = cpu_to_le32(prestera_sdma_map(sdma, buf));
45162306a36Sopenharmony_ci	desc->word2 = cpu_to_le32(word);
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_cistatic void prestera_sdma_tx_desc_xmit(struct prestera_sdma_desc *desc)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	u32 word = le32_to_cpu(desc->word1);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	word |= PRESTERA_SDMA_TX_DESC_DMA_OWN << 31;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	/* make sure everything is written before enable xmit */
46162306a36Sopenharmony_ci	wmb();
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	desc->word1 = cpu_to_le32(word);
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cistatic int prestera_sdma_tx_buf_map(struct prestera_sdma *sdma,
46762306a36Sopenharmony_ci				    struct prestera_sdma_buf *buf,
46862306a36Sopenharmony_ci				    struct sk_buff *skb)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	struct device *dma_dev = sdma->sw->dev->dev;
47162306a36Sopenharmony_ci	dma_addr_t dma;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	dma = dma_map_single(dma_dev, skb->data, skb->len, DMA_TO_DEVICE);
47462306a36Sopenharmony_ci	if (dma_mapping_error(dma_dev, dma))
47562306a36Sopenharmony_ci		return -ENOMEM;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	buf->buf_dma = dma;
47862306a36Sopenharmony_ci	buf->skb = skb;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	return 0;
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic void prestera_sdma_tx_buf_unmap(struct prestera_sdma *sdma,
48462306a36Sopenharmony_ci				       struct prestera_sdma_buf *buf)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	struct device *dma_dev = sdma->sw->dev->dev;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	dma_unmap_single(dma_dev, buf->buf_dma, buf->skb->len, DMA_TO_DEVICE);
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_cistatic void prestera_sdma_tx_recycle_work_fn(struct work_struct *work)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	int bnum = PRESTERA_SDMA_TX_DESC_PER_Q;
49462306a36Sopenharmony_ci	struct prestera_tx_ring *tx_ring;
49562306a36Sopenharmony_ci	struct prestera_sdma *sdma;
49662306a36Sopenharmony_ci	int b;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	sdma = container_of(work, struct prestera_sdma, tx_work);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	tx_ring = &sdma->tx_ring;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	for (b = 0; b < bnum; b++) {
50362306a36Sopenharmony_ci		struct prestera_sdma_buf *buf = &tx_ring->bufs[b];
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci		if (!buf->is_used)
50662306a36Sopenharmony_ci			continue;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci		if (!PRESTERA_SDMA_TX_DESC_IS_SENT(buf->desc))
50962306a36Sopenharmony_ci			continue;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci		prestera_sdma_tx_buf_unmap(sdma, buf);
51262306a36Sopenharmony_ci		dev_consume_skb_any(buf->skb);
51362306a36Sopenharmony_ci		buf->skb = NULL;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci		/* make sure everything is cleaned up */
51662306a36Sopenharmony_ci		wmb();
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci		buf->is_used = false;
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_cistatic int prestera_sdma_tx_init(struct prestera_sdma *sdma)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	struct prestera_sdma_buf *head, *tail, *next, *prev;
52562306a36Sopenharmony_ci	struct prestera_tx_ring *tx_ring = &sdma->tx_ring;
52662306a36Sopenharmony_ci	int bnum = PRESTERA_SDMA_TX_DESC_PER_Q;
52762306a36Sopenharmony_ci	int err;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	INIT_WORK(&sdma->tx_work, prestera_sdma_tx_recycle_work_fn);
53062306a36Sopenharmony_ci	spin_lock_init(&sdma->tx_lock);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	tx_ring->bufs = kmalloc_array(bnum, sizeof(*head), GFP_KERNEL);
53362306a36Sopenharmony_ci	if (!tx_ring->bufs)
53462306a36Sopenharmony_ci		return -ENOMEM;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	tail = &tx_ring->bufs[bnum - 1];
53762306a36Sopenharmony_ci	head = &tx_ring->bufs[0];
53862306a36Sopenharmony_ci	next = head;
53962306a36Sopenharmony_ci	prev = next;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	tx_ring->max_burst = PRESTERA_SDMA_TX_MAX_BURST;
54262306a36Sopenharmony_ci	tx_ring->burst = tx_ring->max_burst;
54362306a36Sopenharmony_ci	tx_ring->next_tx = 0;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	do {
54662306a36Sopenharmony_ci		err = prestera_sdma_buf_init(sdma, next);
54762306a36Sopenharmony_ci		if (err)
54862306a36Sopenharmony_ci			return err;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci		next->is_used = false;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci		prestera_sdma_tx_desc_init(sdma, next->desc);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci		prestera_sdma_tx_desc_set_next(sdma, prev->desc,
55562306a36Sopenharmony_ci					       next->desc_dma);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci		prev = next;
55862306a36Sopenharmony_ci		next++;
55962306a36Sopenharmony_ci	} while (prev != tail);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	/* join tail with head to make a circular list */
56262306a36Sopenharmony_ci	prestera_sdma_tx_desc_set_next(sdma, tail->desc, head->desc_dma);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	/* make sure descriptors are written */
56562306a36Sopenharmony_ci	wmb();
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	prestera_write(sdma->sw, PRESTERA_SDMA_TX_QUEUE_DESC_REG,
56862306a36Sopenharmony_ci		       prestera_sdma_map(sdma, head->desc_dma));
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	return 0;
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistatic void prestera_sdma_tx_fini(struct prestera_sdma *sdma)
57462306a36Sopenharmony_ci{
57562306a36Sopenharmony_ci	struct prestera_tx_ring *ring = &sdma->tx_ring;
57662306a36Sopenharmony_ci	int bnum = PRESTERA_SDMA_TX_DESC_PER_Q;
57762306a36Sopenharmony_ci	int b;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	cancel_work_sync(&sdma->tx_work);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	if (!ring->bufs)
58262306a36Sopenharmony_ci		return;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	for (b = 0; b < bnum; b++) {
58562306a36Sopenharmony_ci		struct prestera_sdma_buf *buf = &ring->bufs[b];
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci		if (buf->desc)
58862306a36Sopenharmony_ci			dma_pool_free(sdma->desc_pool, buf->desc,
58962306a36Sopenharmony_ci				      buf->desc_dma);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci		if (!buf->skb)
59262306a36Sopenharmony_ci			continue;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci		dma_unmap_single(sdma->sw->dev->dev, buf->buf_dma,
59562306a36Sopenharmony_ci				 buf->skb->len, DMA_TO_DEVICE);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci		dev_consume_skb_any(buf->skb);
59862306a36Sopenharmony_ci	}
59962306a36Sopenharmony_ci}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_cistatic void prestera_rxtx_handle_event(struct prestera_switch *sw,
60262306a36Sopenharmony_ci				       struct prestera_event *evt,
60362306a36Sopenharmony_ci				       void *arg)
60462306a36Sopenharmony_ci{
60562306a36Sopenharmony_ci	struct prestera_sdma *sdma = arg;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	if (evt->id != PRESTERA_RXTX_EVENT_RCV_PKT)
60862306a36Sopenharmony_ci		return;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	prestera_write(sdma->sw, PRESTERA_SDMA_RX_INTR_MASK_REG, 0);
61162306a36Sopenharmony_ci	napi_schedule(&sdma->rx_napi);
61262306a36Sopenharmony_ci}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_cistatic int prestera_sdma_switch_init(struct prestera_switch *sw)
61562306a36Sopenharmony_ci{
61662306a36Sopenharmony_ci	struct prestera_sdma *sdma = &sw->rxtx->sdma;
61762306a36Sopenharmony_ci	struct device *dev = sw->dev->dev;
61862306a36Sopenharmony_ci	struct prestera_rxtx_params p;
61962306a36Sopenharmony_ci	int err;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	p.use_sdma = true;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	err = prestera_hw_rxtx_init(sw, &p);
62462306a36Sopenharmony_ci	if (err) {
62562306a36Sopenharmony_ci		dev_err(dev, "failed to init rxtx by hw\n");
62662306a36Sopenharmony_ci		return err;
62762306a36Sopenharmony_ci	}
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	sdma->dma_mask = dma_get_mask(dev);
63062306a36Sopenharmony_ci	sdma->map_addr = p.map_addr;
63162306a36Sopenharmony_ci	sdma->sw = sw;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	sdma->desc_pool = dma_pool_create("desc_pool", dev,
63462306a36Sopenharmony_ci					  sizeof(struct prestera_sdma_desc),
63562306a36Sopenharmony_ci					  16, 0);
63662306a36Sopenharmony_ci	if (!sdma->desc_pool)
63762306a36Sopenharmony_ci		return -ENOMEM;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	err = prestera_sdma_rx_init(sdma);
64062306a36Sopenharmony_ci	if (err) {
64162306a36Sopenharmony_ci		dev_err(dev, "failed to init rx ring\n");
64262306a36Sopenharmony_ci		goto err_rx_init;
64362306a36Sopenharmony_ci	}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	err = prestera_sdma_tx_init(sdma);
64662306a36Sopenharmony_ci	if (err) {
64762306a36Sopenharmony_ci		dev_err(dev, "failed to init tx ring\n");
64862306a36Sopenharmony_ci		goto err_tx_init;
64962306a36Sopenharmony_ci	}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	err = prestera_hw_event_handler_register(sw, PRESTERA_EVENT_TYPE_RXTX,
65262306a36Sopenharmony_ci						 prestera_rxtx_handle_event,
65362306a36Sopenharmony_ci						 sdma);
65462306a36Sopenharmony_ci	if (err)
65562306a36Sopenharmony_ci		goto err_evt_register;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	init_dummy_netdev(&sdma->napi_dev);
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	netif_napi_add(&sdma->napi_dev, &sdma->rx_napi, prestera_sdma_rx_poll);
66062306a36Sopenharmony_ci	napi_enable(&sdma->rx_napi);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	return 0;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_cierr_evt_register:
66562306a36Sopenharmony_cierr_tx_init:
66662306a36Sopenharmony_ci	prestera_sdma_tx_fini(sdma);
66762306a36Sopenharmony_cierr_rx_init:
66862306a36Sopenharmony_ci	prestera_sdma_rx_fini(sdma);
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	dma_pool_destroy(sdma->desc_pool);
67162306a36Sopenharmony_ci	return err;
67262306a36Sopenharmony_ci}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_cistatic void prestera_sdma_switch_fini(struct prestera_switch *sw)
67562306a36Sopenharmony_ci{
67662306a36Sopenharmony_ci	struct prestera_sdma *sdma = &sw->rxtx->sdma;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	napi_disable(&sdma->rx_napi);
67962306a36Sopenharmony_ci	netif_napi_del(&sdma->rx_napi);
68062306a36Sopenharmony_ci	prestera_hw_event_handler_unregister(sw, PRESTERA_EVENT_TYPE_RXTX,
68162306a36Sopenharmony_ci					     prestera_rxtx_handle_event);
68262306a36Sopenharmony_ci	prestera_sdma_tx_fini(sdma);
68362306a36Sopenharmony_ci	prestera_sdma_rx_fini(sdma);
68462306a36Sopenharmony_ci	dma_pool_destroy(sdma->desc_pool);
68562306a36Sopenharmony_ci}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_cistatic bool prestera_sdma_is_ready(struct prestera_sdma *sdma)
68862306a36Sopenharmony_ci{
68962306a36Sopenharmony_ci	return !(prestera_read(sdma->sw, PRESTERA_SDMA_TX_QUEUE_START_REG) & 1);
69062306a36Sopenharmony_ci}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_cistatic int prestera_sdma_tx_wait(struct prestera_sdma *sdma,
69362306a36Sopenharmony_ci				 struct prestera_tx_ring *tx_ring)
69462306a36Sopenharmony_ci{
69562306a36Sopenharmony_ci	int tx_wait_num = PRESTERA_SDMA_WAIT_MUL * tx_ring->max_burst;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	do {
69862306a36Sopenharmony_ci		if (prestera_sdma_is_ready(sdma))
69962306a36Sopenharmony_ci			return 0;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci		udelay(1);
70262306a36Sopenharmony_ci	} while (--tx_wait_num);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	return -EBUSY;
70562306a36Sopenharmony_ci}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cistatic void prestera_sdma_tx_start(struct prestera_sdma *sdma)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	prestera_write(sdma->sw, PRESTERA_SDMA_TX_QUEUE_START_REG, 1);
71062306a36Sopenharmony_ci	schedule_work(&sdma->tx_work);
71162306a36Sopenharmony_ci}
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_cistatic netdev_tx_t prestera_sdma_xmit(struct prestera_sdma *sdma,
71462306a36Sopenharmony_ci				      struct sk_buff *skb)
71562306a36Sopenharmony_ci{
71662306a36Sopenharmony_ci	struct device *dma_dev = sdma->sw->dev->dev;
71762306a36Sopenharmony_ci	struct net_device *dev = skb->dev;
71862306a36Sopenharmony_ci	struct prestera_tx_ring *tx_ring;
71962306a36Sopenharmony_ci	struct prestera_sdma_buf *buf;
72062306a36Sopenharmony_ci	int err;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	spin_lock(&sdma->tx_lock);
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	tx_ring = &sdma->tx_ring;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	buf = &tx_ring->bufs[tx_ring->next_tx];
72762306a36Sopenharmony_ci	if (buf->is_used) {
72862306a36Sopenharmony_ci		schedule_work(&sdma->tx_work);
72962306a36Sopenharmony_ci		goto drop_skb;
73062306a36Sopenharmony_ci	}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	if (unlikely(eth_skb_pad(skb)))
73362306a36Sopenharmony_ci		goto drop_skb_nofree;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	err = prestera_sdma_tx_buf_map(sdma, buf, skb);
73662306a36Sopenharmony_ci	if (err)
73762306a36Sopenharmony_ci		goto drop_skb;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	prestera_sdma_tx_desc_set_buf(sdma, buf->desc, buf->buf_dma, skb->len);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	dma_sync_single_for_device(dma_dev, buf->buf_dma, skb->len,
74262306a36Sopenharmony_ci				   DMA_TO_DEVICE);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	if (tx_ring->burst) {
74562306a36Sopenharmony_ci		tx_ring->burst--;
74662306a36Sopenharmony_ci	} else {
74762306a36Sopenharmony_ci		tx_ring->burst = tx_ring->max_burst;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci		err = prestera_sdma_tx_wait(sdma, tx_ring);
75062306a36Sopenharmony_ci		if (err)
75162306a36Sopenharmony_ci			goto drop_skb_unmap;
75262306a36Sopenharmony_ci	}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	tx_ring->next_tx = (tx_ring->next_tx + 1) % PRESTERA_SDMA_TX_DESC_PER_Q;
75562306a36Sopenharmony_ci	prestera_sdma_tx_desc_xmit(buf->desc);
75662306a36Sopenharmony_ci	buf->is_used = true;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	prestera_sdma_tx_start(sdma);
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	goto tx_done;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_cidrop_skb_unmap:
76362306a36Sopenharmony_ci	prestera_sdma_tx_buf_unmap(sdma, buf);
76462306a36Sopenharmony_cidrop_skb:
76562306a36Sopenharmony_ci	dev_consume_skb_any(skb);
76662306a36Sopenharmony_cidrop_skb_nofree:
76762306a36Sopenharmony_ci	dev->stats.tx_dropped++;
76862306a36Sopenharmony_citx_done:
76962306a36Sopenharmony_ci	spin_unlock(&sdma->tx_lock);
77062306a36Sopenharmony_ci	return NETDEV_TX_OK;
77162306a36Sopenharmony_ci}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ciint prestera_rxtx_switch_init(struct prestera_switch *sw)
77462306a36Sopenharmony_ci{
77562306a36Sopenharmony_ci	struct prestera_rxtx *rxtx;
77662306a36Sopenharmony_ci	int err;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	rxtx = kzalloc(sizeof(*rxtx), GFP_KERNEL);
77962306a36Sopenharmony_ci	if (!rxtx)
78062306a36Sopenharmony_ci		return -ENOMEM;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	sw->rxtx = rxtx;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	err = prestera_sdma_switch_init(sw);
78562306a36Sopenharmony_ci	if (err)
78662306a36Sopenharmony_ci		kfree(rxtx);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	return err;
78962306a36Sopenharmony_ci}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_civoid prestera_rxtx_switch_fini(struct prestera_switch *sw)
79262306a36Sopenharmony_ci{
79362306a36Sopenharmony_ci	prestera_sdma_switch_fini(sw);
79462306a36Sopenharmony_ci	kfree(sw->rxtx);
79562306a36Sopenharmony_ci}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ciint prestera_rxtx_port_init(struct prestera_port *port)
79862306a36Sopenharmony_ci{
79962306a36Sopenharmony_ci	port->dev->needed_headroom = PRESTERA_DSA_HLEN;
80062306a36Sopenharmony_ci	return 0;
80162306a36Sopenharmony_ci}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_cinetdev_tx_t prestera_rxtx_xmit(struct prestera_port *port, struct sk_buff *skb)
80462306a36Sopenharmony_ci{
80562306a36Sopenharmony_ci	struct prestera_dsa dsa;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	dsa.hw_dev_num = port->dev_id;
80862306a36Sopenharmony_ci	dsa.port_num = port->hw_id;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	if (skb_cow_head(skb, PRESTERA_DSA_HLEN) < 0)
81162306a36Sopenharmony_ci		return NET_XMIT_DROP;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	skb_push(skb, PRESTERA_DSA_HLEN);
81462306a36Sopenharmony_ci	memmove(skb->data, skb->data + PRESTERA_DSA_HLEN, 2 * ETH_ALEN);
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	if (prestera_dsa_build(&dsa, skb->data + 2 * ETH_ALEN) != 0)
81762306a36Sopenharmony_ci		return NET_XMIT_DROP;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	return prestera_sdma_xmit(&port->sw->rxtx->sdma, skb);
82062306a36Sopenharmony_ci}
821