162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Fast Ethernet Controller (FEC) driver for Motorola MPC8xx.
462306a36Sopenharmony_ci * Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Right now, I am very wasteful with the buffers.  I allocate memory
762306a36Sopenharmony_ci * pages and then divide them into 2K frame buffers.  This way I know I
862306a36Sopenharmony_ci * have buffers large enough to hold one frame within one buffer descriptor.
962306a36Sopenharmony_ci * Once I get this working, I will use 64 or 128 byte CPM buffers, which
1062306a36Sopenharmony_ci * will be much more memory efficient and will easily handle lots of
1162306a36Sopenharmony_ci * small packets.
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * Much better multiple PHY support by Magnus Damm.
1462306a36Sopenharmony_ci * Copyright (c) 2000 Ericsson Radio Systems AB.
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * Support for FEC controller of ColdFire processors.
1762306a36Sopenharmony_ci * Copyright (c) 2001-2005 Greg Ungerer (gerg@snapgear.com)
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be)
2062306a36Sopenharmony_ci * Copyright (c) 2004-2006 Macq Electronique SA.
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <linux/module.h>
2662306a36Sopenharmony_ci#include <linux/kernel.h>
2762306a36Sopenharmony_ci#include <linux/string.h>
2862306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2962306a36Sopenharmony_ci#include <linux/ptrace.h>
3062306a36Sopenharmony_ci#include <linux/errno.h>
3162306a36Sopenharmony_ci#include <linux/ioport.h>
3262306a36Sopenharmony_ci#include <linux/slab.h>
3362306a36Sopenharmony_ci#include <linux/interrupt.h>
3462306a36Sopenharmony_ci#include <linux/delay.h>
3562306a36Sopenharmony_ci#include <linux/netdevice.h>
3662306a36Sopenharmony_ci#include <linux/etherdevice.h>
3762306a36Sopenharmony_ci#include <linux/skbuff.h>
3862306a36Sopenharmony_ci#include <linux/in.h>
3962306a36Sopenharmony_ci#include <linux/ip.h>
4062306a36Sopenharmony_ci#include <net/ip.h>
4162306a36Sopenharmony_ci#include <net/page_pool/helpers.h>
4262306a36Sopenharmony_ci#include <net/selftests.h>
4362306a36Sopenharmony_ci#include <net/tso.h>
4462306a36Sopenharmony_ci#include <linux/tcp.h>
4562306a36Sopenharmony_ci#include <linux/udp.h>
4662306a36Sopenharmony_ci#include <linux/icmp.h>
4762306a36Sopenharmony_ci#include <linux/spinlock.h>
4862306a36Sopenharmony_ci#include <linux/workqueue.h>
4962306a36Sopenharmony_ci#include <linux/bitops.h>
5062306a36Sopenharmony_ci#include <linux/io.h>
5162306a36Sopenharmony_ci#include <linux/irq.h>
5262306a36Sopenharmony_ci#include <linux/clk.h>
5362306a36Sopenharmony_ci#include <linux/crc32.h>
5462306a36Sopenharmony_ci#include <linux/platform_device.h>
5562306a36Sopenharmony_ci#include <linux/mdio.h>
5662306a36Sopenharmony_ci#include <linux/phy.h>
5762306a36Sopenharmony_ci#include <linux/fec.h>
5862306a36Sopenharmony_ci#include <linux/of.h>
5962306a36Sopenharmony_ci#include <linux/of_device.h>
6062306a36Sopenharmony_ci#include <linux/of_mdio.h>
6162306a36Sopenharmony_ci#include <linux/of_net.h>
6262306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
6362306a36Sopenharmony_ci#include <linux/if_vlan.h>
6462306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h>
6562306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
6662306a36Sopenharmony_ci#include <linux/prefetch.h>
6762306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
6862306a36Sopenharmony_ci#include <linux/regmap.h>
6962306a36Sopenharmony_ci#include <soc/imx/cpuidle.h>
7062306a36Sopenharmony_ci#include <linux/filter.h>
7162306a36Sopenharmony_ci#include <linux/bpf.h>
7262306a36Sopenharmony_ci#include <linux/bpf_trace.h>
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci#include <asm/cacheflush.h>
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci#include "fec.h"
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic void set_multicast_list(struct net_device *ndev);
7962306a36Sopenharmony_cistatic void fec_enet_itr_coal_set(struct net_device *ndev);
8062306a36Sopenharmony_cistatic int fec_enet_xdp_tx_xmit(struct fec_enet_private *fep,
8162306a36Sopenharmony_ci				int cpu, struct xdp_buff *xdp,
8262306a36Sopenharmony_ci				u32 dma_sync_len);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci#define DRIVER_NAME	"fec"
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic const u16 fec_enet_vlan_pri_to_queue[8] = {0, 0, 1, 1, 1, 2, 2, 2};
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/* Pause frame feild and FIFO threshold */
8962306a36Sopenharmony_ci#define FEC_ENET_FCE	(1 << 5)
9062306a36Sopenharmony_ci#define FEC_ENET_RSEM_V	0x84
9162306a36Sopenharmony_ci#define FEC_ENET_RSFL_V	16
9262306a36Sopenharmony_ci#define FEC_ENET_RAEM_V	0x8
9362306a36Sopenharmony_ci#define FEC_ENET_RAFL_V	0x8
9462306a36Sopenharmony_ci#define FEC_ENET_OPD_V	0xFFF0
9562306a36Sopenharmony_ci#define FEC_MDIO_PM_TIMEOUT  100 /* ms */
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci#define FEC_ENET_XDP_PASS          0
9862306a36Sopenharmony_ci#define FEC_ENET_XDP_CONSUMED      BIT(0)
9962306a36Sopenharmony_ci#define FEC_ENET_XDP_TX            BIT(1)
10062306a36Sopenharmony_ci#define FEC_ENET_XDP_REDIR         BIT(2)
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistruct fec_devinfo {
10362306a36Sopenharmony_ci	u32 quirks;
10462306a36Sopenharmony_ci};
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic const struct fec_devinfo fec_imx25_info = {
10762306a36Sopenharmony_ci	.quirks = FEC_QUIRK_USE_GASKET | FEC_QUIRK_MIB_CLEAR |
10862306a36Sopenharmony_ci		  FEC_QUIRK_HAS_FRREG | FEC_QUIRK_HAS_MDIO_C45,
10962306a36Sopenharmony_ci};
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic const struct fec_devinfo fec_imx27_info = {
11262306a36Sopenharmony_ci	.quirks = FEC_QUIRK_MIB_CLEAR | FEC_QUIRK_HAS_FRREG |
11362306a36Sopenharmony_ci		  FEC_QUIRK_HAS_MDIO_C45,
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic const struct fec_devinfo fec_imx28_info = {
11762306a36Sopenharmony_ci	.quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME |
11862306a36Sopenharmony_ci		  FEC_QUIRK_SINGLE_MDIO | FEC_QUIRK_HAS_RACC |
11962306a36Sopenharmony_ci		  FEC_QUIRK_HAS_FRREG | FEC_QUIRK_CLEAR_SETUP_MII |
12062306a36Sopenharmony_ci		  FEC_QUIRK_NO_HARD_RESET | FEC_QUIRK_HAS_MDIO_C45,
12162306a36Sopenharmony_ci};
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic const struct fec_devinfo fec_imx6q_info = {
12462306a36Sopenharmony_ci	.quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
12562306a36Sopenharmony_ci		  FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
12662306a36Sopenharmony_ci		  FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR006358 |
12762306a36Sopenharmony_ci		  FEC_QUIRK_HAS_RACC | FEC_QUIRK_CLEAR_SETUP_MII |
12862306a36Sopenharmony_ci		  FEC_QUIRK_HAS_PMQOS | FEC_QUIRK_HAS_MDIO_C45,
12962306a36Sopenharmony_ci};
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic const struct fec_devinfo fec_mvf600_info = {
13262306a36Sopenharmony_ci	.quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_RACC |
13362306a36Sopenharmony_ci		  FEC_QUIRK_HAS_MDIO_C45,
13462306a36Sopenharmony_ci};
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic const struct fec_devinfo fec_imx6x_info = {
13762306a36Sopenharmony_ci	.quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
13862306a36Sopenharmony_ci		  FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
13962306a36Sopenharmony_ci		  FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB |
14062306a36Sopenharmony_ci		  FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE |
14162306a36Sopenharmony_ci		  FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE |
14262306a36Sopenharmony_ci		  FEC_QUIRK_CLEAR_SETUP_MII | FEC_QUIRK_HAS_MULTI_QUEUES |
14362306a36Sopenharmony_ci		  FEC_QUIRK_HAS_MDIO_C45,
14462306a36Sopenharmony_ci};
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic const struct fec_devinfo fec_imx6ul_info = {
14762306a36Sopenharmony_ci	.quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
14862306a36Sopenharmony_ci		  FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
14962306a36Sopenharmony_ci		  FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR007885 |
15062306a36Sopenharmony_ci		  FEC_QUIRK_BUG_CAPTURE | FEC_QUIRK_HAS_RACC |
15162306a36Sopenharmony_ci		  FEC_QUIRK_HAS_COALESCE | FEC_QUIRK_CLEAR_SETUP_MII |
15262306a36Sopenharmony_ci		  FEC_QUIRK_HAS_MDIO_C45,
15362306a36Sopenharmony_ci};
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic const struct fec_devinfo fec_imx8mq_info = {
15662306a36Sopenharmony_ci	.quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
15762306a36Sopenharmony_ci		  FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
15862306a36Sopenharmony_ci		  FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB |
15962306a36Sopenharmony_ci		  FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE |
16062306a36Sopenharmony_ci		  FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE |
16162306a36Sopenharmony_ci		  FEC_QUIRK_CLEAR_SETUP_MII | FEC_QUIRK_HAS_MULTI_QUEUES |
16262306a36Sopenharmony_ci		  FEC_QUIRK_HAS_EEE | FEC_QUIRK_WAKEUP_FROM_INT2 |
16362306a36Sopenharmony_ci		  FEC_QUIRK_HAS_MDIO_C45,
16462306a36Sopenharmony_ci};
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic const struct fec_devinfo fec_imx8qm_info = {
16762306a36Sopenharmony_ci	.quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
16862306a36Sopenharmony_ci		  FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
16962306a36Sopenharmony_ci		  FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB |
17062306a36Sopenharmony_ci		  FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE |
17162306a36Sopenharmony_ci		  FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE |
17262306a36Sopenharmony_ci		  FEC_QUIRK_CLEAR_SETUP_MII | FEC_QUIRK_HAS_MULTI_QUEUES |
17362306a36Sopenharmony_ci		  FEC_QUIRK_DELAYED_CLKS_SUPPORT | FEC_QUIRK_HAS_MDIO_C45,
17462306a36Sopenharmony_ci};
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic const struct fec_devinfo fec_s32v234_info = {
17762306a36Sopenharmony_ci	.quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
17862306a36Sopenharmony_ci		  FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
17962306a36Sopenharmony_ci		  FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB |
18062306a36Sopenharmony_ci		  FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE |
18162306a36Sopenharmony_ci		  FEC_QUIRK_HAS_MDIO_C45,
18262306a36Sopenharmony_ci};
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic struct platform_device_id fec_devtype[] = {
18562306a36Sopenharmony_ci	{
18662306a36Sopenharmony_ci		/* keep it for coldfire */
18762306a36Sopenharmony_ci		.name = DRIVER_NAME,
18862306a36Sopenharmony_ci		.driver_data = 0,
18962306a36Sopenharmony_ci	}, {
19062306a36Sopenharmony_ci		.name = "imx25-fec",
19162306a36Sopenharmony_ci		.driver_data = (kernel_ulong_t)&fec_imx25_info,
19262306a36Sopenharmony_ci	}, {
19362306a36Sopenharmony_ci		.name = "imx27-fec",
19462306a36Sopenharmony_ci		.driver_data = (kernel_ulong_t)&fec_imx27_info,
19562306a36Sopenharmony_ci	}, {
19662306a36Sopenharmony_ci		.name = "imx28-fec",
19762306a36Sopenharmony_ci		.driver_data = (kernel_ulong_t)&fec_imx28_info,
19862306a36Sopenharmony_ci	}, {
19962306a36Sopenharmony_ci		.name = "imx6q-fec",
20062306a36Sopenharmony_ci		.driver_data = (kernel_ulong_t)&fec_imx6q_info,
20162306a36Sopenharmony_ci	}, {
20262306a36Sopenharmony_ci		.name = "mvf600-fec",
20362306a36Sopenharmony_ci		.driver_data = (kernel_ulong_t)&fec_mvf600_info,
20462306a36Sopenharmony_ci	}, {
20562306a36Sopenharmony_ci		.name = "imx6sx-fec",
20662306a36Sopenharmony_ci		.driver_data = (kernel_ulong_t)&fec_imx6x_info,
20762306a36Sopenharmony_ci	}, {
20862306a36Sopenharmony_ci		.name = "imx6ul-fec",
20962306a36Sopenharmony_ci		.driver_data = (kernel_ulong_t)&fec_imx6ul_info,
21062306a36Sopenharmony_ci	}, {
21162306a36Sopenharmony_ci		.name = "imx8mq-fec",
21262306a36Sopenharmony_ci		.driver_data = (kernel_ulong_t)&fec_imx8mq_info,
21362306a36Sopenharmony_ci	}, {
21462306a36Sopenharmony_ci		.name = "imx8qm-fec",
21562306a36Sopenharmony_ci		.driver_data = (kernel_ulong_t)&fec_imx8qm_info,
21662306a36Sopenharmony_ci	}, {
21762306a36Sopenharmony_ci		.name = "s32v234-fec",
21862306a36Sopenharmony_ci		.driver_data = (kernel_ulong_t)&fec_s32v234_info,
21962306a36Sopenharmony_ci	}, {
22062306a36Sopenharmony_ci		/* sentinel */
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci};
22362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, fec_devtype);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cienum imx_fec_type {
22662306a36Sopenharmony_ci	IMX25_FEC = 1,	/* runs on i.mx25/50/53 */
22762306a36Sopenharmony_ci	IMX27_FEC,	/* runs on i.mx27/35/51 */
22862306a36Sopenharmony_ci	IMX28_FEC,
22962306a36Sopenharmony_ci	IMX6Q_FEC,
23062306a36Sopenharmony_ci	MVF600_FEC,
23162306a36Sopenharmony_ci	IMX6SX_FEC,
23262306a36Sopenharmony_ci	IMX6UL_FEC,
23362306a36Sopenharmony_ci	IMX8MQ_FEC,
23462306a36Sopenharmony_ci	IMX8QM_FEC,
23562306a36Sopenharmony_ci	S32V234_FEC,
23662306a36Sopenharmony_ci};
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic const struct of_device_id fec_dt_ids[] = {
23962306a36Sopenharmony_ci	{ .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], },
24062306a36Sopenharmony_ci	{ .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], },
24162306a36Sopenharmony_ci	{ .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], },
24262306a36Sopenharmony_ci	{ .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], },
24362306a36Sopenharmony_ci	{ .compatible = "fsl,mvf600-fec", .data = &fec_devtype[MVF600_FEC], },
24462306a36Sopenharmony_ci	{ .compatible = "fsl,imx6sx-fec", .data = &fec_devtype[IMX6SX_FEC], },
24562306a36Sopenharmony_ci	{ .compatible = "fsl,imx6ul-fec", .data = &fec_devtype[IMX6UL_FEC], },
24662306a36Sopenharmony_ci	{ .compatible = "fsl,imx8mq-fec", .data = &fec_devtype[IMX8MQ_FEC], },
24762306a36Sopenharmony_ci	{ .compatible = "fsl,imx8qm-fec", .data = &fec_devtype[IMX8QM_FEC], },
24862306a36Sopenharmony_ci	{ .compatible = "fsl,s32v234-fec", .data = &fec_devtype[S32V234_FEC], },
24962306a36Sopenharmony_ci	{ /* sentinel */ }
25062306a36Sopenharmony_ci};
25162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, fec_dt_ids);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cistatic unsigned char macaddr[ETH_ALEN];
25462306a36Sopenharmony_cimodule_param_array(macaddr, byte, NULL, 0);
25562306a36Sopenharmony_ciMODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci#if defined(CONFIG_M5272)
25862306a36Sopenharmony_ci/*
25962306a36Sopenharmony_ci * Some hardware gets it MAC address out of local flash memory.
26062306a36Sopenharmony_ci * if this is non-zero then assume it is the address to get MAC from.
26162306a36Sopenharmony_ci */
26262306a36Sopenharmony_ci#if defined(CONFIG_NETtel)
26362306a36Sopenharmony_ci#define	FEC_FLASHMAC	0xf0006006
26462306a36Sopenharmony_ci#elif defined(CONFIG_GILBARCONAP) || defined(CONFIG_SCALES)
26562306a36Sopenharmony_ci#define	FEC_FLASHMAC	0xf0006000
26662306a36Sopenharmony_ci#elif defined(CONFIG_CANCam)
26762306a36Sopenharmony_ci#define	FEC_FLASHMAC	0xf0020000
26862306a36Sopenharmony_ci#elif defined (CONFIG_M5272C3)
26962306a36Sopenharmony_ci#define	FEC_FLASHMAC	(0xffe04000 + 4)
27062306a36Sopenharmony_ci#elif defined(CONFIG_MOD5272)
27162306a36Sopenharmony_ci#define FEC_FLASHMAC	0xffc0406b
27262306a36Sopenharmony_ci#else
27362306a36Sopenharmony_ci#define	FEC_FLASHMAC	0
27462306a36Sopenharmony_ci#endif
27562306a36Sopenharmony_ci#endif /* CONFIG_M5272 */
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci/* The FEC stores dest/src/type/vlan, data, and checksum for receive packets.
27862306a36Sopenharmony_ci *
27962306a36Sopenharmony_ci * 2048 byte skbufs are allocated. However, alignment requirements
28062306a36Sopenharmony_ci * varies between FEC variants. Worst case is 64, so round down by 64.
28162306a36Sopenharmony_ci */
28262306a36Sopenharmony_ci#define PKT_MAXBUF_SIZE		(round_down(2048 - 64, 64))
28362306a36Sopenharmony_ci#define PKT_MINBUF_SIZE		64
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci/* FEC receive acceleration */
28662306a36Sopenharmony_ci#define FEC_RACC_IPDIS		(1 << 1)
28762306a36Sopenharmony_ci#define FEC_RACC_PRODIS		(1 << 2)
28862306a36Sopenharmony_ci#define FEC_RACC_SHIFT16	BIT(7)
28962306a36Sopenharmony_ci#define FEC_RACC_OPTIONS	(FEC_RACC_IPDIS | FEC_RACC_PRODIS)
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci/* MIB Control Register */
29262306a36Sopenharmony_ci#define FEC_MIB_CTRLSTAT_DISABLE	BIT(31)
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci/*
29562306a36Sopenharmony_ci * The 5270/5271/5280/5282/532x RX control register also contains maximum frame
29662306a36Sopenharmony_ci * size bits. Other FEC hardware does not, so we need to take that into
29762306a36Sopenharmony_ci * account when setting it.
29862306a36Sopenharmony_ci */
29962306a36Sopenharmony_ci#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
30062306a36Sopenharmony_ci    defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \
30162306a36Sopenharmony_ci    defined(CONFIG_ARM64)
30262306a36Sopenharmony_ci#define	OPT_FRAME_SIZE	(PKT_MAXBUF_SIZE << 16)
30362306a36Sopenharmony_ci#else
30462306a36Sopenharmony_ci#define	OPT_FRAME_SIZE	0
30562306a36Sopenharmony_ci#endif
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci/* FEC MII MMFR bits definition */
30862306a36Sopenharmony_ci#define FEC_MMFR_ST		(1 << 30)
30962306a36Sopenharmony_ci#define FEC_MMFR_ST_C45		(0)
31062306a36Sopenharmony_ci#define FEC_MMFR_OP_READ	(2 << 28)
31162306a36Sopenharmony_ci#define FEC_MMFR_OP_READ_C45	(3 << 28)
31262306a36Sopenharmony_ci#define FEC_MMFR_OP_WRITE	(1 << 28)
31362306a36Sopenharmony_ci#define FEC_MMFR_OP_ADDR_WRITE	(0)
31462306a36Sopenharmony_ci#define FEC_MMFR_PA(v)		((v & 0x1f) << 23)
31562306a36Sopenharmony_ci#define FEC_MMFR_RA(v)		((v & 0x1f) << 18)
31662306a36Sopenharmony_ci#define FEC_MMFR_TA		(2 << 16)
31762306a36Sopenharmony_ci#define FEC_MMFR_DATA(v)	(v & 0xffff)
31862306a36Sopenharmony_ci/* FEC ECR bits definition */
31962306a36Sopenharmony_ci#define FEC_ECR_MAGICEN		(1 << 2)
32062306a36Sopenharmony_ci#define FEC_ECR_SLEEP		(1 << 3)
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci#define FEC_MII_TIMEOUT		30000 /* us */
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci/* Transmitter timeout */
32562306a36Sopenharmony_ci#define TX_TIMEOUT (2 * HZ)
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci#define FEC_PAUSE_FLAG_AUTONEG	0x1
32862306a36Sopenharmony_ci#define FEC_PAUSE_FLAG_ENABLE	0x2
32962306a36Sopenharmony_ci#define FEC_WOL_HAS_MAGIC_PACKET	(0x1 << 0)
33062306a36Sopenharmony_ci#define FEC_WOL_FLAG_ENABLE		(0x1 << 1)
33162306a36Sopenharmony_ci#define FEC_WOL_FLAG_SLEEP_ON		(0x1 << 2)
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci/* Max number of allowed TCP segments for software TSO */
33462306a36Sopenharmony_ci#define FEC_MAX_TSO_SEGS	100
33562306a36Sopenharmony_ci#define FEC_MAX_SKB_DESCS	(FEC_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS)
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci#define IS_TSO_HEADER(txq, addr) \
33862306a36Sopenharmony_ci	((addr >= txq->tso_hdrs_dma) && \
33962306a36Sopenharmony_ci	(addr < txq->tso_hdrs_dma + txq->bd.ring_size * TSO_HEADER_SIZE))
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic int mii_cnt;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp,
34462306a36Sopenharmony_ci					     struct bufdesc_prop *bd)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	return (bdp >= bd->last) ? bd->base
34762306a36Sopenharmony_ci			: (struct bufdesc *)(((void *)bdp) + bd->dsize);
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp,
35162306a36Sopenharmony_ci					     struct bufdesc_prop *bd)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	return (bdp <= bd->base) ? bd->last
35462306a36Sopenharmony_ci			: (struct bufdesc *)(((void *)bdp) - bd->dsize);
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic int fec_enet_get_bd_index(struct bufdesc *bdp,
35862306a36Sopenharmony_ci				 struct bufdesc_prop *bd)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	return ((const char *)bdp - (const char *)bd->base) >> bd->dsize_log2;
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic int fec_enet_get_free_txdesc_num(struct fec_enet_priv_tx_q *txq)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	int entries;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	entries = (((const char *)txq->dirty_tx -
36862306a36Sopenharmony_ci			(const char *)txq->bd.cur) >> txq->bd.dsize_log2) - 1;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	return entries >= 0 ? entries : entries + txq->bd.ring_size;
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cistatic void swap_buffer(void *bufaddr, int len)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	int i;
37662306a36Sopenharmony_ci	unsigned int *buf = bufaddr;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	for (i = 0; i < len; i += 4, buf++)
37962306a36Sopenharmony_ci		swab32s(buf);
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistatic void fec_dump(struct net_device *ndev)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
38562306a36Sopenharmony_ci	struct bufdesc *bdp;
38662306a36Sopenharmony_ci	struct fec_enet_priv_tx_q *txq;
38762306a36Sopenharmony_ci	int index = 0;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	netdev_info(ndev, "TX ring dump\n");
39062306a36Sopenharmony_ci	pr_info("Nr     SC     addr       len  SKB\n");
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	txq = fep->tx_queue[0];
39362306a36Sopenharmony_ci	bdp = txq->bd.base;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	do {
39662306a36Sopenharmony_ci		pr_info("%3u %c%c 0x%04x 0x%08x %4u %p\n",
39762306a36Sopenharmony_ci			index,
39862306a36Sopenharmony_ci			bdp == txq->bd.cur ? 'S' : ' ',
39962306a36Sopenharmony_ci			bdp == txq->dirty_tx ? 'H' : ' ',
40062306a36Sopenharmony_ci			fec16_to_cpu(bdp->cbd_sc),
40162306a36Sopenharmony_ci			fec32_to_cpu(bdp->cbd_bufaddr),
40262306a36Sopenharmony_ci			fec16_to_cpu(bdp->cbd_datlen),
40362306a36Sopenharmony_ci			txq->tx_buf[index].buf_p);
40462306a36Sopenharmony_ci		bdp = fec_enet_get_nextdesc(bdp, &txq->bd);
40562306a36Sopenharmony_ci		index++;
40662306a36Sopenharmony_ci	} while (bdp != txq->bd.base);
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic inline bool is_ipv4_pkt(struct sk_buff *skb)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	return skb->protocol == htons(ETH_P_IP) && ip_hdr(skb)->version == 4;
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic int
41562306a36Sopenharmony_cifec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	/* Only run for packets requiring a checksum. */
41862306a36Sopenharmony_ci	if (skb->ip_summed != CHECKSUM_PARTIAL)
41962306a36Sopenharmony_ci		return 0;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	if (unlikely(skb_cow_head(skb, 0)))
42262306a36Sopenharmony_ci		return -1;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	if (is_ipv4_pkt(skb))
42562306a36Sopenharmony_ci		ip_hdr(skb)->check = 0;
42662306a36Sopenharmony_ci	*(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) = 0;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	return 0;
42962306a36Sopenharmony_ci}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cistatic int
43262306a36Sopenharmony_cifec_enet_create_page_pool(struct fec_enet_private *fep,
43362306a36Sopenharmony_ci			  struct fec_enet_priv_rx_q *rxq, int size)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	struct bpf_prog *xdp_prog = READ_ONCE(fep->xdp_prog);
43662306a36Sopenharmony_ci	struct page_pool_params pp_params = {
43762306a36Sopenharmony_ci		.order = 0,
43862306a36Sopenharmony_ci		.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
43962306a36Sopenharmony_ci		.pool_size = size,
44062306a36Sopenharmony_ci		.nid = dev_to_node(&fep->pdev->dev),
44162306a36Sopenharmony_ci		.dev = &fep->pdev->dev,
44262306a36Sopenharmony_ci		.dma_dir = xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE,
44362306a36Sopenharmony_ci		.offset = FEC_ENET_XDP_HEADROOM,
44462306a36Sopenharmony_ci		.max_len = FEC_ENET_RX_FRSIZE,
44562306a36Sopenharmony_ci	};
44662306a36Sopenharmony_ci	int err;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	rxq->page_pool = page_pool_create(&pp_params);
44962306a36Sopenharmony_ci	if (IS_ERR(rxq->page_pool)) {
45062306a36Sopenharmony_ci		err = PTR_ERR(rxq->page_pool);
45162306a36Sopenharmony_ci		rxq->page_pool = NULL;
45262306a36Sopenharmony_ci		return err;
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	err = xdp_rxq_info_reg(&rxq->xdp_rxq, fep->netdev, rxq->id, 0);
45662306a36Sopenharmony_ci	if (err < 0)
45762306a36Sopenharmony_ci		goto err_free_pp;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	err = xdp_rxq_info_reg_mem_model(&rxq->xdp_rxq, MEM_TYPE_PAGE_POOL,
46062306a36Sopenharmony_ci					 rxq->page_pool);
46162306a36Sopenharmony_ci	if (err)
46262306a36Sopenharmony_ci		goto err_unregister_rxq;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	return 0;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cierr_unregister_rxq:
46762306a36Sopenharmony_ci	xdp_rxq_info_unreg(&rxq->xdp_rxq);
46862306a36Sopenharmony_cierr_free_pp:
46962306a36Sopenharmony_ci	page_pool_destroy(rxq->page_pool);
47062306a36Sopenharmony_ci	rxq->page_pool = NULL;
47162306a36Sopenharmony_ci	return err;
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_cistatic struct bufdesc *
47562306a36Sopenharmony_cifec_enet_txq_submit_frag_skb(struct fec_enet_priv_tx_q *txq,
47662306a36Sopenharmony_ci			     struct sk_buff *skb,
47762306a36Sopenharmony_ci			     struct net_device *ndev)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
48062306a36Sopenharmony_ci	struct bufdesc *bdp = txq->bd.cur;
48162306a36Sopenharmony_ci	struct bufdesc_ex *ebdp;
48262306a36Sopenharmony_ci	int nr_frags = skb_shinfo(skb)->nr_frags;
48362306a36Sopenharmony_ci	int frag, frag_len;
48462306a36Sopenharmony_ci	unsigned short status;
48562306a36Sopenharmony_ci	unsigned int estatus = 0;
48662306a36Sopenharmony_ci	skb_frag_t *this_frag;
48762306a36Sopenharmony_ci	unsigned int index;
48862306a36Sopenharmony_ci	void *bufaddr;
48962306a36Sopenharmony_ci	dma_addr_t addr;
49062306a36Sopenharmony_ci	int i;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	for (frag = 0; frag < nr_frags; frag++) {
49362306a36Sopenharmony_ci		this_frag = &skb_shinfo(skb)->frags[frag];
49462306a36Sopenharmony_ci		bdp = fec_enet_get_nextdesc(bdp, &txq->bd);
49562306a36Sopenharmony_ci		ebdp = (struct bufdesc_ex *)bdp;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci		status = fec16_to_cpu(bdp->cbd_sc);
49862306a36Sopenharmony_ci		status &= ~BD_ENET_TX_STATS;
49962306a36Sopenharmony_ci		status |= (BD_ENET_TX_TC | BD_ENET_TX_READY);
50062306a36Sopenharmony_ci		frag_len = skb_frag_size(&skb_shinfo(skb)->frags[frag]);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci		/* Handle the last BD specially */
50362306a36Sopenharmony_ci		if (frag == nr_frags - 1) {
50462306a36Sopenharmony_ci			status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST);
50562306a36Sopenharmony_ci			if (fep->bufdesc_ex) {
50662306a36Sopenharmony_ci				estatus |= BD_ENET_TX_INT;
50762306a36Sopenharmony_ci				if (unlikely(skb_shinfo(skb)->tx_flags &
50862306a36Sopenharmony_ci					SKBTX_HW_TSTAMP && fep->hwts_tx_en))
50962306a36Sopenharmony_ci					estatus |= BD_ENET_TX_TS;
51062306a36Sopenharmony_ci			}
51162306a36Sopenharmony_ci		}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci		if (fep->bufdesc_ex) {
51462306a36Sopenharmony_ci			if (fep->quirks & FEC_QUIRK_HAS_AVB)
51562306a36Sopenharmony_ci				estatus |= FEC_TX_BD_FTYPE(txq->bd.qid);
51662306a36Sopenharmony_ci			if (skb->ip_summed == CHECKSUM_PARTIAL)
51762306a36Sopenharmony_ci				estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci			ebdp->cbd_bdu = 0;
52062306a36Sopenharmony_ci			ebdp->cbd_esc = cpu_to_fec32(estatus);
52162306a36Sopenharmony_ci		}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci		bufaddr = skb_frag_address(this_frag);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci		index = fec_enet_get_bd_index(bdp, &txq->bd);
52662306a36Sopenharmony_ci		if (((unsigned long) bufaddr) & fep->tx_align ||
52762306a36Sopenharmony_ci			fep->quirks & FEC_QUIRK_SWAP_FRAME) {
52862306a36Sopenharmony_ci			memcpy(txq->tx_bounce[index], bufaddr, frag_len);
52962306a36Sopenharmony_ci			bufaddr = txq->tx_bounce[index];
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci			if (fep->quirks & FEC_QUIRK_SWAP_FRAME)
53262306a36Sopenharmony_ci				swap_buffer(bufaddr, frag_len);
53362306a36Sopenharmony_ci		}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci		addr = dma_map_single(&fep->pdev->dev, bufaddr, frag_len,
53662306a36Sopenharmony_ci				      DMA_TO_DEVICE);
53762306a36Sopenharmony_ci		if (dma_mapping_error(&fep->pdev->dev, addr)) {
53862306a36Sopenharmony_ci			if (net_ratelimit())
53962306a36Sopenharmony_ci				netdev_err(ndev, "Tx DMA memory map failed\n");
54062306a36Sopenharmony_ci			goto dma_mapping_error;
54162306a36Sopenharmony_ci		}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci		bdp->cbd_bufaddr = cpu_to_fec32(addr);
54462306a36Sopenharmony_ci		bdp->cbd_datlen = cpu_to_fec16(frag_len);
54562306a36Sopenharmony_ci		/* Make sure the updates to rest of the descriptor are
54662306a36Sopenharmony_ci		 * performed before transferring ownership.
54762306a36Sopenharmony_ci		 */
54862306a36Sopenharmony_ci		wmb();
54962306a36Sopenharmony_ci		bdp->cbd_sc = cpu_to_fec16(status);
55062306a36Sopenharmony_ci	}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	return bdp;
55362306a36Sopenharmony_cidma_mapping_error:
55462306a36Sopenharmony_ci	bdp = txq->bd.cur;
55562306a36Sopenharmony_ci	for (i = 0; i < frag; i++) {
55662306a36Sopenharmony_ci		bdp = fec_enet_get_nextdesc(bdp, &txq->bd);
55762306a36Sopenharmony_ci		dma_unmap_single(&fep->pdev->dev, fec32_to_cpu(bdp->cbd_bufaddr),
55862306a36Sopenharmony_ci				 fec16_to_cpu(bdp->cbd_datlen), DMA_TO_DEVICE);
55962306a36Sopenharmony_ci	}
56062306a36Sopenharmony_ci	return ERR_PTR(-ENOMEM);
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_cistatic int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq,
56462306a36Sopenharmony_ci				   struct sk_buff *skb, struct net_device *ndev)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
56762306a36Sopenharmony_ci	int nr_frags = skb_shinfo(skb)->nr_frags;
56862306a36Sopenharmony_ci	struct bufdesc *bdp, *last_bdp;
56962306a36Sopenharmony_ci	void *bufaddr;
57062306a36Sopenharmony_ci	dma_addr_t addr;
57162306a36Sopenharmony_ci	unsigned short status;
57262306a36Sopenharmony_ci	unsigned short buflen;
57362306a36Sopenharmony_ci	unsigned int estatus = 0;
57462306a36Sopenharmony_ci	unsigned int index;
57562306a36Sopenharmony_ci	int entries_free;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	entries_free = fec_enet_get_free_txdesc_num(txq);
57862306a36Sopenharmony_ci	if (entries_free < MAX_SKB_FRAGS + 1) {
57962306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
58062306a36Sopenharmony_ci		if (net_ratelimit())
58162306a36Sopenharmony_ci			netdev_err(ndev, "NOT enough BD for SG!\n");
58262306a36Sopenharmony_ci		return NETDEV_TX_OK;
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	/* Protocol checksum off-load for TCP and UDP. */
58662306a36Sopenharmony_ci	if (fec_enet_clear_csum(skb, ndev)) {
58762306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
58862306a36Sopenharmony_ci		return NETDEV_TX_OK;
58962306a36Sopenharmony_ci	}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	/* Fill in a Tx ring entry */
59262306a36Sopenharmony_ci	bdp = txq->bd.cur;
59362306a36Sopenharmony_ci	last_bdp = bdp;
59462306a36Sopenharmony_ci	status = fec16_to_cpu(bdp->cbd_sc);
59562306a36Sopenharmony_ci	status &= ~BD_ENET_TX_STATS;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	/* Set buffer length and buffer pointer */
59862306a36Sopenharmony_ci	bufaddr = skb->data;
59962306a36Sopenharmony_ci	buflen = skb_headlen(skb);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	index = fec_enet_get_bd_index(bdp, &txq->bd);
60262306a36Sopenharmony_ci	if (((unsigned long) bufaddr) & fep->tx_align ||
60362306a36Sopenharmony_ci		fep->quirks & FEC_QUIRK_SWAP_FRAME) {
60462306a36Sopenharmony_ci		memcpy(txq->tx_bounce[index], skb->data, buflen);
60562306a36Sopenharmony_ci		bufaddr = txq->tx_bounce[index];
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci		if (fep->quirks & FEC_QUIRK_SWAP_FRAME)
60862306a36Sopenharmony_ci			swap_buffer(bufaddr, buflen);
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	/* Push the data cache so the CPM does not get stale memory data. */
61262306a36Sopenharmony_ci	addr = dma_map_single(&fep->pdev->dev, bufaddr, buflen, DMA_TO_DEVICE);
61362306a36Sopenharmony_ci	if (dma_mapping_error(&fep->pdev->dev, addr)) {
61462306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
61562306a36Sopenharmony_ci		if (net_ratelimit())
61662306a36Sopenharmony_ci			netdev_err(ndev, "Tx DMA memory map failed\n");
61762306a36Sopenharmony_ci		return NETDEV_TX_OK;
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	if (nr_frags) {
62162306a36Sopenharmony_ci		last_bdp = fec_enet_txq_submit_frag_skb(txq, skb, ndev);
62262306a36Sopenharmony_ci		if (IS_ERR(last_bdp)) {
62362306a36Sopenharmony_ci			dma_unmap_single(&fep->pdev->dev, addr,
62462306a36Sopenharmony_ci					 buflen, DMA_TO_DEVICE);
62562306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
62662306a36Sopenharmony_ci			return NETDEV_TX_OK;
62762306a36Sopenharmony_ci		}
62862306a36Sopenharmony_ci	} else {
62962306a36Sopenharmony_ci		status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST);
63062306a36Sopenharmony_ci		if (fep->bufdesc_ex) {
63162306a36Sopenharmony_ci			estatus = BD_ENET_TX_INT;
63262306a36Sopenharmony_ci			if (unlikely(skb_shinfo(skb)->tx_flags &
63362306a36Sopenharmony_ci				SKBTX_HW_TSTAMP && fep->hwts_tx_en))
63462306a36Sopenharmony_ci				estatus |= BD_ENET_TX_TS;
63562306a36Sopenharmony_ci		}
63662306a36Sopenharmony_ci	}
63762306a36Sopenharmony_ci	bdp->cbd_bufaddr = cpu_to_fec32(addr);
63862306a36Sopenharmony_ci	bdp->cbd_datlen = cpu_to_fec16(buflen);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	if (fep->bufdesc_ex) {
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci		struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci		if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
64562306a36Sopenharmony_ci			fep->hwts_tx_en))
64662306a36Sopenharmony_ci			skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci		if (fep->quirks & FEC_QUIRK_HAS_AVB)
64962306a36Sopenharmony_ci			estatus |= FEC_TX_BD_FTYPE(txq->bd.qid);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci		if (skb->ip_summed == CHECKSUM_PARTIAL)
65262306a36Sopenharmony_ci			estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci		ebdp->cbd_bdu = 0;
65562306a36Sopenharmony_ci		ebdp->cbd_esc = cpu_to_fec32(estatus);
65662306a36Sopenharmony_ci	}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	index = fec_enet_get_bd_index(last_bdp, &txq->bd);
65962306a36Sopenharmony_ci	/* Save skb pointer */
66062306a36Sopenharmony_ci	txq->tx_buf[index].buf_p = skb;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	/* Make sure the updates to rest of the descriptor are performed before
66362306a36Sopenharmony_ci	 * transferring ownership.
66462306a36Sopenharmony_ci	 */
66562306a36Sopenharmony_ci	wmb();
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	/* Send it on its way.  Tell FEC it's ready, interrupt when done,
66862306a36Sopenharmony_ci	 * it's the last BD of the frame, and to put the CRC on the end.
66962306a36Sopenharmony_ci	 */
67062306a36Sopenharmony_ci	status |= (BD_ENET_TX_READY | BD_ENET_TX_TC);
67162306a36Sopenharmony_ci	bdp->cbd_sc = cpu_to_fec16(status);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	/* If this was the last BD in the ring, start at the beginning again. */
67462306a36Sopenharmony_ci	bdp = fec_enet_get_nextdesc(last_bdp, &txq->bd);
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	skb_tx_timestamp(skb);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	/* Make sure the update to bdp is performed before txq->bd.cur. */
67962306a36Sopenharmony_ci	wmb();
68062306a36Sopenharmony_ci	txq->bd.cur = bdp;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	/* Trigger transmission start */
68362306a36Sopenharmony_ci	writel(0, txq->bd.reg_desc_active);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	return 0;
68662306a36Sopenharmony_ci}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_cistatic int
68962306a36Sopenharmony_cifec_enet_txq_put_data_tso(struct fec_enet_priv_tx_q *txq, struct sk_buff *skb,
69062306a36Sopenharmony_ci			  struct net_device *ndev,
69162306a36Sopenharmony_ci			  struct bufdesc *bdp, int index, char *data,
69262306a36Sopenharmony_ci			  int size, bool last_tcp, bool is_last)
69362306a36Sopenharmony_ci{
69462306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
69562306a36Sopenharmony_ci	struct bufdesc_ex *ebdp = container_of(bdp, struct bufdesc_ex, desc);
69662306a36Sopenharmony_ci	unsigned short status;
69762306a36Sopenharmony_ci	unsigned int estatus = 0;
69862306a36Sopenharmony_ci	dma_addr_t addr;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	status = fec16_to_cpu(bdp->cbd_sc);
70162306a36Sopenharmony_ci	status &= ~BD_ENET_TX_STATS;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	status |= (BD_ENET_TX_TC | BD_ENET_TX_READY);
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	if (((unsigned long) data) & fep->tx_align ||
70662306a36Sopenharmony_ci		fep->quirks & FEC_QUIRK_SWAP_FRAME) {
70762306a36Sopenharmony_ci		memcpy(txq->tx_bounce[index], data, size);
70862306a36Sopenharmony_ci		data = txq->tx_bounce[index];
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci		if (fep->quirks & FEC_QUIRK_SWAP_FRAME)
71162306a36Sopenharmony_ci			swap_buffer(data, size);
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	addr = dma_map_single(&fep->pdev->dev, data, size, DMA_TO_DEVICE);
71562306a36Sopenharmony_ci	if (dma_mapping_error(&fep->pdev->dev, addr)) {
71662306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
71762306a36Sopenharmony_ci		if (net_ratelimit())
71862306a36Sopenharmony_ci			netdev_err(ndev, "Tx DMA memory map failed\n");
71962306a36Sopenharmony_ci		return NETDEV_TX_OK;
72062306a36Sopenharmony_ci	}
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	bdp->cbd_datlen = cpu_to_fec16(size);
72362306a36Sopenharmony_ci	bdp->cbd_bufaddr = cpu_to_fec32(addr);
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	if (fep->bufdesc_ex) {
72662306a36Sopenharmony_ci		if (fep->quirks & FEC_QUIRK_HAS_AVB)
72762306a36Sopenharmony_ci			estatus |= FEC_TX_BD_FTYPE(txq->bd.qid);
72862306a36Sopenharmony_ci		if (skb->ip_summed == CHECKSUM_PARTIAL)
72962306a36Sopenharmony_ci			estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS;
73062306a36Sopenharmony_ci		ebdp->cbd_bdu = 0;
73162306a36Sopenharmony_ci		ebdp->cbd_esc = cpu_to_fec32(estatus);
73262306a36Sopenharmony_ci	}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	/* Handle the last BD specially */
73562306a36Sopenharmony_ci	if (last_tcp)
73662306a36Sopenharmony_ci		status |= (BD_ENET_TX_LAST | BD_ENET_TX_TC);
73762306a36Sopenharmony_ci	if (is_last) {
73862306a36Sopenharmony_ci		status |= BD_ENET_TX_INTR;
73962306a36Sopenharmony_ci		if (fep->bufdesc_ex)
74062306a36Sopenharmony_ci			ebdp->cbd_esc |= cpu_to_fec32(BD_ENET_TX_INT);
74162306a36Sopenharmony_ci	}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	bdp->cbd_sc = cpu_to_fec16(status);
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	return 0;
74662306a36Sopenharmony_ci}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_cistatic int
74962306a36Sopenharmony_cifec_enet_txq_put_hdr_tso(struct fec_enet_priv_tx_q *txq,
75062306a36Sopenharmony_ci			 struct sk_buff *skb, struct net_device *ndev,
75162306a36Sopenharmony_ci			 struct bufdesc *bdp, int index)
75262306a36Sopenharmony_ci{
75362306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
75462306a36Sopenharmony_ci	int hdr_len = skb_tcp_all_headers(skb);
75562306a36Sopenharmony_ci	struct bufdesc_ex *ebdp = container_of(bdp, struct bufdesc_ex, desc);
75662306a36Sopenharmony_ci	void *bufaddr;
75762306a36Sopenharmony_ci	unsigned long dmabuf;
75862306a36Sopenharmony_ci	unsigned short status;
75962306a36Sopenharmony_ci	unsigned int estatus = 0;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	status = fec16_to_cpu(bdp->cbd_sc);
76262306a36Sopenharmony_ci	status &= ~BD_ENET_TX_STATS;
76362306a36Sopenharmony_ci	status |= (BD_ENET_TX_TC | BD_ENET_TX_READY);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	bufaddr = txq->tso_hdrs + index * TSO_HEADER_SIZE;
76662306a36Sopenharmony_ci	dmabuf = txq->tso_hdrs_dma + index * TSO_HEADER_SIZE;
76762306a36Sopenharmony_ci	if (((unsigned long)bufaddr) & fep->tx_align ||
76862306a36Sopenharmony_ci		fep->quirks & FEC_QUIRK_SWAP_FRAME) {
76962306a36Sopenharmony_ci		memcpy(txq->tx_bounce[index], skb->data, hdr_len);
77062306a36Sopenharmony_ci		bufaddr = txq->tx_bounce[index];
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci		if (fep->quirks & FEC_QUIRK_SWAP_FRAME)
77362306a36Sopenharmony_ci			swap_buffer(bufaddr, hdr_len);
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci		dmabuf = dma_map_single(&fep->pdev->dev, bufaddr,
77662306a36Sopenharmony_ci					hdr_len, DMA_TO_DEVICE);
77762306a36Sopenharmony_ci		if (dma_mapping_error(&fep->pdev->dev, dmabuf)) {
77862306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
77962306a36Sopenharmony_ci			if (net_ratelimit())
78062306a36Sopenharmony_ci				netdev_err(ndev, "Tx DMA memory map failed\n");
78162306a36Sopenharmony_ci			return NETDEV_TX_OK;
78262306a36Sopenharmony_ci		}
78362306a36Sopenharmony_ci	}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	bdp->cbd_bufaddr = cpu_to_fec32(dmabuf);
78662306a36Sopenharmony_ci	bdp->cbd_datlen = cpu_to_fec16(hdr_len);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	if (fep->bufdesc_ex) {
78962306a36Sopenharmony_ci		if (fep->quirks & FEC_QUIRK_HAS_AVB)
79062306a36Sopenharmony_ci			estatus |= FEC_TX_BD_FTYPE(txq->bd.qid);
79162306a36Sopenharmony_ci		if (skb->ip_summed == CHECKSUM_PARTIAL)
79262306a36Sopenharmony_ci			estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS;
79362306a36Sopenharmony_ci		ebdp->cbd_bdu = 0;
79462306a36Sopenharmony_ci		ebdp->cbd_esc = cpu_to_fec32(estatus);
79562306a36Sopenharmony_ci	}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	bdp->cbd_sc = cpu_to_fec16(status);
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	return 0;
80062306a36Sopenharmony_ci}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_cistatic int fec_enet_txq_submit_tso(struct fec_enet_priv_tx_q *txq,
80362306a36Sopenharmony_ci				   struct sk_buff *skb,
80462306a36Sopenharmony_ci				   struct net_device *ndev)
80562306a36Sopenharmony_ci{
80662306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
80762306a36Sopenharmony_ci	int hdr_len, total_len, data_left;
80862306a36Sopenharmony_ci	struct bufdesc *bdp = txq->bd.cur;
80962306a36Sopenharmony_ci	struct tso_t tso;
81062306a36Sopenharmony_ci	unsigned int index = 0;
81162306a36Sopenharmony_ci	int ret;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	if (tso_count_descs(skb) >= fec_enet_get_free_txdesc_num(txq)) {
81462306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
81562306a36Sopenharmony_ci		if (net_ratelimit())
81662306a36Sopenharmony_ci			netdev_err(ndev, "NOT enough BD for TSO!\n");
81762306a36Sopenharmony_ci		return NETDEV_TX_OK;
81862306a36Sopenharmony_ci	}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	/* Protocol checksum off-load for TCP and UDP. */
82162306a36Sopenharmony_ci	if (fec_enet_clear_csum(skb, ndev)) {
82262306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
82362306a36Sopenharmony_ci		return NETDEV_TX_OK;
82462306a36Sopenharmony_ci	}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	/* Initialize the TSO handler, and prepare the first payload */
82762306a36Sopenharmony_ci	hdr_len = tso_start(skb, &tso);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	total_len = skb->len - hdr_len;
83062306a36Sopenharmony_ci	while (total_len > 0) {
83162306a36Sopenharmony_ci		char *hdr;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci		index = fec_enet_get_bd_index(bdp, &txq->bd);
83462306a36Sopenharmony_ci		data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len);
83562306a36Sopenharmony_ci		total_len -= data_left;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci		/* prepare packet headers: MAC + IP + TCP */
83862306a36Sopenharmony_ci		hdr = txq->tso_hdrs + index * TSO_HEADER_SIZE;
83962306a36Sopenharmony_ci		tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0);
84062306a36Sopenharmony_ci		ret = fec_enet_txq_put_hdr_tso(txq, skb, ndev, bdp, index);
84162306a36Sopenharmony_ci		if (ret)
84262306a36Sopenharmony_ci			goto err_release;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci		while (data_left > 0) {
84562306a36Sopenharmony_ci			int size;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci			size = min_t(int, tso.size, data_left);
84862306a36Sopenharmony_ci			bdp = fec_enet_get_nextdesc(bdp, &txq->bd);
84962306a36Sopenharmony_ci			index = fec_enet_get_bd_index(bdp, &txq->bd);
85062306a36Sopenharmony_ci			ret = fec_enet_txq_put_data_tso(txq, skb, ndev,
85162306a36Sopenharmony_ci							bdp, index,
85262306a36Sopenharmony_ci							tso.data, size,
85362306a36Sopenharmony_ci							size == data_left,
85462306a36Sopenharmony_ci							total_len == 0);
85562306a36Sopenharmony_ci			if (ret)
85662306a36Sopenharmony_ci				goto err_release;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci			data_left -= size;
85962306a36Sopenharmony_ci			tso_build_data(skb, &tso, size);
86062306a36Sopenharmony_ci		}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci		bdp = fec_enet_get_nextdesc(bdp, &txq->bd);
86362306a36Sopenharmony_ci	}
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	/* Save skb pointer */
86662306a36Sopenharmony_ci	txq->tx_buf[index].buf_p = skb;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	skb_tx_timestamp(skb);
86962306a36Sopenharmony_ci	txq->bd.cur = bdp;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	/* Trigger transmission start */
87262306a36Sopenharmony_ci	if (!(fep->quirks & FEC_QUIRK_ERR007885) ||
87362306a36Sopenharmony_ci	    !readl(txq->bd.reg_desc_active) ||
87462306a36Sopenharmony_ci	    !readl(txq->bd.reg_desc_active) ||
87562306a36Sopenharmony_ci	    !readl(txq->bd.reg_desc_active) ||
87662306a36Sopenharmony_ci	    !readl(txq->bd.reg_desc_active))
87762306a36Sopenharmony_ci		writel(0, txq->bd.reg_desc_active);
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	return 0;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_cierr_release:
88262306a36Sopenharmony_ci	/* TODO: Release all used data descriptors for TSO */
88362306a36Sopenharmony_ci	return ret;
88462306a36Sopenharmony_ci}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_cistatic netdev_tx_t
88762306a36Sopenharmony_cifec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
88862306a36Sopenharmony_ci{
88962306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
89062306a36Sopenharmony_ci	int entries_free;
89162306a36Sopenharmony_ci	unsigned short queue;
89262306a36Sopenharmony_ci	struct fec_enet_priv_tx_q *txq;
89362306a36Sopenharmony_ci	struct netdev_queue *nq;
89462306a36Sopenharmony_ci	int ret;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	queue = skb_get_queue_mapping(skb);
89762306a36Sopenharmony_ci	txq = fep->tx_queue[queue];
89862306a36Sopenharmony_ci	nq = netdev_get_tx_queue(ndev, queue);
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	if (skb_is_gso(skb))
90162306a36Sopenharmony_ci		ret = fec_enet_txq_submit_tso(txq, skb, ndev);
90262306a36Sopenharmony_ci	else
90362306a36Sopenharmony_ci		ret = fec_enet_txq_submit_skb(txq, skb, ndev);
90462306a36Sopenharmony_ci	if (ret)
90562306a36Sopenharmony_ci		return ret;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	entries_free = fec_enet_get_free_txdesc_num(txq);
90862306a36Sopenharmony_ci	if (entries_free <= txq->tx_stop_threshold)
90962306a36Sopenharmony_ci		netif_tx_stop_queue(nq);
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	return NETDEV_TX_OK;
91262306a36Sopenharmony_ci}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci/* Init RX & TX buffer descriptors
91562306a36Sopenharmony_ci */
91662306a36Sopenharmony_cistatic void fec_enet_bd_init(struct net_device *dev)
91762306a36Sopenharmony_ci{
91862306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(dev);
91962306a36Sopenharmony_ci	struct fec_enet_priv_tx_q *txq;
92062306a36Sopenharmony_ci	struct fec_enet_priv_rx_q *rxq;
92162306a36Sopenharmony_ci	struct bufdesc *bdp;
92262306a36Sopenharmony_ci	unsigned int i;
92362306a36Sopenharmony_ci	unsigned int q;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	for (q = 0; q < fep->num_rx_queues; q++) {
92662306a36Sopenharmony_ci		/* Initialize the receive buffer descriptors. */
92762306a36Sopenharmony_ci		rxq = fep->rx_queue[q];
92862306a36Sopenharmony_ci		bdp = rxq->bd.base;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci		for (i = 0; i < rxq->bd.ring_size; i++) {
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci			/* Initialize the BD for every fragment in the page. */
93362306a36Sopenharmony_ci			if (bdp->cbd_bufaddr)
93462306a36Sopenharmony_ci				bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY);
93562306a36Sopenharmony_ci			else
93662306a36Sopenharmony_ci				bdp->cbd_sc = cpu_to_fec16(0);
93762306a36Sopenharmony_ci			bdp = fec_enet_get_nextdesc(bdp, &rxq->bd);
93862306a36Sopenharmony_ci		}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci		/* Set the last buffer to wrap */
94162306a36Sopenharmony_ci		bdp = fec_enet_get_prevdesc(bdp, &rxq->bd);
94262306a36Sopenharmony_ci		bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP);
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci		rxq->bd.cur = rxq->bd.base;
94562306a36Sopenharmony_ci	}
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	for (q = 0; q < fep->num_tx_queues; q++) {
94862306a36Sopenharmony_ci		/* ...and the same for transmit */
94962306a36Sopenharmony_ci		txq = fep->tx_queue[q];
95062306a36Sopenharmony_ci		bdp = txq->bd.base;
95162306a36Sopenharmony_ci		txq->bd.cur = bdp;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci		for (i = 0; i < txq->bd.ring_size; i++) {
95462306a36Sopenharmony_ci			/* Initialize the BD for every fragment in the page. */
95562306a36Sopenharmony_ci			bdp->cbd_sc = cpu_to_fec16(0);
95662306a36Sopenharmony_ci			if (txq->tx_buf[i].type == FEC_TXBUF_T_SKB) {
95762306a36Sopenharmony_ci				if (bdp->cbd_bufaddr &&
95862306a36Sopenharmony_ci				    !IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr)))
95962306a36Sopenharmony_ci					dma_unmap_single(&fep->pdev->dev,
96062306a36Sopenharmony_ci							 fec32_to_cpu(bdp->cbd_bufaddr),
96162306a36Sopenharmony_ci							 fec16_to_cpu(bdp->cbd_datlen),
96262306a36Sopenharmony_ci							 DMA_TO_DEVICE);
96362306a36Sopenharmony_ci				if (txq->tx_buf[i].buf_p)
96462306a36Sopenharmony_ci					dev_kfree_skb_any(txq->tx_buf[i].buf_p);
96562306a36Sopenharmony_ci			} else if (txq->tx_buf[i].type == FEC_TXBUF_T_XDP_NDO) {
96662306a36Sopenharmony_ci				if (bdp->cbd_bufaddr)
96762306a36Sopenharmony_ci					dma_unmap_single(&fep->pdev->dev,
96862306a36Sopenharmony_ci							 fec32_to_cpu(bdp->cbd_bufaddr),
96962306a36Sopenharmony_ci							 fec16_to_cpu(bdp->cbd_datlen),
97062306a36Sopenharmony_ci							 DMA_TO_DEVICE);
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci				if (txq->tx_buf[i].buf_p)
97362306a36Sopenharmony_ci					xdp_return_frame(txq->tx_buf[i].buf_p);
97462306a36Sopenharmony_ci			} else {
97562306a36Sopenharmony_ci				struct page *page = txq->tx_buf[i].buf_p;
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci				if (page)
97862306a36Sopenharmony_ci					page_pool_put_page(page->pp, page, 0, false);
97962306a36Sopenharmony_ci			}
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci			txq->tx_buf[i].buf_p = NULL;
98262306a36Sopenharmony_ci			/* restore default tx buffer type: FEC_TXBUF_T_SKB */
98362306a36Sopenharmony_ci			txq->tx_buf[i].type = FEC_TXBUF_T_SKB;
98462306a36Sopenharmony_ci			bdp->cbd_bufaddr = cpu_to_fec32(0);
98562306a36Sopenharmony_ci			bdp = fec_enet_get_nextdesc(bdp, &txq->bd);
98662306a36Sopenharmony_ci		}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci		/* Set the last buffer to wrap */
98962306a36Sopenharmony_ci		bdp = fec_enet_get_prevdesc(bdp, &txq->bd);
99062306a36Sopenharmony_ci		bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP);
99162306a36Sopenharmony_ci		txq->dirty_tx = bdp;
99262306a36Sopenharmony_ci	}
99362306a36Sopenharmony_ci}
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_cistatic void fec_enet_active_rxring(struct net_device *ndev)
99662306a36Sopenharmony_ci{
99762306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
99862306a36Sopenharmony_ci	int i;
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	for (i = 0; i < fep->num_rx_queues; i++)
100162306a36Sopenharmony_ci		writel(0, fep->rx_queue[i]->bd.reg_desc_active);
100262306a36Sopenharmony_ci}
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_cistatic void fec_enet_enable_ring(struct net_device *ndev)
100562306a36Sopenharmony_ci{
100662306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
100762306a36Sopenharmony_ci	struct fec_enet_priv_tx_q *txq;
100862306a36Sopenharmony_ci	struct fec_enet_priv_rx_q *rxq;
100962306a36Sopenharmony_ci	int i;
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	for (i = 0; i < fep->num_rx_queues; i++) {
101262306a36Sopenharmony_ci		rxq = fep->rx_queue[i];
101362306a36Sopenharmony_ci		writel(rxq->bd.dma, fep->hwp + FEC_R_DES_START(i));
101462306a36Sopenharmony_ci		writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_R_BUFF_SIZE(i));
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci		/* enable DMA1/2 */
101762306a36Sopenharmony_ci		if (i)
101862306a36Sopenharmony_ci			writel(RCMR_MATCHEN | RCMR_CMP(i),
101962306a36Sopenharmony_ci			       fep->hwp + FEC_RCMR(i));
102062306a36Sopenharmony_ci	}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	for (i = 0; i < fep->num_tx_queues; i++) {
102362306a36Sopenharmony_ci		txq = fep->tx_queue[i];
102462306a36Sopenharmony_ci		writel(txq->bd.dma, fep->hwp + FEC_X_DES_START(i));
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci		/* enable DMA1/2 */
102762306a36Sopenharmony_ci		if (i)
102862306a36Sopenharmony_ci			writel(DMA_CLASS_EN | IDLE_SLOPE(i),
102962306a36Sopenharmony_ci			       fep->hwp + FEC_DMA_CFG(i));
103062306a36Sopenharmony_ci	}
103162306a36Sopenharmony_ci}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci/*
103462306a36Sopenharmony_ci * This function is called to start or restart the FEC during a link
103562306a36Sopenharmony_ci * change, transmit timeout, or to reconfigure the FEC.  The network
103662306a36Sopenharmony_ci * packet processing for this device must be stopped before this call.
103762306a36Sopenharmony_ci */
103862306a36Sopenharmony_cistatic void
103962306a36Sopenharmony_cifec_restart(struct net_device *ndev)
104062306a36Sopenharmony_ci{
104162306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
104262306a36Sopenharmony_ci	u32 temp_mac[2];
104362306a36Sopenharmony_ci	u32 rcntl = OPT_FRAME_SIZE | 0x04;
104462306a36Sopenharmony_ci	u32 ecntl = 0x2; /* ETHEREN */
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	/* Whack a reset.  We should wait for this.
104762306a36Sopenharmony_ci	 * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
104862306a36Sopenharmony_ci	 * instead of reset MAC itself.
104962306a36Sopenharmony_ci	 */
105062306a36Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES ||
105162306a36Sopenharmony_ci	    ((fep->quirks & FEC_QUIRK_NO_HARD_RESET) && fep->link)) {
105262306a36Sopenharmony_ci		writel(0, fep->hwp + FEC_ECNTRL);
105362306a36Sopenharmony_ci	} else {
105462306a36Sopenharmony_ci		writel(1, fep->hwp + FEC_ECNTRL);
105562306a36Sopenharmony_ci		udelay(10);
105662306a36Sopenharmony_ci	}
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	/*
105962306a36Sopenharmony_ci	 * enet-mac reset will reset mac address registers too,
106062306a36Sopenharmony_ci	 * so need to reconfigure it.
106162306a36Sopenharmony_ci	 */
106262306a36Sopenharmony_ci	memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN);
106362306a36Sopenharmony_ci	writel((__force u32)cpu_to_be32(temp_mac[0]),
106462306a36Sopenharmony_ci	       fep->hwp + FEC_ADDR_LOW);
106562306a36Sopenharmony_ci	writel((__force u32)cpu_to_be32(temp_mac[1]),
106662306a36Sopenharmony_ci	       fep->hwp + FEC_ADDR_HIGH);
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	/* Clear any outstanding interrupt, except MDIO. */
106962306a36Sopenharmony_ci	writel((0xffffffff & ~FEC_ENET_MII), fep->hwp + FEC_IEVENT);
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	fec_enet_bd_init(ndev);
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	fec_enet_enable_ring(ndev);
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	/* Enable MII mode */
107662306a36Sopenharmony_ci	if (fep->full_duplex == DUPLEX_FULL) {
107762306a36Sopenharmony_ci		/* FD enable */
107862306a36Sopenharmony_ci		writel(0x04, fep->hwp + FEC_X_CNTRL);
107962306a36Sopenharmony_ci	} else {
108062306a36Sopenharmony_ci		/* No Rcv on Xmit */
108162306a36Sopenharmony_ci		rcntl |= 0x02;
108262306a36Sopenharmony_ci		writel(0x0, fep->hwp + FEC_X_CNTRL);
108362306a36Sopenharmony_ci	}
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	/* Set MII speed */
108662306a36Sopenharmony_ci	writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci#if !defined(CONFIG_M5272)
108962306a36Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_HAS_RACC) {
109062306a36Sopenharmony_ci		u32 val = readl(fep->hwp + FEC_RACC);
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci		/* align IP header */
109362306a36Sopenharmony_ci		val |= FEC_RACC_SHIFT16;
109462306a36Sopenharmony_ci		if (fep->csum_flags & FLAG_RX_CSUM_ENABLED)
109562306a36Sopenharmony_ci			/* set RX checksum */
109662306a36Sopenharmony_ci			val |= FEC_RACC_OPTIONS;
109762306a36Sopenharmony_ci		else
109862306a36Sopenharmony_ci			val &= ~FEC_RACC_OPTIONS;
109962306a36Sopenharmony_ci		writel(val, fep->hwp + FEC_RACC);
110062306a36Sopenharmony_ci		writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_FTRL);
110162306a36Sopenharmony_ci	}
110262306a36Sopenharmony_ci#endif
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	/*
110562306a36Sopenharmony_ci	 * The phy interface and speed need to get configured
110662306a36Sopenharmony_ci	 * differently on enet-mac.
110762306a36Sopenharmony_ci	 */
110862306a36Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_ENET_MAC) {
110962306a36Sopenharmony_ci		/* Enable flow control and length check */
111062306a36Sopenharmony_ci		rcntl |= 0x40000000 | 0x00000020;
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci		/* RGMII, RMII or MII */
111362306a36Sopenharmony_ci		if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII ||
111462306a36Sopenharmony_ci		    fep->phy_interface == PHY_INTERFACE_MODE_RGMII_ID ||
111562306a36Sopenharmony_ci		    fep->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID ||
111662306a36Sopenharmony_ci		    fep->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID)
111762306a36Sopenharmony_ci			rcntl |= (1 << 6);
111862306a36Sopenharmony_ci		else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII)
111962306a36Sopenharmony_ci			rcntl |= (1 << 8);
112062306a36Sopenharmony_ci		else
112162306a36Sopenharmony_ci			rcntl &= ~(1 << 8);
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci		/* 1G, 100M or 10M */
112462306a36Sopenharmony_ci		if (ndev->phydev) {
112562306a36Sopenharmony_ci			if (ndev->phydev->speed == SPEED_1000)
112662306a36Sopenharmony_ci				ecntl |= (1 << 5);
112762306a36Sopenharmony_ci			else if (ndev->phydev->speed == SPEED_100)
112862306a36Sopenharmony_ci				rcntl &= ~(1 << 9);
112962306a36Sopenharmony_ci			else
113062306a36Sopenharmony_ci				rcntl |= (1 << 9);
113162306a36Sopenharmony_ci		}
113262306a36Sopenharmony_ci	} else {
113362306a36Sopenharmony_ci#ifdef FEC_MIIGSK_ENR
113462306a36Sopenharmony_ci		if (fep->quirks & FEC_QUIRK_USE_GASKET) {
113562306a36Sopenharmony_ci			u32 cfgr;
113662306a36Sopenharmony_ci			/* disable the gasket and wait */
113762306a36Sopenharmony_ci			writel(0, fep->hwp + FEC_MIIGSK_ENR);
113862306a36Sopenharmony_ci			while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4)
113962306a36Sopenharmony_ci				udelay(1);
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci			/*
114262306a36Sopenharmony_ci			 * configure the gasket:
114362306a36Sopenharmony_ci			 *   RMII, 50 MHz, no loopback, no echo
114462306a36Sopenharmony_ci			 *   MII, 25 MHz, no loopback, no echo
114562306a36Sopenharmony_ci			 */
114662306a36Sopenharmony_ci			cfgr = (fep->phy_interface == PHY_INTERFACE_MODE_RMII)
114762306a36Sopenharmony_ci				? BM_MIIGSK_CFGR_RMII : BM_MIIGSK_CFGR_MII;
114862306a36Sopenharmony_ci			if (ndev->phydev && ndev->phydev->speed == SPEED_10)
114962306a36Sopenharmony_ci				cfgr |= BM_MIIGSK_CFGR_FRCONT_10M;
115062306a36Sopenharmony_ci			writel(cfgr, fep->hwp + FEC_MIIGSK_CFGR);
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci			/* re-enable the gasket */
115362306a36Sopenharmony_ci			writel(2, fep->hwp + FEC_MIIGSK_ENR);
115462306a36Sopenharmony_ci		}
115562306a36Sopenharmony_ci#endif
115662306a36Sopenharmony_ci	}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci#if !defined(CONFIG_M5272)
115962306a36Sopenharmony_ci	/* enable pause frame*/
116062306a36Sopenharmony_ci	if ((fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) ||
116162306a36Sopenharmony_ci	    ((fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) &&
116262306a36Sopenharmony_ci	     ndev->phydev && ndev->phydev->pause)) {
116362306a36Sopenharmony_ci		rcntl |= FEC_ENET_FCE;
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci		/* set FIFO threshold parameter to reduce overrun */
116662306a36Sopenharmony_ci		writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM);
116762306a36Sopenharmony_ci		writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL);
116862306a36Sopenharmony_ci		writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM);
116962306a36Sopenharmony_ci		writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL);
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci		/* OPD */
117262306a36Sopenharmony_ci		writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD);
117362306a36Sopenharmony_ci	} else {
117462306a36Sopenharmony_ci		rcntl &= ~FEC_ENET_FCE;
117562306a36Sopenharmony_ci	}
117662306a36Sopenharmony_ci#endif /* !defined(CONFIG_M5272) */
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	writel(rcntl, fep->hwp + FEC_R_CNTRL);
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	/* Setup multicast filter. */
118162306a36Sopenharmony_ci	set_multicast_list(ndev);
118262306a36Sopenharmony_ci#ifndef CONFIG_M5272
118362306a36Sopenharmony_ci	writel(0, fep->hwp + FEC_HASH_TABLE_HIGH);
118462306a36Sopenharmony_ci	writel(0, fep->hwp + FEC_HASH_TABLE_LOW);
118562306a36Sopenharmony_ci#endif
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_ENET_MAC) {
118862306a36Sopenharmony_ci		/* enable ENET endian swap */
118962306a36Sopenharmony_ci		ecntl |= (1 << 8);
119062306a36Sopenharmony_ci		/* enable ENET store and forward mode */
119162306a36Sopenharmony_ci		writel(1 << 8, fep->hwp + FEC_X_WMRK);
119262306a36Sopenharmony_ci	}
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	if (fep->bufdesc_ex)
119562306a36Sopenharmony_ci		ecntl |= (1 << 4);
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_DELAYED_CLKS_SUPPORT &&
119862306a36Sopenharmony_ci	    fep->rgmii_txc_dly)
119962306a36Sopenharmony_ci		ecntl |= FEC_ENET_TXC_DLY;
120062306a36Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_DELAYED_CLKS_SUPPORT &&
120162306a36Sopenharmony_ci	    fep->rgmii_rxc_dly)
120262306a36Sopenharmony_ci		ecntl |= FEC_ENET_RXC_DLY;
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci#ifndef CONFIG_M5272
120562306a36Sopenharmony_ci	/* Enable the MIB statistic event counters */
120662306a36Sopenharmony_ci	writel(0 << 31, fep->hwp + FEC_MIB_CTRLSTAT);
120762306a36Sopenharmony_ci#endif
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	/* And last, enable the transmit and receive processing */
121062306a36Sopenharmony_ci	writel(ecntl, fep->hwp + FEC_ECNTRL);
121162306a36Sopenharmony_ci	fec_enet_active_rxring(ndev);
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	if (fep->bufdesc_ex)
121462306a36Sopenharmony_ci		fec_ptp_start_cyclecounter(ndev);
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	/* Enable interrupts we wish to service */
121762306a36Sopenharmony_ci	if (fep->link)
121862306a36Sopenharmony_ci		writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
121962306a36Sopenharmony_ci	else
122062306a36Sopenharmony_ci		writel(0, fep->hwp + FEC_IMASK);
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	/* Init the interrupt coalescing */
122362306a36Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_HAS_COALESCE)
122462306a36Sopenharmony_ci		fec_enet_itr_coal_set(ndev);
122562306a36Sopenharmony_ci}
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_cistatic int fec_enet_ipc_handle_init(struct fec_enet_private *fep)
122862306a36Sopenharmony_ci{
122962306a36Sopenharmony_ci	if (!(of_machine_is_compatible("fsl,imx8qm") ||
123062306a36Sopenharmony_ci	      of_machine_is_compatible("fsl,imx8qxp") ||
123162306a36Sopenharmony_ci	      of_machine_is_compatible("fsl,imx8dxl")))
123262306a36Sopenharmony_ci		return 0;
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci	return imx_scu_get_handle(&fep->ipc_handle);
123562306a36Sopenharmony_ci}
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_cistatic void fec_enet_ipg_stop_set(struct fec_enet_private *fep, bool enabled)
123862306a36Sopenharmony_ci{
123962306a36Sopenharmony_ci	struct device_node *np = fep->pdev->dev.of_node;
124062306a36Sopenharmony_ci	u32 rsrc_id, val;
124162306a36Sopenharmony_ci	int idx;
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	if (!np || !fep->ipc_handle)
124462306a36Sopenharmony_ci		return;
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	idx = of_alias_get_id(np, "ethernet");
124762306a36Sopenharmony_ci	if (idx < 0)
124862306a36Sopenharmony_ci		idx = 0;
124962306a36Sopenharmony_ci	rsrc_id = idx ? IMX_SC_R_ENET_1 : IMX_SC_R_ENET_0;
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	val = enabled ? 1 : 0;
125262306a36Sopenharmony_ci	imx_sc_misc_set_control(fep->ipc_handle, rsrc_id, IMX_SC_C_IPG_STOP, val);
125362306a36Sopenharmony_ci}
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_cistatic void fec_enet_stop_mode(struct fec_enet_private *fep, bool enabled)
125662306a36Sopenharmony_ci{
125762306a36Sopenharmony_ci	struct fec_platform_data *pdata = fep->pdev->dev.platform_data;
125862306a36Sopenharmony_ci	struct fec_stop_mode_gpr *stop_gpr = &fep->stop_gpr;
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	if (stop_gpr->gpr) {
126162306a36Sopenharmony_ci		if (enabled)
126262306a36Sopenharmony_ci			regmap_update_bits(stop_gpr->gpr, stop_gpr->reg,
126362306a36Sopenharmony_ci					   BIT(stop_gpr->bit),
126462306a36Sopenharmony_ci					   BIT(stop_gpr->bit));
126562306a36Sopenharmony_ci		else
126662306a36Sopenharmony_ci			regmap_update_bits(stop_gpr->gpr, stop_gpr->reg,
126762306a36Sopenharmony_ci					   BIT(stop_gpr->bit), 0);
126862306a36Sopenharmony_ci	} else if (pdata && pdata->sleep_mode_enable) {
126962306a36Sopenharmony_ci		pdata->sleep_mode_enable(enabled);
127062306a36Sopenharmony_ci	} else {
127162306a36Sopenharmony_ci		fec_enet_ipg_stop_set(fep, enabled);
127262306a36Sopenharmony_ci	}
127362306a36Sopenharmony_ci}
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_cistatic void fec_irqs_disable(struct net_device *ndev)
127662306a36Sopenharmony_ci{
127762306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	writel(0, fep->hwp + FEC_IMASK);
128062306a36Sopenharmony_ci}
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_cistatic void fec_irqs_disable_except_wakeup(struct net_device *ndev)
128362306a36Sopenharmony_ci{
128462306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	writel(0, fep->hwp + FEC_IMASK);
128762306a36Sopenharmony_ci	writel(FEC_ENET_WAKEUP, fep->hwp + FEC_IMASK);
128862306a36Sopenharmony_ci}
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_cistatic void
129162306a36Sopenharmony_cifec_stop(struct net_device *ndev)
129262306a36Sopenharmony_ci{
129362306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
129462306a36Sopenharmony_ci	u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8);
129562306a36Sopenharmony_ci	u32 val;
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	/* We cannot expect a graceful transmit stop without link !!! */
129862306a36Sopenharmony_ci	if (fep->link) {
129962306a36Sopenharmony_ci		writel(1, fep->hwp + FEC_X_CNTRL); /* Graceful transmit stop */
130062306a36Sopenharmony_ci		udelay(10);
130162306a36Sopenharmony_ci		if (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_GRA))
130262306a36Sopenharmony_ci			netdev_err(ndev, "Graceful transmit stop did not complete!\n");
130362306a36Sopenharmony_ci	}
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	/* Whack a reset.  We should wait for this.
130662306a36Sopenharmony_ci	 * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
130762306a36Sopenharmony_ci	 * instead of reset MAC itself.
130862306a36Sopenharmony_ci	 */
130962306a36Sopenharmony_ci	if (!(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) {
131062306a36Sopenharmony_ci		if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) {
131162306a36Sopenharmony_ci			writel(0, fep->hwp + FEC_ECNTRL);
131262306a36Sopenharmony_ci		} else {
131362306a36Sopenharmony_ci			writel(1, fep->hwp + FEC_ECNTRL);
131462306a36Sopenharmony_ci			udelay(10);
131562306a36Sopenharmony_ci		}
131662306a36Sopenharmony_ci	} else {
131762306a36Sopenharmony_ci		val = readl(fep->hwp + FEC_ECNTRL);
131862306a36Sopenharmony_ci		val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
131962306a36Sopenharmony_ci		writel(val, fep->hwp + FEC_ECNTRL);
132062306a36Sopenharmony_ci	}
132162306a36Sopenharmony_ci	writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
132262306a36Sopenharmony_ci	writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	/* We have to keep ENET enabled to have MII interrupt stay working */
132562306a36Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_ENET_MAC &&
132662306a36Sopenharmony_ci		!(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) {
132762306a36Sopenharmony_ci		writel(2, fep->hwp + FEC_ECNTRL);
132862306a36Sopenharmony_ci		writel(rmii_mode, fep->hwp + FEC_R_CNTRL);
132962306a36Sopenharmony_ci	}
133062306a36Sopenharmony_ci}
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_cistatic void
133462306a36Sopenharmony_cifec_timeout(struct net_device *ndev, unsigned int txqueue)
133562306a36Sopenharmony_ci{
133662306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	fec_dump(ndev);
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	ndev->stats.tx_errors++;
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	schedule_work(&fep->tx_timeout_work);
134362306a36Sopenharmony_ci}
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_cistatic void fec_enet_timeout_work(struct work_struct *work)
134662306a36Sopenharmony_ci{
134762306a36Sopenharmony_ci	struct fec_enet_private *fep =
134862306a36Sopenharmony_ci		container_of(work, struct fec_enet_private, tx_timeout_work);
134962306a36Sopenharmony_ci	struct net_device *ndev = fep->netdev;
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	rtnl_lock();
135262306a36Sopenharmony_ci	if (netif_device_present(ndev) || netif_running(ndev)) {
135362306a36Sopenharmony_ci		napi_disable(&fep->napi);
135462306a36Sopenharmony_ci		netif_tx_lock_bh(ndev);
135562306a36Sopenharmony_ci		fec_restart(ndev);
135662306a36Sopenharmony_ci		netif_tx_wake_all_queues(ndev);
135762306a36Sopenharmony_ci		netif_tx_unlock_bh(ndev);
135862306a36Sopenharmony_ci		napi_enable(&fep->napi);
135962306a36Sopenharmony_ci	}
136062306a36Sopenharmony_ci	rtnl_unlock();
136162306a36Sopenharmony_ci}
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_cistatic void
136462306a36Sopenharmony_cifec_enet_hwtstamp(struct fec_enet_private *fep, unsigned ts,
136562306a36Sopenharmony_ci	struct skb_shared_hwtstamps *hwtstamps)
136662306a36Sopenharmony_ci{
136762306a36Sopenharmony_ci	unsigned long flags;
136862306a36Sopenharmony_ci	u64 ns;
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	spin_lock_irqsave(&fep->tmreg_lock, flags);
137162306a36Sopenharmony_ci	ns = timecounter_cyc2time(&fep->tc, ts);
137262306a36Sopenharmony_ci	spin_unlock_irqrestore(&fep->tmreg_lock, flags);
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	memset(hwtstamps, 0, sizeof(*hwtstamps));
137562306a36Sopenharmony_ci	hwtstamps->hwtstamp = ns_to_ktime(ns);
137662306a36Sopenharmony_ci}
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_cistatic void
137962306a36Sopenharmony_cifec_enet_tx_queue(struct net_device *ndev, u16 queue_id, int budget)
138062306a36Sopenharmony_ci{
138162306a36Sopenharmony_ci	struct	fec_enet_private *fep;
138262306a36Sopenharmony_ci	struct xdp_frame *xdpf;
138362306a36Sopenharmony_ci	struct bufdesc *bdp;
138462306a36Sopenharmony_ci	unsigned short status;
138562306a36Sopenharmony_ci	struct	sk_buff	*skb;
138662306a36Sopenharmony_ci	struct fec_enet_priv_tx_q *txq;
138762306a36Sopenharmony_ci	struct netdev_queue *nq;
138862306a36Sopenharmony_ci	int	index = 0;
138962306a36Sopenharmony_ci	int	entries_free;
139062306a36Sopenharmony_ci	struct page *page;
139162306a36Sopenharmony_ci	int frame_len;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	fep = netdev_priv(ndev);
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	txq = fep->tx_queue[queue_id];
139662306a36Sopenharmony_ci	/* get next bdp of dirty_tx */
139762306a36Sopenharmony_ci	nq = netdev_get_tx_queue(ndev, queue_id);
139862306a36Sopenharmony_ci	bdp = txq->dirty_tx;
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	/* get next bdp of dirty_tx */
140162306a36Sopenharmony_ci	bdp = fec_enet_get_nextdesc(bdp, &txq->bd);
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	while (bdp != READ_ONCE(txq->bd.cur)) {
140462306a36Sopenharmony_ci		/* Order the load of bd.cur and cbd_sc */
140562306a36Sopenharmony_ci		rmb();
140662306a36Sopenharmony_ci		status = fec16_to_cpu(READ_ONCE(bdp->cbd_sc));
140762306a36Sopenharmony_ci		if (status & BD_ENET_TX_READY)
140862306a36Sopenharmony_ci			break;
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci		index = fec_enet_get_bd_index(bdp, &txq->bd);
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci		if (txq->tx_buf[index].type == FEC_TXBUF_T_SKB) {
141362306a36Sopenharmony_ci			skb = txq->tx_buf[index].buf_p;
141462306a36Sopenharmony_ci			if (bdp->cbd_bufaddr &&
141562306a36Sopenharmony_ci			    !IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr)))
141662306a36Sopenharmony_ci				dma_unmap_single(&fep->pdev->dev,
141762306a36Sopenharmony_ci						 fec32_to_cpu(bdp->cbd_bufaddr),
141862306a36Sopenharmony_ci						 fec16_to_cpu(bdp->cbd_datlen),
141962306a36Sopenharmony_ci						 DMA_TO_DEVICE);
142062306a36Sopenharmony_ci			bdp->cbd_bufaddr = cpu_to_fec32(0);
142162306a36Sopenharmony_ci			if (!skb)
142262306a36Sopenharmony_ci				goto tx_buf_done;
142362306a36Sopenharmony_ci		} else {
142462306a36Sopenharmony_ci			/* Tx processing cannot call any XDP (or page pool) APIs if
142562306a36Sopenharmony_ci			 * the "budget" is 0. Because NAPI is called with budget of
142662306a36Sopenharmony_ci			 * 0 (such as netpoll) indicates we may be in an IRQ context,
142762306a36Sopenharmony_ci			 * however, we can't use the page pool from IRQ context.
142862306a36Sopenharmony_ci			 */
142962306a36Sopenharmony_ci			if (unlikely(!budget))
143062306a36Sopenharmony_ci				break;
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci			if (txq->tx_buf[index].type == FEC_TXBUF_T_XDP_NDO) {
143362306a36Sopenharmony_ci				xdpf = txq->tx_buf[index].buf_p;
143462306a36Sopenharmony_ci				if (bdp->cbd_bufaddr)
143562306a36Sopenharmony_ci					dma_unmap_single(&fep->pdev->dev,
143662306a36Sopenharmony_ci							 fec32_to_cpu(bdp->cbd_bufaddr),
143762306a36Sopenharmony_ci							 fec16_to_cpu(bdp->cbd_datlen),
143862306a36Sopenharmony_ci							 DMA_TO_DEVICE);
143962306a36Sopenharmony_ci			} else {
144062306a36Sopenharmony_ci				page = txq->tx_buf[index].buf_p;
144162306a36Sopenharmony_ci			}
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci			bdp->cbd_bufaddr = cpu_to_fec32(0);
144462306a36Sopenharmony_ci			if (unlikely(!txq->tx_buf[index].buf_p)) {
144562306a36Sopenharmony_ci				txq->tx_buf[index].type = FEC_TXBUF_T_SKB;
144662306a36Sopenharmony_ci				goto tx_buf_done;
144762306a36Sopenharmony_ci			}
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci			frame_len = fec16_to_cpu(bdp->cbd_datlen);
145062306a36Sopenharmony_ci		}
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci		/* Check for errors. */
145362306a36Sopenharmony_ci		if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC |
145462306a36Sopenharmony_ci				   BD_ENET_TX_RL | BD_ENET_TX_UN |
145562306a36Sopenharmony_ci				   BD_ENET_TX_CSL)) {
145662306a36Sopenharmony_ci			ndev->stats.tx_errors++;
145762306a36Sopenharmony_ci			if (status & BD_ENET_TX_HB)  /* No heartbeat */
145862306a36Sopenharmony_ci				ndev->stats.tx_heartbeat_errors++;
145962306a36Sopenharmony_ci			if (status & BD_ENET_TX_LC)  /* Late collision */
146062306a36Sopenharmony_ci				ndev->stats.tx_window_errors++;
146162306a36Sopenharmony_ci			if (status & BD_ENET_TX_RL)  /* Retrans limit */
146262306a36Sopenharmony_ci				ndev->stats.tx_aborted_errors++;
146362306a36Sopenharmony_ci			if (status & BD_ENET_TX_UN)  /* Underrun */
146462306a36Sopenharmony_ci				ndev->stats.tx_fifo_errors++;
146562306a36Sopenharmony_ci			if (status & BD_ENET_TX_CSL) /* Carrier lost */
146662306a36Sopenharmony_ci				ndev->stats.tx_carrier_errors++;
146762306a36Sopenharmony_ci		} else {
146862306a36Sopenharmony_ci			ndev->stats.tx_packets++;
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci			if (txq->tx_buf[index].type == FEC_TXBUF_T_SKB)
147162306a36Sopenharmony_ci				ndev->stats.tx_bytes += skb->len;
147262306a36Sopenharmony_ci			else
147362306a36Sopenharmony_ci				ndev->stats.tx_bytes += frame_len;
147462306a36Sopenharmony_ci		}
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci		/* Deferred means some collisions occurred during transmit,
147762306a36Sopenharmony_ci		 * but we eventually sent the packet OK.
147862306a36Sopenharmony_ci		 */
147962306a36Sopenharmony_ci		if (status & BD_ENET_TX_DEF)
148062306a36Sopenharmony_ci			ndev->stats.collisions++;
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci		if (txq->tx_buf[index].type == FEC_TXBUF_T_SKB) {
148362306a36Sopenharmony_ci			/* NOTE: SKBTX_IN_PROGRESS being set does not imply it's we who
148462306a36Sopenharmony_ci			 * are to time stamp the packet, so we still need to check time
148562306a36Sopenharmony_ci			 * stamping enabled flag.
148662306a36Sopenharmony_ci			 */
148762306a36Sopenharmony_ci			if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS &&
148862306a36Sopenharmony_ci				     fep->hwts_tx_en) && fep->bufdesc_ex) {
148962306a36Sopenharmony_ci				struct skb_shared_hwtstamps shhwtstamps;
149062306a36Sopenharmony_ci				struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci				fec_enet_hwtstamp(fep, fec32_to_cpu(ebdp->ts), &shhwtstamps);
149362306a36Sopenharmony_ci				skb_tstamp_tx(skb, &shhwtstamps);
149462306a36Sopenharmony_ci			}
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci			/* Free the sk buffer associated with this last transmit */
149762306a36Sopenharmony_ci			napi_consume_skb(skb, budget);
149862306a36Sopenharmony_ci		} else if (txq->tx_buf[index].type == FEC_TXBUF_T_XDP_NDO) {
149962306a36Sopenharmony_ci			xdp_return_frame_rx_napi(xdpf);
150062306a36Sopenharmony_ci		} else { /* recycle pages of XDP_TX frames */
150162306a36Sopenharmony_ci			/* The dma_sync_size = 0 as XDP_TX has already synced DMA for_device */
150262306a36Sopenharmony_ci			page_pool_put_page(page->pp, page, 0, true);
150362306a36Sopenharmony_ci		}
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci		txq->tx_buf[index].buf_p = NULL;
150662306a36Sopenharmony_ci		/* restore default tx buffer type: FEC_TXBUF_T_SKB */
150762306a36Sopenharmony_ci		txq->tx_buf[index].type = FEC_TXBUF_T_SKB;
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_citx_buf_done:
151062306a36Sopenharmony_ci		/* Make sure the update to bdp and tx_buf are performed
151162306a36Sopenharmony_ci		 * before dirty_tx
151262306a36Sopenharmony_ci		 */
151362306a36Sopenharmony_ci		wmb();
151462306a36Sopenharmony_ci		txq->dirty_tx = bdp;
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci		/* Update pointer to next buffer descriptor to be transmitted */
151762306a36Sopenharmony_ci		bdp = fec_enet_get_nextdesc(bdp, &txq->bd);
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci		/* Since we have freed up a buffer, the ring is no longer full
152062306a36Sopenharmony_ci		 */
152162306a36Sopenharmony_ci		if (netif_tx_queue_stopped(nq)) {
152262306a36Sopenharmony_ci			entries_free = fec_enet_get_free_txdesc_num(txq);
152362306a36Sopenharmony_ci			if (entries_free >= txq->tx_wake_threshold)
152462306a36Sopenharmony_ci				netif_tx_wake_queue(nq);
152562306a36Sopenharmony_ci		}
152662306a36Sopenharmony_ci	}
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	/* ERR006358: Keep the transmitter going */
152962306a36Sopenharmony_ci	if (bdp != txq->bd.cur &&
153062306a36Sopenharmony_ci	    readl(txq->bd.reg_desc_active) == 0)
153162306a36Sopenharmony_ci		writel(0, txq->bd.reg_desc_active);
153262306a36Sopenharmony_ci}
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_cistatic void fec_enet_tx(struct net_device *ndev, int budget)
153562306a36Sopenharmony_ci{
153662306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
153762306a36Sopenharmony_ci	int i;
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	/* Make sure that AVB queues are processed first. */
154062306a36Sopenharmony_ci	for (i = fep->num_tx_queues - 1; i >= 0; i--)
154162306a36Sopenharmony_ci		fec_enet_tx_queue(ndev, i, budget);
154262306a36Sopenharmony_ci}
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_cistatic void fec_enet_update_cbd(struct fec_enet_priv_rx_q *rxq,
154562306a36Sopenharmony_ci				struct bufdesc *bdp, int index)
154662306a36Sopenharmony_ci{
154762306a36Sopenharmony_ci	struct page *new_page;
154862306a36Sopenharmony_ci	dma_addr_t phys_addr;
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	new_page = page_pool_dev_alloc_pages(rxq->page_pool);
155162306a36Sopenharmony_ci	WARN_ON(!new_page);
155262306a36Sopenharmony_ci	rxq->rx_skb_info[index].page = new_page;
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci	rxq->rx_skb_info[index].offset = FEC_ENET_XDP_HEADROOM;
155562306a36Sopenharmony_ci	phys_addr = page_pool_get_dma_addr(new_page) + FEC_ENET_XDP_HEADROOM;
155662306a36Sopenharmony_ci	bdp->cbd_bufaddr = cpu_to_fec32(phys_addr);
155762306a36Sopenharmony_ci}
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_cistatic u32
156062306a36Sopenharmony_cifec_enet_run_xdp(struct fec_enet_private *fep, struct bpf_prog *prog,
156162306a36Sopenharmony_ci		 struct xdp_buff *xdp, struct fec_enet_priv_rx_q *rxq, int cpu)
156262306a36Sopenharmony_ci{
156362306a36Sopenharmony_ci	unsigned int sync, len = xdp->data_end - xdp->data;
156462306a36Sopenharmony_ci	u32 ret = FEC_ENET_XDP_PASS;
156562306a36Sopenharmony_ci	struct page *page;
156662306a36Sopenharmony_ci	int err;
156762306a36Sopenharmony_ci	u32 act;
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci	act = bpf_prog_run_xdp(prog, xdp);
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	/* Due xdp_adjust_tail and xdp_adjust_head: DMA sync for_device cover
157262306a36Sopenharmony_ci	 * max len CPU touch
157362306a36Sopenharmony_ci	 */
157462306a36Sopenharmony_ci	sync = xdp->data_end - xdp->data;
157562306a36Sopenharmony_ci	sync = max(sync, len);
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci	switch (act) {
157862306a36Sopenharmony_ci	case XDP_PASS:
157962306a36Sopenharmony_ci		rxq->stats[RX_XDP_PASS]++;
158062306a36Sopenharmony_ci		ret = FEC_ENET_XDP_PASS;
158162306a36Sopenharmony_ci		break;
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	case XDP_REDIRECT:
158462306a36Sopenharmony_ci		rxq->stats[RX_XDP_REDIRECT]++;
158562306a36Sopenharmony_ci		err = xdp_do_redirect(fep->netdev, xdp, prog);
158662306a36Sopenharmony_ci		if (unlikely(err))
158762306a36Sopenharmony_ci			goto xdp_err;
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci		ret = FEC_ENET_XDP_REDIR;
159062306a36Sopenharmony_ci		break;
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	case XDP_TX:
159362306a36Sopenharmony_ci		rxq->stats[RX_XDP_TX]++;
159462306a36Sopenharmony_ci		err = fec_enet_xdp_tx_xmit(fep, cpu, xdp, sync);
159562306a36Sopenharmony_ci		if (unlikely(err)) {
159662306a36Sopenharmony_ci			rxq->stats[RX_XDP_TX_ERRORS]++;
159762306a36Sopenharmony_ci			goto xdp_err;
159862306a36Sopenharmony_ci		}
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ci		ret = FEC_ENET_XDP_TX;
160162306a36Sopenharmony_ci		break;
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci	default:
160462306a36Sopenharmony_ci		bpf_warn_invalid_xdp_action(fep->netdev, prog, act);
160562306a36Sopenharmony_ci		fallthrough;
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	case XDP_ABORTED:
160862306a36Sopenharmony_ci		fallthrough;    /* handle aborts by dropping packet */
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	case XDP_DROP:
161162306a36Sopenharmony_ci		rxq->stats[RX_XDP_DROP]++;
161262306a36Sopenharmony_cixdp_err:
161362306a36Sopenharmony_ci		ret = FEC_ENET_XDP_CONSUMED;
161462306a36Sopenharmony_ci		page = virt_to_head_page(xdp->data);
161562306a36Sopenharmony_ci		page_pool_put_page(rxq->page_pool, page, sync, true);
161662306a36Sopenharmony_ci		if (act != XDP_DROP)
161762306a36Sopenharmony_ci			trace_xdp_exception(fep->netdev, prog, act);
161862306a36Sopenharmony_ci		break;
161962306a36Sopenharmony_ci	}
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	return ret;
162262306a36Sopenharmony_ci}
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci/* During a receive, the bd_rx.cur points to the current incoming buffer.
162562306a36Sopenharmony_ci * When we update through the ring, if the next incoming buffer has
162662306a36Sopenharmony_ci * not been given to the system, we just set the empty indicator,
162762306a36Sopenharmony_ci * effectively tossing the packet.
162862306a36Sopenharmony_ci */
162962306a36Sopenharmony_cistatic int
163062306a36Sopenharmony_cifec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)
163162306a36Sopenharmony_ci{
163262306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
163362306a36Sopenharmony_ci	struct fec_enet_priv_rx_q *rxq;
163462306a36Sopenharmony_ci	struct bufdesc *bdp;
163562306a36Sopenharmony_ci	unsigned short status;
163662306a36Sopenharmony_ci	struct  sk_buff *skb;
163762306a36Sopenharmony_ci	ushort	pkt_len;
163862306a36Sopenharmony_ci	__u8 *data;
163962306a36Sopenharmony_ci	int	pkt_received = 0;
164062306a36Sopenharmony_ci	struct	bufdesc_ex *ebdp = NULL;
164162306a36Sopenharmony_ci	bool	vlan_packet_rcvd = false;
164262306a36Sopenharmony_ci	u16	vlan_tag;
164362306a36Sopenharmony_ci	int	index = 0;
164462306a36Sopenharmony_ci	bool	need_swap = fep->quirks & FEC_QUIRK_SWAP_FRAME;
164562306a36Sopenharmony_ci	struct bpf_prog *xdp_prog = READ_ONCE(fep->xdp_prog);
164662306a36Sopenharmony_ci	u32 ret, xdp_result = FEC_ENET_XDP_PASS;
164762306a36Sopenharmony_ci	u32 data_start = FEC_ENET_XDP_HEADROOM;
164862306a36Sopenharmony_ci	int cpu = smp_processor_id();
164962306a36Sopenharmony_ci	struct xdp_buff xdp;
165062306a36Sopenharmony_ci	struct page *page;
165162306a36Sopenharmony_ci	u32 sub_len = 4;
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci#if !defined(CONFIG_M5272)
165462306a36Sopenharmony_ci	/*If it has the FEC_QUIRK_HAS_RACC quirk property, the bit of
165562306a36Sopenharmony_ci	 * FEC_RACC_SHIFT16 is set by default in the probe function.
165662306a36Sopenharmony_ci	 */
165762306a36Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_HAS_RACC) {
165862306a36Sopenharmony_ci		data_start += 2;
165962306a36Sopenharmony_ci		sub_len += 2;
166062306a36Sopenharmony_ci	}
166162306a36Sopenharmony_ci#endif
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci#ifdef CONFIG_M532x
166462306a36Sopenharmony_ci	flush_cache_all();
166562306a36Sopenharmony_ci#endif
166662306a36Sopenharmony_ci	rxq = fep->rx_queue[queue_id];
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	/* First, grab all of the stats for the incoming packet.
166962306a36Sopenharmony_ci	 * These get messed up if we get called due to a busy condition.
167062306a36Sopenharmony_ci	 */
167162306a36Sopenharmony_ci	bdp = rxq->bd.cur;
167262306a36Sopenharmony_ci	xdp_init_buff(&xdp, PAGE_SIZE, &rxq->xdp_rxq);
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci	while (!((status = fec16_to_cpu(bdp->cbd_sc)) & BD_ENET_RX_EMPTY)) {
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci		if (pkt_received >= budget)
167762306a36Sopenharmony_ci			break;
167862306a36Sopenharmony_ci		pkt_received++;
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_ci		writel(FEC_ENET_RXF_GET(queue_id), fep->hwp + FEC_IEVENT);
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci		/* Check for errors. */
168362306a36Sopenharmony_ci		status ^= BD_ENET_RX_LAST;
168462306a36Sopenharmony_ci		if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO |
168562306a36Sopenharmony_ci			   BD_ENET_RX_CR | BD_ENET_RX_OV | BD_ENET_RX_LAST |
168662306a36Sopenharmony_ci			   BD_ENET_RX_CL)) {
168762306a36Sopenharmony_ci			ndev->stats.rx_errors++;
168862306a36Sopenharmony_ci			if (status & BD_ENET_RX_OV) {
168962306a36Sopenharmony_ci				/* FIFO overrun */
169062306a36Sopenharmony_ci				ndev->stats.rx_fifo_errors++;
169162306a36Sopenharmony_ci				goto rx_processing_done;
169262306a36Sopenharmony_ci			}
169362306a36Sopenharmony_ci			if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH
169462306a36Sopenharmony_ci						| BD_ENET_RX_LAST)) {
169562306a36Sopenharmony_ci				/* Frame too long or too short. */
169662306a36Sopenharmony_ci				ndev->stats.rx_length_errors++;
169762306a36Sopenharmony_ci				if (status & BD_ENET_RX_LAST)
169862306a36Sopenharmony_ci					netdev_err(ndev, "rcv is not +last\n");
169962306a36Sopenharmony_ci			}
170062306a36Sopenharmony_ci			if (status & BD_ENET_RX_CR)	/* CRC Error */
170162306a36Sopenharmony_ci				ndev->stats.rx_crc_errors++;
170262306a36Sopenharmony_ci			/* Report late collisions as a frame error. */
170362306a36Sopenharmony_ci			if (status & (BD_ENET_RX_NO | BD_ENET_RX_CL))
170462306a36Sopenharmony_ci				ndev->stats.rx_frame_errors++;
170562306a36Sopenharmony_ci			goto rx_processing_done;
170662306a36Sopenharmony_ci		}
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci		/* Process the incoming frame. */
170962306a36Sopenharmony_ci		ndev->stats.rx_packets++;
171062306a36Sopenharmony_ci		pkt_len = fec16_to_cpu(bdp->cbd_datlen);
171162306a36Sopenharmony_ci		ndev->stats.rx_bytes += pkt_len;
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci		index = fec_enet_get_bd_index(bdp, &rxq->bd);
171462306a36Sopenharmony_ci		page = rxq->rx_skb_info[index].page;
171562306a36Sopenharmony_ci		dma_sync_single_for_cpu(&fep->pdev->dev,
171662306a36Sopenharmony_ci					fec32_to_cpu(bdp->cbd_bufaddr),
171762306a36Sopenharmony_ci					pkt_len,
171862306a36Sopenharmony_ci					DMA_FROM_DEVICE);
171962306a36Sopenharmony_ci		prefetch(page_address(page));
172062306a36Sopenharmony_ci		fec_enet_update_cbd(rxq, bdp, index);
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci		if (xdp_prog) {
172362306a36Sopenharmony_ci			xdp_buff_clear_frags_flag(&xdp);
172462306a36Sopenharmony_ci			/* subtract 16bit shift and FCS */
172562306a36Sopenharmony_ci			xdp_prepare_buff(&xdp, page_address(page),
172662306a36Sopenharmony_ci					 data_start, pkt_len - sub_len, false);
172762306a36Sopenharmony_ci			ret = fec_enet_run_xdp(fep, xdp_prog, &xdp, rxq, cpu);
172862306a36Sopenharmony_ci			xdp_result |= ret;
172962306a36Sopenharmony_ci			if (ret != FEC_ENET_XDP_PASS)
173062306a36Sopenharmony_ci				goto rx_processing_done;
173162306a36Sopenharmony_ci		}
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci		/* The packet length includes FCS, but we don't want to
173462306a36Sopenharmony_ci		 * include that when passing upstream as it messes up
173562306a36Sopenharmony_ci		 * bridging applications.
173662306a36Sopenharmony_ci		 */
173762306a36Sopenharmony_ci		skb = build_skb(page_address(page), PAGE_SIZE);
173862306a36Sopenharmony_ci		if (unlikely(!skb)) {
173962306a36Sopenharmony_ci			page_pool_recycle_direct(rxq->page_pool, page);
174062306a36Sopenharmony_ci			ndev->stats.rx_dropped++;
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ci			netdev_err_once(ndev, "build_skb failed!\n");
174362306a36Sopenharmony_ci			goto rx_processing_done;
174462306a36Sopenharmony_ci		}
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_ci		skb_reserve(skb, data_start);
174762306a36Sopenharmony_ci		skb_put(skb, pkt_len - sub_len);
174862306a36Sopenharmony_ci		skb_mark_for_recycle(skb);
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci		if (unlikely(need_swap)) {
175162306a36Sopenharmony_ci			data = page_address(page) + FEC_ENET_XDP_HEADROOM;
175262306a36Sopenharmony_ci			swap_buffer(data, pkt_len);
175362306a36Sopenharmony_ci		}
175462306a36Sopenharmony_ci		data = skb->data;
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci		/* Extract the enhanced buffer descriptor */
175762306a36Sopenharmony_ci		ebdp = NULL;
175862306a36Sopenharmony_ci		if (fep->bufdesc_ex)
175962306a36Sopenharmony_ci			ebdp = (struct bufdesc_ex *)bdp;
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_ci		/* If this is a VLAN packet remove the VLAN Tag */
176262306a36Sopenharmony_ci		vlan_packet_rcvd = false;
176362306a36Sopenharmony_ci		if ((ndev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
176462306a36Sopenharmony_ci		    fep->bufdesc_ex &&
176562306a36Sopenharmony_ci		    (ebdp->cbd_esc & cpu_to_fec32(BD_ENET_RX_VLAN))) {
176662306a36Sopenharmony_ci			/* Push and remove the vlan tag */
176762306a36Sopenharmony_ci			struct vlan_hdr *vlan_header =
176862306a36Sopenharmony_ci					(struct vlan_hdr *) (data + ETH_HLEN);
176962306a36Sopenharmony_ci			vlan_tag = ntohs(vlan_header->h_vlan_TCI);
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci			vlan_packet_rcvd = true;
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci			memmove(skb->data + VLAN_HLEN, data, ETH_ALEN * 2);
177462306a36Sopenharmony_ci			skb_pull(skb, VLAN_HLEN);
177562306a36Sopenharmony_ci		}
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci		skb->protocol = eth_type_trans(skb, ndev);
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci		/* Get receive timestamp from the skb */
178062306a36Sopenharmony_ci		if (fep->hwts_rx_en && fep->bufdesc_ex)
178162306a36Sopenharmony_ci			fec_enet_hwtstamp(fep, fec32_to_cpu(ebdp->ts),
178262306a36Sopenharmony_ci					  skb_hwtstamps(skb));
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci		if (fep->bufdesc_ex &&
178562306a36Sopenharmony_ci		    (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) {
178662306a36Sopenharmony_ci			if (!(ebdp->cbd_esc & cpu_to_fec32(FLAG_RX_CSUM_ERROR))) {
178762306a36Sopenharmony_ci				/* don't check it */
178862306a36Sopenharmony_ci				skb->ip_summed = CHECKSUM_UNNECESSARY;
178962306a36Sopenharmony_ci			} else {
179062306a36Sopenharmony_ci				skb_checksum_none_assert(skb);
179162306a36Sopenharmony_ci			}
179262306a36Sopenharmony_ci		}
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci		/* Handle received VLAN packets */
179562306a36Sopenharmony_ci		if (vlan_packet_rcvd)
179662306a36Sopenharmony_ci			__vlan_hwaccel_put_tag(skb,
179762306a36Sopenharmony_ci					       htons(ETH_P_8021Q),
179862306a36Sopenharmony_ci					       vlan_tag);
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_ci		skb_record_rx_queue(skb, queue_id);
180162306a36Sopenharmony_ci		napi_gro_receive(&fep->napi, skb);
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_cirx_processing_done:
180462306a36Sopenharmony_ci		/* Clear the status flags for this buffer */
180562306a36Sopenharmony_ci		status &= ~BD_ENET_RX_STATS;
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_ci		/* Mark the buffer empty */
180862306a36Sopenharmony_ci		status |= BD_ENET_RX_EMPTY;
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_ci		if (fep->bufdesc_ex) {
181162306a36Sopenharmony_ci			struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci			ebdp->cbd_esc = cpu_to_fec32(BD_ENET_RX_INT);
181462306a36Sopenharmony_ci			ebdp->cbd_prot = 0;
181562306a36Sopenharmony_ci			ebdp->cbd_bdu = 0;
181662306a36Sopenharmony_ci		}
181762306a36Sopenharmony_ci		/* Make sure the updates to rest of the descriptor are
181862306a36Sopenharmony_ci		 * performed before transferring ownership.
181962306a36Sopenharmony_ci		 */
182062306a36Sopenharmony_ci		wmb();
182162306a36Sopenharmony_ci		bdp->cbd_sc = cpu_to_fec16(status);
182262306a36Sopenharmony_ci
182362306a36Sopenharmony_ci		/* Update BD pointer to next entry */
182462306a36Sopenharmony_ci		bdp = fec_enet_get_nextdesc(bdp, &rxq->bd);
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci		/* Doing this here will keep the FEC running while we process
182762306a36Sopenharmony_ci		 * incoming frames.  On a heavily loaded network, we should be
182862306a36Sopenharmony_ci		 * able to keep up at the expense of system resources.
182962306a36Sopenharmony_ci		 */
183062306a36Sopenharmony_ci		writel(0, rxq->bd.reg_desc_active);
183162306a36Sopenharmony_ci	}
183262306a36Sopenharmony_ci	rxq->bd.cur = bdp;
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci	if (xdp_result & FEC_ENET_XDP_REDIR)
183562306a36Sopenharmony_ci		xdp_do_flush_map();
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	return pkt_received;
183862306a36Sopenharmony_ci}
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_cistatic int fec_enet_rx(struct net_device *ndev, int budget)
184162306a36Sopenharmony_ci{
184262306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
184362306a36Sopenharmony_ci	int i, done = 0;
184462306a36Sopenharmony_ci
184562306a36Sopenharmony_ci	/* Make sure that AVB queues are processed first. */
184662306a36Sopenharmony_ci	for (i = fep->num_rx_queues - 1; i >= 0; i--)
184762306a36Sopenharmony_ci		done += fec_enet_rx_queue(ndev, budget - done, i);
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci	return done;
185062306a36Sopenharmony_ci}
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_cistatic bool fec_enet_collect_events(struct fec_enet_private *fep)
185362306a36Sopenharmony_ci{
185462306a36Sopenharmony_ci	uint int_events;
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci	int_events = readl(fep->hwp + FEC_IEVENT);
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci	/* Don't clear MDIO events, we poll for those */
185962306a36Sopenharmony_ci	int_events &= ~FEC_ENET_MII;
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_ci	writel(int_events, fep->hwp + FEC_IEVENT);
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci	return int_events != 0;
186462306a36Sopenharmony_ci}
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_cistatic irqreturn_t
186762306a36Sopenharmony_cifec_enet_interrupt(int irq, void *dev_id)
186862306a36Sopenharmony_ci{
186962306a36Sopenharmony_ci	struct net_device *ndev = dev_id;
187062306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
187162306a36Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci	if (fec_enet_collect_events(fep) && fep->link) {
187462306a36Sopenharmony_ci		ret = IRQ_HANDLED;
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci		if (napi_schedule_prep(&fep->napi)) {
187762306a36Sopenharmony_ci			/* Disable interrupts */
187862306a36Sopenharmony_ci			writel(0, fep->hwp + FEC_IMASK);
187962306a36Sopenharmony_ci			__napi_schedule(&fep->napi);
188062306a36Sopenharmony_ci		}
188162306a36Sopenharmony_ci	}
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci	return ret;
188462306a36Sopenharmony_ci}
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_cistatic int fec_enet_rx_napi(struct napi_struct *napi, int budget)
188762306a36Sopenharmony_ci{
188862306a36Sopenharmony_ci	struct net_device *ndev = napi->dev;
188962306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
189062306a36Sopenharmony_ci	int done = 0;
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci	do {
189362306a36Sopenharmony_ci		done += fec_enet_rx(ndev, budget - done);
189462306a36Sopenharmony_ci		fec_enet_tx(ndev, budget);
189562306a36Sopenharmony_ci	} while ((done < budget) && fec_enet_collect_events(fep));
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci	if (done < budget) {
189862306a36Sopenharmony_ci		napi_complete_done(napi, done);
189962306a36Sopenharmony_ci		writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
190062306a36Sopenharmony_ci	}
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ci	return done;
190362306a36Sopenharmony_ci}
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci/* ------------------------------------------------------------------------- */
190662306a36Sopenharmony_cistatic int fec_get_mac(struct net_device *ndev)
190762306a36Sopenharmony_ci{
190862306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
190962306a36Sopenharmony_ci	unsigned char *iap, tmpaddr[ETH_ALEN];
191062306a36Sopenharmony_ci	int ret;
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci	/*
191362306a36Sopenharmony_ci	 * try to get mac address in following order:
191462306a36Sopenharmony_ci	 *
191562306a36Sopenharmony_ci	 * 1) module parameter via kernel command line in form
191662306a36Sopenharmony_ci	 *    fec.macaddr=0x00,0x04,0x9f,0x01,0x30,0xe0
191762306a36Sopenharmony_ci	 */
191862306a36Sopenharmony_ci	iap = macaddr;
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_ci	/*
192162306a36Sopenharmony_ci	 * 2) from device tree data
192262306a36Sopenharmony_ci	 */
192362306a36Sopenharmony_ci	if (!is_valid_ether_addr(iap)) {
192462306a36Sopenharmony_ci		struct device_node *np = fep->pdev->dev.of_node;
192562306a36Sopenharmony_ci		if (np) {
192662306a36Sopenharmony_ci			ret = of_get_mac_address(np, tmpaddr);
192762306a36Sopenharmony_ci			if (!ret)
192862306a36Sopenharmony_ci				iap = tmpaddr;
192962306a36Sopenharmony_ci			else if (ret == -EPROBE_DEFER)
193062306a36Sopenharmony_ci				return ret;
193162306a36Sopenharmony_ci		}
193262306a36Sopenharmony_ci	}
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_ci	/*
193562306a36Sopenharmony_ci	 * 3) from flash or fuse (via platform data)
193662306a36Sopenharmony_ci	 */
193762306a36Sopenharmony_ci	if (!is_valid_ether_addr(iap)) {
193862306a36Sopenharmony_ci#ifdef CONFIG_M5272
193962306a36Sopenharmony_ci		if (FEC_FLASHMAC)
194062306a36Sopenharmony_ci			iap = (unsigned char *)FEC_FLASHMAC;
194162306a36Sopenharmony_ci#else
194262306a36Sopenharmony_ci		struct fec_platform_data *pdata = dev_get_platdata(&fep->pdev->dev);
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_ci		if (pdata)
194562306a36Sopenharmony_ci			iap = (unsigned char *)&pdata->mac;
194662306a36Sopenharmony_ci#endif
194762306a36Sopenharmony_ci	}
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci	/*
195062306a36Sopenharmony_ci	 * 4) FEC mac registers set by bootloader
195162306a36Sopenharmony_ci	 */
195262306a36Sopenharmony_ci	if (!is_valid_ether_addr(iap)) {
195362306a36Sopenharmony_ci		*((__be32 *) &tmpaddr[0]) =
195462306a36Sopenharmony_ci			cpu_to_be32(readl(fep->hwp + FEC_ADDR_LOW));
195562306a36Sopenharmony_ci		*((__be16 *) &tmpaddr[4]) =
195662306a36Sopenharmony_ci			cpu_to_be16(readl(fep->hwp + FEC_ADDR_HIGH) >> 16);
195762306a36Sopenharmony_ci		iap = &tmpaddr[0];
195862306a36Sopenharmony_ci	}
195962306a36Sopenharmony_ci
196062306a36Sopenharmony_ci	/*
196162306a36Sopenharmony_ci	 * 5) random mac address
196262306a36Sopenharmony_ci	 */
196362306a36Sopenharmony_ci	if (!is_valid_ether_addr(iap)) {
196462306a36Sopenharmony_ci		/* Report it and use a random ethernet address instead */
196562306a36Sopenharmony_ci		dev_err(&fep->pdev->dev, "Invalid MAC address: %pM\n", iap);
196662306a36Sopenharmony_ci		eth_hw_addr_random(ndev);
196762306a36Sopenharmony_ci		dev_info(&fep->pdev->dev, "Using random MAC address: %pM\n",
196862306a36Sopenharmony_ci			 ndev->dev_addr);
196962306a36Sopenharmony_ci		return 0;
197062306a36Sopenharmony_ci	}
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_ci	/* Adjust MAC if using macaddr */
197362306a36Sopenharmony_ci	eth_hw_addr_gen(ndev, iap, iap == macaddr ? fep->dev_id : 0);
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_ci	return 0;
197662306a36Sopenharmony_ci}
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci/* ------------------------------------------------------------------------- */
197962306a36Sopenharmony_ci
198062306a36Sopenharmony_ci/*
198162306a36Sopenharmony_ci * Phy section
198262306a36Sopenharmony_ci */
198362306a36Sopenharmony_cistatic void fec_enet_adjust_link(struct net_device *ndev)
198462306a36Sopenharmony_ci{
198562306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
198662306a36Sopenharmony_ci	struct phy_device *phy_dev = ndev->phydev;
198762306a36Sopenharmony_ci	int status_change = 0;
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_ci	/*
199062306a36Sopenharmony_ci	 * If the netdev is down, or is going down, we're not interested
199162306a36Sopenharmony_ci	 * in link state events, so just mark our idea of the link as down
199262306a36Sopenharmony_ci	 * and ignore the event.
199362306a36Sopenharmony_ci	 */
199462306a36Sopenharmony_ci	if (!netif_running(ndev) || !netif_device_present(ndev)) {
199562306a36Sopenharmony_ci		fep->link = 0;
199662306a36Sopenharmony_ci	} else if (phy_dev->link) {
199762306a36Sopenharmony_ci		if (!fep->link) {
199862306a36Sopenharmony_ci			fep->link = phy_dev->link;
199962306a36Sopenharmony_ci			status_change = 1;
200062306a36Sopenharmony_ci		}
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci		if (fep->full_duplex != phy_dev->duplex) {
200362306a36Sopenharmony_ci			fep->full_duplex = phy_dev->duplex;
200462306a36Sopenharmony_ci			status_change = 1;
200562306a36Sopenharmony_ci		}
200662306a36Sopenharmony_ci
200762306a36Sopenharmony_ci		if (phy_dev->speed != fep->speed) {
200862306a36Sopenharmony_ci			fep->speed = phy_dev->speed;
200962306a36Sopenharmony_ci			status_change = 1;
201062306a36Sopenharmony_ci		}
201162306a36Sopenharmony_ci
201262306a36Sopenharmony_ci		/* if any of the above changed restart the FEC */
201362306a36Sopenharmony_ci		if (status_change) {
201462306a36Sopenharmony_ci			netif_stop_queue(ndev);
201562306a36Sopenharmony_ci			napi_disable(&fep->napi);
201662306a36Sopenharmony_ci			netif_tx_lock_bh(ndev);
201762306a36Sopenharmony_ci			fec_restart(ndev);
201862306a36Sopenharmony_ci			netif_tx_wake_all_queues(ndev);
201962306a36Sopenharmony_ci			netif_tx_unlock_bh(ndev);
202062306a36Sopenharmony_ci			napi_enable(&fep->napi);
202162306a36Sopenharmony_ci		}
202262306a36Sopenharmony_ci	} else {
202362306a36Sopenharmony_ci		if (fep->link) {
202462306a36Sopenharmony_ci			netif_stop_queue(ndev);
202562306a36Sopenharmony_ci			napi_disable(&fep->napi);
202662306a36Sopenharmony_ci			netif_tx_lock_bh(ndev);
202762306a36Sopenharmony_ci			fec_stop(ndev);
202862306a36Sopenharmony_ci			netif_tx_unlock_bh(ndev);
202962306a36Sopenharmony_ci			napi_enable(&fep->napi);
203062306a36Sopenharmony_ci			fep->link = phy_dev->link;
203162306a36Sopenharmony_ci			status_change = 1;
203262306a36Sopenharmony_ci		}
203362306a36Sopenharmony_ci	}
203462306a36Sopenharmony_ci
203562306a36Sopenharmony_ci	if (status_change)
203662306a36Sopenharmony_ci		phy_print_status(phy_dev);
203762306a36Sopenharmony_ci}
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_cistatic int fec_enet_mdio_wait(struct fec_enet_private *fep)
204062306a36Sopenharmony_ci{
204162306a36Sopenharmony_ci	uint ievent;
204262306a36Sopenharmony_ci	int ret;
204362306a36Sopenharmony_ci
204462306a36Sopenharmony_ci	ret = readl_poll_timeout_atomic(fep->hwp + FEC_IEVENT, ievent,
204562306a36Sopenharmony_ci					ievent & FEC_ENET_MII, 2, 30000);
204662306a36Sopenharmony_ci
204762306a36Sopenharmony_ci	if (!ret)
204862306a36Sopenharmony_ci		writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT);
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_ci	return ret;
205162306a36Sopenharmony_ci}
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_cistatic int fec_enet_mdio_read_c22(struct mii_bus *bus, int mii_id, int regnum)
205462306a36Sopenharmony_ci{
205562306a36Sopenharmony_ci	struct fec_enet_private *fep = bus->priv;
205662306a36Sopenharmony_ci	struct device *dev = &fep->pdev->dev;
205762306a36Sopenharmony_ci	int ret = 0, frame_start, frame_addr, frame_op;
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(dev);
206062306a36Sopenharmony_ci	if (ret < 0)
206162306a36Sopenharmony_ci		return ret;
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci	/* C22 read */
206462306a36Sopenharmony_ci	frame_op = FEC_MMFR_OP_READ;
206562306a36Sopenharmony_ci	frame_start = FEC_MMFR_ST;
206662306a36Sopenharmony_ci	frame_addr = regnum;
206762306a36Sopenharmony_ci
206862306a36Sopenharmony_ci	/* start a read op */
206962306a36Sopenharmony_ci	writel(frame_start | frame_op |
207062306a36Sopenharmony_ci	       FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) |
207162306a36Sopenharmony_ci	       FEC_MMFR_TA, fep->hwp + FEC_MII_DATA);
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_ci	/* wait for end of transfer */
207462306a36Sopenharmony_ci	ret = fec_enet_mdio_wait(fep);
207562306a36Sopenharmony_ci	if (ret) {
207662306a36Sopenharmony_ci		netdev_err(fep->netdev, "MDIO read timeout\n");
207762306a36Sopenharmony_ci		goto out;
207862306a36Sopenharmony_ci	}
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci	ret = FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA));
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ciout:
208362306a36Sopenharmony_ci	pm_runtime_mark_last_busy(dev);
208462306a36Sopenharmony_ci	pm_runtime_put_autosuspend(dev);
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_ci	return ret;
208762306a36Sopenharmony_ci}
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_cistatic int fec_enet_mdio_read_c45(struct mii_bus *bus, int mii_id,
209062306a36Sopenharmony_ci				  int devad, int regnum)
209162306a36Sopenharmony_ci{
209262306a36Sopenharmony_ci	struct fec_enet_private *fep = bus->priv;
209362306a36Sopenharmony_ci	struct device *dev = &fep->pdev->dev;
209462306a36Sopenharmony_ci	int ret = 0, frame_start, frame_op;
209562306a36Sopenharmony_ci
209662306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(dev);
209762306a36Sopenharmony_ci	if (ret < 0)
209862306a36Sopenharmony_ci		return ret;
209962306a36Sopenharmony_ci
210062306a36Sopenharmony_ci	frame_start = FEC_MMFR_ST_C45;
210162306a36Sopenharmony_ci
210262306a36Sopenharmony_ci	/* write address */
210362306a36Sopenharmony_ci	writel(frame_start | FEC_MMFR_OP_ADDR_WRITE |
210462306a36Sopenharmony_ci	       FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(devad) |
210562306a36Sopenharmony_ci	       FEC_MMFR_TA | (regnum & 0xFFFF),
210662306a36Sopenharmony_ci	       fep->hwp + FEC_MII_DATA);
210762306a36Sopenharmony_ci
210862306a36Sopenharmony_ci	/* wait for end of transfer */
210962306a36Sopenharmony_ci	ret = fec_enet_mdio_wait(fep);
211062306a36Sopenharmony_ci	if (ret) {
211162306a36Sopenharmony_ci		netdev_err(fep->netdev, "MDIO address write timeout\n");
211262306a36Sopenharmony_ci		goto out;
211362306a36Sopenharmony_ci	}
211462306a36Sopenharmony_ci
211562306a36Sopenharmony_ci	frame_op = FEC_MMFR_OP_READ_C45;
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_ci	/* start a read op */
211862306a36Sopenharmony_ci	writel(frame_start | frame_op |
211962306a36Sopenharmony_ci	       FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(devad) |
212062306a36Sopenharmony_ci	       FEC_MMFR_TA, fep->hwp + FEC_MII_DATA);
212162306a36Sopenharmony_ci
212262306a36Sopenharmony_ci	/* wait for end of transfer */
212362306a36Sopenharmony_ci	ret = fec_enet_mdio_wait(fep);
212462306a36Sopenharmony_ci	if (ret) {
212562306a36Sopenharmony_ci		netdev_err(fep->netdev, "MDIO read timeout\n");
212662306a36Sopenharmony_ci		goto out;
212762306a36Sopenharmony_ci	}
212862306a36Sopenharmony_ci
212962306a36Sopenharmony_ci	ret = FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA));
213062306a36Sopenharmony_ci
213162306a36Sopenharmony_ciout:
213262306a36Sopenharmony_ci	pm_runtime_mark_last_busy(dev);
213362306a36Sopenharmony_ci	pm_runtime_put_autosuspend(dev);
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci	return ret;
213662306a36Sopenharmony_ci}
213762306a36Sopenharmony_ci
213862306a36Sopenharmony_cistatic int fec_enet_mdio_write_c22(struct mii_bus *bus, int mii_id, int regnum,
213962306a36Sopenharmony_ci				   u16 value)
214062306a36Sopenharmony_ci{
214162306a36Sopenharmony_ci	struct fec_enet_private *fep = bus->priv;
214262306a36Sopenharmony_ci	struct device *dev = &fep->pdev->dev;
214362306a36Sopenharmony_ci	int ret, frame_start, frame_addr;
214462306a36Sopenharmony_ci
214562306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(dev);
214662306a36Sopenharmony_ci	if (ret < 0)
214762306a36Sopenharmony_ci		return ret;
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci	/* C22 write */
215062306a36Sopenharmony_ci	frame_start = FEC_MMFR_ST;
215162306a36Sopenharmony_ci	frame_addr = regnum;
215262306a36Sopenharmony_ci
215362306a36Sopenharmony_ci	/* start a write op */
215462306a36Sopenharmony_ci	writel(frame_start | FEC_MMFR_OP_WRITE |
215562306a36Sopenharmony_ci	       FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) |
215662306a36Sopenharmony_ci	       FEC_MMFR_TA | FEC_MMFR_DATA(value),
215762306a36Sopenharmony_ci	       fep->hwp + FEC_MII_DATA);
215862306a36Sopenharmony_ci
215962306a36Sopenharmony_ci	/* wait for end of transfer */
216062306a36Sopenharmony_ci	ret = fec_enet_mdio_wait(fep);
216162306a36Sopenharmony_ci	if (ret)
216262306a36Sopenharmony_ci		netdev_err(fep->netdev, "MDIO write timeout\n");
216362306a36Sopenharmony_ci
216462306a36Sopenharmony_ci	pm_runtime_mark_last_busy(dev);
216562306a36Sopenharmony_ci	pm_runtime_put_autosuspend(dev);
216662306a36Sopenharmony_ci
216762306a36Sopenharmony_ci	return ret;
216862306a36Sopenharmony_ci}
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_cistatic int fec_enet_mdio_write_c45(struct mii_bus *bus, int mii_id,
217162306a36Sopenharmony_ci				   int devad, int regnum, u16 value)
217262306a36Sopenharmony_ci{
217362306a36Sopenharmony_ci	struct fec_enet_private *fep = bus->priv;
217462306a36Sopenharmony_ci	struct device *dev = &fep->pdev->dev;
217562306a36Sopenharmony_ci	int ret, frame_start;
217662306a36Sopenharmony_ci
217762306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(dev);
217862306a36Sopenharmony_ci	if (ret < 0)
217962306a36Sopenharmony_ci		return ret;
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_ci	frame_start = FEC_MMFR_ST_C45;
218262306a36Sopenharmony_ci
218362306a36Sopenharmony_ci	/* write address */
218462306a36Sopenharmony_ci	writel(frame_start | FEC_MMFR_OP_ADDR_WRITE |
218562306a36Sopenharmony_ci	       FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(devad) |
218662306a36Sopenharmony_ci	       FEC_MMFR_TA | (regnum & 0xFFFF),
218762306a36Sopenharmony_ci	       fep->hwp + FEC_MII_DATA);
218862306a36Sopenharmony_ci
218962306a36Sopenharmony_ci	/* wait for end of transfer */
219062306a36Sopenharmony_ci	ret = fec_enet_mdio_wait(fep);
219162306a36Sopenharmony_ci	if (ret) {
219262306a36Sopenharmony_ci		netdev_err(fep->netdev, "MDIO address write timeout\n");
219362306a36Sopenharmony_ci		goto out;
219462306a36Sopenharmony_ci	}
219562306a36Sopenharmony_ci
219662306a36Sopenharmony_ci	/* start a write op */
219762306a36Sopenharmony_ci	writel(frame_start | FEC_MMFR_OP_WRITE |
219862306a36Sopenharmony_ci	       FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(devad) |
219962306a36Sopenharmony_ci	       FEC_MMFR_TA | FEC_MMFR_DATA(value),
220062306a36Sopenharmony_ci	       fep->hwp + FEC_MII_DATA);
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_ci	/* wait for end of transfer */
220362306a36Sopenharmony_ci	ret = fec_enet_mdio_wait(fep);
220462306a36Sopenharmony_ci	if (ret)
220562306a36Sopenharmony_ci		netdev_err(fep->netdev, "MDIO write timeout\n");
220662306a36Sopenharmony_ci
220762306a36Sopenharmony_ciout:
220862306a36Sopenharmony_ci	pm_runtime_mark_last_busy(dev);
220962306a36Sopenharmony_ci	pm_runtime_put_autosuspend(dev);
221062306a36Sopenharmony_ci
221162306a36Sopenharmony_ci	return ret;
221262306a36Sopenharmony_ci}
221362306a36Sopenharmony_ci
221462306a36Sopenharmony_cistatic void fec_enet_phy_reset_after_clk_enable(struct net_device *ndev)
221562306a36Sopenharmony_ci{
221662306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
221762306a36Sopenharmony_ci	struct phy_device *phy_dev = ndev->phydev;
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_ci	if (phy_dev) {
222062306a36Sopenharmony_ci		phy_reset_after_clk_enable(phy_dev);
222162306a36Sopenharmony_ci	} else if (fep->phy_node) {
222262306a36Sopenharmony_ci		/*
222362306a36Sopenharmony_ci		 * If the PHY still is not bound to the MAC, but there is
222462306a36Sopenharmony_ci		 * OF PHY node and a matching PHY device instance already,
222562306a36Sopenharmony_ci		 * use the OF PHY node to obtain the PHY device instance,
222662306a36Sopenharmony_ci		 * and then use that PHY device instance when triggering
222762306a36Sopenharmony_ci		 * the PHY reset.
222862306a36Sopenharmony_ci		 */
222962306a36Sopenharmony_ci		phy_dev = of_phy_find_device(fep->phy_node);
223062306a36Sopenharmony_ci		phy_reset_after_clk_enable(phy_dev);
223162306a36Sopenharmony_ci		put_device(&phy_dev->mdio.dev);
223262306a36Sopenharmony_ci	}
223362306a36Sopenharmony_ci}
223462306a36Sopenharmony_ci
223562306a36Sopenharmony_cistatic int fec_enet_clk_enable(struct net_device *ndev, bool enable)
223662306a36Sopenharmony_ci{
223762306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
223862306a36Sopenharmony_ci	int ret;
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_ci	if (enable) {
224162306a36Sopenharmony_ci		ret = clk_prepare_enable(fep->clk_enet_out);
224262306a36Sopenharmony_ci		if (ret)
224362306a36Sopenharmony_ci			return ret;
224462306a36Sopenharmony_ci
224562306a36Sopenharmony_ci		if (fep->clk_ptp) {
224662306a36Sopenharmony_ci			mutex_lock(&fep->ptp_clk_mutex);
224762306a36Sopenharmony_ci			ret = clk_prepare_enable(fep->clk_ptp);
224862306a36Sopenharmony_ci			if (ret) {
224962306a36Sopenharmony_ci				mutex_unlock(&fep->ptp_clk_mutex);
225062306a36Sopenharmony_ci				goto failed_clk_ptp;
225162306a36Sopenharmony_ci			} else {
225262306a36Sopenharmony_ci				fep->ptp_clk_on = true;
225362306a36Sopenharmony_ci			}
225462306a36Sopenharmony_ci			mutex_unlock(&fep->ptp_clk_mutex);
225562306a36Sopenharmony_ci		}
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_ci		ret = clk_prepare_enable(fep->clk_ref);
225862306a36Sopenharmony_ci		if (ret)
225962306a36Sopenharmony_ci			goto failed_clk_ref;
226062306a36Sopenharmony_ci
226162306a36Sopenharmony_ci		ret = clk_prepare_enable(fep->clk_2x_txclk);
226262306a36Sopenharmony_ci		if (ret)
226362306a36Sopenharmony_ci			goto failed_clk_2x_txclk;
226462306a36Sopenharmony_ci
226562306a36Sopenharmony_ci		fec_enet_phy_reset_after_clk_enable(ndev);
226662306a36Sopenharmony_ci	} else {
226762306a36Sopenharmony_ci		clk_disable_unprepare(fep->clk_enet_out);
226862306a36Sopenharmony_ci		if (fep->clk_ptp) {
226962306a36Sopenharmony_ci			mutex_lock(&fep->ptp_clk_mutex);
227062306a36Sopenharmony_ci			clk_disable_unprepare(fep->clk_ptp);
227162306a36Sopenharmony_ci			fep->ptp_clk_on = false;
227262306a36Sopenharmony_ci			mutex_unlock(&fep->ptp_clk_mutex);
227362306a36Sopenharmony_ci		}
227462306a36Sopenharmony_ci		clk_disable_unprepare(fep->clk_ref);
227562306a36Sopenharmony_ci		clk_disable_unprepare(fep->clk_2x_txclk);
227662306a36Sopenharmony_ci	}
227762306a36Sopenharmony_ci
227862306a36Sopenharmony_ci	return 0;
227962306a36Sopenharmony_ci
228062306a36Sopenharmony_cifailed_clk_2x_txclk:
228162306a36Sopenharmony_ci	if (fep->clk_ref)
228262306a36Sopenharmony_ci		clk_disable_unprepare(fep->clk_ref);
228362306a36Sopenharmony_cifailed_clk_ref:
228462306a36Sopenharmony_ci	if (fep->clk_ptp) {
228562306a36Sopenharmony_ci		mutex_lock(&fep->ptp_clk_mutex);
228662306a36Sopenharmony_ci		clk_disable_unprepare(fep->clk_ptp);
228762306a36Sopenharmony_ci		fep->ptp_clk_on = false;
228862306a36Sopenharmony_ci		mutex_unlock(&fep->ptp_clk_mutex);
228962306a36Sopenharmony_ci	}
229062306a36Sopenharmony_cifailed_clk_ptp:
229162306a36Sopenharmony_ci	clk_disable_unprepare(fep->clk_enet_out);
229262306a36Sopenharmony_ci
229362306a36Sopenharmony_ci	return ret;
229462306a36Sopenharmony_ci}
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_cistatic int fec_enet_parse_rgmii_delay(struct fec_enet_private *fep,
229762306a36Sopenharmony_ci				      struct device_node *np)
229862306a36Sopenharmony_ci{
229962306a36Sopenharmony_ci	u32 rgmii_tx_delay, rgmii_rx_delay;
230062306a36Sopenharmony_ci
230162306a36Sopenharmony_ci	/* For rgmii tx internal delay, valid values are 0ps and 2000ps */
230262306a36Sopenharmony_ci	if (!of_property_read_u32(np, "tx-internal-delay-ps", &rgmii_tx_delay)) {
230362306a36Sopenharmony_ci		if (rgmii_tx_delay != 0 && rgmii_tx_delay != 2000) {
230462306a36Sopenharmony_ci			dev_err(&fep->pdev->dev, "The only allowed RGMII TX delay values are: 0ps, 2000ps");
230562306a36Sopenharmony_ci			return -EINVAL;
230662306a36Sopenharmony_ci		} else if (rgmii_tx_delay == 2000) {
230762306a36Sopenharmony_ci			fep->rgmii_txc_dly = true;
230862306a36Sopenharmony_ci		}
230962306a36Sopenharmony_ci	}
231062306a36Sopenharmony_ci
231162306a36Sopenharmony_ci	/* For rgmii rx internal delay, valid values are 0ps and 2000ps */
231262306a36Sopenharmony_ci	if (!of_property_read_u32(np, "rx-internal-delay-ps", &rgmii_rx_delay)) {
231362306a36Sopenharmony_ci		if (rgmii_rx_delay != 0 && rgmii_rx_delay != 2000) {
231462306a36Sopenharmony_ci			dev_err(&fep->pdev->dev, "The only allowed RGMII RX delay values are: 0ps, 2000ps");
231562306a36Sopenharmony_ci			return -EINVAL;
231662306a36Sopenharmony_ci		} else if (rgmii_rx_delay == 2000) {
231762306a36Sopenharmony_ci			fep->rgmii_rxc_dly = true;
231862306a36Sopenharmony_ci		}
231962306a36Sopenharmony_ci	}
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ci	return 0;
232262306a36Sopenharmony_ci}
232362306a36Sopenharmony_ci
232462306a36Sopenharmony_cistatic int fec_enet_mii_probe(struct net_device *ndev)
232562306a36Sopenharmony_ci{
232662306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
232762306a36Sopenharmony_ci	struct phy_device *phy_dev = NULL;
232862306a36Sopenharmony_ci	char mdio_bus_id[MII_BUS_ID_SIZE];
232962306a36Sopenharmony_ci	char phy_name[MII_BUS_ID_SIZE + 3];
233062306a36Sopenharmony_ci	int phy_id;
233162306a36Sopenharmony_ci	int dev_id = fep->dev_id;
233262306a36Sopenharmony_ci
233362306a36Sopenharmony_ci	if (fep->phy_node) {
233462306a36Sopenharmony_ci		phy_dev = of_phy_connect(ndev, fep->phy_node,
233562306a36Sopenharmony_ci					 &fec_enet_adjust_link, 0,
233662306a36Sopenharmony_ci					 fep->phy_interface);
233762306a36Sopenharmony_ci		if (!phy_dev) {
233862306a36Sopenharmony_ci			netdev_err(ndev, "Unable to connect to phy\n");
233962306a36Sopenharmony_ci			return -ENODEV;
234062306a36Sopenharmony_ci		}
234162306a36Sopenharmony_ci	} else {
234262306a36Sopenharmony_ci		/* check for attached phy */
234362306a36Sopenharmony_ci		for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) {
234462306a36Sopenharmony_ci			if (!mdiobus_is_registered_device(fep->mii_bus, phy_id))
234562306a36Sopenharmony_ci				continue;
234662306a36Sopenharmony_ci			if (dev_id--)
234762306a36Sopenharmony_ci				continue;
234862306a36Sopenharmony_ci			strscpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE);
234962306a36Sopenharmony_ci			break;
235062306a36Sopenharmony_ci		}
235162306a36Sopenharmony_ci
235262306a36Sopenharmony_ci		if (phy_id >= PHY_MAX_ADDR) {
235362306a36Sopenharmony_ci			netdev_info(ndev, "no PHY, assuming direct connection to switch\n");
235462306a36Sopenharmony_ci			strscpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE);
235562306a36Sopenharmony_ci			phy_id = 0;
235662306a36Sopenharmony_ci		}
235762306a36Sopenharmony_ci
235862306a36Sopenharmony_ci		snprintf(phy_name, sizeof(phy_name),
235962306a36Sopenharmony_ci			 PHY_ID_FMT, mdio_bus_id, phy_id);
236062306a36Sopenharmony_ci		phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link,
236162306a36Sopenharmony_ci				      fep->phy_interface);
236262306a36Sopenharmony_ci	}
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_ci	if (IS_ERR(phy_dev)) {
236562306a36Sopenharmony_ci		netdev_err(ndev, "could not attach to PHY\n");
236662306a36Sopenharmony_ci		return PTR_ERR(phy_dev);
236762306a36Sopenharmony_ci	}
236862306a36Sopenharmony_ci
236962306a36Sopenharmony_ci	/* mask with MAC supported features */
237062306a36Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_HAS_GBIT) {
237162306a36Sopenharmony_ci		phy_set_max_speed(phy_dev, 1000);
237262306a36Sopenharmony_ci		phy_remove_link_mode(phy_dev,
237362306a36Sopenharmony_ci				     ETHTOOL_LINK_MODE_1000baseT_Half_BIT);
237462306a36Sopenharmony_ci#if !defined(CONFIG_M5272)
237562306a36Sopenharmony_ci		phy_support_sym_pause(phy_dev);
237662306a36Sopenharmony_ci#endif
237762306a36Sopenharmony_ci	}
237862306a36Sopenharmony_ci	else
237962306a36Sopenharmony_ci		phy_set_max_speed(phy_dev, 100);
238062306a36Sopenharmony_ci
238162306a36Sopenharmony_ci	fep->link = 0;
238262306a36Sopenharmony_ci	fep->full_duplex = 0;
238362306a36Sopenharmony_ci
238462306a36Sopenharmony_ci	phy_dev->mac_managed_pm = true;
238562306a36Sopenharmony_ci
238662306a36Sopenharmony_ci	phy_attached_info(phy_dev);
238762306a36Sopenharmony_ci
238862306a36Sopenharmony_ci	return 0;
238962306a36Sopenharmony_ci}
239062306a36Sopenharmony_ci
239162306a36Sopenharmony_cistatic int fec_enet_mii_init(struct platform_device *pdev)
239262306a36Sopenharmony_ci{
239362306a36Sopenharmony_ci	static struct mii_bus *fec0_mii_bus;
239462306a36Sopenharmony_ci	struct net_device *ndev = platform_get_drvdata(pdev);
239562306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
239662306a36Sopenharmony_ci	bool suppress_preamble = false;
239762306a36Sopenharmony_ci	struct device_node *node;
239862306a36Sopenharmony_ci	int err = -ENXIO;
239962306a36Sopenharmony_ci	u32 mii_speed, holdtime;
240062306a36Sopenharmony_ci	u32 bus_freq;
240162306a36Sopenharmony_ci
240262306a36Sopenharmony_ci	/*
240362306a36Sopenharmony_ci	 * The i.MX28 dual fec interfaces are not equal.
240462306a36Sopenharmony_ci	 * Here are the differences:
240562306a36Sopenharmony_ci	 *
240662306a36Sopenharmony_ci	 *  - fec0 supports MII & RMII modes while fec1 only supports RMII
240762306a36Sopenharmony_ci	 *  - fec0 acts as the 1588 time master while fec1 is slave
240862306a36Sopenharmony_ci	 *  - external phys can only be configured by fec0
240962306a36Sopenharmony_ci	 *
241062306a36Sopenharmony_ci	 * That is to say fec1 can not work independently. It only works
241162306a36Sopenharmony_ci	 * when fec0 is working. The reason behind this design is that the
241262306a36Sopenharmony_ci	 * second interface is added primarily for Switch mode.
241362306a36Sopenharmony_ci	 *
241462306a36Sopenharmony_ci	 * Because of the last point above, both phys are attached on fec0
241562306a36Sopenharmony_ci	 * mdio interface in board design, and need to be configured by
241662306a36Sopenharmony_ci	 * fec0 mii_bus.
241762306a36Sopenharmony_ci	 */
241862306a36Sopenharmony_ci	if ((fep->quirks & FEC_QUIRK_SINGLE_MDIO) && fep->dev_id > 0) {
241962306a36Sopenharmony_ci		/* fec1 uses fec0 mii_bus */
242062306a36Sopenharmony_ci		if (mii_cnt && fec0_mii_bus) {
242162306a36Sopenharmony_ci			fep->mii_bus = fec0_mii_bus;
242262306a36Sopenharmony_ci			mii_cnt++;
242362306a36Sopenharmony_ci			return 0;
242462306a36Sopenharmony_ci		}
242562306a36Sopenharmony_ci		return -ENOENT;
242662306a36Sopenharmony_ci	}
242762306a36Sopenharmony_ci
242862306a36Sopenharmony_ci	bus_freq = 2500000; /* 2.5MHz by default */
242962306a36Sopenharmony_ci	node = of_get_child_by_name(pdev->dev.of_node, "mdio");
243062306a36Sopenharmony_ci	if (node) {
243162306a36Sopenharmony_ci		of_property_read_u32(node, "clock-frequency", &bus_freq);
243262306a36Sopenharmony_ci		suppress_preamble = of_property_read_bool(node,
243362306a36Sopenharmony_ci							  "suppress-preamble");
243462306a36Sopenharmony_ci	}
243562306a36Sopenharmony_ci
243662306a36Sopenharmony_ci	/*
243762306a36Sopenharmony_ci	 * Set MII speed (= clk_get_rate() / 2 * phy_speed)
243862306a36Sopenharmony_ci	 *
243962306a36Sopenharmony_ci	 * The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while
244062306a36Sopenharmony_ci	 * for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'.  The i.MX28
244162306a36Sopenharmony_ci	 * Reference Manual has an error on this, and gets fixed on i.MX6Q
244262306a36Sopenharmony_ci	 * document.
244362306a36Sopenharmony_ci	 */
244462306a36Sopenharmony_ci	mii_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), bus_freq * 2);
244562306a36Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_ENET_MAC)
244662306a36Sopenharmony_ci		mii_speed--;
244762306a36Sopenharmony_ci	if (mii_speed > 63) {
244862306a36Sopenharmony_ci		dev_err(&pdev->dev,
244962306a36Sopenharmony_ci			"fec clock (%lu) too fast to get right mii speed\n",
245062306a36Sopenharmony_ci			clk_get_rate(fep->clk_ipg));
245162306a36Sopenharmony_ci		err = -EINVAL;
245262306a36Sopenharmony_ci		goto err_out;
245362306a36Sopenharmony_ci	}
245462306a36Sopenharmony_ci
245562306a36Sopenharmony_ci	/*
245662306a36Sopenharmony_ci	 * The i.MX28 and i.MX6 types have another filed in the MSCR (aka
245762306a36Sopenharmony_ci	 * MII_SPEED) register that defines the MDIO output hold time. Earlier
245862306a36Sopenharmony_ci	 * versions are RAZ there, so just ignore the difference and write the
245962306a36Sopenharmony_ci	 * register always.
246062306a36Sopenharmony_ci	 * The minimal hold time according to IEE802.3 (clause 22) is 10 ns.
246162306a36Sopenharmony_ci	 * HOLDTIME + 1 is the number of clk cycles the fec is holding the
246262306a36Sopenharmony_ci	 * output.
246362306a36Sopenharmony_ci	 * The HOLDTIME bitfield takes values between 0 and 7 (inclusive).
246462306a36Sopenharmony_ci	 * Given that ceil(clkrate / 5000000) <= 64, the calculation for
246562306a36Sopenharmony_ci	 * holdtime cannot result in a value greater than 3.
246662306a36Sopenharmony_ci	 */
246762306a36Sopenharmony_ci	holdtime = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 100000000) - 1;
246862306a36Sopenharmony_ci
246962306a36Sopenharmony_ci	fep->phy_speed = mii_speed << 1 | holdtime << 8;
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_ci	if (suppress_preamble)
247262306a36Sopenharmony_ci		fep->phy_speed |= BIT(7);
247362306a36Sopenharmony_ci
247462306a36Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_CLEAR_SETUP_MII) {
247562306a36Sopenharmony_ci		/* Clear MMFR to avoid to generate MII event by writing MSCR.
247662306a36Sopenharmony_ci		 * MII event generation condition:
247762306a36Sopenharmony_ci		 * - writing MSCR:
247862306a36Sopenharmony_ci		 *	- mmfr[31:0]_not_zero & mscr[7:0]_is_zero &
247962306a36Sopenharmony_ci		 *	  mscr_reg_data_in[7:0] != 0
248062306a36Sopenharmony_ci		 * - writing MMFR:
248162306a36Sopenharmony_ci		 *	- mscr[7:0]_not_zero
248262306a36Sopenharmony_ci		 */
248362306a36Sopenharmony_ci		writel(0, fep->hwp + FEC_MII_DATA);
248462306a36Sopenharmony_ci	}
248562306a36Sopenharmony_ci
248662306a36Sopenharmony_ci	writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
248762306a36Sopenharmony_ci
248862306a36Sopenharmony_ci	/* Clear any pending transaction complete indication */
248962306a36Sopenharmony_ci	writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT);
249062306a36Sopenharmony_ci
249162306a36Sopenharmony_ci	fep->mii_bus = mdiobus_alloc();
249262306a36Sopenharmony_ci	if (fep->mii_bus == NULL) {
249362306a36Sopenharmony_ci		err = -ENOMEM;
249462306a36Sopenharmony_ci		goto err_out;
249562306a36Sopenharmony_ci	}
249662306a36Sopenharmony_ci
249762306a36Sopenharmony_ci	fep->mii_bus->name = "fec_enet_mii_bus";
249862306a36Sopenharmony_ci	fep->mii_bus->read = fec_enet_mdio_read_c22;
249962306a36Sopenharmony_ci	fep->mii_bus->write = fec_enet_mdio_write_c22;
250062306a36Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_HAS_MDIO_C45) {
250162306a36Sopenharmony_ci		fep->mii_bus->read_c45 = fec_enet_mdio_read_c45;
250262306a36Sopenharmony_ci		fep->mii_bus->write_c45 = fec_enet_mdio_write_c45;
250362306a36Sopenharmony_ci	}
250462306a36Sopenharmony_ci	snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
250562306a36Sopenharmony_ci		pdev->name, fep->dev_id + 1);
250662306a36Sopenharmony_ci	fep->mii_bus->priv = fep;
250762306a36Sopenharmony_ci	fep->mii_bus->parent = &pdev->dev;
250862306a36Sopenharmony_ci
250962306a36Sopenharmony_ci	err = of_mdiobus_register(fep->mii_bus, node);
251062306a36Sopenharmony_ci	if (err)
251162306a36Sopenharmony_ci		goto err_out_free_mdiobus;
251262306a36Sopenharmony_ci	of_node_put(node);
251362306a36Sopenharmony_ci
251462306a36Sopenharmony_ci	mii_cnt++;
251562306a36Sopenharmony_ci
251662306a36Sopenharmony_ci	/* save fec0 mii_bus */
251762306a36Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_SINGLE_MDIO)
251862306a36Sopenharmony_ci		fec0_mii_bus = fep->mii_bus;
251962306a36Sopenharmony_ci
252062306a36Sopenharmony_ci	return 0;
252162306a36Sopenharmony_ci
252262306a36Sopenharmony_cierr_out_free_mdiobus:
252362306a36Sopenharmony_ci	mdiobus_free(fep->mii_bus);
252462306a36Sopenharmony_cierr_out:
252562306a36Sopenharmony_ci	of_node_put(node);
252662306a36Sopenharmony_ci	return err;
252762306a36Sopenharmony_ci}
252862306a36Sopenharmony_ci
252962306a36Sopenharmony_cistatic void fec_enet_mii_remove(struct fec_enet_private *fep)
253062306a36Sopenharmony_ci{
253162306a36Sopenharmony_ci	if (--mii_cnt == 0) {
253262306a36Sopenharmony_ci		mdiobus_unregister(fep->mii_bus);
253362306a36Sopenharmony_ci		mdiobus_free(fep->mii_bus);
253462306a36Sopenharmony_ci	}
253562306a36Sopenharmony_ci}
253662306a36Sopenharmony_ci
253762306a36Sopenharmony_cistatic void fec_enet_get_drvinfo(struct net_device *ndev,
253862306a36Sopenharmony_ci				 struct ethtool_drvinfo *info)
253962306a36Sopenharmony_ci{
254062306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
254162306a36Sopenharmony_ci
254262306a36Sopenharmony_ci	strscpy(info->driver, fep->pdev->dev.driver->name,
254362306a36Sopenharmony_ci		sizeof(info->driver));
254462306a36Sopenharmony_ci	strscpy(info->bus_info, dev_name(&ndev->dev), sizeof(info->bus_info));
254562306a36Sopenharmony_ci}
254662306a36Sopenharmony_ci
254762306a36Sopenharmony_cistatic int fec_enet_get_regs_len(struct net_device *ndev)
254862306a36Sopenharmony_ci{
254962306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
255062306a36Sopenharmony_ci	struct resource *r;
255162306a36Sopenharmony_ci	int s = 0;
255262306a36Sopenharmony_ci
255362306a36Sopenharmony_ci	r = platform_get_resource(fep->pdev, IORESOURCE_MEM, 0);
255462306a36Sopenharmony_ci	if (r)
255562306a36Sopenharmony_ci		s = resource_size(r);
255662306a36Sopenharmony_ci
255762306a36Sopenharmony_ci	return s;
255862306a36Sopenharmony_ci}
255962306a36Sopenharmony_ci
256062306a36Sopenharmony_ci/* List of registers that can be safety be read to dump them with ethtool */
256162306a36Sopenharmony_ci#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
256262306a36Sopenharmony_ci	defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \
256362306a36Sopenharmony_ci	defined(CONFIG_ARM64) || defined(CONFIG_COMPILE_TEST)
256462306a36Sopenharmony_cistatic __u32 fec_enet_register_version = 2;
256562306a36Sopenharmony_cistatic u32 fec_enet_register_offset[] = {
256662306a36Sopenharmony_ci	FEC_IEVENT, FEC_IMASK, FEC_R_DES_ACTIVE_0, FEC_X_DES_ACTIVE_0,
256762306a36Sopenharmony_ci	FEC_ECNTRL, FEC_MII_DATA, FEC_MII_SPEED, FEC_MIB_CTRLSTAT, FEC_R_CNTRL,
256862306a36Sopenharmony_ci	FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, FEC_OPD, FEC_TXIC0, FEC_TXIC1,
256962306a36Sopenharmony_ci	FEC_TXIC2, FEC_RXIC0, FEC_RXIC1, FEC_RXIC2, FEC_HASH_TABLE_HIGH,
257062306a36Sopenharmony_ci	FEC_HASH_TABLE_LOW, FEC_GRP_HASH_TABLE_HIGH, FEC_GRP_HASH_TABLE_LOW,
257162306a36Sopenharmony_ci	FEC_X_WMRK, FEC_R_BOUND, FEC_R_FSTART, FEC_R_DES_START_1,
257262306a36Sopenharmony_ci	FEC_X_DES_START_1, FEC_R_BUFF_SIZE_1, FEC_R_DES_START_2,
257362306a36Sopenharmony_ci	FEC_X_DES_START_2, FEC_R_BUFF_SIZE_2, FEC_R_DES_START_0,
257462306a36Sopenharmony_ci	FEC_X_DES_START_0, FEC_R_BUFF_SIZE_0, FEC_R_FIFO_RSFL, FEC_R_FIFO_RSEM,
257562306a36Sopenharmony_ci	FEC_R_FIFO_RAEM, FEC_R_FIFO_RAFL, FEC_RACC, FEC_RCMR_1, FEC_RCMR_2,
257662306a36Sopenharmony_ci	FEC_DMA_CFG_1, FEC_DMA_CFG_2, FEC_R_DES_ACTIVE_1, FEC_X_DES_ACTIVE_1,
257762306a36Sopenharmony_ci	FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_2, FEC_QOS_SCHEME,
257862306a36Sopenharmony_ci	RMON_T_DROP, RMON_T_PACKETS, RMON_T_BC_PKT, RMON_T_MC_PKT,
257962306a36Sopenharmony_ci	RMON_T_CRC_ALIGN, RMON_T_UNDERSIZE, RMON_T_OVERSIZE, RMON_T_FRAG,
258062306a36Sopenharmony_ci	RMON_T_JAB, RMON_T_COL, RMON_T_P64, RMON_T_P65TO127, RMON_T_P128TO255,
258162306a36Sopenharmony_ci	RMON_T_P256TO511, RMON_T_P512TO1023, RMON_T_P1024TO2047,
258262306a36Sopenharmony_ci	RMON_T_P_GTE2048, RMON_T_OCTETS,
258362306a36Sopenharmony_ci	IEEE_T_DROP, IEEE_T_FRAME_OK, IEEE_T_1COL, IEEE_T_MCOL, IEEE_T_DEF,
258462306a36Sopenharmony_ci	IEEE_T_LCOL, IEEE_T_EXCOL, IEEE_T_MACERR, IEEE_T_CSERR, IEEE_T_SQE,
258562306a36Sopenharmony_ci	IEEE_T_FDXFC, IEEE_T_OCTETS_OK,
258662306a36Sopenharmony_ci	RMON_R_PACKETS, RMON_R_BC_PKT, RMON_R_MC_PKT, RMON_R_CRC_ALIGN,
258762306a36Sopenharmony_ci	RMON_R_UNDERSIZE, RMON_R_OVERSIZE, RMON_R_FRAG, RMON_R_JAB,
258862306a36Sopenharmony_ci	RMON_R_RESVD_O, RMON_R_P64, RMON_R_P65TO127, RMON_R_P128TO255,
258962306a36Sopenharmony_ci	RMON_R_P256TO511, RMON_R_P512TO1023, RMON_R_P1024TO2047,
259062306a36Sopenharmony_ci	RMON_R_P_GTE2048, RMON_R_OCTETS,
259162306a36Sopenharmony_ci	IEEE_R_DROP, IEEE_R_FRAME_OK, IEEE_R_CRC, IEEE_R_ALIGN, IEEE_R_MACERR,
259262306a36Sopenharmony_ci	IEEE_R_FDXFC, IEEE_R_OCTETS_OK
259362306a36Sopenharmony_ci};
259462306a36Sopenharmony_ci/* for i.MX6ul */
259562306a36Sopenharmony_cistatic u32 fec_enet_register_offset_6ul[] = {
259662306a36Sopenharmony_ci	FEC_IEVENT, FEC_IMASK, FEC_R_DES_ACTIVE_0, FEC_X_DES_ACTIVE_0,
259762306a36Sopenharmony_ci	FEC_ECNTRL, FEC_MII_DATA, FEC_MII_SPEED, FEC_MIB_CTRLSTAT, FEC_R_CNTRL,
259862306a36Sopenharmony_ci	FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, FEC_OPD, FEC_TXIC0, FEC_RXIC0,
259962306a36Sopenharmony_ci	FEC_HASH_TABLE_HIGH, FEC_HASH_TABLE_LOW, FEC_GRP_HASH_TABLE_HIGH,
260062306a36Sopenharmony_ci	FEC_GRP_HASH_TABLE_LOW, FEC_X_WMRK, FEC_R_DES_START_0,
260162306a36Sopenharmony_ci	FEC_X_DES_START_0, FEC_R_BUFF_SIZE_0, FEC_R_FIFO_RSFL, FEC_R_FIFO_RSEM,
260262306a36Sopenharmony_ci	FEC_R_FIFO_RAEM, FEC_R_FIFO_RAFL, FEC_RACC,
260362306a36Sopenharmony_ci	RMON_T_DROP, RMON_T_PACKETS, RMON_T_BC_PKT, RMON_T_MC_PKT,
260462306a36Sopenharmony_ci	RMON_T_CRC_ALIGN, RMON_T_UNDERSIZE, RMON_T_OVERSIZE, RMON_T_FRAG,
260562306a36Sopenharmony_ci	RMON_T_JAB, RMON_T_COL, RMON_T_P64, RMON_T_P65TO127, RMON_T_P128TO255,
260662306a36Sopenharmony_ci	RMON_T_P256TO511, RMON_T_P512TO1023, RMON_T_P1024TO2047,
260762306a36Sopenharmony_ci	RMON_T_P_GTE2048, RMON_T_OCTETS,
260862306a36Sopenharmony_ci	IEEE_T_DROP, IEEE_T_FRAME_OK, IEEE_T_1COL, IEEE_T_MCOL, IEEE_T_DEF,
260962306a36Sopenharmony_ci	IEEE_T_LCOL, IEEE_T_EXCOL, IEEE_T_MACERR, IEEE_T_CSERR, IEEE_T_SQE,
261062306a36Sopenharmony_ci	IEEE_T_FDXFC, IEEE_T_OCTETS_OK,
261162306a36Sopenharmony_ci	RMON_R_PACKETS, RMON_R_BC_PKT, RMON_R_MC_PKT, RMON_R_CRC_ALIGN,
261262306a36Sopenharmony_ci	RMON_R_UNDERSIZE, RMON_R_OVERSIZE, RMON_R_FRAG, RMON_R_JAB,
261362306a36Sopenharmony_ci	RMON_R_RESVD_O, RMON_R_P64, RMON_R_P65TO127, RMON_R_P128TO255,
261462306a36Sopenharmony_ci	RMON_R_P256TO511, RMON_R_P512TO1023, RMON_R_P1024TO2047,
261562306a36Sopenharmony_ci	RMON_R_P_GTE2048, RMON_R_OCTETS,
261662306a36Sopenharmony_ci	IEEE_R_DROP, IEEE_R_FRAME_OK, IEEE_R_CRC, IEEE_R_ALIGN, IEEE_R_MACERR,
261762306a36Sopenharmony_ci	IEEE_R_FDXFC, IEEE_R_OCTETS_OK
261862306a36Sopenharmony_ci};
261962306a36Sopenharmony_ci#else
262062306a36Sopenharmony_cistatic __u32 fec_enet_register_version = 1;
262162306a36Sopenharmony_cistatic u32 fec_enet_register_offset[] = {
262262306a36Sopenharmony_ci	FEC_ECNTRL, FEC_IEVENT, FEC_IMASK, FEC_IVEC, FEC_R_DES_ACTIVE_0,
262362306a36Sopenharmony_ci	FEC_R_DES_ACTIVE_1, FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_0,
262462306a36Sopenharmony_ci	FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2, FEC_MII_DATA, FEC_MII_SPEED,
262562306a36Sopenharmony_ci	FEC_R_BOUND, FEC_R_FSTART, FEC_X_WMRK, FEC_X_FSTART, FEC_R_CNTRL,
262662306a36Sopenharmony_ci	FEC_MAX_FRM_LEN, FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH,
262762306a36Sopenharmony_ci	FEC_GRP_HASH_TABLE_HIGH, FEC_GRP_HASH_TABLE_LOW, FEC_R_DES_START_0,
262862306a36Sopenharmony_ci	FEC_R_DES_START_1, FEC_R_DES_START_2, FEC_X_DES_START_0,
262962306a36Sopenharmony_ci	FEC_X_DES_START_1, FEC_X_DES_START_2, FEC_R_BUFF_SIZE_0,
263062306a36Sopenharmony_ci	FEC_R_BUFF_SIZE_1, FEC_R_BUFF_SIZE_2
263162306a36Sopenharmony_ci};
263262306a36Sopenharmony_ci#endif
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_cistatic void fec_enet_get_regs(struct net_device *ndev,
263562306a36Sopenharmony_ci			      struct ethtool_regs *regs, void *regbuf)
263662306a36Sopenharmony_ci{
263762306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
263862306a36Sopenharmony_ci	u32 __iomem *theregs = (u32 __iomem *)fep->hwp;
263962306a36Sopenharmony_ci	struct device *dev = &fep->pdev->dev;
264062306a36Sopenharmony_ci	u32 *buf = (u32 *)regbuf;
264162306a36Sopenharmony_ci	u32 i, off;
264262306a36Sopenharmony_ci	int ret;
264362306a36Sopenharmony_ci#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
264462306a36Sopenharmony_ci	defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \
264562306a36Sopenharmony_ci	defined(CONFIG_ARM64) || defined(CONFIG_COMPILE_TEST)
264662306a36Sopenharmony_ci	u32 *reg_list;
264762306a36Sopenharmony_ci	u32 reg_cnt;
264862306a36Sopenharmony_ci
264962306a36Sopenharmony_ci	if (!of_machine_is_compatible("fsl,imx6ul")) {
265062306a36Sopenharmony_ci		reg_list = fec_enet_register_offset;
265162306a36Sopenharmony_ci		reg_cnt = ARRAY_SIZE(fec_enet_register_offset);
265262306a36Sopenharmony_ci	} else {
265362306a36Sopenharmony_ci		reg_list = fec_enet_register_offset_6ul;
265462306a36Sopenharmony_ci		reg_cnt = ARRAY_SIZE(fec_enet_register_offset_6ul);
265562306a36Sopenharmony_ci	}
265662306a36Sopenharmony_ci#else
265762306a36Sopenharmony_ci	/* coldfire */
265862306a36Sopenharmony_ci	static u32 *reg_list = fec_enet_register_offset;
265962306a36Sopenharmony_ci	static const u32 reg_cnt = ARRAY_SIZE(fec_enet_register_offset);
266062306a36Sopenharmony_ci#endif
266162306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(dev);
266262306a36Sopenharmony_ci	if (ret < 0)
266362306a36Sopenharmony_ci		return;
266462306a36Sopenharmony_ci
266562306a36Sopenharmony_ci	regs->version = fec_enet_register_version;
266662306a36Sopenharmony_ci
266762306a36Sopenharmony_ci	memset(buf, 0, regs->len);
266862306a36Sopenharmony_ci
266962306a36Sopenharmony_ci	for (i = 0; i < reg_cnt; i++) {
267062306a36Sopenharmony_ci		off = reg_list[i];
267162306a36Sopenharmony_ci
267262306a36Sopenharmony_ci		if ((off == FEC_R_BOUND || off == FEC_R_FSTART) &&
267362306a36Sopenharmony_ci		    !(fep->quirks & FEC_QUIRK_HAS_FRREG))
267462306a36Sopenharmony_ci			continue;
267562306a36Sopenharmony_ci
267662306a36Sopenharmony_ci		off >>= 2;
267762306a36Sopenharmony_ci		buf[off] = readl(&theregs[off]);
267862306a36Sopenharmony_ci	}
267962306a36Sopenharmony_ci
268062306a36Sopenharmony_ci	pm_runtime_mark_last_busy(dev);
268162306a36Sopenharmony_ci	pm_runtime_put_autosuspend(dev);
268262306a36Sopenharmony_ci}
268362306a36Sopenharmony_ci
268462306a36Sopenharmony_cistatic int fec_enet_get_ts_info(struct net_device *ndev,
268562306a36Sopenharmony_ci				struct ethtool_ts_info *info)
268662306a36Sopenharmony_ci{
268762306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
268862306a36Sopenharmony_ci
268962306a36Sopenharmony_ci	if (fep->bufdesc_ex) {
269062306a36Sopenharmony_ci
269162306a36Sopenharmony_ci		info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
269262306a36Sopenharmony_ci					SOF_TIMESTAMPING_RX_SOFTWARE |
269362306a36Sopenharmony_ci					SOF_TIMESTAMPING_SOFTWARE |
269462306a36Sopenharmony_ci					SOF_TIMESTAMPING_TX_HARDWARE |
269562306a36Sopenharmony_ci					SOF_TIMESTAMPING_RX_HARDWARE |
269662306a36Sopenharmony_ci					SOF_TIMESTAMPING_RAW_HARDWARE;
269762306a36Sopenharmony_ci		if (fep->ptp_clock)
269862306a36Sopenharmony_ci			info->phc_index = ptp_clock_index(fep->ptp_clock);
269962306a36Sopenharmony_ci		else
270062306a36Sopenharmony_ci			info->phc_index = -1;
270162306a36Sopenharmony_ci
270262306a36Sopenharmony_ci		info->tx_types = (1 << HWTSTAMP_TX_OFF) |
270362306a36Sopenharmony_ci				 (1 << HWTSTAMP_TX_ON);
270462306a36Sopenharmony_ci
270562306a36Sopenharmony_ci		info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) |
270662306a36Sopenharmony_ci				   (1 << HWTSTAMP_FILTER_ALL);
270762306a36Sopenharmony_ci		return 0;
270862306a36Sopenharmony_ci	} else {
270962306a36Sopenharmony_ci		return ethtool_op_get_ts_info(ndev, info);
271062306a36Sopenharmony_ci	}
271162306a36Sopenharmony_ci}
271262306a36Sopenharmony_ci
271362306a36Sopenharmony_ci#if !defined(CONFIG_M5272)
271462306a36Sopenharmony_ci
271562306a36Sopenharmony_cistatic void fec_enet_get_pauseparam(struct net_device *ndev,
271662306a36Sopenharmony_ci				    struct ethtool_pauseparam *pause)
271762306a36Sopenharmony_ci{
271862306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
271962306a36Sopenharmony_ci
272062306a36Sopenharmony_ci	pause->autoneg = (fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) != 0;
272162306a36Sopenharmony_ci	pause->tx_pause = (fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) != 0;
272262306a36Sopenharmony_ci	pause->rx_pause = pause->tx_pause;
272362306a36Sopenharmony_ci}
272462306a36Sopenharmony_ci
272562306a36Sopenharmony_cistatic int fec_enet_set_pauseparam(struct net_device *ndev,
272662306a36Sopenharmony_ci				   struct ethtool_pauseparam *pause)
272762306a36Sopenharmony_ci{
272862306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
272962306a36Sopenharmony_ci
273062306a36Sopenharmony_ci	if (!ndev->phydev)
273162306a36Sopenharmony_ci		return -ENODEV;
273262306a36Sopenharmony_ci
273362306a36Sopenharmony_ci	if (pause->tx_pause != pause->rx_pause) {
273462306a36Sopenharmony_ci		netdev_info(ndev,
273562306a36Sopenharmony_ci			"hardware only support enable/disable both tx and rx");
273662306a36Sopenharmony_ci		return -EINVAL;
273762306a36Sopenharmony_ci	}
273862306a36Sopenharmony_ci
273962306a36Sopenharmony_ci	fep->pause_flag = 0;
274062306a36Sopenharmony_ci
274162306a36Sopenharmony_ci	/* tx pause must be same as rx pause */
274262306a36Sopenharmony_ci	fep->pause_flag |= pause->rx_pause ? FEC_PAUSE_FLAG_ENABLE : 0;
274362306a36Sopenharmony_ci	fep->pause_flag |= pause->autoneg ? FEC_PAUSE_FLAG_AUTONEG : 0;
274462306a36Sopenharmony_ci
274562306a36Sopenharmony_ci	phy_set_sym_pause(ndev->phydev, pause->rx_pause, pause->tx_pause,
274662306a36Sopenharmony_ci			  pause->autoneg);
274762306a36Sopenharmony_ci
274862306a36Sopenharmony_ci	if (pause->autoneg) {
274962306a36Sopenharmony_ci		if (netif_running(ndev))
275062306a36Sopenharmony_ci			fec_stop(ndev);
275162306a36Sopenharmony_ci		phy_start_aneg(ndev->phydev);
275262306a36Sopenharmony_ci	}
275362306a36Sopenharmony_ci	if (netif_running(ndev)) {
275462306a36Sopenharmony_ci		napi_disable(&fep->napi);
275562306a36Sopenharmony_ci		netif_tx_lock_bh(ndev);
275662306a36Sopenharmony_ci		fec_restart(ndev);
275762306a36Sopenharmony_ci		netif_tx_wake_all_queues(ndev);
275862306a36Sopenharmony_ci		netif_tx_unlock_bh(ndev);
275962306a36Sopenharmony_ci		napi_enable(&fep->napi);
276062306a36Sopenharmony_ci	}
276162306a36Sopenharmony_ci
276262306a36Sopenharmony_ci	return 0;
276362306a36Sopenharmony_ci}
276462306a36Sopenharmony_ci
276562306a36Sopenharmony_cistatic const struct fec_stat {
276662306a36Sopenharmony_ci	char name[ETH_GSTRING_LEN];
276762306a36Sopenharmony_ci	u16 offset;
276862306a36Sopenharmony_ci} fec_stats[] = {
276962306a36Sopenharmony_ci	/* RMON TX */
277062306a36Sopenharmony_ci	{ "tx_dropped", RMON_T_DROP },
277162306a36Sopenharmony_ci	{ "tx_packets", RMON_T_PACKETS },
277262306a36Sopenharmony_ci	{ "tx_broadcast", RMON_T_BC_PKT },
277362306a36Sopenharmony_ci	{ "tx_multicast", RMON_T_MC_PKT },
277462306a36Sopenharmony_ci	{ "tx_crc_errors", RMON_T_CRC_ALIGN },
277562306a36Sopenharmony_ci	{ "tx_undersize", RMON_T_UNDERSIZE },
277662306a36Sopenharmony_ci	{ "tx_oversize", RMON_T_OVERSIZE },
277762306a36Sopenharmony_ci	{ "tx_fragment", RMON_T_FRAG },
277862306a36Sopenharmony_ci	{ "tx_jabber", RMON_T_JAB },
277962306a36Sopenharmony_ci	{ "tx_collision", RMON_T_COL },
278062306a36Sopenharmony_ci	{ "tx_64byte", RMON_T_P64 },
278162306a36Sopenharmony_ci	{ "tx_65to127byte", RMON_T_P65TO127 },
278262306a36Sopenharmony_ci	{ "tx_128to255byte", RMON_T_P128TO255 },
278362306a36Sopenharmony_ci	{ "tx_256to511byte", RMON_T_P256TO511 },
278462306a36Sopenharmony_ci	{ "tx_512to1023byte", RMON_T_P512TO1023 },
278562306a36Sopenharmony_ci	{ "tx_1024to2047byte", RMON_T_P1024TO2047 },
278662306a36Sopenharmony_ci	{ "tx_GTE2048byte", RMON_T_P_GTE2048 },
278762306a36Sopenharmony_ci	{ "tx_octets", RMON_T_OCTETS },
278862306a36Sopenharmony_ci
278962306a36Sopenharmony_ci	/* IEEE TX */
279062306a36Sopenharmony_ci	{ "IEEE_tx_drop", IEEE_T_DROP },
279162306a36Sopenharmony_ci	{ "IEEE_tx_frame_ok", IEEE_T_FRAME_OK },
279262306a36Sopenharmony_ci	{ "IEEE_tx_1col", IEEE_T_1COL },
279362306a36Sopenharmony_ci	{ "IEEE_tx_mcol", IEEE_T_MCOL },
279462306a36Sopenharmony_ci	{ "IEEE_tx_def", IEEE_T_DEF },
279562306a36Sopenharmony_ci	{ "IEEE_tx_lcol", IEEE_T_LCOL },
279662306a36Sopenharmony_ci	{ "IEEE_tx_excol", IEEE_T_EXCOL },
279762306a36Sopenharmony_ci	{ "IEEE_tx_macerr", IEEE_T_MACERR },
279862306a36Sopenharmony_ci	{ "IEEE_tx_cserr", IEEE_T_CSERR },
279962306a36Sopenharmony_ci	{ "IEEE_tx_sqe", IEEE_T_SQE },
280062306a36Sopenharmony_ci	{ "IEEE_tx_fdxfc", IEEE_T_FDXFC },
280162306a36Sopenharmony_ci	{ "IEEE_tx_octets_ok", IEEE_T_OCTETS_OK },
280262306a36Sopenharmony_ci
280362306a36Sopenharmony_ci	/* RMON RX */
280462306a36Sopenharmony_ci	{ "rx_packets", RMON_R_PACKETS },
280562306a36Sopenharmony_ci	{ "rx_broadcast", RMON_R_BC_PKT },
280662306a36Sopenharmony_ci	{ "rx_multicast", RMON_R_MC_PKT },
280762306a36Sopenharmony_ci	{ "rx_crc_errors", RMON_R_CRC_ALIGN },
280862306a36Sopenharmony_ci	{ "rx_undersize", RMON_R_UNDERSIZE },
280962306a36Sopenharmony_ci	{ "rx_oversize", RMON_R_OVERSIZE },
281062306a36Sopenharmony_ci	{ "rx_fragment", RMON_R_FRAG },
281162306a36Sopenharmony_ci	{ "rx_jabber", RMON_R_JAB },
281262306a36Sopenharmony_ci	{ "rx_64byte", RMON_R_P64 },
281362306a36Sopenharmony_ci	{ "rx_65to127byte", RMON_R_P65TO127 },
281462306a36Sopenharmony_ci	{ "rx_128to255byte", RMON_R_P128TO255 },
281562306a36Sopenharmony_ci	{ "rx_256to511byte", RMON_R_P256TO511 },
281662306a36Sopenharmony_ci	{ "rx_512to1023byte", RMON_R_P512TO1023 },
281762306a36Sopenharmony_ci	{ "rx_1024to2047byte", RMON_R_P1024TO2047 },
281862306a36Sopenharmony_ci	{ "rx_GTE2048byte", RMON_R_P_GTE2048 },
281962306a36Sopenharmony_ci	{ "rx_octets", RMON_R_OCTETS },
282062306a36Sopenharmony_ci
282162306a36Sopenharmony_ci	/* IEEE RX */
282262306a36Sopenharmony_ci	{ "IEEE_rx_drop", IEEE_R_DROP },
282362306a36Sopenharmony_ci	{ "IEEE_rx_frame_ok", IEEE_R_FRAME_OK },
282462306a36Sopenharmony_ci	{ "IEEE_rx_crc", IEEE_R_CRC },
282562306a36Sopenharmony_ci	{ "IEEE_rx_align", IEEE_R_ALIGN },
282662306a36Sopenharmony_ci	{ "IEEE_rx_macerr", IEEE_R_MACERR },
282762306a36Sopenharmony_ci	{ "IEEE_rx_fdxfc", IEEE_R_FDXFC },
282862306a36Sopenharmony_ci	{ "IEEE_rx_octets_ok", IEEE_R_OCTETS_OK },
282962306a36Sopenharmony_ci};
283062306a36Sopenharmony_ci
283162306a36Sopenharmony_ci#define FEC_STATS_SIZE		(ARRAY_SIZE(fec_stats) * sizeof(u64))
283262306a36Sopenharmony_ci
283362306a36Sopenharmony_cistatic const char *fec_xdp_stat_strs[XDP_STATS_TOTAL] = {
283462306a36Sopenharmony_ci	"rx_xdp_redirect",           /* RX_XDP_REDIRECT = 0, */
283562306a36Sopenharmony_ci	"rx_xdp_pass",               /* RX_XDP_PASS, */
283662306a36Sopenharmony_ci	"rx_xdp_drop",               /* RX_XDP_DROP, */
283762306a36Sopenharmony_ci	"rx_xdp_tx",                 /* RX_XDP_TX, */
283862306a36Sopenharmony_ci	"rx_xdp_tx_errors",          /* RX_XDP_TX_ERRORS, */
283962306a36Sopenharmony_ci	"tx_xdp_xmit",               /* TX_XDP_XMIT, */
284062306a36Sopenharmony_ci	"tx_xdp_xmit_errors",        /* TX_XDP_XMIT_ERRORS, */
284162306a36Sopenharmony_ci};
284262306a36Sopenharmony_ci
284362306a36Sopenharmony_cistatic void fec_enet_update_ethtool_stats(struct net_device *dev)
284462306a36Sopenharmony_ci{
284562306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(dev);
284662306a36Sopenharmony_ci	int i;
284762306a36Sopenharmony_ci
284862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(fec_stats); i++)
284962306a36Sopenharmony_ci		fep->ethtool_stats[i] = readl(fep->hwp + fec_stats[i].offset);
285062306a36Sopenharmony_ci}
285162306a36Sopenharmony_ci
285262306a36Sopenharmony_cistatic void fec_enet_get_xdp_stats(struct fec_enet_private *fep, u64 *data)
285362306a36Sopenharmony_ci{
285462306a36Sopenharmony_ci	u64 xdp_stats[XDP_STATS_TOTAL] = { 0 };
285562306a36Sopenharmony_ci	struct fec_enet_priv_rx_q *rxq;
285662306a36Sopenharmony_ci	int i, j;
285762306a36Sopenharmony_ci
285862306a36Sopenharmony_ci	for (i = fep->num_rx_queues - 1; i >= 0; i--) {
285962306a36Sopenharmony_ci		rxq = fep->rx_queue[i];
286062306a36Sopenharmony_ci
286162306a36Sopenharmony_ci		for (j = 0; j < XDP_STATS_TOTAL; j++)
286262306a36Sopenharmony_ci			xdp_stats[j] += rxq->stats[j];
286362306a36Sopenharmony_ci	}
286462306a36Sopenharmony_ci
286562306a36Sopenharmony_ci	memcpy(data, xdp_stats, sizeof(xdp_stats));
286662306a36Sopenharmony_ci}
286762306a36Sopenharmony_ci
286862306a36Sopenharmony_cistatic void fec_enet_page_pool_stats(struct fec_enet_private *fep, u64 *data)
286962306a36Sopenharmony_ci{
287062306a36Sopenharmony_ci#ifdef CONFIG_PAGE_POOL_STATS
287162306a36Sopenharmony_ci	struct page_pool_stats stats = {};
287262306a36Sopenharmony_ci	struct fec_enet_priv_rx_q *rxq;
287362306a36Sopenharmony_ci	int i;
287462306a36Sopenharmony_ci
287562306a36Sopenharmony_ci	for (i = fep->num_rx_queues - 1; i >= 0; i--) {
287662306a36Sopenharmony_ci		rxq = fep->rx_queue[i];
287762306a36Sopenharmony_ci
287862306a36Sopenharmony_ci		if (!rxq->page_pool)
287962306a36Sopenharmony_ci			continue;
288062306a36Sopenharmony_ci
288162306a36Sopenharmony_ci		page_pool_get_stats(rxq->page_pool, &stats);
288262306a36Sopenharmony_ci	}
288362306a36Sopenharmony_ci
288462306a36Sopenharmony_ci	page_pool_ethtool_stats_get(data, &stats);
288562306a36Sopenharmony_ci#endif
288662306a36Sopenharmony_ci}
288762306a36Sopenharmony_ci
288862306a36Sopenharmony_cistatic void fec_enet_get_ethtool_stats(struct net_device *dev,
288962306a36Sopenharmony_ci				       struct ethtool_stats *stats, u64 *data)
289062306a36Sopenharmony_ci{
289162306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(dev);
289262306a36Sopenharmony_ci
289362306a36Sopenharmony_ci	if (netif_running(dev))
289462306a36Sopenharmony_ci		fec_enet_update_ethtool_stats(dev);
289562306a36Sopenharmony_ci
289662306a36Sopenharmony_ci	memcpy(data, fep->ethtool_stats, FEC_STATS_SIZE);
289762306a36Sopenharmony_ci	data += FEC_STATS_SIZE / sizeof(u64);
289862306a36Sopenharmony_ci
289962306a36Sopenharmony_ci	fec_enet_get_xdp_stats(fep, data);
290062306a36Sopenharmony_ci	data += XDP_STATS_TOTAL;
290162306a36Sopenharmony_ci
290262306a36Sopenharmony_ci	fec_enet_page_pool_stats(fep, data);
290362306a36Sopenharmony_ci}
290462306a36Sopenharmony_ci
290562306a36Sopenharmony_cistatic void fec_enet_get_strings(struct net_device *netdev,
290662306a36Sopenharmony_ci	u32 stringset, u8 *data)
290762306a36Sopenharmony_ci{
290862306a36Sopenharmony_ci	int i;
290962306a36Sopenharmony_ci	switch (stringset) {
291062306a36Sopenharmony_ci	case ETH_SS_STATS:
291162306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(fec_stats); i++) {
291262306a36Sopenharmony_ci			memcpy(data, fec_stats[i].name, ETH_GSTRING_LEN);
291362306a36Sopenharmony_ci			data += ETH_GSTRING_LEN;
291462306a36Sopenharmony_ci		}
291562306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(fec_xdp_stat_strs); i++) {
291662306a36Sopenharmony_ci			strncpy(data, fec_xdp_stat_strs[i], ETH_GSTRING_LEN);
291762306a36Sopenharmony_ci			data += ETH_GSTRING_LEN;
291862306a36Sopenharmony_ci		}
291962306a36Sopenharmony_ci		page_pool_ethtool_stats_get_strings(data);
292062306a36Sopenharmony_ci
292162306a36Sopenharmony_ci		break;
292262306a36Sopenharmony_ci	case ETH_SS_TEST:
292362306a36Sopenharmony_ci		net_selftest_get_strings(data);
292462306a36Sopenharmony_ci		break;
292562306a36Sopenharmony_ci	}
292662306a36Sopenharmony_ci}
292762306a36Sopenharmony_ci
292862306a36Sopenharmony_cistatic int fec_enet_get_sset_count(struct net_device *dev, int sset)
292962306a36Sopenharmony_ci{
293062306a36Sopenharmony_ci	int count;
293162306a36Sopenharmony_ci
293262306a36Sopenharmony_ci	switch (sset) {
293362306a36Sopenharmony_ci	case ETH_SS_STATS:
293462306a36Sopenharmony_ci		count = ARRAY_SIZE(fec_stats) + XDP_STATS_TOTAL;
293562306a36Sopenharmony_ci		count += page_pool_ethtool_stats_get_count();
293662306a36Sopenharmony_ci		return count;
293762306a36Sopenharmony_ci
293862306a36Sopenharmony_ci	case ETH_SS_TEST:
293962306a36Sopenharmony_ci		return net_selftest_get_count();
294062306a36Sopenharmony_ci	default:
294162306a36Sopenharmony_ci		return -EOPNOTSUPP;
294262306a36Sopenharmony_ci	}
294362306a36Sopenharmony_ci}
294462306a36Sopenharmony_ci
294562306a36Sopenharmony_cistatic void fec_enet_clear_ethtool_stats(struct net_device *dev)
294662306a36Sopenharmony_ci{
294762306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(dev);
294862306a36Sopenharmony_ci	struct fec_enet_priv_rx_q *rxq;
294962306a36Sopenharmony_ci	int i, j;
295062306a36Sopenharmony_ci
295162306a36Sopenharmony_ci	/* Disable MIB statistics counters */
295262306a36Sopenharmony_ci	writel(FEC_MIB_CTRLSTAT_DISABLE, fep->hwp + FEC_MIB_CTRLSTAT);
295362306a36Sopenharmony_ci
295462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(fec_stats); i++)
295562306a36Sopenharmony_ci		writel(0, fep->hwp + fec_stats[i].offset);
295662306a36Sopenharmony_ci
295762306a36Sopenharmony_ci	for (i = fep->num_rx_queues - 1; i >= 0; i--) {
295862306a36Sopenharmony_ci		rxq = fep->rx_queue[i];
295962306a36Sopenharmony_ci		for (j = 0; j < XDP_STATS_TOTAL; j++)
296062306a36Sopenharmony_ci			rxq->stats[j] = 0;
296162306a36Sopenharmony_ci	}
296262306a36Sopenharmony_ci
296362306a36Sopenharmony_ci	/* Don't disable MIB statistics counters */
296462306a36Sopenharmony_ci	writel(0, fep->hwp + FEC_MIB_CTRLSTAT);
296562306a36Sopenharmony_ci}
296662306a36Sopenharmony_ci
296762306a36Sopenharmony_ci#else	/* !defined(CONFIG_M5272) */
296862306a36Sopenharmony_ci#define FEC_STATS_SIZE	0
296962306a36Sopenharmony_cistatic inline void fec_enet_update_ethtool_stats(struct net_device *dev)
297062306a36Sopenharmony_ci{
297162306a36Sopenharmony_ci}
297262306a36Sopenharmony_ci
297362306a36Sopenharmony_cistatic inline void fec_enet_clear_ethtool_stats(struct net_device *dev)
297462306a36Sopenharmony_ci{
297562306a36Sopenharmony_ci}
297662306a36Sopenharmony_ci#endif /* !defined(CONFIG_M5272) */
297762306a36Sopenharmony_ci
297862306a36Sopenharmony_ci/* ITR clock source is enet system clock (clk_ahb).
297962306a36Sopenharmony_ci * TCTT unit is cycle_ns * 64 cycle
298062306a36Sopenharmony_ci * So, the ICTT value = X us / (cycle_ns * 64)
298162306a36Sopenharmony_ci */
298262306a36Sopenharmony_cistatic int fec_enet_us_to_itr_clock(struct net_device *ndev, int us)
298362306a36Sopenharmony_ci{
298462306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
298562306a36Sopenharmony_ci
298662306a36Sopenharmony_ci	return us * (fep->itr_clk_rate / 64000) / 1000;
298762306a36Sopenharmony_ci}
298862306a36Sopenharmony_ci
298962306a36Sopenharmony_ci/* Set threshold for interrupt coalescing */
299062306a36Sopenharmony_cistatic void fec_enet_itr_coal_set(struct net_device *ndev)
299162306a36Sopenharmony_ci{
299262306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
299362306a36Sopenharmony_ci	int rx_itr, tx_itr;
299462306a36Sopenharmony_ci
299562306a36Sopenharmony_ci	/* Must be greater than zero to avoid unpredictable behavior */
299662306a36Sopenharmony_ci	if (!fep->rx_time_itr || !fep->rx_pkts_itr ||
299762306a36Sopenharmony_ci	    !fep->tx_time_itr || !fep->tx_pkts_itr)
299862306a36Sopenharmony_ci		return;
299962306a36Sopenharmony_ci
300062306a36Sopenharmony_ci	/* Select enet system clock as Interrupt Coalescing
300162306a36Sopenharmony_ci	 * timer Clock Source
300262306a36Sopenharmony_ci	 */
300362306a36Sopenharmony_ci	rx_itr = FEC_ITR_CLK_SEL;
300462306a36Sopenharmony_ci	tx_itr = FEC_ITR_CLK_SEL;
300562306a36Sopenharmony_ci
300662306a36Sopenharmony_ci	/* set ICFT and ICTT */
300762306a36Sopenharmony_ci	rx_itr |= FEC_ITR_ICFT(fep->rx_pkts_itr);
300862306a36Sopenharmony_ci	rx_itr |= FEC_ITR_ICTT(fec_enet_us_to_itr_clock(ndev, fep->rx_time_itr));
300962306a36Sopenharmony_ci	tx_itr |= FEC_ITR_ICFT(fep->tx_pkts_itr);
301062306a36Sopenharmony_ci	tx_itr |= FEC_ITR_ICTT(fec_enet_us_to_itr_clock(ndev, fep->tx_time_itr));
301162306a36Sopenharmony_ci
301262306a36Sopenharmony_ci	rx_itr |= FEC_ITR_EN;
301362306a36Sopenharmony_ci	tx_itr |= FEC_ITR_EN;
301462306a36Sopenharmony_ci
301562306a36Sopenharmony_ci	writel(tx_itr, fep->hwp + FEC_TXIC0);
301662306a36Sopenharmony_ci	writel(rx_itr, fep->hwp + FEC_RXIC0);
301762306a36Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) {
301862306a36Sopenharmony_ci		writel(tx_itr, fep->hwp + FEC_TXIC1);
301962306a36Sopenharmony_ci		writel(rx_itr, fep->hwp + FEC_RXIC1);
302062306a36Sopenharmony_ci		writel(tx_itr, fep->hwp + FEC_TXIC2);
302162306a36Sopenharmony_ci		writel(rx_itr, fep->hwp + FEC_RXIC2);
302262306a36Sopenharmony_ci	}
302362306a36Sopenharmony_ci}
302462306a36Sopenharmony_ci
302562306a36Sopenharmony_cistatic int fec_enet_get_coalesce(struct net_device *ndev,
302662306a36Sopenharmony_ci				 struct ethtool_coalesce *ec,
302762306a36Sopenharmony_ci				 struct kernel_ethtool_coalesce *kernel_coal,
302862306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
302962306a36Sopenharmony_ci{
303062306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
303162306a36Sopenharmony_ci
303262306a36Sopenharmony_ci	if (!(fep->quirks & FEC_QUIRK_HAS_COALESCE))
303362306a36Sopenharmony_ci		return -EOPNOTSUPP;
303462306a36Sopenharmony_ci
303562306a36Sopenharmony_ci	ec->rx_coalesce_usecs = fep->rx_time_itr;
303662306a36Sopenharmony_ci	ec->rx_max_coalesced_frames = fep->rx_pkts_itr;
303762306a36Sopenharmony_ci
303862306a36Sopenharmony_ci	ec->tx_coalesce_usecs = fep->tx_time_itr;
303962306a36Sopenharmony_ci	ec->tx_max_coalesced_frames = fep->tx_pkts_itr;
304062306a36Sopenharmony_ci
304162306a36Sopenharmony_ci	return 0;
304262306a36Sopenharmony_ci}
304362306a36Sopenharmony_ci
304462306a36Sopenharmony_cistatic int fec_enet_set_coalesce(struct net_device *ndev,
304562306a36Sopenharmony_ci				 struct ethtool_coalesce *ec,
304662306a36Sopenharmony_ci				 struct kernel_ethtool_coalesce *kernel_coal,
304762306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
304862306a36Sopenharmony_ci{
304962306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
305062306a36Sopenharmony_ci	struct device *dev = &fep->pdev->dev;
305162306a36Sopenharmony_ci	unsigned int cycle;
305262306a36Sopenharmony_ci
305362306a36Sopenharmony_ci	if (!(fep->quirks & FEC_QUIRK_HAS_COALESCE))
305462306a36Sopenharmony_ci		return -EOPNOTSUPP;
305562306a36Sopenharmony_ci
305662306a36Sopenharmony_ci	if (ec->rx_max_coalesced_frames > 255) {
305762306a36Sopenharmony_ci		dev_err(dev, "Rx coalesced frames exceed hardware limitation\n");
305862306a36Sopenharmony_ci		return -EINVAL;
305962306a36Sopenharmony_ci	}
306062306a36Sopenharmony_ci
306162306a36Sopenharmony_ci	if (ec->tx_max_coalesced_frames > 255) {
306262306a36Sopenharmony_ci		dev_err(dev, "Tx coalesced frame exceed hardware limitation\n");
306362306a36Sopenharmony_ci		return -EINVAL;
306462306a36Sopenharmony_ci	}
306562306a36Sopenharmony_ci
306662306a36Sopenharmony_ci	cycle = fec_enet_us_to_itr_clock(ndev, ec->rx_coalesce_usecs);
306762306a36Sopenharmony_ci	if (cycle > 0xFFFF) {
306862306a36Sopenharmony_ci		dev_err(dev, "Rx coalesced usec exceed hardware limitation\n");
306962306a36Sopenharmony_ci		return -EINVAL;
307062306a36Sopenharmony_ci	}
307162306a36Sopenharmony_ci
307262306a36Sopenharmony_ci	cycle = fec_enet_us_to_itr_clock(ndev, ec->tx_coalesce_usecs);
307362306a36Sopenharmony_ci	if (cycle > 0xFFFF) {
307462306a36Sopenharmony_ci		dev_err(dev, "Tx coalesced usec exceed hardware limitation\n");
307562306a36Sopenharmony_ci		return -EINVAL;
307662306a36Sopenharmony_ci	}
307762306a36Sopenharmony_ci
307862306a36Sopenharmony_ci	fep->rx_time_itr = ec->rx_coalesce_usecs;
307962306a36Sopenharmony_ci	fep->rx_pkts_itr = ec->rx_max_coalesced_frames;
308062306a36Sopenharmony_ci
308162306a36Sopenharmony_ci	fep->tx_time_itr = ec->tx_coalesce_usecs;
308262306a36Sopenharmony_ci	fep->tx_pkts_itr = ec->tx_max_coalesced_frames;
308362306a36Sopenharmony_ci
308462306a36Sopenharmony_ci	fec_enet_itr_coal_set(ndev);
308562306a36Sopenharmony_ci
308662306a36Sopenharmony_ci	return 0;
308762306a36Sopenharmony_ci}
308862306a36Sopenharmony_ci
308962306a36Sopenharmony_ci/* LPI Sleep Ts count base on tx clk (clk_ref).
309062306a36Sopenharmony_ci * The lpi sleep cnt value = X us / (cycle_ns).
309162306a36Sopenharmony_ci */
309262306a36Sopenharmony_cistatic int fec_enet_us_to_tx_cycle(struct net_device *ndev, int us)
309362306a36Sopenharmony_ci{
309462306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
309562306a36Sopenharmony_ci
309662306a36Sopenharmony_ci	return us * (fep->clk_ref_rate / 1000) / 1000;
309762306a36Sopenharmony_ci}
309862306a36Sopenharmony_ci
309962306a36Sopenharmony_cistatic int fec_enet_eee_mode_set(struct net_device *ndev, bool enable)
310062306a36Sopenharmony_ci{
310162306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
310262306a36Sopenharmony_ci	struct ethtool_eee *p = &fep->eee;
310362306a36Sopenharmony_ci	unsigned int sleep_cycle, wake_cycle;
310462306a36Sopenharmony_ci	int ret = 0;
310562306a36Sopenharmony_ci
310662306a36Sopenharmony_ci	if (enable) {
310762306a36Sopenharmony_ci		ret = phy_init_eee(ndev->phydev, false);
310862306a36Sopenharmony_ci		if (ret)
310962306a36Sopenharmony_ci			return ret;
311062306a36Sopenharmony_ci
311162306a36Sopenharmony_ci		sleep_cycle = fec_enet_us_to_tx_cycle(ndev, p->tx_lpi_timer);
311262306a36Sopenharmony_ci		wake_cycle = sleep_cycle;
311362306a36Sopenharmony_ci	} else {
311462306a36Sopenharmony_ci		sleep_cycle = 0;
311562306a36Sopenharmony_ci		wake_cycle = 0;
311662306a36Sopenharmony_ci	}
311762306a36Sopenharmony_ci
311862306a36Sopenharmony_ci	p->tx_lpi_enabled = enable;
311962306a36Sopenharmony_ci	p->eee_enabled = enable;
312062306a36Sopenharmony_ci	p->eee_active = enable;
312162306a36Sopenharmony_ci
312262306a36Sopenharmony_ci	writel(sleep_cycle, fep->hwp + FEC_LPI_SLEEP);
312362306a36Sopenharmony_ci	writel(wake_cycle, fep->hwp + FEC_LPI_WAKE);
312462306a36Sopenharmony_ci
312562306a36Sopenharmony_ci	return 0;
312662306a36Sopenharmony_ci}
312762306a36Sopenharmony_ci
312862306a36Sopenharmony_cistatic int
312962306a36Sopenharmony_cifec_enet_get_eee(struct net_device *ndev, struct ethtool_eee *edata)
313062306a36Sopenharmony_ci{
313162306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
313262306a36Sopenharmony_ci	struct ethtool_eee *p = &fep->eee;
313362306a36Sopenharmony_ci
313462306a36Sopenharmony_ci	if (!(fep->quirks & FEC_QUIRK_HAS_EEE))
313562306a36Sopenharmony_ci		return -EOPNOTSUPP;
313662306a36Sopenharmony_ci
313762306a36Sopenharmony_ci	if (!netif_running(ndev))
313862306a36Sopenharmony_ci		return -ENETDOWN;
313962306a36Sopenharmony_ci
314062306a36Sopenharmony_ci	edata->eee_enabled = p->eee_enabled;
314162306a36Sopenharmony_ci	edata->eee_active = p->eee_active;
314262306a36Sopenharmony_ci	edata->tx_lpi_timer = p->tx_lpi_timer;
314362306a36Sopenharmony_ci	edata->tx_lpi_enabled = p->tx_lpi_enabled;
314462306a36Sopenharmony_ci
314562306a36Sopenharmony_ci	return phy_ethtool_get_eee(ndev->phydev, edata);
314662306a36Sopenharmony_ci}
314762306a36Sopenharmony_ci
314862306a36Sopenharmony_cistatic int
314962306a36Sopenharmony_cifec_enet_set_eee(struct net_device *ndev, struct ethtool_eee *edata)
315062306a36Sopenharmony_ci{
315162306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
315262306a36Sopenharmony_ci	struct ethtool_eee *p = &fep->eee;
315362306a36Sopenharmony_ci	int ret = 0;
315462306a36Sopenharmony_ci
315562306a36Sopenharmony_ci	if (!(fep->quirks & FEC_QUIRK_HAS_EEE))
315662306a36Sopenharmony_ci		return -EOPNOTSUPP;
315762306a36Sopenharmony_ci
315862306a36Sopenharmony_ci	if (!netif_running(ndev))
315962306a36Sopenharmony_ci		return -ENETDOWN;
316062306a36Sopenharmony_ci
316162306a36Sopenharmony_ci	p->tx_lpi_timer = edata->tx_lpi_timer;
316262306a36Sopenharmony_ci
316362306a36Sopenharmony_ci	if (!edata->eee_enabled || !edata->tx_lpi_enabled ||
316462306a36Sopenharmony_ci	    !edata->tx_lpi_timer)
316562306a36Sopenharmony_ci		ret = fec_enet_eee_mode_set(ndev, false);
316662306a36Sopenharmony_ci	else
316762306a36Sopenharmony_ci		ret = fec_enet_eee_mode_set(ndev, true);
316862306a36Sopenharmony_ci
316962306a36Sopenharmony_ci	if (ret)
317062306a36Sopenharmony_ci		return ret;
317162306a36Sopenharmony_ci
317262306a36Sopenharmony_ci	return phy_ethtool_set_eee(ndev->phydev, edata);
317362306a36Sopenharmony_ci}
317462306a36Sopenharmony_ci
317562306a36Sopenharmony_cistatic void
317662306a36Sopenharmony_cifec_enet_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol)
317762306a36Sopenharmony_ci{
317862306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
317962306a36Sopenharmony_ci
318062306a36Sopenharmony_ci	if (fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET) {
318162306a36Sopenharmony_ci		wol->supported = WAKE_MAGIC;
318262306a36Sopenharmony_ci		wol->wolopts = fep->wol_flag & FEC_WOL_FLAG_ENABLE ? WAKE_MAGIC : 0;
318362306a36Sopenharmony_ci	} else {
318462306a36Sopenharmony_ci		wol->supported = wol->wolopts = 0;
318562306a36Sopenharmony_ci	}
318662306a36Sopenharmony_ci}
318762306a36Sopenharmony_ci
318862306a36Sopenharmony_cistatic int
318962306a36Sopenharmony_cifec_enet_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol)
319062306a36Sopenharmony_ci{
319162306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
319262306a36Sopenharmony_ci
319362306a36Sopenharmony_ci	if (!(fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET))
319462306a36Sopenharmony_ci		return -EINVAL;
319562306a36Sopenharmony_ci
319662306a36Sopenharmony_ci	if (wol->wolopts & ~WAKE_MAGIC)
319762306a36Sopenharmony_ci		return -EINVAL;
319862306a36Sopenharmony_ci
319962306a36Sopenharmony_ci	device_set_wakeup_enable(&ndev->dev, wol->wolopts & WAKE_MAGIC);
320062306a36Sopenharmony_ci	if (device_may_wakeup(&ndev->dev))
320162306a36Sopenharmony_ci		fep->wol_flag |= FEC_WOL_FLAG_ENABLE;
320262306a36Sopenharmony_ci	else
320362306a36Sopenharmony_ci		fep->wol_flag &= (~FEC_WOL_FLAG_ENABLE);
320462306a36Sopenharmony_ci
320562306a36Sopenharmony_ci	return 0;
320662306a36Sopenharmony_ci}
320762306a36Sopenharmony_ci
320862306a36Sopenharmony_cistatic const struct ethtool_ops fec_enet_ethtool_ops = {
320962306a36Sopenharmony_ci	.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
321062306a36Sopenharmony_ci				     ETHTOOL_COALESCE_MAX_FRAMES,
321162306a36Sopenharmony_ci	.get_drvinfo		= fec_enet_get_drvinfo,
321262306a36Sopenharmony_ci	.get_regs_len		= fec_enet_get_regs_len,
321362306a36Sopenharmony_ci	.get_regs		= fec_enet_get_regs,
321462306a36Sopenharmony_ci	.nway_reset		= phy_ethtool_nway_reset,
321562306a36Sopenharmony_ci	.get_link		= ethtool_op_get_link,
321662306a36Sopenharmony_ci	.get_coalesce		= fec_enet_get_coalesce,
321762306a36Sopenharmony_ci	.set_coalesce		= fec_enet_set_coalesce,
321862306a36Sopenharmony_ci#ifndef CONFIG_M5272
321962306a36Sopenharmony_ci	.get_pauseparam		= fec_enet_get_pauseparam,
322062306a36Sopenharmony_ci	.set_pauseparam		= fec_enet_set_pauseparam,
322162306a36Sopenharmony_ci	.get_strings		= fec_enet_get_strings,
322262306a36Sopenharmony_ci	.get_ethtool_stats	= fec_enet_get_ethtool_stats,
322362306a36Sopenharmony_ci	.get_sset_count		= fec_enet_get_sset_count,
322462306a36Sopenharmony_ci#endif
322562306a36Sopenharmony_ci	.get_ts_info		= fec_enet_get_ts_info,
322662306a36Sopenharmony_ci	.get_wol		= fec_enet_get_wol,
322762306a36Sopenharmony_ci	.set_wol		= fec_enet_set_wol,
322862306a36Sopenharmony_ci	.get_eee		= fec_enet_get_eee,
322962306a36Sopenharmony_ci	.set_eee		= fec_enet_set_eee,
323062306a36Sopenharmony_ci	.get_link_ksettings	= phy_ethtool_get_link_ksettings,
323162306a36Sopenharmony_ci	.set_link_ksettings	= phy_ethtool_set_link_ksettings,
323262306a36Sopenharmony_ci	.self_test		= net_selftest,
323362306a36Sopenharmony_ci};
323462306a36Sopenharmony_ci
323562306a36Sopenharmony_cistatic void fec_enet_free_buffers(struct net_device *ndev)
323662306a36Sopenharmony_ci{
323762306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
323862306a36Sopenharmony_ci	unsigned int i;
323962306a36Sopenharmony_ci	struct fec_enet_priv_tx_q *txq;
324062306a36Sopenharmony_ci	struct fec_enet_priv_rx_q *rxq;
324162306a36Sopenharmony_ci	unsigned int q;
324262306a36Sopenharmony_ci
324362306a36Sopenharmony_ci	for (q = 0; q < fep->num_rx_queues; q++) {
324462306a36Sopenharmony_ci		rxq = fep->rx_queue[q];
324562306a36Sopenharmony_ci		for (i = 0; i < rxq->bd.ring_size; i++)
324662306a36Sopenharmony_ci			page_pool_put_full_page(rxq->page_pool, rxq->rx_skb_info[i].page, false);
324762306a36Sopenharmony_ci
324862306a36Sopenharmony_ci		for (i = 0; i < XDP_STATS_TOTAL; i++)
324962306a36Sopenharmony_ci			rxq->stats[i] = 0;
325062306a36Sopenharmony_ci
325162306a36Sopenharmony_ci		if (xdp_rxq_info_is_reg(&rxq->xdp_rxq))
325262306a36Sopenharmony_ci			xdp_rxq_info_unreg(&rxq->xdp_rxq);
325362306a36Sopenharmony_ci		page_pool_destroy(rxq->page_pool);
325462306a36Sopenharmony_ci		rxq->page_pool = NULL;
325562306a36Sopenharmony_ci	}
325662306a36Sopenharmony_ci
325762306a36Sopenharmony_ci	for (q = 0; q < fep->num_tx_queues; q++) {
325862306a36Sopenharmony_ci		txq = fep->tx_queue[q];
325962306a36Sopenharmony_ci		for (i = 0; i < txq->bd.ring_size; i++) {
326062306a36Sopenharmony_ci			kfree(txq->tx_bounce[i]);
326162306a36Sopenharmony_ci			txq->tx_bounce[i] = NULL;
326262306a36Sopenharmony_ci
326362306a36Sopenharmony_ci			if (!txq->tx_buf[i].buf_p) {
326462306a36Sopenharmony_ci				txq->tx_buf[i].type = FEC_TXBUF_T_SKB;
326562306a36Sopenharmony_ci				continue;
326662306a36Sopenharmony_ci			}
326762306a36Sopenharmony_ci
326862306a36Sopenharmony_ci			if (txq->tx_buf[i].type == FEC_TXBUF_T_SKB) {
326962306a36Sopenharmony_ci				dev_kfree_skb(txq->tx_buf[i].buf_p);
327062306a36Sopenharmony_ci			} else if (txq->tx_buf[i].type == FEC_TXBUF_T_XDP_NDO) {
327162306a36Sopenharmony_ci				xdp_return_frame(txq->tx_buf[i].buf_p);
327262306a36Sopenharmony_ci			} else {
327362306a36Sopenharmony_ci				struct page *page = txq->tx_buf[i].buf_p;
327462306a36Sopenharmony_ci
327562306a36Sopenharmony_ci				page_pool_put_page(page->pp, page, 0, false);
327662306a36Sopenharmony_ci			}
327762306a36Sopenharmony_ci
327862306a36Sopenharmony_ci			txq->tx_buf[i].buf_p = NULL;
327962306a36Sopenharmony_ci			txq->tx_buf[i].type = FEC_TXBUF_T_SKB;
328062306a36Sopenharmony_ci		}
328162306a36Sopenharmony_ci	}
328262306a36Sopenharmony_ci}
328362306a36Sopenharmony_ci
328462306a36Sopenharmony_cistatic void fec_enet_free_queue(struct net_device *ndev)
328562306a36Sopenharmony_ci{
328662306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
328762306a36Sopenharmony_ci	int i;
328862306a36Sopenharmony_ci	struct fec_enet_priv_tx_q *txq;
328962306a36Sopenharmony_ci
329062306a36Sopenharmony_ci	for (i = 0; i < fep->num_tx_queues; i++)
329162306a36Sopenharmony_ci		if (fep->tx_queue[i] && fep->tx_queue[i]->tso_hdrs) {
329262306a36Sopenharmony_ci			txq = fep->tx_queue[i];
329362306a36Sopenharmony_ci			dma_free_coherent(&fep->pdev->dev,
329462306a36Sopenharmony_ci					  txq->bd.ring_size * TSO_HEADER_SIZE,
329562306a36Sopenharmony_ci					  txq->tso_hdrs,
329662306a36Sopenharmony_ci					  txq->tso_hdrs_dma);
329762306a36Sopenharmony_ci		}
329862306a36Sopenharmony_ci
329962306a36Sopenharmony_ci	for (i = 0; i < fep->num_rx_queues; i++)
330062306a36Sopenharmony_ci		kfree(fep->rx_queue[i]);
330162306a36Sopenharmony_ci	for (i = 0; i < fep->num_tx_queues; i++)
330262306a36Sopenharmony_ci		kfree(fep->tx_queue[i]);
330362306a36Sopenharmony_ci}
330462306a36Sopenharmony_ci
330562306a36Sopenharmony_cistatic int fec_enet_alloc_queue(struct net_device *ndev)
330662306a36Sopenharmony_ci{
330762306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
330862306a36Sopenharmony_ci	int i;
330962306a36Sopenharmony_ci	int ret = 0;
331062306a36Sopenharmony_ci	struct fec_enet_priv_tx_q *txq;
331162306a36Sopenharmony_ci
331262306a36Sopenharmony_ci	for (i = 0; i < fep->num_tx_queues; i++) {
331362306a36Sopenharmony_ci		txq = kzalloc(sizeof(*txq), GFP_KERNEL);
331462306a36Sopenharmony_ci		if (!txq) {
331562306a36Sopenharmony_ci			ret = -ENOMEM;
331662306a36Sopenharmony_ci			goto alloc_failed;
331762306a36Sopenharmony_ci		}
331862306a36Sopenharmony_ci
331962306a36Sopenharmony_ci		fep->tx_queue[i] = txq;
332062306a36Sopenharmony_ci		txq->bd.ring_size = TX_RING_SIZE;
332162306a36Sopenharmony_ci		fep->total_tx_ring_size += fep->tx_queue[i]->bd.ring_size;
332262306a36Sopenharmony_ci
332362306a36Sopenharmony_ci		txq->tx_stop_threshold = FEC_MAX_SKB_DESCS;
332462306a36Sopenharmony_ci		txq->tx_wake_threshold = FEC_MAX_SKB_DESCS + 2 * MAX_SKB_FRAGS;
332562306a36Sopenharmony_ci
332662306a36Sopenharmony_ci		txq->tso_hdrs = dma_alloc_coherent(&fep->pdev->dev,
332762306a36Sopenharmony_ci					txq->bd.ring_size * TSO_HEADER_SIZE,
332862306a36Sopenharmony_ci					&txq->tso_hdrs_dma,
332962306a36Sopenharmony_ci					GFP_KERNEL);
333062306a36Sopenharmony_ci		if (!txq->tso_hdrs) {
333162306a36Sopenharmony_ci			ret = -ENOMEM;
333262306a36Sopenharmony_ci			goto alloc_failed;
333362306a36Sopenharmony_ci		}
333462306a36Sopenharmony_ci	}
333562306a36Sopenharmony_ci
333662306a36Sopenharmony_ci	for (i = 0; i < fep->num_rx_queues; i++) {
333762306a36Sopenharmony_ci		fep->rx_queue[i] = kzalloc(sizeof(*fep->rx_queue[i]),
333862306a36Sopenharmony_ci					   GFP_KERNEL);
333962306a36Sopenharmony_ci		if (!fep->rx_queue[i]) {
334062306a36Sopenharmony_ci			ret = -ENOMEM;
334162306a36Sopenharmony_ci			goto alloc_failed;
334262306a36Sopenharmony_ci		}
334362306a36Sopenharmony_ci
334462306a36Sopenharmony_ci		fep->rx_queue[i]->bd.ring_size = RX_RING_SIZE;
334562306a36Sopenharmony_ci		fep->total_rx_ring_size += fep->rx_queue[i]->bd.ring_size;
334662306a36Sopenharmony_ci	}
334762306a36Sopenharmony_ci	return ret;
334862306a36Sopenharmony_ci
334962306a36Sopenharmony_cialloc_failed:
335062306a36Sopenharmony_ci	fec_enet_free_queue(ndev);
335162306a36Sopenharmony_ci	return ret;
335262306a36Sopenharmony_ci}
335362306a36Sopenharmony_ci
335462306a36Sopenharmony_cistatic int
335562306a36Sopenharmony_cifec_enet_alloc_rxq_buffers(struct net_device *ndev, unsigned int queue)
335662306a36Sopenharmony_ci{
335762306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
335862306a36Sopenharmony_ci	struct fec_enet_priv_rx_q *rxq;
335962306a36Sopenharmony_ci	dma_addr_t phys_addr;
336062306a36Sopenharmony_ci	struct bufdesc	*bdp;
336162306a36Sopenharmony_ci	struct page *page;
336262306a36Sopenharmony_ci	int i, err;
336362306a36Sopenharmony_ci
336462306a36Sopenharmony_ci	rxq = fep->rx_queue[queue];
336562306a36Sopenharmony_ci	bdp = rxq->bd.base;
336662306a36Sopenharmony_ci
336762306a36Sopenharmony_ci	err = fec_enet_create_page_pool(fep, rxq, rxq->bd.ring_size);
336862306a36Sopenharmony_ci	if (err < 0) {
336962306a36Sopenharmony_ci		netdev_err(ndev, "%s failed queue %d (%d)\n", __func__, queue, err);
337062306a36Sopenharmony_ci		return err;
337162306a36Sopenharmony_ci	}
337262306a36Sopenharmony_ci
337362306a36Sopenharmony_ci	for (i = 0; i < rxq->bd.ring_size; i++) {
337462306a36Sopenharmony_ci		page = page_pool_dev_alloc_pages(rxq->page_pool);
337562306a36Sopenharmony_ci		if (!page)
337662306a36Sopenharmony_ci			goto err_alloc;
337762306a36Sopenharmony_ci
337862306a36Sopenharmony_ci		phys_addr = page_pool_get_dma_addr(page) + FEC_ENET_XDP_HEADROOM;
337962306a36Sopenharmony_ci		bdp->cbd_bufaddr = cpu_to_fec32(phys_addr);
338062306a36Sopenharmony_ci
338162306a36Sopenharmony_ci		rxq->rx_skb_info[i].page = page;
338262306a36Sopenharmony_ci		rxq->rx_skb_info[i].offset = FEC_ENET_XDP_HEADROOM;
338362306a36Sopenharmony_ci		bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY);
338462306a36Sopenharmony_ci
338562306a36Sopenharmony_ci		if (fep->bufdesc_ex) {
338662306a36Sopenharmony_ci			struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
338762306a36Sopenharmony_ci			ebdp->cbd_esc = cpu_to_fec32(BD_ENET_RX_INT);
338862306a36Sopenharmony_ci		}
338962306a36Sopenharmony_ci
339062306a36Sopenharmony_ci		bdp = fec_enet_get_nextdesc(bdp, &rxq->bd);
339162306a36Sopenharmony_ci	}
339262306a36Sopenharmony_ci
339362306a36Sopenharmony_ci	/* Set the last buffer to wrap. */
339462306a36Sopenharmony_ci	bdp = fec_enet_get_prevdesc(bdp, &rxq->bd);
339562306a36Sopenharmony_ci	bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP);
339662306a36Sopenharmony_ci	return 0;
339762306a36Sopenharmony_ci
339862306a36Sopenharmony_ci err_alloc:
339962306a36Sopenharmony_ci	fec_enet_free_buffers(ndev);
340062306a36Sopenharmony_ci	return -ENOMEM;
340162306a36Sopenharmony_ci}
340262306a36Sopenharmony_ci
340362306a36Sopenharmony_cistatic int
340462306a36Sopenharmony_cifec_enet_alloc_txq_buffers(struct net_device *ndev, unsigned int queue)
340562306a36Sopenharmony_ci{
340662306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
340762306a36Sopenharmony_ci	unsigned int i;
340862306a36Sopenharmony_ci	struct bufdesc  *bdp;
340962306a36Sopenharmony_ci	struct fec_enet_priv_tx_q *txq;
341062306a36Sopenharmony_ci
341162306a36Sopenharmony_ci	txq = fep->tx_queue[queue];
341262306a36Sopenharmony_ci	bdp = txq->bd.base;
341362306a36Sopenharmony_ci	for (i = 0; i < txq->bd.ring_size; i++) {
341462306a36Sopenharmony_ci		txq->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL);
341562306a36Sopenharmony_ci		if (!txq->tx_bounce[i])
341662306a36Sopenharmony_ci			goto err_alloc;
341762306a36Sopenharmony_ci
341862306a36Sopenharmony_ci		bdp->cbd_sc = cpu_to_fec16(0);
341962306a36Sopenharmony_ci		bdp->cbd_bufaddr = cpu_to_fec32(0);
342062306a36Sopenharmony_ci
342162306a36Sopenharmony_ci		if (fep->bufdesc_ex) {
342262306a36Sopenharmony_ci			struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
342362306a36Sopenharmony_ci			ebdp->cbd_esc = cpu_to_fec32(BD_ENET_TX_INT);
342462306a36Sopenharmony_ci		}
342562306a36Sopenharmony_ci
342662306a36Sopenharmony_ci		bdp = fec_enet_get_nextdesc(bdp, &txq->bd);
342762306a36Sopenharmony_ci	}
342862306a36Sopenharmony_ci
342962306a36Sopenharmony_ci	/* Set the last buffer to wrap. */
343062306a36Sopenharmony_ci	bdp = fec_enet_get_prevdesc(bdp, &txq->bd);
343162306a36Sopenharmony_ci	bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP);
343262306a36Sopenharmony_ci
343362306a36Sopenharmony_ci	return 0;
343462306a36Sopenharmony_ci
343562306a36Sopenharmony_ci err_alloc:
343662306a36Sopenharmony_ci	fec_enet_free_buffers(ndev);
343762306a36Sopenharmony_ci	return -ENOMEM;
343862306a36Sopenharmony_ci}
343962306a36Sopenharmony_ci
344062306a36Sopenharmony_cistatic int fec_enet_alloc_buffers(struct net_device *ndev)
344162306a36Sopenharmony_ci{
344262306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
344362306a36Sopenharmony_ci	unsigned int i;
344462306a36Sopenharmony_ci
344562306a36Sopenharmony_ci	for (i = 0; i < fep->num_rx_queues; i++)
344662306a36Sopenharmony_ci		if (fec_enet_alloc_rxq_buffers(ndev, i))
344762306a36Sopenharmony_ci			return -ENOMEM;
344862306a36Sopenharmony_ci
344962306a36Sopenharmony_ci	for (i = 0; i < fep->num_tx_queues; i++)
345062306a36Sopenharmony_ci		if (fec_enet_alloc_txq_buffers(ndev, i))
345162306a36Sopenharmony_ci			return -ENOMEM;
345262306a36Sopenharmony_ci	return 0;
345362306a36Sopenharmony_ci}
345462306a36Sopenharmony_ci
345562306a36Sopenharmony_cistatic int
345662306a36Sopenharmony_cifec_enet_open(struct net_device *ndev)
345762306a36Sopenharmony_ci{
345862306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
345962306a36Sopenharmony_ci	int ret;
346062306a36Sopenharmony_ci	bool reset_again;
346162306a36Sopenharmony_ci
346262306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(&fep->pdev->dev);
346362306a36Sopenharmony_ci	if (ret < 0)
346462306a36Sopenharmony_ci		return ret;
346562306a36Sopenharmony_ci
346662306a36Sopenharmony_ci	pinctrl_pm_select_default_state(&fep->pdev->dev);
346762306a36Sopenharmony_ci	ret = fec_enet_clk_enable(ndev, true);
346862306a36Sopenharmony_ci	if (ret)
346962306a36Sopenharmony_ci		goto clk_enable;
347062306a36Sopenharmony_ci
347162306a36Sopenharmony_ci	/* During the first fec_enet_open call the PHY isn't probed at this
347262306a36Sopenharmony_ci	 * point. Therefore the phy_reset_after_clk_enable() call within
347362306a36Sopenharmony_ci	 * fec_enet_clk_enable() fails. As we need this reset in order to be
347462306a36Sopenharmony_ci	 * sure the PHY is working correctly we check if we need to reset again
347562306a36Sopenharmony_ci	 * later when the PHY is probed
347662306a36Sopenharmony_ci	 */
347762306a36Sopenharmony_ci	if (ndev->phydev && ndev->phydev->drv)
347862306a36Sopenharmony_ci		reset_again = false;
347962306a36Sopenharmony_ci	else
348062306a36Sopenharmony_ci		reset_again = true;
348162306a36Sopenharmony_ci
348262306a36Sopenharmony_ci	/* I should reset the ring buffers here, but I don't yet know
348362306a36Sopenharmony_ci	 * a simple way to do that.
348462306a36Sopenharmony_ci	 */
348562306a36Sopenharmony_ci
348662306a36Sopenharmony_ci	ret = fec_enet_alloc_buffers(ndev);
348762306a36Sopenharmony_ci	if (ret)
348862306a36Sopenharmony_ci		goto err_enet_alloc;
348962306a36Sopenharmony_ci
349062306a36Sopenharmony_ci	/* Init MAC prior to mii bus probe */
349162306a36Sopenharmony_ci	fec_restart(ndev);
349262306a36Sopenharmony_ci
349362306a36Sopenharmony_ci	/* Call phy_reset_after_clk_enable() again if it failed during
349462306a36Sopenharmony_ci	 * phy_reset_after_clk_enable() before because the PHY wasn't probed.
349562306a36Sopenharmony_ci	 */
349662306a36Sopenharmony_ci	if (reset_again)
349762306a36Sopenharmony_ci		fec_enet_phy_reset_after_clk_enable(ndev);
349862306a36Sopenharmony_ci
349962306a36Sopenharmony_ci	/* Probe and connect to PHY when open the interface */
350062306a36Sopenharmony_ci	ret = fec_enet_mii_probe(ndev);
350162306a36Sopenharmony_ci	if (ret)
350262306a36Sopenharmony_ci		goto err_enet_mii_probe;
350362306a36Sopenharmony_ci
350462306a36Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_ERR006687)
350562306a36Sopenharmony_ci		imx6q_cpuidle_fec_irqs_used();
350662306a36Sopenharmony_ci
350762306a36Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_HAS_PMQOS)
350862306a36Sopenharmony_ci		cpu_latency_qos_add_request(&fep->pm_qos_req, 0);
350962306a36Sopenharmony_ci
351062306a36Sopenharmony_ci	napi_enable(&fep->napi);
351162306a36Sopenharmony_ci	phy_start(ndev->phydev);
351262306a36Sopenharmony_ci	netif_tx_start_all_queues(ndev);
351362306a36Sopenharmony_ci
351462306a36Sopenharmony_ci	device_set_wakeup_enable(&ndev->dev, fep->wol_flag &
351562306a36Sopenharmony_ci				 FEC_WOL_FLAG_ENABLE);
351662306a36Sopenharmony_ci
351762306a36Sopenharmony_ci	return 0;
351862306a36Sopenharmony_ci
351962306a36Sopenharmony_cierr_enet_mii_probe:
352062306a36Sopenharmony_ci	fec_enet_free_buffers(ndev);
352162306a36Sopenharmony_cierr_enet_alloc:
352262306a36Sopenharmony_ci	fec_enet_clk_enable(ndev, false);
352362306a36Sopenharmony_ciclk_enable:
352462306a36Sopenharmony_ci	pm_runtime_mark_last_busy(&fep->pdev->dev);
352562306a36Sopenharmony_ci	pm_runtime_put_autosuspend(&fep->pdev->dev);
352662306a36Sopenharmony_ci	pinctrl_pm_select_sleep_state(&fep->pdev->dev);
352762306a36Sopenharmony_ci	return ret;
352862306a36Sopenharmony_ci}
352962306a36Sopenharmony_ci
353062306a36Sopenharmony_cistatic int
353162306a36Sopenharmony_cifec_enet_close(struct net_device *ndev)
353262306a36Sopenharmony_ci{
353362306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
353462306a36Sopenharmony_ci
353562306a36Sopenharmony_ci	phy_stop(ndev->phydev);
353662306a36Sopenharmony_ci
353762306a36Sopenharmony_ci	if (netif_device_present(ndev)) {
353862306a36Sopenharmony_ci		napi_disable(&fep->napi);
353962306a36Sopenharmony_ci		netif_tx_disable(ndev);
354062306a36Sopenharmony_ci		fec_stop(ndev);
354162306a36Sopenharmony_ci	}
354262306a36Sopenharmony_ci
354362306a36Sopenharmony_ci	phy_disconnect(ndev->phydev);
354462306a36Sopenharmony_ci
354562306a36Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_ERR006687)
354662306a36Sopenharmony_ci		imx6q_cpuidle_fec_irqs_unused();
354762306a36Sopenharmony_ci
354862306a36Sopenharmony_ci	fec_enet_update_ethtool_stats(ndev);
354962306a36Sopenharmony_ci
355062306a36Sopenharmony_ci	fec_enet_clk_enable(ndev, false);
355162306a36Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_HAS_PMQOS)
355262306a36Sopenharmony_ci		cpu_latency_qos_remove_request(&fep->pm_qos_req);
355362306a36Sopenharmony_ci
355462306a36Sopenharmony_ci	pinctrl_pm_select_sleep_state(&fep->pdev->dev);
355562306a36Sopenharmony_ci	pm_runtime_mark_last_busy(&fep->pdev->dev);
355662306a36Sopenharmony_ci	pm_runtime_put_autosuspend(&fep->pdev->dev);
355762306a36Sopenharmony_ci
355862306a36Sopenharmony_ci	fec_enet_free_buffers(ndev);
355962306a36Sopenharmony_ci
356062306a36Sopenharmony_ci	return 0;
356162306a36Sopenharmony_ci}
356262306a36Sopenharmony_ci
356362306a36Sopenharmony_ci/* Set or clear the multicast filter for this adaptor.
356462306a36Sopenharmony_ci * Skeleton taken from sunlance driver.
356562306a36Sopenharmony_ci * The CPM Ethernet implementation allows Multicast as well as individual
356662306a36Sopenharmony_ci * MAC address filtering.  Some of the drivers check to make sure it is
356762306a36Sopenharmony_ci * a group multicast address, and discard those that are not.  I guess I
356862306a36Sopenharmony_ci * will do the same for now, but just remove the test if you want
356962306a36Sopenharmony_ci * individual filtering as well (do the upper net layers want or support
357062306a36Sopenharmony_ci * this kind of feature?).
357162306a36Sopenharmony_ci */
357262306a36Sopenharmony_ci
357362306a36Sopenharmony_ci#define FEC_HASH_BITS	6		/* #bits in hash */
357462306a36Sopenharmony_ci
357562306a36Sopenharmony_cistatic void set_multicast_list(struct net_device *ndev)
357662306a36Sopenharmony_ci{
357762306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
357862306a36Sopenharmony_ci	struct netdev_hw_addr *ha;
357962306a36Sopenharmony_ci	unsigned int crc, tmp;
358062306a36Sopenharmony_ci	unsigned char hash;
358162306a36Sopenharmony_ci	unsigned int hash_high = 0, hash_low = 0;
358262306a36Sopenharmony_ci
358362306a36Sopenharmony_ci	if (ndev->flags & IFF_PROMISC) {
358462306a36Sopenharmony_ci		tmp = readl(fep->hwp + FEC_R_CNTRL);
358562306a36Sopenharmony_ci		tmp |= 0x8;
358662306a36Sopenharmony_ci		writel(tmp, fep->hwp + FEC_R_CNTRL);
358762306a36Sopenharmony_ci		return;
358862306a36Sopenharmony_ci	}
358962306a36Sopenharmony_ci
359062306a36Sopenharmony_ci	tmp = readl(fep->hwp + FEC_R_CNTRL);
359162306a36Sopenharmony_ci	tmp &= ~0x8;
359262306a36Sopenharmony_ci	writel(tmp, fep->hwp + FEC_R_CNTRL);
359362306a36Sopenharmony_ci
359462306a36Sopenharmony_ci	if (ndev->flags & IFF_ALLMULTI) {
359562306a36Sopenharmony_ci		/* Catch all multicast addresses, so set the
359662306a36Sopenharmony_ci		 * filter to all 1's
359762306a36Sopenharmony_ci		 */
359862306a36Sopenharmony_ci		writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
359962306a36Sopenharmony_ci		writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
360062306a36Sopenharmony_ci
360162306a36Sopenharmony_ci		return;
360262306a36Sopenharmony_ci	}
360362306a36Sopenharmony_ci
360462306a36Sopenharmony_ci	/* Add the addresses in hash register */
360562306a36Sopenharmony_ci	netdev_for_each_mc_addr(ha, ndev) {
360662306a36Sopenharmony_ci		/* calculate crc32 value of mac address */
360762306a36Sopenharmony_ci		crc = ether_crc_le(ndev->addr_len, ha->addr);
360862306a36Sopenharmony_ci
360962306a36Sopenharmony_ci		/* only upper 6 bits (FEC_HASH_BITS) are used
361062306a36Sopenharmony_ci		 * which point to specific bit in the hash registers
361162306a36Sopenharmony_ci		 */
361262306a36Sopenharmony_ci		hash = (crc >> (32 - FEC_HASH_BITS)) & 0x3f;
361362306a36Sopenharmony_ci
361462306a36Sopenharmony_ci		if (hash > 31)
361562306a36Sopenharmony_ci			hash_high |= 1 << (hash - 32);
361662306a36Sopenharmony_ci		else
361762306a36Sopenharmony_ci			hash_low |= 1 << hash;
361862306a36Sopenharmony_ci	}
361962306a36Sopenharmony_ci
362062306a36Sopenharmony_ci	writel(hash_high, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
362162306a36Sopenharmony_ci	writel(hash_low, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
362262306a36Sopenharmony_ci}
362362306a36Sopenharmony_ci
362462306a36Sopenharmony_ci/* Set a MAC change in hardware. */
362562306a36Sopenharmony_cistatic int
362662306a36Sopenharmony_cifec_set_mac_address(struct net_device *ndev, void *p)
362762306a36Sopenharmony_ci{
362862306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
362962306a36Sopenharmony_ci	struct sockaddr *addr = p;
363062306a36Sopenharmony_ci
363162306a36Sopenharmony_ci	if (addr) {
363262306a36Sopenharmony_ci		if (!is_valid_ether_addr(addr->sa_data))
363362306a36Sopenharmony_ci			return -EADDRNOTAVAIL;
363462306a36Sopenharmony_ci		eth_hw_addr_set(ndev, addr->sa_data);
363562306a36Sopenharmony_ci	}
363662306a36Sopenharmony_ci
363762306a36Sopenharmony_ci	/* Add netif status check here to avoid system hang in below case:
363862306a36Sopenharmony_ci	 * ifconfig ethx down; ifconfig ethx hw ether xx:xx:xx:xx:xx:xx;
363962306a36Sopenharmony_ci	 * After ethx down, fec all clocks are gated off and then register
364062306a36Sopenharmony_ci	 * access causes system hang.
364162306a36Sopenharmony_ci	 */
364262306a36Sopenharmony_ci	if (!netif_running(ndev))
364362306a36Sopenharmony_ci		return 0;
364462306a36Sopenharmony_ci
364562306a36Sopenharmony_ci	writel(ndev->dev_addr[3] | (ndev->dev_addr[2] << 8) |
364662306a36Sopenharmony_ci		(ndev->dev_addr[1] << 16) | (ndev->dev_addr[0] << 24),
364762306a36Sopenharmony_ci		fep->hwp + FEC_ADDR_LOW);
364862306a36Sopenharmony_ci	writel((ndev->dev_addr[5] << 16) | (ndev->dev_addr[4] << 24),
364962306a36Sopenharmony_ci		fep->hwp + FEC_ADDR_HIGH);
365062306a36Sopenharmony_ci	return 0;
365162306a36Sopenharmony_ci}
365262306a36Sopenharmony_ci
365362306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
365462306a36Sopenharmony_ci/**
365562306a36Sopenharmony_ci * fec_poll_controller - FEC Poll controller function
365662306a36Sopenharmony_ci * @dev: The FEC network adapter
365762306a36Sopenharmony_ci *
365862306a36Sopenharmony_ci * Polled functionality used by netconsole and others in non interrupt mode
365962306a36Sopenharmony_ci *
366062306a36Sopenharmony_ci */
366162306a36Sopenharmony_cistatic void fec_poll_controller(struct net_device *dev)
366262306a36Sopenharmony_ci{
366362306a36Sopenharmony_ci	int i;
366462306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(dev);
366562306a36Sopenharmony_ci
366662306a36Sopenharmony_ci	for (i = 0; i < FEC_IRQ_NUM; i++) {
366762306a36Sopenharmony_ci		if (fep->irq[i] > 0) {
366862306a36Sopenharmony_ci			disable_irq(fep->irq[i]);
366962306a36Sopenharmony_ci			fec_enet_interrupt(fep->irq[i], dev);
367062306a36Sopenharmony_ci			enable_irq(fep->irq[i]);
367162306a36Sopenharmony_ci		}
367262306a36Sopenharmony_ci	}
367362306a36Sopenharmony_ci}
367462306a36Sopenharmony_ci#endif
367562306a36Sopenharmony_ci
367662306a36Sopenharmony_cistatic inline void fec_enet_set_netdev_features(struct net_device *netdev,
367762306a36Sopenharmony_ci	netdev_features_t features)
367862306a36Sopenharmony_ci{
367962306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(netdev);
368062306a36Sopenharmony_ci	netdev_features_t changed = features ^ netdev->features;
368162306a36Sopenharmony_ci
368262306a36Sopenharmony_ci	netdev->features = features;
368362306a36Sopenharmony_ci
368462306a36Sopenharmony_ci	/* Receive checksum has been changed */
368562306a36Sopenharmony_ci	if (changed & NETIF_F_RXCSUM) {
368662306a36Sopenharmony_ci		if (features & NETIF_F_RXCSUM)
368762306a36Sopenharmony_ci			fep->csum_flags |= FLAG_RX_CSUM_ENABLED;
368862306a36Sopenharmony_ci		else
368962306a36Sopenharmony_ci			fep->csum_flags &= ~FLAG_RX_CSUM_ENABLED;
369062306a36Sopenharmony_ci	}
369162306a36Sopenharmony_ci}
369262306a36Sopenharmony_ci
369362306a36Sopenharmony_cistatic int fec_set_features(struct net_device *netdev,
369462306a36Sopenharmony_ci	netdev_features_t features)
369562306a36Sopenharmony_ci{
369662306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(netdev);
369762306a36Sopenharmony_ci	netdev_features_t changed = features ^ netdev->features;
369862306a36Sopenharmony_ci
369962306a36Sopenharmony_ci	if (netif_running(netdev) && changed & NETIF_F_RXCSUM) {
370062306a36Sopenharmony_ci		napi_disable(&fep->napi);
370162306a36Sopenharmony_ci		netif_tx_lock_bh(netdev);
370262306a36Sopenharmony_ci		fec_stop(netdev);
370362306a36Sopenharmony_ci		fec_enet_set_netdev_features(netdev, features);
370462306a36Sopenharmony_ci		fec_restart(netdev);
370562306a36Sopenharmony_ci		netif_tx_wake_all_queues(netdev);
370662306a36Sopenharmony_ci		netif_tx_unlock_bh(netdev);
370762306a36Sopenharmony_ci		napi_enable(&fep->napi);
370862306a36Sopenharmony_ci	} else {
370962306a36Sopenharmony_ci		fec_enet_set_netdev_features(netdev, features);
371062306a36Sopenharmony_ci	}
371162306a36Sopenharmony_ci
371262306a36Sopenharmony_ci	return 0;
371362306a36Sopenharmony_ci}
371462306a36Sopenharmony_ci
371562306a36Sopenharmony_cistatic u16 fec_enet_select_queue(struct net_device *ndev, struct sk_buff *skb,
371662306a36Sopenharmony_ci				 struct net_device *sb_dev)
371762306a36Sopenharmony_ci{
371862306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
371962306a36Sopenharmony_ci	u16 vlan_tag = 0;
372062306a36Sopenharmony_ci
372162306a36Sopenharmony_ci	if (!(fep->quirks & FEC_QUIRK_HAS_AVB))
372262306a36Sopenharmony_ci		return netdev_pick_tx(ndev, skb, NULL);
372362306a36Sopenharmony_ci
372462306a36Sopenharmony_ci	/* VLAN is present in the payload.*/
372562306a36Sopenharmony_ci	if (eth_type_vlan(skb->protocol)) {
372662306a36Sopenharmony_ci		struct vlan_ethhdr *vhdr = skb_vlan_eth_hdr(skb);
372762306a36Sopenharmony_ci
372862306a36Sopenharmony_ci		vlan_tag = ntohs(vhdr->h_vlan_TCI);
372962306a36Sopenharmony_ci	/*  VLAN is present in the skb but not yet pushed in the payload.*/
373062306a36Sopenharmony_ci	} else if (skb_vlan_tag_present(skb)) {
373162306a36Sopenharmony_ci		vlan_tag = skb->vlan_tci;
373262306a36Sopenharmony_ci	} else {
373362306a36Sopenharmony_ci		return vlan_tag;
373462306a36Sopenharmony_ci	}
373562306a36Sopenharmony_ci
373662306a36Sopenharmony_ci	return fec_enet_vlan_pri_to_queue[vlan_tag >> 13];
373762306a36Sopenharmony_ci}
373862306a36Sopenharmony_ci
373962306a36Sopenharmony_cistatic int fec_enet_bpf(struct net_device *dev, struct netdev_bpf *bpf)
374062306a36Sopenharmony_ci{
374162306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(dev);
374262306a36Sopenharmony_ci	bool is_run = netif_running(dev);
374362306a36Sopenharmony_ci	struct bpf_prog *old_prog;
374462306a36Sopenharmony_ci
374562306a36Sopenharmony_ci	switch (bpf->command) {
374662306a36Sopenharmony_ci	case XDP_SETUP_PROG:
374762306a36Sopenharmony_ci		/* No need to support the SoCs that require to
374862306a36Sopenharmony_ci		 * do the frame swap because the performance wouldn't be
374962306a36Sopenharmony_ci		 * better than the skb mode.
375062306a36Sopenharmony_ci		 */
375162306a36Sopenharmony_ci		if (fep->quirks & FEC_QUIRK_SWAP_FRAME)
375262306a36Sopenharmony_ci			return -EOPNOTSUPP;
375362306a36Sopenharmony_ci
375462306a36Sopenharmony_ci		if (!bpf->prog)
375562306a36Sopenharmony_ci			xdp_features_clear_redirect_target(dev);
375662306a36Sopenharmony_ci
375762306a36Sopenharmony_ci		if (is_run) {
375862306a36Sopenharmony_ci			napi_disable(&fep->napi);
375962306a36Sopenharmony_ci			netif_tx_disable(dev);
376062306a36Sopenharmony_ci		}
376162306a36Sopenharmony_ci
376262306a36Sopenharmony_ci		old_prog = xchg(&fep->xdp_prog, bpf->prog);
376362306a36Sopenharmony_ci		if (old_prog)
376462306a36Sopenharmony_ci			bpf_prog_put(old_prog);
376562306a36Sopenharmony_ci
376662306a36Sopenharmony_ci		fec_restart(dev);
376762306a36Sopenharmony_ci
376862306a36Sopenharmony_ci		if (is_run) {
376962306a36Sopenharmony_ci			napi_enable(&fep->napi);
377062306a36Sopenharmony_ci			netif_tx_start_all_queues(dev);
377162306a36Sopenharmony_ci		}
377262306a36Sopenharmony_ci
377362306a36Sopenharmony_ci		if (bpf->prog)
377462306a36Sopenharmony_ci			xdp_features_set_redirect_target(dev, false);
377562306a36Sopenharmony_ci
377662306a36Sopenharmony_ci		return 0;
377762306a36Sopenharmony_ci
377862306a36Sopenharmony_ci	case XDP_SETUP_XSK_POOL:
377962306a36Sopenharmony_ci		return -EOPNOTSUPP;
378062306a36Sopenharmony_ci
378162306a36Sopenharmony_ci	default:
378262306a36Sopenharmony_ci		return -EOPNOTSUPP;
378362306a36Sopenharmony_ci	}
378462306a36Sopenharmony_ci}
378562306a36Sopenharmony_ci
378662306a36Sopenharmony_cistatic int
378762306a36Sopenharmony_cifec_enet_xdp_get_tx_queue(struct fec_enet_private *fep, int index)
378862306a36Sopenharmony_ci{
378962306a36Sopenharmony_ci	if (unlikely(index < 0))
379062306a36Sopenharmony_ci		return 0;
379162306a36Sopenharmony_ci
379262306a36Sopenharmony_ci	return (index % fep->num_tx_queues);
379362306a36Sopenharmony_ci}
379462306a36Sopenharmony_ci
379562306a36Sopenharmony_cistatic int fec_enet_txq_xmit_frame(struct fec_enet_private *fep,
379662306a36Sopenharmony_ci				   struct fec_enet_priv_tx_q *txq,
379762306a36Sopenharmony_ci				   void *frame, u32 dma_sync_len,
379862306a36Sopenharmony_ci				   bool ndo_xmit)
379962306a36Sopenharmony_ci{
380062306a36Sopenharmony_ci	unsigned int index, status, estatus;
380162306a36Sopenharmony_ci	struct bufdesc *bdp;
380262306a36Sopenharmony_ci	dma_addr_t dma_addr;
380362306a36Sopenharmony_ci	int entries_free;
380462306a36Sopenharmony_ci	u16 frame_len;
380562306a36Sopenharmony_ci
380662306a36Sopenharmony_ci	entries_free = fec_enet_get_free_txdesc_num(txq);
380762306a36Sopenharmony_ci	if (entries_free < MAX_SKB_FRAGS + 1) {
380862306a36Sopenharmony_ci		netdev_err_once(fep->netdev, "NOT enough BD for SG!\n");
380962306a36Sopenharmony_ci		return -EBUSY;
381062306a36Sopenharmony_ci	}
381162306a36Sopenharmony_ci
381262306a36Sopenharmony_ci	/* Fill in a Tx ring entry */
381362306a36Sopenharmony_ci	bdp = txq->bd.cur;
381462306a36Sopenharmony_ci	status = fec16_to_cpu(bdp->cbd_sc);
381562306a36Sopenharmony_ci	status &= ~BD_ENET_TX_STATS;
381662306a36Sopenharmony_ci
381762306a36Sopenharmony_ci	index = fec_enet_get_bd_index(bdp, &txq->bd);
381862306a36Sopenharmony_ci
381962306a36Sopenharmony_ci	if (ndo_xmit) {
382062306a36Sopenharmony_ci		struct xdp_frame *xdpf = frame;
382162306a36Sopenharmony_ci
382262306a36Sopenharmony_ci		dma_addr = dma_map_single(&fep->pdev->dev, xdpf->data,
382362306a36Sopenharmony_ci					  xdpf->len, DMA_TO_DEVICE);
382462306a36Sopenharmony_ci		if (dma_mapping_error(&fep->pdev->dev, dma_addr))
382562306a36Sopenharmony_ci			return -ENOMEM;
382662306a36Sopenharmony_ci
382762306a36Sopenharmony_ci		frame_len = xdpf->len;
382862306a36Sopenharmony_ci		txq->tx_buf[index].buf_p = xdpf;
382962306a36Sopenharmony_ci		txq->tx_buf[index].type = FEC_TXBUF_T_XDP_NDO;
383062306a36Sopenharmony_ci	} else {
383162306a36Sopenharmony_ci		struct xdp_buff *xdpb = frame;
383262306a36Sopenharmony_ci		struct page *page;
383362306a36Sopenharmony_ci
383462306a36Sopenharmony_ci		page = virt_to_page(xdpb->data);
383562306a36Sopenharmony_ci		dma_addr = page_pool_get_dma_addr(page) +
383662306a36Sopenharmony_ci			   (xdpb->data - xdpb->data_hard_start);
383762306a36Sopenharmony_ci		dma_sync_single_for_device(&fep->pdev->dev, dma_addr,
383862306a36Sopenharmony_ci					   dma_sync_len, DMA_BIDIRECTIONAL);
383962306a36Sopenharmony_ci		frame_len = xdpb->data_end - xdpb->data;
384062306a36Sopenharmony_ci		txq->tx_buf[index].buf_p = page;
384162306a36Sopenharmony_ci		txq->tx_buf[index].type = FEC_TXBUF_T_XDP_TX;
384262306a36Sopenharmony_ci	}
384362306a36Sopenharmony_ci
384462306a36Sopenharmony_ci	status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST);
384562306a36Sopenharmony_ci	if (fep->bufdesc_ex)
384662306a36Sopenharmony_ci		estatus = BD_ENET_TX_INT;
384762306a36Sopenharmony_ci
384862306a36Sopenharmony_ci	bdp->cbd_bufaddr = cpu_to_fec32(dma_addr);
384962306a36Sopenharmony_ci	bdp->cbd_datlen = cpu_to_fec16(frame_len);
385062306a36Sopenharmony_ci
385162306a36Sopenharmony_ci	if (fep->bufdesc_ex) {
385262306a36Sopenharmony_ci		struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
385362306a36Sopenharmony_ci
385462306a36Sopenharmony_ci		if (fep->quirks & FEC_QUIRK_HAS_AVB)
385562306a36Sopenharmony_ci			estatus |= FEC_TX_BD_FTYPE(txq->bd.qid);
385662306a36Sopenharmony_ci
385762306a36Sopenharmony_ci		ebdp->cbd_bdu = 0;
385862306a36Sopenharmony_ci		ebdp->cbd_esc = cpu_to_fec32(estatus);
385962306a36Sopenharmony_ci	}
386062306a36Sopenharmony_ci
386162306a36Sopenharmony_ci	/* Make sure the updates to rest of the descriptor are performed before
386262306a36Sopenharmony_ci	 * transferring ownership.
386362306a36Sopenharmony_ci	 */
386462306a36Sopenharmony_ci	dma_wmb();
386562306a36Sopenharmony_ci
386662306a36Sopenharmony_ci	/* Send it on its way.  Tell FEC it's ready, interrupt when done,
386762306a36Sopenharmony_ci	 * it's the last BD of the frame, and to put the CRC on the end.
386862306a36Sopenharmony_ci	 */
386962306a36Sopenharmony_ci	status |= (BD_ENET_TX_READY | BD_ENET_TX_TC);
387062306a36Sopenharmony_ci	bdp->cbd_sc = cpu_to_fec16(status);
387162306a36Sopenharmony_ci
387262306a36Sopenharmony_ci	/* If this was the last BD in the ring, start at the beginning again. */
387362306a36Sopenharmony_ci	bdp = fec_enet_get_nextdesc(bdp, &txq->bd);
387462306a36Sopenharmony_ci
387562306a36Sopenharmony_ci	/* Make sure the update to bdp are performed before txq->bd.cur. */
387662306a36Sopenharmony_ci	dma_wmb();
387762306a36Sopenharmony_ci
387862306a36Sopenharmony_ci	txq->bd.cur = bdp;
387962306a36Sopenharmony_ci
388062306a36Sopenharmony_ci	/* Trigger transmission start */
388162306a36Sopenharmony_ci	writel(0, txq->bd.reg_desc_active);
388262306a36Sopenharmony_ci
388362306a36Sopenharmony_ci	return 0;
388462306a36Sopenharmony_ci}
388562306a36Sopenharmony_ci
388662306a36Sopenharmony_cistatic int fec_enet_xdp_tx_xmit(struct fec_enet_private *fep,
388762306a36Sopenharmony_ci				int cpu, struct xdp_buff *xdp,
388862306a36Sopenharmony_ci				u32 dma_sync_len)
388962306a36Sopenharmony_ci{
389062306a36Sopenharmony_ci	struct fec_enet_priv_tx_q *txq;
389162306a36Sopenharmony_ci	struct netdev_queue *nq;
389262306a36Sopenharmony_ci	int queue, ret;
389362306a36Sopenharmony_ci
389462306a36Sopenharmony_ci	queue = fec_enet_xdp_get_tx_queue(fep, cpu);
389562306a36Sopenharmony_ci	txq = fep->tx_queue[queue];
389662306a36Sopenharmony_ci	nq = netdev_get_tx_queue(fep->netdev, queue);
389762306a36Sopenharmony_ci
389862306a36Sopenharmony_ci	__netif_tx_lock(nq, cpu);
389962306a36Sopenharmony_ci
390062306a36Sopenharmony_ci	/* Avoid tx timeout as XDP shares the queue with kernel stack */
390162306a36Sopenharmony_ci	txq_trans_cond_update(nq);
390262306a36Sopenharmony_ci	ret = fec_enet_txq_xmit_frame(fep, txq, xdp, dma_sync_len, false);
390362306a36Sopenharmony_ci
390462306a36Sopenharmony_ci	__netif_tx_unlock(nq);
390562306a36Sopenharmony_ci
390662306a36Sopenharmony_ci	return ret;
390762306a36Sopenharmony_ci}
390862306a36Sopenharmony_ci
390962306a36Sopenharmony_cistatic int fec_enet_xdp_xmit(struct net_device *dev,
391062306a36Sopenharmony_ci			     int num_frames,
391162306a36Sopenharmony_ci			     struct xdp_frame **frames,
391262306a36Sopenharmony_ci			     u32 flags)
391362306a36Sopenharmony_ci{
391462306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(dev);
391562306a36Sopenharmony_ci	struct fec_enet_priv_tx_q *txq;
391662306a36Sopenharmony_ci	int cpu = smp_processor_id();
391762306a36Sopenharmony_ci	unsigned int sent_frames = 0;
391862306a36Sopenharmony_ci	struct netdev_queue *nq;
391962306a36Sopenharmony_ci	unsigned int queue;
392062306a36Sopenharmony_ci	int i;
392162306a36Sopenharmony_ci
392262306a36Sopenharmony_ci	queue = fec_enet_xdp_get_tx_queue(fep, cpu);
392362306a36Sopenharmony_ci	txq = fep->tx_queue[queue];
392462306a36Sopenharmony_ci	nq = netdev_get_tx_queue(fep->netdev, queue);
392562306a36Sopenharmony_ci
392662306a36Sopenharmony_ci	__netif_tx_lock(nq, cpu);
392762306a36Sopenharmony_ci
392862306a36Sopenharmony_ci	/* Avoid tx timeout as XDP shares the queue with kernel stack */
392962306a36Sopenharmony_ci	txq_trans_cond_update(nq);
393062306a36Sopenharmony_ci	for (i = 0; i < num_frames; i++) {
393162306a36Sopenharmony_ci		if (fec_enet_txq_xmit_frame(fep, txq, frames[i], 0, true) < 0)
393262306a36Sopenharmony_ci			break;
393362306a36Sopenharmony_ci		sent_frames++;
393462306a36Sopenharmony_ci	}
393562306a36Sopenharmony_ci
393662306a36Sopenharmony_ci	__netif_tx_unlock(nq);
393762306a36Sopenharmony_ci
393862306a36Sopenharmony_ci	return sent_frames;
393962306a36Sopenharmony_ci}
394062306a36Sopenharmony_ci
394162306a36Sopenharmony_cistatic int fec_hwtstamp_get(struct net_device *ndev,
394262306a36Sopenharmony_ci			    struct kernel_hwtstamp_config *config)
394362306a36Sopenharmony_ci{
394462306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
394562306a36Sopenharmony_ci
394662306a36Sopenharmony_ci	if (!netif_running(ndev))
394762306a36Sopenharmony_ci		return -EINVAL;
394862306a36Sopenharmony_ci
394962306a36Sopenharmony_ci	if (!fep->bufdesc_ex)
395062306a36Sopenharmony_ci		return -EOPNOTSUPP;
395162306a36Sopenharmony_ci
395262306a36Sopenharmony_ci	fec_ptp_get(ndev, config);
395362306a36Sopenharmony_ci
395462306a36Sopenharmony_ci	return 0;
395562306a36Sopenharmony_ci}
395662306a36Sopenharmony_ci
395762306a36Sopenharmony_cistatic int fec_hwtstamp_set(struct net_device *ndev,
395862306a36Sopenharmony_ci			    struct kernel_hwtstamp_config *config,
395962306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
396062306a36Sopenharmony_ci{
396162306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
396262306a36Sopenharmony_ci
396362306a36Sopenharmony_ci	if (!netif_running(ndev))
396462306a36Sopenharmony_ci		return -EINVAL;
396562306a36Sopenharmony_ci
396662306a36Sopenharmony_ci	if (!fep->bufdesc_ex)
396762306a36Sopenharmony_ci		return -EOPNOTSUPP;
396862306a36Sopenharmony_ci
396962306a36Sopenharmony_ci	return fec_ptp_set(ndev, config, extack);
397062306a36Sopenharmony_ci}
397162306a36Sopenharmony_ci
397262306a36Sopenharmony_cistatic const struct net_device_ops fec_netdev_ops = {
397362306a36Sopenharmony_ci	.ndo_open		= fec_enet_open,
397462306a36Sopenharmony_ci	.ndo_stop		= fec_enet_close,
397562306a36Sopenharmony_ci	.ndo_start_xmit		= fec_enet_start_xmit,
397662306a36Sopenharmony_ci	.ndo_select_queue       = fec_enet_select_queue,
397762306a36Sopenharmony_ci	.ndo_set_rx_mode	= set_multicast_list,
397862306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
397962306a36Sopenharmony_ci	.ndo_tx_timeout		= fec_timeout,
398062306a36Sopenharmony_ci	.ndo_set_mac_address	= fec_set_mac_address,
398162306a36Sopenharmony_ci	.ndo_eth_ioctl		= phy_do_ioctl_running,
398262306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
398362306a36Sopenharmony_ci	.ndo_poll_controller	= fec_poll_controller,
398462306a36Sopenharmony_ci#endif
398562306a36Sopenharmony_ci	.ndo_set_features	= fec_set_features,
398662306a36Sopenharmony_ci	.ndo_bpf		= fec_enet_bpf,
398762306a36Sopenharmony_ci	.ndo_xdp_xmit		= fec_enet_xdp_xmit,
398862306a36Sopenharmony_ci	.ndo_hwtstamp_get	= fec_hwtstamp_get,
398962306a36Sopenharmony_ci	.ndo_hwtstamp_set	= fec_hwtstamp_set,
399062306a36Sopenharmony_ci};
399162306a36Sopenharmony_ci
399262306a36Sopenharmony_cistatic const unsigned short offset_des_active_rxq[] = {
399362306a36Sopenharmony_ci	FEC_R_DES_ACTIVE_0, FEC_R_DES_ACTIVE_1, FEC_R_DES_ACTIVE_2
399462306a36Sopenharmony_ci};
399562306a36Sopenharmony_ci
399662306a36Sopenharmony_cistatic const unsigned short offset_des_active_txq[] = {
399762306a36Sopenharmony_ci	FEC_X_DES_ACTIVE_0, FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2
399862306a36Sopenharmony_ci};
399962306a36Sopenharmony_ci
400062306a36Sopenharmony_ci /*
400162306a36Sopenharmony_ci  * XXX:  We need to clean up on failure exits here.
400262306a36Sopenharmony_ci  *
400362306a36Sopenharmony_ci  */
400462306a36Sopenharmony_cistatic int fec_enet_init(struct net_device *ndev)
400562306a36Sopenharmony_ci{
400662306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
400762306a36Sopenharmony_ci	struct bufdesc *cbd_base;
400862306a36Sopenharmony_ci	dma_addr_t bd_dma;
400962306a36Sopenharmony_ci	int bd_size;
401062306a36Sopenharmony_ci	unsigned int i;
401162306a36Sopenharmony_ci	unsigned dsize = fep->bufdesc_ex ? sizeof(struct bufdesc_ex) :
401262306a36Sopenharmony_ci			sizeof(struct bufdesc);
401362306a36Sopenharmony_ci	unsigned dsize_log2 = __fls(dsize);
401462306a36Sopenharmony_ci	int ret;
401562306a36Sopenharmony_ci
401662306a36Sopenharmony_ci	WARN_ON(dsize != (1 << dsize_log2));
401762306a36Sopenharmony_ci#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
401862306a36Sopenharmony_ci	fep->rx_align = 0xf;
401962306a36Sopenharmony_ci	fep->tx_align = 0xf;
402062306a36Sopenharmony_ci#else
402162306a36Sopenharmony_ci	fep->rx_align = 0x3;
402262306a36Sopenharmony_ci	fep->tx_align = 0x3;
402362306a36Sopenharmony_ci#endif
402462306a36Sopenharmony_ci	fep->rx_pkts_itr = FEC_ITR_ICFT_DEFAULT;
402562306a36Sopenharmony_ci	fep->tx_pkts_itr = FEC_ITR_ICFT_DEFAULT;
402662306a36Sopenharmony_ci	fep->rx_time_itr = FEC_ITR_ICTT_DEFAULT;
402762306a36Sopenharmony_ci	fep->tx_time_itr = FEC_ITR_ICTT_DEFAULT;
402862306a36Sopenharmony_ci
402962306a36Sopenharmony_ci	/* Check mask of the streaming and coherent API */
403062306a36Sopenharmony_ci	ret = dma_set_mask_and_coherent(&fep->pdev->dev, DMA_BIT_MASK(32));
403162306a36Sopenharmony_ci	if (ret < 0) {
403262306a36Sopenharmony_ci		dev_warn(&fep->pdev->dev, "No suitable DMA available\n");
403362306a36Sopenharmony_ci		return ret;
403462306a36Sopenharmony_ci	}
403562306a36Sopenharmony_ci
403662306a36Sopenharmony_ci	ret = fec_enet_alloc_queue(ndev);
403762306a36Sopenharmony_ci	if (ret)
403862306a36Sopenharmony_ci		return ret;
403962306a36Sopenharmony_ci
404062306a36Sopenharmony_ci	bd_size = (fep->total_tx_ring_size + fep->total_rx_ring_size) * dsize;
404162306a36Sopenharmony_ci
404262306a36Sopenharmony_ci	/* Allocate memory for buffer descriptors. */
404362306a36Sopenharmony_ci	cbd_base = dmam_alloc_coherent(&fep->pdev->dev, bd_size, &bd_dma,
404462306a36Sopenharmony_ci				       GFP_KERNEL);
404562306a36Sopenharmony_ci	if (!cbd_base) {
404662306a36Sopenharmony_ci		ret = -ENOMEM;
404762306a36Sopenharmony_ci		goto free_queue_mem;
404862306a36Sopenharmony_ci	}
404962306a36Sopenharmony_ci
405062306a36Sopenharmony_ci	/* Get the Ethernet address */
405162306a36Sopenharmony_ci	ret = fec_get_mac(ndev);
405262306a36Sopenharmony_ci	if (ret)
405362306a36Sopenharmony_ci		goto free_queue_mem;
405462306a36Sopenharmony_ci
405562306a36Sopenharmony_ci	/* Set receive and transmit descriptor base. */
405662306a36Sopenharmony_ci	for (i = 0; i < fep->num_rx_queues; i++) {
405762306a36Sopenharmony_ci		struct fec_enet_priv_rx_q *rxq = fep->rx_queue[i];
405862306a36Sopenharmony_ci		unsigned size = dsize * rxq->bd.ring_size;
405962306a36Sopenharmony_ci
406062306a36Sopenharmony_ci		rxq->bd.qid = i;
406162306a36Sopenharmony_ci		rxq->bd.base = cbd_base;
406262306a36Sopenharmony_ci		rxq->bd.cur = cbd_base;
406362306a36Sopenharmony_ci		rxq->bd.dma = bd_dma;
406462306a36Sopenharmony_ci		rxq->bd.dsize = dsize;
406562306a36Sopenharmony_ci		rxq->bd.dsize_log2 = dsize_log2;
406662306a36Sopenharmony_ci		rxq->bd.reg_desc_active = fep->hwp + offset_des_active_rxq[i];
406762306a36Sopenharmony_ci		bd_dma += size;
406862306a36Sopenharmony_ci		cbd_base = (struct bufdesc *)(((void *)cbd_base) + size);
406962306a36Sopenharmony_ci		rxq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize);
407062306a36Sopenharmony_ci	}
407162306a36Sopenharmony_ci
407262306a36Sopenharmony_ci	for (i = 0; i < fep->num_tx_queues; i++) {
407362306a36Sopenharmony_ci		struct fec_enet_priv_tx_q *txq = fep->tx_queue[i];
407462306a36Sopenharmony_ci		unsigned size = dsize * txq->bd.ring_size;
407562306a36Sopenharmony_ci
407662306a36Sopenharmony_ci		txq->bd.qid = i;
407762306a36Sopenharmony_ci		txq->bd.base = cbd_base;
407862306a36Sopenharmony_ci		txq->bd.cur = cbd_base;
407962306a36Sopenharmony_ci		txq->bd.dma = bd_dma;
408062306a36Sopenharmony_ci		txq->bd.dsize = dsize;
408162306a36Sopenharmony_ci		txq->bd.dsize_log2 = dsize_log2;
408262306a36Sopenharmony_ci		txq->bd.reg_desc_active = fep->hwp + offset_des_active_txq[i];
408362306a36Sopenharmony_ci		bd_dma += size;
408462306a36Sopenharmony_ci		cbd_base = (struct bufdesc *)(((void *)cbd_base) + size);
408562306a36Sopenharmony_ci		txq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize);
408662306a36Sopenharmony_ci	}
408762306a36Sopenharmony_ci
408862306a36Sopenharmony_ci
408962306a36Sopenharmony_ci	/* The FEC Ethernet specific entries in the device structure */
409062306a36Sopenharmony_ci	ndev->watchdog_timeo = TX_TIMEOUT;
409162306a36Sopenharmony_ci	ndev->netdev_ops = &fec_netdev_ops;
409262306a36Sopenharmony_ci	ndev->ethtool_ops = &fec_enet_ethtool_ops;
409362306a36Sopenharmony_ci
409462306a36Sopenharmony_ci	writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK);
409562306a36Sopenharmony_ci	netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi);
409662306a36Sopenharmony_ci
409762306a36Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_HAS_VLAN)
409862306a36Sopenharmony_ci		/* enable hw VLAN support */
409962306a36Sopenharmony_ci		ndev->features |= NETIF_F_HW_VLAN_CTAG_RX;
410062306a36Sopenharmony_ci
410162306a36Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_HAS_CSUM) {
410262306a36Sopenharmony_ci		netif_set_tso_max_segs(ndev, FEC_MAX_TSO_SEGS);
410362306a36Sopenharmony_ci
410462306a36Sopenharmony_ci		/* enable hw accelerator */
410562306a36Sopenharmony_ci		ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM
410662306a36Sopenharmony_ci				| NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO);
410762306a36Sopenharmony_ci		fep->csum_flags |= FLAG_RX_CSUM_ENABLED;
410862306a36Sopenharmony_ci	}
410962306a36Sopenharmony_ci
411062306a36Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) {
411162306a36Sopenharmony_ci		fep->tx_align = 0;
411262306a36Sopenharmony_ci		fep->rx_align = 0x3f;
411362306a36Sopenharmony_ci	}
411462306a36Sopenharmony_ci
411562306a36Sopenharmony_ci	ndev->hw_features = ndev->features;
411662306a36Sopenharmony_ci
411762306a36Sopenharmony_ci	if (!(fep->quirks & FEC_QUIRK_SWAP_FRAME))
411862306a36Sopenharmony_ci		ndev->xdp_features = NETDEV_XDP_ACT_BASIC |
411962306a36Sopenharmony_ci				     NETDEV_XDP_ACT_REDIRECT;
412062306a36Sopenharmony_ci
412162306a36Sopenharmony_ci	fec_restart(ndev);
412262306a36Sopenharmony_ci
412362306a36Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_MIB_CLEAR)
412462306a36Sopenharmony_ci		fec_enet_clear_ethtool_stats(ndev);
412562306a36Sopenharmony_ci	else
412662306a36Sopenharmony_ci		fec_enet_update_ethtool_stats(ndev);
412762306a36Sopenharmony_ci
412862306a36Sopenharmony_ci	return 0;
412962306a36Sopenharmony_ci
413062306a36Sopenharmony_cifree_queue_mem:
413162306a36Sopenharmony_ci	fec_enet_free_queue(ndev);
413262306a36Sopenharmony_ci	return ret;
413362306a36Sopenharmony_ci}
413462306a36Sopenharmony_ci
413562306a36Sopenharmony_ci#ifdef CONFIG_OF
413662306a36Sopenharmony_cistatic int fec_reset_phy(struct platform_device *pdev)
413762306a36Sopenharmony_ci{
413862306a36Sopenharmony_ci	struct gpio_desc *phy_reset;
413962306a36Sopenharmony_ci	int msec = 1, phy_post_delay = 0;
414062306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
414162306a36Sopenharmony_ci	int err;
414262306a36Sopenharmony_ci
414362306a36Sopenharmony_ci	if (!np)
414462306a36Sopenharmony_ci		return 0;
414562306a36Sopenharmony_ci
414662306a36Sopenharmony_ci	err = of_property_read_u32(np, "phy-reset-duration", &msec);
414762306a36Sopenharmony_ci	/* A sane reset duration should not be longer than 1s */
414862306a36Sopenharmony_ci	if (!err && msec > 1000)
414962306a36Sopenharmony_ci		msec = 1;
415062306a36Sopenharmony_ci
415162306a36Sopenharmony_ci	err = of_property_read_u32(np, "phy-reset-post-delay", &phy_post_delay);
415262306a36Sopenharmony_ci	/* valid reset duration should be less than 1s */
415362306a36Sopenharmony_ci	if (!err && phy_post_delay > 1000)
415462306a36Sopenharmony_ci		return -EINVAL;
415562306a36Sopenharmony_ci
415662306a36Sopenharmony_ci	phy_reset = devm_gpiod_get_optional(&pdev->dev, "phy-reset",
415762306a36Sopenharmony_ci					    GPIOD_OUT_HIGH);
415862306a36Sopenharmony_ci	if (IS_ERR(phy_reset))
415962306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, PTR_ERR(phy_reset),
416062306a36Sopenharmony_ci				     "failed to get phy-reset-gpios\n");
416162306a36Sopenharmony_ci
416262306a36Sopenharmony_ci	if (!phy_reset)
416362306a36Sopenharmony_ci		return 0;
416462306a36Sopenharmony_ci
416562306a36Sopenharmony_ci	if (msec > 20)
416662306a36Sopenharmony_ci		msleep(msec);
416762306a36Sopenharmony_ci	else
416862306a36Sopenharmony_ci		usleep_range(msec * 1000, msec * 1000 + 1000);
416962306a36Sopenharmony_ci
417062306a36Sopenharmony_ci	gpiod_set_value_cansleep(phy_reset, 0);
417162306a36Sopenharmony_ci
417262306a36Sopenharmony_ci	if (!phy_post_delay)
417362306a36Sopenharmony_ci		return 0;
417462306a36Sopenharmony_ci
417562306a36Sopenharmony_ci	if (phy_post_delay > 20)
417662306a36Sopenharmony_ci		msleep(phy_post_delay);
417762306a36Sopenharmony_ci	else
417862306a36Sopenharmony_ci		usleep_range(phy_post_delay * 1000,
417962306a36Sopenharmony_ci			     phy_post_delay * 1000 + 1000);
418062306a36Sopenharmony_ci
418162306a36Sopenharmony_ci	return 0;
418262306a36Sopenharmony_ci}
418362306a36Sopenharmony_ci#else /* CONFIG_OF */
418462306a36Sopenharmony_cistatic int fec_reset_phy(struct platform_device *pdev)
418562306a36Sopenharmony_ci{
418662306a36Sopenharmony_ci	/*
418762306a36Sopenharmony_ci	 * In case of platform probe, the reset has been done
418862306a36Sopenharmony_ci	 * by machine code.
418962306a36Sopenharmony_ci	 */
419062306a36Sopenharmony_ci	return 0;
419162306a36Sopenharmony_ci}
419262306a36Sopenharmony_ci#endif /* CONFIG_OF */
419362306a36Sopenharmony_ci
419462306a36Sopenharmony_cistatic void
419562306a36Sopenharmony_cifec_enet_get_queue_num(struct platform_device *pdev, int *num_tx, int *num_rx)
419662306a36Sopenharmony_ci{
419762306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
419862306a36Sopenharmony_ci
419962306a36Sopenharmony_ci	*num_tx = *num_rx = 1;
420062306a36Sopenharmony_ci
420162306a36Sopenharmony_ci	if (!np || !of_device_is_available(np))
420262306a36Sopenharmony_ci		return;
420362306a36Sopenharmony_ci
420462306a36Sopenharmony_ci	/* parse the num of tx and rx queues */
420562306a36Sopenharmony_ci	of_property_read_u32(np, "fsl,num-tx-queues", num_tx);
420662306a36Sopenharmony_ci
420762306a36Sopenharmony_ci	of_property_read_u32(np, "fsl,num-rx-queues", num_rx);
420862306a36Sopenharmony_ci
420962306a36Sopenharmony_ci	if (*num_tx < 1 || *num_tx > FEC_ENET_MAX_TX_QS) {
421062306a36Sopenharmony_ci		dev_warn(&pdev->dev, "Invalid num_tx(=%d), fall back to 1\n",
421162306a36Sopenharmony_ci			 *num_tx);
421262306a36Sopenharmony_ci		*num_tx = 1;
421362306a36Sopenharmony_ci		return;
421462306a36Sopenharmony_ci	}
421562306a36Sopenharmony_ci
421662306a36Sopenharmony_ci	if (*num_rx < 1 || *num_rx > FEC_ENET_MAX_RX_QS) {
421762306a36Sopenharmony_ci		dev_warn(&pdev->dev, "Invalid num_rx(=%d), fall back to 1\n",
421862306a36Sopenharmony_ci			 *num_rx);
421962306a36Sopenharmony_ci		*num_rx = 1;
422062306a36Sopenharmony_ci		return;
422162306a36Sopenharmony_ci	}
422262306a36Sopenharmony_ci
422362306a36Sopenharmony_ci}
422462306a36Sopenharmony_ci
422562306a36Sopenharmony_cistatic int fec_enet_get_irq_cnt(struct platform_device *pdev)
422662306a36Sopenharmony_ci{
422762306a36Sopenharmony_ci	int irq_cnt = platform_irq_count(pdev);
422862306a36Sopenharmony_ci
422962306a36Sopenharmony_ci	if (irq_cnt > FEC_IRQ_NUM)
423062306a36Sopenharmony_ci		irq_cnt = FEC_IRQ_NUM;	/* last for pps */
423162306a36Sopenharmony_ci	else if (irq_cnt == 2)
423262306a36Sopenharmony_ci		irq_cnt = 1;	/* last for pps */
423362306a36Sopenharmony_ci	else if (irq_cnt <= 0)
423462306a36Sopenharmony_ci		irq_cnt = 1;	/* At least 1 irq is needed */
423562306a36Sopenharmony_ci	return irq_cnt;
423662306a36Sopenharmony_ci}
423762306a36Sopenharmony_ci
423862306a36Sopenharmony_cistatic void fec_enet_get_wakeup_irq(struct platform_device *pdev)
423962306a36Sopenharmony_ci{
424062306a36Sopenharmony_ci	struct net_device *ndev = platform_get_drvdata(pdev);
424162306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
424262306a36Sopenharmony_ci
424362306a36Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_WAKEUP_FROM_INT2)
424462306a36Sopenharmony_ci		fep->wake_irq = fep->irq[2];
424562306a36Sopenharmony_ci	else
424662306a36Sopenharmony_ci		fep->wake_irq = fep->irq[0];
424762306a36Sopenharmony_ci}
424862306a36Sopenharmony_ci
424962306a36Sopenharmony_cistatic int fec_enet_init_stop_mode(struct fec_enet_private *fep,
425062306a36Sopenharmony_ci				   struct device_node *np)
425162306a36Sopenharmony_ci{
425262306a36Sopenharmony_ci	struct device_node *gpr_np;
425362306a36Sopenharmony_ci	u32 out_val[3];
425462306a36Sopenharmony_ci	int ret = 0;
425562306a36Sopenharmony_ci
425662306a36Sopenharmony_ci	gpr_np = of_parse_phandle(np, "fsl,stop-mode", 0);
425762306a36Sopenharmony_ci	if (!gpr_np)
425862306a36Sopenharmony_ci		return 0;
425962306a36Sopenharmony_ci
426062306a36Sopenharmony_ci	ret = of_property_read_u32_array(np, "fsl,stop-mode", out_val,
426162306a36Sopenharmony_ci					 ARRAY_SIZE(out_val));
426262306a36Sopenharmony_ci	if (ret) {
426362306a36Sopenharmony_ci		dev_dbg(&fep->pdev->dev, "no stop mode property\n");
426462306a36Sopenharmony_ci		goto out;
426562306a36Sopenharmony_ci	}
426662306a36Sopenharmony_ci
426762306a36Sopenharmony_ci	fep->stop_gpr.gpr = syscon_node_to_regmap(gpr_np);
426862306a36Sopenharmony_ci	if (IS_ERR(fep->stop_gpr.gpr)) {
426962306a36Sopenharmony_ci		dev_err(&fep->pdev->dev, "could not find gpr regmap\n");
427062306a36Sopenharmony_ci		ret = PTR_ERR(fep->stop_gpr.gpr);
427162306a36Sopenharmony_ci		fep->stop_gpr.gpr = NULL;
427262306a36Sopenharmony_ci		goto out;
427362306a36Sopenharmony_ci	}
427462306a36Sopenharmony_ci
427562306a36Sopenharmony_ci	fep->stop_gpr.reg = out_val[1];
427662306a36Sopenharmony_ci	fep->stop_gpr.bit = out_val[2];
427762306a36Sopenharmony_ci
427862306a36Sopenharmony_ciout:
427962306a36Sopenharmony_ci	of_node_put(gpr_np);
428062306a36Sopenharmony_ci
428162306a36Sopenharmony_ci	return ret;
428262306a36Sopenharmony_ci}
428362306a36Sopenharmony_ci
428462306a36Sopenharmony_cistatic int
428562306a36Sopenharmony_cifec_probe(struct platform_device *pdev)
428662306a36Sopenharmony_ci{
428762306a36Sopenharmony_ci	struct fec_enet_private *fep;
428862306a36Sopenharmony_ci	struct fec_platform_data *pdata;
428962306a36Sopenharmony_ci	phy_interface_t interface;
429062306a36Sopenharmony_ci	struct net_device *ndev;
429162306a36Sopenharmony_ci	int i, irq, ret = 0;
429262306a36Sopenharmony_ci	const struct of_device_id *of_id;
429362306a36Sopenharmony_ci	static int dev_id;
429462306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node, *phy_node;
429562306a36Sopenharmony_ci	int num_tx_qs;
429662306a36Sopenharmony_ci	int num_rx_qs;
429762306a36Sopenharmony_ci	char irq_name[8];
429862306a36Sopenharmony_ci	int irq_cnt;
429962306a36Sopenharmony_ci	struct fec_devinfo *dev_info;
430062306a36Sopenharmony_ci
430162306a36Sopenharmony_ci	fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs);
430262306a36Sopenharmony_ci
430362306a36Sopenharmony_ci	/* Init network device */
430462306a36Sopenharmony_ci	ndev = alloc_etherdev_mqs(sizeof(struct fec_enet_private) +
430562306a36Sopenharmony_ci				  FEC_STATS_SIZE, num_tx_qs, num_rx_qs);
430662306a36Sopenharmony_ci	if (!ndev)
430762306a36Sopenharmony_ci		return -ENOMEM;
430862306a36Sopenharmony_ci
430962306a36Sopenharmony_ci	SET_NETDEV_DEV(ndev, &pdev->dev);
431062306a36Sopenharmony_ci
431162306a36Sopenharmony_ci	/* setup board info structure */
431262306a36Sopenharmony_ci	fep = netdev_priv(ndev);
431362306a36Sopenharmony_ci
431462306a36Sopenharmony_ci	of_id = of_match_device(fec_dt_ids, &pdev->dev);
431562306a36Sopenharmony_ci	if (of_id)
431662306a36Sopenharmony_ci		pdev->id_entry = of_id->data;
431762306a36Sopenharmony_ci	dev_info = (struct fec_devinfo *)pdev->id_entry->driver_data;
431862306a36Sopenharmony_ci	if (dev_info)
431962306a36Sopenharmony_ci		fep->quirks = dev_info->quirks;
432062306a36Sopenharmony_ci
432162306a36Sopenharmony_ci	fep->netdev = ndev;
432262306a36Sopenharmony_ci	fep->num_rx_queues = num_rx_qs;
432362306a36Sopenharmony_ci	fep->num_tx_queues = num_tx_qs;
432462306a36Sopenharmony_ci
432562306a36Sopenharmony_ci#if !defined(CONFIG_M5272)
432662306a36Sopenharmony_ci	/* default enable pause frame auto negotiation */
432762306a36Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_HAS_GBIT)
432862306a36Sopenharmony_ci		fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG;
432962306a36Sopenharmony_ci#endif
433062306a36Sopenharmony_ci
433162306a36Sopenharmony_ci	/* Select default pin state */
433262306a36Sopenharmony_ci	pinctrl_pm_select_default_state(&pdev->dev);
433362306a36Sopenharmony_ci
433462306a36Sopenharmony_ci	fep->hwp = devm_platform_ioremap_resource(pdev, 0);
433562306a36Sopenharmony_ci	if (IS_ERR(fep->hwp)) {
433662306a36Sopenharmony_ci		ret = PTR_ERR(fep->hwp);
433762306a36Sopenharmony_ci		goto failed_ioremap;
433862306a36Sopenharmony_ci	}
433962306a36Sopenharmony_ci
434062306a36Sopenharmony_ci	fep->pdev = pdev;
434162306a36Sopenharmony_ci	fep->dev_id = dev_id++;
434262306a36Sopenharmony_ci
434362306a36Sopenharmony_ci	platform_set_drvdata(pdev, ndev);
434462306a36Sopenharmony_ci
434562306a36Sopenharmony_ci	if ((of_machine_is_compatible("fsl,imx6q") ||
434662306a36Sopenharmony_ci	     of_machine_is_compatible("fsl,imx6dl")) &&
434762306a36Sopenharmony_ci	    !of_property_read_bool(np, "fsl,err006687-workaround-present"))
434862306a36Sopenharmony_ci		fep->quirks |= FEC_QUIRK_ERR006687;
434962306a36Sopenharmony_ci
435062306a36Sopenharmony_ci	ret = fec_enet_ipc_handle_init(fep);
435162306a36Sopenharmony_ci	if (ret)
435262306a36Sopenharmony_ci		goto failed_ipc_init;
435362306a36Sopenharmony_ci
435462306a36Sopenharmony_ci	if (of_property_read_bool(np, "fsl,magic-packet"))
435562306a36Sopenharmony_ci		fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET;
435662306a36Sopenharmony_ci
435762306a36Sopenharmony_ci	ret = fec_enet_init_stop_mode(fep, np);
435862306a36Sopenharmony_ci	if (ret)
435962306a36Sopenharmony_ci		goto failed_stop_mode;
436062306a36Sopenharmony_ci
436162306a36Sopenharmony_ci	phy_node = of_parse_phandle(np, "phy-handle", 0);
436262306a36Sopenharmony_ci	if (!phy_node && of_phy_is_fixed_link(np)) {
436362306a36Sopenharmony_ci		ret = of_phy_register_fixed_link(np);
436462306a36Sopenharmony_ci		if (ret < 0) {
436562306a36Sopenharmony_ci			dev_err(&pdev->dev,
436662306a36Sopenharmony_ci				"broken fixed-link specification\n");
436762306a36Sopenharmony_ci			goto failed_phy;
436862306a36Sopenharmony_ci		}
436962306a36Sopenharmony_ci		phy_node = of_node_get(np);
437062306a36Sopenharmony_ci	}
437162306a36Sopenharmony_ci	fep->phy_node = phy_node;
437262306a36Sopenharmony_ci
437362306a36Sopenharmony_ci	ret = of_get_phy_mode(pdev->dev.of_node, &interface);
437462306a36Sopenharmony_ci	if (ret) {
437562306a36Sopenharmony_ci		pdata = dev_get_platdata(&pdev->dev);
437662306a36Sopenharmony_ci		if (pdata)
437762306a36Sopenharmony_ci			fep->phy_interface = pdata->phy;
437862306a36Sopenharmony_ci		else
437962306a36Sopenharmony_ci			fep->phy_interface = PHY_INTERFACE_MODE_MII;
438062306a36Sopenharmony_ci	} else {
438162306a36Sopenharmony_ci		fep->phy_interface = interface;
438262306a36Sopenharmony_ci	}
438362306a36Sopenharmony_ci
438462306a36Sopenharmony_ci	ret = fec_enet_parse_rgmii_delay(fep, np);
438562306a36Sopenharmony_ci	if (ret)
438662306a36Sopenharmony_ci		goto failed_rgmii_delay;
438762306a36Sopenharmony_ci
438862306a36Sopenharmony_ci	fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
438962306a36Sopenharmony_ci	if (IS_ERR(fep->clk_ipg)) {
439062306a36Sopenharmony_ci		ret = PTR_ERR(fep->clk_ipg);
439162306a36Sopenharmony_ci		goto failed_clk;
439262306a36Sopenharmony_ci	}
439362306a36Sopenharmony_ci
439462306a36Sopenharmony_ci	fep->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
439562306a36Sopenharmony_ci	if (IS_ERR(fep->clk_ahb)) {
439662306a36Sopenharmony_ci		ret = PTR_ERR(fep->clk_ahb);
439762306a36Sopenharmony_ci		goto failed_clk;
439862306a36Sopenharmony_ci	}
439962306a36Sopenharmony_ci
440062306a36Sopenharmony_ci	fep->itr_clk_rate = clk_get_rate(fep->clk_ahb);
440162306a36Sopenharmony_ci
440262306a36Sopenharmony_ci	/* enet_out is optional, depends on board */
440362306a36Sopenharmony_ci	fep->clk_enet_out = devm_clk_get_optional(&pdev->dev, "enet_out");
440462306a36Sopenharmony_ci	if (IS_ERR(fep->clk_enet_out)) {
440562306a36Sopenharmony_ci		ret = PTR_ERR(fep->clk_enet_out);
440662306a36Sopenharmony_ci		goto failed_clk;
440762306a36Sopenharmony_ci	}
440862306a36Sopenharmony_ci
440962306a36Sopenharmony_ci	fep->ptp_clk_on = false;
441062306a36Sopenharmony_ci	mutex_init(&fep->ptp_clk_mutex);
441162306a36Sopenharmony_ci
441262306a36Sopenharmony_ci	/* clk_ref is optional, depends on board */
441362306a36Sopenharmony_ci	fep->clk_ref = devm_clk_get_optional(&pdev->dev, "enet_clk_ref");
441462306a36Sopenharmony_ci	if (IS_ERR(fep->clk_ref)) {
441562306a36Sopenharmony_ci		ret = PTR_ERR(fep->clk_ref);
441662306a36Sopenharmony_ci		goto failed_clk;
441762306a36Sopenharmony_ci	}
441862306a36Sopenharmony_ci	fep->clk_ref_rate = clk_get_rate(fep->clk_ref);
441962306a36Sopenharmony_ci
442062306a36Sopenharmony_ci	/* clk_2x_txclk is optional, depends on board */
442162306a36Sopenharmony_ci	if (fep->rgmii_txc_dly || fep->rgmii_rxc_dly) {
442262306a36Sopenharmony_ci		fep->clk_2x_txclk = devm_clk_get(&pdev->dev, "enet_2x_txclk");
442362306a36Sopenharmony_ci		if (IS_ERR(fep->clk_2x_txclk))
442462306a36Sopenharmony_ci			fep->clk_2x_txclk = NULL;
442562306a36Sopenharmony_ci	}
442662306a36Sopenharmony_ci
442762306a36Sopenharmony_ci	fep->bufdesc_ex = fep->quirks & FEC_QUIRK_HAS_BUFDESC_EX;
442862306a36Sopenharmony_ci	fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp");
442962306a36Sopenharmony_ci	if (IS_ERR(fep->clk_ptp)) {
443062306a36Sopenharmony_ci		fep->clk_ptp = NULL;
443162306a36Sopenharmony_ci		fep->bufdesc_ex = false;
443262306a36Sopenharmony_ci	}
443362306a36Sopenharmony_ci
443462306a36Sopenharmony_ci	ret = fec_enet_clk_enable(ndev, true);
443562306a36Sopenharmony_ci	if (ret)
443662306a36Sopenharmony_ci		goto failed_clk;
443762306a36Sopenharmony_ci
443862306a36Sopenharmony_ci	ret = clk_prepare_enable(fep->clk_ipg);
443962306a36Sopenharmony_ci	if (ret)
444062306a36Sopenharmony_ci		goto failed_clk_ipg;
444162306a36Sopenharmony_ci	ret = clk_prepare_enable(fep->clk_ahb);
444262306a36Sopenharmony_ci	if (ret)
444362306a36Sopenharmony_ci		goto failed_clk_ahb;
444462306a36Sopenharmony_ci
444562306a36Sopenharmony_ci	fep->reg_phy = devm_regulator_get_optional(&pdev->dev, "phy");
444662306a36Sopenharmony_ci	if (!IS_ERR(fep->reg_phy)) {
444762306a36Sopenharmony_ci		ret = regulator_enable(fep->reg_phy);
444862306a36Sopenharmony_ci		if (ret) {
444962306a36Sopenharmony_ci			dev_err(&pdev->dev,
445062306a36Sopenharmony_ci				"Failed to enable phy regulator: %d\n", ret);
445162306a36Sopenharmony_ci			goto failed_regulator;
445262306a36Sopenharmony_ci		}
445362306a36Sopenharmony_ci	} else {
445462306a36Sopenharmony_ci		if (PTR_ERR(fep->reg_phy) == -EPROBE_DEFER) {
445562306a36Sopenharmony_ci			ret = -EPROBE_DEFER;
445662306a36Sopenharmony_ci			goto failed_regulator;
445762306a36Sopenharmony_ci		}
445862306a36Sopenharmony_ci		fep->reg_phy = NULL;
445962306a36Sopenharmony_ci	}
446062306a36Sopenharmony_ci
446162306a36Sopenharmony_ci	pm_runtime_set_autosuspend_delay(&pdev->dev, FEC_MDIO_PM_TIMEOUT);
446262306a36Sopenharmony_ci	pm_runtime_use_autosuspend(&pdev->dev);
446362306a36Sopenharmony_ci	pm_runtime_get_noresume(&pdev->dev);
446462306a36Sopenharmony_ci	pm_runtime_set_active(&pdev->dev);
446562306a36Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
446662306a36Sopenharmony_ci
446762306a36Sopenharmony_ci	ret = fec_reset_phy(pdev);
446862306a36Sopenharmony_ci	if (ret)
446962306a36Sopenharmony_ci		goto failed_reset;
447062306a36Sopenharmony_ci
447162306a36Sopenharmony_ci	irq_cnt = fec_enet_get_irq_cnt(pdev);
447262306a36Sopenharmony_ci	if (fep->bufdesc_ex)
447362306a36Sopenharmony_ci		fec_ptp_init(pdev, irq_cnt);
447462306a36Sopenharmony_ci
447562306a36Sopenharmony_ci	ret = fec_enet_init(ndev);
447662306a36Sopenharmony_ci	if (ret)
447762306a36Sopenharmony_ci		goto failed_init;
447862306a36Sopenharmony_ci
447962306a36Sopenharmony_ci	for (i = 0; i < irq_cnt; i++) {
448062306a36Sopenharmony_ci		snprintf(irq_name, sizeof(irq_name), "int%d", i);
448162306a36Sopenharmony_ci		irq = platform_get_irq_byname_optional(pdev, irq_name);
448262306a36Sopenharmony_ci		if (irq < 0)
448362306a36Sopenharmony_ci			irq = platform_get_irq(pdev, i);
448462306a36Sopenharmony_ci		if (irq < 0) {
448562306a36Sopenharmony_ci			ret = irq;
448662306a36Sopenharmony_ci			goto failed_irq;
448762306a36Sopenharmony_ci		}
448862306a36Sopenharmony_ci		ret = devm_request_irq(&pdev->dev, irq, fec_enet_interrupt,
448962306a36Sopenharmony_ci				       0, pdev->name, ndev);
449062306a36Sopenharmony_ci		if (ret)
449162306a36Sopenharmony_ci			goto failed_irq;
449262306a36Sopenharmony_ci
449362306a36Sopenharmony_ci		fep->irq[i] = irq;
449462306a36Sopenharmony_ci	}
449562306a36Sopenharmony_ci
449662306a36Sopenharmony_ci	/* Decide which interrupt line is wakeup capable */
449762306a36Sopenharmony_ci	fec_enet_get_wakeup_irq(pdev);
449862306a36Sopenharmony_ci
449962306a36Sopenharmony_ci	ret = fec_enet_mii_init(pdev);
450062306a36Sopenharmony_ci	if (ret)
450162306a36Sopenharmony_ci		goto failed_mii_init;
450262306a36Sopenharmony_ci
450362306a36Sopenharmony_ci	/* Carrier starts down, phylib will bring it up */
450462306a36Sopenharmony_ci	netif_carrier_off(ndev);
450562306a36Sopenharmony_ci	fec_enet_clk_enable(ndev, false);
450662306a36Sopenharmony_ci	pinctrl_pm_select_sleep_state(&pdev->dev);
450762306a36Sopenharmony_ci
450862306a36Sopenharmony_ci	ndev->max_mtu = PKT_MAXBUF_SIZE - ETH_HLEN - ETH_FCS_LEN;
450962306a36Sopenharmony_ci
451062306a36Sopenharmony_ci	ret = register_netdev(ndev);
451162306a36Sopenharmony_ci	if (ret)
451262306a36Sopenharmony_ci		goto failed_register;
451362306a36Sopenharmony_ci
451462306a36Sopenharmony_ci	device_init_wakeup(&ndev->dev, fep->wol_flag &
451562306a36Sopenharmony_ci			   FEC_WOL_HAS_MAGIC_PACKET);
451662306a36Sopenharmony_ci
451762306a36Sopenharmony_ci	if (fep->bufdesc_ex && fep->ptp_clock)
451862306a36Sopenharmony_ci		netdev_info(ndev, "registered PHC device %d\n", fep->dev_id);
451962306a36Sopenharmony_ci
452062306a36Sopenharmony_ci	INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work);
452162306a36Sopenharmony_ci
452262306a36Sopenharmony_ci	pm_runtime_mark_last_busy(&pdev->dev);
452362306a36Sopenharmony_ci	pm_runtime_put_autosuspend(&pdev->dev);
452462306a36Sopenharmony_ci
452562306a36Sopenharmony_ci	return 0;
452662306a36Sopenharmony_ci
452762306a36Sopenharmony_cifailed_register:
452862306a36Sopenharmony_ci	fec_enet_mii_remove(fep);
452962306a36Sopenharmony_cifailed_mii_init:
453062306a36Sopenharmony_cifailed_irq:
453162306a36Sopenharmony_cifailed_init:
453262306a36Sopenharmony_ci	fec_ptp_stop(pdev);
453362306a36Sopenharmony_cifailed_reset:
453462306a36Sopenharmony_ci	pm_runtime_put_noidle(&pdev->dev);
453562306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
453662306a36Sopenharmony_ci	if (fep->reg_phy)
453762306a36Sopenharmony_ci		regulator_disable(fep->reg_phy);
453862306a36Sopenharmony_cifailed_regulator:
453962306a36Sopenharmony_ci	clk_disable_unprepare(fep->clk_ahb);
454062306a36Sopenharmony_cifailed_clk_ahb:
454162306a36Sopenharmony_ci	clk_disable_unprepare(fep->clk_ipg);
454262306a36Sopenharmony_cifailed_clk_ipg:
454362306a36Sopenharmony_ci	fec_enet_clk_enable(ndev, false);
454462306a36Sopenharmony_cifailed_clk:
454562306a36Sopenharmony_cifailed_rgmii_delay:
454662306a36Sopenharmony_ci	if (of_phy_is_fixed_link(np))
454762306a36Sopenharmony_ci		of_phy_deregister_fixed_link(np);
454862306a36Sopenharmony_ci	of_node_put(phy_node);
454962306a36Sopenharmony_cifailed_stop_mode:
455062306a36Sopenharmony_cifailed_ipc_init:
455162306a36Sopenharmony_cifailed_phy:
455262306a36Sopenharmony_ci	dev_id--;
455362306a36Sopenharmony_cifailed_ioremap:
455462306a36Sopenharmony_ci	free_netdev(ndev);
455562306a36Sopenharmony_ci
455662306a36Sopenharmony_ci	return ret;
455762306a36Sopenharmony_ci}
455862306a36Sopenharmony_ci
455962306a36Sopenharmony_cistatic void
456062306a36Sopenharmony_cifec_drv_remove(struct platform_device *pdev)
456162306a36Sopenharmony_ci{
456262306a36Sopenharmony_ci	struct net_device *ndev = platform_get_drvdata(pdev);
456362306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
456462306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
456562306a36Sopenharmony_ci	int ret;
456662306a36Sopenharmony_ci
456762306a36Sopenharmony_ci	ret = pm_runtime_get_sync(&pdev->dev);
456862306a36Sopenharmony_ci	if (ret < 0)
456962306a36Sopenharmony_ci		dev_err(&pdev->dev,
457062306a36Sopenharmony_ci			"Failed to resume device in remove callback (%pe)\n",
457162306a36Sopenharmony_ci			ERR_PTR(ret));
457262306a36Sopenharmony_ci
457362306a36Sopenharmony_ci	cancel_work_sync(&fep->tx_timeout_work);
457462306a36Sopenharmony_ci	fec_ptp_stop(pdev);
457562306a36Sopenharmony_ci	unregister_netdev(ndev);
457662306a36Sopenharmony_ci	fec_enet_mii_remove(fep);
457762306a36Sopenharmony_ci	if (fep->reg_phy)
457862306a36Sopenharmony_ci		regulator_disable(fep->reg_phy);
457962306a36Sopenharmony_ci
458062306a36Sopenharmony_ci	if (of_phy_is_fixed_link(np))
458162306a36Sopenharmony_ci		of_phy_deregister_fixed_link(np);
458262306a36Sopenharmony_ci	of_node_put(fep->phy_node);
458362306a36Sopenharmony_ci
458462306a36Sopenharmony_ci	/* After pm_runtime_get_sync() failed, the clks are still off, so skip
458562306a36Sopenharmony_ci	 * disabling them again.
458662306a36Sopenharmony_ci	 */
458762306a36Sopenharmony_ci	if (ret >= 0) {
458862306a36Sopenharmony_ci		clk_disable_unprepare(fep->clk_ahb);
458962306a36Sopenharmony_ci		clk_disable_unprepare(fep->clk_ipg);
459062306a36Sopenharmony_ci	}
459162306a36Sopenharmony_ci	pm_runtime_put_noidle(&pdev->dev);
459262306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
459362306a36Sopenharmony_ci
459462306a36Sopenharmony_ci	free_netdev(ndev);
459562306a36Sopenharmony_ci}
459662306a36Sopenharmony_ci
459762306a36Sopenharmony_cistatic int __maybe_unused fec_suspend(struct device *dev)
459862306a36Sopenharmony_ci{
459962306a36Sopenharmony_ci	struct net_device *ndev = dev_get_drvdata(dev);
460062306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
460162306a36Sopenharmony_ci	int ret;
460262306a36Sopenharmony_ci
460362306a36Sopenharmony_ci	rtnl_lock();
460462306a36Sopenharmony_ci	if (netif_running(ndev)) {
460562306a36Sopenharmony_ci		if (fep->wol_flag & FEC_WOL_FLAG_ENABLE)
460662306a36Sopenharmony_ci			fep->wol_flag |= FEC_WOL_FLAG_SLEEP_ON;
460762306a36Sopenharmony_ci		phy_stop(ndev->phydev);
460862306a36Sopenharmony_ci		napi_disable(&fep->napi);
460962306a36Sopenharmony_ci		netif_tx_lock_bh(ndev);
461062306a36Sopenharmony_ci		netif_device_detach(ndev);
461162306a36Sopenharmony_ci		netif_tx_unlock_bh(ndev);
461262306a36Sopenharmony_ci		fec_stop(ndev);
461362306a36Sopenharmony_ci		if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) {
461462306a36Sopenharmony_ci			fec_irqs_disable(ndev);
461562306a36Sopenharmony_ci			pinctrl_pm_select_sleep_state(&fep->pdev->dev);
461662306a36Sopenharmony_ci		} else {
461762306a36Sopenharmony_ci			fec_irqs_disable_except_wakeup(ndev);
461862306a36Sopenharmony_ci			if (fep->wake_irq > 0) {
461962306a36Sopenharmony_ci				disable_irq(fep->wake_irq);
462062306a36Sopenharmony_ci				enable_irq_wake(fep->wake_irq);
462162306a36Sopenharmony_ci			}
462262306a36Sopenharmony_ci			fec_enet_stop_mode(fep, true);
462362306a36Sopenharmony_ci		}
462462306a36Sopenharmony_ci		/* It's safe to disable clocks since interrupts are masked */
462562306a36Sopenharmony_ci		fec_enet_clk_enable(ndev, false);
462662306a36Sopenharmony_ci
462762306a36Sopenharmony_ci		fep->rpm_active = !pm_runtime_status_suspended(dev);
462862306a36Sopenharmony_ci		if (fep->rpm_active) {
462962306a36Sopenharmony_ci			ret = pm_runtime_force_suspend(dev);
463062306a36Sopenharmony_ci			if (ret < 0) {
463162306a36Sopenharmony_ci				rtnl_unlock();
463262306a36Sopenharmony_ci				return ret;
463362306a36Sopenharmony_ci			}
463462306a36Sopenharmony_ci		}
463562306a36Sopenharmony_ci	}
463662306a36Sopenharmony_ci	rtnl_unlock();
463762306a36Sopenharmony_ci
463862306a36Sopenharmony_ci	if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE))
463962306a36Sopenharmony_ci		regulator_disable(fep->reg_phy);
464062306a36Sopenharmony_ci
464162306a36Sopenharmony_ci	/* SOC supply clock to phy, when clock is disabled, phy link down
464262306a36Sopenharmony_ci	 * SOC control phy regulator, when regulator is disabled, phy link down
464362306a36Sopenharmony_ci	 */
464462306a36Sopenharmony_ci	if (fep->clk_enet_out || fep->reg_phy)
464562306a36Sopenharmony_ci		fep->link = 0;
464662306a36Sopenharmony_ci
464762306a36Sopenharmony_ci	return 0;
464862306a36Sopenharmony_ci}
464962306a36Sopenharmony_ci
465062306a36Sopenharmony_cistatic int __maybe_unused fec_resume(struct device *dev)
465162306a36Sopenharmony_ci{
465262306a36Sopenharmony_ci	struct net_device *ndev = dev_get_drvdata(dev);
465362306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
465462306a36Sopenharmony_ci	int ret;
465562306a36Sopenharmony_ci	int val;
465662306a36Sopenharmony_ci
465762306a36Sopenharmony_ci	if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) {
465862306a36Sopenharmony_ci		ret = regulator_enable(fep->reg_phy);
465962306a36Sopenharmony_ci		if (ret)
466062306a36Sopenharmony_ci			return ret;
466162306a36Sopenharmony_ci	}
466262306a36Sopenharmony_ci
466362306a36Sopenharmony_ci	rtnl_lock();
466462306a36Sopenharmony_ci	if (netif_running(ndev)) {
466562306a36Sopenharmony_ci		if (fep->rpm_active)
466662306a36Sopenharmony_ci			pm_runtime_force_resume(dev);
466762306a36Sopenharmony_ci
466862306a36Sopenharmony_ci		ret = fec_enet_clk_enable(ndev, true);
466962306a36Sopenharmony_ci		if (ret) {
467062306a36Sopenharmony_ci			rtnl_unlock();
467162306a36Sopenharmony_ci			goto failed_clk;
467262306a36Sopenharmony_ci		}
467362306a36Sopenharmony_ci		if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) {
467462306a36Sopenharmony_ci			fec_enet_stop_mode(fep, false);
467562306a36Sopenharmony_ci			if (fep->wake_irq) {
467662306a36Sopenharmony_ci				disable_irq_wake(fep->wake_irq);
467762306a36Sopenharmony_ci				enable_irq(fep->wake_irq);
467862306a36Sopenharmony_ci			}
467962306a36Sopenharmony_ci
468062306a36Sopenharmony_ci			val = readl(fep->hwp + FEC_ECNTRL);
468162306a36Sopenharmony_ci			val &= ~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
468262306a36Sopenharmony_ci			writel(val, fep->hwp + FEC_ECNTRL);
468362306a36Sopenharmony_ci			fep->wol_flag &= ~FEC_WOL_FLAG_SLEEP_ON;
468462306a36Sopenharmony_ci		} else {
468562306a36Sopenharmony_ci			pinctrl_pm_select_default_state(&fep->pdev->dev);
468662306a36Sopenharmony_ci		}
468762306a36Sopenharmony_ci		fec_restart(ndev);
468862306a36Sopenharmony_ci		netif_tx_lock_bh(ndev);
468962306a36Sopenharmony_ci		netif_device_attach(ndev);
469062306a36Sopenharmony_ci		netif_tx_unlock_bh(ndev);
469162306a36Sopenharmony_ci		napi_enable(&fep->napi);
469262306a36Sopenharmony_ci		phy_init_hw(ndev->phydev);
469362306a36Sopenharmony_ci		phy_start(ndev->phydev);
469462306a36Sopenharmony_ci	}
469562306a36Sopenharmony_ci	rtnl_unlock();
469662306a36Sopenharmony_ci
469762306a36Sopenharmony_ci	return 0;
469862306a36Sopenharmony_ci
469962306a36Sopenharmony_cifailed_clk:
470062306a36Sopenharmony_ci	if (fep->reg_phy)
470162306a36Sopenharmony_ci		regulator_disable(fep->reg_phy);
470262306a36Sopenharmony_ci	return ret;
470362306a36Sopenharmony_ci}
470462306a36Sopenharmony_ci
470562306a36Sopenharmony_cistatic int __maybe_unused fec_runtime_suspend(struct device *dev)
470662306a36Sopenharmony_ci{
470762306a36Sopenharmony_ci	struct net_device *ndev = dev_get_drvdata(dev);
470862306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
470962306a36Sopenharmony_ci
471062306a36Sopenharmony_ci	clk_disable_unprepare(fep->clk_ahb);
471162306a36Sopenharmony_ci	clk_disable_unprepare(fep->clk_ipg);
471262306a36Sopenharmony_ci
471362306a36Sopenharmony_ci	return 0;
471462306a36Sopenharmony_ci}
471562306a36Sopenharmony_ci
471662306a36Sopenharmony_cistatic int __maybe_unused fec_runtime_resume(struct device *dev)
471762306a36Sopenharmony_ci{
471862306a36Sopenharmony_ci	struct net_device *ndev = dev_get_drvdata(dev);
471962306a36Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
472062306a36Sopenharmony_ci	int ret;
472162306a36Sopenharmony_ci
472262306a36Sopenharmony_ci	ret = clk_prepare_enable(fep->clk_ahb);
472362306a36Sopenharmony_ci	if (ret)
472462306a36Sopenharmony_ci		return ret;
472562306a36Sopenharmony_ci	ret = clk_prepare_enable(fep->clk_ipg);
472662306a36Sopenharmony_ci	if (ret)
472762306a36Sopenharmony_ci		goto failed_clk_ipg;
472862306a36Sopenharmony_ci
472962306a36Sopenharmony_ci	return 0;
473062306a36Sopenharmony_ci
473162306a36Sopenharmony_cifailed_clk_ipg:
473262306a36Sopenharmony_ci	clk_disable_unprepare(fep->clk_ahb);
473362306a36Sopenharmony_ci	return ret;
473462306a36Sopenharmony_ci}
473562306a36Sopenharmony_ci
473662306a36Sopenharmony_cistatic const struct dev_pm_ops fec_pm_ops = {
473762306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(fec_suspend, fec_resume)
473862306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(fec_runtime_suspend, fec_runtime_resume, NULL)
473962306a36Sopenharmony_ci};
474062306a36Sopenharmony_ci
474162306a36Sopenharmony_cistatic struct platform_driver fec_driver = {
474262306a36Sopenharmony_ci	.driver	= {
474362306a36Sopenharmony_ci		.name	= DRIVER_NAME,
474462306a36Sopenharmony_ci		.pm	= &fec_pm_ops,
474562306a36Sopenharmony_ci		.of_match_table = fec_dt_ids,
474662306a36Sopenharmony_ci		.suppress_bind_attrs = true,
474762306a36Sopenharmony_ci	},
474862306a36Sopenharmony_ci	.id_table = fec_devtype,
474962306a36Sopenharmony_ci	.probe	= fec_probe,
475062306a36Sopenharmony_ci	.remove_new = fec_drv_remove,
475162306a36Sopenharmony_ci};
475262306a36Sopenharmony_ci
475362306a36Sopenharmony_cimodule_platform_driver(fec_driver);
475462306a36Sopenharmony_ci
475562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
4756