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 79*52c4a1a8SFugang Duan static const u16 fec_enet_vlan_pri_to_queue[8] = {0, 0, 1, 1, 1, 2, 2, 2}; 80*52c4a1a8SFugang 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 138793fc096SFrank Li static struct platform_device_id fec_devtype[] = { 139793fc096SFrank Li { 140793fc096SFrank Li /* keep it for coldfire */ 141793fc096SFrank Li .name = DRIVER_NAME, 142793fc096SFrank Li .driver_data = 0, 143793fc096SFrank Li }, { 144793fc096SFrank Li .name = "imx25-fec", 145da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_imx25_info, 146793fc096SFrank Li }, { 147793fc096SFrank Li .name = "imx27-fec", 148da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_imx27_info, 149793fc096SFrank Li }, { 150793fc096SFrank Li .name = "imx28-fec", 151da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_imx28_info, 152793fc096SFrank Li }, { 153793fc096SFrank Li .name = "imx6q-fec", 154da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_imx6q_info, 155793fc096SFrank Li }, { 15636803542SShawn Guo .name = "mvf600-fec", 157da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_mvf600_info, 158ca7c4a45SJingchang Lu }, { 15995a77470SFugang Duan .name = "imx6sx-fec", 160da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_imx6x_info, 16195a77470SFugang Duan }, { 162a51d3ab5SFugang Duan .name = "imx6ul-fec", 163da722186SMartin Fuzzey .driver_data = (kernel_ulong_t)&fec_imx6ul_info, 164a51d3ab5SFugang Duan }, { 165793fc096SFrank Li /* sentinel */ 166793fc096SFrank Li } 167793fc096SFrank Li }; 168793fc096SFrank Li MODULE_DEVICE_TABLE(platform, fec_devtype); 169793fc096SFrank Li 170793fc096SFrank Li enum imx_fec_type { 171793fc096SFrank Li IMX25_FEC = 1, /* runs on i.mx25/50/53 */ 172793fc096SFrank Li IMX27_FEC, /* runs on i.mx27/35/51 */ 173793fc096SFrank Li IMX28_FEC, 174793fc096SFrank Li IMX6Q_FEC, 17536803542SShawn Guo MVF600_FEC, 176ba593e00SFugang Duan IMX6SX_FEC, 177a51d3ab5SFugang Duan IMX6UL_FEC, 178793fc096SFrank Li }; 179793fc096SFrank Li 180793fc096SFrank Li static const struct of_device_id fec_dt_ids[] = { 181793fc096SFrank Li { .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], }, 182793fc096SFrank Li { .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], }, 183793fc096SFrank Li { .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], }, 184793fc096SFrank Li { .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], }, 18536803542SShawn Guo { .compatible = "fsl,mvf600-fec", .data = &fec_devtype[MVF600_FEC], }, 186ba593e00SFugang Duan { .compatible = "fsl,imx6sx-fec", .data = &fec_devtype[IMX6SX_FEC], }, 187a51d3ab5SFugang Duan { .compatible = "fsl,imx6ul-fec", .data = &fec_devtype[IMX6UL_FEC], }, 188793fc096SFrank Li { /* sentinel */ } 189793fc096SFrank Li }; 190793fc096SFrank Li MODULE_DEVICE_TABLE(of, fec_dt_ids); 191793fc096SFrank Li 192793fc096SFrank Li static unsigned char macaddr[ETH_ALEN]; 193793fc096SFrank Li module_param_array(macaddr, byte, NULL, 0); 194793fc096SFrank Li MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); 195793fc096SFrank Li 196793fc096SFrank Li #if defined(CONFIG_M5272) 197793fc096SFrank Li /* 198793fc096SFrank Li * Some hardware gets it MAC address out of local flash memory. 199793fc096SFrank Li * if this is non-zero then assume it is the address to get MAC from. 200793fc096SFrank Li */ 201793fc096SFrank Li #if defined(CONFIG_NETtel) 202793fc096SFrank Li #define FEC_FLASHMAC 0xf0006006 203793fc096SFrank Li #elif defined(CONFIG_GILBARCONAP) || defined(CONFIG_SCALES) 204793fc096SFrank Li #define FEC_FLASHMAC 0xf0006000 205793fc096SFrank Li #elif defined(CONFIG_CANCam) 206793fc096SFrank Li #define FEC_FLASHMAC 0xf0020000 207793fc096SFrank Li #elif defined (CONFIG_M5272C3) 208793fc096SFrank Li #define FEC_FLASHMAC (0xffe04000 + 4) 209793fc096SFrank Li #elif defined(CONFIG_MOD5272) 210793fc096SFrank Li #define FEC_FLASHMAC 0xffc0406b 211793fc096SFrank Li #else 212793fc096SFrank Li #define FEC_FLASHMAC 0 213793fc096SFrank Li #endif 214793fc096SFrank Li #endif /* CONFIG_M5272 */ 215793fc096SFrank Li 216cdffcf1bSJim Baxter /* The FEC stores dest/src/type/vlan, data, and checksum for receive packets. 217fbbeefddSAndrew Lunn * 218fbbeefddSAndrew Lunn * 2048 byte skbufs are allocated. However, alignment requirements 219fbbeefddSAndrew Lunn * varies between FEC variants. Worst case is 64, so round down by 64. 220793fc096SFrank Li */ 221fbbeefddSAndrew Lunn #define PKT_MAXBUF_SIZE (round_down(2048 - 64, 64)) 222793fc096SFrank Li #define PKT_MINBUF_SIZE 64 223793fc096SFrank Li 2244c09eed9SJim Baxter /* FEC receive acceleration */ 2254c09eed9SJim Baxter #define FEC_RACC_IPDIS (1 << 1) 2264c09eed9SJim Baxter #define FEC_RACC_PRODIS (1 << 2) 2273ac72b7bSEric Nelson #define FEC_RACC_SHIFT16 BIT(7) 2284c09eed9SJim Baxter #define FEC_RACC_OPTIONS (FEC_RACC_IPDIS | FEC_RACC_PRODIS) 2294c09eed9SJim Baxter 2302b30842bSAndrew Lunn /* MIB Control Register */ 2312b30842bSAndrew Lunn #define FEC_MIB_CTRLSTAT_DISABLE BIT(31) 2322b30842bSAndrew Lunn 233793fc096SFrank Li /* 234793fc096SFrank Li * The 5270/5271/5280/5282/532x RX control register also contains maximum frame 235793fc096SFrank Li * size bits. Other FEC hardware does not, so we need to take that into 236793fc096SFrank Li * account when setting it. 237793fc096SFrank Li */ 238793fc096SFrank Li #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ 2393f1dcc6aSLucas Stach defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ 2403f1dcc6aSLucas Stach defined(CONFIG_ARM64) 241793fc096SFrank Li #define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16) 242793fc096SFrank Li #else 243793fc096SFrank Li #define OPT_FRAME_SIZE 0 244793fc096SFrank Li #endif 245793fc096SFrank Li 246793fc096SFrank Li /* FEC MII MMFR bits definition */ 247793fc096SFrank Li #define FEC_MMFR_ST (1 << 30) 248d3ee8ec7SMarco Hartmann #define FEC_MMFR_ST_C45 (0) 249793fc096SFrank Li #define FEC_MMFR_OP_READ (2 << 28) 250d3ee8ec7SMarco Hartmann #define FEC_MMFR_OP_READ_C45 (3 << 28) 251793fc096SFrank Li #define FEC_MMFR_OP_WRITE (1 << 28) 252d3ee8ec7SMarco Hartmann #define FEC_MMFR_OP_ADDR_WRITE (0) 253793fc096SFrank Li #define FEC_MMFR_PA(v) ((v & 0x1f) << 23) 254793fc096SFrank Li #define FEC_MMFR_RA(v) ((v & 0x1f) << 18) 255793fc096SFrank Li #define FEC_MMFR_TA (2 << 16) 256793fc096SFrank Li #define FEC_MMFR_DATA(v) (v & 0xffff) 257de40ed31SNimrod Andy /* FEC ECR bits definition */ 258de40ed31SNimrod Andy #define FEC_ECR_MAGICEN (1 << 2) 259de40ed31SNimrod Andy #define FEC_ECR_SLEEP (1 << 3) 260793fc096SFrank Li 261793fc096SFrank Li #define FEC_MII_TIMEOUT 30000 /* us */ 262793fc096SFrank Li 263793fc096SFrank Li /* Transmitter timeout */ 264793fc096SFrank Li #define TX_TIMEOUT (2 * HZ) 265793fc096SFrank Li 266793fc096SFrank Li #define FEC_PAUSE_FLAG_AUTONEG 0x1 267793fc096SFrank Li #define FEC_PAUSE_FLAG_ENABLE 0x2 268de40ed31SNimrod Andy #define FEC_WOL_HAS_MAGIC_PACKET (0x1 << 0) 269de40ed31SNimrod Andy #define FEC_WOL_FLAG_ENABLE (0x1 << 1) 270de40ed31SNimrod Andy #define FEC_WOL_FLAG_SLEEP_ON (0x1 << 2) 271793fc096SFrank Li 2721b7bde6dSNimrod Andy #define COPYBREAK_DEFAULT 256 2731b7bde6dSNimrod Andy 27479f33912SNimrod Andy /* Max number of allowed TCP segments for software TSO */ 27579f33912SNimrod Andy #define FEC_MAX_TSO_SEGS 100 27679f33912SNimrod Andy #define FEC_MAX_SKB_DESCS (FEC_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS) 27779f33912SNimrod Andy 27879f33912SNimrod Andy #define IS_TSO_HEADER(txq, addr) \ 27979f33912SNimrod Andy ((addr >= txq->tso_hdrs_dma) && \ 2807355f276STroy Kisky (addr < txq->tso_hdrs_dma + txq->bd.ring_size * TSO_HEADER_SIZE)) 28179f33912SNimrod Andy 282793fc096SFrank Li static int mii_cnt; 283793fc096SFrank Li 2847355f276STroy Kisky static struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, 2857355f276STroy Kisky struct bufdesc_prop *bd) 286793fc096SFrank Li { 2877355f276STroy Kisky return (bdp >= bd->last) ? bd->base 288145d6e29SFugang Duan : (struct bufdesc *)(((void *)bdp) + bd->dsize); 289793fc096SFrank Li } 290793fc096SFrank Li 2917355f276STroy Kisky static struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, 2927355f276STroy Kisky struct bufdesc_prop *bd) 29336e24e2eSDuan Fugang-B38611 { 2947355f276STroy Kisky return (bdp <= bd->base) ? bd->last 295145d6e29SFugang Duan : (struct bufdesc *)(((void *)bdp) - bd->dsize); 29636e24e2eSDuan Fugang-B38611 } 29736e24e2eSDuan Fugang-B38611 2987355f276STroy Kisky static int fec_enet_get_bd_index(struct bufdesc *bdp, 2997355f276STroy Kisky struct bufdesc_prop *bd) 30061a4427bSNimrod Andy { 3017355f276STroy Kisky return ((const char *)bdp - (const char *)bd->base) >> bd->dsize_log2; 30261a4427bSNimrod Andy } 30361a4427bSNimrod Andy 3047355f276STroy Kisky static int fec_enet_get_free_txdesc_num(struct fec_enet_priv_tx_q *txq) 3056e909283SNimrod Andy { 3066e909283SNimrod Andy int entries; 3076e909283SNimrod Andy 3087355f276STroy Kisky entries = (((const char *)txq->dirty_tx - 3097355f276STroy Kisky (const char *)txq->bd.cur) >> txq->bd.dsize_log2) - 1; 3106e909283SNimrod Andy 3117355f276STroy Kisky return entries >= 0 ? entries : entries + txq->bd.ring_size; 3126e909283SNimrod Andy } 3136e909283SNimrod Andy 314c20e599bSLothar Waßmann static void swap_buffer(void *bufaddr, int len) 315793fc096SFrank Li { 316793fc096SFrank Li int i; 317793fc096SFrank Li unsigned int *buf = bufaddr; 318793fc096SFrank Li 3197b487d07SLothar Waßmann for (i = 0; i < len; i += 4, buf++) 320e453789aSLothar Waßmann swab32s(buf); 321793fc096SFrank Li } 322793fc096SFrank Li 3231310b544SLothar Waßmann static void swap_buffer2(void *dst_buf, void *src_buf, int len) 3241310b544SLothar Waßmann { 3251310b544SLothar Waßmann int i; 3261310b544SLothar Waßmann unsigned int *src = src_buf; 3271310b544SLothar Waßmann unsigned int *dst = dst_buf; 3281310b544SLothar Waßmann 3291310b544SLothar Waßmann for (i = 0; i < len; i += 4, src++, dst++) 3301310b544SLothar Waßmann *dst = swab32p(src); 3311310b544SLothar Waßmann } 3321310b544SLothar Waßmann 333344756f6SRussell King static void fec_dump(struct net_device *ndev) 334344756f6SRussell King { 335344756f6SRussell King struct fec_enet_private *fep = netdev_priv(ndev); 3364d494cdcSFugang Duan struct bufdesc *bdp; 3374d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 3384d494cdcSFugang Duan int index = 0; 339344756f6SRussell King 340344756f6SRussell King netdev_info(ndev, "TX ring dump\n"); 341344756f6SRussell King pr_info("Nr SC addr len SKB\n"); 342344756f6SRussell King 3434d494cdcSFugang Duan txq = fep->tx_queue[0]; 3447355f276STroy Kisky bdp = txq->bd.base; 3454d494cdcSFugang Duan 346344756f6SRussell King do { 3475cfa3039SJohannes Berg pr_info("%3u %c%c 0x%04x 0x%08x %4u %p\n", 348344756f6SRussell King index, 3497355f276STroy Kisky bdp == txq->bd.cur ? 'S' : ' ', 3504d494cdcSFugang Duan bdp == txq->dirty_tx ? 'H' : ' ', 3515cfa3039SJohannes Berg fec16_to_cpu(bdp->cbd_sc), 3525cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 3535cfa3039SJohannes Berg fec16_to_cpu(bdp->cbd_datlen), 3544d494cdcSFugang Duan txq->tx_skbuff[index]); 3557355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 356344756f6SRussell King index++; 3577355f276STroy Kisky } while (bdp != txq->bd.base); 358344756f6SRussell King } 359344756f6SRussell King 36062a02c98SFugang Duan static inline bool is_ipv4_pkt(struct sk_buff *skb) 36162a02c98SFugang Duan { 36262a02c98SFugang Duan return skb->protocol == htons(ETH_P_IP) && ip_hdr(skb)->version == 4; 36362a02c98SFugang Duan } 36462a02c98SFugang Duan 3654c09eed9SJim Baxter static int 3664c09eed9SJim Baxter fec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev) 3674c09eed9SJim Baxter { 3684c09eed9SJim Baxter /* Only run for packets requiring a checksum. */ 3694c09eed9SJim Baxter if (skb->ip_summed != CHECKSUM_PARTIAL) 3704c09eed9SJim Baxter return 0; 3714c09eed9SJim Baxter 3724c09eed9SJim Baxter if (unlikely(skb_cow_head(skb, 0))) 3734c09eed9SJim Baxter return -1; 3744c09eed9SJim Baxter 37562a02c98SFugang Duan if (is_ipv4_pkt(skb)) 37696c50caaSNimrod Andy ip_hdr(skb)->check = 0; 3774c09eed9SJim Baxter *(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) = 0; 3784c09eed9SJim Baxter 3794c09eed9SJim Baxter return 0; 3804c09eed9SJim Baxter } 3814c09eed9SJim Baxter 382c4bc44c6SKevin Hao static struct bufdesc * 3834d494cdcSFugang Duan fec_enet_txq_submit_frag_skb(struct fec_enet_priv_tx_q *txq, 3844d494cdcSFugang Duan struct sk_buff *skb, 3854d494cdcSFugang Duan struct net_device *ndev) 3866e909283SNimrod Andy { 3876e909283SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 3887355f276STroy Kisky struct bufdesc *bdp = txq->bd.cur; 3896e909283SNimrod Andy struct bufdesc_ex *ebdp; 3906e909283SNimrod Andy int nr_frags = skb_shinfo(skb)->nr_frags; 3916e909283SNimrod Andy int frag, frag_len; 3926e909283SNimrod Andy unsigned short status; 3936e909283SNimrod Andy unsigned int estatus = 0; 3946e909283SNimrod Andy skb_frag_t *this_frag; 3956e909283SNimrod Andy unsigned int index; 3966e909283SNimrod Andy void *bufaddr; 397d6bf3143SRussell King dma_addr_t addr; 3986e909283SNimrod Andy int i; 3996e909283SNimrod Andy 4006e909283SNimrod Andy for (frag = 0; frag < nr_frags; frag++) { 4016e909283SNimrod Andy this_frag = &skb_shinfo(skb)->frags[frag]; 4027355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 4036e909283SNimrod Andy ebdp = (struct bufdesc_ex *)bdp; 4046e909283SNimrod Andy 4055cfa3039SJohannes Berg status = fec16_to_cpu(bdp->cbd_sc); 4066e909283SNimrod Andy status &= ~BD_ENET_TX_STATS; 4076e909283SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 408d7840976SMatthew Wilcox (Oracle) frag_len = skb_frag_size(&skb_shinfo(skb)->frags[frag]); 4096e909283SNimrod Andy 4106e909283SNimrod Andy /* Handle the last BD specially */ 4116e909283SNimrod Andy if (frag == nr_frags - 1) { 4126e909283SNimrod Andy status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); 4136e909283SNimrod Andy if (fep->bufdesc_ex) { 4146e909283SNimrod Andy estatus |= BD_ENET_TX_INT; 4156e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & 4166e909283SNimrod Andy SKBTX_HW_TSTAMP && fep->hwts_tx_en)) 4176e909283SNimrod Andy estatus |= BD_ENET_TX_TS; 4186e909283SNimrod Andy } 4196e909283SNimrod Andy } 4206e909283SNimrod Andy 4216e909283SNimrod Andy if (fep->bufdesc_ex) { 4226b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 42353bb20d1STroy Kisky estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 4246e909283SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 4256e909283SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 426471ff445SJoakim Zhang 4276e909283SNimrod Andy ebdp->cbd_bdu = 0; 4285cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(estatus); 4296e909283SNimrod Andy } 4306e909283SNimrod Andy 431d7840976SMatthew Wilcox (Oracle) bufaddr = skb_frag_address(this_frag); 4326e909283SNimrod Andy 4337355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 43441ef84ceSFugang Duan if (((unsigned long) bufaddr) & fep->tx_align || 4356b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 4364d494cdcSFugang Duan memcpy(txq->tx_bounce[index], bufaddr, frag_len); 4374d494cdcSFugang Duan bufaddr = txq->tx_bounce[index]; 4386e909283SNimrod Andy 4396b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 4406e909283SNimrod Andy swap_buffer(bufaddr, frag_len); 4416e909283SNimrod Andy } 4426e909283SNimrod Andy 443d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, bufaddr, frag_len, 444d6bf3143SRussell King DMA_TO_DEVICE); 445d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 4466e909283SNimrod Andy if (net_ratelimit()) 4476e909283SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 4486e909283SNimrod Andy goto dma_mapping_error; 4496e909283SNimrod Andy } 4506e909283SNimrod Andy 4515cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(addr); 4525cfa3039SJohannes Berg bdp->cbd_datlen = cpu_to_fec16(frag_len); 453be293467STroy Kisky /* Make sure the updates to rest of the descriptor are 454be293467STroy Kisky * performed before transferring ownership. 455be293467STroy Kisky */ 456be293467STroy Kisky wmb(); 4575cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(status); 4586e909283SNimrod Andy } 4596e909283SNimrod Andy 460c4bc44c6SKevin Hao return bdp; 4616e909283SNimrod Andy dma_mapping_error: 4627355f276STroy Kisky bdp = txq->bd.cur; 4636e909283SNimrod Andy for (i = 0; i < frag; i++) { 4647355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 4655cfa3039SJohannes Berg dma_unmap_single(&fep->pdev->dev, fec32_to_cpu(bdp->cbd_bufaddr), 4665cfa3039SJohannes Berg fec16_to_cpu(bdp->cbd_datlen), DMA_TO_DEVICE); 4676e909283SNimrod Andy } 468c4bc44c6SKevin Hao return ERR_PTR(-ENOMEM); 4696e909283SNimrod Andy } 4706e909283SNimrod Andy 4714d494cdcSFugang Duan static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq, 4724d494cdcSFugang Duan struct sk_buff *skb, struct net_device *ndev) 4736e909283SNimrod Andy { 4746e909283SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 4756e909283SNimrod Andy int nr_frags = skb_shinfo(skb)->nr_frags; 4766e909283SNimrod Andy struct bufdesc *bdp, *last_bdp; 4776e909283SNimrod Andy void *bufaddr; 478d6bf3143SRussell King dma_addr_t addr; 4796e909283SNimrod Andy unsigned short status; 4806e909283SNimrod Andy unsigned short buflen; 4816e909283SNimrod Andy unsigned int estatus = 0; 4826e909283SNimrod Andy unsigned int index; 48379f33912SNimrod Andy int entries_free; 4846e909283SNimrod Andy 4857355f276STroy Kisky entries_free = fec_enet_get_free_txdesc_num(txq); 48679f33912SNimrod Andy if (entries_free < MAX_SKB_FRAGS + 1) { 48779f33912SNimrod Andy dev_kfree_skb_any(skb); 48879f33912SNimrod Andy if (net_ratelimit()) 48979f33912SNimrod Andy netdev_err(ndev, "NOT enough BD for SG!\n"); 49079f33912SNimrod Andy return NETDEV_TX_OK; 49179f33912SNimrod Andy } 49279f33912SNimrod Andy 4936e909283SNimrod Andy /* Protocol checksum off-load for TCP and UDP. */ 4946e909283SNimrod Andy if (fec_enet_clear_csum(skb, ndev)) { 4956e909283SNimrod Andy dev_kfree_skb_any(skb); 4966e909283SNimrod Andy return NETDEV_TX_OK; 4976e909283SNimrod Andy } 4986e909283SNimrod Andy 4996e909283SNimrod Andy /* Fill in a Tx ring entry */ 5007355f276STroy Kisky bdp = txq->bd.cur; 501c4bc44c6SKevin Hao last_bdp = bdp; 5025cfa3039SJohannes Berg status = fec16_to_cpu(bdp->cbd_sc); 5036e909283SNimrod Andy status &= ~BD_ENET_TX_STATS; 5046e909283SNimrod Andy 5056e909283SNimrod Andy /* Set buffer length and buffer pointer */ 5066e909283SNimrod Andy bufaddr = skb->data; 5076e909283SNimrod Andy buflen = skb_headlen(skb); 5086e909283SNimrod Andy 5097355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 51041ef84ceSFugang Duan if (((unsigned long) bufaddr) & fep->tx_align || 5116b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 5124d494cdcSFugang Duan memcpy(txq->tx_bounce[index], skb->data, buflen); 5134d494cdcSFugang Duan bufaddr = txq->tx_bounce[index]; 5146e909283SNimrod Andy 5156b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 5166e909283SNimrod Andy swap_buffer(bufaddr, buflen); 5176e909283SNimrod Andy } 5186e909283SNimrod Andy 519d6bf3143SRussell King /* Push the data cache so the CPM does not get stale memory data. */ 520d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, bufaddr, buflen, DMA_TO_DEVICE); 521d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 5226e909283SNimrod Andy dev_kfree_skb_any(skb); 5236e909283SNimrod Andy if (net_ratelimit()) 5246e909283SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 5256e909283SNimrod Andy return NETDEV_TX_OK; 5266e909283SNimrod Andy } 5276e909283SNimrod Andy 5286e909283SNimrod Andy if (nr_frags) { 529c4bc44c6SKevin Hao last_bdp = fec_enet_txq_submit_frag_skb(txq, skb, ndev); 530fc75ba51STroy Kisky if (IS_ERR(last_bdp)) { 531fc75ba51STroy Kisky dma_unmap_single(&fep->pdev->dev, addr, 532fc75ba51STroy Kisky buflen, DMA_TO_DEVICE); 533fc75ba51STroy Kisky dev_kfree_skb_any(skb); 534c4bc44c6SKevin Hao return NETDEV_TX_OK; 535fc75ba51STroy Kisky } 5366e909283SNimrod Andy } else { 5376e909283SNimrod Andy status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); 5386e909283SNimrod Andy if (fep->bufdesc_ex) { 5396e909283SNimrod Andy estatus = BD_ENET_TX_INT; 5406e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & 5416e909283SNimrod Andy SKBTX_HW_TSTAMP && fep->hwts_tx_en)) 5426e909283SNimrod Andy estatus |= BD_ENET_TX_TS; 5436e909283SNimrod Andy } 5446e909283SNimrod Andy } 545fc75ba51STroy Kisky bdp->cbd_bufaddr = cpu_to_fec32(addr); 546fc75ba51STroy Kisky bdp->cbd_datlen = cpu_to_fec16(buflen); 5476e909283SNimrod Andy 5486e909283SNimrod Andy if (fep->bufdesc_ex) { 5496e909283SNimrod Andy 5506e909283SNimrod Andy struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 5516e909283SNimrod Andy 5526e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && 5536e909283SNimrod Andy fep->hwts_tx_en)) 5546e909283SNimrod Andy skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; 5556e909283SNimrod Andy 5566b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 55753bb20d1STroy Kisky estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 558befe8213SNimrod Andy 5596e909283SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 5606e909283SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 5616e909283SNimrod Andy 5626e909283SNimrod Andy ebdp->cbd_bdu = 0; 5635cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(estatus); 5646e909283SNimrod Andy } 5656e909283SNimrod Andy 5667355f276STroy Kisky index = fec_enet_get_bd_index(last_bdp, &txq->bd); 5676e909283SNimrod Andy /* Save skb pointer */ 5684d494cdcSFugang Duan txq->tx_skbuff[index] = skb; 5696e909283SNimrod Andy 570be293467STroy Kisky /* Make sure the updates to rest of the descriptor are performed before 571be293467STroy Kisky * transferring ownership. 572be293467STroy Kisky */ 573be293467STroy Kisky wmb(); 5746e909283SNimrod Andy 5756e909283SNimrod Andy /* Send it on its way. Tell FEC it's ready, interrupt when done, 5766e909283SNimrod Andy * it's the last BD of the frame, and to put the CRC on the end. 5776e909283SNimrod Andy */ 5786e909283SNimrod Andy status |= (BD_ENET_TX_READY | BD_ENET_TX_TC); 5795cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(status); 5806e909283SNimrod Andy 581793fc096SFrank Li /* If this was the last BD in the ring, start at the beginning again. */ 5827355f276STroy Kisky bdp = fec_enet_get_nextdesc(last_bdp, &txq->bd); 583793fc096SFrank Li 5847a2a8451SEric Dumazet skb_tx_timestamp(skb); 5857a2a8451SEric Dumazet 586c4bc44c6SKevin Hao /* Make sure the update to bdp and tx_skbuff are performed before 5877355f276STroy Kisky * txq->bd.cur. 588c4bc44c6SKevin Hao */ 589c4bc44c6SKevin Hao wmb(); 5907355f276STroy Kisky txq->bd.cur = bdp; 591793fc096SFrank Li 592793fc096SFrank Li /* Trigger transmission start */ 59353bb20d1STroy Kisky writel(0, txq->bd.reg_desc_active); 594793fc096SFrank Li 5956e909283SNimrod Andy return 0; 596793fc096SFrank Li } 597793fc096SFrank Li 59879f33912SNimrod Andy static int 5994d494cdcSFugang Duan fec_enet_txq_put_data_tso(struct fec_enet_priv_tx_q *txq, struct sk_buff *skb, 6004d494cdcSFugang Duan struct net_device *ndev, 60179f33912SNimrod Andy struct bufdesc *bdp, int index, char *data, 60279f33912SNimrod Andy int size, bool last_tcp, bool is_last) 60379f33912SNimrod Andy { 60479f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 60561cd2ebbSFabian Frederick struct bufdesc_ex *ebdp = container_of(bdp, struct bufdesc_ex, desc); 60679f33912SNimrod Andy unsigned short status; 60779f33912SNimrod Andy unsigned int estatus = 0; 608d6bf3143SRussell King dma_addr_t addr; 60979f33912SNimrod Andy 6105cfa3039SJohannes Berg status = fec16_to_cpu(bdp->cbd_sc); 61179f33912SNimrod Andy status &= ~BD_ENET_TX_STATS; 61279f33912SNimrod Andy 61379f33912SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 61479f33912SNimrod Andy 61541ef84ceSFugang Duan if (((unsigned long) data) & fep->tx_align || 6166b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 6174d494cdcSFugang Duan memcpy(txq->tx_bounce[index], data, size); 6184d494cdcSFugang Duan data = txq->tx_bounce[index]; 61979f33912SNimrod Andy 6206b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 62179f33912SNimrod Andy swap_buffer(data, size); 62279f33912SNimrod Andy } 62379f33912SNimrod Andy 624d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, data, size, DMA_TO_DEVICE); 625d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 62679f33912SNimrod Andy dev_kfree_skb_any(skb); 62779f33912SNimrod Andy if (net_ratelimit()) 62879f33912SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 62979f33912SNimrod Andy return NETDEV_TX_BUSY; 63079f33912SNimrod Andy } 63179f33912SNimrod Andy 6325cfa3039SJohannes Berg bdp->cbd_datlen = cpu_to_fec16(size); 6335cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(addr); 634d6bf3143SRussell King 63579f33912SNimrod Andy if (fep->bufdesc_ex) { 6366b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 63753bb20d1STroy Kisky estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 63879f33912SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 63979f33912SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 64079f33912SNimrod Andy ebdp->cbd_bdu = 0; 6415cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(estatus); 64279f33912SNimrod Andy } 64379f33912SNimrod Andy 64479f33912SNimrod Andy /* Handle the last BD specially */ 64579f33912SNimrod Andy if (last_tcp) 64679f33912SNimrod Andy status |= (BD_ENET_TX_LAST | BD_ENET_TX_TC); 64779f33912SNimrod Andy if (is_last) { 64879f33912SNimrod Andy status |= BD_ENET_TX_INTR; 64979f33912SNimrod Andy if (fep->bufdesc_ex) 6505cfa3039SJohannes Berg ebdp->cbd_esc |= cpu_to_fec32(BD_ENET_TX_INT); 65179f33912SNimrod Andy } 65279f33912SNimrod Andy 6535cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(status); 65479f33912SNimrod Andy 65579f33912SNimrod Andy return 0; 65679f33912SNimrod Andy } 65779f33912SNimrod Andy 65879f33912SNimrod Andy static int 6594d494cdcSFugang Duan fec_enet_txq_put_hdr_tso(struct fec_enet_priv_tx_q *txq, 6604d494cdcSFugang Duan struct sk_buff *skb, struct net_device *ndev, 66179f33912SNimrod Andy struct bufdesc *bdp, int index) 66279f33912SNimrod Andy { 66379f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 66479f33912SNimrod Andy int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); 66561cd2ebbSFabian Frederick struct bufdesc_ex *ebdp = container_of(bdp, struct bufdesc_ex, desc); 66679f33912SNimrod Andy void *bufaddr; 66779f33912SNimrod Andy unsigned long dmabuf; 66879f33912SNimrod Andy unsigned short status; 66979f33912SNimrod Andy unsigned int estatus = 0; 67079f33912SNimrod Andy 6715cfa3039SJohannes Berg status = fec16_to_cpu(bdp->cbd_sc); 67279f33912SNimrod Andy status &= ~BD_ENET_TX_STATS; 67379f33912SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 67479f33912SNimrod Andy 6754d494cdcSFugang Duan bufaddr = txq->tso_hdrs + index * TSO_HEADER_SIZE; 6764d494cdcSFugang Duan dmabuf = txq->tso_hdrs_dma + index * TSO_HEADER_SIZE; 67741ef84ceSFugang Duan if (((unsigned long)bufaddr) & fep->tx_align || 6786b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 6794d494cdcSFugang Duan memcpy(txq->tx_bounce[index], skb->data, hdr_len); 6804d494cdcSFugang Duan bufaddr = txq->tx_bounce[index]; 68179f33912SNimrod Andy 6826b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 68379f33912SNimrod Andy swap_buffer(bufaddr, hdr_len); 68479f33912SNimrod Andy 68579f33912SNimrod Andy dmabuf = dma_map_single(&fep->pdev->dev, bufaddr, 68679f33912SNimrod Andy hdr_len, DMA_TO_DEVICE); 68779f33912SNimrod Andy if (dma_mapping_error(&fep->pdev->dev, dmabuf)) { 68879f33912SNimrod Andy dev_kfree_skb_any(skb); 68979f33912SNimrod Andy if (net_ratelimit()) 69079f33912SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 69179f33912SNimrod Andy return NETDEV_TX_BUSY; 69279f33912SNimrod Andy } 69379f33912SNimrod Andy } 69479f33912SNimrod Andy 6955cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(dmabuf); 6965cfa3039SJohannes Berg bdp->cbd_datlen = cpu_to_fec16(hdr_len); 69779f33912SNimrod Andy 69879f33912SNimrod Andy if (fep->bufdesc_ex) { 6996b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 70053bb20d1STroy Kisky estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 70179f33912SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 70279f33912SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 70379f33912SNimrod Andy ebdp->cbd_bdu = 0; 7045cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(estatus); 70579f33912SNimrod Andy } 70679f33912SNimrod Andy 7075cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(status); 70879f33912SNimrod Andy 70979f33912SNimrod Andy return 0; 71079f33912SNimrod Andy } 71179f33912SNimrod Andy 7124d494cdcSFugang Duan static int fec_enet_txq_submit_tso(struct fec_enet_priv_tx_q *txq, 7134d494cdcSFugang Duan struct sk_buff *skb, 7144d494cdcSFugang Duan struct net_device *ndev) 71579f33912SNimrod Andy { 71679f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 717761b331cSEric Dumazet int hdr_len, total_len, data_left; 7187355f276STroy Kisky struct bufdesc *bdp = txq->bd.cur; 71979f33912SNimrod Andy struct tso_t tso; 72079f33912SNimrod Andy unsigned int index = 0; 72179f33912SNimrod Andy int ret; 72279f33912SNimrod Andy 7237355f276STroy Kisky if (tso_count_descs(skb) >= fec_enet_get_free_txdesc_num(txq)) { 72479f33912SNimrod Andy dev_kfree_skb_any(skb); 72579f33912SNimrod Andy if (net_ratelimit()) 72679f33912SNimrod Andy netdev_err(ndev, "NOT enough BD for TSO!\n"); 72779f33912SNimrod Andy return NETDEV_TX_OK; 72879f33912SNimrod Andy } 72979f33912SNimrod Andy 73079f33912SNimrod Andy /* Protocol checksum off-load for TCP and UDP. */ 73179f33912SNimrod Andy if (fec_enet_clear_csum(skb, ndev)) { 73279f33912SNimrod Andy dev_kfree_skb_any(skb); 73379f33912SNimrod Andy return NETDEV_TX_OK; 73479f33912SNimrod Andy } 73579f33912SNimrod Andy 73679f33912SNimrod Andy /* Initialize the TSO handler, and prepare the first payload */ 737761b331cSEric Dumazet hdr_len = tso_start(skb, &tso); 73879f33912SNimrod Andy 73979f33912SNimrod Andy total_len = skb->len - hdr_len; 74079f33912SNimrod Andy while (total_len > 0) { 74179f33912SNimrod Andy char *hdr; 74279f33912SNimrod Andy 7437355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 74479f33912SNimrod Andy data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len); 74579f33912SNimrod Andy total_len -= data_left; 74679f33912SNimrod Andy 74779f33912SNimrod Andy /* prepare packet headers: MAC + IP + TCP */ 7484d494cdcSFugang Duan hdr = txq->tso_hdrs + index * TSO_HEADER_SIZE; 74979f33912SNimrod Andy tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0); 7504d494cdcSFugang Duan ret = fec_enet_txq_put_hdr_tso(txq, skb, ndev, bdp, index); 75179f33912SNimrod Andy if (ret) 75279f33912SNimrod Andy goto err_release; 75379f33912SNimrod Andy 75479f33912SNimrod Andy while (data_left > 0) { 75579f33912SNimrod Andy int size; 75679f33912SNimrod Andy 75779f33912SNimrod Andy size = min_t(int, tso.size, data_left); 7587355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 7597355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 7604d494cdcSFugang Duan ret = fec_enet_txq_put_data_tso(txq, skb, ndev, 7614d494cdcSFugang Duan bdp, index, 7624d494cdcSFugang Duan tso.data, size, 7634d494cdcSFugang Duan size == data_left, 76479f33912SNimrod Andy total_len == 0); 76579f33912SNimrod Andy if (ret) 76679f33912SNimrod Andy goto err_release; 76779f33912SNimrod Andy 76879f33912SNimrod Andy data_left -= size; 76979f33912SNimrod Andy tso_build_data(skb, &tso, size); 77079f33912SNimrod Andy } 77179f33912SNimrod Andy 7727355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 77379f33912SNimrod Andy } 77479f33912SNimrod Andy 77579f33912SNimrod Andy /* Save skb pointer */ 7764d494cdcSFugang Duan txq->tx_skbuff[index] = skb; 77779f33912SNimrod Andy 77879f33912SNimrod Andy skb_tx_timestamp(skb); 7797355f276STroy Kisky txq->bd.cur = bdp; 78079f33912SNimrod Andy 78179f33912SNimrod Andy /* Trigger transmission start */ 7826b7e4008SLothar Waßmann if (!(fep->quirks & FEC_QUIRK_ERR007885) || 78353bb20d1STroy Kisky !readl(txq->bd.reg_desc_active) || 78453bb20d1STroy Kisky !readl(txq->bd.reg_desc_active) || 78553bb20d1STroy Kisky !readl(txq->bd.reg_desc_active) || 78653bb20d1STroy Kisky !readl(txq->bd.reg_desc_active)) 78753bb20d1STroy Kisky writel(0, txq->bd.reg_desc_active); 78879f33912SNimrod Andy 78979f33912SNimrod Andy return 0; 79079f33912SNimrod Andy 79179f33912SNimrod Andy err_release: 79279f33912SNimrod Andy /* TODO: Release all used data descriptors for TSO */ 79379f33912SNimrod Andy return ret; 79479f33912SNimrod Andy } 79579f33912SNimrod Andy 79661a4427bSNimrod Andy static netdev_tx_t 79761a4427bSNimrod Andy fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) 79861a4427bSNimrod Andy { 79961a4427bSNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 8006e909283SNimrod Andy int entries_free; 8014d494cdcSFugang Duan unsigned short queue; 8024d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 8034d494cdcSFugang Duan struct netdev_queue *nq; 80461a4427bSNimrod Andy int ret; 80561a4427bSNimrod Andy 8064d494cdcSFugang Duan queue = skb_get_queue_mapping(skb); 8074d494cdcSFugang Duan txq = fep->tx_queue[queue]; 8084d494cdcSFugang Duan nq = netdev_get_tx_queue(ndev, queue); 8094d494cdcSFugang Duan 81079f33912SNimrod Andy if (skb_is_gso(skb)) 8114d494cdcSFugang Duan ret = fec_enet_txq_submit_tso(txq, skb, ndev); 81279f33912SNimrod Andy else 8134d494cdcSFugang Duan ret = fec_enet_txq_submit_skb(txq, skb, ndev); 8146e909283SNimrod Andy if (ret) 8156e909283SNimrod Andy return ret; 81661a4427bSNimrod Andy 8177355f276STroy Kisky entries_free = fec_enet_get_free_txdesc_num(txq); 8184d494cdcSFugang Duan if (entries_free <= txq->tx_stop_threshold) 8194d494cdcSFugang Duan netif_tx_stop_queue(nq); 82061a4427bSNimrod Andy 82161a4427bSNimrod Andy return NETDEV_TX_OK; 82261a4427bSNimrod Andy } 82361a4427bSNimrod Andy 824a210576cSDavid S. Miller /* Init RX & TX buffer descriptors 825a210576cSDavid S. Miller */ 826a210576cSDavid S. Miller static void fec_enet_bd_init(struct net_device *dev) 827a210576cSDavid S. Miller { 828a210576cSDavid S. Miller struct fec_enet_private *fep = netdev_priv(dev); 8294d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 8304d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 831a210576cSDavid S. Miller struct bufdesc *bdp; 832a210576cSDavid S. Miller unsigned int i; 83359d0f746SFrank Li unsigned int q; 834a210576cSDavid S. Miller 83559d0f746SFrank Li for (q = 0; q < fep->num_rx_queues; q++) { 836a210576cSDavid S. Miller /* Initialize the receive buffer descriptors. */ 83759d0f746SFrank Li rxq = fep->rx_queue[q]; 8387355f276STroy Kisky bdp = rxq->bd.base; 8394d494cdcSFugang Duan 8407355f276STroy Kisky for (i = 0; i < rxq->bd.ring_size; i++) { 841a210576cSDavid S. Miller 842a210576cSDavid S. Miller /* Initialize the BD for every fragment in the page. */ 843a210576cSDavid S. Miller if (bdp->cbd_bufaddr) 8445cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); 845a210576cSDavid S. Miller else 8465cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(0); 8477355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 848a210576cSDavid S. Miller } 849a210576cSDavid S. Miller 850a210576cSDavid S. Miller /* Set the last buffer to wrap */ 8517355f276STroy Kisky bdp = fec_enet_get_prevdesc(bdp, &rxq->bd); 8525cfa3039SJohannes Berg bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 853a210576cSDavid S. Miller 8547355f276STroy Kisky rxq->bd.cur = rxq->bd.base; 85559d0f746SFrank Li } 856a210576cSDavid S. Miller 85759d0f746SFrank Li for (q = 0; q < fep->num_tx_queues; q++) { 858a210576cSDavid S. Miller /* ...and the same for transmit */ 85959d0f746SFrank Li txq = fep->tx_queue[q]; 8607355f276STroy Kisky bdp = txq->bd.base; 8617355f276STroy Kisky txq->bd.cur = bdp; 862a210576cSDavid S. Miller 8637355f276STroy Kisky for (i = 0; i < txq->bd.ring_size; i++) { 864a210576cSDavid S. Miller /* Initialize the BD for every fragment in the page. */ 8655cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(0); 866178e5f57SFugang Duan if (bdp->cbd_bufaddr && 867178e5f57SFugang Duan !IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr))) 868178e5f57SFugang Duan dma_unmap_single(&fep->pdev->dev, 869178e5f57SFugang Duan fec32_to_cpu(bdp->cbd_bufaddr), 870178e5f57SFugang Duan fec16_to_cpu(bdp->cbd_datlen), 871178e5f57SFugang Duan DMA_TO_DEVICE); 8724d494cdcSFugang Duan if (txq->tx_skbuff[i]) { 8734d494cdcSFugang Duan dev_kfree_skb_any(txq->tx_skbuff[i]); 8744d494cdcSFugang Duan txq->tx_skbuff[i] = NULL; 875a210576cSDavid S. Miller } 8765cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(0); 8777355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 878a210576cSDavid S. Miller } 879a210576cSDavid S. Miller 880a210576cSDavid S. Miller /* Set the last buffer to wrap */ 8817355f276STroy Kisky bdp = fec_enet_get_prevdesc(bdp, &txq->bd); 8825cfa3039SJohannes Berg bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 8834d494cdcSFugang Duan txq->dirty_tx = bdp; 884a210576cSDavid S. Miller } 88559d0f746SFrank Li } 88659d0f746SFrank Li 887ce99d0d3SFrank Li static void fec_enet_active_rxring(struct net_device *ndev) 888ce99d0d3SFrank Li { 889ce99d0d3SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 890ce99d0d3SFrank Li int i; 891ce99d0d3SFrank Li 892ce99d0d3SFrank Li for (i = 0; i < fep->num_rx_queues; i++) 89353bb20d1STroy Kisky writel(0, fep->rx_queue[i]->bd.reg_desc_active); 894ce99d0d3SFrank Li } 895ce99d0d3SFrank Li 89659d0f746SFrank Li static void fec_enet_enable_ring(struct net_device *ndev) 89759d0f746SFrank Li { 89859d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 89959d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 90059d0f746SFrank Li struct fec_enet_priv_rx_q *rxq; 90159d0f746SFrank Li int i; 90259d0f746SFrank Li 90359d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) { 90459d0f746SFrank Li rxq = fep->rx_queue[i]; 9057355f276STroy Kisky writel(rxq->bd.dma, fep->hwp + FEC_R_DES_START(i)); 906fbbeefddSAndrew Lunn writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_R_BUFF_SIZE(i)); 90759d0f746SFrank Li 90859d0f746SFrank Li /* enable DMA1/2 */ 90959d0f746SFrank Li if (i) 91059d0f746SFrank Li writel(RCMR_MATCHEN | RCMR_CMP(i), 91159d0f746SFrank Li fep->hwp + FEC_RCMR(i)); 91259d0f746SFrank Li } 91359d0f746SFrank Li 91459d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 91559d0f746SFrank Li txq = fep->tx_queue[i]; 9167355f276STroy Kisky writel(txq->bd.dma, fep->hwp + FEC_X_DES_START(i)); 91759d0f746SFrank Li 91859d0f746SFrank Li /* enable DMA1/2 */ 91959d0f746SFrank Li if (i) 92059d0f746SFrank Li writel(DMA_CLASS_EN | IDLE_SLOPE(i), 92159d0f746SFrank Li fep->hwp + FEC_DMA_CFG(i)); 92259d0f746SFrank Li } 92359d0f746SFrank Li } 92459d0f746SFrank Li 92559d0f746SFrank Li static void fec_enet_reset_skb(struct net_device *ndev) 92659d0f746SFrank Li { 92759d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 92859d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 92959d0f746SFrank Li int i, j; 93059d0f746SFrank Li 93159d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 93259d0f746SFrank Li txq = fep->tx_queue[i]; 93359d0f746SFrank Li 9347355f276STroy Kisky for (j = 0; j < txq->bd.ring_size; j++) { 93559d0f746SFrank Li if (txq->tx_skbuff[j]) { 93659d0f746SFrank Li dev_kfree_skb_any(txq->tx_skbuff[j]); 93759d0f746SFrank Li txq->tx_skbuff[j] = NULL; 93859d0f746SFrank Li } 93959d0f746SFrank Li } 94059d0f746SFrank Li } 94159d0f746SFrank Li } 942a210576cSDavid S. Miller 943dbc64a8eSRussell King /* 944dbc64a8eSRussell King * This function is called to start or restart the FEC during a link 945dbc64a8eSRussell King * change, transmit timeout, or to reconfigure the FEC. The network 946dbc64a8eSRussell King * packet processing for this device must be stopped before this call. 947793fc096SFrank Li */ 948793fc096SFrank Li static void 949ef83337dSRussell King fec_restart(struct net_device *ndev) 950793fc096SFrank Li { 951793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 952793fc096SFrank Li u32 temp_mac[2]; 953793fc096SFrank Li u32 rcntl = OPT_FRAME_SIZE | 0x04; 954793fc096SFrank Li u32 ecntl = 0x2; /* ETHEREN */ 955793fc096SFrank Li 956106c314cSFugang Duan /* Whack a reset. We should wait for this. 957106c314cSFugang Duan * For i.MX6SX SOC, enet use AXI bus, we use disable MAC 958106c314cSFugang Duan * instead of reset MAC itself. 959106c314cSFugang Duan */ 960471ff445SJoakim Zhang if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES || 961c730ab42SLaurent Badel ((fep->quirks & FEC_QUIRK_NO_HARD_RESET) && fep->link)) { 962106c314cSFugang Duan writel(0, fep->hwp + FEC_ECNTRL); 963106c314cSFugang Duan } else { 964793fc096SFrank Li writel(1, fep->hwp + FEC_ECNTRL); 965793fc096SFrank Li udelay(10); 966106c314cSFugang Duan } 967793fc096SFrank Li 968793fc096SFrank Li /* 969793fc096SFrank Li * enet-mac reset will reset mac address registers too, 970793fc096SFrank Li * so need to reconfigure it. 971793fc096SFrank Li */ 972793fc096SFrank Li memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN); 9735cfa3039SJohannes Berg writel((__force u32)cpu_to_be32(temp_mac[0]), 9745cfa3039SJohannes Berg fep->hwp + FEC_ADDR_LOW); 9755cfa3039SJohannes Berg writel((__force u32)cpu_to_be32(temp_mac[1]), 9765cfa3039SJohannes Berg fep->hwp + FEC_ADDR_HIGH); 977793fc096SFrank Li 978f166f890SAndrew Lunn /* Clear any outstanding interrupt, except MDIO. */ 979f166f890SAndrew Lunn writel((0xffffffff & ~FEC_ENET_MII), fep->hwp + FEC_IEVENT); 980793fc096SFrank Li 981a210576cSDavid S. Miller fec_enet_bd_init(ndev); 982a210576cSDavid S. Miller 98359d0f746SFrank Li fec_enet_enable_ring(ndev); 984793fc096SFrank Li 98559d0f746SFrank Li /* Reset tx SKB buffers. */ 98659d0f746SFrank Li fec_enet_reset_skb(ndev); 987793fc096SFrank Li 988793fc096SFrank Li /* Enable MII mode */ 989ef83337dSRussell King if (fep->full_duplex == DUPLEX_FULL) { 990793fc096SFrank Li /* FD enable */ 991793fc096SFrank Li writel(0x04, fep->hwp + FEC_X_CNTRL); 992793fc096SFrank Li } else { 993793fc096SFrank Li /* No Rcv on Xmit */ 994793fc096SFrank Li rcntl |= 0x02; 995793fc096SFrank Li writel(0x0, fep->hwp + FEC_X_CNTRL); 996793fc096SFrank Li } 997793fc096SFrank Li 998793fc096SFrank Li /* Set MII speed */ 999793fc096SFrank Li writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 1000793fc096SFrank Li 1001d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 100218803495SGreg Ungerer if (fep->quirks & FEC_QUIRK_HAS_RACC) { 100332d1bbb1SGeert Uytterhoeven u32 val = readl(fep->hwp + FEC_RACC); 100432d1bbb1SGeert Uytterhoeven 10053ac72b7bSEric Nelson /* align IP header */ 10063ac72b7bSEric Nelson val |= FEC_RACC_SHIFT16; 10074c09eed9SJim Baxter if (fep->csum_flags & FLAG_RX_CSUM_ENABLED) 10083ac72b7bSEric Nelson /* set RX checksum */ 10094c09eed9SJim Baxter val |= FEC_RACC_OPTIONS; 10104c09eed9SJim Baxter else 10114c09eed9SJim Baxter val &= ~FEC_RACC_OPTIONS; 10124c09eed9SJim Baxter writel(val, fep->hwp + FEC_RACC); 101355cd48c8STroy Kisky writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_FTRL); 101432867fccSFabio Estevam } 1015d1391930SGuenter Roeck #endif 10164c09eed9SJim Baxter 1017793fc096SFrank Li /* 1018793fc096SFrank Li * The phy interface and speed need to get configured 1019793fc096SFrank Li * differently on enet-mac. 1020793fc096SFrank Li */ 10216b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_ENET_MAC) { 1022793fc096SFrank Li /* Enable flow control and length check */ 1023793fc096SFrank Li rcntl |= 0x40000000 | 0x00000020; 1024793fc096SFrank Li 1025793fc096SFrank Li /* RGMII, RMII or MII */ 1026e813bb2bSMarkus Pargmann if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII || 1027e813bb2bSMarkus Pargmann fep->phy_interface == PHY_INTERFACE_MODE_RGMII_ID || 1028e813bb2bSMarkus Pargmann fep->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID || 1029e813bb2bSMarkus Pargmann fep->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID) 1030793fc096SFrank Li rcntl |= (1 << 6); 1031793fc096SFrank Li else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) 1032793fc096SFrank Li rcntl |= (1 << 8); 1033793fc096SFrank Li else 1034793fc096SFrank Li rcntl &= ~(1 << 8); 1035793fc096SFrank Li 1036793fc096SFrank Li /* 1G, 100M or 10M */ 103745f5c327SPhilippe Reynes if (ndev->phydev) { 103845f5c327SPhilippe Reynes if (ndev->phydev->speed == SPEED_1000) 1039793fc096SFrank Li ecntl |= (1 << 5); 104045f5c327SPhilippe Reynes else if (ndev->phydev->speed == SPEED_100) 1041793fc096SFrank Li rcntl &= ~(1 << 9); 1042793fc096SFrank Li else 1043793fc096SFrank Li rcntl |= (1 << 9); 1044793fc096SFrank Li } 1045793fc096SFrank Li } else { 1046793fc096SFrank Li #ifdef FEC_MIIGSK_ENR 10476b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_USE_GASKET) { 1048793fc096SFrank Li u32 cfgr; 1049793fc096SFrank Li /* disable the gasket and wait */ 1050793fc096SFrank Li writel(0, fep->hwp + FEC_MIIGSK_ENR); 1051793fc096SFrank Li while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4) 1052793fc096SFrank Li udelay(1); 1053793fc096SFrank Li 1054793fc096SFrank Li /* 1055793fc096SFrank Li * configure the gasket: 1056793fc096SFrank Li * RMII, 50 MHz, no loopback, no echo 1057793fc096SFrank Li * MII, 25 MHz, no loopback, no echo 1058793fc096SFrank Li */ 1059793fc096SFrank Li cfgr = (fep->phy_interface == PHY_INTERFACE_MODE_RMII) 1060793fc096SFrank Li ? BM_MIIGSK_CFGR_RMII : BM_MIIGSK_CFGR_MII; 106145f5c327SPhilippe Reynes if (ndev->phydev && ndev->phydev->speed == SPEED_10) 1062793fc096SFrank Li cfgr |= BM_MIIGSK_CFGR_FRCONT_10M; 1063793fc096SFrank Li writel(cfgr, fep->hwp + FEC_MIIGSK_CFGR); 1064793fc096SFrank Li 1065793fc096SFrank Li /* re-enable the gasket */ 1066793fc096SFrank Li writel(2, fep->hwp + FEC_MIIGSK_ENR); 1067793fc096SFrank Li } 1068793fc096SFrank Li #endif 1069793fc096SFrank Li } 1070793fc096SFrank Li 1071d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 1072793fc096SFrank Li /* enable pause frame*/ 1073793fc096SFrank Li if ((fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) || 1074793fc096SFrank Li ((fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) && 107545f5c327SPhilippe Reynes ndev->phydev && ndev->phydev->pause)) { 1076793fc096SFrank Li rcntl |= FEC_ENET_FCE; 1077793fc096SFrank Li 10784c09eed9SJim Baxter /* set FIFO threshold parameter to reduce overrun */ 1079793fc096SFrank Li writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM); 1080793fc096SFrank Li writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL); 1081793fc096SFrank Li writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM); 1082793fc096SFrank Li writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL); 1083793fc096SFrank Li 1084793fc096SFrank Li /* OPD */ 1085793fc096SFrank Li writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD); 1086793fc096SFrank Li } else { 1087793fc096SFrank Li rcntl &= ~FEC_ENET_FCE; 1088793fc096SFrank Li } 1089d1391930SGuenter Roeck #endif /* !defined(CONFIG_M5272) */ 1090793fc096SFrank Li 1091793fc096SFrank Li writel(rcntl, fep->hwp + FEC_R_CNTRL); 1092793fc096SFrank Li 109384fe6182SStefan Wahren /* Setup multicast filter. */ 109484fe6182SStefan Wahren set_multicast_list(ndev); 109584fe6182SStefan Wahren #ifndef CONFIG_M5272 109684fe6182SStefan Wahren writel(0, fep->hwp + FEC_HASH_TABLE_HIGH); 109784fe6182SStefan Wahren writel(0, fep->hwp + FEC_HASH_TABLE_LOW); 109884fe6182SStefan Wahren #endif 109984fe6182SStefan Wahren 11006b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_ENET_MAC) { 1101793fc096SFrank Li /* enable ENET endian swap */ 1102793fc096SFrank Li ecntl |= (1 << 8); 1103793fc096SFrank Li /* enable ENET store and forward mode */ 1104793fc096SFrank Li writel(1 << 8, fep->hwp + FEC_X_WMRK); 1105793fc096SFrank Li } 1106793fc096SFrank Li 1107793fc096SFrank Li if (fep->bufdesc_ex) 1108793fc096SFrank Li ecntl |= (1 << 4); 1109793fc096SFrank Li 111038ae92dcSChris Healy #ifndef CONFIG_M5272 1111b9eef55cSJim Baxter /* Enable the MIB statistic event counters */ 1112b9eef55cSJim Baxter writel(0 << 31, fep->hwp + FEC_MIB_CTRLSTAT); 111338ae92dcSChris Healy #endif 111438ae92dcSChris Healy 1115793fc096SFrank Li /* And last, enable the transmit and receive processing */ 1116793fc096SFrank Li writel(ecntl, fep->hwp + FEC_ECNTRL); 1117ce99d0d3SFrank Li fec_enet_active_rxring(ndev); 1118793fc096SFrank Li 1119793fc096SFrank Li if (fep->bufdesc_ex) 1120793fc096SFrank Li fec_ptp_start_cyclecounter(ndev); 1121793fc096SFrank Li 1122793fc096SFrank Li /* Enable interrupts we wish to service */ 11230c5a3aefSNimrod Andy if (fep->link) 1124793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 11250c5a3aefSNimrod Andy else 1126f166f890SAndrew Lunn writel(0, fep->hwp + FEC_IMASK); 1127d851b47bSFugang Duan 1128d851b47bSFugang Duan /* Init the interrupt coalescing */ 1129d851b47bSFugang Duan fec_enet_itr_coal_init(ndev); 1130d851b47bSFugang Duan 1131793fc096SFrank Li } 1132793fc096SFrank Li 1133da722186SMartin Fuzzey static void fec_enet_stop_mode(struct fec_enet_private *fep, bool enabled) 1134da722186SMartin Fuzzey { 1135da722186SMartin Fuzzey struct fec_platform_data *pdata = fep->pdev->dev.platform_data; 1136da722186SMartin Fuzzey struct fec_stop_mode_gpr *stop_gpr = &fep->stop_gpr; 1137da722186SMartin Fuzzey 1138da722186SMartin Fuzzey if (stop_gpr->gpr) { 1139da722186SMartin Fuzzey if (enabled) 1140da722186SMartin Fuzzey regmap_update_bits(stop_gpr->gpr, stop_gpr->reg, 1141da722186SMartin Fuzzey BIT(stop_gpr->bit), 1142da722186SMartin Fuzzey BIT(stop_gpr->bit)); 1143da722186SMartin Fuzzey else 1144da722186SMartin Fuzzey regmap_update_bits(stop_gpr->gpr, stop_gpr->reg, 1145da722186SMartin Fuzzey BIT(stop_gpr->bit), 0); 1146da722186SMartin Fuzzey } else if (pdata && pdata->sleep_mode_enable) { 1147da722186SMartin Fuzzey pdata->sleep_mode_enable(enabled); 1148da722186SMartin Fuzzey } 1149da722186SMartin Fuzzey } 1150da722186SMartin Fuzzey 1151793fc096SFrank Li static void 1152793fc096SFrank Li fec_stop(struct net_device *ndev) 1153793fc096SFrank Li { 1154793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1155793fc096SFrank Li u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8); 1156de40ed31SNimrod Andy u32 val; 1157793fc096SFrank Li 1158793fc096SFrank Li /* We cannot expect a graceful transmit stop without link !!! */ 1159793fc096SFrank Li if (fep->link) { 1160793fc096SFrank Li writel(1, fep->hwp + FEC_X_CNTRL); /* Graceful transmit stop */ 1161793fc096SFrank Li udelay(10); 1162793fc096SFrank Li if (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_GRA)) 116331b7720cSJoe Perches netdev_err(ndev, "Graceful transmit stop did not complete!\n"); 1164793fc096SFrank Li } 1165793fc096SFrank Li 1166106c314cSFugang Duan /* Whack a reset. We should wait for this. 1167106c314cSFugang Duan * For i.MX6SX SOC, enet use AXI bus, we use disable MAC 1168106c314cSFugang Duan * instead of reset MAC itself. 1169106c314cSFugang Duan */ 1170de40ed31SNimrod Andy if (!(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { 1171471ff445SJoakim Zhang if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) { 1172106c314cSFugang Duan writel(0, fep->hwp + FEC_ECNTRL); 1173106c314cSFugang Duan } else { 1174793fc096SFrank Li writel(1, fep->hwp + FEC_ECNTRL); 1175793fc096SFrank Li udelay(10); 1176106c314cSFugang Duan } 1177793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 1178de40ed31SNimrod Andy } else { 1179de40ed31SNimrod Andy writel(FEC_DEFAULT_IMASK | FEC_ENET_WAKEUP, fep->hwp + FEC_IMASK); 1180de40ed31SNimrod Andy val = readl(fep->hwp + FEC_ECNTRL); 1181de40ed31SNimrod Andy val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP); 1182de40ed31SNimrod Andy writel(val, fep->hwp + FEC_ECNTRL); 1183da722186SMartin Fuzzey fec_enet_stop_mode(fep, true); 1184de40ed31SNimrod Andy } 1185de40ed31SNimrod Andy writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 1186793fc096SFrank Li 1187793fc096SFrank Li /* We have to keep ENET enabled to have MII interrupt stay working */ 1188de40ed31SNimrod Andy if (fep->quirks & FEC_QUIRK_ENET_MAC && 1189de40ed31SNimrod Andy !(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { 1190793fc096SFrank Li writel(2, fep->hwp + FEC_ECNTRL); 1191793fc096SFrank Li writel(rmii_mode, fep->hwp + FEC_R_CNTRL); 1192793fc096SFrank Li } 1193793fc096SFrank Li } 1194793fc096SFrank Li 1195793fc096SFrank Li 1196793fc096SFrank Li static void 11970290bd29SMichael S. Tsirkin fec_timeout(struct net_device *ndev, unsigned int txqueue) 1198793fc096SFrank Li { 1199793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1200793fc096SFrank Li 1201344756f6SRussell King fec_dump(ndev); 1202344756f6SRussell King 1203793fc096SFrank Li ndev->stats.tx_errors++; 1204793fc096SFrank Li 120536cdc743SRussell King schedule_work(&fep->tx_timeout_work); 120654309fa6SFrank Li } 120754309fa6SFrank Li 120836cdc743SRussell King static void fec_enet_timeout_work(struct work_struct *work) 120954309fa6SFrank Li { 121054309fa6SFrank Li struct fec_enet_private *fep = 121136cdc743SRussell King container_of(work, struct fec_enet_private, tx_timeout_work); 12128ce5624fSRussell King struct net_device *ndev = fep->netdev; 121354309fa6SFrank Li 1214da1774e5SRussell King rtnl_lock(); 12158ce5624fSRussell King if (netif_device_present(ndev) || netif_running(ndev)) { 1216dbc64a8eSRussell King napi_disable(&fep->napi); 1217dbc64a8eSRussell King netif_tx_lock_bh(ndev); 1218ef83337dSRussell King fec_restart(ndev); 1219657ade07SRickard x Andersson netif_tx_wake_all_queues(ndev); 12206af42d42SRussell King netif_tx_unlock_bh(ndev); 1221dbc64a8eSRussell King napi_enable(&fep->napi); 12228ce5624fSRussell King } 1223da1774e5SRussell King rtnl_unlock(); 122454309fa6SFrank Li } 1225793fc096SFrank Li 1226793fc096SFrank Li static void 1227bfd4ecddSRussell King fec_enet_hwtstamp(struct fec_enet_private *fep, unsigned ts, 1228bfd4ecddSRussell King struct skb_shared_hwtstamps *hwtstamps) 1229bfd4ecddSRussell King { 1230bfd4ecddSRussell King unsigned long flags; 1231bfd4ecddSRussell King u64 ns; 1232bfd4ecddSRussell King 1233bfd4ecddSRussell King spin_lock_irqsave(&fep->tmreg_lock, flags); 1234bfd4ecddSRussell King ns = timecounter_cyc2time(&fep->tc, ts); 1235bfd4ecddSRussell King spin_unlock_irqrestore(&fep->tmreg_lock, flags); 1236bfd4ecddSRussell King 1237bfd4ecddSRussell King memset(hwtstamps, 0, sizeof(*hwtstamps)); 1238bfd4ecddSRussell King hwtstamps->hwtstamp = ns_to_ktime(ns); 1239bfd4ecddSRussell King } 1240bfd4ecddSRussell King 1241bfd4ecddSRussell King static void 12424d494cdcSFugang Duan fec_enet_tx_queue(struct net_device *ndev, u16 queue_id) 1243793fc096SFrank Li { 1244793fc096SFrank Li struct fec_enet_private *fep; 1245a2fe37b6SFabio Estevam struct bufdesc *bdp; 1246793fc096SFrank Li unsigned short status; 1247793fc096SFrank Li struct sk_buff *skb; 12484d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 12494d494cdcSFugang Duan struct netdev_queue *nq; 1250793fc096SFrank Li int index = 0; 125179f33912SNimrod Andy int entries_free; 1252793fc096SFrank Li 1253793fc096SFrank Li fep = netdev_priv(ndev); 12544d494cdcSFugang Duan 12554d494cdcSFugang Duan txq = fep->tx_queue[queue_id]; 12564d494cdcSFugang Duan /* get next bdp of dirty_tx */ 12574d494cdcSFugang Duan nq = netdev_get_tx_queue(ndev, queue_id); 12584d494cdcSFugang Duan bdp = txq->dirty_tx; 1259793fc096SFrank Li 1260793fc096SFrank Li /* get next bdp of dirty_tx */ 12617355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 1262793fc096SFrank Li 12637355f276STroy Kisky while (bdp != READ_ONCE(txq->bd.cur)) { 12647355f276STroy Kisky /* Order the load of bd.cur and cbd_sc */ 1265c4bc44c6SKevin Hao rmb(); 12665cfa3039SJohannes Berg status = fec16_to_cpu(READ_ONCE(bdp->cbd_sc)); 1267c4bc44c6SKevin Hao if (status & BD_ENET_TX_READY) 1268793fc096SFrank Li break; 1269793fc096SFrank Li 12707355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 12712b995f63SNimrod Andy 1272a2fe37b6SFabio Estevam skb = txq->tx_skbuff[index]; 1273a2fe37b6SFabio Estevam txq->tx_skbuff[index] = NULL; 12745cfa3039SJohannes Berg if (!IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr))) 12755cfa3039SJohannes Berg dma_unmap_single(&fep->pdev->dev, 12765cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 12775cfa3039SJohannes Berg fec16_to_cpu(bdp->cbd_datlen), 12785cfa3039SJohannes Berg DMA_TO_DEVICE); 12795cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(0); 12807fafe803STroy Kisky if (!skb) 12817fafe803STroy Kisky goto skb_done; 1282793fc096SFrank Li 1283793fc096SFrank Li /* Check for errors. */ 1284793fc096SFrank Li if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC | 1285793fc096SFrank Li BD_ENET_TX_RL | BD_ENET_TX_UN | 1286793fc096SFrank Li BD_ENET_TX_CSL)) { 1287793fc096SFrank Li ndev->stats.tx_errors++; 1288793fc096SFrank Li if (status & BD_ENET_TX_HB) /* No heartbeat */ 1289793fc096SFrank Li ndev->stats.tx_heartbeat_errors++; 1290793fc096SFrank Li if (status & BD_ENET_TX_LC) /* Late collision */ 1291793fc096SFrank Li ndev->stats.tx_window_errors++; 1292793fc096SFrank Li if (status & BD_ENET_TX_RL) /* Retrans limit */ 1293793fc096SFrank Li ndev->stats.tx_aborted_errors++; 1294793fc096SFrank Li if (status & BD_ENET_TX_UN) /* Underrun */ 1295793fc096SFrank Li ndev->stats.tx_fifo_errors++; 1296793fc096SFrank Li if (status & BD_ENET_TX_CSL) /* Carrier lost */ 1297793fc096SFrank Li ndev->stats.tx_carrier_errors++; 1298793fc096SFrank Li } else { 1299793fc096SFrank Li ndev->stats.tx_packets++; 13006e909283SNimrod Andy ndev->stats.tx_bytes += skb->len; 1301793fc096SFrank Li } 1302793fc096SFrank Li 130334074639SSergey Organov /* NOTE: SKBTX_IN_PROGRESS being set does not imply it's we who 130434074639SSergey Organov * are to time stamp the packet, so we still need to check time 130534074639SSergey Organov * stamping enabled flag. 130634074639SSergey Organov */ 130734074639SSergey Organov if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS && 130834074639SSergey Organov fep->hwts_tx_en) && 1309793fc096SFrank Li fep->bufdesc_ex) { 1310793fc096SFrank Li struct skb_shared_hwtstamps shhwtstamps; 1311793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 1312793fc096SFrank Li 13135cfa3039SJohannes Berg fec_enet_hwtstamp(fep, fec32_to_cpu(ebdp->ts), &shhwtstamps); 1314793fc096SFrank Li skb_tstamp_tx(skb, &shhwtstamps); 1315793fc096SFrank Li } 1316793fc096SFrank Li 1317793fc096SFrank Li /* Deferred means some collisions occurred during transmit, 1318793fc096SFrank Li * but we eventually sent the packet OK. 1319793fc096SFrank Li */ 1320793fc096SFrank Li if (status & BD_ENET_TX_DEF) 1321793fc096SFrank Li ndev->stats.collisions++; 1322793fc096SFrank Li 1323793fc096SFrank Li /* Free the sk buffer associated with this last transmit */ 1324793fc096SFrank Li dev_kfree_skb_any(skb); 13257fafe803STroy Kisky skb_done: 1326c4bc44c6SKevin Hao /* Make sure the update to bdp and tx_skbuff are performed 1327c4bc44c6SKevin Hao * before dirty_tx 1328c4bc44c6SKevin Hao */ 1329c4bc44c6SKevin Hao wmb(); 13304d494cdcSFugang Duan txq->dirty_tx = bdp; 1331793fc096SFrank Li 1332793fc096SFrank Li /* Update pointer to next buffer descriptor to be transmitted */ 13337355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 1334793fc096SFrank Li 1335793fc096SFrank Li /* Since we have freed up a buffer, the ring is no longer full 1336793fc096SFrank Li */ 1337657ade07SRickard x Andersson if (netif_tx_queue_stopped(nq)) { 13387355f276STroy Kisky entries_free = fec_enet_get_free_txdesc_num(txq); 13394d494cdcSFugang Duan if (entries_free >= txq->tx_wake_threshold) 13404d494cdcSFugang Duan netif_tx_wake_queue(nq); 1341793fc096SFrank Li } 134279f33912SNimrod Andy } 1343ccea2968SRussell King 1344c10bc0e7SFugang Duan /* ERR006358: Keep the transmitter going */ 13457355f276STroy Kisky if (bdp != txq->bd.cur && 134653bb20d1STroy Kisky readl(txq->bd.reg_desc_active) == 0) 134753bb20d1STroy Kisky writel(0, txq->bd.reg_desc_active); 13484d494cdcSFugang Duan } 13494d494cdcSFugang Duan 13507cdaa4ccSTobias Waldekranz static void fec_enet_tx(struct net_device *ndev) 13514d494cdcSFugang Duan { 13524d494cdcSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 13537cdaa4ccSTobias Waldekranz int i; 13547cdaa4ccSTobias Waldekranz 13557cdaa4ccSTobias Waldekranz /* Make sure that AVB queues are processed first. */ 13567cdaa4ccSTobias Waldekranz for (i = fep->num_tx_queues - 1; i >= 0; i--) 13577cdaa4ccSTobias Waldekranz fec_enet_tx_queue(ndev, i); 1358793fc096SFrank Li } 1359793fc096SFrank Li 13601b7bde6dSNimrod Andy static int 13611b7bde6dSNimrod Andy fec_enet_new_rxbdp(struct net_device *ndev, struct bufdesc *bdp, struct sk_buff *skb) 13621b7bde6dSNimrod Andy { 13631b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 13641b7bde6dSNimrod Andy int off; 13651b7bde6dSNimrod Andy 13661b7bde6dSNimrod Andy off = ((unsigned long)skb->data) & fep->rx_align; 13671b7bde6dSNimrod Andy if (off) 13681b7bde6dSNimrod Andy skb_reserve(skb, fep->rx_align + 1 - off); 13691b7bde6dSNimrod Andy 13705cfa3039SJohannes 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)); 13715cfa3039SJohannes Berg if (dma_mapping_error(&fep->pdev->dev, fec32_to_cpu(bdp->cbd_bufaddr))) { 13721b7bde6dSNimrod Andy if (net_ratelimit()) 13731b7bde6dSNimrod Andy netdev_err(ndev, "Rx DMA memory map failed\n"); 13741b7bde6dSNimrod Andy return -ENOMEM; 13751b7bde6dSNimrod Andy } 13761b7bde6dSNimrod Andy 13771b7bde6dSNimrod Andy return 0; 13781b7bde6dSNimrod Andy } 13791b7bde6dSNimrod Andy 13801b7bde6dSNimrod Andy static bool fec_enet_copybreak(struct net_device *ndev, struct sk_buff **skb, 13811310b544SLothar Waßmann struct bufdesc *bdp, u32 length, bool swap) 13821b7bde6dSNimrod Andy { 13831b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 13841b7bde6dSNimrod Andy struct sk_buff *new_skb; 13851b7bde6dSNimrod Andy 13861b7bde6dSNimrod Andy if (length > fep->rx_copybreak) 13871b7bde6dSNimrod Andy return false; 13881b7bde6dSNimrod Andy 13891b7bde6dSNimrod Andy new_skb = netdev_alloc_skb(ndev, length); 13901b7bde6dSNimrod Andy if (!new_skb) 13911b7bde6dSNimrod Andy return false; 13921b7bde6dSNimrod Andy 13935cfa3039SJohannes Berg dma_sync_single_for_cpu(&fep->pdev->dev, 13945cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 13951b7bde6dSNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 13961b7bde6dSNimrod Andy DMA_FROM_DEVICE); 13971310b544SLothar Waßmann if (!swap) 13981b7bde6dSNimrod Andy memcpy(new_skb->data, (*skb)->data, length); 13991310b544SLothar Waßmann else 14001310b544SLothar Waßmann swap_buffer2(new_skb->data, (*skb)->data, length); 14011b7bde6dSNimrod Andy *skb = new_skb; 14021b7bde6dSNimrod Andy 14031b7bde6dSNimrod Andy return true; 14041b7bde6dSNimrod Andy } 14051b7bde6dSNimrod Andy 14067355f276STroy Kisky /* During a receive, the bd_rx.cur points to the current incoming buffer. 1407793fc096SFrank Li * When we update through the ring, if the next incoming buffer has 1408793fc096SFrank Li * not been given to the system, we just set the empty indicator, 1409793fc096SFrank Li * effectively tossing the packet. 1410793fc096SFrank Li */ 1411793fc096SFrank Li static int 14124d494cdcSFugang Duan fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) 1413793fc096SFrank Li { 1414793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 14154d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 1416793fc096SFrank Li struct bufdesc *bdp; 1417793fc096SFrank Li unsigned short status; 14181b7bde6dSNimrod Andy struct sk_buff *skb_new = NULL; 1419793fc096SFrank Li struct sk_buff *skb; 1420793fc096SFrank Li ushort pkt_len; 1421793fc096SFrank Li __u8 *data; 1422793fc096SFrank Li int pkt_received = 0; 1423cdffcf1bSJim Baxter struct bufdesc_ex *ebdp = NULL; 1424cdffcf1bSJim Baxter bool vlan_packet_rcvd = false; 1425cdffcf1bSJim Baxter u16 vlan_tag; 1426d842a31fSDuan Fugang-B38611 int index = 0; 14271b7bde6dSNimrod Andy bool is_copybreak; 14286b7e4008SLothar Waßmann bool need_swap = fep->quirks & FEC_QUIRK_SWAP_FRAME; 1429793fc096SFrank Li 1430793fc096SFrank Li #ifdef CONFIG_M532x 1431793fc096SFrank Li flush_cache_all(); 1432793fc096SFrank Li #endif 14334d494cdcSFugang Duan rxq = fep->rx_queue[queue_id]; 1434793fc096SFrank Li 1435793fc096SFrank Li /* First, grab all of the stats for the incoming packet. 1436793fc096SFrank Li * These get messed up if we get called due to a busy condition. 1437793fc096SFrank Li */ 14387355f276STroy Kisky bdp = rxq->bd.cur; 1439793fc096SFrank Li 14405cfa3039SJohannes Berg while (!((status = fec16_to_cpu(bdp->cbd_sc)) & BD_ENET_RX_EMPTY)) { 1441793fc096SFrank Li 1442793fc096SFrank Li if (pkt_received >= budget) 1443793fc096SFrank Li break; 1444793fc096SFrank Li pkt_received++; 1445793fc096SFrank Li 1446ed63f1dcSRussell King writel(FEC_ENET_RXF, fep->hwp + FEC_IEVENT); 1447db3421c1SRussell King 1448793fc096SFrank Li /* Check for errors. */ 1449095098e1STroy Kisky status ^= BD_ENET_RX_LAST; 1450793fc096SFrank Li if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | 1451095098e1STroy Kisky BD_ENET_RX_CR | BD_ENET_RX_OV | BD_ENET_RX_LAST | 1452095098e1STroy Kisky BD_ENET_RX_CL)) { 1453793fc096SFrank Li ndev->stats.rx_errors++; 1454095098e1STroy Kisky if (status & BD_ENET_RX_OV) { 1455095098e1STroy Kisky /* FIFO overrun */ 1456095098e1STroy Kisky ndev->stats.rx_fifo_errors++; 1457095098e1STroy Kisky goto rx_processing_done; 1458095098e1STroy Kisky } 1459095098e1STroy Kisky if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH 1460095098e1STroy Kisky | BD_ENET_RX_LAST)) { 1461793fc096SFrank Li /* Frame too long or too short. */ 1462793fc096SFrank Li ndev->stats.rx_length_errors++; 1463095098e1STroy Kisky if (status & BD_ENET_RX_LAST) 1464095098e1STroy Kisky netdev_err(ndev, "rcv is not +last\n"); 1465793fc096SFrank Li } 1466793fc096SFrank Li if (status & BD_ENET_RX_CR) /* CRC Error */ 1467793fc096SFrank Li ndev->stats.rx_crc_errors++; 1468095098e1STroy Kisky /* Report late collisions as a frame error. */ 1469095098e1STroy Kisky if (status & (BD_ENET_RX_NO | BD_ENET_RX_CL)) 1470793fc096SFrank Li ndev->stats.rx_frame_errors++; 1471793fc096SFrank Li goto rx_processing_done; 1472793fc096SFrank Li } 1473793fc096SFrank Li 1474793fc096SFrank Li /* Process the incoming frame. */ 1475793fc096SFrank Li ndev->stats.rx_packets++; 14765cfa3039SJohannes Berg pkt_len = fec16_to_cpu(bdp->cbd_datlen); 1477793fc096SFrank Li ndev->stats.rx_bytes += pkt_len; 1478793fc096SFrank Li 14797355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &rxq->bd); 14801b7bde6dSNimrod Andy skb = rxq->rx_skbuff[index]; 14811b7bde6dSNimrod Andy 14821b7bde6dSNimrod Andy /* The packet length includes FCS, but we don't want to 14831b7bde6dSNimrod Andy * include that when passing upstream as it messes up 14841b7bde6dSNimrod Andy * bridging applications. 14851b7bde6dSNimrod Andy */ 14861310b544SLothar Waßmann is_copybreak = fec_enet_copybreak(ndev, &skb, bdp, pkt_len - 4, 14871310b544SLothar Waßmann need_swap); 14881b7bde6dSNimrod Andy if (!is_copybreak) { 14891b7bde6dSNimrod Andy skb_new = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE); 14901b7bde6dSNimrod Andy if (unlikely(!skb_new)) { 14911b7bde6dSNimrod Andy ndev->stats.rx_dropped++; 14921b7bde6dSNimrod Andy goto rx_processing_done; 14931b7bde6dSNimrod Andy } 14945cfa3039SJohannes Berg dma_unmap_single(&fep->pdev->dev, 14955cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 1496b64bf4b7SNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 1497b64bf4b7SNimrod Andy DMA_FROM_DEVICE); 14981b7bde6dSNimrod Andy } 1499793fc096SFrank Li 15001b7bde6dSNimrod Andy prefetch(skb->data - NET_IP_ALIGN); 15011b7bde6dSNimrod Andy skb_put(skb, pkt_len - 4); 15021b7bde6dSNimrod Andy data = skb->data; 15033ac72b7bSEric Nelson 1504235bde1eSFabio Estevam if (!is_copybreak && need_swap) 1505235bde1eSFabio Estevam swap_buffer(data, pkt_len); 1506235bde1eSFabio Estevam 15073ac72b7bSEric Nelson #if !defined(CONFIG_M5272) 15083ac72b7bSEric Nelson if (fep->quirks & FEC_QUIRK_HAS_RACC) 15093ac72b7bSEric Nelson data = skb_pull_inline(skb, 2); 15103ac72b7bSEric Nelson #endif 15113ac72b7bSEric Nelson 1512cdffcf1bSJim Baxter /* Extract the enhanced buffer descriptor */ 1513cdffcf1bSJim Baxter ebdp = NULL; 1514cdffcf1bSJim Baxter if (fep->bufdesc_ex) 1515cdffcf1bSJim Baxter ebdp = (struct bufdesc_ex *)bdp; 1516cdffcf1bSJim Baxter 1517cdffcf1bSJim Baxter /* If this is a VLAN packet remove the VLAN Tag */ 1518cdffcf1bSJim Baxter vlan_packet_rcvd = false; 1519cdffcf1bSJim Baxter if ((ndev->features & NETIF_F_HW_VLAN_CTAG_RX) && 15205cfa3039SJohannes Berg fep->bufdesc_ex && 15215cfa3039SJohannes Berg (ebdp->cbd_esc & cpu_to_fec32(BD_ENET_RX_VLAN))) { 1522cdffcf1bSJim Baxter /* Push and remove the vlan tag */ 1523cdffcf1bSJim Baxter struct vlan_hdr *vlan_header = 1524cdffcf1bSJim Baxter (struct vlan_hdr *) (data + ETH_HLEN); 1525cdffcf1bSJim Baxter vlan_tag = ntohs(vlan_header->h_vlan_TCI); 1526cdffcf1bSJim Baxter 1527cdffcf1bSJim Baxter vlan_packet_rcvd = true; 15281b7bde6dSNimrod Andy 1529af5cbc98SNimrod Andy memmove(skb->data + VLAN_HLEN, data, ETH_ALEN * 2); 15301b7bde6dSNimrod Andy skb_pull(skb, VLAN_HLEN); 1531cdffcf1bSJim Baxter } 1532cdffcf1bSJim Baxter 1533793fc096SFrank Li skb->protocol = eth_type_trans(skb, ndev); 1534793fc096SFrank Li 1535793fc096SFrank Li /* Get receive timestamp from the skb */ 1536bfd4ecddSRussell King if (fep->hwts_rx_en && fep->bufdesc_ex) 15375cfa3039SJohannes Berg fec_enet_hwtstamp(fep, fec32_to_cpu(ebdp->ts), 1538bfd4ecddSRussell King skb_hwtstamps(skb)); 1539793fc096SFrank Li 15404c09eed9SJim Baxter if (fep->bufdesc_ex && 15414c09eed9SJim Baxter (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { 15425cfa3039SJohannes Berg if (!(ebdp->cbd_esc & cpu_to_fec32(FLAG_RX_CSUM_ERROR))) { 15434c09eed9SJim Baxter /* don't check it */ 15444c09eed9SJim Baxter skb->ip_summed = CHECKSUM_UNNECESSARY; 15454c09eed9SJim Baxter } else { 15464c09eed9SJim Baxter skb_checksum_none_assert(skb); 15474c09eed9SJim Baxter } 15484c09eed9SJim Baxter } 15494c09eed9SJim Baxter 1550cdffcf1bSJim Baxter /* Handle received VLAN packets */ 1551cdffcf1bSJim Baxter if (vlan_packet_rcvd) 1552cdffcf1bSJim Baxter __vlan_hwaccel_put_tag(skb, 1553cdffcf1bSJim Baxter htons(ETH_P_8021Q), 1554cdffcf1bSJim Baxter vlan_tag); 1555cdffcf1bSJim Baxter 15567cdaa4ccSTobias Waldekranz skb_record_rx_queue(skb, queue_id); 1557793fc096SFrank Li napi_gro_receive(&fep->napi, skb); 1558793fc096SFrank Li 15591b7bde6dSNimrod Andy if (is_copybreak) { 15605cfa3039SJohannes Berg dma_sync_single_for_device(&fep->pdev->dev, 15615cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 1562b64bf4b7SNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 1563b64bf4b7SNimrod Andy DMA_FROM_DEVICE); 15641b7bde6dSNimrod Andy } else { 15651b7bde6dSNimrod Andy rxq->rx_skbuff[index] = skb_new; 15661b7bde6dSNimrod Andy fec_enet_new_rxbdp(ndev, bdp, skb_new); 15671b7bde6dSNimrod Andy } 15681b7bde6dSNimrod Andy 1569793fc096SFrank Li rx_processing_done: 1570793fc096SFrank Li /* Clear the status flags for this buffer */ 1571793fc096SFrank Li status &= ~BD_ENET_RX_STATS; 1572793fc096SFrank Li 1573793fc096SFrank Li /* Mark the buffer empty */ 1574793fc096SFrank Li status |= BD_ENET_RX_EMPTY; 1575793fc096SFrank Li 1576793fc096SFrank Li if (fep->bufdesc_ex) { 1577793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 1578793fc096SFrank Li 15795cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(BD_ENET_RX_INT); 1580793fc096SFrank Li ebdp->cbd_prot = 0; 1581793fc096SFrank Li ebdp->cbd_bdu = 0; 1582793fc096SFrank Li } 1583be293467STroy Kisky /* Make sure the updates to rest of the descriptor are 1584be293467STroy Kisky * performed before transferring ownership. 1585be293467STroy Kisky */ 1586be293467STroy Kisky wmb(); 1587be293467STroy Kisky bdp->cbd_sc = cpu_to_fec16(status); 1588793fc096SFrank Li 1589793fc096SFrank Li /* Update BD pointer to next entry */ 15907355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 159136e24e2eSDuan Fugang-B38611 1592793fc096SFrank Li /* Doing this here will keep the FEC running while we process 1593793fc096SFrank Li * incoming frames. On a heavily loaded network, we should be 1594793fc096SFrank Li * able to keep up at the expense of system resources. 1595793fc096SFrank Li */ 159653bb20d1STroy Kisky writel(0, rxq->bd.reg_desc_active); 1597793fc096SFrank Li } 15987355f276STroy Kisky rxq->bd.cur = bdp; 1599793fc096SFrank Li return pkt_received; 1600793fc096SFrank Li } 1601793fc096SFrank Li 16027cdaa4ccSTobias Waldekranz static int fec_enet_rx(struct net_device *ndev, int budget) 16034d494cdcSFugang Duan { 16044d494cdcSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 16057cdaa4ccSTobias Waldekranz int i, done = 0; 16064d494cdcSFugang Duan 16077cdaa4ccSTobias Waldekranz /* Make sure that AVB queues are processed first. */ 16087cdaa4ccSTobias Waldekranz for (i = fep->num_rx_queues - 1; i >= 0; i--) 16097cdaa4ccSTobias Waldekranz done += fec_enet_rx_queue(ndev, budget - done, i); 16101c021bb7SUwe Kleine-König 16117cdaa4ccSTobias Waldekranz return done; 16124d494cdcSFugang Duan } 16134d494cdcSFugang Duan 16147cdaa4ccSTobias Waldekranz static bool fec_enet_collect_events(struct fec_enet_private *fep) 16154d494cdcSFugang Duan { 1616793fc096SFrank Li uint int_events; 1617793fc096SFrank Li 1618793fc096SFrank Li int_events = readl(fep->hwp + FEC_IEVENT); 1619f166f890SAndrew Lunn 1620f166f890SAndrew Lunn /* Don't clear MDIO events, we poll for those */ 1621f166f890SAndrew Lunn int_events &= ~FEC_ENET_MII; 1622f166f890SAndrew Lunn 162394191fd6SNimrod Andy writel(int_events, fep->hwp + FEC_IEVENT); 1624793fc096SFrank Li 16257cdaa4ccSTobias Waldekranz return int_events != 0; 16267cdaa4ccSTobias Waldekranz } 16277cdaa4ccSTobias Waldekranz 16287cdaa4ccSTobias Waldekranz static irqreturn_t 16297cdaa4ccSTobias Waldekranz fec_enet_interrupt(int irq, void *dev_id) 16307cdaa4ccSTobias Waldekranz { 16317cdaa4ccSTobias Waldekranz struct net_device *ndev = dev_id; 16327cdaa4ccSTobias Waldekranz struct fec_enet_private *fep = netdev_priv(ndev); 16337cdaa4ccSTobias Waldekranz irqreturn_t ret = IRQ_NONE; 16347cdaa4ccSTobias Waldekranz 16357cdaa4ccSTobias Waldekranz if (fec_enet_collect_events(fep) && fep->link) { 1636793fc096SFrank Li ret = IRQ_HANDLED; 1637793fc096SFrank Li 163894191fd6SNimrod Andy if (napi_schedule_prep(&fep->napi)) { 1639f166f890SAndrew Lunn /* Disable interrupts */ 1640f166f890SAndrew Lunn writel(0, fep->hwp + FEC_IMASK); 164194191fd6SNimrod Andy __napi_schedule(&fep->napi); 164294191fd6SNimrod Andy } 1643793fc096SFrank Li } 1644793fc096SFrank Li 1645793fc096SFrank Li return ret; 1646793fc096SFrank Li } 1647793fc096SFrank Li 1648793fc096SFrank Li static int fec_enet_rx_napi(struct napi_struct *napi, int budget) 1649793fc096SFrank Li { 1650793fc096SFrank Li struct net_device *ndev = napi->dev; 1651793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 16527cdaa4ccSTobias Waldekranz int done = 0; 16537a16807cSRussell King 16547cdaa4ccSTobias Waldekranz do { 16557cdaa4ccSTobias Waldekranz done += fec_enet_rx(ndev, budget - done); 1656793fc096SFrank Li fec_enet_tx(ndev); 16577cdaa4ccSTobias Waldekranz } while ((done < budget) && fec_enet_collect_events(fep)); 1658793fc096SFrank Li 16597cdaa4ccSTobias Waldekranz if (done < budget) { 16607cdaa4ccSTobias Waldekranz napi_complete_done(napi, done); 1661793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 1662793fc096SFrank Li } 16637cdaa4ccSTobias Waldekranz 16647cdaa4ccSTobias Waldekranz return done; 1665793fc096SFrank Li } 1666793fc096SFrank Li 1667793fc096SFrank Li /* ------------------------------------------------------------------------- */ 1668052fcc45SFugang Duan static int fec_get_mac(struct net_device *ndev) 1669793fc096SFrank Li { 1670793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1671793fc096SFrank Li unsigned char *iap, tmpaddr[ETH_ALEN]; 167283216e39SMichael Walle int ret; 1673793fc096SFrank Li 1674793fc096SFrank Li /* 1675793fc096SFrank Li * try to get mac address in following order: 1676793fc096SFrank Li * 1677793fc096SFrank Li * 1) module parameter via kernel command line in form 1678793fc096SFrank Li * fec.macaddr=0x00,0x04,0x9f,0x01,0x30,0xe0 1679793fc096SFrank Li */ 1680793fc096SFrank Li iap = macaddr; 1681793fc096SFrank Li 1682793fc096SFrank Li /* 1683793fc096SFrank Li * 2) from device tree data 1684793fc096SFrank Li */ 1685793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 1686793fc096SFrank Li struct device_node *np = fep->pdev->dev.of_node; 1687793fc096SFrank Li if (np) { 168883216e39SMichael Walle ret = of_get_mac_address(np, tmpaddr); 168983216e39SMichael Walle if (!ret) 169083216e39SMichael Walle iap = tmpaddr; 1691052fcc45SFugang Duan else if (ret == -EPROBE_DEFER) 1692052fcc45SFugang Duan return ret; 1693793fc096SFrank Li } 1694793fc096SFrank Li } 1695793fc096SFrank Li 1696793fc096SFrank Li /* 1697793fc096SFrank Li * 3) from flash or fuse (via platform data) 1698793fc096SFrank Li */ 1699793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 1700793fc096SFrank Li #ifdef CONFIG_M5272 1701793fc096SFrank Li if (FEC_FLASHMAC) 1702793fc096SFrank Li iap = (unsigned char *)FEC_FLASHMAC; 1703793fc096SFrank Li #else 170432d1bbb1SGeert Uytterhoeven struct fec_platform_data *pdata = dev_get_platdata(&fep->pdev->dev); 170532d1bbb1SGeert Uytterhoeven 1706793fc096SFrank Li if (pdata) 1707793fc096SFrank Li iap = (unsigned char *)&pdata->mac; 1708793fc096SFrank Li #endif 1709793fc096SFrank Li } 1710793fc096SFrank Li 1711793fc096SFrank Li /* 1712793fc096SFrank Li * 4) FEC mac registers set by bootloader 1713793fc096SFrank Li */ 1714793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 17157d7628f3SDan Carpenter *((__be32 *) &tmpaddr[0]) = 17167d7628f3SDan Carpenter cpu_to_be32(readl(fep->hwp + FEC_ADDR_LOW)); 17177d7628f3SDan Carpenter *((__be16 *) &tmpaddr[4]) = 17187d7628f3SDan Carpenter cpu_to_be16(readl(fep->hwp + FEC_ADDR_HIGH) >> 16); 1719793fc096SFrank Li iap = &tmpaddr[0]; 1720793fc096SFrank Li } 1721793fc096SFrank Li 1722ff5b2fabSLucas Stach /* 1723ff5b2fabSLucas Stach * 5) random mac address 1724ff5b2fabSLucas Stach */ 1725ff5b2fabSLucas Stach if (!is_valid_ether_addr(iap)) { 1726ff5b2fabSLucas Stach /* Report it and use a random ethernet address instead */ 1727a19a0582SFabio Estevam dev_err(&fep->pdev->dev, "Invalid MAC address: %pM\n", iap); 1728ff5b2fabSLucas Stach eth_hw_addr_random(ndev); 1729a19a0582SFabio Estevam dev_info(&fep->pdev->dev, "Using random MAC address: %pM\n", 1730ff5b2fabSLucas Stach ndev->dev_addr); 1731052fcc45SFugang Duan return 0; 1732ff5b2fabSLucas Stach } 1733ff5b2fabSLucas Stach 1734793fc096SFrank Li memcpy(ndev->dev_addr, iap, ETH_ALEN); 1735793fc096SFrank Li 1736793fc096SFrank Li /* Adjust MAC if using macaddr */ 1737793fc096SFrank Li if (iap == macaddr) 1738793fc096SFrank Li ndev->dev_addr[ETH_ALEN-1] = macaddr[ETH_ALEN-1] + fep->dev_id; 1739052fcc45SFugang Duan 1740052fcc45SFugang Duan return 0; 1741793fc096SFrank Li } 1742793fc096SFrank Li 1743793fc096SFrank Li /* ------------------------------------------------------------------------- */ 1744793fc096SFrank Li 1745793fc096SFrank Li /* 1746793fc096SFrank Li * Phy section 1747793fc096SFrank Li */ 1748793fc096SFrank Li static void fec_enet_adjust_link(struct net_device *ndev) 1749793fc096SFrank Li { 1750793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 175145f5c327SPhilippe Reynes struct phy_device *phy_dev = ndev->phydev; 1752793fc096SFrank Li int status_change = 0; 1753793fc096SFrank Li 17548ce5624fSRussell King /* 17558ce5624fSRussell King * If the netdev is down, or is going down, we're not interested 17568ce5624fSRussell King * in link state events, so just mark our idea of the link as down 17578ce5624fSRussell King * and ignore the event. 17588ce5624fSRussell King */ 17598ce5624fSRussell King if (!netif_running(ndev) || !netif_device_present(ndev)) { 17608ce5624fSRussell King fep->link = 0; 17618ce5624fSRussell King } else if (phy_dev->link) { 1762793fc096SFrank Li if (!fep->link) { 1763793fc096SFrank Li fep->link = phy_dev->link; 1764793fc096SFrank Li status_change = 1; 1765793fc096SFrank Li } 1766793fc096SFrank Li 1767ef83337dSRussell King if (fep->full_duplex != phy_dev->duplex) { 1768ef83337dSRussell King fep->full_duplex = phy_dev->duplex; 1769793fc096SFrank Li status_change = 1; 1770ef83337dSRussell King } 1771793fc096SFrank Li 1772793fc096SFrank Li if (phy_dev->speed != fep->speed) { 1773793fc096SFrank Li fep->speed = phy_dev->speed; 1774793fc096SFrank Li status_change = 1; 1775793fc096SFrank Li } 1776793fc096SFrank Li 1777793fc096SFrank Li /* if any of the above changed restart the FEC */ 1778dbc64a8eSRussell King if (status_change) { 1779dbc64a8eSRussell King napi_disable(&fep->napi); 1780dbc64a8eSRussell King netif_tx_lock_bh(ndev); 1781ef83337dSRussell King fec_restart(ndev); 1782657ade07SRickard x Andersson netif_tx_wake_all_queues(ndev); 17836af42d42SRussell King netif_tx_unlock_bh(ndev); 1784dbc64a8eSRussell King napi_enable(&fep->napi); 1785dbc64a8eSRussell King } 1786793fc096SFrank Li } else { 1787793fc096SFrank Li if (fep->link) { 1788f208ce10SRussell King napi_disable(&fep->napi); 1789f208ce10SRussell King netif_tx_lock_bh(ndev); 1790793fc096SFrank Li fec_stop(ndev); 1791f208ce10SRussell King netif_tx_unlock_bh(ndev); 1792f208ce10SRussell King napi_enable(&fep->napi); 17936e0895c2SDavid S. Miller fep->link = phy_dev->link; 1794793fc096SFrank Li status_change = 1; 1795793fc096SFrank Li } 1796793fc096SFrank Li } 1797793fc096SFrank Li 1798793fc096SFrank Li if (status_change) 1799793fc096SFrank Li phy_print_status(phy_dev); 1800793fc096SFrank Li } 1801793fc096SFrank Li 1802f166f890SAndrew Lunn static int fec_enet_mdio_wait(struct fec_enet_private *fep) 1803f166f890SAndrew Lunn { 1804f166f890SAndrew Lunn uint ievent; 1805f166f890SAndrew Lunn int ret; 1806f166f890SAndrew Lunn 1807f166f890SAndrew Lunn ret = readl_poll_timeout_atomic(fep->hwp + FEC_IEVENT, ievent, 1808f166f890SAndrew Lunn ievent & FEC_ENET_MII, 2, 30000); 1809f166f890SAndrew Lunn 1810f166f890SAndrew Lunn if (!ret) 1811f166f890SAndrew Lunn writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT); 1812f166f890SAndrew Lunn 1813f166f890SAndrew Lunn return ret; 1814f166f890SAndrew Lunn } 1815f166f890SAndrew Lunn 1816793fc096SFrank Li static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum) 1817793fc096SFrank Li { 1818793fc096SFrank Li struct fec_enet_private *fep = bus->priv; 18198fff755eSAndrew Lunn struct device *dev = &fep->pdev->dev; 1820d3ee8ec7SMarco Hartmann int ret = 0, frame_start, frame_addr, frame_op; 1821d3ee8ec7SMarco Hartmann bool is_c45 = !!(regnum & MII_ADDR_C45); 18228fff755eSAndrew Lunn 1823da875fa5SZhang Qilong ret = pm_runtime_resume_and_get(dev); 1824b0c6ce24SFabio Estevam if (ret < 0) 18258fff755eSAndrew Lunn return ret; 1826793fc096SFrank Li 1827d3ee8ec7SMarco Hartmann if (is_c45) { 1828d3ee8ec7SMarco Hartmann frame_start = FEC_MMFR_ST_C45; 1829d3ee8ec7SMarco Hartmann 1830d3ee8ec7SMarco Hartmann /* write address */ 1831d3ee8ec7SMarco Hartmann frame_addr = (regnum >> 16); 1832d3ee8ec7SMarco Hartmann writel(frame_start | FEC_MMFR_OP_ADDR_WRITE | 1833d3ee8ec7SMarco Hartmann FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | 1834d3ee8ec7SMarco Hartmann FEC_MMFR_TA | (regnum & 0xFFFF), 1835d3ee8ec7SMarco Hartmann fep->hwp + FEC_MII_DATA); 1836d3ee8ec7SMarco Hartmann 1837d3ee8ec7SMarco Hartmann /* wait for end of transfer */ 1838f166f890SAndrew Lunn ret = fec_enet_mdio_wait(fep); 1839f166f890SAndrew Lunn if (ret) { 1840d3ee8ec7SMarco Hartmann netdev_err(fep->netdev, "MDIO address write timeout\n"); 1841d3ee8ec7SMarco Hartmann goto out; 1842d3ee8ec7SMarco Hartmann } 1843d3ee8ec7SMarco Hartmann 1844d3ee8ec7SMarco Hartmann frame_op = FEC_MMFR_OP_READ_C45; 1845d3ee8ec7SMarco Hartmann 1846d3ee8ec7SMarco Hartmann } else { 1847d3ee8ec7SMarco Hartmann /* C22 read */ 1848d3ee8ec7SMarco Hartmann frame_op = FEC_MMFR_OP_READ; 1849d3ee8ec7SMarco Hartmann frame_start = FEC_MMFR_ST; 1850d3ee8ec7SMarco Hartmann frame_addr = regnum; 1851d3ee8ec7SMarco Hartmann } 1852d3ee8ec7SMarco Hartmann 1853793fc096SFrank Li /* start a read op */ 1854d3ee8ec7SMarco Hartmann writel(frame_start | frame_op | 1855d3ee8ec7SMarco Hartmann FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | 1856793fc096SFrank Li FEC_MMFR_TA, fep->hwp + FEC_MII_DATA); 1857793fc096SFrank Li 1858793fc096SFrank Li /* wait for end of transfer */ 1859f166f890SAndrew Lunn ret = fec_enet_mdio_wait(fep); 1860f166f890SAndrew Lunn if (ret) { 186131b7720cSJoe Perches netdev_err(fep->netdev, "MDIO read timeout\n"); 18628fff755eSAndrew Lunn goto out; 1863793fc096SFrank Li } 1864793fc096SFrank Li 18658fff755eSAndrew Lunn ret = FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA)); 18668fff755eSAndrew Lunn 18678fff755eSAndrew Lunn out: 18688fff755eSAndrew Lunn pm_runtime_mark_last_busy(dev); 18698fff755eSAndrew Lunn pm_runtime_put_autosuspend(dev); 18708fff755eSAndrew Lunn 18718fff755eSAndrew Lunn return ret; 1872793fc096SFrank Li } 1873793fc096SFrank Li 1874793fc096SFrank Li static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum, 1875793fc096SFrank Li u16 value) 1876793fc096SFrank Li { 1877793fc096SFrank Li struct fec_enet_private *fep = bus->priv; 18788fff755eSAndrew Lunn struct device *dev = &fep->pdev->dev; 1879d3ee8ec7SMarco Hartmann int ret, frame_start, frame_addr; 1880d3ee8ec7SMarco Hartmann bool is_c45 = !!(regnum & MII_ADDR_C45); 18818fff755eSAndrew Lunn 1882da875fa5SZhang Qilong ret = pm_runtime_resume_and_get(dev); 1883b0c6ce24SFabio Estevam if (ret < 0) 18848fff755eSAndrew Lunn return ret; 1885793fc096SFrank Li 1886d3ee8ec7SMarco Hartmann if (is_c45) { 1887d3ee8ec7SMarco Hartmann frame_start = FEC_MMFR_ST_C45; 1888d3ee8ec7SMarco Hartmann 1889d3ee8ec7SMarco Hartmann /* write address */ 1890d3ee8ec7SMarco Hartmann frame_addr = (regnum >> 16); 1891d3ee8ec7SMarco Hartmann writel(frame_start | FEC_MMFR_OP_ADDR_WRITE | 1892d3ee8ec7SMarco Hartmann FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | 1893d3ee8ec7SMarco Hartmann FEC_MMFR_TA | (regnum & 0xFFFF), 1894d3ee8ec7SMarco Hartmann fep->hwp + FEC_MII_DATA); 1895d3ee8ec7SMarco Hartmann 1896d3ee8ec7SMarco Hartmann /* wait for end of transfer */ 1897f166f890SAndrew Lunn ret = fec_enet_mdio_wait(fep); 1898f166f890SAndrew Lunn if (ret) { 1899d3ee8ec7SMarco Hartmann netdev_err(fep->netdev, "MDIO address write timeout\n"); 1900d3ee8ec7SMarco Hartmann goto out; 1901d3ee8ec7SMarco Hartmann } 1902d3ee8ec7SMarco Hartmann } else { 1903d3ee8ec7SMarco Hartmann /* C22 write */ 1904d3ee8ec7SMarco Hartmann frame_start = FEC_MMFR_ST; 1905d3ee8ec7SMarco Hartmann frame_addr = regnum; 1906d3ee8ec7SMarco Hartmann } 1907d3ee8ec7SMarco Hartmann 1908793fc096SFrank Li /* start a write op */ 1909d3ee8ec7SMarco Hartmann writel(frame_start | FEC_MMFR_OP_WRITE | 1910d3ee8ec7SMarco Hartmann FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | 1911793fc096SFrank Li FEC_MMFR_TA | FEC_MMFR_DATA(value), 1912793fc096SFrank Li fep->hwp + FEC_MII_DATA); 1913793fc096SFrank Li 1914793fc096SFrank Li /* wait for end of transfer */ 1915f166f890SAndrew Lunn ret = fec_enet_mdio_wait(fep); 1916f166f890SAndrew Lunn if (ret) 191731b7720cSJoe Perches netdev_err(fep->netdev, "MDIO write timeout\n"); 1918793fc096SFrank Li 1919d3ee8ec7SMarco Hartmann out: 19208fff755eSAndrew Lunn pm_runtime_mark_last_busy(dev); 19218fff755eSAndrew Lunn pm_runtime_put_autosuspend(dev); 19228fff755eSAndrew Lunn 19238fff755eSAndrew Lunn return ret; 1924793fc096SFrank Li } 1925793fc096SFrank Li 192664a632daSMarek Vasut static void fec_enet_phy_reset_after_clk_enable(struct net_device *ndev) 192764a632daSMarek Vasut { 192864a632daSMarek Vasut struct fec_enet_private *fep = netdev_priv(ndev); 192964a632daSMarek Vasut struct phy_device *phy_dev = ndev->phydev; 193064a632daSMarek Vasut 193164a632daSMarek Vasut if (phy_dev) { 193264a632daSMarek Vasut phy_reset_after_clk_enable(phy_dev); 193364a632daSMarek Vasut } else if (fep->phy_node) { 193464a632daSMarek Vasut /* 193564a632daSMarek Vasut * If the PHY still is not bound to the MAC, but there is 193664a632daSMarek Vasut * OF PHY node and a matching PHY device instance already, 193764a632daSMarek Vasut * use the OF PHY node to obtain the PHY device instance, 193864a632daSMarek Vasut * and then use that PHY device instance when triggering 193964a632daSMarek Vasut * the PHY reset. 194064a632daSMarek Vasut */ 194164a632daSMarek Vasut phy_dev = of_phy_find_device(fep->phy_node); 194264a632daSMarek Vasut phy_reset_after_clk_enable(phy_dev); 194364a632daSMarek Vasut put_device(&phy_dev->mdio.dev); 194464a632daSMarek Vasut } 194564a632daSMarek Vasut } 194664a632daSMarek Vasut 1947e8fcfcd5SNimrod Andy static int fec_enet_clk_enable(struct net_device *ndev, bool enable) 1948e8fcfcd5SNimrod Andy { 1949e8fcfcd5SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 1950e8fcfcd5SNimrod Andy int ret; 1951e8fcfcd5SNimrod Andy 1952e8fcfcd5SNimrod Andy if (enable) { 1953e8fcfcd5SNimrod Andy ret = clk_prepare_enable(fep->clk_enet_out); 1954e8fcfcd5SNimrod Andy if (ret) 1955d7c3a206SAndy Duan return ret; 195601e5943aSUwe Kleine-König 1957e8fcfcd5SNimrod Andy if (fep->clk_ptp) { 195891c0d987SNimrod Andy mutex_lock(&fep->ptp_clk_mutex); 1959e8fcfcd5SNimrod Andy ret = clk_prepare_enable(fep->clk_ptp); 196091c0d987SNimrod Andy if (ret) { 196191c0d987SNimrod Andy mutex_unlock(&fep->ptp_clk_mutex); 1962e8fcfcd5SNimrod Andy goto failed_clk_ptp; 196391c0d987SNimrod Andy } else { 196491c0d987SNimrod Andy fep->ptp_clk_on = true; 196591c0d987SNimrod Andy } 196691c0d987SNimrod Andy mutex_unlock(&fep->ptp_clk_mutex); 1967e8fcfcd5SNimrod Andy } 196801e5943aSUwe Kleine-König 19699b5330edSFugang Duan ret = clk_prepare_enable(fep->clk_ref); 19709b5330edSFugang Duan if (ret) 19719b5330edSFugang Duan goto failed_clk_ref; 19721b0a83acSRichard Leitner 197364a632daSMarek Vasut fec_enet_phy_reset_after_clk_enable(ndev); 1974e8fcfcd5SNimrod Andy } else { 1975e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_enet_out); 197691c0d987SNimrod Andy if (fep->clk_ptp) { 197791c0d987SNimrod Andy mutex_lock(&fep->ptp_clk_mutex); 1978e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_ptp); 197991c0d987SNimrod Andy fep->ptp_clk_on = false; 198091c0d987SNimrod Andy mutex_unlock(&fep->ptp_clk_mutex); 198191c0d987SNimrod Andy } 19829b5330edSFugang Duan clk_disable_unprepare(fep->clk_ref); 1983e8fcfcd5SNimrod Andy } 1984e8fcfcd5SNimrod Andy 1985e8fcfcd5SNimrod Andy return 0; 19869b5330edSFugang Duan 19879b5330edSFugang Duan failed_clk_ref: 1988a74d19baSLiu Xiang if (fep->clk_ptp) { 1989a74d19baSLiu Xiang mutex_lock(&fep->ptp_clk_mutex); 1990a74d19baSLiu Xiang clk_disable_unprepare(fep->clk_ptp); 1991a74d19baSLiu Xiang fep->ptp_clk_on = false; 1992a74d19baSLiu Xiang mutex_unlock(&fep->ptp_clk_mutex); 1993a74d19baSLiu Xiang } 1994e8fcfcd5SNimrod Andy failed_clk_ptp: 1995e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_enet_out); 1996e8fcfcd5SNimrod Andy 1997e8fcfcd5SNimrod Andy return ret; 1998e8fcfcd5SNimrod Andy } 1999e8fcfcd5SNimrod Andy 2000793fc096SFrank Li static int fec_enet_mii_probe(struct net_device *ndev) 2001793fc096SFrank Li { 2002793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2003793fc096SFrank Li struct phy_device *phy_dev = NULL; 2004793fc096SFrank Li char mdio_bus_id[MII_BUS_ID_SIZE]; 2005793fc096SFrank Li char phy_name[MII_BUS_ID_SIZE + 3]; 2006793fc096SFrank Li int phy_id; 2007793fc096SFrank Li int dev_id = fep->dev_id; 2008793fc096SFrank Li 2009407066f8SUwe Kleine-König if (fep->phy_node) { 2010407066f8SUwe Kleine-König phy_dev = of_phy_connect(ndev, fep->phy_node, 2011407066f8SUwe Kleine-König &fec_enet_adjust_link, 0, 2012407066f8SUwe Kleine-König fep->phy_interface); 20139558df3aSAndrew Lunn if (!phy_dev) { 20149558df3aSAndrew Lunn netdev_err(ndev, "Unable to connect to phy\n"); 2015213a9922SNimrod Andy return -ENODEV; 20169558df3aSAndrew Lunn } 2017407066f8SUwe Kleine-König } else { 2018793fc096SFrank Li /* check for attached phy */ 2019793fc096SFrank Li for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) { 20207f854420SAndrew Lunn if (!mdiobus_is_registered_device(fep->mii_bus, phy_id)) 2021793fc096SFrank Li continue; 2022793fc096SFrank Li if (dev_id--) 2023793fc096SFrank Li continue; 2024949bdd20SRickard Strandqvist strlcpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE); 2025793fc096SFrank Li break; 2026793fc096SFrank Li } 2027793fc096SFrank Li 2028793fc096SFrank Li if (phy_id >= PHY_MAX_ADDR) { 202931b7720cSJoe Perches netdev_info(ndev, "no PHY, assuming direct connection to switch\n"); 2030949bdd20SRickard Strandqvist strlcpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE); 2031793fc096SFrank Li phy_id = 0; 2032793fc096SFrank Li } 2033793fc096SFrank Li 2034407066f8SUwe Kleine-König snprintf(phy_name, sizeof(phy_name), 2035407066f8SUwe Kleine-König PHY_ID_FMT, mdio_bus_id, phy_id); 2036793fc096SFrank Li phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, 2037793fc096SFrank Li fep->phy_interface); 2038407066f8SUwe Kleine-König } 2039407066f8SUwe Kleine-König 2040793fc096SFrank Li if (IS_ERR(phy_dev)) { 204131b7720cSJoe Perches netdev_err(ndev, "could not attach to PHY\n"); 2042793fc096SFrank Li return PTR_ERR(phy_dev); 2043793fc096SFrank Li } 2044793fc096SFrank Li 2045793fc096SFrank Li /* mask with MAC supported features */ 20466b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_GBIT) { 204758056c1eSAndrew Lunn phy_set_max_speed(phy_dev, 1000); 204841124fa6SAndrew Lunn phy_remove_link_mode(phy_dev, 204941124fa6SAndrew Lunn ETHTOOL_LINK_MODE_1000baseT_Half_BIT); 2050d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 2051c306ad36SAndrew Lunn phy_support_sym_pause(phy_dev); 2052d1391930SGuenter Roeck #endif 2053793fc096SFrank Li } 2054793fc096SFrank Li else 205558056c1eSAndrew Lunn phy_set_max_speed(phy_dev, 100); 2056793fc096SFrank Li 2057793fc096SFrank Li fep->link = 0; 2058793fc096SFrank Li fep->full_duplex = 0; 2059793fc096SFrank Li 2060557d5dc8SHeiner Kallweit phy_dev->mac_managed_pm = 1; 2061557d5dc8SHeiner Kallweit 20622220943aSAndrew Lunn phy_attached_info(phy_dev); 2063793fc096SFrank Li 2064793fc096SFrank Li return 0; 2065793fc096SFrank Li } 2066793fc096SFrank Li 2067793fc096SFrank Li static int fec_enet_mii_init(struct platform_device *pdev) 2068793fc096SFrank Li { 2069793fc096SFrank Li static struct mii_bus *fec0_mii_bus; 2070793fc096SFrank Li struct net_device *ndev = platform_get_drvdata(pdev); 2071793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 20723c01eb62SAndrew Lunn bool suppress_preamble = false; 2073407066f8SUwe Kleine-König struct device_node *node; 2074e7f4dc35SAndrew Lunn int err = -ENXIO; 207563c60732SUwe Kleine-König u32 mii_speed, holdtime; 20763e782985SAndrew Lunn u32 bus_freq; 2077793fc096SFrank Li 2078793fc096SFrank Li /* 20793d125f9cSStefan Agner * The i.MX28 dual fec interfaces are not equal. 2080793fc096SFrank Li * Here are the differences: 2081793fc096SFrank Li * 2082793fc096SFrank Li * - fec0 supports MII & RMII modes while fec1 only supports RMII 2083793fc096SFrank Li * - fec0 acts as the 1588 time master while fec1 is slave 2084793fc096SFrank Li * - external phys can only be configured by fec0 2085793fc096SFrank Li * 2086793fc096SFrank Li * That is to say fec1 can not work independently. It only works 2087793fc096SFrank Li * when fec0 is working. The reason behind this design is that the 2088793fc096SFrank Li * second interface is added primarily for Switch mode. 2089793fc096SFrank Li * 2090793fc096SFrank Li * Because of the last point above, both phys are attached on fec0 2091793fc096SFrank Li * mdio interface in board design, and need to be configured by 2092793fc096SFrank Li * fec0 mii_bus. 2093793fc096SFrank Li */ 20943d125f9cSStefan Agner if ((fep->quirks & FEC_QUIRK_SINGLE_MDIO) && fep->dev_id > 0) { 2095793fc096SFrank Li /* fec1 uses fec0 mii_bus */ 2096793fc096SFrank Li if (mii_cnt && fec0_mii_bus) { 2097793fc096SFrank Li fep->mii_bus = fec0_mii_bus; 2098793fc096SFrank Li mii_cnt++; 2099793fc096SFrank Li return 0; 2100793fc096SFrank Li } 2101793fc096SFrank Li return -ENOENT; 2102793fc096SFrank Li } 2103793fc096SFrank Li 21043e782985SAndrew Lunn bus_freq = 2500000; /* 2.5MHz by default */ 21053e782985SAndrew Lunn node = of_get_child_by_name(pdev->dev.of_node, "mdio"); 21063c01eb62SAndrew Lunn if (node) { 21073e782985SAndrew Lunn of_property_read_u32(node, "clock-frequency", &bus_freq); 21083c01eb62SAndrew Lunn suppress_preamble = of_property_read_bool(node, 21093c01eb62SAndrew Lunn "suppress-preamble"); 21103c01eb62SAndrew Lunn } 21113e782985SAndrew Lunn 2112793fc096SFrank Li /* 21133e782985SAndrew Lunn * Set MII speed (= clk_get_rate() / 2 * phy_speed) 2114793fc096SFrank Li * 2115793fc096SFrank Li * The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while 2116793fc096SFrank Li * for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'. The i.MX28 2117793fc096SFrank Li * Reference Manual has an error on this, and gets fixed on i.MX6Q 2118793fc096SFrank Li * document. 2119793fc096SFrank Li */ 21203e782985SAndrew Lunn mii_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), bus_freq * 2); 21216b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_ENET_MAC) 212263c60732SUwe Kleine-König mii_speed--; 212363c60732SUwe Kleine-König if (mii_speed > 63) { 212463c60732SUwe Kleine-König dev_err(&pdev->dev, 2125981a0547SPeter Meerwald-Stadler "fec clock (%lu) too fast to get right mii speed\n", 212663c60732SUwe Kleine-König clk_get_rate(fep->clk_ipg)); 212763c60732SUwe Kleine-König err = -EINVAL; 212863c60732SUwe Kleine-König goto err_out; 212963c60732SUwe Kleine-König } 213063c60732SUwe Kleine-König 213163c60732SUwe Kleine-König /* 213263c60732SUwe Kleine-König * The i.MX28 and i.MX6 types have another filed in the MSCR (aka 213363c60732SUwe Kleine-König * MII_SPEED) register that defines the MDIO output hold time. Earlier 213463c60732SUwe Kleine-König * versions are RAZ there, so just ignore the difference and write the 213563c60732SUwe Kleine-König * register always. 213663c60732SUwe Kleine-König * The minimal hold time according to IEE802.3 (clause 22) is 10 ns. 213763c60732SUwe Kleine-König * HOLDTIME + 1 is the number of clk cycles the fec is holding the 213863c60732SUwe Kleine-König * output. 213963c60732SUwe Kleine-König * The HOLDTIME bitfield takes values between 0 and 7 (inclusive). 214063c60732SUwe Kleine-König * Given that ceil(clkrate / 5000000) <= 64, the calculation for 214163c60732SUwe Kleine-König * holdtime cannot result in a value greater than 3. 214263c60732SUwe Kleine-König */ 214363c60732SUwe Kleine-König holdtime = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 100000000) - 1; 214463c60732SUwe Kleine-König 214563c60732SUwe Kleine-König fep->phy_speed = mii_speed << 1 | holdtime << 8; 214663c60732SUwe Kleine-König 21473c01eb62SAndrew Lunn if (suppress_preamble) 21483c01eb62SAndrew Lunn fep->phy_speed |= BIT(7); 21493c01eb62SAndrew Lunn 21501e6114f5SGreg Ungerer if (fep->quirks & FEC_QUIRK_CLEAR_SETUP_MII) { 2151f166f890SAndrew Lunn /* Clear MMFR to avoid to generate MII event by writing MSCR. 2152f166f890SAndrew Lunn * MII event generation condition: 2153f166f890SAndrew Lunn * - writing MSCR: 2154f166f890SAndrew Lunn * - mmfr[31:0]_not_zero & mscr[7:0]_is_zero & 2155f166f890SAndrew Lunn * mscr_reg_data_in[7:0] != 0 2156f166f890SAndrew Lunn * - writing MMFR: 2157f166f890SAndrew Lunn * - mscr[7:0]_not_zero 2158f166f890SAndrew Lunn */ 2159f166f890SAndrew Lunn writel(0, fep->hwp + FEC_MII_DATA); 21601e6114f5SGreg Ungerer } 2161f166f890SAndrew Lunn 2162793fc096SFrank Li writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 2163793fc096SFrank Li 2164f166f890SAndrew Lunn /* Clear any pending transaction complete indication */ 2165f166f890SAndrew Lunn writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT); 2166f166f890SAndrew Lunn 2167793fc096SFrank Li fep->mii_bus = mdiobus_alloc(); 2168793fc096SFrank Li if (fep->mii_bus == NULL) { 2169793fc096SFrank Li err = -ENOMEM; 2170793fc096SFrank Li goto err_out; 2171793fc096SFrank Li } 2172793fc096SFrank Li 2173793fc096SFrank Li fep->mii_bus->name = "fec_enet_mii_bus"; 2174793fc096SFrank Li fep->mii_bus->read = fec_enet_mdio_read; 2175793fc096SFrank Li fep->mii_bus->write = fec_enet_mdio_write; 2176793fc096SFrank Li snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", 2177793fc096SFrank Li pdev->name, fep->dev_id + 1); 2178793fc096SFrank Li fep->mii_bus->priv = fep; 2179793fc096SFrank Li fep->mii_bus->parent = &pdev->dev; 2180793fc096SFrank Li 2181407066f8SUwe Kleine-König err = of_mdiobus_register(fep->mii_bus, node); 2182407066f8SUwe Kleine-König if (err) 2183e7f4dc35SAndrew Lunn goto err_out_free_mdiobus; 21840607a2cdSPan Bian of_node_put(node); 2185793fc096SFrank Li 2186793fc096SFrank Li mii_cnt++; 2187793fc096SFrank Li 2188793fc096SFrank Li /* save fec0 mii_bus */ 21893d125f9cSStefan Agner if (fep->quirks & FEC_QUIRK_SINGLE_MDIO) 2190793fc096SFrank Li fec0_mii_bus = fep->mii_bus; 2191793fc096SFrank Li 2192793fc096SFrank Li return 0; 2193793fc096SFrank Li 2194793fc096SFrank Li err_out_free_mdiobus: 2195793fc096SFrank Li mdiobus_free(fep->mii_bus); 2196793fc096SFrank Li err_out: 21970607a2cdSPan Bian of_node_put(node); 2198793fc096SFrank Li return err; 2199793fc096SFrank Li } 2200793fc096SFrank Li 2201793fc096SFrank Li static void fec_enet_mii_remove(struct fec_enet_private *fep) 2202793fc096SFrank Li { 2203793fc096SFrank Li if (--mii_cnt == 0) { 2204793fc096SFrank Li mdiobus_unregister(fep->mii_bus); 2205793fc096SFrank Li mdiobus_free(fep->mii_bus); 2206793fc096SFrank Li } 2207793fc096SFrank Li } 2208793fc096SFrank Li 2209793fc096SFrank Li static void fec_enet_get_drvinfo(struct net_device *ndev, 2210793fc096SFrank Li struct ethtool_drvinfo *info) 2211793fc096SFrank Li { 2212793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2213793fc096SFrank Li 2214793fc096SFrank Li strlcpy(info->driver, fep->pdev->dev.driver->name, 2215793fc096SFrank Li sizeof(info->driver)); 2216793fc096SFrank Li strlcpy(info->bus_info, dev_name(&ndev->dev), sizeof(info->bus_info)); 2217793fc096SFrank Li } 2218793fc096SFrank Li 2219db65f35fSPhilippe Reynes static int fec_enet_get_regs_len(struct net_device *ndev) 2220db65f35fSPhilippe Reynes { 2221db65f35fSPhilippe Reynes struct fec_enet_private *fep = netdev_priv(ndev); 2222db65f35fSPhilippe Reynes struct resource *r; 2223db65f35fSPhilippe Reynes int s = 0; 2224db65f35fSPhilippe Reynes 2225db65f35fSPhilippe Reynes r = platform_get_resource(fep->pdev, IORESOURCE_MEM, 0); 2226db65f35fSPhilippe Reynes if (r) 2227db65f35fSPhilippe Reynes s = resource_size(r); 2228db65f35fSPhilippe Reynes 2229db65f35fSPhilippe Reynes return s; 2230db65f35fSPhilippe Reynes } 2231db65f35fSPhilippe Reynes 2232db65f35fSPhilippe Reynes /* List of registers that can be safety be read to dump them with ethtool */ 2233db65f35fSPhilippe Reynes #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ 22343f1dcc6aSLucas Stach defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ 223578cc6e7eSFlorian Fainelli defined(CONFIG_ARM64) || defined(CONFIG_COMPILE_TEST) 2236f9bcc9f3SVivien Didelot static __u32 fec_enet_register_version = 2; 2237db65f35fSPhilippe Reynes static u32 fec_enet_register_offset[] = { 2238db65f35fSPhilippe Reynes FEC_IEVENT, FEC_IMASK, FEC_R_DES_ACTIVE_0, FEC_X_DES_ACTIVE_0, 2239db65f35fSPhilippe Reynes FEC_ECNTRL, FEC_MII_DATA, FEC_MII_SPEED, FEC_MIB_CTRLSTAT, FEC_R_CNTRL, 2240db65f35fSPhilippe Reynes FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, FEC_OPD, FEC_TXIC0, FEC_TXIC1, 2241db65f35fSPhilippe Reynes FEC_TXIC2, FEC_RXIC0, FEC_RXIC1, FEC_RXIC2, FEC_HASH_TABLE_HIGH, 2242db65f35fSPhilippe Reynes FEC_HASH_TABLE_LOW, FEC_GRP_HASH_TABLE_HIGH, FEC_GRP_HASH_TABLE_LOW, 2243db65f35fSPhilippe Reynes FEC_X_WMRK, FEC_R_BOUND, FEC_R_FSTART, FEC_R_DES_START_1, 2244db65f35fSPhilippe Reynes FEC_X_DES_START_1, FEC_R_BUFF_SIZE_1, FEC_R_DES_START_2, 2245db65f35fSPhilippe Reynes FEC_X_DES_START_2, FEC_R_BUFF_SIZE_2, FEC_R_DES_START_0, 2246db65f35fSPhilippe Reynes FEC_X_DES_START_0, FEC_R_BUFF_SIZE_0, FEC_R_FIFO_RSFL, FEC_R_FIFO_RSEM, 2247db65f35fSPhilippe Reynes FEC_R_FIFO_RAEM, FEC_R_FIFO_RAFL, FEC_RACC, FEC_RCMR_1, FEC_RCMR_2, 2248db65f35fSPhilippe Reynes FEC_DMA_CFG_1, FEC_DMA_CFG_2, FEC_R_DES_ACTIVE_1, FEC_X_DES_ACTIVE_1, 2249db65f35fSPhilippe Reynes FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_2, FEC_QOS_SCHEME, 2250db65f35fSPhilippe Reynes RMON_T_DROP, RMON_T_PACKETS, RMON_T_BC_PKT, RMON_T_MC_PKT, 2251db65f35fSPhilippe Reynes RMON_T_CRC_ALIGN, RMON_T_UNDERSIZE, RMON_T_OVERSIZE, RMON_T_FRAG, 2252db65f35fSPhilippe Reynes RMON_T_JAB, RMON_T_COL, RMON_T_P64, RMON_T_P65TO127, RMON_T_P128TO255, 2253db65f35fSPhilippe Reynes RMON_T_P256TO511, RMON_T_P512TO1023, RMON_T_P1024TO2047, 2254db65f35fSPhilippe Reynes RMON_T_P_GTE2048, RMON_T_OCTETS, 2255db65f35fSPhilippe Reynes IEEE_T_DROP, IEEE_T_FRAME_OK, IEEE_T_1COL, IEEE_T_MCOL, IEEE_T_DEF, 2256db65f35fSPhilippe Reynes IEEE_T_LCOL, IEEE_T_EXCOL, IEEE_T_MACERR, IEEE_T_CSERR, IEEE_T_SQE, 2257db65f35fSPhilippe Reynes IEEE_T_FDXFC, IEEE_T_OCTETS_OK, 2258db65f35fSPhilippe Reynes RMON_R_PACKETS, RMON_R_BC_PKT, RMON_R_MC_PKT, RMON_R_CRC_ALIGN, 2259db65f35fSPhilippe Reynes RMON_R_UNDERSIZE, RMON_R_OVERSIZE, RMON_R_FRAG, RMON_R_JAB, 2260db65f35fSPhilippe Reynes RMON_R_RESVD_O, RMON_R_P64, RMON_R_P65TO127, RMON_R_P128TO255, 2261db65f35fSPhilippe Reynes RMON_R_P256TO511, RMON_R_P512TO1023, RMON_R_P1024TO2047, 2262db65f35fSPhilippe Reynes RMON_R_P_GTE2048, RMON_R_OCTETS, 2263db65f35fSPhilippe Reynes IEEE_R_DROP, IEEE_R_FRAME_OK, IEEE_R_CRC, IEEE_R_ALIGN, IEEE_R_MACERR, 2264db65f35fSPhilippe Reynes IEEE_R_FDXFC, IEEE_R_OCTETS_OK 2265db65f35fSPhilippe Reynes }; 2266db65f35fSPhilippe Reynes #else 2267f9bcc9f3SVivien Didelot static __u32 fec_enet_register_version = 1; 2268db65f35fSPhilippe Reynes static u32 fec_enet_register_offset[] = { 2269db65f35fSPhilippe Reynes FEC_ECNTRL, FEC_IEVENT, FEC_IMASK, FEC_IVEC, FEC_R_DES_ACTIVE_0, 2270db65f35fSPhilippe Reynes FEC_R_DES_ACTIVE_1, FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_0, 2271db65f35fSPhilippe Reynes FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2, FEC_MII_DATA, FEC_MII_SPEED, 2272db65f35fSPhilippe Reynes FEC_R_BOUND, FEC_R_FSTART, FEC_X_WMRK, FEC_X_FSTART, FEC_R_CNTRL, 2273db65f35fSPhilippe Reynes FEC_MAX_FRM_LEN, FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, 2274db65f35fSPhilippe Reynes FEC_GRP_HASH_TABLE_HIGH, FEC_GRP_HASH_TABLE_LOW, FEC_R_DES_START_0, 2275db65f35fSPhilippe Reynes FEC_R_DES_START_1, FEC_R_DES_START_2, FEC_X_DES_START_0, 2276db65f35fSPhilippe Reynes FEC_X_DES_START_1, FEC_X_DES_START_2, FEC_R_BUFF_SIZE_0, 2277db65f35fSPhilippe Reynes FEC_R_BUFF_SIZE_1, FEC_R_BUFF_SIZE_2 2278db65f35fSPhilippe Reynes }; 2279db65f35fSPhilippe Reynes #endif 2280db65f35fSPhilippe Reynes 2281db65f35fSPhilippe Reynes static void fec_enet_get_regs(struct net_device *ndev, 2282db65f35fSPhilippe Reynes struct ethtool_regs *regs, void *regbuf) 2283db65f35fSPhilippe Reynes { 2284db65f35fSPhilippe Reynes struct fec_enet_private *fep = netdev_priv(ndev); 2285db65f35fSPhilippe Reynes u32 __iomem *theregs = (u32 __iomem *)fep->hwp; 2286c72a0bc0SAndrew Lunn struct device *dev = &fep->pdev->dev; 2287db65f35fSPhilippe Reynes u32 *buf = (u32 *)regbuf; 2288db65f35fSPhilippe Reynes u32 i, off; 2289c72a0bc0SAndrew Lunn int ret; 2290c72a0bc0SAndrew Lunn 2291da875fa5SZhang Qilong ret = pm_runtime_resume_and_get(dev); 2292c72a0bc0SAndrew Lunn if (ret < 0) 2293c72a0bc0SAndrew Lunn return; 2294db65f35fSPhilippe Reynes 2295f9bcc9f3SVivien Didelot regs->version = fec_enet_register_version; 2296f9bcc9f3SVivien Didelot 2297db65f35fSPhilippe Reynes memset(buf, 0, regs->len); 2298db65f35fSPhilippe Reynes 2299db65f35fSPhilippe Reynes for (i = 0; i < ARRAY_SIZE(fec_enet_register_offset); i++) { 2300ec20a63aSFugang Duan off = fec_enet_register_offset[i]; 2301ec20a63aSFugang Duan 2302ec20a63aSFugang Duan if ((off == FEC_R_BOUND || off == FEC_R_FSTART) && 2303ec20a63aSFugang Duan !(fep->quirks & FEC_QUIRK_HAS_FRREG)) 2304ec20a63aSFugang Duan continue; 2305ec20a63aSFugang Duan 2306ec20a63aSFugang Duan off >>= 2; 2307db65f35fSPhilippe Reynes buf[off] = readl(&theregs[off]); 2308db65f35fSPhilippe Reynes } 2309c72a0bc0SAndrew Lunn 2310c72a0bc0SAndrew Lunn pm_runtime_mark_last_busy(dev); 2311c72a0bc0SAndrew Lunn pm_runtime_put_autosuspend(dev); 2312db65f35fSPhilippe Reynes } 2313db65f35fSPhilippe Reynes 2314793fc096SFrank Li static int fec_enet_get_ts_info(struct net_device *ndev, 2315793fc096SFrank Li struct ethtool_ts_info *info) 2316793fc096SFrank Li { 2317793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2318793fc096SFrank Li 2319793fc096SFrank Li if (fep->bufdesc_ex) { 2320793fc096SFrank Li 2321793fc096SFrank Li info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | 2322793fc096SFrank Li SOF_TIMESTAMPING_RX_SOFTWARE | 2323793fc096SFrank Li SOF_TIMESTAMPING_SOFTWARE | 2324793fc096SFrank Li SOF_TIMESTAMPING_TX_HARDWARE | 2325793fc096SFrank Li SOF_TIMESTAMPING_RX_HARDWARE | 2326793fc096SFrank Li SOF_TIMESTAMPING_RAW_HARDWARE; 2327793fc096SFrank Li if (fep->ptp_clock) 2328793fc096SFrank Li info->phc_index = ptp_clock_index(fep->ptp_clock); 2329793fc096SFrank Li else 2330793fc096SFrank Li info->phc_index = -1; 2331793fc096SFrank Li 2332793fc096SFrank Li info->tx_types = (1 << HWTSTAMP_TX_OFF) | 2333793fc096SFrank Li (1 << HWTSTAMP_TX_ON); 2334793fc096SFrank Li 2335793fc096SFrank Li info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | 2336793fc096SFrank Li (1 << HWTSTAMP_FILTER_ALL); 2337793fc096SFrank Li return 0; 2338793fc096SFrank Li } else { 2339793fc096SFrank Li return ethtool_op_get_ts_info(ndev, info); 2340793fc096SFrank Li } 2341793fc096SFrank Li } 2342793fc096SFrank Li 2343d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 2344d1391930SGuenter Roeck 2345793fc096SFrank Li static void fec_enet_get_pauseparam(struct net_device *ndev, 2346793fc096SFrank Li struct ethtool_pauseparam *pause) 2347793fc096SFrank Li { 2348793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2349793fc096SFrank Li 2350793fc096SFrank Li pause->autoneg = (fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) != 0; 2351793fc096SFrank Li pause->tx_pause = (fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) != 0; 2352793fc096SFrank Li pause->rx_pause = pause->tx_pause; 2353793fc096SFrank Li } 2354793fc096SFrank Li 2355793fc096SFrank Li static int fec_enet_set_pauseparam(struct net_device *ndev, 2356793fc096SFrank Li struct ethtool_pauseparam *pause) 2357793fc096SFrank Li { 2358793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2359793fc096SFrank Li 236045f5c327SPhilippe Reynes if (!ndev->phydev) 23610b146ca8SRussell King return -ENODEV; 23620b146ca8SRussell King 2363793fc096SFrank Li if (pause->tx_pause != pause->rx_pause) { 2364793fc096SFrank Li netdev_info(ndev, 2365793fc096SFrank Li "hardware only support enable/disable both tx and rx"); 2366793fc096SFrank Li return -EINVAL; 2367793fc096SFrank Li } 2368793fc096SFrank Li 2369793fc096SFrank Li fep->pause_flag = 0; 2370793fc096SFrank Li 2371793fc096SFrank Li /* tx pause must be same as rx pause */ 2372793fc096SFrank Li fep->pause_flag |= pause->rx_pause ? FEC_PAUSE_FLAG_ENABLE : 0; 2373793fc096SFrank Li fep->pause_flag |= pause->autoneg ? FEC_PAUSE_FLAG_AUTONEG : 0; 2374793fc096SFrank Li 23750c122405SAndrew Lunn phy_set_sym_pause(ndev->phydev, pause->rx_pause, pause->tx_pause, 23760c122405SAndrew Lunn pause->autoneg); 2377793fc096SFrank Li 2378793fc096SFrank Li if (pause->autoneg) { 2379793fc096SFrank Li if (netif_running(ndev)) 2380793fc096SFrank Li fec_stop(ndev); 238145f5c327SPhilippe Reynes phy_start_aneg(ndev->phydev); 2382793fc096SFrank Li } 2383dbc64a8eSRussell King if (netif_running(ndev)) { 2384dbc64a8eSRussell King napi_disable(&fep->napi); 2385dbc64a8eSRussell King netif_tx_lock_bh(ndev); 2386ef83337dSRussell King fec_restart(ndev); 2387657ade07SRickard x Andersson netif_tx_wake_all_queues(ndev); 23886af42d42SRussell King netif_tx_unlock_bh(ndev); 2389dbc64a8eSRussell King napi_enable(&fep->napi); 2390dbc64a8eSRussell King } 2391793fc096SFrank Li 2392793fc096SFrank Li return 0; 2393793fc096SFrank Li } 2394793fc096SFrank Li 239538ae92dcSChris Healy static const struct fec_stat { 239638ae92dcSChris Healy char name[ETH_GSTRING_LEN]; 239738ae92dcSChris Healy u16 offset; 239838ae92dcSChris Healy } fec_stats[] = { 239938ae92dcSChris Healy /* RMON TX */ 240038ae92dcSChris Healy { "tx_dropped", RMON_T_DROP }, 240138ae92dcSChris Healy { "tx_packets", RMON_T_PACKETS }, 240238ae92dcSChris Healy { "tx_broadcast", RMON_T_BC_PKT }, 240338ae92dcSChris Healy { "tx_multicast", RMON_T_MC_PKT }, 240438ae92dcSChris Healy { "tx_crc_errors", RMON_T_CRC_ALIGN }, 240538ae92dcSChris Healy { "tx_undersize", RMON_T_UNDERSIZE }, 240638ae92dcSChris Healy { "tx_oversize", RMON_T_OVERSIZE }, 240738ae92dcSChris Healy { "tx_fragment", RMON_T_FRAG }, 240838ae92dcSChris Healy { "tx_jabber", RMON_T_JAB }, 240938ae92dcSChris Healy { "tx_collision", RMON_T_COL }, 241038ae92dcSChris Healy { "tx_64byte", RMON_T_P64 }, 241138ae92dcSChris Healy { "tx_65to127byte", RMON_T_P65TO127 }, 241238ae92dcSChris Healy { "tx_128to255byte", RMON_T_P128TO255 }, 241338ae92dcSChris Healy { "tx_256to511byte", RMON_T_P256TO511 }, 241438ae92dcSChris Healy { "tx_512to1023byte", RMON_T_P512TO1023 }, 241538ae92dcSChris Healy { "tx_1024to2047byte", RMON_T_P1024TO2047 }, 241638ae92dcSChris Healy { "tx_GTE2048byte", RMON_T_P_GTE2048 }, 241738ae92dcSChris Healy { "tx_octets", RMON_T_OCTETS }, 241838ae92dcSChris Healy 241938ae92dcSChris Healy /* IEEE TX */ 242038ae92dcSChris Healy { "IEEE_tx_drop", IEEE_T_DROP }, 242138ae92dcSChris Healy { "IEEE_tx_frame_ok", IEEE_T_FRAME_OK }, 242238ae92dcSChris Healy { "IEEE_tx_1col", IEEE_T_1COL }, 242338ae92dcSChris Healy { "IEEE_tx_mcol", IEEE_T_MCOL }, 242438ae92dcSChris Healy { "IEEE_tx_def", IEEE_T_DEF }, 242538ae92dcSChris Healy { "IEEE_tx_lcol", IEEE_T_LCOL }, 242638ae92dcSChris Healy { "IEEE_tx_excol", IEEE_T_EXCOL }, 242738ae92dcSChris Healy { "IEEE_tx_macerr", IEEE_T_MACERR }, 242838ae92dcSChris Healy { "IEEE_tx_cserr", IEEE_T_CSERR }, 242938ae92dcSChris Healy { "IEEE_tx_sqe", IEEE_T_SQE }, 243038ae92dcSChris Healy { "IEEE_tx_fdxfc", IEEE_T_FDXFC }, 243138ae92dcSChris Healy { "IEEE_tx_octets_ok", IEEE_T_OCTETS_OK }, 243238ae92dcSChris Healy 243338ae92dcSChris Healy /* RMON RX */ 243438ae92dcSChris Healy { "rx_packets", RMON_R_PACKETS }, 243538ae92dcSChris Healy { "rx_broadcast", RMON_R_BC_PKT }, 243638ae92dcSChris Healy { "rx_multicast", RMON_R_MC_PKT }, 243738ae92dcSChris Healy { "rx_crc_errors", RMON_R_CRC_ALIGN }, 243838ae92dcSChris Healy { "rx_undersize", RMON_R_UNDERSIZE }, 243938ae92dcSChris Healy { "rx_oversize", RMON_R_OVERSIZE }, 244038ae92dcSChris Healy { "rx_fragment", RMON_R_FRAG }, 244138ae92dcSChris Healy { "rx_jabber", RMON_R_JAB }, 244238ae92dcSChris Healy { "rx_64byte", RMON_R_P64 }, 244338ae92dcSChris Healy { "rx_65to127byte", RMON_R_P65TO127 }, 244438ae92dcSChris Healy { "rx_128to255byte", RMON_R_P128TO255 }, 244538ae92dcSChris Healy { "rx_256to511byte", RMON_R_P256TO511 }, 244638ae92dcSChris Healy { "rx_512to1023byte", RMON_R_P512TO1023 }, 244738ae92dcSChris Healy { "rx_1024to2047byte", RMON_R_P1024TO2047 }, 244838ae92dcSChris Healy { "rx_GTE2048byte", RMON_R_P_GTE2048 }, 244938ae92dcSChris Healy { "rx_octets", RMON_R_OCTETS }, 245038ae92dcSChris Healy 245138ae92dcSChris Healy /* IEEE RX */ 245238ae92dcSChris Healy { "IEEE_rx_drop", IEEE_R_DROP }, 245338ae92dcSChris Healy { "IEEE_rx_frame_ok", IEEE_R_FRAME_OK }, 245438ae92dcSChris Healy { "IEEE_rx_crc", IEEE_R_CRC }, 245538ae92dcSChris Healy { "IEEE_rx_align", IEEE_R_ALIGN }, 245638ae92dcSChris Healy { "IEEE_rx_macerr", IEEE_R_MACERR }, 245738ae92dcSChris Healy { "IEEE_rx_fdxfc", IEEE_R_FDXFC }, 245838ae92dcSChris Healy { "IEEE_rx_octets_ok", IEEE_R_OCTETS_OK }, 245938ae92dcSChris Healy }; 246038ae92dcSChris Healy 2461f85de666SNikita Yushchenko #define FEC_STATS_SIZE (ARRAY_SIZE(fec_stats) * sizeof(u64)) 2462f85de666SNikita Yushchenko 246380cca775SNikita Yushchenko static void fec_enet_update_ethtool_stats(struct net_device *dev) 246438ae92dcSChris Healy { 246538ae92dcSChris Healy struct fec_enet_private *fep = netdev_priv(dev); 246638ae92dcSChris Healy int i; 246738ae92dcSChris Healy 246838ae92dcSChris Healy for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 246980cca775SNikita Yushchenko fep->ethtool_stats[i] = readl(fep->hwp + fec_stats[i].offset); 247080cca775SNikita Yushchenko } 247180cca775SNikita Yushchenko 247280cca775SNikita Yushchenko static void fec_enet_get_ethtool_stats(struct net_device *dev, 247380cca775SNikita Yushchenko struct ethtool_stats *stats, u64 *data) 247480cca775SNikita Yushchenko { 247580cca775SNikita Yushchenko struct fec_enet_private *fep = netdev_priv(dev); 247680cca775SNikita Yushchenko 247780cca775SNikita Yushchenko if (netif_running(dev)) 247880cca775SNikita Yushchenko fec_enet_update_ethtool_stats(dev); 247980cca775SNikita Yushchenko 2480f85de666SNikita Yushchenko memcpy(data, fep->ethtool_stats, FEC_STATS_SIZE); 248138ae92dcSChris Healy } 248238ae92dcSChris Healy 248338ae92dcSChris Healy static void fec_enet_get_strings(struct net_device *netdev, 248438ae92dcSChris Healy u32 stringset, u8 *data) 248538ae92dcSChris Healy { 248638ae92dcSChris Healy int i; 248738ae92dcSChris Healy switch (stringset) { 248838ae92dcSChris Healy case ETH_SS_STATS: 248938ae92dcSChris Healy for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 249038ae92dcSChris Healy memcpy(data + i * ETH_GSTRING_LEN, 249138ae92dcSChris Healy fec_stats[i].name, ETH_GSTRING_LEN); 249238ae92dcSChris Healy break; 24936016ba34SOleksij Rempel case ETH_SS_TEST: 24946016ba34SOleksij Rempel net_selftest_get_strings(data); 24956016ba34SOleksij Rempel break; 249638ae92dcSChris Healy } 249738ae92dcSChris Healy } 249838ae92dcSChris Healy 249938ae92dcSChris Healy static int fec_enet_get_sset_count(struct net_device *dev, int sset) 250038ae92dcSChris Healy { 250138ae92dcSChris Healy switch (sset) { 250238ae92dcSChris Healy case ETH_SS_STATS: 250338ae92dcSChris Healy return ARRAY_SIZE(fec_stats); 25046016ba34SOleksij Rempel case ETH_SS_TEST: 25056016ba34SOleksij Rempel return net_selftest_get_count(); 250638ae92dcSChris Healy default: 250738ae92dcSChris Healy return -EOPNOTSUPP; 250838ae92dcSChris Healy } 250938ae92dcSChris Healy } 2510f85de666SNikita Yushchenko 25112b30842bSAndrew Lunn static void fec_enet_clear_ethtool_stats(struct net_device *dev) 25122b30842bSAndrew Lunn { 25132b30842bSAndrew Lunn struct fec_enet_private *fep = netdev_priv(dev); 25142b30842bSAndrew Lunn int i; 25152b30842bSAndrew Lunn 25162b30842bSAndrew Lunn /* Disable MIB statistics counters */ 25172b30842bSAndrew Lunn writel(FEC_MIB_CTRLSTAT_DISABLE, fep->hwp + FEC_MIB_CTRLSTAT); 25182b30842bSAndrew Lunn 25192b30842bSAndrew Lunn for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 25202b30842bSAndrew Lunn writel(0, fep->hwp + fec_stats[i].offset); 25212b30842bSAndrew Lunn 25222b30842bSAndrew Lunn /* Don't disable MIB statistics counters */ 25232b30842bSAndrew Lunn writel(0, fep->hwp + FEC_MIB_CTRLSTAT); 25242b30842bSAndrew Lunn } 25252b30842bSAndrew Lunn 2526f85de666SNikita Yushchenko #else /* !defined(CONFIG_M5272) */ 2527f85de666SNikita Yushchenko #define FEC_STATS_SIZE 0 2528f85de666SNikita Yushchenko static inline void fec_enet_update_ethtool_stats(struct net_device *dev) 2529f85de666SNikita Yushchenko { 2530f85de666SNikita Yushchenko } 253141e8e404SFabio Estevam 253241e8e404SFabio Estevam static inline void fec_enet_clear_ethtool_stats(struct net_device *dev) 253341e8e404SFabio Estevam { 253441e8e404SFabio Estevam } 2535d1391930SGuenter Roeck #endif /* !defined(CONFIG_M5272) */ 253638ae92dcSChris Healy 2537d851b47bSFugang Duan /* ITR clock source is enet system clock (clk_ahb). 2538d851b47bSFugang Duan * TCTT unit is cycle_ns * 64 cycle 2539d851b47bSFugang Duan * So, the ICTT value = X us / (cycle_ns * 64) 2540d851b47bSFugang Duan */ 2541d851b47bSFugang Duan static int fec_enet_us_to_itr_clock(struct net_device *ndev, int us) 2542d851b47bSFugang Duan { 2543d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2544d851b47bSFugang Duan 2545d851b47bSFugang Duan return us * (fep->itr_clk_rate / 64000) / 1000; 2546d851b47bSFugang Duan } 2547d851b47bSFugang Duan 2548d851b47bSFugang Duan /* Set threshold for interrupt coalescing */ 2549d851b47bSFugang Duan static void fec_enet_itr_coal_set(struct net_device *ndev) 2550d851b47bSFugang Duan { 2551d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2552d851b47bSFugang Duan int rx_itr, tx_itr; 2553d851b47bSFugang Duan 2554d851b47bSFugang Duan /* Must be greater than zero to avoid unpredictable behavior */ 2555d851b47bSFugang Duan if (!fep->rx_time_itr || !fep->rx_pkts_itr || 2556d851b47bSFugang Duan !fep->tx_time_itr || !fep->tx_pkts_itr) 2557d851b47bSFugang Duan return; 2558d851b47bSFugang Duan 2559d851b47bSFugang Duan /* Select enet system clock as Interrupt Coalescing 2560d851b47bSFugang Duan * timer Clock Source 2561d851b47bSFugang Duan */ 2562d851b47bSFugang Duan rx_itr = FEC_ITR_CLK_SEL; 2563d851b47bSFugang Duan tx_itr = FEC_ITR_CLK_SEL; 2564d851b47bSFugang Duan 2565d851b47bSFugang Duan /* set ICFT and ICTT */ 2566d851b47bSFugang Duan rx_itr |= FEC_ITR_ICFT(fep->rx_pkts_itr); 2567d851b47bSFugang Duan rx_itr |= FEC_ITR_ICTT(fec_enet_us_to_itr_clock(ndev, fep->rx_time_itr)); 2568d851b47bSFugang Duan tx_itr |= FEC_ITR_ICFT(fep->tx_pkts_itr); 2569d851b47bSFugang Duan tx_itr |= FEC_ITR_ICTT(fec_enet_us_to_itr_clock(ndev, fep->tx_time_itr)); 2570d851b47bSFugang Duan 2571d851b47bSFugang Duan rx_itr |= FEC_ITR_EN; 2572d851b47bSFugang Duan tx_itr |= FEC_ITR_EN; 2573d851b47bSFugang Duan 2574d851b47bSFugang Duan writel(tx_itr, fep->hwp + FEC_TXIC0); 2575d851b47bSFugang Duan writel(rx_itr, fep->hwp + FEC_RXIC0); 2576471ff445SJoakim Zhang if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) { 2577d851b47bSFugang Duan writel(tx_itr, fep->hwp + FEC_TXIC1); 2578d851b47bSFugang Duan writel(rx_itr, fep->hwp + FEC_RXIC1); 2579d851b47bSFugang Duan writel(tx_itr, fep->hwp + FEC_TXIC2); 2580d851b47bSFugang Duan writel(rx_itr, fep->hwp + FEC_RXIC2); 2581d851b47bSFugang Duan } 2582ff7566b8SFugang Duan } 2583d851b47bSFugang Duan 2584d851b47bSFugang Duan static int 2585d851b47bSFugang Duan fec_enet_get_coalesce(struct net_device *ndev, struct ethtool_coalesce *ec) 2586d851b47bSFugang Duan { 2587d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2588d851b47bSFugang Duan 2589ff7566b8SFugang Duan if (!(fep->quirks & FEC_QUIRK_HAS_COALESCE)) 2590d851b47bSFugang Duan return -EOPNOTSUPP; 2591d851b47bSFugang Duan 2592d851b47bSFugang Duan ec->rx_coalesce_usecs = fep->rx_time_itr; 2593d851b47bSFugang Duan ec->rx_max_coalesced_frames = fep->rx_pkts_itr; 2594d851b47bSFugang Duan 2595d851b47bSFugang Duan ec->tx_coalesce_usecs = fep->tx_time_itr; 2596d851b47bSFugang Duan ec->tx_max_coalesced_frames = fep->tx_pkts_itr; 2597d851b47bSFugang Duan 2598d851b47bSFugang Duan return 0; 2599d851b47bSFugang Duan } 2600d851b47bSFugang Duan 2601d851b47bSFugang Duan static int 2602d851b47bSFugang Duan fec_enet_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *ec) 2603d851b47bSFugang Duan { 2604d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2605517a772cSFabio Estevam struct device *dev = &fep->pdev->dev; 2606d851b47bSFugang Duan unsigned int cycle; 2607d851b47bSFugang Duan 2608ff7566b8SFugang Duan if (!(fep->quirks & FEC_QUIRK_HAS_COALESCE)) 2609d851b47bSFugang Duan return -EOPNOTSUPP; 2610d851b47bSFugang Duan 2611d851b47bSFugang Duan if (ec->rx_max_coalesced_frames > 255) { 2612517a772cSFabio Estevam dev_err(dev, "Rx coalesced frames exceed hardware limitation\n"); 2613d851b47bSFugang Duan return -EINVAL; 2614d851b47bSFugang Duan } 2615d851b47bSFugang Duan 2616d851b47bSFugang Duan if (ec->tx_max_coalesced_frames > 255) { 2617517a772cSFabio Estevam dev_err(dev, "Tx coalesced frame exceed hardware limitation\n"); 2618d851b47bSFugang Duan return -EINVAL; 2619d851b47bSFugang Duan } 2620d851b47bSFugang Duan 2621ab14961dSJakub Kicinski cycle = fec_enet_us_to_itr_clock(ndev, ec->rx_coalesce_usecs); 2622d851b47bSFugang Duan if (cycle > 0xFFFF) { 2623517a772cSFabio Estevam dev_err(dev, "Rx coalesced usec exceed hardware limitation\n"); 2624d851b47bSFugang Duan return -EINVAL; 2625d851b47bSFugang Duan } 2626d851b47bSFugang Duan 2627ab14961dSJakub Kicinski cycle = fec_enet_us_to_itr_clock(ndev, ec->tx_coalesce_usecs); 2628d851b47bSFugang Duan if (cycle > 0xFFFF) { 2629ab14961dSJakub Kicinski dev_err(dev, "Tx coalesced usec exceed hardware limitation\n"); 2630d851b47bSFugang Duan return -EINVAL; 2631d851b47bSFugang Duan } 2632d851b47bSFugang Duan 2633d851b47bSFugang Duan fep->rx_time_itr = ec->rx_coalesce_usecs; 2634d851b47bSFugang Duan fep->rx_pkts_itr = ec->rx_max_coalesced_frames; 2635d851b47bSFugang Duan 2636d851b47bSFugang Duan fep->tx_time_itr = ec->tx_coalesce_usecs; 2637d851b47bSFugang Duan fep->tx_pkts_itr = ec->tx_max_coalesced_frames; 2638d851b47bSFugang Duan 2639d851b47bSFugang Duan fec_enet_itr_coal_set(ndev); 2640d851b47bSFugang Duan 2641d851b47bSFugang Duan return 0; 2642d851b47bSFugang Duan } 2643d851b47bSFugang Duan 2644d851b47bSFugang Duan static void fec_enet_itr_coal_init(struct net_device *ndev) 2645d851b47bSFugang Duan { 2646d851b47bSFugang Duan struct ethtool_coalesce ec; 2647d851b47bSFugang Duan 2648d851b47bSFugang Duan ec.rx_coalesce_usecs = FEC_ITR_ICTT_DEFAULT; 2649d851b47bSFugang Duan ec.rx_max_coalesced_frames = FEC_ITR_ICFT_DEFAULT; 2650d851b47bSFugang Duan 2651d851b47bSFugang Duan ec.tx_coalesce_usecs = FEC_ITR_ICTT_DEFAULT; 2652d851b47bSFugang Duan ec.tx_max_coalesced_frames = FEC_ITR_ICFT_DEFAULT; 2653d851b47bSFugang Duan 2654d851b47bSFugang Duan fec_enet_set_coalesce(ndev, &ec); 2655d851b47bSFugang Duan } 2656d851b47bSFugang Duan 26571b7bde6dSNimrod Andy static int fec_enet_get_tunable(struct net_device *netdev, 26581b7bde6dSNimrod Andy const struct ethtool_tunable *tuna, 26591b7bde6dSNimrod Andy void *data) 26601b7bde6dSNimrod Andy { 26611b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(netdev); 26621b7bde6dSNimrod Andy int ret = 0; 26631b7bde6dSNimrod Andy 26641b7bde6dSNimrod Andy switch (tuna->id) { 26651b7bde6dSNimrod Andy case ETHTOOL_RX_COPYBREAK: 26661b7bde6dSNimrod Andy *(u32 *)data = fep->rx_copybreak; 26671b7bde6dSNimrod Andy break; 26681b7bde6dSNimrod Andy default: 26691b7bde6dSNimrod Andy ret = -EINVAL; 26701b7bde6dSNimrod Andy break; 26711b7bde6dSNimrod Andy } 26721b7bde6dSNimrod Andy 26731b7bde6dSNimrod Andy return ret; 26741b7bde6dSNimrod Andy } 26751b7bde6dSNimrod Andy 26761b7bde6dSNimrod Andy static int fec_enet_set_tunable(struct net_device *netdev, 26771b7bde6dSNimrod Andy const struct ethtool_tunable *tuna, 26781b7bde6dSNimrod Andy const void *data) 26791b7bde6dSNimrod Andy { 26801b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(netdev); 26811b7bde6dSNimrod Andy int ret = 0; 26821b7bde6dSNimrod Andy 26831b7bde6dSNimrod Andy switch (tuna->id) { 26841b7bde6dSNimrod Andy case ETHTOOL_RX_COPYBREAK: 26851b7bde6dSNimrod Andy fep->rx_copybreak = *(u32 *)data; 26861b7bde6dSNimrod Andy break; 26871b7bde6dSNimrod Andy default: 26881b7bde6dSNimrod Andy ret = -EINVAL; 26891b7bde6dSNimrod Andy break; 26901b7bde6dSNimrod Andy } 26911b7bde6dSNimrod Andy 26921b7bde6dSNimrod Andy return ret; 26931b7bde6dSNimrod Andy } 26941b7bde6dSNimrod Andy 2695de40ed31SNimrod Andy static void 2696de40ed31SNimrod Andy fec_enet_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) 2697de40ed31SNimrod Andy { 2698de40ed31SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 2699de40ed31SNimrod Andy 2700de40ed31SNimrod Andy if (fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET) { 2701de40ed31SNimrod Andy wol->supported = WAKE_MAGIC; 2702de40ed31SNimrod Andy wol->wolopts = fep->wol_flag & FEC_WOL_FLAG_ENABLE ? WAKE_MAGIC : 0; 2703de40ed31SNimrod Andy } else { 2704de40ed31SNimrod Andy wol->supported = wol->wolopts = 0; 2705de40ed31SNimrod Andy } 2706de40ed31SNimrod Andy } 2707de40ed31SNimrod Andy 2708de40ed31SNimrod Andy static int 2709de40ed31SNimrod Andy fec_enet_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) 2710de40ed31SNimrod Andy { 2711de40ed31SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 2712de40ed31SNimrod Andy 2713de40ed31SNimrod Andy if (!(fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET)) 2714de40ed31SNimrod Andy return -EINVAL; 2715de40ed31SNimrod Andy 2716de40ed31SNimrod Andy if (wol->wolopts & ~WAKE_MAGIC) 2717de40ed31SNimrod Andy return -EINVAL; 2718de40ed31SNimrod Andy 2719de40ed31SNimrod Andy device_set_wakeup_enable(&ndev->dev, wol->wolopts & WAKE_MAGIC); 2720de40ed31SNimrod Andy if (device_may_wakeup(&ndev->dev)) { 2721de40ed31SNimrod Andy fep->wol_flag |= FEC_WOL_FLAG_ENABLE; 2722de40ed31SNimrod Andy if (fep->irq[0] > 0) 2723de40ed31SNimrod Andy enable_irq_wake(fep->irq[0]); 2724de40ed31SNimrod Andy } else { 2725de40ed31SNimrod Andy fep->wol_flag &= (~FEC_WOL_FLAG_ENABLE); 2726de40ed31SNimrod Andy if (fep->irq[0] > 0) 2727de40ed31SNimrod Andy disable_irq_wake(fep->irq[0]); 2728de40ed31SNimrod Andy } 2729de40ed31SNimrod Andy 2730de40ed31SNimrod Andy return 0; 2731de40ed31SNimrod Andy } 2732de40ed31SNimrod Andy 2733793fc096SFrank Li static const struct ethtool_ops fec_enet_ethtool_ops = { 2734d5e3c87dSJakub Kicinski .supported_coalesce_params = ETHTOOL_COALESCE_USECS | 2735d5e3c87dSJakub Kicinski ETHTOOL_COALESCE_MAX_FRAMES, 2736793fc096SFrank Li .get_drvinfo = fec_enet_get_drvinfo, 2737db65f35fSPhilippe Reynes .get_regs_len = fec_enet_get_regs_len, 2738db65f35fSPhilippe Reynes .get_regs = fec_enet_get_regs, 273911d59289SFlorian Fainelli .nway_reset = phy_ethtool_nway_reset, 2740c1d7c48fSRussell King .get_link = ethtool_op_get_link, 2741d851b47bSFugang Duan .get_coalesce = fec_enet_get_coalesce, 2742d851b47bSFugang Duan .set_coalesce = fec_enet_set_coalesce, 274338ae92dcSChris Healy #ifndef CONFIG_M5272 2744c1d7c48fSRussell King .get_pauseparam = fec_enet_get_pauseparam, 2745c1d7c48fSRussell King .set_pauseparam = fec_enet_set_pauseparam, 274638ae92dcSChris Healy .get_strings = fec_enet_get_strings, 2747c1d7c48fSRussell King .get_ethtool_stats = fec_enet_get_ethtool_stats, 274838ae92dcSChris Healy .get_sset_count = fec_enet_get_sset_count, 274938ae92dcSChris Healy #endif 2750c1d7c48fSRussell King .get_ts_info = fec_enet_get_ts_info, 27511b7bde6dSNimrod Andy .get_tunable = fec_enet_get_tunable, 27521b7bde6dSNimrod Andy .set_tunable = fec_enet_set_tunable, 2753de40ed31SNimrod Andy .get_wol = fec_enet_get_wol, 2754de40ed31SNimrod Andy .set_wol = fec_enet_set_wol, 27559365fbf5SPhilippe Reynes .get_link_ksettings = phy_ethtool_get_link_ksettings, 27569365fbf5SPhilippe Reynes .set_link_ksettings = phy_ethtool_set_link_ksettings, 27576016ba34SOleksij Rempel .self_test = net_selftest, 2758793fc096SFrank Li }; 2759793fc096SFrank Li 2760793fc096SFrank Li static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) 2761793fc096SFrank Li { 2762793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 276345f5c327SPhilippe Reynes struct phy_device *phydev = ndev->phydev; 2764793fc096SFrank Li 2765793fc096SFrank Li if (!netif_running(ndev)) 2766793fc096SFrank Li return -EINVAL; 2767793fc096SFrank Li 2768793fc096SFrank Li if (!phydev) 2769793fc096SFrank Li return -ENODEV; 2770793fc096SFrank Li 27711d5244d0SBen Hutchings if (fep->bufdesc_ex) { 277234074639SSergey Organov bool use_fec_hwts = !phy_has_hwtstamp(phydev); 277334074639SSergey Organov 277434074639SSergey Organov if (cmd == SIOCSHWTSTAMP) { 277534074639SSergey Organov if (use_fec_hwts) 27761d5244d0SBen Hutchings return fec_ptp_set(ndev, rq); 277734074639SSergey Organov fec_ptp_disable_hwts(ndev); 277834074639SSergey Organov } else if (cmd == SIOCGHWTSTAMP) { 277934074639SSergey Organov if (use_fec_hwts) 27801d5244d0SBen Hutchings return fec_ptp_get(ndev, rq); 27811d5244d0SBen Hutchings } 278234074639SSergey Organov } 2783793fc096SFrank Li 2784793fc096SFrank Li return phy_mii_ioctl(phydev, rq, cmd); 2785793fc096SFrank Li } 2786793fc096SFrank Li 2787793fc096SFrank Li static void fec_enet_free_buffers(struct net_device *ndev) 2788793fc096SFrank Li { 2789793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2790793fc096SFrank Li unsigned int i; 2791793fc096SFrank Li struct sk_buff *skb; 2792793fc096SFrank Li struct bufdesc *bdp; 27934d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 27944d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 279559d0f746SFrank Li unsigned int q; 2796793fc096SFrank Li 279759d0f746SFrank Li for (q = 0; q < fep->num_rx_queues; q++) { 279859d0f746SFrank Li rxq = fep->rx_queue[q]; 27997355f276STroy Kisky bdp = rxq->bd.base; 28007355f276STroy Kisky for (i = 0; i < rxq->bd.ring_size; i++) { 28014d494cdcSFugang Duan skb = rxq->rx_skbuff[i]; 28024d494cdcSFugang Duan rxq->rx_skbuff[i] = NULL; 2803730ee360SRussell King if (skb) { 280459d0f746SFrank Li dma_unmap_single(&fep->pdev->dev, 28055cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 2806b64bf4b7SNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 280759d0f746SFrank Li DMA_FROM_DEVICE); 2808793fc096SFrank Li dev_kfree_skb(skb); 2809730ee360SRussell King } 28107355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 281159d0f746SFrank Li } 2812793fc096SFrank Li } 2813793fc096SFrank Li 281459d0f746SFrank Li for (q = 0; q < fep->num_tx_queues; q++) { 281559d0f746SFrank Li txq = fep->tx_queue[q]; 28167355f276STroy Kisky for (i = 0; i < txq->bd.ring_size; i++) { 28174d494cdcSFugang Duan kfree(txq->tx_bounce[i]); 28184d494cdcSFugang Duan txq->tx_bounce[i] = NULL; 28194d494cdcSFugang Duan skb = txq->tx_skbuff[i]; 28204d494cdcSFugang Duan txq->tx_skbuff[i] = NULL; 28218b7c9efaSRussell King dev_kfree_skb(skb); 28228b7c9efaSRussell King } 2823793fc096SFrank Li } 282459d0f746SFrank Li } 2825793fc096SFrank Li 282659d0f746SFrank Li static void fec_enet_free_queue(struct net_device *ndev) 282759d0f746SFrank Li { 282859d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 282959d0f746SFrank Li int i; 283059d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 283159d0f746SFrank Li 283259d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) 283359d0f746SFrank Li if (fep->tx_queue[i] && fep->tx_queue[i]->tso_hdrs) { 283459d0f746SFrank Li txq = fep->tx_queue[i]; 283594920128SFugang Duan dma_free_coherent(&fep->pdev->dev, 28367355f276STroy Kisky txq->bd.ring_size * TSO_HEADER_SIZE, 283759d0f746SFrank Li txq->tso_hdrs, 283859d0f746SFrank Li txq->tso_hdrs_dma); 283959d0f746SFrank Li } 284059d0f746SFrank Li 284159d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) 284259d0f746SFrank Li kfree(fep->rx_queue[i]); 284359d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) 284459d0f746SFrank Li kfree(fep->tx_queue[i]); 284559d0f746SFrank Li } 284659d0f746SFrank Li 284759d0f746SFrank Li static int fec_enet_alloc_queue(struct net_device *ndev) 284859d0f746SFrank Li { 284959d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 285059d0f746SFrank Li int i; 285159d0f746SFrank Li int ret = 0; 285259d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 285359d0f746SFrank Li 285459d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 285559d0f746SFrank Li txq = kzalloc(sizeof(*txq), GFP_KERNEL); 285659d0f746SFrank Li if (!txq) { 285759d0f746SFrank Li ret = -ENOMEM; 285859d0f746SFrank Li goto alloc_failed; 285959d0f746SFrank Li } 286059d0f746SFrank Li 286159d0f746SFrank Li fep->tx_queue[i] = txq; 28627355f276STroy Kisky txq->bd.ring_size = TX_RING_SIZE; 28637355f276STroy Kisky fep->total_tx_ring_size += fep->tx_queue[i]->bd.ring_size; 286459d0f746SFrank Li 286559d0f746SFrank Li txq->tx_stop_threshold = FEC_MAX_SKB_DESCS; 286659d0f746SFrank Li txq->tx_wake_threshold = 28677355f276STroy Kisky (txq->bd.ring_size - txq->tx_stop_threshold) / 2; 286859d0f746SFrank Li 286994920128SFugang Duan txq->tso_hdrs = dma_alloc_coherent(&fep->pdev->dev, 28707355f276STroy Kisky txq->bd.ring_size * TSO_HEADER_SIZE, 287159d0f746SFrank Li &txq->tso_hdrs_dma, 287259d0f746SFrank Li GFP_KERNEL); 287359d0f746SFrank Li if (!txq->tso_hdrs) { 287459d0f746SFrank Li ret = -ENOMEM; 287559d0f746SFrank Li goto alloc_failed; 287659d0f746SFrank Li } 287759d0f746SFrank Li } 287859d0f746SFrank Li 287959d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) { 288059d0f746SFrank Li fep->rx_queue[i] = kzalloc(sizeof(*fep->rx_queue[i]), 288159d0f746SFrank Li GFP_KERNEL); 288259d0f746SFrank Li if (!fep->rx_queue[i]) { 288359d0f746SFrank Li ret = -ENOMEM; 288459d0f746SFrank Li goto alloc_failed; 288559d0f746SFrank Li } 288659d0f746SFrank Li 28877355f276STroy Kisky fep->rx_queue[i]->bd.ring_size = RX_RING_SIZE; 28887355f276STroy Kisky fep->total_rx_ring_size += fep->rx_queue[i]->bd.ring_size; 288959d0f746SFrank Li } 289059d0f746SFrank Li return ret; 289159d0f746SFrank Li 289259d0f746SFrank Li alloc_failed: 289359d0f746SFrank Li fec_enet_free_queue(ndev); 289459d0f746SFrank Li return ret; 289559d0f746SFrank Li } 289659d0f746SFrank Li 289759d0f746SFrank Li static int 289859d0f746SFrank Li fec_enet_alloc_rxq_buffers(struct net_device *ndev, unsigned int queue) 2899793fc096SFrank Li { 2900793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2901793fc096SFrank Li unsigned int i; 2902793fc096SFrank Li struct sk_buff *skb; 2903793fc096SFrank Li struct bufdesc *bdp; 29044d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 2905793fc096SFrank Li 290659d0f746SFrank Li rxq = fep->rx_queue[queue]; 29077355f276STroy Kisky bdp = rxq->bd.base; 29087355f276STroy Kisky for (i = 0; i < rxq->bd.ring_size; i++) { 2909793fc096SFrank Li skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE); 2910ffdce2ccSRussell King if (!skb) 2911ffdce2ccSRussell King goto err_alloc; 2912793fc096SFrank Li 29131b7bde6dSNimrod Andy if (fec_enet_new_rxbdp(ndev, bdp, skb)) { 2914730ee360SRussell King dev_kfree_skb(skb); 2915ffdce2ccSRussell King goto err_alloc; 2916d842a31fSDuan Fugang-B38611 } 2917730ee360SRussell King 29184d494cdcSFugang Duan rxq->rx_skbuff[i] = skb; 29195cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); 2920793fc096SFrank Li 2921793fc096SFrank Li if (fep->bufdesc_ex) { 2922793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 29235cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(BD_ENET_RX_INT); 2924793fc096SFrank Li } 2925793fc096SFrank Li 29267355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 2927793fc096SFrank Li } 2928793fc096SFrank Li 2929793fc096SFrank Li /* Set the last buffer to wrap. */ 29307355f276STroy Kisky bdp = fec_enet_get_prevdesc(bdp, &rxq->bd); 29315cfa3039SJohannes Berg bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 293259d0f746SFrank Li return 0; 2933793fc096SFrank Li 293459d0f746SFrank Li err_alloc: 293559d0f746SFrank Li fec_enet_free_buffers(ndev); 293659d0f746SFrank Li return -ENOMEM; 293759d0f746SFrank Li } 293859d0f746SFrank Li 293959d0f746SFrank Li static int 294059d0f746SFrank Li fec_enet_alloc_txq_buffers(struct net_device *ndev, unsigned int queue) 294159d0f746SFrank Li { 294259d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 294359d0f746SFrank Li unsigned int i; 294459d0f746SFrank Li struct bufdesc *bdp; 294559d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 294659d0f746SFrank Li 294759d0f746SFrank Li txq = fep->tx_queue[queue]; 29487355f276STroy Kisky bdp = txq->bd.base; 29497355f276STroy Kisky for (i = 0; i < txq->bd.ring_size; i++) { 29504d494cdcSFugang Duan txq->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL); 29514d494cdcSFugang Duan if (!txq->tx_bounce[i]) 2952ffdce2ccSRussell King goto err_alloc; 2953793fc096SFrank Li 29545cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(0); 29555cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(0); 2956793fc096SFrank Li 2957793fc096SFrank Li if (fep->bufdesc_ex) { 2958793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 29595cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(BD_ENET_TX_INT); 2960793fc096SFrank Li } 2961793fc096SFrank Li 29627355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 2963793fc096SFrank Li } 2964793fc096SFrank Li 2965793fc096SFrank Li /* Set the last buffer to wrap. */ 29667355f276STroy Kisky bdp = fec_enet_get_prevdesc(bdp, &txq->bd); 29675cfa3039SJohannes Berg bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 2968793fc096SFrank Li 2969793fc096SFrank Li return 0; 2970ffdce2ccSRussell King 2971ffdce2ccSRussell King err_alloc: 2972ffdce2ccSRussell King fec_enet_free_buffers(ndev); 2973ffdce2ccSRussell King return -ENOMEM; 2974793fc096SFrank Li } 2975793fc096SFrank Li 297659d0f746SFrank Li static int fec_enet_alloc_buffers(struct net_device *ndev) 297759d0f746SFrank Li { 297859d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 297959d0f746SFrank Li unsigned int i; 298059d0f746SFrank Li 298159d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) 298259d0f746SFrank Li if (fec_enet_alloc_rxq_buffers(ndev, i)) 298359d0f746SFrank Li return -ENOMEM; 298459d0f746SFrank Li 298559d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) 298659d0f746SFrank Li if (fec_enet_alloc_txq_buffers(ndev, i)) 298759d0f746SFrank Li return -ENOMEM; 298859d0f746SFrank Li return 0; 298959d0f746SFrank Li } 299059d0f746SFrank Li 2991793fc096SFrank Li static int 2992793fc096SFrank Li fec_enet_open(struct net_device *ndev) 2993793fc096SFrank Li { 2994793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2995793fc096SFrank Li int ret; 29961b0a83acSRichard Leitner bool reset_again; 2997793fc096SFrank Li 2998da875fa5SZhang Qilong ret = pm_runtime_resume_and_get(&fep->pdev->dev); 2999b0c6ce24SFabio Estevam if (ret < 0) 30008fff755eSAndrew Lunn return ret; 30018fff755eSAndrew Lunn 30025bbde4d2SNimrod Andy pinctrl_pm_select_default_state(&fep->pdev->dev); 3003e8fcfcd5SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 3004e8fcfcd5SNimrod Andy if (ret) 30058fff755eSAndrew Lunn goto clk_enable; 3006e8fcfcd5SNimrod Andy 30071b0a83acSRichard Leitner /* During the first fec_enet_open call the PHY isn't probed at this 30081b0a83acSRichard Leitner * point. Therefore the phy_reset_after_clk_enable() call within 30091b0a83acSRichard Leitner * fec_enet_clk_enable() fails. As we need this reset in order to be 30101b0a83acSRichard Leitner * sure the PHY is working correctly we check if we need to reset again 30111b0a83acSRichard Leitner * later when the PHY is probed 30121b0a83acSRichard Leitner */ 30131b0a83acSRichard Leitner if (ndev->phydev && ndev->phydev->drv) 30141b0a83acSRichard Leitner reset_again = false; 30151b0a83acSRichard Leitner else 30161b0a83acSRichard Leitner reset_again = true; 30171b0a83acSRichard Leitner 3018793fc096SFrank Li /* I should reset the ring buffers here, but I don't yet know 3019793fc096SFrank Li * a simple way to do that. 3020793fc096SFrank Li */ 3021793fc096SFrank Li 3022793fc096SFrank Li ret = fec_enet_alloc_buffers(ndev); 3023793fc096SFrank Li if (ret) 3024681d2421SFabio Estevam goto err_enet_alloc; 3025793fc096SFrank Li 302655dd2753SNimrod Andy /* Init MAC prior to mii bus probe */ 302755dd2753SNimrod Andy fec_restart(ndev); 302855dd2753SNimrod Andy 30291b0a83acSRichard Leitner /* Call phy_reset_after_clk_enable() again if it failed during 30301b0a83acSRichard Leitner * phy_reset_after_clk_enable() before because the PHY wasn't probed. 30311b0a83acSRichard Leitner */ 30321b0a83acSRichard Leitner if (reset_again) 303364a632daSMarek Vasut fec_enet_phy_reset_after_clk_enable(ndev); 30341b0a83acSRichard Leitner 30350da1ccbbSMarek Vasut /* Probe and connect to PHY when open the interface */ 30360da1ccbbSMarek Vasut ret = fec_enet_mii_probe(ndev); 30370da1ccbbSMarek Vasut if (ret) 30380da1ccbbSMarek Vasut goto err_enet_mii_probe; 3039681d2421SFabio Estevam 3040681d2421SFabio Estevam if (fep->quirks & FEC_QUIRK_ERR006687) 3041793fc096SFrank Li imx6q_cpuidle_fec_irqs_used(); 3042793fc096SFrank Li 3043793fc096SFrank Li napi_enable(&fep->napi); 3044793fc096SFrank Li phy_start(ndev->phydev); 3045793fc096SFrank Li netif_tx_start_all_queues(ndev); 3046793fc096SFrank Li 3047793fc096SFrank Li device_set_wakeup_enable(&ndev->dev, fep->wol_flag & 304845f5c327SPhilippe Reynes FEC_WOL_FLAG_ENABLE); 3049d76cfae9SRussell King 305031a6de34SRussell King return 0; 3051793fc096SFrank Li 3052b49cd504SRussell King err_enet_mii_probe: 3053793fc096SFrank Li fec_enet_free_buffers(ndev); 305431a6de34SRussell King err_enet_alloc: 3055793fc096SFrank Li fec_enet_clk_enable(ndev, false); 305645f5c327SPhilippe Reynes clk_enable: 3057793fc096SFrank Li pm_runtime_mark_last_busy(&fep->pdev->dev); 305829380905SLucas Stach pm_runtime_put_autosuspend(&fep->pdev->dev); 305929380905SLucas Stach pinctrl_pm_select_sleep_state(&fep->pdev->dev); 306029380905SLucas Stach return ret; 306180cca775SNikita Yushchenko } 306280cca775SNikita Yushchenko 3063e8fcfcd5SNimrod Andy static int 30645bbde4d2SNimrod Andy fec_enet_close(struct net_device *ndev) 30658fff755eSAndrew Lunn { 30668fff755eSAndrew Lunn struct fec_enet_private *fep = netdev_priv(ndev); 30678fff755eSAndrew Lunn 3068793fc096SFrank Li phy_stop(ndev->phydev); 3069793fc096SFrank Li 3070793fc096SFrank Li if (netif_device_present(ndev)) { 3071793fc096SFrank Li napi_disable(&fep->napi); 3072793fc096SFrank Li netif_tx_disable(ndev); 3073793fc096SFrank Li fec_stop(ndev); 3074793fc096SFrank Li } 3075793fc096SFrank Li 3076793fc096SFrank Li phy_disconnect(ndev->phydev); 3077793fc096SFrank Li 3078793fc096SFrank Li if (fep->quirks & FEC_QUIRK_ERR006687) 3079793fc096SFrank Li imx6q_cpuidle_fec_irqs_unused(); 3080793fc096SFrank Li 3081793fc096SFrank Li fec_enet_update_ethtool_stats(ndev); 3082793fc096SFrank Li 30836176e89cSJiri Kosina fec_enet_clk_enable(ndev, false); 3084793fc096SFrank Li pinctrl_pm_select_sleep_state(&fep->pdev->dev); 3085793fc096SFrank Li pm_runtime_mark_last_busy(&fep->pdev->dev); 3086793fc096SFrank Li pm_runtime_put_autosuspend(&fep->pdev->dev); 3087793fc096SFrank Li 3088793fc096SFrank Li fec_enet_free_buffers(ndev); 3089793fc096SFrank Li 3090793fc096SFrank Li return 0; 3091793fc096SFrank Li } 309201f8902bSRui Sousa 3093793fc096SFrank Li /* Set or clear the multicast filter for this adaptor. 3094793fc096SFrank Li * Skeleton taken from sunlance driver. 3095793fc096SFrank Li * The CPM Ethernet implementation allows Multicast as well as individual 3096793fc096SFrank Li * MAC address filtering. Some of the drivers check to make sure it is 3097793fc096SFrank Li * a group multicast address, and discard those that are not. I guess I 3098793fc096SFrank Li * will do the same for now, but just remove the test if you want 3099793fc096SFrank Li * individual filtering as well (do the upper net layers want or support 3100793fc096SFrank Li * this kind of feature?). 3101793fc096SFrank Li */ 3102793fc096SFrank Li 3103793fc096SFrank Li #define FEC_HASH_BITS 6 /* #bits in hash */ 3104793fc096SFrank Li 3105793fc096SFrank Li static void set_multicast_list(struct net_device *ndev) 3106793fc096SFrank Li { 3107793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3108793fc096SFrank Li struct netdev_hw_addr *ha; 310916f6e983SKrzysztof Kozlowski unsigned int crc, tmp; 3110793fc096SFrank Li unsigned char hash; 3111793fc096SFrank Li unsigned int hash_high = 0, hash_low = 0; 3112793fc096SFrank Li 3113793fc096SFrank Li if (ndev->flags & IFF_PROMISC) { 3114793fc096SFrank Li tmp = readl(fep->hwp + FEC_R_CNTRL); 3115793fc096SFrank Li tmp |= 0x8; 3116793fc096SFrank Li writel(tmp, fep->hwp + FEC_R_CNTRL); 3117793fc096SFrank Li return; 3118793fc096SFrank Li } 3119793fc096SFrank Li 3120793fc096SFrank Li tmp = readl(fep->hwp + FEC_R_CNTRL); 3121793fc096SFrank Li tmp &= ~0x8; 3122793fc096SFrank Li writel(tmp, fep->hwp + FEC_R_CNTRL); 3123793fc096SFrank Li 3124793fc096SFrank Li if (ndev->flags & IFF_ALLMULTI) { 3125793fc096SFrank Li /* Catch all multicast addresses, so set the 3126793fc096SFrank Li * filter to all 1's 3127793fc096SFrank Li */ 3128793fc096SFrank Li writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 3129793fc096SFrank Li writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 3130793fc096SFrank Li 3131793fc096SFrank Li return; 3132793fc096SFrank Li } 3133793fc096SFrank Li 313401f8902bSRui Sousa /* Add the addresses in hash register */ 3135793fc096SFrank Li netdev_for_each_mc_addr(ha, ndev) { 3136793fc096SFrank Li /* calculate crc32 value of mac address */ 313716f6e983SKrzysztof Kozlowski crc = ether_crc_le(ndev->addr_len, ha->addr); 3138793fc096SFrank Li 31396176e89cSJiri Kosina /* only upper 6 bits (FEC_HASH_BITS) are used 3140981a0547SPeter Meerwald-Stadler * which point to specific bit in the hash registers 3141793fc096SFrank Li */ 31426176e89cSJiri Kosina hash = (crc >> (32 - FEC_HASH_BITS)) & 0x3f; 3143793fc096SFrank Li 314401f8902bSRui Sousa if (hash > 31) 314501f8902bSRui Sousa hash_high |= 1 << (hash - 32); 314601f8902bSRui Sousa else 314701f8902bSRui Sousa hash_low |= 1 << hash; 3148793fc096SFrank Li } 314901f8902bSRui Sousa 315001f8902bSRui Sousa writel(hash_high, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 315101f8902bSRui Sousa writel(hash_low, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 3152793fc096SFrank Li } 3153793fc096SFrank Li 3154793fc096SFrank Li /* Set a MAC change in hardware. */ 3155793fc096SFrank Li static int 3156793fc096SFrank Li fec_set_mac_address(struct net_device *ndev, void *p) 3157793fc096SFrank Li { 3158793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3159793fc096SFrank Li struct sockaddr *addr = p; 3160793fc096SFrank Li 316144934facSLucas Stach if (addr) { 3162793fc096SFrank Li if (!is_valid_ether_addr(addr->sa_data)) 3163793fc096SFrank Li return -EADDRNOTAVAIL; 3164793fc096SFrank Li memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len); 316544934facSLucas Stach } 3166793fc096SFrank Li 31679638d19eSNimrod Andy /* Add netif status check here to avoid system hang in below case: 31689638d19eSNimrod Andy * ifconfig ethx down; ifconfig ethx hw ether xx:xx:xx:xx:xx:xx; 31699638d19eSNimrod Andy * After ethx down, fec all clocks are gated off and then register 31709638d19eSNimrod Andy * access causes system hang. 31719638d19eSNimrod Andy */ 31729638d19eSNimrod Andy if (!netif_running(ndev)) 31739638d19eSNimrod Andy return 0; 31749638d19eSNimrod Andy 3175793fc096SFrank Li writel(ndev->dev_addr[3] | (ndev->dev_addr[2] << 8) | 3176793fc096SFrank Li (ndev->dev_addr[1] << 16) | (ndev->dev_addr[0] << 24), 3177793fc096SFrank Li fep->hwp + FEC_ADDR_LOW); 3178793fc096SFrank Li writel((ndev->dev_addr[5] << 16) | (ndev->dev_addr[4] << 24), 3179793fc096SFrank Li fep->hwp + FEC_ADDR_HIGH); 3180793fc096SFrank Li return 0; 3181793fc096SFrank Li } 3182793fc096SFrank Li 3183793fc096SFrank Li #ifdef CONFIG_NET_POLL_CONTROLLER 3184793fc096SFrank Li /** 3185793fc096SFrank Li * fec_poll_controller - FEC Poll controller function 3186793fc096SFrank Li * @dev: The FEC network adapter 3187793fc096SFrank Li * 3188793fc096SFrank Li * Polled functionality used by netconsole and others in non interrupt mode 3189793fc096SFrank Li * 3190793fc096SFrank Li */ 3191793fc096SFrank Li static void fec_poll_controller(struct net_device *dev) 3192793fc096SFrank Li { 3193793fc096SFrank Li int i; 3194793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(dev); 3195793fc096SFrank Li 3196793fc096SFrank Li for (i = 0; i < FEC_IRQ_NUM; i++) { 3197793fc096SFrank Li if (fep->irq[i] > 0) { 3198793fc096SFrank Li disable_irq(fep->irq[i]); 3199793fc096SFrank Li fec_enet_interrupt(fep->irq[i], dev); 3200793fc096SFrank Li enable_irq(fep->irq[i]); 3201793fc096SFrank Li } 3202793fc096SFrank Li } 3203793fc096SFrank Li } 3204793fc096SFrank Li #endif 3205793fc096SFrank Li 32065bc26726SNimrod Andy static inline void fec_enet_set_netdev_features(struct net_device *netdev, 32074c09eed9SJim Baxter netdev_features_t features) 32084c09eed9SJim Baxter { 32094c09eed9SJim Baxter struct fec_enet_private *fep = netdev_priv(netdev); 32104c09eed9SJim Baxter netdev_features_t changed = features ^ netdev->features; 32114c09eed9SJim Baxter 32124c09eed9SJim Baxter netdev->features = features; 32134c09eed9SJim Baxter 32144c09eed9SJim Baxter /* Receive checksum has been changed */ 32154c09eed9SJim Baxter if (changed & NETIF_F_RXCSUM) { 32164c09eed9SJim Baxter if (features & NETIF_F_RXCSUM) 32174c09eed9SJim Baxter fep->csum_flags |= FLAG_RX_CSUM_ENABLED; 32184c09eed9SJim Baxter else 32194c09eed9SJim Baxter fep->csum_flags &= ~FLAG_RX_CSUM_ENABLED; 32208506fa1dSRussell King } 32215bc26726SNimrod Andy } 32224c09eed9SJim Baxter 32235bc26726SNimrod Andy static int fec_set_features(struct net_device *netdev, 32245bc26726SNimrod Andy netdev_features_t features) 32255bc26726SNimrod Andy { 32265bc26726SNimrod Andy struct fec_enet_private *fep = netdev_priv(netdev); 32275bc26726SNimrod Andy netdev_features_t changed = features ^ netdev->features; 32285bc26726SNimrod Andy 32295b40f709SFabio Estevam if (netif_running(netdev) && changed & NETIF_F_RXCSUM) { 32305bc26726SNimrod Andy napi_disable(&fep->napi); 32315bc26726SNimrod Andy netif_tx_lock_bh(netdev); 32325bc26726SNimrod Andy fec_stop(netdev); 32335bc26726SNimrod Andy fec_enet_set_netdev_features(netdev, features); 3234ef83337dSRussell King fec_restart(netdev); 32354d494cdcSFugang Duan netif_tx_wake_all_queues(netdev); 32366af42d42SRussell King netif_tx_unlock_bh(netdev); 3237dbc64a8eSRussell King napi_enable(&fep->napi); 32385bc26726SNimrod Andy } else { 32395bc26726SNimrod Andy fec_enet_set_netdev_features(netdev, features); 32404c09eed9SJim Baxter } 32414c09eed9SJim Baxter 32424c09eed9SJim Baxter return 0; 32434c09eed9SJim Baxter } 32444c09eed9SJim Baxter 3245*52c4a1a8SFugang Duan static u16 fec_enet_get_raw_vlan_tci(struct sk_buff *skb) 3246*52c4a1a8SFugang Duan { 3247*52c4a1a8SFugang Duan struct vlan_ethhdr *vhdr; 3248*52c4a1a8SFugang Duan unsigned short vlan_TCI = 0; 3249*52c4a1a8SFugang Duan 3250*52c4a1a8SFugang Duan if (skb->protocol == htons(ETH_P_ALL)) { 3251*52c4a1a8SFugang Duan vhdr = (struct vlan_ethhdr *)(skb->data); 3252*52c4a1a8SFugang Duan vlan_TCI = ntohs(vhdr->h_vlan_TCI); 3253*52c4a1a8SFugang Duan } 3254*52c4a1a8SFugang Duan 3255*52c4a1a8SFugang Duan return vlan_TCI; 3256*52c4a1a8SFugang Duan } 3257*52c4a1a8SFugang Duan 3258*52c4a1a8SFugang Duan static u16 fec_enet_select_queue(struct net_device *ndev, struct sk_buff *skb, 3259*52c4a1a8SFugang Duan struct net_device *sb_dev) 3260*52c4a1a8SFugang Duan { 3261*52c4a1a8SFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 3262*52c4a1a8SFugang Duan u16 vlan_tag; 3263*52c4a1a8SFugang Duan 3264*52c4a1a8SFugang Duan if (!(fep->quirks & FEC_QUIRK_HAS_AVB)) 3265*52c4a1a8SFugang Duan return netdev_pick_tx(ndev, skb, NULL); 3266*52c4a1a8SFugang Duan 3267*52c4a1a8SFugang Duan vlan_tag = fec_enet_get_raw_vlan_tci(skb); 3268*52c4a1a8SFugang Duan if (!vlan_tag) 3269*52c4a1a8SFugang Duan return vlan_tag; 3270*52c4a1a8SFugang Duan 3271*52c4a1a8SFugang Duan return fec_enet_vlan_pri_to_queue[vlan_tag >> 13]; 3272*52c4a1a8SFugang Duan } 3273*52c4a1a8SFugang Duan 3274793fc096SFrank Li static const struct net_device_ops fec_netdev_ops = { 3275793fc096SFrank Li .ndo_open = fec_enet_open, 3276793fc096SFrank Li .ndo_stop = fec_enet_close, 3277793fc096SFrank Li .ndo_start_xmit = fec_enet_start_xmit, 3278*52c4a1a8SFugang Duan .ndo_select_queue = fec_enet_select_queue, 3279793fc096SFrank Li .ndo_set_rx_mode = set_multicast_list, 3280793fc096SFrank Li .ndo_validate_addr = eth_validate_addr, 3281793fc096SFrank Li .ndo_tx_timeout = fec_timeout, 3282793fc096SFrank Li .ndo_set_mac_address = fec_set_mac_address, 3283793fc096SFrank Li .ndo_do_ioctl = fec_enet_ioctl, 3284793fc096SFrank Li #ifdef CONFIG_NET_POLL_CONTROLLER 3285793fc096SFrank Li .ndo_poll_controller = fec_poll_controller, 3286793fc096SFrank Li #endif 32874c09eed9SJim Baxter .ndo_set_features = fec_set_features, 3288793fc096SFrank Li }; 3289793fc096SFrank Li 329053bb20d1STroy Kisky static const unsigned short offset_des_active_rxq[] = { 329153bb20d1STroy Kisky FEC_R_DES_ACTIVE_0, FEC_R_DES_ACTIVE_1, FEC_R_DES_ACTIVE_2 329253bb20d1STroy Kisky }; 329353bb20d1STroy Kisky 329453bb20d1STroy Kisky static const unsigned short offset_des_active_txq[] = { 329553bb20d1STroy Kisky FEC_X_DES_ACTIVE_0, FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2 329653bb20d1STroy Kisky }; 329753bb20d1STroy Kisky 3298793fc096SFrank Li /* 3299793fc096SFrank Li * XXX: We need to clean up on failure exits here. 3300793fc096SFrank Li * 3301793fc096SFrank Li */ 3302793fc096SFrank Li static int fec_enet_init(struct net_device *ndev) 3303793fc096SFrank Li { 3304793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3305793fc096SFrank Li struct bufdesc *cbd_base; 33064d494cdcSFugang Duan dma_addr_t bd_dma; 330755d0218aSNimrod Andy int bd_size; 330859d0f746SFrank Li unsigned int i; 33097355f276STroy Kisky unsigned dsize = fep->bufdesc_ex ? sizeof(struct bufdesc_ex) : 33107355f276STroy Kisky sizeof(struct bufdesc); 33117355f276STroy Kisky unsigned dsize_log2 = __fls(dsize); 3312453e9dc4SStefan Agner int ret; 331355d0218aSNimrod Andy 33147355f276STroy Kisky WARN_ON(dsize != (1 << dsize_log2)); 33153f1dcc6aSLucas Stach #if defined(CONFIG_ARM) || defined(CONFIG_ARM64) 331641ef84ceSFugang Duan fep->rx_align = 0xf; 331741ef84ceSFugang Duan fep->tx_align = 0xf; 331841ef84ceSFugang Duan #else 331941ef84ceSFugang Duan fep->rx_align = 0x3; 332041ef84ceSFugang Duan fep->tx_align = 0x3; 332141ef84ceSFugang Duan #endif 332241ef84ceSFugang Duan 3323453e9dc4SStefan Agner /* Check mask of the streaming and coherent API */ 3324453e9dc4SStefan Agner ret = dma_set_mask_and_coherent(&fep->pdev->dev, DMA_BIT_MASK(32)); 3325453e9dc4SStefan Agner if (ret < 0) { 3326453e9dc4SStefan Agner dev_warn(&fep->pdev->dev, "No suitable DMA available\n"); 3327453e9dc4SStefan Agner return ret; 3328453e9dc4SStefan Agner } 3329453e9dc4SStefan Agner 3330619fee9eSFugang Duan ret = fec_enet_alloc_queue(ndev); 3331619fee9eSFugang Duan if (ret) 3332619fee9eSFugang Duan return ret; 333379f33912SNimrod Andy 33347355f276STroy Kisky bd_size = (fep->total_tx_ring_size + fep->total_rx_ring_size) * dsize; 3335793fc096SFrank Li 3336793fc096SFrank Li /* Allocate memory for buffer descriptors. */ 3337c0a1a0a6SLucas Stach cbd_base = dmam_alloc_coherent(&fep->pdev->dev, bd_size, &bd_dma, 3338793fc096SFrank Li GFP_KERNEL); 33394d494cdcSFugang Duan if (!cbd_base) { 3340619fee9eSFugang Duan ret = -ENOMEM; 3341619fee9eSFugang Duan goto free_queue_mem; 33424d494cdcSFugang Duan } 3343793fc096SFrank Li 3344793fc096SFrank Li /* Get the Ethernet address */ 3345052fcc45SFugang Duan ret = fec_get_mac(ndev); 3346052fcc45SFugang Duan if (ret) 3347052fcc45SFugang Duan goto free_queue_mem; 3348052fcc45SFugang Duan 334944934facSLucas Stach /* make sure MAC we just acquired is programmed into the hw */ 335044934facSLucas Stach fec_set_mac_address(ndev, NULL); 3351793fc096SFrank Li 3352793fc096SFrank Li /* Set receive and transmit descriptor base. */ 335359d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) { 33547355f276STroy Kisky struct fec_enet_priv_rx_q *rxq = fep->rx_queue[i]; 33557355f276STroy Kisky unsigned size = dsize * rxq->bd.ring_size; 33567355f276STroy Kisky 33577355f276STroy Kisky rxq->bd.qid = i; 33587355f276STroy Kisky rxq->bd.base = cbd_base; 33597355f276STroy Kisky rxq->bd.cur = cbd_base; 33607355f276STroy Kisky rxq->bd.dma = bd_dma; 33617355f276STroy Kisky rxq->bd.dsize = dsize; 33627355f276STroy Kisky rxq->bd.dsize_log2 = dsize_log2; 336353bb20d1STroy Kisky rxq->bd.reg_desc_active = fep->hwp + offset_des_active_rxq[i]; 33647355f276STroy Kisky bd_dma += size; 33657355f276STroy Kisky cbd_base = (struct bufdesc *)(((void *)cbd_base) + size); 33667355f276STroy Kisky rxq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize); 336759d0f746SFrank Li } 336859d0f746SFrank Li 336959d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 33707355f276STroy Kisky struct fec_enet_priv_tx_q *txq = fep->tx_queue[i]; 33717355f276STroy Kisky unsigned size = dsize * txq->bd.ring_size; 33727355f276STroy Kisky 33737355f276STroy Kisky txq->bd.qid = i; 33747355f276STroy Kisky txq->bd.base = cbd_base; 33757355f276STroy Kisky txq->bd.cur = cbd_base; 33767355f276STroy Kisky txq->bd.dma = bd_dma; 33777355f276STroy Kisky txq->bd.dsize = dsize; 33787355f276STroy Kisky txq->bd.dsize_log2 = dsize_log2; 337953bb20d1STroy Kisky txq->bd.reg_desc_active = fep->hwp + offset_des_active_txq[i]; 33807355f276STroy Kisky bd_dma += size; 33817355f276STroy Kisky cbd_base = (struct bufdesc *)(((void *)cbd_base) + size); 33827355f276STroy Kisky txq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize); 338359d0f746SFrank Li } 33844d494cdcSFugang Duan 3385793fc096SFrank Li 3386793fc096SFrank Li /* The FEC Ethernet specific entries in the device structure */ 3387793fc096SFrank Li ndev->watchdog_timeo = TX_TIMEOUT; 3388793fc096SFrank Li ndev->netdev_ops = &fec_netdev_ops; 3389793fc096SFrank Li ndev->ethtool_ops = &fec_enet_ethtool_ops; 3390793fc096SFrank Li 3391793fc096SFrank Li writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK); 3392322555f5SFabio Estevam netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, NAPI_POLL_WEIGHT); 3393793fc096SFrank Li 33946b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_VLAN) 3395cdffcf1bSJim Baxter /* enable hw VLAN support */ 3396cdffcf1bSJim Baxter ndev->features |= NETIF_F_HW_VLAN_CTAG_RX; 3397cdffcf1bSJim Baxter 33986b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_CSUM) { 339979f33912SNimrod Andy ndev->gso_max_segs = FEC_MAX_TSO_SEGS; 340079f33912SNimrod Andy 34014c09eed9SJim Baxter /* enable hw accelerator */ 34024c09eed9SJim Baxter ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM 340379f33912SNimrod Andy | NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO); 34044c09eed9SJim Baxter fep->csum_flags |= FLAG_RX_CSUM_ENABLED; 340548496255SShawn Guo } 34064c09eed9SJim Baxter 3407471ff445SJoakim Zhang if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) { 340841ef84ceSFugang Duan fep->tx_align = 0; 340941ef84ceSFugang Duan fep->rx_align = 0x3f; 341041ef84ceSFugang Duan } 341141ef84ceSFugang Duan 341209d1e541SNimrod Andy ndev->hw_features = ndev->features; 341309d1e541SNimrod Andy 3414ef83337dSRussell King fec_restart(ndev); 3415793fc096SFrank Li 34162b30842bSAndrew Lunn if (fep->quirks & FEC_QUIRK_MIB_CLEAR) 34172b30842bSAndrew Lunn fec_enet_clear_ethtool_stats(ndev); 34182b30842bSAndrew Lunn else 341980cca775SNikita Yushchenko fec_enet_update_ethtool_stats(ndev); 342080cca775SNikita Yushchenko 3421793fc096SFrank Li return 0; 3422619fee9eSFugang Duan 3423619fee9eSFugang Duan free_queue_mem: 3424619fee9eSFugang Duan fec_enet_free_queue(ndev); 3425619fee9eSFugang Duan return ret; 3426793fc096SFrank Li } 3427793fc096SFrank Li 3428793fc096SFrank Li #ifdef CONFIG_OF 34299269e556SFugang Duan static int fec_reset_phy(struct platform_device *pdev) 3430793fc096SFrank Li { 3431793fc096SFrank Li int err, phy_reset; 3432962d8cdcSBernhard Walle bool active_high = false; 3433159a0760SQuentin Schulz int msec = 1, phy_post_delay = 0; 3434793fc096SFrank Li struct device_node *np = pdev->dev.of_node; 3435793fc096SFrank Li 3436793fc096SFrank Li if (!np) 34379269e556SFugang Duan return 0; 3438793fc096SFrank Li 343961e04ccbSFugang Duan err = of_property_read_u32(np, "phy-reset-duration", &msec); 3440793fc096SFrank Li /* A sane reset duration should not be longer than 1s */ 344161e04ccbSFugang Duan if (!err && msec > 1000) 3442793fc096SFrank Li msec = 1; 3443793fc096SFrank Li 3444793fc096SFrank Li phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0); 34459269e556SFugang Duan if (phy_reset == -EPROBE_DEFER) 34469269e556SFugang Duan return phy_reset; 34479269e556SFugang Duan else if (!gpio_is_valid(phy_reset)) 34489269e556SFugang Duan return 0; 3449793fc096SFrank Li 3450159a0760SQuentin Schulz err = of_property_read_u32(np, "phy-reset-post-delay", &phy_post_delay); 3451159a0760SQuentin Schulz /* valid reset duration should be less than 1s */ 3452159a0760SQuentin Schulz if (!err && phy_post_delay > 1000) 3453159a0760SQuentin Schulz return -EINVAL; 3454159a0760SQuentin Schulz 3455962d8cdcSBernhard Walle active_high = of_property_read_bool(np, "phy-reset-active-high"); 345664f10f6eSBernhard Walle 3457793fc096SFrank Li err = devm_gpio_request_one(&pdev->dev, phy_reset, 3458962d8cdcSBernhard Walle active_high ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, 345964f10f6eSBernhard Walle "phy-reset"); 3460793fc096SFrank Li if (err) { 3461793fc096SFrank Li dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err); 34629269e556SFugang Duan return err; 3463793fc096SFrank Li } 3464eb37c563SStefan Wahren 3465eb37c563SStefan Wahren if (msec > 20) 3466793fc096SFrank Li msleep(msec); 3467eb37c563SStefan Wahren else 3468eb37c563SStefan Wahren usleep_range(msec * 1000, msec * 1000 + 1000); 3469eb37c563SStefan Wahren 3470962d8cdcSBernhard Walle gpio_set_value_cansleep(phy_reset, !active_high); 34719269e556SFugang Duan 3472159a0760SQuentin Schulz if (!phy_post_delay) 3473159a0760SQuentin Schulz return 0; 3474159a0760SQuentin Schulz 3475159a0760SQuentin Schulz if (phy_post_delay > 20) 3476159a0760SQuentin Schulz msleep(phy_post_delay); 3477159a0760SQuentin Schulz else 3478159a0760SQuentin Schulz usleep_range(phy_post_delay * 1000, 3479159a0760SQuentin Schulz phy_post_delay * 1000 + 1000); 3480159a0760SQuentin Schulz 34819269e556SFugang Duan return 0; 3482793fc096SFrank Li } 3483793fc096SFrank Li #else /* CONFIG_OF */ 34849269e556SFugang Duan static int fec_reset_phy(struct platform_device *pdev) 3485793fc096SFrank Li { 3486793fc096SFrank Li /* 3487793fc096SFrank Li * In case of platform probe, the reset has been done 3488793fc096SFrank Li * by machine code. 3489793fc096SFrank Li */ 34909269e556SFugang Duan return 0; 3491793fc096SFrank Li } 3492793fc096SFrank Li #endif /* CONFIG_OF */ 3493793fc096SFrank Li 34949fc095f1SFugang Duan static void 34959fc095f1SFugang Duan fec_enet_get_queue_num(struct platform_device *pdev, int *num_tx, int *num_rx) 34969fc095f1SFugang Duan { 34979fc095f1SFugang Duan struct device_node *np = pdev->dev.of_node; 34989fc095f1SFugang Duan 34999fc095f1SFugang Duan *num_tx = *num_rx = 1; 35009fc095f1SFugang Duan 35019fc095f1SFugang Duan if (!np || !of_device_is_available(np)) 35029fc095f1SFugang Duan return; 35039fc095f1SFugang Duan 35049fc095f1SFugang Duan /* parse the num of tx and rx queues */ 350573b1c90dSSaurabh Sengar of_property_read_u32(np, "fsl,num-tx-queues", num_tx); 3506b7bd75cfSFrank Li 350773b1c90dSSaurabh Sengar of_property_read_u32(np, "fsl,num-rx-queues", num_rx); 35089fc095f1SFugang Duan 35099fc095f1SFugang Duan if (*num_tx < 1 || *num_tx > FEC_ENET_MAX_TX_QS) { 3510b7bd75cfSFrank Li dev_warn(&pdev->dev, "Invalid num_tx(=%d), fall back to 1\n", 35119fc095f1SFugang Duan *num_tx); 35129fc095f1SFugang Duan *num_tx = 1; 35139fc095f1SFugang Duan return; 35149fc095f1SFugang Duan } 35159fc095f1SFugang Duan 35169fc095f1SFugang Duan if (*num_rx < 1 || *num_rx > FEC_ENET_MAX_RX_QS) { 3517b7bd75cfSFrank Li dev_warn(&pdev->dev, "Invalid num_rx(=%d), fall back to 1\n", 35189fc095f1SFugang Duan *num_rx); 35199fc095f1SFugang Duan *num_rx = 1; 35209fc095f1SFugang Duan return; 35219fc095f1SFugang Duan } 35229fc095f1SFugang Duan 35239fc095f1SFugang Duan } 35249fc095f1SFugang Duan 35254ad1ceecSTroy Kisky static int fec_enet_get_irq_cnt(struct platform_device *pdev) 35264ad1ceecSTroy Kisky { 35274ad1ceecSTroy Kisky int irq_cnt = platform_irq_count(pdev); 35284ad1ceecSTroy Kisky 35294ad1ceecSTroy Kisky if (irq_cnt > FEC_IRQ_NUM) 35304ad1ceecSTroy Kisky irq_cnt = FEC_IRQ_NUM; /* last for pps */ 35314ad1ceecSTroy Kisky else if (irq_cnt == 2) 35324ad1ceecSTroy Kisky irq_cnt = 1; /* last for pps */ 35334ad1ceecSTroy Kisky else if (irq_cnt <= 0) 35344ad1ceecSTroy Kisky irq_cnt = 1; /* At least 1 irq is needed */ 35354ad1ceecSTroy Kisky return irq_cnt; 35364ad1ceecSTroy Kisky } 35374ad1ceecSTroy Kisky 3538da722186SMartin Fuzzey static int fec_enet_init_stop_mode(struct fec_enet_private *fep, 3539da722186SMartin Fuzzey struct device_node *np) 3540da722186SMartin Fuzzey { 3541da722186SMartin Fuzzey struct device_node *gpr_np; 35428a448bf8SFugang Duan u32 out_val[3]; 3543da722186SMartin Fuzzey int ret = 0; 3544da722186SMartin Fuzzey 35458a448bf8SFugang Duan gpr_np = of_parse_phandle(np, "fsl,stop-mode", 0); 3546da722186SMartin Fuzzey if (!gpr_np) 3547da722186SMartin Fuzzey return 0; 3548da722186SMartin Fuzzey 35498a448bf8SFugang Duan ret = of_property_read_u32_array(np, "fsl,stop-mode", out_val, 35508a448bf8SFugang Duan ARRAY_SIZE(out_val)); 35518a448bf8SFugang Duan if (ret) { 35528a448bf8SFugang Duan dev_dbg(&fep->pdev->dev, "no stop mode property\n"); 35538a448bf8SFugang Duan return ret; 35548a448bf8SFugang Duan } 35558a448bf8SFugang Duan 3556da722186SMartin Fuzzey fep->stop_gpr.gpr = syscon_node_to_regmap(gpr_np); 3557da722186SMartin Fuzzey if (IS_ERR(fep->stop_gpr.gpr)) { 3558da722186SMartin Fuzzey dev_err(&fep->pdev->dev, "could not find gpr regmap\n"); 3559da722186SMartin Fuzzey ret = PTR_ERR(fep->stop_gpr.gpr); 3560da722186SMartin Fuzzey fep->stop_gpr.gpr = NULL; 3561da722186SMartin Fuzzey goto out; 3562da722186SMartin Fuzzey } 3563da722186SMartin Fuzzey 35648a448bf8SFugang Duan fep->stop_gpr.reg = out_val[1]; 35658a448bf8SFugang Duan fep->stop_gpr.bit = out_val[2]; 3566da722186SMartin Fuzzey 3567da722186SMartin Fuzzey out: 3568da722186SMartin Fuzzey of_node_put(gpr_np); 3569da722186SMartin Fuzzey 3570da722186SMartin Fuzzey return ret; 3571da722186SMartin Fuzzey } 3572da722186SMartin Fuzzey 3573793fc096SFrank Li static int 3574793fc096SFrank Li fec_probe(struct platform_device *pdev) 3575793fc096SFrank Li { 3576793fc096SFrank Li struct fec_enet_private *fep; 3577793fc096SFrank Li struct fec_platform_data *pdata; 35780c65b2b9SAndrew Lunn phy_interface_t interface; 3579793fc096SFrank Li struct net_device *ndev; 3580793fc096SFrank Li int i, irq, ret = 0; 3581793fc096SFrank Li const struct of_device_id *of_id; 3582793fc096SFrank Li static int dev_id; 3583407066f8SUwe Kleine-König struct device_node *np = pdev->dev.of_node, *phy_node; 3584b7bd75cfSFrank Li int num_tx_qs; 3585b7bd75cfSFrank Li int num_rx_qs; 35864ad1ceecSTroy Kisky char irq_name[8]; 35874ad1ceecSTroy Kisky int irq_cnt; 3588da722186SMartin Fuzzey struct fec_devinfo *dev_info; 3589793fc096SFrank Li 35909fc095f1SFugang Duan fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs); 35919fc095f1SFugang Duan 3592793fc096SFrank Li /* Init network device */ 359380cca775SNikita Yushchenko ndev = alloc_etherdev_mqs(sizeof(struct fec_enet_private) + 3594f85de666SNikita Yushchenko FEC_STATS_SIZE, num_tx_qs, num_rx_qs); 3595793fc096SFrank Li if (!ndev) 3596793fc096SFrank Li return -ENOMEM; 3597793fc096SFrank Li 3598793fc096SFrank Li SET_NETDEV_DEV(ndev, &pdev->dev); 3599793fc096SFrank Li 3600793fc096SFrank Li /* setup board info structure */ 3601793fc096SFrank Li fep = netdev_priv(ndev); 3602793fc096SFrank Li 36036b7e4008SLothar Waßmann of_id = of_match_device(fec_dt_ids, &pdev->dev); 36046b7e4008SLothar Waßmann if (of_id) 36056b7e4008SLothar Waßmann pdev->id_entry = of_id->data; 3606da722186SMartin Fuzzey dev_info = (struct fec_devinfo *)pdev->id_entry->driver_data; 3607da722186SMartin Fuzzey if (dev_info) 3608da722186SMartin Fuzzey fep->quirks = dev_info->quirks; 36096b7e4008SLothar Waßmann 36100c818594SHubert Feurstein fep->netdev = ndev; 36119fc095f1SFugang Duan fep->num_rx_queues = num_rx_qs; 36129fc095f1SFugang Duan fep->num_tx_queues = num_tx_qs; 36139fc095f1SFugang Duan 3614d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 3615793fc096SFrank Li /* default enable pause frame auto negotiation */ 36166b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_GBIT) 3617793fc096SFrank Li fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG; 3618d1391930SGuenter Roeck #endif 3619793fc096SFrank Li 36205bbde4d2SNimrod Andy /* Select default pin state */ 36215bbde4d2SNimrod Andy pinctrl_pm_select_default_state(&pdev->dev); 36225bbde4d2SNimrod Andy 36234f830a5aSYueHaibing fep->hwp = devm_platform_ioremap_resource(pdev, 0); 3624941e173aSTushar Behera if (IS_ERR(fep->hwp)) { 3625941e173aSTushar Behera ret = PTR_ERR(fep->hwp); 3626941e173aSTushar Behera goto failed_ioremap; 3627941e173aSTushar Behera } 3628941e173aSTushar Behera 3629793fc096SFrank Li fep->pdev = pdev; 3630793fc096SFrank Li fep->dev_id = dev_id++; 3631793fc096SFrank Li 3632793fc096SFrank Li platform_set_drvdata(pdev, ndev); 3633793fc096SFrank Li 363429380905SLucas Stach if ((of_machine_is_compatible("fsl,imx6q") || 363529380905SLucas Stach of_machine_is_compatible("fsl,imx6dl")) && 363629380905SLucas Stach !of_property_read_bool(np, "fsl,err006687-workaround-present")) 363729380905SLucas Stach fep->quirks |= FEC_QUIRK_ERR006687; 363829380905SLucas Stach 3639de40ed31SNimrod Andy if (of_get_property(np, "fsl,magic-packet", NULL)) 3640de40ed31SNimrod Andy fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET; 3641de40ed31SNimrod Andy 36428a448bf8SFugang Duan ret = fec_enet_init_stop_mode(fep, np); 3643da722186SMartin Fuzzey if (ret) 3644da722186SMartin Fuzzey goto failed_stop_mode; 3645da722186SMartin Fuzzey 3646407066f8SUwe Kleine-König phy_node = of_parse_phandle(np, "phy-handle", 0); 3647407066f8SUwe Kleine-König if (!phy_node && of_phy_is_fixed_link(np)) { 3648407066f8SUwe Kleine-König ret = of_phy_register_fixed_link(np); 3649407066f8SUwe Kleine-König if (ret < 0) { 3650407066f8SUwe Kleine-König dev_err(&pdev->dev, 3651407066f8SUwe Kleine-König "broken fixed-link specification\n"); 3652407066f8SUwe Kleine-König goto failed_phy; 3653407066f8SUwe Kleine-König } 3654407066f8SUwe Kleine-König phy_node = of_node_get(np); 3655407066f8SUwe Kleine-König } 3656407066f8SUwe Kleine-König fep->phy_node = phy_node; 3657407066f8SUwe Kleine-König 36580c65b2b9SAndrew Lunn ret = of_get_phy_mode(pdev->dev.of_node, &interface); 36590c65b2b9SAndrew Lunn if (ret) { 366094660ba0SJingoo Han pdata = dev_get_platdata(&pdev->dev); 3661793fc096SFrank Li if (pdata) 3662793fc096SFrank Li fep->phy_interface = pdata->phy; 3663793fc096SFrank Li else 3664793fc096SFrank Li fep->phy_interface = PHY_INTERFACE_MODE_MII; 3665793fc096SFrank Li } else { 36660c65b2b9SAndrew Lunn fep->phy_interface = interface; 3667793fc096SFrank Li } 3668793fc096SFrank Li 3669793fc096SFrank Li fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); 3670793fc096SFrank Li if (IS_ERR(fep->clk_ipg)) { 3671793fc096SFrank Li ret = PTR_ERR(fep->clk_ipg); 3672793fc096SFrank Li goto failed_clk; 3673793fc096SFrank Li } 3674793fc096SFrank Li 3675793fc096SFrank Li fep->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); 3676793fc096SFrank Li if (IS_ERR(fep->clk_ahb)) { 3677793fc096SFrank Li ret = PTR_ERR(fep->clk_ahb); 3678793fc096SFrank Li goto failed_clk; 3679793fc096SFrank Li } 3680793fc096SFrank Li 3681d851b47bSFugang Duan fep->itr_clk_rate = clk_get_rate(fep->clk_ahb); 3682d851b47bSFugang Duan 368338f56f33SLinus Torvalds /* enet_out is optional, depends on board */ 368438f56f33SLinus Torvalds fep->clk_enet_out = devm_clk_get(&pdev->dev, "enet_out"); 368538f56f33SLinus Torvalds if (IS_ERR(fep->clk_enet_out)) 368638f56f33SLinus Torvalds fep->clk_enet_out = NULL; 368738f56f33SLinus Torvalds 368891c0d987SNimrod Andy fep->ptp_clk_on = false; 368991c0d987SNimrod Andy mutex_init(&fep->ptp_clk_mutex); 36909b5330edSFugang Duan 36919b5330edSFugang Duan /* clk_ref is optional, depends on board */ 36929b5330edSFugang Duan fep->clk_ref = devm_clk_get(&pdev->dev, "enet_clk_ref"); 36939b5330edSFugang Duan if (IS_ERR(fep->clk_ref)) 36949b5330edSFugang Duan fep->clk_ref = NULL; 36959b5330edSFugang Duan 36966b7e4008SLothar Waßmann fep->bufdesc_ex = fep->quirks & FEC_QUIRK_HAS_BUFDESC_EX; 3697793fc096SFrank Li fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp"); 3698793fc096SFrank Li if (IS_ERR(fep->clk_ptp)) { 369938f56f33SLinus Torvalds fep->clk_ptp = NULL; 3700217b5844SLothar Waßmann fep->bufdesc_ex = false; 3701793fc096SFrank Li } 3702793fc096SFrank Li 3703e8fcfcd5SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 370413a097bdSFabio Estevam if (ret) 370513a097bdSFabio Estevam goto failed_clk; 370613a097bdSFabio Estevam 37078fff755eSAndrew Lunn ret = clk_prepare_enable(fep->clk_ipg); 37088fff755eSAndrew Lunn if (ret) 37098fff755eSAndrew Lunn goto failed_clk_ipg; 3710d7c3a206SAndy Duan ret = clk_prepare_enable(fep->clk_ahb); 3711d7c3a206SAndy Duan if (ret) 3712d7c3a206SAndy Duan goto failed_clk_ahb; 37138fff755eSAndrew Lunn 371425974d8aSStefan Agner fep->reg_phy = devm_regulator_get_optional(&pdev->dev, "phy"); 3715f4e9f3d2SFabio Estevam if (!IS_ERR(fep->reg_phy)) { 3716f4e9f3d2SFabio Estevam ret = regulator_enable(fep->reg_phy); 3717793fc096SFrank Li if (ret) { 3718793fc096SFrank Li dev_err(&pdev->dev, 3719793fc096SFrank Li "Failed to enable phy regulator: %d\n", ret); 3720793fc096SFrank Li goto failed_regulator; 3721793fc096SFrank Li } 3722f6a4d607SFabio Estevam } else { 37233f38c683SFugang Duan if (PTR_ERR(fep->reg_phy) == -EPROBE_DEFER) { 37243f38c683SFugang Duan ret = -EPROBE_DEFER; 37253f38c683SFugang Duan goto failed_regulator; 37263f38c683SFugang Duan } 3727f6a4d607SFabio Estevam fep->reg_phy = NULL; 3728793fc096SFrank Li } 3729793fc096SFrank Li 37308fff755eSAndrew Lunn pm_runtime_set_autosuspend_delay(&pdev->dev, FEC_MDIO_PM_TIMEOUT); 37318fff755eSAndrew Lunn pm_runtime_use_autosuspend(&pdev->dev); 373214d2b7c1SLucas Stach pm_runtime_get_noresume(&pdev->dev); 37338fff755eSAndrew Lunn pm_runtime_set_active(&pdev->dev); 37348fff755eSAndrew Lunn pm_runtime_enable(&pdev->dev); 37358fff755eSAndrew Lunn 37369269e556SFugang Duan ret = fec_reset_phy(pdev); 37379269e556SFugang Duan if (ret) 37389269e556SFugang Duan goto failed_reset; 3739793fc096SFrank Li 37404ad1ceecSTroy Kisky irq_cnt = fec_enet_get_irq_cnt(pdev); 3741793fc096SFrank Li if (fep->bufdesc_ex) 37424ad1ceecSTroy Kisky fec_ptp_init(pdev, irq_cnt); 3743793fc096SFrank Li 3744793fc096SFrank Li ret = fec_enet_init(ndev); 3745793fc096SFrank Li if (ret) 3746793fc096SFrank Li goto failed_init; 3747793fc096SFrank Li 37484ad1ceecSTroy Kisky for (i = 0; i < irq_cnt; i++) { 37493ded9f2bSArnd Bergmann snprintf(irq_name, sizeof(irq_name), "int%d", i); 37503b56be21SAnson Huang irq = platform_get_irq_byname_optional(pdev, irq_name); 37514ad1ceecSTroy Kisky if (irq < 0) 3752793fc096SFrank Li irq = platform_get_irq(pdev, i); 3753793fc096SFrank Li if (irq < 0) { 3754793fc096SFrank Li ret = irq; 3755793fc096SFrank Li goto failed_irq; 3756793fc096SFrank Li } 37570d9b2ab1SFabio Estevam ret = devm_request_irq(&pdev->dev, irq, fec_enet_interrupt, 375844a272ddSMichael Opdenacker 0, pdev->name, ndev); 37590d9b2ab1SFabio Estevam if (ret) 3760793fc096SFrank Li goto failed_irq; 3761de40ed31SNimrod Andy 3762de40ed31SNimrod Andy fep->irq[i] = irq; 3763793fc096SFrank Li } 3764793fc096SFrank Li 3765793fc096SFrank Li ret = fec_enet_mii_init(pdev); 3766793fc096SFrank Li if (ret) 3767793fc096SFrank Li goto failed_mii_init; 3768793fc096SFrank Li 3769793fc096SFrank Li /* Carrier starts down, phylib will bring it up */ 3770793fc096SFrank Li netif_carrier_off(ndev); 3771e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 37725bbde4d2SNimrod Andy pinctrl_pm_select_sleep_state(&pdev->dev); 3773793fc096SFrank Li 377459193053SAndrew Lunn ndev->max_mtu = PKT_MAXBUF_SIZE - ETH_HLEN - ETH_FCS_LEN; 377559193053SAndrew Lunn 3776793fc096SFrank Li ret = register_netdev(ndev); 3777793fc096SFrank Li if (ret) 3778793fc096SFrank Li goto failed_register; 3779793fc096SFrank Li 3780de40ed31SNimrod Andy device_init_wakeup(&ndev->dev, fep->wol_flag & 3781de40ed31SNimrod Andy FEC_WOL_HAS_MAGIC_PACKET); 3782de40ed31SNimrod Andy 3783eb1d0640SFabio Estevam if (fep->bufdesc_ex && fep->ptp_clock) 3784eb1d0640SFabio Estevam netdev_info(ndev, "registered PHC device %d\n", fep->dev_id); 3785eb1d0640SFabio Estevam 37861b7bde6dSNimrod Andy fep->rx_copybreak = COPYBREAK_DEFAULT; 378736cdc743SRussell King INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work); 37888fff755eSAndrew Lunn 37898fff755eSAndrew Lunn pm_runtime_mark_last_busy(&pdev->dev); 37908fff755eSAndrew Lunn pm_runtime_put_autosuspend(&pdev->dev); 37918fff755eSAndrew Lunn 3792793fc096SFrank Li return 0; 3793793fc096SFrank Li 3794793fc096SFrank Li failed_register: 3795793fc096SFrank Li fec_enet_mii_remove(fep); 3796793fc096SFrank Li failed_mii_init: 37977a2bbd8dSFabio Estevam failed_irq: 37987a2bbd8dSFabio Estevam failed_init: 379932cba57bSLucas Stach fec_ptp_stop(pdev); 38009269e556SFugang Duan failed_reset: 3801ce8d24f9SAndy Duan pm_runtime_put_noidle(&pdev->dev); 38029269e556SFugang Duan pm_runtime_disable(&pdev->dev); 3803c6165cf0SFugang Duan if (fep->reg_phy) 3804c6165cf0SFugang Duan regulator_disable(fep->reg_phy); 3805793fc096SFrank Li failed_regulator: 3806d7c3a206SAndy Duan clk_disable_unprepare(fep->clk_ahb); 3807d7c3a206SAndy Duan failed_clk_ahb: 3808d7c3a206SAndy Duan clk_disable_unprepare(fep->clk_ipg); 38098fff755eSAndrew Lunn failed_clk_ipg: 3810e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 3811793fc096SFrank Li failed_clk: 381282005b1cSJohan Hovold if (of_phy_is_fixed_link(np)) 381382005b1cSJohan Hovold of_phy_deregister_fixed_link(np); 3814407066f8SUwe Kleine-König of_node_put(phy_node); 3815da722186SMartin Fuzzey failed_stop_mode: 3816d1616f07SFugang Duan failed_phy: 3817d1616f07SFugang Duan dev_id--; 3818793fc096SFrank Li failed_ioremap: 3819793fc096SFrank Li free_netdev(ndev); 3820793fc096SFrank Li 3821793fc096SFrank Li return ret; 3822793fc096SFrank Li } 3823793fc096SFrank Li 3824793fc096SFrank Li static int 3825793fc096SFrank Li fec_drv_remove(struct platform_device *pdev) 3826793fc096SFrank Li { 3827793fc096SFrank Li struct net_device *ndev = platform_get_drvdata(pdev); 3828793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 382982005b1cSJohan Hovold struct device_node *np = pdev->dev.of_node; 3830a31eda65SChuhong Yuan int ret; 3831a31eda65SChuhong Yuan 3832da875fa5SZhang Qilong ret = pm_runtime_resume_and_get(&pdev->dev); 3833a31eda65SChuhong Yuan if (ret < 0) 3834a31eda65SChuhong Yuan return ret; 3835793fc096SFrank Li 383636cdc743SRussell King cancel_work_sync(&fep->tx_timeout_work); 383732cba57bSLucas Stach fec_ptp_stop(pdev); 3838793fc096SFrank Li unregister_netdev(ndev); 3839793fc096SFrank Li fec_enet_mii_remove(fep); 3840f6a4d607SFabio Estevam if (fep->reg_phy) 3841f6a4d607SFabio Estevam regulator_disable(fep->reg_phy); 3842a31eda65SChuhong Yuan 384382005b1cSJohan Hovold if (of_phy_is_fixed_link(np)) 384482005b1cSJohan Hovold of_phy_deregister_fixed_link(np); 3845407066f8SUwe Kleine-König of_node_put(fep->phy_node); 3846793fc096SFrank Li free_netdev(ndev); 3847793fc096SFrank Li 3848a31eda65SChuhong Yuan clk_disable_unprepare(fep->clk_ahb); 3849a31eda65SChuhong Yuan clk_disable_unprepare(fep->clk_ipg); 3850a31eda65SChuhong Yuan pm_runtime_put_noidle(&pdev->dev); 3851a31eda65SChuhong Yuan pm_runtime_disable(&pdev->dev); 3852a31eda65SChuhong Yuan 3853793fc096SFrank Li return 0; 3854793fc096SFrank Li } 3855793fc096SFrank Li 3856dd66d386SFabio Estevam static int __maybe_unused fec_suspend(struct device *dev) 3857793fc096SFrank Li { 3858793fc096SFrank Li struct net_device *ndev = dev_get_drvdata(dev); 3859793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3860793fc096SFrank Li 3861da1774e5SRussell King rtnl_lock(); 3862793fc096SFrank Li if (netif_running(ndev)) { 3863de40ed31SNimrod Andy if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) 3864de40ed31SNimrod Andy fep->wol_flag |= FEC_WOL_FLAG_SLEEP_ON; 386545f5c327SPhilippe Reynes phy_stop(ndev->phydev); 386631a6de34SRussell King napi_disable(&fep->napi); 386731a6de34SRussell King netif_tx_lock_bh(ndev); 3868793fc096SFrank Li netif_device_detach(ndev); 386931a6de34SRussell King netif_tx_unlock_bh(ndev); 387031a6de34SRussell King fec_stop(ndev); 3871e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 3872de40ed31SNimrod Andy if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) 38735bbde4d2SNimrod Andy pinctrl_pm_select_sleep_state(&fep->pdev->dev); 3874f4c4a4e0SNimrod Andy } 3875f4c4a4e0SNimrod Andy rtnl_unlock(); 3876793fc096SFrank Li 3877de40ed31SNimrod Andy if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) 3878238f7bc7SFabio Estevam regulator_disable(fep->reg_phy); 3879238f7bc7SFabio Estevam 3880858eeb7dSNimrod Andy /* SOC supply clock to phy, when clock is disabled, phy link down 3881858eeb7dSNimrod Andy * SOC control phy regulator, when regulator is disabled, phy link down 3882858eeb7dSNimrod Andy */ 3883858eeb7dSNimrod Andy if (fep->clk_enet_out || fep->reg_phy) 3884858eeb7dSNimrod Andy fep->link = 0; 3885858eeb7dSNimrod Andy 3886793fc096SFrank Li return 0; 3887793fc096SFrank Li } 3888793fc096SFrank Li 3889dd66d386SFabio Estevam static int __maybe_unused fec_resume(struct device *dev) 3890793fc096SFrank Li { 3891793fc096SFrank Li struct net_device *ndev = dev_get_drvdata(dev); 3892793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3893238f7bc7SFabio Estevam int ret; 3894de40ed31SNimrod Andy int val; 3895238f7bc7SFabio Estevam 3896de40ed31SNimrod Andy if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) { 3897238f7bc7SFabio Estevam ret = regulator_enable(fep->reg_phy); 3898238f7bc7SFabio Estevam if (ret) 3899238f7bc7SFabio Estevam return ret; 3900238f7bc7SFabio Estevam } 3901793fc096SFrank Li 3902da1774e5SRussell King rtnl_lock(); 3903793fc096SFrank Li if (netif_running(ndev)) { 3904f4c4a4e0SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 3905f4c4a4e0SNimrod Andy if (ret) { 3906f4c4a4e0SNimrod Andy rtnl_unlock(); 3907f4c4a4e0SNimrod Andy goto failed_clk; 3908f4c4a4e0SNimrod Andy } 3909de40ed31SNimrod Andy if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) { 3910da722186SMartin Fuzzey fec_enet_stop_mode(fep, false); 3911da722186SMartin Fuzzey 3912de40ed31SNimrod Andy val = readl(fep->hwp + FEC_ECNTRL); 3913de40ed31SNimrod Andy val &= ~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP); 3914de40ed31SNimrod Andy writel(val, fep->hwp + FEC_ECNTRL); 3915de40ed31SNimrod Andy fep->wol_flag &= ~FEC_WOL_FLAG_SLEEP_ON; 3916de40ed31SNimrod Andy } else { 3917de40ed31SNimrod Andy pinctrl_pm_select_default_state(&fep->pdev->dev); 3918de40ed31SNimrod Andy } 3919ef83337dSRussell King fec_restart(ndev); 392031a6de34SRussell King netif_tx_lock_bh(ndev); 3921793fc096SFrank Li netif_device_attach(ndev); 39226af42d42SRussell King netif_tx_unlock_bh(ndev); 39236af42d42SRussell King napi_enable(&fep->napi); 3924557d5dc8SHeiner Kallweit phy_init_hw(ndev->phydev); 392545f5c327SPhilippe Reynes phy_start(ndev->phydev); 3926793fc096SFrank Li } 3927da1774e5SRussell King rtnl_unlock(); 3928793fc096SFrank Li 3929793fc096SFrank Li return 0; 393013a097bdSFabio Estevam 3931e8fcfcd5SNimrod Andy failed_clk: 393213a097bdSFabio Estevam if (fep->reg_phy) 393313a097bdSFabio Estevam regulator_disable(fep->reg_phy); 393413a097bdSFabio Estevam return ret; 3935793fc096SFrank Li } 3936793fc096SFrank Li 39378fff755eSAndrew Lunn static int __maybe_unused fec_runtime_suspend(struct device *dev) 39388fff755eSAndrew Lunn { 39398fff755eSAndrew Lunn struct net_device *ndev = dev_get_drvdata(dev); 39408fff755eSAndrew Lunn struct fec_enet_private *fep = netdev_priv(ndev); 39418fff755eSAndrew Lunn 3942d7c3a206SAndy Duan clk_disable_unprepare(fep->clk_ahb); 39438fff755eSAndrew Lunn clk_disable_unprepare(fep->clk_ipg); 39448fff755eSAndrew Lunn 39458fff755eSAndrew Lunn return 0; 39468fff755eSAndrew Lunn } 39478fff755eSAndrew Lunn 39488fff755eSAndrew Lunn static int __maybe_unused fec_runtime_resume(struct device *dev) 39498fff755eSAndrew Lunn { 39508fff755eSAndrew Lunn struct net_device *ndev = dev_get_drvdata(dev); 39518fff755eSAndrew Lunn struct fec_enet_private *fep = netdev_priv(ndev); 3952d7c3a206SAndy Duan int ret; 39538fff755eSAndrew Lunn 3954d7c3a206SAndy Duan ret = clk_prepare_enable(fep->clk_ahb); 3955d7c3a206SAndy Duan if (ret) 3956d7c3a206SAndy Duan return ret; 3957d7c3a206SAndy Duan ret = clk_prepare_enable(fep->clk_ipg); 3958d7c3a206SAndy Duan if (ret) 3959d7c3a206SAndy Duan goto failed_clk_ipg; 3960d7c3a206SAndy Duan 3961d7c3a206SAndy Duan return 0; 3962d7c3a206SAndy Duan 3963d7c3a206SAndy Duan failed_clk_ipg: 3964d7c3a206SAndy Duan clk_disable_unprepare(fep->clk_ahb); 3965d7c3a206SAndy Duan return ret; 39668fff755eSAndrew Lunn } 39678fff755eSAndrew Lunn 39688fff755eSAndrew Lunn static const struct dev_pm_ops fec_pm_ops = { 39698fff755eSAndrew Lunn SET_SYSTEM_SLEEP_PM_OPS(fec_suspend, fec_resume) 39708fff755eSAndrew Lunn SET_RUNTIME_PM_OPS(fec_runtime_suspend, fec_runtime_resume, NULL) 39718fff755eSAndrew Lunn }; 3972793fc096SFrank Li 3973793fc096SFrank Li static struct platform_driver fec_driver = { 3974793fc096SFrank Li .driver = { 3975793fc096SFrank Li .name = DRIVER_NAME, 3976793fc096SFrank Li .pm = &fec_pm_ops, 3977793fc096SFrank Li .of_match_table = fec_dt_ids, 3978272bb0e9SFabio Estevam .suppress_bind_attrs = true, 3979793fc096SFrank Li }, 3980793fc096SFrank Li .id_table = fec_devtype, 3981793fc096SFrank Li .probe = fec_probe, 3982793fc096SFrank Li .remove = fec_drv_remove, 3983793fc096SFrank Li }; 3984793fc096SFrank Li 3985793fc096SFrank Li module_platform_driver(fec_driver); 3986793fc096SFrank Li 3987f8c0aca9SFabio Estevam MODULE_ALIAS("platform:"DRIVER_NAME); 3988793fc096SFrank Li MODULE_LICENSE("GPL"); 3989