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> 59793fc096SFrank Li #include <linux/of_gpio.h> 60407066f8SUwe Kleine-König #include <linux/of_mdio.h> 61793fc096SFrank Li #include <linux/of_net.h> 62793fc096SFrank Li #include <linux/regulator/consumer.h> 63cdffcf1bSJim Baxter #include <linux/if_vlan.h> 64a68ab98eSFabio Estevam #include <linux/pinctrl/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 92da722186SMartin Fuzzey struct fec_devinfo { 93da722186SMartin Fuzzey u32 quirks; 94da722186SMartin Fuzzey }; 95da722186SMartin Fuzzey 96da722186SMartin Fuzzey static const struct fec_devinfo fec_imx25_info = { 97da722186SMartin Fuzzey .quirks = FEC_QUIRK_USE_GASKET | FEC_QUIRK_MIB_CLEAR | 98da722186SMartin Fuzzey FEC_QUIRK_HAS_FRREG, 99da722186SMartin Fuzzey }; 100da722186SMartin Fuzzey 101da722186SMartin Fuzzey static const struct fec_devinfo fec_imx27_info = { 102da722186SMartin Fuzzey .quirks = FEC_QUIRK_MIB_CLEAR | FEC_QUIRK_HAS_FRREG, 103da722186SMartin Fuzzey }; 104da722186SMartin Fuzzey 105da722186SMartin Fuzzey static const struct fec_devinfo fec_imx28_info = { 106da722186SMartin Fuzzey .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME | 107da722186SMartin Fuzzey FEC_QUIRK_SINGLE_MDIO | FEC_QUIRK_HAS_RACC | 108c730ab42SLaurent Badel FEC_QUIRK_HAS_FRREG | FEC_QUIRK_CLEAR_SETUP_MII | 109c730ab42SLaurent Badel FEC_QUIRK_NO_HARD_RESET, 110da722186SMartin Fuzzey }; 111da722186SMartin Fuzzey 112da722186SMartin Fuzzey static const struct fec_devinfo fec_imx6q_info = { 113da722186SMartin Fuzzey .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 114da722186SMartin Fuzzey FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 115da722186SMartin Fuzzey FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR006358 | 1167d650df9SWei Fang FEC_QUIRK_HAS_RACC | FEC_QUIRK_CLEAR_SETUP_MII | 1177d650df9SWei Fang FEC_QUIRK_HAS_PMQOS, 118da722186SMartin Fuzzey }; 119da722186SMartin Fuzzey 120da722186SMartin Fuzzey static const struct fec_devinfo fec_mvf600_info = { 121da722186SMartin Fuzzey .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_RACC, 122da722186SMartin Fuzzey }; 123da722186SMartin Fuzzey 124da722186SMartin Fuzzey static const struct fec_devinfo fec_imx6x_info = { 125da722186SMartin Fuzzey .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 126da722186SMartin Fuzzey FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 127da722186SMartin Fuzzey FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | 128da722186SMartin Fuzzey FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | 1291e6114f5SGreg Ungerer FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE | 130471ff445SJoakim Zhang FEC_QUIRK_CLEAR_SETUP_MII | FEC_QUIRK_HAS_MULTI_QUEUES, 131da722186SMartin Fuzzey }; 132da722186SMartin Fuzzey 133da722186SMartin Fuzzey static const struct fec_devinfo fec_imx6ul_info = { 134da722186SMartin Fuzzey .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 135da722186SMartin Fuzzey FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 136da722186SMartin Fuzzey FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR007885 | 137da722186SMartin Fuzzey FEC_QUIRK_BUG_CAPTURE | FEC_QUIRK_HAS_RACC | 1381e6114f5SGreg Ungerer FEC_QUIRK_HAS_COALESCE | FEC_QUIRK_CLEAR_SETUP_MII, 139da722186SMartin Fuzzey }; 140da722186SMartin Fuzzey 141947240ebSFugang Duan static const struct fec_devinfo fec_imx8mq_info = { 142947240ebSFugang Duan .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 143947240ebSFugang Duan FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 144947240ebSFugang Duan FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | 145947240ebSFugang Duan FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | 146947240ebSFugang Duan FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE | 147947240ebSFugang Duan FEC_QUIRK_CLEAR_SETUP_MII | FEC_QUIRK_HAS_MULTI_QUEUES | 148b7cdc965SJoakim Zhang FEC_QUIRK_HAS_EEE | FEC_QUIRK_WAKEUP_FROM_INT2, 149947240ebSFugang Duan }; 150947240ebSFugang Duan 151947240ebSFugang Duan static const struct fec_devinfo fec_imx8qm_info = { 152947240ebSFugang Duan .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 153947240ebSFugang Duan FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 154947240ebSFugang Duan FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | 155947240ebSFugang Duan FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | 156947240ebSFugang Duan FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE | 157947240ebSFugang Duan FEC_QUIRK_CLEAR_SETUP_MII | FEC_QUIRK_HAS_MULTI_QUEUES | 158947240ebSFugang Duan FEC_QUIRK_DELAYED_CLKS_SUPPORT, 159947240ebSFugang Duan }; 160947240ebSFugang Duan 161167d5fe0SWei Fang static const struct fec_devinfo fec_s32v234_info = { 162167d5fe0SWei Fang .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 163167d5fe0SWei Fang FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 164167d5fe0SWei Fang FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | 165167d5fe0SWei Fang FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE, 166167d5fe0SWei Fang }; 167167d5fe0SWei Fang 168793fc096SFrank Li static struct platform_device_id fec_devtype[] = { 169793fc096SFrank Li { 170793fc096SFrank Li /* keep it for coldfire */ 171793fc096SFrank Li .name = DRIVER_NAME, 172793fc096SFrank Li .driver_data = 0, 173793fc096SFrank Li }, { 174793fc096SFrank Li .name = "imx25-fec", 175da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_imx25_info, 176793fc096SFrank Li }, { 177793fc096SFrank Li .name = "imx27-fec", 178da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_imx27_info, 179793fc096SFrank Li }, { 180793fc096SFrank Li .name = "imx28-fec", 181da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_imx28_info, 182793fc096SFrank Li }, { 183793fc096SFrank Li .name = "imx6q-fec", 184da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_imx6q_info, 185793fc096SFrank Li }, { 18636803542SShawn Guo .name = "mvf600-fec", 187da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_mvf600_info, 188ca7c4a45SJingchang Lu }, { 18995a77470SFugang Duan .name = "imx6sx-fec", 190da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_imx6x_info, 19195a77470SFugang Duan }, { 192a51d3ab5SFugang Duan .name = "imx6ul-fec", 193da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_imx6ul_info, 194a51d3ab5SFugang Duan }, { 195947240ebSFugang Duan .name = "imx8mq-fec", 196947240ebSFugang Duan .driver_data = (kernel_ulong_t)&fec_imx8mq_info, 197947240ebSFugang Duan }, { 198947240ebSFugang Duan .name = "imx8qm-fec", 199947240ebSFugang Duan .driver_data = (kernel_ulong_t)&fec_imx8qm_info, 200947240ebSFugang Duan }, { 201167d5fe0SWei Fang .name = "s32v234-fec", 202167d5fe0SWei Fang .driver_data = (kernel_ulong_t)&fec_s32v234_info, 203167d5fe0SWei Fang }, { 204793fc096SFrank Li /* sentinel */ 205793fc096SFrank Li } 206793fc096SFrank Li }; 207793fc096SFrank Li MODULE_DEVICE_TABLE(platform, fec_devtype); 208793fc096SFrank Li 209793fc096SFrank Li enum imx_fec_type { 210793fc096SFrank Li IMX25_FEC = 1, /* runs on i.mx25/50/53 */ 211793fc096SFrank Li IMX27_FEC, /* runs on i.mx27/35/51 */ 212793fc096SFrank Li IMX28_FEC, 213793fc096SFrank Li IMX6Q_FEC, 21436803542SShawn Guo MVF600_FEC, 215ba593e00SFugang Duan IMX6SX_FEC, 216a51d3ab5SFugang Duan IMX6UL_FEC, 217947240ebSFugang Duan IMX8MQ_FEC, 218947240ebSFugang Duan IMX8QM_FEC, 219167d5fe0SWei Fang S32V234_FEC, 220793fc096SFrank Li }; 221793fc096SFrank Li 222793fc096SFrank Li static const struct of_device_id fec_dt_ids[] = { 223793fc096SFrank Li { .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], }, 224793fc096SFrank Li { .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], }, 225793fc096SFrank Li { .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], }, 226793fc096SFrank Li { .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], }, 22736803542SShawn Guo { .compatible = "fsl,mvf600-fec", .data = &fec_devtype[MVF600_FEC], }, 228ba593e00SFugang Duan { .compatible = "fsl,imx6sx-fec", .data = &fec_devtype[IMX6SX_FEC], }, 229a51d3ab5SFugang Duan { .compatible = "fsl,imx6ul-fec", .data = &fec_devtype[IMX6UL_FEC], }, 230947240ebSFugang Duan { .compatible = "fsl,imx8mq-fec", .data = &fec_devtype[IMX8MQ_FEC], }, 231947240ebSFugang Duan { .compatible = "fsl,imx8qm-fec", .data = &fec_devtype[IMX8QM_FEC], }, 232167d5fe0SWei Fang { .compatible = "fsl,s32v234-fec", .data = &fec_devtype[S32V234_FEC], }, 233793fc096SFrank Li { /* sentinel */ } 234793fc096SFrank Li }; 235793fc096SFrank Li MODULE_DEVICE_TABLE(of, fec_dt_ids); 236793fc096SFrank Li 237793fc096SFrank Li static unsigned char macaddr[ETH_ALEN]; 238793fc096SFrank Li module_param_array(macaddr, byte, NULL, 0); 239793fc096SFrank Li MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); 240793fc096SFrank Li 241793fc096SFrank Li #if defined(CONFIG_M5272) 242793fc096SFrank Li /* 243793fc096SFrank Li * Some hardware gets it MAC address out of local flash memory. 244793fc096SFrank Li * if this is non-zero then assume it is the address to get MAC from. 245793fc096SFrank Li */ 246793fc096SFrank Li #if defined(CONFIG_NETtel) 247793fc096SFrank Li #define FEC_FLASHMAC 0xf0006006 248793fc096SFrank Li #elif defined(CONFIG_GILBARCONAP) || defined(CONFIG_SCALES) 249793fc096SFrank Li #define FEC_FLASHMAC 0xf0006000 250793fc096SFrank Li #elif defined(CONFIG_CANCam) 251793fc096SFrank Li #define FEC_FLASHMAC 0xf0020000 252793fc096SFrank Li #elif defined (CONFIG_M5272C3) 253793fc096SFrank Li #define FEC_FLASHMAC (0xffe04000 + 4) 254793fc096SFrank Li #elif defined(CONFIG_MOD5272) 255793fc096SFrank Li #define FEC_FLASHMAC 0xffc0406b 256793fc096SFrank Li #else 257793fc096SFrank Li #define FEC_FLASHMAC 0 258793fc096SFrank Li #endif 259793fc096SFrank Li #endif /* CONFIG_M5272 */ 260793fc096SFrank Li 261cdffcf1bSJim Baxter /* The FEC stores dest/src/type/vlan, data, and checksum for receive packets. 262fbbeefddSAndrew Lunn * 263fbbeefddSAndrew Lunn * 2048 byte skbufs are allocated. However, alignment requirements 264fbbeefddSAndrew Lunn * varies between FEC variants. Worst case is 64, so round down by 64. 265793fc096SFrank Li */ 266fbbeefddSAndrew Lunn #define PKT_MAXBUF_SIZE (round_down(2048 - 64, 64)) 267793fc096SFrank Li #define PKT_MINBUF_SIZE 64 268793fc096SFrank Li 2694c09eed9SJim Baxter /* FEC receive acceleration */ 2704c09eed9SJim Baxter #define FEC_RACC_IPDIS (1 << 1) 2714c09eed9SJim Baxter #define FEC_RACC_PRODIS (1 << 2) 2723ac72b7bSEric Nelson #define FEC_RACC_SHIFT16 BIT(7) 2734c09eed9SJim Baxter #define FEC_RACC_OPTIONS (FEC_RACC_IPDIS | FEC_RACC_PRODIS) 2744c09eed9SJim Baxter 2752b30842bSAndrew Lunn /* MIB Control Register */ 2762b30842bSAndrew Lunn #define FEC_MIB_CTRLSTAT_DISABLE BIT(31) 2772b30842bSAndrew Lunn 278793fc096SFrank Li /* 279793fc096SFrank Li * The 5270/5271/5280/5282/532x RX control register also contains maximum frame 280793fc096SFrank Li * size bits. Other FEC hardware does not, so we need to take that into 281793fc096SFrank Li * account when setting it. 282793fc096SFrank Li */ 283793fc096SFrank Li #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ 2843f1dcc6aSLucas Stach defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ 2853f1dcc6aSLucas Stach defined(CONFIG_ARM64) 286793fc096SFrank Li #define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16) 287793fc096SFrank Li #else 288793fc096SFrank Li #define OPT_FRAME_SIZE 0 289793fc096SFrank Li #endif 290793fc096SFrank Li 291793fc096SFrank Li /* FEC MII MMFR bits definition */ 292793fc096SFrank Li #define FEC_MMFR_ST (1 << 30) 293d3ee8ec7SMarco Hartmann #define FEC_MMFR_ST_C45 (0) 294793fc096SFrank Li #define FEC_MMFR_OP_READ (2 << 28) 295d3ee8ec7SMarco Hartmann #define FEC_MMFR_OP_READ_C45 (3 << 28) 296793fc096SFrank Li #define FEC_MMFR_OP_WRITE (1 << 28) 297d3ee8ec7SMarco Hartmann #define FEC_MMFR_OP_ADDR_WRITE (0) 298793fc096SFrank Li #define FEC_MMFR_PA(v) ((v & 0x1f) << 23) 299793fc096SFrank Li #define FEC_MMFR_RA(v) ((v & 0x1f) << 18) 300793fc096SFrank Li #define FEC_MMFR_TA (2 << 16) 301793fc096SFrank Li #define FEC_MMFR_DATA(v) (v & 0xffff) 302de40ed31SNimrod Andy /* FEC ECR bits definition */ 3037b15515fSFrancesco Dolcini #define FEC_ECR_MAGICEN (1 << 2) 3047b15515fSFrancesco Dolcini #define FEC_ECR_SLEEP (1 << 3) 305793fc096SFrank Li 306793fc096SFrank Li #define FEC_MII_TIMEOUT 30000 /* us */ 307793fc096SFrank Li 308793fc096SFrank Li /* Transmitter timeout */ 309793fc096SFrank Li #define TX_TIMEOUT (2 * HZ) 310793fc096SFrank Li 311793fc096SFrank Li #define FEC_PAUSE_FLAG_AUTONEG 0x1 312793fc096SFrank Li #define FEC_PAUSE_FLAG_ENABLE 0x2 313de40ed31SNimrod Andy #define FEC_WOL_HAS_MAGIC_PACKET (0x1 << 0) 314de40ed31SNimrod Andy #define FEC_WOL_FLAG_ENABLE (0x1 << 1) 315de40ed31SNimrod Andy #define FEC_WOL_FLAG_SLEEP_ON (0x1 << 2) 316793fc096SFrank Li 3171b7bde6dSNimrod Andy #define COPYBREAK_DEFAULT 256 3181b7bde6dSNimrod Andy 31979f33912SNimrod Andy /* Max number of allowed TCP segments for software TSO */ 32079f33912SNimrod Andy #define FEC_MAX_TSO_SEGS 100 32179f33912SNimrod Andy #define FEC_MAX_SKB_DESCS (FEC_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS) 32279f33912SNimrod Andy 32379f33912SNimrod Andy #define IS_TSO_HEADER(txq, addr) \ 32479f33912SNimrod Andy ((addr >= txq->tso_hdrs_dma) && \ 3257355f276STroy Kisky (addr < txq->tso_hdrs_dma + txq->bd.ring_size * TSO_HEADER_SIZE)) 32679f33912SNimrod Andy 327793fc096SFrank Li static int mii_cnt; 328793fc096SFrank Li 3297355f276STroy Kisky static struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, 3307355f276STroy Kisky struct bufdesc_prop *bd) 331793fc096SFrank Li { 3327355f276STroy Kisky return (bdp >= bd->last) ? bd->base 333145d6e29SFugang Duan : (struct bufdesc *)(((void *)bdp) + bd->dsize); 334793fc096SFrank Li } 335793fc096SFrank Li 3367355f276STroy Kisky static struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, 3377355f276STroy Kisky struct bufdesc_prop *bd) 33836e24e2eSDuan Fugang-B38611 { 3397355f276STroy Kisky return (bdp <= bd->base) ? bd->last 340145d6e29SFugang Duan : (struct bufdesc *)(((void *)bdp) - bd->dsize); 34136e24e2eSDuan Fugang-B38611 } 34236e24e2eSDuan Fugang-B38611 3437355f276STroy Kisky static int fec_enet_get_bd_index(struct bufdesc *bdp, 3447355f276STroy Kisky struct bufdesc_prop *bd) 34561a4427bSNimrod Andy { 3467355f276STroy Kisky return ((const char *)bdp - (const char *)bd->base) >> bd->dsize_log2; 34761a4427bSNimrod Andy } 34861a4427bSNimrod Andy 3497355f276STroy Kisky static int fec_enet_get_free_txdesc_num(struct fec_enet_priv_tx_q *txq) 3506e909283SNimrod Andy { 3516e909283SNimrod Andy int entries; 3526e909283SNimrod Andy 3537355f276STroy Kisky entries = (((const char *)txq->dirty_tx - 3547355f276STroy Kisky (const char *)txq->bd.cur) >> txq->bd.dsize_log2) - 1; 3556e909283SNimrod Andy 3567355f276STroy Kisky return entries >= 0 ? entries : entries + txq->bd.ring_size; 3576e909283SNimrod Andy } 3586e909283SNimrod Andy 359c20e599bSLothar Waßmann static void swap_buffer(void *bufaddr, int len) 360793fc096SFrank Li { 361793fc096SFrank Li int i; 362793fc096SFrank Li unsigned int *buf = bufaddr; 363793fc096SFrank Li 3647b487d07SLothar Waßmann for (i = 0; i < len; i += 4, buf++) 365e453789aSLothar Waßmann swab32s(buf); 366793fc096SFrank Li } 367793fc096SFrank Li 3681310b544SLothar Waßmann static void swap_buffer2(void *dst_buf, void *src_buf, int len) 3691310b544SLothar Waßmann { 3701310b544SLothar Waßmann int i; 3711310b544SLothar Waßmann unsigned int *src = src_buf; 3721310b544SLothar Waßmann unsigned int *dst = dst_buf; 3731310b544SLothar Waßmann 3741310b544SLothar Waßmann for (i = 0; i < len; i += 4, src++, dst++) 3751310b544SLothar Waßmann *dst = swab32p(src); 3761310b544SLothar Waßmann } 3771310b544SLothar Waßmann 378344756f6SRussell King static void fec_dump(struct net_device *ndev) 379344756f6SRussell King { 380344756f6SRussell King struct fec_enet_private *fep = netdev_priv(ndev); 3814d494cdcSFugang Duan struct bufdesc *bdp; 3824d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 3834d494cdcSFugang Duan int index = 0; 384344756f6SRussell King 385344756f6SRussell King netdev_info(ndev, "TX ring dump\n"); 386344756f6SRussell King pr_info("Nr SC addr len SKB\n"); 387344756f6SRussell King 3884d494cdcSFugang Duan txq = fep->tx_queue[0]; 3897355f276STroy Kisky bdp = txq->bd.base; 3904d494cdcSFugang Duan 391344756f6SRussell King do { 3925cfa3039SJohannes Berg pr_info("%3u %c%c 0x%04x 0x%08x %4u %p\n", 393344756f6SRussell King index, 3947355f276STroy Kisky bdp == txq->bd.cur ? 'S' : ' ', 3954d494cdcSFugang Duan bdp == txq->dirty_tx ? 'H' : ' ', 3965cfa3039SJohannes Berg fec16_to_cpu(bdp->cbd_sc), 3975cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 3985cfa3039SJohannes Berg fec16_to_cpu(bdp->cbd_datlen), 3994d494cdcSFugang Duan txq->tx_skbuff[index]); 4007355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 401344756f6SRussell King index++; 4027355f276STroy Kisky } while (bdp != txq->bd.base); 403344756f6SRussell King } 404344756f6SRussell King 40562a02c98SFugang Duan static inline bool is_ipv4_pkt(struct sk_buff *skb) 40662a02c98SFugang Duan { 40762a02c98SFugang Duan return skb->protocol == htons(ETH_P_IP) && ip_hdr(skb)->version == 4; 40862a02c98SFugang Duan } 40962a02c98SFugang Duan 4104c09eed9SJim Baxter static int 4114c09eed9SJim Baxter fec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev) 4124c09eed9SJim Baxter { 4134c09eed9SJim Baxter /* Only run for packets requiring a checksum. */ 4144c09eed9SJim Baxter if (skb->ip_summed != CHECKSUM_PARTIAL) 4154c09eed9SJim Baxter return 0; 4164c09eed9SJim Baxter 4174c09eed9SJim Baxter if (unlikely(skb_cow_head(skb, 0))) 4184c09eed9SJim Baxter return -1; 4194c09eed9SJim Baxter 42062a02c98SFugang Duan if (is_ipv4_pkt(skb)) 42196c50caaSNimrod Andy ip_hdr(skb)->check = 0; 4224c09eed9SJim Baxter *(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) = 0; 4234c09eed9SJim Baxter 4244c09eed9SJim Baxter return 0; 4254c09eed9SJim Baxter } 4264c09eed9SJim Baxter 42795698ff6SShenwei Wang static int 42895698ff6SShenwei Wang fec_enet_create_page_pool(struct fec_enet_private *fep, 42995698ff6SShenwei Wang struct fec_enet_priv_rx_q *rxq, int size) 43095698ff6SShenwei Wang { 43195698ff6SShenwei Wang struct page_pool_params pp_params = { 43295698ff6SShenwei Wang .order = 0, 43395698ff6SShenwei Wang .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV, 43495698ff6SShenwei Wang .pool_size = size, 43595698ff6SShenwei Wang .nid = dev_to_node(&fep->pdev->dev), 43695698ff6SShenwei Wang .dev = &fep->pdev->dev, 43795698ff6SShenwei Wang .dma_dir = DMA_FROM_DEVICE, 43895698ff6SShenwei Wang .offset = FEC_ENET_XDP_HEADROOM, 43995698ff6SShenwei Wang .max_len = FEC_ENET_RX_FRSIZE, 44095698ff6SShenwei Wang }; 44195698ff6SShenwei Wang int err; 44295698ff6SShenwei Wang 44395698ff6SShenwei Wang rxq->page_pool = page_pool_create(&pp_params); 44495698ff6SShenwei Wang if (IS_ERR(rxq->page_pool)) { 44595698ff6SShenwei Wang err = PTR_ERR(rxq->page_pool); 44695698ff6SShenwei Wang rxq->page_pool = NULL; 44795698ff6SShenwei Wang return err; 44895698ff6SShenwei Wang } 44995698ff6SShenwei Wang 45095698ff6SShenwei Wang err = xdp_rxq_info_reg(&rxq->xdp_rxq, fep->netdev, rxq->id, 0); 45195698ff6SShenwei Wang if (err < 0) 45295698ff6SShenwei Wang goto err_free_pp; 45395698ff6SShenwei Wang 45495698ff6SShenwei Wang err = xdp_rxq_info_reg_mem_model(&rxq->xdp_rxq, MEM_TYPE_PAGE_POOL, 45595698ff6SShenwei Wang rxq->page_pool); 45695698ff6SShenwei Wang if (err) 45795698ff6SShenwei Wang goto err_unregister_rxq; 45895698ff6SShenwei Wang 45995698ff6SShenwei Wang return 0; 46095698ff6SShenwei Wang 46195698ff6SShenwei Wang err_unregister_rxq: 46295698ff6SShenwei Wang xdp_rxq_info_unreg(&rxq->xdp_rxq); 46395698ff6SShenwei Wang err_free_pp: 46495698ff6SShenwei Wang page_pool_destroy(rxq->page_pool); 46595698ff6SShenwei Wang rxq->page_pool = NULL; 46695698ff6SShenwei Wang return err; 46795698ff6SShenwei Wang } 46895698ff6SShenwei Wang 469c4bc44c6SKevin Hao static struct bufdesc * 4704d494cdcSFugang Duan fec_enet_txq_submit_frag_skb(struct fec_enet_priv_tx_q *txq, 4714d494cdcSFugang Duan struct sk_buff *skb, 4724d494cdcSFugang Duan struct net_device *ndev) 4736e909283SNimrod Andy { 4746e909283SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 4757355f276STroy Kisky struct bufdesc *bdp = txq->bd.cur; 4766e909283SNimrod Andy struct bufdesc_ex *ebdp; 4776e909283SNimrod Andy int nr_frags = skb_shinfo(skb)->nr_frags; 4786e909283SNimrod Andy int frag, frag_len; 4796e909283SNimrod Andy unsigned short status; 4806e909283SNimrod Andy unsigned int estatus = 0; 4816e909283SNimrod Andy skb_frag_t *this_frag; 4826e909283SNimrod Andy unsigned int index; 4836e909283SNimrod Andy void *bufaddr; 484d6bf3143SRussell King dma_addr_t addr; 4856e909283SNimrod Andy int i; 4866e909283SNimrod Andy 4876e909283SNimrod Andy for (frag = 0; frag < nr_frags; frag++) { 4886e909283SNimrod Andy this_frag = &skb_shinfo(skb)->frags[frag]; 4897355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 4906e909283SNimrod Andy ebdp = (struct bufdesc_ex *)bdp; 4916e909283SNimrod Andy 4925cfa3039SJohannes Berg status = fec16_to_cpu(bdp->cbd_sc); 4936e909283SNimrod Andy status &= ~BD_ENET_TX_STATS; 4946e909283SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 495d7840976SMatthew Wilcox (Oracle) frag_len = skb_frag_size(&skb_shinfo(skb)->frags[frag]); 4966e909283SNimrod Andy 4976e909283SNimrod Andy /* Handle the last BD specially */ 4986e909283SNimrod Andy if (frag == nr_frags - 1) { 4996e909283SNimrod Andy status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); 5006e909283SNimrod Andy if (fep->bufdesc_ex) { 5016e909283SNimrod Andy estatus |= BD_ENET_TX_INT; 5026e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & 5036e909283SNimrod Andy SKBTX_HW_TSTAMP && fep->hwts_tx_en)) 5046e909283SNimrod Andy estatus |= BD_ENET_TX_TS; 5056e909283SNimrod Andy } 5066e909283SNimrod Andy } 5076e909283SNimrod Andy 5086e909283SNimrod Andy if (fep->bufdesc_ex) { 5096b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 51053bb20d1STroy Kisky estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 5116e909283SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 5126e909283SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 513471ff445SJoakim Zhang 5146e909283SNimrod Andy ebdp->cbd_bdu = 0; 5155cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(estatus); 5166e909283SNimrod Andy } 5176e909283SNimrod Andy 518d7840976SMatthew Wilcox (Oracle) bufaddr = skb_frag_address(this_frag); 5196e909283SNimrod Andy 5207355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 52141ef84ceSFugang Duan if (((unsigned long) bufaddr) & fep->tx_align || 5226b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 5234d494cdcSFugang Duan memcpy(txq->tx_bounce[index], bufaddr, frag_len); 5244d494cdcSFugang Duan bufaddr = txq->tx_bounce[index]; 5256e909283SNimrod Andy 5266b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 5276e909283SNimrod Andy swap_buffer(bufaddr, frag_len); 5286e909283SNimrod Andy } 5296e909283SNimrod Andy 530d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, bufaddr, frag_len, 531d6bf3143SRussell King DMA_TO_DEVICE); 532d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 5336e909283SNimrod Andy if (net_ratelimit()) 5346e909283SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 5356e909283SNimrod Andy goto dma_mapping_error; 5366e909283SNimrod Andy } 5376e909283SNimrod Andy 5385cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(addr); 5395cfa3039SJohannes Berg bdp->cbd_datlen = cpu_to_fec16(frag_len); 540be293467STroy Kisky /* Make sure the updates to rest of the descriptor are 541be293467STroy Kisky * performed before transferring ownership. 542be293467STroy Kisky */ 543be293467STroy Kisky wmb(); 5445cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(status); 5456e909283SNimrod Andy } 5466e909283SNimrod Andy 547c4bc44c6SKevin Hao return bdp; 5486e909283SNimrod Andy dma_mapping_error: 5497355f276STroy Kisky bdp = txq->bd.cur; 5506e909283SNimrod Andy for (i = 0; i < frag; i++) { 5517355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 5525cfa3039SJohannes Berg dma_unmap_single(&fep->pdev->dev, fec32_to_cpu(bdp->cbd_bufaddr), 5535cfa3039SJohannes Berg fec16_to_cpu(bdp->cbd_datlen), DMA_TO_DEVICE); 5546e909283SNimrod Andy } 555c4bc44c6SKevin Hao return ERR_PTR(-ENOMEM); 5566e909283SNimrod Andy } 5576e909283SNimrod Andy 5584d494cdcSFugang Duan static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq, 5594d494cdcSFugang Duan struct sk_buff *skb, struct net_device *ndev) 5606e909283SNimrod Andy { 5616e909283SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 5626e909283SNimrod Andy int nr_frags = skb_shinfo(skb)->nr_frags; 5636e909283SNimrod Andy struct bufdesc *bdp, *last_bdp; 5646e909283SNimrod Andy void *bufaddr; 565d6bf3143SRussell King dma_addr_t addr; 5666e909283SNimrod Andy unsigned short status; 5676e909283SNimrod Andy unsigned short buflen; 5686e909283SNimrod Andy unsigned int estatus = 0; 5696e909283SNimrod Andy unsigned int index; 57079f33912SNimrod Andy int entries_free; 5716e909283SNimrod Andy 5727355f276STroy Kisky entries_free = fec_enet_get_free_txdesc_num(txq); 57379f33912SNimrod Andy if (entries_free < MAX_SKB_FRAGS + 1) { 57479f33912SNimrod Andy dev_kfree_skb_any(skb); 57579f33912SNimrod Andy if (net_ratelimit()) 57679f33912SNimrod Andy netdev_err(ndev, "NOT enough BD for SG!\n"); 57779f33912SNimrod Andy return NETDEV_TX_OK; 57879f33912SNimrod Andy } 57979f33912SNimrod Andy 5806e909283SNimrod Andy /* Protocol checksum off-load for TCP and UDP. */ 5816e909283SNimrod Andy if (fec_enet_clear_csum(skb, ndev)) { 5826e909283SNimrod Andy dev_kfree_skb_any(skb); 5836e909283SNimrod Andy return NETDEV_TX_OK; 5846e909283SNimrod Andy } 5856e909283SNimrod Andy 5866e909283SNimrod Andy /* Fill in a Tx ring entry */ 5877355f276STroy Kisky bdp = txq->bd.cur; 588c4bc44c6SKevin Hao last_bdp = bdp; 5895cfa3039SJohannes Berg status = fec16_to_cpu(bdp->cbd_sc); 5906e909283SNimrod Andy status &= ~BD_ENET_TX_STATS; 5916e909283SNimrod Andy 5926e909283SNimrod Andy /* Set buffer length and buffer pointer */ 5936e909283SNimrod Andy bufaddr = skb->data; 5946e909283SNimrod Andy buflen = skb_headlen(skb); 5956e909283SNimrod Andy 5967355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 59741ef84ceSFugang Duan if (((unsigned long) bufaddr) & fep->tx_align || 5986b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 5994d494cdcSFugang Duan memcpy(txq->tx_bounce[index], skb->data, buflen); 6004d494cdcSFugang Duan bufaddr = txq->tx_bounce[index]; 6016e909283SNimrod Andy 6026b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 6036e909283SNimrod Andy swap_buffer(bufaddr, buflen); 6046e909283SNimrod Andy } 6056e909283SNimrod Andy 606d6bf3143SRussell King /* Push the data cache so the CPM does not get stale memory data. */ 607d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, bufaddr, buflen, DMA_TO_DEVICE); 608d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 6096e909283SNimrod Andy dev_kfree_skb_any(skb); 6106e909283SNimrod Andy if (net_ratelimit()) 6116e909283SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 6126e909283SNimrod Andy return NETDEV_TX_OK; 6136e909283SNimrod Andy } 6146e909283SNimrod Andy 6156e909283SNimrod Andy if (nr_frags) { 616c4bc44c6SKevin Hao last_bdp = fec_enet_txq_submit_frag_skb(txq, skb, ndev); 617fc75ba51STroy Kisky if (IS_ERR(last_bdp)) { 618fc75ba51STroy Kisky dma_unmap_single(&fep->pdev->dev, addr, 619fc75ba51STroy Kisky buflen, DMA_TO_DEVICE); 620fc75ba51STroy Kisky dev_kfree_skb_any(skb); 621c4bc44c6SKevin Hao return NETDEV_TX_OK; 622fc75ba51STroy Kisky } 6236e909283SNimrod Andy } else { 6246e909283SNimrod Andy status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); 6256e909283SNimrod Andy if (fep->bufdesc_ex) { 6266e909283SNimrod Andy estatus = BD_ENET_TX_INT; 6276e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & 6286e909283SNimrod Andy SKBTX_HW_TSTAMP && fep->hwts_tx_en)) 6296e909283SNimrod Andy estatus |= BD_ENET_TX_TS; 6306e909283SNimrod Andy } 6316e909283SNimrod Andy } 632fc75ba51STroy Kisky bdp->cbd_bufaddr = cpu_to_fec32(addr); 633fc75ba51STroy Kisky bdp->cbd_datlen = cpu_to_fec16(buflen); 6346e909283SNimrod Andy 6356e909283SNimrod Andy if (fep->bufdesc_ex) { 6366e909283SNimrod Andy 6376e909283SNimrod Andy struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 6386e909283SNimrod Andy 6396e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && 6406e909283SNimrod Andy fep->hwts_tx_en)) 6416e909283SNimrod Andy skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; 6426e909283SNimrod Andy 6436b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 64453bb20d1STroy Kisky estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 645befe8213SNimrod Andy 6466e909283SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 6476e909283SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 6486e909283SNimrod Andy 6496e909283SNimrod Andy ebdp->cbd_bdu = 0; 6505cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(estatus); 6516e909283SNimrod Andy } 6526e909283SNimrod Andy 6537355f276STroy Kisky index = fec_enet_get_bd_index(last_bdp, &txq->bd); 6546e909283SNimrod Andy /* Save skb pointer */ 6554d494cdcSFugang Duan txq->tx_skbuff[index] = skb; 6566e909283SNimrod Andy 657be293467STroy Kisky /* Make sure the updates to rest of the descriptor are performed before 658be293467STroy Kisky * transferring ownership. 659be293467STroy Kisky */ 660be293467STroy Kisky wmb(); 6616e909283SNimrod Andy 6626e909283SNimrod Andy /* Send it on its way. Tell FEC it's ready, interrupt when done, 6636e909283SNimrod Andy * it's the last BD of the frame, and to put the CRC on the end. 6646e909283SNimrod Andy */ 6656e909283SNimrod Andy status |= (BD_ENET_TX_READY | BD_ENET_TX_TC); 6665cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(status); 6676e909283SNimrod Andy 668793fc096SFrank Li /* If this was the last BD in the ring, start at the beginning again. */ 6697355f276STroy Kisky bdp = fec_enet_get_nextdesc(last_bdp, &txq->bd); 670793fc096SFrank Li 6717a2a8451SEric Dumazet skb_tx_timestamp(skb); 6727a2a8451SEric Dumazet 673c4bc44c6SKevin Hao /* Make sure the update to bdp and tx_skbuff are performed before 6747355f276STroy Kisky * txq->bd.cur. 675c4bc44c6SKevin Hao */ 676c4bc44c6SKevin Hao wmb(); 6777355f276STroy Kisky txq->bd.cur = bdp; 678793fc096SFrank Li 679793fc096SFrank Li /* Trigger transmission start */ 68053bb20d1STroy Kisky writel(0, txq->bd.reg_desc_active); 681793fc096SFrank Li 6826e909283SNimrod Andy return 0; 683793fc096SFrank Li } 684793fc096SFrank Li 68579f33912SNimrod Andy static int 6864d494cdcSFugang Duan fec_enet_txq_put_data_tso(struct fec_enet_priv_tx_q *txq, struct sk_buff *skb, 6874d494cdcSFugang Duan struct net_device *ndev, 68879f33912SNimrod Andy struct bufdesc *bdp, int index, char *data, 68979f33912SNimrod Andy int size, bool last_tcp, bool is_last) 69079f33912SNimrod Andy { 69179f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 69261cd2ebbSFabian Frederick struct bufdesc_ex *ebdp = container_of(bdp, struct bufdesc_ex, desc); 69379f33912SNimrod Andy unsigned short status; 69479f33912SNimrod Andy unsigned int estatus = 0; 695d6bf3143SRussell King dma_addr_t addr; 69679f33912SNimrod Andy 6975cfa3039SJohannes Berg status = fec16_to_cpu(bdp->cbd_sc); 69879f33912SNimrod Andy status &= ~BD_ENET_TX_STATS; 69979f33912SNimrod Andy 70079f33912SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 70179f33912SNimrod Andy 70241ef84ceSFugang Duan if (((unsigned long) data) & fep->tx_align || 7036b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 7044d494cdcSFugang Duan memcpy(txq->tx_bounce[index], data, size); 7054d494cdcSFugang Duan data = txq->tx_bounce[index]; 70679f33912SNimrod Andy 7076b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 70879f33912SNimrod Andy swap_buffer(data, size); 70979f33912SNimrod Andy } 71079f33912SNimrod Andy 711d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, data, size, DMA_TO_DEVICE); 712d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 71379f33912SNimrod Andy dev_kfree_skb_any(skb); 71479f33912SNimrod Andy if (net_ratelimit()) 71579f33912SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 71606a4df58SZhang Changzhong return NETDEV_TX_OK; 71779f33912SNimrod Andy } 71879f33912SNimrod Andy 7195cfa3039SJohannes Berg bdp->cbd_datlen = cpu_to_fec16(size); 7205cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(addr); 721d6bf3143SRussell King 72279f33912SNimrod Andy if (fep->bufdesc_ex) { 7236b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 72453bb20d1STroy Kisky estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 72579f33912SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 72679f33912SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 72779f33912SNimrod Andy ebdp->cbd_bdu = 0; 7285cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(estatus); 72979f33912SNimrod Andy } 73079f33912SNimrod Andy 73179f33912SNimrod Andy /* Handle the last BD specially */ 73279f33912SNimrod Andy if (last_tcp) 73379f33912SNimrod Andy status |= (BD_ENET_TX_LAST | BD_ENET_TX_TC); 73479f33912SNimrod Andy if (is_last) { 73579f33912SNimrod Andy status |= BD_ENET_TX_INTR; 73679f33912SNimrod Andy if (fep->bufdesc_ex) 7375cfa3039SJohannes Berg ebdp->cbd_esc |= cpu_to_fec32(BD_ENET_TX_INT); 73879f33912SNimrod Andy } 73979f33912SNimrod Andy 7405cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(status); 74179f33912SNimrod Andy 74279f33912SNimrod Andy return 0; 74379f33912SNimrod Andy } 74479f33912SNimrod Andy 74579f33912SNimrod Andy static int 7464d494cdcSFugang Duan fec_enet_txq_put_hdr_tso(struct fec_enet_priv_tx_q *txq, 7474d494cdcSFugang Duan struct sk_buff *skb, struct net_device *ndev, 74879f33912SNimrod Andy struct bufdesc *bdp, int index) 74979f33912SNimrod Andy { 75079f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 751504148feSEric Dumazet int hdr_len = skb_tcp_all_headers(skb); 75261cd2ebbSFabian Frederick struct bufdesc_ex *ebdp = container_of(bdp, struct bufdesc_ex, desc); 75379f33912SNimrod Andy void *bufaddr; 75479f33912SNimrod Andy unsigned long dmabuf; 75579f33912SNimrod Andy unsigned short status; 75679f33912SNimrod Andy unsigned int estatus = 0; 75779f33912SNimrod Andy 7585cfa3039SJohannes Berg status = fec16_to_cpu(bdp->cbd_sc); 75979f33912SNimrod Andy status &= ~BD_ENET_TX_STATS; 76079f33912SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 76179f33912SNimrod Andy 7624d494cdcSFugang Duan bufaddr = txq->tso_hdrs + index * TSO_HEADER_SIZE; 7634d494cdcSFugang Duan dmabuf = txq->tso_hdrs_dma + index * TSO_HEADER_SIZE; 76441ef84ceSFugang Duan if (((unsigned long)bufaddr) & fep->tx_align || 7656b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 7664d494cdcSFugang Duan memcpy(txq->tx_bounce[index], skb->data, hdr_len); 7674d494cdcSFugang Duan bufaddr = txq->tx_bounce[index]; 76879f33912SNimrod Andy 7696b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 77079f33912SNimrod Andy swap_buffer(bufaddr, hdr_len); 77179f33912SNimrod Andy 77279f33912SNimrod Andy dmabuf = dma_map_single(&fep->pdev->dev, bufaddr, 77379f33912SNimrod Andy hdr_len, DMA_TO_DEVICE); 77479f33912SNimrod Andy if (dma_mapping_error(&fep->pdev->dev, dmabuf)) { 77579f33912SNimrod Andy dev_kfree_skb_any(skb); 77679f33912SNimrod Andy if (net_ratelimit()) 77779f33912SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 77806a4df58SZhang Changzhong return NETDEV_TX_OK; 77979f33912SNimrod Andy } 78079f33912SNimrod Andy } 78179f33912SNimrod Andy 7825cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(dmabuf); 7835cfa3039SJohannes Berg bdp->cbd_datlen = cpu_to_fec16(hdr_len); 78479f33912SNimrod Andy 78579f33912SNimrod Andy if (fep->bufdesc_ex) { 7866b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 78753bb20d1STroy Kisky estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 78879f33912SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 78979f33912SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 79079f33912SNimrod Andy ebdp->cbd_bdu = 0; 7915cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(estatus); 79279f33912SNimrod Andy } 79379f33912SNimrod Andy 7945cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(status); 79579f33912SNimrod Andy 79679f33912SNimrod Andy return 0; 79779f33912SNimrod Andy } 79879f33912SNimrod Andy 7994d494cdcSFugang Duan static int fec_enet_txq_submit_tso(struct fec_enet_priv_tx_q *txq, 8004d494cdcSFugang Duan struct sk_buff *skb, 8014d494cdcSFugang Duan struct net_device *ndev) 80279f33912SNimrod Andy { 80379f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 804761b331cSEric Dumazet int hdr_len, total_len, data_left; 8057355f276STroy Kisky struct bufdesc *bdp = txq->bd.cur; 80679f33912SNimrod Andy struct tso_t tso; 80779f33912SNimrod Andy unsigned int index = 0; 80879f33912SNimrod Andy int ret; 80979f33912SNimrod Andy 8107355f276STroy Kisky if (tso_count_descs(skb) >= fec_enet_get_free_txdesc_num(txq)) { 81179f33912SNimrod Andy dev_kfree_skb_any(skb); 81279f33912SNimrod Andy if (net_ratelimit()) 81379f33912SNimrod Andy netdev_err(ndev, "NOT enough BD for TSO!\n"); 81479f33912SNimrod Andy return NETDEV_TX_OK; 81579f33912SNimrod Andy } 81679f33912SNimrod Andy 81779f33912SNimrod Andy /* Protocol checksum off-load for TCP and UDP. */ 81879f33912SNimrod Andy if (fec_enet_clear_csum(skb, ndev)) { 81979f33912SNimrod Andy dev_kfree_skb_any(skb); 82079f33912SNimrod Andy return NETDEV_TX_OK; 82179f33912SNimrod Andy } 82279f33912SNimrod Andy 82379f33912SNimrod Andy /* Initialize the TSO handler, and prepare the first payload */ 824761b331cSEric Dumazet hdr_len = tso_start(skb, &tso); 82579f33912SNimrod Andy 82679f33912SNimrod Andy total_len = skb->len - hdr_len; 82779f33912SNimrod Andy while (total_len > 0) { 82879f33912SNimrod Andy char *hdr; 82979f33912SNimrod Andy 8307355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 83179f33912SNimrod Andy data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len); 83279f33912SNimrod Andy total_len -= data_left; 83379f33912SNimrod Andy 83479f33912SNimrod Andy /* prepare packet headers: MAC + IP + TCP */ 8354d494cdcSFugang Duan hdr = txq->tso_hdrs + index * TSO_HEADER_SIZE; 83679f33912SNimrod Andy tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0); 8374d494cdcSFugang Duan ret = fec_enet_txq_put_hdr_tso(txq, skb, ndev, bdp, index); 83879f33912SNimrod Andy if (ret) 83979f33912SNimrod Andy goto err_release; 84079f33912SNimrod Andy 84179f33912SNimrod Andy while (data_left > 0) { 84279f33912SNimrod Andy int size; 84379f33912SNimrod Andy 84479f33912SNimrod Andy size = min_t(int, tso.size, data_left); 8457355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 8467355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 8474d494cdcSFugang Duan ret = fec_enet_txq_put_data_tso(txq, skb, ndev, 8484d494cdcSFugang Duan bdp, index, 8494d494cdcSFugang Duan tso.data, size, 8504d494cdcSFugang Duan size == data_left, 85179f33912SNimrod Andy total_len == 0); 85279f33912SNimrod Andy if (ret) 85379f33912SNimrod Andy goto err_release; 85479f33912SNimrod Andy 85579f33912SNimrod Andy data_left -= size; 85679f33912SNimrod Andy tso_build_data(skb, &tso, size); 85779f33912SNimrod Andy } 85879f33912SNimrod Andy 8597355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 86079f33912SNimrod Andy } 86179f33912SNimrod Andy 86279f33912SNimrod Andy /* Save skb pointer */ 8634d494cdcSFugang Duan txq->tx_skbuff[index] = skb; 86479f33912SNimrod Andy 86579f33912SNimrod Andy skb_tx_timestamp(skb); 8667355f276STroy Kisky txq->bd.cur = bdp; 86779f33912SNimrod Andy 86879f33912SNimrod Andy /* Trigger transmission start */ 8696b7e4008SLothar Waßmann if (!(fep->quirks & FEC_QUIRK_ERR007885) || 87053bb20d1STroy Kisky !readl(txq->bd.reg_desc_active) || 87153bb20d1STroy Kisky !readl(txq->bd.reg_desc_active) || 87253bb20d1STroy Kisky !readl(txq->bd.reg_desc_active) || 87353bb20d1STroy Kisky !readl(txq->bd.reg_desc_active)) 87453bb20d1STroy Kisky writel(0, txq->bd.reg_desc_active); 87579f33912SNimrod Andy 87679f33912SNimrod Andy return 0; 87779f33912SNimrod Andy 87879f33912SNimrod Andy err_release: 87979f33912SNimrod Andy /* TODO: Release all used data descriptors for TSO */ 88079f33912SNimrod Andy return ret; 88179f33912SNimrod Andy } 88279f33912SNimrod Andy 88361a4427bSNimrod Andy static netdev_tx_t 88461a4427bSNimrod Andy fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) 88561a4427bSNimrod Andy { 88661a4427bSNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 8876e909283SNimrod Andy int entries_free; 8884d494cdcSFugang Duan unsigned short queue; 8894d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 8904d494cdcSFugang Duan struct netdev_queue *nq; 89161a4427bSNimrod Andy int ret; 89261a4427bSNimrod Andy 8934d494cdcSFugang Duan queue = skb_get_queue_mapping(skb); 8944d494cdcSFugang Duan txq = fep->tx_queue[queue]; 8954d494cdcSFugang Duan nq = netdev_get_tx_queue(ndev, queue); 8964d494cdcSFugang Duan 89779f33912SNimrod Andy if (skb_is_gso(skb)) 8984d494cdcSFugang Duan ret = fec_enet_txq_submit_tso(txq, skb, ndev); 89979f33912SNimrod Andy else 9004d494cdcSFugang Duan ret = fec_enet_txq_submit_skb(txq, skb, ndev); 9016e909283SNimrod Andy if (ret) 9026e909283SNimrod Andy return ret; 90361a4427bSNimrod Andy 9047355f276STroy Kisky entries_free = fec_enet_get_free_txdesc_num(txq); 9054d494cdcSFugang Duan if (entries_free <= txq->tx_stop_threshold) 9064d494cdcSFugang Duan netif_tx_stop_queue(nq); 90761a4427bSNimrod Andy 90861a4427bSNimrod Andy return NETDEV_TX_OK; 90961a4427bSNimrod Andy } 91061a4427bSNimrod Andy 911a210576cSDavid S. Miller /* Init RX & TX buffer descriptors 912a210576cSDavid S. Miller */ 913a210576cSDavid S. Miller static void fec_enet_bd_init(struct net_device *dev) 914a210576cSDavid S. Miller { 915a210576cSDavid S. Miller struct fec_enet_private *fep = netdev_priv(dev); 9164d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 9174d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 918a210576cSDavid S. Miller struct bufdesc *bdp; 919a210576cSDavid S. Miller unsigned int i; 92059d0f746SFrank Li unsigned int q; 921a210576cSDavid S. Miller 92259d0f746SFrank Li for (q = 0; q < fep->num_rx_queues; q++) { 923a210576cSDavid S. Miller /* Initialize the receive buffer descriptors. */ 92459d0f746SFrank Li rxq = fep->rx_queue[q]; 9257355f276STroy Kisky bdp = rxq->bd.base; 9264d494cdcSFugang Duan 9277355f276STroy Kisky for (i = 0; i < rxq->bd.ring_size; i++) { 928a210576cSDavid S. Miller 929a210576cSDavid S. Miller /* Initialize the BD for every fragment in the page. */ 930a210576cSDavid S. Miller if (bdp->cbd_bufaddr) 9315cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); 932a210576cSDavid S. Miller else 9335cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(0); 9347355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 935a210576cSDavid S. Miller } 936a210576cSDavid S. Miller 937a210576cSDavid S. Miller /* Set the last buffer to wrap */ 9387355f276STroy Kisky bdp = fec_enet_get_prevdesc(bdp, &rxq->bd); 9395cfa3039SJohannes Berg bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 940a210576cSDavid S. Miller 9417355f276STroy Kisky rxq->bd.cur = rxq->bd.base; 94259d0f746SFrank Li } 943a210576cSDavid S. Miller 94459d0f746SFrank Li for (q = 0; q < fep->num_tx_queues; q++) { 945a210576cSDavid S. Miller /* ...and the same for transmit */ 94659d0f746SFrank Li txq = fep->tx_queue[q]; 9477355f276STroy Kisky bdp = txq->bd.base; 9487355f276STroy Kisky txq->bd.cur = bdp; 949a210576cSDavid S. Miller 9507355f276STroy Kisky for (i = 0; i < txq->bd.ring_size; i++) { 951a210576cSDavid S. Miller /* Initialize the BD for every fragment in the page. */ 9525cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(0); 953178e5f57SFugang Duan if (bdp->cbd_bufaddr && 954178e5f57SFugang Duan !IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr))) 955178e5f57SFugang Duan dma_unmap_single(&fep->pdev->dev, 956178e5f57SFugang Duan fec32_to_cpu(bdp->cbd_bufaddr), 957178e5f57SFugang Duan fec16_to_cpu(bdp->cbd_datlen), 958178e5f57SFugang Duan DMA_TO_DEVICE); 9594d494cdcSFugang Duan if (txq->tx_skbuff[i]) { 9604d494cdcSFugang Duan dev_kfree_skb_any(txq->tx_skbuff[i]); 9614d494cdcSFugang Duan txq->tx_skbuff[i] = NULL; 962a210576cSDavid S. Miller } 9635cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(0); 9647355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 965a210576cSDavid S. Miller } 966a210576cSDavid S. Miller 967a210576cSDavid S. Miller /* Set the last buffer to wrap */ 9687355f276STroy Kisky bdp = fec_enet_get_prevdesc(bdp, &txq->bd); 9695cfa3039SJohannes Berg bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 9704d494cdcSFugang Duan txq->dirty_tx = bdp; 971a210576cSDavid S. Miller } 97259d0f746SFrank Li } 97359d0f746SFrank Li 974ce99d0d3SFrank Li static void fec_enet_active_rxring(struct net_device *ndev) 975ce99d0d3SFrank Li { 976ce99d0d3SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 977ce99d0d3SFrank Li int i; 978ce99d0d3SFrank Li 979ce99d0d3SFrank Li for (i = 0; i < fep->num_rx_queues; i++) 98053bb20d1STroy Kisky writel(0, fep->rx_queue[i]->bd.reg_desc_active); 981ce99d0d3SFrank Li } 982ce99d0d3SFrank Li 98359d0f746SFrank Li static void fec_enet_enable_ring(struct net_device *ndev) 98459d0f746SFrank Li { 98559d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 98659d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 98759d0f746SFrank Li struct fec_enet_priv_rx_q *rxq; 98859d0f746SFrank Li int i; 98959d0f746SFrank Li 99059d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) { 99159d0f746SFrank Li rxq = fep->rx_queue[i]; 9927355f276STroy Kisky writel(rxq->bd.dma, fep->hwp + FEC_R_DES_START(i)); 993fbbeefddSAndrew Lunn writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_R_BUFF_SIZE(i)); 99459d0f746SFrank Li 99559d0f746SFrank Li /* enable DMA1/2 */ 99659d0f746SFrank Li if (i) 99759d0f746SFrank Li writel(RCMR_MATCHEN | RCMR_CMP(i), 99859d0f746SFrank Li fep->hwp + FEC_RCMR(i)); 99959d0f746SFrank Li } 100059d0f746SFrank Li 100159d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 100259d0f746SFrank Li txq = fep->tx_queue[i]; 10037355f276STroy Kisky writel(txq->bd.dma, fep->hwp + FEC_X_DES_START(i)); 100459d0f746SFrank Li 100559d0f746SFrank Li /* enable DMA1/2 */ 100659d0f746SFrank Li if (i) 100759d0f746SFrank Li writel(DMA_CLASS_EN | IDLE_SLOPE(i), 100859d0f746SFrank Li fep->hwp + FEC_DMA_CFG(i)); 100959d0f746SFrank Li } 101059d0f746SFrank Li } 101159d0f746SFrank Li 101259d0f746SFrank Li static void fec_enet_reset_skb(struct net_device *ndev) 101359d0f746SFrank Li { 101459d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 101559d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 101659d0f746SFrank Li int i, j; 101759d0f746SFrank Li 101859d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 101959d0f746SFrank Li txq = fep->tx_queue[i]; 102059d0f746SFrank Li 10217355f276STroy Kisky for (j = 0; j < txq->bd.ring_size; j++) { 102259d0f746SFrank Li if (txq->tx_skbuff[j]) { 102359d0f746SFrank Li dev_kfree_skb_any(txq->tx_skbuff[j]); 102459d0f746SFrank Li txq->tx_skbuff[j] = NULL; 102559d0f746SFrank Li } 102659d0f746SFrank Li } 102759d0f746SFrank Li } 102859d0f746SFrank Li } 1029a210576cSDavid S. Miller 1030dbc64a8eSRussell King /* 1031dbc64a8eSRussell King * This function is called to start or restart the FEC during a link 1032dbc64a8eSRussell King * change, transmit timeout, or to reconfigure the FEC. The network 1033dbc64a8eSRussell King * packet processing for this device must be stopped before this call. 1034793fc096SFrank Li */ 1035793fc096SFrank Li static void 1036ef83337dSRussell King fec_restart(struct net_device *ndev) 1037793fc096SFrank Li { 1038793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1039793fc096SFrank Li u32 temp_mac[2]; 1040793fc096SFrank Li u32 rcntl = OPT_FRAME_SIZE | 0x04; 1041793fc096SFrank Li u32 ecntl = 0x2; /* ETHEREN */ 1042793fc096SFrank Li 1043106c314cSFugang Duan /* Whack a reset. We should wait for this. 1044106c314cSFugang Duan * For i.MX6SX SOC, enet use AXI bus, we use disable MAC 1045106c314cSFugang Duan * instead of reset MAC itself. 1046106c314cSFugang Duan */ 1047471ff445SJoakim Zhang if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES || 1048c730ab42SLaurent Badel ((fep->quirks & FEC_QUIRK_NO_HARD_RESET) && fep->link)) { 1049106c314cSFugang Duan writel(0, fep->hwp + FEC_ECNTRL); 1050106c314cSFugang Duan } else { 1051793fc096SFrank Li writel(1, fep->hwp + FEC_ECNTRL); 1052793fc096SFrank Li udelay(10); 1053106c314cSFugang Duan } 1054793fc096SFrank Li 1055793fc096SFrank Li /* 1056793fc096SFrank Li * enet-mac reset will reset mac address registers too, 1057793fc096SFrank Li * so need to reconfigure it. 1058793fc096SFrank Li */ 1059793fc096SFrank Li memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN); 10605cfa3039SJohannes Berg writel((__force u32)cpu_to_be32(temp_mac[0]), 10615cfa3039SJohannes Berg fep->hwp + FEC_ADDR_LOW); 10625cfa3039SJohannes Berg writel((__force u32)cpu_to_be32(temp_mac[1]), 10635cfa3039SJohannes Berg fep->hwp + FEC_ADDR_HIGH); 1064793fc096SFrank Li 1065f166f890SAndrew Lunn /* Clear any outstanding interrupt, except MDIO. */ 1066f166f890SAndrew Lunn writel((0xffffffff & ~FEC_ENET_MII), fep->hwp + FEC_IEVENT); 1067793fc096SFrank Li 1068a210576cSDavid S. Miller fec_enet_bd_init(ndev); 1069a210576cSDavid S. Miller 107059d0f746SFrank Li fec_enet_enable_ring(ndev); 1071793fc096SFrank Li 107259d0f746SFrank Li /* Reset tx SKB buffers. */ 107359d0f746SFrank Li fec_enet_reset_skb(ndev); 1074793fc096SFrank Li 1075793fc096SFrank Li /* Enable MII mode */ 1076ef83337dSRussell King if (fep->full_duplex == DUPLEX_FULL) { 1077793fc096SFrank Li /* FD enable */ 1078793fc096SFrank Li writel(0x04, fep->hwp + FEC_X_CNTRL); 1079793fc096SFrank Li } else { 1080793fc096SFrank Li /* No Rcv on Xmit */ 1081793fc096SFrank Li rcntl |= 0x02; 1082793fc096SFrank Li writel(0x0, fep->hwp + FEC_X_CNTRL); 1083793fc096SFrank Li } 1084793fc096SFrank Li 1085793fc096SFrank Li /* Set MII speed */ 1086793fc096SFrank Li writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 1087793fc096SFrank Li 1088d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 108918803495SGreg Ungerer if (fep->quirks & FEC_QUIRK_HAS_RACC) { 109032d1bbb1SGeert Uytterhoeven u32 val = readl(fep->hwp + FEC_RACC); 109132d1bbb1SGeert Uytterhoeven 10923ac72b7bSEric Nelson /* align IP header */ 10933ac72b7bSEric Nelson val |= FEC_RACC_SHIFT16; 10944c09eed9SJim Baxter if (fep->csum_flags & FLAG_RX_CSUM_ENABLED) 10953ac72b7bSEric Nelson /* set RX checksum */ 10964c09eed9SJim Baxter val |= FEC_RACC_OPTIONS; 10974c09eed9SJim Baxter else 10984c09eed9SJim Baxter val &= ~FEC_RACC_OPTIONS; 10994c09eed9SJim Baxter writel(val, fep->hwp + FEC_RACC); 110055cd48c8STroy Kisky writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_FTRL); 110132867fccSFabio Estevam } 1102d1391930SGuenter Roeck #endif 11034c09eed9SJim Baxter 1104793fc096SFrank Li /* 1105793fc096SFrank Li * The phy interface and speed need to get configured 1106793fc096SFrank Li * differently on enet-mac. 1107793fc096SFrank Li */ 11086b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_ENET_MAC) { 1109793fc096SFrank Li /* Enable flow control and length check */ 1110793fc096SFrank Li rcntl |= 0x40000000 | 0x00000020; 1111793fc096SFrank Li 1112793fc096SFrank Li /* RGMII, RMII or MII */ 1113e813bb2bSMarkus Pargmann if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII || 1114e813bb2bSMarkus Pargmann fep->phy_interface == PHY_INTERFACE_MODE_RGMII_ID || 1115e813bb2bSMarkus Pargmann fep->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID || 1116e813bb2bSMarkus Pargmann fep->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID) 1117793fc096SFrank Li rcntl |= (1 << 6); 1118793fc096SFrank Li else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) 1119793fc096SFrank Li rcntl |= (1 << 8); 1120793fc096SFrank Li else 1121793fc096SFrank Li rcntl &= ~(1 << 8); 1122793fc096SFrank Li 1123793fc096SFrank Li /* 1G, 100M or 10M */ 112445f5c327SPhilippe Reynes if (ndev->phydev) { 112545f5c327SPhilippe Reynes if (ndev->phydev->speed == SPEED_1000) 1126793fc096SFrank Li ecntl |= (1 << 5); 112745f5c327SPhilippe Reynes else if (ndev->phydev->speed == SPEED_100) 1128793fc096SFrank Li rcntl &= ~(1 << 9); 1129793fc096SFrank Li else 1130793fc096SFrank Li rcntl |= (1 << 9); 1131793fc096SFrank Li } 1132793fc096SFrank Li } else { 1133793fc096SFrank Li #ifdef FEC_MIIGSK_ENR 11346b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_USE_GASKET) { 1135793fc096SFrank Li u32 cfgr; 1136793fc096SFrank Li /* disable the gasket and wait */ 1137793fc096SFrank Li writel(0, fep->hwp + FEC_MIIGSK_ENR); 1138793fc096SFrank Li while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4) 1139793fc096SFrank Li udelay(1); 1140793fc096SFrank Li 1141793fc096SFrank Li /* 1142793fc096SFrank Li * configure the gasket: 1143793fc096SFrank Li * RMII, 50 MHz, no loopback, no echo 1144793fc096SFrank Li * MII, 25 MHz, no loopback, no echo 1145793fc096SFrank Li */ 1146793fc096SFrank Li cfgr = (fep->phy_interface == PHY_INTERFACE_MODE_RMII) 1147793fc096SFrank Li ? BM_MIIGSK_CFGR_RMII : BM_MIIGSK_CFGR_MII; 114845f5c327SPhilippe Reynes if (ndev->phydev && ndev->phydev->speed == SPEED_10) 1149793fc096SFrank Li cfgr |= BM_MIIGSK_CFGR_FRCONT_10M; 1150793fc096SFrank Li writel(cfgr, fep->hwp + FEC_MIIGSK_CFGR); 1151793fc096SFrank Li 1152793fc096SFrank Li /* re-enable the gasket */ 1153793fc096SFrank Li writel(2, fep->hwp + FEC_MIIGSK_ENR); 1154793fc096SFrank Li } 1155793fc096SFrank Li #endif 1156793fc096SFrank Li } 1157793fc096SFrank Li 1158d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 1159793fc096SFrank Li /* enable pause frame*/ 1160793fc096SFrank Li if ((fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) || 1161793fc096SFrank Li ((fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) && 116245f5c327SPhilippe Reynes ndev->phydev && ndev->phydev->pause)) { 1163793fc096SFrank Li rcntl |= FEC_ENET_FCE; 1164793fc096SFrank Li 11654c09eed9SJim Baxter /* set FIFO threshold parameter to reduce overrun */ 1166793fc096SFrank Li writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM); 1167793fc096SFrank Li writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL); 1168793fc096SFrank Li writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM); 1169793fc096SFrank Li writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL); 1170793fc096SFrank Li 1171793fc096SFrank Li /* OPD */ 1172793fc096SFrank Li writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD); 1173793fc096SFrank Li } else { 1174793fc096SFrank Li rcntl &= ~FEC_ENET_FCE; 1175793fc096SFrank Li } 1176d1391930SGuenter Roeck #endif /* !defined(CONFIG_M5272) */ 1177793fc096SFrank Li 1178793fc096SFrank Li writel(rcntl, fep->hwp + FEC_R_CNTRL); 1179793fc096SFrank Li 118084fe6182SStefan Wahren /* Setup multicast filter. */ 118184fe6182SStefan Wahren set_multicast_list(ndev); 118284fe6182SStefan Wahren #ifndef CONFIG_M5272 118384fe6182SStefan Wahren writel(0, fep->hwp + FEC_HASH_TABLE_HIGH); 118484fe6182SStefan Wahren writel(0, fep->hwp + FEC_HASH_TABLE_LOW); 118584fe6182SStefan Wahren #endif 118684fe6182SStefan Wahren 11876b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_ENET_MAC) { 1188793fc096SFrank Li /* enable ENET endian swap */ 1189793fc096SFrank Li ecntl |= (1 << 8); 1190793fc096SFrank Li /* enable ENET store and forward mode */ 1191793fc096SFrank Li writel(1 << 8, fep->hwp + FEC_X_WMRK); 1192793fc096SFrank Li } 1193793fc096SFrank Li 1194793fc096SFrank Li if (fep->bufdesc_ex) 11957b15515fSFrancesco Dolcini ecntl |= (1 << 4); 1196793fc096SFrank Li 1197fc539459SFugang Duan if (fep->quirks & FEC_QUIRK_DELAYED_CLKS_SUPPORT && 1198fc539459SFugang Duan fep->rgmii_txc_dly) 1199fc539459SFugang Duan ecntl |= FEC_ENET_TXC_DLY; 1200fc539459SFugang Duan if (fep->quirks & FEC_QUIRK_DELAYED_CLKS_SUPPORT && 1201fc539459SFugang Duan fep->rgmii_rxc_dly) 1202fc539459SFugang Duan ecntl |= FEC_ENET_RXC_DLY; 1203fc539459SFugang Duan 120438ae92dcSChris Healy #ifndef CONFIG_M5272 1205b9eef55cSJim Baxter /* Enable the MIB statistic event counters */ 1206b9eef55cSJim Baxter writel(0 << 31, fep->hwp + FEC_MIB_CTRLSTAT); 120738ae92dcSChris Healy #endif 120838ae92dcSChris Healy 1209793fc096SFrank Li /* And last, enable the transmit and receive processing */ 1210793fc096SFrank Li writel(ecntl, fep->hwp + FEC_ECNTRL); 1211ce99d0d3SFrank Li fec_enet_active_rxring(ndev); 1212793fc096SFrank Li 1213793fc096SFrank Li if (fep->bufdesc_ex) 1214793fc096SFrank Li fec_ptp_start_cyclecounter(ndev); 1215793fc096SFrank Li 1216793fc096SFrank Li /* Enable interrupts we wish to service */ 12170c5a3aefSNimrod Andy if (fep->link) 1218793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 12190c5a3aefSNimrod Andy else 1220f166f890SAndrew Lunn writel(0, fep->hwp + FEC_IMASK); 1221d851b47bSFugang Duan 1222d851b47bSFugang Duan /* Init the interrupt coalescing */ 1223*7e630356SRasmus Villemoes if (fep->quirks & FEC_QUIRK_HAS_COALESCE) 1224df727d45SRasmus Villemoes fec_enet_itr_coal_set(ndev); 1225793fc096SFrank Li } 1226793fc096SFrank Li 122740c79ce1SWei Fang static int fec_enet_ipc_handle_init(struct fec_enet_private *fep) 122840c79ce1SWei Fang { 122940c79ce1SWei Fang if (!(of_machine_is_compatible("fsl,imx8qm") || 123040c79ce1SWei Fang of_machine_is_compatible("fsl,imx8qxp") || 123140c79ce1SWei Fang of_machine_is_compatible("fsl,imx8dxl"))) 123240c79ce1SWei Fang return 0; 123340c79ce1SWei Fang 123440c79ce1SWei Fang return imx_scu_get_handle(&fep->ipc_handle); 123540c79ce1SWei Fang } 123640c79ce1SWei Fang 123740c79ce1SWei Fang static void fec_enet_ipg_stop_set(struct fec_enet_private *fep, bool enabled) 123840c79ce1SWei Fang { 123940c79ce1SWei Fang struct device_node *np = fep->pdev->dev.of_node; 124040c79ce1SWei Fang u32 rsrc_id, val; 124140c79ce1SWei Fang int idx; 124240c79ce1SWei Fang 124340c79ce1SWei Fang if (!np || !fep->ipc_handle) 124440c79ce1SWei Fang return; 124540c79ce1SWei Fang 124640c79ce1SWei Fang idx = of_alias_get_id(np, "ethernet"); 124740c79ce1SWei Fang if (idx < 0) 124840c79ce1SWei Fang idx = 0; 124940c79ce1SWei Fang rsrc_id = idx ? IMX_SC_R_ENET_1 : IMX_SC_R_ENET_0; 125040c79ce1SWei Fang 125140c79ce1SWei Fang val = enabled ? 1 : 0; 125240c79ce1SWei Fang imx_sc_misc_set_control(fep->ipc_handle, rsrc_id, IMX_SC_C_IPG_STOP, val); 125340c79ce1SWei Fang } 125440c79ce1SWei Fang 1255da722186SMartin Fuzzey static void fec_enet_stop_mode(struct fec_enet_private *fep, bool enabled) 1256da722186SMartin Fuzzey { 1257da722186SMartin Fuzzey struct fec_platform_data *pdata = fep->pdev->dev.platform_data; 1258da722186SMartin Fuzzey struct fec_stop_mode_gpr *stop_gpr = &fep->stop_gpr; 1259da722186SMartin Fuzzey 1260da722186SMartin Fuzzey if (stop_gpr->gpr) { 1261da722186SMartin Fuzzey if (enabled) 1262da722186SMartin Fuzzey regmap_update_bits(stop_gpr->gpr, stop_gpr->reg, 1263da722186SMartin Fuzzey BIT(stop_gpr->bit), 1264da722186SMartin Fuzzey BIT(stop_gpr->bit)); 1265da722186SMartin Fuzzey else 1266da722186SMartin Fuzzey regmap_update_bits(stop_gpr->gpr, stop_gpr->reg, 1267da722186SMartin Fuzzey BIT(stop_gpr->bit), 0); 1268da722186SMartin Fuzzey } else if (pdata && pdata->sleep_mode_enable) { 1269da722186SMartin Fuzzey pdata->sleep_mode_enable(enabled); 127040c79ce1SWei Fang } else { 127140c79ce1SWei Fang fec_enet_ipg_stop_set(fep, enabled); 1272da722186SMartin Fuzzey } 1273da722186SMartin Fuzzey } 1274da722186SMartin Fuzzey 12750b6f65c7SJoakim Zhang static void fec_irqs_disable(struct net_device *ndev) 12760b6f65c7SJoakim Zhang { 12770b6f65c7SJoakim Zhang struct fec_enet_private *fep = netdev_priv(ndev); 12780b6f65c7SJoakim Zhang 12790b6f65c7SJoakim Zhang writel(0, fep->hwp + FEC_IMASK); 12800b6f65c7SJoakim Zhang } 12810b6f65c7SJoakim Zhang 12820b6f65c7SJoakim Zhang static void fec_irqs_disable_except_wakeup(struct net_device *ndev) 12830b6f65c7SJoakim Zhang { 12840b6f65c7SJoakim Zhang struct fec_enet_private *fep = netdev_priv(ndev); 12850b6f65c7SJoakim Zhang 12860b6f65c7SJoakim Zhang writel(0, fep->hwp + FEC_IMASK); 12870b6f65c7SJoakim Zhang writel(FEC_ENET_WAKEUP, fep->hwp + FEC_IMASK); 12880b6f65c7SJoakim Zhang } 12890b6f65c7SJoakim Zhang 1290793fc096SFrank Li static void 1291793fc096SFrank Li fec_stop(struct net_device *ndev) 1292793fc096SFrank Li { 1293793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1294793fc096SFrank Li u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8); 1295de40ed31SNimrod Andy u32 val; 1296793fc096SFrank Li 1297793fc096SFrank Li /* We cannot expect a graceful transmit stop without link !!! */ 1298793fc096SFrank Li if (fep->link) { 1299793fc096SFrank Li writel(1, fep->hwp + FEC_X_CNTRL); /* Graceful transmit stop */ 1300793fc096SFrank Li udelay(10); 1301793fc096SFrank Li if (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_GRA)) 130231b7720cSJoe Perches netdev_err(ndev, "Graceful transmit stop did not complete!\n"); 1303793fc096SFrank Li } 1304793fc096SFrank Li 1305106c314cSFugang Duan /* Whack a reset. We should wait for this. 1306106c314cSFugang Duan * For i.MX6SX SOC, enet use AXI bus, we use disable MAC 1307106c314cSFugang Duan * instead of reset MAC itself. 1308106c314cSFugang Duan */ 1309de40ed31SNimrod Andy if (!(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { 1310471ff445SJoakim Zhang if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) { 1311106c314cSFugang Duan writel(0, fep->hwp + FEC_ECNTRL); 1312106c314cSFugang Duan } else { 1313793fc096SFrank Li writel(1, fep->hwp + FEC_ECNTRL); 1314793fc096SFrank Li udelay(10); 1315106c314cSFugang Duan } 1316de40ed31SNimrod Andy } else { 1317de40ed31SNimrod Andy val = readl(fep->hwp + FEC_ECNTRL); 1318de40ed31SNimrod Andy val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP); 1319de40ed31SNimrod Andy writel(val, fep->hwp + FEC_ECNTRL); 1320de40ed31SNimrod Andy } 1321de40ed31SNimrod Andy writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 13220b6f65c7SJoakim Zhang writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 1323793fc096SFrank Li 1324793fc096SFrank Li /* We have to keep ENET enabled to have MII interrupt stay working */ 1325de40ed31SNimrod Andy if (fep->quirks & FEC_QUIRK_ENET_MAC && 1326de40ed31SNimrod Andy !(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { 13277b15515fSFrancesco Dolcini writel(2, fep->hwp + FEC_ECNTRL); 1328793fc096SFrank Li writel(rmii_mode, fep->hwp + FEC_R_CNTRL); 1329793fc096SFrank Li } 1330793fc096SFrank Li } 1331793fc096SFrank Li 1332793fc096SFrank Li 1333793fc096SFrank Li static void 13340290bd29SMichael S. Tsirkin fec_timeout(struct net_device *ndev, unsigned int txqueue) 1335793fc096SFrank Li { 1336793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1337793fc096SFrank Li 1338344756f6SRussell King fec_dump(ndev); 1339344756f6SRussell King 1340793fc096SFrank Li ndev->stats.tx_errors++; 1341793fc096SFrank Li 134236cdc743SRussell King schedule_work(&fep->tx_timeout_work); 134354309fa6SFrank Li } 134454309fa6SFrank Li 134536cdc743SRussell King static void fec_enet_timeout_work(struct work_struct *work) 134654309fa6SFrank Li { 134754309fa6SFrank Li struct fec_enet_private *fep = 134836cdc743SRussell King container_of(work, struct fec_enet_private, tx_timeout_work); 13498ce5624fSRussell King struct net_device *ndev = fep->netdev; 135054309fa6SFrank Li 1351da1774e5SRussell King rtnl_lock(); 13528ce5624fSRussell King if (netif_device_present(ndev) || netif_running(ndev)) { 1353dbc64a8eSRussell King napi_disable(&fep->napi); 1354dbc64a8eSRussell King netif_tx_lock_bh(ndev); 1355ef83337dSRussell King fec_restart(ndev); 1356657ade07SRickard x Andersson netif_tx_wake_all_queues(ndev); 13576af42d42SRussell King netif_tx_unlock_bh(ndev); 1358dbc64a8eSRussell King napi_enable(&fep->napi); 13598ce5624fSRussell King } 1360da1774e5SRussell King rtnl_unlock(); 136154309fa6SFrank Li } 1362793fc096SFrank Li 1363793fc096SFrank Li static void 1364bfd4ecddSRussell King fec_enet_hwtstamp(struct fec_enet_private *fep, unsigned ts, 1365bfd4ecddSRussell King struct skb_shared_hwtstamps *hwtstamps) 1366bfd4ecddSRussell King { 1367bfd4ecddSRussell King unsigned long flags; 1368bfd4ecddSRussell King u64 ns; 1369bfd4ecddSRussell King 1370bfd4ecddSRussell King spin_lock_irqsave(&fep->tmreg_lock, flags); 1371bfd4ecddSRussell King ns = timecounter_cyc2time(&fep->tc, ts); 1372bfd4ecddSRussell King spin_unlock_irqrestore(&fep->tmreg_lock, flags); 1373bfd4ecddSRussell King 1374bfd4ecddSRussell King memset(hwtstamps, 0, sizeof(*hwtstamps)); 1375bfd4ecddSRussell King hwtstamps->hwtstamp = ns_to_ktime(ns); 1376bfd4ecddSRussell King } 1377bfd4ecddSRussell King 1378bfd4ecddSRussell King static void 13794d494cdcSFugang Duan fec_enet_tx_queue(struct net_device *ndev, u16 queue_id) 1380793fc096SFrank Li { 1381793fc096SFrank Li struct fec_enet_private *fep; 1382a2fe37b6SFabio Estevam struct bufdesc *bdp; 1383793fc096SFrank Li unsigned short status; 1384793fc096SFrank Li struct sk_buff *skb; 13854d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 13864d494cdcSFugang Duan struct netdev_queue *nq; 1387793fc096SFrank Li int index = 0; 138879f33912SNimrod Andy int entries_free; 1389793fc096SFrank Li 1390793fc096SFrank Li fep = netdev_priv(ndev); 13914d494cdcSFugang Duan 13924d494cdcSFugang Duan txq = fep->tx_queue[queue_id]; 13934d494cdcSFugang Duan /* get next bdp of dirty_tx */ 13944d494cdcSFugang Duan nq = netdev_get_tx_queue(ndev, queue_id); 13954d494cdcSFugang Duan bdp = txq->dirty_tx; 1396793fc096SFrank Li 1397793fc096SFrank Li /* get next bdp of dirty_tx */ 13987355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 1399793fc096SFrank Li 14007355f276STroy Kisky while (bdp != READ_ONCE(txq->bd.cur)) { 14017355f276STroy Kisky /* Order the load of bd.cur and cbd_sc */ 1402c4bc44c6SKevin Hao rmb(); 14035cfa3039SJohannes Berg status = fec16_to_cpu(READ_ONCE(bdp->cbd_sc)); 1404c4bc44c6SKevin Hao if (status & BD_ENET_TX_READY) 1405793fc096SFrank Li break; 1406793fc096SFrank Li 14077355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 14082b995f63SNimrod Andy 1409a2fe37b6SFabio Estevam skb = txq->tx_skbuff[index]; 1410a2fe37b6SFabio Estevam txq->tx_skbuff[index] = NULL; 14115cfa3039SJohannes Berg if (!IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr))) 14125cfa3039SJohannes Berg dma_unmap_single(&fep->pdev->dev, 14135cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 14145cfa3039SJohannes Berg fec16_to_cpu(bdp->cbd_datlen), 14155cfa3039SJohannes Berg DMA_TO_DEVICE); 14165cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(0); 14177fafe803STroy Kisky if (!skb) 14187fafe803STroy Kisky goto skb_done; 1419793fc096SFrank Li 1420793fc096SFrank Li /* Check for errors. */ 1421793fc096SFrank Li if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC | 1422793fc096SFrank Li BD_ENET_TX_RL | BD_ENET_TX_UN | 1423793fc096SFrank Li BD_ENET_TX_CSL)) { 1424793fc096SFrank Li ndev->stats.tx_errors++; 1425793fc096SFrank Li if (status & BD_ENET_TX_HB) /* No heartbeat */ 1426793fc096SFrank Li ndev->stats.tx_heartbeat_errors++; 1427793fc096SFrank Li if (status & BD_ENET_TX_LC) /* Late collision */ 1428793fc096SFrank Li ndev->stats.tx_window_errors++; 1429793fc096SFrank Li if (status & BD_ENET_TX_RL) /* Retrans limit */ 1430793fc096SFrank Li ndev->stats.tx_aborted_errors++; 1431793fc096SFrank Li if (status & BD_ENET_TX_UN) /* Underrun */ 1432793fc096SFrank Li ndev->stats.tx_fifo_errors++; 1433793fc096SFrank Li if (status & BD_ENET_TX_CSL) /* Carrier lost */ 1434793fc096SFrank Li ndev->stats.tx_carrier_errors++; 1435793fc096SFrank Li } else { 1436793fc096SFrank Li ndev->stats.tx_packets++; 14376e909283SNimrod Andy ndev->stats.tx_bytes += skb->len; 1438793fc096SFrank Li } 1439793fc096SFrank Li 144034074639SSergey Organov /* NOTE: SKBTX_IN_PROGRESS being set does not imply it's we who 144134074639SSergey Organov * are to time stamp the packet, so we still need to check time 144234074639SSergey Organov * stamping enabled flag. 144334074639SSergey Organov */ 144434074639SSergey Organov if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS && 144534074639SSergey Organov fep->hwts_tx_en) && 1446793fc096SFrank Li fep->bufdesc_ex) { 1447793fc096SFrank Li struct skb_shared_hwtstamps shhwtstamps; 1448793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 1449793fc096SFrank Li 14505cfa3039SJohannes Berg fec_enet_hwtstamp(fep, fec32_to_cpu(ebdp->ts), &shhwtstamps); 1451793fc096SFrank Li skb_tstamp_tx(skb, &shhwtstamps); 1452793fc096SFrank Li } 1453793fc096SFrank Li 1454793fc096SFrank Li /* Deferred means some collisions occurred during transmit, 1455793fc096SFrank Li * but we eventually sent the packet OK. 1456793fc096SFrank Li */ 1457793fc096SFrank Li if (status & BD_ENET_TX_DEF) 1458793fc096SFrank Li ndev->stats.collisions++; 1459793fc096SFrank Li 1460793fc096SFrank Li /* Free the sk buffer associated with this last transmit */ 1461793fc096SFrank Li dev_kfree_skb_any(skb); 14627fafe803STroy Kisky skb_done: 1463c4bc44c6SKevin Hao /* Make sure the update to bdp and tx_skbuff are performed 1464c4bc44c6SKevin Hao * before dirty_tx 1465c4bc44c6SKevin Hao */ 1466c4bc44c6SKevin Hao wmb(); 14674d494cdcSFugang Duan txq->dirty_tx = bdp; 1468793fc096SFrank Li 1469793fc096SFrank Li /* Update pointer to next buffer descriptor to be transmitted */ 14707355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 1471793fc096SFrank Li 1472793fc096SFrank Li /* Since we have freed up a buffer, the ring is no longer full 1473793fc096SFrank Li */ 1474657ade07SRickard x Andersson if (netif_tx_queue_stopped(nq)) { 14757355f276STroy Kisky entries_free = fec_enet_get_free_txdesc_num(txq); 14764d494cdcSFugang Duan if (entries_free >= txq->tx_wake_threshold) 14774d494cdcSFugang Duan netif_tx_wake_queue(nq); 1478793fc096SFrank Li } 147979f33912SNimrod Andy } 1480ccea2968SRussell King 1481c10bc0e7SFugang Duan /* ERR006358: Keep the transmitter going */ 14827355f276STroy Kisky if (bdp != txq->bd.cur && 148353bb20d1STroy Kisky readl(txq->bd.reg_desc_active) == 0) 148453bb20d1STroy Kisky writel(0, txq->bd.reg_desc_active); 14854d494cdcSFugang Duan } 14864d494cdcSFugang Duan 14877cdaa4ccSTobias Waldekranz static void fec_enet_tx(struct net_device *ndev) 14884d494cdcSFugang Duan { 14894d494cdcSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 14907cdaa4ccSTobias Waldekranz int i; 14917cdaa4ccSTobias Waldekranz 14927cdaa4ccSTobias Waldekranz /* Make sure that AVB queues are processed first. */ 14937cdaa4ccSTobias Waldekranz for (i = fep->num_tx_queues - 1; i >= 0; i--) 14947cdaa4ccSTobias Waldekranz fec_enet_tx_queue(ndev, i); 1495793fc096SFrank Li } 1496793fc096SFrank Li 149795698ff6SShenwei Wang static int __maybe_unused 14981b7bde6dSNimrod Andy fec_enet_new_rxbdp(struct net_device *ndev, struct bufdesc *bdp, struct sk_buff *skb) 14991b7bde6dSNimrod Andy { 15001b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 15011b7bde6dSNimrod Andy int off; 15021b7bde6dSNimrod Andy 15031b7bde6dSNimrod Andy off = ((unsigned long)skb->data) & fep->rx_align; 15041b7bde6dSNimrod Andy if (off) 15051b7bde6dSNimrod Andy skb_reserve(skb, fep->rx_align + 1 - off); 15061b7bde6dSNimrod Andy 15075cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(dma_map_single(&fep->pdev->dev, skb->data, FEC_ENET_RX_FRSIZE - fep->rx_align, DMA_FROM_DEVICE)); 15085cfa3039SJohannes Berg if (dma_mapping_error(&fep->pdev->dev, fec32_to_cpu(bdp->cbd_bufaddr))) { 15091b7bde6dSNimrod Andy if (net_ratelimit()) 15101b7bde6dSNimrod Andy netdev_err(ndev, "Rx DMA memory map failed\n"); 15111b7bde6dSNimrod Andy return -ENOMEM; 15121b7bde6dSNimrod Andy } 15131b7bde6dSNimrod Andy 15141b7bde6dSNimrod Andy return 0; 15151b7bde6dSNimrod Andy } 15161b7bde6dSNimrod Andy 151795698ff6SShenwei Wang static bool __maybe_unused 151895698ff6SShenwei Wang fec_enet_copybreak(struct net_device *ndev, struct sk_buff **skb, 15191310b544SLothar Waßmann struct bufdesc *bdp, u32 length, bool swap) 15201b7bde6dSNimrod Andy { 15211b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 15221b7bde6dSNimrod Andy struct sk_buff *new_skb; 15231b7bde6dSNimrod Andy 15241b7bde6dSNimrod Andy if (length > fep->rx_copybreak) 15251b7bde6dSNimrod Andy return false; 15261b7bde6dSNimrod Andy 15271b7bde6dSNimrod Andy new_skb = netdev_alloc_skb(ndev, length); 15281b7bde6dSNimrod Andy if (!new_skb) 15291b7bde6dSNimrod Andy return false; 15301b7bde6dSNimrod Andy 15315cfa3039SJohannes Berg dma_sync_single_for_cpu(&fep->pdev->dev, 15325cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 15331b7bde6dSNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 15341b7bde6dSNimrod Andy DMA_FROM_DEVICE); 15351310b544SLothar Waßmann if (!swap) 15361b7bde6dSNimrod Andy memcpy(new_skb->data, (*skb)->data, length); 15371310b544SLothar Waßmann else 15381310b544SLothar Waßmann swap_buffer2(new_skb->data, (*skb)->data, length); 15391b7bde6dSNimrod Andy *skb = new_skb; 15401b7bde6dSNimrod Andy 15411b7bde6dSNimrod Andy return true; 15421b7bde6dSNimrod Andy } 15431b7bde6dSNimrod Andy 154495698ff6SShenwei Wang static void fec_enet_update_cbd(struct fec_enet_priv_rx_q *rxq, 154595698ff6SShenwei Wang struct bufdesc *bdp, int index) 154695698ff6SShenwei Wang { 154795698ff6SShenwei Wang struct page *new_page; 154895698ff6SShenwei Wang dma_addr_t phys_addr; 154995698ff6SShenwei Wang 155095698ff6SShenwei Wang new_page = page_pool_dev_alloc_pages(rxq->page_pool); 155195698ff6SShenwei Wang WARN_ON(!new_page); 155295698ff6SShenwei Wang rxq->rx_skb_info[index].page = new_page; 155395698ff6SShenwei Wang 155495698ff6SShenwei Wang rxq->rx_skb_info[index].offset = FEC_ENET_XDP_HEADROOM; 155595698ff6SShenwei Wang phys_addr = page_pool_get_dma_addr(new_page) + FEC_ENET_XDP_HEADROOM; 155695698ff6SShenwei Wang bdp->cbd_bufaddr = cpu_to_fec32(phys_addr); 155795698ff6SShenwei Wang } 155895698ff6SShenwei Wang 15597355f276STroy Kisky /* During a receive, the bd_rx.cur points to the current incoming buffer. 1560793fc096SFrank Li * When we update through the ring, if the next incoming buffer has 1561793fc096SFrank Li * not been given to the system, we just set the empty indicator, 1562793fc096SFrank Li * effectively tossing the packet. 1563793fc096SFrank Li */ 1564793fc096SFrank Li static int 15654d494cdcSFugang Duan fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) 1566793fc096SFrank Li { 1567793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 15684d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 1569793fc096SFrank Li struct bufdesc *bdp; 1570793fc096SFrank Li unsigned short status; 1571793fc096SFrank Li struct sk_buff *skb; 1572793fc096SFrank Li ushort pkt_len; 1573793fc096SFrank Li __u8 *data; 1574793fc096SFrank Li int pkt_received = 0; 1575cdffcf1bSJim Baxter struct bufdesc_ex *ebdp = NULL; 1576cdffcf1bSJim Baxter bool vlan_packet_rcvd = false; 1577cdffcf1bSJim Baxter u16 vlan_tag; 1578d842a31fSDuan Fugang-B38611 int index = 0; 15796b7e4008SLothar Waßmann bool need_swap = fep->quirks & FEC_QUIRK_SWAP_FRAME; 158095698ff6SShenwei Wang struct page *page; 1581793fc096SFrank Li 1582793fc096SFrank Li #ifdef CONFIG_M532x 1583793fc096SFrank Li flush_cache_all(); 1584793fc096SFrank Li #endif 15854d494cdcSFugang Duan rxq = fep->rx_queue[queue_id]; 1586793fc096SFrank Li 1587793fc096SFrank Li /* First, grab all of the stats for the incoming packet. 1588793fc096SFrank Li * These get messed up if we get called due to a busy condition. 1589793fc096SFrank Li */ 15907355f276STroy Kisky bdp = rxq->bd.cur; 1591793fc096SFrank Li 15925cfa3039SJohannes Berg while (!((status = fec16_to_cpu(bdp->cbd_sc)) & BD_ENET_RX_EMPTY)) { 1593793fc096SFrank Li 1594793fc096SFrank Li if (pkt_received >= budget) 1595793fc096SFrank Li break; 1596793fc096SFrank Li pkt_received++; 1597793fc096SFrank Li 1598b5bd95d1SJoakim Zhang writel(FEC_ENET_RXF_GET(queue_id), fep->hwp + FEC_IEVENT); 1599db3421c1SRussell King 1600793fc096SFrank Li /* Check for errors. */ 1601095098e1STroy Kisky status ^= BD_ENET_RX_LAST; 1602793fc096SFrank Li if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | 1603095098e1STroy Kisky BD_ENET_RX_CR | BD_ENET_RX_OV | BD_ENET_RX_LAST | 1604095098e1STroy Kisky BD_ENET_RX_CL)) { 1605793fc096SFrank Li ndev->stats.rx_errors++; 1606095098e1STroy Kisky if (status & BD_ENET_RX_OV) { 1607095098e1STroy Kisky /* FIFO overrun */ 1608095098e1STroy Kisky ndev->stats.rx_fifo_errors++; 1609095098e1STroy Kisky goto rx_processing_done; 1610095098e1STroy Kisky } 1611095098e1STroy Kisky if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH 1612095098e1STroy Kisky | BD_ENET_RX_LAST)) { 1613793fc096SFrank Li /* Frame too long or too short. */ 1614793fc096SFrank Li ndev->stats.rx_length_errors++; 1615095098e1STroy Kisky if (status & BD_ENET_RX_LAST) 1616095098e1STroy Kisky netdev_err(ndev, "rcv is not +last\n"); 1617793fc096SFrank Li } 1618793fc096SFrank Li if (status & BD_ENET_RX_CR) /* CRC Error */ 1619793fc096SFrank Li ndev->stats.rx_crc_errors++; 1620095098e1STroy Kisky /* Report late collisions as a frame error. */ 1621095098e1STroy Kisky if (status & (BD_ENET_RX_NO | BD_ENET_RX_CL)) 1622793fc096SFrank Li ndev->stats.rx_frame_errors++; 1623793fc096SFrank Li goto rx_processing_done; 1624793fc096SFrank Li } 1625793fc096SFrank Li 1626793fc096SFrank Li /* Process the incoming frame. */ 1627793fc096SFrank Li ndev->stats.rx_packets++; 16285cfa3039SJohannes Berg pkt_len = fec16_to_cpu(bdp->cbd_datlen); 1629793fc096SFrank Li ndev->stats.rx_bytes += pkt_len; 1630793fc096SFrank Li 16317355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &rxq->bd); 163295698ff6SShenwei Wang page = rxq->rx_skb_info[index].page; 163395698ff6SShenwei Wang dma_sync_single_for_cpu(&fep->pdev->dev, 163495698ff6SShenwei Wang fec32_to_cpu(bdp->cbd_bufaddr), 163595698ff6SShenwei Wang pkt_len, 163695698ff6SShenwei Wang DMA_FROM_DEVICE); 163795698ff6SShenwei Wang prefetch(page_address(page)); 163895698ff6SShenwei Wang fec_enet_update_cbd(rxq, bdp, index); 16391b7bde6dSNimrod Andy 16401b7bde6dSNimrod Andy /* The packet length includes FCS, but we don't want to 16411b7bde6dSNimrod Andy * include that when passing upstream as it messes up 16421b7bde6dSNimrod Andy * bridging applications. 16431b7bde6dSNimrod Andy */ 164495698ff6SShenwei Wang skb = build_skb(page_address(page), PAGE_SIZE); 164595698ff6SShenwei Wang skb_reserve(skb, FEC_ENET_XDP_HEADROOM); 16461b7bde6dSNimrod Andy skb_put(skb, pkt_len - 4); 164795698ff6SShenwei Wang skb_mark_for_recycle(skb); 16481b7bde6dSNimrod Andy data = skb->data; 16493ac72b7bSEric Nelson 165095698ff6SShenwei Wang if (need_swap) 1651235bde1eSFabio Estevam swap_buffer(data, pkt_len); 1652235bde1eSFabio Estevam 16533ac72b7bSEric Nelson #if !defined(CONFIG_M5272) 16543ac72b7bSEric Nelson if (fep->quirks & FEC_QUIRK_HAS_RACC) 16553ac72b7bSEric Nelson data = skb_pull_inline(skb, 2); 16563ac72b7bSEric Nelson #endif 16573ac72b7bSEric Nelson 1658cdffcf1bSJim Baxter /* Extract the enhanced buffer descriptor */ 1659cdffcf1bSJim Baxter ebdp = NULL; 1660cdffcf1bSJim Baxter if (fep->bufdesc_ex) 1661cdffcf1bSJim Baxter ebdp = (struct bufdesc_ex *)bdp; 1662cdffcf1bSJim Baxter 1663cdffcf1bSJim Baxter /* If this is a VLAN packet remove the VLAN Tag */ 1664cdffcf1bSJim Baxter vlan_packet_rcvd = false; 1665cdffcf1bSJim Baxter if ((ndev->features & NETIF_F_HW_VLAN_CTAG_RX) && 16665cfa3039SJohannes Berg fep->bufdesc_ex && 16675cfa3039SJohannes Berg (ebdp->cbd_esc & cpu_to_fec32(BD_ENET_RX_VLAN))) { 1668cdffcf1bSJim Baxter /* Push and remove the vlan tag */ 1669cdffcf1bSJim Baxter struct vlan_hdr *vlan_header = 1670cdffcf1bSJim Baxter (struct vlan_hdr *) (data + ETH_HLEN); 1671cdffcf1bSJim Baxter vlan_tag = ntohs(vlan_header->h_vlan_TCI); 1672cdffcf1bSJim Baxter 1673cdffcf1bSJim Baxter vlan_packet_rcvd = true; 16741b7bde6dSNimrod Andy 1675af5cbc98SNimrod Andy memmove(skb->data + VLAN_HLEN, data, ETH_ALEN * 2); 16761b7bde6dSNimrod Andy skb_pull(skb, VLAN_HLEN); 1677cdffcf1bSJim Baxter } 1678cdffcf1bSJim Baxter 1679793fc096SFrank Li skb->protocol = eth_type_trans(skb, ndev); 1680793fc096SFrank Li 1681793fc096SFrank Li /* Get receive timestamp from the skb */ 1682bfd4ecddSRussell King if (fep->hwts_rx_en && fep->bufdesc_ex) 16835cfa3039SJohannes Berg fec_enet_hwtstamp(fep, fec32_to_cpu(ebdp->ts), 1684bfd4ecddSRussell King skb_hwtstamps(skb)); 1685793fc096SFrank Li 16864c09eed9SJim Baxter if (fep->bufdesc_ex && 16874c09eed9SJim Baxter (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { 16885cfa3039SJohannes Berg if (!(ebdp->cbd_esc & cpu_to_fec32(FLAG_RX_CSUM_ERROR))) { 16894c09eed9SJim Baxter /* don't check it */ 16904c09eed9SJim Baxter skb->ip_summed = CHECKSUM_UNNECESSARY; 16914c09eed9SJim Baxter } else { 16924c09eed9SJim Baxter skb_checksum_none_assert(skb); 16934c09eed9SJim Baxter } 16944c09eed9SJim Baxter } 16954c09eed9SJim Baxter 1696cdffcf1bSJim Baxter /* Handle received VLAN packets */ 1697cdffcf1bSJim Baxter if (vlan_packet_rcvd) 1698cdffcf1bSJim Baxter __vlan_hwaccel_put_tag(skb, 1699cdffcf1bSJim Baxter htons(ETH_P_8021Q), 1700cdffcf1bSJim Baxter vlan_tag); 1701cdffcf1bSJim Baxter 17027cdaa4ccSTobias Waldekranz skb_record_rx_queue(skb, queue_id); 1703793fc096SFrank Li napi_gro_receive(&fep->napi, skb); 1704793fc096SFrank Li 1705793fc096SFrank Li rx_processing_done: 1706793fc096SFrank Li /* Clear the status flags for this buffer */ 1707793fc096SFrank Li status &= ~BD_ENET_RX_STATS; 1708793fc096SFrank Li 1709793fc096SFrank Li /* Mark the buffer empty */ 1710793fc096SFrank Li status |= BD_ENET_RX_EMPTY; 1711793fc096SFrank Li 1712793fc096SFrank Li if (fep->bufdesc_ex) { 1713793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 1714793fc096SFrank Li 17155cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(BD_ENET_RX_INT); 1716793fc096SFrank Li ebdp->cbd_prot = 0; 1717793fc096SFrank Li ebdp->cbd_bdu = 0; 1718793fc096SFrank Li } 1719be293467STroy Kisky /* Make sure the updates to rest of the descriptor are 1720be293467STroy Kisky * performed before transferring ownership. 1721be293467STroy Kisky */ 1722be293467STroy Kisky wmb(); 1723be293467STroy Kisky bdp->cbd_sc = cpu_to_fec16(status); 1724793fc096SFrank Li 1725793fc096SFrank Li /* Update BD pointer to next entry */ 17267355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 172736e24e2eSDuan Fugang-B38611 1728793fc096SFrank Li /* Doing this here will keep the FEC running while we process 1729793fc096SFrank Li * incoming frames. On a heavily loaded network, we should be 1730793fc096SFrank Li * able to keep up at the expense of system resources. 1731793fc096SFrank Li */ 173253bb20d1STroy Kisky writel(0, rxq->bd.reg_desc_active); 1733793fc096SFrank Li } 17347355f276STroy Kisky rxq->bd.cur = bdp; 1735793fc096SFrank Li return pkt_received; 1736793fc096SFrank Li } 1737793fc096SFrank Li 17387cdaa4ccSTobias Waldekranz static int fec_enet_rx(struct net_device *ndev, int budget) 17394d494cdcSFugang Duan { 17404d494cdcSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 17417cdaa4ccSTobias Waldekranz int i, done = 0; 17424d494cdcSFugang Duan 17437cdaa4ccSTobias Waldekranz /* Make sure that AVB queues are processed first. */ 17447cdaa4ccSTobias Waldekranz for (i = fep->num_rx_queues - 1; i >= 0; i--) 17457cdaa4ccSTobias Waldekranz done += fec_enet_rx_queue(ndev, budget - done, i); 17461c021bb7SUwe Kleine-König 17477cdaa4ccSTobias Waldekranz return done; 17484d494cdcSFugang Duan } 17494d494cdcSFugang Duan 17507cdaa4ccSTobias Waldekranz static bool fec_enet_collect_events(struct fec_enet_private *fep) 17514d494cdcSFugang Duan { 1752793fc096SFrank Li uint int_events; 1753793fc096SFrank Li 1754793fc096SFrank Li int_events = readl(fep->hwp + FEC_IEVENT); 1755f166f890SAndrew Lunn 1756f166f890SAndrew Lunn /* Don't clear MDIO events, we poll for those */ 1757f166f890SAndrew Lunn int_events &= ~FEC_ENET_MII; 1758f166f890SAndrew Lunn 175994191fd6SNimrod Andy writel(int_events, fep->hwp + FEC_IEVENT); 1760793fc096SFrank Li 17617cdaa4ccSTobias Waldekranz return int_events != 0; 17627cdaa4ccSTobias Waldekranz } 17637cdaa4ccSTobias Waldekranz 17647cdaa4ccSTobias Waldekranz static irqreturn_t 17657cdaa4ccSTobias Waldekranz fec_enet_interrupt(int irq, void *dev_id) 17667cdaa4ccSTobias Waldekranz { 17677cdaa4ccSTobias Waldekranz struct net_device *ndev = dev_id; 17687cdaa4ccSTobias Waldekranz struct fec_enet_private *fep = netdev_priv(ndev); 17697cdaa4ccSTobias Waldekranz irqreturn_t ret = IRQ_NONE; 17707cdaa4ccSTobias Waldekranz 17717cdaa4ccSTobias Waldekranz if (fec_enet_collect_events(fep) && fep->link) { 1772793fc096SFrank Li ret = IRQ_HANDLED; 1773793fc096SFrank Li 177494191fd6SNimrod Andy if (napi_schedule_prep(&fep->napi)) { 1775f166f890SAndrew Lunn /* Disable interrupts */ 1776f166f890SAndrew Lunn writel(0, fep->hwp + FEC_IMASK); 177794191fd6SNimrod Andy __napi_schedule(&fep->napi); 177894191fd6SNimrod Andy } 1779793fc096SFrank Li } 1780793fc096SFrank Li 1781793fc096SFrank Li return ret; 1782793fc096SFrank Li } 1783793fc096SFrank Li 1784793fc096SFrank Li static int fec_enet_rx_napi(struct napi_struct *napi, int budget) 1785793fc096SFrank Li { 1786793fc096SFrank Li struct net_device *ndev = napi->dev; 1787793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 17887cdaa4ccSTobias Waldekranz int done = 0; 17897a16807cSRussell King 17907cdaa4ccSTobias Waldekranz do { 17917cdaa4ccSTobias Waldekranz done += fec_enet_rx(ndev, budget - done); 1792793fc096SFrank Li fec_enet_tx(ndev); 17937cdaa4ccSTobias Waldekranz } while ((done < budget) && fec_enet_collect_events(fep)); 1794793fc096SFrank Li 17957cdaa4ccSTobias Waldekranz if (done < budget) { 17967cdaa4ccSTobias Waldekranz napi_complete_done(napi, done); 1797793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 1798793fc096SFrank Li } 17997cdaa4ccSTobias Waldekranz 18007cdaa4ccSTobias Waldekranz return done; 1801793fc096SFrank Li } 1802793fc096SFrank Li 1803793fc096SFrank Li /* ------------------------------------------------------------------------- */ 1804052fcc45SFugang Duan static int fec_get_mac(struct net_device *ndev) 1805793fc096SFrank Li { 1806793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1807793fc096SFrank Li unsigned char *iap, tmpaddr[ETH_ALEN]; 180883216e39SMichael Walle int ret; 1809793fc096SFrank Li 1810793fc096SFrank Li /* 1811793fc096SFrank Li * try to get mac address in following order: 1812793fc096SFrank Li * 1813793fc096SFrank Li * 1) module parameter via kernel command line in form 1814793fc096SFrank Li * fec.macaddr=0x00,0x04,0x9f,0x01,0x30,0xe0 1815793fc096SFrank Li */ 1816793fc096SFrank Li iap = macaddr; 1817793fc096SFrank Li 1818793fc096SFrank Li /* 1819793fc096SFrank Li * 2) from device tree data 1820793fc096SFrank Li */ 1821793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 1822793fc096SFrank Li struct device_node *np = fep->pdev->dev.of_node; 1823793fc096SFrank Li if (np) { 182483216e39SMichael Walle ret = of_get_mac_address(np, tmpaddr); 182583216e39SMichael Walle if (!ret) 182683216e39SMichael Walle iap = tmpaddr; 1827052fcc45SFugang Duan else if (ret == -EPROBE_DEFER) 1828052fcc45SFugang Duan return ret; 1829793fc096SFrank Li } 1830793fc096SFrank Li } 1831793fc096SFrank Li 1832793fc096SFrank Li /* 1833793fc096SFrank Li * 3) from flash or fuse (via platform data) 1834793fc096SFrank Li */ 1835793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 1836793fc096SFrank Li #ifdef CONFIG_M5272 1837793fc096SFrank Li if (FEC_FLASHMAC) 1838793fc096SFrank Li iap = (unsigned char *)FEC_FLASHMAC; 1839793fc096SFrank Li #else 184032d1bbb1SGeert Uytterhoeven struct fec_platform_data *pdata = dev_get_platdata(&fep->pdev->dev); 184132d1bbb1SGeert Uytterhoeven 1842793fc096SFrank Li if (pdata) 1843793fc096SFrank Li iap = (unsigned char *)&pdata->mac; 1844793fc096SFrank Li #endif 1845793fc096SFrank Li } 1846793fc096SFrank Li 1847793fc096SFrank Li /* 1848793fc096SFrank Li * 4) FEC mac registers set by bootloader 1849793fc096SFrank Li */ 1850793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 18517d7628f3SDan Carpenter *((__be32 *) &tmpaddr[0]) = 18527d7628f3SDan Carpenter cpu_to_be32(readl(fep->hwp + FEC_ADDR_LOW)); 18537d7628f3SDan Carpenter *((__be16 *) &tmpaddr[4]) = 18547d7628f3SDan Carpenter cpu_to_be16(readl(fep->hwp + FEC_ADDR_HIGH) >> 16); 1855793fc096SFrank Li iap = &tmpaddr[0]; 1856793fc096SFrank Li } 1857793fc096SFrank Li 1858ff5b2fabSLucas Stach /* 1859ff5b2fabSLucas Stach * 5) random mac address 1860ff5b2fabSLucas Stach */ 1861ff5b2fabSLucas Stach if (!is_valid_ether_addr(iap)) { 1862ff5b2fabSLucas Stach /* Report it and use a random ethernet address instead */ 1863a19a0582SFabio Estevam dev_err(&fep->pdev->dev, "Invalid MAC address: %pM\n", iap); 1864ff5b2fabSLucas Stach eth_hw_addr_random(ndev); 1865a19a0582SFabio Estevam dev_info(&fep->pdev->dev, "Using random MAC address: %pM\n", 1866ff5b2fabSLucas Stach ndev->dev_addr); 1867052fcc45SFugang Duan return 0; 1868ff5b2fabSLucas Stach } 1869ff5b2fabSLucas Stach 1870793fc096SFrank Li /* Adjust MAC if using macaddr */ 1871ba3fdfe3SJakub Kicinski eth_hw_addr_gen(ndev, iap, iap == macaddr ? fep->dev_id : 0); 1872052fcc45SFugang Duan 1873052fcc45SFugang Duan return 0; 1874793fc096SFrank Li } 1875793fc096SFrank Li 1876793fc096SFrank Li /* ------------------------------------------------------------------------- */ 1877793fc096SFrank Li 1878793fc096SFrank Li /* 1879793fc096SFrank Li * Phy section 1880793fc096SFrank Li */ 1881793fc096SFrank Li static void fec_enet_adjust_link(struct net_device *ndev) 1882793fc096SFrank Li { 1883793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 188445f5c327SPhilippe Reynes struct phy_device *phy_dev = ndev->phydev; 1885793fc096SFrank Li int status_change = 0; 1886793fc096SFrank Li 18878ce5624fSRussell King /* 18888ce5624fSRussell King * If the netdev is down, or is going down, we're not interested 18898ce5624fSRussell King * in link state events, so just mark our idea of the link as down 18908ce5624fSRussell King * and ignore the event. 18918ce5624fSRussell King */ 18928ce5624fSRussell King if (!netif_running(ndev) || !netif_device_present(ndev)) { 18938ce5624fSRussell King fep->link = 0; 18948ce5624fSRussell King } else if (phy_dev->link) { 1895793fc096SFrank Li if (!fep->link) { 1896793fc096SFrank Li fep->link = phy_dev->link; 1897793fc096SFrank Li status_change = 1; 1898793fc096SFrank Li } 1899793fc096SFrank Li 1900ef83337dSRussell King if (fep->full_duplex != phy_dev->duplex) { 1901ef83337dSRussell King fep->full_duplex = phy_dev->duplex; 1902793fc096SFrank Li status_change = 1; 1903ef83337dSRussell King } 1904793fc096SFrank Li 1905793fc096SFrank Li if (phy_dev->speed != fep->speed) { 1906793fc096SFrank Li fep->speed = phy_dev->speed; 1907793fc096SFrank Li status_change = 1; 1908793fc096SFrank Li } 1909793fc096SFrank Li 1910793fc096SFrank Li /* if any of the above changed restart the FEC */ 1911dbc64a8eSRussell King if (status_change) { 1912dbc64a8eSRussell King napi_disable(&fep->napi); 1913dbc64a8eSRussell King netif_tx_lock_bh(ndev); 1914ef83337dSRussell King fec_restart(ndev); 1915657ade07SRickard x Andersson netif_tx_wake_all_queues(ndev); 19166af42d42SRussell King netif_tx_unlock_bh(ndev); 1917dbc64a8eSRussell King napi_enable(&fep->napi); 1918dbc64a8eSRussell King } 1919793fc096SFrank Li } else { 1920793fc096SFrank Li if (fep->link) { 1921f208ce10SRussell King napi_disable(&fep->napi); 1922f208ce10SRussell King netif_tx_lock_bh(ndev); 1923793fc096SFrank Li fec_stop(ndev); 1924f208ce10SRussell King netif_tx_unlock_bh(ndev); 1925f208ce10SRussell King napi_enable(&fep->napi); 19266e0895c2SDavid S. Miller fep->link = phy_dev->link; 1927793fc096SFrank Li status_change = 1; 1928793fc096SFrank Li } 1929793fc096SFrank Li } 1930793fc096SFrank Li 1931793fc096SFrank Li if (status_change) 1932793fc096SFrank Li phy_print_status(phy_dev); 1933793fc096SFrank Li } 1934793fc096SFrank Li 1935f166f890SAndrew Lunn static int fec_enet_mdio_wait(struct fec_enet_private *fep) 1936f166f890SAndrew Lunn { 1937f166f890SAndrew Lunn uint ievent; 1938f166f890SAndrew Lunn int ret; 1939f166f890SAndrew Lunn 1940f166f890SAndrew Lunn ret = readl_poll_timeout_atomic(fep->hwp + FEC_IEVENT, ievent, 1941f166f890SAndrew Lunn ievent & FEC_ENET_MII, 2, 30000); 1942f166f890SAndrew Lunn 1943f166f890SAndrew Lunn if (!ret) 1944f166f890SAndrew Lunn writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT); 1945f166f890SAndrew Lunn 1946f166f890SAndrew Lunn return ret; 1947f166f890SAndrew Lunn } 1948f166f890SAndrew Lunn 1949793fc096SFrank Li static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum) 1950793fc096SFrank Li { 1951793fc096SFrank Li struct fec_enet_private *fep = bus->priv; 19528fff755eSAndrew Lunn struct device *dev = &fep->pdev->dev; 1953d3ee8ec7SMarco Hartmann int ret = 0, frame_start, frame_addr, frame_op; 1954d3ee8ec7SMarco Hartmann bool is_c45 = !!(regnum & MII_ADDR_C45); 19558fff755eSAndrew Lunn 1956da875fa5SZhang Qilong ret = pm_runtime_resume_and_get(dev); 1957b0c6ce24SFabio Estevam if (ret < 0) 19588fff755eSAndrew Lunn return ret; 1959793fc096SFrank Li 1960d3ee8ec7SMarco Hartmann if (is_c45) { 1961d3ee8ec7SMarco Hartmann frame_start = FEC_MMFR_ST_C45; 1962d3ee8ec7SMarco Hartmann 1963d3ee8ec7SMarco Hartmann /* write address */ 1964d3ee8ec7SMarco Hartmann frame_addr = (regnum >> 16); 1965d3ee8ec7SMarco Hartmann writel(frame_start | FEC_MMFR_OP_ADDR_WRITE | 1966d3ee8ec7SMarco Hartmann FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | 1967d3ee8ec7SMarco Hartmann FEC_MMFR_TA | (regnum & 0xFFFF), 1968d3ee8ec7SMarco Hartmann fep->hwp + FEC_MII_DATA); 1969d3ee8ec7SMarco Hartmann 1970d3ee8ec7SMarco Hartmann /* wait for end of transfer */ 1971f166f890SAndrew Lunn ret = fec_enet_mdio_wait(fep); 1972f166f890SAndrew Lunn if (ret) { 1973d3ee8ec7SMarco Hartmann netdev_err(fep->netdev, "MDIO address write timeout\n"); 1974d3ee8ec7SMarco Hartmann goto out; 1975d3ee8ec7SMarco Hartmann } 1976d3ee8ec7SMarco Hartmann 1977d3ee8ec7SMarco Hartmann frame_op = FEC_MMFR_OP_READ_C45; 1978d3ee8ec7SMarco Hartmann 1979d3ee8ec7SMarco Hartmann } else { 1980d3ee8ec7SMarco Hartmann /* C22 read */ 1981d3ee8ec7SMarco Hartmann frame_op = FEC_MMFR_OP_READ; 1982d3ee8ec7SMarco Hartmann frame_start = FEC_MMFR_ST; 1983d3ee8ec7SMarco Hartmann frame_addr = regnum; 1984d3ee8ec7SMarco Hartmann } 1985d3ee8ec7SMarco Hartmann 1986793fc096SFrank Li /* start a read op */ 1987d3ee8ec7SMarco Hartmann writel(frame_start | frame_op | 1988d3ee8ec7SMarco Hartmann FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | 1989793fc096SFrank Li FEC_MMFR_TA, fep->hwp + FEC_MII_DATA); 1990793fc096SFrank Li 1991793fc096SFrank Li /* wait for end of transfer */ 1992f166f890SAndrew Lunn ret = fec_enet_mdio_wait(fep); 1993f166f890SAndrew Lunn if (ret) { 199431b7720cSJoe Perches netdev_err(fep->netdev, "MDIO read timeout\n"); 19958fff755eSAndrew Lunn goto out; 1996793fc096SFrank Li } 1997793fc096SFrank Li 19988fff755eSAndrew Lunn ret = FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA)); 19998fff755eSAndrew Lunn 20008fff755eSAndrew Lunn out: 20018fff755eSAndrew Lunn pm_runtime_mark_last_busy(dev); 20028fff755eSAndrew Lunn pm_runtime_put_autosuspend(dev); 20038fff755eSAndrew Lunn 20048fff755eSAndrew Lunn return ret; 2005793fc096SFrank Li } 2006793fc096SFrank Li 2007793fc096SFrank Li static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum, 2008793fc096SFrank Li u16 value) 2009793fc096SFrank Li { 2010793fc096SFrank Li struct fec_enet_private *fep = bus->priv; 20118fff755eSAndrew Lunn struct device *dev = &fep->pdev->dev; 2012d3ee8ec7SMarco Hartmann int ret, frame_start, frame_addr; 2013d3ee8ec7SMarco Hartmann bool is_c45 = !!(regnum & MII_ADDR_C45); 20148fff755eSAndrew Lunn 2015da875fa5SZhang Qilong ret = pm_runtime_resume_and_get(dev); 2016b0c6ce24SFabio Estevam if (ret < 0) 20178fff755eSAndrew Lunn return ret; 2018793fc096SFrank Li 2019d3ee8ec7SMarco Hartmann if (is_c45) { 2020d3ee8ec7SMarco Hartmann frame_start = FEC_MMFR_ST_C45; 2021d3ee8ec7SMarco Hartmann 2022d3ee8ec7SMarco Hartmann /* write address */ 2023d3ee8ec7SMarco Hartmann frame_addr = (regnum >> 16); 2024d3ee8ec7SMarco Hartmann writel(frame_start | FEC_MMFR_OP_ADDR_WRITE | 2025d3ee8ec7SMarco Hartmann FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | 2026d3ee8ec7SMarco Hartmann FEC_MMFR_TA | (regnum & 0xFFFF), 2027d3ee8ec7SMarco Hartmann fep->hwp + FEC_MII_DATA); 2028d3ee8ec7SMarco Hartmann 2029d3ee8ec7SMarco Hartmann /* wait for end of transfer */ 2030f166f890SAndrew Lunn ret = fec_enet_mdio_wait(fep); 2031f166f890SAndrew Lunn if (ret) { 2032d3ee8ec7SMarco Hartmann netdev_err(fep->netdev, "MDIO address write timeout\n"); 2033d3ee8ec7SMarco Hartmann goto out; 2034d3ee8ec7SMarco Hartmann } 2035d3ee8ec7SMarco Hartmann } else { 2036d3ee8ec7SMarco Hartmann /* C22 write */ 2037d3ee8ec7SMarco Hartmann frame_start = FEC_MMFR_ST; 2038d3ee8ec7SMarco Hartmann frame_addr = regnum; 2039d3ee8ec7SMarco Hartmann } 2040d3ee8ec7SMarco Hartmann 2041793fc096SFrank Li /* start a write op */ 2042d3ee8ec7SMarco Hartmann writel(frame_start | FEC_MMFR_OP_WRITE | 2043d3ee8ec7SMarco Hartmann FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | 2044793fc096SFrank Li FEC_MMFR_TA | FEC_MMFR_DATA(value), 2045793fc096SFrank Li fep->hwp + FEC_MII_DATA); 2046793fc096SFrank Li 2047793fc096SFrank Li /* wait for end of transfer */ 2048f166f890SAndrew Lunn ret = fec_enet_mdio_wait(fep); 2049f166f890SAndrew Lunn if (ret) 205031b7720cSJoe Perches netdev_err(fep->netdev, "MDIO write timeout\n"); 2051793fc096SFrank Li 2052d3ee8ec7SMarco Hartmann out: 20538fff755eSAndrew Lunn pm_runtime_mark_last_busy(dev); 20548fff755eSAndrew Lunn pm_runtime_put_autosuspend(dev); 20558fff755eSAndrew Lunn 20568fff755eSAndrew Lunn return ret; 2057793fc096SFrank Li } 2058793fc096SFrank Li 205964a632daSMarek Vasut static void fec_enet_phy_reset_after_clk_enable(struct net_device *ndev) 206064a632daSMarek Vasut { 206164a632daSMarek Vasut struct fec_enet_private *fep = netdev_priv(ndev); 206264a632daSMarek Vasut struct phy_device *phy_dev = ndev->phydev; 206364a632daSMarek Vasut 206464a632daSMarek Vasut if (phy_dev) { 206564a632daSMarek Vasut phy_reset_after_clk_enable(phy_dev); 206664a632daSMarek Vasut } else if (fep->phy_node) { 206764a632daSMarek Vasut /* 206864a632daSMarek Vasut * If the PHY still is not bound to the MAC, but there is 206964a632daSMarek Vasut * OF PHY node and a matching PHY device instance already, 207064a632daSMarek Vasut * use the OF PHY node to obtain the PHY device instance, 207164a632daSMarek Vasut * and then use that PHY device instance when triggering 207264a632daSMarek Vasut * the PHY reset. 207364a632daSMarek Vasut */ 207464a632daSMarek Vasut phy_dev = of_phy_find_device(fep->phy_node); 207564a632daSMarek Vasut phy_reset_after_clk_enable(phy_dev); 207664a632daSMarek Vasut put_device(&phy_dev->mdio.dev); 207764a632daSMarek Vasut } 207864a632daSMarek Vasut } 207964a632daSMarek Vasut 2080e8fcfcd5SNimrod Andy static int fec_enet_clk_enable(struct net_device *ndev, bool enable) 2081e8fcfcd5SNimrod Andy { 2082e8fcfcd5SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 2083e8fcfcd5SNimrod Andy int ret; 2084e8fcfcd5SNimrod Andy 2085e8fcfcd5SNimrod Andy if (enable) { 2086e8fcfcd5SNimrod Andy ret = clk_prepare_enable(fep->clk_enet_out); 2087e8fcfcd5SNimrod Andy if (ret) 2088d7c3a206SAndy Duan return ret; 208901e5943aSUwe Kleine-König 2090e8fcfcd5SNimrod Andy if (fep->clk_ptp) { 209101b825f9SFrancesco Dolcini mutex_lock(&fep->ptp_clk_mutex); 2092e8fcfcd5SNimrod Andy ret = clk_prepare_enable(fep->clk_ptp); 209391c0d987SNimrod Andy if (ret) { 209401b825f9SFrancesco Dolcini mutex_unlock(&fep->ptp_clk_mutex); 2095e8fcfcd5SNimrod Andy goto failed_clk_ptp; 209691c0d987SNimrod Andy } else { 209791c0d987SNimrod Andy fep->ptp_clk_on = true; 209891c0d987SNimrod Andy } 209901b825f9SFrancesco Dolcini mutex_unlock(&fep->ptp_clk_mutex); 2100e8fcfcd5SNimrod Andy } 210101e5943aSUwe Kleine-König 21029b5330edSFugang Duan ret = clk_prepare_enable(fep->clk_ref); 21039b5330edSFugang Duan if (ret) 21049b5330edSFugang Duan goto failed_clk_ref; 21051b0a83acSRichard Leitner 2106fc539459SFugang Duan ret = clk_prepare_enable(fep->clk_2x_txclk); 2107fc539459SFugang Duan if (ret) 2108fc539459SFugang Duan goto failed_clk_2x_txclk; 2109fc539459SFugang Duan 211064a632daSMarek Vasut fec_enet_phy_reset_after_clk_enable(ndev); 2111e8fcfcd5SNimrod Andy } else { 2112e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_enet_out); 211391c0d987SNimrod Andy if (fep->clk_ptp) { 211401b825f9SFrancesco Dolcini mutex_lock(&fep->ptp_clk_mutex); 2115e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_ptp); 211691c0d987SNimrod Andy fep->ptp_clk_on = false; 211701b825f9SFrancesco Dolcini mutex_unlock(&fep->ptp_clk_mutex); 211891c0d987SNimrod Andy } 21199b5330edSFugang Duan clk_disable_unprepare(fep->clk_ref); 2120fc539459SFugang Duan clk_disable_unprepare(fep->clk_2x_txclk); 2121e8fcfcd5SNimrod Andy } 2122e8fcfcd5SNimrod Andy 2123e8fcfcd5SNimrod Andy return 0; 21249b5330edSFugang Duan 2125fc539459SFugang Duan failed_clk_2x_txclk: 2126fc539459SFugang Duan if (fep->clk_ref) 2127fc539459SFugang Duan clk_disable_unprepare(fep->clk_ref); 21289b5330edSFugang Duan failed_clk_ref: 2129a74d19baSLiu Xiang if (fep->clk_ptp) { 213001b825f9SFrancesco Dolcini mutex_lock(&fep->ptp_clk_mutex); 2131a74d19baSLiu Xiang clk_disable_unprepare(fep->clk_ptp); 2132a74d19baSLiu Xiang fep->ptp_clk_on = false; 213301b825f9SFrancesco Dolcini mutex_unlock(&fep->ptp_clk_mutex); 2134a74d19baSLiu Xiang } 2135e8fcfcd5SNimrod Andy failed_clk_ptp: 2136e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_enet_out); 2137e8fcfcd5SNimrod Andy 2138e8fcfcd5SNimrod Andy return ret; 2139e8fcfcd5SNimrod Andy } 2140e8fcfcd5SNimrod Andy 2141b820c114SJoakim Zhang static int fec_enet_parse_rgmii_delay(struct fec_enet_private *fep, 2142b820c114SJoakim Zhang struct device_node *np) 2143b820c114SJoakim Zhang { 2144b820c114SJoakim Zhang u32 rgmii_tx_delay, rgmii_rx_delay; 2145b820c114SJoakim Zhang 2146b820c114SJoakim Zhang /* For rgmii tx internal delay, valid values are 0ps and 2000ps */ 2147b820c114SJoakim Zhang if (!of_property_read_u32(np, "tx-internal-delay-ps", &rgmii_tx_delay)) { 2148b820c114SJoakim Zhang if (rgmii_tx_delay != 0 && rgmii_tx_delay != 2000) { 2149b820c114SJoakim Zhang dev_err(&fep->pdev->dev, "The only allowed RGMII TX delay values are: 0ps, 2000ps"); 2150b820c114SJoakim Zhang return -EINVAL; 2151b820c114SJoakim Zhang } else if (rgmii_tx_delay == 2000) { 2152b820c114SJoakim Zhang fep->rgmii_txc_dly = true; 2153b820c114SJoakim Zhang } 2154b820c114SJoakim Zhang } 2155b820c114SJoakim Zhang 2156b820c114SJoakim Zhang /* For rgmii rx internal delay, valid values are 0ps and 2000ps */ 2157b820c114SJoakim Zhang if (!of_property_read_u32(np, "rx-internal-delay-ps", &rgmii_rx_delay)) { 2158b820c114SJoakim Zhang if (rgmii_rx_delay != 0 && rgmii_rx_delay != 2000) { 2159b820c114SJoakim Zhang dev_err(&fep->pdev->dev, "The only allowed RGMII RX delay values are: 0ps, 2000ps"); 2160b820c114SJoakim Zhang return -EINVAL; 2161b820c114SJoakim Zhang } else if (rgmii_rx_delay == 2000) { 2162b820c114SJoakim Zhang fep->rgmii_rxc_dly = true; 2163b820c114SJoakim Zhang } 2164b820c114SJoakim Zhang } 2165b820c114SJoakim Zhang 2166b820c114SJoakim Zhang return 0; 2167b820c114SJoakim Zhang } 2168b820c114SJoakim Zhang 2169793fc096SFrank Li static int fec_enet_mii_probe(struct net_device *ndev) 2170793fc096SFrank Li { 2171793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2172793fc096SFrank Li struct phy_device *phy_dev = NULL; 2173793fc096SFrank Li char mdio_bus_id[MII_BUS_ID_SIZE]; 2174793fc096SFrank Li char phy_name[MII_BUS_ID_SIZE + 3]; 2175793fc096SFrank Li int phy_id; 2176793fc096SFrank Li int dev_id = fep->dev_id; 2177793fc096SFrank Li 2178407066f8SUwe Kleine-König if (fep->phy_node) { 2179407066f8SUwe Kleine-König phy_dev = of_phy_connect(ndev, fep->phy_node, 2180407066f8SUwe Kleine-König &fec_enet_adjust_link, 0, 2181407066f8SUwe Kleine-König fep->phy_interface); 21829558df3aSAndrew Lunn if (!phy_dev) { 21839558df3aSAndrew Lunn netdev_err(ndev, "Unable to connect to phy\n"); 2184213a9922SNimrod Andy return -ENODEV; 21859558df3aSAndrew Lunn } 2186407066f8SUwe Kleine-König } else { 2187793fc096SFrank Li /* check for attached phy */ 2188793fc096SFrank Li for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) { 21897f854420SAndrew Lunn if (!mdiobus_is_registered_device(fep->mii_bus, phy_id)) 2190793fc096SFrank Li continue; 2191793fc096SFrank Li if (dev_id--) 2192793fc096SFrank Li continue; 2193f029c781SWolfram Sang strscpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE); 2194793fc096SFrank Li break; 2195793fc096SFrank Li } 2196793fc096SFrank Li 2197793fc096SFrank Li if (phy_id >= PHY_MAX_ADDR) { 219831b7720cSJoe Perches netdev_info(ndev, "no PHY, assuming direct connection to switch\n"); 2199f029c781SWolfram Sang strscpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE); 2200793fc096SFrank Li phy_id = 0; 2201793fc096SFrank Li } 2202793fc096SFrank Li 2203407066f8SUwe Kleine-König snprintf(phy_name, sizeof(phy_name), 2204407066f8SUwe Kleine-König PHY_ID_FMT, mdio_bus_id, phy_id); 2205793fc096SFrank Li phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, 2206793fc096SFrank Li fep->phy_interface); 2207407066f8SUwe Kleine-König } 2208407066f8SUwe Kleine-König 2209793fc096SFrank Li if (IS_ERR(phy_dev)) { 221031b7720cSJoe Perches netdev_err(ndev, "could not attach to PHY\n"); 2211793fc096SFrank Li return PTR_ERR(phy_dev); 2212793fc096SFrank Li } 2213793fc096SFrank Li 2214793fc096SFrank Li /* mask with MAC supported features */ 22156b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_GBIT) { 221658056c1eSAndrew Lunn phy_set_max_speed(phy_dev, 1000); 221741124fa6SAndrew Lunn phy_remove_link_mode(phy_dev, 221841124fa6SAndrew Lunn ETHTOOL_LINK_MODE_1000baseT_Half_BIT); 2219d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 2220c306ad36SAndrew Lunn phy_support_sym_pause(phy_dev); 2221d1391930SGuenter Roeck #endif 2222793fc096SFrank Li } 2223793fc096SFrank Li else 222458056c1eSAndrew Lunn phy_set_max_speed(phy_dev, 100); 2225793fc096SFrank Li 2226793fc096SFrank Li fep->link = 0; 2227793fc096SFrank Li fep->full_duplex = 0; 2228793fc096SFrank Li 2229557d5dc8SHeiner Kallweit phy_dev->mac_managed_pm = 1; 2230557d5dc8SHeiner Kallweit 22312220943aSAndrew Lunn phy_attached_info(phy_dev); 2232793fc096SFrank Li 2233793fc096SFrank Li return 0; 2234793fc096SFrank Li } 2235793fc096SFrank Li 2236793fc096SFrank Li static int fec_enet_mii_init(struct platform_device *pdev) 2237793fc096SFrank Li { 2238793fc096SFrank Li static struct mii_bus *fec0_mii_bus; 2239793fc096SFrank Li struct net_device *ndev = platform_get_drvdata(pdev); 2240793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 22413c01eb62SAndrew Lunn bool suppress_preamble = false; 2242407066f8SUwe Kleine-König struct device_node *node; 2243e7f4dc35SAndrew Lunn int err = -ENXIO; 224463c60732SUwe Kleine-König u32 mii_speed, holdtime; 22453e782985SAndrew Lunn u32 bus_freq; 2246793fc096SFrank Li 2247793fc096SFrank Li /* 22483d125f9cSStefan Agner * The i.MX28 dual fec interfaces are not equal. 2249793fc096SFrank Li * Here are the differences: 2250793fc096SFrank Li * 2251793fc096SFrank Li * - fec0 supports MII & RMII modes while fec1 only supports RMII 2252793fc096SFrank Li * - fec0 acts as the 1588 time master while fec1 is slave 2253793fc096SFrank Li * - external phys can only be configured by fec0 2254793fc096SFrank Li * 2255793fc096SFrank Li * That is to say fec1 can not work independently. It only works 2256793fc096SFrank Li * when fec0 is working. The reason behind this design is that the 2257793fc096SFrank Li * second interface is added primarily for Switch mode. 2258793fc096SFrank Li * 2259793fc096SFrank Li * Because of the last point above, both phys are attached on fec0 2260793fc096SFrank Li * mdio interface in board design, and need to be configured by 2261793fc096SFrank Li * fec0 mii_bus. 2262793fc096SFrank Li */ 22633d125f9cSStefan Agner if ((fep->quirks & FEC_QUIRK_SINGLE_MDIO) && fep->dev_id > 0) { 2264793fc096SFrank Li /* fec1 uses fec0 mii_bus */ 2265793fc096SFrank Li if (mii_cnt && fec0_mii_bus) { 2266793fc096SFrank Li fep->mii_bus = fec0_mii_bus; 2267793fc096SFrank Li mii_cnt++; 2268793fc096SFrank Li return 0; 2269793fc096SFrank Li } 2270793fc096SFrank Li return -ENOENT; 2271793fc096SFrank Li } 2272793fc096SFrank Li 22733e782985SAndrew Lunn bus_freq = 2500000; /* 2.5MHz by default */ 22743e782985SAndrew Lunn node = of_get_child_by_name(pdev->dev.of_node, "mdio"); 22753c01eb62SAndrew Lunn if (node) { 22763e782985SAndrew Lunn of_property_read_u32(node, "clock-frequency", &bus_freq); 22773c01eb62SAndrew Lunn suppress_preamble = of_property_read_bool(node, 22783c01eb62SAndrew Lunn "suppress-preamble"); 22793c01eb62SAndrew Lunn } 22803e782985SAndrew Lunn 2281793fc096SFrank Li /* 22823e782985SAndrew Lunn * Set MII speed (= clk_get_rate() / 2 * phy_speed) 2283793fc096SFrank Li * 2284793fc096SFrank Li * The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while 2285793fc096SFrank Li * for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'. The i.MX28 2286793fc096SFrank Li * Reference Manual has an error on this, and gets fixed on i.MX6Q 2287793fc096SFrank Li * document. 2288793fc096SFrank Li */ 22893e782985SAndrew Lunn mii_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), bus_freq * 2); 22906b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_ENET_MAC) 229163c60732SUwe Kleine-König mii_speed--; 229263c60732SUwe Kleine-König if (mii_speed > 63) { 229363c60732SUwe Kleine-König dev_err(&pdev->dev, 2294981a0547SPeter Meerwald-Stadler "fec clock (%lu) too fast to get right mii speed\n", 229563c60732SUwe Kleine-König clk_get_rate(fep->clk_ipg)); 229663c60732SUwe Kleine-König err = -EINVAL; 229763c60732SUwe Kleine-König goto err_out; 229863c60732SUwe Kleine-König } 229963c60732SUwe Kleine-König 230063c60732SUwe Kleine-König /* 230163c60732SUwe Kleine-König * The i.MX28 and i.MX6 types have another filed in the MSCR (aka 230263c60732SUwe Kleine-König * MII_SPEED) register that defines the MDIO output hold time. Earlier 230363c60732SUwe Kleine-König * versions are RAZ there, so just ignore the difference and write the 230463c60732SUwe Kleine-König * register always. 230563c60732SUwe Kleine-König * The minimal hold time according to IEE802.3 (clause 22) is 10 ns. 230663c60732SUwe Kleine-König * HOLDTIME + 1 is the number of clk cycles the fec is holding the 230763c60732SUwe Kleine-König * output. 230863c60732SUwe Kleine-König * The HOLDTIME bitfield takes values between 0 and 7 (inclusive). 230963c60732SUwe Kleine-König * Given that ceil(clkrate / 5000000) <= 64, the calculation for 231063c60732SUwe Kleine-König * holdtime cannot result in a value greater than 3. 231163c60732SUwe Kleine-König */ 231263c60732SUwe Kleine-König holdtime = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 100000000) - 1; 231363c60732SUwe Kleine-König 231463c60732SUwe Kleine-König fep->phy_speed = mii_speed << 1 | holdtime << 8; 231563c60732SUwe Kleine-König 23163c01eb62SAndrew Lunn if (suppress_preamble) 23173c01eb62SAndrew Lunn fep->phy_speed |= BIT(7); 23183c01eb62SAndrew Lunn 23191e6114f5SGreg Ungerer if (fep->quirks & FEC_QUIRK_CLEAR_SETUP_MII) { 2320f166f890SAndrew Lunn /* Clear MMFR to avoid to generate MII event by writing MSCR. 2321f166f890SAndrew Lunn * MII event generation condition: 2322f166f890SAndrew Lunn * - writing MSCR: 2323f166f890SAndrew Lunn * - mmfr[31:0]_not_zero & mscr[7:0]_is_zero & 2324f166f890SAndrew Lunn * mscr_reg_data_in[7:0] != 0 2325f166f890SAndrew Lunn * - writing MMFR: 2326f166f890SAndrew Lunn * - mscr[7:0]_not_zero 2327f166f890SAndrew Lunn */ 2328f166f890SAndrew Lunn writel(0, fep->hwp + FEC_MII_DATA); 23291e6114f5SGreg Ungerer } 2330f166f890SAndrew Lunn 2331793fc096SFrank Li writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 2332793fc096SFrank Li 2333f166f890SAndrew Lunn /* Clear any pending transaction complete indication */ 2334f166f890SAndrew Lunn writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT); 2335f166f890SAndrew Lunn 2336793fc096SFrank Li fep->mii_bus = mdiobus_alloc(); 2337793fc096SFrank Li if (fep->mii_bus == NULL) { 2338793fc096SFrank Li err = -ENOMEM; 2339793fc096SFrank Li goto err_out; 2340793fc096SFrank Li } 2341793fc096SFrank Li 2342793fc096SFrank Li fep->mii_bus->name = "fec_enet_mii_bus"; 2343793fc096SFrank Li fep->mii_bus->read = fec_enet_mdio_read; 2344793fc096SFrank Li fep->mii_bus->write = fec_enet_mdio_write; 2345793fc096SFrank Li snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", 2346793fc096SFrank Li pdev->name, fep->dev_id + 1); 2347793fc096SFrank Li fep->mii_bus->priv = fep; 2348793fc096SFrank Li fep->mii_bus->parent = &pdev->dev; 2349793fc096SFrank Li 2350407066f8SUwe Kleine-König err = of_mdiobus_register(fep->mii_bus, node); 2351407066f8SUwe Kleine-König if (err) 2352e7f4dc35SAndrew Lunn goto err_out_free_mdiobus; 23530607a2cdSPan Bian of_node_put(node); 2354793fc096SFrank Li 2355793fc096SFrank Li mii_cnt++; 2356793fc096SFrank Li 2357793fc096SFrank Li /* save fec0 mii_bus */ 23583d125f9cSStefan Agner if (fep->quirks & FEC_QUIRK_SINGLE_MDIO) 2359793fc096SFrank Li fec0_mii_bus = fep->mii_bus; 2360793fc096SFrank Li 2361793fc096SFrank Li return 0; 2362793fc096SFrank Li 2363793fc096SFrank Li err_out_free_mdiobus: 2364793fc096SFrank Li mdiobus_free(fep->mii_bus); 2365793fc096SFrank Li err_out: 23660607a2cdSPan Bian of_node_put(node); 2367793fc096SFrank Li return err; 2368793fc096SFrank Li } 2369793fc096SFrank Li 2370793fc096SFrank Li static void fec_enet_mii_remove(struct fec_enet_private *fep) 2371793fc096SFrank Li { 2372793fc096SFrank Li if (--mii_cnt == 0) { 2373793fc096SFrank Li mdiobus_unregister(fep->mii_bus); 2374793fc096SFrank Li mdiobus_free(fep->mii_bus); 2375793fc096SFrank Li } 2376793fc096SFrank Li } 2377793fc096SFrank Li 2378793fc096SFrank Li static void fec_enet_get_drvinfo(struct net_device *ndev, 2379793fc096SFrank Li struct ethtool_drvinfo *info) 2380793fc096SFrank Li { 2381793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2382793fc096SFrank Li 2383f029c781SWolfram Sang strscpy(info->driver, fep->pdev->dev.driver->name, 2384793fc096SFrank Li sizeof(info->driver)); 2385f029c781SWolfram Sang strscpy(info->bus_info, dev_name(&ndev->dev), sizeof(info->bus_info)); 2386793fc096SFrank Li } 2387793fc096SFrank Li 2388db65f35fSPhilippe Reynes static int fec_enet_get_regs_len(struct net_device *ndev) 2389db65f35fSPhilippe Reynes { 2390db65f35fSPhilippe Reynes struct fec_enet_private *fep = netdev_priv(ndev); 2391db65f35fSPhilippe Reynes struct resource *r; 2392db65f35fSPhilippe Reynes int s = 0; 2393db65f35fSPhilippe Reynes 2394db65f35fSPhilippe Reynes r = platform_get_resource(fep->pdev, IORESOURCE_MEM, 0); 2395db65f35fSPhilippe Reynes if (r) 2396db65f35fSPhilippe Reynes s = resource_size(r); 2397db65f35fSPhilippe Reynes 2398db65f35fSPhilippe Reynes return s; 2399db65f35fSPhilippe Reynes } 2400db65f35fSPhilippe Reynes 2401db65f35fSPhilippe Reynes /* List of registers that can be safety be read to dump them with ethtool */ 2402db65f35fSPhilippe Reynes #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ 24033f1dcc6aSLucas Stach defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ 240478cc6e7eSFlorian Fainelli defined(CONFIG_ARM64) || defined(CONFIG_COMPILE_TEST) 2405f9bcc9f3SVivien Didelot static __u32 fec_enet_register_version = 2; 2406db65f35fSPhilippe Reynes static u32 fec_enet_register_offset[] = { 2407db65f35fSPhilippe Reynes FEC_IEVENT, FEC_IMASK, FEC_R_DES_ACTIVE_0, FEC_X_DES_ACTIVE_0, 2408db65f35fSPhilippe Reynes FEC_ECNTRL, FEC_MII_DATA, FEC_MII_SPEED, FEC_MIB_CTRLSTAT, FEC_R_CNTRL, 2409db65f35fSPhilippe Reynes FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, FEC_OPD, FEC_TXIC0, FEC_TXIC1, 2410db65f35fSPhilippe Reynes FEC_TXIC2, FEC_RXIC0, FEC_RXIC1, FEC_RXIC2, FEC_HASH_TABLE_HIGH, 2411db65f35fSPhilippe Reynes FEC_HASH_TABLE_LOW, FEC_GRP_HASH_TABLE_HIGH, FEC_GRP_HASH_TABLE_LOW, 2412db65f35fSPhilippe Reynes FEC_X_WMRK, FEC_R_BOUND, FEC_R_FSTART, FEC_R_DES_START_1, 2413db65f35fSPhilippe Reynes FEC_X_DES_START_1, FEC_R_BUFF_SIZE_1, FEC_R_DES_START_2, 2414db65f35fSPhilippe Reynes FEC_X_DES_START_2, FEC_R_BUFF_SIZE_2, FEC_R_DES_START_0, 2415db65f35fSPhilippe Reynes FEC_X_DES_START_0, FEC_R_BUFF_SIZE_0, FEC_R_FIFO_RSFL, FEC_R_FIFO_RSEM, 2416db65f35fSPhilippe Reynes FEC_R_FIFO_RAEM, FEC_R_FIFO_RAFL, FEC_RACC, FEC_RCMR_1, FEC_RCMR_2, 2417db65f35fSPhilippe Reynes FEC_DMA_CFG_1, FEC_DMA_CFG_2, FEC_R_DES_ACTIVE_1, FEC_X_DES_ACTIVE_1, 2418db65f35fSPhilippe Reynes FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_2, FEC_QOS_SCHEME, 2419db65f35fSPhilippe Reynes RMON_T_DROP, RMON_T_PACKETS, RMON_T_BC_PKT, RMON_T_MC_PKT, 2420db65f35fSPhilippe Reynes RMON_T_CRC_ALIGN, RMON_T_UNDERSIZE, RMON_T_OVERSIZE, RMON_T_FRAG, 2421db65f35fSPhilippe Reynes RMON_T_JAB, RMON_T_COL, RMON_T_P64, RMON_T_P65TO127, RMON_T_P128TO255, 2422db65f35fSPhilippe Reynes RMON_T_P256TO511, RMON_T_P512TO1023, RMON_T_P1024TO2047, 2423db65f35fSPhilippe Reynes RMON_T_P_GTE2048, RMON_T_OCTETS, 2424db65f35fSPhilippe Reynes IEEE_T_DROP, IEEE_T_FRAME_OK, IEEE_T_1COL, IEEE_T_MCOL, IEEE_T_DEF, 2425db65f35fSPhilippe Reynes IEEE_T_LCOL, IEEE_T_EXCOL, IEEE_T_MACERR, IEEE_T_CSERR, IEEE_T_SQE, 2426db65f35fSPhilippe Reynes IEEE_T_FDXFC, IEEE_T_OCTETS_OK, 2427db65f35fSPhilippe Reynes RMON_R_PACKETS, RMON_R_BC_PKT, RMON_R_MC_PKT, RMON_R_CRC_ALIGN, 2428db65f35fSPhilippe Reynes RMON_R_UNDERSIZE, RMON_R_OVERSIZE, RMON_R_FRAG, RMON_R_JAB, 2429db65f35fSPhilippe Reynes RMON_R_RESVD_O, RMON_R_P64, RMON_R_P65TO127, RMON_R_P128TO255, 2430db65f35fSPhilippe Reynes RMON_R_P256TO511, RMON_R_P512TO1023, RMON_R_P1024TO2047, 2431db65f35fSPhilippe Reynes RMON_R_P_GTE2048, RMON_R_OCTETS, 2432db65f35fSPhilippe Reynes IEEE_R_DROP, IEEE_R_FRAME_OK, IEEE_R_CRC, IEEE_R_ALIGN, IEEE_R_MACERR, 2433db65f35fSPhilippe Reynes IEEE_R_FDXFC, IEEE_R_OCTETS_OK 2434db65f35fSPhilippe Reynes }; 24350a8b43b1SJuergen Borleis /* for i.MX6ul */ 24360a8b43b1SJuergen Borleis static u32 fec_enet_register_offset_6ul[] = { 24370a8b43b1SJuergen Borleis FEC_IEVENT, FEC_IMASK, FEC_R_DES_ACTIVE_0, FEC_X_DES_ACTIVE_0, 24380a8b43b1SJuergen Borleis FEC_ECNTRL, FEC_MII_DATA, FEC_MII_SPEED, FEC_MIB_CTRLSTAT, FEC_R_CNTRL, 24390a8b43b1SJuergen Borleis FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, FEC_OPD, FEC_TXIC0, FEC_RXIC0, 24400a8b43b1SJuergen Borleis FEC_HASH_TABLE_HIGH, FEC_HASH_TABLE_LOW, FEC_GRP_HASH_TABLE_HIGH, 24410a8b43b1SJuergen Borleis FEC_GRP_HASH_TABLE_LOW, FEC_X_WMRK, FEC_R_DES_START_0, 24420a8b43b1SJuergen Borleis FEC_X_DES_START_0, FEC_R_BUFF_SIZE_0, FEC_R_FIFO_RSFL, FEC_R_FIFO_RSEM, 24430a8b43b1SJuergen Borleis FEC_R_FIFO_RAEM, FEC_R_FIFO_RAFL, FEC_RACC, 24440a8b43b1SJuergen Borleis RMON_T_DROP, RMON_T_PACKETS, RMON_T_BC_PKT, RMON_T_MC_PKT, 24450a8b43b1SJuergen Borleis RMON_T_CRC_ALIGN, RMON_T_UNDERSIZE, RMON_T_OVERSIZE, RMON_T_FRAG, 24460a8b43b1SJuergen Borleis RMON_T_JAB, RMON_T_COL, RMON_T_P64, RMON_T_P65TO127, RMON_T_P128TO255, 24470a8b43b1SJuergen Borleis RMON_T_P256TO511, RMON_T_P512TO1023, RMON_T_P1024TO2047, 24480a8b43b1SJuergen Borleis RMON_T_P_GTE2048, RMON_T_OCTETS, 24490a8b43b1SJuergen Borleis IEEE_T_DROP, IEEE_T_FRAME_OK, IEEE_T_1COL, IEEE_T_MCOL, IEEE_T_DEF, 24500a8b43b1SJuergen Borleis IEEE_T_LCOL, IEEE_T_EXCOL, IEEE_T_MACERR, IEEE_T_CSERR, IEEE_T_SQE, 24510a8b43b1SJuergen Borleis IEEE_T_FDXFC, IEEE_T_OCTETS_OK, 24520a8b43b1SJuergen Borleis RMON_R_PACKETS, RMON_R_BC_PKT, RMON_R_MC_PKT, RMON_R_CRC_ALIGN, 24530a8b43b1SJuergen Borleis RMON_R_UNDERSIZE, RMON_R_OVERSIZE, RMON_R_FRAG, RMON_R_JAB, 24540a8b43b1SJuergen Borleis RMON_R_RESVD_O, RMON_R_P64, RMON_R_P65TO127, RMON_R_P128TO255, 24550a8b43b1SJuergen Borleis RMON_R_P256TO511, RMON_R_P512TO1023, RMON_R_P1024TO2047, 24560a8b43b1SJuergen Borleis RMON_R_P_GTE2048, RMON_R_OCTETS, 24570a8b43b1SJuergen Borleis IEEE_R_DROP, IEEE_R_FRAME_OK, IEEE_R_CRC, IEEE_R_ALIGN, IEEE_R_MACERR, 24580a8b43b1SJuergen Borleis IEEE_R_FDXFC, IEEE_R_OCTETS_OK 24590a8b43b1SJuergen Borleis }; 2460db65f35fSPhilippe Reynes #else 2461f9bcc9f3SVivien Didelot static __u32 fec_enet_register_version = 1; 2462db65f35fSPhilippe Reynes static u32 fec_enet_register_offset[] = { 2463db65f35fSPhilippe Reynes FEC_ECNTRL, FEC_IEVENT, FEC_IMASK, FEC_IVEC, FEC_R_DES_ACTIVE_0, 2464db65f35fSPhilippe Reynes FEC_R_DES_ACTIVE_1, FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_0, 2465db65f35fSPhilippe Reynes FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2, FEC_MII_DATA, FEC_MII_SPEED, 2466db65f35fSPhilippe Reynes FEC_R_BOUND, FEC_R_FSTART, FEC_X_WMRK, FEC_X_FSTART, FEC_R_CNTRL, 2467db65f35fSPhilippe Reynes FEC_MAX_FRM_LEN, FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, 2468db65f35fSPhilippe Reynes FEC_GRP_HASH_TABLE_HIGH, FEC_GRP_HASH_TABLE_LOW, FEC_R_DES_START_0, 2469db65f35fSPhilippe Reynes FEC_R_DES_START_1, FEC_R_DES_START_2, FEC_X_DES_START_0, 2470db65f35fSPhilippe Reynes FEC_X_DES_START_1, FEC_X_DES_START_2, FEC_R_BUFF_SIZE_0, 2471db65f35fSPhilippe Reynes FEC_R_BUFF_SIZE_1, FEC_R_BUFF_SIZE_2 2472db65f35fSPhilippe Reynes }; 2473db65f35fSPhilippe Reynes #endif 2474db65f35fSPhilippe Reynes 2475db65f35fSPhilippe Reynes static void fec_enet_get_regs(struct net_device *ndev, 2476db65f35fSPhilippe Reynes struct ethtool_regs *regs, void *regbuf) 2477db65f35fSPhilippe Reynes { 2478db65f35fSPhilippe Reynes struct fec_enet_private *fep = netdev_priv(ndev); 2479db65f35fSPhilippe Reynes u32 __iomem *theregs = (u32 __iomem *)fep->hwp; 2480c72a0bc0SAndrew Lunn struct device *dev = &fep->pdev->dev; 2481db65f35fSPhilippe Reynes u32 *buf = (u32 *)regbuf; 2482db65f35fSPhilippe Reynes u32 i, off; 2483c72a0bc0SAndrew Lunn int ret; 24840a8b43b1SJuergen Borleis #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ 24850a8b43b1SJuergen Borleis defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ 24860a8b43b1SJuergen Borleis defined(CONFIG_ARM64) || defined(CONFIG_COMPILE_TEST) 24870a8b43b1SJuergen Borleis u32 *reg_list; 24880a8b43b1SJuergen Borleis u32 reg_cnt; 2489c72a0bc0SAndrew Lunn 24900a8b43b1SJuergen Borleis if (!of_machine_is_compatible("fsl,imx6ul")) { 24910a8b43b1SJuergen Borleis reg_list = fec_enet_register_offset; 24920a8b43b1SJuergen Borleis reg_cnt = ARRAY_SIZE(fec_enet_register_offset); 24930a8b43b1SJuergen Borleis } else { 24940a8b43b1SJuergen Borleis reg_list = fec_enet_register_offset_6ul; 24950a8b43b1SJuergen Borleis reg_cnt = ARRAY_SIZE(fec_enet_register_offset_6ul); 24960a8b43b1SJuergen Borleis } 24970a8b43b1SJuergen Borleis #else 24980a8b43b1SJuergen Borleis /* coldfire */ 24990a8b43b1SJuergen Borleis static u32 *reg_list = fec_enet_register_offset; 25000a8b43b1SJuergen Borleis static const u32 reg_cnt = ARRAY_SIZE(fec_enet_register_offset); 25010a8b43b1SJuergen Borleis #endif 2502da875fa5SZhang Qilong ret = pm_runtime_resume_and_get(dev); 2503c72a0bc0SAndrew Lunn if (ret < 0) 2504c72a0bc0SAndrew Lunn return; 2505db65f35fSPhilippe Reynes 2506f9bcc9f3SVivien Didelot regs->version = fec_enet_register_version; 2507f9bcc9f3SVivien Didelot 2508db65f35fSPhilippe Reynes memset(buf, 0, regs->len); 2509db65f35fSPhilippe Reynes 25100a8b43b1SJuergen Borleis for (i = 0; i < reg_cnt; i++) { 25110a8b43b1SJuergen Borleis off = reg_list[i]; 2512ec20a63aSFugang Duan 2513ec20a63aSFugang Duan if ((off == FEC_R_BOUND || off == FEC_R_FSTART) && 2514ec20a63aSFugang Duan !(fep->quirks & FEC_QUIRK_HAS_FRREG)) 2515ec20a63aSFugang Duan continue; 2516ec20a63aSFugang Duan 2517ec20a63aSFugang Duan off >>= 2; 2518db65f35fSPhilippe Reynes buf[off] = readl(&theregs[off]); 2519db65f35fSPhilippe Reynes } 2520c72a0bc0SAndrew Lunn 2521c72a0bc0SAndrew Lunn pm_runtime_mark_last_busy(dev); 2522c72a0bc0SAndrew Lunn pm_runtime_put_autosuspend(dev); 2523db65f35fSPhilippe Reynes } 2524db65f35fSPhilippe Reynes 2525793fc096SFrank Li static int fec_enet_get_ts_info(struct net_device *ndev, 2526793fc096SFrank Li struct ethtool_ts_info *info) 2527793fc096SFrank Li { 2528793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2529793fc096SFrank Li 2530793fc096SFrank Li if (fep->bufdesc_ex) { 2531793fc096SFrank Li 2532793fc096SFrank Li info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | 2533793fc096SFrank Li SOF_TIMESTAMPING_RX_SOFTWARE | 2534793fc096SFrank Li SOF_TIMESTAMPING_SOFTWARE | 2535793fc096SFrank Li SOF_TIMESTAMPING_TX_HARDWARE | 2536793fc096SFrank Li SOF_TIMESTAMPING_RX_HARDWARE | 2537793fc096SFrank Li SOF_TIMESTAMPING_RAW_HARDWARE; 2538793fc096SFrank Li if (fep->ptp_clock) 2539793fc096SFrank Li info->phc_index = ptp_clock_index(fep->ptp_clock); 2540793fc096SFrank Li else 2541793fc096SFrank Li info->phc_index = -1; 2542793fc096SFrank Li 2543793fc096SFrank Li info->tx_types = (1 << HWTSTAMP_TX_OFF) | 2544793fc096SFrank Li (1 << HWTSTAMP_TX_ON); 2545793fc096SFrank Li 2546793fc096SFrank Li info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | 2547793fc096SFrank Li (1 << HWTSTAMP_FILTER_ALL); 2548793fc096SFrank Li return 0; 2549793fc096SFrank Li } else { 2550793fc096SFrank Li return ethtool_op_get_ts_info(ndev, info); 2551793fc096SFrank Li } 2552793fc096SFrank Li } 2553793fc096SFrank Li 2554d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 2555d1391930SGuenter Roeck 2556793fc096SFrank Li static void fec_enet_get_pauseparam(struct net_device *ndev, 2557793fc096SFrank Li struct ethtool_pauseparam *pause) 2558793fc096SFrank Li { 2559793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2560793fc096SFrank Li 2561793fc096SFrank Li pause->autoneg = (fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) != 0; 2562793fc096SFrank Li pause->tx_pause = (fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) != 0; 2563793fc096SFrank Li pause->rx_pause = pause->tx_pause; 2564793fc096SFrank Li } 2565793fc096SFrank Li 2566793fc096SFrank Li static int fec_enet_set_pauseparam(struct net_device *ndev, 2567793fc096SFrank Li struct ethtool_pauseparam *pause) 2568793fc096SFrank Li { 2569793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2570793fc096SFrank Li 257145f5c327SPhilippe Reynes if (!ndev->phydev) 25720b146ca8SRussell King return -ENODEV; 25730b146ca8SRussell King 2574793fc096SFrank Li if (pause->tx_pause != pause->rx_pause) { 2575793fc096SFrank Li netdev_info(ndev, 2576793fc096SFrank Li "hardware only support enable/disable both tx and rx"); 2577793fc096SFrank Li return -EINVAL; 2578793fc096SFrank Li } 2579793fc096SFrank Li 2580793fc096SFrank Li fep->pause_flag = 0; 2581793fc096SFrank Li 2582793fc096SFrank Li /* tx pause must be same as rx pause */ 2583793fc096SFrank Li fep->pause_flag |= pause->rx_pause ? FEC_PAUSE_FLAG_ENABLE : 0; 2584793fc096SFrank Li fep->pause_flag |= pause->autoneg ? FEC_PAUSE_FLAG_AUTONEG : 0; 2585793fc096SFrank Li 25860c122405SAndrew Lunn phy_set_sym_pause(ndev->phydev, pause->rx_pause, pause->tx_pause, 25870c122405SAndrew Lunn pause->autoneg); 2588793fc096SFrank Li 2589793fc096SFrank Li if (pause->autoneg) { 2590793fc096SFrank Li if (netif_running(ndev)) 2591793fc096SFrank Li fec_stop(ndev); 259245f5c327SPhilippe Reynes phy_start_aneg(ndev->phydev); 2593793fc096SFrank Li } 2594dbc64a8eSRussell King if (netif_running(ndev)) { 2595dbc64a8eSRussell King napi_disable(&fep->napi); 2596dbc64a8eSRussell King netif_tx_lock_bh(ndev); 2597ef83337dSRussell King fec_restart(ndev); 2598657ade07SRickard x Andersson netif_tx_wake_all_queues(ndev); 25996af42d42SRussell King netif_tx_unlock_bh(ndev); 2600dbc64a8eSRussell King napi_enable(&fep->napi); 2601dbc64a8eSRussell King } 2602793fc096SFrank Li 2603793fc096SFrank Li return 0; 2604793fc096SFrank Li } 2605793fc096SFrank Li 260638ae92dcSChris Healy static const struct fec_stat { 260738ae92dcSChris Healy char name[ETH_GSTRING_LEN]; 260838ae92dcSChris Healy u16 offset; 260938ae92dcSChris Healy } fec_stats[] = { 261038ae92dcSChris Healy /* RMON TX */ 261138ae92dcSChris Healy { "tx_dropped", RMON_T_DROP }, 261238ae92dcSChris Healy { "tx_packets", RMON_T_PACKETS }, 261338ae92dcSChris Healy { "tx_broadcast", RMON_T_BC_PKT }, 261438ae92dcSChris Healy { "tx_multicast", RMON_T_MC_PKT }, 261538ae92dcSChris Healy { "tx_crc_errors", RMON_T_CRC_ALIGN }, 261638ae92dcSChris Healy { "tx_undersize", RMON_T_UNDERSIZE }, 261738ae92dcSChris Healy { "tx_oversize", RMON_T_OVERSIZE }, 261838ae92dcSChris Healy { "tx_fragment", RMON_T_FRAG }, 261938ae92dcSChris Healy { "tx_jabber", RMON_T_JAB }, 262038ae92dcSChris Healy { "tx_collision", RMON_T_COL }, 262138ae92dcSChris Healy { "tx_64byte", RMON_T_P64 }, 262238ae92dcSChris Healy { "tx_65to127byte", RMON_T_P65TO127 }, 262338ae92dcSChris Healy { "tx_128to255byte", RMON_T_P128TO255 }, 262438ae92dcSChris Healy { "tx_256to511byte", RMON_T_P256TO511 }, 262538ae92dcSChris Healy { "tx_512to1023byte", RMON_T_P512TO1023 }, 262638ae92dcSChris Healy { "tx_1024to2047byte", RMON_T_P1024TO2047 }, 262738ae92dcSChris Healy { "tx_GTE2048byte", RMON_T_P_GTE2048 }, 262838ae92dcSChris Healy { "tx_octets", RMON_T_OCTETS }, 262938ae92dcSChris Healy 263038ae92dcSChris Healy /* IEEE TX */ 263138ae92dcSChris Healy { "IEEE_tx_drop", IEEE_T_DROP }, 263238ae92dcSChris Healy { "IEEE_tx_frame_ok", IEEE_T_FRAME_OK }, 263338ae92dcSChris Healy { "IEEE_tx_1col", IEEE_T_1COL }, 263438ae92dcSChris Healy { "IEEE_tx_mcol", IEEE_T_MCOL }, 263538ae92dcSChris Healy { "IEEE_tx_def", IEEE_T_DEF }, 263638ae92dcSChris Healy { "IEEE_tx_lcol", IEEE_T_LCOL }, 263738ae92dcSChris Healy { "IEEE_tx_excol", IEEE_T_EXCOL }, 263838ae92dcSChris Healy { "IEEE_tx_macerr", IEEE_T_MACERR }, 263938ae92dcSChris Healy { "IEEE_tx_cserr", IEEE_T_CSERR }, 264038ae92dcSChris Healy { "IEEE_tx_sqe", IEEE_T_SQE }, 264138ae92dcSChris Healy { "IEEE_tx_fdxfc", IEEE_T_FDXFC }, 264238ae92dcSChris Healy { "IEEE_tx_octets_ok", IEEE_T_OCTETS_OK }, 264338ae92dcSChris Healy 264438ae92dcSChris Healy /* RMON RX */ 264538ae92dcSChris Healy { "rx_packets", RMON_R_PACKETS }, 264638ae92dcSChris Healy { "rx_broadcast", RMON_R_BC_PKT }, 264738ae92dcSChris Healy { "rx_multicast", RMON_R_MC_PKT }, 264838ae92dcSChris Healy { "rx_crc_errors", RMON_R_CRC_ALIGN }, 264938ae92dcSChris Healy { "rx_undersize", RMON_R_UNDERSIZE }, 265038ae92dcSChris Healy { "rx_oversize", RMON_R_OVERSIZE }, 265138ae92dcSChris Healy { "rx_fragment", RMON_R_FRAG }, 265238ae92dcSChris Healy { "rx_jabber", RMON_R_JAB }, 265338ae92dcSChris Healy { "rx_64byte", RMON_R_P64 }, 265438ae92dcSChris Healy { "rx_65to127byte", RMON_R_P65TO127 }, 265538ae92dcSChris Healy { "rx_128to255byte", RMON_R_P128TO255 }, 265638ae92dcSChris Healy { "rx_256to511byte", RMON_R_P256TO511 }, 265738ae92dcSChris Healy { "rx_512to1023byte", RMON_R_P512TO1023 }, 265838ae92dcSChris Healy { "rx_1024to2047byte", RMON_R_P1024TO2047 }, 265938ae92dcSChris Healy { "rx_GTE2048byte", RMON_R_P_GTE2048 }, 266038ae92dcSChris Healy { "rx_octets", RMON_R_OCTETS }, 266138ae92dcSChris Healy 266238ae92dcSChris Healy /* IEEE RX */ 266338ae92dcSChris Healy { "IEEE_rx_drop", IEEE_R_DROP }, 266438ae92dcSChris Healy { "IEEE_rx_frame_ok", IEEE_R_FRAME_OK }, 266538ae92dcSChris Healy { "IEEE_rx_crc", IEEE_R_CRC }, 266638ae92dcSChris Healy { "IEEE_rx_align", IEEE_R_ALIGN }, 266738ae92dcSChris Healy { "IEEE_rx_macerr", IEEE_R_MACERR }, 266838ae92dcSChris Healy { "IEEE_rx_fdxfc", IEEE_R_FDXFC }, 266938ae92dcSChris Healy { "IEEE_rx_octets_ok", IEEE_R_OCTETS_OK }, 267038ae92dcSChris Healy }; 267138ae92dcSChris Healy 2672f85de666SNikita Yushchenko #define FEC_STATS_SIZE (ARRAY_SIZE(fec_stats) * sizeof(u64)) 2673f85de666SNikita Yushchenko 267480cca775SNikita Yushchenko static void fec_enet_update_ethtool_stats(struct net_device *dev) 267538ae92dcSChris Healy { 267638ae92dcSChris Healy struct fec_enet_private *fep = netdev_priv(dev); 267738ae92dcSChris Healy int i; 267838ae92dcSChris Healy 267938ae92dcSChris Healy for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 268080cca775SNikita Yushchenko fep->ethtool_stats[i] = readl(fep->hwp + fec_stats[i].offset); 268180cca775SNikita Yushchenko } 268280cca775SNikita Yushchenko 268380cca775SNikita Yushchenko static void fec_enet_get_ethtool_stats(struct net_device *dev, 268480cca775SNikita Yushchenko struct ethtool_stats *stats, u64 *data) 268580cca775SNikita Yushchenko { 268680cca775SNikita Yushchenko struct fec_enet_private *fep = netdev_priv(dev); 268780cca775SNikita Yushchenko 268880cca775SNikita Yushchenko if (netif_running(dev)) 268980cca775SNikita Yushchenko fec_enet_update_ethtool_stats(dev); 269080cca775SNikita Yushchenko 2691f85de666SNikita Yushchenko memcpy(data, fep->ethtool_stats, FEC_STATS_SIZE); 269238ae92dcSChris Healy } 269338ae92dcSChris Healy 269438ae92dcSChris Healy static void fec_enet_get_strings(struct net_device *netdev, 269538ae92dcSChris Healy u32 stringset, u8 *data) 269638ae92dcSChris Healy { 269738ae92dcSChris Healy int i; 269838ae92dcSChris Healy switch (stringset) { 269938ae92dcSChris Healy case ETH_SS_STATS: 270038ae92dcSChris Healy for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 270138ae92dcSChris Healy memcpy(data + i * ETH_GSTRING_LEN, 270238ae92dcSChris Healy fec_stats[i].name, ETH_GSTRING_LEN); 270338ae92dcSChris Healy break; 27046016ba34SOleksij Rempel case ETH_SS_TEST: 27056016ba34SOleksij Rempel net_selftest_get_strings(data); 27066016ba34SOleksij Rempel break; 270738ae92dcSChris Healy } 270838ae92dcSChris Healy } 270938ae92dcSChris Healy 271038ae92dcSChris Healy static int fec_enet_get_sset_count(struct net_device *dev, int sset) 271138ae92dcSChris Healy { 271238ae92dcSChris Healy switch (sset) { 271338ae92dcSChris Healy case ETH_SS_STATS: 271438ae92dcSChris Healy return ARRAY_SIZE(fec_stats); 27156016ba34SOleksij Rempel case ETH_SS_TEST: 27166016ba34SOleksij Rempel return net_selftest_get_count(); 271738ae92dcSChris Healy default: 271838ae92dcSChris Healy return -EOPNOTSUPP; 271938ae92dcSChris Healy } 272038ae92dcSChris Healy } 2721f85de666SNikita Yushchenko 27222b30842bSAndrew Lunn static void fec_enet_clear_ethtool_stats(struct net_device *dev) 27232b30842bSAndrew Lunn { 27242b30842bSAndrew Lunn struct fec_enet_private *fep = netdev_priv(dev); 27252b30842bSAndrew Lunn int i; 27262b30842bSAndrew Lunn 27272b30842bSAndrew Lunn /* Disable MIB statistics counters */ 27282b30842bSAndrew Lunn writel(FEC_MIB_CTRLSTAT_DISABLE, fep->hwp + FEC_MIB_CTRLSTAT); 27292b30842bSAndrew Lunn 27302b30842bSAndrew Lunn for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 27312b30842bSAndrew Lunn writel(0, fep->hwp + fec_stats[i].offset); 27322b30842bSAndrew Lunn 27332b30842bSAndrew Lunn /* Don't disable MIB statistics counters */ 27342b30842bSAndrew Lunn writel(0, fep->hwp + FEC_MIB_CTRLSTAT); 27352b30842bSAndrew Lunn } 27362b30842bSAndrew Lunn 2737f85de666SNikita Yushchenko #else /* !defined(CONFIG_M5272) */ 2738f85de666SNikita Yushchenko #define FEC_STATS_SIZE 0 2739f85de666SNikita Yushchenko static inline void fec_enet_update_ethtool_stats(struct net_device *dev) 2740f85de666SNikita Yushchenko { 2741f85de666SNikita Yushchenko } 274241e8e404SFabio Estevam 274341e8e404SFabio Estevam static inline void fec_enet_clear_ethtool_stats(struct net_device *dev) 274441e8e404SFabio Estevam { 274541e8e404SFabio Estevam } 2746d1391930SGuenter Roeck #endif /* !defined(CONFIG_M5272) */ 274738ae92dcSChris Healy 2748d851b47bSFugang Duan /* ITR clock source is enet system clock (clk_ahb). 2749d851b47bSFugang Duan * TCTT unit is cycle_ns * 64 cycle 2750d851b47bSFugang Duan * So, the ICTT value = X us / (cycle_ns * 64) 2751d851b47bSFugang Duan */ 2752d851b47bSFugang Duan static int fec_enet_us_to_itr_clock(struct net_device *ndev, int us) 2753d851b47bSFugang Duan { 2754d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2755d851b47bSFugang Duan 2756d851b47bSFugang Duan return us * (fep->itr_clk_rate / 64000) / 1000; 2757d851b47bSFugang Duan } 2758d851b47bSFugang Duan 2759d851b47bSFugang Duan /* Set threshold for interrupt coalescing */ 2760d851b47bSFugang Duan static void fec_enet_itr_coal_set(struct net_device *ndev) 2761d851b47bSFugang Duan { 2762d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2763d851b47bSFugang Duan int rx_itr, tx_itr; 2764d851b47bSFugang Duan 2765d851b47bSFugang Duan /* Must be greater than zero to avoid unpredictable behavior */ 2766d851b47bSFugang Duan if (!fep->rx_time_itr || !fep->rx_pkts_itr || 2767d851b47bSFugang Duan !fep->tx_time_itr || !fep->tx_pkts_itr) 2768d851b47bSFugang Duan return; 2769d851b47bSFugang Duan 2770d851b47bSFugang Duan /* Select enet system clock as Interrupt Coalescing 2771d851b47bSFugang Duan * timer Clock Source 2772d851b47bSFugang Duan */ 2773d851b47bSFugang Duan rx_itr = FEC_ITR_CLK_SEL; 2774d851b47bSFugang Duan tx_itr = FEC_ITR_CLK_SEL; 2775d851b47bSFugang Duan 2776d851b47bSFugang Duan /* set ICFT and ICTT */ 2777d851b47bSFugang Duan rx_itr |= FEC_ITR_ICFT(fep->rx_pkts_itr); 2778d851b47bSFugang Duan rx_itr |= FEC_ITR_ICTT(fec_enet_us_to_itr_clock(ndev, fep->rx_time_itr)); 2779d851b47bSFugang Duan tx_itr |= FEC_ITR_ICFT(fep->tx_pkts_itr); 2780d851b47bSFugang Duan tx_itr |= FEC_ITR_ICTT(fec_enet_us_to_itr_clock(ndev, fep->tx_time_itr)); 2781d851b47bSFugang Duan 2782d851b47bSFugang Duan rx_itr |= FEC_ITR_EN; 2783d851b47bSFugang Duan tx_itr |= FEC_ITR_EN; 2784d851b47bSFugang Duan 2785d851b47bSFugang Duan writel(tx_itr, fep->hwp + FEC_TXIC0); 2786d851b47bSFugang Duan writel(rx_itr, fep->hwp + FEC_RXIC0); 2787471ff445SJoakim Zhang if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) { 2788d851b47bSFugang Duan writel(tx_itr, fep->hwp + FEC_TXIC1); 2789d851b47bSFugang Duan writel(rx_itr, fep->hwp + FEC_RXIC1); 2790d851b47bSFugang Duan writel(tx_itr, fep->hwp + FEC_TXIC2); 2791d851b47bSFugang Duan writel(rx_itr, fep->hwp + FEC_RXIC2); 2792d851b47bSFugang Duan } 2793ff7566b8SFugang Duan } 2794d851b47bSFugang Duan 2795f3ccfda1SYufeng Mo static int fec_enet_get_coalesce(struct net_device *ndev, 2796f3ccfda1SYufeng Mo struct ethtool_coalesce *ec, 2797f3ccfda1SYufeng Mo struct kernel_ethtool_coalesce *kernel_coal, 2798f3ccfda1SYufeng Mo struct netlink_ext_ack *extack) 2799d851b47bSFugang Duan { 2800d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2801d851b47bSFugang Duan 2802ff7566b8SFugang Duan if (!(fep->quirks & FEC_QUIRK_HAS_COALESCE)) 2803d851b47bSFugang Duan return -EOPNOTSUPP; 2804d851b47bSFugang Duan 2805d851b47bSFugang Duan ec->rx_coalesce_usecs = fep->rx_time_itr; 2806d851b47bSFugang Duan ec->rx_max_coalesced_frames = fep->rx_pkts_itr; 2807d851b47bSFugang Duan 2808d851b47bSFugang Duan ec->tx_coalesce_usecs = fep->tx_time_itr; 2809d851b47bSFugang Duan ec->tx_max_coalesced_frames = fep->tx_pkts_itr; 2810d851b47bSFugang Duan 2811d851b47bSFugang Duan return 0; 2812d851b47bSFugang Duan } 2813d851b47bSFugang Duan 2814f3ccfda1SYufeng Mo static int fec_enet_set_coalesce(struct net_device *ndev, 2815f3ccfda1SYufeng Mo struct ethtool_coalesce *ec, 2816f3ccfda1SYufeng Mo struct kernel_ethtool_coalesce *kernel_coal, 2817f3ccfda1SYufeng Mo struct netlink_ext_ack *extack) 2818d851b47bSFugang Duan { 2819d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2820517a772cSFabio Estevam struct device *dev = &fep->pdev->dev; 2821d851b47bSFugang Duan unsigned int cycle; 2822d851b47bSFugang Duan 2823ff7566b8SFugang Duan if (!(fep->quirks & FEC_QUIRK_HAS_COALESCE)) 2824d851b47bSFugang Duan return -EOPNOTSUPP; 2825d851b47bSFugang Duan 2826d851b47bSFugang Duan if (ec->rx_max_coalesced_frames > 255) { 2827517a772cSFabio Estevam dev_err(dev, "Rx coalesced frames exceed hardware limitation\n"); 2828d851b47bSFugang Duan return -EINVAL; 2829d851b47bSFugang Duan } 2830d851b47bSFugang Duan 2831d851b47bSFugang Duan if (ec->tx_max_coalesced_frames > 255) { 2832517a772cSFabio Estevam dev_err(dev, "Tx coalesced frame exceed hardware limitation\n"); 2833d851b47bSFugang Duan return -EINVAL; 2834d851b47bSFugang Duan } 2835d851b47bSFugang Duan 2836ab14961dSJakub Kicinski cycle = fec_enet_us_to_itr_clock(ndev, ec->rx_coalesce_usecs); 2837d851b47bSFugang Duan if (cycle > 0xFFFF) { 2838517a772cSFabio Estevam dev_err(dev, "Rx coalesced usec exceed hardware limitation\n"); 2839d851b47bSFugang Duan return -EINVAL; 2840d851b47bSFugang Duan } 2841d851b47bSFugang Duan 2842ab14961dSJakub Kicinski cycle = fec_enet_us_to_itr_clock(ndev, ec->tx_coalesce_usecs); 2843d851b47bSFugang Duan if (cycle > 0xFFFF) { 2844ab14961dSJakub Kicinski dev_err(dev, "Tx coalesced usec exceed hardware limitation\n"); 2845d851b47bSFugang Duan return -EINVAL; 2846d851b47bSFugang Duan } 2847d851b47bSFugang Duan 2848d851b47bSFugang Duan fep->rx_time_itr = ec->rx_coalesce_usecs; 2849d851b47bSFugang Duan fep->rx_pkts_itr = ec->rx_max_coalesced_frames; 2850d851b47bSFugang Duan 2851d851b47bSFugang Duan fep->tx_time_itr = ec->tx_coalesce_usecs; 2852d851b47bSFugang Duan fep->tx_pkts_itr = ec->tx_max_coalesced_frames; 2853d851b47bSFugang Duan 2854d851b47bSFugang Duan fec_enet_itr_coal_set(ndev); 2855d851b47bSFugang Duan 2856d851b47bSFugang Duan return 0; 2857d851b47bSFugang Duan } 2858d851b47bSFugang Duan 28591b7bde6dSNimrod Andy static int fec_enet_get_tunable(struct net_device *netdev, 28601b7bde6dSNimrod Andy const struct ethtool_tunable *tuna, 28611b7bde6dSNimrod Andy void *data) 28621b7bde6dSNimrod Andy { 28631b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(netdev); 28641b7bde6dSNimrod Andy int ret = 0; 28651b7bde6dSNimrod Andy 28661b7bde6dSNimrod Andy switch (tuna->id) { 28671b7bde6dSNimrod Andy case ETHTOOL_RX_COPYBREAK: 28681b7bde6dSNimrod Andy *(u32 *)data = fep->rx_copybreak; 28691b7bde6dSNimrod Andy break; 28701b7bde6dSNimrod Andy default: 28711b7bde6dSNimrod Andy ret = -EINVAL; 28721b7bde6dSNimrod Andy break; 28731b7bde6dSNimrod Andy } 28741b7bde6dSNimrod Andy 28751b7bde6dSNimrod Andy return ret; 28761b7bde6dSNimrod Andy } 28771b7bde6dSNimrod Andy 28781b7bde6dSNimrod Andy static int fec_enet_set_tunable(struct net_device *netdev, 28791b7bde6dSNimrod Andy const struct ethtool_tunable *tuna, 28801b7bde6dSNimrod Andy const void *data) 28811b7bde6dSNimrod Andy { 28821b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(netdev); 28831b7bde6dSNimrod Andy int ret = 0; 28841b7bde6dSNimrod Andy 28851b7bde6dSNimrod Andy switch (tuna->id) { 28861b7bde6dSNimrod Andy case ETHTOOL_RX_COPYBREAK: 28871b7bde6dSNimrod Andy fep->rx_copybreak = *(u32 *)data; 28881b7bde6dSNimrod Andy break; 28891b7bde6dSNimrod Andy default: 28901b7bde6dSNimrod Andy ret = -EINVAL; 28911b7bde6dSNimrod Andy break; 28921b7bde6dSNimrod Andy } 28931b7bde6dSNimrod Andy 28941b7bde6dSNimrod Andy return ret; 28951b7bde6dSNimrod Andy } 28961b7bde6dSNimrod Andy 2897b82f8c3fSFugang Duan /* LPI Sleep Ts count base on tx clk (clk_ref). 2898b82f8c3fSFugang Duan * The lpi sleep cnt value = X us / (cycle_ns). 2899b82f8c3fSFugang Duan */ 2900b82f8c3fSFugang Duan static int fec_enet_us_to_tx_cycle(struct net_device *ndev, int us) 2901b82f8c3fSFugang Duan { 2902b82f8c3fSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2903b82f8c3fSFugang Duan 2904b82f8c3fSFugang Duan return us * (fep->clk_ref_rate / 1000) / 1000; 2905b82f8c3fSFugang Duan } 2906b82f8c3fSFugang Duan 2907b82f8c3fSFugang Duan static int fec_enet_eee_mode_set(struct net_device *ndev, bool enable) 2908b82f8c3fSFugang Duan { 2909b82f8c3fSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2910b82f8c3fSFugang Duan struct ethtool_eee *p = &fep->eee; 2911b82f8c3fSFugang Duan unsigned int sleep_cycle, wake_cycle; 2912b82f8c3fSFugang Duan int ret = 0; 2913b82f8c3fSFugang Duan 2914b82f8c3fSFugang Duan if (enable) { 291553243d41SJisheng Zhang ret = phy_init_eee(ndev->phydev, false); 2916b82f8c3fSFugang Duan if (ret) 2917b82f8c3fSFugang Duan return ret; 2918b82f8c3fSFugang Duan 2919b82f8c3fSFugang Duan sleep_cycle = fec_enet_us_to_tx_cycle(ndev, p->tx_lpi_timer); 2920b82f8c3fSFugang Duan wake_cycle = sleep_cycle; 2921b82f8c3fSFugang Duan } else { 2922b82f8c3fSFugang Duan sleep_cycle = 0; 2923b82f8c3fSFugang Duan wake_cycle = 0; 2924b82f8c3fSFugang Duan } 2925b82f8c3fSFugang Duan 2926b82f8c3fSFugang Duan p->tx_lpi_enabled = enable; 2927b82f8c3fSFugang Duan p->eee_enabled = enable; 2928b82f8c3fSFugang Duan p->eee_active = enable; 2929b82f8c3fSFugang Duan 2930b82f8c3fSFugang Duan writel(sleep_cycle, fep->hwp + FEC_LPI_SLEEP); 2931b82f8c3fSFugang Duan writel(wake_cycle, fep->hwp + FEC_LPI_WAKE); 2932b82f8c3fSFugang Duan 2933b82f8c3fSFugang Duan return 0; 2934b82f8c3fSFugang Duan } 2935b82f8c3fSFugang Duan 2936b82f8c3fSFugang Duan static int 2937b82f8c3fSFugang Duan fec_enet_get_eee(struct net_device *ndev, struct ethtool_eee *edata) 2938b82f8c3fSFugang Duan { 2939b82f8c3fSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2940b82f8c3fSFugang Duan struct ethtool_eee *p = &fep->eee; 2941b82f8c3fSFugang Duan 2942b82f8c3fSFugang Duan if (!(fep->quirks & FEC_QUIRK_HAS_EEE)) 2943b82f8c3fSFugang Duan return -EOPNOTSUPP; 2944b82f8c3fSFugang Duan 2945b82f8c3fSFugang Duan if (!netif_running(ndev)) 2946b82f8c3fSFugang Duan return -ENETDOWN; 2947b82f8c3fSFugang Duan 2948b82f8c3fSFugang Duan edata->eee_enabled = p->eee_enabled; 2949b82f8c3fSFugang Duan edata->eee_active = p->eee_active; 2950b82f8c3fSFugang Duan edata->tx_lpi_timer = p->tx_lpi_timer; 2951b82f8c3fSFugang Duan edata->tx_lpi_enabled = p->tx_lpi_enabled; 2952b82f8c3fSFugang Duan 2953b82f8c3fSFugang Duan return phy_ethtool_get_eee(ndev->phydev, edata); 2954b82f8c3fSFugang Duan } 2955b82f8c3fSFugang Duan 2956b82f8c3fSFugang Duan static int 2957b82f8c3fSFugang Duan fec_enet_set_eee(struct net_device *ndev, struct ethtool_eee *edata) 2958b82f8c3fSFugang Duan { 2959b82f8c3fSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2960b82f8c3fSFugang Duan struct ethtool_eee *p = &fep->eee; 2961b82f8c3fSFugang Duan int ret = 0; 2962b82f8c3fSFugang Duan 2963b82f8c3fSFugang Duan if (!(fep->quirks & FEC_QUIRK_HAS_EEE)) 2964b82f8c3fSFugang Duan return -EOPNOTSUPP; 2965b82f8c3fSFugang Duan 2966b82f8c3fSFugang Duan if (!netif_running(ndev)) 2967b82f8c3fSFugang Duan return -ENETDOWN; 2968b82f8c3fSFugang Duan 2969b82f8c3fSFugang Duan p->tx_lpi_timer = edata->tx_lpi_timer; 2970b82f8c3fSFugang Duan 2971b82f8c3fSFugang Duan if (!edata->eee_enabled || !edata->tx_lpi_enabled || 2972b82f8c3fSFugang Duan !edata->tx_lpi_timer) 2973b82f8c3fSFugang Duan ret = fec_enet_eee_mode_set(ndev, false); 2974b82f8c3fSFugang Duan else 2975b82f8c3fSFugang Duan ret = fec_enet_eee_mode_set(ndev, true); 2976b82f8c3fSFugang Duan 2977b82f8c3fSFugang Duan if (ret) 2978b82f8c3fSFugang Duan return ret; 2979b82f8c3fSFugang Duan 2980b82f8c3fSFugang Duan return phy_ethtool_set_eee(ndev->phydev, edata); 2981b82f8c3fSFugang Duan } 2982b82f8c3fSFugang Duan 2983de40ed31SNimrod Andy static void 2984de40ed31SNimrod Andy fec_enet_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) 2985de40ed31SNimrod Andy { 2986de40ed31SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 2987de40ed31SNimrod Andy 2988de40ed31SNimrod Andy if (fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET) { 2989de40ed31SNimrod Andy wol->supported = WAKE_MAGIC; 2990de40ed31SNimrod Andy wol->wolopts = fep->wol_flag & FEC_WOL_FLAG_ENABLE ? WAKE_MAGIC : 0; 2991de40ed31SNimrod Andy } else { 2992de40ed31SNimrod Andy wol->supported = wol->wolopts = 0; 2993de40ed31SNimrod Andy } 2994de40ed31SNimrod Andy } 2995de40ed31SNimrod Andy 2996de40ed31SNimrod Andy static int 2997de40ed31SNimrod Andy fec_enet_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) 2998de40ed31SNimrod Andy { 2999de40ed31SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 3000de40ed31SNimrod Andy 3001de40ed31SNimrod Andy if (!(fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET)) 3002de40ed31SNimrod Andy return -EINVAL; 3003de40ed31SNimrod Andy 3004de40ed31SNimrod Andy if (wol->wolopts & ~WAKE_MAGIC) 3005de40ed31SNimrod Andy return -EINVAL; 3006de40ed31SNimrod Andy 3007de40ed31SNimrod Andy device_set_wakeup_enable(&ndev->dev, wol->wolopts & WAKE_MAGIC); 30080b6f65c7SJoakim Zhang if (device_may_wakeup(&ndev->dev)) 3009de40ed31SNimrod Andy fep->wol_flag |= FEC_WOL_FLAG_ENABLE; 30100b6f65c7SJoakim Zhang else 3011de40ed31SNimrod Andy fep->wol_flag &= (~FEC_WOL_FLAG_ENABLE); 3012de40ed31SNimrod Andy 3013de40ed31SNimrod Andy return 0; 3014de40ed31SNimrod Andy } 3015de40ed31SNimrod Andy 3016793fc096SFrank Li static const struct ethtool_ops fec_enet_ethtool_ops = { 3017d5e3c87dSJakub Kicinski .supported_coalesce_params = ETHTOOL_COALESCE_USECS | 3018d5e3c87dSJakub Kicinski ETHTOOL_COALESCE_MAX_FRAMES, 3019793fc096SFrank Li .get_drvinfo = fec_enet_get_drvinfo, 3020db65f35fSPhilippe Reynes .get_regs_len = fec_enet_get_regs_len, 3021db65f35fSPhilippe Reynes .get_regs = fec_enet_get_regs, 302211d59289SFlorian Fainelli .nway_reset = phy_ethtool_nway_reset, 3023c1d7c48fSRussell King .get_link = ethtool_op_get_link, 3024d851b47bSFugang Duan .get_coalesce = fec_enet_get_coalesce, 3025d851b47bSFugang Duan .set_coalesce = fec_enet_set_coalesce, 302638ae92dcSChris Healy #ifndef CONFIG_M5272 3027c1d7c48fSRussell King .get_pauseparam = fec_enet_get_pauseparam, 3028c1d7c48fSRussell King .set_pauseparam = fec_enet_set_pauseparam, 302938ae92dcSChris Healy .get_strings = fec_enet_get_strings, 3030c1d7c48fSRussell King .get_ethtool_stats = fec_enet_get_ethtool_stats, 303138ae92dcSChris Healy .get_sset_count = fec_enet_get_sset_count, 303238ae92dcSChris Healy #endif 3033c1d7c48fSRussell King .get_ts_info = fec_enet_get_ts_info, 30341b7bde6dSNimrod Andy .get_tunable = fec_enet_get_tunable, 30351b7bde6dSNimrod Andy .set_tunable = fec_enet_set_tunable, 3036de40ed31SNimrod Andy .get_wol = fec_enet_get_wol, 3037de40ed31SNimrod Andy .set_wol = fec_enet_set_wol, 3038b82f8c3fSFugang Duan .get_eee = fec_enet_get_eee, 3039b82f8c3fSFugang Duan .set_eee = fec_enet_set_eee, 30409365fbf5SPhilippe Reynes .get_link_ksettings = phy_ethtool_get_link_ksettings, 30419365fbf5SPhilippe Reynes .set_link_ksettings = phy_ethtool_set_link_ksettings, 30426016ba34SOleksij Rempel .self_test = net_selftest, 3043793fc096SFrank Li }; 3044793fc096SFrank Li 3045793fc096SFrank Li static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) 3046793fc096SFrank Li { 3047793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 304845f5c327SPhilippe Reynes struct phy_device *phydev = ndev->phydev; 3049793fc096SFrank Li 3050793fc096SFrank Li if (!netif_running(ndev)) 3051793fc096SFrank Li return -EINVAL; 3052793fc096SFrank Li 3053793fc096SFrank Li if (!phydev) 3054793fc096SFrank Li return -ENODEV; 3055793fc096SFrank Li 30561d5244d0SBen Hutchings if (fep->bufdesc_ex) { 305734074639SSergey Organov bool use_fec_hwts = !phy_has_hwtstamp(phydev); 305834074639SSergey Organov 305934074639SSergey Organov if (cmd == SIOCSHWTSTAMP) { 306034074639SSergey Organov if (use_fec_hwts) 30611d5244d0SBen Hutchings return fec_ptp_set(ndev, rq); 306234074639SSergey Organov fec_ptp_disable_hwts(ndev); 306334074639SSergey Organov } else if (cmd == SIOCGHWTSTAMP) { 306434074639SSergey Organov if (use_fec_hwts) 30651d5244d0SBen Hutchings return fec_ptp_get(ndev, rq); 30661d5244d0SBen Hutchings } 306734074639SSergey Organov } 3068793fc096SFrank Li 3069793fc096SFrank Li return phy_mii_ioctl(phydev, rq, cmd); 3070793fc096SFrank Li } 3071793fc096SFrank Li 3072793fc096SFrank Li static void fec_enet_free_buffers(struct net_device *ndev) 3073793fc096SFrank Li { 3074793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3075793fc096SFrank Li unsigned int i; 3076793fc096SFrank Li struct sk_buff *skb; 30774d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 30784d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 307959d0f746SFrank Li unsigned int q; 3080793fc096SFrank Li 308159d0f746SFrank Li for (q = 0; q < fep->num_rx_queues; q++) { 308259d0f746SFrank Li rxq = fep->rx_queue[q]; 308395698ff6SShenwei Wang for (i = 0; i < rxq->bd.ring_size; i++) 308495698ff6SShenwei Wang page_pool_release_page(rxq->page_pool, rxq->rx_skb_info[i].page); 308595698ff6SShenwei Wang 308695698ff6SShenwei Wang if (xdp_rxq_info_is_reg(&rxq->xdp_rxq)) 308795698ff6SShenwei Wang xdp_rxq_info_unreg(&rxq->xdp_rxq); 308895698ff6SShenwei Wang page_pool_destroy(rxq->page_pool); 308995698ff6SShenwei Wang rxq->page_pool = NULL; 3090793fc096SFrank Li } 3091793fc096SFrank Li 309259d0f746SFrank Li for (q = 0; q < fep->num_tx_queues; q++) { 309359d0f746SFrank Li txq = fep->tx_queue[q]; 30947355f276STroy Kisky for (i = 0; i < txq->bd.ring_size; i++) { 30954d494cdcSFugang Duan kfree(txq->tx_bounce[i]); 30964d494cdcSFugang Duan txq->tx_bounce[i] = NULL; 30974d494cdcSFugang Duan skb = txq->tx_skbuff[i]; 30984d494cdcSFugang Duan txq->tx_skbuff[i] = NULL; 30998b7c9efaSRussell King dev_kfree_skb(skb); 31008b7c9efaSRussell King } 3101793fc096SFrank Li } 310259d0f746SFrank Li } 3103793fc096SFrank Li 310459d0f746SFrank Li static void fec_enet_free_queue(struct net_device *ndev) 310559d0f746SFrank Li { 310659d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 310759d0f746SFrank Li int i; 310859d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 310959d0f746SFrank Li 311059d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) 311159d0f746SFrank Li if (fep->tx_queue[i] && fep->tx_queue[i]->tso_hdrs) { 311259d0f746SFrank Li txq = fep->tx_queue[i]; 311394920128SFugang Duan dma_free_coherent(&fep->pdev->dev, 31147355f276STroy Kisky txq->bd.ring_size * TSO_HEADER_SIZE, 311559d0f746SFrank Li txq->tso_hdrs, 311659d0f746SFrank Li txq->tso_hdrs_dma); 311759d0f746SFrank Li } 311859d0f746SFrank Li 311959d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) 312059d0f746SFrank Li kfree(fep->rx_queue[i]); 312159d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) 312259d0f746SFrank Li kfree(fep->tx_queue[i]); 312359d0f746SFrank Li } 312459d0f746SFrank Li 312559d0f746SFrank Li static int fec_enet_alloc_queue(struct net_device *ndev) 312659d0f746SFrank Li { 312759d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 312859d0f746SFrank Li int i; 312959d0f746SFrank Li int ret = 0; 313059d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 313159d0f746SFrank Li 313259d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 313359d0f746SFrank Li txq = kzalloc(sizeof(*txq), GFP_KERNEL); 313459d0f746SFrank Li if (!txq) { 313559d0f746SFrank Li ret = -ENOMEM; 313659d0f746SFrank Li goto alloc_failed; 313759d0f746SFrank Li } 313859d0f746SFrank Li 313959d0f746SFrank Li fep->tx_queue[i] = txq; 31407355f276STroy Kisky txq->bd.ring_size = TX_RING_SIZE; 31417355f276STroy Kisky fep->total_tx_ring_size += fep->tx_queue[i]->bd.ring_size; 314259d0f746SFrank Li 314359d0f746SFrank Li txq->tx_stop_threshold = FEC_MAX_SKB_DESCS; 314459d0f746SFrank Li txq->tx_wake_threshold = 31457355f276STroy Kisky (txq->bd.ring_size - txq->tx_stop_threshold) / 2; 314659d0f746SFrank Li 314794920128SFugang Duan txq->tso_hdrs = dma_alloc_coherent(&fep->pdev->dev, 31487355f276STroy Kisky txq->bd.ring_size * TSO_HEADER_SIZE, 314959d0f746SFrank Li &txq->tso_hdrs_dma, 315059d0f746SFrank Li GFP_KERNEL); 315159d0f746SFrank Li if (!txq->tso_hdrs) { 315259d0f746SFrank Li ret = -ENOMEM; 315359d0f746SFrank Li goto alloc_failed; 315459d0f746SFrank Li } 315559d0f746SFrank Li } 315659d0f746SFrank Li 315759d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) { 315859d0f746SFrank Li fep->rx_queue[i] = kzalloc(sizeof(*fep->rx_queue[i]), 315959d0f746SFrank Li GFP_KERNEL); 316059d0f746SFrank Li if (!fep->rx_queue[i]) { 316159d0f746SFrank Li ret = -ENOMEM; 316259d0f746SFrank Li goto alloc_failed; 316359d0f746SFrank Li } 316459d0f746SFrank Li 31657355f276STroy Kisky fep->rx_queue[i]->bd.ring_size = RX_RING_SIZE; 31667355f276STroy Kisky fep->total_rx_ring_size += fep->rx_queue[i]->bd.ring_size; 316759d0f746SFrank Li } 316859d0f746SFrank Li return ret; 316959d0f746SFrank Li 317059d0f746SFrank Li alloc_failed: 317159d0f746SFrank Li fec_enet_free_queue(ndev); 317259d0f746SFrank Li return ret; 317359d0f746SFrank Li } 317459d0f746SFrank Li 317559d0f746SFrank Li static int 317659d0f746SFrank Li fec_enet_alloc_rxq_buffers(struct net_device *ndev, unsigned int queue) 3177793fc096SFrank Li { 3178793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 31794d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 318095698ff6SShenwei Wang dma_addr_t phys_addr; 318195698ff6SShenwei Wang struct bufdesc *bdp; 318295698ff6SShenwei Wang struct page *page; 318395698ff6SShenwei Wang int i, err; 3184793fc096SFrank Li 318559d0f746SFrank Li rxq = fep->rx_queue[queue]; 31867355f276STroy Kisky bdp = rxq->bd.base; 3187793fc096SFrank Li 318895698ff6SShenwei Wang err = fec_enet_create_page_pool(fep, rxq, rxq->bd.ring_size); 318995698ff6SShenwei Wang if (err < 0) { 319095698ff6SShenwei Wang netdev_err(ndev, "%s failed queue %d (%d)\n", __func__, queue, err); 319195698ff6SShenwei Wang return err; 3192d842a31fSDuan Fugang-B38611 } 3193730ee360SRussell King 319495698ff6SShenwei Wang for (i = 0; i < rxq->bd.ring_size; i++) { 319595698ff6SShenwei Wang page = page_pool_dev_alloc_pages(rxq->page_pool); 319695698ff6SShenwei Wang if (!page) 319795698ff6SShenwei Wang goto err_alloc; 319895698ff6SShenwei Wang 319995698ff6SShenwei Wang phys_addr = page_pool_get_dma_addr(page) + FEC_ENET_XDP_HEADROOM; 320095698ff6SShenwei Wang bdp->cbd_bufaddr = cpu_to_fec32(phys_addr); 320195698ff6SShenwei Wang 320295698ff6SShenwei Wang rxq->rx_skb_info[i].page = page; 320395698ff6SShenwei Wang rxq->rx_skb_info[i].offset = FEC_ENET_XDP_HEADROOM; 32045cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); 3205793fc096SFrank Li 3206793fc096SFrank Li if (fep->bufdesc_ex) { 3207793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 32085cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(BD_ENET_RX_INT); 3209793fc096SFrank Li } 3210793fc096SFrank Li 32117355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 3212793fc096SFrank Li } 3213793fc096SFrank Li 3214793fc096SFrank Li /* Set the last buffer to wrap. */ 32157355f276STroy Kisky bdp = fec_enet_get_prevdesc(bdp, &rxq->bd); 32165cfa3039SJohannes Berg bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 321759d0f746SFrank Li return 0; 3218793fc096SFrank Li 321959d0f746SFrank Li err_alloc: 322059d0f746SFrank Li fec_enet_free_buffers(ndev); 322159d0f746SFrank Li return -ENOMEM; 322259d0f746SFrank Li } 322359d0f746SFrank Li 322459d0f746SFrank Li static int 322559d0f746SFrank Li fec_enet_alloc_txq_buffers(struct net_device *ndev, unsigned int queue) 322659d0f746SFrank Li { 322759d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 322859d0f746SFrank Li unsigned int i; 322959d0f746SFrank Li struct bufdesc *bdp; 323059d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 323159d0f746SFrank Li 323259d0f746SFrank Li txq = fep->tx_queue[queue]; 32337355f276STroy Kisky bdp = txq->bd.base; 32347355f276STroy Kisky for (i = 0; i < txq->bd.ring_size; i++) { 32354d494cdcSFugang Duan txq->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL); 32364d494cdcSFugang Duan if (!txq->tx_bounce[i]) 3237ffdce2ccSRussell King goto err_alloc; 3238793fc096SFrank Li 32395cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(0); 32405cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(0); 3241793fc096SFrank Li 3242793fc096SFrank Li if (fep->bufdesc_ex) { 3243793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 32445cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(BD_ENET_TX_INT); 3245793fc096SFrank Li } 3246793fc096SFrank Li 32477355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 3248793fc096SFrank Li } 3249793fc096SFrank Li 3250793fc096SFrank Li /* Set the last buffer to wrap. */ 32517355f276STroy Kisky bdp = fec_enet_get_prevdesc(bdp, &txq->bd); 32525cfa3039SJohannes Berg bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 3253793fc096SFrank Li 3254793fc096SFrank Li return 0; 3255ffdce2ccSRussell King 3256ffdce2ccSRussell King err_alloc: 3257ffdce2ccSRussell King fec_enet_free_buffers(ndev); 3258ffdce2ccSRussell King return -ENOMEM; 3259793fc096SFrank Li } 3260793fc096SFrank Li 326159d0f746SFrank Li static int fec_enet_alloc_buffers(struct net_device *ndev) 326259d0f746SFrank Li { 326359d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 326459d0f746SFrank Li unsigned int i; 326559d0f746SFrank Li 326659d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) 326759d0f746SFrank Li if (fec_enet_alloc_rxq_buffers(ndev, i)) 326859d0f746SFrank Li return -ENOMEM; 326959d0f746SFrank Li 327059d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) 327159d0f746SFrank Li if (fec_enet_alloc_txq_buffers(ndev, i)) 327259d0f746SFrank Li return -ENOMEM; 327359d0f746SFrank Li return 0; 327459d0f746SFrank Li } 327559d0f746SFrank Li 3276793fc096SFrank Li static int 3277793fc096SFrank Li fec_enet_open(struct net_device *ndev) 3278793fc096SFrank Li { 3279793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3280793fc096SFrank Li int ret; 32811b0a83acSRichard Leitner bool reset_again; 3282793fc096SFrank Li 3283da875fa5SZhang Qilong ret = pm_runtime_resume_and_get(&fep->pdev->dev); 3284b0c6ce24SFabio Estevam if (ret < 0) 32858fff755eSAndrew Lunn return ret; 32868fff755eSAndrew Lunn 32875bbde4d2SNimrod Andy pinctrl_pm_select_default_state(&fep->pdev->dev); 3288e8fcfcd5SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 3289e8fcfcd5SNimrod Andy if (ret) 32908fff755eSAndrew Lunn goto clk_enable; 3291e8fcfcd5SNimrod Andy 32921b0a83acSRichard Leitner /* During the first fec_enet_open call the PHY isn't probed at this 32931b0a83acSRichard Leitner * point. Therefore the phy_reset_after_clk_enable() call within 32941b0a83acSRichard Leitner * fec_enet_clk_enable() fails. As we need this reset in order to be 32951b0a83acSRichard Leitner * sure the PHY is working correctly we check if we need to reset again 32961b0a83acSRichard Leitner * later when the PHY is probed 32971b0a83acSRichard Leitner */ 32981b0a83acSRichard Leitner if (ndev->phydev && ndev->phydev->drv) 32991b0a83acSRichard Leitner reset_again = false; 33001b0a83acSRichard Leitner else 33011b0a83acSRichard Leitner reset_again = true; 33021b0a83acSRichard Leitner 3303793fc096SFrank Li /* I should reset the ring buffers here, but I don't yet know 3304793fc096SFrank Li * a simple way to do that. 3305793fc096SFrank Li */ 3306793fc096SFrank Li 3307793fc096SFrank Li ret = fec_enet_alloc_buffers(ndev); 3308793fc096SFrank Li if (ret) 3309681d2421SFabio Estevam goto err_enet_alloc; 3310793fc096SFrank Li 331155dd2753SNimrod Andy /* Init MAC prior to mii bus probe */ 331255dd2753SNimrod Andy fec_restart(ndev); 331355dd2753SNimrod Andy 33141b0a83acSRichard Leitner /* Call phy_reset_after_clk_enable() again if it failed during 33151b0a83acSRichard Leitner * phy_reset_after_clk_enable() before because the PHY wasn't probed. 33161b0a83acSRichard Leitner */ 33171b0a83acSRichard Leitner if (reset_again) 331864a632daSMarek Vasut fec_enet_phy_reset_after_clk_enable(ndev); 33191b0a83acSRichard Leitner 33200da1ccbbSMarek Vasut /* Probe and connect to PHY when open the interface */ 33210da1ccbbSMarek Vasut ret = fec_enet_mii_probe(ndev); 33220da1ccbbSMarek Vasut if (ret) 33230da1ccbbSMarek Vasut goto err_enet_mii_probe; 3324681d2421SFabio Estevam 3325681d2421SFabio Estevam if (fep->quirks & FEC_QUIRK_ERR006687) 3326793fc096SFrank Li imx6q_cpuidle_fec_irqs_used(); 3327793fc096SFrank Li 33287d650df9SWei Fang if (fep->quirks & FEC_QUIRK_HAS_PMQOS) 33297d650df9SWei Fang cpu_latency_qos_add_request(&fep->pm_qos_req, 0); 33307d650df9SWei Fang 3331793fc096SFrank Li napi_enable(&fep->napi); 3332793fc096SFrank Li phy_start(ndev->phydev); 3333793fc096SFrank Li netif_tx_start_all_queues(ndev); 3334793fc096SFrank Li 3335793fc096SFrank Li device_set_wakeup_enable(&ndev->dev, fep->wol_flag & 3336793fc096SFrank Li FEC_WOL_FLAG_ENABLE); 3337793fc096SFrank Li 3338793fc096SFrank Li return 0; 3339793fc096SFrank Li 3340793fc096SFrank Li err_enet_mii_probe: 3341793fc096SFrank Li fec_enet_free_buffers(ndev); 3342793fc096SFrank Li err_enet_alloc: 3343793fc096SFrank Li fec_enet_clk_enable(ndev, false); 3344793fc096SFrank Li clk_enable: 3345793fc096SFrank Li pm_runtime_mark_last_busy(&fep->pdev->dev); 3346793fc096SFrank Li pm_runtime_put_autosuspend(&fep->pdev->dev); 3347793fc096SFrank Li pinctrl_pm_select_sleep_state(&fep->pdev->dev); 3348793fc096SFrank Li return ret; 3349d76cfae9SRussell King } 3350d76cfae9SRussell King 335131a6de34SRussell King static int 3352793fc096SFrank Li fec_enet_close(struct net_device *ndev) 3353b49cd504SRussell King { 3354793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 335531a6de34SRussell King 335645f5c327SPhilippe Reynes phy_stop(ndev->phydev); 3357793fc096SFrank Li 3358793fc096SFrank Li if (netif_device_present(ndev)) { 3359793fc096SFrank Li napi_disable(&fep->napi); 3360793fc096SFrank Li netif_tx_disable(ndev); 3361793fc096SFrank Li fec_stop(ndev); 3362793fc096SFrank Li } 3363793fc096SFrank Li 336445f5c327SPhilippe Reynes phy_disconnect(ndev->phydev); 3365793fc096SFrank Li 336629380905SLucas Stach if (fep->quirks & FEC_QUIRK_ERR006687) 336729380905SLucas Stach imx6q_cpuidle_fec_irqs_unused(); 336829380905SLucas Stach 336980cca775SNikita Yushchenko fec_enet_update_ethtool_stats(ndev); 337080cca775SNikita Yushchenko 3371e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 33727d650df9SWei Fang if (fep->quirks & FEC_QUIRK_HAS_PMQOS) 33737d650df9SWei Fang cpu_latency_qos_remove_request(&fep->pm_qos_req); 33747d650df9SWei Fang 33755bbde4d2SNimrod Andy pinctrl_pm_select_sleep_state(&fep->pdev->dev); 33768fff755eSAndrew Lunn pm_runtime_mark_last_busy(&fep->pdev->dev); 33778fff755eSAndrew Lunn pm_runtime_put_autosuspend(&fep->pdev->dev); 33788fff755eSAndrew Lunn 3379793fc096SFrank Li fec_enet_free_buffers(ndev); 3380793fc096SFrank Li 3381793fc096SFrank Li return 0; 3382793fc096SFrank Li } 3383793fc096SFrank Li 3384793fc096SFrank Li /* Set or clear the multicast filter for this adaptor. 3385793fc096SFrank Li * Skeleton taken from sunlance driver. 3386793fc096SFrank Li * The CPM Ethernet implementation allows Multicast as well as individual 3387793fc096SFrank Li * MAC address filtering. Some of the drivers check to make sure it is 3388793fc096SFrank Li * a group multicast address, and discard those that are not. I guess I 3389793fc096SFrank Li * will do the same for now, but just remove the test if you want 3390793fc096SFrank Li * individual filtering as well (do the upper net layers want or support 3391793fc096SFrank Li * this kind of feature?). 3392793fc096SFrank Li */ 3393793fc096SFrank Li 33946176e89cSJiri Kosina #define FEC_HASH_BITS 6 /* #bits in hash */ 3395793fc096SFrank Li 3396793fc096SFrank Li static void set_multicast_list(struct net_device *ndev) 3397793fc096SFrank Li { 3398793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3399793fc096SFrank Li struct netdev_hw_addr *ha; 340016f6e983SKrzysztof Kozlowski unsigned int crc, tmp; 3401793fc096SFrank Li unsigned char hash; 340201f8902bSRui Sousa unsigned int hash_high = 0, hash_low = 0; 3403793fc096SFrank Li 3404793fc096SFrank Li if (ndev->flags & IFF_PROMISC) { 3405793fc096SFrank Li tmp = readl(fep->hwp + FEC_R_CNTRL); 3406793fc096SFrank Li tmp |= 0x8; 3407793fc096SFrank Li writel(tmp, fep->hwp + FEC_R_CNTRL); 3408793fc096SFrank Li return; 3409793fc096SFrank Li } 3410793fc096SFrank Li 3411793fc096SFrank Li tmp = readl(fep->hwp + FEC_R_CNTRL); 3412793fc096SFrank Li tmp &= ~0x8; 3413793fc096SFrank Li writel(tmp, fep->hwp + FEC_R_CNTRL); 3414793fc096SFrank Li 3415793fc096SFrank Li if (ndev->flags & IFF_ALLMULTI) { 3416793fc096SFrank Li /* Catch all multicast addresses, so set the 3417793fc096SFrank Li * filter to all 1's 3418793fc096SFrank Li */ 3419793fc096SFrank Li writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 3420793fc096SFrank Li writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 3421793fc096SFrank Li 3422793fc096SFrank Li return; 3423793fc096SFrank Li } 3424793fc096SFrank Li 342501f8902bSRui Sousa /* Add the addresses in hash register */ 3426793fc096SFrank Li netdev_for_each_mc_addr(ha, ndev) { 3427793fc096SFrank Li /* calculate crc32 value of mac address */ 342816f6e983SKrzysztof Kozlowski crc = ether_crc_le(ndev->addr_len, ha->addr); 3429793fc096SFrank Li 34306176e89cSJiri Kosina /* only upper 6 bits (FEC_HASH_BITS) are used 3431981a0547SPeter Meerwald-Stadler * which point to specific bit in the hash registers 3432793fc096SFrank Li */ 34336176e89cSJiri Kosina hash = (crc >> (32 - FEC_HASH_BITS)) & 0x3f; 3434793fc096SFrank Li 343501f8902bSRui Sousa if (hash > 31) 343601f8902bSRui Sousa hash_high |= 1 << (hash - 32); 343701f8902bSRui Sousa else 343801f8902bSRui Sousa hash_low |= 1 << hash; 3439793fc096SFrank Li } 344001f8902bSRui Sousa 344101f8902bSRui Sousa writel(hash_high, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 344201f8902bSRui Sousa writel(hash_low, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 3443793fc096SFrank Li } 3444793fc096SFrank Li 3445793fc096SFrank Li /* Set a MAC change in hardware. */ 3446793fc096SFrank Li static int 3447793fc096SFrank Li fec_set_mac_address(struct net_device *ndev, void *p) 3448793fc096SFrank Li { 3449793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3450793fc096SFrank Li struct sockaddr *addr = p; 3451793fc096SFrank Li 345244934facSLucas Stach if (addr) { 3453793fc096SFrank Li if (!is_valid_ether_addr(addr->sa_data)) 3454793fc096SFrank Li return -EADDRNOTAVAIL; 3455a05e4c0aSJakub Kicinski eth_hw_addr_set(ndev, addr->sa_data); 345644934facSLucas Stach } 3457793fc096SFrank Li 34589638d19eSNimrod Andy /* Add netif status check here to avoid system hang in below case: 34599638d19eSNimrod Andy * ifconfig ethx down; ifconfig ethx hw ether xx:xx:xx:xx:xx:xx; 34609638d19eSNimrod Andy * After ethx down, fec all clocks are gated off and then register 34619638d19eSNimrod Andy * access causes system hang. 34629638d19eSNimrod Andy */ 34639638d19eSNimrod Andy if (!netif_running(ndev)) 34649638d19eSNimrod Andy return 0; 34659638d19eSNimrod Andy 3466793fc096SFrank Li writel(ndev->dev_addr[3] | (ndev->dev_addr[2] << 8) | 3467793fc096SFrank Li (ndev->dev_addr[1] << 16) | (ndev->dev_addr[0] << 24), 3468793fc096SFrank Li fep->hwp + FEC_ADDR_LOW); 3469793fc096SFrank Li writel((ndev->dev_addr[5] << 16) | (ndev->dev_addr[4] << 24), 3470793fc096SFrank Li fep->hwp + FEC_ADDR_HIGH); 3471793fc096SFrank Li return 0; 3472793fc096SFrank Li } 3473793fc096SFrank Li 3474793fc096SFrank Li #ifdef CONFIG_NET_POLL_CONTROLLER 3475793fc096SFrank Li /** 3476793fc096SFrank Li * fec_poll_controller - FEC Poll controller function 3477793fc096SFrank Li * @dev: The FEC network adapter 3478793fc096SFrank Li * 3479793fc096SFrank Li * Polled functionality used by netconsole and others in non interrupt mode 3480793fc096SFrank Li * 3481793fc096SFrank Li */ 3482793fc096SFrank Li static void fec_poll_controller(struct net_device *dev) 3483793fc096SFrank Li { 3484793fc096SFrank Li int i; 3485793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(dev); 3486793fc096SFrank Li 3487793fc096SFrank Li for (i = 0; i < FEC_IRQ_NUM; i++) { 3488793fc096SFrank Li if (fep->irq[i] > 0) { 3489793fc096SFrank Li disable_irq(fep->irq[i]); 3490793fc096SFrank Li fec_enet_interrupt(fep->irq[i], dev); 3491793fc096SFrank Li enable_irq(fep->irq[i]); 3492793fc096SFrank Li } 3493793fc096SFrank Li } 3494793fc096SFrank Li } 3495793fc096SFrank Li #endif 3496793fc096SFrank Li 34975bc26726SNimrod Andy static inline void fec_enet_set_netdev_features(struct net_device *netdev, 34984c09eed9SJim Baxter netdev_features_t features) 34994c09eed9SJim Baxter { 35004c09eed9SJim Baxter struct fec_enet_private *fep = netdev_priv(netdev); 35014c09eed9SJim Baxter netdev_features_t changed = features ^ netdev->features; 35024c09eed9SJim Baxter 35034c09eed9SJim Baxter netdev->features = features; 35044c09eed9SJim Baxter 35054c09eed9SJim Baxter /* Receive checksum has been changed */ 35064c09eed9SJim Baxter if (changed & NETIF_F_RXCSUM) { 35074c09eed9SJim Baxter if (features & NETIF_F_RXCSUM) 35084c09eed9SJim Baxter fep->csum_flags |= FLAG_RX_CSUM_ENABLED; 35094c09eed9SJim Baxter else 35104c09eed9SJim Baxter fep->csum_flags &= ~FLAG_RX_CSUM_ENABLED; 35118506fa1dSRussell King } 35125bc26726SNimrod Andy } 35134c09eed9SJim Baxter 35145bc26726SNimrod Andy static int fec_set_features(struct net_device *netdev, 35155bc26726SNimrod Andy netdev_features_t features) 35165bc26726SNimrod Andy { 35175bc26726SNimrod Andy struct fec_enet_private *fep = netdev_priv(netdev); 35185bc26726SNimrod Andy netdev_features_t changed = features ^ netdev->features; 35195bc26726SNimrod Andy 35205b40f709SFabio Estevam if (netif_running(netdev) && changed & NETIF_F_RXCSUM) { 35215bc26726SNimrod Andy napi_disable(&fep->napi); 35225bc26726SNimrod Andy netif_tx_lock_bh(netdev); 35235bc26726SNimrod Andy fec_stop(netdev); 35245bc26726SNimrod Andy fec_enet_set_netdev_features(netdev, features); 3525ef83337dSRussell King fec_restart(netdev); 35264d494cdcSFugang Duan netif_tx_wake_all_queues(netdev); 35276af42d42SRussell King netif_tx_unlock_bh(netdev); 3528dbc64a8eSRussell King napi_enable(&fep->napi); 35295bc26726SNimrod Andy } else { 35305bc26726SNimrod Andy fec_enet_set_netdev_features(netdev, features); 35314c09eed9SJim Baxter } 35324c09eed9SJim Baxter 35334c09eed9SJim Baxter return 0; 35344c09eed9SJim Baxter } 35354c09eed9SJim Baxter 353652c4a1a8SFugang Duan static u16 fec_enet_get_raw_vlan_tci(struct sk_buff *skb) 353752c4a1a8SFugang Duan { 353852c4a1a8SFugang Duan struct vlan_ethhdr *vhdr; 353952c4a1a8SFugang Duan unsigned short vlan_TCI = 0; 354052c4a1a8SFugang Duan 354152c4a1a8SFugang Duan if (skb->protocol == htons(ETH_P_ALL)) { 354252c4a1a8SFugang Duan vhdr = (struct vlan_ethhdr *)(skb->data); 354352c4a1a8SFugang Duan vlan_TCI = ntohs(vhdr->h_vlan_TCI); 354452c4a1a8SFugang Duan } 354552c4a1a8SFugang Duan 354652c4a1a8SFugang Duan return vlan_TCI; 354752c4a1a8SFugang Duan } 354852c4a1a8SFugang Duan 354952c4a1a8SFugang Duan static u16 fec_enet_select_queue(struct net_device *ndev, struct sk_buff *skb, 355052c4a1a8SFugang Duan struct net_device *sb_dev) 355152c4a1a8SFugang Duan { 355252c4a1a8SFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 355352c4a1a8SFugang Duan u16 vlan_tag; 355452c4a1a8SFugang Duan 355552c4a1a8SFugang Duan if (!(fep->quirks & FEC_QUIRK_HAS_AVB)) 355652c4a1a8SFugang Duan return netdev_pick_tx(ndev, skb, NULL); 355752c4a1a8SFugang Duan 355852c4a1a8SFugang Duan vlan_tag = fec_enet_get_raw_vlan_tci(skb); 355952c4a1a8SFugang Duan if (!vlan_tag) 356052c4a1a8SFugang Duan return vlan_tag; 356152c4a1a8SFugang Duan 356252c4a1a8SFugang Duan return fec_enet_vlan_pri_to_queue[vlan_tag >> 13]; 356352c4a1a8SFugang Duan } 356452c4a1a8SFugang Duan 3565793fc096SFrank Li static const struct net_device_ops fec_netdev_ops = { 3566793fc096SFrank Li .ndo_open = fec_enet_open, 3567793fc096SFrank Li .ndo_stop = fec_enet_close, 3568793fc096SFrank Li .ndo_start_xmit = fec_enet_start_xmit, 356952c4a1a8SFugang Duan .ndo_select_queue = fec_enet_select_queue, 3570793fc096SFrank Li .ndo_set_rx_mode = set_multicast_list, 3571793fc096SFrank Li .ndo_validate_addr = eth_validate_addr, 3572793fc096SFrank Li .ndo_tx_timeout = fec_timeout, 3573793fc096SFrank Li .ndo_set_mac_address = fec_set_mac_address, 3574a7605370SArnd Bergmann .ndo_eth_ioctl = fec_enet_ioctl, 3575793fc096SFrank Li #ifdef CONFIG_NET_POLL_CONTROLLER 3576793fc096SFrank Li .ndo_poll_controller = fec_poll_controller, 3577793fc096SFrank Li #endif 35784c09eed9SJim Baxter .ndo_set_features = fec_set_features, 3579793fc096SFrank Li }; 3580793fc096SFrank Li 358153bb20d1STroy Kisky static const unsigned short offset_des_active_rxq[] = { 358253bb20d1STroy Kisky FEC_R_DES_ACTIVE_0, FEC_R_DES_ACTIVE_1, FEC_R_DES_ACTIVE_2 358353bb20d1STroy Kisky }; 358453bb20d1STroy Kisky 358553bb20d1STroy Kisky static const unsigned short offset_des_active_txq[] = { 358653bb20d1STroy Kisky FEC_X_DES_ACTIVE_0, FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2 358753bb20d1STroy Kisky }; 358853bb20d1STroy Kisky 3589793fc096SFrank Li /* 3590793fc096SFrank Li * XXX: We need to clean up on failure exits here. 3591793fc096SFrank Li * 3592793fc096SFrank Li */ 3593793fc096SFrank Li static int fec_enet_init(struct net_device *ndev) 3594793fc096SFrank Li { 3595793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3596793fc096SFrank Li struct bufdesc *cbd_base; 35974d494cdcSFugang Duan dma_addr_t bd_dma; 359855d0218aSNimrod Andy int bd_size; 359959d0f746SFrank Li unsigned int i; 36007355f276STroy Kisky unsigned dsize = fep->bufdesc_ex ? sizeof(struct bufdesc_ex) : 36017355f276STroy Kisky sizeof(struct bufdesc); 36027355f276STroy Kisky unsigned dsize_log2 = __fls(dsize); 3603453e9dc4SStefan Agner int ret; 360455d0218aSNimrod Andy 36057355f276STroy Kisky WARN_ON(dsize != (1 << dsize_log2)); 36063f1dcc6aSLucas Stach #if defined(CONFIG_ARM) || defined(CONFIG_ARM64) 360741ef84ceSFugang Duan fep->rx_align = 0xf; 360841ef84ceSFugang Duan fep->tx_align = 0xf; 360941ef84ceSFugang Duan #else 361041ef84ceSFugang Duan fep->rx_align = 0x3; 361141ef84ceSFugang Duan fep->tx_align = 0x3; 361241ef84ceSFugang Duan #endif 3613df727d45SRasmus Villemoes fep->rx_pkts_itr = FEC_ITR_ICFT_DEFAULT; 3614df727d45SRasmus Villemoes fep->tx_pkts_itr = FEC_ITR_ICFT_DEFAULT; 3615df727d45SRasmus Villemoes fep->rx_time_itr = FEC_ITR_ICTT_DEFAULT; 3616df727d45SRasmus Villemoes fep->tx_time_itr = FEC_ITR_ICTT_DEFAULT; 361741ef84ceSFugang Duan 3618453e9dc4SStefan Agner /* Check mask of the streaming and coherent API */ 3619453e9dc4SStefan Agner ret = dma_set_mask_and_coherent(&fep->pdev->dev, DMA_BIT_MASK(32)); 3620453e9dc4SStefan Agner if (ret < 0) { 3621453e9dc4SStefan Agner dev_warn(&fep->pdev->dev, "No suitable DMA available\n"); 3622453e9dc4SStefan Agner return ret; 3623453e9dc4SStefan Agner } 3624453e9dc4SStefan Agner 3625619fee9eSFugang Duan ret = fec_enet_alloc_queue(ndev); 3626619fee9eSFugang Duan if (ret) 3627619fee9eSFugang Duan return ret; 362879f33912SNimrod Andy 36297355f276STroy Kisky bd_size = (fep->total_tx_ring_size + fep->total_rx_ring_size) * dsize; 3630793fc096SFrank Li 3631793fc096SFrank Li /* Allocate memory for buffer descriptors. */ 3632c0a1a0a6SLucas Stach cbd_base = dmam_alloc_coherent(&fep->pdev->dev, bd_size, &bd_dma, 3633793fc096SFrank Li GFP_KERNEL); 36344d494cdcSFugang Duan if (!cbd_base) { 3635619fee9eSFugang Duan ret = -ENOMEM; 3636619fee9eSFugang Duan goto free_queue_mem; 36374d494cdcSFugang Duan } 3638793fc096SFrank Li 3639793fc096SFrank Li /* Get the Ethernet address */ 3640052fcc45SFugang Duan ret = fec_get_mac(ndev); 3641052fcc45SFugang Duan if (ret) 3642052fcc45SFugang Duan goto free_queue_mem; 3643052fcc45SFugang Duan 364444934facSLucas Stach /* make sure MAC we just acquired is programmed into the hw */ 364544934facSLucas Stach fec_set_mac_address(ndev, NULL); 3646793fc096SFrank Li 3647793fc096SFrank Li /* Set receive and transmit descriptor base. */ 364859d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) { 36497355f276STroy Kisky struct fec_enet_priv_rx_q *rxq = fep->rx_queue[i]; 36507355f276STroy Kisky unsigned size = dsize * rxq->bd.ring_size; 36517355f276STroy Kisky 36527355f276STroy Kisky rxq->bd.qid = i; 36537355f276STroy Kisky rxq->bd.base = cbd_base; 36547355f276STroy Kisky rxq->bd.cur = cbd_base; 36557355f276STroy Kisky rxq->bd.dma = bd_dma; 36567355f276STroy Kisky rxq->bd.dsize = dsize; 36577355f276STroy Kisky rxq->bd.dsize_log2 = dsize_log2; 365853bb20d1STroy Kisky rxq->bd.reg_desc_active = fep->hwp + offset_des_active_rxq[i]; 36597355f276STroy Kisky bd_dma += size; 36607355f276STroy Kisky cbd_base = (struct bufdesc *)(((void *)cbd_base) + size); 36617355f276STroy Kisky rxq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize); 366259d0f746SFrank Li } 366359d0f746SFrank Li 366459d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 36657355f276STroy Kisky struct fec_enet_priv_tx_q *txq = fep->tx_queue[i]; 36667355f276STroy Kisky unsigned size = dsize * txq->bd.ring_size; 36677355f276STroy Kisky 36687355f276STroy Kisky txq->bd.qid = i; 36697355f276STroy Kisky txq->bd.base = cbd_base; 36707355f276STroy Kisky txq->bd.cur = cbd_base; 36717355f276STroy Kisky txq->bd.dma = bd_dma; 36727355f276STroy Kisky txq->bd.dsize = dsize; 36737355f276STroy Kisky txq->bd.dsize_log2 = dsize_log2; 367453bb20d1STroy Kisky txq->bd.reg_desc_active = fep->hwp + offset_des_active_txq[i]; 36757355f276STroy Kisky bd_dma += size; 36767355f276STroy Kisky cbd_base = (struct bufdesc *)(((void *)cbd_base) + size); 36777355f276STroy Kisky txq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize); 367859d0f746SFrank Li } 36794d494cdcSFugang Duan 3680793fc096SFrank Li 3681793fc096SFrank Li /* The FEC Ethernet specific entries in the device structure */ 3682793fc096SFrank Li ndev->watchdog_timeo = TX_TIMEOUT; 3683793fc096SFrank Li ndev->netdev_ops = &fec_netdev_ops; 3684793fc096SFrank Li ndev->ethtool_ops = &fec_enet_ethtool_ops; 3685793fc096SFrank Li 3686793fc096SFrank Li writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK); 3687b48b89f9SJakub Kicinski netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi); 3688793fc096SFrank Li 36896b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_VLAN) 3690cdffcf1bSJim Baxter /* enable hw VLAN support */ 3691cdffcf1bSJim Baxter ndev->features |= NETIF_F_HW_VLAN_CTAG_RX; 3692cdffcf1bSJim Baxter 36936b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_CSUM) { 3694ee8b7a11SJakub Kicinski netif_set_tso_max_segs(ndev, FEC_MAX_TSO_SEGS); 369579f33912SNimrod Andy 36964c09eed9SJim Baxter /* enable hw accelerator */ 36974c09eed9SJim Baxter ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM 369879f33912SNimrod Andy | NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO); 36994c09eed9SJim Baxter fep->csum_flags |= FLAG_RX_CSUM_ENABLED; 370048496255SShawn Guo } 37014c09eed9SJim Baxter 3702471ff445SJoakim Zhang if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) { 370341ef84ceSFugang Duan fep->tx_align = 0; 370441ef84ceSFugang Duan fep->rx_align = 0x3f; 370541ef84ceSFugang Duan } 370641ef84ceSFugang Duan 370709d1e541SNimrod Andy ndev->hw_features = ndev->features; 370809d1e541SNimrod Andy 3709ef83337dSRussell King fec_restart(ndev); 3710793fc096SFrank Li 37112b30842bSAndrew Lunn if (fep->quirks & FEC_QUIRK_MIB_CLEAR) 37122b30842bSAndrew Lunn fec_enet_clear_ethtool_stats(ndev); 37132b30842bSAndrew Lunn else 371480cca775SNikita Yushchenko fec_enet_update_ethtool_stats(ndev); 371580cca775SNikita Yushchenko 3716793fc096SFrank Li return 0; 3717619fee9eSFugang Duan 3718619fee9eSFugang Duan free_queue_mem: 3719619fee9eSFugang Duan fec_enet_free_queue(ndev); 3720619fee9eSFugang Duan return ret; 3721793fc096SFrank Li } 3722793fc096SFrank Li 3723793fc096SFrank Li #ifdef CONFIG_OF 37249269e556SFugang Duan static int fec_reset_phy(struct platform_device *pdev) 3725793fc096SFrank Li { 3726793fc096SFrank Li int err, phy_reset; 3727962d8cdcSBernhard Walle bool active_high = false; 3728159a0760SQuentin Schulz int msec = 1, phy_post_delay = 0; 3729793fc096SFrank Li struct device_node *np = pdev->dev.of_node; 3730793fc096SFrank Li 3731793fc096SFrank Li if (!np) 37329269e556SFugang Duan return 0; 3733793fc096SFrank Li 373461e04ccbSFugang Duan err = of_property_read_u32(np, "phy-reset-duration", &msec); 3735793fc096SFrank Li /* A sane reset duration should not be longer than 1s */ 373661e04ccbSFugang Duan if (!err && msec > 1000) 3737793fc096SFrank Li msec = 1; 3738793fc096SFrank Li 3739793fc096SFrank Li phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0); 37409269e556SFugang Duan if (phy_reset == -EPROBE_DEFER) 37419269e556SFugang Duan return phy_reset; 37429269e556SFugang Duan else if (!gpio_is_valid(phy_reset)) 37439269e556SFugang Duan return 0; 3744793fc096SFrank Li 3745159a0760SQuentin Schulz err = of_property_read_u32(np, "phy-reset-post-delay", &phy_post_delay); 3746159a0760SQuentin Schulz /* valid reset duration should be less than 1s */ 3747159a0760SQuentin Schulz if (!err && phy_post_delay > 1000) 3748159a0760SQuentin Schulz return -EINVAL; 3749159a0760SQuentin Schulz 3750962d8cdcSBernhard Walle active_high = of_property_read_bool(np, "phy-reset-active-high"); 375164f10f6eSBernhard Walle 3752793fc096SFrank Li err = devm_gpio_request_one(&pdev->dev, phy_reset, 3753962d8cdcSBernhard Walle active_high ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, 375464f10f6eSBernhard Walle "phy-reset"); 3755793fc096SFrank Li if (err) { 3756793fc096SFrank Li dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err); 37579269e556SFugang Duan return err; 3758793fc096SFrank Li } 3759eb37c563SStefan Wahren 3760eb37c563SStefan Wahren if (msec > 20) 3761793fc096SFrank Li msleep(msec); 3762eb37c563SStefan Wahren else 3763eb37c563SStefan Wahren usleep_range(msec * 1000, msec * 1000 + 1000); 3764eb37c563SStefan Wahren 3765962d8cdcSBernhard Walle gpio_set_value_cansleep(phy_reset, !active_high); 37669269e556SFugang Duan 3767159a0760SQuentin Schulz if (!phy_post_delay) 3768159a0760SQuentin Schulz return 0; 3769159a0760SQuentin Schulz 3770159a0760SQuentin Schulz if (phy_post_delay > 20) 3771159a0760SQuentin Schulz msleep(phy_post_delay); 3772159a0760SQuentin Schulz else 3773159a0760SQuentin Schulz usleep_range(phy_post_delay * 1000, 3774159a0760SQuentin Schulz phy_post_delay * 1000 + 1000); 3775159a0760SQuentin Schulz 37769269e556SFugang Duan return 0; 3777793fc096SFrank Li } 3778793fc096SFrank Li #else /* CONFIG_OF */ 37799269e556SFugang Duan static int fec_reset_phy(struct platform_device *pdev) 3780793fc096SFrank Li { 3781793fc096SFrank Li /* 3782793fc096SFrank Li * In case of platform probe, the reset has been done 3783793fc096SFrank Li * by machine code. 3784793fc096SFrank Li */ 37859269e556SFugang Duan return 0; 3786793fc096SFrank Li } 3787793fc096SFrank Li #endif /* CONFIG_OF */ 3788793fc096SFrank Li 37899fc095f1SFugang Duan static void 37909fc095f1SFugang Duan fec_enet_get_queue_num(struct platform_device *pdev, int *num_tx, int *num_rx) 37919fc095f1SFugang Duan { 37929fc095f1SFugang Duan struct device_node *np = pdev->dev.of_node; 37939fc095f1SFugang Duan 37949fc095f1SFugang Duan *num_tx = *num_rx = 1; 37959fc095f1SFugang Duan 37969fc095f1SFugang Duan if (!np || !of_device_is_available(np)) 37979fc095f1SFugang Duan return; 37989fc095f1SFugang Duan 37999fc095f1SFugang Duan /* parse the num of tx and rx queues */ 380073b1c90dSSaurabh Sengar of_property_read_u32(np, "fsl,num-tx-queues", num_tx); 3801b7bd75cfSFrank Li 380273b1c90dSSaurabh Sengar of_property_read_u32(np, "fsl,num-rx-queues", num_rx); 38039fc095f1SFugang Duan 38049fc095f1SFugang Duan if (*num_tx < 1 || *num_tx > FEC_ENET_MAX_TX_QS) { 3805b7bd75cfSFrank Li dev_warn(&pdev->dev, "Invalid num_tx(=%d), fall back to 1\n", 38069fc095f1SFugang Duan *num_tx); 38079fc095f1SFugang Duan *num_tx = 1; 38089fc095f1SFugang Duan return; 38099fc095f1SFugang Duan } 38109fc095f1SFugang Duan 38119fc095f1SFugang Duan if (*num_rx < 1 || *num_rx > FEC_ENET_MAX_RX_QS) { 3812b7bd75cfSFrank Li dev_warn(&pdev->dev, "Invalid num_rx(=%d), fall back to 1\n", 38139fc095f1SFugang Duan *num_rx); 38149fc095f1SFugang Duan *num_rx = 1; 38159fc095f1SFugang Duan return; 38169fc095f1SFugang Duan } 38179fc095f1SFugang Duan 38189fc095f1SFugang Duan } 38199fc095f1SFugang Duan 38204ad1ceecSTroy Kisky static int fec_enet_get_irq_cnt(struct platform_device *pdev) 38214ad1ceecSTroy Kisky { 38224ad1ceecSTroy Kisky int irq_cnt = platform_irq_count(pdev); 38234ad1ceecSTroy Kisky 38244ad1ceecSTroy Kisky if (irq_cnt > FEC_IRQ_NUM) 38254ad1ceecSTroy Kisky irq_cnt = FEC_IRQ_NUM; /* last for pps */ 38264ad1ceecSTroy Kisky else if (irq_cnt == 2) 38274ad1ceecSTroy Kisky irq_cnt = 1; /* last for pps */ 38284ad1ceecSTroy Kisky else if (irq_cnt <= 0) 38294ad1ceecSTroy Kisky irq_cnt = 1; /* At least 1 irq is needed */ 38304ad1ceecSTroy Kisky return irq_cnt; 38314ad1ceecSTroy Kisky } 38324ad1ceecSTroy Kisky 3833b7cdc965SJoakim Zhang static void fec_enet_get_wakeup_irq(struct platform_device *pdev) 3834b7cdc965SJoakim Zhang { 3835b7cdc965SJoakim Zhang struct net_device *ndev = platform_get_drvdata(pdev); 3836b7cdc965SJoakim Zhang struct fec_enet_private *fep = netdev_priv(ndev); 3837b7cdc965SJoakim Zhang 3838b7cdc965SJoakim Zhang if (fep->quirks & FEC_QUIRK_WAKEUP_FROM_INT2) 3839b7cdc965SJoakim Zhang fep->wake_irq = fep->irq[2]; 3840b7cdc965SJoakim Zhang else 3841b7cdc965SJoakim Zhang fep->wake_irq = fep->irq[0]; 3842b7cdc965SJoakim Zhang } 3843b7cdc965SJoakim Zhang 3844da722186SMartin Fuzzey static int fec_enet_init_stop_mode(struct fec_enet_private *fep, 3845da722186SMartin Fuzzey struct device_node *np) 3846da722186SMartin Fuzzey { 3847da722186SMartin Fuzzey struct device_node *gpr_np; 38488a448bf8SFugang Duan u32 out_val[3]; 3849da722186SMartin Fuzzey int ret = 0; 3850da722186SMartin Fuzzey 38518a448bf8SFugang Duan gpr_np = of_parse_phandle(np, "fsl,stop-mode", 0); 3852da722186SMartin Fuzzey if (!gpr_np) 3853da722186SMartin Fuzzey return 0; 3854da722186SMartin Fuzzey 38558a448bf8SFugang Duan ret = of_property_read_u32_array(np, "fsl,stop-mode", out_val, 38568a448bf8SFugang Duan ARRAY_SIZE(out_val)); 38578a448bf8SFugang Duan if (ret) { 38588a448bf8SFugang Duan dev_dbg(&fep->pdev->dev, "no stop mode property\n"); 3859d2b52ec0SYang Yingliang goto out; 38608a448bf8SFugang Duan } 38618a448bf8SFugang Duan 3862da722186SMartin Fuzzey fep->stop_gpr.gpr = syscon_node_to_regmap(gpr_np); 3863da722186SMartin Fuzzey if (IS_ERR(fep->stop_gpr.gpr)) { 3864da722186SMartin Fuzzey dev_err(&fep->pdev->dev, "could not find gpr regmap\n"); 3865da722186SMartin Fuzzey ret = PTR_ERR(fep->stop_gpr.gpr); 3866da722186SMartin Fuzzey fep->stop_gpr.gpr = NULL; 3867da722186SMartin Fuzzey goto out; 3868da722186SMartin Fuzzey } 3869da722186SMartin Fuzzey 38708a448bf8SFugang Duan fep->stop_gpr.reg = out_val[1]; 38718a448bf8SFugang Duan fep->stop_gpr.bit = out_val[2]; 3872da722186SMartin Fuzzey 3873da722186SMartin Fuzzey out: 3874da722186SMartin Fuzzey of_node_put(gpr_np); 3875da722186SMartin Fuzzey 3876da722186SMartin Fuzzey return ret; 3877da722186SMartin Fuzzey } 3878da722186SMartin Fuzzey 3879793fc096SFrank Li static int 3880793fc096SFrank Li fec_probe(struct platform_device *pdev) 3881793fc096SFrank Li { 3882793fc096SFrank Li struct fec_enet_private *fep; 3883793fc096SFrank Li struct fec_platform_data *pdata; 38840c65b2b9SAndrew Lunn phy_interface_t interface; 3885793fc096SFrank Li struct net_device *ndev; 3886793fc096SFrank Li int i, irq, ret = 0; 3887793fc096SFrank Li const struct of_device_id *of_id; 3888793fc096SFrank Li static int dev_id; 3889407066f8SUwe Kleine-König struct device_node *np = pdev->dev.of_node, *phy_node; 3890b7bd75cfSFrank Li int num_tx_qs; 3891b7bd75cfSFrank Li int num_rx_qs; 38924ad1ceecSTroy Kisky char irq_name[8]; 38934ad1ceecSTroy Kisky int irq_cnt; 3894da722186SMartin Fuzzey struct fec_devinfo *dev_info; 3895793fc096SFrank Li 38969fc095f1SFugang Duan fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs); 38979fc095f1SFugang Duan 3898793fc096SFrank Li /* Init network device */ 389980cca775SNikita Yushchenko ndev = alloc_etherdev_mqs(sizeof(struct fec_enet_private) + 3900f85de666SNikita Yushchenko FEC_STATS_SIZE, num_tx_qs, num_rx_qs); 3901793fc096SFrank Li if (!ndev) 3902793fc096SFrank Li return -ENOMEM; 3903793fc096SFrank Li 3904793fc096SFrank Li SET_NETDEV_DEV(ndev, &pdev->dev); 3905793fc096SFrank Li 3906793fc096SFrank Li /* setup board info structure */ 3907793fc096SFrank Li fep = netdev_priv(ndev); 3908793fc096SFrank Li 39096b7e4008SLothar Waßmann of_id = of_match_device(fec_dt_ids, &pdev->dev); 39106b7e4008SLothar Waßmann if (of_id) 39116b7e4008SLothar Waßmann pdev->id_entry = of_id->data; 3912da722186SMartin Fuzzey dev_info = (struct fec_devinfo *)pdev->id_entry->driver_data; 3913da722186SMartin Fuzzey if (dev_info) 3914da722186SMartin Fuzzey fep->quirks = dev_info->quirks; 39156b7e4008SLothar Waßmann 39160c818594SHubert Feurstein fep->netdev = ndev; 39179fc095f1SFugang Duan fep->num_rx_queues = num_rx_qs; 39189fc095f1SFugang Duan fep->num_tx_queues = num_tx_qs; 39199fc095f1SFugang Duan 3920d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 3921793fc096SFrank Li /* default enable pause frame auto negotiation */ 39226b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_GBIT) 3923793fc096SFrank Li fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG; 3924d1391930SGuenter Roeck #endif 3925793fc096SFrank Li 39265bbde4d2SNimrod Andy /* Select default pin state */ 39275bbde4d2SNimrod Andy pinctrl_pm_select_default_state(&pdev->dev); 39285bbde4d2SNimrod Andy 39294f830a5aSYueHaibing fep->hwp = devm_platform_ioremap_resource(pdev, 0); 3930941e173aSTushar Behera if (IS_ERR(fep->hwp)) { 3931941e173aSTushar Behera ret = PTR_ERR(fep->hwp); 3932941e173aSTushar Behera goto failed_ioremap; 3933941e173aSTushar Behera } 3934941e173aSTushar Behera 3935793fc096SFrank Li fep->pdev = pdev; 3936793fc096SFrank Li fep->dev_id = dev_id++; 3937793fc096SFrank Li 3938793fc096SFrank Li platform_set_drvdata(pdev, ndev); 3939793fc096SFrank Li 394029380905SLucas Stach if ((of_machine_is_compatible("fsl,imx6q") || 394129380905SLucas Stach of_machine_is_compatible("fsl,imx6dl")) && 394229380905SLucas Stach !of_property_read_bool(np, "fsl,err006687-workaround-present")) 394329380905SLucas Stach fep->quirks |= FEC_QUIRK_ERR006687; 394429380905SLucas Stach 394540c79ce1SWei Fang ret = fec_enet_ipc_handle_init(fep); 394640c79ce1SWei Fang if (ret) 394740c79ce1SWei Fang goto failed_ipc_init; 394840c79ce1SWei Fang 3949de40ed31SNimrod Andy if (of_get_property(np, "fsl,magic-packet", NULL)) 3950de40ed31SNimrod Andy fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET; 3951de40ed31SNimrod Andy 39528a448bf8SFugang Duan ret = fec_enet_init_stop_mode(fep, np); 3953da722186SMartin Fuzzey if (ret) 3954da722186SMartin Fuzzey goto failed_stop_mode; 3955da722186SMartin Fuzzey 3956407066f8SUwe Kleine-König phy_node = of_parse_phandle(np, "phy-handle", 0); 3957407066f8SUwe Kleine-König if (!phy_node && of_phy_is_fixed_link(np)) { 3958407066f8SUwe Kleine-König ret = of_phy_register_fixed_link(np); 3959407066f8SUwe Kleine-König if (ret < 0) { 3960407066f8SUwe Kleine-König dev_err(&pdev->dev, 3961407066f8SUwe Kleine-König "broken fixed-link specification\n"); 3962407066f8SUwe Kleine-König goto failed_phy; 3963407066f8SUwe Kleine-König } 3964407066f8SUwe Kleine-König phy_node = of_node_get(np); 3965407066f8SUwe Kleine-König } 3966407066f8SUwe Kleine-König fep->phy_node = phy_node; 3967407066f8SUwe Kleine-König 39680c65b2b9SAndrew Lunn ret = of_get_phy_mode(pdev->dev.of_node, &interface); 39690c65b2b9SAndrew Lunn if (ret) { 397094660ba0SJingoo Han pdata = dev_get_platdata(&pdev->dev); 3971793fc096SFrank Li if (pdata) 3972793fc096SFrank Li fep->phy_interface = pdata->phy; 3973793fc096SFrank Li else 3974793fc096SFrank Li fep->phy_interface = PHY_INTERFACE_MODE_MII; 3975793fc096SFrank Li } else { 39760c65b2b9SAndrew Lunn fep->phy_interface = interface; 3977793fc096SFrank Li } 3978793fc096SFrank Li 3979b820c114SJoakim Zhang ret = fec_enet_parse_rgmii_delay(fep, np); 3980b820c114SJoakim Zhang if (ret) 3981b820c114SJoakim Zhang goto failed_rgmii_delay; 3982b820c114SJoakim Zhang 3983793fc096SFrank Li fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); 3984793fc096SFrank Li if (IS_ERR(fep->clk_ipg)) { 3985793fc096SFrank Li ret = PTR_ERR(fep->clk_ipg); 3986793fc096SFrank Li goto failed_clk; 3987793fc096SFrank Li } 3988793fc096SFrank Li 3989793fc096SFrank Li fep->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); 3990793fc096SFrank Li if (IS_ERR(fep->clk_ahb)) { 3991793fc096SFrank Li ret = PTR_ERR(fep->clk_ahb); 3992793fc096SFrank Li goto failed_clk; 3993793fc096SFrank Li } 3994793fc096SFrank Li 3995d851b47bSFugang Duan fep->itr_clk_rate = clk_get_rate(fep->clk_ahb); 3996d851b47bSFugang Duan 399738f56f33SLinus Torvalds /* enet_out is optional, depends on board */ 39985ff851b7SUwe Kleine-König fep->clk_enet_out = devm_clk_get_optional(&pdev->dev, "enet_out"); 39995ff851b7SUwe Kleine-König if (IS_ERR(fep->clk_enet_out)) { 40005ff851b7SUwe Kleine-König ret = PTR_ERR(fep->clk_enet_out); 40015ff851b7SUwe Kleine-König goto failed_clk; 40025ff851b7SUwe Kleine-König } 400338f56f33SLinus Torvalds 400491c0d987SNimrod Andy fep->ptp_clk_on = false; 400501b825f9SFrancesco Dolcini mutex_init(&fep->ptp_clk_mutex); 40069b5330edSFugang Duan 40079b5330edSFugang Duan /* clk_ref is optional, depends on board */ 400843252ed1SUwe Kleine-König fep->clk_ref = devm_clk_get_optional(&pdev->dev, "enet_clk_ref"); 400943252ed1SUwe Kleine-König if (IS_ERR(fep->clk_ref)) { 401043252ed1SUwe Kleine-König ret = PTR_ERR(fep->clk_ref); 401143252ed1SUwe Kleine-König goto failed_clk; 401243252ed1SUwe Kleine-König } 4013b82f8c3fSFugang Duan fep->clk_ref_rate = clk_get_rate(fep->clk_ref); 40149b5330edSFugang Duan 4015fc539459SFugang Duan /* clk_2x_txclk is optional, depends on board */ 4016b820c114SJoakim Zhang if (fep->rgmii_txc_dly || fep->rgmii_rxc_dly) { 4017fc539459SFugang Duan fep->clk_2x_txclk = devm_clk_get(&pdev->dev, "enet_2x_txclk"); 4018fc539459SFugang Duan if (IS_ERR(fep->clk_2x_txclk)) 4019fc539459SFugang Duan fep->clk_2x_txclk = NULL; 4020b820c114SJoakim Zhang } 4021fc539459SFugang Duan 40226b7e4008SLothar Waßmann fep->bufdesc_ex = fep->quirks & FEC_QUIRK_HAS_BUFDESC_EX; 4023793fc096SFrank Li fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp"); 4024793fc096SFrank Li if (IS_ERR(fep->clk_ptp)) { 402538f56f33SLinus Torvalds fep->clk_ptp = NULL; 4026217b5844SLothar Waßmann fep->bufdesc_ex = false; 4027793fc096SFrank Li } 4028793fc096SFrank Li 4029e8fcfcd5SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 403013a097bdSFabio Estevam if (ret) 403113a097bdSFabio Estevam goto failed_clk; 403213a097bdSFabio Estevam 40338fff755eSAndrew Lunn ret = clk_prepare_enable(fep->clk_ipg); 40348fff755eSAndrew Lunn if (ret) 40358fff755eSAndrew Lunn goto failed_clk_ipg; 4036d7c3a206SAndy Duan ret = clk_prepare_enable(fep->clk_ahb); 4037d7c3a206SAndy Duan if (ret) 4038d7c3a206SAndy Duan goto failed_clk_ahb; 40398fff755eSAndrew Lunn 404025974d8aSStefan Agner fep->reg_phy = devm_regulator_get_optional(&pdev->dev, "phy"); 4041f4e9f3d2SFabio Estevam if (!IS_ERR(fep->reg_phy)) { 4042f4e9f3d2SFabio Estevam ret = regulator_enable(fep->reg_phy); 4043793fc096SFrank Li if (ret) { 4044793fc096SFrank Li dev_err(&pdev->dev, 4045793fc096SFrank Li "Failed to enable phy regulator: %d\n", ret); 4046793fc096SFrank Li goto failed_regulator; 4047793fc096SFrank Li } 4048f6a4d607SFabio Estevam } else { 40493f38c683SFugang Duan if (PTR_ERR(fep->reg_phy) == -EPROBE_DEFER) { 40503f38c683SFugang Duan ret = -EPROBE_DEFER; 40513f38c683SFugang Duan goto failed_regulator; 40523f38c683SFugang Duan } 4053f6a4d607SFabio Estevam fep->reg_phy = NULL; 4054793fc096SFrank Li } 4055793fc096SFrank Li 40568fff755eSAndrew Lunn pm_runtime_set_autosuspend_delay(&pdev->dev, FEC_MDIO_PM_TIMEOUT); 40578fff755eSAndrew Lunn pm_runtime_use_autosuspend(&pdev->dev); 405814d2b7c1SLucas Stach pm_runtime_get_noresume(&pdev->dev); 40598fff755eSAndrew Lunn pm_runtime_set_active(&pdev->dev); 40608fff755eSAndrew Lunn pm_runtime_enable(&pdev->dev); 40618fff755eSAndrew Lunn 40629269e556SFugang Duan ret = fec_reset_phy(pdev); 40639269e556SFugang Duan if (ret) 40649269e556SFugang Duan goto failed_reset; 4065793fc096SFrank Li 40664ad1ceecSTroy Kisky irq_cnt = fec_enet_get_irq_cnt(pdev); 4067793fc096SFrank Li if (fep->bufdesc_ex) 40684ad1ceecSTroy Kisky fec_ptp_init(pdev, irq_cnt); 4069793fc096SFrank Li 4070793fc096SFrank Li ret = fec_enet_init(ndev); 4071793fc096SFrank Li if (ret) 4072793fc096SFrank Li goto failed_init; 4073793fc096SFrank Li 40744ad1ceecSTroy Kisky for (i = 0; i < irq_cnt; i++) { 40753ded9f2bSArnd Bergmann snprintf(irq_name, sizeof(irq_name), "int%d", i); 40763b56be21SAnson Huang irq = platform_get_irq_byname_optional(pdev, irq_name); 40774ad1ceecSTroy Kisky if (irq < 0) 4078793fc096SFrank Li irq = platform_get_irq(pdev, i); 4079793fc096SFrank Li if (irq < 0) { 4080793fc096SFrank Li ret = irq; 4081793fc096SFrank Li goto failed_irq; 4082793fc096SFrank Li } 40830d9b2ab1SFabio Estevam ret = devm_request_irq(&pdev->dev, irq, fec_enet_interrupt, 408444a272ddSMichael Opdenacker 0, pdev->name, ndev); 40850d9b2ab1SFabio Estevam if (ret) 4086793fc096SFrank Li goto failed_irq; 4087de40ed31SNimrod Andy 4088de40ed31SNimrod Andy fep->irq[i] = irq; 4089793fc096SFrank Li } 4090793fc096SFrank Li 4091b7cdc965SJoakim Zhang /* Decide which interrupt line is wakeup capable */ 4092b7cdc965SJoakim Zhang fec_enet_get_wakeup_irq(pdev); 4093b7cdc965SJoakim Zhang 4094793fc096SFrank Li ret = fec_enet_mii_init(pdev); 4095793fc096SFrank Li if (ret) 4096793fc096SFrank Li goto failed_mii_init; 4097793fc096SFrank Li 4098793fc096SFrank Li /* Carrier starts down, phylib will bring it up */ 4099793fc096SFrank Li netif_carrier_off(ndev); 4100e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 41015bbde4d2SNimrod Andy pinctrl_pm_select_sleep_state(&pdev->dev); 4102793fc096SFrank Li 410359193053SAndrew Lunn ndev->max_mtu = PKT_MAXBUF_SIZE - ETH_HLEN - ETH_FCS_LEN; 410459193053SAndrew Lunn 4105793fc096SFrank Li ret = register_netdev(ndev); 4106793fc096SFrank Li if (ret) 4107793fc096SFrank Li goto failed_register; 4108793fc096SFrank Li 4109de40ed31SNimrod Andy device_init_wakeup(&ndev->dev, fep->wol_flag & 4110de40ed31SNimrod Andy FEC_WOL_HAS_MAGIC_PACKET); 4111de40ed31SNimrod Andy 4112eb1d0640SFabio Estevam if (fep->bufdesc_ex && fep->ptp_clock) 4113eb1d0640SFabio Estevam netdev_info(ndev, "registered PHC device %d\n", fep->dev_id); 4114eb1d0640SFabio Estevam 41151b7bde6dSNimrod Andy fep->rx_copybreak = COPYBREAK_DEFAULT; 411636cdc743SRussell King INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work); 41178fff755eSAndrew Lunn 41188fff755eSAndrew Lunn pm_runtime_mark_last_busy(&pdev->dev); 41198fff755eSAndrew Lunn pm_runtime_put_autosuspend(&pdev->dev); 41208fff755eSAndrew Lunn 4121793fc096SFrank Li return 0; 4122793fc096SFrank Li 4123793fc096SFrank Li failed_register: 4124793fc096SFrank Li fec_enet_mii_remove(fep); 4125793fc096SFrank Li failed_mii_init: 41267a2bbd8dSFabio Estevam failed_irq: 41277a2bbd8dSFabio Estevam failed_init: 412832cba57bSLucas Stach fec_ptp_stop(pdev); 41299269e556SFugang Duan failed_reset: 4130ce8d24f9SAndy Duan pm_runtime_put_noidle(&pdev->dev); 41319269e556SFugang Duan pm_runtime_disable(&pdev->dev); 4132c6165cf0SFugang Duan if (fep->reg_phy) 4133c6165cf0SFugang Duan regulator_disable(fep->reg_phy); 4134793fc096SFrank Li failed_regulator: 4135d7c3a206SAndy Duan clk_disable_unprepare(fep->clk_ahb); 4136d7c3a206SAndy Duan failed_clk_ahb: 4137d7c3a206SAndy Duan clk_disable_unprepare(fep->clk_ipg); 41388fff755eSAndrew Lunn failed_clk_ipg: 4139e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 4140793fc096SFrank Li failed_clk: 4141b820c114SJoakim Zhang failed_rgmii_delay: 414282005b1cSJohan Hovold if (of_phy_is_fixed_link(np)) 414382005b1cSJohan Hovold of_phy_deregister_fixed_link(np); 4144407066f8SUwe Kleine-König of_node_put(phy_node); 4145da722186SMartin Fuzzey failed_stop_mode: 414640c79ce1SWei Fang failed_ipc_init: 4147d1616f07SFugang Duan failed_phy: 4148d1616f07SFugang Duan dev_id--; 4149793fc096SFrank Li failed_ioremap: 4150793fc096SFrank Li free_netdev(ndev); 4151793fc096SFrank Li 4152793fc096SFrank Li return ret; 4153793fc096SFrank Li } 4154793fc096SFrank Li 4155793fc096SFrank Li static int 4156793fc096SFrank Li fec_drv_remove(struct platform_device *pdev) 4157793fc096SFrank Li { 4158793fc096SFrank Li struct net_device *ndev = platform_get_drvdata(pdev); 4159793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 416082005b1cSJohan Hovold struct device_node *np = pdev->dev.of_node; 4161a31eda65SChuhong Yuan int ret; 4162a31eda65SChuhong Yuan 4163da875fa5SZhang Qilong ret = pm_runtime_resume_and_get(&pdev->dev); 4164a31eda65SChuhong Yuan if (ret < 0) 4165a31eda65SChuhong Yuan return ret; 4166793fc096SFrank Li 416736cdc743SRussell King cancel_work_sync(&fep->tx_timeout_work); 416832cba57bSLucas Stach fec_ptp_stop(pdev); 4169793fc096SFrank Li unregister_netdev(ndev); 4170793fc096SFrank Li fec_enet_mii_remove(fep); 4171f6a4d607SFabio Estevam if (fep->reg_phy) 4172f6a4d607SFabio Estevam regulator_disable(fep->reg_phy); 4173a31eda65SChuhong Yuan 417482005b1cSJohan Hovold if (of_phy_is_fixed_link(np)) 417582005b1cSJohan Hovold of_phy_deregister_fixed_link(np); 4176407066f8SUwe Kleine-König of_node_put(fep->phy_node); 4177793fc096SFrank Li 4178a31eda65SChuhong Yuan clk_disable_unprepare(fep->clk_ahb); 4179a31eda65SChuhong Yuan clk_disable_unprepare(fep->clk_ipg); 4180a31eda65SChuhong Yuan pm_runtime_put_noidle(&pdev->dev); 4181a31eda65SChuhong Yuan pm_runtime_disable(&pdev->dev); 4182a31eda65SChuhong Yuan 418344712965SPavel Skripkin free_netdev(ndev); 4184793fc096SFrank Li return 0; 4185793fc096SFrank Li } 4186793fc096SFrank Li 4187dd66d386SFabio Estevam static int __maybe_unused fec_suspend(struct device *dev) 4188793fc096SFrank Li { 4189793fc096SFrank Li struct net_device *ndev = dev_get_drvdata(dev); 4190793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 4191da970726SWei Fang int ret; 4192793fc096SFrank Li 4193da1774e5SRussell King rtnl_lock(); 4194793fc096SFrank Li if (netif_running(ndev)) { 4195de40ed31SNimrod Andy if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) 4196de40ed31SNimrod Andy fep->wol_flag |= FEC_WOL_FLAG_SLEEP_ON; 419745f5c327SPhilippe Reynes phy_stop(ndev->phydev); 419831a6de34SRussell King napi_disable(&fep->napi); 419931a6de34SRussell King netif_tx_lock_bh(ndev); 4200793fc096SFrank Li netif_device_detach(ndev); 420131a6de34SRussell King netif_tx_unlock_bh(ndev); 420231a6de34SRussell King fec_stop(ndev); 42030b6f65c7SJoakim Zhang if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) { 42040b6f65c7SJoakim Zhang fec_irqs_disable(ndev); 42055bbde4d2SNimrod Andy pinctrl_pm_select_sleep_state(&fep->pdev->dev); 42060b6f65c7SJoakim Zhang } else { 42070b6f65c7SJoakim Zhang fec_irqs_disable_except_wakeup(ndev); 42080b6f65c7SJoakim Zhang if (fep->wake_irq > 0) { 42090b6f65c7SJoakim Zhang disable_irq(fep->wake_irq); 42100b6f65c7SJoakim Zhang enable_irq_wake(fep->wake_irq); 42110b6f65c7SJoakim Zhang } 42120b6f65c7SJoakim Zhang fec_enet_stop_mode(fep, true); 42130b6f65c7SJoakim Zhang } 42140b6f65c7SJoakim Zhang /* It's safe to disable clocks since interrupts are masked */ 42150b6f65c7SJoakim Zhang fec_enet_clk_enable(ndev, false); 4216da970726SWei Fang 4217da970726SWei Fang fep->rpm_active = !pm_runtime_status_suspended(dev); 4218da970726SWei Fang if (fep->rpm_active) { 4219da970726SWei Fang ret = pm_runtime_force_suspend(dev); 4220da970726SWei Fang if (ret < 0) { 4221da970726SWei Fang rtnl_unlock(); 4222da970726SWei Fang return ret; 4223da970726SWei Fang } 4224da970726SWei Fang } 4225f4c4a4e0SNimrod Andy } 4226f4c4a4e0SNimrod Andy rtnl_unlock(); 4227793fc096SFrank Li 4228de40ed31SNimrod Andy if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) 4229238f7bc7SFabio Estevam regulator_disable(fep->reg_phy); 4230238f7bc7SFabio Estevam 4231858eeb7dSNimrod Andy /* SOC supply clock to phy, when clock is disabled, phy link down 4232858eeb7dSNimrod Andy * SOC control phy regulator, when regulator is disabled, phy link down 4233858eeb7dSNimrod Andy */ 4234858eeb7dSNimrod Andy if (fep->clk_enet_out || fep->reg_phy) 4235858eeb7dSNimrod Andy fep->link = 0; 4236858eeb7dSNimrod Andy 4237793fc096SFrank Li return 0; 4238793fc096SFrank Li } 4239793fc096SFrank Li 4240dd66d386SFabio Estevam static int __maybe_unused fec_resume(struct device *dev) 4241793fc096SFrank Li { 4242793fc096SFrank Li struct net_device *ndev = dev_get_drvdata(dev); 4243793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 4244238f7bc7SFabio Estevam int ret; 4245de40ed31SNimrod Andy int val; 4246238f7bc7SFabio Estevam 4247de40ed31SNimrod Andy if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) { 4248238f7bc7SFabio Estevam ret = regulator_enable(fep->reg_phy); 4249238f7bc7SFabio Estevam if (ret) 4250238f7bc7SFabio Estevam return ret; 4251238f7bc7SFabio Estevam } 4252793fc096SFrank Li 4253da1774e5SRussell King rtnl_lock(); 4254793fc096SFrank Li if (netif_running(ndev)) { 4255da970726SWei Fang if (fep->rpm_active) 4256da970726SWei Fang pm_runtime_force_resume(dev); 4257da970726SWei Fang 4258f4c4a4e0SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 4259f4c4a4e0SNimrod Andy if (ret) { 4260f4c4a4e0SNimrod Andy rtnl_unlock(); 4261f4c4a4e0SNimrod Andy goto failed_clk; 4262f4c4a4e0SNimrod Andy } 4263de40ed31SNimrod Andy if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) { 4264da722186SMartin Fuzzey fec_enet_stop_mode(fep, false); 42650b6f65c7SJoakim Zhang if (fep->wake_irq) { 42660b6f65c7SJoakim Zhang disable_irq_wake(fep->wake_irq); 42670b6f65c7SJoakim Zhang enable_irq(fep->wake_irq); 42680b6f65c7SJoakim Zhang } 4269da722186SMartin Fuzzey 4270de40ed31SNimrod Andy val = readl(fep->hwp + FEC_ECNTRL); 4271de40ed31SNimrod Andy val &= ~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP); 4272de40ed31SNimrod Andy writel(val, fep->hwp + FEC_ECNTRL); 4273de40ed31SNimrod Andy fep->wol_flag &= ~FEC_WOL_FLAG_SLEEP_ON; 4274de40ed31SNimrod Andy } else { 4275de40ed31SNimrod Andy pinctrl_pm_select_default_state(&fep->pdev->dev); 4276de40ed31SNimrod Andy } 4277ef83337dSRussell King fec_restart(ndev); 427831a6de34SRussell King netif_tx_lock_bh(ndev); 4279793fc096SFrank Li netif_device_attach(ndev); 42806af42d42SRussell King netif_tx_unlock_bh(ndev); 42816af42d42SRussell King napi_enable(&fep->napi); 4282557d5dc8SHeiner Kallweit phy_init_hw(ndev->phydev); 428345f5c327SPhilippe Reynes phy_start(ndev->phydev); 4284793fc096SFrank Li } 4285da1774e5SRussell King rtnl_unlock(); 4286793fc096SFrank Li 4287793fc096SFrank Li return 0; 428813a097bdSFabio Estevam 4289e8fcfcd5SNimrod Andy failed_clk: 429013a097bdSFabio Estevam if (fep->reg_phy) 429113a097bdSFabio Estevam regulator_disable(fep->reg_phy); 429213a097bdSFabio Estevam return ret; 4293793fc096SFrank Li } 4294793fc096SFrank Li 42958fff755eSAndrew Lunn static int __maybe_unused fec_runtime_suspend(struct device *dev) 42968fff755eSAndrew Lunn { 42978fff755eSAndrew Lunn struct net_device *ndev = dev_get_drvdata(dev); 42988fff755eSAndrew Lunn struct fec_enet_private *fep = netdev_priv(ndev); 42998fff755eSAndrew Lunn 4300d7c3a206SAndy Duan clk_disable_unprepare(fep->clk_ahb); 43018fff755eSAndrew Lunn clk_disable_unprepare(fep->clk_ipg); 43028fff755eSAndrew Lunn 43038fff755eSAndrew Lunn return 0; 43048fff755eSAndrew Lunn } 43058fff755eSAndrew Lunn 43068fff755eSAndrew Lunn static int __maybe_unused fec_runtime_resume(struct device *dev) 43078fff755eSAndrew Lunn { 43088fff755eSAndrew Lunn struct net_device *ndev = dev_get_drvdata(dev); 43098fff755eSAndrew Lunn struct fec_enet_private *fep = netdev_priv(ndev); 4310d7c3a206SAndy Duan int ret; 43118fff755eSAndrew Lunn 4312d7c3a206SAndy Duan ret = clk_prepare_enable(fep->clk_ahb); 4313d7c3a206SAndy Duan if (ret) 4314d7c3a206SAndy Duan return ret; 4315d7c3a206SAndy Duan ret = clk_prepare_enable(fep->clk_ipg); 4316d7c3a206SAndy Duan if (ret) 4317d7c3a206SAndy Duan goto failed_clk_ipg; 4318d7c3a206SAndy Duan 4319d7c3a206SAndy Duan return 0; 4320d7c3a206SAndy Duan 4321d7c3a206SAndy Duan failed_clk_ipg: 4322d7c3a206SAndy Duan clk_disable_unprepare(fep->clk_ahb); 4323d7c3a206SAndy Duan return ret; 43248fff755eSAndrew Lunn } 43258fff755eSAndrew Lunn 43268fff755eSAndrew Lunn static const struct dev_pm_ops fec_pm_ops = { 43278fff755eSAndrew Lunn SET_SYSTEM_SLEEP_PM_OPS(fec_suspend, fec_resume) 43288fff755eSAndrew Lunn SET_RUNTIME_PM_OPS(fec_runtime_suspend, fec_runtime_resume, NULL) 43298fff755eSAndrew Lunn }; 4330793fc096SFrank Li 4331793fc096SFrank Li static struct platform_driver fec_driver = { 4332793fc096SFrank Li .driver = { 4333793fc096SFrank Li .name = DRIVER_NAME, 4334793fc096SFrank Li .pm = &fec_pm_ops, 4335793fc096SFrank Li .of_match_table = fec_dt_ids, 4336272bb0e9SFabio Estevam .suppress_bind_attrs = true, 4337793fc096SFrank Li }, 4338793fc096SFrank Li .id_table = fec_devtype, 4339793fc096SFrank Li .probe = fec_probe, 4340793fc096SFrank Li .remove = fec_drv_remove, 4341793fc096SFrank Li }; 4342793fc096SFrank Li 4343793fc096SFrank Li module_platform_driver(fec_driver); 4344793fc096SFrank Li 4345793fc096SFrank Li MODULE_LICENSE("GPL"); 4346