18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Fast Ethernet Controller (FEC) driver for Motorola MPC8xx.
48c2ecf20Sopenharmony_ci * Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Right now, I am very wasteful with the buffers.  I allocate memory
78c2ecf20Sopenharmony_ci * pages and then divide them into 2K frame buffers.  This way I know I
88c2ecf20Sopenharmony_ci * have buffers large enough to hold one frame within one buffer descriptor.
98c2ecf20Sopenharmony_ci * Once I get this working, I will use 64 or 128 byte CPM buffers, which
108c2ecf20Sopenharmony_ci * will be much more memory efficient and will easily handle lots of
118c2ecf20Sopenharmony_ci * small packets.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * Much better multiple PHY support by Magnus Damm.
148c2ecf20Sopenharmony_ci * Copyright (c) 2000 Ericsson Radio Systems AB.
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * Support for FEC controller of ColdFire processors.
178c2ecf20Sopenharmony_ci * Copyright (c) 2001-2005 Greg Ungerer (gerg@snapgear.com)
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be)
208c2ecf20Sopenharmony_ci * Copyright (c) 2004-2006 Macq Electronique SA.
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <linux/module.h>
268c2ecf20Sopenharmony_ci#include <linux/kernel.h>
278c2ecf20Sopenharmony_ci#include <linux/string.h>
288c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
298c2ecf20Sopenharmony_ci#include <linux/ptrace.h>
308c2ecf20Sopenharmony_ci#include <linux/errno.h>
318c2ecf20Sopenharmony_ci#include <linux/ioport.h>
328c2ecf20Sopenharmony_ci#include <linux/slab.h>
338c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
348c2ecf20Sopenharmony_ci#include <linux/delay.h>
358c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
368c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
378c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
388c2ecf20Sopenharmony_ci#include <linux/in.h>
398c2ecf20Sopenharmony_ci#include <linux/ip.h>
408c2ecf20Sopenharmony_ci#include <net/ip.h>
418c2ecf20Sopenharmony_ci#include <net/tso.h>
428c2ecf20Sopenharmony_ci#include <linux/tcp.h>
438c2ecf20Sopenharmony_ci#include <linux/udp.h>
448c2ecf20Sopenharmony_ci#include <linux/icmp.h>
458c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
468c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
478c2ecf20Sopenharmony_ci#include <linux/bitops.h>
488c2ecf20Sopenharmony_ci#include <linux/io.h>
498c2ecf20Sopenharmony_ci#include <linux/irq.h>
508c2ecf20Sopenharmony_ci#include <linux/clk.h>
518c2ecf20Sopenharmony_ci#include <linux/crc32.h>
528c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
538c2ecf20Sopenharmony_ci#include <linux/mdio.h>
548c2ecf20Sopenharmony_ci#include <linux/phy.h>
558c2ecf20Sopenharmony_ci#include <linux/fec.h>
568c2ecf20Sopenharmony_ci#include <linux/of.h>
578c2ecf20Sopenharmony_ci#include <linux/of_device.h>
588c2ecf20Sopenharmony_ci#include <linux/of_gpio.h>
598c2ecf20Sopenharmony_ci#include <linux/of_mdio.h>
608c2ecf20Sopenharmony_ci#include <linux/of_net.h>
618c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
628c2ecf20Sopenharmony_ci#include <linux/if_vlan.h>
638c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h>
648c2ecf20Sopenharmony_ci#include <linux/prefetch.h>
658c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
668c2ecf20Sopenharmony_ci#include <linux/regmap.h>
678c2ecf20Sopenharmony_ci#include <soc/imx/cpuidle.h>
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci#include "fec.h"
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic void set_multicast_list(struct net_device *ndev);
748c2ecf20Sopenharmony_cistatic void fec_enet_itr_coal_init(struct net_device *ndev);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci#define DRIVER_NAME	"fec"
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic const u16 fec_enet_vlan_pri_to_queue[8] = {0, 0, 1, 1, 1, 2, 2, 2};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/* Pause frame feild and FIFO threshold */
818c2ecf20Sopenharmony_ci#define FEC_ENET_FCE	(1 << 5)
828c2ecf20Sopenharmony_ci#define FEC_ENET_RSEM_V	0x84
838c2ecf20Sopenharmony_ci#define FEC_ENET_RSFL_V	16
848c2ecf20Sopenharmony_ci#define FEC_ENET_RAEM_V	0x8
858c2ecf20Sopenharmony_ci#define FEC_ENET_RAFL_V	0x8
868c2ecf20Sopenharmony_ci#define FEC_ENET_OPD_V	0xFFF0
878c2ecf20Sopenharmony_ci#define FEC_MDIO_PM_TIMEOUT  100 /* ms */
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistruct fec_devinfo {
908c2ecf20Sopenharmony_ci	u32 quirks;
918c2ecf20Sopenharmony_ci};
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic const struct fec_devinfo fec_imx25_info = {
948c2ecf20Sopenharmony_ci	.quirks = FEC_QUIRK_USE_GASKET | FEC_QUIRK_MIB_CLEAR |
958c2ecf20Sopenharmony_ci		  FEC_QUIRK_HAS_FRREG,
968c2ecf20Sopenharmony_ci};
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic const struct fec_devinfo fec_imx27_info = {
998c2ecf20Sopenharmony_ci	.quirks = FEC_QUIRK_MIB_CLEAR | FEC_QUIRK_HAS_FRREG,
1008c2ecf20Sopenharmony_ci};
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic const struct fec_devinfo fec_imx28_info = {
1038c2ecf20Sopenharmony_ci	.quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME |
1048c2ecf20Sopenharmony_ci		  FEC_QUIRK_SINGLE_MDIO | FEC_QUIRK_HAS_RACC |
1058c2ecf20Sopenharmony_ci		  FEC_QUIRK_HAS_FRREG | FEC_QUIRK_CLEAR_SETUP_MII,
1068c2ecf20Sopenharmony_ci};
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic const struct fec_devinfo fec_imx6q_info = {
1098c2ecf20Sopenharmony_ci	.quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
1108c2ecf20Sopenharmony_ci		  FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
1118c2ecf20Sopenharmony_ci		  FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR006358 |
1128c2ecf20Sopenharmony_ci		  FEC_QUIRK_HAS_RACC | FEC_QUIRK_CLEAR_SETUP_MII,
1138c2ecf20Sopenharmony_ci};
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic const struct fec_devinfo fec_mvf600_info = {
1168c2ecf20Sopenharmony_ci	.quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_RACC,
1178c2ecf20Sopenharmony_ci};
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic const struct fec_devinfo fec_imx6x_info = {
1208c2ecf20Sopenharmony_ci	.quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
1218c2ecf20Sopenharmony_ci		  FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
1228c2ecf20Sopenharmony_ci		  FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB |
1238c2ecf20Sopenharmony_ci		  FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE |
1248c2ecf20Sopenharmony_ci		  FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE |
1258c2ecf20Sopenharmony_ci		  FEC_QUIRK_CLEAR_SETUP_MII,
1268c2ecf20Sopenharmony_ci};
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic const struct fec_devinfo fec_imx6ul_info = {
1298c2ecf20Sopenharmony_ci	.quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
1308c2ecf20Sopenharmony_ci		  FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
1318c2ecf20Sopenharmony_ci		  FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR007885 |
1328c2ecf20Sopenharmony_ci		  FEC_QUIRK_BUG_CAPTURE | FEC_QUIRK_HAS_RACC |
1338c2ecf20Sopenharmony_ci		  FEC_QUIRK_HAS_COALESCE | FEC_QUIRK_CLEAR_SETUP_MII,
1348c2ecf20Sopenharmony_ci};
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic struct platform_device_id fec_devtype[] = {
1378c2ecf20Sopenharmony_ci	{
1388c2ecf20Sopenharmony_ci		/* keep it for coldfire */
1398c2ecf20Sopenharmony_ci		.name = DRIVER_NAME,
1408c2ecf20Sopenharmony_ci		.driver_data = 0,
1418c2ecf20Sopenharmony_ci	}, {
1428c2ecf20Sopenharmony_ci		.name = "imx25-fec",
1438c2ecf20Sopenharmony_ci		.driver_data = (kernel_ulong_t)&fec_imx25_info,
1448c2ecf20Sopenharmony_ci	}, {
1458c2ecf20Sopenharmony_ci		.name = "imx27-fec",
1468c2ecf20Sopenharmony_ci		.driver_data = (kernel_ulong_t)&fec_imx27_info,
1478c2ecf20Sopenharmony_ci	}, {
1488c2ecf20Sopenharmony_ci		.name = "imx28-fec",
1498c2ecf20Sopenharmony_ci		.driver_data = (kernel_ulong_t)&fec_imx28_info,
1508c2ecf20Sopenharmony_ci	}, {
1518c2ecf20Sopenharmony_ci		.name = "imx6q-fec",
1528c2ecf20Sopenharmony_ci		.driver_data = (kernel_ulong_t)&fec_imx6q_info,
1538c2ecf20Sopenharmony_ci	}, {
1548c2ecf20Sopenharmony_ci		.name = "mvf600-fec",
1558c2ecf20Sopenharmony_ci		.driver_data = (kernel_ulong_t)&fec_mvf600_info,
1568c2ecf20Sopenharmony_ci	}, {
1578c2ecf20Sopenharmony_ci		.name = "imx6sx-fec",
1588c2ecf20Sopenharmony_ci		.driver_data = (kernel_ulong_t)&fec_imx6x_info,
1598c2ecf20Sopenharmony_ci	}, {
1608c2ecf20Sopenharmony_ci		.name = "imx6ul-fec",
1618c2ecf20Sopenharmony_ci		.driver_data = (kernel_ulong_t)&fec_imx6ul_info,
1628c2ecf20Sopenharmony_ci	}, {
1638c2ecf20Sopenharmony_ci		/* sentinel */
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci};
1668c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, fec_devtype);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cienum imx_fec_type {
1698c2ecf20Sopenharmony_ci	IMX25_FEC = 1,	/* runs on i.mx25/50/53 */
1708c2ecf20Sopenharmony_ci	IMX27_FEC,	/* runs on i.mx27/35/51 */
1718c2ecf20Sopenharmony_ci	IMX28_FEC,
1728c2ecf20Sopenharmony_ci	IMX6Q_FEC,
1738c2ecf20Sopenharmony_ci	MVF600_FEC,
1748c2ecf20Sopenharmony_ci	IMX6SX_FEC,
1758c2ecf20Sopenharmony_ci	IMX6UL_FEC,
1768c2ecf20Sopenharmony_ci};
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic const struct of_device_id fec_dt_ids[] = {
1798c2ecf20Sopenharmony_ci	{ .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], },
1808c2ecf20Sopenharmony_ci	{ .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], },
1818c2ecf20Sopenharmony_ci	{ .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], },
1828c2ecf20Sopenharmony_ci	{ .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], },
1838c2ecf20Sopenharmony_ci	{ .compatible = "fsl,mvf600-fec", .data = &fec_devtype[MVF600_FEC], },
1848c2ecf20Sopenharmony_ci	{ .compatible = "fsl,imx6sx-fec", .data = &fec_devtype[IMX6SX_FEC], },
1858c2ecf20Sopenharmony_ci	{ .compatible = "fsl,imx6ul-fec", .data = &fec_devtype[IMX6UL_FEC], },
1868c2ecf20Sopenharmony_ci	{ /* sentinel */ }
1878c2ecf20Sopenharmony_ci};
1888c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, fec_dt_ids);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic unsigned char macaddr[ETH_ALEN];
1918c2ecf20Sopenharmony_cimodule_param_array(macaddr, byte, NULL, 0);
1928c2ecf20Sopenharmony_ciMODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci#if defined(CONFIG_M5272)
1958c2ecf20Sopenharmony_ci/*
1968c2ecf20Sopenharmony_ci * Some hardware gets it MAC address out of local flash memory.
1978c2ecf20Sopenharmony_ci * if this is non-zero then assume it is the address to get MAC from.
1988c2ecf20Sopenharmony_ci */
1998c2ecf20Sopenharmony_ci#if defined(CONFIG_NETtel)
2008c2ecf20Sopenharmony_ci#define	FEC_FLASHMAC	0xf0006006
2018c2ecf20Sopenharmony_ci#elif defined(CONFIG_GILBARCONAP) || defined(CONFIG_SCALES)
2028c2ecf20Sopenharmony_ci#define	FEC_FLASHMAC	0xf0006000
2038c2ecf20Sopenharmony_ci#elif defined(CONFIG_CANCam)
2048c2ecf20Sopenharmony_ci#define	FEC_FLASHMAC	0xf0020000
2058c2ecf20Sopenharmony_ci#elif defined (CONFIG_M5272C3)
2068c2ecf20Sopenharmony_ci#define	FEC_FLASHMAC	(0xffe04000 + 4)
2078c2ecf20Sopenharmony_ci#elif defined(CONFIG_MOD5272)
2088c2ecf20Sopenharmony_ci#define FEC_FLASHMAC	0xffc0406b
2098c2ecf20Sopenharmony_ci#else
2108c2ecf20Sopenharmony_ci#define	FEC_FLASHMAC	0
2118c2ecf20Sopenharmony_ci#endif
2128c2ecf20Sopenharmony_ci#endif /* CONFIG_M5272 */
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci/* The FEC stores dest/src/type/vlan, data, and checksum for receive packets.
2158c2ecf20Sopenharmony_ci *
2168c2ecf20Sopenharmony_ci * 2048 byte skbufs are allocated. However, alignment requirements
2178c2ecf20Sopenharmony_ci * varies between FEC variants. Worst case is 64, so round down by 64.
2188c2ecf20Sopenharmony_ci */
2198c2ecf20Sopenharmony_ci#define PKT_MAXBUF_SIZE		(round_down(2048 - 64, 64))
2208c2ecf20Sopenharmony_ci#define PKT_MINBUF_SIZE		64
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci/* FEC receive acceleration */
2238c2ecf20Sopenharmony_ci#define FEC_RACC_IPDIS		(1 << 1)
2248c2ecf20Sopenharmony_ci#define FEC_RACC_PRODIS		(1 << 2)
2258c2ecf20Sopenharmony_ci#define FEC_RACC_SHIFT16	BIT(7)
2268c2ecf20Sopenharmony_ci#define FEC_RACC_OPTIONS	(FEC_RACC_IPDIS | FEC_RACC_PRODIS)
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci/* MIB Control Register */
2298c2ecf20Sopenharmony_ci#define FEC_MIB_CTRLSTAT_DISABLE	BIT(31)
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci/*
2328c2ecf20Sopenharmony_ci * The 5270/5271/5280/5282/532x RX control register also contains maximum frame
2338c2ecf20Sopenharmony_ci * size bits. Other FEC hardware does not, so we need to take that into
2348c2ecf20Sopenharmony_ci * account when setting it.
2358c2ecf20Sopenharmony_ci */
2368c2ecf20Sopenharmony_ci#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
2378c2ecf20Sopenharmony_ci    defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \
2388c2ecf20Sopenharmony_ci    defined(CONFIG_ARM64)
2398c2ecf20Sopenharmony_ci#define	OPT_FRAME_SIZE	(PKT_MAXBUF_SIZE << 16)
2408c2ecf20Sopenharmony_ci#else
2418c2ecf20Sopenharmony_ci#define	OPT_FRAME_SIZE	0
2428c2ecf20Sopenharmony_ci#endif
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci/* FEC MII MMFR bits definition */
2458c2ecf20Sopenharmony_ci#define FEC_MMFR_ST		(1 << 30)
2468c2ecf20Sopenharmony_ci#define FEC_MMFR_ST_C45		(0)
2478c2ecf20Sopenharmony_ci#define FEC_MMFR_OP_READ	(2 << 28)
2488c2ecf20Sopenharmony_ci#define FEC_MMFR_OP_READ_C45	(3 << 28)
2498c2ecf20Sopenharmony_ci#define FEC_MMFR_OP_WRITE	(1 << 28)
2508c2ecf20Sopenharmony_ci#define FEC_MMFR_OP_ADDR_WRITE	(0)
2518c2ecf20Sopenharmony_ci#define FEC_MMFR_PA(v)		((v & 0x1f) << 23)
2528c2ecf20Sopenharmony_ci#define FEC_MMFR_RA(v)		((v & 0x1f) << 18)
2538c2ecf20Sopenharmony_ci#define FEC_MMFR_TA		(2 << 16)
2548c2ecf20Sopenharmony_ci#define FEC_MMFR_DATA(v)	(v & 0xffff)
2558c2ecf20Sopenharmony_ci/* FEC ECR bits definition */
2568c2ecf20Sopenharmony_ci#define FEC_ECR_MAGICEN		(1 << 2)
2578c2ecf20Sopenharmony_ci#define FEC_ECR_SLEEP		(1 << 3)
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci#define FEC_MII_TIMEOUT		30000 /* us */
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci/* Transmitter timeout */
2628c2ecf20Sopenharmony_ci#define TX_TIMEOUT (2 * HZ)
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci#define FEC_PAUSE_FLAG_AUTONEG	0x1
2658c2ecf20Sopenharmony_ci#define FEC_PAUSE_FLAG_ENABLE	0x2
2668c2ecf20Sopenharmony_ci#define FEC_WOL_HAS_MAGIC_PACKET	(0x1 << 0)
2678c2ecf20Sopenharmony_ci#define FEC_WOL_FLAG_ENABLE		(0x1 << 1)
2688c2ecf20Sopenharmony_ci#define FEC_WOL_FLAG_SLEEP_ON		(0x1 << 2)
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci#define COPYBREAK_DEFAULT	256
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci/* Max number of allowed TCP segments for software TSO */
2738c2ecf20Sopenharmony_ci#define FEC_MAX_TSO_SEGS	100
2748c2ecf20Sopenharmony_ci#define FEC_MAX_SKB_DESCS	(FEC_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS)
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci#define IS_TSO_HEADER(txq, addr) \
2778c2ecf20Sopenharmony_ci	((addr >= txq->tso_hdrs_dma) && \
2788c2ecf20Sopenharmony_ci	(addr < txq->tso_hdrs_dma + txq->bd.ring_size * TSO_HEADER_SIZE))
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_cistatic int mii_cnt;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp,
2838c2ecf20Sopenharmony_ci					     struct bufdesc_prop *bd)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	return (bdp >= bd->last) ? bd->base
2868c2ecf20Sopenharmony_ci			: (struct bufdesc *)(((void *)bdp) + bd->dsize);
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cistatic struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp,
2908c2ecf20Sopenharmony_ci					     struct bufdesc_prop *bd)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	return (bdp <= bd->base) ? bd->last
2938c2ecf20Sopenharmony_ci			: (struct bufdesc *)(((void *)bdp) - bd->dsize);
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistatic int fec_enet_get_bd_index(struct bufdesc *bdp,
2978c2ecf20Sopenharmony_ci				 struct bufdesc_prop *bd)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	return ((const char *)bdp - (const char *)bd->base) >> bd->dsize_log2;
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic int fec_enet_get_free_txdesc_num(struct fec_enet_priv_tx_q *txq)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	int entries;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	entries = (((const char *)txq->dirty_tx -
3078c2ecf20Sopenharmony_ci			(const char *)txq->bd.cur) >> txq->bd.dsize_log2) - 1;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	return entries >= 0 ? entries : entries + txq->bd.ring_size;
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic void swap_buffer(void *bufaddr, int len)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	int i;
3158c2ecf20Sopenharmony_ci	unsigned int *buf = bufaddr;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	for (i = 0; i < len; i += 4, buf++)
3188c2ecf20Sopenharmony_ci		swab32s(buf);
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic void swap_buffer2(void *dst_buf, void *src_buf, int len)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	int i;
3248c2ecf20Sopenharmony_ci	unsigned int *src = src_buf;
3258c2ecf20Sopenharmony_ci	unsigned int *dst = dst_buf;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	for (i = 0; i < len; i += 4, src++, dst++)
3288c2ecf20Sopenharmony_ci		*dst = swab32p(src);
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_cistatic void fec_dump(struct net_device *ndev)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
3348c2ecf20Sopenharmony_ci	struct bufdesc *bdp;
3358c2ecf20Sopenharmony_ci	struct fec_enet_priv_tx_q *txq;
3368c2ecf20Sopenharmony_ci	int index = 0;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	netdev_info(ndev, "TX ring dump\n");
3398c2ecf20Sopenharmony_ci	pr_info("Nr     SC     addr       len  SKB\n");
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	txq = fep->tx_queue[0];
3428c2ecf20Sopenharmony_ci	bdp = txq->bd.base;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	do {
3458c2ecf20Sopenharmony_ci		pr_info("%3u %c%c 0x%04x 0x%08x %4u %p\n",
3468c2ecf20Sopenharmony_ci			index,
3478c2ecf20Sopenharmony_ci			bdp == txq->bd.cur ? 'S' : ' ',
3488c2ecf20Sopenharmony_ci			bdp == txq->dirty_tx ? 'H' : ' ',
3498c2ecf20Sopenharmony_ci			fec16_to_cpu(bdp->cbd_sc),
3508c2ecf20Sopenharmony_ci			fec32_to_cpu(bdp->cbd_bufaddr),
3518c2ecf20Sopenharmony_ci			fec16_to_cpu(bdp->cbd_datlen),
3528c2ecf20Sopenharmony_ci			txq->tx_skbuff[index]);
3538c2ecf20Sopenharmony_ci		bdp = fec_enet_get_nextdesc(bdp, &txq->bd);
3548c2ecf20Sopenharmony_ci		index++;
3558c2ecf20Sopenharmony_ci	} while (bdp != txq->bd.base);
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic inline bool is_ipv4_pkt(struct sk_buff *skb)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	return skb->protocol == htons(ETH_P_IP) && ip_hdr(skb)->version == 4;
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cistatic int
3648c2ecf20Sopenharmony_cifec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev)
3658c2ecf20Sopenharmony_ci{
3668c2ecf20Sopenharmony_ci	/* Only run for packets requiring a checksum. */
3678c2ecf20Sopenharmony_ci	if (skb->ip_summed != CHECKSUM_PARTIAL)
3688c2ecf20Sopenharmony_ci		return 0;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	if (unlikely(skb_cow_head(skb, 0)))
3718c2ecf20Sopenharmony_ci		return -1;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	if (is_ipv4_pkt(skb))
3748c2ecf20Sopenharmony_ci		ip_hdr(skb)->check = 0;
3758c2ecf20Sopenharmony_ci	*(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) = 0;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	return 0;
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic struct bufdesc *
3818c2ecf20Sopenharmony_cifec_enet_txq_submit_frag_skb(struct fec_enet_priv_tx_q *txq,
3828c2ecf20Sopenharmony_ci			     struct sk_buff *skb,
3838c2ecf20Sopenharmony_ci			     struct net_device *ndev)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
3868c2ecf20Sopenharmony_ci	struct bufdesc *bdp = txq->bd.cur;
3878c2ecf20Sopenharmony_ci	struct bufdesc_ex *ebdp;
3888c2ecf20Sopenharmony_ci	int nr_frags = skb_shinfo(skb)->nr_frags;
3898c2ecf20Sopenharmony_ci	int frag, frag_len;
3908c2ecf20Sopenharmony_ci	unsigned short status;
3918c2ecf20Sopenharmony_ci	unsigned int estatus = 0;
3928c2ecf20Sopenharmony_ci	skb_frag_t *this_frag;
3938c2ecf20Sopenharmony_ci	unsigned int index;
3948c2ecf20Sopenharmony_ci	void *bufaddr;
3958c2ecf20Sopenharmony_ci	dma_addr_t addr;
3968c2ecf20Sopenharmony_ci	int i;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	for (frag = 0; frag < nr_frags; frag++) {
3998c2ecf20Sopenharmony_ci		this_frag = &skb_shinfo(skb)->frags[frag];
4008c2ecf20Sopenharmony_ci		bdp = fec_enet_get_nextdesc(bdp, &txq->bd);
4018c2ecf20Sopenharmony_ci		ebdp = (struct bufdesc_ex *)bdp;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci		status = fec16_to_cpu(bdp->cbd_sc);
4048c2ecf20Sopenharmony_ci		status &= ~BD_ENET_TX_STATS;
4058c2ecf20Sopenharmony_ci		status |= (BD_ENET_TX_TC | BD_ENET_TX_READY);
4068c2ecf20Sopenharmony_ci		frag_len = skb_frag_size(&skb_shinfo(skb)->frags[frag]);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci		/* Handle the last BD specially */
4098c2ecf20Sopenharmony_ci		if (frag == nr_frags - 1) {
4108c2ecf20Sopenharmony_ci			status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST);
4118c2ecf20Sopenharmony_ci			if (fep->bufdesc_ex) {
4128c2ecf20Sopenharmony_ci				estatus |= BD_ENET_TX_INT;
4138c2ecf20Sopenharmony_ci				if (unlikely(skb_shinfo(skb)->tx_flags &
4148c2ecf20Sopenharmony_ci					SKBTX_HW_TSTAMP && fep->hwts_tx_en))
4158c2ecf20Sopenharmony_ci					estatus |= BD_ENET_TX_TS;
4168c2ecf20Sopenharmony_ci			}
4178c2ecf20Sopenharmony_ci		}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci		if (fep->bufdesc_ex) {
4208c2ecf20Sopenharmony_ci			if (fep->quirks & FEC_QUIRK_HAS_AVB)
4218c2ecf20Sopenharmony_ci				estatus |= FEC_TX_BD_FTYPE(txq->bd.qid);
4228c2ecf20Sopenharmony_ci			if (skb->ip_summed == CHECKSUM_PARTIAL)
4238c2ecf20Sopenharmony_ci				estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS;
4248c2ecf20Sopenharmony_ci			ebdp->cbd_bdu = 0;
4258c2ecf20Sopenharmony_ci			ebdp->cbd_esc = cpu_to_fec32(estatus);
4268c2ecf20Sopenharmony_ci		}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci		bufaddr = skb_frag_address(this_frag);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci		index = fec_enet_get_bd_index(bdp, &txq->bd);
4318c2ecf20Sopenharmony_ci		if (((unsigned long) bufaddr) & fep->tx_align ||
4328c2ecf20Sopenharmony_ci			fep->quirks & FEC_QUIRK_SWAP_FRAME) {
4338c2ecf20Sopenharmony_ci			memcpy(txq->tx_bounce[index], bufaddr, frag_len);
4348c2ecf20Sopenharmony_ci			bufaddr = txq->tx_bounce[index];
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci			if (fep->quirks & FEC_QUIRK_SWAP_FRAME)
4378c2ecf20Sopenharmony_ci				swap_buffer(bufaddr, frag_len);
4388c2ecf20Sopenharmony_ci		}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci		addr = dma_map_single(&fep->pdev->dev, bufaddr, frag_len,
4418c2ecf20Sopenharmony_ci				      DMA_TO_DEVICE);
4428c2ecf20Sopenharmony_ci		if (dma_mapping_error(&fep->pdev->dev, addr)) {
4438c2ecf20Sopenharmony_ci			if (net_ratelimit())
4448c2ecf20Sopenharmony_ci				netdev_err(ndev, "Tx DMA memory map failed\n");
4458c2ecf20Sopenharmony_ci			goto dma_mapping_error;
4468c2ecf20Sopenharmony_ci		}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci		bdp->cbd_bufaddr = cpu_to_fec32(addr);
4498c2ecf20Sopenharmony_ci		bdp->cbd_datlen = cpu_to_fec16(frag_len);
4508c2ecf20Sopenharmony_ci		/* Make sure the updates to rest of the descriptor are
4518c2ecf20Sopenharmony_ci		 * performed before transferring ownership.
4528c2ecf20Sopenharmony_ci		 */
4538c2ecf20Sopenharmony_ci		wmb();
4548c2ecf20Sopenharmony_ci		bdp->cbd_sc = cpu_to_fec16(status);
4558c2ecf20Sopenharmony_ci	}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	return bdp;
4588c2ecf20Sopenharmony_cidma_mapping_error:
4598c2ecf20Sopenharmony_ci	bdp = txq->bd.cur;
4608c2ecf20Sopenharmony_ci	for (i = 0; i < frag; i++) {
4618c2ecf20Sopenharmony_ci		bdp = fec_enet_get_nextdesc(bdp, &txq->bd);
4628c2ecf20Sopenharmony_ci		dma_unmap_single(&fep->pdev->dev, fec32_to_cpu(bdp->cbd_bufaddr),
4638c2ecf20Sopenharmony_ci				 fec16_to_cpu(bdp->cbd_datlen), DMA_TO_DEVICE);
4648c2ecf20Sopenharmony_ci	}
4658c2ecf20Sopenharmony_ci	return ERR_PTR(-ENOMEM);
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cistatic int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq,
4698c2ecf20Sopenharmony_ci				   struct sk_buff *skb, struct net_device *ndev)
4708c2ecf20Sopenharmony_ci{
4718c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
4728c2ecf20Sopenharmony_ci	int nr_frags = skb_shinfo(skb)->nr_frags;
4738c2ecf20Sopenharmony_ci	struct bufdesc *bdp, *last_bdp;
4748c2ecf20Sopenharmony_ci	void *bufaddr;
4758c2ecf20Sopenharmony_ci	dma_addr_t addr;
4768c2ecf20Sopenharmony_ci	unsigned short status;
4778c2ecf20Sopenharmony_ci	unsigned short buflen;
4788c2ecf20Sopenharmony_ci	unsigned int estatus = 0;
4798c2ecf20Sopenharmony_ci	unsigned int index;
4808c2ecf20Sopenharmony_ci	int entries_free;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	entries_free = fec_enet_get_free_txdesc_num(txq);
4838c2ecf20Sopenharmony_ci	if (entries_free < MAX_SKB_FRAGS + 1) {
4848c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
4858c2ecf20Sopenharmony_ci		if (net_ratelimit())
4868c2ecf20Sopenharmony_ci			netdev_err(ndev, "NOT enough BD for SG!\n");
4878c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	/* Protocol checksum off-load for TCP and UDP. */
4918c2ecf20Sopenharmony_ci	if (fec_enet_clear_csum(skb, ndev)) {
4928c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
4938c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
4948c2ecf20Sopenharmony_ci	}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	/* Fill in a Tx ring entry */
4978c2ecf20Sopenharmony_ci	bdp = txq->bd.cur;
4988c2ecf20Sopenharmony_ci	last_bdp = bdp;
4998c2ecf20Sopenharmony_ci	status = fec16_to_cpu(bdp->cbd_sc);
5008c2ecf20Sopenharmony_ci	status &= ~BD_ENET_TX_STATS;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	/* Set buffer length and buffer pointer */
5038c2ecf20Sopenharmony_ci	bufaddr = skb->data;
5048c2ecf20Sopenharmony_ci	buflen = skb_headlen(skb);
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	index = fec_enet_get_bd_index(bdp, &txq->bd);
5078c2ecf20Sopenharmony_ci	if (((unsigned long) bufaddr) & fep->tx_align ||
5088c2ecf20Sopenharmony_ci		fep->quirks & FEC_QUIRK_SWAP_FRAME) {
5098c2ecf20Sopenharmony_ci		memcpy(txq->tx_bounce[index], skb->data, buflen);
5108c2ecf20Sopenharmony_ci		bufaddr = txq->tx_bounce[index];
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci		if (fep->quirks & FEC_QUIRK_SWAP_FRAME)
5138c2ecf20Sopenharmony_ci			swap_buffer(bufaddr, buflen);
5148c2ecf20Sopenharmony_ci	}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	/* Push the data cache so the CPM does not get stale memory data. */
5178c2ecf20Sopenharmony_ci	addr = dma_map_single(&fep->pdev->dev, bufaddr, buflen, DMA_TO_DEVICE);
5188c2ecf20Sopenharmony_ci	if (dma_mapping_error(&fep->pdev->dev, addr)) {
5198c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
5208c2ecf20Sopenharmony_ci		if (net_ratelimit())
5218c2ecf20Sopenharmony_ci			netdev_err(ndev, "Tx DMA memory map failed\n");
5228c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
5238c2ecf20Sopenharmony_ci	}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	if (nr_frags) {
5268c2ecf20Sopenharmony_ci		last_bdp = fec_enet_txq_submit_frag_skb(txq, skb, ndev);
5278c2ecf20Sopenharmony_ci		if (IS_ERR(last_bdp)) {
5288c2ecf20Sopenharmony_ci			dma_unmap_single(&fep->pdev->dev, addr,
5298c2ecf20Sopenharmony_ci					 buflen, DMA_TO_DEVICE);
5308c2ecf20Sopenharmony_ci			dev_kfree_skb_any(skb);
5318c2ecf20Sopenharmony_ci			return NETDEV_TX_OK;
5328c2ecf20Sopenharmony_ci		}
5338c2ecf20Sopenharmony_ci	} else {
5348c2ecf20Sopenharmony_ci		status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST);
5358c2ecf20Sopenharmony_ci		if (fep->bufdesc_ex) {
5368c2ecf20Sopenharmony_ci			estatus = BD_ENET_TX_INT;
5378c2ecf20Sopenharmony_ci			if (unlikely(skb_shinfo(skb)->tx_flags &
5388c2ecf20Sopenharmony_ci				SKBTX_HW_TSTAMP && fep->hwts_tx_en))
5398c2ecf20Sopenharmony_ci				estatus |= BD_ENET_TX_TS;
5408c2ecf20Sopenharmony_ci		}
5418c2ecf20Sopenharmony_ci	}
5428c2ecf20Sopenharmony_ci	bdp->cbd_bufaddr = cpu_to_fec32(addr);
5438c2ecf20Sopenharmony_ci	bdp->cbd_datlen = cpu_to_fec16(buflen);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	if (fep->bufdesc_ex) {
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci		struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci		if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
5508c2ecf20Sopenharmony_ci			fep->hwts_tx_en))
5518c2ecf20Sopenharmony_ci			skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci		if (fep->quirks & FEC_QUIRK_HAS_AVB)
5548c2ecf20Sopenharmony_ci			estatus |= FEC_TX_BD_FTYPE(txq->bd.qid);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci		if (skb->ip_summed == CHECKSUM_PARTIAL)
5578c2ecf20Sopenharmony_ci			estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci		ebdp->cbd_bdu = 0;
5608c2ecf20Sopenharmony_ci		ebdp->cbd_esc = cpu_to_fec32(estatus);
5618c2ecf20Sopenharmony_ci	}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	index = fec_enet_get_bd_index(last_bdp, &txq->bd);
5648c2ecf20Sopenharmony_ci	/* Save skb pointer */
5658c2ecf20Sopenharmony_ci	txq->tx_skbuff[index] = skb;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	/* Make sure the updates to rest of the descriptor are performed before
5688c2ecf20Sopenharmony_ci	 * transferring ownership.
5698c2ecf20Sopenharmony_ci	 */
5708c2ecf20Sopenharmony_ci	wmb();
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	/* Send it on its way.  Tell FEC it's ready, interrupt when done,
5738c2ecf20Sopenharmony_ci	 * it's the last BD of the frame, and to put the CRC on the end.
5748c2ecf20Sopenharmony_ci	 */
5758c2ecf20Sopenharmony_ci	status |= (BD_ENET_TX_READY | BD_ENET_TX_TC);
5768c2ecf20Sopenharmony_ci	bdp->cbd_sc = cpu_to_fec16(status);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	/* If this was the last BD in the ring, start at the beginning again. */
5798c2ecf20Sopenharmony_ci	bdp = fec_enet_get_nextdesc(last_bdp, &txq->bd);
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	skb_tx_timestamp(skb);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	/* Make sure the update to bdp and tx_skbuff are performed before
5848c2ecf20Sopenharmony_ci	 * txq->bd.cur.
5858c2ecf20Sopenharmony_ci	 */
5868c2ecf20Sopenharmony_ci	wmb();
5878c2ecf20Sopenharmony_ci	txq->bd.cur = bdp;
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	/* Trigger transmission start */
5908c2ecf20Sopenharmony_ci	writel(0, txq->bd.reg_desc_active);
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	return 0;
5938c2ecf20Sopenharmony_ci}
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_cistatic int
5968c2ecf20Sopenharmony_cifec_enet_txq_put_data_tso(struct fec_enet_priv_tx_q *txq, struct sk_buff *skb,
5978c2ecf20Sopenharmony_ci			  struct net_device *ndev,
5988c2ecf20Sopenharmony_ci			  struct bufdesc *bdp, int index, char *data,
5998c2ecf20Sopenharmony_ci			  int size, bool last_tcp, bool is_last)
6008c2ecf20Sopenharmony_ci{
6018c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
6028c2ecf20Sopenharmony_ci	struct bufdesc_ex *ebdp = container_of(bdp, struct bufdesc_ex, desc);
6038c2ecf20Sopenharmony_ci	unsigned short status;
6048c2ecf20Sopenharmony_ci	unsigned int estatus = 0;
6058c2ecf20Sopenharmony_ci	dma_addr_t addr;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	status = fec16_to_cpu(bdp->cbd_sc);
6088c2ecf20Sopenharmony_ci	status &= ~BD_ENET_TX_STATS;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	status |= (BD_ENET_TX_TC | BD_ENET_TX_READY);
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	if (((unsigned long) data) & fep->tx_align ||
6138c2ecf20Sopenharmony_ci		fep->quirks & FEC_QUIRK_SWAP_FRAME) {
6148c2ecf20Sopenharmony_ci		memcpy(txq->tx_bounce[index], data, size);
6158c2ecf20Sopenharmony_ci		data = txq->tx_bounce[index];
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci		if (fep->quirks & FEC_QUIRK_SWAP_FRAME)
6188c2ecf20Sopenharmony_ci			swap_buffer(data, size);
6198c2ecf20Sopenharmony_ci	}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	addr = dma_map_single(&fep->pdev->dev, data, size, DMA_TO_DEVICE);
6228c2ecf20Sopenharmony_ci	if (dma_mapping_error(&fep->pdev->dev, addr)) {
6238c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
6248c2ecf20Sopenharmony_ci		if (net_ratelimit())
6258c2ecf20Sopenharmony_ci			netdev_err(ndev, "Tx DMA memory map failed\n");
6268c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
6278c2ecf20Sopenharmony_ci	}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	bdp->cbd_datlen = cpu_to_fec16(size);
6308c2ecf20Sopenharmony_ci	bdp->cbd_bufaddr = cpu_to_fec32(addr);
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	if (fep->bufdesc_ex) {
6338c2ecf20Sopenharmony_ci		if (fep->quirks & FEC_QUIRK_HAS_AVB)
6348c2ecf20Sopenharmony_ci			estatus |= FEC_TX_BD_FTYPE(txq->bd.qid);
6358c2ecf20Sopenharmony_ci		if (skb->ip_summed == CHECKSUM_PARTIAL)
6368c2ecf20Sopenharmony_ci			estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS;
6378c2ecf20Sopenharmony_ci		ebdp->cbd_bdu = 0;
6388c2ecf20Sopenharmony_ci		ebdp->cbd_esc = cpu_to_fec32(estatus);
6398c2ecf20Sopenharmony_ci	}
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	/* Handle the last BD specially */
6428c2ecf20Sopenharmony_ci	if (last_tcp)
6438c2ecf20Sopenharmony_ci		status |= (BD_ENET_TX_LAST | BD_ENET_TX_TC);
6448c2ecf20Sopenharmony_ci	if (is_last) {
6458c2ecf20Sopenharmony_ci		status |= BD_ENET_TX_INTR;
6468c2ecf20Sopenharmony_ci		if (fep->bufdesc_ex)
6478c2ecf20Sopenharmony_ci			ebdp->cbd_esc |= cpu_to_fec32(BD_ENET_TX_INT);
6488c2ecf20Sopenharmony_ci	}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	bdp->cbd_sc = cpu_to_fec16(status);
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	return 0;
6538c2ecf20Sopenharmony_ci}
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_cistatic int
6568c2ecf20Sopenharmony_cifec_enet_txq_put_hdr_tso(struct fec_enet_priv_tx_q *txq,
6578c2ecf20Sopenharmony_ci			 struct sk_buff *skb, struct net_device *ndev,
6588c2ecf20Sopenharmony_ci			 struct bufdesc *bdp, int index)
6598c2ecf20Sopenharmony_ci{
6608c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
6618c2ecf20Sopenharmony_ci	int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
6628c2ecf20Sopenharmony_ci	struct bufdesc_ex *ebdp = container_of(bdp, struct bufdesc_ex, desc);
6638c2ecf20Sopenharmony_ci	void *bufaddr;
6648c2ecf20Sopenharmony_ci	unsigned long dmabuf;
6658c2ecf20Sopenharmony_ci	unsigned short status;
6668c2ecf20Sopenharmony_ci	unsigned int estatus = 0;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	status = fec16_to_cpu(bdp->cbd_sc);
6698c2ecf20Sopenharmony_ci	status &= ~BD_ENET_TX_STATS;
6708c2ecf20Sopenharmony_ci	status |= (BD_ENET_TX_TC | BD_ENET_TX_READY);
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	bufaddr = txq->tso_hdrs + index * TSO_HEADER_SIZE;
6738c2ecf20Sopenharmony_ci	dmabuf = txq->tso_hdrs_dma + index * TSO_HEADER_SIZE;
6748c2ecf20Sopenharmony_ci	if (((unsigned long)bufaddr) & fep->tx_align ||
6758c2ecf20Sopenharmony_ci		fep->quirks & FEC_QUIRK_SWAP_FRAME) {
6768c2ecf20Sopenharmony_ci		memcpy(txq->tx_bounce[index], skb->data, hdr_len);
6778c2ecf20Sopenharmony_ci		bufaddr = txq->tx_bounce[index];
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci		if (fep->quirks & FEC_QUIRK_SWAP_FRAME)
6808c2ecf20Sopenharmony_ci			swap_buffer(bufaddr, hdr_len);
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci		dmabuf = dma_map_single(&fep->pdev->dev, bufaddr,
6838c2ecf20Sopenharmony_ci					hdr_len, DMA_TO_DEVICE);
6848c2ecf20Sopenharmony_ci		if (dma_mapping_error(&fep->pdev->dev, dmabuf)) {
6858c2ecf20Sopenharmony_ci			dev_kfree_skb_any(skb);
6868c2ecf20Sopenharmony_ci			if (net_ratelimit())
6878c2ecf20Sopenharmony_ci				netdev_err(ndev, "Tx DMA memory map failed\n");
6888c2ecf20Sopenharmony_ci			return NETDEV_TX_OK;
6898c2ecf20Sopenharmony_ci		}
6908c2ecf20Sopenharmony_ci	}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	bdp->cbd_bufaddr = cpu_to_fec32(dmabuf);
6938c2ecf20Sopenharmony_ci	bdp->cbd_datlen = cpu_to_fec16(hdr_len);
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	if (fep->bufdesc_ex) {
6968c2ecf20Sopenharmony_ci		if (fep->quirks & FEC_QUIRK_HAS_AVB)
6978c2ecf20Sopenharmony_ci			estatus |= FEC_TX_BD_FTYPE(txq->bd.qid);
6988c2ecf20Sopenharmony_ci		if (skb->ip_summed == CHECKSUM_PARTIAL)
6998c2ecf20Sopenharmony_ci			estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS;
7008c2ecf20Sopenharmony_ci		ebdp->cbd_bdu = 0;
7018c2ecf20Sopenharmony_ci		ebdp->cbd_esc = cpu_to_fec32(estatus);
7028c2ecf20Sopenharmony_ci	}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	bdp->cbd_sc = cpu_to_fec16(status);
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	return 0;
7078c2ecf20Sopenharmony_ci}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_cistatic int fec_enet_txq_submit_tso(struct fec_enet_priv_tx_q *txq,
7108c2ecf20Sopenharmony_ci				   struct sk_buff *skb,
7118c2ecf20Sopenharmony_ci				   struct net_device *ndev)
7128c2ecf20Sopenharmony_ci{
7138c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
7148c2ecf20Sopenharmony_ci	int hdr_len, total_len, data_left;
7158c2ecf20Sopenharmony_ci	struct bufdesc *bdp = txq->bd.cur;
7168c2ecf20Sopenharmony_ci	struct tso_t tso;
7178c2ecf20Sopenharmony_ci	unsigned int index = 0;
7188c2ecf20Sopenharmony_ci	int ret;
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	if (tso_count_descs(skb) >= fec_enet_get_free_txdesc_num(txq)) {
7218c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
7228c2ecf20Sopenharmony_ci		if (net_ratelimit())
7238c2ecf20Sopenharmony_ci			netdev_err(ndev, "NOT enough BD for TSO!\n");
7248c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
7258c2ecf20Sopenharmony_ci	}
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	/* Protocol checksum off-load for TCP and UDP. */
7288c2ecf20Sopenharmony_ci	if (fec_enet_clear_csum(skb, ndev)) {
7298c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
7308c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
7318c2ecf20Sopenharmony_ci	}
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	/* Initialize the TSO handler, and prepare the first payload */
7348c2ecf20Sopenharmony_ci	hdr_len = tso_start(skb, &tso);
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	total_len = skb->len - hdr_len;
7378c2ecf20Sopenharmony_ci	while (total_len > 0) {
7388c2ecf20Sopenharmony_ci		char *hdr;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci		index = fec_enet_get_bd_index(bdp, &txq->bd);
7418c2ecf20Sopenharmony_ci		data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len);
7428c2ecf20Sopenharmony_ci		total_len -= data_left;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci		/* prepare packet headers: MAC + IP + TCP */
7458c2ecf20Sopenharmony_ci		hdr = txq->tso_hdrs + index * TSO_HEADER_SIZE;
7468c2ecf20Sopenharmony_ci		tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0);
7478c2ecf20Sopenharmony_ci		ret = fec_enet_txq_put_hdr_tso(txq, skb, ndev, bdp, index);
7488c2ecf20Sopenharmony_ci		if (ret)
7498c2ecf20Sopenharmony_ci			goto err_release;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci		while (data_left > 0) {
7528c2ecf20Sopenharmony_ci			int size;
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci			size = min_t(int, tso.size, data_left);
7558c2ecf20Sopenharmony_ci			bdp = fec_enet_get_nextdesc(bdp, &txq->bd);
7568c2ecf20Sopenharmony_ci			index = fec_enet_get_bd_index(bdp, &txq->bd);
7578c2ecf20Sopenharmony_ci			ret = fec_enet_txq_put_data_tso(txq, skb, ndev,
7588c2ecf20Sopenharmony_ci							bdp, index,
7598c2ecf20Sopenharmony_ci							tso.data, size,
7608c2ecf20Sopenharmony_ci							size == data_left,
7618c2ecf20Sopenharmony_ci							total_len == 0);
7628c2ecf20Sopenharmony_ci			if (ret)
7638c2ecf20Sopenharmony_ci				goto err_release;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci			data_left -= size;
7668c2ecf20Sopenharmony_ci			tso_build_data(skb, &tso, size);
7678c2ecf20Sopenharmony_ci		}
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci		bdp = fec_enet_get_nextdesc(bdp, &txq->bd);
7708c2ecf20Sopenharmony_ci	}
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	/* Save skb pointer */
7738c2ecf20Sopenharmony_ci	txq->tx_skbuff[index] = skb;
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	skb_tx_timestamp(skb);
7768c2ecf20Sopenharmony_ci	txq->bd.cur = bdp;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	/* Trigger transmission start */
7798c2ecf20Sopenharmony_ci	if (!(fep->quirks & FEC_QUIRK_ERR007885) ||
7808c2ecf20Sopenharmony_ci	    !readl(txq->bd.reg_desc_active) ||
7818c2ecf20Sopenharmony_ci	    !readl(txq->bd.reg_desc_active) ||
7828c2ecf20Sopenharmony_ci	    !readl(txq->bd.reg_desc_active) ||
7838c2ecf20Sopenharmony_ci	    !readl(txq->bd.reg_desc_active))
7848c2ecf20Sopenharmony_ci		writel(0, txq->bd.reg_desc_active);
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	return 0;
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_cierr_release:
7898c2ecf20Sopenharmony_ci	/* TODO: Release all used data descriptors for TSO */
7908c2ecf20Sopenharmony_ci	return ret;
7918c2ecf20Sopenharmony_ci}
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_cistatic netdev_tx_t
7948c2ecf20Sopenharmony_cifec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
7958c2ecf20Sopenharmony_ci{
7968c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
7978c2ecf20Sopenharmony_ci	int entries_free;
7988c2ecf20Sopenharmony_ci	unsigned short queue;
7998c2ecf20Sopenharmony_ci	struct fec_enet_priv_tx_q *txq;
8008c2ecf20Sopenharmony_ci	struct netdev_queue *nq;
8018c2ecf20Sopenharmony_ci	int ret;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	queue = skb_get_queue_mapping(skb);
8048c2ecf20Sopenharmony_ci	txq = fep->tx_queue[queue];
8058c2ecf20Sopenharmony_ci	nq = netdev_get_tx_queue(ndev, queue);
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	if (skb_is_gso(skb))
8088c2ecf20Sopenharmony_ci		ret = fec_enet_txq_submit_tso(txq, skb, ndev);
8098c2ecf20Sopenharmony_ci	else
8108c2ecf20Sopenharmony_ci		ret = fec_enet_txq_submit_skb(txq, skb, ndev);
8118c2ecf20Sopenharmony_ci	if (ret)
8128c2ecf20Sopenharmony_ci		return ret;
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	entries_free = fec_enet_get_free_txdesc_num(txq);
8158c2ecf20Sopenharmony_ci	if (entries_free <= txq->tx_stop_threshold)
8168c2ecf20Sopenharmony_ci		netif_tx_stop_queue(nq);
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
8198c2ecf20Sopenharmony_ci}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci/* Init RX & TX buffer descriptors
8228c2ecf20Sopenharmony_ci */
8238c2ecf20Sopenharmony_cistatic void fec_enet_bd_init(struct net_device *dev)
8248c2ecf20Sopenharmony_ci{
8258c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(dev);
8268c2ecf20Sopenharmony_ci	struct fec_enet_priv_tx_q *txq;
8278c2ecf20Sopenharmony_ci	struct fec_enet_priv_rx_q *rxq;
8288c2ecf20Sopenharmony_ci	struct bufdesc *bdp;
8298c2ecf20Sopenharmony_ci	unsigned int i;
8308c2ecf20Sopenharmony_ci	unsigned int q;
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	for (q = 0; q < fep->num_rx_queues; q++) {
8338c2ecf20Sopenharmony_ci		/* Initialize the receive buffer descriptors. */
8348c2ecf20Sopenharmony_ci		rxq = fep->rx_queue[q];
8358c2ecf20Sopenharmony_ci		bdp = rxq->bd.base;
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci		for (i = 0; i < rxq->bd.ring_size; i++) {
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci			/* Initialize the BD for every fragment in the page. */
8408c2ecf20Sopenharmony_ci			if (bdp->cbd_bufaddr)
8418c2ecf20Sopenharmony_ci				bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY);
8428c2ecf20Sopenharmony_ci			else
8438c2ecf20Sopenharmony_ci				bdp->cbd_sc = cpu_to_fec16(0);
8448c2ecf20Sopenharmony_ci			bdp = fec_enet_get_nextdesc(bdp, &rxq->bd);
8458c2ecf20Sopenharmony_ci		}
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci		/* Set the last buffer to wrap */
8488c2ecf20Sopenharmony_ci		bdp = fec_enet_get_prevdesc(bdp, &rxq->bd);
8498c2ecf20Sopenharmony_ci		bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP);
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci		rxq->bd.cur = rxq->bd.base;
8528c2ecf20Sopenharmony_ci	}
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	for (q = 0; q < fep->num_tx_queues; q++) {
8558c2ecf20Sopenharmony_ci		/* ...and the same for transmit */
8568c2ecf20Sopenharmony_ci		txq = fep->tx_queue[q];
8578c2ecf20Sopenharmony_ci		bdp = txq->bd.base;
8588c2ecf20Sopenharmony_ci		txq->bd.cur = bdp;
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci		for (i = 0; i < txq->bd.ring_size; i++) {
8618c2ecf20Sopenharmony_ci			/* Initialize the BD for every fragment in the page. */
8628c2ecf20Sopenharmony_ci			bdp->cbd_sc = cpu_to_fec16(0);
8638c2ecf20Sopenharmony_ci			if (bdp->cbd_bufaddr &&
8648c2ecf20Sopenharmony_ci			    !IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr)))
8658c2ecf20Sopenharmony_ci				dma_unmap_single(&fep->pdev->dev,
8668c2ecf20Sopenharmony_ci						 fec32_to_cpu(bdp->cbd_bufaddr),
8678c2ecf20Sopenharmony_ci						 fec16_to_cpu(bdp->cbd_datlen),
8688c2ecf20Sopenharmony_ci						 DMA_TO_DEVICE);
8698c2ecf20Sopenharmony_ci			if (txq->tx_skbuff[i]) {
8708c2ecf20Sopenharmony_ci				dev_kfree_skb_any(txq->tx_skbuff[i]);
8718c2ecf20Sopenharmony_ci				txq->tx_skbuff[i] = NULL;
8728c2ecf20Sopenharmony_ci			}
8738c2ecf20Sopenharmony_ci			bdp->cbd_bufaddr = cpu_to_fec32(0);
8748c2ecf20Sopenharmony_ci			bdp = fec_enet_get_nextdesc(bdp, &txq->bd);
8758c2ecf20Sopenharmony_ci		}
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci		/* Set the last buffer to wrap */
8788c2ecf20Sopenharmony_ci		bdp = fec_enet_get_prevdesc(bdp, &txq->bd);
8798c2ecf20Sopenharmony_ci		bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP);
8808c2ecf20Sopenharmony_ci		txq->dirty_tx = bdp;
8818c2ecf20Sopenharmony_ci	}
8828c2ecf20Sopenharmony_ci}
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_cistatic void fec_enet_active_rxring(struct net_device *ndev)
8858c2ecf20Sopenharmony_ci{
8868c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
8878c2ecf20Sopenharmony_ci	int i;
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	for (i = 0; i < fep->num_rx_queues; i++)
8908c2ecf20Sopenharmony_ci		writel(0, fep->rx_queue[i]->bd.reg_desc_active);
8918c2ecf20Sopenharmony_ci}
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_cistatic void fec_enet_enable_ring(struct net_device *ndev)
8948c2ecf20Sopenharmony_ci{
8958c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
8968c2ecf20Sopenharmony_ci	struct fec_enet_priv_tx_q *txq;
8978c2ecf20Sopenharmony_ci	struct fec_enet_priv_rx_q *rxq;
8988c2ecf20Sopenharmony_ci	int i;
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	for (i = 0; i < fep->num_rx_queues; i++) {
9018c2ecf20Sopenharmony_ci		rxq = fep->rx_queue[i];
9028c2ecf20Sopenharmony_ci		writel(rxq->bd.dma, fep->hwp + FEC_R_DES_START(i));
9038c2ecf20Sopenharmony_ci		writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_R_BUFF_SIZE(i));
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci		/* enable DMA1/2 */
9068c2ecf20Sopenharmony_ci		if (i)
9078c2ecf20Sopenharmony_ci			writel(RCMR_MATCHEN | RCMR_CMP(i),
9088c2ecf20Sopenharmony_ci			       fep->hwp + FEC_RCMR(i));
9098c2ecf20Sopenharmony_ci	}
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	for (i = 0; i < fep->num_tx_queues; i++) {
9128c2ecf20Sopenharmony_ci		txq = fep->tx_queue[i];
9138c2ecf20Sopenharmony_ci		writel(txq->bd.dma, fep->hwp + FEC_X_DES_START(i));
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci		/* enable DMA1/2 */
9168c2ecf20Sopenharmony_ci		if (i)
9178c2ecf20Sopenharmony_ci			writel(DMA_CLASS_EN | IDLE_SLOPE(i),
9188c2ecf20Sopenharmony_ci			       fep->hwp + FEC_DMA_CFG(i));
9198c2ecf20Sopenharmony_ci	}
9208c2ecf20Sopenharmony_ci}
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_cistatic void fec_enet_reset_skb(struct net_device *ndev)
9238c2ecf20Sopenharmony_ci{
9248c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
9258c2ecf20Sopenharmony_ci	struct fec_enet_priv_tx_q *txq;
9268c2ecf20Sopenharmony_ci	int i, j;
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	for (i = 0; i < fep->num_tx_queues; i++) {
9298c2ecf20Sopenharmony_ci		txq = fep->tx_queue[i];
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci		for (j = 0; j < txq->bd.ring_size; j++) {
9328c2ecf20Sopenharmony_ci			if (txq->tx_skbuff[j]) {
9338c2ecf20Sopenharmony_ci				dev_kfree_skb_any(txq->tx_skbuff[j]);
9348c2ecf20Sopenharmony_ci				txq->tx_skbuff[j] = NULL;
9358c2ecf20Sopenharmony_ci			}
9368c2ecf20Sopenharmony_ci		}
9378c2ecf20Sopenharmony_ci	}
9388c2ecf20Sopenharmony_ci}
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci/*
9418c2ecf20Sopenharmony_ci * This function is called to start or restart the FEC during a link
9428c2ecf20Sopenharmony_ci * change, transmit timeout, or to reconfigure the FEC.  The network
9438c2ecf20Sopenharmony_ci * packet processing for this device must be stopped before this call.
9448c2ecf20Sopenharmony_ci */
9458c2ecf20Sopenharmony_cistatic void
9468c2ecf20Sopenharmony_cifec_restart(struct net_device *ndev)
9478c2ecf20Sopenharmony_ci{
9488c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
9498c2ecf20Sopenharmony_ci	u32 val;
9508c2ecf20Sopenharmony_ci	u32 temp_mac[2];
9518c2ecf20Sopenharmony_ci	u32 rcntl = OPT_FRAME_SIZE | 0x04;
9528c2ecf20Sopenharmony_ci	u32 ecntl = 0x2; /* ETHEREN */
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	/* Whack a reset.  We should wait for this.
9558c2ecf20Sopenharmony_ci	 * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
9568c2ecf20Sopenharmony_ci	 * instead of reset MAC itself.
9578c2ecf20Sopenharmony_ci	 */
9588c2ecf20Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_HAS_AVB) {
9598c2ecf20Sopenharmony_ci		writel(0, fep->hwp + FEC_ECNTRL);
9608c2ecf20Sopenharmony_ci	} else {
9618c2ecf20Sopenharmony_ci		writel(1, fep->hwp + FEC_ECNTRL);
9628c2ecf20Sopenharmony_ci		udelay(10);
9638c2ecf20Sopenharmony_ci	}
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	/*
9668c2ecf20Sopenharmony_ci	 * enet-mac reset will reset mac address registers too,
9678c2ecf20Sopenharmony_ci	 * so need to reconfigure it.
9688c2ecf20Sopenharmony_ci	 */
9698c2ecf20Sopenharmony_ci	memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN);
9708c2ecf20Sopenharmony_ci	writel((__force u32)cpu_to_be32(temp_mac[0]),
9718c2ecf20Sopenharmony_ci	       fep->hwp + FEC_ADDR_LOW);
9728c2ecf20Sopenharmony_ci	writel((__force u32)cpu_to_be32(temp_mac[1]),
9738c2ecf20Sopenharmony_ci	       fep->hwp + FEC_ADDR_HIGH);
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci	/* Clear any outstanding interrupt, except MDIO. */
9768c2ecf20Sopenharmony_ci	writel((0xffffffff & ~FEC_ENET_MII), fep->hwp + FEC_IEVENT);
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	fec_enet_bd_init(ndev);
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	fec_enet_enable_ring(ndev);
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	/* Reset tx SKB buffers. */
9838c2ecf20Sopenharmony_ci	fec_enet_reset_skb(ndev);
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	/* Enable MII mode */
9868c2ecf20Sopenharmony_ci	if (fep->full_duplex == DUPLEX_FULL) {
9878c2ecf20Sopenharmony_ci		/* FD enable */
9888c2ecf20Sopenharmony_ci		writel(0x04, fep->hwp + FEC_X_CNTRL);
9898c2ecf20Sopenharmony_ci	} else {
9908c2ecf20Sopenharmony_ci		/* No Rcv on Xmit */
9918c2ecf20Sopenharmony_ci		rcntl |= 0x02;
9928c2ecf20Sopenharmony_ci		writel(0x0, fep->hwp + FEC_X_CNTRL);
9938c2ecf20Sopenharmony_ci	}
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	/* Set MII speed */
9968c2ecf20Sopenharmony_ci	writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci#if !defined(CONFIG_M5272)
9998c2ecf20Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_HAS_RACC) {
10008c2ecf20Sopenharmony_ci		val = readl(fep->hwp + FEC_RACC);
10018c2ecf20Sopenharmony_ci		/* align IP header */
10028c2ecf20Sopenharmony_ci		val |= FEC_RACC_SHIFT16;
10038c2ecf20Sopenharmony_ci		if (fep->csum_flags & FLAG_RX_CSUM_ENABLED)
10048c2ecf20Sopenharmony_ci			/* set RX checksum */
10058c2ecf20Sopenharmony_ci			val |= FEC_RACC_OPTIONS;
10068c2ecf20Sopenharmony_ci		else
10078c2ecf20Sopenharmony_ci			val &= ~FEC_RACC_OPTIONS;
10088c2ecf20Sopenharmony_ci		writel(val, fep->hwp + FEC_RACC);
10098c2ecf20Sopenharmony_ci		writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_FTRL);
10108c2ecf20Sopenharmony_ci	}
10118c2ecf20Sopenharmony_ci#endif
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	/*
10148c2ecf20Sopenharmony_ci	 * The phy interface and speed need to get configured
10158c2ecf20Sopenharmony_ci	 * differently on enet-mac.
10168c2ecf20Sopenharmony_ci	 */
10178c2ecf20Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_ENET_MAC) {
10188c2ecf20Sopenharmony_ci		/* Enable flow control and length check */
10198c2ecf20Sopenharmony_ci		rcntl |= 0x40000000 | 0x00000020;
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci		/* RGMII, RMII or MII */
10228c2ecf20Sopenharmony_ci		if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII ||
10238c2ecf20Sopenharmony_ci		    fep->phy_interface == PHY_INTERFACE_MODE_RGMII_ID ||
10248c2ecf20Sopenharmony_ci		    fep->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID ||
10258c2ecf20Sopenharmony_ci		    fep->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID)
10268c2ecf20Sopenharmony_ci			rcntl |= (1 << 6);
10278c2ecf20Sopenharmony_ci		else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII)
10288c2ecf20Sopenharmony_ci			rcntl |= (1 << 8);
10298c2ecf20Sopenharmony_ci		else
10308c2ecf20Sopenharmony_ci			rcntl &= ~(1 << 8);
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci		/* 1G, 100M or 10M */
10338c2ecf20Sopenharmony_ci		if (ndev->phydev) {
10348c2ecf20Sopenharmony_ci			if (ndev->phydev->speed == SPEED_1000)
10358c2ecf20Sopenharmony_ci				ecntl |= (1 << 5);
10368c2ecf20Sopenharmony_ci			else if (ndev->phydev->speed == SPEED_100)
10378c2ecf20Sopenharmony_ci				rcntl &= ~(1 << 9);
10388c2ecf20Sopenharmony_ci			else
10398c2ecf20Sopenharmony_ci				rcntl |= (1 << 9);
10408c2ecf20Sopenharmony_ci		}
10418c2ecf20Sopenharmony_ci	} else {
10428c2ecf20Sopenharmony_ci#ifdef FEC_MIIGSK_ENR
10438c2ecf20Sopenharmony_ci		if (fep->quirks & FEC_QUIRK_USE_GASKET) {
10448c2ecf20Sopenharmony_ci			u32 cfgr;
10458c2ecf20Sopenharmony_ci			/* disable the gasket and wait */
10468c2ecf20Sopenharmony_ci			writel(0, fep->hwp + FEC_MIIGSK_ENR);
10478c2ecf20Sopenharmony_ci			while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4)
10488c2ecf20Sopenharmony_ci				udelay(1);
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci			/*
10518c2ecf20Sopenharmony_ci			 * configure the gasket:
10528c2ecf20Sopenharmony_ci			 *   RMII, 50 MHz, no loopback, no echo
10538c2ecf20Sopenharmony_ci			 *   MII, 25 MHz, no loopback, no echo
10548c2ecf20Sopenharmony_ci			 */
10558c2ecf20Sopenharmony_ci			cfgr = (fep->phy_interface == PHY_INTERFACE_MODE_RMII)
10568c2ecf20Sopenharmony_ci				? BM_MIIGSK_CFGR_RMII : BM_MIIGSK_CFGR_MII;
10578c2ecf20Sopenharmony_ci			if (ndev->phydev && ndev->phydev->speed == SPEED_10)
10588c2ecf20Sopenharmony_ci				cfgr |= BM_MIIGSK_CFGR_FRCONT_10M;
10598c2ecf20Sopenharmony_ci			writel(cfgr, fep->hwp + FEC_MIIGSK_CFGR);
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci			/* re-enable the gasket */
10628c2ecf20Sopenharmony_ci			writel(2, fep->hwp + FEC_MIIGSK_ENR);
10638c2ecf20Sopenharmony_ci		}
10648c2ecf20Sopenharmony_ci#endif
10658c2ecf20Sopenharmony_ci	}
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci#if !defined(CONFIG_M5272)
10688c2ecf20Sopenharmony_ci	/* enable pause frame*/
10698c2ecf20Sopenharmony_ci	if ((fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) ||
10708c2ecf20Sopenharmony_ci	    ((fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) &&
10718c2ecf20Sopenharmony_ci	     ndev->phydev && ndev->phydev->pause)) {
10728c2ecf20Sopenharmony_ci		rcntl |= FEC_ENET_FCE;
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci		/* set FIFO threshold parameter to reduce overrun */
10758c2ecf20Sopenharmony_ci		writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM);
10768c2ecf20Sopenharmony_ci		writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL);
10778c2ecf20Sopenharmony_ci		writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM);
10788c2ecf20Sopenharmony_ci		writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL);
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci		/* OPD */
10818c2ecf20Sopenharmony_ci		writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD);
10828c2ecf20Sopenharmony_ci	} else {
10838c2ecf20Sopenharmony_ci		rcntl &= ~FEC_ENET_FCE;
10848c2ecf20Sopenharmony_ci	}
10858c2ecf20Sopenharmony_ci#endif /* !defined(CONFIG_M5272) */
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	writel(rcntl, fep->hwp + FEC_R_CNTRL);
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	/* Setup multicast filter. */
10908c2ecf20Sopenharmony_ci	set_multicast_list(ndev);
10918c2ecf20Sopenharmony_ci#ifndef CONFIG_M5272
10928c2ecf20Sopenharmony_ci	writel(0, fep->hwp + FEC_HASH_TABLE_HIGH);
10938c2ecf20Sopenharmony_ci	writel(0, fep->hwp + FEC_HASH_TABLE_LOW);
10948c2ecf20Sopenharmony_ci#endif
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_ENET_MAC) {
10978c2ecf20Sopenharmony_ci		/* enable ENET endian swap */
10988c2ecf20Sopenharmony_ci		ecntl |= (1 << 8);
10998c2ecf20Sopenharmony_ci		/* enable ENET store and forward mode */
11008c2ecf20Sopenharmony_ci		writel(1 << 8, fep->hwp + FEC_X_WMRK);
11018c2ecf20Sopenharmony_ci	}
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	if (fep->bufdesc_ex)
11048c2ecf20Sopenharmony_ci		ecntl |= (1 << 4);
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci#ifndef CONFIG_M5272
11078c2ecf20Sopenharmony_ci	/* Enable the MIB statistic event counters */
11088c2ecf20Sopenharmony_ci	writel(0 << 31, fep->hwp + FEC_MIB_CTRLSTAT);
11098c2ecf20Sopenharmony_ci#endif
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	/* And last, enable the transmit and receive processing */
11128c2ecf20Sopenharmony_ci	writel(ecntl, fep->hwp + FEC_ECNTRL);
11138c2ecf20Sopenharmony_ci	fec_enet_active_rxring(ndev);
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	if (fep->bufdesc_ex)
11168c2ecf20Sopenharmony_ci		fec_ptp_start_cyclecounter(ndev);
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	/* Enable interrupts we wish to service */
11198c2ecf20Sopenharmony_ci	if (fep->link)
11208c2ecf20Sopenharmony_ci		writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
11218c2ecf20Sopenharmony_ci	else
11228c2ecf20Sopenharmony_ci		writel(0, fep->hwp + FEC_IMASK);
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	/* Init the interrupt coalescing */
11258c2ecf20Sopenharmony_ci	fec_enet_itr_coal_init(ndev);
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci}
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_cistatic void fec_enet_stop_mode(struct fec_enet_private *fep, bool enabled)
11308c2ecf20Sopenharmony_ci{
11318c2ecf20Sopenharmony_ci	struct fec_platform_data *pdata = fep->pdev->dev.platform_data;
11328c2ecf20Sopenharmony_ci	struct fec_stop_mode_gpr *stop_gpr = &fep->stop_gpr;
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci	if (stop_gpr->gpr) {
11358c2ecf20Sopenharmony_ci		if (enabled)
11368c2ecf20Sopenharmony_ci			regmap_update_bits(stop_gpr->gpr, stop_gpr->reg,
11378c2ecf20Sopenharmony_ci					   BIT(stop_gpr->bit),
11388c2ecf20Sopenharmony_ci					   BIT(stop_gpr->bit));
11398c2ecf20Sopenharmony_ci		else
11408c2ecf20Sopenharmony_ci			regmap_update_bits(stop_gpr->gpr, stop_gpr->reg,
11418c2ecf20Sopenharmony_ci					   BIT(stop_gpr->bit), 0);
11428c2ecf20Sopenharmony_ci	} else if (pdata && pdata->sleep_mode_enable) {
11438c2ecf20Sopenharmony_ci		pdata->sleep_mode_enable(enabled);
11448c2ecf20Sopenharmony_ci	}
11458c2ecf20Sopenharmony_ci}
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_cistatic void
11488c2ecf20Sopenharmony_cifec_stop(struct net_device *ndev)
11498c2ecf20Sopenharmony_ci{
11508c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
11518c2ecf20Sopenharmony_ci	u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8);
11528c2ecf20Sopenharmony_ci	u32 val;
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	/* We cannot expect a graceful transmit stop without link !!! */
11558c2ecf20Sopenharmony_ci	if (fep->link) {
11568c2ecf20Sopenharmony_ci		writel(1, fep->hwp + FEC_X_CNTRL); /* Graceful transmit stop */
11578c2ecf20Sopenharmony_ci		udelay(10);
11588c2ecf20Sopenharmony_ci		if (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_GRA))
11598c2ecf20Sopenharmony_ci			netdev_err(ndev, "Graceful transmit stop did not complete!\n");
11608c2ecf20Sopenharmony_ci	}
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	/* Whack a reset.  We should wait for this.
11638c2ecf20Sopenharmony_ci	 * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
11648c2ecf20Sopenharmony_ci	 * instead of reset MAC itself.
11658c2ecf20Sopenharmony_ci	 */
11668c2ecf20Sopenharmony_ci	if (!(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) {
11678c2ecf20Sopenharmony_ci		if (fep->quirks & FEC_QUIRK_HAS_AVB) {
11688c2ecf20Sopenharmony_ci			writel(0, fep->hwp + FEC_ECNTRL);
11698c2ecf20Sopenharmony_ci		} else {
11708c2ecf20Sopenharmony_ci			writel(1, fep->hwp + FEC_ECNTRL);
11718c2ecf20Sopenharmony_ci			udelay(10);
11728c2ecf20Sopenharmony_ci		}
11738c2ecf20Sopenharmony_ci		writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
11748c2ecf20Sopenharmony_ci	} else {
11758c2ecf20Sopenharmony_ci		writel(FEC_DEFAULT_IMASK | FEC_ENET_WAKEUP, fep->hwp + FEC_IMASK);
11768c2ecf20Sopenharmony_ci		val = readl(fep->hwp + FEC_ECNTRL);
11778c2ecf20Sopenharmony_ci		val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
11788c2ecf20Sopenharmony_ci		writel(val, fep->hwp + FEC_ECNTRL);
11798c2ecf20Sopenharmony_ci		fec_enet_stop_mode(fep, true);
11808c2ecf20Sopenharmony_ci	}
11818c2ecf20Sopenharmony_ci	writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	/* We have to keep ENET enabled to have MII interrupt stay working */
11848c2ecf20Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_ENET_MAC &&
11858c2ecf20Sopenharmony_ci		!(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) {
11868c2ecf20Sopenharmony_ci		writel(2, fep->hwp + FEC_ECNTRL);
11878c2ecf20Sopenharmony_ci		writel(rmii_mode, fep->hwp + FEC_R_CNTRL);
11888c2ecf20Sopenharmony_ci	}
11898c2ecf20Sopenharmony_ci}
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_cistatic void
11938c2ecf20Sopenharmony_cifec_timeout(struct net_device *ndev, unsigned int txqueue)
11948c2ecf20Sopenharmony_ci{
11958c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ci	fec_dump(ndev);
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	ndev->stats.tx_errors++;
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	schedule_work(&fep->tx_timeout_work);
12028c2ecf20Sopenharmony_ci}
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_cistatic void fec_enet_timeout_work(struct work_struct *work)
12058c2ecf20Sopenharmony_ci{
12068c2ecf20Sopenharmony_ci	struct fec_enet_private *fep =
12078c2ecf20Sopenharmony_ci		container_of(work, struct fec_enet_private, tx_timeout_work);
12088c2ecf20Sopenharmony_ci	struct net_device *ndev = fep->netdev;
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci	rtnl_lock();
12118c2ecf20Sopenharmony_ci	if (netif_device_present(ndev) || netif_running(ndev)) {
12128c2ecf20Sopenharmony_ci		napi_disable(&fep->napi);
12138c2ecf20Sopenharmony_ci		netif_tx_lock_bh(ndev);
12148c2ecf20Sopenharmony_ci		fec_restart(ndev);
12158c2ecf20Sopenharmony_ci		netif_tx_wake_all_queues(ndev);
12168c2ecf20Sopenharmony_ci		netif_tx_unlock_bh(ndev);
12178c2ecf20Sopenharmony_ci		napi_enable(&fep->napi);
12188c2ecf20Sopenharmony_ci	}
12198c2ecf20Sopenharmony_ci	rtnl_unlock();
12208c2ecf20Sopenharmony_ci}
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_cistatic void
12238c2ecf20Sopenharmony_cifec_enet_hwtstamp(struct fec_enet_private *fep, unsigned ts,
12248c2ecf20Sopenharmony_ci	struct skb_shared_hwtstamps *hwtstamps)
12258c2ecf20Sopenharmony_ci{
12268c2ecf20Sopenharmony_ci	unsigned long flags;
12278c2ecf20Sopenharmony_ci	u64 ns;
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	spin_lock_irqsave(&fep->tmreg_lock, flags);
12308c2ecf20Sopenharmony_ci	ns = timecounter_cyc2time(&fep->tc, ts);
12318c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&fep->tmreg_lock, flags);
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci	memset(hwtstamps, 0, sizeof(*hwtstamps));
12348c2ecf20Sopenharmony_ci	hwtstamps->hwtstamp = ns_to_ktime(ns);
12358c2ecf20Sopenharmony_ci}
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_cistatic void
12388c2ecf20Sopenharmony_cifec_enet_tx_queue(struct net_device *ndev, u16 queue_id)
12398c2ecf20Sopenharmony_ci{
12408c2ecf20Sopenharmony_ci	struct	fec_enet_private *fep;
12418c2ecf20Sopenharmony_ci	struct bufdesc *bdp;
12428c2ecf20Sopenharmony_ci	unsigned short status;
12438c2ecf20Sopenharmony_ci	struct	sk_buff	*skb;
12448c2ecf20Sopenharmony_ci	struct fec_enet_priv_tx_q *txq;
12458c2ecf20Sopenharmony_ci	struct netdev_queue *nq;
12468c2ecf20Sopenharmony_ci	int	index = 0;
12478c2ecf20Sopenharmony_ci	int	entries_free;
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	fep = netdev_priv(ndev);
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci	txq = fep->tx_queue[queue_id];
12528c2ecf20Sopenharmony_ci	/* get next bdp of dirty_tx */
12538c2ecf20Sopenharmony_ci	nq = netdev_get_tx_queue(ndev, queue_id);
12548c2ecf20Sopenharmony_ci	bdp = txq->dirty_tx;
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	/* get next bdp of dirty_tx */
12578c2ecf20Sopenharmony_ci	bdp = fec_enet_get_nextdesc(bdp, &txq->bd);
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci	while (bdp != READ_ONCE(txq->bd.cur)) {
12608c2ecf20Sopenharmony_ci		/* Order the load of bd.cur and cbd_sc */
12618c2ecf20Sopenharmony_ci		rmb();
12628c2ecf20Sopenharmony_ci		status = fec16_to_cpu(READ_ONCE(bdp->cbd_sc));
12638c2ecf20Sopenharmony_ci		if (status & BD_ENET_TX_READY)
12648c2ecf20Sopenharmony_ci			break;
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci		index = fec_enet_get_bd_index(bdp, &txq->bd);
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_ci		skb = txq->tx_skbuff[index];
12698c2ecf20Sopenharmony_ci		txq->tx_skbuff[index] = NULL;
12708c2ecf20Sopenharmony_ci		if (!IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr)))
12718c2ecf20Sopenharmony_ci			dma_unmap_single(&fep->pdev->dev,
12728c2ecf20Sopenharmony_ci					 fec32_to_cpu(bdp->cbd_bufaddr),
12738c2ecf20Sopenharmony_ci					 fec16_to_cpu(bdp->cbd_datlen),
12748c2ecf20Sopenharmony_ci					 DMA_TO_DEVICE);
12758c2ecf20Sopenharmony_ci		bdp->cbd_bufaddr = cpu_to_fec32(0);
12768c2ecf20Sopenharmony_ci		if (!skb)
12778c2ecf20Sopenharmony_ci			goto skb_done;
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci		/* Check for errors. */
12808c2ecf20Sopenharmony_ci		if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC |
12818c2ecf20Sopenharmony_ci				   BD_ENET_TX_RL | BD_ENET_TX_UN |
12828c2ecf20Sopenharmony_ci				   BD_ENET_TX_CSL)) {
12838c2ecf20Sopenharmony_ci			ndev->stats.tx_errors++;
12848c2ecf20Sopenharmony_ci			if (status & BD_ENET_TX_HB)  /* No heartbeat */
12858c2ecf20Sopenharmony_ci				ndev->stats.tx_heartbeat_errors++;
12868c2ecf20Sopenharmony_ci			if (status & BD_ENET_TX_LC)  /* Late collision */
12878c2ecf20Sopenharmony_ci				ndev->stats.tx_window_errors++;
12888c2ecf20Sopenharmony_ci			if (status & BD_ENET_TX_RL)  /* Retrans limit */
12898c2ecf20Sopenharmony_ci				ndev->stats.tx_aborted_errors++;
12908c2ecf20Sopenharmony_ci			if (status & BD_ENET_TX_UN)  /* Underrun */
12918c2ecf20Sopenharmony_ci				ndev->stats.tx_fifo_errors++;
12928c2ecf20Sopenharmony_ci			if (status & BD_ENET_TX_CSL) /* Carrier lost */
12938c2ecf20Sopenharmony_ci				ndev->stats.tx_carrier_errors++;
12948c2ecf20Sopenharmony_ci		} else {
12958c2ecf20Sopenharmony_ci			ndev->stats.tx_packets++;
12968c2ecf20Sopenharmony_ci			ndev->stats.tx_bytes += skb->len;
12978c2ecf20Sopenharmony_ci		}
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci		/* NOTE: SKBTX_IN_PROGRESS being set does not imply it's we who
13008c2ecf20Sopenharmony_ci		 * are to time stamp the packet, so we still need to check time
13018c2ecf20Sopenharmony_ci		 * stamping enabled flag.
13028c2ecf20Sopenharmony_ci		 */
13038c2ecf20Sopenharmony_ci		if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS &&
13048c2ecf20Sopenharmony_ci			     fep->hwts_tx_en) &&
13058c2ecf20Sopenharmony_ci		    fep->bufdesc_ex) {
13068c2ecf20Sopenharmony_ci			struct skb_shared_hwtstamps shhwtstamps;
13078c2ecf20Sopenharmony_ci			struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci			fec_enet_hwtstamp(fep, fec32_to_cpu(ebdp->ts), &shhwtstamps);
13108c2ecf20Sopenharmony_ci			skb_tstamp_tx(skb, &shhwtstamps);
13118c2ecf20Sopenharmony_ci		}
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci		/* Deferred means some collisions occurred during transmit,
13148c2ecf20Sopenharmony_ci		 * but we eventually sent the packet OK.
13158c2ecf20Sopenharmony_ci		 */
13168c2ecf20Sopenharmony_ci		if (status & BD_ENET_TX_DEF)
13178c2ecf20Sopenharmony_ci			ndev->stats.collisions++;
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci		/* Free the sk buffer associated with this last transmit */
13208c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
13218c2ecf20Sopenharmony_ciskb_done:
13228c2ecf20Sopenharmony_ci		/* Make sure the update to bdp and tx_skbuff are performed
13238c2ecf20Sopenharmony_ci		 * before dirty_tx
13248c2ecf20Sopenharmony_ci		 */
13258c2ecf20Sopenharmony_ci		wmb();
13268c2ecf20Sopenharmony_ci		txq->dirty_tx = bdp;
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_ci		/* Update pointer to next buffer descriptor to be transmitted */
13298c2ecf20Sopenharmony_ci		bdp = fec_enet_get_nextdesc(bdp, &txq->bd);
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci		/* Since we have freed up a buffer, the ring is no longer full
13328c2ecf20Sopenharmony_ci		 */
13338c2ecf20Sopenharmony_ci		if (netif_tx_queue_stopped(nq)) {
13348c2ecf20Sopenharmony_ci			entries_free = fec_enet_get_free_txdesc_num(txq);
13358c2ecf20Sopenharmony_ci			if (entries_free >= txq->tx_wake_threshold)
13368c2ecf20Sopenharmony_ci				netif_tx_wake_queue(nq);
13378c2ecf20Sopenharmony_ci		}
13388c2ecf20Sopenharmony_ci	}
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci	/* ERR006358: Keep the transmitter going */
13418c2ecf20Sopenharmony_ci	if (bdp != txq->bd.cur &&
13428c2ecf20Sopenharmony_ci	    readl(txq->bd.reg_desc_active) == 0)
13438c2ecf20Sopenharmony_ci		writel(0, txq->bd.reg_desc_active);
13448c2ecf20Sopenharmony_ci}
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_cistatic void fec_enet_tx(struct net_device *ndev)
13478c2ecf20Sopenharmony_ci{
13488c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
13498c2ecf20Sopenharmony_ci	int i;
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci	/* Make sure that AVB queues are processed first. */
13528c2ecf20Sopenharmony_ci	for (i = fep->num_tx_queues - 1; i >= 0; i--)
13538c2ecf20Sopenharmony_ci		fec_enet_tx_queue(ndev, i);
13548c2ecf20Sopenharmony_ci}
13558c2ecf20Sopenharmony_ci
13568c2ecf20Sopenharmony_cistatic int
13578c2ecf20Sopenharmony_cifec_enet_new_rxbdp(struct net_device *ndev, struct bufdesc *bdp, struct sk_buff *skb)
13588c2ecf20Sopenharmony_ci{
13598c2ecf20Sopenharmony_ci	struct  fec_enet_private *fep = netdev_priv(ndev);
13608c2ecf20Sopenharmony_ci	int off;
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci	off = ((unsigned long)skb->data) & fep->rx_align;
13638c2ecf20Sopenharmony_ci	if (off)
13648c2ecf20Sopenharmony_ci		skb_reserve(skb, fep->rx_align + 1 - off);
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci	bdp->cbd_bufaddr = cpu_to_fec32(dma_map_single(&fep->pdev->dev, skb->data, FEC_ENET_RX_FRSIZE - fep->rx_align, DMA_FROM_DEVICE));
13678c2ecf20Sopenharmony_ci	if (dma_mapping_error(&fep->pdev->dev, fec32_to_cpu(bdp->cbd_bufaddr))) {
13688c2ecf20Sopenharmony_ci		if (net_ratelimit())
13698c2ecf20Sopenharmony_ci			netdev_err(ndev, "Rx DMA memory map failed\n");
13708c2ecf20Sopenharmony_ci		return -ENOMEM;
13718c2ecf20Sopenharmony_ci	}
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_ci	return 0;
13748c2ecf20Sopenharmony_ci}
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_cistatic bool fec_enet_copybreak(struct net_device *ndev, struct sk_buff **skb,
13778c2ecf20Sopenharmony_ci			       struct bufdesc *bdp, u32 length, bool swap)
13788c2ecf20Sopenharmony_ci{
13798c2ecf20Sopenharmony_ci	struct  fec_enet_private *fep = netdev_priv(ndev);
13808c2ecf20Sopenharmony_ci	struct sk_buff *new_skb;
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	if (length > fep->rx_copybreak)
13838c2ecf20Sopenharmony_ci		return false;
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci	new_skb = netdev_alloc_skb(ndev, length);
13868c2ecf20Sopenharmony_ci	if (!new_skb)
13878c2ecf20Sopenharmony_ci		return false;
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_ci	dma_sync_single_for_cpu(&fep->pdev->dev,
13908c2ecf20Sopenharmony_ci				fec32_to_cpu(bdp->cbd_bufaddr),
13918c2ecf20Sopenharmony_ci				FEC_ENET_RX_FRSIZE - fep->rx_align,
13928c2ecf20Sopenharmony_ci				DMA_FROM_DEVICE);
13938c2ecf20Sopenharmony_ci	if (!swap)
13948c2ecf20Sopenharmony_ci		memcpy(new_skb->data, (*skb)->data, length);
13958c2ecf20Sopenharmony_ci	else
13968c2ecf20Sopenharmony_ci		swap_buffer2(new_skb->data, (*skb)->data, length);
13978c2ecf20Sopenharmony_ci	*skb = new_skb;
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	return true;
14008c2ecf20Sopenharmony_ci}
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_ci/* During a receive, the bd_rx.cur points to the current incoming buffer.
14038c2ecf20Sopenharmony_ci * When we update through the ring, if the next incoming buffer has
14048c2ecf20Sopenharmony_ci * not been given to the system, we just set the empty indicator,
14058c2ecf20Sopenharmony_ci * effectively tossing the packet.
14068c2ecf20Sopenharmony_ci */
14078c2ecf20Sopenharmony_cistatic int
14088c2ecf20Sopenharmony_cifec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)
14098c2ecf20Sopenharmony_ci{
14108c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
14118c2ecf20Sopenharmony_ci	struct fec_enet_priv_rx_q *rxq;
14128c2ecf20Sopenharmony_ci	struct bufdesc *bdp;
14138c2ecf20Sopenharmony_ci	unsigned short status;
14148c2ecf20Sopenharmony_ci	struct  sk_buff *skb_new = NULL;
14158c2ecf20Sopenharmony_ci	struct  sk_buff *skb;
14168c2ecf20Sopenharmony_ci	ushort	pkt_len;
14178c2ecf20Sopenharmony_ci	__u8 *data;
14188c2ecf20Sopenharmony_ci	int	pkt_received = 0;
14198c2ecf20Sopenharmony_ci	struct	bufdesc_ex *ebdp = NULL;
14208c2ecf20Sopenharmony_ci	bool	vlan_packet_rcvd = false;
14218c2ecf20Sopenharmony_ci	u16	vlan_tag;
14228c2ecf20Sopenharmony_ci	int	index = 0;
14238c2ecf20Sopenharmony_ci	bool	is_copybreak;
14248c2ecf20Sopenharmony_ci	bool	need_swap = fep->quirks & FEC_QUIRK_SWAP_FRAME;
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci#ifdef CONFIG_M532x
14278c2ecf20Sopenharmony_ci	flush_cache_all();
14288c2ecf20Sopenharmony_ci#endif
14298c2ecf20Sopenharmony_ci	rxq = fep->rx_queue[queue_id];
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_ci	/* First, grab all of the stats for the incoming packet.
14328c2ecf20Sopenharmony_ci	 * These get messed up if we get called due to a busy condition.
14338c2ecf20Sopenharmony_ci	 */
14348c2ecf20Sopenharmony_ci	bdp = rxq->bd.cur;
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci	while (!((status = fec16_to_cpu(bdp->cbd_sc)) & BD_ENET_RX_EMPTY)) {
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_ci		if (pkt_received >= budget)
14398c2ecf20Sopenharmony_ci			break;
14408c2ecf20Sopenharmony_ci		pkt_received++;
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_ci		writel(FEC_ENET_RXF_GET(queue_id), fep->hwp + FEC_IEVENT);
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_ci		/* Check for errors. */
14458c2ecf20Sopenharmony_ci		status ^= BD_ENET_RX_LAST;
14468c2ecf20Sopenharmony_ci		if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO |
14478c2ecf20Sopenharmony_ci			   BD_ENET_RX_CR | BD_ENET_RX_OV | BD_ENET_RX_LAST |
14488c2ecf20Sopenharmony_ci			   BD_ENET_RX_CL)) {
14498c2ecf20Sopenharmony_ci			ndev->stats.rx_errors++;
14508c2ecf20Sopenharmony_ci			if (status & BD_ENET_RX_OV) {
14518c2ecf20Sopenharmony_ci				/* FIFO overrun */
14528c2ecf20Sopenharmony_ci				ndev->stats.rx_fifo_errors++;
14538c2ecf20Sopenharmony_ci				goto rx_processing_done;
14548c2ecf20Sopenharmony_ci			}
14558c2ecf20Sopenharmony_ci			if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH
14568c2ecf20Sopenharmony_ci						| BD_ENET_RX_LAST)) {
14578c2ecf20Sopenharmony_ci				/* Frame too long or too short. */
14588c2ecf20Sopenharmony_ci				ndev->stats.rx_length_errors++;
14598c2ecf20Sopenharmony_ci				if (status & BD_ENET_RX_LAST)
14608c2ecf20Sopenharmony_ci					netdev_err(ndev, "rcv is not +last\n");
14618c2ecf20Sopenharmony_ci			}
14628c2ecf20Sopenharmony_ci			if (status & BD_ENET_RX_CR)	/* CRC Error */
14638c2ecf20Sopenharmony_ci				ndev->stats.rx_crc_errors++;
14648c2ecf20Sopenharmony_ci			/* Report late collisions as a frame error. */
14658c2ecf20Sopenharmony_ci			if (status & (BD_ENET_RX_NO | BD_ENET_RX_CL))
14668c2ecf20Sopenharmony_ci				ndev->stats.rx_frame_errors++;
14678c2ecf20Sopenharmony_ci			goto rx_processing_done;
14688c2ecf20Sopenharmony_ci		}
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci		/* Process the incoming frame. */
14718c2ecf20Sopenharmony_ci		ndev->stats.rx_packets++;
14728c2ecf20Sopenharmony_ci		pkt_len = fec16_to_cpu(bdp->cbd_datlen);
14738c2ecf20Sopenharmony_ci		ndev->stats.rx_bytes += pkt_len;
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_ci		index = fec_enet_get_bd_index(bdp, &rxq->bd);
14768c2ecf20Sopenharmony_ci		skb = rxq->rx_skbuff[index];
14778c2ecf20Sopenharmony_ci
14788c2ecf20Sopenharmony_ci		/* The packet length includes FCS, but we don't want to
14798c2ecf20Sopenharmony_ci		 * include that when passing upstream as it messes up
14808c2ecf20Sopenharmony_ci		 * bridging applications.
14818c2ecf20Sopenharmony_ci		 */
14828c2ecf20Sopenharmony_ci		is_copybreak = fec_enet_copybreak(ndev, &skb, bdp, pkt_len - 4,
14838c2ecf20Sopenharmony_ci						  need_swap);
14848c2ecf20Sopenharmony_ci		if (!is_copybreak) {
14858c2ecf20Sopenharmony_ci			skb_new = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE);
14868c2ecf20Sopenharmony_ci			if (unlikely(!skb_new)) {
14878c2ecf20Sopenharmony_ci				ndev->stats.rx_dropped++;
14888c2ecf20Sopenharmony_ci				goto rx_processing_done;
14898c2ecf20Sopenharmony_ci			}
14908c2ecf20Sopenharmony_ci			dma_unmap_single(&fep->pdev->dev,
14918c2ecf20Sopenharmony_ci					 fec32_to_cpu(bdp->cbd_bufaddr),
14928c2ecf20Sopenharmony_ci					 FEC_ENET_RX_FRSIZE - fep->rx_align,
14938c2ecf20Sopenharmony_ci					 DMA_FROM_DEVICE);
14948c2ecf20Sopenharmony_ci		}
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci		prefetch(skb->data - NET_IP_ALIGN);
14978c2ecf20Sopenharmony_ci		skb_put(skb, pkt_len - 4);
14988c2ecf20Sopenharmony_ci		data = skb->data;
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci		if (!is_copybreak && need_swap)
15018c2ecf20Sopenharmony_ci			swap_buffer(data, pkt_len);
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci#if !defined(CONFIG_M5272)
15048c2ecf20Sopenharmony_ci		if (fep->quirks & FEC_QUIRK_HAS_RACC)
15058c2ecf20Sopenharmony_ci			data = skb_pull_inline(skb, 2);
15068c2ecf20Sopenharmony_ci#endif
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_ci		/* Extract the enhanced buffer descriptor */
15098c2ecf20Sopenharmony_ci		ebdp = NULL;
15108c2ecf20Sopenharmony_ci		if (fep->bufdesc_ex)
15118c2ecf20Sopenharmony_ci			ebdp = (struct bufdesc_ex *)bdp;
15128c2ecf20Sopenharmony_ci
15138c2ecf20Sopenharmony_ci		/* If this is a VLAN packet remove the VLAN Tag */
15148c2ecf20Sopenharmony_ci		vlan_packet_rcvd = false;
15158c2ecf20Sopenharmony_ci		if ((ndev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
15168c2ecf20Sopenharmony_ci		    fep->bufdesc_ex &&
15178c2ecf20Sopenharmony_ci		    (ebdp->cbd_esc & cpu_to_fec32(BD_ENET_RX_VLAN))) {
15188c2ecf20Sopenharmony_ci			/* Push and remove the vlan tag */
15198c2ecf20Sopenharmony_ci			struct vlan_hdr *vlan_header =
15208c2ecf20Sopenharmony_ci					(struct vlan_hdr *) (data + ETH_HLEN);
15218c2ecf20Sopenharmony_ci			vlan_tag = ntohs(vlan_header->h_vlan_TCI);
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci			vlan_packet_rcvd = true;
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ci			memmove(skb->data + VLAN_HLEN, data, ETH_ALEN * 2);
15268c2ecf20Sopenharmony_ci			skb_pull(skb, VLAN_HLEN);
15278c2ecf20Sopenharmony_ci		}
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_ci		skb->protocol = eth_type_trans(skb, ndev);
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_ci		/* Get receive timestamp from the skb */
15328c2ecf20Sopenharmony_ci		if (fep->hwts_rx_en && fep->bufdesc_ex)
15338c2ecf20Sopenharmony_ci			fec_enet_hwtstamp(fep, fec32_to_cpu(ebdp->ts),
15348c2ecf20Sopenharmony_ci					  skb_hwtstamps(skb));
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci		if (fep->bufdesc_ex &&
15378c2ecf20Sopenharmony_ci		    (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) {
15388c2ecf20Sopenharmony_ci			if (!(ebdp->cbd_esc & cpu_to_fec32(FLAG_RX_CSUM_ERROR))) {
15398c2ecf20Sopenharmony_ci				/* don't check it */
15408c2ecf20Sopenharmony_ci				skb->ip_summed = CHECKSUM_UNNECESSARY;
15418c2ecf20Sopenharmony_ci			} else {
15428c2ecf20Sopenharmony_ci				skb_checksum_none_assert(skb);
15438c2ecf20Sopenharmony_ci			}
15448c2ecf20Sopenharmony_ci		}
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci		/* Handle received VLAN packets */
15478c2ecf20Sopenharmony_ci		if (vlan_packet_rcvd)
15488c2ecf20Sopenharmony_ci			__vlan_hwaccel_put_tag(skb,
15498c2ecf20Sopenharmony_ci					       htons(ETH_P_8021Q),
15508c2ecf20Sopenharmony_ci					       vlan_tag);
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci		skb_record_rx_queue(skb, queue_id);
15538c2ecf20Sopenharmony_ci		napi_gro_receive(&fep->napi, skb);
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci		if (is_copybreak) {
15568c2ecf20Sopenharmony_ci			dma_sync_single_for_device(&fep->pdev->dev,
15578c2ecf20Sopenharmony_ci						   fec32_to_cpu(bdp->cbd_bufaddr),
15588c2ecf20Sopenharmony_ci						   FEC_ENET_RX_FRSIZE - fep->rx_align,
15598c2ecf20Sopenharmony_ci						   DMA_FROM_DEVICE);
15608c2ecf20Sopenharmony_ci		} else {
15618c2ecf20Sopenharmony_ci			rxq->rx_skbuff[index] = skb_new;
15628c2ecf20Sopenharmony_ci			fec_enet_new_rxbdp(ndev, bdp, skb_new);
15638c2ecf20Sopenharmony_ci		}
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_cirx_processing_done:
15668c2ecf20Sopenharmony_ci		/* Clear the status flags for this buffer */
15678c2ecf20Sopenharmony_ci		status &= ~BD_ENET_RX_STATS;
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_ci		/* Mark the buffer empty */
15708c2ecf20Sopenharmony_ci		status |= BD_ENET_RX_EMPTY;
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci		if (fep->bufdesc_ex) {
15738c2ecf20Sopenharmony_ci			struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
15748c2ecf20Sopenharmony_ci
15758c2ecf20Sopenharmony_ci			ebdp->cbd_esc = cpu_to_fec32(BD_ENET_RX_INT);
15768c2ecf20Sopenharmony_ci			ebdp->cbd_prot = 0;
15778c2ecf20Sopenharmony_ci			ebdp->cbd_bdu = 0;
15788c2ecf20Sopenharmony_ci		}
15798c2ecf20Sopenharmony_ci		/* Make sure the updates to rest of the descriptor are
15808c2ecf20Sopenharmony_ci		 * performed before transferring ownership.
15818c2ecf20Sopenharmony_ci		 */
15828c2ecf20Sopenharmony_ci		wmb();
15838c2ecf20Sopenharmony_ci		bdp->cbd_sc = cpu_to_fec16(status);
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_ci		/* Update BD pointer to next entry */
15868c2ecf20Sopenharmony_ci		bdp = fec_enet_get_nextdesc(bdp, &rxq->bd);
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci		/* Doing this here will keep the FEC running while we process
15898c2ecf20Sopenharmony_ci		 * incoming frames.  On a heavily loaded network, we should be
15908c2ecf20Sopenharmony_ci		 * able to keep up at the expense of system resources.
15918c2ecf20Sopenharmony_ci		 */
15928c2ecf20Sopenharmony_ci		writel(0, rxq->bd.reg_desc_active);
15938c2ecf20Sopenharmony_ci	}
15948c2ecf20Sopenharmony_ci	rxq->bd.cur = bdp;
15958c2ecf20Sopenharmony_ci	return pkt_received;
15968c2ecf20Sopenharmony_ci}
15978c2ecf20Sopenharmony_ci
15988c2ecf20Sopenharmony_cistatic int fec_enet_rx(struct net_device *ndev, int budget)
15998c2ecf20Sopenharmony_ci{
16008c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
16018c2ecf20Sopenharmony_ci	int i, done = 0;
16028c2ecf20Sopenharmony_ci
16038c2ecf20Sopenharmony_ci	/* Make sure that AVB queues are processed first. */
16048c2ecf20Sopenharmony_ci	for (i = fep->num_rx_queues - 1; i >= 0; i--)
16058c2ecf20Sopenharmony_ci		done += fec_enet_rx_queue(ndev, budget - done, i);
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_ci	return done;
16088c2ecf20Sopenharmony_ci}
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_cistatic bool fec_enet_collect_events(struct fec_enet_private *fep)
16118c2ecf20Sopenharmony_ci{
16128c2ecf20Sopenharmony_ci	uint int_events;
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci	int_events = readl(fep->hwp + FEC_IEVENT);
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_ci	/* Don't clear MDIO events, we poll for those */
16178c2ecf20Sopenharmony_ci	int_events &= ~FEC_ENET_MII;
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_ci	writel(int_events, fep->hwp + FEC_IEVENT);
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ci	return int_events != 0;
16228c2ecf20Sopenharmony_ci}
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_cistatic irqreturn_t
16258c2ecf20Sopenharmony_cifec_enet_interrupt(int irq, void *dev_id)
16268c2ecf20Sopenharmony_ci{
16278c2ecf20Sopenharmony_ci	struct net_device *ndev = dev_id;
16288c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
16298c2ecf20Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci	if (fec_enet_collect_events(fep) && fep->link) {
16328c2ecf20Sopenharmony_ci		ret = IRQ_HANDLED;
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci		if (napi_schedule_prep(&fep->napi)) {
16358c2ecf20Sopenharmony_ci			/* Disable interrupts */
16368c2ecf20Sopenharmony_ci			writel(0, fep->hwp + FEC_IMASK);
16378c2ecf20Sopenharmony_ci			__napi_schedule(&fep->napi);
16388c2ecf20Sopenharmony_ci		}
16398c2ecf20Sopenharmony_ci	}
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_ci	return ret;
16428c2ecf20Sopenharmony_ci}
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_cistatic int fec_enet_rx_napi(struct napi_struct *napi, int budget)
16458c2ecf20Sopenharmony_ci{
16468c2ecf20Sopenharmony_ci	struct net_device *ndev = napi->dev;
16478c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
16488c2ecf20Sopenharmony_ci	int done = 0;
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_ci	do {
16518c2ecf20Sopenharmony_ci		done += fec_enet_rx(ndev, budget - done);
16528c2ecf20Sopenharmony_ci		fec_enet_tx(ndev);
16538c2ecf20Sopenharmony_ci	} while ((done < budget) && fec_enet_collect_events(fep));
16548c2ecf20Sopenharmony_ci
16558c2ecf20Sopenharmony_ci	if (done < budget) {
16568c2ecf20Sopenharmony_ci		napi_complete_done(napi, done);
16578c2ecf20Sopenharmony_ci		writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
16588c2ecf20Sopenharmony_ci	}
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_ci	return done;
16618c2ecf20Sopenharmony_ci}
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */
16648c2ecf20Sopenharmony_cistatic void fec_get_mac(struct net_device *ndev)
16658c2ecf20Sopenharmony_ci{
16668c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
16678c2ecf20Sopenharmony_ci	struct fec_platform_data *pdata = dev_get_platdata(&fep->pdev->dev);
16688c2ecf20Sopenharmony_ci	unsigned char *iap, tmpaddr[ETH_ALEN];
16698c2ecf20Sopenharmony_ci
16708c2ecf20Sopenharmony_ci	/*
16718c2ecf20Sopenharmony_ci	 * try to get mac address in following order:
16728c2ecf20Sopenharmony_ci	 *
16738c2ecf20Sopenharmony_ci	 * 1) module parameter via kernel command line in form
16748c2ecf20Sopenharmony_ci	 *    fec.macaddr=0x00,0x04,0x9f,0x01,0x30,0xe0
16758c2ecf20Sopenharmony_ci	 */
16768c2ecf20Sopenharmony_ci	iap = macaddr;
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci	/*
16798c2ecf20Sopenharmony_ci	 * 2) from device tree data
16808c2ecf20Sopenharmony_ci	 */
16818c2ecf20Sopenharmony_ci	if (!is_valid_ether_addr(iap)) {
16828c2ecf20Sopenharmony_ci		struct device_node *np = fep->pdev->dev.of_node;
16838c2ecf20Sopenharmony_ci		if (np) {
16848c2ecf20Sopenharmony_ci			const char *mac = of_get_mac_address(np);
16858c2ecf20Sopenharmony_ci			if (!IS_ERR(mac))
16868c2ecf20Sopenharmony_ci				iap = (unsigned char *) mac;
16878c2ecf20Sopenharmony_ci		}
16888c2ecf20Sopenharmony_ci	}
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci	/*
16918c2ecf20Sopenharmony_ci	 * 3) from flash or fuse (via platform data)
16928c2ecf20Sopenharmony_ci	 */
16938c2ecf20Sopenharmony_ci	if (!is_valid_ether_addr(iap)) {
16948c2ecf20Sopenharmony_ci#ifdef CONFIG_M5272
16958c2ecf20Sopenharmony_ci		if (FEC_FLASHMAC)
16968c2ecf20Sopenharmony_ci			iap = (unsigned char *)FEC_FLASHMAC;
16978c2ecf20Sopenharmony_ci#else
16988c2ecf20Sopenharmony_ci		if (pdata)
16998c2ecf20Sopenharmony_ci			iap = (unsigned char *)&pdata->mac;
17008c2ecf20Sopenharmony_ci#endif
17018c2ecf20Sopenharmony_ci	}
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_ci	/*
17048c2ecf20Sopenharmony_ci	 * 4) FEC mac registers set by bootloader
17058c2ecf20Sopenharmony_ci	 */
17068c2ecf20Sopenharmony_ci	if (!is_valid_ether_addr(iap)) {
17078c2ecf20Sopenharmony_ci		*((__be32 *) &tmpaddr[0]) =
17088c2ecf20Sopenharmony_ci			cpu_to_be32(readl(fep->hwp + FEC_ADDR_LOW));
17098c2ecf20Sopenharmony_ci		*((__be16 *) &tmpaddr[4]) =
17108c2ecf20Sopenharmony_ci			cpu_to_be16(readl(fep->hwp + FEC_ADDR_HIGH) >> 16);
17118c2ecf20Sopenharmony_ci		iap = &tmpaddr[0];
17128c2ecf20Sopenharmony_ci	}
17138c2ecf20Sopenharmony_ci
17148c2ecf20Sopenharmony_ci	/*
17158c2ecf20Sopenharmony_ci	 * 5) random mac address
17168c2ecf20Sopenharmony_ci	 */
17178c2ecf20Sopenharmony_ci	if (!is_valid_ether_addr(iap)) {
17188c2ecf20Sopenharmony_ci		/* Report it and use a random ethernet address instead */
17198c2ecf20Sopenharmony_ci		dev_err(&fep->pdev->dev, "Invalid MAC address: %pM\n", iap);
17208c2ecf20Sopenharmony_ci		eth_hw_addr_random(ndev);
17218c2ecf20Sopenharmony_ci		dev_info(&fep->pdev->dev, "Using random MAC address: %pM\n",
17228c2ecf20Sopenharmony_ci			 ndev->dev_addr);
17238c2ecf20Sopenharmony_ci		return;
17248c2ecf20Sopenharmony_ci	}
17258c2ecf20Sopenharmony_ci
17268c2ecf20Sopenharmony_ci	memcpy(ndev->dev_addr, iap, ETH_ALEN);
17278c2ecf20Sopenharmony_ci
17288c2ecf20Sopenharmony_ci	/* Adjust MAC if using macaddr */
17298c2ecf20Sopenharmony_ci	if (iap == macaddr)
17308c2ecf20Sopenharmony_ci		 ndev->dev_addr[ETH_ALEN-1] = macaddr[ETH_ALEN-1] + fep->dev_id;
17318c2ecf20Sopenharmony_ci}
17328c2ecf20Sopenharmony_ci
17338c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_ci/*
17368c2ecf20Sopenharmony_ci * Phy section
17378c2ecf20Sopenharmony_ci */
17388c2ecf20Sopenharmony_cistatic void fec_enet_adjust_link(struct net_device *ndev)
17398c2ecf20Sopenharmony_ci{
17408c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
17418c2ecf20Sopenharmony_ci	struct phy_device *phy_dev = ndev->phydev;
17428c2ecf20Sopenharmony_ci	int status_change = 0;
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_ci	/*
17458c2ecf20Sopenharmony_ci	 * If the netdev is down, or is going down, we're not interested
17468c2ecf20Sopenharmony_ci	 * in link state events, so just mark our idea of the link as down
17478c2ecf20Sopenharmony_ci	 * and ignore the event.
17488c2ecf20Sopenharmony_ci	 */
17498c2ecf20Sopenharmony_ci	if (!netif_running(ndev) || !netif_device_present(ndev)) {
17508c2ecf20Sopenharmony_ci		fep->link = 0;
17518c2ecf20Sopenharmony_ci	} else if (phy_dev->link) {
17528c2ecf20Sopenharmony_ci		if (!fep->link) {
17538c2ecf20Sopenharmony_ci			fep->link = phy_dev->link;
17548c2ecf20Sopenharmony_ci			status_change = 1;
17558c2ecf20Sopenharmony_ci		}
17568c2ecf20Sopenharmony_ci
17578c2ecf20Sopenharmony_ci		if (fep->full_duplex != phy_dev->duplex) {
17588c2ecf20Sopenharmony_ci			fep->full_duplex = phy_dev->duplex;
17598c2ecf20Sopenharmony_ci			status_change = 1;
17608c2ecf20Sopenharmony_ci		}
17618c2ecf20Sopenharmony_ci
17628c2ecf20Sopenharmony_ci		if (phy_dev->speed != fep->speed) {
17638c2ecf20Sopenharmony_ci			fep->speed = phy_dev->speed;
17648c2ecf20Sopenharmony_ci			status_change = 1;
17658c2ecf20Sopenharmony_ci		}
17668c2ecf20Sopenharmony_ci
17678c2ecf20Sopenharmony_ci		/* if any of the above changed restart the FEC */
17688c2ecf20Sopenharmony_ci		if (status_change) {
17698c2ecf20Sopenharmony_ci			netif_stop_queue(ndev);
17708c2ecf20Sopenharmony_ci			napi_disable(&fep->napi);
17718c2ecf20Sopenharmony_ci			netif_tx_lock_bh(ndev);
17728c2ecf20Sopenharmony_ci			fec_restart(ndev);
17738c2ecf20Sopenharmony_ci			netif_tx_wake_all_queues(ndev);
17748c2ecf20Sopenharmony_ci			netif_tx_unlock_bh(ndev);
17758c2ecf20Sopenharmony_ci			napi_enable(&fep->napi);
17768c2ecf20Sopenharmony_ci		}
17778c2ecf20Sopenharmony_ci	} else {
17788c2ecf20Sopenharmony_ci		if (fep->link) {
17798c2ecf20Sopenharmony_ci			netif_stop_queue(ndev);
17808c2ecf20Sopenharmony_ci			napi_disable(&fep->napi);
17818c2ecf20Sopenharmony_ci			netif_tx_lock_bh(ndev);
17828c2ecf20Sopenharmony_ci			fec_stop(ndev);
17838c2ecf20Sopenharmony_ci			netif_tx_unlock_bh(ndev);
17848c2ecf20Sopenharmony_ci			napi_enable(&fep->napi);
17858c2ecf20Sopenharmony_ci			fep->link = phy_dev->link;
17868c2ecf20Sopenharmony_ci			status_change = 1;
17878c2ecf20Sopenharmony_ci		}
17888c2ecf20Sopenharmony_ci	}
17898c2ecf20Sopenharmony_ci
17908c2ecf20Sopenharmony_ci	if (status_change)
17918c2ecf20Sopenharmony_ci		phy_print_status(phy_dev);
17928c2ecf20Sopenharmony_ci}
17938c2ecf20Sopenharmony_ci
17948c2ecf20Sopenharmony_cistatic int fec_enet_mdio_wait(struct fec_enet_private *fep)
17958c2ecf20Sopenharmony_ci{
17968c2ecf20Sopenharmony_ci	uint ievent;
17978c2ecf20Sopenharmony_ci	int ret;
17988c2ecf20Sopenharmony_ci
17998c2ecf20Sopenharmony_ci	ret = readl_poll_timeout_atomic(fep->hwp + FEC_IEVENT, ievent,
18008c2ecf20Sopenharmony_ci					ievent & FEC_ENET_MII, 2, 30000);
18018c2ecf20Sopenharmony_ci
18028c2ecf20Sopenharmony_ci	if (!ret)
18038c2ecf20Sopenharmony_ci		writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT);
18048c2ecf20Sopenharmony_ci
18058c2ecf20Sopenharmony_ci	return ret;
18068c2ecf20Sopenharmony_ci}
18078c2ecf20Sopenharmony_ci
18088c2ecf20Sopenharmony_cistatic int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
18098c2ecf20Sopenharmony_ci{
18108c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = bus->priv;
18118c2ecf20Sopenharmony_ci	struct device *dev = &fep->pdev->dev;
18128c2ecf20Sopenharmony_ci	int ret = 0, frame_start, frame_addr, frame_op;
18138c2ecf20Sopenharmony_ci	bool is_c45 = !!(regnum & MII_ADDR_C45);
18148c2ecf20Sopenharmony_ci
18158c2ecf20Sopenharmony_ci	ret = pm_runtime_resume_and_get(dev);
18168c2ecf20Sopenharmony_ci	if (ret < 0)
18178c2ecf20Sopenharmony_ci		return ret;
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_ci	if (is_c45) {
18208c2ecf20Sopenharmony_ci		frame_start = FEC_MMFR_ST_C45;
18218c2ecf20Sopenharmony_ci
18228c2ecf20Sopenharmony_ci		/* write address */
18238c2ecf20Sopenharmony_ci		frame_addr = (regnum >> 16);
18248c2ecf20Sopenharmony_ci		writel(frame_start | FEC_MMFR_OP_ADDR_WRITE |
18258c2ecf20Sopenharmony_ci		       FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) |
18268c2ecf20Sopenharmony_ci		       FEC_MMFR_TA | (regnum & 0xFFFF),
18278c2ecf20Sopenharmony_ci		       fep->hwp + FEC_MII_DATA);
18288c2ecf20Sopenharmony_ci
18298c2ecf20Sopenharmony_ci		/* wait for end of transfer */
18308c2ecf20Sopenharmony_ci		ret = fec_enet_mdio_wait(fep);
18318c2ecf20Sopenharmony_ci		if (ret) {
18328c2ecf20Sopenharmony_ci			netdev_err(fep->netdev, "MDIO address write timeout\n");
18338c2ecf20Sopenharmony_ci			goto out;
18348c2ecf20Sopenharmony_ci		}
18358c2ecf20Sopenharmony_ci
18368c2ecf20Sopenharmony_ci		frame_op = FEC_MMFR_OP_READ_C45;
18378c2ecf20Sopenharmony_ci
18388c2ecf20Sopenharmony_ci	} else {
18398c2ecf20Sopenharmony_ci		/* C22 read */
18408c2ecf20Sopenharmony_ci		frame_op = FEC_MMFR_OP_READ;
18418c2ecf20Sopenharmony_ci		frame_start = FEC_MMFR_ST;
18428c2ecf20Sopenharmony_ci		frame_addr = regnum;
18438c2ecf20Sopenharmony_ci	}
18448c2ecf20Sopenharmony_ci
18458c2ecf20Sopenharmony_ci	/* start a read op */
18468c2ecf20Sopenharmony_ci	writel(frame_start | frame_op |
18478c2ecf20Sopenharmony_ci		FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) |
18488c2ecf20Sopenharmony_ci		FEC_MMFR_TA, fep->hwp + FEC_MII_DATA);
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci	/* wait for end of transfer */
18518c2ecf20Sopenharmony_ci	ret = fec_enet_mdio_wait(fep);
18528c2ecf20Sopenharmony_ci	if (ret) {
18538c2ecf20Sopenharmony_ci		netdev_err(fep->netdev, "MDIO read timeout\n");
18548c2ecf20Sopenharmony_ci		goto out;
18558c2ecf20Sopenharmony_ci	}
18568c2ecf20Sopenharmony_ci
18578c2ecf20Sopenharmony_ci	ret = FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA));
18588c2ecf20Sopenharmony_ci
18598c2ecf20Sopenharmony_ciout:
18608c2ecf20Sopenharmony_ci	pm_runtime_mark_last_busy(dev);
18618c2ecf20Sopenharmony_ci	pm_runtime_put_autosuspend(dev);
18628c2ecf20Sopenharmony_ci
18638c2ecf20Sopenharmony_ci	return ret;
18648c2ecf20Sopenharmony_ci}
18658c2ecf20Sopenharmony_ci
18668c2ecf20Sopenharmony_cistatic int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
18678c2ecf20Sopenharmony_ci			   u16 value)
18688c2ecf20Sopenharmony_ci{
18698c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = bus->priv;
18708c2ecf20Sopenharmony_ci	struct device *dev = &fep->pdev->dev;
18718c2ecf20Sopenharmony_ci	int ret, frame_start, frame_addr;
18728c2ecf20Sopenharmony_ci	bool is_c45 = !!(regnum & MII_ADDR_C45);
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ci	ret = pm_runtime_resume_and_get(dev);
18758c2ecf20Sopenharmony_ci	if (ret < 0)
18768c2ecf20Sopenharmony_ci		return ret;
18778c2ecf20Sopenharmony_ci
18788c2ecf20Sopenharmony_ci	if (is_c45) {
18798c2ecf20Sopenharmony_ci		frame_start = FEC_MMFR_ST_C45;
18808c2ecf20Sopenharmony_ci
18818c2ecf20Sopenharmony_ci		/* write address */
18828c2ecf20Sopenharmony_ci		frame_addr = (regnum >> 16);
18838c2ecf20Sopenharmony_ci		writel(frame_start | FEC_MMFR_OP_ADDR_WRITE |
18848c2ecf20Sopenharmony_ci		       FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) |
18858c2ecf20Sopenharmony_ci		       FEC_MMFR_TA | (regnum & 0xFFFF),
18868c2ecf20Sopenharmony_ci		       fep->hwp + FEC_MII_DATA);
18878c2ecf20Sopenharmony_ci
18888c2ecf20Sopenharmony_ci		/* wait for end of transfer */
18898c2ecf20Sopenharmony_ci		ret = fec_enet_mdio_wait(fep);
18908c2ecf20Sopenharmony_ci		if (ret) {
18918c2ecf20Sopenharmony_ci			netdev_err(fep->netdev, "MDIO address write timeout\n");
18928c2ecf20Sopenharmony_ci			goto out;
18938c2ecf20Sopenharmony_ci		}
18948c2ecf20Sopenharmony_ci	} else {
18958c2ecf20Sopenharmony_ci		/* C22 write */
18968c2ecf20Sopenharmony_ci		frame_start = FEC_MMFR_ST;
18978c2ecf20Sopenharmony_ci		frame_addr = regnum;
18988c2ecf20Sopenharmony_ci	}
18998c2ecf20Sopenharmony_ci
19008c2ecf20Sopenharmony_ci	/* start a write op */
19018c2ecf20Sopenharmony_ci	writel(frame_start | FEC_MMFR_OP_WRITE |
19028c2ecf20Sopenharmony_ci		FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) |
19038c2ecf20Sopenharmony_ci		FEC_MMFR_TA | FEC_MMFR_DATA(value),
19048c2ecf20Sopenharmony_ci		fep->hwp + FEC_MII_DATA);
19058c2ecf20Sopenharmony_ci
19068c2ecf20Sopenharmony_ci	/* wait for end of transfer */
19078c2ecf20Sopenharmony_ci	ret = fec_enet_mdio_wait(fep);
19088c2ecf20Sopenharmony_ci	if (ret)
19098c2ecf20Sopenharmony_ci		netdev_err(fep->netdev, "MDIO write timeout\n");
19108c2ecf20Sopenharmony_ci
19118c2ecf20Sopenharmony_ciout:
19128c2ecf20Sopenharmony_ci	pm_runtime_mark_last_busy(dev);
19138c2ecf20Sopenharmony_ci	pm_runtime_put_autosuspend(dev);
19148c2ecf20Sopenharmony_ci
19158c2ecf20Sopenharmony_ci	return ret;
19168c2ecf20Sopenharmony_ci}
19178c2ecf20Sopenharmony_ci
19188c2ecf20Sopenharmony_cistatic void fec_enet_phy_reset_after_clk_enable(struct net_device *ndev)
19198c2ecf20Sopenharmony_ci{
19208c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
19218c2ecf20Sopenharmony_ci	struct phy_device *phy_dev = ndev->phydev;
19228c2ecf20Sopenharmony_ci
19238c2ecf20Sopenharmony_ci	if (phy_dev) {
19248c2ecf20Sopenharmony_ci		phy_reset_after_clk_enable(phy_dev);
19258c2ecf20Sopenharmony_ci	} else if (fep->phy_node) {
19268c2ecf20Sopenharmony_ci		/*
19278c2ecf20Sopenharmony_ci		 * If the PHY still is not bound to the MAC, but there is
19288c2ecf20Sopenharmony_ci		 * OF PHY node and a matching PHY device instance already,
19298c2ecf20Sopenharmony_ci		 * use the OF PHY node to obtain the PHY device instance,
19308c2ecf20Sopenharmony_ci		 * and then use that PHY device instance when triggering
19318c2ecf20Sopenharmony_ci		 * the PHY reset.
19328c2ecf20Sopenharmony_ci		 */
19338c2ecf20Sopenharmony_ci		phy_dev = of_phy_find_device(fep->phy_node);
19348c2ecf20Sopenharmony_ci		phy_reset_after_clk_enable(phy_dev);
19358c2ecf20Sopenharmony_ci		put_device(&phy_dev->mdio.dev);
19368c2ecf20Sopenharmony_ci	}
19378c2ecf20Sopenharmony_ci}
19388c2ecf20Sopenharmony_ci
19398c2ecf20Sopenharmony_cistatic int fec_enet_clk_enable(struct net_device *ndev, bool enable)
19408c2ecf20Sopenharmony_ci{
19418c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
19428c2ecf20Sopenharmony_ci	int ret;
19438c2ecf20Sopenharmony_ci
19448c2ecf20Sopenharmony_ci	if (enable) {
19458c2ecf20Sopenharmony_ci		ret = clk_prepare_enable(fep->clk_enet_out);
19468c2ecf20Sopenharmony_ci		if (ret)
19478c2ecf20Sopenharmony_ci			return ret;
19488c2ecf20Sopenharmony_ci
19498c2ecf20Sopenharmony_ci		if (fep->clk_ptp) {
19508c2ecf20Sopenharmony_ci			mutex_lock(&fep->ptp_clk_mutex);
19518c2ecf20Sopenharmony_ci			ret = clk_prepare_enable(fep->clk_ptp);
19528c2ecf20Sopenharmony_ci			if (ret) {
19538c2ecf20Sopenharmony_ci				mutex_unlock(&fep->ptp_clk_mutex);
19548c2ecf20Sopenharmony_ci				goto failed_clk_ptp;
19558c2ecf20Sopenharmony_ci			} else {
19568c2ecf20Sopenharmony_ci				fep->ptp_clk_on = true;
19578c2ecf20Sopenharmony_ci			}
19588c2ecf20Sopenharmony_ci			mutex_unlock(&fep->ptp_clk_mutex);
19598c2ecf20Sopenharmony_ci		}
19608c2ecf20Sopenharmony_ci
19618c2ecf20Sopenharmony_ci		ret = clk_prepare_enable(fep->clk_ref);
19628c2ecf20Sopenharmony_ci		if (ret)
19638c2ecf20Sopenharmony_ci			goto failed_clk_ref;
19648c2ecf20Sopenharmony_ci
19658c2ecf20Sopenharmony_ci		fec_enet_phy_reset_after_clk_enable(ndev);
19668c2ecf20Sopenharmony_ci	} else {
19678c2ecf20Sopenharmony_ci		clk_disable_unprepare(fep->clk_enet_out);
19688c2ecf20Sopenharmony_ci		if (fep->clk_ptp) {
19698c2ecf20Sopenharmony_ci			mutex_lock(&fep->ptp_clk_mutex);
19708c2ecf20Sopenharmony_ci			clk_disable_unprepare(fep->clk_ptp);
19718c2ecf20Sopenharmony_ci			fep->ptp_clk_on = false;
19728c2ecf20Sopenharmony_ci			mutex_unlock(&fep->ptp_clk_mutex);
19738c2ecf20Sopenharmony_ci		}
19748c2ecf20Sopenharmony_ci		clk_disable_unprepare(fep->clk_ref);
19758c2ecf20Sopenharmony_ci	}
19768c2ecf20Sopenharmony_ci
19778c2ecf20Sopenharmony_ci	return 0;
19788c2ecf20Sopenharmony_ci
19798c2ecf20Sopenharmony_cifailed_clk_ref:
19808c2ecf20Sopenharmony_ci	if (fep->clk_ptp) {
19818c2ecf20Sopenharmony_ci		mutex_lock(&fep->ptp_clk_mutex);
19828c2ecf20Sopenharmony_ci		clk_disable_unprepare(fep->clk_ptp);
19838c2ecf20Sopenharmony_ci		fep->ptp_clk_on = false;
19848c2ecf20Sopenharmony_ci		mutex_unlock(&fep->ptp_clk_mutex);
19858c2ecf20Sopenharmony_ci	}
19868c2ecf20Sopenharmony_cifailed_clk_ptp:
19878c2ecf20Sopenharmony_ci	clk_disable_unprepare(fep->clk_enet_out);
19888c2ecf20Sopenharmony_ci
19898c2ecf20Sopenharmony_ci	return ret;
19908c2ecf20Sopenharmony_ci}
19918c2ecf20Sopenharmony_ci
19928c2ecf20Sopenharmony_cistatic int fec_enet_mii_probe(struct net_device *ndev)
19938c2ecf20Sopenharmony_ci{
19948c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
19958c2ecf20Sopenharmony_ci	struct phy_device *phy_dev = NULL;
19968c2ecf20Sopenharmony_ci	char mdio_bus_id[MII_BUS_ID_SIZE];
19978c2ecf20Sopenharmony_ci	char phy_name[MII_BUS_ID_SIZE + 3];
19988c2ecf20Sopenharmony_ci	int phy_id;
19998c2ecf20Sopenharmony_ci	int dev_id = fep->dev_id;
20008c2ecf20Sopenharmony_ci
20018c2ecf20Sopenharmony_ci	if (fep->phy_node) {
20028c2ecf20Sopenharmony_ci		phy_dev = of_phy_connect(ndev, fep->phy_node,
20038c2ecf20Sopenharmony_ci					 &fec_enet_adjust_link, 0,
20048c2ecf20Sopenharmony_ci					 fep->phy_interface);
20058c2ecf20Sopenharmony_ci		if (!phy_dev) {
20068c2ecf20Sopenharmony_ci			netdev_err(ndev, "Unable to connect to phy\n");
20078c2ecf20Sopenharmony_ci			return -ENODEV;
20088c2ecf20Sopenharmony_ci		}
20098c2ecf20Sopenharmony_ci	} else {
20108c2ecf20Sopenharmony_ci		/* check for attached phy */
20118c2ecf20Sopenharmony_ci		for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) {
20128c2ecf20Sopenharmony_ci			if (!mdiobus_is_registered_device(fep->mii_bus, phy_id))
20138c2ecf20Sopenharmony_ci				continue;
20148c2ecf20Sopenharmony_ci			if (dev_id--)
20158c2ecf20Sopenharmony_ci				continue;
20168c2ecf20Sopenharmony_ci			strlcpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE);
20178c2ecf20Sopenharmony_ci			break;
20188c2ecf20Sopenharmony_ci		}
20198c2ecf20Sopenharmony_ci
20208c2ecf20Sopenharmony_ci		if (phy_id >= PHY_MAX_ADDR) {
20218c2ecf20Sopenharmony_ci			netdev_info(ndev, "no PHY, assuming direct connection to switch\n");
20228c2ecf20Sopenharmony_ci			strlcpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE);
20238c2ecf20Sopenharmony_ci			phy_id = 0;
20248c2ecf20Sopenharmony_ci		}
20258c2ecf20Sopenharmony_ci
20268c2ecf20Sopenharmony_ci		snprintf(phy_name, sizeof(phy_name),
20278c2ecf20Sopenharmony_ci			 PHY_ID_FMT, mdio_bus_id, phy_id);
20288c2ecf20Sopenharmony_ci		phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link,
20298c2ecf20Sopenharmony_ci				      fep->phy_interface);
20308c2ecf20Sopenharmony_ci	}
20318c2ecf20Sopenharmony_ci
20328c2ecf20Sopenharmony_ci	if (IS_ERR(phy_dev)) {
20338c2ecf20Sopenharmony_ci		netdev_err(ndev, "could not attach to PHY\n");
20348c2ecf20Sopenharmony_ci		return PTR_ERR(phy_dev);
20358c2ecf20Sopenharmony_ci	}
20368c2ecf20Sopenharmony_ci
20378c2ecf20Sopenharmony_ci	/* mask with MAC supported features */
20388c2ecf20Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_HAS_GBIT) {
20398c2ecf20Sopenharmony_ci		phy_set_max_speed(phy_dev, 1000);
20408c2ecf20Sopenharmony_ci		phy_remove_link_mode(phy_dev,
20418c2ecf20Sopenharmony_ci				     ETHTOOL_LINK_MODE_1000baseT_Half_BIT);
20428c2ecf20Sopenharmony_ci#if !defined(CONFIG_M5272)
20438c2ecf20Sopenharmony_ci		phy_support_sym_pause(phy_dev);
20448c2ecf20Sopenharmony_ci#endif
20458c2ecf20Sopenharmony_ci	}
20468c2ecf20Sopenharmony_ci	else
20478c2ecf20Sopenharmony_ci		phy_set_max_speed(phy_dev, 100);
20488c2ecf20Sopenharmony_ci
20498c2ecf20Sopenharmony_ci	fep->link = 0;
20508c2ecf20Sopenharmony_ci	fep->full_duplex = 0;
20518c2ecf20Sopenharmony_ci
20528c2ecf20Sopenharmony_ci	phy_attached_info(phy_dev);
20538c2ecf20Sopenharmony_ci
20548c2ecf20Sopenharmony_ci	return 0;
20558c2ecf20Sopenharmony_ci}
20568c2ecf20Sopenharmony_ci
20578c2ecf20Sopenharmony_cistatic int fec_enet_mii_init(struct platform_device *pdev)
20588c2ecf20Sopenharmony_ci{
20598c2ecf20Sopenharmony_ci	static struct mii_bus *fec0_mii_bus;
20608c2ecf20Sopenharmony_ci	struct net_device *ndev = platform_get_drvdata(pdev);
20618c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
20628c2ecf20Sopenharmony_ci	bool suppress_preamble = false;
20638c2ecf20Sopenharmony_ci	struct device_node *node;
20648c2ecf20Sopenharmony_ci	int err = -ENXIO;
20658c2ecf20Sopenharmony_ci	u32 mii_speed, holdtime;
20668c2ecf20Sopenharmony_ci	u32 bus_freq;
20678c2ecf20Sopenharmony_ci
20688c2ecf20Sopenharmony_ci	/*
20698c2ecf20Sopenharmony_ci	 * The i.MX28 dual fec interfaces are not equal.
20708c2ecf20Sopenharmony_ci	 * Here are the differences:
20718c2ecf20Sopenharmony_ci	 *
20728c2ecf20Sopenharmony_ci	 *  - fec0 supports MII & RMII modes while fec1 only supports RMII
20738c2ecf20Sopenharmony_ci	 *  - fec0 acts as the 1588 time master while fec1 is slave
20748c2ecf20Sopenharmony_ci	 *  - external phys can only be configured by fec0
20758c2ecf20Sopenharmony_ci	 *
20768c2ecf20Sopenharmony_ci	 * That is to say fec1 can not work independently. It only works
20778c2ecf20Sopenharmony_ci	 * when fec0 is working. The reason behind this design is that the
20788c2ecf20Sopenharmony_ci	 * second interface is added primarily for Switch mode.
20798c2ecf20Sopenharmony_ci	 *
20808c2ecf20Sopenharmony_ci	 * Because of the last point above, both phys are attached on fec0
20818c2ecf20Sopenharmony_ci	 * mdio interface in board design, and need to be configured by
20828c2ecf20Sopenharmony_ci	 * fec0 mii_bus.
20838c2ecf20Sopenharmony_ci	 */
20848c2ecf20Sopenharmony_ci	if ((fep->quirks & FEC_QUIRK_SINGLE_MDIO) && fep->dev_id > 0) {
20858c2ecf20Sopenharmony_ci		/* fec1 uses fec0 mii_bus */
20868c2ecf20Sopenharmony_ci		if (mii_cnt && fec0_mii_bus) {
20878c2ecf20Sopenharmony_ci			fep->mii_bus = fec0_mii_bus;
20888c2ecf20Sopenharmony_ci			mii_cnt++;
20898c2ecf20Sopenharmony_ci			return 0;
20908c2ecf20Sopenharmony_ci		}
20918c2ecf20Sopenharmony_ci		return -ENOENT;
20928c2ecf20Sopenharmony_ci	}
20938c2ecf20Sopenharmony_ci
20948c2ecf20Sopenharmony_ci	bus_freq = 2500000; /* 2.5MHz by default */
20958c2ecf20Sopenharmony_ci	node = of_get_child_by_name(pdev->dev.of_node, "mdio");
20968c2ecf20Sopenharmony_ci	if (node) {
20978c2ecf20Sopenharmony_ci		of_property_read_u32(node, "clock-frequency", &bus_freq);
20988c2ecf20Sopenharmony_ci		suppress_preamble = of_property_read_bool(node,
20998c2ecf20Sopenharmony_ci							  "suppress-preamble");
21008c2ecf20Sopenharmony_ci	}
21018c2ecf20Sopenharmony_ci
21028c2ecf20Sopenharmony_ci	/*
21038c2ecf20Sopenharmony_ci	 * Set MII speed (= clk_get_rate() / 2 * phy_speed)
21048c2ecf20Sopenharmony_ci	 *
21058c2ecf20Sopenharmony_ci	 * The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while
21068c2ecf20Sopenharmony_ci	 * for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'.  The i.MX28
21078c2ecf20Sopenharmony_ci	 * Reference Manual has an error on this, and gets fixed on i.MX6Q
21088c2ecf20Sopenharmony_ci	 * document.
21098c2ecf20Sopenharmony_ci	 */
21108c2ecf20Sopenharmony_ci	mii_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), bus_freq * 2);
21118c2ecf20Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_ENET_MAC)
21128c2ecf20Sopenharmony_ci		mii_speed--;
21138c2ecf20Sopenharmony_ci	if (mii_speed > 63) {
21148c2ecf20Sopenharmony_ci		dev_err(&pdev->dev,
21158c2ecf20Sopenharmony_ci			"fec clock (%lu) too fast to get right mii speed\n",
21168c2ecf20Sopenharmony_ci			clk_get_rate(fep->clk_ipg));
21178c2ecf20Sopenharmony_ci		err = -EINVAL;
21188c2ecf20Sopenharmony_ci		goto err_out;
21198c2ecf20Sopenharmony_ci	}
21208c2ecf20Sopenharmony_ci
21218c2ecf20Sopenharmony_ci	/*
21228c2ecf20Sopenharmony_ci	 * The i.MX28 and i.MX6 types have another filed in the MSCR (aka
21238c2ecf20Sopenharmony_ci	 * MII_SPEED) register that defines the MDIO output hold time. Earlier
21248c2ecf20Sopenharmony_ci	 * versions are RAZ there, so just ignore the difference and write the
21258c2ecf20Sopenharmony_ci	 * register always.
21268c2ecf20Sopenharmony_ci	 * The minimal hold time according to IEE802.3 (clause 22) is 10 ns.
21278c2ecf20Sopenharmony_ci	 * HOLDTIME + 1 is the number of clk cycles the fec is holding the
21288c2ecf20Sopenharmony_ci	 * output.
21298c2ecf20Sopenharmony_ci	 * The HOLDTIME bitfield takes values between 0 and 7 (inclusive).
21308c2ecf20Sopenharmony_ci	 * Given that ceil(clkrate / 5000000) <= 64, the calculation for
21318c2ecf20Sopenharmony_ci	 * holdtime cannot result in a value greater than 3.
21328c2ecf20Sopenharmony_ci	 */
21338c2ecf20Sopenharmony_ci	holdtime = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 100000000) - 1;
21348c2ecf20Sopenharmony_ci
21358c2ecf20Sopenharmony_ci	fep->phy_speed = mii_speed << 1 | holdtime << 8;
21368c2ecf20Sopenharmony_ci
21378c2ecf20Sopenharmony_ci	if (suppress_preamble)
21388c2ecf20Sopenharmony_ci		fep->phy_speed |= BIT(7);
21398c2ecf20Sopenharmony_ci
21408c2ecf20Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_CLEAR_SETUP_MII) {
21418c2ecf20Sopenharmony_ci		/* Clear MMFR to avoid to generate MII event by writing MSCR.
21428c2ecf20Sopenharmony_ci		 * MII event generation condition:
21438c2ecf20Sopenharmony_ci		 * - writing MSCR:
21448c2ecf20Sopenharmony_ci		 *	- mmfr[31:0]_not_zero & mscr[7:0]_is_zero &
21458c2ecf20Sopenharmony_ci		 *	  mscr_reg_data_in[7:0] != 0
21468c2ecf20Sopenharmony_ci		 * - writing MMFR:
21478c2ecf20Sopenharmony_ci		 *	- mscr[7:0]_not_zero
21488c2ecf20Sopenharmony_ci		 */
21498c2ecf20Sopenharmony_ci		writel(0, fep->hwp + FEC_MII_DATA);
21508c2ecf20Sopenharmony_ci	}
21518c2ecf20Sopenharmony_ci
21528c2ecf20Sopenharmony_ci	writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
21538c2ecf20Sopenharmony_ci
21548c2ecf20Sopenharmony_ci	/* Clear any pending transaction complete indication */
21558c2ecf20Sopenharmony_ci	writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT);
21568c2ecf20Sopenharmony_ci
21578c2ecf20Sopenharmony_ci	fep->mii_bus = mdiobus_alloc();
21588c2ecf20Sopenharmony_ci	if (fep->mii_bus == NULL) {
21598c2ecf20Sopenharmony_ci		err = -ENOMEM;
21608c2ecf20Sopenharmony_ci		goto err_out;
21618c2ecf20Sopenharmony_ci	}
21628c2ecf20Sopenharmony_ci
21638c2ecf20Sopenharmony_ci	fep->mii_bus->name = "fec_enet_mii_bus";
21648c2ecf20Sopenharmony_ci	fep->mii_bus->read = fec_enet_mdio_read;
21658c2ecf20Sopenharmony_ci	fep->mii_bus->write = fec_enet_mdio_write;
21668c2ecf20Sopenharmony_ci	snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
21678c2ecf20Sopenharmony_ci		pdev->name, fep->dev_id + 1);
21688c2ecf20Sopenharmony_ci	fep->mii_bus->priv = fep;
21698c2ecf20Sopenharmony_ci	fep->mii_bus->parent = &pdev->dev;
21708c2ecf20Sopenharmony_ci
21718c2ecf20Sopenharmony_ci	err = of_mdiobus_register(fep->mii_bus, node);
21728c2ecf20Sopenharmony_ci	if (err)
21738c2ecf20Sopenharmony_ci		goto err_out_free_mdiobus;
21748c2ecf20Sopenharmony_ci	of_node_put(node);
21758c2ecf20Sopenharmony_ci
21768c2ecf20Sopenharmony_ci	mii_cnt++;
21778c2ecf20Sopenharmony_ci
21788c2ecf20Sopenharmony_ci	/* save fec0 mii_bus */
21798c2ecf20Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_SINGLE_MDIO)
21808c2ecf20Sopenharmony_ci		fec0_mii_bus = fep->mii_bus;
21818c2ecf20Sopenharmony_ci
21828c2ecf20Sopenharmony_ci	return 0;
21838c2ecf20Sopenharmony_ci
21848c2ecf20Sopenharmony_cierr_out_free_mdiobus:
21858c2ecf20Sopenharmony_ci	mdiobus_free(fep->mii_bus);
21868c2ecf20Sopenharmony_cierr_out:
21878c2ecf20Sopenharmony_ci	of_node_put(node);
21888c2ecf20Sopenharmony_ci	return err;
21898c2ecf20Sopenharmony_ci}
21908c2ecf20Sopenharmony_ci
21918c2ecf20Sopenharmony_cistatic void fec_enet_mii_remove(struct fec_enet_private *fep)
21928c2ecf20Sopenharmony_ci{
21938c2ecf20Sopenharmony_ci	if (--mii_cnt == 0) {
21948c2ecf20Sopenharmony_ci		mdiobus_unregister(fep->mii_bus);
21958c2ecf20Sopenharmony_ci		mdiobus_free(fep->mii_bus);
21968c2ecf20Sopenharmony_ci	}
21978c2ecf20Sopenharmony_ci}
21988c2ecf20Sopenharmony_ci
21998c2ecf20Sopenharmony_cistatic void fec_enet_get_drvinfo(struct net_device *ndev,
22008c2ecf20Sopenharmony_ci				 struct ethtool_drvinfo *info)
22018c2ecf20Sopenharmony_ci{
22028c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
22038c2ecf20Sopenharmony_ci
22048c2ecf20Sopenharmony_ci	strlcpy(info->driver, fep->pdev->dev.driver->name,
22058c2ecf20Sopenharmony_ci		sizeof(info->driver));
22068c2ecf20Sopenharmony_ci	strlcpy(info->bus_info, dev_name(&ndev->dev), sizeof(info->bus_info));
22078c2ecf20Sopenharmony_ci}
22088c2ecf20Sopenharmony_ci
22098c2ecf20Sopenharmony_cistatic int fec_enet_get_regs_len(struct net_device *ndev)
22108c2ecf20Sopenharmony_ci{
22118c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
22128c2ecf20Sopenharmony_ci	struct resource *r;
22138c2ecf20Sopenharmony_ci	int s = 0;
22148c2ecf20Sopenharmony_ci
22158c2ecf20Sopenharmony_ci	r = platform_get_resource(fep->pdev, IORESOURCE_MEM, 0);
22168c2ecf20Sopenharmony_ci	if (r)
22178c2ecf20Sopenharmony_ci		s = resource_size(r);
22188c2ecf20Sopenharmony_ci
22198c2ecf20Sopenharmony_ci	return s;
22208c2ecf20Sopenharmony_ci}
22218c2ecf20Sopenharmony_ci
22228c2ecf20Sopenharmony_ci/* List of registers that can be safety be read to dump them with ethtool */
22238c2ecf20Sopenharmony_ci#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
22248c2ecf20Sopenharmony_ci	defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \
22258c2ecf20Sopenharmony_ci	defined(CONFIG_ARM64) || defined(CONFIG_COMPILE_TEST)
22268c2ecf20Sopenharmony_cistatic __u32 fec_enet_register_version = 2;
22278c2ecf20Sopenharmony_cistatic u32 fec_enet_register_offset[] = {
22288c2ecf20Sopenharmony_ci	FEC_IEVENT, FEC_IMASK, FEC_R_DES_ACTIVE_0, FEC_X_DES_ACTIVE_0,
22298c2ecf20Sopenharmony_ci	FEC_ECNTRL, FEC_MII_DATA, FEC_MII_SPEED, FEC_MIB_CTRLSTAT, FEC_R_CNTRL,
22308c2ecf20Sopenharmony_ci	FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, FEC_OPD, FEC_TXIC0, FEC_TXIC1,
22318c2ecf20Sopenharmony_ci	FEC_TXIC2, FEC_RXIC0, FEC_RXIC1, FEC_RXIC2, FEC_HASH_TABLE_HIGH,
22328c2ecf20Sopenharmony_ci	FEC_HASH_TABLE_LOW, FEC_GRP_HASH_TABLE_HIGH, FEC_GRP_HASH_TABLE_LOW,
22338c2ecf20Sopenharmony_ci	FEC_X_WMRK, FEC_R_BOUND, FEC_R_FSTART, FEC_R_DES_START_1,
22348c2ecf20Sopenharmony_ci	FEC_X_DES_START_1, FEC_R_BUFF_SIZE_1, FEC_R_DES_START_2,
22358c2ecf20Sopenharmony_ci	FEC_X_DES_START_2, FEC_R_BUFF_SIZE_2, FEC_R_DES_START_0,
22368c2ecf20Sopenharmony_ci	FEC_X_DES_START_0, FEC_R_BUFF_SIZE_0, FEC_R_FIFO_RSFL, FEC_R_FIFO_RSEM,
22378c2ecf20Sopenharmony_ci	FEC_R_FIFO_RAEM, FEC_R_FIFO_RAFL, FEC_RACC, FEC_RCMR_1, FEC_RCMR_2,
22388c2ecf20Sopenharmony_ci	FEC_DMA_CFG_1, FEC_DMA_CFG_2, FEC_R_DES_ACTIVE_1, FEC_X_DES_ACTIVE_1,
22398c2ecf20Sopenharmony_ci	FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_2, FEC_QOS_SCHEME,
22408c2ecf20Sopenharmony_ci	RMON_T_DROP, RMON_T_PACKETS, RMON_T_BC_PKT, RMON_T_MC_PKT,
22418c2ecf20Sopenharmony_ci	RMON_T_CRC_ALIGN, RMON_T_UNDERSIZE, RMON_T_OVERSIZE, RMON_T_FRAG,
22428c2ecf20Sopenharmony_ci	RMON_T_JAB, RMON_T_COL, RMON_T_P64, RMON_T_P65TO127, RMON_T_P128TO255,
22438c2ecf20Sopenharmony_ci	RMON_T_P256TO511, RMON_T_P512TO1023, RMON_T_P1024TO2047,
22448c2ecf20Sopenharmony_ci	RMON_T_P_GTE2048, RMON_T_OCTETS,
22458c2ecf20Sopenharmony_ci	IEEE_T_DROP, IEEE_T_FRAME_OK, IEEE_T_1COL, IEEE_T_MCOL, IEEE_T_DEF,
22468c2ecf20Sopenharmony_ci	IEEE_T_LCOL, IEEE_T_EXCOL, IEEE_T_MACERR, IEEE_T_CSERR, IEEE_T_SQE,
22478c2ecf20Sopenharmony_ci	IEEE_T_FDXFC, IEEE_T_OCTETS_OK,
22488c2ecf20Sopenharmony_ci	RMON_R_PACKETS, RMON_R_BC_PKT, RMON_R_MC_PKT, RMON_R_CRC_ALIGN,
22498c2ecf20Sopenharmony_ci	RMON_R_UNDERSIZE, RMON_R_OVERSIZE, RMON_R_FRAG, RMON_R_JAB,
22508c2ecf20Sopenharmony_ci	RMON_R_RESVD_O, RMON_R_P64, RMON_R_P65TO127, RMON_R_P128TO255,
22518c2ecf20Sopenharmony_ci	RMON_R_P256TO511, RMON_R_P512TO1023, RMON_R_P1024TO2047,
22528c2ecf20Sopenharmony_ci	RMON_R_P_GTE2048, RMON_R_OCTETS,
22538c2ecf20Sopenharmony_ci	IEEE_R_DROP, IEEE_R_FRAME_OK, IEEE_R_CRC, IEEE_R_ALIGN, IEEE_R_MACERR,
22548c2ecf20Sopenharmony_ci	IEEE_R_FDXFC, IEEE_R_OCTETS_OK
22558c2ecf20Sopenharmony_ci};
22568c2ecf20Sopenharmony_ci/* for i.MX6ul */
22578c2ecf20Sopenharmony_cistatic u32 fec_enet_register_offset_6ul[] = {
22588c2ecf20Sopenharmony_ci	FEC_IEVENT, FEC_IMASK, FEC_R_DES_ACTIVE_0, FEC_X_DES_ACTIVE_0,
22598c2ecf20Sopenharmony_ci	FEC_ECNTRL, FEC_MII_DATA, FEC_MII_SPEED, FEC_MIB_CTRLSTAT, FEC_R_CNTRL,
22608c2ecf20Sopenharmony_ci	FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, FEC_OPD, FEC_TXIC0, FEC_RXIC0,
22618c2ecf20Sopenharmony_ci	FEC_HASH_TABLE_HIGH, FEC_HASH_TABLE_LOW, FEC_GRP_HASH_TABLE_HIGH,
22628c2ecf20Sopenharmony_ci	FEC_GRP_HASH_TABLE_LOW, FEC_X_WMRK, FEC_R_DES_START_0,
22638c2ecf20Sopenharmony_ci	FEC_X_DES_START_0, FEC_R_BUFF_SIZE_0, FEC_R_FIFO_RSFL, FEC_R_FIFO_RSEM,
22648c2ecf20Sopenharmony_ci	FEC_R_FIFO_RAEM, FEC_R_FIFO_RAFL, FEC_RACC,
22658c2ecf20Sopenharmony_ci	RMON_T_DROP, RMON_T_PACKETS, RMON_T_BC_PKT, RMON_T_MC_PKT,
22668c2ecf20Sopenharmony_ci	RMON_T_CRC_ALIGN, RMON_T_UNDERSIZE, RMON_T_OVERSIZE, RMON_T_FRAG,
22678c2ecf20Sopenharmony_ci	RMON_T_JAB, RMON_T_COL, RMON_T_P64, RMON_T_P65TO127, RMON_T_P128TO255,
22688c2ecf20Sopenharmony_ci	RMON_T_P256TO511, RMON_T_P512TO1023, RMON_T_P1024TO2047,
22698c2ecf20Sopenharmony_ci	RMON_T_P_GTE2048, RMON_T_OCTETS,
22708c2ecf20Sopenharmony_ci	IEEE_T_DROP, IEEE_T_FRAME_OK, IEEE_T_1COL, IEEE_T_MCOL, IEEE_T_DEF,
22718c2ecf20Sopenharmony_ci	IEEE_T_LCOL, IEEE_T_EXCOL, IEEE_T_MACERR, IEEE_T_CSERR, IEEE_T_SQE,
22728c2ecf20Sopenharmony_ci	IEEE_T_FDXFC, IEEE_T_OCTETS_OK,
22738c2ecf20Sopenharmony_ci	RMON_R_PACKETS, RMON_R_BC_PKT, RMON_R_MC_PKT, RMON_R_CRC_ALIGN,
22748c2ecf20Sopenharmony_ci	RMON_R_UNDERSIZE, RMON_R_OVERSIZE, RMON_R_FRAG, RMON_R_JAB,
22758c2ecf20Sopenharmony_ci	RMON_R_RESVD_O, RMON_R_P64, RMON_R_P65TO127, RMON_R_P128TO255,
22768c2ecf20Sopenharmony_ci	RMON_R_P256TO511, RMON_R_P512TO1023, RMON_R_P1024TO2047,
22778c2ecf20Sopenharmony_ci	RMON_R_P_GTE2048, RMON_R_OCTETS,
22788c2ecf20Sopenharmony_ci	IEEE_R_DROP, IEEE_R_FRAME_OK, IEEE_R_CRC, IEEE_R_ALIGN, IEEE_R_MACERR,
22798c2ecf20Sopenharmony_ci	IEEE_R_FDXFC, IEEE_R_OCTETS_OK
22808c2ecf20Sopenharmony_ci};
22818c2ecf20Sopenharmony_ci#else
22828c2ecf20Sopenharmony_cistatic __u32 fec_enet_register_version = 1;
22838c2ecf20Sopenharmony_cistatic u32 fec_enet_register_offset[] = {
22848c2ecf20Sopenharmony_ci	FEC_ECNTRL, FEC_IEVENT, FEC_IMASK, FEC_IVEC, FEC_R_DES_ACTIVE_0,
22858c2ecf20Sopenharmony_ci	FEC_R_DES_ACTIVE_1, FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_0,
22868c2ecf20Sopenharmony_ci	FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2, FEC_MII_DATA, FEC_MII_SPEED,
22878c2ecf20Sopenharmony_ci	FEC_R_BOUND, FEC_R_FSTART, FEC_X_WMRK, FEC_X_FSTART, FEC_R_CNTRL,
22888c2ecf20Sopenharmony_ci	FEC_MAX_FRM_LEN, FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH,
22898c2ecf20Sopenharmony_ci	FEC_GRP_HASH_TABLE_HIGH, FEC_GRP_HASH_TABLE_LOW, FEC_R_DES_START_0,
22908c2ecf20Sopenharmony_ci	FEC_R_DES_START_1, FEC_R_DES_START_2, FEC_X_DES_START_0,
22918c2ecf20Sopenharmony_ci	FEC_X_DES_START_1, FEC_X_DES_START_2, FEC_R_BUFF_SIZE_0,
22928c2ecf20Sopenharmony_ci	FEC_R_BUFF_SIZE_1, FEC_R_BUFF_SIZE_2
22938c2ecf20Sopenharmony_ci};
22948c2ecf20Sopenharmony_ci#endif
22958c2ecf20Sopenharmony_ci
22968c2ecf20Sopenharmony_cistatic void fec_enet_get_regs(struct net_device *ndev,
22978c2ecf20Sopenharmony_ci			      struct ethtool_regs *regs, void *regbuf)
22988c2ecf20Sopenharmony_ci{
22998c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
23008c2ecf20Sopenharmony_ci	u32 __iomem *theregs = (u32 __iomem *)fep->hwp;
23018c2ecf20Sopenharmony_ci	struct device *dev = &fep->pdev->dev;
23028c2ecf20Sopenharmony_ci	u32 *buf = (u32 *)regbuf;
23038c2ecf20Sopenharmony_ci	u32 i, off;
23048c2ecf20Sopenharmony_ci	int ret;
23058c2ecf20Sopenharmony_ci#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
23068c2ecf20Sopenharmony_ci	defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \
23078c2ecf20Sopenharmony_ci	defined(CONFIG_ARM64) || defined(CONFIG_COMPILE_TEST)
23088c2ecf20Sopenharmony_ci	u32 *reg_list;
23098c2ecf20Sopenharmony_ci	u32 reg_cnt;
23108c2ecf20Sopenharmony_ci
23118c2ecf20Sopenharmony_ci	if (!of_machine_is_compatible("fsl,imx6ul")) {
23128c2ecf20Sopenharmony_ci		reg_list = fec_enet_register_offset;
23138c2ecf20Sopenharmony_ci		reg_cnt = ARRAY_SIZE(fec_enet_register_offset);
23148c2ecf20Sopenharmony_ci	} else {
23158c2ecf20Sopenharmony_ci		reg_list = fec_enet_register_offset_6ul;
23168c2ecf20Sopenharmony_ci		reg_cnt = ARRAY_SIZE(fec_enet_register_offset_6ul);
23178c2ecf20Sopenharmony_ci	}
23188c2ecf20Sopenharmony_ci#else
23198c2ecf20Sopenharmony_ci	/* coldfire */
23208c2ecf20Sopenharmony_ci	static u32 *reg_list = fec_enet_register_offset;
23218c2ecf20Sopenharmony_ci	static const u32 reg_cnt = ARRAY_SIZE(fec_enet_register_offset);
23228c2ecf20Sopenharmony_ci#endif
23238c2ecf20Sopenharmony_ci	ret = pm_runtime_resume_and_get(dev);
23248c2ecf20Sopenharmony_ci	if (ret < 0)
23258c2ecf20Sopenharmony_ci		return;
23268c2ecf20Sopenharmony_ci
23278c2ecf20Sopenharmony_ci	regs->version = fec_enet_register_version;
23288c2ecf20Sopenharmony_ci
23298c2ecf20Sopenharmony_ci	memset(buf, 0, regs->len);
23308c2ecf20Sopenharmony_ci
23318c2ecf20Sopenharmony_ci	for (i = 0; i < reg_cnt; i++) {
23328c2ecf20Sopenharmony_ci		off = reg_list[i];
23338c2ecf20Sopenharmony_ci
23348c2ecf20Sopenharmony_ci		if ((off == FEC_R_BOUND || off == FEC_R_FSTART) &&
23358c2ecf20Sopenharmony_ci		    !(fep->quirks & FEC_QUIRK_HAS_FRREG))
23368c2ecf20Sopenharmony_ci			continue;
23378c2ecf20Sopenharmony_ci
23388c2ecf20Sopenharmony_ci		off >>= 2;
23398c2ecf20Sopenharmony_ci		buf[off] = readl(&theregs[off]);
23408c2ecf20Sopenharmony_ci	}
23418c2ecf20Sopenharmony_ci
23428c2ecf20Sopenharmony_ci	pm_runtime_mark_last_busy(dev);
23438c2ecf20Sopenharmony_ci	pm_runtime_put_autosuspend(dev);
23448c2ecf20Sopenharmony_ci}
23458c2ecf20Sopenharmony_ci
23468c2ecf20Sopenharmony_cistatic int fec_enet_get_ts_info(struct net_device *ndev,
23478c2ecf20Sopenharmony_ci				struct ethtool_ts_info *info)
23488c2ecf20Sopenharmony_ci{
23498c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
23508c2ecf20Sopenharmony_ci
23518c2ecf20Sopenharmony_ci	if (fep->bufdesc_ex) {
23528c2ecf20Sopenharmony_ci
23538c2ecf20Sopenharmony_ci		info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
23548c2ecf20Sopenharmony_ci					SOF_TIMESTAMPING_RX_SOFTWARE |
23558c2ecf20Sopenharmony_ci					SOF_TIMESTAMPING_SOFTWARE |
23568c2ecf20Sopenharmony_ci					SOF_TIMESTAMPING_TX_HARDWARE |
23578c2ecf20Sopenharmony_ci					SOF_TIMESTAMPING_RX_HARDWARE |
23588c2ecf20Sopenharmony_ci					SOF_TIMESTAMPING_RAW_HARDWARE;
23598c2ecf20Sopenharmony_ci		if (fep->ptp_clock)
23608c2ecf20Sopenharmony_ci			info->phc_index = ptp_clock_index(fep->ptp_clock);
23618c2ecf20Sopenharmony_ci		else
23628c2ecf20Sopenharmony_ci			info->phc_index = -1;
23638c2ecf20Sopenharmony_ci
23648c2ecf20Sopenharmony_ci		info->tx_types = (1 << HWTSTAMP_TX_OFF) |
23658c2ecf20Sopenharmony_ci				 (1 << HWTSTAMP_TX_ON);
23668c2ecf20Sopenharmony_ci
23678c2ecf20Sopenharmony_ci		info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) |
23688c2ecf20Sopenharmony_ci				   (1 << HWTSTAMP_FILTER_ALL);
23698c2ecf20Sopenharmony_ci		return 0;
23708c2ecf20Sopenharmony_ci	} else {
23718c2ecf20Sopenharmony_ci		return ethtool_op_get_ts_info(ndev, info);
23728c2ecf20Sopenharmony_ci	}
23738c2ecf20Sopenharmony_ci}
23748c2ecf20Sopenharmony_ci
23758c2ecf20Sopenharmony_ci#if !defined(CONFIG_M5272)
23768c2ecf20Sopenharmony_ci
23778c2ecf20Sopenharmony_cistatic void fec_enet_get_pauseparam(struct net_device *ndev,
23788c2ecf20Sopenharmony_ci				    struct ethtool_pauseparam *pause)
23798c2ecf20Sopenharmony_ci{
23808c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
23818c2ecf20Sopenharmony_ci
23828c2ecf20Sopenharmony_ci	pause->autoneg = (fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) != 0;
23838c2ecf20Sopenharmony_ci	pause->tx_pause = (fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) != 0;
23848c2ecf20Sopenharmony_ci	pause->rx_pause = pause->tx_pause;
23858c2ecf20Sopenharmony_ci}
23868c2ecf20Sopenharmony_ci
23878c2ecf20Sopenharmony_cistatic int fec_enet_set_pauseparam(struct net_device *ndev,
23888c2ecf20Sopenharmony_ci				   struct ethtool_pauseparam *pause)
23898c2ecf20Sopenharmony_ci{
23908c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
23918c2ecf20Sopenharmony_ci
23928c2ecf20Sopenharmony_ci	if (!ndev->phydev)
23938c2ecf20Sopenharmony_ci		return -ENODEV;
23948c2ecf20Sopenharmony_ci
23958c2ecf20Sopenharmony_ci	if (pause->tx_pause != pause->rx_pause) {
23968c2ecf20Sopenharmony_ci		netdev_info(ndev,
23978c2ecf20Sopenharmony_ci			"hardware only support enable/disable both tx and rx");
23988c2ecf20Sopenharmony_ci		return -EINVAL;
23998c2ecf20Sopenharmony_ci	}
24008c2ecf20Sopenharmony_ci
24018c2ecf20Sopenharmony_ci	fep->pause_flag = 0;
24028c2ecf20Sopenharmony_ci
24038c2ecf20Sopenharmony_ci	/* tx pause must be same as rx pause */
24048c2ecf20Sopenharmony_ci	fep->pause_flag |= pause->rx_pause ? FEC_PAUSE_FLAG_ENABLE : 0;
24058c2ecf20Sopenharmony_ci	fep->pause_flag |= pause->autoneg ? FEC_PAUSE_FLAG_AUTONEG : 0;
24068c2ecf20Sopenharmony_ci
24078c2ecf20Sopenharmony_ci	phy_set_sym_pause(ndev->phydev, pause->rx_pause, pause->tx_pause,
24088c2ecf20Sopenharmony_ci			  pause->autoneg);
24098c2ecf20Sopenharmony_ci
24108c2ecf20Sopenharmony_ci	if (pause->autoneg) {
24118c2ecf20Sopenharmony_ci		if (netif_running(ndev))
24128c2ecf20Sopenharmony_ci			fec_stop(ndev);
24138c2ecf20Sopenharmony_ci		phy_start_aneg(ndev->phydev);
24148c2ecf20Sopenharmony_ci	}
24158c2ecf20Sopenharmony_ci	if (netif_running(ndev)) {
24168c2ecf20Sopenharmony_ci		napi_disable(&fep->napi);
24178c2ecf20Sopenharmony_ci		netif_tx_lock_bh(ndev);
24188c2ecf20Sopenharmony_ci		fec_restart(ndev);
24198c2ecf20Sopenharmony_ci		netif_tx_wake_all_queues(ndev);
24208c2ecf20Sopenharmony_ci		netif_tx_unlock_bh(ndev);
24218c2ecf20Sopenharmony_ci		napi_enable(&fep->napi);
24228c2ecf20Sopenharmony_ci	}
24238c2ecf20Sopenharmony_ci
24248c2ecf20Sopenharmony_ci	return 0;
24258c2ecf20Sopenharmony_ci}
24268c2ecf20Sopenharmony_ci
24278c2ecf20Sopenharmony_cistatic const struct fec_stat {
24288c2ecf20Sopenharmony_ci	char name[ETH_GSTRING_LEN];
24298c2ecf20Sopenharmony_ci	u16 offset;
24308c2ecf20Sopenharmony_ci} fec_stats[] = {
24318c2ecf20Sopenharmony_ci	/* RMON TX */
24328c2ecf20Sopenharmony_ci	{ "tx_dropped", RMON_T_DROP },
24338c2ecf20Sopenharmony_ci	{ "tx_packets", RMON_T_PACKETS },
24348c2ecf20Sopenharmony_ci	{ "tx_broadcast", RMON_T_BC_PKT },
24358c2ecf20Sopenharmony_ci	{ "tx_multicast", RMON_T_MC_PKT },
24368c2ecf20Sopenharmony_ci	{ "tx_crc_errors", RMON_T_CRC_ALIGN },
24378c2ecf20Sopenharmony_ci	{ "tx_undersize", RMON_T_UNDERSIZE },
24388c2ecf20Sopenharmony_ci	{ "tx_oversize", RMON_T_OVERSIZE },
24398c2ecf20Sopenharmony_ci	{ "tx_fragment", RMON_T_FRAG },
24408c2ecf20Sopenharmony_ci	{ "tx_jabber", RMON_T_JAB },
24418c2ecf20Sopenharmony_ci	{ "tx_collision", RMON_T_COL },
24428c2ecf20Sopenharmony_ci	{ "tx_64byte", RMON_T_P64 },
24438c2ecf20Sopenharmony_ci	{ "tx_65to127byte", RMON_T_P65TO127 },
24448c2ecf20Sopenharmony_ci	{ "tx_128to255byte", RMON_T_P128TO255 },
24458c2ecf20Sopenharmony_ci	{ "tx_256to511byte", RMON_T_P256TO511 },
24468c2ecf20Sopenharmony_ci	{ "tx_512to1023byte", RMON_T_P512TO1023 },
24478c2ecf20Sopenharmony_ci	{ "tx_1024to2047byte", RMON_T_P1024TO2047 },
24488c2ecf20Sopenharmony_ci	{ "tx_GTE2048byte", RMON_T_P_GTE2048 },
24498c2ecf20Sopenharmony_ci	{ "tx_octets", RMON_T_OCTETS },
24508c2ecf20Sopenharmony_ci
24518c2ecf20Sopenharmony_ci	/* IEEE TX */
24528c2ecf20Sopenharmony_ci	{ "IEEE_tx_drop", IEEE_T_DROP },
24538c2ecf20Sopenharmony_ci	{ "IEEE_tx_frame_ok", IEEE_T_FRAME_OK },
24548c2ecf20Sopenharmony_ci	{ "IEEE_tx_1col", IEEE_T_1COL },
24558c2ecf20Sopenharmony_ci	{ "IEEE_tx_mcol", IEEE_T_MCOL },
24568c2ecf20Sopenharmony_ci	{ "IEEE_tx_def", IEEE_T_DEF },
24578c2ecf20Sopenharmony_ci	{ "IEEE_tx_lcol", IEEE_T_LCOL },
24588c2ecf20Sopenharmony_ci	{ "IEEE_tx_excol", IEEE_T_EXCOL },
24598c2ecf20Sopenharmony_ci	{ "IEEE_tx_macerr", IEEE_T_MACERR },
24608c2ecf20Sopenharmony_ci	{ "IEEE_tx_cserr", IEEE_T_CSERR },
24618c2ecf20Sopenharmony_ci	{ "IEEE_tx_sqe", IEEE_T_SQE },
24628c2ecf20Sopenharmony_ci	{ "IEEE_tx_fdxfc", IEEE_T_FDXFC },
24638c2ecf20Sopenharmony_ci	{ "IEEE_tx_octets_ok", IEEE_T_OCTETS_OK },
24648c2ecf20Sopenharmony_ci
24658c2ecf20Sopenharmony_ci	/* RMON RX */
24668c2ecf20Sopenharmony_ci	{ "rx_packets", RMON_R_PACKETS },
24678c2ecf20Sopenharmony_ci	{ "rx_broadcast", RMON_R_BC_PKT },
24688c2ecf20Sopenharmony_ci	{ "rx_multicast", RMON_R_MC_PKT },
24698c2ecf20Sopenharmony_ci	{ "rx_crc_errors", RMON_R_CRC_ALIGN },
24708c2ecf20Sopenharmony_ci	{ "rx_undersize", RMON_R_UNDERSIZE },
24718c2ecf20Sopenharmony_ci	{ "rx_oversize", RMON_R_OVERSIZE },
24728c2ecf20Sopenharmony_ci	{ "rx_fragment", RMON_R_FRAG },
24738c2ecf20Sopenharmony_ci	{ "rx_jabber", RMON_R_JAB },
24748c2ecf20Sopenharmony_ci	{ "rx_64byte", RMON_R_P64 },
24758c2ecf20Sopenharmony_ci	{ "rx_65to127byte", RMON_R_P65TO127 },
24768c2ecf20Sopenharmony_ci	{ "rx_128to255byte", RMON_R_P128TO255 },
24778c2ecf20Sopenharmony_ci	{ "rx_256to511byte", RMON_R_P256TO511 },
24788c2ecf20Sopenharmony_ci	{ "rx_512to1023byte", RMON_R_P512TO1023 },
24798c2ecf20Sopenharmony_ci	{ "rx_1024to2047byte", RMON_R_P1024TO2047 },
24808c2ecf20Sopenharmony_ci	{ "rx_GTE2048byte", RMON_R_P_GTE2048 },
24818c2ecf20Sopenharmony_ci	{ "rx_octets", RMON_R_OCTETS },
24828c2ecf20Sopenharmony_ci
24838c2ecf20Sopenharmony_ci	/* IEEE RX */
24848c2ecf20Sopenharmony_ci	{ "IEEE_rx_drop", IEEE_R_DROP },
24858c2ecf20Sopenharmony_ci	{ "IEEE_rx_frame_ok", IEEE_R_FRAME_OK },
24868c2ecf20Sopenharmony_ci	{ "IEEE_rx_crc", IEEE_R_CRC },
24878c2ecf20Sopenharmony_ci	{ "IEEE_rx_align", IEEE_R_ALIGN },
24888c2ecf20Sopenharmony_ci	{ "IEEE_rx_macerr", IEEE_R_MACERR },
24898c2ecf20Sopenharmony_ci	{ "IEEE_rx_fdxfc", IEEE_R_FDXFC },
24908c2ecf20Sopenharmony_ci	{ "IEEE_rx_octets_ok", IEEE_R_OCTETS_OK },
24918c2ecf20Sopenharmony_ci};
24928c2ecf20Sopenharmony_ci
24938c2ecf20Sopenharmony_ci#define FEC_STATS_SIZE		(ARRAY_SIZE(fec_stats) * sizeof(u64))
24948c2ecf20Sopenharmony_ci
24958c2ecf20Sopenharmony_cistatic void fec_enet_update_ethtool_stats(struct net_device *dev)
24968c2ecf20Sopenharmony_ci{
24978c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(dev);
24988c2ecf20Sopenharmony_ci	int i;
24998c2ecf20Sopenharmony_ci
25008c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(fec_stats); i++)
25018c2ecf20Sopenharmony_ci		fep->ethtool_stats[i] = readl(fep->hwp + fec_stats[i].offset);
25028c2ecf20Sopenharmony_ci}
25038c2ecf20Sopenharmony_ci
25048c2ecf20Sopenharmony_cistatic void fec_enet_get_ethtool_stats(struct net_device *dev,
25058c2ecf20Sopenharmony_ci				       struct ethtool_stats *stats, u64 *data)
25068c2ecf20Sopenharmony_ci{
25078c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(dev);
25088c2ecf20Sopenharmony_ci
25098c2ecf20Sopenharmony_ci	if (netif_running(dev))
25108c2ecf20Sopenharmony_ci		fec_enet_update_ethtool_stats(dev);
25118c2ecf20Sopenharmony_ci
25128c2ecf20Sopenharmony_ci	memcpy(data, fep->ethtool_stats, FEC_STATS_SIZE);
25138c2ecf20Sopenharmony_ci}
25148c2ecf20Sopenharmony_ci
25158c2ecf20Sopenharmony_cistatic void fec_enet_get_strings(struct net_device *netdev,
25168c2ecf20Sopenharmony_ci	u32 stringset, u8 *data)
25178c2ecf20Sopenharmony_ci{
25188c2ecf20Sopenharmony_ci	int i;
25198c2ecf20Sopenharmony_ci	switch (stringset) {
25208c2ecf20Sopenharmony_ci	case ETH_SS_STATS:
25218c2ecf20Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(fec_stats); i++)
25228c2ecf20Sopenharmony_ci			memcpy(data + i * ETH_GSTRING_LEN,
25238c2ecf20Sopenharmony_ci				fec_stats[i].name, ETH_GSTRING_LEN);
25248c2ecf20Sopenharmony_ci		break;
25258c2ecf20Sopenharmony_ci	}
25268c2ecf20Sopenharmony_ci}
25278c2ecf20Sopenharmony_ci
25288c2ecf20Sopenharmony_cistatic int fec_enet_get_sset_count(struct net_device *dev, int sset)
25298c2ecf20Sopenharmony_ci{
25308c2ecf20Sopenharmony_ci	switch (sset) {
25318c2ecf20Sopenharmony_ci	case ETH_SS_STATS:
25328c2ecf20Sopenharmony_ci		return ARRAY_SIZE(fec_stats);
25338c2ecf20Sopenharmony_ci	default:
25348c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
25358c2ecf20Sopenharmony_ci	}
25368c2ecf20Sopenharmony_ci}
25378c2ecf20Sopenharmony_ci
25388c2ecf20Sopenharmony_cistatic void fec_enet_clear_ethtool_stats(struct net_device *dev)
25398c2ecf20Sopenharmony_ci{
25408c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(dev);
25418c2ecf20Sopenharmony_ci	int i;
25428c2ecf20Sopenharmony_ci
25438c2ecf20Sopenharmony_ci	/* Disable MIB statistics counters */
25448c2ecf20Sopenharmony_ci	writel(FEC_MIB_CTRLSTAT_DISABLE, fep->hwp + FEC_MIB_CTRLSTAT);
25458c2ecf20Sopenharmony_ci
25468c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(fec_stats); i++)
25478c2ecf20Sopenharmony_ci		writel(0, fep->hwp + fec_stats[i].offset);
25488c2ecf20Sopenharmony_ci
25498c2ecf20Sopenharmony_ci	/* Don't disable MIB statistics counters */
25508c2ecf20Sopenharmony_ci	writel(0, fep->hwp + FEC_MIB_CTRLSTAT);
25518c2ecf20Sopenharmony_ci}
25528c2ecf20Sopenharmony_ci
25538c2ecf20Sopenharmony_ci#else	/* !defined(CONFIG_M5272) */
25548c2ecf20Sopenharmony_ci#define FEC_STATS_SIZE	0
25558c2ecf20Sopenharmony_cistatic inline void fec_enet_update_ethtool_stats(struct net_device *dev)
25568c2ecf20Sopenharmony_ci{
25578c2ecf20Sopenharmony_ci}
25588c2ecf20Sopenharmony_ci
25598c2ecf20Sopenharmony_cistatic inline void fec_enet_clear_ethtool_stats(struct net_device *dev)
25608c2ecf20Sopenharmony_ci{
25618c2ecf20Sopenharmony_ci}
25628c2ecf20Sopenharmony_ci#endif /* !defined(CONFIG_M5272) */
25638c2ecf20Sopenharmony_ci
25648c2ecf20Sopenharmony_ci/* ITR clock source is enet system clock (clk_ahb).
25658c2ecf20Sopenharmony_ci * TCTT unit is cycle_ns * 64 cycle
25668c2ecf20Sopenharmony_ci * So, the ICTT value = X us / (cycle_ns * 64)
25678c2ecf20Sopenharmony_ci */
25688c2ecf20Sopenharmony_cistatic int fec_enet_us_to_itr_clock(struct net_device *ndev, int us)
25698c2ecf20Sopenharmony_ci{
25708c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
25718c2ecf20Sopenharmony_ci
25728c2ecf20Sopenharmony_ci	return us * (fep->itr_clk_rate / 64000) / 1000;
25738c2ecf20Sopenharmony_ci}
25748c2ecf20Sopenharmony_ci
25758c2ecf20Sopenharmony_ci/* Set threshold for interrupt coalescing */
25768c2ecf20Sopenharmony_cistatic void fec_enet_itr_coal_set(struct net_device *ndev)
25778c2ecf20Sopenharmony_ci{
25788c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
25798c2ecf20Sopenharmony_ci	int rx_itr, tx_itr;
25808c2ecf20Sopenharmony_ci
25818c2ecf20Sopenharmony_ci	/* Must be greater than zero to avoid unpredictable behavior */
25828c2ecf20Sopenharmony_ci	if (!fep->rx_time_itr || !fep->rx_pkts_itr ||
25838c2ecf20Sopenharmony_ci	    !fep->tx_time_itr || !fep->tx_pkts_itr)
25848c2ecf20Sopenharmony_ci		return;
25858c2ecf20Sopenharmony_ci
25868c2ecf20Sopenharmony_ci	/* Select enet system clock as Interrupt Coalescing
25878c2ecf20Sopenharmony_ci	 * timer Clock Source
25888c2ecf20Sopenharmony_ci	 */
25898c2ecf20Sopenharmony_ci	rx_itr = FEC_ITR_CLK_SEL;
25908c2ecf20Sopenharmony_ci	tx_itr = FEC_ITR_CLK_SEL;
25918c2ecf20Sopenharmony_ci
25928c2ecf20Sopenharmony_ci	/* set ICFT and ICTT */
25938c2ecf20Sopenharmony_ci	rx_itr |= FEC_ITR_ICFT(fep->rx_pkts_itr);
25948c2ecf20Sopenharmony_ci	rx_itr |= FEC_ITR_ICTT(fec_enet_us_to_itr_clock(ndev, fep->rx_time_itr));
25958c2ecf20Sopenharmony_ci	tx_itr |= FEC_ITR_ICFT(fep->tx_pkts_itr);
25968c2ecf20Sopenharmony_ci	tx_itr |= FEC_ITR_ICTT(fec_enet_us_to_itr_clock(ndev, fep->tx_time_itr));
25978c2ecf20Sopenharmony_ci
25988c2ecf20Sopenharmony_ci	rx_itr |= FEC_ITR_EN;
25998c2ecf20Sopenharmony_ci	tx_itr |= FEC_ITR_EN;
26008c2ecf20Sopenharmony_ci
26018c2ecf20Sopenharmony_ci	writel(tx_itr, fep->hwp + FEC_TXIC0);
26028c2ecf20Sopenharmony_ci	writel(rx_itr, fep->hwp + FEC_RXIC0);
26038c2ecf20Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_HAS_AVB) {
26048c2ecf20Sopenharmony_ci		writel(tx_itr, fep->hwp + FEC_TXIC1);
26058c2ecf20Sopenharmony_ci		writel(rx_itr, fep->hwp + FEC_RXIC1);
26068c2ecf20Sopenharmony_ci		writel(tx_itr, fep->hwp + FEC_TXIC2);
26078c2ecf20Sopenharmony_ci		writel(rx_itr, fep->hwp + FEC_RXIC2);
26088c2ecf20Sopenharmony_ci	}
26098c2ecf20Sopenharmony_ci}
26108c2ecf20Sopenharmony_ci
26118c2ecf20Sopenharmony_cistatic int
26128c2ecf20Sopenharmony_cifec_enet_get_coalesce(struct net_device *ndev, struct ethtool_coalesce *ec)
26138c2ecf20Sopenharmony_ci{
26148c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
26158c2ecf20Sopenharmony_ci
26168c2ecf20Sopenharmony_ci	if (!(fep->quirks & FEC_QUIRK_HAS_COALESCE))
26178c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
26188c2ecf20Sopenharmony_ci
26198c2ecf20Sopenharmony_ci	ec->rx_coalesce_usecs = fep->rx_time_itr;
26208c2ecf20Sopenharmony_ci	ec->rx_max_coalesced_frames = fep->rx_pkts_itr;
26218c2ecf20Sopenharmony_ci
26228c2ecf20Sopenharmony_ci	ec->tx_coalesce_usecs = fep->tx_time_itr;
26238c2ecf20Sopenharmony_ci	ec->tx_max_coalesced_frames = fep->tx_pkts_itr;
26248c2ecf20Sopenharmony_ci
26258c2ecf20Sopenharmony_ci	return 0;
26268c2ecf20Sopenharmony_ci}
26278c2ecf20Sopenharmony_ci
26288c2ecf20Sopenharmony_cistatic int
26298c2ecf20Sopenharmony_cifec_enet_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *ec)
26308c2ecf20Sopenharmony_ci{
26318c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
26328c2ecf20Sopenharmony_ci	struct device *dev = &fep->pdev->dev;
26338c2ecf20Sopenharmony_ci	unsigned int cycle;
26348c2ecf20Sopenharmony_ci
26358c2ecf20Sopenharmony_ci	if (!(fep->quirks & FEC_QUIRK_HAS_COALESCE))
26368c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
26378c2ecf20Sopenharmony_ci
26388c2ecf20Sopenharmony_ci	if (ec->rx_max_coalesced_frames > 255) {
26398c2ecf20Sopenharmony_ci		dev_err(dev, "Rx coalesced frames exceed hardware limitation\n");
26408c2ecf20Sopenharmony_ci		return -EINVAL;
26418c2ecf20Sopenharmony_ci	}
26428c2ecf20Sopenharmony_ci
26438c2ecf20Sopenharmony_ci	if (ec->tx_max_coalesced_frames > 255) {
26448c2ecf20Sopenharmony_ci		dev_err(dev, "Tx coalesced frame exceed hardware limitation\n");
26458c2ecf20Sopenharmony_ci		return -EINVAL;
26468c2ecf20Sopenharmony_ci	}
26478c2ecf20Sopenharmony_ci
26488c2ecf20Sopenharmony_ci	cycle = fec_enet_us_to_itr_clock(ndev, ec->rx_coalesce_usecs);
26498c2ecf20Sopenharmony_ci	if (cycle > 0xFFFF) {
26508c2ecf20Sopenharmony_ci		dev_err(dev, "Rx coalesced usec exceed hardware limitation\n");
26518c2ecf20Sopenharmony_ci		return -EINVAL;
26528c2ecf20Sopenharmony_ci	}
26538c2ecf20Sopenharmony_ci
26548c2ecf20Sopenharmony_ci	cycle = fec_enet_us_to_itr_clock(ndev, ec->tx_coalesce_usecs);
26558c2ecf20Sopenharmony_ci	if (cycle > 0xFFFF) {
26568c2ecf20Sopenharmony_ci		dev_err(dev, "Tx coalesced usec exceed hardware limitation\n");
26578c2ecf20Sopenharmony_ci		return -EINVAL;
26588c2ecf20Sopenharmony_ci	}
26598c2ecf20Sopenharmony_ci
26608c2ecf20Sopenharmony_ci	fep->rx_time_itr = ec->rx_coalesce_usecs;
26618c2ecf20Sopenharmony_ci	fep->rx_pkts_itr = ec->rx_max_coalesced_frames;
26628c2ecf20Sopenharmony_ci
26638c2ecf20Sopenharmony_ci	fep->tx_time_itr = ec->tx_coalesce_usecs;
26648c2ecf20Sopenharmony_ci	fep->tx_pkts_itr = ec->tx_max_coalesced_frames;
26658c2ecf20Sopenharmony_ci
26668c2ecf20Sopenharmony_ci	fec_enet_itr_coal_set(ndev);
26678c2ecf20Sopenharmony_ci
26688c2ecf20Sopenharmony_ci	return 0;
26698c2ecf20Sopenharmony_ci}
26708c2ecf20Sopenharmony_ci
26718c2ecf20Sopenharmony_cistatic void fec_enet_itr_coal_init(struct net_device *ndev)
26728c2ecf20Sopenharmony_ci{
26738c2ecf20Sopenharmony_ci	struct ethtool_coalesce ec;
26748c2ecf20Sopenharmony_ci
26758c2ecf20Sopenharmony_ci	ec.rx_coalesce_usecs = FEC_ITR_ICTT_DEFAULT;
26768c2ecf20Sopenharmony_ci	ec.rx_max_coalesced_frames = FEC_ITR_ICFT_DEFAULT;
26778c2ecf20Sopenharmony_ci
26788c2ecf20Sopenharmony_ci	ec.tx_coalesce_usecs = FEC_ITR_ICTT_DEFAULT;
26798c2ecf20Sopenharmony_ci	ec.tx_max_coalesced_frames = FEC_ITR_ICFT_DEFAULT;
26808c2ecf20Sopenharmony_ci
26818c2ecf20Sopenharmony_ci	fec_enet_set_coalesce(ndev, &ec);
26828c2ecf20Sopenharmony_ci}
26838c2ecf20Sopenharmony_ci
26848c2ecf20Sopenharmony_cistatic int fec_enet_get_tunable(struct net_device *netdev,
26858c2ecf20Sopenharmony_ci				const struct ethtool_tunable *tuna,
26868c2ecf20Sopenharmony_ci				void *data)
26878c2ecf20Sopenharmony_ci{
26888c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(netdev);
26898c2ecf20Sopenharmony_ci	int ret = 0;
26908c2ecf20Sopenharmony_ci
26918c2ecf20Sopenharmony_ci	switch (tuna->id) {
26928c2ecf20Sopenharmony_ci	case ETHTOOL_RX_COPYBREAK:
26938c2ecf20Sopenharmony_ci		*(u32 *)data = fep->rx_copybreak;
26948c2ecf20Sopenharmony_ci		break;
26958c2ecf20Sopenharmony_ci	default:
26968c2ecf20Sopenharmony_ci		ret = -EINVAL;
26978c2ecf20Sopenharmony_ci		break;
26988c2ecf20Sopenharmony_ci	}
26998c2ecf20Sopenharmony_ci
27008c2ecf20Sopenharmony_ci	return ret;
27018c2ecf20Sopenharmony_ci}
27028c2ecf20Sopenharmony_ci
27038c2ecf20Sopenharmony_cistatic int fec_enet_set_tunable(struct net_device *netdev,
27048c2ecf20Sopenharmony_ci				const struct ethtool_tunable *tuna,
27058c2ecf20Sopenharmony_ci				const void *data)
27068c2ecf20Sopenharmony_ci{
27078c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(netdev);
27088c2ecf20Sopenharmony_ci	int ret = 0;
27098c2ecf20Sopenharmony_ci
27108c2ecf20Sopenharmony_ci	switch (tuna->id) {
27118c2ecf20Sopenharmony_ci	case ETHTOOL_RX_COPYBREAK:
27128c2ecf20Sopenharmony_ci		fep->rx_copybreak = *(u32 *)data;
27138c2ecf20Sopenharmony_ci		break;
27148c2ecf20Sopenharmony_ci	default:
27158c2ecf20Sopenharmony_ci		ret = -EINVAL;
27168c2ecf20Sopenharmony_ci		break;
27178c2ecf20Sopenharmony_ci	}
27188c2ecf20Sopenharmony_ci
27198c2ecf20Sopenharmony_ci	return ret;
27208c2ecf20Sopenharmony_ci}
27218c2ecf20Sopenharmony_ci
27228c2ecf20Sopenharmony_cistatic void
27238c2ecf20Sopenharmony_cifec_enet_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol)
27248c2ecf20Sopenharmony_ci{
27258c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
27268c2ecf20Sopenharmony_ci
27278c2ecf20Sopenharmony_ci	if (fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET) {
27288c2ecf20Sopenharmony_ci		wol->supported = WAKE_MAGIC;
27298c2ecf20Sopenharmony_ci		wol->wolopts = fep->wol_flag & FEC_WOL_FLAG_ENABLE ? WAKE_MAGIC : 0;
27308c2ecf20Sopenharmony_ci	} else {
27318c2ecf20Sopenharmony_ci		wol->supported = wol->wolopts = 0;
27328c2ecf20Sopenharmony_ci	}
27338c2ecf20Sopenharmony_ci}
27348c2ecf20Sopenharmony_ci
27358c2ecf20Sopenharmony_cistatic int
27368c2ecf20Sopenharmony_cifec_enet_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol)
27378c2ecf20Sopenharmony_ci{
27388c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
27398c2ecf20Sopenharmony_ci
27408c2ecf20Sopenharmony_ci	if (!(fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET))
27418c2ecf20Sopenharmony_ci		return -EINVAL;
27428c2ecf20Sopenharmony_ci
27438c2ecf20Sopenharmony_ci	if (wol->wolopts & ~WAKE_MAGIC)
27448c2ecf20Sopenharmony_ci		return -EINVAL;
27458c2ecf20Sopenharmony_ci
27468c2ecf20Sopenharmony_ci	device_set_wakeup_enable(&ndev->dev, wol->wolopts & WAKE_MAGIC);
27478c2ecf20Sopenharmony_ci	if (device_may_wakeup(&ndev->dev)) {
27488c2ecf20Sopenharmony_ci		fep->wol_flag |= FEC_WOL_FLAG_ENABLE;
27498c2ecf20Sopenharmony_ci		if (fep->irq[0] > 0)
27508c2ecf20Sopenharmony_ci			enable_irq_wake(fep->irq[0]);
27518c2ecf20Sopenharmony_ci	} else {
27528c2ecf20Sopenharmony_ci		fep->wol_flag &= (~FEC_WOL_FLAG_ENABLE);
27538c2ecf20Sopenharmony_ci		if (fep->irq[0] > 0)
27548c2ecf20Sopenharmony_ci			disable_irq_wake(fep->irq[0]);
27558c2ecf20Sopenharmony_ci	}
27568c2ecf20Sopenharmony_ci
27578c2ecf20Sopenharmony_ci	return 0;
27588c2ecf20Sopenharmony_ci}
27598c2ecf20Sopenharmony_ci
27608c2ecf20Sopenharmony_cistatic const struct ethtool_ops fec_enet_ethtool_ops = {
27618c2ecf20Sopenharmony_ci	.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
27628c2ecf20Sopenharmony_ci				     ETHTOOL_COALESCE_MAX_FRAMES,
27638c2ecf20Sopenharmony_ci	.get_drvinfo		= fec_enet_get_drvinfo,
27648c2ecf20Sopenharmony_ci	.get_regs_len		= fec_enet_get_regs_len,
27658c2ecf20Sopenharmony_ci	.get_regs		= fec_enet_get_regs,
27668c2ecf20Sopenharmony_ci	.nway_reset		= phy_ethtool_nway_reset,
27678c2ecf20Sopenharmony_ci	.get_link		= ethtool_op_get_link,
27688c2ecf20Sopenharmony_ci	.get_coalesce		= fec_enet_get_coalesce,
27698c2ecf20Sopenharmony_ci	.set_coalesce		= fec_enet_set_coalesce,
27708c2ecf20Sopenharmony_ci#ifndef CONFIG_M5272
27718c2ecf20Sopenharmony_ci	.get_pauseparam		= fec_enet_get_pauseparam,
27728c2ecf20Sopenharmony_ci	.set_pauseparam		= fec_enet_set_pauseparam,
27738c2ecf20Sopenharmony_ci	.get_strings		= fec_enet_get_strings,
27748c2ecf20Sopenharmony_ci	.get_ethtool_stats	= fec_enet_get_ethtool_stats,
27758c2ecf20Sopenharmony_ci	.get_sset_count		= fec_enet_get_sset_count,
27768c2ecf20Sopenharmony_ci#endif
27778c2ecf20Sopenharmony_ci	.get_ts_info		= fec_enet_get_ts_info,
27788c2ecf20Sopenharmony_ci	.get_tunable		= fec_enet_get_tunable,
27798c2ecf20Sopenharmony_ci	.set_tunable		= fec_enet_set_tunable,
27808c2ecf20Sopenharmony_ci	.get_wol		= fec_enet_get_wol,
27818c2ecf20Sopenharmony_ci	.set_wol		= fec_enet_set_wol,
27828c2ecf20Sopenharmony_ci	.get_link_ksettings	= phy_ethtool_get_link_ksettings,
27838c2ecf20Sopenharmony_ci	.set_link_ksettings	= phy_ethtool_set_link_ksettings,
27848c2ecf20Sopenharmony_ci};
27858c2ecf20Sopenharmony_ci
27868c2ecf20Sopenharmony_cistatic int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
27878c2ecf20Sopenharmony_ci{
27888c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
27898c2ecf20Sopenharmony_ci	struct phy_device *phydev = ndev->phydev;
27908c2ecf20Sopenharmony_ci
27918c2ecf20Sopenharmony_ci	if (!netif_running(ndev))
27928c2ecf20Sopenharmony_ci		return -EINVAL;
27938c2ecf20Sopenharmony_ci
27948c2ecf20Sopenharmony_ci	if (!phydev)
27958c2ecf20Sopenharmony_ci		return -ENODEV;
27968c2ecf20Sopenharmony_ci
27978c2ecf20Sopenharmony_ci	if (fep->bufdesc_ex) {
27988c2ecf20Sopenharmony_ci		bool use_fec_hwts = !phy_has_hwtstamp(phydev);
27998c2ecf20Sopenharmony_ci
28008c2ecf20Sopenharmony_ci		if (cmd == SIOCSHWTSTAMP) {
28018c2ecf20Sopenharmony_ci			if (use_fec_hwts)
28028c2ecf20Sopenharmony_ci				return fec_ptp_set(ndev, rq);
28038c2ecf20Sopenharmony_ci			fec_ptp_disable_hwts(ndev);
28048c2ecf20Sopenharmony_ci		} else if (cmd == SIOCGHWTSTAMP) {
28058c2ecf20Sopenharmony_ci			if (use_fec_hwts)
28068c2ecf20Sopenharmony_ci				return fec_ptp_get(ndev, rq);
28078c2ecf20Sopenharmony_ci		}
28088c2ecf20Sopenharmony_ci	}
28098c2ecf20Sopenharmony_ci
28108c2ecf20Sopenharmony_ci	return phy_mii_ioctl(phydev, rq, cmd);
28118c2ecf20Sopenharmony_ci}
28128c2ecf20Sopenharmony_ci
28138c2ecf20Sopenharmony_cistatic void fec_enet_free_buffers(struct net_device *ndev)
28148c2ecf20Sopenharmony_ci{
28158c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
28168c2ecf20Sopenharmony_ci	unsigned int i;
28178c2ecf20Sopenharmony_ci	struct sk_buff *skb;
28188c2ecf20Sopenharmony_ci	struct bufdesc	*bdp;
28198c2ecf20Sopenharmony_ci	struct fec_enet_priv_tx_q *txq;
28208c2ecf20Sopenharmony_ci	struct fec_enet_priv_rx_q *rxq;
28218c2ecf20Sopenharmony_ci	unsigned int q;
28228c2ecf20Sopenharmony_ci
28238c2ecf20Sopenharmony_ci	for (q = 0; q < fep->num_rx_queues; q++) {
28248c2ecf20Sopenharmony_ci		rxq = fep->rx_queue[q];
28258c2ecf20Sopenharmony_ci		bdp = rxq->bd.base;
28268c2ecf20Sopenharmony_ci		for (i = 0; i < rxq->bd.ring_size; i++) {
28278c2ecf20Sopenharmony_ci			skb = rxq->rx_skbuff[i];
28288c2ecf20Sopenharmony_ci			rxq->rx_skbuff[i] = NULL;
28298c2ecf20Sopenharmony_ci			if (skb) {
28308c2ecf20Sopenharmony_ci				dma_unmap_single(&fep->pdev->dev,
28318c2ecf20Sopenharmony_ci						 fec32_to_cpu(bdp->cbd_bufaddr),
28328c2ecf20Sopenharmony_ci						 FEC_ENET_RX_FRSIZE - fep->rx_align,
28338c2ecf20Sopenharmony_ci						 DMA_FROM_DEVICE);
28348c2ecf20Sopenharmony_ci				dev_kfree_skb(skb);
28358c2ecf20Sopenharmony_ci			}
28368c2ecf20Sopenharmony_ci			bdp = fec_enet_get_nextdesc(bdp, &rxq->bd);
28378c2ecf20Sopenharmony_ci		}
28388c2ecf20Sopenharmony_ci	}
28398c2ecf20Sopenharmony_ci
28408c2ecf20Sopenharmony_ci	for (q = 0; q < fep->num_tx_queues; q++) {
28418c2ecf20Sopenharmony_ci		txq = fep->tx_queue[q];
28428c2ecf20Sopenharmony_ci		for (i = 0; i < txq->bd.ring_size; i++) {
28438c2ecf20Sopenharmony_ci			kfree(txq->tx_bounce[i]);
28448c2ecf20Sopenharmony_ci			txq->tx_bounce[i] = NULL;
28458c2ecf20Sopenharmony_ci			skb = txq->tx_skbuff[i];
28468c2ecf20Sopenharmony_ci			txq->tx_skbuff[i] = NULL;
28478c2ecf20Sopenharmony_ci			dev_kfree_skb(skb);
28488c2ecf20Sopenharmony_ci		}
28498c2ecf20Sopenharmony_ci	}
28508c2ecf20Sopenharmony_ci}
28518c2ecf20Sopenharmony_ci
28528c2ecf20Sopenharmony_cistatic void fec_enet_free_queue(struct net_device *ndev)
28538c2ecf20Sopenharmony_ci{
28548c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
28558c2ecf20Sopenharmony_ci	int i;
28568c2ecf20Sopenharmony_ci	struct fec_enet_priv_tx_q *txq;
28578c2ecf20Sopenharmony_ci
28588c2ecf20Sopenharmony_ci	for (i = 0; i < fep->num_tx_queues; i++)
28598c2ecf20Sopenharmony_ci		if (fep->tx_queue[i] && fep->tx_queue[i]->tso_hdrs) {
28608c2ecf20Sopenharmony_ci			txq = fep->tx_queue[i];
28618c2ecf20Sopenharmony_ci			dma_free_coherent(&fep->pdev->dev,
28628c2ecf20Sopenharmony_ci					  txq->bd.ring_size * TSO_HEADER_SIZE,
28638c2ecf20Sopenharmony_ci					  txq->tso_hdrs,
28648c2ecf20Sopenharmony_ci					  txq->tso_hdrs_dma);
28658c2ecf20Sopenharmony_ci		}
28668c2ecf20Sopenharmony_ci
28678c2ecf20Sopenharmony_ci	for (i = 0; i < fep->num_rx_queues; i++)
28688c2ecf20Sopenharmony_ci		kfree(fep->rx_queue[i]);
28698c2ecf20Sopenharmony_ci	for (i = 0; i < fep->num_tx_queues; i++)
28708c2ecf20Sopenharmony_ci		kfree(fep->tx_queue[i]);
28718c2ecf20Sopenharmony_ci}
28728c2ecf20Sopenharmony_ci
28738c2ecf20Sopenharmony_cistatic int fec_enet_alloc_queue(struct net_device *ndev)
28748c2ecf20Sopenharmony_ci{
28758c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
28768c2ecf20Sopenharmony_ci	int i;
28778c2ecf20Sopenharmony_ci	int ret = 0;
28788c2ecf20Sopenharmony_ci	struct fec_enet_priv_tx_q *txq;
28798c2ecf20Sopenharmony_ci
28808c2ecf20Sopenharmony_ci	for (i = 0; i < fep->num_tx_queues; i++) {
28818c2ecf20Sopenharmony_ci		txq = kzalloc(sizeof(*txq), GFP_KERNEL);
28828c2ecf20Sopenharmony_ci		if (!txq) {
28838c2ecf20Sopenharmony_ci			ret = -ENOMEM;
28848c2ecf20Sopenharmony_ci			goto alloc_failed;
28858c2ecf20Sopenharmony_ci		}
28868c2ecf20Sopenharmony_ci
28878c2ecf20Sopenharmony_ci		fep->tx_queue[i] = txq;
28888c2ecf20Sopenharmony_ci		txq->bd.ring_size = TX_RING_SIZE;
28898c2ecf20Sopenharmony_ci		fep->total_tx_ring_size += fep->tx_queue[i]->bd.ring_size;
28908c2ecf20Sopenharmony_ci
28918c2ecf20Sopenharmony_ci		txq->tx_stop_threshold = FEC_MAX_SKB_DESCS;
28928c2ecf20Sopenharmony_ci		txq->tx_wake_threshold =
28938c2ecf20Sopenharmony_ci			(txq->bd.ring_size - txq->tx_stop_threshold) / 2;
28948c2ecf20Sopenharmony_ci
28958c2ecf20Sopenharmony_ci		txq->tso_hdrs = dma_alloc_coherent(&fep->pdev->dev,
28968c2ecf20Sopenharmony_ci					txq->bd.ring_size * TSO_HEADER_SIZE,
28978c2ecf20Sopenharmony_ci					&txq->tso_hdrs_dma,
28988c2ecf20Sopenharmony_ci					GFP_KERNEL);
28998c2ecf20Sopenharmony_ci		if (!txq->tso_hdrs) {
29008c2ecf20Sopenharmony_ci			ret = -ENOMEM;
29018c2ecf20Sopenharmony_ci			goto alloc_failed;
29028c2ecf20Sopenharmony_ci		}
29038c2ecf20Sopenharmony_ci	}
29048c2ecf20Sopenharmony_ci
29058c2ecf20Sopenharmony_ci	for (i = 0; i < fep->num_rx_queues; i++) {
29068c2ecf20Sopenharmony_ci		fep->rx_queue[i] = kzalloc(sizeof(*fep->rx_queue[i]),
29078c2ecf20Sopenharmony_ci					   GFP_KERNEL);
29088c2ecf20Sopenharmony_ci		if (!fep->rx_queue[i]) {
29098c2ecf20Sopenharmony_ci			ret = -ENOMEM;
29108c2ecf20Sopenharmony_ci			goto alloc_failed;
29118c2ecf20Sopenharmony_ci		}
29128c2ecf20Sopenharmony_ci
29138c2ecf20Sopenharmony_ci		fep->rx_queue[i]->bd.ring_size = RX_RING_SIZE;
29148c2ecf20Sopenharmony_ci		fep->total_rx_ring_size += fep->rx_queue[i]->bd.ring_size;
29158c2ecf20Sopenharmony_ci	}
29168c2ecf20Sopenharmony_ci	return ret;
29178c2ecf20Sopenharmony_ci
29188c2ecf20Sopenharmony_cialloc_failed:
29198c2ecf20Sopenharmony_ci	fec_enet_free_queue(ndev);
29208c2ecf20Sopenharmony_ci	return ret;
29218c2ecf20Sopenharmony_ci}
29228c2ecf20Sopenharmony_ci
29238c2ecf20Sopenharmony_cistatic int
29248c2ecf20Sopenharmony_cifec_enet_alloc_rxq_buffers(struct net_device *ndev, unsigned int queue)
29258c2ecf20Sopenharmony_ci{
29268c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
29278c2ecf20Sopenharmony_ci	unsigned int i;
29288c2ecf20Sopenharmony_ci	struct sk_buff *skb;
29298c2ecf20Sopenharmony_ci	struct bufdesc	*bdp;
29308c2ecf20Sopenharmony_ci	struct fec_enet_priv_rx_q *rxq;
29318c2ecf20Sopenharmony_ci
29328c2ecf20Sopenharmony_ci	rxq = fep->rx_queue[queue];
29338c2ecf20Sopenharmony_ci	bdp = rxq->bd.base;
29348c2ecf20Sopenharmony_ci	for (i = 0; i < rxq->bd.ring_size; i++) {
29358c2ecf20Sopenharmony_ci		skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE);
29368c2ecf20Sopenharmony_ci		if (!skb)
29378c2ecf20Sopenharmony_ci			goto err_alloc;
29388c2ecf20Sopenharmony_ci
29398c2ecf20Sopenharmony_ci		if (fec_enet_new_rxbdp(ndev, bdp, skb)) {
29408c2ecf20Sopenharmony_ci			dev_kfree_skb(skb);
29418c2ecf20Sopenharmony_ci			goto err_alloc;
29428c2ecf20Sopenharmony_ci		}
29438c2ecf20Sopenharmony_ci
29448c2ecf20Sopenharmony_ci		rxq->rx_skbuff[i] = skb;
29458c2ecf20Sopenharmony_ci		bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY);
29468c2ecf20Sopenharmony_ci
29478c2ecf20Sopenharmony_ci		if (fep->bufdesc_ex) {
29488c2ecf20Sopenharmony_ci			struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
29498c2ecf20Sopenharmony_ci			ebdp->cbd_esc = cpu_to_fec32(BD_ENET_RX_INT);
29508c2ecf20Sopenharmony_ci		}
29518c2ecf20Sopenharmony_ci
29528c2ecf20Sopenharmony_ci		bdp = fec_enet_get_nextdesc(bdp, &rxq->bd);
29538c2ecf20Sopenharmony_ci	}
29548c2ecf20Sopenharmony_ci
29558c2ecf20Sopenharmony_ci	/* Set the last buffer to wrap. */
29568c2ecf20Sopenharmony_ci	bdp = fec_enet_get_prevdesc(bdp, &rxq->bd);
29578c2ecf20Sopenharmony_ci	bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP);
29588c2ecf20Sopenharmony_ci	return 0;
29598c2ecf20Sopenharmony_ci
29608c2ecf20Sopenharmony_ci err_alloc:
29618c2ecf20Sopenharmony_ci	fec_enet_free_buffers(ndev);
29628c2ecf20Sopenharmony_ci	return -ENOMEM;
29638c2ecf20Sopenharmony_ci}
29648c2ecf20Sopenharmony_ci
29658c2ecf20Sopenharmony_cistatic int
29668c2ecf20Sopenharmony_cifec_enet_alloc_txq_buffers(struct net_device *ndev, unsigned int queue)
29678c2ecf20Sopenharmony_ci{
29688c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
29698c2ecf20Sopenharmony_ci	unsigned int i;
29708c2ecf20Sopenharmony_ci	struct bufdesc  *bdp;
29718c2ecf20Sopenharmony_ci	struct fec_enet_priv_tx_q *txq;
29728c2ecf20Sopenharmony_ci
29738c2ecf20Sopenharmony_ci	txq = fep->tx_queue[queue];
29748c2ecf20Sopenharmony_ci	bdp = txq->bd.base;
29758c2ecf20Sopenharmony_ci	for (i = 0; i < txq->bd.ring_size; i++) {
29768c2ecf20Sopenharmony_ci		txq->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL);
29778c2ecf20Sopenharmony_ci		if (!txq->tx_bounce[i])
29788c2ecf20Sopenharmony_ci			goto err_alloc;
29798c2ecf20Sopenharmony_ci
29808c2ecf20Sopenharmony_ci		bdp->cbd_sc = cpu_to_fec16(0);
29818c2ecf20Sopenharmony_ci		bdp->cbd_bufaddr = cpu_to_fec32(0);
29828c2ecf20Sopenharmony_ci
29838c2ecf20Sopenharmony_ci		if (fep->bufdesc_ex) {
29848c2ecf20Sopenharmony_ci			struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
29858c2ecf20Sopenharmony_ci			ebdp->cbd_esc = cpu_to_fec32(BD_ENET_TX_INT);
29868c2ecf20Sopenharmony_ci		}
29878c2ecf20Sopenharmony_ci
29888c2ecf20Sopenharmony_ci		bdp = fec_enet_get_nextdesc(bdp, &txq->bd);
29898c2ecf20Sopenharmony_ci	}
29908c2ecf20Sopenharmony_ci
29918c2ecf20Sopenharmony_ci	/* Set the last buffer to wrap. */
29928c2ecf20Sopenharmony_ci	bdp = fec_enet_get_prevdesc(bdp, &txq->bd);
29938c2ecf20Sopenharmony_ci	bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP);
29948c2ecf20Sopenharmony_ci
29958c2ecf20Sopenharmony_ci	return 0;
29968c2ecf20Sopenharmony_ci
29978c2ecf20Sopenharmony_ci err_alloc:
29988c2ecf20Sopenharmony_ci	fec_enet_free_buffers(ndev);
29998c2ecf20Sopenharmony_ci	return -ENOMEM;
30008c2ecf20Sopenharmony_ci}
30018c2ecf20Sopenharmony_ci
30028c2ecf20Sopenharmony_cistatic int fec_enet_alloc_buffers(struct net_device *ndev)
30038c2ecf20Sopenharmony_ci{
30048c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
30058c2ecf20Sopenharmony_ci	unsigned int i;
30068c2ecf20Sopenharmony_ci
30078c2ecf20Sopenharmony_ci	for (i = 0; i < fep->num_rx_queues; i++)
30088c2ecf20Sopenharmony_ci		if (fec_enet_alloc_rxq_buffers(ndev, i))
30098c2ecf20Sopenharmony_ci			return -ENOMEM;
30108c2ecf20Sopenharmony_ci
30118c2ecf20Sopenharmony_ci	for (i = 0; i < fep->num_tx_queues; i++)
30128c2ecf20Sopenharmony_ci		if (fec_enet_alloc_txq_buffers(ndev, i))
30138c2ecf20Sopenharmony_ci			return -ENOMEM;
30148c2ecf20Sopenharmony_ci	return 0;
30158c2ecf20Sopenharmony_ci}
30168c2ecf20Sopenharmony_ci
30178c2ecf20Sopenharmony_cistatic int
30188c2ecf20Sopenharmony_cifec_enet_open(struct net_device *ndev)
30198c2ecf20Sopenharmony_ci{
30208c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
30218c2ecf20Sopenharmony_ci	int ret;
30228c2ecf20Sopenharmony_ci	bool reset_again;
30238c2ecf20Sopenharmony_ci
30248c2ecf20Sopenharmony_ci	ret = pm_runtime_resume_and_get(&fep->pdev->dev);
30258c2ecf20Sopenharmony_ci	if (ret < 0)
30268c2ecf20Sopenharmony_ci		return ret;
30278c2ecf20Sopenharmony_ci
30288c2ecf20Sopenharmony_ci	pinctrl_pm_select_default_state(&fep->pdev->dev);
30298c2ecf20Sopenharmony_ci	ret = fec_enet_clk_enable(ndev, true);
30308c2ecf20Sopenharmony_ci	if (ret)
30318c2ecf20Sopenharmony_ci		goto clk_enable;
30328c2ecf20Sopenharmony_ci
30338c2ecf20Sopenharmony_ci	/* During the first fec_enet_open call the PHY isn't probed at this
30348c2ecf20Sopenharmony_ci	 * point. Therefore the phy_reset_after_clk_enable() call within
30358c2ecf20Sopenharmony_ci	 * fec_enet_clk_enable() fails. As we need this reset in order to be
30368c2ecf20Sopenharmony_ci	 * sure the PHY is working correctly we check if we need to reset again
30378c2ecf20Sopenharmony_ci	 * later when the PHY is probed
30388c2ecf20Sopenharmony_ci	 */
30398c2ecf20Sopenharmony_ci	if (ndev->phydev && ndev->phydev->drv)
30408c2ecf20Sopenharmony_ci		reset_again = false;
30418c2ecf20Sopenharmony_ci	else
30428c2ecf20Sopenharmony_ci		reset_again = true;
30438c2ecf20Sopenharmony_ci
30448c2ecf20Sopenharmony_ci	/* I should reset the ring buffers here, but I don't yet know
30458c2ecf20Sopenharmony_ci	 * a simple way to do that.
30468c2ecf20Sopenharmony_ci	 */
30478c2ecf20Sopenharmony_ci
30488c2ecf20Sopenharmony_ci	ret = fec_enet_alloc_buffers(ndev);
30498c2ecf20Sopenharmony_ci	if (ret)
30508c2ecf20Sopenharmony_ci		goto err_enet_alloc;
30518c2ecf20Sopenharmony_ci
30528c2ecf20Sopenharmony_ci	/* Init MAC prior to mii bus probe */
30538c2ecf20Sopenharmony_ci	fec_restart(ndev);
30548c2ecf20Sopenharmony_ci
30558c2ecf20Sopenharmony_ci	/* Call phy_reset_after_clk_enable() again if it failed during
30568c2ecf20Sopenharmony_ci	 * phy_reset_after_clk_enable() before because the PHY wasn't probed.
30578c2ecf20Sopenharmony_ci	 */
30588c2ecf20Sopenharmony_ci	if (reset_again)
30598c2ecf20Sopenharmony_ci		fec_enet_phy_reset_after_clk_enable(ndev);
30608c2ecf20Sopenharmony_ci
30618c2ecf20Sopenharmony_ci	/* Probe and connect to PHY when open the interface */
30628c2ecf20Sopenharmony_ci	ret = fec_enet_mii_probe(ndev);
30638c2ecf20Sopenharmony_ci	if (ret)
30648c2ecf20Sopenharmony_ci		goto err_enet_mii_probe;
30658c2ecf20Sopenharmony_ci
30668c2ecf20Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_ERR006687)
30678c2ecf20Sopenharmony_ci		imx6q_cpuidle_fec_irqs_used();
30688c2ecf20Sopenharmony_ci
30698c2ecf20Sopenharmony_ci	napi_enable(&fep->napi);
30708c2ecf20Sopenharmony_ci	phy_start(ndev->phydev);
30718c2ecf20Sopenharmony_ci	netif_tx_start_all_queues(ndev);
30728c2ecf20Sopenharmony_ci
30738c2ecf20Sopenharmony_ci	device_set_wakeup_enable(&ndev->dev, fep->wol_flag &
30748c2ecf20Sopenharmony_ci				 FEC_WOL_FLAG_ENABLE);
30758c2ecf20Sopenharmony_ci
30768c2ecf20Sopenharmony_ci	return 0;
30778c2ecf20Sopenharmony_ci
30788c2ecf20Sopenharmony_cierr_enet_mii_probe:
30798c2ecf20Sopenharmony_ci	fec_enet_free_buffers(ndev);
30808c2ecf20Sopenharmony_cierr_enet_alloc:
30818c2ecf20Sopenharmony_ci	fec_enet_clk_enable(ndev, false);
30828c2ecf20Sopenharmony_ciclk_enable:
30838c2ecf20Sopenharmony_ci	pm_runtime_mark_last_busy(&fep->pdev->dev);
30848c2ecf20Sopenharmony_ci	pm_runtime_put_autosuspend(&fep->pdev->dev);
30858c2ecf20Sopenharmony_ci	pinctrl_pm_select_sleep_state(&fep->pdev->dev);
30868c2ecf20Sopenharmony_ci	return ret;
30878c2ecf20Sopenharmony_ci}
30888c2ecf20Sopenharmony_ci
30898c2ecf20Sopenharmony_cistatic int
30908c2ecf20Sopenharmony_cifec_enet_close(struct net_device *ndev)
30918c2ecf20Sopenharmony_ci{
30928c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
30938c2ecf20Sopenharmony_ci
30948c2ecf20Sopenharmony_ci	phy_stop(ndev->phydev);
30958c2ecf20Sopenharmony_ci
30968c2ecf20Sopenharmony_ci	if (netif_device_present(ndev)) {
30978c2ecf20Sopenharmony_ci		napi_disable(&fep->napi);
30988c2ecf20Sopenharmony_ci		netif_tx_disable(ndev);
30998c2ecf20Sopenharmony_ci		fec_stop(ndev);
31008c2ecf20Sopenharmony_ci	}
31018c2ecf20Sopenharmony_ci
31028c2ecf20Sopenharmony_ci	phy_disconnect(ndev->phydev);
31038c2ecf20Sopenharmony_ci
31048c2ecf20Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_ERR006687)
31058c2ecf20Sopenharmony_ci		imx6q_cpuidle_fec_irqs_unused();
31068c2ecf20Sopenharmony_ci
31078c2ecf20Sopenharmony_ci	fec_enet_update_ethtool_stats(ndev);
31088c2ecf20Sopenharmony_ci
31098c2ecf20Sopenharmony_ci	fec_enet_clk_enable(ndev, false);
31108c2ecf20Sopenharmony_ci	pinctrl_pm_select_sleep_state(&fep->pdev->dev);
31118c2ecf20Sopenharmony_ci	pm_runtime_mark_last_busy(&fep->pdev->dev);
31128c2ecf20Sopenharmony_ci	pm_runtime_put_autosuspend(&fep->pdev->dev);
31138c2ecf20Sopenharmony_ci
31148c2ecf20Sopenharmony_ci	fec_enet_free_buffers(ndev);
31158c2ecf20Sopenharmony_ci
31168c2ecf20Sopenharmony_ci	return 0;
31178c2ecf20Sopenharmony_ci}
31188c2ecf20Sopenharmony_ci
31198c2ecf20Sopenharmony_ci/* Set or clear the multicast filter for this adaptor.
31208c2ecf20Sopenharmony_ci * Skeleton taken from sunlance driver.
31218c2ecf20Sopenharmony_ci * The CPM Ethernet implementation allows Multicast as well as individual
31228c2ecf20Sopenharmony_ci * MAC address filtering.  Some of the drivers check to make sure it is
31238c2ecf20Sopenharmony_ci * a group multicast address, and discard those that are not.  I guess I
31248c2ecf20Sopenharmony_ci * will do the same for now, but just remove the test if you want
31258c2ecf20Sopenharmony_ci * individual filtering as well (do the upper net layers want or support
31268c2ecf20Sopenharmony_ci * this kind of feature?).
31278c2ecf20Sopenharmony_ci */
31288c2ecf20Sopenharmony_ci
31298c2ecf20Sopenharmony_ci#define FEC_HASH_BITS	6		/* #bits in hash */
31308c2ecf20Sopenharmony_ci
31318c2ecf20Sopenharmony_cistatic void set_multicast_list(struct net_device *ndev)
31328c2ecf20Sopenharmony_ci{
31338c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
31348c2ecf20Sopenharmony_ci	struct netdev_hw_addr *ha;
31358c2ecf20Sopenharmony_ci	unsigned int crc, tmp;
31368c2ecf20Sopenharmony_ci	unsigned char hash;
31378c2ecf20Sopenharmony_ci	unsigned int hash_high = 0, hash_low = 0;
31388c2ecf20Sopenharmony_ci
31398c2ecf20Sopenharmony_ci	if (ndev->flags & IFF_PROMISC) {
31408c2ecf20Sopenharmony_ci		tmp = readl(fep->hwp + FEC_R_CNTRL);
31418c2ecf20Sopenharmony_ci		tmp |= 0x8;
31428c2ecf20Sopenharmony_ci		writel(tmp, fep->hwp + FEC_R_CNTRL);
31438c2ecf20Sopenharmony_ci		return;
31448c2ecf20Sopenharmony_ci	}
31458c2ecf20Sopenharmony_ci
31468c2ecf20Sopenharmony_ci	tmp = readl(fep->hwp + FEC_R_CNTRL);
31478c2ecf20Sopenharmony_ci	tmp &= ~0x8;
31488c2ecf20Sopenharmony_ci	writel(tmp, fep->hwp + FEC_R_CNTRL);
31498c2ecf20Sopenharmony_ci
31508c2ecf20Sopenharmony_ci	if (ndev->flags & IFF_ALLMULTI) {
31518c2ecf20Sopenharmony_ci		/* Catch all multicast addresses, so set the
31528c2ecf20Sopenharmony_ci		 * filter to all 1's
31538c2ecf20Sopenharmony_ci		 */
31548c2ecf20Sopenharmony_ci		writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
31558c2ecf20Sopenharmony_ci		writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
31568c2ecf20Sopenharmony_ci
31578c2ecf20Sopenharmony_ci		return;
31588c2ecf20Sopenharmony_ci	}
31598c2ecf20Sopenharmony_ci
31608c2ecf20Sopenharmony_ci	/* Add the addresses in hash register */
31618c2ecf20Sopenharmony_ci	netdev_for_each_mc_addr(ha, ndev) {
31628c2ecf20Sopenharmony_ci		/* calculate crc32 value of mac address */
31638c2ecf20Sopenharmony_ci		crc = ether_crc_le(ndev->addr_len, ha->addr);
31648c2ecf20Sopenharmony_ci
31658c2ecf20Sopenharmony_ci		/* only upper 6 bits (FEC_HASH_BITS) are used
31668c2ecf20Sopenharmony_ci		 * which point to specific bit in the hash registers
31678c2ecf20Sopenharmony_ci		 */
31688c2ecf20Sopenharmony_ci		hash = (crc >> (32 - FEC_HASH_BITS)) & 0x3f;
31698c2ecf20Sopenharmony_ci
31708c2ecf20Sopenharmony_ci		if (hash > 31)
31718c2ecf20Sopenharmony_ci			hash_high |= 1 << (hash - 32);
31728c2ecf20Sopenharmony_ci		else
31738c2ecf20Sopenharmony_ci			hash_low |= 1 << hash;
31748c2ecf20Sopenharmony_ci	}
31758c2ecf20Sopenharmony_ci
31768c2ecf20Sopenharmony_ci	writel(hash_high, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
31778c2ecf20Sopenharmony_ci	writel(hash_low, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
31788c2ecf20Sopenharmony_ci}
31798c2ecf20Sopenharmony_ci
31808c2ecf20Sopenharmony_ci/* Set a MAC change in hardware. */
31818c2ecf20Sopenharmony_cistatic int
31828c2ecf20Sopenharmony_cifec_set_mac_address(struct net_device *ndev, void *p)
31838c2ecf20Sopenharmony_ci{
31848c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
31858c2ecf20Sopenharmony_ci	struct sockaddr *addr = p;
31868c2ecf20Sopenharmony_ci
31878c2ecf20Sopenharmony_ci	if (addr) {
31888c2ecf20Sopenharmony_ci		if (!is_valid_ether_addr(addr->sa_data))
31898c2ecf20Sopenharmony_ci			return -EADDRNOTAVAIL;
31908c2ecf20Sopenharmony_ci		memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len);
31918c2ecf20Sopenharmony_ci	}
31928c2ecf20Sopenharmony_ci
31938c2ecf20Sopenharmony_ci	/* Add netif status check here to avoid system hang in below case:
31948c2ecf20Sopenharmony_ci	 * ifconfig ethx down; ifconfig ethx hw ether xx:xx:xx:xx:xx:xx;
31958c2ecf20Sopenharmony_ci	 * After ethx down, fec all clocks are gated off and then register
31968c2ecf20Sopenharmony_ci	 * access causes system hang.
31978c2ecf20Sopenharmony_ci	 */
31988c2ecf20Sopenharmony_ci	if (!netif_running(ndev))
31998c2ecf20Sopenharmony_ci		return 0;
32008c2ecf20Sopenharmony_ci
32018c2ecf20Sopenharmony_ci	writel(ndev->dev_addr[3] | (ndev->dev_addr[2] << 8) |
32028c2ecf20Sopenharmony_ci		(ndev->dev_addr[1] << 16) | (ndev->dev_addr[0] << 24),
32038c2ecf20Sopenharmony_ci		fep->hwp + FEC_ADDR_LOW);
32048c2ecf20Sopenharmony_ci	writel((ndev->dev_addr[5] << 16) | (ndev->dev_addr[4] << 24),
32058c2ecf20Sopenharmony_ci		fep->hwp + FEC_ADDR_HIGH);
32068c2ecf20Sopenharmony_ci	return 0;
32078c2ecf20Sopenharmony_ci}
32088c2ecf20Sopenharmony_ci
32098c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
32108c2ecf20Sopenharmony_ci/**
32118c2ecf20Sopenharmony_ci * fec_poll_controller - FEC Poll controller function
32128c2ecf20Sopenharmony_ci * @dev: The FEC network adapter
32138c2ecf20Sopenharmony_ci *
32148c2ecf20Sopenharmony_ci * Polled functionality used by netconsole and others in non interrupt mode
32158c2ecf20Sopenharmony_ci *
32168c2ecf20Sopenharmony_ci */
32178c2ecf20Sopenharmony_cistatic void fec_poll_controller(struct net_device *dev)
32188c2ecf20Sopenharmony_ci{
32198c2ecf20Sopenharmony_ci	int i;
32208c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(dev);
32218c2ecf20Sopenharmony_ci
32228c2ecf20Sopenharmony_ci	for (i = 0; i < FEC_IRQ_NUM; i++) {
32238c2ecf20Sopenharmony_ci		if (fep->irq[i] > 0) {
32248c2ecf20Sopenharmony_ci			disable_irq(fep->irq[i]);
32258c2ecf20Sopenharmony_ci			fec_enet_interrupt(fep->irq[i], dev);
32268c2ecf20Sopenharmony_ci			enable_irq(fep->irq[i]);
32278c2ecf20Sopenharmony_ci		}
32288c2ecf20Sopenharmony_ci	}
32298c2ecf20Sopenharmony_ci}
32308c2ecf20Sopenharmony_ci#endif
32318c2ecf20Sopenharmony_ci
32328c2ecf20Sopenharmony_cistatic inline void fec_enet_set_netdev_features(struct net_device *netdev,
32338c2ecf20Sopenharmony_ci	netdev_features_t features)
32348c2ecf20Sopenharmony_ci{
32358c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(netdev);
32368c2ecf20Sopenharmony_ci	netdev_features_t changed = features ^ netdev->features;
32378c2ecf20Sopenharmony_ci
32388c2ecf20Sopenharmony_ci	netdev->features = features;
32398c2ecf20Sopenharmony_ci
32408c2ecf20Sopenharmony_ci	/* Receive checksum has been changed */
32418c2ecf20Sopenharmony_ci	if (changed & NETIF_F_RXCSUM) {
32428c2ecf20Sopenharmony_ci		if (features & NETIF_F_RXCSUM)
32438c2ecf20Sopenharmony_ci			fep->csum_flags |= FLAG_RX_CSUM_ENABLED;
32448c2ecf20Sopenharmony_ci		else
32458c2ecf20Sopenharmony_ci			fep->csum_flags &= ~FLAG_RX_CSUM_ENABLED;
32468c2ecf20Sopenharmony_ci	}
32478c2ecf20Sopenharmony_ci}
32488c2ecf20Sopenharmony_ci
32498c2ecf20Sopenharmony_cistatic int fec_set_features(struct net_device *netdev,
32508c2ecf20Sopenharmony_ci	netdev_features_t features)
32518c2ecf20Sopenharmony_ci{
32528c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(netdev);
32538c2ecf20Sopenharmony_ci	netdev_features_t changed = features ^ netdev->features;
32548c2ecf20Sopenharmony_ci
32558c2ecf20Sopenharmony_ci	if (netif_running(netdev) && changed & NETIF_F_RXCSUM) {
32568c2ecf20Sopenharmony_ci		napi_disable(&fep->napi);
32578c2ecf20Sopenharmony_ci		netif_tx_lock_bh(netdev);
32588c2ecf20Sopenharmony_ci		fec_stop(netdev);
32598c2ecf20Sopenharmony_ci		fec_enet_set_netdev_features(netdev, features);
32608c2ecf20Sopenharmony_ci		fec_restart(netdev);
32618c2ecf20Sopenharmony_ci		netif_tx_wake_all_queues(netdev);
32628c2ecf20Sopenharmony_ci		netif_tx_unlock_bh(netdev);
32638c2ecf20Sopenharmony_ci		napi_enable(&fep->napi);
32648c2ecf20Sopenharmony_ci	} else {
32658c2ecf20Sopenharmony_ci		fec_enet_set_netdev_features(netdev, features);
32668c2ecf20Sopenharmony_ci	}
32678c2ecf20Sopenharmony_ci
32688c2ecf20Sopenharmony_ci	return 0;
32698c2ecf20Sopenharmony_ci}
32708c2ecf20Sopenharmony_ci
32718c2ecf20Sopenharmony_cistatic u16 fec_enet_select_queue(struct net_device *ndev, struct sk_buff *skb,
32728c2ecf20Sopenharmony_ci				 struct net_device *sb_dev)
32738c2ecf20Sopenharmony_ci{
32748c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
32758c2ecf20Sopenharmony_ci	u16 vlan_tag = 0;
32768c2ecf20Sopenharmony_ci
32778c2ecf20Sopenharmony_ci	if (!(fep->quirks & FEC_QUIRK_HAS_AVB))
32788c2ecf20Sopenharmony_ci		return netdev_pick_tx(ndev, skb, NULL);
32798c2ecf20Sopenharmony_ci
32808c2ecf20Sopenharmony_ci	/* VLAN is present in the payload.*/
32818c2ecf20Sopenharmony_ci	if (eth_type_vlan(skb->protocol)) {
32828c2ecf20Sopenharmony_ci		struct vlan_ethhdr *vhdr = skb_vlan_eth_hdr(skb);
32838c2ecf20Sopenharmony_ci
32848c2ecf20Sopenharmony_ci		vlan_tag = ntohs(vhdr->h_vlan_TCI);
32858c2ecf20Sopenharmony_ci	/*  VLAN is present in the skb but not yet pushed in the payload.*/
32868c2ecf20Sopenharmony_ci	} else if (skb_vlan_tag_present(skb)) {
32878c2ecf20Sopenharmony_ci		vlan_tag = skb->vlan_tci;
32888c2ecf20Sopenharmony_ci	} else {
32898c2ecf20Sopenharmony_ci		return vlan_tag;
32908c2ecf20Sopenharmony_ci	}
32918c2ecf20Sopenharmony_ci
32928c2ecf20Sopenharmony_ci	return fec_enet_vlan_pri_to_queue[vlan_tag >> 13];
32938c2ecf20Sopenharmony_ci}
32948c2ecf20Sopenharmony_ci
32958c2ecf20Sopenharmony_cistatic const struct net_device_ops fec_netdev_ops = {
32968c2ecf20Sopenharmony_ci	.ndo_open		= fec_enet_open,
32978c2ecf20Sopenharmony_ci	.ndo_stop		= fec_enet_close,
32988c2ecf20Sopenharmony_ci	.ndo_start_xmit		= fec_enet_start_xmit,
32998c2ecf20Sopenharmony_ci	.ndo_select_queue       = fec_enet_select_queue,
33008c2ecf20Sopenharmony_ci	.ndo_set_rx_mode	= set_multicast_list,
33018c2ecf20Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
33028c2ecf20Sopenharmony_ci	.ndo_tx_timeout		= fec_timeout,
33038c2ecf20Sopenharmony_ci	.ndo_set_mac_address	= fec_set_mac_address,
33048c2ecf20Sopenharmony_ci	.ndo_do_ioctl		= fec_enet_ioctl,
33058c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
33068c2ecf20Sopenharmony_ci	.ndo_poll_controller	= fec_poll_controller,
33078c2ecf20Sopenharmony_ci#endif
33088c2ecf20Sopenharmony_ci	.ndo_set_features	= fec_set_features,
33098c2ecf20Sopenharmony_ci};
33108c2ecf20Sopenharmony_ci
33118c2ecf20Sopenharmony_cistatic const unsigned short offset_des_active_rxq[] = {
33128c2ecf20Sopenharmony_ci	FEC_R_DES_ACTIVE_0, FEC_R_DES_ACTIVE_1, FEC_R_DES_ACTIVE_2
33138c2ecf20Sopenharmony_ci};
33148c2ecf20Sopenharmony_ci
33158c2ecf20Sopenharmony_cistatic const unsigned short offset_des_active_txq[] = {
33168c2ecf20Sopenharmony_ci	FEC_X_DES_ACTIVE_0, FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2
33178c2ecf20Sopenharmony_ci};
33188c2ecf20Sopenharmony_ci
33198c2ecf20Sopenharmony_ci /*
33208c2ecf20Sopenharmony_ci  * XXX:  We need to clean up on failure exits here.
33218c2ecf20Sopenharmony_ci  *
33228c2ecf20Sopenharmony_ci  */
33238c2ecf20Sopenharmony_cistatic int fec_enet_init(struct net_device *ndev)
33248c2ecf20Sopenharmony_ci{
33258c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
33268c2ecf20Sopenharmony_ci	struct bufdesc *cbd_base;
33278c2ecf20Sopenharmony_ci	dma_addr_t bd_dma;
33288c2ecf20Sopenharmony_ci	int bd_size;
33298c2ecf20Sopenharmony_ci	unsigned int i;
33308c2ecf20Sopenharmony_ci	unsigned dsize = fep->bufdesc_ex ? sizeof(struct bufdesc_ex) :
33318c2ecf20Sopenharmony_ci			sizeof(struct bufdesc);
33328c2ecf20Sopenharmony_ci	unsigned dsize_log2 = __fls(dsize);
33338c2ecf20Sopenharmony_ci	int ret;
33348c2ecf20Sopenharmony_ci
33358c2ecf20Sopenharmony_ci	WARN_ON(dsize != (1 << dsize_log2));
33368c2ecf20Sopenharmony_ci#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
33378c2ecf20Sopenharmony_ci	fep->rx_align = 0xf;
33388c2ecf20Sopenharmony_ci	fep->tx_align = 0xf;
33398c2ecf20Sopenharmony_ci#else
33408c2ecf20Sopenharmony_ci	fep->rx_align = 0x3;
33418c2ecf20Sopenharmony_ci	fep->tx_align = 0x3;
33428c2ecf20Sopenharmony_ci#endif
33438c2ecf20Sopenharmony_ci
33448c2ecf20Sopenharmony_ci	/* Check mask of the streaming and coherent API */
33458c2ecf20Sopenharmony_ci	ret = dma_set_mask_and_coherent(&fep->pdev->dev, DMA_BIT_MASK(32));
33468c2ecf20Sopenharmony_ci	if (ret < 0) {
33478c2ecf20Sopenharmony_ci		dev_warn(&fep->pdev->dev, "No suitable DMA available\n");
33488c2ecf20Sopenharmony_ci		return ret;
33498c2ecf20Sopenharmony_ci	}
33508c2ecf20Sopenharmony_ci
33518c2ecf20Sopenharmony_ci	ret = fec_enet_alloc_queue(ndev);
33528c2ecf20Sopenharmony_ci	if (ret)
33538c2ecf20Sopenharmony_ci		return ret;
33548c2ecf20Sopenharmony_ci
33558c2ecf20Sopenharmony_ci	bd_size = (fep->total_tx_ring_size + fep->total_rx_ring_size) * dsize;
33568c2ecf20Sopenharmony_ci
33578c2ecf20Sopenharmony_ci	/* Allocate memory for buffer descriptors. */
33588c2ecf20Sopenharmony_ci	cbd_base = dmam_alloc_coherent(&fep->pdev->dev, bd_size, &bd_dma,
33598c2ecf20Sopenharmony_ci				       GFP_KERNEL);
33608c2ecf20Sopenharmony_ci	if (!cbd_base) {
33618c2ecf20Sopenharmony_ci		ret = -ENOMEM;
33628c2ecf20Sopenharmony_ci		goto free_queue_mem;
33638c2ecf20Sopenharmony_ci	}
33648c2ecf20Sopenharmony_ci
33658c2ecf20Sopenharmony_ci	/* Get the Ethernet address */
33668c2ecf20Sopenharmony_ci	fec_get_mac(ndev);
33678c2ecf20Sopenharmony_ci	/* make sure MAC we just acquired is programmed into the hw */
33688c2ecf20Sopenharmony_ci	fec_set_mac_address(ndev, NULL);
33698c2ecf20Sopenharmony_ci
33708c2ecf20Sopenharmony_ci	/* Set receive and transmit descriptor base. */
33718c2ecf20Sopenharmony_ci	for (i = 0; i < fep->num_rx_queues; i++) {
33728c2ecf20Sopenharmony_ci		struct fec_enet_priv_rx_q *rxq = fep->rx_queue[i];
33738c2ecf20Sopenharmony_ci		unsigned size = dsize * rxq->bd.ring_size;
33748c2ecf20Sopenharmony_ci
33758c2ecf20Sopenharmony_ci		rxq->bd.qid = i;
33768c2ecf20Sopenharmony_ci		rxq->bd.base = cbd_base;
33778c2ecf20Sopenharmony_ci		rxq->bd.cur = cbd_base;
33788c2ecf20Sopenharmony_ci		rxq->bd.dma = bd_dma;
33798c2ecf20Sopenharmony_ci		rxq->bd.dsize = dsize;
33808c2ecf20Sopenharmony_ci		rxq->bd.dsize_log2 = dsize_log2;
33818c2ecf20Sopenharmony_ci		rxq->bd.reg_desc_active = fep->hwp + offset_des_active_rxq[i];
33828c2ecf20Sopenharmony_ci		bd_dma += size;
33838c2ecf20Sopenharmony_ci		cbd_base = (struct bufdesc *)(((void *)cbd_base) + size);
33848c2ecf20Sopenharmony_ci		rxq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize);
33858c2ecf20Sopenharmony_ci	}
33868c2ecf20Sopenharmony_ci
33878c2ecf20Sopenharmony_ci	for (i = 0; i < fep->num_tx_queues; i++) {
33888c2ecf20Sopenharmony_ci		struct fec_enet_priv_tx_q *txq = fep->tx_queue[i];
33898c2ecf20Sopenharmony_ci		unsigned size = dsize * txq->bd.ring_size;
33908c2ecf20Sopenharmony_ci
33918c2ecf20Sopenharmony_ci		txq->bd.qid = i;
33928c2ecf20Sopenharmony_ci		txq->bd.base = cbd_base;
33938c2ecf20Sopenharmony_ci		txq->bd.cur = cbd_base;
33948c2ecf20Sopenharmony_ci		txq->bd.dma = bd_dma;
33958c2ecf20Sopenharmony_ci		txq->bd.dsize = dsize;
33968c2ecf20Sopenharmony_ci		txq->bd.dsize_log2 = dsize_log2;
33978c2ecf20Sopenharmony_ci		txq->bd.reg_desc_active = fep->hwp + offset_des_active_txq[i];
33988c2ecf20Sopenharmony_ci		bd_dma += size;
33998c2ecf20Sopenharmony_ci		cbd_base = (struct bufdesc *)(((void *)cbd_base) + size);
34008c2ecf20Sopenharmony_ci		txq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize);
34018c2ecf20Sopenharmony_ci	}
34028c2ecf20Sopenharmony_ci
34038c2ecf20Sopenharmony_ci
34048c2ecf20Sopenharmony_ci	/* The FEC Ethernet specific entries in the device structure */
34058c2ecf20Sopenharmony_ci	ndev->watchdog_timeo = TX_TIMEOUT;
34068c2ecf20Sopenharmony_ci	ndev->netdev_ops = &fec_netdev_ops;
34078c2ecf20Sopenharmony_ci	ndev->ethtool_ops = &fec_enet_ethtool_ops;
34088c2ecf20Sopenharmony_ci
34098c2ecf20Sopenharmony_ci	writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK);
34108c2ecf20Sopenharmony_ci	netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, NAPI_POLL_WEIGHT);
34118c2ecf20Sopenharmony_ci
34128c2ecf20Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_HAS_VLAN)
34138c2ecf20Sopenharmony_ci		/* enable hw VLAN support */
34148c2ecf20Sopenharmony_ci		ndev->features |= NETIF_F_HW_VLAN_CTAG_RX;
34158c2ecf20Sopenharmony_ci
34168c2ecf20Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_HAS_CSUM) {
34178c2ecf20Sopenharmony_ci		ndev->gso_max_segs = FEC_MAX_TSO_SEGS;
34188c2ecf20Sopenharmony_ci
34198c2ecf20Sopenharmony_ci		/* enable hw accelerator */
34208c2ecf20Sopenharmony_ci		ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM
34218c2ecf20Sopenharmony_ci				| NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO);
34228c2ecf20Sopenharmony_ci		fep->csum_flags |= FLAG_RX_CSUM_ENABLED;
34238c2ecf20Sopenharmony_ci	}
34248c2ecf20Sopenharmony_ci
34258c2ecf20Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_HAS_AVB) {
34268c2ecf20Sopenharmony_ci		fep->tx_align = 0;
34278c2ecf20Sopenharmony_ci		fep->rx_align = 0x3f;
34288c2ecf20Sopenharmony_ci	}
34298c2ecf20Sopenharmony_ci
34308c2ecf20Sopenharmony_ci	ndev->hw_features = ndev->features;
34318c2ecf20Sopenharmony_ci
34328c2ecf20Sopenharmony_ci	fec_restart(ndev);
34338c2ecf20Sopenharmony_ci
34348c2ecf20Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_MIB_CLEAR)
34358c2ecf20Sopenharmony_ci		fec_enet_clear_ethtool_stats(ndev);
34368c2ecf20Sopenharmony_ci	else
34378c2ecf20Sopenharmony_ci		fec_enet_update_ethtool_stats(ndev);
34388c2ecf20Sopenharmony_ci
34398c2ecf20Sopenharmony_ci	return 0;
34408c2ecf20Sopenharmony_ci
34418c2ecf20Sopenharmony_cifree_queue_mem:
34428c2ecf20Sopenharmony_ci	fec_enet_free_queue(ndev);
34438c2ecf20Sopenharmony_ci	return ret;
34448c2ecf20Sopenharmony_ci}
34458c2ecf20Sopenharmony_ci
34468c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
34478c2ecf20Sopenharmony_cistatic int fec_reset_phy(struct platform_device *pdev)
34488c2ecf20Sopenharmony_ci{
34498c2ecf20Sopenharmony_ci	int err, phy_reset;
34508c2ecf20Sopenharmony_ci	bool active_high = false;
34518c2ecf20Sopenharmony_ci	int msec = 1, phy_post_delay = 0;
34528c2ecf20Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
34538c2ecf20Sopenharmony_ci
34548c2ecf20Sopenharmony_ci	if (!np)
34558c2ecf20Sopenharmony_ci		return 0;
34568c2ecf20Sopenharmony_ci
34578c2ecf20Sopenharmony_ci	err = of_property_read_u32(np, "phy-reset-duration", &msec);
34588c2ecf20Sopenharmony_ci	/* A sane reset duration should not be longer than 1s */
34598c2ecf20Sopenharmony_ci	if (!err && msec > 1000)
34608c2ecf20Sopenharmony_ci		msec = 1;
34618c2ecf20Sopenharmony_ci
34628c2ecf20Sopenharmony_ci	phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0);
34638c2ecf20Sopenharmony_ci	if (phy_reset == -EPROBE_DEFER)
34648c2ecf20Sopenharmony_ci		return phy_reset;
34658c2ecf20Sopenharmony_ci	else if (!gpio_is_valid(phy_reset))
34668c2ecf20Sopenharmony_ci		return 0;
34678c2ecf20Sopenharmony_ci
34688c2ecf20Sopenharmony_ci	err = of_property_read_u32(np, "phy-reset-post-delay", &phy_post_delay);
34698c2ecf20Sopenharmony_ci	/* valid reset duration should be less than 1s */
34708c2ecf20Sopenharmony_ci	if (!err && phy_post_delay > 1000)
34718c2ecf20Sopenharmony_ci		return -EINVAL;
34728c2ecf20Sopenharmony_ci
34738c2ecf20Sopenharmony_ci	active_high = of_property_read_bool(np, "phy-reset-active-high");
34748c2ecf20Sopenharmony_ci
34758c2ecf20Sopenharmony_ci	err = devm_gpio_request_one(&pdev->dev, phy_reset,
34768c2ecf20Sopenharmony_ci			active_high ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW,
34778c2ecf20Sopenharmony_ci			"phy-reset");
34788c2ecf20Sopenharmony_ci	if (err) {
34798c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err);
34808c2ecf20Sopenharmony_ci		return err;
34818c2ecf20Sopenharmony_ci	}
34828c2ecf20Sopenharmony_ci
34838c2ecf20Sopenharmony_ci	if (msec > 20)
34848c2ecf20Sopenharmony_ci		msleep(msec);
34858c2ecf20Sopenharmony_ci	else
34868c2ecf20Sopenharmony_ci		usleep_range(msec * 1000, msec * 1000 + 1000);
34878c2ecf20Sopenharmony_ci
34888c2ecf20Sopenharmony_ci	gpio_set_value_cansleep(phy_reset, !active_high);
34898c2ecf20Sopenharmony_ci
34908c2ecf20Sopenharmony_ci	if (!phy_post_delay)
34918c2ecf20Sopenharmony_ci		return 0;
34928c2ecf20Sopenharmony_ci
34938c2ecf20Sopenharmony_ci	if (phy_post_delay > 20)
34948c2ecf20Sopenharmony_ci		msleep(phy_post_delay);
34958c2ecf20Sopenharmony_ci	else
34968c2ecf20Sopenharmony_ci		usleep_range(phy_post_delay * 1000,
34978c2ecf20Sopenharmony_ci			     phy_post_delay * 1000 + 1000);
34988c2ecf20Sopenharmony_ci
34998c2ecf20Sopenharmony_ci	return 0;
35008c2ecf20Sopenharmony_ci}
35018c2ecf20Sopenharmony_ci#else /* CONFIG_OF */
35028c2ecf20Sopenharmony_cistatic int fec_reset_phy(struct platform_device *pdev)
35038c2ecf20Sopenharmony_ci{
35048c2ecf20Sopenharmony_ci	/*
35058c2ecf20Sopenharmony_ci	 * In case of platform probe, the reset has been done
35068c2ecf20Sopenharmony_ci	 * by machine code.
35078c2ecf20Sopenharmony_ci	 */
35088c2ecf20Sopenharmony_ci	return 0;
35098c2ecf20Sopenharmony_ci}
35108c2ecf20Sopenharmony_ci#endif /* CONFIG_OF */
35118c2ecf20Sopenharmony_ci
35128c2ecf20Sopenharmony_cistatic void
35138c2ecf20Sopenharmony_cifec_enet_get_queue_num(struct platform_device *pdev, int *num_tx, int *num_rx)
35148c2ecf20Sopenharmony_ci{
35158c2ecf20Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
35168c2ecf20Sopenharmony_ci
35178c2ecf20Sopenharmony_ci	*num_tx = *num_rx = 1;
35188c2ecf20Sopenharmony_ci
35198c2ecf20Sopenharmony_ci	if (!np || !of_device_is_available(np))
35208c2ecf20Sopenharmony_ci		return;
35218c2ecf20Sopenharmony_ci
35228c2ecf20Sopenharmony_ci	/* parse the num of tx and rx queues */
35238c2ecf20Sopenharmony_ci	of_property_read_u32(np, "fsl,num-tx-queues", num_tx);
35248c2ecf20Sopenharmony_ci
35258c2ecf20Sopenharmony_ci	of_property_read_u32(np, "fsl,num-rx-queues", num_rx);
35268c2ecf20Sopenharmony_ci
35278c2ecf20Sopenharmony_ci	if (*num_tx < 1 || *num_tx > FEC_ENET_MAX_TX_QS) {
35288c2ecf20Sopenharmony_ci		dev_warn(&pdev->dev, "Invalid num_tx(=%d), fall back to 1\n",
35298c2ecf20Sopenharmony_ci			 *num_tx);
35308c2ecf20Sopenharmony_ci		*num_tx = 1;
35318c2ecf20Sopenharmony_ci		return;
35328c2ecf20Sopenharmony_ci	}
35338c2ecf20Sopenharmony_ci
35348c2ecf20Sopenharmony_ci	if (*num_rx < 1 || *num_rx > FEC_ENET_MAX_RX_QS) {
35358c2ecf20Sopenharmony_ci		dev_warn(&pdev->dev, "Invalid num_rx(=%d), fall back to 1\n",
35368c2ecf20Sopenharmony_ci			 *num_rx);
35378c2ecf20Sopenharmony_ci		*num_rx = 1;
35388c2ecf20Sopenharmony_ci		return;
35398c2ecf20Sopenharmony_ci	}
35408c2ecf20Sopenharmony_ci
35418c2ecf20Sopenharmony_ci}
35428c2ecf20Sopenharmony_ci
35438c2ecf20Sopenharmony_cistatic int fec_enet_get_irq_cnt(struct platform_device *pdev)
35448c2ecf20Sopenharmony_ci{
35458c2ecf20Sopenharmony_ci	int irq_cnt = platform_irq_count(pdev);
35468c2ecf20Sopenharmony_ci
35478c2ecf20Sopenharmony_ci	if (irq_cnt > FEC_IRQ_NUM)
35488c2ecf20Sopenharmony_ci		irq_cnt = FEC_IRQ_NUM;	/* last for pps */
35498c2ecf20Sopenharmony_ci	else if (irq_cnt == 2)
35508c2ecf20Sopenharmony_ci		irq_cnt = 1;	/* last for pps */
35518c2ecf20Sopenharmony_ci	else if (irq_cnt <= 0)
35528c2ecf20Sopenharmony_ci		irq_cnt = 1;	/* At least 1 irq is needed */
35538c2ecf20Sopenharmony_ci	return irq_cnt;
35548c2ecf20Sopenharmony_ci}
35558c2ecf20Sopenharmony_ci
35568c2ecf20Sopenharmony_cistatic int fec_enet_init_stop_mode(struct fec_enet_private *fep,
35578c2ecf20Sopenharmony_ci				   struct device_node *np)
35588c2ecf20Sopenharmony_ci{
35598c2ecf20Sopenharmony_ci	struct device_node *gpr_np;
35608c2ecf20Sopenharmony_ci	u32 out_val[3];
35618c2ecf20Sopenharmony_ci	int ret = 0;
35628c2ecf20Sopenharmony_ci
35638c2ecf20Sopenharmony_ci	gpr_np = of_parse_phandle(np, "fsl,stop-mode", 0);
35648c2ecf20Sopenharmony_ci	if (!gpr_np)
35658c2ecf20Sopenharmony_ci		return 0;
35668c2ecf20Sopenharmony_ci
35678c2ecf20Sopenharmony_ci	ret = of_property_read_u32_array(np, "fsl,stop-mode", out_val,
35688c2ecf20Sopenharmony_ci					 ARRAY_SIZE(out_val));
35698c2ecf20Sopenharmony_ci	if (ret) {
35708c2ecf20Sopenharmony_ci		dev_dbg(&fep->pdev->dev, "no stop mode property\n");
35718c2ecf20Sopenharmony_ci		goto out;
35728c2ecf20Sopenharmony_ci	}
35738c2ecf20Sopenharmony_ci
35748c2ecf20Sopenharmony_ci	fep->stop_gpr.gpr = syscon_node_to_regmap(gpr_np);
35758c2ecf20Sopenharmony_ci	if (IS_ERR(fep->stop_gpr.gpr)) {
35768c2ecf20Sopenharmony_ci		dev_err(&fep->pdev->dev, "could not find gpr regmap\n");
35778c2ecf20Sopenharmony_ci		ret = PTR_ERR(fep->stop_gpr.gpr);
35788c2ecf20Sopenharmony_ci		fep->stop_gpr.gpr = NULL;
35798c2ecf20Sopenharmony_ci		goto out;
35808c2ecf20Sopenharmony_ci	}
35818c2ecf20Sopenharmony_ci
35828c2ecf20Sopenharmony_ci	fep->stop_gpr.reg = out_val[1];
35838c2ecf20Sopenharmony_ci	fep->stop_gpr.bit = out_val[2];
35848c2ecf20Sopenharmony_ci
35858c2ecf20Sopenharmony_ciout:
35868c2ecf20Sopenharmony_ci	of_node_put(gpr_np);
35878c2ecf20Sopenharmony_ci
35888c2ecf20Sopenharmony_ci	return ret;
35898c2ecf20Sopenharmony_ci}
35908c2ecf20Sopenharmony_ci
35918c2ecf20Sopenharmony_cistatic int
35928c2ecf20Sopenharmony_cifec_probe(struct platform_device *pdev)
35938c2ecf20Sopenharmony_ci{
35948c2ecf20Sopenharmony_ci	struct fec_enet_private *fep;
35958c2ecf20Sopenharmony_ci	struct fec_platform_data *pdata;
35968c2ecf20Sopenharmony_ci	phy_interface_t interface;
35978c2ecf20Sopenharmony_ci	struct net_device *ndev;
35988c2ecf20Sopenharmony_ci	int i, irq, ret = 0;
35998c2ecf20Sopenharmony_ci	const struct of_device_id *of_id;
36008c2ecf20Sopenharmony_ci	static int dev_id;
36018c2ecf20Sopenharmony_ci	struct device_node *np = pdev->dev.of_node, *phy_node;
36028c2ecf20Sopenharmony_ci	int num_tx_qs;
36038c2ecf20Sopenharmony_ci	int num_rx_qs;
36048c2ecf20Sopenharmony_ci	char irq_name[8];
36058c2ecf20Sopenharmony_ci	int irq_cnt;
36068c2ecf20Sopenharmony_ci	struct fec_devinfo *dev_info;
36078c2ecf20Sopenharmony_ci
36088c2ecf20Sopenharmony_ci	fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs);
36098c2ecf20Sopenharmony_ci
36108c2ecf20Sopenharmony_ci	/* Init network device */
36118c2ecf20Sopenharmony_ci	ndev = alloc_etherdev_mqs(sizeof(struct fec_enet_private) +
36128c2ecf20Sopenharmony_ci				  FEC_STATS_SIZE, num_tx_qs, num_rx_qs);
36138c2ecf20Sopenharmony_ci	if (!ndev)
36148c2ecf20Sopenharmony_ci		return -ENOMEM;
36158c2ecf20Sopenharmony_ci
36168c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(ndev, &pdev->dev);
36178c2ecf20Sopenharmony_ci
36188c2ecf20Sopenharmony_ci	/* setup board info structure */
36198c2ecf20Sopenharmony_ci	fep = netdev_priv(ndev);
36208c2ecf20Sopenharmony_ci
36218c2ecf20Sopenharmony_ci	of_id = of_match_device(fec_dt_ids, &pdev->dev);
36228c2ecf20Sopenharmony_ci	if (of_id)
36238c2ecf20Sopenharmony_ci		pdev->id_entry = of_id->data;
36248c2ecf20Sopenharmony_ci	dev_info = (struct fec_devinfo *)pdev->id_entry->driver_data;
36258c2ecf20Sopenharmony_ci	if (dev_info)
36268c2ecf20Sopenharmony_ci		fep->quirks = dev_info->quirks;
36278c2ecf20Sopenharmony_ci
36288c2ecf20Sopenharmony_ci	fep->netdev = ndev;
36298c2ecf20Sopenharmony_ci	fep->num_rx_queues = num_rx_qs;
36308c2ecf20Sopenharmony_ci	fep->num_tx_queues = num_tx_qs;
36318c2ecf20Sopenharmony_ci
36328c2ecf20Sopenharmony_ci#if !defined(CONFIG_M5272)
36338c2ecf20Sopenharmony_ci	/* default enable pause frame auto negotiation */
36348c2ecf20Sopenharmony_ci	if (fep->quirks & FEC_QUIRK_HAS_GBIT)
36358c2ecf20Sopenharmony_ci		fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG;
36368c2ecf20Sopenharmony_ci#endif
36378c2ecf20Sopenharmony_ci
36388c2ecf20Sopenharmony_ci	/* Select default pin state */
36398c2ecf20Sopenharmony_ci	pinctrl_pm_select_default_state(&pdev->dev);
36408c2ecf20Sopenharmony_ci
36418c2ecf20Sopenharmony_ci	fep->hwp = devm_platform_ioremap_resource(pdev, 0);
36428c2ecf20Sopenharmony_ci	if (IS_ERR(fep->hwp)) {
36438c2ecf20Sopenharmony_ci		ret = PTR_ERR(fep->hwp);
36448c2ecf20Sopenharmony_ci		goto failed_ioremap;
36458c2ecf20Sopenharmony_ci	}
36468c2ecf20Sopenharmony_ci
36478c2ecf20Sopenharmony_ci	fep->pdev = pdev;
36488c2ecf20Sopenharmony_ci	fep->dev_id = dev_id++;
36498c2ecf20Sopenharmony_ci
36508c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, ndev);
36518c2ecf20Sopenharmony_ci
36528c2ecf20Sopenharmony_ci	if ((of_machine_is_compatible("fsl,imx6q") ||
36538c2ecf20Sopenharmony_ci	     of_machine_is_compatible("fsl,imx6dl")) &&
36548c2ecf20Sopenharmony_ci	    !of_property_read_bool(np, "fsl,err006687-workaround-present"))
36558c2ecf20Sopenharmony_ci		fep->quirks |= FEC_QUIRK_ERR006687;
36568c2ecf20Sopenharmony_ci
36578c2ecf20Sopenharmony_ci	if (of_get_property(np, "fsl,magic-packet", NULL))
36588c2ecf20Sopenharmony_ci		fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET;
36598c2ecf20Sopenharmony_ci
36608c2ecf20Sopenharmony_ci	ret = fec_enet_init_stop_mode(fep, np);
36618c2ecf20Sopenharmony_ci	if (ret)
36628c2ecf20Sopenharmony_ci		goto failed_stop_mode;
36638c2ecf20Sopenharmony_ci
36648c2ecf20Sopenharmony_ci	phy_node = of_parse_phandle(np, "phy-handle", 0);
36658c2ecf20Sopenharmony_ci	if (!phy_node && of_phy_is_fixed_link(np)) {
36668c2ecf20Sopenharmony_ci		ret = of_phy_register_fixed_link(np);
36678c2ecf20Sopenharmony_ci		if (ret < 0) {
36688c2ecf20Sopenharmony_ci			dev_err(&pdev->dev,
36698c2ecf20Sopenharmony_ci				"broken fixed-link specification\n");
36708c2ecf20Sopenharmony_ci			goto failed_phy;
36718c2ecf20Sopenharmony_ci		}
36728c2ecf20Sopenharmony_ci		phy_node = of_node_get(np);
36738c2ecf20Sopenharmony_ci	}
36748c2ecf20Sopenharmony_ci	fep->phy_node = phy_node;
36758c2ecf20Sopenharmony_ci
36768c2ecf20Sopenharmony_ci	ret = of_get_phy_mode(pdev->dev.of_node, &interface);
36778c2ecf20Sopenharmony_ci	if (ret) {
36788c2ecf20Sopenharmony_ci		pdata = dev_get_platdata(&pdev->dev);
36798c2ecf20Sopenharmony_ci		if (pdata)
36808c2ecf20Sopenharmony_ci			fep->phy_interface = pdata->phy;
36818c2ecf20Sopenharmony_ci		else
36828c2ecf20Sopenharmony_ci			fep->phy_interface = PHY_INTERFACE_MODE_MII;
36838c2ecf20Sopenharmony_ci	} else {
36848c2ecf20Sopenharmony_ci		fep->phy_interface = interface;
36858c2ecf20Sopenharmony_ci	}
36868c2ecf20Sopenharmony_ci
36878c2ecf20Sopenharmony_ci	fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
36888c2ecf20Sopenharmony_ci	if (IS_ERR(fep->clk_ipg)) {
36898c2ecf20Sopenharmony_ci		ret = PTR_ERR(fep->clk_ipg);
36908c2ecf20Sopenharmony_ci		goto failed_clk;
36918c2ecf20Sopenharmony_ci	}
36928c2ecf20Sopenharmony_ci
36938c2ecf20Sopenharmony_ci	fep->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
36948c2ecf20Sopenharmony_ci	if (IS_ERR(fep->clk_ahb)) {
36958c2ecf20Sopenharmony_ci		ret = PTR_ERR(fep->clk_ahb);
36968c2ecf20Sopenharmony_ci		goto failed_clk;
36978c2ecf20Sopenharmony_ci	}
36988c2ecf20Sopenharmony_ci
36998c2ecf20Sopenharmony_ci	fep->itr_clk_rate = clk_get_rate(fep->clk_ahb);
37008c2ecf20Sopenharmony_ci
37018c2ecf20Sopenharmony_ci	/* enet_out is optional, depends on board */
37028c2ecf20Sopenharmony_ci	fep->clk_enet_out = devm_clk_get(&pdev->dev, "enet_out");
37038c2ecf20Sopenharmony_ci	if (IS_ERR(fep->clk_enet_out))
37048c2ecf20Sopenharmony_ci		fep->clk_enet_out = NULL;
37058c2ecf20Sopenharmony_ci
37068c2ecf20Sopenharmony_ci	fep->ptp_clk_on = false;
37078c2ecf20Sopenharmony_ci	mutex_init(&fep->ptp_clk_mutex);
37088c2ecf20Sopenharmony_ci
37098c2ecf20Sopenharmony_ci	/* clk_ref is optional, depends on board */
37108c2ecf20Sopenharmony_ci	fep->clk_ref = devm_clk_get(&pdev->dev, "enet_clk_ref");
37118c2ecf20Sopenharmony_ci	if (IS_ERR(fep->clk_ref))
37128c2ecf20Sopenharmony_ci		fep->clk_ref = NULL;
37138c2ecf20Sopenharmony_ci
37148c2ecf20Sopenharmony_ci	fep->bufdesc_ex = fep->quirks & FEC_QUIRK_HAS_BUFDESC_EX;
37158c2ecf20Sopenharmony_ci	fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp");
37168c2ecf20Sopenharmony_ci	if (IS_ERR(fep->clk_ptp)) {
37178c2ecf20Sopenharmony_ci		fep->clk_ptp = NULL;
37188c2ecf20Sopenharmony_ci		fep->bufdesc_ex = false;
37198c2ecf20Sopenharmony_ci	}
37208c2ecf20Sopenharmony_ci
37218c2ecf20Sopenharmony_ci	ret = fec_enet_clk_enable(ndev, true);
37228c2ecf20Sopenharmony_ci	if (ret)
37238c2ecf20Sopenharmony_ci		goto failed_clk;
37248c2ecf20Sopenharmony_ci
37258c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(fep->clk_ipg);
37268c2ecf20Sopenharmony_ci	if (ret)
37278c2ecf20Sopenharmony_ci		goto failed_clk_ipg;
37288c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(fep->clk_ahb);
37298c2ecf20Sopenharmony_ci	if (ret)
37308c2ecf20Sopenharmony_ci		goto failed_clk_ahb;
37318c2ecf20Sopenharmony_ci
37328c2ecf20Sopenharmony_ci	fep->reg_phy = devm_regulator_get_optional(&pdev->dev, "phy");
37338c2ecf20Sopenharmony_ci	if (!IS_ERR(fep->reg_phy)) {
37348c2ecf20Sopenharmony_ci		ret = regulator_enable(fep->reg_phy);
37358c2ecf20Sopenharmony_ci		if (ret) {
37368c2ecf20Sopenharmony_ci			dev_err(&pdev->dev,
37378c2ecf20Sopenharmony_ci				"Failed to enable phy regulator: %d\n", ret);
37388c2ecf20Sopenharmony_ci			goto failed_regulator;
37398c2ecf20Sopenharmony_ci		}
37408c2ecf20Sopenharmony_ci	} else {
37418c2ecf20Sopenharmony_ci		if (PTR_ERR(fep->reg_phy) == -EPROBE_DEFER) {
37428c2ecf20Sopenharmony_ci			ret = -EPROBE_DEFER;
37438c2ecf20Sopenharmony_ci			goto failed_regulator;
37448c2ecf20Sopenharmony_ci		}
37458c2ecf20Sopenharmony_ci		fep->reg_phy = NULL;
37468c2ecf20Sopenharmony_ci	}
37478c2ecf20Sopenharmony_ci
37488c2ecf20Sopenharmony_ci	pm_runtime_set_autosuspend_delay(&pdev->dev, FEC_MDIO_PM_TIMEOUT);
37498c2ecf20Sopenharmony_ci	pm_runtime_use_autosuspend(&pdev->dev);
37508c2ecf20Sopenharmony_ci	pm_runtime_get_noresume(&pdev->dev);
37518c2ecf20Sopenharmony_ci	pm_runtime_set_active(&pdev->dev);
37528c2ecf20Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
37538c2ecf20Sopenharmony_ci
37548c2ecf20Sopenharmony_ci	ret = fec_reset_phy(pdev);
37558c2ecf20Sopenharmony_ci	if (ret)
37568c2ecf20Sopenharmony_ci		goto failed_reset;
37578c2ecf20Sopenharmony_ci
37588c2ecf20Sopenharmony_ci	irq_cnt = fec_enet_get_irq_cnt(pdev);
37598c2ecf20Sopenharmony_ci	if (fep->bufdesc_ex)
37608c2ecf20Sopenharmony_ci		fec_ptp_init(pdev, irq_cnt);
37618c2ecf20Sopenharmony_ci
37628c2ecf20Sopenharmony_ci	ret = fec_enet_init(ndev);
37638c2ecf20Sopenharmony_ci	if (ret)
37648c2ecf20Sopenharmony_ci		goto failed_init;
37658c2ecf20Sopenharmony_ci
37668c2ecf20Sopenharmony_ci	for (i = 0; i < irq_cnt; i++) {
37678c2ecf20Sopenharmony_ci		snprintf(irq_name, sizeof(irq_name), "int%d", i);
37688c2ecf20Sopenharmony_ci		irq = platform_get_irq_byname_optional(pdev, irq_name);
37698c2ecf20Sopenharmony_ci		if (irq < 0)
37708c2ecf20Sopenharmony_ci			irq = platform_get_irq(pdev, i);
37718c2ecf20Sopenharmony_ci		if (irq < 0) {
37728c2ecf20Sopenharmony_ci			ret = irq;
37738c2ecf20Sopenharmony_ci			goto failed_irq;
37748c2ecf20Sopenharmony_ci		}
37758c2ecf20Sopenharmony_ci		ret = devm_request_irq(&pdev->dev, irq, fec_enet_interrupt,
37768c2ecf20Sopenharmony_ci				       0, pdev->name, ndev);
37778c2ecf20Sopenharmony_ci		if (ret)
37788c2ecf20Sopenharmony_ci			goto failed_irq;
37798c2ecf20Sopenharmony_ci
37808c2ecf20Sopenharmony_ci		fep->irq[i] = irq;
37818c2ecf20Sopenharmony_ci	}
37828c2ecf20Sopenharmony_ci
37838c2ecf20Sopenharmony_ci	ret = fec_enet_mii_init(pdev);
37848c2ecf20Sopenharmony_ci	if (ret)
37858c2ecf20Sopenharmony_ci		goto failed_mii_init;
37868c2ecf20Sopenharmony_ci
37878c2ecf20Sopenharmony_ci	/* Carrier starts down, phylib will bring it up */
37888c2ecf20Sopenharmony_ci	netif_carrier_off(ndev);
37898c2ecf20Sopenharmony_ci	fec_enet_clk_enable(ndev, false);
37908c2ecf20Sopenharmony_ci	pinctrl_pm_select_sleep_state(&pdev->dev);
37918c2ecf20Sopenharmony_ci
37928c2ecf20Sopenharmony_ci	ndev->max_mtu = PKT_MAXBUF_SIZE - ETH_HLEN - ETH_FCS_LEN;
37938c2ecf20Sopenharmony_ci
37948c2ecf20Sopenharmony_ci	ret = register_netdev(ndev);
37958c2ecf20Sopenharmony_ci	if (ret)
37968c2ecf20Sopenharmony_ci		goto failed_register;
37978c2ecf20Sopenharmony_ci
37988c2ecf20Sopenharmony_ci	device_init_wakeup(&ndev->dev, fep->wol_flag &
37998c2ecf20Sopenharmony_ci			   FEC_WOL_HAS_MAGIC_PACKET);
38008c2ecf20Sopenharmony_ci
38018c2ecf20Sopenharmony_ci	if (fep->bufdesc_ex && fep->ptp_clock)
38028c2ecf20Sopenharmony_ci		netdev_info(ndev, "registered PHC device %d\n", fep->dev_id);
38038c2ecf20Sopenharmony_ci
38048c2ecf20Sopenharmony_ci	fep->rx_copybreak = COPYBREAK_DEFAULT;
38058c2ecf20Sopenharmony_ci	INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work);
38068c2ecf20Sopenharmony_ci
38078c2ecf20Sopenharmony_ci	pm_runtime_mark_last_busy(&pdev->dev);
38088c2ecf20Sopenharmony_ci	pm_runtime_put_autosuspend(&pdev->dev);
38098c2ecf20Sopenharmony_ci
38108c2ecf20Sopenharmony_ci	return 0;
38118c2ecf20Sopenharmony_ci
38128c2ecf20Sopenharmony_cifailed_register:
38138c2ecf20Sopenharmony_ci	fec_enet_mii_remove(fep);
38148c2ecf20Sopenharmony_cifailed_mii_init:
38158c2ecf20Sopenharmony_cifailed_irq:
38168c2ecf20Sopenharmony_cifailed_init:
38178c2ecf20Sopenharmony_ci	fec_ptp_stop(pdev);
38188c2ecf20Sopenharmony_cifailed_reset:
38198c2ecf20Sopenharmony_ci	pm_runtime_put_noidle(&pdev->dev);
38208c2ecf20Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
38218c2ecf20Sopenharmony_ci	if (fep->reg_phy)
38228c2ecf20Sopenharmony_ci		regulator_disable(fep->reg_phy);
38238c2ecf20Sopenharmony_cifailed_regulator:
38248c2ecf20Sopenharmony_ci	clk_disable_unprepare(fep->clk_ahb);
38258c2ecf20Sopenharmony_cifailed_clk_ahb:
38268c2ecf20Sopenharmony_ci	clk_disable_unprepare(fep->clk_ipg);
38278c2ecf20Sopenharmony_cifailed_clk_ipg:
38288c2ecf20Sopenharmony_ci	fec_enet_clk_enable(ndev, false);
38298c2ecf20Sopenharmony_cifailed_clk:
38308c2ecf20Sopenharmony_ci	if (of_phy_is_fixed_link(np))
38318c2ecf20Sopenharmony_ci		of_phy_deregister_fixed_link(np);
38328c2ecf20Sopenharmony_ci	of_node_put(phy_node);
38338c2ecf20Sopenharmony_cifailed_stop_mode:
38348c2ecf20Sopenharmony_cifailed_phy:
38358c2ecf20Sopenharmony_ci	dev_id--;
38368c2ecf20Sopenharmony_cifailed_ioremap:
38378c2ecf20Sopenharmony_ci	free_netdev(ndev);
38388c2ecf20Sopenharmony_ci
38398c2ecf20Sopenharmony_ci	return ret;
38408c2ecf20Sopenharmony_ci}
38418c2ecf20Sopenharmony_ci
38428c2ecf20Sopenharmony_cistatic int
38438c2ecf20Sopenharmony_cifec_drv_remove(struct platform_device *pdev)
38448c2ecf20Sopenharmony_ci{
38458c2ecf20Sopenharmony_ci	struct net_device *ndev = platform_get_drvdata(pdev);
38468c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
38478c2ecf20Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
38488c2ecf20Sopenharmony_ci	int ret;
38498c2ecf20Sopenharmony_ci
38508c2ecf20Sopenharmony_ci	ret = pm_runtime_get_sync(&pdev->dev);
38518c2ecf20Sopenharmony_ci	if (ret < 0)
38528c2ecf20Sopenharmony_ci		dev_err(&pdev->dev,
38538c2ecf20Sopenharmony_ci			"Failed to resume device in remove callback (%pe)\n",
38548c2ecf20Sopenharmony_ci			ERR_PTR(ret));
38558c2ecf20Sopenharmony_ci
38568c2ecf20Sopenharmony_ci	cancel_work_sync(&fep->tx_timeout_work);
38578c2ecf20Sopenharmony_ci	fec_ptp_stop(pdev);
38588c2ecf20Sopenharmony_ci	unregister_netdev(ndev);
38598c2ecf20Sopenharmony_ci	fec_enet_mii_remove(fep);
38608c2ecf20Sopenharmony_ci	if (fep->reg_phy)
38618c2ecf20Sopenharmony_ci		regulator_disable(fep->reg_phy);
38628c2ecf20Sopenharmony_ci
38638c2ecf20Sopenharmony_ci	if (of_phy_is_fixed_link(np))
38648c2ecf20Sopenharmony_ci		of_phy_deregister_fixed_link(np);
38658c2ecf20Sopenharmony_ci	of_node_put(fep->phy_node);
38668c2ecf20Sopenharmony_ci
38678c2ecf20Sopenharmony_ci	/* After pm_runtime_get_sync() failed, the clks are still off, so skip
38688c2ecf20Sopenharmony_ci	 * disabling them again.
38698c2ecf20Sopenharmony_ci	 */
38708c2ecf20Sopenharmony_ci	if (ret >= 0) {
38718c2ecf20Sopenharmony_ci		clk_disable_unprepare(fep->clk_ahb);
38728c2ecf20Sopenharmony_ci		clk_disable_unprepare(fep->clk_ipg);
38738c2ecf20Sopenharmony_ci	}
38748c2ecf20Sopenharmony_ci	pm_runtime_put_noidle(&pdev->dev);
38758c2ecf20Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
38768c2ecf20Sopenharmony_ci
38778c2ecf20Sopenharmony_ci	free_netdev(ndev);
38788c2ecf20Sopenharmony_ci	return 0;
38798c2ecf20Sopenharmony_ci}
38808c2ecf20Sopenharmony_ci
38818c2ecf20Sopenharmony_cistatic int __maybe_unused fec_suspend(struct device *dev)
38828c2ecf20Sopenharmony_ci{
38838c2ecf20Sopenharmony_ci	struct net_device *ndev = dev_get_drvdata(dev);
38848c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
38858c2ecf20Sopenharmony_ci
38868c2ecf20Sopenharmony_ci	rtnl_lock();
38878c2ecf20Sopenharmony_ci	if (netif_running(ndev)) {
38888c2ecf20Sopenharmony_ci		if (fep->wol_flag & FEC_WOL_FLAG_ENABLE)
38898c2ecf20Sopenharmony_ci			fep->wol_flag |= FEC_WOL_FLAG_SLEEP_ON;
38908c2ecf20Sopenharmony_ci		phy_stop(ndev->phydev);
38918c2ecf20Sopenharmony_ci		napi_disable(&fep->napi);
38928c2ecf20Sopenharmony_ci		netif_tx_lock_bh(ndev);
38938c2ecf20Sopenharmony_ci		netif_device_detach(ndev);
38948c2ecf20Sopenharmony_ci		netif_tx_unlock_bh(ndev);
38958c2ecf20Sopenharmony_ci		fec_stop(ndev);
38968c2ecf20Sopenharmony_ci		fec_enet_clk_enable(ndev, false);
38978c2ecf20Sopenharmony_ci		if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE))
38988c2ecf20Sopenharmony_ci			pinctrl_pm_select_sleep_state(&fep->pdev->dev);
38998c2ecf20Sopenharmony_ci	}
39008c2ecf20Sopenharmony_ci	rtnl_unlock();
39018c2ecf20Sopenharmony_ci
39028c2ecf20Sopenharmony_ci	if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE))
39038c2ecf20Sopenharmony_ci		regulator_disable(fep->reg_phy);
39048c2ecf20Sopenharmony_ci
39058c2ecf20Sopenharmony_ci	/* SOC supply clock to phy, when clock is disabled, phy link down
39068c2ecf20Sopenharmony_ci	 * SOC control phy regulator, when regulator is disabled, phy link down
39078c2ecf20Sopenharmony_ci	 */
39088c2ecf20Sopenharmony_ci	if (fep->clk_enet_out || fep->reg_phy)
39098c2ecf20Sopenharmony_ci		fep->link = 0;
39108c2ecf20Sopenharmony_ci
39118c2ecf20Sopenharmony_ci	return 0;
39128c2ecf20Sopenharmony_ci}
39138c2ecf20Sopenharmony_ci
39148c2ecf20Sopenharmony_cistatic int __maybe_unused fec_resume(struct device *dev)
39158c2ecf20Sopenharmony_ci{
39168c2ecf20Sopenharmony_ci	struct net_device *ndev = dev_get_drvdata(dev);
39178c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
39188c2ecf20Sopenharmony_ci	int ret;
39198c2ecf20Sopenharmony_ci	int val;
39208c2ecf20Sopenharmony_ci
39218c2ecf20Sopenharmony_ci	if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) {
39228c2ecf20Sopenharmony_ci		ret = regulator_enable(fep->reg_phy);
39238c2ecf20Sopenharmony_ci		if (ret)
39248c2ecf20Sopenharmony_ci			return ret;
39258c2ecf20Sopenharmony_ci	}
39268c2ecf20Sopenharmony_ci
39278c2ecf20Sopenharmony_ci	rtnl_lock();
39288c2ecf20Sopenharmony_ci	if (netif_running(ndev)) {
39298c2ecf20Sopenharmony_ci		ret = fec_enet_clk_enable(ndev, true);
39308c2ecf20Sopenharmony_ci		if (ret) {
39318c2ecf20Sopenharmony_ci			rtnl_unlock();
39328c2ecf20Sopenharmony_ci			goto failed_clk;
39338c2ecf20Sopenharmony_ci		}
39348c2ecf20Sopenharmony_ci		if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) {
39358c2ecf20Sopenharmony_ci			fec_enet_stop_mode(fep, false);
39368c2ecf20Sopenharmony_ci
39378c2ecf20Sopenharmony_ci			val = readl(fep->hwp + FEC_ECNTRL);
39388c2ecf20Sopenharmony_ci			val &= ~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
39398c2ecf20Sopenharmony_ci			writel(val, fep->hwp + FEC_ECNTRL);
39408c2ecf20Sopenharmony_ci			fep->wol_flag &= ~FEC_WOL_FLAG_SLEEP_ON;
39418c2ecf20Sopenharmony_ci		} else {
39428c2ecf20Sopenharmony_ci			pinctrl_pm_select_default_state(&fep->pdev->dev);
39438c2ecf20Sopenharmony_ci		}
39448c2ecf20Sopenharmony_ci		fec_restart(ndev);
39458c2ecf20Sopenharmony_ci		netif_tx_lock_bh(ndev);
39468c2ecf20Sopenharmony_ci		netif_device_attach(ndev);
39478c2ecf20Sopenharmony_ci		netif_tx_unlock_bh(ndev);
39488c2ecf20Sopenharmony_ci		napi_enable(&fep->napi);
39498c2ecf20Sopenharmony_ci		phy_start(ndev->phydev);
39508c2ecf20Sopenharmony_ci	}
39518c2ecf20Sopenharmony_ci	rtnl_unlock();
39528c2ecf20Sopenharmony_ci
39538c2ecf20Sopenharmony_ci	return 0;
39548c2ecf20Sopenharmony_ci
39558c2ecf20Sopenharmony_cifailed_clk:
39568c2ecf20Sopenharmony_ci	if (fep->reg_phy)
39578c2ecf20Sopenharmony_ci		regulator_disable(fep->reg_phy);
39588c2ecf20Sopenharmony_ci	return ret;
39598c2ecf20Sopenharmony_ci}
39608c2ecf20Sopenharmony_ci
39618c2ecf20Sopenharmony_cistatic int __maybe_unused fec_runtime_suspend(struct device *dev)
39628c2ecf20Sopenharmony_ci{
39638c2ecf20Sopenharmony_ci	struct net_device *ndev = dev_get_drvdata(dev);
39648c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
39658c2ecf20Sopenharmony_ci
39668c2ecf20Sopenharmony_ci	clk_disable_unprepare(fep->clk_ahb);
39678c2ecf20Sopenharmony_ci	clk_disable_unprepare(fep->clk_ipg);
39688c2ecf20Sopenharmony_ci
39698c2ecf20Sopenharmony_ci	return 0;
39708c2ecf20Sopenharmony_ci}
39718c2ecf20Sopenharmony_ci
39728c2ecf20Sopenharmony_cistatic int __maybe_unused fec_runtime_resume(struct device *dev)
39738c2ecf20Sopenharmony_ci{
39748c2ecf20Sopenharmony_ci	struct net_device *ndev = dev_get_drvdata(dev);
39758c2ecf20Sopenharmony_ci	struct fec_enet_private *fep = netdev_priv(ndev);
39768c2ecf20Sopenharmony_ci	int ret;
39778c2ecf20Sopenharmony_ci
39788c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(fep->clk_ahb);
39798c2ecf20Sopenharmony_ci	if (ret)
39808c2ecf20Sopenharmony_ci		return ret;
39818c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(fep->clk_ipg);
39828c2ecf20Sopenharmony_ci	if (ret)
39838c2ecf20Sopenharmony_ci		goto failed_clk_ipg;
39848c2ecf20Sopenharmony_ci
39858c2ecf20Sopenharmony_ci	return 0;
39868c2ecf20Sopenharmony_ci
39878c2ecf20Sopenharmony_cifailed_clk_ipg:
39888c2ecf20Sopenharmony_ci	clk_disable_unprepare(fep->clk_ahb);
39898c2ecf20Sopenharmony_ci	return ret;
39908c2ecf20Sopenharmony_ci}
39918c2ecf20Sopenharmony_ci
39928c2ecf20Sopenharmony_cistatic const struct dev_pm_ops fec_pm_ops = {
39938c2ecf20Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(fec_suspend, fec_resume)
39948c2ecf20Sopenharmony_ci	SET_RUNTIME_PM_OPS(fec_runtime_suspend, fec_runtime_resume, NULL)
39958c2ecf20Sopenharmony_ci};
39968c2ecf20Sopenharmony_ci
39978c2ecf20Sopenharmony_cistatic struct platform_driver fec_driver = {
39988c2ecf20Sopenharmony_ci	.driver	= {
39998c2ecf20Sopenharmony_ci		.name	= DRIVER_NAME,
40008c2ecf20Sopenharmony_ci		.pm	= &fec_pm_ops,
40018c2ecf20Sopenharmony_ci		.of_match_table = fec_dt_ids,
40028c2ecf20Sopenharmony_ci		.suppress_bind_attrs = true,
40038c2ecf20Sopenharmony_ci	},
40048c2ecf20Sopenharmony_ci	.id_table = fec_devtype,
40058c2ecf20Sopenharmony_ci	.probe	= fec_probe,
40068c2ecf20Sopenharmony_ci	.remove	= fec_drv_remove,
40078c2ecf20Sopenharmony_ci};
40088c2ecf20Sopenharmony_ci
40098c2ecf20Sopenharmony_cimodule_platform_driver(fec_driver);
40108c2ecf20Sopenharmony_ci
40118c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:"DRIVER_NAME);
40128c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
4013