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> 4179f33912SNimrod Andy #include <net/tso.h> 424c09eed9SJim Baxter #include <linux/tcp.h> 434c09eed9SJim Baxter #include <linux/udp.h> 444c09eed9SJim Baxter #include <linux/icmp.h> 45793fc096SFrank Li #include <linux/spinlock.h> 46793fc096SFrank Li #include <linux/workqueue.h> 47793fc096SFrank Li #include <linux/bitops.h> 48793fc096SFrank Li #include <linux/io.h> 49793fc096SFrank Li #include <linux/irq.h> 50793fc096SFrank Li #include <linux/clk.h> 5116f6e983SKrzysztof Kozlowski #include <linux/crc32.h> 52793fc096SFrank Li #include <linux/platform_device.h> 537f854420SAndrew Lunn #include <linux/mdio.h> 54793fc096SFrank Li #include <linux/phy.h> 55793fc096SFrank Li #include <linux/fec.h> 56793fc096SFrank Li #include <linux/of.h> 57793fc096SFrank Li #include <linux/of_device.h> 58793fc096SFrank Li #include <linux/of_gpio.h> 59407066f8SUwe Kleine-König #include <linux/of_mdio.h> 60793fc096SFrank Li #include <linux/of_net.h> 61793fc096SFrank Li #include <linux/regulator/consumer.h> 62cdffcf1bSJim Baxter #include <linux/if_vlan.h> 63a68ab98eSFabio Estevam #include <linux/pinctrl/consumer.h> 64c259c132SFrank Li #include <linux/prefetch.h> 6529380905SLucas Stach #include <soc/imx/cpuidle.h> 66793fc096SFrank Li 67793fc096SFrank Li #include <asm/cacheflush.h> 68793fc096SFrank Li 69793fc096SFrank Li #include "fec.h" 70793fc096SFrank Li 71772e42b0SChristoph Müllner static void set_multicast_list(struct net_device *ndev); 72d851b47bSFugang Duan static void fec_enet_itr_coal_init(struct net_device *ndev); 73772e42b0SChristoph Müllner 74793fc096SFrank Li #define DRIVER_NAME "fec" 75793fc096SFrank Li 764d494cdcSFugang Duan #define FEC_ENET_GET_QUQUE(_x) ((_x == 0) ? 1 : ((_x == 1) ? 2 : 0)) 774d494cdcSFugang Duan 78793fc096SFrank Li /* Pause frame feild and FIFO threshold */ 79793fc096SFrank Li #define FEC_ENET_FCE (1 << 5) 80793fc096SFrank Li #define FEC_ENET_RSEM_V 0x84 81793fc096SFrank Li #define FEC_ENET_RSFL_V 16 82793fc096SFrank Li #define FEC_ENET_RAEM_V 0x8 83793fc096SFrank Li #define FEC_ENET_RAFL_V 0x8 84793fc096SFrank Li #define FEC_ENET_OPD_V 0xFFF0 858fff755eSAndrew Lunn #define FEC_MDIO_PM_TIMEOUT 100 /* ms */ 86793fc096SFrank Li 87793fc096SFrank Li static struct platform_device_id fec_devtype[] = { 88793fc096SFrank Li { 89793fc096SFrank Li /* keep it for coldfire */ 90793fc096SFrank Li .name = DRIVER_NAME, 91793fc096SFrank Li .driver_data = 0, 92793fc096SFrank Li }, { 93793fc096SFrank Li .name = "imx25-fec", 94ec20a63aSFugang Duan .driver_data = FEC_QUIRK_USE_GASKET | FEC_QUIRK_MIB_CLEAR | 95ec20a63aSFugang Duan FEC_QUIRK_HAS_FRREG, 96793fc096SFrank Li }, { 97793fc096SFrank Li .name = "imx27-fec", 98ec20a63aSFugang Duan .driver_data = FEC_QUIRK_MIB_CLEAR | FEC_QUIRK_HAS_FRREG, 99793fc096SFrank Li }, { 100793fc096SFrank Li .name = "imx28-fec", 1013d125f9cSStefan Agner .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME | 102ec20a63aSFugang Duan FEC_QUIRK_SINGLE_MDIO | FEC_QUIRK_HAS_RACC | 103ec20a63aSFugang Duan FEC_QUIRK_HAS_FRREG, 104793fc096SFrank Li }, { 105793fc096SFrank Li .name = "imx6q-fec", 106793fc096SFrank Li .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 107cdffcf1bSJim Baxter FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 10818803495SGreg Ungerer FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR006358 | 10918803495SGreg Ungerer FEC_QUIRK_HAS_RACC, 110793fc096SFrank Li }, { 11136803542SShawn Guo .name = "mvf600-fec", 11218803495SGreg Ungerer .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_RACC, 113ca7c4a45SJingchang Lu }, { 11495a77470SFugang Duan .name = "imx6sx-fec", 11595a77470SFugang Duan .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 11695a77470SFugang Duan FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 117f88c7edeSNimrod Andy FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | 11818803495SGreg Ungerer FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | 119ff7566b8SFugang Duan FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE, 12095a77470SFugang Duan }, { 121a51d3ab5SFugang Duan .name = "imx6ul-fec", 122a51d3ab5SFugang Duan .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 123a51d3ab5SFugang Duan FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 12499492ad4SFugang Duan FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR007885 | 12599492ad4SFugang Duan FEC_QUIRK_BUG_CAPTURE | FEC_QUIRK_HAS_RACC | 12699492ad4SFugang Duan FEC_QUIRK_HAS_COALESCE, 127a51d3ab5SFugang Duan }, { 128793fc096SFrank Li /* sentinel */ 129793fc096SFrank Li } 130793fc096SFrank Li }; 131793fc096SFrank Li MODULE_DEVICE_TABLE(platform, fec_devtype); 132793fc096SFrank Li 133793fc096SFrank Li enum imx_fec_type { 134793fc096SFrank Li IMX25_FEC = 1, /* runs on i.mx25/50/53 */ 135793fc096SFrank Li IMX27_FEC, /* runs on i.mx27/35/51 */ 136793fc096SFrank Li IMX28_FEC, 137793fc096SFrank Li IMX6Q_FEC, 13836803542SShawn Guo MVF600_FEC, 139ba593e00SFugang Duan IMX6SX_FEC, 140a51d3ab5SFugang Duan IMX6UL_FEC, 141793fc096SFrank Li }; 142793fc096SFrank Li 143793fc096SFrank Li static const struct of_device_id fec_dt_ids[] = { 144793fc096SFrank Li { .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], }, 145793fc096SFrank Li { .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], }, 146793fc096SFrank Li { .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], }, 147793fc096SFrank Li { .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], }, 14836803542SShawn Guo { .compatible = "fsl,mvf600-fec", .data = &fec_devtype[MVF600_FEC], }, 149ba593e00SFugang Duan { .compatible = "fsl,imx6sx-fec", .data = &fec_devtype[IMX6SX_FEC], }, 150a51d3ab5SFugang Duan { .compatible = "fsl,imx6ul-fec", .data = &fec_devtype[IMX6UL_FEC], }, 151793fc096SFrank Li { /* sentinel */ } 152793fc096SFrank Li }; 153793fc096SFrank Li MODULE_DEVICE_TABLE(of, fec_dt_ids); 154793fc096SFrank Li 155793fc096SFrank Li static unsigned char macaddr[ETH_ALEN]; 156793fc096SFrank Li module_param_array(macaddr, byte, NULL, 0); 157793fc096SFrank Li MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); 158793fc096SFrank Li 159793fc096SFrank Li #if defined(CONFIG_M5272) 160793fc096SFrank Li /* 161793fc096SFrank Li * Some hardware gets it MAC address out of local flash memory. 162793fc096SFrank Li * if this is non-zero then assume it is the address to get MAC from. 163793fc096SFrank Li */ 164793fc096SFrank Li #if defined(CONFIG_NETtel) 165793fc096SFrank Li #define FEC_FLASHMAC 0xf0006006 166793fc096SFrank Li #elif defined(CONFIG_GILBARCONAP) || defined(CONFIG_SCALES) 167793fc096SFrank Li #define FEC_FLASHMAC 0xf0006000 168793fc096SFrank Li #elif defined(CONFIG_CANCam) 169793fc096SFrank Li #define FEC_FLASHMAC 0xf0020000 170793fc096SFrank Li #elif defined (CONFIG_M5272C3) 171793fc096SFrank Li #define FEC_FLASHMAC (0xffe04000 + 4) 172793fc096SFrank Li #elif defined(CONFIG_MOD5272) 173793fc096SFrank Li #define FEC_FLASHMAC 0xffc0406b 174793fc096SFrank Li #else 175793fc096SFrank Li #define FEC_FLASHMAC 0 176793fc096SFrank Li #endif 177793fc096SFrank Li #endif /* CONFIG_M5272 */ 178793fc096SFrank Li 179cdffcf1bSJim Baxter /* The FEC stores dest/src/type/vlan, data, and checksum for receive packets. 180fbbeefddSAndrew Lunn * 181fbbeefddSAndrew Lunn * 2048 byte skbufs are allocated. However, alignment requirements 182fbbeefddSAndrew Lunn * varies between FEC variants. Worst case is 64, so round down by 64. 183793fc096SFrank Li */ 184fbbeefddSAndrew Lunn #define PKT_MAXBUF_SIZE (round_down(2048 - 64, 64)) 185793fc096SFrank Li #define PKT_MINBUF_SIZE 64 186793fc096SFrank Li 1874c09eed9SJim Baxter /* FEC receive acceleration */ 1884c09eed9SJim Baxter #define FEC_RACC_IPDIS (1 << 1) 1894c09eed9SJim Baxter #define FEC_RACC_PRODIS (1 << 2) 1903ac72b7bSEric Nelson #define FEC_RACC_SHIFT16 BIT(7) 1914c09eed9SJim Baxter #define FEC_RACC_OPTIONS (FEC_RACC_IPDIS | FEC_RACC_PRODIS) 1924c09eed9SJim Baxter 1932b30842bSAndrew Lunn /* MIB Control Register */ 1942b30842bSAndrew Lunn #define FEC_MIB_CTRLSTAT_DISABLE BIT(31) 1952b30842bSAndrew Lunn 196793fc096SFrank Li /* 197793fc096SFrank Li * The 5270/5271/5280/5282/532x RX control register also contains maximum frame 198793fc096SFrank Li * size bits. Other FEC hardware does not, so we need to take that into 199793fc096SFrank Li * account when setting it. 200793fc096SFrank Li */ 201793fc096SFrank Li #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ 2023f1dcc6aSLucas Stach defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ 2033f1dcc6aSLucas Stach defined(CONFIG_ARM64) 204793fc096SFrank Li #define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16) 205793fc096SFrank Li #else 206793fc096SFrank Li #define OPT_FRAME_SIZE 0 207793fc096SFrank Li #endif 208793fc096SFrank Li 209793fc096SFrank Li /* FEC MII MMFR bits definition */ 210793fc096SFrank Li #define FEC_MMFR_ST (1 << 30) 211d3ee8ec7SMarco Hartmann #define FEC_MMFR_ST_C45 (0) 212793fc096SFrank Li #define FEC_MMFR_OP_READ (2 << 28) 213d3ee8ec7SMarco Hartmann #define FEC_MMFR_OP_READ_C45 (3 << 28) 214793fc096SFrank Li #define FEC_MMFR_OP_WRITE (1 << 28) 215d3ee8ec7SMarco Hartmann #define FEC_MMFR_OP_ADDR_WRITE (0) 216793fc096SFrank Li #define FEC_MMFR_PA(v) ((v & 0x1f) << 23) 217793fc096SFrank Li #define FEC_MMFR_RA(v) ((v & 0x1f) << 18) 218793fc096SFrank Li #define FEC_MMFR_TA (2 << 16) 219793fc096SFrank Li #define FEC_MMFR_DATA(v) (v & 0xffff) 220de40ed31SNimrod Andy /* FEC ECR bits definition */ 221de40ed31SNimrod Andy #define FEC_ECR_MAGICEN (1 << 2) 222de40ed31SNimrod Andy #define FEC_ECR_SLEEP (1 << 3) 223793fc096SFrank Li 224793fc096SFrank Li #define FEC_MII_TIMEOUT 30000 /* us */ 225793fc096SFrank Li 226793fc096SFrank Li /* Transmitter timeout */ 227793fc096SFrank Li #define TX_TIMEOUT (2 * HZ) 228793fc096SFrank Li 229793fc096SFrank Li #define FEC_PAUSE_FLAG_AUTONEG 0x1 230793fc096SFrank Li #define FEC_PAUSE_FLAG_ENABLE 0x2 231de40ed31SNimrod Andy #define FEC_WOL_HAS_MAGIC_PACKET (0x1 << 0) 232de40ed31SNimrod Andy #define FEC_WOL_FLAG_ENABLE (0x1 << 1) 233de40ed31SNimrod Andy #define FEC_WOL_FLAG_SLEEP_ON (0x1 << 2) 234793fc096SFrank Li 2351b7bde6dSNimrod Andy #define COPYBREAK_DEFAULT 256 2361b7bde6dSNimrod Andy 23779f33912SNimrod Andy /* Max number of allowed TCP segments for software TSO */ 23879f33912SNimrod Andy #define FEC_MAX_TSO_SEGS 100 23979f33912SNimrod Andy #define FEC_MAX_SKB_DESCS (FEC_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS) 24079f33912SNimrod Andy 24179f33912SNimrod Andy #define IS_TSO_HEADER(txq, addr) \ 24279f33912SNimrod Andy ((addr >= txq->tso_hdrs_dma) && \ 2437355f276STroy Kisky (addr < txq->tso_hdrs_dma + txq->bd.ring_size * TSO_HEADER_SIZE)) 24479f33912SNimrod Andy 245793fc096SFrank Li static int mii_cnt; 246793fc096SFrank Li 2477355f276STroy Kisky static struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, 2487355f276STroy Kisky struct bufdesc_prop *bd) 249793fc096SFrank Li { 2507355f276STroy Kisky return (bdp >= bd->last) ? bd->base 251145d6e29SFugang Duan : (struct bufdesc *)(((void *)bdp) + bd->dsize); 252793fc096SFrank Li } 253793fc096SFrank Li 2547355f276STroy Kisky static struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, 2557355f276STroy Kisky struct bufdesc_prop *bd) 25636e24e2eSDuan Fugang-B38611 { 2577355f276STroy Kisky return (bdp <= bd->base) ? bd->last 258145d6e29SFugang Duan : (struct bufdesc *)(((void *)bdp) - bd->dsize); 25936e24e2eSDuan Fugang-B38611 } 26036e24e2eSDuan Fugang-B38611 2617355f276STroy Kisky static int fec_enet_get_bd_index(struct bufdesc *bdp, 2627355f276STroy Kisky struct bufdesc_prop *bd) 26361a4427bSNimrod Andy { 2647355f276STroy Kisky return ((const char *)bdp - (const char *)bd->base) >> bd->dsize_log2; 26561a4427bSNimrod Andy } 26661a4427bSNimrod Andy 2677355f276STroy Kisky static int fec_enet_get_free_txdesc_num(struct fec_enet_priv_tx_q *txq) 2686e909283SNimrod Andy { 2696e909283SNimrod Andy int entries; 2706e909283SNimrod Andy 2717355f276STroy Kisky entries = (((const char *)txq->dirty_tx - 2727355f276STroy Kisky (const char *)txq->bd.cur) >> txq->bd.dsize_log2) - 1; 2736e909283SNimrod Andy 2747355f276STroy Kisky return entries >= 0 ? entries : entries + txq->bd.ring_size; 2756e909283SNimrod Andy } 2766e909283SNimrod Andy 277c20e599bSLothar Waßmann static void swap_buffer(void *bufaddr, int len) 278793fc096SFrank Li { 279793fc096SFrank Li int i; 280793fc096SFrank Li unsigned int *buf = bufaddr; 281793fc096SFrank Li 2827b487d07SLothar Waßmann for (i = 0; i < len; i += 4, buf++) 283e453789aSLothar Waßmann swab32s(buf); 284793fc096SFrank Li } 285793fc096SFrank Li 2861310b544SLothar Waßmann static void swap_buffer2(void *dst_buf, void *src_buf, int len) 2871310b544SLothar Waßmann { 2881310b544SLothar Waßmann int i; 2891310b544SLothar Waßmann unsigned int *src = src_buf; 2901310b544SLothar Waßmann unsigned int *dst = dst_buf; 2911310b544SLothar Waßmann 2921310b544SLothar Waßmann for (i = 0; i < len; i += 4, src++, dst++) 2931310b544SLothar Waßmann *dst = swab32p(src); 2941310b544SLothar Waßmann } 2951310b544SLothar Waßmann 296344756f6SRussell King static void fec_dump(struct net_device *ndev) 297344756f6SRussell King { 298344756f6SRussell King struct fec_enet_private *fep = netdev_priv(ndev); 2994d494cdcSFugang Duan struct bufdesc *bdp; 3004d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 3014d494cdcSFugang Duan int index = 0; 302344756f6SRussell King 303344756f6SRussell King netdev_info(ndev, "TX ring dump\n"); 304344756f6SRussell King pr_info("Nr SC addr len SKB\n"); 305344756f6SRussell King 3064d494cdcSFugang Duan txq = fep->tx_queue[0]; 3077355f276STroy Kisky bdp = txq->bd.base; 3084d494cdcSFugang Duan 309344756f6SRussell King do { 3105cfa3039SJohannes Berg pr_info("%3u %c%c 0x%04x 0x%08x %4u %p\n", 311344756f6SRussell King index, 3127355f276STroy Kisky bdp == txq->bd.cur ? 'S' : ' ', 3134d494cdcSFugang Duan bdp == txq->dirty_tx ? 'H' : ' ', 3145cfa3039SJohannes Berg fec16_to_cpu(bdp->cbd_sc), 3155cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 3165cfa3039SJohannes Berg fec16_to_cpu(bdp->cbd_datlen), 3174d494cdcSFugang Duan txq->tx_skbuff[index]); 3187355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 319344756f6SRussell King index++; 3207355f276STroy Kisky } while (bdp != txq->bd.base); 321344756f6SRussell King } 322344756f6SRussell King 32362a02c98SFugang Duan static inline bool is_ipv4_pkt(struct sk_buff *skb) 32462a02c98SFugang Duan { 32562a02c98SFugang Duan return skb->protocol == htons(ETH_P_IP) && ip_hdr(skb)->version == 4; 32662a02c98SFugang Duan } 32762a02c98SFugang Duan 3284c09eed9SJim Baxter static int 3294c09eed9SJim Baxter fec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev) 3304c09eed9SJim Baxter { 3314c09eed9SJim Baxter /* Only run for packets requiring a checksum. */ 3324c09eed9SJim Baxter if (skb->ip_summed != CHECKSUM_PARTIAL) 3334c09eed9SJim Baxter return 0; 3344c09eed9SJim Baxter 3354c09eed9SJim Baxter if (unlikely(skb_cow_head(skb, 0))) 3364c09eed9SJim Baxter return -1; 3374c09eed9SJim Baxter 33862a02c98SFugang Duan if (is_ipv4_pkt(skb)) 33996c50caaSNimrod Andy ip_hdr(skb)->check = 0; 3404c09eed9SJim Baxter *(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) = 0; 3414c09eed9SJim Baxter 3424c09eed9SJim Baxter return 0; 3434c09eed9SJim Baxter } 3444c09eed9SJim Baxter 345c4bc44c6SKevin Hao static struct bufdesc * 3464d494cdcSFugang Duan fec_enet_txq_submit_frag_skb(struct fec_enet_priv_tx_q *txq, 3474d494cdcSFugang Duan struct sk_buff *skb, 3484d494cdcSFugang Duan struct net_device *ndev) 3496e909283SNimrod Andy { 3506e909283SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 3517355f276STroy Kisky struct bufdesc *bdp = txq->bd.cur; 3526e909283SNimrod Andy struct bufdesc_ex *ebdp; 3536e909283SNimrod Andy int nr_frags = skb_shinfo(skb)->nr_frags; 3546e909283SNimrod Andy int frag, frag_len; 3556e909283SNimrod Andy unsigned short status; 3566e909283SNimrod Andy unsigned int estatus = 0; 3576e909283SNimrod Andy skb_frag_t *this_frag; 3586e909283SNimrod Andy unsigned int index; 3596e909283SNimrod Andy void *bufaddr; 360d6bf3143SRussell King dma_addr_t addr; 3616e909283SNimrod Andy int i; 3626e909283SNimrod Andy 3636e909283SNimrod Andy for (frag = 0; frag < nr_frags; frag++) { 3646e909283SNimrod Andy this_frag = &skb_shinfo(skb)->frags[frag]; 3657355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 3666e909283SNimrod Andy ebdp = (struct bufdesc_ex *)bdp; 3676e909283SNimrod Andy 3685cfa3039SJohannes Berg status = fec16_to_cpu(bdp->cbd_sc); 3696e909283SNimrod Andy status &= ~BD_ENET_TX_STATS; 3706e909283SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 371d7840976SMatthew Wilcox (Oracle) frag_len = skb_frag_size(&skb_shinfo(skb)->frags[frag]); 3726e909283SNimrod Andy 3736e909283SNimrod Andy /* Handle the last BD specially */ 3746e909283SNimrod Andy if (frag == nr_frags - 1) { 3756e909283SNimrod Andy status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); 3766e909283SNimrod Andy if (fep->bufdesc_ex) { 3776e909283SNimrod Andy estatus |= BD_ENET_TX_INT; 3786e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & 3796e909283SNimrod Andy SKBTX_HW_TSTAMP && fep->hwts_tx_en)) 3806e909283SNimrod Andy estatus |= BD_ENET_TX_TS; 3816e909283SNimrod Andy } 3826e909283SNimrod Andy } 3836e909283SNimrod Andy 3846e909283SNimrod Andy if (fep->bufdesc_ex) { 3856b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 38653bb20d1STroy Kisky estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 3876e909283SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 3886e909283SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 3896e909283SNimrod Andy ebdp->cbd_bdu = 0; 3905cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(estatus); 3916e909283SNimrod Andy } 3926e909283SNimrod Andy 393d7840976SMatthew Wilcox (Oracle) bufaddr = skb_frag_address(this_frag); 3946e909283SNimrod Andy 3957355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 39641ef84ceSFugang Duan if (((unsigned long) bufaddr) & fep->tx_align || 3976b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 3984d494cdcSFugang Duan memcpy(txq->tx_bounce[index], bufaddr, frag_len); 3994d494cdcSFugang Duan bufaddr = txq->tx_bounce[index]; 4006e909283SNimrod Andy 4016b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 4026e909283SNimrod Andy swap_buffer(bufaddr, frag_len); 4036e909283SNimrod Andy } 4046e909283SNimrod Andy 405d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, bufaddr, frag_len, 406d6bf3143SRussell King DMA_TO_DEVICE); 407d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 4086e909283SNimrod Andy if (net_ratelimit()) 4096e909283SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 4106e909283SNimrod Andy goto dma_mapping_error; 4116e909283SNimrod Andy } 4126e909283SNimrod Andy 4135cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(addr); 4145cfa3039SJohannes Berg bdp->cbd_datlen = cpu_to_fec16(frag_len); 415be293467STroy Kisky /* Make sure the updates to rest of the descriptor are 416be293467STroy Kisky * performed before transferring ownership. 417be293467STroy Kisky */ 418be293467STroy Kisky wmb(); 4195cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(status); 4206e909283SNimrod Andy } 4216e909283SNimrod Andy 422c4bc44c6SKevin Hao return bdp; 4236e909283SNimrod Andy dma_mapping_error: 4247355f276STroy Kisky bdp = txq->bd.cur; 4256e909283SNimrod Andy for (i = 0; i < frag; i++) { 4267355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 4275cfa3039SJohannes Berg dma_unmap_single(&fep->pdev->dev, fec32_to_cpu(bdp->cbd_bufaddr), 4285cfa3039SJohannes Berg fec16_to_cpu(bdp->cbd_datlen), DMA_TO_DEVICE); 4296e909283SNimrod Andy } 430c4bc44c6SKevin Hao return ERR_PTR(-ENOMEM); 4316e909283SNimrod Andy } 4326e909283SNimrod Andy 4334d494cdcSFugang Duan static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq, 4344d494cdcSFugang Duan struct sk_buff *skb, struct net_device *ndev) 4356e909283SNimrod Andy { 4366e909283SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 4376e909283SNimrod Andy int nr_frags = skb_shinfo(skb)->nr_frags; 4386e909283SNimrod Andy struct bufdesc *bdp, *last_bdp; 4396e909283SNimrod Andy void *bufaddr; 440d6bf3143SRussell King dma_addr_t addr; 4416e909283SNimrod Andy unsigned short status; 4426e909283SNimrod Andy unsigned short buflen; 4436e909283SNimrod Andy unsigned int estatus = 0; 4446e909283SNimrod Andy unsigned int index; 44579f33912SNimrod Andy int entries_free; 4466e909283SNimrod Andy 4477355f276STroy Kisky entries_free = fec_enet_get_free_txdesc_num(txq); 44879f33912SNimrod Andy if (entries_free < MAX_SKB_FRAGS + 1) { 44979f33912SNimrod Andy dev_kfree_skb_any(skb); 45079f33912SNimrod Andy if (net_ratelimit()) 45179f33912SNimrod Andy netdev_err(ndev, "NOT enough BD for SG!\n"); 45279f33912SNimrod Andy return NETDEV_TX_OK; 45379f33912SNimrod Andy } 45479f33912SNimrod Andy 4556e909283SNimrod Andy /* Protocol checksum off-load for TCP and UDP. */ 4566e909283SNimrod Andy if (fec_enet_clear_csum(skb, ndev)) { 4576e909283SNimrod Andy dev_kfree_skb_any(skb); 4586e909283SNimrod Andy return NETDEV_TX_OK; 4596e909283SNimrod Andy } 4606e909283SNimrod Andy 4616e909283SNimrod Andy /* Fill in a Tx ring entry */ 4627355f276STroy Kisky bdp = txq->bd.cur; 463c4bc44c6SKevin Hao last_bdp = bdp; 4645cfa3039SJohannes Berg status = fec16_to_cpu(bdp->cbd_sc); 4656e909283SNimrod Andy status &= ~BD_ENET_TX_STATS; 4666e909283SNimrod Andy 4676e909283SNimrod Andy /* Set buffer length and buffer pointer */ 4686e909283SNimrod Andy bufaddr = skb->data; 4696e909283SNimrod Andy buflen = skb_headlen(skb); 4706e909283SNimrod Andy 4717355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 47241ef84ceSFugang Duan if (((unsigned long) bufaddr) & fep->tx_align || 4736b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 4744d494cdcSFugang Duan memcpy(txq->tx_bounce[index], skb->data, buflen); 4754d494cdcSFugang Duan bufaddr = txq->tx_bounce[index]; 4766e909283SNimrod Andy 4776b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 4786e909283SNimrod Andy swap_buffer(bufaddr, buflen); 4796e909283SNimrod Andy } 4806e909283SNimrod Andy 481d6bf3143SRussell King /* Push the data cache so the CPM does not get stale memory data. */ 482d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, bufaddr, buflen, DMA_TO_DEVICE); 483d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 4846e909283SNimrod Andy dev_kfree_skb_any(skb); 4856e909283SNimrod Andy if (net_ratelimit()) 4866e909283SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 4876e909283SNimrod Andy return NETDEV_TX_OK; 4886e909283SNimrod Andy } 4896e909283SNimrod Andy 4906e909283SNimrod Andy if (nr_frags) { 491c4bc44c6SKevin Hao last_bdp = fec_enet_txq_submit_frag_skb(txq, skb, ndev); 492fc75ba51STroy Kisky if (IS_ERR(last_bdp)) { 493fc75ba51STroy Kisky dma_unmap_single(&fep->pdev->dev, addr, 494fc75ba51STroy Kisky buflen, DMA_TO_DEVICE); 495fc75ba51STroy Kisky dev_kfree_skb_any(skb); 496c4bc44c6SKevin Hao return NETDEV_TX_OK; 497fc75ba51STroy Kisky } 4986e909283SNimrod Andy } else { 4996e909283SNimrod Andy status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); 5006e909283SNimrod Andy if (fep->bufdesc_ex) { 5016e909283SNimrod Andy estatus = BD_ENET_TX_INT; 5026e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & 5036e909283SNimrod Andy SKBTX_HW_TSTAMP && fep->hwts_tx_en)) 5046e909283SNimrod Andy estatus |= BD_ENET_TX_TS; 5056e909283SNimrod Andy } 5066e909283SNimrod Andy } 507fc75ba51STroy Kisky bdp->cbd_bufaddr = cpu_to_fec32(addr); 508fc75ba51STroy Kisky bdp->cbd_datlen = cpu_to_fec16(buflen); 5096e909283SNimrod Andy 5106e909283SNimrod Andy if (fep->bufdesc_ex) { 5116e909283SNimrod Andy 5126e909283SNimrod Andy struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 5136e909283SNimrod Andy 5146e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && 5156e909283SNimrod Andy fep->hwts_tx_en)) 5166e909283SNimrod Andy skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; 5176e909283SNimrod Andy 5186b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 51953bb20d1STroy Kisky estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 520befe8213SNimrod Andy 5216e909283SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 5226e909283SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 5236e909283SNimrod Andy 5246e909283SNimrod Andy ebdp->cbd_bdu = 0; 5255cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(estatus); 5266e909283SNimrod Andy } 5276e909283SNimrod Andy 5287355f276STroy Kisky index = fec_enet_get_bd_index(last_bdp, &txq->bd); 5296e909283SNimrod Andy /* Save skb pointer */ 5304d494cdcSFugang Duan txq->tx_skbuff[index] = skb; 5316e909283SNimrod Andy 532be293467STroy Kisky /* Make sure the updates to rest of the descriptor are performed before 533be293467STroy Kisky * transferring ownership. 534be293467STroy Kisky */ 535be293467STroy Kisky wmb(); 5366e909283SNimrod Andy 5376e909283SNimrod Andy /* Send it on its way. Tell FEC it's ready, interrupt when done, 5386e909283SNimrod Andy * it's the last BD of the frame, and to put the CRC on the end. 5396e909283SNimrod Andy */ 5406e909283SNimrod Andy status |= (BD_ENET_TX_READY | BD_ENET_TX_TC); 5415cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(status); 5426e909283SNimrod Andy 543793fc096SFrank Li /* If this was the last BD in the ring, start at the beginning again. */ 5447355f276STroy Kisky bdp = fec_enet_get_nextdesc(last_bdp, &txq->bd); 545793fc096SFrank Li 5467a2a8451SEric Dumazet skb_tx_timestamp(skb); 5477a2a8451SEric Dumazet 548c4bc44c6SKevin Hao /* Make sure the update to bdp and tx_skbuff are performed before 5497355f276STroy Kisky * txq->bd.cur. 550c4bc44c6SKevin Hao */ 551c4bc44c6SKevin Hao wmb(); 5527355f276STroy Kisky txq->bd.cur = bdp; 553793fc096SFrank Li 554793fc096SFrank Li /* Trigger transmission start */ 55553bb20d1STroy Kisky writel(0, txq->bd.reg_desc_active); 556793fc096SFrank Li 5576e909283SNimrod Andy return 0; 558793fc096SFrank Li } 559793fc096SFrank Li 56079f33912SNimrod Andy static int 5614d494cdcSFugang Duan fec_enet_txq_put_data_tso(struct fec_enet_priv_tx_q *txq, struct sk_buff *skb, 5624d494cdcSFugang Duan struct net_device *ndev, 56379f33912SNimrod Andy struct bufdesc *bdp, int index, char *data, 56479f33912SNimrod Andy int size, bool last_tcp, bool is_last) 56579f33912SNimrod Andy { 56679f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 56761cd2ebbSFabian Frederick struct bufdesc_ex *ebdp = container_of(bdp, struct bufdesc_ex, desc); 56879f33912SNimrod Andy unsigned short status; 56979f33912SNimrod Andy unsigned int estatus = 0; 570d6bf3143SRussell King dma_addr_t addr; 57179f33912SNimrod Andy 5725cfa3039SJohannes Berg status = fec16_to_cpu(bdp->cbd_sc); 57379f33912SNimrod Andy status &= ~BD_ENET_TX_STATS; 57479f33912SNimrod Andy 57579f33912SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 57679f33912SNimrod Andy 57741ef84ceSFugang Duan if (((unsigned long) data) & fep->tx_align || 5786b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 5794d494cdcSFugang Duan memcpy(txq->tx_bounce[index], data, size); 5804d494cdcSFugang Duan data = txq->tx_bounce[index]; 58179f33912SNimrod Andy 5826b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 58379f33912SNimrod Andy swap_buffer(data, size); 58479f33912SNimrod Andy } 58579f33912SNimrod Andy 586d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, data, size, DMA_TO_DEVICE); 587d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 58879f33912SNimrod Andy dev_kfree_skb_any(skb); 58979f33912SNimrod Andy if (net_ratelimit()) 59079f33912SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 59179f33912SNimrod Andy return NETDEV_TX_BUSY; 59279f33912SNimrod Andy } 59379f33912SNimrod Andy 5945cfa3039SJohannes Berg bdp->cbd_datlen = cpu_to_fec16(size); 5955cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(addr); 596d6bf3143SRussell King 59779f33912SNimrod Andy if (fep->bufdesc_ex) { 5986b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 59953bb20d1STroy Kisky estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 60079f33912SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 60179f33912SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 60279f33912SNimrod Andy ebdp->cbd_bdu = 0; 6035cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(estatus); 60479f33912SNimrod Andy } 60579f33912SNimrod Andy 60679f33912SNimrod Andy /* Handle the last BD specially */ 60779f33912SNimrod Andy if (last_tcp) 60879f33912SNimrod Andy status |= (BD_ENET_TX_LAST | BD_ENET_TX_TC); 60979f33912SNimrod Andy if (is_last) { 61079f33912SNimrod Andy status |= BD_ENET_TX_INTR; 61179f33912SNimrod Andy if (fep->bufdesc_ex) 6125cfa3039SJohannes Berg ebdp->cbd_esc |= cpu_to_fec32(BD_ENET_TX_INT); 61379f33912SNimrod Andy } 61479f33912SNimrod Andy 6155cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(status); 61679f33912SNimrod Andy 61779f33912SNimrod Andy return 0; 61879f33912SNimrod Andy } 61979f33912SNimrod Andy 62079f33912SNimrod Andy static int 6214d494cdcSFugang Duan fec_enet_txq_put_hdr_tso(struct fec_enet_priv_tx_q *txq, 6224d494cdcSFugang Duan struct sk_buff *skb, struct net_device *ndev, 62379f33912SNimrod Andy struct bufdesc *bdp, int index) 62479f33912SNimrod Andy { 62579f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 62679f33912SNimrod Andy int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); 62761cd2ebbSFabian Frederick struct bufdesc_ex *ebdp = container_of(bdp, struct bufdesc_ex, desc); 62879f33912SNimrod Andy void *bufaddr; 62979f33912SNimrod Andy unsigned long dmabuf; 63079f33912SNimrod Andy unsigned short status; 63179f33912SNimrod Andy unsigned int estatus = 0; 63279f33912SNimrod Andy 6335cfa3039SJohannes Berg status = fec16_to_cpu(bdp->cbd_sc); 63479f33912SNimrod Andy status &= ~BD_ENET_TX_STATS; 63579f33912SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 63679f33912SNimrod Andy 6374d494cdcSFugang Duan bufaddr = txq->tso_hdrs + index * TSO_HEADER_SIZE; 6384d494cdcSFugang Duan dmabuf = txq->tso_hdrs_dma + index * TSO_HEADER_SIZE; 63941ef84ceSFugang Duan if (((unsigned long)bufaddr) & fep->tx_align || 6406b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 6414d494cdcSFugang Duan memcpy(txq->tx_bounce[index], skb->data, hdr_len); 6424d494cdcSFugang Duan bufaddr = txq->tx_bounce[index]; 64379f33912SNimrod Andy 6446b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 64579f33912SNimrod Andy swap_buffer(bufaddr, hdr_len); 64679f33912SNimrod Andy 64779f33912SNimrod Andy dmabuf = dma_map_single(&fep->pdev->dev, bufaddr, 64879f33912SNimrod Andy hdr_len, DMA_TO_DEVICE); 64979f33912SNimrod Andy if (dma_mapping_error(&fep->pdev->dev, dmabuf)) { 65079f33912SNimrod Andy dev_kfree_skb_any(skb); 65179f33912SNimrod Andy if (net_ratelimit()) 65279f33912SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 65379f33912SNimrod Andy return NETDEV_TX_BUSY; 65479f33912SNimrod Andy } 65579f33912SNimrod Andy } 65679f33912SNimrod Andy 6575cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(dmabuf); 6585cfa3039SJohannes Berg bdp->cbd_datlen = cpu_to_fec16(hdr_len); 65979f33912SNimrod Andy 66079f33912SNimrod Andy if (fep->bufdesc_ex) { 6616b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 66253bb20d1STroy Kisky estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 66379f33912SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 66479f33912SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 66579f33912SNimrod Andy ebdp->cbd_bdu = 0; 6665cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(estatus); 66779f33912SNimrod Andy } 66879f33912SNimrod Andy 6695cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(status); 67079f33912SNimrod Andy 67179f33912SNimrod Andy return 0; 67279f33912SNimrod Andy } 67379f33912SNimrod Andy 6744d494cdcSFugang Duan static int fec_enet_txq_submit_tso(struct fec_enet_priv_tx_q *txq, 6754d494cdcSFugang Duan struct sk_buff *skb, 6764d494cdcSFugang Duan struct net_device *ndev) 67779f33912SNimrod Andy { 67879f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 67979f33912SNimrod Andy int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); 68079f33912SNimrod Andy int total_len, data_left; 6817355f276STroy Kisky struct bufdesc *bdp = txq->bd.cur; 68279f33912SNimrod Andy struct tso_t tso; 68379f33912SNimrod Andy unsigned int index = 0; 68479f33912SNimrod Andy int ret; 68579f33912SNimrod Andy 6867355f276STroy Kisky if (tso_count_descs(skb) >= fec_enet_get_free_txdesc_num(txq)) { 68779f33912SNimrod Andy dev_kfree_skb_any(skb); 68879f33912SNimrod Andy if (net_ratelimit()) 68979f33912SNimrod Andy netdev_err(ndev, "NOT enough BD for TSO!\n"); 69079f33912SNimrod Andy return NETDEV_TX_OK; 69179f33912SNimrod Andy } 69279f33912SNimrod Andy 69379f33912SNimrod Andy /* Protocol checksum off-load for TCP and UDP. */ 69479f33912SNimrod Andy if (fec_enet_clear_csum(skb, ndev)) { 69579f33912SNimrod Andy dev_kfree_skb_any(skb); 69679f33912SNimrod Andy return NETDEV_TX_OK; 69779f33912SNimrod Andy } 69879f33912SNimrod Andy 69979f33912SNimrod Andy /* Initialize the TSO handler, and prepare the first payload */ 70079f33912SNimrod Andy tso_start(skb, &tso); 70179f33912SNimrod Andy 70279f33912SNimrod Andy total_len = skb->len - hdr_len; 70379f33912SNimrod Andy while (total_len > 0) { 70479f33912SNimrod Andy char *hdr; 70579f33912SNimrod Andy 7067355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 70779f33912SNimrod Andy data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len); 70879f33912SNimrod Andy total_len -= data_left; 70979f33912SNimrod Andy 71079f33912SNimrod Andy /* prepare packet headers: MAC + IP + TCP */ 7114d494cdcSFugang Duan hdr = txq->tso_hdrs + index * TSO_HEADER_SIZE; 71279f33912SNimrod Andy tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0); 7134d494cdcSFugang Duan ret = fec_enet_txq_put_hdr_tso(txq, skb, ndev, bdp, index); 71479f33912SNimrod Andy if (ret) 71579f33912SNimrod Andy goto err_release; 71679f33912SNimrod Andy 71779f33912SNimrod Andy while (data_left > 0) { 71879f33912SNimrod Andy int size; 71979f33912SNimrod Andy 72079f33912SNimrod Andy size = min_t(int, tso.size, data_left); 7217355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 7227355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 7234d494cdcSFugang Duan ret = fec_enet_txq_put_data_tso(txq, skb, ndev, 7244d494cdcSFugang Duan bdp, index, 7254d494cdcSFugang Duan tso.data, size, 7264d494cdcSFugang Duan size == data_left, 72779f33912SNimrod Andy total_len == 0); 72879f33912SNimrod Andy if (ret) 72979f33912SNimrod Andy goto err_release; 73079f33912SNimrod Andy 73179f33912SNimrod Andy data_left -= size; 73279f33912SNimrod Andy tso_build_data(skb, &tso, size); 73379f33912SNimrod Andy } 73479f33912SNimrod Andy 7357355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 73679f33912SNimrod Andy } 73779f33912SNimrod Andy 73879f33912SNimrod Andy /* Save skb pointer */ 7394d494cdcSFugang Duan txq->tx_skbuff[index] = skb; 74079f33912SNimrod Andy 74179f33912SNimrod Andy skb_tx_timestamp(skb); 7427355f276STroy Kisky txq->bd.cur = bdp; 74379f33912SNimrod Andy 74479f33912SNimrod Andy /* Trigger transmission start */ 7456b7e4008SLothar Waßmann if (!(fep->quirks & FEC_QUIRK_ERR007885) || 74653bb20d1STroy Kisky !readl(txq->bd.reg_desc_active) || 74753bb20d1STroy Kisky !readl(txq->bd.reg_desc_active) || 74853bb20d1STroy Kisky !readl(txq->bd.reg_desc_active) || 74953bb20d1STroy Kisky !readl(txq->bd.reg_desc_active)) 75053bb20d1STroy Kisky writel(0, txq->bd.reg_desc_active); 75179f33912SNimrod Andy 75279f33912SNimrod Andy return 0; 75379f33912SNimrod Andy 75479f33912SNimrod Andy err_release: 75579f33912SNimrod Andy /* TODO: Release all used data descriptors for TSO */ 75679f33912SNimrod Andy return ret; 75779f33912SNimrod Andy } 75879f33912SNimrod Andy 75961a4427bSNimrod Andy static netdev_tx_t 76061a4427bSNimrod Andy fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) 76161a4427bSNimrod Andy { 76261a4427bSNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 7636e909283SNimrod Andy int entries_free; 7644d494cdcSFugang Duan unsigned short queue; 7654d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 7664d494cdcSFugang Duan struct netdev_queue *nq; 76761a4427bSNimrod Andy int ret; 76861a4427bSNimrod Andy 7694d494cdcSFugang Duan queue = skb_get_queue_mapping(skb); 7704d494cdcSFugang Duan txq = fep->tx_queue[queue]; 7714d494cdcSFugang Duan nq = netdev_get_tx_queue(ndev, queue); 7724d494cdcSFugang Duan 77379f33912SNimrod Andy if (skb_is_gso(skb)) 7744d494cdcSFugang Duan ret = fec_enet_txq_submit_tso(txq, skb, ndev); 77579f33912SNimrod Andy else 7764d494cdcSFugang Duan ret = fec_enet_txq_submit_skb(txq, skb, ndev); 7776e909283SNimrod Andy if (ret) 7786e909283SNimrod Andy return ret; 77961a4427bSNimrod Andy 7807355f276STroy Kisky entries_free = fec_enet_get_free_txdesc_num(txq); 7814d494cdcSFugang Duan if (entries_free <= txq->tx_stop_threshold) 7824d494cdcSFugang Duan netif_tx_stop_queue(nq); 78361a4427bSNimrod Andy 78461a4427bSNimrod Andy return NETDEV_TX_OK; 78561a4427bSNimrod Andy } 78661a4427bSNimrod Andy 787a210576cSDavid S. Miller /* Init RX & TX buffer descriptors 788a210576cSDavid S. Miller */ 789a210576cSDavid S. Miller static void fec_enet_bd_init(struct net_device *dev) 790a210576cSDavid S. Miller { 791a210576cSDavid S. Miller struct fec_enet_private *fep = netdev_priv(dev); 7924d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 7934d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 794a210576cSDavid S. Miller struct bufdesc *bdp; 795a210576cSDavid S. Miller unsigned int i; 79659d0f746SFrank Li unsigned int q; 797a210576cSDavid S. Miller 79859d0f746SFrank Li for (q = 0; q < fep->num_rx_queues; q++) { 799a210576cSDavid S. Miller /* Initialize the receive buffer descriptors. */ 80059d0f746SFrank Li rxq = fep->rx_queue[q]; 8017355f276STroy Kisky bdp = rxq->bd.base; 8024d494cdcSFugang Duan 8037355f276STroy Kisky for (i = 0; i < rxq->bd.ring_size; i++) { 804a210576cSDavid S. Miller 805a210576cSDavid S. Miller /* Initialize the BD for every fragment in the page. */ 806a210576cSDavid S. Miller if (bdp->cbd_bufaddr) 8075cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); 808a210576cSDavid S. Miller else 8095cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(0); 8107355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 811a210576cSDavid S. Miller } 812a210576cSDavid S. Miller 813a210576cSDavid S. Miller /* Set the last buffer to wrap */ 8147355f276STroy Kisky bdp = fec_enet_get_prevdesc(bdp, &rxq->bd); 8155cfa3039SJohannes Berg bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 816a210576cSDavid S. Miller 8177355f276STroy Kisky rxq->bd.cur = rxq->bd.base; 81859d0f746SFrank Li } 819a210576cSDavid S. Miller 82059d0f746SFrank Li for (q = 0; q < fep->num_tx_queues; q++) { 821a210576cSDavid S. Miller /* ...and the same for transmit */ 82259d0f746SFrank Li txq = fep->tx_queue[q]; 8237355f276STroy Kisky bdp = txq->bd.base; 8247355f276STroy Kisky txq->bd.cur = bdp; 825a210576cSDavid S. Miller 8267355f276STroy Kisky for (i = 0; i < txq->bd.ring_size; i++) { 827a210576cSDavid S. Miller /* Initialize the BD for every fragment in the page. */ 8285cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(0); 829178e5f57SFugang Duan if (bdp->cbd_bufaddr && 830178e5f57SFugang Duan !IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr))) 831178e5f57SFugang Duan dma_unmap_single(&fep->pdev->dev, 832178e5f57SFugang Duan fec32_to_cpu(bdp->cbd_bufaddr), 833178e5f57SFugang Duan fec16_to_cpu(bdp->cbd_datlen), 834178e5f57SFugang Duan DMA_TO_DEVICE); 8354d494cdcSFugang Duan if (txq->tx_skbuff[i]) { 8364d494cdcSFugang Duan dev_kfree_skb_any(txq->tx_skbuff[i]); 8374d494cdcSFugang Duan txq->tx_skbuff[i] = NULL; 838a210576cSDavid S. Miller } 8395cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(0); 8407355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 841a210576cSDavid S. Miller } 842a210576cSDavid S. Miller 843a210576cSDavid S. Miller /* Set the last buffer to wrap */ 8447355f276STroy Kisky bdp = fec_enet_get_prevdesc(bdp, &txq->bd); 8455cfa3039SJohannes Berg bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 8464d494cdcSFugang Duan txq->dirty_tx = bdp; 847a210576cSDavid S. Miller } 84859d0f746SFrank Li } 84959d0f746SFrank Li 850ce99d0d3SFrank Li static void fec_enet_active_rxring(struct net_device *ndev) 851ce99d0d3SFrank Li { 852ce99d0d3SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 853ce99d0d3SFrank Li int i; 854ce99d0d3SFrank Li 855ce99d0d3SFrank Li for (i = 0; i < fep->num_rx_queues; i++) 85653bb20d1STroy Kisky writel(0, fep->rx_queue[i]->bd.reg_desc_active); 857ce99d0d3SFrank Li } 858ce99d0d3SFrank Li 85959d0f746SFrank Li static void fec_enet_enable_ring(struct net_device *ndev) 86059d0f746SFrank Li { 86159d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 86259d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 86359d0f746SFrank Li struct fec_enet_priv_rx_q *rxq; 86459d0f746SFrank Li int i; 86559d0f746SFrank Li 86659d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) { 86759d0f746SFrank Li rxq = fep->rx_queue[i]; 8687355f276STroy Kisky writel(rxq->bd.dma, fep->hwp + FEC_R_DES_START(i)); 869fbbeefddSAndrew Lunn writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_R_BUFF_SIZE(i)); 87059d0f746SFrank Li 87159d0f746SFrank Li /* enable DMA1/2 */ 87259d0f746SFrank Li if (i) 87359d0f746SFrank Li writel(RCMR_MATCHEN | RCMR_CMP(i), 87459d0f746SFrank Li fep->hwp + FEC_RCMR(i)); 87559d0f746SFrank Li } 87659d0f746SFrank Li 87759d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 87859d0f746SFrank Li txq = fep->tx_queue[i]; 8797355f276STroy Kisky writel(txq->bd.dma, fep->hwp + FEC_X_DES_START(i)); 88059d0f746SFrank Li 88159d0f746SFrank Li /* enable DMA1/2 */ 88259d0f746SFrank Li if (i) 88359d0f746SFrank Li writel(DMA_CLASS_EN | IDLE_SLOPE(i), 88459d0f746SFrank Li fep->hwp + FEC_DMA_CFG(i)); 88559d0f746SFrank Li } 88659d0f746SFrank Li } 88759d0f746SFrank Li 88859d0f746SFrank Li static void fec_enet_reset_skb(struct net_device *ndev) 88959d0f746SFrank Li { 89059d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 89159d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 89259d0f746SFrank Li int i, j; 89359d0f746SFrank Li 89459d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 89559d0f746SFrank Li txq = fep->tx_queue[i]; 89659d0f746SFrank Li 8977355f276STroy Kisky for (j = 0; j < txq->bd.ring_size; j++) { 89859d0f746SFrank Li if (txq->tx_skbuff[j]) { 89959d0f746SFrank Li dev_kfree_skb_any(txq->tx_skbuff[j]); 90059d0f746SFrank Li txq->tx_skbuff[j] = NULL; 90159d0f746SFrank Li } 90259d0f746SFrank Li } 90359d0f746SFrank Li } 90459d0f746SFrank Li } 905a210576cSDavid S. Miller 906dbc64a8eSRussell King /* 907dbc64a8eSRussell King * This function is called to start or restart the FEC during a link 908dbc64a8eSRussell King * change, transmit timeout, or to reconfigure the FEC. The network 909dbc64a8eSRussell King * packet processing for this device must be stopped before this call. 910793fc096SFrank Li */ 911793fc096SFrank Li static void 912ef83337dSRussell King fec_restart(struct net_device *ndev) 913793fc096SFrank Li { 914793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 9154c09eed9SJim Baxter u32 val; 916793fc096SFrank Li u32 temp_mac[2]; 917793fc096SFrank Li u32 rcntl = OPT_FRAME_SIZE | 0x04; 918793fc096SFrank Li u32 ecntl = 0x2; /* ETHEREN */ 919793fc096SFrank Li 920106c314cSFugang Duan /* Whack a reset. We should wait for this. 921106c314cSFugang Duan * For i.MX6SX SOC, enet use AXI bus, we use disable MAC 922106c314cSFugang Duan * instead of reset MAC itself. 923106c314cSFugang Duan */ 9246b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) { 925106c314cSFugang Duan writel(0, fep->hwp + FEC_ECNTRL); 926106c314cSFugang Duan } else { 927793fc096SFrank Li writel(1, fep->hwp + FEC_ECNTRL); 928793fc096SFrank Li udelay(10); 929106c314cSFugang Duan } 930793fc096SFrank Li 931793fc096SFrank Li /* 932793fc096SFrank Li * enet-mac reset will reset mac address registers too, 933793fc096SFrank Li * so need to reconfigure it. 934793fc096SFrank Li */ 935793fc096SFrank Li memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN); 9365cfa3039SJohannes Berg writel((__force u32)cpu_to_be32(temp_mac[0]), 9375cfa3039SJohannes Berg fep->hwp + FEC_ADDR_LOW); 9385cfa3039SJohannes Berg writel((__force u32)cpu_to_be32(temp_mac[1]), 9395cfa3039SJohannes Berg fep->hwp + FEC_ADDR_HIGH); 940793fc096SFrank Li 941793fc096SFrank Li /* Clear any outstanding interrupt. */ 942e17f7fecSNimrod Andy writel(0xffffffff, fep->hwp + FEC_IEVENT); 943793fc096SFrank Li 944a210576cSDavid S. Miller fec_enet_bd_init(ndev); 945a210576cSDavid S. Miller 94659d0f746SFrank Li fec_enet_enable_ring(ndev); 947793fc096SFrank Li 94859d0f746SFrank Li /* Reset tx SKB buffers. */ 94959d0f746SFrank Li fec_enet_reset_skb(ndev); 950793fc096SFrank Li 951793fc096SFrank Li /* Enable MII mode */ 952ef83337dSRussell King if (fep->full_duplex == DUPLEX_FULL) { 953793fc096SFrank Li /* FD enable */ 954793fc096SFrank Li writel(0x04, fep->hwp + FEC_X_CNTRL); 955793fc096SFrank Li } else { 956793fc096SFrank Li /* No Rcv on Xmit */ 957793fc096SFrank Li rcntl |= 0x02; 958793fc096SFrank Li writel(0x0, fep->hwp + FEC_X_CNTRL); 959793fc096SFrank Li } 960793fc096SFrank Li 961793fc096SFrank Li /* Set MII speed */ 962793fc096SFrank Li writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 963793fc096SFrank Li 964d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 96518803495SGreg Ungerer if (fep->quirks & FEC_QUIRK_HAS_RACC) { 9664c09eed9SJim Baxter val = readl(fep->hwp + FEC_RACC); 9673ac72b7bSEric Nelson /* align IP header */ 9683ac72b7bSEric Nelson val |= FEC_RACC_SHIFT16; 9694c09eed9SJim Baxter if (fep->csum_flags & FLAG_RX_CSUM_ENABLED) 9703ac72b7bSEric Nelson /* set RX checksum */ 9714c09eed9SJim Baxter val |= FEC_RACC_OPTIONS; 9724c09eed9SJim Baxter else 9734c09eed9SJim Baxter val &= ~FEC_RACC_OPTIONS; 9744c09eed9SJim Baxter writel(val, fep->hwp + FEC_RACC); 97555cd48c8STroy Kisky writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_FTRL); 97632867fccSFabio Estevam } 977d1391930SGuenter Roeck #endif 9784c09eed9SJim Baxter 979793fc096SFrank Li /* 980793fc096SFrank Li * The phy interface and speed need to get configured 981793fc096SFrank Li * differently on enet-mac. 982793fc096SFrank Li */ 9836b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_ENET_MAC) { 984793fc096SFrank Li /* Enable flow control and length check */ 985793fc096SFrank Li rcntl |= 0x40000000 | 0x00000020; 986793fc096SFrank Li 987793fc096SFrank Li /* RGMII, RMII or MII */ 988e813bb2bSMarkus Pargmann if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII || 989e813bb2bSMarkus Pargmann fep->phy_interface == PHY_INTERFACE_MODE_RGMII_ID || 990e813bb2bSMarkus Pargmann fep->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID || 991e813bb2bSMarkus Pargmann fep->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID) 992793fc096SFrank Li rcntl |= (1 << 6); 993793fc096SFrank Li else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) 994793fc096SFrank Li rcntl |= (1 << 8); 995793fc096SFrank Li else 996793fc096SFrank Li rcntl &= ~(1 << 8); 997793fc096SFrank Li 998793fc096SFrank Li /* 1G, 100M or 10M */ 99945f5c327SPhilippe Reynes if (ndev->phydev) { 100045f5c327SPhilippe Reynes if (ndev->phydev->speed == SPEED_1000) 1001793fc096SFrank Li ecntl |= (1 << 5); 100245f5c327SPhilippe Reynes else if (ndev->phydev->speed == SPEED_100) 1003793fc096SFrank Li rcntl &= ~(1 << 9); 1004793fc096SFrank Li else 1005793fc096SFrank Li rcntl |= (1 << 9); 1006793fc096SFrank Li } 1007793fc096SFrank Li } else { 1008793fc096SFrank Li #ifdef FEC_MIIGSK_ENR 10096b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_USE_GASKET) { 1010793fc096SFrank Li u32 cfgr; 1011793fc096SFrank Li /* disable the gasket and wait */ 1012793fc096SFrank Li writel(0, fep->hwp + FEC_MIIGSK_ENR); 1013793fc096SFrank Li while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4) 1014793fc096SFrank Li udelay(1); 1015793fc096SFrank Li 1016793fc096SFrank Li /* 1017793fc096SFrank Li * configure the gasket: 1018793fc096SFrank Li * RMII, 50 MHz, no loopback, no echo 1019793fc096SFrank Li * MII, 25 MHz, no loopback, no echo 1020793fc096SFrank Li */ 1021793fc096SFrank Li cfgr = (fep->phy_interface == PHY_INTERFACE_MODE_RMII) 1022793fc096SFrank Li ? BM_MIIGSK_CFGR_RMII : BM_MIIGSK_CFGR_MII; 102345f5c327SPhilippe Reynes if (ndev->phydev && ndev->phydev->speed == SPEED_10) 1024793fc096SFrank Li cfgr |= BM_MIIGSK_CFGR_FRCONT_10M; 1025793fc096SFrank Li writel(cfgr, fep->hwp + FEC_MIIGSK_CFGR); 1026793fc096SFrank Li 1027793fc096SFrank Li /* re-enable the gasket */ 1028793fc096SFrank Li writel(2, fep->hwp + FEC_MIIGSK_ENR); 1029793fc096SFrank Li } 1030793fc096SFrank Li #endif 1031793fc096SFrank Li } 1032793fc096SFrank Li 1033d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 1034793fc096SFrank Li /* enable pause frame*/ 1035793fc096SFrank Li if ((fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) || 1036793fc096SFrank Li ((fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) && 103745f5c327SPhilippe Reynes ndev->phydev && ndev->phydev->pause)) { 1038793fc096SFrank Li rcntl |= FEC_ENET_FCE; 1039793fc096SFrank Li 10404c09eed9SJim Baxter /* set FIFO threshold parameter to reduce overrun */ 1041793fc096SFrank Li writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM); 1042793fc096SFrank Li writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL); 1043793fc096SFrank Li writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM); 1044793fc096SFrank Li writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL); 1045793fc096SFrank Li 1046793fc096SFrank Li /* OPD */ 1047793fc096SFrank Li writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD); 1048793fc096SFrank Li } else { 1049793fc096SFrank Li rcntl &= ~FEC_ENET_FCE; 1050793fc096SFrank Li } 1051d1391930SGuenter Roeck #endif /* !defined(CONFIG_M5272) */ 1052793fc096SFrank Li 1053793fc096SFrank Li writel(rcntl, fep->hwp + FEC_R_CNTRL); 1054793fc096SFrank Li 105584fe6182SStefan Wahren /* Setup multicast filter. */ 105684fe6182SStefan Wahren set_multicast_list(ndev); 105784fe6182SStefan Wahren #ifndef CONFIG_M5272 105884fe6182SStefan Wahren writel(0, fep->hwp + FEC_HASH_TABLE_HIGH); 105984fe6182SStefan Wahren writel(0, fep->hwp + FEC_HASH_TABLE_LOW); 106084fe6182SStefan Wahren #endif 106184fe6182SStefan Wahren 10626b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_ENET_MAC) { 1063793fc096SFrank Li /* enable ENET endian swap */ 1064793fc096SFrank Li ecntl |= (1 << 8); 1065793fc096SFrank Li /* enable ENET store and forward mode */ 1066793fc096SFrank Li writel(1 << 8, fep->hwp + FEC_X_WMRK); 1067793fc096SFrank Li } 1068793fc096SFrank Li 1069793fc096SFrank Li if (fep->bufdesc_ex) 1070793fc096SFrank Li ecntl |= (1 << 4); 1071793fc096SFrank Li 107238ae92dcSChris Healy #ifndef CONFIG_M5272 1073b9eef55cSJim Baxter /* Enable the MIB statistic event counters */ 1074b9eef55cSJim Baxter writel(0 << 31, fep->hwp + FEC_MIB_CTRLSTAT); 107538ae92dcSChris Healy #endif 107638ae92dcSChris Healy 1077793fc096SFrank Li /* And last, enable the transmit and receive processing */ 1078793fc096SFrank Li writel(ecntl, fep->hwp + FEC_ECNTRL); 1079ce99d0d3SFrank Li fec_enet_active_rxring(ndev); 1080793fc096SFrank Li 1081793fc096SFrank Li if (fep->bufdesc_ex) 1082793fc096SFrank Li fec_ptp_start_cyclecounter(ndev); 1083793fc096SFrank Li 1084793fc096SFrank Li /* Enable interrupts we wish to service */ 10850c5a3aefSNimrod Andy if (fep->link) 1086793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 10870c5a3aefSNimrod Andy else 10880c5a3aefSNimrod Andy writel(FEC_ENET_MII, fep->hwp + FEC_IMASK); 1089d851b47bSFugang Duan 1090d851b47bSFugang Duan /* Init the interrupt coalescing */ 1091d851b47bSFugang Duan fec_enet_itr_coal_init(ndev); 1092d851b47bSFugang Duan 1093793fc096SFrank Li } 1094793fc096SFrank Li 1095793fc096SFrank Li static void 1096793fc096SFrank Li fec_stop(struct net_device *ndev) 1097793fc096SFrank Li { 1098793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1099de40ed31SNimrod Andy struct fec_platform_data *pdata = fep->pdev->dev.platform_data; 1100793fc096SFrank Li u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8); 1101de40ed31SNimrod Andy u32 val; 1102793fc096SFrank Li 1103793fc096SFrank Li /* We cannot expect a graceful transmit stop without link !!! */ 1104793fc096SFrank Li if (fep->link) { 1105793fc096SFrank Li writel(1, fep->hwp + FEC_X_CNTRL); /* Graceful transmit stop */ 1106793fc096SFrank Li udelay(10); 1107793fc096SFrank Li if (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_GRA)) 110831b7720cSJoe Perches netdev_err(ndev, "Graceful transmit stop did not complete!\n"); 1109793fc096SFrank Li } 1110793fc096SFrank Li 1111106c314cSFugang Duan /* Whack a reset. We should wait for this. 1112106c314cSFugang Duan * For i.MX6SX SOC, enet use AXI bus, we use disable MAC 1113106c314cSFugang Duan * instead of reset MAC itself. 1114106c314cSFugang Duan */ 1115de40ed31SNimrod Andy if (!(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { 11166b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) { 1117106c314cSFugang Duan writel(0, fep->hwp + FEC_ECNTRL); 1118106c314cSFugang Duan } else { 1119793fc096SFrank Li writel(1, fep->hwp + FEC_ECNTRL); 1120793fc096SFrank Li udelay(10); 1121106c314cSFugang Duan } 1122793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 1123de40ed31SNimrod Andy } else { 1124de40ed31SNimrod Andy writel(FEC_DEFAULT_IMASK | FEC_ENET_WAKEUP, fep->hwp + FEC_IMASK); 1125de40ed31SNimrod Andy val = readl(fep->hwp + FEC_ECNTRL); 1126de40ed31SNimrod Andy val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP); 1127de40ed31SNimrod Andy writel(val, fep->hwp + FEC_ECNTRL); 1128de40ed31SNimrod Andy 1129de40ed31SNimrod Andy if (pdata && pdata->sleep_mode_enable) 1130de40ed31SNimrod Andy pdata->sleep_mode_enable(true); 1131de40ed31SNimrod Andy } 1132de40ed31SNimrod Andy writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 1133793fc096SFrank Li 1134793fc096SFrank Li /* We have to keep ENET enabled to have MII interrupt stay working */ 1135de40ed31SNimrod Andy if (fep->quirks & FEC_QUIRK_ENET_MAC && 1136de40ed31SNimrod Andy !(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { 1137793fc096SFrank Li writel(2, fep->hwp + FEC_ECNTRL); 1138793fc096SFrank Li writel(rmii_mode, fep->hwp + FEC_R_CNTRL); 1139793fc096SFrank Li } 1140793fc096SFrank Li } 1141793fc096SFrank Li 1142793fc096SFrank Li 1143793fc096SFrank Li static void 11440290bd29SMichael S. Tsirkin fec_timeout(struct net_device *ndev, unsigned int txqueue) 1145793fc096SFrank Li { 1146793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1147793fc096SFrank Li 1148344756f6SRussell King fec_dump(ndev); 1149344756f6SRussell King 1150793fc096SFrank Li ndev->stats.tx_errors++; 1151793fc096SFrank Li 115236cdc743SRussell King schedule_work(&fep->tx_timeout_work); 115354309fa6SFrank Li } 115454309fa6SFrank Li 115536cdc743SRussell King static void fec_enet_timeout_work(struct work_struct *work) 115654309fa6SFrank Li { 115754309fa6SFrank Li struct fec_enet_private *fep = 115836cdc743SRussell King container_of(work, struct fec_enet_private, tx_timeout_work); 11598ce5624fSRussell King struct net_device *ndev = fep->netdev; 116054309fa6SFrank Li 1161da1774e5SRussell King rtnl_lock(); 11628ce5624fSRussell King if (netif_device_present(ndev) || netif_running(ndev)) { 1163dbc64a8eSRussell King napi_disable(&fep->napi); 1164dbc64a8eSRussell King netif_tx_lock_bh(ndev); 1165ef83337dSRussell King fec_restart(ndev); 1166657ade07SRickard x Andersson netif_tx_wake_all_queues(ndev); 11676af42d42SRussell King netif_tx_unlock_bh(ndev); 1168dbc64a8eSRussell King napi_enable(&fep->napi); 11698ce5624fSRussell King } 1170da1774e5SRussell King rtnl_unlock(); 117154309fa6SFrank Li } 1172793fc096SFrank Li 1173793fc096SFrank Li static void 1174bfd4ecddSRussell King fec_enet_hwtstamp(struct fec_enet_private *fep, unsigned ts, 1175bfd4ecddSRussell King struct skb_shared_hwtstamps *hwtstamps) 1176bfd4ecddSRussell King { 1177bfd4ecddSRussell King unsigned long flags; 1178bfd4ecddSRussell King u64 ns; 1179bfd4ecddSRussell King 1180bfd4ecddSRussell King spin_lock_irqsave(&fep->tmreg_lock, flags); 1181bfd4ecddSRussell King ns = timecounter_cyc2time(&fep->tc, ts); 1182bfd4ecddSRussell King spin_unlock_irqrestore(&fep->tmreg_lock, flags); 1183bfd4ecddSRussell King 1184bfd4ecddSRussell King memset(hwtstamps, 0, sizeof(*hwtstamps)); 1185bfd4ecddSRussell King hwtstamps->hwtstamp = ns_to_ktime(ns); 1186bfd4ecddSRussell King } 1187bfd4ecddSRussell King 1188bfd4ecddSRussell King static void 11894d494cdcSFugang Duan fec_enet_tx_queue(struct net_device *ndev, u16 queue_id) 1190793fc096SFrank Li { 1191793fc096SFrank Li struct fec_enet_private *fep; 1192a2fe37b6SFabio Estevam struct bufdesc *bdp; 1193793fc096SFrank Li unsigned short status; 1194793fc096SFrank Li struct sk_buff *skb; 11954d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 11964d494cdcSFugang Duan struct netdev_queue *nq; 1197793fc096SFrank Li int index = 0; 119879f33912SNimrod Andy int entries_free; 1199793fc096SFrank Li 1200793fc096SFrank Li fep = netdev_priv(ndev); 12014d494cdcSFugang Duan 12024d494cdcSFugang Duan queue_id = FEC_ENET_GET_QUQUE(queue_id); 12034d494cdcSFugang Duan 12044d494cdcSFugang Duan txq = fep->tx_queue[queue_id]; 12054d494cdcSFugang Duan /* get next bdp of dirty_tx */ 12064d494cdcSFugang Duan nq = netdev_get_tx_queue(ndev, queue_id); 12074d494cdcSFugang Duan bdp = txq->dirty_tx; 1208793fc096SFrank Li 1209793fc096SFrank Li /* get next bdp of dirty_tx */ 12107355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 1211793fc096SFrank Li 12127355f276STroy Kisky while (bdp != READ_ONCE(txq->bd.cur)) { 12137355f276STroy Kisky /* Order the load of bd.cur and cbd_sc */ 1214c4bc44c6SKevin Hao rmb(); 12155cfa3039SJohannes Berg status = fec16_to_cpu(READ_ONCE(bdp->cbd_sc)); 1216c4bc44c6SKevin Hao if (status & BD_ENET_TX_READY) 1217793fc096SFrank Li break; 1218793fc096SFrank Li 12197355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 12202b995f63SNimrod Andy 1221a2fe37b6SFabio Estevam skb = txq->tx_skbuff[index]; 1222a2fe37b6SFabio Estevam txq->tx_skbuff[index] = NULL; 12235cfa3039SJohannes Berg if (!IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr))) 12245cfa3039SJohannes Berg dma_unmap_single(&fep->pdev->dev, 12255cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 12265cfa3039SJohannes Berg fec16_to_cpu(bdp->cbd_datlen), 12275cfa3039SJohannes Berg DMA_TO_DEVICE); 12285cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(0); 12297fafe803STroy Kisky if (!skb) 12307fafe803STroy Kisky goto skb_done; 1231793fc096SFrank Li 1232793fc096SFrank Li /* Check for errors. */ 1233793fc096SFrank Li if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC | 1234793fc096SFrank Li BD_ENET_TX_RL | BD_ENET_TX_UN | 1235793fc096SFrank Li BD_ENET_TX_CSL)) { 1236793fc096SFrank Li ndev->stats.tx_errors++; 1237793fc096SFrank Li if (status & BD_ENET_TX_HB) /* No heartbeat */ 1238793fc096SFrank Li ndev->stats.tx_heartbeat_errors++; 1239793fc096SFrank Li if (status & BD_ENET_TX_LC) /* Late collision */ 1240793fc096SFrank Li ndev->stats.tx_window_errors++; 1241793fc096SFrank Li if (status & BD_ENET_TX_RL) /* Retrans limit */ 1242793fc096SFrank Li ndev->stats.tx_aborted_errors++; 1243793fc096SFrank Li if (status & BD_ENET_TX_UN) /* Underrun */ 1244793fc096SFrank Li ndev->stats.tx_fifo_errors++; 1245793fc096SFrank Li if (status & BD_ENET_TX_CSL) /* Carrier lost */ 1246793fc096SFrank Li ndev->stats.tx_carrier_errors++; 1247793fc096SFrank Li } else { 1248793fc096SFrank Li ndev->stats.tx_packets++; 12496e909283SNimrod Andy ndev->stats.tx_bytes += skb->len; 1250793fc096SFrank Li } 1251793fc096SFrank Li 1252793fc096SFrank Li if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) && 1253793fc096SFrank Li fep->bufdesc_ex) { 1254793fc096SFrank Li struct skb_shared_hwtstamps shhwtstamps; 1255793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 1256793fc096SFrank Li 12575cfa3039SJohannes Berg fec_enet_hwtstamp(fep, fec32_to_cpu(ebdp->ts), &shhwtstamps); 1258793fc096SFrank Li skb_tstamp_tx(skb, &shhwtstamps); 1259793fc096SFrank Li } 1260793fc096SFrank Li 1261793fc096SFrank Li /* Deferred means some collisions occurred during transmit, 1262793fc096SFrank Li * but we eventually sent the packet OK. 1263793fc096SFrank Li */ 1264793fc096SFrank Li if (status & BD_ENET_TX_DEF) 1265793fc096SFrank Li ndev->stats.collisions++; 1266793fc096SFrank Li 1267793fc096SFrank Li /* Free the sk buffer associated with this last transmit */ 1268793fc096SFrank Li dev_kfree_skb_any(skb); 12697fafe803STroy Kisky skb_done: 1270c4bc44c6SKevin Hao /* Make sure the update to bdp and tx_skbuff are performed 1271c4bc44c6SKevin Hao * before dirty_tx 1272c4bc44c6SKevin Hao */ 1273c4bc44c6SKevin Hao wmb(); 12744d494cdcSFugang Duan txq->dirty_tx = bdp; 1275793fc096SFrank Li 1276793fc096SFrank Li /* Update pointer to next buffer descriptor to be transmitted */ 12777355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 1278793fc096SFrank Li 1279793fc096SFrank Li /* Since we have freed up a buffer, the ring is no longer full 1280793fc096SFrank Li */ 1281657ade07SRickard x Andersson if (netif_tx_queue_stopped(nq)) { 12827355f276STroy Kisky entries_free = fec_enet_get_free_txdesc_num(txq); 12834d494cdcSFugang Duan if (entries_free >= txq->tx_wake_threshold) 12844d494cdcSFugang Duan netif_tx_wake_queue(nq); 1285793fc096SFrank Li } 128679f33912SNimrod Andy } 1287ccea2968SRussell King 1288c10bc0e7SFugang Duan /* ERR006358: Keep the transmitter going */ 12897355f276STroy Kisky if (bdp != txq->bd.cur && 129053bb20d1STroy Kisky readl(txq->bd.reg_desc_active) == 0) 129153bb20d1STroy Kisky writel(0, txq->bd.reg_desc_active); 12924d494cdcSFugang Duan } 12934d494cdcSFugang Duan 12944d494cdcSFugang Duan static void 12954d494cdcSFugang Duan fec_enet_tx(struct net_device *ndev) 12964d494cdcSFugang Duan { 12974d494cdcSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 12984d494cdcSFugang Duan u16 queue_id; 12994d494cdcSFugang Duan /* First process class A queue, then Class B and Best Effort queue */ 13004d494cdcSFugang Duan for_each_set_bit(queue_id, &fep->work_tx, FEC_ENET_MAX_TX_QS) { 13014d494cdcSFugang Duan clear_bit(queue_id, &fep->work_tx); 13024d494cdcSFugang Duan fec_enet_tx_queue(ndev, queue_id); 13034d494cdcSFugang Duan } 13044d494cdcSFugang Duan return; 1305793fc096SFrank Li } 1306793fc096SFrank Li 13071b7bde6dSNimrod Andy static int 13081b7bde6dSNimrod Andy fec_enet_new_rxbdp(struct net_device *ndev, struct bufdesc *bdp, struct sk_buff *skb) 13091b7bde6dSNimrod Andy { 13101b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 13111b7bde6dSNimrod Andy int off; 13121b7bde6dSNimrod Andy 13131b7bde6dSNimrod Andy off = ((unsigned long)skb->data) & fep->rx_align; 13141b7bde6dSNimrod Andy if (off) 13151b7bde6dSNimrod Andy skb_reserve(skb, fep->rx_align + 1 - off); 13161b7bde6dSNimrod Andy 13175cfa3039SJohannes 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)); 13185cfa3039SJohannes Berg if (dma_mapping_error(&fep->pdev->dev, fec32_to_cpu(bdp->cbd_bufaddr))) { 13191b7bde6dSNimrod Andy if (net_ratelimit()) 13201b7bde6dSNimrod Andy netdev_err(ndev, "Rx DMA memory map failed\n"); 13211b7bde6dSNimrod Andy return -ENOMEM; 13221b7bde6dSNimrod Andy } 13231b7bde6dSNimrod Andy 13241b7bde6dSNimrod Andy return 0; 13251b7bde6dSNimrod Andy } 13261b7bde6dSNimrod Andy 13271b7bde6dSNimrod Andy static bool fec_enet_copybreak(struct net_device *ndev, struct sk_buff **skb, 13281310b544SLothar Waßmann struct bufdesc *bdp, u32 length, bool swap) 13291b7bde6dSNimrod Andy { 13301b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 13311b7bde6dSNimrod Andy struct sk_buff *new_skb; 13321b7bde6dSNimrod Andy 13331b7bde6dSNimrod Andy if (length > fep->rx_copybreak) 13341b7bde6dSNimrod Andy return false; 13351b7bde6dSNimrod Andy 13361b7bde6dSNimrod Andy new_skb = netdev_alloc_skb(ndev, length); 13371b7bde6dSNimrod Andy if (!new_skb) 13381b7bde6dSNimrod Andy return false; 13391b7bde6dSNimrod Andy 13405cfa3039SJohannes Berg dma_sync_single_for_cpu(&fep->pdev->dev, 13415cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 13421b7bde6dSNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 13431b7bde6dSNimrod Andy DMA_FROM_DEVICE); 13441310b544SLothar Waßmann if (!swap) 13451b7bde6dSNimrod Andy memcpy(new_skb->data, (*skb)->data, length); 13461310b544SLothar Waßmann else 13471310b544SLothar Waßmann swap_buffer2(new_skb->data, (*skb)->data, length); 13481b7bde6dSNimrod Andy *skb = new_skb; 13491b7bde6dSNimrod Andy 13501b7bde6dSNimrod Andy return true; 13511b7bde6dSNimrod Andy } 13521b7bde6dSNimrod Andy 13537355f276STroy Kisky /* During a receive, the bd_rx.cur points to the current incoming buffer. 1354793fc096SFrank Li * When we update through the ring, if the next incoming buffer has 1355793fc096SFrank Li * not been given to the system, we just set the empty indicator, 1356793fc096SFrank Li * effectively tossing the packet. 1357793fc096SFrank Li */ 1358793fc096SFrank Li static int 13594d494cdcSFugang Duan fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) 1360793fc096SFrank Li { 1361793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 13624d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 1363793fc096SFrank Li struct bufdesc *bdp; 1364793fc096SFrank Li unsigned short status; 13651b7bde6dSNimrod Andy struct sk_buff *skb_new = NULL; 1366793fc096SFrank Li struct sk_buff *skb; 1367793fc096SFrank Li ushort pkt_len; 1368793fc096SFrank Li __u8 *data; 1369793fc096SFrank Li int pkt_received = 0; 1370cdffcf1bSJim Baxter struct bufdesc_ex *ebdp = NULL; 1371cdffcf1bSJim Baxter bool vlan_packet_rcvd = false; 1372cdffcf1bSJim Baxter u16 vlan_tag; 1373d842a31fSDuan Fugang-B38611 int index = 0; 13741b7bde6dSNimrod Andy bool is_copybreak; 13756b7e4008SLothar Waßmann bool need_swap = fep->quirks & FEC_QUIRK_SWAP_FRAME; 1376793fc096SFrank Li 1377793fc096SFrank Li #ifdef CONFIG_M532x 1378793fc096SFrank Li flush_cache_all(); 1379793fc096SFrank Li #endif 13804d494cdcSFugang Duan queue_id = FEC_ENET_GET_QUQUE(queue_id); 13814d494cdcSFugang Duan rxq = fep->rx_queue[queue_id]; 1382793fc096SFrank Li 1383793fc096SFrank Li /* First, grab all of the stats for the incoming packet. 1384793fc096SFrank Li * These get messed up if we get called due to a busy condition. 1385793fc096SFrank Li */ 13867355f276STroy Kisky bdp = rxq->bd.cur; 1387793fc096SFrank Li 13885cfa3039SJohannes Berg while (!((status = fec16_to_cpu(bdp->cbd_sc)) & BD_ENET_RX_EMPTY)) { 1389793fc096SFrank Li 1390793fc096SFrank Li if (pkt_received >= budget) 1391793fc096SFrank Li break; 1392793fc096SFrank Li pkt_received++; 1393793fc096SFrank Li 1394ed63f1dcSRussell King writel(FEC_ENET_RXF, fep->hwp + FEC_IEVENT); 1395db3421c1SRussell King 1396793fc096SFrank Li /* Check for errors. */ 1397095098e1STroy Kisky status ^= BD_ENET_RX_LAST; 1398793fc096SFrank Li if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | 1399095098e1STroy Kisky BD_ENET_RX_CR | BD_ENET_RX_OV | BD_ENET_RX_LAST | 1400095098e1STroy Kisky BD_ENET_RX_CL)) { 1401793fc096SFrank Li ndev->stats.rx_errors++; 1402095098e1STroy Kisky if (status & BD_ENET_RX_OV) { 1403095098e1STroy Kisky /* FIFO overrun */ 1404095098e1STroy Kisky ndev->stats.rx_fifo_errors++; 1405095098e1STroy Kisky goto rx_processing_done; 1406095098e1STroy Kisky } 1407095098e1STroy Kisky if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH 1408095098e1STroy Kisky | BD_ENET_RX_LAST)) { 1409793fc096SFrank Li /* Frame too long or too short. */ 1410793fc096SFrank Li ndev->stats.rx_length_errors++; 1411095098e1STroy Kisky if (status & BD_ENET_RX_LAST) 1412095098e1STroy Kisky netdev_err(ndev, "rcv is not +last\n"); 1413793fc096SFrank Li } 1414793fc096SFrank Li if (status & BD_ENET_RX_CR) /* CRC Error */ 1415793fc096SFrank Li ndev->stats.rx_crc_errors++; 1416095098e1STroy Kisky /* Report late collisions as a frame error. */ 1417095098e1STroy Kisky if (status & (BD_ENET_RX_NO | BD_ENET_RX_CL)) 1418793fc096SFrank Li ndev->stats.rx_frame_errors++; 1419793fc096SFrank Li goto rx_processing_done; 1420793fc096SFrank Li } 1421793fc096SFrank Li 1422793fc096SFrank Li /* Process the incoming frame. */ 1423793fc096SFrank Li ndev->stats.rx_packets++; 14245cfa3039SJohannes Berg pkt_len = fec16_to_cpu(bdp->cbd_datlen); 1425793fc096SFrank Li ndev->stats.rx_bytes += pkt_len; 1426793fc096SFrank Li 14277355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &rxq->bd); 14281b7bde6dSNimrod Andy skb = rxq->rx_skbuff[index]; 14291b7bde6dSNimrod Andy 14301b7bde6dSNimrod Andy /* The packet length includes FCS, but we don't want to 14311b7bde6dSNimrod Andy * include that when passing upstream as it messes up 14321b7bde6dSNimrod Andy * bridging applications. 14331b7bde6dSNimrod Andy */ 14341310b544SLothar Waßmann is_copybreak = fec_enet_copybreak(ndev, &skb, bdp, pkt_len - 4, 14351310b544SLothar Waßmann need_swap); 14361b7bde6dSNimrod Andy if (!is_copybreak) { 14371b7bde6dSNimrod Andy skb_new = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE); 14381b7bde6dSNimrod Andy if (unlikely(!skb_new)) { 14391b7bde6dSNimrod Andy ndev->stats.rx_dropped++; 14401b7bde6dSNimrod Andy goto rx_processing_done; 14411b7bde6dSNimrod Andy } 14425cfa3039SJohannes Berg dma_unmap_single(&fep->pdev->dev, 14435cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 1444b64bf4b7SNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 1445b64bf4b7SNimrod Andy DMA_FROM_DEVICE); 14461b7bde6dSNimrod Andy } 1447793fc096SFrank Li 14481b7bde6dSNimrod Andy prefetch(skb->data - NET_IP_ALIGN); 14491b7bde6dSNimrod Andy skb_put(skb, pkt_len - 4); 14501b7bde6dSNimrod Andy data = skb->data; 14513ac72b7bSEric Nelson 1452235bde1eSFabio Estevam if (!is_copybreak && need_swap) 1453235bde1eSFabio Estevam swap_buffer(data, pkt_len); 1454235bde1eSFabio Estevam 14553ac72b7bSEric Nelson #if !defined(CONFIG_M5272) 14563ac72b7bSEric Nelson if (fep->quirks & FEC_QUIRK_HAS_RACC) 14573ac72b7bSEric Nelson data = skb_pull_inline(skb, 2); 14583ac72b7bSEric Nelson #endif 14593ac72b7bSEric Nelson 1460cdffcf1bSJim Baxter /* Extract the enhanced buffer descriptor */ 1461cdffcf1bSJim Baxter ebdp = NULL; 1462cdffcf1bSJim Baxter if (fep->bufdesc_ex) 1463cdffcf1bSJim Baxter ebdp = (struct bufdesc_ex *)bdp; 1464cdffcf1bSJim Baxter 1465cdffcf1bSJim Baxter /* If this is a VLAN packet remove the VLAN Tag */ 1466cdffcf1bSJim Baxter vlan_packet_rcvd = false; 1467cdffcf1bSJim Baxter if ((ndev->features & NETIF_F_HW_VLAN_CTAG_RX) && 14685cfa3039SJohannes Berg fep->bufdesc_ex && 14695cfa3039SJohannes Berg (ebdp->cbd_esc & cpu_to_fec32(BD_ENET_RX_VLAN))) { 1470cdffcf1bSJim Baxter /* Push and remove the vlan tag */ 1471cdffcf1bSJim Baxter struct vlan_hdr *vlan_header = 1472cdffcf1bSJim Baxter (struct vlan_hdr *) (data + ETH_HLEN); 1473cdffcf1bSJim Baxter vlan_tag = ntohs(vlan_header->h_vlan_TCI); 1474cdffcf1bSJim Baxter 1475cdffcf1bSJim Baxter vlan_packet_rcvd = true; 14761b7bde6dSNimrod Andy 1477af5cbc98SNimrod Andy memmove(skb->data + VLAN_HLEN, data, ETH_ALEN * 2); 14781b7bde6dSNimrod Andy skb_pull(skb, VLAN_HLEN); 1479cdffcf1bSJim Baxter } 1480cdffcf1bSJim Baxter 1481793fc096SFrank Li skb->protocol = eth_type_trans(skb, ndev); 1482793fc096SFrank Li 1483793fc096SFrank Li /* Get receive timestamp from the skb */ 1484bfd4ecddSRussell King if (fep->hwts_rx_en && fep->bufdesc_ex) 14855cfa3039SJohannes Berg fec_enet_hwtstamp(fep, fec32_to_cpu(ebdp->ts), 1486bfd4ecddSRussell King skb_hwtstamps(skb)); 1487793fc096SFrank Li 14884c09eed9SJim Baxter if (fep->bufdesc_ex && 14894c09eed9SJim Baxter (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { 14905cfa3039SJohannes Berg if (!(ebdp->cbd_esc & cpu_to_fec32(FLAG_RX_CSUM_ERROR))) { 14914c09eed9SJim Baxter /* don't check it */ 14924c09eed9SJim Baxter skb->ip_summed = CHECKSUM_UNNECESSARY; 14934c09eed9SJim Baxter } else { 14944c09eed9SJim Baxter skb_checksum_none_assert(skb); 14954c09eed9SJim Baxter } 14964c09eed9SJim Baxter } 14974c09eed9SJim Baxter 1498cdffcf1bSJim Baxter /* Handle received VLAN packets */ 1499cdffcf1bSJim Baxter if (vlan_packet_rcvd) 1500cdffcf1bSJim Baxter __vlan_hwaccel_put_tag(skb, 1501cdffcf1bSJim Baxter htons(ETH_P_8021Q), 1502cdffcf1bSJim Baxter vlan_tag); 1503cdffcf1bSJim Baxter 1504793fc096SFrank Li napi_gro_receive(&fep->napi, skb); 1505793fc096SFrank Li 15061b7bde6dSNimrod Andy if (is_copybreak) { 15075cfa3039SJohannes Berg dma_sync_single_for_device(&fep->pdev->dev, 15085cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 1509b64bf4b7SNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 1510b64bf4b7SNimrod Andy DMA_FROM_DEVICE); 15111b7bde6dSNimrod Andy } else { 15121b7bde6dSNimrod Andy rxq->rx_skbuff[index] = skb_new; 15131b7bde6dSNimrod Andy fec_enet_new_rxbdp(ndev, bdp, skb_new); 15141b7bde6dSNimrod Andy } 15151b7bde6dSNimrod Andy 1516793fc096SFrank Li rx_processing_done: 1517793fc096SFrank Li /* Clear the status flags for this buffer */ 1518793fc096SFrank Li status &= ~BD_ENET_RX_STATS; 1519793fc096SFrank Li 1520793fc096SFrank Li /* Mark the buffer empty */ 1521793fc096SFrank Li status |= BD_ENET_RX_EMPTY; 1522793fc096SFrank Li 1523793fc096SFrank Li if (fep->bufdesc_ex) { 1524793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 1525793fc096SFrank Li 15265cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(BD_ENET_RX_INT); 1527793fc096SFrank Li ebdp->cbd_prot = 0; 1528793fc096SFrank Li ebdp->cbd_bdu = 0; 1529793fc096SFrank Li } 1530be293467STroy Kisky /* Make sure the updates to rest of the descriptor are 1531be293467STroy Kisky * performed before transferring ownership. 1532be293467STroy Kisky */ 1533be293467STroy Kisky wmb(); 1534be293467STroy Kisky bdp->cbd_sc = cpu_to_fec16(status); 1535793fc096SFrank Li 1536793fc096SFrank Li /* Update BD pointer to next entry */ 15377355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 153836e24e2eSDuan Fugang-B38611 1539793fc096SFrank Li /* Doing this here will keep the FEC running while we process 1540793fc096SFrank Li * incoming frames. On a heavily loaded network, we should be 1541793fc096SFrank Li * able to keep up at the expense of system resources. 1542793fc096SFrank Li */ 154353bb20d1STroy Kisky writel(0, rxq->bd.reg_desc_active); 1544793fc096SFrank Li } 15457355f276STroy Kisky rxq->bd.cur = bdp; 1546793fc096SFrank Li return pkt_received; 1547793fc096SFrank Li } 1548793fc096SFrank Li 15494d494cdcSFugang Duan static int 15504d494cdcSFugang Duan fec_enet_rx(struct net_device *ndev, int budget) 15514d494cdcSFugang Duan { 15524d494cdcSFugang Duan int pkt_received = 0; 15534d494cdcSFugang Duan u16 queue_id; 15544d494cdcSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 15554d494cdcSFugang Duan 15564d494cdcSFugang Duan for_each_set_bit(queue_id, &fep->work_rx, FEC_ENET_MAX_RX_QS) { 15571c021bb7SUwe Kleine-König int ret; 15581c021bb7SUwe Kleine-König 15591c021bb7SUwe Kleine-König ret = fec_enet_rx_queue(ndev, 15604d494cdcSFugang Duan budget - pkt_received, queue_id); 15611c021bb7SUwe Kleine-König 15621c021bb7SUwe Kleine-König if (ret < budget - pkt_received) 15631c021bb7SUwe Kleine-König clear_bit(queue_id, &fep->work_rx); 15641c021bb7SUwe Kleine-König 15651c021bb7SUwe Kleine-König pkt_received += ret; 15664d494cdcSFugang Duan } 15674d494cdcSFugang Duan return pkt_received; 15684d494cdcSFugang Duan } 15694d494cdcSFugang Duan 15704d494cdcSFugang Duan static bool 15714d494cdcSFugang Duan fec_enet_collect_events(struct fec_enet_private *fep, uint int_events) 15724d494cdcSFugang Duan { 15734d494cdcSFugang Duan if (int_events == 0) 15744d494cdcSFugang Duan return false; 15754d494cdcSFugang Duan 15765e62d98cSTroy Kisky if (int_events & FEC_ENET_RXF_0) 15774d494cdcSFugang Duan fep->work_rx |= (1 << 2); 1578ce99d0d3SFrank Li if (int_events & FEC_ENET_RXF_1) 1579ce99d0d3SFrank Li fep->work_rx |= (1 << 0); 1580ce99d0d3SFrank Li if (int_events & FEC_ENET_RXF_2) 1581ce99d0d3SFrank Li fep->work_rx |= (1 << 1); 15824d494cdcSFugang Duan 15835e62d98cSTroy Kisky if (int_events & FEC_ENET_TXF_0) 15844d494cdcSFugang Duan fep->work_tx |= (1 << 2); 1585ce99d0d3SFrank Li if (int_events & FEC_ENET_TXF_1) 1586ce99d0d3SFrank Li fep->work_tx |= (1 << 0); 1587ce99d0d3SFrank Li if (int_events & FEC_ENET_TXF_2) 1588ce99d0d3SFrank Li fep->work_tx |= (1 << 1); 15894d494cdcSFugang Duan 15904d494cdcSFugang Duan return true; 15914d494cdcSFugang Duan } 15924d494cdcSFugang Duan 1593793fc096SFrank Li static irqreturn_t 1594793fc096SFrank Li fec_enet_interrupt(int irq, void *dev_id) 1595793fc096SFrank Li { 1596793fc096SFrank Li struct net_device *ndev = dev_id; 1597793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1598793fc096SFrank Li uint int_events; 1599793fc096SFrank Li irqreturn_t ret = IRQ_NONE; 1600793fc096SFrank Li 1601793fc096SFrank Li int_events = readl(fep->hwp + FEC_IEVENT); 160294191fd6SNimrod Andy writel(int_events, fep->hwp + FEC_IEVENT); 16034d494cdcSFugang Duan fec_enet_collect_events(fep, int_events); 1604793fc096SFrank Li 160561615cd2SFugang Duan if ((fep->work_tx || fep->work_rx) && fep->link) { 1606793fc096SFrank Li ret = IRQ_HANDLED; 1607793fc096SFrank Li 160894191fd6SNimrod Andy if (napi_schedule_prep(&fep->napi)) { 16097a16807cSRussell King /* Disable the NAPI interrupts */ 161080dc6a9fSTroy Kisky writel(FEC_NAPI_IMASK, fep->hwp + FEC_IMASK); 161194191fd6SNimrod Andy __napi_schedule(&fep->napi); 161294191fd6SNimrod Andy } 1613793fc096SFrank Li } 1614793fc096SFrank Li 1615793fc096SFrank Li if (int_events & FEC_ENET_MII) { 1616793fc096SFrank Li ret = IRQ_HANDLED; 1617793fc096SFrank Li complete(&fep->mdio_done); 1618793fc096SFrank Li } 1619793fc096SFrank Li return ret; 1620793fc096SFrank Li } 1621793fc096SFrank Li 1622793fc096SFrank Li static int fec_enet_rx_napi(struct napi_struct *napi, int budget) 1623793fc096SFrank Li { 1624793fc096SFrank Li struct net_device *ndev = napi->dev; 1625793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 16267a16807cSRussell King int pkts; 16277a16807cSRussell King 16287a16807cSRussell King pkts = fec_enet_rx(ndev, budget); 1629793fc096SFrank Li 1630793fc096SFrank Li fec_enet_tx(ndev); 1631793fc096SFrank Li 1632793fc096SFrank Li if (pkts < budget) { 16336ad20165SEric Dumazet napi_complete_done(napi, pkts); 1634793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 1635793fc096SFrank Li } 1636793fc096SFrank Li return pkts; 1637793fc096SFrank Li } 1638793fc096SFrank Li 1639793fc096SFrank Li /* ------------------------------------------------------------------------- */ 1640793fc096SFrank Li static void fec_get_mac(struct net_device *ndev) 1641793fc096SFrank Li { 1642793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 164394660ba0SJingoo Han struct fec_platform_data *pdata = dev_get_platdata(&fep->pdev->dev); 1644793fc096SFrank Li unsigned char *iap, tmpaddr[ETH_ALEN]; 1645793fc096SFrank Li 1646793fc096SFrank Li /* 1647793fc096SFrank Li * try to get mac address in following order: 1648793fc096SFrank Li * 1649793fc096SFrank Li * 1) module parameter via kernel command line in form 1650793fc096SFrank Li * fec.macaddr=0x00,0x04,0x9f,0x01,0x30,0xe0 1651793fc096SFrank Li */ 1652793fc096SFrank Li iap = macaddr; 1653793fc096SFrank Li 1654793fc096SFrank Li /* 1655793fc096SFrank Li * 2) from device tree data 1656793fc096SFrank Li */ 1657793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 1658793fc096SFrank Li struct device_node *np = fep->pdev->dev.of_node; 1659793fc096SFrank Li if (np) { 1660793fc096SFrank Li const char *mac = of_get_mac_address(np); 1661a51645f7SPetr Štetiar if (!IS_ERR(mac)) 1662793fc096SFrank Li iap = (unsigned char *) mac; 1663793fc096SFrank Li } 1664793fc096SFrank Li } 1665793fc096SFrank Li 1666793fc096SFrank Li /* 1667793fc096SFrank Li * 3) from flash or fuse (via platform data) 1668793fc096SFrank Li */ 1669793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 1670793fc096SFrank Li #ifdef CONFIG_M5272 1671793fc096SFrank Li if (FEC_FLASHMAC) 1672793fc096SFrank Li iap = (unsigned char *)FEC_FLASHMAC; 1673793fc096SFrank Li #else 1674793fc096SFrank Li if (pdata) 1675793fc096SFrank Li iap = (unsigned char *)&pdata->mac; 1676793fc096SFrank Li #endif 1677793fc096SFrank Li } 1678793fc096SFrank Li 1679793fc096SFrank Li /* 1680793fc096SFrank Li * 4) FEC mac registers set by bootloader 1681793fc096SFrank Li */ 1682793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 16837d7628f3SDan Carpenter *((__be32 *) &tmpaddr[0]) = 16847d7628f3SDan Carpenter cpu_to_be32(readl(fep->hwp + FEC_ADDR_LOW)); 16857d7628f3SDan Carpenter *((__be16 *) &tmpaddr[4]) = 16867d7628f3SDan Carpenter cpu_to_be16(readl(fep->hwp + FEC_ADDR_HIGH) >> 16); 1687793fc096SFrank Li iap = &tmpaddr[0]; 1688793fc096SFrank Li } 1689793fc096SFrank Li 1690ff5b2fabSLucas Stach /* 1691ff5b2fabSLucas Stach * 5) random mac address 1692ff5b2fabSLucas Stach */ 1693ff5b2fabSLucas Stach if (!is_valid_ether_addr(iap)) { 1694ff5b2fabSLucas Stach /* Report it and use a random ethernet address instead */ 1695a19a0582SFabio Estevam dev_err(&fep->pdev->dev, "Invalid MAC address: %pM\n", iap); 1696ff5b2fabSLucas Stach eth_hw_addr_random(ndev); 1697a19a0582SFabio Estevam dev_info(&fep->pdev->dev, "Using random MAC address: %pM\n", 1698ff5b2fabSLucas Stach ndev->dev_addr); 1699ff5b2fabSLucas Stach return; 1700ff5b2fabSLucas Stach } 1701ff5b2fabSLucas Stach 1702793fc096SFrank Li memcpy(ndev->dev_addr, iap, ETH_ALEN); 1703793fc096SFrank Li 1704793fc096SFrank Li /* Adjust MAC if using macaddr */ 1705793fc096SFrank Li if (iap == macaddr) 1706793fc096SFrank Li ndev->dev_addr[ETH_ALEN-1] = macaddr[ETH_ALEN-1] + fep->dev_id; 1707793fc096SFrank Li } 1708793fc096SFrank Li 1709793fc096SFrank Li /* ------------------------------------------------------------------------- */ 1710793fc096SFrank Li 1711793fc096SFrank Li /* 1712793fc096SFrank Li * Phy section 1713793fc096SFrank Li */ 1714793fc096SFrank Li static void fec_enet_adjust_link(struct net_device *ndev) 1715793fc096SFrank Li { 1716793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 171745f5c327SPhilippe Reynes struct phy_device *phy_dev = ndev->phydev; 1718793fc096SFrank Li int status_change = 0; 1719793fc096SFrank Li 17208ce5624fSRussell King /* 17218ce5624fSRussell King * If the netdev is down, or is going down, we're not interested 17228ce5624fSRussell King * in link state events, so just mark our idea of the link as down 17238ce5624fSRussell King * and ignore the event. 17248ce5624fSRussell King */ 17258ce5624fSRussell King if (!netif_running(ndev) || !netif_device_present(ndev)) { 17268ce5624fSRussell King fep->link = 0; 17278ce5624fSRussell King } else if (phy_dev->link) { 1728793fc096SFrank Li if (!fep->link) { 1729793fc096SFrank Li fep->link = phy_dev->link; 1730793fc096SFrank Li status_change = 1; 1731793fc096SFrank Li } 1732793fc096SFrank Li 1733ef83337dSRussell King if (fep->full_duplex != phy_dev->duplex) { 1734ef83337dSRussell King fep->full_duplex = phy_dev->duplex; 1735793fc096SFrank Li status_change = 1; 1736ef83337dSRussell King } 1737793fc096SFrank Li 1738793fc096SFrank Li if (phy_dev->speed != fep->speed) { 1739793fc096SFrank Li fep->speed = phy_dev->speed; 1740793fc096SFrank Li status_change = 1; 1741793fc096SFrank Li } 1742793fc096SFrank Li 1743793fc096SFrank Li /* if any of the above changed restart the FEC */ 1744dbc64a8eSRussell King if (status_change) { 1745dbc64a8eSRussell King napi_disable(&fep->napi); 1746dbc64a8eSRussell King netif_tx_lock_bh(ndev); 1747ef83337dSRussell King fec_restart(ndev); 1748657ade07SRickard x Andersson netif_tx_wake_all_queues(ndev); 17496af42d42SRussell King netif_tx_unlock_bh(ndev); 1750dbc64a8eSRussell King napi_enable(&fep->napi); 1751dbc64a8eSRussell King } 1752793fc096SFrank Li } else { 1753793fc096SFrank Li if (fep->link) { 1754f208ce10SRussell King napi_disable(&fep->napi); 1755f208ce10SRussell King netif_tx_lock_bh(ndev); 1756793fc096SFrank Li fec_stop(ndev); 1757f208ce10SRussell King netif_tx_unlock_bh(ndev); 1758f208ce10SRussell King napi_enable(&fep->napi); 17596e0895c2SDavid S. Miller fep->link = phy_dev->link; 1760793fc096SFrank Li status_change = 1; 1761793fc096SFrank Li } 1762793fc096SFrank Li } 1763793fc096SFrank Li 1764793fc096SFrank Li if (status_change) 1765793fc096SFrank Li phy_print_status(phy_dev); 1766793fc096SFrank Li } 1767793fc096SFrank Li 1768793fc096SFrank Li static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum) 1769793fc096SFrank Li { 1770793fc096SFrank Li struct fec_enet_private *fep = bus->priv; 17718fff755eSAndrew Lunn struct device *dev = &fep->pdev->dev; 1772793fc096SFrank Li unsigned long time_left; 1773d3ee8ec7SMarco Hartmann int ret = 0, frame_start, frame_addr, frame_op; 1774d3ee8ec7SMarco Hartmann bool is_c45 = !!(regnum & MII_ADDR_C45); 17758fff755eSAndrew Lunn 17768fff755eSAndrew Lunn ret = pm_runtime_get_sync(dev); 1777b0c6ce24SFabio Estevam if (ret < 0) 17788fff755eSAndrew Lunn return ret; 1779793fc096SFrank Li 1780aac27c7aSRussell King reinit_completion(&fep->mdio_done); 1781793fc096SFrank Li 1782d3ee8ec7SMarco Hartmann if (is_c45) { 1783d3ee8ec7SMarco Hartmann frame_start = FEC_MMFR_ST_C45; 1784d3ee8ec7SMarco Hartmann 1785d3ee8ec7SMarco Hartmann /* write address */ 1786d3ee8ec7SMarco Hartmann frame_addr = (regnum >> 16); 1787d3ee8ec7SMarco Hartmann writel(frame_start | FEC_MMFR_OP_ADDR_WRITE | 1788d3ee8ec7SMarco Hartmann FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | 1789d3ee8ec7SMarco Hartmann FEC_MMFR_TA | (regnum & 0xFFFF), 1790d3ee8ec7SMarco Hartmann fep->hwp + FEC_MII_DATA); 1791d3ee8ec7SMarco Hartmann 1792d3ee8ec7SMarco Hartmann /* wait for end of transfer */ 1793d3ee8ec7SMarco Hartmann time_left = wait_for_completion_timeout(&fep->mdio_done, 1794d3ee8ec7SMarco Hartmann usecs_to_jiffies(FEC_MII_TIMEOUT)); 1795d3ee8ec7SMarco Hartmann if (time_left == 0) { 1796d3ee8ec7SMarco Hartmann netdev_err(fep->netdev, "MDIO address write timeout\n"); 1797d3ee8ec7SMarco Hartmann ret = -ETIMEDOUT; 1798d3ee8ec7SMarco Hartmann goto out; 1799d3ee8ec7SMarco Hartmann } 1800d3ee8ec7SMarco Hartmann 1801d3ee8ec7SMarco Hartmann frame_op = FEC_MMFR_OP_READ_C45; 1802d3ee8ec7SMarco Hartmann 1803d3ee8ec7SMarco Hartmann } else { 1804d3ee8ec7SMarco Hartmann /* C22 read */ 1805d3ee8ec7SMarco Hartmann frame_op = FEC_MMFR_OP_READ; 1806d3ee8ec7SMarco Hartmann frame_start = FEC_MMFR_ST; 1807d3ee8ec7SMarco Hartmann frame_addr = regnum; 1808d3ee8ec7SMarco Hartmann } 1809d3ee8ec7SMarco Hartmann 1810793fc096SFrank Li /* start a read op */ 1811d3ee8ec7SMarco Hartmann writel(frame_start | frame_op | 1812d3ee8ec7SMarco Hartmann FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | 1813793fc096SFrank Li FEC_MMFR_TA, fep->hwp + FEC_MII_DATA); 1814793fc096SFrank Li 1815793fc096SFrank Li /* wait for end of transfer */ 1816793fc096SFrank Li time_left = wait_for_completion_timeout(&fep->mdio_done, 1817793fc096SFrank Li usecs_to_jiffies(FEC_MII_TIMEOUT)); 1818793fc096SFrank Li if (time_left == 0) { 181931b7720cSJoe Perches netdev_err(fep->netdev, "MDIO read timeout\n"); 18208fff755eSAndrew Lunn ret = -ETIMEDOUT; 18218fff755eSAndrew Lunn goto out; 1822793fc096SFrank Li } 1823793fc096SFrank Li 18248fff755eSAndrew Lunn ret = FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA)); 18258fff755eSAndrew Lunn 18268fff755eSAndrew Lunn out: 18278fff755eSAndrew Lunn pm_runtime_mark_last_busy(dev); 18288fff755eSAndrew Lunn pm_runtime_put_autosuspend(dev); 18298fff755eSAndrew Lunn 18308fff755eSAndrew Lunn return ret; 1831793fc096SFrank Li } 1832793fc096SFrank Li 1833793fc096SFrank Li static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum, 1834793fc096SFrank Li u16 value) 1835793fc096SFrank Li { 1836793fc096SFrank Li struct fec_enet_private *fep = bus->priv; 18378fff755eSAndrew Lunn struct device *dev = &fep->pdev->dev; 1838793fc096SFrank Li unsigned long time_left; 1839d3ee8ec7SMarco Hartmann int ret, frame_start, frame_addr; 1840d3ee8ec7SMarco Hartmann bool is_c45 = !!(regnum & MII_ADDR_C45); 18418fff755eSAndrew Lunn 18428fff755eSAndrew Lunn ret = pm_runtime_get_sync(dev); 1843b0c6ce24SFabio Estevam if (ret < 0) 18448fff755eSAndrew Lunn return ret; 184542ea4457SMaciej S. Szmigiero else 184642ea4457SMaciej S. Szmigiero ret = 0; 1847793fc096SFrank Li 1848aac27c7aSRussell King reinit_completion(&fep->mdio_done); 1849793fc096SFrank Li 1850d3ee8ec7SMarco Hartmann if (is_c45) { 1851d3ee8ec7SMarco Hartmann frame_start = FEC_MMFR_ST_C45; 1852d3ee8ec7SMarco Hartmann 1853d3ee8ec7SMarco Hartmann /* write address */ 1854d3ee8ec7SMarco Hartmann frame_addr = (regnum >> 16); 1855d3ee8ec7SMarco Hartmann writel(frame_start | FEC_MMFR_OP_ADDR_WRITE | 1856d3ee8ec7SMarco Hartmann FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | 1857d3ee8ec7SMarco Hartmann FEC_MMFR_TA | (regnum & 0xFFFF), 1858d3ee8ec7SMarco Hartmann fep->hwp + FEC_MII_DATA); 1859d3ee8ec7SMarco Hartmann 1860d3ee8ec7SMarco Hartmann /* wait for end of transfer */ 1861d3ee8ec7SMarco Hartmann time_left = wait_for_completion_timeout(&fep->mdio_done, 1862d3ee8ec7SMarco Hartmann usecs_to_jiffies(FEC_MII_TIMEOUT)); 1863d3ee8ec7SMarco Hartmann if (time_left == 0) { 1864d3ee8ec7SMarco Hartmann netdev_err(fep->netdev, "MDIO address write timeout\n"); 1865d3ee8ec7SMarco Hartmann ret = -ETIMEDOUT; 1866d3ee8ec7SMarco Hartmann goto out; 1867d3ee8ec7SMarco Hartmann } 1868d3ee8ec7SMarco Hartmann } else { 1869d3ee8ec7SMarco Hartmann /* C22 write */ 1870d3ee8ec7SMarco Hartmann frame_start = FEC_MMFR_ST; 1871d3ee8ec7SMarco Hartmann frame_addr = regnum; 1872d3ee8ec7SMarco Hartmann } 1873d3ee8ec7SMarco Hartmann 1874793fc096SFrank Li /* start a write op */ 1875d3ee8ec7SMarco Hartmann writel(frame_start | FEC_MMFR_OP_WRITE | 1876d3ee8ec7SMarco Hartmann FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | 1877793fc096SFrank Li FEC_MMFR_TA | FEC_MMFR_DATA(value), 1878793fc096SFrank Li fep->hwp + FEC_MII_DATA); 1879793fc096SFrank Li 1880793fc096SFrank Li /* wait for end of transfer */ 1881793fc096SFrank Li time_left = wait_for_completion_timeout(&fep->mdio_done, 1882793fc096SFrank Li usecs_to_jiffies(FEC_MII_TIMEOUT)); 1883793fc096SFrank Li if (time_left == 0) { 188431b7720cSJoe Perches netdev_err(fep->netdev, "MDIO write timeout\n"); 18858fff755eSAndrew Lunn ret = -ETIMEDOUT; 1886793fc096SFrank Li } 1887793fc096SFrank Li 1888d3ee8ec7SMarco Hartmann out: 18898fff755eSAndrew Lunn pm_runtime_mark_last_busy(dev); 18908fff755eSAndrew Lunn pm_runtime_put_autosuspend(dev); 18918fff755eSAndrew Lunn 18928fff755eSAndrew Lunn return ret; 1893793fc096SFrank Li } 1894793fc096SFrank Li 1895e8fcfcd5SNimrod Andy static int fec_enet_clk_enable(struct net_device *ndev, bool enable) 1896e8fcfcd5SNimrod Andy { 1897e8fcfcd5SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 1898e8fcfcd5SNimrod Andy int ret; 1899e8fcfcd5SNimrod Andy 1900e8fcfcd5SNimrod Andy if (enable) { 1901e8fcfcd5SNimrod Andy ret = clk_prepare_enable(fep->clk_enet_out); 1902e8fcfcd5SNimrod Andy if (ret) 1903d7c3a206SAndy Duan return ret; 190401e5943aSUwe Kleine-König 1905e8fcfcd5SNimrod Andy if (fep->clk_ptp) { 190691c0d987SNimrod Andy mutex_lock(&fep->ptp_clk_mutex); 1907e8fcfcd5SNimrod Andy ret = clk_prepare_enable(fep->clk_ptp); 190891c0d987SNimrod Andy if (ret) { 190991c0d987SNimrod Andy mutex_unlock(&fep->ptp_clk_mutex); 1910e8fcfcd5SNimrod Andy goto failed_clk_ptp; 191191c0d987SNimrod Andy } else { 191291c0d987SNimrod Andy fep->ptp_clk_on = true; 191391c0d987SNimrod Andy } 191491c0d987SNimrod Andy mutex_unlock(&fep->ptp_clk_mutex); 1915e8fcfcd5SNimrod Andy } 191601e5943aSUwe Kleine-König 19179b5330edSFugang Duan ret = clk_prepare_enable(fep->clk_ref); 19189b5330edSFugang Duan if (ret) 19199b5330edSFugang Duan goto failed_clk_ref; 19201b0a83acSRichard Leitner 19211b0a83acSRichard Leitner phy_reset_after_clk_enable(ndev->phydev); 1922e8fcfcd5SNimrod Andy } else { 1923e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_enet_out); 192491c0d987SNimrod Andy if (fep->clk_ptp) { 192591c0d987SNimrod Andy mutex_lock(&fep->ptp_clk_mutex); 1926e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_ptp); 192791c0d987SNimrod Andy fep->ptp_clk_on = false; 192891c0d987SNimrod Andy mutex_unlock(&fep->ptp_clk_mutex); 192991c0d987SNimrod Andy } 19309b5330edSFugang Duan clk_disable_unprepare(fep->clk_ref); 1931e8fcfcd5SNimrod Andy } 1932e8fcfcd5SNimrod Andy 1933e8fcfcd5SNimrod Andy return 0; 19349b5330edSFugang Duan 19359b5330edSFugang Duan failed_clk_ref: 19369b5330edSFugang Duan if (fep->clk_ref) 19379b5330edSFugang Duan clk_disable_unprepare(fep->clk_ref); 1938e8fcfcd5SNimrod Andy failed_clk_ptp: 1939e8fcfcd5SNimrod Andy if (fep->clk_enet_out) 1940e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_enet_out); 1941e8fcfcd5SNimrod Andy 1942e8fcfcd5SNimrod Andy return ret; 1943e8fcfcd5SNimrod Andy } 1944e8fcfcd5SNimrod Andy 1945793fc096SFrank Li static int fec_enet_mii_probe(struct net_device *ndev) 1946793fc096SFrank Li { 1947793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1948793fc096SFrank Li struct phy_device *phy_dev = NULL; 1949793fc096SFrank Li char mdio_bus_id[MII_BUS_ID_SIZE]; 1950793fc096SFrank Li char phy_name[MII_BUS_ID_SIZE + 3]; 1951793fc096SFrank Li int phy_id; 1952793fc096SFrank Li int dev_id = fep->dev_id; 1953793fc096SFrank Li 1954407066f8SUwe Kleine-König if (fep->phy_node) { 1955407066f8SUwe Kleine-König phy_dev = of_phy_connect(ndev, fep->phy_node, 1956407066f8SUwe Kleine-König &fec_enet_adjust_link, 0, 1957407066f8SUwe Kleine-König fep->phy_interface); 19589558df3aSAndrew Lunn if (!phy_dev) { 19599558df3aSAndrew Lunn netdev_err(ndev, "Unable to connect to phy\n"); 1960213a9922SNimrod Andy return -ENODEV; 19619558df3aSAndrew Lunn } 1962407066f8SUwe Kleine-König } else { 1963793fc096SFrank Li /* check for attached phy */ 1964793fc096SFrank Li for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) { 19657f854420SAndrew Lunn if (!mdiobus_is_registered_device(fep->mii_bus, phy_id)) 1966793fc096SFrank Li continue; 1967793fc096SFrank Li if (dev_id--) 1968793fc096SFrank Li continue; 1969949bdd20SRickard Strandqvist strlcpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE); 1970793fc096SFrank Li break; 1971793fc096SFrank Li } 1972793fc096SFrank Li 1973793fc096SFrank Li if (phy_id >= PHY_MAX_ADDR) { 197431b7720cSJoe Perches netdev_info(ndev, "no PHY, assuming direct connection to switch\n"); 1975949bdd20SRickard Strandqvist strlcpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE); 1976793fc096SFrank Li phy_id = 0; 1977793fc096SFrank Li } 1978793fc096SFrank Li 1979407066f8SUwe Kleine-König snprintf(phy_name, sizeof(phy_name), 1980407066f8SUwe Kleine-König PHY_ID_FMT, mdio_bus_id, phy_id); 1981793fc096SFrank Li phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, 1982793fc096SFrank Li fep->phy_interface); 1983407066f8SUwe Kleine-König } 1984407066f8SUwe Kleine-König 1985793fc096SFrank Li if (IS_ERR(phy_dev)) { 198631b7720cSJoe Perches netdev_err(ndev, "could not attach to PHY\n"); 1987793fc096SFrank Li return PTR_ERR(phy_dev); 1988793fc096SFrank Li } 1989793fc096SFrank Li 1990793fc096SFrank Li /* mask with MAC supported features */ 19916b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_GBIT) { 199258056c1eSAndrew Lunn phy_set_max_speed(phy_dev, 1000); 199341124fa6SAndrew Lunn phy_remove_link_mode(phy_dev, 199441124fa6SAndrew Lunn ETHTOOL_LINK_MODE_1000baseT_Half_BIT); 1995d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 1996c306ad36SAndrew Lunn phy_support_sym_pause(phy_dev); 1997d1391930SGuenter Roeck #endif 1998793fc096SFrank Li } 1999793fc096SFrank Li else 200058056c1eSAndrew Lunn phy_set_max_speed(phy_dev, 100); 2001793fc096SFrank Li 2002793fc096SFrank Li fep->link = 0; 2003793fc096SFrank Li fep->full_duplex = 0; 2004793fc096SFrank Li 20052220943aSAndrew Lunn phy_attached_info(phy_dev); 2006793fc096SFrank Li 2007793fc096SFrank Li return 0; 2008793fc096SFrank Li } 2009793fc096SFrank Li 2010793fc096SFrank Li static int fec_enet_mii_init(struct platform_device *pdev) 2011793fc096SFrank Li { 2012793fc096SFrank Li static struct mii_bus *fec0_mii_bus; 2013793fc096SFrank Li struct net_device *ndev = platform_get_drvdata(pdev); 2014793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2015407066f8SUwe Kleine-König struct device_node *node; 2016e7f4dc35SAndrew Lunn int err = -ENXIO; 201763c60732SUwe Kleine-König u32 mii_speed, holdtime; 2018793fc096SFrank Li 2019793fc096SFrank Li /* 20203d125f9cSStefan Agner * The i.MX28 dual fec interfaces are not equal. 2021793fc096SFrank Li * Here are the differences: 2022793fc096SFrank Li * 2023793fc096SFrank Li * - fec0 supports MII & RMII modes while fec1 only supports RMII 2024793fc096SFrank Li * - fec0 acts as the 1588 time master while fec1 is slave 2025793fc096SFrank Li * - external phys can only be configured by fec0 2026793fc096SFrank Li * 2027793fc096SFrank Li * That is to say fec1 can not work independently. It only works 2028793fc096SFrank Li * when fec0 is working. The reason behind this design is that the 2029793fc096SFrank Li * second interface is added primarily for Switch mode. 2030793fc096SFrank Li * 2031793fc096SFrank Li * Because of the last point above, both phys are attached on fec0 2032793fc096SFrank Li * mdio interface in board design, and need to be configured by 2033793fc096SFrank Li * fec0 mii_bus. 2034793fc096SFrank Li */ 20353d125f9cSStefan Agner if ((fep->quirks & FEC_QUIRK_SINGLE_MDIO) && fep->dev_id > 0) { 2036793fc096SFrank Li /* fec1 uses fec0 mii_bus */ 2037793fc096SFrank Li if (mii_cnt && fec0_mii_bus) { 2038793fc096SFrank Li fep->mii_bus = fec0_mii_bus; 2039793fc096SFrank Li mii_cnt++; 2040793fc096SFrank Li return 0; 2041793fc096SFrank Li } 2042793fc096SFrank Li return -ENOENT; 2043793fc096SFrank Li } 2044793fc096SFrank Li 2045793fc096SFrank Li /* 2046793fc096SFrank Li * Set MII speed to 2.5 MHz (= clk_get_rate() / 2 * phy_speed) 2047793fc096SFrank Li * 2048793fc096SFrank Li * The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while 2049793fc096SFrank Li * for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'. The i.MX28 2050793fc096SFrank Li * Reference Manual has an error on this, and gets fixed on i.MX6Q 2051793fc096SFrank Li * document. 2052793fc096SFrank Li */ 205363c60732SUwe Kleine-König mii_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 5000000); 20546b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_ENET_MAC) 205563c60732SUwe Kleine-König mii_speed--; 205663c60732SUwe Kleine-König if (mii_speed > 63) { 205763c60732SUwe Kleine-König dev_err(&pdev->dev, 2058981a0547SPeter Meerwald-Stadler "fec clock (%lu) too fast to get right mii speed\n", 205963c60732SUwe Kleine-König clk_get_rate(fep->clk_ipg)); 206063c60732SUwe Kleine-König err = -EINVAL; 206163c60732SUwe Kleine-König goto err_out; 206263c60732SUwe Kleine-König } 206363c60732SUwe Kleine-König 206463c60732SUwe Kleine-König /* 206563c60732SUwe Kleine-König * The i.MX28 and i.MX6 types have another filed in the MSCR (aka 206663c60732SUwe Kleine-König * MII_SPEED) register that defines the MDIO output hold time. Earlier 206763c60732SUwe Kleine-König * versions are RAZ there, so just ignore the difference and write the 206863c60732SUwe Kleine-König * register always. 206963c60732SUwe Kleine-König * The minimal hold time according to IEE802.3 (clause 22) is 10 ns. 207063c60732SUwe Kleine-König * HOLDTIME + 1 is the number of clk cycles the fec is holding the 207163c60732SUwe Kleine-König * output. 207263c60732SUwe Kleine-König * The HOLDTIME bitfield takes values between 0 and 7 (inclusive). 207363c60732SUwe Kleine-König * Given that ceil(clkrate / 5000000) <= 64, the calculation for 207463c60732SUwe Kleine-König * holdtime cannot result in a value greater than 3. 207563c60732SUwe Kleine-König */ 207663c60732SUwe Kleine-König holdtime = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 100000000) - 1; 207763c60732SUwe Kleine-König 207863c60732SUwe Kleine-König fep->phy_speed = mii_speed << 1 | holdtime << 8; 207963c60732SUwe Kleine-König 2080793fc096SFrank Li writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 2081793fc096SFrank Li 2082793fc096SFrank Li fep->mii_bus = mdiobus_alloc(); 2083793fc096SFrank Li if (fep->mii_bus == NULL) { 2084793fc096SFrank Li err = -ENOMEM; 2085793fc096SFrank Li goto err_out; 2086793fc096SFrank Li } 2087793fc096SFrank Li 2088793fc096SFrank Li fep->mii_bus->name = "fec_enet_mii_bus"; 2089793fc096SFrank Li fep->mii_bus->read = fec_enet_mdio_read; 2090793fc096SFrank Li fep->mii_bus->write = fec_enet_mdio_write; 2091793fc096SFrank Li snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", 2092793fc096SFrank Li pdev->name, fep->dev_id + 1); 2093793fc096SFrank Li fep->mii_bus->priv = fep; 2094793fc096SFrank Li fep->mii_bus->parent = &pdev->dev; 2095793fc096SFrank Li 2096407066f8SUwe Kleine-König node = of_get_child_by_name(pdev->dev.of_node, "mdio"); 2097407066f8SUwe Kleine-König err = of_mdiobus_register(fep->mii_bus, node); 2098407066f8SUwe Kleine-König of_node_put(node); 2099407066f8SUwe Kleine-König if (err) 2100e7f4dc35SAndrew Lunn goto err_out_free_mdiobus; 2101793fc096SFrank Li 2102793fc096SFrank Li mii_cnt++; 2103793fc096SFrank Li 2104793fc096SFrank Li /* save fec0 mii_bus */ 21053d125f9cSStefan Agner if (fep->quirks & FEC_QUIRK_SINGLE_MDIO) 2106793fc096SFrank Li fec0_mii_bus = fep->mii_bus; 2107793fc096SFrank Li 2108793fc096SFrank Li return 0; 2109793fc096SFrank Li 2110793fc096SFrank Li err_out_free_mdiobus: 2111793fc096SFrank Li mdiobus_free(fep->mii_bus); 2112793fc096SFrank Li err_out: 2113793fc096SFrank Li return err; 2114793fc096SFrank Li } 2115793fc096SFrank Li 2116793fc096SFrank Li static void fec_enet_mii_remove(struct fec_enet_private *fep) 2117793fc096SFrank Li { 2118793fc096SFrank Li if (--mii_cnt == 0) { 2119793fc096SFrank Li mdiobus_unregister(fep->mii_bus); 2120793fc096SFrank Li mdiobus_free(fep->mii_bus); 2121793fc096SFrank Li } 2122793fc096SFrank Li } 2123793fc096SFrank Li 2124793fc096SFrank Li static void fec_enet_get_drvinfo(struct net_device *ndev, 2125793fc096SFrank Li struct ethtool_drvinfo *info) 2126793fc096SFrank Li { 2127793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2128793fc096SFrank Li 2129793fc096SFrank Li strlcpy(info->driver, fep->pdev->dev.driver->name, 2130793fc096SFrank Li sizeof(info->driver)); 2131793fc096SFrank Li strlcpy(info->bus_info, dev_name(&ndev->dev), sizeof(info->bus_info)); 2132793fc096SFrank Li } 2133793fc096SFrank Li 2134db65f35fSPhilippe Reynes static int fec_enet_get_regs_len(struct net_device *ndev) 2135db65f35fSPhilippe Reynes { 2136db65f35fSPhilippe Reynes struct fec_enet_private *fep = netdev_priv(ndev); 2137db65f35fSPhilippe Reynes struct resource *r; 2138db65f35fSPhilippe Reynes int s = 0; 2139db65f35fSPhilippe Reynes 2140db65f35fSPhilippe Reynes r = platform_get_resource(fep->pdev, IORESOURCE_MEM, 0); 2141db65f35fSPhilippe Reynes if (r) 2142db65f35fSPhilippe Reynes s = resource_size(r); 2143db65f35fSPhilippe Reynes 2144db65f35fSPhilippe Reynes return s; 2145db65f35fSPhilippe Reynes } 2146db65f35fSPhilippe Reynes 2147db65f35fSPhilippe Reynes /* List of registers that can be safety be read to dump them with ethtool */ 2148db65f35fSPhilippe Reynes #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ 21493f1dcc6aSLucas Stach defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ 215078cc6e7eSFlorian Fainelli defined(CONFIG_ARM64) || defined(CONFIG_COMPILE_TEST) 2151f9bcc9f3SVivien Didelot static __u32 fec_enet_register_version = 2; 2152db65f35fSPhilippe Reynes static u32 fec_enet_register_offset[] = { 2153db65f35fSPhilippe Reynes FEC_IEVENT, FEC_IMASK, FEC_R_DES_ACTIVE_0, FEC_X_DES_ACTIVE_0, 2154db65f35fSPhilippe Reynes FEC_ECNTRL, FEC_MII_DATA, FEC_MII_SPEED, FEC_MIB_CTRLSTAT, FEC_R_CNTRL, 2155db65f35fSPhilippe Reynes FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, FEC_OPD, FEC_TXIC0, FEC_TXIC1, 2156db65f35fSPhilippe Reynes FEC_TXIC2, FEC_RXIC0, FEC_RXIC1, FEC_RXIC2, FEC_HASH_TABLE_HIGH, 2157db65f35fSPhilippe Reynes FEC_HASH_TABLE_LOW, FEC_GRP_HASH_TABLE_HIGH, FEC_GRP_HASH_TABLE_LOW, 2158db65f35fSPhilippe Reynes FEC_X_WMRK, FEC_R_BOUND, FEC_R_FSTART, FEC_R_DES_START_1, 2159db65f35fSPhilippe Reynes FEC_X_DES_START_1, FEC_R_BUFF_SIZE_1, FEC_R_DES_START_2, 2160db65f35fSPhilippe Reynes FEC_X_DES_START_2, FEC_R_BUFF_SIZE_2, FEC_R_DES_START_0, 2161db65f35fSPhilippe Reynes FEC_X_DES_START_0, FEC_R_BUFF_SIZE_0, FEC_R_FIFO_RSFL, FEC_R_FIFO_RSEM, 2162db65f35fSPhilippe Reynes FEC_R_FIFO_RAEM, FEC_R_FIFO_RAFL, FEC_RACC, FEC_RCMR_1, FEC_RCMR_2, 2163db65f35fSPhilippe Reynes FEC_DMA_CFG_1, FEC_DMA_CFG_2, FEC_R_DES_ACTIVE_1, FEC_X_DES_ACTIVE_1, 2164db65f35fSPhilippe Reynes FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_2, FEC_QOS_SCHEME, 2165db65f35fSPhilippe Reynes RMON_T_DROP, RMON_T_PACKETS, RMON_T_BC_PKT, RMON_T_MC_PKT, 2166db65f35fSPhilippe Reynes RMON_T_CRC_ALIGN, RMON_T_UNDERSIZE, RMON_T_OVERSIZE, RMON_T_FRAG, 2167db65f35fSPhilippe Reynes RMON_T_JAB, RMON_T_COL, RMON_T_P64, RMON_T_P65TO127, RMON_T_P128TO255, 2168db65f35fSPhilippe Reynes RMON_T_P256TO511, RMON_T_P512TO1023, RMON_T_P1024TO2047, 2169db65f35fSPhilippe Reynes RMON_T_P_GTE2048, RMON_T_OCTETS, 2170db65f35fSPhilippe Reynes IEEE_T_DROP, IEEE_T_FRAME_OK, IEEE_T_1COL, IEEE_T_MCOL, IEEE_T_DEF, 2171db65f35fSPhilippe Reynes IEEE_T_LCOL, IEEE_T_EXCOL, IEEE_T_MACERR, IEEE_T_CSERR, IEEE_T_SQE, 2172db65f35fSPhilippe Reynes IEEE_T_FDXFC, IEEE_T_OCTETS_OK, 2173db65f35fSPhilippe Reynes RMON_R_PACKETS, RMON_R_BC_PKT, RMON_R_MC_PKT, RMON_R_CRC_ALIGN, 2174db65f35fSPhilippe Reynes RMON_R_UNDERSIZE, RMON_R_OVERSIZE, RMON_R_FRAG, RMON_R_JAB, 2175db65f35fSPhilippe Reynes RMON_R_RESVD_O, RMON_R_P64, RMON_R_P65TO127, RMON_R_P128TO255, 2176db65f35fSPhilippe Reynes RMON_R_P256TO511, RMON_R_P512TO1023, RMON_R_P1024TO2047, 2177db65f35fSPhilippe Reynes RMON_R_P_GTE2048, RMON_R_OCTETS, 2178db65f35fSPhilippe Reynes IEEE_R_DROP, IEEE_R_FRAME_OK, IEEE_R_CRC, IEEE_R_ALIGN, IEEE_R_MACERR, 2179db65f35fSPhilippe Reynes IEEE_R_FDXFC, IEEE_R_OCTETS_OK 2180db65f35fSPhilippe Reynes }; 2181db65f35fSPhilippe Reynes #else 2182f9bcc9f3SVivien Didelot static __u32 fec_enet_register_version = 1; 2183db65f35fSPhilippe Reynes static u32 fec_enet_register_offset[] = { 2184db65f35fSPhilippe Reynes FEC_ECNTRL, FEC_IEVENT, FEC_IMASK, FEC_IVEC, FEC_R_DES_ACTIVE_0, 2185db65f35fSPhilippe Reynes FEC_R_DES_ACTIVE_1, FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_0, 2186db65f35fSPhilippe Reynes FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2, FEC_MII_DATA, FEC_MII_SPEED, 2187db65f35fSPhilippe Reynes FEC_R_BOUND, FEC_R_FSTART, FEC_X_WMRK, FEC_X_FSTART, FEC_R_CNTRL, 2188db65f35fSPhilippe Reynes FEC_MAX_FRM_LEN, FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, 2189db65f35fSPhilippe Reynes FEC_GRP_HASH_TABLE_HIGH, FEC_GRP_HASH_TABLE_LOW, FEC_R_DES_START_0, 2190db65f35fSPhilippe Reynes FEC_R_DES_START_1, FEC_R_DES_START_2, FEC_X_DES_START_0, 2191db65f35fSPhilippe Reynes FEC_X_DES_START_1, FEC_X_DES_START_2, FEC_R_BUFF_SIZE_0, 2192db65f35fSPhilippe Reynes FEC_R_BUFF_SIZE_1, FEC_R_BUFF_SIZE_2 2193db65f35fSPhilippe Reynes }; 2194db65f35fSPhilippe Reynes #endif 2195db65f35fSPhilippe Reynes 2196db65f35fSPhilippe Reynes static void fec_enet_get_regs(struct net_device *ndev, 2197db65f35fSPhilippe Reynes struct ethtool_regs *regs, void *regbuf) 2198db65f35fSPhilippe Reynes { 2199db65f35fSPhilippe Reynes struct fec_enet_private *fep = netdev_priv(ndev); 2200db65f35fSPhilippe Reynes u32 __iomem *theregs = (u32 __iomem *)fep->hwp; 2201c72a0bc0SAndrew Lunn struct device *dev = &fep->pdev->dev; 2202db65f35fSPhilippe Reynes u32 *buf = (u32 *)regbuf; 2203db65f35fSPhilippe Reynes u32 i, off; 2204c72a0bc0SAndrew Lunn int ret; 2205c72a0bc0SAndrew Lunn 2206c72a0bc0SAndrew Lunn ret = pm_runtime_get_sync(dev); 2207c72a0bc0SAndrew Lunn if (ret < 0) 2208c72a0bc0SAndrew Lunn return; 2209db65f35fSPhilippe Reynes 2210f9bcc9f3SVivien Didelot regs->version = fec_enet_register_version; 2211f9bcc9f3SVivien Didelot 2212db65f35fSPhilippe Reynes memset(buf, 0, regs->len); 2213db65f35fSPhilippe Reynes 2214db65f35fSPhilippe Reynes for (i = 0; i < ARRAY_SIZE(fec_enet_register_offset); i++) { 2215ec20a63aSFugang Duan off = fec_enet_register_offset[i]; 2216ec20a63aSFugang Duan 2217ec20a63aSFugang Duan if ((off == FEC_R_BOUND || off == FEC_R_FSTART) && 2218ec20a63aSFugang Duan !(fep->quirks & FEC_QUIRK_HAS_FRREG)) 2219ec20a63aSFugang Duan continue; 2220ec20a63aSFugang Duan 2221ec20a63aSFugang Duan off >>= 2; 2222db65f35fSPhilippe Reynes buf[off] = readl(&theregs[off]); 2223db65f35fSPhilippe Reynes } 2224c72a0bc0SAndrew Lunn 2225c72a0bc0SAndrew Lunn pm_runtime_mark_last_busy(dev); 2226c72a0bc0SAndrew Lunn pm_runtime_put_autosuspend(dev); 2227db65f35fSPhilippe Reynes } 2228db65f35fSPhilippe Reynes 2229793fc096SFrank Li static int fec_enet_get_ts_info(struct net_device *ndev, 2230793fc096SFrank Li struct ethtool_ts_info *info) 2231793fc096SFrank Li { 2232793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2233793fc096SFrank Li 2234793fc096SFrank Li if (fep->bufdesc_ex) { 2235793fc096SFrank Li 2236793fc096SFrank Li info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | 2237793fc096SFrank Li SOF_TIMESTAMPING_RX_SOFTWARE | 2238793fc096SFrank Li SOF_TIMESTAMPING_SOFTWARE | 2239793fc096SFrank Li SOF_TIMESTAMPING_TX_HARDWARE | 2240793fc096SFrank Li SOF_TIMESTAMPING_RX_HARDWARE | 2241793fc096SFrank Li SOF_TIMESTAMPING_RAW_HARDWARE; 2242793fc096SFrank Li if (fep->ptp_clock) 2243793fc096SFrank Li info->phc_index = ptp_clock_index(fep->ptp_clock); 2244793fc096SFrank Li else 2245793fc096SFrank Li info->phc_index = -1; 2246793fc096SFrank Li 2247793fc096SFrank Li info->tx_types = (1 << HWTSTAMP_TX_OFF) | 2248793fc096SFrank Li (1 << HWTSTAMP_TX_ON); 2249793fc096SFrank Li 2250793fc096SFrank Li info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | 2251793fc096SFrank Li (1 << HWTSTAMP_FILTER_ALL); 2252793fc096SFrank Li return 0; 2253793fc096SFrank Li } else { 2254793fc096SFrank Li return ethtool_op_get_ts_info(ndev, info); 2255793fc096SFrank Li } 2256793fc096SFrank Li } 2257793fc096SFrank Li 2258d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 2259d1391930SGuenter Roeck 2260793fc096SFrank Li static void fec_enet_get_pauseparam(struct net_device *ndev, 2261793fc096SFrank Li struct ethtool_pauseparam *pause) 2262793fc096SFrank Li { 2263793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2264793fc096SFrank Li 2265793fc096SFrank Li pause->autoneg = (fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) != 0; 2266793fc096SFrank Li pause->tx_pause = (fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) != 0; 2267793fc096SFrank Li pause->rx_pause = pause->tx_pause; 2268793fc096SFrank Li } 2269793fc096SFrank Li 2270793fc096SFrank Li static int fec_enet_set_pauseparam(struct net_device *ndev, 2271793fc096SFrank Li struct ethtool_pauseparam *pause) 2272793fc096SFrank Li { 2273793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2274793fc096SFrank Li 227545f5c327SPhilippe Reynes if (!ndev->phydev) 22760b146ca8SRussell King return -ENODEV; 22770b146ca8SRussell King 2278793fc096SFrank Li if (pause->tx_pause != pause->rx_pause) { 2279793fc096SFrank Li netdev_info(ndev, 2280793fc096SFrank Li "hardware only support enable/disable both tx and rx"); 2281793fc096SFrank Li return -EINVAL; 2282793fc096SFrank Li } 2283793fc096SFrank Li 2284793fc096SFrank Li fep->pause_flag = 0; 2285793fc096SFrank Li 2286793fc096SFrank Li /* tx pause must be same as rx pause */ 2287793fc096SFrank Li fep->pause_flag |= pause->rx_pause ? FEC_PAUSE_FLAG_ENABLE : 0; 2288793fc096SFrank Li fep->pause_flag |= pause->autoneg ? FEC_PAUSE_FLAG_AUTONEG : 0; 2289793fc096SFrank Li 22900c122405SAndrew Lunn phy_set_sym_pause(ndev->phydev, pause->rx_pause, pause->tx_pause, 22910c122405SAndrew Lunn pause->autoneg); 2292793fc096SFrank Li 2293793fc096SFrank Li if (pause->autoneg) { 2294793fc096SFrank Li if (netif_running(ndev)) 2295793fc096SFrank Li fec_stop(ndev); 229645f5c327SPhilippe Reynes phy_start_aneg(ndev->phydev); 2297793fc096SFrank Li } 2298dbc64a8eSRussell King if (netif_running(ndev)) { 2299dbc64a8eSRussell King napi_disable(&fep->napi); 2300dbc64a8eSRussell King netif_tx_lock_bh(ndev); 2301ef83337dSRussell King fec_restart(ndev); 2302657ade07SRickard x Andersson netif_tx_wake_all_queues(ndev); 23036af42d42SRussell King netif_tx_unlock_bh(ndev); 2304dbc64a8eSRussell King napi_enable(&fep->napi); 2305dbc64a8eSRussell King } 2306793fc096SFrank Li 2307793fc096SFrank Li return 0; 2308793fc096SFrank Li } 2309793fc096SFrank Li 231038ae92dcSChris Healy static const struct fec_stat { 231138ae92dcSChris Healy char name[ETH_GSTRING_LEN]; 231238ae92dcSChris Healy u16 offset; 231338ae92dcSChris Healy } fec_stats[] = { 231438ae92dcSChris Healy /* RMON TX */ 231538ae92dcSChris Healy { "tx_dropped", RMON_T_DROP }, 231638ae92dcSChris Healy { "tx_packets", RMON_T_PACKETS }, 231738ae92dcSChris Healy { "tx_broadcast", RMON_T_BC_PKT }, 231838ae92dcSChris Healy { "tx_multicast", RMON_T_MC_PKT }, 231938ae92dcSChris Healy { "tx_crc_errors", RMON_T_CRC_ALIGN }, 232038ae92dcSChris Healy { "tx_undersize", RMON_T_UNDERSIZE }, 232138ae92dcSChris Healy { "tx_oversize", RMON_T_OVERSIZE }, 232238ae92dcSChris Healy { "tx_fragment", RMON_T_FRAG }, 232338ae92dcSChris Healy { "tx_jabber", RMON_T_JAB }, 232438ae92dcSChris Healy { "tx_collision", RMON_T_COL }, 232538ae92dcSChris Healy { "tx_64byte", RMON_T_P64 }, 232638ae92dcSChris Healy { "tx_65to127byte", RMON_T_P65TO127 }, 232738ae92dcSChris Healy { "tx_128to255byte", RMON_T_P128TO255 }, 232838ae92dcSChris Healy { "tx_256to511byte", RMON_T_P256TO511 }, 232938ae92dcSChris Healy { "tx_512to1023byte", RMON_T_P512TO1023 }, 233038ae92dcSChris Healy { "tx_1024to2047byte", RMON_T_P1024TO2047 }, 233138ae92dcSChris Healy { "tx_GTE2048byte", RMON_T_P_GTE2048 }, 233238ae92dcSChris Healy { "tx_octets", RMON_T_OCTETS }, 233338ae92dcSChris Healy 233438ae92dcSChris Healy /* IEEE TX */ 233538ae92dcSChris Healy { "IEEE_tx_drop", IEEE_T_DROP }, 233638ae92dcSChris Healy { "IEEE_tx_frame_ok", IEEE_T_FRAME_OK }, 233738ae92dcSChris Healy { "IEEE_tx_1col", IEEE_T_1COL }, 233838ae92dcSChris Healy { "IEEE_tx_mcol", IEEE_T_MCOL }, 233938ae92dcSChris Healy { "IEEE_tx_def", IEEE_T_DEF }, 234038ae92dcSChris Healy { "IEEE_tx_lcol", IEEE_T_LCOL }, 234138ae92dcSChris Healy { "IEEE_tx_excol", IEEE_T_EXCOL }, 234238ae92dcSChris Healy { "IEEE_tx_macerr", IEEE_T_MACERR }, 234338ae92dcSChris Healy { "IEEE_tx_cserr", IEEE_T_CSERR }, 234438ae92dcSChris Healy { "IEEE_tx_sqe", IEEE_T_SQE }, 234538ae92dcSChris Healy { "IEEE_tx_fdxfc", IEEE_T_FDXFC }, 234638ae92dcSChris Healy { "IEEE_tx_octets_ok", IEEE_T_OCTETS_OK }, 234738ae92dcSChris Healy 234838ae92dcSChris Healy /* RMON RX */ 234938ae92dcSChris Healy { "rx_packets", RMON_R_PACKETS }, 235038ae92dcSChris Healy { "rx_broadcast", RMON_R_BC_PKT }, 235138ae92dcSChris Healy { "rx_multicast", RMON_R_MC_PKT }, 235238ae92dcSChris Healy { "rx_crc_errors", RMON_R_CRC_ALIGN }, 235338ae92dcSChris Healy { "rx_undersize", RMON_R_UNDERSIZE }, 235438ae92dcSChris Healy { "rx_oversize", RMON_R_OVERSIZE }, 235538ae92dcSChris Healy { "rx_fragment", RMON_R_FRAG }, 235638ae92dcSChris Healy { "rx_jabber", RMON_R_JAB }, 235738ae92dcSChris Healy { "rx_64byte", RMON_R_P64 }, 235838ae92dcSChris Healy { "rx_65to127byte", RMON_R_P65TO127 }, 235938ae92dcSChris Healy { "rx_128to255byte", RMON_R_P128TO255 }, 236038ae92dcSChris Healy { "rx_256to511byte", RMON_R_P256TO511 }, 236138ae92dcSChris Healy { "rx_512to1023byte", RMON_R_P512TO1023 }, 236238ae92dcSChris Healy { "rx_1024to2047byte", RMON_R_P1024TO2047 }, 236338ae92dcSChris Healy { "rx_GTE2048byte", RMON_R_P_GTE2048 }, 236438ae92dcSChris Healy { "rx_octets", RMON_R_OCTETS }, 236538ae92dcSChris Healy 236638ae92dcSChris Healy /* IEEE RX */ 236738ae92dcSChris Healy { "IEEE_rx_drop", IEEE_R_DROP }, 236838ae92dcSChris Healy { "IEEE_rx_frame_ok", IEEE_R_FRAME_OK }, 236938ae92dcSChris Healy { "IEEE_rx_crc", IEEE_R_CRC }, 237038ae92dcSChris Healy { "IEEE_rx_align", IEEE_R_ALIGN }, 237138ae92dcSChris Healy { "IEEE_rx_macerr", IEEE_R_MACERR }, 237238ae92dcSChris Healy { "IEEE_rx_fdxfc", IEEE_R_FDXFC }, 237338ae92dcSChris Healy { "IEEE_rx_octets_ok", IEEE_R_OCTETS_OK }, 237438ae92dcSChris Healy }; 237538ae92dcSChris Healy 2376f85de666SNikita Yushchenko #define FEC_STATS_SIZE (ARRAY_SIZE(fec_stats) * sizeof(u64)) 2377f85de666SNikita Yushchenko 237880cca775SNikita Yushchenko static void fec_enet_update_ethtool_stats(struct net_device *dev) 237938ae92dcSChris Healy { 238038ae92dcSChris Healy struct fec_enet_private *fep = netdev_priv(dev); 238138ae92dcSChris Healy int i; 238238ae92dcSChris Healy 238338ae92dcSChris Healy for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 238480cca775SNikita Yushchenko fep->ethtool_stats[i] = readl(fep->hwp + fec_stats[i].offset); 238580cca775SNikita Yushchenko } 238680cca775SNikita Yushchenko 238780cca775SNikita Yushchenko static void fec_enet_get_ethtool_stats(struct net_device *dev, 238880cca775SNikita Yushchenko struct ethtool_stats *stats, u64 *data) 238980cca775SNikita Yushchenko { 239080cca775SNikita Yushchenko struct fec_enet_private *fep = netdev_priv(dev); 239180cca775SNikita Yushchenko 239280cca775SNikita Yushchenko if (netif_running(dev)) 239380cca775SNikita Yushchenko fec_enet_update_ethtool_stats(dev); 239480cca775SNikita Yushchenko 2395f85de666SNikita Yushchenko memcpy(data, fep->ethtool_stats, FEC_STATS_SIZE); 239638ae92dcSChris Healy } 239738ae92dcSChris Healy 239838ae92dcSChris Healy static void fec_enet_get_strings(struct net_device *netdev, 239938ae92dcSChris Healy u32 stringset, u8 *data) 240038ae92dcSChris Healy { 240138ae92dcSChris Healy int i; 240238ae92dcSChris Healy switch (stringset) { 240338ae92dcSChris Healy case ETH_SS_STATS: 240438ae92dcSChris Healy for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 240538ae92dcSChris Healy memcpy(data + i * ETH_GSTRING_LEN, 240638ae92dcSChris Healy fec_stats[i].name, ETH_GSTRING_LEN); 240738ae92dcSChris Healy break; 240838ae92dcSChris Healy } 240938ae92dcSChris Healy } 241038ae92dcSChris Healy 241138ae92dcSChris Healy static int fec_enet_get_sset_count(struct net_device *dev, int sset) 241238ae92dcSChris Healy { 241338ae92dcSChris Healy switch (sset) { 241438ae92dcSChris Healy case ETH_SS_STATS: 241538ae92dcSChris Healy return ARRAY_SIZE(fec_stats); 241638ae92dcSChris Healy default: 241738ae92dcSChris Healy return -EOPNOTSUPP; 241838ae92dcSChris Healy } 241938ae92dcSChris Healy } 2420f85de666SNikita Yushchenko 24212b30842bSAndrew Lunn static void fec_enet_clear_ethtool_stats(struct net_device *dev) 24222b30842bSAndrew Lunn { 24232b30842bSAndrew Lunn struct fec_enet_private *fep = netdev_priv(dev); 24242b30842bSAndrew Lunn int i; 24252b30842bSAndrew Lunn 24262b30842bSAndrew Lunn /* Disable MIB statistics counters */ 24272b30842bSAndrew Lunn writel(FEC_MIB_CTRLSTAT_DISABLE, fep->hwp + FEC_MIB_CTRLSTAT); 24282b30842bSAndrew Lunn 24292b30842bSAndrew Lunn for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 24302b30842bSAndrew Lunn writel(0, fep->hwp + fec_stats[i].offset); 24312b30842bSAndrew Lunn 24322b30842bSAndrew Lunn /* Don't disable MIB statistics counters */ 24332b30842bSAndrew Lunn writel(0, fep->hwp + FEC_MIB_CTRLSTAT); 24342b30842bSAndrew Lunn } 24352b30842bSAndrew Lunn 2436f85de666SNikita Yushchenko #else /* !defined(CONFIG_M5272) */ 2437f85de666SNikita Yushchenko #define FEC_STATS_SIZE 0 2438f85de666SNikita Yushchenko static inline void fec_enet_update_ethtool_stats(struct net_device *dev) 2439f85de666SNikita Yushchenko { 2440f85de666SNikita Yushchenko } 244141e8e404SFabio Estevam 244241e8e404SFabio Estevam static inline void fec_enet_clear_ethtool_stats(struct net_device *dev) 244341e8e404SFabio Estevam { 244441e8e404SFabio Estevam } 2445d1391930SGuenter Roeck #endif /* !defined(CONFIG_M5272) */ 244638ae92dcSChris Healy 2447d851b47bSFugang Duan /* ITR clock source is enet system clock (clk_ahb). 2448d851b47bSFugang Duan * TCTT unit is cycle_ns * 64 cycle 2449d851b47bSFugang Duan * So, the ICTT value = X us / (cycle_ns * 64) 2450d851b47bSFugang Duan */ 2451d851b47bSFugang Duan static int fec_enet_us_to_itr_clock(struct net_device *ndev, int us) 2452d851b47bSFugang Duan { 2453d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2454d851b47bSFugang Duan 2455d851b47bSFugang Duan return us * (fep->itr_clk_rate / 64000) / 1000; 2456d851b47bSFugang Duan } 2457d851b47bSFugang Duan 2458d851b47bSFugang Duan /* Set threshold for interrupt coalescing */ 2459d851b47bSFugang Duan static void fec_enet_itr_coal_set(struct net_device *ndev) 2460d851b47bSFugang Duan { 2461d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2462d851b47bSFugang Duan int rx_itr, tx_itr; 2463d851b47bSFugang Duan 2464d851b47bSFugang Duan /* Must be greater than zero to avoid unpredictable behavior */ 2465d851b47bSFugang Duan if (!fep->rx_time_itr || !fep->rx_pkts_itr || 2466d851b47bSFugang Duan !fep->tx_time_itr || !fep->tx_pkts_itr) 2467d851b47bSFugang Duan return; 2468d851b47bSFugang Duan 2469d851b47bSFugang Duan /* Select enet system clock as Interrupt Coalescing 2470d851b47bSFugang Duan * timer Clock Source 2471d851b47bSFugang Duan */ 2472d851b47bSFugang Duan rx_itr = FEC_ITR_CLK_SEL; 2473d851b47bSFugang Duan tx_itr = FEC_ITR_CLK_SEL; 2474d851b47bSFugang Duan 2475d851b47bSFugang Duan /* set ICFT and ICTT */ 2476d851b47bSFugang Duan rx_itr |= FEC_ITR_ICFT(fep->rx_pkts_itr); 2477d851b47bSFugang Duan rx_itr |= FEC_ITR_ICTT(fec_enet_us_to_itr_clock(ndev, fep->rx_time_itr)); 2478d851b47bSFugang Duan tx_itr |= FEC_ITR_ICFT(fep->tx_pkts_itr); 2479d851b47bSFugang Duan tx_itr |= FEC_ITR_ICTT(fec_enet_us_to_itr_clock(ndev, fep->tx_time_itr)); 2480d851b47bSFugang Duan 2481d851b47bSFugang Duan rx_itr |= FEC_ITR_EN; 2482d851b47bSFugang Duan tx_itr |= FEC_ITR_EN; 2483d851b47bSFugang Duan 2484d851b47bSFugang Duan writel(tx_itr, fep->hwp + FEC_TXIC0); 2485d851b47bSFugang Duan writel(rx_itr, fep->hwp + FEC_RXIC0); 2486ff7566b8SFugang Duan if (fep->quirks & FEC_QUIRK_HAS_AVB) { 2487d851b47bSFugang Duan writel(tx_itr, fep->hwp + FEC_TXIC1); 2488d851b47bSFugang Duan writel(rx_itr, fep->hwp + FEC_RXIC1); 2489d851b47bSFugang Duan writel(tx_itr, fep->hwp + FEC_TXIC2); 2490d851b47bSFugang Duan writel(rx_itr, fep->hwp + FEC_RXIC2); 2491d851b47bSFugang Duan } 2492ff7566b8SFugang Duan } 2493d851b47bSFugang Duan 2494d851b47bSFugang Duan static int 2495d851b47bSFugang Duan fec_enet_get_coalesce(struct net_device *ndev, struct ethtool_coalesce *ec) 2496d851b47bSFugang Duan { 2497d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2498d851b47bSFugang Duan 2499ff7566b8SFugang Duan if (!(fep->quirks & FEC_QUIRK_HAS_COALESCE)) 2500d851b47bSFugang Duan return -EOPNOTSUPP; 2501d851b47bSFugang Duan 2502d851b47bSFugang Duan ec->rx_coalesce_usecs = fep->rx_time_itr; 2503d851b47bSFugang Duan ec->rx_max_coalesced_frames = fep->rx_pkts_itr; 2504d851b47bSFugang Duan 2505d851b47bSFugang Duan ec->tx_coalesce_usecs = fep->tx_time_itr; 2506d851b47bSFugang Duan ec->tx_max_coalesced_frames = fep->tx_pkts_itr; 2507d851b47bSFugang Duan 2508d851b47bSFugang Duan return 0; 2509d851b47bSFugang Duan } 2510d851b47bSFugang Duan 2511d851b47bSFugang Duan static int 2512d851b47bSFugang Duan fec_enet_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *ec) 2513d851b47bSFugang Duan { 2514d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2515517a772cSFabio Estevam struct device *dev = &fep->pdev->dev; 2516d851b47bSFugang Duan unsigned int cycle; 2517d851b47bSFugang Duan 2518ff7566b8SFugang Duan if (!(fep->quirks & FEC_QUIRK_HAS_COALESCE)) 2519d851b47bSFugang Duan return -EOPNOTSUPP; 2520d851b47bSFugang Duan 2521d851b47bSFugang Duan if (ec->rx_max_coalesced_frames > 255) { 2522517a772cSFabio Estevam dev_err(dev, "Rx coalesced frames exceed hardware limitation\n"); 2523d851b47bSFugang Duan return -EINVAL; 2524d851b47bSFugang Duan } 2525d851b47bSFugang Duan 2526d851b47bSFugang Duan if (ec->tx_max_coalesced_frames > 255) { 2527517a772cSFabio Estevam dev_err(dev, "Tx coalesced frame exceed hardware limitation\n"); 2528d851b47bSFugang Duan return -EINVAL; 2529d851b47bSFugang Duan } 2530d851b47bSFugang Duan 2531d851b47bSFugang Duan cycle = fec_enet_us_to_itr_clock(ndev, fep->rx_time_itr); 2532d851b47bSFugang Duan if (cycle > 0xFFFF) { 2533517a772cSFabio Estevam dev_err(dev, "Rx coalesced usec exceed hardware limitation\n"); 2534d851b47bSFugang Duan return -EINVAL; 2535d851b47bSFugang Duan } 2536d851b47bSFugang Duan 2537d851b47bSFugang Duan cycle = fec_enet_us_to_itr_clock(ndev, fep->tx_time_itr); 2538d851b47bSFugang Duan if (cycle > 0xFFFF) { 2539517a772cSFabio Estevam dev_err(dev, "Rx coalesced usec exceed hardware limitation\n"); 2540d851b47bSFugang Duan return -EINVAL; 2541d851b47bSFugang Duan } 2542d851b47bSFugang Duan 2543d851b47bSFugang Duan fep->rx_time_itr = ec->rx_coalesce_usecs; 2544d851b47bSFugang Duan fep->rx_pkts_itr = ec->rx_max_coalesced_frames; 2545d851b47bSFugang Duan 2546d851b47bSFugang Duan fep->tx_time_itr = ec->tx_coalesce_usecs; 2547d851b47bSFugang Duan fep->tx_pkts_itr = ec->tx_max_coalesced_frames; 2548d851b47bSFugang Duan 2549d851b47bSFugang Duan fec_enet_itr_coal_set(ndev); 2550d851b47bSFugang Duan 2551d851b47bSFugang Duan return 0; 2552d851b47bSFugang Duan } 2553d851b47bSFugang Duan 2554d851b47bSFugang Duan static void fec_enet_itr_coal_init(struct net_device *ndev) 2555d851b47bSFugang Duan { 2556d851b47bSFugang Duan struct ethtool_coalesce ec; 2557d851b47bSFugang Duan 2558d851b47bSFugang Duan ec.rx_coalesce_usecs = FEC_ITR_ICTT_DEFAULT; 2559d851b47bSFugang Duan ec.rx_max_coalesced_frames = FEC_ITR_ICFT_DEFAULT; 2560d851b47bSFugang Duan 2561d851b47bSFugang Duan ec.tx_coalesce_usecs = FEC_ITR_ICTT_DEFAULT; 2562d851b47bSFugang Duan ec.tx_max_coalesced_frames = FEC_ITR_ICFT_DEFAULT; 2563d851b47bSFugang Duan 2564d851b47bSFugang Duan fec_enet_set_coalesce(ndev, &ec); 2565d851b47bSFugang Duan } 2566d851b47bSFugang Duan 25671b7bde6dSNimrod Andy static int fec_enet_get_tunable(struct net_device *netdev, 25681b7bde6dSNimrod Andy const struct ethtool_tunable *tuna, 25691b7bde6dSNimrod Andy void *data) 25701b7bde6dSNimrod Andy { 25711b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(netdev); 25721b7bde6dSNimrod Andy int ret = 0; 25731b7bde6dSNimrod Andy 25741b7bde6dSNimrod Andy switch (tuna->id) { 25751b7bde6dSNimrod Andy case ETHTOOL_RX_COPYBREAK: 25761b7bde6dSNimrod Andy *(u32 *)data = fep->rx_copybreak; 25771b7bde6dSNimrod Andy break; 25781b7bde6dSNimrod Andy default: 25791b7bde6dSNimrod Andy ret = -EINVAL; 25801b7bde6dSNimrod Andy break; 25811b7bde6dSNimrod Andy } 25821b7bde6dSNimrod Andy 25831b7bde6dSNimrod Andy return ret; 25841b7bde6dSNimrod Andy } 25851b7bde6dSNimrod Andy 25861b7bde6dSNimrod Andy static int fec_enet_set_tunable(struct net_device *netdev, 25871b7bde6dSNimrod Andy const struct ethtool_tunable *tuna, 25881b7bde6dSNimrod Andy const void *data) 25891b7bde6dSNimrod Andy { 25901b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(netdev); 25911b7bde6dSNimrod Andy int ret = 0; 25921b7bde6dSNimrod Andy 25931b7bde6dSNimrod Andy switch (tuna->id) { 25941b7bde6dSNimrod Andy case ETHTOOL_RX_COPYBREAK: 25951b7bde6dSNimrod Andy fep->rx_copybreak = *(u32 *)data; 25961b7bde6dSNimrod Andy break; 25971b7bde6dSNimrod Andy default: 25981b7bde6dSNimrod Andy ret = -EINVAL; 25991b7bde6dSNimrod Andy break; 26001b7bde6dSNimrod Andy } 26011b7bde6dSNimrod Andy 26021b7bde6dSNimrod Andy return ret; 26031b7bde6dSNimrod Andy } 26041b7bde6dSNimrod Andy 2605de40ed31SNimrod Andy static void 2606de40ed31SNimrod Andy fec_enet_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) 2607de40ed31SNimrod Andy { 2608de40ed31SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 2609de40ed31SNimrod Andy 2610de40ed31SNimrod Andy if (fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET) { 2611de40ed31SNimrod Andy wol->supported = WAKE_MAGIC; 2612de40ed31SNimrod Andy wol->wolopts = fep->wol_flag & FEC_WOL_FLAG_ENABLE ? WAKE_MAGIC : 0; 2613de40ed31SNimrod Andy } else { 2614de40ed31SNimrod Andy wol->supported = wol->wolopts = 0; 2615de40ed31SNimrod Andy } 2616de40ed31SNimrod Andy } 2617de40ed31SNimrod Andy 2618de40ed31SNimrod Andy static int 2619de40ed31SNimrod Andy fec_enet_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) 2620de40ed31SNimrod Andy { 2621de40ed31SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 2622de40ed31SNimrod Andy 2623de40ed31SNimrod Andy if (!(fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET)) 2624de40ed31SNimrod Andy return -EINVAL; 2625de40ed31SNimrod Andy 2626de40ed31SNimrod Andy if (wol->wolopts & ~WAKE_MAGIC) 2627de40ed31SNimrod Andy return -EINVAL; 2628de40ed31SNimrod Andy 2629de40ed31SNimrod Andy device_set_wakeup_enable(&ndev->dev, wol->wolopts & WAKE_MAGIC); 2630de40ed31SNimrod Andy if (device_may_wakeup(&ndev->dev)) { 2631de40ed31SNimrod Andy fep->wol_flag |= FEC_WOL_FLAG_ENABLE; 2632de40ed31SNimrod Andy if (fep->irq[0] > 0) 2633de40ed31SNimrod Andy enable_irq_wake(fep->irq[0]); 2634de40ed31SNimrod Andy } else { 2635de40ed31SNimrod Andy fep->wol_flag &= (~FEC_WOL_FLAG_ENABLE); 2636de40ed31SNimrod Andy if (fep->irq[0] > 0) 2637de40ed31SNimrod Andy disable_irq_wake(fep->irq[0]); 2638de40ed31SNimrod Andy } 2639de40ed31SNimrod Andy 2640de40ed31SNimrod Andy return 0; 2641de40ed31SNimrod Andy } 2642de40ed31SNimrod Andy 2643793fc096SFrank Li static const struct ethtool_ops fec_enet_ethtool_ops = { 2644*d5e3c87dSJakub Kicinski .supported_coalesce_params = ETHTOOL_COALESCE_USECS | 2645*d5e3c87dSJakub Kicinski ETHTOOL_COALESCE_MAX_FRAMES, 2646793fc096SFrank Li .get_drvinfo = fec_enet_get_drvinfo, 2647db65f35fSPhilippe Reynes .get_regs_len = fec_enet_get_regs_len, 2648db65f35fSPhilippe Reynes .get_regs = fec_enet_get_regs, 264911d59289SFlorian Fainelli .nway_reset = phy_ethtool_nway_reset, 2650c1d7c48fSRussell King .get_link = ethtool_op_get_link, 2651d851b47bSFugang Duan .get_coalesce = fec_enet_get_coalesce, 2652d851b47bSFugang Duan .set_coalesce = fec_enet_set_coalesce, 265338ae92dcSChris Healy #ifndef CONFIG_M5272 2654c1d7c48fSRussell King .get_pauseparam = fec_enet_get_pauseparam, 2655c1d7c48fSRussell King .set_pauseparam = fec_enet_set_pauseparam, 265638ae92dcSChris Healy .get_strings = fec_enet_get_strings, 2657c1d7c48fSRussell King .get_ethtool_stats = fec_enet_get_ethtool_stats, 265838ae92dcSChris Healy .get_sset_count = fec_enet_get_sset_count, 265938ae92dcSChris Healy #endif 2660c1d7c48fSRussell King .get_ts_info = fec_enet_get_ts_info, 26611b7bde6dSNimrod Andy .get_tunable = fec_enet_get_tunable, 26621b7bde6dSNimrod Andy .set_tunable = fec_enet_set_tunable, 2663de40ed31SNimrod Andy .get_wol = fec_enet_get_wol, 2664de40ed31SNimrod Andy .set_wol = fec_enet_set_wol, 26659365fbf5SPhilippe Reynes .get_link_ksettings = phy_ethtool_get_link_ksettings, 26669365fbf5SPhilippe Reynes .set_link_ksettings = phy_ethtool_set_link_ksettings, 2667793fc096SFrank Li }; 2668793fc096SFrank Li 2669793fc096SFrank Li static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) 2670793fc096SFrank Li { 2671793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 267245f5c327SPhilippe Reynes struct phy_device *phydev = ndev->phydev; 2673793fc096SFrank Li 2674793fc096SFrank Li if (!netif_running(ndev)) 2675793fc096SFrank Li return -EINVAL; 2676793fc096SFrank Li 2677793fc096SFrank Li if (!phydev) 2678793fc096SFrank Li return -ENODEV; 2679793fc096SFrank Li 26801d5244d0SBen Hutchings if (fep->bufdesc_ex) { 26811d5244d0SBen Hutchings if (cmd == SIOCSHWTSTAMP) 26821d5244d0SBen Hutchings return fec_ptp_set(ndev, rq); 26831d5244d0SBen Hutchings if (cmd == SIOCGHWTSTAMP) 26841d5244d0SBen Hutchings return fec_ptp_get(ndev, rq); 26851d5244d0SBen Hutchings } 2686793fc096SFrank Li 2687793fc096SFrank Li return phy_mii_ioctl(phydev, rq, cmd); 2688793fc096SFrank Li } 2689793fc096SFrank Li 2690793fc096SFrank Li static void fec_enet_free_buffers(struct net_device *ndev) 2691793fc096SFrank Li { 2692793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2693793fc096SFrank Li unsigned int i; 2694793fc096SFrank Li struct sk_buff *skb; 2695793fc096SFrank Li struct bufdesc *bdp; 26964d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 26974d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 269859d0f746SFrank Li unsigned int q; 2699793fc096SFrank Li 270059d0f746SFrank Li for (q = 0; q < fep->num_rx_queues; q++) { 270159d0f746SFrank Li rxq = fep->rx_queue[q]; 27027355f276STroy Kisky bdp = rxq->bd.base; 27037355f276STroy Kisky for (i = 0; i < rxq->bd.ring_size; i++) { 27044d494cdcSFugang Duan skb = rxq->rx_skbuff[i]; 27054d494cdcSFugang Duan rxq->rx_skbuff[i] = NULL; 2706730ee360SRussell King if (skb) { 270759d0f746SFrank Li dma_unmap_single(&fep->pdev->dev, 27085cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 2709b64bf4b7SNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 271059d0f746SFrank Li DMA_FROM_DEVICE); 2711793fc096SFrank Li dev_kfree_skb(skb); 2712730ee360SRussell King } 27137355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 271459d0f746SFrank Li } 2715793fc096SFrank Li } 2716793fc096SFrank Li 271759d0f746SFrank Li for (q = 0; q < fep->num_tx_queues; q++) { 271859d0f746SFrank Li txq = fep->tx_queue[q]; 27197355f276STroy Kisky for (i = 0; i < txq->bd.ring_size; i++) { 27204d494cdcSFugang Duan kfree(txq->tx_bounce[i]); 27214d494cdcSFugang Duan txq->tx_bounce[i] = NULL; 27224d494cdcSFugang Duan skb = txq->tx_skbuff[i]; 27234d494cdcSFugang Duan txq->tx_skbuff[i] = NULL; 27248b7c9efaSRussell King dev_kfree_skb(skb); 27258b7c9efaSRussell King } 2726793fc096SFrank Li } 272759d0f746SFrank Li } 2728793fc096SFrank Li 272959d0f746SFrank Li static void fec_enet_free_queue(struct net_device *ndev) 273059d0f746SFrank Li { 273159d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 273259d0f746SFrank Li int i; 273359d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 273459d0f746SFrank Li 273559d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) 273659d0f746SFrank Li if (fep->tx_queue[i] && fep->tx_queue[i]->tso_hdrs) { 273759d0f746SFrank Li txq = fep->tx_queue[i]; 273894920128SFugang Duan dma_free_coherent(&fep->pdev->dev, 27397355f276STroy Kisky txq->bd.ring_size * TSO_HEADER_SIZE, 274059d0f746SFrank Li txq->tso_hdrs, 274159d0f746SFrank Li txq->tso_hdrs_dma); 274259d0f746SFrank Li } 274359d0f746SFrank Li 274459d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) 274559d0f746SFrank Li kfree(fep->rx_queue[i]); 274659d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) 274759d0f746SFrank Li kfree(fep->tx_queue[i]); 274859d0f746SFrank Li } 274959d0f746SFrank Li 275059d0f746SFrank Li static int fec_enet_alloc_queue(struct net_device *ndev) 275159d0f746SFrank Li { 275259d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 275359d0f746SFrank Li int i; 275459d0f746SFrank Li int ret = 0; 275559d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 275659d0f746SFrank Li 275759d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 275859d0f746SFrank Li txq = kzalloc(sizeof(*txq), GFP_KERNEL); 275959d0f746SFrank Li if (!txq) { 276059d0f746SFrank Li ret = -ENOMEM; 276159d0f746SFrank Li goto alloc_failed; 276259d0f746SFrank Li } 276359d0f746SFrank Li 276459d0f746SFrank Li fep->tx_queue[i] = txq; 27657355f276STroy Kisky txq->bd.ring_size = TX_RING_SIZE; 27667355f276STroy Kisky fep->total_tx_ring_size += fep->tx_queue[i]->bd.ring_size; 276759d0f746SFrank Li 276859d0f746SFrank Li txq->tx_stop_threshold = FEC_MAX_SKB_DESCS; 276959d0f746SFrank Li txq->tx_wake_threshold = 27707355f276STroy Kisky (txq->bd.ring_size - txq->tx_stop_threshold) / 2; 277159d0f746SFrank Li 277294920128SFugang Duan txq->tso_hdrs = dma_alloc_coherent(&fep->pdev->dev, 27737355f276STroy Kisky txq->bd.ring_size * TSO_HEADER_SIZE, 277459d0f746SFrank Li &txq->tso_hdrs_dma, 277559d0f746SFrank Li GFP_KERNEL); 277659d0f746SFrank Li if (!txq->tso_hdrs) { 277759d0f746SFrank Li ret = -ENOMEM; 277859d0f746SFrank Li goto alloc_failed; 277959d0f746SFrank Li } 278059d0f746SFrank Li } 278159d0f746SFrank Li 278259d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) { 278359d0f746SFrank Li fep->rx_queue[i] = kzalloc(sizeof(*fep->rx_queue[i]), 278459d0f746SFrank Li GFP_KERNEL); 278559d0f746SFrank Li if (!fep->rx_queue[i]) { 278659d0f746SFrank Li ret = -ENOMEM; 278759d0f746SFrank Li goto alloc_failed; 278859d0f746SFrank Li } 278959d0f746SFrank Li 27907355f276STroy Kisky fep->rx_queue[i]->bd.ring_size = RX_RING_SIZE; 27917355f276STroy Kisky fep->total_rx_ring_size += fep->rx_queue[i]->bd.ring_size; 279259d0f746SFrank Li } 279359d0f746SFrank Li return ret; 279459d0f746SFrank Li 279559d0f746SFrank Li alloc_failed: 279659d0f746SFrank Li fec_enet_free_queue(ndev); 279759d0f746SFrank Li return ret; 279859d0f746SFrank Li } 279959d0f746SFrank Li 280059d0f746SFrank Li static int 280159d0f746SFrank Li fec_enet_alloc_rxq_buffers(struct net_device *ndev, unsigned int queue) 2802793fc096SFrank Li { 2803793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2804793fc096SFrank Li unsigned int i; 2805793fc096SFrank Li struct sk_buff *skb; 2806793fc096SFrank Li struct bufdesc *bdp; 28074d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 2808793fc096SFrank Li 280959d0f746SFrank Li rxq = fep->rx_queue[queue]; 28107355f276STroy Kisky bdp = rxq->bd.base; 28117355f276STroy Kisky for (i = 0; i < rxq->bd.ring_size; i++) { 2812793fc096SFrank Li skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE); 2813ffdce2ccSRussell King if (!skb) 2814ffdce2ccSRussell King goto err_alloc; 2815793fc096SFrank Li 28161b7bde6dSNimrod Andy if (fec_enet_new_rxbdp(ndev, bdp, skb)) { 2817730ee360SRussell King dev_kfree_skb(skb); 2818ffdce2ccSRussell King goto err_alloc; 2819d842a31fSDuan Fugang-B38611 } 2820730ee360SRussell King 28214d494cdcSFugang Duan rxq->rx_skbuff[i] = skb; 28225cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); 2823793fc096SFrank Li 2824793fc096SFrank Li if (fep->bufdesc_ex) { 2825793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 28265cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(BD_ENET_RX_INT); 2827793fc096SFrank Li } 2828793fc096SFrank Li 28297355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 2830793fc096SFrank Li } 2831793fc096SFrank Li 2832793fc096SFrank Li /* Set the last buffer to wrap. */ 28337355f276STroy Kisky bdp = fec_enet_get_prevdesc(bdp, &rxq->bd); 28345cfa3039SJohannes Berg bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 283559d0f746SFrank Li return 0; 2836793fc096SFrank Li 283759d0f746SFrank Li err_alloc: 283859d0f746SFrank Li fec_enet_free_buffers(ndev); 283959d0f746SFrank Li return -ENOMEM; 284059d0f746SFrank Li } 284159d0f746SFrank Li 284259d0f746SFrank Li static int 284359d0f746SFrank Li fec_enet_alloc_txq_buffers(struct net_device *ndev, unsigned int queue) 284459d0f746SFrank Li { 284559d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 284659d0f746SFrank Li unsigned int i; 284759d0f746SFrank Li struct bufdesc *bdp; 284859d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 284959d0f746SFrank Li 285059d0f746SFrank Li txq = fep->tx_queue[queue]; 28517355f276STroy Kisky bdp = txq->bd.base; 28527355f276STroy Kisky for (i = 0; i < txq->bd.ring_size; i++) { 28534d494cdcSFugang Duan txq->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL); 28544d494cdcSFugang Duan if (!txq->tx_bounce[i]) 2855ffdce2ccSRussell King goto err_alloc; 2856793fc096SFrank Li 28575cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(0); 28585cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(0); 2859793fc096SFrank Li 2860793fc096SFrank Li if (fep->bufdesc_ex) { 2861793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 28625cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(BD_ENET_TX_INT); 2863793fc096SFrank Li } 2864793fc096SFrank Li 28657355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 2866793fc096SFrank Li } 2867793fc096SFrank Li 2868793fc096SFrank Li /* Set the last buffer to wrap. */ 28697355f276STroy Kisky bdp = fec_enet_get_prevdesc(bdp, &txq->bd); 28705cfa3039SJohannes Berg bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 2871793fc096SFrank Li 2872793fc096SFrank Li return 0; 2873ffdce2ccSRussell King 2874ffdce2ccSRussell King err_alloc: 2875ffdce2ccSRussell King fec_enet_free_buffers(ndev); 2876ffdce2ccSRussell King return -ENOMEM; 2877793fc096SFrank Li } 2878793fc096SFrank Li 287959d0f746SFrank Li static int fec_enet_alloc_buffers(struct net_device *ndev) 288059d0f746SFrank Li { 288159d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 288259d0f746SFrank Li unsigned int i; 288359d0f746SFrank Li 288459d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) 288559d0f746SFrank Li if (fec_enet_alloc_rxq_buffers(ndev, i)) 288659d0f746SFrank Li return -ENOMEM; 288759d0f746SFrank Li 288859d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) 288959d0f746SFrank Li if (fec_enet_alloc_txq_buffers(ndev, i)) 289059d0f746SFrank Li return -ENOMEM; 289159d0f746SFrank Li return 0; 289259d0f746SFrank Li } 289359d0f746SFrank Li 2894793fc096SFrank Li static int 2895793fc096SFrank Li fec_enet_open(struct net_device *ndev) 2896793fc096SFrank Li { 2897793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2898793fc096SFrank Li int ret; 28991b0a83acSRichard Leitner bool reset_again; 2900793fc096SFrank Li 29018fff755eSAndrew Lunn ret = pm_runtime_get_sync(&fep->pdev->dev); 2902b0c6ce24SFabio Estevam if (ret < 0) 29038fff755eSAndrew Lunn return ret; 29048fff755eSAndrew Lunn 29055bbde4d2SNimrod Andy pinctrl_pm_select_default_state(&fep->pdev->dev); 2906e8fcfcd5SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 2907e8fcfcd5SNimrod Andy if (ret) 29088fff755eSAndrew Lunn goto clk_enable; 2909e8fcfcd5SNimrod Andy 29101b0a83acSRichard Leitner /* During the first fec_enet_open call the PHY isn't probed at this 29111b0a83acSRichard Leitner * point. Therefore the phy_reset_after_clk_enable() call within 29121b0a83acSRichard Leitner * fec_enet_clk_enable() fails. As we need this reset in order to be 29131b0a83acSRichard Leitner * sure the PHY is working correctly we check if we need to reset again 29141b0a83acSRichard Leitner * later when the PHY is probed 29151b0a83acSRichard Leitner */ 29161b0a83acSRichard Leitner if (ndev->phydev && ndev->phydev->drv) 29171b0a83acSRichard Leitner reset_again = false; 29181b0a83acSRichard Leitner else 29191b0a83acSRichard Leitner reset_again = true; 29201b0a83acSRichard Leitner 2921793fc096SFrank Li /* I should reset the ring buffers here, but I don't yet know 2922793fc096SFrank Li * a simple way to do that. 2923793fc096SFrank Li */ 2924793fc096SFrank Li 2925793fc096SFrank Li ret = fec_enet_alloc_buffers(ndev); 2926793fc096SFrank Li if (ret) 2927681d2421SFabio Estevam goto err_enet_alloc; 2928793fc096SFrank Li 292955dd2753SNimrod Andy /* Init MAC prior to mii bus probe */ 293055dd2753SNimrod Andy fec_restart(ndev); 293155dd2753SNimrod Andy 2932793fc096SFrank Li /* Probe and connect to PHY when open the interface */ 2933793fc096SFrank Li ret = fec_enet_mii_probe(ndev); 2934681d2421SFabio Estevam if (ret) 2935681d2421SFabio Estevam goto err_enet_mii_probe; 2936ce5eaf02SRussell King 29371b0a83acSRichard Leitner /* Call phy_reset_after_clk_enable() again if it failed during 29381b0a83acSRichard Leitner * phy_reset_after_clk_enable() before because the PHY wasn't probed. 29391b0a83acSRichard Leitner */ 29401b0a83acSRichard Leitner if (reset_again) 29411b0a83acSRichard Leitner phy_reset_after_clk_enable(ndev->phydev); 29421b0a83acSRichard Leitner 294329380905SLucas Stach if (fep->quirks & FEC_QUIRK_ERR006687) 294429380905SLucas Stach imx6q_cpuidle_fec_irqs_used(); 294529380905SLucas Stach 2946ce5eaf02SRussell King napi_enable(&fep->napi); 294745f5c327SPhilippe Reynes phy_start(ndev->phydev); 29484d494cdcSFugang Duan netif_tx_start_all_queues(ndev); 29494d494cdcSFugang Duan 2950de40ed31SNimrod Andy device_set_wakeup_enable(&ndev->dev, fep->wol_flag & 2951de40ed31SNimrod Andy FEC_WOL_FLAG_ENABLE); 2952de40ed31SNimrod Andy 2953793fc096SFrank Li return 0; 2954681d2421SFabio Estevam 2955681d2421SFabio Estevam err_enet_mii_probe: 2956681d2421SFabio Estevam fec_enet_free_buffers(ndev); 2957681d2421SFabio Estevam err_enet_alloc: 2958681d2421SFabio Estevam fec_enet_clk_enable(ndev, false); 29598fff755eSAndrew Lunn clk_enable: 29608fff755eSAndrew Lunn pm_runtime_mark_last_busy(&fep->pdev->dev); 29618fff755eSAndrew Lunn pm_runtime_put_autosuspend(&fep->pdev->dev); 2962681d2421SFabio Estevam pinctrl_pm_select_sleep_state(&fep->pdev->dev); 2963681d2421SFabio Estevam return ret; 2964793fc096SFrank Li } 2965793fc096SFrank Li 2966793fc096SFrank Li static int 2967793fc096SFrank Li fec_enet_close(struct net_device *ndev) 2968793fc096SFrank Li { 2969793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2970793fc096SFrank Li 297145f5c327SPhilippe Reynes phy_stop(ndev->phydev); 2972d76cfae9SRussell King 297331a6de34SRussell King if (netif_device_present(ndev)) { 2974793fc096SFrank Li napi_disable(&fep->napi); 2975b49cd504SRussell King netif_tx_disable(ndev); 2976793fc096SFrank Li fec_stop(ndev); 297731a6de34SRussell King } 2978793fc096SFrank Li 297945f5c327SPhilippe Reynes phy_disconnect(ndev->phydev); 2980793fc096SFrank Li 298129380905SLucas Stach if (fep->quirks & FEC_QUIRK_ERR006687) 298229380905SLucas Stach imx6q_cpuidle_fec_irqs_unused(); 298329380905SLucas Stach 298480cca775SNikita Yushchenko fec_enet_update_ethtool_stats(ndev); 298580cca775SNikita Yushchenko 2986e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 29875bbde4d2SNimrod Andy pinctrl_pm_select_sleep_state(&fep->pdev->dev); 29888fff755eSAndrew Lunn pm_runtime_mark_last_busy(&fep->pdev->dev); 29898fff755eSAndrew Lunn pm_runtime_put_autosuspend(&fep->pdev->dev); 29908fff755eSAndrew Lunn 2991793fc096SFrank Li fec_enet_free_buffers(ndev); 2992793fc096SFrank Li 2993793fc096SFrank Li return 0; 2994793fc096SFrank Li } 2995793fc096SFrank Li 2996793fc096SFrank Li /* Set or clear the multicast filter for this adaptor. 2997793fc096SFrank Li * Skeleton taken from sunlance driver. 2998793fc096SFrank Li * The CPM Ethernet implementation allows Multicast as well as individual 2999793fc096SFrank Li * MAC address filtering. Some of the drivers check to make sure it is 3000793fc096SFrank Li * a group multicast address, and discard those that are not. I guess I 3001793fc096SFrank Li * will do the same for now, but just remove the test if you want 3002793fc096SFrank Li * individual filtering as well (do the upper net layers want or support 3003793fc096SFrank Li * this kind of feature?). 3004793fc096SFrank Li */ 3005793fc096SFrank Li 30066176e89cSJiri Kosina #define FEC_HASH_BITS 6 /* #bits in hash */ 3007793fc096SFrank Li 3008793fc096SFrank Li static void set_multicast_list(struct net_device *ndev) 3009793fc096SFrank Li { 3010793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3011793fc096SFrank Li struct netdev_hw_addr *ha; 301216f6e983SKrzysztof Kozlowski unsigned int crc, tmp; 3013793fc096SFrank Li unsigned char hash; 301401f8902bSRui Sousa unsigned int hash_high = 0, hash_low = 0; 3015793fc096SFrank Li 3016793fc096SFrank Li if (ndev->flags & IFF_PROMISC) { 3017793fc096SFrank Li tmp = readl(fep->hwp + FEC_R_CNTRL); 3018793fc096SFrank Li tmp |= 0x8; 3019793fc096SFrank Li writel(tmp, fep->hwp + FEC_R_CNTRL); 3020793fc096SFrank Li return; 3021793fc096SFrank Li } 3022793fc096SFrank Li 3023793fc096SFrank Li tmp = readl(fep->hwp + FEC_R_CNTRL); 3024793fc096SFrank Li tmp &= ~0x8; 3025793fc096SFrank Li writel(tmp, fep->hwp + FEC_R_CNTRL); 3026793fc096SFrank Li 3027793fc096SFrank Li if (ndev->flags & IFF_ALLMULTI) { 3028793fc096SFrank Li /* Catch all multicast addresses, so set the 3029793fc096SFrank Li * filter to all 1's 3030793fc096SFrank Li */ 3031793fc096SFrank Li writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 3032793fc096SFrank Li writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 3033793fc096SFrank Li 3034793fc096SFrank Li return; 3035793fc096SFrank Li } 3036793fc096SFrank Li 303701f8902bSRui Sousa /* Add the addresses in hash register */ 3038793fc096SFrank Li netdev_for_each_mc_addr(ha, ndev) { 3039793fc096SFrank Li /* calculate crc32 value of mac address */ 304016f6e983SKrzysztof Kozlowski crc = ether_crc_le(ndev->addr_len, ha->addr); 3041793fc096SFrank Li 30426176e89cSJiri Kosina /* only upper 6 bits (FEC_HASH_BITS) are used 3043981a0547SPeter Meerwald-Stadler * which point to specific bit in the hash registers 3044793fc096SFrank Li */ 30456176e89cSJiri Kosina hash = (crc >> (32 - FEC_HASH_BITS)) & 0x3f; 3046793fc096SFrank Li 304701f8902bSRui Sousa if (hash > 31) 304801f8902bSRui Sousa hash_high |= 1 << (hash - 32); 304901f8902bSRui Sousa else 305001f8902bSRui Sousa hash_low |= 1 << hash; 3051793fc096SFrank Li } 305201f8902bSRui Sousa 305301f8902bSRui Sousa writel(hash_high, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 305401f8902bSRui Sousa writel(hash_low, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 3055793fc096SFrank Li } 3056793fc096SFrank Li 3057793fc096SFrank Li /* Set a MAC change in hardware. */ 3058793fc096SFrank Li static int 3059793fc096SFrank Li fec_set_mac_address(struct net_device *ndev, void *p) 3060793fc096SFrank Li { 3061793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3062793fc096SFrank Li struct sockaddr *addr = p; 3063793fc096SFrank Li 306444934facSLucas Stach if (addr) { 3065793fc096SFrank Li if (!is_valid_ether_addr(addr->sa_data)) 3066793fc096SFrank Li return -EADDRNOTAVAIL; 3067793fc096SFrank Li memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len); 306844934facSLucas Stach } 3069793fc096SFrank Li 30709638d19eSNimrod Andy /* Add netif status check here to avoid system hang in below case: 30719638d19eSNimrod Andy * ifconfig ethx down; ifconfig ethx hw ether xx:xx:xx:xx:xx:xx; 30729638d19eSNimrod Andy * After ethx down, fec all clocks are gated off and then register 30739638d19eSNimrod Andy * access causes system hang. 30749638d19eSNimrod Andy */ 30759638d19eSNimrod Andy if (!netif_running(ndev)) 30769638d19eSNimrod Andy return 0; 30779638d19eSNimrod Andy 3078793fc096SFrank Li writel(ndev->dev_addr[3] | (ndev->dev_addr[2] << 8) | 3079793fc096SFrank Li (ndev->dev_addr[1] << 16) | (ndev->dev_addr[0] << 24), 3080793fc096SFrank Li fep->hwp + FEC_ADDR_LOW); 3081793fc096SFrank Li writel((ndev->dev_addr[5] << 16) | (ndev->dev_addr[4] << 24), 3082793fc096SFrank Li fep->hwp + FEC_ADDR_HIGH); 3083793fc096SFrank Li return 0; 3084793fc096SFrank Li } 3085793fc096SFrank Li 3086793fc096SFrank Li #ifdef CONFIG_NET_POLL_CONTROLLER 3087793fc096SFrank Li /** 3088793fc096SFrank Li * fec_poll_controller - FEC Poll controller function 3089793fc096SFrank Li * @dev: The FEC network adapter 3090793fc096SFrank Li * 3091793fc096SFrank Li * Polled functionality used by netconsole and others in non interrupt mode 3092793fc096SFrank Li * 3093793fc096SFrank Li */ 3094793fc096SFrank Li static void fec_poll_controller(struct net_device *dev) 3095793fc096SFrank Li { 3096793fc096SFrank Li int i; 3097793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(dev); 3098793fc096SFrank Li 3099793fc096SFrank Li for (i = 0; i < FEC_IRQ_NUM; i++) { 3100793fc096SFrank Li if (fep->irq[i] > 0) { 3101793fc096SFrank Li disable_irq(fep->irq[i]); 3102793fc096SFrank Li fec_enet_interrupt(fep->irq[i], dev); 3103793fc096SFrank Li enable_irq(fep->irq[i]); 3104793fc096SFrank Li } 3105793fc096SFrank Li } 3106793fc096SFrank Li } 3107793fc096SFrank Li #endif 3108793fc096SFrank Li 31095bc26726SNimrod Andy static inline void fec_enet_set_netdev_features(struct net_device *netdev, 31104c09eed9SJim Baxter netdev_features_t features) 31114c09eed9SJim Baxter { 31124c09eed9SJim Baxter struct fec_enet_private *fep = netdev_priv(netdev); 31134c09eed9SJim Baxter netdev_features_t changed = features ^ netdev->features; 31144c09eed9SJim Baxter 31154c09eed9SJim Baxter netdev->features = features; 31164c09eed9SJim Baxter 31174c09eed9SJim Baxter /* Receive checksum has been changed */ 31184c09eed9SJim Baxter if (changed & NETIF_F_RXCSUM) { 31194c09eed9SJim Baxter if (features & NETIF_F_RXCSUM) 31204c09eed9SJim Baxter fep->csum_flags |= FLAG_RX_CSUM_ENABLED; 31214c09eed9SJim Baxter else 31224c09eed9SJim Baxter fep->csum_flags &= ~FLAG_RX_CSUM_ENABLED; 31238506fa1dSRussell King } 31245bc26726SNimrod Andy } 31254c09eed9SJim Baxter 31265bc26726SNimrod Andy static int fec_set_features(struct net_device *netdev, 31275bc26726SNimrod Andy netdev_features_t features) 31285bc26726SNimrod Andy { 31295bc26726SNimrod Andy struct fec_enet_private *fep = netdev_priv(netdev); 31305bc26726SNimrod Andy netdev_features_t changed = features ^ netdev->features; 31315bc26726SNimrod Andy 31325b40f709SFabio Estevam if (netif_running(netdev) && changed & NETIF_F_RXCSUM) { 31335bc26726SNimrod Andy napi_disable(&fep->napi); 31345bc26726SNimrod Andy netif_tx_lock_bh(netdev); 31355bc26726SNimrod Andy fec_stop(netdev); 31365bc26726SNimrod Andy fec_enet_set_netdev_features(netdev, features); 3137ef83337dSRussell King fec_restart(netdev); 31384d494cdcSFugang Duan netif_tx_wake_all_queues(netdev); 31396af42d42SRussell King netif_tx_unlock_bh(netdev); 3140dbc64a8eSRussell King napi_enable(&fep->napi); 31415bc26726SNimrod Andy } else { 31425bc26726SNimrod Andy fec_enet_set_netdev_features(netdev, features); 31434c09eed9SJim Baxter } 31444c09eed9SJim Baxter 31454c09eed9SJim Baxter return 0; 31464c09eed9SJim Baxter } 31474c09eed9SJim Baxter 3148793fc096SFrank Li static const struct net_device_ops fec_netdev_ops = { 3149793fc096SFrank Li .ndo_open = fec_enet_open, 3150793fc096SFrank Li .ndo_stop = fec_enet_close, 3151793fc096SFrank Li .ndo_start_xmit = fec_enet_start_xmit, 3152793fc096SFrank Li .ndo_set_rx_mode = set_multicast_list, 3153793fc096SFrank Li .ndo_validate_addr = eth_validate_addr, 3154793fc096SFrank Li .ndo_tx_timeout = fec_timeout, 3155793fc096SFrank Li .ndo_set_mac_address = fec_set_mac_address, 3156793fc096SFrank Li .ndo_do_ioctl = fec_enet_ioctl, 3157793fc096SFrank Li #ifdef CONFIG_NET_POLL_CONTROLLER 3158793fc096SFrank Li .ndo_poll_controller = fec_poll_controller, 3159793fc096SFrank Li #endif 31604c09eed9SJim Baxter .ndo_set_features = fec_set_features, 3161793fc096SFrank Li }; 3162793fc096SFrank Li 316353bb20d1STroy Kisky static const unsigned short offset_des_active_rxq[] = { 316453bb20d1STroy Kisky FEC_R_DES_ACTIVE_0, FEC_R_DES_ACTIVE_1, FEC_R_DES_ACTIVE_2 316553bb20d1STroy Kisky }; 316653bb20d1STroy Kisky 316753bb20d1STroy Kisky static const unsigned short offset_des_active_txq[] = { 316853bb20d1STroy Kisky FEC_X_DES_ACTIVE_0, FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2 316953bb20d1STroy Kisky }; 317053bb20d1STroy Kisky 3171793fc096SFrank Li /* 3172793fc096SFrank Li * XXX: We need to clean up on failure exits here. 3173793fc096SFrank Li * 3174793fc096SFrank Li */ 3175793fc096SFrank Li static int fec_enet_init(struct net_device *ndev) 3176793fc096SFrank Li { 3177793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3178793fc096SFrank Li struct bufdesc *cbd_base; 31794d494cdcSFugang Duan dma_addr_t bd_dma; 318055d0218aSNimrod Andy int bd_size; 318159d0f746SFrank Li unsigned int i; 31827355f276STroy Kisky unsigned dsize = fep->bufdesc_ex ? sizeof(struct bufdesc_ex) : 31837355f276STroy Kisky sizeof(struct bufdesc); 31847355f276STroy Kisky unsigned dsize_log2 = __fls(dsize); 3185453e9dc4SStefan Agner int ret; 318655d0218aSNimrod Andy 31877355f276STroy Kisky WARN_ON(dsize != (1 << dsize_log2)); 31883f1dcc6aSLucas Stach #if defined(CONFIG_ARM) || defined(CONFIG_ARM64) 318941ef84ceSFugang Duan fep->rx_align = 0xf; 319041ef84ceSFugang Duan fep->tx_align = 0xf; 319141ef84ceSFugang Duan #else 319241ef84ceSFugang Duan fep->rx_align = 0x3; 319341ef84ceSFugang Duan fep->tx_align = 0x3; 319441ef84ceSFugang Duan #endif 319541ef84ceSFugang Duan 3196453e9dc4SStefan Agner /* Check mask of the streaming and coherent API */ 3197453e9dc4SStefan Agner ret = dma_set_mask_and_coherent(&fep->pdev->dev, DMA_BIT_MASK(32)); 3198453e9dc4SStefan Agner if (ret < 0) { 3199453e9dc4SStefan Agner dev_warn(&fep->pdev->dev, "No suitable DMA available\n"); 3200453e9dc4SStefan Agner return ret; 3201453e9dc4SStefan Agner } 3202453e9dc4SStefan Agner 320359d0f746SFrank Li fec_enet_alloc_queue(ndev); 320479f33912SNimrod Andy 32057355f276STroy Kisky bd_size = (fep->total_tx_ring_size + fep->total_rx_ring_size) * dsize; 3206793fc096SFrank Li 3207793fc096SFrank Li /* Allocate memory for buffer descriptors. */ 3208c0a1a0a6SLucas Stach cbd_base = dmam_alloc_coherent(&fep->pdev->dev, bd_size, &bd_dma, 3209793fc096SFrank Li GFP_KERNEL); 32104d494cdcSFugang Duan if (!cbd_base) { 32114d494cdcSFugang Duan return -ENOMEM; 32124d494cdcSFugang Duan } 3213793fc096SFrank Li 3214793fc096SFrank Li /* Get the Ethernet address */ 3215793fc096SFrank Li fec_get_mac(ndev); 321644934facSLucas Stach /* make sure MAC we just acquired is programmed into the hw */ 321744934facSLucas Stach fec_set_mac_address(ndev, NULL); 3218793fc096SFrank Li 3219793fc096SFrank Li /* Set receive and transmit descriptor base. */ 322059d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) { 32217355f276STroy Kisky struct fec_enet_priv_rx_q *rxq = fep->rx_queue[i]; 32227355f276STroy Kisky unsigned size = dsize * rxq->bd.ring_size; 32237355f276STroy Kisky 32247355f276STroy Kisky rxq->bd.qid = i; 32257355f276STroy Kisky rxq->bd.base = cbd_base; 32267355f276STroy Kisky rxq->bd.cur = cbd_base; 32277355f276STroy Kisky rxq->bd.dma = bd_dma; 32287355f276STroy Kisky rxq->bd.dsize = dsize; 32297355f276STroy Kisky rxq->bd.dsize_log2 = dsize_log2; 323053bb20d1STroy Kisky rxq->bd.reg_desc_active = fep->hwp + offset_des_active_rxq[i]; 32317355f276STroy Kisky bd_dma += size; 32327355f276STroy Kisky cbd_base = (struct bufdesc *)(((void *)cbd_base) + size); 32337355f276STroy Kisky rxq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize); 323459d0f746SFrank Li } 323559d0f746SFrank Li 323659d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 32377355f276STroy Kisky struct fec_enet_priv_tx_q *txq = fep->tx_queue[i]; 32387355f276STroy Kisky unsigned size = dsize * txq->bd.ring_size; 32397355f276STroy Kisky 32407355f276STroy Kisky txq->bd.qid = i; 32417355f276STroy Kisky txq->bd.base = cbd_base; 32427355f276STroy Kisky txq->bd.cur = cbd_base; 32437355f276STroy Kisky txq->bd.dma = bd_dma; 32447355f276STroy Kisky txq->bd.dsize = dsize; 32457355f276STroy Kisky txq->bd.dsize_log2 = dsize_log2; 324653bb20d1STroy Kisky txq->bd.reg_desc_active = fep->hwp + offset_des_active_txq[i]; 32477355f276STroy Kisky bd_dma += size; 32487355f276STroy Kisky cbd_base = (struct bufdesc *)(((void *)cbd_base) + size); 32497355f276STroy Kisky txq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize); 325059d0f746SFrank Li } 32514d494cdcSFugang Duan 3252793fc096SFrank Li 3253793fc096SFrank Li /* The FEC Ethernet specific entries in the device structure */ 3254793fc096SFrank Li ndev->watchdog_timeo = TX_TIMEOUT; 3255793fc096SFrank Li ndev->netdev_ops = &fec_netdev_ops; 3256793fc096SFrank Li ndev->ethtool_ops = &fec_enet_ethtool_ops; 3257793fc096SFrank Li 3258793fc096SFrank Li writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK); 3259322555f5SFabio Estevam netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, NAPI_POLL_WEIGHT); 3260793fc096SFrank Li 32616b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_VLAN) 3262cdffcf1bSJim Baxter /* enable hw VLAN support */ 3263cdffcf1bSJim Baxter ndev->features |= NETIF_F_HW_VLAN_CTAG_RX; 3264cdffcf1bSJim Baxter 32656b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_CSUM) { 326679f33912SNimrod Andy ndev->gso_max_segs = FEC_MAX_TSO_SEGS; 326779f33912SNimrod Andy 32684c09eed9SJim Baxter /* enable hw accelerator */ 32694c09eed9SJim Baxter ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM 327079f33912SNimrod Andy | NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO); 32714c09eed9SJim Baxter fep->csum_flags |= FLAG_RX_CSUM_ENABLED; 327248496255SShawn Guo } 32734c09eed9SJim Baxter 32746b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) { 327541ef84ceSFugang Duan fep->tx_align = 0; 327641ef84ceSFugang Duan fep->rx_align = 0x3f; 327741ef84ceSFugang Duan } 327841ef84ceSFugang Duan 327909d1e541SNimrod Andy ndev->hw_features = ndev->features; 328009d1e541SNimrod Andy 3281ef83337dSRussell King fec_restart(ndev); 3282793fc096SFrank Li 32832b30842bSAndrew Lunn if (fep->quirks & FEC_QUIRK_MIB_CLEAR) 32842b30842bSAndrew Lunn fec_enet_clear_ethtool_stats(ndev); 32852b30842bSAndrew Lunn else 328680cca775SNikita Yushchenko fec_enet_update_ethtool_stats(ndev); 328780cca775SNikita Yushchenko 3288793fc096SFrank Li return 0; 3289793fc096SFrank Li } 3290793fc096SFrank Li 3291793fc096SFrank Li #ifdef CONFIG_OF 32929269e556SFugang Duan static int fec_reset_phy(struct platform_device *pdev) 3293793fc096SFrank Li { 3294793fc096SFrank Li int err, phy_reset; 3295962d8cdcSBernhard Walle bool active_high = false; 3296159a0760SQuentin Schulz int msec = 1, phy_post_delay = 0; 3297793fc096SFrank Li struct device_node *np = pdev->dev.of_node; 3298793fc096SFrank Li 3299793fc096SFrank Li if (!np) 33009269e556SFugang Duan return 0; 3301793fc096SFrank Li 330261e04ccbSFugang Duan err = of_property_read_u32(np, "phy-reset-duration", &msec); 3303793fc096SFrank Li /* A sane reset duration should not be longer than 1s */ 330461e04ccbSFugang Duan if (!err && msec > 1000) 3305793fc096SFrank Li msec = 1; 3306793fc096SFrank Li 3307793fc096SFrank Li phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0); 33089269e556SFugang Duan if (phy_reset == -EPROBE_DEFER) 33099269e556SFugang Duan return phy_reset; 33109269e556SFugang Duan else if (!gpio_is_valid(phy_reset)) 33119269e556SFugang Duan return 0; 3312793fc096SFrank Li 3313159a0760SQuentin Schulz err = of_property_read_u32(np, "phy-reset-post-delay", &phy_post_delay); 3314159a0760SQuentin Schulz /* valid reset duration should be less than 1s */ 3315159a0760SQuentin Schulz if (!err && phy_post_delay > 1000) 3316159a0760SQuentin Schulz return -EINVAL; 3317159a0760SQuentin Schulz 3318962d8cdcSBernhard Walle active_high = of_property_read_bool(np, "phy-reset-active-high"); 331964f10f6eSBernhard Walle 3320793fc096SFrank Li err = devm_gpio_request_one(&pdev->dev, phy_reset, 3321962d8cdcSBernhard Walle active_high ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, 332264f10f6eSBernhard Walle "phy-reset"); 3323793fc096SFrank Li if (err) { 3324793fc096SFrank Li dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err); 33259269e556SFugang Duan return err; 3326793fc096SFrank Li } 3327eb37c563SStefan Wahren 3328eb37c563SStefan Wahren if (msec > 20) 3329793fc096SFrank Li msleep(msec); 3330eb37c563SStefan Wahren else 3331eb37c563SStefan Wahren usleep_range(msec * 1000, msec * 1000 + 1000); 3332eb37c563SStefan Wahren 3333962d8cdcSBernhard Walle gpio_set_value_cansleep(phy_reset, !active_high); 33349269e556SFugang Duan 3335159a0760SQuentin Schulz if (!phy_post_delay) 3336159a0760SQuentin Schulz return 0; 3337159a0760SQuentin Schulz 3338159a0760SQuentin Schulz if (phy_post_delay > 20) 3339159a0760SQuentin Schulz msleep(phy_post_delay); 3340159a0760SQuentin Schulz else 3341159a0760SQuentin Schulz usleep_range(phy_post_delay * 1000, 3342159a0760SQuentin Schulz phy_post_delay * 1000 + 1000); 3343159a0760SQuentin Schulz 33449269e556SFugang Duan return 0; 3345793fc096SFrank Li } 3346793fc096SFrank Li #else /* CONFIG_OF */ 33479269e556SFugang Duan static int fec_reset_phy(struct platform_device *pdev) 3348793fc096SFrank Li { 3349793fc096SFrank Li /* 3350793fc096SFrank Li * In case of platform probe, the reset has been done 3351793fc096SFrank Li * by machine code. 3352793fc096SFrank Li */ 33539269e556SFugang Duan return 0; 3354793fc096SFrank Li } 3355793fc096SFrank Li #endif /* CONFIG_OF */ 3356793fc096SFrank Li 33579fc095f1SFugang Duan static void 33589fc095f1SFugang Duan fec_enet_get_queue_num(struct platform_device *pdev, int *num_tx, int *num_rx) 33599fc095f1SFugang Duan { 33609fc095f1SFugang Duan struct device_node *np = pdev->dev.of_node; 33619fc095f1SFugang Duan 33629fc095f1SFugang Duan *num_tx = *num_rx = 1; 33639fc095f1SFugang Duan 33649fc095f1SFugang Duan if (!np || !of_device_is_available(np)) 33659fc095f1SFugang Duan return; 33669fc095f1SFugang Duan 33679fc095f1SFugang Duan /* parse the num of tx and rx queues */ 336873b1c90dSSaurabh Sengar of_property_read_u32(np, "fsl,num-tx-queues", num_tx); 3369b7bd75cfSFrank Li 337073b1c90dSSaurabh Sengar of_property_read_u32(np, "fsl,num-rx-queues", num_rx); 33719fc095f1SFugang Duan 33729fc095f1SFugang Duan if (*num_tx < 1 || *num_tx > FEC_ENET_MAX_TX_QS) { 3373b7bd75cfSFrank Li dev_warn(&pdev->dev, "Invalid num_tx(=%d), fall back to 1\n", 33749fc095f1SFugang Duan *num_tx); 33759fc095f1SFugang Duan *num_tx = 1; 33769fc095f1SFugang Duan return; 33779fc095f1SFugang Duan } 33789fc095f1SFugang Duan 33799fc095f1SFugang Duan if (*num_rx < 1 || *num_rx > FEC_ENET_MAX_RX_QS) { 3380b7bd75cfSFrank Li dev_warn(&pdev->dev, "Invalid num_rx(=%d), fall back to 1\n", 33819fc095f1SFugang Duan *num_rx); 33829fc095f1SFugang Duan *num_rx = 1; 33839fc095f1SFugang Duan return; 33849fc095f1SFugang Duan } 33859fc095f1SFugang Duan 33869fc095f1SFugang Duan } 33879fc095f1SFugang Duan 33884ad1ceecSTroy Kisky static int fec_enet_get_irq_cnt(struct platform_device *pdev) 33894ad1ceecSTroy Kisky { 33904ad1ceecSTroy Kisky int irq_cnt = platform_irq_count(pdev); 33914ad1ceecSTroy Kisky 33924ad1ceecSTroy Kisky if (irq_cnt > FEC_IRQ_NUM) 33934ad1ceecSTroy Kisky irq_cnt = FEC_IRQ_NUM; /* last for pps */ 33944ad1ceecSTroy Kisky else if (irq_cnt == 2) 33954ad1ceecSTroy Kisky irq_cnt = 1; /* last for pps */ 33964ad1ceecSTroy Kisky else if (irq_cnt <= 0) 33974ad1ceecSTroy Kisky irq_cnt = 1; /* At least 1 irq is needed */ 33984ad1ceecSTroy Kisky return irq_cnt; 33994ad1ceecSTroy Kisky } 34004ad1ceecSTroy Kisky 3401793fc096SFrank Li static int 3402793fc096SFrank Li fec_probe(struct platform_device *pdev) 3403793fc096SFrank Li { 3404793fc096SFrank Li struct fec_enet_private *fep; 3405793fc096SFrank Li struct fec_platform_data *pdata; 34060c65b2b9SAndrew Lunn phy_interface_t interface; 3407793fc096SFrank Li struct net_device *ndev; 3408793fc096SFrank Li int i, irq, ret = 0; 3409793fc096SFrank Li const struct of_device_id *of_id; 3410793fc096SFrank Li static int dev_id; 3411407066f8SUwe Kleine-König struct device_node *np = pdev->dev.of_node, *phy_node; 3412b7bd75cfSFrank Li int num_tx_qs; 3413b7bd75cfSFrank Li int num_rx_qs; 34144ad1ceecSTroy Kisky char irq_name[8]; 34154ad1ceecSTroy Kisky int irq_cnt; 3416793fc096SFrank Li 34179fc095f1SFugang Duan fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs); 34189fc095f1SFugang Duan 3419793fc096SFrank Li /* Init network device */ 342080cca775SNikita Yushchenko ndev = alloc_etherdev_mqs(sizeof(struct fec_enet_private) + 3421f85de666SNikita Yushchenko FEC_STATS_SIZE, num_tx_qs, num_rx_qs); 3422793fc096SFrank Li if (!ndev) 3423793fc096SFrank Li return -ENOMEM; 3424793fc096SFrank Li 3425793fc096SFrank Li SET_NETDEV_DEV(ndev, &pdev->dev); 3426793fc096SFrank Li 3427793fc096SFrank Li /* setup board info structure */ 3428793fc096SFrank Li fep = netdev_priv(ndev); 3429793fc096SFrank Li 34306b7e4008SLothar Waßmann of_id = of_match_device(fec_dt_ids, &pdev->dev); 34316b7e4008SLothar Waßmann if (of_id) 34326b7e4008SLothar Waßmann pdev->id_entry = of_id->data; 34336b7e4008SLothar Waßmann fep->quirks = pdev->id_entry->driver_data; 34346b7e4008SLothar Waßmann 34350c818594SHubert Feurstein fep->netdev = ndev; 34369fc095f1SFugang Duan fep->num_rx_queues = num_rx_qs; 34379fc095f1SFugang Duan fep->num_tx_queues = num_tx_qs; 34389fc095f1SFugang Duan 3439d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 3440793fc096SFrank Li /* default enable pause frame auto negotiation */ 34416b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_GBIT) 3442793fc096SFrank Li fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG; 3443d1391930SGuenter Roeck #endif 3444793fc096SFrank Li 34455bbde4d2SNimrod Andy /* Select default pin state */ 34465bbde4d2SNimrod Andy pinctrl_pm_select_default_state(&pdev->dev); 34475bbde4d2SNimrod Andy 34484f830a5aSYueHaibing fep->hwp = devm_platform_ioremap_resource(pdev, 0); 3449941e173aSTushar Behera if (IS_ERR(fep->hwp)) { 3450941e173aSTushar Behera ret = PTR_ERR(fep->hwp); 3451941e173aSTushar Behera goto failed_ioremap; 3452941e173aSTushar Behera } 3453941e173aSTushar Behera 3454793fc096SFrank Li fep->pdev = pdev; 3455793fc096SFrank Li fep->dev_id = dev_id++; 3456793fc096SFrank Li 3457793fc096SFrank Li platform_set_drvdata(pdev, ndev); 3458793fc096SFrank Li 345929380905SLucas Stach if ((of_machine_is_compatible("fsl,imx6q") || 346029380905SLucas Stach of_machine_is_compatible("fsl,imx6dl")) && 346129380905SLucas Stach !of_property_read_bool(np, "fsl,err006687-workaround-present")) 346229380905SLucas Stach fep->quirks |= FEC_QUIRK_ERR006687; 346329380905SLucas Stach 3464de40ed31SNimrod Andy if (of_get_property(np, "fsl,magic-packet", NULL)) 3465de40ed31SNimrod Andy fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET; 3466de40ed31SNimrod Andy 3467407066f8SUwe Kleine-König phy_node = of_parse_phandle(np, "phy-handle", 0); 3468407066f8SUwe Kleine-König if (!phy_node && of_phy_is_fixed_link(np)) { 3469407066f8SUwe Kleine-König ret = of_phy_register_fixed_link(np); 3470407066f8SUwe Kleine-König if (ret < 0) { 3471407066f8SUwe Kleine-König dev_err(&pdev->dev, 3472407066f8SUwe Kleine-König "broken fixed-link specification\n"); 3473407066f8SUwe Kleine-König goto failed_phy; 3474407066f8SUwe Kleine-König } 3475407066f8SUwe Kleine-König phy_node = of_node_get(np); 3476407066f8SUwe Kleine-König } 3477407066f8SUwe Kleine-König fep->phy_node = phy_node; 3478407066f8SUwe Kleine-König 34790c65b2b9SAndrew Lunn ret = of_get_phy_mode(pdev->dev.of_node, &interface); 34800c65b2b9SAndrew Lunn if (ret) { 348194660ba0SJingoo Han pdata = dev_get_platdata(&pdev->dev); 3482793fc096SFrank Li if (pdata) 3483793fc096SFrank Li fep->phy_interface = pdata->phy; 3484793fc096SFrank Li else 3485793fc096SFrank Li fep->phy_interface = PHY_INTERFACE_MODE_MII; 3486793fc096SFrank Li } else { 34870c65b2b9SAndrew Lunn fep->phy_interface = interface; 3488793fc096SFrank Li } 3489793fc096SFrank Li 3490793fc096SFrank Li fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); 3491793fc096SFrank Li if (IS_ERR(fep->clk_ipg)) { 3492793fc096SFrank Li ret = PTR_ERR(fep->clk_ipg); 3493793fc096SFrank Li goto failed_clk; 3494793fc096SFrank Li } 3495793fc096SFrank Li 3496793fc096SFrank Li fep->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); 3497793fc096SFrank Li if (IS_ERR(fep->clk_ahb)) { 3498793fc096SFrank Li ret = PTR_ERR(fep->clk_ahb); 3499793fc096SFrank Li goto failed_clk; 3500793fc096SFrank Li } 3501793fc096SFrank Li 3502d851b47bSFugang Duan fep->itr_clk_rate = clk_get_rate(fep->clk_ahb); 3503d851b47bSFugang Duan 350438f56f33SLinus Torvalds /* enet_out is optional, depends on board */ 350538f56f33SLinus Torvalds fep->clk_enet_out = devm_clk_get(&pdev->dev, "enet_out"); 350638f56f33SLinus Torvalds if (IS_ERR(fep->clk_enet_out)) 350738f56f33SLinus Torvalds fep->clk_enet_out = NULL; 350838f56f33SLinus Torvalds 350991c0d987SNimrod Andy fep->ptp_clk_on = false; 351091c0d987SNimrod Andy mutex_init(&fep->ptp_clk_mutex); 35119b5330edSFugang Duan 35129b5330edSFugang Duan /* clk_ref is optional, depends on board */ 35139b5330edSFugang Duan fep->clk_ref = devm_clk_get(&pdev->dev, "enet_clk_ref"); 35149b5330edSFugang Duan if (IS_ERR(fep->clk_ref)) 35159b5330edSFugang Duan fep->clk_ref = NULL; 35169b5330edSFugang Duan 35176b7e4008SLothar Waßmann fep->bufdesc_ex = fep->quirks & FEC_QUIRK_HAS_BUFDESC_EX; 3518793fc096SFrank Li fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp"); 3519793fc096SFrank Li if (IS_ERR(fep->clk_ptp)) { 352038f56f33SLinus Torvalds fep->clk_ptp = NULL; 3521217b5844SLothar Waßmann fep->bufdesc_ex = false; 3522793fc096SFrank Li } 3523793fc096SFrank Li 3524e8fcfcd5SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 352513a097bdSFabio Estevam if (ret) 352613a097bdSFabio Estevam goto failed_clk; 352713a097bdSFabio Estevam 35288fff755eSAndrew Lunn ret = clk_prepare_enable(fep->clk_ipg); 35298fff755eSAndrew Lunn if (ret) 35308fff755eSAndrew Lunn goto failed_clk_ipg; 3531d7c3a206SAndy Duan ret = clk_prepare_enable(fep->clk_ahb); 3532d7c3a206SAndy Duan if (ret) 3533d7c3a206SAndy Duan goto failed_clk_ahb; 35348fff755eSAndrew Lunn 353525974d8aSStefan Agner fep->reg_phy = devm_regulator_get_optional(&pdev->dev, "phy"); 3536f4e9f3d2SFabio Estevam if (!IS_ERR(fep->reg_phy)) { 3537f4e9f3d2SFabio Estevam ret = regulator_enable(fep->reg_phy); 3538793fc096SFrank Li if (ret) { 3539793fc096SFrank Li dev_err(&pdev->dev, 3540793fc096SFrank Li "Failed to enable phy regulator: %d\n", ret); 3541793fc096SFrank Li goto failed_regulator; 3542793fc096SFrank Li } 3543f6a4d607SFabio Estevam } else { 35443f38c683SFugang Duan if (PTR_ERR(fep->reg_phy) == -EPROBE_DEFER) { 35453f38c683SFugang Duan ret = -EPROBE_DEFER; 35463f38c683SFugang Duan goto failed_regulator; 35473f38c683SFugang Duan } 3548f6a4d607SFabio Estevam fep->reg_phy = NULL; 3549793fc096SFrank Li } 3550793fc096SFrank Li 35518fff755eSAndrew Lunn pm_runtime_set_autosuspend_delay(&pdev->dev, FEC_MDIO_PM_TIMEOUT); 35528fff755eSAndrew Lunn pm_runtime_use_autosuspend(&pdev->dev); 355314d2b7c1SLucas Stach pm_runtime_get_noresume(&pdev->dev); 35548fff755eSAndrew Lunn pm_runtime_set_active(&pdev->dev); 35558fff755eSAndrew Lunn pm_runtime_enable(&pdev->dev); 35568fff755eSAndrew Lunn 35579269e556SFugang Duan ret = fec_reset_phy(pdev); 35589269e556SFugang Duan if (ret) 35599269e556SFugang Duan goto failed_reset; 3560793fc096SFrank Li 35614ad1ceecSTroy Kisky irq_cnt = fec_enet_get_irq_cnt(pdev); 3562793fc096SFrank Li if (fep->bufdesc_ex) 35634ad1ceecSTroy Kisky fec_ptp_init(pdev, irq_cnt); 3564793fc096SFrank Li 3565793fc096SFrank Li ret = fec_enet_init(ndev); 3566793fc096SFrank Li if (ret) 3567793fc096SFrank Li goto failed_init; 3568793fc096SFrank Li 35694ad1ceecSTroy Kisky for (i = 0; i < irq_cnt; i++) { 35703ded9f2bSArnd Bergmann snprintf(irq_name, sizeof(irq_name), "int%d", i); 35713b56be21SAnson Huang irq = platform_get_irq_byname_optional(pdev, irq_name); 35724ad1ceecSTroy Kisky if (irq < 0) 3573793fc096SFrank Li irq = platform_get_irq(pdev, i); 3574793fc096SFrank Li if (irq < 0) { 3575793fc096SFrank Li ret = irq; 3576793fc096SFrank Li goto failed_irq; 3577793fc096SFrank Li } 35780d9b2ab1SFabio Estevam ret = devm_request_irq(&pdev->dev, irq, fec_enet_interrupt, 357944a272ddSMichael Opdenacker 0, pdev->name, ndev); 35800d9b2ab1SFabio Estevam if (ret) 3581793fc096SFrank Li goto failed_irq; 3582de40ed31SNimrod Andy 3583de40ed31SNimrod Andy fep->irq[i] = irq; 3584793fc096SFrank Li } 3585793fc096SFrank Li 3586b4d39b53SFugang Duan init_completion(&fep->mdio_done); 3587793fc096SFrank Li ret = fec_enet_mii_init(pdev); 3588793fc096SFrank Li if (ret) 3589793fc096SFrank Li goto failed_mii_init; 3590793fc096SFrank Li 3591793fc096SFrank Li /* Carrier starts down, phylib will bring it up */ 3592793fc096SFrank Li netif_carrier_off(ndev); 3593e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 35945bbde4d2SNimrod Andy pinctrl_pm_select_sleep_state(&pdev->dev); 3595793fc096SFrank Li 3596793fc096SFrank Li ret = register_netdev(ndev); 3597793fc096SFrank Li if (ret) 3598793fc096SFrank Li goto failed_register; 3599793fc096SFrank Li 3600de40ed31SNimrod Andy device_init_wakeup(&ndev->dev, fep->wol_flag & 3601de40ed31SNimrod Andy FEC_WOL_HAS_MAGIC_PACKET); 3602de40ed31SNimrod Andy 3603eb1d0640SFabio Estevam if (fep->bufdesc_ex && fep->ptp_clock) 3604eb1d0640SFabio Estevam netdev_info(ndev, "registered PHC device %d\n", fep->dev_id); 3605eb1d0640SFabio Estevam 36061b7bde6dSNimrod Andy fep->rx_copybreak = COPYBREAK_DEFAULT; 360736cdc743SRussell King INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work); 36088fff755eSAndrew Lunn 36098fff755eSAndrew Lunn pm_runtime_mark_last_busy(&pdev->dev); 36108fff755eSAndrew Lunn pm_runtime_put_autosuspend(&pdev->dev); 36118fff755eSAndrew Lunn 3612793fc096SFrank Li return 0; 3613793fc096SFrank Li 3614793fc096SFrank Li failed_register: 3615793fc096SFrank Li fec_enet_mii_remove(fep); 3616793fc096SFrank Li failed_mii_init: 36177a2bbd8dSFabio Estevam failed_irq: 36187a2bbd8dSFabio Estevam failed_init: 361932cba57bSLucas Stach fec_ptp_stop(pdev); 3620f6a4d607SFabio Estevam if (fep->reg_phy) 3621f6a4d607SFabio Estevam regulator_disable(fep->reg_phy); 36229269e556SFugang Duan failed_reset: 3623ce8d24f9SAndy Duan pm_runtime_put_noidle(&pdev->dev); 36249269e556SFugang Duan pm_runtime_disable(&pdev->dev); 3625793fc096SFrank Li failed_regulator: 3626d7c3a206SAndy Duan clk_disable_unprepare(fep->clk_ahb); 3627d7c3a206SAndy Duan failed_clk_ahb: 3628d7c3a206SAndy Duan clk_disable_unprepare(fep->clk_ipg); 36298fff755eSAndrew Lunn failed_clk_ipg: 3630e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 3631793fc096SFrank Li failed_clk: 363282005b1cSJohan Hovold if (of_phy_is_fixed_link(np)) 363382005b1cSJohan Hovold of_phy_deregister_fixed_link(np); 3634407066f8SUwe Kleine-König of_node_put(phy_node); 3635d1616f07SFugang Duan failed_phy: 3636d1616f07SFugang Duan dev_id--; 3637793fc096SFrank Li failed_ioremap: 3638793fc096SFrank Li free_netdev(ndev); 3639793fc096SFrank Li 3640793fc096SFrank Li return ret; 3641793fc096SFrank Li } 3642793fc096SFrank Li 3643793fc096SFrank Li static int 3644793fc096SFrank Li fec_drv_remove(struct platform_device *pdev) 3645793fc096SFrank Li { 3646793fc096SFrank Li struct net_device *ndev = platform_get_drvdata(pdev); 3647793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 364882005b1cSJohan Hovold struct device_node *np = pdev->dev.of_node; 3649a31eda65SChuhong Yuan int ret; 3650a31eda65SChuhong Yuan 3651a31eda65SChuhong Yuan ret = pm_runtime_get_sync(&pdev->dev); 3652a31eda65SChuhong Yuan if (ret < 0) 3653a31eda65SChuhong Yuan return ret; 3654793fc096SFrank Li 365536cdc743SRussell King cancel_work_sync(&fep->tx_timeout_work); 365632cba57bSLucas Stach fec_ptp_stop(pdev); 3657793fc096SFrank Li unregister_netdev(ndev); 3658793fc096SFrank Li fec_enet_mii_remove(fep); 3659f6a4d607SFabio Estevam if (fep->reg_phy) 3660f6a4d607SFabio Estevam regulator_disable(fep->reg_phy); 3661a31eda65SChuhong Yuan 366282005b1cSJohan Hovold if (of_phy_is_fixed_link(np)) 366382005b1cSJohan Hovold of_phy_deregister_fixed_link(np); 3664407066f8SUwe Kleine-König of_node_put(fep->phy_node); 3665793fc096SFrank Li free_netdev(ndev); 3666793fc096SFrank Li 3667a31eda65SChuhong Yuan clk_disable_unprepare(fep->clk_ahb); 3668a31eda65SChuhong Yuan clk_disable_unprepare(fep->clk_ipg); 3669a31eda65SChuhong Yuan pm_runtime_put_noidle(&pdev->dev); 3670a31eda65SChuhong Yuan pm_runtime_disable(&pdev->dev); 3671a31eda65SChuhong Yuan 3672793fc096SFrank Li return 0; 3673793fc096SFrank Li } 3674793fc096SFrank Li 3675dd66d386SFabio Estevam static int __maybe_unused fec_suspend(struct device *dev) 3676793fc096SFrank Li { 3677793fc096SFrank Li struct net_device *ndev = dev_get_drvdata(dev); 3678793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3679793fc096SFrank Li 3680da1774e5SRussell King rtnl_lock(); 3681793fc096SFrank Li if (netif_running(ndev)) { 3682de40ed31SNimrod Andy if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) 3683de40ed31SNimrod Andy fep->wol_flag |= FEC_WOL_FLAG_SLEEP_ON; 368445f5c327SPhilippe Reynes phy_stop(ndev->phydev); 368531a6de34SRussell King napi_disable(&fep->napi); 368631a6de34SRussell King netif_tx_lock_bh(ndev); 3687793fc096SFrank Li netif_device_detach(ndev); 368831a6de34SRussell King netif_tx_unlock_bh(ndev); 368931a6de34SRussell King fec_stop(ndev); 3690e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 3691de40ed31SNimrod Andy if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) 36925bbde4d2SNimrod Andy pinctrl_pm_select_sleep_state(&fep->pdev->dev); 3693f4c4a4e0SNimrod Andy } 3694f4c4a4e0SNimrod Andy rtnl_unlock(); 3695793fc096SFrank Li 3696de40ed31SNimrod Andy if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) 3697238f7bc7SFabio Estevam regulator_disable(fep->reg_phy); 3698238f7bc7SFabio Estevam 3699858eeb7dSNimrod Andy /* SOC supply clock to phy, when clock is disabled, phy link down 3700858eeb7dSNimrod Andy * SOC control phy regulator, when regulator is disabled, phy link down 3701858eeb7dSNimrod Andy */ 3702858eeb7dSNimrod Andy if (fep->clk_enet_out || fep->reg_phy) 3703858eeb7dSNimrod Andy fep->link = 0; 3704858eeb7dSNimrod Andy 3705793fc096SFrank Li return 0; 3706793fc096SFrank Li } 3707793fc096SFrank Li 3708dd66d386SFabio Estevam static int __maybe_unused fec_resume(struct device *dev) 3709793fc096SFrank Li { 3710793fc096SFrank Li struct net_device *ndev = dev_get_drvdata(dev); 3711793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3712de40ed31SNimrod Andy struct fec_platform_data *pdata = fep->pdev->dev.platform_data; 3713238f7bc7SFabio Estevam int ret; 3714de40ed31SNimrod Andy int val; 3715238f7bc7SFabio Estevam 3716de40ed31SNimrod Andy if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) { 3717238f7bc7SFabio Estevam ret = regulator_enable(fep->reg_phy); 3718238f7bc7SFabio Estevam if (ret) 3719238f7bc7SFabio Estevam return ret; 3720238f7bc7SFabio Estevam } 3721793fc096SFrank Li 3722da1774e5SRussell King rtnl_lock(); 3723793fc096SFrank Li if (netif_running(ndev)) { 3724f4c4a4e0SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 3725f4c4a4e0SNimrod Andy if (ret) { 3726f4c4a4e0SNimrod Andy rtnl_unlock(); 3727f4c4a4e0SNimrod Andy goto failed_clk; 3728f4c4a4e0SNimrod Andy } 3729de40ed31SNimrod Andy if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) { 3730de40ed31SNimrod Andy if (pdata && pdata->sleep_mode_enable) 3731de40ed31SNimrod Andy pdata->sleep_mode_enable(false); 3732de40ed31SNimrod Andy val = readl(fep->hwp + FEC_ECNTRL); 3733de40ed31SNimrod Andy val &= ~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP); 3734de40ed31SNimrod Andy writel(val, fep->hwp + FEC_ECNTRL); 3735de40ed31SNimrod Andy fep->wol_flag &= ~FEC_WOL_FLAG_SLEEP_ON; 3736de40ed31SNimrod Andy } else { 3737de40ed31SNimrod Andy pinctrl_pm_select_default_state(&fep->pdev->dev); 3738de40ed31SNimrod Andy } 3739ef83337dSRussell King fec_restart(ndev); 374031a6de34SRussell King netif_tx_lock_bh(ndev); 3741793fc096SFrank Li netif_device_attach(ndev); 37426af42d42SRussell King netif_tx_unlock_bh(ndev); 37436af42d42SRussell King napi_enable(&fep->napi); 374445f5c327SPhilippe Reynes phy_start(ndev->phydev); 3745793fc096SFrank Li } 3746da1774e5SRussell King rtnl_unlock(); 3747793fc096SFrank Li 3748793fc096SFrank Li return 0; 374913a097bdSFabio Estevam 3750e8fcfcd5SNimrod Andy failed_clk: 375113a097bdSFabio Estevam if (fep->reg_phy) 375213a097bdSFabio Estevam regulator_disable(fep->reg_phy); 375313a097bdSFabio Estevam return ret; 3754793fc096SFrank Li } 3755793fc096SFrank Li 37568fff755eSAndrew Lunn static int __maybe_unused fec_runtime_suspend(struct device *dev) 37578fff755eSAndrew Lunn { 37588fff755eSAndrew Lunn struct net_device *ndev = dev_get_drvdata(dev); 37598fff755eSAndrew Lunn struct fec_enet_private *fep = netdev_priv(ndev); 37608fff755eSAndrew Lunn 3761d7c3a206SAndy Duan clk_disable_unprepare(fep->clk_ahb); 37628fff755eSAndrew Lunn clk_disable_unprepare(fep->clk_ipg); 37638fff755eSAndrew Lunn 37648fff755eSAndrew Lunn return 0; 37658fff755eSAndrew Lunn } 37668fff755eSAndrew Lunn 37678fff755eSAndrew Lunn static int __maybe_unused fec_runtime_resume(struct device *dev) 37688fff755eSAndrew Lunn { 37698fff755eSAndrew Lunn struct net_device *ndev = dev_get_drvdata(dev); 37708fff755eSAndrew Lunn struct fec_enet_private *fep = netdev_priv(ndev); 3771d7c3a206SAndy Duan int ret; 37728fff755eSAndrew Lunn 3773d7c3a206SAndy Duan ret = clk_prepare_enable(fep->clk_ahb); 3774d7c3a206SAndy Duan if (ret) 3775d7c3a206SAndy Duan return ret; 3776d7c3a206SAndy Duan ret = clk_prepare_enable(fep->clk_ipg); 3777d7c3a206SAndy Duan if (ret) 3778d7c3a206SAndy Duan goto failed_clk_ipg; 3779d7c3a206SAndy Duan 3780d7c3a206SAndy Duan return 0; 3781d7c3a206SAndy Duan 3782d7c3a206SAndy Duan failed_clk_ipg: 3783d7c3a206SAndy Duan clk_disable_unprepare(fep->clk_ahb); 3784d7c3a206SAndy Duan return ret; 37858fff755eSAndrew Lunn } 37868fff755eSAndrew Lunn 37878fff755eSAndrew Lunn static const struct dev_pm_ops fec_pm_ops = { 37888fff755eSAndrew Lunn SET_SYSTEM_SLEEP_PM_OPS(fec_suspend, fec_resume) 37898fff755eSAndrew Lunn SET_RUNTIME_PM_OPS(fec_runtime_suspend, fec_runtime_resume, NULL) 37908fff755eSAndrew Lunn }; 3791793fc096SFrank Li 3792793fc096SFrank Li static struct platform_driver fec_driver = { 3793793fc096SFrank Li .driver = { 3794793fc096SFrank Li .name = DRIVER_NAME, 3795793fc096SFrank Li .pm = &fec_pm_ops, 3796793fc096SFrank Li .of_match_table = fec_dt_ids, 3797272bb0e9SFabio Estevam .suppress_bind_attrs = true, 3798793fc096SFrank Li }, 3799793fc096SFrank Li .id_table = fec_devtype, 3800793fc096SFrank Li .probe = fec_probe, 3801793fc096SFrank Li .remove = fec_drv_remove, 3802793fc096SFrank Li }; 3803793fc096SFrank Li 3804793fc096SFrank Li module_platform_driver(fec_driver); 3805793fc096SFrank Li 3806f8c0aca9SFabio Estevam MODULE_ALIAS("platform:"DRIVER_NAME); 3807793fc096SFrank Li MODULE_LICENSE("GPL"); 3808