162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/* Microchip Sparx5 Switch driver
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * The Sparx5 Chip Register Model can be browsed at this location:
762306a36Sopenharmony_ci * https://github.com/microchip-ung/sparx-5_reginfo
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/types.h>
1162306a36Sopenharmony_ci#include <linux/skbuff.h>
1262306a36Sopenharmony_ci#include <linux/netdevice.h>
1362306a36Sopenharmony_ci#include <linux/interrupt.h>
1462306a36Sopenharmony_ci#include <linux/ip.h>
1562306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "sparx5_main_regs.h"
1862306a36Sopenharmony_ci#include "sparx5_main.h"
1962306a36Sopenharmony_ci#include "sparx5_port.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define FDMA_XTR_CHANNEL		6
2262306a36Sopenharmony_ci#define FDMA_INJ_CHANNEL		0
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define FDMA_DCB_INFO_DATAL(x)		((x) & GENMASK(15, 0))
2562306a36Sopenharmony_ci#define FDMA_DCB_INFO_TOKEN		BIT(17)
2662306a36Sopenharmony_ci#define FDMA_DCB_INFO_INTR		BIT(18)
2762306a36Sopenharmony_ci#define FDMA_DCB_INFO_SW(x)		(((x) << 24) & GENMASK(31, 24))
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define FDMA_DCB_STATUS_BLOCKL(x)	((x) & GENMASK(15, 0))
3062306a36Sopenharmony_ci#define FDMA_DCB_STATUS_SOF		BIT(16)
3162306a36Sopenharmony_ci#define FDMA_DCB_STATUS_EOF		BIT(17)
3262306a36Sopenharmony_ci#define FDMA_DCB_STATUS_INTR		BIT(18)
3362306a36Sopenharmony_ci#define FDMA_DCB_STATUS_DONE		BIT(19)
3462306a36Sopenharmony_ci#define FDMA_DCB_STATUS_BLOCKO(x)	(((x) << 20) & GENMASK(31, 20))
3562306a36Sopenharmony_ci#define FDMA_DCB_INVALID_DATA		0x1
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define FDMA_XTR_BUFFER_SIZE		2048
3862306a36Sopenharmony_ci#define FDMA_WEIGHT			4
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/* Frame DMA DCB format
4162306a36Sopenharmony_ci *
4262306a36Sopenharmony_ci * +---------------------------+
4362306a36Sopenharmony_ci * |         Next Ptr          |
4462306a36Sopenharmony_ci * +---------------------------+
4562306a36Sopenharmony_ci * |   Reserved  |    Info     |
4662306a36Sopenharmony_ci * +---------------------------+
4762306a36Sopenharmony_ci * |         Data0 Ptr         |
4862306a36Sopenharmony_ci * +---------------------------+
4962306a36Sopenharmony_ci * |   Reserved  |    Status0  |
5062306a36Sopenharmony_ci * +---------------------------+
5162306a36Sopenharmony_ci * |         Data1 Ptr         |
5262306a36Sopenharmony_ci * +---------------------------+
5362306a36Sopenharmony_ci * |   Reserved  |    Status1  |
5462306a36Sopenharmony_ci * +---------------------------+
5562306a36Sopenharmony_ci * |         Data2 Ptr         |
5662306a36Sopenharmony_ci * +---------------------------+
5762306a36Sopenharmony_ci * |   Reserved  |    Status2  |
5862306a36Sopenharmony_ci * |-------------|-------------|
5962306a36Sopenharmony_ci * |                           |
6062306a36Sopenharmony_ci * |                           |
6162306a36Sopenharmony_ci * |                           |
6262306a36Sopenharmony_ci * |                           |
6362306a36Sopenharmony_ci * |                           |
6462306a36Sopenharmony_ci * |---------------------------|
6562306a36Sopenharmony_ci * |         Data14 Ptr        |
6662306a36Sopenharmony_ci * +-------------|-------------+
6762306a36Sopenharmony_ci * |   Reserved  |    Status14 |
6862306a36Sopenharmony_ci * +-------------|-------------+
6962306a36Sopenharmony_ci */
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/* For each hardware DB there is an entry in this list and when the HW DB
7262306a36Sopenharmony_ci * entry is used, this SW DB entry is moved to the back of the list
7362306a36Sopenharmony_ci */
7462306a36Sopenharmony_cistruct sparx5_db {
7562306a36Sopenharmony_ci	struct list_head list;
7662306a36Sopenharmony_ci	void *cpu_addr;
7762306a36Sopenharmony_ci};
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic void sparx5_fdma_rx_add_dcb(struct sparx5_rx *rx,
8062306a36Sopenharmony_ci				   struct sparx5_rx_dcb_hw *dcb,
8162306a36Sopenharmony_ci				   u64 nextptr)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	int idx = 0;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	/* Reset the status of the DB */
8662306a36Sopenharmony_ci	for (idx = 0; idx < FDMA_RX_DCB_MAX_DBS; ++idx) {
8762306a36Sopenharmony_ci		struct sparx5_db_hw *db = &dcb->db[idx];
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci		db->status = FDMA_DCB_STATUS_INTR;
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci	dcb->nextptr = FDMA_DCB_INVALID_DATA;
9262306a36Sopenharmony_ci	dcb->info = FDMA_DCB_INFO_DATAL(FDMA_XTR_BUFFER_SIZE);
9362306a36Sopenharmony_ci	rx->last_entry->nextptr = nextptr;
9462306a36Sopenharmony_ci	rx->last_entry = dcb;
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic void sparx5_fdma_tx_add_dcb(struct sparx5_tx *tx,
9862306a36Sopenharmony_ci				   struct sparx5_tx_dcb_hw *dcb,
9962306a36Sopenharmony_ci				   u64 nextptr)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	int idx = 0;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	/* Reset the status of the DB */
10462306a36Sopenharmony_ci	for (idx = 0; idx < FDMA_TX_DCB_MAX_DBS; ++idx) {
10562306a36Sopenharmony_ci		struct sparx5_db_hw *db = &dcb->db[idx];
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci		db->status = FDMA_DCB_STATUS_DONE;
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci	dcb->nextptr = FDMA_DCB_INVALID_DATA;
11062306a36Sopenharmony_ci	dcb->info = FDMA_DCB_INFO_DATAL(FDMA_XTR_BUFFER_SIZE);
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic void sparx5_fdma_rx_activate(struct sparx5 *sparx5, struct sparx5_rx *rx)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	/* Write the buffer address in the LLP and LLP1 regs */
11662306a36Sopenharmony_ci	spx5_wr(((u64)rx->dma) & GENMASK(31, 0), sparx5,
11762306a36Sopenharmony_ci		FDMA_DCB_LLP(rx->channel_id));
11862306a36Sopenharmony_ci	spx5_wr(((u64)rx->dma) >> 32, sparx5, FDMA_DCB_LLP1(rx->channel_id));
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/* Set the number of RX DBs to be used, and DB end-of-frame interrupt */
12162306a36Sopenharmony_ci	spx5_wr(FDMA_CH_CFG_CH_DCB_DB_CNT_SET(FDMA_RX_DCB_MAX_DBS) |
12262306a36Sopenharmony_ci		FDMA_CH_CFG_CH_INTR_DB_EOF_ONLY_SET(1) |
12362306a36Sopenharmony_ci		FDMA_CH_CFG_CH_INJ_PORT_SET(XTR_QUEUE),
12462306a36Sopenharmony_ci		sparx5, FDMA_CH_CFG(rx->channel_id));
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	/* Set the RX Watermark to max */
12762306a36Sopenharmony_ci	spx5_rmw(FDMA_XTR_CFG_XTR_FIFO_WM_SET(31), FDMA_XTR_CFG_XTR_FIFO_WM,
12862306a36Sopenharmony_ci		 sparx5,
12962306a36Sopenharmony_ci		 FDMA_XTR_CFG);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/* Start RX fdma */
13262306a36Sopenharmony_ci	spx5_rmw(FDMA_PORT_CTRL_XTR_STOP_SET(0), FDMA_PORT_CTRL_XTR_STOP,
13362306a36Sopenharmony_ci		 sparx5, FDMA_PORT_CTRL(0));
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	/* Enable RX channel DB interrupt */
13662306a36Sopenharmony_ci	spx5_rmw(BIT(rx->channel_id),
13762306a36Sopenharmony_ci		 BIT(rx->channel_id) & FDMA_INTR_DB_ENA_INTR_DB_ENA,
13862306a36Sopenharmony_ci		 sparx5, FDMA_INTR_DB_ENA);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/* Activate the RX channel */
14162306a36Sopenharmony_ci	spx5_wr(BIT(rx->channel_id), sparx5, FDMA_CH_ACTIVATE);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic void sparx5_fdma_rx_deactivate(struct sparx5 *sparx5, struct sparx5_rx *rx)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	/* Dectivate the RX channel */
14762306a36Sopenharmony_ci	spx5_rmw(0, BIT(rx->channel_id) & FDMA_CH_ACTIVATE_CH_ACTIVATE,
14862306a36Sopenharmony_ci		 sparx5, FDMA_CH_ACTIVATE);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* Disable RX channel DB interrupt */
15162306a36Sopenharmony_ci	spx5_rmw(0, BIT(rx->channel_id) & FDMA_INTR_DB_ENA_INTR_DB_ENA,
15262306a36Sopenharmony_ci		 sparx5, FDMA_INTR_DB_ENA);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	/* Stop RX fdma */
15562306a36Sopenharmony_ci	spx5_rmw(FDMA_PORT_CTRL_XTR_STOP_SET(1), FDMA_PORT_CTRL_XTR_STOP,
15662306a36Sopenharmony_ci		 sparx5, FDMA_PORT_CTRL(0));
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic void sparx5_fdma_tx_activate(struct sparx5 *sparx5, struct sparx5_tx *tx)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	/* Write the buffer address in the LLP and LLP1 regs */
16262306a36Sopenharmony_ci	spx5_wr(((u64)tx->dma) & GENMASK(31, 0), sparx5,
16362306a36Sopenharmony_ci		FDMA_DCB_LLP(tx->channel_id));
16462306a36Sopenharmony_ci	spx5_wr(((u64)tx->dma) >> 32, sparx5, FDMA_DCB_LLP1(tx->channel_id));
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	/* Set the number of TX DBs to be used, and DB end-of-frame interrupt */
16762306a36Sopenharmony_ci	spx5_wr(FDMA_CH_CFG_CH_DCB_DB_CNT_SET(FDMA_TX_DCB_MAX_DBS) |
16862306a36Sopenharmony_ci		FDMA_CH_CFG_CH_INTR_DB_EOF_ONLY_SET(1) |
16962306a36Sopenharmony_ci		FDMA_CH_CFG_CH_INJ_PORT_SET(INJ_QUEUE),
17062306a36Sopenharmony_ci		sparx5, FDMA_CH_CFG(tx->channel_id));
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/* Start TX fdma */
17362306a36Sopenharmony_ci	spx5_rmw(FDMA_PORT_CTRL_INJ_STOP_SET(0), FDMA_PORT_CTRL_INJ_STOP,
17462306a36Sopenharmony_ci		 sparx5, FDMA_PORT_CTRL(0));
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	/* Activate the channel */
17762306a36Sopenharmony_ci	spx5_wr(BIT(tx->channel_id), sparx5, FDMA_CH_ACTIVATE);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic void sparx5_fdma_tx_deactivate(struct sparx5 *sparx5, struct sparx5_tx *tx)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	/* Disable the channel */
18362306a36Sopenharmony_ci	spx5_rmw(0, BIT(tx->channel_id) & FDMA_CH_ACTIVATE_CH_ACTIVATE,
18462306a36Sopenharmony_ci		 sparx5, FDMA_CH_ACTIVATE);
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic void sparx5_fdma_rx_reload(struct sparx5 *sparx5, struct sparx5_rx *rx)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	/* Reload the RX channel */
19062306a36Sopenharmony_ci	spx5_wr(BIT(rx->channel_id), sparx5, FDMA_CH_RELOAD);
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic void sparx5_fdma_tx_reload(struct sparx5 *sparx5, struct sparx5_tx *tx)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	/* Reload the TX channel */
19662306a36Sopenharmony_ci	spx5_wr(BIT(tx->channel_id), sparx5, FDMA_CH_RELOAD);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic struct sk_buff *sparx5_fdma_rx_alloc_skb(struct sparx5_rx *rx)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	return __netdev_alloc_skb(rx->ndev, FDMA_XTR_BUFFER_SIZE,
20262306a36Sopenharmony_ci				  GFP_ATOMIC);
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic bool sparx5_fdma_rx_get_frame(struct sparx5 *sparx5, struct sparx5_rx *rx)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	struct sparx5_db_hw *db_hw;
20862306a36Sopenharmony_ci	unsigned int packet_size;
20962306a36Sopenharmony_ci	struct sparx5_port *port;
21062306a36Sopenharmony_ci	struct sk_buff *new_skb;
21162306a36Sopenharmony_ci	struct frame_info fi;
21262306a36Sopenharmony_ci	struct sk_buff *skb;
21362306a36Sopenharmony_ci	dma_addr_t dma_addr;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* Check if the DCB is done */
21662306a36Sopenharmony_ci	db_hw = &rx->dcb_entries[rx->dcb_index].db[rx->db_index];
21762306a36Sopenharmony_ci	if (unlikely(!(db_hw->status & FDMA_DCB_STATUS_DONE)))
21862306a36Sopenharmony_ci		return false;
21962306a36Sopenharmony_ci	skb = rx->skb[rx->dcb_index][rx->db_index];
22062306a36Sopenharmony_ci	/* Replace the DB entry with a new SKB */
22162306a36Sopenharmony_ci	new_skb = sparx5_fdma_rx_alloc_skb(rx);
22262306a36Sopenharmony_ci	if (unlikely(!new_skb))
22362306a36Sopenharmony_ci		return false;
22462306a36Sopenharmony_ci	/* Map the new skb data and set the new skb */
22562306a36Sopenharmony_ci	dma_addr = virt_to_phys(new_skb->data);
22662306a36Sopenharmony_ci	rx->skb[rx->dcb_index][rx->db_index] = new_skb;
22762306a36Sopenharmony_ci	db_hw->dataptr = dma_addr;
22862306a36Sopenharmony_ci	packet_size = FDMA_DCB_STATUS_BLOCKL(db_hw->status);
22962306a36Sopenharmony_ci	skb_put(skb, packet_size);
23062306a36Sopenharmony_ci	/* Now do the normal processing of the skb */
23162306a36Sopenharmony_ci	sparx5_ifh_parse((u32 *)skb->data, &fi);
23262306a36Sopenharmony_ci	/* Map to port netdev */
23362306a36Sopenharmony_ci	port = fi.src_port < SPX5_PORTS ?  sparx5->ports[fi.src_port] : NULL;
23462306a36Sopenharmony_ci	if (!port || !port->ndev) {
23562306a36Sopenharmony_ci		dev_err(sparx5->dev, "Data on inactive port %d\n", fi.src_port);
23662306a36Sopenharmony_ci		sparx5_xtr_flush(sparx5, XTR_QUEUE);
23762306a36Sopenharmony_ci		return false;
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci	skb->dev = port->ndev;
24062306a36Sopenharmony_ci	skb_pull(skb, IFH_LEN * sizeof(u32));
24162306a36Sopenharmony_ci	if (likely(!(skb->dev->features & NETIF_F_RXFCS)))
24262306a36Sopenharmony_ci		skb_trim(skb, skb->len - ETH_FCS_LEN);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	sparx5_ptp_rxtstamp(sparx5, skb, fi.timestamp);
24562306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, skb->dev);
24662306a36Sopenharmony_ci	/* Everything we see on an interface that is in the HW bridge
24762306a36Sopenharmony_ci	 * has already been forwarded
24862306a36Sopenharmony_ci	 */
24962306a36Sopenharmony_ci	if (test_bit(port->portno, sparx5->bridge_mask))
25062306a36Sopenharmony_ci		skb->offload_fwd_mark = 1;
25162306a36Sopenharmony_ci	skb->dev->stats.rx_bytes += skb->len;
25262306a36Sopenharmony_ci	skb->dev->stats.rx_packets++;
25362306a36Sopenharmony_ci	rx->packets++;
25462306a36Sopenharmony_ci	netif_receive_skb(skb);
25562306a36Sopenharmony_ci	return true;
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic int sparx5_fdma_napi_callback(struct napi_struct *napi, int weight)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct sparx5_rx *rx = container_of(napi, struct sparx5_rx, napi);
26162306a36Sopenharmony_ci	struct sparx5 *sparx5 = container_of(rx, struct sparx5, rx);
26262306a36Sopenharmony_ci	int counter = 0;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	while (counter < weight && sparx5_fdma_rx_get_frame(sparx5, rx)) {
26562306a36Sopenharmony_ci		struct sparx5_rx_dcb_hw *old_dcb;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci		rx->db_index++;
26862306a36Sopenharmony_ci		counter++;
26962306a36Sopenharmony_ci		/* Check if the DCB can be reused */
27062306a36Sopenharmony_ci		if (rx->db_index != FDMA_RX_DCB_MAX_DBS)
27162306a36Sopenharmony_ci			continue;
27262306a36Sopenharmony_ci		/* As the DCB  can be reused, just advance the dcb_index
27362306a36Sopenharmony_ci		 * pointer and set the nextptr in the DCB
27462306a36Sopenharmony_ci		 */
27562306a36Sopenharmony_ci		rx->db_index = 0;
27662306a36Sopenharmony_ci		old_dcb = &rx->dcb_entries[rx->dcb_index];
27762306a36Sopenharmony_ci		rx->dcb_index++;
27862306a36Sopenharmony_ci		rx->dcb_index &= FDMA_DCB_MAX - 1;
27962306a36Sopenharmony_ci		sparx5_fdma_rx_add_dcb(rx, old_dcb,
28062306a36Sopenharmony_ci				       rx->dma +
28162306a36Sopenharmony_ci				       ((unsigned long)old_dcb -
28262306a36Sopenharmony_ci					(unsigned long)rx->dcb_entries));
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci	if (counter < weight) {
28562306a36Sopenharmony_ci		napi_complete_done(&rx->napi, counter);
28662306a36Sopenharmony_ci		spx5_rmw(BIT(rx->channel_id),
28762306a36Sopenharmony_ci			 BIT(rx->channel_id) & FDMA_INTR_DB_ENA_INTR_DB_ENA,
28862306a36Sopenharmony_ci			 sparx5, FDMA_INTR_DB_ENA);
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci	if (counter)
29162306a36Sopenharmony_ci		sparx5_fdma_rx_reload(sparx5, rx);
29262306a36Sopenharmony_ci	return counter;
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic struct sparx5_tx_dcb_hw *sparx5_fdma_next_dcb(struct sparx5_tx *tx,
29662306a36Sopenharmony_ci						     struct sparx5_tx_dcb_hw *dcb)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	struct sparx5_tx_dcb_hw *next_dcb;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	next_dcb = dcb;
30162306a36Sopenharmony_ci	next_dcb++;
30262306a36Sopenharmony_ci	/* Handle wrap-around */
30362306a36Sopenharmony_ci	if ((unsigned long)next_dcb >=
30462306a36Sopenharmony_ci	    ((unsigned long)tx->first_entry + FDMA_DCB_MAX * sizeof(*dcb)))
30562306a36Sopenharmony_ci		next_dcb = tx->first_entry;
30662306a36Sopenharmony_ci	return next_dcb;
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ciint sparx5_fdma_xmit(struct sparx5 *sparx5, u32 *ifh, struct sk_buff *skb)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	struct sparx5_tx_dcb_hw *next_dcb_hw;
31262306a36Sopenharmony_ci	struct sparx5_tx *tx = &sparx5->tx;
31362306a36Sopenharmony_ci	static bool first_time = true;
31462306a36Sopenharmony_ci	struct sparx5_db_hw *db_hw;
31562306a36Sopenharmony_ci	struct sparx5_db *db;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	next_dcb_hw = sparx5_fdma_next_dcb(tx, tx->curr_entry);
31862306a36Sopenharmony_ci	db_hw = &next_dcb_hw->db[0];
31962306a36Sopenharmony_ci	if (!(db_hw->status & FDMA_DCB_STATUS_DONE))
32062306a36Sopenharmony_ci		return -EINVAL;
32162306a36Sopenharmony_ci	db = list_first_entry(&tx->db_list, struct sparx5_db, list);
32262306a36Sopenharmony_ci	list_move_tail(&db->list, &tx->db_list);
32362306a36Sopenharmony_ci	next_dcb_hw->nextptr = FDMA_DCB_INVALID_DATA;
32462306a36Sopenharmony_ci	tx->curr_entry->nextptr = tx->dma +
32562306a36Sopenharmony_ci		((unsigned long)next_dcb_hw -
32662306a36Sopenharmony_ci		 (unsigned long)tx->first_entry);
32762306a36Sopenharmony_ci	tx->curr_entry = next_dcb_hw;
32862306a36Sopenharmony_ci	memset(db->cpu_addr, 0, FDMA_XTR_BUFFER_SIZE);
32962306a36Sopenharmony_ci	memcpy(db->cpu_addr, ifh, IFH_LEN * 4);
33062306a36Sopenharmony_ci	memcpy(db->cpu_addr + IFH_LEN * 4, skb->data, skb->len);
33162306a36Sopenharmony_ci	db_hw->status = FDMA_DCB_STATUS_SOF |
33262306a36Sopenharmony_ci			FDMA_DCB_STATUS_EOF |
33362306a36Sopenharmony_ci			FDMA_DCB_STATUS_BLOCKO(0) |
33462306a36Sopenharmony_ci			FDMA_DCB_STATUS_BLOCKL(skb->len + IFH_LEN * 4 + 4);
33562306a36Sopenharmony_ci	if (first_time) {
33662306a36Sopenharmony_ci		sparx5_fdma_tx_activate(sparx5, tx);
33762306a36Sopenharmony_ci		first_time = false;
33862306a36Sopenharmony_ci	} else {
33962306a36Sopenharmony_ci		sparx5_fdma_tx_reload(sparx5, tx);
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci	return NETDEV_TX_OK;
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cistatic int sparx5_fdma_rx_alloc(struct sparx5 *sparx5)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	struct sparx5_rx *rx = &sparx5->rx;
34762306a36Sopenharmony_ci	struct sparx5_rx_dcb_hw *dcb;
34862306a36Sopenharmony_ci	int idx, jdx;
34962306a36Sopenharmony_ci	int size;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	size = sizeof(struct sparx5_rx_dcb_hw) * FDMA_DCB_MAX;
35262306a36Sopenharmony_ci	size = ALIGN(size, PAGE_SIZE);
35362306a36Sopenharmony_ci	rx->dcb_entries = devm_kzalloc(sparx5->dev, size, GFP_KERNEL);
35462306a36Sopenharmony_ci	if (!rx->dcb_entries)
35562306a36Sopenharmony_ci		return -ENOMEM;
35662306a36Sopenharmony_ci	rx->dma = virt_to_phys(rx->dcb_entries);
35762306a36Sopenharmony_ci	rx->last_entry = rx->dcb_entries;
35862306a36Sopenharmony_ci	rx->db_index = 0;
35962306a36Sopenharmony_ci	rx->dcb_index = 0;
36062306a36Sopenharmony_ci	/* Now for each dcb allocate the db */
36162306a36Sopenharmony_ci	for (idx = 0; idx < FDMA_DCB_MAX; ++idx) {
36262306a36Sopenharmony_ci		dcb = &rx->dcb_entries[idx];
36362306a36Sopenharmony_ci		dcb->info = 0;
36462306a36Sopenharmony_ci		/* For each db allocate an skb and map skb data pointer to the DB
36562306a36Sopenharmony_ci		 * dataptr. In this way when the frame is received the skb->data
36662306a36Sopenharmony_ci		 * will contain the frame, so no memcpy is needed
36762306a36Sopenharmony_ci		 */
36862306a36Sopenharmony_ci		for (jdx = 0; jdx < FDMA_RX_DCB_MAX_DBS; ++jdx) {
36962306a36Sopenharmony_ci			struct sparx5_db_hw *db_hw = &dcb->db[jdx];
37062306a36Sopenharmony_ci			dma_addr_t dma_addr;
37162306a36Sopenharmony_ci			struct sk_buff *skb;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci			skb = sparx5_fdma_rx_alloc_skb(rx);
37462306a36Sopenharmony_ci			if (!skb)
37562306a36Sopenharmony_ci				return -ENOMEM;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci			dma_addr = virt_to_phys(skb->data);
37862306a36Sopenharmony_ci			db_hw->dataptr = dma_addr;
37962306a36Sopenharmony_ci			db_hw->status = 0;
38062306a36Sopenharmony_ci			rx->skb[idx][jdx] = skb;
38162306a36Sopenharmony_ci		}
38262306a36Sopenharmony_ci		sparx5_fdma_rx_add_dcb(rx, dcb, rx->dma + sizeof(*dcb) * idx);
38362306a36Sopenharmony_ci	}
38462306a36Sopenharmony_ci	netif_napi_add_weight(rx->ndev, &rx->napi, sparx5_fdma_napi_callback,
38562306a36Sopenharmony_ci			      FDMA_WEIGHT);
38662306a36Sopenharmony_ci	napi_enable(&rx->napi);
38762306a36Sopenharmony_ci	sparx5_fdma_rx_activate(sparx5, rx);
38862306a36Sopenharmony_ci	return 0;
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_cistatic int sparx5_fdma_tx_alloc(struct sparx5 *sparx5)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	struct sparx5_tx *tx = &sparx5->tx;
39462306a36Sopenharmony_ci	struct sparx5_tx_dcb_hw *dcb;
39562306a36Sopenharmony_ci	int idx, jdx;
39662306a36Sopenharmony_ci	int size;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	size = sizeof(struct sparx5_tx_dcb_hw) * FDMA_DCB_MAX;
39962306a36Sopenharmony_ci	size = ALIGN(size, PAGE_SIZE);
40062306a36Sopenharmony_ci	tx->curr_entry = devm_kzalloc(sparx5->dev, size, GFP_KERNEL);
40162306a36Sopenharmony_ci	if (!tx->curr_entry)
40262306a36Sopenharmony_ci		return -ENOMEM;
40362306a36Sopenharmony_ci	tx->dma = virt_to_phys(tx->curr_entry);
40462306a36Sopenharmony_ci	tx->first_entry = tx->curr_entry;
40562306a36Sopenharmony_ci	INIT_LIST_HEAD(&tx->db_list);
40662306a36Sopenharmony_ci	/* Now for each dcb allocate the db */
40762306a36Sopenharmony_ci	for (idx = 0; idx < FDMA_DCB_MAX; ++idx) {
40862306a36Sopenharmony_ci		dcb = &tx->curr_entry[idx];
40962306a36Sopenharmony_ci		dcb->info = 0;
41062306a36Sopenharmony_ci		/* TX databuffers must be 16byte aligned */
41162306a36Sopenharmony_ci		for (jdx = 0; jdx < FDMA_TX_DCB_MAX_DBS; ++jdx) {
41262306a36Sopenharmony_ci			struct sparx5_db_hw *db_hw = &dcb->db[jdx];
41362306a36Sopenharmony_ci			struct sparx5_db *db;
41462306a36Sopenharmony_ci			dma_addr_t phys;
41562306a36Sopenharmony_ci			void *cpu_addr;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci			cpu_addr = devm_kzalloc(sparx5->dev,
41862306a36Sopenharmony_ci						FDMA_XTR_BUFFER_SIZE,
41962306a36Sopenharmony_ci						GFP_KERNEL);
42062306a36Sopenharmony_ci			if (!cpu_addr)
42162306a36Sopenharmony_ci				return -ENOMEM;
42262306a36Sopenharmony_ci			phys = virt_to_phys(cpu_addr);
42362306a36Sopenharmony_ci			db_hw->dataptr = phys;
42462306a36Sopenharmony_ci			db_hw->status = 0;
42562306a36Sopenharmony_ci			db = devm_kzalloc(sparx5->dev, sizeof(*db), GFP_KERNEL);
42662306a36Sopenharmony_ci			if (!db)
42762306a36Sopenharmony_ci				return -ENOMEM;
42862306a36Sopenharmony_ci			db->cpu_addr = cpu_addr;
42962306a36Sopenharmony_ci			list_add_tail(&db->list, &tx->db_list);
43062306a36Sopenharmony_ci		}
43162306a36Sopenharmony_ci		sparx5_fdma_tx_add_dcb(tx, dcb, tx->dma + sizeof(*dcb) * idx);
43262306a36Sopenharmony_ci		/* Let the curr_entry to point to the last allocated entry */
43362306a36Sopenharmony_ci		if (idx == FDMA_DCB_MAX - 1)
43462306a36Sopenharmony_ci			tx->curr_entry = dcb;
43562306a36Sopenharmony_ci	}
43662306a36Sopenharmony_ci	return 0;
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic void sparx5_fdma_rx_init(struct sparx5 *sparx5,
44062306a36Sopenharmony_ci				struct sparx5_rx *rx, int channel)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	int idx;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	rx->channel_id = channel;
44562306a36Sopenharmony_ci	/* Fetch a netdev for SKB and NAPI use, any will do */
44662306a36Sopenharmony_ci	for (idx = 0; idx < SPX5_PORTS; ++idx) {
44762306a36Sopenharmony_ci		struct sparx5_port *port = sparx5->ports[idx];
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci		if (port && port->ndev) {
45062306a36Sopenharmony_ci			rx->ndev = port->ndev;
45162306a36Sopenharmony_ci			break;
45262306a36Sopenharmony_ci		}
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_cistatic void sparx5_fdma_tx_init(struct sparx5 *sparx5,
45762306a36Sopenharmony_ci				struct sparx5_tx *tx, int channel)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	tx->channel_id = channel;
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ciirqreturn_t sparx5_fdma_handler(int irq, void *args)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	struct sparx5 *sparx5 = args;
46562306a36Sopenharmony_ci	u32 db = 0, err = 0;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	db = spx5_rd(sparx5, FDMA_INTR_DB);
46862306a36Sopenharmony_ci	err = spx5_rd(sparx5, FDMA_INTR_ERR);
46962306a36Sopenharmony_ci	/* Clear interrupt */
47062306a36Sopenharmony_ci	if (db) {
47162306a36Sopenharmony_ci		spx5_wr(0, sparx5, FDMA_INTR_DB_ENA);
47262306a36Sopenharmony_ci		spx5_wr(db, sparx5, FDMA_INTR_DB);
47362306a36Sopenharmony_ci		napi_schedule(&sparx5->rx.napi);
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci	if (err) {
47662306a36Sopenharmony_ci		u32 err_type = spx5_rd(sparx5, FDMA_ERRORS);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci		dev_err_ratelimited(sparx5->dev,
47962306a36Sopenharmony_ci				    "ERR: int: %#x, type: %#x\n",
48062306a36Sopenharmony_ci				    err, err_type);
48162306a36Sopenharmony_ci		spx5_wr(err, sparx5, FDMA_INTR_ERR);
48262306a36Sopenharmony_ci		spx5_wr(err_type, sparx5, FDMA_ERRORS);
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci	return IRQ_HANDLED;
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistatic void sparx5_fdma_injection_mode(struct sparx5 *sparx5)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	const int byte_swap = 1;
49062306a36Sopenharmony_ci	int portno;
49162306a36Sopenharmony_ci	int urgency;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	/* Change mode to fdma extraction and injection */
49462306a36Sopenharmony_ci	spx5_wr(QS_XTR_GRP_CFG_MODE_SET(2) |
49562306a36Sopenharmony_ci		QS_XTR_GRP_CFG_STATUS_WORD_POS_SET(1) |
49662306a36Sopenharmony_ci		QS_XTR_GRP_CFG_BYTE_SWAP_SET(byte_swap),
49762306a36Sopenharmony_ci		sparx5, QS_XTR_GRP_CFG(XTR_QUEUE));
49862306a36Sopenharmony_ci	spx5_wr(QS_INJ_GRP_CFG_MODE_SET(2) |
49962306a36Sopenharmony_ci		QS_INJ_GRP_CFG_BYTE_SWAP_SET(byte_swap),
50062306a36Sopenharmony_ci		sparx5, QS_INJ_GRP_CFG(INJ_QUEUE));
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	/* CPU ports capture setup */
50362306a36Sopenharmony_ci	for (portno = SPX5_PORT_CPU_0; portno <= SPX5_PORT_CPU_1; portno++) {
50462306a36Sopenharmony_ci		/* ASM CPU port: No preamble, IFH, enable padding */
50562306a36Sopenharmony_ci		spx5_wr(ASM_PORT_CFG_PAD_ENA_SET(1) |
50662306a36Sopenharmony_ci			ASM_PORT_CFG_NO_PREAMBLE_ENA_SET(1) |
50762306a36Sopenharmony_ci			ASM_PORT_CFG_INJ_FORMAT_CFG_SET(1), /* 1 = IFH */
50862306a36Sopenharmony_ci			sparx5, ASM_PORT_CFG(portno));
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci		/* Reset WM cnt to unclog queued frames */
51162306a36Sopenharmony_ci		spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR_SET(1),
51262306a36Sopenharmony_ci			 DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR,
51362306a36Sopenharmony_ci			 sparx5,
51462306a36Sopenharmony_ci			 DSM_DEV_TX_STOP_WM_CFG(portno));
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci		/* Set Disassembler Stop Watermark level */
51762306a36Sopenharmony_ci		spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM_SET(100),
51862306a36Sopenharmony_ci			 DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM,
51962306a36Sopenharmony_ci			 sparx5,
52062306a36Sopenharmony_ci			 DSM_DEV_TX_STOP_WM_CFG(portno));
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci		/* Enable port in queue system */
52362306a36Sopenharmony_ci		urgency = sparx5_port_fwd_urg(sparx5, SPEED_2500);
52462306a36Sopenharmony_ci		spx5_rmw(QFWD_SWITCH_PORT_MODE_PORT_ENA_SET(1) |
52562306a36Sopenharmony_ci			 QFWD_SWITCH_PORT_MODE_FWD_URGENCY_SET(urgency),
52662306a36Sopenharmony_ci			 QFWD_SWITCH_PORT_MODE_PORT_ENA |
52762306a36Sopenharmony_ci			 QFWD_SWITCH_PORT_MODE_FWD_URGENCY,
52862306a36Sopenharmony_ci			 sparx5,
52962306a36Sopenharmony_ci			 QFWD_SWITCH_PORT_MODE(portno));
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci		/* Disable Disassembler buffer underrun watchdog
53262306a36Sopenharmony_ci		 * to avoid truncated packets in XTR
53362306a36Sopenharmony_ci		 */
53462306a36Sopenharmony_ci		spx5_rmw(DSM_BUF_CFG_UNDERFLOW_WATCHDOG_DIS_SET(1),
53562306a36Sopenharmony_ci			 DSM_BUF_CFG_UNDERFLOW_WATCHDOG_DIS,
53662306a36Sopenharmony_ci			 sparx5,
53762306a36Sopenharmony_ci			 DSM_BUF_CFG(portno));
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci		/* Disabling frame aging */
54062306a36Sopenharmony_ci		spx5_rmw(HSCH_PORT_MODE_AGE_DIS_SET(1),
54162306a36Sopenharmony_ci			 HSCH_PORT_MODE_AGE_DIS,
54262306a36Sopenharmony_ci			 sparx5,
54362306a36Sopenharmony_ci			 HSCH_PORT_MODE(portno));
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ciint sparx5_fdma_start(struct sparx5 *sparx5)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	int err;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	/* Reset FDMA state */
55262306a36Sopenharmony_ci	spx5_wr(FDMA_CTRL_NRESET_SET(0), sparx5, FDMA_CTRL);
55362306a36Sopenharmony_ci	spx5_wr(FDMA_CTRL_NRESET_SET(1), sparx5, FDMA_CTRL);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	/* Force ACP caching but disable read/write allocation */
55662306a36Sopenharmony_ci	spx5_rmw(CPU_PROC_CTRL_ACP_CACHE_FORCE_ENA_SET(1) |
55762306a36Sopenharmony_ci		 CPU_PROC_CTRL_ACP_AWCACHE_SET(0) |
55862306a36Sopenharmony_ci		 CPU_PROC_CTRL_ACP_ARCACHE_SET(0),
55962306a36Sopenharmony_ci		 CPU_PROC_CTRL_ACP_CACHE_FORCE_ENA |
56062306a36Sopenharmony_ci		 CPU_PROC_CTRL_ACP_AWCACHE |
56162306a36Sopenharmony_ci		 CPU_PROC_CTRL_ACP_ARCACHE,
56262306a36Sopenharmony_ci		 sparx5, CPU_PROC_CTRL);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	sparx5_fdma_injection_mode(sparx5);
56562306a36Sopenharmony_ci	sparx5_fdma_rx_init(sparx5, &sparx5->rx, FDMA_XTR_CHANNEL);
56662306a36Sopenharmony_ci	sparx5_fdma_tx_init(sparx5, &sparx5->tx, FDMA_INJ_CHANNEL);
56762306a36Sopenharmony_ci	err = sparx5_fdma_rx_alloc(sparx5);
56862306a36Sopenharmony_ci	if (err) {
56962306a36Sopenharmony_ci		dev_err(sparx5->dev, "Could not allocate RX buffers: %d\n", err);
57062306a36Sopenharmony_ci		return err;
57162306a36Sopenharmony_ci	}
57262306a36Sopenharmony_ci	err = sparx5_fdma_tx_alloc(sparx5);
57362306a36Sopenharmony_ci	if (err) {
57462306a36Sopenharmony_ci		dev_err(sparx5->dev, "Could not allocate TX buffers: %d\n", err);
57562306a36Sopenharmony_ci		return err;
57662306a36Sopenharmony_ci	}
57762306a36Sopenharmony_ci	return err;
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic u32 sparx5_fdma_port_ctrl(struct sparx5 *sparx5)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	return spx5_rd(sparx5, FDMA_PORT_CTRL(0));
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ciint sparx5_fdma_stop(struct sparx5 *sparx5)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	u32 val;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	napi_disable(&sparx5->rx.napi);
59062306a36Sopenharmony_ci	/* Stop the fdma and channel interrupts */
59162306a36Sopenharmony_ci	sparx5_fdma_rx_deactivate(sparx5, &sparx5->rx);
59262306a36Sopenharmony_ci	sparx5_fdma_tx_deactivate(sparx5, &sparx5->tx);
59362306a36Sopenharmony_ci	/* Wait for the RX channel to stop */
59462306a36Sopenharmony_ci	read_poll_timeout(sparx5_fdma_port_ctrl, val,
59562306a36Sopenharmony_ci			  FDMA_PORT_CTRL_XTR_BUF_IS_EMPTY_GET(val) == 0,
59662306a36Sopenharmony_ci			  500, 10000, 0, sparx5);
59762306a36Sopenharmony_ci	return 0;
59862306a36Sopenharmony_ci}
599