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> 69793fc096SFrank Li 70793fc096SFrank Li #include <asm/cacheflush.h> 71793fc096SFrank Li 72793fc096SFrank Li #include "fec.h" 73793fc096SFrank Li 74772e42b0SChristoph Müllner static void set_multicast_list(struct net_device *ndev); 75d851b47bSFugang Duan static void fec_enet_itr_coal_init(struct net_device *ndev); 76772e42b0SChristoph Müllner 77793fc096SFrank Li #define DRIVER_NAME "fec" 78793fc096SFrank Li 7952c4a1a8SFugang Duan static const u16 fec_enet_vlan_pri_to_queue[8] = {0, 0, 1, 1, 1, 2, 2, 2}; 8052c4a1a8SFugang Duan 81793fc096SFrank Li /* Pause frame feild and FIFO threshold */ 82793fc096SFrank Li #define FEC_ENET_FCE (1 << 5) 83793fc096SFrank Li #define FEC_ENET_RSEM_V 0x84 84793fc096SFrank Li #define FEC_ENET_RSFL_V 16 85793fc096SFrank Li #define FEC_ENET_RAEM_V 0x8 86793fc096SFrank Li #define FEC_ENET_RAFL_V 0x8 87793fc096SFrank Li #define FEC_ENET_OPD_V 0xFFF0 888fff755eSAndrew Lunn #define FEC_MDIO_PM_TIMEOUT 100 /* ms */ 89793fc096SFrank Li 90da722186SMartin Fuzzey struct fec_devinfo { 91da722186SMartin Fuzzey u32 quirks; 92da722186SMartin Fuzzey }; 93da722186SMartin Fuzzey 94da722186SMartin Fuzzey static const struct fec_devinfo fec_imx25_info = { 95da722186SMartin Fuzzey .quirks = FEC_QUIRK_USE_GASKET | FEC_QUIRK_MIB_CLEAR | 96da722186SMartin Fuzzey FEC_QUIRK_HAS_FRREG, 97da722186SMartin Fuzzey }; 98da722186SMartin Fuzzey 99da722186SMartin Fuzzey static const struct fec_devinfo fec_imx27_info = { 100da722186SMartin Fuzzey .quirks = FEC_QUIRK_MIB_CLEAR | FEC_QUIRK_HAS_FRREG, 101da722186SMartin Fuzzey }; 102da722186SMartin Fuzzey 103da722186SMartin Fuzzey static const struct fec_devinfo fec_imx28_info = { 104da722186SMartin Fuzzey .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME | 105da722186SMartin Fuzzey FEC_QUIRK_SINGLE_MDIO | FEC_QUIRK_HAS_RACC | 106c730ab42SLaurent Badel FEC_QUIRK_HAS_FRREG | FEC_QUIRK_CLEAR_SETUP_MII | 107c730ab42SLaurent Badel FEC_QUIRK_NO_HARD_RESET, 108da722186SMartin Fuzzey }; 109da722186SMartin Fuzzey 110da722186SMartin Fuzzey static const struct fec_devinfo fec_imx6q_info = { 111da722186SMartin Fuzzey .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 112da722186SMartin Fuzzey FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 113da722186SMartin Fuzzey FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR006358 | 1141e6114f5SGreg Ungerer FEC_QUIRK_HAS_RACC | FEC_QUIRK_CLEAR_SETUP_MII, 115da722186SMartin Fuzzey }; 116da722186SMartin Fuzzey 117da722186SMartin Fuzzey static const struct fec_devinfo fec_mvf600_info = { 118da722186SMartin Fuzzey .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_RACC, 119da722186SMartin Fuzzey }; 120da722186SMartin Fuzzey 121da722186SMartin Fuzzey static const struct fec_devinfo fec_imx6x_info = { 122da722186SMartin Fuzzey .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 123da722186SMartin Fuzzey FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 124da722186SMartin Fuzzey FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | 125da722186SMartin Fuzzey FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | 1261e6114f5SGreg Ungerer FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE | 127471ff445SJoakim Zhang FEC_QUIRK_CLEAR_SETUP_MII | FEC_QUIRK_HAS_MULTI_QUEUES, 128da722186SMartin Fuzzey }; 129da722186SMartin Fuzzey 130da722186SMartin Fuzzey static const struct fec_devinfo fec_imx6ul_info = { 131da722186SMartin Fuzzey .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 132da722186SMartin Fuzzey FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 133da722186SMartin Fuzzey FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR007885 | 134da722186SMartin Fuzzey FEC_QUIRK_BUG_CAPTURE | FEC_QUIRK_HAS_RACC | 1351e6114f5SGreg Ungerer FEC_QUIRK_HAS_COALESCE | FEC_QUIRK_CLEAR_SETUP_MII, 136da722186SMartin Fuzzey }; 137da722186SMartin Fuzzey 138947240ebSFugang Duan static const struct fec_devinfo fec_imx8mq_info = { 139947240ebSFugang Duan .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 140947240ebSFugang Duan FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 141947240ebSFugang Duan FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | 142947240ebSFugang Duan FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | 143947240ebSFugang Duan FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE | 144947240ebSFugang Duan FEC_QUIRK_CLEAR_SETUP_MII | FEC_QUIRK_HAS_MULTI_QUEUES | 145b7cdc965SJoakim Zhang FEC_QUIRK_HAS_EEE | FEC_QUIRK_WAKEUP_FROM_INT2, 146947240ebSFugang Duan }; 147947240ebSFugang Duan 148947240ebSFugang Duan static const struct fec_devinfo fec_imx8qm_info = { 149947240ebSFugang Duan .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 150947240ebSFugang Duan FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 151947240ebSFugang Duan FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | 152947240ebSFugang Duan FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | 153947240ebSFugang Duan FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE | 154947240ebSFugang Duan FEC_QUIRK_CLEAR_SETUP_MII | FEC_QUIRK_HAS_MULTI_QUEUES | 155947240ebSFugang Duan FEC_QUIRK_DELAYED_CLKS_SUPPORT, 156947240ebSFugang Duan }; 157947240ebSFugang Duan 158793fc096SFrank Li static struct platform_device_id fec_devtype[] = { 159793fc096SFrank Li { 160793fc096SFrank Li /* keep it for coldfire */ 161793fc096SFrank Li .name = DRIVER_NAME, 162793fc096SFrank Li .driver_data = 0, 163793fc096SFrank Li }, { 164793fc096SFrank Li .name = "imx25-fec", 165da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_imx25_info, 166793fc096SFrank Li }, { 167793fc096SFrank Li .name = "imx27-fec", 168da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_imx27_info, 169793fc096SFrank Li }, { 170793fc096SFrank Li .name = "imx28-fec", 171da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_imx28_info, 172793fc096SFrank Li }, { 173793fc096SFrank Li .name = "imx6q-fec", 174da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_imx6q_info, 175793fc096SFrank Li }, { 17636803542SShawn Guo .name = "mvf600-fec", 177da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_mvf600_info, 178ca7c4a45SJingchang Lu }, { 17995a77470SFugang Duan .name = "imx6sx-fec", 180da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_imx6x_info, 18195a77470SFugang Duan }, { 182a51d3ab5SFugang Duan .name = "imx6ul-fec", 183da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_imx6ul_info, 184a51d3ab5SFugang Duan }, { 185947240ebSFugang Duan .name = "imx8mq-fec", 186947240ebSFugang Duan .driver_data = (kernel_ulong_t)&fec_imx8mq_info, 187947240ebSFugang Duan }, { 188947240ebSFugang Duan .name = "imx8qm-fec", 189947240ebSFugang Duan .driver_data = (kernel_ulong_t)&fec_imx8qm_info, 190947240ebSFugang Duan }, { 191793fc096SFrank Li /* sentinel */ 192793fc096SFrank Li } 193793fc096SFrank Li }; 194793fc096SFrank Li MODULE_DEVICE_TABLE(platform, fec_devtype); 195793fc096SFrank Li 196793fc096SFrank Li enum imx_fec_type { 197793fc096SFrank Li IMX25_FEC = 1, /* runs on i.mx25/50/53 */ 198793fc096SFrank Li IMX27_FEC, /* runs on i.mx27/35/51 */ 199793fc096SFrank Li IMX28_FEC, 200793fc096SFrank Li IMX6Q_FEC, 20136803542SShawn Guo MVF600_FEC, 202ba593e00SFugang Duan IMX6SX_FEC, 203a51d3ab5SFugang Duan IMX6UL_FEC, 204947240ebSFugang Duan IMX8MQ_FEC, 205947240ebSFugang Duan IMX8QM_FEC, 206793fc096SFrank Li }; 207793fc096SFrank Li 208793fc096SFrank Li static const struct of_device_id fec_dt_ids[] = { 209793fc096SFrank Li { .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], }, 210793fc096SFrank Li { .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], }, 211793fc096SFrank Li { .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], }, 212793fc096SFrank Li { .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], }, 21336803542SShawn Guo { .compatible = "fsl,mvf600-fec", .data = &fec_devtype[MVF600_FEC], }, 214ba593e00SFugang Duan { .compatible = "fsl,imx6sx-fec", .data = &fec_devtype[IMX6SX_FEC], }, 215a51d3ab5SFugang Duan { .compatible = "fsl,imx6ul-fec", .data = &fec_devtype[IMX6UL_FEC], }, 216947240ebSFugang Duan { .compatible = "fsl,imx8mq-fec", .data = &fec_devtype[IMX8MQ_FEC], }, 217947240ebSFugang Duan { .compatible = "fsl,imx8qm-fec", .data = &fec_devtype[IMX8QM_FEC], }, 218793fc096SFrank Li { /* sentinel */ } 219793fc096SFrank Li }; 220793fc096SFrank Li MODULE_DEVICE_TABLE(of, fec_dt_ids); 221793fc096SFrank Li 222793fc096SFrank Li static unsigned char macaddr[ETH_ALEN]; 223793fc096SFrank Li module_param_array(macaddr, byte, NULL, 0); 224793fc096SFrank Li MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); 225793fc096SFrank Li 226793fc096SFrank Li #if defined(CONFIG_M5272) 227793fc096SFrank Li /* 228793fc096SFrank Li * Some hardware gets it MAC address out of local flash memory. 229793fc096SFrank Li * if this is non-zero then assume it is the address to get MAC from. 230793fc096SFrank Li */ 231793fc096SFrank Li #if defined(CONFIG_NETtel) 232793fc096SFrank Li #define FEC_FLASHMAC 0xf0006006 233793fc096SFrank Li #elif defined(CONFIG_GILBARCONAP) || defined(CONFIG_SCALES) 234793fc096SFrank Li #define FEC_FLASHMAC 0xf0006000 235793fc096SFrank Li #elif defined(CONFIG_CANCam) 236793fc096SFrank Li #define FEC_FLASHMAC 0xf0020000 237793fc096SFrank Li #elif defined (CONFIG_M5272C3) 238793fc096SFrank Li #define FEC_FLASHMAC (0xffe04000 + 4) 239793fc096SFrank Li #elif defined(CONFIG_MOD5272) 240793fc096SFrank Li #define FEC_FLASHMAC 0xffc0406b 241793fc096SFrank Li #else 242793fc096SFrank Li #define FEC_FLASHMAC 0 243793fc096SFrank Li #endif 244793fc096SFrank Li #endif /* CONFIG_M5272 */ 245793fc096SFrank Li 246cdffcf1bSJim Baxter /* The FEC stores dest/src/type/vlan, data, and checksum for receive packets. 247fbbeefddSAndrew Lunn * 248fbbeefddSAndrew Lunn * 2048 byte skbufs are allocated. However, alignment requirements 249fbbeefddSAndrew Lunn * varies between FEC variants. Worst case is 64, so round down by 64. 250793fc096SFrank Li */ 251fbbeefddSAndrew Lunn #define PKT_MAXBUF_SIZE (round_down(2048 - 64, 64)) 252793fc096SFrank Li #define PKT_MINBUF_SIZE 64 253793fc096SFrank Li 2544c09eed9SJim Baxter /* FEC receive acceleration */ 2554c09eed9SJim Baxter #define FEC_RACC_IPDIS (1 << 1) 2564c09eed9SJim Baxter #define FEC_RACC_PRODIS (1 << 2) 2573ac72b7bSEric Nelson #define FEC_RACC_SHIFT16 BIT(7) 2584c09eed9SJim Baxter #define FEC_RACC_OPTIONS (FEC_RACC_IPDIS | FEC_RACC_PRODIS) 2594c09eed9SJim Baxter 2602b30842bSAndrew Lunn /* MIB Control Register */ 2612b30842bSAndrew Lunn #define FEC_MIB_CTRLSTAT_DISABLE BIT(31) 2622b30842bSAndrew Lunn 263793fc096SFrank Li /* 264793fc096SFrank Li * The 5270/5271/5280/5282/532x RX control register also contains maximum frame 265793fc096SFrank Li * size bits. Other FEC hardware does not, so we need to take that into 266793fc096SFrank Li * account when setting it. 267793fc096SFrank Li */ 268793fc096SFrank Li #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ 2693f1dcc6aSLucas Stach defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ 2703f1dcc6aSLucas Stach defined(CONFIG_ARM64) 271793fc096SFrank Li #define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16) 272793fc096SFrank Li #else 273793fc096SFrank Li #define OPT_FRAME_SIZE 0 274793fc096SFrank Li #endif 275793fc096SFrank Li 276793fc096SFrank Li /* FEC MII MMFR bits definition */ 277793fc096SFrank Li #define FEC_MMFR_ST (1 << 30) 278d3ee8ec7SMarco Hartmann #define FEC_MMFR_ST_C45 (0) 279793fc096SFrank Li #define FEC_MMFR_OP_READ (2 << 28) 280d3ee8ec7SMarco Hartmann #define FEC_MMFR_OP_READ_C45 (3 << 28) 281793fc096SFrank Li #define FEC_MMFR_OP_WRITE (1 << 28) 282d3ee8ec7SMarco Hartmann #define FEC_MMFR_OP_ADDR_WRITE (0) 283793fc096SFrank Li #define FEC_MMFR_PA(v) ((v & 0x1f) << 23) 284793fc096SFrank Li #define FEC_MMFR_RA(v) ((v & 0x1f) << 18) 285793fc096SFrank Li #define FEC_MMFR_TA (2 << 16) 286793fc096SFrank Li #define FEC_MMFR_DATA(v) (v & 0xffff) 287de40ed31SNimrod Andy /* FEC ECR bits definition */ 288f7995922SCsókás Bence #define FEC_ECR_RESET BIT(0) 289f7995922SCsókás Bence #define FEC_ECR_ETHEREN BIT(1) 290f7995922SCsókás Bence #define FEC_ECR_MAGICEN BIT(2) 291f7995922SCsókás Bence #define FEC_ECR_SLEEP BIT(3) 292f7995922SCsókás Bence #define FEC_ECR_EN1588 BIT(4) 293793fc096SFrank Li 294793fc096SFrank Li #define FEC_MII_TIMEOUT 30000 /* us */ 295793fc096SFrank Li 296793fc096SFrank Li /* Transmitter timeout */ 297793fc096SFrank Li #define TX_TIMEOUT (2 * HZ) 298793fc096SFrank Li 299793fc096SFrank Li #define FEC_PAUSE_FLAG_AUTONEG 0x1 300793fc096SFrank Li #define FEC_PAUSE_FLAG_ENABLE 0x2 301de40ed31SNimrod Andy #define FEC_WOL_HAS_MAGIC_PACKET (0x1 << 0) 302de40ed31SNimrod Andy #define FEC_WOL_FLAG_ENABLE (0x1 << 1) 303de40ed31SNimrod Andy #define FEC_WOL_FLAG_SLEEP_ON (0x1 << 2) 304793fc096SFrank Li 3051b7bde6dSNimrod Andy #define COPYBREAK_DEFAULT 256 3061b7bde6dSNimrod Andy 30779f33912SNimrod Andy /* Max number of allowed TCP segments for software TSO */ 30879f33912SNimrod Andy #define FEC_MAX_TSO_SEGS 100 30979f33912SNimrod Andy #define FEC_MAX_SKB_DESCS (FEC_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS) 31079f33912SNimrod Andy 31179f33912SNimrod Andy #define IS_TSO_HEADER(txq, addr) \ 31279f33912SNimrod Andy ((addr >= txq->tso_hdrs_dma) && \ 3137355f276STroy Kisky (addr < txq->tso_hdrs_dma + txq->bd.ring_size * TSO_HEADER_SIZE)) 31479f33912SNimrod Andy 315793fc096SFrank Li static int mii_cnt; 316793fc096SFrank Li 3177355f276STroy Kisky static struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, 3187355f276STroy Kisky struct bufdesc_prop *bd) 319793fc096SFrank Li { 3207355f276STroy Kisky return (bdp >= bd->last) ? bd->base 321145d6e29SFugang Duan : (struct bufdesc *)(((void *)bdp) + bd->dsize); 322793fc096SFrank Li } 323793fc096SFrank Li 3247355f276STroy Kisky static struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, 3257355f276STroy Kisky struct bufdesc_prop *bd) 32636e24e2eSDuan Fugang-B38611 { 3277355f276STroy Kisky return (bdp <= bd->base) ? bd->last 328145d6e29SFugang Duan : (struct bufdesc *)(((void *)bdp) - bd->dsize); 32936e24e2eSDuan Fugang-B38611 } 33036e24e2eSDuan Fugang-B38611 3317355f276STroy Kisky static int fec_enet_get_bd_index(struct bufdesc *bdp, 3327355f276STroy Kisky struct bufdesc_prop *bd) 33361a4427bSNimrod Andy { 3347355f276STroy Kisky return ((const char *)bdp - (const char *)bd->base) >> bd->dsize_log2; 33561a4427bSNimrod Andy } 33661a4427bSNimrod Andy 3377355f276STroy Kisky static int fec_enet_get_free_txdesc_num(struct fec_enet_priv_tx_q *txq) 3386e909283SNimrod Andy { 3396e909283SNimrod Andy int entries; 3406e909283SNimrod Andy 3417355f276STroy Kisky entries = (((const char *)txq->dirty_tx - 3427355f276STroy Kisky (const char *)txq->bd.cur) >> txq->bd.dsize_log2) - 1; 3436e909283SNimrod Andy 3447355f276STroy Kisky return entries >= 0 ? entries : entries + txq->bd.ring_size; 3456e909283SNimrod Andy } 3466e909283SNimrod Andy 347c20e599bSLothar Waßmann static void swap_buffer(void *bufaddr, int len) 348793fc096SFrank Li { 349793fc096SFrank Li int i; 350793fc096SFrank Li unsigned int *buf = bufaddr; 351793fc096SFrank Li 3527b487d07SLothar Waßmann for (i = 0; i < len; i += 4, buf++) 353e453789aSLothar Waßmann swab32s(buf); 354793fc096SFrank Li } 355793fc096SFrank Li 3561310b544SLothar Waßmann static void swap_buffer2(void *dst_buf, void *src_buf, int len) 3571310b544SLothar Waßmann { 3581310b544SLothar Waßmann int i; 3591310b544SLothar Waßmann unsigned int *src = src_buf; 3601310b544SLothar Waßmann unsigned int *dst = dst_buf; 3611310b544SLothar Waßmann 3621310b544SLothar Waßmann for (i = 0; i < len; i += 4, src++, dst++) 3631310b544SLothar Waßmann *dst = swab32p(src); 3641310b544SLothar Waßmann } 3651310b544SLothar Waßmann 366344756f6SRussell King static void fec_dump(struct net_device *ndev) 367344756f6SRussell King { 368344756f6SRussell King struct fec_enet_private *fep = netdev_priv(ndev); 3694d494cdcSFugang Duan struct bufdesc *bdp; 3704d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 3714d494cdcSFugang Duan int index = 0; 372344756f6SRussell King 373344756f6SRussell King netdev_info(ndev, "TX ring dump\n"); 374344756f6SRussell King pr_info("Nr SC addr len SKB\n"); 375344756f6SRussell King 3764d494cdcSFugang Duan txq = fep->tx_queue[0]; 3777355f276STroy Kisky bdp = txq->bd.base; 3784d494cdcSFugang Duan 379344756f6SRussell King do { 3805cfa3039SJohannes Berg pr_info("%3u %c%c 0x%04x 0x%08x %4u %p\n", 381344756f6SRussell King index, 3827355f276STroy Kisky bdp == txq->bd.cur ? 'S' : ' ', 3834d494cdcSFugang Duan bdp == txq->dirty_tx ? 'H' : ' ', 3845cfa3039SJohannes Berg fec16_to_cpu(bdp->cbd_sc), 3855cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 3865cfa3039SJohannes Berg fec16_to_cpu(bdp->cbd_datlen), 3874d494cdcSFugang Duan txq->tx_skbuff[index]); 3887355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 389344756f6SRussell King index++; 3907355f276STroy Kisky } while (bdp != txq->bd.base); 391344756f6SRussell King } 392344756f6SRussell King 39362a02c98SFugang Duan static inline bool is_ipv4_pkt(struct sk_buff *skb) 39462a02c98SFugang Duan { 39562a02c98SFugang Duan return skb->protocol == htons(ETH_P_IP) && ip_hdr(skb)->version == 4; 39662a02c98SFugang Duan } 39762a02c98SFugang Duan 3984c09eed9SJim Baxter static int 3994c09eed9SJim Baxter fec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev) 4004c09eed9SJim Baxter { 4014c09eed9SJim Baxter /* Only run for packets requiring a checksum. */ 4024c09eed9SJim Baxter if (skb->ip_summed != CHECKSUM_PARTIAL) 4034c09eed9SJim Baxter return 0; 4044c09eed9SJim Baxter 4054c09eed9SJim Baxter if (unlikely(skb_cow_head(skb, 0))) 4064c09eed9SJim Baxter return -1; 4074c09eed9SJim Baxter 40862a02c98SFugang Duan if (is_ipv4_pkt(skb)) 40996c50caaSNimrod Andy ip_hdr(skb)->check = 0; 4104c09eed9SJim Baxter *(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) = 0; 4114c09eed9SJim Baxter 4124c09eed9SJim Baxter return 0; 4134c09eed9SJim Baxter } 4144c09eed9SJim Baxter 415c4bc44c6SKevin Hao static struct bufdesc * 4164d494cdcSFugang Duan fec_enet_txq_submit_frag_skb(struct fec_enet_priv_tx_q *txq, 4174d494cdcSFugang Duan struct sk_buff *skb, 4184d494cdcSFugang Duan struct net_device *ndev) 4196e909283SNimrod Andy { 4206e909283SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 4217355f276STroy Kisky struct bufdesc *bdp = txq->bd.cur; 4226e909283SNimrod Andy struct bufdesc_ex *ebdp; 4236e909283SNimrod Andy int nr_frags = skb_shinfo(skb)->nr_frags; 4246e909283SNimrod Andy int frag, frag_len; 4256e909283SNimrod Andy unsigned short status; 4266e909283SNimrod Andy unsigned int estatus = 0; 4276e909283SNimrod Andy skb_frag_t *this_frag; 4286e909283SNimrod Andy unsigned int index; 4296e909283SNimrod Andy void *bufaddr; 430d6bf3143SRussell King dma_addr_t addr; 4316e909283SNimrod Andy int i; 4326e909283SNimrod Andy 4336e909283SNimrod Andy for (frag = 0; frag < nr_frags; frag++) { 4346e909283SNimrod Andy this_frag = &skb_shinfo(skb)->frags[frag]; 4357355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 4366e909283SNimrod Andy ebdp = (struct bufdesc_ex *)bdp; 4376e909283SNimrod Andy 4385cfa3039SJohannes Berg status = fec16_to_cpu(bdp->cbd_sc); 4396e909283SNimrod Andy status &= ~BD_ENET_TX_STATS; 4406e909283SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 441d7840976SMatthew Wilcox (Oracle) frag_len = skb_frag_size(&skb_shinfo(skb)->frags[frag]); 4426e909283SNimrod Andy 4436e909283SNimrod Andy /* Handle the last BD specially */ 4446e909283SNimrod Andy if (frag == nr_frags - 1) { 4456e909283SNimrod Andy status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); 4466e909283SNimrod Andy if (fep->bufdesc_ex) { 4476e909283SNimrod Andy estatus |= BD_ENET_TX_INT; 4486e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & 4496e909283SNimrod Andy SKBTX_HW_TSTAMP && fep->hwts_tx_en)) 4506e909283SNimrod Andy estatus |= BD_ENET_TX_TS; 4516e909283SNimrod Andy } 4526e909283SNimrod Andy } 4536e909283SNimrod Andy 4546e909283SNimrod Andy if (fep->bufdesc_ex) { 4556b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 45653bb20d1STroy Kisky estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 4576e909283SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 4586e909283SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 459471ff445SJoakim Zhang 4606e909283SNimrod Andy ebdp->cbd_bdu = 0; 4615cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(estatus); 4626e909283SNimrod Andy } 4636e909283SNimrod Andy 464d7840976SMatthew Wilcox (Oracle) bufaddr = skb_frag_address(this_frag); 4656e909283SNimrod Andy 4667355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 46741ef84ceSFugang Duan if (((unsigned long) bufaddr) & fep->tx_align || 4686b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 4694d494cdcSFugang Duan memcpy(txq->tx_bounce[index], bufaddr, frag_len); 4704d494cdcSFugang Duan bufaddr = txq->tx_bounce[index]; 4716e909283SNimrod Andy 4726b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 4736e909283SNimrod Andy swap_buffer(bufaddr, frag_len); 4746e909283SNimrod Andy } 4756e909283SNimrod Andy 476d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, bufaddr, frag_len, 477d6bf3143SRussell King DMA_TO_DEVICE); 478d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 4796e909283SNimrod Andy if (net_ratelimit()) 4806e909283SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 4816e909283SNimrod Andy goto dma_mapping_error; 4826e909283SNimrod Andy } 4836e909283SNimrod Andy 4845cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(addr); 4855cfa3039SJohannes Berg bdp->cbd_datlen = cpu_to_fec16(frag_len); 486be293467STroy Kisky /* Make sure the updates to rest of the descriptor are 487be293467STroy Kisky * performed before transferring ownership. 488be293467STroy Kisky */ 489be293467STroy Kisky wmb(); 4905cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(status); 4916e909283SNimrod Andy } 4926e909283SNimrod Andy 493c4bc44c6SKevin Hao return bdp; 4946e909283SNimrod Andy dma_mapping_error: 4957355f276STroy Kisky bdp = txq->bd.cur; 4966e909283SNimrod Andy for (i = 0; i < frag; i++) { 4977355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 4985cfa3039SJohannes Berg dma_unmap_single(&fep->pdev->dev, fec32_to_cpu(bdp->cbd_bufaddr), 4995cfa3039SJohannes Berg fec16_to_cpu(bdp->cbd_datlen), DMA_TO_DEVICE); 5006e909283SNimrod Andy } 501c4bc44c6SKevin Hao return ERR_PTR(-ENOMEM); 5026e909283SNimrod Andy } 5036e909283SNimrod Andy 5044d494cdcSFugang Duan static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq, 5054d494cdcSFugang Duan struct sk_buff *skb, struct net_device *ndev) 5066e909283SNimrod Andy { 5076e909283SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 5086e909283SNimrod Andy int nr_frags = skb_shinfo(skb)->nr_frags; 5096e909283SNimrod Andy struct bufdesc *bdp, *last_bdp; 5106e909283SNimrod Andy void *bufaddr; 511d6bf3143SRussell King dma_addr_t addr; 5126e909283SNimrod Andy unsigned short status; 5136e909283SNimrod Andy unsigned short buflen; 5146e909283SNimrod Andy unsigned int estatus = 0; 5156e909283SNimrod Andy unsigned int index; 51679f33912SNimrod Andy int entries_free; 5176e909283SNimrod Andy 5187355f276STroy Kisky entries_free = fec_enet_get_free_txdesc_num(txq); 51979f33912SNimrod Andy if (entries_free < MAX_SKB_FRAGS + 1) { 52079f33912SNimrod Andy dev_kfree_skb_any(skb); 52179f33912SNimrod Andy if (net_ratelimit()) 52279f33912SNimrod Andy netdev_err(ndev, "NOT enough BD for SG!\n"); 52379f33912SNimrod Andy return NETDEV_TX_OK; 52479f33912SNimrod Andy } 52579f33912SNimrod Andy 5266e909283SNimrod Andy /* Protocol checksum off-load for TCP and UDP. */ 5276e909283SNimrod Andy if (fec_enet_clear_csum(skb, ndev)) { 5286e909283SNimrod Andy dev_kfree_skb_any(skb); 5296e909283SNimrod Andy return NETDEV_TX_OK; 5306e909283SNimrod Andy } 5316e909283SNimrod Andy 5326e909283SNimrod Andy /* Fill in a Tx ring entry */ 5337355f276STroy Kisky bdp = txq->bd.cur; 534c4bc44c6SKevin Hao last_bdp = bdp; 5355cfa3039SJohannes Berg status = fec16_to_cpu(bdp->cbd_sc); 5366e909283SNimrod Andy status &= ~BD_ENET_TX_STATS; 5376e909283SNimrod Andy 5386e909283SNimrod Andy /* Set buffer length and buffer pointer */ 5396e909283SNimrod Andy bufaddr = skb->data; 5406e909283SNimrod Andy buflen = skb_headlen(skb); 5416e909283SNimrod Andy 5427355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 54341ef84ceSFugang Duan if (((unsigned long) bufaddr) & fep->tx_align || 5446b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 5454d494cdcSFugang Duan memcpy(txq->tx_bounce[index], skb->data, buflen); 5464d494cdcSFugang Duan bufaddr = txq->tx_bounce[index]; 5476e909283SNimrod Andy 5486b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 5496e909283SNimrod Andy swap_buffer(bufaddr, buflen); 5506e909283SNimrod Andy } 5516e909283SNimrod Andy 552d6bf3143SRussell King /* Push the data cache so the CPM does not get stale memory data. */ 553d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, bufaddr, buflen, DMA_TO_DEVICE); 554d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 5556e909283SNimrod Andy dev_kfree_skb_any(skb); 5566e909283SNimrod Andy if (net_ratelimit()) 5576e909283SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 5586e909283SNimrod Andy return NETDEV_TX_OK; 5596e909283SNimrod Andy } 5606e909283SNimrod Andy 5616e909283SNimrod Andy if (nr_frags) { 562c4bc44c6SKevin Hao last_bdp = fec_enet_txq_submit_frag_skb(txq, skb, ndev); 563fc75ba51STroy Kisky if (IS_ERR(last_bdp)) { 564fc75ba51STroy Kisky dma_unmap_single(&fep->pdev->dev, addr, 565fc75ba51STroy Kisky buflen, DMA_TO_DEVICE); 566fc75ba51STroy Kisky dev_kfree_skb_any(skb); 567c4bc44c6SKevin Hao return NETDEV_TX_OK; 568fc75ba51STroy Kisky } 5696e909283SNimrod Andy } else { 5706e909283SNimrod Andy status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); 5716e909283SNimrod Andy if (fep->bufdesc_ex) { 5726e909283SNimrod Andy estatus = BD_ENET_TX_INT; 5736e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & 5746e909283SNimrod Andy SKBTX_HW_TSTAMP && fep->hwts_tx_en)) 5756e909283SNimrod Andy estatus |= BD_ENET_TX_TS; 5766e909283SNimrod Andy } 5776e909283SNimrod Andy } 578fc75ba51STroy Kisky bdp->cbd_bufaddr = cpu_to_fec32(addr); 579fc75ba51STroy Kisky bdp->cbd_datlen = cpu_to_fec16(buflen); 5806e909283SNimrod Andy 5816e909283SNimrod Andy if (fep->bufdesc_ex) { 5826e909283SNimrod Andy 5836e909283SNimrod Andy struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 5846e909283SNimrod Andy 5856e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && 5866e909283SNimrod Andy fep->hwts_tx_en)) 5876e909283SNimrod Andy skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; 5886e909283SNimrod Andy 5896b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 59053bb20d1STroy Kisky estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 591befe8213SNimrod Andy 5926e909283SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 5936e909283SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 5946e909283SNimrod Andy 5956e909283SNimrod Andy ebdp->cbd_bdu = 0; 5965cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(estatus); 5976e909283SNimrod Andy } 5986e909283SNimrod Andy 5997355f276STroy Kisky index = fec_enet_get_bd_index(last_bdp, &txq->bd); 6006e909283SNimrod Andy /* Save skb pointer */ 6014d494cdcSFugang Duan txq->tx_skbuff[index] = skb; 6026e909283SNimrod Andy 603be293467STroy Kisky /* Make sure the updates to rest of the descriptor are performed before 604be293467STroy Kisky * transferring ownership. 605be293467STroy Kisky */ 606be293467STroy Kisky wmb(); 6076e909283SNimrod Andy 6086e909283SNimrod Andy /* Send it on its way. Tell FEC it's ready, interrupt when done, 6096e909283SNimrod Andy * it's the last BD of the frame, and to put the CRC on the end. 6106e909283SNimrod Andy */ 6116e909283SNimrod Andy status |= (BD_ENET_TX_READY | BD_ENET_TX_TC); 6125cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(status); 6136e909283SNimrod Andy 614793fc096SFrank Li /* If this was the last BD in the ring, start at the beginning again. */ 6157355f276STroy Kisky bdp = fec_enet_get_nextdesc(last_bdp, &txq->bd); 616793fc096SFrank Li 6177a2a8451SEric Dumazet skb_tx_timestamp(skb); 6187a2a8451SEric Dumazet 619c4bc44c6SKevin Hao /* Make sure the update to bdp and tx_skbuff are performed before 6207355f276STroy Kisky * txq->bd.cur. 621c4bc44c6SKevin Hao */ 622c4bc44c6SKevin Hao wmb(); 6237355f276STroy Kisky txq->bd.cur = bdp; 624793fc096SFrank Li 625793fc096SFrank Li /* Trigger transmission start */ 62653bb20d1STroy Kisky writel(0, txq->bd.reg_desc_active); 627793fc096SFrank Li 6286e909283SNimrod Andy return 0; 629793fc096SFrank Li } 630793fc096SFrank Li 63179f33912SNimrod Andy static int 6324d494cdcSFugang Duan fec_enet_txq_put_data_tso(struct fec_enet_priv_tx_q *txq, struct sk_buff *skb, 6334d494cdcSFugang Duan struct net_device *ndev, 63479f33912SNimrod Andy struct bufdesc *bdp, int index, char *data, 63579f33912SNimrod Andy int size, bool last_tcp, bool is_last) 63679f33912SNimrod Andy { 63779f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 63861cd2ebbSFabian Frederick struct bufdesc_ex *ebdp = container_of(bdp, struct bufdesc_ex, desc); 63979f33912SNimrod Andy unsigned short status; 64079f33912SNimrod Andy unsigned int estatus = 0; 641d6bf3143SRussell King dma_addr_t addr; 64279f33912SNimrod Andy 6435cfa3039SJohannes Berg status = fec16_to_cpu(bdp->cbd_sc); 64479f33912SNimrod Andy status &= ~BD_ENET_TX_STATS; 64579f33912SNimrod Andy 64679f33912SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 64779f33912SNimrod Andy 64841ef84ceSFugang Duan if (((unsigned long) data) & fep->tx_align || 6496b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 6504d494cdcSFugang Duan memcpy(txq->tx_bounce[index], data, size); 6514d494cdcSFugang Duan data = txq->tx_bounce[index]; 65279f33912SNimrod Andy 6536b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 65479f33912SNimrod Andy swap_buffer(data, size); 65579f33912SNimrod Andy } 65679f33912SNimrod Andy 657d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, data, size, DMA_TO_DEVICE); 658d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 65979f33912SNimrod Andy dev_kfree_skb_any(skb); 66079f33912SNimrod Andy if (net_ratelimit()) 66179f33912SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 66279f33912SNimrod Andy return NETDEV_TX_BUSY; 66379f33912SNimrod Andy } 66479f33912SNimrod Andy 6655cfa3039SJohannes Berg bdp->cbd_datlen = cpu_to_fec16(size); 6665cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(addr); 667d6bf3143SRussell King 66879f33912SNimrod Andy if (fep->bufdesc_ex) { 6696b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 67053bb20d1STroy Kisky estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 67179f33912SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 67279f33912SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 67379f33912SNimrod Andy ebdp->cbd_bdu = 0; 6745cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(estatus); 67579f33912SNimrod Andy } 67679f33912SNimrod Andy 67779f33912SNimrod Andy /* Handle the last BD specially */ 67879f33912SNimrod Andy if (last_tcp) 67979f33912SNimrod Andy status |= (BD_ENET_TX_LAST | BD_ENET_TX_TC); 68079f33912SNimrod Andy if (is_last) { 68179f33912SNimrod Andy status |= BD_ENET_TX_INTR; 68279f33912SNimrod Andy if (fep->bufdesc_ex) 6835cfa3039SJohannes Berg ebdp->cbd_esc |= cpu_to_fec32(BD_ENET_TX_INT); 68479f33912SNimrod Andy } 68579f33912SNimrod Andy 6865cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(status); 68779f33912SNimrod Andy 68879f33912SNimrod Andy return 0; 68979f33912SNimrod Andy } 69079f33912SNimrod Andy 69179f33912SNimrod Andy static int 6924d494cdcSFugang Duan fec_enet_txq_put_hdr_tso(struct fec_enet_priv_tx_q *txq, 6934d494cdcSFugang Duan struct sk_buff *skb, struct net_device *ndev, 69479f33912SNimrod Andy struct bufdesc *bdp, int index) 69579f33912SNimrod Andy { 69679f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 697504148feSEric Dumazet int hdr_len = skb_tcp_all_headers(skb); 69861cd2ebbSFabian Frederick struct bufdesc_ex *ebdp = container_of(bdp, struct bufdesc_ex, desc); 69979f33912SNimrod Andy void *bufaddr; 70079f33912SNimrod Andy unsigned long dmabuf; 70179f33912SNimrod Andy unsigned short status; 70279f33912SNimrod Andy unsigned int estatus = 0; 70379f33912SNimrod Andy 7045cfa3039SJohannes Berg status = fec16_to_cpu(bdp->cbd_sc); 70579f33912SNimrod Andy status &= ~BD_ENET_TX_STATS; 70679f33912SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 70779f33912SNimrod Andy 7084d494cdcSFugang Duan bufaddr = txq->tso_hdrs + index * TSO_HEADER_SIZE; 7094d494cdcSFugang Duan dmabuf = txq->tso_hdrs_dma + index * TSO_HEADER_SIZE; 71041ef84ceSFugang Duan if (((unsigned long)bufaddr) & fep->tx_align || 7116b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 7124d494cdcSFugang Duan memcpy(txq->tx_bounce[index], skb->data, hdr_len); 7134d494cdcSFugang Duan bufaddr = txq->tx_bounce[index]; 71479f33912SNimrod Andy 7156b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 71679f33912SNimrod Andy swap_buffer(bufaddr, hdr_len); 71779f33912SNimrod Andy 71879f33912SNimrod Andy dmabuf = dma_map_single(&fep->pdev->dev, bufaddr, 71979f33912SNimrod Andy hdr_len, DMA_TO_DEVICE); 72079f33912SNimrod Andy if (dma_mapping_error(&fep->pdev->dev, dmabuf)) { 72179f33912SNimrod Andy dev_kfree_skb_any(skb); 72279f33912SNimrod Andy if (net_ratelimit()) 72379f33912SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 72479f33912SNimrod Andy return NETDEV_TX_BUSY; 72579f33912SNimrod Andy } 72679f33912SNimrod Andy } 72779f33912SNimrod Andy 7285cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(dmabuf); 7295cfa3039SJohannes Berg bdp->cbd_datlen = cpu_to_fec16(hdr_len); 73079f33912SNimrod Andy 73179f33912SNimrod Andy if (fep->bufdesc_ex) { 7326b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 73353bb20d1STroy Kisky estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 73479f33912SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 73579f33912SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 73679f33912SNimrod Andy ebdp->cbd_bdu = 0; 7375cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(estatus); 73879f33912SNimrod Andy } 73979f33912SNimrod Andy 7405cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(status); 74179f33912SNimrod Andy 74279f33912SNimrod Andy return 0; 74379f33912SNimrod Andy } 74479f33912SNimrod Andy 7454d494cdcSFugang Duan static int fec_enet_txq_submit_tso(struct fec_enet_priv_tx_q *txq, 7464d494cdcSFugang Duan struct sk_buff *skb, 7474d494cdcSFugang Duan struct net_device *ndev) 74879f33912SNimrod Andy { 74979f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 750761b331cSEric Dumazet int hdr_len, total_len, data_left; 7517355f276STroy Kisky struct bufdesc *bdp = txq->bd.cur; 75279f33912SNimrod Andy struct tso_t tso; 75379f33912SNimrod Andy unsigned int index = 0; 75479f33912SNimrod Andy int ret; 75579f33912SNimrod Andy 7567355f276STroy Kisky if (tso_count_descs(skb) >= fec_enet_get_free_txdesc_num(txq)) { 75779f33912SNimrod Andy dev_kfree_skb_any(skb); 75879f33912SNimrod Andy if (net_ratelimit()) 75979f33912SNimrod Andy netdev_err(ndev, "NOT enough BD for TSO!\n"); 76079f33912SNimrod Andy return NETDEV_TX_OK; 76179f33912SNimrod Andy } 76279f33912SNimrod Andy 76379f33912SNimrod Andy /* Protocol checksum off-load for TCP and UDP. */ 76479f33912SNimrod Andy if (fec_enet_clear_csum(skb, ndev)) { 76579f33912SNimrod Andy dev_kfree_skb_any(skb); 76679f33912SNimrod Andy return NETDEV_TX_OK; 76779f33912SNimrod Andy } 76879f33912SNimrod Andy 76979f33912SNimrod Andy /* Initialize the TSO handler, and prepare the first payload */ 770761b331cSEric Dumazet hdr_len = tso_start(skb, &tso); 77179f33912SNimrod Andy 77279f33912SNimrod Andy total_len = skb->len - hdr_len; 77379f33912SNimrod Andy while (total_len > 0) { 77479f33912SNimrod Andy char *hdr; 77579f33912SNimrod Andy 7767355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 77779f33912SNimrod Andy data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len); 77879f33912SNimrod Andy total_len -= data_left; 77979f33912SNimrod Andy 78079f33912SNimrod Andy /* prepare packet headers: MAC + IP + TCP */ 7814d494cdcSFugang Duan hdr = txq->tso_hdrs + index * TSO_HEADER_SIZE; 78279f33912SNimrod Andy tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0); 7834d494cdcSFugang Duan ret = fec_enet_txq_put_hdr_tso(txq, skb, ndev, bdp, index); 78479f33912SNimrod Andy if (ret) 78579f33912SNimrod Andy goto err_release; 78679f33912SNimrod Andy 78779f33912SNimrod Andy while (data_left > 0) { 78879f33912SNimrod Andy int size; 78979f33912SNimrod Andy 79079f33912SNimrod Andy size = min_t(int, tso.size, data_left); 7917355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 7927355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 7934d494cdcSFugang Duan ret = fec_enet_txq_put_data_tso(txq, skb, ndev, 7944d494cdcSFugang Duan bdp, index, 7954d494cdcSFugang Duan tso.data, size, 7964d494cdcSFugang Duan size == data_left, 79779f33912SNimrod Andy total_len == 0); 79879f33912SNimrod Andy if (ret) 79979f33912SNimrod Andy goto err_release; 80079f33912SNimrod Andy 80179f33912SNimrod Andy data_left -= size; 80279f33912SNimrod Andy tso_build_data(skb, &tso, size); 80379f33912SNimrod Andy } 80479f33912SNimrod Andy 8057355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 80679f33912SNimrod Andy } 80779f33912SNimrod Andy 80879f33912SNimrod Andy /* Save skb pointer */ 8094d494cdcSFugang Duan txq->tx_skbuff[index] = skb; 81079f33912SNimrod Andy 81179f33912SNimrod Andy skb_tx_timestamp(skb); 8127355f276STroy Kisky txq->bd.cur = bdp; 81379f33912SNimrod Andy 81479f33912SNimrod Andy /* Trigger transmission start */ 8156b7e4008SLothar Waßmann if (!(fep->quirks & FEC_QUIRK_ERR007885) || 81653bb20d1STroy Kisky !readl(txq->bd.reg_desc_active) || 81753bb20d1STroy Kisky !readl(txq->bd.reg_desc_active) || 81853bb20d1STroy Kisky !readl(txq->bd.reg_desc_active) || 81953bb20d1STroy Kisky !readl(txq->bd.reg_desc_active)) 82053bb20d1STroy Kisky writel(0, txq->bd.reg_desc_active); 82179f33912SNimrod Andy 82279f33912SNimrod Andy return 0; 82379f33912SNimrod Andy 82479f33912SNimrod Andy err_release: 82579f33912SNimrod Andy /* TODO: Release all used data descriptors for TSO */ 82679f33912SNimrod Andy return ret; 82779f33912SNimrod Andy } 82879f33912SNimrod Andy 82961a4427bSNimrod Andy static netdev_tx_t 83061a4427bSNimrod Andy fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) 83161a4427bSNimrod Andy { 83261a4427bSNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 8336e909283SNimrod Andy int entries_free; 8344d494cdcSFugang Duan unsigned short queue; 8354d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 8364d494cdcSFugang Duan struct netdev_queue *nq; 83761a4427bSNimrod Andy int ret; 83861a4427bSNimrod Andy 8394d494cdcSFugang Duan queue = skb_get_queue_mapping(skb); 8404d494cdcSFugang Duan txq = fep->tx_queue[queue]; 8414d494cdcSFugang Duan nq = netdev_get_tx_queue(ndev, queue); 8424d494cdcSFugang Duan 84379f33912SNimrod Andy if (skb_is_gso(skb)) 8444d494cdcSFugang Duan ret = fec_enet_txq_submit_tso(txq, skb, ndev); 84579f33912SNimrod Andy else 8464d494cdcSFugang Duan ret = fec_enet_txq_submit_skb(txq, skb, ndev); 8476e909283SNimrod Andy if (ret) 8486e909283SNimrod Andy return ret; 84961a4427bSNimrod Andy 8507355f276STroy Kisky entries_free = fec_enet_get_free_txdesc_num(txq); 8514d494cdcSFugang Duan if (entries_free <= txq->tx_stop_threshold) 8524d494cdcSFugang Duan netif_tx_stop_queue(nq); 85361a4427bSNimrod Andy 85461a4427bSNimrod Andy return NETDEV_TX_OK; 85561a4427bSNimrod Andy } 85661a4427bSNimrod Andy 857a210576cSDavid S. Miller /* Init RX & TX buffer descriptors 858a210576cSDavid S. Miller */ 859a210576cSDavid S. Miller static void fec_enet_bd_init(struct net_device *dev) 860a210576cSDavid S. Miller { 861a210576cSDavid S. Miller struct fec_enet_private *fep = netdev_priv(dev); 8624d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 8634d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 864a210576cSDavid S. Miller struct bufdesc *bdp; 865a210576cSDavid S. Miller unsigned int i; 86659d0f746SFrank Li unsigned int q; 867a210576cSDavid S. Miller 86859d0f746SFrank Li for (q = 0; q < fep->num_rx_queues; q++) { 869a210576cSDavid S. Miller /* Initialize the receive buffer descriptors. */ 87059d0f746SFrank Li rxq = fep->rx_queue[q]; 8717355f276STroy Kisky bdp = rxq->bd.base; 8724d494cdcSFugang Duan 8737355f276STroy Kisky for (i = 0; i < rxq->bd.ring_size; i++) { 874a210576cSDavid S. Miller 875a210576cSDavid S. Miller /* Initialize the BD for every fragment in the page. */ 876a210576cSDavid S. Miller if (bdp->cbd_bufaddr) 8775cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); 878a210576cSDavid S. Miller else 8795cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(0); 8807355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 881a210576cSDavid S. Miller } 882a210576cSDavid S. Miller 883a210576cSDavid S. Miller /* Set the last buffer to wrap */ 8847355f276STroy Kisky bdp = fec_enet_get_prevdesc(bdp, &rxq->bd); 8855cfa3039SJohannes Berg bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 886a210576cSDavid S. Miller 8877355f276STroy Kisky rxq->bd.cur = rxq->bd.base; 88859d0f746SFrank Li } 889a210576cSDavid S. Miller 89059d0f746SFrank Li for (q = 0; q < fep->num_tx_queues; q++) { 891a210576cSDavid S. Miller /* ...and the same for transmit */ 89259d0f746SFrank Li txq = fep->tx_queue[q]; 8937355f276STroy Kisky bdp = txq->bd.base; 8947355f276STroy Kisky txq->bd.cur = bdp; 895a210576cSDavid S. Miller 8967355f276STroy Kisky for (i = 0; i < txq->bd.ring_size; i++) { 897a210576cSDavid S. Miller /* Initialize the BD for every fragment in the page. */ 8985cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(0); 899178e5f57SFugang Duan if (bdp->cbd_bufaddr && 900178e5f57SFugang Duan !IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr))) 901178e5f57SFugang Duan dma_unmap_single(&fep->pdev->dev, 902178e5f57SFugang Duan fec32_to_cpu(bdp->cbd_bufaddr), 903178e5f57SFugang Duan fec16_to_cpu(bdp->cbd_datlen), 904178e5f57SFugang Duan DMA_TO_DEVICE); 9054d494cdcSFugang Duan if (txq->tx_skbuff[i]) { 9064d494cdcSFugang Duan dev_kfree_skb_any(txq->tx_skbuff[i]); 9074d494cdcSFugang Duan txq->tx_skbuff[i] = NULL; 908a210576cSDavid S. Miller } 9095cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(0); 9107355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 911a210576cSDavid S. Miller } 912a210576cSDavid S. Miller 913a210576cSDavid S. Miller /* Set the last buffer to wrap */ 9147355f276STroy Kisky bdp = fec_enet_get_prevdesc(bdp, &txq->bd); 9155cfa3039SJohannes Berg bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 9164d494cdcSFugang Duan txq->dirty_tx = bdp; 917a210576cSDavid S. Miller } 91859d0f746SFrank Li } 91959d0f746SFrank Li 920ce99d0d3SFrank Li static void fec_enet_active_rxring(struct net_device *ndev) 921ce99d0d3SFrank Li { 922ce99d0d3SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 923ce99d0d3SFrank Li int i; 924ce99d0d3SFrank Li 925ce99d0d3SFrank Li for (i = 0; i < fep->num_rx_queues; i++) 92653bb20d1STroy Kisky writel(0, fep->rx_queue[i]->bd.reg_desc_active); 927ce99d0d3SFrank Li } 928ce99d0d3SFrank Li 92959d0f746SFrank Li static void fec_enet_enable_ring(struct net_device *ndev) 93059d0f746SFrank Li { 93159d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 93259d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 93359d0f746SFrank Li struct fec_enet_priv_rx_q *rxq; 93459d0f746SFrank Li int i; 93559d0f746SFrank Li 93659d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) { 93759d0f746SFrank Li rxq = fep->rx_queue[i]; 9387355f276STroy Kisky writel(rxq->bd.dma, fep->hwp + FEC_R_DES_START(i)); 939fbbeefddSAndrew Lunn writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_R_BUFF_SIZE(i)); 94059d0f746SFrank Li 94159d0f746SFrank Li /* enable DMA1/2 */ 94259d0f746SFrank Li if (i) 94359d0f746SFrank Li writel(RCMR_MATCHEN | RCMR_CMP(i), 94459d0f746SFrank Li fep->hwp + FEC_RCMR(i)); 94559d0f746SFrank Li } 94659d0f746SFrank Li 94759d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 94859d0f746SFrank Li txq = fep->tx_queue[i]; 9497355f276STroy Kisky writel(txq->bd.dma, fep->hwp + FEC_X_DES_START(i)); 95059d0f746SFrank Li 95159d0f746SFrank Li /* enable DMA1/2 */ 95259d0f746SFrank Li if (i) 95359d0f746SFrank Li writel(DMA_CLASS_EN | IDLE_SLOPE(i), 95459d0f746SFrank Li fep->hwp + FEC_DMA_CFG(i)); 95559d0f746SFrank Li } 95659d0f746SFrank Li } 95759d0f746SFrank Li 95859d0f746SFrank Li static void fec_enet_reset_skb(struct net_device *ndev) 95959d0f746SFrank Li { 96059d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 96159d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 96259d0f746SFrank Li int i, j; 96359d0f746SFrank Li 96459d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 96559d0f746SFrank Li txq = fep->tx_queue[i]; 96659d0f746SFrank Li 9677355f276STroy Kisky for (j = 0; j < txq->bd.ring_size; j++) { 96859d0f746SFrank Li if (txq->tx_skbuff[j]) { 96959d0f746SFrank Li dev_kfree_skb_any(txq->tx_skbuff[j]); 97059d0f746SFrank Li txq->tx_skbuff[j] = NULL; 97159d0f746SFrank Li } 97259d0f746SFrank Li } 97359d0f746SFrank Li } 97459d0f746SFrank Li } 975a210576cSDavid S. Miller 976dbc64a8eSRussell King /* 977dbc64a8eSRussell King * This function is called to start or restart the FEC during a link 978dbc64a8eSRussell King * change, transmit timeout, or to reconfigure the FEC. The network 979dbc64a8eSRussell King * packet processing for this device must be stopped before this call. 980793fc096SFrank Li */ 981793fc096SFrank Li static void 982ef83337dSRussell King fec_restart(struct net_device *ndev) 983793fc096SFrank Li { 984793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 985793fc096SFrank Li u32 temp_mac[2]; 986793fc096SFrank Li u32 rcntl = OPT_FRAME_SIZE | 0x04; 987793fc096SFrank Li u32 ecntl = 0x2; /* ETHEREN */ 988f7995922SCsókás Bence struct ptp_clock_request ptp_rq = { .type = PTP_CLK_REQ_PPS }; 989f7995922SCsókás Bence 990f7995922SCsókás Bence fec_ptp_save_state(fep); 991793fc096SFrank Li 992106c314cSFugang Duan /* Whack a reset. We should wait for this. 993106c314cSFugang Duan * For i.MX6SX SOC, enet use AXI bus, we use disable MAC 994106c314cSFugang Duan * instead of reset MAC itself. 995106c314cSFugang Duan */ 996471ff445SJoakim Zhang if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES || 997c730ab42SLaurent Badel ((fep->quirks & FEC_QUIRK_NO_HARD_RESET) && fep->link)) { 998106c314cSFugang Duan writel(0, fep->hwp + FEC_ECNTRL); 999106c314cSFugang Duan } else { 1000793fc096SFrank Li writel(1, fep->hwp + FEC_ECNTRL); 1001793fc096SFrank Li udelay(10); 1002106c314cSFugang Duan } 1003793fc096SFrank Li 1004793fc096SFrank Li /* 1005793fc096SFrank Li * enet-mac reset will reset mac address registers too, 1006793fc096SFrank Li * so need to reconfigure it. 1007793fc096SFrank Li */ 1008793fc096SFrank Li memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN); 10095cfa3039SJohannes Berg writel((__force u32)cpu_to_be32(temp_mac[0]), 10105cfa3039SJohannes Berg fep->hwp + FEC_ADDR_LOW); 10115cfa3039SJohannes Berg writel((__force u32)cpu_to_be32(temp_mac[1]), 10125cfa3039SJohannes Berg fep->hwp + FEC_ADDR_HIGH); 1013793fc096SFrank Li 1014f166f890SAndrew Lunn /* Clear any outstanding interrupt, except MDIO. */ 1015f166f890SAndrew Lunn writel((0xffffffff & ~FEC_ENET_MII), fep->hwp + FEC_IEVENT); 1016793fc096SFrank Li 1017a210576cSDavid S. Miller fec_enet_bd_init(ndev); 1018a210576cSDavid S. Miller 101959d0f746SFrank Li fec_enet_enable_ring(ndev); 1020793fc096SFrank Li 102159d0f746SFrank Li /* Reset tx SKB buffers. */ 102259d0f746SFrank Li fec_enet_reset_skb(ndev); 1023793fc096SFrank Li 1024793fc096SFrank Li /* Enable MII mode */ 1025ef83337dSRussell King if (fep->full_duplex == DUPLEX_FULL) { 1026793fc096SFrank Li /* FD enable */ 1027793fc096SFrank Li writel(0x04, fep->hwp + FEC_X_CNTRL); 1028793fc096SFrank Li } else { 1029793fc096SFrank Li /* No Rcv on Xmit */ 1030793fc096SFrank Li rcntl |= 0x02; 1031793fc096SFrank Li writel(0x0, fep->hwp + FEC_X_CNTRL); 1032793fc096SFrank Li } 1033793fc096SFrank Li 1034793fc096SFrank Li /* Set MII speed */ 1035793fc096SFrank Li writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 1036793fc096SFrank Li 1037d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 103818803495SGreg Ungerer if (fep->quirks & FEC_QUIRK_HAS_RACC) { 103932d1bbb1SGeert Uytterhoeven u32 val = readl(fep->hwp + FEC_RACC); 104032d1bbb1SGeert Uytterhoeven 10413ac72b7bSEric Nelson /* align IP header */ 10423ac72b7bSEric Nelson val |= FEC_RACC_SHIFT16; 10434c09eed9SJim Baxter if (fep->csum_flags & FLAG_RX_CSUM_ENABLED) 10443ac72b7bSEric Nelson /* set RX checksum */ 10454c09eed9SJim Baxter val |= FEC_RACC_OPTIONS; 10464c09eed9SJim Baxter else 10474c09eed9SJim Baxter val &= ~FEC_RACC_OPTIONS; 10484c09eed9SJim Baxter writel(val, fep->hwp + FEC_RACC); 104955cd48c8STroy Kisky writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_FTRL); 105032867fccSFabio Estevam } 1051d1391930SGuenter Roeck #endif 10524c09eed9SJim Baxter 1053793fc096SFrank Li /* 1054793fc096SFrank Li * The phy interface and speed need to get configured 1055793fc096SFrank Li * differently on enet-mac. 1056793fc096SFrank Li */ 10576b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_ENET_MAC) { 1058793fc096SFrank Li /* Enable flow control and length check */ 1059793fc096SFrank Li rcntl |= 0x40000000 | 0x00000020; 1060793fc096SFrank Li 1061793fc096SFrank Li /* RGMII, RMII or MII */ 1062e813bb2bSMarkus Pargmann if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII || 1063e813bb2bSMarkus Pargmann fep->phy_interface == PHY_INTERFACE_MODE_RGMII_ID || 1064e813bb2bSMarkus Pargmann fep->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID || 1065e813bb2bSMarkus Pargmann fep->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID) 1066793fc096SFrank Li rcntl |= (1 << 6); 1067793fc096SFrank Li else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) 1068793fc096SFrank Li rcntl |= (1 << 8); 1069793fc096SFrank Li else 1070793fc096SFrank Li rcntl &= ~(1 << 8); 1071793fc096SFrank Li 1072793fc096SFrank Li /* 1G, 100M or 10M */ 107345f5c327SPhilippe Reynes if (ndev->phydev) { 107445f5c327SPhilippe Reynes if (ndev->phydev->speed == SPEED_1000) 1075793fc096SFrank Li ecntl |= (1 << 5); 107645f5c327SPhilippe Reynes else if (ndev->phydev->speed == SPEED_100) 1077793fc096SFrank Li rcntl &= ~(1 << 9); 1078793fc096SFrank Li else 1079793fc096SFrank Li rcntl |= (1 << 9); 1080793fc096SFrank Li } 1081793fc096SFrank Li } else { 1082793fc096SFrank Li #ifdef FEC_MIIGSK_ENR 10836b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_USE_GASKET) { 1084793fc096SFrank Li u32 cfgr; 1085793fc096SFrank Li /* disable the gasket and wait */ 1086793fc096SFrank Li writel(0, fep->hwp + FEC_MIIGSK_ENR); 1087793fc096SFrank Li while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4) 1088793fc096SFrank Li udelay(1); 1089793fc096SFrank Li 1090793fc096SFrank Li /* 1091793fc096SFrank Li * configure the gasket: 1092793fc096SFrank Li * RMII, 50 MHz, no loopback, no echo 1093793fc096SFrank Li * MII, 25 MHz, no loopback, no echo 1094793fc096SFrank Li */ 1095793fc096SFrank Li cfgr = (fep->phy_interface == PHY_INTERFACE_MODE_RMII) 1096793fc096SFrank Li ? BM_MIIGSK_CFGR_RMII : BM_MIIGSK_CFGR_MII; 109745f5c327SPhilippe Reynes if (ndev->phydev && ndev->phydev->speed == SPEED_10) 1098793fc096SFrank Li cfgr |= BM_MIIGSK_CFGR_FRCONT_10M; 1099793fc096SFrank Li writel(cfgr, fep->hwp + FEC_MIIGSK_CFGR); 1100793fc096SFrank Li 1101793fc096SFrank Li /* re-enable the gasket */ 1102793fc096SFrank Li writel(2, fep->hwp + FEC_MIIGSK_ENR); 1103793fc096SFrank Li } 1104793fc096SFrank Li #endif 1105793fc096SFrank Li } 1106793fc096SFrank Li 1107d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 1108793fc096SFrank Li /* enable pause frame*/ 1109793fc096SFrank Li if ((fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) || 1110793fc096SFrank Li ((fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) && 111145f5c327SPhilippe Reynes ndev->phydev && ndev->phydev->pause)) { 1112793fc096SFrank Li rcntl |= FEC_ENET_FCE; 1113793fc096SFrank Li 11144c09eed9SJim Baxter /* set FIFO threshold parameter to reduce overrun */ 1115793fc096SFrank Li writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM); 1116793fc096SFrank Li writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL); 1117793fc096SFrank Li writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM); 1118793fc096SFrank Li writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL); 1119793fc096SFrank Li 1120793fc096SFrank Li /* OPD */ 1121793fc096SFrank Li writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD); 1122793fc096SFrank Li } else { 1123793fc096SFrank Li rcntl &= ~FEC_ENET_FCE; 1124793fc096SFrank Li } 1125d1391930SGuenter Roeck #endif /* !defined(CONFIG_M5272) */ 1126793fc096SFrank Li 1127793fc096SFrank Li writel(rcntl, fep->hwp + FEC_R_CNTRL); 1128793fc096SFrank Li 112984fe6182SStefan Wahren /* Setup multicast filter. */ 113084fe6182SStefan Wahren set_multicast_list(ndev); 113184fe6182SStefan Wahren #ifndef CONFIG_M5272 113284fe6182SStefan Wahren writel(0, fep->hwp + FEC_HASH_TABLE_HIGH); 113384fe6182SStefan Wahren writel(0, fep->hwp + FEC_HASH_TABLE_LOW); 113484fe6182SStefan Wahren #endif 113584fe6182SStefan Wahren 11366b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_ENET_MAC) { 1137793fc096SFrank Li /* enable ENET endian swap */ 1138793fc096SFrank Li ecntl |= (1 << 8); 1139793fc096SFrank Li /* enable ENET store and forward mode */ 1140793fc096SFrank Li writel(1 << 8, fep->hwp + FEC_X_WMRK); 1141793fc096SFrank Li } 1142793fc096SFrank Li 1143793fc096SFrank Li if (fep->bufdesc_ex) 1144f7995922SCsókás Bence ecntl |= FEC_ECR_EN1588; 1145793fc096SFrank Li 1146fc539459SFugang Duan if (fep->quirks & FEC_QUIRK_DELAYED_CLKS_SUPPORT && 1147fc539459SFugang Duan fep->rgmii_txc_dly) 1148fc539459SFugang Duan ecntl |= FEC_ENET_TXC_DLY; 1149fc539459SFugang Duan if (fep->quirks & FEC_QUIRK_DELAYED_CLKS_SUPPORT && 1150fc539459SFugang Duan fep->rgmii_rxc_dly) 1151fc539459SFugang Duan ecntl |= FEC_ENET_RXC_DLY; 1152fc539459SFugang Duan 115338ae92dcSChris Healy #ifndef CONFIG_M5272 1154b9eef55cSJim Baxter /* Enable the MIB statistic event counters */ 1155b9eef55cSJim Baxter writel(0 << 31, fep->hwp + FEC_MIB_CTRLSTAT); 115638ae92dcSChris Healy #endif 115738ae92dcSChris Healy 1158793fc096SFrank Li /* And last, enable the transmit and receive processing */ 1159793fc096SFrank Li writel(ecntl, fep->hwp + FEC_ECNTRL); 1160ce99d0d3SFrank Li fec_enet_active_rxring(ndev); 1161793fc096SFrank Li 1162793fc096SFrank Li if (fep->bufdesc_ex) 1163793fc096SFrank Li fec_ptp_start_cyclecounter(ndev); 1164793fc096SFrank Li 1165f7995922SCsókás Bence /* Restart PPS if needed */ 1166f7995922SCsókás Bence if (fep->pps_enable) { 1167f7995922SCsókás Bence /* Clear flag so fec_ptp_enable_pps() doesn't return immediately */ 1168f7995922SCsókás Bence fep->pps_enable = 0; 1169f7995922SCsókás Bence fec_ptp_restore_state(fep); 1170f7995922SCsókás Bence fep->ptp_caps.enable(&fep->ptp_caps, &ptp_rq, 1); 1171f7995922SCsókás Bence } 1172f7995922SCsókás Bence 1173793fc096SFrank Li /* Enable interrupts we wish to service */ 11740c5a3aefSNimrod Andy if (fep->link) 1175793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 11760c5a3aefSNimrod Andy else 1177f166f890SAndrew Lunn writel(0, fep->hwp + FEC_IMASK); 1178d851b47bSFugang Duan 1179d851b47bSFugang Duan /* Init the interrupt coalescing */ 1180d851b47bSFugang Duan fec_enet_itr_coal_init(ndev); 1181d851b47bSFugang Duan 1182793fc096SFrank Li } 1183793fc096SFrank Li 1184*40c79ce1SWei Fang static int fec_enet_ipc_handle_init(struct fec_enet_private *fep) 1185*40c79ce1SWei Fang { 1186*40c79ce1SWei Fang if (!(of_machine_is_compatible("fsl,imx8qm") || 1187*40c79ce1SWei Fang of_machine_is_compatible("fsl,imx8qxp") || 1188*40c79ce1SWei Fang of_machine_is_compatible("fsl,imx8dxl"))) 1189*40c79ce1SWei Fang return 0; 1190*40c79ce1SWei Fang 1191*40c79ce1SWei Fang return imx_scu_get_handle(&fep->ipc_handle); 1192*40c79ce1SWei Fang } 1193*40c79ce1SWei Fang 1194*40c79ce1SWei Fang static void fec_enet_ipg_stop_set(struct fec_enet_private *fep, bool enabled) 1195*40c79ce1SWei Fang { 1196*40c79ce1SWei Fang struct device_node *np = fep->pdev->dev.of_node; 1197*40c79ce1SWei Fang u32 rsrc_id, val; 1198*40c79ce1SWei Fang int idx; 1199*40c79ce1SWei Fang 1200*40c79ce1SWei Fang if (!np || !fep->ipc_handle) 1201*40c79ce1SWei Fang return; 1202*40c79ce1SWei Fang 1203*40c79ce1SWei Fang idx = of_alias_get_id(np, "ethernet"); 1204*40c79ce1SWei Fang if (idx < 0) 1205*40c79ce1SWei Fang idx = 0; 1206*40c79ce1SWei Fang rsrc_id = idx ? IMX_SC_R_ENET_1 : IMX_SC_R_ENET_0; 1207*40c79ce1SWei Fang 1208*40c79ce1SWei Fang val = enabled ? 1 : 0; 1209*40c79ce1SWei Fang imx_sc_misc_set_control(fep->ipc_handle, rsrc_id, IMX_SC_C_IPG_STOP, val); 1210*40c79ce1SWei Fang } 1211*40c79ce1SWei Fang 1212da722186SMartin Fuzzey static void fec_enet_stop_mode(struct fec_enet_private *fep, bool enabled) 1213da722186SMartin Fuzzey { 1214da722186SMartin Fuzzey struct fec_platform_data *pdata = fep->pdev->dev.platform_data; 1215da722186SMartin Fuzzey struct fec_stop_mode_gpr *stop_gpr = &fep->stop_gpr; 1216da722186SMartin Fuzzey 1217da722186SMartin Fuzzey if (stop_gpr->gpr) { 1218da722186SMartin Fuzzey if (enabled) 1219da722186SMartin Fuzzey regmap_update_bits(stop_gpr->gpr, stop_gpr->reg, 1220da722186SMartin Fuzzey BIT(stop_gpr->bit), 1221da722186SMartin Fuzzey BIT(stop_gpr->bit)); 1222da722186SMartin Fuzzey else 1223da722186SMartin Fuzzey regmap_update_bits(stop_gpr->gpr, stop_gpr->reg, 1224da722186SMartin Fuzzey BIT(stop_gpr->bit), 0); 1225da722186SMartin Fuzzey } else if (pdata && pdata->sleep_mode_enable) { 1226da722186SMartin Fuzzey pdata->sleep_mode_enable(enabled); 1227*40c79ce1SWei Fang } else { 1228*40c79ce1SWei Fang fec_enet_ipg_stop_set(fep, enabled); 1229da722186SMartin Fuzzey } 1230da722186SMartin Fuzzey } 1231da722186SMartin Fuzzey 12320b6f65c7SJoakim Zhang static void fec_irqs_disable(struct net_device *ndev) 12330b6f65c7SJoakim Zhang { 12340b6f65c7SJoakim Zhang struct fec_enet_private *fep = netdev_priv(ndev); 12350b6f65c7SJoakim Zhang 12360b6f65c7SJoakim Zhang writel(0, fep->hwp + FEC_IMASK); 12370b6f65c7SJoakim Zhang } 12380b6f65c7SJoakim Zhang 12390b6f65c7SJoakim Zhang static void fec_irqs_disable_except_wakeup(struct net_device *ndev) 12400b6f65c7SJoakim Zhang { 12410b6f65c7SJoakim Zhang struct fec_enet_private *fep = netdev_priv(ndev); 12420b6f65c7SJoakim Zhang 12430b6f65c7SJoakim Zhang writel(0, fep->hwp + FEC_IMASK); 12440b6f65c7SJoakim Zhang writel(FEC_ENET_WAKEUP, fep->hwp + FEC_IMASK); 12450b6f65c7SJoakim Zhang } 12460b6f65c7SJoakim Zhang 1247793fc096SFrank Li static void 1248793fc096SFrank Li fec_stop(struct net_device *ndev) 1249793fc096SFrank Li { 1250793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1251793fc096SFrank Li u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8); 1252de40ed31SNimrod Andy u32 val; 1253f7995922SCsókás Bence struct ptp_clock_request ptp_rq = { .type = PTP_CLK_REQ_PPS }; 1254f7995922SCsókás Bence u32 ecntl = 0; 1255793fc096SFrank Li 1256793fc096SFrank Li /* We cannot expect a graceful transmit stop without link !!! */ 1257793fc096SFrank Li if (fep->link) { 1258793fc096SFrank Li writel(1, fep->hwp + FEC_X_CNTRL); /* Graceful transmit stop */ 1259793fc096SFrank Li udelay(10); 1260793fc096SFrank Li if (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_GRA)) 126131b7720cSJoe Perches netdev_err(ndev, "Graceful transmit stop did not complete!\n"); 1262793fc096SFrank Li } 1263793fc096SFrank Li 1264f7995922SCsókás Bence fec_ptp_save_state(fep); 1265f7995922SCsókás Bence 1266106c314cSFugang Duan /* Whack a reset. We should wait for this. 1267106c314cSFugang Duan * For i.MX6SX SOC, enet use AXI bus, we use disable MAC 1268106c314cSFugang Duan * instead of reset MAC itself. 1269106c314cSFugang Duan */ 1270de40ed31SNimrod Andy if (!(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { 1271471ff445SJoakim Zhang if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) { 1272106c314cSFugang Duan writel(0, fep->hwp + FEC_ECNTRL); 1273106c314cSFugang Duan } else { 1274793fc096SFrank Li writel(1, fep->hwp + FEC_ECNTRL); 1275793fc096SFrank Li udelay(10); 1276106c314cSFugang Duan } 1277de40ed31SNimrod Andy } else { 1278de40ed31SNimrod Andy val = readl(fep->hwp + FEC_ECNTRL); 1279de40ed31SNimrod Andy val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP); 1280de40ed31SNimrod Andy writel(val, fep->hwp + FEC_ECNTRL); 1281de40ed31SNimrod Andy } 1282de40ed31SNimrod Andy writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 12830b6f65c7SJoakim Zhang writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 1284793fc096SFrank Li 1285f7995922SCsókás Bence if (fep->bufdesc_ex) 1286f7995922SCsókás Bence ecntl |= FEC_ECR_EN1588; 1287f7995922SCsókás Bence 1288793fc096SFrank Li /* We have to keep ENET enabled to have MII interrupt stay working */ 1289de40ed31SNimrod Andy if (fep->quirks & FEC_QUIRK_ENET_MAC && 1290de40ed31SNimrod Andy !(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { 1291f7995922SCsókás Bence ecntl |= FEC_ECR_ETHEREN; 1292793fc096SFrank Li writel(rmii_mode, fep->hwp + FEC_R_CNTRL); 1293793fc096SFrank Li } 1294f7995922SCsókás Bence 1295f7995922SCsókás Bence writel(ecntl, fep->hwp + FEC_ECNTRL); 1296f7995922SCsókás Bence 1297f7995922SCsókás Bence if (fep->bufdesc_ex) 1298f7995922SCsókás Bence fec_ptp_start_cyclecounter(ndev); 1299f7995922SCsókás Bence 1300f7995922SCsókás Bence /* Restart PPS if needed */ 1301f7995922SCsókás Bence if (fep->pps_enable) { 1302f7995922SCsókás Bence /* Clear flag so fec_ptp_enable_pps() doesn't return immediately */ 1303f7995922SCsókás Bence fep->pps_enable = 0; 1304f7995922SCsókás Bence fec_ptp_restore_state(fep); 1305f7995922SCsókás Bence fep->ptp_caps.enable(&fep->ptp_caps, &ptp_rq, 1); 1306f7995922SCsókás Bence } 1307793fc096SFrank Li } 1308793fc096SFrank Li 1309793fc096SFrank Li 1310793fc096SFrank Li static void 13110290bd29SMichael S. Tsirkin fec_timeout(struct net_device *ndev, unsigned int txqueue) 1312793fc096SFrank Li { 1313793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1314793fc096SFrank Li 1315344756f6SRussell King fec_dump(ndev); 1316344756f6SRussell King 1317793fc096SFrank Li ndev->stats.tx_errors++; 1318793fc096SFrank Li 131936cdc743SRussell King schedule_work(&fep->tx_timeout_work); 132054309fa6SFrank Li } 132154309fa6SFrank Li 132236cdc743SRussell King static void fec_enet_timeout_work(struct work_struct *work) 132354309fa6SFrank Li { 132454309fa6SFrank Li struct fec_enet_private *fep = 132536cdc743SRussell King container_of(work, struct fec_enet_private, tx_timeout_work); 13268ce5624fSRussell King struct net_device *ndev = fep->netdev; 132754309fa6SFrank Li 1328da1774e5SRussell King rtnl_lock(); 13298ce5624fSRussell King if (netif_device_present(ndev) || netif_running(ndev)) { 1330dbc64a8eSRussell King napi_disable(&fep->napi); 1331dbc64a8eSRussell King netif_tx_lock_bh(ndev); 1332ef83337dSRussell King fec_restart(ndev); 1333657ade07SRickard x Andersson netif_tx_wake_all_queues(ndev); 13346af42d42SRussell King netif_tx_unlock_bh(ndev); 1335dbc64a8eSRussell King napi_enable(&fep->napi); 13368ce5624fSRussell King } 1337da1774e5SRussell King rtnl_unlock(); 133854309fa6SFrank Li } 1339793fc096SFrank Li 1340793fc096SFrank Li static void 1341bfd4ecddSRussell King fec_enet_hwtstamp(struct fec_enet_private *fep, unsigned ts, 1342bfd4ecddSRussell King struct skb_shared_hwtstamps *hwtstamps) 1343bfd4ecddSRussell King { 1344bfd4ecddSRussell King unsigned long flags; 1345bfd4ecddSRussell King u64 ns; 1346bfd4ecddSRussell King 1347bfd4ecddSRussell King spin_lock_irqsave(&fep->tmreg_lock, flags); 1348bfd4ecddSRussell King ns = timecounter_cyc2time(&fep->tc, ts); 1349bfd4ecddSRussell King spin_unlock_irqrestore(&fep->tmreg_lock, flags); 1350bfd4ecddSRussell King 1351bfd4ecddSRussell King memset(hwtstamps, 0, sizeof(*hwtstamps)); 1352bfd4ecddSRussell King hwtstamps->hwtstamp = ns_to_ktime(ns); 1353bfd4ecddSRussell King } 1354bfd4ecddSRussell King 1355bfd4ecddSRussell King static void 13564d494cdcSFugang Duan fec_enet_tx_queue(struct net_device *ndev, u16 queue_id) 1357793fc096SFrank Li { 1358793fc096SFrank Li struct fec_enet_private *fep; 1359a2fe37b6SFabio Estevam struct bufdesc *bdp; 1360793fc096SFrank Li unsigned short status; 1361793fc096SFrank Li struct sk_buff *skb; 13624d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 13634d494cdcSFugang Duan struct netdev_queue *nq; 1364793fc096SFrank Li int index = 0; 136579f33912SNimrod Andy int entries_free; 1366793fc096SFrank Li 1367793fc096SFrank Li fep = netdev_priv(ndev); 13684d494cdcSFugang Duan 13694d494cdcSFugang Duan txq = fep->tx_queue[queue_id]; 13704d494cdcSFugang Duan /* get next bdp of dirty_tx */ 13714d494cdcSFugang Duan nq = netdev_get_tx_queue(ndev, queue_id); 13724d494cdcSFugang Duan bdp = txq->dirty_tx; 1373793fc096SFrank Li 1374793fc096SFrank Li /* get next bdp of dirty_tx */ 13757355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 1376793fc096SFrank Li 13777355f276STroy Kisky while (bdp != READ_ONCE(txq->bd.cur)) { 13787355f276STroy Kisky /* Order the load of bd.cur and cbd_sc */ 1379c4bc44c6SKevin Hao rmb(); 13805cfa3039SJohannes Berg status = fec16_to_cpu(READ_ONCE(bdp->cbd_sc)); 1381c4bc44c6SKevin Hao if (status & BD_ENET_TX_READY) 1382793fc096SFrank Li break; 1383793fc096SFrank Li 13847355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 13852b995f63SNimrod Andy 1386a2fe37b6SFabio Estevam skb = txq->tx_skbuff[index]; 1387a2fe37b6SFabio Estevam txq->tx_skbuff[index] = NULL; 13885cfa3039SJohannes Berg if (!IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr))) 13895cfa3039SJohannes Berg dma_unmap_single(&fep->pdev->dev, 13905cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 13915cfa3039SJohannes Berg fec16_to_cpu(bdp->cbd_datlen), 13925cfa3039SJohannes Berg DMA_TO_DEVICE); 13935cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(0); 13947fafe803STroy Kisky if (!skb) 13957fafe803STroy Kisky goto skb_done; 1396793fc096SFrank Li 1397793fc096SFrank Li /* Check for errors. */ 1398793fc096SFrank Li if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC | 1399793fc096SFrank Li BD_ENET_TX_RL | BD_ENET_TX_UN | 1400793fc096SFrank Li BD_ENET_TX_CSL)) { 1401793fc096SFrank Li ndev->stats.tx_errors++; 1402793fc096SFrank Li if (status & BD_ENET_TX_HB) /* No heartbeat */ 1403793fc096SFrank Li ndev->stats.tx_heartbeat_errors++; 1404793fc096SFrank Li if (status & BD_ENET_TX_LC) /* Late collision */ 1405793fc096SFrank Li ndev->stats.tx_window_errors++; 1406793fc096SFrank Li if (status & BD_ENET_TX_RL) /* Retrans limit */ 1407793fc096SFrank Li ndev->stats.tx_aborted_errors++; 1408793fc096SFrank Li if (status & BD_ENET_TX_UN) /* Underrun */ 1409793fc096SFrank Li ndev->stats.tx_fifo_errors++; 1410793fc096SFrank Li if (status & BD_ENET_TX_CSL) /* Carrier lost */ 1411793fc096SFrank Li ndev->stats.tx_carrier_errors++; 1412793fc096SFrank Li } else { 1413793fc096SFrank Li ndev->stats.tx_packets++; 14146e909283SNimrod Andy ndev->stats.tx_bytes += skb->len; 1415793fc096SFrank Li } 1416793fc096SFrank Li 141734074639SSergey Organov /* NOTE: SKBTX_IN_PROGRESS being set does not imply it's we who 141834074639SSergey Organov * are to time stamp the packet, so we still need to check time 141934074639SSergey Organov * stamping enabled flag. 142034074639SSergey Organov */ 142134074639SSergey Organov if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS && 142234074639SSergey Organov fep->hwts_tx_en) && 1423793fc096SFrank Li fep->bufdesc_ex) { 1424793fc096SFrank Li struct skb_shared_hwtstamps shhwtstamps; 1425793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 1426793fc096SFrank Li 14275cfa3039SJohannes Berg fec_enet_hwtstamp(fep, fec32_to_cpu(ebdp->ts), &shhwtstamps); 1428793fc096SFrank Li skb_tstamp_tx(skb, &shhwtstamps); 1429793fc096SFrank Li } 1430793fc096SFrank Li 1431793fc096SFrank Li /* Deferred means some collisions occurred during transmit, 1432793fc096SFrank Li * but we eventually sent the packet OK. 1433793fc096SFrank Li */ 1434793fc096SFrank Li if (status & BD_ENET_TX_DEF) 1435793fc096SFrank Li ndev->stats.collisions++; 1436793fc096SFrank Li 1437793fc096SFrank Li /* Free the sk buffer associated with this last transmit */ 1438793fc096SFrank Li dev_kfree_skb_any(skb); 14397fafe803STroy Kisky skb_done: 1440c4bc44c6SKevin Hao /* Make sure the update to bdp and tx_skbuff are performed 1441c4bc44c6SKevin Hao * before dirty_tx 1442c4bc44c6SKevin Hao */ 1443c4bc44c6SKevin Hao wmb(); 14444d494cdcSFugang Duan txq->dirty_tx = bdp; 1445793fc096SFrank Li 1446793fc096SFrank Li /* Update pointer to next buffer descriptor to be transmitted */ 14477355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 1448793fc096SFrank Li 1449793fc096SFrank Li /* Since we have freed up a buffer, the ring is no longer full 1450793fc096SFrank Li */ 1451657ade07SRickard x Andersson if (netif_tx_queue_stopped(nq)) { 14527355f276STroy Kisky entries_free = fec_enet_get_free_txdesc_num(txq); 14534d494cdcSFugang Duan if (entries_free >= txq->tx_wake_threshold) 14544d494cdcSFugang Duan netif_tx_wake_queue(nq); 1455793fc096SFrank Li } 145679f33912SNimrod Andy } 1457ccea2968SRussell King 1458c10bc0e7SFugang Duan /* ERR006358: Keep the transmitter going */ 14597355f276STroy Kisky if (bdp != txq->bd.cur && 146053bb20d1STroy Kisky readl(txq->bd.reg_desc_active) == 0) 146153bb20d1STroy Kisky writel(0, txq->bd.reg_desc_active); 14624d494cdcSFugang Duan } 14634d494cdcSFugang Duan 14647cdaa4ccSTobias Waldekranz static void fec_enet_tx(struct net_device *ndev) 14654d494cdcSFugang Duan { 14664d494cdcSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 14677cdaa4ccSTobias Waldekranz int i; 14687cdaa4ccSTobias Waldekranz 14697cdaa4ccSTobias Waldekranz /* Make sure that AVB queues are processed first. */ 14707cdaa4ccSTobias Waldekranz for (i = fep->num_tx_queues - 1; i >= 0; i--) 14717cdaa4ccSTobias Waldekranz fec_enet_tx_queue(ndev, i); 1472793fc096SFrank Li } 1473793fc096SFrank Li 14741b7bde6dSNimrod Andy static int 14751b7bde6dSNimrod Andy fec_enet_new_rxbdp(struct net_device *ndev, struct bufdesc *bdp, struct sk_buff *skb) 14761b7bde6dSNimrod Andy { 14771b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 14781b7bde6dSNimrod Andy int off; 14791b7bde6dSNimrod Andy 14801b7bde6dSNimrod Andy off = ((unsigned long)skb->data) & fep->rx_align; 14811b7bde6dSNimrod Andy if (off) 14821b7bde6dSNimrod Andy skb_reserve(skb, fep->rx_align + 1 - off); 14831b7bde6dSNimrod Andy 14845cfa3039SJohannes 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)); 14855cfa3039SJohannes Berg if (dma_mapping_error(&fep->pdev->dev, fec32_to_cpu(bdp->cbd_bufaddr))) { 14861b7bde6dSNimrod Andy if (net_ratelimit()) 14871b7bde6dSNimrod Andy netdev_err(ndev, "Rx DMA memory map failed\n"); 14881b7bde6dSNimrod Andy return -ENOMEM; 14891b7bde6dSNimrod Andy } 14901b7bde6dSNimrod Andy 14911b7bde6dSNimrod Andy return 0; 14921b7bde6dSNimrod Andy } 14931b7bde6dSNimrod Andy 14941b7bde6dSNimrod Andy static bool fec_enet_copybreak(struct net_device *ndev, struct sk_buff **skb, 14951310b544SLothar Waßmann struct bufdesc *bdp, u32 length, bool swap) 14961b7bde6dSNimrod Andy { 14971b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 14981b7bde6dSNimrod Andy struct sk_buff *new_skb; 14991b7bde6dSNimrod Andy 15001b7bde6dSNimrod Andy if (length > fep->rx_copybreak) 15011b7bde6dSNimrod Andy return false; 15021b7bde6dSNimrod Andy 15031b7bde6dSNimrod Andy new_skb = netdev_alloc_skb(ndev, length); 15041b7bde6dSNimrod Andy if (!new_skb) 15051b7bde6dSNimrod Andy return false; 15061b7bde6dSNimrod Andy 15075cfa3039SJohannes Berg dma_sync_single_for_cpu(&fep->pdev->dev, 15085cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 15091b7bde6dSNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 15101b7bde6dSNimrod Andy DMA_FROM_DEVICE); 15111310b544SLothar Waßmann if (!swap) 15121b7bde6dSNimrod Andy memcpy(new_skb->data, (*skb)->data, length); 15131310b544SLothar Waßmann else 15141310b544SLothar Waßmann swap_buffer2(new_skb->data, (*skb)->data, length); 15151b7bde6dSNimrod Andy *skb = new_skb; 15161b7bde6dSNimrod Andy 15171b7bde6dSNimrod Andy return true; 15181b7bde6dSNimrod Andy } 15191b7bde6dSNimrod Andy 15207355f276STroy Kisky /* During a receive, the bd_rx.cur points to the current incoming buffer. 1521793fc096SFrank Li * When we update through the ring, if the next incoming buffer has 1522793fc096SFrank Li * not been given to the system, we just set the empty indicator, 1523793fc096SFrank Li * effectively tossing the packet. 1524793fc096SFrank Li */ 1525793fc096SFrank Li static int 15264d494cdcSFugang Duan fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) 1527793fc096SFrank Li { 1528793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 15294d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 1530793fc096SFrank Li struct bufdesc *bdp; 1531793fc096SFrank Li unsigned short status; 15321b7bde6dSNimrod Andy struct sk_buff *skb_new = NULL; 1533793fc096SFrank Li struct sk_buff *skb; 1534793fc096SFrank Li ushort pkt_len; 1535793fc096SFrank Li __u8 *data; 1536793fc096SFrank Li int pkt_received = 0; 1537cdffcf1bSJim Baxter struct bufdesc_ex *ebdp = NULL; 1538cdffcf1bSJim Baxter bool vlan_packet_rcvd = false; 1539cdffcf1bSJim Baxter u16 vlan_tag; 1540d842a31fSDuan Fugang-B38611 int index = 0; 15411b7bde6dSNimrod Andy bool is_copybreak; 15426b7e4008SLothar Waßmann bool need_swap = fep->quirks & FEC_QUIRK_SWAP_FRAME; 1543793fc096SFrank Li 1544793fc096SFrank Li #ifdef CONFIG_M532x 1545793fc096SFrank Li flush_cache_all(); 1546793fc096SFrank Li #endif 15474d494cdcSFugang Duan rxq = fep->rx_queue[queue_id]; 1548793fc096SFrank Li 1549793fc096SFrank Li /* First, grab all of the stats for the incoming packet. 1550793fc096SFrank Li * These get messed up if we get called due to a busy condition. 1551793fc096SFrank Li */ 15527355f276STroy Kisky bdp = rxq->bd.cur; 1553793fc096SFrank Li 15545cfa3039SJohannes Berg while (!((status = fec16_to_cpu(bdp->cbd_sc)) & BD_ENET_RX_EMPTY)) { 1555793fc096SFrank Li 1556793fc096SFrank Li if (pkt_received >= budget) 1557793fc096SFrank Li break; 1558793fc096SFrank Li pkt_received++; 1559793fc096SFrank Li 1560b5bd95d1SJoakim Zhang writel(FEC_ENET_RXF_GET(queue_id), fep->hwp + FEC_IEVENT); 1561db3421c1SRussell King 1562793fc096SFrank Li /* Check for errors. */ 1563095098e1STroy Kisky status ^= BD_ENET_RX_LAST; 1564793fc096SFrank Li if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | 1565095098e1STroy Kisky BD_ENET_RX_CR | BD_ENET_RX_OV | BD_ENET_RX_LAST | 1566095098e1STroy Kisky BD_ENET_RX_CL)) { 1567793fc096SFrank Li ndev->stats.rx_errors++; 1568095098e1STroy Kisky if (status & BD_ENET_RX_OV) { 1569095098e1STroy Kisky /* FIFO overrun */ 1570095098e1STroy Kisky ndev->stats.rx_fifo_errors++; 1571095098e1STroy Kisky goto rx_processing_done; 1572095098e1STroy Kisky } 1573095098e1STroy Kisky if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH 1574095098e1STroy Kisky | BD_ENET_RX_LAST)) { 1575793fc096SFrank Li /* Frame too long or too short. */ 1576793fc096SFrank Li ndev->stats.rx_length_errors++; 1577095098e1STroy Kisky if (status & BD_ENET_RX_LAST) 1578095098e1STroy Kisky netdev_err(ndev, "rcv is not +last\n"); 1579793fc096SFrank Li } 1580793fc096SFrank Li if (status & BD_ENET_RX_CR) /* CRC Error */ 1581793fc096SFrank Li ndev->stats.rx_crc_errors++; 1582095098e1STroy Kisky /* Report late collisions as a frame error. */ 1583095098e1STroy Kisky if (status & (BD_ENET_RX_NO | BD_ENET_RX_CL)) 1584793fc096SFrank Li ndev->stats.rx_frame_errors++; 1585793fc096SFrank Li goto rx_processing_done; 1586793fc096SFrank Li } 1587793fc096SFrank Li 1588793fc096SFrank Li /* Process the incoming frame. */ 1589793fc096SFrank Li ndev->stats.rx_packets++; 15905cfa3039SJohannes Berg pkt_len = fec16_to_cpu(bdp->cbd_datlen); 1591793fc096SFrank Li ndev->stats.rx_bytes += pkt_len; 1592793fc096SFrank Li 15937355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &rxq->bd); 15941b7bde6dSNimrod Andy skb = rxq->rx_skbuff[index]; 15951b7bde6dSNimrod Andy 15961b7bde6dSNimrod Andy /* The packet length includes FCS, but we don't want to 15971b7bde6dSNimrod Andy * include that when passing upstream as it messes up 15981b7bde6dSNimrod Andy * bridging applications. 15991b7bde6dSNimrod Andy */ 16001310b544SLothar Waßmann is_copybreak = fec_enet_copybreak(ndev, &skb, bdp, pkt_len - 4, 16011310b544SLothar Waßmann need_swap); 16021b7bde6dSNimrod Andy if (!is_copybreak) { 16031b7bde6dSNimrod Andy skb_new = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE); 16041b7bde6dSNimrod Andy if (unlikely(!skb_new)) { 16051b7bde6dSNimrod Andy ndev->stats.rx_dropped++; 16061b7bde6dSNimrod Andy goto rx_processing_done; 16071b7bde6dSNimrod Andy } 16085cfa3039SJohannes Berg dma_unmap_single(&fep->pdev->dev, 16095cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 1610b64bf4b7SNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 1611b64bf4b7SNimrod Andy DMA_FROM_DEVICE); 16121b7bde6dSNimrod Andy } 1613793fc096SFrank Li 16141b7bde6dSNimrod Andy prefetch(skb->data - NET_IP_ALIGN); 16151b7bde6dSNimrod Andy skb_put(skb, pkt_len - 4); 16161b7bde6dSNimrod Andy data = skb->data; 16173ac72b7bSEric Nelson 1618235bde1eSFabio Estevam if (!is_copybreak && need_swap) 1619235bde1eSFabio Estevam swap_buffer(data, pkt_len); 1620235bde1eSFabio Estevam 16213ac72b7bSEric Nelson #if !defined(CONFIG_M5272) 16223ac72b7bSEric Nelson if (fep->quirks & FEC_QUIRK_HAS_RACC) 16233ac72b7bSEric Nelson data = skb_pull_inline(skb, 2); 16243ac72b7bSEric Nelson #endif 16253ac72b7bSEric Nelson 1626cdffcf1bSJim Baxter /* Extract the enhanced buffer descriptor */ 1627cdffcf1bSJim Baxter ebdp = NULL; 1628cdffcf1bSJim Baxter if (fep->bufdesc_ex) 1629cdffcf1bSJim Baxter ebdp = (struct bufdesc_ex *)bdp; 1630cdffcf1bSJim Baxter 1631cdffcf1bSJim Baxter /* If this is a VLAN packet remove the VLAN Tag */ 1632cdffcf1bSJim Baxter vlan_packet_rcvd = false; 1633cdffcf1bSJim Baxter if ((ndev->features & NETIF_F_HW_VLAN_CTAG_RX) && 16345cfa3039SJohannes Berg fep->bufdesc_ex && 16355cfa3039SJohannes Berg (ebdp->cbd_esc & cpu_to_fec32(BD_ENET_RX_VLAN))) { 1636cdffcf1bSJim Baxter /* Push and remove the vlan tag */ 1637cdffcf1bSJim Baxter struct vlan_hdr *vlan_header = 1638cdffcf1bSJim Baxter (struct vlan_hdr *) (data + ETH_HLEN); 1639cdffcf1bSJim Baxter vlan_tag = ntohs(vlan_header->h_vlan_TCI); 1640cdffcf1bSJim Baxter 1641cdffcf1bSJim Baxter vlan_packet_rcvd = true; 16421b7bde6dSNimrod Andy 1643af5cbc98SNimrod Andy memmove(skb->data + VLAN_HLEN, data, ETH_ALEN * 2); 16441b7bde6dSNimrod Andy skb_pull(skb, VLAN_HLEN); 1645cdffcf1bSJim Baxter } 1646cdffcf1bSJim Baxter 1647793fc096SFrank Li skb->protocol = eth_type_trans(skb, ndev); 1648793fc096SFrank Li 1649793fc096SFrank Li /* Get receive timestamp from the skb */ 1650bfd4ecddSRussell King if (fep->hwts_rx_en && fep->bufdesc_ex) 16515cfa3039SJohannes Berg fec_enet_hwtstamp(fep, fec32_to_cpu(ebdp->ts), 1652bfd4ecddSRussell King skb_hwtstamps(skb)); 1653793fc096SFrank Li 16544c09eed9SJim Baxter if (fep->bufdesc_ex && 16554c09eed9SJim Baxter (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { 16565cfa3039SJohannes Berg if (!(ebdp->cbd_esc & cpu_to_fec32(FLAG_RX_CSUM_ERROR))) { 16574c09eed9SJim Baxter /* don't check it */ 16584c09eed9SJim Baxter skb->ip_summed = CHECKSUM_UNNECESSARY; 16594c09eed9SJim Baxter } else { 16604c09eed9SJim Baxter skb_checksum_none_assert(skb); 16614c09eed9SJim Baxter } 16624c09eed9SJim Baxter } 16634c09eed9SJim Baxter 1664cdffcf1bSJim Baxter /* Handle received VLAN packets */ 1665cdffcf1bSJim Baxter if (vlan_packet_rcvd) 1666cdffcf1bSJim Baxter __vlan_hwaccel_put_tag(skb, 1667cdffcf1bSJim Baxter htons(ETH_P_8021Q), 1668cdffcf1bSJim Baxter vlan_tag); 1669cdffcf1bSJim Baxter 16707cdaa4ccSTobias Waldekranz skb_record_rx_queue(skb, queue_id); 1671793fc096SFrank Li napi_gro_receive(&fep->napi, skb); 1672793fc096SFrank Li 16731b7bde6dSNimrod Andy if (is_copybreak) { 16745cfa3039SJohannes Berg dma_sync_single_for_device(&fep->pdev->dev, 16755cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 1676b64bf4b7SNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 1677b64bf4b7SNimrod Andy DMA_FROM_DEVICE); 16781b7bde6dSNimrod Andy } else { 16791b7bde6dSNimrod Andy rxq->rx_skbuff[index] = skb_new; 16801b7bde6dSNimrod Andy fec_enet_new_rxbdp(ndev, bdp, skb_new); 16811b7bde6dSNimrod Andy } 16821b7bde6dSNimrod Andy 1683793fc096SFrank Li rx_processing_done: 1684793fc096SFrank Li /* Clear the status flags for this buffer */ 1685793fc096SFrank Li status &= ~BD_ENET_RX_STATS; 1686793fc096SFrank Li 1687793fc096SFrank Li /* Mark the buffer empty */ 1688793fc096SFrank Li status |= BD_ENET_RX_EMPTY; 1689793fc096SFrank Li 1690793fc096SFrank Li if (fep->bufdesc_ex) { 1691793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 1692793fc096SFrank Li 16935cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(BD_ENET_RX_INT); 1694793fc096SFrank Li ebdp->cbd_prot = 0; 1695793fc096SFrank Li ebdp->cbd_bdu = 0; 1696793fc096SFrank Li } 1697be293467STroy Kisky /* Make sure the updates to rest of the descriptor are 1698be293467STroy Kisky * performed before transferring ownership. 1699be293467STroy Kisky */ 1700be293467STroy Kisky wmb(); 1701be293467STroy Kisky bdp->cbd_sc = cpu_to_fec16(status); 1702793fc096SFrank Li 1703793fc096SFrank Li /* Update BD pointer to next entry */ 17047355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 170536e24e2eSDuan Fugang-B38611 1706793fc096SFrank Li /* Doing this here will keep the FEC running while we process 1707793fc096SFrank Li * incoming frames. On a heavily loaded network, we should be 1708793fc096SFrank Li * able to keep up at the expense of system resources. 1709793fc096SFrank Li */ 171053bb20d1STroy Kisky writel(0, rxq->bd.reg_desc_active); 1711793fc096SFrank Li } 17127355f276STroy Kisky rxq->bd.cur = bdp; 1713793fc096SFrank Li return pkt_received; 1714793fc096SFrank Li } 1715793fc096SFrank Li 17167cdaa4ccSTobias Waldekranz static int fec_enet_rx(struct net_device *ndev, int budget) 17174d494cdcSFugang Duan { 17184d494cdcSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 17197cdaa4ccSTobias Waldekranz int i, done = 0; 17204d494cdcSFugang Duan 17217cdaa4ccSTobias Waldekranz /* Make sure that AVB queues are processed first. */ 17227cdaa4ccSTobias Waldekranz for (i = fep->num_rx_queues - 1; i >= 0; i--) 17237cdaa4ccSTobias Waldekranz done += fec_enet_rx_queue(ndev, budget - done, i); 17241c021bb7SUwe Kleine-König 17257cdaa4ccSTobias Waldekranz return done; 17264d494cdcSFugang Duan } 17274d494cdcSFugang Duan 17287cdaa4ccSTobias Waldekranz static bool fec_enet_collect_events(struct fec_enet_private *fep) 17294d494cdcSFugang Duan { 1730793fc096SFrank Li uint int_events; 1731793fc096SFrank Li 1732793fc096SFrank Li int_events = readl(fep->hwp + FEC_IEVENT); 1733f166f890SAndrew Lunn 1734f166f890SAndrew Lunn /* Don't clear MDIO events, we poll for those */ 1735f166f890SAndrew Lunn int_events &= ~FEC_ENET_MII; 1736f166f890SAndrew Lunn 173794191fd6SNimrod Andy writel(int_events, fep->hwp + FEC_IEVENT); 1738793fc096SFrank Li 17397cdaa4ccSTobias Waldekranz return int_events != 0; 17407cdaa4ccSTobias Waldekranz } 17417cdaa4ccSTobias Waldekranz 17427cdaa4ccSTobias Waldekranz static irqreturn_t 17437cdaa4ccSTobias Waldekranz fec_enet_interrupt(int irq, void *dev_id) 17447cdaa4ccSTobias Waldekranz { 17457cdaa4ccSTobias Waldekranz struct net_device *ndev = dev_id; 17467cdaa4ccSTobias Waldekranz struct fec_enet_private *fep = netdev_priv(ndev); 17477cdaa4ccSTobias Waldekranz irqreturn_t ret = IRQ_NONE; 17487cdaa4ccSTobias Waldekranz 17497cdaa4ccSTobias Waldekranz if (fec_enet_collect_events(fep) && fep->link) { 1750793fc096SFrank Li ret = IRQ_HANDLED; 1751793fc096SFrank Li 175294191fd6SNimrod Andy if (napi_schedule_prep(&fep->napi)) { 1753f166f890SAndrew Lunn /* Disable interrupts */ 1754f166f890SAndrew Lunn writel(0, fep->hwp + FEC_IMASK); 175594191fd6SNimrod Andy __napi_schedule(&fep->napi); 175694191fd6SNimrod Andy } 1757793fc096SFrank Li } 1758793fc096SFrank Li 1759793fc096SFrank Li return ret; 1760793fc096SFrank Li } 1761793fc096SFrank Li 1762793fc096SFrank Li static int fec_enet_rx_napi(struct napi_struct *napi, int budget) 1763793fc096SFrank Li { 1764793fc096SFrank Li struct net_device *ndev = napi->dev; 1765793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 17667cdaa4ccSTobias Waldekranz int done = 0; 17677a16807cSRussell King 17687cdaa4ccSTobias Waldekranz do { 17697cdaa4ccSTobias Waldekranz done += fec_enet_rx(ndev, budget - done); 1770793fc096SFrank Li fec_enet_tx(ndev); 17717cdaa4ccSTobias Waldekranz } while ((done < budget) && fec_enet_collect_events(fep)); 1772793fc096SFrank Li 17737cdaa4ccSTobias Waldekranz if (done < budget) { 17747cdaa4ccSTobias Waldekranz napi_complete_done(napi, done); 1775793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 1776793fc096SFrank Li } 17777cdaa4ccSTobias Waldekranz 17787cdaa4ccSTobias Waldekranz return done; 1779793fc096SFrank Li } 1780793fc096SFrank Li 1781793fc096SFrank Li /* ------------------------------------------------------------------------- */ 1782052fcc45SFugang Duan static int fec_get_mac(struct net_device *ndev) 1783793fc096SFrank Li { 1784793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1785793fc096SFrank Li unsigned char *iap, tmpaddr[ETH_ALEN]; 178683216e39SMichael Walle int ret; 1787793fc096SFrank Li 1788793fc096SFrank Li /* 1789793fc096SFrank Li * try to get mac address in following order: 1790793fc096SFrank Li * 1791793fc096SFrank Li * 1) module parameter via kernel command line in form 1792793fc096SFrank Li * fec.macaddr=0x00,0x04,0x9f,0x01,0x30,0xe0 1793793fc096SFrank Li */ 1794793fc096SFrank Li iap = macaddr; 1795793fc096SFrank Li 1796793fc096SFrank Li /* 1797793fc096SFrank Li * 2) from device tree data 1798793fc096SFrank Li */ 1799793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 1800793fc096SFrank Li struct device_node *np = fep->pdev->dev.of_node; 1801793fc096SFrank Li if (np) { 180283216e39SMichael Walle ret = of_get_mac_address(np, tmpaddr); 180383216e39SMichael Walle if (!ret) 180483216e39SMichael Walle iap = tmpaddr; 1805052fcc45SFugang Duan else if (ret == -EPROBE_DEFER) 1806052fcc45SFugang Duan return ret; 1807793fc096SFrank Li } 1808793fc096SFrank Li } 1809793fc096SFrank Li 1810793fc096SFrank Li /* 1811793fc096SFrank Li * 3) from flash or fuse (via platform data) 1812793fc096SFrank Li */ 1813793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 1814793fc096SFrank Li #ifdef CONFIG_M5272 1815793fc096SFrank Li if (FEC_FLASHMAC) 1816793fc096SFrank Li iap = (unsigned char *)FEC_FLASHMAC; 1817793fc096SFrank Li #else 181832d1bbb1SGeert Uytterhoeven struct fec_platform_data *pdata = dev_get_platdata(&fep->pdev->dev); 181932d1bbb1SGeert Uytterhoeven 1820793fc096SFrank Li if (pdata) 1821793fc096SFrank Li iap = (unsigned char *)&pdata->mac; 1822793fc096SFrank Li #endif 1823793fc096SFrank Li } 1824793fc096SFrank Li 1825793fc096SFrank Li /* 1826793fc096SFrank Li * 4) FEC mac registers set by bootloader 1827793fc096SFrank Li */ 1828793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 18297d7628f3SDan Carpenter *((__be32 *) &tmpaddr[0]) = 18307d7628f3SDan Carpenter cpu_to_be32(readl(fep->hwp + FEC_ADDR_LOW)); 18317d7628f3SDan Carpenter *((__be16 *) &tmpaddr[4]) = 18327d7628f3SDan Carpenter cpu_to_be16(readl(fep->hwp + FEC_ADDR_HIGH) >> 16); 1833793fc096SFrank Li iap = &tmpaddr[0]; 1834793fc096SFrank Li } 1835793fc096SFrank Li 1836ff5b2fabSLucas Stach /* 1837ff5b2fabSLucas Stach * 5) random mac address 1838ff5b2fabSLucas Stach */ 1839ff5b2fabSLucas Stach if (!is_valid_ether_addr(iap)) { 1840ff5b2fabSLucas Stach /* Report it and use a random ethernet address instead */ 1841a19a0582SFabio Estevam dev_err(&fep->pdev->dev, "Invalid MAC address: %pM\n", iap); 1842ff5b2fabSLucas Stach eth_hw_addr_random(ndev); 1843a19a0582SFabio Estevam dev_info(&fep->pdev->dev, "Using random MAC address: %pM\n", 1844ff5b2fabSLucas Stach ndev->dev_addr); 1845052fcc45SFugang Duan return 0; 1846ff5b2fabSLucas Stach } 1847ff5b2fabSLucas Stach 1848793fc096SFrank Li /* Adjust MAC if using macaddr */ 1849ba3fdfe3SJakub Kicinski eth_hw_addr_gen(ndev, iap, iap == macaddr ? fep->dev_id : 0); 1850052fcc45SFugang Duan 1851052fcc45SFugang Duan return 0; 1852793fc096SFrank Li } 1853793fc096SFrank Li 1854793fc096SFrank Li /* ------------------------------------------------------------------------- */ 1855793fc096SFrank Li 1856793fc096SFrank Li /* 1857793fc096SFrank Li * Phy section 1858793fc096SFrank Li */ 1859793fc096SFrank Li static void fec_enet_adjust_link(struct net_device *ndev) 1860793fc096SFrank Li { 1861793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 186245f5c327SPhilippe Reynes struct phy_device *phy_dev = ndev->phydev; 1863793fc096SFrank Li int status_change = 0; 1864793fc096SFrank Li 18658ce5624fSRussell King /* 18668ce5624fSRussell King * If the netdev is down, or is going down, we're not interested 18678ce5624fSRussell King * in link state events, so just mark our idea of the link as down 18688ce5624fSRussell King * and ignore the event. 18698ce5624fSRussell King */ 18708ce5624fSRussell King if (!netif_running(ndev) || !netif_device_present(ndev)) { 18718ce5624fSRussell King fep->link = 0; 18728ce5624fSRussell King } else if (phy_dev->link) { 1873793fc096SFrank Li if (!fep->link) { 1874793fc096SFrank Li fep->link = phy_dev->link; 1875793fc096SFrank Li status_change = 1; 1876793fc096SFrank Li } 1877793fc096SFrank Li 1878ef83337dSRussell King if (fep->full_duplex != phy_dev->duplex) { 1879ef83337dSRussell King fep->full_duplex = phy_dev->duplex; 1880793fc096SFrank Li status_change = 1; 1881ef83337dSRussell King } 1882793fc096SFrank Li 1883793fc096SFrank Li if (phy_dev->speed != fep->speed) { 1884793fc096SFrank Li fep->speed = phy_dev->speed; 1885793fc096SFrank Li status_change = 1; 1886793fc096SFrank Li } 1887793fc096SFrank Li 1888793fc096SFrank Li /* if any of the above changed restart the FEC */ 1889dbc64a8eSRussell King if (status_change) { 1890dbc64a8eSRussell King napi_disable(&fep->napi); 1891dbc64a8eSRussell King netif_tx_lock_bh(ndev); 1892ef83337dSRussell King fec_restart(ndev); 1893657ade07SRickard x Andersson netif_tx_wake_all_queues(ndev); 18946af42d42SRussell King netif_tx_unlock_bh(ndev); 1895dbc64a8eSRussell King napi_enable(&fep->napi); 1896dbc64a8eSRussell King } 1897793fc096SFrank Li } else { 1898793fc096SFrank Li if (fep->link) { 1899f208ce10SRussell King napi_disable(&fep->napi); 1900f208ce10SRussell King netif_tx_lock_bh(ndev); 1901793fc096SFrank Li fec_stop(ndev); 1902f208ce10SRussell King netif_tx_unlock_bh(ndev); 1903f208ce10SRussell King napi_enable(&fep->napi); 19046e0895c2SDavid S. Miller fep->link = phy_dev->link; 1905793fc096SFrank Li status_change = 1; 1906793fc096SFrank Li } 1907793fc096SFrank Li } 1908793fc096SFrank Li 1909793fc096SFrank Li if (status_change) 1910793fc096SFrank Li phy_print_status(phy_dev); 1911793fc096SFrank Li } 1912793fc096SFrank Li 1913f166f890SAndrew Lunn static int fec_enet_mdio_wait(struct fec_enet_private *fep) 1914f166f890SAndrew Lunn { 1915f166f890SAndrew Lunn uint ievent; 1916f166f890SAndrew Lunn int ret; 1917f166f890SAndrew Lunn 1918f166f890SAndrew Lunn ret = readl_poll_timeout_atomic(fep->hwp + FEC_IEVENT, ievent, 1919f166f890SAndrew Lunn ievent & FEC_ENET_MII, 2, 30000); 1920f166f890SAndrew Lunn 1921f166f890SAndrew Lunn if (!ret) 1922f166f890SAndrew Lunn writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT); 1923f166f890SAndrew Lunn 1924f166f890SAndrew Lunn return ret; 1925f166f890SAndrew Lunn } 1926f166f890SAndrew Lunn 1927793fc096SFrank Li static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum) 1928793fc096SFrank Li { 1929793fc096SFrank Li struct fec_enet_private *fep = bus->priv; 19308fff755eSAndrew Lunn struct device *dev = &fep->pdev->dev; 1931d3ee8ec7SMarco Hartmann int ret = 0, frame_start, frame_addr, frame_op; 1932d3ee8ec7SMarco Hartmann bool is_c45 = !!(regnum & MII_ADDR_C45); 19338fff755eSAndrew Lunn 1934da875fa5SZhang Qilong ret = pm_runtime_resume_and_get(dev); 1935b0c6ce24SFabio Estevam if (ret < 0) 19368fff755eSAndrew Lunn return ret; 1937793fc096SFrank Li 1938d3ee8ec7SMarco Hartmann if (is_c45) { 1939d3ee8ec7SMarco Hartmann frame_start = FEC_MMFR_ST_C45; 1940d3ee8ec7SMarco Hartmann 1941d3ee8ec7SMarco Hartmann /* write address */ 1942d3ee8ec7SMarco Hartmann frame_addr = (regnum >> 16); 1943d3ee8ec7SMarco Hartmann writel(frame_start | FEC_MMFR_OP_ADDR_WRITE | 1944d3ee8ec7SMarco Hartmann FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | 1945d3ee8ec7SMarco Hartmann FEC_MMFR_TA | (regnum & 0xFFFF), 1946d3ee8ec7SMarco Hartmann fep->hwp + FEC_MII_DATA); 1947d3ee8ec7SMarco Hartmann 1948d3ee8ec7SMarco Hartmann /* wait for end of transfer */ 1949f166f890SAndrew Lunn ret = fec_enet_mdio_wait(fep); 1950f166f890SAndrew Lunn if (ret) { 1951d3ee8ec7SMarco Hartmann netdev_err(fep->netdev, "MDIO address write timeout\n"); 1952d3ee8ec7SMarco Hartmann goto out; 1953d3ee8ec7SMarco Hartmann } 1954d3ee8ec7SMarco Hartmann 1955d3ee8ec7SMarco Hartmann frame_op = FEC_MMFR_OP_READ_C45; 1956d3ee8ec7SMarco Hartmann 1957d3ee8ec7SMarco Hartmann } else { 1958d3ee8ec7SMarco Hartmann /* C22 read */ 1959d3ee8ec7SMarco Hartmann frame_op = FEC_MMFR_OP_READ; 1960d3ee8ec7SMarco Hartmann frame_start = FEC_MMFR_ST; 1961d3ee8ec7SMarco Hartmann frame_addr = regnum; 1962d3ee8ec7SMarco Hartmann } 1963d3ee8ec7SMarco Hartmann 1964793fc096SFrank Li /* start a read op */ 1965d3ee8ec7SMarco Hartmann writel(frame_start | frame_op | 1966d3ee8ec7SMarco Hartmann FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | 1967793fc096SFrank Li FEC_MMFR_TA, fep->hwp + FEC_MII_DATA); 1968793fc096SFrank Li 1969793fc096SFrank Li /* wait for end of transfer */ 1970f166f890SAndrew Lunn ret = fec_enet_mdio_wait(fep); 1971f166f890SAndrew Lunn if (ret) { 197231b7720cSJoe Perches netdev_err(fep->netdev, "MDIO read timeout\n"); 19738fff755eSAndrew Lunn goto out; 1974793fc096SFrank Li } 1975793fc096SFrank Li 19768fff755eSAndrew Lunn ret = FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA)); 19778fff755eSAndrew Lunn 19788fff755eSAndrew Lunn out: 19798fff755eSAndrew Lunn pm_runtime_mark_last_busy(dev); 19808fff755eSAndrew Lunn pm_runtime_put_autosuspend(dev); 19818fff755eSAndrew Lunn 19828fff755eSAndrew Lunn return ret; 1983793fc096SFrank Li } 1984793fc096SFrank Li 1985793fc096SFrank Li static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum, 1986793fc096SFrank Li u16 value) 1987793fc096SFrank Li { 1988793fc096SFrank Li struct fec_enet_private *fep = bus->priv; 19898fff755eSAndrew Lunn struct device *dev = &fep->pdev->dev; 1990d3ee8ec7SMarco Hartmann int ret, frame_start, frame_addr; 1991d3ee8ec7SMarco Hartmann bool is_c45 = !!(regnum & MII_ADDR_C45); 19928fff755eSAndrew Lunn 1993da875fa5SZhang Qilong ret = pm_runtime_resume_and_get(dev); 1994b0c6ce24SFabio Estevam if (ret < 0) 19958fff755eSAndrew Lunn return ret; 1996793fc096SFrank Li 1997d3ee8ec7SMarco Hartmann if (is_c45) { 1998d3ee8ec7SMarco Hartmann frame_start = FEC_MMFR_ST_C45; 1999d3ee8ec7SMarco Hartmann 2000d3ee8ec7SMarco Hartmann /* write address */ 2001d3ee8ec7SMarco Hartmann frame_addr = (regnum >> 16); 2002d3ee8ec7SMarco Hartmann writel(frame_start | FEC_MMFR_OP_ADDR_WRITE | 2003d3ee8ec7SMarco Hartmann FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | 2004d3ee8ec7SMarco Hartmann FEC_MMFR_TA | (regnum & 0xFFFF), 2005d3ee8ec7SMarco Hartmann fep->hwp + FEC_MII_DATA); 2006d3ee8ec7SMarco Hartmann 2007d3ee8ec7SMarco Hartmann /* wait for end of transfer */ 2008f166f890SAndrew Lunn ret = fec_enet_mdio_wait(fep); 2009f166f890SAndrew Lunn if (ret) { 2010d3ee8ec7SMarco Hartmann netdev_err(fep->netdev, "MDIO address write timeout\n"); 2011d3ee8ec7SMarco Hartmann goto out; 2012d3ee8ec7SMarco Hartmann } 2013d3ee8ec7SMarco Hartmann } else { 2014d3ee8ec7SMarco Hartmann /* C22 write */ 2015d3ee8ec7SMarco Hartmann frame_start = FEC_MMFR_ST; 2016d3ee8ec7SMarco Hartmann frame_addr = regnum; 2017d3ee8ec7SMarco Hartmann } 2018d3ee8ec7SMarco Hartmann 2019793fc096SFrank Li /* start a write op */ 2020d3ee8ec7SMarco Hartmann writel(frame_start | FEC_MMFR_OP_WRITE | 2021d3ee8ec7SMarco Hartmann FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | 2022793fc096SFrank Li FEC_MMFR_TA | FEC_MMFR_DATA(value), 2023793fc096SFrank Li fep->hwp + FEC_MII_DATA); 2024793fc096SFrank Li 2025793fc096SFrank Li /* wait for end of transfer */ 2026f166f890SAndrew Lunn ret = fec_enet_mdio_wait(fep); 2027f166f890SAndrew Lunn if (ret) 202831b7720cSJoe Perches netdev_err(fep->netdev, "MDIO write timeout\n"); 2029793fc096SFrank Li 2030d3ee8ec7SMarco Hartmann out: 20318fff755eSAndrew Lunn pm_runtime_mark_last_busy(dev); 20328fff755eSAndrew Lunn pm_runtime_put_autosuspend(dev); 20338fff755eSAndrew Lunn 20348fff755eSAndrew Lunn return ret; 2035793fc096SFrank Li } 2036793fc096SFrank Li 203764a632daSMarek Vasut static void fec_enet_phy_reset_after_clk_enable(struct net_device *ndev) 203864a632daSMarek Vasut { 203964a632daSMarek Vasut struct fec_enet_private *fep = netdev_priv(ndev); 204064a632daSMarek Vasut struct phy_device *phy_dev = ndev->phydev; 204164a632daSMarek Vasut 204264a632daSMarek Vasut if (phy_dev) { 204364a632daSMarek Vasut phy_reset_after_clk_enable(phy_dev); 204464a632daSMarek Vasut } else if (fep->phy_node) { 204564a632daSMarek Vasut /* 204664a632daSMarek Vasut * If the PHY still is not bound to the MAC, but there is 204764a632daSMarek Vasut * OF PHY node and a matching PHY device instance already, 204864a632daSMarek Vasut * use the OF PHY node to obtain the PHY device instance, 204964a632daSMarek Vasut * and then use that PHY device instance when triggering 205064a632daSMarek Vasut * the PHY reset. 205164a632daSMarek Vasut */ 205264a632daSMarek Vasut phy_dev = of_phy_find_device(fep->phy_node); 205364a632daSMarek Vasut phy_reset_after_clk_enable(phy_dev); 205464a632daSMarek Vasut put_device(&phy_dev->mdio.dev); 205564a632daSMarek Vasut } 205664a632daSMarek Vasut } 205764a632daSMarek Vasut 2058e8fcfcd5SNimrod Andy static int fec_enet_clk_enable(struct net_device *ndev, bool enable) 2059e8fcfcd5SNimrod Andy { 2060e8fcfcd5SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 2061e8fcfcd5SNimrod Andy int ret; 2062e8fcfcd5SNimrod Andy 2063e8fcfcd5SNimrod Andy if (enable) { 2064e8fcfcd5SNimrod Andy ret = clk_prepare_enable(fep->clk_enet_out); 2065e8fcfcd5SNimrod Andy if (ret) 2066d7c3a206SAndy Duan return ret; 206701e5943aSUwe Kleine-König 2068e8fcfcd5SNimrod Andy if (fep->clk_ptp) { 206991c0d987SNimrod Andy mutex_lock(&fep->ptp_clk_mutex); 2070e8fcfcd5SNimrod Andy ret = clk_prepare_enable(fep->clk_ptp); 207191c0d987SNimrod Andy if (ret) { 207291c0d987SNimrod Andy mutex_unlock(&fep->ptp_clk_mutex); 2073e8fcfcd5SNimrod Andy goto failed_clk_ptp; 207491c0d987SNimrod Andy } else { 207591c0d987SNimrod Andy fep->ptp_clk_on = true; 207691c0d987SNimrod Andy } 207791c0d987SNimrod Andy mutex_unlock(&fep->ptp_clk_mutex); 2078e8fcfcd5SNimrod Andy } 207901e5943aSUwe Kleine-König 20809b5330edSFugang Duan ret = clk_prepare_enable(fep->clk_ref); 20819b5330edSFugang Duan if (ret) 20829b5330edSFugang Duan goto failed_clk_ref; 20831b0a83acSRichard Leitner 2084fc539459SFugang Duan ret = clk_prepare_enable(fep->clk_2x_txclk); 2085fc539459SFugang Duan if (ret) 2086fc539459SFugang Duan goto failed_clk_2x_txclk; 2087fc539459SFugang Duan 208864a632daSMarek Vasut fec_enet_phy_reset_after_clk_enable(ndev); 2089e8fcfcd5SNimrod Andy } else { 2090e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_enet_out); 209191c0d987SNimrod Andy if (fep->clk_ptp) { 209291c0d987SNimrod Andy mutex_lock(&fep->ptp_clk_mutex); 2093e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_ptp); 209491c0d987SNimrod Andy fep->ptp_clk_on = false; 209591c0d987SNimrod Andy mutex_unlock(&fep->ptp_clk_mutex); 209691c0d987SNimrod Andy } 20979b5330edSFugang Duan clk_disable_unprepare(fep->clk_ref); 2098fc539459SFugang Duan clk_disable_unprepare(fep->clk_2x_txclk); 2099e8fcfcd5SNimrod Andy } 2100e8fcfcd5SNimrod Andy 2101e8fcfcd5SNimrod Andy return 0; 21029b5330edSFugang Duan 2103fc539459SFugang Duan failed_clk_2x_txclk: 2104fc539459SFugang Duan if (fep->clk_ref) 2105fc539459SFugang Duan clk_disable_unprepare(fep->clk_ref); 21069b5330edSFugang Duan failed_clk_ref: 2107a74d19baSLiu Xiang if (fep->clk_ptp) { 2108a74d19baSLiu Xiang mutex_lock(&fep->ptp_clk_mutex); 2109a74d19baSLiu Xiang clk_disable_unprepare(fep->clk_ptp); 2110a74d19baSLiu Xiang fep->ptp_clk_on = false; 2111a74d19baSLiu Xiang mutex_unlock(&fep->ptp_clk_mutex); 2112a74d19baSLiu Xiang } 2113e8fcfcd5SNimrod Andy failed_clk_ptp: 2114e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_enet_out); 2115e8fcfcd5SNimrod Andy 2116e8fcfcd5SNimrod Andy return ret; 2117e8fcfcd5SNimrod Andy } 2118e8fcfcd5SNimrod Andy 2119b820c114SJoakim Zhang static int fec_enet_parse_rgmii_delay(struct fec_enet_private *fep, 2120b820c114SJoakim Zhang struct device_node *np) 2121b820c114SJoakim Zhang { 2122b820c114SJoakim Zhang u32 rgmii_tx_delay, rgmii_rx_delay; 2123b820c114SJoakim Zhang 2124b820c114SJoakim Zhang /* For rgmii tx internal delay, valid values are 0ps and 2000ps */ 2125b820c114SJoakim Zhang if (!of_property_read_u32(np, "tx-internal-delay-ps", &rgmii_tx_delay)) { 2126b820c114SJoakim Zhang if (rgmii_tx_delay != 0 && rgmii_tx_delay != 2000) { 2127b820c114SJoakim Zhang dev_err(&fep->pdev->dev, "The only allowed RGMII TX delay values are: 0ps, 2000ps"); 2128b820c114SJoakim Zhang return -EINVAL; 2129b820c114SJoakim Zhang } else if (rgmii_tx_delay == 2000) { 2130b820c114SJoakim Zhang fep->rgmii_txc_dly = true; 2131b820c114SJoakim Zhang } 2132b820c114SJoakim Zhang } 2133b820c114SJoakim Zhang 2134b820c114SJoakim Zhang /* For rgmii rx internal delay, valid values are 0ps and 2000ps */ 2135b820c114SJoakim Zhang if (!of_property_read_u32(np, "rx-internal-delay-ps", &rgmii_rx_delay)) { 2136b820c114SJoakim Zhang if (rgmii_rx_delay != 0 && rgmii_rx_delay != 2000) { 2137b820c114SJoakim Zhang dev_err(&fep->pdev->dev, "The only allowed RGMII RX delay values are: 0ps, 2000ps"); 2138b820c114SJoakim Zhang return -EINVAL; 2139b820c114SJoakim Zhang } else if (rgmii_rx_delay == 2000) { 2140b820c114SJoakim Zhang fep->rgmii_rxc_dly = true; 2141b820c114SJoakim Zhang } 2142b820c114SJoakim Zhang } 2143b820c114SJoakim Zhang 2144b820c114SJoakim Zhang return 0; 2145b820c114SJoakim Zhang } 2146b820c114SJoakim Zhang 2147793fc096SFrank Li static int fec_enet_mii_probe(struct net_device *ndev) 2148793fc096SFrank Li { 2149793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2150793fc096SFrank Li struct phy_device *phy_dev = NULL; 2151793fc096SFrank Li char mdio_bus_id[MII_BUS_ID_SIZE]; 2152793fc096SFrank Li char phy_name[MII_BUS_ID_SIZE + 3]; 2153793fc096SFrank Li int phy_id; 2154793fc096SFrank Li int dev_id = fep->dev_id; 2155793fc096SFrank Li 2156407066f8SUwe Kleine-König if (fep->phy_node) { 2157407066f8SUwe Kleine-König phy_dev = of_phy_connect(ndev, fep->phy_node, 2158407066f8SUwe Kleine-König &fec_enet_adjust_link, 0, 2159407066f8SUwe Kleine-König fep->phy_interface); 21609558df3aSAndrew Lunn if (!phy_dev) { 21619558df3aSAndrew Lunn netdev_err(ndev, "Unable to connect to phy\n"); 2162213a9922SNimrod Andy return -ENODEV; 21639558df3aSAndrew Lunn } 2164407066f8SUwe Kleine-König } else { 2165793fc096SFrank Li /* check for attached phy */ 2166793fc096SFrank Li for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) { 21677f854420SAndrew Lunn if (!mdiobus_is_registered_device(fep->mii_bus, phy_id)) 2168793fc096SFrank Li continue; 2169793fc096SFrank Li if (dev_id--) 2170793fc096SFrank Li continue; 2171f029c781SWolfram Sang strscpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE); 2172793fc096SFrank Li break; 2173793fc096SFrank Li } 2174793fc096SFrank Li 2175793fc096SFrank Li if (phy_id >= PHY_MAX_ADDR) { 217631b7720cSJoe Perches netdev_info(ndev, "no PHY, assuming direct connection to switch\n"); 2177f029c781SWolfram Sang strscpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE); 2178793fc096SFrank Li phy_id = 0; 2179793fc096SFrank Li } 2180793fc096SFrank Li 2181407066f8SUwe Kleine-König snprintf(phy_name, sizeof(phy_name), 2182407066f8SUwe Kleine-König PHY_ID_FMT, mdio_bus_id, phy_id); 2183793fc096SFrank Li phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, 2184793fc096SFrank Li fep->phy_interface); 2185407066f8SUwe Kleine-König } 2186407066f8SUwe Kleine-König 2187793fc096SFrank Li if (IS_ERR(phy_dev)) { 218831b7720cSJoe Perches netdev_err(ndev, "could not attach to PHY\n"); 2189793fc096SFrank Li return PTR_ERR(phy_dev); 2190793fc096SFrank Li } 2191793fc096SFrank Li 2192793fc096SFrank Li /* mask with MAC supported features */ 21936b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_GBIT) { 219458056c1eSAndrew Lunn phy_set_max_speed(phy_dev, 1000); 219541124fa6SAndrew Lunn phy_remove_link_mode(phy_dev, 219641124fa6SAndrew Lunn ETHTOOL_LINK_MODE_1000baseT_Half_BIT); 2197d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 2198c306ad36SAndrew Lunn phy_support_sym_pause(phy_dev); 2199d1391930SGuenter Roeck #endif 2200793fc096SFrank Li } 2201793fc096SFrank Li else 220258056c1eSAndrew Lunn phy_set_max_speed(phy_dev, 100); 2203793fc096SFrank Li 2204793fc096SFrank Li fep->link = 0; 2205793fc096SFrank Li fep->full_duplex = 0; 2206793fc096SFrank Li 2207557d5dc8SHeiner Kallweit phy_dev->mac_managed_pm = 1; 2208557d5dc8SHeiner Kallweit 22092220943aSAndrew Lunn phy_attached_info(phy_dev); 2210793fc096SFrank Li 2211793fc096SFrank Li return 0; 2212793fc096SFrank Li } 2213793fc096SFrank Li 2214793fc096SFrank Li static int fec_enet_mii_init(struct platform_device *pdev) 2215793fc096SFrank Li { 2216793fc096SFrank Li static struct mii_bus *fec0_mii_bus; 2217793fc096SFrank Li struct net_device *ndev = platform_get_drvdata(pdev); 2218793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 22193c01eb62SAndrew Lunn bool suppress_preamble = false; 2220407066f8SUwe Kleine-König struct device_node *node; 2221e7f4dc35SAndrew Lunn int err = -ENXIO; 222263c60732SUwe Kleine-König u32 mii_speed, holdtime; 22233e782985SAndrew Lunn u32 bus_freq; 2224793fc096SFrank Li 2225793fc096SFrank Li /* 22263d125f9cSStefan Agner * The i.MX28 dual fec interfaces are not equal. 2227793fc096SFrank Li * Here are the differences: 2228793fc096SFrank Li * 2229793fc096SFrank Li * - fec0 supports MII & RMII modes while fec1 only supports RMII 2230793fc096SFrank Li * - fec0 acts as the 1588 time master while fec1 is slave 2231793fc096SFrank Li * - external phys can only be configured by fec0 2232793fc096SFrank Li * 2233793fc096SFrank Li * That is to say fec1 can not work independently. It only works 2234793fc096SFrank Li * when fec0 is working. The reason behind this design is that the 2235793fc096SFrank Li * second interface is added primarily for Switch mode. 2236793fc096SFrank Li * 2237793fc096SFrank Li * Because of the last point above, both phys are attached on fec0 2238793fc096SFrank Li * mdio interface in board design, and need to be configured by 2239793fc096SFrank Li * fec0 mii_bus. 2240793fc096SFrank Li */ 22413d125f9cSStefan Agner if ((fep->quirks & FEC_QUIRK_SINGLE_MDIO) && fep->dev_id > 0) { 2242793fc096SFrank Li /* fec1 uses fec0 mii_bus */ 2243793fc096SFrank Li if (mii_cnt && fec0_mii_bus) { 2244793fc096SFrank Li fep->mii_bus = fec0_mii_bus; 2245793fc096SFrank Li mii_cnt++; 2246793fc096SFrank Li return 0; 2247793fc096SFrank Li } 2248793fc096SFrank Li return -ENOENT; 2249793fc096SFrank Li } 2250793fc096SFrank Li 22513e782985SAndrew Lunn bus_freq = 2500000; /* 2.5MHz by default */ 22523e782985SAndrew Lunn node = of_get_child_by_name(pdev->dev.of_node, "mdio"); 22533c01eb62SAndrew Lunn if (node) { 22543e782985SAndrew Lunn of_property_read_u32(node, "clock-frequency", &bus_freq); 22553c01eb62SAndrew Lunn suppress_preamble = of_property_read_bool(node, 22563c01eb62SAndrew Lunn "suppress-preamble"); 22573c01eb62SAndrew Lunn } 22583e782985SAndrew Lunn 2259793fc096SFrank Li /* 22603e782985SAndrew Lunn * Set MII speed (= clk_get_rate() / 2 * phy_speed) 2261793fc096SFrank Li * 2262793fc096SFrank Li * The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while 2263793fc096SFrank Li * for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'. The i.MX28 2264793fc096SFrank Li * Reference Manual has an error on this, and gets fixed on i.MX6Q 2265793fc096SFrank Li * document. 2266793fc096SFrank Li */ 22673e782985SAndrew Lunn mii_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), bus_freq * 2); 22686b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_ENET_MAC) 226963c60732SUwe Kleine-König mii_speed--; 227063c60732SUwe Kleine-König if (mii_speed > 63) { 227163c60732SUwe Kleine-König dev_err(&pdev->dev, 2272981a0547SPeter Meerwald-Stadler "fec clock (%lu) too fast to get right mii speed\n", 227363c60732SUwe Kleine-König clk_get_rate(fep->clk_ipg)); 227463c60732SUwe Kleine-König err = -EINVAL; 227563c60732SUwe Kleine-König goto err_out; 227663c60732SUwe Kleine-König } 227763c60732SUwe Kleine-König 227863c60732SUwe Kleine-König /* 227963c60732SUwe Kleine-König * The i.MX28 and i.MX6 types have another filed in the MSCR (aka 228063c60732SUwe Kleine-König * MII_SPEED) register that defines the MDIO output hold time. Earlier 228163c60732SUwe Kleine-König * versions are RAZ there, so just ignore the difference and write the 228263c60732SUwe Kleine-König * register always. 228363c60732SUwe Kleine-König * The minimal hold time according to IEE802.3 (clause 22) is 10 ns. 228463c60732SUwe Kleine-König * HOLDTIME + 1 is the number of clk cycles the fec is holding the 228563c60732SUwe Kleine-König * output. 228663c60732SUwe Kleine-König * The HOLDTIME bitfield takes values between 0 and 7 (inclusive). 228763c60732SUwe Kleine-König * Given that ceil(clkrate / 5000000) <= 64, the calculation for 228863c60732SUwe Kleine-König * holdtime cannot result in a value greater than 3. 228963c60732SUwe Kleine-König */ 229063c60732SUwe Kleine-König holdtime = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 100000000) - 1; 229163c60732SUwe Kleine-König 229263c60732SUwe Kleine-König fep->phy_speed = mii_speed << 1 | holdtime << 8; 229363c60732SUwe Kleine-König 22943c01eb62SAndrew Lunn if (suppress_preamble) 22953c01eb62SAndrew Lunn fep->phy_speed |= BIT(7); 22963c01eb62SAndrew Lunn 22971e6114f5SGreg Ungerer if (fep->quirks & FEC_QUIRK_CLEAR_SETUP_MII) { 2298f166f890SAndrew Lunn /* Clear MMFR to avoid to generate MII event by writing MSCR. 2299f166f890SAndrew Lunn * MII event generation condition: 2300f166f890SAndrew Lunn * - writing MSCR: 2301f166f890SAndrew Lunn * - mmfr[31:0]_not_zero & mscr[7:0]_is_zero & 2302f166f890SAndrew Lunn * mscr_reg_data_in[7:0] != 0 2303f166f890SAndrew Lunn * - writing MMFR: 2304f166f890SAndrew Lunn * - mscr[7:0]_not_zero 2305f166f890SAndrew Lunn */ 2306f166f890SAndrew Lunn writel(0, fep->hwp + FEC_MII_DATA); 23071e6114f5SGreg Ungerer } 2308f166f890SAndrew Lunn 2309793fc096SFrank Li writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 2310793fc096SFrank Li 2311f166f890SAndrew Lunn /* Clear any pending transaction complete indication */ 2312f166f890SAndrew Lunn writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT); 2313f166f890SAndrew Lunn 2314793fc096SFrank Li fep->mii_bus = mdiobus_alloc(); 2315793fc096SFrank Li if (fep->mii_bus == NULL) { 2316793fc096SFrank Li err = -ENOMEM; 2317793fc096SFrank Li goto err_out; 2318793fc096SFrank Li } 2319793fc096SFrank Li 2320793fc096SFrank Li fep->mii_bus->name = "fec_enet_mii_bus"; 2321793fc096SFrank Li fep->mii_bus->read = fec_enet_mdio_read; 2322793fc096SFrank Li fep->mii_bus->write = fec_enet_mdio_write; 2323793fc096SFrank Li snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", 2324793fc096SFrank Li pdev->name, fep->dev_id + 1); 2325793fc096SFrank Li fep->mii_bus->priv = fep; 2326793fc096SFrank Li fep->mii_bus->parent = &pdev->dev; 2327793fc096SFrank Li 2328407066f8SUwe Kleine-König err = of_mdiobus_register(fep->mii_bus, node); 2329407066f8SUwe Kleine-König if (err) 2330e7f4dc35SAndrew Lunn goto err_out_free_mdiobus; 23310607a2cdSPan Bian of_node_put(node); 2332793fc096SFrank Li 2333793fc096SFrank Li mii_cnt++; 2334793fc096SFrank Li 2335793fc096SFrank Li /* save fec0 mii_bus */ 23363d125f9cSStefan Agner if (fep->quirks & FEC_QUIRK_SINGLE_MDIO) 2337793fc096SFrank Li fec0_mii_bus = fep->mii_bus; 2338793fc096SFrank Li 2339793fc096SFrank Li return 0; 2340793fc096SFrank Li 2341793fc096SFrank Li err_out_free_mdiobus: 2342793fc096SFrank Li mdiobus_free(fep->mii_bus); 2343793fc096SFrank Li err_out: 23440607a2cdSPan Bian of_node_put(node); 2345793fc096SFrank Li return err; 2346793fc096SFrank Li } 2347793fc096SFrank Li 2348793fc096SFrank Li static void fec_enet_mii_remove(struct fec_enet_private *fep) 2349793fc096SFrank Li { 2350793fc096SFrank Li if (--mii_cnt == 0) { 2351793fc096SFrank Li mdiobus_unregister(fep->mii_bus); 2352793fc096SFrank Li mdiobus_free(fep->mii_bus); 2353793fc096SFrank Li } 2354793fc096SFrank Li } 2355793fc096SFrank Li 2356793fc096SFrank Li static void fec_enet_get_drvinfo(struct net_device *ndev, 2357793fc096SFrank Li struct ethtool_drvinfo *info) 2358793fc096SFrank Li { 2359793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2360793fc096SFrank Li 2361f029c781SWolfram Sang strscpy(info->driver, fep->pdev->dev.driver->name, 2362793fc096SFrank Li sizeof(info->driver)); 2363f029c781SWolfram Sang strscpy(info->bus_info, dev_name(&ndev->dev), sizeof(info->bus_info)); 2364793fc096SFrank Li } 2365793fc096SFrank Li 2366db65f35fSPhilippe Reynes static int fec_enet_get_regs_len(struct net_device *ndev) 2367db65f35fSPhilippe Reynes { 2368db65f35fSPhilippe Reynes struct fec_enet_private *fep = netdev_priv(ndev); 2369db65f35fSPhilippe Reynes struct resource *r; 2370db65f35fSPhilippe Reynes int s = 0; 2371db65f35fSPhilippe Reynes 2372db65f35fSPhilippe Reynes r = platform_get_resource(fep->pdev, IORESOURCE_MEM, 0); 2373db65f35fSPhilippe Reynes if (r) 2374db65f35fSPhilippe Reynes s = resource_size(r); 2375db65f35fSPhilippe Reynes 2376db65f35fSPhilippe Reynes return s; 2377db65f35fSPhilippe Reynes } 2378db65f35fSPhilippe Reynes 2379db65f35fSPhilippe Reynes /* List of registers that can be safety be read to dump them with ethtool */ 2380db65f35fSPhilippe Reynes #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ 23813f1dcc6aSLucas Stach defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ 238278cc6e7eSFlorian Fainelli defined(CONFIG_ARM64) || defined(CONFIG_COMPILE_TEST) 2383f9bcc9f3SVivien Didelot static __u32 fec_enet_register_version = 2; 2384db65f35fSPhilippe Reynes static u32 fec_enet_register_offset[] = { 2385db65f35fSPhilippe Reynes FEC_IEVENT, FEC_IMASK, FEC_R_DES_ACTIVE_0, FEC_X_DES_ACTIVE_0, 2386db65f35fSPhilippe Reynes FEC_ECNTRL, FEC_MII_DATA, FEC_MII_SPEED, FEC_MIB_CTRLSTAT, FEC_R_CNTRL, 2387db65f35fSPhilippe Reynes FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, FEC_OPD, FEC_TXIC0, FEC_TXIC1, 2388db65f35fSPhilippe Reynes FEC_TXIC2, FEC_RXIC0, FEC_RXIC1, FEC_RXIC2, FEC_HASH_TABLE_HIGH, 2389db65f35fSPhilippe Reynes FEC_HASH_TABLE_LOW, FEC_GRP_HASH_TABLE_HIGH, FEC_GRP_HASH_TABLE_LOW, 2390db65f35fSPhilippe Reynes FEC_X_WMRK, FEC_R_BOUND, FEC_R_FSTART, FEC_R_DES_START_1, 2391db65f35fSPhilippe Reynes FEC_X_DES_START_1, FEC_R_BUFF_SIZE_1, FEC_R_DES_START_2, 2392db65f35fSPhilippe Reynes FEC_X_DES_START_2, FEC_R_BUFF_SIZE_2, FEC_R_DES_START_0, 2393db65f35fSPhilippe Reynes FEC_X_DES_START_0, FEC_R_BUFF_SIZE_0, FEC_R_FIFO_RSFL, FEC_R_FIFO_RSEM, 2394db65f35fSPhilippe Reynes FEC_R_FIFO_RAEM, FEC_R_FIFO_RAFL, FEC_RACC, FEC_RCMR_1, FEC_RCMR_2, 2395db65f35fSPhilippe Reynes FEC_DMA_CFG_1, FEC_DMA_CFG_2, FEC_R_DES_ACTIVE_1, FEC_X_DES_ACTIVE_1, 2396db65f35fSPhilippe Reynes FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_2, FEC_QOS_SCHEME, 2397db65f35fSPhilippe Reynes RMON_T_DROP, RMON_T_PACKETS, RMON_T_BC_PKT, RMON_T_MC_PKT, 2398db65f35fSPhilippe Reynes RMON_T_CRC_ALIGN, RMON_T_UNDERSIZE, RMON_T_OVERSIZE, RMON_T_FRAG, 2399db65f35fSPhilippe Reynes RMON_T_JAB, RMON_T_COL, RMON_T_P64, RMON_T_P65TO127, RMON_T_P128TO255, 2400db65f35fSPhilippe Reynes RMON_T_P256TO511, RMON_T_P512TO1023, RMON_T_P1024TO2047, 2401db65f35fSPhilippe Reynes RMON_T_P_GTE2048, RMON_T_OCTETS, 2402db65f35fSPhilippe Reynes IEEE_T_DROP, IEEE_T_FRAME_OK, IEEE_T_1COL, IEEE_T_MCOL, IEEE_T_DEF, 2403db65f35fSPhilippe Reynes IEEE_T_LCOL, IEEE_T_EXCOL, IEEE_T_MACERR, IEEE_T_CSERR, IEEE_T_SQE, 2404db65f35fSPhilippe Reynes IEEE_T_FDXFC, IEEE_T_OCTETS_OK, 2405db65f35fSPhilippe Reynes RMON_R_PACKETS, RMON_R_BC_PKT, RMON_R_MC_PKT, RMON_R_CRC_ALIGN, 2406db65f35fSPhilippe Reynes RMON_R_UNDERSIZE, RMON_R_OVERSIZE, RMON_R_FRAG, RMON_R_JAB, 2407db65f35fSPhilippe Reynes RMON_R_RESVD_O, RMON_R_P64, RMON_R_P65TO127, RMON_R_P128TO255, 2408db65f35fSPhilippe Reynes RMON_R_P256TO511, RMON_R_P512TO1023, RMON_R_P1024TO2047, 2409db65f35fSPhilippe Reynes RMON_R_P_GTE2048, RMON_R_OCTETS, 2410db65f35fSPhilippe Reynes IEEE_R_DROP, IEEE_R_FRAME_OK, IEEE_R_CRC, IEEE_R_ALIGN, IEEE_R_MACERR, 2411db65f35fSPhilippe Reynes IEEE_R_FDXFC, IEEE_R_OCTETS_OK 2412db65f35fSPhilippe Reynes }; 2413db65f35fSPhilippe Reynes #else 2414f9bcc9f3SVivien Didelot static __u32 fec_enet_register_version = 1; 2415db65f35fSPhilippe Reynes static u32 fec_enet_register_offset[] = { 2416db65f35fSPhilippe Reynes FEC_ECNTRL, FEC_IEVENT, FEC_IMASK, FEC_IVEC, FEC_R_DES_ACTIVE_0, 2417db65f35fSPhilippe Reynes FEC_R_DES_ACTIVE_1, FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_0, 2418db65f35fSPhilippe Reynes FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2, FEC_MII_DATA, FEC_MII_SPEED, 2419db65f35fSPhilippe Reynes FEC_R_BOUND, FEC_R_FSTART, FEC_X_WMRK, FEC_X_FSTART, FEC_R_CNTRL, 2420db65f35fSPhilippe Reynes FEC_MAX_FRM_LEN, FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, 2421db65f35fSPhilippe Reynes FEC_GRP_HASH_TABLE_HIGH, FEC_GRP_HASH_TABLE_LOW, FEC_R_DES_START_0, 2422db65f35fSPhilippe Reynes FEC_R_DES_START_1, FEC_R_DES_START_2, FEC_X_DES_START_0, 2423db65f35fSPhilippe Reynes FEC_X_DES_START_1, FEC_X_DES_START_2, FEC_R_BUFF_SIZE_0, 2424db65f35fSPhilippe Reynes FEC_R_BUFF_SIZE_1, FEC_R_BUFF_SIZE_2 2425db65f35fSPhilippe Reynes }; 2426db65f35fSPhilippe Reynes #endif 2427db65f35fSPhilippe Reynes 2428db65f35fSPhilippe Reynes static void fec_enet_get_regs(struct net_device *ndev, 2429db65f35fSPhilippe Reynes struct ethtool_regs *regs, void *regbuf) 2430db65f35fSPhilippe Reynes { 2431db65f35fSPhilippe Reynes struct fec_enet_private *fep = netdev_priv(ndev); 2432db65f35fSPhilippe Reynes u32 __iomem *theregs = (u32 __iomem *)fep->hwp; 2433c72a0bc0SAndrew Lunn struct device *dev = &fep->pdev->dev; 2434db65f35fSPhilippe Reynes u32 *buf = (u32 *)regbuf; 2435db65f35fSPhilippe Reynes u32 i, off; 2436c72a0bc0SAndrew Lunn int ret; 2437c72a0bc0SAndrew Lunn 2438da875fa5SZhang Qilong ret = pm_runtime_resume_and_get(dev); 2439c72a0bc0SAndrew Lunn if (ret < 0) 2440c72a0bc0SAndrew Lunn return; 2441db65f35fSPhilippe Reynes 2442f9bcc9f3SVivien Didelot regs->version = fec_enet_register_version; 2443f9bcc9f3SVivien Didelot 2444db65f35fSPhilippe Reynes memset(buf, 0, regs->len); 2445db65f35fSPhilippe Reynes 2446db65f35fSPhilippe Reynes for (i = 0; i < ARRAY_SIZE(fec_enet_register_offset); i++) { 2447ec20a63aSFugang Duan off = fec_enet_register_offset[i]; 2448ec20a63aSFugang Duan 2449ec20a63aSFugang Duan if ((off == FEC_R_BOUND || off == FEC_R_FSTART) && 2450ec20a63aSFugang Duan !(fep->quirks & FEC_QUIRK_HAS_FRREG)) 2451ec20a63aSFugang Duan continue; 2452ec20a63aSFugang Duan 2453ec20a63aSFugang Duan off >>= 2; 2454db65f35fSPhilippe Reynes buf[off] = readl(&theregs[off]); 2455db65f35fSPhilippe Reynes } 2456c72a0bc0SAndrew Lunn 2457c72a0bc0SAndrew Lunn pm_runtime_mark_last_busy(dev); 2458c72a0bc0SAndrew Lunn pm_runtime_put_autosuspend(dev); 2459db65f35fSPhilippe Reynes } 2460db65f35fSPhilippe Reynes 2461793fc096SFrank Li static int fec_enet_get_ts_info(struct net_device *ndev, 2462793fc096SFrank Li struct ethtool_ts_info *info) 2463793fc096SFrank Li { 2464793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2465793fc096SFrank Li 2466793fc096SFrank Li if (fep->bufdesc_ex) { 2467793fc096SFrank Li 2468793fc096SFrank Li info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | 2469793fc096SFrank Li SOF_TIMESTAMPING_RX_SOFTWARE | 2470793fc096SFrank Li SOF_TIMESTAMPING_SOFTWARE | 2471793fc096SFrank Li SOF_TIMESTAMPING_TX_HARDWARE | 2472793fc096SFrank Li SOF_TIMESTAMPING_RX_HARDWARE | 2473793fc096SFrank Li SOF_TIMESTAMPING_RAW_HARDWARE; 2474793fc096SFrank Li if (fep->ptp_clock) 2475793fc096SFrank Li info->phc_index = ptp_clock_index(fep->ptp_clock); 2476793fc096SFrank Li else 2477793fc096SFrank Li info->phc_index = -1; 2478793fc096SFrank Li 2479793fc096SFrank Li info->tx_types = (1 << HWTSTAMP_TX_OFF) | 2480793fc096SFrank Li (1 << HWTSTAMP_TX_ON); 2481793fc096SFrank Li 2482793fc096SFrank Li info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | 2483793fc096SFrank Li (1 << HWTSTAMP_FILTER_ALL); 2484793fc096SFrank Li return 0; 2485793fc096SFrank Li } else { 2486793fc096SFrank Li return ethtool_op_get_ts_info(ndev, info); 2487793fc096SFrank Li } 2488793fc096SFrank Li } 2489793fc096SFrank Li 2490d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 2491d1391930SGuenter Roeck 2492793fc096SFrank Li static void fec_enet_get_pauseparam(struct net_device *ndev, 2493793fc096SFrank Li struct ethtool_pauseparam *pause) 2494793fc096SFrank Li { 2495793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2496793fc096SFrank Li 2497793fc096SFrank Li pause->autoneg = (fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) != 0; 2498793fc096SFrank Li pause->tx_pause = (fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) != 0; 2499793fc096SFrank Li pause->rx_pause = pause->tx_pause; 2500793fc096SFrank Li } 2501793fc096SFrank Li 2502793fc096SFrank Li static int fec_enet_set_pauseparam(struct net_device *ndev, 2503793fc096SFrank Li struct ethtool_pauseparam *pause) 2504793fc096SFrank Li { 2505793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2506793fc096SFrank Li 250745f5c327SPhilippe Reynes if (!ndev->phydev) 25080b146ca8SRussell King return -ENODEV; 25090b146ca8SRussell King 2510793fc096SFrank Li if (pause->tx_pause != pause->rx_pause) { 2511793fc096SFrank Li netdev_info(ndev, 2512793fc096SFrank Li "hardware only support enable/disable both tx and rx"); 2513793fc096SFrank Li return -EINVAL; 2514793fc096SFrank Li } 2515793fc096SFrank Li 2516793fc096SFrank Li fep->pause_flag = 0; 2517793fc096SFrank Li 2518793fc096SFrank Li /* tx pause must be same as rx pause */ 2519793fc096SFrank Li fep->pause_flag |= pause->rx_pause ? FEC_PAUSE_FLAG_ENABLE : 0; 2520793fc096SFrank Li fep->pause_flag |= pause->autoneg ? FEC_PAUSE_FLAG_AUTONEG : 0; 2521793fc096SFrank Li 25220c122405SAndrew Lunn phy_set_sym_pause(ndev->phydev, pause->rx_pause, pause->tx_pause, 25230c122405SAndrew Lunn pause->autoneg); 2524793fc096SFrank Li 2525793fc096SFrank Li if (pause->autoneg) { 2526793fc096SFrank Li if (netif_running(ndev)) 2527793fc096SFrank Li fec_stop(ndev); 252845f5c327SPhilippe Reynes phy_start_aneg(ndev->phydev); 2529793fc096SFrank Li } 2530dbc64a8eSRussell King if (netif_running(ndev)) { 2531dbc64a8eSRussell King napi_disable(&fep->napi); 2532dbc64a8eSRussell King netif_tx_lock_bh(ndev); 2533ef83337dSRussell King fec_restart(ndev); 2534657ade07SRickard x Andersson netif_tx_wake_all_queues(ndev); 25356af42d42SRussell King netif_tx_unlock_bh(ndev); 2536dbc64a8eSRussell King napi_enable(&fep->napi); 2537dbc64a8eSRussell King } 2538793fc096SFrank Li 2539793fc096SFrank Li return 0; 2540793fc096SFrank Li } 2541793fc096SFrank Li 254238ae92dcSChris Healy static const struct fec_stat { 254338ae92dcSChris Healy char name[ETH_GSTRING_LEN]; 254438ae92dcSChris Healy u16 offset; 254538ae92dcSChris Healy } fec_stats[] = { 254638ae92dcSChris Healy /* RMON TX */ 254738ae92dcSChris Healy { "tx_dropped", RMON_T_DROP }, 254838ae92dcSChris Healy { "tx_packets", RMON_T_PACKETS }, 254938ae92dcSChris Healy { "tx_broadcast", RMON_T_BC_PKT }, 255038ae92dcSChris Healy { "tx_multicast", RMON_T_MC_PKT }, 255138ae92dcSChris Healy { "tx_crc_errors", RMON_T_CRC_ALIGN }, 255238ae92dcSChris Healy { "tx_undersize", RMON_T_UNDERSIZE }, 255338ae92dcSChris Healy { "tx_oversize", RMON_T_OVERSIZE }, 255438ae92dcSChris Healy { "tx_fragment", RMON_T_FRAG }, 255538ae92dcSChris Healy { "tx_jabber", RMON_T_JAB }, 255638ae92dcSChris Healy { "tx_collision", RMON_T_COL }, 255738ae92dcSChris Healy { "tx_64byte", RMON_T_P64 }, 255838ae92dcSChris Healy { "tx_65to127byte", RMON_T_P65TO127 }, 255938ae92dcSChris Healy { "tx_128to255byte", RMON_T_P128TO255 }, 256038ae92dcSChris Healy { "tx_256to511byte", RMON_T_P256TO511 }, 256138ae92dcSChris Healy { "tx_512to1023byte", RMON_T_P512TO1023 }, 256238ae92dcSChris Healy { "tx_1024to2047byte", RMON_T_P1024TO2047 }, 256338ae92dcSChris Healy { "tx_GTE2048byte", RMON_T_P_GTE2048 }, 256438ae92dcSChris Healy { "tx_octets", RMON_T_OCTETS }, 256538ae92dcSChris Healy 256638ae92dcSChris Healy /* IEEE TX */ 256738ae92dcSChris Healy { "IEEE_tx_drop", IEEE_T_DROP }, 256838ae92dcSChris Healy { "IEEE_tx_frame_ok", IEEE_T_FRAME_OK }, 256938ae92dcSChris Healy { "IEEE_tx_1col", IEEE_T_1COL }, 257038ae92dcSChris Healy { "IEEE_tx_mcol", IEEE_T_MCOL }, 257138ae92dcSChris Healy { "IEEE_tx_def", IEEE_T_DEF }, 257238ae92dcSChris Healy { "IEEE_tx_lcol", IEEE_T_LCOL }, 257338ae92dcSChris Healy { "IEEE_tx_excol", IEEE_T_EXCOL }, 257438ae92dcSChris Healy { "IEEE_tx_macerr", IEEE_T_MACERR }, 257538ae92dcSChris Healy { "IEEE_tx_cserr", IEEE_T_CSERR }, 257638ae92dcSChris Healy { "IEEE_tx_sqe", IEEE_T_SQE }, 257738ae92dcSChris Healy { "IEEE_tx_fdxfc", IEEE_T_FDXFC }, 257838ae92dcSChris Healy { "IEEE_tx_octets_ok", IEEE_T_OCTETS_OK }, 257938ae92dcSChris Healy 258038ae92dcSChris Healy /* RMON RX */ 258138ae92dcSChris Healy { "rx_packets", RMON_R_PACKETS }, 258238ae92dcSChris Healy { "rx_broadcast", RMON_R_BC_PKT }, 258338ae92dcSChris Healy { "rx_multicast", RMON_R_MC_PKT }, 258438ae92dcSChris Healy { "rx_crc_errors", RMON_R_CRC_ALIGN }, 258538ae92dcSChris Healy { "rx_undersize", RMON_R_UNDERSIZE }, 258638ae92dcSChris Healy { "rx_oversize", RMON_R_OVERSIZE }, 258738ae92dcSChris Healy { "rx_fragment", RMON_R_FRAG }, 258838ae92dcSChris Healy { "rx_jabber", RMON_R_JAB }, 258938ae92dcSChris Healy { "rx_64byte", RMON_R_P64 }, 259038ae92dcSChris Healy { "rx_65to127byte", RMON_R_P65TO127 }, 259138ae92dcSChris Healy { "rx_128to255byte", RMON_R_P128TO255 }, 259238ae92dcSChris Healy { "rx_256to511byte", RMON_R_P256TO511 }, 259338ae92dcSChris Healy { "rx_512to1023byte", RMON_R_P512TO1023 }, 259438ae92dcSChris Healy { "rx_1024to2047byte", RMON_R_P1024TO2047 }, 259538ae92dcSChris Healy { "rx_GTE2048byte", RMON_R_P_GTE2048 }, 259638ae92dcSChris Healy { "rx_octets", RMON_R_OCTETS }, 259738ae92dcSChris Healy 259838ae92dcSChris Healy /* IEEE RX */ 259938ae92dcSChris Healy { "IEEE_rx_drop", IEEE_R_DROP }, 260038ae92dcSChris Healy { "IEEE_rx_frame_ok", IEEE_R_FRAME_OK }, 260138ae92dcSChris Healy { "IEEE_rx_crc", IEEE_R_CRC }, 260238ae92dcSChris Healy { "IEEE_rx_align", IEEE_R_ALIGN }, 260338ae92dcSChris Healy { "IEEE_rx_macerr", IEEE_R_MACERR }, 260438ae92dcSChris Healy { "IEEE_rx_fdxfc", IEEE_R_FDXFC }, 260538ae92dcSChris Healy { "IEEE_rx_octets_ok", IEEE_R_OCTETS_OK }, 260638ae92dcSChris Healy }; 260738ae92dcSChris Healy 2608f85de666SNikita Yushchenko #define FEC_STATS_SIZE (ARRAY_SIZE(fec_stats) * sizeof(u64)) 2609f85de666SNikita Yushchenko 261080cca775SNikita Yushchenko static void fec_enet_update_ethtool_stats(struct net_device *dev) 261138ae92dcSChris Healy { 261238ae92dcSChris Healy struct fec_enet_private *fep = netdev_priv(dev); 261338ae92dcSChris Healy int i; 261438ae92dcSChris Healy 261538ae92dcSChris Healy for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 261680cca775SNikita Yushchenko fep->ethtool_stats[i] = readl(fep->hwp + fec_stats[i].offset); 261780cca775SNikita Yushchenko } 261880cca775SNikita Yushchenko 261980cca775SNikita Yushchenko static void fec_enet_get_ethtool_stats(struct net_device *dev, 262080cca775SNikita Yushchenko struct ethtool_stats *stats, u64 *data) 262180cca775SNikita Yushchenko { 262280cca775SNikita Yushchenko struct fec_enet_private *fep = netdev_priv(dev); 262380cca775SNikita Yushchenko 262480cca775SNikita Yushchenko if (netif_running(dev)) 262580cca775SNikita Yushchenko fec_enet_update_ethtool_stats(dev); 262680cca775SNikita Yushchenko 2627f85de666SNikita Yushchenko memcpy(data, fep->ethtool_stats, FEC_STATS_SIZE); 262838ae92dcSChris Healy } 262938ae92dcSChris Healy 263038ae92dcSChris Healy static void fec_enet_get_strings(struct net_device *netdev, 263138ae92dcSChris Healy u32 stringset, u8 *data) 263238ae92dcSChris Healy { 263338ae92dcSChris Healy int i; 263438ae92dcSChris Healy switch (stringset) { 263538ae92dcSChris Healy case ETH_SS_STATS: 263638ae92dcSChris Healy for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 263738ae92dcSChris Healy memcpy(data + i * ETH_GSTRING_LEN, 263838ae92dcSChris Healy fec_stats[i].name, ETH_GSTRING_LEN); 263938ae92dcSChris Healy break; 26406016ba34SOleksij Rempel case ETH_SS_TEST: 26416016ba34SOleksij Rempel net_selftest_get_strings(data); 26426016ba34SOleksij Rempel break; 264338ae92dcSChris Healy } 264438ae92dcSChris Healy } 264538ae92dcSChris Healy 264638ae92dcSChris Healy static int fec_enet_get_sset_count(struct net_device *dev, int sset) 264738ae92dcSChris Healy { 264838ae92dcSChris Healy switch (sset) { 264938ae92dcSChris Healy case ETH_SS_STATS: 265038ae92dcSChris Healy return ARRAY_SIZE(fec_stats); 26516016ba34SOleksij Rempel case ETH_SS_TEST: 26526016ba34SOleksij Rempel return net_selftest_get_count(); 265338ae92dcSChris Healy default: 265438ae92dcSChris Healy return -EOPNOTSUPP; 265538ae92dcSChris Healy } 265638ae92dcSChris Healy } 2657f85de666SNikita Yushchenko 26582b30842bSAndrew Lunn static void fec_enet_clear_ethtool_stats(struct net_device *dev) 26592b30842bSAndrew Lunn { 26602b30842bSAndrew Lunn struct fec_enet_private *fep = netdev_priv(dev); 26612b30842bSAndrew Lunn int i; 26622b30842bSAndrew Lunn 26632b30842bSAndrew Lunn /* Disable MIB statistics counters */ 26642b30842bSAndrew Lunn writel(FEC_MIB_CTRLSTAT_DISABLE, fep->hwp + FEC_MIB_CTRLSTAT); 26652b30842bSAndrew Lunn 26662b30842bSAndrew Lunn for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 26672b30842bSAndrew Lunn writel(0, fep->hwp + fec_stats[i].offset); 26682b30842bSAndrew Lunn 26692b30842bSAndrew Lunn /* Don't disable MIB statistics counters */ 26702b30842bSAndrew Lunn writel(0, fep->hwp + FEC_MIB_CTRLSTAT); 26712b30842bSAndrew Lunn } 26722b30842bSAndrew Lunn 2673f85de666SNikita Yushchenko #else /* !defined(CONFIG_M5272) */ 2674f85de666SNikita Yushchenko #define FEC_STATS_SIZE 0 2675f85de666SNikita Yushchenko static inline void fec_enet_update_ethtool_stats(struct net_device *dev) 2676f85de666SNikita Yushchenko { 2677f85de666SNikita Yushchenko } 267841e8e404SFabio Estevam 267941e8e404SFabio Estevam static inline void fec_enet_clear_ethtool_stats(struct net_device *dev) 268041e8e404SFabio Estevam { 268141e8e404SFabio Estevam } 2682d1391930SGuenter Roeck #endif /* !defined(CONFIG_M5272) */ 268338ae92dcSChris Healy 2684d851b47bSFugang Duan /* ITR clock source is enet system clock (clk_ahb). 2685d851b47bSFugang Duan * TCTT unit is cycle_ns * 64 cycle 2686d851b47bSFugang Duan * So, the ICTT value = X us / (cycle_ns * 64) 2687d851b47bSFugang Duan */ 2688d851b47bSFugang Duan static int fec_enet_us_to_itr_clock(struct net_device *ndev, int us) 2689d851b47bSFugang Duan { 2690d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2691d851b47bSFugang Duan 2692d851b47bSFugang Duan return us * (fep->itr_clk_rate / 64000) / 1000; 2693d851b47bSFugang Duan } 2694d851b47bSFugang Duan 2695d851b47bSFugang Duan /* Set threshold for interrupt coalescing */ 2696d851b47bSFugang Duan static void fec_enet_itr_coal_set(struct net_device *ndev) 2697d851b47bSFugang Duan { 2698d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2699d851b47bSFugang Duan int rx_itr, tx_itr; 2700d851b47bSFugang Duan 2701d851b47bSFugang Duan /* Must be greater than zero to avoid unpredictable behavior */ 2702d851b47bSFugang Duan if (!fep->rx_time_itr || !fep->rx_pkts_itr || 2703d851b47bSFugang Duan !fep->tx_time_itr || !fep->tx_pkts_itr) 2704d851b47bSFugang Duan return; 2705d851b47bSFugang Duan 2706d851b47bSFugang Duan /* Select enet system clock as Interrupt Coalescing 2707d851b47bSFugang Duan * timer Clock Source 2708d851b47bSFugang Duan */ 2709d851b47bSFugang Duan rx_itr = FEC_ITR_CLK_SEL; 2710d851b47bSFugang Duan tx_itr = FEC_ITR_CLK_SEL; 2711d851b47bSFugang Duan 2712d851b47bSFugang Duan /* set ICFT and ICTT */ 2713d851b47bSFugang Duan rx_itr |= FEC_ITR_ICFT(fep->rx_pkts_itr); 2714d851b47bSFugang Duan rx_itr |= FEC_ITR_ICTT(fec_enet_us_to_itr_clock(ndev, fep->rx_time_itr)); 2715d851b47bSFugang Duan tx_itr |= FEC_ITR_ICFT(fep->tx_pkts_itr); 2716d851b47bSFugang Duan tx_itr |= FEC_ITR_ICTT(fec_enet_us_to_itr_clock(ndev, fep->tx_time_itr)); 2717d851b47bSFugang Duan 2718d851b47bSFugang Duan rx_itr |= FEC_ITR_EN; 2719d851b47bSFugang Duan tx_itr |= FEC_ITR_EN; 2720d851b47bSFugang Duan 2721d851b47bSFugang Duan writel(tx_itr, fep->hwp + FEC_TXIC0); 2722d851b47bSFugang Duan writel(rx_itr, fep->hwp + FEC_RXIC0); 2723471ff445SJoakim Zhang if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) { 2724d851b47bSFugang Duan writel(tx_itr, fep->hwp + FEC_TXIC1); 2725d851b47bSFugang Duan writel(rx_itr, fep->hwp + FEC_RXIC1); 2726d851b47bSFugang Duan writel(tx_itr, fep->hwp + FEC_TXIC2); 2727d851b47bSFugang Duan writel(rx_itr, fep->hwp + FEC_RXIC2); 2728d851b47bSFugang Duan } 2729ff7566b8SFugang Duan } 2730d851b47bSFugang Duan 2731f3ccfda1SYufeng Mo static int fec_enet_get_coalesce(struct net_device *ndev, 2732f3ccfda1SYufeng Mo struct ethtool_coalesce *ec, 2733f3ccfda1SYufeng Mo struct kernel_ethtool_coalesce *kernel_coal, 2734f3ccfda1SYufeng Mo struct netlink_ext_ack *extack) 2735d851b47bSFugang Duan { 2736d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2737d851b47bSFugang Duan 2738ff7566b8SFugang Duan if (!(fep->quirks & FEC_QUIRK_HAS_COALESCE)) 2739d851b47bSFugang Duan return -EOPNOTSUPP; 2740d851b47bSFugang Duan 2741d851b47bSFugang Duan ec->rx_coalesce_usecs = fep->rx_time_itr; 2742d851b47bSFugang Duan ec->rx_max_coalesced_frames = fep->rx_pkts_itr; 2743d851b47bSFugang Duan 2744d851b47bSFugang Duan ec->tx_coalesce_usecs = fep->tx_time_itr; 2745d851b47bSFugang Duan ec->tx_max_coalesced_frames = fep->tx_pkts_itr; 2746d851b47bSFugang Duan 2747d851b47bSFugang Duan return 0; 2748d851b47bSFugang Duan } 2749d851b47bSFugang Duan 2750f3ccfda1SYufeng Mo static int fec_enet_set_coalesce(struct net_device *ndev, 2751f3ccfda1SYufeng Mo struct ethtool_coalesce *ec, 2752f3ccfda1SYufeng Mo struct kernel_ethtool_coalesce *kernel_coal, 2753f3ccfda1SYufeng Mo struct netlink_ext_ack *extack) 2754d851b47bSFugang Duan { 2755d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2756517a772cSFabio Estevam struct device *dev = &fep->pdev->dev; 2757d851b47bSFugang Duan unsigned int cycle; 2758d851b47bSFugang Duan 2759ff7566b8SFugang Duan if (!(fep->quirks & FEC_QUIRK_HAS_COALESCE)) 2760d851b47bSFugang Duan return -EOPNOTSUPP; 2761d851b47bSFugang Duan 2762d851b47bSFugang Duan if (ec->rx_max_coalesced_frames > 255) { 2763517a772cSFabio Estevam dev_err(dev, "Rx coalesced frames exceed hardware limitation\n"); 2764d851b47bSFugang Duan return -EINVAL; 2765d851b47bSFugang Duan } 2766d851b47bSFugang Duan 2767d851b47bSFugang Duan if (ec->tx_max_coalesced_frames > 255) { 2768517a772cSFabio Estevam dev_err(dev, "Tx coalesced frame exceed hardware limitation\n"); 2769d851b47bSFugang Duan return -EINVAL; 2770d851b47bSFugang Duan } 2771d851b47bSFugang Duan 2772ab14961dSJakub Kicinski cycle = fec_enet_us_to_itr_clock(ndev, ec->rx_coalesce_usecs); 2773d851b47bSFugang Duan if (cycle > 0xFFFF) { 2774517a772cSFabio Estevam dev_err(dev, "Rx coalesced usec exceed hardware limitation\n"); 2775d851b47bSFugang Duan return -EINVAL; 2776d851b47bSFugang Duan } 2777d851b47bSFugang Duan 2778ab14961dSJakub Kicinski cycle = fec_enet_us_to_itr_clock(ndev, ec->tx_coalesce_usecs); 2779d851b47bSFugang Duan if (cycle > 0xFFFF) { 2780ab14961dSJakub Kicinski dev_err(dev, "Tx coalesced usec exceed hardware limitation\n"); 2781d851b47bSFugang Duan return -EINVAL; 2782d851b47bSFugang Duan } 2783d851b47bSFugang Duan 2784d851b47bSFugang Duan fep->rx_time_itr = ec->rx_coalesce_usecs; 2785d851b47bSFugang Duan fep->rx_pkts_itr = ec->rx_max_coalesced_frames; 2786d851b47bSFugang Duan 2787d851b47bSFugang Duan fep->tx_time_itr = ec->tx_coalesce_usecs; 2788d851b47bSFugang Duan fep->tx_pkts_itr = ec->tx_max_coalesced_frames; 2789d851b47bSFugang Duan 2790d851b47bSFugang Duan fec_enet_itr_coal_set(ndev); 2791d851b47bSFugang Duan 2792d851b47bSFugang Duan return 0; 2793d851b47bSFugang Duan } 2794d851b47bSFugang Duan 2795d851b47bSFugang Duan static void fec_enet_itr_coal_init(struct net_device *ndev) 2796d851b47bSFugang Duan { 2797d851b47bSFugang Duan struct ethtool_coalesce ec; 2798d851b47bSFugang Duan 2799d851b47bSFugang Duan ec.rx_coalesce_usecs = FEC_ITR_ICTT_DEFAULT; 2800d851b47bSFugang Duan ec.rx_max_coalesced_frames = FEC_ITR_ICFT_DEFAULT; 2801d851b47bSFugang Duan 2802d851b47bSFugang Duan ec.tx_coalesce_usecs = FEC_ITR_ICTT_DEFAULT; 2803d851b47bSFugang Duan ec.tx_max_coalesced_frames = FEC_ITR_ICFT_DEFAULT; 2804d851b47bSFugang Duan 2805f3ccfda1SYufeng Mo fec_enet_set_coalesce(ndev, &ec, NULL, NULL); 2806d851b47bSFugang Duan } 2807d851b47bSFugang Duan 28081b7bde6dSNimrod Andy static int fec_enet_get_tunable(struct net_device *netdev, 28091b7bde6dSNimrod Andy const struct ethtool_tunable *tuna, 28101b7bde6dSNimrod Andy void *data) 28111b7bde6dSNimrod Andy { 28121b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(netdev); 28131b7bde6dSNimrod Andy int ret = 0; 28141b7bde6dSNimrod Andy 28151b7bde6dSNimrod Andy switch (tuna->id) { 28161b7bde6dSNimrod Andy case ETHTOOL_RX_COPYBREAK: 28171b7bde6dSNimrod Andy *(u32 *)data = fep->rx_copybreak; 28181b7bde6dSNimrod Andy break; 28191b7bde6dSNimrod Andy default: 28201b7bde6dSNimrod Andy ret = -EINVAL; 28211b7bde6dSNimrod Andy break; 28221b7bde6dSNimrod Andy } 28231b7bde6dSNimrod Andy 28241b7bde6dSNimrod Andy return ret; 28251b7bde6dSNimrod Andy } 28261b7bde6dSNimrod Andy 28271b7bde6dSNimrod Andy static int fec_enet_set_tunable(struct net_device *netdev, 28281b7bde6dSNimrod Andy const struct ethtool_tunable *tuna, 28291b7bde6dSNimrod Andy const void *data) 28301b7bde6dSNimrod Andy { 28311b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(netdev); 28321b7bde6dSNimrod Andy int ret = 0; 28331b7bde6dSNimrod Andy 28341b7bde6dSNimrod Andy switch (tuna->id) { 28351b7bde6dSNimrod Andy case ETHTOOL_RX_COPYBREAK: 28361b7bde6dSNimrod Andy fep->rx_copybreak = *(u32 *)data; 28371b7bde6dSNimrod Andy break; 28381b7bde6dSNimrod Andy default: 28391b7bde6dSNimrod Andy ret = -EINVAL; 28401b7bde6dSNimrod Andy break; 28411b7bde6dSNimrod Andy } 28421b7bde6dSNimrod Andy 28431b7bde6dSNimrod Andy return ret; 28441b7bde6dSNimrod Andy } 28451b7bde6dSNimrod Andy 2846b82f8c3fSFugang Duan /* LPI Sleep Ts count base on tx clk (clk_ref). 2847b82f8c3fSFugang Duan * The lpi sleep cnt value = X us / (cycle_ns). 2848b82f8c3fSFugang Duan */ 2849b82f8c3fSFugang Duan static int fec_enet_us_to_tx_cycle(struct net_device *ndev, int us) 2850b82f8c3fSFugang Duan { 2851b82f8c3fSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2852b82f8c3fSFugang Duan 2853b82f8c3fSFugang Duan return us * (fep->clk_ref_rate / 1000) / 1000; 2854b82f8c3fSFugang Duan } 2855b82f8c3fSFugang Duan 2856b82f8c3fSFugang Duan static int fec_enet_eee_mode_set(struct net_device *ndev, bool enable) 2857b82f8c3fSFugang Duan { 2858b82f8c3fSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2859b82f8c3fSFugang Duan struct ethtool_eee *p = &fep->eee; 2860b82f8c3fSFugang Duan unsigned int sleep_cycle, wake_cycle; 2861b82f8c3fSFugang Duan int ret = 0; 2862b82f8c3fSFugang Duan 2863b82f8c3fSFugang Duan if (enable) { 286453243d41SJisheng Zhang ret = phy_init_eee(ndev->phydev, false); 2865b82f8c3fSFugang Duan if (ret) 2866b82f8c3fSFugang Duan return ret; 2867b82f8c3fSFugang Duan 2868b82f8c3fSFugang Duan sleep_cycle = fec_enet_us_to_tx_cycle(ndev, p->tx_lpi_timer); 2869b82f8c3fSFugang Duan wake_cycle = sleep_cycle; 2870b82f8c3fSFugang Duan } else { 2871b82f8c3fSFugang Duan sleep_cycle = 0; 2872b82f8c3fSFugang Duan wake_cycle = 0; 2873b82f8c3fSFugang Duan } 2874b82f8c3fSFugang Duan 2875b82f8c3fSFugang Duan p->tx_lpi_enabled = enable; 2876b82f8c3fSFugang Duan p->eee_enabled = enable; 2877b82f8c3fSFugang Duan p->eee_active = enable; 2878b82f8c3fSFugang Duan 2879b82f8c3fSFugang Duan writel(sleep_cycle, fep->hwp + FEC_LPI_SLEEP); 2880b82f8c3fSFugang Duan writel(wake_cycle, fep->hwp + FEC_LPI_WAKE); 2881b82f8c3fSFugang Duan 2882b82f8c3fSFugang Duan return 0; 2883b82f8c3fSFugang Duan } 2884b82f8c3fSFugang Duan 2885b82f8c3fSFugang Duan static int 2886b82f8c3fSFugang Duan fec_enet_get_eee(struct net_device *ndev, struct ethtool_eee *edata) 2887b82f8c3fSFugang Duan { 2888b82f8c3fSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2889b82f8c3fSFugang Duan struct ethtool_eee *p = &fep->eee; 2890b82f8c3fSFugang Duan 2891b82f8c3fSFugang Duan if (!(fep->quirks & FEC_QUIRK_HAS_EEE)) 2892b82f8c3fSFugang Duan return -EOPNOTSUPP; 2893b82f8c3fSFugang Duan 2894b82f8c3fSFugang Duan if (!netif_running(ndev)) 2895b82f8c3fSFugang Duan return -ENETDOWN; 2896b82f8c3fSFugang Duan 2897b82f8c3fSFugang Duan edata->eee_enabled = p->eee_enabled; 2898b82f8c3fSFugang Duan edata->eee_active = p->eee_active; 2899b82f8c3fSFugang Duan edata->tx_lpi_timer = p->tx_lpi_timer; 2900b82f8c3fSFugang Duan edata->tx_lpi_enabled = p->tx_lpi_enabled; 2901b82f8c3fSFugang Duan 2902b82f8c3fSFugang Duan return phy_ethtool_get_eee(ndev->phydev, edata); 2903b82f8c3fSFugang Duan } 2904b82f8c3fSFugang Duan 2905b82f8c3fSFugang Duan static int 2906b82f8c3fSFugang Duan fec_enet_set_eee(struct net_device *ndev, struct ethtool_eee *edata) 2907b82f8c3fSFugang Duan { 2908b82f8c3fSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2909b82f8c3fSFugang Duan struct ethtool_eee *p = &fep->eee; 2910b82f8c3fSFugang Duan int ret = 0; 2911b82f8c3fSFugang Duan 2912b82f8c3fSFugang Duan if (!(fep->quirks & FEC_QUIRK_HAS_EEE)) 2913b82f8c3fSFugang Duan return -EOPNOTSUPP; 2914b82f8c3fSFugang Duan 2915b82f8c3fSFugang Duan if (!netif_running(ndev)) 2916b82f8c3fSFugang Duan return -ENETDOWN; 2917b82f8c3fSFugang Duan 2918b82f8c3fSFugang Duan p->tx_lpi_timer = edata->tx_lpi_timer; 2919b82f8c3fSFugang Duan 2920b82f8c3fSFugang Duan if (!edata->eee_enabled || !edata->tx_lpi_enabled || 2921b82f8c3fSFugang Duan !edata->tx_lpi_timer) 2922b82f8c3fSFugang Duan ret = fec_enet_eee_mode_set(ndev, false); 2923b82f8c3fSFugang Duan else 2924b82f8c3fSFugang Duan ret = fec_enet_eee_mode_set(ndev, true); 2925b82f8c3fSFugang Duan 2926b82f8c3fSFugang Duan if (ret) 2927b82f8c3fSFugang Duan return ret; 2928b82f8c3fSFugang Duan 2929b82f8c3fSFugang Duan return phy_ethtool_set_eee(ndev->phydev, edata); 2930b82f8c3fSFugang Duan } 2931b82f8c3fSFugang Duan 2932de40ed31SNimrod Andy static void 2933de40ed31SNimrod Andy fec_enet_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) 2934de40ed31SNimrod Andy { 2935de40ed31SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 2936de40ed31SNimrod Andy 2937de40ed31SNimrod Andy if (fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET) { 2938de40ed31SNimrod Andy wol->supported = WAKE_MAGIC; 2939de40ed31SNimrod Andy wol->wolopts = fep->wol_flag & FEC_WOL_FLAG_ENABLE ? WAKE_MAGIC : 0; 2940de40ed31SNimrod Andy } else { 2941de40ed31SNimrod Andy wol->supported = wol->wolopts = 0; 2942de40ed31SNimrod Andy } 2943de40ed31SNimrod Andy } 2944de40ed31SNimrod Andy 2945de40ed31SNimrod Andy static int 2946de40ed31SNimrod Andy fec_enet_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) 2947de40ed31SNimrod Andy { 2948de40ed31SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 2949de40ed31SNimrod Andy 2950de40ed31SNimrod Andy if (!(fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET)) 2951de40ed31SNimrod Andy return -EINVAL; 2952de40ed31SNimrod Andy 2953de40ed31SNimrod Andy if (wol->wolopts & ~WAKE_MAGIC) 2954de40ed31SNimrod Andy return -EINVAL; 2955de40ed31SNimrod Andy 2956de40ed31SNimrod Andy device_set_wakeup_enable(&ndev->dev, wol->wolopts & WAKE_MAGIC); 29570b6f65c7SJoakim Zhang if (device_may_wakeup(&ndev->dev)) 2958de40ed31SNimrod Andy fep->wol_flag |= FEC_WOL_FLAG_ENABLE; 29590b6f65c7SJoakim Zhang else 2960de40ed31SNimrod Andy fep->wol_flag &= (~FEC_WOL_FLAG_ENABLE); 2961de40ed31SNimrod Andy 2962de40ed31SNimrod Andy return 0; 2963de40ed31SNimrod Andy } 2964de40ed31SNimrod Andy 2965793fc096SFrank Li static const struct ethtool_ops fec_enet_ethtool_ops = { 2966d5e3c87dSJakub Kicinski .supported_coalesce_params = ETHTOOL_COALESCE_USECS | 2967d5e3c87dSJakub Kicinski ETHTOOL_COALESCE_MAX_FRAMES, 2968793fc096SFrank Li .get_drvinfo = fec_enet_get_drvinfo, 2969db65f35fSPhilippe Reynes .get_regs_len = fec_enet_get_regs_len, 2970db65f35fSPhilippe Reynes .get_regs = fec_enet_get_regs, 297111d59289SFlorian Fainelli .nway_reset = phy_ethtool_nway_reset, 2972c1d7c48fSRussell King .get_link = ethtool_op_get_link, 2973d851b47bSFugang Duan .get_coalesce = fec_enet_get_coalesce, 2974d851b47bSFugang Duan .set_coalesce = fec_enet_set_coalesce, 297538ae92dcSChris Healy #ifndef CONFIG_M5272 2976c1d7c48fSRussell King .get_pauseparam = fec_enet_get_pauseparam, 2977c1d7c48fSRussell King .set_pauseparam = fec_enet_set_pauseparam, 297838ae92dcSChris Healy .get_strings = fec_enet_get_strings, 2979c1d7c48fSRussell King .get_ethtool_stats = fec_enet_get_ethtool_stats, 298038ae92dcSChris Healy .get_sset_count = fec_enet_get_sset_count, 298138ae92dcSChris Healy #endif 2982c1d7c48fSRussell King .get_ts_info = fec_enet_get_ts_info, 29831b7bde6dSNimrod Andy .get_tunable = fec_enet_get_tunable, 29841b7bde6dSNimrod Andy .set_tunable = fec_enet_set_tunable, 2985de40ed31SNimrod Andy .get_wol = fec_enet_get_wol, 2986de40ed31SNimrod Andy .set_wol = fec_enet_set_wol, 2987b82f8c3fSFugang Duan .get_eee = fec_enet_get_eee, 2988b82f8c3fSFugang Duan .set_eee = fec_enet_set_eee, 29899365fbf5SPhilippe Reynes .get_link_ksettings = phy_ethtool_get_link_ksettings, 29909365fbf5SPhilippe Reynes .set_link_ksettings = phy_ethtool_set_link_ksettings, 29916016ba34SOleksij Rempel .self_test = net_selftest, 2992793fc096SFrank Li }; 2993793fc096SFrank Li 2994793fc096SFrank Li static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) 2995793fc096SFrank Li { 2996793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 299745f5c327SPhilippe Reynes struct phy_device *phydev = ndev->phydev; 2998793fc096SFrank Li 2999793fc096SFrank Li if (!netif_running(ndev)) 3000793fc096SFrank Li return -EINVAL; 3001793fc096SFrank Li 3002793fc096SFrank Li if (!phydev) 3003793fc096SFrank Li return -ENODEV; 3004793fc096SFrank Li 30051d5244d0SBen Hutchings if (fep->bufdesc_ex) { 300634074639SSergey Organov bool use_fec_hwts = !phy_has_hwtstamp(phydev); 300734074639SSergey Organov 300834074639SSergey Organov if (cmd == SIOCSHWTSTAMP) { 300934074639SSergey Organov if (use_fec_hwts) 30101d5244d0SBen Hutchings return fec_ptp_set(ndev, rq); 301134074639SSergey Organov fec_ptp_disable_hwts(ndev); 301234074639SSergey Organov } else if (cmd == SIOCGHWTSTAMP) { 301334074639SSergey Organov if (use_fec_hwts) 30141d5244d0SBen Hutchings return fec_ptp_get(ndev, rq); 30151d5244d0SBen Hutchings } 301634074639SSergey Organov } 3017793fc096SFrank Li 3018793fc096SFrank Li return phy_mii_ioctl(phydev, rq, cmd); 3019793fc096SFrank Li } 3020793fc096SFrank Li 3021793fc096SFrank Li static void fec_enet_free_buffers(struct net_device *ndev) 3022793fc096SFrank Li { 3023793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3024793fc096SFrank Li unsigned int i; 3025793fc096SFrank Li struct sk_buff *skb; 3026793fc096SFrank Li struct bufdesc *bdp; 30274d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 30284d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 302959d0f746SFrank Li unsigned int q; 3030793fc096SFrank Li 303159d0f746SFrank Li for (q = 0; q < fep->num_rx_queues; q++) { 303259d0f746SFrank Li rxq = fep->rx_queue[q]; 30337355f276STroy Kisky bdp = rxq->bd.base; 30347355f276STroy Kisky for (i = 0; i < rxq->bd.ring_size; i++) { 30354d494cdcSFugang Duan skb = rxq->rx_skbuff[i]; 30364d494cdcSFugang Duan rxq->rx_skbuff[i] = NULL; 3037730ee360SRussell King if (skb) { 303859d0f746SFrank Li dma_unmap_single(&fep->pdev->dev, 30395cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 3040b64bf4b7SNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 304159d0f746SFrank Li DMA_FROM_DEVICE); 3042793fc096SFrank Li dev_kfree_skb(skb); 3043730ee360SRussell King } 30447355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 304559d0f746SFrank Li } 3046793fc096SFrank Li } 3047793fc096SFrank Li 304859d0f746SFrank Li for (q = 0; q < fep->num_tx_queues; q++) { 304959d0f746SFrank Li txq = fep->tx_queue[q]; 30507355f276STroy Kisky for (i = 0; i < txq->bd.ring_size; i++) { 30514d494cdcSFugang Duan kfree(txq->tx_bounce[i]); 30524d494cdcSFugang Duan txq->tx_bounce[i] = NULL; 30534d494cdcSFugang Duan skb = txq->tx_skbuff[i]; 30544d494cdcSFugang Duan txq->tx_skbuff[i] = NULL; 30558b7c9efaSRussell King dev_kfree_skb(skb); 30568b7c9efaSRussell King } 3057793fc096SFrank Li } 305859d0f746SFrank Li } 3059793fc096SFrank Li 306059d0f746SFrank Li static void fec_enet_free_queue(struct net_device *ndev) 306159d0f746SFrank Li { 306259d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 306359d0f746SFrank Li int i; 306459d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 306559d0f746SFrank Li 306659d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) 306759d0f746SFrank Li if (fep->tx_queue[i] && fep->tx_queue[i]->tso_hdrs) { 306859d0f746SFrank Li txq = fep->tx_queue[i]; 306994920128SFugang Duan dma_free_coherent(&fep->pdev->dev, 30707355f276STroy Kisky txq->bd.ring_size * TSO_HEADER_SIZE, 307159d0f746SFrank Li txq->tso_hdrs, 307259d0f746SFrank Li txq->tso_hdrs_dma); 307359d0f746SFrank Li } 307459d0f746SFrank Li 307559d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) 307659d0f746SFrank Li kfree(fep->rx_queue[i]); 307759d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) 307859d0f746SFrank Li kfree(fep->tx_queue[i]); 307959d0f746SFrank Li } 308059d0f746SFrank Li 308159d0f746SFrank Li static int fec_enet_alloc_queue(struct net_device *ndev) 308259d0f746SFrank Li { 308359d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 308459d0f746SFrank Li int i; 308559d0f746SFrank Li int ret = 0; 308659d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 308759d0f746SFrank Li 308859d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 308959d0f746SFrank Li txq = kzalloc(sizeof(*txq), GFP_KERNEL); 309059d0f746SFrank Li if (!txq) { 309159d0f746SFrank Li ret = -ENOMEM; 309259d0f746SFrank Li goto alloc_failed; 309359d0f746SFrank Li } 309459d0f746SFrank Li 309559d0f746SFrank Li fep->tx_queue[i] = txq; 30967355f276STroy Kisky txq->bd.ring_size = TX_RING_SIZE; 30977355f276STroy Kisky fep->total_tx_ring_size += fep->tx_queue[i]->bd.ring_size; 309859d0f746SFrank Li 309959d0f746SFrank Li txq->tx_stop_threshold = FEC_MAX_SKB_DESCS; 310059d0f746SFrank Li txq->tx_wake_threshold = 31017355f276STroy Kisky (txq->bd.ring_size - txq->tx_stop_threshold) / 2; 310259d0f746SFrank Li 310394920128SFugang Duan txq->tso_hdrs = dma_alloc_coherent(&fep->pdev->dev, 31047355f276STroy Kisky txq->bd.ring_size * TSO_HEADER_SIZE, 310559d0f746SFrank Li &txq->tso_hdrs_dma, 310659d0f746SFrank Li GFP_KERNEL); 310759d0f746SFrank Li if (!txq->tso_hdrs) { 310859d0f746SFrank Li ret = -ENOMEM; 310959d0f746SFrank Li goto alloc_failed; 311059d0f746SFrank Li } 311159d0f746SFrank Li } 311259d0f746SFrank Li 311359d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) { 311459d0f746SFrank Li fep->rx_queue[i] = kzalloc(sizeof(*fep->rx_queue[i]), 311559d0f746SFrank Li GFP_KERNEL); 311659d0f746SFrank Li if (!fep->rx_queue[i]) { 311759d0f746SFrank Li ret = -ENOMEM; 311859d0f746SFrank Li goto alloc_failed; 311959d0f746SFrank Li } 312059d0f746SFrank Li 31217355f276STroy Kisky fep->rx_queue[i]->bd.ring_size = RX_RING_SIZE; 31227355f276STroy Kisky fep->total_rx_ring_size += fep->rx_queue[i]->bd.ring_size; 312359d0f746SFrank Li } 312459d0f746SFrank Li return ret; 312559d0f746SFrank Li 312659d0f746SFrank Li alloc_failed: 312759d0f746SFrank Li fec_enet_free_queue(ndev); 312859d0f746SFrank Li return ret; 312959d0f746SFrank Li } 313059d0f746SFrank Li 313159d0f746SFrank Li static int 313259d0f746SFrank Li fec_enet_alloc_rxq_buffers(struct net_device *ndev, unsigned int queue) 3133793fc096SFrank Li { 3134793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3135793fc096SFrank Li unsigned int i; 3136793fc096SFrank Li struct sk_buff *skb; 3137793fc096SFrank Li struct bufdesc *bdp; 31384d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 3139793fc096SFrank Li 314059d0f746SFrank Li rxq = fep->rx_queue[queue]; 31417355f276STroy Kisky bdp = rxq->bd.base; 31427355f276STroy Kisky for (i = 0; i < rxq->bd.ring_size; i++) { 3143b885aab3SMichael Trimarchi skb = __netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE, GFP_KERNEL); 3144ffdce2ccSRussell King if (!skb) 3145ffdce2ccSRussell King goto err_alloc; 3146793fc096SFrank Li 31471b7bde6dSNimrod Andy if (fec_enet_new_rxbdp(ndev, bdp, skb)) { 3148730ee360SRussell King dev_kfree_skb(skb); 3149ffdce2ccSRussell King goto err_alloc; 3150d842a31fSDuan Fugang-B38611 } 3151730ee360SRussell King 31524d494cdcSFugang Duan rxq->rx_skbuff[i] = skb; 31535cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); 3154793fc096SFrank Li 3155793fc096SFrank Li if (fep->bufdesc_ex) { 3156793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 31575cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(BD_ENET_RX_INT); 3158793fc096SFrank Li } 3159793fc096SFrank Li 31607355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 3161793fc096SFrank Li } 3162793fc096SFrank Li 3163793fc096SFrank Li /* Set the last buffer to wrap. */ 31647355f276STroy Kisky bdp = fec_enet_get_prevdesc(bdp, &rxq->bd); 31655cfa3039SJohannes Berg bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 316659d0f746SFrank Li return 0; 3167793fc096SFrank Li 316859d0f746SFrank Li err_alloc: 316959d0f746SFrank Li fec_enet_free_buffers(ndev); 317059d0f746SFrank Li return -ENOMEM; 317159d0f746SFrank Li } 317259d0f746SFrank Li 317359d0f746SFrank Li static int 317459d0f746SFrank Li fec_enet_alloc_txq_buffers(struct net_device *ndev, unsigned int queue) 317559d0f746SFrank Li { 317659d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 317759d0f746SFrank Li unsigned int i; 317859d0f746SFrank Li struct bufdesc *bdp; 317959d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 318059d0f746SFrank Li 318159d0f746SFrank Li txq = fep->tx_queue[queue]; 31827355f276STroy Kisky bdp = txq->bd.base; 31837355f276STroy Kisky for (i = 0; i < txq->bd.ring_size; i++) { 31844d494cdcSFugang Duan txq->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL); 31854d494cdcSFugang Duan if (!txq->tx_bounce[i]) 3186ffdce2ccSRussell King goto err_alloc; 3187793fc096SFrank Li 31885cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(0); 31895cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(0); 3190793fc096SFrank Li 3191793fc096SFrank Li if (fep->bufdesc_ex) { 3192793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 31935cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(BD_ENET_TX_INT); 3194793fc096SFrank Li } 3195793fc096SFrank Li 31967355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 3197793fc096SFrank Li } 3198793fc096SFrank Li 3199793fc096SFrank Li /* Set the last buffer to wrap. */ 32007355f276STroy Kisky bdp = fec_enet_get_prevdesc(bdp, &txq->bd); 32015cfa3039SJohannes Berg bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 3202793fc096SFrank Li 3203793fc096SFrank Li return 0; 3204ffdce2ccSRussell King 3205ffdce2ccSRussell King err_alloc: 3206ffdce2ccSRussell King fec_enet_free_buffers(ndev); 3207ffdce2ccSRussell King return -ENOMEM; 3208793fc096SFrank Li } 3209793fc096SFrank Li 321059d0f746SFrank Li static int fec_enet_alloc_buffers(struct net_device *ndev) 321159d0f746SFrank Li { 321259d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 321359d0f746SFrank Li unsigned int i; 321459d0f746SFrank Li 321559d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) 321659d0f746SFrank Li if (fec_enet_alloc_rxq_buffers(ndev, i)) 321759d0f746SFrank Li return -ENOMEM; 321859d0f746SFrank Li 321959d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) 322059d0f746SFrank Li if (fec_enet_alloc_txq_buffers(ndev, i)) 322159d0f746SFrank Li return -ENOMEM; 322259d0f746SFrank Li return 0; 322359d0f746SFrank Li } 322459d0f746SFrank Li 3225793fc096SFrank Li static int 3226793fc096SFrank Li fec_enet_open(struct net_device *ndev) 3227793fc096SFrank Li { 3228793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3229793fc096SFrank Li int ret; 32301b0a83acSRichard Leitner bool reset_again; 3231793fc096SFrank Li 3232da875fa5SZhang Qilong ret = pm_runtime_resume_and_get(&fep->pdev->dev); 3233b0c6ce24SFabio Estevam if (ret < 0) 32348fff755eSAndrew Lunn return ret; 32358fff755eSAndrew Lunn 32365bbde4d2SNimrod Andy pinctrl_pm_select_default_state(&fep->pdev->dev); 3237e8fcfcd5SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 3238e8fcfcd5SNimrod Andy if (ret) 32398fff755eSAndrew Lunn goto clk_enable; 3240e8fcfcd5SNimrod Andy 32411b0a83acSRichard Leitner /* During the first fec_enet_open call the PHY isn't probed at this 32421b0a83acSRichard Leitner * point. Therefore the phy_reset_after_clk_enable() call within 32431b0a83acSRichard Leitner * fec_enet_clk_enable() fails. As we need this reset in order to be 32441b0a83acSRichard Leitner * sure the PHY is working correctly we check if we need to reset again 32451b0a83acSRichard Leitner * later when the PHY is probed 32461b0a83acSRichard Leitner */ 32471b0a83acSRichard Leitner if (ndev->phydev && ndev->phydev->drv) 32481b0a83acSRichard Leitner reset_again = false; 32491b0a83acSRichard Leitner else 32501b0a83acSRichard Leitner reset_again = true; 32511b0a83acSRichard Leitner 3252793fc096SFrank Li /* I should reset the ring buffers here, but I don't yet know 3253793fc096SFrank Li * a simple way to do that. 3254793fc096SFrank Li */ 3255793fc096SFrank Li 3256793fc096SFrank Li ret = fec_enet_alloc_buffers(ndev); 3257793fc096SFrank Li if (ret) 3258681d2421SFabio Estevam goto err_enet_alloc; 3259793fc096SFrank Li 326055dd2753SNimrod Andy /* Init MAC prior to mii bus probe */ 326155dd2753SNimrod Andy fec_restart(ndev); 326255dd2753SNimrod Andy 32631b0a83acSRichard Leitner /* Call phy_reset_after_clk_enable() again if it failed during 32641b0a83acSRichard Leitner * phy_reset_after_clk_enable() before because the PHY wasn't probed. 32651b0a83acSRichard Leitner */ 32661b0a83acSRichard Leitner if (reset_again) 326764a632daSMarek Vasut fec_enet_phy_reset_after_clk_enable(ndev); 32681b0a83acSRichard Leitner 32690da1ccbbSMarek Vasut /* Probe and connect to PHY when open the interface */ 32700da1ccbbSMarek Vasut ret = fec_enet_mii_probe(ndev); 32710da1ccbbSMarek Vasut if (ret) 32720da1ccbbSMarek Vasut goto err_enet_mii_probe; 3273681d2421SFabio Estevam 3274681d2421SFabio Estevam if (fep->quirks & FEC_QUIRK_ERR006687) 3275793fc096SFrank Li imx6q_cpuidle_fec_irqs_used(); 3276793fc096SFrank Li 3277793fc096SFrank Li napi_enable(&fep->napi); 3278793fc096SFrank Li phy_start(ndev->phydev); 3279793fc096SFrank Li netif_tx_start_all_queues(ndev); 3280793fc096SFrank Li 3281793fc096SFrank Li device_set_wakeup_enable(&ndev->dev, fep->wol_flag & 328245f5c327SPhilippe Reynes FEC_WOL_FLAG_ENABLE); 3283d76cfae9SRussell King 328431a6de34SRussell King return 0; 3285793fc096SFrank Li 3286b49cd504SRussell King err_enet_mii_probe: 3287793fc096SFrank Li fec_enet_free_buffers(ndev); 328831a6de34SRussell King err_enet_alloc: 3289793fc096SFrank Li fec_enet_clk_enable(ndev, false); 329045f5c327SPhilippe Reynes clk_enable: 3291793fc096SFrank Li pm_runtime_mark_last_busy(&fep->pdev->dev); 329229380905SLucas Stach pm_runtime_put_autosuspend(&fep->pdev->dev); 329329380905SLucas Stach pinctrl_pm_select_sleep_state(&fep->pdev->dev); 329429380905SLucas Stach return ret; 329580cca775SNikita Yushchenko } 329680cca775SNikita Yushchenko 3297e8fcfcd5SNimrod Andy static int 32985bbde4d2SNimrod Andy fec_enet_close(struct net_device *ndev) 32998fff755eSAndrew Lunn { 33008fff755eSAndrew Lunn struct fec_enet_private *fep = netdev_priv(ndev); 33018fff755eSAndrew Lunn 3302793fc096SFrank Li phy_stop(ndev->phydev); 3303793fc096SFrank Li 3304793fc096SFrank Li if (netif_device_present(ndev)) { 3305793fc096SFrank Li napi_disable(&fep->napi); 3306793fc096SFrank Li netif_tx_disable(ndev); 3307793fc096SFrank Li fec_stop(ndev); 3308793fc096SFrank Li } 3309793fc096SFrank Li 3310793fc096SFrank Li phy_disconnect(ndev->phydev); 3311793fc096SFrank Li 3312793fc096SFrank Li if (fep->quirks & FEC_QUIRK_ERR006687) 3313793fc096SFrank Li imx6q_cpuidle_fec_irqs_unused(); 3314793fc096SFrank Li 3315793fc096SFrank Li fec_enet_update_ethtool_stats(ndev); 3316793fc096SFrank Li 33176176e89cSJiri Kosina fec_enet_clk_enable(ndev, false); 3318793fc096SFrank Li pinctrl_pm_select_sleep_state(&fep->pdev->dev); 3319793fc096SFrank Li pm_runtime_mark_last_busy(&fep->pdev->dev); 3320793fc096SFrank Li pm_runtime_put_autosuspend(&fep->pdev->dev); 3321793fc096SFrank Li 3322793fc096SFrank Li fec_enet_free_buffers(ndev); 3323793fc096SFrank Li 3324793fc096SFrank Li return 0; 3325793fc096SFrank Li } 332601f8902bSRui Sousa 3327793fc096SFrank Li /* Set or clear the multicast filter for this adaptor. 3328793fc096SFrank Li * Skeleton taken from sunlance driver. 3329793fc096SFrank Li * The CPM Ethernet implementation allows Multicast as well as individual 3330793fc096SFrank Li * MAC address filtering. Some of the drivers check to make sure it is 3331793fc096SFrank Li * a group multicast address, and discard those that are not. I guess I 3332793fc096SFrank Li * will do the same for now, but just remove the test if you want 3333793fc096SFrank Li * individual filtering as well (do the upper net layers want or support 3334793fc096SFrank Li * this kind of feature?). 3335793fc096SFrank Li */ 3336793fc096SFrank Li 3337793fc096SFrank Li #define FEC_HASH_BITS 6 /* #bits in hash */ 3338793fc096SFrank Li 3339793fc096SFrank Li static void set_multicast_list(struct net_device *ndev) 3340793fc096SFrank Li { 3341793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3342793fc096SFrank Li struct netdev_hw_addr *ha; 334316f6e983SKrzysztof Kozlowski unsigned int crc, tmp; 3344793fc096SFrank Li unsigned char hash; 3345793fc096SFrank Li unsigned int hash_high = 0, hash_low = 0; 3346793fc096SFrank Li 3347793fc096SFrank Li if (ndev->flags & IFF_PROMISC) { 3348793fc096SFrank Li tmp = readl(fep->hwp + FEC_R_CNTRL); 3349793fc096SFrank Li tmp |= 0x8; 3350793fc096SFrank Li writel(tmp, fep->hwp + FEC_R_CNTRL); 3351793fc096SFrank Li return; 3352793fc096SFrank Li } 3353793fc096SFrank Li 3354793fc096SFrank Li tmp = readl(fep->hwp + FEC_R_CNTRL); 3355793fc096SFrank Li tmp &= ~0x8; 3356793fc096SFrank Li writel(tmp, fep->hwp + FEC_R_CNTRL); 3357793fc096SFrank Li 3358793fc096SFrank Li if (ndev->flags & IFF_ALLMULTI) { 3359793fc096SFrank Li /* Catch all multicast addresses, so set the 3360793fc096SFrank Li * filter to all 1's 3361793fc096SFrank Li */ 3362793fc096SFrank Li writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 3363793fc096SFrank Li writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 3364793fc096SFrank Li 3365793fc096SFrank Li return; 3366793fc096SFrank Li } 3367793fc096SFrank Li 336801f8902bSRui Sousa /* Add the addresses in hash register */ 3369793fc096SFrank Li netdev_for_each_mc_addr(ha, ndev) { 3370793fc096SFrank Li /* calculate crc32 value of mac address */ 337116f6e983SKrzysztof Kozlowski crc = ether_crc_le(ndev->addr_len, ha->addr); 3372793fc096SFrank Li 33736176e89cSJiri Kosina /* only upper 6 bits (FEC_HASH_BITS) are used 3374981a0547SPeter Meerwald-Stadler * which point to specific bit in the hash registers 3375793fc096SFrank Li */ 33766176e89cSJiri Kosina hash = (crc >> (32 - FEC_HASH_BITS)) & 0x3f; 3377793fc096SFrank Li 337801f8902bSRui Sousa if (hash > 31) 337901f8902bSRui Sousa hash_high |= 1 << (hash - 32); 338001f8902bSRui Sousa else 338101f8902bSRui Sousa hash_low |= 1 << hash; 3382793fc096SFrank Li } 338301f8902bSRui Sousa 338401f8902bSRui Sousa writel(hash_high, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 338501f8902bSRui Sousa writel(hash_low, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 3386793fc096SFrank Li } 3387793fc096SFrank Li 3388793fc096SFrank Li /* Set a MAC change in hardware. */ 3389793fc096SFrank Li static int 3390793fc096SFrank Li fec_set_mac_address(struct net_device *ndev, void *p) 3391793fc096SFrank Li { 3392793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3393793fc096SFrank Li struct sockaddr *addr = p; 3394793fc096SFrank Li 339544934facSLucas Stach if (addr) { 3396793fc096SFrank Li if (!is_valid_ether_addr(addr->sa_data)) 3397793fc096SFrank Li return -EADDRNOTAVAIL; 3398a05e4c0aSJakub Kicinski eth_hw_addr_set(ndev, addr->sa_data); 339944934facSLucas Stach } 3400793fc096SFrank Li 34019638d19eSNimrod Andy /* Add netif status check here to avoid system hang in below case: 34029638d19eSNimrod Andy * ifconfig ethx down; ifconfig ethx hw ether xx:xx:xx:xx:xx:xx; 34039638d19eSNimrod Andy * After ethx down, fec all clocks are gated off and then register 34049638d19eSNimrod Andy * access causes system hang. 34059638d19eSNimrod Andy */ 34069638d19eSNimrod Andy if (!netif_running(ndev)) 34079638d19eSNimrod Andy return 0; 34089638d19eSNimrod Andy 3409793fc096SFrank Li writel(ndev->dev_addr[3] | (ndev->dev_addr[2] << 8) | 3410793fc096SFrank Li (ndev->dev_addr[1] << 16) | (ndev->dev_addr[0] << 24), 3411793fc096SFrank Li fep->hwp + FEC_ADDR_LOW); 3412793fc096SFrank Li writel((ndev->dev_addr[5] << 16) | (ndev->dev_addr[4] << 24), 3413793fc096SFrank Li fep->hwp + FEC_ADDR_HIGH); 3414793fc096SFrank Li return 0; 3415793fc096SFrank Li } 3416793fc096SFrank Li 3417793fc096SFrank Li #ifdef CONFIG_NET_POLL_CONTROLLER 3418793fc096SFrank Li /** 3419793fc096SFrank Li * fec_poll_controller - FEC Poll controller function 3420793fc096SFrank Li * @dev: The FEC network adapter 3421793fc096SFrank Li * 3422793fc096SFrank Li * Polled functionality used by netconsole and others in non interrupt mode 3423793fc096SFrank Li * 3424793fc096SFrank Li */ 3425793fc096SFrank Li static void fec_poll_controller(struct net_device *dev) 3426793fc096SFrank Li { 3427793fc096SFrank Li int i; 3428793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(dev); 3429793fc096SFrank Li 3430793fc096SFrank Li for (i = 0; i < FEC_IRQ_NUM; i++) { 3431793fc096SFrank Li if (fep->irq[i] > 0) { 3432793fc096SFrank Li disable_irq(fep->irq[i]); 3433793fc096SFrank Li fec_enet_interrupt(fep->irq[i], dev); 3434793fc096SFrank Li enable_irq(fep->irq[i]); 3435793fc096SFrank Li } 3436793fc096SFrank Li } 3437793fc096SFrank Li } 3438793fc096SFrank Li #endif 3439793fc096SFrank Li 34405bc26726SNimrod Andy static inline void fec_enet_set_netdev_features(struct net_device *netdev, 34414c09eed9SJim Baxter netdev_features_t features) 34424c09eed9SJim Baxter { 34434c09eed9SJim Baxter struct fec_enet_private *fep = netdev_priv(netdev); 34444c09eed9SJim Baxter netdev_features_t changed = features ^ netdev->features; 34454c09eed9SJim Baxter 34464c09eed9SJim Baxter netdev->features = features; 34474c09eed9SJim Baxter 34484c09eed9SJim Baxter /* Receive checksum has been changed */ 34494c09eed9SJim Baxter if (changed & NETIF_F_RXCSUM) { 34504c09eed9SJim Baxter if (features & NETIF_F_RXCSUM) 34514c09eed9SJim Baxter fep->csum_flags |= FLAG_RX_CSUM_ENABLED; 34524c09eed9SJim Baxter else 34534c09eed9SJim Baxter fep->csum_flags &= ~FLAG_RX_CSUM_ENABLED; 34548506fa1dSRussell King } 34555bc26726SNimrod Andy } 34564c09eed9SJim Baxter 34575bc26726SNimrod Andy static int fec_set_features(struct net_device *netdev, 34585bc26726SNimrod Andy netdev_features_t features) 34595bc26726SNimrod Andy { 34605bc26726SNimrod Andy struct fec_enet_private *fep = netdev_priv(netdev); 34615bc26726SNimrod Andy netdev_features_t changed = features ^ netdev->features; 34625bc26726SNimrod Andy 34635b40f709SFabio Estevam if (netif_running(netdev) && changed & NETIF_F_RXCSUM) { 34645bc26726SNimrod Andy napi_disable(&fep->napi); 34655bc26726SNimrod Andy netif_tx_lock_bh(netdev); 34665bc26726SNimrod Andy fec_stop(netdev); 34675bc26726SNimrod Andy fec_enet_set_netdev_features(netdev, features); 3468ef83337dSRussell King fec_restart(netdev); 34694d494cdcSFugang Duan netif_tx_wake_all_queues(netdev); 34706af42d42SRussell King netif_tx_unlock_bh(netdev); 3471dbc64a8eSRussell King napi_enable(&fep->napi); 34725bc26726SNimrod Andy } else { 34735bc26726SNimrod Andy fec_enet_set_netdev_features(netdev, features); 34744c09eed9SJim Baxter } 34754c09eed9SJim Baxter 34764c09eed9SJim Baxter return 0; 34774c09eed9SJim Baxter } 34784c09eed9SJim Baxter 347952c4a1a8SFugang Duan static u16 fec_enet_get_raw_vlan_tci(struct sk_buff *skb) 348052c4a1a8SFugang Duan { 348152c4a1a8SFugang Duan struct vlan_ethhdr *vhdr; 348252c4a1a8SFugang Duan unsigned short vlan_TCI = 0; 348352c4a1a8SFugang Duan 348452c4a1a8SFugang Duan if (skb->protocol == htons(ETH_P_ALL)) { 348552c4a1a8SFugang Duan vhdr = (struct vlan_ethhdr *)(skb->data); 348652c4a1a8SFugang Duan vlan_TCI = ntohs(vhdr->h_vlan_TCI); 348752c4a1a8SFugang Duan } 348852c4a1a8SFugang Duan 348952c4a1a8SFugang Duan return vlan_TCI; 349052c4a1a8SFugang Duan } 349152c4a1a8SFugang Duan 349252c4a1a8SFugang Duan static u16 fec_enet_select_queue(struct net_device *ndev, struct sk_buff *skb, 349352c4a1a8SFugang Duan struct net_device *sb_dev) 349452c4a1a8SFugang Duan { 349552c4a1a8SFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 349652c4a1a8SFugang Duan u16 vlan_tag; 349752c4a1a8SFugang Duan 349852c4a1a8SFugang Duan if (!(fep->quirks & FEC_QUIRK_HAS_AVB)) 349952c4a1a8SFugang Duan return netdev_pick_tx(ndev, skb, NULL); 350052c4a1a8SFugang Duan 350152c4a1a8SFugang Duan vlan_tag = fec_enet_get_raw_vlan_tci(skb); 350252c4a1a8SFugang Duan if (!vlan_tag) 350352c4a1a8SFugang Duan return vlan_tag; 350452c4a1a8SFugang Duan 350552c4a1a8SFugang Duan return fec_enet_vlan_pri_to_queue[vlan_tag >> 13]; 350652c4a1a8SFugang Duan } 350752c4a1a8SFugang Duan 3508793fc096SFrank Li static const struct net_device_ops fec_netdev_ops = { 3509793fc096SFrank Li .ndo_open = fec_enet_open, 3510793fc096SFrank Li .ndo_stop = fec_enet_close, 3511793fc096SFrank Li .ndo_start_xmit = fec_enet_start_xmit, 351252c4a1a8SFugang Duan .ndo_select_queue = fec_enet_select_queue, 3513793fc096SFrank Li .ndo_set_rx_mode = set_multicast_list, 3514793fc096SFrank Li .ndo_validate_addr = eth_validate_addr, 3515793fc096SFrank Li .ndo_tx_timeout = fec_timeout, 3516793fc096SFrank Li .ndo_set_mac_address = fec_set_mac_address, 3517a7605370SArnd Bergmann .ndo_eth_ioctl = fec_enet_ioctl, 3518793fc096SFrank Li #ifdef CONFIG_NET_POLL_CONTROLLER 3519793fc096SFrank Li .ndo_poll_controller = fec_poll_controller, 3520793fc096SFrank Li #endif 35214c09eed9SJim Baxter .ndo_set_features = fec_set_features, 3522793fc096SFrank Li }; 3523793fc096SFrank Li 352453bb20d1STroy Kisky static const unsigned short offset_des_active_rxq[] = { 352553bb20d1STroy Kisky FEC_R_DES_ACTIVE_0, FEC_R_DES_ACTIVE_1, FEC_R_DES_ACTIVE_2 352653bb20d1STroy Kisky }; 352753bb20d1STroy Kisky 352853bb20d1STroy Kisky static const unsigned short offset_des_active_txq[] = { 352953bb20d1STroy Kisky FEC_X_DES_ACTIVE_0, FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2 353053bb20d1STroy Kisky }; 353153bb20d1STroy Kisky 3532793fc096SFrank Li /* 3533793fc096SFrank Li * XXX: We need to clean up on failure exits here. 3534793fc096SFrank Li * 3535793fc096SFrank Li */ 3536793fc096SFrank Li static int fec_enet_init(struct net_device *ndev) 3537793fc096SFrank Li { 3538793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3539793fc096SFrank Li struct bufdesc *cbd_base; 35404d494cdcSFugang Duan dma_addr_t bd_dma; 354155d0218aSNimrod Andy int bd_size; 354259d0f746SFrank Li unsigned int i; 35437355f276STroy Kisky unsigned dsize = fep->bufdesc_ex ? sizeof(struct bufdesc_ex) : 35447355f276STroy Kisky sizeof(struct bufdesc); 35457355f276STroy Kisky unsigned dsize_log2 = __fls(dsize); 3546453e9dc4SStefan Agner int ret; 354755d0218aSNimrod Andy 35487355f276STroy Kisky WARN_ON(dsize != (1 << dsize_log2)); 35493f1dcc6aSLucas Stach #if defined(CONFIG_ARM) || defined(CONFIG_ARM64) 355041ef84ceSFugang Duan fep->rx_align = 0xf; 355141ef84ceSFugang Duan fep->tx_align = 0xf; 355241ef84ceSFugang Duan #else 355341ef84ceSFugang Duan fep->rx_align = 0x3; 355441ef84ceSFugang Duan fep->tx_align = 0x3; 355541ef84ceSFugang Duan #endif 355641ef84ceSFugang Duan 3557453e9dc4SStefan Agner /* Check mask of the streaming and coherent API */ 3558453e9dc4SStefan Agner ret = dma_set_mask_and_coherent(&fep->pdev->dev, DMA_BIT_MASK(32)); 3559453e9dc4SStefan Agner if (ret < 0) { 3560453e9dc4SStefan Agner dev_warn(&fep->pdev->dev, "No suitable DMA available\n"); 3561453e9dc4SStefan Agner return ret; 3562453e9dc4SStefan Agner } 3563453e9dc4SStefan Agner 3564619fee9eSFugang Duan ret = fec_enet_alloc_queue(ndev); 3565619fee9eSFugang Duan if (ret) 3566619fee9eSFugang Duan return ret; 356779f33912SNimrod Andy 35687355f276STroy Kisky bd_size = (fep->total_tx_ring_size + fep->total_rx_ring_size) * dsize; 3569793fc096SFrank Li 3570793fc096SFrank Li /* Allocate memory for buffer descriptors. */ 3571c0a1a0a6SLucas Stach cbd_base = dmam_alloc_coherent(&fep->pdev->dev, bd_size, &bd_dma, 3572793fc096SFrank Li GFP_KERNEL); 35734d494cdcSFugang Duan if (!cbd_base) { 3574619fee9eSFugang Duan ret = -ENOMEM; 3575619fee9eSFugang Duan goto free_queue_mem; 35764d494cdcSFugang Duan } 3577793fc096SFrank Li 3578793fc096SFrank Li /* Get the Ethernet address */ 3579052fcc45SFugang Duan ret = fec_get_mac(ndev); 3580052fcc45SFugang Duan if (ret) 3581052fcc45SFugang Duan goto free_queue_mem; 3582052fcc45SFugang Duan 358344934facSLucas Stach /* make sure MAC we just acquired is programmed into the hw */ 358444934facSLucas Stach fec_set_mac_address(ndev, NULL); 3585793fc096SFrank Li 3586793fc096SFrank Li /* Set receive and transmit descriptor base. */ 358759d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) { 35887355f276STroy Kisky struct fec_enet_priv_rx_q *rxq = fep->rx_queue[i]; 35897355f276STroy Kisky unsigned size = dsize * rxq->bd.ring_size; 35907355f276STroy Kisky 35917355f276STroy Kisky rxq->bd.qid = i; 35927355f276STroy Kisky rxq->bd.base = cbd_base; 35937355f276STroy Kisky rxq->bd.cur = cbd_base; 35947355f276STroy Kisky rxq->bd.dma = bd_dma; 35957355f276STroy Kisky rxq->bd.dsize = dsize; 35967355f276STroy Kisky rxq->bd.dsize_log2 = dsize_log2; 359753bb20d1STroy Kisky rxq->bd.reg_desc_active = fep->hwp + offset_des_active_rxq[i]; 35987355f276STroy Kisky bd_dma += size; 35997355f276STroy Kisky cbd_base = (struct bufdesc *)(((void *)cbd_base) + size); 36007355f276STroy Kisky rxq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize); 360159d0f746SFrank Li } 360259d0f746SFrank Li 360359d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 36047355f276STroy Kisky struct fec_enet_priv_tx_q *txq = fep->tx_queue[i]; 36057355f276STroy Kisky unsigned size = dsize * txq->bd.ring_size; 36067355f276STroy Kisky 36077355f276STroy Kisky txq->bd.qid = i; 36087355f276STroy Kisky txq->bd.base = cbd_base; 36097355f276STroy Kisky txq->bd.cur = cbd_base; 36107355f276STroy Kisky txq->bd.dma = bd_dma; 36117355f276STroy Kisky txq->bd.dsize = dsize; 36127355f276STroy Kisky txq->bd.dsize_log2 = dsize_log2; 361353bb20d1STroy Kisky txq->bd.reg_desc_active = fep->hwp + offset_des_active_txq[i]; 36147355f276STroy Kisky bd_dma += size; 36157355f276STroy Kisky cbd_base = (struct bufdesc *)(((void *)cbd_base) + size); 36167355f276STroy Kisky txq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize); 361759d0f746SFrank Li } 36184d494cdcSFugang Duan 3619793fc096SFrank Li 3620793fc096SFrank Li /* The FEC Ethernet specific entries in the device structure */ 3621793fc096SFrank Li ndev->watchdog_timeo = TX_TIMEOUT; 3622793fc096SFrank Li ndev->netdev_ops = &fec_netdev_ops; 3623793fc096SFrank Li ndev->ethtool_ops = &fec_enet_ethtool_ops; 3624793fc096SFrank Li 3625793fc096SFrank Li writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK); 3626322555f5SFabio Estevam netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, NAPI_POLL_WEIGHT); 3627793fc096SFrank Li 36286b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_VLAN) 3629cdffcf1bSJim Baxter /* enable hw VLAN support */ 3630cdffcf1bSJim Baxter ndev->features |= NETIF_F_HW_VLAN_CTAG_RX; 3631cdffcf1bSJim Baxter 36326b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_CSUM) { 3633ee8b7a11SJakub Kicinski netif_set_tso_max_segs(ndev, FEC_MAX_TSO_SEGS); 363479f33912SNimrod Andy 36354c09eed9SJim Baxter /* enable hw accelerator */ 36364c09eed9SJim Baxter ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM 363779f33912SNimrod Andy | NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO); 36384c09eed9SJim Baxter fep->csum_flags |= FLAG_RX_CSUM_ENABLED; 363948496255SShawn Guo } 36404c09eed9SJim Baxter 3641471ff445SJoakim Zhang if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) { 364241ef84ceSFugang Duan fep->tx_align = 0; 364341ef84ceSFugang Duan fep->rx_align = 0x3f; 364441ef84ceSFugang Duan } 364541ef84ceSFugang Duan 364609d1e541SNimrod Andy ndev->hw_features = ndev->features; 364709d1e541SNimrod Andy 3648ef83337dSRussell King fec_restart(ndev); 3649793fc096SFrank Li 36502b30842bSAndrew Lunn if (fep->quirks & FEC_QUIRK_MIB_CLEAR) 36512b30842bSAndrew Lunn fec_enet_clear_ethtool_stats(ndev); 36522b30842bSAndrew Lunn else 365380cca775SNikita Yushchenko fec_enet_update_ethtool_stats(ndev); 365480cca775SNikita Yushchenko 3655793fc096SFrank Li return 0; 3656619fee9eSFugang Duan 3657619fee9eSFugang Duan free_queue_mem: 3658619fee9eSFugang Duan fec_enet_free_queue(ndev); 3659619fee9eSFugang Duan return ret; 3660793fc096SFrank Li } 3661793fc096SFrank Li 3662793fc096SFrank Li #ifdef CONFIG_OF 36639269e556SFugang Duan static int fec_reset_phy(struct platform_device *pdev) 3664793fc096SFrank Li { 3665793fc096SFrank Li int err, phy_reset; 3666962d8cdcSBernhard Walle bool active_high = false; 3667159a0760SQuentin Schulz int msec = 1, phy_post_delay = 0; 3668793fc096SFrank Li struct device_node *np = pdev->dev.of_node; 3669793fc096SFrank Li 3670793fc096SFrank Li if (!np) 36719269e556SFugang Duan return 0; 3672793fc096SFrank Li 367361e04ccbSFugang Duan err = of_property_read_u32(np, "phy-reset-duration", &msec); 3674793fc096SFrank Li /* A sane reset duration should not be longer than 1s */ 367561e04ccbSFugang Duan if (!err && msec > 1000) 3676793fc096SFrank Li msec = 1; 3677793fc096SFrank Li 3678793fc096SFrank Li phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0); 36799269e556SFugang Duan if (phy_reset == -EPROBE_DEFER) 36809269e556SFugang Duan return phy_reset; 36819269e556SFugang Duan else if (!gpio_is_valid(phy_reset)) 36829269e556SFugang Duan return 0; 3683793fc096SFrank Li 3684159a0760SQuentin Schulz err = of_property_read_u32(np, "phy-reset-post-delay", &phy_post_delay); 3685159a0760SQuentin Schulz /* valid reset duration should be less than 1s */ 3686159a0760SQuentin Schulz if (!err && phy_post_delay > 1000) 3687159a0760SQuentin Schulz return -EINVAL; 3688159a0760SQuentin Schulz 3689962d8cdcSBernhard Walle active_high = of_property_read_bool(np, "phy-reset-active-high"); 369064f10f6eSBernhard Walle 3691793fc096SFrank Li err = devm_gpio_request_one(&pdev->dev, phy_reset, 3692962d8cdcSBernhard Walle active_high ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, 369364f10f6eSBernhard Walle "phy-reset"); 3694793fc096SFrank Li if (err) { 3695793fc096SFrank Li dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err); 36969269e556SFugang Duan return err; 3697793fc096SFrank Li } 3698eb37c563SStefan Wahren 3699eb37c563SStefan Wahren if (msec > 20) 3700793fc096SFrank Li msleep(msec); 3701eb37c563SStefan Wahren else 3702eb37c563SStefan Wahren usleep_range(msec * 1000, msec * 1000 + 1000); 3703eb37c563SStefan Wahren 3704962d8cdcSBernhard Walle gpio_set_value_cansleep(phy_reset, !active_high); 37059269e556SFugang Duan 3706159a0760SQuentin Schulz if (!phy_post_delay) 3707159a0760SQuentin Schulz return 0; 3708159a0760SQuentin Schulz 3709159a0760SQuentin Schulz if (phy_post_delay > 20) 3710159a0760SQuentin Schulz msleep(phy_post_delay); 3711159a0760SQuentin Schulz else 3712159a0760SQuentin Schulz usleep_range(phy_post_delay * 1000, 3713159a0760SQuentin Schulz phy_post_delay * 1000 + 1000); 3714159a0760SQuentin Schulz 37159269e556SFugang Duan return 0; 3716793fc096SFrank Li } 3717793fc096SFrank Li #else /* CONFIG_OF */ 37189269e556SFugang Duan static int fec_reset_phy(struct platform_device *pdev) 3719793fc096SFrank Li { 3720793fc096SFrank Li /* 3721793fc096SFrank Li * In case of platform probe, the reset has been done 3722793fc096SFrank Li * by machine code. 3723793fc096SFrank Li */ 37249269e556SFugang Duan return 0; 3725793fc096SFrank Li } 3726793fc096SFrank Li #endif /* CONFIG_OF */ 3727793fc096SFrank Li 37289fc095f1SFugang Duan static void 37299fc095f1SFugang Duan fec_enet_get_queue_num(struct platform_device *pdev, int *num_tx, int *num_rx) 37309fc095f1SFugang Duan { 37319fc095f1SFugang Duan struct device_node *np = pdev->dev.of_node; 37329fc095f1SFugang Duan 37339fc095f1SFugang Duan *num_tx = *num_rx = 1; 37349fc095f1SFugang Duan 37359fc095f1SFugang Duan if (!np || !of_device_is_available(np)) 37369fc095f1SFugang Duan return; 37379fc095f1SFugang Duan 37389fc095f1SFugang Duan /* parse the num of tx and rx queues */ 373973b1c90dSSaurabh Sengar of_property_read_u32(np, "fsl,num-tx-queues", num_tx); 3740b7bd75cfSFrank Li 374173b1c90dSSaurabh Sengar of_property_read_u32(np, "fsl,num-rx-queues", num_rx); 37429fc095f1SFugang Duan 37439fc095f1SFugang Duan if (*num_tx < 1 || *num_tx > FEC_ENET_MAX_TX_QS) { 3744b7bd75cfSFrank Li dev_warn(&pdev->dev, "Invalid num_tx(=%d), fall back to 1\n", 37459fc095f1SFugang Duan *num_tx); 37469fc095f1SFugang Duan *num_tx = 1; 37479fc095f1SFugang Duan return; 37489fc095f1SFugang Duan } 37499fc095f1SFugang Duan 37509fc095f1SFugang Duan if (*num_rx < 1 || *num_rx > FEC_ENET_MAX_RX_QS) { 3751b7bd75cfSFrank Li dev_warn(&pdev->dev, "Invalid num_rx(=%d), fall back to 1\n", 37529fc095f1SFugang Duan *num_rx); 37539fc095f1SFugang Duan *num_rx = 1; 37549fc095f1SFugang Duan return; 37559fc095f1SFugang Duan } 37569fc095f1SFugang Duan 37579fc095f1SFugang Duan } 37589fc095f1SFugang Duan 37594ad1ceecSTroy Kisky static int fec_enet_get_irq_cnt(struct platform_device *pdev) 37604ad1ceecSTroy Kisky { 37614ad1ceecSTroy Kisky int irq_cnt = platform_irq_count(pdev); 37624ad1ceecSTroy Kisky 37634ad1ceecSTroy Kisky if (irq_cnt > FEC_IRQ_NUM) 37644ad1ceecSTroy Kisky irq_cnt = FEC_IRQ_NUM; /* last for pps */ 37654ad1ceecSTroy Kisky else if (irq_cnt == 2) 37664ad1ceecSTroy Kisky irq_cnt = 1; /* last for pps */ 37674ad1ceecSTroy Kisky else if (irq_cnt <= 0) 37684ad1ceecSTroy Kisky irq_cnt = 1; /* At least 1 irq is needed */ 37694ad1ceecSTroy Kisky return irq_cnt; 37704ad1ceecSTroy Kisky } 37714ad1ceecSTroy Kisky 3772b7cdc965SJoakim Zhang static void fec_enet_get_wakeup_irq(struct platform_device *pdev) 3773b7cdc965SJoakim Zhang { 3774b7cdc965SJoakim Zhang struct net_device *ndev = platform_get_drvdata(pdev); 3775b7cdc965SJoakim Zhang struct fec_enet_private *fep = netdev_priv(ndev); 3776b7cdc965SJoakim Zhang 3777b7cdc965SJoakim Zhang if (fep->quirks & FEC_QUIRK_WAKEUP_FROM_INT2) 3778b7cdc965SJoakim Zhang fep->wake_irq = fep->irq[2]; 3779b7cdc965SJoakim Zhang else 3780b7cdc965SJoakim Zhang fep->wake_irq = fep->irq[0]; 3781b7cdc965SJoakim Zhang } 3782b7cdc965SJoakim Zhang 3783da722186SMartin Fuzzey static int fec_enet_init_stop_mode(struct fec_enet_private *fep, 3784da722186SMartin Fuzzey struct device_node *np) 3785da722186SMartin Fuzzey { 3786da722186SMartin Fuzzey struct device_node *gpr_np; 37878a448bf8SFugang Duan u32 out_val[3]; 3788da722186SMartin Fuzzey int ret = 0; 3789da722186SMartin Fuzzey 37908a448bf8SFugang Duan gpr_np = of_parse_phandle(np, "fsl,stop-mode", 0); 3791da722186SMartin Fuzzey if (!gpr_np) 3792da722186SMartin Fuzzey return 0; 3793da722186SMartin Fuzzey 37948a448bf8SFugang Duan ret = of_property_read_u32_array(np, "fsl,stop-mode", out_val, 37958a448bf8SFugang Duan ARRAY_SIZE(out_val)); 37968a448bf8SFugang Duan if (ret) { 37978a448bf8SFugang Duan dev_dbg(&fep->pdev->dev, "no stop mode property\n"); 3798d2b52ec0SYang Yingliang goto out; 37998a448bf8SFugang Duan } 38008a448bf8SFugang Duan 3801da722186SMartin Fuzzey fep->stop_gpr.gpr = syscon_node_to_regmap(gpr_np); 3802da722186SMartin Fuzzey if (IS_ERR(fep->stop_gpr.gpr)) { 3803da722186SMartin Fuzzey dev_err(&fep->pdev->dev, "could not find gpr regmap\n"); 3804da722186SMartin Fuzzey ret = PTR_ERR(fep->stop_gpr.gpr); 3805da722186SMartin Fuzzey fep->stop_gpr.gpr = NULL; 3806da722186SMartin Fuzzey goto out; 3807da722186SMartin Fuzzey } 3808da722186SMartin Fuzzey 38098a448bf8SFugang Duan fep->stop_gpr.reg = out_val[1]; 38108a448bf8SFugang Duan fep->stop_gpr.bit = out_val[2]; 3811da722186SMartin Fuzzey 3812da722186SMartin Fuzzey out: 3813da722186SMartin Fuzzey of_node_put(gpr_np); 3814da722186SMartin Fuzzey 3815da722186SMartin Fuzzey return ret; 3816da722186SMartin Fuzzey } 3817da722186SMartin Fuzzey 3818793fc096SFrank Li static int 3819793fc096SFrank Li fec_probe(struct platform_device *pdev) 3820793fc096SFrank Li { 3821793fc096SFrank Li struct fec_enet_private *fep; 3822793fc096SFrank Li struct fec_platform_data *pdata; 38230c65b2b9SAndrew Lunn phy_interface_t interface; 3824793fc096SFrank Li struct net_device *ndev; 3825793fc096SFrank Li int i, irq, ret = 0; 3826793fc096SFrank Li const struct of_device_id *of_id; 3827793fc096SFrank Li static int dev_id; 3828407066f8SUwe Kleine-König struct device_node *np = pdev->dev.of_node, *phy_node; 3829b7bd75cfSFrank Li int num_tx_qs; 3830b7bd75cfSFrank Li int num_rx_qs; 38314ad1ceecSTroy Kisky char irq_name[8]; 38324ad1ceecSTroy Kisky int irq_cnt; 3833da722186SMartin Fuzzey struct fec_devinfo *dev_info; 3834793fc096SFrank Li 38359fc095f1SFugang Duan fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs); 38369fc095f1SFugang Duan 3837793fc096SFrank Li /* Init network device */ 383880cca775SNikita Yushchenko ndev = alloc_etherdev_mqs(sizeof(struct fec_enet_private) + 3839f85de666SNikita Yushchenko FEC_STATS_SIZE, num_tx_qs, num_rx_qs); 3840793fc096SFrank Li if (!ndev) 3841793fc096SFrank Li return -ENOMEM; 3842793fc096SFrank Li 3843793fc096SFrank Li SET_NETDEV_DEV(ndev, &pdev->dev); 3844793fc096SFrank Li 3845793fc096SFrank Li /* setup board info structure */ 3846793fc096SFrank Li fep = netdev_priv(ndev); 3847793fc096SFrank Li 38486b7e4008SLothar Waßmann of_id = of_match_device(fec_dt_ids, &pdev->dev); 38496b7e4008SLothar Waßmann if (of_id) 38506b7e4008SLothar Waßmann pdev->id_entry = of_id->data; 3851da722186SMartin Fuzzey dev_info = (struct fec_devinfo *)pdev->id_entry->driver_data; 3852da722186SMartin Fuzzey if (dev_info) 3853da722186SMartin Fuzzey fep->quirks = dev_info->quirks; 38546b7e4008SLothar Waßmann 38550c818594SHubert Feurstein fep->netdev = ndev; 38569fc095f1SFugang Duan fep->num_rx_queues = num_rx_qs; 38579fc095f1SFugang Duan fep->num_tx_queues = num_tx_qs; 38589fc095f1SFugang Duan 3859d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 3860793fc096SFrank Li /* default enable pause frame auto negotiation */ 38616b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_GBIT) 3862793fc096SFrank Li fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG; 3863d1391930SGuenter Roeck #endif 3864793fc096SFrank Li 38655bbde4d2SNimrod Andy /* Select default pin state */ 38665bbde4d2SNimrod Andy pinctrl_pm_select_default_state(&pdev->dev); 38675bbde4d2SNimrod Andy 38684f830a5aSYueHaibing fep->hwp = devm_platform_ioremap_resource(pdev, 0); 3869941e173aSTushar Behera if (IS_ERR(fep->hwp)) { 3870941e173aSTushar Behera ret = PTR_ERR(fep->hwp); 3871941e173aSTushar Behera goto failed_ioremap; 3872941e173aSTushar Behera } 3873941e173aSTushar Behera 3874793fc096SFrank Li fep->pdev = pdev; 3875793fc096SFrank Li fep->dev_id = dev_id++; 3876793fc096SFrank Li 3877793fc096SFrank Li platform_set_drvdata(pdev, ndev); 3878793fc096SFrank Li 387929380905SLucas Stach if ((of_machine_is_compatible("fsl,imx6q") || 388029380905SLucas Stach of_machine_is_compatible("fsl,imx6dl")) && 388129380905SLucas Stach !of_property_read_bool(np, "fsl,err006687-workaround-present")) 388229380905SLucas Stach fep->quirks |= FEC_QUIRK_ERR006687; 388329380905SLucas Stach 3884*40c79ce1SWei Fang ret = fec_enet_ipc_handle_init(fep); 3885*40c79ce1SWei Fang if (ret) 3886*40c79ce1SWei Fang goto failed_ipc_init; 3887*40c79ce1SWei Fang 3888de40ed31SNimrod Andy if (of_get_property(np, "fsl,magic-packet", NULL)) 3889de40ed31SNimrod Andy fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET; 3890de40ed31SNimrod Andy 38918a448bf8SFugang Duan ret = fec_enet_init_stop_mode(fep, np); 3892da722186SMartin Fuzzey if (ret) 3893da722186SMartin Fuzzey goto failed_stop_mode; 3894da722186SMartin Fuzzey 3895407066f8SUwe Kleine-König phy_node = of_parse_phandle(np, "phy-handle", 0); 3896407066f8SUwe Kleine-König if (!phy_node && of_phy_is_fixed_link(np)) { 3897407066f8SUwe Kleine-König ret = of_phy_register_fixed_link(np); 3898407066f8SUwe Kleine-König if (ret < 0) { 3899407066f8SUwe Kleine-König dev_err(&pdev->dev, 3900407066f8SUwe Kleine-König "broken fixed-link specification\n"); 3901407066f8SUwe Kleine-König goto failed_phy; 3902407066f8SUwe Kleine-König } 3903407066f8SUwe Kleine-König phy_node = of_node_get(np); 3904407066f8SUwe Kleine-König } 3905407066f8SUwe Kleine-König fep->phy_node = phy_node; 3906407066f8SUwe Kleine-König 39070c65b2b9SAndrew Lunn ret = of_get_phy_mode(pdev->dev.of_node, &interface); 39080c65b2b9SAndrew Lunn if (ret) { 390994660ba0SJingoo Han pdata = dev_get_platdata(&pdev->dev); 3910793fc096SFrank Li if (pdata) 3911793fc096SFrank Li fep->phy_interface = pdata->phy; 3912793fc096SFrank Li else 3913793fc096SFrank Li fep->phy_interface = PHY_INTERFACE_MODE_MII; 3914793fc096SFrank Li } else { 39150c65b2b9SAndrew Lunn fep->phy_interface = interface; 3916793fc096SFrank Li } 3917793fc096SFrank Li 3918b820c114SJoakim Zhang ret = fec_enet_parse_rgmii_delay(fep, np); 3919b820c114SJoakim Zhang if (ret) 3920b820c114SJoakim Zhang goto failed_rgmii_delay; 3921b820c114SJoakim Zhang 3922793fc096SFrank Li fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); 3923793fc096SFrank Li if (IS_ERR(fep->clk_ipg)) { 3924793fc096SFrank Li ret = PTR_ERR(fep->clk_ipg); 3925793fc096SFrank Li goto failed_clk; 3926793fc096SFrank Li } 3927793fc096SFrank Li 3928793fc096SFrank Li fep->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); 3929793fc096SFrank Li if (IS_ERR(fep->clk_ahb)) { 3930793fc096SFrank Li ret = PTR_ERR(fep->clk_ahb); 3931793fc096SFrank Li goto failed_clk; 3932793fc096SFrank Li } 3933793fc096SFrank Li 3934d851b47bSFugang Duan fep->itr_clk_rate = clk_get_rate(fep->clk_ahb); 3935d851b47bSFugang Duan 393638f56f33SLinus Torvalds /* enet_out is optional, depends on board */ 39375ff851b7SUwe Kleine-König fep->clk_enet_out = devm_clk_get_optional(&pdev->dev, "enet_out"); 39385ff851b7SUwe Kleine-König if (IS_ERR(fep->clk_enet_out)) { 39395ff851b7SUwe Kleine-König ret = PTR_ERR(fep->clk_enet_out); 39405ff851b7SUwe Kleine-König goto failed_clk; 39415ff851b7SUwe Kleine-König } 394238f56f33SLinus Torvalds 394391c0d987SNimrod Andy fep->ptp_clk_on = false; 394491c0d987SNimrod Andy mutex_init(&fep->ptp_clk_mutex); 39459b5330edSFugang Duan 39469b5330edSFugang Duan /* clk_ref is optional, depends on board */ 394743252ed1SUwe Kleine-König fep->clk_ref = devm_clk_get_optional(&pdev->dev, "enet_clk_ref"); 394843252ed1SUwe Kleine-König if (IS_ERR(fep->clk_ref)) { 394943252ed1SUwe Kleine-König ret = PTR_ERR(fep->clk_ref); 395043252ed1SUwe Kleine-König goto failed_clk; 395143252ed1SUwe Kleine-König } 3952b82f8c3fSFugang Duan fep->clk_ref_rate = clk_get_rate(fep->clk_ref); 39539b5330edSFugang Duan 3954fc539459SFugang Duan /* clk_2x_txclk is optional, depends on board */ 3955b820c114SJoakim Zhang if (fep->rgmii_txc_dly || fep->rgmii_rxc_dly) { 3956fc539459SFugang Duan fep->clk_2x_txclk = devm_clk_get(&pdev->dev, "enet_2x_txclk"); 3957fc539459SFugang Duan if (IS_ERR(fep->clk_2x_txclk)) 3958fc539459SFugang Duan fep->clk_2x_txclk = NULL; 3959b820c114SJoakim Zhang } 3960fc539459SFugang Duan 39616b7e4008SLothar Waßmann fep->bufdesc_ex = fep->quirks & FEC_QUIRK_HAS_BUFDESC_EX; 3962793fc096SFrank Li fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp"); 3963793fc096SFrank Li if (IS_ERR(fep->clk_ptp)) { 396438f56f33SLinus Torvalds fep->clk_ptp = NULL; 3965217b5844SLothar Waßmann fep->bufdesc_ex = false; 3966793fc096SFrank Li } 3967793fc096SFrank Li 3968e8fcfcd5SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 396913a097bdSFabio Estevam if (ret) 397013a097bdSFabio Estevam goto failed_clk; 397113a097bdSFabio Estevam 39728fff755eSAndrew Lunn ret = clk_prepare_enable(fep->clk_ipg); 39738fff755eSAndrew Lunn if (ret) 39748fff755eSAndrew Lunn goto failed_clk_ipg; 3975d7c3a206SAndy Duan ret = clk_prepare_enable(fep->clk_ahb); 3976d7c3a206SAndy Duan if (ret) 3977d7c3a206SAndy Duan goto failed_clk_ahb; 39788fff755eSAndrew Lunn 397925974d8aSStefan Agner fep->reg_phy = devm_regulator_get_optional(&pdev->dev, "phy"); 3980f4e9f3d2SFabio Estevam if (!IS_ERR(fep->reg_phy)) { 3981f4e9f3d2SFabio Estevam ret = regulator_enable(fep->reg_phy); 3982793fc096SFrank Li if (ret) { 3983793fc096SFrank Li dev_err(&pdev->dev, 3984793fc096SFrank Li "Failed to enable phy regulator: %d\n", ret); 3985793fc096SFrank Li goto failed_regulator; 3986793fc096SFrank Li } 3987f6a4d607SFabio Estevam } else { 39883f38c683SFugang Duan if (PTR_ERR(fep->reg_phy) == -EPROBE_DEFER) { 39893f38c683SFugang Duan ret = -EPROBE_DEFER; 39903f38c683SFugang Duan goto failed_regulator; 39913f38c683SFugang Duan } 3992f6a4d607SFabio Estevam fep->reg_phy = NULL; 3993793fc096SFrank Li } 3994793fc096SFrank Li 39958fff755eSAndrew Lunn pm_runtime_set_autosuspend_delay(&pdev->dev, FEC_MDIO_PM_TIMEOUT); 39968fff755eSAndrew Lunn pm_runtime_use_autosuspend(&pdev->dev); 399714d2b7c1SLucas Stach pm_runtime_get_noresume(&pdev->dev); 39988fff755eSAndrew Lunn pm_runtime_set_active(&pdev->dev); 39998fff755eSAndrew Lunn pm_runtime_enable(&pdev->dev); 40008fff755eSAndrew Lunn 40019269e556SFugang Duan ret = fec_reset_phy(pdev); 40029269e556SFugang Duan if (ret) 40039269e556SFugang Duan goto failed_reset; 4004793fc096SFrank Li 40054ad1ceecSTroy Kisky irq_cnt = fec_enet_get_irq_cnt(pdev); 4006793fc096SFrank Li if (fep->bufdesc_ex) 40074ad1ceecSTroy Kisky fec_ptp_init(pdev, irq_cnt); 4008793fc096SFrank Li 4009793fc096SFrank Li ret = fec_enet_init(ndev); 4010793fc096SFrank Li if (ret) 4011793fc096SFrank Li goto failed_init; 4012793fc096SFrank Li 40134ad1ceecSTroy Kisky for (i = 0; i < irq_cnt; i++) { 40143ded9f2bSArnd Bergmann snprintf(irq_name, sizeof(irq_name), "int%d", i); 40153b56be21SAnson Huang irq = platform_get_irq_byname_optional(pdev, irq_name); 40164ad1ceecSTroy Kisky if (irq < 0) 4017793fc096SFrank Li irq = platform_get_irq(pdev, i); 4018793fc096SFrank Li if (irq < 0) { 4019793fc096SFrank Li ret = irq; 4020793fc096SFrank Li goto failed_irq; 4021793fc096SFrank Li } 40220d9b2ab1SFabio Estevam ret = devm_request_irq(&pdev->dev, irq, fec_enet_interrupt, 402344a272ddSMichael Opdenacker 0, pdev->name, ndev); 40240d9b2ab1SFabio Estevam if (ret) 4025793fc096SFrank Li goto failed_irq; 4026de40ed31SNimrod Andy 4027de40ed31SNimrod Andy fep->irq[i] = irq; 4028793fc096SFrank Li } 4029793fc096SFrank Li 4030b7cdc965SJoakim Zhang /* Decide which interrupt line is wakeup capable */ 4031b7cdc965SJoakim Zhang fec_enet_get_wakeup_irq(pdev); 4032b7cdc965SJoakim Zhang 4033793fc096SFrank Li ret = fec_enet_mii_init(pdev); 4034793fc096SFrank Li if (ret) 4035793fc096SFrank Li goto failed_mii_init; 4036793fc096SFrank Li 4037793fc096SFrank Li /* Carrier starts down, phylib will bring it up */ 4038793fc096SFrank Li netif_carrier_off(ndev); 4039e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 40405bbde4d2SNimrod Andy pinctrl_pm_select_sleep_state(&pdev->dev); 4041793fc096SFrank Li 404259193053SAndrew Lunn ndev->max_mtu = PKT_MAXBUF_SIZE - ETH_HLEN - ETH_FCS_LEN; 404359193053SAndrew Lunn 4044793fc096SFrank Li ret = register_netdev(ndev); 4045793fc096SFrank Li if (ret) 4046793fc096SFrank Li goto failed_register; 4047793fc096SFrank Li 4048de40ed31SNimrod Andy device_init_wakeup(&ndev->dev, fep->wol_flag & 4049de40ed31SNimrod Andy FEC_WOL_HAS_MAGIC_PACKET); 4050de40ed31SNimrod Andy 4051eb1d0640SFabio Estevam if (fep->bufdesc_ex && fep->ptp_clock) 4052eb1d0640SFabio Estevam netdev_info(ndev, "registered PHC device %d\n", fep->dev_id); 4053eb1d0640SFabio Estevam 40541b7bde6dSNimrod Andy fep->rx_copybreak = COPYBREAK_DEFAULT; 405536cdc743SRussell King INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work); 40568fff755eSAndrew Lunn 40578fff755eSAndrew Lunn pm_runtime_mark_last_busy(&pdev->dev); 40588fff755eSAndrew Lunn pm_runtime_put_autosuspend(&pdev->dev); 40598fff755eSAndrew Lunn 4060793fc096SFrank Li return 0; 4061793fc096SFrank Li 4062793fc096SFrank Li failed_register: 4063793fc096SFrank Li fec_enet_mii_remove(fep); 4064793fc096SFrank Li failed_mii_init: 40657a2bbd8dSFabio Estevam failed_irq: 40667a2bbd8dSFabio Estevam failed_init: 406732cba57bSLucas Stach fec_ptp_stop(pdev); 40689269e556SFugang Duan failed_reset: 4069ce8d24f9SAndy Duan pm_runtime_put_noidle(&pdev->dev); 40709269e556SFugang Duan pm_runtime_disable(&pdev->dev); 4071c6165cf0SFugang Duan if (fep->reg_phy) 4072c6165cf0SFugang Duan regulator_disable(fep->reg_phy); 4073793fc096SFrank Li failed_regulator: 4074d7c3a206SAndy Duan clk_disable_unprepare(fep->clk_ahb); 4075d7c3a206SAndy Duan failed_clk_ahb: 4076d7c3a206SAndy Duan clk_disable_unprepare(fep->clk_ipg); 40778fff755eSAndrew Lunn failed_clk_ipg: 4078e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 4079793fc096SFrank Li failed_clk: 4080b820c114SJoakim Zhang failed_rgmii_delay: 408182005b1cSJohan Hovold if (of_phy_is_fixed_link(np)) 408282005b1cSJohan Hovold of_phy_deregister_fixed_link(np); 4083407066f8SUwe Kleine-König of_node_put(phy_node); 4084da722186SMartin Fuzzey failed_stop_mode: 4085*40c79ce1SWei Fang failed_ipc_init: 4086d1616f07SFugang Duan failed_phy: 4087d1616f07SFugang Duan dev_id--; 4088793fc096SFrank Li failed_ioremap: 4089793fc096SFrank Li free_netdev(ndev); 4090793fc096SFrank Li 4091793fc096SFrank Li return ret; 4092793fc096SFrank Li } 4093793fc096SFrank Li 4094793fc096SFrank Li static int 4095793fc096SFrank Li fec_drv_remove(struct platform_device *pdev) 4096793fc096SFrank Li { 4097793fc096SFrank Li struct net_device *ndev = platform_get_drvdata(pdev); 4098793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 409982005b1cSJohan Hovold struct device_node *np = pdev->dev.of_node; 4100a31eda65SChuhong Yuan int ret; 4101a31eda65SChuhong Yuan 4102da875fa5SZhang Qilong ret = pm_runtime_resume_and_get(&pdev->dev); 4103a31eda65SChuhong Yuan if (ret < 0) 4104a31eda65SChuhong Yuan return ret; 4105793fc096SFrank Li 410636cdc743SRussell King cancel_work_sync(&fep->tx_timeout_work); 410732cba57bSLucas Stach fec_ptp_stop(pdev); 4108793fc096SFrank Li unregister_netdev(ndev); 4109793fc096SFrank Li fec_enet_mii_remove(fep); 4110f6a4d607SFabio Estevam if (fep->reg_phy) 4111f6a4d607SFabio Estevam regulator_disable(fep->reg_phy); 4112a31eda65SChuhong Yuan 411382005b1cSJohan Hovold if (of_phy_is_fixed_link(np)) 411482005b1cSJohan Hovold of_phy_deregister_fixed_link(np); 4115407066f8SUwe Kleine-König of_node_put(fep->phy_node); 4116793fc096SFrank Li 4117a31eda65SChuhong Yuan clk_disable_unprepare(fep->clk_ahb); 4118a31eda65SChuhong Yuan clk_disable_unprepare(fep->clk_ipg); 4119a31eda65SChuhong Yuan pm_runtime_put_noidle(&pdev->dev); 4120a31eda65SChuhong Yuan pm_runtime_disable(&pdev->dev); 4121a31eda65SChuhong Yuan 412244712965SPavel Skripkin free_netdev(ndev); 4123793fc096SFrank Li return 0; 4124793fc096SFrank Li } 4125793fc096SFrank Li 4126dd66d386SFabio Estevam static int __maybe_unused fec_suspend(struct device *dev) 4127793fc096SFrank Li { 4128793fc096SFrank Li struct net_device *ndev = dev_get_drvdata(dev); 4129793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 4130793fc096SFrank Li 4131da1774e5SRussell King rtnl_lock(); 4132793fc096SFrank Li if (netif_running(ndev)) { 4133de40ed31SNimrod Andy if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) 4134de40ed31SNimrod Andy fep->wol_flag |= FEC_WOL_FLAG_SLEEP_ON; 413545f5c327SPhilippe Reynes phy_stop(ndev->phydev); 413631a6de34SRussell King napi_disable(&fep->napi); 413731a6de34SRussell King netif_tx_lock_bh(ndev); 4138793fc096SFrank Li netif_device_detach(ndev); 413931a6de34SRussell King netif_tx_unlock_bh(ndev); 414031a6de34SRussell King fec_stop(ndev); 41410b6f65c7SJoakim Zhang if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) { 41420b6f65c7SJoakim Zhang fec_irqs_disable(ndev); 41435bbde4d2SNimrod Andy pinctrl_pm_select_sleep_state(&fep->pdev->dev); 41440b6f65c7SJoakim Zhang } else { 41450b6f65c7SJoakim Zhang fec_irqs_disable_except_wakeup(ndev); 41460b6f65c7SJoakim Zhang if (fep->wake_irq > 0) { 41470b6f65c7SJoakim Zhang disable_irq(fep->wake_irq); 41480b6f65c7SJoakim Zhang enable_irq_wake(fep->wake_irq); 41490b6f65c7SJoakim Zhang } 41500b6f65c7SJoakim Zhang fec_enet_stop_mode(fep, true); 41510b6f65c7SJoakim Zhang } 41520b6f65c7SJoakim Zhang /* It's safe to disable clocks since interrupts are masked */ 41530b6f65c7SJoakim Zhang fec_enet_clk_enable(ndev, false); 4154f4c4a4e0SNimrod Andy } 4155f4c4a4e0SNimrod Andy rtnl_unlock(); 4156793fc096SFrank Li 4157de40ed31SNimrod Andy if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) 4158238f7bc7SFabio Estevam regulator_disable(fep->reg_phy); 4159238f7bc7SFabio Estevam 4160858eeb7dSNimrod Andy /* SOC supply clock to phy, when clock is disabled, phy link down 4161858eeb7dSNimrod Andy * SOC control phy regulator, when regulator is disabled, phy link down 4162858eeb7dSNimrod Andy */ 4163858eeb7dSNimrod Andy if (fep->clk_enet_out || fep->reg_phy) 4164858eeb7dSNimrod Andy fep->link = 0; 4165858eeb7dSNimrod Andy 4166793fc096SFrank Li return 0; 4167793fc096SFrank Li } 4168793fc096SFrank Li 4169dd66d386SFabio Estevam static int __maybe_unused fec_resume(struct device *dev) 4170793fc096SFrank Li { 4171793fc096SFrank Li struct net_device *ndev = dev_get_drvdata(dev); 4172793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 4173238f7bc7SFabio Estevam int ret; 4174de40ed31SNimrod Andy int val; 4175238f7bc7SFabio Estevam 4176de40ed31SNimrod Andy if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) { 4177238f7bc7SFabio Estevam ret = regulator_enable(fep->reg_phy); 4178238f7bc7SFabio Estevam if (ret) 4179238f7bc7SFabio Estevam return ret; 4180238f7bc7SFabio Estevam } 4181793fc096SFrank Li 4182da1774e5SRussell King rtnl_lock(); 4183793fc096SFrank Li if (netif_running(ndev)) { 4184f4c4a4e0SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 4185f4c4a4e0SNimrod Andy if (ret) { 4186f4c4a4e0SNimrod Andy rtnl_unlock(); 4187f4c4a4e0SNimrod Andy goto failed_clk; 4188f4c4a4e0SNimrod Andy } 4189de40ed31SNimrod Andy if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) { 4190da722186SMartin Fuzzey fec_enet_stop_mode(fep, false); 41910b6f65c7SJoakim Zhang if (fep->wake_irq) { 41920b6f65c7SJoakim Zhang disable_irq_wake(fep->wake_irq); 41930b6f65c7SJoakim Zhang enable_irq(fep->wake_irq); 41940b6f65c7SJoakim Zhang } 4195da722186SMartin Fuzzey 4196de40ed31SNimrod Andy val = readl(fep->hwp + FEC_ECNTRL); 4197de40ed31SNimrod Andy val &= ~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP); 4198de40ed31SNimrod Andy writel(val, fep->hwp + FEC_ECNTRL); 4199de40ed31SNimrod Andy fep->wol_flag &= ~FEC_WOL_FLAG_SLEEP_ON; 4200de40ed31SNimrod Andy } else { 4201de40ed31SNimrod Andy pinctrl_pm_select_default_state(&fep->pdev->dev); 4202de40ed31SNimrod Andy } 4203ef83337dSRussell King fec_restart(ndev); 420431a6de34SRussell King netif_tx_lock_bh(ndev); 4205793fc096SFrank Li netif_device_attach(ndev); 42066af42d42SRussell King netif_tx_unlock_bh(ndev); 42076af42d42SRussell King napi_enable(&fep->napi); 4208557d5dc8SHeiner Kallweit phy_init_hw(ndev->phydev); 420945f5c327SPhilippe Reynes phy_start(ndev->phydev); 4210793fc096SFrank Li } 4211da1774e5SRussell King rtnl_unlock(); 4212793fc096SFrank Li 4213793fc096SFrank Li return 0; 421413a097bdSFabio Estevam 4215e8fcfcd5SNimrod Andy failed_clk: 421613a097bdSFabio Estevam if (fep->reg_phy) 421713a097bdSFabio Estevam regulator_disable(fep->reg_phy); 421813a097bdSFabio Estevam return ret; 4219793fc096SFrank Li } 4220793fc096SFrank Li 42218fff755eSAndrew Lunn static int __maybe_unused fec_runtime_suspend(struct device *dev) 42228fff755eSAndrew Lunn { 42238fff755eSAndrew Lunn struct net_device *ndev = dev_get_drvdata(dev); 42248fff755eSAndrew Lunn struct fec_enet_private *fep = netdev_priv(ndev); 42258fff755eSAndrew Lunn 4226d7c3a206SAndy Duan clk_disable_unprepare(fep->clk_ahb); 42278fff755eSAndrew Lunn clk_disable_unprepare(fep->clk_ipg); 42288fff755eSAndrew Lunn 42298fff755eSAndrew Lunn return 0; 42308fff755eSAndrew Lunn } 42318fff755eSAndrew Lunn 42328fff755eSAndrew Lunn static int __maybe_unused fec_runtime_resume(struct device *dev) 42338fff755eSAndrew Lunn { 42348fff755eSAndrew Lunn struct net_device *ndev = dev_get_drvdata(dev); 42358fff755eSAndrew Lunn struct fec_enet_private *fep = netdev_priv(ndev); 4236d7c3a206SAndy Duan int ret; 42378fff755eSAndrew Lunn 4238d7c3a206SAndy Duan ret = clk_prepare_enable(fep->clk_ahb); 4239d7c3a206SAndy Duan if (ret) 4240d7c3a206SAndy Duan return ret; 4241d7c3a206SAndy Duan ret = clk_prepare_enable(fep->clk_ipg); 4242d7c3a206SAndy Duan if (ret) 4243d7c3a206SAndy Duan goto failed_clk_ipg; 4244d7c3a206SAndy Duan 4245d7c3a206SAndy Duan return 0; 4246d7c3a206SAndy Duan 4247d7c3a206SAndy Duan failed_clk_ipg: 4248d7c3a206SAndy Duan clk_disable_unprepare(fep->clk_ahb); 4249d7c3a206SAndy Duan return ret; 42508fff755eSAndrew Lunn } 42518fff755eSAndrew Lunn 42528fff755eSAndrew Lunn static const struct dev_pm_ops fec_pm_ops = { 42538fff755eSAndrew Lunn SET_SYSTEM_SLEEP_PM_OPS(fec_suspend, fec_resume) 42548fff755eSAndrew Lunn SET_RUNTIME_PM_OPS(fec_runtime_suspend, fec_runtime_resume, NULL) 42558fff755eSAndrew Lunn }; 4256793fc096SFrank Li 4257793fc096SFrank Li static struct platform_driver fec_driver = { 4258793fc096SFrank Li .driver = { 4259793fc096SFrank Li .name = DRIVER_NAME, 4260793fc096SFrank Li .pm = &fec_pm_ops, 4261793fc096SFrank Li .of_match_table = fec_dt_ids, 4262272bb0e9SFabio Estevam .suppress_bind_attrs = true, 4263793fc096SFrank Li }, 4264793fc096SFrank Li .id_table = fec_devtype, 4265793fc096SFrank Li .probe = fec_probe, 4266793fc096SFrank Li .remove = fec_drv_remove, 4267793fc096SFrank Li }; 4268793fc096SFrank Li 4269793fc096SFrank Li module_platform_driver(fec_driver); 4270793fc096SFrank Li 4271793fc096SFrank Li MODULE_LICENSE("GPL"); 4272