1793fc096SFrank Li /* 2793fc096SFrank Li * Fast Ethernet Controller (FEC) driver for Motorola MPC8xx. 3793fc096SFrank Li * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) 4793fc096SFrank Li * 5793fc096SFrank Li * Right now, I am very wasteful with the buffers. I allocate memory 6793fc096SFrank Li * pages and then divide them into 2K frame buffers. This way I know I 7793fc096SFrank Li * have buffers large enough to hold one frame within one buffer descriptor. 8793fc096SFrank Li * Once I get this working, I will use 64 or 128 byte CPM buffers, which 9793fc096SFrank Li * will be much more memory efficient and will easily handle lots of 10793fc096SFrank Li * small packets. 11793fc096SFrank Li * 12793fc096SFrank Li * Much better multiple PHY support by Magnus Damm. 13793fc096SFrank Li * Copyright (c) 2000 Ericsson Radio Systems AB. 14793fc096SFrank Li * 15793fc096SFrank Li * Support for FEC controller of ColdFire processors. 16793fc096SFrank Li * Copyright (c) 2001-2005 Greg Ungerer (gerg@snapgear.com) 17793fc096SFrank Li * 18793fc096SFrank Li * Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be) 19793fc096SFrank Li * Copyright (c) 2004-2006 Macq Electronique SA. 20793fc096SFrank Li * 21793fc096SFrank Li * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. 22793fc096SFrank Li */ 23793fc096SFrank Li 24793fc096SFrank Li #include <linux/module.h> 25793fc096SFrank Li #include <linux/kernel.h> 26793fc096SFrank Li #include <linux/string.h> 27793fc096SFrank Li #include <linux/ptrace.h> 28793fc096SFrank Li #include <linux/errno.h> 29793fc096SFrank Li #include <linux/ioport.h> 30793fc096SFrank Li #include <linux/slab.h> 31793fc096SFrank Li #include <linux/interrupt.h> 32793fc096SFrank Li #include <linux/delay.h> 33793fc096SFrank Li #include <linux/netdevice.h> 34793fc096SFrank Li #include <linux/etherdevice.h> 35793fc096SFrank Li #include <linux/skbuff.h> 364c09eed9SJim Baxter #include <linux/in.h> 374c09eed9SJim Baxter #include <linux/ip.h> 384c09eed9SJim Baxter #include <net/ip.h> 3979f33912SNimrod Andy #include <net/tso.h> 404c09eed9SJim Baxter #include <linux/tcp.h> 414c09eed9SJim Baxter #include <linux/udp.h> 424c09eed9SJim Baxter #include <linux/icmp.h> 43793fc096SFrank Li #include <linux/spinlock.h> 44793fc096SFrank Li #include <linux/workqueue.h> 45793fc096SFrank Li #include <linux/bitops.h> 46793fc096SFrank Li #include <linux/io.h> 47793fc096SFrank Li #include <linux/irq.h> 48793fc096SFrank Li #include <linux/clk.h> 49793fc096SFrank Li #include <linux/platform_device.h> 50793fc096SFrank Li #include <linux/phy.h> 51793fc096SFrank Li #include <linux/fec.h> 52793fc096SFrank Li #include <linux/of.h> 53793fc096SFrank Li #include <linux/of_device.h> 54793fc096SFrank Li #include <linux/of_gpio.h> 55407066f8SUwe Kleine-König #include <linux/of_mdio.h> 56793fc096SFrank Li #include <linux/of_net.h> 57793fc096SFrank Li #include <linux/regulator/consumer.h> 58cdffcf1bSJim Baxter #include <linux/if_vlan.h> 59a68ab98eSFabio Estevam #include <linux/pinctrl/consumer.h> 60c259c132SFrank Li #include <linux/prefetch.h> 61793fc096SFrank Li 62793fc096SFrank Li #include <asm/cacheflush.h> 63793fc096SFrank Li 64793fc096SFrank Li #include "fec.h" 65793fc096SFrank Li 66772e42b0SChristoph Müllner static void set_multicast_list(struct net_device *ndev); 67d851b47bSFugang Duan static void fec_enet_itr_coal_init(struct net_device *ndev); 68772e42b0SChristoph Müllner 69793fc096SFrank Li #define DRIVER_NAME "fec" 70793fc096SFrank Li 714d494cdcSFugang Duan #define FEC_ENET_GET_QUQUE(_x) ((_x == 0) ? 1 : ((_x == 1) ? 2 : 0)) 724d494cdcSFugang Duan 73793fc096SFrank Li /* Pause frame feild and FIFO threshold */ 74793fc096SFrank Li #define FEC_ENET_FCE (1 << 5) 75793fc096SFrank Li #define FEC_ENET_RSEM_V 0x84 76793fc096SFrank Li #define FEC_ENET_RSFL_V 16 77793fc096SFrank Li #define FEC_ENET_RAEM_V 0x8 78793fc096SFrank Li #define FEC_ENET_RAFL_V 0x8 79793fc096SFrank Li #define FEC_ENET_OPD_V 0xFFF0 80793fc096SFrank Li 81793fc096SFrank Li /* Controller is ENET-MAC */ 82793fc096SFrank Li #define FEC_QUIRK_ENET_MAC (1 << 0) 83793fc096SFrank Li /* Controller needs driver to swap frame */ 84793fc096SFrank Li #define FEC_QUIRK_SWAP_FRAME (1 << 1) 85793fc096SFrank Li /* Controller uses gasket */ 86793fc096SFrank Li #define FEC_QUIRK_USE_GASKET (1 << 2) 87793fc096SFrank Li /* Controller has GBIT support */ 88793fc096SFrank Li #define FEC_QUIRK_HAS_GBIT (1 << 3) 89793fc096SFrank Li /* Controller has extend desc buffer */ 90793fc096SFrank Li #define FEC_QUIRK_HAS_BUFDESC_EX (1 << 4) 9148496255SShawn Guo /* Controller has hardware checksum support */ 9248496255SShawn Guo #define FEC_QUIRK_HAS_CSUM (1 << 5) 93cdffcf1bSJim Baxter /* Controller has hardware vlan support */ 94cdffcf1bSJim Baxter #define FEC_QUIRK_HAS_VLAN (1 << 6) 9503191656SFrank Li /* ENET IP errata ERR006358 9603191656SFrank Li * 9703191656SFrank Li * If the ready bit in the transmit buffer descriptor (TxBD[R]) is previously 9803191656SFrank Li * detected as not set during a prior frame transmission, then the 9903191656SFrank Li * ENET_TDAR[TDAR] bit is cleared at a later time, even if additional TxBDs 10003191656SFrank Li * were added to the ring and the ENET_TDAR[TDAR] bit is set. This results in 10103191656SFrank Li * frames not being transmitted until there is a 0-to-1 transition on 10203191656SFrank Li * ENET_TDAR[TDAR]. 10303191656SFrank Li */ 10403191656SFrank Li #define FEC_QUIRK_ERR006358 (1 << 7) 10595a77470SFugang Duan /* ENET IP hw AVB 10695a77470SFugang Duan * 10795a77470SFugang Duan * i.MX6SX ENET IP add Audio Video Bridging (AVB) feature support. 10895a77470SFugang Duan * - Two class indicators on receive with configurable priority 10995a77470SFugang Duan * - Two class indicators and line speed timer on transmit allowing 11095a77470SFugang Duan * implementation class credit based shapers externally 11195a77470SFugang Duan * - Additional DMA registers provisioned to allow managing up to 3 11295a77470SFugang Duan * independent rings 11395a77470SFugang Duan */ 11495a77470SFugang Duan #define FEC_QUIRK_HAS_AVB (1 << 8) 11537d6017bSFugang Duan /* There is a TDAR race condition for mutliQ when the software sets TDAR 11637d6017bSFugang Duan * and the UDMA clears TDAR simultaneously or in a small window (2-4 cycles). 11737d6017bSFugang Duan * This will cause the udma_tx and udma_tx_arbiter state machines to hang. 11837d6017bSFugang Duan * The issue exist at i.MX6SX enet IP. 11937d6017bSFugang Duan */ 12037d6017bSFugang Duan #define FEC_QUIRK_ERR007885 (1 << 9) 121793fc096SFrank Li 122793fc096SFrank Li static struct platform_device_id fec_devtype[] = { 123793fc096SFrank Li { 124793fc096SFrank Li /* keep it for coldfire */ 125793fc096SFrank Li .name = DRIVER_NAME, 126793fc096SFrank Li .driver_data = 0, 127793fc096SFrank Li }, { 128793fc096SFrank Li .name = "imx25-fec", 129793fc096SFrank Li .driver_data = FEC_QUIRK_USE_GASKET, 130793fc096SFrank Li }, { 131793fc096SFrank Li .name = "imx27-fec", 132793fc096SFrank Li .driver_data = 0, 133793fc096SFrank Li }, { 134793fc096SFrank Li .name = "imx28-fec", 135793fc096SFrank Li .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME, 136793fc096SFrank Li }, { 137793fc096SFrank Li .name = "imx6q-fec", 138793fc096SFrank Li .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 139cdffcf1bSJim Baxter FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 14003191656SFrank Li FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR006358, 141793fc096SFrank Li }, { 14236803542SShawn Guo .name = "mvf600-fec", 143ca7c4a45SJingchang Lu .driver_data = FEC_QUIRK_ENET_MAC, 144ca7c4a45SJingchang Lu }, { 14595a77470SFugang Duan .name = "imx6sx-fec", 14695a77470SFugang Duan .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 14795a77470SFugang Duan FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 148f88c7edeSNimrod Andy FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | 149f88c7edeSNimrod Andy FEC_QUIRK_ERR007885, 15095a77470SFugang Duan }, { 151793fc096SFrank Li /* sentinel */ 152793fc096SFrank Li } 153793fc096SFrank Li }; 154793fc096SFrank Li MODULE_DEVICE_TABLE(platform, fec_devtype); 155793fc096SFrank Li 156793fc096SFrank Li enum imx_fec_type { 157793fc096SFrank Li IMX25_FEC = 1, /* runs on i.mx25/50/53 */ 158793fc096SFrank Li IMX27_FEC, /* runs on i.mx27/35/51 */ 159793fc096SFrank Li IMX28_FEC, 160793fc096SFrank Li IMX6Q_FEC, 16136803542SShawn Guo MVF600_FEC, 162ba593e00SFugang Duan IMX6SX_FEC, 163793fc096SFrank Li }; 164793fc096SFrank Li 165793fc096SFrank Li static const struct of_device_id fec_dt_ids[] = { 166793fc096SFrank Li { .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], }, 167793fc096SFrank Li { .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], }, 168793fc096SFrank Li { .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], }, 169793fc096SFrank Li { .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], }, 17036803542SShawn Guo { .compatible = "fsl,mvf600-fec", .data = &fec_devtype[MVF600_FEC], }, 171ba593e00SFugang Duan { .compatible = "fsl,imx6sx-fec", .data = &fec_devtype[IMX6SX_FEC], }, 172793fc096SFrank Li { /* sentinel */ } 173793fc096SFrank Li }; 174793fc096SFrank Li MODULE_DEVICE_TABLE(of, fec_dt_ids); 175793fc096SFrank Li 176793fc096SFrank Li static unsigned char macaddr[ETH_ALEN]; 177793fc096SFrank Li module_param_array(macaddr, byte, NULL, 0); 178793fc096SFrank Li MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); 179793fc096SFrank Li 180793fc096SFrank Li #if defined(CONFIG_M5272) 181793fc096SFrank Li /* 182793fc096SFrank Li * Some hardware gets it MAC address out of local flash memory. 183793fc096SFrank Li * if this is non-zero then assume it is the address to get MAC from. 184793fc096SFrank Li */ 185793fc096SFrank Li #if defined(CONFIG_NETtel) 186793fc096SFrank Li #define FEC_FLASHMAC 0xf0006006 187793fc096SFrank Li #elif defined(CONFIG_GILBARCONAP) || defined(CONFIG_SCALES) 188793fc096SFrank Li #define FEC_FLASHMAC 0xf0006000 189793fc096SFrank Li #elif defined(CONFIG_CANCam) 190793fc096SFrank Li #define FEC_FLASHMAC 0xf0020000 191793fc096SFrank Li #elif defined (CONFIG_M5272C3) 192793fc096SFrank Li #define FEC_FLASHMAC (0xffe04000 + 4) 193793fc096SFrank Li #elif defined(CONFIG_MOD5272) 194793fc096SFrank Li #define FEC_FLASHMAC 0xffc0406b 195793fc096SFrank Li #else 196793fc096SFrank Li #define FEC_FLASHMAC 0 197793fc096SFrank Li #endif 198793fc096SFrank Li #endif /* CONFIG_M5272 */ 199793fc096SFrank Li 200cdffcf1bSJim Baxter /* The FEC stores dest/src/type/vlan, data, and checksum for receive packets. 201793fc096SFrank Li */ 202cdffcf1bSJim Baxter #define PKT_MAXBUF_SIZE 1522 203793fc096SFrank Li #define PKT_MINBUF_SIZE 64 204cdffcf1bSJim Baxter #define PKT_MAXBLR_SIZE 1536 205793fc096SFrank Li 2064c09eed9SJim Baxter /* FEC receive acceleration */ 2074c09eed9SJim Baxter #define FEC_RACC_IPDIS (1 << 1) 2084c09eed9SJim Baxter #define FEC_RACC_PRODIS (1 << 2) 2094c09eed9SJim Baxter #define FEC_RACC_OPTIONS (FEC_RACC_IPDIS | FEC_RACC_PRODIS) 2104c09eed9SJim Baxter 211793fc096SFrank Li /* 212793fc096SFrank Li * The 5270/5271/5280/5282/532x RX control register also contains maximum frame 213793fc096SFrank Li * size bits. Other FEC hardware does not, so we need to take that into 214793fc096SFrank Li * account when setting it. 215793fc096SFrank Li */ 216793fc096SFrank Li #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ 217793fc096SFrank Li defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) 218793fc096SFrank Li #define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16) 219793fc096SFrank Li #else 220793fc096SFrank Li #define OPT_FRAME_SIZE 0 221793fc096SFrank Li #endif 222793fc096SFrank Li 223793fc096SFrank Li /* FEC MII MMFR bits definition */ 224793fc096SFrank Li #define FEC_MMFR_ST (1 << 30) 225793fc096SFrank Li #define FEC_MMFR_OP_READ (2 << 28) 226793fc096SFrank Li #define FEC_MMFR_OP_WRITE (1 << 28) 227793fc096SFrank Li #define FEC_MMFR_PA(v) ((v & 0x1f) << 23) 228793fc096SFrank Li #define FEC_MMFR_RA(v) ((v & 0x1f) << 18) 229793fc096SFrank Li #define FEC_MMFR_TA (2 << 16) 230793fc096SFrank Li #define FEC_MMFR_DATA(v) (v & 0xffff) 231793fc096SFrank Li 232793fc096SFrank Li #define FEC_MII_TIMEOUT 30000 /* us */ 233793fc096SFrank Li 234793fc096SFrank Li /* Transmitter timeout */ 235793fc096SFrank Li #define TX_TIMEOUT (2 * HZ) 236793fc096SFrank Li 237793fc096SFrank Li #define FEC_PAUSE_FLAG_AUTONEG 0x1 238793fc096SFrank Li #define FEC_PAUSE_FLAG_ENABLE 0x2 239793fc096SFrank Li 2401b7bde6dSNimrod Andy #define COPYBREAK_DEFAULT 256 2411b7bde6dSNimrod Andy 24279f33912SNimrod Andy #define TSO_HEADER_SIZE 128 24379f33912SNimrod Andy /* Max number of allowed TCP segments for software TSO */ 24479f33912SNimrod Andy #define FEC_MAX_TSO_SEGS 100 24579f33912SNimrod Andy #define FEC_MAX_SKB_DESCS (FEC_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS) 24679f33912SNimrod Andy 24779f33912SNimrod Andy #define IS_TSO_HEADER(txq, addr) \ 24879f33912SNimrod Andy ((addr >= txq->tso_hdrs_dma) && \ 24979f33912SNimrod Andy (addr < txq->tso_hdrs_dma + txq->tx_ring_size * TSO_HEADER_SIZE)) 25079f33912SNimrod Andy 251793fc096SFrank Li static int mii_cnt; 252793fc096SFrank Li 25336e24e2eSDuan Fugang-B38611 static inline 2544d494cdcSFugang Duan struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, 2554d494cdcSFugang Duan struct fec_enet_private *fep, 2564d494cdcSFugang Duan int queue_id) 257793fc096SFrank Li { 25836e24e2eSDuan Fugang-B38611 struct bufdesc *new_bd = bdp + 1; 25936e24e2eSDuan Fugang-B38611 struct bufdesc_ex *ex_new_bd = (struct bufdesc_ex *)bdp + 1; 2604d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq = fep->tx_queue[queue_id]; 2614d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq = fep->rx_queue[queue_id]; 26236e24e2eSDuan Fugang-B38611 struct bufdesc_ex *ex_base; 26336e24e2eSDuan Fugang-B38611 struct bufdesc *base; 26436e24e2eSDuan Fugang-B38611 int ring_size; 26536e24e2eSDuan Fugang-B38611 2664d494cdcSFugang Duan if (bdp >= txq->tx_bd_base) { 2674d494cdcSFugang Duan base = txq->tx_bd_base; 2684d494cdcSFugang Duan ring_size = txq->tx_ring_size; 2694d494cdcSFugang Duan ex_base = (struct bufdesc_ex *)txq->tx_bd_base; 27036e24e2eSDuan Fugang-B38611 } else { 2714d494cdcSFugang Duan base = rxq->rx_bd_base; 2724d494cdcSFugang Duan ring_size = rxq->rx_ring_size; 2734d494cdcSFugang Duan ex_base = (struct bufdesc_ex *)rxq->rx_bd_base; 274793fc096SFrank Li } 275793fc096SFrank Li 27636e24e2eSDuan Fugang-B38611 if (fep->bufdesc_ex) 27736e24e2eSDuan Fugang-B38611 return (struct bufdesc *)((ex_new_bd >= (ex_base + ring_size)) ? 27836e24e2eSDuan Fugang-B38611 ex_base : ex_new_bd); 279793fc096SFrank Li else 28036e24e2eSDuan Fugang-B38611 return (new_bd >= (base + ring_size)) ? 28136e24e2eSDuan Fugang-B38611 base : new_bd; 28236e24e2eSDuan Fugang-B38611 } 28336e24e2eSDuan Fugang-B38611 28436e24e2eSDuan Fugang-B38611 static inline 2854d494cdcSFugang Duan struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, 2864d494cdcSFugang Duan struct fec_enet_private *fep, 2874d494cdcSFugang Duan int queue_id) 28836e24e2eSDuan Fugang-B38611 { 28936e24e2eSDuan Fugang-B38611 struct bufdesc *new_bd = bdp - 1; 29036e24e2eSDuan Fugang-B38611 struct bufdesc_ex *ex_new_bd = (struct bufdesc_ex *)bdp - 1; 2914d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq = fep->tx_queue[queue_id]; 2924d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq = fep->rx_queue[queue_id]; 29336e24e2eSDuan Fugang-B38611 struct bufdesc_ex *ex_base; 29436e24e2eSDuan Fugang-B38611 struct bufdesc *base; 29536e24e2eSDuan Fugang-B38611 int ring_size; 29636e24e2eSDuan Fugang-B38611 2974d494cdcSFugang Duan if (bdp >= txq->tx_bd_base) { 2984d494cdcSFugang Duan base = txq->tx_bd_base; 2994d494cdcSFugang Duan ring_size = txq->tx_ring_size; 3004d494cdcSFugang Duan ex_base = (struct bufdesc_ex *)txq->tx_bd_base; 30136e24e2eSDuan Fugang-B38611 } else { 3024d494cdcSFugang Duan base = rxq->rx_bd_base; 3034d494cdcSFugang Duan ring_size = rxq->rx_ring_size; 3044d494cdcSFugang Duan ex_base = (struct bufdesc_ex *)rxq->rx_bd_base; 30536e24e2eSDuan Fugang-B38611 } 30636e24e2eSDuan Fugang-B38611 30736e24e2eSDuan Fugang-B38611 if (fep->bufdesc_ex) 30836e24e2eSDuan Fugang-B38611 return (struct bufdesc *)((ex_new_bd < ex_base) ? 30936e24e2eSDuan Fugang-B38611 (ex_new_bd + ring_size) : ex_new_bd); 31036e24e2eSDuan Fugang-B38611 else 31136e24e2eSDuan Fugang-B38611 return (new_bd < base) ? (new_bd + ring_size) : new_bd; 312793fc096SFrank Li } 313793fc096SFrank Li 31461a4427bSNimrod Andy static int fec_enet_get_bd_index(struct bufdesc *base, struct bufdesc *bdp, 31561a4427bSNimrod Andy struct fec_enet_private *fep) 31661a4427bSNimrod Andy { 31761a4427bSNimrod Andy return ((const char *)bdp - (const char *)base) / fep->bufdesc_size; 31861a4427bSNimrod Andy } 31961a4427bSNimrod Andy 3204d494cdcSFugang Duan static int fec_enet_get_free_txdesc_num(struct fec_enet_private *fep, 3214d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq) 3226e909283SNimrod Andy { 3236e909283SNimrod Andy int entries; 3246e909283SNimrod Andy 3254d494cdcSFugang Duan entries = ((const char *)txq->dirty_tx - 3264d494cdcSFugang Duan (const char *)txq->cur_tx) / fep->bufdesc_size - 1; 3276e909283SNimrod Andy 3284d494cdcSFugang Duan return entries > 0 ? entries : entries + txq->tx_ring_size; 3296e909283SNimrod Andy } 3306e909283SNimrod Andy 331793fc096SFrank Li static void *swap_buffer(void *bufaddr, int len) 332793fc096SFrank Li { 333793fc096SFrank Li int i; 334793fc096SFrank Li unsigned int *buf = bufaddr; 335793fc096SFrank Li 336ffed61e6SFabio Estevam for (i = 0; i < DIV_ROUND_UP(len, 4); i++, buf++) 337793fc096SFrank Li *buf = cpu_to_be32(*buf); 338793fc096SFrank Li 339793fc096SFrank Li return bufaddr; 340793fc096SFrank Li } 341793fc096SFrank Li 342344756f6SRussell King static void fec_dump(struct net_device *ndev) 343344756f6SRussell King { 344344756f6SRussell King struct fec_enet_private *fep = netdev_priv(ndev); 3454d494cdcSFugang Duan struct bufdesc *bdp; 3464d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 3474d494cdcSFugang Duan int index = 0; 348344756f6SRussell King 349344756f6SRussell King netdev_info(ndev, "TX ring dump\n"); 350344756f6SRussell King pr_info("Nr SC addr len SKB\n"); 351344756f6SRussell King 3524d494cdcSFugang Duan txq = fep->tx_queue[0]; 3534d494cdcSFugang Duan bdp = txq->tx_bd_base; 3544d494cdcSFugang Duan 355344756f6SRussell King do { 356344756f6SRussell King pr_info("%3u %c%c 0x%04x 0x%08lx %4u %p\n", 357344756f6SRussell King index, 3584d494cdcSFugang Duan bdp == txq->cur_tx ? 'S' : ' ', 3594d494cdcSFugang Duan bdp == txq->dirty_tx ? 'H' : ' ', 360344756f6SRussell King bdp->cbd_sc, bdp->cbd_bufaddr, bdp->cbd_datlen, 3614d494cdcSFugang Duan txq->tx_skbuff[index]); 3624d494cdcSFugang Duan bdp = fec_enet_get_nextdesc(bdp, fep, 0); 363344756f6SRussell King index++; 3644d494cdcSFugang Duan } while (bdp != txq->tx_bd_base); 365344756f6SRussell King } 366344756f6SRussell King 36762a02c98SFugang Duan static inline bool is_ipv4_pkt(struct sk_buff *skb) 36862a02c98SFugang Duan { 36962a02c98SFugang Duan return skb->protocol == htons(ETH_P_IP) && ip_hdr(skb)->version == 4; 37062a02c98SFugang Duan } 37162a02c98SFugang Duan 3724c09eed9SJim Baxter static int 3734c09eed9SJim Baxter fec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev) 3744c09eed9SJim Baxter { 3754c09eed9SJim Baxter /* Only run for packets requiring a checksum. */ 3764c09eed9SJim Baxter if (skb->ip_summed != CHECKSUM_PARTIAL) 3774c09eed9SJim Baxter return 0; 3784c09eed9SJim Baxter 3794c09eed9SJim Baxter if (unlikely(skb_cow_head(skb, 0))) 3804c09eed9SJim Baxter return -1; 3814c09eed9SJim Baxter 38262a02c98SFugang Duan if (is_ipv4_pkt(skb)) 38396c50caaSNimrod Andy ip_hdr(skb)->check = 0; 3844c09eed9SJim Baxter *(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) = 0; 3854c09eed9SJim Baxter 3864c09eed9SJim Baxter return 0; 3874c09eed9SJim Baxter } 3884c09eed9SJim Baxter 3896e909283SNimrod Andy static int 3904d494cdcSFugang Duan fec_enet_txq_submit_frag_skb(struct fec_enet_priv_tx_q *txq, 3914d494cdcSFugang Duan struct sk_buff *skb, 3924d494cdcSFugang Duan struct net_device *ndev) 3936e909283SNimrod Andy { 3946e909283SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 3956e909283SNimrod Andy const struct platform_device_id *id_entry = 3966e909283SNimrod Andy platform_get_device_id(fep->pdev); 3974d494cdcSFugang Duan struct bufdesc *bdp = txq->cur_tx; 3986e909283SNimrod Andy struct bufdesc_ex *ebdp; 3996e909283SNimrod Andy int nr_frags = skb_shinfo(skb)->nr_frags; 4004d494cdcSFugang Duan unsigned short queue = skb_get_queue_mapping(skb); 4016e909283SNimrod Andy int frag, frag_len; 4026e909283SNimrod Andy unsigned short status; 4036e909283SNimrod Andy unsigned int estatus = 0; 4046e909283SNimrod Andy skb_frag_t *this_frag; 4056e909283SNimrod Andy unsigned int index; 4066e909283SNimrod Andy void *bufaddr; 407d6bf3143SRussell King dma_addr_t addr; 4086e909283SNimrod Andy int i; 4096e909283SNimrod Andy 4106e909283SNimrod Andy for (frag = 0; frag < nr_frags; frag++) { 4116e909283SNimrod Andy this_frag = &skb_shinfo(skb)->frags[frag]; 4124d494cdcSFugang Duan bdp = fec_enet_get_nextdesc(bdp, fep, queue); 4136e909283SNimrod Andy ebdp = (struct bufdesc_ex *)bdp; 4146e909283SNimrod Andy 4156e909283SNimrod Andy status = bdp->cbd_sc; 4166e909283SNimrod Andy status &= ~BD_ENET_TX_STATS; 4176e909283SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 4186e909283SNimrod Andy frag_len = skb_shinfo(skb)->frags[frag].size; 4196e909283SNimrod Andy 4206e909283SNimrod Andy /* Handle the last BD specially */ 4216e909283SNimrod Andy if (frag == nr_frags - 1) { 4226e909283SNimrod Andy status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); 4236e909283SNimrod Andy if (fep->bufdesc_ex) { 4246e909283SNimrod Andy estatus |= BD_ENET_TX_INT; 4256e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & 4266e909283SNimrod Andy SKBTX_HW_TSTAMP && fep->hwts_tx_en)) 4276e909283SNimrod Andy estatus |= BD_ENET_TX_TS; 4286e909283SNimrod Andy } 4296e909283SNimrod Andy } 4306e909283SNimrod Andy 4316e909283SNimrod Andy if (fep->bufdesc_ex) { 432befe8213SNimrod Andy if (id_entry->driver_data & FEC_QUIRK_HAS_AVB) 433befe8213SNimrod Andy estatus |= FEC_TX_BD_FTYPE(queue); 4346e909283SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 4356e909283SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 4366e909283SNimrod Andy ebdp->cbd_bdu = 0; 4376e909283SNimrod Andy ebdp->cbd_esc = estatus; 4386e909283SNimrod Andy } 4396e909283SNimrod Andy 4406e909283SNimrod Andy bufaddr = page_address(this_frag->page.p) + this_frag->page_offset; 4416e909283SNimrod Andy 4424d494cdcSFugang Duan index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep); 44341ef84ceSFugang Duan if (((unsigned long) bufaddr) & fep->tx_align || 4446e909283SNimrod Andy id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) { 4454d494cdcSFugang Duan memcpy(txq->tx_bounce[index], bufaddr, frag_len); 4464d494cdcSFugang Duan bufaddr = txq->tx_bounce[index]; 4476e909283SNimrod Andy 4486e909283SNimrod Andy if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) 4496e909283SNimrod Andy swap_buffer(bufaddr, frag_len); 4506e909283SNimrod Andy } 4516e909283SNimrod Andy 452d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, bufaddr, frag_len, 453d6bf3143SRussell King DMA_TO_DEVICE); 454d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 4556e909283SNimrod Andy dev_kfree_skb_any(skb); 4566e909283SNimrod Andy if (net_ratelimit()) 4576e909283SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 4586e909283SNimrod Andy goto dma_mapping_error; 4596e909283SNimrod Andy } 4606e909283SNimrod Andy 461d6bf3143SRussell King bdp->cbd_bufaddr = addr; 4626e909283SNimrod Andy bdp->cbd_datlen = frag_len; 4636e909283SNimrod Andy bdp->cbd_sc = status; 4646e909283SNimrod Andy } 4656e909283SNimrod Andy 4664d494cdcSFugang Duan txq->cur_tx = bdp; 4676e909283SNimrod Andy 4686e909283SNimrod Andy return 0; 4696e909283SNimrod Andy 4706e909283SNimrod Andy dma_mapping_error: 4714d494cdcSFugang Duan bdp = txq->cur_tx; 4726e909283SNimrod Andy for (i = 0; i < frag; i++) { 4734d494cdcSFugang Duan bdp = fec_enet_get_nextdesc(bdp, fep, queue); 4746e909283SNimrod Andy dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, 4756e909283SNimrod Andy bdp->cbd_datlen, DMA_TO_DEVICE); 4766e909283SNimrod Andy } 4776e909283SNimrod Andy return NETDEV_TX_OK; 4786e909283SNimrod Andy } 4796e909283SNimrod Andy 4804d494cdcSFugang Duan static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq, 4814d494cdcSFugang Duan struct sk_buff *skb, struct net_device *ndev) 4826e909283SNimrod Andy { 4836e909283SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 4846e909283SNimrod Andy const struct platform_device_id *id_entry = 4856e909283SNimrod Andy platform_get_device_id(fep->pdev); 4866e909283SNimrod Andy int nr_frags = skb_shinfo(skb)->nr_frags; 4876e909283SNimrod Andy struct bufdesc *bdp, *last_bdp; 4886e909283SNimrod Andy void *bufaddr; 489d6bf3143SRussell King dma_addr_t addr; 4906e909283SNimrod Andy unsigned short status; 4916e909283SNimrod Andy unsigned short buflen; 4924d494cdcSFugang Duan unsigned short queue; 4936e909283SNimrod Andy unsigned int estatus = 0; 4946e909283SNimrod Andy unsigned int index; 49579f33912SNimrod Andy int entries_free; 4966e909283SNimrod Andy int ret; 4976e909283SNimrod Andy 4984d494cdcSFugang Duan entries_free = fec_enet_get_free_txdesc_num(fep, txq); 49979f33912SNimrod Andy if (entries_free < MAX_SKB_FRAGS + 1) { 50079f33912SNimrod Andy dev_kfree_skb_any(skb); 50179f33912SNimrod Andy if (net_ratelimit()) 50279f33912SNimrod Andy netdev_err(ndev, "NOT enough BD for SG!\n"); 50379f33912SNimrod Andy return NETDEV_TX_OK; 50479f33912SNimrod Andy } 50579f33912SNimrod Andy 5066e909283SNimrod Andy /* Protocol checksum off-load for TCP and UDP. */ 5076e909283SNimrod Andy if (fec_enet_clear_csum(skb, ndev)) { 5086e909283SNimrod Andy dev_kfree_skb_any(skb); 5096e909283SNimrod Andy return NETDEV_TX_OK; 5106e909283SNimrod Andy } 5116e909283SNimrod Andy 5126e909283SNimrod Andy /* Fill in a Tx ring entry */ 5134d494cdcSFugang Duan bdp = txq->cur_tx; 5146e909283SNimrod Andy status = bdp->cbd_sc; 5156e909283SNimrod Andy status &= ~BD_ENET_TX_STATS; 5166e909283SNimrod Andy 5176e909283SNimrod Andy /* Set buffer length and buffer pointer */ 5186e909283SNimrod Andy bufaddr = skb->data; 5196e909283SNimrod Andy buflen = skb_headlen(skb); 5206e909283SNimrod Andy 5214d494cdcSFugang Duan queue = skb_get_queue_mapping(skb); 5224d494cdcSFugang Duan index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep); 52341ef84ceSFugang Duan if (((unsigned long) bufaddr) & fep->tx_align || 5246e909283SNimrod Andy id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) { 5254d494cdcSFugang Duan memcpy(txq->tx_bounce[index], skb->data, buflen); 5264d494cdcSFugang Duan bufaddr = txq->tx_bounce[index]; 5276e909283SNimrod Andy 5286e909283SNimrod Andy if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) 5296e909283SNimrod Andy swap_buffer(bufaddr, buflen); 5306e909283SNimrod Andy } 5316e909283SNimrod Andy 532d6bf3143SRussell King /* Push the data cache so the CPM does not get stale memory data. */ 533d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, bufaddr, buflen, DMA_TO_DEVICE); 534d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 5356e909283SNimrod Andy dev_kfree_skb_any(skb); 5366e909283SNimrod Andy if (net_ratelimit()) 5376e909283SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 5386e909283SNimrod Andy return NETDEV_TX_OK; 5396e909283SNimrod Andy } 5406e909283SNimrod Andy 5416e909283SNimrod Andy if (nr_frags) { 5424d494cdcSFugang Duan ret = fec_enet_txq_submit_frag_skb(txq, skb, ndev); 5436e909283SNimrod Andy if (ret) 5446e909283SNimrod Andy return ret; 5456e909283SNimrod Andy } else { 5466e909283SNimrod Andy status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); 5476e909283SNimrod Andy if (fep->bufdesc_ex) { 5486e909283SNimrod Andy estatus = BD_ENET_TX_INT; 5496e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & 5506e909283SNimrod Andy SKBTX_HW_TSTAMP && fep->hwts_tx_en)) 5516e909283SNimrod Andy estatus |= BD_ENET_TX_TS; 5526e909283SNimrod Andy } 5536e909283SNimrod Andy } 5546e909283SNimrod Andy 5556e909283SNimrod Andy if (fep->bufdesc_ex) { 5566e909283SNimrod Andy 5576e909283SNimrod Andy struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 5586e909283SNimrod Andy 5596e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && 5606e909283SNimrod Andy fep->hwts_tx_en)) 5616e909283SNimrod Andy skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; 5626e909283SNimrod Andy 563befe8213SNimrod Andy if (id_entry->driver_data & FEC_QUIRK_HAS_AVB) 564befe8213SNimrod Andy estatus |= FEC_TX_BD_FTYPE(queue); 565befe8213SNimrod Andy 5666e909283SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 5676e909283SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 5686e909283SNimrod Andy 5696e909283SNimrod Andy ebdp->cbd_bdu = 0; 5706e909283SNimrod Andy ebdp->cbd_esc = estatus; 5716e909283SNimrod Andy } 5726e909283SNimrod Andy 5734d494cdcSFugang Duan last_bdp = txq->cur_tx; 5744d494cdcSFugang Duan index = fec_enet_get_bd_index(txq->tx_bd_base, last_bdp, fep); 5756e909283SNimrod Andy /* Save skb pointer */ 5764d494cdcSFugang Duan txq->tx_skbuff[index] = skb; 5776e909283SNimrod Andy 5786e909283SNimrod Andy bdp->cbd_datlen = buflen; 579d6bf3143SRussell King bdp->cbd_bufaddr = addr; 5806e909283SNimrod Andy 5816e909283SNimrod Andy /* Send it on its way. Tell FEC it's ready, interrupt when done, 5826e909283SNimrod Andy * it's the last BD of the frame, and to put the CRC on the end. 5836e909283SNimrod Andy */ 5846e909283SNimrod Andy status |= (BD_ENET_TX_READY | BD_ENET_TX_TC); 5856e909283SNimrod Andy bdp->cbd_sc = status; 5866e909283SNimrod Andy 587793fc096SFrank Li /* If this was the last BD in the ring, start at the beginning again. */ 5884d494cdcSFugang Duan bdp = fec_enet_get_nextdesc(last_bdp, fep, queue); 589793fc096SFrank Li 5907a2a8451SEric Dumazet skb_tx_timestamp(skb); 5917a2a8451SEric Dumazet 5924d494cdcSFugang Duan txq->cur_tx = bdp; 593793fc096SFrank Li 594793fc096SFrank Li /* Trigger transmission start */ 5954d494cdcSFugang Duan writel(0, fep->hwp + FEC_X_DES_ACTIVE(queue)); 596793fc096SFrank Li 5976e909283SNimrod Andy return 0; 598793fc096SFrank Li } 599793fc096SFrank Li 60079f33912SNimrod Andy static int 6014d494cdcSFugang Duan fec_enet_txq_put_data_tso(struct fec_enet_priv_tx_q *txq, struct sk_buff *skb, 6024d494cdcSFugang Duan struct net_device *ndev, 60379f33912SNimrod Andy struct bufdesc *bdp, int index, char *data, 60479f33912SNimrod Andy int size, bool last_tcp, bool is_last) 60579f33912SNimrod Andy { 60679f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 60779f33912SNimrod Andy const struct platform_device_id *id_entry = 60879f33912SNimrod Andy platform_get_device_id(fep->pdev); 60961cd2ebbSFabian Frederick struct bufdesc_ex *ebdp = container_of(bdp, struct bufdesc_ex, desc); 610befe8213SNimrod Andy unsigned short queue = skb_get_queue_mapping(skb); 61179f33912SNimrod Andy unsigned short status; 61279f33912SNimrod Andy unsigned int estatus = 0; 613d6bf3143SRussell King dma_addr_t addr; 61479f33912SNimrod Andy 61579f33912SNimrod Andy status = bdp->cbd_sc; 61679f33912SNimrod Andy status &= ~BD_ENET_TX_STATS; 61779f33912SNimrod Andy 61879f33912SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 61979f33912SNimrod Andy 62041ef84ceSFugang Duan if (((unsigned long) data) & fep->tx_align || 62179f33912SNimrod Andy id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) { 6224d494cdcSFugang Duan memcpy(txq->tx_bounce[index], data, size); 6234d494cdcSFugang Duan data = txq->tx_bounce[index]; 62479f33912SNimrod Andy 62579f33912SNimrod Andy if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) 62679f33912SNimrod Andy swap_buffer(data, size); 62779f33912SNimrod Andy } 62879f33912SNimrod Andy 629d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, data, size, DMA_TO_DEVICE); 630d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 63179f33912SNimrod Andy dev_kfree_skb_any(skb); 63279f33912SNimrod Andy if (net_ratelimit()) 63379f33912SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 63479f33912SNimrod Andy return NETDEV_TX_BUSY; 63579f33912SNimrod Andy } 63679f33912SNimrod Andy 637d6bf3143SRussell King bdp->cbd_datlen = size; 638d6bf3143SRussell King bdp->cbd_bufaddr = addr; 639d6bf3143SRussell King 64079f33912SNimrod Andy if (fep->bufdesc_ex) { 641befe8213SNimrod Andy if (id_entry->driver_data & FEC_QUIRK_HAS_AVB) 642befe8213SNimrod Andy estatus |= FEC_TX_BD_FTYPE(queue); 64379f33912SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 64479f33912SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 64579f33912SNimrod Andy ebdp->cbd_bdu = 0; 64679f33912SNimrod Andy ebdp->cbd_esc = estatus; 64779f33912SNimrod Andy } 64879f33912SNimrod Andy 64979f33912SNimrod Andy /* Handle the last BD specially */ 65079f33912SNimrod Andy if (last_tcp) 65179f33912SNimrod Andy status |= (BD_ENET_TX_LAST | BD_ENET_TX_TC); 65279f33912SNimrod Andy if (is_last) { 65379f33912SNimrod Andy status |= BD_ENET_TX_INTR; 65479f33912SNimrod Andy if (fep->bufdesc_ex) 65579f33912SNimrod Andy ebdp->cbd_esc |= BD_ENET_TX_INT; 65679f33912SNimrod Andy } 65779f33912SNimrod Andy 65879f33912SNimrod Andy bdp->cbd_sc = status; 65979f33912SNimrod Andy 66079f33912SNimrod Andy return 0; 66179f33912SNimrod Andy } 66279f33912SNimrod Andy 66379f33912SNimrod Andy static int 6644d494cdcSFugang Duan fec_enet_txq_put_hdr_tso(struct fec_enet_priv_tx_q *txq, 6654d494cdcSFugang Duan struct sk_buff *skb, struct net_device *ndev, 66679f33912SNimrod Andy struct bufdesc *bdp, int index) 66779f33912SNimrod Andy { 66879f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 66979f33912SNimrod Andy const struct platform_device_id *id_entry = 67079f33912SNimrod Andy platform_get_device_id(fep->pdev); 67179f33912SNimrod Andy int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); 67261cd2ebbSFabian Frederick struct bufdesc_ex *ebdp = container_of(bdp, struct bufdesc_ex, desc); 673befe8213SNimrod Andy unsigned short queue = skb_get_queue_mapping(skb); 67479f33912SNimrod Andy void *bufaddr; 67579f33912SNimrod Andy unsigned long dmabuf; 67679f33912SNimrod Andy unsigned short status; 67779f33912SNimrod Andy unsigned int estatus = 0; 67879f33912SNimrod Andy 67979f33912SNimrod Andy status = bdp->cbd_sc; 68079f33912SNimrod Andy status &= ~BD_ENET_TX_STATS; 68179f33912SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 68279f33912SNimrod Andy 6834d494cdcSFugang Duan bufaddr = txq->tso_hdrs + index * TSO_HEADER_SIZE; 6844d494cdcSFugang Duan dmabuf = txq->tso_hdrs_dma + index * TSO_HEADER_SIZE; 68541ef84ceSFugang Duan if (((unsigned long)bufaddr) & fep->tx_align || 68679f33912SNimrod Andy id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) { 6874d494cdcSFugang Duan memcpy(txq->tx_bounce[index], skb->data, hdr_len); 6884d494cdcSFugang Duan bufaddr = txq->tx_bounce[index]; 68979f33912SNimrod Andy 69079f33912SNimrod Andy if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) 69179f33912SNimrod Andy swap_buffer(bufaddr, hdr_len); 69279f33912SNimrod Andy 69379f33912SNimrod Andy dmabuf = dma_map_single(&fep->pdev->dev, bufaddr, 69479f33912SNimrod Andy hdr_len, DMA_TO_DEVICE); 69579f33912SNimrod Andy if (dma_mapping_error(&fep->pdev->dev, dmabuf)) { 69679f33912SNimrod Andy dev_kfree_skb_any(skb); 69779f33912SNimrod Andy if (net_ratelimit()) 69879f33912SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 69979f33912SNimrod Andy return NETDEV_TX_BUSY; 70079f33912SNimrod Andy } 70179f33912SNimrod Andy } 70279f33912SNimrod Andy 70379f33912SNimrod Andy bdp->cbd_bufaddr = dmabuf; 70479f33912SNimrod Andy bdp->cbd_datlen = hdr_len; 70579f33912SNimrod Andy 70679f33912SNimrod Andy if (fep->bufdesc_ex) { 707befe8213SNimrod Andy if (id_entry->driver_data & FEC_QUIRK_HAS_AVB) 708befe8213SNimrod Andy estatus |= FEC_TX_BD_FTYPE(queue); 70979f33912SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 71079f33912SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 71179f33912SNimrod Andy ebdp->cbd_bdu = 0; 71279f33912SNimrod Andy ebdp->cbd_esc = estatus; 71379f33912SNimrod Andy } 71479f33912SNimrod Andy 71579f33912SNimrod Andy bdp->cbd_sc = status; 71679f33912SNimrod Andy 71779f33912SNimrod Andy return 0; 71879f33912SNimrod Andy } 71979f33912SNimrod Andy 7204d494cdcSFugang Duan static int fec_enet_txq_submit_tso(struct fec_enet_priv_tx_q *txq, 7214d494cdcSFugang Duan struct sk_buff *skb, 7224d494cdcSFugang Duan struct net_device *ndev) 72379f33912SNimrod Andy { 72479f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 72579f33912SNimrod Andy int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); 72679f33912SNimrod Andy int total_len, data_left; 7274d494cdcSFugang Duan struct bufdesc *bdp = txq->cur_tx; 7284d494cdcSFugang Duan unsigned short queue = skb_get_queue_mapping(skb); 72979f33912SNimrod Andy struct tso_t tso; 73079f33912SNimrod Andy unsigned int index = 0; 73179f33912SNimrod Andy int ret; 73237d6017bSFugang Duan const struct platform_device_id *id_entry = 73337d6017bSFugang Duan platform_get_device_id(fep->pdev); 73479f33912SNimrod Andy 7354d494cdcSFugang Duan if (tso_count_descs(skb) >= fec_enet_get_free_txdesc_num(fep, txq)) { 73679f33912SNimrod Andy dev_kfree_skb_any(skb); 73779f33912SNimrod Andy if (net_ratelimit()) 73879f33912SNimrod Andy netdev_err(ndev, "NOT enough BD for TSO!\n"); 73979f33912SNimrod Andy return NETDEV_TX_OK; 74079f33912SNimrod Andy } 74179f33912SNimrod Andy 74279f33912SNimrod Andy /* Protocol checksum off-load for TCP and UDP. */ 74379f33912SNimrod Andy if (fec_enet_clear_csum(skb, ndev)) { 74479f33912SNimrod Andy dev_kfree_skb_any(skb); 74579f33912SNimrod Andy return NETDEV_TX_OK; 74679f33912SNimrod Andy } 74779f33912SNimrod Andy 74879f33912SNimrod Andy /* Initialize the TSO handler, and prepare the first payload */ 74979f33912SNimrod Andy tso_start(skb, &tso); 75079f33912SNimrod Andy 75179f33912SNimrod Andy total_len = skb->len - hdr_len; 75279f33912SNimrod Andy while (total_len > 0) { 75379f33912SNimrod Andy char *hdr; 75479f33912SNimrod Andy 7554d494cdcSFugang Duan index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep); 75679f33912SNimrod Andy data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len); 75779f33912SNimrod Andy total_len -= data_left; 75879f33912SNimrod Andy 75979f33912SNimrod Andy /* prepare packet headers: MAC + IP + TCP */ 7604d494cdcSFugang Duan hdr = txq->tso_hdrs + index * TSO_HEADER_SIZE; 76179f33912SNimrod Andy tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0); 7624d494cdcSFugang Duan ret = fec_enet_txq_put_hdr_tso(txq, skb, ndev, bdp, index); 76379f33912SNimrod Andy if (ret) 76479f33912SNimrod Andy goto err_release; 76579f33912SNimrod Andy 76679f33912SNimrod Andy while (data_left > 0) { 76779f33912SNimrod Andy int size; 76879f33912SNimrod Andy 76979f33912SNimrod Andy size = min_t(int, tso.size, data_left); 7704d494cdcSFugang Duan bdp = fec_enet_get_nextdesc(bdp, fep, queue); 7714d494cdcSFugang Duan index = fec_enet_get_bd_index(txq->tx_bd_base, 7724d494cdcSFugang Duan bdp, fep); 7734d494cdcSFugang Duan ret = fec_enet_txq_put_data_tso(txq, skb, ndev, 7744d494cdcSFugang Duan bdp, index, 7754d494cdcSFugang Duan tso.data, size, 7764d494cdcSFugang Duan size == data_left, 77779f33912SNimrod Andy total_len == 0); 77879f33912SNimrod Andy if (ret) 77979f33912SNimrod Andy goto err_release; 78079f33912SNimrod Andy 78179f33912SNimrod Andy data_left -= size; 78279f33912SNimrod Andy tso_build_data(skb, &tso, size); 78379f33912SNimrod Andy } 78479f33912SNimrod Andy 7854d494cdcSFugang Duan bdp = fec_enet_get_nextdesc(bdp, fep, queue); 78679f33912SNimrod Andy } 78779f33912SNimrod Andy 78879f33912SNimrod Andy /* Save skb pointer */ 7894d494cdcSFugang Duan txq->tx_skbuff[index] = skb; 79079f33912SNimrod Andy 79179f33912SNimrod Andy skb_tx_timestamp(skb); 7924d494cdcSFugang Duan txq->cur_tx = bdp; 79379f33912SNimrod Andy 79479f33912SNimrod Andy /* Trigger transmission start */ 79537d6017bSFugang Duan if (!(id_entry->driver_data & FEC_QUIRK_ERR007885) || 79637d6017bSFugang Duan !readl(fep->hwp + FEC_X_DES_ACTIVE(queue)) || 79737d6017bSFugang Duan !readl(fep->hwp + FEC_X_DES_ACTIVE(queue)) || 79837d6017bSFugang Duan !readl(fep->hwp + FEC_X_DES_ACTIVE(queue)) || 79937d6017bSFugang Duan !readl(fep->hwp + FEC_X_DES_ACTIVE(queue))) 8004d494cdcSFugang Duan writel(0, fep->hwp + FEC_X_DES_ACTIVE(queue)); 80179f33912SNimrod Andy 80279f33912SNimrod Andy return 0; 80379f33912SNimrod Andy 80479f33912SNimrod Andy err_release: 80579f33912SNimrod Andy /* TODO: Release all used data descriptors for TSO */ 80679f33912SNimrod Andy return ret; 80779f33912SNimrod Andy } 80879f33912SNimrod Andy 80961a4427bSNimrod Andy static netdev_tx_t 81061a4427bSNimrod Andy fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) 81161a4427bSNimrod Andy { 81261a4427bSNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 8136e909283SNimrod Andy int entries_free; 8144d494cdcSFugang Duan unsigned short queue; 8154d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 8164d494cdcSFugang Duan struct netdev_queue *nq; 81761a4427bSNimrod Andy int ret; 81861a4427bSNimrod Andy 8194d494cdcSFugang Duan queue = skb_get_queue_mapping(skb); 8204d494cdcSFugang Duan txq = fep->tx_queue[queue]; 8214d494cdcSFugang Duan nq = netdev_get_tx_queue(ndev, queue); 8224d494cdcSFugang Duan 82379f33912SNimrod Andy if (skb_is_gso(skb)) 8244d494cdcSFugang Duan ret = fec_enet_txq_submit_tso(txq, skb, ndev); 82579f33912SNimrod Andy else 8264d494cdcSFugang Duan ret = fec_enet_txq_submit_skb(txq, skb, ndev); 8276e909283SNimrod Andy if (ret) 8286e909283SNimrod Andy return ret; 82961a4427bSNimrod Andy 8304d494cdcSFugang Duan entries_free = fec_enet_get_free_txdesc_num(fep, txq); 8314d494cdcSFugang Duan if (entries_free <= txq->tx_stop_threshold) 8324d494cdcSFugang Duan netif_tx_stop_queue(nq); 83361a4427bSNimrod Andy 83461a4427bSNimrod Andy return NETDEV_TX_OK; 83561a4427bSNimrod Andy } 83661a4427bSNimrod Andy 837a210576cSDavid S. Miller /* Init RX & TX buffer descriptors 838a210576cSDavid S. Miller */ 839a210576cSDavid S. Miller static void fec_enet_bd_init(struct net_device *dev) 840a210576cSDavid S. Miller { 841a210576cSDavid S. Miller struct fec_enet_private *fep = netdev_priv(dev); 8424d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 8434d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 844a210576cSDavid S. Miller struct bufdesc *bdp; 845a210576cSDavid S. Miller unsigned int i; 84659d0f746SFrank Li unsigned int q; 847a210576cSDavid S. Miller 84859d0f746SFrank Li for (q = 0; q < fep->num_rx_queues; q++) { 849a210576cSDavid S. Miller /* Initialize the receive buffer descriptors. */ 85059d0f746SFrank Li rxq = fep->rx_queue[q]; 8514d494cdcSFugang Duan bdp = rxq->rx_bd_base; 8524d494cdcSFugang Duan 8534d494cdcSFugang Duan for (i = 0; i < rxq->rx_ring_size; i++) { 854a210576cSDavid S. Miller 855a210576cSDavid S. Miller /* Initialize the BD for every fragment in the page. */ 856a210576cSDavid S. Miller if (bdp->cbd_bufaddr) 857a210576cSDavid S. Miller bdp->cbd_sc = BD_ENET_RX_EMPTY; 858a210576cSDavid S. Miller else 859a210576cSDavid S. Miller bdp->cbd_sc = 0; 86059d0f746SFrank Li bdp = fec_enet_get_nextdesc(bdp, fep, q); 861a210576cSDavid S. Miller } 862a210576cSDavid S. Miller 863a210576cSDavid S. Miller /* Set the last buffer to wrap */ 86459d0f746SFrank Li bdp = fec_enet_get_prevdesc(bdp, fep, q); 865a210576cSDavid S. Miller bdp->cbd_sc |= BD_SC_WRAP; 866a210576cSDavid S. Miller 8674d494cdcSFugang Duan rxq->cur_rx = rxq->rx_bd_base; 86859d0f746SFrank Li } 869a210576cSDavid S. Miller 87059d0f746SFrank Li for (q = 0; q < fep->num_tx_queues; q++) { 871a210576cSDavid S. Miller /* ...and the same for transmit */ 87259d0f746SFrank Li txq = fep->tx_queue[q]; 8734d494cdcSFugang Duan bdp = txq->tx_bd_base; 8744d494cdcSFugang Duan txq->cur_tx = bdp; 875a210576cSDavid S. Miller 8764d494cdcSFugang Duan for (i = 0; i < txq->tx_ring_size; i++) { 877a210576cSDavid S. Miller /* Initialize the BD for every fragment in the page. */ 878a210576cSDavid S. Miller bdp->cbd_sc = 0; 8794d494cdcSFugang Duan if (txq->tx_skbuff[i]) { 8804d494cdcSFugang Duan dev_kfree_skb_any(txq->tx_skbuff[i]); 8814d494cdcSFugang Duan txq->tx_skbuff[i] = NULL; 882a210576cSDavid S. Miller } 883a210576cSDavid S. Miller bdp->cbd_bufaddr = 0; 88459d0f746SFrank Li bdp = fec_enet_get_nextdesc(bdp, fep, q); 885a210576cSDavid S. Miller } 886a210576cSDavid S. Miller 887a210576cSDavid S. Miller /* Set the last buffer to wrap */ 88859d0f746SFrank Li bdp = fec_enet_get_prevdesc(bdp, fep, q); 889a210576cSDavid S. Miller bdp->cbd_sc |= BD_SC_WRAP; 8904d494cdcSFugang Duan txq->dirty_tx = bdp; 891a210576cSDavid S. Miller } 89259d0f746SFrank Li } 89359d0f746SFrank Li 894ce99d0d3SFrank Li static void fec_enet_active_rxring(struct net_device *ndev) 895ce99d0d3SFrank Li { 896ce99d0d3SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 897ce99d0d3SFrank Li int i; 898ce99d0d3SFrank Li 899ce99d0d3SFrank Li for (i = 0; i < fep->num_rx_queues; i++) 900ce99d0d3SFrank Li writel(0, fep->hwp + FEC_R_DES_ACTIVE(i)); 901ce99d0d3SFrank Li } 902ce99d0d3SFrank Li 90359d0f746SFrank Li static void fec_enet_enable_ring(struct net_device *ndev) 90459d0f746SFrank Li { 90559d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 90659d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 90759d0f746SFrank Li struct fec_enet_priv_rx_q *rxq; 90859d0f746SFrank Li int i; 90959d0f746SFrank Li 91059d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) { 91159d0f746SFrank Li rxq = fep->rx_queue[i]; 91259d0f746SFrank Li writel(rxq->bd_dma, fep->hwp + FEC_R_DES_START(i)); 91359d0f746SFrank Li 91459d0f746SFrank Li /* enable DMA1/2 */ 91559d0f746SFrank Li if (i) 91659d0f746SFrank Li writel(RCMR_MATCHEN | RCMR_CMP(i), 91759d0f746SFrank Li fep->hwp + FEC_RCMR(i)); 91859d0f746SFrank Li } 91959d0f746SFrank Li 92059d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 92159d0f746SFrank Li txq = fep->tx_queue[i]; 92259d0f746SFrank Li writel(txq->bd_dma, fep->hwp + FEC_X_DES_START(i)); 92359d0f746SFrank Li 92459d0f746SFrank Li /* enable DMA1/2 */ 92559d0f746SFrank Li if (i) 92659d0f746SFrank Li writel(DMA_CLASS_EN | IDLE_SLOPE(i), 92759d0f746SFrank Li fep->hwp + FEC_DMA_CFG(i)); 92859d0f746SFrank Li } 92959d0f746SFrank Li } 93059d0f746SFrank Li 93159d0f746SFrank Li static void fec_enet_reset_skb(struct net_device *ndev) 93259d0f746SFrank Li { 93359d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 93459d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 93559d0f746SFrank Li int i, j; 93659d0f746SFrank Li 93759d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 93859d0f746SFrank Li txq = fep->tx_queue[i]; 93959d0f746SFrank Li 94059d0f746SFrank Li for (j = 0; j < txq->tx_ring_size; j++) { 94159d0f746SFrank Li if (txq->tx_skbuff[j]) { 94259d0f746SFrank Li dev_kfree_skb_any(txq->tx_skbuff[j]); 94359d0f746SFrank Li txq->tx_skbuff[j] = NULL; 94459d0f746SFrank Li } 94559d0f746SFrank Li } 94659d0f746SFrank Li } 94759d0f746SFrank Li } 948a210576cSDavid S. Miller 949dbc64a8eSRussell King /* 950dbc64a8eSRussell King * This function is called to start or restart the FEC during a link 951dbc64a8eSRussell King * change, transmit timeout, or to reconfigure the FEC. The network 952dbc64a8eSRussell King * packet processing for this device must be stopped before this call. 953793fc096SFrank Li */ 954793fc096SFrank Li static void 955ef83337dSRussell King fec_restart(struct net_device *ndev) 956793fc096SFrank Li { 957793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 958793fc096SFrank Li const struct platform_device_id *id_entry = 959793fc096SFrank Li platform_get_device_id(fep->pdev); 9604c09eed9SJim Baxter u32 val; 961793fc096SFrank Li u32 temp_mac[2]; 962793fc096SFrank Li u32 rcntl = OPT_FRAME_SIZE | 0x04; 963793fc096SFrank Li u32 ecntl = 0x2; /* ETHEREN */ 964793fc096SFrank Li 965106c314cSFugang Duan /* Whack a reset. We should wait for this. 966106c314cSFugang Duan * For i.MX6SX SOC, enet use AXI bus, we use disable MAC 967106c314cSFugang Duan * instead of reset MAC itself. 968106c314cSFugang Duan */ 969106c314cSFugang Duan if (id_entry && id_entry->driver_data & FEC_QUIRK_HAS_AVB) { 970106c314cSFugang Duan writel(0, fep->hwp + FEC_ECNTRL); 971106c314cSFugang Duan } else { 972793fc096SFrank Li writel(1, fep->hwp + FEC_ECNTRL); 973793fc096SFrank Li udelay(10); 974106c314cSFugang Duan } 975793fc096SFrank Li 976793fc096SFrank Li /* 977793fc096SFrank Li * enet-mac reset will reset mac address registers too, 978793fc096SFrank Li * so need to reconfigure it. 979793fc096SFrank Li */ 980793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) { 981793fc096SFrank Li memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN); 982793fc096SFrank Li writel(cpu_to_be32(temp_mac[0]), fep->hwp + FEC_ADDR_LOW); 983793fc096SFrank Li writel(cpu_to_be32(temp_mac[1]), fep->hwp + FEC_ADDR_HIGH); 984793fc096SFrank Li } 985793fc096SFrank Li 986793fc096SFrank Li /* Clear any outstanding interrupt. */ 987793fc096SFrank Li writel(0xffc00000, fep->hwp + FEC_IEVENT); 988793fc096SFrank Li 989793fc096SFrank Li /* Set maximum receive buffer size. */ 990793fc096SFrank Li writel(PKT_MAXBLR_SIZE, fep->hwp + FEC_R_BUFF_SIZE); 991793fc096SFrank Li 992a210576cSDavid S. Miller fec_enet_bd_init(ndev); 993a210576cSDavid S. Miller 99459d0f746SFrank Li fec_enet_enable_ring(ndev); 995793fc096SFrank Li 99659d0f746SFrank Li /* Reset tx SKB buffers. */ 99759d0f746SFrank Li fec_enet_reset_skb(ndev); 998793fc096SFrank Li 999793fc096SFrank Li /* Enable MII mode */ 1000ef83337dSRussell King if (fep->full_duplex == DUPLEX_FULL) { 1001793fc096SFrank Li /* FD enable */ 1002793fc096SFrank Li writel(0x04, fep->hwp + FEC_X_CNTRL); 1003793fc096SFrank Li } else { 1004793fc096SFrank Li /* No Rcv on Xmit */ 1005793fc096SFrank Li rcntl |= 0x02; 1006793fc096SFrank Li writel(0x0, fep->hwp + FEC_X_CNTRL); 1007793fc096SFrank Li } 1008793fc096SFrank Li 1009793fc096SFrank Li /* Set MII speed */ 1010793fc096SFrank Li writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 1011793fc096SFrank Li 1012d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 10134c09eed9SJim Baxter /* set RX checksum */ 10144c09eed9SJim Baxter val = readl(fep->hwp + FEC_RACC); 10154c09eed9SJim Baxter if (fep->csum_flags & FLAG_RX_CSUM_ENABLED) 10164c09eed9SJim Baxter val |= FEC_RACC_OPTIONS; 10174c09eed9SJim Baxter else 10184c09eed9SJim Baxter val &= ~FEC_RACC_OPTIONS; 10194c09eed9SJim Baxter writel(val, fep->hwp + FEC_RACC); 1020d1391930SGuenter Roeck #endif 10214c09eed9SJim Baxter 1022793fc096SFrank Li /* 1023793fc096SFrank Li * The phy interface and speed need to get configured 1024793fc096SFrank Li * differently on enet-mac. 1025793fc096SFrank Li */ 1026793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) { 1027793fc096SFrank Li /* Enable flow control and length check */ 1028793fc096SFrank Li rcntl |= 0x40000000 | 0x00000020; 1029793fc096SFrank Li 1030793fc096SFrank Li /* RGMII, RMII or MII */ 1031793fc096SFrank Li if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII) 1032793fc096SFrank Li rcntl |= (1 << 6); 1033793fc096SFrank Li else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) 1034793fc096SFrank Li rcntl |= (1 << 8); 1035793fc096SFrank Li else 1036793fc096SFrank Li rcntl &= ~(1 << 8); 1037793fc096SFrank Li 1038793fc096SFrank Li /* 1G, 100M or 10M */ 1039793fc096SFrank Li if (fep->phy_dev) { 1040793fc096SFrank Li if (fep->phy_dev->speed == SPEED_1000) 1041793fc096SFrank Li ecntl |= (1 << 5); 1042793fc096SFrank Li else if (fep->phy_dev->speed == SPEED_100) 1043793fc096SFrank Li rcntl &= ~(1 << 9); 1044793fc096SFrank Li else 1045793fc096SFrank Li rcntl |= (1 << 9); 1046793fc096SFrank Li } 1047793fc096SFrank Li } else { 1048793fc096SFrank Li #ifdef FEC_MIIGSK_ENR 1049793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_USE_GASKET) { 1050793fc096SFrank Li u32 cfgr; 1051793fc096SFrank Li /* disable the gasket and wait */ 1052793fc096SFrank Li writel(0, fep->hwp + FEC_MIIGSK_ENR); 1053793fc096SFrank Li while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4) 1054793fc096SFrank Li udelay(1); 1055793fc096SFrank Li 1056793fc096SFrank Li /* 1057793fc096SFrank Li * configure the gasket: 1058793fc096SFrank Li * RMII, 50 MHz, no loopback, no echo 1059793fc096SFrank Li * MII, 25 MHz, no loopback, no echo 1060793fc096SFrank Li */ 1061793fc096SFrank Li cfgr = (fep->phy_interface == PHY_INTERFACE_MODE_RMII) 1062793fc096SFrank Li ? BM_MIIGSK_CFGR_RMII : BM_MIIGSK_CFGR_MII; 1063793fc096SFrank Li if (fep->phy_dev && fep->phy_dev->speed == SPEED_10) 1064793fc096SFrank Li cfgr |= BM_MIIGSK_CFGR_FRCONT_10M; 1065793fc096SFrank Li writel(cfgr, fep->hwp + FEC_MIIGSK_CFGR); 1066793fc096SFrank Li 1067793fc096SFrank Li /* re-enable the gasket */ 1068793fc096SFrank Li writel(2, fep->hwp + FEC_MIIGSK_ENR); 1069793fc096SFrank Li } 1070793fc096SFrank Li #endif 1071793fc096SFrank Li } 1072793fc096SFrank Li 1073d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 1074793fc096SFrank Li /* enable pause frame*/ 1075793fc096SFrank Li if ((fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) || 1076793fc096SFrank Li ((fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) && 1077793fc096SFrank Li fep->phy_dev && fep->phy_dev->pause)) { 1078793fc096SFrank Li rcntl |= FEC_ENET_FCE; 1079793fc096SFrank Li 10804c09eed9SJim Baxter /* set FIFO threshold parameter to reduce overrun */ 1081793fc096SFrank Li writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM); 1082793fc096SFrank Li writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL); 1083793fc096SFrank Li writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM); 1084793fc096SFrank Li writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL); 1085793fc096SFrank Li 1086793fc096SFrank Li /* OPD */ 1087793fc096SFrank Li writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD); 1088793fc096SFrank Li } else { 1089793fc096SFrank Li rcntl &= ~FEC_ENET_FCE; 1090793fc096SFrank Li } 1091d1391930SGuenter Roeck #endif /* !defined(CONFIG_M5272) */ 1092793fc096SFrank Li 1093793fc096SFrank Li writel(rcntl, fep->hwp + FEC_R_CNTRL); 1094793fc096SFrank Li 109584fe6182SStefan Wahren /* Setup multicast filter. */ 109684fe6182SStefan Wahren set_multicast_list(ndev); 109784fe6182SStefan Wahren #ifndef CONFIG_M5272 109884fe6182SStefan Wahren writel(0, fep->hwp + FEC_HASH_TABLE_HIGH); 109984fe6182SStefan Wahren writel(0, fep->hwp + FEC_HASH_TABLE_LOW); 110084fe6182SStefan Wahren #endif 110184fe6182SStefan Wahren 1102793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) { 1103793fc096SFrank Li /* enable ENET endian swap */ 1104793fc096SFrank Li ecntl |= (1 << 8); 1105793fc096SFrank Li /* enable ENET store and forward mode */ 1106793fc096SFrank Li writel(1 << 8, fep->hwp + FEC_X_WMRK); 1107793fc096SFrank Li } 1108793fc096SFrank Li 1109793fc096SFrank Li if (fep->bufdesc_ex) 1110793fc096SFrank Li ecntl |= (1 << 4); 1111793fc096SFrank Li 111238ae92dcSChris Healy #ifndef CONFIG_M5272 1113b9eef55cSJim Baxter /* Enable the MIB statistic event counters */ 1114b9eef55cSJim Baxter writel(0 << 31, fep->hwp + FEC_MIB_CTRLSTAT); 111538ae92dcSChris Healy #endif 111638ae92dcSChris Healy 1117793fc096SFrank Li /* And last, enable the transmit and receive processing */ 1118793fc096SFrank Li writel(ecntl, fep->hwp + FEC_ECNTRL); 1119ce99d0d3SFrank Li fec_enet_active_rxring(ndev); 1120793fc096SFrank Li 1121793fc096SFrank Li if (fep->bufdesc_ex) 1122793fc096SFrank Li fec_ptp_start_cyclecounter(ndev); 1123793fc096SFrank Li 1124793fc096SFrank Li /* Enable interrupts we wish to service */ 1125793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 1126d851b47bSFugang Duan 1127d851b47bSFugang Duan /* Init the interrupt coalescing */ 1128d851b47bSFugang Duan fec_enet_itr_coal_init(ndev); 1129d851b47bSFugang Duan 1130793fc096SFrank Li } 1131793fc096SFrank Li 1132793fc096SFrank Li static void 1133793fc096SFrank Li fec_stop(struct net_device *ndev) 1134793fc096SFrank Li { 1135793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1136793fc096SFrank Li const struct platform_device_id *id_entry = 1137793fc096SFrank Li platform_get_device_id(fep->pdev); 1138793fc096SFrank Li u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8); 1139793fc096SFrank Li 1140793fc096SFrank Li /* We cannot expect a graceful transmit stop without link !!! */ 1141793fc096SFrank Li if (fep->link) { 1142793fc096SFrank Li writel(1, fep->hwp + FEC_X_CNTRL); /* Graceful transmit stop */ 1143793fc096SFrank Li udelay(10); 1144793fc096SFrank Li if (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_GRA)) 114531b7720cSJoe Perches netdev_err(ndev, "Graceful transmit stop did not complete!\n"); 1146793fc096SFrank Li } 1147793fc096SFrank Li 1148106c314cSFugang Duan /* Whack a reset. We should wait for this. 1149106c314cSFugang Duan * For i.MX6SX SOC, enet use AXI bus, we use disable MAC 1150106c314cSFugang Duan * instead of reset MAC itself. 1151106c314cSFugang Duan */ 1152106c314cSFugang Duan if (id_entry && id_entry->driver_data & FEC_QUIRK_HAS_AVB) { 1153106c314cSFugang Duan writel(0, fep->hwp + FEC_ECNTRL); 1154106c314cSFugang Duan } else { 1155793fc096SFrank Li writel(1, fep->hwp + FEC_ECNTRL); 1156793fc096SFrank Li udelay(10); 1157106c314cSFugang Duan } 1158793fc096SFrank Li writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 1159793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 1160793fc096SFrank Li 1161793fc096SFrank Li /* We have to keep ENET enabled to have MII interrupt stay working */ 1162793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) { 1163793fc096SFrank Li writel(2, fep->hwp + FEC_ECNTRL); 1164793fc096SFrank Li writel(rmii_mode, fep->hwp + FEC_R_CNTRL); 1165793fc096SFrank Li } 1166793fc096SFrank Li } 1167793fc096SFrank Li 1168793fc096SFrank Li 1169793fc096SFrank Li static void 1170793fc096SFrank Li fec_timeout(struct net_device *ndev) 1171793fc096SFrank Li { 1172793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1173793fc096SFrank Li 1174344756f6SRussell King fec_dump(ndev); 1175344756f6SRussell King 1176793fc096SFrank Li ndev->stats.tx_errors++; 1177793fc096SFrank Li 117836cdc743SRussell King schedule_work(&fep->tx_timeout_work); 117954309fa6SFrank Li } 118054309fa6SFrank Li 118136cdc743SRussell King static void fec_enet_timeout_work(struct work_struct *work) 118254309fa6SFrank Li { 118354309fa6SFrank Li struct fec_enet_private *fep = 118436cdc743SRussell King container_of(work, struct fec_enet_private, tx_timeout_work); 11858ce5624fSRussell King struct net_device *ndev = fep->netdev; 118654309fa6SFrank Li 1187da1774e5SRussell King rtnl_lock(); 11888ce5624fSRussell King if (netif_device_present(ndev) || netif_running(ndev)) { 1189dbc64a8eSRussell King napi_disable(&fep->napi); 1190dbc64a8eSRussell King netif_tx_lock_bh(ndev); 1191ef83337dSRussell King fec_restart(ndev); 11928ce5624fSRussell King netif_wake_queue(ndev); 11936af42d42SRussell King netif_tx_unlock_bh(ndev); 1194dbc64a8eSRussell King napi_enable(&fep->napi); 11958ce5624fSRussell King } 1196da1774e5SRussell King rtnl_unlock(); 119754309fa6SFrank Li } 1198793fc096SFrank Li 1199793fc096SFrank Li static void 1200bfd4ecddSRussell King fec_enet_hwtstamp(struct fec_enet_private *fep, unsigned ts, 1201bfd4ecddSRussell King struct skb_shared_hwtstamps *hwtstamps) 1202bfd4ecddSRussell King { 1203bfd4ecddSRussell King unsigned long flags; 1204bfd4ecddSRussell King u64 ns; 1205bfd4ecddSRussell King 1206bfd4ecddSRussell King spin_lock_irqsave(&fep->tmreg_lock, flags); 1207bfd4ecddSRussell King ns = timecounter_cyc2time(&fep->tc, ts); 1208bfd4ecddSRussell King spin_unlock_irqrestore(&fep->tmreg_lock, flags); 1209bfd4ecddSRussell King 1210bfd4ecddSRussell King memset(hwtstamps, 0, sizeof(*hwtstamps)); 1211bfd4ecddSRussell King hwtstamps->hwtstamp = ns_to_ktime(ns); 1212bfd4ecddSRussell King } 1213bfd4ecddSRussell King 1214bfd4ecddSRussell King static void 12154d494cdcSFugang Duan fec_enet_tx_queue(struct net_device *ndev, u16 queue_id) 1216793fc096SFrank Li { 1217793fc096SFrank Li struct fec_enet_private *fep; 1218793fc096SFrank Li struct bufdesc *bdp; 1219793fc096SFrank Li unsigned short status; 1220793fc096SFrank Li struct sk_buff *skb; 12214d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 12224d494cdcSFugang Duan struct netdev_queue *nq; 1223793fc096SFrank Li int index = 0; 122479f33912SNimrod Andy int entries_free; 1225793fc096SFrank Li 1226793fc096SFrank Li fep = netdev_priv(ndev); 12274d494cdcSFugang Duan 12284d494cdcSFugang Duan queue_id = FEC_ENET_GET_QUQUE(queue_id); 12294d494cdcSFugang Duan 12304d494cdcSFugang Duan txq = fep->tx_queue[queue_id]; 12314d494cdcSFugang Duan /* get next bdp of dirty_tx */ 12324d494cdcSFugang Duan nq = netdev_get_tx_queue(ndev, queue_id); 12334d494cdcSFugang Duan bdp = txq->dirty_tx; 1234793fc096SFrank Li 1235793fc096SFrank Li /* get next bdp of dirty_tx */ 12364d494cdcSFugang Duan bdp = fec_enet_get_nextdesc(bdp, fep, queue_id); 1237793fc096SFrank Li 1238793fc096SFrank Li while (((status = bdp->cbd_sc) & BD_ENET_TX_READY) == 0) { 1239793fc096SFrank Li 1240793fc096SFrank Li /* current queue is empty */ 12414d494cdcSFugang Duan if (bdp == txq->cur_tx) 1242793fc096SFrank Li break; 1243793fc096SFrank Li 12444d494cdcSFugang Duan index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep); 1245793fc096SFrank Li 12464d494cdcSFugang Duan skb = txq->tx_skbuff[index]; 12474d494cdcSFugang Duan txq->tx_skbuff[index] = NULL; 12484d494cdcSFugang Duan if (!IS_TSO_HEADER(txq, bdp->cbd_bufaddr)) 124979f33912SNimrod Andy dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, 125079f33912SNimrod Andy bdp->cbd_datlen, DMA_TO_DEVICE); 12512488a54eSSebastian Siewior bdp->cbd_bufaddr = 0; 12526e909283SNimrod Andy if (!skb) { 12534d494cdcSFugang Duan bdp = fec_enet_get_nextdesc(bdp, fep, queue_id); 12546e909283SNimrod Andy continue; 12556e909283SNimrod Andy } 1256793fc096SFrank Li 1257793fc096SFrank Li /* Check for errors. */ 1258793fc096SFrank Li if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC | 1259793fc096SFrank Li BD_ENET_TX_RL | BD_ENET_TX_UN | 1260793fc096SFrank Li BD_ENET_TX_CSL)) { 1261793fc096SFrank Li ndev->stats.tx_errors++; 1262793fc096SFrank Li if (status & BD_ENET_TX_HB) /* No heartbeat */ 1263793fc096SFrank Li ndev->stats.tx_heartbeat_errors++; 1264793fc096SFrank Li if (status & BD_ENET_TX_LC) /* Late collision */ 1265793fc096SFrank Li ndev->stats.tx_window_errors++; 1266793fc096SFrank Li if (status & BD_ENET_TX_RL) /* Retrans limit */ 1267793fc096SFrank Li ndev->stats.tx_aborted_errors++; 1268793fc096SFrank Li if (status & BD_ENET_TX_UN) /* Underrun */ 1269793fc096SFrank Li ndev->stats.tx_fifo_errors++; 1270793fc096SFrank Li if (status & BD_ENET_TX_CSL) /* Carrier lost */ 1271793fc096SFrank Li ndev->stats.tx_carrier_errors++; 1272793fc096SFrank Li } else { 1273793fc096SFrank Li ndev->stats.tx_packets++; 12746e909283SNimrod Andy ndev->stats.tx_bytes += skb->len; 1275793fc096SFrank Li } 1276793fc096SFrank Li 1277793fc096SFrank Li if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) && 1278793fc096SFrank Li fep->bufdesc_ex) { 1279793fc096SFrank Li struct skb_shared_hwtstamps shhwtstamps; 1280793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 1281793fc096SFrank Li 1282bfd4ecddSRussell King fec_enet_hwtstamp(fep, ebdp->ts, &shhwtstamps); 1283793fc096SFrank Li skb_tstamp_tx(skb, &shhwtstamps); 1284793fc096SFrank Li } 1285793fc096SFrank Li 1286793fc096SFrank Li /* Deferred means some collisions occurred during transmit, 1287793fc096SFrank Li * but we eventually sent the packet OK. 1288793fc096SFrank Li */ 1289793fc096SFrank Li if (status & BD_ENET_TX_DEF) 1290793fc096SFrank Li ndev->stats.collisions++; 1291793fc096SFrank Li 1292793fc096SFrank Li /* Free the sk buffer associated with this last transmit */ 1293793fc096SFrank Li dev_kfree_skb_any(skb); 1294793fc096SFrank Li 12954d494cdcSFugang Duan txq->dirty_tx = bdp; 1296793fc096SFrank Li 1297793fc096SFrank Li /* Update pointer to next buffer descriptor to be transmitted */ 12984d494cdcSFugang Duan bdp = fec_enet_get_nextdesc(bdp, fep, queue_id); 1299793fc096SFrank Li 1300793fc096SFrank Li /* Since we have freed up a buffer, the ring is no longer full 1301793fc096SFrank Li */ 130279f33912SNimrod Andy if (netif_queue_stopped(ndev)) { 13034d494cdcSFugang Duan entries_free = fec_enet_get_free_txdesc_num(fep, txq); 13044d494cdcSFugang Duan if (entries_free >= txq->tx_wake_threshold) 13054d494cdcSFugang Duan netif_tx_wake_queue(nq); 1306793fc096SFrank Li } 130779f33912SNimrod Andy } 1308ccea2968SRussell King 1309ccea2968SRussell King /* ERR006538: Keep the transmitter going */ 13104d494cdcSFugang Duan if (bdp != txq->cur_tx && 13114d494cdcSFugang Duan readl(fep->hwp + FEC_X_DES_ACTIVE(queue_id)) == 0) 13124d494cdcSFugang Duan writel(0, fep->hwp + FEC_X_DES_ACTIVE(queue_id)); 13134d494cdcSFugang Duan } 13144d494cdcSFugang Duan 13154d494cdcSFugang Duan static void 13164d494cdcSFugang Duan fec_enet_tx(struct net_device *ndev) 13174d494cdcSFugang Duan { 13184d494cdcSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 13194d494cdcSFugang Duan u16 queue_id; 13204d494cdcSFugang Duan /* First process class A queue, then Class B and Best Effort queue */ 13214d494cdcSFugang Duan for_each_set_bit(queue_id, &fep->work_tx, FEC_ENET_MAX_TX_QS) { 13224d494cdcSFugang Duan clear_bit(queue_id, &fep->work_tx); 13234d494cdcSFugang Duan fec_enet_tx_queue(ndev, queue_id); 13244d494cdcSFugang Duan } 13254d494cdcSFugang Duan return; 1326793fc096SFrank Li } 1327793fc096SFrank Li 13281b7bde6dSNimrod Andy static int 13291b7bde6dSNimrod Andy fec_enet_new_rxbdp(struct net_device *ndev, struct bufdesc *bdp, struct sk_buff *skb) 13301b7bde6dSNimrod Andy { 13311b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 13321b7bde6dSNimrod Andy int off; 13331b7bde6dSNimrod Andy 13341b7bde6dSNimrod Andy off = ((unsigned long)skb->data) & fep->rx_align; 13351b7bde6dSNimrod Andy if (off) 13361b7bde6dSNimrod Andy skb_reserve(skb, fep->rx_align + 1 - off); 13371b7bde6dSNimrod Andy 13381b7bde6dSNimrod Andy bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, skb->data, 13391b7bde6dSNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 13401b7bde6dSNimrod Andy DMA_FROM_DEVICE); 13411b7bde6dSNimrod Andy if (dma_mapping_error(&fep->pdev->dev, bdp->cbd_bufaddr)) { 13421b7bde6dSNimrod Andy if (net_ratelimit()) 13431b7bde6dSNimrod Andy netdev_err(ndev, "Rx DMA memory map failed\n"); 13441b7bde6dSNimrod Andy return -ENOMEM; 13451b7bde6dSNimrod Andy } 13461b7bde6dSNimrod Andy 13471b7bde6dSNimrod Andy return 0; 13481b7bde6dSNimrod Andy } 13491b7bde6dSNimrod Andy 13501b7bde6dSNimrod Andy static bool fec_enet_copybreak(struct net_device *ndev, struct sk_buff **skb, 13511b7bde6dSNimrod Andy struct bufdesc *bdp, u32 length) 13521b7bde6dSNimrod Andy { 13531b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 13541b7bde6dSNimrod Andy struct sk_buff *new_skb; 13551b7bde6dSNimrod Andy 13561b7bde6dSNimrod Andy if (length > fep->rx_copybreak) 13571b7bde6dSNimrod Andy return false; 13581b7bde6dSNimrod Andy 13591b7bde6dSNimrod Andy new_skb = netdev_alloc_skb(ndev, length); 13601b7bde6dSNimrod Andy if (!new_skb) 13611b7bde6dSNimrod Andy return false; 13621b7bde6dSNimrod Andy 13631b7bde6dSNimrod Andy dma_sync_single_for_cpu(&fep->pdev->dev, bdp->cbd_bufaddr, 13641b7bde6dSNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 13651b7bde6dSNimrod Andy DMA_FROM_DEVICE); 13661b7bde6dSNimrod Andy memcpy(new_skb->data, (*skb)->data, length); 13671b7bde6dSNimrod Andy *skb = new_skb; 13681b7bde6dSNimrod Andy 13691b7bde6dSNimrod Andy return true; 13701b7bde6dSNimrod Andy } 13711b7bde6dSNimrod Andy 1372793fc096SFrank Li /* During a receive, the cur_rx points to the current incoming buffer. 1373793fc096SFrank Li * When we update through the ring, if the next incoming buffer has 1374793fc096SFrank Li * not been given to the system, we just set the empty indicator, 1375793fc096SFrank Li * effectively tossing the packet. 1376793fc096SFrank Li */ 1377793fc096SFrank Li static int 13784d494cdcSFugang Duan fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) 1379793fc096SFrank Li { 1380793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1381793fc096SFrank Li const struct platform_device_id *id_entry = 1382793fc096SFrank Li platform_get_device_id(fep->pdev); 13834d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 1384793fc096SFrank Li struct bufdesc *bdp; 1385793fc096SFrank Li unsigned short status; 13861b7bde6dSNimrod Andy struct sk_buff *skb_new = NULL; 1387793fc096SFrank Li struct sk_buff *skb; 1388793fc096SFrank Li ushort pkt_len; 1389793fc096SFrank Li __u8 *data; 1390793fc096SFrank Li int pkt_received = 0; 1391cdffcf1bSJim Baxter struct bufdesc_ex *ebdp = NULL; 1392cdffcf1bSJim Baxter bool vlan_packet_rcvd = false; 1393cdffcf1bSJim Baxter u16 vlan_tag; 1394d842a31fSDuan Fugang-B38611 int index = 0; 13951b7bde6dSNimrod Andy bool is_copybreak; 1396793fc096SFrank Li 1397793fc096SFrank Li #ifdef CONFIG_M532x 1398793fc096SFrank Li flush_cache_all(); 1399793fc096SFrank Li #endif 14004d494cdcSFugang Duan queue_id = FEC_ENET_GET_QUQUE(queue_id); 14014d494cdcSFugang Duan rxq = fep->rx_queue[queue_id]; 1402793fc096SFrank Li 1403793fc096SFrank Li /* First, grab all of the stats for the incoming packet. 1404793fc096SFrank Li * These get messed up if we get called due to a busy condition. 1405793fc096SFrank Li */ 14064d494cdcSFugang Duan bdp = rxq->cur_rx; 1407793fc096SFrank Li 1408793fc096SFrank Li while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) { 1409793fc096SFrank Li 1410793fc096SFrank Li if (pkt_received >= budget) 1411793fc096SFrank Li break; 1412793fc096SFrank Li pkt_received++; 1413793fc096SFrank Li 1414793fc096SFrank Li /* Since we have allocated space to hold a complete frame, 1415793fc096SFrank Li * the last indicator should be set. 1416793fc096SFrank Li */ 1417793fc096SFrank Li if ((status & BD_ENET_RX_LAST) == 0) 141831b7720cSJoe Perches netdev_err(ndev, "rcv is not +last\n"); 1419793fc096SFrank Li 1420db3421c1SRussell King 1421793fc096SFrank Li /* Check for errors. */ 1422793fc096SFrank Li if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | 1423793fc096SFrank Li BD_ENET_RX_CR | BD_ENET_RX_OV)) { 1424793fc096SFrank Li ndev->stats.rx_errors++; 1425793fc096SFrank Li if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH)) { 1426793fc096SFrank Li /* Frame too long or too short. */ 1427793fc096SFrank Li ndev->stats.rx_length_errors++; 1428793fc096SFrank Li } 1429793fc096SFrank Li if (status & BD_ENET_RX_NO) /* Frame alignment */ 1430793fc096SFrank Li ndev->stats.rx_frame_errors++; 1431793fc096SFrank Li if (status & BD_ENET_RX_CR) /* CRC Error */ 1432793fc096SFrank Li ndev->stats.rx_crc_errors++; 1433793fc096SFrank Li if (status & BD_ENET_RX_OV) /* FIFO overrun */ 1434793fc096SFrank Li ndev->stats.rx_fifo_errors++; 1435793fc096SFrank Li } 1436793fc096SFrank Li 1437793fc096SFrank Li /* Report late collisions as a frame error. 1438793fc096SFrank Li * On this error, the BD is closed, but we don't know what we 1439793fc096SFrank Li * have in the buffer. So, just drop this frame on the floor. 1440793fc096SFrank Li */ 1441793fc096SFrank Li if (status & BD_ENET_RX_CL) { 1442793fc096SFrank Li ndev->stats.rx_errors++; 1443793fc096SFrank Li ndev->stats.rx_frame_errors++; 1444793fc096SFrank Li goto rx_processing_done; 1445793fc096SFrank Li } 1446793fc096SFrank Li 1447793fc096SFrank Li /* Process the incoming frame. */ 1448793fc096SFrank Li ndev->stats.rx_packets++; 1449793fc096SFrank Li pkt_len = bdp->cbd_datlen; 1450793fc096SFrank Li ndev->stats.rx_bytes += pkt_len; 1451793fc096SFrank Li 14524d494cdcSFugang Duan index = fec_enet_get_bd_index(rxq->rx_bd_base, bdp, fep); 14531b7bde6dSNimrod Andy skb = rxq->rx_skbuff[index]; 14541b7bde6dSNimrod Andy 14551b7bde6dSNimrod Andy /* The packet length includes FCS, but we don't want to 14561b7bde6dSNimrod Andy * include that when passing upstream as it messes up 14571b7bde6dSNimrod Andy * bridging applications. 14581b7bde6dSNimrod Andy */ 14591b7bde6dSNimrod Andy is_copybreak = fec_enet_copybreak(ndev, &skb, bdp, pkt_len - 4); 14601b7bde6dSNimrod Andy if (!is_copybreak) { 14611b7bde6dSNimrod Andy skb_new = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE); 14621b7bde6dSNimrod Andy if (unlikely(!skb_new)) { 14631b7bde6dSNimrod Andy ndev->stats.rx_dropped++; 14641b7bde6dSNimrod Andy goto rx_processing_done; 14651b7bde6dSNimrod Andy } 14661b7bde6dSNimrod Andy dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, 1467b64bf4b7SNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 1468b64bf4b7SNimrod Andy DMA_FROM_DEVICE); 14691b7bde6dSNimrod Andy } 1470793fc096SFrank Li 14711b7bde6dSNimrod Andy prefetch(skb->data - NET_IP_ALIGN); 14721b7bde6dSNimrod Andy skb_put(skb, pkt_len - 4); 14731b7bde6dSNimrod Andy data = skb->data; 1474793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) 1475793fc096SFrank Li swap_buffer(data, pkt_len); 1476793fc096SFrank Li 1477cdffcf1bSJim Baxter /* Extract the enhanced buffer descriptor */ 1478cdffcf1bSJim Baxter ebdp = NULL; 1479cdffcf1bSJim Baxter if (fep->bufdesc_ex) 1480cdffcf1bSJim Baxter ebdp = (struct bufdesc_ex *)bdp; 1481cdffcf1bSJim Baxter 1482cdffcf1bSJim Baxter /* If this is a VLAN packet remove the VLAN Tag */ 1483cdffcf1bSJim Baxter vlan_packet_rcvd = false; 1484cdffcf1bSJim Baxter if ((ndev->features & NETIF_F_HW_VLAN_CTAG_RX) && 1485cdffcf1bSJim Baxter fep->bufdesc_ex && (ebdp->cbd_esc & BD_ENET_RX_VLAN)) { 1486cdffcf1bSJim Baxter /* Push and remove the vlan tag */ 1487cdffcf1bSJim Baxter struct vlan_hdr *vlan_header = 1488cdffcf1bSJim Baxter (struct vlan_hdr *) (data + ETH_HLEN); 1489cdffcf1bSJim Baxter vlan_tag = ntohs(vlan_header->h_vlan_TCI); 1490cdffcf1bSJim Baxter 1491cdffcf1bSJim Baxter vlan_packet_rcvd = true; 14921b7bde6dSNimrod Andy 14931b7bde6dSNimrod Andy skb_copy_to_linear_data_offset(skb, VLAN_HLEN, 14941b7bde6dSNimrod Andy data, (2 * ETH_ALEN)); 14951b7bde6dSNimrod Andy skb_pull(skb, VLAN_HLEN); 1496cdffcf1bSJim Baxter } 1497cdffcf1bSJim Baxter 1498793fc096SFrank Li skb->protocol = eth_type_trans(skb, ndev); 1499793fc096SFrank Li 1500793fc096SFrank Li /* Get receive timestamp from the skb */ 1501bfd4ecddSRussell King if (fep->hwts_rx_en && fep->bufdesc_ex) 1502bfd4ecddSRussell King fec_enet_hwtstamp(fep, ebdp->ts, 1503bfd4ecddSRussell King skb_hwtstamps(skb)); 1504793fc096SFrank Li 15054c09eed9SJim Baxter if (fep->bufdesc_ex && 15064c09eed9SJim Baxter (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { 15074c09eed9SJim Baxter if (!(ebdp->cbd_esc & FLAG_RX_CSUM_ERROR)) { 15084c09eed9SJim Baxter /* don't check it */ 15094c09eed9SJim Baxter skb->ip_summed = CHECKSUM_UNNECESSARY; 15104c09eed9SJim Baxter } else { 15114c09eed9SJim Baxter skb_checksum_none_assert(skb); 15124c09eed9SJim Baxter } 15134c09eed9SJim Baxter } 15144c09eed9SJim Baxter 1515cdffcf1bSJim Baxter /* Handle received VLAN packets */ 1516cdffcf1bSJim Baxter if (vlan_packet_rcvd) 1517cdffcf1bSJim Baxter __vlan_hwaccel_put_tag(skb, 1518cdffcf1bSJim Baxter htons(ETH_P_8021Q), 1519cdffcf1bSJim Baxter vlan_tag); 1520cdffcf1bSJim Baxter 1521793fc096SFrank Li napi_gro_receive(&fep->napi, skb); 1522793fc096SFrank Li 15231b7bde6dSNimrod Andy if (is_copybreak) { 1524d842a31fSDuan Fugang-B38611 dma_sync_single_for_device(&fep->pdev->dev, bdp->cbd_bufaddr, 1525b64bf4b7SNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 1526b64bf4b7SNimrod Andy DMA_FROM_DEVICE); 15271b7bde6dSNimrod Andy } else { 15281b7bde6dSNimrod Andy rxq->rx_skbuff[index] = skb_new; 15291b7bde6dSNimrod Andy fec_enet_new_rxbdp(ndev, bdp, skb_new); 15301b7bde6dSNimrod Andy } 15311b7bde6dSNimrod Andy 1532793fc096SFrank Li rx_processing_done: 1533793fc096SFrank Li /* Clear the status flags for this buffer */ 1534793fc096SFrank Li status &= ~BD_ENET_RX_STATS; 1535793fc096SFrank Li 1536793fc096SFrank Li /* Mark the buffer empty */ 1537793fc096SFrank Li status |= BD_ENET_RX_EMPTY; 1538793fc096SFrank Li bdp->cbd_sc = status; 1539793fc096SFrank Li 1540793fc096SFrank Li if (fep->bufdesc_ex) { 1541793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 1542793fc096SFrank Li 1543793fc096SFrank Li ebdp->cbd_esc = BD_ENET_RX_INT; 1544793fc096SFrank Li ebdp->cbd_prot = 0; 1545793fc096SFrank Li ebdp->cbd_bdu = 0; 1546793fc096SFrank Li } 1547793fc096SFrank Li 1548793fc096SFrank Li /* Update BD pointer to next entry */ 15494d494cdcSFugang Duan bdp = fec_enet_get_nextdesc(bdp, fep, queue_id); 155036e24e2eSDuan Fugang-B38611 1551793fc096SFrank Li /* Doing this here will keep the FEC running while we process 1552793fc096SFrank Li * incoming frames. On a heavily loaded network, we should be 1553793fc096SFrank Li * able to keep up at the expense of system resources. 1554793fc096SFrank Li */ 15554d494cdcSFugang Duan writel(0, fep->hwp + FEC_R_DES_ACTIVE(queue_id)); 1556793fc096SFrank Li } 15574d494cdcSFugang Duan rxq->cur_rx = bdp; 1558793fc096SFrank Li return pkt_received; 1559793fc096SFrank Li } 1560793fc096SFrank Li 15614d494cdcSFugang Duan static int 15624d494cdcSFugang Duan fec_enet_rx(struct net_device *ndev, int budget) 15634d494cdcSFugang Duan { 15644d494cdcSFugang Duan int pkt_received = 0; 15654d494cdcSFugang Duan u16 queue_id; 15664d494cdcSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 15674d494cdcSFugang Duan 15684d494cdcSFugang Duan for_each_set_bit(queue_id, &fep->work_rx, FEC_ENET_MAX_RX_QS) { 15694d494cdcSFugang Duan clear_bit(queue_id, &fep->work_rx); 15704d494cdcSFugang Duan pkt_received += fec_enet_rx_queue(ndev, 15714d494cdcSFugang Duan budget - pkt_received, queue_id); 15724d494cdcSFugang Duan } 15734d494cdcSFugang Duan return pkt_received; 15744d494cdcSFugang Duan } 15754d494cdcSFugang Duan 15764d494cdcSFugang Duan static bool 15774d494cdcSFugang Duan fec_enet_collect_events(struct fec_enet_private *fep, uint int_events) 15784d494cdcSFugang Duan { 15794d494cdcSFugang Duan if (int_events == 0) 15804d494cdcSFugang Duan return false; 15814d494cdcSFugang Duan 15824d494cdcSFugang Duan if (int_events & FEC_ENET_RXF) 15834d494cdcSFugang Duan fep->work_rx |= (1 << 2); 1584ce99d0d3SFrank Li if (int_events & FEC_ENET_RXF_1) 1585ce99d0d3SFrank Li fep->work_rx |= (1 << 0); 1586ce99d0d3SFrank Li if (int_events & FEC_ENET_RXF_2) 1587ce99d0d3SFrank Li fep->work_rx |= (1 << 1); 15884d494cdcSFugang Duan 15894d494cdcSFugang Duan if (int_events & FEC_ENET_TXF) 15904d494cdcSFugang Duan fep->work_tx |= (1 << 2); 1591ce99d0d3SFrank Li if (int_events & FEC_ENET_TXF_1) 1592ce99d0d3SFrank Li fep->work_tx |= (1 << 0); 1593ce99d0d3SFrank Li if (int_events & FEC_ENET_TXF_2) 1594ce99d0d3SFrank Li fep->work_tx |= (1 << 1); 15954d494cdcSFugang Duan 15964d494cdcSFugang Duan return true; 15974d494cdcSFugang Duan } 15984d494cdcSFugang Duan 1599793fc096SFrank Li static irqreturn_t 1600793fc096SFrank Li fec_enet_interrupt(int irq, void *dev_id) 1601793fc096SFrank Li { 1602793fc096SFrank Li struct net_device *ndev = dev_id; 1603793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 16047a16807cSRussell King const unsigned napi_mask = FEC_ENET_RXF | FEC_ENET_TXF; 1605793fc096SFrank Li uint int_events; 1606793fc096SFrank Li irqreturn_t ret = IRQ_NONE; 1607793fc096SFrank Li 1608793fc096SFrank Li int_events = readl(fep->hwp + FEC_IEVENT); 16097a16807cSRussell King writel(int_events & ~napi_mask, fep->hwp + FEC_IEVENT); 16104d494cdcSFugang Duan fec_enet_collect_events(fep, int_events); 1611793fc096SFrank Li 16127a16807cSRussell King if (int_events & napi_mask) { 1613793fc096SFrank Li ret = IRQ_HANDLED; 1614793fc096SFrank Li 16157a16807cSRussell King /* Disable the NAPI interrupts */ 16167a16807cSRussell King writel(FEC_ENET_MII, fep->hwp + FEC_IMASK); 16177a16807cSRussell King napi_schedule(&fep->napi); 1618793fc096SFrank Li } 1619793fc096SFrank Li 1620793fc096SFrank Li if (int_events & FEC_ENET_MII) { 1621793fc096SFrank Li ret = IRQ_HANDLED; 1622793fc096SFrank Li complete(&fep->mdio_done); 1623793fc096SFrank Li } 1624793fc096SFrank Li 1625793fc096SFrank Li return ret; 1626793fc096SFrank Li } 1627793fc096SFrank Li 1628793fc096SFrank Li static int fec_enet_rx_napi(struct napi_struct *napi, int budget) 1629793fc096SFrank Li { 1630793fc096SFrank Li struct net_device *ndev = napi->dev; 1631793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 16327a16807cSRussell King int pkts; 16337a16807cSRussell King 16347a16807cSRussell King /* 16357a16807cSRussell King * Clear any pending transmit or receive interrupts before 16367a16807cSRussell King * processing the rings to avoid racing with the hardware. 16377a16807cSRussell King */ 16387a16807cSRussell King writel(FEC_ENET_RXF | FEC_ENET_TXF, fep->hwp + FEC_IEVENT); 16397a16807cSRussell King 16407a16807cSRussell King pkts = fec_enet_rx(ndev, budget); 1641793fc096SFrank Li 1642793fc096SFrank Li fec_enet_tx(ndev); 1643793fc096SFrank Li 1644793fc096SFrank Li if (pkts < budget) { 1645793fc096SFrank Li napi_complete(napi); 1646793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 1647793fc096SFrank Li } 1648793fc096SFrank Li return pkts; 1649793fc096SFrank Li } 1650793fc096SFrank Li 1651793fc096SFrank Li /* ------------------------------------------------------------------------- */ 1652793fc096SFrank Li static void fec_get_mac(struct net_device *ndev) 1653793fc096SFrank Li { 1654793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 165594660ba0SJingoo Han struct fec_platform_data *pdata = dev_get_platdata(&fep->pdev->dev); 1656793fc096SFrank Li unsigned char *iap, tmpaddr[ETH_ALEN]; 1657793fc096SFrank Li 1658793fc096SFrank Li /* 1659793fc096SFrank Li * try to get mac address in following order: 1660793fc096SFrank Li * 1661793fc096SFrank Li * 1) module parameter via kernel command line in form 1662793fc096SFrank Li * fec.macaddr=0x00,0x04,0x9f,0x01,0x30,0xe0 1663793fc096SFrank Li */ 1664793fc096SFrank Li iap = macaddr; 1665793fc096SFrank Li 1666793fc096SFrank Li /* 1667793fc096SFrank Li * 2) from device tree data 1668793fc096SFrank Li */ 1669793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 1670793fc096SFrank Li struct device_node *np = fep->pdev->dev.of_node; 1671793fc096SFrank Li if (np) { 1672793fc096SFrank Li const char *mac = of_get_mac_address(np); 1673793fc096SFrank Li if (mac) 1674793fc096SFrank Li iap = (unsigned char *) mac; 1675793fc096SFrank Li } 1676793fc096SFrank Li } 1677793fc096SFrank Li 1678793fc096SFrank Li /* 1679793fc096SFrank Li * 3) from flash or fuse (via platform data) 1680793fc096SFrank Li */ 1681793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 1682793fc096SFrank Li #ifdef CONFIG_M5272 1683793fc096SFrank Li if (FEC_FLASHMAC) 1684793fc096SFrank Li iap = (unsigned char *)FEC_FLASHMAC; 1685793fc096SFrank Li #else 1686793fc096SFrank Li if (pdata) 1687793fc096SFrank Li iap = (unsigned char *)&pdata->mac; 1688793fc096SFrank Li #endif 1689793fc096SFrank Li } 1690793fc096SFrank Li 1691793fc096SFrank Li /* 1692793fc096SFrank Li * 4) FEC mac registers set by bootloader 1693793fc096SFrank Li */ 1694793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 16957d7628f3SDan Carpenter *((__be32 *) &tmpaddr[0]) = 16967d7628f3SDan Carpenter cpu_to_be32(readl(fep->hwp + FEC_ADDR_LOW)); 16977d7628f3SDan Carpenter *((__be16 *) &tmpaddr[4]) = 16987d7628f3SDan Carpenter cpu_to_be16(readl(fep->hwp + FEC_ADDR_HIGH) >> 16); 1699793fc096SFrank Li iap = &tmpaddr[0]; 1700793fc096SFrank Li } 1701793fc096SFrank Li 1702ff5b2fabSLucas Stach /* 1703ff5b2fabSLucas Stach * 5) random mac address 1704ff5b2fabSLucas Stach */ 1705ff5b2fabSLucas Stach if (!is_valid_ether_addr(iap)) { 1706ff5b2fabSLucas Stach /* Report it and use a random ethernet address instead */ 1707ff5b2fabSLucas Stach netdev_err(ndev, "Invalid MAC address: %pM\n", iap); 1708ff5b2fabSLucas Stach eth_hw_addr_random(ndev); 1709ff5b2fabSLucas Stach netdev_info(ndev, "Using random MAC address: %pM\n", 1710ff5b2fabSLucas Stach ndev->dev_addr); 1711ff5b2fabSLucas Stach return; 1712ff5b2fabSLucas Stach } 1713ff5b2fabSLucas Stach 1714793fc096SFrank Li memcpy(ndev->dev_addr, iap, ETH_ALEN); 1715793fc096SFrank Li 1716793fc096SFrank Li /* Adjust MAC if using macaddr */ 1717793fc096SFrank Li if (iap == macaddr) 1718793fc096SFrank Li ndev->dev_addr[ETH_ALEN-1] = macaddr[ETH_ALEN-1] + fep->dev_id; 1719793fc096SFrank Li } 1720793fc096SFrank Li 1721793fc096SFrank Li /* ------------------------------------------------------------------------- */ 1722793fc096SFrank Li 1723793fc096SFrank Li /* 1724793fc096SFrank Li * Phy section 1725793fc096SFrank Li */ 1726793fc096SFrank Li static void fec_enet_adjust_link(struct net_device *ndev) 1727793fc096SFrank Li { 1728793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1729793fc096SFrank Li struct phy_device *phy_dev = fep->phy_dev; 1730793fc096SFrank Li int status_change = 0; 1731793fc096SFrank Li 1732793fc096SFrank Li /* Prevent a state halted on mii error */ 1733793fc096SFrank Li if (fep->mii_timeout && phy_dev->state == PHY_HALTED) { 1734793fc096SFrank Li phy_dev->state = PHY_RESUMING; 173554309fa6SFrank Li return; 1736793fc096SFrank Li } 1737793fc096SFrank Li 17388ce5624fSRussell King /* 17398ce5624fSRussell King * If the netdev is down, or is going down, we're not interested 17408ce5624fSRussell King * in link state events, so just mark our idea of the link as down 17418ce5624fSRussell King * and ignore the event. 17428ce5624fSRussell King */ 17438ce5624fSRussell King if (!netif_running(ndev) || !netif_device_present(ndev)) { 17448ce5624fSRussell King fep->link = 0; 17458ce5624fSRussell King } else if (phy_dev->link) { 1746793fc096SFrank Li if (!fep->link) { 1747793fc096SFrank Li fep->link = phy_dev->link; 1748793fc096SFrank Li status_change = 1; 1749793fc096SFrank Li } 1750793fc096SFrank Li 1751ef83337dSRussell King if (fep->full_duplex != phy_dev->duplex) { 1752ef83337dSRussell King fep->full_duplex = phy_dev->duplex; 1753793fc096SFrank Li status_change = 1; 1754ef83337dSRussell King } 1755793fc096SFrank Li 1756793fc096SFrank Li if (phy_dev->speed != fep->speed) { 1757793fc096SFrank Li fep->speed = phy_dev->speed; 1758793fc096SFrank Li status_change = 1; 1759793fc096SFrank Li } 1760793fc096SFrank Li 1761793fc096SFrank Li /* if any of the above changed restart the FEC */ 1762dbc64a8eSRussell King if (status_change) { 1763dbc64a8eSRussell King napi_disable(&fep->napi); 1764dbc64a8eSRussell King netif_tx_lock_bh(ndev); 1765ef83337dSRussell King fec_restart(ndev); 1766dbc64a8eSRussell King netif_wake_queue(ndev); 17676af42d42SRussell King netif_tx_unlock_bh(ndev); 1768dbc64a8eSRussell King napi_enable(&fep->napi); 1769dbc64a8eSRussell King } 1770793fc096SFrank Li } else { 1771793fc096SFrank Li if (fep->link) { 1772f208ce10SRussell King napi_disable(&fep->napi); 1773f208ce10SRussell King netif_tx_lock_bh(ndev); 1774793fc096SFrank Li fec_stop(ndev); 1775f208ce10SRussell King netif_tx_unlock_bh(ndev); 1776f208ce10SRussell King napi_enable(&fep->napi); 17776e0895c2SDavid S. Miller fep->link = phy_dev->link; 1778793fc096SFrank Li status_change = 1; 1779793fc096SFrank Li } 1780793fc096SFrank Li } 1781793fc096SFrank Li 1782793fc096SFrank Li if (status_change) 1783793fc096SFrank Li phy_print_status(phy_dev); 1784793fc096SFrank Li } 1785793fc096SFrank Li 1786793fc096SFrank Li static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum) 1787793fc096SFrank Li { 1788793fc096SFrank Li struct fec_enet_private *fep = bus->priv; 1789793fc096SFrank Li unsigned long time_left; 1790793fc096SFrank Li 1791793fc096SFrank Li fep->mii_timeout = 0; 1792793fc096SFrank Li init_completion(&fep->mdio_done); 1793793fc096SFrank Li 1794793fc096SFrank Li /* start a read op */ 1795793fc096SFrank Li writel(FEC_MMFR_ST | FEC_MMFR_OP_READ | 1796793fc096SFrank Li FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) | 1797793fc096SFrank Li FEC_MMFR_TA, fep->hwp + FEC_MII_DATA); 1798793fc096SFrank Li 1799793fc096SFrank Li /* wait for end of transfer */ 1800793fc096SFrank Li time_left = wait_for_completion_timeout(&fep->mdio_done, 1801793fc096SFrank Li usecs_to_jiffies(FEC_MII_TIMEOUT)); 1802793fc096SFrank Li if (time_left == 0) { 1803793fc096SFrank Li fep->mii_timeout = 1; 180431b7720cSJoe Perches netdev_err(fep->netdev, "MDIO read timeout\n"); 1805793fc096SFrank Li return -ETIMEDOUT; 1806793fc096SFrank Li } 1807793fc096SFrank Li 1808793fc096SFrank Li /* return value */ 1809793fc096SFrank Li return FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA)); 1810793fc096SFrank Li } 1811793fc096SFrank Li 1812793fc096SFrank Li static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum, 1813793fc096SFrank Li u16 value) 1814793fc096SFrank Li { 1815793fc096SFrank Li struct fec_enet_private *fep = bus->priv; 1816793fc096SFrank Li unsigned long time_left; 1817793fc096SFrank Li 1818793fc096SFrank Li fep->mii_timeout = 0; 1819793fc096SFrank Li init_completion(&fep->mdio_done); 1820793fc096SFrank Li 1821793fc096SFrank Li /* start a write op */ 1822793fc096SFrank Li writel(FEC_MMFR_ST | FEC_MMFR_OP_WRITE | 1823793fc096SFrank Li FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) | 1824793fc096SFrank Li FEC_MMFR_TA | FEC_MMFR_DATA(value), 1825793fc096SFrank Li fep->hwp + FEC_MII_DATA); 1826793fc096SFrank Li 1827793fc096SFrank Li /* wait for end of transfer */ 1828793fc096SFrank Li time_left = wait_for_completion_timeout(&fep->mdio_done, 1829793fc096SFrank Li usecs_to_jiffies(FEC_MII_TIMEOUT)); 1830793fc096SFrank Li if (time_left == 0) { 1831793fc096SFrank Li fep->mii_timeout = 1; 183231b7720cSJoe Perches netdev_err(fep->netdev, "MDIO write timeout\n"); 1833793fc096SFrank Li return -ETIMEDOUT; 1834793fc096SFrank Li } 1835793fc096SFrank Li 1836793fc096SFrank Li return 0; 1837793fc096SFrank Li } 1838793fc096SFrank Li 1839e8fcfcd5SNimrod Andy static int fec_enet_clk_enable(struct net_device *ndev, bool enable) 1840e8fcfcd5SNimrod Andy { 1841e8fcfcd5SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 1842e8fcfcd5SNimrod Andy int ret; 1843e8fcfcd5SNimrod Andy 1844e8fcfcd5SNimrod Andy if (enable) { 1845e8fcfcd5SNimrod Andy ret = clk_prepare_enable(fep->clk_ahb); 1846e8fcfcd5SNimrod Andy if (ret) 1847e8fcfcd5SNimrod Andy return ret; 1848e8fcfcd5SNimrod Andy ret = clk_prepare_enable(fep->clk_ipg); 1849e8fcfcd5SNimrod Andy if (ret) 1850e8fcfcd5SNimrod Andy goto failed_clk_ipg; 1851e8fcfcd5SNimrod Andy if (fep->clk_enet_out) { 1852e8fcfcd5SNimrod Andy ret = clk_prepare_enable(fep->clk_enet_out); 1853e8fcfcd5SNimrod Andy if (ret) 1854e8fcfcd5SNimrod Andy goto failed_clk_enet_out; 1855e8fcfcd5SNimrod Andy } 1856e8fcfcd5SNimrod Andy if (fep->clk_ptp) { 185791c0d987SNimrod Andy mutex_lock(&fep->ptp_clk_mutex); 1858e8fcfcd5SNimrod Andy ret = clk_prepare_enable(fep->clk_ptp); 185991c0d987SNimrod Andy if (ret) { 186091c0d987SNimrod Andy mutex_unlock(&fep->ptp_clk_mutex); 1861e8fcfcd5SNimrod Andy goto failed_clk_ptp; 186291c0d987SNimrod Andy } else { 186391c0d987SNimrod Andy fep->ptp_clk_on = true; 186491c0d987SNimrod Andy } 186591c0d987SNimrod Andy mutex_unlock(&fep->ptp_clk_mutex); 1866e8fcfcd5SNimrod Andy } 18679b5330edSFugang Duan if (fep->clk_ref) { 18689b5330edSFugang Duan ret = clk_prepare_enable(fep->clk_ref); 18699b5330edSFugang Duan if (ret) 18709b5330edSFugang Duan goto failed_clk_ref; 18719b5330edSFugang Duan } 1872e8fcfcd5SNimrod Andy } else { 1873e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_ahb); 1874e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_ipg); 1875e8fcfcd5SNimrod Andy if (fep->clk_enet_out) 1876e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_enet_out); 187791c0d987SNimrod Andy if (fep->clk_ptp) { 187891c0d987SNimrod Andy mutex_lock(&fep->ptp_clk_mutex); 1879e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_ptp); 188091c0d987SNimrod Andy fep->ptp_clk_on = false; 188191c0d987SNimrod Andy mutex_unlock(&fep->ptp_clk_mutex); 188291c0d987SNimrod Andy } 18839b5330edSFugang Duan if (fep->clk_ref) 18849b5330edSFugang Duan clk_disable_unprepare(fep->clk_ref); 1885e8fcfcd5SNimrod Andy } 1886e8fcfcd5SNimrod Andy 1887e8fcfcd5SNimrod Andy return 0; 18889b5330edSFugang Duan 18899b5330edSFugang Duan failed_clk_ref: 18909b5330edSFugang Duan if (fep->clk_ref) 18919b5330edSFugang Duan clk_disable_unprepare(fep->clk_ref); 1892e8fcfcd5SNimrod Andy failed_clk_ptp: 1893e8fcfcd5SNimrod Andy if (fep->clk_enet_out) 1894e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_enet_out); 1895e8fcfcd5SNimrod Andy failed_clk_enet_out: 1896e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_ipg); 1897e8fcfcd5SNimrod Andy failed_clk_ipg: 1898e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_ahb); 1899e8fcfcd5SNimrod Andy 1900e8fcfcd5SNimrod Andy return ret; 1901e8fcfcd5SNimrod Andy } 1902e8fcfcd5SNimrod Andy 1903793fc096SFrank Li static int fec_enet_mii_probe(struct net_device *ndev) 1904793fc096SFrank Li { 1905793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1906793fc096SFrank Li const struct platform_device_id *id_entry = 1907793fc096SFrank Li platform_get_device_id(fep->pdev); 1908793fc096SFrank Li struct phy_device *phy_dev = NULL; 1909793fc096SFrank Li char mdio_bus_id[MII_BUS_ID_SIZE]; 1910793fc096SFrank Li char phy_name[MII_BUS_ID_SIZE + 3]; 1911793fc096SFrank Li int phy_id; 1912793fc096SFrank Li int dev_id = fep->dev_id; 1913793fc096SFrank Li 1914793fc096SFrank Li fep->phy_dev = NULL; 1915793fc096SFrank Li 1916407066f8SUwe Kleine-König if (fep->phy_node) { 1917407066f8SUwe Kleine-König phy_dev = of_phy_connect(ndev, fep->phy_node, 1918407066f8SUwe Kleine-König &fec_enet_adjust_link, 0, 1919407066f8SUwe Kleine-König fep->phy_interface); 1920407066f8SUwe Kleine-König } else { 1921793fc096SFrank Li /* check for attached phy */ 1922793fc096SFrank Li for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) { 1923793fc096SFrank Li if ((fep->mii_bus->phy_mask & (1 << phy_id))) 1924793fc096SFrank Li continue; 1925793fc096SFrank Li if (fep->mii_bus->phy_map[phy_id] == NULL) 1926793fc096SFrank Li continue; 1927793fc096SFrank Li if (fep->mii_bus->phy_map[phy_id]->phy_id == 0) 1928793fc096SFrank Li continue; 1929793fc096SFrank Li if (dev_id--) 1930793fc096SFrank Li continue; 1931949bdd20SRickard Strandqvist strlcpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE); 1932793fc096SFrank Li break; 1933793fc096SFrank Li } 1934793fc096SFrank Li 1935793fc096SFrank Li if (phy_id >= PHY_MAX_ADDR) { 193631b7720cSJoe Perches netdev_info(ndev, "no PHY, assuming direct connection to switch\n"); 1937949bdd20SRickard Strandqvist strlcpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE); 1938793fc096SFrank Li phy_id = 0; 1939793fc096SFrank Li } 1940793fc096SFrank Li 1941407066f8SUwe Kleine-König snprintf(phy_name, sizeof(phy_name), 1942407066f8SUwe Kleine-König PHY_ID_FMT, mdio_bus_id, phy_id); 1943793fc096SFrank Li phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, 1944793fc096SFrank Li fep->phy_interface); 1945407066f8SUwe Kleine-König } 1946407066f8SUwe Kleine-König 1947793fc096SFrank Li if (IS_ERR(phy_dev)) { 194831b7720cSJoe Perches netdev_err(ndev, "could not attach to PHY\n"); 1949793fc096SFrank Li return PTR_ERR(phy_dev); 1950793fc096SFrank Li } 1951793fc096SFrank Li 1952793fc096SFrank Li /* mask with MAC supported features */ 1953793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_HAS_GBIT) { 1954793fc096SFrank Li phy_dev->supported &= PHY_GBIT_FEATURES; 1955b44592ffSRussell King phy_dev->supported &= ~SUPPORTED_1000baseT_Half; 1956d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 1957793fc096SFrank Li phy_dev->supported |= SUPPORTED_Pause; 1958d1391930SGuenter Roeck #endif 1959793fc096SFrank Li } 1960793fc096SFrank Li else 1961793fc096SFrank Li phy_dev->supported &= PHY_BASIC_FEATURES; 1962793fc096SFrank Li 1963793fc096SFrank Li phy_dev->advertising = phy_dev->supported; 1964793fc096SFrank Li 1965793fc096SFrank Li fep->phy_dev = phy_dev; 1966793fc096SFrank Li fep->link = 0; 1967793fc096SFrank Li fep->full_duplex = 0; 1968793fc096SFrank Li 196931b7720cSJoe Perches netdev_info(ndev, "Freescale FEC PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n", 1970793fc096SFrank Li fep->phy_dev->drv->name, dev_name(&fep->phy_dev->dev), 1971793fc096SFrank Li fep->phy_dev->irq); 1972793fc096SFrank Li 1973793fc096SFrank Li return 0; 1974793fc096SFrank Li } 1975793fc096SFrank Li 1976793fc096SFrank Li static int fec_enet_mii_init(struct platform_device *pdev) 1977793fc096SFrank Li { 1978793fc096SFrank Li static struct mii_bus *fec0_mii_bus; 1979793fc096SFrank Li struct net_device *ndev = platform_get_drvdata(pdev); 1980793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1981793fc096SFrank Li const struct platform_device_id *id_entry = 1982793fc096SFrank Li platform_get_device_id(fep->pdev); 1983407066f8SUwe Kleine-König struct device_node *node; 1984793fc096SFrank Li int err = -ENXIO, i; 1985793fc096SFrank Li 1986793fc096SFrank Li /* 1987793fc096SFrank Li * The dual fec interfaces are not equivalent with enet-mac. 1988793fc096SFrank Li * Here are the differences: 1989793fc096SFrank Li * 1990793fc096SFrank Li * - fec0 supports MII & RMII modes while fec1 only supports RMII 1991793fc096SFrank Li * - fec0 acts as the 1588 time master while fec1 is slave 1992793fc096SFrank Li * - external phys can only be configured by fec0 1993793fc096SFrank Li * 1994793fc096SFrank Li * That is to say fec1 can not work independently. It only works 1995793fc096SFrank Li * when fec0 is working. The reason behind this design is that the 1996793fc096SFrank Li * second interface is added primarily for Switch mode. 1997793fc096SFrank Li * 1998793fc096SFrank Li * Because of the last point above, both phys are attached on fec0 1999793fc096SFrank Li * mdio interface in board design, and need to be configured by 2000793fc096SFrank Li * fec0 mii_bus. 2001793fc096SFrank Li */ 2002793fc096SFrank Li if ((id_entry->driver_data & FEC_QUIRK_ENET_MAC) && fep->dev_id > 0) { 2003793fc096SFrank Li /* fec1 uses fec0 mii_bus */ 2004793fc096SFrank Li if (mii_cnt && fec0_mii_bus) { 2005793fc096SFrank Li fep->mii_bus = fec0_mii_bus; 2006793fc096SFrank Li mii_cnt++; 2007793fc096SFrank Li return 0; 2008793fc096SFrank Li } 2009793fc096SFrank Li return -ENOENT; 2010793fc096SFrank Li } 2011793fc096SFrank Li 2012793fc096SFrank Li fep->mii_timeout = 0; 2013793fc096SFrank Li 2014793fc096SFrank Li /* 2015793fc096SFrank Li * Set MII speed to 2.5 MHz (= clk_get_rate() / 2 * phy_speed) 2016793fc096SFrank Li * 2017793fc096SFrank Li * The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while 2018793fc096SFrank Li * for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'. The i.MX28 2019793fc096SFrank Li * Reference Manual has an error on this, and gets fixed on i.MX6Q 2020793fc096SFrank Li * document. 2021793fc096SFrank Li */ 202298a6eeb8SNimrod Andy fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 5000000); 2023793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) 2024793fc096SFrank Li fep->phy_speed--; 2025793fc096SFrank Li fep->phy_speed <<= 1; 2026793fc096SFrank Li writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 2027793fc096SFrank Li 2028793fc096SFrank Li fep->mii_bus = mdiobus_alloc(); 2029793fc096SFrank Li if (fep->mii_bus == NULL) { 2030793fc096SFrank Li err = -ENOMEM; 2031793fc096SFrank Li goto err_out; 2032793fc096SFrank Li } 2033793fc096SFrank Li 2034793fc096SFrank Li fep->mii_bus->name = "fec_enet_mii_bus"; 2035793fc096SFrank Li fep->mii_bus->read = fec_enet_mdio_read; 2036793fc096SFrank Li fep->mii_bus->write = fec_enet_mdio_write; 2037793fc096SFrank Li snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", 2038793fc096SFrank Li pdev->name, fep->dev_id + 1); 2039793fc096SFrank Li fep->mii_bus->priv = fep; 2040793fc096SFrank Li fep->mii_bus->parent = &pdev->dev; 2041793fc096SFrank Li 2042793fc096SFrank Li fep->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); 2043793fc096SFrank Li if (!fep->mii_bus->irq) { 2044793fc096SFrank Li err = -ENOMEM; 2045793fc096SFrank Li goto err_out_free_mdiobus; 2046793fc096SFrank Li } 2047793fc096SFrank Li 2048793fc096SFrank Li for (i = 0; i < PHY_MAX_ADDR; i++) 2049793fc096SFrank Li fep->mii_bus->irq[i] = PHY_POLL; 2050793fc096SFrank Li 2051407066f8SUwe Kleine-König node = of_get_child_by_name(pdev->dev.of_node, "mdio"); 2052407066f8SUwe Kleine-König if (node) { 2053407066f8SUwe Kleine-König err = of_mdiobus_register(fep->mii_bus, node); 2054407066f8SUwe Kleine-König of_node_put(node); 2055407066f8SUwe Kleine-König } else { 2056407066f8SUwe Kleine-König err = mdiobus_register(fep->mii_bus); 2057407066f8SUwe Kleine-König } 2058407066f8SUwe Kleine-König 2059407066f8SUwe Kleine-König if (err) 2060793fc096SFrank Li goto err_out_free_mdio_irq; 2061793fc096SFrank Li 2062793fc096SFrank Li mii_cnt++; 2063793fc096SFrank Li 2064793fc096SFrank Li /* save fec0 mii_bus */ 2065793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) 2066793fc096SFrank Li fec0_mii_bus = fep->mii_bus; 2067793fc096SFrank Li 2068793fc096SFrank Li return 0; 2069793fc096SFrank Li 2070793fc096SFrank Li err_out_free_mdio_irq: 2071793fc096SFrank Li kfree(fep->mii_bus->irq); 2072793fc096SFrank Li err_out_free_mdiobus: 2073793fc096SFrank Li mdiobus_free(fep->mii_bus); 2074793fc096SFrank Li err_out: 2075793fc096SFrank Li return err; 2076793fc096SFrank Li } 2077793fc096SFrank Li 2078793fc096SFrank Li static void fec_enet_mii_remove(struct fec_enet_private *fep) 2079793fc096SFrank Li { 2080793fc096SFrank Li if (--mii_cnt == 0) { 2081793fc096SFrank Li mdiobus_unregister(fep->mii_bus); 2082793fc096SFrank Li kfree(fep->mii_bus->irq); 2083793fc096SFrank Li mdiobus_free(fep->mii_bus); 2084793fc096SFrank Li } 2085793fc096SFrank Li } 2086793fc096SFrank Li 2087793fc096SFrank Li static int fec_enet_get_settings(struct net_device *ndev, 2088793fc096SFrank Li struct ethtool_cmd *cmd) 2089793fc096SFrank Li { 2090793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2091793fc096SFrank Li struct phy_device *phydev = fep->phy_dev; 2092793fc096SFrank Li 2093793fc096SFrank Li if (!phydev) 2094793fc096SFrank Li return -ENODEV; 2095793fc096SFrank Li 2096793fc096SFrank Li return phy_ethtool_gset(phydev, cmd); 2097793fc096SFrank Li } 2098793fc096SFrank Li 2099793fc096SFrank Li static int fec_enet_set_settings(struct net_device *ndev, 2100793fc096SFrank Li struct ethtool_cmd *cmd) 2101793fc096SFrank Li { 2102793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2103793fc096SFrank Li struct phy_device *phydev = fep->phy_dev; 2104793fc096SFrank Li 2105793fc096SFrank Li if (!phydev) 2106793fc096SFrank Li return -ENODEV; 2107793fc096SFrank Li 2108793fc096SFrank Li return phy_ethtool_sset(phydev, cmd); 2109793fc096SFrank Li } 2110793fc096SFrank Li 2111793fc096SFrank Li static void fec_enet_get_drvinfo(struct net_device *ndev, 2112793fc096SFrank Li struct ethtool_drvinfo *info) 2113793fc096SFrank Li { 2114793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2115793fc096SFrank Li 2116793fc096SFrank Li strlcpy(info->driver, fep->pdev->dev.driver->name, 2117793fc096SFrank Li sizeof(info->driver)); 2118793fc096SFrank Li strlcpy(info->version, "Revision: 1.0", sizeof(info->version)); 2119793fc096SFrank Li strlcpy(info->bus_info, dev_name(&ndev->dev), sizeof(info->bus_info)); 2120793fc096SFrank Li } 2121793fc096SFrank Li 2122793fc096SFrank Li static int fec_enet_get_ts_info(struct net_device *ndev, 2123793fc096SFrank Li struct ethtool_ts_info *info) 2124793fc096SFrank Li { 2125793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2126793fc096SFrank Li 2127793fc096SFrank Li if (fep->bufdesc_ex) { 2128793fc096SFrank Li 2129793fc096SFrank Li info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | 2130793fc096SFrank Li SOF_TIMESTAMPING_RX_SOFTWARE | 2131793fc096SFrank Li SOF_TIMESTAMPING_SOFTWARE | 2132793fc096SFrank Li SOF_TIMESTAMPING_TX_HARDWARE | 2133793fc096SFrank Li SOF_TIMESTAMPING_RX_HARDWARE | 2134793fc096SFrank Li SOF_TIMESTAMPING_RAW_HARDWARE; 2135793fc096SFrank Li if (fep->ptp_clock) 2136793fc096SFrank Li info->phc_index = ptp_clock_index(fep->ptp_clock); 2137793fc096SFrank Li else 2138793fc096SFrank Li info->phc_index = -1; 2139793fc096SFrank Li 2140793fc096SFrank Li info->tx_types = (1 << HWTSTAMP_TX_OFF) | 2141793fc096SFrank Li (1 << HWTSTAMP_TX_ON); 2142793fc096SFrank Li 2143793fc096SFrank Li info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | 2144793fc096SFrank Li (1 << HWTSTAMP_FILTER_ALL); 2145793fc096SFrank Li return 0; 2146793fc096SFrank Li } else { 2147793fc096SFrank Li return ethtool_op_get_ts_info(ndev, info); 2148793fc096SFrank Li } 2149793fc096SFrank Li } 2150793fc096SFrank Li 2151d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 2152d1391930SGuenter Roeck 2153793fc096SFrank Li static void fec_enet_get_pauseparam(struct net_device *ndev, 2154793fc096SFrank Li struct ethtool_pauseparam *pause) 2155793fc096SFrank Li { 2156793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2157793fc096SFrank Li 2158793fc096SFrank Li pause->autoneg = (fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) != 0; 2159793fc096SFrank Li pause->tx_pause = (fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) != 0; 2160793fc096SFrank Li pause->rx_pause = pause->tx_pause; 2161793fc096SFrank Li } 2162793fc096SFrank Li 2163793fc096SFrank Li static int fec_enet_set_pauseparam(struct net_device *ndev, 2164793fc096SFrank Li struct ethtool_pauseparam *pause) 2165793fc096SFrank Li { 2166793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2167793fc096SFrank Li 21680b146ca8SRussell King if (!fep->phy_dev) 21690b146ca8SRussell King return -ENODEV; 21700b146ca8SRussell King 2171793fc096SFrank Li if (pause->tx_pause != pause->rx_pause) { 2172793fc096SFrank Li netdev_info(ndev, 2173793fc096SFrank Li "hardware only support enable/disable both tx and rx"); 2174793fc096SFrank Li return -EINVAL; 2175793fc096SFrank Li } 2176793fc096SFrank Li 2177793fc096SFrank Li fep->pause_flag = 0; 2178793fc096SFrank Li 2179793fc096SFrank Li /* tx pause must be same as rx pause */ 2180793fc096SFrank Li fep->pause_flag |= pause->rx_pause ? FEC_PAUSE_FLAG_ENABLE : 0; 2181793fc096SFrank Li fep->pause_flag |= pause->autoneg ? FEC_PAUSE_FLAG_AUTONEG : 0; 2182793fc096SFrank Li 2183793fc096SFrank Li if (pause->rx_pause || pause->autoneg) { 2184793fc096SFrank Li fep->phy_dev->supported |= ADVERTISED_Pause; 2185793fc096SFrank Li fep->phy_dev->advertising |= ADVERTISED_Pause; 2186793fc096SFrank Li } else { 2187793fc096SFrank Li fep->phy_dev->supported &= ~ADVERTISED_Pause; 2188793fc096SFrank Li fep->phy_dev->advertising &= ~ADVERTISED_Pause; 2189793fc096SFrank Li } 2190793fc096SFrank Li 2191793fc096SFrank Li if (pause->autoneg) { 2192793fc096SFrank Li if (netif_running(ndev)) 2193793fc096SFrank Li fec_stop(ndev); 2194793fc096SFrank Li phy_start_aneg(fep->phy_dev); 2195793fc096SFrank Li } 2196dbc64a8eSRussell King if (netif_running(ndev)) { 2197dbc64a8eSRussell King napi_disable(&fep->napi); 2198dbc64a8eSRussell King netif_tx_lock_bh(ndev); 2199ef83337dSRussell King fec_restart(ndev); 2200dbc64a8eSRussell King netif_wake_queue(ndev); 22016af42d42SRussell King netif_tx_unlock_bh(ndev); 2202dbc64a8eSRussell King napi_enable(&fep->napi); 2203dbc64a8eSRussell King } 2204793fc096SFrank Li 2205793fc096SFrank Li return 0; 2206793fc096SFrank Li } 2207793fc096SFrank Li 220838ae92dcSChris Healy static const struct fec_stat { 220938ae92dcSChris Healy char name[ETH_GSTRING_LEN]; 221038ae92dcSChris Healy u16 offset; 221138ae92dcSChris Healy } fec_stats[] = { 221238ae92dcSChris Healy /* RMON TX */ 221338ae92dcSChris Healy { "tx_dropped", RMON_T_DROP }, 221438ae92dcSChris Healy { "tx_packets", RMON_T_PACKETS }, 221538ae92dcSChris Healy { "tx_broadcast", RMON_T_BC_PKT }, 221638ae92dcSChris Healy { "tx_multicast", RMON_T_MC_PKT }, 221738ae92dcSChris Healy { "tx_crc_errors", RMON_T_CRC_ALIGN }, 221838ae92dcSChris Healy { "tx_undersize", RMON_T_UNDERSIZE }, 221938ae92dcSChris Healy { "tx_oversize", RMON_T_OVERSIZE }, 222038ae92dcSChris Healy { "tx_fragment", RMON_T_FRAG }, 222138ae92dcSChris Healy { "tx_jabber", RMON_T_JAB }, 222238ae92dcSChris Healy { "tx_collision", RMON_T_COL }, 222338ae92dcSChris Healy { "tx_64byte", RMON_T_P64 }, 222438ae92dcSChris Healy { "tx_65to127byte", RMON_T_P65TO127 }, 222538ae92dcSChris Healy { "tx_128to255byte", RMON_T_P128TO255 }, 222638ae92dcSChris Healy { "tx_256to511byte", RMON_T_P256TO511 }, 222738ae92dcSChris Healy { "tx_512to1023byte", RMON_T_P512TO1023 }, 222838ae92dcSChris Healy { "tx_1024to2047byte", RMON_T_P1024TO2047 }, 222938ae92dcSChris Healy { "tx_GTE2048byte", RMON_T_P_GTE2048 }, 223038ae92dcSChris Healy { "tx_octets", RMON_T_OCTETS }, 223138ae92dcSChris Healy 223238ae92dcSChris Healy /* IEEE TX */ 223338ae92dcSChris Healy { "IEEE_tx_drop", IEEE_T_DROP }, 223438ae92dcSChris Healy { "IEEE_tx_frame_ok", IEEE_T_FRAME_OK }, 223538ae92dcSChris Healy { "IEEE_tx_1col", IEEE_T_1COL }, 223638ae92dcSChris Healy { "IEEE_tx_mcol", IEEE_T_MCOL }, 223738ae92dcSChris Healy { "IEEE_tx_def", IEEE_T_DEF }, 223838ae92dcSChris Healy { "IEEE_tx_lcol", IEEE_T_LCOL }, 223938ae92dcSChris Healy { "IEEE_tx_excol", IEEE_T_EXCOL }, 224038ae92dcSChris Healy { "IEEE_tx_macerr", IEEE_T_MACERR }, 224138ae92dcSChris Healy { "IEEE_tx_cserr", IEEE_T_CSERR }, 224238ae92dcSChris Healy { "IEEE_tx_sqe", IEEE_T_SQE }, 224338ae92dcSChris Healy { "IEEE_tx_fdxfc", IEEE_T_FDXFC }, 224438ae92dcSChris Healy { "IEEE_tx_octets_ok", IEEE_T_OCTETS_OK }, 224538ae92dcSChris Healy 224638ae92dcSChris Healy /* RMON RX */ 224738ae92dcSChris Healy { "rx_packets", RMON_R_PACKETS }, 224838ae92dcSChris Healy { "rx_broadcast", RMON_R_BC_PKT }, 224938ae92dcSChris Healy { "rx_multicast", RMON_R_MC_PKT }, 225038ae92dcSChris Healy { "rx_crc_errors", RMON_R_CRC_ALIGN }, 225138ae92dcSChris Healy { "rx_undersize", RMON_R_UNDERSIZE }, 225238ae92dcSChris Healy { "rx_oversize", RMON_R_OVERSIZE }, 225338ae92dcSChris Healy { "rx_fragment", RMON_R_FRAG }, 225438ae92dcSChris Healy { "rx_jabber", RMON_R_JAB }, 225538ae92dcSChris Healy { "rx_64byte", RMON_R_P64 }, 225638ae92dcSChris Healy { "rx_65to127byte", RMON_R_P65TO127 }, 225738ae92dcSChris Healy { "rx_128to255byte", RMON_R_P128TO255 }, 225838ae92dcSChris Healy { "rx_256to511byte", RMON_R_P256TO511 }, 225938ae92dcSChris Healy { "rx_512to1023byte", RMON_R_P512TO1023 }, 226038ae92dcSChris Healy { "rx_1024to2047byte", RMON_R_P1024TO2047 }, 226138ae92dcSChris Healy { "rx_GTE2048byte", RMON_R_P_GTE2048 }, 226238ae92dcSChris Healy { "rx_octets", RMON_R_OCTETS }, 226338ae92dcSChris Healy 226438ae92dcSChris Healy /* IEEE RX */ 226538ae92dcSChris Healy { "IEEE_rx_drop", IEEE_R_DROP }, 226638ae92dcSChris Healy { "IEEE_rx_frame_ok", IEEE_R_FRAME_OK }, 226738ae92dcSChris Healy { "IEEE_rx_crc", IEEE_R_CRC }, 226838ae92dcSChris Healy { "IEEE_rx_align", IEEE_R_ALIGN }, 226938ae92dcSChris Healy { "IEEE_rx_macerr", IEEE_R_MACERR }, 227038ae92dcSChris Healy { "IEEE_rx_fdxfc", IEEE_R_FDXFC }, 227138ae92dcSChris Healy { "IEEE_rx_octets_ok", IEEE_R_OCTETS_OK }, 227238ae92dcSChris Healy }; 227338ae92dcSChris Healy 227438ae92dcSChris Healy static void fec_enet_get_ethtool_stats(struct net_device *dev, 227538ae92dcSChris Healy struct ethtool_stats *stats, u64 *data) 227638ae92dcSChris Healy { 227738ae92dcSChris Healy struct fec_enet_private *fep = netdev_priv(dev); 227838ae92dcSChris Healy int i; 227938ae92dcSChris Healy 228038ae92dcSChris Healy for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 228138ae92dcSChris Healy data[i] = readl(fep->hwp + fec_stats[i].offset); 228238ae92dcSChris Healy } 228338ae92dcSChris Healy 228438ae92dcSChris Healy static void fec_enet_get_strings(struct net_device *netdev, 228538ae92dcSChris Healy u32 stringset, u8 *data) 228638ae92dcSChris Healy { 228738ae92dcSChris Healy int i; 228838ae92dcSChris Healy switch (stringset) { 228938ae92dcSChris Healy case ETH_SS_STATS: 229038ae92dcSChris Healy for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 229138ae92dcSChris Healy memcpy(data + i * ETH_GSTRING_LEN, 229238ae92dcSChris Healy fec_stats[i].name, ETH_GSTRING_LEN); 229338ae92dcSChris Healy break; 229438ae92dcSChris Healy } 229538ae92dcSChris Healy } 229638ae92dcSChris Healy 229738ae92dcSChris Healy static int fec_enet_get_sset_count(struct net_device *dev, int sset) 229838ae92dcSChris Healy { 229938ae92dcSChris Healy switch (sset) { 230038ae92dcSChris Healy case ETH_SS_STATS: 230138ae92dcSChris Healy return ARRAY_SIZE(fec_stats); 230238ae92dcSChris Healy default: 230338ae92dcSChris Healy return -EOPNOTSUPP; 230438ae92dcSChris Healy } 230538ae92dcSChris Healy } 2306d1391930SGuenter Roeck #endif /* !defined(CONFIG_M5272) */ 230738ae92dcSChris Healy 230832bc9b46SChris Healy static int fec_enet_nway_reset(struct net_device *dev) 230932bc9b46SChris Healy { 231032bc9b46SChris Healy struct fec_enet_private *fep = netdev_priv(dev); 231132bc9b46SChris Healy struct phy_device *phydev = fep->phy_dev; 231232bc9b46SChris Healy 231332bc9b46SChris Healy if (!phydev) 231432bc9b46SChris Healy return -ENODEV; 231532bc9b46SChris Healy 231632bc9b46SChris Healy return genphy_restart_aneg(phydev); 231732bc9b46SChris Healy } 231832bc9b46SChris Healy 2319d851b47bSFugang Duan /* ITR clock source is enet system clock (clk_ahb). 2320d851b47bSFugang Duan * TCTT unit is cycle_ns * 64 cycle 2321d851b47bSFugang Duan * So, the ICTT value = X us / (cycle_ns * 64) 2322d851b47bSFugang Duan */ 2323d851b47bSFugang Duan static int fec_enet_us_to_itr_clock(struct net_device *ndev, int us) 2324d851b47bSFugang Duan { 2325d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2326d851b47bSFugang Duan 2327d851b47bSFugang Duan return us * (fep->itr_clk_rate / 64000) / 1000; 2328d851b47bSFugang Duan } 2329d851b47bSFugang Duan 2330d851b47bSFugang Duan /* Set threshold for interrupt coalescing */ 2331d851b47bSFugang Duan static void fec_enet_itr_coal_set(struct net_device *ndev) 2332d851b47bSFugang Duan { 2333d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2334d851b47bSFugang Duan const struct platform_device_id *id_entry = 2335d851b47bSFugang Duan platform_get_device_id(fep->pdev); 2336d851b47bSFugang Duan int rx_itr, tx_itr; 2337d851b47bSFugang Duan 2338d851b47bSFugang Duan if (!(id_entry->driver_data & FEC_QUIRK_HAS_AVB)) 2339d851b47bSFugang Duan return; 2340d851b47bSFugang Duan 2341d851b47bSFugang Duan /* Must be greater than zero to avoid unpredictable behavior */ 2342d851b47bSFugang Duan if (!fep->rx_time_itr || !fep->rx_pkts_itr || 2343d851b47bSFugang Duan !fep->tx_time_itr || !fep->tx_pkts_itr) 2344d851b47bSFugang Duan return; 2345d851b47bSFugang Duan 2346d851b47bSFugang Duan /* Select enet system clock as Interrupt Coalescing 2347d851b47bSFugang Duan * timer Clock Source 2348d851b47bSFugang Duan */ 2349d851b47bSFugang Duan rx_itr = FEC_ITR_CLK_SEL; 2350d851b47bSFugang Duan tx_itr = FEC_ITR_CLK_SEL; 2351d851b47bSFugang Duan 2352d851b47bSFugang Duan /* set ICFT and ICTT */ 2353d851b47bSFugang Duan rx_itr |= FEC_ITR_ICFT(fep->rx_pkts_itr); 2354d851b47bSFugang Duan rx_itr |= FEC_ITR_ICTT(fec_enet_us_to_itr_clock(ndev, fep->rx_time_itr)); 2355d851b47bSFugang Duan tx_itr |= FEC_ITR_ICFT(fep->tx_pkts_itr); 2356d851b47bSFugang Duan tx_itr |= FEC_ITR_ICTT(fec_enet_us_to_itr_clock(ndev, fep->tx_time_itr)); 2357d851b47bSFugang Duan 2358d851b47bSFugang Duan rx_itr |= FEC_ITR_EN; 2359d851b47bSFugang Duan tx_itr |= FEC_ITR_EN; 2360d851b47bSFugang Duan 2361d851b47bSFugang Duan writel(tx_itr, fep->hwp + FEC_TXIC0); 2362d851b47bSFugang Duan writel(rx_itr, fep->hwp + FEC_RXIC0); 2363d851b47bSFugang Duan writel(tx_itr, fep->hwp + FEC_TXIC1); 2364d851b47bSFugang Duan writel(rx_itr, fep->hwp + FEC_RXIC1); 2365d851b47bSFugang Duan writel(tx_itr, fep->hwp + FEC_TXIC2); 2366d851b47bSFugang Duan writel(rx_itr, fep->hwp + FEC_RXIC2); 2367d851b47bSFugang Duan } 2368d851b47bSFugang Duan 2369d851b47bSFugang Duan static int 2370d851b47bSFugang Duan fec_enet_get_coalesce(struct net_device *ndev, struct ethtool_coalesce *ec) 2371d851b47bSFugang Duan { 2372d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2373d851b47bSFugang Duan const struct platform_device_id *id_entry = 2374d851b47bSFugang Duan platform_get_device_id(fep->pdev); 2375d851b47bSFugang Duan 2376d851b47bSFugang Duan if (!(id_entry->driver_data & FEC_QUIRK_HAS_AVB)) 2377d851b47bSFugang Duan return -EOPNOTSUPP; 2378d851b47bSFugang Duan 2379d851b47bSFugang Duan ec->rx_coalesce_usecs = fep->rx_time_itr; 2380d851b47bSFugang Duan ec->rx_max_coalesced_frames = fep->rx_pkts_itr; 2381d851b47bSFugang Duan 2382d851b47bSFugang Duan ec->tx_coalesce_usecs = fep->tx_time_itr; 2383d851b47bSFugang Duan ec->tx_max_coalesced_frames = fep->tx_pkts_itr; 2384d851b47bSFugang Duan 2385d851b47bSFugang Duan return 0; 2386d851b47bSFugang Duan } 2387d851b47bSFugang Duan 2388d851b47bSFugang Duan static int 2389d851b47bSFugang Duan fec_enet_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *ec) 2390d851b47bSFugang Duan { 2391d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2392d851b47bSFugang Duan const struct platform_device_id *id_entry = 2393d851b47bSFugang Duan platform_get_device_id(fep->pdev); 2394d851b47bSFugang Duan 2395d851b47bSFugang Duan unsigned int cycle; 2396d851b47bSFugang Duan 2397d851b47bSFugang Duan if (!(id_entry->driver_data & FEC_QUIRK_HAS_AVB)) 2398d851b47bSFugang Duan return -EOPNOTSUPP; 2399d851b47bSFugang Duan 2400d851b47bSFugang Duan if (ec->rx_max_coalesced_frames > 255) { 2401d851b47bSFugang Duan pr_err("Rx coalesced frames exceed hardware limiation"); 2402d851b47bSFugang Duan return -EINVAL; 2403d851b47bSFugang Duan } 2404d851b47bSFugang Duan 2405d851b47bSFugang Duan if (ec->tx_max_coalesced_frames > 255) { 2406d851b47bSFugang Duan pr_err("Tx coalesced frame exceed hardware limiation"); 2407d851b47bSFugang Duan return -EINVAL; 2408d851b47bSFugang Duan } 2409d851b47bSFugang Duan 2410d851b47bSFugang Duan cycle = fec_enet_us_to_itr_clock(ndev, fep->rx_time_itr); 2411d851b47bSFugang Duan if (cycle > 0xFFFF) { 2412d851b47bSFugang Duan pr_err("Rx coalesed usec exceeed hardware limiation"); 2413d851b47bSFugang Duan return -EINVAL; 2414d851b47bSFugang Duan } 2415d851b47bSFugang Duan 2416d851b47bSFugang Duan cycle = fec_enet_us_to_itr_clock(ndev, fep->tx_time_itr); 2417d851b47bSFugang Duan if (cycle > 0xFFFF) { 2418d851b47bSFugang Duan pr_err("Rx coalesed usec exceeed hardware limiation"); 2419d851b47bSFugang Duan return -EINVAL; 2420d851b47bSFugang Duan } 2421d851b47bSFugang Duan 2422d851b47bSFugang Duan fep->rx_time_itr = ec->rx_coalesce_usecs; 2423d851b47bSFugang Duan fep->rx_pkts_itr = ec->rx_max_coalesced_frames; 2424d851b47bSFugang Duan 2425d851b47bSFugang Duan fep->tx_time_itr = ec->tx_coalesce_usecs; 2426d851b47bSFugang Duan fep->tx_pkts_itr = ec->tx_max_coalesced_frames; 2427d851b47bSFugang Duan 2428d851b47bSFugang Duan fec_enet_itr_coal_set(ndev); 2429d851b47bSFugang Duan 2430d851b47bSFugang Duan return 0; 2431d851b47bSFugang Duan } 2432d851b47bSFugang Duan 2433d851b47bSFugang Duan static void fec_enet_itr_coal_init(struct net_device *ndev) 2434d851b47bSFugang Duan { 2435d851b47bSFugang Duan struct ethtool_coalesce ec; 2436d851b47bSFugang Duan 2437d851b47bSFugang Duan ec.rx_coalesce_usecs = FEC_ITR_ICTT_DEFAULT; 2438d851b47bSFugang Duan ec.rx_max_coalesced_frames = FEC_ITR_ICFT_DEFAULT; 2439d851b47bSFugang Duan 2440d851b47bSFugang Duan ec.tx_coalesce_usecs = FEC_ITR_ICTT_DEFAULT; 2441d851b47bSFugang Duan ec.tx_max_coalesced_frames = FEC_ITR_ICFT_DEFAULT; 2442d851b47bSFugang Duan 2443d851b47bSFugang Duan fec_enet_set_coalesce(ndev, &ec); 2444d851b47bSFugang Duan } 2445d851b47bSFugang Duan 24461b7bde6dSNimrod Andy static int fec_enet_get_tunable(struct net_device *netdev, 24471b7bde6dSNimrod Andy const struct ethtool_tunable *tuna, 24481b7bde6dSNimrod Andy void *data) 24491b7bde6dSNimrod Andy { 24501b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(netdev); 24511b7bde6dSNimrod Andy int ret = 0; 24521b7bde6dSNimrod Andy 24531b7bde6dSNimrod Andy switch (tuna->id) { 24541b7bde6dSNimrod Andy case ETHTOOL_RX_COPYBREAK: 24551b7bde6dSNimrod Andy *(u32 *)data = fep->rx_copybreak; 24561b7bde6dSNimrod Andy break; 24571b7bde6dSNimrod Andy default: 24581b7bde6dSNimrod Andy ret = -EINVAL; 24591b7bde6dSNimrod Andy break; 24601b7bde6dSNimrod Andy } 24611b7bde6dSNimrod Andy 24621b7bde6dSNimrod Andy return ret; 24631b7bde6dSNimrod Andy } 24641b7bde6dSNimrod Andy 24651b7bde6dSNimrod Andy static int fec_enet_set_tunable(struct net_device *netdev, 24661b7bde6dSNimrod Andy const struct ethtool_tunable *tuna, 24671b7bde6dSNimrod Andy const void *data) 24681b7bde6dSNimrod Andy { 24691b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(netdev); 24701b7bde6dSNimrod Andy int ret = 0; 24711b7bde6dSNimrod Andy 24721b7bde6dSNimrod Andy switch (tuna->id) { 24731b7bde6dSNimrod Andy case ETHTOOL_RX_COPYBREAK: 24741b7bde6dSNimrod Andy fep->rx_copybreak = *(u32 *)data; 24751b7bde6dSNimrod Andy break; 24761b7bde6dSNimrod Andy default: 24771b7bde6dSNimrod Andy ret = -EINVAL; 24781b7bde6dSNimrod Andy break; 24791b7bde6dSNimrod Andy } 24801b7bde6dSNimrod Andy 24811b7bde6dSNimrod Andy return ret; 24821b7bde6dSNimrod Andy } 24831b7bde6dSNimrod Andy 2484793fc096SFrank Li static const struct ethtool_ops fec_enet_ethtool_ops = { 2485793fc096SFrank Li .get_settings = fec_enet_get_settings, 2486793fc096SFrank Li .set_settings = fec_enet_set_settings, 2487793fc096SFrank Li .get_drvinfo = fec_enet_get_drvinfo, 248832bc9b46SChris Healy .nway_reset = fec_enet_nway_reset, 2489c1d7c48fSRussell King .get_link = ethtool_op_get_link, 2490d851b47bSFugang Duan .get_coalesce = fec_enet_get_coalesce, 2491d851b47bSFugang Duan .set_coalesce = fec_enet_set_coalesce, 249238ae92dcSChris Healy #ifndef CONFIG_M5272 2493c1d7c48fSRussell King .get_pauseparam = fec_enet_get_pauseparam, 2494c1d7c48fSRussell King .set_pauseparam = fec_enet_set_pauseparam, 249538ae92dcSChris Healy .get_strings = fec_enet_get_strings, 2496c1d7c48fSRussell King .get_ethtool_stats = fec_enet_get_ethtool_stats, 249738ae92dcSChris Healy .get_sset_count = fec_enet_get_sset_count, 249838ae92dcSChris Healy #endif 2499c1d7c48fSRussell King .get_ts_info = fec_enet_get_ts_info, 25001b7bde6dSNimrod Andy .get_tunable = fec_enet_get_tunable, 25011b7bde6dSNimrod Andy .set_tunable = fec_enet_set_tunable, 2502793fc096SFrank Li }; 2503793fc096SFrank Li 2504793fc096SFrank Li static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) 2505793fc096SFrank Li { 2506793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2507793fc096SFrank Li struct phy_device *phydev = fep->phy_dev; 2508793fc096SFrank Li 2509793fc096SFrank Li if (!netif_running(ndev)) 2510793fc096SFrank Li return -EINVAL; 2511793fc096SFrank Li 2512793fc096SFrank Li if (!phydev) 2513793fc096SFrank Li return -ENODEV; 2514793fc096SFrank Li 25151d5244d0SBen Hutchings if (fep->bufdesc_ex) { 25161d5244d0SBen Hutchings if (cmd == SIOCSHWTSTAMP) 25171d5244d0SBen Hutchings return fec_ptp_set(ndev, rq); 25181d5244d0SBen Hutchings if (cmd == SIOCGHWTSTAMP) 25191d5244d0SBen Hutchings return fec_ptp_get(ndev, rq); 25201d5244d0SBen Hutchings } 2521793fc096SFrank Li 2522793fc096SFrank Li return phy_mii_ioctl(phydev, rq, cmd); 2523793fc096SFrank Li } 2524793fc096SFrank Li 2525793fc096SFrank Li static void fec_enet_free_buffers(struct net_device *ndev) 2526793fc096SFrank Li { 2527793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2528793fc096SFrank Li unsigned int i; 2529793fc096SFrank Li struct sk_buff *skb; 2530793fc096SFrank Li struct bufdesc *bdp; 25314d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 25324d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 253359d0f746SFrank Li unsigned int q; 2534793fc096SFrank Li 253559d0f746SFrank Li for (q = 0; q < fep->num_rx_queues; q++) { 253659d0f746SFrank Li rxq = fep->rx_queue[q]; 25374d494cdcSFugang Duan bdp = rxq->rx_bd_base; 25384d494cdcSFugang Duan for (i = 0; i < rxq->rx_ring_size; i++) { 25394d494cdcSFugang Duan skb = rxq->rx_skbuff[i]; 25404d494cdcSFugang Duan rxq->rx_skbuff[i] = NULL; 2541730ee360SRussell King if (skb) { 254259d0f746SFrank Li dma_unmap_single(&fep->pdev->dev, 254359d0f746SFrank Li bdp->cbd_bufaddr, 2544b64bf4b7SNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 254559d0f746SFrank Li DMA_FROM_DEVICE); 2546793fc096SFrank Li dev_kfree_skb(skb); 2547730ee360SRussell King } 254859d0f746SFrank Li bdp = fec_enet_get_nextdesc(bdp, fep, q); 254959d0f746SFrank Li } 2550793fc096SFrank Li } 2551793fc096SFrank Li 255259d0f746SFrank Li for (q = 0; q < fep->num_tx_queues; q++) { 255359d0f746SFrank Li txq = fep->tx_queue[q]; 25544d494cdcSFugang Duan bdp = txq->tx_bd_base; 25554d494cdcSFugang Duan for (i = 0; i < txq->tx_ring_size; i++) { 25564d494cdcSFugang Duan kfree(txq->tx_bounce[i]); 25574d494cdcSFugang Duan txq->tx_bounce[i] = NULL; 25584d494cdcSFugang Duan skb = txq->tx_skbuff[i]; 25594d494cdcSFugang Duan txq->tx_skbuff[i] = NULL; 25608b7c9efaSRussell King dev_kfree_skb(skb); 25618b7c9efaSRussell King } 2562793fc096SFrank Li } 256359d0f746SFrank Li } 2564793fc096SFrank Li 256559d0f746SFrank Li static void fec_enet_free_queue(struct net_device *ndev) 256659d0f746SFrank Li { 256759d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 256859d0f746SFrank Li int i; 256959d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 257059d0f746SFrank Li 257159d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) 257259d0f746SFrank Li if (fep->tx_queue[i] && fep->tx_queue[i]->tso_hdrs) { 257359d0f746SFrank Li txq = fep->tx_queue[i]; 257459d0f746SFrank Li dma_free_coherent(NULL, 257559d0f746SFrank Li txq->tx_ring_size * TSO_HEADER_SIZE, 257659d0f746SFrank Li txq->tso_hdrs, 257759d0f746SFrank Li txq->tso_hdrs_dma); 257859d0f746SFrank Li } 257959d0f746SFrank Li 258059d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) 258159d0f746SFrank Li if (fep->rx_queue[i]) 258259d0f746SFrank Li kfree(fep->rx_queue[i]); 258359d0f746SFrank Li 258459d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) 258559d0f746SFrank Li if (fep->tx_queue[i]) 258659d0f746SFrank Li kfree(fep->tx_queue[i]); 258759d0f746SFrank Li } 258859d0f746SFrank Li 258959d0f746SFrank Li static int fec_enet_alloc_queue(struct net_device *ndev) 259059d0f746SFrank Li { 259159d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 259259d0f746SFrank Li int i; 259359d0f746SFrank Li int ret = 0; 259459d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 259559d0f746SFrank Li 259659d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 259759d0f746SFrank Li txq = kzalloc(sizeof(*txq), GFP_KERNEL); 259859d0f746SFrank Li if (!txq) { 259959d0f746SFrank Li ret = -ENOMEM; 260059d0f746SFrank Li goto alloc_failed; 260159d0f746SFrank Li } 260259d0f746SFrank Li 260359d0f746SFrank Li fep->tx_queue[i] = txq; 260459d0f746SFrank Li txq->tx_ring_size = TX_RING_SIZE; 260559d0f746SFrank Li fep->total_tx_ring_size += fep->tx_queue[i]->tx_ring_size; 260659d0f746SFrank Li 260759d0f746SFrank Li txq->tx_stop_threshold = FEC_MAX_SKB_DESCS; 260859d0f746SFrank Li txq->tx_wake_threshold = 260959d0f746SFrank Li (txq->tx_ring_size - txq->tx_stop_threshold) / 2; 261059d0f746SFrank Li 261159d0f746SFrank Li txq->tso_hdrs = dma_alloc_coherent(NULL, 261259d0f746SFrank Li txq->tx_ring_size * TSO_HEADER_SIZE, 261359d0f746SFrank Li &txq->tso_hdrs_dma, 261459d0f746SFrank Li GFP_KERNEL); 261559d0f746SFrank Li if (!txq->tso_hdrs) { 261659d0f746SFrank Li ret = -ENOMEM; 261759d0f746SFrank Li goto alloc_failed; 261859d0f746SFrank Li } 261959d0f746SFrank Li } 262059d0f746SFrank Li 262159d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) { 262259d0f746SFrank Li fep->rx_queue[i] = kzalloc(sizeof(*fep->rx_queue[i]), 262359d0f746SFrank Li GFP_KERNEL); 262459d0f746SFrank Li if (!fep->rx_queue[i]) { 262559d0f746SFrank Li ret = -ENOMEM; 262659d0f746SFrank Li goto alloc_failed; 262759d0f746SFrank Li } 262859d0f746SFrank Li 262959d0f746SFrank Li fep->rx_queue[i]->rx_ring_size = RX_RING_SIZE; 263059d0f746SFrank Li fep->total_rx_ring_size += fep->rx_queue[i]->rx_ring_size; 263159d0f746SFrank Li } 263259d0f746SFrank Li return ret; 263359d0f746SFrank Li 263459d0f746SFrank Li alloc_failed: 263559d0f746SFrank Li fec_enet_free_queue(ndev); 263659d0f746SFrank Li return ret; 263759d0f746SFrank Li } 263859d0f746SFrank Li 263959d0f746SFrank Li static int 264059d0f746SFrank Li fec_enet_alloc_rxq_buffers(struct net_device *ndev, unsigned int queue) 2641793fc096SFrank Li { 2642793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2643793fc096SFrank Li unsigned int i; 2644793fc096SFrank Li struct sk_buff *skb; 2645793fc096SFrank Li struct bufdesc *bdp; 26464d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 2647793fc096SFrank Li 264859d0f746SFrank Li rxq = fep->rx_queue[queue]; 26494d494cdcSFugang Duan bdp = rxq->rx_bd_base; 26504d494cdcSFugang Duan for (i = 0; i < rxq->rx_ring_size; i++) { 2651793fc096SFrank Li skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE); 2652ffdce2ccSRussell King if (!skb) 2653ffdce2ccSRussell King goto err_alloc; 2654793fc096SFrank Li 26551b7bde6dSNimrod Andy if (fec_enet_new_rxbdp(ndev, bdp, skb)) { 2656730ee360SRussell King dev_kfree_skb(skb); 2657ffdce2ccSRussell King goto err_alloc; 2658d842a31fSDuan Fugang-B38611 } 2659730ee360SRussell King 26604d494cdcSFugang Duan rxq->rx_skbuff[i] = skb; 2661793fc096SFrank Li bdp->cbd_sc = BD_ENET_RX_EMPTY; 2662793fc096SFrank Li 2663793fc096SFrank Li if (fep->bufdesc_ex) { 2664793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 2665793fc096SFrank Li ebdp->cbd_esc = BD_ENET_RX_INT; 2666793fc096SFrank Li } 2667793fc096SFrank Li 266859d0f746SFrank Li bdp = fec_enet_get_nextdesc(bdp, fep, queue); 2669793fc096SFrank Li } 2670793fc096SFrank Li 2671793fc096SFrank Li /* Set the last buffer to wrap. */ 267259d0f746SFrank Li bdp = fec_enet_get_prevdesc(bdp, fep, queue); 2673793fc096SFrank Li bdp->cbd_sc |= BD_SC_WRAP; 267459d0f746SFrank Li return 0; 2675793fc096SFrank Li 267659d0f746SFrank Li err_alloc: 267759d0f746SFrank Li fec_enet_free_buffers(ndev); 267859d0f746SFrank Li return -ENOMEM; 267959d0f746SFrank Li } 268059d0f746SFrank Li 268159d0f746SFrank Li static int 268259d0f746SFrank Li fec_enet_alloc_txq_buffers(struct net_device *ndev, unsigned int queue) 268359d0f746SFrank Li { 268459d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 268559d0f746SFrank Li unsigned int i; 268659d0f746SFrank Li struct bufdesc *bdp; 268759d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 268859d0f746SFrank Li 268959d0f746SFrank Li txq = fep->tx_queue[queue]; 26904d494cdcSFugang Duan bdp = txq->tx_bd_base; 26914d494cdcSFugang Duan for (i = 0; i < txq->tx_ring_size; i++) { 26924d494cdcSFugang Duan txq->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL); 26934d494cdcSFugang Duan if (!txq->tx_bounce[i]) 2694ffdce2ccSRussell King goto err_alloc; 2695793fc096SFrank Li 2696793fc096SFrank Li bdp->cbd_sc = 0; 2697793fc096SFrank Li bdp->cbd_bufaddr = 0; 2698793fc096SFrank Li 2699793fc096SFrank Li if (fep->bufdesc_ex) { 2700793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 270196d2222bSJim Baxter ebdp->cbd_esc = BD_ENET_TX_INT; 2702793fc096SFrank Li } 2703793fc096SFrank Li 270459d0f746SFrank Li bdp = fec_enet_get_nextdesc(bdp, fep, queue); 2705793fc096SFrank Li } 2706793fc096SFrank Li 2707793fc096SFrank Li /* Set the last buffer to wrap. */ 270859d0f746SFrank Li bdp = fec_enet_get_prevdesc(bdp, fep, queue); 2709793fc096SFrank Li bdp->cbd_sc |= BD_SC_WRAP; 2710793fc096SFrank Li 2711793fc096SFrank Li return 0; 2712ffdce2ccSRussell King 2713ffdce2ccSRussell King err_alloc: 2714ffdce2ccSRussell King fec_enet_free_buffers(ndev); 2715ffdce2ccSRussell King return -ENOMEM; 2716793fc096SFrank Li } 2717793fc096SFrank Li 271859d0f746SFrank Li static int fec_enet_alloc_buffers(struct net_device *ndev) 271959d0f746SFrank Li { 272059d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 272159d0f746SFrank Li unsigned int i; 272259d0f746SFrank Li 272359d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) 272459d0f746SFrank Li if (fec_enet_alloc_rxq_buffers(ndev, i)) 272559d0f746SFrank Li return -ENOMEM; 272659d0f746SFrank Li 272759d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) 272859d0f746SFrank Li if (fec_enet_alloc_txq_buffers(ndev, i)) 272959d0f746SFrank Li return -ENOMEM; 273059d0f746SFrank Li return 0; 273159d0f746SFrank Li } 273259d0f746SFrank Li 2733793fc096SFrank Li static int 2734793fc096SFrank Li fec_enet_open(struct net_device *ndev) 2735793fc096SFrank Li { 2736793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2737793fc096SFrank Li int ret; 2738793fc096SFrank Li 27395bbde4d2SNimrod Andy pinctrl_pm_select_default_state(&fep->pdev->dev); 2740e8fcfcd5SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 2741e8fcfcd5SNimrod Andy if (ret) 2742e8fcfcd5SNimrod Andy return ret; 2743e8fcfcd5SNimrod Andy 2744793fc096SFrank Li /* I should reset the ring buffers here, but I don't yet know 2745793fc096SFrank Li * a simple way to do that. 2746793fc096SFrank Li */ 2747793fc096SFrank Li 2748793fc096SFrank Li ret = fec_enet_alloc_buffers(ndev); 2749793fc096SFrank Li if (ret) 2750681d2421SFabio Estevam goto err_enet_alloc; 2751793fc096SFrank Li 2752793fc096SFrank Li /* Probe and connect to PHY when open the interface */ 2753793fc096SFrank Li ret = fec_enet_mii_probe(ndev); 2754681d2421SFabio Estevam if (ret) 2755681d2421SFabio Estevam goto err_enet_mii_probe; 2756ce5eaf02SRussell King 2757ef83337dSRussell King fec_restart(ndev); 2758ce5eaf02SRussell King napi_enable(&fep->napi); 2759793fc096SFrank Li phy_start(fep->phy_dev); 27604d494cdcSFugang Duan netif_tx_start_all_queues(ndev); 27614d494cdcSFugang Duan 2762793fc096SFrank Li return 0; 2763681d2421SFabio Estevam 2764681d2421SFabio Estevam err_enet_mii_probe: 2765681d2421SFabio Estevam fec_enet_free_buffers(ndev); 2766681d2421SFabio Estevam err_enet_alloc: 2767681d2421SFabio Estevam fec_enet_clk_enable(ndev, false); 2768681d2421SFabio Estevam pinctrl_pm_select_sleep_state(&fep->pdev->dev); 2769681d2421SFabio Estevam return ret; 2770793fc096SFrank Li } 2771793fc096SFrank Li 2772793fc096SFrank Li static int 2773793fc096SFrank Li fec_enet_close(struct net_device *ndev) 2774793fc096SFrank Li { 2775793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2776793fc096SFrank Li 2777d76cfae9SRussell King phy_stop(fep->phy_dev); 2778d76cfae9SRussell King 277931a6de34SRussell King if (netif_device_present(ndev)) { 2780793fc096SFrank Li napi_disable(&fep->napi); 2781b49cd504SRussell King netif_tx_disable(ndev); 2782793fc096SFrank Li fec_stop(ndev); 278331a6de34SRussell King } 2784793fc096SFrank Li 2785793fc096SFrank Li phy_disconnect(fep->phy_dev); 27860b146ca8SRussell King fep->phy_dev = NULL; 2787793fc096SFrank Li 2788e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 27895bbde4d2SNimrod Andy pinctrl_pm_select_sleep_state(&fep->pdev->dev); 2790793fc096SFrank Li fec_enet_free_buffers(ndev); 2791793fc096SFrank Li 2792793fc096SFrank Li return 0; 2793793fc096SFrank Li } 2794793fc096SFrank Li 2795793fc096SFrank Li /* Set or clear the multicast filter for this adaptor. 2796793fc096SFrank Li * Skeleton taken from sunlance driver. 2797793fc096SFrank Li * The CPM Ethernet implementation allows Multicast as well as individual 2798793fc096SFrank Li * MAC address filtering. Some of the drivers check to make sure it is 2799793fc096SFrank Li * a group multicast address, and discard those that are not. I guess I 2800793fc096SFrank Li * will do the same for now, but just remove the test if you want 2801793fc096SFrank Li * individual filtering as well (do the upper net layers want or support 2802793fc096SFrank Li * this kind of feature?). 2803793fc096SFrank Li */ 2804793fc096SFrank Li 2805793fc096SFrank Li #define HASH_BITS 6 /* #bits in hash */ 2806793fc096SFrank Li #define CRC32_POLY 0xEDB88320 2807793fc096SFrank Li 2808793fc096SFrank Li static void set_multicast_list(struct net_device *ndev) 2809793fc096SFrank Li { 2810793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2811793fc096SFrank Li struct netdev_hw_addr *ha; 2812793fc096SFrank Li unsigned int i, bit, data, crc, tmp; 2813793fc096SFrank Li unsigned char hash; 2814793fc096SFrank Li 2815793fc096SFrank Li if (ndev->flags & IFF_PROMISC) { 2816793fc096SFrank Li tmp = readl(fep->hwp + FEC_R_CNTRL); 2817793fc096SFrank Li tmp |= 0x8; 2818793fc096SFrank Li writel(tmp, fep->hwp + FEC_R_CNTRL); 2819793fc096SFrank Li return; 2820793fc096SFrank Li } 2821793fc096SFrank Li 2822793fc096SFrank Li tmp = readl(fep->hwp + FEC_R_CNTRL); 2823793fc096SFrank Li tmp &= ~0x8; 2824793fc096SFrank Li writel(tmp, fep->hwp + FEC_R_CNTRL); 2825793fc096SFrank Li 2826793fc096SFrank Li if (ndev->flags & IFF_ALLMULTI) { 2827793fc096SFrank Li /* Catch all multicast addresses, so set the 2828793fc096SFrank Li * filter to all 1's 2829793fc096SFrank Li */ 2830793fc096SFrank Li writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 2831793fc096SFrank Li writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 2832793fc096SFrank Li 2833793fc096SFrank Li return; 2834793fc096SFrank Li } 2835793fc096SFrank Li 2836793fc096SFrank Li /* Clear filter and add the addresses in hash register 2837793fc096SFrank Li */ 2838793fc096SFrank Li writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 2839793fc096SFrank Li writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 2840793fc096SFrank Li 2841793fc096SFrank Li netdev_for_each_mc_addr(ha, ndev) { 2842793fc096SFrank Li /* calculate crc32 value of mac address */ 2843793fc096SFrank Li crc = 0xffffffff; 2844793fc096SFrank Li 2845793fc096SFrank Li for (i = 0; i < ndev->addr_len; i++) { 2846793fc096SFrank Li data = ha->addr[i]; 2847793fc096SFrank Li for (bit = 0; bit < 8; bit++, data >>= 1) { 2848793fc096SFrank Li crc = (crc >> 1) ^ 2849793fc096SFrank Li (((crc ^ data) & 1) ? CRC32_POLY : 0); 2850793fc096SFrank Li } 2851793fc096SFrank Li } 2852793fc096SFrank Li 2853793fc096SFrank Li /* only upper 6 bits (HASH_BITS) are used 2854793fc096SFrank Li * which point to specific bit in he hash registers 2855793fc096SFrank Li */ 2856793fc096SFrank Li hash = (crc >> (32 - HASH_BITS)) & 0x3f; 2857793fc096SFrank Li 2858793fc096SFrank Li if (hash > 31) { 2859793fc096SFrank Li tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 2860793fc096SFrank Li tmp |= 1 << (hash - 32); 2861793fc096SFrank Li writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 2862793fc096SFrank Li } else { 2863793fc096SFrank Li tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_LOW); 2864793fc096SFrank Li tmp |= 1 << hash; 2865793fc096SFrank Li writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 2866793fc096SFrank Li } 2867793fc096SFrank Li } 2868793fc096SFrank Li } 2869793fc096SFrank Li 2870793fc096SFrank Li /* Set a MAC change in hardware. */ 2871793fc096SFrank Li static int 2872793fc096SFrank Li fec_set_mac_address(struct net_device *ndev, void *p) 2873793fc096SFrank Li { 2874793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2875793fc096SFrank Li struct sockaddr *addr = p; 2876793fc096SFrank Li 287744934facSLucas Stach if (addr) { 2878793fc096SFrank Li if (!is_valid_ether_addr(addr->sa_data)) 2879793fc096SFrank Li return -EADDRNOTAVAIL; 2880793fc096SFrank Li memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len); 288144934facSLucas Stach } 2882793fc096SFrank Li 2883793fc096SFrank Li writel(ndev->dev_addr[3] | (ndev->dev_addr[2] << 8) | 2884793fc096SFrank Li (ndev->dev_addr[1] << 16) | (ndev->dev_addr[0] << 24), 2885793fc096SFrank Li fep->hwp + FEC_ADDR_LOW); 2886793fc096SFrank Li writel((ndev->dev_addr[5] << 16) | (ndev->dev_addr[4] << 24), 2887793fc096SFrank Li fep->hwp + FEC_ADDR_HIGH); 2888793fc096SFrank Li return 0; 2889793fc096SFrank Li } 2890793fc096SFrank Li 2891793fc096SFrank Li #ifdef CONFIG_NET_POLL_CONTROLLER 2892793fc096SFrank Li /** 2893793fc096SFrank Li * fec_poll_controller - FEC Poll controller function 2894793fc096SFrank Li * @dev: The FEC network adapter 2895793fc096SFrank Li * 2896793fc096SFrank Li * Polled functionality used by netconsole and others in non interrupt mode 2897793fc096SFrank Li * 2898793fc096SFrank Li */ 2899793fc096SFrank Li static void fec_poll_controller(struct net_device *dev) 2900793fc096SFrank Li { 2901793fc096SFrank Li int i; 2902793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(dev); 2903793fc096SFrank Li 2904793fc096SFrank Li for (i = 0; i < FEC_IRQ_NUM; i++) { 2905793fc096SFrank Li if (fep->irq[i] > 0) { 2906793fc096SFrank Li disable_irq(fep->irq[i]); 2907793fc096SFrank Li fec_enet_interrupt(fep->irq[i], dev); 2908793fc096SFrank Li enable_irq(fep->irq[i]); 2909793fc096SFrank Li } 2910793fc096SFrank Li } 2911793fc096SFrank Li } 2912793fc096SFrank Li #endif 2913793fc096SFrank Li 29148506fa1dSRussell King #define FEATURES_NEED_QUIESCE NETIF_F_RXCSUM 2915*5bc26726SNimrod Andy static inline void fec_enet_set_netdev_features(struct net_device *netdev, 29164c09eed9SJim Baxter netdev_features_t features) 29174c09eed9SJim Baxter { 29184c09eed9SJim Baxter struct fec_enet_private *fep = netdev_priv(netdev); 29194c09eed9SJim Baxter netdev_features_t changed = features ^ netdev->features; 29204c09eed9SJim Baxter 29214c09eed9SJim Baxter netdev->features = features; 29224c09eed9SJim Baxter 29234c09eed9SJim Baxter /* Receive checksum has been changed */ 29244c09eed9SJim Baxter if (changed & NETIF_F_RXCSUM) { 29254c09eed9SJim Baxter if (features & NETIF_F_RXCSUM) 29264c09eed9SJim Baxter fep->csum_flags |= FLAG_RX_CSUM_ENABLED; 29274c09eed9SJim Baxter else 29284c09eed9SJim Baxter fep->csum_flags &= ~FLAG_RX_CSUM_ENABLED; 29298506fa1dSRussell King } 2930*5bc26726SNimrod Andy } 29314c09eed9SJim Baxter 2932*5bc26726SNimrod Andy static int fec_set_features(struct net_device *netdev, 2933*5bc26726SNimrod Andy netdev_features_t features) 2934*5bc26726SNimrod Andy { 2935*5bc26726SNimrod Andy struct fec_enet_private *fep = netdev_priv(netdev); 2936*5bc26726SNimrod Andy netdev_features_t changed = features ^ netdev->features; 2937*5bc26726SNimrod Andy 29388506fa1dSRussell King if (netif_running(netdev) && changed & FEATURES_NEED_QUIESCE) { 2939*5bc26726SNimrod Andy napi_disable(&fep->napi); 2940*5bc26726SNimrod Andy netif_tx_lock_bh(netdev); 2941*5bc26726SNimrod Andy fec_stop(netdev); 2942*5bc26726SNimrod Andy fec_enet_set_netdev_features(netdev, features); 2943ef83337dSRussell King fec_restart(netdev); 29444d494cdcSFugang Duan netif_tx_wake_all_queues(netdev); 29456af42d42SRussell King netif_tx_unlock_bh(netdev); 2946dbc64a8eSRussell King napi_enable(&fep->napi); 2947*5bc26726SNimrod Andy } else { 2948*5bc26726SNimrod Andy fec_enet_set_netdev_features(netdev, features); 29494c09eed9SJim Baxter } 29504c09eed9SJim Baxter 29514c09eed9SJim Baxter return 0; 29524c09eed9SJim Baxter } 29534c09eed9SJim Baxter 2954793fc096SFrank Li static const struct net_device_ops fec_netdev_ops = { 2955793fc096SFrank Li .ndo_open = fec_enet_open, 2956793fc096SFrank Li .ndo_stop = fec_enet_close, 2957793fc096SFrank Li .ndo_start_xmit = fec_enet_start_xmit, 2958793fc096SFrank Li .ndo_set_rx_mode = set_multicast_list, 2959793fc096SFrank Li .ndo_change_mtu = eth_change_mtu, 2960793fc096SFrank Li .ndo_validate_addr = eth_validate_addr, 2961793fc096SFrank Li .ndo_tx_timeout = fec_timeout, 2962793fc096SFrank Li .ndo_set_mac_address = fec_set_mac_address, 2963793fc096SFrank Li .ndo_do_ioctl = fec_enet_ioctl, 2964793fc096SFrank Li #ifdef CONFIG_NET_POLL_CONTROLLER 2965793fc096SFrank Li .ndo_poll_controller = fec_poll_controller, 2966793fc096SFrank Li #endif 29674c09eed9SJim Baxter .ndo_set_features = fec_set_features, 2968793fc096SFrank Li }; 2969793fc096SFrank Li 2970793fc096SFrank Li /* 2971793fc096SFrank Li * XXX: We need to clean up on failure exits here. 2972793fc096SFrank Li * 2973793fc096SFrank Li */ 2974793fc096SFrank Li static int fec_enet_init(struct net_device *ndev) 2975793fc096SFrank Li { 2976793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 297748496255SShawn Guo const struct platform_device_id *id_entry = 297848496255SShawn Guo platform_get_device_id(fep->pdev); 29794d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 29804d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 2981793fc096SFrank Li struct bufdesc *cbd_base; 29824d494cdcSFugang Duan dma_addr_t bd_dma; 298355d0218aSNimrod Andy int bd_size; 298459d0f746SFrank Li unsigned int i; 298555d0218aSNimrod Andy 298641ef84ceSFugang Duan #if defined(CONFIG_ARM) 298741ef84ceSFugang Duan fep->rx_align = 0xf; 298841ef84ceSFugang Duan fep->tx_align = 0xf; 298941ef84ceSFugang Duan #else 299041ef84ceSFugang Duan fep->rx_align = 0x3; 299141ef84ceSFugang Duan fep->tx_align = 0x3; 299241ef84ceSFugang Duan #endif 299341ef84ceSFugang Duan 299459d0f746SFrank Li fec_enet_alloc_queue(ndev); 299579f33912SNimrod Andy 299655d0218aSNimrod Andy if (fep->bufdesc_ex) 299755d0218aSNimrod Andy fep->bufdesc_size = sizeof(struct bufdesc_ex); 299855d0218aSNimrod Andy else 299955d0218aSNimrod Andy fep->bufdesc_size = sizeof(struct bufdesc); 30004d494cdcSFugang Duan bd_size = (fep->total_tx_ring_size + fep->total_rx_ring_size) * 300155d0218aSNimrod Andy fep->bufdesc_size; 3002793fc096SFrank Li 3003793fc096SFrank Li /* Allocate memory for buffer descriptors. */ 30044d494cdcSFugang Duan cbd_base = dma_alloc_coherent(NULL, bd_size, &bd_dma, 3005793fc096SFrank Li GFP_KERNEL); 30064d494cdcSFugang Duan if (!cbd_base) { 30074d494cdcSFugang Duan return -ENOMEM; 30084d494cdcSFugang Duan } 3009793fc096SFrank Li 30104d494cdcSFugang Duan memset(cbd_base, 0, bd_size); 3011793fc096SFrank Li 3012793fc096SFrank Li /* Get the Ethernet address */ 3013793fc096SFrank Li fec_get_mac(ndev); 301444934facSLucas Stach /* make sure MAC we just acquired is programmed into the hw */ 301544934facSLucas Stach fec_set_mac_address(ndev, NULL); 3016793fc096SFrank Li 3017793fc096SFrank Li /* Set receive and transmit descriptor base. */ 301859d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) { 301959d0f746SFrank Li rxq = fep->rx_queue[i]; 302059d0f746SFrank Li rxq->index = i; 302159d0f746SFrank Li rxq->rx_bd_base = (struct bufdesc *)cbd_base; 302259d0f746SFrank Li rxq->bd_dma = bd_dma; 302359d0f746SFrank Li if (fep->bufdesc_ex) { 302459d0f746SFrank Li bd_dma += sizeof(struct bufdesc_ex) * rxq->rx_ring_size; 302559d0f746SFrank Li cbd_base = (struct bufdesc *) 30264d494cdcSFugang Duan (((struct bufdesc_ex *)cbd_base) + rxq->rx_ring_size); 302759d0f746SFrank Li } else { 302859d0f746SFrank Li bd_dma += sizeof(struct bufdesc) * rxq->rx_ring_size; 302959d0f746SFrank Li cbd_base += rxq->rx_ring_size; 303059d0f746SFrank Li } 303159d0f746SFrank Li } 303259d0f746SFrank Li 303359d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 303459d0f746SFrank Li txq = fep->tx_queue[i]; 303559d0f746SFrank Li txq->index = i; 303659d0f746SFrank Li txq->tx_bd_base = (struct bufdesc *)cbd_base; 303759d0f746SFrank Li txq->bd_dma = bd_dma; 303859d0f746SFrank Li if (fep->bufdesc_ex) { 303959d0f746SFrank Li bd_dma += sizeof(struct bufdesc_ex) * txq->tx_ring_size; 304059d0f746SFrank Li cbd_base = (struct bufdesc *) 304159d0f746SFrank Li (((struct bufdesc_ex *)cbd_base) + txq->tx_ring_size); 304259d0f746SFrank Li } else { 304359d0f746SFrank Li bd_dma += sizeof(struct bufdesc) * txq->tx_ring_size; 304459d0f746SFrank Li cbd_base += txq->tx_ring_size; 304559d0f746SFrank Li } 304659d0f746SFrank Li } 30474d494cdcSFugang Duan 3048793fc096SFrank Li 3049793fc096SFrank Li /* The FEC Ethernet specific entries in the device structure */ 3050793fc096SFrank Li ndev->watchdog_timeo = TX_TIMEOUT; 3051793fc096SFrank Li ndev->netdev_ops = &fec_netdev_ops; 3052793fc096SFrank Li ndev->ethtool_ops = &fec_enet_ethtool_ops; 3053793fc096SFrank Li 3054793fc096SFrank Li writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK); 3055322555f5SFabio Estevam netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, NAPI_POLL_WEIGHT); 3056793fc096SFrank Li 305709d1e541SNimrod Andy if (id_entry->driver_data & FEC_QUIRK_HAS_VLAN) 3058cdffcf1bSJim Baxter /* enable hw VLAN support */ 3059cdffcf1bSJim Baxter ndev->features |= NETIF_F_HW_VLAN_CTAG_RX; 3060cdffcf1bSJim Baxter 306148496255SShawn Guo if (id_entry->driver_data & FEC_QUIRK_HAS_CSUM) { 306279f33912SNimrod Andy ndev->gso_max_segs = FEC_MAX_TSO_SEGS; 306379f33912SNimrod Andy 30644c09eed9SJim Baxter /* enable hw accelerator */ 30654c09eed9SJim Baxter ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM 306679f33912SNimrod Andy | NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO); 30674c09eed9SJim Baxter fep->csum_flags |= FLAG_RX_CSUM_ENABLED; 306848496255SShawn Guo } 30694c09eed9SJim Baxter 307041ef84ceSFugang Duan if (id_entry->driver_data & FEC_QUIRK_HAS_AVB) { 307141ef84ceSFugang Duan fep->tx_align = 0; 307241ef84ceSFugang Duan fep->rx_align = 0x3f; 307341ef84ceSFugang Duan } 307441ef84ceSFugang Duan 307509d1e541SNimrod Andy ndev->hw_features = ndev->features; 307609d1e541SNimrod Andy 3077ef83337dSRussell King fec_restart(ndev); 3078793fc096SFrank Li 3079793fc096SFrank Li return 0; 3080793fc096SFrank Li } 3081793fc096SFrank Li 3082793fc096SFrank Li #ifdef CONFIG_OF 3083793fc096SFrank Li static void fec_reset_phy(struct platform_device *pdev) 3084793fc096SFrank Li { 3085793fc096SFrank Li int err, phy_reset; 3086793fc096SFrank Li int msec = 1; 3087793fc096SFrank Li struct device_node *np = pdev->dev.of_node; 3088793fc096SFrank Li 3089793fc096SFrank Li if (!np) 3090793fc096SFrank Li return; 3091793fc096SFrank Li 3092793fc096SFrank Li of_property_read_u32(np, "phy-reset-duration", &msec); 3093793fc096SFrank Li /* A sane reset duration should not be longer than 1s */ 3094793fc096SFrank Li if (msec > 1000) 3095793fc096SFrank Li msec = 1; 3096793fc096SFrank Li 3097793fc096SFrank Li phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0); 3098793fc096SFrank Li if (!gpio_is_valid(phy_reset)) 3099793fc096SFrank Li return; 3100793fc096SFrank Li 3101793fc096SFrank Li err = devm_gpio_request_one(&pdev->dev, phy_reset, 3102793fc096SFrank Li GPIOF_OUT_INIT_LOW, "phy-reset"); 3103793fc096SFrank Li if (err) { 3104793fc096SFrank Li dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err); 3105793fc096SFrank Li return; 3106793fc096SFrank Li } 3107793fc096SFrank Li msleep(msec); 3108793fc096SFrank Li gpio_set_value(phy_reset, 1); 3109793fc096SFrank Li } 3110793fc096SFrank Li #else /* CONFIG_OF */ 3111793fc096SFrank Li static void fec_reset_phy(struct platform_device *pdev) 3112793fc096SFrank Li { 3113793fc096SFrank Li /* 3114793fc096SFrank Li * In case of platform probe, the reset has been done 3115793fc096SFrank Li * by machine code. 3116793fc096SFrank Li */ 3117793fc096SFrank Li } 3118793fc096SFrank Li #endif /* CONFIG_OF */ 3119793fc096SFrank Li 31209fc095f1SFugang Duan static void 31219fc095f1SFugang Duan fec_enet_get_queue_num(struct platform_device *pdev, int *num_tx, int *num_rx) 31229fc095f1SFugang Duan { 31239fc095f1SFugang Duan struct device_node *np = pdev->dev.of_node; 31249fc095f1SFugang Duan int err; 31259fc095f1SFugang Duan 31269fc095f1SFugang Duan *num_tx = *num_rx = 1; 31279fc095f1SFugang Duan 31289fc095f1SFugang Duan if (!np || !of_device_is_available(np)) 31299fc095f1SFugang Duan return; 31309fc095f1SFugang Duan 31319fc095f1SFugang Duan /* parse the num of tx and rx queues */ 31329fc095f1SFugang Duan err = of_property_read_u32(np, "fsl,num-tx-queues", num_tx); 3133b7bd75cfSFrank Li if (err) 31349fc095f1SFugang Duan *num_tx = 1; 3135b7bd75cfSFrank Li 3136b7bd75cfSFrank Li err = of_property_read_u32(np, "fsl,num-rx-queues", num_rx); 3137b7bd75cfSFrank Li if (err) 31389fc095f1SFugang Duan *num_rx = 1; 31399fc095f1SFugang Duan 31409fc095f1SFugang Duan if (*num_tx < 1 || *num_tx > FEC_ENET_MAX_TX_QS) { 3141b7bd75cfSFrank Li dev_warn(&pdev->dev, "Invalid num_tx(=%d), fall back to 1\n", 31429fc095f1SFugang Duan *num_tx); 31439fc095f1SFugang Duan *num_tx = 1; 31449fc095f1SFugang Duan return; 31459fc095f1SFugang Duan } 31469fc095f1SFugang Duan 31479fc095f1SFugang Duan if (*num_rx < 1 || *num_rx > FEC_ENET_MAX_RX_QS) { 3148b7bd75cfSFrank Li dev_warn(&pdev->dev, "Invalid num_rx(=%d), fall back to 1\n", 31499fc095f1SFugang Duan *num_rx); 31509fc095f1SFugang Duan *num_rx = 1; 31519fc095f1SFugang Duan return; 31529fc095f1SFugang Duan } 31539fc095f1SFugang Duan 31549fc095f1SFugang Duan } 31559fc095f1SFugang Duan 3156793fc096SFrank Li static int 3157793fc096SFrank Li fec_probe(struct platform_device *pdev) 3158793fc096SFrank Li { 3159793fc096SFrank Li struct fec_enet_private *fep; 3160793fc096SFrank Li struct fec_platform_data *pdata; 3161793fc096SFrank Li struct net_device *ndev; 3162793fc096SFrank Li int i, irq, ret = 0; 3163793fc096SFrank Li struct resource *r; 3164793fc096SFrank Li const struct of_device_id *of_id; 3165793fc096SFrank Li static int dev_id; 3166407066f8SUwe Kleine-König struct device_node *np = pdev->dev.of_node, *phy_node; 3167b7bd75cfSFrank Li int num_tx_qs; 3168b7bd75cfSFrank Li int num_rx_qs; 3169793fc096SFrank Li 3170793fc096SFrank Li of_id = of_match_device(fec_dt_ids, &pdev->dev); 3171793fc096SFrank Li if (of_id) 3172793fc096SFrank Li pdev->id_entry = of_id->data; 3173793fc096SFrank Li 31749fc095f1SFugang Duan fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs); 31759fc095f1SFugang Duan 3176793fc096SFrank Li /* Init network device */ 31779fc095f1SFugang Duan ndev = alloc_etherdev_mqs(sizeof(struct fec_enet_private), 31789fc095f1SFugang Duan num_tx_qs, num_rx_qs); 3179793fc096SFrank Li if (!ndev) 3180793fc096SFrank Li return -ENOMEM; 3181793fc096SFrank Li 3182793fc096SFrank Li SET_NETDEV_DEV(ndev, &pdev->dev); 3183793fc096SFrank Li 3184793fc096SFrank Li /* setup board info structure */ 3185793fc096SFrank Li fep = netdev_priv(ndev); 3186793fc096SFrank Li 31879fc095f1SFugang Duan fep->num_rx_queues = num_rx_qs; 31889fc095f1SFugang Duan fep->num_tx_queues = num_tx_qs; 31899fc095f1SFugang Duan 3190d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 3191793fc096SFrank Li /* default enable pause frame auto negotiation */ 3192793fc096SFrank Li if (pdev->id_entry && 3193793fc096SFrank Li (pdev->id_entry->driver_data & FEC_QUIRK_HAS_GBIT)) 3194793fc096SFrank Li fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG; 3195d1391930SGuenter Roeck #endif 3196793fc096SFrank Li 31975bbde4d2SNimrod Andy /* Select default pin state */ 31985bbde4d2SNimrod Andy pinctrl_pm_select_default_state(&pdev->dev); 31995bbde4d2SNimrod Andy 3200399db75bSFabio Estevam r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3201941e173aSTushar Behera fep->hwp = devm_ioremap_resource(&pdev->dev, r); 3202941e173aSTushar Behera if (IS_ERR(fep->hwp)) { 3203941e173aSTushar Behera ret = PTR_ERR(fep->hwp); 3204941e173aSTushar Behera goto failed_ioremap; 3205941e173aSTushar Behera } 3206941e173aSTushar Behera 3207793fc096SFrank Li fep->pdev = pdev; 3208793fc096SFrank Li fep->dev_id = dev_id++; 3209793fc096SFrank Li 3210793fc096SFrank Li fep->bufdesc_ex = 0; 3211793fc096SFrank Li 3212793fc096SFrank Li platform_set_drvdata(pdev, ndev); 3213793fc096SFrank Li 3214407066f8SUwe Kleine-König phy_node = of_parse_phandle(np, "phy-handle", 0); 3215407066f8SUwe Kleine-König if (!phy_node && of_phy_is_fixed_link(np)) { 3216407066f8SUwe Kleine-König ret = of_phy_register_fixed_link(np); 3217407066f8SUwe Kleine-König if (ret < 0) { 3218407066f8SUwe Kleine-König dev_err(&pdev->dev, 3219407066f8SUwe Kleine-König "broken fixed-link specification\n"); 3220407066f8SUwe Kleine-König goto failed_phy; 3221407066f8SUwe Kleine-König } 3222407066f8SUwe Kleine-König phy_node = of_node_get(np); 3223407066f8SUwe Kleine-König } 3224407066f8SUwe Kleine-König fep->phy_node = phy_node; 3225407066f8SUwe Kleine-König 32266c5f7808SGuenter Roeck ret = of_get_phy_mode(pdev->dev.of_node); 3227793fc096SFrank Li if (ret < 0) { 322894660ba0SJingoo Han pdata = dev_get_platdata(&pdev->dev); 3229793fc096SFrank Li if (pdata) 3230793fc096SFrank Li fep->phy_interface = pdata->phy; 3231793fc096SFrank Li else 3232793fc096SFrank Li fep->phy_interface = PHY_INTERFACE_MODE_MII; 3233793fc096SFrank Li } else { 3234793fc096SFrank Li fep->phy_interface = ret; 3235793fc096SFrank Li } 3236793fc096SFrank Li 3237793fc096SFrank Li fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); 3238793fc096SFrank Li if (IS_ERR(fep->clk_ipg)) { 3239793fc096SFrank Li ret = PTR_ERR(fep->clk_ipg); 3240793fc096SFrank Li goto failed_clk; 3241793fc096SFrank Li } 3242793fc096SFrank Li 3243793fc096SFrank Li fep->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); 3244793fc096SFrank Li if (IS_ERR(fep->clk_ahb)) { 3245793fc096SFrank Li ret = PTR_ERR(fep->clk_ahb); 3246793fc096SFrank Li goto failed_clk; 3247793fc096SFrank Li } 3248793fc096SFrank Li 3249d851b47bSFugang Duan fep->itr_clk_rate = clk_get_rate(fep->clk_ahb); 3250d851b47bSFugang Duan 325138f56f33SLinus Torvalds /* enet_out is optional, depends on board */ 325238f56f33SLinus Torvalds fep->clk_enet_out = devm_clk_get(&pdev->dev, "enet_out"); 325338f56f33SLinus Torvalds if (IS_ERR(fep->clk_enet_out)) 325438f56f33SLinus Torvalds fep->clk_enet_out = NULL; 325538f56f33SLinus Torvalds 325691c0d987SNimrod Andy fep->ptp_clk_on = false; 325791c0d987SNimrod Andy mutex_init(&fep->ptp_clk_mutex); 32589b5330edSFugang Duan 32599b5330edSFugang Duan /* clk_ref is optional, depends on board */ 32609b5330edSFugang Duan fep->clk_ref = devm_clk_get(&pdev->dev, "enet_clk_ref"); 32619b5330edSFugang Duan if (IS_ERR(fep->clk_ref)) 32629b5330edSFugang Duan fep->clk_ref = NULL; 32639b5330edSFugang Duan 3264793fc096SFrank Li fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp"); 3265793fc096SFrank Li fep->bufdesc_ex = 3266793fc096SFrank Li pdev->id_entry->driver_data & FEC_QUIRK_HAS_BUFDESC_EX; 3267793fc096SFrank Li if (IS_ERR(fep->clk_ptp)) { 326838f56f33SLinus Torvalds fep->clk_ptp = NULL; 3269793fc096SFrank Li fep->bufdesc_ex = 0; 3270793fc096SFrank Li } 3271793fc096SFrank Li 3272e8fcfcd5SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 327313a097bdSFabio Estevam if (ret) 327413a097bdSFabio Estevam goto failed_clk; 327513a097bdSFabio Estevam 3276f4e9f3d2SFabio Estevam fep->reg_phy = devm_regulator_get(&pdev->dev, "phy"); 3277f4e9f3d2SFabio Estevam if (!IS_ERR(fep->reg_phy)) { 3278f4e9f3d2SFabio Estevam ret = regulator_enable(fep->reg_phy); 3279793fc096SFrank Li if (ret) { 3280793fc096SFrank Li dev_err(&pdev->dev, 3281793fc096SFrank Li "Failed to enable phy regulator: %d\n", ret); 3282793fc096SFrank Li goto failed_regulator; 3283793fc096SFrank Li } 3284f6a4d607SFabio Estevam } else { 3285f6a4d607SFabio Estevam fep->reg_phy = NULL; 3286793fc096SFrank Li } 3287793fc096SFrank Li 3288793fc096SFrank Li fec_reset_phy(pdev); 3289793fc096SFrank Li 3290793fc096SFrank Li if (fep->bufdesc_ex) 3291ca162a82SFabio Estevam fec_ptp_init(pdev); 3292793fc096SFrank Li 3293793fc096SFrank Li ret = fec_enet_init(ndev); 3294793fc096SFrank Li if (ret) 3295793fc096SFrank Li goto failed_init; 3296793fc096SFrank Li 3297793fc096SFrank Li for (i = 0; i < FEC_IRQ_NUM; i++) { 3298793fc096SFrank Li irq = platform_get_irq(pdev, i); 3299793fc096SFrank Li if (irq < 0) { 3300793fc096SFrank Li if (i) 3301793fc096SFrank Li break; 3302793fc096SFrank Li ret = irq; 3303793fc096SFrank Li goto failed_irq; 3304793fc096SFrank Li } 33050d9b2ab1SFabio Estevam ret = devm_request_irq(&pdev->dev, irq, fec_enet_interrupt, 330644a272ddSMichael Opdenacker 0, pdev->name, ndev); 33070d9b2ab1SFabio Estevam if (ret) 3308793fc096SFrank Li goto failed_irq; 3309793fc096SFrank Li } 3310793fc096SFrank Li 3311b4d39b53SFugang Duan init_completion(&fep->mdio_done); 3312793fc096SFrank Li ret = fec_enet_mii_init(pdev); 3313793fc096SFrank Li if (ret) 3314793fc096SFrank Li goto failed_mii_init; 3315793fc096SFrank Li 3316793fc096SFrank Li /* Carrier starts down, phylib will bring it up */ 3317793fc096SFrank Li netif_carrier_off(ndev); 3318e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 33195bbde4d2SNimrod Andy pinctrl_pm_select_sleep_state(&pdev->dev); 3320793fc096SFrank Li 3321793fc096SFrank Li ret = register_netdev(ndev); 3322793fc096SFrank Li if (ret) 3323793fc096SFrank Li goto failed_register; 3324793fc096SFrank Li 3325eb1d0640SFabio Estevam if (fep->bufdesc_ex && fep->ptp_clock) 3326eb1d0640SFabio Estevam netdev_info(ndev, "registered PHC device %d\n", fep->dev_id); 3327eb1d0640SFabio Estevam 33281b7bde6dSNimrod Andy fep->rx_copybreak = COPYBREAK_DEFAULT; 332936cdc743SRussell King INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work); 3330793fc096SFrank Li return 0; 3331793fc096SFrank Li 3332793fc096SFrank Li failed_register: 3333793fc096SFrank Li fec_enet_mii_remove(fep); 3334793fc096SFrank Li failed_mii_init: 33357a2bbd8dSFabio Estevam failed_irq: 33367a2bbd8dSFabio Estevam failed_init: 3337f6a4d607SFabio Estevam if (fep->reg_phy) 3338f6a4d607SFabio Estevam regulator_disable(fep->reg_phy); 3339793fc096SFrank Li failed_regulator: 3340e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 3341793fc096SFrank Li failed_clk: 3342407066f8SUwe Kleine-König failed_phy: 3343407066f8SUwe Kleine-König of_node_put(phy_node); 3344793fc096SFrank Li failed_ioremap: 3345793fc096SFrank Li free_netdev(ndev); 3346793fc096SFrank Li 3347793fc096SFrank Li return ret; 3348793fc096SFrank Li } 3349793fc096SFrank Li 3350793fc096SFrank Li static int 3351793fc096SFrank Li fec_drv_remove(struct platform_device *pdev) 3352793fc096SFrank Li { 3353793fc096SFrank Li struct net_device *ndev = platform_get_drvdata(pdev); 3354793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3355793fc096SFrank Li 335691c0d987SNimrod Andy cancel_delayed_work_sync(&fep->time_keep); 335736cdc743SRussell King cancel_work_sync(&fep->tx_timeout_work); 3358793fc096SFrank Li unregister_netdev(ndev); 3359793fc096SFrank Li fec_enet_mii_remove(fep); 3360f6a4d607SFabio Estevam if (fep->reg_phy) 3361f6a4d607SFabio Estevam regulator_disable(fep->reg_phy); 3362793fc096SFrank Li if (fep->ptp_clock) 3363793fc096SFrank Li ptp_clock_unregister(fep->ptp_clock); 3364e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 3365407066f8SUwe Kleine-König of_node_put(fep->phy_node); 3366793fc096SFrank Li free_netdev(ndev); 3367793fc096SFrank Li 3368793fc096SFrank Li return 0; 3369793fc096SFrank Li } 3370793fc096SFrank Li 3371dd66d386SFabio Estevam static int __maybe_unused fec_suspend(struct device *dev) 3372793fc096SFrank Li { 3373793fc096SFrank Li struct net_device *ndev = dev_get_drvdata(dev); 3374793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3375793fc096SFrank Li 3376da1774e5SRussell King rtnl_lock(); 3377793fc096SFrank Li if (netif_running(ndev)) { 3378d76cfae9SRussell King phy_stop(fep->phy_dev); 337931a6de34SRussell King napi_disable(&fep->napi); 338031a6de34SRussell King netif_tx_lock_bh(ndev); 3381793fc096SFrank Li netif_device_detach(ndev); 338231a6de34SRussell King netif_tx_unlock_bh(ndev); 338331a6de34SRussell King fec_stop(ndev); 3384793fc096SFrank Li } 3385da1774e5SRussell King rtnl_unlock(); 3386da1774e5SRussell King 3387e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 33885bbde4d2SNimrod Andy pinctrl_pm_select_sleep_state(&fep->pdev->dev); 3389793fc096SFrank Li 3390238f7bc7SFabio Estevam if (fep->reg_phy) 3391238f7bc7SFabio Estevam regulator_disable(fep->reg_phy); 3392238f7bc7SFabio Estevam 3393793fc096SFrank Li return 0; 3394793fc096SFrank Li } 3395793fc096SFrank Li 3396dd66d386SFabio Estevam static int __maybe_unused fec_resume(struct device *dev) 3397793fc096SFrank Li { 3398793fc096SFrank Li struct net_device *ndev = dev_get_drvdata(dev); 3399793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3400238f7bc7SFabio Estevam int ret; 3401238f7bc7SFabio Estevam 3402238f7bc7SFabio Estevam if (fep->reg_phy) { 3403238f7bc7SFabio Estevam ret = regulator_enable(fep->reg_phy); 3404238f7bc7SFabio Estevam if (ret) 3405238f7bc7SFabio Estevam return ret; 3406238f7bc7SFabio Estevam } 3407793fc096SFrank Li 34085bbde4d2SNimrod Andy pinctrl_pm_select_default_state(&fep->pdev->dev); 3409e8fcfcd5SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 341013a097bdSFabio Estevam if (ret) 3411e8fcfcd5SNimrod Andy goto failed_clk; 341213a097bdSFabio Estevam 3413da1774e5SRussell King rtnl_lock(); 3414793fc096SFrank Li if (netif_running(ndev)) { 3415ef83337dSRussell King fec_restart(ndev); 341631a6de34SRussell King netif_tx_lock_bh(ndev); 3417793fc096SFrank Li netif_device_attach(ndev); 34186af42d42SRussell King netif_tx_unlock_bh(ndev); 34196af42d42SRussell King napi_enable(&fep->napi); 3420d76cfae9SRussell King phy_start(fep->phy_dev); 3421793fc096SFrank Li } 3422da1774e5SRussell King rtnl_unlock(); 3423793fc096SFrank Li 3424793fc096SFrank Li return 0; 342513a097bdSFabio Estevam 3426e8fcfcd5SNimrod Andy failed_clk: 342713a097bdSFabio Estevam if (fep->reg_phy) 342813a097bdSFabio Estevam regulator_disable(fep->reg_phy); 342913a097bdSFabio Estevam return ret; 3430793fc096SFrank Li } 3431793fc096SFrank Li 3432bf7bfd7fSFabio Estevam static SIMPLE_DEV_PM_OPS(fec_pm_ops, fec_suspend, fec_resume); 3433793fc096SFrank Li 3434793fc096SFrank Li static struct platform_driver fec_driver = { 3435793fc096SFrank Li .driver = { 3436793fc096SFrank Li .name = DRIVER_NAME, 3437793fc096SFrank Li .owner = THIS_MODULE, 3438793fc096SFrank Li .pm = &fec_pm_ops, 3439793fc096SFrank Li .of_match_table = fec_dt_ids, 3440793fc096SFrank Li }, 3441793fc096SFrank Li .id_table = fec_devtype, 3442793fc096SFrank Li .probe = fec_probe, 3443793fc096SFrank Li .remove = fec_drv_remove, 3444793fc096SFrank Li }; 3445793fc096SFrank Li 3446793fc096SFrank Li module_platform_driver(fec_driver); 3447793fc096SFrank Li 3448f8c0aca9SFabio Estevam MODULE_ALIAS("platform:"DRIVER_NAME); 3449793fc096SFrank Li MODULE_LICENSE("GPL"); 3450