162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci *   Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
562306a36Sopenharmony_ci *   Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
662306a36Sopenharmony_ci *   Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/of.h>
1062306a36Sopenharmony_ci#include <linux/of_mdio.h>
1162306a36Sopenharmony_ci#include <linux/of_net.h>
1262306a36Sopenharmony_ci#include <linux/of_address.h>
1362306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
1462306a36Sopenharmony_ci#include <linux/platform_device.h>
1562306a36Sopenharmony_ci#include <linux/regmap.h>
1662306a36Sopenharmony_ci#include <linux/clk.h>
1762306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1862306a36Sopenharmony_ci#include <linux/if_vlan.h>
1962306a36Sopenharmony_ci#include <linux/reset.h>
2062306a36Sopenharmony_ci#include <linux/tcp.h>
2162306a36Sopenharmony_ci#include <linux/interrupt.h>
2262306a36Sopenharmony_ci#include <linux/pinctrl/devinfo.h>
2362306a36Sopenharmony_ci#include <linux/phylink.h>
2462306a36Sopenharmony_ci#include <linux/pcs/pcs-mtk-lynxi.h>
2562306a36Sopenharmony_ci#include <linux/jhash.h>
2662306a36Sopenharmony_ci#include <linux/bitfield.h>
2762306a36Sopenharmony_ci#include <net/dsa.h>
2862306a36Sopenharmony_ci#include <net/dst_metadata.h>
2962306a36Sopenharmony_ci#include <net/page_pool/helpers.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include "mtk_eth_soc.h"
3262306a36Sopenharmony_ci#include "mtk_wed.h"
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic int mtk_msg_level = -1;
3562306a36Sopenharmony_cimodule_param_named(msg_level, mtk_msg_level, int, 0);
3662306a36Sopenharmony_ciMODULE_PARM_DESC(msg_level, "Message level (-1=defaults,0=none,...,16=all)");
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define MTK_ETHTOOL_STAT(x) { #x, \
3962306a36Sopenharmony_ci			      offsetof(struct mtk_hw_stats, x) / sizeof(u64) }
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define MTK_ETHTOOL_XDP_STAT(x) { #x, \
4262306a36Sopenharmony_ci				  offsetof(struct mtk_hw_stats, xdp_stats.x) / \
4362306a36Sopenharmony_ci				  sizeof(u64) }
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic const struct mtk_reg_map mtk_reg_map = {
4662306a36Sopenharmony_ci	.tx_irq_mask		= 0x1a1c,
4762306a36Sopenharmony_ci	.tx_irq_status		= 0x1a18,
4862306a36Sopenharmony_ci	.pdma = {
4962306a36Sopenharmony_ci		.rx_ptr		= 0x0900,
5062306a36Sopenharmony_ci		.rx_cnt_cfg	= 0x0904,
5162306a36Sopenharmony_ci		.pcrx_ptr	= 0x0908,
5262306a36Sopenharmony_ci		.glo_cfg	= 0x0a04,
5362306a36Sopenharmony_ci		.rst_idx	= 0x0a08,
5462306a36Sopenharmony_ci		.delay_irq	= 0x0a0c,
5562306a36Sopenharmony_ci		.irq_status	= 0x0a20,
5662306a36Sopenharmony_ci		.irq_mask	= 0x0a28,
5762306a36Sopenharmony_ci		.adma_rx_dbg0	= 0x0a38,
5862306a36Sopenharmony_ci		.int_grp	= 0x0a50,
5962306a36Sopenharmony_ci	},
6062306a36Sopenharmony_ci	.qdma = {
6162306a36Sopenharmony_ci		.qtx_cfg	= 0x1800,
6262306a36Sopenharmony_ci		.qtx_sch	= 0x1804,
6362306a36Sopenharmony_ci		.rx_ptr		= 0x1900,
6462306a36Sopenharmony_ci		.rx_cnt_cfg	= 0x1904,
6562306a36Sopenharmony_ci		.qcrx_ptr	= 0x1908,
6662306a36Sopenharmony_ci		.glo_cfg	= 0x1a04,
6762306a36Sopenharmony_ci		.rst_idx	= 0x1a08,
6862306a36Sopenharmony_ci		.delay_irq	= 0x1a0c,
6962306a36Sopenharmony_ci		.fc_th		= 0x1a10,
7062306a36Sopenharmony_ci		.tx_sch_rate	= 0x1a14,
7162306a36Sopenharmony_ci		.int_grp	= 0x1a20,
7262306a36Sopenharmony_ci		.hred		= 0x1a44,
7362306a36Sopenharmony_ci		.ctx_ptr	= 0x1b00,
7462306a36Sopenharmony_ci		.dtx_ptr	= 0x1b04,
7562306a36Sopenharmony_ci		.crx_ptr	= 0x1b10,
7662306a36Sopenharmony_ci		.drx_ptr	= 0x1b14,
7762306a36Sopenharmony_ci		.fq_head	= 0x1b20,
7862306a36Sopenharmony_ci		.fq_tail	= 0x1b24,
7962306a36Sopenharmony_ci		.fq_count	= 0x1b28,
8062306a36Sopenharmony_ci		.fq_blen	= 0x1b2c,
8162306a36Sopenharmony_ci	},
8262306a36Sopenharmony_ci	.gdm1_cnt		= 0x2400,
8362306a36Sopenharmony_ci	.gdma_to_ppe		= 0x4444,
8462306a36Sopenharmony_ci	.ppe_base		= 0x0c00,
8562306a36Sopenharmony_ci	.wdma_base = {
8662306a36Sopenharmony_ci		[0]		= 0x2800,
8762306a36Sopenharmony_ci		[1]		= 0x2c00,
8862306a36Sopenharmony_ci	},
8962306a36Sopenharmony_ci	.pse_iq_sta		= 0x0110,
9062306a36Sopenharmony_ci	.pse_oq_sta		= 0x0118,
9162306a36Sopenharmony_ci};
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic const struct mtk_reg_map mt7628_reg_map = {
9462306a36Sopenharmony_ci	.tx_irq_mask		= 0x0a28,
9562306a36Sopenharmony_ci	.tx_irq_status		= 0x0a20,
9662306a36Sopenharmony_ci	.pdma = {
9762306a36Sopenharmony_ci		.rx_ptr		= 0x0900,
9862306a36Sopenharmony_ci		.rx_cnt_cfg	= 0x0904,
9962306a36Sopenharmony_ci		.pcrx_ptr	= 0x0908,
10062306a36Sopenharmony_ci		.glo_cfg	= 0x0a04,
10162306a36Sopenharmony_ci		.rst_idx	= 0x0a08,
10262306a36Sopenharmony_ci		.delay_irq	= 0x0a0c,
10362306a36Sopenharmony_ci		.irq_status	= 0x0a20,
10462306a36Sopenharmony_ci		.irq_mask	= 0x0a28,
10562306a36Sopenharmony_ci		.int_grp	= 0x0a50,
10662306a36Sopenharmony_ci	},
10762306a36Sopenharmony_ci};
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic const struct mtk_reg_map mt7986_reg_map = {
11062306a36Sopenharmony_ci	.tx_irq_mask		= 0x461c,
11162306a36Sopenharmony_ci	.tx_irq_status		= 0x4618,
11262306a36Sopenharmony_ci	.pdma = {
11362306a36Sopenharmony_ci		.rx_ptr		= 0x6100,
11462306a36Sopenharmony_ci		.rx_cnt_cfg	= 0x6104,
11562306a36Sopenharmony_ci		.pcrx_ptr	= 0x6108,
11662306a36Sopenharmony_ci		.glo_cfg	= 0x6204,
11762306a36Sopenharmony_ci		.rst_idx	= 0x6208,
11862306a36Sopenharmony_ci		.delay_irq	= 0x620c,
11962306a36Sopenharmony_ci		.irq_status	= 0x6220,
12062306a36Sopenharmony_ci		.irq_mask	= 0x6228,
12162306a36Sopenharmony_ci		.adma_rx_dbg0	= 0x6238,
12262306a36Sopenharmony_ci		.int_grp	= 0x6250,
12362306a36Sopenharmony_ci	},
12462306a36Sopenharmony_ci	.qdma = {
12562306a36Sopenharmony_ci		.qtx_cfg	= 0x4400,
12662306a36Sopenharmony_ci		.qtx_sch	= 0x4404,
12762306a36Sopenharmony_ci		.rx_ptr		= 0x4500,
12862306a36Sopenharmony_ci		.rx_cnt_cfg	= 0x4504,
12962306a36Sopenharmony_ci		.qcrx_ptr	= 0x4508,
13062306a36Sopenharmony_ci		.glo_cfg	= 0x4604,
13162306a36Sopenharmony_ci		.rst_idx	= 0x4608,
13262306a36Sopenharmony_ci		.delay_irq	= 0x460c,
13362306a36Sopenharmony_ci		.fc_th		= 0x4610,
13462306a36Sopenharmony_ci		.int_grp	= 0x4620,
13562306a36Sopenharmony_ci		.hred		= 0x4644,
13662306a36Sopenharmony_ci		.ctx_ptr	= 0x4700,
13762306a36Sopenharmony_ci		.dtx_ptr	= 0x4704,
13862306a36Sopenharmony_ci		.crx_ptr	= 0x4710,
13962306a36Sopenharmony_ci		.drx_ptr	= 0x4714,
14062306a36Sopenharmony_ci		.fq_head	= 0x4720,
14162306a36Sopenharmony_ci		.fq_tail	= 0x4724,
14262306a36Sopenharmony_ci		.fq_count	= 0x4728,
14362306a36Sopenharmony_ci		.fq_blen	= 0x472c,
14462306a36Sopenharmony_ci		.tx_sch_rate	= 0x4798,
14562306a36Sopenharmony_ci	},
14662306a36Sopenharmony_ci	.gdm1_cnt		= 0x1c00,
14762306a36Sopenharmony_ci	.gdma_to_ppe		= 0x3333,
14862306a36Sopenharmony_ci	.ppe_base		= 0x2000,
14962306a36Sopenharmony_ci	.wdma_base = {
15062306a36Sopenharmony_ci		[0]		= 0x4800,
15162306a36Sopenharmony_ci		[1]		= 0x4c00,
15262306a36Sopenharmony_ci	},
15362306a36Sopenharmony_ci	.pse_iq_sta		= 0x0180,
15462306a36Sopenharmony_ci	.pse_oq_sta		= 0x01a0,
15562306a36Sopenharmony_ci};
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic const struct mtk_reg_map mt7988_reg_map = {
15862306a36Sopenharmony_ci	.tx_irq_mask		= 0x461c,
15962306a36Sopenharmony_ci	.tx_irq_status		= 0x4618,
16062306a36Sopenharmony_ci	.pdma = {
16162306a36Sopenharmony_ci		.rx_ptr		= 0x6900,
16262306a36Sopenharmony_ci		.rx_cnt_cfg	= 0x6904,
16362306a36Sopenharmony_ci		.pcrx_ptr	= 0x6908,
16462306a36Sopenharmony_ci		.glo_cfg	= 0x6a04,
16562306a36Sopenharmony_ci		.rst_idx	= 0x6a08,
16662306a36Sopenharmony_ci		.delay_irq	= 0x6a0c,
16762306a36Sopenharmony_ci		.irq_status	= 0x6a20,
16862306a36Sopenharmony_ci		.irq_mask	= 0x6a28,
16962306a36Sopenharmony_ci		.adma_rx_dbg0	= 0x6a38,
17062306a36Sopenharmony_ci		.int_grp	= 0x6a50,
17162306a36Sopenharmony_ci	},
17262306a36Sopenharmony_ci	.qdma = {
17362306a36Sopenharmony_ci		.qtx_cfg	= 0x4400,
17462306a36Sopenharmony_ci		.qtx_sch	= 0x4404,
17562306a36Sopenharmony_ci		.rx_ptr		= 0x4500,
17662306a36Sopenharmony_ci		.rx_cnt_cfg	= 0x4504,
17762306a36Sopenharmony_ci		.qcrx_ptr	= 0x4508,
17862306a36Sopenharmony_ci		.glo_cfg	= 0x4604,
17962306a36Sopenharmony_ci		.rst_idx	= 0x4608,
18062306a36Sopenharmony_ci		.delay_irq	= 0x460c,
18162306a36Sopenharmony_ci		.fc_th		= 0x4610,
18262306a36Sopenharmony_ci		.int_grp	= 0x4620,
18362306a36Sopenharmony_ci		.hred		= 0x4644,
18462306a36Sopenharmony_ci		.ctx_ptr	= 0x4700,
18562306a36Sopenharmony_ci		.dtx_ptr	= 0x4704,
18662306a36Sopenharmony_ci		.crx_ptr	= 0x4710,
18762306a36Sopenharmony_ci		.drx_ptr	= 0x4714,
18862306a36Sopenharmony_ci		.fq_head	= 0x4720,
18962306a36Sopenharmony_ci		.fq_tail	= 0x4724,
19062306a36Sopenharmony_ci		.fq_count	= 0x4728,
19162306a36Sopenharmony_ci		.fq_blen	= 0x472c,
19262306a36Sopenharmony_ci		.tx_sch_rate	= 0x4798,
19362306a36Sopenharmony_ci	},
19462306a36Sopenharmony_ci	.gdm1_cnt		= 0x1c00,
19562306a36Sopenharmony_ci	.gdma_to_ppe		= 0x3333,
19662306a36Sopenharmony_ci	.ppe_base		= 0x2000,
19762306a36Sopenharmony_ci	.wdma_base = {
19862306a36Sopenharmony_ci		[0]		= 0x4800,
19962306a36Sopenharmony_ci		[1]		= 0x4c00,
20062306a36Sopenharmony_ci	},
20162306a36Sopenharmony_ci	.pse_iq_sta		= 0x0180,
20262306a36Sopenharmony_ci	.pse_oq_sta		= 0x01a0,
20362306a36Sopenharmony_ci};
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci/* strings used by ethtool */
20662306a36Sopenharmony_cistatic const struct mtk_ethtool_stats {
20762306a36Sopenharmony_ci	char str[ETH_GSTRING_LEN];
20862306a36Sopenharmony_ci	u32 offset;
20962306a36Sopenharmony_ci} mtk_ethtool_stats[] = {
21062306a36Sopenharmony_ci	MTK_ETHTOOL_STAT(tx_bytes),
21162306a36Sopenharmony_ci	MTK_ETHTOOL_STAT(tx_packets),
21262306a36Sopenharmony_ci	MTK_ETHTOOL_STAT(tx_skip),
21362306a36Sopenharmony_ci	MTK_ETHTOOL_STAT(tx_collisions),
21462306a36Sopenharmony_ci	MTK_ETHTOOL_STAT(rx_bytes),
21562306a36Sopenharmony_ci	MTK_ETHTOOL_STAT(rx_packets),
21662306a36Sopenharmony_ci	MTK_ETHTOOL_STAT(rx_overflow),
21762306a36Sopenharmony_ci	MTK_ETHTOOL_STAT(rx_fcs_errors),
21862306a36Sopenharmony_ci	MTK_ETHTOOL_STAT(rx_short_errors),
21962306a36Sopenharmony_ci	MTK_ETHTOOL_STAT(rx_long_errors),
22062306a36Sopenharmony_ci	MTK_ETHTOOL_STAT(rx_checksum_errors),
22162306a36Sopenharmony_ci	MTK_ETHTOOL_STAT(rx_flow_control_packets),
22262306a36Sopenharmony_ci	MTK_ETHTOOL_XDP_STAT(rx_xdp_redirect),
22362306a36Sopenharmony_ci	MTK_ETHTOOL_XDP_STAT(rx_xdp_pass),
22462306a36Sopenharmony_ci	MTK_ETHTOOL_XDP_STAT(rx_xdp_drop),
22562306a36Sopenharmony_ci	MTK_ETHTOOL_XDP_STAT(rx_xdp_tx),
22662306a36Sopenharmony_ci	MTK_ETHTOOL_XDP_STAT(rx_xdp_tx_errors),
22762306a36Sopenharmony_ci	MTK_ETHTOOL_XDP_STAT(tx_xdp_xmit),
22862306a36Sopenharmony_ci	MTK_ETHTOOL_XDP_STAT(tx_xdp_xmit_errors),
22962306a36Sopenharmony_ci};
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic const char * const mtk_clks_source_name[] = {
23262306a36Sopenharmony_ci	"ethif",
23362306a36Sopenharmony_ci	"sgmiitop",
23462306a36Sopenharmony_ci	"esw",
23562306a36Sopenharmony_ci	"gp0",
23662306a36Sopenharmony_ci	"gp1",
23762306a36Sopenharmony_ci	"gp2",
23862306a36Sopenharmony_ci	"gp3",
23962306a36Sopenharmony_ci	"xgp1",
24062306a36Sopenharmony_ci	"xgp2",
24162306a36Sopenharmony_ci	"xgp3",
24262306a36Sopenharmony_ci	"crypto",
24362306a36Sopenharmony_ci	"fe",
24462306a36Sopenharmony_ci	"trgpll",
24562306a36Sopenharmony_ci	"sgmii_tx250m",
24662306a36Sopenharmony_ci	"sgmii_rx250m",
24762306a36Sopenharmony_ci	"sgmii_cdr_ref",
24862306a36Sopenharmony_ci	"sgmii_cdr_fb",
24962306a36Sopenharmony_ci	"sgmii2_tx250m",
25062306a36Sopenharmony_ci	"sgmii2_rx250m",
25162306a36Sopenharmony_ci	"sgmii2_cdr_ref",
25262306a36Sopenharmony_ci	"sgmii2_cdr_fb",
25362306a36Sopenharmony_ci	"sgmii_ck",
25462306a36Sopenharmony_ci	"eth2pll",
25562306a36Sopenharmony_ci	"wocpu0",
25662306a36Sopenharmony_ci	"wocpu1",
25762306a36Sopenharmony_ci	"netsys0",
25862306a36Sopenharmony_ci	"netsys1",
25962306a36Sopenharmony_ci	"ethwarp_wocpu2",
26062306a36Sopenharmony_ci	"ethwarp_wocpu1",
26162306a36Sopenharmony_ci	"ethwarp_wocpu0",
26262306a36Sopenharmony_ci	"top_usxgmii0_sel",
26362306a36Sopenharmony_ci	"top_usxgmii1_sel",
26462306a36Sopenharmony_ci	"top_sgm0_sel",
26562306a36Sopenharmony_ci	"top_sgm1_sel",
26662306a36Sopenharmony_ci	"top_xfi_phy0_xtal_sel",
26762306a36Sopenharmony_ci	"top_xfi_phy1_xtal_sel",
26862306a36Sopenharmony_ci	"top_eth_gmii_sel",
26962306a36Sopenharmony_ci	"top_eth_refck_50m_sel",
27062306a36Sopenharmony_ci	"top_eth_sys_200m_sel",
27162306a36Sopenharmony_ci	"top_eth_sys_sel",
27262306a36Sopenharmony_ci	"top_eth_xgmii_sel",
27362306a36Sopenharmony_ci	"top_eth_mii_sel",
27462306a36Sopenharmony_ci	"top_netsys_sel",
27562306a36Sopenharmony_ci	"top_netsys_500m_sel",
27662306a36Sopenharmony_ci	"top_netsys_pao_2x_sel",
27762306a36Sopenharmony_ci	"top_netsys_sync_250m_sel",
27862306a36Sopenharmony_ci	"top_netsys_ppefb_250m_sel",
27962306a36Sopenharmony_ci	"top_netsys_warp_sel",
28062306a36Sopenharmony_ci};
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_civoid mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	__raw_writel(val, eth->base + reg);
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ciu32 mtk_r32(struct mtk_eth *eth, unsigned reg)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	return __raw_readl(eth->base + reg);
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ciu32 mtk_m32(struct mtk_eth *eth, u32 mask, u32 set, unsigned int reg)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	u32 val;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	val = mtk_r32(eth, reg);
29762306a36Sopenharmony_ci	val &= ~mask;
29862306a36Sopenharmony_ci	val |= set;
29962306a36Sopenharmony_ci	mtk_w32(eth, val, reg);
30062306a36Sopenharmony_ci	return reg;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic int mtk_mdio_busy_wait(struct mtk_eth *eth)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	unsigned long t_start = jiffies;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	while (1) {
30862306a36Sopenharmony_ci		if (!(mtk_r32(eth, MTK_PHY_IAC) & PHY_IAC_ACCESS))
30962306a36Sopenharmony_ci			return 0;
31062306a36Sopenharmony_ci		if (time_after(jiffies, t_start + PHY_IAC_TIMEOUT))
31162306a36Sopenharmony_ci			break;
31262306a36Sopenharmony_ci		cond_resched();
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	dev_err(eth->dev, "mdio: MDIO timeout\n");
31662306a36Sopenharmony_ci	return -ETIMEDOUT;
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic int _mtk_mdio_write_c22(struct mtk_eth *eth, u32 phy_addr, u32 phy_reg,
32062306a36Sopenharmony_ci			       u32 write_data)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	int ret;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	ret = mtk_mdio_busy_wait(eth);
32562306a36Sopenharmony_ci	if (ret < 0)
32662306a36Sopenharmony_ci		return ret;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	mtk_w32(eth, PHY_IAC_ACCESS |
32962306a36Sopenharmony_ci		PHY_IAC_START_C22 |
33062306a36Sopenharmony_ci		PHY_IAC_CMD_WRITE |
33162306a36Sopenharmony_ci		PHY_IAC_REG(phy_reg) |
33262306a36Sopenharmony_ci		PHY_IAC_ADDR(phy_addr) |
33362306a36Sopenharmony_ci		PHY_IAC_DATA(write_data),
33462306a36Sopenharmony_ci		MTK_PHY_IAC);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	ret = mtk_mdio_busy_wait(eth);
33762306a36Sopenharmony_ci	if (ret < 0)
33862306a36Sopenharmony_ci		return ret;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	return 0;
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic int _mtk_mdio_write_c45(struct mtk_eth *eth, u32 phy_addr,
34462306a36Sopenharmony_ci			       u32 devad, u32 phy_reg, u32 write_data)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	int ret;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	ret = mtk_mdio_busy_wait(eth);
34962306a36Sopenharmony_ci	if (ret < 0)
35062306a36Sopenharmony_ci		return ret;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	mtk_w32(eth, PHY_IAC_ACCESS |
35362306a36Sopenharmony_ci		PHY_IAC_START_C45 |
35462306a36Sopenharmony_ci		PHY_IAC_CMD_C45_ADDR |
35562306a36Sopenharmony_ci		PHY_IAC_REG(devad) |
35662306a36Sopenharmony_ci		PHY_IAC_ADDR(phy_addr) |
35762306a36Sopenharmony_ci		PHY_IAC_DATA(phy_reg),
35862306a36Sopenharmony_ci		MTK_PHY_IAC);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	ret = mtk_mdio_busy_wait(eth);
36162306a36Sopenharmony_ci	if (ret < 0)
36262306a36Sopenharmony_ci		return ret;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	mtk_w32(eth, PHY_IAC_ACCESS |
36562306a36Sopenharmony_ci		PHY_IAC_START_C45 |
36662306a36Sopenharmony_ci		PHY_IAC_CMD_WRITE |
36762306a36Sopenharmony_ci		PHY_IAC_REG(devad) |
36862306a36Sopenharmony_ci		PHY_IAC_ADDR(phy_addr) |
36962306a36Sopenharmony_ci		PHY_IAC_DATA(write_data),
37062306a36Sopenharmony_ci		MTK_PHY_IAC);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	ret = mtk_mdio_busy_wait(eth);
37362306a36Sopenharmony_ci	if (ret < 0)
37462306a36Sopenharmony_ci		return ret;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	return 0;
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic int _mtk_mdio_read_c22(struct mtk_eth *eth, u32 phy_addr, u32 phy_reg)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	int ret;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	ret = mtk_mdio_busy_wait(eth);
38462306a36Sopenharmony_ci	if (ret < 0)
38562306a36Sopenharmony_ci		return ret;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	mtk_w32(eth, PHY_IAC_ACCESS |
38862306a36Sopenharmony_ci		PHY_IAC_START_C22 |
38962306a36Sopenharmony_ci		PHY_IAC_CMD_C22_READ |
39062306a36Sopenharmony_ci		PHY_IAC_REG(phy_reg) |
39162306a36Sopenharmony_ci		PHY_IAC_ADDR(phy_addr),
39262306a36Sopenharmony_ci		MTK_PHY_IAC);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	ret = mtk_mdio_busy_wait(eth);
39562306a36Sopenharmony_ci	if (ret < 0)
39662306a36Sopenharmony_ci		return ret;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	return mtk_r32(eth, MTK_PHY_IAC) & PHY_IAC_DATA_MASK;
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic int _mtk_mdio_read_c45(struct mtk_eth *eth, u32 phy_addr,
40262306a36Sopenharmony_ci			      u32 devad, u32 phy_reg)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	int ret;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	ret = mtk_mdio_busy_wait(eth);
40762306a36Sopenharmony_ci	if (ret < 0)
40862306a36Sopenharmony_ci		return ret;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	mtk_w32(eth, PHY_IAC_ACCESS |
41162306a36Sopenharmony_ci		PHY_IAC_START_C45 |
41262306a36Sopenharmony_ci		PHY_IAC_CMD_C45_ADDR |
41362306a36Sopenharmony_ci		PHY_IAC_REG(devad) |
41462306a36Sopenharmony_ci		PHY_IAC_ADDR(phy_addr) |
41562306a36Sopenharmony_ci		PHY_IAC_DATA(phy_reg),
41662306a36Sopenharmony_ci		MTK_PHY_IAC);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	ret = mtk_mdio_busy_wait(eth);
41962306a36Sopenharmony_ci	if (ret < 0)
42062306a36Sopenharmony_ci		return ret;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	mtk_w32(eth, PHY_IAC_ACCESS |
42362306a36Sopenharmony_ci		PHY_IAC_START_C45 |
42462306a36Sopenharmony_ci		PHY_IAC_CMD_C45_READ |
42562306a36Sopenharmony_ci		PHY_IAC_REG(devad) |
42662306a36Sopenharmony_ci		PHY_IAC_ADDR(phy_addr),
42762306a36Sopenharmony_ci		MTK_PHY_IAC);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	ret = mtk_mdio_busy_wait(eth);
43062306a36Sopenharmony_ci	if (ret < 0)
43162306a36Sopenharmony_ci		return ret;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	return mtk_r32(eth, MTK_PHY_IAC) & PHY_IAC_DATA_MASK;
43462306a36Sopenharmony_ci}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_cistatic int mtk_mdio_write_c22(struct mii_bus *bus, int phy_addr,
43762306a36Sopenharmony_ci			      int phy_reg, u16 val)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	struct mtk_eth *eth = bus->priv;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	return _mtk_mdio_write_c22(eth, phy_addr, phy_reg, val);
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic int mtk_mdio_write_c45(struct mii_bus *bus, int phy_addr,
44562306a36Sopenharmony_ci			      int devad, int phy_reg, u16 val)
44662306a36Sopenharmony_ci{
44762306a36Sopenharmony_ci	struct mtk_eth *eth = bus->priv;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	return _mtk_mdio_write_c45(eth, phy_addr, devad, phy_reg, val);
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic int mtk_mdio_read_c22(struct mii_bus *bus, int phy_addr, int phy_reg)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	struct mtk_eth *eth = bus->priv;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	return _mtk_mdio_read_c22(eth, phy_addr, phy_reg);
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_cistatic int mtk_mdio_read_c45(struct mii_bus *bus, int phy_addr, int devad,
46062306a36Sopenharmony_ci			     int phy_reg)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	struct mtk_eth *eth = bus->priv;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	return _mtk_mdio_read_c45(eth, phy_addr, devad, phy_reg);
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cistatic int mt7621_gmac0_rgmii_adjust(struct mtk_eth *eth,
46862306a36Sopenharmony_ci				     phy_interface_t interface)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	u32 val;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	val = (interface == PHY_INTERFACE_MODE_TRGMII) ?
47362306a36Sopenharmony_ci		ETHSYS_TRGMII_MT7621_DDR_PLL : 0;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	regmap_update_bits(eth->ethsys, ETHSYS_CLKCFG0,
47662306a36Sopenharmony_ci			   ETHSYS_TRGMII_MT7621_MASK, val);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	return 0;
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_cistatic void mtk_gmac0_rgmii_adjust(struct mtk_eth *eth,
48262306a36Sopenharmony_ci				   phy_interface_t interface)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	int ret;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	if (interface == PHY_INTERFACE_MODE_TRGMII) {
48762306a36Sopenharmony_ci		mtk_w32(eth, TRGMII_MODE, INTF_MODE);
48862306a36Sopenharmony_ci		ret = clk_set_rate(eth->clks[MTK_CLK_TRGPLL], 500000000);
48962306a36Sopenharmony_ci		if (ret)
49062306a36Sopenharmony_ci			dev_err(eth->dev, "Failed to set trgmii pll: %d\n", ret);
49162306a36Sopenharmony_ci		return;
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	dev_err(eth->dev, "Missing PLL configuration, ethernet may not work\n");
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cistatic void mtk_setup_bridge_switch(struct mtk_eth *eth)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci	/* Force Port1 XGMAC Link Up */
50062306a36Sopenharmony_ci	mtk_m32(eth, 0, MTK_XGMAC_FORCE_LINK(MTK_GMAC1_ID),
50162306a36Sopenharmony_ci		MTK_XGMAC_STS(MTK_GMAC1_ID));
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	/* Adjust GSW bridge IPG to 11 */
50462306a36Sopenharmony_ci	mtk_m32(eth, GSWTX_IPG_MASK | GSWRX_IPG_MASK,
50562306a36Sopenharmony_ci		(GSW_IPG_11 << GSWTX_IPG_SHIFT) |
50662306a36Sopenharmony_ci		(GSW_IPG_11 << GSWRX_IPG_SHIFT),
50762306a36Sopenharmony_ci		MTK_GSW_CFG);
50862306a36Sopenharmony_ci}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_cistatic struct phylink_pcs *mtk_mac_select_pcs(struct phylink_config *config,
51162306a36Sopenharmony_ci					      phy_interface_t interface)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	struct mtk_mac *mac = container_of(config, struct mtk_mac,
51462306a36Sopenharmony_ci					   phylink_config);
51562306a36Sopenharmony_ci	struct mtk_eth *eth = mac->hw;
51662306a36Sopenharmony_ci	unsigned int sid;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	if (interface == PHY_INTERFACE_MODE_SGMII ||
51962306a36Sopenharmony_ci	    phy_interface_mode_is_8023z(interface)) {
52062306a36Sopenharmony_ci		sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ?
52162306a36Sopenharmony_ci		       0 : mac->id;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci		return eth->sgmii_pcs[sid];
52462306a36Sopenharmony_ci	}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	return NULL;
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic void mtk_mac_config(struct phylink_config *config, unsigned int mode,
53062306a36Sopenharmony_ci			   const struct phylink_link_state *state)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	struct mtk_mac *mac = container_of(config, struct mtk_mac,
53362306a36Sopenharmony_ci					   phylink_config);
53462306a36Sopenharmony_ci	struct mtk_eth *eth = mac->hw;
53562306a36Sopenharmony_ci	int val, ge_mode, err = 0;
53662306a36Sopenharmony_ci	u32 i;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	/* MT76x8 has no hardware settings between for the MAC */
53962306a36Sopenharmony_ci	if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) &&
54062306a36Sopenharmony_ci	    mac->interface != state->interface) {
54162306a36Sopenharmony_ci		/* Setup soc pin functions */
54262306a36Sopenharmony_ci		switch (state->interface) {
54362306a36Sopenharmony_ci		case PHY_INTERFACE_MODE_TRGMII:
54462306a36Sopenharmony_ci		case PHY_INTERFACE_MODE_RGMII_TXID:
54562306a36Sopenharmony_ci		case PHY_INTERFACE_MODE_RGMII_RXID:
54662306a36Sopenharmony_ci		case PHY_INTERFACE_MODE_RGMII_ID:
54762306a36Sopenharmony_ci		case PHY_INTERFACE_MODE_RGMII:
54862306a36Sopenharmony_ci		case PHY_INTERFACE_MODE_MII:
54962306a36Sopenharmony_ci			if (MTK_HAS_CAPS(eth->soc->caps, MTK_RGMII)) {
55062306a36Sopenharmony_ci				err = mtk_gmac_rgmii_path_setup(eth, mac->id);
55162306a36Sopenharmony_ci				if (err)
55262306a36Sopenharmony_ci					goto init_err;
55362306a36Sopenharmony_ci			}
55462306a36Sopenharmony_ci			break;
55562306a36Sopenharmony_ci		case PHY_INTERFACE_MODE_1000BASEX:
55662306a36Sopenharmony_ci		case PHY_INTERFACE_MODE_2500BASEX:
55762306a36Sopenharmony_ci		case PHY_INTERFACE_MODE_SGMII:
55862306a36Sopenharmony_ci			err = mtk_gmac_sgmii_path_setup(eth, mac->id);
55962306a36Sopenharmony_ci			if (err)
56062306a36Sopenharmony_ci				goto init_err;
56162306a36Sopenharmony_ci			break;
56262306a36Sopenharmony_ci		case PHY_INTERFACE_MODE_GMII:
56362306a36Sopenharmony_ci			if (MTK_HAS_CAPS(eth->soc->caps, MTK_GEPHY)) {
56462306a36Sopenharmony_ci				err = mtk_gmac_gephy_path_setup(eth, mac->id);
56562306a36Sopenharmony_ci				if (err)
56662306a36Sopenharmony_ci					goto init_err;
56762306a36Sopenharmony_ci			}
56862306a36Sopenharmony_ci			break;
56962306a36Sopenharmony_ci		case PHY_INTERFACE_MODE_INTERNAL:
57062306a36Sopenharmony_ci			break;
57162306a36Sopenharmony_ci		default:
57262306a36Sopenharmony_ci			goto err_phy;
57362306a36Sopenharmony_ci		}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci		/* Setup clock for 1st gmac */
57662306a36Sopenharmony_ci		if (!mac->id && state->interface != PHY_INTERFACE_MODE_SGMII &&
57762306a36Sopenharmony_ci		    !phy_interface_mode_is_8023z(state->interface) &&
57862306a36Sopenharmony_ci		    MTK_HAS_CAPS(mac->hw->soc->caps, MTK_GMAC1_TRGMII)) {
57962306a36Sopenharmony_ci			if (MTK_HAS_CAPS(mac->hw->soc->caps,
58062306a36Sopenharmony_ci					 MTK_TRGMII_MT7621_CLK)) {
58162306a36Sopenharmony_ci				if (mt7621_gmac0_rgmii_adjust(mac->hw,
58262306a36Sopenharmony_ci							      state->interface))
58362306a36Sopenharmony_ci					goto err_phy;
58462306a36Sopenharmony_ci			} else {
58562306a36Sopenharmony_ci				mtk_gmac0_rgmii_adjust(mac->hw,
58662306a36Sopenharmony_ci						       state->interface);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci				/* mt7623_pad_clk_setup */
58962306a36Sopenharmony_ci				for (i = 0 ; i < NUM_TRGMII_CTRL; i++)
59062306a36Sopenharmony_ci					mtk_w32(mac->hw,
59162306a36Sopenharmony_ci						TD_DM_DRVP(8) | TD_DM_DRVN(8),
59262306a36Sopenharmony_ci						TRGMII_TD_ODT(i));
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci				/* Assert/release MT7623 RXC reset */
59562306a36Sopenharmony_ci				mtk_m32(mac->hw, 0, RXC_RST | RXC_DQSISEL,
59662306a36Sopenharmony_ci					TRGMII_RCK_CTRL);
59762306a36Sopenharmony_ci				mtk_m32(mac->hw, RXC_RST, 0, TRGMII_RCK_CTRL);
59862306a36Sopenharmony_ci			}
59962306a36Sopenharmony_ci		}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci		switch (state->interface) {
60262306a36Sopenharmony_ci		case PHY_INTERFACE_MODE_MII:
60362306a36Sopenharmony_ci		case PHY_INTERFACE_MODE_GMII:
60462306a36Sopenharmony_ci			ge_mode = 1;
60562306a36Sopenharmony_ci			break;
60662306a36Sopenharmony_ci		default:
60762306a36Sopenharmony_ci			ge_mode = 0;
60862306a36Sopenharmony_ci			break;
60962306a36Sopenharmony_ci		}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci		/* put the gmac into the right mode */
61262306a36Sopenharmony_ci		regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val);
61362306a36Sopenharmony_ci		val &= ~SYSCFG0_GE_MODE(SYSCFG0_GE_MASK, mac->id);
61462306a36Sopenharmony_ci		val |= SYSCFG0_GE_MODE(ge_mode, mac->id);
61562306a36Sopenharmony_ci		regmap_write(eth->ethsys, ETHSYS_SYSCFG0, val);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci		mac->interface = state->interface;
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	/* SGMII */
62162306a36Sopenharmony_ci	if (state->interface == PHY_INTERFACE_MODE_SGMII ||
62262306a36Sopenharmony_ci	    phy_interface_mode_is_8023z(state->interface)) {
62362306a36Sopenharmony_ci		/* The path GMAC to SGMII will be enabled once the SGMIISYS is
62462306a36Sopenharmony_ci		 * being setup done.
62562306a36Sopenharmony_ci		 */
62662306a36Sopenharmony_ci		regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val);
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci		regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0,
62962306a36Sopenharmony_ci				   SYSCFG0_SGMII_MASK,
63062306a36Sopenharmony_ci				   ~(u32)SYSCFG0_SGMII_MASK);
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci		/* Save the syscfg0 value for mac_finish */
63362306a36Sopenharmony_ci		mac->syscfg0 = val;
63462306a36Sopenharmony_ci	} else if (phylink_autoneg_inband(mode)) {
63562306a36Sopenharmony_ci		dev_err(eth->dev,
63662306a36Sopenharmony_ci			"In-band mode not supported in non SGMII mode!\n");
63762306a36Sopenharmony_ci		return;
63862306a36Sopenharmony_ci	}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	/* Setup gmac */
64162306a36Sopenharmony_ci	if (mtk_is_netsys_v3_or_greater(eth) &&
64262306a36Sopenharmony_ci	    mac->interface == PHY_INTERFACE_MODE_INTERNAL) {
64362306a36Sopenharmony_ci		mtk_w32(mac->hw, MTK_GDMA_XGDM_SEL, MTK_GDMA_EG_CTRL(mac->id));
64462306a36Sopenharmony_ci		mtk_w32(mac->hw, MAC_MCR_FORCE_LINK_DOWN, MTK_MAC_MCR(mac->id));
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci		mtk_setup_bridge_switch(eth);
64762306a36Sopenharmony_ci	}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	return;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_cierr_phy:
65262306a36Sopenharmony_ci	dev_err(eth->dev, "%s: GMAC%d mode %s not supported!\n", __func__,
65362306a36Sopenharmony_ci		mac->id, phy_modes(state->interface));
65462306a36Sopenharmony_ci	return;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ciinit_err:
65762306a36Sopenharmony_ci	dev_err(eth->dev, "%s: GMAC%d mode %s err: %d!\n", __func__,
65862306a36Sopenharmony_ci		mac->id, phy_modes(state->interface), err);
65962306a36Sopenharmony_ci}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_cistatic int mtk_mac_finish(struct phylink_config *config, unsigned int mode,
66262306a36Sopenharmony_ci			  phy_interface_t interface)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci	struct mtk_mac *mac = container_of(config, struct mtk_mac,
66562306a36Sopenharmony_ci					   phylink_config);
66662306a36Sopenharmony_ci	struct mtk_eth *eth = mac->hw;
66762306a36Sopenharmony_ci	u32 mcr_cur, mcr_new;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	/* Enable SGMII */
67062306a36Sopenharmony_ci	if (interface == PHY_INTERFACE_MODE_SGMII ||
67162306a36Sopenharmony_ci	    phy_interface_mode_is_8023z(interface))
67262306a36Sopenharmony_ci		regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0,
67362306a36Sopenharmony_ci				   SYSCFG0_SGMII_MASK, mac->syscfg0);
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	/* Setup gmac */
67662306a36Sopenharmony_ci	mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
67762306a36Sopenharmony_ci	mcr_new = mcr_cur;
67862306a36Sopenharmony_ci	mcr_new |= MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE |
67962306a36Sopenharmony_ci		   MAC_MCR_BACKOFF_EN | MAC_MCR_BACKPR_EN | MAC_MCR_RX_FIFO_CLR_DIS;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	/* Only update control register when needed! */
68262306a36Sopenharmony_ci	if (mcr_new != mcr_cur)
68362306a36Sopenharmony_ci		mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id));
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	return 0;
68662306a36Sopenharmony_ci}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_cistatic void mtk_mac_link_down(struct phylink_config *config, unsigned int mode,
68962306a36Sopenharmony_ci			      phy_interface_t interface)
69062306a36Sopenharmony_ci{
69162306a36Sopenharmony_ci	struct mtk_mac *mac = container_of(config, struct mtk_mac,
69262306a36Sopenharmony_ci					   phylink_config);
69362306a36Sopenharmony_ci	u32 mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	mcr &= ~(MAC_MCR_TX_EN | MAC_MCR_RX_EN | MAC_MCR_FORCE_LINK);
69662306a36Sopenharmony_ci	mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id));
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_cistatic void mtk_set_queue_speed(struct mtk_eth *eth, unsigned int idx,
70062306a36Sopenharmony_ci				int speed)
70162306a36Sopenharmony_ci{
70262306a36Sopenharmony_ci	const struct mtk_soc_data *soc = eth->soc;
70362306a36Sopenharmony_ci	u32 ofs, val;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA))
70662306a36Sopenharmony_ci		return;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	val = MTK_QTX_SCH_MIN_RATE_EN |
70962306a36Sopenharmony_ci	      /* minimum: 10 Mbps */
71062306a36Sopenharmony_ci	      FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN, 1) |
71162306a36Sopenharmony_ci	      FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP, 4) |
71262306a36Sopenharmony_ci	      MTK_QTX_SCH_LEAKY_BUCKET_SIZE;
71362306a36Sopenharmony_ci	if (mtk_is_netsys_v1(eth))
71462306a36Sopenharmony_ci		val |= MTK_QTX_SCH_LEAKY_BUCKET_EN;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_SOC_MT7621)) {
71762306a36Sopenharmony_ci		switch (speed) {
71862306a36Sopenharmony_ci		case SPEED_10:
71962306a36Sopenharmony_ci			val |= MTK_QTX_SCH_MAX_RATE_EN |
72062306a36Sopenharmony_ci			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, 103) |
72162306a36Sopenharmony_ci			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, 2) |
72262306a36Sopenharmony_ci			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 1);
72362306a36Sopenharmony_ci			break;
72462306a36Sopenharmony_ci		case SPEED_100:
72562306a36Sopenharmony_ci			val |= MTK_QTX_SCH_MAX_RATE_EN |
72662306a36Sopenharmony_ci			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, 103) |
72762306a36Sopenharmony_ci			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, 3);
72862306a36Sopenharmony_ci			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 1);
72962306a36Sopenharmony_ci			break;
73062306a36Sopenharmony_ci		case SPEED_1000:
73162306a36Sopenharmony_ci			val |= MTK_QTX_SCH_MAX_RATE_EN |
73262306a36Sopenharmony_ci			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, 105) |
73362306a36Sopenharmony_ci			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, 4) |
73462306a36Sopenharmony_ci			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 10);
73562306a36Sopenharmony_ci			break;
73662306a36Sopenharmony_ci		default:
73762306a36Sopenharmony_ci			break;
73862306a36Sopenharmony_ci		}
73962306a36Sopenharmony_ci	} else {
74062306a36Sopenharmony_ci		switch (speed) {
74162306a36Sopenharmony_ci		case SPEED_10:
74262306a36Sopenharmony_ci			val |= MTK_QTX_SCH_MAX_RATE_EN |
74362306a36Sopenharmony_ci			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, 1) |
74462306a36Sopenharmony_ci			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, 4) |
74562306a36Sopenharmony_ci			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 1);
74662306a36Sopenharmony_ci			break;
74762306a36Sopenharmony_ci		case SPEED_100:
74862306a36Sopenharmony_ci			val |= MTK_QTX_SCH_MAX_RATE_EN |
74962306a36Sopenharmony_ci			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, 1) |
75062306a36Sopenharmony_ci			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, 5);
75162306a36Sopenharmony_ci			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 1);
75262306a36Sopenharmony_ci			break;
75362306a36Sopenharmony_ci		case SPEED_1000:
75462306a36Sopenharmony_ci			val |= MTK_QTX_SCH_MAX_RATE_EN |
75562306a36Sopenharmony_ci			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, 10) |
75662306a36Sopenharmony_ci			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, 5) |
75762306a36Sopenharmony_ci			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 10);
75862306a36Sopenharmony_ci			break;
75962306a36Sopenharmony_ci		default:
76062306a36Sopenharmony_ci			break;
76162306a36Sopenharmony_ci		}
76262306a36Sopenharmony_ci	}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	ofs = MTK_QTX_OFFSET * idx;
76562306a36Sopenharmony_ci	mtk_w32(eth, val, soc->reg_map->qdma.qtx_sch + ofs);
76662306a36Sopenharmony_ci}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_cistatic void mtk_mac_link_up(struct phylink_config *config,
76962306a36Sopenharmony_ci			    struct phy_device *phy,
77062306a36Sopenharmony_ci			    unsigned int mode, phy_interface_t interface,
77162306a36Sopenharmony_ci			    int speed, int duplex, bool tx_pause, bool rx_pause)
77262306a36Sopenharmony_ci{
77362306a36Sopenharmony_ci	struct mtk_mac *mac = container_of(config, struct mtk_mac,
77462306a36Sopenharmony_ci					   phylink_config);
77562306a36Sopenharmony_ci	u32 mcr;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
77862306a36Sopenharmony_ci	mcr &= ~(MAC_MCR_SPEED_100 | MAC_MCR_SPEED_1000 |
77962306a36Sopenharmony_ci		 MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_TX_FC |
78062306a36Sopenharmony_ci		 MAC_MCR_FORCE_RX_FC);
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	/* Configure speed */
78362306a36Sopenharmony_ci	mac->speed = speed;
78462306a36Sopenharmony_ci	switch (speed) {
78562306a36Sopenharmony_ci	case SPEED_2500:
78662306a36Sopenharmony_ci	case SPEED_1000:
78762306a36Sopenharmony_ci		mcr |= MAC_MCR_SPEED_1000;
78862306a36Sopenharmony_ci		break;
78962306a36Sopenharmony_ci	case SPEED_100:
79062306a36Sopenharmony_ci		mcr |= MAC_MCR_SPEED_100;
79162306a36Sopenharmony_ci		break;
79262306a36Sopenharmony_ci	}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	/* Configure duplex */
79562306a36Sopenharmony_ci	if (duplex == DUPLEX_FULL)
79662306a36Sopenharmony_ci		mcr |= MAC_MCR_FORCE_DPX;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	/* Configure pause modes - phylink will avoid these for half duplex */
79962306a36Sopenharmony_ci	if (tx_pause)
80062306a36Sopenharmony_ci		mcr |= MAC_MCR_FORCE_TX_FC;
80162306a36Sopenharmony_ci	if (rx_pause)
80262306a36Sopenharmony_ci		mcr |= MAC_MCR_FORCE_RX_FC;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	mcr |= MAC_MCR_TX_EN | MAC_MCR_RX_EN | MAC_MCR_FORCE_LINK;
80562306a36Sopenharmony_ci	mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id));
80662306a36Sopenharmony_ci}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_cistatic const struct phylink_mac_ops mtk_phylink_ops = {
80962306a36Sopenharmony_ci	.mac_select_pcs = mtk_mac_select_pcs,
81062306a36Sopenharmony_ci	.mac_config = mtk_mac_config,
81162306a36Sopenharmony_ci	.mac_finish = mtk_mac_finish,
81262306a36Sopenharmony_ci	.mac_link_down = mtk_mac_link_down,
81362306a36Sopenharmony_ci	.mac_link_up = mtk_mac_link_up,
81462306a36Sopenharmony_ci};
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_cistatic int mtk_mdio_init(struct mtk_eth *eth)
81762306a36Sopenharmony_ci{
81862306a36Sopenharmony_ci	unsigned int max_clk = 2500000, divider;
81962306a36Sopenharmony_ci	struct device_node *mii_np;
82062306a36Sopenharmony_ci	int ret;
82162306a36Sopenharmony_ci	u32 val;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	mii_np = of_get_child_by_name(eth->dev->of_node, "mdio-bus");
82462306a36Sopenharmony_ci	if (!mii_np) {
82562306a36Sopenharmony_ci		dev_err(eth->dev, "no %s child node found", "mdio-bus");
82662306a36Sopenharmony_ci		return -ENODEV;
82762306a36Sopenharmony_ci	}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	if (!of_device_is_available(mii_np)) {
83062306a36Sopenharmony_ci		ret = -ENODEV;
83162306a36Sopenharmony_ci		goto err_put_node;
83262306a36Sopenharmony_ci	}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	eth->mii_bus = devm_mdiobus_alloc(eth->dev);
83562306a36Sopenharmony_ci	if (!eth->mii_bus) {
83662306a36Sopenharmony_ci		ret = -ENOMEM;
83762306a36Sopenharmony_ci		goto err_put_node;
83862306a36Sopenharmony_ci	}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	eth->mii_bus->name = "mdio";
84162306a36Sopenharmony_ci	eth->mii_bus->read = mtk_mdio_read_c22;
84262306a36Sopenharmony_ci	eth->mii_bus->write = mtk_mdio_write_c22;
84362306a36Sopenharmony_ci	eth->mii_bus->read_c45 = mtk_mdio_read_c45;
84462306a36Sopenharmony_ci	eth->mii_bus->write_c45 = mtk_mdio_write_c45;
84562306a36Sopenharmony_ci	eth->mii_bus->priv = eth;
84662306a36Sopenharmony_ci	eth->mii_bus->parent = eth->dev;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	snprintf(eth->mii_bus->id, MII_BUS_ID_SIZE, "%pOFn", mii_np);
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	if (!of_property_read_u32(mii_np, "clock-frequency", &val)) {
85162306a36Sopenharmony_ci		if (val > MDC_MAX_FREQ || val < MDC_MAX_FREQ / MDC_MAX_DIVIDER) {
85262306a36Sopenharmony_ci			dev_err(eth->dev, "MDIO clock frequency out of range");
85362306a36Sopenharmony_ci			ret = -EINVAL;
85462306a36Sopenharmony_ci			goto err_put_node;
85562306a36Sopenharmony_ci		}
85662306a36Sopenharmony_ci		max_clk = val;
85762306a36Sopenharmony_ci	}
85862306a36Sopenharmony_ci	divider = min_t(unsigned int, DIV_ROUND_UP(MDC_MAX_FREQ, max_clk), 63);
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	/* Configure MDC Turbo Mode */
86162306a36Sopenharmony_ci	if (mtk_is_netsys_v3_or_greater(eth))
86262306a36Sopenharmony_ci		mtk_m32(eth, 0, MISC_MDC_TURBO, MTK_MAC_MISC_V3);
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	/* Configure MDC Divider */
86562306a36Sopenharmony_ci	val = FIELD_PREP(PPSC_MDC_CFG, divider);
86662306a36Sopenharmony_ci	if (!mtk_is_netsys_v3_or_greater(eth))
86762306a36Sopenharmony_ci		val |= PPSC_MDC_TURBO;
86862306a36Sopenharmony_ci	mtk_m32(eth, PPSC_MDC_CFG, val, MTK_PPSC);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	dev_dbg(eth->dev, "MDC is running on %d Hz\n", MDC_MAX_FREQ / divider);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	ret = of_mdiobus_register(eth->mii_bus, mii_np);
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_cierr_put_node:
87562306a36Sopenharmony_ci	of_node_put(mii_np);
87662306a36Sopenharmony_ci	return ret;
87762306a36Sopenharmony_ci}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_cistatic void mtk_mdio_cleanup(struct mtk_eth *eth)
88062306a36Sopenharmony_ci{
88162306a36Sopenharmony_ci	if (!eth->mii_bus)
88262306a36Sopenharmony_ci		return;
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	mdiobus_unregister(eth->mii_bus);
88562306a36Sopenharmony_ci}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_cistatic inline void mtk_tx_irq_disable(struct mtk_eth *eth, u32 mask)
88862306a36Sopenharmony_ci{
88962306a36Sopenharmony_ci	unsigned long flags;
89062306a36Sopenharmony_ci	u32 val;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	spin_lock_irqsave(&eth->tx_irq_lock, flags);
89362306a36Sopenharmony_ci	val = mtk_r32(eth, eth->soc->reg_map->tx_irq_mask);
89462306a36Sopenharmony_ci	mtk_w32(eth, val & ~mask, eth->soc->reg_map->tx_irq_mask);
89562306a36Sopenharmony_ci	spin_unlock_irqrestore(&eth->tx_irq_lock, flags);
89662306a36Sopenharmony_ci}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_cistatic inline void mtk_tx_irq_enable(struct mtk_eth *eth, u32 mask)
89962306a36Sopenharmony_ci{
90062306a36Sopenharmony_ci	unsigned long flags;
90162306a36Sopenharmony_ci	u32 val;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	spin_lock_irqsave(&eth->tx_irq_lock, flags);
90462306a36Sopenharmony_ci	val = mtk_r32(eth, eth->soc->reg_map->tx_irq_mask);
90562306a36Sopenharmony_ci	mtk_w32(eth, val | mask, eth->soc->reg_map->tx_irq_mask);
90662306a36Sopenharmony_ci	spin_unlock_irqrestore(&eth->tx_irq_lock, flags);
90762306a36Sopenharmony_ci}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_cistatic inline void mtk_rx_irq_disable(struct mtk_eth *eth, u32 mask)
91062306a36Sopenharmony_ci{
91162306a36Sopenharmony_ci	unsigned long flags;
91262306a36Sopenharmony_ci	u32 val;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	spin_lock_irqsave(&eth->rx_irq_lock, flags);
91562306a36Sopenharmony_ci	val = mtk_r32(eth, eth->soc->reg_map->pdma.irq_mask);
91662306a36Sopenharmony_ci	mtk_w32(eth, val & ~mask, eth->soc->reg_map->pdma.irq_mask);
91762306a36Sopenharmony_ci	spin_unlock_irqrestore(&eth->rx_irq_lock, flags);
91862306a36Sopenharmony_ci}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_cistatic inline void mtk_rx_irq_enable(struct mtk_eth *eth, u32 mask)
92162306a36Sopenharmony_ci{
92262306a36Sopenharmony_ci	unsigned long flags;
92362306a36Sopenharmony_ci	u32 val;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	spin_lock_irqsave(&eth->rx_irq_lock, flags);
92662306a36Sopenharmony_ci	val = mtk_r32(eth, eth->soc->reg_map->pdma.irq_mask);
92762306a36Sopenharmony_ci	mtk_w32(eth, val | mask, eth->soc->reg_map->pdma.irq_mask);
92862306a36Sopenharmony_ci	spin_unlock_irqrestore(&eth->rx_irq_lock, flags);
92962306a36Sopenharmony_ci}
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_cistatic int mtk_set_mac_address(struct net_device *dev, void *p)
93262306a36Sopenharmony_ci{
93362306a36Sopenharmony_ci	int ret = eth_mac_addr(dev, p);
93462306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
93562306a36Sopenharmony_ci	struct mtk_eth *eth = mac->hw;
93662306a36Sopenharmony_ci	const char *macaddr = dev->dev_addr;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	if (ret)
93962306a36Sopenharmony_ci		return ret;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	if (unlikely(test_bit(MTK_RESETTING, &mac->hw->state)))
94262306a36Sopenharmony_ci		return -EBUSY;
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	spin_lock_bh(&mac->hw->page_lock);
94562306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) {
94662306a36Sopenharmony_ci		mtk_w32(mac->hw, (macaddr[0] << 8) | macaddr[1],
94762306a36Sopenharmony_ci			MT7628_SDM_MAC_ADRH);
94862306a36Sopenharmony_ci		mtk_w32(mac->hw, (macaddr[2] << 24) | (macaddr[3] << 16) |
94962306a36Sopenharmony_ci			(macaddr[4] << 8) | macaddr[5],
95062306a36Sopenharmony_ci			MT7628_SDM_MAC_ADRL);
95162306a36Sopenharmony_ci	} else {
95262306a36Sopenharmony_ci		mtk_w32(mac->hw, (macaddr[0] << 8) | macaddr[1],
95362306a36Sopenharmony_ci			MTK_GDMA_MAC_ADRH(mac->id));
95462306a36Sopenharmony_ci		mtk_w32(mac->hw, (macaddr[2] << 24) | (macaddr[3] << 16) |
95562306a36Sopenharmony_ci			(macaddr[4] << 8) | macaddr[5],
95662306a36Sopenharmony_ci			MTK_GDMA_MAC_ADRL(mac->id));
95762306a36Sopenharmony_ci	}
95862306a36Sopenharmony_ci	spin_unlock_bh(&mac->hw->page_lock);
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	return 0;
96162306a36Sopenharmony_ci}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_civoid mtk_stats_update_mac(struct mtk_mac *mac)
96462306a36Sopenharmony_ci{
96562306a36Sopenharmony_ci	struct mtk_hw_stats *hw_stats = mac->hw_stats;
96662306a36Sopenharmony_ci	struct mtk_eth *eth = mac->hw;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	u64_stats_update_begin(&hw_stats->syncp);
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) {
97162306a36Sopenharmony_ci		hw_stats->tx_packets += mtk_r32(mac->hw, MT7628_SDM_TPCNT);
97262306a36Sopenharmony_ci		hw_stats->tx_bytes += mtk_r32(mac->hw, MT7628_SDM_TBCNT);
97362306a36Sopenharmony_ci		hw_stats->rx_packets += mtk_r32(mac->hw, MT7628_SDM_RPCNT);
97462306a36Sopenharmony_ci		hw_stats->rx_bytes += mtk_r32(mac->hw, MT7628_SDM_RBCNT);
97562306a36Sopenharmony_ci		hw_stats->rx_checksum_errors +=
97662306a36Sopenharmony_ci			mtk_r32(mac->hw, MT7628_SDM_CS_ERR);
97762306a36Sopenharmony_ci	} else {
97862306a36Sopenharmony_ci		const struct mtk_reg_map *reg_map = eth->soc->reg_map;
97962306a36Sopenharmony_ci		unsigned int offs = hw_stats->reg_offset;
98062306a36Sopenharmony_ci		u64 stats;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci		hw_stats->rx_bytes += mtk_r32(mac->hw, reg_map->gdm1_cnt + offs);
98362306a36Sopenharmony_ci		stats = mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x4 + offs);
98462306a36Sopenharmony_ci		if (stats)
98562306a36Sopenharmony_ci			hw_stats->rx_bytes += (stats << 32);
98662306a36Sopenharmony_ci		hw_stats->rx_packets +=
98762306a36Sopenharmony_ci			mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x8 + offs);
98862306a36Sopenharmony_ci		hw_stats->rx_overflow +=
98962306a36Sopenharmony_ci			mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x10 + offs);
99062306a36Sopenharmony_ci		hw_stats->rx_fcs_errors +=
99162306a36Sopenharmony_ci			mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x14 + offs);
99262306a36Sopenharmony_ci		hw_stats->rx_short_errors +=
99362306a36Sopenharmony_ci			mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x18 + offs);
99462306a36Sopenharmony_ci		hw_stats->rx_long_errors +=
99562306a36Sopenharmony_ci			mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x1c + offs);
99662306a36Sopenharmony_ci		hw_stats->rx_checksum_errors +=
99762306a36Sopenharmony_ci			mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x20 + offs);
99862306a36Sopenharmony_ci		hw_stats->rx_flow_control_packets +=
99962306a36Sopenharmony_ci			mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x24 + offs);
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci		if (mtk_is_netsys_v3_or_greater(eth)) {
100262306a36Sopenharmony_ci			hw_stats->tx_skip +=
100362306a36Sopenharmony_ci				mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x50 + offs);
100462306a36Sopenharmony_ci			hw_stats->tx_collisions +=
100562306a36Sopenharmony_ci				mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x54 + offs);
100662306a36Sopenharmony_ci			hw_stats->tx_bytes +=
100762306a36Sopenharmony_ci				mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x40 + offs);
100862306a36Sopenharmony_ci			stats =  mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x44 + offs);
100962306a36Sopenharmony_ci			if (stats)
101062306a36Sopenharmony_ci				hw_stats->tx_bytes += (stats << 32);
101162306a36Sopenharmony_ci			hw_stats->tx_packets +=
101262306a36Sopenharmony_ci				mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x48 + offs);
101362306a36Sopenharmony_ci		} else {
101462306a36Sopenharmony_ci			hw_stats->tx_skip +=
101562306a36Sopenharmony_ci				mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x28 + offs);
101662306a36Sopenharmony_ci			hw_stats->tx_collisions +=
101762306a36Sopenharmony_ci				mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x2c + offs);
101862306a36Sopenharmony_ci			hw_stats->tx_bytes +=
101962306a36Sopenharmony_ci				mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x30 + offs);
102062306a36Sopenharmony_ci			stats =  mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x34 + offs);
102162306a36Sopenharmony_ci			if (stats)
102262306a36Sopenharmony_ci				hw_stats->tx_bytes += (stats << 32);
102362306a36Sopenharmony_ci			hw_stats->tx_packets +=
102462306a36Sopenharmony_ci				mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x38 + offs);
102562306a36Sopenharmony_ci		}
102662306a36Sopenharmony_ci	}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	u64_stats_update_end(&hw_stats->syncp);
102962306a36Sopenharmony_ci}
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_cistatic void mtk_stats_update(struct mtk_eth *eth)
103262306a36Sopenharmony_ci{
103362306a36Sopenharmony_ci	int i;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	for (i = 0; i < MTK_MAX_DEVS; i++) {
103662306a36Sopenharmony_ci		if (!eth->mac[i] || !eth->mac[i]->hw_stats)
103762306a36Sopenharmony_ci			continue;
103862306a36Sopenharmony_ci		if (spin_trylock(&eth->mac[i]->hw_stats->stats_lock)) {
103962306a36Sopenharmony_ci			mtk_stats_update_mac(eth->mac[i]);
104062306a36Sopenharmony_ci			spin_unlock(&eth->mac[i]->hw_stats->stats_lock);
104162306a36Sopenharmony_ci		}
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_cistatic void mtk_get_stats64(struct net_device *dev,
104662306a36Sopenharmony_ci			    struct rtnl_link_stats64 *storage)
104762306a36Sopenharmony_ci{
104862306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
104962306a36Sopenharmony_ci	struct mtk_hw_stats *hw_stats = mac->hw_stats;
105062306a36Sopenharmony_ci	unsigned int start;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	if (netif_running(dev) && netif_device_present(dev)) {
105362306a36Sopenharmony_ci		if (spin_trylock_bh(&hw_stats->stats_lock)) {
105462306a36Sopenharmony_ci			mtk_stats_update_mac(mac);
105562306a36Sopenharmony_ci			spin_unlock_bh(&hw_stats->stats_lock);
105662306a36Sopenharmony_ci		}
105762306a36Sopenharmony_ci	}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	do {
106062306a36Sopenharmony_ci		start = u64_stats_fetch_begin(&hw_stats->syncp);
106162306a36Sopenharmony_ci		storage->rx_packets = hw_stats->rx_packets;
106262306a36Sopenharmony_ci		storage->tx_packets = hw_stats->tx_packets;
106362306a36Sopenharmony_ci		storage->rx_bytes = hw_stats->rx_bytes;
106462306a36Sopenharmony_ci		storage->tx_bytes = hw_stats->tx_bytes;
106562306a36Sopenharmony_ci		storage->collisions = hw_stats->tx_collisions;
106662306a36Sopenharmony_ci		storage->rx_length_errors = hw_stats->rx_short_errors +
106762306a36Sopenharmony_ci			hw_stats->rx_long_errors;
106862306a36Sopenharmony_ci		storage->rx_over_errors = hw_stats->rx_overflow;
106962306a36Sopenharmony_ci		storage->rx_crc_errors = hw_stats->rx_fcs_errors;
107062306a36Sopenharmony_ci		storage->rx_errors = hw_stats->rx_checksum_errors;
107162306a36Sopenharmony_ci		storage->tx_aborted_errors = hw_stats->tx_skip;
107262306a36Sopenharmony_ci	} while (u64_stats_fetch_retry(&hw_stats->syncp, start));
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	storage->tx_errors = dev->stats.tx_errors;
107562306a36Sopenharmony_ci	storage->rx_dropped = dev->stats.rx_dropped;
107662306a36Sopenharmony_ci	storage->tx_dropped = dev->stats.tx_dropped;
107762306a36Sopenharmony_ci}
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_cistatic inline int mtk_max_frag_size(int mtu)
108062306a36Sopenharmony_ci{
108162306a36Sopenharmony_ci	/* make sure buf_size will be at least MTK_MAX_RX_LENGTH */
108262306a36Sopenharmony_ci	if (mtu + MTK_RX_ETH_HLEN < MTK_MAX_RX_LENGTH_2K)
108362306a36Sopenharmony_ci		mtu = MTK_MAX_RX_LENGTH_2K - MTK_RX_ETH_HLEN;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	return SKB_DATA_ALIGN(MTK_RX_HLEN + mtu) +
108662306a36Sopenharmony_ci		SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
108762306a36Sopenharmony_ci}
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_cistatic inline int mtk_max_buf_size(int frag_size)
109062306a36Sopenharmony_ci{
109162306a36Sopenharmony_ci	int buf_size = frag_size - NET_SKB_PAD - NET_IP_ALIGN -
109262306a36Sopenharmony_ci		       SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	WARN_ON(buf_size < MTK_MAX_RX_LENGTH_2K);
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	return buf_size;
109762306a36Sopenharmony_ci}
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_cistatic bool mtk_rx_get_desc(struct mtk_eth *eth, struct mtk_rx_dma_v2 *rxd,
110062306a36Sopenharmony_ci			    struct mtk_rx_dma_v2 *dma_rxd)
110162306a36Sopenharmony_ci{
110262306a36Sopenharmony_ci	rxd->rxd2 = READ_ONCE(dma_rxd->rxd2);
110362306a36Sopenharmony_ci	if (!(rxd->rxd2 & RX_DMA_DONE))
110462306a36Sopenharmony_ci		return false;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	rxd->rxd1 = READ_ONCE(dma_rxd->rxd1);
110762306a36Sopenharmony_ci	rxd->rxd3 = READ_ONCE(dma_rxd->rxd3);
110862306a36Sopenharmony_ci	rxd->rxd4 = READ_ONCE(dma_rxd->rxd4);
110962306a36Sopenharmony_ci	if (mtk_is_netsys_v2_or_greater(eth)) {
111062306a36Sopenharmony_ci		rxd->rxd5 = READ_ONCE(dma_rxd->rxd5);
111162306a36Sopenharmony_ci		rxd->rxd6 = READ_ONCE(dma_rxd->rxd6);
111262306a36Sopenharmony_ci	}
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	return true;
111562306a36Sopenharmony_ci}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_cistatic void *mtk_max_lro_buf_alloc(gfp_t gfp_mask)
111862306a36Sopenharmony_ci{
111962306a36Sopenharmony_ci	unsigned int size = mtk_max_frag_size(MTK_MAX_LRO_RX_LENGTH);
112062306a36Sopenharmony_ci	unsigned long data;
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	data = __get_free_pages(gfp_mask | __GFP_COMP | __GFP_NOWARN,
112362306a36Sopenharmony_ci				get_order(size));
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	return (void *)data;
112662306a36Sopenharmony_ci}
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci/* the qdma core needs scratch memory to be setup */
112962306a36Sopenharmony_cistatic int mtk_init_fq_dma(struct mtk_eth *eth)
113062306a36Sopenharmony_ci{
113162306a36Sopenharmony_ci	const struct mtk_soc_data *soc = eth->soc;
113262306a36Sopenharmony_ci	dma_addr_t phy_ring_tail;
113362306a36Sopenharmony_ci	int cnt = MTK_QDMA_RING_SIZE;
113462306a36Sopenharmony_ci	dma_addr_t dma_addr;
113562306a36Sopenharmony_ci	int i;
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_SRAM))
113862306a36Sopenharmony_ci		eth->scratch_ring = eth->sram_base;
113962306a36Sopenharmony_ci	else
114062306a36Sopenharmony_ci		eth->scratch_ring = dma_alloc_coherent(eth->dma_dev,
114162306a36Sopenharmony_ci						       cnt * soc->txrx.txd_size,
114262306a36Sopenharmony_ci						       &eth->phy_scratch_ring,
114362306a36Sopenharmony_ci						       GFP_KERNEL);
114462306a36Sopenharmony_ci	if (unlikely(!eth->scratch_ring))
114562306a36Sopenharmony_ci		return -ENOMEM;
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	eth->scratch_head = kcalloc(cnt, MTK_QDMA_PAGE_SIZE, GFP_KERNEL);
114862306a36Sopenharmony_ci	if (unlikely(!eth->scratch_head))
114962306a36Sopenharmony_ci		return -ENOMEM;
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	dma_addr = dma_map_single(eth->dma_dev,
115262306a36Sopenharmony_ci				  eth->scratch_head, cnt * MTK_QDMA_PAGE_SIZE,
115362306a36Sopenharmony_ci				  DMA_FROM_DEVICE);
115462306a36Sopenharmony_ci	if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr)))
115562306a36Sopenharmony_ci		return -ENOMEM;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	phy_ring_tail = eth->phy_scratch_ring + soc->txrx.txd_size * (cnt - 1);
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	for (i = 0; i < cnt; i++) {
116062306a36Sopenharmony_ci		struct mtk_tx_dma_v2 *txd;
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci		txd = eth->scratch_ring + i * soc->txrx.txd_size;
116362306a36Sopenharmony_ci		txd->txd1 = dma_addr + i * MTK_QDMA_PAGE_SIZE;
116462306a36Sopenharmony_ci		if (i < cnt - 1)
116562306a36Sopenharmony_ci			txd->txd2 = eth->phy_scratch_ring +
116662306a36Sopenharmony_ci				    (i + 1) * soc->txrx.txd_size;
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci		txd->txd3 = TX_DMA_PLEN0(MTK_QDMA_PAGE_SIZE);
116962306a36Sopenharmony_ci		txd->txd4 = 0;
117062306a36Sopenharmony_ci		if (mtk_is_netsys_v2_or_greater(eth)) {
117162306a36Sopenharmony_ci			txd->txd5 = 0;
117262306a36Sopenharmony_ci			txd->txd6 = 0;
117362306a36Sopenharmony_ci			txd->txd7 = 0;
117462306a36Sopenharmony_ci			txd->txd8 = 0;
117562306a36Sopenharmony_ci		}
117662306a36Sopenharmony_ci	}
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	mtk_w32(eth, eth->phy_scratch_ring, soc->reg_map->qdma.fq_head);
117962306a36Sopenharmony_ci	mtk_w32(eth, phy_ring_tail, soc->reg_map->qdma.fq_tail);
118062306a36Sopenharmony_ci	mtk_w32(eth, (cnt << 16) | cnt, soc->reg_map->qdma.fq_count);
118162306a36Sopenharmony_ci	mtk_w32(eth, MTK_QDMA_PAGE_SIZE << 16, soc->reg_map->qdma.fq_blen);
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	return 0;
118462306a36Sopenharmony_ci}
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_cistatic void *mtk_qdma_phys_to_virt(struct mtk_tx_ring *ring, u32 desc)
118762306a36Sopenharmony_ci{
118862306a36Sopenharmony_ci	return ring->dma + (desc - ring->phys);
118962306a36Sopenharmony_ci}
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_cistatic struct mtk_tx_buf *mtk_desc_to_tx_buf(struct mtk_tx_ring *ring,
119262306a36Sopenharmony_ci					     void *txd, u32 txd_size)
119362306a36Sopenharmony_ci{
119462306a36Sopenharmony_ci	int idx = (txd - ring->dma) / txd_size;
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	return &ring->buf[idx];
119762306a36Sopenharmony_ci}
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_cistatic struct mtk_tx_dma *qdma_to_pdma(struct mtk_tx_ring *ring,
120062306a36Sopenharmony_ci				       struct mtk_tx_dma *dma)
120162306a36Sopenharmony_ci{
120262306a36Sopenharmony_ci	return ring->dma_pdma - (struct mtk_tx_dma *)ring->dma + dma;
120362306a36Sopenharmony_ci}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_cistatic int txd_to_idx(struct mtk_tx_ring *ring, void *dma, u32 txd_size)
120662306a36Sopenharmony_ci{
120762306a36Sopenharmony_ci	return (dma - ring->dma) / txd_size;
120862306a36Sopenharmony_ci}
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_cistatic void mtk_tx_unmap(struct mtk_eth *eth, struct mtk_tx_buf *tx_buf,
121162306a36Sopenharmony_ci			 struct xdp_frame_bulk *bq, bool napi)
121262306a36Sopenharmony_ci{
121362306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
121462306a36Sopenharmony_ci		if (tx_buf->flags & MTK_TX_FLAGS_SINGLE0) {
121562306a36Sopenharmony_ci			dma_unmap_single(eth->dma_dev,
121662306a36Sopenharmony_ci					 dma_unmap_addr(tx_buf, dma_addr0),
121762306a36Sopenharmony_ci					 dma_unmap_len(tx_buf, dma_len0),
121862306a36Sopenharmony_ci					 DMA_TO_DEVICE);
121962306a36Sopenharmony_ci		} else if (tx_buf->flags & MTK_TX_FLAGS_PAGE0) {
122062306a36Sopenharmony_ci			dma_unmap_page(eth->dma_dev,
122162306a36Sopenharmony_ci				       dma_unmap_addr(tx_buf, dma_addr0),
122262306a36Sopenharmony_ci				       dma_unmap_len(tx_buf, dma_len0),
122362306a36Sopenharmony_ci				       DMA_TO_DEVICE);
122462306a36Sopenharmony_ci		}
122562306a36Sopenharmony_ci	} else {
122662306a36Sopenharmony_ci		if (dma_unmap_len(tx_buf, dma_len0)) {
122762306a36Sopenharmony_ci			dma_unmap_page(eth->dma_dev,
122862306a36Sopenharmony_ci				       dma_unmap_addr(tx_buf, dma_addr0),
122962306a36Sopenharmony_ci				       dma_unmap_len(tx_buf, dma_len0),
123062306a36Sopenharmony_ci				       DMA_TO_DEVICE);
123162306a36Sopenharmony_ci		}
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci		if (dma_unmap_len(tx_buf, dma_len1)) {
123462306a36Sopenharmony_ci			dma_unmap_page(eth->dma_dev,
123562306a36Sopenharmony_ci				       dma_unmap_addr(tx_buf, dma_addr1),
123662306a36Sopenharmony_ci				       dma_unmap_len(tx_buf, dma_len1),
123762306a36Sopenharmony_ci				       DMA_TO_DEVICE);
123862306a36Sopenharmony_ci		}
123962306a36Sopenharmony_ci	}
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	if (tx_buf->data && tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) {
124262306a36Sopenharmony_ci		if (tx_buf->type == MTK_TYPE_SKB) {
124362306a36Sopenharmony_ci			struct sk_buff *skb = tx_buf->data;
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci			if (napi)
124662306a36Sopenharmony_ci				napi_consume_skb(skb, napi);
124762306a36Sopenharmony_ci			else
124862306a36Sopenharmony_ci				dev_kfree_skb_any(skb);
124962306a36Sopenharmony_ci		} else {
125062306a36Sopenharmony_ci			struct xdp_frame *xdpf = tx_buf->data;
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci			if (napi && tx_buf->type == MTK_TYPE_XDP_TX)
125362306a36Sopenharmony_ci				xdp_return_frame_rx_napi(xdpf);
125462306a36Sopenharmony_ci			else if (bq)
125562306a36Sopenharmony_ci				xdp_return_frame_bulk(xdpf, bq);
125662306a36Sopenharmony_ci			else
125762306a36Sopenharmony_ci				xdp_return_frame(xdpf);
125862306a36Sopenharmony_ci		}
125962306a36Sopenharmony_ci	}
126062306a36Sopenharmony_ci	tx_buf->flags = 0;
126162306a36Sopenharmony_ci	tx_buf->data = NULL;
126262306a36Sopenharmony_ci}
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_cistatic void setup_tx_buf(struct mtk_eth *eth, struct mtk_tx_buf *tx_buf,
126562306a36Sopenharmony_ci			 struct mtk_tx_dma *txd, dma_addr_t mapped_addr,
126662306a36Sopenharmony_ci			 size_t size, int idx)
126762306a36Sopenharmony_ci{
126862306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
126962306a36Sopenharmony_ci		dma_unmap_addr_set(tx_buf, dma_addr0, mapped_addr);
127062306a36Sopenharmony_ci		dma_unmap_len_set(tx_buf, dma_len0, size);
127162306a36Sopenharmony_ci	} else {
127262306a36Sopenharmony_ci		if (idx & 1) {
127362306a36Sopenharmony_ci			txd->txd3 = mapped_addr;
127462306a36Sopenharmony_ci			txd->txd2 |= TX_DMA_PLEN1(size);
127562306a36Sopenharmony_ci			dma_unmap_addr_set(tx_buf, dma_addr1, mapped_addr);
127662306a36Sopenharmony_ci			dma_unmap_len_set(tx_buf, dma_len1, size);
127762306a36Sopenharmony_ci		} else {
127862306a36Sopenharmony_ci			tx_buf->data = (void *)MTK_DMA_DUMMY_DESC;
127962306a36Sopenharmony_ci			txd->txd1 = mapped_addr;
128062306a36Sopenharmony_ci			txd->txd2 = TX_DMA_PLEN0(size);
128162306a36Sopenharmony_ci			dma_unmap_addr_set(tx_buf, dma_addr0, mapped_addr);
128262306a36Sopenharmony_ci			dma_unmap_len_set(tx_buf, dma_len0, size);
128362306a36Sopenharmony_ci		}
128462306a36Sopenharmony_ci	}
128562306a36Sopenharmony_ci}
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_cistatic void mtk_tx_set_dma_desc_v1(struct net_device *dev, void *txd,
128862306a36Sopenharmony_ci				   struct mtk_tx_dma_desc_info *info)
128962306a36Sopenharmony_ci{
129062306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
129162306a36Sopenharmony_ci	struct mtk_eth *eth = mac->hw;
129262306a36Sopenharmony_ci	struct mtk_tx_dma *desc = txd;
129362306a36Sopenharmony_ci	u32 data;
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	WRITE_ONCE(desc->txd1, info->addr);
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	data = TX_DMA_SWC | TX_DMA_PLEN0(info->size) |
129862306a36Sopenharmony_ci	       FIELD_PREP(TX_DMA_PQID, info->qid);
129962306a36Sopenharmony_ci	if (info->last)
130062306a36Sopenharmony_ci		data |= TX_DMA_LS0;
130162306a36Sopenharmony_ci	WRITE_ONCE(desc->txd3, data);
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci	data = (mac->id + 1) << TX_DMA_FPORT_SHIFT; /* forward port */
130462306a36Sopenharmony_ci	if (info->first) {
130562306a36Sopenharmony_ci		if (info->gso)
130662306a36Sopenharmony_ci			data |= TX_DMA_TSO;
130762306a36Sopenharmony_ci		/* tx checksum offload */
130862306a36Sopenharmony_ci		if (info->csum)
130962306a36Sopenharmony_ci			data |= TX_DMA_CHKSUM;
131062306a36Sopenharmony_ci		/* vlan header offload */
131162306a36Sopenharmony_ci		if (info->vlan)
131262306a36Sopenharmony_ci			data |= TX_DMA_INS_VLAN | info->vlan_tci;
131362306a36Sopenharmony_ci	}
131462306a36Sopenharmony_ci	WRITE_ONCE(desc->txd4, data);
131562306a36Sopenharmony_ci}
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_cistatic void mtk_tx_set_dma_desc_v2(struct net_device *dev, void *txd,
131862306a36Sopenharmony_ci				   struct mtk_tx_dma_desc_info *info)
131962306a36Sopenharmony_ci{
132062306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
132162306a36Sopenharmony_ci	struct mtk_tx_dma_v2 *desc = txd;
132262306a36Sopenharmony_ci	struct mtk_eth *eth = mac->hw;
132362306a36Sopenharmony_ci	u32 data;
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	WRITE_ONCE(desc->txd1, info->addr);
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	data = TX_DMA_PLEN0(info->size);
132862306a36Sopenharmony_ci	if (info->last)
132962306a36Sopenharmony_ci		data |= TX_DMA_LS0;
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_36BIT_DMA))
133262306a36Sopenharmony_ci		data |= TX_DMA_PREP_ADDR64(info->addr);
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	WRITE_ONCE(desc->txd3, data);
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	 /* set forward port */
133762306a36Sopenharmony_ci	switch (mac->id) {
133862306a36Sopenharmony_ci	case MTK_GMAC1_ID:
133962306a36Sopenharmony_ci		data = PSE_GDM1_PORT << TX_DMA_FPORT_SHIFT_V2;
134062306a36Sopenharmony_ci		break;
134162306a36Sopenharmony_ci	case MTK_GMAC2_ID:
134262306a36Sopenharmony_ci		data = PSE_GDM2_PORT << TX_DMA_FPORT_SHIFT_V2;
134362306a36Sopenharmony_ci		break;
134462306a36Sopenharmony_ci	case MTK_GMAC3_ID:
134562306a36Sopenharmony_ci		data = PSE_GDM3_PORT << TX_DMA_FPORT_SHIFT_V2;
134662306a36Sopenharmony_ci		break;
134762306a36Sopenharmony_ci	}
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	data |= TX_DMA_SWC_V2 | QID_BITS_V2(info->qid);
135062306a36Sopenharmony_ci	WRITE_ONCE(desc->txd4, data);
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	data = 0;
135362306a36Sopenharmony_ci	if (info->first) {
135462306a36Sopenharmony_ci		if (info->gso)
135562306a36Sopenharmony_ci			data |= TX_DMA_TSO_V2;
135662306a36Sopenharmony_ci		/* tx checksum offload */
135762306a36Sopenharmony_ci		if (info->csum)
135862306a36Sopenharmony_ci			data |= TX_DMA_CHKSUM_V2;
135962306a36Sopenharmony_ci		if (mtk_is_netsys_v3_or_greater(eth) && netdev_uses_dsa(dev))
136062306a36Sopenharmony_ci			data |= TX_DMA_SPTAG_V3;
136162306a36Sopenharmony_ci	}
136262306a36Sopenharmony_ci	WRITE_ONCE(desc->txd5, data);
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	data = 0;
136562306a36Sopenharmony_ci	if (info->first && info->vlan)
136662306a36Sopenharmony_ci		data |= TX_DMA_INS_VLAN_V2 | info->vlan_tci;
136762306a36Sopenharmony_ci	WRITE_ONCE(desc->txd6, data);
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	WRITE_ONCE(desc->txd7, 0);
137062306a36Sopenharmony_ci	WRITE_ONCE(desc->txd8, 0);
137162306a36Sopenharmony_ci}
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_cistatic void mtk_tx_set_dma_desc(struct net_device *dev, void *txd,
137462306a36Sopenharmony_ci				struct mtk_tx_dma_desc_info *info)
137562306a36Sopenharmony_ci{
137662306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
137762306a36Sopenharmony_ci	struct mtk_eth *eth = mac->hw;
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	if (mtk_is_netsys_v2_or_greater(eth))
138062306a36Sopenharmony_ci		mtk_tx_set_dma_desc_v2(dev, txd, info);
138162306a36Sopenharmony_ci	else
138262306a36Sopenharmony_ci		mtk_tx_set_dma_desc_v1(dev, txd, info);
138362306a36Sopenharmony_ci}
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_cistatic int mtk_tx_map(struct sk_buff *skb, struct net_device *dev,
138662306a36Sopenharmony_ci		      int tx_num, struct mtk_tx_ring *ring, bool gso)
138762306a36Sopenharmony_ci{
138862306a36Sopenharmony_ci	struct mtk_tx_dma_desc_info txd_info = {
138962306a36Sopenharmony_ci		.size = skb_headlen(skb),
139062306a36Sopenharmony_ci		.gso = gso,
139162306a36Sopenharmony_ci		.csum = skb->ip_summed == CHECKSUM_PARTIAL,
139262306a36Sopenharmony_ci		.vlan = skb_vlan_tag_present(skb),
139362306a36Sopenharmony_ci		.qid = skb_get_queue_mapping(skb),
139462306a36Sopenharmony_ci		.vlan_tci = skb_vlan_tag_get(skb),
139562306a36Sopenharmony_ci		.first = true,
139662306a36Sopenharmony_ci		.last = !skb_is_nonlinear(skb),
139762306a36Sopenharmony_ci	};
139862306a36Sopenharmony_ci	struct netdev_queue *txq;
139962306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
140062306a36Sopenharmony_ci	struct mtk_eth *eth = mac->hw;
140162306a36Sopenharmony_ci	const struct mtk_soc_data *soc = eth->soc;
140262306a36Sopenharmony_ci	struct mtk_tx_dma *itxd, *txd;
140362306a36Sopenharmony_ci	struct mtk_tx_dma *itxd_pdma, *txd_pdma;
140462306a36Sopenharmony_ci	struct mtk_tx_buf *itx_buf, *tx_buf;
140562306a36Sopenharmony_ci	int i, n_desc = 1;
140662306a36Sopenharmony_ci	int queue = skb_get_queue_mapping(skb);
140762306a36Sopenharmony_ci	int k = 0;
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	txq = netdev_get_tx_queue(dev, queue);
141062306a36Sopenharmony_ci	itxd = ring->next_free;
141162306a36Sopenharmony_ci	itxd_pdma = qdma_to_pdma(ring, itxd);
141262306a36Sopenharmony_ci	if (itxd == ring->last_free)
141362306a36Sopenharmony_ci		return -ENOMEM;
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci	itx_buf = mtk_desc_to_tx_buf(ring, itxd, soc->txrx.txd_size);
141662306a36Sopenharmony_ci	memset(itx_buf, 0, sizeof(*itx_buf));
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	txd_info.addr = dma_map_single(eth->dma_dev, skb->data, txd_info.size,
141962306a36Sopenharmony_ci				       DMA_TO_DEVICE);
142062306a36Sopenharmony_ci	if (unlikely(dma_mapping_error(eth->dma_dev, txd_info.addr)))
142162306a36Sopenharmony_ci		return -ENOMEM;
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	mtk_tx_set_dma_desc(dev, itxd, &txd_info);
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	itx_buf->flags |= MTK_TX_FLAGS_SINGLE0;
142662306a36Sopenharmony_ci	itx_buf->mac_id = mac->id;
142762306a36Sopenharmony_ci	setup_tx_buf(eth, itx_buf, itxd_pdma, txd_info.addr, txd_info.size,
142862306a36Sopenharmony_ci		     k++);
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	/* TX SG offload */
143162306a36Sopenharmony_ci	txd = itxd;
143262306a36Sopenharmony_ci	txd_pdma = qdma_to_pdma(ring, txd);
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
143562306a36Sopenharmony_ci		skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
143662306a36Sopenharmony_ci		unsigned int offset = 0;
143762306a36Sopenharmony_ci		int frag_size = skb_frag_size(frag);
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci		while (frag_size) {
144062306a36Sopenharmony_ci			bool new_desc = true;
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci			if (MTK_HAS_CAPS(soc->caps, MTK_QDMA) ||
144362306a36Sopenharmony_ci			    (i & 0x1)) {
144462306a36Sopenharmony_ci				txd = mtk_qdma_phys_to_virt(ring, txd->txd2);
144562306a36Sopenharmony_ci				txd_pdma = qdma_to_pdma(ring, txd);
144662306a36Sopenharmony_ci				if (txd == ring->last_free)
144762306a36Sopenharmony_ci					goto err_dma;
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci				n_desc++;
145062306a36Sopenharmony_ci			} else {
145162306a36Sopenharmony_ci				new_desc = false;
145262306a36Sopenharmony_ci			}
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci			memset(&txd_info, 0, sizeof(struct mtk_tx_dma_desc_info));
145562306a36Sopenharmony_ci			txd_info.size = min_t(unsigned int, frag_size,
145662306a36Sopenharmony_ci					      soc->txrx.dma_max_len);
145762306a36Sopenharmony_ci			txd_info.qid = queue;
145862306a36Sopenharmony_ci			txd_info.last = i == skb_shinfo(skb)->nr_frags - 1 &&
145962306a36Sopenharmony_ci					!(frag_size - txd_info.size);
146062306a36Sopenharmony_ci			txd_info.addr = skb_frag_dma_map(eth->dma_dev, frag,
146162306a36Sopenharmony_ci							 offset, txd_info.size,
146262306a36Sopenharmony_ci							 DMA_TO_DEVICE);
146362306a36Sopenharmony_ci			if (unlikely(dma_mapping_error(eth->dma_dev, txd_info.addr)))
146462306a36Sopenharmony_ci				goto err_dma;
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci			mtk_tx_set_dma_desc(dev, txd, &txd_info);
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci			tx_buf = mtk_desc_to_tx_buf(ring, txd,
146962306a36Sopenharmony_ci						    soc->txrx.txd_size);
147062306a36Sopenharmony_ci			if (new_desc)
147162306a36Sopenharmony_ci				memset(tx_buf, 0, sizeof(*tx_buf));
147262306a36Sopenharmony_ci			tx_buf->data = (void *)MTK_DMA_DUMMY_DESC;
147362306a36Sopenharmony_ci			tx_buf->flags |= MTK_TX_FLAGS_PAGE0;
147462306a36Sopenharmony_ci			tx_buf->mac_id = mac->id;
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci			setup_tx_buf(eth, tx_buf, txd_pdma, txd_info.addr,
147762306a36Sopenharmony_ci				     txd_info.size, k++);
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci			frag_size -= txd_info.size;
148062306a36Sopenharmony_ci			offset += txd_info.size;
148162306a36Sopenharmony_ci		}
148262306a36Sopenharmony_ci	}
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	/* store skb to cleanup */
148562306a36Sopenharmony_ci	itx_buf->type = MTK_TYPE_SKB;
148662306a36Sopenharmony_ci	itx_buf->data = skb;
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) {
148962306a36Sopenharmony_ci		if (k & 0x1)
149062306a36Sopenharmony_ci			txd_pdma->txd2 |= TX_DMA_LS0;
149162306a36Sopenharmony_ci		else
149262306a36Sopenharmony_ci			txd_pdma->txd2 |= TX_DMA_LS1;
149362306a36Sopenharmony_ci	}
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	netdev_tx_sent_queue(txq, skb->len);
149662306a36Sopenharmony_ci	skb_tx_timestamp(skb);
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	ring->next_free = mtk_qdma_phys_to_virt(ring, txd->txd2);
149962306a36Sopenharmony_ci	atomic_sub(n_desc, &ring->free_count);
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	/* make sure that all changes to the dma ring are flushed before we
150262306a36Sopenharmony_ci	 * continue
150362306a36Sopenharmony_ci	 */
150462306a36Sopenharmony_ci	wmb();
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	if (MTK_HAS_CAPS(soc->caps, MTK_QDMA)) {
150762306a36Sopenharmony_ci		if (netif_xmit_stopped(txq) || !netdev_xmit_more())
150862306a36Sopenharmony_ci			mtk_w32(eth, txd->txd2, soc->reg_map->qdma.ctx_ptr);
150962306a36Sopenharmony_ci	} else {
151062306a36Sopenharmony_ci		int next_idx;
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci		next_idx = NEXT_DESP_IDX(txd_to_idx(ring, txd, soc->txrx.txd_size),
151362306a36Sopenharmony_ci					 ring->dma_size);
151462306a36Sopenharmony_ci		mtk_w32(eth, next_idx, MT7628_TX_CTX_IDX0);
151562306a36Sopenharmony_ci	}
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	return 0;
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_cierr_dma:
152062306a36Sopenharmony_ci	do {
152162306a36Sopenharmony_ci		tx_buf = mtk_desc_to_tx_buf(ring, itxd, soc->txrx.txd_size);
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci		/* unmap dma */
152462306a36Sopenharmony_ci		mtk_tx_unmap(eth, tx_buf, NULL, false);
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci		itxd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU;
152762306a36Sopenharmony_ci		if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA))
152862306a36Sopenharmony_ci			itxd_pdma->txd2 = TX_DMA_DESP2_DEF;
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci		itxd = mtk_qdma_phys_to_virt(ring, itxd->txd2);
153162306a36Sopenharmony_ci		itxd_pdma = qdma_to_pdma(ring, itxd);
153262306a36Sopenharmony_ci	} while (itxd != txd);
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	return -ENOMEM;
153562306a36Sopenharmony_ci}
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_cistatic int mtk_cal_txd_req(struct mtk_eth *eth, struct sk_buff *skb)
153862306a36Sopenharmony_ci{
153962306a36Sopenharmony_ci	int i, nfrags = 1;
154062306a36Sopenharmony_ci	skb_frag_t *frag;
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci	if (skb_is_gso(skb)) {
154362306a36Sopenharmony_ci		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
154462306a36Sopenharmony_ci			frag = &skb_shinfo(skb)->frags[i];
154562306a36Sopenharmony_ci			nfrags += DIV_ROUND_UP(skb_frag_size(frag),
154662306a36Sopenharmony_ci					       eth->soc->txrx.dma_max_len);
154762306a36Sopenharmony_ci		}
154862306a36Sopenharmony_ci	} else {
154962306a36Sopenharmony_ci		nfrags += skb_shinfo(skb)->nr_frags;
155062306a36Sopenharmony_ci	}
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	return nfrags;
155362306a36Sopenharmony_ci}
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_cistatic int mtk_queue_stopped(struct mtk_eth *eth)
155662306a36Sopenharmony_ci{
155762306a36Sopenharmony_ci	int i;
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	for (i = 0; i < MTK_MAX_DEVS; i++) {
156062306a36Sopenharmony_ci		if (!eth->netdev[i])
156162306a36Sopenharmony_ci			continue;
156262306a36Sopenharmony_ci		if (netif_queue_stopped(eth->netdev[i]))
156362306a36Sopenharmony_ci			return 1;
156462306a36Sopenharmony_ci	}
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	return 0;
156762306a36Sopenharmony_ci}
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_cistatic void mtk_wake_queue(struct mtk_eth *eth)
157062306a36Sopenharmony_ci{
157162306a36Sopenharmony_ci	int i;
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	for (i = 0; i < MTK_MAX_DEVS; i++) {
157462306a36Sopenharmony_ci		if (!eth->netdev[i])
157562306a36Sopenharmony_ci			continue;
157662306a36Sopenharmony_ci		netif_tx_wake_all_queues(eth->netdev[i]);
157762306a36Sopenharmony_ci	}
157862306a36Sopenharmony_ci}
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_cistatic netdev_tx_t mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
158162306a36Sopenharmony_ci{
158262306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
158362306a36Sopenharmony_ci	struct mtk_eth *eth = mac->hw;
158462306a36Sopenharmony_ci	struct mtk_tx_ring *ring = &eth->tx_ring;
158562306a36Sopenharmony_ci	struct net_device_stats *stats = &dev->stats;
158662306a36Sopenharmony_ci	bool gso = false;
158762306a36Sopenharmony_ci	int tx_num;
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	/* normally we can rely on the stack not calling this more than once,
159062306a36Sopenharmony_ci	 * however we have 2 queues running on the same ring so we need to lock
159162306a36Sopenharmony_ci	 * the ring access
159262306a36Sopenharmony_ci	 */
159362306a36Sopenharmony_ci	spin_lock(&eth->page_lock);
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	if (unlikely(test_bit(MTK_RESETTING, &eth->state)))
159662306a36Sopenharmony_ci		goto drop;
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci	tx_num = mtk_cal_txd_req(eth, skb);
159962306a36Sopenharmony_ci	if (unlikely(atomic_read(&ring->free_count) <= tx_num)) {
160062306a36Sopenharmony_ci		netif_tx_stop_all_queues(dev);
160162306a36Sopenharmony_ci		netif_err(eth, tx_queued, dev,
160262306a36Sopenharmony_ci			  "Tx Ring full when queue awake!\n");
160362306a36Sopenharmony_ci		spin_unlock(&eth->page_lock);
160462306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
160562306a36Sopenharmony_ci	}
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	/* TSO: fill MSS info in tcp checksum field */
160862306a36Sopenharmony_ci	if (skb_is_gso(skb)) {
160962306a36Sopenharmony_ci		if (skb_cow_head(skb, 0)) {
161062306a36Sopenharmony_ci			netif_warn(eth, tx_err, dev,
161162306a36Sopenharmony_ci				   "GSO expand head fail.\n");
161262306a36Sopenharmony_ci			goto drop;
161362306a36Sopenharmony_ci		}
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_ci		if (skb_shinfo(skb)->gso_type &
161662306a36Sopenharmony_ci				(SKB_GSO_TCPV4 | SKB_GSO_TCPV6)) {
161762306a36Sopenharmony_ci			gso = true;
161862306a36Sopenharmony_ci			tcp_hdr(skb)->check = htons(skb_shinfo(skb)->gso_size);
161962306a36Sopenharmony_ci		}
162062306a36Sopenharmony_ci	}
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	if (mtk_tx_map(skb, dev, tx_num, ring, gso) < 0)
162362306a36Sopenharmony_ci		goto drop;
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	if (unlikely(atomic_read(&ring->free_count) <= ring->thresh))
162662306a36Sopenharmony_ci		netif_tx_stop_all_queues(dev);
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	spin_unlock(&eth->page_lock);
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	return NETDEV_TX_OK;
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_cidrop:
163362306a36Sopenharmony_ci	spin_unlock(&eth->page_lock);
163462306a36Sopenharmony_ci	stats->tx_dropped++;
163562306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
163662306a36Sopenharmony_ci	return NETDEV_TX_OK;
163762306a36Sopenharmony_ci}
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_cistatic struct mtk_rx_ring *mtk_get_rx_ring(struct mtk_eth *eth)
164062306a36Sopenharmony_ci{
164162306a36Sopenharmony_ci	int i;
164262306a36Sopenharmony_ci	struct mtk_rx_ring *ring;
164362306a36Sopenharmony_ci	int idx;
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci	if (!eth->hwlro)
164662306a36Sopenharmony_ci		return &eth->rx_ring[0];
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci	for (i = 0; i < MTK_MAX_RX_RING_NUM; i++) {
164962306a36Sopenharmony_ci		struct mtk_rx_dma *rxd;
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci		ring = &eth->rx_ring[i];
165262306a36Sopenharmony_ci		idx = NEXT_DESP_IDX(ring->calc_idx, ring->dma_size);
165362306a36Sopenharmony_ci		rxd = ring->dma + idx * eth->soc->txrx.rxd_size;
165462306a36Sopenharmony_ci		if (rxd->rxd2 & RX_DMA_DONE) {
165562306a36Sopenharmony_ci			ring->calc_idx_update = true;
165662306a36Sopenharmony_ci			return ring;
165762306a36Sopenharmony_ci		}
165862306a36Sopenharmony_ci	}
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci	return NULL;
166162306a36Sopenharmony_ci}
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_cistatic void mtk_update_rx_cpu_idx(struct mtk_eth *eth)
166462306a36Sopenharmony_ci{
166562306a36Sopenharmony_ci	struct mtk_rx_ring *ring;
166662306a36Sopenharmony_ci	int i;
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	if (!eth->hwlro) {
166962306a36Sopenharmony_ci		ring = &eth->rx_ring[0];
167062306a36Sopenharmony_ci		mtk_w32(eth, ring->calc_idx, ring->crx_idx_reg);
167162306a36Sopenharmony_ci	} else {
167262306a36Sopenharmony_ci		for (i = 0; i < MTK_MAX_RX_RING_NUM; i++) {
167362306a36Sopenharmony_ci			ring = &eth->rx_ring[i];
167462306a36Sopenharmony_ci			if (ring->calc_idx_update) {
167562306a36Sopenharmony_ci				ring->calc_idx_update = false;
167662306a36Sopenharmony_ci				mtk_w32(eth, ring->calc_idx, ring->crx_idx_reg);
167762306a36Sopenharmony_ci			}
167862306a36Sopenharmony_ci		}
167962306a36Sopenharmony_ci	}
168062306a36Sopenharmony_ci}
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_cistatic bool mtk_page_pool_enabled(struct mtk_eth *eth)
168362306a36Sopenharmony_ci{
168462306a36Sopenharmony_ci	return mtk_is_netsys_v2_or_greater(eth);
168562306a36Sopenharmony_ci}
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_cistatic struct page_pool *mtk_create_page_pool(struct mtk_eth *eth,
168862306a36Sopenharmony_ci					      struct xdp_rxq_info *xdp_q,
168962306a36Sopenharmony_ci					      int id, int size)
169062306a36Sopenharmony_ci{
169162306a36Sopenharmony_ci	struct page_pool_params pp_params = {
169262306a36Sopenharmony_ci		.order = 0,
169362306a36Sopenharmony_ci		.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
169462306a36Sopenharmony_ci		.pool_size = size,
169562306a36Sopenharmony_ci		.nid = NUMA_NO_NODE,
169662306a36Sopenharmony_ci		.dev = eth->dma_dev,
169762306a36Sopenharmony_ci		.offset = MTK_PP_HEADROOM,
169862306a36Sopenharmony_ci		.max_len = MTK_PP_MAX_BUF_SIZE,
169962306a36Sopenharmony_ci	};
170062306a36Sopenharmony_ci	struct page_pool *pp;
170162306a36Sopenharmony_ci	int err;
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci	pp_params.dma_dir = rcu_access_pointer(eth->prog) ? DMA_BIDIRECTIONAL
170462306a36Sopenharmony_ci							  : DMA_FROM_DEVICE;
170562306a36Sopenharmony_ci	pp = page_pool_create(&pp_params);
170662306a36Sopenharmony_ci	if (IS_ERR(pp))
170762306a36Sopenharmony_ci		return pp;
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci	err = __xdp_rxq_info_reg(xdp_q, &eth->dummy_dev, id,
171062306a36Sopenharmony_ci				 eth->rx_napi.napi_id, PAGE_SIZE);
171162306a36Sopenharmony_ci	if (err < 0)
171262306a36Sopenharmony_ci		goto err_free_pp;
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci	err = xdp_rxq_info_reg_mem_model(xdp_q, MEM_TYPE_PAGE_POOL, pp);
171562306a36Sopenharmony_ci	if (err)
171662306a36Sopenharmony_ci		goto err_unregister_rxq;
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	return pp;
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_cierr_unregister_rxq:
172162306a36Sopenharmony_ci	xdp_rxq_info_unreg(xdp_q);
172262306a36Sopenharmony_cierr_free_pp:
172362306a36Sopenharmony_ci	page_pool_destroy(pp);
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci	return ERR_PTR(err);
172662306a36Sopenharmony_ci}
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_cistatic void *mtk_page_pool_get_buff(struct page_pool *pp, dma_addr_t *dma_addr,
172962306a36Sopenharmony_ci				    gfp_t gfp_mask)
173062306a36Sopenharmony_ci{
173162306a36Sopenharmony_ci	struct page *page;
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci	page = page_pool_alloc_pages(pp, gfp_mask | __GFP_NOWARN);
173462306a36Sopenharmony_ci	if (!page)
173562306a36Sopenharmony_ci		return NULL;
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	*dma_addr = page_pool_get_dma_addr(page) + MTK_PP_HEADROOM;
173862306a36Sopenharmony_ci	return page_address(page);
173962306a36Sopenharmony_ci}
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_cistatic void mtk_rx_put_buff(struct mtk_rx_ring *ring, void *data, bool napi)
174262306a36Sopenharmony_ci{
174362306a36Sopenharmony_ci	if (ring->page_pool)
174462306a36Sopenharmony_ci		page_pool_put_full_page(ring->page_pool,
174562306a36Sopenharmony_ci					virt_to_head_page(data), napi);
174662306a36Sopenharmony_ci	else
174762306a36Sopenharmony_ci		skb_free_frag(data);
174862306a36Sopenharmony_ci}
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_cistatic int mtk_xdp_frame_map(struct mtk_eth *eth, struct net_device *dev,
175162306a36Sopenharmony_ci			     struct mtk_tx_dma_desc_info *txd_info,
175262306a36Sopenharmony_ci			     struct mtk_tx_dma *txd, struct mtk_tx_buf *tx_buf,
175362306a36Sopenharmony_ci			     void *data, u16 headroom, int index, bool dma_map)
175462306a36Sopenharmony_ci{
175562306a36Sopenharmony_ci	struct mtk_tx_ring *ring = &eth->tx_ring;
175662306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
175762306a36Sopenharmony_ci	struct mtk_tx_dma *txd_pdma;
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_ci	if (dma_map) {  /* ndo_xdp_xmit */
176062306a36Sopenharmony_ci		txd_info->addr = dma_map_single(eth->dma_dev, data,
176162306a36Sopenharmony_ci						txd_info->size, DMA_TO_DEVICE);
176262306a36Sopenharmony_ci		if (unlikely(dma_mapping_error(eth->dma_dev, txd_info->addr)))
176362306a36Sopenharmony_ci			return -ENOMEM;
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci		tx_buf->flags |= MTK_TX_FLAGS_SINGLE0;
176662306a36Sopenharmony_ci	} else {
176762306a36Sopenharmony_ci		struct page *page = virt_to_head_page(data);
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_ci		txd_info->addr = page_pool_get_dma_addr(page) +
177062306a36Sopenharmony_ci				 sizeof(struct xdp_frame) + headroom;
177162306a36Sopenharmony_ci		dma_sync_single_for_device(eth->dma_dev, txd_info->addr,
177262306a36Sopenharmony_ci					   txd_info->size, DMA_BIDIRECTIONAL);
177362306a36Sopenharmony_ci	}
177462306a36Sopenharmony_ci	mtk_tx_set_dma_desc(dev, txd, txd_info);
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci	tx_buf->mac_id = mac->id;
177762306a36Sopenharmony_ci	tx_buf->type = dma_map ? MTK_TYPE_XDP_NDO : MTK_TYPE_XDP_TX;
177862306a36Sopenharmony_ci	tx_buf->data = (void *)MTK_DMA_DUMMY_DESC;
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci	txd_pdma = qdma_to_pdma(ring, txd);
178162306a36Sopenharmony_ci	setup_tx_buf(eth, tx_buf, txd_pdma, txd_info->addr, txd_info->size,
178262306a36Sopenharmony_ci		     index);
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	return 0;
178562306a36Sopenharmony_ci}
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_cistatic int mtk_xdp_submit_frame(struct mtk_eth *eth, struct xdp_frame *xdpf,
178862306a36Sopenharmony_ci				struct net_device *dev, bool dma_map)
178962306a36Sopenharmony_ci{
179062306a36Sopenharmony_ci	struct skb_shared_info *sinfo = xdp_get_shared_info_from_frame(xdpf);
179162306a36Sopenharmony_ci	const struct mtk_soc_data *soc = eth->soc;
179262306a36Sopenharmony_ci	struct mtk_tx_ring *ring = &eth->tx_ring;
179362306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
179462306a36Sopenharmony_ci	struct mtk_tx_dma_desc_info txd_info = {
179562306a36Sopenharmony_ci		.size	= xdpf->len,
179662306a36Sopenharmony_ci		.first	= true,
179762306a36Sopenharmony_ci		.last	= !xdp_frame_has_frags(xdpf),
179862306a36Sopenharmony_ci		.qid	= mac->id,
179962306a36Sopenharmony_ci	};
180062306a36Sopenharmony_ci	int err, index = 0, n_desc = 1, nr_frags;
180162306a36Sopenharmony_ci	struct mtk_tx_buf *htx_buf, *tx_buf;
180262306a36Sopenharmony_ci	struct mtk_tx_dma *htxd, *txd;
180362306a36Sopenharmony_ci	void *data = xdpf->data;
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci	if (unlikely(test_bit(MTK_RESETTING, &eth->state)))
180662306a36Sopenharmony_ci		return -EBUSY;
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	nr_frags = unlikely(xdp_frame_has_frags(xdpf)) ? sinfo->nr_frags : 0;
180962306a36Sopenharmony_ci	if (unlikely(atomic_read(&ring->free_count) <= 1 + nr_frags))
181062306a36Sopenharmony_ci		return -EBUSY;
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_ci	spin_lock(&eth->page_lock);
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci	txd = ring->next_free;
181562306a36Sopenharmony_ci	if (txd == ring->last_free) {
181662306a36Sopenharmony_ci		spin_unlock(&eth->page_lock);
181762306a36Sopenharmony_ci		return -ENOMEM;
181862306a36Sopenharmony_ci	}
181962306a36Sopenharmony_ci	htxd = txd;
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci	tx_buf = mtk_desc_to_tx_buf(ring, txd, soc->txrx.txd_size);
182262306a36Sopenharmony_ci	memset(tx_buf, 0, sizeof(*tx_buf));
182362306a36Sopenharmony_ci	htx_buf = tx_buf;
182462306a36Sopenharmony_ci
182562306a36Sopenharmony_ci	for (;;) {
182662306a36Sopenharmony_ci		err = mtk_xdp_frame_map(eth, dev, &txd_info, txd, tx_buf,
182762306a36Sopenharmony_ci					data, xdpf->headroom, index, dma_map);
182862306a36Sopenharmony_ci		if (err < 0)
182962306a36Sopenharmony_ci			goto unmap;
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci		if (txd_info.last)
183262306a36Sopenharmony_ci			break;
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci		if (MTK_HAS_CAPS(soc->caps, MTK_QDMA) || (index & 0x1)) {
183562306a36Sopenharmony_ci			txd = mtk_qdma_phys_to_virt(ring, txd->txd2);
183662306a36Sopenharmony_ci			if (txd == ring->last_free)
183762306a36Sopenharmony_ci				goto unmap;
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ci			tx_buf = mtk_desc_to_tx_buf(ring, txd,
184062306a36Sopenharmony_ci						    soc->txrx.txd_size);
184162306a36Sopenharmony_ci			memset(tx_buf, 0, sizeof(*tx_buf));
184262306a36Sopenharmony_ci			n_desc++;
184362306a36Sopenharmony_ci		}
184462306a36Sopenharmony_ci
184562306a36Sopenharmony_ci		memset(&txd_info, 0, sizeof(struct mtk_tx_dma_desc_info));
184662306a36Sopenharmony_ci		txd_info.size = skb_frag_size(&sinfo->frags[index]);
184762306a36Sopenharmony_ci		txd_info.last = index + 1 == nr_frags;
184862306a36Sopenharmony_ci		txd_info.qid = mac->id;
184962306a36Sopenharmony_ci		data = skb_frag_address(&sinfo->frags[index]);
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci		index++;
185262306a36Sopenharmony_ci	}
185362306a36Sopenharmony_ci	/* store xdpf for cleanup */
185462306a36Sopenharmony_ci	htx_buf->data = xdpf;
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci	if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) {
185762306a36Sopenharmony_ci		struct mtk_tx_dma *txd_pdma = qdma_to_pdma(ring, txd);
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_ci		if (index & 1)
186062306a36Sopenharmony_ci			txd_pdma->txd2 |= TX_DMA_LS0;
186162306a36Sopenharmony_ci		else
186262306a36Sopenharmony_ci			txd_pdma->txd2 |= TX_DMA_LS1;
186362306a36Sopenharmony_ci	}
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci	ring->next_free = mtk_qdma_phys_to_virt(ring, txd->txd2);
186662306a36Sopenharmony_ci	atomic_sub(n_desc, &ring->free_count);
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ci	/* make sure that all changes to the dma ring are flushed before we
186962306a36Sopenharmony_ci	 * continue
187062306a36Sopenharmony_ci	 */
187162306a36Sopenharmony_ci	wmb();
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci	if (MTK_HAS_CAPS(soc->caps, MTK_QDMA)) {
187462306a36Sopenharmony_ci		mtk_w32(eth, txd->txd2, soc->reg_map->qdma.ctx_ptr);
187562306a36Sopenharmony_ci	} else {
187662306a36Sopenharmony_ci		int idx;
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_ci		idx = txd_to_idx(ring, txd, soc->txrx.txd_size);
187962306a36Sopenharmony_ci		mtk_w32(eth, NEXT_DESP_IDX(idx, ring->dma_size),
188062306a36Sopenharmony_ci			MT7628_TX_CTX_IDX0);
188162306a36Sopenharmony_ci	}
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci	spin_unlock(&eth->page_lock);
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_ci	return 0;
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ciunmap:
188862306a36Sopenharmony_ci	while (htxd != txd) {
188962306a36Sopenharmony_ci		tx_buf = mtk_desc_to_tx_buf(ring, htxd, soc->txrx.txd_size);
189062306a36Sopenharmony_ci		mtk_tx_unmap(eth, tx_buf, NULL, false);
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci		htxd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU;
189362306a36Sopenharmony_ci		if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) {
189462306a36Sopenharmony_ci			struct mtk_tx_dma *txd_pdma = qdma_to_pdma(ring, htxd);
189562306a36Sopenharmony_ci
189662306a36Sopenharmony_ci			txd_pdma->txd2 = TX_DMA_DESP2_DEF;
189762306a36Sopenharmony_ci		}
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci		htxd = mtk_qdma_phys_to_virt(ring, htxd->txd2);
190062306a36Sopenharmony_ci	}
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ci	spin_unlock(&eth->page_lock);
190362306a36Sopenharmony_ci
190462306a36Sopenharmony_ci	return err;
190562306a36Sopenharmony_ci}
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_cistatic int mtk_xdp_xmit(struct net_device *dev, int num_frame,
190862306a36Sopenharmony_ci			struct xdp_frame **frames, u32 flags)
190962306a36Sopenharmony_ci{
191062306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
191162306a36Sopenharmony_ci	struct mtk_hw_stats *hw_stats = mac->hw_stats;
191262306a36Sopenharmony_ci	struct mtk_eth *eth = mac->hw;
191362306a36Sopenharmony_ci	int i, nxmit = 0;
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_ci	if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK))
191662306a36Sopenharmony_ci		return -EINVAL;
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci	for (i = 0; i < num_frame; i++) {
191962306a36Sopenharmony_ci		if (mtk_xdp_submit_frame(eth, frames[i], dev, true))
192062306a36Sopenharmony_ci			break;
192162306a36Sopenharmony_ci		nxmit++;
192262306a36Sopenharmony_ci	}
192362306a36Sopenharmony_ci
192462306a36Sopenharmony_ci	u64_stats_update_begin(&hw_stats->syncp);
192562306a36Sopenharmony_ci	hw_stats->xdp_stats.tx_xdp_xmit += nxmit;
192662306a36Sopenharmony_ci	hw_stats->xdp_stats.tx_xdp_xmit_errors += num_frame - nxmit;
192762306a36Sopenharmony_ci	u64_stats_update_end(&hw_stats->syncp);
192862306a36Sopenharmony_ci
192962306a36Sopenharmony_ci	return nxmit;
193062306a36Sopenharmony_ci}
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_cistatic u32 mtk_xdp_run(struct mtk_eth *eth, struct mtk_rx_ring *ring,
193362306a36Sopenharmony_ci		       struct xdp_buff *xdp, struct net_device *dev)
193462306a36Sopenharmony_ci{
193562306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
193662306a36Sopenharmony_ci	struct mtk_hw_stats *hw_stats = mac->hw_stats;
193762306a36Sopenharmony_ci	u64 *count = &hw_stats->xdp_stats.rx_xdp_drop;
193862306a36Sopenharmony_ci	struct bpf_prog *prog;
193962306a36Sopenharmony_ci	u32 act = XDP_PASS;
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci	rcu_read_lock();
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci	prog = rcu_dereference(eth->prog);
194462306a36Sopenharmony_ci	if (!prog)
194562306a36Sopenharmony_ci		goto out;
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci	act = bpf_prog_run_xdp(prog, xdp);
194862306a36Sopenharmony_ci	switch (act) {
194962306a36Sopenharmony_ci	case XDP_PASS:
195062306a36Sopenharmony_ci		count = &hw_stats->xdp_stats.rx_xdp_pass;
195162306a36Sopenharmony_ci		goto update_stats;
195262306a36Sopenharmony_ci	case XDP_REDIRECT:
195362306a36Sopenharmony_ci		if (unlikely(xdp_do_redirect(dev, xdp, prog))) {
195462306a36Sopenharmony_ci			act = XDP_DROP;
195562306a36Sopenharmony_ci			break;
195662306a36Sopenharmony_ci		}
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci		count = &hw_stats->xdp_stats.rx_xdp_redirect;
195962306a36Sopenharmony_ci		goto update_stats;
196062306a36Sopenharmony_ci	case XDP_TX: {
196162306a36Sopenharmony_ci		struct xdp_frame *xdpf = xdp_convert_buff_to_frame(xdp);
196262306a36Sopenharmony_ci
196362306a36Sopenharmony_ci		if (!xdpf || mtk_xdp_submit_frame(eth, xdpf, dev, false)) {
196462306a36Sopenharmony_ci			count = &hw_stats->xdp_stats.rx_xdp_tx_errors;
196562306a36Sopenharmony_ci			act = XDP_DROP;
196662306a36Sopenharmony_ci			break;
196762306a36Sopenharmony_ci		}
196862306a36Sopenharmony_ci
196962306a36Sopenharmony_ci		count = &hw_stats->xdp_stats.rx_xdp_tx;
197062306a36Sopenharmony_ci		goto update_stats;
197162306a36Sopenharmony_ci	}
197262306a36Sopenharmony_ci	default:
197362306a36Sopenharmony_ci		bpf_warn_invalid_xdp_action(dev, prog, act);
197462306a36Sopenharmony_ci		fallthrough;
197562306a36Sopenharmony_ci	case XDP_ABORTED:
197662306a36Sopenharmony_ci		trace_xdp_exception(dev, prog, act);
197762306a36Sopenharmony_ci		fallthrough;
197862306a36Sopenharmony_ci	case XDP_DROP:
197962306a36Sopenharmony_ci		break;
198062306a36Sopenharmony_ci	}
198162306a36Sopenharmony_ci
198262306a36Sopenharmony_ci	page_pool_put_full_page(ring->page_pool,
198362306a36Sopenharmony_ci				virt_to_head_page(xdp->data), true);
198462306a36Sopenharmony_ci
198562306a36Sopenharmony_ciupdate_stats:
198662306a36Sopenharmony_ci	u64_stats_update_begin(&hw_stats->syncp);
198762306a36Sopenharmony_ci	*count = *count + 1;
198862306a36Sopenharmony_ci	u64_stats_update_end(&hw_stats->syncp);
198962306a36Sopenharmony_ciout:
199062306a36Sopenharmony_ci	rcu_read_unlock();
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_ci	return act;
199362306a36Sopenharmony_ci}
199462306a36Sopenharmony_ci
199562306a36Sopenharmony_cistatic int mtk_poll_rx(struct napi_struct *napi, int budget,
199662306a36Sopenharmony_ci		       struct mtk_eth *eth)
199762306a36Sopenharmony_ci{
199862306a36Sopenharmony_ci	struct dim_sample dim_sample = {};
199962306a36Sopenharmony_ci	struct mtk_rx_ring *ring;
200062306a36Sopenharmony_ci	bool xdp_flush = false;
200162306a36Sopenharmony_ci	int idx;
200262306a36Sopenharmony_ci	struct sk_buff *skb;
200362306a36Sopenharmony_ci	u64 addr64 = 0;
200462306a36Sopenharmony_ci	u8 *data, *new_data;
200562306a36Sopenharmony_ci	struct mtk_rx_dma_v2 *rxd, trxd;
200662306a36Sopenharmony_ci	int done = 0, bytes = 0;
200762306a36Sopenharmony_ci	dma_addr_t dma_addr = DMA_MAPPING_ERROR;
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_ci	while (done < budget) {
201062306a36Sopenharmony_ci		unsigned int pktlen, *rxdcsum;
201162306a36Sopenharmony_ci		struct net_device *netdev;
201262306a36Sopenharmony_ci		u32 hash, reason;
201362306a36Sopenharmony_ci		int mac = 0;
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_ci		ring = mtk_get_rx_ring(eth);
201662306a36Sopenharmony_ci		if (unlikely(!ring))
201762306a36Sopenharmony_ci			goto rx_done;
201862306a36Sopenharmony_ci
201962306a36Sopenharmony_ci		idx = NEXT_DESP_IDX(ring->calc_idx, ring->dma_size);
202062306a36Sopenharmony_ci		rxd = ring->dma + idx * eth->soc->txrx.rxd_size;
202162306a36Sopenharmony_ci		data = ring->data[idx];
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci		if (!mtk_rx_get_desc(eth, &trxd, rxd))
202462306a36Sopenharmony_ci			break;
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_ci		/* find out which mac the packet come from. values start at 1 */
202762306a36Sopenharmony_ci		if (mtk_is_netsys_v2_or_greater(eth)) {
202862306a36Sopenharmony_ci			u32 val = RX_DMA_GET_SPORT_V2(trxd.rxd5);
202962306a36Sopenharmony_ci
203062306a36Sopenharmony_ci			switch (val) {
203162306a36Sopenharmony_ci			case PSE_GDM1_PORT:
203262306a36Sopenharmony_ci			case PSE_GDM2_PORT:
203362306a36Sopenharmony_ci				mac = val - 1;
203462306a36Sopenharmony_ci				break;
203562306a36Sopenharmony_ci			case PSE_GDM3_PORT:
203662306a36Sopenharmony_ci				mac = MTK_GMAC3_ID;
203762306a36Sopenharmony_ci				break;
203862306a36Sopenharmony_ci			default:
203962306a36Sopenharmony_ci				break;
204062306a36Sopenharmony_ci			}
204162306a36Sopenharmony_ci		} else if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) &&
204262306a36Sopenharmony_ci			   !(trxd.rxd4 & RX_DMA_SPECIAL_TAG)) {
204362306a36Sopenharmony_ci			mac = RX_DMA_GET_SPORT(trxd.rxd4) - 1;
204462306a36Sopenharmony_ci		}
204562306a36Sopenharmony_ci
204662306a36Sopenharmony_ci		if (unlikely(mac < 0 || mac >= MTK_MAX_DEVS ||
204762306a36Sopenharmony_ci			     !eth->netdev[mac]))
204862306a36Sopenharmony_ci			goto release_desc;
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_ci		netdev = eth->netdev[mac];
205162306a36Sopenharmony_ci
205262306a36Sopenharmony_ci		if (unlikely(test_bit(MTK_RESETTING, &eth->state)))
205362306a36Sopenharmony_ci			goto release_desc;
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci		pktlen = RX_DMA_GET_PLEN0(trxd.rxd2);
205662306a36Sopenharmony_ci
205762306a36Sopenharmony_ci		/* alloc new buffer */
205862306a36Sopenharmony_ci		if (ring->page_pool) {
205962306a36Sopenharmony_ci			struct page *page = virt_to_head_page(data);
206062306a36Sopenharmony_ci			struct xdp_buff xdp;
206162306a36Sopenharmony_ci			u32 ret;
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci			new_data = mtk_page_pool_get_buff(ring->page_pool,
206462306a36Sopenharmony_ci							  &dma_addr,
206562306a36Sopenharmony_ci							  GFP_ATOMIC);
206662306a36Sopenharmony_ci			if (unlikely(!new_data)) {
206762306a36Sopenharmony_ci				netdev->stats.rx_dropped++;
206862306a36Sopenharmony_ci				goto release_desc;
206962306a36Sopenharmony_ci			}
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_ci			dma_sync_single_for_cpu(eth->dma_dev,
207262306a36Sopenharmony_ci				page_pool_get_dma_addr(page) + MTK_PP_HEADROOM,
207362306a36Sopenharmony_ci				pktlen, page_pool_get_dma_dir(ring->page_pool));
207462306a36Sopenharmony_ci
207562306a36Sopenharmony_ci			xdp_init_buff(&xdp, PAGE_SIZE, &ring->xdp_q);
207662306a36Sopenharmony_ci			xdp_prepare_buff(&xdp, data, MTK_PP_HEADROOM, pktlen,
207762306a36Sopenharmony_ci					 false);
207862306a36Sopenharmony_ci			xdp_buff_clear_frags_flag(&xdp);
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci			ret = mtk_xdp_run(eth, ring, &xdp, netdev);
208162306a36Sopenharmony_ci			if (ret == XDP_REDIRECT)
208262306a36Sopenharmony_ci				xdp_flush = true;
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci			if (ret != XDP_PASS)
208562306a36Sopenharmony_ci				goto skip_rx;
208662306a36Sopenharmony_ci
208762306a36Sopenharmony_ci			skb = build_skb(data, PAGE_SIZE);
208862306a36Sopenharmony_ci			if (unlikely(!skb)) {
208962306a36Sopenharmony_ci				page_pool_put_full_page(ring->page_pool,
209062306a36Sopenharmony_ci							page, true);
209162306a36Sopenharmony_ci				netdev->stats.rx_dropped++;
209262306a36Sopenharmony_ci				goto skip_rx;
209362306a36Sopenharmony_ci			}
209462306a36Sopenharmony_ci
209562306a36Sopenharmony_ci			skb_reserve(skb, xdp.data - xdp.data_hard_start);
209662306a36Sopenharmony_ci			skb_put(skb, xdp.data_end - xdp.data);
209762306a36Sopenharmony_ci			skb_mark_for_recycle(skb);
209862306a36Sopenharmony_ci		} else {
209962306a36Sopenharmony_ci			if (ring->frag_size <= PAGE_SIZE)
210062306a36Sopenharmony_ci				new_data = napi_alloc_frag(ring->frag_size);
210162306a36Sopenharmony_ci			else
210262306a36Sopenharmony_ci				new_data = mtk_max_lro_buf_alloc(GFP_ATOMIC);
210362306a36Sopenharmony_ci
210462306a36Sopenharmony_ci			if (unlikely(!new_data)) {
210562306a36Sopenharmony_ci				netdev->stats.rx_dropped++;
210662306a36Sopenharmony_ci				goto release_desc;
210762306a36Sopenharmony_ci			}
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_ci			dma_addr = dma_map_single(eth->dma_dev,
211062306a36Sopenharmony_ci				new_data + NET_SKB_PAD + eth->ip_align,
211162306a36Sopenharmony_ci				ring->buf_size, DMA_FROM_DEVICE);
211262306a36Sopenharmony_ci			if (unlikely(dma_mapping_error(eth->dma_dev,
211362306a36Sopenharmony_ci						       dma_addr))) {
211462306a36Sopenharmony_ci				skb_free_frag(new_data);
211562306a36Sopenharmony_ci				netdev->stats.rx_dropped++;
211662306a36Sopenharmony_ci				goto release_desc;
211762306a36Sopenharmony_ci			}
211862306a36Sopenharmony_ci
211962306a36Sopenharmony_ci			if (MTK_HAS_CAPS(eth->soc->caps, MTK_36BIT_DMA))
212062306a36Sopenharmony_ci				addr64 = RX_DMA_GET_ADDR64(trxd.rxd2);
212162306a36Sopenharmony_ci
212262306a36Sopenharmony_ci			dma_unmap_single(eth->dma_dev, ((u64)trxd.rxd1 | addr64),
212362306a36Sopenharmony_ci					 ring->buf_size, DMA_FROM_DEVICE);
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ci			skb = build_skb(data, ring->frag_size);
212662306a36Sopenharmony_ci			if (unlikely(!skb)) {
212762306a36Sopenharmony_ci				netdev->stats.rx_dropped++;
212862306a36Sopenharmony_ci				skb_free_frag(data);
212962306a36Sopenharmony_ci				goto skip_rx;
213062306a36Sopenharmony_ci			}
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_ci			skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
213362306a36Sopenharmony_ci			skb_put(skb, pktlen);
213462306a36Sopenharmony_ci		}
213562306a36Sopenharmony_ci
213662306a36Sopenharmony_ci		skb->dev = netdev;
213762306a36Sopenharmony_ci		bytes += skb->len;
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci		if (mtk_is_netsys_v2_or_greater(eth)) {
214062306a36Sopenharmony_ci			reason = FIELD_GET(MTK_RXD5_PPE_CPU_REASON, trxd.rxd5);
214162306a36Sopenharmony_ci			hash = trxd.rxd5 & MTK_RXD5_FOE_ENTRY;
214262306a36Sopenharmony_ci			if (hash != MTK_RXD5_FOE_ENTRY)
214362306a36Sopenharmony_ci				skb_set_hash(skb, jhash_1word(hash, 0),
214462306a36Sopenharmony_ci					     PKT_HASH_TYPE_L4);
214562306a36Sopenharmony_ci			rxdcsum = &trxd.rxd3;
214662306a36Sopenharmony_ci		} else {
214762306a36Sopenharmony_ci			reason = FIELD_GET(MTK_RXD4_PPE_CPU_REASON, trxd.rxd4);
214862306a36Sopenharmony_ci			hash = trxd.rxd4 & MTK_RXD4_FOE_ENTRY;
214962306a36Sopenharmony_ci			if (hash != MTK_RXD4_FOE_ENTRY)
215062306a36Sopenharmony_ci				skb_set_hash(skb, jhash_1word(hash, 0),
215162306a36Sopenharmony_ci					     PKT_HASH_TYPE_L4);
215262306a36Sopenharmony_ci			rxdcsum = &trxd.rxd4;
215362306a36Sopenharmony_ci		}
215462306a36Sopenharmony_ci
215562306a36Sopenharmony_ci		if (*rxdcsum & eth->soc->txrx.rx_dma_l4_valid)
215662306a36Sopenharmony_ci			skb->ip_summed = CHECKSUM_UNNECESSARY;
215762306a36Sopenharmony_ci		else
215862306a36Sopenharmony_ci			skb_checksum_none_assert(skb);
215962306a36Sopenharmony_ci		skb->protocol = eth_type_trans(skb, netdev);
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_ci		/* When using VLAN untagging in combination with DSA, the
216262306a36Sopenharmony_ci		 * hardware treats the MTK special tag as a VLAN and untags it.
216362306a36Sopenharmony_ci		 */
216462306a36Sopenharmony_ci		if (mtk_is_netsys_v1(eth) && (trxd.rxd2 & RX_DMA_VTAG) &&
216562306a36Sopenharmony_ci		    netdev_uses_dsa(netdev)) {
216662306a36Sopenharmony_ci			unsigned int port = RX_DMA_VPID(trxd.rxd3) & GENMASK(2, 0);
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_ci			if (port < ARRAY_SIZE(eth->dsa_meta) &&
216962306a36Sopenharmony_ci			    eth->dsa_meta[port])
217062306a36Sopenharmony_ci				skb_dst_set_noref(skb, &eth->dsa_meta[port]->dst);
217162306a36Sopenharmony_ci		}
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_ci		if (reason == MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED)
217462306a36Sopenharmony_ci			mtk_ppe_check_skb(eth->ppe[0], skb, hash);
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci		skb_record_rx_queue(skb, 0);
217762306a36Sopenharmony_ci		napi_gro_receive(napi, skb);
217862306a36Sopenharmony_ci
217962306a36Sopenharmony_ciskip_rx:
218062306a36Sopenharmony_ci		ring->data[idx] = new_data;
218162306a36Sopenharmony_ci		rxd->rxd1 = (unsigned int)dma_addr;
218262306a36Sopenharmony_cirelease_desc:
218362306a36Sopenharmony_ci		if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
218462306a36Sopenharmony_ci			rxd->rxd2 = RX_DMA_LSO;
218562306a36Sopenharmony_ci		else
218662306a36Sopenharmony_ci			rxd->rxd2 = RX_DMA_PREP_PLEN0(ring->buf_size);
218762306a36Sopenharmony_ci
218862306a36Sopenharmony_ci		if (MTK_HAS_CAPS(eth->soc->caps, MTK_36BIT_DMA) &&
218962306a36Sopenharmony_ci		    likely(dma_addr != DMA_MAPPING_ERROR))
219062306a36Sopenharmony_ci			rxd->rxd2 |= RX_DMA_PREP_ADDR64(dma_addr);
219162306a36Sopenharmony_ci
219262306a36Sopenharmony_ci		ring->calc_idx = idx;
219362306a36Sopenharmony_ci		done++;
219462306a36Sopenharmony_ci	}
219562306a36Sopenharmony_ci
219662306a36Sopenharmony_cirx_done:
219762306a36Sopenharmony_ci	if (done) {
219862306a36Sopenharmony_ci		/* make sure that all changes to the dma ring are flushed before
219962306a36Sopenharmony_ci		 * we continue
220062306a36Sopenharmony_ci		 */
220162306a36Sopenharmony_ci		wmb();
220262306a36Sopenharmony_ci		mtk_update_rx_cpu_idx(eth);
220362306a36Sopenharmony_ci	}
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_ci	eth->rx_packets += done;
220662306a36Sopenharmony_ci	eth->rx_bytes += bytes;
220762306a36Sopenharmony_ci	dim_update_sample(eth->rx_events, eth->rx_packets, eth->rx_bytes,
220862306a36Sopenharmony_ci			  &dim_sample);
220962306a36Sopenharmony_ci	net_dim(&eth->rx_dim, dim_sample);
221062306a36Sopenharmony_ci
221162306a36Sopenharmony_ci	if (xdp_flush)
221262306a36Sopenharmony_ci		xdp_do_flush_map();
221362306a36Sopenharmony_ci
221462306a36Sopenharmony_ci	return done;
221562306a36Sopenharmony_ci}
221662306a36Sopenharmony_ci
221762306a36Sopenharmony_cistruct mtk_poll_state {
221862306a36Sopenharmony_ci    struct netdev_queue *txq;
221962306a36Sopenharmony_ci    unsigned int total;
222062306a36Sopenharmony_ci    unsigned int done;
222162306a36Sopenharmony_ci    unsigned int bytes;
222262306a36Sopenharmony_ci};
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_cistatic void
222562306a36Sopenharmony_cimtk_poll_tx_done(struct mtk_eth *eth, struct mtk_poll_state *state, u8 mac,
222662306a36Sopenharmony_ci		 struct sk_buff *skb)
222762306a36Sopenharmony_ci{
222862306a36Sopenharmony_ci	struct netdev_queue *txq;
222962306a36Sopenharmony_ci	struct net_device *dev;
223062306a36Sopenharmony_ci	unsigned int bytes = skb->len;
223162306a36Sopenharmony_ci
223262306a36Sopenharmony_ci	state->total++;
223362306a36Sopenharmony_ci	eth->tx_packets++;
223462306a36Sopenharmony_ci	eth->tx_bytes += bytes;
223562306a36Sopenharmony_ci
223662306a36Sopenharmony_ci	dev = eth->netdev[mac];
223762306a36Sopenharmony_ci	if (!dev)
223862306a36Sopenharmony_ci		return;
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_ci	txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb));
224162306a36Sopenharmony_ci	if (state->txq == txq) {
224262306a36Sopenharmony_ci		state->done++;
224362306a36Sopenharmony_ci		state->bytes += bytes;
224462306a36Sopenharmony_ci		return;
224562306a36Sopenharmony_ci	}
224662306a36Sopenharmony_ci
224762306a36Sopenharmony_ci	if (state->txq)
224862306a36Sopenharmony_ci		netdev_tx_completed_queue(state->txq, state->done, state->bytes);
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_ci	state->txq = txq;
225162306a36Sopenharmony_ci	state->done = 1;
225262306a36Sopenharmony_ci	state->bytes = bytes;
225362306a36Sopenharmony_ci}
225462306a36Sopenharmony_ci
225562306a36Sopenharmony_cistatic int mtk_poll_tx_qdma(struct mtk_eth *eth, int budget,
225662306a36Sopenharmony_ci			    struct mtk_poll_state *state)
225762306a36Sopenharmony_ci{
225862306a36Sopenharmony_ci	const struct mtk_reg_map *reg_map = eth->soc->reg_map;
225962306a36Sopenharmony_ci	struct mtk_tx_ring *ring = &eth->tx_ring;
226062306a36Sopenharmony_ci	struct mtk_tx_buf *tx_buf;
226162306a36Sopenharmony_ci	struct xdp_frame_bulk bq;
226262306a36Sopenharmony_ci	struct mtk_tx_dma *desc;
226362306a36Sopenharmony_ci	u32 cpu, dma;
226462306a36Sopenharmony_ci
226562306a36Sopenharmony_ci	cpu = ring->last_free_ptr;
226662306a36Sopenharmony_ci	dma = mtk_r32(eth, reg_map->qdma.drx_ptr);
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci	desc = mtk_qdma_phys_to_virt(ring, cpu);
226962306a36Sopenharmony_ci	xdp_frame_bulk_init(&bq);
227062306a36Sopenharmony_ci
227162306a36Sopenharmony_ci	while ((cpu != dma) && budget) {
227262306a36Sopenharmony_ci		u32 next_cpu = desc->txd2;
227362306a36Sopenharmony_ci
227462306a36Sopenharmony_ci		desc = mtk_qdma_phys_to_virt(ring, desc->txd2);
227562306a36Sopenharmony_ci		if ((desc->txd3 & TX_DMA_OWNER_CPU) == 0)
227662306a36Sopenharmony_ci			break;
227762306a36Sopenharmony_ci
227862306a36Sopenharmony_ci		tx_buf = mtk_desc_to_tx_buf(ring, desc,
227962306a36Sopenharmony_ci					    eth->soc->txrx.txd_size);
228062306a36Sopenharmony_ci		if (!tx_buf->data)
228162306a36Sopenharmony_ci			break;
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_ci		if (tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) {
228462306a36Sopenharmony_ci			if (tx_buf->type == MTK_TYPE_SKB)
228562306a36Sopenharmony_ci				mtk_poll_tx_done(eth, state, tx_buf->mac_id,
228662306a36Sopenharmony_ci						 tx_buf->data);
228762306a36Sopenharmony_ci
228862306a36Sopenharmony_ci			budget--;
228962306a36Sopenharmony_ci		}
229062306a36Sopenharmony_ci		mtk_tx_unmap(eth, tx_buf, &bq, true);
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_ci		ring->last_free = desc;
229362306a36Sopenharmony_ci		atomic_inc(&ring->free_count);
229462306a36Sopenharmony_ci
229562306a36Sopenharmony_ci		cpu = next_cpu;
229662306a36Sopenharmony_ci	}
229762306a36Sopenharmony_ci	xdp_flush_frame_bulk(&bq);
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_ci	ring->last_free_ptr = cpu;
230062306a36Sopenharmony_ci	mtk_w32(eth, cpu, reg_map->qdma.crx_ptr);
230162306a36Sopenharmony_ci
230262306a36Sopenharmony_ci	return budget;
230362306a36Sopenharmony_ci}
230462306a36Sopenharmony_ci
230562306a36Sopenharmony_cistatic int mtk_poll_tx_pdma(struct mtk_eth *eth, int budget,
230662306a36Sopenharmony_ci			    struct mtk_poll_state *state)
230762306a36Sopenharmony_ci{
230862306a36Sopenharmony_ci	struct mtk_tx_ring *ring = &eth->tx_ring;
230962306a36Sopenharmony_ci	struct mtk_tx_buf *tx_buf;
231062306a36Sopenharmony_ci	struct xdp_frame_bulk bq;
231162306a36Sopenharmony_ci	struct mtk_tx_dma *desc;
231262306a36Sopenharmony_ci	u32 cpu, dma;
231362306a36Sopenharmony_ci
231462306a36Sopenharmony_ci	cpu = ring->cpu_idx;
231562306a36Sopenharmony_ci	dma = mtk_r32(eth, MT7628_TX_DTX_IDX0);
231662306a36Sopenharmony_ci	xdp_frame_bulk_init(&bq);
231762306a36Sopenharmony_ci
231862306a36Sopenharmony_ci	while ((cpu != dma) && budget) {
231962306a36Sopenharmony_ci		tx_buf = &ring->buf[cpu];
232062306a36Sopenharmony_ci		if (!tx_buf->data)
232162306a36Sopenharmony_ci			break;
232262306a36Sopenharmony_ci
232362306a36Sopenharmony_ci		if (tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) {
232462306a36Sopenharmony_ci			if (tx_buf->type == MTK_TYPE_SKB)
232562306a36Sopenharmony_ci				mtk_poll_tx_done(eth, state, 0, tx_buf->data);
232662306a36Sopenharmony_ci			budget--;
232762306a36Sopenharmony_ci		}
232862306a36Sopenharmony_ci		mtk_tx_unmap(eth, tx_buf, &bq, true);
232962306a36Sopenharmony_ci
233062306a36Sopenharmony_ci		desc = ring->dma + cpu * eth->soc->txrx.txd_size;
233162306a36Sopenharmony_ci		ring->last_free = desc;
233262306a36Sopenharmony_ci		atomic_inc(&ring->free_count);
233362306a36Sopenharmony_ci
233462306a36Sopenharmony_ci		cpu = NEXT_DESP_IDX(cpu, ring->dma_size);
233562306a36Sopenharmony_ci	}
233662306a36Sopenharmony_ci	xdp_flush_frame_bulk(&bq);
233762306a36Sopenharmony_ci
233862306a36Sopenharmony_ci	ring->cpu_idx = cpu;
233962306a36Sopenharmony_ci
234062306a36Sopenharmony_ci	return budget;
234162306a36Sopenharmony_ci}
234262306a36Sopenharmony_ci
234362306a36Sopenharmony_cistatic int mtk_poll_tx(struct mtk_eth *eth, int budget)
234462306a36Sopenharmony_ci{
234562306a36Sopenharmony_ci	struct mtk_tx_ring *ring = &eth->tx_ring;
234662306a36Sopenharmony_ci	struct dim_sample dim_sample = {};
234762306a36Sopenharmony_ci	struct mtk_poll_state state = {};
234862306a36Sopenharmony_ci
234962306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
235062306a36Sopenharmony_ci		budget = mtk_poll_tx_qdma(eth, budget, &state);
235162306a36Sopenharmony_ci	else
235262306a36Sopenharmony_ci		budget = mtk_poll_tx_pdma(eth, budget, &state);
235362306a36Sopenharmony_ci
235462306a36Sopenharmony_ci	if (state.txq)
235562306a36Sopenharmony_ci		netdev_tx_completed_queue(state.txq, state.done, state.bytes);
235662306a36Sopenharmony_ci
235762306a36Sopenharmony_ci	dim_update_sample(eth->tx_events, eth->tx_packets, eth->tx_bytes,
235862306a36Sopenharmony_ci			  &dim_sample);
235962306a36Sopenharmony_ci	net_dim(&eth->tx_dim, dim_sample);
236062306a36Sopenharmony_ci
236162306a36Sopenharmony_ci	if (mtk_queue_stopped(eth) &&
236262306a36Sopenharmony_ci	    (atomic_read(&ring->free_count) > ring->thresh))
236362306a36Sopenharmony_ci		mtk_wake_queue(eth);
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci	return state.total;
236662306a36Sopenharmony_ci}
236762306a36Sopenharmony_ci
236862306a36Sopenharmony_cistatic void mtk_handle_status_irq(struct mtk_eth *eth)
236962306a36Sopenharmony_ci{
237062306a36Sopenharmony_ci	u32 status2 = mtk_r32(eth, MTK_INT_STATUS2);
237162306a36Sopenharmony_ci
237262306a36Sopenharmony_ci	if (unlikely(status2 & (MTK_GDM1_AF | MTK_GDM2_AF))) {
237362306a36Sopenharmony_ci		mtk_stats_update(eth);
237462306a36Sopenharmony_ci		mtk_w32(eth, (MTK_GDM1_AF | MTK_GDM2_AF),
237562306a36Sopenharmony_ci			MTK_INT_STATUS2);
237662306a36Sopenharmony_ci	}
237762306a36Sopenharmony_ci}
237862306a36Sopenharmony_ci
237962306a36Sopenharmony_cistatic int mtk_napi_tx(struct napi_struct *napi, int budget)
238062306a36Sopenharmony_ci{
238162306a36Sopenharmony_ci	struct mtk_eth *eth = container_of(napi, struct mtk_eth, tx_napi);
238262306a36Sopenharmony_ci	const struct mtk_reg_map *reg_map = eth->soc->reg_map;
238362306a36Sopenharmony_ci	int tx_done = 0;
238462306a36Sopenharmony_ci
238562306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
238662306a36Sopenharmony_ci		mtk_handle_status_irq(eth);
238762306a36Sopenharmony_ci	mtk_w32(eth, MTK_TX_DONE_INT, reg_map->tx_irq_status);
238862306a36Sopenharmony_ci	tx_done = mtk_poll_tx(eth, budget);
238962306a36Sopenharmony_ci
239062306a36Sopenharmony_ci	if (unlikely(netif_msg_intr(eth))) {
239162306a36Sopenharmony_ci		dev_info(eth->dev,
239262306a36Sopenharmony_ci			 "done tx %d, intr 0x%08x/0x%x\n", tx_done,
239362306a36Sopenharmony_ci			 mtk_r32(eth, reg_map->tx_irq_status),
239462306a36Sopenharmony_ci			 mtk_r32(eth, reg_map->tx_irq_mask));
239562306a36Sopenharmony_ci	}
239662306a36Sopenharmony_ci
239762306a36Sopenharmony_ci	if (tx_done == budget)
239862306a36Sopenharmony_ci		return budget;
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_ci	if (mtk_r32(eth, reg_map->tx_irq_status) & MTK_TX_DONE_INT)
240162306a36Sopenharmony_ci		return budget;
240262306a36Sopenharmony_ci
240362306a36Sopenharmony_ci	if (napi_complete_done(napi, tx_done))
240462306a36Sopenharmony_ci		mtk_tx_irq_enable(eth, MTK_TX_DONE_INT);
240562306a36Sopenharmony_ci
240662306a36Sopenharmony_ci	return tx_done;
240762306a36Sopenharmony_ci}
240862306a36Sopenharmony_ci
240962306a36Sopenharmony_cistatic int mtk_napi_rx(struct napi_struct *napi, int budget)
241062306a36Sopenharmony_ci{
241162306a36Sopenharmony_ci	struct mtk_eth *eth = container_of(napi, struct mtk_eth, rx_napi);
241262306a36Sopenharmony_ci	const struct mtk_reg_map *reg_map = eth->soc->reg_map;
241362306a36Sopenharmony_ci	int rx_done_total = 0;
241462306a36Sopenharmony_ci
241562306a36Sopenharmony_ci	mtk_handle_status_irq(eth);
241662306a36Sopenharmony_ci
241762306a36Sopenharmony_ci	do {
241862306a36Sopenharmony_ci		int rx_done;
241962306a36Sopenharmony_ci
242062306a36Sopenharmony_ci		mtk_w32(eth, eth->soc->txrx.rx_irq_done_mask,
242162306a36Sopenharmony_ci			reg_map->pdma.irq_status);
242262306a36Sopenharmony_ci		rx_done = mtk_poll_rx(napi, budget - rx_done_total, eth);
242362306a36Sopenharmony_ci		rx_done_total += rx_done;
242462306a36Sopenharmony_ci
242562306a36Sopenharmony_ci		if (unlikely(netif_msg_intr(eth))) {
242662306a36Sopenharmony_ci			dev_info(eth->dev,
242762306a36Sopenharmony_ci				 "done rx %d, intr 0x%08x/0x%x\n", rx_done,
242862306a36Sopenharmony_ci				 mtk_r32(eth, reg_map->pdma.irq_status),
242962306a36Sopenharmony_ci				 mtk_r32(eth, reg_map->pdma.irq_mask));
243062306a36Sopenharmony_ci		}
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_ci		if (rx_done_total == budget)
243362306a36Sopenharmony_ci			return budget;
243462306a36Sopenharmony_ci
243562306a36Sopenharmony_ci	} while (mtk_r32(eth, reg_map->pdma.irq_status) &
243662306a36Sopenharmony_ci		 eth->soc->txrx.rx_irq_done_mask);
243762306a36Sopenharmony_ci
243862306a36Sopenharmony_ci	if (napi_complete_done(napi, rx_done_total))
243962306a36Sopenharmony_ci		mtk_rx_irq_enable(eth, eth->soc->txrx.rx_irq_done_mask);
244062306a36Sopenharmony_ci
244162306a36Sopenharmony_ci	return rx_done_total;
244262306a36Sopenharmony_ci}
244362306a36Sopenharmony_ci
244462306a36Sopenharmony_cistatic int mtk_tx_alloc(struct mtk_eth *eth)
244562306a36Sopenharmony_ci{
244662306a36Sopenharmony_ci	const struct mtk_soc_data *soc = eth->soc;
244762306a36Sopenharmony_ci	struct mtk_tx_ring *ring = &eth->tx_ring;
244862306a36Sopenharmony_ci	int i, sz = soc->txrx.txd_size;
244962306a36Sopenharmony_ci	struct mtk_tx_dma_v2 *txd;
245062306a36Sopenharmony_ci	int ring_size;
245162306a36Sopenharmony_ci	u32 ofs, val;
245262306a36Sopenharmony_ci
245362306a36Sopenharmony_ci	if (MTK_HAS_CAPS(soc->caps, MTK_QDMA))
245462306a36Sopenharmony_ci		ring_size = MTK_QDMA_RING_SIZE;
245562306a36Sopenharmony_ci	else
245662306a36Sopenharmony_ci		ring_size = MTK_DMA_SIZE;
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_ci	ring->buf = kcalloc(ring_size, sizeof(*ring->buf),
245962306a36Sopenharmony_ci			       GFP_KERNEL);
246062306a36Sopenharmony_ci	if (!ring->buf)
246162306a36Sopenharmony_ci		goto no_tx_mem;
246262306a36Sopenharmony_ci
246362306a36Sopenharmony_ci	if (MTK_HAS_CAPS(soc->caps, MTK_SRAM)) {
246462306a36Sopenharmony_ci		ring->dma = eth->sram_base + ring_size * sz;
246562306a36Sopenharmony_ci		ring->phys = eth->phy_scratch_ring + ring_size * (dma_addr_t)sz;
246662306a36Sopenharmony_ci	} else {
246762306a36Sopenharmony_ci		ring->dma = dma_alloc_coherent(eth->dma_dev, ring_size * sz,
246862306a36Sopenharmony_ci					       &ring->phys, GFP_KERNEL);
246962306a36Sopenharmony_ci	}
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_ci	if (!ring->dma)
247262306a36Sopenharmony_ci		goto no_tx_mem;
247362306a36Sopenharmony_ci
247462306a36Sopenharmony_ci	for (i = 0; i < ring_size; i++) {
247562306a36Sopenharmony_ci		int next = (i + 1) % ring_size;
247662306a36Sopenharmony_ci		u32 next_ptr = ring->phys + next * sz;
247762306a36Sopenharmony_ci
247862306a36Sopenharmony_ci		txd = ring->dma + i * sz;
247962306a36Sopenharmony_ci		txd->txd2 = next_ptr;
248062306a36Sopenharmony_ci		txd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU;
248162306a36Sopenharmony_ci		txd->txd4 = 0;
248262306a36Sopenharmony_ci		if (mtk_is_netsys_v2_or_greater(eth)) {
248362306a36Sopenharmony_ci			txd->txd5 = 0;
248462306a36Sopenharmony_ci			txd->txd6 = 0;
248562306a36Sopenharmony_ci			txd->txd7 = 0;
248662306a36Sopenharmony_ci			txd->txd8 = 0;
248762306a36Sopenharmony_ci		}
248862306a36Sopenharmony_ci	}
248962306a36Sopenharmony_ci
249062306a36Sopenharmony_ci	/* On MT7688 (PDMA only) this driver uses the ring->dma structs
249162306a36Sopenharmony_ci	 * only as the framework. The real HW descriptors are the PDMA
249262306a36Sopenharmony_ci	 * descriptors in ring->dma_pdma.
249362306a36Sopenharmony_ci	 */
249462306a36Sopenharmony_ci	if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) {
249562306a36Sopenharmony_ci		ring->dma_pdma = dma_alloc_coherent(eth->dma_dev, ring_size * sz,
249662306a36Sopenharmony_ci						    &ring->phys_pdma, GFP_KERNEL);
249762306a36Sopenharmony_ci		if (!ring->dma_pdma)
249862306a36Sopenharmony_ci			goto no_tx_mem;
249962306a36Sopenharmony_ci
250062306a36Sopenharmony_ci		for (i = 0; i < ring_size; i++) {
250162306a36Sopenharmony_ci			ring->dma_pdma[i].txd2 = TX_DMA_DESP2_DEF;
250262306a36Sopenharmony_ci			ring->dma_pdma[i].txd4 = 0;
250362306a36Sopenharmony_ci		}
250462306a36Sopenharmony_ci	}
250562306a36Sopenharmony_ci
250662306a36Sopenharmony_ci	ring->dma_size = ring_size;
250762306a36Sopenharmony_ci	atomic_set(&ring->free_count, ring_size - 2);
250862306a36Sopenharmony_ci	ring->next_free = ring->dma;
250962306a36Sopenharmony_ci	ring->last_free = (void *)txd;
251062306a36Sopenharmony_ci	ring->last_free_ptr = (u32)(ring->phys + ((ring_size - 1) * sz));
251162306a36Sopenharmony_ci	ring->thresh = MAX_SKB_FRAGS;
251262306a36Sopenharmony_ci
251362306a36Sopenharmony_ci	/* make sure that all changes to the dma ring are flushed before we
251462306a36Sopenharmony_ci	 * continue
251562306a36Sopenharmony_ci	 */
251662306a36Sopenharmony_ci	wmb();
251762306a36Sopenharmony_ci
251862306a36Sopenharmony_ci	if (MTK_HAS_CAPS(soc->caps, MTK_QDMA)) {
251962306a36Sopenharmony_ci		mtk_w32(eth, ring->phys, soc->reg_map->qdma.ctx_ptr);
252062306a36Sopenharmony_ci		mtk_w32(eth, ring->phys, soc->reg_map->qdma.dtx_ptr);
252162306a36Sopenharmony_ci		mtk_w32(eth,
252262306a36Sopenharmony_ci			ring->phys + ((ring_size - 1) * sz),
252362306a36Sopenharmony_ci			soc->reg_map->qdma.crx_ptr);
252462306a36Sopenharmony_ci		mtk_w32(eth, ring->last_free_ptr, soc->reg_map->qdma.drx_ptr);
252562306a36Sopenharmony_ci
252662306a36Sopenharmony_ci		for (i = 0, ofs = 0; i < MTK_QDMA_NUM_QUEUES; i++) {
252762306a36Sopenharmony_ci			val = (QDMA_RES_THRES << 8) | QDMA_RES_THRES;
252862306a36Sopenharmony_ci			mtk_w32(eth, val, soc->reg_map->qdma.qtx_cfg + ofs);
252962306a36Sopenharmony_ci
253062306a36Sopenharmony_ci			val = MTK_QTX_SCH_MIN_RATE_EN |
253162306a36Sopenharmony_ci			      /* minimum: 10 Mbps */
253262306a36Sopenharmony_ci			      FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN, 1) |
253362306a36Sopenharmony_ci			      FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP, 4) |
253462306a36Sopenharmony_ci			      MTK_QTX_SCH_LEAKY_BUCKET_SIZE;
253562306a36Sopenharmony_ci			if (mtk_is_netsys_v1(eth))
253662306a36Sopenharmony_ci				val |= MTK_QTX_SCH_LEAKY_BUCKET_EN;
253762306a36Sopenharmony_ci			mtk_w32(eth, val, soc->reg_map->qdma.qtx_sch + ofs);
253862306a36Sopenharmony_ci			ofs += MTK_QTX_OFFSET;
253962306a36Sopenharmony_ci		}
254062306a36Sopenharmony_ci		val = MTK_QDMA_TX_SCH_MAX_WFQ | (MTK_QDMA_TX_SCH_MAX_WFQ << 16);
254162306a36Sopenharmony_ci		mtk_w32(eth, val, soc->reg_map->qdma.tx_sch_rate);
254262306a36Sopenharmony_ci		if (mtk_is_netsys_v2_or_greater(eth))
254362306a36Sopenharmony_ci			mtk_w32(eth, val, soc->reg_map->qdma.tx_sch_rate + 4);
254462306a36Sopenharmony_ci	} else {
254562306a36Sopenharmony_ci		mtk_w32(eth, ring->phys_pdma, MT7628_TX_BASE_PTR0);
254662306a36Sopenharmony_ci		mtk_w32(eth, ring_size, MT7628_TX_MAX_CNT0);
254762306a36Sopenharmony_ci		mtk_w32(eth, 0, MT7628_TX_CTX_IDX0);
254862306a36Sopenharmony_ci		mtk_w32(eth, MT7628_PST_DTX_IDX0, soc->reg_map->pdma.rst_idx);
254962306a36Sopenharmony_ci	}
255062306a36Sopenharmony_ci
255162306a36Sopenharmony_ci	return 0;
255262306a36Sopenharmony_ci
255362306a36Sopenharmony_cino_tx_mem:
255462306a36Sopenharmony_ci	return -ENOMEM;
255562306a36Sopenharmony_ci}
255662306a36Sopenharmony_ci
255762306a36Sopenharmony_cistatic void mtk_tx_clean(struct mtk_eth *eth)
255862306a36Sopenharmony_ci{
255962306a36Sopenharmony_ci	const struct mtk_soc_data *soc = eth->soc;
256062306a36Sopenharmony_ci	struct mtk_tx_ring *ring = &eth->tx_ring;
256162306a36Sopenharmony_ci	int i;
256262306a36Sopenharmony_ci
256362306a36Sopenharmony_ci	if (ring->buf) {
256462306a36Sopenharmony_ci		for (i = 0; i < ring->dma_size; i++)
256562306a36Sopenharmony_ci			mtk_tx_unmap(eth, &ring->buf[i], NULL, false);
256662306a36Sopenharmony_ci		kfree(ring->buf);
256762306a36Sopenharmony_ci		ring->buf = NULL;
256862306a36Sopenharmony_ci	}
256962306a36Sopenharmony_ci	if (!MTK_HAS_CAPS(soc->caps, MTK_SRAM) && ring->dma) {
257062306a36Sopenharmony_ci		dma_free_coherent(eth->dma_dev,
257162306a36Sopenharmony_ci				  ring->dma_size * soc->txrx.txd_size,
257262306a36Sopenharmony_ci				  ring->dma, ring->phys);
257362306a36Sopenharmony_ci		ring->dma = NULL;
257462306a36Sopenharmony_ci	}
257562306a36Sopenharmony_ci
257662306a36Sopenharmony_ci	if (ring->dma_pdma) {
257762306a36Sopenharmony_ci		dma_free_coherent(eth->dma_dev,
257862306a36Sopenharmony_ci				  ring->dma_size * soc->txrx.txd_size,
257962306a36Sopenharmony_ci				  ring->dma_pdma, ring->phys_pdma);
258062306a36Sopenharmony_ci		ring->dma_pdma = NULL;
258162306a36Sopenharmony_ci	}
258262306a36Sopenharmony_ci}
258362306a36Sopenharmony_ci
258462306a36Sopenharmony_cistatic int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag)
258562306a36Sopenharmony_ci{
258662306a36Sopenharmony_ci	const struct mtk_reg_map *reg_map = eth->soc->reg_map;
258762306a36Sopenharmony_ci	struct mtk_rx_ring *ring;
258862306a36Sopenharmony_ci	int rx_data_len, rx_dma_size, tx_ring_size;
258962306a36Sopenharmony_ci	int i;
259062306a36Sopenharmony_ci
259162306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
259262306a36Sopenharmony_ci		tx_ring_size = MTK_QDMA_RING_SIZE;
259362306a36Sopenharmony_ci	else
259462306a36Sopenharmony_ci		tx_ring_size = MTK_DMA_SIZE;
259562306a36Sopenharmony_ci
259662306a36Sopenharmony_ci	if (rx_flag == MTK_RX_FLAGS_QDMA) {
259762306a36Sopenharmony_ci		if (ring_no)
259862306a36Sopenharmony_ci			return -EINVAL;
259962306a36Sopenharmony_ci		ring = &eth->rx_ring_qdma;
260062306a36Sopenharmony_ci	} else {
260162306a36Sopenharmony_ci		ring = &eth->rx_ring[ring_no];
260262306a36Sopenharmony_ci	}
260362306a36Sopenharmony_ci
260462306a36Sopenharmony_ci	if (rx_flag == MTK_RX_FLAGS_HWLRO) {
260562306a36Sopenharmony_ci		rx_data_len = MTK_MAX_LRO_RX_LENGTH;
260662306a36Sopenharmony_ci		rx_dma_size = MTK_HW_LRO_DMA_SIZE;
260762306a36Sopenharmony_ci	} else {
260862306a36Sopenharmony_ci		rx_data_len = ETH_DATA_LEN;
260962306a36Sopenharmony_ci		rx_dma_size = MTK_DMA_SIZE;
261062306a36Sopenharmony_ci	}
261162306a36Sopenharmony_ci
261262306a36Sopenharmony_ci	ring->frag_size = mtk_max_frag_size(rx_data_len);
261362306a36Sopenharmony_ci	ring->buf_size = mtk_max_buf_size(ring->frag_size);
261462306a36Sopenharmony_ci	ring->data = kcalloc(rx_dma_size, sizeof(*ring->data),
261562306a36Sopenharmony_ci			     GFP_KERNEL);
261662306a36Sopenharmony_ci	if (!ring->data)
261762306a36Sopenharmony_ci		return -ENOMEM;
261862306a36Sopenharmony_ci
261962306a36Sopenharmony_ci	if (mtk_page_pool_enabled(eth)) {
262062306a36Sopenharmony_ci		struct page_pool *pp;
262162306a36Sopenharmony_ci
262262306a36Sopenharmony_ci		pp = mtk_create_page_pool(eth, &ring->xdp_q, ring_no,
262362306a36Sopenharmony_ci					  rx_dma_size);
262462306a36Sopenharmony_ci		if (IS_ERR(pp))
262562306a36Sopenharmony_ci			return PTR_ERR(pp);
262662306a36Sopenharmony_ci
262762306a36Sopenharmony_ci		ring->page_pool = pp;
262862306a36Sopenharmony_ci	}
262962306a36Sopenharmony_ci
263062306a36Sopenharmony_ci	if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SRAM) ||
263162306a36Sopenharmony_ci	    rx_flag != MTK_RX_FLAGS_NORMAL) {
263262306a36Sopenharmony_ci		ring->dma = dma_alloc_coherent(eth->dma_dev,
263362306a36Sopenharmony_ci					       rx_dma_size * eth->soc->txrx.rxd_size,
263462306a36Sopenharmony_ci					       &ring->phys, GFP_KERNEL);
263562306a36Sopenharmony_ci	} else {
263662306a36Sopenharmony_ci		struct mtk_tx_ring *tx_ring = &eth->tx_ring;
263762306a36Sopenharmony_ci
263862306a36Sopenharmony_ci		ring->dma = tx_ring->dma + tx_ring_size *
263962306a36Sopenharmony_ci			    eth->soc->txrx.txd_size * (ring_no + 1);
264062306a36Sopenharmony_ci		ring->phys = tx_ring->phys + tx_ring_size *
264162306a36Sopenharmony_ci			     eth->soc->txrx.txd_size * (ring_no + 1);
264262306a36Sopenharmony_ci	}
264362306a36Sopenharmony_ci
264462306a36Sopenharmony_ci	if (!ring->dma)
264562306a36Sopenharmony_ci		return -ENOMEM;
264662306a36Sopenharmony_ci
264762306a36Sopenharmony_ci	for (i = 0; i < rx_dma_size; i++) {
264862306a36Sopenharmony_ci		struct mtk_rx_dma_v2 *rxd;
264962306a36Sopenharmony_ci		dma_addr_t dma_addr;
265062306a36Sopenharmony_ci		void *data;
265162306a36Sopenharmony_ci
265262306a36Sopenharmony_ci		rxd = ring->dma + i * eth->soc->txrx.rxd_size;
265362306a36Sopenharmony_ci		if (ring->page_pool) {
265462306a36Sopenharmony_ci			data = mtk_page_pool_get_buff(ring->page_pool,
265562306a36Sopenharmony_ci						      &dma_addr, GFP_KERNEL);
265662306a36Sopenharmony_ci			if (!data)
265762306a36Sopenharmony_ci				return -ENOMEM;
265862306a36Sopenharmony_ci		} else {
265962306a36Sopenharmony_ci			if (ring->frag_size <= PAGE_SIZE)
266062306a36Sopenharmony_ci				data = netdev_alloc_frag(ring->frag_size);
266162306a36Sopenharmony_ci			else
266262306a36Sopenharmony_ci				data = mtk_max_lro_buf_alloc(GFP_KERNEL);
266362306a36Sopenharmony_ci
266462306a36Sopenharmony_ci			if (!data)
266562306a36Sopenharmony_ci				return -ENOMEM;
266662306a36Sopenharmony_ci
266762306a36Sopenharmony_ci			dma_addr = dma_map_single(eth->dma_dev,
266862306a36Sopenharmony_ci				data + NET_SKB_PAD + eth->ip_align,
266962306a36Sopenharmony_ci				ring->buf_size, DMA_FROM_DEVICE);
267062306a36Sopenharmony_ci			if (unlikely(dma_mapping_error(eth->dma_dev,
267162306a36Sopenharmony_ci						       dma_addr))) {
267262306a36Sopenharmony_ci				skb_free_frag(data);
267362306a36Sopenharmony_ci				return -ENOMEM;
267462306a36Sopenharmony_ci			}
267562306a36Sopenharmony_ci		}
267662306a36Sopenharmony_ci		rxd->rxd1 = (unsigned int)dma_addr;
267762306a36Sopenharmony_ci		ring->data[i] = data;
267862306a36Sopenharmony_ci
267962306a36Sopenharmony_ci		if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
268062306a36Sopenharmony_ci			rxd->rxd2 = RX_DMA_LSO;
268162306a36Sopenharmony_ci		else
268262306a36Sopenharmony_ci			rxd->rxd2 = RX_DMA_PREP_PLEN0(ring->buf_size);
268362306a36Sopenharmony_ci
268462306a36Sopenharmony_ci		if (MTK_HAS_CAPS(eth->soc->caps, MTK_36BIT_DMA))
268562306a36Sopenharmony_ci			rxd->rxd2 |= RX_DMA_PREP_ADDR64(dma_addr);
268662306a36Sopenharmony_ci
268762306a36Sopenharmony_ci		rxd->rxd3 = 0;
268862306a36Sopenharmony_ci		rxd->rxd4 = 0;
268962306a36Sopenharmony_ci		if (mtk_is_netsys_v2_or_greater(eth)) {
269062306a36Sopenharmony_ci			rxd->rxd5 = 0;
269162306a36Sopenharmony_ci			rxd->rxd6 = 0;
269262306a36Sopenharmony_ci			rxd->rxd7 = 0;
269362306a36Sopenharmony_ci			rxd->rxd8 = 0;
269462306a36Sopenharmony_ci		}
269562306a36Sopenharmony_ci	}
269662306a36Sopenharmony_ci
269762306a36Sopenharmony_ci	ring->dma_size = rx_dma_size;
269862306a36Sopenharmony_ci	ring->calc_idx_update = false;
269962306a36Sopenharmony_ci	ring->calc_idx = rx_dma_size - 1;
270062306a36Sopenharmony_ci	if (rx_flag == MTK_RX_FLAGS_QDMA)
270162306a36Sopenharmony_ci		ring->crx_idx_reg = reg_map->qdma.qcrx_ptr +
270262306a36Sopenharmony_ci				    ring_no * MTK_QRX_OFFSET;
270362306a36Sopenharmony_ci	else
270462306a36Sopenharmony_ci		ring->crx_idx_reg = reg_map->pdma.pcrx_ptr +
270562306a36Sopenharmony_ci				    ring_no * MTK_QRX_OFFSET;
270662306a36Sopenharmony_ci	/* make sure that all changes to the dma ring are flushed before we
270762306a36Sopenharmony_ci	 * continue
270862306a36Sopenharmony_ci	 */
270962306a36Sopenharmony_ci	wmb();
271062306a36Sopenharmony_ci
271162306a36Sopenharmony_ci	if (rx_flag == MTK_RX_FLAGS_QDMA) {
271262306a36Sopenharmony_ci		mtk_w32(eth, ring->phys,
271362306a36Sopenharmony_ci			reg_map->qdma.rx_ptr + ring_no * MTK_QRX_OFFSET);
271462306a36Sopenharmony_ci		mtk_w32(eth, rx_dma_size,
271562306a36Sopenharmony_ci			reg_map->qdma.rx_cnt_cfg + ring_no * MTK_QRX_OFFSET);
271662306a36Sopenharmony_ci		mtk_w32(eth, MTK_PST_DRX_IDX_CFG(ring_no),
271762306a36Sopenharmony_ci			reg_map->qdma.rst_idx);
271862306a36Sopenharmony_ci	} else {
271962306a36Sopenharmony_ci		mtk_w32(eth, ring->phys,
272062306a36Sopenharmony_ci			reg_map->pdma.rx_ptr + ring_no * MTK_QRX_OFFSET);
272162306a36Sopenharmony_ci		mtk_w32(eth, rx_dma_size,
272262306a36Sopenharmony_ci			reg_map->pdma.rx_cnt_cfg + ring_no * MTK_QRX_OFFSET);
272362306a36Sopenharmony_ci		mtk_w32(eth, MTK_PST_DRX_IDX_CFG(ring_no),
272462306a36Sopenharmony_ci			reg_map->pdma.rst_idx);
272562306a36Sopenharmony_ci	}
272662306a36Sopenharmony_ci	mtk_w32(eth, ring->calc_idx, ring->crx_idx_reg);
272762306a36Sopenharmony_ci
272862306a36Sopenharmony_ci	return 0;
272962306a36Sopenharmony_ci}
273062306a36Sopenharmony_ci
273162306a36Sopenharmony_cistatic void mtk_rx_clean(struct mtk_eth *eth, struct mtk_rx_ring *ring, bool in_sram)
273262306a36Sopenharmony_ci{
273362306a36Sopenharmony_ci	u64 addr64 = 0;
273462306a36Sopenharmony_ci	int i;
273562306a36Sopenharmony_ci
273662306a36Sopenharmony_ci	if (ring->data && ring->dma) {
273762306a36Sopenharmony_ci		for (i = 0; i < ring->dma_size; i++) {
273862306a36Sopenharmony_ci			struct mtk_rx_dma *rxd;
273962306a36Sopenharmony_ci
274062306a36Sopenharmony_ci			if (!ring->data[i])
274162306a36Sopenharmony_ci				continue;
274262306a36Sopenharmony_ci
274362306a36Sopenharmony_ci			rxd = ring->dma + i * eth->soc->txrx.rxd_size;
274462306a36Sopenharmony_ci			if (!rxd->rxd1)
274562306a36Sopenharmony_ci				continue;
274662306a36Sopenharmony_ci
274762306a36Sopenharmony_ci			if (MTK_HAS_CAPS(eth->soc->caps, MTK_36BIT_DMA))
274862306a36Sopenharmony_ci				addr64 = RX_DMA_GET_ADDR64(rxd->rxd2);
274962306a36Sopenharmony_ci
275062306a36Sopenharmony_ci			dma_unmap_single(eth->dma_dev, ((u64)rxd->rxd1 | addr64),
275162306a36Sopenharmony_ci					 ring->buf_size, DMA_FROM_DEVICE);
275262306a36Sopenharmony_ci			mtk_rx_put_buff(ring, ring->data[i], false);
275362306a36Sopenharmony_ci		}
275462306a36Sopenharmony_ci		kfree(ring->data);
275562306a36Sopenharmony_ci		ring->data = NULL;
275662306a36Sopenharmony_ci	}
275762306a36Sopenharmony_ci
275862306a36Sopenharmony_ci	if (!in_sram && ring->dma) {
275962306a36Sopenharmony_ci		dma_free_coherent(eth->dma_dev,
276062306a36Sopenharmony_ci				  ring->dma_size * eth->soc->txrx.rxd_size,
276162306a36Sopenharmony_ci				  ring->dma, ring->phys);
276262306a36Sopenharmony_ci		ring->dma = NULL;
276362306a36Sopenharmony_ci	}
276462306a36Sopenharmony_ci
276562306a36Sopenharmony_ci	if (ring->page_pool) {
276662306a36Sopenharmony_ci		if (xdp_rxq_info_is_reg(&ring->xdp_q))
276762306a36Sopenharmony_ci			xdp_rxq_info_unreg(&ring->xdp_q);
276862306a36Sopenharmony_ci		page_pool_destroy(ring->page_pool);
276962306a36Sopenharmony_ci		ring->page_pool = NULL;
277062306a36Sopenharmony_ci	}
277162306a36Sopenharmony_ci}
277262306a36Sopenharmony_ci
277362306a36Sopenharmony_cistatic int mtk_hwlro_rx_init(struct mtk_eth *eth)
277462306a36Sopenharmony_ci{
277562306a36Sopenharmony_ci	int i;
277662306a36Sopenharmony_ci	u32 ring_ctrl_dw1 = 0, ring_ctrl_dw2 = 0, ring_ctrl_dw3 = 0;
277762306a36Sopenharmony_ci	u32 lro_ctrl_dw0 = 0, lro_ctrl_dw3 = 0;
277862306a36Sopenharmony_ci
277962306a36Sopenharmony_ci	/* set LRO rings to auto-learn modes */
278062306a36Sopenharmony_ci	ring_ctrl_dw2 |= MTK_RING_AUTO_LERAN_MODE;
278162306a36Sopenharmony_ci
278262306a36Sopenharmony_ci	/* validate LRO ring */
278362306a36Sopenharmony_ci	ring_ctrl_dw2 |= MTK_RING_VLD;
278462306a36Sopenharmony_ci
278562306a36Sopenharmony_ci	/* set AGE timer (unit: 20us) */
278662306a36Sopenharmony_ci	ring_ctrl_dw2 |= MTK_RING_AGE_TIME_H;
278762306a36Sopenharmony_ci	ring_ctrl_dw1 |= MTK_RING_AGE_TIME_L;
278862306a36Sopenharmony_ci
278962306a36Sopenharmony_ci	/* set max AGG timer (unit: 20us) */
279062306a36Sopenharmony_ci	ring_ctrl_dw2 |= MTK_RING_MAX_AGG_TIME;
279162306a36Sopenharmony_ci
279262306a36Sopenharmony_ci	/* set max LRO AGG count */
279362306a36Sopenharmony_ci	ring_ctrl_dw2 |= MTK_RING_MAX_AGG_CNT_L;
279462306a36Sopenharmony_ci	ring_ctrl_dw3 |= MTK_RING_MAX_AGG_CNT_H;
279562306a36Sopenharmony_ci
279662306a36Sopenharmony_ci	for (i = 1; i < MTK_MAX_RX_RING_NUM; i++) {
279762306a36Sopenharmony_ci		mtk_w32(eth, ring_ctrl_dw1, MTK_LRO_CTRL_DW1_CFG(i));
279862306a36Sopenharmony_ci		mtk_w32(eth, ring_ctrl_dw2, MTK_LRO_CTRL_DW2_CFG(i));
279962306a36Sopenharmony_ci		mtk_w32(eth, ring_ctrl_dw3, MTK_LRO_CTRL_DW3_CFG(i));
280062306a36Sopenharmony_ci	}
280162306a36Sopenharmony_ci
280262306a36Sopenharmony_ci	/* IPv4 checksum update enable */
280362306a36Sopenharmony_ci	lro_ctrl_dw0 |= MTK_L3_CKS_UPD_EN;
280462306a36Sopenharmony_ci
280562306a36Sopenharmony_ci	/* switch priority comparison to packet count mode */
280662306a36Sopenharmony_ci	lro_ctrl_dw0 |= MTK_LRO_ALT_PKT_CNT_MODE;
280762306a36Sopenharmony_ci
280862306a36Sopenharmony_ci	/* bandwidth threshold setting */
280962306a36Sopenharmony_ci	mtk_w32(eth, MTK_HW_LRO_BW_THRE, MTK_PDMA_LRO_CTRL_DW2);
281062306a36Sopenharmony_ci
281162306a36Sopenharmony_ci	/* auto-learn score delta setting */
281262306a36Sopenharmony_ci	mtk_w32(eth, MTK_HW_LRO_REPLACE_DELTA, MTK_PDMA_LRO_ALT_SCORE_DELTA);
281362306a36Sopenharmony_ci
281462306a36Sopenharmony_ci	/* set refresh timer for altering flows to 1 sec. (unit: 20us) */
281562306a36Sopenharmony_ci	mtk_w32(eth, (MTK_HW_LRO_TIMER_UNIT << 16) | MTK_HW_LRO_REFRESH_TIME,
281662306a36Sopenharmony_ci		MTK_PDMA_LRO_ALT_REFRESH_TIMER);
281762306a36Sopenharmony_ci
281862306a36Sopenharmony_ci	/* set HW LRO mode & the max aggregation count for rx packets */
281962306a36Sopenharmony_ci	lro_ctrl_dw3 |= MTK_ADMA_MODE | (MTK_HW_LRO_MAX_AGG_CNT & 0xff);
282062306a36Sopenharmony_ci
282162306a36Sopenharmony_ci	/* the minimal remaining room of SDL0 in RXD for lro aggregation */
282262306a36Sopenharmony_ci	lro_ctrl_dw3 |= MTK_LRO_MIN_RXD_SDL;
282362306a36Sopenharmony_ci
282462306a36Sopenharmony_ci	/* enable HW LRO */
282562306a36Sopenharmony_ci	lro_ctrl_dw0 |= MTK_LRO_EN;
282662306a36Sopenharmony_ci
282762306a36Sopenharmony_ci	mtk_w32(eth, lro_ctrl_dw3, MTK_PDMA_LRO_CTRL_DW3);
282862306a36Sopenharmony_ci	mtk_w32(eth, lro_ctrl_dw0, MTK_PDMA_LRO_CTRL_DW0);
282962306a36Sopenharmony_ci
283062306a36Sopenharmony_ci	return 0;
283162306a36Sopenharmony_ci}
283262306a36Sopenharmony_ci
283362306a36Sopenharmony_cistatic void mtk_hwlro_rx_uninit(struct mtk_eth *eth)
283462306a36Sopenharmony_ci{
283562306a36Sopenharmony_ci	int i;
283662306a36Sopenharmony_ci	u32 val;
283762306a36Sopenharmony_ci
283862306a36Sopenharmony_ci	/* relinquish lro rings, flush aggregated packets */
283962306a36Sopenharmony_ci	mtk_w32(eth, MTK_LRO_RING_RELINQUISH_REQ, MTK_PDMA_LRO_CTRL_DW0);
284062306a36Sopenharmony_ci
284162306a36Sopenharmony_ci	/* wait for relinquishments done */
284262306a36Sopenharmony_ci	for (i = 0; i < 10; i++) {
284362306a36Sopenharmony_ci		val = mtk_r32(eth, MTK_PDMA_LRO_CTRL_DW0);
284462306a36Sopenharmony_ci		if (val & MTK_LRO_RING_RELINQUISH_DONE) {
284562306a36Sopenharmony_ci			msleep(20);
284662306a36Sopenharmony_ci			continue;
284762306a36Sopenharmony_ci		}
284862306a36Sopenharmony_ci		break;
284962306a36Sopenharmony_ci	}
285062306a36Sopenharmony_ci
285162306a36Sopenharmony_ci	/* invalidate lro rings */
285262306a36Sopenharmony_ci	for (i = 1; i < MTK_MAX_RX_RING_NUM; i++)
285362306a36Sopenharmony_ci		mtk_w32(eth, 0, MTK_LRO_CTRL_DW2_CFG(i));
285462306a36Sopenharmony_ci
285562306a36Sopenharmony_ci	/* disable HW LRO */
285662306a36Sopenharmony_ci	mtk_w32(eth, 0, MTK_PDMA_LRO_CTRL_DW0);
285762306a36Sopenharmony_ci}
285862306a36Sopenharmony_ci
285962306a36Sopenharmony_cistatic void mtk_hwlro_val_ipaddr(struct mtk_eth *eth, int idx, __be32 ip)
286062306a36Sopenharmony_ci{
286162306a36Sopenharmony_ci	u32 reg_val;
286262306a36Sopenharmony_ci
286362306a36Sopenharmony_ci	reg_val = mtk_r32(eth, MTK_LRO_CTRL_DW2_CFG(idx));
286462306a36Sopenharmony_ci
286562306a36Sopenharmony_ci	/* invalidate the IP setting */
286662306a36Sopenharmony_ci	mtk_w32(eth, (reg_val & ~MTK_RING_MYIP_VLD), MTK_LRO_CTRL_DW2_CFG(idx));
286762306a36Sopenharmony_ci
286862306a36Sopenharmony_ci	mtk_w32(eth, ip, MTK_LRO_DIP_DW0_CFG(idx));
286962306a36Sopenharmony_ci
287062306a36Sopenharmony_ci	/* validate the IP setting */
287162306a36Sopenharmony_ci	mtk_w32(eth, (reg_val | MTK_RING_MYIP_VLD), MTK_LRO_CTRL_DW2_CFG(idx));
287262306a36Sopenharmony_ci}
287362306a36Sopenharmony_ci
287462306a36Sopenharmony_cistatic void mtk_hwlro_inval_ipaddr(struct mtk_eth *eth, int idx)
287562306a36Sopenharmony_ci{
287662306a36Sopenharmony_ci	u32 reg_val;
287762306a36Sopenharmony_ci
287862306a36Sopenharmony_ci	reg_val = mtk_r32(eth, MTK_LRO_CTRL_DW2_CFG(idx));
287962306a36Sopenharmony_ci
288062306a36Sopenharmony_ci	/* invalidate the IP setting */
288162306a36Sopenharmony_ci	mtk_w32(eth, (reg_val & ~MTK_RING_MYIP_VLD), MTK_LRO_CTRL_DW2_CFG(idx));
288262306a36Sopenharmony_ci
288362306a36Sopenharmony_ci	mtk_w32(eth, 0, MTK_LRO_DIP_DW0_CFG(idx));
288462306a36Sopenharmony_ci}
288562306a36Sopenharmony_ci
288662306a36Sopenharmony_cistatic int mtk_hwlro_get_ip_cnt(struct mtk_mac *mac)
288762306a36Sopenharmony_ci{
288862306a36Sopenharmony_ci	int cnt = 0;
288962306a36Sopenharmony_ci	int i;
289062306a36Sopenharmony_ci
289162306a36Sopenharmony_ci	for (i = 0; i < MTK_MAX_LRO_IP_CNT; i++) {
289262306a36Sopenharmony_ci		if (mac->hwlro_ip[i])
289362306a36Sopenharmony_ci			cnt++;
289462306a36Sopenharmony_ci	}
289562306a36Sopenharmony_ci
289662306a36Sopenharmony_ci	return cnt;
289762306a36Sopenharmony_ci}
289862306a36Sopenharmony_ci
289962306a36Sopenharmony_cistatic int mtk_hwlro_add_ipaddr(struct net_device *dev,
290062306a36Sopenharmony_ci				struct ethtool_rxnfc *cmd)
290162306a36Sopenharmony_ci{
290262306a36Sopenharmony_ci	struct ethtool_rx_flow_spec *fsp =
290362306a36Sopenharmony_ci		(struct ethtool_rx_flow_spec *)&cmd->fs;
290462306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
290562306a36Sopenharmony_ci	struct mtk_eth *eth = mac->hw;
290662306a36Sopenharmony_ci	int hwlro_idx;
290762306a36Sopenharmony_ci
290862306a36Sopenharmony_ci	if ((fsp->flow_type != TCP_V4_FLOW) ||
290962306a36Sopenharmony_ci	    (!fsp->h_u.tcp_ip4_spec.ip4dst) ||
291062306a36Sopenharmony_ci	    (fsp->location > 1))
291162306a36Sopenharmony_ci		return -EINVAL;
291262306a36Sopenharmony_ci
291362306a36Sopenharmony_ci	mac->hwlro_ip[fsp->location] = htonl(fsp->h_u.tcp_ip4_spec.ip4dst);
291462306a36Sopenharmony_ci	hwlro_idx = (mac->id * MTK_MAX_LRO_IP_CNT) + fsp->location;
291562306a36Sopenharmony_ci
291662306a36Sopenharmony_ci	mac->hwlro_ip_cnt = mtk_hwlro_get_ip_cnt(mac);
291762306a36Sopenharmony_ci
291862306a36Sopenharmony_ci	mtk_hwlro_val_ipaddr(eth, hwlro_idx, mac->hwlro_ip[fsp->location]);
291962306a36Sopenharmony_ci
292062306a36Sopenharmony_ci	return 0;
292162306a36Sopenharmony_ci}
292262306a36Sopenharmony_ci
292362306a36Sopenharmony_cistatic int mtk_hwlro_del_ipaddr(struct net_device *dev,
292462306a36Sopenharmony_ci				struct ethtool_rxnfc *cmd)
292562306a36Sopenharmony_ci{
292662306a36Sopenharmony_ci	struct ethtool_rx_flow_spec *fsp =
292762306a36Sopenharmony_ci		(struct ethtool_rx_flow_spec *)&cmd->fs;
292862306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
292962306a36Sopenharmony_ci	struct mtk_eth *eth = mac->hw;
293062306a36Sopenharmony_ci	int hwlro_idx;
293162306a36Sopenharmony_ci
293262306a36Sopenharmony_ci	if (fsp->location > 1)
293362306a36Sopenharmony_ci		return -EINVAL;
293462306a36Sopenharmony_ci
293562306a36Sopenharmony_ci	mac->hwlro_ip[fsp->location] = 0;
293662306a36Sopenharmony_ci	hwlro_idx = (mac->id * MTK_MAX_LRO_IP_CNT) + fsp->location;
293762306a36Sopenharmony_ci
293862306a36Sopenharmony_ci	mac->hwlro_ip_cnt = mtk_hwlro_get_ip_cnt(mac);
293962306a36Sopenharmony_ci
294062306a36Sopenharmony_ci	mtk_hwlro_inval_ipaddr(eth, hwlro_idx);
294162306a36Sopenharmony_ci
294262306a36Sopenharmony_ci	return 0;
294362306a36Sopenharmony_ci}
294462306a36Sopenharmony_ci
294562306a36Sopenharmony_cistatic void mtk_hwlro_netdev_disable(struct net_device *dev)
294662306a36Sopenharmony_ci{
294762306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
294862306a36Sopenharmony_ci	struct mtk_eth *eth = mac->hw;
294962306a36Sopenharmony_ci	int i, hwlro_idx;
295062306a36Sopenharmony_ci
295162306a36Sopenharmony_ci	for (i = 0; i < MTK_MAX_LRO_IP_CNT; i++) {
295262306a36Sopenharmony_ci		mac->hwlro_ip[i] = 0;
295362306a36Sopenharmony_ci		hwlro_idx = (mac->id * MTK_MAX_LRO_IP_CNT) + i;
295462306a36Sopenharmony_ci
295562306a36Sopenharmony_ci		mtk_hwlro_inval_ipaddr(eth, hwlro_idx);
295662306a36Sopenharmony_ci	}
295762306a36Sopenharmony_ci
295862306a36Sopenharmony_ci	mac->hwlro_ip_cnt = 0;
295962306a36Sopenharmony_ci}
296062306a36Sopenharmony_ci
296162306a36Sopenharmony_cistatic int mtk_hwlro_get_fdir_entry(struct net_device *dev,
296262306a36Sopenharmony_ci				    struct ethtool_rxnfc *cmd)
296362306a36Sopenharmony_ci{
296462306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
296562306a36Sopenharmony_ci	struct ethtool_rx_flow_spec *fsp =
296662306a36Sopenharmony_ci		(struct ethtool_rx_flow_spec *)&cmd->fs;
296762306a36Sopenharmony_ci
296862306a36Sopenharmony_ci	if (fsp->location >= ARRAY_SIZE(mac->hwlro_ip))
296962306a36Sopenharmony_ci		return -EINVAL;
297062306a36Sopenharmony_ci
297162306a36Sopenharmony_ci	/* only tcp dst ipv4 is meaningful, others are meaningless */
297262306a36Sopenharmony_ci	fsp->flow_type = TCP_V4_FLOW;
297362306a36Sopenharmony_ci	fsp->h_u.tcp_ip4_spec.ip4dst = ntohl(mac->hwlro_ip[fsp->location]);
297462306a36Sopenharmony_ci	fsp->m_u.tcp_ip4_spec.ip4dst = 0;
297562306a36Sopenharmony_ci
297662306a36Sopenharmony_ci	fsp->h_u.tcp_ip4_spec.ip4src = 0;
297762306a36Sopenharmony_ci	fsp->m_u.tcp_ip4_spec.ip4src = 0xffffffff;
297862306a36Sopenharmony_ci	fsp->h_u.tcp_ip4_spec.psrc = 0;
297962306a36Sopenharmony_ci	fsp->m_u.tcp_ip4_spec.psrc = 0xffff;
298062306a36Sopenharmony_ci	fsp->h_u.tcp_ip4_spec.pdst = 0;
298162306a36Sopenharmony_ci	fsp->m_u.tcp_ip4_spec.pdst = 0xffff;
298262306a36Sopenharmony_ci	fsp->h_u.tcp_ip4_spec.tos = 0;
298362306a36Sopenharmony_ci	fsp->m_u.tcp_ip4_spec.tos = 0xff;
298462306a36Sopenharmony_ci
298562306a36Sopenharmony_ci	return 0;
298662306a36Sopenharmony_ci}
298762306a36Sopenharmony_ci
298862306a36Sopenharmony_cistatic int mtk_hwlro_get_fdir_all(struct net_device *dev,
298962306a36Sopenharmony_ci				  struct ethtool_rxnfc *cmd,
299062306a36Sopenharmony_ci				  u32 *rule_locs)
299162306a36Sopenharmony_ci{
299262306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
299362306a36Sopenharmony_ci	int cnt = 0;
299462306a36Sopenharmony_ci	int i;
299562306a36Sopenharmony_ci
299662306a36Sopenharmony_ci	for (i = 0; i < MTK_MAX_LRO_IP_CNT; i++) {
299762306a36Sopenharmony_ci		if (cnt == cmd->rule_cnt)
299862306a36Sopenharmony_ci			return -EMSGSIZE;
299962306a36Sopenharmony_ci
300062306a36Sopenharmony_ci		if (mac->hwlro_ip[i]) {
300162306a36Sopenharmony_ci			rule_locs[cnt] = i;
300262306a36Sopenharmony_ci			cnt++;
300362306a36Sopenharmony_ci		}
300462306a36Sopenharmony_ci	}
300562306a36Sopenharmony_ci
300662306a36Sopenharmony_ci	cmd->rule_cnt = cnt;
300762306a36Sopenharmony_ci
300862306a36Sopenharmony_ci	return 0;
300962306a36Sopenharmony_ci}
301062306a36Sopenharmony_ci
301162306a36Sopenharmony_cistatic netdev_features_t mtk_fix_features(struct net_device *dev,
301262306a36Sopenharmony_ci					  netdev_features_t features)
301362306a36Sopenharmony_ci{
301462306a36Sopenharmony_ci	if (!(features & NETIF_F_LRO)) {
301562306a36Sopenharmony_ci		struct mtk_mac *mac = netdev_priv(dev);
301662306a36Sopenharmony_ci		int ip_cnt = mtk_hwlro_get_ip_cnt(mac);
301762306a36Sopenharmony_ci
301862306a36Sopenharmony_ci		if (ip_cnt) {
301962306a36Sopenharmony_ci			netdev_info(dev, "RX flow is programmed, LRO should keep on\n");
302062306a36Sopenharmony_ci
302162306a36Sopenharmony_ci			features |= NETIF_F_LRO;
302262306a36Sopenharmony_ci		}
302362306a36Sopenharmony_ci	}
302462306a36Sopenharmony_ci
302562306a36Sopenharmony_ci	return features;
302662306a36Sopenharmony_ci}
302762306a36Sopenharmony_ci
302862306a36Sopenharmony_cistatic int mtk_set_features(struct net_device *dev, netdev_features_t features)
302962306a36Sopenharmony_ci{
303062306a36Sopenharmony_ci	netdev_features_t diff = dev->features ^ features;
303162306a36Sopenharmony_ci
303262306a36Sopenharmony_ci	if ((diff & NETIF_F_LRO) && !(features & NETIF_F_LRO))
303362306a36Sopenharmony_ci		mtk_hwlro_netdev_disable(dev);
303462306a36Sopenharmony_ci
303562306a36Sopenharmony_ci	return 0;
303662306a36Sopenharmony_ci}
303762306a36Sopenharmony_ci
303862306a36Sopenharmony_ci/* wait for DMA to finish whatever it is doing before we start using it again */
303962306a36Sopenharmony_cistatic int mtk_dma_busy_wait(struct mtk_eth *eth)
304062306a36Sopenharmony_ci{
304162306a36Sopenharmony_ci	unsigned int reg;
304262306a36Sopenharmony_ci	int ret;
304362306a36Sopenharmony_ci	u32 val;
304462306a36Sopenharmony_ci
304562306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
304662306a36Sopenharmony_ci		reg = eth->soc->reg_map->qdma.glo_cfg;
304762306a36Sopenharmony_ci	else
304862306a36Sopenharmony_ci		reg = eth->soc->reg_map->pdma.glo_cfg;
304962306a36Sopenharmony_ci
305062306a36Sopenharmony_ci	ret = readx_poll_timeout_atomic(__raw_readl, eth->base + reg, val,
305162306a36Sopenharmony_ci					!(val & (MTK_RX_DMA_BUSY | MTK_TX_DMA_BUSY)),
305262306a36Sopenharmony_ci					5, MTK_DMA_BUSY_TIMEOUT_US);
305362306a36Sopenharmony_ci	if (ret)
305462306a36Sopenharmony_ci		dev_err(eth->dev, "DMA init timeout\n");
305562306a36Sopenharmony_ci
305662306a36Sopenharmony_ci	return ret;
305762306a36Sopenharmony_ci}
305862306a36Sopenharmony_ci
305962306a36Sopenharmony_cistatic int mtk_dma_init(struct mtk_eth *eth)
306062306a36Sopenharmony_ci{
306162306a36Sopenharmony_ci	int err;
306262306a36Sopenharmony_ci	u32 i;
306362306a36Sopenharmony_ci
306462306a36Sopenharmony_ci	if (mtk_dma_busy_wait(eth))
306562306a36Sopenharmony_ci		return -EBUSY;
306662306a36Sopenharmony_ci
306762306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
306862306a36Sopenharmony_ci		/* QDMA needs scratch memory for internal reordering of the
306962306a36Sopenharmony_ci		 * descriptors
307062306a36Sopenharmony_ci		 */
307162306a36Sopenharmony_ci		err = mtk_init_fq_dma(eth);
307262306a36Sopenharmony_ci		if (err)
307362306a36Sopenharmony_ci			return err;
307462306a36Sopenharmony_ci	}
307562306a36Sopenharmony_ci
307662306a36Sopenharmony_ci	err = mtk_tx_alloc(eth);
307762306a36Sopenharmony_ci	if (err)
307862306a36Sopenharmony_ci		return err;
307962306a36Sopenharmony_ci
308062306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
308162306a36Sopenharmony_ci		err = mtk_rx_alloc(eth, 0, MTK_RX_FLAGS_QDMA);
308262306a36Sopenharmony_ci		if (err)
308362306a36Sopenharmony_ci			return err;
308462306a36Sopenharmony_ci	}
308562306a36Sopenharmony_ci
308662306a36Sopenharmony_ci	err = mtk_rx_alloc(eth, 0, MTK_RX_FLAGS_NORMAL);
308762306a36Sopenharmony_ci	if (err)
308862306a36Sopenharmony_ci		return err;
308962306a36Sopenharmony_ci
309062306a36Sopenharmony_ci	if (eth->hwlro) {
309162306a36Sopenharmony_ci		for (i = 1; i < MTK_MAX_RX_RING_NUM; i++) {
309262306a36Sopenharmony_ci			err = mtk_rx_alloc(eth, i, MTK_RX_FLAGS_HWLRO);
309362306a36Sopenharmony_ci			if (err)
309462306a36Sopenharmony_ci				return err;
309562306a36Sopenharmony_ci		}
309662306a36Sopenharmony_ci		err = mtk_hwlro_rx_init(eth);
309762306a36Sopenharmony_ci		if (err)
309862306a36Sopenharmony_ci			return err;
309962306a36Sopenharmony_ci	}
310062306a36Sopenharmony_ci
310162306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
310262306a36Sopenharmony_ci		/* Enable random early drop and set drop threshold
310362306a36Sopenharmony_ci		 * automatically
310462306a36Sopenharmony_ci		 */
310562306a36Sopenharmony_ci		mtk_w32(eth, FC_THRES_DROP_MODE | FC_THRES_DROP_EN |
310662306a36Sopenharmony_ci			FC_THRES_MIN, eth->soc->reg_map->qdma.fc_th);
310762306a36Sopenharmony_ci		mtk_w32(eth, 0x0, eth->soc->reg_map->qdma.hred);
310862306a36Sopenharmony_ci	}
310962306a36Sopenharmony_ci
311062306a36Sopenharmony_ci	return 0;
311162306a36Sopenharmony_ci}
311262306a36Sopenharmony_ci
311362306a36Sopenharmony_cistatic void mtk_dma_free(struct mtk_eth *eth)
311462306a36Sopenharmony_ci{
311562306a36Sopenharmony_ci	const struct mtk_soc_data *soc = eth->soc;
311662306a36Sopenharmony_ci	int i;
311762306a36Sopenharmony_ci
311862306a36Sopenharmony_ci	for (i = 0; i < MTK_MAX_DEVS; i++)
311962306a36Sopenharmony_ci		if (eth->netdev[i])
312062306a36Sopenharmony_ci			netdev_reset_queue(eth->netdev[i]);
312162306a36Sopenharmony_ci	if (!MTK_HAS_CAPS(soc->caps, MTK_SRAM) && eth->scratch_ring) {
312262306a36Sopenharmony_ci		dma_free_coherent(eth->dma_dev,
312362306a36Sopenharmony_ci				  MTK_QDMA_RING_SIZE * soc->txrx.txd_size,
312462306a36Sopenharmony_ci				  eth->scratch_ring, eth->phy_scratch_ring);
312562306a36Sopenharmony_ci		eth->scratch_ring = NULL;
312662306a36Sopenharmony_ci		eth->phy_scratch_ring = 0;
312762306a36Sopenharmony_ci	}
312862306a36Sopenharmony_ci	mtk_tx_clean(eth);
312962306a36Sopenharmony_ci	mtk_rx_clean(eth, &eth->rx_ring[0], MTK_HAS_CAPS(soc->caps, MTK_SRAM));
313062306a36Sopenharmony_ci	mtk_rx_clean(eth, &eth->rx_ring_qdma, false);
313162306a36Sopenharmony_ci
313262306a36Sopenharmony_ci	if (eth->hwlro) {
313362306a36Sopenharmony_ci		mtk_hwlro_rx_uninit(eth);
313462306a36Sopenharmony_ci		for (i = 1; i < MTK_MAX_RX_RING_NUM; i++)
313562306a36Sopenharmony_ci			mtk_rx_clean(eth, &eth->rx_ring[i], false);
313662306a36Sopenharmony_ci	}
313762306a36Sopenharmony_ci
313862306a36Sopenharmony_ci	kfree(eth->scratch_head);
313962306a36Sopenharmony_ci}
314062306a36Sopenharmony_ci
314162306a36Sopenharmony_cistatic bool mtk_hw_reset_check(struct mtk_eth *eth)
314262306a36Sopenharmony_ci{
314362306a36Sopenharmony_ci	u32 val = mtk_r32(eth, MTK_INT_STATUS2);
314462306a36Sopenharmony_ci
314562306a36Sopenharmony_ci	return (val & MTK_FE_INT_FQ_EMPTY) || (val & MTK_FE_INT_RFIFO_UF) ||
314662306a36Sopenharmony_ci	       (val & MTK_FE_INT_RFIFO_OV) || (val & MTK_FE_INT_TSO_FAIL) ||
314762306a36Sopenharmony_ci	       (val & MTK_FE_INT_TSO_ALIGN) || (val & MTK_FE_INT_TSO_ILLEGAL);
314862306a36Sopenharmony_ci}
314962306a36Sopenharmony_ci
315062306a36Sopenharmony_cistatic void mtk_tx_timeout(struct net_device *dev, unsigned int txqueue)
315162306a36Sopenharmony_ci{
315262306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
315362306a36Sopenharmony_ci	struct mtk_eth *eth = mac->hw;
315462306a36Sopenharmony_ci
315562306a36Sopenharmony_ci	if (test_bit(MTK_RESETTING, &eth->state))
315662306a36Sopenharmony_ci		return;
315762306a36Sopenharmony_ci
315862306a36Sopenharmony_ci	if (!mtk_hw_reset_check(eth))
315962306a36Sopenharmony_ci		return;
316062306a36Sopenharmony_ci
316162306a36Sopenharmony_ci	eth->netdev[mac->id]->stats.tx_errors++;
316262306a36Sopenharmony_ci	netif_err(eth, tx_err, dev, "transmit timed out\n");
316362306a36Sopenharmony_ci
316462306a36Sopenharmony_ci	schedule_work(&eth->pending_work);
316562306a36Sopenharmony_ci}
316662306a36Sopenharmony_ci
316762306a36Sopenharmony_cistatic irqreturn_t mtk_handle_irq_rx(int irq, void *_eth)
316862306a36Sopenharmony_ci{
316962306a36Sopenharmony_ci	struct mtk_eth *eth = _eth;
317062306a36Sopenharmony_ci
317162306a36Sopenharmony_ci	eth->rx_events++;
317262306a36Sopenharmony_ci	if (likely(napi_schedule_prep(&eth->rx_napi))) {
317362306a36Sopenharmony_ci		mtk_rx_irq_disable(eth, eth->soc->txrx.rx_irq_done_mask);
317462306a36Sopenharmony_ci		__napi_schedule(&eth->rx_napi);
317562306a36Sopenharmony_ci	}
317662306a36Sopenharmony_ci
317762306a36Sopenharmony_ci	return IRQ_HANDLED;
317862306a36Sopenharmony_ci}
317962306a36Sopenharmony_ci
318062306a36Sopenharmony_cistatic irqreturn_t mtk_handle_irq_tx(int irq, void *_eth)
318162306a36Sopenharmony_ci{
318262306a36Sopenharmony_ci	struct mtk_eth *eth = _eth;
318362306a36Sopenharmony_ci
318462306a36Sopenharmony_ci	eth->tx_events++;
318562306a36Sopenharmony_ci	if (likely(napi_schedule_prep(&eth->tx_napi))) {
318662306a36Sopenharmony_ci		mtk_tx_irq_disable(eth, MTK_TX_DONE_INT);
318762306a36Sopenharmony_ci		__napi_schedule(&eth->tx_napi);
318862306a36Sopenharmony_ci	}
318962306a36Sopenharmony_ci
319062306a36Sopenharmony_ci	return IRQ_HANDLED;
319162306a36Sopenharmony_ci}
319262306a36Sopenharmony_ci
319362306a36Sopenharmony_cistatic irqreturn_t mtk_handle_irq(int irq, void *_eth)
319462306a36Sopenharmony_ci{
319562306a36Sopenharmony_ci	struct mtk_eth *eth = _eth;
319662306a36Sopenharmony_ci	const struct mtk_reg_map *reg_map = eth->soc->reg_map;
319762306a36Sopenharmony_ci
319862306a36Sopenharmony_ci	if (mtk_r32(eth, reg_map->pdma.irq_mask) &
319962306a36Sopenharmony_ci	    eth->soc->txrx.rx_irq_done_mask) {
320062306a36Sopenharmony_ci		if (mtk_r32(eth, reg_map->pdma.irq_status) &
320162306a36Sopenharmony_ci		    eth->soc->txrx.rx_irq_done_mask)
320262306a36Sopenharmony_ci			mtk_handle_irq_rx(irq, _eth);
320362306a36Sopenharmony_ci	}
320462306a36Sopenharmony_ci	if (mtk_r32(eth, reg_map->tx_irq_mask) & MTK_TX_DONE_INT) {
320562306a36Sopenharmony_ci		if (mtk_r32(eth, reg_map->tx_irq_status) & MTK_TX_DONE_INT)
320662306a36Sopenharmony_ci			mtk_handle_irq_tx(irq, _eth);
320762306a36Sopenharmony_ci	}
320862306a36Sopenharmony_ci
320962306a36Sopenharmony_ci	return IRQ_HANDLED;
321062306a36Sopenharmony_ci}
321162306a36Sopenharmony_ci
321262306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
321362306a36Sopenharmony_cistatic void mtk_poll_controller(struct net_device *dev)
321462306a36Sopenharmony_ci{
321562306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
321662306a36Sopenharmony_ci	struct mtk_eth *eth = mac->hw;
321762306a36Sopenharmony_ci
321862306a36Sopenharmony_ci	mtk_tx_irq_disable(eth, MTK_TX_DONE_INT);
321962306a36Sopenharmony_ci	mtk_rx_irq_disable(eth, eth->soc->txrx.rx_irq_done_mask);
322062306a36Sopenharmony_ci	mtk_handle_irq_rx(eth->irq[2], dev);
322162306a36Sopenharmony_ci	mtk_tx_irq_enable(eth, MTK_TX_DONE_INT);
322262306a36Sopenharmony_ci	mtk_rx_irq_enable(eth, eth->soc->txrx.rx_irq_done_mask);
322362306a36Sopenharmony_ci}
322462306a36Sopenharmony_ci#endif
322562306a36Sopenharmony_ci
322662306a36Sopenharmony_cistatic int mtk_start_dma(struct mtk_eth *eth)
322762306a36Sopenharmony_ci{
322862306a36Sopenharmony_ci	u32 val, rx_2b_offset = (NET_IP_ALIGN == 2) ? MTK_RX_2B_OFFSET : 0;
322962306a36Sopenharmony_ci	const struct mtk_reg_map *reg_map = eth->soc->reg_map;
323062306a36Sopenharmony_ci	int err;
323162306a36Sopenharmony_ci
323262306a36Sopenharmony_ci	err = mtk_dma_init(eth);
323362306a36Sopenharmony_ci	if (err) {
323462306a36Sopenharmony_ci		mtk_dma_free(eth);
323562306a36Sopenharmony_ci		return err;
323662306a36Sopenharmony_ci	}
323762306a36Sopenharmony_ci
323862306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
323962306a36Sopenharmony_ci		val = mtk_r32(eth, reg_map->qdma.glo_cfg);
324062306a36Sopenharmony_ci		val |= MTK_TX_DMA_EN | MTK_RX_DMA_EN |
324162306a36Sopenharmony_ci		       MTK_TX_BT_32DWORDS | MTK_NDP_CO_PRO |
324262306a36Sopenharmony_ci		       MTK_RX_2B_OFFSET | MTK_TX_WB_DDONE;
324362306a36Sopenharmony_ci
324462306a36Sopenharmony_ci		if (mtk_is_netsys_v2_or_greater(eth))
324562306a36Sopenharmony_ci			val |= MTK_MUTLI_CNT | MTK_RESV_BUF |
324662306a36Sopenharmony_ci			       MTK_WCOMP_EN | MTK_DMAD_WR_WDONE |
324762306a36Sopenharmony_ci			       MTK_CHK_DDONE_EN | MTK_LEAKY_BUCKET_EN;
324862306a36Sopenharmony_ci		else
324962306a36Sopenharmony_ci			val |= MTK_RX_BT_32DWORDS;
325062306a36Sopenharmony_ci		mtk_w32(eth, val, reg_map->qdma.glo_cfg);
325162306a36Sopenharmony_ci
325262306a36Sopenharmony_ci		mtk_w32(eth,
325362306a36Sopenharmony_ci			MTK_RX_DMA_EN | rx_2b_offset |
325462306a36Sopenharmony_ci			MTK_RX_BT_32DWORDS | MTK_MULTI_EN,
325562306a36Sopenharmony_ci			reg_map->pdma.glo_cfg);
325662306a36Sopenharmony_ci	} else {
325762306a36Sopenharmony_ci		mtk_w32(eth, MTK_TX_WB_DDONE | MTK_TX_DMA_EN | MTK_RX_DMA_EN |
325862306a36Sopenharmony_ci			MTK_MULTI_EN | MTK_PDMA_SIZE_8DWORDS,
325962306a36Sopenharmony_ci			reg_map->pdma.glo_cfg);
326062306a36Sopenharmony_ci	}
326162306a36Sopenharmony_ci
326262306a36Sopenharmony_ci	return 0;
326362306a36Sopenharmony_ci}
326462306a36Sopenharmony_ci
326562306a36Sopenharmony_cistatic void mtk_gdm_config(struct mtk_eth *eth, u32 config)
326662306a36Sopenharmony_ci{
326762306a36Sopenharmony_ci	int i;
326862306a36Sopenharmony_ci
326962306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
327062306a36Sopenharmony_ci		return;
327162306a36Sopenharmony_ci
327262306a36Sopenharmony_ci	for (i = 0; i < MTK_MAX_DEVS; i++) {
327362306a36Sopenharmony_ci		u32 val;
327462306a36Sopenharmony_ci
327562306a36Sopenharmony_ci		if (!eth->netdev[i])
327662306a36Sopenharmony_ci			continue;
327762306a36Sopenharmony_ci
327862306a36Sopenharmony_ci		val = mtk_r32(eth, MTK_GDMA_FWD_CFG(i));
327962306a36Sopenharmony_ci
328062306a36Sopenharmony_ci		/* default setup the forward port to send frame to PDMA */
328162306a36Sopenharmony_ci		val &= ~0xffff;
328262306a36Sopenharmony_ci
328362306a36Sopenharmony_ci		/* Enable RX checksum */
328462306a36Sopenharmony_ci		val |= MTK_GDMA_ICS_EN | MTK_GDMA_TCS_EN | MTK_GDMA_UCS_EN;
328562306a36Sopenharmony_ci
328662306a36Sopenharmony_ci		val |= config;
328762306a36Sopenharmony_ci
328862306a36Sopenharmony_ci		if (netdev_uses_dsa(eth->netdev[i]))
328962306a36Sopenharmony_ci			val |= MTK_GDMA_SPECIAL_TAG;
329062306a36Sopenharmony_ci
329162306a36Sopenharmony_ci		mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i));
329262306a36Sopenharmony_ci	}
329362306a36Sopenharmony_ci	/* Reset and enable PSE */
329462306a36Sopenharmony_ci	mtk_w32(eth, RST_GL_PSE, MTK_RST_GL);
329562306a36Sopenharmony_ci	mtk_w32(eth, 0, MTK_RST_GL);
329662306a36Sopenharmony_ci}
329762306a36Sopenharmony_ci
329862306a36Sopenharmony_ci
329962306a36Sopenharmony_cistatic bool mtk_uses_dsa(struct net_device *dev)
330062306a36Sopenharmony_ci{
330162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_DSA)
330262306a36Sopenharmony_ci	return netdev_uses_dsa(dev) &&
330362306a36Sopenharmony_ci	       dev->dsa_ptr->tag_ops->proto == DSA_TAG_PROTO_MTK;
330462306a36Sopenharmony_ci#else
330562306a36Sopenharmony_ci	return false;
330662306a36Sopenharmony_ci#endif
330762306a36Sopenharmony_ci}
330862306a36Sopenharmony_ci
330962306a36Sopenharmony_cistatic int mtk_device_event(struct notifier_block *n, unsigned long event, void *ptr)
331062306a36Sopenharmony_ci{
331162306a36Sopenharmony_ci	struct mtk_mac *mac = container_of(n, struct mtk_mac, device_notifier);
331262306a36Sopenharmony_ci	struct mtk_eth *eth = mac->hw;
331362306a36Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
331462306a36Sopenharmony_ci	struct ethtool_link_ksettings s;
331562306a36Sopenharmony_ci	struct net_device *ldev;
331662306a36Sopenharmony_ci	struct list_head *iter;
331762306a36Sopenharmony_ci	struct dsa_port *dp;
331862306a36Sopenharmony_ci
331962306a36Sopenharmony_ci	if (event != NETDEV_CHANGE)
332062306a36Sopenharmony_ci		return NOTIFY_DONE;
332162306a36Sopenharmony_ci
332262306a36Sopenharmony_ci	netdev_for_each_lower_dev(dev, ldev, iter) {
332362306a36Sopenharmony_ci		if (netdev_priv(ldev) == mac)
332462306a36Sopenharmony_ci			goto found;
332562306a36Sopenharmony_ci	}
332662306a36Sopenharmony_ci
332762306a36Sopenharmony_ci	return NOTIFY_DONE;
332862306a36Sopenharmony_ci
332962306a36Sopenharmony_cifound:
333062306a36Sopenharmony_ci	if (!dsa_slave_dev_check(dev))
333162306a36Sopenharmony_ci		return NOTIFY_DONE;
333262306a36Sopenharmony_ci
333362306a36Sopenharmony_ci	if (__ethtool_get_link_ksettings(dev, &s))
333462306a36Sopenharmony_ci		return NOTIFY_DONE;
333562306a36Sopenharmony_ci
333662306a36Sopenharmony_ci	if (s.base.speed == 0 || s.base.speed == ((__u32)-1))
333762306a36Sopenharmony_ci		return NOTIFY_DONE;
333862306a36Sopenharmony_ci
333962306a36Sopenharmony_ci	dp = dsa_port_from_netdev(dev);
334062306a36Sopenharmony_ci	if (dp->index >= MTK_QDMA_NUM_QUEUES)
334162306a36Sopenharmony_ci		return NOTIFY_DONE;
334262306a36Sopenharmony_ci
334362306a36Sopenharmony_ci	if (mac->speed > 0 && mac->speed <= s.base.speed)
334462306a36Sopenharmony_ci		s.base.speed = 0;
334562306a36Sopenharmony_ci
334662306a36Sopenharmony_ci	mtk_set_queue_speed(eth, dp->index + 3, s.base.speed);
334762306a36Sopenharmony_ci
334862306a36Sopenharmony_ci	return NOTIFY_DONE;
334962306a36Sopenharmony_ci}
335062306a36Sopenharmony_ci
335162306a36Sopenharmony_cistatic int mtk_open(struct net_device *dev)
335262306a36Sopenharmony_ci{
335362306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
335462306a36Sopenharmony_ci	struct mtk_eth *eth = mac->hw;
335562306a36Sopenharmony_ci	int i, err;
335662306a36Sopenharmony_ci
335762306a36Sopenharmony_ci	err = phylink_of_phy_connect(mac->phylink, mac->of_node, 0);
335862306a36Sopenharmony_ci	if (err) {
335962306a36Sopenharmony_ci		netdev_err(dev, "%s: could not attach PHY: %d\n", __func__,
336062306a36Sopenharmony_ci			   err);
336162306a36Sopenharmony_ci		return err;
336262306a36Sopenharmony_ci	}
336362306a36Sopenharmony_ci
336462306a36Sopenharmony_ci	/* we run 2 netdevs on the same dma ring so we only bring it up once */
336562306a36Sopenharmony_ci	if (!refcount_read(&eth->dma_refcnt)) {
336662306a36Sopenharmony_ci		const struct mtk_soc_data *soc = eth->soc;
336762306a36Sopenharmony_ci		u32 gdm_config;
336862306a36Sopenharmony_ci		int i;
336962306a36Sopenharmony_ci
337062306a36Sopenharmony_ci		err = mtk_start_dma(eth);
337162306a36Sopenharmony_ci		if (err) {
337262306a36Sopenharmony_ci			phylink_disconnect_phy(mac->phylink);
337362306a36Sopenharmony_ci			return err;
337462306a36Sopenharmony_ci		}
337562306a36Sopenharmony_ci
337662306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(eth->ppe); i++)
337762306a36Sopenharmony_ci			mtk_ppe_start(eth->ppe[i]);
337862306a36Sopenharmony_ci
337962306a36Sopenharmony_ci		gdm_config = soc->offload_version ? soc->reg_map->gdma_to_ppe
338062306a36Sopenharmony_ci						  : MTK_GDMA_TO_PDMA;
338162306a36Sopenharmony_ci		mtk_gdm_config(eth, gdm_config);
338262306a36Sopenharmony_ci
338362306a36Sopenharmony_ci		napi_enable(&eth->tx_napi);
338462306a36Sopenharmony_ci		napi_enable(&eth->rx_napi);
338562306a36Sopenharmony_ci		mtk_tx_irq_enable(eth, MTK_TX_DONE_INT);
338662306a36Sopenharmony_ci		mtk_rx_irq_enable(eth, soc->txrx.rx_irq_done_mask);
338762306a36Sopenharmony_ci		refcount_set(&eth->dma_refcnt, 1);
338862306a36Sopenharmony_ci	}
338962306a36Sopenharmony_ci	else
339062306a36Sopenharmony_ci		refcount_inc(&eth->dma_refcnt);
339162306a36Sopenharmony_ci
339262306a36Sopenharmony_ci	phylink_start(mac->phylink);
339362306a36Sopenharmony_ci	netif_tx_start_all_queues(dev);
339462306a36Sopenharmony_ci
339562306a36Sopenharmony_ci	if (mtk_is_netsys_v2_or_greater(eth))
339662306a36Sopenharmony_ci		return 0;
339762306a36Sopenharmony_ci
339862306a36Sopenharmony_ci	if (mtk_uses_dsa(dev) && !eth->prog) {
339962306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(eth->dsa_meta); i++) {
340062306a36Sopenharmony_ci			struct metadata_dst *md_dst = eth->dsa_meta[i];
340162306a36Sopenharmony_ci
340262306a36Sopenharmony_ci			if (md_dst)
340362306a36Sopenharmony_ci				continue;
340462306a36Sopenharmony_ci
340562306a36Sopenharmony_ci			md_dst = metadata_dst_alloc(0, METADATA_HW_PORT_MUX,
340662306a36Sopenharmony_ci						    GFP_KERNEL);
340762306a36Sopenharmony_ci			if (!md_dst)
340862306a36Sopenharmony_ci				return -ENOMEM;
340962306a36Sopenharmony_ci
341062306a36Sopenharmony_ci			md_dst->u.port_info.port_id = i;
341162306a36Sopenharmony_ci			eth->dsa_meta[i] = md_dst;
341262306a36Sopenharmony_ci		}
341362306a36Sopenharmony_ci	} else {
341462306a36Sopenharmony_ci		/* Hardware DSA untagging and VLAN RX offloading need to be
341562306a36Sopenharmony_ci		 * disabled if at least one MAC does not use DSA.
341662306a36Sopenharmony_ci		 */
341762306a36Sopenharmony_ci		u32 val = mtk_r32(eth, MTK_CDMP_IG_CTRL);
341862306a36Sopenharmony_ci
341962306a36Sopenharmony_ci		val &= ~MTK_CDMP_STAG_EN;
342062306a36Sopenharmony_ci		mtk_w32(eth, val, MTK_CDMP_IG_CTRL);
342162306a36Sopenharmony_ci
342262306a36Sopenharmony_ci		mtk_w32(eth, 0, MTK_CDMP_EG_CTRL);
342362306a36Sopenharmony_ci	}
342462306a36Sopenharmony_ci
342562306a36Sopenharmony_ci	return 0;
342662306a36Sopenharmony_ci}
342762306a36Sopenharmony_ci
342862306a36Sopenharmony_cistatic void mtk_stop_dma(struct mtk_eth *eth, u32 glo_cfg)
342962306a36Sopenharmony_ci{
343062306a36Sopenharmony_ci	u32 val;
343162306a36Sopenharmony_ci	int i;
343262306a36Sopenharmony_ci
343362306a36Sopenharmony_ci	/* stop the dma engine */
343462306a36Sopenharmony_ci	spin_lock_bh(&eth->page_lock);
343562306a36Sopenharmony_ci	val = mtk_r32(eth, glo_cfg);
343662306a36Sopenharmony_ci	mtk_w32(eth, val & ~(MTK_TX_WB_DDONE | MTK_RX_DMA_EN | MTK_TX_DMA_EN),
343762306a36Sopenharmony_ci		glo_cfg);
343862306a36Sopenharmony_ci	spin_unlock_bh(&eth->page_lock);
343962306a36Sopenharmony_ci
344062306a36Sopenharmony_ci	/* wait for dma stop */
344162306a36Sopenharmony_ci	for (i = 0; i < 10; i++) {
344262306a36Sopenharmony_ci		val = mtk_r32(eth, glo_cfg);
344362306a36Sopenharmony_ci		if (val & (MTK_TX_DMA_BUSY | MTK_RX_DMA_BUSY)) {
344462306a36Sopenharmony_ci			msleep(20);
344562306a36Sopenharmony_ci			continue;
344662306a36Sopenharmony_ci		}
344762306a36Sopenharmony_ci		break;
344862306a36Sopenharmony_ci	}
344962306a36Sopenharmony_ci}
345062306a36Sopenharmony_ci
345162306a36Sopenharmony_cistatic int mtk_stop(struct net_device *dev)
345262306a36Sopenharmony_ci{
345362306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
345462306a36Sopenharmony_ci	struct mtk_eth *eth = mac->hw;
345562306a36Sopenharmony_ci	int i;
345662306a36Sopenharmony_ci
345762306a36Sopenharmony_ci	phylink_stop(mac->phylink);
345862306a36Sopenharmony_ci
345962306a36Sopenharmony_ci	netif_tx_disable(dev);
346062306a36Sopenharmony_ci
346162306a36Sopenharmony_ci	phylink_disconnect_phy(mac->phylink);
346262306a36Sopenharmony_ci
346362306a36Sopenharmony_ci	/* only shutdown DMA if this is the last user */
346462306a36Sopenharmony_ci	if (!refcount_dec_and_test(&eth->dma_refcnt))
346562306a36Sopenharmony_ci		return 0;
346662306a36Sopenharmony_ci
346762306a36Sopenharmony_ci	mtk_gdm_config(eth, MTK_GDMA_DROP_ALL);
346862306a36Sopenharmony_ci
346962306a36Sopenharmony_ci	mtk_tx_irq_disable(eth, MTK_TX_DONE_INT);
347062306a36Sopenharmony_ci	mtk_rx_irq_disable(eth, eth->soc->txrx.rx_irq_done_mask);
347162306a36Sopenharmony_ci	napi_disable(&eth->tx_napi);
347262306a36Sopenharmony_ci	napi_disable(&eth->rx_napi);
347362306a36Sopenharmony_ci
347462306a36Sopenharmony_ci	cancel_work_sync(&eth->rx_dim.work);
347562306a36Sopenharmony_ci	cancel_work_sync(&eth->tx_dim.work);
347662306a36Sopenharmony_ci
347762306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
347862306a36Sopenharmony_ci		mtk_stop_dma(eth, eth->soc->reg_map->qdma.glo_cfg);
347962306a36Sopenharmony_ci	mtk_stop_dma(eth, eth->soc->reg_map->pdma.glo_cfg);
348062306a36Sopenharmony_ci
348162306a36Sopenharmony_ci	mtk_dma_free(eth);
348262306a36Sopenharmony_ci
348362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(eth->ppe); i++)
348462306a36Sopenharmony_ci		mtk_ppe_stop(eth->ppe[i]);
348562306a36Sopenharmony_ci
348662306a36Sopenharmony_ci	return 0;
348762306a36Sopenharmony_ci}
348862306a36Sopenharmony_ci
348962306a36Sopenharmony_cistatic int mtk_xdp_setup(struct net_device *dev, struct bpf_prog *prog,
349062306a36Sopenharmony_ci			 struct netlink_ext_ack *extack)
349162306a36Sopenharmony_ci{
349262306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
349362306a36Sopenharmony_ci	struct mtk_eth *eth = mac->hw;
349462306a36Sopenharmony_ci	struct bpf_prog *old_prog;
349562306a36Sopenharmony_ci	bool need_update;
349662306a36Sopenharmony_ci
349762306a36Sopenharmony_ci	if (eth->hwlro) {
349862306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "XDP not supported with HWLRO");
349962306a36Sopenharmony_ci		return -EOPNOTSUPP;
350062306a36Sopenharmony_ci	}
350162306a36Sopenharmony_ci
350262306a36Sopenharmony_ci	if (dev->mtu > MTK_PP_MAX_BUF_SIZE) {
350362306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "MTU too large for XDP");
350462306a36Sopenharmony_ci		return -EOPNOTSUPP;
350562306a36Sopenharmony_ci	}
350662306a36Sopenharmony_ci
350762306a36Sopenharmony_ci	need_update = !!eth->prog != !!prog;
350862306a36Sopenharmony_ci	if (netif_running(dev) && need_update)
350962306a36Sopenharmony_ci		mtk_stop(dev);
351062306a36Sopenharmony_ci
351162306a36Sopenharmony_ci	old_prog = rcu_replace_pointer(eth->prog, prog, lockdep_rtnl_is_held());
351262306a36Sopenharmony_ci	if (old_prog)
351362306a36Sopenharmony_ci		bpf_prog_put(old_prog);
351462306a36Sopenharmony_ci
351562306a36Sopenharmony_ci	if (netif_running(dev) && need_update)
351662306a36Sopenharmony_ci		return mtk_open(dev);
351762306a36Sopenharmony_ci
351862306a36Sopenharmony_ci	return 0;
351962306a36Sopenharmony_ci}
352062306a36Sopenharmony_ci
352162306a36Sopenharmony_cistatic int mtk_xdp(struct net_device *dev, struct netdev_bpf *xdp)
352262306a36Sopenharmony_ci{
352362306a36Sopenharmony_ci	switch (xdp->command) {
352462306a36Sopenharmony_ci	case XDP_SETUP_PROG:
352562306a36Sopenharmony_ci		return mtk_xdp_setup(dev, xdp->prog, xdp->extack);
352662306a36Sopenharmony_ci	default:
352762306a36Sopenharmony_ci		return -EINVAL;
352862306a36Sopenharmony_ci	}
352962306a36Sopenharmony_ci}
353062306a36Sopenharmony_ci
353162306a36Sopenharmony_cistatic void ethsys_reset(struct mtk_eth *eth, u32 reset_bits)
353262306a36Sopenharmony_ci{
353362306a36Sopenharmony_ci	regmap_update_bits(eth->ethsys, ETHSYS_RSTCTRL,
353462306a36Sopenharmony_ci			   reset_bits,
353562306a36Sopenharmony_ci			   reset_bits);
353662306a36Sopenharmony_ci
353762306a36Sopenharmony_ci	usleep_range(1000, 1100);
353862306a36Sopenharmony_ci	regmap_update_bits(eth->ethsys, ETHSYS_RSTCTRL,
353962306a36Sopenharmony_ci			   reset_bits,
354062306a36Sopenharmony_ci			   ~reset_bits);
354162306a36Sopenharmony_ci	mdelay(10);
354262306a36Sopenharmony_ci}
354362306a36Sopenharmony_ci
354462306a36Sopenharmony_cistatic void mtk_clk_disable(struct mtk_eth *eth)
354562306a36Sopenharmony_ci{
354662306a36Sopenharmony_ci	int clk;
354762306a36Sopenharmony_ci
354862306a36Sopenharmony_ci	for (clk = MTK_CLK_MAX - 1; clk >= 0; clk--)
354962306a36Sopenharmony_ci		clk_disable_unprepare(eth->clks[clk]);
355062306a36Sopenharmony_ci}
355162306a36Sopenharmony_ci
355262306a36Sopenharmony_cistatic int mtk_clk_enable(struct mtk_eth *eth)
355362306a36Sopenharmony_ci{
355462306a36Sopenharmony_ci	int clk, ret;
355562306a36Sopenharmony_ci
355662306a36Sopenharmony_ci	for (clk = 0; clk < MTK_CLK_MAX ; clk++) {
355762306a36Sopenharmony_ci		ret = clk_prepare_enable(eth->clks[clk]);
355862306a36Sopenharmony_ci		if (ret)
355962306a36Sopenharmony_ci			goto err_disable_clks;
356062306a36Sopenharmony_ci	}
356162306a36Sopenharmony_ci
356262306a36Sopenharmony_ci	return 0;
356362306a36Sopenharmony_ci
356462306a36Sopenharmony_cierr_disable_clks:
356562306a36Sopenharmony_ci	while (--clk >= 0)
356662306a36Sopenharmony_ci		clk_disable_unprepare(eth->clks[clk]);
356762306a36Sopenharmony_ci
356862306a36Sopenharmony_ci	return ret;
356962306a36Sopenharmony_ci}
357062306a36Sopenharmony_ci
357162306a36Sopenharmony_cistatic void mtk_dim_rx(struct work_struct *work)
357262306a36Sopenharmony_ci{
357362306a36Sopenharmony_ci	struct dim *dim = container_of(work, struct dim, work);
357462306a36Sopenharmony_ci	struct mtk_eth *eth = container_of(dim, struct mtk_eth, rx_dim);
357562306a36Sopenharmony_ci	const struct mtk_reg_map *reg_map = eth->soc->reg_map;
357662306a36Sopenharmony_ci	struct dim_cq_moder cur_profile;
357762306a36Sopenharmony_ci	u32 val, cur;
357862306a36Sopenharmony_ci
357962306a36Sopenharmony_ci	cur_profile = net_dim_get_rx_moderation(eth->rx_dim.mode,
358062306a36Sopenharmony_ci						dim->profile_ix);
358162306a36Sopenharmony_ci	spin_lock_bh(&eth->dim_lock);
358262306a36Sopenharmony_ci
358362306a36Sopenharmony_ci	val = mtk_r32(eth, reg_map->pdma.delay_irq);
358462306a36Sopenharmony_ci	val &= MTK_PDMA_DELAY_TX_MASK;
358562306a36Sopenharmony_ci	val |= MTK_PDMA_DELAY_RX_EN;
358662306a36Sopenharmony_ci
358762306a36Sopenharmony_ci	cur = min_t(u32, DIV_ROUND_UP(cur_profile.usec, 20), MTK_PDMA_DELAY_PTIME_MASK);
358862306a36Sopenharmony_ci	val |= cur << MTK_PDMA_DELAY_RX_PTIME_SHIFT;
358962306a36Sopenharmony_ci
359062306a36Sopenharmony_ci	cur = min_t(u32, cur_profile.pkts, MTK_PDMA_DELAY_PINT_MASK);
359162306a36Sopenharmony_ci	val |= cur << MTK_PDMA_DELAY_RX_PINT_SHIFT;
359262306a36Sopenharmony_ci
359362306a36Sopenharmony_ci	mtk_w32(eth, val, reg_map->pdma.delay_irq);
359462306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
359562306a36Sopenharmony_ci		mtk_w32(eth, val, reg_map->qdma.delay_irq);
359662306a36Sopenharmony_ci
359762306a36Sopenharmony_ci	spin_unlock_bh(&eth->dim_lock);
359862306a36Sopenharmony_ci
359962306a36Sopenharmony_ci	dim->state = DIM_START_MEASURE;
360062306a36Sopenharmony_ci}
360162306a36Sopenharmony_ci
360262306a36Sopenharmony_cistatic void mtk_dim_tx(struct work_struct *work)
360362306a36Sopenharmony_ci{
360462306a36Sopenharmony_ci	struct dim *dim = container_of(work, struct dim, work);
360562306a36Sopenharmony_ci	struct mtk_eth *eth = container_of(dim, struct mtk_eth, tx_dim);
360662306a36Sopenharmony_ci	const struct mtk_reg_map *reg_map = eth->soc->reg_map;
360762306a36Sopenharmony_ci	struct dim_cq_moder cur_profile;
360862306a36Sopenharmony_ci	u32 val, cur;
360962306a36Sopenharmony_ci
361062306a36Sopenharmony_ci	cur_profile = net_dim_get_tx_moderation(eth->tx_dim.mode,
361162306a36Sopenharmony_ci						dim->profile_ix);
361262306a36Sopenharmony_ci	spin_lock_bh(&eth->dim_lock);
361362306a36Sopenharmony_ci
361462306a36Sopenharmony_ci	val = mtk_r32(eth, reg_map->pdma.delay_irq);
361562306a36Sopenharmony_ci	val &= MTK_PDMA_DELAY_RX_MASK;
361662306a36Sopenharmony_ci	val |= MTK_PDMA_DELAY_TX_EN;
361762306a36Sopenharmony_ci
361862306a36Sopenharmony_ci	cur = min_t(u32, DIV_ROUND_UP(cur_profile.usec, 20), MTK_PDMA_DELAY_PTIME_MASK);
361962306a36Sopenharmony_ci	val |= cur << MTK_PDMA_DELAY_TX_PTIME_SHIFT;
362062306a36Sopenharmony_ci
362162306a36Sopenharmony_ci	cur = min_t(u32, cur_profile.pkts, MTK_PDMA_DELAY_PINT_MASK);
362262306a36Sopenharmony_ci	val |= cur << MTK_PDMA_DELAY_TX_PINT_SHIFT;
362362306a36Sopenharmony_ci
362462306a36Sopenharmony_ci	mtk_w32(eth, val, reg_map->pdma.delay_irq);
362562306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
362662306a36Sopenharmony_ci		mtk_w32(eth, val, reg_map->qdma.delay_irq);
362762306a36Sopenharmony_ci
362862306a36Sopenharmony_ci	spin_unlock_bh(&eth->dim_lock);
362962306a36Sopenharmony_ci
363062306a36Sopenharmony_ci	dim->state = DIM_START_MEASURE;
363162306a36Sopenharmony_ci}
363262306a36Sopenharmony_ci
363362306a36Sopenharmony_cistatic void mtk_set_mcr_max_rx(struct mtk_mac *mac, u32 val)
363462306a36Sopenharmony_ci{
363562306a36Sopenharmony_ci	struct mtk_eth *eth = mac->hw;
363662306a36Sopenharmony_ci	u32 mcr_cur, mcr_new;
363762306a36Sopenharmony_ci
363862306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
363962306a36Sopenharmony_ci		return;
364062306a36Sopenharmony_ci
364162306a36Sopenharmony_ci	mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
364262306a36Sopenharmony_ci	mcr_new = mcr_cur & ~MAC_MCR_MAX_RX_MASK;
364362306a36Sopenharmony_ci
364462306a36Sopenharmony_ci	if (val <= 1518)
364562306a36Sopenharmony_ci		mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1518);
364662306a36Sopenharmony_ci	else if (val <= 1536)
364762306a36Sopenharmony_ci		mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1536);
364862306a36Sopenharmony_ci	else if (val <= 1552)
364962306a36Sopenharmony_ci		mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1552);
365062306a36Sopenharmony_ci	else
365162306a36Sopenharmony_ci		mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_2048);
365262306a36Sopenharmony_ci
365362306a36Sopenharmony_ci	if (mcr_new != mcr_cur)
365462306a36Sopenharmony_ci		mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id));
365562306a36Sopenharmony_ci}
365662306a36Sopenharmony_ci
365762306a36Sopenharmony_cistatic void mtk_hw_reset(struct mtk_eth *eth)
365862306a36Sopenharmony_ci{
365962306a36Sopenharmony_ci	u32 val;
366062306a36Sopenharmony_ci
366162306a36Sopenharmony_ci	if (mtk_is_netsys_v2_or_greater(eth))
366262306a36Sopenharmony_ci		regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, 0);
366362306a36Sopenharmony_ci
366462306a36Sopenharmony_ci	if (mtk_is_netsys_v3_or_greater(eth)) {
366562306a36Sopenharmony_ci		val = RSTCTRL_PPE0_V3;
366662306a36Sopenharmony_ci
366762306a36Sopenharmony_ci		if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
366862306a36Sopenharmony_ci			val |= RSTCTRL_PPE1_V3;
366962306a36Sopenharmony_ci
367062306a36Sopenharmony_ci		if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE2))
367162306a36Sopenharmony_ci			val |= RSTCTRL_PPE2;
367262306a36Sopenharmony_ci
367362306a36Sopenharmony_ci		val |= RSTCTRL_WDMA0 | RSTCTRL_WDMA1 | RSTCTRL_WDMA2;
367462306a36Sopenharmony_ci	} else if (mtk_is_netsys_v2_or_greater(eth)) {
367562306a36Sopenharmony_ci		val = RSTCTRL_PPE0_V2;
367662306a36Sopenharmony_ci
367762306a36Sopenharmony_ci		if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
367862306a36Sopenharmony_ci			val |= RSTCTRL_PPE1;
367962306a36Sopenharmony_ci	} else {
368062306a36Sopenharmony_ci		val = RSTCTRL_PPE0;
368162306a36Sopenharmony_ci	}
368262306a36Sopenharmony_ci
368362306a36Sopenharmony_ci	ethsys_reset(eth, RSTCTRL_ETH | RSTCTRL_FE | val);
368462306a36Sopenharmony_ci
368562306a36Sopenharmony_ci	if (mtk_is_netsys_v3_or_greater(eth))
368662306a36Sopenharmony_ci		regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN,
368762306a36Sopenharmony_ci			     0x6f8ff);
368862306a36Sopenharmony_ci	else if (mtk_is_netsys_v2_or_greater(eth))
368962306a36Sopenharmony_ci		regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN,
369062306a36Sopenharmony_ci			     0x3ffffff);
369162306a36Sopenharmony_ci}
369262306a36Sopenharmony_ci
369362306a36Sopenharmony_cistatic u32 mtk_hw_reset_read(struct mtk_eth *eth)
369462306a36Sopenharmony_ci{
369562306a36Sopenharmony_ci	u32 val;
369662306a36Sopenharmony_ci
369762306a36Sopenharmony_ci	regmap_read(eth->ethsys, ETHSYS_RSTCTRL, &val);
369862306a36Sopenharmony_ci	return val;
369962306a36Sopenharmony_ci}
370062306a36Sopenharmony_ci
370162306a36Sopenharmony_cistatic void mtk_hw_warm_reset(struct mtk_eth *eth)
370262306a36Sopenharmony_ci{
370362306a36Sopenharmony_ci	u32 rst_mask, val;
370462306a36Sopenharmony_ci
370562306a36Sopenharmony_ci	regmap_update_bits(eth->ethsys, ETHSYS_RSTCTRL, RSTCTRL_FE,
370662306a36Sopenharmony_ci			   RSTCTRL_FE);
370762306a36Sopenharmony_ci	if (readx_poll_timeout_atomic(mtk_hw_reset_read, eth, val,
370862306a36Sopenharmony_ci				      val & RSTCTRL_FE, 1, 1000)) {
370962306a36Sopenharmony_ci		dev_err(eth->dev, "warm reset failed\n");
371062306a36Sopenharmony_ci		mtk_hw_reset(eth);
371162306a36Sopenharmony_ci		return;
371262306a36Sopenharmony_ci	}
371362306a36Sopenharmony_ci
371462306a36Sopenharmony_ci	if (mtk_is_netsys_v3_or_greater(eth)) {
371562306a36Sopenharmony_ci		rst_mask = RSTCTRL_ETH | RSTCTRL_PPE0_V3;
371662306a36Sopenharmony_ci		if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
371762306a36Sopenharmony_ci			rst_mask |= RSTCTRL_PPE1_V3;
371862306a36Sopenharmony_ci		if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE2))
371962306a36Sopenharmony_ci			rst_mask |= RSTCTRL_PPE2;
372062306a36Sopenharmony_ci
372162306a36Sopenharmony_ci		rst_mask |= RSTCTRL_WDMA0 | RSTCTRL_WDMA1 | RSTCTRL_WDMA2;
372262306a36Sopenharmony_ci	} else if (mtk_is_netsys_v2_or_greater(eth)) {
372362306a36Sopenharmony_ci		rst_mask = RSTCTRL_ETH | RSTCTRL_PPE0_V2;
372462306a36Sopenharmony_ci		if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
372562306a36Sopenharmony_ci			rst_mask |= RSTCTRL_PPE1;
372662306a36Sopenharmony_ci	} else {
372762306a36Sopenharmony_ci		rst_mask = RSTCTRL_ETH | RSTCTRL_PPE0;
372862306a36Sopenharmony_ci	}
372962306a36Sopenharmony_ci
373062306a36Sopenharmony_ci	regmap_update_bits(eth->ethsys, ETHSYS_RSTCTRL, rst_mask, rst_mask);
373162306a36Sopenharmony_ci
373262306a36Sopenharmony_ci	udelay(1);
373362306a36Sopenharmony_ci	val = mtk_hw_reset_read(eth);
373462306a36Sopenharmony_ci	if (!(val & rst_mask))
373562306a36Sopenharmony_ci		dev_err(eth->dev, "warm reset stage0 failed %08x (%08x)\n",
373662306a36Sopenharmony_ci			val, rst_mask);
373762306a36Sopenharmony_ci
373862306a36Sopenharmony_ci	rst_mask |= RSTCTRL_FE;
373962306a36Sopenharmony_ci	regmap_update_bits(eth->ethsys, ETHSYS_RSTCTRL, rst_mask, ~rst_mask);
374062306a36Sopenharmony_ci
374162306a36Sopenharmony_ci	udelay(1);
374262306a36Sopenharmony_ci	val = mtk_hw_reset_read(eth);
374362306a36Sopenharmony_ci	if (val & rst_mask)
374462306a36Sopenharmony_ci		dev_err(eth->dev, "warm reset stage1 failed %08x (%08x)\n",
374562306a36Sopenharmony_ci			val, rst_mask);
374662306a36Sopenharmony_ci}
374762306a36Sopenharmony_ci
374862306a36Sopenharmony_cistatic bool mtk_hw_check_dma_hang(struct mtk_eth *eth)
374962306a36Sopenharmony_ci{
375062306a36Sopenharmony_ci	const struct mtk_reg_map *reg_map = eth->soc->reg_map;
375162306a36Sopenharmony_ci	bool gmac1_tx, gmac2_tx, gdm1_tx, gdm2_tx;
375262306a36Sopenharmony_ci	bool oq_hang, cdm1_busy, adma_busy;
375362306a36Sopenharmony_ci	bool wtx_busy, cdm_full, oq_free;
375462306a36Sopenharmony_ci	u32 wdidx, val, gdm1_fc, gdm2_fc;
375562306a36Sopenharmony_ci	bool qfsm_hang, qfwd_hang;
375662306a36Sopenharmony_ci	bool ret = false;
375762306a36Sopenharmony_ci
375862306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
375962306a36Sopenharmony_ci		return false;
376062306a36Sopenharmony_ci
376162306a36Sopenharmony_ci	/* WDMA sanity checks */
376262306a36Sopenharmony_ci	wdidx = mtk_r32(eth, reg_map->wdma_base[0] + 0xc);
376362306a36Sopenharmony_ci
376462306a36Sopenharmony_ci	val = mtk_r32(eth, reg_map->wdma_base[0] + 0x204);
376562306a36Sopenharmony_ci	wtx_busy = FIELD_GET(MTK_TX_DMA_BUSY, val);
376662306a36Sopenharmony_ci
376762306a36Sopenharmony_ci	val = mtk_r32(eth, reg_map->wdma_base[0] + 0x230);
376862306a36Sopenharmony_ci	cdm_full = !FIELD_GET(MTK_CDM_TXFIFO_RDY, val);
376962306a36Sopenharmony_ci
377062306a36Sopenharmony_ci	oq_free  = (!(mtk_r32(eth, reg_map->pse_oq_sta) & GENMASK(24, 16)) &&
377162306a36Sopenharmony_ci		    !(mtk_r32(eth, reg_map->pse_oq_sta + 0x4) & GENMASK(8, 0)) &&
377262306a36Sopenharmony_ci		    !(mtk_r32(eth, reg_map->pse_oq_sta + 0x10) & GENMASK(24, 16)));
377362306a36Sopenharmony_ci
377462306a36Sopenharmony_ci	if (wdidx == eth->reset.wdidx && wtx_busy && cdm_full && oq_free) {
377562306a36Sopenharmony_ci		if (++eth->reset.wdma_hang_count > 2) {
377662306a36Sopenharmony_ci			eth->reset.wdma_hang_count = 0;
377762306a36Sopenharmony_ci			ret = true;
377862306a36Sopenharmony_ci		}
377962306a36Sopenharmony_ci		goto out;
378062306a36Sopenharmony_ci	}
378162306a36Sopenharmony_ci
378262306a36Sopenharmony_ci	/* QDMA sanity checks */
378362306a36Sopenharmony_ci	qfsm_hang = !!mtk_r32(eth, reg_map->qdma.qtx_cfg + 0x234);
378462306a36Sopenharmony_ci	qfwd_hang = !mtk_r32(eth, reg_map->qdma.qtx_cfg + 0x308);
378562306a36Sopenharmony_ci
378662306a36Sopenharmony_ci	gdm1_tx = FIELD_GET(GENMASK(31, 16), mtk_r32(eth, MTK_FE_GDM1_FSM)) > 0;
378762306a36Sopenharmony_ci	gdm2_tx = FIELD_GET(GENMASK(31, 16), mtk_r32(eth, MTK_FE_GDM2_FSM)) > 0;
378862306a36Sopenharmony_ci	gmac1_tx = FIELD_GET(GENMASK(31, 24), mtk_r32(eth, MTK_MAC_FSM(0))) != 1;
378962306a36Sopenharmony_ci	gmac2_tx = FIELD_GET(GENMASK(31, 24), mtk_r32(eth, MTK_MAC_FSM(1))) != 1;
379062306a36Sopenharmony_ci	gdm1_fc = mtk_r32(eth, reg_map->gdm1_cnt + 0x24);
379162306a36Sopenharmony_ci	gdm2_fc = mtk_r32(eth, reg_map->gdm1_cnt + 0x64);
379262306a36Sopenharmony_ci
379362306a36Sopenharmony_ci	if (qfsm_hang && qfwd_hang &&
379462306a36Sopenharmony_ci	    ((gdm1_tx && gmac1_tx && gdm1_fc < 1) ||
379562306a36Sopenharmony_ci	     (gdm2_tx && gmac2_tx && gdm2_fc < 1))) {
379662306a36Sopenharmony_ci		if (++eth->reset.qdma_hang_count > 2) {
379762306a36Sopenharmony_ci			eth->reset.qdma_hang_count = 0;
379862306a36Sopenharmony_ci			ret = true;
379962306a36Sopenharmony_ci		}
380062306a36Sopenharmony_ci		goto out;
380162306a36Sopenharmony_ci	}
380262306a36Sopenharmony_ci
380362306a36Sopenharmony_ci	/* ADMA sanity checks */
380462306a36Sopenharmony_ci	oq_hang = !!(mtk_r32(eth, reg_map->pse_oq_sta) & GENMASK(8, 0));
380562306a36Sopenharmony_ci	cdm1_busy = !!(mtk_r32(eth, MTK_FE_CDM1_FSM) & GENMASK(31, 16));
380662306a36Sopenharmony_ci	adma_busy = !(mtk_r32(eth, reg_map->pdma.adma_rx_dbg0) & GENMASK(4, 0)) &&
380762306a36Sopenharmony_ci		    !(mtk_r32(eth, reg_map->pdma.adma_rx_dbg0) & BIT(6));
380862306a36Sopenharmony_ci
380962306a36Sopenharmony_ci	if (oq_hang && cdm1_busy && adma_busy) {
381062306a36Sopenharmony_ci		if (++eth->reset.adma_hang_count > 2) {
381162306a36Sopenharmony_ci			eth->reset.adma_hang_count = 0;
381262306a36Sopenharmony_ci			ret = true;
381362306a36Sopenharmony_ci		}
381462306a36Sopenharmony_ci		goto out;
381562306a36Sopenharmony_ci	}
381662306a36Sopenharmony_ci
381762306a36Sopenharmony_ci	eth->reset.wdma_hang_count = 0;
381862306a36Sopenharmony_ci	eth->reset.qdma_hang_count = 0;
381962306a36Sopenharmony_ci	eth->reset.adma_hang_count = 0;
382062306a36Sopenharmony_ciout:
382162306a36Sopenharmony_ci	eth->reset.wdidx = wdidx;
382262306a36Sopenharmony_ci
382362306a36Sopenharmony_ci	return ret;
382462306a36Sopenharmony_ci}
382562306a36Sopenharmony_ci
382662306a36Sopenharmony_cistatic void mtk_hw_reset_monitor_work(struct work_struct *work)
382762306a36Sopenharmony_ci{
382862306a36Sopenharmony_ci	struct delayed_work *del_work = to_delayed_work(work);
382962306a36Sopenharmony_ci	struct mtk_eth *eth = container_of(del_work, struct mtk_eth,
383062306a36Sopenharmony_ci					   reset.monitor_work);
383162306a36Sopenharmony_ci
383262306a36Sopenharmony_ci	if (test_bit(MTK_RESETTING, &eth->state))
383362306a36Sopenharmony_ci		goto out;
383462306a36Sopenharmony_ci
383562306a36Sopenharmony_ci	/* DMA stuck checks */
383662306a36Sopenharmony_ci	if (mtk_hw_check_dma_hang(eth))
383762306a36Sopenharmony_ci		schedule_work(&eth->pending_work);
383862306a36Sopenharmony_ci
383962306a36Sopenharmony_ciout:
384062306a36Sopenharmony_ci	schedule_delayed_work(&eth->reset.monitor_work,
384162306a36Sopenharmony_ci			      MTK_DMA_MONITOR_TIMEOUT);
384262306a36Sopenharmony_ci}
384362306a36Sopenharmony_ci
384462306a36Sopenharmony_cistatic int mtk_hw_init(struct mtk_eth *eth, bool reset)
384562306a36Sopenharmony_ci{
384662306a36Sopenharmony_ci	u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA |
384762306a36Sopenharmony_ci		       ETHSYS_DMA_AG_MAP_PPE;
384862306a36Sopenharmony_ci	const struct mtk_reg_map *reg_map = eth->soc->reg_map;
384962306a36Sopenharmony_ci	int i, val, ret;
385062306a36Sopenharmony_ci
385162306a36Sopenharmony_ci	if (!reset && test_and_set_bit(MTK_HW_INIT, &eth->state))
385262306a36Sopenharmony_ci		return 0;
385362306a36Sopenharmony_ci
385462306a36Sopenharmony_ci	if (!reset) {
385562306a36Sopenharmony_ci		pm_runtime_enable(eth->dev);
385662306a36Sopenharmony_ci		pm_runtime_get_sync(eth->dev);
385762306a36Sopenharmony_ci
385862306a36Sopenharmony_ci		ret = mtk_clk_enable(eth);
385962306a36Sopenharmony_ci		if (ret)
386062306a36Sopenharmony_ci			goto err_disable_pm;
386162306a36Sopenharmony_ci	}
386262306a36Sopenharmony_ci
386362306a36Sopenharmony_ci	if (eth->ethsys)
386462306a36Sopenharmony_ci		regmap_update_bits(eth->ethsys, ETHSYS_DMA_AG_MAP, dma_mask,
386562306a36Sopenharmony_ci				   of_dma_is_coherent(eth->dma_dev->of_node) * dma_mask);
386662306a36Sopenharmony_ci
386762306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) {
386862306a36Sopenharmony_ci		ret = device_reset(eth->dev);
386962306a36Sopenharmony_ci		if (ret) {
387062306a36Sopenharmony_ci			dev_err(eth->dev, "MAC reset failed!\n");
387162306a36Sopenharmony_ci			goto err_disable_pm;
387262306a36Sopenharmony_ci		}
387362306a36Sopenharmony_ci
387462306a36Sopenharmony_ci		/* set interrupt delays based on current Net DIM sample */
387562306a36Sopenharmony_ci		mtk_dim_rx(&eth->rx_dim.work);
387662306a36Sopenharmony_ci		mtk_dim_tx(&eth->tx_dim.work);
387762306a36Sopenharmony_ci
387862306a36Sopenharmony_ci		/* disable delay and normal interrupt */
387962306a36Sopenharmony_ci		mtk_tx_irq_disable(eth, ~0);
388062306a36Sopenharmony_ci		mtk_rx_irq_disable(eth, ~0);
388162306a36Sopenharmony_ci
388262306a36Sopenharmony_ci		return 0;
388362306a36Sopenharmony_ci	}
388462306a36Sopenharmony_ci
388562306a36Sopenharmony_ci	msleep(100);
388662306a36Sopenharmony_ci
388762306a36Sopenharmony_ci	if (reset)
388862306a36Sopenharmony_ci		mtk_hw_warm_reset(eth);
388962306a36Sopenharmony_ci	else
389062306a36Sopenharmony_ci		mtk_hw_reset(eth);
389162306a36Sopenharmony_ci
389262306a36Sopenharmony_ci	if (mtk_is_netsys_v2_or_greater(eth)) {
389362306a36Sopenharmony_ci		/* Set FE to PDMAv2 if necessary */
389462306a36Sopenharmony_ci		val = mtk_r32(eth, MTK_FE_GLO_MISC);
389562306a36Sopenharmony_ci		mtk_w32(eth,  val | BIT(4), MTK_FE_GLO_MISC);
389662306a36Sopenharmony_ci	}
389762306a36Sopenharmony_ci
389862306a36Sopenharmony_ci	if (eth->pctl) {
389962306a36Sopenharmony_ci		/* Set GE2 driving and slew rate */
390062306a36Sopenharmony_ci		regmap_write(eth->pctl, GPIO_DRV_SEL10, 0xa00);
390162306a36Sopenharmony_ci
390262306a36Sopenharmony_ci		/* set GE2 TDSEL */
390362306a36Sopenharmony_ci		regmap_write(eth->pctl, GPIO_OD33_CTRL8, 0x5);
390462306a36Sopenharmony_ci
390562306a36Sopenharmony_ci		/* set GE2 TUNE */
390662306a36Sopenharmony_ci		regmap_write(eth->pctl, GPIO_BIAS_CTRL, 0x0);
390762306a36Sopenharmony_ci	}
390862306a36Sopenharmony_ci
390962306a36Sopenharmony_ci	/* Set linkdown as the default for each GMAC. Its own MCR would be set
391062306a36Sopenharmony_ci	 * up with the more appropriate value when mtk_mac_config call is being
391162306a36Sopenharmony_ci	 * invoked.
391262306a36Sopenharmony_ci	 */
391362306a36Sopenharmony_ci	for (i = 0; i < MTK_MAX_DEVS; i++) {
391462306a36Sopenharmony_ci		struct net_device *dev = eth->netdev[i];
391562306a36Sopenharmony_ci
391662306a36Sopenharmony_ci		if (!dev)
391762306a36Sopenharmony_ci			continue;
391862306a36Sopenharmony_ci
391962306a36Sopenharmony_ci		mtk_w32(eth, MAC_MCR_FORCE_LINK_DOWN, MTK_MAC_MCR(i));
392062306a36Sopenharmony_ci		mtk_set_mcr_max_rx(netdev_priv(dev),
392162306a36Sopenharmony_ci				   dev->mtu + MTK_RX_ETH_HLEN);
392262306a36Sopenharmony_ci	}
392362306a36Sopenharmony_ci
392462306a36Sopenharmony_ci	/* Indicates CDM to parse the MTK special tag from CPU
392562306a36Sopenharmony_ci	 * which also is working out for untag packets.
392662306a36Sopenharmony_ci	 */
392762306a36Sopenharmony_ci	val = mtk_r32(eth, MTK_CDMQ_IG_CTRL);
392862306a36Sopenharmony_ci	mtk_w32(eth, val | MTK_CDMQ_STAG_EN, MTK_CDMQ_IG_CTRL);
392962306a36Sopenharmony_ci	if (mtk_is_netsys_v1(eth)) {
393062306a36Sopenharmony_ci		val = mtk_r32(eth, MTK_CDMP_IG_CTRL);
393162306a36Sopenharmony_ci		mtk_w32(eth, val | MTK_CDMP_STAG_EN, MTK_CDMP_IG_CTRL);
393262306a36Sopenharmony_ci
393362306a36Sopenharmony_ci		mtk_w32(eth, 1, MTK_CDMP_EG_CTRL);
393462306a36Sopenharmony_ci	}
393562306a36Sopenharmony_ci
393662306a36Sopenharmony_ci	/* set interrupt delays based on current Net DIM sample */
393762306a36Sopenharmony_ci	mtk_dim_rx(&eth->rx_dim.work);
393862306a36Sopenharmony_ci	mtk_dim_tx(&eth->tx_dim.work);
393962306a36Sopenharmony_ci
394062306a36Sopenharmony_ci	/* disable delay and normal interrupt */
394162306a36Sopenharmony_ci	mtk_tx_irq_disable(eth, ~0);
394262306a36Sopenharmony_ci	mtk_rx_irq_disable(eth, ~0);
394362306a36Sopenharmony_ci
394462306a36Sopenharmony_ci	/* FE int grouping */
394562306a36Sopenharmony_ci	mtk_w32(eth, MTK_TX_DONE_INT, reg_map->pdma.int_grp);
394662306a36Sopenharmony_ci	mtk_w32(eth, eth->soc->txrx.rx_irq_done_mask, reg_map->pdma.int_grp + 4);
394762306a36Sopenharmony_ci	mtk_w32(eth, MTK_TX_DONE_INT, reg_map->qdma.int_grp);
394862306a36Sopenharmony_ci	mtk_w32(eth, eth->soc->txrx.rx_irq_done_mask, reg_map->qdma.int_grp + 4);
394962306a36Sopenharmony_ci	mtk_w32(eth, 0x21021000, MTK_FE_INT_GRP);
395062306a36Sopenharmony_ci
395162306a36Sopenharmony_ci	if (mtk_is_netsys_v3_or_greater(eth)) {
395262306a36Sopenharmony_ci		/* PSE should not drop port1, port8 and port9 packets */
395362306a36Sopenharmony_ci		mtk_w32(eth, 0x00000302, PSE_DROP_CFG);
395462306a36Sopenharmony_ci
395562306a36Sopenharmony_ci		/* GDM and CDM Threshold */
395662306a36Sopenharmony_ci		mtk_w32(eth, 0x00000707, MTK_CDMW0_THRES);
395762306a36Sopenharmony_ci		mtk_w32(eth, 0x00000077, MTK_CDMW1_THRES);
395862306a36Sopenharmony_ci
395962306a36Sopenharmony_ci		/* Disable GDM1 RX CRC stripping */
396062306a36Sopenharmony_ci		mtk_m32(eth, MTK_GDMA_STRP_CRC, 0, MTK_GDMA_FWD_CFG(0));
396162306a36Sopenharmony_ci
396262306a36Sopenharmony_ci		/* PSE GDM3 MIB counter has incorrect hw default values,
396362306a36Sopenharmony_ci		 * so the driver ought to read clear the values beforehand
396462306a36Sopenharmony_ci		 * in case ethtool retrieve wrong mib values.
396562306a36Sopenharmony_ci		 */
396662306a36Sopenharmony_ci		for (i = 0; i < 0x80; i += 0x4)
396762306a36Sopenharmony_ci			mtk_r32(eth, reg_map->gdm1_cnt + 0x100 + i);
396862306a36Sopenharmony_ci	} else if (!mtk_is_netsys_v1(eth)) {
396962306a36Sopenharmony_ci		/* PSE should not drop port8 and port9 packets from WDMA Tx */
397062306a36Sopenharmony_ci		mtk_w32(eth, 0x00000300, PSE_DROP_CFG);
397162306a36Sopenharmony_ci
397262306a36Sopenharmony_ci		/* PSE should drop packets to port 8/9 on WDMA Rx ring full */
397362306a36Sopenharmony_ci		mtk_w32(eth, 0x00000300, PSE_PPE0_DROP);
397462306a36Sopenharmony_ci
397562306a36Sopenharmony_ci		/* PSE Free Queue Flow Control  */
397662306a36Sopenharmony_ci		mtk_w32(eth, 0x01fa01f4, PSE_FQFC_CFG2);
397762306a36Sopenharmony_ci
397862306a36Sopenharmony_ci		/* PSE config input queue threshold */
397962306a36Sopenharmony_ci		mtk_w32(eth, 0x001a000e, PSE_IQ_REV(1));
398062306a36Sopenharmony_ci		mtk_w32(eth, 0x01ff001a, PSE_IQ_REV(2));
398162306a36Sopenharmony_ci		mtk_w32(eth, 0x000e01ff, PSE_IQ_REV(3));
398262306a36Sopenharmony_ci		mtk_w32(eth, 0x000e000e, PSE_IQ_REV(4));
398362306a36Sopenharmony_ci		mtk_w32(eth, 0x000e000e, PSE_IQ_REV(5));
398462306a36Sopenharmony_ci		mtk_w32(eth, 0x000e000e, PSE_IQ_REV(6));
398562306a36Sopenharmony_ci		mtk_w32(eth, 0x000e000e, PSE_IQ_REV(7));
398662306a36Sopenharmony_ci		mtk_w32(eth, 0x000e000e, PSE_IQ_REV(8));
398762306a36Sopenharmony_ci
398862306a36Sopenharmony_ci		/* PSE config output queue threshold */
398962306a36Sopenharmony_ci		mtk_w32(eth, 0x000f000a, PSE_OQ_TH(1));
399062306a36Sopenharmony_ci		mtk_w32(eth, 0x001a000f, PSE_OQ_TH(2));
399162306a36Sopenharmony_ci		mtk_w32(eth, 0x000f001a, PSE_OQ_TH(3));
399262306a36Sopenharmony_ci		mtk_w32(eth, 0x01ff000f, PSE_OQ_TH(4));
399362306a36Sopenharmony_ci		mtk_w32(eth, 0x000f000f, PSE_OQ_TH(5));
399462306a36Sopenharmony_ci		mtk_w32(eth, 0x0006000f, PSE_OQ_TH(6));
399562306a36Sopenharmony_ci		mtk_w32(eth, 0x00060006, PSE_OQ_TH(7));
399662306a36Sopenharmony_ci		mtk_w32(eth, 0x00060006, PSE_OQ_TH(8));
399762306a36Sopenharmony_ci
399862306a36Sopenharmony_ci		/* GDM and CDM Threshold */
399962306a36Sopenharmony_ci		mtk_w32(eth, 0x00000004, MTK_GDM2_THRES);
400062306a36Sopenharmony_ci		mtk_w32(eth, 0x00000004, MTK_CDMW0_THRES);
400162306a36Sopenharmony_ci		mtk_w32(eth, 0x00000004, MTK_CDMW1_THRES);
400262306a36Sopenharmony_ci		mtk_w32(eth, 0x00000004, MTK_CDME0_THRES);
400362306a36Sopenharmony_ci		mtk_w32(eth, 0x00000004, MTK_CDME1_THRES);
400462306a36Sopenharmony_ci		mtk_w32(eth, 0x00000004, MTK_CDMM_THRES);
400562306a36Sopenharmony_ci	}
400662306a36Sopenharmony_ci
400762306a36Sopenharmony_ci	return 0;
400862306a36Sopenharmony_ci
400962306a36Sopenharmony_cierr_disable_pm:
401062306a36Sopenharmony_ci	if (!reset) {
401162306a36Sopenharmony_ci		pm_runtime_put_sync(eth->dev);
401262306a36Sopenharmony_ci		pm_runtime_disable(eth->dev);
401362306a36Sopenharmony_ci	}
401462306a36Sopenharmony_ci
401562306a36Sopenharmony_ci	return ret;
401662306a36Sopenharmony_ci}
401762306a36Sopenharmony_ci
401862306a36Sopenharmony_cistatic int mtk_hw_deinit(struct mtk_eth *eth)
401962306a36Sopenharmony_ci{
402062306a36Sopenharmony_ci	if (!test_and_clear_bit(MTK_HW_INIT, &eth->state))
402162306a36Sopenharmony_ci		return 0;
402262306a36Sopenharmony_ci
402362306a36Sopenharmony_ci	mtk_clk_disable(eth);
402462306a36Sopenharmony_ci
402562306a36Sopenharmony_ci	pm_runtime_put_sync(eth->dev);
402662306a36Sopenharmony_ci	pm_runtime_disable(eth->dev);
402762306a36Sopenharmony_ci
402862306a36Sopenharmony_ci	return 0;
402962306a36Sopenharmony_ci}
403062306a36Sopenharmony_ci
403162306a36Sopenharmony_cistatic void mtk_uninit(struct net_device *dev)
403262306a36Sopenharmony_ci{
403362306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
403462306a36Sopenharmony_ci	struct mtk_eth *eth = mac->hw;
403562306a36Sopenharmony_ci
403662306a36Sopenharmony_ci	phylink_disconnect_phy(mac->phylink);
403762306a36Sopenharmony_ci	mtk_tx_irq_disable(eth, ~0);
403862306a36Sopenharmony_ci	mtk_rx_irq_disable(eth, ~0);
403962306a36Sopenharmony_ci}
404062306a36Sopenharmony_ci
404162306a36Sopenharmony_cistatic int mtk_change_mtu(struct net_device *dev, int new_mtu)
404262306a36Sopenharmony_ci{
404362306a36Sopenharmony_ci	int length = new_mtu + MTK_RX_ETH_HLEN;
404462306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
404562306a36Sopenharmony_ci	struct mtk_eth *eth = mac->hw;
404662306a36Sopenharmony_ci
404762306a36Sopenharmony_ci	if (rcu_access_pointer(eth->prog) &&
404862306a36Sopenharmony_ci	    length > MTK_PP_MAX_BUF_SIZE) {
404962306a36Sopenharmony_ci		netdev_err(dev, "Invalid MTU for XDP mode\n");
405062306a36Sopenharmony_ci		return -EINVAL;
405162306a36Sopenharmony_ci	}
405262306a36Sopenharmony_ci
405362306a36Sopenharmony_ci	mtk_set_mcr_max_rx(mac, length);
405462306a36Sopenharmony_ci	dev->mtu = new_mtu;
405562306a36Sopenharmony_ci
405662306a36Sopenharmony_ci	return 0;
405762306a36Sopenharmony_ci}
405862306a36Sopenharmony_ci
405962306a36Sopenharmony_cistatic int mtk_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
406062306a36Sopenharmony_ci{
406162306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
406262306a36Sopenharmony_ci
406362306a36Sopenharmony_ci	switch (cmd) {
406462306a36Sopenharmony_ci	case SIOCGMIIPHY:
406562306a36Sopenharmony_ci	case SIOCGMIIREG:
406662306a36Sopenharmony_ci	case SIOCSMIIREG:
406762306a36Sopenharmony_ci		return phylink_mii_ioctl(mac->phylink, ifr, cmd);
406862306a36Sopenharmony_ci	default:
406962306a36Sopenharmony_ci		break;
407062306a36Sopenharmony_ci	}
407162306a36Sopenharmony_ci
407262306a36Sopenharmony_ci	return -EOPNOTSUPP;
407362306a36Sopenharmony_ci}
407462306a36Sopenharmony_ci
407562306a36Sopenharmony_cistatic void mtk_prepare_for_reset(struct mtk_eth *eth)
407662306a36Sopenharmony_ci{
407762306a36Sopenharmony_ci	u32 val;
407862306a36Sopenharmony_ci	int i;
407962306a36Sopenharmony_ci
408062306a36Sopenharmony_ci	/* set FE PPE ports link down */
408162306a36Sopenharmony_ci	for (i = MTK_GMAC1_ID;
408262306a36Sopenharmony_ci	     i <= (mtk_is_netsys_v3_or_greater(eth) ? MTK_GMAC3_ID : MTK_GMAC2_ID);
408362306a36Sopenharmony_ci	     i += 2) {
408462306a36Sopenharmony_ci		val = mtk_r32(eth, MTK_FE_GLO_CFG(i)) | MTK_FE_LINK_DOWN_P(PSE_PPE0_PORT);
408562306a36Sopenharmony_ci		if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
408662306a36Sopenharmony_ci			val |= MTK_FE_LINK_DOWN_P(PSE_PPE1_PORT);
408762306a36Sopenharmony_ci		if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE2))
408862306a36Sopenharmony_ci			val |= MTK_FE_LINK_DOWN_P(PSE_PPE2_PORT);
408962306a36Sopenharmony_ci		mtk_w32(eth, val, MTK_FE_GLO_CFG(i));
409062306a36Sopenharmony_ci	}
409162306a36Sopenharmony_ci
409262306a36Sopenharmony_ci	/* adjust PPE configurations to prepare for reset */
409362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(eth->ppe); i++)
409462306a36Sopenharmony_ci		mtk_ppe_prepare_reset(eth->ppe[i]);
409562306a36Sopenharmony_ci
409662306a36Sopenharmony_ci	/* disable NETSYS interrupts */
409762306a36Sopenharmony_ci	mtk_w32(eth, 0, MTK_FE_INT_ENABLE);
409862306a36Sopenharmony_ci
409962306a36Sopenharmony_ci	/* force link down GMAC */
410062306a36Sopenharmony_ci	for (i = 0; i < 2; i++) {
410162306a36Sopenharmony_ci		val = mtk_r32(eth, MTK_MAC_MCR(i)) & ~MAC_MCR_FORCE_LINK;
410262306a36Sopenharmony_ci		mtk_w32(eth, val, MTK_MAC_MCR(i));
410362306a36Sopenharmony_ci	}
410462306a36Sopenharmony_ci}
410562306a36Sopenharmony_ci
410662306a36Sopenharmony_cistatic void mtk_pending_work(struct work_struct *work)
410762306a36Sopenharmony_ci{
410862306a36Sopenharmony_ci	struct mtk_eth *eth = container_of(work, struct mtk_eth, pending_work);
410962306a36Sopenharmony_ci	unsigned long restart = 0;
411062306a36Sopenharmony_ci	u32 val;
411162306a36Sopenharmony_ci	int i;
411262306a36Sopenharmony_ci
411362306a36Sopenharmony_ci	rtnl_lock();
411462306a36Sopenharmony_ci	set_bit(MTK_RESETTING, &eth->state);
411562306a36Sopenharmony_ci
411662306a36Sopenharmony_ci	mtk_prepare_for_reset(eth);
411762306a36Sopenharmony_ci	mtk_wed_fe_reset();
411862306a36Sopenharmony_ci	/* Run again reset preliminary configuration in order to avoid any
411962306a36Sopenharmony_ci	 * possible race during FE reset since it can run releasing RTNL lock.
412062306a36Sopenharmony_ci	 */
412162306a36Sopenharmony_ci	mtk_prepare_for_reset(eth);
412262306a36Sopenharmony_ci
412362306a36Sopenharmony_ci	/* stop all devices to make sure that dma is properly shut down */
412462306a36Sopenharmony_ci	for (i = 0; i < MTK_MAX_DEVS; i++) {
412562306a36Sopenharmony_ci		if (!eth->netdev[i] || !netif_running(eth->netdev[i]))
412662306a36Sopenharmony_ci			continue;
412762306a36Sopenharmony_ci
412862306a36Sopenharmony_ci		mtk_stop(eth->netdev[i]);
412962306a36Sopenharmony_ci		__set_bit(i, &restart);
413062306a36Sopenharmony_ci	}
413162306a36Sopenharmony_ci
413262306a36Sopenharmony_ci	usleep_range(15000, 16000);
413362306a36Sopenharmony_ci
413462306a36Sopenharmony_ci	if (eth->dev->pins)
413562306a36Sopenharmony_ci		pinctrl_select_state(eth->dev->pins->p,
413662306a36Sopenharmony_ci				     eth->dev->pins->default_state);
413762306a36Sopenharmony_ci	mtk_hw_init(eth, true);
413862306a36Sopenharmony_ci
413962306a36Sopenharmony_ci	/* restart DMA and enable IRQs */
414062306a36Sopenharmony_ci	for (i = 0; i < MTK_MAX_DEVS; i++) {
414162306a36Sopenharmony_ci		if (!eth->netdev[i] || !test_bit(i, &restart))
414262306a36Sopenharmony_ci			continue;
414362306a36Sopenharmony_ci
414462306a36Sopenharmony_ci		if (mtk_open(eth->netdev[i])) {
414562306a36Sopenharmony_ci			netif_alert(eth, ifup, eth->netdev[i],
414662306a36Sopenharmony_ci				    "Driver up/down cycle failed\n");
414762306a36Sopenharmony_ci			dev_close(eth->netdev[i]);
414862306a36Sopenharmony_ci		}
414962306a36Sopenharmony_ci	}
415062306a36Sopenharmony_ci
415162306a36Sopenharmony_ci	/* set FE PPE ports link up */
415262306a36Sopenharmony_ci	for (i = MTK_GMAC1_ID;
415362306a36Sopenharmony_ci	     i <= (mtk_is_netsys_v3_or_greater(eth) ? MTK_GMAC3_ID : MTK_GMAC2_ID);
415462306a36Sopenharmony_ci	     i += 2) {
415562306a36Sopenharmony_ci		val = mtk_r32(eth, MTK_FE_GLO_CFG(i)) & ~MTK_FE_LINK_DOWN_P(PSE_PPE0_PORT);
415662306a36Sopenharmony_ci		if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
415762306a36Sopenharmony_ci			val &= ~MTK_FE_LINK_DOWN_P(PSE_PPE1_PORT);
415862306a36Sopenharmony_ci		if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE2))
415962306a36Sopenharmony_ci			val &= ~MTK_FE_LINK_DOWN_P(PSE_PPE2_PORT);
416062306a36Sopenharmony_ci
416162306a36Sopenharmony_ci		mtk_w32(eth, val, MTK_FE_GLO_CFG(i));
416262306a36Sopenharmony_ci	}
416362306a36Sopenharmony_ci
416462306a36Sopenharmony_ci	clear_bit(MTK_RESETTING, &eth->state);
416562306a36Sopenharmony_ci
416662306a36Sopenharmony_ci	mtk_wed_fe_reset_complete();
416762306a36Sopenharmony_ci
416862306a36Sopenharmony_ci	rtnl_unlock();
416962306a36Sopenharmony_ci}
417062306a36Sopenharmony_ci
417162306a36Sopenharmony_cistatic int mtk_free_dev(struct mtk_eth *eth)
417262306a36Sopenharmony_ci{
417362306a36Sopenharmony_ci	int i;
417462306a36Sopenharmony_ci
417562306a36Sopenharmony_ci	for (i = 0; i < MTK_MAX_DEVS; i++) {
417662306a36Sopenharmony_ci		if (!eth->netdev[i])
417762306a36Sopenharmony_ci			continue;
417862306a36Sopenharmony_ci		free_netdev(eth->netdev[i]);
417962306a36Sopenharmony_ci	}
418062306a36Sopenharmony_ci
418162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(eth->dsa_meta); i++) {
418262306a36Sopenharmony_ci		if (!eth->dsa_meta[i])
418362306a36Sopenharmony_ci			break;
418462306a36Sopenharmony_ci		metadata_dst_free(eth->dsa_meta[i]);
418562306a36Sopenharmony_ci	}
418662306a36Sopenharmony_ci
418762306a36Sopenharmony_ci	return 0;
418862306a36Sopenharmony_ci}
418962306a36Sopenharmony_ci
419062306a36Sopenharmony_cistatic int mtk_unreg_dev(struct mtk_eth *eth)
419162306a36Sopenharmony_ci{
419262306a36Sopenharmony_ci	int i;
419362306a36Sopenharmony_ci
419462306a36Sopenharmony_ci	for (i = 0; i < MTK_MAX_DEVS; i++) {
419562306a36Sopenharmony_ci		struct mtk_mac *mac;
419662306a36Sopenharmony_ci		if (!eth->netdev[i])
419762306a36Sopenharmony_ci			continue;
419862306a36Sopenharmony_ci		mac = netdev_priv(eth->netdev[i]);
419962306a36Sopenharmony_ci		if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
420062306a36Sopenharmony_ci			unregister_netdevice_notifier(&mac->device_notifier);
420162306a36Sopenharmony_ci		unregister_netdev(eth->netdev[i]);
420262306a36Sopenharmony_ci	}
420362306a36Sopenharmony_ci
420462306a36Sopenharmony_ci	return 0;
420562306a36Sopenharmony_ci}
420662306a36Sopenharmony_ci
420762306a36Sopenharmony_cistatic void mtk_sgmii_destroy(struct mtk_eth *eth)
420862306a36Sopenharmony_ci{
420962306a36Sopenharmony_ci	int i;
421062306a36Sopenharmony_ci
421162306a36Sopenharmony_ci	for (i = 0; i < MTK_MAX_DEVS; i++)
421262306a36Sopenharmony_ci		mtk_pcs_lynxi_destroy(eth->sgmii_pcs[i]);
421362306a36Sopenharmony_ci}
421462306a36Sopenharmony_ci
421562306a36Sopenharmony_cistatic int mtk_cleanup(struct mtk_eth *eth)
421662306a36Sopenharmony_ci{
421762306a36Sopenharmony_ci	mtk_sgmii_destroy(eth);
421862306a36Sopenharmony_ci	mtk_unreg_dev(eth);
421962306a36Sopenharmony_ci	mtk_free_dev(eth);
422062306a36Sopenharmony_ci	cancel_work_sync(&eth->pending_work);
422162306a36Sopenharmony_ci	cancel_delayed_work_sync(&eth->reset.monitor_work);
422262306a36Sopenharmony_ci
422362306a36Sopenharmony_ci	return 0;
422462306a36Sopenharmony_ci}
422562306a36Sopenharmony_ci
422662306a36Sopenharmony_cistatic int mtk_get_link_ksettings(struct net_device *ndev,
422762306a36Sopenharmony_ci				  struct ethtool_link_ksettings *cmd)
422862306a36Sopenharmony_ci{
422962306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(ndev);
423062306a36Sopenharmony_ci
423162306a36Sopenharmony_ci	if (unlikely(test_bit(MTK_RESETTING, &mac->hw->state)))
423262306a36Sopenharmony_ci		return -EBUSY;
423362306a36Sopenharmony_ci
423462306a36Sopenharmony_ci	return phylink_ethtool_ksettings_get(mac->phylink, cmd);
423562306a36Sopenharmony_ci}
423662306a36Sopenharmony_ci
423762306a36Sopenharmony_cistatic int mtk_set_link_ksettings(struct net_device *ndev,
423862306a36Sopenharmony_ci				  const struct ethtool_link_ksettings *cmd)
423962306a36Sopenharmony_ci{
424062306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(ndev);
424162306a36Sopenharmony_ci
424262306a36Sopenharmony_ci	if (unlikely(test_bit(MTK_RESETTING, &mac->hw->state)))
424362306a36Sopenharmony_ci		return -EBUSY;
424462306a36Sopenharmony_ci
424562306a36Sopenharmony_ci	return phylink_ethtool_ksettings_set(mac->phylink, cmd);
424662306a36Sopenharmony_ci}
424762306a36Sopenharmony_ci
424862306a36Sopenharmony_cistatic void mtk_get_drvinfo(struct net_device *dev,
424962306a36Sopenharmony_ci			    struct ethtool_drvinfo *info)
425062306a36Sopenharmony_ci{
425162306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
425262306a36Sopenharmony_ci
425362306a36Sopenharmony_ci	strscpy(info->driver, mac->hw->dev->driver->name, sizeof(info->driver));
425462306a36Sopenharmony_ci	strscpy(info->bus_info, dev_name(mac->hw->dev), sizeof(info->bus_info));
425562306a36Sopenharmony_ci	info->n_stats = ARRAY_SIZE(mtk_ethtool_stats);
425662306a36Sopenharmony_ci}
425762306a36Sopenharmony_ci
425862306a36Sopenharmony_cistatic u32 mtk_get_msglevel(struct net_device *dev)
425962306a36Sopenharmony_ci{
426062306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
426162306a36Sopenharmony_ci
426262306a36Sopenharmony_ci	return mac->hw->msg_enable;
426362306a36Sopenharmony_ci}
426462306a36Sopenharmony_ci
426562306a36Sopenharmony_cistatic void mtk_set_msglevel(struct net_device *dev, u32 value)
426662306a36Sopenharmony_ci{
426762306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
426862306a36Sopenharmony_ci
426962306a36Sopenharmony_ci	mac->hw->msg_enable = value;
427062306a36Sopenharmony_ci}
427162306a36Sopenharmony_ci
427262306a36Sopenharmony_cistatic int mtk_nway_reset(struct net_device *dev)
427362306a36Sopenharmony_ci{
427462306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
427562306a36Sopenharmony_ci
427662306a36Sopenharmony_ci	if (unlikely(test_bit(MTK_RESETTING, &mac->hw->state)))
427762306a36Sopenharmony_ci		return -EBUSY;
427862306a36Sopenharmony_ci
427962306a36Sopenharmony_ci	if (!mac->phylink)
428062306a36Sopenharmony_ci		return -ENOTSUPP;
428162306a36Sopenharmony_ci
428262306a36Sopenharmony_ci	return phylink_ethtool_nway_reset(mac->phylink);
428362306a36Sopenharmony_ci}
428462306a36Sopenharmony_ci
428562306a36Sopenharmony_cistatic void mtk_get_strings(struct net_device *dev, u32 stringset, u8 *data)
428662306a36Sopenharmony_ci{
428762306a36Sopenharmony_ci	int i;
428862306a36Sopenharmony_ci
428962306a36Sopenharmony_ci	switch (stringset) {
429062306a36Sopenharmony_ci	case ETH_SS_STATS: {
429162306a36Sopenharmony_ci		struct mtk_mac *mac = netdev_priv(dev);
429262306a36Sopenharmony_ci
429362306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(mtk_ethtool_stats); i++) {
429462306a36Sopenharmony_ci			memcpy(data, mtk_ethtool_stats[i].str, ETH_GSTRING_LEN);
429562306a36Sopenharmony_ci			data += ETH_GSTRING_LEN;
429662306a36Sopenharmony_ci		}
429762306a36Sopenharmony_ci		if (mtk_page_pool_enabled(mac->hw))
429862306a36Sopenharmony_ci			page_pool_ethtool_stats_get_strings(data);
429962306a36Sopenharmony_ci		break;
430062306a36Sopenharmony_ci	}
430162306a36Sopenharmony_ci	default:
430262306a36Sopenharmony_ci		break;
430362306a36Sopenharmony_ci	}
430462306a36Sopenharmony_ci}
430562306a36Sopenharmony_ci
430662306a36Sopenharmony_cistatic int mtk_get_sset_count(struct net_device *dev, int sset)
430762306a36Sopenharmony_ci{
430862306a36Sopenharmony_ci	switch (sset) {
430962306a36Sopenharmony_ci	case ETH_SS_STATS: {
431062306a36Sopenharmony_ci		int count = ARRAY_SIZE(mtk_ethtool_stats);
431162306a36Sopenharmony_ci		struct mtk_mac *mac = netdev_priv(dev);
431262306a36Sopenharmony_ci
431362306a36Sopenharmony_ci		if (mtk_page_pool_enabled(mac->hw))
431462306a36Sopenharmony_ci			count += page_pool_ethtool_stats_get_count();
431562306a36Sopenharmony_ci		return count;
431662306a36Sopenharmony_ci	}
431762306a36Sopenharmony_ci	default:
431862306a36Sopenharmony_ci		return -EOPNOTSUPP;
431962306a36Sopenharmony_ci	}
432062306a36Sopenharmony_ci}
432162306a36Sopenharmony_ci
432262306a36Sopenharmony_cistatic void mtk_ethtool_pp_stats(struct mtk_eth *eth, u64 *data)
432362306a36Sopenharmony_ci{
432462306a36Sopenharmony_ci	struct page_pool_stats stats = {};
432562306a36Sopenharmony_ci	int i;
432662306a36Sopenharmony_ci
432762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(eth->rx_ring); i++) {
432862306a36Sopenharmony_ci		struct mtk_rx_ring *ring = &eth->rx_ring[i];
432962306a36Sopenharmony_ci
433062306a36Sopenharmony_ci		if (!ring->page_pool)
433162306a36Sopenharmony_ci			continue;
433262306a36Sopenharmony_ci
433362306a36Sopenharmony_ci		page_pool_get_stats(ring->page_pool, &stats);
433462306a36Sopenharmony_ci	}
433562306a36Sopenharmony_ci	page_pool_ethtool_stats_get(data, &stats);
433662306a36Sopenharmony_ci}
433762306a36Sopenharmony_ci
433862306a36Sopenharmony_cistatic void mtk_get_ethtool_stats(struct net_device *dev,
433962306a36Sopenharmony_ci				  struct ethtool_stats *stats, u64 *data)
434062306a36Sopenharmony_ci{
434162306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
434262306a36Sopenharmony_ci	struct mtk_hw_stats *hwstats = mac->hw_stats;
434362306a36Sopenharmony_ci	u64 *data_src, *data_dst;
434462306a36Sopenharmony_ci	unsigned int start;
434562306a36Sopenharmony_ci	int i;
434662306a36Sopenharmony_ci
434762306a36Sopenharmony_ci	if (unlikely(test_bit(MTK_RESETTING, &mac->hw->state)))
434862306a36Sopenharmony_ci		return;
434962306a36Sopenharmony_ci
435062306a36Sopenharmony_ci	if (netif_running(dev) && netif_device_present(dev)) {
435162306a36Sopenharmony_ci		if (spin_trylock_bh(&hwstats->stats_lock)) {
435262306a36Sopenharmony_ci			mtk_stats_update_mac(mac);
435362306a36Sopenharmony_ci			spin_unlock_bh(&hwstats->stats_lock);
435462306a36Sopenharmony_ci		}
435562306a36Sopenharmony_ci	}
435662306a36Sopenharmony_ci
435762306a36Sopenharmony_ci	data_src = (u64 *)hwstats;
435862306a36Sopenharmony_ci
435962306a36Sopenharmony_ci	do {
436062306a36Sopenharmony_ci		data_dst = data;
436162306a36Sopenharmony_ci		start = u64_stats_fetch_begin(&hwstats->syncp);
436262306a36Sopenharmony_ci
436362306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(mtk_ethtool_stats); i++)
436462306a36Sopenharmony_ci			*data_dst++ = *(data_src + mtk_ethtool_stats[i].offset);
436562306a36Sopenharmony_ci		if (mtk_page_pool_enabled(mac->hw))
436662306a36Sopenharmony_ci			mtk_ethtool_pp_stats(mac->hw, data_dst);
436762306a36Sopenharmony_ci	} while (u64_stats_fetch_retry(&hwstats->syncp, start));
436862306a36Sopenharmony_ci}
436962306a36Sopenharmony_ci
437062306a36Sopenharmony_cistatic int mtk_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
437162306a36Sopenharmony_ci			 u32 *rule_locs)
437262306a36Sopenharmony_ci{
437362306a36Sopenharmony_ci	int ret = -EOPNOTSUPP;
437462306a36Sopenharmony_ci
437562306a36Sopenharmony_ci	switch (cmd->cmd) {
437662306a36Sopenharmony_ci	case ETHTOOL_GRXRINGS:
437762306a36Sopenharmony_ci		if (dev->hw_features & NETIF_F_LRO) {
437862306a36Sopenharmony_ci			cmd->data = MTK_MAX_RX_RING_NUM;
437962306a36Sopenharmony_ci			ret = 0;
438062306a36Sopenharmony_ci		}
438162306a36Sopenharmony_ci		break;
438262306a36Sopenharmony_ci	case ETHTOOL_GRXCLSRLCNT:
438362306a36Sopenharmony_ci		if (dev->hw_features & NETIF_F_LRO) {
438462306a36Sopenharmony_ci			struct mtk_mac *mac = netdev_priv(dev);
438562306a36Sopenharmony_ci
438662306a36Sopenharmony_ci			cmd->rule_cnt = mac->hwlro_ip_cnt;
438762306a36Sopenharmony_ci			ret = 0;
438862306a36Sopenharmony_ci		}
438962306a36Sopenharmony_ci		break;
439062306a36Sopenharmony_ci	case ETHTOOL_GRXCLSRULE:
439162306a36Sopenharmony_ci		if (dev->hw_features & NETIF_F_LRO)
439262306a36Sopenharmony_ci			ret = mtk_hwlro_get_fdir_entry(dev, cmd);
439362306a36Sopenharmony_ci		break;
439462306a36Sopenharmony_ci	case ETHTOOL_GRXCLSRLALL:
439562306a36Sopenharmony_ci		if (dev->hw_features & NETIF_F_LRO)
439662306a36Sopenharmony_ci			ret = mtk_hwlro_get_fdir_all(dev, cmd,
439762306a36Sopenharmony_ci						     rule_locs);
439862306a36Sopenharmony_ci		break;
439962306a36Sopenharmony_ci	default:
440062306a36Sopenharmony_ci		break;
440162306a36Sopenharmony_ci	}
440262306a36Sopenharmony_ci
440362306a36Sopenharmony_ci	return ret;
440462306a36Sopenharmony_ci}
440562306a36Sopenharmony_ci
440662306a36Sopenharmony_cistatic int mtk_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
440762306a36Sopenharmony_ci{
440862306a36Sopenharmony_ci	int ret = -EOPNOTSUPP;
440962306a36Sopenharmony_ci
441062306a36Sopenharmony_ci	switch (cmd->cmd) {
441162306a36Sopenharmony_ci	case ETHTOOL_SRXCLSRLINS:
441262306a36Sopenharmony_ci		if (dev->hw_features & NETIF_F_LRO)
441362306a36Sopenharmony_ci			ret = mtk_hwlro_add_ipaddr(dev, cmd);
441462306a36Sopenharmony_ci		break;
441562306a36Sopenharmony_ci	case ETHTOOL_SRXCLSRLDEL:
441662306a36Sopenharmony_ci		if (dev->hw_features & NETIF_F_LRO)
441762306a36Sopenharmony_ci			ret = mtk_hwlro_del_ipaddr(dev, cmd);
441862306a36Sopenharmony_ci		break;
441962306a36Sopenharmony_ci	default:
442062306a36Sopenharmony_ci		break;
442162306a36Sopenharmony_ci	}
442262306a36Sopenharmony_ci
442362306a36Sopenharmony_ci	return ret;
442462306a36Sopenharmony_ci}
442562306a36Sopenharmony_ci
442662306a36Sopenharmony_cistatic u16 mtk_select_queue(struct net_device *dev, struct sk_buff *skb,
442762306a36Sopenharmony_ci			    struct net_device *sb_dev)
442862306a36Sopenharmony_ci{
442962306a36Sopenharmony_ci	struct mtk_mac *mac = netdev_priv(dev);
443062306a36Sopenharmony_ci	unsigned int queue = 0;
443162306a36Sopenharmony_ci
443262306a36Sopenharmony_ci	if (netdev_uses_dsa(dev))
443362306a36Sopenharmony_ci		queue = skb_get_queue_mapping(skb) + 3;
443462306a36Sopenharmony_ci	else
443562306a36Sopenharmony_ci		queue = mac->id;
443662306a36Sopenharmony_ci
443762306a36Sopenharmony_ci	if (queue >= dev->num_tx_queues)
443862306a36Sopenharmony_ci		queue = 0;
443962306a36Sopenharmony_ci
444062306a36Sopenharmony_ci	return queue;
444162306a36Sopenharmony_ci}
444262306a36Sopenharmony_ci
444362306a36Sopenharmony_cistatic const struct ethtool_ops mtk_ethtool_ops = {
444462306a36Sopenharmony_ci	.get_link_ksettings	= mtk_get_link_ksettings,
444562306a36Sopenharmony_ci	.set_link_ksettings	= mtk_set_link_ksettings,
444662306a36Sopenharmony_ci	.get_drvinfo		= mtk_get_drvinfo,
444762306a36Sopenharmony_ci	.get_msglevel		= mtk_get_msglevel,
444862306a36Sopenharmony_ci	.set_msglevel		= mtk_set_msglevel,
444962306a36Sopenharmony_ci	.nway_reset		= mtk_nway_reset,
445062306a36Sopenharmony_ci	.get_link		= ethtool_op_get_link,
445162306a36Sopenharmony_ci	.get_strings		= mtk_get_strings,
445262306a36Sopenharmony_ci	.get_sset_count		= mtk_get_sset_count,
445362306a36Sopenharmony_ci	.get_ethtool_stats	= mtk_get_ethtool_stats,
445462306a36Sopenharmony_ci	.get_rxnfc		= mtk_get_rxnfc,
445562306a36Sopenharmony_ci	.set_rxnfc              = mtk_set_rxnfc,
445662306a36Sopenharmony_ci};
445762306a36Sopenharmony_ci
445862306a36Sopenharmony_cistatic const struct net_device_ops mtk_netdev_ops = {
445962306a36Sopenharmony_ci	.ndo_uninit		= mtk_uninit,
446062306a36Sopenharmony_ci	.ndo_open		= mtk_open,
446162306a36Sopenharmony_ci	.ndo_stop		= mtk_stop,
446262306a36Sopenharmony_ci	.ndo_start_xmit		= mtk_start_xmit,
446362306a36Sopenharmony_ci	.ndo_set_mac_address	= mtk_set_mac_address,
446462306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
446562306a36Sopenharmony_ci	.ndo_eth_ioctl		= mtk_do_ioctl,
446662306a36Sopenharmony_ci	.ndo_change_mtu		= mtk_change_mtu,
446762306a36Sopenharmony_ci	.ndo_tx_timeout		= mtk_tx_timeout,
446862306a36Sopenharmony_ci	.ndo_get_stats64        = mtk_get_stats64,
446962306a36Sopenharmony_ci	.ndo_fix_features	= mtk_fix_features,
447062306a36Sopenharmony_ci	.ndo_set_features	= mtk_set_features,
447162306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
447262306a36Sopenharmony_ci	.ndo_poll_controller	= mtk_poll_controller,
447362306a36Sopenharmony_ci#endif
447462306a36Sopenharmony_ci	.ndo_setup_tc		= mtk_eth_setup_tc,
447562306a36Sopenharmony_ci	.ndo_bpf		= mtk_xdp,
447662306a36Sopenharmony_ci	.ndo_xdp_xmit		= mtk_xdp_xmit,
447762306a36Sopenharmony_ci	.ndo_select_queue	= mtk_select_queue,
447862306a36Sopenharmony_ci};
447962306a36Sopenharmony_ci
448062306a36Sopenharmony_cistatic int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
448162306a36Sopenharmony_ci{
448262306a36Sopenharmony_ci	const __be32 *_id = of_get_property(np, "reg", NULL);
448362306a36Sopenharmony_ci	phy_interface_t phy_mode;
448462306a36Sopenharmony_ci	struct phylink *phylink;
448562306a36Sopenharmony_ci	struct mtk_mac *mac;
448662306a36Sopenharmony_ci	int id, err;
448762306a36Sopenharmony_ci	int txqs = 1;
448862306a36Sopenharmony_ci	u32 val;
448962306a36Sopenharmony_ci
449062306a36Sopenharmony_ci	if (!_id) {
449162306a36Sopenharmony_ci		dev_err(eth->dev, "missing mac id\n");
449262306a36Sopenharmony_ci		return -EINVAL;
449362306a36Sopenharmony_ci	}
449462306a36Sopenharmony_ci
449562306a36Sopenharmony_ci	id = be32_to_cpup(_id);
449662306a36Sopenharmony_ci	if (id >= MTK_MAX_DEVS) {
449762306a36Sopenharmony_ci		dev_err(eth->dev, "%d is not a valid mac id\n", id);
449862306a36Sopenharmony_ci		return -EINVAL;
449962306a36Sopenharmony_ci	}
450062306a36Sopenharmony_ci
450162306a36Sopenharmony_ci	if (eth->netdev[id]) {
450262306a36Sopenharmony_ci		dev_err(eth->dev, "duplicate mac id found: %d\n", id);
450362306a36Sopenharmony_ci		return -EINVAL;
450462306a36Sopenharmony_ci	}
450562306a36Sopenharmony_ci
450662306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
450762306a36Sopenharmony_ci		txqs = MTK_QDMA_NUM_QUEUES;
450862306a36Sopenharmony_ci
450962306a36Sopenharmony_ci	eth->netdev[id] = alloc_etherdev_mqs(sizeof(*mac), txqs, 1);
451062306a36Sopenharmony_ci	if (!eth->netdev[id]) {
451162306a36Sopenharmony_ci		dev_err(eth->dev, "alloc_etherdev failed\n");
451262306a36Sopenharmony_ci		return -ENOMEM;
451362306a36Sopenharmony_ci	}
451462306a36Sopenharmony_ci	mac = netdev_priv(eth->netdev[id]);
451562306a36Sopenharmony_ci	eth->mac[id] = mac;
451662306a36Sopenharmony_ci	mac->id = id;
451762306a36Sopenharmony_ci	mac->hw = eth;
451862306a36Sopenharmony_ci	mac->of_node = np;
451962306a36Sopenharmony_ci
452062306a36Sopenharmony_ci	err = of_get_ethdev_address(mac->of_node, eth->netdev[id]);
452162306a36Sopenharmony_ci	if (err == -EPROBE_DEFER)
452262306a36Sopenharmony_ci		return err;
452362306a36Sopenharmony_ci
452462306a36Sopenharmony_ci	if (err) {
452562306a36Sopenharmony_ci		/* If the mac address is invalid, use random mac address */
452662306a36Sopenharmony_ci		eth_hw_addr_random(eth->netdev[id]);
452762306a36Sopenharmony_ci		dev_err(eth->dev, "generated random MAC address %pM\n",
452862306a36Sopenharmony_ci			eth->netdev[id]->dev_addr);
452962306a36Sopenharmony_ci	}
453062306a36Sopenharmony_ci
453162306a36Sopenharmony_ci	memset(mac->hwlro_ip, 0, sizeof(mac->hwlro_ip));
453262306a36Sopenharmony_ci	mac->hwlro_ip_cnt = 0;
453362306a36Sopenharmony_ci
453462306a36Sopenharmony_ci	mac->hw_stats = devm_kzalloc(eth->dev,
453562306a36Sopenharmony_ci				     sizeof(*mac->hw_stats),
453662306a36Sopenharmony_ci				     GFP_KERNEL);
453762306a36Sopenharmony_ci	if (!mac->hw_stats) {
453862306a36Sopenharmony_ci		dev_err(eth->dev, "failed to allocate counter memory\n");
453962306a36Sopenharmony_ci		err = -ENOMEM;
454062306a36Sopenharmony_ci		goto free_netdev;
454162306a36Sopenharmony_ci	}
454262306a36Sopenharmony_ci	spin_lock_init(&mac->hw_stats->stats_lock);
454362306a36Sopenharmony_ci	u64_stats_init(&mac->hw_stats->syncp);
454462306a36Sopenharmony_ci
454562306a36Sopenharmony_ci	if (mtk_is_netsys_v3_or_greater(eth))
454662306a36Sopenharmony_ci		mac->hw_stats->reg_offset = id * 0x80;
454762306a36Sopenharmony_ci	else
454862306a36Sopenharmony_ci		mac->hw_stats->reg_offset = id * 0x40;
454962306a36Sopenharmony_ci
455062306a36Sopenharmony_ci	/* phylink create */
455162306a36Sopenharmony_ci	err = of_get_phy_mode(np, &phy_mode);
455262306a36Sopenharmony_ci	if (err) {
455362306a36Sopenharmony_ci		dev_err(eth->dev, "incorrect phy-mode\n");
455462306a36Sopenharmony_ci		goto free_netdev;
455562306a36Sopenharmony_ci	}
455662306a36Sopenharmony_ci
455762306a36Sopenharmony_ci	/* mac config is not set */
455862306a36Sopenharmony_ci	mac->interface = PHY_INTERFACE_MODE_NA;
455962306a36Sopenharmony_ci	mac->speed = SPEED_UNKNOWN;
456062306a36Sopenharmony_ci
456162306a36Sopenharmony_ci	mac->phylink_config.dev = &eth->netdev[id]->dev;
456262306a36Sopenharmony_ci	mac->phylink_config.type = PHYLINK_NETDEV;
456362306a36Sopenharmony_ci	mac->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
456462306a36Sopenharmony_ci		MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD;
456562306a36Sopenharmony_ci
456662306a36Sopenharmony_ci	/* MT7623 gmac0 is now missing its speed-specific PLL configuration
456762306a36Sopenharmony_ci	 * in its .mac_config method (since state->speed is not valid there.
456862306a36Sopenharmony_ci	 * Disable support for MII, GMII and RGMII.
456962306a36Sopenharmony_ci	 */
457062306a36Sopenharmony_ci	if (!mac->hw->soc->disable_pll_modes || mac->id != 0) {
457162306a36Sopenharmony_ci		__set_bit(PHY_INTERFACE_MODE_MII,
457262306a36Sopenharmony_ci			  mac->phylink_config.supported_interfaces);
457362306a36Sopenharmony_ci		__set_bit(PHY_INTERFACE_MODE_GMII,
457462306a36Sopenharmony_ci			  mac->phylink_config.supported_interfaces);
457562306a36Sopenharmony_ci
457662306a36Sopenharmony_ci		if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_RGMII))
457762306a36Sopenharmony_ci			phy_interface_set_rgmii(mac->phylink_config.supported_interfaces);
457862306a36Sopenharmony_ci	}
457962306a36Sopenharmony_ci
458062306a36Sopenharmony_ci	if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_TRGMII) && !mac->id)
458162306a36Sopenharmony_ci		__set_bit(PHY_INTERFACE_MODE_TRGMII,
458262306a36Sopenharmony_ci			  mac->phylink_config.supported_interfaces);
458362306a36Sopenharmony_ci
458462306a36Sopenharmony_ci	/* TRGMII is not permitted on MT7621 if using DDR2 */
458562306a36Sopenharmony_ci	if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_GMAC1_TRGMII) &&
458662306a36Sopenharmony_ci	    MTK_HAS_CAPS(mac->hw->soc->caps, MTK_TRGMII_MT7621_CLK)) {
458762306a36Sopenharmony_ci		regmap_read(eth->ethsys, ETHSYS_SYSCFG, &val);
458862306a36Sopenharmony_ci		if (val & SYSCFG_DRAM_TYPE_DDR2)
458962306a36Sopenharmony_ci			__clear_bit(PHY_INTERFACE_MODE_TRGMII,
459062306a36Sopenharmony_ci				    mac->phylink_config.supported_interfaces);
459162306a36Sopenharmony_ci	}
459262306a36Sopenharmony_ci
459362306a36Sopenharmony_ci	if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_SGMII)) {
459462306a36Sopenharmony_ci		__set_bit(PHY_INTERFACE_MODE_SGMII,
459562306a36Sopenharmony_ci			  mac->phylink_config.supported_interfaces);
459662306a36Sopenharmony_ci		__set_bit(PHY_INTERFACE_MODE_1000BASEX,
459762306a36Sopenharmony_ci			  mac->phylink_config.supported_interfaces);
459862306a36Sopenharmony_ci		__set_bit(PHY_INTERFACE_MODE_2500BASEX,
459962306a36Sopenharmony_ci			  mac->phylink_config.supported_interfaces);
460062306a36Sopenharmony_ci	}
460162306a36Sopenharmony_ci
460262306a36Sopenharmony_ci	if (mtk_is_netsys_v3_or_greater(mac->hw) &&
460362306a36Sopenharmony_ci	    MTK_HAS_CAPS(mac->hw->soc->caps, MTK_ESW_BIT) &&
460462306a36Sopenharmony_ci	    id == MTK_GMAC1_ID) {
460562306a36Sopenharmony_ci		mac->phylink_config.mac_capabilities = MAC_ASYM_PAUSE |
460662306a36Sopenharmony_ci						       MAC_SYM_PAUSE |
460762306a36Sopenharmony_ci						       MAC_10000FD;
460862306a36Sopenharmony_ci		phy_interface_zero(mac->phylink_config.supported_interfaces);
460962306a36Sopenharmony_ci		__set_bit(PHY_INTERFACE_MODE_INTERNAL,
461062306a36Sopenharmony_ci			  mac->phylink_config.supported_interfaces);
461162306a36Sopenharmony_ci	}
461262306a36Sopenharmony_ci
461362306a36Sopenharmony_ci	phylink = phylink_create(&mac->phylink_config,
461462306a36Sopenharmony_ci				 of_fwnode_handle(mac->of_node),
461562306a36Sopenharmony_ci				 phy_mode, &mtk_phylink_ops);
461662306a36Sopenharmony_ci	if (IS_ERR(phylink)) {
461762306a36Sopenharmony_ci		err = PTR_ERR(phylink);
461862306a36Sopenharmony_ci		goto free_netdev;
461962306a36Sopenharmony_ci	}
462062306a36Sopenharmony_ci
462162306a36Sopenharmony_ci	mac->phylink = phylink;
462262306a36Sopenharmony_ci
462362306a36Sopenharmony_ci	SET_NETDEV_DEV(eth->netdev[id], eth->dev);
462462306a36Sopenharmony_ci	eth->netdev[id]->watchdog_timeo = 5 * HZ;
462562306a36Sopenharmony_ci	eth->netdev[id]->netdev_ops = &mtk_netdev_ops;
462662306a36Sopenharmony_ci	eth->netdev[id]->base_addr = (unsigned long)eth->base;
462762306a36Sopenharmony_ci
462862306a36Sopenharmony_ci	eth->netdev[id]->hw_features = eth->soc->hw_features;
462962306a36Sopenharmony_ci	if (eth->hwlro)
463062306a36Sopenharmony_ci		eth->netdev[id]->hw_features |= NETIF_F_LRO;
463162306a36Sopenharmony_ci
463262306a36Sopenharmony_ci	eth->netdev[id]->vlan_features = eth->soc->hw_features &
463362306a36Sopenharmony_ci		~NETIF_F_HW_VLAN_CTAG_TX;
463462306a36Sopenharmony_ci	eth->netdev[id]->features |= eth->soc->hw_features;
463562306a36Sopenharmony_ci	eth->netdev[id]->ethtool_ops = &mtk_ethtool_ops;
463662306a36Sopenharmony_ci
463762306a36Sopenharmony_ci	eth->netdev[id]->irq = eth->irq[0];
463862306a36Sopenharmony_ci	eth->netdev[id]->dev.of_node = np;
463962306a36Sopenharmony_ci
464062306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
464162306a36Sopenharmony_ci		eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH - MTK_RX_ETH_HLEN;
464262306a36Sopenharmony_ci	else
464362306a36Sopenharmony_ci		eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH_2K - MTK_RX_ETH_HLEN;
464462306a36Sopenharmony_ci
464562306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
464662306a36Sopenharmony_ci		mac->device_notifier.notifier_call = mtk_device_event;
464762306a36Sopenharmony_ci		register_netdevice_notifier(&mac->device_notifier);
464862306a36Sopenharmony_ci	}
464962306a36Sopenharmony_ci
465062306a36Sopenharmony_ci	if (mtk_page_pool_enabled(eth))
465162306a36Sopenharmony_ci		eth->netdev[id]->xdp_features = NETDEV_XDP_ACT_BASIC |
465262306a36Sopenharmony_ci						NETDEV_XDP_ACT_REDIRECT |
465362306a36Sopenharmony_ci						NETDEV_XDP_ACT_NDO_XMIT |
465462306a36Sopenharmony_ci						NETDEV_XDP_ACT_NDO_XMIT_SG;
465562306a36Sopenharmony_ci
465662306a36Sopenharmony_ci	return 0;
465762306a36Sopenharmony_ci
465862306a36Sopenharmony_cifree_netdev:
465962306a36Sopenharmony_ci	free_netdev(eth->netdev[id]);
466062306a36Sopenharmony_ci	return err;
466162306a36Sopenharmony_ci}
466262306a36Sopenharmony_ci
466362306a36Sopenharmony_civoid mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev)
466462306a36Sopenharmony_ci{
466562306a36Sopenharmony_ci	struct net_device *dev, *tmp;
466662306a36Sopenharmony_ci	LIST_HEAD(dev_list);
466762306a36Sopenharmony_ci	int i;
466862306a36Sopenharmony_ci
466962306a36Sopenharmony_ci	rtnl_lock();
467062306a36Sopenharmony_ci
467162306a36Sopenharmony_ci	for (i = 0; i < MTK_MAX_DEVS; i++) {
467262306a36Sopenharmony_ci		dev = eth->netdev[i];
467362306a36Sopenharmony_ci
467462306a36Sopenharmony_ci		if (!dev || !(dev->flags & IFF_UP))
467562306a36Sopenharmony_ci			continue;
467662306a36Sopenharmony_ci
467762306a36Sopenharmony_ci		list_add_tail(&dev->close_list, &dev_list);
467862306a36Sopenharmony_ci	}
467962306a36Sopenharmony_ci
468062306a36Sopenharmony_ci	dev_close_many(&dev_list, false);
468162306a36Sopenharmony_ci
468262306a36Sopenharmony_ci	eth->dma_dev = dma_dev;
468362306a36Sopenharmony_ci
468462306a36Sopenharmony_ci	list_for_each_entry_safe(dev, tmp, &dev_list, close_list) {
468562306a36Sopenharmony_ci		list_del_init(&dev->close_list);
468662306a36Sopenharmony_ci		dev_open(dev, NULL);
468762306a36Sopenharmony_ci	}
468862306a36Sopenharmony_ci
468962306a36Sopenharmony_ci	rtnl_unlock();
469062306a36Sopenharmony_ci}
469162306a36Sopenharmony_ci
469262306a36Sopenharmony_cistatic int mtk_sgmii_init(struct mtk_eth *eth)
469362306a36Sopenharmony_ci{
469462306a36Sopenharmony_ci	struct device_node *np;
469562306a36Sopenharmony_ci	struct regmap *regmap;
469662306a36Sopenharmony_ci	u32 flags;
469762306a36Sopenharmony_ci	int i;
469862306a36Sopenharmony_ci
469962306a36Sopenharmony_ci	for (i = 0; i < MTK_MAX_DEVS; i++) {
470062306a36Sopenharmony_ci		np = of_parse_phandle(eth->dev->of_node, "mediatek,sgmiisys", i);
470162306a36Sopenharmony_ci		if (!np)
470262306a36Sopenharmony_ci			break;
470362306a36Sopenharmony_ci
470462306a36Sopenharmony_ci		regmap = syscon_node_to_regmap(np);
470562306a36Sopenharmony_ci		flags = 0;
470662306a36Sopenharmony_ci		if (of_property_read_bool(np, "mediatek,pnswap"))
470762306a36Sopenharmony_ci			flags |= MTK_SGMII_FLAG_PN_SWAP;
470862306a36Sopenharmony_ci
470962306a36Sopenharmony_ci		of_node_put(np);
471062306a36Sopenharmony_ci
471162306a36Sopenharmony_ci		if (IS_ERR(regmap))
471262306a36Sopenharmony_ci			return PTR_ERR(regmap);
471362306a36Sopenharmony_ci
471462306a36Sopenharmony_ci		eth->sgmii_pcs[i] = mtk_pcs_lynxi_create(eth->dev, regmap,
471562306a36Sopenharmony_ci							 eth->soc->ana_rgc3,
471662306a36Sopenharmony_ci							 flags);
471762306a36Sopenharmony_ci	}
471862306a36Sopenharmony_ci
471962306a36Sopenharmony_ci	return 0;
472062306a36Sopenharmony_ci}
472162306a36Sopenharmony_ci
472262306a36Sopenharmony_cistatic int mtk_probe(struct platform_device *pdev)
472362306a36Sopenharmony_ci{
472462306a36Sopenharmony_ci	struct resource *res = NULL, *res_sram;
472562306a36Sopenharmony_ci	struct device_node *mac_np;
472662306a36Sopenharmony_ci	struct mtk_eth *eth;
472762306a36Sopenharmony_ci	int err, i;
472862306a36Sopenharmony_ci
472962306a36Sopenharmony_ci	eth = devm_kzalloc(&pdev->dev, sizeof(*eth), GFP_KERNEL);
473062306a36Sopenharmony_ci	if (!eth)
473162306a36Sopenharmony_ci		return -ENOMEM;
473262306a36Sopenharmony_ci
473362306a36Sopenharmony_ci	eth->soc = of_device_get_match_data(&pdev->dev);
473462306a36Sopenharmony_ci
473562306a36Sopenharmony_ci	eth->dev = &pdev->dev;
473662306a36Sopenharmony_ci	eth->dma_dev = &pdev->dev;
473762306a36Sopenharmony_ci	eth->base = devm_platform_ioremap_resource(pdev, 0);
473862306a36Sopenharmony_ci	if (IS_ERR(eth->base))
473962306a36Sopenharmony_ci		return PTR_ERR(eth->base);
474062306a36Sopenharmony_ci
474162306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
474262306a36Sopenharmony_ci		eth->ip_align = NET_IP_ALIGN;
474362306a36Sopenharmony_ci
474462306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_SRAM)) {
474562306a36Sopenharmony_ci		/* SRAM is actual memory and supports transparent access just like DRAM.
474662306a36Sopenharmony_ci		 * Hence we don't require __iomem being set and don't need to use accessor
474762306a36Sopenharmony_ci		 * functions to read from or write to SRAM.
474862306a36Sopenharmony_ci		 */
474962306a36Sopenharmony_ci		if (mtk_is_netsys_v3_or_greater(eth)) {
475062306a36Sopenharmony_ci			eth->sram_base = (void __force *)devm_platform_ioremap_resource(pdev, 1);
475162306a36Sopenharmony_ci			if (IS_ERR(eth->sram_base))
475262306a36Sopenharmony_ci				return PTR_ERR(eth->sram_base);
475362306a36Sopenharmony_ci		} else {
475462306a36Sopenharmony_ci			eth->sram_base = (void __force *)eth->base + MTK_ETH_SRAM_OFFSET;
475562306a36Sopenharmony_ci		}
475662306a36Sopenharmony_ci	}
475762306a36Sopenharmony_ci
475862306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_36BIT_DMA)) {
475962306a36Sopenharmony_ci		err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(36));
476062306a36Sopenharmony_ci		if (!err)
476162306a36Sopenharmony_ci			err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
476262306a36Sopenharmony_ci
476362306a36Sopenharmony_ci		if (err) {
476462306a36Sopenharmony_ci			dev_err(&pdev->dev, "Wrong DMA config\n");
476562306a36Sopenharmony_ci			return -EINVAL;
476662306a36Sopenharmony_ci		}
476762306a36Sopenharmony_ci	}
476862306a36Sopenharmony_ci
476962306a36Sopenharmony_ci	spin_lock_init(&eth->page_lock);
477062306a36Sopenharmony_ci	spin_lock_init(&eth->tx_irq_lock);
477162306a36Sopenharmony_ci	spin_lock_init(&eth->rx_irq_lock);
477262306a36Sopenharmony_ci	spin_lock_init(&eth->dim_lock);
477362306a36Sopenharmony_ci
477462306a36Sopenharmony_ci	eth->rx_dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE;
477562306a36Sopenharmony_ci	INIT_WORK(&eth->rx_dim.work, mtk_dim_rx);
477662306a36Sopenharmony_ci	INIT_DELAYED_WORK(&eth->reset.monitor_work, mtk_hw_reset_monitor_work);
477762306a36Sopenharmony_ci
477862306a36Sopenharmony_ci	eth->tx_dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE;
477962306a36Sopenharmony_ci	INIT_WORK(&eth->tx_dim.work, mtk_dim_tx);
478062306a36Sopenharmony_ci
478162306a36Sopenharmony_ci	if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) {
478262306a36Sopenharmony_ci		eth->ethsys = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
478362306a36Sopenharmony_ci							      "mediatek,ethsys");
478462306a36Sopenharmony_ci		if (IS_ERR(eth->ethsys)) {
478562306a36Sopenharmony_ci			dev_err(&pdev->dev, "no ethsys regmap found\n");
478662306a36Sopenharmony_ci			return PTR_ERR(eth->ethsys);
478762306a36Sopenharmony_ci		}
478862306a36Sopenharmony_ci	}
478962306a36Sopenharmony_ci
479062306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_INFRA)) {
479162306a36Sopenharmony_ci		eth->infra = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
479262306a36Sopenharmony_ci							     "mediatek,infracfg");
479362306a36Sopenharmony_ci		if (IS_ERR(eth->infra)) {
479462306a36Sopenharmony_ci			dev_err(&pdev->dev, "no infracfg regmap found\n");
479562306a36Sopenharmony_ci			return PTR_ERR(eth->infra);
479662306a36Sopenharmony_ci		}
479762306a36Sopenharmony_ci	}
479862306a36Sopenharmony_ci
479962306a36Sopenharmony_ci	if (of_dma_is_coherent(pdev->dev.of_node)) {
480062306a36Sopenharmony_ci		struct regmap *cci;
480162306a36Sopenharmony_ci
480262306a36Sopenharmony_ci		cci = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
480362306a36Sopenharmony_ci						      "cci-control-port");
480462306a36Sopenharmony_ci		/* enable CPU/bus coherency */
480562306a36Sopenharmony_ci		if (!IS_ERR(cci))
480662306a36Sopenharmony_ci			regmap_write(cci, 0, 3);
480762306a36Sopenharmony_ci	}
480862306a36Sopenharmony_ci
480962306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) {
481062306a36Sopenharmony_ci		err = mtk_sgmii_init(eth);
481162306a36Sopenharmony_ci
481262306a36Sopenharmony_ci		if (err)
481362306a36Sopenharmony_ci			return err;
481462306a36Sopenharmony_ci	}
481562306a36Sopenharmony_ci
481662306a36Sopenharmony_ci	if (eth->soc->required_pctl) {
481762306a36Sopenharmony_ci		eth->pctl = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
481862306a36Sopenharmony_ci							    "mediatek,pctl");
481962306a36Sopenharmony_ci		if (IS_ERR(eth->pctl)) {
482062306a36Sopenharmony_ci			dev_err(&pdev->dev, "no pctl regmap found\n");
482162306a36Sopenharmony_ci			err = PTR_ERR(eth->pctl);
482262306a36Sopenharmony_ci			goto err_destroy_sgmii;
482362306a36Sopenharmony_ci		}
482462306a36Sopenharmony_ci	}
482562306a36Sopenharmony_ci
482662306a36Sopenharmony_ci	if (mtk_is_netsys_v2_or_greater(eth)) {
482762306a36Sopenharmony_ci		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
482862306a36Sopenharmony_ci		if (!res) {
482962306a36Sopenharmony_ci			err = -EINVAL;
483062306a36Sopenharmony_ci			goto err_destroy_sgmii;
483162306a36Sopenharmony_ci		}
483262306a36Sopenharmony_ci		if (MTK_HAS_CAPS(eth->soc->caps, MTK_SRAM)) {
483362306a36Sopenharmony_ci			if (mtk_is_netsys_v3_or_greater(eth)) {
483462306a36Sopenharmony_ci				res_sram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
483562306a36Sopenharmony_ci				if (!res_sram) {
483662306a36Sopenharmony_ci					err = -EINVAL;
483762306a36Sopenharmony_ci					goto err_destroy_sgmii;
483862306a36Sopenharmony_ci				}
483962306a36Sopenharmony_ci				eth->phy_scratch_ring = res_sram->start;
484062306a36Sopenharmony_ci			} else {
484162306a36Sopenharmony_ci				eth->phy_scratch_ring = res->start + MTK_ETH_SRAM_OFFSET;
484262306a36Sopenharmony_ci			}
484362306a36Sopenharmony_ci		}
484462306a36Sopenharmony_ci	}
484562306a36Sopenharmony_ci
484662306a36Sopenharmony_ci	if (eth->soc->offload_version) {
484762306a36Sopenharmony_ci		for (i = 0;; i++) {
484862306a36Sopenharmony_ci			struct device_node *np;
484962306a36Sopenharmony_ci			phys_addr_t wdma_phy;
485062306a36Sopenharmony_ci			u32 wdma_base;
485162306a36Sopenharmony_ci
485262306a36Sopenharmony_ci			if (i >= ARRAY_SIZE(eth->soc->reg_map->wdma_base))
485362306a36Sopenharmony_ci				break;
485462306a36Sopenharmony_ci
485562306a36Sopenharmony_ci			np = of_parse_phandle(pdev->dev.of_node,
485662306a36Sopenharmony_ci					      "mediatek,wed", i);
485762306a36Sopenharmony_ci			if (!np)
485862306a36Sopenharmony_ci				break;
485962306a36Sopenharmony_ci
486062306a36Sopenharmony_ci			wdma_base = eth->soc->reg_map->wdma_base[i];
486162306a36Sopenharmony_ci			wdma_phy = res ? res->start + wdma_base : 0;
486262306a36Sopenharmony_ci			mtk_wed_add_hw(np, eth, eth->base + wdma_base,
486362306a36Sopenharmony_ci				       wdma_phy, i);
486462306a36Sopenharmony_ci		}
486562306a36Sopenharmony_ci	}
486662306a36Sopenharmony_ci
486762306a36Sopenharmony_ci	for (i = 0; i < 3; i++) {
486862306a36Sopenharmony_ci		if (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_INT) && i > 0)
486962306a36Sopenharmony_ci			eth->irq[i] = eth->irq[0];
487062306a36Sopenharmony_ci		else
487162306a36Sopenharmony_ci			eth->irq[i] = platform_get_irq(pdev, i);
487262306a36Sopenharmony_ci		if (eth->irq[i] < 0) {
487362306a36Sopenharmony_ci			dev_err(&pdev->dev, "no IRQ%d resource found\n", i);
487462306a36Sopenharmony_ci			err = -ENXIO;
487562306a36Sopenharmony_ci			goto err_wed_exit;
487662306a36Sopenharmony_ci		}
487762306a36Sopenharmony_ci	}
487862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(eth->clks); i++) {
487962306a36Sopenharmony_ci		eth->clks[i] = devm_clk_get(eth->dev,
488062306a36Sopenharmony_ci					    mtk_clks_source_name[i]);
488162306a36Sopenharmony_ci		if (IS_ERR(eth->clks[i])) {
488262306a36Sopenharmony_ci			if (PTR_ERR(eth->clks[i]) == -EPROBE_DEFER) {
488362306a36Sopenharmony_ci				err = -EPROBE_DEFER;
488462306a36Sopenharmony_ci				goto err_wed_exit;
488562306a36Sopenharmony_ci			}
488662306a36Sopenharmony_ci			if (eth->soc->required_clks & BIT(i)) {
488762306a36Sopenharmony_ci				dev_err(&pdev->dev, "clock %s not found\n",
488862306a36Sopenharmony_ci					mtk_clks_source_name[i]);
488962306a36Sopenharmony_ci				err = -EINVAL;
489062306a36Sopenharmony_ci				goto err_wed_exit;
489162306a36Sopenharmony_ci			}
489262306a36Sopenharmony_ci			eth->clks[i] = NULL;
489362306a36Sopenharmony_ci		}
489462306a36Sopenharmony_ci	}
489562306a36Sopenharmony_ci
489662306a36Sopenharmony_ci	eth->msg_enable = netif_msg_init(mtk_msg_level, MTK_DEFAULT_MSG_ENABLE);
489762306a36Sopenharmony_ci	INIT_WORK(&eth->pending_work, mtk_pending_work);
489862306a36Sopenharmony_ci
489962306a36Sopenharmony_ci	err = mtk_hw_init(eth, false);
490062306a36Sopenharmony_ci	if (err)
490162306a36Sopenharmony_ci		goto err_wed_exit;
490262306a36Sopenharmony_ci
490362306a36Sopenharmony_ci	eth->hwlro = MTK_HAS_CAPS(eth->soc->caps, MTK_HWLRO);
490462306a36Sopenharmony_ci
490562306a36Sopenharmony_ci	for_each_child_of_node(pdev->dev.of_node, mac_np) {
490662306a36Sopenharmony_ci		if (!of_device_is_compatible(mac_np,
490762306a36Sopenharmony_ci					     "mediatek,eth-mac"))
490862306a36Sopenharmony_ci			continue;
490962306a36Sopenharmony_ci
491062306a36Sopenharmony_ci		if (!of_device_is_available(mac_np))
491162306a36Sopenharmony_ci			continue;
491262306a36Sopenharmony_ci
491362306a36Sopenharmony_ci		err = mtk_add_mac(eth, mac_np);
491462306a36Sopenharmony_ci		if (err) {
491562306a36Sopenharmony_ci			of_node_put(mac_np);
491662306a36Sopenharmony_ci			goto err_deinit_hw;
491762306a36Sopenharmony_ci		}
491862306a36Sopenharmony_ci	}
491962306a36Sopenharmony_ci
492062306a36Sopenharmony_ci	if (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_INT)) {
492162306a36Sopenharmony_ci		err = devm_request_irq(eth->dev, eth->irq[0],
492262306a36Sopenharmony_ci				       mtk_handle_irq, 0,
492362306a36Sopenharmony_ci				       dev_name(eth->dev), eth);
492462306a36Sopenharmony_ci	} else {
492562306a36Sopenharmony_ci		err = devm_request_irq(eth->dev, eth->irq[1],
492662306a36Sopenharmony_ci				       mtk_handle_irq_tx, 0,
492762306a36Sopenharmony_ci				       dev_name(eth->dev), eth);
492862306a36Sopenharmony_ci		if (err)
492962306a36Sopenharmony_ci			goto err_free_dev;
493062306a36Sopenharmony_ci
493162306a36Sopenharmony_ci		err = devm_request_irq(eth->dev, eth->irq[2],
493262306a36Sopenharmony_ci				       mtk_handle_irq_rx, 0,
493362306a36Sopenharmony_ci				       dev_name(eth->dev), eth);
493462306a36Sopenharmony_ci	}
493562306a36Sopenharmony_ci	if (err)
493662306a36Sopenharmony_ci		goto err_free_dev;
493762306a36Sopenharmony_ci
493862306a36Sopenharmony_ci	/* No MT7628/88 support yet */
493962306a36Sopenharmony_ci	if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) {
494062306a36Sopenharmony_ci		err = mtk_mdio_init(eth);
494162306a36Sopenharmony_ci		if (err)
494262306a36Sopenharmony_ci			goto err_free_dev;
494362306a36Sopenharmony_ci	}
494462306a36Sopenharmony_ci
494562306a36Sopenharmony_ci	if (eth->soc->offload_version) {
494662306a36Sopenharmony_ci		u32 num_ppe = mtk_is_netsys_v2_or_greater(eth) ? 2 : 1;
494762306a36Sopenharmony_ci
494862306a36Sopenharmony_ci		num_ppe = min_t(u32, ARRAY_SIZE(eth->ppe), num_ppe);
494962306a36Sopenharmony_ci		for (i = 0; i < num_ppe; i++) {
495062306a36Sopenharmony_ci			u32 ppe_addr = eth->soc->reg_map->ppe_base + i * 0x400;
495162306a36Sopenharmony_ci
495262306a36Sopenharmony_ci			eth->ppe[i] = mtk_ppe_init(eth, eth->base + ppe_addr, i);
495362306a36Sopenharmony_ci
495462306a36Sopenharmony_ci			if (!eth->ppe[i]) {
495562306a36Sopenharmony_ci				err = -ENOMEM;
495662306a36Sopenharmony_ci				goto err_deinit_ppe;
495762306a36Sopenharmony_ci			}
495862306a36Sopenharmony_ci		}
495962306a36Sopenharmony_ci
496062306a36Sopenharmony_ci		err = mtk_eth_offload_init(eth);
496162306a36Sopenharmony_ci		if (err)
496262306a36Sopenharmony_ci			goto err_deinit_ppe;
496362306a36Sopenharmony_ci	}
496462306a36Sopenharmony_ci
496562306a36Sopenharmony_ci	for (i = 0; i < MTK_MAX_DEVS; i++) {
496662306a36Sopenharmony_ci		if (!eth->netdev[i])
496762306a36Sopenharmony_ci			continue;
496862306a36Sopenharmony_ci
496962306a36Sopenharmony_ci		err = register_netdev(eth->netdev[i]);
497062306a36Sopenharmony_ci		if (err) {
497162306a36Sopenharmony_ci			dev_err(eth->dev, "error bringing up device\n");
497262306a36Sopenharmony_ci			goto err_deinit_ppe;
497362306a36Sopenharmony_ci		} else
497462306a36Sopenharmony_ci			netif_info(eth, probe, eth->netdev[i],
497562306a36Sopenharmony_ci				   "mediatek frame engine at 0x%08lx, irq %d\n",
497662306a36Sopenharmony_ci				   eth->netdev[i]->base_addr, eth->irq[0]);
497762306a36Sopenharmony_ci	}
497862306a36Sopenharmony_ci
497962306a36Sopenharmony_ci	/* we run 2 devices on the same DMA ring so we need a dummy device
498062306a36Sopenharmony_ci	 * for NAPI to work
498162306a36Sopenharmony_ci	 */
498262306a36Sopenharmony_ci	init_dummy_netdev(&eth->dummy_dev);
498362306a36Sopenharmony_ci	netif_napi_add(&eth->dummy_dev, &eth->tx_napi, mtk_napi_tx);
498462306a36Sopenharmony_ci	netif_napi_add(&eth->dummy_dev, &eth->rx_napi, mtk_napi_rx);
498562306a36Sopenharmony_ci
498662306a36Sopenharmony_ci	platform_set_drvdata(pdev, eth);
498762306a36Sopenharmony_ci	schedule_delayed_work(&eth->reset.monitor_work,
498862306a36Sopenharmony_ci			      MTK_DMA_MONITOR_TIMEOUT);
498962306a36Sopenharmony_ci
499062306a36Sopenharmony_ci	return 0;
499162306a36Sopenharmony_ci
499262306a36Sopenharmony_cierr_deinit_ppe:
499362306a36Sopenharmony_ci	mtk_ppe_deinit(eth);
499462306a36Sopenharmony_ci	mtk_mdio_cleanup(eth);
499562306a36Sopenharmony_cierr_free_dev:
499662306a36Sopenharmony_ci	mtk_free_dev(eth);
499762306a36Sopenharmony_cierr_deinit_hw:
499862306a36Sopenharmony_ci	mtk_hw_deinit(eth);
499962306a36Sopenharmony_cierr_wed_exit:
500062306a36Sopenharmony_ci	mtk_wed_exit();
500162306a36Sopenharmony_cierr_destroy_sgmii:
500262306a36Sopenharmony_ci	mtk_sgmii_destroy(eth);
500362306a36Sopenharmony_ci
500462306a36Sopenharmony_ci	return err;
500562306a36Sopenharmony_ci}
500662306a36Sopenharmony_ci
500762306a36Sopenharmony_cistatic int mtk_remove(struct platform_device *pdev)
500862306a36Sopenharmony_ci{
500962306a36Sopenharmony_ci	struct mtk_eth *eth = platform_get_drvdata(pdev);
501062306a36Sopenharmony_ci	struct mtk_mac *mac;
501162306a36Sopenharmony_ci	int i;
501262306a36Sopenharmony_ci
501362306a36Sopenharmony_ci	/* stop all devices to make sure that dma is properly shut down */
501462306a36Sopenharmony_ci	for (i = 0; i < MTK_MAX_DEVS; i++) {
501562306a36Sopenharmony_ci		if (!eth->netdev[i])
501662306a36Sopenharmony_ci			continue;
501762306a36Sopenharmony_ci		mtk_stop(eth->netdev[i]);
501862306a36Sopenharmony_ci		mac = netdev_priv(eth->netdev[i]);
501962306a36Sopenharmony_ci		phylink_disconnect_phy(mac->phylink);
502062306a36Sopenharmony_ci	}
502162306a36Sopenharmony_ci
502262306a36Sopenharmony_ci	mtk_wed_exit();
502362306a36Sopenharmony_ci	mtk_hw_deinit(eth);
502462306a36Sopenharmony_ci
502562306a36Sopenharmony_ci	netif_napi_del(&eth->tx_napi);
502662306a36Sopenharmony_ci	netif_napi_del(&eth->rx_napi);
502762306a36Sopenharmony_ci	mtk_cleanup(eth);
502862306a36Sopenharmony_ci	mtk_mdio_cleanup(eth);
502962306a36Sopenharmony_ci
503062306a36Sopenharmony_ci	return 0;
503162306a36Sopenharmony_ci}
503262306a36Sopenharmony_ci
503362306a36Sopenharmony_cistatic const struct mtk_soc_data mt2701_data = {
503462306a36Sopenharmony_ci	.reg_map = &mtk_reg_map,
503562306a36Sopenharmony_ci	.caps = MT7623_CAPS | MTK_HWLRO,
503662306a36Sopenharmony_ci	.hw_features = MTK_HW_FEATURES,
503762306a36Sopenharmony_ci	.required_clks = MT7623_CLKS_BITMAP,
503862306a36Sopenharmony_ci	.required_pctl = true,
503962306a36Sopenharmony_ci	.version = 1,
504062306a36Sopenharmony_ci	.txrx = {
504162306a36Sopenharmony_ci		.txd_size = sizeof(struct mtk_tx_dma),
504262306a36Sopenharmony_ci		.rxd_size = sizeof(struct mtk_rx_dma),
504362306a36Sopenharmony_ci		.rx_irq_done_mask = MTK_RX_DONE_INT,
504462306a36Sopenharmony_ci		.rx_dma_l4_valid = RX_DMA_L4_VALID,
504562306a36Sopenharmony_ci		.dma_max_len = MTK_TX_DMA_BUF_LEN,
504662306a36Sopenharmony_ci		.dma_len_offset = 16,
504762306a36Sopenharmony_ci	},
504862306a36Sopenharmony_ci};
504962306a36Sopenharmony_ci
505062306a36Sopenharmony_cistatic const struct mtk_soc_data mt7621_data = {
505162306a36Sopenharmony_ci	.reg_map = &mtk_reg_map,
505262306a36Sopenharmony_ci	.caps = MT7621_CAPS,
505362306a36Sopenharmony_ci	.hw_features = MTK_HW_FEATURES,
505462306a36Sopenharmony_ci	.required_clks = MT7621_CLKS_BITMAP,
505562306a36Sopenharmony_ci	.required_pctl = false,
505662306a36Sopenharmony_ci	.version = 1,
505762306a36Sopenharmony_ci	.offload_version = 1,
505862306a36Sopenharmony_ci	.hash_offset = 2,
505962306a36Sopenharmony_ci	.foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
506062306a36Sopenharmony_ci	.txrx = {
506162306a36Sopenharmony_ci		.txd_size = sizeof(struct mtk_tx_dma),
506262306a36Sopenharmony_ci		.rxd_size = sizeof(struct mtk_rx_dma),
506362306a36Sopenharmony_ci		.rx_irq_done_mask = MTK_RX_DONE_INT,
506462306a36Sopenharmony_ci		.rx_dma_l4_valid = RX_DMA_L4_VALID,
506562306a36Sopenharmony_ci		.dma_max_len = MTK_TX_DMA_BUF_LEN,
506662306a36Sopenharmony_ci		.dma_len_offset = 16,
506762306a36Sopenharmony_ci	},
506862306a36Sopenharmony_ci};
506962306a36Sopenharmony_ci
507062306a36Sopenharmony_cistatic const struct mtk_soc_data mt7622_data = {
507162306a36Sopenharmony_ci	.reg_map = &mtk_reg_map,
507262306a36Sopenharmony_ci	.ana_rgc3 = 0x2028,
507362306a36Sopenharmony_ci	.caps = MT7622_CAPS | MTK_HWLRO,
507462306a36Sopenharmony_ci	.hw_features = MTK_HW_FEATURES,
507562306a36Sopenharmony_ci	.required_clks = MT7622_CLKS_BITMAP,
507662306a36Sopenharmony_ci	.required_pctl = false,
507762306a36Sopenharmony_ci	.version = 1,
507862306a36Sopenharmony_ci	.offload_version = 2,
507962306a36Sopenharmony_ci	.hash_offset = 2,
508062306a36Sopenharmony_ci	.has_accounting = true,
508162306a36Sopenharmony_ci	.foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
508262306a36Sopenharmony_ci	.txrx = {
508362306a36Sopenharmony_ci		.txd_size = sizeof(struct mtk_tx_dma),
508462306a36Sopenharmony_ci		.rxd_size = sizeof(struct mtk_rx_dma),
508562306a36Sopenharmony_ci		.rx_irq_done_mask = MTK_RX_DONE_INT,
508662306a36Sopenharmony_ci		.rx_dma_l4_valid = RX_DMA_L4_VALID,
508762306a36Sopenharmony_ci		.dma_max_len = MTK_TX_DMA_BUF_LEN,
508862306a36Sopenharmony_ci		.dma_len_offset = 16,
508962306a36Sopenharmony_ci	},
509062306a36Sopenharmony_ci};
509162306a36Sopenharmony_ci
509262306a36Sopenharmony_cistatic const struct mtk_soc_data mt7623_data = {
509362306a36Sopenharmony_ci	.reg_map = &mtk_reg_map,
509462306a36Sopenharmony_ci	.caps = MT7623_CAPS | MTK_HWLRO,
509562306a36Sopenharmony_ci	.hw_features = MTK_HW_FEATURES,
509662306a36Sopenharmony_ci	.required_clks = MT7623_CLKS_BITMAP,
509762306a36Sopenharmony_ci	.required_pctl = true,
509862306a36Sopenharmony_ci	.version = 1,
509962306a36Sopenharmony_ci	.offload_version = 1,
510062306a36Sopenharmony_ci	.hash_offset = 2,
510162306a36Sopenharmony_ci	.foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
510262306a36Sopenharmony_ci	.disable_pll_modes = true,
510362306a36Sopenharmony_ci	.txrx = {
510462306a36Sopenharmony_ci		.txd_size = sizeof(struct mtk_tx_dma),
510562306a36Sopenharmony_ci		.rxd_size = sizeof(struct mtk_rx_dma),
510662306a36Sopenharmony_ci		.rx_irq_done_mask = MTK_RX_DONE_INT,
510762306a36Sopenharmony_ci		.rx_dma_l4_valid = RX_DMA_L4_VALID,
510862306a36Sopenharmony_ci		.dma_max_len = MTK_TX_DMA_BUF_LEN,
510962306a36Sopenharmony_ci		.dma_len_offset = 16,
511062306a36Sopenharmony_ci	},
511162306a36Sopenharmony_ci};
511262306a36Sopenharmony_ci
511362306a36Sopenharmony_cistatic const struct mtk_soc_data mt7629_data = {
511462306a36Sopenharmony_ci	.reg_map = &mtk_reg_map,
511562306a36Sopenharmony_ci	.ana_rgc3 = 0x128,
511662306a36Sopenharmony_ci	.caps = MT7629_CAPS | MTK_HWLRO,
511762306a36Sopenharmony_ci	.hw_features = MTK_HW_FEATURES,
511862306a36Sopenharmony_ci	.required_clks = MT7629_CLKS_BITMAP,
511962306a36Sopenharmony_ci	.required_pctl = false,
512062306a36Sopenharmony_ci	.has_accounting = true,
512162306a36Sopenharmony_ci	.version = 1,
512262306a36Sopenharmony_ci	.txrx = {
512362306a36Sopenharmony_ci		.txd_size = sizeof(struct mtk_tx_dma),
512462306a36Sopenharmony_ci		.rxd_size = sizeof(struct mtk_rx_dma),
512562306a36Sopenharmony_ci		.rx_irq_done_mask = MTK_RX_DONE_INT,
512662306a36Sopenharmony_ci		.rx_dma_l4_valid = RX_DMA_L4_VALID,
512762306a36Sopenharmony_ci		.dma_max_len = MTK_TX_DMA_BUF_LEN,
512862306a36Sopenharmony_ci		.dma_len_offset = 16,
512962306a36Sopenharmony_ci	},
513062306a36Sopenharmony_ci};
513162306a36Sopenharmony_ci
513262306a36Sopenharmony_cistatic const struct mtk_soc_data mt7981_data = {
513362306a36Sopenharmony_ci	.reg_map = &mt7986_reg_map,
513462306a36Sopenharmony_ci	.ana_rgc3 = 0x128,
513562306a36Sopenharmony_ci	.caps = MT7981_CAPS,
513662306a36Sopenharmony_ci	.hw_features = MTK_HW_FEATURES,
513762306a36Sopenharmony_ci	.required_clks = MT7981_CLKS_BITMAP,
513862306a36Sopenharmony_ci	.required_pctl = false,
513962306a36Sopenharmony_ci	.version = 2,
514062306a36Sopenharmony_ci	.offload_version = 2,
514162306a36Sopenharmony_ci	.hash_offset = 4,
514262306a36Sopenharmony_ci	.has_accounting = true,
514362306a36Sopenharmony_ci	.foe_entry_size = MTK_FOE_ENTRY_V2_SIZE,
514462306a36Sopenharmony_ci	.txrx = {
514562306a36Sopenharmony_ci		.txd_size = sizeof(struct mtk_tx_dma_v2),
514662306a36Sopenharmony_ci		.rxd_size = sizeof(struct mtk_rx_dma_v2),
514762306a36Sopenharmony_ci		.rx_irq_done_mask = MTK_RX_DONE_INT_V2,
514862306a36Sopenharmony_ci		.rx_dma_l4_valid = RX_DMA_L4_VALID_V2,
514962306a36Sopenharmony_ci		.dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
515062306a36Sopenharmony_ci		.dma_len_offset = 8,
515162306a36Sopenharmony_ci	},
515262306a36Sopenharmony_ci};
515362306a36Sopenharmony_ci
515462306a36Sopenharmony_cistatic const struct mtk_soc_data mt7986_data = {
515562306a36Sopenharmony_ci	.reg_map = &mt7986_reg_map,
515662306a36Sopenharmony_ci	.ana_rgc3 = 0x128,
515762306a36Sopenharmony_ci	.caps = MT7986_CAPS,
515862306a36Sopenharmony_ci	.hw_features = MTK_HW_FEATURES,
515962306a36Sopenharmony_ci	.required_clks = MT7986_CLKS_BITMAP,
516062306a36Sopenharmony_ci	.required_pctl = false,
516162306a36Sopenharmony_ci	.version = 2,
516262306a36Sopenharmony_ci	.offload_version = 2,
516362306a36Sopenharmony_ci	.hash_offset = 4,
516462306a36Sopenharmony_ci	.has_accounting = true,
516562306a36Sopenharmony_ci	.foe_entry_size = MTK_FOE_ENTRY_V2_SIZE,
516662306a36Sopenharmony_ci	.txrx = {
516762306a36Sopenharmony_ci		.txd_size = sizeof(struct mtk_tx_dma_v2),
516862306a36Sopenharmony_ci		.rxd_size = sizeof(struct mtk_rx_dma_v2),
516962306a36Sopenharmony_ci		.rx_irq_done_mask = MTK_RX_DONE_INT_V2,
517062306a36Sopenharmony_ci		.rx_dma_l4_valid = RX_DMA_L4_VALID_V2,
517162306a36Sopenharmony_ci		.dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
517262306a36Sopenharmony_ci		.dma_len_offset = 8,
517362306a36Sopenharmony_ci	},
517462306a36Sopenharmony_ci};
517562306a36Sopenharmony_ci
517662306a36Sopenharmony_cistatic const struct mtk_soc_data mt7988_data = {
517762306a36Sopenharmony_ci	.reg_map = &mt7988_reg_map,
517862306a36Sopenharmony_ci	.ana_rgc3 = 0x128,
517962306a36Sopenharmony_ci	.caps = MT7988_CAPS,
518062306a36Sopenharmony_ci	.hw_features = MTK_HW_FEATURES,
518162306a36Sopenharmony_ci	.required_clks = MT7988_CLKS_BITMAP,
518262306a36Sopenharmony_ci	.required_pctl = false,
518362306a36Sopenharmony_ci	.version = 3,
518462306a36Sopenharmony_ci	.offload_version = 2,
518562306a36Sopenharmony_ci	.hash_offset = 4,
518662306a36Sopenharmony_ci	.has_accounting = true,
518762306a36Sopenharmony_ci	.foe_entry_size = MTK_FOE_ENTRY_V3_SIZE,
518862306a36Sopenharmony_ci	.txrx = {
518962306a36Sopenharmony_ci		.txd_size = sizeof(struct mtk_tx_dma_v2),
519062306a36Sopenharmony_ci		.rxd_size = sizeof(struct mtk_rx_dma_v2),
519162306a36Sopenharmony_ci		.rx_irq_done_mask = MTK_RX_DONE_INT_V2,
519262306a36Sopenharmony_ci		.rx_dma_l4_valid = RX_DMA_L4_VALID_V2,
519362306a36Sopenharmony_ci		.dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
519462306a36Sopenharmony_ci		.dma_len_offset = 8,
519562306a36Sopenharmony_ci	},
519662306a36Sopenharmony_ci};
519762306a36Sopenharmony_ci
519862306a36Sopenharmony_cistatic const struct mtk_soc_data rt5350_data = {
519962306a36Sopenharmony_ci	.reg_map = &mt7628_reg_map,
520062306a36Sopenharmony_ci	.caps = MT7628_CAPS,
520162306a36Sopenharmony_ci	.hw_features = MTK_HW_FEATURES_MT7628,
520262306a36Sopenharmony_ci	.required_clks = MT7628_CLKS_BITMAP,
520362306a36Sopenharmony_ci	.required_pctl = false,
520462306a36Sopenharmony_ci	.version = 1,
520562306a36Sopenharmony_ci	.txrx = {
520662306a36Sopenharmony_ci		.txd_size = sizeof(struct mtk_tx_dma),
520762306a36Sopenharmony_ci		.rxd_size = sizeof(struct mtk_rx_dma),
520862306a36Sopenharmony_ci		.rx_irq_done_mask = MTK_RX_DONE_INT,
520962306a36Sopenharmony_ci		.rx_dma_l4_valid = RX_DMA_L4_VALID_PDMA,
521062306a36Sopenharmony_ci		.dma_max_len = MTK_TX_DMA_BUF_LEN,
521162306a36Sopenharmony_ci		.dma_len_offset = 16,
521262306a36Sopenharmony_ci	},
521362306a36Sopenharmony_ci};
521462306a36Sopenharmony_ci
521562306a36Sopenharmony_ciconst struct of_device_id of_mtk_match[] = {
521662306a36Sopenharmony_ci	{ .compatible = "mediatek,mt2701-eth", .data = &mt2701_data },
521762306a36Sopenharmony_ci	{ .compatible = "mediatek,mt7621-eth", .data = &mt7621_data },
521862306a36Sopenharmony_ci	{ .compatible = "mediatek,mt7622-eth", .data = &mt7622_data },
521962306a36Sopenharmony_ci	{ .compatible = "mediatek,mt7623-eth", .data = &mt7623_data },
522062306a36Sopenharmony_ci	{ .compatible = "mediatek,mt7629-eth", .data = &mt7629_data },
522162306a36Sopenharmony_ci	{ .compatible = "mediatek,mt7981-eth", .data = &mt7981_data },
522262306a36Sopenharmony_ci	{ .compatible = "mediatek,mt7986-eth", .data = &mt7986_data },
522362306a36Sopenharmony_ci	{ .compatible = "mediatek,mt7988-eth", .data = &mt7988_data },
522462306a36Sopenharmony_ci	{ .compatible = "ralink,rt5350-eth", .data = &rt5350_data },
522562306a36Sopenharmony_ci	{},
522662306a36Sopenharmony_ci};
522762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_mtk_match);
522862306a36Sopenharmony_ci
522962306a36Sopenharmony_cistatic struct platform_driver mtk_driver = {
523062306a36Sopenharmony_ci	.probe = mtk_probe,
523162306a36Sopenharmony_ci	.remove = mtk_remove,
523262306a36Sopenharmony_ci	.driver = {
523362306a36Sopenharmony_ci		.name = "mtk_soc_eth",
523462306a36Sopenharmony_ci		.of_match_table = of_mtk_match,
523562306a36Sopenharmony_ci	},
523662306a36Sopenharmony_ci};
523762306a36Sopenharmony_ci
523862306a36Sopenharmony_cimodule_platform_driver(mtk_driver);
523962306a36Sopenharmony_ci
524062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
524162306a36Sopenharmony_ciMODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
524262306a36Sopenharmony_ciMODULE_DESCRIPTION("Ethernet driver for MediaTek SoC");
5243