18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/bitfield.h>
58c2ecf20Sopenharmony_ci#include <linux/dmapool.h>
68c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
78c2ecf20Sopenharmony_ci#include <linux/if_vlan.h>
88c2ecf20Sopenharmony_ci#include <linux/of_address.h>
98c2ecf20Sopenharmony_ci#include <linux/of_device.h>
108c2ecf20Sopenharmony_ci#include <linux/of.h>
118c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "prestera_dsa.h"
148c2ecf20Sopenharmony_ci#include "prestera.h"
158c2ecf20Sopenharmony_ci#include "prestera_hw.h"
168c2ecf20Sopenharmony_ci#include "prestera_rxtx.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define PRESTERA_SDMA_WAIT_MUL		10
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistruct prestera_sdma_desc {
218c2ecf20Sopenharmony_ci	__le32 word1;
228c2ecf20Sopenharmony_ci	__le32 word2;
238c2ecf20Sopenharmony_ci	__le32 buff;
248c2ecf20Sopenharmony_ci	__le32 next;
258c2ecf20Sopenharmony_ci} __packed __aligned(16);
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define PRESTERA_SDMA_BUFF_SIZE_MAX	1544
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define PRESTERA_SDMA_RX_DESC_PKT_LEN(desc) \
308c2ecf20Sopenharmony_ci	((le32_to_cpu((desc)->word2) >> 16) & GENMASK(13, 0))
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define PRESTERA_SDMA_RX_DESC_OWNER(desc) \
338c2ecf20Sopenharmony_ci	((le32_to_cpu((desc)->word1) & BIT(31)) >> 31)
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define PRESTERA_SDMA_RX_DESC_IS_RCVD(desc) \
368c2ecf20Sopenharmony_ci	(PRESTERA_SDMA_RX_DESC_OWNER(desc) == PRESTERA_SDMA_RX_DESC_CPU_OWN)
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define PRESTERA_SDMA_RX_DESC_CPU_OWN	0
398c2ecf20Sopenharmony_ci#define PRESTERA_SDMA_RX_DESC_DMA_OWN	1
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define PRESTERA_SDMA_RX_QUEUE_NUM	8
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define PRESTERA_SDMA_RX_DESC_PER_Q	1000
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_PER_Q	1000
468c2ecf20Sopenharmony_ci#define PRESTERA_SDMA_TX_MAX_BURST	64
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_OWNER(desc) \
498c2ecf20Sopenharmony_ci	((le32_to_cpu((desc)->word1) & BIT(31)) >> 31)
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_CPU_OWN	0
528c2ecf20Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_DMA_OWN	1U
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_IS_SENT(desc) \
558c2ecf20Sopenharmony_ci	(PRESTERA_SDMA_TX_DESC_OWNER(desc) == PRESTERA_SDMA_TX_DESC_CPU_OWN)
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_LAST	BIT(20)
588c2ecf20Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_FIRST	BIT(21)
598c2ecf20Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_CALC_CRC	BIT(12)
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_SINGLE	\
628c2ecf20Sopenharmony_ci	(PRESTERA_SDMA_TX_DESC_FIRST | PRESTERA_SDMA_TX_DESC_LAST)
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#define PRESTERA_SDMA_TX_DESC_INIT	\
658c2ecf20Sopenharmony_ci	(PRESTERA_SDMA_TX_DESC_SINGLE | PRESTERA_SDMA_TX_DESC_CALC_CRC)
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci#define PRESTERA_SDMA_RX_INTR_MASK_REG		0x2814
688c2ecf20Sopenharmony_ci#define PRESTERA_SDMA_RX_QUEUE_STATUS_REG	0x2680
698c2ecf20Sopenharmony_ci#define PRESTERA_SDMA_RX_QUEUE_DESC_REG(n)	(0x260C + (n) * 16)
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci#define PRESTERA_SDMA_TX_QUEUE_DESC_REG		0x26C0
728c2ecf20Sopenharmony_ci#define PRESTERA_SDMA_TX_QUEUE_START_REG	0x2868
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistruct prestera_sdma_buf {
758c2ecf20Sopenharmony_ci	struct prestera_sdma_desc *desc;
768c2ecf20Sopenharmony_ci	dma_addr_t desc_dma;
778c2ecf20Sopenharmony_ci	struct sk_buff *skb;
788c2ecf20Sopenharmony_ci	dma_addr_t buf_dma;
798c2ecf20Sopenharmony_ci	bool is_used;
808c2ecf20Sopenharmony_ci};
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistruct prestera_rx_ring {
838c2ecf20Sopenharmony_ci	struct prestera_sdma_buf *bufs;
848c2ecf20Sopenharmony_ci	int next_rx;
858c2ecf20Sopenharmony_ci};
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistruct prestera_tx_ring {
888c2ecf20Sopenharmony_ci	struct prestera_sdma_buf *bufs;
898c2ecf20Sopenharmony_ci	int next_tx;
908c2ecf20Sopenharmony_ci	int max_burst;
918c2ecf20Sopenharmony_ci	int burst;
928c2ecf20Sopenharmony_ci};
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistruct prestera_sdma {
958c2ecf20Sopenharmony_ci	struct prestera_rx_ring rx_ring[PRESTERA_SDMA_RX_QUEUE_NUM];
968c2ecf20Sopenharmony_ci	struct prestera_tx_ring tx_ring;
978c2ecf20Sopenharmony_ci	struct prestera_switch *sw;
988c2ecf20Sopenharmony_ci	struct dma_pool *desc_pool;
998c2ecf20Sopenharmony_ci	struct work_struct tx_work;
1008c2ecf20Sopenharmony_ci	struct napi_struct rx_napi;
1018c2ecf20Sopenharmony_ci	struct net_device napi_dev;
1028c2ecf20Sopenharmony_ci	u32 map_addr;
1038c2ecf20Sopenharmony_ci	u64 dma_mask;
1048c2ecf20Sopenharmony_ci	/* protect SDMA with concurrrent access from multiple CPUs */
1058c2ecf20Sopenharmony_ci	spinlock_t tx_lock;
1068c2ecf20Sopenharmony_ci};
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistruct prestera_rxtx {
1098c2ecf20Sopenharmony_ci	struct prestera_sdma sdma;
1108c2ecf20Sopenharmony_ci};
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic int prestera_sdma_buf_init(struct prestera_sdma *sdma,
1138c2ecf20Sopenharmony_ci				  struct prestera_sdma_buf *buf)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct prestera_sdma_desc *desc;
1168c2ecf20Sopenharmony_ci	dma_addr_t dma;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	desc = dma_pool_alloc(sdma->desc_pool, GFP_DMA | GFP_KERNEL, &dma);
1198c2ecf20Sopenharmony_ci	if (!desc)
1208c2ecf20Sopenharmony_ci		return -ENOMEM;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	buf->buf_dma = DMA_MAPPING_ERROR;
1238c2ecf20Sopenharmony_ci	buf->desc_dma = dma;
1248c2ecf20Sopenharmony_ci	buf->desc = desc;
1258c2ecf20Sopenharmony_ci	buf->skb = NULL;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	return 0;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic u32 prestera_sdma_map(struct prestera_sdma *sdma, dma_addr_t pa)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	return sdma->map_addr + pa;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic void prestera_sdma_rx_desc_init(struct prestera_sdma *sdma,
1368c2ecf20Sopenharmony_ci				       struct prestera_sdma_desc *desc,
1378c2ecf20Sopenharmony_ci				       dma_addr_t buf)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	u32 word = le32_to_cpu(desc->word2);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	u32p_replace_bits(&word, PRESTERA_SDMA_BUFF_SIZE_MAX, GENMASK(15, 0));
1428c2ecf20Sopenharmony_ci	desc->word2 = cpu_to_le32(word);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	desc->buff = cpu_to_le32(prestera_sdma_map(sdma, buf));
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	/* make sure buffer is set before reset the descriptor */
1478c2ecf20Sopenharmony_ci	wmb();
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	desc->word1 = cpu_to_le32(0xA0000000);
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic void prestera_sdma_rx_desc_set_next(struct prestera_sdma *sdma,
1538c2ecf20Sopenharmony_ci					   struct prestera_sdma_desc *desc,
1548c2ecf20Sopenharmony_ci					   dma_addr_t next)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	desc->next = cpu_to_le32(prestera_sdma_map(sdma, next));
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic int prestera_sdma_rx_skb_alloc(struct prestera_sdma *sdma,
1608c2ecf20Sopenharmony_ci				      struct prestera_sdma_buf *buf)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	struct device *dev = sdma->sw->dev->dev;
1638c2ecf20Sopenharmony_ci	struct sk_buff *skb;
1648c2ecf20Sopenharmony_ci	dma_addr_t dma;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	skb = alloc_skb(PRESTERA_SDMA_BUFF_SIZE_MAX, GFP_DMA | GFP_ATOMIC);
1678c2ecf20Sopenharmony_ci	if (!skb)
1688c2ecf20Sopenharmony_ci		return -ENOMEM;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	dma = dma_map_single(dev, skb->data, skb->len, DMA_FROM_DEVICE);
1718c2ecf20Sopenharmony_ci	if (dma_mapping_error(dev, dma))
1728c2ecf20Sopenharmony_ci		goto err_dma_map;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (buf->skb)
1758c2ecf20Sopenharmony_ci		dma_unmap_single(dev, buf->buf_dma, buf->skb->len,
1768c2ecf20Sopenharmony_ci				 DMA_FROM_DEVICE);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	buf->buf_dma = dma;
1798c2ecf20Sopenharmony_ci	buf->skb = skb;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	return 0;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cierr_dma_map:
1848c2ecf20Sopenharmony_ci	kfree_skb(skb);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	return -ENOMEM;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic struct sk_buff *prestera_sdma_rx_skb_get(struct prestera_sdma *sdma,
1908c2ecf20Sopenharmony_ci						struct prestera_sdma_buf *buf)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	dma_addr_t buf_dma = buf->buf_dma;
1938c2ecf20Sopenharmony_ci	struct sk_buff *skb = buf->skb;
1948c2ecf20Sopenharmony_ci	u32 len = skb->len;
1958c2ecf20Sopenharmony_ci	int err;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	err = prestera_sdma_rx_skb_alloc(sdma, buf);
1988c2ecf20Sopenharmony_ci	if (err) {
1998c2ecf20Sopenharmony_ci		buf->buf_dma = buf_dma;
2008c2ecf20Sopenharmony_ci		buf->skb = skb;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci		skb = alloc_skb(skb->len, GFP_ATOMIC);
2038c2ecf20Sopenharmony_ci		if (skb) {
2048c2ecf20Sopenharmony_ci			skb_put(skb, len);
2058c2ecf20Sopenharmony_ci			skb_copy_from_linear_data(buf->skb, skb->data, len);
2068c2ecf20Sopenharmony_ci		}
2078c2ecf20Sopenharmony_ci	}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	prestera_sdma_rx_desc_init(sdma, buf->desc, buf->buf_dma);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	return skb;
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic int prestera_rxtx_process_skb(struct prestera_sdma *sdma,
2158c2ecf20Sopenharmony_ci				     struct sk_buff *skb)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	const struct prestera_port *port;
2188c2ecf20Sopenharmony_ci	struct prestera_dsa dsa;
2198c2ecf20Sopenharmony_ci	u32 hw_port, dev_id;
2208c2ecf20Sopenharmony_ci	int err;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	skb_pull(skb, ETH_HLEN);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	/* ethertype field is part of the dsa header */
2258c2ecf20Sopenharmony_ci	err = prestera_dsa_parse(&dsa, skb->data - ETH_TLEN);
2268c2ecf20Sopenharmony_ci	if (err)
2278c2ecf20Sopenharmony_ci		return err;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	dev_id = dsa.hw_dev_num;
2308c2ecf20Sopenharmony_ci	hw_port = dsa.port_num;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	port = prestera_port_find_by_hwid(sdma->sw, dev_id, hw_port);
2338c2ecf20Sopenharmony_ci	if (unlikely(!port)) {
2348c2ecf20Sopenharmony_ci		dev_warn_ratelimited(prestera_dev(sdma->sw), "received pkt for non-existent port(%u, %u)\n",
2358c2ecf20Sopenharmony_ci				     dev_id, hw_port);
2368c2ecf20Sopenharmony_ci		return -ENOENT;
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	if (unlikely(!pskb_may_pull(skb, PRESTERA_DSA_HLEN)))
2408c2ecf20Sopenharmony_ci		return -EINVAL;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	/* remove DSA tag and update checksum */
2438c2ecf20Sopenharmony_ci	skb_pull_rcsum(skb, PRESTERA_DSA_HLEN);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	memmove(skb->data - ETH_HLEN, skb->data - ETH_HLEN - PRESTERA_DSA_HLEN,
2468c2ecf20Sopenharmony_ci		ETH_ALEN * 2);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	skb_push(skb, ETH_HLEN);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	skb->protocol = eth_type_trans(skb, port->dev);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	if (dsa.vlan.is_tagged) {
2538c2ecf20Sopenharmony_ci		u16 tci = dsa.vlan.vid & VLAN_VID_MASK;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci		tci |= dsa.vlan.vpt << VLAN_PRIO_SHIFT;
2568c2ecf20Sopenharmony_ci		if (dsa.vlan.cfi_bit)
2578c2ecf20Sopenharmony_ci			tci |= VLAN_CFI_MASK;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), tci);
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	return 0;
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic int prestera_sdma_next_rx_buf_idx(int buf_idx)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	return (buf_idx + 1) % PRESTERA_SDMA_RX_DESC_PER_Q;
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic int prestera_sdma_rx_poll(struct napi_struct *napi, int budget)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	int qnum = PRESTERA_SDMA_RX_QUEUE_NUM;
2738c2ecf20Sopenharmony_ci	unsigned int rxq_done_map = 0;
2748c2ecf20Sopenharmony_ci	struct prestera_sdma *sdma;
2758c2ecf20Sopenharmony_ci	struct list_head rx_list;
2768c2ecf20Sopenharmony_ci	unsigned int qmask;
2778c2ecf20Sopenharmony_ci	int pkts_done = 0;
2788c2ecf20Sopenharmony_ci	int q;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	qnum = PRESTERA_SDMA_RX_QUEUE_NUM;
2818c2ecf20Sopenharmony_ci	qmask = GENMASK(qnum - 1, 0);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&rx_list);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	sdma = container_of(napi, struct prestera_sdma, rx_napi);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	while (pkts_done < budget && rxq_done_map != qmask) {
2888c2ecf20Sopenharmony_ci		for (q = 0; q < qnum && pkts_done < budget; q++) {
2898c2ecf20Sopenharmony_ci			struct prestera_rx_ring *ring = &sdma->rx_ring[q];
2908c2ecf20Sopenharmony_ci			struct prestera_sdma_desc *desc;
2918c2ecf20Sopenharmony_ci			struct prestera_sdma_buf *buf;
2928c2ecf20Sopenharmony_ci			int buf_idx = ring->next_rx;
2938c2ecf20Sopenharmony_ci			struct sk_buff *skb;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci			buf = &ring->bufs[buf_idx];
2968c2ecf20Sopenharmony_ci			desc = buf->desc;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci			if (PRESTERA_SDMA_RX_DESC_IS_RCVD(desc)) {
2998c2ecf20Sopenharmony_ci				rxq_done_map &= ~BIT(q);
3008c2ecf20Sopenharmony_ci			} else {
3018c2ecf20Sopenharmony_ci				rxq_done_map |= BIT(q);
3028c2ecf20Sopenharmony_ci				continue;
3038c2ecf20Sopenharmony_ci			}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci			pkts_done++;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci			__skb_trim(buf->skb, PRESTERA_SDMA_RX_DESC_PKT_LEN(desc));
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci			skb = prestera_sdma_rx_skb_get(sdma, buf);
3108c2ecf20Sopenharmony_ci			if (!skb)
3118c2ecf20Sopenharmony_ci				goto rx_next_buf;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci			if (unlikely(prestera_rxtx_process_skb(sdma, skb)))
3148c2ecf20Sopenharmony_ci				goto rx_next_buf;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci			list_add_tail(&skb->list, &rx_list);
3178c2ecf20Sopenharmony_cirx_next_buf:
3188c2ecf20Sopenharmony_ci			ring->next_rx = prestera_sdma_next_rx_buf_idx(buf_idx);
3198c2ecf20Sopenharmony_ci		}
3208c2ecf20Sopenharmony_ci	}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	if (pkts_done < budget && napi_complete_done(napi, pkts_done))
3238c2ecf20Sopenharmony_ci		prestera_write(sdma->sw, PRESTERA_SDMA_RX_INTR_MASK_REG,
3248c2ecf20Sopenharmony_ci			       GENMASK(9, 2));
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	netif_receive_skb_list(&rx_list);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	return pkts_done;
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_cistatic void prestera_sdma_rx_fini(struct prestera_sdma *sdma)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	int qnum = PRESTERA_SDMA_RX_QUEUE_NUM;
3348c2ecf20Sopenharmony_ci	int q, b;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/* disable all rx queues */
3378c2ecf20Sopenharmony_ci	prestera_write(sdma->sw, PRESTERA_SDMA_RX_QUEUE_STATUS_REG,
3388c2ecf20Sopenharmony_ci		       GENMASK(15, 8));
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	for (q = 0; q < qnum; q++) {
3418c2ecf20Sopenharmony_ci		struct prestera_rx_ring *ring = &sdma->rx_ring[q];
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci		if (!ring->bufs)
3448c2ecf20Sopenharmony_ci			break;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci		for (b = 0; b < PRESTERA_SDMA_RX_DESC_PER_Q; b++) {
3478c2ecf20Sopenharmony_ci			struct prestera_sdma_buf *buf = &ring->bufs[b];
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci			if (buf->desc_dma)
3508c2ecf20Sopenharmony_ci				dma_pool_free(sdma->desc_pool, buf->desc,
3518c2ecf20Sopenharmony_ci					      buf->desc_dma);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci			if (!buf->skb)
3548c2ecf20Sopenharmony_ci				continue;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci			if (buf->buf_dma != DMA_MAPPING_ERROR)
3578c2ecf20Sopenharmony_ci				dma_unmap_single(sdma->sw->dev->dev,
3588c2ecf20Sopenharmony_ci						 buf->buf_dma, buf->skb->len,
3598c2ecf20Sopenharmony_ci						 DMA_FROM_DEVICE);
3608c2ecf20Sopenharmony_ci			kfree_skb(buf->skb);
3618c2ecf20Sopenharmony_ci		}
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_cistatic int prestera_sdma_rx_init(struct prestera_sdma *sdma)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	int bnum = PRESTERA_SDMA_RX_DESC_PER_Q;
3688c2ecf20Sopenharmony_ci	int qnum = PRESTERA_SDMA_RX_QUEUE_NUM;
3698c2ecf20Sopenharmony_ci	int err;
3708c2ecf20Sopenharmony_ci	int q;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	/* disable all rx queues */
3738c2ecf20Sopenharmony_ci	prestera_write(sdma->sw, PRESTERA_SDMA_RX_QUEUE_STATUS_REG,
3748c2ecf20Sopenharmony_ci		       GENMASK(15, 8));
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	for (q = 0; q < qnum; q++) {
3778c2ecf20Sopenharmony_ci		struct prestera_sdma_buf *head, *tail, *next, *prev;
3788c2ecf20Sopenharmony_ci		struct prestera_rx_ring *ring = &sdma->rx_ring[q];
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci		ring->bufs = kmalloc_array(bnum, sizeof(*head), GFP_KERNEL);
3818c2ecf20Sopenharmony_ci		if (!ring->bufs)
3828c2ecf20Sopenharmony_ci			return -ENOMEM;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci		ring->next_rx = 0;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci		tail = &ring->bufs[bnum - 1];
3878c2ecf20Sopenharmony_ci		head = &ring->bufs[0];
3888c2ecf20Sopenharmony_ci		next = head;
3898c2ecf20Sopenharmony_ci		prev = next;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci		do {
3928c2ecf20Sopenharmony_ci			err = prestera_sdma_buf_init(sdma, next);
3938c2ecf20Sopenharmony_ci			if (err)
3948c2ecf20Sopenharmony_ci				return err;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci			err = prestera_sdma_rx_skb_alloc(sdma, next);
3978c2ecf20Sopenharmony_ci			if (err)
3988c2ecf20Sopenharmony_ci				return err;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci			prestera_sdma_rx_desc_init(sdma, next->desc,
4018c2ecf20Sopenharmony_ci						   next->buf_dma);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci			prestera_sdma_rx_desc_set_next(sdma, prev->desc,
4048c2ecf20Sopenharmony_ci						       next->desc_dma);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci			prev = next;
4078c2ecf20Sopenharmony_ci			next++;
4088c2ecf20Sopenharmony_ci		} while (prev != tail);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci		/* join tail with head to make a circular list */
4118c2ecf20Sopenharmony_ci		prestera_sdma_rx_desc_set_next(sdma, tail->desc, head->desc_dma);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci		prestera_write(sdma->sw, PRESTERA_SDMA_RX_QUEUE_DESC_REG(q),
4148c2ecf20Sopenharmony_ci			       prestera_sdma_map(sdma, head->desc_dma));
4158c2ecf20Sopenharmony_ci	}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	/* make sure all rx descs are filled before enabling all rx queues */
4188c2ecf20Sopenharmony_ci	wmb();
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	prestera_write(sdma->sw, PRESTERA_SDMA_RX_QUEUE_STATUS_REG,
4218c2ecf20Sopenharmony_ci		       GENMASK(7, 0));
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	return 0;
4248c2ecf20Sopenharmony_ci}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_cistatic void prestera_sdma_tx_desc_init(struct prestera_sdma *sdma,
4278c2ecf20Sopenharmony_ci				       struct prestera_sdma_desc *desc)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	desc->word1 = cpu_to_le32(PRESTERA_SDMA_TX_DESC_INIT);
4308c2ecf20Sopenharmony_ci	desc->word2 = 0;
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_cistatic void prestera_sdma_tx_desc_set_next(struct prestera_sdma *sdma,
4348c2ecf20Sopenharmony_ci					   struct prestera_sdma_desc *desc,
4358c2ecf20Sopenharmony_ci					   dma_addr_t next)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	desc->next = cpu_to_le32(prestera_sdma_map(sdma, next));
4388c2ecf20Sopenharmony_ci}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_cistatic void prestera_sdma_tx_desc_set_buf(struct prestera_sdma *sdma,
4418c2ecf20Sopenharmony_ci					  struct prestera_sdma_desc *desc,
4428c2ecf20Sopenharmony_ci					  dma_addr_t buf, size_t len)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	u32 word = le32_to_cpu(desc->word2);
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	u32p_replace_bits(&word, len + ETH_FCS_LEN, GENMASK(30, 16));
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	desc->buff = cpu_to_le32(prestera_sdma_map(sdma, buf));
4498c2ecf20Sopenharmony_ci	desc->word2 = cpu_to_le32(word);
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_cistatic void prestera_sdma_tx_desc_xmit(struct prestera_sdma_desc *desc)
4538c2ecf20Sopenharmony_ci{
4548c2ecf20Sopenharmony_ci	u32 word = le32_to_cpu(desc->word1);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	word |= PRESTERA_SDMA_TX_DESC_DMA_OWN << 31;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	/* make sure everything is written before enable xmit */
4598c2ecf20Sopenharmony_ci	wmb();
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	desc->word1 = cpu_to_le32(word);
4628c2ecf20Sopenharmony_ci}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_cistatic int prestera_sdma_tx_buf_map(struct prestera_sdma *sdma,
4658c2ecf20Sopenharmony_ci				    struct prestera_sdma_buf *buf,
4668c2ecf20Sopenharmony_ci				    struct sk_buff *skb)
4678c2ecf20Sopenharmony_ci{
4688c2ecf20Sopenharmony_ci	struct device *dma_dev = sdma->sw->dev->dev;
4698c2ecf20Sopenharmony_ci	dma_addr_t dma;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	dma = dma_map_single(dma_dev, skb->data, skb->len, DMA_TO_DEVICE);
4728c2ecf20Sopenharmony_ci	if (dma_mapping_error(dma_dev, dma))
4738c2ecf20Sopenharmony_ci		return -ENOMEM;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	buf->buf_dma = dma;
4768c2ecf20Sopenharmony_ci	buf->skb = skb;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	return 0;
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_cistatic void prestera_sdma_tx_buf_unmap(struct prestera_sdma *sdma,
4828c2ecf20Sopenharmony_ci				       struct prestera_sdma_buf *buf)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	struct device *dma_dev = sdma->sw->dev->dev;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	dma_unmap_single(dma_dev, buf->buf_dma, buf->skb->len, DMA_TO_DEVICE);
4878c2ecf20Sopenharmony_ci}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_cistatic void prestera_sdma_tx_recycle_work_fn(struct work_struct *work)
4908c2ecf20Sopenharmony_ci{
4918c2ecf20Sopenharmony_ci	int bnum = PRESTERA_SDMA_TX_DESC_PER_Q;
4928c2ecf20Sopenharmony_ci	struct prestera_tx_ring *tx_ring;
4938c2ecf20Sopenharmony_ci	struct prestera_sdma *sdma;
4948c2ecf20Sopenharmony_ci	int b;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	sdma = container_of(work, struct prestera_sdma, tx_work);
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	tx_ring = &sdma->tx_ring;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	for (b = 0; b < bnum; b++) {
5018c2ecf20Sopenharmony_ci		struct prestera_sdma_buf *buf = &tx_ring->bufs[b];
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci		if (!buf->is_used)
5048c2ecf20Sopenharmony_ci			continue;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci		if (!PRESTERA_SDMA_TX_DESC_IS_SENT(buf->desc))
5078c2ecf20Sopenharmony_ci			continue;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci		prestera_sdma_tx_buf_unmap(sdma, buf);
5108c2ecf20Sopenharmony_ci		dev_consume_skb_any(buf->skb);
5118c2ecf20Sopenharmony_ci		buf->skb = NULL;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci		/* make sure everything is cleaned up */
5148c2ecf20Sopenharmony_ci		wmb();
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci		buf->is_used = false;
5178c2ecf20Sopenharmony_ci	}
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_cistatic int prestera_sdma_tx_init(struct prestera_sdma *sdma)
5218c2ecf20Sopenharmony_ci{
5228c2ecf20Sopenharmony_ci	struct prestera_sdma_buf *head, *tail, *next, *prev;
5238c2ecf20Sopenharmony_ci	struct prestera_tx_ring *tx_ring = &sdma->tx_ring;
5248c2ecf20Sopenharmony_ci	int bnum = PRESTERA_SDMA_TX_DESC_PER_Q;
5258c2ecf20Sopenharmony_ci	int err;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	INIT_WORK(&sdma->tx_work, prestera_sdma_tx_recycle_work_fn);
5288c2ecf20Sopenharmony_ci	spin_lock_init(&sdma->tx_lock);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	tx_ring->bufs = kmalloc_array(bnum, sizeof(*head), GFP_KERNEL);
5318c2ecf20Sopenharmony_ci	if (!tx_ring->bufs)
5328c2ecf20Sopenharmony_ci		return -ENOMEM;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	tail = &tx_ring->bufs[bnum - 1];
5358c2ecf20Sopenharmony_ci	head = &tx_ring->bufs[0];
5368c2ecf20Sopenharmony_ci	next = head;
5378c2ecf20Sopenharmony_ci	prev = next;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	tx_ring->max_burst = PRESTERA_SDMA_TX_MAX_BURST;
5408c2ecf20Sopenharmony_ci	tx_ring->burst = tx_ring->max_burst;
5418c2ecf20Sopenharmony_ci	tx_ring->next_tx = 0;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	do {
5448c2ecf20Sopenharmony_ci		err = prestera_sdma_buf_init(sdma, next);
5458c2ecf20Sopenharmony_ci		if (err)
5468c2ecf20Sopenharmony_ci			return err;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci		next->is_used = false;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci		prestera_sdma_tx_desc_init(sdma, next->desc);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci		prestera_sdma_tx_desc_set_next(sdma, prev->desc,
5538c2ecf20Sopenharmony_ci					       next->desc_dma);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci		prev = next;
5568c2ecf20Sopenharmony_ci		next++;
5578c2ecf20Sopenharmony_ci	} while (prev != tail);
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	/* join tail with head to make a circular list */
5608c2ecf20Sopenharmony_ci	prestera_sdma_tx_desc_set_next(sdma, tail->desc, head->desc_dma);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	/* make sure descriptors are written */
5638c2ecf20Sopenharmony_ci	wmb();
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	prestera_write(sdma->sw, PRESTERA_SDMA_TX_QUEUE_DESC_REG,
5668c2ecf20Sopenharmony_ci		       prestera_sdma_map(sdma, head->desc_dma));
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	return 0;
5698c2ecf20Sopenharmony_ci}
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_cistatic void prestera_sdma_tx_fini(struct prestera_sdma *sdma)
5728c2ecf20Sopenharmony_ci{
5738c2ecf20Sopenharmony_ci	struct prestera_tx_ring *ring = &sdma->tx_ring;
5748c2ecf20Sopenharmony_ci	int bnum = PRESTERA_SDMA_TX_DESC_PER_Q;
5758c2ecf20Sopenharmony_ci	int b;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	cancel_work_sync(&sdma->tx_work);
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	if (!ring->bufs)
5808c2ecf20Sopenharmony_ci		return;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	for (b = 0; b < bnum; b++) {
5838c2ecf20Sopenharmony_ci		struct prestera_sdma_buf *buf = &ring->bufs[b];
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci		if (buf->desc)
5868c2ecf20Sopenharmony_ci			dma_pool_free(sdma->desc_pool, buf->desc,
5878c2ecf20Sopenharmony_ci				      buf->desc_dma);
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci		if (!buf->skb)
5908c2ecf20Sopenharmony_ci			continue;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci		dma_unmap_single(sdma->sw->dev->dev, buf->buf_dma,
5938c2ecf20Sopenharmony_ci				 buf->skb->len, DMA_TO_DEVICE);
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci		dev_consume_skb_any(buf->skb);
5968c2ecf20Sopenharmony_ci	}
5978c2ecf20Sopenharmony_ci}
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_cistatic void prestera_rxtx_handle_event(struct prestera_switch *sw,
6008c2ecf20Sopenharmony_ci				       struct prestera_event *evt,
6018c2ecf20Sopenharmony_ci				       void *arg)
6028c2ecf20Sopenharmony_ci{
6038c2ecf20Sopenharmony_ci	struct prestera_sdma *sdma = arg;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	if (evt->id != PRESTERA_RXTX_EVENT_RCV_PKT)
6068c2ecf20Sopenharmony_ci		return;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	prestera_write(sdma->sw, PRESTERA_SDMA_RX_INTR_MASK_REG, 0);
6098c2ecf20Sopenharmony_ci	napi_schedule(&sdma->rx_napi);
6108c2ecf20Sopenharmony_ci}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_cistatic int prestera_sdma_switch_init(struct prestera_switch *sw)
6138c2ecf20Sopenharmony_ci{
6148c2ecf20Sopenharmony_ci	struct prestera_sdma *sdma = &sw->rxtx->sdma;
6158c2ecf20Sopenharmony_ci	struct device *dev = sw->dev->dev;
6168c2ecf20Sopenharmony_ci	struct prestera_rxtx_params p;
6178c2ecf20Sopenharmony_ci	int err;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	p.use_sdma = true;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	err = prestera_hw_rxtx_init(sw, &p);
6228c2ecf20Sopenharmony_ci	if (err) {
6238c2ecf20Sopenharmony_ci		dev_err(dev, "failed to init rxtx by hw\n");
6248c2ecf20Sopenharmony_ci		return err;
6258c2ecf20Sopenharmony_ci	}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	sdma->dma_mask = dma_get_mask(dev);
6288c2ecf20Sopenharmony_ci	sdma->map_addr = p.map_addr;
6298c2ecf20Sopenharmony_ci	sdma->sw = sw;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	sdma->desc_pool = dma_pool_create("desc_pool", dev,
6328c2ecf20Sopenharmony_ci					  sizeof(struct prestera_sdma_desc),
6338c2ecf20Sopenharmony_ci					  16, 0);
6348c2ecf20Sopenharmony_ci	if (!sdma->desc_pool)
6358c2ecf20Sopenharmony_ci		return -ENOMEM;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	err = prestera_sdma_rx_init(sdma);
6388c2ecf20Sopenharmony_ci	if (err) {
6398c2ecf20Sopenharmony_ci		dev_err(dev, "failed to init rx ring\n");
6408c2ecf20Sopenharmony_ci		goto err_rx_init;
6418c2ecf20Sopenharmony_ci	}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	err = prestera_sdma_tx_init(sdma);
6448c2ecf20Sopenharmony_ci	if (err) {
6458c2ecf20Sopenharmony_ci		dev_err(dev, "failed to init tx ring\n");
6468c2ecf20Sopenharmony_ci		goto err_tx_init;
6478c2ecf20Sopenharmony_ci	}
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	err = prestera_hw_event_handler_register(sw, PRESTERA_EVENT_TYPE_RXTX,
6508c2ecf20Sopenharmony_ci						 prestera_rxtx_handle_event,
6518c2ecf20Sopenharmony_ci						 sdma);
6528c2ecf20Sopenharmony_ci	if (err)
6538c2ecf20Sopenharmony_ci		goto err_evt_register;
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	init_dummy_netdev(&sdma->napi_dev);
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	netif_napi_add(&sdma->napi_dev, &sdma->rx_napi, prestera_sdma_rx_poll, 64);
6588c2ecf20Sopenharmony_ci	napi_enable(&sdma->rx_napi);
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	return 0;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_cierr_evt_register:
6638c2ecf20Sopenharmony_cierr_tx_init:
6648c2ecf20Sopenharmony_ci	prestera_sdma_tx_fini(sdma);
6658c2ecf20Sopenharmony_cierr_rx_init:
6668c2ecf20Sopenharmony_ci	prestera_sdma_rx_fini(sdma);
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	dma_pool_destroy(sdma->desc_pool);
6698c2ecf20Sopenharmony_ci	return err;
6708c2ecf20Sopenharmony_ci}
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_cistatic void prestera_sdma_switch_fini(struct prestera_switch *sw)
6738c2ecf20Sopenharmony_ci{
6748c2ecf20Sopenharmony_ci	struct prestera_sdma *sdma = &sw->rxtx->sdma;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	napi_disable(&sdma->rx_napi);
6778c2ecf20Sopenharmony_ci	netif_napi_del(&sdma->rx_napi);
6788c2ecf20Sopenharmony_ci	prestera_hw_event_handler_unregister(sw, PRESTERA_EVENT_TYPE_RXTX,
6798c2ecf20Sopenharmony_ci					     prestera_rxtx_handle_event);
6808c2ecf20Sopenharmony_ci	prestera_sdma_tx_fini(sdma);
6818c2ecf20Sopenharmony_ci	prestera_sdma_rx_fini(sdma);
6828c2ecf20Sopenharmony_ci	dma_pool_destroy(sdma->desc_pool);
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_cistatic bool prestera_sdma_is_ready(struct prestera_sdma *sdma)
6868c2ecf20Sopenharmony_ci{
6878c2ecf20Sopenharmony_ci	return !(prestera_read(sdma->sw, PRESTERA_SDMA_TX_QUEUE_START_REG) & 1);
6888c2ecf20Sopenharmony_ci}
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_cistatic int prestera_sdma_tx_wait(struct prestera_sdma *sdma,
6918c2ecf20Sopenharmony_ci				 struct prestera_tx_ring *tx_ring)
6928c2ecf20Sopenharmony_ci{
6938c2ecf20Sopenharmony_ci	int tx_wait_num = PRESTERA_SDMA_WAIT_MUL * tx_ring->max_burst;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	do {
6968c2ecf20Sopenharmony_ci		if (prestera_sdma_is_ready(sdma))
6978c2ecf20Sopenharmony_ci			return 0;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci		udelay(1);
7008c2ecf20Sopenharmony_ci	} while (--tx_wait_num);
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	return -EBUSY;
7038c2ecf20Sopenharmony_ci}
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_cistatic void prestera_sdma_tx_start(struct prestera_sdma *sdma)
7068c2ecf20Sopenharmony_ci{
7078c2ecf20Sopenharmony_ci	prestera_write(sdma->sw, PRESTERA_SDMA_TX_QUEUE_START_REG, 1);
7088c2ecf20Sopenharmony_ci	schedule_work(&sdma->tx_work);
7098c2ecf20Sopenharmony_ci}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_cistatic netdev_tx_t prestera_sdma_xmit(struct prestera_sdma *sdma,
7128c2ecf20Sopenharmony_ci				      struct sk_buff *skb)
7138c2ecf20Sopenharmony_ci{
7148c2ecf20Sopenharmony_ci	struct device *dma_dev = sdma->sw->dev->dev;
7158c2ecf20Sopenharmony_ci	struct net_device *dev = skb->dev;
7168c2ecf20Sopenharmony_ci	struct prestera_tx_ring *tx_ring;
7178c2ecf20Sopenharmony_ci	struct prestera_sdma_buf *buf;
7188c2ecf20Sopenharmony_ci	int err;
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	spin_lock(&sdma->tx_lock);
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	tx_ring = &sdma->tx_ring;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	buf = &tx_ring->bufs[tx_ring->next_tx];
7258c2ecf20Sopenharmony_ci	if (buf->is_used) {
7268c2ecf20Sopenharmony_ci		schedule_work(&sdma->tx_work);
7278c2ecf20Sopenharmony_ci		goto drop_skb;
7288c2ecf20Sopenharmony_ci	}
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	if (unlikely(eth_skb_pad(skb)))
7318c2ecf20Sopenharmony_ci		goto drop_skb_nofree;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	err = prestera_sdma_tx_buf_map(sdma, buf, skb);
7348c2ecf20Sopenharmony_ci	if (err)
7358c2ecf20Sopenharmony_ci		goto drop_skb;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	prestera_sdma_tx_desc_set_buf(sdma, buf->desc, buf->buf_dma, skb->len);
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	dma_sync_single_for_device(dma_dev, buf->buf_dma, skb->len,
7408c2ecf20Sopenharmony_ci				   DMA_TO_DEVICE);
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	if (tx_ring->burst) {
7438c2ecf20Sopenharmony_ci		tx_ring->burst--;
7448c2ecf20Sopenharmony_ci	} else {
7458c2ecf20Sopenharmony_ci		tx_ring->burst = tx_ring->max_burst;
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci		err = prestera_sdma_tx_wait(sdma, tx_ring);
7488c2ecf20Sopenharmony_ci		if (err)
7498c2ecf20Sopenharmony_ci			goto drop_skb_unmap;
7508c2ecf20Sopenharmony_ci	}
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	tx_ring->next_tx = (tx_ring->next_tx + 1) % PRESTERA_SDMA_TX_DESC_PER_Q;
7538c2ecf20Sopenharmony_ci	prestera_sdma_tx_desc_xmit(buf->desc);
7548c2ecf20Sopenharmony_ci	buf->is_used = true;
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	prestera_sdma_tx_start(sdma);
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	goto tx_done;
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_cidrop_skb_unmap:
7618c2ecf20Sopenharmony_ci	prestera_sdma_tx_buf_unmap(sdma, buf);
7628c2ecf20Sopenharmony_cidrop_skb:
7638c2ecf20Sopenharmony_ci	dev_consume_skb_any(skb);
7648c2ecf20Sopenharmony_cidrop_skb_nofree:
7658c2ecf20Sopenharmony_ci	dev->stats.tx_dropped++;
7668c2ecf20Sopenharmony_citx_done:
7678c2ecf20Sopenharmony_ci	spin_unlock(&sdma->tx_lock);
7688c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
7698c2ecf20Sopenharmony_ci}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ciint prestera_rxtx_switch_init(struct prestera_switch *sw)
7728c2ecf20Sopenharmony_ci{
7738c2ecf20Sopenharmony_ci	struct prestera_rxtx *rxtx;
7748c2ecf20Sopenharmony_ci	int err;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	rxtx = kzalloc(sizeof(*rxtx), GFP_KERNEL);
7778c2ecf20Sopenharmony_ci	if (!rxtx)
7788c2ecf20Sopenharmony_ci		return -ENOMEM;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	sw->rxtx = rxtx;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	err = prestera_sdma_switch_init(sw);
7838c2ecf20Sopenharmony_ci	if (err)
7848c2ecf20Sopenharmony_ci		kfree(rxtx);
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	return err;
7878c2ecf20Sopenharmony_ci}
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_civoid prestera_rxtx_switch_fini(struct prestera_switch *sw)
7908c2ecf20Sopenharmony_ci{
7918c2ecf20Sopenharmony_ci	prestera_sdma_switch_fini(sw);
7928c2ecf20Sopenharmony_ci	kfree(sw->rxtx);
7938c2ecf20Sopenharmony_ci}
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ciint prestera_rxtx_port_init(struct prestera_port *port)
7968c2ecf20Sopenharmony_ci{
7978c2ecf20Sopenharmony_ci	int err;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	err = prestera_hw_rxtx_port_init(port);
8008c2ecf20Sopenharmony_ci	if (err)
8018c2ecf20Sopenharmony_ci		return err;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	port->dev->needed_headroom = PRESTERA_DSA_HLEN;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	return 0;
8068c2ecf20Sopenharmony_ci}
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_cinetdev_tx_t prestera_rxtx_xmit(struct prestera_port *port, struct sk_buff *skb)
8098c2ecf20Sopenharmony_ci{
8108c2ecf20Sopenharmony_ci	struct prestera_dsa dsa;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	dsa.hw_dev_num = port->dev_id;
8138c2ecf20Sopenharmony_ci	dsa.port_num = port->hw_id;
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	if (skb_cow_head(skb, PRESTERA_DSA_HLEN) < 0)
8168c2ecf20Sopenharmony_ci		return NET_XMIT_DROP;
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	skb_push(skb, PRESTERA_DSA_HLEN);
8198c2ecf20Sopenharmony_ci	memmove(skb->data, skb->data + PRESTERA_DSA_HLEN, 2 * ETH_ALEN);
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	if (prestera_dsa_build(&dsa, skb->data + 2 * ETH_ALEN) != 0)
8228c2ecf20Sopenharmony_ci		return NET_XMIT_DROP;
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	return prestera_sdma_xmit(&port->sw->rxtx->sdma, skb);
8258c2ecf20Sopenharmony_ci}
826