xref: /openbmc/linux/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
110615907SSteen Hegelund // SPDX-License-Identifier: GPL-2.0+
210615907SSteen Hegelund /* Microchip Sparx5 Switch driver
310615907SSteen Hegelund  *
410615907SSteen Hegelund  * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
510615907SSteen Hegelund  *
610615907SSteen Hegelund  * The Sparx5 Chip Register Model can be browsed at this location:
710615907SSteen Hegelund  * https://github.com/microchip-ung/sparx-5_reginfo
810615907SSteen Hegelund  */
910615907SSteen Hegelund 
1010615907SSteen Hegelund #include <linux/types.h>
1110615907SSteen Hegelund #include <linux/skbuff.h>
1210615907SSteen Hegelund #include <linux/netdevice.h>
1310615907SSteen Hegelund #include <linux/interrupt.h>
1410615907SSteen Hegelund #include <linux/ip.h>
1510615907SSteen Hegelund #include <linux/dma-mapping.h>
1610615907SSteen Hegelund 
1710615907SSteen Hegelund #include "sparx5_main_regs.h"
1810615907SSteen Hegelund #include "sparx5_main.h"
1910615907SSteen Hegelund #include "sparx5_port.h"
2010615907SSteen Hegelund 
2110615907SSteen Hegelund #define FDMA_XTR_CHANNEL		6
2210615907SSteen Hegelund #define FDMA_INJ_CHANNEL		0
2310615907SSteen Hegelund 
2410615907SSteen Hegelund #define FDMA_DCB_INFO_DATAL(x)		((x) & GENMASK(15, 0))
2510615907SSteen Hegelund #define FDMA_DCB_INFO_TOKEN		BIT(17)
2610615907SSteen Hegelund #define FDMA_DCB_INFO_INTR		BIT(18)
2710615907SSteen Hegelund #define FDMA_DCB_INFO_SW(x)		(((x) << 24) & GENMASK(31, 24))
2810615907SSteen Hegelund 
2910615907SSteen Hegelund #define FDMA_DCB_STATUS_BLOCKL(x)	((x) & GENMASK(15, 0))
3010615907SSteen Hegelund #define FDMA_DCB_STATUS_SOF		BIT(16)
3110615907SSteen Hegelund #define FDMA_DCB_STATUS_EOF		BIT(17)
3210615907SSteen Hegelund #define FDMA_DCB_STATUS_INTR		BIT(18)
3310615907SSteen Hegelund #define FDMA_DCB_STATUS_DONE		BIT(19)
3410615907SSteen Hegelund #define FDMA_DCB_STATUS_BLOCKO(x)	(((x) << 20) & GENMASK(31, 20))
3510615907SSteen Hegelund #define FDMA_DCB_INVALID_DATA		0x1
3610615907SSteen Hegelund 
3710615907SSteen Hegelund #define FDMA_XTR_BUFFER_SIZE		2048
3810615907SSteen Hegelund #define FDMA_WEIGHT			4
3910615907SSteen Hegelund 
4010615907SSteen Hegelund /* Frame DMA DCB format
4110615907SSteen Hegelund  *
4210615907SSteen Hegelund  * +---------------------------+
4310615907SSteen Hegelund  * |         Next Ptr          |
4410615907SSteen Hegelund  * +---------------------------+
4510615907SSteen Hegelund  * |   Reserved  |    Info     |
4610615907SSteen Hegelund  * +---------------------------+
4710615907SSteen Hegelund  * |         Data0 Ptr         |
4810615907SSteen Hegelund  * +---------------------------+
4910615907SSteen Hegelund  * |   Reserved  |    Status0  |
5010615907SSteen Hegelund  * +---------------------------+
5110615907SSteen Hegelund  * |         Data1 Ptr         |
5210615907SSteen Hegelund  * +---------------------------+
5310615907SSteen Hegelund  * |   Reserved  |    Status1  |
5410615907SSteen Hegelund  * +---------------------------+
5510615907SSteen Hegelund  * |         Data2 Ptr         |
5610615907SSteen Hegelund  * +---------------------------+
5710615907SSteen Hegelund  * |   Reserved  |    Status2  |
5810615907SSteen Hegelund  * |-------------|-------------|
5910615907SSteen Hegelund  * |                           |
6010615907SSteen Hegelund  * |                           |
6110615907SSteen Hegelund  * |                           |
6210615907SSteen Hegelund  * |                           |
6310615907SSteen Hegelund  * |                           |
6410615907SSteen Hegelund  * |---------------------------|
6510615907SSteen Hegelund  * |         Data14 Ptr        |
6610615907SSteen Hegelund  * +-------------|-------------+
6710615907SSteen Hegelund  * |   Reserved  |    Status14 |
6810615907SSteen Hegelund  * +-------------|-------------+
6910615907SSteen Hegelund  */
7010615907SSteen Hegelund 
7110615907SSteen Hegelund /* For each hardware DB there is an entry in this list and when the HW DB
7210615907SSteen Hegelund  * entry is used, this SW DB entry is moved to the back of the list
7310615907SSteen Hegelund  */
7410615907SSteen Hegelund struct sparx5_db {
7510615907SSteen Hegelund 	struct list_head list;
7610615907SSteen Hegelund 	void *cpu_addr;
7710615907SSteen Hegelund };
7810615907SSteen Hegelund 
sparx5_fdma_rx_add_dcb(struct sparx5_rx * rx,struct sparx5_rx_dcb_hw * dcb,u64 nextptr)7910615907SSteen Hegelund static void sparx5_fdma_rx_add_dcb(struct sparx5_rx *rx,
8010615907SSteen Hegelund 				   struct sparx5_rx_dcb_hw *dcb,
8110615907SSteen Hegelund 				   u64 nextptr)
8210615907SSteen Hegelund {
8310615907SSteen Hegelund 	int idx = 0;
8410615907SSteen Hegelund 
8510615907SSteen Hegelund 	/* Reset the status of the DB */
8610615907SSteen Hegelund 	for (idx = 0; idx < FDMA_RX_DCB_MAX_DBS; ++idx) {
8710615907SSteen Hegelund 		struct sparx5_db_hw *db = &dcb->db[idx];
8810615907SSteen Hegelund 
8910615907SSteen Hegelund 		db->status = FDMA_DCB_STATUS_INTR;
9010615907SSteen Hegelund 	}
9110615907SSteen Hegelund 	dcb->nextptr = FDMA_DCB_INVALID_DATA;
9210615907SSteen Hegelund 	dcb->info = FDMA_DCB_INFO_DATAL(FDMA_XTR_BUFFER_SIZE);
9310615907SSteen Hegelund 	rx->last_entry->nextptr = nextptr;
9410615907SSteen Hegelund 	rx->last_entry = dcb;
9510615907SSteen Hegelund }
9610615907SSteen Hegelund 
sparx5_fdma_tx_add_dcb(struct sparx5_tx * tx,struct sparx5_tx_dcb_hw * dcb,u64 nextptr)9710615907SSteen Hegelund static void sparx5_fdma_tx_add_dcb(struct sparx5_tx *tx,
9810615907SSteen Hegelund 				   struct sparx5_tx_dcb_hw *dcb,
9910615907SSteen Hegelund 				   u64 nextptr)
10010615907SSteen Hegelund {
10110615907SSteen Hegelund 	int idx = 0;
10210615907SSteen Hegelund 
10310615907SSteen Hegelund 	/* Reset the status of the DB */
10410615907SSteen Hegelund 	for (idx = 0; idx < FDMA_TX_DCB_MAX_DBS; ++idx) {
10510615907SSteen Hegelund 		struct sparx5_db_hw *db = &dcb->db[idx];
10610615907SSteen Hegelund 
10710615907SSteen Hegelund 		db->status = FDMA_DCB_STATUS_DONE;
10810615907SSteen Hegelund 	}
10910615907SSteen Hegelund 	dcb->nextptr = FDMA_DCB_INVALID_DATA;
11010615907SSteen Hegelund 	dcb->info = FDMA_DCB_INFO_DATAL(FDMA_XTR_BUFFER_SIZE);
11110615907SSteen Hegelund }
11210615907SSteen Hegelund 
sparx5_fdma_rx_activate(struct sparx5 * sparx5,struct sparx5_rx * rx)11310615907SSteen Hegelund static void sparx5_fdma_rx_activate(struct sparx5 *sparx5, struct sparx5_rx *rx)
11410615907SSteen Hegelund {
11510615907SSteen Hegelund 	/* Write the buffer address in the LLP and LLP1 regs */
11610615907SSteen Hegelund 	spx5_wr(((u64)rx->dma) & GENMASK(31, 0), sparx5,
11710615907SSteen Hegelund 		FDMA_DCB_LLP(rx->channel_id));
11810615907SSteen Hegelund 	spx5_wr(((u64)rx->dma) >> 32, sparx5, FDMA_DCB_LLP1(rx->channel_id));
11910615907SSteen Hegelund 
12010615907SSteen Hegelund 	/* Set the number of RX DBs to be used, and DB end-of-frame interrupt */
12110615907SSteen Hegelund 	spx5_wr(FDMA_CH_CFG_CH_DCB_DB_CNT_SET(FDMA_RX_DCB_MAX_DBS) |
12210615907SSteen Hegelund 		FDMA_CH_CFG_CH_INTR_DB_EOF_ONLY_SET(1) |
12310615907SSteen Hegelund 		FDMA_CH_CFG_CH_INJ_PORT_SET(XTR_QUEUE),
12410615907SSteen Hegelund 		sparx5, FDMA_CH_CFG(rx->channel_id));
12510615907SSteen Hegelund 
12610615907SSteen Hegelund 	/* Set the RX Watermark to max */
12710615907SSteen Hegelund 	spx5_rmw(FDMA_XTR_CFG_XTR_FIFO_WM_SET(31), FDMA_XTR_CFG_XTR_FIFO_WM,
12810615907SSteen Hegelund 		 sparx5,
12910615907SSteen Hegelund 		 FDMA_XTR_CFG);
13010615907SSteen Hegelund 
13110615907SSteen Hegelund 	/* Start RX fdma */
13210615907SSteen Hegelund 	spx5_rmw(FDMA_PORT_CTRL_XTR_STOP_SET(0), FDMA_PORT_CTRL_XTR_STOP,
13310615907SSteen Hegelund 		 sparx5, FDMA_PORT_CTRL(0));
13410615907SSteen Hegelund 
13510615907SSteen Hegelund 	/* Enable RX channel DB interrupt */
13610615907SSteen Hegelund 	spx5_rmw(BIT(rx->channel_id),
13710615907SSteen Hegelund 		 BIT(rx->channel_id) & FDMA_INTR_DB_ENA_INTR_DB_ENA,
13810615907SSteen Hegelund 		 sparx5, FDMA_INTR_DB_ENA);
13910615907SSteen Hegelund 
14010615907SSteen Hegelund 	/* Activate the RX channel */
14110615907SSteen Hegelund 	spx5_wr(BIT(rx->channel_id), sparx5, FDMA_CH_ACTIVATE);
14210615907SSteen Hegelund }
14310615907SSteen Hegelund 
sparx5_fdma_rx_deactivate(struct sparx5 * sparx5,struct sparx5_rx * rx)14410615907SSteen Hegelund static void sparx5_fdma_rx_deactivate(struct sparx5 *sparx5, struct sparx5_rx *rx)
14510615907SSteen Hegelund {
14610615907SSteen Hegelund 	/* Dectivate the RX channel */
14710615907SSteen Hegelund 	spx5_rmw(0, BIT(rx->channel_id) & FDMA_CH_ACTIVATE_CH_ACTIVATE,
14810615907SSteen Hegelund 		 sparx5, FDMA_CH_ACTIVATE);
14910615907SSteen Hegelund 
15010615907SSteen Hegelund 	/* Disable RX channel DB interrupt */
15110615907SSteen Hegelund 	spx5_rmw(0, BIT(rx->channel_id) & FDMA_INTR_DB_ENA_INTR_DB_ENA,
15210615907SSteen Hegelund 		 sparx5, FDMA_INTR_DB_ENA);
15310615907SSteen Hegelund 
15410615907SSteen Hegelund 	/* Stop RX fdma */
15510615907SSteen Hegelund 	spx5_rmw(FDMA_PORT_CTRL_XTR_STOP_SET(1), FDMA_PORT_CTRL_XTR_STOP,
15610615907SSteen Hegelund 		 sparx5, FDMA_PORT_CTRL(0));
15710615907SSteen Hegelund }
15810615907SSteen Hegelund 
sparx5_fdma_tx_activate(struct sparx5 * sparx5,struct sparx5_tx * tx)15910615907SSteen Hegelund static void sparx5_fdma_tx_activate(struct sparx5 *sparx5, struct sparx5_tx *tx)
16010615907SSteen Hegelund {
16110615907SSteen Hegelund 	/* Write the buffer address in the LLP and LLP1 regs */
16210615907SSteen Hegelund 	spx5_wr(((u64)tx->dma) & GENMASK(31, 0), sparx5,
16310615907SSteen Hegelund 		FDMA_DCB_LLP(tx->channel_id));
16410615907SSteen Hegelund 	spx5_wr(((u64)tx->dma) >> 32, sparx5, FDMA_DCB_LLP1(tx->channel_id));
16510615907SSteen Hegelund 
16610615907SSteen Hegelund 	/* Set the number of TX DBs to be used, and DB end-of-frame interrupt */
16710615907SSteen Hegelund 	spx5_wr(FDMA_CH_CFG_CH_DCB_DB_CNT_SET(FDMA_TX_DCB_MAX_DBS) |
16810615907SSteen Hegelund 		FDMA_CH_CFG_CH_INTR_DB_EOF_ONLY_SET(1) |
16910615907SSteen Hegelund 		FDMA_CH_CFG_CH_INJ_PORT_SET(INJ_QUEUE),
17010615907SSteen Hegelund 		sparx5, FDMA_CH_CFG(tx->channel_id));
17110615907SSteen Hegelund 
17210615907SSteen Hegelund 	/* Start TX fdma */
17310615907SSteen Hegelund 	spx5_rmw(FDMA_PORT_CTRL_INJ_STOP_SET(0), FDMA_PORT_CTRL_INJ_STOP,
17410615907SSteen Hegelund 		 sparx5, FDMA_PORT_CTRL(0));
17510615907SSteen Hegelund 
17610615907SSteen Hegelund 	/* Activate the channel */
17710615907SSteen Hegelund 	spx5_wr(BIT(tx->channel_id), sparx5, FDMA_CH_ACTIVATE);
17810615907SSteen Hegelund }
17910615907SSteen Hegelund 
sparx5_fdma_tx_deactivate(struct sparx5 * sparx5,struct sparx5_tx * tx)18010615907SSteen Hegelund static void sparx5_fdma_tx_deactivate(struct sparx5 *sparx5, struct sparx5_tx *tx)
18110615907SSteen Hegelund {
18210615907SSteen Hegelund 	/* Disable the channel */
18310615907SSteen Hegelund 	spx5_rmw(0, BIT(tx->channel_id) & FDMA_CH_ACTIVATE_CH_ACTIVATE,
18410615907SSteen Hegelund 		 sparx5, FDMA_CH_ACTIVATE);
18510615907SSteen Hegelund }
18610615907SSteen Hegelund 
sparx5_fdma_rx_reload(struct sparx5 * sparx5,struct sparx5_rx * rx)18710615907SSteen Hegelund static void sparx5_fdma_rx_reload(struct sparx5 *sparx5, struct sparx5_rx *rx)
18810615907SSteen Hegelund {
18910615907SSteen Hegelund 	/* Reload the RX channel */
19010615907SSteen Hegelund 	spx5_wr(BIT(rx->channel_id), sparx5, FDMA_CH_RELOAD);
19110615907SSteen Hegelund }
19210615907SSteen Hegelund 
sparx5_fdma_tx_reload(struct sparx5 * sparx5,struct sparx5_tx * tx)19310615907SSteen Hegelund static void sparx5_fdma_tx_reload(struct sparx5 *sparx5, struct sparx5_tx *tx)
19410615907SSteen Hegelund {
19510615907SSteen Hegelund 	/* Reload the TX channel */
19610615907SSteen Hegelund 	spx5_wr(BIT(tx->channel_id), sparx5, FDMA_CH_RELOAD);
19710615907SSteen Hegelund }
19810615907SSteen Hegelund 
sparx5_fdma_rx_alloc_skb(struct sparx5_rx * rx)19910615907SSteen Hegelund static struct sk_buff *sparx5_fdma_rx_alloc_skb(struct sparx5_rx *rx)
20010615907SSteen Hegelund {
20110615907SSteen Hegelund 	return __netdev_alloc_skb(rx->ndev, FDMA_XTR_BUFFER_SIZE,
20210615907SSteen Hegelund 				  GFP_ATOMIC);
20310615907SSteen Hegelund }
20410615907SSteen Hegelund 
sparx5_fdma_rx_get_frame(struct sparx5 * sparx5,struct sparx5_rx * rx)20510615907SSteen Hegelund static bool sparx5_fdma_rx_get_frame(struct sparx5 *sparx5, struct sparx5_rx *rx)
20610615907SSteen Hegelund {
20710615907SSteen Hegelund 	struct sparx5_db_hw *db_hw;
20810615907SSteen Hegelund 	unsigned int packet_size;
20910615907SSteen Hegelund 	struct sparx5_port *port;
21010615907SSteen Hegelund 	struct sk_buff *new_skb;
21110615907SSteen Hegelund 	struct frame_info fi;
21210615907SSteen Hegelund 	struct sk_buff *skb;
21310615907SSteen Hegelund 	dma_addr_t dma_addr;
21410615907SSteen Hegelund 
21510615907SSteen Hegelund 	/* Check if the DCB is done */
21610615907SSteen Hegelund 	db_hw = &rx->dcb_entries[rx->dcb_index].db[rx->db_index];
21710615907SSteen Hegelund 	if (unlikely(!(db_hw->status & FDMA_DCB_STATUS_DONE)))
21810615907SSteen Hegelund 		return false;
21910615907SSteen Hegelund 	skb = rx->skb[rx->dcb_index][rx->db_index];
22010615907SSteen Hegelund 	/* Replace the DB entry with a new SKB */
22110615907SSteen Hegelund 	new_skb = sparx5_fdma_rx_alloc_skb(rx);
22210615907SSteen Hegelund 	if (unlikely(!new_skb))
22310615907SSteen Hegelund 		return false;
22410615907SSteen Hegelund 	/* Map the new skb data and set the new skb */
22510615907SSteen Hegelund 	dma_addr = virt_to_phys(new_skb->data);
22610615907SSteen Hegelund 	rx->skb[rx->dcb_index][rx->db_index] = new_skb;
22710615907SSteen Hegelund 	db_hw->dataptr = dma_addr;
22810615907SSteen Hegelund 	packet_size = FDMA_DCB_STATUS_BLOCKL(db_hw->status);
22910615907SSteen Hegelund 	skb_put(skb, packet_size);
23010615907SSteen Hegelund 	/* Now do the normal processing of the skb */
23110615907SSteen Hegelund 	sparx5_ifh_parse((u32 *)skb->data, &fi);
23210615907SSteen Hegelund 	/* Map to port netdev */
23310615907SSteen Hegelund 	port = fi.src_port < SPX5_PORTS ?  sparx5->ports[fi.src_port] : NULL;
23410615907SSteen Hegelund 	if (!port || !port->ndev) {
23510615907SSteen Hegelund 		dev_err(sparx5->dev, "Data on inactive port %d\n", fi.src_port);
23610615907SSteen Hegelund 		sparx5_xtr_flush(sparx5, XTR_QUEUE);
23710615907SSteen Hegelund 		return false;
23810615907SSteen Hegelund 	}
23910615907SSteen Hegelund 	skb->dev = port->ndev;
24010615907SSteen Hegelund 	skb_pull(skb, IFH_LEN * sizeof(u32));
24110615907SSteen Hegelund 	if (likely(!(skb->dev->features & NETIF_F_RXFCS)))
24210615907SSteen Hegelund 		skb_trim(skb, skb->len - ETH_FCS_LEN);
24370dfe25cSHoratiu Vultur 
24470dfe25cSHoratiu Vultur 	sparx5_ptp_rxtstamp(sparx5, skb, fi.timestamp);
24510615907SSteen Hegelund 	skb->protocol = eth_type_trans(skb, skb->dev);
24610615907SSteen Hegelund 	/* Everything we see on an interface that is in the HW bridge
24710615907SSteen Hegelund 	 * has already been forwarded
24810615907SSteen Hegelund 	 */
24910615907SSteen Hegelund 	if (test_bit(port->portno, sparx5->bridge_mask))
25010615907SSteen Hegelund 		skb->offload_fwd_mark = 1;
25110615907SSteen Hegelund 	skb->dev->stats.rx_bytes += skb->len;
25210615907SSteen Hegelund 	skb->dev->stats.rx_packets++;
25310615907SSteen Hegelund 	rx->packets++;
25410615907SSteen Hegelund 	netif_receive_skb(skb);
25510615907SSteen Hegelund 	return true;
25610615907SSteen Hegelund }
25710615907SSteen Hegelund 
sparx5_fdma_napi_callback(struct napi_struct * napi,int weight)25810615907SSteen Hegelund static int sparx5_fdma_napi_callback(struct napi_struct *napi, int weight)
25910615907SSteen Hegelund {
26010615907SSteen Hegelund 	struct sparx5_rx *rx = container_of(napi, struct sparx5_rx, napi);
26110615907SSteen Hegelund 	struct sparx5 *sparx5 = container_of(rx, struct sparx5, rx);
26210615907SSteen Hegelund 	int counter = 0;
26310615907SSteen Hegelund 
26410615907SSteen Hegelund 	while (counter < weight && sparx5_fdma_rx_get_frame(sparx5, rx)) {
26510615907SSteen Hegelund 		struct sparx5_rx_dcb_hw *old_dcb;
26610615907SSteen Hegelund 
26710615907SSteen Hegelund 		rx->db_index++;
26810615907SSteen Hegelund 		counter++;
26910615907SSteen Hegelund 		/* Check if the DCB can be reused */
27010615907SSteen Hegelund 		if (rx->db_index != FDMA_RX_DCB_MAX_DBS)
27110615907SSteen Hegelund 			continue;
27210615907SSteen Hegelund 		/* As the DCB  can be reused, just advance the dcb_index
27310615907SSteen Hegelund 		 * pointer and set the nextptr in the DCB
27410615907SSteen Hegelund 		 */
27510615907SSteen Hegelund 		rx->db_index = 0;
27610615907SSteen Hegelund 		old_dcb = &rx->dcb_entries[rx->dcb_index];
27710615907SSteen Hegelund 		rx->dcb_index++;
27810615907SSteen Hegelund 		rx->dcb_index &= FDMA_DCB_MAX - 1;
27910615907SSteen Hegelund 		sparx5_fdma_rx_add_dcb(rx, old_dcb,
28010615907SSteen Hegelund 				       rx->dma +
28110615907SSteen Hegelund 				       ((unsigned long)old_dcb -
28210615907SSteen Hegelund 					(unsigned long)rx->dcb_entries));
28310615907SSteen Hegelund 	}
28410615907SSteen Hegelund 	if (counter < weight) {
28510615907SSteen Hegelund 		napi_complete_done(&rx->napi, counter);
28610615907SSteen Hegelund 		spx5_rmw(BIT(rx->channel_id),
28710615907SSteen Hegelund 			 BIT(rx->channel_id) & FDMA_INTR_DB_ENA_INTR_DB_ENA,
28810615907SSteen Hegelund 			 sparx5, FDMA_INTR_DB_ENA);
28910615907SSteen Hegelund 	}
29010615907SSteen Hegelund 	if (counter)
29110615907SSteen Hegelund 		sparx5_fdma_rx_reload(sparx5, rx);
29210615907SSteen Hegelund 	return counter;
29310615907SSteen Hegelund }
29410615907SSteen Hegelund 
sparx5_fdma_next_dcb(struct sparx5_tx * tx,struct sparx5_tx_dcb_hw * dcb)29510615907SSteen Hegelund static struct sparx5_tx_dcb_hw *sparx5_fdma_next_dcb(struct sparx5_tx *tx,
29610615907SSteen Hegelund 						     struct sparx5_tx_dcb_hw *dcb)
29710615907SSteen Hegelund {
29810615907SSteen Hegelund 	struct sparx5_tx_dcb_hw *next_dcb;
29910615907SSteen Hegelund 
30010615907SSteen Hegelund 	next_dcb = dcb;
30110615907SSteen Hegelund 	next_dcb++;
30210615907SSteen Hegelund 	/* Handle wrap-around */
30310615907SSteen Hegelund 	if ((unsigned long)next_dcb >=
30410615907SSteen Hegelund 	    ((unsigned long)tx->first_entry + FDMA_DCB_MAX * sizeof(*dcb)))
30510615907SSteen Hegelund 		next_dcb = tx->first_entry;
30610615907SSteen Hegelund 	return next_dcb;
30710615907SSteen Hegelund }
30810615907SSteen Hegelund 
sparx5_fdma_xmit(struct sparx5 * sparx5,u32 * ifh,struct sk_buff * skb)30910615907SSteen Hegelund int sparx5_fdma_xmit(struct sparx5 *sparx5, u32 *ifh, struct sk_buff *skb)
31010615907SSteen Hegelund {
31110615907SSteen Hegelund 	struct sparx5_tx_dcb_hw *next_dcb_hw;
31210615907SSteen Hegelund 	struct sparx5_tx *tx = &sparx5->tx;
31310615907SSteen Hegelund 	static bool first_time = true;
31410615907SSteen Hegelund 	struct sparx5_db_hw *db_hw;
31510615907SSteen Hegelund 	struct sparx5_db *db;
31610615907SSteen Hegelund 
31710615907SSteen Hegelund 	next_dcb_hw = sparx5_fdma_next_dcb(tx, tx->curr_entry);
31810615907SSteen Hegelund 	db_hw = &next_dcb_hw->db[0];
31910615907SSteen Hegelund 	if (!(db_hw->status & FDMA_DCB_STATUS_DONE))
320*121c6672SCasper Andersson 		return -EINVAL;
32110615907SSteen Hegelund 	db = list_first_entry(&tx->db_list, struct sparx5_db, list);
32210615907SSteen Hegelund 	list_move_tail(&db->list, &tx->db_list);
32310615907SSteen Hegelund 	next_dcb_hw->nextptr = FDMA_DCB_INVALID_DATA;
32410615907SSteen Hegelund 	tx->curr_entry->nextptr = tx->dma +
32510615907SSteen Hegelund 		((unsigned long)next_dcb_hw -
32610615907SSteen Hegelund 		 (unsigned long)tx->first_entry);
32710615907SSteen Hegelund 	tx->curr_entry = next_dcb_hw;
32810615907SSteen Hegelund 	memset(db->cpu_addr, 0, FDMA_XTR_BUFFER_SIZE);
32910615907SSteen Hegelund 	memcpy(db->cpu_addr, ifh, IFH_LEN * 4);
33010615907SSteen Hegelund 	memcpy(db->cpu_addr + IFH_LEN * 4, skb->data, skb->len);
33110615907SSteen Hegelund 	db_hw->status = FDMA_DCB_STATUS_SOF |
33210615907SSteen Hegelund 			FDMA_DCB_STATUS_EOF |
33310615907SSteen Hegelund 			FDMA_DCB_STATUS_BLOCKO(0) |
33410615907SSteen Hegelund 			FDMA_DCB_STATUS_BLOCKL(skb->len + IFH_LEN * 4 + 4);
33510615907SSteen Hegelund 	if (first_time) {
33610615907SSteen Hegelund 		sparx5_fdma_tx_activate(sparx5, tx);
33710615907SSteen Hegelund 		first_time = false;
33810615907SSteen Hegelund 	} else {
33910615907SSteen Hegelund 		sparx5_fdma_tx_reload(sparx5, tx);
34010615907SSteen Hegelund 	}
34110615907SSteen Hegelund 	return NETDEV_TX_OK;
34210615907SSteen Hegelund }
34310615907SSteen Hegelund 
sparx5_fdma_rx_alloc(struct sparx5 * sparx5)34410615907SSteen Hegelund static int sparx5_fdma_rx_alloc(struct sparx5 *sparx5)
34510615907SSteen Hegelund {
34610615907SSteen Hegelund 	struct sparx5_rx *rx = &sparx5->rx;
34710615907SSteen Hegelund 	struct sparx5_rx_dcb_hw *dcb;
34810615907SSteen Hegelund 	int idx, jdx;
34910615907SSteen Hegelund 	int size;
35010615907SSteen Hegelund 
35110615907SSteen Hegelund 	size = sizeof(struct sparx5_rx_dcb_hw) * FDMA_DCB_MAX;
35210615907SSteen Hegelund 	size = ALIGN(size, PAGE_SIZE);
35310615907SSteen Hegelund 	rx->dcb_entries = devm_kzalloc(sparx5->dev, size, GFP_KERNEL);
35410615907SSteen Hegelund 	if (!rx->dcb_entries)
35510615907SSteen Hegelund 		return -ENOMEM;
35610615907SSteen Hegelund 	rx->dma = virt_to_phys(rx->dcb_entries);
35710615907SSteen Hegelund 	rx->last_entry = rx->dcb_entries;
35810615907SSteen Hegelund 	rx->db_index = 0;
35910615907SSteen Hegelund 	rx->dcb_index = 0;
36010615907SSteen Hegelund 	/* Now for each dcb allocate the db */
36110615907SSteen Hegelund 	for (idx = 0; idx < FDMA_DCB_MAX; ++idx) {
36210615907SSteen Hegelund 		dcb = &rx->dcb_entries[idx];
36310615907SSteen Hegelund 		dcb->info = 0;
36410615907SSteen Hegelund 		/* For each db allocate an skb and map skb data pointer to the DB
36510615907SSteen Hegelund 		 * dataptr. In this way when the frame is received the skb->data
36610615907SSteen Hegelund 		 * will contain the frame, so no memcpy is needed
36710615907SSteen Hegelund 		 */
36810615907SSteen Hegelund 		for (jdx = 0; jdx < FDMA_RX_DCB_MAX_DBS; ++jdx) {
36910615907SSteen Hegelund 			struct sparx5_db_hw *db_hw = &dcb->db[jdx];
37010615907SSteen Hegelund 			dma_addr_t dma_addr;
37110615907SSteen Hegelund 			struct sk_buff *skb;
37210615907SSteen Hegelund 
37310615907SSteen Hegelund 			skb = sparx5_fdma_rx_alloc_skb(rx);
37410615907SSteen Hegelund 			if (!skb)
37510615907SSteen Hegelund 				return -ENOMEM;
37610615907SSteen Hegelund 
37710615907SSteen Hegelund 			dma_addr = virt_to_phys(skb->data);
37810615907SSteen Hegelund 			db_hw->dataptr = dma_addr;
37910615907SSteen Hegelund 			db_hw->status = 0;
38010615907SSteen Hegelund 			rx->skb[idx][jdx] = skb;
38110615907SSteen Hegelund 		}
38210615907SSteen Hegelund 		sparx5_fdma_rx_add_dcb(rx, dcb, rx->dma + sizeof(*dcb) * idx);
38310615907SSteen Hegelund 	}
384b707b89fSJakub Kicinski 	netif_napi_add_weight(rx->ndev, &rx->napi, sparx5_fdma_napi_callback,
385b707b89fSJakub Kicinski 			      FDMA_WEIGHT);
38610615907SSteen Hegelund 	napi_enable(&rx->napi);
38710615907SSteen Hegelund 	sparx5_fdma_rx_activate(sparx5, rx);
38810615907SSteen Hegelund 	return 0;
38910615907SSteen Hegelund }
39010615907SSteen Hegelund 
sparx5_fdma_tx_alloc(struct sparx5 * sparx5)39110615907SSteen Hegelund static int sparx5_fdma_tx_alloc(struct sparx5 *sparx5)
39210615907SSteen Hegelund {
39310615907SSteen Hegelund 	struct sparx5_tx *tx = &sparx5->tx;
39410615907SSteen Hegelund 	struct sparx5_tx_dcb_hw *dcb;
39510615907SSteen Hegelund 	int idx, jdx;
39610615907SSteen Hegelund 	int size;
39710615907SSteen Hegelund 
39810615907SSteen Hegelund 	size = sizeof(struct sparx5_tx_dcb_hw) * FDMA_DCB_MAX;
39910615907SSteen Hegelund 	size = ALIGN(size, PAGE_SIZE);
40010615907SSteen Hegelund 	tx->curr_entry = devm_kzalloc(sparx5->dev, size, GFP_KERNEL);
40110615907SSteen Hegelund 	if (!tx->curr_entry)
40210615907SSteen Hegelund 		return -ENOMEM;
40310615907SSteen Hegelund 	tx->dma = virt_to_phys(tx->curr_entry);
40410615907SSteen Hegelund 	tx->first_entry = tx->curr_entry;
40510615907SSteen Hegelund 	INIT_LIST_HEAD(&tx->db_list);
40610615907SSteen Hegelund 	/* Now for each dcb allocate the db */
40710615907SSteen Hegelund 	for (idx = 0; idx < FDMA_DCB_MAX; ++idx) {
40810615907SSteen Hegelund 		dcb = &tx->curr_entry[idx];
40910615907SSteen Hegelund 		dcb->info = 0;
41010615907SSteen Hegelund 		/* TX databuffers must be 16byte aligned */
41110615907SSteen Hegelund 		for (jdx = 0; jdx < FDMA_TX_DCB_MAX_DBS; ++jdx) {
41210615907SSteen Hegelund 			struct sparx5_db_hw *db_hw = &dcb->db[jdx];
41310615907SSteen Hegelund 			struct sparx5_db *db;
41410615907SSteen Hegelund 			dma_addr_t phys;
41510615907SSteen Hegelund 			void *cpu_addr;
41610615907SSteen Hegelund 
41710615907SSteen Hegelund 			cpu_addr = devm_kzalloc(sparx5->dev,
41810615907SSteen Hegelund 						FDMA_XTR_BUFFER_SIZE,
41910615907SSteen Hegelund 						GFP_KERNEL);
42010615907SSteen Hegelund 			if (!cpu_addr)
42110615907SSteen Hegelund 				return -ENOMEM;
42210615907SSteen Hegelund 			phys = virt_to_phys(cpu_addr);
42310615907SSteen Hegelund 			db_hw->dataptr = phys;
42410615907SSteen Hegelund 			db_hw->status = 0;
42510615907SSteen Hegelund 			db = devm_kzalloc(sparx5->dev, sizeof(*db), GFP_KERNEL);
4260906f3a3SZheng Yongjun 			if (!db)
4270906f3a3SZheng Yongjun 				return -ENOMEM;
42810615907SSteen Hegelund 			db->cpu_addr = cpu_addr;
42910615907SSteen Hegelund 			list_add_tail(&db->list, &tx->db_list);
43010615907SSteen Hegelund 		}
43110615907SSteen Hegelund 		sparx5_fdma_tx_add_dcb(tx, dcb, tx->dma + sizeof(*dcb) * idx);
43210615907SSteen Hegelund 		/* Let the curr_entry to point to the last allocated entry */
43310615907SSteen Hegelund 		if (idx == FDMA_DCB_MAX - 1)
43410615907SSteen Hegelund 			tx->curr_entry = dcb;
43510615907SSteen Hegelund 	}
43610615907SSteen Hegelund 	return 0;
43710615907SSteen Hegelund }
43810615907SSteen Hegelund 
sparx5_fdma_rx_init(struct sparx5 * sparx5,struct sparx5_rx * rx,int channel)43910615907SSteen Hegelund static void sparx5_fdma_rx_init(struct sparx5 *sparx5,
44010615907SSteen Hegelund 				struct sparx5_rx *rx, int channel)
44110615907SSteen Hegelund {
44210615907SSteen Hegelund 	int idx;
44310615907SSteen Hegelund 
44410615907SSteen Hegelund 	rx->channel_id = channel;
44510615907SSteen Hegelund 	/* Fetch a netdev for SKB and NAPI use, any will do */
44610615907SSteen Hegelund 	for (idx = 0; idx < SPX5_PORTS; ++idx) {
44710615907SSteen Hegelund 		struct sparx5_port *port = sparx5->ports[idx];
44810615907SSteen Hegelund 
44910615907SSteen Hegelund 		if (port && port->ndev) {
45010615907SSteen Hegelund 			rx->ndev = port->ndev;
45110615907SSteen Hegelund 			break;
45210615907SSteen Hegelund 		}
45310615907SSteen Hegelund 	}
45410615907SSteen Hegelund }
45510615907SSteen Hegelund 
sparx5_fdma_tx_init(struct sparx5 * sparx5,struct sparx5_tx * tx,int channel)45610615907SSteen Hegelund static void sparx5_fdma_tx_init(struct sparx5 *sparx5,
45710615907SSteen Hegelund 				struct sparx5_tx *tx, int channel)
45810615907SSteen Hegelund {
45910615907SSteen Hegelund 	tx->channel_id = channel;
46010615907SSteen Hegelund }
46110615907SSteen Hegelund 
sparx5_fdma_handler(int irq,void * args)46210615907SSteen Hegelund irqreturn_t sparx5_fdma_handler(int irq, void *args)
46310615907SSteen Hegelund {
46410615907SSteen Hegelund 	struct sparx5 *sparx5 = args;
46510615907SSteen Hegelund 	u32 db = 0, err = 0;
46610615907SSteen Hegelund 
46710615907SSteen Hegelund 	db = spx5_rd(sparx5, FDMA_INTR_DB);
46810615907SSteen Hegelund 	err = spx5_rd(sparx5, FDMA_INTR_ERR);
46910615907SSteen Hegelund 	/* Clear interrupt */
47010615907SSteen Hegelund 	if (db) {
47110615907SSteen Hegelund 		spx5_wr(0, sparx5, FDMA_INTR_DB_ENA);
47210615907SSteen Hegelund 		spx5_wr(db, sparx5, FDMA_INTR_DB);
47310615907SSteen Hegelund 		napi_schedule(&sparx5->rx.napi);
47410615907SSteen Hegelund 	}
47510615907SSteen Hegelund 	if (err) {
47610615907SSteen Hegelund 		u32 err_type = spx5_rd(sparx5, FDMA_ERRORS);
47710615907SSteen Hegelund 
47810615907SSteen Hegelund 		dev_err_ratelimited(sparx5->dev,
47910615907SSteen Hegelund 				    "ERR: int: %#x, type: %#x\n",
48010615907SSteen Hegelund 				    err, err_type);
48110615907SSteen Hegelund 		spx5_wr(err, sparx5, FDMA_INTR_ERR);
48210615907SSteen Hegelund 		spx5_wr(err_type, sparx5, FDMA_ERRORS);
48310615907SSteen Hegelund 	}
48410615907SSteen Hegelund 	return IRQ_HANDLED;
48510615907SSteen Hegelund }
48610615907SSteen Hegelund 
sparx5_fdma_injection_mode(struct sparx5 * sparx5)48710615907SSteen Hegelund static void sparx5_fdma_injection_mode(struct sparx5 *sparx5)
48810615907SSteen Hegelund {
48910615907SSteen Hegelund 	const int byte_swap = 1;
49010615907SSteen Hegelund 	int portno;
49110615907SSteen Hegelund 	int urgency;
49210615907SSteen Hegelund 
49310615907SSteen Hegelund 	/* Change mode to fdma extraction and injection */
49410615907SSteen Hegelund 	spx5_wr(QS_XTR_GRP_CFG_MODE_SET(2) |
49510615907SSteen Hegelund 		QS_XTR_GRP_CFG_STATUS_WORD_POS_SET(1) |
49610615907SSteen Hegelund 		QS_XTR_GRP_CFG_BYTE_SWAP_SET(byte_swap),
49710615907SSteen Hegelund 		sparx5, QS_XTR_GRP_CFG(XTR_QUEUE));
49810615907SSteen Hegelund 	spx5_wr(QS_INJ_GRP_CFG_MODE_SET(2) |
49910615907SSteen Hegelund 		QS_INJ_GRP_CFG_BYTE_SWAP_SET(byte_swap),
50010615907SSteen Hegelund 		sparx5, QS_INJ_GRP_CFG(INJ_QUEUE));
50110615907SSteen Hegelund 
50210615907SSteen Hegelund 	/* CPU ports capture setup */
50310615907SSteen Hegelund 	for (portno = SPX5_PORT_CPU_0; portno <= SPX5_PORT_CPU_1; portno++) {
50410615907SSteen Hegelund 		/* ASM CPU port: No preamble, IFH, enable padding */
50510615907SSteen Hegelund 		spx5_wr(ASM_PORT_CFG_PAD_ENA_SET(1) |
50610615907SSteen Hegelund 			ASM_PORT_CFG_NO_PREAMBLE_ENA_SET(1) |
50710615907SSteen Hegelund 			ASM_PORT_CFG_INJ_FORMAT_CFG_SET(1), /* 1 = IFH */
50810615907SSteen Hegelund 			sparx5, ASM_PORT_CFG(portno));
50910615907SSteen Hegelund 
51010615907SSteen Hegelund 		/* Reset WM cnt to unclog queued frames */
51110615907SSteen Hegelund 		spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR_SET(1),
51210615907SSteen Hegelund 			 DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR,
51310615907SSteen Hegelund 			 sparx5,
51410615907SSteen Hegelund 			 DSM_DEV_TX_STOP_WM_CFG(portno));
51510615907SSteen Hegelund 
51610615907SSteen Hegelund 		/* Set Disassembler Stop Watermark level */
51710615907SSteen Hegelund 		spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM_SET(100),
51810615907SSteen Hegelund 			 DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM,
51910615907SSteen Hegelund 			 sparx5,
52010615907SSteen Hegelund 			 DSM_DEV_TX_STOP_WM_CFG(portno));
52110615907SSteen Hegelund 
52210615907SSteen Hegelund 		/* Enable port in queue system */
52310615907SSteen Hegelund 		urgency = sparx5_port_fwd_urg(sparx5, SPEED_2500);
52410615907SSteen Hegelund 		spx5_rmw(QFWD_SWITCH_PORT_MODE_PORT_ENA_SET(1) |
52510615907SSteen Hegelund 			 QFWD_SWITCH_PORT_MODE_FWD_URGENCY_SET(urgency),
52610615907SSteen Hegelund 			 QFWD_SWITCH_PORT_MODE_PORT_ENA |
52710615907SSteen Hegelund 			 QFWD_SWITCH_PORT_MODE_FWD_URGENCY,
52810615907SSteen Hegelund 			 sparx5,
52910615907SSteen Hegelund 			 QFWD_SWITCH_PORT_MODE(portno));
53010615907SSteen Hegelund 
53110615907SSteen Hegelund 		/* Disable Disassembler buffer underrun watchdog
53210615907SSteen Hegelund 		 * to avoid truncated packets in XTR
53310615907SSteen Hegelund 		 */
53410615907SSteen Hegelund 		spx5_rmw(DSM_BUF_CFG_UNDERFLOW_WATCHDOG_DIS_SET(1),
53510615907SSteen Hegelund 			 DSM_BUF_CFG_UNDERFLOW_WATCHDOG_DIS,
53610615907SSteen Hegelund 			 sparx5,
53710615907SSteen Hegelund 			 DSM_BUF_CFG(portno));
53810615907SSteen Hegelund 
53910615907SSteen Hegelund 		/* Disabling frame aging */
54010615907SSteen Hegelund 		spx5_rmw(HSCH_PORT_MODE_AGE_DIS_SET(1),
54110615907SSteen Hegelund 			 HSCH_PORT_MODE_AGE_DIS,
54210615907SSteen Hegelund 			 sparx5,
54310615907SSteen Hegelund 			 HSCH_PORT_MODE(portno));
54410615907SSteen Hegelund 	}
54510615907SSteen Hegelund }
54610615907SSteen Hegelund 
sparx5_fdma_start(struct sparx5 * sparx5)54710615907SSteen Hegelund int sparx5_fdma_start(struct sparx5 *sparx5)
54810615907SSteen Hegelund {
54910615907SSteen Hegelund 	int err;
55010615907SSteen Hegelund 
55110615907SSteen Hegelund 	/* Reset FDMA state */
55210615907SSteen Hegelund 	spx5_wr(FDMA_CTRL_NRESET_SET(0), sparx5, FDMA_CTRL);
55310615907SSteen Hegelund 	spx5_wr(FDMA_CTRL_NRESET_SET(1), sparx5, FDMA_CTRL);
55410615907SSteen Hegelund 
55510615907SSteen Hegelund 	/* Force ACP caching but disable read/write allocation */
55610615907SSteen Hegelund 	spx5_rmw(CPU_PROC_CTRL_ACP_CACHE_FORCE_ENA_SET(1) |
55710615907SSteen Hegelund 		 CPU_PROC_CTRL_ACP_AWCACHE_SET(0) |
55810615907SSteen Hegelund 		 CPU_PROC_CTRL_ACP_ARCACHE_SET(0),
55910615907SSteen Hegelund 		 CPU_PROC_CTRL_ACP_CACHE_FORCE_ENA |
56010615907SSteen Hegelund 		 CPU_PROC_CTRL_ACP_AWCACHE |
56110615907SSteen Hegelund 		 CPU_PROC_CTRL_ACP_ARCACHE,
56210615907SSteen Hegelund 		 sparx5, CPU_PROC_CTRL);
56310615907SSteen Hegelund 
56410615907SSteen Hegelund 	sparx5_fdma_injection_mode(sparx5);
56510615907SSteen Hegelund 	sparx5_fdma_rx_init(sparx5, &sparx5->rx, FDMA_XTR_CHANNEL);
56610615907SSteen Hegelund 	sparx5_fdma_tx_init(sparx5, &sparx5->tx, FDMA_INJ_CHANNEL);
56710615907SSteen Hegelund 	err = sparx5_fdma_rx_alloc(sparx5);
56810615907SSteen Hegelund 	if (err) {
56910615907SSteen Hegelund 		dev_err(sparx5->dev, "Could not allocate RX buffers: %d\n", err);
57010615907SSteen Hegelund 		return err;
57110615907SSteen Hegelund 	}
57210615907SSteen Hegelund 	err = sparx5_fdma_tx_alloc(sparx5);
57310615907SSteen Hegelund 	if (err) {
57410615907SSteen Hegelund 		dev_err(sparx5->dev, "Could not allocate TX buffers: %d\n", err);
57510615907SSteen Hegelund 		return err;
57610615907SSteen Hegelund 	}
57710615907SSteen Hegelund 	return err;
57810615907SSteen Hegelund }
57910615907SSteen Hegelund 
sparx5_fdma_port_ctrl(struct sparx5 * sparx5)58010615907SSteen Hegelund static u32 sparx5_fdma_port_ctrl(struct sparx5 *sparx5)
58110615907SSteen Hegelund {
58210615907SSteen Hegelund 	return spx5_rd(sparx5, FDMA_PORT_CTRL(0));
58310615907SSteen Hegelund }
58410615907SSteen Hegelund 
sparx5_fdma_stop(struct sparx5 * sparx5)58510615907SSteen Hegelund int sparx5_fdma_stop(struct sparx5 *sparx5)
58610615907SSteen Hegelund {
58710615907SSteen Hegelund 	u32 val;
58810615907SSteen Hegelund 
58910615907SSteen Hegelund 	napi_disable(&sparx5->rx.napi);
59010615907SSteen Hegelund 	/* Stop the fdma and channel interrupts */
59110615907SSteen Hegelund 	sparx5_fdma_rx_deactivate(sparx5, &sparx5->rx);
59210615907SSteen Hegelund 	sparx5_fdma_tx_deactivate(sparx5, &sparx5->tx);
59310615907SSteen Hegelund 	/* Wait for the RX channel to stop */
59410615907SSteen Hegelund 	read_poll_timeout(sparx5_fdma_port_ctrl, val,
59510615907SSteen Hegelund 			  FDMA_PORT_CTRL_XTR_BUF_IS_EMPTY_GET(val) == 0,
59610615907SSteen Hegelund 			  500, 10000, 0, sparx5);
59710615907SSteen Hegelund 	return 0;
59810615907SSteen Hegelund }
599