11f508124SFabio Estevam // SPDX-License-Identifier: GPL-2.0+ 2793fc096SFrank Li /* 3793fc096SFrank Li * Fast Ethernet Controller (FEC) driver for Motorola MPC8xx. 4793fc096SFrank Li * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) 5793fc096SFrank Li * 6793fc096SFrank Li * Right now, I am very wasteful with the buffers. I allocate memory 7793fc096SFrank Li * pages and then divide them into 2K frame buffers. This way I know I 8793fc096SFrank Li * have buffers large enough to hold one frame within one buffer descriptor. 9793fc096SFrank Li * Once I get this working, I will use 64 or 128 byte CPM buffers, which 10793fc096SFrank Li * will be much more memory efficient and will easily handle lots of 11793fc096SFrank Li * small packets. 12793fc096SFrank Li * 13793fc096SFrank Li * Much better multiple PHY support by Magnus Damm. 14793fc096SFrank Li * Copyright (c) 2000 Ericsson Radio Systems AB. 15793fc096SFrank Li * 16793fc096SFrank Li * Support for FEC controller of ColdFire processors. 17793fc096SFrank Li * Copyright (c) 2001-2005 Greg Ungerer (gerg@snapgear.com) 18793fc096SFrank Li * 19793fc096SFrank Li * Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be) 20793fc096SFrank Li * Copyright (c) 2004-2006 Macq Electronique SA. 21793fc096SFrank Li * 22793fc096SFrank Li * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. 23793fc096SFrank Li */ 24793fc096SFrank Li 25793fc096SFrank Li #include <linux/module.h> 26793fc096SFrank Li #include <linux/kernel.h> 27793fc096SFrank Li #include <linux/string.h> 288fff755eSAndrew Lunn #include <linux/pm_runtime.h> 29793fc096SFrank Li #include <linux/ptrace.h> 30793fc096SFrank Li #include <linux/errno.h> 31793fc096SFrank Li #include <linux/ioport.h> 32793fc096SFrank Li #include <linux/slab.h> 33793fc096SFrank Li #include <linux/interrupt.h> 34793fc096SFrank Li #include <linux/delay.h> 35793fc096SFrank Li #include <linux/netdevice.h> 36793fc096SFrank Li #include <linux/etherdevice.h> 37793fc096SFrank Li #include <linux/skbuff.h> 384c09eed9SJim Baxter #include <linux/in.h> 394c09eed9SJim Baxter #include <linux/ip.h> 404c09eed9SJim Baxter #include <net/ip.h> 416016ba34SOleksij Rempel #include <net/selftests.h> 4279f33912SNimrod Andy #include <net/tso.h> 434c09eed9SJim Baxter #include <linux/tcp.h> 444c09eed9SJim Baxter #include <linux/udp.h> 454c09eed9SJim Baxter #include <linux/icmp.h> 46793fc096SFrank Li #include <linux/spinlock.h> 47793fc096SFrank Li #include <linux/workqueue.h> 48793fc096SFrank Li #include <linux/bitops.h> 49793fc096SFrank Li #include <linux/io.h> 50793fc096SFrank Li #include <linux/irq.h> 51793fc096SFrank Li #include <linux/clk.h> 5216f6e983SKrzysztof Kozlowski #include <linux/crc32.h> 53793fc096SFrank Li #include <linux/platform_device.h> 547f854420SAndrew Lunn #include <linux/mdio.h> 55793fc096SFrank Li #include <linux/phy.h> 56793fc096SFrank Li #include <linux/fec.h> 57793fc096SFrank Li #include <linux/of.h> 58793fc096SFrank Li #include <linux/of_device.h> 59407066f8SUwe Kleine-König #include <linux/of_mdio.h> 60793fc096SFrank Li #include <linux/of_net.h> 61793fc096SFrank Li #include <linux/regulator/consumer.h> 62cdffcf1bSJim Baxter #include <linux/if_vlan.h> 63a68ab98eSFabio Estevam #include <linux/pinctrl/consumer.h> 64468ba54bSArnd Bergmann #include <linux/gpio/consumer.h> 65c259c132SFrank Li #include <linux/prefetch.h> 66da722186SMartin Fuzzey #include <linux/mfd/syscon.h> 67da722186SMartin Fuzzey #include <linux/regmap.h> 6829380905SLucas Stach #include <soc/imx/cpuidle.h> 6995698ff6SShenwei Wang #include <linux/filter.h> 7095698ff6SShenwei Wang #include <linux/bpf.h> 71793fc096SFrank Li 72793fc096SFrank Li #include <asm/cacheflush.h> 73793fc096SFrank Li 74793fc096SFrank Li #include "fec.h" 75793fc096SFrank Li 76772e42b0SChristoph Müllner static void set_multicast_list(struct net_device *ndev); 77df727d45SRasmus Villemoes static void fec_enet_itr_coal_set(struct net_device *ndev); 78772e42b0SChristoph Müllner 79793fc096SFrank Li #define DRIVER_NAME "fec" 80793fc096SFrank Li 8152c4a1a8SFugang Duan static const u16 fec_enet_vlan_pri_to_queue[8] = {0, 0, 1, 1, 1, 2, 2, 2}; 8252c4a1a8SFugang Duan 83793fc096SFrank Li /* Pause frame feild and FIFO threshold */ 84793fc096SFrank Li #define FEC_ENET_FCE (1 << 5) 85793fc096SFrank Li #define FEC_ENET_RSEM_V 0x84 86793fc096SFrank Li #define FEC_ENET_RSFL_V 16 87793fc096SFrank Li #define FEC_ENET_RAEM_V 0x8 88793fc096SFrank Li #define FEC_ENET_RAFL_V 0x8 89793fc096SFrank Li #define FEC_ENET_OPD_V 0xFFF0 908fff755eSAndrew Lunn #define FEC_MDIO_PM_TIMEOUT 100 /* ms */ 91793fc096SFrank Li 926d6b39f1SShenwei Wang #define FEC_ENET_XDP_PASS 0 936d6b39f1SShenwei Wang #define FEC_ENET_XDP_CONSUMED BIT(0) 946d6b39f1SShenwei Wang #define FEC_ENET_XDP_TX BIT(1) 956d6b39f1SShenwei Wang #define FEC_ENET_XDP_REDIR BIT(2) 966d6b39f1SShenwei Wang 97da722186SMartin Fuzzey struct fec_devinfo { 98da722186SMartin Fuzzey u32 quirks; 99da722186SMartin Fuzzey }; 100da722186SMartin Fuzzey 101da722186SMartin Fuzzey static const struct fec_devinfo fec_imx25_info = { 102da722186SMartin Fuzzey .quirks = FEC_QUIRK_USE_GASKET | FEC_QUIRK_MIB_CLEAR | 103abc33494SGreg Ungerer FEC_QUIRK_HAS_FRREG | FEC_QUIRK_HAS_MDIO_C45, 104da722186SMartin Fuzzey }; 105da722186SMartin Fuzzey 106da722186SMartin Fuzzey static const struct fec_devinfo fec_imx27_info = { 107abc33494SGreg Ungerer .quirks = FEC_QUIRK_MIB_CLEAR | FEC_QUIRK_HAS_FRREG | 108abc33494SGreg Ungerer FEC_QUIRK_HAS_MDIO_C45, 109da722186SMartin Fuzzey }; 110da722186SMartin Fuzzey 111da722186SMartin Fuzzey static const struct fec_devinfo fec_imx28_info = { 112da722186SMartin Fuzzey .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME | 113da722186SMartin Fuzzey FEC_QUIRK_SINGLE_MDIO | FEC_QUIRK_HAS_RACC | 114c730ab42SLaurent Badel FEC_QUIRK_HAS_FRREG | FEC_QUIRK_CLEAR_SETUP_MII | 115abc33494SGreg Ungerer FEC_QUIRK_NO_HARD_RESET | FEC_QUIRK_HAS_MDIO_C45, 116da722186SMartin Fuzzey }; 117da722186SMartin Fuzzey 118da722186SMartin Fuzzey static const struct fec_devinfo fec_imx6q_info = { 119da722186SMartin Fuzzey .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 120da722186SMartin Fuzzey FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 121da722186SMartin Fuzzey FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR006358 | 1227d650df9SWei Fang FEC_QUIRK_HAS_RACC | FEC_QUIRK_CLEAR_SETUP_MII | 123abc33494SGreg Ungerer FEC_QUIRK_HAS_PMQOS | FEC_QUIRK_HAS_MDIO_C45, 124da722186SMartin Fuzzey }; 125da722186SMartin Fuzzey 126da722186SMartin Fuzzey static const struct fec_devinfo fec_mvf600_info = { 127abc33494SGreg Ungerer .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_RACC | 128abc33494SGreg Ungerer FEC_QUIRK_HAS_MDIO_C45, 129da722186SMartin Fuzzey }; 130da722186SMartin Fuzzey 131da722186SMartin Fuzzey static const struct fec_devinfo fec_imx6x_info = { 132da722186SMartin Fuzzey .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 133da722186SMartin Fuzzey FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 134da722186SMartin Fuzzey FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | 135da722186SMartin Fuzzey FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | 1361e6114f5SGreg Ungerer FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE | 137abc33494SGreg Ungerer FEC_QUIRK_CLEAR_SETUP_MII | FEC_QUIRK_HAS_MULTI_QUEUES | 138abc33494SGreg Ungerer FEC_QUIRK_HAS_MDIO_C45, 139da722186SMartin Fuzzey }; 140da722186SMartin Fuzzey 141da722186SMartin Fuzzey static const struct fec_devinfo fec_imx6ul_info = { 142da722186SMartin Fuzzey .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 143da722186SMartin Fuzzey FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 144da722186SMartin Fuzzey FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR007885 | 145da722186SMartin Fuzzey FEC_QUIRK_BUG_CAPTURE | FEC_QUIRK_HAS_RACC | 146abc33494SGreg Ungerer FEC_QUIRK_HAS_COALESCE | FEC_QUIRK_CLEAR_SETUP_MII | 147abc33494SGreg Ungerer FEC_QUIRK_HAS_MDIO_C45, 148da722186SMartin Fuzzey }; 149da722186SMartin Fuzzey 150947240ebSFugang Duan static const struct fec_devinfo fec_imx8mq_info = { 151947240ebSFugang Duan .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 152947240ebSFugang Duan FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 153947240ebSFugang Duan FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | 154947240ebSFugang Duan FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | 155947240ebSFugang Duan FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE | 156947240ebSFugang Duan FEC_QUIRK_CLEAR_SETUP_MII | FEC_QUIRK_HAS_MULTI_QUEUES | 157abc33494SGreg Ungerer FEC_QUIRK_HAS_EEE | FEC_QUIRK_WAKEUP_FROM_INT2 | 158abc33494SGreg Ungerer FEC_QUIRK_HAS_MDIO_C45, 159947240ebSFugang Duan }; 160947240ebSFugang Duan 161947240ebSFugang Duan static const struct fec_devinfo fec_imx8qm_info = { 162947240ebSFugang Duan .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 163947240ebSFugang Duan FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 164947240ebSFugang Duan FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | 165947240ebSFugang Duan FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | 166947240ebSFugang Duan FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE | 167947240ebSFugang Duan FEC_QUIRK_CLEAR_SETUP_MII | FEC_QUIRK_HAS_MULTI_QUEUES | 168abc33494SGreg Ungerer FEC_QUIRK_DELAYED_CLKS_SUPPORT | FEC_QUIRK_HAS_MDIO_C45, 169947240ebSFugang Duan }; 170947240ebSFugang Duan 171167d5fe0SWei Fang static const struct fec_devinfo fec_s32v234_info = { 172167d5fe0SWei Fang .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 173167d5fe0SWei Fang FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 174167d5fe0SWei Fang FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | 175abc33494SGreg Ungerer FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | 176abc33494SGreg Ungerer FEC_QUIRK_HAS_MDIO_C45, 177167d5fe0SWei Fang }; 178167d5fe0SWei Fang 179793fc096SFrank Li static struct platform_device_id fec_devtype[] = { 180793fc096SFrank Li { 181793fc096SFrank Li /* keep it for coldfire */ 182793fc096SFrank Li .name = DRIVER_NAME, 183793fc096SFrank Li .driver_data = 0, 184793fc096SFrank Li }, { 185793fc096SFrank Li .name = "imx25-fec", 186da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_imx25_info, 187793fc096SFrank Li }, { 188793fc096SFrank Li .name = "imx27-fec", 189da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_imx27_info, 190793fc096SFrank Li }, { 191793fc096SFrank Li .name = "imx28-fec", 192da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_imx28_info, 193793fc096SFrank Li }, { 194793fc096SFrank Li .name = "imx6q-fec", 195da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_imx6q_info, 196793fc096SFrank Li }, { 19736803542SShawn Guo .name = "mvf600-fec", 198da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_mvf600_info, 199ca7c4a45SJingchang Lu }, { 20095a77470SFugang Duan .name = "imx6sx-fec", 201da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_imx6x_info, 20295a77470SFugang Duan }, { 203a51d3ab5SFugang Duan .name = "imx6ul-fec", 204da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_imx6ul_info, 205a51d3ab5SFugang Duan }, { 206947240ebSFugang Duan .name = "imx8mq-fec", 207947240ebSFugang Duan .driver_data = (kernel_ulong_t)&fec_imx8mq_info, 208947240ebSFugang Duan }, { 209947240ebSFugang Duan .name = "imx8qm-fec", 210947240ebSFugang Duan .driver_data = (kernel_ulong_t)&fec_imx8qm_info, 211947240ebSFugang Duan }, { 212167d5fe0SWei Fang .name = "s32v234-fec", 213167d5fe0SWei Fang .driver_data = (kernel_ulong_t)&fec_s32v234_info, 214167d5fe0SWei Fang }, { 215793fc096SFrank Li /* sentinel */ 216793fc096SFrank Li } 217793fc096SFrank Li }; 218793fc096SFrank Li MODULE_DEVICE_TABLE(platform, fec_devtype); 219793fc096SFrank Li 220793fc096SFrank Li enum imx_fec_type { 221793fc096SFrank Li IMX25_FEC = 1, /* runs on i.mx25/50/53 */ 222793fc096SFrank Li IMX27_FEC, /* runs on i.mx27/35/51 */ 223793fc096SFrank Li IMX28_FEC, 224793fc096SFrank Li IMX6Q_FEC, 22536803542SShawn Guo MVF600_FEC, 226ba593e00SFugang Duan IMX6SX_FEC, 227a51d3ab5SFugang Duan IMX6UL_FEC, 228947240ebSFugang Duan IMX8MQ_FEC, 229947240ebSFugang Duan IMX8QM_FEC, 230167d5fe0SWei Fang S32V234_FEC, 231793fc096SFrank Li }; 232793fc096SFrank Li 233793fc096SFrank Li static const struct of_device_id fec_dt_ids[] = { 234793fc096SFrank Li { .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], }, 235793fc096SFrank Li { .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], }, 236793fc096SFrank Li { .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], }, 237793fc096SFrank Li { .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], }, 23836803542SShawn Guo { .compatible = "fsl,mvf600-fec", .data = &fec_devtype[MVF600_FEC], }, 239ba593e00SFugang Duan { .compatible = "fsl,imx6sx-fec", .data = &fec_devtype[IMX6SX_FEC], }, 240a51d3ab5SFugang Duan { .compatible = "fsl,imx6ul-fec", .data = &fec_devtype[IMX6UL_FEC], }, 241947240ebSFugang Duan { .compatible = "fsl,imx8mq-fec", .data = &fec_devtype[IMX8MQ_FEC], }, 242947240ebSFugang Duan { .compatible = "fsl,imx8qm-fec", .data = &fec_devtype[IMX8QM_FEC], }, 243167d5fe0SWei Fang { .compatible = "fsl,s32v234-fec", .data = &fec_devtype[S32V234_FEC], }, 244793fc096SFrank Li { /* sentinel */ } 245793fc096SFrank Li }; 246793fc096SFrank Li MODULE_DEVICE_TABLE(of, fec_dt_ids); 247793fc096SFrank Li 248793fc096SFrank Li static unsigned char macaddr[ETH_ALEN]; 249793fc096SFrank Li module_param_array(macaddr, byte, NULL, 0); 250793fc096SFrank Li MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); 251793fc096SFrank Li 252793fc096SFrank Li #if defined(CONFIG_M5272) 253793fc096SFrank Li /* 254793fc096SFrank Li * Some hardware gets it MAC address out of local flash memory. 255793fc096SFrank Li * if this is non-zero then assume it is the address to get MAC from. 256793fc096SFrank Li */ 257793fc096SFrank Li #if defined(CONFIG_NETtel) 258793fc096SFrank Li #define FEC_FLASHMAC 0xf0006006 259793fc096SFrank Li #elif defined(CONFIG_GILBARCONAP) || defined(CONFIG_SCALES) 260793fc096SFrank Li #define FEC_FLASHMAC 0xf0006000 261793fc096SFrank Li #elif defined(CONFIG_CANCam) 262793fc096SFrank Li #define FEC_FLASHMAC 0xf0020000 263793fc096SFrank Li #elif defined (CONFIG_M5272C3) 264793fc096SFrank Li #define FEC_FLASHMAC (0xffe04000 + 4) 265793fc096SFrank Li #elif defined(CONFIG_MOD5272) 266793fc096SFrank Li #define FEC_FLASHMAC 0xffc0406b 267793fc096SFrank Li #else 268793fc096SFrank Li #define FEC_FLASHMAC 0 269793fc096SFrank Li #endif 270793fc096SFrank Li #endif /* CONFIG_M5272 */ 271793fc096SFrank Li 272cdffcf1bSJim Baxter /* The FEC stores dest/src/type/vlan, data, and checksum for receive packets. 273fbbeefddSAndrew Lunn * 274fbbeefddSAndrew Lunn * 2048 byte skbufs are allocated. However, alignment requirements 275fbbeefddSAndrew Lunn * varies between FEC variants. Worst case is 64, so round down by 64. 276793fc096SFrank Li */ 277fbbeefddSAndrew Lunn #define PKT_MAXBUF_SIZE (round_down(2048 - 64, 64)) 278793fc096SFrank Li #define PKT_MINBUF_SIZE 64 279793fc096SFrank Li 2804c09eed9SJim Baxter /* FEC receive acceleration */ 2814c09eed9SJim Baxter #define FEC_RACC_IPDIS (1 << 1) 2824c09eed9SJim Baxter #define FEC_RACC_PRODIS (1 << 2) 2833ac72b7bSEric Nelson #define FEC_RACC_SHIFT16 BIT(7) 2844c09eed9SJim Baxter #define FEC_RACC_OPTIONS (FEC_RACC_IPDIS | FEC_RACC_PRODIS) 2854c09eed9SJim Baxter 2862b30842bSAndrew Lunn /* MIB Control Register */ 2872b30842bSAndrew Lunn #define FEC_MIB_CTRLSTAT_DISABLE BIT(31) 2882b30842bSAndrew Lunn 289793fc096SFrank Li /* 290793fc096SFrank Li * The 5270/5271/5280/5282/532x RX control register also contains maximum frame 291793fc096SFrank Li * size bits. Other FEC hardware does not, so we need to take that into 292793fc096SFrank Li * account when setting it. 293793fc096SFrank Li */ 294793fc096SFrank Li #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ 2953f1dcc6aSLucas Stach defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ 2963f1dcc6aSLucas Stach defined(CONFIG_ARM64) 297793fc096SFrank Li #define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16) 298793fc096SFrank Li #else 299793fc096SFrank Li #define OPT_FRAME_SIZE 0 300793fc096SFrank Li #endif 301793fc096SFrank Li 302793fc096SFrank Li /* FEC MII MMFR bits definition */ 303793fc096SFrank Li #define FEC_MMFR_ST (1 << 30) 304d3ee8ec7SMarco Hartmann #define FEC_MMFR_ST_C45 (0) 305793fc096SFrank Li #define FEC_MMFR_OP_READ (2 << 28) 306d3ee8ec7SMarco Hartmann #define FEC_MMFR_OP_READ_C45 (3 << 28) 307793fc096SFrank Li #define FEC_MMFR_OP_WRITE (1 << 28) 308d3ee8ec7SMarco Hartmann #define FEC_MMFR_OP_ADDR_WRITE (0) 309793fc096SFrank Li #define FEC_MMFR_PA(v) ((v & 0x1f) << 23) 310793fc096SFrank Li #define FEC_MMFR_RA(v) ((v & 0x1f) << 18) 311793fc096SFrank Li #define FEC_MMFR_TA (2 << 16) 312793fc096SFrank Li #define FEC_MMFR_DATA(v) (v & 0xffff) 313de40ed31SNimrod Andy /* FEC ECR bits definition */ 3147b15515fSFrancesco Dolcini #define FEC_ECR_MAGICEN (1 << 2) 3157b15515fSFrancesco Dolcini #define FEC_ECR_SLEEP (1 << 3) 316793fc096SFrank Li 317793fc096SFrank Li #define FEC_MII_TIMEOUT 30000 /* us */ 318793fc096SFrank Li 319793fc096SFrank Li /* Transmitter timeout */ 320793fc096SFrank Li #define TX_TIMEOUT (2 * HZ) 321793fc096SFrank Li 322793fc096SFrank Li #define FEC_PAUSE_FLAG_AUTONEG 0x1 323793fc096SFrank Li #define FEC_PAUSE_FLAG_ENABLE 0x2 324de40ed31SNimrod Andy #define FEC_WOL_HAS_MAGIC_PACKET (0x1 << 0) 325de40ed31SNimrod Andy #define FEC_WOL_FLAG_ENABLE (0x1 << 1) 326de40ed31SNimrod Andy #define FEC_WOL_FLAG_SLEEP_ON (0x1 << 2) 327793fc096SFrank Li 3281b7bde6dSNimrod Andy #define COPYBREAK_DEFAULT 256 3291b7bde6dSNimrod Andy 33079f33912SNimrod Andy /* Max number of allowed TCP segments for software TSO */ 33179f33912SNimrod Andy #define FEC_MAX_TSO_SEGS 100 33279f33912SNimrod Andy #define FEC_MAX_SKB_DESCS (FEC_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS) 33379f33912SNimrod Andy 33479f33912SNimrod Andy #define IS_TSO_HEADER(txq, addr) \ 33579f33912SNimrod Andy ((addr >= txq->tso_hdrs_dma) && \ 3367355f276STroy Kisky (addr < txq->tso_hdrs_dma + txq->bd.ring_size * TSO_HEADER_SIZE)) 33779f33912SNimrod Andy 338793fc096SFrank Li static int mii_cnt; 339793fc096SFrank Li 3407355f276STroy Kisky static struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, 3417355f276STroy Kisky struct bufdesc_prop *bd) 342793fc096SFrank Li { 3437355f276STroy Kisky return (bdp >= bd->last) ? bd->base 344145d6e29SFugang Duan : (struct bufdesc *)(((void *)bdp) + bd->dsize); 345793fc096SFrank Li } 346793fc096SFrank Li 3477355f276STroy Kisky static struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, 3487355f276STroy Kisky struct bufdesc_prop *bd) 34936e24e2eSDuan Fugang-B38611 { 3507355f276STroy Kisky return (bdp <= bd->base) ? bd->last 351145d6e29SFugang Duan : (struct bufdesc *)(((void *)bdp) - bd->dsize); 35236e24e2eSDuan Fugang-B38611 } 35336e24e2eSDuan Fugang-B38611 3547355f276STroy Kisky static int fec_enet_get_bd_index(struct bufdesc *bdp, 3557355f276STroy Kisky struct bufdesc_prop *bd) 35661a4427bSNimrod Andy { 3577355f276STroy Kisky return ((const char *)bdp - (const char *)bd->base) >> bd->dsize_log2; 35861a4427bSNimrod Andy } 35961a4427bSNimrod Andy 3607355f276STroy Kisky static int fec_enet_get_free_txdesc_num(struct fec_enet_priv_tx_q *txq) 3616e909283SNimrod Andy { 3626e909283SNimrod Andy int entries; 3636e909283SNimrod Andy 3647355f276STroy Kisky entries = (((const char *)txq->dirty_tx - 3657355f276STroy Kisky (const char *)txq->bd.cur) >> txq->bd.dsize_log2) - 1; 3666e909283SNimrod Andy 3677355f276STroy Kisky return entries >= 0 ? entries : entries + txq->bd.ring_size; 3686e909283SNimrod Andy } 3696e909283SNimrod Andy 370c20e599bSLothar Waßmann static void swap_buffer(void *bufaddr, int len) 371793fc096SFrank Li { 372793fc096SFrank Li int i; 373793fc096SFrank Li unsigned int *buf = bufaddr; 374793fc096SFrank Li 3757b487d07SLothar Waßmann for (i = 0; i < len; i += 4, buf++) 376e453789aSLothar Waßmann swab32s(buf); 377793fc096SFrank Li } 378793fc096SFrank Li 379344756f6SRussell King static void fec_dump(struct net_device *ndev) 380344756f6SRussell King { 381344756f6SRussell King struct fec_enet_private *fep = netdev_priv(ndev); 3824d494cdcSFugang Duan struct bufdesc *bdp; 3834d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 3844d494cdcSFugang Duan int index = 0; 385344756f6SRussell King 386344756f6SRussell King netdev_info(ndev, "TX ring dump\n"); 387344756f6SRussell King pr_info("Nr SC addr len SKB\n"); 388344756f6SRussell King 3894d494cdcSFugang Duan txq = fep->tx_queue[0]; 3907355f276STroy Kisky bdp = txq->bd.base; 3914d494cdcSFugang Duan 392344756f6SRussell King do { 3935cfa3039SJohannes Berg pr_info("%3u %c%c 0x%04x 0x%08x %4u %p\n", 394344756f6SRussell King index, 3957355f276STroy Kisky bdp == txq->bd.cur ? 'S' : ' ', 3964d494cdcSFugang Duan bdp == txq->dirty_tx ? 'H' : ' ', 3975cfa3039SJohannes Berg fec16_to_cpu(bdp->cbd_sc), 3985cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 3995cfa3039SJohannes Berg fec16_to_cpu(bdp->cbd_datlen), 4004d494cdcSFugang Duan txq->tx_skbuff[index]); 4017355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 402344756f6SRussell King index++; 4037355f276STroy Kisky } while (bdp != txq->bd.base); 404344756f6SRussell King } 405344756f6SRussell King 40662a02c98SFugang Duan static inline bool is_ipv4_pkt(struct sk_buff *skb) 40762a02c98SFugang Duan { 40862a02c98SFugang Duan return skb->protocol == htons(ETH_P_IP) && ip_hdr(skb)->version == 4; 40962a02c98SFugang Duan } 41062a02c98SFugang Duan 4114c09eed9SJim Baxter static int 4124c09eed9SJim Baxter fec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev) 4134c09eed9SJim Baxter { 4144c09eed9SJim Baxter /* Only run for packets requiring a checksum. */ 4154c09eed9SJim Baxter if (skb->ip_summed != CHECKSUM_PARTIAL) 4164c09eed9SJim Baxter return 0; 4174c09eed9SJim Baxter 4184c09eed9SJim Baxter if (unlikely(skb_cow_head(skb, 0))) 4194c09eed9SJim Baxter return -1; 4204c09eed9SJim Baxter 42162a02c98SFugang Duan if (is_ipv4_pkt(skb)) 42296c50caaSNimrod Andy ip_hdr(skb)->check = 0; 4234c09eed9SJim Baxter *(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) = 0; 4244c09eed9SJim Baxter 4254c09eed9SJim Baxter return 0; 4264c09eed9SJim Baxter } 4274c09eed9SJim Baxter 42895698ff6SShenwei Wang static int 42995698ff6SShenwei Wang fec_enet_create_page_pool(struct fec_enet_private *fep, 43095698ff6SShenwei Wang struct fec_enet_priv_rx_q *rxq, int size) 43195698ff6SShenwei Wang { 4326d6b39f1SShenwei Wang struct bpf_prog *xdp_prog = READ_ONCE(fep->xdp_prog); 43395698ff6SShenwei Wang struct page_pool_params pp_params = { 43495698ff6SShenwei Wang .order = 0, 43595698ff6SShenwei Wang .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV, 43695698ff6SShenwei Wang .pool_size = size, 43795698ff6SShenwei Wang .nid = dev_to_node(&fep->pdev->dev), 43895698ff6SShenwei Wang .dev = &fep->pdev->dev, 4396d6b39f1SShenwei Wang .dma_dir = xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE, 44095698ff6SShenwei Wang .offset = FEC_ENET_XDP_HEADROOM, 44195698ff6SShenwei Wang .max_len = FEC_ENET_RX_FRSIZE, 44295698ff6SShenwei Wang }; 44395698ff6SShenwei Wang int err; 44495698ff6SShenwei Wang 44595698ff6SShenwei Wang rxq->page_pool = page_pool_create(&pp_params); 44695698ff6SShenwei Wang if (IS_ERR(rxq->page_pool)) { 44795698ff6SShenwei Wang err = PTR_ERR(rxq->page_pool); 44895698ff6SShenwei Wang rxq->page_pool = NULL; 44995698ff6SShenwei Wang return err; 45095698ff6SShenwei Wang } 45195698ff6SShenwei Wang 45295698ff6SShenwei Wang err = xdp_rxq_info_reg(&rxq->xdp_rxq, fep->netdev, rxq->id, 0); 45395698ff6SShenwei Wang if (err < 0) 45495698ff6SShenwei Wang goto err_free_pp; 45595698ff6SShenwei Wang 45695698ff6SShenwei Wang err = xdp_rxq_info_reg_mem_model(&rxq->xdp_rxq, MEM_TYPE_PAGE_POOL, 45795698ff6SShenwei Wang rxq->page_pool); 45895698ff6SShenwei Wang if (err) 45995698ff6SShenwei Wang goto err_unregister_rxq; 46095698ff6SShenwei Wang 46195698ff6SShenwei Wang return 0; 46295698ff6SShenwei Wang 46395698ff6SShenwei Wang err_unregister_rxq: 46495698ff6SShenwei Wang xdp_rxq_info_unreg(&rxq->xdp_rxq); 46595698ff6SShenwei Wang err_free_pp: 46695698ff6SShenwei Wang page_pool_destroy(rxq->page_pool); 46795698ff6SShenwei Wang rxq->page_pool = NULL; 46895698ff6SShenwei Wang return err; 46995698ff6SShenwei Wang } 47095698ff6SShenwei Wang 471c4bc44c6SKevin Hao static struct bufdesc * 4724d494cdcSFugang Duan fec_enet_txq_submit_frag_skb(struct fec_enet_priv_tx_q *txq, 4734d494cdcSFugang Duan struct sk_buff *skb, 4744d494cdcSFugang Duan struct net_device *ndev) 4756e909283SNimrod Andy { 4766e909283SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 4777355f276STroy Kisky struct bufdesc *bdp = txq->bd.cur; 4786e909283SNimrod Andy struct bufdesc_ex *ebdp; 4796e909283SNimrod Andy int nr_frags = skb_shinfo(skb)->nr_frags; 4806e909283SNimrod Andy int frag, frag_len; 4816e909283SNimrod Andy unsigned short status; 4826e909283SNimrod Andy unsigned int estatus = 0; 4836e909283SNimrod Andy skb_frag_t *this_frag; 4846e909283SNimrod Andy unsigned int index; 4856e909283SNimrod Andy void *bufaddr; 486d6bf3143SRussell King dma_addr_t addr; 4876e909283SNimrod Andy int i; 4886e909283SNimrod Andy 4896e909283SNimrod Andy for (frag = 0; frag < nr_frags; frag++) { 4906e909283SNimrod Andy this_frag = &skb_shinfo(skb)->frags[frag]; 4917355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 4926e909283SNimrod Andy ebdp = (struct bufdesc_ex *)bdp; 4936e909283SNimrod Andy 4945cfa3039SJohannes Berg status = fec16_to_cpu(bdp->cbd_sc); 4956e909283SNimrod Andy status &= ~BD_ENET_TX_STATS; 4966e909283SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 497d7840976SMatthew Wilcox (Oracle) frag_len = skb_frag_size(&skb_shinfo(skb)->frags[frag]); 4986e909283SNimrod Andy 4996e909283SNimrod Andy /* Handle the last BD specially */ 5006e909283SNimrod Andy if (frag == nr_frags - 1) { 5016e909283SNimrod Andy status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); 5026e909283SNimrod Andy if (fep->bufdesc_ex) { 5036e909283SNimrod Andy estatus |= BD_ENET_TX_INT; 5046e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & 5056e909283SNimrod Andy SKBTX_HW_TSTAMP && fep->hwts_tx_en)) 5066e909283SNimrod Andy estatus |= BD_ENET_TX_TS; 5076e909283SNimrod Andy } 5086e909283SNimrod Andy } 5096e909283SNimrod Andy 5106e909283SNimrod Andy if (fep->bufdesc_ex) { 5116b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 51253bb20d1STroy Kisky estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 5136e909283SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 5146e909283SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 515471ff445SJoakim Zhang 5166e909283SNimrod Andy ebdp->cbd_bdu = 0; 5175cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(estatus); 5186e909283SNimrod Andy } 5196e909283SNimrod Andy 520d7840976SMatthew Wilcox (Oracle) bufaddr = skb_frag_address(this_frag); 5216e909283SNimrod Andy 5227355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 52341ef84ceSFugang Duan if (((unsigned long) bufaddr) & fep->tx_align || 5246b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 5254d494cdcSFugang Duan memcpy(txq->tx_bounce[index], bufaddr, frag_len); 5264d494cdcSFugang Duan bufaddr = txq->tx_bounce[index]; 5276e909283SNimrod Andy 5286b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 5296e909283SNimrod Andy swap_buffer(bufaddr, frag_len); 5306e909283SNimrod Andy } 5316e909283SNimrod Andy 532d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, bufaddr, frag_len, 533d6bf3143SRussell King DMA_TO_DEVICE); 534d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 5356e909283SNimrod Andy if (net_ratelimit()) 5366e909283SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 5376e909283SNimrod Andy goto dma_mapping_error; 5386e909283SNimrod Andy } 5396e909283SNimrod Andy 5405cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(addr); 5415cfa3039SJohannes Berg bdp->cbd_datlen = cpu_to_fec16(frag_len); 542be293467STroy Kisky /* Make sure the updates to rest of the descriptor are 543be293467STroy Kisky * performed before transferring ownership. 544be293467STroy Kisky */ 545be293467STroy Kisky wmb(); 5465cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(status); 5476e909283SNimrod Andy } 5486e909283SNimrod Andy 549c4bc44c6SKevin Hao return bdp; 5506e909283SNimrod Andy dma_mapping_error: 5517355f276STroy Kisky bdp = txq->bd.cur; 5526e909283SNimrod Andy for (i = 0; i < frag; i++) { 5537355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 5545cfa3039SJohannes Berg dma_unmap_single(&fep->pdev->dev, fec32_to_cpu(bdp->cbd_bufaddr), 5555cfa3039SJohannes Berg fec16_to_cpu(bdp->cbd_datlen), DMA_TO_DEVICE); 5566e909283SNimrod Andy } 557c4bc44c6SKevin Hao return ERR_PTR(-ENOMEM); 5586e909283SNimrod Andy } 5596e909283SNimrod Andy 5604d494cdcSFugang Duan static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq, 5614d494cdcSFugang Duan struct sk_buff *skb, struct net_device *ndev) 5626e909283SNimrod Andy { 5636e909283SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 5646e909283SNimrod Andy int nr_frags = skb_shinfo(skb)->nr_frags; 5656e909283SNimrod Andy struct bufdesc *bdp, *last_bdp; 5666e909283SNimrod Andy void *bufaddr; 567d6bf3143SRussell King dma_addr_t addr; 5686e909283SNimrod Andy unsigned short status; 5696e909283SNimrod Andy unsigned short buflen; 5706e909283SNimrod Andy unsigned int estatus = 0; 5716e909283SNimrod Andy unsigned int index; 57279f33912SNimrod Andy int entries_free; 5736e909283SNimrod Andy 5747355f276STroy Kisky entries_free = fec_enet_get_free_txdesc_num(txq); 57579f33912SNimrod Andy if (entries_free < MAX_SKB_FRAGS + 1) { 57679f33912SNimrod Andy dev_kfree_skb_any(skb); 57779f33912SNimrod Andy if (net_ratelimit()) 57879f33912SNimrod Andy netdev_err(ndev, "NOT enough BD for SG!\n"); 57979f33912SNimrod Andy return NETDEV_TX_OK; 58079f33912SNimrod Andy } 58179f33912SNimrod Andy 5826e909283SNimrod Andy /* Protocol checksum off-load for TCP and UDP. */ 5836e909283SNimrod Andy if (fec_enet_clear_csum(skb, ndev)) { 5846e909283SNimrod Andy dev_kfree_skb_any(skb); 5856e909283SNimrod Andy return NETDEV_TX_OK; 5866e909283SNimrod Andy } 5876e909283SNimrod Andy 5886e909283SNimrod Andy /* Fill in a Tx ring entry */ 5897355f276STroy Kisky bdp = txq->bd.cur; 590c4bc44c6SKevin Hao last_bdp = bdp; 5915cfa3039SJohannes Berg status = fec16_to_cpu(bdp->cbd_sc); 5926e909283SNimrod Andy status &= ~BD_ENET_TX_STATS; 5936e909283SNimrod Andy 5946e909283SNimrod Andy /* Set buffer length and buffer pointer */ 5956e909283SNimrod Andy bufaddr = skb->data; 5966e909283SNimrod Andy buflen = skb_headlen(skb); 5976e909283SNimrod Andy 5987355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 59941ef84ceSFugang Duan if (((unsigned long) bufaddr) & fep->tx_align || 6006b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 6014d494cdcSFugang Duan memcpy(txq->tx_bounce[index], skb->data, buflen); 6024d494cdcSFugang Duan bufaddr = txq->tx_bounce[index]; 6036e909283SNimrod Andy 6046b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 6056e909283SNimrod Andy swap_buffer(bufaddr, buflen); 6066e909283SNimrod Andy } 6076e909283SNimrod Andy 608d6bf3143SRussell King /* Push the data cache so the CPM does not get stale memory data. */ 609d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, bufaddr, buflen, DMA_TO_DEVICE); 610d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 6116e909283SNimrod Andy dev_kfree_skb_any(skb); 6126e909283SNimrod Andy if (net_ratelimit()) 6136e909283SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 6146e909283SNimrod Andy return NETDEV_TX_OK; 6156e909283SNimrod Andy } 6166e909283SNimrod Andy 6176e909283SNimrod Andy if (nr_frags) { 618c4bc44c6SKevin Hao last_bdp = fec_enet_txq_submit_frag_skb(txq, skb, ndev); 619fc75ba51STroy Kisky if (IS_ERR(last_bdp)) { 620fc75ba51STroy Kisky dma_unmap_single(&fep->pdev->dev, addr, 621fc75ba51STroy Kisky buflen, DMA_TO_DEVICE); 622fc75ba51STroy Kisky dev_kfree_skb_any(skb); 623c4bc44c6SKevin Hao return NETDEV_TX_OK; 624fc75ba51STroy Kisky } 6256e909283SNimrod Andy } else { 6266e909283SNimrod Andy status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); 6276e909283SNimrod Andy if (fep->bufdesc_ex) { 6286e909283SNimrod Andy estatus = BD_ENET_TX_INT; 6296e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & 6306e909283SNimrod Andy SKBTX_HW_TSTAMP && fep->hwts_tx_en)) 6316e909283SNimrod Andy estatus |= BD_ENET_TX_TS; 6326e909283SNimrod Andy } 6336e909283SNimrod Andy } 634fc75ba51STroy Kisky bdp->cbd_bufaddr = cpu_to_fec32(addr); 635fc75ba51STroy Kisky bdp->cbd_datlen = cpu_to_fec16(buflen); 6366e909283SNimrod Andy 6376e909283SNimrod Andy if (fep->bufdesc_ex) { 6386e909283SNimrod Andy 6396e909283SNimrod Andy struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 6406e909283SNimrod Andy 6416e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && 6426e909283SNimrod Andy fep->hwts_tx_en)) 6436e909283SNimrod Andy skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; 6446e909283SNimrod Andy 6456b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 64653bb20d1STroy Kisky estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 647befe8213SNimrod Andy 6486e909283SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 6496e909283SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 6506e909283SNimrod Andy 6516e909283SNimrod Andy ebdp->cbd_bdu = 0; 6525cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(estatus); 6536e909283SNimrod Andy } 6546e909283SNimrod Andy 6557355f276STroy Kisky index = fec_enet_get_bd_index(last_bdp, &txq->bd); 6566e909283SNimrod Andy /* Save skb pointer */ 6574d494cdcSFugang Duan txq->tx_skbuff[index] = skb; 6586e909283SNimrod Andy 659be293467STroy Kisky /* Make sure the updates to rest of the descriptor are performed before 660be293467STroy Kisky * transferring ownership. 661be293467STroy Kisky */ 662be293467STroy Kisky wmb(); 6636e909283SNimrod Andy 6646e909283SNimrod Andy /* Send it on its way. Tell FEC it's ready, interrupt when done, 6656e909283SNimrod Andy * it's the last BD of the frame, and to put the CRC on the end. 6666e909283SNimrod Andy */ 6676e909283SNimrod Andy status |= (BD_ENET_TX_READY | BD_ENET_TX_TC); 6685cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(status); 6696e909283SNimrod Andy 670793fc096SFrank Li /* If this was the last BD in the ring, start at the beginning again. */ 6717355f276STroy Kisky bdp = fec_enet_get_nextdesc(last_bdp, &txq->bd); 672793fc096SFrank Li 6737a2a8451SEric Dumazet skb_tx_timestamp(skb); 6747a2a8451SEric Dumazet 675c4bc44c6SKevin Hao /* Make sure the update to bdp and tx_skbuff are performed before 6767355f276STroy Kisky * txq->bd.cur. 677c4bc44c6SKevin Hao */ 678c4bc44c6SKevin Hao wmb(); 6797355f276STroy Kisky txq->bd.cur = bdp; 680793fc096SFrank Li 681793fc096SFrank Li /* Trigger transmission start */ 68253bb20d1STroy Kisky writel(0, txq->bd.reg_desc_active); 683793fc096SFrank Li 6846e909283SNimrod Andy return 0; 685793fc096SFrank Li } 686793fc096SFrank Li 68779f33912SNimrod Andy static int 6884d494cdcSFugang Duan fec_enet_txq_put_data_tso(struct fec_enet_priv_tx_q *txq, struct sk_buff *skb, 6894d494cdcSFugang Duan struct net_device *ndev, 69079f33912SNimrod Andy struct bufdesc *bdp, int index, char *data, 69179f33912SNimrod Andy int size, bool last_tcp, bool is_last) 69279f33912SNimrod Andy { 69379f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 69461cd2ebbSFabian Frederick struct bufdesc_ex *ebdp = container_of(bdp, struct bufdesc_ex, desc); 69579f33912SNimrod Andy unsigned short status; 69679f33912SNimrod Andy unsigned int estatus = 0; 697d6bf3143SRussell King dma_addr_t addr; 69879f33912SNimrod Andy 6995cfa3039SJohannes Berg status = fec16_to_cpu(bdp->cbd_sc); 70079f33912SNimrod Andy status &= ~BD_ENET_TX_STATS; 70179f33912SNimrod Andy 70279f33912SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 70379f33912SNimrod Andy 70441ef84ceSFugang Duan if (((unsigned long) data) & fep->tx_align || 7056b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 7064d494cdcSFugang Duan memcpy(txq->tx_bounce[index], data, size); 7074d494cdcSFugang Duan data = txq->tx_bounce[index]; 70879f33912SNimrod Andy 7096b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 71079f33912SNimrod Andy swap_buffer(data, size); 71179f33912SNimrod Andy } 71279f33912SNimrod Andy 713d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, data, size, DMA_TO_DEVICE); 714d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 71579f33912SNimrod Andy dev_kfree_skb_any(skb); 71679f33912SNimrod Andy if (net_ratelimit()) 71779f33912SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 71806a4df58SZhang Changzhong return NETDEV_TX_OK; 71979f33912SNimrod Andy } 72079f33912SNimrod Andy 7215cfa3039SJohannes Berg bdp->cbd_datlen = cpu_to_fec16(size); 7225cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(addr); 723d6bf3143SRussell King 72479f33912SNimrod Andy if (fep->bufdesc_ex) { 7256b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 72653bb20d1STroy Kisky estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 72779f33912SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 72879f33912SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 72979f33912SNimrod Andy ebdp->cbd_bdu = 0; 7305cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(estatus); 73179f33912SNimrod Andy } 73279f33912SNimrod Andy 73379f33912SNimrod Andy /* Handle the last BD specially */ 73479f33912SNimrod Andy if (last_tcp) 73579f33912SNimrod Andy status |= (BD_ENET_TX_LAST | BD_ENET_TX_TC); 73679f33912SNimrod Andy if (is_last) { 73779f33912SNimrod Andy status |= BD_ENET_TX_INTR; 73879f33912SNimrod Andy if (fep->bufdesc_ex) 7395cfa3039SJohannes Berg ebdp->cbd_esc |= cpu_to_fec32(BD_ENET_TX_INT); 74079f33912SNimrod Andy } 74179f33912SNimrod Andy 7425cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(status); 74379f33912SNimrod Andy 74479f33912SNimrod Andy return 0; 74579f33912SNimrod Andy } 74679f33912SNimrod Andy 74779f33912SNimrod Andy static int 7484d494cdcSFugang Duan fec_enet_txq_put_hdr_tso(struct fec_enet_priv_tx_q *txq, 7494d494cdcSFugang Duan struct sk_buff *skb, struct net_device *ndev, 75079f33912SNimrod Andy struct bufdesc *bdp, int index) 75179f33912SNimrod Andy { 75279f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 753504148feSEric Dumazet int hdr_len = skb_tcp_all_headers(skb); 75461cd2ebbSFabian Frederick struct bufdesc_ex *ebdp = container_of(bdp, struct bufdesc_ex, desc); 75579f33912SNimrod Andy void *bufaddr; 75679f33912SNimrod Andy unsigned long dmabuf; 75779f33912SNimrod Andy unsigned short status; 75879f33912SNimrod Andy unsigned int estatus = 0; 75979f33912SNimrod Andy 7605cfa3039SJohannes Berg status = fec16_to_cpu(bdp->cbd_sc); 76179f33912SNimrod Andy status &= ~BD_ENET_TX_STATS; 76279f33912SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 76379f33912SNimrod Andy 7644d494cdcSFugang Duan bufaddr = txq->tso_hdrs + index * TSO_HEADER_SIZE; 7654d494cdcSFugang Duan dmabuf = txq->tso_hdrs_dma + index * TSO_HEADER_SIZE; 76641ef84ceSFugang Duan if (((unsigned long)bufaddr) & fep->tx_align || 7676b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 7684d494cdcSFugang Duan memcpy(txq->tx_bounce[index], skb->data, hdr_len); 7694d494cdcSFugang Duan bufaddr = txq->tx_bounce[index]; 77079f33912SNimrod Andy 7716b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 77279f33912SNimrod Andy swap_buffer(bufaddr, hdr_len); 77379f33912SNimrod Andy 77479f33912SNimrod Andy dmabuf = dma_map_single(&fep->pdev->dev, bufaddr, 77579f33912SNimrod Andy hdr_len, DMA_TO_DEVICE); 77679f33912SNimrod Andy if (dma_mapping_error(&fep->pdev->dev, dmabuf)) { 77779f33912SNimrod Andy dev_kfree_skb_any(skb); 77879f33912SNimrod Andy if (net_ratelimit()) 77979f33912SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 78006a4df58SZhang Changzhong return NETDEV_TX_OK; 78179f33912SNimrod Andy } 78279f33912SNimrod Andy } 78379f33912SNimrod Andy 7845cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(dmabuf); 7855cfa3039SJohannes Berg bdp->cbd_datlen = cpu_to_fec16(hdr_len); 78679f33912SNimrod Andy 78779f33912SNimrod Andy if (fep->bufdesc_ex) { 7886b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 78953bb20d1STroy Kisky estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 79079f33912SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 79179f33912SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 79279f33912SNimrod Andy ebdp->cbd_bdu = 0; 7935cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(estatus); 79479f33912SNimrod Andy } 79579f33912SNimrod Andy 7965cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(status); 79779f33912SNimrod Andy 79879f33912SNimrod Andy return 0; 79979f33912SNimrod Andy } 80079f33912SNimrod Andy 8014d494cdcSFugang Duan static int fec_enet_txq_submit_tso(struct fec_enet_priv_tx_q *txq, 8024d494cdcSFugang Duan struct sk_buff *skb, 8034d494cdcSFugang Duan struct net_device *ndev) 80479f33912SNimrod Andy { 80579f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 806761b331cSEric Dumazet int hdr_len, total_len, data_left; 8077355f276STroy Kisky struct bufdesc *bdp = txq->bd.cur; 80879f33912SNimrod Andy struct tso_t tso; 80979f33912SNimrod Andy unsigned int index = 0; 81079f33912SNimrod Andy int ret; 81179f33912SNimrod Andy 8127355f276STroy Kisky if (tso_count_descs(skb) >= fec_enet_get_free_txdesc_num(txq)) { 81379f33912SNimrod Andy dev_kfree_skb_any(skb); 81479f33912SNimrod Andy if (net_ratelimit()) 81579f33912SNimrod Andy netdev_err(ndev, "NOT enough BD for TSO!\n"); 81679f33912SNimrod Andy return NETDEV_TX_OK; 81779f33912SNimrod Andy } 81879f33912SNimrod Andy 81979f33912SNimrod Andy /* Protocol checksum off-load for TCP and UDP. */ 82079f33912SNimrod Andy if (fec_enet_clear_csum(skb, ndev)) { 82179f33912SNimrod Andy dev_kfree_skb_any(skb); 82279f33912SNimrod Andy return NETDEV_TX_OK; 82379f33912SNimrod Andy } 82479f33912SNimrod Andy 82579f33912SNimrod Andy /* Initialize the TSO handler, and prepare the first payload */ 826761b331cSEric Dumazet hdr_len = tso_start(skb, &tso); 82779f33912SNimrod Andy 82879f33912SNimrod Andy total_len = skb->len - hdr_len; 82979f33912SNimrod Andy while (total_len > 0) { 83079f33912SNimrod Andy char *hdr; 83179f33912SNimrod Andy 8327355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 83379f33912SNimrod Andy data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len); 83479f33912SNimrod Andy total_len -= data_left; 83579f33912SNimrod Andy 83679f33912SNimrod Andy /* prepare packet headers: MAC + IP + TCP */ 8374d494cdcSFugang Duan hdr = txq->tso_hdrs + index * TSO_HEADER_SIZE; 83879f33912SNimrod Andy tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0); 8394d494cdcSFugang Duan ret = fec_enet_txq_put_hdr_tso(txq, skb, ndev, bdp, index); 84079f33912SNimrod Andy if (ret) 84179f33912SNimrod Andy goto err_release; 84279f33912SNimrod Andy 84379f33912SNimrod Andy while (data_left > 0) { 84479f33912SNimrod Andy int size; 84579f33912SNimrod Andy 84679f33912SNimrod Andy size = min_t(int, tso.size, data_left); 8477355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 8487355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 8494d494cdcSFugang Duan ret = fec_enet_txq_put_data_tso(txq, skb, ndev, 8504d494cdcSFugang Duan bdp, index, 8514d494cdcSFugang Duan tso.data, size, 8524d494cdcSFugang Duan size == data_left, 85379f33912SNimrod Andy total_len == 0); 85479f33912SNimrod Andy if (ret) 85579f33912SNimrod Andy goto err_release; 85679f33912SNimrod Andy 85779f33912SNimrod Andy data_left -= size; 85879f33912SNimrod Andy tso_build_data(skb, &tso, size); 85979f33912SNimrod Andy } 86079f33912SNimrod Andy 8617355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 86279f33912SNimrod Andy } 86379f33912SNimrod Andy 86479f33912SNimrod Andy /* Save skb pointer */ 8654d494cdcSFugang Duan txq->tx_skbuff[index] = skb; 86679f33912SNimrod Andy 86779f33912SNimrod Andy skb_tx_timestamp(skb); 8687355f276STroy Kisky txq->bd.cur = bdp; 86979f33912SNimrod Andy 87079f33912SNimrod Andy /* Trigger transmission start */ 8716b7e4008SLothar Waßmann if (!(fep->quirks & FEC_QUIRK_ERR007885) || 87253bb20d1STroy Kisky !readl(txq->bd.reg_desc_active) || 87353bb20d1STroy Kisky !readl(txq->bd.reg_desc_active) || 87453bb20d1STroy Kisky !readl(txq->bd.reg_desc_active) || 87553bb20d1STroy Kisky !readl(txq->bd.reg_desc_active)) 87653bb20d1STroy Kisky writel(0, txq->bd.reg_desc_active); 87779f33912SNimrod Andy 87879f33912SNimrod Andy return 0; 87979f33912SNimrod Andy 88079f33912SNimrod Andy err_release: 88179f33912SNimrod Andy /* TODO: Release all used data descriptors for TSO */ 88279f33912SNimrod Andy return ret; 88379f33912SNimrod Andy } 88479f33912SNimrod Andy 88561a4427bSNimrod Andy static netdev_tx_t 88661a4427bSNimrod Andy fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) 88761a4427bSNimrod Andy { 88861a4427bSNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 8896e909283SNimrod Andy int entries_free; 8904d494cdcSFugang Duan unsigned short queue; 8914d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 8924d494cdcSFugang Duan struct netdev_queue *nq; 89361a4427bSNimrod Andy int ret; 89461a4427bSNimrod Andy 8954d494cdcSFugang Duan queue = skb_get_queue_mapping(skb); 8964d494cdcSFugang Duan txq = fep->tx_queue[queue]; 8974d494cdcSFugang Duan nq = netdev_get_tx_queue(ndev, queue); 8984d494cdcSFugang Duan 89979f33912SNimrod Andy if (skb_is_gso(skb)) 9004d494cdcSFugang Duan ret = fec_enet_txq_submit_tso(txq, skb, ndev); 90179f33912SNimrod Andy else 9024d494cdcSFugang Duan ret = fec_enet_txq_submit_skb(txq, skb, ndev); 9036e909283SNimrod Andy if (ret) 9046e909283SNimrod Andy return ret; 90561a4427bSNimrod Andy 9067355f276STroy Kisky entries_free = fec_enet_get_free_txdesc_num(txq); 9074d494cdcSFugang Duan if (entries_free <= txq->tx_stop_threshold) 9084d494cdcSFugang Duan netif_tx_stop_queue(nq); 90961a4427bSNimrod Andy 91061a4427bSNimrod Andy return NETDEV_TX_OK; 91161a4427bSNimrod Andy } 91261a4427bSNimrod Andy 913a210576cSDavid S. Miller /* Init RX & TX buffer descriptors 914a210576cSDavid S. Miller */ 915a210576cSDavid S. Miller static void fec_enet_bd_init(struct net_device *dev) 916a210576cSDavid S. Miller { 917a210576cSDavid S. Miller struct fec_enet_private *fep = netdev_priv(dev); 9184d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 9194d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 920a210576cSDavid S. Miller struct bufdesc *bdp; 921a210576cSDavid S. Miller unsigned int i; 92259d0f746SFrank Li unsigned int q; 923a210576cSDavid S. Miller 92459d0f746SFrank Li for (q = 0; q < fep->num_rx_queues; q++) { 925a210576cSDavid S. Miller /* Initialize the receive buffer descriptors. */ 92659d0f746SFrank Li rxq = fep->rx_queue[q]; 9277355f276STroy Kisky bdp = rxq->bd.base; 9284d494cdcSFugang Duan 9297355f276STroy Kisky for (i = 0; i < rxq->bd.ring_size; i++) { 930a210576cSDavid S. Miller 931a210576cSDavid S. Miller /* Initialize the BD for every fragment in the page. */ 932a210576cSDavid S. Miller if (bdp->cbd_bufaddr) 9335cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); 934a210576cSDavid S. Miller else 9355cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(0); 9367355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 937a210576cSDavid S. Miller } 938a210576cSDavid S. Miller 939a210576cSDavid S. Miller /* Set the last buffer to wrap */ 9407355f276STroy Kisky bdp = fec_enet_get_prevdesc(bdp, &rxq->bd); 9415cfa3039SJohannes Berg bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 942a210576cSDavid S. Miller 9437355f276STroy Kisky rxq->bd.cur = rxq->bd.base; 94459d0f746SFrank Li } 945a210576cSDavid S. Miller 94659d0f746SFrank Li for (q = 0; q < fep->num_tx_queues; q++) { 947a210576cSDavid S. Miller /* ...and the same for transmit */ 94859d0f746SFrank Li txq = fep->tx_queue[q]; 9497355f276STroy Kisky bdp = txq->bd.base; 9507355f276STroy Kisky txq->bd.cur = bdp; 951a210576cSDavid S. Miller 9527355f276STroy Kisky for (i = 0; i < txq->bd.ring_size; i++) { 953a210576cSDavid S. Miller /* Initialize the BD for every fragment in the page. */ 9545cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(0); 955178e5f57SFugang Duan if (bdp->cbd_bufaddr && 956178e5f57SFugang Duan !IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr))) 957178e5f57SFugang Duan dma_unmap_single(&fep->pdev->dev, 958178e5f57SFugang Duan fec32_to_cpu(bdp->cbd_bufaddr), 959178e5f57SFugang Duan fec16_to_cpu(bdp->cbd_datlen), 960178e5f57SFugang Duan DMA_TO_DEVICE); 9614d494cdcSFugang Duan if (txq->tx_skbuff[i]) { 9624d494cdcSFugang Duan dev_kfree_skb_any(txq->tx_skbuff[i]); 9634d494cdcSFugang Duan txq->tx_skbuff[i] = NULL; 964a210576cSDavid S. Miller } 9655cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(0); 9667355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 967a210576cSDavid S. Miller } 968a210576cSDavid S. Miller 969a210576cSDavid S. Miller /* Set the last buffer to wrap */ 9707355f276STroy Kisky bdp = fec_enet_get_prevdesc(bdp, &txq->bd); 9715cfa3039SJohannes Berg bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 9724d494cdcSFugang Duan txq->dirty_tx = bdp; 973a210576cSDavid S. Miller } 97459d0f746SFrank Li } 97559d0f746SFrank Li 976ce99d0d3SFrank Li static void fec_enet_active_rxring(struct net_device *ndev) 977ce99d0d3SFrank Li { 978ce99d0d3SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 979ce99d0d3SFrank Li int i; 980ce99d0d3SFrank Li 981ce99d0d3SFrank Li for (i = 0; i < fep->num_rx_queues; i++) 98253bb20d1STroy Kisky writel(0, fep->rx_queue[i]->bd.reg_desc_active); 983ce99d0d3SFrank Li } 984ce99d0d3SFrank Li 98559d0f746SFrank Li static void fec_enet_enable_ring(struct net_device *ndev) 98659d0f746SFrank Li { 98759d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 98859d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 98959d0f746SFrank Li struct fec_enet_priv_rx_q *rxq; 99059d0f746SFrank Li int i; 99159d0f746SFrank Li 99259d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) { 99359d0f746SFrank Li rxq = fep->rx_queue[i]; 9947355f276STroy Kisky writel(rxq->bd.dma, fep->hwp + FEC_R_DES_START(i)); 995fbbeefddSAndrew Lunn writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_R_BUFF_SIZE(i)); 99659d0f746SFrank Li 99759d0f746SFrank Li /* enable DMA1/2 */ 99859d0f746SFrank Li if (i) 99959d0f746SFrank Li writel(RCMR_MATCHEN | RCMR_CMP(i), 100059d0f746SFrank Li fep->hwp + FEC_RCMR(i)); 100159d0f746SFrank Li } 100259d0f746SFrank Li 100359d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 100459d0f746SFrank Li txq = fep->tx_queue[i]; 10057355f276STroy Kisky writel(txq->bd.dma, fep->hwp + FEC_X_DES_START(i)); 100659d0f746SFrank Li 100759d0f746SFrank Li /* enable DMA1/2 */ 100859d0f746SFrank Li if (i) 100959d0f746SFrank Li writel(DMA_CLASS_EN | IDLE_SLOPE(i), 101059d0f746SFrank Li fep->hwp + FEC_DMA_CFG(i)); 101159d0f746SFrank Li } 101259d0f746SFrank Li } 101359d0f746SFrank Li 101459d0f746SFrank Li static void fec_enet_reset_skb(struct net_device *ndev) 101559d0f746SFrank Li { 101659d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 101759d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 101859d0f746SFrank Li int i, j; 101959d0f746SFrank Li 102059d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 102159d0f746SFrank Li txq = fep->tx_queue[i]; 102259d0f746SFrank Li 10237355f276STroy Kisky for (j = 0; j < txq->bd.ring_size; j++) { 102459d0f746SFrank Li if (txq->tx_skbuff[j]) { 102559d0f746SFrank Li dev_kfree_skb_any(txq->tx_skbuff[j]); 102659d0f746SFrank Li txq->tx_skbuff[j] = NULL; 102759d0f746SFrank Li } 102859d0f746SFrank Li } 102959d0f746SFrank Li } 103059d0f746SFrank Li } 1031a210576cSDavid S. Miller 1032dbc64a8eSRussell King /* 1033dbc64a8eSRussell King * This function is called to start or restart the FEC during a link 1034dbc64a8eSRussell King * change, transmit timeout, or to reconfigure the FEC. The network 1035dbc64a8eSRussell King * packet processing for this device must be stopped before this call. 1036793fc096SFrank Li */ 1037793fc096SFrank Li static void 1038ef83337dSRussell King fec_restart(struct net_device *ndev) 1039793fc096SFrank Li { 1040793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1041793fc096SFrank Li u32 temp_mac[2]; 1042793fc096SFrank Li u32 rcntl = OPT_FRAME_SIZE | 0x04; 1043793fc096SFrank Li u32 ecntl = 0x2; /* ETHEREN */ 1044793fc096SFrank Li 1045106c314cSFugang Duan /* Whack a reset. We should wait for this. 1046106c314cSFugang Duan * For i.MX6SX SOC, enet use AXI bus, we use disable MAC 1047106c314cSFugang Duan * instead of reset MAC itself. 1048106c314cSFugang Duan */ 1049471ff445SJoakim Zhang if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES || 1050c730ab42SLaurent Badel ((fep->quirks & FEC_QUIRK_NO_HARD_RESET) && fep->link)) { 1051106c314cSFugang Duan writel(0, fep->hwp + FEC_ECNTRL); 1052106c314cSFugang Duan } else { 1053793fc096SFrank Li writel(1, fep->hwp + FEC_ECNTRL); 1054793fc096SFrank Li udelay(10); 1055106c314cSFugang Duan } 1056793fc096SFrank Li 1057793fc096SFrank Li /* 1058793fc096SFrank Li * enet-mac reset will reset mac address registers too, 1059793fc096SFrank Li * so need to reconfigure it. 1060793fc096SFrank Li */ 1061793fc096SFrank Li memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN); 10625cfa3039SJohannes Berg writel((__force u32)cpu_to_be32(temp_mac[0]), 10635cfa3039SJohannes Berg fep->hwp + FEC_ADDR_LOW); 10645cfa3039SJohannes Berg writel((__force u32)cpu_to_be32(temp_mac[1]), 10655cfa3039SJohannes Berg fep->hwp + FEC_ADDR_HIGH); 1066793fc096SFrank Li 1067f166f890SAndrew Lunn /* Clear any outstanding interrupt, except MDIO. */ 1068f166f890SAndrew Lunn writel((0xffffffff & ~FEC_ENET_MII), fep->hwp + FEC_IEVENT); 1069793fc096SFrank Li 1070a210576cSDavid S. Miller fec_enet_bd_init(ndev); 1071a210576cSDavid S. Miller 107259d0f746SFrank Li fec_enet_enable_ring(ndev); 1073793fc096SFrank Li 107459d0f746SFrank Li /* Reset tx SKB buffers. */ 107559d0f746SFrank Li fec_enet_reset_skb(ndev); 1076793fc096SFrank Li 1077793fc096SFrank Li /* Enable MII mode */ 1078ef83337dSRussell King if (fep->full_duplex == DUPLEX_FULL) { 1079793fc096SFrank Li /* FD enable */ 1080793fc096SFrank Li writel(0x04, fep->hwp + FEC_X_CNTRL); 1081793fc096SFrank Li } else { 1082793fc096SFrank Li /* No Rcv on Xmit */ 1083793fc096SFrank Li rcntl |= 0x02; 1084793fc096SFrank Li writel(0x0, fep->hwp + FEC_X_CNTRL); 1085793fc096SFrank Li } 1086793fc096SFrank Li 1087793fc096SFrank Li /* Set MII speed */ 1088793fc096SFrank Li writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 1089793fc096SFrank Li 1090d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 109118803495SGreg Ungerer if (fep->quirks & FEC_QUIRK_HAS_RACC) { 109232d1bbb1SGeert Uytterhoeven u32 val = readl(fep->hwp + FEC_RACC); 109332d1bbb1SGeert Uytterhoeven 10943ac72b7bSEric Nelson /* align IP header */ 10953ac72b7bSEric Nelson val |= FEC_RACC_SHIFT16; 10964c09eed9SJim Baxter if (fep->csum_flags & FLAG_RX_CSUM_ENABLED) 10973ac72b7bSEric Nelson /* set RX checksum */ 10984c09eed9SJim Baxter val |= FEC_RACC_OPTIONS; 10994c09eed9SJim Baxter else 11004c09eed9SJim Baxter val &= ~FEC_RACC_OPTIONS; 11014c09eed9SJim Baxter writel(val, fep->hwp + FEC_RACC); 110255cd48c8STroy Kisky writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_FTRL); 110332867fccSFabio Estevam } 1104d1391930SGuenter Roeck #endif 11054c09eed9SJim Baxter 1106793fc096SFrank Li /* 1107793fc096SFrank Li * The phy interface and speed need to get configured 1108793fc096SFrank Li * differently on enet-mac. 1109793fc096SFrank Li */ 11106b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_ENET_MAC) { 1111793fc096SFrank Li /* Enable flow control and length check */ 1112793fc096SFrank Li rcntl |= 0x40000000 | 0x00000020; 1113793fc096SFrank Li 1114793fc096SFrank Li /* RGMII, RMII or MII */ 1115e813bb2bSMarkus Pargmann if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII || 1116e813bb2bSMarkus Pargmann fep->phy_interface == PHY_INTERFACE_MODE_RGMII_ID || 1117e813bb2bSMarkus Pargmann fep->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID || 1118e813bb2bSMarkus Pargmann fep->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID) 1119793fc096SFrank Li rcntl |= (1 << 6); 1120793fc096SFrank Li else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) 1121793fc096SFrank Li rcntl |= (1 << 8); 1122793fc096SFrank Li else 1123793fc096SFrank Li rcntl &= ~(1 << 8); 1124793fc096SFrank Li 1125793fc096SFrank Li /* 1G, 100M or 10M */ 112645f5c327SPhilippe Reynes if (ndev->phydev) { 112745f5c327SPhilippe Reynes if (ndev->phydev->speed == SPEED_1000) 1128793fc096SFrank Li ecntl |= (1 << 5); 112945f5c327SPhilippe Reynes else if (ndev->phydev->speed == SPEED_100) 1130793fc096SFrank Li rcntl &= ~(1 << 9); 1131793fc096SFrank Li else 1132793fc096SFrank Li rcntl |= (1 << 9); 1133793fc096SFrank Li } 1134793fc096SFrank Li } else { 1135793fc096SFrank Li #ifdef FEC_MIIGSK_ENR 11366b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_USE_GASKET) { 1137793fc096SFrank Li u32 cfgr; 1138793fc096SFrank Li /* disable the gasket and wait */ 1139793fc096SFrank Li writel(0, fep->hwp + FEC_MIIGSK_ENR); 1140793fc096SFrank Li while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4) 1141793fc096SFrank Li udelay(1); 1142793fc096SFrank Li 1143793fc096SFrank Li /* 1144793fc096SFrank Li * configure the gasket: 1145793fc096SFrank Li * RMII, 50 MHz, no loopback, no echo 1146793fc096SFrank Li * MII, 25 MHz, no loopback, no echo 1147793fc096SFrank Li */ 1148793fc096SFrank Li cfgr = (fep->phy_interface == PHY_INTERFACE_MODE_RMII) 1149793fc096SFrank Li ? BM_MIIGSK_CFGR_RMII : BM_MIIGSK_CFGR_MII; 115045f5c327SPhilippe Reynes if (ndev->phydev && ndev->phydev->speed == SPEED_10) 1151793fc096SFrank Li cfgr |= BM_MIIGSK_CFGR_FRCONT_10M; 1152793fc096SFrank Li writel(cfgr, fep->hwp + FEC_MIIGSK_CFGR); 1153793fc096SFrank Li 1154793fc096SFrank Li /* re-enable the gasket */ 1155793fc096SFrank Li writel(2, fep->hwp + FEC_MIIGSK_ENR); 1156793fc096SFrank Li } 1157793fc096SFrank Li #endif 1158793fc096SFrank Li } 1159793fc096SFrank Li 1160d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 1161793fc096SFrank Li /* enable pause frame*/ 1162793fc096SFrank Li if ((fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) || 1163793fc096SFrank Li ((fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) && 116445f5c327SPhilippe Reynes ndev->phydev && ndev->phydev->pause)) { 1165793fc096SFrank Li rcntl |= FEC_ENET_FCE; 1166793fc096SFrank Li 11674c09eed9SJim Baxter /* set FIFO threshold parameter to reduce overrun */ 1168793fc096SFrank Li writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM); 1169793fc096SFrank Li writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL); 1170793fc096SFrank Li writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM); 1171793fc096SFrank Li writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL); 1172793fc096SFrank Li 1173793fc096SFrank Li /* OPD */ 1174793fc096SFrank Li writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD); 1175793fc096SFrank Li } else { 1176793fc096SFrank Li rcntl &= ~FEC_ENET_FCE; 1177793fc096SFrank Li } 1178d1391930SGuenter Roeck #endif /* !defined(CONFIG_M5272) */ 1179793fc096SFrank Li 1180793fc096SFrank Li writel(rcntl, fep->hwp + FEC_R_CNTRL); 1181793fc096SFrank Li 118284fe6182SStefan Wahren /* Setup multicast filter. */ 118384fe6182SStefan Wahren set_multicast_list(ndev); 118484fe6182SStefan Wahren #ifndef CONFIG_M5272 118584fe6182SStefan Wahren writel(0, fep->hwp + FEC_HASH_TABLE_HIGH); 118684fe6182SStefan Wahren writel(0, fep->hwp + FEC_HASH_TABLE_LOW); 118784fe6182SStefan Wahren #endif 118884fe6182SStefan Wahren 11896b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_ENET_MAC) { 1190793fc096SFrank Li /* enable ENET endian swap */ 1191793fc096SFrank Li ecntl |= (1 << 8); 1192793fc096SFrank Li /* enable ENET store and forward mode */ 1193793fc096SFrank Li writel(1 << 8, fep->hwp + FEC_X_WMRK); 1194793fc096SFrank Li } 1195793fc096SFrank Li 1196793fc096SFrank Li if (fep->bufdesc_ex) 11977b15515fSFrancesco Dolcini ecntl |= (1 << 4); 1198793fc096SFrank Li 1199fc539459SFugang Duan if (fep->quirks & FEC_QUIRK_DELAYED_CLKS_SUPPORT && 1200fc539459SFugang Duan fep->rgmii_txc_dly) 1201fc539459SFugang Duan ecntl |= FEC_ENET_TXC_DLY; 1202fc539459SFugang Duan if (fep->quirks & FEC_QUIRK_DELAYED_CLKS_SUPPORT && 1203fc539459SFugang Duan fep->rgmii_rxc_dly) 1204fc539459SFugang Duan ecntl |= FEC_ENET_RXC_DLY; 1205fc539459SFugang Duan 120638ae92dcSChris Healy #ifndef CONFIG_M5272 1207b9eef55cSJim Baxter /* Enable the MIB statistic event counters */ 1208b9eef55cSJim Baxter writel(0 << 31, fep->hwp + FEC_MIB_CTRLSTAT); 120938ae92dcSChris Healy #endif 121038ae92dcSChris Healy 1211793fc096SFrank Li /* And last, enable the transmit and receive processing */ 1212793fc096SFrank Li writel(ecntl, fep->hwp + FEC_ECNTRL); 1213ce99d0d3SFrank Li fec_enet_active_rxring(ndev); 1214793fc096SFrank Li 1215793fc096SFrank Li if (fep->bufdesc_ex) 1216793fc096SFrank Li fec_ptp_start_cyclecounter(ndev); 1217793fc096SFrank Li 1218793fc096SFrank Li /* Enable interrupts we wish to service */ 12190c5a3aefSNimrod Andy if (fep->link) 1220793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 12210c5a3aefSNimrod Andy else 1222f166f890SAndrew Lunn writel(0, fep->hwp + FEC_IMASK); 1223d851b47bSFugang Duan 1224d851b47bSFugang Duan /* Init the interrupt coalescing */ 12257e630356SRasmus Villemoes if (fep->quirks & FEC_QUIRK_HAS_COALESCE) 1226df727d45SRasmus Villemoes fec_enet_itr_coal_set(ndev); 1227793fc096SFrank Li } 1228793fc096SFrank Li 122940c79ce1SWei Fang static int fec_enet_ipc_handle_init(struct fec_enet_private *fep) 123040c79ce1SWei Fang { 123140c79ce1SWei Fang if (!(of_machine_is_compatible("fsl,imx8qm") || 123240c79ce1SWei Fang of_machine_is_compatible("fsl,imx8qxp") || 123340c79ce1SWei Fang of_machine_is_compatible("fsl,imx8dxl"))) 123440c79ce1SWei Fang return 0; 123540c79ce1SWei Fang 123640c79ce1SWei Fang return imx_scu_get_handle(&fep->ipc_handle); 123740c79ce1SWei Fang } 123840c79ce1SWei Fang 123940c79ce1SWei Fang static void fec_enet_ipg_stop_set(struct fec_enet_private *fep, bool enabled) 124040c79ce1SWei Fang { 124140c79ce1SWei Fang struct device_node *np = fep->pdev->dev.of_node; 124240c79ce1SWei Fang u32 rsrc_id, val; 124340c79ce1SWei Fang int idx; 124440c79ce1SWei Fang 124540c79ce1SWei Fang if (!np || !fep->ipc_handle) 124640c79ce1SWei Fang return; 124740c79ce1SWei Fang 124840c79ce1SWei Fang idx = of_alias_get_id(np, "ethernet"); 124940c79ce1SWei Fang if (idx < 0) 125040c79ce1SWei Fang idx = 0; 125140c79ce1SWei Fang rsrc_id = idx ? IMX_SC_R_ENET_1 : IMX_SC_R_ENET_0; 125240c79ce1SWei Fang 125340c79ce1SWei Fang val = enabled ? 1 : 0; 125440c79ce1SWei Fang imx_sc_misc_set_control(fep->ipc_handle, rsrc_id, IMX_SC_C_IPG_STOP, val); 125540c79ce1SWei Fang } 125640c79ce1SWei Fang 1257da722186SMartin Fuzzey static void fec_enet_stop_mode(struct fec_enet_private *fep, bool enabled) 1258da722186SMartin Fuzzey { 1259da722186SMartin Fuzzey struct fec_platform_data *pdata = fep->pdev->dev.platform_data; 1260da722186SMartin Fuzzey struct fec_stop_mode_gpr *stop_gpr = &fep->stop_gpr; 1261da722186SMartin Fuzzey 1262da722186SMartin Fuzzey if (stop_gpr->gpr) { 1263da722186SMartin Fuzzey if (enabled) 1264da722186SMartin Fuzzey regmap_update_bits(stop_gpr->gpr, stop_gpr->reg, 1265da722186SMartin Fuzzey BIT(stop_gpr->bit), 1266da722186SMartin Fuzzey BIT(stop_gpr->bit)); 1267da722186SMartin Fuzzey else 1268da722186SMartin Fuzzey regmap_update_bits(stop_gpr->gpr, stop_gpr->reg, 1269da722186SMartin Fuzzey BIT(stop_gpr->bit), 0); 1270da722186SMartin Fuzzey } else if (pdata && pdata->sleep_mode_enable) { 1271da722186SMartin Fuzzey pdata->sleep_mode_enable(enabled); 127240c79ce1SWei Fang } else { 127340c79ce1SWei Fang fec_enet_ipg_stop_set(fep, enabled); 1274da722186SMartin Fuzzey } 1275da722186SMartin Fuzzey } 1276da722186SMartin Fuzzey 12770b6f65c7SJoakim Zhang static void fec_irqs_disable(struct net_device *ndev) 12780b6f65c7SJoakim Zhang { 12790b6f65c7SJoakim Zhang struct fec_enet_private *fep = netdev_priv(ndev); 12800b6f65c7SJoakim Zhang 12810b6f65c7SJoakim Zhang writel(0, fep->hwp + FEC_IMASK); 12820b6f65c7SJoakim Zhang } 12830b6f65c7SJoakim Zhang 12840b6f65c7SJoakim Zhang static void fec_irqs_disable_except_wakeup(struct net_device *ndev) 12850b6f65c7SJoakim Zhang { 12860b6f65c7SJoakim Zhang struct fec_enet_private *fep = netdev_priv(ndev); 12870b6f65c7SJoakim Zhang 12880b6f65c7SJoakim Zhang writel(0, fep->hwp + FEC_IMASK); 12890b6f65c7SJoakim Zhang writel(FEC_ENET_WAKEUP, fep->hwp + FEC_IMASK); 12900b6f65c7SJoakim Zhang } 12910b6f65c7SJoakim Zhang 1292793fc096SFrank Li static void 1293793fc096SFrank Li fec_stop(struct net_device *ndev) 1294793fc096SFrank Li { 1295793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1296793fc096SFrank Li u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8); 1297de40ed31SNimrod Andy u32 val; 1298793fc096SFrank Li 1299793fc096SFrank Li /* We cannot expect a graceful transmit stop without link !!! */ 1300793fc096SFrank Li if (fep->link) { 1301793fc096SFrank Li writel(1, fep->hwp + FEC_X_CNTRL); /* Graceful transmit stop */ 1302793fc096SFrank Li udelay(10); 1303793fc096SFrank Li if (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_GRA)) 130431b7720cSJoe Perches netdev_err(ndev, "Graceful transmit stop did not complete!\n"); 1305793fc096SFrank Li } 1306793fc096SFrank Li 1307106c314cSFugang Duan /* Whack a reset. We should wait for this. 1308106c314cSFugang Duan * For i.MX6SX SOC, enet use AXI bus, we use disable MAC 1309106c314cSFugang Duan * instead of reset MAC itself. 1310106c314cSFugang Duan */ 1311de40ed31SNimrod Andy if (!(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { 1312471ff445SJoakim Zhang if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) { 1313106c314cSFugang Duan writel(0, fep->hwp + FEC_ECNTRL); 1314106c314cSFugang Duan } else { 1315793fc096SFrank Li writel(1, fep->hwp + FEC_ECNTRL); 1316793fc096SFrank Li udelay(10); 1317106c314cSFugang Duan } 1318de40ed31SNimrod Andy } else { 1319de40ed31SNimrod Andy val = readl(fep->hwp + FEC_ECNTRL); 1320de40ed31SNimrod Andy val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP); 1321de40ed31SNimrod Andy writel(val, fep->hwp + FEC_ECNTRL); 1322de40ed31SNimrod Andy } 1323de40ed31SNimrod Andy writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 13240b6f65c7SJoakim Zhang writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 1325793fc096SFrank Li 1326793fc096SFrank Li /* We have to keep ENET enabled to have MII interrupt stay working */ 1327de40ed31SNimrod Andy if (fep->quirks & FEC_QUIRK_ENET_MAC && 1328de40ed31SNimrod Andy !(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { 13297b15515fSFrancesco Dolcini writel(2, fep->hwp + FEC_ECNTRL); 1330793fc096SFrank Li writel(rmii_mode, fep->hwp + FEC_R_CNTRL); 1331793fc096SFrank Li } 1332793fc096SFrank Li } 1333793fc096SFrank Li 1334793fc096SFrank Li 1335793fc096SFrank Li static void 13360290bd29SMichael S. Tsirkin fec_timeout(struct net_device *ndev, unsigned int txqueue) 1337793fc096SFrank Li { 1338793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1339793fc096SFrank Li 1340344756f6SRussell King fec_dump(ndev); 1341344756f6SRussell King 1342793fc096SFrank Li ndev->stats.tx_errors++; 1343793fc096SFrank Li 134436cdc743SRussell King schedule_work(&fep->tx_timeout_work); 134554309fa6SFrank Li } 134654309fa6SFrank Li 134736cdc743SRussell King static void fec_enet_timeout_work(struct work_struct *work) 134854309fa6SFrank Li { 134954309fa6SFrank Li struct fec_enet_private *fep = 135036cdc743SRussell King container_of(work, struct fec_enet_private, tx_timeout_work); 13518ce5624fSRussell King struct net_device *ndev = fep->netdev; 135254309fa6SFrank Li 1353da1774e5SRussell King rtnl_lock(); 13548ce5624fSRussell King if (netif_device_present(ndev) || netif_running(ndev)) { 1355dbc64a8eSRussell King napi_disable(&fep->napi); 1356dbc64a8eSRussell King netif_tx_lock_bh(ndev); 1357ef83337dSRussell King fec_restart(ndev); 1358657ade07SRickard x Andersson netif_tx_wake_all_queues(ndev); 13596af42d42SRussell King netif_tx_unlock_bh(ndev); 1360dbc64a8eSRussell King napi_enable(&fep->napi); 13618ce5624fSRussell King } 1362da1774e5SRussell King rtnl_unlock(); 136354309fa6SFrank Li } 1364793fc096SFrank Li 1365793fc096SFrank Li static void 1366bfd4ecddSRussell King fec_enet_hwtstamp(struct fec_enet_private *fep, unsigned ts, 1367bfd4ecddSRussell King struct skb_shared_hwtstamps *hwtstamps) 1368bfd4ecddSRussell King { 1369bfd4ecddSRussell King unsigned long flags; 1370bfd4ecddSRussell King u64 ns; 1371bfd4ecddSRussell King 1372bfd4ecddSRussell King spin_lock_irqsave(&fep->tmreg_lock, flags); 1373bfd4ecddSRussell King ns = timecounter_cyc2time(&fep->tc, ts); 1374bfd4ecddSRussell King spin_unlock_irqrestore(&fep->tmreg_lock, flags); 1375bfd4ecddSRussell King 1376bfd4ecddSRussell King memset(hwtstamps, 0, sizeof(*hwtstamps)); 1377bfd4ecddSRussell King hwtstamps->hwtstamp = ns_to_ktime(ns); 1378bfd4ecddSRussell King } 1379bfd4ecddSRussell King 1380bfd4ecddSRussell King static void 13814d494cdcSFugang Duan fec_enet_tx_queue(struct net_device *ndev, u16 queue_id) 1382793fc096SFrank Li { 1383793fc096SFrank Li struct fec_enet_private *fep; 1384a2fe37b6SFabio Estevam struct bufdesc *bdp; 1385793fc096SFrank Li unsigned short status; 1386793fc096SFrank Li struct sk_buff *skb; 13874d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 13884d494cdcSFugang Duan struct netdev_queue *nq; 1389793fc096SFrank Li int index = 0; 139079f33912SNimrod Andy int entries_free; 1391793fc096SFrank Li 1392793fc096SFrank Li fep = netdev_priv(ndev); 13934d494cdcSFugang Duan 13944d494cdcSFugang Duan txq = fep->tx_queue[queue_id]; 13954d494cdcSFugang Duan /* get next bdp of dirty_tx */ 13964d494cdcSFugang Duan nq = netdev_get_tx_queue(ndev, queue_id); 13974d494cdcSFugang Duan bdp = txq->dirty_tx; 1398793fc096SFrank Li 1399793fc096SFrank Li /* get next bdp of dirty_tx */ 14007355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 1401793fc096SFrank Li 14027355f276STroy Kisky while (bdp != READ_ONCE(txq->bd.cur)) { 14037355f276STroy Kisky /* Order the load of bd.cur and cbd_sc */ 1404c4bc44c6SKevin Hao rmb(); 14055cfa3039SJohannes Berg status = fec16_to_cpu(READ_ONCE(bdp->cbd_sc)); 1406c4bc44c6SKevin Hao if (status & BD_ENET_TX_READY) 1407793fc096SFrank Li break; 1408793fc096SFrank Li 14097355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 14102b995f63SNimrod Andy 1411a2fe37b6SFabio Estevam skb = txq->tx_skbuff[index]; 1412a2fe37b6SFabio Estevam txq->tx_skbuff[index] = NULL; 14135cfa3039SJohannes Berg if (!IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr))) 14145cfa3039SJohannes Berg dma_unmap_single(&fep->pdev->dev, 14155cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 14165cfa3039SJohannes Berg fec16_to_cpu(bdp->cbd_datlen), 14175cfa3039SJohannes Berg DMA_TO_DEVICE); 14185cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(0); 14197fafe803STroy Kisky if (!skb) 14207fafe803STroy Kisky goto skb_done; 1421793fc096SFrank Li 1422793fc096SFrank Li /* Check for errors. */ 1423793fc096SFrank Li if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC | 1424793fc096SFrank Li BD_ENET_TX_RL | BD_ENET_TX_UN | 1425793fc096SFrank Li BD_ENET_TX_CSL)) { 1426793fc096SFrank Li ndev->stats.tx_errors++; 1427793fc096SFrank Li if (status & BD_ENET_TX_HB) /* No heartbeat */ 1428793fc096SFrank Li ndev->stats.tx_heartbeat_errors++; 1429793fc096SFrank Li if (status & BD_ENET_TX_LC) /* Late collision */ 1430793fc096SFrank Li ndev->stats.tx_window_errors++; 1431793fc096SFrank Li if (status & BD_ENET_TX_RL) /* Retrans limit */ 1432793fc096SFrank Li ndev->stats.tx_aborted_errors++; 1433793fc096SFrank Li if (status & BD_ENET_TX_UN) /* Underrun */ 1434793fc096SFrank Li ndev->stats.tx_fifo_errors++; 1435793fc096SFrank Li if (status & BD_ENET_TX_CSL) /* Carrier lost */ 1436793fc096SFrank Li ndev->stats.tx_carrier_errors++; 1437793fc096SFrank Li } else { 1438793fc096SFrank Li ndev->stats.tx_packets++; 14396e909283SNimrod Andy ndev->stats.tx_bytes += skb->len; 1440793fc096SFrank Li } 1441793fc096SFrank Li 144234074639SSergey Organov /* NOTE: SKBTX_IN_PROGRESS being set does not imply it's we who 144334074639SSergey Organov * are to time stamp the packet, so we still need to check time 144434074639SSergey Organov * stamping enabled flag. 144534074639SSergey Organov */ 144634074639SSergey Organov if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS && 144734074639SSergey Organov fep->hwts_tx_en) && 1448793fc096SFrank Li fep->bufdesc_ex) { 1449793fc096SFrank Li struct skb_shared_hwtstamps shhwtstamps; 1450793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 1451793fc096SFrank Li 14525cfa3039SJohannes Berg fec_enet_hwtstamp(fep, fec32_to_cpu(ebdp->ts), &shhwtstamps); 1453793fc096SFrank Li skb_tstamp_tx(skb, &shhwtstamps); 1454793fc096SFrank Li } 1455793fc096SFrank Li 1456793fc096SFrank Li /* Deferred means some collisions occurred during transmit, 1457793fc096SFrank Li * but we eventually sent the packet OK. 1458793fc096SFrank Li */ 1459793fc096SFrank Li if (status & BD_ENET_TX_DEF) 1460793fc096SFrank Li ndev->stats.collisions++; 1461793fc096SFrank Li 1462793fc096SFrank Li /* Free the sk buffer associated with this last transmit */ 1463793fc096SFrank Li dev_kfree_skb_any(skb); 14647fafe803STroy Kisky skb_done: 1465c4bc44c6SKevin Hao /* Make sure the update to bdp and tx_skbuff are performed 1466c4bc44c6SKevin Hao * before dirty_tx 1467c4bc44c6SKevin Hao */ 1468c4bc44c6SKevin Hao wmb(); 14694d494cdcSFugang Duan txq->dirty_tx = bdp; 1470793fc096SFrank Li 1471793fc096SFrank Li /* Update pointer to next buffer descriptor to be transmitted */ 14727355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 1473793fc096SFrank Li 1474793fc096SFrank Li /* Since we have freed up a buffer, the ring is no longer full 1475793fc096SFrank Li */ 1476657ade07SRickard x Andersson if (netif_tx_queue_stopped(nq)) { 14777355f276STroy Kisky entries_free = fec_enet_get_free_txdesc_num(txq); 14784d494cdcSFugang Duan if (entries_free >= txq->tx_wake_threshold) 14794d494cdcSFugang Duan netif_tx_wake_queue(nq); 1480793fc096SFrank Li } 148179f33912SNimrod Andy } 1482ccea2968SRussell King 1483c10bc0e7SFugang Duan /* ERR006358: Keep the transmitter going */ 14847355f276STroy Kisky if (bdp != txq->bd.cur && 148553bb20d1STroy Kisky readl(txq->bd.reg_desc_active) == 0) 148653bb20d1STroy Kisky writel(0, txq->bd.reg_desc_active); 14874d494cdcSFugang Duan } 14884d494cdcSFugang Duan 14897cdaa4ccSTobias Waldekranz static void fec_enet_tx(struct net_device *ndev) 14904d494cdcSFugang Duan { 14914d494cdcSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 14927cdaa4ccSTobias Waldekranz int i; 14937cdaa4ccSTobias Waldekranz 14947cdaa4ccSTobias Waldekranz /* Make sure that AVB queues are processed first. */ 14957cdaa4ccSTobias Waldekranz for (i = fep->num_tx_queues - 1; i >= 0; i--) 14967cdaa4ccSTobias Waldekranz fec_enet_tx_queue(ndev, i); 1497793fc096SFrank Li } 1498793fc096SFrank Li 149995698ff6SShenwei Wang static void fec_enet_update_cbd(struct fec_enet_priv_rx_q *rxq, 150095698ff6SShenwei Wang struct bufdesc *bdp, int index) 150195698ff6SShenwei Wang { 150295698ff6SShenwei Wang struct page *new_page; 150395698ff6SShenwei Wang dma_addr_t phys_addr; 150495698ff6SShenwei Wang 150595698ff6SShenwei Wang new_page = page_pool_dev_alloc_pages(rxq->page_pool); 150695698ff6SShenwei Wang WARN_ON(!new_page); 150795698ff6SShenwei Wang rxq->rx_skb_info[index].page = new_page; 150895698ff6SShenwei Wang 150995698ff6SShenwei Wang rxq->rx_skb_info[index].offset = FEC_ENET_XDP_HEADROOM; 151095698ff6SShenwei Wang phys_addr = page_pool_get_dma_addr(new_page) + FEC_ENET_XDP_HEADROOM; 151195698ff6SShenwei Wang bdp->cbd_bufaddr = cpu_to_fec32(phys_addr); 151295698ff6SShenwei Wang } 151395698ff6SShenwei Wang 15146d6b39f1SShenwei Wang static u32 15156d6b39f1SShenwei Wang fec_enet_run_xdp(struct fec_enet_private *fep, struct bpf_prog *prog, 15166d6b39f1SShenwei Wang struct xdp_buff *xdp, struct fec_enet_priv_rx_q *rxq, int index) 15176d6b39f1SShenwei Wang { 15186d6b39f1SShenwei Wang unsigned int sync, len = xdp->data_end - xdp->data; 15196d6b39f1SShenwei Wang u32 ret = FEC_ENET_XDP_PASS; 15206d6b39f1SShenwei Wang struct page *page; 15216d6b39f1SShenwei Wang int err; 15226d6b39f1SShenwei Wang u32 act; 15236d6b39f1SShenwei Wang 15246d6b39f1SShenwei Wang act = bpf_prog_run_xdp(prog, xdp); 15256d6b39f1SShenwei Wang 15266d6b39f1SShenwei Wang /* Due xdp_adjust_tail: DMA sync for_device cover max len CPU touch */ 15276d6b39f1SShenwei Wang sync = xdp->data_end - xdp->data_hard_start - FEC_ENET_XDP_HEADROOM; 15286d6b39f1SShenwei Wang sync = max(sync, len); 15296d6b39f1SShenwei Wang 15306d6b39f1SShenwei Wang switch (act) { 15316d6b39f1SShenwei Wang case XDP_PASS: 15326970ef27SShenwei Wang rxq->stats[RX_XDP_PASS]++; 15336d6b39f1SShenwei Wang ret = FEC_ENET_XDP_PASS; 15346d6b39f1SShenwei Wang break; 15356d6b39f1SShenwei Wang 15366d6b39f1SShenwei Wang case XDP_REDIRECT: 15376970ef27SShenwei Wang rxq->stats[RX_XDP_REDIRECT]++; 15386d6b39f1SShenwei Wang err = xdp_do_redirect(fep->netdev, xdp, prog); 15396d6b39f1SShenwei Wang if (!err) { 15406d6b39f1SShenwei Wang ret = FEC_ENET_XDP_REDIR; 15416d6b39f1SShenwei Wang } else { 15426d6b39f1SShenwei Wang ret = FEC_ENET_XDP_CONSUMED; 15436d6b39f1SShenwei Wang page = virt_to_head_page(xdp->data); 15446d6b39f1SShenwei Wang page_pool_put_page(rxq->page_pool, page, sync, true); 15456d6b39f1SShenwei Wang } 15466d6b39f1SShenwei Wang break; 15476d6b39f1SShenwei Wang 15486d6b39f1SShenwei Wang default: 15496d6b39f1SShenwei Wang bpf_warn_invalid_xdp_action(fep->netdev, prog, act); 15506d6b39f1SShenwei Wang fallthrough; 15516d6b39f1SShenwei Wang 15526d6b39f1SShenwei Wang case XDP_TX: 15536d6b39f1SShenwei Wang bpf_warn_invalid_xdp_action(fep->netdev, prog, act); 15546d6b39f1SShenwei Wang fallthrough; 15556d6b39f1SShenwei Wang 15566d6b39f1SShenwei Wang case XDP_ABORTED: 15576d6b39f1SShenwei Wang fallthrough; /* handle aborts by dropping packet */ 15586d6b39f1SShenwei Wang 15596d6b39f1SShenwei Wang case XDP_DROP: 15606970ef27SShenwei Wang rxq->stats[RX_XDP_DROP]++; 15616d6b39f1SShenwei Wang ret = FEC_ENET_XDP_CONSUMED; 15626d6b39f1SShenwei Wang page = virt_to_head_page(xdp->data); 15636d6b39f1SShenwei Wang page_pool_put_page(rxq->page_pool, page, sync, true); 15646d6b39f1SShenwei Wang break; 15656d6b39f1SShenwei Wang } 15666d6b39f1SShenwei Wang 15676d6b39f1SShenwei Wang return ret; 15686d6b39f1SShenwei Wang } 15696d6b39f1SShenwei Wang 15707355f276STroy Kisky /* During a receive, the bd_rx.cur points to the current incoming buffer. 1571793fc096SFrank Li * When we update through the ring, if the next incoming buffer has 1572793fc096SFrank Li * not been given to the system, we just set the empty indicator, 1573793fc096SFrank Li * effectively tossing the packet. 1574793fc096SFrank Li */ 1575793fc096SFrank Li static int 15764d494cdcSFugang Duan fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) 1577793fc096SFrank Li { 1578793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 15794d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 1580793fc096SFrank Li struct bufdesc *bdp; 1581793fc096SFrank Li unsigned short status; 1582793fc096SFrank Li struct sk_buff *skb; 1583793fc096SFrank Li ushort pkt_len; 1584793fc096SFrank Li __u8 *data; 1585793fc096SFrank Li int pkt_received = 0; 1586cdffcf1bSJim Baxter struct bufdesc_ex *ebdp = NULL; 1587cdffcf1bSJim Baxter bool vlan_packet_rcvd = false; 1588cdffcf1bSJim Baxter u16 vlan_tag; 1589d842a31fSDuan Fugang-B38611 int index = 0; 15906b7e4008SLothar Waßmann bool need_swap = fep->quirks & FEC_QUIRK_SWAP_FRAME; 15916d6b39f1SShenwei Wang struct bpf_prog *xdp_prog = READ_ONCE(fep->xdp_prog); 15926d6b39f1SShenwei Wang u32 ret, xdp_result = FEC_ENET_XDP_PASS; 15936c8fae0cSShenwei Wang u32 data_start = FEC_ENET_XDP_HEADROOM; 15946d6b39f1SShenwei Wang struct xdp_buff xdp; 159595698ff6SShenwei Wang struct page *page; 15966c8fae0cSShenwei Wang u32 sub_len = 4; 15976c8fae0cSShenwei Wang 15986c8fae0cSShenwei Wang #if !defined(CONFIG_M5272) 15996c8fae0cSShenwei Wang /*If it has the FEC_QUIRK_HAS_RACC quirk property, the bit of 16006c8fae0cSShenwei Wang * FEC_RACC_SHIFT16 is set by default in the probe function. 16016c8fae0cSShenwei Wang */ 16026c8fae0cSShenwei Wang if (fep->quirks & FEC_QUIRK_HAS_RACC) { 16036c8fae0cSShenwei Wang data_start += 2; 16046c8fae0cSShenwei Wang sub_len += 2; 16056c8fae0cSShenwei Wang } 16066c8fae0cSShenwei Wang #endif 1607793fc096SFrank Li 1608793fc096SFrank Li #ifdef CONFIG_M532x 1609793fc096SFrank Li flush_cache_all(); 1610793fc096SFrank Li #endif 16114d494cdcSFugang Duan rxq = fep->rx_queue[queue_id]; 1612793fc096SFrank Li 1613793fc096SFrank Li /* First, grab all of the stats for the incoming packet. 1614793fc096SFrank Li * These get messed up if we get called due to a busy condition. 1615793fc096SFrank Li */ 16167355f276STroy Kisky bdp = rxq->bd.cur; 16176d6b39f1SShenwei Wang xdp_init_buff(&xdp, PAGE_SIZE, &rxq->xdp_rxq); 1618793fc096SFrank Li 16195cfa3039SJohannes Berg while (!((status = fec16_to_cpu(bdp->cbd_sc)) & BD_ENET_RX_EMPTY)) { 1620793fc096SFrank Li 1621793fc096SFrank Li if (pkt_received >= budget) 1622793fc096SFrank Li break; 1623793fc096SFrank Li pkt_received++; 1624793fc096SFrank Li 1625b5bd95d1SJoakim Zhang writel(FEC_ENET_RXF_GET(queue_id), fep->hwp + FEC_IEVENT); 1626db3421c1SRussell King 1627793fc096SFrank Li /* Check for errors. */ 1628095098e1STroy Kisky status ^= BD_ENET_RX_LAST; 1629793fc096SFrank Li if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | 1630095098e1STroy Kisky BD_ENET_RX_CR | BD_ENET_RX_OV | BD_ENET_RX_LAST | 1631095098e1STroy Kisky BD_ENET_RX_CL)) { 1632793fc096SFrank Li ndev->stats.rx_errors++; 1633095098e1STroy Kisky if (status & BD_ENET_RX_OV) { 1634095098e1STroy Kisky /* FIFO overrun */ 1635095098e1STroy Kisky ndev->stats.rx_fifo_errors++; 1636095098e1STroy Kisky goto rx_processing_done; 1637095098e1STroy Kisky } 1638095098e1STroy Kisky if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH 1639095098e1STroy Kisky | BD_ENET_RX_LAST)) { 1640793fc096SFrank Li /* Frame too long or too short. */ 1641793fc096SFrank Li ndev->stats.rx_length_errors++; 1642095098e1STroy Kisky if (status & BD_ENET_RX_LAST) 1643095098e1STroy Kisky netdev_err(ndev, "rcv is not +last\n"); 1644793fc096SFrank Li } 1645793fc096SFrank Li if (status & BD_ENET_RX_CR) /* CRC Error */ 1646793fc096SFrank Li ndev->stats.rx_crc_errors++; 1647095098e1STroy Kisky /* Report late collisions as a frame error. */ 1648095098e1STroy Kisky if (status & (BD_ENET_RX_NO | BD_ENET_RX_CL)) 1649793fc096SFrank Li ndev->stats.rx_frame_errors++; 1650793fc096SFrank Li goto rx_processing_done; 1651793fc096SFrank Li } 1652793fc096SFrank Li 1653793fc096SFrank Li /* Process the incoming frame. */ 1654793fc096SFrank Li ndev->stats.rx_packets++; 16555cfa3039SJohannes Berg pkt_len = fec16_to_cpu(bdp->cbd_datlen); 1656793fc096SFrank Li ndev->stats.rx_bytes += pkt_len; 1657793fc096SFrank Li 16587355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &rxq->bd); 165995698ff6SShenwei Wang page = rxq->rx_skb_info[index].page; 166095698ff6SShenwei Wang dma_sync_single_for_cpu(&fep->pdev->dev, 166195698ff6SShenwei Wang fec32_to_cpu(bdp->cbd_bufaddr), 166295698ff6SShenwei Wang pkt_len, 166395698ff6SShenwei Wang DMA_FROM_DEVICE); 166495698ff6SShenwei Wang prefetch(page_address(page)); 166595698ff6SShenwei Wang fec_enet_update_cbd(rxq, bdp, index); 16661b7bde6dSNimrod Andy 16676d6b39f1SShenwei Wang if (xdp_prog) { 16686d6b39f1SShenwei Wang xdp_buff_clear_frags_flag(&xdp); 16696c8fae0cSShenwei Wang /* subtract 16bit shift and FCS */ 16706d6b39f1SShenwei Wang xdp_prepare_buff(&xdp, page_address(page), 16716c8fae0cSShenwei Wang data_start, pkt_len - sub_len, false); 16726d6b39f1SShenwei Wang ret = fec_enet_run_xdp(fep, xdp_prog, &xdp, rxq, index); 16736d6b39f1SShenwei Wang xdp_result |= ret; 16746d6b39f1SShenwei Wang if (ret != FEC_ENET_XDP_PASS) 16756d6b39f1SShenwei Wang goto rx_processing_done; 16766d6b39f1SShenwei Wang } 16776d6b39f1SShenwei Wang 16781b7bde6dSNimrod Andy /* The packet length includes FCS, but we don't want to 16791b7bde6dSNimrod Andy * include that when passing upstream as it messes up 16801b7bde6dSNimrod Andy * bridging applications. 16811b7bde6dSNimrod Andy */ 168295698ff6SShenwei Wang skb = build_skb(page_address(page), PAGE_SIZE); 168319e72b06SWei Fang if (unlikely(!skb)) { 168419e72b06SWei Fang page_pool_recycle_direct(rxq->page_pool, page); 168519e72b06SWei Fang ndev->stats.rx_dropped++; 168619e72b06SWei Fang 168719e72b06SWei Fang netdev_err_once(ndev, "build_skb failed!\n"); 168819e72b06SWei Fang goto rx_processing_done; 168919e72b06SWei Fang } 169019e72b06SWei Fang 16916c8fae0cSShenwei Wang skb_reserve(skb, data_start); 16926c8fae0cSShenwei Wang skb_put(skb, pkt_len - sub_len); 169395698ff6SShenwei Wang skb_mark_for_recycle(skb); 16943ac72b7bSEric Nelson 16956c8fae0cSShenwei Wang if (unlikely(need_swap)) { 16966c8fae0cSShenwei Wang data = page_address(page) + FEC_ENET_XDP_HEADROOM; 1697235bde1eSFabio Estevam swap_buffer(data, pkt_len); 16986c8fae0cSShenwei Wang } 16996c8fae0cSShenwei Wang data = skb->data; 17003ac72b7bSEric Nelson 1701cdffcf1bSJim Baxter /* Extract the enhanced buffer descriptor */ 1702cdffcf1bSJim Baxter ebdp = NULL; 1703cdffcf1bSJim Baxter if (fep->bufdesc_ex) 1704cdffcf1bSJim Baxter ebdp = (struct bufdesc_ex *)bdp; 1705cdffcf1bSJim Baxter 1706cdffcf1bSJim Baxter /* If this is a VLAN packet remove the VLAN Tag */ 1707cdffcf1bSJim Baxter vlan_packet_rcvd = false; 1708cdffcf1bSJim Baxter if ((ndev->features & NETIF_F_HW_VLAN_CTAG_RX) && 17095cfa3039SJohannes Berg fep->bufdesc_ex && 17105cfa3039SJohannes Berg (ebdp->cbd_esc & cpu_to_fec32(BD_ENET_RX_VLAN))) { 1711cdffcf1bSJim Baxter /* Push and remove the vlan tag */ 1712cdffcf1bSJim Baxter struct vlan_hdr *vlan_header = 1713cdffcf1bSJim Baxter (struct vlan_hdr *) (data + ETH_HLEN); 1714cdffcf1bSJim Baxter vlan_tag = ntohs(vlan_header->h_vlan_TCI); 1715cdffcf1bSJim Baxter 1716cdffcf1bSJim Baxter vlan_packet_rcvd = true; 17171b7bde6dSNimrod Andy 1718af5cbc98SNimrod Andy memmove(skb->data + VLAN_HLEN, data, ETH_ALEN * 2); 17191b7bde6dSNimrod Andy skb_pull(skb, VLAN_HLEN); 1720cdffcf1bSJim Baxter } 1721cdffcf1bSJim Baxter 1722793fc096SFrank Li skb->protocol = eth_type_trans(skb, ndev); 1723793fc096SFrank Li 1724793fc096SFrank Li /* Get receive timestamp from the skb */ 1725bfd4ecddSRussell King if (fep->hwts_rx_en && fep->bufdesc_ex) 17265cfa3039SJohannes Berg fec_enet_hwtstamp(fep, fec32_to_cpu(ebdp->ts), 1727bfd4ecddSRussell King skb_hwtstamps(skb)); 1728793fc096SFrank Li 17294c09eed9SJim Baxter if (fep->bufdesc_ex && 17304c09eed9SJim Baxter (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { 17315cfa3039SJohannes Berg if (!(ebdp->cbd_esc & cpu_to_fec32(FLAG_RX_CSUM_ERROR))) { 17324c09eed9SJim Baxter /* don't check it */ 17334c09eed9SJim Baxter skb->ip_summed = CHECKSUM_UNNECESSARY; 17344c09eed9SJim Baxter } else { 17354c09eed9SJim Baxter skb_checksum_none_assert(skb); 17364c09eed9SJim Baxter } 17374c09eed9SJim Baxter } 17384c09eed9SJim Baxter 1739cdffcf1bSJim Baxter /* Handle received VLAN packets */ 1740cdffcf1bSJim Baxter if (vlan_packet_rcvd) 1741cdffcf1bSJim Baxter __vlan_hwaccel_put_tag(skb, 1742cdffcf1bSJim Baxter htons(ETH_P_8021Q), 1743cdffcf1bSJim Baxter vlan_tag); 1744cdffcf1bSJim Baxter 17457cdaa4ccSTobias Waldekranz skb_record_rx_queue(skb, queue_id); 1746793fc096SFrank Li napi_gro_receive(&fep->napi, skb); 1747793fc096SFrank Li 1748793fc096SFrank Li rx_processing_done: 1749793fc096SFrank Li /* Clear the status flags for this buffer */ 1750793fc096SFrank Li status &= ~BD_ENET_RX_STATS; 1751793fc096SFrank Li 1752793fc096SFrank Li /* Mark the buffer empty */ 1753793fc096SFrank Li status |= BD_ENET_RX_EMPTY; 1754793fc096SFrank Li 1755793fc096SFrank Li if (fep->bufdesc_ex) { 1756793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 1757793fc096SFrank Li 17585cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(BD_ENET_RX_INT); 1759793fc096SFrank Li ebdp->cbd_prot = 0; 1760793fc096SFrank Li ebdp->cbd_bdu = 0; 1761793fc096SFrank Li } 1762be293467STroy Kisky /* Make sure the updates to rest of the descriptor are 1763be293467STroy Kisky * performed before transferring ownership. 1764be293467STroy Kisky */ 1765be293467STroy Kisky wmb(); 1766be293467STroy Kisky bdp->cbd_sc = cpu_to_fec16(status); 1767793fc096SFrank Li 1768793fc096SFrank Li /* Update BD pointer to next entry */ 17697355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 177036e24e2eSDuan Fugang-B38611 1771793fc096SFrank Li /* Doing this here will keep the FEC running while we process 1772793fc096SFrank Li * incoming frames. On a heavily loaded network, we should be 1773793fc096SFrank Li * able to keep up at the expense of system resources. 1774793fc096SFrank Li */ 177553bb20d1STroy Kisky writel(0, rxq->bd.reg_desc_active); 1776793fc096SFrank Li } 17777355f276STroy Kisky rxq->bd.cur = bdp; 17786d6b39f1SShenwei Wang 17796d6b39f1SShenwei Wang if (xdp_result & FEC_ENET_XDP_REDIR) 17806d6b39f1SShenwei Wang xdp_do_flush_map(); 17816d6b39f1SShenwei Wang 1782793fc096SFrank Li return pkt_received; 1783793fc096SFrank Li } 1784793fc096SFrank Li 17857cdaa4ccSTobias Waldekranz static int fec_enet_rx(struct net_device *ndev, int budget) 17864d494cdcSFugang Duan { 17874d494cdcSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 17887cdaa4ccSTobias Waldekranz int i, done = 0; 17894d494cdcSFugang Duan 17907cdaa4ccSTobias Waldekranz /* Make sure that AVB queues are processed first. */ 17917cdaa4ccSTobias Waldekranz for (i = fep->num_rx_queues - 1; i >= 0; i--) 17927cdaa4ccSTobias Waldekranz done += fec_enet_rx_queue(ndev, budget - done, i); 17931c021bb7SUwe Kleine-König 17947cdaa4ccSTobias Waldekranz return done; 17954d494cdcSFugang Duan } 17964d494cdcSFugang Duan 17977cdaa4ccSTobias Waldekranz static bool fec_enet_collect_events(struct fec_enet_private *fep) 17984d494cdcSFugang Duan { 1799793fc096SFrank Li uint int_events; 1800793fc096SFrank Li 1801793fc096SFrank Li int_events = readl(fep->hwp + FEC_IEVENT); 1802f166f890SAndrew Lunn 1803f166f890SAndrew Lunn /* Don't clear MDIO events, we poll for those */ 1804f166f890SAndrew Lunn int_events &= ~FEC_ENET_MII; 1805f166f890SAndrew Lunn 180694191fd6SNimrod Andy writel(int_events, fep->hwp + FEC_IEVENT); 1807793fc096SFrank Li 18087cdaa4ccSTobias Waldekranz return int_events != 0; 18097cdaa4ccSTobias Waldekranz } 18107cdaa4ccSTobias Waldekranz 18117cdaa4ccSTobias Waldekranz static irqreturn_t 18127cdaa4ccSTobias Waldekranz fec_enet_interrupt(int irq, void *dev_id) 18137cdaa4ccSTobias Waldekranz { 18147cdaa4ccSTobias Waldekranz struct net_device *ndev = dev_id; 18157cdaa4ccSTobias Waldekranz struct fec_enet_private *fep = netdev_priv(ndev); 18167cdaa4ccSTobias Waldekranz irqreturn_t ret = IRQ_NONE; 18177cdaa4ccSTobias Waldekranz 18187cdaa4ccSTobias Waldekranz if (fec_enet_collect_events(fep) && fep->link) { 1819793fc096SFrank Li ret = IRQ_HANDLED; 1820793fc096SFrank Li 182194191fd6SNimrod Andy if (napi_schedule_prep(&fep->napi)) { 1822f166f890SAndrew Lunn /* Disable interrupts */ 1823f166f890SAndrew Lunn writel(0, fep->hwp + FEC_IMASK); 182494191fd6SNimrod Andy __napi_schedule(&fep->napi); 182594191fd6SNimrod Andy } 1826793fc096SFrank Li } 1827793fc096SFrank Li 1828793fc096SFrank Li return ret; 1829793fc096SFrank Li } 1830793fc096SFrank Li 1831793fc096SFrank Li static int fec_enet_rx_napi(struct napi_struct *napi, int budget) 1832793fc096SFrank Li { 1833793fc096SFrank Li struct net_device *ndev = napi->dev; 1834793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 18357cdaa4ccSTobias Waldekranz int done = 0; 18367a16807cSRussell King 18377cdaa4ccSTobias Waldekranz do { 18387cdaa4ccSTobias Waldekranz done += fec_enet_rx(ndev, budget - done); 1839793fc096SFrank Li fec_enet_tx(ndev); 18407cdaa4ccSTobias Waldekranz } while ((done < budget) && fec_enet_collect_events(fep)); 1841793fc096SFrank Li 18427cdaa4ccSTobias Waldekranz if (done < budget) { 18437cdaa4ccSTobias Waldekranz napi_complete_done(napi, done); 1844793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 1845793fc096SFrank Li } 18467cdaa4ccSTobias Waldekranz 18477cdaa4ccSTobias Waldekranz return done; 1848793fc096SFrank Li } 1849793fc096SFrank Li 1850793fc096SFrank Li /* ------------------------------------------------------------------------- */ 1851052fcc45SFugang Duan static int fec_get_mac(struct net_device *ndev) 1852793fc096SFrank Li { 1853793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1854793fc096SFrank Li unsigned char *iap, tmpaddr[ETH_ALEN]; 185583216e39SMichael Walle int ret; 1856793fc096SFrank Li 1857793fc096SFrank Li /* 1858793fc096SFrank Li * try to get mac address in following order: 1859793fc096SFrank Li * 1860793fc096SFrank Li * 1) module parameter via kernel command line in form 1861793fc096SFrank Li * fec.macaddr=0x00,0x04,0x9f,0x01,0x30,0xe0 1862793fc096SFrank Li */ 1863793fc096SFrank Li iap = macaddr; 1864793fc096SFrank Li 1865793fc096SFrank Li /* 1866793fc096SFrank Li * 2) from device tree data 1867793fc096SFrank Li */ 1868793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 1869793fc096SFrank Li struct device_node *np = fep->pdev->dev.of_node; 1870793fc096SFrank Li if (np) { 187183216e39SMichael Walle ret = of_get_mac_address(np, tmpaddr); 187283216e39SMichael Walle if (!ret) 187383216e39SMichael Walle iap = tmpaddr; 1874052fcc45SFugang Duan else if (ret == -EPROBE_DEFER) 1875052fcc45SFugang Duan return ret; 1876793fc096SFrank Li } 1877793fc096SFrank Li } 1878793fc096SFrank Li 1879793fc096SFrank Li /* 1880793fc096SFrank Li * 3) from flash or fuse (via platform data) 1881793fc096SFrank Li */ 1882793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 1883793fc096SFrank Li #ifdef CONFIG_M5272 1884793fc096SFrank Li if (FEC_FLASHMAC) 1885793fc096SFrank Li iap = (unsigned char *)FEC_FLASHMAC; 1886793fc096SFrank Li #else 188732d1bbb1SGeert Uytterhoeven struct fec_platform_data *pdata = dev_get_platdata(&fep->pdev->dev); 188832d1bbb1SGeert Uytterhoeven 1889793fc096SFrank Li if (pdata) 1890793fc096SFrank Li iap = (unsigned char *)&pdata->mac; 1891793fc096SFrank Li #endif 1892793fc096SFrank Li } 1893793fc096SFrank Li 1894793fc096SFrank Li /* 1895793fc096SFrank Li * 4) FEC mac registers set by bootloader 1896793fc096SFrank Li */ 1897793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 18987d7628f3SDan Carpenter *((__be32 *) &tmpaddr[0]) = 18997d7628f3SDan Carpenter cpu_to_be32(readl(fep->hwp + FEC_ADDR_LOW)); 19007d7628f3SDan Carpenter *((__be16 *) &tmpaddr[4]) = 19017d7628f3SDan Carpenter cpu_to_be16(readl(fep->hwp + FEC_ADDR_HIGH) >> 16); 1902793fc096SFrank Li iap = &tmpaddr[0]; 1903793fc096SFrank Li } 1904793fc096SFrank Li 1905ff5b2fabSLucas Stach /* 1906ff5b2fabSLucas Stach * 5) random mac address 1907ff5b2fabSLucas Stach */ 1908ff5b2fabSLucas Stach if (!is_valid_ether_addr(iap)) { 1909ff5b2fabSLucas Stach /* Report it and use a random ethernet address instead */ 1910a19a0582SFabio Estevam dev_err(&fep->pdev->dev, "Invalid MAC address: %pM\n", iap); 1911ff5b2fabSLucas Stach eth_hw_addr_random(ndev); 1912a19a0582SFabio Estevam dev_info(&fep->pdev->dev, "Using random MAC address: %pM\n", 1913ff5b2fabSLucas Stach ndev->dev_addr); 1914052fcc45SFugang Duan return 0; 1915ff5b2fabSLucas Stach } 1916ff5b2fabSLucas Stach 1917793fc096SFrank Li /* Adjust MAC if using macaddr */ 1918ba3fdfe3SJakub Kicinski eth_hw_addr_gen(ndev, iap, iap == macaddr ? fep->dev_id : 0); 1919052fcc45SFugang Duan 1920052fcc45SFugang Duan return 0; 1921793fc096SFrank Li } 1922793fc096SFrank Li 1923793fc096SFrank Li /* ------------------------------------------------------------------------- */ 1924793fc096SFrank Li 1925793fc096SFrank Li /* 1926793fc096SFrank Li * Phy section 1927793fc096SFrank Li */ 1928793fc096SFrank Li static void fec_enet_adjust_link(struct net_device *ndev) 1929793fc096SFrank Li { 1930793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 193145f5c327SPhilippe Reynes struct phy_device *phy_dev = ndev->phydev; 1932793fc096SFrank Li int status_change = 0; 1933793fc096SFrank Li 19348ce5624fSRussell King /* 19358ce5624fSRussell King * If the netdev is down, or is going down, we're not interested 19368ce5624fSRussell King * in link state events, so just mark our idea of the link as down 19378ce5624fSRussell King * and ignore the event. 19388ce5624fSRussell King */ 19398ce5624fSRussell King if (!netif_running(ndev) || !netif_device_present(ndev)) { 19408ce5624fSRussell King fep->link = 0; 19418ce5624fSRussell King } else if (phy_dev->link) { 1942793fc096SFrank Li if (!fep->link) { 1943793fc096SFrank Li fep->link = phy_dev->link; 1944793fc096SFrank Li status_change = 1; 1945793fc096SFrank Li } 1946793fc096SFrank Li 1947ef83337dSRussell King if (fep->full_duplex != phy_dev->duplex) { 1948ef83337dSRussell King fep->full_duplex = phy_dev->duplex; 1949793fc096SFrank Li status_change = 1; 1950ef83337dSRussell King } 1951793fc096SFrank Li 1952793fc096SFrank Li if (phy_dev->speed != fep->speed) { 1953793fc096SFrank Li fep->speed = phy_dev->speed; 1954793fc096SFrank Li status_change = 1; 1955793fc096SFrank Li } 1956793fc096SFrank Li 1957793fc096SFrank Li /* if any of the above changed restart the FEC */ 1958dbc64a8eSRussell King if (status_change) { 1959dbc64a8eSRussell King napi_disable(&fep->napi); 1960dbc64a8eSRussell King netif_tx_lock_bh(ndev); 1961ef83337dSRussell King fec_restart(ndev); 1962657ade07SRickard x Andersson netif_tx_wake_all_queues(ndev); 19636af42d42SRussell King netif_tx_unlock_bh(ndev); 1964dbc64a8eSRussell King napi_enable(&fep->napi); 1965dbc64a8eSRussell King } 1966793fc096SFrank Li } else { 1967793fc096SFrank Li if (fep->link) { 1968f208ce10SRussell King napi_disable(&fep->napi); 1969f208ce10SRussell King netif_tx_lock_bh(ndev); 1970793fc096SFrank Li fec_stop(ndev); 1971f208ce10SRussell King netif_tx_unlock_bh(ndev); 1972f208ce10SRussell King napi_enable(&fep->napi); 19736e0895c2SDavid S. Miller fep->link = phy_dev->link; 1974793fc096SFrank Li status_change = 1; 1975793fc096SFrank Li } 1976793fc096SFrank Li } 1977793fc096SFrank Li 1978793fc096SFrank Li if (status_change) 1979793fc096SFrank Li phy_print_status(phy_dev); 1980793fc096SFrank Li } 1981793fc096SFrank Li 1982f166f890SAndrew Lunn static int fec_enet_mdio_wait(struct fec_enet_private *fep) 1983f166f890SAndrew Lunn { 1984f166f890SAndrew Lunn uint ievent; 1985f166f890SAndrew Lunn int ret; 1986f166f890SAndrew Lunn 1987f166f890SAndrew Lunn ret = readl_poll_timeout_atomic(fep->hwp + FEC_IEVENT, ievent, 1988f166f890SAndrew Lunn ievent & FEC_ENET_MII, 2, 30000); 1989f166f890SAndrew Lunn 1990f166f890SAndrew Lunn if (!ret) 1991f166f890SAndrew Lunn writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT); 1992f166f890SAndrew Lunn 1993f166f890SAndrew Lunn return ret; 1994f166f890SAndrew Lunn } 1995f166f890SAndrew Lunn 19968d03ad1aSAndrew Lunn static int fec_enet_mdio_read_c22(struct mii_bus *bus, int mii_id, int regnum) 1997793fc096SFrank Li { 1998793fc096SFrank Li struct fec_enet_private *fep = bus->priv; 19998fff755eSAndrew Lunn struct device *dev = &fep->pdev->dev; 2000d3ee8ec7SMarco Hartmann int ret = 0, frame_start, frame_addr, frame_op; 20018fff755eSAndrew Lunn 2002da875fa5SZhang Qilong ret = pm_runtime_resume_and_get(dev); 2003b0c6ce24SFabio Estevam if (ret < 0) 20048fff755eSAndrew Lunn return ret; 2005793fc096SFrank Li 2006d3ee8ec7SMarco Hartmann /* C22 read */ 2007d3ee8ec7SMarco Hartmann frame_op = FEC_MMFR_OP_READ; 2008d3ee8ec7SMarco Hartmann frame_start = FEC_MMFR_ST; 2009d3ee8ec7SMarco Hartmann frame_addr = regnum; 2010d3ee8ec7SMarco Hartmann 2011793fc096SFrank Li /* start a read op */ 2012d3ee8ec7SMarco Hartmann writel(frame_start | frame_op | 2013d3ee8ec7SMarco Hartmann FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | 2014793fc096SFrank Li FEC_MMFR_TA, fep->hwp + FEC_MII_DATA); 2015793fc096SFrank Li 2016793fc096SFrank Li /* wait for end of transfer */ 2017f166f890SAndrew Lunn ret = fec_enet_mdio_wait(fep); 2018f166f890SAndrew Lunn if (ret) { 201931b7720cSJoe Perches netdev_err(fep->netdev, "MDIO read timeout\n"); 20208fff755eSAndrew Lunn goto out; 2021793fc096SFrank Li } 2022793fc096SFrank Li 20238fff755eSAndrew Lunn ret = FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA)); 20248fff755eSAndrew Lunn 20258fff755eSAndrew Lunn out: 20268fff755eSAndrew Lunn pm_runtime_mark_last_busy(dev); 20278fff755eSAndrew Lunn pm_runtime_put_autosuspend(dev); 20288fff755eSAndrew Lunn 20298fff755eSAndrew Lunn return ret; 2030793fc096SFrank Li } 2031793fc096SFrank Li 20328d03ad1aSAndrew Lunn static int fec_enet_mdio_read_c45(struct mii_bus *bus, int mii_id, 20338d03ad1aSAndrew Lunn int devad, int regnum) 2034793fc096SFrank Li { 2035793fc096SFrank Li struct fec_enet_private *fep = bus->priv; 20368fff755eSAndrew Lunn struct device *dev = &fep->pdev->dev; 20378d03ad1aSAndrew Lunn int ret = 0, frame_start, frame_op; 20388fff755eSAndrew Lunn 2039da875fa5SZhang Qilong ret = pm_runtime_resume_and_get(dev); 2040b0c6ce24SFabio Estevam if (ret < 0) 20418fff755eSAndrew Lunn return ret; 2042793fc096SFrank Li 2043d3ee8ec7SMarco Hartmann frame_start = FEC_MMFR_ST_C45; 2044d3ee8ec7SMarco Hartmann 2045d3ee8ec7SMarco Hartmann /* write address */ 2046d3ee8ec7SMarco Hartmann writel(frame_start | FEC_MMFR_OP_ADDR_WRITE | 20478d03ad1aSAndrew Lunn FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(devad) | 2048d3ee8ec7SMarco Hartmann FEC_MMFR_TA | (regnum & 0xFFFF), 2049d3ee8ec7SMarco Hartmann fep->hwp + FEC_MII_DATA); 2050d3ee8ec7SMarco Hartmann 2051d3ee8ec7SMarco Hartmann /* wait for end of transfer */ 2052f166f890SAndrew Lunn ret = fec_enet_mdio_wait(fep); 2053f166f890SAndrew Lunn if (ret) { 2054d3ee8ec7SMarco Hartmann netdev_err(fep->netdev, "MDIO address write timeout\n"); 2055d3ee8ec7SMarco Hartmann goto out; 2056d3ee8ec7SMarco Hartmann } 20578d03ad1aSAndrew Lunn 20588d03ad1aSAndrew Lunn frame_op = FEC_MMFR_OP_READ_C45; 20598d03ad1aSAndrew Lunn 20608d03ad1aSAndrew Lunn /* start a read op */ 20618d03ad1aSAndrew Lunn writel(frame_start | frame_op | 20628d03ad1aSAndrew Lunn FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(devad) | 20638d03ad1aSAndrew Lunn FEC_MMFR_TA, fep->hwp + FEC_MII_DATA); 20648d03ad1aSAndrew Lunn 20658d03ad1aSAndrew Lunn /* wait for end of transfer */ 20668d03ad1aSAndrew Lunn ret = fec_enet_mdio_wait(fep); 20678d03ad1aSAndrew Lunn if (ret) { 20688d03ad1aSAndrew Lunn netdev_err(fep->netdev, "MDIO read timeout\n"); 20698d03ad1aSAndrew Lunn goto out; 20708d03ad1aSAndrew Lunn } 20718d03ad1aSAndrew Lunn 20728d03ad1aSAndrew Lunn ret = FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA)); 20738d03ad1aSAndrew Lunn 20748d03ad1aSAndrew Lunn out: 20758d03ad1aSAndrew Lunn pm_runtime_mark_last_busy(dev); 20768d03ad1aSAndrew Lunn pm_runtime_put_autosuspend(dev); 20778d03ad1aSAndrew Lunn 20788d03ad1aSAndrew Lunn return ret; 20798d03ad1aSAndrew Lunn } 20808d03ad1aSAndrew Lunn 20818d03ad1aSAndrew Lunn static int fec_enet_mdio_write_c22(struct mii_bus *bus, int mii_id, int regnum, 20828d03ad1aSAndrew Lunn u16 value) 20838d03ad1aSAndrew Lunn { 20848d03ad1aSAndrew Lunn struct fec_enet_private *fep = bus->priv; 20858d03ad1aSAndrew Lunn struct device *dev = &fep->pdev->dev; 20868d03ad1aSAndrew Lunn int ret, frame_start, frame_addr; 20878d03ad1aSAndrew Lunn 20888d03ad1aSAndrew Lunn ret = pm_runtime_resume_and_get(dev); 20898d03ad1aSAndrew Lunn if (ret < 0) 20908d03ad1aSAndrew Lunn return ret; 20918d03ad1aSAndrew Lunn 2092d3ee8ec7SMarco Hartmann /* C22 write */ 2093d3ee8ec7SMarco Hartmann frame_start = FEC_MMFR_ST; 2094d3ee8ec7SMarco Hartmann frame_addr = regnum; 2095d3ee8ec7SMarco Hartmann 2096793fc096SFrank Li /* start a write op */ 2097d3ee8ec7SMarco Hartmann writel(frame_start | FEC_MMFR_OP_WRITE | 2098d3ee8ec7SMarco Hartmann FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | 2099793fc096SFrank Li FEC_MMFR_TA | FEC_MMFR_DATA(value), 2100793fc096SFrank Li fep->hwp + FEC_MII_DATA); 2101793fc096SFrank Li 2102793fc096SFrank Li /* wait for end of transfer */ 2103f166f890SAndrew Lunn ret = fec_enet_mdio_wait(fep); 2104f166f890SAndrew Lunn if (ret) 210531b7720cSJoe Perches netdev_err(fep->netdev, "MDIO write timeout\n"); 2106793fc096SFrank Li 21078d03ad1aSAndrew Lunn pm_runtime_mark_last_busy(dev); 21088d03ad1aSAndrew Lunn pm_runtime_put_autosuspend(dev); 21098d03ad1aSAndrew Lunn 21108d03ad1aSAndrew Lunn return ret; 21118d03ad1aSAndrew Lunn } 21128d03ad1aSAndrew Lunn 21138d03ad1aSAndrew Lunn static int fec_enet_mdio_write_c45(struct mii_bus *bus, int mii_id, 21148d03ad1aSAndrew Lunn int devad, int regnum, u16 value) 21158d03ad1aSAndrew Lunn { 21168d03ad1aSAndrew Lunn struct fec_enet_private *fep = bus->priv; 21178d03ad1aSAndrew Lunn struct device *dev = &fep->pdev->dev; 21188d03ad1aSAndrew Lunn int ret, frame_start; 21198d03ad1aSAndrew Lunn 21208d03ad1aSAndrew Lunn ret = pm_runtime_resume_and_get(dev); 21218d03ad1aSAndrew Lunn if (ret < 0) 21228d03ad1aSAndrew Lunn return ret; 21238d03ad1aSAndrew Lunn 21248d03ad1aSAndrew Lunn frame_start = FEC_MMFR_ST_C45; 21258d03ad1aSAndrew Lunn 21268d03ad1aSAndrew Lunn /* write address */ 21278d03ad1aSAndrew Lunn writel(frame_start | FEC_MMFR_OP_ADDR_WRITE | 21288d03ad1aSAndrew Lunn FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(devad) | 21298d03ad1aSAndrew Lunn FEC_MMFR_TA | (regnum & 0xFFFF), 21308d03ad1aSAndrew Lunn fep->hwp + FEC_MII_DATA); 21318d03ad1aSAndrew Lunn 21328d03ad1aSAndrew Lunn /* wait for end of transfer */ 21338d03ad1aSAndrew Lunn ret = fec_enet_mdio_wait(fep); 21348d03ad1aSAndrew Lunn if (ret) { 21358d03ad1aSAndrew Lunn netdev_err(fep->netdev, "MDIO address write timeout\n"); 21368d03ad1aSAndrew Lunn goto out; 21378d03ad1aSAndrew Lunn } 21388d03ad1aSAndrew Lunn 21398d03ad1aSAndrew Lunn /* start a write op */ 21408d03ad1aSAndrew Lunn writel(frame_start | FEC_MMFR_OP_WRITE | 21418d03ad1aSAndrew Lunn FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(devad) | 21428d03ad1aSAndrew Lunn FEC_MMFR_TA | FEC_MMFR_DATA(value), 21438d03ad1aSAndrew Lunn fep->hwp + FEC_MII_DATA); 21448d03ad1aSAndrew Lunn 21458d03ad1aSAndrew Lunn /* wait for end of transfer */ 21468d03ad1aSAndrew Lunn ret = fec_enet_mdio_wait(fep); 21478d03ad1aSAndrew Lunn if (ret) 21488d03ad1aSAndrew Lunn netdev_err(fep->netdev, "MDIO write timeout\n"); 21498d03ad1aSAndrew Lunn 2150d3ee8ec7SMarco Hartmann out: 21518fff755eSAndrew Lunn pm_runtime_mark_last_busy(dev); 21528fff755eSAndrew Lunn pm_runtime_put_autosuspend(dev); 21538fff755eSAndrew Lunn 21548fff755eSAndrew Lunn return ret; 2155793fc096SFrank Li } 2156793fc096SFrank Li 215764a632daSMarek Vasut static void fec_enet_phy_reset_after_clk_enable(struct net_device *ndev) 215864a632daSMarek Vasut { 215964a632daSMarek Vasut struct fec_enet_private *fep = netdev_priv(ndev); 216064a632daSMarek Vasut struct phy_device *phy_dev = ndev->phydev; 216164a632daSMarek Vasut 216264a632daSMarek Vasut if (phy_dev) { 216364a632daSMarek Vasut phy_reset_after_clk_enable(phy_dev); 216464a632daSMarek Vasut } else if (fep->phy_node) { 216564a632daSMarek Vasut /* 216664a632daSMarek Vasut * If the PHY still is not bound to the MAC, but there is 216764a632daSMarek Vasut * OF PHY node and a matching PHY device instance already, 216864a632daSMarek Vasut * use the OF PHY node to obtain the PHY device instance, 216964a632daSMarek Vasut * and then use that PHY device instance when triggering 217064a632daSMarek Vasut * the PHY reset. 217164a632daSMarek Vasut */ 217264a632daSMarek Vasut phy_dev = of_phy_find_device(fep->phy_node); 217364a632daSMarek Vasut phy_reset_after_clk_enable(phy_dev); 217464a632daSMarek Vasut put_device(&phy_dev->mdio.dev); 217564a632daSMarek Vasut } 217664a632daSMarek Vasut } 217764a632daSMarek Vasut 2178e8fcfcd5SNimrod Andy static int fec_enet_clk_enable(struct net_device *ndev, bool enable) 2179e8fcfcd5SNimrod Andy { 2180e8fcfcd5SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 2181e8fcfcd5SNimrod Andy int ret; 2182e8fcfcd5SNimrod Andy 2183e8fcfcd5SNimrod Andy if (enable) { 2184e8fcfcd5SNimrod Andy ret = clk_prepare_enable(fep->clk_enet_out); 2185e8fcfcd5SNimrod Andy if (ret) 2186d7c3a206SAndy Duan return ret; 218701e5943aSUwe Kleine-König 2188e8fcfcd5SNimrod Andy if (fep->clk_ptp) { 218901b825f9SFrancesco Dolcini mutex_lock(&fep->ptp_clk_mutex); 2190e8fcfcd5SNimrod Andy ret = clk_prepare_enable(fep->clk_ptp); 219191c0d987SNimrod Andy if (ret) { 219201b825f9SFrancesco Dolcini mutex_unlock(&fep->ptp_clk_mutex); 2193e8fcfcd5SNimrod Andy goto failed_clk_ptp; 219491c0d987SNimrod Andy } else { 219591c0d987SNimrod Andy fep->ptp_clk_on = true; 219691c0d987SNimrod Andy } 219701b825f9SFrancesco Dolcini mutex_unlock(&fep->ptp_clk_mutex); 2198e8fcfcd5SNimrod Andy } 219901e5943aSUwe Kleine-König 22009b5330edSFugang Duan ret = clk_prepare_enable(fep->clk_ref); 22019b5330edSFugang Duan if (ret) 22029b5330edSFugang Duan goto failed_clk_ref; 22031b0a83acSRichard Leitner 2204fc539459SFugang Duan ret = clk_prepare_enable(fep->clk_2x_txclk); 2205fc539459SFugang Duan if (ret) 2206fc539459SFugang Duan goto failed_clk_2x_txclk; 2207fc539459SFugang Duan 220864a632daSMarek Vasut fec_enet_phy_reset_after_clk_enable(ndev); 2209e8fcfcd5SNimrod Andy } else { 2210e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_enet_out); 221191c0d987SNimrod Andy if (fep->clk_ptp) { 221201b825f9SFrancesco Dolcini mutex_lock(&fep->ptp_clk_mutex); 2213e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_ptp); 221491c0d987SNimrod Andy fep->ptp_clk_on = false; 221501b825f9SFrancesco Dolcini mutex_unlock(&fep->ptp_clk_mutex); 221691c0d987SNimrod Andy } 22179b5330edSFugang Duan clk_disable_unprepare(fep->clk_ref); 2218fc539459SFugang Duan clk_disable_unprepare(fep->clk_2x_txclk); 2219e8fcfcd5SNimrod Andy } 2220e8fcfcd5SNimrod Andy 2221e8fcfcd5SNimrod Andy return 0; 22229b5330edSFugang Duan 2223fc539459SFugang Duan failed_clk_2x_txclk: 2224fc539459SFugang Duan if (fep->clk_ref) 2225fc539459SFugang Duan clk_disable_unprepare(fep->clk_ref); 22269b5330edSFugang Duan failed_clk_ref: 2227a74d19baSLiu Xiang if (fep->clk_ptp) { 222801b825f9SFrancesco Dolcini mutex_lock(&fep->ptp_clk_mutex); 2229a74d19baSLiu Xiang clk_disable_unprepare(fep->clk_ptp); 2230a74d19baSLiu Xiang fep->ptp_clk_on = false; 223101b825f9SFrancesco Dolcini mutex_unlock(&fep->ptp_clk_mutex); 2232a74d19baSLiu Xiang } 2233e8fcfcd5SNimrod Andy failed_clk_ptp: 2234e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_enet_out); 2235e8fcfcd5SNimrod Andy 2236e8fcfcd5SNimrod Andy return ret; 2237e8fcfcd5SNimrod Andy } 2238e8fcfcd5SNimrod Andy 2239b820c114SJoakim Zhang static int fec_enet_parse_rgmii_delay(struct fec_enet_private *fep, 2240b820c114SJoakim Zhang struct device_node *np) 2241b820c114SJoakim Zhang { 2242b820c114SJoakim Zhang u32 rgmii_tx_delay, rgmii_rx_delay; 2243b820c114SJoakim Zhang 2244b820c114SJoakim Zhang /* For rgmii tx internal delay, valid values are 0ps and 2000ps */ 2245b820c114SJoakim Zhang if (!of_property_read_u32(np, "tx-internal-delay-ps", &rgmii_tx_delay)) { 2246b820c114SJoakim Zhang if (rgmii_tx_delay != 0 && rgmii_tx_delay != 2000) { 2247b820c114SJoakim Zhang dev_err(&fep->pdev->dev, "The only allowed RGMII TX delay values are: 0ps, 2000ps"); 2248b820c114SJoakim Zhang return -EINVAL; 2249b820c114SJoakim Zhang } else if (rgmii_tx_delay == 2000) { 2250b820c114SJoakim Zhang fep->rgmii_txc_dly = true; 2251b820c114SJoakim Zhang } 2252b820c114SJoakim Zhang } 2253b820c114SJoakim Zhang 2254b820c114SJoakim Zhang /* For rgmii rx internal delay, valid values are 0ps and 2000ps */ 2255b820c114SJoakim Zhang if (!of_property_read_u32(np, "rx-internal-delay-ps", &rgmii_rx_delay)) { 2256b820c114SJoakim Zhang if (rgmii_rx_delay != 0 && rgmii_rx_delay != 2000) { 2257b820c114SJoakim Zhang dev_err(&fep->pdev->dev, "The only allowed RGMII RX delay values are: 0ps, 2000ps"); 2258b820c114SJoakim Zhang return -EINVAL; 2259b820c114SJoakim Zhang } else if (rgmii_rx_delay == 2000) { 2260b820c114SJoakim Zhang fep->rgmii_rxc_dly = true; 2261b820c114SJoakim Zhang } 2262b820c114SJoakim Zhang } 2263b820c114SJoakim Zhang 2264b820c114SJoakim Zhang return 0; 2265b820c114SJoakim Zhang } 2266b820c114SJoakim Zhang 2267793fc096SFrank Li static int fec_enet_mii_probe(struct net_device *ndev) 2268793fc096SFrank Li { 2269793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2270793fc096SFrank Li struct phy_device *phy_dev = NULL; 2271793fc096SFrank Li char mdio_bus_id[MII_BUS_ID_SIZE]; 2272793fc096SFrank Li char phy_name[MII_BUS_ID_SIZE + 3]; 2273793fc096SFrank Li int phy_id; 2274793fc096SFrank Li int dev_id = fep->dev_id; 2275793fc096SFrank Li 2276407066f8SUwe Kleine-König if (fep->phy_node) { 2277407066f8SUwe Kleine-König phy_dev = of_phy_connect(ndev, fep->phy_node, 2278407066f8SUwe Kleine-König &fec_enet_adjust_link, 0, 2279407066f8SUwe Kleine-König fep->phy_interface); 22809558df3aSAndrew Lunn if (!phy_dev) { 22819558df3aSAndrew Lunn netdev_err(ndev, "Unable to connect to phy\n"); 2282213a9922SNimrod Andy return -ENODEV; 22839558df3aSAndrew Lunn } 2284407066f8SUwe Kleine-König } else { 2285793fc096SFrank Li /* check for attached phy */ 2286793fc096SFrank Li for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) { 22877f854420SAndrew Lunn if (!mdiobus_is_registered_device(fep->mii_bus, phy_id)) 2288793fc096SFrank Li continue; 2289793fc096SFrank Li if (dev_id--) 2290793fc096SFrank Li continue; 2291f029c781SWolfram Sang strscpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE); 2292793fc096SFrank Li break; 2293793fc096SFrank Li } 2294793fc096SFrank Li 2295793fc096SFrank Li if (phy_id >= PHY_MAX_ADDR) { 229631b7720cSJoe Perches netdev_info(ndev, "no PHY, assuming direct connection to switch\n"); 2297f029c781SWolfram Sang strscpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE); 2298793fc096SFrank Li phy_id = 0; 2299793fc096SFrank Li } 2300793fc096SFrank Li 2301407066f8SUwe Kleine-König snprintf(phy_name, sizeof(phy_name), 2302407066f8SUwe Kleine-König PHY_ID_FMT, mdio_bus_id, phy_id); 2303793fc096SFrank Li phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, 2304793fc096SFrank Li fep->phy_interface); 2305407066f8SUwe Kleine-König } 2306407066f8SUwe Kleine-König 2307793fc096SFrank Li if (IS_ERR(phy_dev)) { 230831b7720cSJoe Perches netdev_err(ndev, "could not attach to PHY\n"); 2309793fc096SFrank Li return PTR_ERR(phy_dev); 2310793fc096SFrank Li } 2311793fc096SFrank Li 2312793fc096SFrank Li /* mask with MAC supported features */ 23136b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_GBIT) { 231458056c1eSAndrew Lunn phy_set_max_speed(phy_dev, 1000); 231541124fa6SAndrew Lunn phy_remove_link_mode(phy_dev, 231641124fa6SAndrew Lunn ETHTOOL_LINK_MODE_1000baseT_Half_BIT); 2317d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 2318c306ad36SAndrew Lunn phy_support_sym_pause(phy_dev); 2319d1391930SGuenter Roeck #endif 2320793fc096SFrank Li } 2321793fc096SFrank Li else 232258056c1eSAndrew Lunn phy_set_max_speed(phy_dev, 100); 2323793fc096SFrank Li 2324793fc096SFrank Li fep->link = 0; 2325793fc096SFrank Li fep->full_duplex = 0; 2326793fc096SFrank Li 2327eca485d2SDenis Kirjanov phy_dev->mac_managed_pm = true; 2328557d5dc8SHeiner Kallweit 23292220943aSAndrew Lunn phy_attached_info(phy_dev); 2330793fc096SFrank Li 2331793fc096SFrank Li return 0; 2332793fc096SFrank Li } 2333793fc096SFrank Li 2334793fc096SFrank Li static int fec_enet_mii_init(struct platform_device *pdev) 2335793fc096SFrank Li { 2336793fc096SFrank Li static struct mii_bus *fec0_mii_bus; 2337793fc096SFrank Li struct net_device *ndev = platform_get_drvdata(pdev); 2338793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 23393c01eb62SAndrew Lunn bool suppress_preamble = false; 2340407066f8SUwe Kleine-König struct device_node *node; 2341e7f4dc35SAndrew Lunn int err = -ENXIO; 234263c60732SUwe Kleine-König u32 mii_speed, holdtime; 23433e782985SAndrew Lunn u32 bus_freq; 2344793fc096SFrank Li 2345793fc096SFrank Li /* 23463d125f9cSStefan Agner * The i.MX28 dual fec interfaces are not equal. 2347793fc096SFrank Li * Here are the differences: 2348793fc096SFrank Li * 2349793fc096SFrank Li * - fec0 supports MII & RMII modes while fec1 only supports RMII 2350793fc096SFrank Li * - fec0 acts as the 1588 time master while fec1 is slave 2351793fc096SFrank Li * - external phys can only be configured by fec0 2352793fc096SFrank Li * 2353793fc096SFrank Li * That is to say fec1 can not work independently. It only works 2354793fc096SFrank Li * when fec0 is working. The reason behind this design is that the 2355793fc096SFrank Li * second interface is added primarily for Switch mode. 2356793fc096SFrank Li * 2357793fc096SFrank Li * Because of the last point above, both phys are attached on fec0 2358793fc096SFrank Li * mdio interface in board design, and need to be configured by 2359793fc096SFrank Li * fec0 mii_bus. 2360793fc096SFrank Li */ 23613d125f9cSStefan Agner if ((fep->quirks & FEC_QUIRK_SINGLE_MDIO) && fep->dev_id > 0) { 2362793fc096SFrank Li /* fec1 uses fec0 mii_bus */ 2363793fc096SFrank Li if (mii_cnt && fec0_mii_bus) { 2364793fc096SFrank Li fep->mii_bus = fec0_mii_bus; 2365793fc096SFrank Li mii_cnt++; 2366793fc096SFrank Li return 0; 2367793fc096SFrank Li } 2368793fc096SFrank Li return -ENOENT; 2369793fc096SFrank Li } 2370793fc096SFrank Li 23713e782985SAndrew Lunn bus_freq = 2500000; /* 2.5MHz by default */ 23723e782985SAndrew Lunn node = of_get_child_by_name(pdev->dev.of_node, "mdio"); 23733c01eb62SAndrew Lunn if (node) { 23743e782985SAndrew Lunn of_property_read_u32(node, "clock-frequency", &bus_freq); 23753c01eb62SAndrew Lunn suppress_preamble = of_property_read_bool(node, 23763c01eb62SAndrew Lunn "suppress-preamble"); 23773c01eb62SAndrew Lunn } 23783e782985SAndrew Lunn 2379793fc096SFrank Li /* 23803e782985SAndrew Lunn * Set MII speed (= clk_get_rate() / 2 * phy_speed) 2381793fc096SFrank Li * 2382793fc096SFrank Li * The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while 2383793fc096SFrank Li * for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'. The i.MX28 2384793fc096SFrank Li * Reference Manual has an error on this, and gets fixed on i.MX6Q 2385793fc096SFrank Li * document. 2386793fc096SFrank Li */ 23873e782985SAndrew Lunn mii_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), bus_freq * 2); 23886b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_ENET_MAC) 238963c60732SUwe Kleine-König mii_speed--; 239063c60732SUwe Kleine-König if (mii_speed > 63) { 239163c60732SUwe Kleine-König dev_err(&pdev->dev, 2392981a0547SPeter Meerwald-Stadler "fec clock (%lu) too fast to get right mii speed\n", 239363c60732SUwe Kleine-König clk_get_rate(fep->clk_ipg)); 239463c60732SUwe Kleine-König err = -EINVAL; 239563c60732SUwe Kleine-König goto err_out; 239663c60732SUwe Kleine-König } 239763c60732SUwe Kleine-König 239863c60732SUwe Kleine-König /* 239963c60732SUwe Kleine-König * The i.MX28 and i.MX6 types have another filed in the MSCR (aka 240063c60732SUwe Kleine-König * MII_SPEED) register that defines the MDIO output hold time. Earlier 240163c60732SUwe Kleine-König * versions are RAZ there, so just ignore the difference and write the 240263c60732SUwe Kleine-König * register always. 240363c60732SUwe Kleine-König * The minimal hold time according to IEE802.3 (clause 22) is 10 ns. 240463c60732SUwe Kleine-König * HOLDTIME + 1 is the number of clk cycles the fec is holding the 240563c60732SUwe Kleine-König * output. 240663c60732SUwe Kleine-König * The HOLDTIME bitfield takes values between 0 and 7 (inclusive). 240763c60732SUwe Kleine-König * Given that ceil(clkrate / 5000000) <= 64, the calculation for 240863c60732SUwe Kleine-König * holdtime cannot result in a value greater than 3. 240963c60732SUwe Kleine-König */ 241063c60732SUwe Kleine-König holdtime = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 100000000) - 1; 241163c60732SUwe Kleine-König 241263c60732SUwe Kleine-König fep->phy_speed = mii_speed << 1 | holdtime << 8; 241363c60732SUwe Kleine-König 24143c01eb62SAndrew Lunn if (suppress_preamble) 24153c01eb62SAndrew Lunn fep->phy_speed |= BIT(7); 24163c01eb62SAndrew Lunn 24171e6114f5SGreg Ungerer if (fep->quirks & FEC_QUIRK_CLEAR_SETUP_MII) { 2418f166f890SAndrew Lunn /* Clear MMFR to avoid to generate MII event by writing MSCR. 2419f166f890SAndrew Lunn * MII event generation condition: 2420f166f890SAndrew Lunn * - writing MSCR: 2421f166f890SAndrew Lunn * - mmfr[31:0]_not_zero & mscr[7:0]_is_zero & 2422f166f890SAndrew Lunn * mscr_reg_data_in[7:0] != 0 2423f166f890SAndrew Lunn * - writing MMFR: 2424f166f890SAndrew Lunn * - mscr[7:0]_not_zero 2425f166f890SAndrew Lunn */ 2426f166f890SAndrew Lunn writel(0, fep->hwp + FEC_MII_DATA); 24271e6114f5SGreg Ungerer } 2428f166f890SAndrew Lunn 2429793fc096SFrank Li writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 2430793fc096SFrank Li 2431f166f890SAndrew Lunn /* Clear any pending transaction complete indication */ 2432f166f890SAndrew Lunn writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT); 2433f166f890SAndrew Lunn 2434793fc096SFrank Li fep->mii_bus = mdiobus_alloc(); 2435793fc096SFrank Li if (fep->mii_bus == NULL) { 2436793fc096SFrank Li err = -ENOMEM; 2437793fc096SFrank Li goto err_out; 2438793fc096SFrank Li } 2439793fc096SFrank Li 2440793fc096SFrank Li fep->mii_bus->name = "fec_enet_mii_bus"; 24418d03ad1aSAndrew Lunn fep->mii_bus->read = fec_enet_mdio_read_c22; 24428d03ad1aSAndrew Lunn fep->mii_bus->write = fec_enet_mdio_write_c22; 2443abc33494SGreg Ungerer if (fep->quirks & FEC_QUIRK_HAS_MDIO_C45) { 24448d03ad1aSAndrew Lunn fep->mii_bus->read_c45 = fec_enet_mdio_read_c45; 24458d03ad1aSAndrew Lunn fep->mii_bus->write_c45 = fec_enet_mdio_write_c45; 2446abc33494SGreg Ungerer } 2447793fc096SFrank Li snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", 2448793fc096SFrank Li pdev->name, fep->dev_id + 1); 2449793fc096SFrank Li fep->mii_bus->priv = fep; 2450793fc096SFrank Li fep->mii_bus->parent = &pdev->dev; 2451793fc096SFrank Li 2452407066f8SUwe Kleine-König err = of_mdiobus_register(fep->mii_bus, node); 2453407066f8SUwe Kleine-König if (err) 2454e7f4dc35SAndrew Lunn goto err_out_free_mdiobus; 24550607a2cdSPan Bian of_node_put(node); 2456793fc096SFrank Li 2457793fc096SFrank Li mii_cnt++; 2458793fc096SFrank Li 2459793fc096SFrank Li /* save fec0 mii_bus */ 24603d125f9cSStefan Agner if (fep->quirks & FEC_QUIRK_SINGLE_MDIO) 2461793fc096SFrank Li fec0_mii_bus = fep->mii_bus; 2462793fc096SFrank Li 2463793fc096SFrank Li return 0; 2464793fc096SFrank Li 2465793fc096SFrank Li err_out_free_mdiobus: 2466793fc096SFrank Li mdiobus_free(fep->mii_bus); 2467793fc096SFrank Li err_out: 24680607a2cdSPan Bian of_node_put(node); 2469793fc096SFrank Li return err; 2470793fc096SFrank Li } 2471793fc096SFrank Li 2472793fc096SFrank Li static void fec_enet_mii_remove(struct fec_enet_private *fep) 2473793fc096SFrank Li { 2474793fc096SFrank Li if (--mii_cnt == 0) { 2475793fc096SFrank Li mdiobus_unregister(fep->mii_bus); 2476793fc096SFrank Li mdiobus_free(fep->mii_bus); 2477793fc096SFrank Li } 2478793fc096SFrank Li } 2479793fc096SFrank Li 2480793fc096SFrank Li static void fec_enet_get_drvinfo(struct net_device *ndev, 2481793fc096SFrank Li struct ethtool_drvinfo *info) 2482793fc096SFrank Li { 2483793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2484793fc096SFrank Li 2485f029c781SWolfram Sang strscpy(info->driver, fep->pdev->dev.driver->name, 2486793fc096SFrank Li sizeof(info->driver)); 2487f029c781SWolfram Sang strscpy(info->bus_info, dev_name(&ndev->dev), sizeof(info->bus_info)); 2488793fc096SFrank Li } 2489793fc096SFrank Li 2490db65f35fSPhilippe Reynes static int fec_enet_get_regs_len(struct net_device *ndev) 2491db65f35fSPhilippe Reynes { 2492db65f35fSPhilippe Reynes struct fec_enet_private *fep = netdev_priv(ndev); 2493db65f35fSPhilippe Reynes struct resource *r; 2494db65f35fSPhilippe Reynes int s = 0; 2495db65f35fSPhilippe Reynes 2496db65f35fSPhilippe Reynes r = platform_get_resource(fep->pdev, IORESOURCE_MEM, 0); 2497db65f35fSPhilippe Reynes if (r) 2498db65f35fSPhilippe Reynes s = resource_size(r); 2499db65f35fSPhilippe Reynes 2500db65f35fSPhilippe Reynes return s; 2501db65f35fSPhilippe Reynes } 2502db65f35fSPhilippe Reynes 2503db65f35fSPhilippe Reynes /* List of registers that can be safety be read to dump them with ethtool */ 2504db65f35fSPhilippe Reynes #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ 25053f1dcc6aSLucas Stach defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ 250678cc6e7eSFlorian Fainelli defined(CONFIG_ARM64) || defined(CONFIG_COMPILE_TEST) 2507f9bcc9f3SVivien Didelot static __u32 fec_enet_register_version = 2; 2508db65f35fSPhilippe Reynes static u32 fec_enet_register_offset[] = { 2509db65f35fSPhilippe Reynes FEC_IEVENT, FEC_IMASK, FEC_R_DES_ACTIVE_0, FEC_X_DES_ACTIVE_0, 2510db65f35fSPhilippe Reynes FEC_ECNTRL, FEC_MII_DATA, FEC_MII_SPEED, FEC_MIB_CTRLSTAT, FEC_R_CNTRL, 2511db65f35fSPhilippe Reynes FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, FEC_OPD, FEC_TXIC0, FEC_TXIC1, 2512db65f35fSPhilippe Reynes FEC_TXIC2, FEC_RXIC0, FEC_RXIC1, FEC_RXIC2, FEC_HASH_TABLE_HIGH, 2513db65f35fSPhilippe Reynes FEC_HASH_TABLE_LOW, FEC_GRP_HASH_TABLE_HIGH, FEC_GRP_HASH_TABLE_LOW, 2514db65f35fSPhilippe Reynes FEC_X_WMRK, FEC_R_BOUND, FEC_R_FSTART, FEC_R_DES_START_1, 2515db65f35fSPhilippe Reynes FEC_X_DES_START_1, FEC_R_BUFF_SIZE_1, FEC_R_DES_START_2, 2516db65f35fSPhilippe Reynes FEC_X_DES_START_2, FEC_R_BUFF_SIZE_2, FEC_R_DES_START_0, 2517db65f35fSPhilippe Reynes FEC_X_DES_START_0, FEC_R_BUFF_SIZE_0, FEC_R_FIFO_RSFL, FEC_R_FIFO_RSEM, 2518db65f35fSPhilippe Reynes FEC_R_FIFO_RAEM, FEC_R_FIFO_RAFL, FEC_RACC, FEC_RCMR_1, FEC_RCMR_2, 2519db65f35fSPhilippe Reynes FEC_DMA_CFG_1, FEC_DMA_CFG_2, FEC_R_DES_ACTIVE_1, FEC_X_DES_ACTIVE_1, 2520db65f35fSPhilippe Reynes FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_2, FEC_QOS_SCHEME, 2521db65f35fSPhilippe Reynes RMON_T_DROP, RMON_T_PACKETS, RMON_T_BC_PKT, RMON_T_MC_PKT, 2522db65f35fSPhilippe Reynes RMON_T_CRC_ALIGN, RMON_T_UNDERSIZE, RMON_T_OVERSIZE, RMON_T_FRAG, 2523db65f35fSPhilippe Reynes RMON_T_JAB, RMON_T_COL, RMON_T_P64, RMON_T_P65TO127, RMON_T_P128TO255, 2524db65f35fSPhilippe Reynes RMON_T_P256TO511, RMON_T_P512TO1023, RMON_T_P1024TO2047, 2525db65f35fSPhilippe Reynes RMON_T_P_GTE2048, RMON_T_OCTETS, 2526db65f35fSPhilippe Reynes IEEE_T_DROP, IEEE_T_FRAME_OK, IEEE_T_1COL, IEEE_T_MCOL, IEEE_T_DEF, 2527db65f35fSPhilippe Reynes IEEE_T_LCOL, IEEE_T_EXCOL, IEEE_T_MACERR, IEEE_T_CSERR, IEEE_T_SQE, 2528db65f35fSPhilippe Reynes IEEE_T_FDXFC, IEEE_T_OCTETS_OK, 2529db65f35fSPhilippe Reynes RMON_R_PACKETS, RMON_R_BC_PKT, RMON_R_MC_PKT, RMON_R_CRC_ALIGN, 2530db65f35fSPhilippe Reynes RMON_R_UNDERSIZE, RMON_R_OVERSIZE, RMON_R_FRAG, RMON_R_JAB, 2531db65f35fSPhilippe Reynes RMON_R_RESVD_O, RMON_R_P64, RMON_R_P65TO127, RMON_R_P128TO255, 2532db65f35fSPhilippe Reynes RMON_R_P256TO511, RMON_R_P512TO1023, RMON_R_P1024TO2047, 2533db65f35fSPhilippe Reynes RMON_R_P_GTE2048, RMON_R_OCTETS, 2534db65f35fSPhilippe Reynes IEEE_R_DROP, IEEE_R_FRAME_OK, IEEE_R_CRC, IEEE_R_ALIGN, IEEE_R_MACERR, 2535db65f35fSPhilippe Reynes IEEE_R_FDXFC, IEEE_R_OCTETS_OK 2536db65f35fSPhilippe Reynes }; 25370a8b43b1SJuergen Borleis /* for i.MX6ul */ 25380a8b43b1SJuergen Borleis static u32 fec_enet_register_offset_6ul[] = { 25390a8b43b1SJuergen Borleis FEC_IEVENT, FEC_IMASK, FEC_R_DES_ACTIVE_0, FEC_X_DES_ACTIVE_0, 25400a8b43b1SJuergen Borleis FEC_ECNTRL, FEC_MII_DATA, FEC_MII_SPEED, FEC_MIB_CTRLSTAT, FEC_R_CNTRL, 25410a8b43b1SJuergen Borleis FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, FEC_OPD, FEC_TXIC0, FEC_RXIC0, 25420a8b43b1SJuergen Borleis FEC_HASH_TABLE_HIGH, FEC_HASH_TABLE_LOW, FEC_GRP_HASH_TABLE_HIGH, 25430a8b43b1SJuergen Borleis FEC_GRP_HASH_TABLE_LOW, FEC_X_WMRK, FEC_R_DES_START_0, 25440a8b43b1SJuergen Borleis FEC_X_DES_START_0, FEC_R_BUFF_SIZE_0, FEC_R_FIFO_RSFL, FEC_R_FIFO_RSEM, 25450a8b43b1SJuergen Borleis FEC_R_FIFO_RAEM, FEC_R_FIFO_RAFL, FEC_RACC, 25460a8b43b1SJuergen Borleis RMON_T_DROP, RMON_T_PACKETS, RMON_T_BC_PKT, RMON_T_MC_PKT, 25470a8b43b1SJuergen Borleis RMON_T_CRC_ALIGN, RMON_T_UNDERSIZE, RMON_T_OVERSIZE, RMON_T_FRAG, 25480a8b43b1SJuergen Borleis RMON_T_JAB, RMON_T_COL, RMON_T_P64, RMON_T_P65TO127, RMON_T_P128TO255, 25490a8b43b1SJuergen Borleis RMON_T_P256TO511, RMON_T_P512TO1023, RMON_T_P1024TO2047, 25500a8b43b1SJuergen Borleis RMON_T_P_GTE2048, RMON_T_OCTETS, 25510a8b43b1SJuergen Borleis IEEE_T_DROP, IEEE_T_FRAME_OK, IEEE_T_1COL, IEEE_T_MCOL, IEEE_T_DEF, 25520a8b43b1SJuergen Borleis IEEE_T_LCOL, IEEE_T_EXCOL, IEEE_T_MACERR, IEEE_T_CSERR, IEEE_T_SQE, 25530a8b43b1SJuergen Borleis IEEE_T_FDXFC, IEEE_T_OCTETS_OK, 25540a8b43b1SJuergen Borleis RMON_R_PACKETS, RMON_R_BC_PKT, RMON_R_MC_PKT, RMON_R_CRC_ALIGN, 25550a8b43b1SJuergen Borleis RMON_R_UNDERSIZE, RMON_R_OVERSIZE, RMON_R_FRAG, RMON_R_JAB, 25560a8b43b1SJuergen Borleis RMON_R_RESVD_O, RMON_R_P64, RMON_R_P65TO127, RMON_R_P128TO255, 25570a8b43b1SJuergen Borleis RMON_R_P256TO511, RMON_R_P512TO1023, RMON_R_P1024TO2047, 25580a8b43b1SJuergen Borleis RMON_R_P_GTE2048, RMON_R_OCTETS, 25590a8b43b1SJuergen Borleis IEEE_R_DROP, IEEE_R_FRAME_OK, IEEE_R_CRC, IEEE_R_ALIGN, IEEE_R_MACERR, 25600a8b43b1SJuergen Borleis IEEE_R_FDXFC, IEEE_R_OCTETS_OK 25610a8b43b1SJuergen Borleis }; 2562db65f35fSPhilippe Reynes #else 2563f9bcc9f3SVivien Didelot static __u32 fec_enet_register_version = 1; 2564db65f35fSPhilippe Reynes static u32 fec_enet_register_offset[] = { 2565db65f35fSPhilippe Reynes FEC_ECNTRL, FEC_IEVENT, FEC_IMASK, FEC_IVEC, FEC_R_DES_ACTIVE_0, 2566db65f35fSPhilippe Reynes FEC_R_DES_ACTIVE_1, FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_0, 2567db65f35fSPhilippe Reynes FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2, FEC_MII_DATA, FEC_MII_SPEED, 2568db65f35fSPhilippe Reynes FEC_R_BOUND, FEC_R_FSTART, FEC_X_WMRK, FEC_X_FSTART, FEC_R_CNTRL, 2569db65f35fSPhilippe Reynes FEC_MAX_FRM_LEN, FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, 2570db65f35fSPhilippe Reynes FEC_GRP_HASH_TABLE_HIGH, FEC_GRP_HASH_TABLE_LOW, FEC_R_DES_START_0, 2571db65f35fSPhilippe Reynes FEC_R_DES_START_1, FEC_R_DES_START_2, FEC_X_DES_START_0, 2572db65f35fSPhilippe Reynes FEC_X_DES_START_1, FEC_X_DES_START_2, FEC_R_BUFF_SIZE_0, 2573db65f35fSPhilippe Reynes FEC_R_BUFF_SIZE_1, FEC_R_BUFF_SIZE_2 2574db65f35fSPhilippe Reynes }; 2575db65f35fSPhilippe Reynes #endif 2576db65f35fSPhilippe Reynes 2577db65f35fSPhilippe Reynes static void fec_enet_get_regs(struct net_device *ndev, 2578db65f35fSPhilippe Reynes struct ethtool_regs *regs, void *regbuf) 2579db65f35fSPhilippe Reynes { 2580db65f35fSPhilippe Reynes struct fec_enet_private *fep = netdev_priv(ndev); 2581db65f35fSPhilippe Reynes u32 __iomem *theregs = (u32 __iomem *)fep->hwp; 2582c72a0bc0SAndrew Lunn struct device *dev = &fep->pdev->dev; 2583db65f35fSPhilippe Reynes u32 *buf = (u32 *)regbuf; 2584db65f35fSPhilippe Reynes u32 i, off; 2585c72a0bc0SAndrew Lunn int ret; 25860a8b43b1SJuergen Borleis #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ 25870a8b43b1SJuergen Borleis defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ 25880a8b43b1SJuergen Borleis defined(CONFIG_ARM64) || defined(CONFIG_COMPILE_TEST) 25890a8b43b1SJuergen Borleis u32 *reg_list; 25900a8b43b1SJuergen Borleis u32 reg_cnt; 2591c72a0bc0SAndrew Lunn 25920a8b43b1SJuergen Borleis if (!of_machine_is_compatible("fsl,imx6ul")) { 25930a8b43b1SJuergen Borleis reg_list = fec_enet_register_offset; 25940a8b43b1SJuergen Borleis reg_cnt = ARRAY_SIZE(fec_enet_register_offset); 25950a8b43b1SJuergen Borleis } else { 25960a8b43b1SJuergen Borleis reg_list = fec_enet_register_offset_6ul; 25970a8b43b1SJuergen Borleis reg_cnt = ARRAY_SIZE(fec_enet_register_offset_6ul); 25980a8b43b1SJuergen Borleis } 25990a8b43b1SJuergen Borleis #else 26000a8b43b1SJuergen Borleis /* coldfire */ 26010a8b43b1SJuergen Borleis static u32 *reg_list = fec_enet_register_offset; 26020a8b43b1SJuergen Borleis static const u32 reg_cnt = ARRAY_SIZE(fec_enet_register_offset); 26030a8b43b1SJuergen Borleis #endif 2604da875fa5SZhang Qilong ret = pm_runtime_resume_and_get(dev); 2605c72a0bc0SAndrew Lunn if (ret < 0) 2606c72a0bc0SAndrew Lunn return; 2607db65f35fSPhilippe Reynes 2608f9bcc9f3SVivien Didelot regs->version = fec_enet_register_version; 2609f9bcc9f3SVivien Didelot 2610db65f35fSPhilippe Reynes memset(buf, 0, regs->len); 2611db65f35fSPhilippe Reynes 26120a8b43b1SJuergen Borleis for (i = 0; i < reg_cnt; i++) { 26130a8b43b1SJuergen Borleis off = reg_list[i]; 2614ec20a63aSFugang Duan 2615ec20a63aSFugang Duan if ((off == FEC_R_BOUND || off == FEC_R_FSTART) && 2616ec20a63aSFugang Duan !(fep->quirks & FEC_QUIRK_HAS_FRREG)) 2617ec20a63aSFugang Duan continue; 2618ec20a63aSFugang Duan 2619ec20a63aSFugang Duan off >>= 2; 2620db65f35fSPhilippe Reynes buf[off] = readl(&theregs[off]); 2621db65f35fSPhilippe Reynes } 2622c72a0bc0SAndrew Lunn 2623c72a0bc0SAndrew Lunn pm_runtime_mark_last_busy(dev); 2624c72a0bc0SAndrew Lunn pm_runtime_put_autosuspend(dev); 2625db65f35fSPhilippe Reynes } 2626db65f35fSPhilippe Reynes 2627793fc096SFrank Li static int fec_enet_get_ts_info(struct net_device *ndev, 2628793fc096SFrank Li struct ethtool_ts_info *info) 2629793fc096SFrank Li { 2630793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2631793fc096SFrank Li 2632793fc096SFrank Li if (fep->bufdesc_ex) { 2633793fc096SFrank Li 2634793fc096SFrank Li info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | 2635793fc096SFrank Li SOF_TIMESTAMPING_RX_SOFTWARE | 2636793fc096SFrank Li SOF_TIMESTAMPING_SOFTWARE | 2637793fc096SFrank Li SOF_TIMESTAMPING_TX_HARDWARE | 2638793fc096SFrank Li SOF_TIMESTAMPING_RX_HARDWARE | 2639793fc096SFrank Li SOF_TIMESTAMPING_RAW_HARDWARE; 2640793fc096SFrank Li if (fep->ptp_clock) 2641793fc096SFrank Li info->phc_index = ptp_clock_index(fep->ptp_clock); 2642793fc096SFrank Li else 2643793fc096SFrank Li info->phc_index = -1; 2644793fc096SFrank Li 2645793fc096SFrank Li info->tx_types = (1 << HWTSTAMP_TX_OFF) | 2646793fc096SFrank Li (1 << HWTSTAMP_TX_ON); 2647793fc096SFrank Li 2648793fc096SFrank Li info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | 2649793fc096SFrank Li (1 << HWTSTAMP_FILTER_ALL); 2650793fc096SFrank Li return 0; 2651793fc096SFrank Li } else { 2652793fc096SFrank Li return ethtool_op_get_ts_info(ndev, info); 2653793fc096SFrank Li } 2654793fc096SFrank Li } 2655793fc096SFrank Li 2656d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 2657d1391930SGuenter Roeck 2658793fc096SFrank Li static void fec_enet_get_pauseparam(struct net_device *ndev, 2659793fc096SFrank Li struct ethtool_pauseparam *pause) 2660793fc096SFrank Li { 2661793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2662793fc096SFrank Li 2663793fc096SFrank Li pause->autoneg = (fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) != 0; 2664793fc096SFrank Li pause->tx_pause = (fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) != 0; 2665793fc096SFrank Li pause->rx_pause = pause->tx_pause; 2666793fc096SFrank Li } 2667793fc096SFrank Li 2668793fc096SFrank Li static int fec_enet_set_pauseparam(struct net_device *ndev, 2669793fc096SFrank Li struct ethtool_pauseparam *pause) 2670793fc096SFrank Li { 2671793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2672793fc096SFrank Li 267345f5c327SPhilippe Reynes if (!ndev->phydev) 26740b146ca8SRussell King return -ENODEV; 26750b146ca8SRussell King 2676793fc096SFrank Li if (pause->tx_pause != pause->rx_pause) { 2677793fc096SFrank Li netdev_info(ndev, 2678793fc096SFrank Li "hardware only support enable/disable both tx and rx"); 2679793fc096SFrank Li return -EINVAL; 2680793fc096SFrank Li } 2681793fc096SFrank Li 2682793fc096SFrank Li fep->pause_flag = 0; 2683793fc096SFrank Li 2684793fc096SFrank Li /* tx pause must be same as rx pause */ 2685793fc096SFrank Li fep->pause_flag |= pause->rx_pause ? FEC_PAUSE_FLAG_ENABLE : 0; 2686793fc096SFrank Li fep->pause_flag |= pause->autoneg ? FEC_PAUSE_FLAG_AUTONEG : 0; 2687793fc096SFrank Li 26880c122405SAndrew Lunn phy_set_sym_pause(ndev->phydev, pause->rx_pause, pause->tx_pause, 26890c122405SAndrew Lunn pause->autoneg); 2690793fc096SFrank Li 2691793fc096SFrank Li if (pause->autoneg) { 2692793fc096SFrank Li if (netif_running(ndev)) 2693793fc096SFrank Li fec_stop(ndev); 269445f5c327SPhilippe Reynes phy_start_aneg(ndev->phydev); 2695793fc096SFrank Li } 2696dbc64a8eSRussell King if (netif_running(ndev)) { 2697dbc64a8eSRussell King napi_disable(&fep->napi); 2698dbc64a8eSRussell King netif_tx_lock_bh(ndev); 2699ef83337dSRussell King fec_restart(ndev); 2700657ade07SRickard x Andersson netif_tx_wake_all_queues(ndev); 27016af42d42SRussell King netif_tx_unlock_bh(ndev); 2702dbc64a8eSRussell King napi_enable(&fep->napi); 2703dbc64a8eSRussell King } 2704793fc096SFrank Li 2705793fc096SFrank Li return 0; 2706793fc096SFrank Li } 2707793fc096SFrank Li 270838ae92dcSChris Healy static const struct fec_stat { 270938ae92dcSChris Healy char name[ETH_GSTRING_LEN]; 271038ae92dcSChris Healy u16 offset; 271138ae92dcSChris Healy } fec_stats[] = { 271238ae92dcSChris Healy /* RMON TX */ 271338ae92dcSChris Healy { "tx_dropped", RMON_T_DROP }, 271438ae92dcSChris Healy { "tx_packets", RMON_T_PACKETS }, 271538ae92dcSChris Healy { "tx_broadcast", RMON_T_BC_PKT }, 271638ae92dcSChris Healy { "tx_multicast", RMON_T_MC_PKT }, 271738ae92dcSChris Healy { "tx_crc_errors", RMON_T_CRC_ALIGN }, 271838ae92dcSChris Healy { "tx_undersize", RMON_T_UNDERSIZE }, 271938ae92dcSChris Healy { "tx_oversize", RMON_T_OVERSIZE }, 272038ae92dcSChris Healy { "tx_fragment", RMON_T_FRAG }, 272138ae92dcSChris Healy { "tx_jabber", RMON_T_JAB }, 272238ae92dcSChris Healy { "tx_collision", RMON_T_COL }, 272338ae92dcSChris Healy { "tx_64byte", RMON_T_P64 }, 272438ae92dcSChris Healy { "tx_65to127byte", RMON_T_P65TO127 }, 272538ae92dcSChris Healy { "tx_128to255byte", RMON_T_P128TO255 }, 272638ae92dcSChris Healy { "tx_256to511byte", RMON_T_P256TO511 }, 272738ae92dcSChris Healy { "tx_512to1023byte", RMON_T_P512TO1023 }, 272838ae92dcSChris Healy { "tx_1024to2047byte", RMON_T_P1024TO2047 }, 272938ae92dcSChris Healy { "tx_GTE2048byte", RMON_T_P_GTE2048 }, 273038ae92dcSChris Healy { "tx_octets", RMON_T_OCTETS }, 273138ae92dcSChris Healy 273238ae92dcSChris Healy /* IEEE TX */ 273338ae92dcSChris Healy { "IEEE_tx_drop", IEEE_T_DROP }, 273438ae92dcSChris Healy { "IEEE_tx_frame_ok", IEEE_T_FRAME_OK }, 273538ae92dcSChris Healy { "IEEE_tx_1col", IEEE_T_1COL }, 273638ae92dcSChris Healy { "IEEE_tx_mcol", IEEE_T_MCOL }, 273738ae92dcSChris Healy { "IEEE_tx_def", IEEE_T_DEF }, 273838ae92dcSChris Healy { "IEEE_tx_lcol", IEEE_T_LCOL }, 273938ae92dcSChris Healy { "IEEE_tx_excol", IEEE_T_EXCOL }, 274038ae92dcSChris Healy { "IEEE_tx_macerr", IEEE_T_MACERR }, 274138ae92dcSChris Healy { "IEEE_tx_cserr", IEEE_T_CSERR }, 274238ae92dcSChris Healy { "IEEE_tx_sqe", IEEE_T_SQE }, 274338ae92dcSChris Healy { "IEEE_tx_fdxfc", IEEE_T_FDXFC }, 274438ae92dcSChris Healy { "IEEE_tx_octets_ok", IEEE_T_OCTETS_OK }, 274538ae92dcSChris Healy 274638ae92dcSChris Healy /* RMON RX */ 274738ae92dcSChris Healy { "rx_packets", RMON_R_PACKETS }, 274838ae92dcSChris Healy { "rx_broadcast", RMON_R_BC_PKT }, 274938ae92dcSChris Healy { "rx_multicast", RMON_R_MC_PKT }, 275038ae92dcSChris Healy { "rx_crc_errors", RMON_R_CRC_ALIGN }, 275138ae92dcSChris Healy { "rx_undersize", RMON_R_UNDERSIZE }, 275238ae92dcSChris Healy { "rx_oversize", RMON_R_OVERSIZE }, 275338ae92dcSChris Healy { "rx_fragment", RMON_R_FRAG }, 275438ae92dcSChris Healy { "rx_jabber", RMON_R_JAB }, 275538ae92dcSChris Healy { "rx_64byte", RMON_R_P64 }, 275638ae92dcSChris Healy { "rx_65to127byte", RMON_R_P65TO127 }, 275738ae92dcSChris Healy { "rx_128to255byte", RMON_R_P128TO255 }, 275838ae92dcSChris Healy { "rx_256to511byte", RMON_R_P256TO511 }, 275938ae92dcSChris Healy { "rx_512to1023byte", RMON_R_P512TO1023 }, 276038ae92dcSChris Healy { "rx_1024to2047byte", RMON_R_P1024TO2047 }, 276138ae92dcSChris Healy { "rx_GTE2048byte", RMON_R_P_GTE2048 }, 276238ae92dcSChris Healy { "rx_octets", RMON_R_OCTETS }, 276338ae92dcSChris Healy 276438ae92dcSChris Healy /* IEEE RX */ 276538ae92dcSChris Healy { "IEEE_rx_drop", IEEE_R_DROP }, 276638ae92dcSChris Healy { "IEEE_rx_frame_ok", IEEE_R_FRAME_OK }, 276738ae92dcSChris Healy { "IEEE_rx_crc", IEEE_R_CRC }, 276838ae92dcSChris Healy { "IEEE_rx_align", IEEE_R_ALIGN }, 276938ae92dcSChris Healy { "IEEE_rx_macerr", IEEE_R_MACERR }, 277038ae92dcSChris Healy { "IEEE_rx_fdxfc", IEEE_R_FDXFC }, 277138ae92dcSChris Healy { "IEEE_rx_octets_ok", IEEE_R_OCTETS_OK }, 277238ae92dcSChris Healy }; 277338ae92dcSChris Healy 2774f85de666SNikita Yushchenko #define FEC_STATS_SIZE (ARRAY_SIZE(fec_stats) * sizeof(u64)) 2775f85de666SNikita Yushchenko 27766970ef27SShenwei Wang static const char *fec_xdp_stat_strs[XDP_STATS_TOTAL] = { 27776970ef27SShenwei Wang "rx_xdp_redirect", /* RX_XDP_REDIRECT = 0, */ 27786970ef27SShenwei Wang "rx_xdp_pass", /* RX_XDP_PASS, */ 27796970ef27SShenwei Wang "rx_xdp_drop", /* RX_XDP_DROP, */ 27806970ef27SShenwei Wang "rx_xdp_tx", /* RX_XDP_TX, */ 27816970ef27SShenwei Wang "rx_xdp_tx_errors", /* RX_XDP_TX_ERRORS, */ 27826970ef27SShenwei Wang "tx_xdp_xmit", /* TX_XDP_XMIT, */ 27836970ef27SShenwei Wang "tx_xdp_xmit_errors", /* TX_XDP_XMIT_ERRORS, */ 27846970ef27SShenwei Wang }; 27856970ef27SShenwei Wang 278680cca775SNikita Yushchenko static void fec_enet_update_ethtool_stats(struct net_device *dev) 278738ae92dcSChris Healy { 278838ae92dcSChris Healy struct fec_enet_private *fep = netdev_priv(dev); 278938ae92dcSChris Healy int i; 279038ae92dcSChris Healy 279138ae92dcSChris Healy for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 279280cca775SNikita Yushchenko fep->ethtool_stats[i] = readl(fep->hwp + fec_stats[i].offset); 279380cca775SNikita Yushchenko } 279480cca775SNikita Yushchenko 27956970ef27SShenwei Wang static void fec_enet_get_xdp_stats(struct fec_enet_private *fep, u64 *data) 27966970ef27SShenwei Wang { 27976970ef27SShenwei Wang u64 xdp_stats[XDP_STATS_TOTAL] = { 0 }; 27986970ef27SShenwei Wang struct fec_enet_priv_rx_q *rxq; 27996970ef27SShenwei Wang int i, j; 28006970ef27SShenwei Wang 28016970ef27SShenwei Wang for (i = fep->num_rx_queues - 1; i >= 0; i--) { 28026970ef27SShenwei Wang rxq = fep->rx_queue[i]; 28036970ef27SShenwei Wang 28046970ef27SShenwei Wang for (j = 0; j < XDP_STATS_TOTAL; j++) 28056970ef27SShenwei Wang xdp_stats[j] += rxq->stats[j]; 28066970ef27SShenwei Wang } 28076970ef27SShenwei Wang 28086970ef27SShenwei Wang memcpy(data, xdp_stats, sizeof(xdp_stats)); 28096970ef27SShenwei Wang } 28106970ef27SShenwei Wang 28116970ef27SShenwei Wang static void fec_enet_page_pool_stats(struct fec_enet_private *fep, u64 *data) 28126970ef27SShenwei Wang { 28136970ef27SShenwei Wang struct page_pool_stats stats = {}; 28146970ef27SShenwei Wang struct fec_enet_priv_rx_q *rxq; 28156970ef27SShenwei Wang int i; 28166970ef27SShenwei Wang 28176970ef27SShenwei Wang for (i = fep->num_rx_queues - 1; i >= 0; i--) { 28186970ef27SShenwei Wang rxq = fep->rx_queue[i]; 28196970ef27SShenwei Wang 28206970ef27SShenwei Wang if (!rxq->page_pool) 28216970ef27SShenwei Wang continue; 28226970ef27SShenwei Wang 28236970ef27SShenwei Wang page_pool_get_stats(rxq->page_pool, &stats); 28246970ef27SShenwei Wang } 28256970ef27SShenwei Wang 28266970ef27SShenwei Wang page_pool_ethtool_stats_get(data, &stats); 28276970ef27SShenwei Wang } 28286970ef27SShenwei Wang 282980cca775SNikita Yushchenko static void fec_enet_get_ethtool_stats(struct net_device *dev, 283080cca775SNikita Yushchenko struct ethtool_stats *stats, u64 *data) 283180cca775SNikita Yushchenko { 283280cca775SNikita Yushchenko struct fec_enet_private *fep = netdev_priv(dev); 283380cca775SNikita Yushchenko 283480cca775SNikita Yushchenko if (netif_running(dev)) 283580cca775SNikita Yushchenko fec_enet_update_ethtool_stats(dev); 283680cca775SNikita Yushchenko 2837f85de666SNikita Yushchenko memcpy(data, fep->ethtool_stats, FEC_STATS_SIZE); 28386970ef27SShenwei Wang data += FEC_STATS_SIZE / sizeof(u64); 28396970ef27SShenwei Wang 28406970ef27SShenwei Wang fec_enet_get_xdp_stats(fep, data); 28416970ef27SShenwei Wang data += XDP_STATS_TOTAL; 28426970ef27SShenwei Wang 28436970ef27SShenwei Wang fec_enet_page_pool_stats(fep, data); 284438ae92dcSChris Healy } 284538ae92dcSChris Healy 284638ae92dcSChris Healy static void fec_enet_get_strings(struct net_device *netdev, 284738ae92dcSChris Healy u32 stringset, u8 *data) 284838ae92dcSChris Healy { 284938ae92dcSChris Healy int i; 285038ae92dcSChris Healy switch (stringset) { 285138ae92dcSChris Healy case ETH_SS_STATS: 28526970ef27SShenwei Wang for (i = 0; i < ARRAY_SIZE(fec_stats); i++) { 28536970ef27SShenwei Wang memcpy(data, fec_stats[i].name, ETH_GSTRING_LEN); 28546970ef27SShenwei Wang data += ETH_GSTRING_LEN; 28556970ef27SShenwei Wang } 28566970ef27SShenwei Wang for (i = 0; i < ARRAY_SIZE(fec_xdp_stat_strs); i++) { 28576970ef27SShenwei Wang strncpy(data, fec_xdp_stat_strs[i], ETH_GSTRING_LEN); 28586970ef27SShenwei Wang data += ETH_GSTRING_LEN; 28596970ef27SShenwei Wang } 28606970ef27SShenwei Wang page_pool_ethtool_stats_get_strings(data); 28616970ef27SShenwei Wang 286238ae92dcSChris Healy break; 28636016ba34SOleksij Rempel case ETH_SS_TEST: 28646016ba34SOleksij Rempel net_selftest_get_strings(data); 28656016ba34SOleksij Rempel break; 286638ae92dcSChris Healy } 286738ae92dcSChris Healy } 286838ae92dcSChris Healy 286938ae92dcSChris Healy static int fec_enet_get_sset_count(struct net_device *dev, int sset) 287038ae92dcSChris Healy { 28716970ef27SShenwei Wang int count; 28726970ef27SShenwei Wang 287338ae92dcSChris Healy switch (sset) { 287438ae92dcSChris Healy case ETH_SS_STATS: 28756970ef27SShenwei Wang count = ARRAY_SIZE(fec_stats) + XDP_STATS_TOTAL; 28766970ef27SShenwei Wang count += page_pool_ethtool_stats_get_count(); 28776970ef27SShenwei Wang return count; 28786970ef27SShenwei Wang 28796016ba34SOleksij Rempel case ETH_SS_TEST: 28806016ba34SOleksij Rempel return net_selftest_get_count(); 288138ae92dcSChris Healy default: 288238ae92dcSChris Healy return -EOPNOTSUPP; 288338ae92dcSChris Healy } 288438ae92dcSChris Healy } 2885f85de666SNikita Yushchenko 28862b30842bSAndrew Lunn static void fec_enet_clear_ethtool_stats(struct net_device *dev) 28872b30842bSAndrew Lunn { 28882b30842bSAndrew Lunn struct fec_enet_private *fep = netdev_priv(dev); 28896970ef27SShenwei Wang struct fec_enet_priv_rx_q *rxq; 28906970ef27SShenwei Wang int i, j; 28912b30842bSAndrew Lunn 28922b30842bSAndrew Lunn /* Disable MIB statistics counters */ 28932b30842bSAndrew Lunn writel(FEC_MIB_CTRLSTAT_DISABLE, fep->hwp + FEC_MIB_CTRLSTAT); 28942b30842bSAndrew Lunn 28952b30842bSAndrew Lunn for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 28962b30842bSAndrew Lunn writel(0, fep->hwp + fec_stats[i].offset); 28972b30842bSAndrew Lunn 28986970ef27SShenwei Wang for (i = fep->num_rx_queues - 1; i >= 0; i--) { 28996970ef27SShenwei Wang rxq = fep->rx_queue[i]; 29006970ef27SShenwei Wang for (j = 0; j < XDP_STATS_TOTAL; j++) 29016970ef27SShenwei Wang rxq->stats[j] = 0; 29026970ef27SShenwei Wang } 29036970ef27SShenwei Wang 29042b30842bSAndrew Lunn /* Don't disable MIB statistics counters */ 29052b30842bSAndrew Lunn writel(0, fep->hwp + FEC_MIB_CTRLSTAT); 29062b30842bSAndrew Lunn } 29072b30842bSAndrew Lunn 2908f85de666SNikita Yushchenko #else /* !defined(CONFIG_M5272) */ 2909f85de666SNikita Yushchenko #define FEC_STATS_SIZE 0 2910f85de666SNikita Yushchenko static inline void fec_enet_update_ethtool_stats(struct net_device *dev) 2911f85de666SNikita Yushchenko { 2912f85de666SNikita Yushchenko } 291341e8e404SFabio Estevam 291441e8e404SFabio Estevam static inline void fec_enet_clear_ethtool_stats(struct net_device *dev) 291541e8e404SFabio Estevam { 291641e8e404SFabio Estevam } 2917d1391930SGuenter Roeck #endif /* !defined(CONFIG_M5272) */ 291838ae92dcSChris Healy 2919d851b47bSFugang Duan /* ITR clock source is enet system clock (clk_ahb). 2920d851b47bSFugang Duan * TCTT unit is cycle_ns * 64 cycle 2921d851b47bSFugang Duan * So, the ICTT value = X us / (cycle_ns * 64) 2922d851b47bSFugang Duan */ 2923d851b47bSFugang Duan static int fec_enet_us_to_itr_clock(struct net_device *ndev, int us) 2924d851b47bSFugang Duan { 2925d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2926d851b47bSFugang Duan 2927d851b47bSFugang Duan return us * (fep->itr_clk_rate / 64000) / 1000; 2928d851b47bSFugang Duan } 2929d851b47bSFugang Duan 2930d851b47bSFugang Duan /* Set threshold for interrupt coalescing */ 2931d851b47bSFugang Duan static void fec_enet_itr_coal_set(struct net_device *ndev) 2932d851b47bSFugang Duan { 2933d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2934d851b47bSFugang Duan int rx_itr, tx_itr; 2935d851b47bSFugang Duan 2936d851b47bSFugang Duan /* Must be greater than zero to avoid unpredictable behavior */ 2937d851b47bSFugang Duan if (!fep->rx_time_itr || !fep->rx_pkts_itr || 2938d851b47bSFugang Duan !fep->tx_time_itr || !fep->tx_pkts_itr) 2939d851b47bSFugang Duan return; 2940d851b47bSFugang Duan 2941d851b47bSFugang Duan /* Select enet system clock as Interrupt Coalescing 2942d851b47bSFugang Duan * timer Clock Source 2943d851b47bSFugang Duan */ 2944d851b47bSFugang Duan rx_itr = FEC_ITR_CLK_SEL; 2945d851b47bSFugang Duan tx_itr = FEC_ITR_CLK_SEL; 2946d851b47bSFugang Duan 2947d851b47bSFugang Duan /* set ICFT and ICTT */ 2948d851b47bSFugang Duan rx_itr |= FEC_ITR_ICFT(fep->rx_pkts_itr); 2949d851b47bSFugang Duan rx_itr |= FEC_ITR_ICTT(fec_enet_us_to_itr_clock(ndev, fep->rx_time_itr)); 2950d851b47bSFugang Duan tx_itr |= FEC_ITR_ICFT(fep->tx_pkts_itr); 2951d851b47bSFugang Duan tx_itr |= FEC_ITR_ICTT(fec_enet_us_to_itr_clock(ndev, fep->tx_time_itr)); 2952d851b47bSFugang Duan 2953d851b47bSFugang Duan rx_itr |= FEC_ITR_EN; 2954d851b47bSFugang Duan tx_itr |= FEC_ITR_EN; 2955d851b47bSFugang Duan 2956d851b47bSFugang Duan writel(tx_itr, fep->hwp + FEC_TXIC0); 2957d851b47bSFugang Duan writel(rx_itr, fep->hwp + FEC_RXIC0); 2958471ff445SJoakim Zhang if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) { 2959d851b47bSFugang Duan writel(tx_itr, fep->hwp + FEC_TXIC1); 2960d851b47bSFugang Duan writel(rx_itr, fep->hwp + FEC_RXIC1); 2961d851b47bSFugang Duan writel(tx_itr, fep->hwp + FEC_TXIC2); 2962d851b47bSFugang Duan writel(rx_itr, fep->hwp + FEC_RXIC2); 2963d851b47bSFugang Duan } 2964ff7566b8SFugang Duan } 2965d851b47bSFugang Duan 2966f3ccfda1SYufeng Mo static int fec_enet_get_coalesce(struct net_device *ndev, 2967f3ccfda1SYufeng Mo struct ethtool_coalesce *ec, 2968f3ccfda1SYufeng Mo struct kernel_ethtool_coalesce *kernel_coal, 2969f3ccfda1SYufeng Mo struct netlink_ext_ack *extack) 2970d851b47bSFugang Duan { 2971d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2972d851b47bSFugang Duan 2973ff7566b8SFugang Duan if (!(fep->quirks & FEC_QUIRK_HAS_COALESCE)) 2974d851b47bSFugang Duan return -EOPNOTSUPP; 2975d851b47bSFugang Duan 2976d851b47bSFugang Duan ec->rx_coalesce_usecs = fep->rx_time_itr; 2977d851b47bSFugang Duan ec->rx_max_coalesced_frames = fep->rx_pkts_itr; 2978d851b47bSFugang Duan 2979d851b47bSFugang Duan ec->tx_coalesce_usecs = fep->tx_time_itr; 2980d851b47bSFugang Duan ec->tx_max_coalesced_frames = fep->tx_pkts_itr; 2981d851b47bSFugang Duan 2982d851b47bSFugang Duan return 0; 2983d851b47bSFugang Duan } 2984d851b47bSFugang Duan 2985f3ccfda1SYufeng Mo static int fec_enet_set_coalesce(struct net_device *ndev, 2986f3ccfda1SYufeng Mo struct ethtool_coalesce *ec, 2987f3ccfda1SYufeng Mo struct kernel_ethtool_coalesce *kernel_coal, 2988f3ccfda1SYufeng Mo struct netlink_ext_ack *extack) 2989d851b47bSFugang Duan { 2990d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2991517a772cSFabio Estevam struct device *dev = &fep->pdev->dev; 2992d851b47bSFugang Duan unsigned int cycle; 2993d851b47bSFugang Duan 2994ff7566b8SFugang Duan if (!(fep->quirks & FEC_QUIRK_HAS_COALESCE)) 2995d851b47bSFugang Duan return -EOPNOTSUPP; 2996d851b47bSFugang Duan 2997d851b47bSFugang Duan if (ec->rx_max_coalesced_frames > 255) { 2998517a772cSFabio Estevam dev_err(dev, "Rx coalesced frames exceed hardware limitation\n"); 2999d851b47bSFugang Duan return -EINVAL; 3000d851b47bSFugang Duan } 3001d851b47bSFugang Duan 3002d851b47bSFugang Duan if (ec->tx_max_coalesced_frames > 255) { 3003517a772cSFabio Estevam dev_err(dev, "Tx coalesced frame exceed hardware limitation\n"); 3004d851b47bSFugang Duan return -EINVAL; 3005d851b47bSFugang Duan } 3006d851b47bSFugang Duan 3007ab14961dSJakub Kicinski cycle = fec_enet_us_to_itr_clock(ndev, ec->rx_coalesce_usecs); 3008d851b47bSFugang Duan if (cycle > 0xFFFF) { 3009517a772cSFabio Estevam dev_err(dev, "Rx coalesced usec exceed hardware limitation\n"); 3010d851b47bSFugang Duan return -EINVAL; 3011d851b47bSFugang Duan } 3012d851b47bSFugang Duan 3013ab14961dSJakub Kicinski cycle = fec_enet_us_to_itr_clock(ndev, ec->tx_coalesce_usecs); 3014d851b47bSFugang Duan if (cycle > 0xFFFF) { 3015ab14961dSJakub Kicinski dev_err(dev, "Tx coalesced usec exceed hardware limitation\n"); 3016d851b47bSFugang Duan return -EINVAL; 3017d851b47bSFugang Duan } 3018d851b47bSFugang Duan 3019d851b47bSFugang Duan fep->rx_time_itr = ec->rx_coalesce_usecs; 3020d851b47bSFugang Duan fep->rx_pkts_itr = ec->rx_max_coalesced_frames; 3021d851b47bSFugang Duan 3022d851b47bSFugang Duan fep->tx_time_itr = ec->tx_coalesce_usecs; 3023d851b47bSFugang Duan fep->tx_pkts_itr = ec->tx_max_coalesced_frames; 3024d851b47bSFugang Duan 3025d851b47bSFugang Duan fec_enet_itr_coal_set(ndev); 3026d851b47bSFugang Duan 3027d851b47bSFugang Duan return 0; 3028d851b47bSFugang Duan } 3029d851b47bSFugang Duan 30301b7bde6dSNimrod Andy static int fec_enet_get_tunable(struct net_device *netdev, 30311b7bde6dSNimrod Andy const struct ethtool_tunable *tuna, 30321b7bde6dSNimrod Andy void *data) 30331b7bde6dSNimrod Andy { 30341b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(netdev); 30351b7bde6dSNimrod Andy int ret = 0; 30361b7bde6dSNimrod Andy 30371b7bde6dSNimrod Andy switch (tuna->id) { 30381b7bde6dSNimrod Andy case ETHTOOL_RX_COPYBREAK: 30391b7bde6dSNimrod Andy *(u32 *)data = fep->rx_copybreak; 30401b7bde6dSNimrod Andy break; 30411b7bde6dSNimrod Andy default: 30421b7bde6dSNimrod Andy ret = -EINVAL; 30431b7bde6dSNimrod Andy break; 30441b7bde6dSNimrod Andy } 30451b7bde6dSNimrod Andy 30461b7bde6dSNimrod Andy return ret; 30471b7bde6dSNimrod Andy } 30481b7bde6dSNimrod Andy 30491b7bde6dSNimrod Andy static int fec_enet_set_tunable(struct net_device *netdev, 30501b7bde6dSNimrod Andy const struct ethtool_tunable *tuna, 30511b7bde6dSNimrod Andy const void *data) 30521b7bde6dSNimrod Andy { 30531b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(netdev); 30541b7bde6dSNimrod Andy int ret = 0; 30551b7bde6dSNimrod Andy 30561b7bde6dSNimrod Andy switch (tuna->id) { 30571b7bde6dSNimrod Andy case ETHTOOL_RX_COPYBREAK: 30581b7bde6dSNimrod Andy fep->rx_copybreak = *(u32 *)data; 30591b7bde6dSNimrod Andy break; 30601b7bde6dSNimrod Andy default: 30611b7bde6dSNimrod Andy ret = -EINVAL; 30621b7bde6dSNimrod Andy break; 30631b7bde6dSNimrod Andy } 30641b7bde6dSNimrod Andy 30651b7bde6dSNimrod Andy return ret; 30661b7bde6dSNimrod Andy } 30671b7bde6dSNimrod Andy 3068b82f8c3fSFugang Duan /* LPI Sleep Ts count base on tx clk (clk_ref). 3069b82f8c3fSFugang Duan * The lpi sleep cnt value = X us / (cycle_ns). 3070b82f8c3fSFugang Duan */ 3071b82f8c3fSFugang Duan static int fec_enet_us_to_tx_cycle(struct net_device *ndev, int us) 3072b82f8c3fSFugang Duan { 3073b82f8c3fSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 3074b82f8c3fSFugang Duan 3075b82f8c3fSFugang Duan return us * (fep->clk_ref_rate / 1000) / 1000; 3076b82f8c3fSFugang Duan } 3077b82f8c3fSFugang Duan 3078b82f8c3fSFugang Duan static int fec_enet_eee_mode_set(struct net_device *ndev, bool enable) 3079b82f8c3fSFugang Duan { 3080b82f8c3fSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 3081b82f8c3fSFugang Duan struct ethtool_eee *p = &fep->eee; 3082b82f8c3fSFugang Duan unsigned int sleep_cycle, wake_cycle; 3083b82f8c3fSFugang Duan int ret = 0; 3084b82f8c3fSFugang Duan 3085b82f8c3fSFugang Duan if (enable) { 308653243d41SJisheng Zhang ret = phy_init_eee(ndev->phydev, false); 3087b82f8c3fSFugang Duan if (ret) 3088b82f8c3fSFugang Duan return ret; 3089b82f8c3fSFugang Duan 3090b82f8c3fSFugang Duan sleep_cycle = fec_enet_us_to_tx_cycle(ndev, p->tx_lpi_timer); 3091b82f8c3fSFugang Duan wake_cycle = sleep_cycle; 3092b82f8c3fSFugang Duan } else { 3093b82f8c3fSFugang Duan sleep_cycle = 0; 3094b82f8c3fSFugang Duan wake_cycle = 0; 3095b82f8c3fSFugang Duan } 3096b82f8c3fSFugang Duan 3097b82f8c3fSFugang Duan p->tx_lpi_enabled = enable; 3098b82f8c3fSFugang Duan p->eee_enabled = enable; 3099b82f8c3fSFugang Duan p->eee_active = enable; 3100b82f8c3fSFugang Duan 3101b82f8c3fSFugang Duan writel(sleep_cycle, fep->hwp + FEC_LPI_SLEEP); 3102b82f8c3fSFugang Duan writel(wake_cycle, fep->hwp + FEC_LPI_WAKE); 3103b82f8c3fSFugang Duan 3104b82f8c3fSFugang Duan return 0; 3105b82f8c3fSFugang Duan } 3106b82f8c3fSFugang Duan 3107b82f8c3fSFugang Duan static int 3108b82f8c3fSFugang Duan fec_enet_get_eee(struct net_device *ndev, struct ethtool_eee *edata) 3109b82f8c3fSFugang Duan { 3110b82f8c3fSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 3111b82f8c3fSFugang Duan struct ethtool_eee *p = &fep->eee; 3112b82f8c3fSFugang Duan 3113b82f8c3fSFugang Duan if (!(fep->quirks & FEC_QUIRK_HAS_EEE)) 3114b82f8c3fSFugang Duan return -EOPNOTSUPP; 3115b82f8c3fSFugang Duan 3116b82f8c3fSFugang Duan if (!netif_running(ndev)) 3117b82f8c3fSFugang Duan return -ENETDOWN; 3118b82f8c3fSFugang Duan 3119b82f8c3fSFugang Duan edata->eee_enabled = p->eee_enabled; 3120b82f8c3fSFugang Duan edata->eee_active = p->eee_active; 3121b82f8c3fSFugang Duan edata->tx_lpi_timer = p->tx_lpi_timer; 3122b82f8c3fSFugang Duan edata->tx_lpi_enabled = p->tx_lpi_enabled; 3123b82f8c3fSFugang Duan 3124b82f8c3fSFugang Duan return phy_ethtool_get_eee(ndev->phydev, edata); 3125b82f8c3fSFugang Duan } 3126b82f8c3fSFugang Duan 3127b82f8c3fSFugang Duan static int 3128b82f8c3fSFugang Duan fec_enet_set_eee(struct net_device *ndev, struct ethtool_eee *edata) 3129b82f8c3fSFugang Duan { 3130b82f8c3fSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 3131b82f8c3fSFugang Duan struct ethtool_eee *p = &fep->eee; 3132b82f8c3fSFugang Duan int ret = 0; 3133b82f8c3fSFugang Duan 3134b82f8c3fSFugang Duan if (!(fep->quirks & FEC_QUIRK_HAS_EEE)) 3135b82f8c3fSFugang Duan return -EOPNOTSUPP; 3136b82f8c3fSFugang Duan 3137b82f8c3fSFugang Duan if (!netif_running(ndev)) 3138b82f8c3fSFugang Duan return -ENETDOWN; 3139b82f8c3fSFugang Duan 3140b82f8c3fSFugang Duan p->tx_lpi_timer = edata->tx_lpi_timer; 3141b82f8c3fSFugang Duan 3142b82f8c3fSFugang Duan if (!edata->eee_enabled || !edata->tx_lpi_enabled || 3143b82f8c3fSFugang Duan !edata->tx_lpi_timer) 3144b82f8c3fSFugang Duan ret = fec_enet_eee_mode_set(ndev, false); 3145b82f8c3fSFugang Duan else 3146b82f8c3fSFugang Duan ret = fec_enet_eee_mode_set(ndev, true); 3147b82f8c3fSFugang Duan 3148b82f8c3fSFugang Duan if (ret) 3149b82f8c3fSFugang Duan return ret; 3150b82f8c3fSFugang Duan 3151b82f8c3fSFugang Duan return phy_ethtool_set_eee(ndev->phydev, edata); 3152b82f8c3fSFugang Duan } 3153b82f8c3fSFugang Duan 3154de40ed31SNimrod Andy static void 3155de40ed31SNimrod Andy fec_enet_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) 3156de40ed31SNimrod Andy { 3157de40ed31SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 3158de40ed31SNimrod Andy 3159de40ed31SNimrod Andy if (fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET) { 3160de40ed31SNimrod Andy wol->supported = WAKE_MAGIC; 3161de40ed31SNimrod Andy wol->wolopts = fep->wol_flag & FEC_WOL_FLAG_ENABLE ? WAKE_MAGIC : 0; 3162de40ed31SNimrod Andy } else { 3163de40ed31SNimrod Andy wol->supported = wol->wolopts = 0; 3164de40ed31SNimrod Andy } 3165de40ed31SNimrod Andy } 3166de40ed31SNimrod Andy 3167de40ed31SNimrod Andy static int 3168de40ed31SNimrod Andy fec_enet_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) 3169de40ed31SNimrod Andy { 3170de40ed31SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 3171de40ed31SNimrod Andy 3172de40ed31SNimrod Andy if (!(fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET)) 3173de40ed31SNimrod Andy return -EINVAL; 3174de40ed31SNimrod Andy 3175de40ed31SNimrod Andy if (wol->wolopts & ~WAKE_MAGIC) 3176de40ed31SNimrod Andy return -EINVAL; 3177de40ed31SNimrod Andy 3178de40ed31SNimrod Andy device_set_wakeup_enable(&ndev->dev, wol->wolopts & WAKE_MAGIC); 31790b6f65c7SJoakim Zhang if (device_may_wakeup(&ndev->dev)) 3180de40ed31SNimrod Andy fep->wol_flag |= FEC_WOL_FLAG_ENABLE; 31810b6f65c7SJoakim Zhang else 3182de40ed31SNimrod Andy fep->wol_flag &= (~FEC_WOL_FLAG_ENABLE); 3183de40ed31SNimrod Andy 3184de40ed31SNimrod Andy return 0; 3185de40ed31SNimrod Andy } 3186de40ed31SNimrod Andy 3187793fc096SFrank Li static const struct ethtool_ops fec_enet_ethtool_ops = { 3188d5e3c87dSJakub Kicinski .supported_coalesce_params = ETHTOOL_COALESCE_USECS | 3189d5e3c87dSJakub Kicinski ETHTOOL_COALESCE_MAX_FRAMES, 3190793fc096SFrank Li .get_drvinfo = fec_enet_get_drvinfo, 3191db65f35fSPhilippe Reynes .get_regs_len = fec_enet_get_regs_len, 3192db65f35fSPhilippe Reynes .get_regs = fec_enet_get_regs, 319311d59289SFlorian Fainelli .nway_reset = phy_ethtool_nway_reset, 3194c1d7c48fSRussell King .get_link = ethtool_op_get_link, 3195d851b47bSFugang Duan .get_coalesce = fec_enet_get_coalesce, 3196d851b47bSFugang Duan .set_coalesce = fec_enet_set_coalesce, 319738ae92dcSChris Healy #ifndef CONFIG_M5272 3198c1d7c48fSRussell King .get_pauseparam = fec_enet_get_pauseparam, 3199c1d7c48fSRussell King .set_pauseparam = fec_enet_set_pauseparam, 320038ae92dcSChris Healy .get_strings = fec_enet_get_strings, 3201c1d7c48fSRussell King .get_ethtool_stats = fec_enet_get_ethtool_stats, 320238ae92dcSChris Healy .get_sset_count = fec_enet_get_sset_count, 320338ae92dcSChris Healy #endif 3204c1d7c48fSRussell King .get_ts_info = fec_enet_get_ts_info, 32051b7bde6dSNimrod Andy .get_tunable = fec_enet_get_tunable, 32061b7bde6dSNimrod Andy .set_tunable = fec_enet_set_tunable, 3207de40ed31SNimrod Andy .get_wol = fec_enet_get_wol, 3208de40ed31SNimrod Andy .set_wol = fec_enet_set_wol, 3209b82f8c3fSFugang Duan .get_eee = fec_enet_get_eee, 3210b82f8c3fSFugang Duan .set_eee = fec_enet_set_eee, 32119365fbf5SPhilippe Reynes .get_link_ksettings = phy_ethtool_get_link_ksettings, 32129365fbf5SPhilippe Reynes .set_link_ksettings = phy_ethtool_set_link_ksettings, 32136016ba34SOleksij Rempel .self_test = net_selftest, 3214793fc096SFrank Li }; 3215793fc096SFrank Li 3216793fc096SFrank Li static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) 3217793fc096SFrank Li { 3218793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 321945f5c327SPhilippe Reynes struct phy_device *phydev = ndev->phydev; 3220793fc096SFrank Li 3221793fc096SFrank Li if (!netif_running(ndev)) 3222793fc096SFrank Li return -EINVAL; 3223793fc096SFrank Li 3224793fc096SFrank Li if (!phydev) 3225793fc096SFrank Li return -ENODEV; 3226793fc096SFrank Li 32271d5244d0SBen Hutchings if (fep->bufdesc_ex) { 322834074639SSergey Organov bool use_fec_hwts = !phy_has_hwtstamp(phydev); 322934074639SSergey Organov 323034074639SSergey Organov if (cmd == SIOCSHWTSTAMP) { 323134074639SSergey Organov if (use_fec_hwts) 32321d5244d0SBen Hutchings return fec_ptp_set(ndev, rq); 323334074639SSergey Organov fec_ptp_disable_hwts(ndev); 323434074639SSergey Organov } else if (cmd == SIOCGHWTSTAMP) { 323534074639SSergey Organov if (use_fec_hwts) 32361d5244d0SBen Hutchings return fec_ptp_get(ndev, rq); 32371d5244d0SBen Hutchings } 323834074639SSergey Organov } 3239793fc096SFrank Li 3240793fc096SFrank Li return phy_mii_ioctl(phydev, rq, cmd); 3241793fc096SFrank Li } 3242793fc096SFrank Li 3243793fc096SFrank Li static void fec_enet_free_buffers(struct net_device *ndev) 3244793fc096SFrank Li { 3245793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3246793fc096SFrank Li unsigned int i; 3247793fc096SFrank Li struct sk_buff *skb; 32484d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 32494d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 325059d0f746SFrank Li unsigned int q; 3251793fc096SFrank Li 325259d0f746SFrank Li for (q = 0; q < fep->num_rx_queues; q++) { 325359d0f746SFrank Li rxq = fep->rx_queue[q]; 325495698ff6SShenwei Wang for (i = 0; i < rxq->bd.ring_size; i++) 3255e38553bdSWei Fang page_pool_put_full_page(rxq->page_pool, rxq->rx_skb_info[i].page, false); 325695698ff6SShenwei Wang 32576970ef27SShenwei Wang for (i = 0; i < XDP_STATS_TOTAL; i++) 32586970ef27SShenwei Wang rxq->stats[i] = 0; 32596970ef27SShenwei Wang 326095698ff6SShenwei Wang if (xdp_rxq_info_is_reg(&rxq->xdp_rxq)) 326195698ff6SShenwei Wang xdp_rxq_info_unreg(&rxq->xdp_rxq); 326295698ff6SShenwei Wang page_pool_destroy(rxq->page_pool); 326395698ff6SShenwei Wang rxq->page_pool = NULL; 3264793fc096SFrank Li } 3265793fc096SFrank Li 326659d0f746SFrank Li for (q = 0; q < fep->num_tx_queues; q++) { 326759d0f746SFrank Li txq = fep->tx_queue[q]; 32687355f276STroy Kisky for (i = 0; i < txq->bd.ring_size; i++) { 32694d494cdcSFugang Duan kfree(txq->tx_bounce[i]); 32704d494cdcSFugang Duan txq->tx_bounce[i] = NULL; 32714d494cdcSFugang Duan skb = txq->tx_skbuff[i]; 32724d494cdcSFugang Duan txq->tx_skbuff[i] = NULL; 32738b7c9efaSRussell King dev_kfree_skb(skb); 32748b7c9efaSRussell King } 3275793fc096SFrank Li } 327659d0f746SFrank Li } 3277793fc096SFrank Li 327859d0f746SFrank Li static void fec_enet_free_queue(struct net_device *ndev) 327959d0f746SFrank Li { 328059d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 328159d0f746SFrank Li int i; 328259d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 328359d0f746SFrank Li 328459d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) 328559d0f746SFrank Li if (fep->tx_queue[i] && fep->tx_queue[i]->tso_hdrs) { 328659d0f746SFrank Li txq = fep->tx_queue[i]; 328794920128SFugang Duan dma_free_coherent(&fep->pdev->dev, 32887355f276STroy Kisky txq->bd.ring_size * TSO_HEADER_SIZE, 328959d0f746SFrank Li txq->tso_hdrs, 329059d0f746SFrank Li txq->tso_hdrs_dma); 329159d0f746SFrank Li } 329259d0f746SFrank Li 329359d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) 329459d0f746SFrank Li kfree(fep->rx_queue[i]); 329559d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) 329659d0f746SFrank Li kfree(fep->tx_queue[i]); 329759d0f746SFrank Li } 329859d0f746SFrank Li 329959d0f746SFrank Li static int fec_enet_alloc_queue(struct net_device *ndev) 330059d0f746SFrank Li { 330159d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 330259d0f746SFrank Li int i; 330359d0f746SFrank Li int ret = 0; 330459d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 330559d0f746SFrank Li 330659d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 330759d0f746SFrank Li txq = kzalloc(sizeof(*txq), GFP_KERNEL); 330859d0f746SFrank Li if (!txq) { 330959d0f746SFrank Li ret = -ENOMEM; 331059d0f746SFrank Li goto alloc_failed; 331159d0f746SFrank Li } 331259d0f746SFrank Li 331359d0f746SFrank Li fep->tx_queue[i] = txq; 33147355f276STroy Kisky txq->bd.ring_size = TX_RING_SIZE; 33157355f276STroy Kisky fep->total_tx_ring_size += fep->tx_queue[i]->bd.ring_size; 331659d0f746SFrank Li 331759d0f746SFrank Li txq->tx_stop_threshold = FEC_MAX_SKB_DESCS; 331859d0f746SFrank Li txq->tx_wake_threshold = 33197355f276STroy Kisky (txq->bd.ring_size - txq->tx_stop_threshold) / 2; 332059d0f746SFrank Li 332194920128SFugang Duan txq->tso_hdrs = dma_alloc_coherent(&fep->pdev->dev, 33227355f276STroy Kisky txq->bd.ring_size * TSO_HEADER_SIZE, 332359d0f746SFrank Li &txq->tso_hdrs_dma, 332459d0f746SFrank Li GFP_KERNEL); 332559d0f746SFrank Li if (!txq->tso_hdrs) { 332659d0f746SFrank Li ret = -ENOMEM; 332759d0f746SFrank Li goto alloc_failed; 332859d0f746SFrank Li } 332959d0f746SFrank Li } 333059d0f746SFrank Li 333159d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) { 333259d0f746SFrank Li fep->rx_queue[i] = kzalloc(sizeof(*fep->rx_queue[i]), 333359d0f746SFrank Li GFP_KERNEL); 333459d0f746SFrank Li if (!fep->rx_queue[i]) { 333559d0f746SFrank Li ret = -ENOMEM; 333659d0f746SFrank Li goto alloc_failed; 333759d0f746SFrank Li } 333859d0f746SFrank Li 33397355f276STroy Kisky fep->rx_queue[i]->bd.ring_size = RX_RING_SIZE; 33407355f276STroy Kisky fep->total_rx_ring_size += fep->rx_queue[i]->bd.ring_size; 334159d0f746SFrank Li } 334259d0f746SFrank Li return ret; 334359d0f746SFrank Li 334459d0f746SFrank Li alloc_failed: 334559d0f746SFrank Li fec_enet_free_queue(ndev); 334659d0f746SFrank Li return ret; 334759d0f746SFrank Li } 334859d0f746SFrank Li 334959d0f746SFrank Li static int 335059d0f746SFrank Li fec_enet_alloc_rxq_buffers(struct net_device *ndev, unsigned int queue) 3351793fc096SFrank Li { 3352793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 33534d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 335495698ff6SShenwei Wang dma_addr_t phys_addr; 335595698ff6SShenwei Wang struct bufdesc *bdp; 335695698ff6SShenwei Wang struct page *page; 335795698ff6SShenwei Wang int i, err; 3358793fc096SFrank Li 335959d0f746SFrank Li rxq = fep->rx_queue[queue]; 33607355f276STroy Kisky bdp = rxq->bd.base; 3361793fc096SFrank Li 336295698ff6SShenwei Wang err = fec_enet_create_page_pool(fep, rxq, rxq->bd.ring_size); 336395698ff6SShenwei Wang if (err < 0) { 336495698ff6SShenwei Wang netdev_err(ndev, "%s failed queue %d (%d)\n", __func__, queue, err); 336595698ff6SShenwei Wang return err; 3366d842a31fSDuan Fugang-B38611 } 3367730ee360SRussell King 336895698ff6SShenwei Wang for (i = 0; i < rxq->bd.ring_size; i++) { 336995698ff6SShenwei Wang page = page_pool_dev_alloc_pages(rxq->page_pool); 337095698ff6SShenwei Wang if (!page) 337195698ff6SShenwei Wang goto err_alloc; 337295698ff6SShenwei Wang 337395698ff6SShenwei Wang phys_addr = page_pool_get_dma_addr(page) + FEC_ENET_XDP_HEADROOM; 337495698ff6SShenwei Wang bdp->cbd_bufaddr = cpu_to_fec32(phys_addr); 337595698ff6SShenwei Wang 337695698ff6SShenwei Wang rxq->rx_skb_info[i].page = page; 337795698ff6SShenwei Wang rxq->rx_skb_info[i].offset = FEC_ENET_XDP_HEADROOM; 33785cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); 3379793fc096SFrank Li 3380793fc096SFrank Li if (fep->bufdesc_ex) { 3381793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 33825cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(BD_ENET_RX_INT); 3383793fc096SFrank Li } 3384793fc096SFrank Li 33857355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 3386793fc096SFrank Li } 3387793fc096SFrank Li 3388793fc096SFrank Li /* Set the last buffer to wrap. */ 33897355f276STroy Kisky bdp = fec_enet_get_prevdesc(bdp, &rxq->bd); 33905cfa3039SJohannes Berg bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 339159d0f746SFrank Li return 0; 3392793fc096SFrank Li 339359d0f746SFrank Li err_alloc: 339459d0f746SFrank Li fec_enet_free_buffers(ndev); 339559d0f746SFrank Li return -ENOMEM; 339659d0f746SFrank Li } 339759d0f746SFrank Li 339859d0f746SFrank Li static int 339959d0f746SFrank Li fec_enet_alloc_txq_buffers(struct net_device *ndev, unsigned int queue) 340059d0f746SFrank Li { 340159d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 340259d0f746SFrank Li unsigned int i; 340359d0f746SFrank Li struct bufdesc *bdp; 340459d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 340559d0f746SFrank Li 340659d0f746SFrank Li txq = fep->tx_queue[queue]; 34077355f276STroy Kisky bdp = txq->bd.base; 34087355f276STroy Kisky for (i = 0; i < txq->bd.ring_size; i++) { 34094d494cdcSFugang Duan txq->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL); 34104d494cdcSFugang Duan if (!txq->tx_bounce[i]) 3411ffdce2ccSRussell King goto err_alloc; 3412793fc096SFrank Li 34135cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(0); 34145cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(0); 3415793fc096SFrank Li 3416793fc096SFrank Li if (fep->bufdesc_ex) { 3417793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 34185cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(BD_ENET_TX_INT); 3419793fc096SFrank Li } 3420793fc096SFrank Li 34217355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 3422793fc096SFrank Li } 3423793fc096SFrank Li 3424793fc096SFrank Li /* Set the last buffer to wrap. */ 34257355f276STroy Kisky bdp = fec_enet_get_prevdesc(bdp, &txq->bd); 34265cfa3039SJohannes Berg bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 3427793fc096SFrank Li 3428793fc096SFrank Li return 0; 3429ffdce2ccSRussell King 3430ffdce2ccSRussell King err_alloc: 3431ffdce2ccSRussell King fec_enet_free_buffers(ndev); 3432ffdce2ccSRussell King return -ENOMEM; 3433793fc096SFrank Li } 3434793fc096SFrank Li 343559d0f746SFrank Li static int fec_enet_alloc_buffers(struct net_device *ndev) 343659d0f746SFrank Li { 343759d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 343859d0f746SFrank Li unsigned int i; 343959d0f746SFrank Li 344059d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) 344159d0f746SFrank Li if (fec_enet_alloc_rxq_buffers(ndev, i)) 344259d0f746SFrank Li return -ENOMEM; 344359d0f746SFrank Li 344459d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) 344559d0f746SFrank Li if (fec_enet_alloc_txq_buffers(ndev, i)) 344659d0f746SFrank Li return -ENOMEM; 344759d0f746SFrank Li return 0; 344859d0f746SFrank Li } 344959d0f746SFrank Li 3450793fc096SFrank Li static int 3451793fc096SFrank Li fec_enet_open(struct net_device *ndev) 3452793fc096SFrank Li { 3453793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3454793fc096SFrank Li int ret; 34551b0a83acSRichard Leitner bool reset_again; 3456793fc096SFrank Li 3457da875fa5SZhang Qilong ret = pm_runtime_resume_and_get(&fep->pdev->dev); 3458b0c6ce24SFabio Estevam if (ret < 0) 34598fff755eSAndrew Lunn return ret; 34608fff755eSAndrew Lunn 34615bbde4d2SNimrod Andy pinctrl_pm_select_default_state(&fep->pdev->dev); 3462e8fcfcd5SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 3463e8fcfcd5SNimrod Andy if (ret) 34648fff755eSAndrew Lunn goto clk_enable; 3465e8fcfcd5SNimrod Andy 34661b0a83acSRichard Leitner /* During the first fec_enet_open call the PHY isn't probed at this 34671b0a83acSRichard Leitner * point. Therefore the phy_reset_after_clk_enable() call within 34681b0a83acSRichard Leitner * fec_enet_clk_enable() fails. As we need this reset in order to be 34691b0a83acSRichard Leitner * sure the PHY is working correctly we check if we need to reset again 34701b0a83acSRichard Leitner * later when the PHY is probed 34711b0a83acSRichard Leitner */ 34721b0a83acSRichard Leitner if (ndev->phydev && ndev->phydev->drv) 34731b0a83acSRichard Leitner reset_again = false; 34741b0a83acSRichard Leitner else 34751b0a83acSRichard Leitner reset_again = true; 34761b0a83acSRichard Leitner 3477793fc096SFrank Li /* I should reset the ring buffers here, but I don't yet know 3478793fc096SFrank Li * a simple way to do that. 3479793fc096SFrank Li */ 3480793fc096SFrank Li 3481793fc096SFrank Li ret = fec_enet_alloc_buffers(ndev); 3482793fc096SFrank Li if (ret) 3483681d2421SFabio Estevam goto err_enet_alloc; 3484793fc096SFrank Li 348555dd2753SNimrod Andy /* Init MAC prior to mii bus probe */ 348655dd2753SNimrod Andy fec_restart(ndev); 348755dd2753SNimrod Andy 34881b0a83acSRichard Leitner /* Call phy_reset_after_clk_enable() again if it failed during 34891b0a83acSRichard Leitner * phy_reset_after_clk_enable() before because the PHY wasn't probed. 34901b0a83acSRichard Leitner */ 34911b0a83acSRichard Leitner if (reset_again) 349264a632daSMarek Vasut fec_enet_phy_reset_after_clk_enable(ndev); 34931b0a83acSRichard Leitner 34940da1ccbbSMarek Vasut /* Probe and connect to PHY when open the interface */ 34950da1ccbbSMarek Vasut ret = fec_enet_mii_probe(ndev); 34960da1ccbbSMarek Vasut if (ret) 34970da1ccbbSMarek Vasut goto err_enet_mii_probe; 3498681d2421SFabio Estevam 3499681d2421SFabio Estevam if (fep->quirks & FEC_QUIRK_ERR006687) 3500793fc096SFrank Li imx6q_cpuidle_fec_irqs_used(); 3501793fc096SFrank Li 35027d650df9SWei Fang if (fep->quirks & FEC_QUIRK_HAS_PMQOS) 35037d650df9SWei Fang cpu_latency_qos_add_request(&fep->pm_qos_req, 0); 35047d650df9SWei Fang 3505793fc096SFrank Li napi_enable(&fep->napi); 3506793fc096SFrank Li phy_start(ndev->phydev); 3507793fc096SFrank Li netif_tx_start_all_queues(ndev); 3508793fc096SFrank Li 3509793fc096SFrank Li device_set_wakeup_enable(&ndev->dev, fep->wol_flag & 3510793fc096SFrank Li FEC_WOL_FLAG_ENABLE); 3511793fc096SFrank Li 3512793fc096SFrank Li return 0; 3513793fc096SFrank Li 3514793fc096SFrank Li err_enet_mii_probe: 3515793fc096SFrank Li fec_enet_free_buffers(ndev); 3516793fc096SFrank Li err_enet_alloc: 3517793fc096SFrank Li fec_enet_clk_enable(ndev, false); 3518793fc096SFrank Li clk_enable: 3519793fc096SFrank Li pm_runtime_mark_last_busy(&fep->pdev->dev); 3520793fc096SFrank Li pm_runtime_put_autosuspend(&fep->pdev->dev); 3521793fc096SFrank Li pinctrl_pm_select_sleep_state(&fep->pdev->dev); 3522793fc096SFrank Li return ret; 3523d76cfae9SRussell King } 3524d76cfae9SRussell King 352531a6de34SRussell King static int 3526793fc096SFrank Li fec_enet_close(struct net_device *ndev) 3527b49cd504SRussell King { 3528793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 352931a6de34SRussell King 353045f5c327SPhilippe Reynes phy_stop(ndev->phydev); 3531793fc096SFrank Li 3532793fc096SFrank Li if (netif_device_present(ndev)) { 3533793fc096SFrank Li napi_disable(&fep->napi); 3534793fc096SFrank Li netif_tx_disable(ndev); 3535793fc096SFrank Li fec_stop(ndev); 3536793fc096SFrank Li } 3537793fc096SFrank Li 353845f5c327SPhilippe Reynes phy_disconnect(ndev->phydev); 3539793fc096SFrank Li 354029380905SLucas Stach if (fep->quirks & FEC_QUIRK_ERR006687) 354129380905SLucas Stach imx6q_cpuidle_fec_irqs_unused(); 354229380905SLucas Stach 354380cca775SNikita Yushchenko fec_enet_update_ethtool_stats(ndev); 354480cca775SNikita Yushchenko 3545e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 35467d650df9SWei Fang if (fep->quirks & FEC_QUIRK_HAS_PMQOS) 35477d650df9SWei Fang cpu_latency_qos_remove_request(&fep->pm_qos_req); 35487d650df9SWei Fang 35495bbde4d2SNimrod Andy pinctrl_pm_select_sleep_state(&fep->pdev->dev); 35508fff755eSAndrew Lunn pm_runtime_mark_last_busy(&fep->pdev->dev); 35518fff755eSAndrew Lunn pm_runtime_put_autosuspend(&fep->pdev->dev); 35528fff755eSAndrew Lunn 3553793fc096SFrank Li fec_enet_free_buffers(ndev); 3554793fc096SFrank Li 3555793fc096SFrank Li return 0; 3556793fc096SFrank Li } 3557793fc096SFrank Li 3558793fc096SFrank Li /* Set or clear the multicast filter for this adaptor. 3559793fc096SFrank Li * Skeleton taken from sunlance driver. 3560793fc096SFrank Li * The CPM Ethernet implementation allows Multicast as well as individual 3561793fc096SFrank Li * MAC address filtering. Some of the drivers check to make sure it is 3562793fc096SFrank Li * a group multicast address, and discard those that are not. I guess I 3563793fc096SFrank Li * will do the same for now, but just remove the test if you want 3564793fc096SFrank Li * individual filtering as well (do the upper net layers want or support 3565793fc096SFrank Li * this kind of feature?). 3566793fc096SFrank Li */ 3567793fc096SFrank Li 35686176e89cSJiri Kosina #define FEC_HASH_BITS 6 /* #bits in hash */ 3569793fc096SFrank Li 3570793fc096SFrank Li static void set_multicast_list(struct net_device *ndev) 3571793fc096SFrank Li { 3572793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3573793fc096SFrank Li struct netdev_hw_addr *ha; 357416f6e983SKrzysztof Kozlowski unsigned int crc, tmp; 3575793fc096SFrank Li unsigned char hash; 357601f8902bSRui Sousa unsigned int hash_high = 0, hash_low = 0; 3577793fc096SFrank Li 3578793fc096SFrank Li if (ndev->flags & IFF_PROMISC) { 3579793fc096SFrank Li tmp = readl(fep->hwp + FEC_R_CNTRL); 3580793fc096SFrank Li tmp |= 0x8; 3581793fc096SFrank Li writel(tmp, fep->hwp + FEC_R_CNTRL); 3582793fc096SFrank Li return; 3583793fc096SFrank Li } 3584793fc096SFrank Li 3585793fc096SFrank Li tmp = readl(fep->hwp + FEC_R_CNTRL); 3586793fc096SFrank Li tmp &= ~0x8; 3587793fc096SFrank Li writel(tmp, fep->hwp + FEC_R_CNTRL); 3588793fc096SFrank Li 3589793fc096SFrank Li if (ndev->flags & IFF_ALLMULTI) { 3590793fc096SFrank Li /* Catch all multicast addresses, so set the 3591793fc096SFrank Li * filter to all 1's 3592793fc096SFrank Li */ 3593793fc096SFrank Li writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 3594793fc096SFrank Li writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 3595793fc096SFrank Li 3596793fc096SFrank Li return; 3597793fc096SFrank Li } 3598793fc096SFrank Li 359901f8902bSRui Sousa /* Add the addresses in hash register */ 3600793fc096SFrank Li netdev_for_each_mc_addr(ha, ndev) { 3601793fc096SFrank Li /* calculate crc32 value of mac address */ 360216f6e983SKrzysztof Kozlowski crc = ether_crc_le(ndev->addr_len, ha->addr); 3603793fc096SFrank Li 36046176e89cSJiri Kosina /* only upper 6 bits (FEC_HASH_BITS) are used 3605981a0547SPeter Meerwald-Stadler * which point to specific bit in the hash registers 3606793fc096SFrank Li */ 36076176e89cSJiri Kosina hash = (crc >> (32 - FEC_HASH_BITS)) & 0x3f; 3608793fc096SFrank Li 360901f8902bSRui Sousa if (hash > 31) 361001f8902bSRui Sousa hash_high |= 1 << (hash - 32); 361101f8902bSRui Sousa else 361201f8902bSRui Sousa hash_low |= 1 << hash; 3613793fc096SFrank Li } 361401f8902bSRui Sousa 361501f8902bSRui Sousa writel(hash_high, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 361601f8902bSRui Sousa writel(hash_low, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 3617793fc096SFrank Li } 3618793fc096SFrank Li 3619793fc096SFrank Li /* Set a MAC change in hardware. */ 3620793fc096SFrank Li static int 3621793fc096SFrank Li fec_set_mac_address(struct net_device *ndev, void *p) 3622793fc096SFrank Li { 3623793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3624793fc096SFrank Li struct sockaddr *addr = p; 3625793fc096SFrank Li 362644934facSLucas Stach if (addr) { 3627793fc096SFrank Li if (!is_valid_ether_addr(addr->sa_data)) 3628793fc096SFrank Li return -EADDRNOTAVAIL; 3629a05e4c0aSJakub Kicinski eth_hw_addr_set(ndev, addr->sa_data); 363044934facSLucas Stach } 3631793fc096SFrank Li 36329638d19eSNimrod Andy /* Add netif status check here to avoid system hang in below case: 36339638d19eSNimrod Andy * ifconfig ethx down; ifconfig ethx hw ether xx:xx:xx:xx:xx:xx; 36349638d19eSNimrod Andy * After ethx down, fec all clocks are gated off and then register 36359638d19eSNimrod Andy * access causes system hang. 36369638d19eSNimrod Andy */ 36379638d19eSNimrod Andy if (!netif_running(ndev)) 36389638d19eSNimrod Andy return 0; 36399638d19eSNimrod Andy 3640793fc096SFrank Li writel(ndev->dev_addr[3] | (ndev->dev_addr[2] << 8) | 3641793fc096SFrank Li (ndev->dev_addr[1] << 16) | (ndev->dev_addr[0] << 24), 3642793fc096SFrank Li fep->hwp + FEC_ADDR_LOW); 3643793fc096SFrank Li writel((ndev->dev_addr[5] << 16) | (ndev->dev_addr[4] << 24), 3644793fc096SFrank Li fep->hwp + FEC_ADDR_HIGH); 3645793fc096SFrank Li return 0; 3646793fc096SFrank Li } 3647793fc096SFrank Li 3648793fc096SFrank Li #ifdef CONFIG_NET_POLL_CONTROLLER 3649793fc096SFrank Li /** 3650793fc096SFrank Li * fec_poll_controller - FEC Poll controller function 3651793fc096SFrank Li * @dev: The FEC network adapter 3652793fc096SFrank Li * 3653793fc096SFrank Li * Polled functionality used by netconsole and others in non interrupt mode 3654793fc096SFrank Li * 3655793fc096SFrank Li */ 3656793fc096SFrank Li static void fec_poll_controller(struct net_device *dev) 3657793fc096SFrank Li { 3658793fc096SFrank Li int i; 3659793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(dev); 3660793fc096SFrank Li 3661793fc096SFrank Li for (i = 0; i < FEC_IRQ_NUM; i++) { 3662793fc096SFrank Li if (fep->irq[i] > 0) { 3663793fc096SFrank Li disable_irq(fep->irq[i]); 3664793fc096SFrank Li fec_enet_interrupt(fep->irq[i], dev); 3665793fc096SFrank Li enable_irq(fep->irq[i]); 3666793fc096SFrank Li } 3667793fc096SFrank Li } 3668793fc096SFrank Li } 3669793fc096SFrank Li #endif 3670793fc096SFrank Li 36715bc26726SNimrod Andy static inline void fec_enet_set_netdev_features(struct net_device *netdev, 36724c09eed9SJim Baxter netdev_features_t features) 36734c09eed9SJim Baxter { 36744c09eed9SJim Baxter struct fec_enet_private *fep = netdev_priv(netdev); 36754c09eed9SJim Baxter netdev_features_t changed = features ^ netdev->features; 36764c09eed9SJim Baxter 36774c09eed9SJim Baxter netdev->features = features; 36784c09eed9SJim Baxter 36794c09eed9SJim Baxter /* Receive checksum has been changed */ 36804c09eed9SJim Baxter if (changed & NETIF_F_RXCSUM) { 36814c09eed9SJim Baxter if (features & NETIF_F_RXCSUM) 36824c09eed9SJim Baxter fep->csum_flags |= FLAG_RX_CSUM_ENABLED; 36834c09eed9SJim Baxter else 36844c09eed9SJim Baxter fep->csum_flags &= ~FLAG_RX_CSUM_ENABLED; 36858506fa1dSRussell King } 36865bc26726SNimrod Andy } 36874c09eed9SJim Baxter 36885bc26726SNimrod Andy static int fec_set_features(struct net_device *netdev, 36895bc26726SNimrod Andy netdev_features_t features) 36905bc26726SNimrod Andy { 36915bc26726SNimrod Andy struct fec_enet_private *fep = netdev_priv(netdev); 36925bc26726SNimrod Andy netdev_features_t changed = features ^ netdev->features; 36935bc26726SNimrod Andy 36945b40f709SFabio Estevam if (netif_running(netdev) && changed & NETIF_F_RXCSUM) { 36955bc26726SNimrod Andy napi_disable(&fep->napi); 36965bc26726SNimrod Andy netif_tx_lock_bh(netdev); 36975bc26726SNimrod Andy fec_stop(netdev); 36985bc26726SNimrod Andy fec_enet_set_netdev_features(netdev, features); 3699ef83337dSRussell King fec_restart(netdev); 37004d494cdcSFugang Duan netif_tx_wake_all_queues(netdev); 37016af42d42SRussell King netif_tx_unlock_bh(netdev); 3702dbc64a8eSRussell King napi_enable(&fep->napi); 37035bc26726SNimrod Andy } else { 37045bc26726SNimrod Andy fec_enet_set_netdev_features(netdev, features); 37054c09eed9SJim Baxter } 37064c09eed9SJim Baxter 37074c09eed9SJim Baxter return 0; 37084c09eed9SJim Baxter } 37094c09eed9SJim Baxter 371052c4a1a8SFugang Duan static u16 fec_enet_get_raw_vlan_tci(struct sk_buff *skb) 371152c4a1a8SFugang Duan { 371252c4a1a8SFugang Duan struct vlan_ethhdr *vhdr; 371352c4a1a8SFugang Duan unsigned short vlan_TCI = 0; 371452c4a1a8SFugang Duan 371552c4a1a8SFugang Duan if (skb->protocol == htons(ETH_P_ALL)) { 371652c4a1a8SFugang Duan vhdr = (struct vlan_ethhdr *)(skb->data); 371752c4a1a8SFugang Duan vlan_TCI = ntohs(vhdr->h_vlan_TCI); 371852c4a1a8SFugang Duan } 371952c4a1a8SFugang Duan 372052c4a1a8SFugang Duan return vlan_TCI; 372152c4a1a8SFugang Duan } 372252c4a1a8SFugang Duan 372352c4a1a8SFugang Duan static u16 fec_enet_select_queue(struct net_device *ndev, struct sk_buff *skb, 372452c4a1a8SFugang Duan struct net_device *sb_dev) 372552c4a1a8SFugang Duan { 372652c4a1a8SFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 372752c4a1a8SFugang Duan u16 vlan_tag; 372852c4a1a8SFugang Duan 372952c4a1a8SFugang Duan if (!(fep->quirks & FEC_QUIRK_HAS_AVB)) 373052c4a1a8SFugang Duan return netdev_pick_tx(ndev, skb, NULL); 373152c4a1a8SFugang Duan 373252c4a1a8SFugang Duan vlan_tag = fec_enet_get_raw_vlan_tci(skb); 373352c4a1a8SFugang Duan if (!vlan_tag) 373452c4a1a8SFugang Duan return vlan_tag; 373552c4a1a8SFugang Duan 373652c4a1a8SFugang Duan return fec_enet_vlan_pri_to_queue[vlan_tag >> 13]; 373752c4a1a8SFugang Duan } 373852c4a1a8SFugang Duan 37396d6b39f1SShenwei Wang static int fec_enet_bpf(struct net_device *dev, struct netdev_bpf *bpf) 37406d6b39f1SShenwei Wang { 37416d6b39f1SShenwei Wang struct fec_enet_private *fep = netdev_priv(dev); 37426d6b39f1SShenwei Wang bool is_run = netif_running(dev); 37436d6b39f1SShenwei Wang struct bpf_prog *old_prog; 37446d6b39f1SShenwei Wang 37456d6b39f1SShenwei Wang switch (bpf->command) { 37466d6b39f1SShenwei Wang case XDP_SETUP_PROG: 37476c8fae0cSShenwei Wang /* No need to support the SoCs that require to 37486c8fae0cSShenwei Wang * do the frame swap because the performance wouldn't be 37496c8fae0cSShenwei Wang * better than the skb mode. 37506c8fae0cSShenwei Wang */ 37516c8fae0cSShenwei Wang if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 37526c8fae0cSShenwei Wang return -EOPNOTSUPP; 37536c8fae0cSShenwei Wang 37546d6b39f1SShenwei Wang if (is_run) { 37556d6b39f1SShenwei Wang napi_disable(&fep->napi); 37566d6b39f1SShenwei Wang netif_tx_disable(dev); 37576d6b39f1SShenwei Wang } 37586d6b39f1SShenwei Wang 37596d6b39f1SShenwei Wang old_prog = xchg(&fep->xdp_prog, bpf->prog); 37606d6b39f1SShenwei Wang fec_restart(dev); 37616d6b39f1SShenwei Wang 37626d6b39f1SShenwei Wang if (is_run) { 37636d6b39f1SShenwei Wang napi_enable(&fep->napi); 37646d6b39f1SShenwei Wang netif_tx_start_all_queues(dev); 37656d6b39f1SShenwei Wang } 37666d6b39f1SShenwei Wang 37676d6b39f1SShenwei Wang if (old_prog) 37686d6b39f1SShenwei Wang bpf_prog_put(old_prog); 37696d6b39f1SShenwei Wang 37706d6b39f1SShenwei Wang return 0; 37716d6b39f1SShenwei Wang 37726d6b39f1SShenwei Wang case XDP_SETUP_XSK_POOL: 37736d6b39f1SShenwei Wang return -EOPNOTSUPP; 37746d6b39f1SShenwei Wang 37756d6b39f1SShenwei Wang default: 37766d6b39f1SShenwei Wang return -EOPNOTSUPP; 37776d6b39f1SShenwei Wang } 37786d6b39f1SShenwei Wang } 37796d6b39f1SShenwei Wang 37806d6b39f1SShenwei Wang static int 37816c8fae0cSShenwei Wang fec_enet_xdp_get_tx_queue(struct fec_enet_private *fep, int index) 37826d6b39f1SShenwei Wang { 37836d6b39f1SShenwei Wang if (unlikely(index < 0)) 37846c8fae0cSShenwei Wang return 0; 37856d6b39f1SShenwei Wang 37866c8fae0cSShenwei Wang return (index % fep->num_tx_queues); 37876d6b39f1SShenwei Wang } 37886d6b39f1SShenwei Wang 37896d6b39f1SShenwei Wang static int fec_enet_txq_xmit_frame(struct fec_enet_private *fep, 37906d6b39f1SShenwei Wang struct fec_enet_priv_tx_q *txq, 37916d6b39f1SShenwei Wang struct xdp_frame *frame) 37926d6b39f1SShenwei Wang { 37936d6b39f1SShenwei Wang unsigned int index, status, estatus; 37946d6b39f1SShenwei Wang struct bufdesc *bdp, *last_bdp; 37956d6b39f1SShenwei Wang dma_addr_t dma_addr; 37966d6b39f1SShenwei Wang int entries_free; 37976d6b39f1SShenwei Wang 37986d6b39f1SShenwei Wang entries_free = fec_enet_get_free_txdesc_num(txq); 37996d6b39f1SShenwei Wang if (entries_free < MAX_SKB_FRAGS + 1) { 38006d6b39f1SShenwei Wang netdev_err(fep->netdev, "NOT enough BD for SG!\n"); 380126312c68SShenwei Wang xdp_return_frame(frame); 380226312c68SShenwei Wang return NETDEV_TX_BUSY; 38036d6b39f1SShenwei Wang } 38046d6b39f1SShenwei Wang 38056d6b39f1SShenwei Wang /* Fill in a Tx ring entry */ 38066d6b39f1SShenwei Wang bdp = txq->bd.cur; 38076d6b39f1SShenwei Wang last_bdp = bdp; 38086d6b39f1SShenwei Wang status = fec16_to_cpu(bdp->cbd_sc); 38096d6b39f1SShenwei Wang status &= ~BD_ENET_TX_STATS; 38106d6b39f1SShenwei Wang 38116d6b39f1SShenwei Wang index = fec_enet_get_bd_index(bdp, &txq->bd); 38126d6b39f1SShenwei Wang 38136d6b39f1SShenwei Wang dma_addr = dma_map_single(&fep->pdev->dev, frame->data, 38146d6b39f1SShenwei Wang frame->len, DMA_TO_DEVICE); 38156d6b39f1SShenwei Wang if (dma_mapping_error(&fep->pdev->dev, dma_addr)) 38166d6b39f1SShenwei Wang return FEC_ENET_XDP_CONSUMED; 38176d6b39f1SShenwei Wang 38186d6b39f1SShenwei Wang status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); 38196d6b39f1SShenwei Wang if (fep->bufdesc_ex) 38206d6b39f1SShenwei Wang estatus = BD_ENET_TX_INT; 38216d6b39f1SShenwei Wang 38226d6b39f1SShenwei Wang bdp->cbd_bufaddr = cpu_to_fec32(dma_addr); 38236d6b39f1SShenwei Wang bdp->cbd_datlen = cpu_to_fec16(frame->len); 38246d6b39f1SShenwei Wang 38256d6b39f1SShenwei Wang if (fep->bufdesc_ex) { 38266d6b39f1SShenwei Wang struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 38276d6b39f1SShenwei Wang 38286d6b39f1SShenwei Wang if (fep->quirks & FEC_QUIRK_HAS_AVB) 38296d6b39f1SShenwei Wang estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 38306d6b39f1SShenwei Wang 38316d6b39f1SShenwei Wang ebdp->cbd_bdu = 0; 38326d6b39f1SShenwei Wang ebdp->cbd_esc = cpu_to_fec32(estatus); 38336d6b39f1SShenwei Wang } 38346d6b39f1SShenwei Wang 38356d6b39f1SShenwei Wang index = fec_enet_get_bd_index(last_bdp, &txq->bd); 38366d6b39f1SShenwei Wang txq->tx_skbuff[index] = NULL; 38376d6b39f1SShenwei Wang 38386d6b39f1SShenwei Wang /* Send it on its way. Tell FEC it's ready, interrupt when done, 38396d6b39f1SShenwei Wang * it's the last BD of the frame, and to put the CRC on the end. 38406d6b39f1SShenwei Wang */ 38416d6b39f1SShenwei Wang status |= (BD_ENET_TX_READY | BD_ENET_TX_TC); 38426d6b39f1SShenwei Wang bdp->cbd_sc = cpu_to_fec16(status); 38436d6b39f1SShenwei Wang 38446d6b39f1SShenwei Wang /* If this was the last BD in the ring, start at the beginning again. */ 38456d6b39f1SShenwei Wang bdp = fec_enet_get_nextdesc(last_bdp, &txq->bd); 38466d6b39f1SShenwei Wang 38476d6b39f1SShenwei Wang txq->bd.cur = bdp; 38486d6b39f1SShenwei Wang 38496d6b39f1SShenwei Wang return 0; 38506d6b39f1SShenwei Wang } 38516d6b39f1SShenwei Wang 38526d6b39f1SShenwei Wang static int fec_enet_xdp_xmit(struct net_device *dev, 38536d6b39f1SShenwei Wang int num_frames, 38546d6b39f1SShenwei Wang struct xdp_frame **frames, 38556d6b39f1SShenwei Wang u32 flags) 38566d6b39f1SShenwei Wang { 38576d6b39f1SShenwei Wang struct fec_enet_private *fep = netdev_priv(dev); 38586d6b39f1SShenwei Wang struct fec_enet_priv_tx_q *txq; 38596d6b39f1SShenwei Wang int cpu = smp_processor_id(); 386026312c68SShenwei Wang unsigned int sent_frames = 0; 38616d6b39f1SShenwei Wang struct netdev_queue *nq; 38626d6b39f1SShenwei Wang unsigned int queue; 38636d6b39f1SShenwei Wang int i; 38646d6b39f1SShenwei Wang 38656d6b39f1SShenwei Wang queue = fec_enet_xdp_get_tx_queue(fep, cpu); 38666d6b39f1SShenwei Wang txq = fep->tx_queue[queue]; 38676d6b39f1SShenwei Wang nq = netdev_get_tx_queue(fep->netdev, queue); 38686d6b39f1SShenwei Wang 38696d6b39f1SShenwei Wang __netif_tx_lock(nq, cpu); 38706d6b39f1SShenwei Wang 387126312c68SShenwei Wang for (i = 0; i < num_frames; i++) { 387226312c68SShenwei Wang if (fec_enet_txq_xmit_frame(fep, txq, frames[i]) != 0) 387326312c68SShenwei Wang break; 387426312c68SShenwei Wang sent_frames++; 387526312c68SShenwei Wang } 38766d6b39f1SShenwei Wang 38776d6b39f1SShenwei Wang /* Make sure the update to bdp and tx_skbuff are performed. */ 38786d6b39f1SShenwei Wang wmb(); 38796d6b39f1SShenwei Wang 38806d6b39f1SShenwei Wang /* Trigger transmission start */ 38816d6b39f1SShenwei Wang writel(0, txq->bd.reg_desc_active); 38826d6b39f1SShenwei Wang 38836d6b39f1SShenwei Wang __netif_tx_unlock(nq); 38846d6b39f1SShenwei Wang 388526312c68SShenwei Wang return sent_frames; 38866d6b39f1SShenwei Wang } 38876d6b39f1SShenwei Wang 3888793fc096SFrank Li static const struct net_device_ops fec_netdev_ops = { 3889793fc096SFrank Li .ndo_open = fec_enet_open, 3890793fc096SFrank Li .ndo_stop = fec_enet_close, 3891793fc096SFrank Li .ndo_start_xmit = fec_enet_start_xmit, 389252c4a1a8SFugang Duan .ndo_select_queue = fec_enet_select_queue, 3893793fc096SFrank Li .ndo_set_rx_mode = set_multicast_list, 3894793fc096SFrank Li .ndo_validate_addr = eth_validate_addr, 3895793fc096SFrank Li .ndo_tx_timeout = fec_timeout, 3896793fc096SFrank Li .ndo_set_mac_address = fec_set_mac_address, 3897a7605370SArnd Bergmann .ndo_eth_ioctl = fec_enet_ioctl, 3898793fc096SFrank Li #ifdef CONFIG_NET_POLL_CONTROLLER 3899793fc096SFrank Li .ndo_poll_controller = fec_poll_controller, 3900793fc096SFrank Li #endif 39014c09eed9SJim Baxter .ndo_set_features = fec_set_features, 39026d6b39f1SShenwei Wang .ndo_bpf = fec_enet_bpf, 39036d6b39f1SShenwei Wang .ndo_xdp_xmit = fec_enet_xdp_xmit, 3904793fc096SFrank Li }; 3905793fc096SFrank Li 390653bb20d1STroy Kisky static const unsigned short offset_des_active_rxq[] = { 390753bb20d1STroy Kisky FEC_R_DES_ACTIVE_0, FEC_R_DES_ACTIVE_1, FEC_R_DES_ACTIVE_2 390853bb20d1STroy Kisky }; 390953bb20d1STroy Kisky 391053bb20d1STroy Kisky static const unsigned short offset_des_active_txq[] = { 391153bb20d1STroy Kisky FEC_X_DES_ACTIVE_0, FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2 391253bb20d1STroy Kisky }; 391353bb20d1STroy Kisky 3914793fc096SFrank Li /* 3915793fc096SFrank Li * XXX: We need to clean up on failure exits here. 3916793fc096SFrank Li * 3917793fc096SFrank Li */ 3918793fc096SFrank Li static int fec_enet_init(struct net_device *ndev) 3919793fc096SFrank Li { 3920793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3921793fc096SFrank Li struct bufdesc *cbd_base; 39224d494cdcSFugang Duan dma_addr_t bd_dma; 392355d0218aSNimrod Andy int bd_size; 392459d0f746SFrank Li unsigned int i; 39257355f276STroy Kisky unsigned dsize = fep->bufdesc_ex ? sizeof(struct bufdesc_ex) : 39267355f276STroy Kisky sizeof(struct bufdesc); 39277355f276STroy Kisky unsigned dsize_log2 = __fls(dsize); 3928453e9dc4SStefan Agner int ret; 392955d0218aSNimrod Andy 39307355f276STroy Kisky WARN_ON(dsize != (1 << dsize_log2)); 39313f1dcc6aSLucas Stach #if defined(CONFIG_ARM) || defined(CONFIG_ARM64) 393241ef84ceSFugang Duan fep->rx_align = 0xf; 393341ef84ceSFugang Duan fep->tx_align = 0xf; 393441ef84ceSFugang Duan #else 393541ef84ceSFugang Duan fep->rx_align = 0x3; 393641ef84ceSFugang Duan fep->tx_align = 0x3; 393741ef84ceSFugang Duan #endif 3938df727d45SRasmus Villemoes fep->rx_pkts_itr = FEC_ITR_ICFT_DEFAULT; 3939df727d45SRasmus Villemoes fep->tx_pkts_itr = FEC_ITR_ICFT_DEFAULT; 3940df727d45SRasmus Villemoes fep->rx_time_itr = FEC_ITR_ICTT_DEFAULT; 3941df727d45SRasmus Villemoes fep->tx_time_itr = FEC_ITR_ICTT_DEFAULT; 394241ef84ceSFugang Duan 3943453e9dc4SStefan Agner /* Check mask of the streaming and coherent API */ 3944453e9dc4SStefan Agner ret = dma_set_mask_and_coherent(&fep->pdev->dev, DMA_BIT_MASK(32)); 3945453e9dc4SStefan Agner if (ret < 0) { 3946453e9dc4SStefan Agner dev_warn(&fep->pdev->dev, "No suitable DMA available\n"); 3947453e9dc4SStefan Agner return ret; 3948453e9dc4SStefan Agner } 3949453e9dc4SStefan Agner 3950619fee9eSFugang Duan ret = fec_enet_alloc_queue(ndev); 3951619fee9eSFugang Duan if (ret) 3952619fee9eSFugang Duan return ret; 395379f33912SNimrod Andy 39547355f276STroy Kisky bd_size = (fep->total_tx_ring_size + fep->total_rx_ring_size) * dsize; 3955793fc096SFrank Li 3956793fc096SFrank Li /* Allocate memory for buffer descriptors. */ 3957c0a1a0a6SLucas Stach cbd_base = dmam_alloc_coherent(&fep->pdev->dev, bd_size, &bd_dma, 3958793fc096SFrank Li GFP_KERNEL); 39594d494cdcSFugang Duan if (!cbd_base) { 3960619fee9eSFugang Duan ret = -ENOMEM; 3961619fee9eSFugang Duan goto free_queue_mem; 39624d494cdcSFugang Duan } 3963793fc096SFrank Li 3964793fc096SFrank Li /* Get the Ethernet address */ 3965052fcc45SFugang Duan ret = fec_get_mac(ndev); 3966052fcc45SFugang Duan if (ret) 3967052fcc45SFugang Duan goto free_queue_mem; 3968052fcc45SFugang Duan 396944934facSLucas Stach /* make sure MAC we just acquired is programmed into the hw */ 397044934facSLucas Stach fec_set_mac_address(ndev, NULL); 3971793fc096SFrank Li 3972793fc096SFrank Li /* Set receive and transmit descriptor base. */ 397359d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) { 39747355f276STroy Kisky struct fec_enet_priv_rx_q *rxq = fep->rx_queue[i]; 39757355f276STroy Kisky unsigned size = dsize * rxq->bd.ring_size; 39767355f276STroy Kisky 39777355f276STroy Kisky rxq->bd.qid = i; 39787355f276STroy Kisky rxq->bd.base = cbd_base; 39797355f276STroy Kisky rxq->bd.cur = cbd_base; 39807355f276STroy Kisky rxq->bd.dma = bd_dma; 39817355f276STroy Kisky rxq->bd.dsize = dsize; 39827355f276STroy Kisky rxq->bd.dsize_log2 = dsize_log2; 398353bb20d1STroy Kisky rxq->bd.reg_desc_active = fep->hwp + offset_des_active_rxq[i]; 39847355f276STroy Kisky bd_dma += size; 39857355f276STroy Kisky cbd_base = (struct bufdesc *)(((void *)cbd_base) + size); 39867355f276STroy Kisky rxq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize); 398759d0f746SFrank Li } 398859d0f746SFrank Li 398959d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 39907355f276STroy Kisky struct fec_enet_priv_tx_q *txq = fep->tx_queue[i]; 39917355f276STroy Kisky unsigned size = dsize * txq->bd.ring_size; 39927355f276STroy Kisky 39937355f276STroy Kisky txq->bd.qid = i; 39947355f276STroy Kisky txq->bd.base = cbd_base; 39957355f276STroy Kisky txq->bd.cur = cbd_base; 39967355f276STroy Kisky txq->bd.dma = bd_dma; 39977355f276STroy Kisky txq->bd.dsize = dsize; 39987355f276STroy Kisky txq->bd.dsize_log2 = dsize_log2; 399953bb20d1STroy Kisky txq->bd.reg_desc_active = fep->hwp + offset_des_active_txq[i]; 40007355f276STroy Kisky bd_dma += size; 40017355f276STroy Kisky cbd_base = (struct bufdesc *)(((void *)cbd_base) + size); 40027355f276STroy Kisky txq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize); 400359d0f746SFrank Li } 40044d494cdcSFugang Duan 4005793fc096SFrank Li 4006793fc096SFrank Li /* The FEC Ethernet specific entries in the device structure */ 4007793fc096SFrank Li ndev->watchdog_timeo = TX_TIMEOUT; 4008793fc096SFrank Li ndev->netdev_ops = &fec_netdev_ops; 4009793fc096SFrank Li ndev->ethtool_ops = &fec_enet_ethtool_ops; 4010793fc096SFrank Li 4011793fc096SFrank Li writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK); 4012b48b89f9SJakub Kicinski netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi); 4013793fc096SFrank Li 40146b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_VLAN) 4015cdffcf1bSJim Baxter /* enable hw VLAN support */ 4016cdffcf1bSJim Baxter ndev->features |= NETIF_F_HW_VLAN_CTAG_RX; 4017cdffcf1bSJim Baxter 40186b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_CSUM) { 4019ee8b7a11SJakub Kicinski netif_set_tso_max_segs(ndev, FEC_MAX_TSO_SEGS); 402079f33912SNimrod Andy 40214c09eed9SJim Baxter /* enable hw accelerator */ 40224c09eed9SJim Baxter ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM 402379f33912SNimrod Andy | NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO); 40244c09eed9SJim Baxter fep->csum_flags |= FLAG_RX_CSUM_ENABLED; 402548496255SShawn Guo } 40264c09eed9SJim Baxter 4027471ff445SJoakim Zhang if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) { 402841ef84ceSFugang Duan fep->tx_align = 0; 402941ef84ceSFugang Duan fep->rx_align = 0x3f; 403041ef84ceSFugang Duan } 403141ef84ceSFugang Duan 403209d1e541SNimrod Andy ndev->hw_features = ndev->features; 403309d1e541SNimrod Andy 4034ef83337dSRussell King fec_restart(ndev); 4035793fc096SFrank Li 40362b30842bSAndrew Lunn if (fep->quirks & FEC_QUIRK_MIB_CLEAR) 40372b30842bSAndrew Lunn fec_enet_clear_ethtool_stats(ndev); 40382b30842bSAndrew Lunn else 403980cca775SNikita Yushchenko fec_enet_update_ethtool_stats(ndev); 404080cca775SNikita Yushchenko 4041793fc096SFrank Li return 0; 4042619fee9eSFugang Duan 4043619fee9eSFugang Duan free_queue_mem: 4044619fee9eSFugang Duan fec_enet_free_queue(ndev); 4045619fee9eSFugang Duan return ret; 4046793fc096SFrank Li } 4047793fc096SFrank Li 4048793fc096SFrank Li #ifdef CONFIG_OF 40499269e556SFugang Duan static int fec_reset_phy(struct platform_device *pdev) 4050793fc096SFrank Li { 4051468ba54bSArnd Bergmann struct gpio_desc *phy_reset; 4052159a0760SQuentin Schulz int msec = 1, phy_post_delay = 0; 4053793fc096SFrank Li struct device_node *np = pdev->dev.of_node; 4054468ba54bSArnd Bergmann int err; 4055793fc096SFrank Li 4056793fc096SFrank Li if (!np) 40579269e556SFugang Duan return 0; 4058793fc096SFrank Li 405961e04ccbSFugang Duan err = of_property_read_u32(np, "phy-reset-duration", &msec); 4060793fc096SFrank Li /* A sane reset duration should not be longer than 1s */ 406161e04ccbSFugang Duan if (!err && msec > 1000) 4062793fc096SFrank Li msec = 1; 4063793fc096SFrank Li 4064159a0760SQuentin Schulz err = of_property_read_u32(np, "phy-reset-post-delay", &phy_post_delay); 4065159a0760SQuentin Schulz /* valid reset duration should be less than 1s */ 4066159a0760SQuentin Schulz if (!err && phy_post_delay > 1000) 4067159a0760SQuentin Schulz return -EINVAL; 4068159a0760SQuentin Schulz 4069d7b5e5ddSDmitry Torokhov phy_reset = devm_gpiod_get_optional(&pdev->dev, "phy-reset", 40700719bc3aSDmitry Torokhov GPIOD_OUT_HIGH); 4071468ba54bSArnd Bergmann if (IS_ERR(phy_reset)) 4072468ba54bSArnd Bergmann return dev_err_probe(&pdev->dev, PTR_ERR(phy_reset), 4073468ba54bSArnd Bergmann "failed to get phy-reset-gpios\n"); 4074eb37c563SStefan Wahren 4075d7b5e5ddSDmitry Torokhov if (!phy_reset) 4076d7b5e5ddSDmitry Torokhov return 0; 4077d7b5e5ddSDmitry Torokhov 4078eb37c563SStefan Wahren if (msec > 20) 4079793fc096SFrank Li msleep(msec); 4080eb37c563SStefan Wahren else 4081eb37c563SStefan Wahren usleep_range(msec * 1000, msec * 1000 + 1000); 4082eb37c563SStefan Wahren 40830719bc3aSDmitry Torokhov gpiod_set_value_cansleep(phy_reset, 0); 40849269e556SFugang Duan 4085159a0760SQuentin Schulz if (!phy_post_delay) 4086159a0760SQuentin Schulz return 0; 4087159a0760SQuentin Schulz 4088159a0760SQuentin Schulz if (phy_post_delay > 20) 4089159a0760SQuentin Schulz msleep(phy_post_delay); 4090159a0760SQuentin Schulz else 4091159a0760SQuentin Schulz usleep_range(phy_post_delay * 1000, 4092159a0760SQuentin Schulz phy_post_delay * 1000 + 1000); 4093159a0760SQuentin Schulz 40949269e556SFugang Duan return 0; 4095793fc096SFrank Li } 4096793fc096SFrank Li #else /* CONFIG_OF */ 40979269e556SFugang Duan static int fec_reset_phy(struct platform_device *pdev) 4098793fc096SFrank Li { 4099793fc096SFrank Li /* 4100793fc096SFrank Li * In case of platform probe, the reset has been done 4101793fc096SFrank Li * by machine code. 4102793fc096SFrank Li */ 41039269e556SFugang Duan return 0; 4104793fc096SFrank Li } 4105793fc096SFrank Li #endif /* CONFIG_OF */ 4106793fc096SFrank Li 41079fc095f1SFugang Duan static void 41089fc095f1SFugang Duan fec_enet_get_queue_num(struct platform_device *pdev, int *num_tx, int *num_rx) 41099fc095f1SFugang Duan { 41109fc095f1SFugang Duan struct device_node *np = pdev->dev.of_node; 41119fc095f1SFugang Duan 41129fc095f1SFugang Duan *num_tx = *num_rx = 1; 41139fc095f1SFugang Duan 41149fc095f1SFugang Duan if (!np || !of_device_is_available(np)) 41159fc095f1SFugang Duan return; 41169fc095f1SFugang Duan 41179fc095f1SFugang Duan /* parse the num of tx and rx queues */ 411873b1c90dSSaurabh Sengar of_property_read_u32(np, "fsl,num-tx-queues", num_tx); 4119b7bd75cfSFrank Li 412073b1c90dSSaurabh Sengar of_property_read_u32(np, "fsl,num-rx-queues", num_rx); 41219fc095f1SFugang Duan 41229fc095f1SFugang Duan if (*num_tx < 1 || *num_tx > FEC_ENET_MAX_TX_QS) { 4123b7bd75cfSFrank Li dev_warn(&pdev->dev, "Invalid num_tx(=%d), fall back to 1\n", 41249fc095f1SFugang Duan *num_tx); 41259fc095f1SFugang Duan *num_tx = 1; 41269fc095f1SFugang Duan return; 41279fc095f1SFugang Duan } 41289fc095f1SFugang Duan 41299fc095f1SFugang Duan if (*num_rx < 1 || *num_rx > FEC_ENET_MAX_RX_QS) { 4130b7bd75cfSFrank Li dev_warn(&pdev->dev, "Invalid num_rx(=%d), fall back to 1\n", 41319fc095f1SFugang Duan *num_rx); 41329fc095f1SFugang Duan *num_rx = 1; 41339fc095f1SFugang Duan return; 41349fc095f1SFugang Duan } 41359fc095f1SFugang Duan 41369fc095f1SFugang Duan } 41379fc095f1SFugang Duan 41384ad1ceecSTroy Kisky static int fec_enet_get_irq_cnt(struct platform_device *pdev) 41394ad1ceecSTroy Kisky { 41404ad1ceecSTroy Kisky int irq_cnt = platform_irq_count(pdev); 41414ad1ceecSTroy Kisky 41424ad1ceecSTroy Kisky if (irq_cnt > FEC_IRQ_NUM) 41434ad1ceecSTroy Kisky irq_cnt = FEC_IRQ_NUM; /* last for pps */ 41444ad1ceecSTroy Kisky else if (irq_cnt == 2) 41454ad1ceecSTroy Kisky irq_cnt = 1; /* last for pps */ 41464ad1ceecSTroy Kisky else if (irq_cnt <= 0) 41474ad1ceecSTroy Kisky irq_cnt = 1; /* At least 1 irq is needed */ 41484ad1ceecSTroy Kisky return irq_cnt; 41494ad1ceecSTroy Kisky } 41504ad1ceecSTroy Kisky 4151b7cdc965SJoakim Zhang static void fec_enet_get_wakeup_irq(struct platform_device *pdev) 4152b7cdc965SJoakim Zhang { 4153b7cdc965SJoakim Zhang struct net_device *ndev = platform_get_drvdata(pdev); 4154b7cdc965SJoakim Zhang struct fec_enet_private *fep = netdev_priv(ndev); 4155b7cdc965SJoakim Zhang 4156b7cdc965SJoakim Zhang if (fep->quirks & FEC_QUIRK_WAKEUP_FROM_INT2) 4157b7cdc965SJoakim Zhang fep->wake_irq = fep->irq[2]; 4158b7cdc965SJoakim Zhang else 4159b7cdc965SJoakim Zhang fep->wake_irq = fep->irq[0]; 4160b7cdc965SJoakim Zhang } 4161b7cdc965SJoakim Zhang 4162da722186SMartin Fuzzey static int fec_enet_init_stop_mode(struct fec_enet_private *fep, 4163da722186SMartin Fuzzey struct device_node *np) 4164da722186SMartin Fuzzey { 4165da722186SMartin Fuzzey struct device_node *gpr_np; 41668a448bf8SFugang Duan u32 out_val[3]; 4167da722186SMartin Fuzzey int ret = 0; 4168da722186SMartin Fuzzey 41698a448bf8SFugang Duan gpr_np = of_parse_phandle(np, "fsl,stop-mode", 0); 4170da722186SMartin Fuzzey if (!gpr_np) 4171da722186SMartin Fuzzey return 0; 4172da722186SMartin Fuzzey 41738a448bf8SFugang Duan ret = of_property_read_u32_array(np, "fsl,stop-mode", out_val, 41748a448bf8SFugang Duan ARRAY_SIZE(out_val)); 41758a448bf8SFugang Duan if (ret) { 41768a448bf8SFugang Duan dev_dbg(&fep->pdev->dev, "no stop mode property\n"); 4177d2b52ec0SYang Yingliang goto out; 41788a448bf8SFugang Duan } 41798a448bf8SFugang Duan 4180da722186SMartin Fuzzey fep->stop_gpr.gpr = syscon_node_to_regmap(gpr_np); 4181da722186SMartin Fuzzey if (IS_ERR(fep->stop_gpr.gpr)) { 4182da722186SMartin Fuzzey dev_err(&fep->pdev->dev, "could not find gpr regmap\n"); 4183da722186SMartin Fuzzey ret = PTR_ERR(fep->stop_gpr.gpr); 4184da722186SMartin Fuzzey fep->stop_gpr.gpr = NULL; 4185da722186SMartin Fuzzey goto out; 4186da722186SMartin Fuzzey } 4187da722186SMartin Fuzzey 41888a448bf8SFugang Duan fep->stop_gpr.reg = out_val[1]; 41898a448bf8SFugang Duan fep->stop_gpr.bit = out_val[2]; 4190da722186SMartin Fuzzey 4191da722186SMartin Fuzzey out: 4192da722186SMartin Fuzzey of_node_put(gpr_np); 4193da722186SMartin Fuzzey 4194da722186SMartin Fuzzey return ret; 4195da722186SMartin Fuzzey } 4196da722186SMartin Fuzzey 4197793fc096SFrank Li static int 4198793fc096SFrank Li fec_probe(struct platform_device *pdev) 4199793fc096SFrank Li { 4200793fc096SFrank Li struct fec_enet_private *fep; 4201793fc096SFrank Li struct fec_platform_data *pdata; 42020c65b2b9SAndrew Lunn phy_interface_t interface; 4203793fc096SFrank Li struct net_device *ndev; 4204793fc096SFrank Li int i, irq, ret = 0; 4205793fc096SFrank Li const struct of_device_id *of_id; 4206793fc096SFrank Li static int dev_id; 4207407066f8SUwe Kleine-König struct device_node *np = pdev->dev.of_node, *phy_node; 4208b7bd75cfSFrank Li int num_tx_qs; 4209b7bd75cfSFrank Li int num_rx_qs; 42104ad1ceecSTroy Kisky char irq_name[8]; 42114ad1ceecSTroy Kisky int irq_cnt; 4212da722186SMartin Fuzzey struct fec_devinfo *dev_info; 4213793fc096SFrank Li 42149fc095f1SFugang Duan fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs); 42159fc095f1SFugang Duan 4216793fc096SFrank Li /* Init network device */ 421780cca775SNikita Yushchenko ndev = alloc_etherdev_mqs(sizeof(struct fec_enet_private) + 4218f85de666SNikita Yushchenko FEC_STATS_SIZE, num_tx_qs, num_rx_qs); 4219793fc096SFrank Li if (!ndev) 4220793fc096SFrank Li return -ENOMEM; 4221793fc096SFrank Li 4222793fc096SFrank Li SET_NETDEV_DEV(ndev, &pdev->dev); 4223793fc096SFrank Li 4224793fc096SFrank Li /* setup board info structure */ 4225793fc096SFrank Li fep = netdev_priv(ndev); 4226793fc096SFrank Li 42276b7e4008SLothar Waßmann of_id = of_match_device(fec_dt_ids, &pdev->dev); 42286b7e4008SLothar Waßmann if (of_id) 42296b7e4008SLothar Waßmann pdev->id_entry = of_id->data; 4230da722186SMartin Fuzzey dev_info = (struct fec_devinfo *)pdev->id_entry->driver_data; 4231da722186SMartin Fuzzey if (dev_info) 4232da722186SMartin Fuzzey fep->quirks = dev_info->quirks; 42336b7e4008SLothar Waßmann 42340c818594SHubert Feurstein fep->netdev = ndev; 42359fc095f1SFugang Duan fep->num_rx_queues = num_rx_qs; 42369fc095f1SFugang Duan fep->num_tx_queues = num_tx_qs; 42379fc095f1SFugang Duan 4238d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 4239793fc096SFrank Li /* default enable pause frame auto negotiation */ 42406b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_GBIT) 4241793fc096SFrank Li fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG; 4242d1391930SGuenter Roeck #endif 4243793fc096SFrank Li 42445bbde4d2SNimrod Andy /* Select default pin state */ 42455bbde4d2SNimrod Andy pinctrl_pm_select_default_state(&pdev->dev); 42465bbde4d2SNimrod Andy 42474f830a5aSYueHaibing fep->hwp = devm_platform_ioremap_resource(pdev, 0); 4248941e173aSTushar Behera if (IS_ERR(fep->hwp)) { 4249941e173aSTushar Behera ret = PTR_ERR(fep->hwp); 4250941e173aSTushar Behera goto failed_ioremap; 4251941e173aSTushar Behera } 4252941e173aSTushar Behera 4253793fc096SFrank Li fep->pdev = pdev; 4254793fc096SFrank Li fep->dev_id = dev_id++; 4255793fc096SFrank Li 4256793fc096SFrank Li platform_set_drvdata(pdev, ndev); 4257793fc096SFrank Li 425829380905SLucas Stach if ((of_machine_is_compatible("fsl,imx6q") || 425929380905SLucas Stach of_machine_is_compatible("fsl,imx6dl")) && 426029380905SLucas Stach !of_property_read_bool(np, "fsl,err006687-workaround-present")) 426129380905SLucas Stach fep->quirks |= FEC_QUIRK_ERR006687; 426229380905SLucas Stach 426340c79ce1SWei Fang ret = fec_enet_ipc_handle_init(fep); 426440c79ce1SWei Fang if (ret) 426540c79ce1SWei Fang goto failed_ipc_init; 426640c79ce1SWei Fang 42671a87e641SRob Herring if (of_property_read_bool(np, "fsl,magic-packet")) 4268de40ed31SNimrod Andy fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET; 4269de40ed31SNimrod Andy 42708a448bf8SFugang Duan ret = fec_enet_init_stop_mode(fep, np); 4271da722186SMartin Fuzzey if (ret) 4272da722186SMartin Fuzzey goto failed_stop_mode; 4273da722186SMartin Fuzzey 4274407066f8SUwe Kleine-König phy_node = of_parse_phandle(np, "phy-handle", 0); 4275407066f8SUwe Kleine-König if (!phy_node && of_phy_is_fixed_link(np)) { 4276407066f8SUwe Kleine-König ret = of_phy_register_fixed_link(np); 4277407066f8SUwe Kleine-König if (ret < 0) { 4278407066f8SUwe Kleine-König dev_err(&pdev->dev, 4279407066f8SUwe Kleine-König "broken fixed-link specification\n"); 4280407066f8SUwe Kleine-König goto failed_phy; 4281407066f8SUwe Kleine-König } 4282407066f8SUwe Kleine-König phy_node = of_node_get(np); 4283407066f8SUwe Kleine-König } 4284407066f8SUwe Kleine-König fep->phy_node = phy_node; 4285407066f8SUwe Kleine-König 42860c65b2b9SAndrew Lunn ret = of_get_phy_mode(pdev->dev.of_node, &interface); 42870c65b2b9SAndrew Lunn if (ret) { 428894660ba0SJingoo Han pdata = dev_get_platdata(&pdev->dev); 4289793fc096SFrank Li if (pdata) 4290793fc096SFrank Li fep->phy_interface = pdata->phy; 4291793fc096SFrank Li else 4292793fc096SFrank Li fep->phy_interface = PHY_INTERFACE_MODE_MII; 4293793fc096SFrank Li } else { 42940c65b2b9SAndrew Lunn fep->phy_interface = interface; 4295793fc096SFrank Li } 4296793fc096SFrank Li 4297b820c114SJoakim Zhang ret = fec_enet_parse_rgmii_delay(fep, np); 4298b820c114SJoakim Zhang if (ret) 4299b820c114SJoakim Zhang goto failed_rgmii_delay; 4300b820c114SJoakim Zhang 4301793fc096SFrank Li fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); 4302793fc096SFrank Li if (IS_ERR(fep->clk_ipg)) { 4303793fc096SFrank Li ret = PTR_ERR(fep->clk_ipg); 4304793fc096SFrank Li goto failed_clk; 4305793fc096SFrank Li } 4306793fc096SFrank Li 4307793fc096SFrank Li fep->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); 4308793fc096SFrank Li if (IS_ERR(fep->clk_ahb)) { 4309793fc096SFrank Li ret = PTR_ERR(fep->clk_ahb); 4310793fc096SFrank Li goto failed_clk; 4311793fc096SFrank Li } 4312793fc096SFrank Li 4313d851b47bSFugang Duan fep->itr_clk_rate = clk_get_rate(fep->clk_ahb); 4314d851b47bSFugang Duan 431538f56f33SLinus Torvalds /* enet_out is optional, depends on board */ 43165ff851b7SUwe Kleine-König fep->clk_enet_out = devm_clk_get_optional(&pdev->dev, "enet_out"); 43175ff851b7SUwe Kleine-König if (IS_ERR(fep->clk_enet_out)) { 43185ff851b7SUwe Kleine-König ret = PTR_ERR(fep->clk_enet_out); 43195ff851b7SUwe Kleine-König goto failed_clk; 43205ff851b7SUwe Kleine-König } 432138f56f33SLinus Torvalds 432291c0d987SNimrod Andy fep->ptp_clk_on = false; 432301b825f9SFrancesco Dolcini mutex_init(&fep->ptp_clk_mutex); 43249b5330edSFugang Duan 43259b5330edSFugang Duan /* clk_ref is optional, depends on board */ 432643252ed1SUwe Kleine-König fep->clk_ref = devm_clk_get_optional(&pdev->dev, "enet_clk_ref"); 432743252ed1SUwe Kleine-König if (IS_ERR(fep->clk_ref)) { 432843252ed1SUwe Kleine-König ret = PTR_ERR(fep->clk_ref); 432943252ed1SUwe Kleine-König goto failed_clk; 433043252ed1SUwe Kleine-König } 4331b82f8c3fSFugang Duan fep->clk_ref_rate = clk_get_rate(fep->clk_ref); 43329b5330edSFugang Duan 4333fc539459SFugang Duan /* clk_2x_txclk is optional, depends on board */ 4334b820c114SJoakim Zhang if (fep->rgmii_txc_dly || fep->rgmii_rxc_dly) { 4335fc539459SFugang Duan fep->clk_2x_txclk = devm_clk_get(&pdev->dev, "enet_2x_txclk"); 4336fc539459SFugang Duan if (IS_ERR(fep->clk_2x_txclk)) 4337fc539459SFugang Duan fep->clk_2x_txclk = NULL; 4338b820c114SJoakim Zhang } 4339fc539459SFugang Duan 43406b7e4008SLothar Waßmann fep->bufdesc_ex = fep->quirks & FEC_QUIRK_HAS_BUFDESC_EX; 4341793fc096SFrank Li fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp"); 4342793fc096SFrank Li if (IS_ERR(fep->clk_ptp)) { 434338f56f33SLinus Torvalds fep->clk_ptp = NULL; 4344217b5844SLothar Waßmann fep->bufdesc_ex = false; 4345793fc096SFrank Li } 4346793fc096SFrank Li 4347e8fcfcd5SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 434813a097bdSFabio Estevam if (ret) 434913a097bdSFabio Estevam goto failed_clk; 435013a097bdSFabio Estevam 43518fff755eSAndrew Lunn ret = clk_prepare_enable(fep->clk_ipg); 43528fff755eSAndrew Lunn if (ret) 43538fff755eSAndrew Lunn goto failed_clk_ipg; 4354d7c3a206SAndy Duan ret = clk_prepare_enable(fep->clk_ahb); 4355d7c3a206SAndy Duan if (ret) 4356d7c3a206SAndy Duan goto failed_clk_ahb; 43578fff755eSAndrew Lunn 435825974d8aSStefan Agner fep->reg_phy = devm_regulator_get_optional(&pdev->dev, "phy"); 4359f4e9f3d2SFabio Estevam if (!IS_ERR(fep->reg_phy)) { 4360f4e9f3d2SFabio Estevam ret = regulator_enable(fep->reg_phy); 4361793fc096SFrank Li if (ret) { 4362793fc096SFrank Li dev_err(&pdev->dev, 4363793fc096SFrank Li "Failed to enable phy regulator: %d\n", ret); 4364793fc096SFrank Li goto failed_regulator; 4365793fc096SFrank Li } 4366f6a4d607SFabio Estevam } else { 43673f38c683SFugang Duan if (PTR_ERR(fep->reg_phy) == -EPROBE_DEFER) { 43683f38c683SFugang Duan ret = -EPROBE_DEFER; 43693f38c683SFugang Duan goto failed_regulator; 43703f38c683SFugang Duan } 4371f6a4d607SFabio Estevam fep->reg_phy = NULL; 4372793fc096SFrank Li } 4373793fc096SFrank Li 43748fff755eSAndrew Lunn pm_runtime_set_autosuspend_delay(&pdev->dev, FEC_MDIO_PM_TIMEOUT); 43758fff755eSAndrew Lunn pm_runtime_use_autosuspend(&pdev->dev); 437614d2b7c1SLucas Stach pm_runtime_get_noresume(&pdev->dev); 43778fff755eSAndrew Lunn pm_runtime_set_active(&pdev->dev); 43788fff755eSAndrew Lunn pm_runtime_enable(&pdev->dev); 43798fff755eSAndrew Lunn 43809269e556SFugang Duan ret = fec_reset_phy(pdev); 43819269e556SFugang Duan if (ret) 43829269e556SFugang Duan goto failed_reset; 4383793fc096SFrank Li 43844ad1ceecSTroy Kisky irq_cnt = fec_enet_get_irq_cnt(pdev); 4385793fc096SFrank Li if (fep->bufdesc_ex) 43864ad1ceecSTroy Kisky fec_ptp_init(pdev, irq_cnt); 4387793fc096SFrank Li 4388793fc096SFrank Li ret = fec_enet_init(ndev); 4389793fc096SFrank Li if (ret) 4390793fc096SFrank Li goto failed_init; 4391793fc096SFrank Li 43924ad1ceecSTroy Kisky for (i = 0; i < irq_cnt; i++) { 43933ded9f2bSArnd Bergmann snprintf(irq_name, sizeof(irq_name), "int%d", i); 43943b56be21SAnson Huang irq = platform_get_irq_byname_optional(pdev, irq_name); 43954ad1ceecSTroy Kisky if (irq < 0) 4396793fc096SFrank Li irq = platform_get_irq(pdev, i); 4397793fc096SFrank Li if (irq < 0) { 4398793fc096SFrank Li ret = irq; 4399793fc096SFrank Li goto failed_irq; 4400793fc096SFrank Li } 44010d9b2ab1SFabio Estevam ret = devm_request_irq(&pdev->dev, irq, fec_enet_interrupt, 440244a272ddSMichael Opdenacker 0, pdev->name, ndev); 44030d9b2ab1SFabio Estevam if (ret) 4404793fc096SFrank Li goto failed_irq; 4405de40ed31SNimrod Andy 4406de40ed31SNimrod Andy fep->irq[i] = irq; 4407793fc096SFrank Li } 4408793fc096SFrank Li 4409b7cdc965SJoakim Zhang /* Decide which interrupt line is wakeup capable */ 4410b7cdc965SJoakim Zhang fec_enet_get_wakeup_irq(pdev); 4411b7cdc965SJoakim Zhang 4412793fc096SFrank Li ret = fec_enet_mii_init(pdev); 4413793fc096SFrank Li if (ret) 4414793fc096SFrank Li goto failed_mii_init; 4415793fc096SFrank Li 4416793fc096SFrank Li /* Carrier starts down, phylib will bring it up */ 4417793fc096SFrank Li netif_carrier_off(ndev); 4418e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 44195bbde4d2SNimrod Andy pinctrl_pm_select_sleep_state(&pdev->dev); 4420793fc096SFrank Li 442159193053SAndrew Lunn ndev->max_mtu = PKT_MAXBUF_SIZE - ETH_HLEN - ETH_FCS_LEN; 442259193053SAndrew Lunn 4423793fc096SFrank Li ret = register_netdev(ndev); 4424793fc096SFrank Li if (ret) 4425793fc096SFrank Li goto failed_register; 4426793fc096SFrank Li 4427de40ed31SNimrod Andy device_init_wakeup(&ndev->dev, fep->wol_flag & 4428de40ed31SNimrod Andy FEC_WOL_HAS_MAGIC_PACKET); 4429de40ed31SNimrod Andy 4430eb1d0640SFabio Estevam if (fep->bufdesc_ex && fep->ptp_clock) 4431eb1d0640SFabio Estevam netdev_info(ndev, "registered PHC device %d\n", fep->dev_id); 4432eb1d0640SFabio Estevam 44331b7bde6dSNimrod Andy fep->rx_copybreak = COPYBREAK_DEFAULT; 443436cdc743SRussell King INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work); 44358fff755eSAndrew Lunn 44368fff755eSAndrew Lunn pm_runtime_mark_last_busy(&pdev->dev); 44378fff755eSAndrew Lunn pm_runtime_put_autosuspend(&pdev->dev); 44388fff755eSAndrew Lunn 4439793fc096SFrank Li return 0; 4440793fc096SFrank Li 4441793fc096SFrank Li failed_register: 4442793fc096SFrank Li fec_enet_mii_remove(fep); 4443793fc096SFrank Li failed_mii_init: 44447a2bbd8dSFabio Estevam failed_irq: 44457a2bbd8dSFabio Estevam failed_init: 444632cba57bSLucas Stach fec_ptp_stop(pdev); 44479269e556SFugang Duan failed_reset: 4448ce8d24f9SAndy Duan pm_runtime_put_noidle(&pdev->dev); 44499269e556SFugang Duan pm_runtime_disable(&pdev->dev); 4450c6165cf0SFugang Duan if (fep->reg_phy) 4451c6165cf0SFugang Duan regulator_disable(fep->reg_phy); 4452793fc096SFrank Li failed_regulator: 4453d7c3a206SAndy Duan clk_disable_unprepare(fep->clk_ahb); 4454d7c3a206SAndy Duan failed_clk_ahb: 4455d7c3a206SAndy Duan clk_disable_unprepare(fep->clk_ipg); 44568fff755eSAndrew Lunn failed_clk_ipg: 4457e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 4458793fc096SFrank Li failed_clk: 4459b820c114SJoakim Zhang failed_rgmii_delay: 446082005b1cSJohan Hovold if (of_phy_is_fixed_link(np)) 446182005b1cSJohan Hovold of_phy_deregister_fixed_link(np); 4462407066f8SUwe Kleine-König of_node_put(phy_node); 4463da722186SMartin Fuzzey failed_stop_mode: 446440c79ce1SWei Fang failed_ipc_init: 4465d1616f07SFugang Duan failed_phy: 4466d1616f07SFugang Duan dev_id--; 4467793fc096SFrank Li failed_ioremap: 4468793fc096SFrank Li free_netdev(ndev); 4469793fc096SFrank Li 4470793fc096SFrank Li return ret; 4471793fc096SFrank Li } 4472793fc096SFrank Li 4473793fc096SFrank Li static int 4474793fc096SFrank Li fec_drv_remove(struct platform_device *pdev) 4475793fc096SFrank Li { 4476793fc096SFrank Li struct net_device *ndev = platform_get_drvdata(pdev); 4477793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 447882005b1cSJohan Hovold struct device_node *np = pdev->dev.of_node; 4479a31eda65SChuhong Yuan int ret; 4480a31eda65SChuhong Yuan 4481*f816b982SUwe Kleine-König ret = pm_runtime_get_sync(&pdev->dev); 4482a31eda65SChuhong Yuan if (ret < 0) 4483*f816b982SUwe Kleine-König dev_err(&pdev->dev, 4484*f816b982SUwe Kleine-König "Failed to resume device in remove callback (%pe)\n", 4485*f816b982SUwe Kleine-König ERR_PTR(ret)); 4486793fc096SFrank Li 448736cdc743SRussell King cancel_work_sync(&fep->tx_timeout_work); 448832cba57bSLucas Stach fec_ptp_stop(pdev); 4489793fc096SFrank Li unregister_netdev(ndev); 4490793fc096SFrank Li fec_enet_mii_remove(fep); 4491f6a4d607SFabio Estevam if (fep->reg_phy) 4492f6a4d607SFabio Estevam regulator_disable(fep->reg_phy); 4493a31eda65SChuhong Yuan 449482005b1cSJohan Hovold if (of_phy_is_fixed_link(np)) 449582005b1cSJohan Hovold of_phy_deregister_fixed_link(np); 4496407066f8SUwe Kleine-König of_node_put(fep->phy_node); 4497793fc096SFrank Li 4498*f816b982SUwe Kleine-König /* After pm_runtime_get_sync() failed, the clks are still off, so skip 4499*f816b982SUwe Kleine-König * disabling them again. 4500*f816b982SUwe Kleine-König */ 4501*f816b982SUwe Kleine-König if (ret >= 0) { 4502a31eda65SChuhong Yuan clk_disable_unprepare(fep->clk_ahb); 4503a31eda65SChuhong Yuan clk_disable_unprepare(fep->clk_ipg); 4504*f816b982SUwe Kleine-König } 4505a31eda65SChuhong Yuan pm_runtime_put_noidle(&pdev->dev); 4506a31eda65SChuhong Yuan pm_runtime_disable(&pdev->dev); 4507a31eda65SChuhong Yuan 450844712965SPavel Skripkin free_netdev(ndev); 4509793fc096SFrank Li return 0; 4510793fc096SFrank Li } 4511793fc096SFrank Li 4512dd66d386SFabio Estevam static int __maybe_unused fec_suspend(struct device *dev) 4513793fc096SFrank Li { 4514793fc096SFrank Li struct net_device *ndev = dev_get_drvdata(dev); 4515793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 4516da970726SWei Fang int ret; 4517793fc096SFrank Li 4518da1774e5SRussell King rtnl_lock(); 4519793fc096SFrank Li if (netif_running(ndev)) { 4520de40ed31SNimrod Andy if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) 4521de40ed31SNimrod Andy fep->wol_flag |= FEC_WOL_FLAG_SLEEP_ON; 452245f5c327SPhilippe Reynes phy_stop(ndev->phydev); 452331a6de34SRussell King napi_disable(&fep->napi); 452431a6de34SRussell King netif_tx_lock_bh(ndev); 4525793fc096SFrank Li netif_device_detach(ndev); 452631a6de34SRussell King netif_tx_unlock_bh(ndev); 452731a6de34SRussell King fec_stop(ndev); 45280b6f65c7SJoakim Zhang if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) { 45290b6f65c7SJoakim Zhang fec_irqs_disable(ndev); 45305bbde4d2SNimrod Andy pinctrl_pm_select_sleep_state(&fep->pdev->dev); 45310b6f65c7SJoakim Zhang } else { 45320b6f65c7SJoakim Zhang fec_irqs_disable_except_wakeup(ndev); 45330b6f65c7SJoakim Zhang if (fep->wake_irq > 0) { 45340b6f65c7SJoakim Zhang disable_irq(fep->wake_irq); 45350b6f65c7SJoakim Zhang enable_irq_wake(fep->wake_irq); 45360b6f65c7SJoakim Zhang } 45370b6f65c7SJoakim Zhang fec_enet_stop_mode(fep, true); 45380b6f65c7SJoakim Zhang } 45390b6f65c7SJoakim Zhang /* It's safe to disable clocks since interrupts are masked */ 45400b6f65c7SJoakim Zhang fec_enet_clk_enable(ndev, false); 4541da970726SWei Fang 4542da970726SWei Fang fep->rpm_active = !pm_runtime_status_suspended(dev); 4543da970726SWei Fang if (fep->rpm_active) { 4544da970726SWei Fang ret = pm_runtime_force_suspend(dev); 4545da970726SWei Fang if (ret < 0) { 4546da970726SWei Fang rtnl_unlock(); 4547da970726SWei Fang return ret; 4548da970726SWei Fang } 4549da970726SWei Fang } 4550f4c4a4e0SNimrod Andy } 4551f4c4a4e0SNimrod Andy rtnl_unlock(); 4552793fc096SFrank Li 4553de40ed31SNimrod Andy if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) 4554238f7bc7SFabio Estevam regulator_disable(fep->reg_phy); 4555238f7bc7SFabio Estevam 4556858eeb7dSNimrod Andy /* SOC supply clock to phy, when clock is disabled, phy link down 4557858eeb7dSNimrod Andy * SOC control phy regulator, when regulator is disabled, phy link down 4558858eeb7dSNimrod Andy */ 4559858eeb7dSNimrod Andy if (fep->clk_enet_out || fep->reg_phy) 4560858eeb7dSNimrod Andy fep->link = 0; 4561858eeb7dSNimrod Andy 4562793fc096SFrank Li return 0; 4563793fc096SFrank Li } 4564793fc096SFrank Li 4565dd66d386SFabio Estevam static int __maybe_unused fec_resume(struct device *dev) 4566793fc096SFrank Li { 4567793fc096SFrank Li struct net_device *ndev = dev_get_drvdata(dev); 4568793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 4569238f7bc7SFabio Estevam int ret; 4570de40ed31SNimrod Andy int val; 4571238f7bc7SFabio Estevam 4572de40ed31SNimrod Andy if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) { 4573238f7bc7SFabio Estevam ret = regulator_enable(fep->reg_phy); 4574238f7bc7SFabio Estevam if (ret) 4575238f7bc7SFabio Estevam return ret; 4576238f7bc7SFabio Estevam } 4577793fc096SFrank Li 4578da1774e5SRussell King rtnl_lock(); 4579793fc096SFrank Li if (netif_running(ndev)) { 4580da970726SWei Fang if (fep->rpm_active) 4581da970726SWei Fang pm_runtime_force_resume(dev); 4582da970726SWei Fang 4583f4c4a4e0SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 4584f4c4a4e0SNimrod Andy if (ret) { 4585f4c4a4e0SNimrod Andy rtnl_unlock(); 4586f4c4a4e0SNimrod Andy goto failed_clk; 4587f4c4a4e0SNimrod Andy } 4588de40ed31SNimrod Andy if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) { 4589da722186SMartin Fuzzey fec_enet_stop_mode(fep, false); 45900b6f65c7SJoakim Zhang if (fep->wake_irq) { 45910b6f65c7SJoakim Zhang disable_irq_wake(fep->wake_irq); 45920b6f65c7SJoakim Zhang enable_irq(fep->wake_irq); 45930b6f65c7SJoakim Zhang } 4594da722186SMartin Fuzzey 4595de40ed31SNimrod Andy val = readl(fep->hwp + FEC_ECNTRL); 4596de40ed31SNimrod Andy val &= ~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP); 4597de40ed31SNimrod Andy writel(val, fep->hwp + FEC_ECNTRL); 4598de40ed31SNimrod Andy fep->wol_flag &= ~FEC_WOL_FLAG_SLEEP_ON; 4599de40ed31SNimrod Andy } else { 4600de40ed31SNimrod Andy pinctrl_pm_select_default_state(&fep->pdev->dev); 4601de40ed31SNimrod Andy } 4602ef83337dSRussell King fec_restart(ndev); 460331a6de34SRussell King netif_tx_lock_bh(ndev); 4604793fc096SFrank Li netif_device_attach(ndev); 46056af42d42SRussell King netif_tx_unlock_bh(ndev); 46066af42d42SRussell King napi_enable(&fep->napi); 4607557d5dc8SHeiner Kallweit phy_init_hw(ndev->phydev); 460845f5c327SPhilippe Reynes phy_start(ndev->phydev); 4609793fc096SFrank Li } 4610da1774e5SRussell King rtnl_unlock(); 4611793fc096SFrank Li 4612793fc096SFrank Li return 0; 461313a097bdSFabio Estevam 4614e8fcfcd5SNimrod Andy failed_clk: 461513a097bdSFabio Estevam if (fep->reg_phy) 461613a097bdSFabio Estevam regulator_disable(fep->reg_phy); 461713a097bdSFabio Estevam return ret; 4618793fc096SFrank Li } 4619793fc096SFrank Li 46208fff755eSAndrew Lunn static int __maybe_unused fec_runtime_suspend(struct device *dev) 46218fff755eSAndrew Lunn { 46228fff755eSAndrew Lunn struct net_device *ndev = dev_get_drvdata(dev); 46238fff755eSAndrew Lunn struct fec_enet_private *fep = netdev_priv(ndev); 46248fff755eSAndrew Lunn 4625d7c3a206SAndy Duan clk_disable_unprepare(fep->clk_ahb); 46268fff755eSAndrew Lunn clk_disable_unprepare(fep->clk_ipg); 46278fff755eSAndrew Lunn 46288fff755eSAndrew Lunn return 0; 46298fff755eSAndrew Lunn } 46308fff755eSAndrew Lunn 46318fff755eSAndrew Lunn static int __maybe_unused fec_runtime_resume(struct device *dev) 46328fff755eSAndrew Lunn { 46338fff755eSAndrew Lunn struct net_device *ndev = dev_get_drvdata(dev); 46348fff755eSAndrew Lunn struct fec_enet_private *fep = netdev_priv(ndev); 4635d7c3a206SAndy Duan int ret; 46368fff755eSAndrew Lunn 4637d7c3a206SAndy Duan ret = clk_prepare_enable(fep->clk_ahb); 4638d7c3a206SAndy Duan if (ret) 4639d7c3a206SAndy Duan return ret; 4640d7c3a206SAndy Duan ret = clk_prepare_enable(fep->clk_ipg); 4641d7c3a206SAndy Duan if (ret) 4642d7c3a206SAndy Duan goto failed_clk_ipg; 4643d7c3a206SAndy Duan 4644d7c3a206SAndy Duan return 0; 4645d7c3a206SAndy Duan 4646d7c3a206SAndy Duan failed_clk_ipg: 4647d7c3a206SAndy Duan clk_disable_unprepare(fep->clk_ahb); 4648d7c3a206SAndy Duan return ret; 46498fff755eSAndrew Lunn } 46508fff755eSAndrew Lunn 46518fff755eSAndrew Lunn static const struct dev_pm_ops fec_pm_ops = { 46528fff755eSAndrew Lunn SET_SYSTEM_SLEEP_PM_OPS(fec_suspend, fec_resume) 46538fff755eSAndrew Lunn SET_RUNTIME_PM_OPS(fec_runtime_suspend, fec_runtime_resume, NULL) 46548fff755eSAndrew Lunn }; 4655793fc096SFrank Li 4656793fc096SFrank Li static struct platform_driver fec_driver = { 4657793fc096SFrank Li .driver = { 4658793fc096SFrank Li .name = DRIVER_NAME, 4659793fc096SFrank Li .pm = &fec_pm_ops, 4660793fc096SFrank Li .of_match_table = fec_dt_ids, 4661272bb0e9SFabio Estevam .suppress_bind_attrs = true, 4662793fc096SFrank Li }, 4663793fc096SFrank Li .id_table = fec_devtype, 4664793fc096SFrank Li .probe = fec_probe, 4665793fc096SFrank Li .remove = fec_drv_remove, 4666793fc096SFrank Li }; 4667793fc096SFrank Li 4668793fc096SFrank Li module_platform_driver(fec_driver); 4669793fc096SFrank Li 4670793fc096SFrank Li MODULE_LICENSE("GPL"); 4671