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> 60793fc096SFrank Li 61793fc096SFrank Li #include <asm/cacheflush.h> 62793fc096SFrank Li 63793fc096SFrank Li #include "fec.h" 64793fc096SFrank Li 65772e42b0SChristoph Müllner static void set_multicast_list(struct net_device *ndev); 66772e42b0SChristoph Müllner 67793fc096SFrank Li #if defined(CONFIG_ARM) 68793fc096SFrank Li #define FEC_ALIGNMENT 0xf 69793fc096SFrank Li #else 70793fc096SFrank Li #define FEC_ALIGNMENT 0x3 71793fc096SFrank Li #endif 72793fc096SFrank Li 73793fc096SFrank Li #define DRIVER_NAME "fec" 74793fc096SFrank Li 75793fc096SFrank Li /* Pause frame feild and FIFO threshold */ 76793fc096SFrank Li #define FEC_ENET_FCE (1 << 5) 77793fc096SFrank Li #define FEC_ENET_RSEM_V 0x84 78793fc096SFrank Li #define FEC_ENET_RSFL_V 16 79793fc096SFrank Li #define FEC_ENET_RAEM_V 0x8 80793fc096SFrank Li #define FEC_ENET_RAFL_V 0x8 81793fc096SFrank Li #define FEC_ENET_OPD_V 0xFFF0 82793fc096SFrank Li 83793fc096SFrank Li /* Controller is ENET-MAC */ 84793fc096SFrank Li #define FEC_QUIRK_ENET_MAC (1 << 0) 85793fc096SFrank Li /* Controller needs driver to swap frame */ 86793fc096SFrank Li #define FEC_QUIRK_SWAP_FRAME (1 << 1) 87793fc096SFrank Li /* Controller uses gasket */ 88793fc096SFrank Li #define FEC_QUIRK_USE_GASKET (1 << 2) 89793fc096SFrank Li /* Controller has GBIT support */ 90793fc096SFrank Li #define FEC_QUIRK_HAS_GBIT (1 << 3) 91793fc096SFrank Li /* Controller has extend desc buffer */ 92793fc096SFrank Li #define FEC_QUIRK_HAS_BUFDESC_EX (1 << 4) 9348496255SShawn Guo /* Controller has hardware checksum support */ 9448496255SShawn Guo #define FEC_QUIRK_HAS_CSUM (1 << 5) 95cdffcf1bSJim Baxter /* Controller has hardware vlan support */ 96cdffcf1bSJim Baxter #define FEC_QUIRK_HAS_VLAN (1 << 6) 9703191656SFrank Li /* ENET IP errata ERR006358 9803191656SFrank Li * 9903191656SFrank Li * If the ready bit in the transmit buffer descriptor (TxBD[R]) is previously 10003191656SFrank Li * detected as not set during a prior frame transmission, then the 10103191656SFrank Li * ENET_TDAR[TDAR] bit is cleared at a later time, even if additional TxBDs 10203191656SFrank Li * were added to the ring and the ENET_TDAR[TDAR] bit is set. This results in 10303191656SFrank Li * frames not being transmitted until there is a 0-to-1 transition on 10403191656SFrank Li * ENET_TDAR[TDAR]. 10503191656SFrank Li */ 10603191656SFrank Li #define FEC_QUIRK_ERR006358 (1 << 7) 107793fc096SFrank Li 108793fc096SFrank Li static struct platform_device_id fec_devtype[] = { 109793fc096SFrank Li { 110793fc096SFrank Li /* keep it for coldfire */ 111793fc096SFrank Li .name = DRIVER_NAME, 112793fc096SFrank Li .driver_data = 0, 113793fc096SFrank Li }, { 114793fc096SFrank Li .name = "imx25-fec", 115793fc096SFrank Li .driver_data = FEC_QUIRK_USE_GASKET, 116793fc096SFrank Li }, { 117793fc096SFrank Li .name = "imx27-fec", 118793fc096SFrank Li .driver_data = 0, 119793fc096SFrank Li }, { 120793fc096SFrank Li .name = "imx28-fec", 121793fc096SFrank Li .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME, 122793fc096SFrank Li }, { 123793fc096SFrank Li .name = "imx6q-fec", 124793fc096SFrank Li .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 125cdffcf1bSJim Baxter FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 12603191656SFrank Li FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR006358, 127793fc096SFrank Li }, { 12836803542SShawn Guo .name = "mvf600-fec", 129ca7c4a45SJingchang Lu .driver_data = FEC_QUIRK_ENET_MAC, 130ca7c4a45SJingchang Lu }, { 131793fc096SFrank Li /* sentinel */ 132793fc096SFrank Li } 133793fc096SFrank Li }; 134793fc096SFrank Li MODULE_DEVICE_TABLE(platform, fec_devtype); 135793fc096SFrank Li 136793fc096SFrank Li enum imx_fec_type { 137793fc096SFrank Li IMX25_FEC = 1, /* runs on i.mx25/50/53 */ 138793fc096SFrank Li IMX27_FEC, /* runs on i.mx27/35/51 */ 139793fc096SFrank Li IMX28_FEC, 140793fc096SFrank Li IMX6Q_FEC, 14136803542SShawn Guo MVF600_FEC, 142793fc096SFrank Li }; 143793fc096SFrank Li 144793fc096SFrank Li static const struct of_device_id fec_dt_ids[] = { 145793fc096SFrank Li { .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], }, 146793fc096SFrank Li { .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], }, 147793fc096SFrank Li { .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], }, 148793fc096SFrank Li { .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], }, 14936803542SShawn Guo { .compatible = "fsl,mvf600-fec", .data = &fec_devtype[MVF600_FEC], }, 150793fc096SFrank Li { /* sentinel */ } 151793fc096SFrank Li }; 152793fc096SFrank Li MODULE_DEVICE_TABLE(of, fec_dt_ids); 153793fc096SFrank Li 154793fc096SFrank Li static unsigned char macaddr[ETH_ALEN]; 155793fc096SFrank Li module_param_array(macaddr, byte, NULL, 0); 156793fc096SFrank Li MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); 157793fc096SFrank Li 158793fc096SFrank Li #if defined(CONFIG_M5272) 159793fc096SFrank Li /* 160793fc096SFrank Li * Some hardware gets it MAC address out of local flash memory. 161793fc096SFrank Li * if this is non-zero then assume it is the address to get MAC from. 162793fc096SFrank Li */ 163793fc096SFrank Li #if defined(CONFIG_NETtel) 164793fc096SFrank Li #define FEC_FLASHMAC 0xf0006006 165793fc096SFrank Li #elif defined(CONFIG_GILBARCONAP) || defined(CONFIG_SCALES) 166793fc096SFrank Li #define FEC_FLASHMAC 0xf0006000 167793fc096SFrank Li #elif defined(CONFIG_CANCam) 168793fc096SFrank Li #define FEC_FLASHMAC 0xf0020000 169793fc096SFrank Li #elif defined (CONFIG_M5272C3) 170793fc096SFrank Li #define FEC_FLASHMAC (0xffe04000 + 4) 171793fc096SFrank Li #elif defined(CONFIG_MOD5272) 172793fc096SFrank Li #define FEC_FLASHMAC 0xffc0406b 173793fc096SFrank Li #else 174793fc096SFrank Li #define FEC_FLASHMAC 0 175793fc096SFrank Li #endif 176793fc096SFrank Li #endif /* CONFIG_M5272 */ 177793fc096SFrank Li 178793fc096SFrank Li /* Interrupt events/masks. */ 179793fc096SFrank Li #define FEC_ENET_HBERR ((uint)0x80000000) /* Heartbeat error */ 180793fc096SFrank Li #define FEC_ENET_BABR ((uint)0x40000000) /* Babbling receiver */ 181793fc096SFrank Li #define FEC_ENET_BABT ((uint)0x20000000) /* Babbling transmitter */ 182793fc096SFrank Li #define FEC_ENET_GRA ((uint)0x10000000) /* Graceful stop complete */ 183793fc096SFrank Li #define FEC_ENET_TXF ((uint)0x08000000) /* Full frame transmitted */ 184793fc096SFrank Li #define FEC_ENET_TXB ((uint)0x04000000) /* A buffer was transmitted */ 185793fc096SFrank Li #define FEC_ENET_RXF ((uint)0x02000000) /* Full frame received */ 186793fc096SFrank Li #define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */ 187793fc096SFrank Li #define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */ 188793fc096SFrank Li #define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */ 189793fc096SFrank Li 190793fc096SFrank Li #define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII) 191793fc096SFrank Li #define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF)) 192793fc096SFrank Li 193cdffcf1bSJim Baxter /* The FEC stores dest/src/type/vlan, data, and checksum for receive packets. 194793fc096SFrank Li */ 195cdffcf1bSJim Baxter #define PKT_MAXBUF_SIZE 1522 196793fc096SFrank Li #define PKT_MINBUF_SIZE 64 197cdffcf1bSJim Baxter #define PKT_MAXBLR_SIZE 1536 198793fc096SFrank Li 1994c09eed9SJim Baxter /* FEC receive acceleration */ 2004c09eed9SJim Baxter #define FEC_RACC_IPDIS (1 << 1) 2014c09eed9SJim Baxter #define FEC_RACC_PRODIS (1 << 2) 2024c09eed9SJim Baxter #define FEC_RACC_OPTIONS (FEC_RACC_IPDIS | FEC_RACC_PRODIS) 2034c09eed9SJim Baxter 204793fc096SFrank Li /* 205793fc096SFrank Li * The 5270/5271/5280/5282/532x RX control register also contains maximum frame 206793fc096SFrank Li * size bits. Other FEC hardware does not, so we need to take that into 207793fc096SFrank Li * account when setting it. 208793fc096SFrank Li */ 209793fc096SFrank Li #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ 210793fc096SFrank Li defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) 211793fc096SFrank Li #define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16) 212793fc096SFrank Li #else 213793fc096SFrank Li #define OPT_FRAME_SIZE 0 214793fc096SFrank Li #endif 215793fc096SFrank Li 216793fc096SFrank Li /* FEC MII MMFR bits definition */ 217793fc096SFrank Li #define FEC_MMFR_ST (1 << 30) 218793fc096SFrank Li #define FEC_MMFR_OP_READ (2 << 28) 219793fc096SFrank Li #define FEC_MMFR_OP_WRITE (1 << 28) 220793fc096SFrank Li #define FEC_MMFR_PA(v) ((v & 0x1f) << 23) 221793fc096SFrank Li #define FEC_MMFR_RA(v) ((v & 0x1f) << 18) 222793fc096SFrank Li #define FEC_MMFR_TA (2 << 16) 223793fc096SFrank Li #define FEC_MMFR_DATA(v) (v & 0xffff) 224793fc096SFrank Li 225793fc096SFrank Li #define FEC_MII_TIMEOUT 30000 /* us */ 226793fc096SFrank Li 227793fc096SFrank Li /* Transmitter timeout */ 228793fc096SFrank Li #define TX_TIMEOUT (2 * HZ) 229793fc096SFrank Li 230793fc096SFrank Li #define FEC_PAUSE_FLAG_AUTONEG 0x1 231793fc096SFrank Li #define FEC_PAUSE_FLAG_ENABLE 0x2 232793fc096SFrank Li 23379f33912SNimrod Andy #define TSO_HEADER_SIZE 128 23479f33912SNimrod Andy /* Max number of allowed TCP segments for software TSO */ 23579f33912SNimrod Andy #define FEC_MAX_TSO_SEGS 100 23679f33912SNimrod Andy #define FEC_MAX_SKB_DESCS (FEC_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS) 23779f33912SNimrod Andy 23879f33912SNimrod Andy #define IS_TSO_HEADER(txq, addr) \ 23979f33912SNimrod Andy ((addr >= txq->tso_hdrs_dma) && \ 24079f33912SNimrod Andy (addr < txq->tso_hdrs_dma + txq->tx_ring_size * TSO_HEADER_SIZE)) 24179f33912SNimrod Andy 242793fc096SFrank Li static int mii_cnt; 243793fc096SFrank Li 24436e24e2eSDuan Fugang-B38611 static inline 24536e24e2eSDuan Fugang-B38611 struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, struct fec_enet_private *fep) 246793fc096SFrank Li { 24736e24e2eSDuan Fugang-B38611 struct bufdesc *new_bd = bdp + 1; 24836e24e2eSDuan Fugang-B38611 struct bufdesc_ex *ex_new_bd = (struct bufdesc_ex *)bdp + 1; 24936e24e2eSDuan Fugang-B38611 struct bufdesc_ex *ex_base; 25036e24e2eSDuan Fugang-B38611 struct bufdesc *base; 25136e24e2eSDuan Fugang-B38611 int ring_size; 25236e24e2eSDuan Fugang-B38611 25336e24e2eSDuan Fugang-B38611 if (bdp >= fep->tx_bd_base) { 25436e24e2eSDuan Fugang-B38611 base = fep->tx_bd_base; 25536e24e2eSDuan Fugang-B38611 ring_size = fep->tx_ring_size; 25636e24e2eSDuan Fugang-B38611 ex_base = (struct bufdesc_ex *)fep->tx_bd_base; 25736e24e2eSDuan Fugang-B38611 } else { 25836e24e2eSDuan Fugang-B38611 base = fep->rx_bd_base; 25936e24e2eSDuan Fugang-B38611 ring_size = fep->rx_ring_size; 26036e24e2eSDuan Fugang-B38611 ex_base = (struct bufdesc_ex *)fep->rx_bd_base; 261793fc096SFrank Li } 262793fc096SFrank Li 26336e24e2eSDuan Fugang-B38611 if (fep->bufdesc_ex) 26436e24e2eSDuan Fugang-B38611 return (struct bufdesc *)((ex_new_bd >= (ex_base + ring_size)) ? 26536e24e2eSDuan Fugang-B38611 ex_base : ex_new_bd); 266793fc096SFrank Li else 26736e24e2eSDuan Fugang-B38611 return (new_bd >= (base + ring_size)) ? 26836e24e2eSDuan Fugang-B38611 base : new_bd; 26936e24e2eSDuan Fugang-B38611 } 27036e24e2eSDuan Fugang-B38611 27136e24e2eSDuan Fugang-B38611 static inline 27236e24e2eSDuan Fugang-B38611 struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, struct fec_enet_private *fep) 27336e24e2eSDuan Fugang-B38611 { 27436e24e2eSDuan Fugang-B38611 struct bufdesc *new_bd = bdp - 1; 27536e24e2eSDuan Fugang-B38611 struct bufdesc_ex *ex_new_bd = (struct bufdesc_ex *)bdp - 1; 27636e24e2eSDuan Fugang-B38611 struct bufdesc_ex *ex_base; 27736e24e2eSDuan Fugang-B38611 struct bufdesc *base; 27836e24e2eSDuan Fugang-B38611 int ring_size; 27936e24e2eSDuan Fugang-B38611 28036e24e2eSDuan Fugang-B38611 if (bdp >= fep->tx_bd_base) { 28136e24e2eSDuan Fugang-B38611 base = fep->tx_bd_base; 28236e24e2eSDuan Fugang-B38611 ring_size = fep->tx_ring_size; 28336e24e2eSDuan Fugang-B38611 ex_base = (struct bufdesc_ex *)fep->tx_bd_base; 28436e24e2eSDuan Fugang-B38611 } else { 28536e24e2eSDuan Fugang-B38611 base = fep->rx_bd_base; 28636e24e2eSDuan Fugang-B38611 ring_size = fep->rx_ring_size; 28736e24e2eSDuan Fugang-B38611 ex_base = (struct bufdesc_ex *)fep->rx_bd_base; 28836e24e2eSDuan Fugang-B38611 } 28936e24e2eSDuan Fugang-B38611 29036e24e2eSDuan Fugang-B38611 if (fep->bufdesc_ex) 29136e24e2eSDuan Fugang-B38611 return (struct bufdesc *)((ex_new_bd < ex_base) ? 29236e24e2eSDuan Fugang-B38611 (ex_new_bd + ring_size) : ex_new_bd); 29336e24e2eSDuan Fugang-B38611 else 29436e24e2eSDuan Fugang-B38611 return (new_bd < base) ? (new_bd + ring_size) : new_bd; 295793fc096SFrank Li } 296793fc096SFrank Li 29761a4427bSNimrod Andy static int fec_enet_get_bd_index(struct bufdesc *base, struct bufdesc *bdp, 29861a4427bSNimrod Andy struct fec_enet_private *fep) 29961a4427bSNimrod Andy { 30061a4427bSNimrod Andy return ((const char *)bdp - (const char *)base) / fep->bufdesc_size; 30161a4427bSNimrod Andy } 30261a4427bSNimrod Andy 3036e909283SNimrod Andy static int fec_enet_get_free_txdesc_num(struct fec_enet_private *fep) 3046e909283SNimrod Andy { 3056e909283SNimrod Andy int entries; 3066e909283SNimrod Andy 3076e909283SNimrod Andy entries = ((const char *)fep->dirty_tx - 3086e909283SNimrod Andy (const char *)fep->cur_tx) / fep->bufdesc_size - 1; 3096e909283SNimrod Andy 3106e909283SNimrod Andy return entries > 0 ? entries : entries + fep->tx_ring_size; 3116e909283SNimrod Andy } 3126e909283SNimrod Andy 313793fc096SFrank Li static void *swap_buffer(void *bufaddr, int len) 314793fc096SFrank Li { 315793fc096SFrank Li int i; 316793fc096SFrank Li unsigned int *buf = bufaddr; 317793fc096SFrank Li 318ffed61e6SFabio Estevam for (i = 0; i < DIV_ROUND_UP(len, 4); i++, buf++) 319793fc096SFrank Li *buf = cpu_to_be32(*buf); 320793fc096SFrank Li 321793fc096SFrank Li return bufaddr; 322793fc096SFrank Li } 323793fc096SFrank Li 324344756f6SRussell King static void fec_dump(struct net_device *ndev) 325344756f6SRussell King { 326344756f6SRussell King struct fec_enet_private *fep = netdev_priv(ndev); 327344756f6SRussell King struct bufdesc *bdp = fep->tx_bd_base; 328344756f6SRussell King unsigned int index = 0; 329344756f6SRussell King 330344756f6SRussell King netdev_info(ndev, "TX ring dump\n"); 331344756f6SRussell King pr_info("Nr SC addr len SKB\n"); 332344756f6SRussell King 333344756f6SRussell King do { 334344756f6SRussell King pr_info("%3u %c%c 0x%04x 0x%08lx %4u %p\n", 335344756f6SRussell King index, 336344756f6SRussell King bdp == fep->cur_tx ? 'S' : ' ', 337344756f6SRussell King bdp == fep->dirty_tx ? 'H' : ' ', 338344756f6SRussell King bdp->cbd_sc, bdp->cbd_bufaddr, bdp->cbd_datlen, 339344756f6SRussell King fep->tx_skbuff[index]); 340344756f6SRussell King bdp = fec_enet_get_nextdesc(bdp, fep); 341344756f6SRussell King index++; 342344756f6SRussell King } while (bdp != fep->tx_bd_base); 343344756f6SRussell King } 344344756f6SRussell King 34562a02c98SFugang Duan static inline bool is_ipv4_pkt(struct sk_buff *skb) 34662a02c98SFugang Duan { 34762a02c98SFugang Duan return skb->protocol == htons(ETH_P_IP) && ip_hdr(skb)->version == 4; 34862a02c98SFugang Duan } 34962a02c98SFugang Duan 3504c09eed9SJim Baxter static int 3514c09eed9SJim Baxter fec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev) 3524c09eed9SJim Baxter { 3534c09eed9SJim Baxter /* Only run for packets requiring a checksum. */ 3544c09eed9SJim Baxter if (skb->ip_summed != CHECKSUM_PARTIAL) 3554c09eed9SJim Baxter return 0; 3564c09eed9SJim Baxter 3574c09eed9SJim Baxter if (unlikely(skb_cow_head(skb, 0))) 3584c09eed9SJim Baxter return -1; 3594c09eed9SJim Baxter 36062a02c98SFugang Duan if (is_ipv4_pkt(skb)) 36196c50caaSNimrod Andy ip_hdr(skb)->check = 0; 3624c09eed9SJim Baxter *(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) = 0; 3634c09eed9SJim Baxter 3644c09eed9SJim Baxter return 0; 3654c09eed9SJim Baxter } 3664c09eed9SJim Baxter 3676e909283SNimrod Andy static int 3686e909283SNimrod Andy fec_enet_txq_submit_frag_skb(struct sk_buff *skb, struct net_device *ndev) 3696e909283SNimrod Andy { 3706e909283SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 3716e909283SNimrod Andy const struct platform_device_id *id_entry = 3726e909283SNimrod Andy platform_get_device_id(fep->pdev); 3736e909283SNimrod Andy struct bufdesc *bdp = fep->cur_tx; 3746e909283SNimrod Andy struct bufdesc_ex *ebdp; 3756e909283SNimrod Andy int nr_frags = skb_shinfo(skb)->nr_frags; 3766e909283SNimrod Andy int frag, frag_len; 3776e909283SNimrod Andy unsigned short status; 3786e909283SNimrod Andy unsigned int estatus = 0; 3796e909283SNimrod Andy skb_frag_t *this_frag; 3806e909283SNimrod Andy unsigned int index; 3816e909283SNimrod Andy void *bufaddr; 382d6bf3143SRussell King dma_addr_t addr; 3836e909283SNimrod Andy int i; 3846e909283SNimrod Andy 3856e909283SNimrod Andy for (frag = 0; frag < nr_frags; frag++) { 3866e909283SNimrod Andy this_frag = &skb_shinfo(skb)->frags[frag]; 3876e909283SNimrod Andy bdp = fec_enet_get_nextdesc(bdp, fep); 3886e909283SNimrod Andy ebdp = (struct bufdesc_ex *)bdp; 3896e909283SNimrod Andy 3906e909283SNimrod Andy status = bdp->cbd_sc; 3916e909283SNimrod Andy status &= ~BD_ENET_TX_STATS; 3926e909283SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 3936e909283SNimrod Andy frag_len = skb_shinfo(skb)->frags[frag].size; 3946e909283SNimrod Andy 3956e909283SNimrod Andy /* Handle the last BD specially */ 3966e909283SNimrod Andy if (frag == nr_frags - 1) { 3976e909283SNimrod Andy status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); 3986e909283SNimrod Andy if (fep->bufdesc_ex) { 3996e909283SNimrod Andy estatus |= BD_ENET_TX_INT; 4006e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & 4016e909283SNimrod Andy SKBTX_HW_TSTAMP && fep->hwts_tx_en)) 4026e909283SNimrod Andy estatus |= BD_ENET_TX_TS; 4036e909283SNimrod Andy } 4046e909283SNimrod Andy } 4056e909283SNimrod Andy 4066e909283SNimrod Andy if (fep->bufdesc_ex) { 4076e909283SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 4086e909283SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 4096e909283SNimrod Andy ebdp->cbd_bdu = 0; 4106e909283SNimrod Andy ebdp->cbd_esc = estatus; 4116e909283SNimrod Andy } 4126e909283SNimrod Andy 4136e909283SNimrod Andy bufaddr = page_address(this_frag->page.p) + this_frag->page_offset; 4146e909283SNimrod Andy 4156e909283SNimrod Andy index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep); 4166e909283SNimrod Andy if (((unsigned long) bufaddr) & FEC_ALIGNMENT || 4176e909283SNimrod Andy id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) { 4186e909283SNimrod Andy memcpy(fep->tx_bounce[index], bufaddr, frag_len); 4196e909283SNimrod Andy bufaddr = fep->tx_bounce[index]; 4206e909283SNimrod Andy 4216e909283SNimrod Andy if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) 4226e909283SNimrod Andy swap_buffer(bufaddr, frag_len); 4236e909283SNimrod Andy } 4246e909283SNimrod Andy 425d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, bufaddr, frag_len, 426d6bf3143SRussell King DMA_TO_DEVICE); 427d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 4286e909283SNimrod Andy dev_kfree_skb_any(skb); 4296e909283SNimrod Andy if (net_ratelimit()) 4306e909283SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 4316e909283SNimrod Andy goto dma_mapping_error; 4326e909283SNimrod Andy } 4336e909283SNimrod Andy 434d6bf3143SRussell King bdp->cbd_bufaddr = addr; 4356e909283SNimrod Andy bdp->cbd_datlen = frag_len; 4366e909283SNimrod Andy bdp->cbd_sc = status; 4376e909283SNimrod Andy } 4386e909283SNimrod Andy 4396e909283SNimrod Andy fep->cur_tx = bdp; 4406e909283SNimrod Andy 4416e909283SNimrod Andy return 0; 4426e909283SNimrod Andy 4436e909283SNimrod Andy dma_mapping_error: 4446e909283SNimrod Andy bdp = fep->cur_tx; 4456e909283SNimrod Andy for (i = 0; i < frag; i++) { 4466e909283SNimrod Andy bdp = fec_enet_get_nextdesc(bdp, fep); 4476e909283SNimrod Andy dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, 4486e909283SNimrod Andy bdp->cbd_datlen, DMA_TO_DEVICE); 4496e909283SNimrod Andy } 4506e909283SNimrod Andy return NETDEV_TX_OK; 4516e909283SNimrod Andy } 4526e909283SNimrod Andy 4536e909283SNimrod Andy static int fec_enet_txq_submit_skb(struct sk_buff *skb, struct net_device *ndev) 4546e909283SNimrod Andy { 4556e909283SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 4566e909283SNimrod Andy const struct platform_device_id *id_entry = 4576e909283SNimrod Andy platform_get_device_id(fep->pdev); 4586e909283SNimrod Andy int nr_frags = skb_shinfo(skb)->nr_frags; 4596e909283SNimrod Andy struct bufdesc *bdp, *last_bdp; 4606e909283SNimrod Andy void *bufaddr; 461d6bf3143SRussell King dma_addr_t addr; 4626e909283SNimrod Andy unsigned short status; 4636e909283SNimrod Andy unsigned short buflen; 4646e909283SNimrod Andy unsigned int estatus = 0; 4656e909283SNimrod Andy unsigned int index; 46679f33912SNimrod Andy int entries_free; 4676e909283SNimrod Andy int ret; 4686e909283SNimrod Andy 46979f33912SNimrod Andy entries_free = fec_enet_get_free_txdesc_num(fep); 47079f33912SNimrod Andy if (entries_free < MAX_SKB_FRAGS + 1) { 47179f33912SNimrod Andy dev_kfree_skb_any(skb); 47279f33912SNimrod Andy if (net_ratelimit()) 47379f33912SNimrod Andy netdev_err(ndev, "NOT enough BD for SG!\n"); 47479f33912SNimrod Andy return NETDEV_TX_OK; 47579f33912SNimrod Andy } 47679f33912SNimrod Andy 4776e909283SNimrod Andy /* Protocol checksum off-load for TCP and UDP. */ 4786e909283SNimrod Andy if (fec_enet_clear_csum(skb, ndev)) { 4796e909283SNimrod Andy dev_kfree_skb_any(skb); 4806e909283SNimrod Andy return NETDEV_TX_OK; 4816e909283SNimrod Andy } 4826e909283SNimrod Andy 4836e909283SNimrod Andy /* Fill in a Tx ring entry */ 4846e909283SNimrod Andy bdp = fep->cur_tx; 4856e909283SNimrod Andy status = bdp->cbd_sc; 4866e909283SNimrod Andy status &= ~BD_ENET_TX_STATS; 4876e909283SNimrod Andy 4886e909283SNimrod Andy /* Set buffer length and buffer pointer */ 4896e909283SNimrod Andy bufaddr = skb->data; 4906e909283SNimrod Andy buflen = skb_headlen(skb); 4916e909283SNimrod Andy 4926e909283SNimrod Andy index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep); 4936e909283SNimrod Andy if (((unsigned long) bufaddr) & FEC_ALIGNMENT || 4946e909283SNimrod Andy id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) { 4956e909283SNimrod Andy memcpy(fep->tx_bounce[index], skb->data, buflen); 4966e909283SNimrod Andy bufaddr = fep->tx_bounce[index]; 4976e909283SNimrod Andy 4986e909283SNimrod Andy if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) 4996e909283SNimrod Andy swap_buffer(bufaddr, buflen); 5006e909283SNimrod Andy } 5016e909283SNimrod Andy 502d6bf3143SRussell King /* Push the data cache so the CPM does not get stale memory data. */ 503d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, bufaddr, buflen, DMA_TO_DEVICE); 504d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 5056e909283SNimrod Andy dev_kfree_skb_any(skb); 5066e909283SNimrod Andy if (net_ratelimit()) 5076e909283SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 5086e909283SNimrod Andy return NETDEV_TX_OK; 5096e909283SNimrod Andy } 5106e909283SNimrod Andy 5116e909283SNimrod Andy if (nr_frags) { 5126e909283SNimrod Andy ret = fec_enet_txq_submit_frag_skb(skb, ndev); 5136e909283SNimrod Andy if (ret) 5146e909283SNimrod Andy return ret; 5156e909283SNimrod Andy } else { 5166e909283SNimrod Andy status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); 5176e909283SNimrod Andy if (fep->bufdesc_ex) { 5186e909283SNimrod Andy estatus = BD_ENET_TX_INT; 5196e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & 5206e909283SNimrod Andy SKBTX_HW_TSTAMP && fep->hwts_tx_en)) 5216e909283SNimrod Andy estatus |= BD_ENET_TX_TS; 5226e909283SNimrod Andy } 5236e909283SNimrod Andy } 5246e909283SNimrod Andy 5256e909283SNimrod Andy if (fep->bufdesc_ex) { 5266e909283SNimrod Andy 5276e909283SNimrod Andy struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 5286e909283SNimrod Andy 5296e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && 5306e909283SNimrod Andy fep->hwts_tx_en)) 5316e909283SNimrod Andy skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; 5326e909283SNimrod Andy 5336e909283SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 5346e909283SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 5356e909283SNimrod Andy 5366e909283SNimrod Andy ebdp->cbd_bdu = 0; 5376e909283SNimrod Andy ebdp->cbd_esc = estatus; 5386e909283SNimrod Andy } 5396e909283SNimrod Andy 5406e909283SNimrod Andy last_bdp = fep->cur_tx; 5416e909283SNimrod Andy index = fec_enet_get_bd_index(fep->tx_bd_base, last_bdp, fep); 5426e909283SNimrod Andy /* Save skb pointer */ 5436e909283SNimrod Andy fep->tx_skbuff[index] = skb; 5446e909283SNimrod Andy 5456e909283SNimrod Andy bdp->cbd_datlen = buflen; 546d6bf3143SRussell King bdp->cbd_bufaddr = addr; 5476e909283SNimrod Andy 5486e909283SNimrod Andy /* Send it on its way. Tell FEC it's ready, interrupt when done, 5496e909283SNimrod Andy * it's the last BD of the frame, and to put the CRC on the end. 5506e909283SNimrod Andy */ 5516e909283SNimrod Andy status |= (BD_ENET_TX_READY | BD_ENET_TX_TC); 5526e909283SNimrod Andy bdp->cbd_sc = status; 5536e909283SNimrod Andy 554793fc096SFrank Li /* If this was the last BD in the ring, start at the beginning again. */ 5556e909283SNimrod Andy bdp = fec_enet_get_nextdesc(last_bdp, fep); 556793fc096SFrank Li 5577a2a8451SEric Dumazet skb_tx_timestamp(skb); 5587a2a8451SEric Dumazet 559793fc096SFrank Li fep->cur_tx = bdp; 560793fc096SFrank Li 561793fc096SFrank Li /* Trigger transmission start */ 562793fc096SFrank Li writel(0, fep->hwp + FEC_X_DES_ACTIVE); 563793fc096SFrank Li 5646e909283SNimrod Andy return 0; 565793fc096SFrank Li } 566793fc096SFrank Li 56779f33912SNimrod Andy static int 56879f33912SNimrod Andy fec_enet_txq_put_data_tso(struct sk_buff *skb, struct net_device *ndev, 56979f33912SNimrod Andy struct bufdesc *bdp, int index, char *data, 57079f33912SNimrod Andy int size, bool last_tcp, bool is_last) 57179f33912SNimrod Andy { 57279f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 57379f33912SNimrod Andy const struct platform_device_id *id_entry = 57479f33912SNimrod Andy platform_get_device_id(fep->pdev); 57579f33912SNimrod Andy struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 57679f33912SNimrod Andy unsigned short status; 57779f33912SNimrod Andy unsigned int estatus = 0; 578d6bf3143SRussell King dma_addr_t addr; 57979f33912SNimrod Andy 58079f33912SNimrod Andy status = bdp->cbd_sc; 58179f33912SNimrod Andy status &= ~BD_ENET_TX_STATS; 58279f33912SNimrod Andy 58379f33912SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 58479f33912SNimrod Andy 58579f33912SNimrod Andy if (((unsigned long) data) & FEC_ALIGNMENT || 58679f33912SNimrod Andy id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) { 58779f33912SNimrod Andy memcpy(fep->tx_bounce[index], data, size); 58879f33912SNimrod Andy data = fep->tx_bounce[index]; 58979f33912SNimrod Andy 59079f33912SNimrod Andy if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) 59179f33912SNimrod Andy swap_buffer(data, size); 59279f33912SNimrod Andy } 59379f33912SNimrod Andy 594d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, data, size, DMA_TO_DEVICE); 595d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 59679f33912SNimrod Andy dev_kfree_skb_any(skb); 59779f33912SNimrod Andy if (net_ratelimit()) 59879f33912SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 59979f33912SNimrod Andy return NETDEV_TX_BUSY; 60079f33912SNimrod Andy } 60179f33912SNimrod Andy 602d6bf3143SRussell King bdp->cbd_datlen = size; 603d6bf3143SRussell King bdp->cbd_bufaddr = addr; 604d6bf3143SRussell King 60579f33912SNimrod Andy if (fep->bufdesc_ex) { 60679f33912SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 60779f33912SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 60879f33912SNimrod Andy ebdp->cbd_bdu = 0; 60979f33912SNimrod Andy ebdp->cbd_esc = estatus; 61079f33912SNimrod Andy } 61179f33912SNimrod Andy 61279f33912SNimrod Andy /* Handle the last BD specially */ 61379f33912SNimrod Andy if (last_tcp) 61479f33912SNimrod Andy status |= (BD_ENET_TX_LAST | BD_ENET_TX_TC); 61579f33912SNimrod Andy if (is_last) { 61679f33912SNimrod Andy status |= BD_ENET_TX_INTR; 61779f33912SNimrod Andy if (fep->bufdesc_ex) 61879f33912SNimrod Andy ebdp->cbd_esc |= BD_ENET_TX_INT; 61979f33912SNimrod Andy } 62079f33912SNimrod Andy 62179f33912SNimrod Andy bdp->cbd_sc = status; 62279f33912SNimrod Andy 62379f33912SNimrod Andy return 0; 62479f33912SNimrod Andy } 62579f33912SNimrod Andy 62679f33912SNimrod Andy static int 62779f33912SNimrod Andy fec_enet_txq_put_hdr_tso(struct sk_buff *skb, struct net_device *ndev, 62879f33912SNimrod Andy struct bufdesc *bdp, int index) 62979f33912SNimrod Andy { 63079f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 63179f33912SNimrod Andy const struct platform_device_id *id_entry = 63279f33912SNimrod Andy platform_get_device_id(fep->pdev); 63379f33912SNimrod Andy int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); 63479f33912SNimrod Andy struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 63579f33912SNimrod Andy void *bufaddr; 63679f33912SNimrod Andy unsigned long dmabuf; 63779f33912SNimrod Andy unsigned short status; 63879f33912SNimrod Andy unsigned int estatus = 0; 63979f33912SNimrod Andy 64079f33912SNimrod Andy status = bdp->cbd_sc; 64179f33912SNimrod Andy status &= ~BD_ENET_TX_STATS; 64279f33912SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 64379f33912SNimrod Andy 64479f33912SNimrod Andy bufaddr = fep->tso_hdrs + index * TSO_HEADER_SIZE; 64579f33912SNimrod Andy dmabuf = fep->tso_hdrs_dma + index * TSO_HEADER_SIZE; 64679f33912SNimrod Andy if (((unsigned long) bufaddr) & FEC_ALIGNMENT || 64779f33912SNimrod Andy id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) { 64879f33912SNimrod Andy memcpy(fep->tx_bounce[index], skb->data, hdr_len); 64979f33912SNimrod Andy bufaddr = fep->tx_bounce[index]; 65079f33912SNimrod Andy 65179f33912SNimrod Andy if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) 65279f33912SNimrod Andy swap_buffer(bufaddr, hdr_len); 65379f33912SNimrod Andy 65479f33912SNimrod Andy dmabuf = dma_map_single(&fep->pdev->dev, bufaddr, 65579f33912SNimrod Andy hdr_len, DMA_TO_DEVICE); 65679f33912SNimrod Andy if (dma_mapping_error(&fep->pdev->dev, dmabuf)) { 65779f33912SNimrod Andy dev_kfree_skb_any(skb); 65879f33912SNimrod Andy if (net_ratelimit()) 65979f33912SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 66079f33912SNimrod Andy return NETDEV_TX_BUSY; 66179f33912SNimrod Andy } 66279f33912SNimrod Andy } 66379f33912SNimrod Andy 66479f33912SNimrod Andy bdp->cbd_bufaddr = dmabuf; 66579f33912SNimrod Andy bdp->cbd_datlen = hdr_len; 66679f33912SNimrod Andy 66779f33912SNimrod Andy if (fep->bufdesc_ex) { 66879f33912SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 66979f33912SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 67079f33912SNimrod Andy ebdp->cbd_bdu = 0; 67179f33912SNimrod Andy ebdp->cbd_esc = estatus; 67279f33912SNimrod Andy } 67379f33912SNimrod Andy 67479f33912SNimrod Andy bdp->cbd_sc = status; 67579f33912SNimrod Andy 67679f33912SNimrod Andy return 0; 67779f33912SNimrod Andy } 67879f33912SNimrod Andy 67979f33912SNimrod Andy static int fec_enet_txq_submit_tso(struct sk_buff *skb, struct net_device *ndev) 68079f33912SNimrod Andy { 68179f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 68279f33912SNimrod Andy int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); 68379f33912SNimrod Andy int total_len, data_left; 68479f33912SNimrod Andy struct bufdesc *bdp = fep->cur_tx; 68579f33912SNimrod Andy struct tso_t tso; 68679f33912SNimrod Andy unsigned int index = 0; 68779f33912SNimrod Andy int ret; 68879f33912SNimrod Andy 68979f33912SNimrod Andy if (tso_count_descs(skb) >= fec_enet_get_free_txdesc_num(fep)) { 69079f33912SNimrod Andy dev_kfree_skb_any(skb); 69179f33912SNimrod Andy if (net_ratelimit()) 69279f33912SNimrod Andy netdev_err(ndev, "NOT enough BD for TSO!\n"); 69379f33912SNimrod Andy return NETDEV_TX_OK; 69479f33912SNimrod Andy } 69579f33912SNimrod Andy 69679f33912SNimrod Andy /* Protocol checksum off-load for TCP and UDP. */ 69779f33912SNimrod Andy if (fec_enet_clear_csum(skb, ndev)) { 69879f33912SNimrod Andy dev_kfree_skb_any(skb); 69979f33912SNimrod Andy return NETDEV_TX_OK; 70079f33912SNimrod Andy } 70179f33912SNimrod Andy 70279f33912SNimrod Andy /* Initialize the TSO handler, and prepare the first payload */ 70379f33912SNimrod Andy tso_start(skb, &tso); 70479f33912SNimrod Andy 70579f33912SNimrod Andy total_len = skb->len - hdr_len; 70679f33912SNimrod Andy while (total_len > 0) { 70779f33912SNimrod Andy char *hdr; 70879f33912SNimrod Andy 70979f33912SNimrod Andy index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep); 71079f33912SNimrod Andy data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len); 71179f33912SNimrod Andy total_len -= data_left; 71279f33912SNimrod Andy 71379f33912SNimrod Andy /* prepare packet headers: MAC + IP + TCP */ 71479f33912SNimrod Andy hdr = fep->tso_hdrs + index * TSO_HEADER_SIZE; 71579f33912SNimrod Andy tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0); 71679f33912SNimrod Andy ret = fec_enet_txq_put_hdr_tso(skb, ndev, bdp, index); 71779f33912SNimrod Andy if (ret) 71879f33912SNimrod Andy goto err_release; 71979f33912SNimrod Andy 72079f33912SNimrod Andy while (data_left > 0) { 72179f33912SNimrod Andy int size; 72279f33912SNimrod Andy 72379f33912SNimrod Andy size = min_t(int, tso.size, data_left); 72479f33912SNimrod Andy bdp = fec_enet_get_nextdesc(bdp, fep); 72579f33912SNimrod Andy index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep); 72679f33912SNimrod Andy ret = fec_enet_txq_put_data_tso(skb, ndev, bdp, index, tso.data, 72779f33912SNimrod Andy size, size == data_left, 72879f33912SNimrod Andy total_len == 0); 72979f33912SNimrod Andy if (ret) 73079f33912SNimrod Andy goto err_release; 73179f33912SNimrod Andy 73279f33912SNimrod Andy data_left -= size; 73379f33912SNimrod Andy tso_build_data(skb, &tso, size); 73479f33912SNimrod Andy } 73579f33912SNimrod Andy 73679f33912SNimrod Andy bdp = fec_enet_get_nextdesc(bdp, fep); 73779f33912SNimrod Andy } 73879f33912SNimrod Andy 73979f33912SNimrod Andy /* Save skb pointer */ 74079f33912SNimrod Andy fep->tx_skbuff[index] = skb; 74179f33912SNimrod Andy 74279f33912SNimrod Andy skb_tx_timestamp(skb); 74379f33912SNimrod Andy fep->cur_tx = bdp; 74479f33912SNimrod Andy 74579f33912SNimrod Andy /* Trigger transmission start */ 74679f33912SNimrod Andy writel(0, fep->hwp + FEC_X_DES_ACTIVE); 74779f33912SNimrod Andy 74879f33912SNimrod Andy return 0; 74979f33912SNimrod Andy 75079f33912SNimrod Andy err_release: 75179f33912SNimrod Andy /* TODO: Release all used data descriptors for TSO */ 75279f33912SNimrod Andy return ret; 75379f33912SNimrod Andy } 75479f33912SNimrod Andy 75561a4427bSNimrod Andy static netdev_tx_t 75661a4427bSNimrod Andy fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) 75761a4427bSNimrod Andy { 75861a4427bSNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 7596e909283SNimrod Andy int entries_free; 76061a4427bSNimrod Andy int ret; 76161a4427bSNimrod Andy 76279f33912SNimrod Andy if (skb_is_gso(skb)) 76379f33912SNimrod Andy ret = fec_enet_txq_submit_tso(skb, ndev); 76479f33912SNimrod Andy else 7656e909283SNimrod Andy ret = fec_enet_txq_submit_skb(skb, ndev); 7666e909283SNimrod Andy if (ret) 7676e909283SNimrod Andy return ret; 76861a4427bSNimrod Andy 7696e909283SNimrod Andy entries_free = fec_enet_get_free_txdesc_num(fep); 77079f33912SNimrod Andy if (entries_free <= fep->tx_stop_threshold) 77161a4427bSNimrod Andy netif_stop_queue(ndev); 77261a4427bSNimrod Andy 77361a4427bSNimrod Andy return NETDEV_TX_OK; 77461a4427bSNimrod Andy } 77561a4427bSNimrod Andy 776a210576cSDavid S. Miller /* Init RX & TX buffer descriptors 777a210576cSDavid S. Miller */ 778a210576cSDavid S. Miller static void fec_enet_bd_init(struct net_device *dev) 779a210576cSDavid S. Miller { 780a210576cSDavid S. Miller struct fec_enet_private *fep = netdev_priv(dev); 781a210576cSDavid S. Miller struct bufdesc *bdp; 782a210576cSDavid S. Miller unsigned int i; 783a210576cSDavid S. Miller 784a210576cSDavid S. Miller /* Initialize the receive buffer descriptors. */ 785a210576cSDavid S. Miller bdp = fep->rx_bd_base; 78636e24e2eSDuan Fugang-B38611 for (i = 0; i < fep->rx_ring_size; i++) { 787a210576cSDavid S. Miller 788a210576cSDavid S. Miller /* Initialize the BD for every fragment in the page. */ 789a210576cSDavid S. Miller if (bdp->cbd_bufaddr) 790a210576cSDavid S. Miller bdp->cbd_sc = BD_ENET_RX_EMPTY; 791a210576cSDavid S. Miller else 792a210576cSDavid S. Miller bdp->cbd_sc = 0; 79336e24e2eSDuan Fugang-B38611 bdp = fec_enet_get_nextdesc(bdp, fep); 794a210576cSDavid S. Miller } 795a210576cSDavid S. Miller 796a210576cSDavid S. Miller /* Set the last buffer to wrap */ 79736e24e2eSDuan Fugang-B38611 bdp = fec_enet_get_prevdesc(bdp, fep); 798a210576cSDavid S. Miller bdp->cbd_sc |= BD_SC_WRAP; 799a210576cSDavid S. Miller 800a210576cSDavid S. Miller fep->cur_rx = fep->rx_bd_base; 801a210576cSDavid S. Miller 802a210576cSDavid S. Miller /* ...and the same for transmit */ 803a210576cSDavid S. Miller bdp = fep->tx_bd_base; 804a210576cSDavid S. Miller fep->cur_tx = bdp; 80536e24e2eSDuan Fugang-B38611 for (i = 0; i < fep->tx_ring_size; i++) { 806a210576cSDavid S. Miller 807a210576cSDavid S. Miller /* Initialize the BD for every fragment in the page. */ 808a210576cSDavid S. Miller bdp->cbd_sc = 0; 809d6bf3143SRussell King if (fep->tx_skbuff[i]) { 810a210576cSDavid S. Miller dev_kfree_skb_any(fep->tx_skbuff[i]); 811a210576cSDavid S. Miller fep->tx_skbuff[i] = NULL; 812a210576cSDavid S. Miller } 813a210576cSDavid S. Miller bdp->cbd_bufaddr = 0; 81436e24e2eSDuan Fugang-B38611 bdp = fec_enet_get_nextdesc(bdp, fep); 815a210576cSDavid S. Miller } 816a210576cSDavid S. Miller 817a210576cSDavid S. Miller /* Set the last buffer to wrap */ 81836e24e2eSDuan Fugang-B38611 bdp = fec_enet_get_prevdesc(bdp, fep); 819a210576cSDavid S. Miller bdp->cbd_sc |= BD_SC_WRAP; 820a210576cSDavid S. Miller fep->dirty_tx = bdp; 821a210576cSDavid S. Miller } 822a210576cSDavid S. Miller 823dbc64a8eSRussell King /* 824dbc64a8eSRussell King * This function is called to start or restart the FEC during a link 825dbc64a8eSRussell King * change, transmit timeout, or to reconfigure the FEC. The network 826dbc64a8eSRussell King * packet processing for this device must be stopped before this call. 827793fc096SFrank Li */ 828793fc096SFrank Li static void 829ef83337dSRussell King fec_restart(struct net_device *ndev) 830793fc096SFrank Li { 831793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 832793fc096SFrank Li const struct platform_device_id *id_entry = 833793fc096SFrank Li platform_get_device_id(fep->pdev); 834793fc096SFrank Li int i; 8354c09eed9SJim Baxter u32 val; 836793fc096SFrank Li u32 temp_mac[2]; 837793fc096SFrank Li u32 rcntl = OPT_FRAME_SIZE | 0x04; 838793fc096SFrank Li u32 ecntl = 0x2; /* ETHEREN */ 839793fc096SFrank Li 840793fc096SFrank Li /* Whack a reset. We should wait for this. */ 841793fc096SFrank Li writel(1, fep->hwp + FEC_ECNTRL); 842793fc096SFrank Li udelay(10); 843793fc096SFrank Li 844793fc096SFrank Li /* 845793fc096SFrank Li * enet-mac reset will reset mac address registers too, 846793fc096SFrank Li * so need to reconfigure it. 847793fc096SFrank Li */ 848793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) { 849793fc096SFrank Li memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN); 850793fc096SFrank Li writel(cpu_to_be32(temp_mac[0]), fep->hwp + FEC_ADDR_LOW); 851793fc096SFrank Li writel(cpu_to_be32(temp_mac[1]), fep->hwp + FEC_ADDR_HIGH); 852793fc096SFrank Li } 853793fc096SFrank Li 854793fc096SFrank Li /* Clear any outstanding interrupt. */ 855793fc096SFrank Li writel(0xffc00000, fep->hwp + FEC_IEVENT); 856793fc096SFrank Li 857793fc096SFrank Li /* Set maximum receive buffer size. */ 858793fc096SFrank Li writel(PKT_MAXBLR_SIZE, fep->hwp + FEC_R_BUFF_SIZE); 859793fc096SFrank Li 860a210576cSDavid S. Miller fec_enet_bd_init(ndev); 861a210576cSDavid S. Miller 862793fc096SFrank Li /* Set receive and transmit descriptor base. */ 863793fc096SFrank Li writel(fep->bd_dma, fep->hwp + FEC_R_DES_START); 864793fc096SFrank Li if (fep->bufdesc_ex) 865793fc096SFrank Li writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc_ex) 86636e24e2eSDuan Fugang-B38611 * fep->rx_ring_size, fep->hwp + FEC_X_DES_START); 867793fc096SFrank Li else 868793fc096SFrank Li writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc) 86936e24e2eSDuan Fugang-B38611 * fep->rx_ring_size, fep->hwp + FEC_X_DES_START); 870793fc096SFrank Li 871793fc096SFrank Li 872793fc096SFrank Li for (i = 0; i <= TX_RING_MOD_MASK; i++) { 873793fc096SFrank Li if (fep->tx_skbuff[i]) { 874793fc096SFrank Li dev_kfree_skb_any(fep->tx_skbuff[i]); 875793fc096SFrank Li fep->tx_skbuff[i] = NULL; 876793fc096SFrank Li } 877793fc096SFrank Li } 878793fc096SFrank Li 879793fc096SFrank Li /* Enable MII mode */ 880ef83337dSRussell King if (fep->full_duplex == DUPLEX_FULL) { 881793fc096SFrank Li /* FD enable */ 882793fc096SFrank Li writel(0x04, fep->hwp + FEC_X_CNTRL); 883793fc096SFrank Li } else { 884793fc096SFrank Li /* No Rcv on Xmit */ 885793fc096SFrank Li rcntl |= 0x02; 886793fc096SFrank Li writel(0x0, fep->hwp + FEC_X_CNTRL); 887793fc096SFrank Li } 888793fc096SFrank Li 889793fc096SFrank Li /* Set MII speed */ 890793fc096SFrank Li writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 891793fc096SFrank Li 892d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 8934c09eed9SJim Baxter /* set RX checksum */ 8944c09eed9SJim Baxter val = readl(fep->hwp + FEC_RACC); 8954c09eed9SJim Baxter if (fep->csum_flags & FLAG_RX_CSUM_ENABLED) 8964c09eed9SJim Baxter val |= FEC_RACC_OPTIONS; 8974c09eed9SJim Baxter else 8984c09eed9SJim Baxter val &= ~FEC_RACC_OPTIONS; 8994c09eed9SJim Baxter writel(val, fep->hwp + FEC_RACC); 900d1391930SGuenter Roeck #endif 9014c09eed9SJim Baxter 902793fc096SFrank Li /* 903793fc096SFrank Li * The phy interface and speed need to get configured 904793fc096SFrank Li * differently on enet-mac. 905793fc096SFrank Li */ 906793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) { 907793fc096SFrank Li /* Enable flow control and length check */ 908793fc096SFrank Li rcntl |= 0x40000000 | 0x00000020; 909793fc096SFrank Li 910793fc096SFrank Li /* RGMII, RMII or MII */ 911793fc096SFrank Li if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII) 912793fc096SFrank Li rcntl |= (1 << 6); 913793fc096SFrank Li else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) 914793fc096SFrank Li rcntl |= (1 << 8); 915793fc096SFrank Li else 916793fc096SFrank Li rcntl &= ~(1 << 8); 917793fc096SFrank Li 918793fc096SFrank Li /* 1G, 100M or 10M */ 919793fc096SFrank Li if (fep->phy_dev) { 920793fc096SFrank Li if (fep->phy_dev->speed == SPEED_1000) 921793fc096SFrank Li ecntl |= (1 << 5); 922793fc096SFrank Li else if (fep->phy_dev->speed == SPEED_100) 923793fc096SFrank Li rcntl &= ~(1 << 9); 924793fc096SFrank Li else 925793fc096SFrank Li rcntl |= (1 << 9); 926793fc096SFrank Li } 927793fc096SFrank Li } else { 928793fc096SFrank Li #ifdef FEC_MIIGSK_ENR 929793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_USE_GASKET) { 930793fc096SFrank Li u32 cfgr; 931793fc096SFrank Li /* disable the gasket and wait */ 932793fc096SFrank Li writel(0, fep->hwp + FEC_MIIGSK_ENR); 933793fc096SFrank Li while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4) 934793fc096SFrank Li udelay(1); 935793fc096SFrank Li 936793fc096SFrank Li /* 937793fc096SFrank Li * configure the gasket: 938793fc096SFrank Li * RMII, 50 MHz, no loopback, no echo 939793fc096SFrank Li * MII, 25 MHz, no loopback, no echo 940793fc096SFrank Li */ 941793fc096SFrank Li cfgr = (fep->phy_interface == PHY_INTERFACE_MODE_RMII) 942793fc096SFrank Li ? BM_MIIGSK_CFGR_RMII : BM_MIIGSK_CFGR_MII; 943793fc096SFrank Li if (fep->phy_dev && fep->phy_dev->speed == SPEED_10) 944793fc096SFrank Li cfgr |= BM_MIIGSK_CFGR_FRCONT_10M; 945793fc096SFrank Li writel(cfgr, fep->hwp + FEC_MIIGSK_CFGR); 946793fc096SFrank Li 947793fc096SFrank Li /* re-enable the gasket */ 948793fc096SFrank Li writel(2, fep->hwp + FEC_MIIGSK_ENR); 949793fc096SFrank Li } 950793fc096SFrank Li #endif 951793fc096SFrank Li } 952793fc096SFrank Li 953d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 954793fc096SFrank Li /* enable pause frame*/ 955793fc096SFrank Li if ((fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) || 956793fc096SFrank Li ((fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) && 957793fc096SFrank Li fep->phy_dev && fep->phy_dev->pause)) { 958793fc096SFrank Li rcntl |= FEC_ENET_FCE; 959793fc096SFrank Li 9604c09eed9SJim Baxter /* set FIFO threshold parameter to reduce overrun */ 961793fc096SFrank Li writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM); 962793fc096SFrank Li writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL); 963793fc096SFrank Li writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM); 964793fc096SFrank Li writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL); 965793fc096SFrank Li 966793fc096SFrank Li /* OPD */ 967793fc096SFrank Li writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD); 968793fc096SFrank Li } else { 969793fc096SFrank Li rcntl &= ~FEC_ENET_FCE; 970793fc096SFrank Li } 971d1391930SGuenter Roeck #endif /* !defined(CONFIG_M5272) */ 972793fc096SFrank Li 973793fc096SFrank Li writel(rcntl, fep->hwp + FEC_R_CNTRL); 974793fc096SFrank Li 97584fe6182SStefan Wahren /* Setup multicast filter. */ 97684fe6182SStefan Wahren set_multicast_list(ndev); 97784fe6182SStefan Wahren #ifndef CONFIG_M5272 97884fe6182SStefan Wahren writel(0, fep->hwp + FEC_HASH_TABLE_HIGH); 97984fe6182SStefan Wahren writel(0, fep->hwp + FEC_HASH_TABLE_LOW); 98084fe6182SStefan Wahren #endif 98184fe6182SStefan Wahren 982793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) { 983793fc096SFrank Li /* enable ENET endian swap */ 984793fc096SFrank Li ecntl |= (1 << 8); 985793fc096SFrank Li /* enable ENET store and forward mode */ 986793fc096SFrank Li writel(1 << 8, fep->hwp + FEC_X_WMRK); 987793fc096SFrank Li } 988793fc096SFrank Li 989793fc096SFrank Li if (fep->bufdesc_ex) 990793fc096SFrank Li ecntl |= (1 << 4); 991793fc096SFrank Li 99238ae92dcSChris Healy #ifndef CONFIG_M5272 993b9eef55cSJim Baxter /* Enable the MIB statistic event counters */ 994b9eef55cSJim Baxter writel(0 << 31, fep->hwp + FEC_MIB_CTRLSTAT); 99538ae92dcSChris Healy #endif 99638ae92dcSChris Healy 997793fc096SFrank Li /* And last, enable the transmit and receive processing */ 998793fc096SFrank Li writel(ecntl, fep->hwp + FEC_ECNTRL); 999793fc096SFrank Li writel(0, fep->hwp + FEC_R_DES_ACTIVE); 1000793fc096SFrank Li 1001793fc096SFrank Li if (fep->bufdesc_ex) 1002793fc096SFrank Li fec_ptp_start_cyclecounter(ndev); 1003793fc096SFrank Li 1004793fc096SFrank Li /* Enable interrupts we wish to service */ 1005793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 1006793fc096SFrank Li } 1007793fc096SFrank Li 1008793fc096SFrank Li static void 1009793fc096SFrank Li fec_stop(struct net_device *ndev) 1010793fc096SFrank Li { 1011793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1012793fc096SFrank Li const struct platform_device_id *id_entry = 1013793fc096SFrank Li platform_get_device_id(fep->pdev); 1014793fc096SFrank Li u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8); 1015793fc096SFrank Li 1016793fc096SFrank Li /* We cannot expect a graceful transmit stop without link !!! */ 1017793fc096SFrank Li if (fep->link) { 1018793fc096SFrank Li writel(1, fep->hwp + FEC_X_CNTRL); /* Graceful transmit stop */ 1019793fc096SFrank Li udelay(10); 1020793fc096SFrank Li if (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_GRA)) 102131b7720cSJoe Perches netdev_err(ndev, "Graceful transmit stop did not complete!\n"); 1022793fc096SFrank Li } 1023793fc096SFrank Li 1024793fc096SFrank Li /* Whack a reset. We should wait for this. */ 1025793fc096SFrank Li writel(1, fep->hwp + FEC_ECNTRL); 1026793fc096SFrank Li udelay(10); 1027793fc096SFrank Li writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 1028793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 1029793fc096SFrank Li 1030793fc096SFrank Li /* We have to keep ENET enabled to have MII interrupt stay working */ 1031793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) { 1032793fc096SFrank Li writel(2, fep->hwp + FEC_ECNTRL); 1033793fc096SFrank Li writel(rmii_mode, fep->hwp + FEC_R_CNTRL); 1034793fc096SFrank Li } 1035793fc096SFrank Li } 1036793fc096SFrank Li 1037793fc096SFrank Li 1038793fc096SFrank Li static void 1039793fc096SFrank Li fec_timeout(struct net_device *ndev) 1040793fc096SFrank Li { 1041793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1042793fc096SFrank Li 1043344756f6SRussell King fec_dump(ndev); 1044344756f6SRussell King 1045793fc096SFrank Li ndev->stats.tx_errors++; 1046793fc096SFrank Li 104736cdc743SRussell King schedule_work(&fep->tx_timeout_work); 104854309fa6SFrank Li } 104954309fa6SFrank Li 105036cdc743SRussell King static void fec_enet_timeout_work(struct work_struct *work) 105154309fa6SFrank Li { 105254309fa6SFrank Li struct fec_enet_private *fep = 105336cdc743SRussell King container_of(work, struct fec_enet_private, tx_timeout_work); 10548ce5624fSRussell King struct net_device *ndev = fep->netdev; 105554309fa6SFrank Li 1056da1774e5SRussell King rtnl_lock(); 10578ce5624fSRussell King if (netif_device_present(ndev) || netif_running(ndev)) { 1058dbc64a8eSRussell King napi_disable(&fep->napi); 1059dbc64a8eSRussell King netif_tx_lock_bh(ndev); 1060ef83337dSRussell King fec_restart(ndev); 10618ce5624fSRussell King netif_wake_queue(ndev); 10626af42d42SRussell King netif_tx_unlock_bh(ndev); 1063dbc64a8eSRussell King napi_enable(&fep->napi); 10648ce5624fSRussell King } 1065da1774e5SRussell King rtnl_unlock(); 106654309fa6SFrank Li } 1067793fc096SFrank Li 1068793fc096SFrank Li static void 1069bfd4ecddSRussell King fec_enet_hwtstamp(struct fec_enet_private *fep, unsigned ts, 1070bfd4ecddSRussell King struct skb_shared_hwtstamps *hwtstamps) 1071bfd4ecddSRussell King { 1072bfd4ecddSRussell King unsigned long flags; 1073bfd4ecddSRussell King u64 ns; 1074bfd4ecddSRussell King 1075bfd4ecddSRussell King spin_lock_irqsave(&fep->tmreg_lock, flags); 1076bfd4ecddSRussell King ns = timecounter_cyc2time(&fep->tc, ts); 1077bfd4ecddSRussell King spin_unlock_irqrestore(&fep->tmreg_lock, flags); 1078bfd4ecddSRussell King 1079bfd4ecddSRussell King memset(hwtstamps, 0, sizeof(*hwtstamps)); 1080bfd4ecddSRussell King hwtstamps->hwtstamp = ns_to_ktime(ns); 1081bfd4ecddSRussell King } 1082bfd4ecddSRussell King 1083bfd4ecddSRussell King static void 1084793fc096SFrank Li fec_enet_tx(struct net_device *ndev) 1085793fc096SFrank Li { 1086793fc096SFrank Li struct fec_enet_private *fep; 1087793fc096SFrank Li struct bufdesc *bdp; 1088793fc096SFrank Li unsigned short status; 1089793fc096SFrank Li struct sk_buff *skb; 1090793fc096SFrank Li int index = 0; 109179f33912SNimrod Andy int entries_free; 1092793fc096SFrank Li 1093793fc096SFrank Li fep = netdev_priv(ndev); 1094793fc096SFrank Li bdp = fep->dirty_tx; 1095793fc096SFrank Li 1096793fc096SFrank Li /* get next bdp of dirty_tx */ 109736e24e2eSDuan Fugang-B38611 bdp = fec_enet_get_nextdesc(bdp, fep); 1098793fc096SFrank Li 1099793fc096SFrank Li while (((status = bdp->cbd_sc) & BD_ENET_TX_READY) == 0) { 1100793fc096SFrank Li 1101793fc096SFrank Li /* current queue is empty */ 1102793fc096SFrank Li if (bdp == fep->cur_tx) 1103793fc096SFrank Li break; 1104793fc096SFrank Li 110561a4427bSNimrod Andy index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep); 1106793fc096SFrank Li 1107793fc096SFrank Li skb = fep->tx_skbuff[index]; 1108d6bf3143SRussell King fep->tx_skbuff[index] = NULL; 110979f33912SNimrod Andy if (!IS_TSO_HEADER(fep, bdp->cbd_bufaddr)) 111079f33912SNimrod Andy dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, 111179f33912SNimrod Andy bdp->cbd_datlen, DMA_TO_DEVICE); 11122488a54eSSebastian Siewior bdp->cbd_bufaddr = 0; 11136e909283SNimrod Andy if (!skb) { 11146e909283SNimrod Andy bdp = fec_enet_get_nextdesc(bdp, fep); 11156e909283SNimrod Andy continue; 11166e909283SNimrod Andy } 1117793fc096SFrank Li 1118793fc096SFrank Li /* Check for errors. */ 1119793fc096SFrank Li if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC | 1120793fc096SFrank Li BD_ENET_TX_RL | BD_ENET_TX_UN | 1121793fc096SFrank Li BD_ENET_TX_CSL)) { 1122793fc096SFrank Li ndev->stats.tx_errors++; 1123793fc096SFrank Li if (status & BD_ENET_TX_HB) /* No heartbeat */ 1124793fc096SFrank Li ndev->stats.tx_heartbeat_errors++; 1125793fc096SFrank Li if (status & BD_ENET_TX_LC) /* Late collision */ 1126793fc096SFrank Li ndev->stats.tx_window_errors++; 1127793fc096SFrank Li if (status & BD_ENET_TX_RL) /* Retrans limit */ 1128793fc096SFrank Li ndev->stats.tx_aborted_errors++; 1129793fc096SFrank Li if (status & BD_ENET_TX_UN) /* Underrun */ 1130793fc096SFrank Li ndev->stats.tx_fifo_errors++; 1131793fc096SFrank Li if (status & BD_ENET_TX_CSL) /* Carrier lost */ 1132793fc096SFrank Li ndev->stats.tx_carrier_errors++; 1133793fc096SFrank Li } else { 1134793fc096SFrank Li ndev->stats.tx_packets++; 11356e909283SNimrod Andy ndev->stats.tx_bytes += skb->len; 1136793fc096SFrank Li } 1137793fc096SFrank Li 1138793fc096SFrank Li if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) && 1139793fc096SFrank Li fep->bufdesc_ex) { 1140793fc096SFrank Li struct skb_shared_hwtstamps shhwtstamps; 1141793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 1142793fc096SFrank Li 1143bfd4ecddSRussell King fec_enet_hwtstamp(fep, ebdp->ts, &shhwtstamps); 1144793fc096SFrank Li skb_tstamp_tx(skb, &shhwtstamps); 1145793fc096SFrank Li } 1146793fc096SFrank Li 1147793fc096SFrank Li /* Deferred means some collisions occurred during transmit, 1148793fc096SFrank Li * but we eventually sent the packet OK. 1149793fc096SFrank Li */ 1150793fc096SFrank Li if (status & BD_ENET_TX_DEF) 1151793fc096SFrank Li ndev->stats.collisions++; 1152793fc096SFrank Li 1153793fc096SFrank Li /* Free the sk buffer associated with this last transmit */ 1154793fc096SFrank Li dev_kfree_skb_any(skb); 1155793fc096SFrank Li 1156793fc096SFrank Li fep->dirty_tx = bdp; 1157793fc096SFrank Li 1158793fc096SFrank Li /* Update pointer to next buffer descriptor to be transmitted */ 115936e24e2eSDuan Fugang-B38611 bdp = fec_enet_get_nextdesc(bdp, fep); 1160793fc096SFrank Li 1161793fc096SFrank Li /* Since we have freed up a buffer, the ring is no longer full 1162793fc096SFrank Li */ 116379f33912SNimrod Andy if (netif_queue_stopped(ndev)) { 116479f33912SNimrod Andy entries_free = fec_enet_get_free_txdesc_num(fep); 116579f33912SNimrod Andy if (entries_free >= fep->tx_wake_threshold) 1166793fc096SFrank Li netif_wake_queue(ndev); 1167793fc096SFrank Li } 116879f33912SNimrod Andy } 1169ccea2968SRussell King 1170ccea2968SRussell King /* ERR006538: Keep the transmitter going */ 1171ccea2968SRussell King if (bdp != fep->cur_tx && readl(fep->hwp + FEC_X_DES_ACTIVE) == 0) 1172ccea2968SRussell King writel(0, fep->hwp + FEC_X_DES_ACTIVE); 1173793fc096SFrank Li } 1174793fc096SFrank Li 1175793fc096SFrank Li /* During a receive, the cur_rx points to the current incoming buffer. 1176793fc096SFrank Li * When we update through the ring, if the next incoming buffer has 1177793fc096SFrank Li * not been given to the system, we just set the empty indicator, 1178793fc096SFrank Li * effectively tossing the packet. 1179793fc096SFrank Li */ 1180793fc096SFrank Li static int 1181793fc096SFrank Li fec_enet_rx(struct net_device *ndev, int budget) 1182793fc096SFrank Li { 1183793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1184793fc096SFrank Li const struct platform_device_id *id_entry = 1185793fc096SFrank Li platform_get_device_id(fep->pdev); 1186793fc096SFrank Li struct bufdesc *bdp; 1187793fc096SFrank Li unsigned short status; 1188793fc096SFrank Li struct sk_buff *skb; 1189793fc096SFrank Li ushort pkt_len; 1190793fc096SFrank Li __u8 *data; 1191793fc096SFrank Li int pkt_received = 0; 1192cdffcf1bSJim Baxter struct bufdesc_ex *ebdp = NULL; 1193cdffcf1bSJim Baxter bool vlan_packet_rcvd = false; 1194cdffcf1bSJim Baxter u16 vlan_tag; 1195d842a31fSDuan Fugang-B38611 int index = 0; 1196793fc096SFrank Li 1197793fc096SFrank Li #ifdef CONFIG_M532x 1198793fc096SFrank Li flush_cache_all(); 1199793fc096SFrank Li #endif 1200793fc096SFrank Li 1201793fc096SFrank Li /* First, grab all of the stats for the incoming packet. 1202793fc096SFrank Li * These get messed up if we get called due to a busy condition. 1203793fc096SFrank Li */ 1204793fc096SFrank Li bdp = fep->cur_rx; 1205793fc096SFrank Li 1206793fc096SFrank Li while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) { 1207793fc096SFrank Li 1208793fc096SFrank Li if (pkt_received >= budget) 1209793fc096SFrank Li break; 1210793fc096SFrank Li pkt_received++; 1211793fc096SFrank Li 1212793fc096SFrank Li /* Since we have allocated space to hold a complete frame, 1213793fc096SFrank Li * the last indicator should be set. 1214793fc096SFrank Li */ 1215793fc096SFrank Li if ((status & BD_ENET_RX_LAST) == 0) 121631b7720cSJoe Perches netdev_err(ndev, "rcv is not +last\n"); 1217793fc096SFrank Li 1218db3421c1SRussell King writel(FEC_ENET_RXF, fep->hwp + FEC_IEVENT); 1219db3421c1SRussell King 1220793fc096SFrank Li /* Check for errors. */ 1221793fc096SFrank Li if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | 1222793fc096SFrank Li BD_ENET_RX_CR | BD_ENET_RX_OV)) { 1223793fc096SFrank Li ndev->stats.rx_errors++; 1224793fc096SFrank Li if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH)) { 1225793fc096SFrank Li /* Frame too long or too short. */ 1226793fc096SFrank Li ndev->stats.rx_length_errors++; 1227793fc096SFrank Li } 1228793fc096SFrank Li if (status & BD_ENET_RX_NO) /* Frame alignment */ 1229793fc096SFrank Li ndev->stats.rx_frame_errors++; 1230793fc096SFrank Li if (status & BD_ENET_RX_CR) /* CRC Error */ 1231793fc096SFrank Li ndev->stats.rx_crc_errors++; 1232793fc096SFrank Li if (status & BD_ENET_RX_OV) /* FIFO overrun */ 1233793fc096SFrank Li ndev->stats.rx_fifo_errors++; 1234793fc096SFrank Li } 1235793fc096SFrank Li 1236793fc096SFrank Li /* Report late collisions as a frame error. 1237793fc096SFrank Li * On this error, the BD is closed, but we don't know what we 1238793fc096SFrank Li * have in the buffer. So, just drop this frame on the floor. 1239793fc096SFrank Li */ 1240793fc096SFrank Li if (status & BD_ENET_RX_CL) { 1241793fc096SFrank Li ndev->stats.rx_errors++; 1242793fc096SFrank Li ndev->stats.rx_frame_errors++; 1243793fc096SFrank Li goto rx_processing_done; 1244793fc096SFrank Li } 1245793fc096SFrank Li 1246793fc096SFrank Li /* Process the incoming frame. */ 1247793fc096SFrank Li ndev->stats.rx_packets++; 1248793fc096SFrank Li pkt_len = bdp->cbd_datlen; 1249793fc096SFrank Li ndev->stats.rx_bytes += pkt_len; 1250793fc096SFrank Li 125161a4427bSNimrod Andy index = fec_enet_get_bd_index(fep->rx_bd_base, bdp, fep); 1252d842a31fSDuan Fugang-B38611 data = fep->rx_skbuff[index]->data; 1253d842a31fSDuan Fugang-B38611 dma_sync_single_for_cpu(&fep->pdev->dev, bdp->cbd_bufaddr, 1254d842a31fSDuan Fugang-B38611 FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE); 1255793fc096SFrank Li 1256793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) 1257793fc096SFrank Li swap_buffer(data, pkt_len); 1258793fc096SFrank Li 1259cdffcf1bSJim Baxter /* Extract the enhanced buffer descriptor */ 1260cdffcf1bSJim Baxter ebdp = NULL; 1261cdffcf1bSJim Baxter if (fep->bufdesc_ex) 1262cdffcf1bSJim Baxter ebdp = (struct bufdesc_ex *)bdp; 1263cdffcf1bSJim Baxter 1264cdffcf1bSJim Baxter /* If this is a VLAN packet remove the VLAN Tag */ 1265cdffcf1bSJim Baxter vlan_packet_rcvd = false; 1266cdffcf1bSJim Baxter if ((ndev->features & NETIF_F_HW_VLAN_CTAG_RX) && 1267cdffcf1bSJim Baxter fep->bufdesc_ex && (ebdp->cbd_esc & BD_ENET_RX_VLAN)) { 1268cdffcf1bSJim Baxter /* Push and remove the vlan tag */ 1269cdffcf1bSJim Baxter struct vlan_hdr *vlan_header = 1270cdffcf1bSJim Baxter (struct vlan_hdr *) (data + ETH_HLEN); 1271cdffcf1bSJim Baxter vlan_tag = ntohs(vlan_header->h_vlan_TCI); 1272cdffcf1bSJim Baxter pkt_len -= VLAN_HLEN; 1273cdffcf1bSJim Baxter 1274cdffcf1bSJim Baxter vlan_packet_rcvd = true; 1275cdffcf1bSJim Baxter } 1276cdffcf1bSJim Baxter 1277793fc096SFrank Li /* This does 16 byte alignment, exactly what we need. 1278793fc096SFrank Li * The packet length includes FCS, but we don't want to 1279793fc096SFrank Li * include that when passing upstream as it messes up 1280793fc096SFrank Li * bridging applications. 1281793fc096SFrank Li */ 1282793fc096SFrank Li skb = netdev_alloc_skb(ndev, pkt_len - 4 + NET_IP_ALIGN); 1283793fc096SFrank Li 1284793fc096SFrank Li if (unlikely(!skb)) { 1285793fc096SFrank Li ndev->stats.rx_dropped++; 1286793fc096SFrank Li } else { 1287cdffcf1bSJim Baxter int payload_offset = (2 * ETH_ALEN); 1288793fc096SFrank Li skb_reserve(skb, NET_IP_ALIGN); 1289793fc096SFrank Li skb_put(skb, pkt_len - 4); /* Make room */ 1290cdffcf1bSJim Baxter 1291cdffcf1bSJim Baxter /* Extract the frame data without the VLAN header. */ 1292cdffcf1bSJim Baxter skb_copy_to_linear_data(skb, data, (2 * ETH_ALEN)); 1293cdffcf1bSJim Baxter if (vlan_packet_rcvd) 1294cdffcf1bSJim Baxter payload_offset = (2 * ETH_ALEN) + VLAN_HLEN; 1295cdffcf1bSJim Baxter skb_copy_to_linear_data_offset(skb, (2 * ETH_ALEN), 1296cdffcf1bSJim Baxter data + payload_offset, 1297cdffcf1bSJim Baxter pkt_len - 4 - (2 * ETH_ALEN)); 1298cdffcf1bSJim Baxter 1299793fc096SFrank Li skb->protocol = eth_type_trans(skb, ndev); 1300793fc096SFrank Li 1301793fc096SFrank Li /* Get receive timestamp from the skb */ 1302bfd4ecddSRussell King if (fep->hwts_rx_en && fep->bufdesc_ex) 1303bfd4ecddSRussell King fec_enet_hwtstamp(fep, ebdp->ts, 1304bfd4ecddSRussell King skb_hwtstamps(skb)); 1305793fc096SFrank Li 13064c09eed9SJim Baxter if (fep->bufdesc_ex && 13074c09eed9SJim Baxter (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { 13084c09eed9SJim Baxter if (!(ebdp->cbd_esc & FLAG_RX_CSUM_ERROR)) { 13094c09eed9SJim Baxter /* don't check it */ 13104c09eed9SJim Baxter skb->ip_summed = CHECKSUM_UNNECESSARY; 13114c09eed9SJim Baxter } else { 13124c09eed9SJim Baxter skb_checksum_none_assert(skb); 13134c09eed9SJim Baxter } 13144c09eed9SJim Baxter } 13154c09eed9SJim Baxter 1316cdffcf1bSJim Baxter /* Handle received VLAN packets */ 1317cdffcf1bSJim Baxter if (vlan_packet_rcvd) 1318cdffcf1bSJim Baxter __vlan_hwaccel_put_tag(skb, 1319cdffcf1bSJim Baxter htons(ETH_P_8021Q), 1320cdffcf1bSJim Baxter vlan_tag); 1321cdffcf1bSJim Baxter 1322793fc096SFrank Li napi_gro_receive(&fep->napi, skb); 1323793fc096SFrank Li } 1324793fc096SFrank Li 1325d842a31fSDuan Fugang-B38611 dma_sync_single_for_device(&fep->pdev->dev, bdp->cbd_bufaddr, 1326d842a31fSDuan Fugang-B38611 FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE); 1327793fc096SFrank Li rx_processing_done: 1328793fc096SFrank Li /* Clear the status flags for this buffer */ 1329793fc096SFrank Li status &= ~BD_ENET_RX_STATS; 1330793fc096SFrank Li 1331793fc096SFrank Li /* Mark the buffer empty */ 1332793fc096SFrank Li status |= BD_ENET_RX_EMPTY; 1333793fc096SFrank Li bdp->cbd_sc = status; 1334793fc096SFrank Li 1335793fc096SFrank Li if (fep->bufdesc_ex) { 1336793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 1337793fc096SFrank Li 1338793fc096SFrank Li ebdp->cbd_esc = BD_ENET_RX_INT; 1339793fc096SFrank Li ebdp->cbd_prot = 0; 1340793fc096SFrank Li ebdp->cbd_bdu = 0; 1341793fc096SFrank Li } 1342793fc096SFrank Li 1343793fc096SFrank Li /* Update BD pointer to next entry */ 134436e24e2eSDuan Fugang-B38611 bdp = fec_enet_get_nextdesc(bdp, fep); 134536e24e2eSDuan Fugang-B38611 1346793fc096SFrank Li /* Doing this here will keep the FEC running while we process 1347793fc096SFrank Li * incoming frames. On a heavily loaded network, we should be 1348793fc096SFrank Li * able to keep up at the expense of system resources. 1349793fc096SFrank Li */ 1350793fc096SFrank Li writel(0, fep->hwp + FEC_R_DES_ACTIVE); 1351793fc096SFrank Li } 1352793fc096SFrank Li fep->cur_rx = bdp; 1353793fc096SFrank Li 1354793fc096SFrank Li return pkt_received; 1355793fc096SFrank Li } 1356793fc096SFrank Li 1357793fc096SFrank Li static irqreturn_t 1358793fc096SFrank Li fec_enet_interrupt(int irq, void *dev_id) 1359793fc096SFrank Li { 1360793fc096SFrank Li struct net_device *ndev = dev_id; 1361793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 13627a16807cSRussell King const unsigned napi_mask = FEC_ENET_RXF | FEC_ENET_TXF; 1363793fc096SFrank Li uint int_events; 1364793fc096SFrank Li irqreturn_t ret = IRQ_NONE; 1365793fc096SFrank Li 1366793fc096SFrank Li int_events = readl(fep->hwp + FEC_IEVENT); 13677a16807cSRussell King writel(int_events & ~napi_mask, fep->hwp + FEC_IEVENT); 1368793fc096SFrank Li 13697a16807cSRussell King if (int_events & napi_mask) { 1370793fc096SFrank Li ret = IRQ_HANDLED; 1371793fc096SFrank Li 13727a16807cSRussell King /* Disable the NAPI interrupts */ 13737a16807cSRussell King writel(FEC_ENET_MII, fep->hwp + FEC_IMASK); 13747a16807cSRussell King napi_schedule(&fep->napi); 1375793fc096SFrank Li } 1376793fc096SFrank Li 1377793fc096SFrank Li if (int_events & FEC_ENET_MII) { 1378793fc096SFrank Li ret = IRQ_HANDLED; 1379793fc096SFrank Li complete(&fep->mdio_done); 1380793fc096SFrank Li } 1381793fc096SFrank Li 1382793fc096SFrank Li return ret; 1383793fc096SFrank Li } 1384793fc096SFrank Li 1385793fc096SFrank Li static int fec_enet_rx_napi(struct napi_struct *napi, int budget) 1386793fc096SFrank Li { 1387793fc096SFrank Li struct net_device *ndev = napi->dev; 1388793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 13897a16807cSRussell King int pkts; 13907a16807cSRussell King 13917a16807cSRussell King /* 13927a16807cSRussell King * Clear any pending transmit or receive interrupts before 13937a16807cSRussell King * processing the rings to avoid racing with the hardware. 13947a16807cSRussell King */ 13957a16807cSRussell King writel(FEC_ENET_RXF | FEC_ENET_TXF, fep->hwp + FEC_IEVENT); 13967a16807cSRussell King 13977a16807cSRussell King pkts = fec_enet_rx(ndev, budget); 1398793fc096SFrank Li 1399793fc096SFrank Li fec_enet_tx(ndev); 1400793fc096SFrank Li 1401793fc096SFrank Li if (pkts < budget) { 1402793fc096SFrank Li napi_complete(napi); 1403793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 1404793fc096SFrank Li } 1405793fc096SFrank Li return pkts; 1406793fc096SFrank Li } 1407793fc096SFrank Li 1408793fc096SFrank Li /* ------------------------------------------------------------------------- */ 1409793fc096SFrank Li static void fec_get_mac(struct net_device *ndev) 1410793fc096SFrank Li { 1411793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 141294660ba0SJingoo Han struct fec_platform_data *pdata = dev_get_platdata(&fep->pdev->dev); 1413793fc096SFrank Li unsigned char *iap, tmpaddr[ETH_ALEN]; 1414793fc096SFrank Li 1415793fc096SFrank Li /* 1416793fc096SFrank Li * try to get mac address in following order: 1417793fc096SFrank Li * 1418793fc096SFrank Li * 1) module parameter via kernel command line in form 1419793fc096SFrank Li * fec.macaddr=0x00,0x04,0x9f,0x01,0x30,0xe0 1420793fc096SFrank Li */ 1421793fc096SFrank Li iap = macaddr; 1422793fc096SFrank Li 1423793fc096SFrank Li /* 1424793fc096SFrank Li * 2) from device tree data 1425793fc096SFrank Li */ 1426793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 1427793fc096SFrank Li struct device_node *np = fep->pdev->dev.of_node; 1428793fc096SFrank Li if (np) { 1429793fc096SFrank Li const char *mac = of_get_mac_address(np); 1430793fc096SFrank Li if (mac) 1431793fc096SFrank Li iap = (unsigned char *) mac; 1432793fc096SFrank Li } 1433793fc096SFrank Li } 1434793fc096SFrank Li 1435793fc096SFrank Li /* 1436793fc096SFrank Li * 3) from flash or fuse (via platform data) 1437793fc096SFrank Li */ 1438793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 1439793fc096SFrank Li #ifdef CONFIG_M5272 1440793fc096SFrank Li if (FEC_FLASHMAC) 1441793fc096SFrank Li iap = (unsigned char *)FEC_FLASHMAC; 1442793fc096SFrank Li #else 1443793fc096SFrank Li if (pdata) 1444793fc096SFrank Li iap = (unsigned char *)&pdata->mac; 1445793fc096SFrank Li #endif 1446793fc096SFrank Li } 1447793fc096SFrank Li 1448793fc096SFrank Li /* 1449793fc096SFrank Li * 4) FEC mac registers set by bootloader 1450793fc096SFrank Li */ 1451793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 14527d7628f3SDan Carpenter *((__be32 *) &tmpaddr[0]) = 14537d7628f3SDan Carpenter cpu_to_be32(readl(fep->hwp + FEC_ADDR_LOW)); 14547d7628f3SDan Carpenter *((__be16 *) &tmpaddr[4]) = 14557d7628f3SDan Carpenter cpu_to_be16(readl(fep->hwp + FEC_ADDR_HIGH) >> 16); 1456793fc096SFrank Li iap = &tmpaddr[0]; 1457793fc096SFrank Li } 1458793fc096SFrank Li 1459ff5b2fabSLucas Stach /* 1460ff5b2fabSLucas Stach * 5) random mac address 1461ff5b2fabSLucas Stach */ 1462ff5b2fabSLucas Stach if (!is_valid_ether_addr(iap)) { 1463ff5b2fabSLucas Stach /* Report it and use a random ethernet address instead */ 1464ff5b2fabSLucas Stach netdev_err(ndev, "Invalid MAC address: %pM\n", iap); 1465ff5b2fabSLucas Stach eth_hw_addr_random(ndev); 1466ff5b2fabSLucas Stach netdev_info(ndev, "Using random MAC address: %pM\n", 1467ff5b2fabSLucas Stach ndev->dev_addr); 1468ff5b2fabSLucas Stach return; 1469ff5b2fabSLucas Stach } 1470ff5b2fabSLucas Stach 1471793fc096SFrank Li memcpy(ndev->dev_addr, iap, ETH_ALEN); 1472793fc096SFrank Li 1473793fc096SFrank Li /* Adjust MAC if using macaddr */ 1474793fc096SFrank Li if (iap == macaddr) 1475793fc096SFrank Li ndev->dev_addr[ETH_ALEN-1] = macaddr[ETH_ALEN-1] + fep->dev_id; 1476793fc096SFrank Li } 1477793fc096SFrank Li 1478793fc096SFrank Li /* ------------------------------------------------------------------------- */ 1479793fc096SFrank Li 1480793fc096SFrank Li /* 1481793fc096SFrank Li * Phy section 1482793fc096SFrank Li */ 1483793fc096SFrank Li static void fec_enet_adjust_link(struct net_device *ndev) 1484793fc096SFrank Li { 1485793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1486793fc096SFrank Li struct phy_device *phy_dev = fep->phy_dev; 1487793fc096SFrank Li int status_change = 0; 1488793fc096SFrank Li 1489793fc096SFrank Li /* Prevent a state halted on mii error */ 1490793fc096SFrank Li if (fep->mii_timeout && phy_dev->state == PHY_HALTED) { 1491793fc096SFrank Li phy_dev->state = PHY_RESUMING; 149254309fa6SFrank Li return; 1493793fc096SFrank Li } 1494793fc096SFrank Li 14958ce5624fSRussell King /* 14968ce5624fSRussell King * If the netdev is down, or is going down, we're not interested 14978ce5624fSRussell King * in link state events, so just mark our idea of the link as down 14988ce5624fSRussell King * and ignore the event. 14998ce5624fSRussell King */ 15008ce5624fSRussell King if (!netif_running(ndev) || !netif_device_present(ndev)) { 15018ce5624fSRussell King fep->link = 0; 15028ce5624fSRussell King } else if (phy_dev->link) { 1503793fc096SFrank Li if (!fep->link) { 1504793fc096SFrank Li fep->link = phy_dev->link; 1505793fc096SFrank Li status_change = 1; 1506793fc096SFrank Li } 1507793fc096SFrank Li 1508ef83337dSRussell King if (fep->full_duplex != phy_dev->duplex) { 1509ef83337dSRussell King fep->full_duplex = phy_dev->duplex; 1510793fc096SFrank Li status_change = 1; 1511ef83337dSRussell King } 1512793fc096SFrank Li 1513793fc096SFrank Li if (phy_dev->speed != fep->speed) { 1514793fc096SFrank Li fep->speed = phy_dev->speed; 1515793fc096SFrank Li status_change = 1; 1516793fc096SFrank Li } 1517793fc096SFrank Li 1518793fc096SFrank Li /* if any of the above changed restart the FEC */ 1519dbc64a8eSRussell King if (status_change) { 1520dbc64a8eSRussell King napi_disable(&fep->napi); 1521dbc64a8eSRussell King netif_tx_lock_bh(ndev); 1522ef83337dSRussell King fec_restart(ndev); 1523dbc64a8eSRussell King netif_wake_queue(ndev); 15246af42d42SRussell King netif_tx_unlock_bh(ndev); 1525dbc64a8eSRussell King napi_enable(&fep->napi); 1526dbc64a8eSRussell King } 1527793fc096SFrank Li } else { 1528793fc096SFrank Li if (fep->link) { 1529f208ce10SRussell King napi_disable(&fep->napi); 1530f208ce10SRussell King netif_tx_lock_bh(ndev); 1531793fc096SFrank Li fec_stop(ndev); 1532f208ce10SRussell King netif_tx_unlock_bh(ndev); 1533f208ce10SRussell King napi_enable(&fep->napi); 15346e0895c2SDavid S. Miller fep->link = phy_dev->link; 1535793fc096SFrank Li status_change = 1; 1536793fc096SFrank Li } 1537793fc096SFrank Li } 1538793fc096SFrank Li 1539793fc096SFrank Li if (status_change) 1540793fc096SFrank Li phy_print_status(phy_dev); 1541793fc096SFrank Li } 1542793fc096SFrank Li 1543793fc096SFrank Li static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum) 1544793fc096SFrank Li { 1545793fc096SFrank Li struct fec_enet_private *fep = bus->priv; 1546793fc096SFrank Li unsigned long time_left; 1547793fc096SFrank Li 1548793fc096SFrank Li fep->mii_timeout = 0; 1549793fc096SFrank Li init_completion(&fep->mdio_done); 1550793fc096SFrank Li 1551793fc096SFrank Li /* start a read op */ 1552793fc096SFrank Li writel(FEC_MMFR_ST | FEC_MMFR_OP_READ | 1553793fc096SFrank Li FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) | 1554793fc096SFrank Li FEC_MMFR_TA, fep->hwp + FEC_MII_DATA); 1555793fc096SFrank Li 1556793fc096SFrank Li /* wait for end of transfer */ 1557793fc096SFrank Li time_left = wait_for_completion_timeout(&fep->mdio_done, 1558793fc096SFrank Li usecs_to_jiffies(FEC_MII_TIMEOUT)); 1559793fc096SFrank Li if (time_left == 0) { 1560793fc096SFrank Li fep->mii_timeout = 1; 156131b7720cSJoe Perches netdev_err(fep->netdev, "MDIO read timeout\n"); 1562793fc096SFrank Li return -ETIMEDOUT; 1563793fc096SFrank Li } 1564793fc096SFrank Li 1565793fc096SFrank Li /* return value */ 1566793fc096SFrank Li return FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA)); 1567793fc096SFrank Li } 1568793fc096SFrank Li 1569793fc096SFrank Li static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum, 1570793fc096SFrank Li u16 value) 1571793fc096SFrank Li { 1572793fc096SFrank Li struct fec_enet_private *fep = bus->priv; 1573793fc096SFrank Li unsigned long time_left; 1574793fc096SFrank Li 1575793fc096SFrank Li fep->mii_timeout = 0; 1576793fc096SFrank Li init_completion(&fep->mdio_done); 1577793fc096SFrank Li 1578793fc096SFrank Li /* start a write op */ 1579793fc096SFrank Li writel(FEC_MMFR_ST | FEC_MMFR_OP_WRITE | 1580793fc096SFrank Li FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) | 1581793fc096SFrank Li FEC_MMFR_TA | FEC_MMFR_DATA(value), 1582793fc096SFrank Li fep->hwp + FEC_MII_DATA); 1583793fc096SFrank Li 1584793fc096SFrank Li /* wait for end of transfer */ 1585793fc096SFrank Li time_left = wait_for_completion_timeout(&fep->mdio_done, 1586793fc096SFrank Li usecs_to_jiffies(FEC_MII_TIMEOUT)); 1587793fc096SFrank Li if (time_left == 0) { 1588793fc096SFrank Li fep->mii_timeout = 1; 158931b7720cSJoe Perches netdev_err(fep->netdev, "MDIO write timeout\n"); 1590793fc096SFrank Li return -ETIMEDOUT; 1591793fc096SFrank Li } 1592793fc096SFrank Li 1593793fc096SFrank Li return 0; 1594793fc096SFrank Li } 1595793fc096SFrank Li 1596e8fcfcd5SNimrod Andy static int fec_enet_clk_enable(struct net_device *ndev, bool enable) 1597e8fcfcd5SNimrod Andy { 1598e8fcfcd5SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 1599e8fcfcd5SNimrod Andy int ret; 1600e8fcfcd5SNimrod Andy 1601e8fcfcd5SNimrod Andy if (enable) { 1602e8fcfcd5SNimrod Andy ret = clk_prepare_enable(fep->clk_ahb); 1603e8fcfcd5SNimrod Andy if (ret) 1604e8fcfcd5SNimrod Andy return ret; 1605e8fcfcd5SNimrod Andy ret = clk_prepare_enable(fep->clk_ipg); 1606e8fcfcd5SNimrod Andy if (ret) 1607e8fcfcd5SNimrod Andy goto failed_clk_ipg; 1608e8fcfcd5SNimrod Andy if (fep->clk_enet_out) { 1609e8fcfcd5SNimrod Andy ret = clk_prepare_enable(fep->clk_enet_out); 1610e8fcfcd5SNimrod Andy if (ret) 1611e8fcfcd5SNimrod Andy goto failed_clk_enet_out; 1612e8fcfcd5SNimrod Andy } 1613e8fcfcd5SNimrod Andy if (fep->clk_ptp) { 1614*91c0d987SNimrod Andy mutex_lock(&fep->ptp_clk_mutex); 1615e8fcfcd5SNimrod Andy ret = clk_prepare_enable(fep->clk_ptp); 1616*91c0d987SNimrod Andy if (ret) { 1617*91c0d987SNimrod Andy mutex_unlock(&fep->ptp_clk_mutex); 1618e8fcfcd5SNimrod Andy goto failed_clk_ptp; 1619*91c0d987SNimrod Andy } else { 1620*91c0d987SNimrod Andy fep->ptp_clk_on = true; 1621*91c0d987SNimrod Andy } 1622*91c0d987SNimrod Andy mutex_unlock(&fep->ptp_clk_mutex); 1623e8fcfcd5SNimrod Andy } 1624e8fcfcd5SNimrod Andy } else { 1625e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_ahb); 1626e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_ipg); 1627e8fcfcd5SNimrod Andy if (fep->clk_enet_out) 1628e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_enet_out); 1629*91c0d987SNimrod Andy if (fep->clk_ptp) { 1630*91c0d987SNimrod Andy mutex_lock(&fep->ptp_clk_mutex); 1631e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_ptp); 1632*91c0d987SNimrod Andy fep->ptp_clk_on = false; 1633*91c0d987SNimrod Andy mutex_unlock(&fep->ptp_clk_mutex); 1634*91c0d987SNimrod Andy } 1635e8fcfcd5SNimrod Andy } 1636e8fcfcd5SNimrod Andy 1637e8fcfcd5SNimrod Andy return 0; 1638e8fcfcd5SNimrod Andy failed_clk_ptp: 1639e8fcfcd5SNimrod Andy if (fep->clk_enet_out) 1640e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_enet_out); 1641e8fcfcd5SNimrod Andy failed_clk_enet_out: 1642e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_ipg); 1643e8fcfcd5SNimrod Andy failed_clk_ipg: 1644e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_ahb); 1645e8fcfcd5SNimrod Andy 1646e8fcfcd5SNimrod Andy return ret; 1647e8fcfcd5SNimrod Andy } 1648e8fcfcd5SNimrod Andy 1649793fc096SFrank Li static int fec_enet_mii_probe(struct net_device *ndev) 1650793fc096SFrank Li { 1651793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1652793fc096SFrank Li const struct platform_device_id *id_entry = 1653793fc096SFrank Li platform_get_device_id(fep->pdev); 1654793fc096SFrank Li struct phy_device *phy_dev = NULL; 1655793fc096SFrank Li char mdio_bus_id[MII_BUS_ID_SIZE]; 1656793fc096SFrank Li char phy_name[MII_BUS_ID_SIZE + 3]; 1657793fc096SFrank Li int phy_id; 1658793fc096SFrank Li int dev_id = fep->dev_id; 1659793fc096SFrank Li 1660793fc096SFrank Li fep->phy_dev = NULL; 1661793fc096SFrank Li 1662407066f8SUwe Kleine-König if (fep->phy_node) { 1663407066f8SUwe Kleine-König phy_dev = of_phy_connect(ndev, fep->phy_node, 1664407066f8SUwe Kleine-König &fec_enet_adjust_link, 0, 1665407066f8SUwe Kleine-König fep->phy_interface); 1666407066f8SUwe Kleine-König } else { 1667793fc096SFrank Li /* check for attached phy */ 1668793fc096SFrank Li for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) { 1669793fc096SFrank Li if ((fep->mii_bus->phy_mask & (1 << phy_id))) 1670793fc096SFrank Li continue; 1671793fc096SFrank Li if (fep->mii_bus->phy_map[phy_id] == NULL) 1672793fc096SFrank Li continue; 1673793fc096SFrank Li if (fep->mii_bus->phy_map[phy_id]->phy_id == 0) 1674793fc096SFrank Li continue; 1675793fc096SFrank Li if (dev_id--) 1676793fc096SFrank Li continue; 1677793fc096SFrank Li strncpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE); 1678793fc096SFrank Li break; 1679793fc096SFrank Li } 1680793fc096SFrank Li 1681793fc096SFrank Li if (phy_id >= PHY_MAX_ADDR) { 168231b7720cSJoe Perches netdev_info(ndev, "no PHY, assuming direct connection to switch\n"); 1683793fc096SFrank Li strncpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE); 1684793fc096SFrank Li phy_id = 0; 1685793fc096SFrank Li } 1686793fc096SFrank Li 1687407066f8SUwe Kleine-König snprintf(phy_name, sizeof(phy_name), 1688407066f8SUwe Kleine-König PHY_ID_FMT, mdio_bus_id, phy_id); 1689793fc096SFrank Li phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, 1690793fc096SFrank Li fep->phy_interface); 1691407066f8SUwe Kleine-König } 1692407066f8SUwe Kleine-König 1693793fc096SFrank Li if (IS_ERR(phy_dev)) { 169431b7720cSJoe Perches netdev_err(ndev, "could not attach to PHY\n"); 1695793fc096SFrank Li return PTR_ERR(phy_dev); 1696793fc096SFrank Li } 1697793fc096SFrank Li 1698793fc096SFrank Li /* mask with MAC supported features */ 1699793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_HAS_GBIT) { 1700793fc096SFrank Li phy_dev->supported &= PHY_GBIT_FEATURES; 1701b44592ffSRussell King phy_dev->supported &= ~SUPPORTED_1000baseT_Half; 1702d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 1703793fc096SFrank Li phy_dev->supported |= SUPPORTED_Pause; 1704d1391930SGuenter Roeck #endif 1705793fc096SFrank Li } 1706793fc096SFrank Li else 1707793fc096SFrank Li phy_dev->supported &= PHY_BASIC_FEATURES; 1708793fc096SFrank Li 1709793fc096SFrank Li phy_dev->advertising = phy_dev->supported; 1710793fc096SFrank Li 1711793fc096SFrank Li fep->phy_dev = phy_dev; 1712793fc096SFrank Li fep->link = 0; 1713793fc096SFrank Li fep->full_duplex = 0; 1714793fc096SFrank Li 171531b7720cSJoe Perches netdev_info(ndev, "Freescale FEC PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n", 1716793fc096SFrank Li fep->phy_dev->drv->name, dev_name(&fep->phy_dev->dev), 1717793fc096SFrank Li fep->phy_dev->irq); 1718793fc096SFrank Li 1719793fc096SFrank Li return 0; 1720793fc096SFrank Li } 1721793fc096SFrank Li 1722793fc096SFrank Li static int fec_enet_mii_init(struct platform_device *pdev) 1723793fc096SFrank Li { 1724793fc096SFrank Li static struct mii_bus *fec0_mii_bus; 1725793fc096SFrank Li struct net_device *ndev = platform_get_drvdata(pdev); 1726793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1727793fc096SFrank Li const struct platform_device_id *id_entry = 1728793fc096SFrank Li platform_get_device_id(fep->pdev); 1729407066f8SUwe Kleine-König struct device_node *node; 1730793fc096SFrank Li int err = -ENXIO, i; 1731793fc096SFrank Li 1732793fc096SFrank Li /* 1733793fc096SFrank Li * The dual fec interfaces are not equivalent with enet-mac. 1734793fc096SFrank Li * Here are the differences: 1735793fc096SFrank Li * 1736793fc096SFrank Li * - fec0 supports MII & RMII modes while fec1 only supports RMII 1737793fc096SFrank Li * - fec0 acts as the 1588 time master while fec1 is slave 1738793fc096SFrank Li * - external phys can only be configured by fec0 1739793fc096SFrank Li * 1740793fc096SFrank Li * That is to say fec1 can not work independently. It only works 1741793fc096SFrank Li * when fec0 is working. The reason behind this design is that the 1742793fc096SFrank Li * second interface is added primarily for Switch mode. 1743793fc096SFrank Li * 1744793fc096SFrank Li * Because of the last point above, both phys are attached on fec0 1745793fc096SFrank Li * mdio interface in board design, and need to be configured by 1746793fc096SFrank Li * fec0 mii_bus. 1747793fc096SFrank Li */ 1748793fc096SFrank Li if ((id_entry->driver_data & FEC_QUIRK_ENET_MAC) && fep->dev_id > 0) { 1749793fc096SFrank Li /* fec1 uses fec0 mii_bus */ 1750793fc096SFrank Li if (mii_cnt && fec0_mii_bus) { 1751793fc096SFrank Li fep->mii_bus = fec0_mii_bus; 1752793fc096SFrank Li mii_cnt++; 1753793fc096SFrank Li return 0; 1754793fc096SFrank Li } 1755793fc096SFrank Li return -ENOENT; 1756793fc096SFrank Li } 1757793fc096SFrank Li 1758793fc096SFrank Li fep->mii_timeout = 0; 1759793fc096SFrank Li 1760793fc096SFrank Li /* 1761793fc096SFrank Li * Set MII speed to 2.5 MHz (= clk_get_rate() / 2 * phy_speed) 1762793fc096SFrank Li * 1763793fc096SFrank Li * The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while 1764793fc096SFrank Li * for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'. The i.MX28 1765793fc096SFrank Li * Reference Manual has an error on this, and gets fixed on i.MX6Q 1766793fc096SFrank Li * document. 1767793fc096SFrank Li */ 176898a6eeb8SNimrod Andy fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 5000000); 1769793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) 1770793fc096SFrank Li fep->phy_speed--; 1771793fc096SFrank Li fep->phy_speed <<= 1; 1772793fc096SFrank Li writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 1773793fc096SFrank Li 1774793fc096SFrank Li fep->mii_bus = mdiobus_alloc(); 1775793fc096SFrank Li if (fep->mii_bus == NULL) { 1776793fc096SFrank Li err = -ENOMEM; 1777793fc096SFrank Li goto err_out; 1778793fc096SFrank Li } 1779793fc096SFrank Li 1780793fc096SFrank Li fep->mii_bus->name = "fec_enet_mii_bus"; 1781793fc096SFrank Li fep->mii_bus->read = fec_enet_mdio_read; 1782793fc096SFrank Li fep->mii_bus->write = fec_enet_mdio_write; 1783793fc096SFrank Li snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", 1784793fc096SFrank Li pdev->name, fep->dev_id + 1); 1785793fc096SFrank Li fep->mii_bus->priv = fep; 1786793fc096SFrank Li fep->mii_bus->parent = &pdev->dev; 1787793fc096SFrank Li 1788793fc096SFrank Li fep->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); 1789793fc096SFrank Li if (!fep->mii_bus->irq) { 1790793fc096SFrank Li err = -ENOMEM; 1791793fc096SFrank Li goto err_out_free_mdiobus; 1792793fc096SFrank Li } 1793793fc096SFrank Li 1794793fc096SFrank Li for (i = 0; i < PHY_MAX_ADDR; i++) 1795793fc096SFrank Li fep->mii_bus->irq[i] = PHY_POLL; 1796793fc096SFrank Li 1797407066f8SUwe Kleine-König node = of_get_child_by_name(pdev->dev.of_node, "mdio"); 1798407066f8SUwe Kleine-König if (node) { 1799407066f8SUwe Kleine-König err = of_mdiobus_register(fep->mii_bus, node); 1800407066f8SUwe Kleine-König of_node_put(node); 1801407066f8SUwe Kleine-König } else { 1802407066f8SUwe Kleine-König err = mdiobus_register(fep->mii_bus); 1803407066f8SUwe Kleine-König } 1804407066f8SUwe Kleine-König 1805407066f8SUwe Kleine-König if (err) 1806793fc096SFrank Li goto err_out_free_mdio_irq; 1807793fc096SFrank Li 1808793fc096SFrank Li mii_cnt++; 1809793fc096SFrank Li 1810793fc096SFrank Li /* save fec0 mii_bus */ 1811793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) 1812793fc096SFrank Li fec0_mii_bus = fep->mii_bus; 1813793fc096SFrank Li 1814793fc096SFrank Li return 0; 1815793fc096SFrank Li 1816793fc096SFrank Li err_out_free_mdio_irq: 1817793fc096SFrank Li kfree(fep->mii_bus->irq); 1818793fc096SFrank Li err_out_free_mdiobus: 1819793fc096SFrank Li mdiobus_free(fep->mii_bus); 1820793fc096SFrank Li err_out: 1821793fc096SFrank Li return err; 1822793fc096SFrank Li } 1823793fc096SFrank Li 1824793fc096SFrank Li static void fec_enet_mii_remove(struct fec_enet_private *fep) 1825793fc096SFrank Li { 1826793fc096SFrank Li if (--mii_cnt == 0) { 1827793fc096SFrank Li mdiobus_unregister(fep->mii_bus); 1828793fc096SFrank Li kfree(fep->mii_bus->irq); 1829793fc096SFrank Li mdiobus_free(fep->mii_bus); 1830793fc096SFrank Li } 1831793fc096SFrank Li } 1832793fc096SFrank Li 1833793fc096SFrank Li static int fec_enet_get_settings(struct net_device *ndev, 1834793fc096SFrank Li struct ethtool_cmd *cmd) 1835793fc096SFrank Li { 1836793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1837793fc096SFrank Li struct phy_device *phydev = fep->phy_dev; 1838793fc096SFrank Li 1839793fc096SFrank Li if (!phydev) 1840793fc096SFrank Li return -ENODEV; 1841793fc096SFrank Li 1842793fc096SFrank Li return phy_ethtool_gset(phydev, cmd); 1843793fc096SFrank Li } 1844793fc096SFrank Li 1845793fc096SFrank Li static int fec_enet_set_settings(struct net_device *ndev, 1846793fc096SFrank Li struct ethtool_cmd *cmd) 1847793fc096SFrank Li { 1848793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1849793fc096SFrank Li struct phy_device *phydev = fep->phy_dev; 1850793fc096SFrank Li 1851793fc096SFrank Li if (!phydev) 1852793fc096SFrank Li return -ENODEV; 1853793fc096SFrank Li 1854793fc096SFrank Li return phy_ethtool_sset(phydev, cmd); 1855793fc096SFrank Li } 1856793fc096SFrank Li 1857793fc096SFrank Li static void fec_enet_get_drvinfo(struct net_device *ndev, 1858793fc096SFrank Li struct ethtool_drvinfo *info) 1859793fc096SFrank Li { 1860793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1861793fc096SFrank Li 1862793fc096SFrank Li strlcpy(info->driver, fep->pdev->dev.driver->name, 1863793fc096SFrank Li sizeof(info->driver)); 1864793fc096SFrank Li strlcpy(info->version, "Revision: 1.0", sizeof(info->version)); 1865793fc096SFrank Li strlcpy(info->bus_info, dev_name(&ndev->dev), sizeof(info->bus_info)); 1866793fc096SFrank Li } 1867793fc096SFrank Li 1868793fc096SFrank Li static int fec_enet_get_ts_info(struct net_device *ndev, 1869793fc096SFrank Li struct ethtool_ts_info *info) 1870793fc096SFrank Li { 1871793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1872793fc096SFrank Li 1873793fc096SFrank Li if (fep->bufdesc_ex) { 1874793fc096SFrank Li 1875793fc096SFrank Li info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | 1876793fc096SFrank Li SOF_TIMESTAMPING_RX_SOFTWARE | 1877793fc096SFrank Li SOF_TIMESTAMPING_SOFTWARE | 1878793fc096SFrank Li SOF_TIMESTAMPING_TX_HARDWARE | 1879793fc096SFrank Li SOF_TIMESTAMPING_RX_HARDWARE | 1880793fc096SFrank Li SOF_TIMESTAMPING_RAW_HARDWARE; 1881793fc096SFrank Li if (fep->ptp_clock) 1882793fc096SFrank Li info->phc_index = ptp_clock_index(fep->ptp_clock); 1883793fc096SFrank Li else 1884793fc096SFrank Li info->phc_index = -1; 1885793fc096SFrank Li 1886793fc096SFrank Li info->tx_types = (1 << HWTSTAMP_TX_OFF) | 1887793fc096SFrank Li (1 << HWTSTAMP_TX_ON); 1888793fc096SFrank Li 1889793fc096SFrank Li info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | 1890793fc096SFrank Li (1 << HWTSTAMP_FILTER_ALL); 1891793fc096SFrank Li return 0; 1892793fc096SFrank Li } else { 1893793fc096SFrank Li return ethtool_op_get_ts_info(ndev, info); 1894793fc096SFrank Li } 1895793fc096SFrank Li } 1896793fc096SFrank Li 1897d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 1898d1391930SGuenter Roeck 1899793fc096SFrank Li static void fec_enet_get_pauseparam(struct net_device *ndev, 1900793fc096SFrank Li struct ethtool_pauseparam *pause) 1901793fc096SFrank Li { 1902793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1903793fc096SFrank Li 1904793fc096SFrank Li pause->autoneg = (fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) != 0; 1905793fc096SFrank Li pause->tx_pause = (fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) != 0; 1906793fc096SFrank Li pause->rx_pause = pause->tx_pause; 1907793fc096SFrank Li } 1908793fc096SFrank Li 1909793fc096SFrank Li static int fec_enet_set_pauseparam(struct net_device *ndev, 1910793fc096SFrank Li struct ethtool_pauseparam *pause) 1911793fc096SFrank Li { 1912793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1913793fc096SFrank Li 19140b146ca8SRussell King if (!fep->phy_dev) 19150b146ca8SRussell King return -ENODEV; 19160b146ca8SRussell King 1917793fc096SFrank Li if (pause->tx_pause != pause->rx_pause) { 1918793fc096SFrank Li netdev_info(ndev, 1919793fc096SFrank Li "hardware only support enable/disable both tx and rx"); 1920793fc096SFrank Li return -EINVAL; 1921793fc096SFrank Li } 1922793fc096SFrank Li 1923793fc096SFrank Li fep->pause_flag = 0; 1924793fc096SFrank Li 1925793fc096SFrank Li /* tx pause must be same as rx pause */ 1926793fc096SFrank Li fep->pause_flag |= pause->rx_pause ? FEC_PAUSE_FLAG_ENABLE : 0; 1927793fc096SFrank Li fep->pause_flag |= pause->autoneg ? FEC_PAUSE_FLAG_AUTONEG : 0; 1928793fc096SFrank Li 1929793fc096SFrank Li if (pause->rx_pause || pause->autoneg) { 1930793fc096SFrank Li fep->phy_dev->supported |= ADVERTISED_Pause; 1931793fc096SFrank Li fep->phy_dev->advertising |= ADVERTISED_Pause; 1932793fc096SFrank Li } else { 1933793fc096SFrank Li fep->phy_dev->supported &= ~ADVERTISED_Pause; 1934793fc096SFrank Li fep->phy_dev->advertising &= ~ADVERTISED_Pause; 1935793fc096SFrank Li } 1936793fc096SFrank Li 1937793fc096SFrank Li if (pause->autoneg) { 1938793fc096SFrank Li if (netif_running(ndev)) 1939793fc096SFrank Li fec_stop(ndev); 1940793fc096SFrank Li phy_start_aneg(fep->phy_dev); 1941793fc096SFrank Li } 1942dbc64a8eSRussell King if (netif_running(ndev)) { 1943dbc64a8eSRussell King napi_disable(&fep->napi); 1944dbc64a8eSRussell King netif_tx_lock_bh(ndev); 1945ef83337dSRussell King fec_restart(ndev); 1946dbc64a8eSRussell King netif_wake_queue(ndev); 19476af42d42SRussell King netif_tx_unlock_bh(ndev); 1948dbc64a8eSRussell King napi_enable(&fep->napi); 1949dbc64a8eSRussell King } 1950793fc096SFrank Li 1951793fc096SFrank Li return 0; 1952793fc096SFrank Li } 1953793fc096SFrank Li 195438ae92dcSChris Healy static const struct fec_stat { 195538ae92dcSChris Healy char name[ETH_GSTRING_LEN]; 195638ae92dcSChris Healy u16 offset; 195738ae92dcSChris Healy } fec_stats[] = { 195838ae92dcSChris Healy /* RMON TX */ 195938ae92dcSChris Healy { "tx_dropped", RMON_T_DROP }, 196038ae92dcSChris Healy { "tx_packets", RMON_T_PACKETS }, 196138ae92dcSChris Healy { "tx_broadcast", RMON_T_BC_PKT }, 196238ae92dcSChris Healy { "tx_multicast", RMON_T_MC_PKT }, 196338ae92dcSChris Healy { "tx_crc_errors", RMON_T_CRC_ALIGN }, 196438ae92dcSChris Healy { "tx_undersize", RMON_T_UNDERSIZE }, 196538ae92dcSChris Healy { "tx_oversize", RMON_T_OVERSIZE }, 196638ae92dcSChris Healy { "tx_fragment", RMON_T_FRAG }, 196738ae92dcSChris Healy { "tx_jabber", RMON_T_JAB }, 196838ae92dcSChris Healy { "tx_collision", RMON_T_COL }, 196938ae92dcSChris Healy { "tx_64byte", RMON_T_P64 }, 197038ae92dcSChris Healy { "tx_65to127byte", RMON_T_P65TO127 }, 197138ae92dcSChris Healy { "tx_128to255byte", RMON_T_P128TO255 }, 197238ae92dcSChris Healy { "tx_256to511byte", RMON_T_P256TO511 }, 197338ae92dcSChris Healy { "tx_512to1023byte", RMON_T_P512TO1023 }, 197438ae92dcSChris Healy { "tx_1024to2047byte", RMON_T_P1024TO2047 }, 197538ae92dcSChris Healy { "tx_GTE2048byte", RMON_T_P_GTE2048 }, 197638ae92dcSChris Healy { "tx_octets", RMON_T_OCTETS }, 197738ae92dcSChris Healy 197838ae92dcSChris Healy /* IEEE TX */ 197938ae92dcSChris Healy { "IEEE_tx_drop", IEEE_T_DROP }, 198038ae92dcSChris Healy { "IEEE_tx_frame_ok", IEEE_T_FRAME_OK }, 198138ae92dcSChris Healy { "IEEE_tx_1col", IEEE_T_1COL }, 198238ae92dcSChris Healy { "IEEE_tx_mcol", IEEE_T_MCOL }, 198338ae92dcSChris Healy { "IEEE_tx_def", IEEE_T_DEF }, 198438ae92dcSChris Healy { "IEEE_tx_lcol", IEEE_T_LCOL }, 198538ae92dcSChris Healy { "IEEE_tx_excol", IEEE_T_EXCOL }, 198638ae92dcSChris Healy { "IEEE_tx_macerr", IEEE_T_MACERR }, 198738ae92dcSChris Healy { "IEEE_tx_cserr", IEEE_T_CSERR }, 198838ae92dcSChris Healy { "IEEE_tx_sqe", IEEE_T_SQE }, 198938ae92dcSChris Healy { "IEEE_tx_fdxfc", IEEE_T_FDXFC }, 199038ae92dcSChris Healy { "IEEE_tx_octets_ok", IEEE_T_OCTETS_OK }, 199138ae92dcSChris Healy 199238ae92dcSChris Healy /* RMON RX */ 199338ae92dcSChris Healy { "rx_packets", RMON_R_PACKETS }, 199438ae92dcSChris Healy { "rx_broadcast", RMON_R_BC_PKT }, 199538ae92dcSChris Healy { "rx_multicast", RMON_R_MC_PKT }, 199638ae92dcSChris Healy { "rx_crc_errors", RMON_R_CRC_ALIGN }, 199738ae92dcSChris Healy { "rx_undersize", RMON_R_UNDERSIZE }, 199838ae92dcSChris Healy { "rx_oversize", RMON_R_OVERSIZE }, 199938ae92dcSChris Healy { "rx_fragment", RMON_R_FRAG }, 200038ae92dcSChris Healy { "rx_jabber", RMON_R_JAB }, 200138ae92dcSChris Healy { "rx_64byte", RMON_R_P64 }, 200238ae92dcSChris Healy { "rx_65to127byte", RMON_R_P65TO127 }, 200338ae92dcSChris Healy { "rx_128to255byte", RMON_R_P128TO255 }, 200438ae92dcSChris Healy { "rx_256to511byte", RMON_R_P256TO511 }, 200538ae92dcSChris Healy { "rx_512to1023byte", RMON_R_P512TO1023 }, 200638ae92dcSChris Healy { "rx_1024to2047byte", RMON_R_P1024TO2047 }, 200738ae92dcSChris Healy { "rx_GTE2048byte", RMON_R_P_GTE2048 }, 200838ae92dcSChris Healy { "rx_octets", RMON_R_OCTETS }, 200938ae92dcSChris Healy 201038ae92dcSChris Healy /* IEEE RX */ 201138ae92dcSChris Healy { "IEEE_rx_drop", IEEE_R_DROP }, 201238ae92dcSChris Healy { "IEEE_rx_frame_ok", IEEE_R_FRAME_OK }, 201338ae92dcSChris Healy { "IEEE_rx_crc", IEEE_R_CRC }, 201438ae92dcSChris Healy { "IEEE_rx_align", IEEE_R_ALIGN }, 201538ae92dcSChris Healy { "IEEE_rx_macerr", IEEE_R_MACERR }, 201638ae92dcSChris Healy { "IEEE_rx_fdxfc", IEEE_R_FDXFC }, 201738ae92dcSChris Healy { "IEEE_rx_octets_ok", IEEE_R_OCTETS_OK }, 201838ae92dcSChris Healy }; 201938ae92dcSChris Healy 202038ae92dcSChris Healy static void fec_enet_get_ethtool_stats(struct net_device *dev, 202138ae92dcSChris Healy struct ethtool_stats *stats, u64 *data) 202238ae92dcSChris Healy { 202338ae92dcSChris Healy struct fec_enet_private *fep = netdev_priv(dev); 202438ae92dcSChris Healy int i; 202538ae92dcSChris Healy 202638ae92dcSChris Healy for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 202738ae92dcSChris Healy data[i] = readl(fep->hwp + fec_stats[i].offset); 202838ae92dcSChris Healy } 202938ae92dcSChris Healy 203038ae92dcSChris Healy static void fec_enet_get_strings(struct net_device *netdev, 203138ae92dcSChris Healy u32 stringset, u8 *data) 203238ae92dcSChris Healy { 203338ae92dcSChris Healy int i; 203438ae92dcSChris Healy switch (stringset) { 203538ae92dcSChris Healy case ETH_SS_STATS: 203638ae92dcSChris Healy for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 203738ae92dcSChris Healy memcpy(data + i * ETH_GSTRING_LEN, 203838ae92dcSChris Healy fec_stats[i].name, ETH_GSTRING_LEN); 203938ae92dcSChris Healy break; 204038ae92dcSChris Healy } 204138ae92dcSChris Healy } 204238ae92dcSChris Healy 204338ae92dcSChris Healy static int fec_enet_get_sset_count(struct net_device *dev, int sset) 204438ae92dcSChris Healy { 204538ae92dcSChris Healy switch (sset) { 204638ae92dcSChris Healy case ETH_SS_STATS: 204738ae92dcSChris Healy return ARRAY_SIZE(fec_stats); 204838ae92dcSChris Healy default: 204938ae92dcSChris Healy return -EOPNOTSUPP; 205038ae92dcSChris Healy } 205138ae92dcSChris Healy } 2052d1391930SGuenter Roeck #endif /* !defined(CONFIG_M5272) */ 205338ae92dcSChris Healy 205432bc9b46SChris Healy static int fec_enet_nway_reset(struct net_device *dev) 205532bc9b46SChris Healy { 205632bc9b46SChris Healy struct fec_enet_private *fep = netdev_priv(dev); 205732bc9b46SChris Healy struct phy_device *phydev = fep->phy_dev; 205832bc9b46SChris Healy 205932bc9b46SChris Healy if (!phydev) 206032bc9b46SChris Healy return -ENODEV; 206132bc9b46SChris Healy 206232bc9b46SChris Healy return genphy_restart_aneg(phydev); 206332bc9b46SChris Healy } 206432bc9b46SChris Healy 2065793fc096SFrank Li static const struct ethtool_ops fec_enet_ethtool_ops = { 2066793fc096SFrank Li .get_settings = fec_enet_get_settings, 2067793fc096SFrank Li .set_settings = fec_enet_set_settings, 2068793fc096SFrank Li .get_drvinfo = fec_enet_get_drvinfo, 206932bc9b46SChris Healy .nway_reset = fec_enet_nway_reset, 2070c1d7c48fSRussell King .get_link = ethtool_op_get_link, 207138ae92dcSChris Healy #ifndef CONFIG_M5272 2072c1d7c48fSRussell King .get_pauseparam = fec_enet_get_pauseparam, 2073c1d7c48fSRussell King .set_pauseparam = fec_enet_set_pauseparam, 207438ae92dcSChris Healy .get_strings = fec_enet_get_strings, 2075c1d7c48fSRussell King .get_ethtool_stats = fec_enet_get_ethtool_stats, 207638ae92dcSChris Healy .get_sset_count = fec_enet_get_sset_count, 207738ae92dcSChris Healy #endif 2078c1d7c48fSRussell King .get_ts_info = fec_enet_get_ts_info, 2079793fc096SFrank Li }; 2080793fc096SFrank Li 2081793fc096SFrank Li static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) 2082793fc096SFrank Li { 2083793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2084793fc096SFrank Li struct phy_device *phydev = fep->phy_dev; 2085793fc096SFrank Li 2086793fc096SFrank Li if (!netif_running(ndev)) 2087793fc096SFrank Li return -EINVAL; 2088793fc096SFrank Li 2089793fc096SFrank Li if (!phydev) 2090793fc096SFrank Li return -ENODEV; 2091793fc096SFrank Li 20921d5244d0SBen Hutchings if (fep->bufdesc_ex) { 20931d5244d0SBen Hutchings if (cmd == SIOCSHWTSTAMP) 20941d5244d0SBen Hutchings return fec_ptp_set(ndev, rq); 20951d5244d0SBen Hutchings if (cmd == SIOCGHWTSTAMP) 20961d5244d0SBen Hutchings return fec_ptp_get(ndev, rq); 20971d5244d0SBen Hutchings } 2098793fc096SFrank Li 2099793fc096SFrank Li return phy_mii_ioctl(phydev, rq, cmd); 2100793fc096SFrank Li } 2101793fc096SFrank Li 2102793fc096SFrank Li static void fec_enet_free_buffers(struct net_device *ndev) 2103793fc096SFrank Li { 2104793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2105793fc096SFrank Li unsigned int i; 2106793fc096SFrank Li struct sk_buff *skb; 2107793fc096SFrank Li struct bufdesc *bdp; 2108793fc096SFrank Li 2109793fc096SFrank Li bdp = fep->rx_bd_base; 211036e24e2eSDuan Fugang-B38611 for (i = 0; i < fep->rx_ring_size; i++) { 2111793fc096SFrank Li skb = fep->rx_skbuff[i]; 2112730ee360SRussell King fep->rx_skbuff[i] = NULL; 2113730ee360SRussell King if (skb) { 2114793fc096SFrank Li dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, 2115793fc096SFrank Li FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE); 2116793fc096SFrank Li dev_kfree_skb(skb); 2117730ee360SRussell King } 211836e24e2eSDuan Fugang-B38611 bdp = fec_enet_get_nextdesc(bdp, fep); 2119793fc096SFrank Li } 2120793fc096SFrank Li 2121793fc096SFrank Li bdp = fep->tx_bd_base; 21228b7c9efaSRussell King for (i = 0; i < fep->tx_ring_size; i++) { 2123793fc096SFrank Li kfree(fep->tx_bounce[i]); 21248b7c9efaSRussell King fep->tx_bounce[i] = NULL; 21258b7c9efaSRussell King skb = fep->tx_skbuff[i]; 21268b7c9efaSRussell King fep->tx_skbuff[i] = NULL; 21278b7c9efaSRussell King dev_kfree_skb(skb); 21288b7c9efaSRussell King } 2129793fc096SFrank Li } 2130793fc096SFrank Li 2131793fc096SFrank Li static int fec_enet_alloc_buffers(struct net_device *ndev) 2132793fc096SFrank Li { 2133793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2134793fc096SFrank Li unsigned int i; 2135793fc096SFrank Li struct sk_buff *skb; 2136793fc096SFrank Li struct bufdesc *bdp; 2137793fc096SFrank Li 2138793fc096SFrank Li bdp = fep->rx_bd_base; 213936e24e2eSDuan Fugang-B38611 for (i = 0; i < fep->rx_ring_size; i++) { 2140730ee360SRussell King dma_addr_t addr; 2141730ee360SRussell King 2142793fc096SFrank Li skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE); 2143ffdce2ccSRussell King if (!skb) 2144ffdce2ccSRussell King goto err_alloc; 2145793fc096SFrank Li 2146730ee360SRussell King addr = dma_map_single(&fep->pdev->dev, skb->data, 2147793fc096SFrank Li FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE); 2148730ee360SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 2149730ee360SRussell King dev_kfree_skb(skb); 2150d842a31fSDuan Fugang-B38611 if (net_ratelimit()) 2151d842a31fSDuan Fugang-B38611 netdev_err(ndev, "Rx DMA memory map failed\n"); 2152ffdce2ccSRussell King goto err_alloc; 2153d842a31fSDuan Fugang-B38611 } 2154730ee360SRussell King 2155730ee360SRussell King fep->rx_skbuff[i] = skb; 2156730ee360SRussell King bdp->cbd_bufaddr = addr; 2157793fc096SFrank Li bdp->cbd_sc = BD_ENET_RX_EMPTY; 2158793fc096SFrank Li 2159793fc096SFrank Li if (fep->bufdesc_ex) { 2160793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 2161793fc096SFrank Li ebdp->cbd_esc = BD_ENET_RX_INT; 2162793fc096SFrank Li } 2163793fc096SFrank Li 216436e24e2eSDuan Fugang-B38611 bdp = fec_enet_get_nextdesc(bdp, fep); 2165793fc096SFrank Li } 2166793fc096SFrank Li 2167793fc096SFrank Li /* Set the last buffer to wrap. */ 216836e24e2eSDuan Fugang-B38611 bdp = fec_enet_get_prevdesc(bdp, fep); 2169793fc096SFrank Li bdp->cbd_sc |= BD_SC_WRAP; 2170793fc096SFrank Li 2171793fc096SFrank Li bdp = fep->tx_bd_base; 217236e24e2eSDuan Fugang-B38611 for (i = 0; i < fep->tx_ring_size; i++) { 2173793fc096SFrank Li fep->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL); 2174ffdce2ccSRussell King if (!fep->tx_bounce[i]) 2175ffdce2ccSRussell King goto err_alloc; 2176793fc096SFrank Li 2177793fc096SFrank Li bdp->cbd_sc = 0; 2178793fc096SFrank Li bdp->cbd_bufaddr = 0; 2179793fc096SFrank Li 2180793fc096SFrank Li if (fep->bufdesc_ex) { 2181793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 218296d2222bSJim Baxter ebdp->cbd_esc = BD_ENET_TX_INT; 2183793fc096SFrank Li } 2184793fc096SFrank Li 218536e24e2eSDuan Fugang-B38611 bdp = fec_enet_get_nextdesc(bdp, fep); 2186793fc096SFrank Li } 2187793fc096SFrank Li 2188793fc096SFrank Li /* Set the last buffer to wrap. */ 218936e24e2eSDuan Fugang-B38611 bdp = fec_enet_get_prevdesc(bdp, fep); 2190793fc096SFrank Li bdp->cbd_sc |= BD_SC_WRAP; 2191793fc096SFrank Li 2192793fc096SFrank Li return 0; 2193ffdce2ccSRussell King 2194ffdce2ccSRussell King err_alloc: 2195ffdce2ccSRussell King fec_enet_free_buffers(ndev); 2196ffdce2ccSRussell King return -ENOMEM; 2197793fc096SFrank Li } 2198793fc096SFrank Li 2199793fc096SFrank Li static int 2200793fc096SFrank Li fec_enet_open(struct net_device *ndev) 2201793fc096SFrank Li { 2202793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2203793fc096SFrank Li int ret; 2204793fc096SFrank Li 22055bbde4d2SNimrod Andy pinctrl_pm_select_default_state(&fep->pdev->dev); 2206e8fcfcd5SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 2207e8fcfcd5SNimrod Andy if (ret) 2208e8fcfcd5SNimrod Andy return ret; 2209e8fcfcd5SNimrod Andy 2210793fc096SFrank Li /* I should reset the ring buffers here, but I don't yet know 2211793fc096SFrank Li * a simple way to do that. 2212793fc096SFrank Li */ 2213793fc096SFrank Li 2214793fc096SFrank Li ret = fec_enet_alloc_buffers(ndev); 2215793fc096SFrank Li if (ret) 2216793fc096SFrank Li return ret; 2217793fc096SFrank Li 2218793fc096SFrank Li /* Probe and connect to PHY when open the interface */ 2219793fc096SFrank Li ret = fec_enet_mii_probe(ndev); 2220793fc096SFrank Li if (ret) { 2221793fc096SFrank Li fec_enet_free_buffers(ndev); 2222793fc096SFrank Li return ret; 2223793fc096SFrank Li } 2224ce5eaf02SRussell King 2225ef83337dSRussell King fec_restart(ndev); 2226ce5eaf02SRussell King napi_enable(&fep->napi); 2227793fc096SFrank Li phy_start(fep->phy_dev); 2228793fc096SFrank Li netif_start_queue(ndev); 2229793fc096SFrank Li return 0; 2230793fc096SFrank Li } 2231793fc096SFrank Li 2232793fc096SFrank Li static int 2233793fc096SFrank Li fec_enet_close(struct net_device *ndev) 2234793fc096SFrank Li { 2235793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2236793fc096SFrank Li 2237d76cfae9SRussell King phy_stop(fep->phy_dev); 2238d76cfae9SRussell King 223931a6de34SRussell King if (netif_device_present(ndev)) { 2240793fc096SFrank Li napi_disable(&fep->napi); 2241b49cd504SRussell King netif_tx_disable(ndev); 2242793fc096SFrank Li fec_stop(ndev); 224331a6de34SRussell King } 2244793fc096SFrank Li 2245793fc096SFrank Li phy_disconnect(fep->phy_dev); 22460b146ca8SRussell King fep->phy_dev = NULL; 2247793fc096SFrank Li 2248e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 22495bbde4d2SNimrod Andy pinctrl_pm_select_sleep_state(&fep->pdev->dev); 2250793fc096SFrank Li fec_enet_free_buffers(ndev); 2251793fc096SFrank Li 2252793fc096SFrank Li return 0; 2253793fc096SFrank Li } 2254793fc096SFrank Li 2255793fc096SFrank Li /* Set or clear the multicast filter for this adaptor. 2256793fc096SFrank Li * Skeleton taken from sunlance driver. 2257793fc096SFrank Li * The CPM Ethernet implementation allows Multicast as well as individual 2258793fc096SFrank Li * MAC address filtering. Some of the drivers check to make sure it is 2259793fc096SFrank Li * a group multicast address, and discard those that are not. I guess I 2260793fc096SFrank Li * will do the same for now, but just remove the test if you want 2261793fc096SFrank Li * individual filtering as well (do the upper net layers want or support 2262793fc096SFrank Li * this kind of feature?). 2263793fc096SFrank Li */ 2264793fc096SFrank Li 2265793fc096SFrank Li #define HASH_BITS 6 /* #bits in hash */ 2266793fc096SFrank Li #define CRC32_POLY 0xEDB88320 2267793fc096SFrank Li 2268793fc096SFrank Li static void set_multicast_list(struct net_device *ndev) 2269793fc096SFrank Li { 2270793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2271793fc096SFrank Li struct netdev_hw_addr *ha; 2272793fc096SFrank Li unsigned int i, bit, data, crc, tmp; 2273793fc096SFrank Li unsigned char hash; 2274793fc096SFrank Li 2275793fc096SFrank Li if (ndev->flags & IFF_PROMISC) { 2276793fc096SFrank Li tmp = readl(fep->hwp + FEC_R_CNTRL); 2277793fc096SFrank Li tmp |= 0x8; 2278793fc096SFrank Li writel(tmp, fep->hwp + FEC_R_CNTRL); 2279793fc096SFrank Li return; 2280793fc096SFrank Li } 2281793fc096SFrank Li 2282793fc096SFrank Li tmp = readl(fep->hwp + FEC_R_CNTRL); 2283793fc096SFrank Li tmp &= ~0x8; 2284793fc096SFrank Li writel(tmp, fep->hwp + FEC_R_CNTRL); 2285793fc096SFrank Li 2286793fc096SFrank Li if (ndev->flags & IFF_ALLMULTI) { 2287793fc096SFrank Li /* Catch all multicast addresses, so set the 2288793fc096SFrank Li * filter to all 1's 2289793fc096SFrank Li */ 2290793fc096SFrank Li writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 2291793fc096SFrank Li writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 2292793fc096SFrank Li 2293793fc096SFrank Li return; 2294793fc096SFrank Li } 2295793fc096SFrank Li 2296793fc096SFrank Li /* Clear filter and add the addresses in hash register 2297793fc096SFrank Li */ 2298793fc096SFrank Li writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 2299793fc096SFrank Li writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 2300793fc096SFrank Li 2301793fc096SFrank Li netdev_for_each_mc_addr(ha, ndev) { 2302793fc096SFrank Li /* calculate crc32 value of mac address */ 2303793fc096SFrank Li crc = 0xffffffff; 2304793fc096SFrank Li 2305793fc096SFrank Li for (i = 0; i < ndev->addr_len; i++) { 2306793fc096SFrank Li data = ha->addr[i]; 2307793fc096SFrank Li for (bit = 0; bit < 8; bit++, data >>= 1) { 2308793fc096SFrank Li crc = (crc >> 1) ^ 2309793fc096SFrank Li (((crc ^ data) & 1) ? CRC32_POLY : 0); 2310793fc096SFrank Li } 2311793fc096SFrank Li } 2312793fc096SFrank Li 2313793fc096SFrank Li /* only upper 6 bits (HASH_BITS) are used 2314793fc096SFrank Li * which point to specific bit in he hash registers 2315793fc096SFrank Li */ 2316793fc096SFrank Li hash = (crc >> (32 - HASH_BITS)) & 0x3f; 2317793fc096SFrank Li 2318793fc096SFrank Li if (hash > 31) { 2319793fc096SFrank Li tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 2320793fc096SFrank Li tmp |= 1 << (hash - 32); 2321793fc096SFrank Li writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 2322793fc096SFrank Li } else { 2323793fc096SFrank Li tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_LOW); 2324793fc096SFrank Li tmp |= 1 << hash; 2325793fc096SFrank Li writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 2326793fc096SFrank Li } 2327793fc096SFrank Li } 2328793fc096SFrank Li } 2329793fc096SFrank Li 2330793fc096SFrank Li /* Set a MAC change in hardware. */ 2331793fc096SFrank Li static int 2332793fc096SFrank Li fec_set_mac_address(struct net_device *ndev, void *p) 2333793fc096SFrank Li { 2334793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2335793fc096SFrank Li struct sockaddr *addr = p; 2336793fc096SFrank Li 233744934facSLucas Stach if (addr) { 2338793fc096SFrank Li if (!is_valid_ether_addr(addr->sa_data)) 2339793fc096SFrank Li return -EADDRNOTAVAIL; 2340793fc096SFrank Li memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len); 234144934facSLucas Stach } 2342793fc096SFrank Li 2343793fc096SFrank Li writel(ndev->dev_addr[3] | (ndev->dev_addr[2] << 8) | 2344793fc096SFrank Li (ndev->dev_addr[1] << 16) | (ndev->dev_addr[0] << 24), 2345793fc096SFrank Li fep->hwp + FEC_ADDR_LOW); 2346793fc096SFrank Li writel((ndev->dev_addr[5] << 16) | (ndev->dev_addr[4] << 24), 2347793fc096SFrank Li fep->hwp + FEC_ADDR_HIGH); 2348793fc096SFrank Li return 0; 2349793fc096SFrank Li } 2350793fc096SFrank Li 2351793fc096SFrank Li #ifdef CONFIG_NET_POLL_CONTROLLER 2352793fc096SFrank Li /** 2353793fc096SFrank Li * fec_poll_controller - FEC Poll controller function 2354793fc096SFrank Li * @dev: The FEC network adapter 2355793fc096SFrank Li * 2356793fc096SFrank Li * Polled functionality used by netconsole and others in non interrupt mode 2357793fc096SFrank Li * 2358793fc096SFrank Li */ 2359793fc096SFrank Li static void fec_poll_controller(struct net_device *dev) 2360793fc096SFrank Li { 2361793fc096SFrank Li int i; 2362793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(dev); 2363793fc096SFrank Li 2364793fc096SFrank Li for (i = 0; i < FEC_IRQ_NUM; i++) { 2365793fc096SFrank Li if (fep->irq[i] > 0) { 2366793fc096SFrank Li disable_irq(fep->irq[i]); 2367793fc096SFrank Li fec_enet_interrupt(fep->irq[i], dev); 2368793fc096SFrank Li enable_irq(fep->irq[i]); 2369793fc096SFrank Li } 2370793fc096SFrank Li } 2371793fc096SFrank Li } 2372793fc096SFrank Li #endif 2373793fc096SFrank Li 23748506fa1dSRussell King #define FEATURES_NEED_QUIESCE NETIF_F_RXCSUM 23758506fa1dSRussell King 23764c09eed9SJim Baxter static int fec_set_features(struct net_device *netdev, 23774c09eed9SJim Baxter netdev_features_t features) 23784c09eed9SJim Baxter { 23794c09eed9SJim Baxter struct fec_enet_private *fep = netdev_priv(netdev); 23804c09eed9SJim Baxter netdev_features_t changed = features ^ netdev->features; 23814c09eed9SJim Baxter 23828506fa1dSRussell King /* Quiesce the device if necessary */ 23838506fa1dSRussell King if (netif_running(netdev) && changed & FEATURES_NEED_QUIESCE) { 23848506fa1dSRussell King napi_disable(&fep->napi); 23858506fa1dSRussell King netif_tx_lock_bh(netdev); 23868506fa1dSRussell King fec_stop(netdev); 23878506fa1dSRussell King } 23888506fa1dSRussell King 23894c09eed9SJim Baxter netdev->features = features; 23904c09eed9SJim Baxter 23914c09eed9SJim Baxter /* Receive checksum has been changed */ 23924c09eed9SJim Baxter if (changed & NETIF_F_RXCSUM) { 23934c09eed9SJim Baxter if (features & NETIF_F_RXCSUM) 23944c09eed9SJim Baxter fep->csum_flags |= FLAG_RX_CSUM_ENABLED; 23954c09eed9SJim Baxter else 23964c09eed9SJim Baxter fep->csum_flags &= ~FLAG_RX_CSUM_ENABLED; 23978506fa1dSRussell King } 23984c09eed9SJim Baxter 23998506fa1dSRussell King /* Resume the device after updates */ 24008506fa1dSRussell King if (netif_running(netdev) && changed & FEATURES_NEED_QUIESCE) { 2401ef83337dSRussell King fec_restart(netdev); 24024c09eed9SJim Baxter netif_wake_queue(netdev); 24036af42d42SRussell King netif_tx_unlock_bh(netdev); 2404dbc64a8eSRussell King napi_enable(&fep->napi); 24054c09eed9SJim Baxter } 24064c09eed9SJim Baxter 24074c09eed9SJim Baxter return 0; 24084c09eed9SJim Baxter } 24094c09eed9SJim Baxter 2410793fc096SFrank Li static const struct net_device_ops fec_netdev_ops = { 2411793fc096SFrank Li .ndo_open = fec_enet_open, 2412793fc096SFrank Li .ndo_stop = fec_enet_close, 2413793fc096SFrank Li .ndo_start_xmit = fec_enet_start_xmit, 2414793fc096SFrank Li .ndo_set_rx_mode = set_multicast_list, 2415793fc096SFrank Li .ndo_change_mtu = eth_change_mtu, 2416793fc096SFrank Li .ndo_validate_addr = eth_validate_addr, 2417793fc096SFrank Li .ndo_tx_timeout = fec_timeout, 2418793fc096SFrank Li .ndo_set_mac_address = fec_set_mac_address, 2419793fc096SFrank Li .ndo_do_ioctl = fec_enet_ioctl, 2420793fc096SFrank Li #ifdef CONFIG_NET_POLL_CONTROLLER 2421793fc096SFrank Li .ndo_poll_controller = fec_poll_controller, 2422793fc096SFrank Li #endif 24234c09eed9SJim Baxter .ndo_set_features = fec_set_features, 2424793fc096SFrank Li }; 2425793fc096SFrank Li 2426793fc096SFrank Li /* 2427793fc096SFrank Li * XXX: We need to clean up on failure exits here. 2428793fc096SFrank Li * 2429793fc096SFrank Li */ 2430793fc096SFrank Li static int fec_enet_init(struct net_device *ndev) 2431793fc096SFrank Li { 2432793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 243348496255SShawn Guo const struct platform_device_id *id_entry = 243448496255SShawn Guo platform_get_device_id(fep->pdev); 2435793fc096SFrank Li struct bufdesc *cbd_base; 243655d0218aSNimrod Andy int bd_size; 243755d0218aSNimrod Andy 243855d0218aSNimrod Andy /* init the tx & rx ring size */ 243955d0218aSNimrod Andy fep->tx_ring_size = TX_RING_SIZE; 244055d0218aSNimrod Andy fep->rx_ring_size = RX_RING_SIZE; 244155d0218aSNimrod Andy 244279f33912SNimrod Andy fep->tx_stop_threshold = FEC_MAX_SKB_DESCS; 244379f33912SNimrod Andy fep->tx_wake_threshold = (fep->tx_ring_size - fep->tx_stop_threshold) / 2; 244479f33912SNimrod Andy 244555d0218aSNimrod Andy if (fep->bufdesc_ex) 244655d0218aSNimrod Andy fep->bufdesc_size = sizeof(struct bufdesc_ex); 244755d0218aSNimrod Andy else 244855d0218aSNimrod Andy fep->bufdesc_size = sizeof(struct bufdesc); 244955d0218aSNimrod Andy bd_size = (fep->tx_ring_size + fep->rx_ring_size) * 245055d0218aSNimrod Andy fep->bufdesc_size; 2451793fc096SFrank Li 2452793fc096SFrank Li /* Allocate memory for buffer descriptors. */ 245355d0218aSNimrod Andy cbd_base = dma_alloc_coherent(NULL, bd_size, &fep->bd_dma, 2454793fc096SFrank Li GFP_KERNEL); 2455793fc096SFrank Li if (!cbd_base) 2456793fc096SFrank Li return -ENOMEM; 2457793fc096SFrank Li 245879f33912SNimrod Andy fep->tso_hdrs = dma_alloc_coherent(NULL, fep->tx_ring_size * TSO_HEADER_SIZE, 245979f33912SNimrod Andy &fep->tso_hdrs_dma, GFP_KERNEL); 246079f33912SNimrod Andy if (!fep->tso_hdrs) { 246179f33912SNimrod Andy dma_free_coherent(NULL, bd_size, cbd_base, fep->bd_dma); 246279f33912SNimrod Andy return -ENOMEM; 246379f33912SNimrod Andy } 246479f33912SNimrod Andy 2465a210576cSDavid S. Miller memset(cbd_base, 0, PAGE_SIZE); 2466793fc096SFrank Li 2467793fc096SFrank Li fep->netdev = ndev; 2468793fc096SFrank Li 2469793fc096SFrank Li /* Get the Ethernet address */ 2470793fc096SFrank Li fec_get_mac(ndev); 247144934facSLucas Stach /* make sure MAC we just acquired is programmed into the hw */ 247244934facSLucas Stach fec_set_mac_address(ndev, NULL); 2473793fc096SFrank Li 2474793fc096SFrank Li /* Set receive and transmit descriptor base. */ 2475793fc096SFrank Li fep->rx_bd_base = cbd_base; 247655d0218aSNimrod Andy if (fep->bufdesc_ex) 2477793fc096SFrank Li fep->tx_bd_base = (struct bufdesc *) 247836e24e2eSDuan Fugang-B38611 (((struct bufdesc_ex *)cbd_base) + fep->rx_ring_size); 247955d0218aSNimrod Andy else 248036e24e2eSDuan Fugang-B38611 fep->tx_bd_base = cbd_base + fep->rx_ring_size; 2481793fc096SFrank Li 2482793fc096SFrank Li /* The FEC Ethernet specific entries in the device structure */ 2483793fc096SFrank Li ndev->watchdog_timeo = TX_TIMEOUT; 2484793fc096SFrank Li ndev->netdev_ops = &fec_netdev_ops; 2485793fc096SFrank Li ndev->ethtool_ops = &fec_enet_ethtool_ops; 2486793fc096SFrank Li 2487793fc096SFrank Li writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK); 2488322555f5SFabio Estevam netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, NAPI_POLL_WEIGHT); 2489793fc096SFrank Li 249009d1e541SNimrod Andy if (id_entry->driver_data & FEC_QUIRK_HAS_VLAN) 2491cdffcf1bSJim Baxter /* enable hw VLAN support */ 2492cdffcf1bSJim Baxter ndev->features |= NETIF_F_HW_VLAN_CTAG_RX; 2493cdffcf1bSJim Baxter 249448496255SShawn Guo if (id_entry->driver_data & FEC_QUIRK_HAS_CSUM) { 249579f33912SNimrod Andy ndev->gso_max_segs = FEC_MAX_TSO_SEGS; 249679f33912SNimrod Andy 24974c09eed9SJim Baxter /* enable hw accelerator */ 24984c09eed9SJim Baxter ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM 249979f33912SNimrod Andy | NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO); 25004c09eed9SJim Baxter fep->csum_flags |= FLAG_RX_CSUM_ENABLED; 250148496255SShawn Guo } 25024c09eed9SJim Baxter 250309d1e541SNimrod Andy ndev->hw_features = ndev->features; 250409d1e541SNimrod Andy 2505ef83337dSRussell King fec_restart(ndev); 2506793fc096SFrank Li 2507793fc096SFrank Li return 0; 2508793fc096SFrank Li } 2509793fc096SFrank Li 2510793fc096SFrank Li #ifdef CONFIG_OF 2511793fc096SFrank Li static void fec_reset_phy(struct platform_device *pdev) 2512793fc096SFrank Li { 2513793fc096SFrank Li int err, phy_reset; 2514793fc096SFrank Li int msec = 1; 2515793fc096SFrank Li struct device_node *np = pdev->dev.of_node; 2516793fc096SFrank Li 2517793fc096SFrank Li if (!np) 2518793fc096SFrank Li return; 2519793fc096SFrank Li 2520793fc096SFrank Li of_property_read_u32(np, "phy-reset-duration", &msec); 2521793fc096SFrank Li /* A sane reset duration should not be longer than 1s */ 2522793fc096SFrank Li if (msec > 1000) 2523793fc096SFrank Li msec = 1; 2524793fc096SFrank Li 2525793fc096SFrank Li phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0); 2526793fc096SFrank Li if (!gpio_is_valid(phy_reset)) 2527793fc096SFrank Li return; 2528793fc096SFrank Li 2529793fc096SFrank Li err = devm_gpio_request_one(&pdev->dev, phy_reset, 2530793fc096SFrank Li GPIOF_OUT_INIT_LOW, "phy-reset"); 2531793fc096SFrank Li if (err) { 2532793fc096SFrank Li dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err); 2533793fc096SFrank Li return; 2534793fc096SFrank Li } 2535793fc096SFrank Li msleep(msec); 2536793fc096SFrank Li gpio_set_value(phy_reset, 1); 2537793fc096SFrank Li } 2538793fc096SFrank Li #else /* CONFIG_OF */ 2539793fc096SFrank Li static void fec_reset_phy(struct platform_device *pdev) 2540793fc096SFrank Li { 2541793fc096SFrank Li /* 2542793fc096SFrank Li * In case of platform probe, the reset has been done 2543793fc096SFrank Li * by machine code. 2544793fc096SFrank Li */ 2545793fc096SFrank Li } 2546793fc096SFrank Li #endif /* CONFIG_OF */ 2547793fc096SFrank Li 2548793fc096SFrank Li static int 2549793fc096SFrank Li fec_probe(struct platform_device *pdev) 2550793fc096SFrank Li { 2551793fc096SFrank Li struct fec_enet_private *fep; 2552793fc096SFrank Li struct fec_platform_data *pdata; 2553793fc096SFrank Li struct net_device *ndev; 2554793fc096SFrank Li int i, irq, ret = 0; 2555793fc096SFrank Li struct resource *r; 2556793fc096SFrank Li const struct of_device_id *of_id; 2557793fc096SFrank Li static int dev_id; 2558407066f8SUwe Kleine-König struct device_node *np = pdev->dev.of_node, *phy_node; 2559793fc096SFrank Li 2560793fc096SFrank Li of_id = of_match_device(fec_dt_ids, &pdev->dev); 2561793fc096SFrank Li if (of_id) 2562793fc096SFrank Li pdev->id_entry = of_id->data; 2563793fc096SFrank Li 2564793fc096SFrank Li /* Init network device */ 2565793fc096SFrank Li ndev = alloc_etherdev(sizeof(struct fec_enet_private)); 2566793fc096SFrank Li if (!ndev) 2567793fc096SFrank Li return -ENOMEM; 2568793fc096SFrank Li 2569793fc096SFrank Li SET_NETDEV_DEV(ndev, &pdev->dev); 2570793fc096SFrank Li 2571793fc096SFrank Li /* setup board info structure */ 2572793fc096SFrank Li fep = netdev_priv(ndev); 2573793fc096SFrank Li 2574d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 2575793fc096SFrank Li /* default enable pause frame auto negotiation */ 2576793fc096SFrank Li if (pdev->id_entry && 2577793fc096SFrank Li (pdev->id_entry->driver_data & FEC_QUIRK_HAS_GBIT)) 2578793fc096SFrank Li fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG; 2579d1391930SGuenter Roeck #endif 2580793fc096SFrank Li 25815bbde4d2SNimrod Andy /* Select default pin state */ 25825bbde4d2SNimrod Andy pinctrl_pm_select_default_state(&pdev->dev); 25835bbde4d2SNimrod Andy 2584399db75bSFabio Estevam r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2585941e173aSTushar Behera fep->hwp = devm_ioremap_resource(&pdev->dev, r); 2586941e173aSTushar Behera if (IS_ERR(fep->hwp)) { 2587941e173aSTushar Behera ret = PTR_ERR(fep->hwp); 2588941e173aSTushar Behera goto failed_ioremap; 2589941e173aSTushar Behera } 2590941e173aSTushar Behera 2591793fc096SFrank Li fep->pdev = pdev; 2592793fc096SFrank Li fep->dev_id = dev_id++; 2593793fc096SFrank Li 2594793fc096SFrank Li fep->bufdesc_ex = 0; 2595793fc096SFrank Li 2596793fc096SFrank Li platform_set_drvdata(pdev, ndev); 2597793fc096SFrank Li 2598407066f8SUwe Kleine-König phy_node = of_parse_phandle(np, "phy-handle", 0); 2599407066f8SUwe Kleine-König if (!phy_node && of_phy_is_fixed_link(np)) { 2600407066f8SUwe Kleine-König ret = of_phy_register_fixed_link(np); 2601407066f8SUwe Kleine-König if (ret < 0) { 2602407066f8SUwe Kleine-König dev_err(&pdev->dev, 2603407066f8SUwe Kleine-König "broken fixed-link specification\n"); 2604407066f8SUwe Kleine-König goto failed_phy; 2605407066f8SUwe Kleine-König } 2606407066f8SUwe Kleine-König phy_node = of_node_get(np); 2607407066f8SUwe Kleine-König } 2608407066f8SUwe Kleine-König fep->phy_node = phy_node; 2609407066f8SUwe Kleine-König 26106c5f7808SGuenter Roeck ret = of_get_phy_mode(pdev->dev.of_node); 2611793fc096SFrank Li if (ret < 0) { 261294660ba0SJingoo Han pdata = dev_get_platdata(&pdev->dev); 2613793fc096SFrank Li if (pdata) 2614793fc096SFrank Li fep->phy_interface = pdata->phy; 2615793fc096SFrank Li else 2616793fc096SFrank Li fep->phy_interface = PHY_INTERFACE_MODE_MII; 2617793fc096SFrank Li } else { 2618793fc096SFrank Li fep->phy_interface = ret; 2619793fc096SFrank Li } 2620793fc096SFrank Li 2621793fc096SFrank Li fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); 2622793fc096SFrank Li if (IS_ERR(fep->clk_ipg)) { 2623793fc096SFrank Li ret = PTR_ERR(fep->clk_ipg); 2624793fc096SFrank Li goto failed_clk; 2625793fc096SFrank Li } 2626793fc096SFrank Li 2627793fc096SFrank Li fep->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); 2628793fc096SFrank Li if (IS_ERR(fep->clk_ahb)) { 2629793fc096SFrank Li ret = PTR_ERR(fep->clk_ahb); 2630793fc096SFrank Li goto failed_clk; 2631793fc096SFrank Li } 2632793fc096SFrank Li 263338f56f33SLinus Torvalds /* enet_out is optional, depends on board */ 263438f56f33SLinus Torvalds fep->clk_enet_out = devm_clk_get(&pdev->dev, "enet_out"); 263538f56f33SLinus Torvalds if (IS_ERR(fep->clk_enet_out)) 263638f56f33SLinus Torvalds fep->clk_enet_out = NULL; 263738f56f33SLinus Torvalds 2638*91c0d987SNimrod Andy fep->ptp_clk_on = false; 2639*91c0d987SNimrod Andy mutex_init(&fep->ptp_clk_mutex); 2640793fc096SFrank Li fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp"); 2641793fc096SFrank Li fep->bufdesc_ex = 2642793fc096SFrank Li pdev->id_entry->driver_data & FEC_QUIRK_HAS_BUFDESC_EX; 2643793fc096SFrank Li if (IS_ERR(fep->clk_ptp)) { 264438f56f33SLinus Torvalds fep->clk_ptp = NULL; 2645793fc096SFrank Li fep->bufdesc_ex = 0; 2646793fc096SFrank Li } 2647793fc096SFrank Li 2648e8fcfcd5SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 264913a097bdSFabio Estevam if (ret) 265013a097bdSFabio Estevam goto failed_clk; 265113a097bdSFabio Estevam 2652f4e9f3d2SFabio Estevam fep->reg_phy = devm_regulator_get(&pdev->dev, "phy"); 2653f4e9f3d2SFabio Estevam if (!IS_ERR(fep->reg_phy)) { 2654f4e9f3d2SFabio Estevam ret = regulator_enable(fep->reg_phy); 2655793fc096SFrank Li if (ret) { 2656793fc096SFrank Li dev_err(&pdev->dev, 2657793fc096SFrank Li "Failed to enable phy regulator: %d\n", ret); 2658793fc096SFrank Li goto failed_regulator; 2659793fc096SFrank Li } 2660f6a4d607SFabio Estevam } else { 2661f6a4d607SFabio Estevam fep->reg_phy = NULL; 2662793fc096SFrank Li } 2663793fc096SFrank Li 2664793fc096SFrank Li fec_reset_phy(pdev); 2665793fc096SFrank Li 2666793fc096SFrank Li if (fep->bufdesc_ex) 2667ca162a82SFabio Estevam fec_ptp_init(pdev); 2668793fc096SFrank Li 2669793fc096SFrank Li ret = fec_enet_init(ndev); 2670793fc096SFrank Li if (ret) 2671793fc096SFrank Li goto failed_init; 2672793fc096SFrank Li 2673793fc096SFrank Li for (i = 0; i < FEC_IRQ_NUM; i++) { 2674793fc096SFrank Li irq = platform_get_irq(pdev, i); 2675793fc096SFrank Li if (irq < 0) { 2676793fc096SFrank Li if (i) 2677793fc096SFrank Li break; 2678793fc096SFrank Li ret = irq; 2679793fc096SFrank Li goto failed_irq; 2680793fc096SFrank Li } 26810d9b2ab1SFabio Estevam ret = devm_request_irq(&pdev->dev, irq, fec_enet_interrupt, 268244a272ddSMichael Opdenacker 0, pdev->name, ndev); 26830d9b2ab1SFabio Estevam if (ret) 2684793fc096SFrank Li goto failed_irq; 2685793fc096SFrank Li } 2686793fc096SFrank Li 2687793fc096SFrank Li ret = fec_enet_mii_init(pdev); 2688793fc096SFrank Li if (ret) 2689793fc096SFrank Li goto failed_mii_init; 2690793fc096SFrank Li 2691793fc096SFrank Li /* Carrier starts down, phylib will bring it up */ 2692793fc096SFrank Li netif_carrier_off(ndev); 2693e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 26945bbde4d2SNimrod Andy pinctrl_pm_select_sleep_state(&pdev->dev); 2695793fc096SFrank Li 2696793fc096SFrank Li ret = register_netdev(ndev); 2697793fc096SFrank Li if (ret) 2698793fc096SFrank Li goto failed_register; 2699793fc096SFrank Li 2700eb1d0640SFabio Estevam if (fep->bufdesc_ex && fep->ptp_clock) 2701eb1d0640SFabio Estevam netdev_info(ndev, "registered PHC device %d\n", fep->dev_id); 2702eb1d0640SFabio Estevam 270336cdc743SRussell King INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work); 2704793fc096SFrank Li return 0; 2705793fc096SFrank Li 2706793fc096SFrank Li failed_register: 2707793fc096SFrank Li fec_enet_mii_remove(fep); 2708793fc096SFrank Li failed_mii_init: 27097a2bbd8dSFabio Estevam failed_irq: 27107a2bbd8dSFabio Estevam failed_init: 2711f6a4d607SFabio Estevam if (fep->reg_phy) 2712f6a4d607SFabio Estevam regulator_disable(fep->reg_phy); 2713793fc096SFrank Li failed_regulator: 2714e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 2715793fc096SFrank Li failed_clk: 2716407066f8SUwe Kleine-König failed_phy: 2717407066f8SUwe Kleine-König of_node_put(phy_node); 2718793fc096SFrank Li failed_ioremap: 2719793fc096SFrank Li free_netdev(ndev); 2720793fc096SFrank Li 2721793fc096SFrank Li return ret; 2722793fc096SFrank Li } 2723793fc096SFrank Li 2724793fc096SFrank Li static int 2725793fc096SFrank Li fec_drv_remove(struct platform_device *pdev) 2726793fc096SFrank Li { 2727793fc096SFrank Li struct net_device *ndev = platform_get_drvdata(pdev); 2728793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2729793fc096SFrank Li 2730*91c0d987SNimrod Andy cancel_delayed_work_sync(&fep->time_keep); 273136cdc743SRussell King cancel_work_sync(&fep->tx_timeout_work); 2732793fc096SFrank Li unregister_netdev(ndev); 2733793fc096SFrank Li fec_enet_mii_remove(fep); 2734f6a4d607SFabio Estevam if (fep->reg_phy) 2735f6a4d607SFabio Estevam regulator_disable(fep->reg_phy); 2736793fc096SFrank Li if (fep->ptp_clock) 2737793fc096SFrank Li ptp_clock_unregister(fep->ptp_clock); 2738e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 2739407066f8SUwe Kleine-König of_node_put(fep->phy_node); 2740793fc096SFrank Li free_netdev(ndev); 2741793fc096SFrank Li 2742793fc096SFrank Li return 0; 2743793fc096SFrank Li } 2744793fc096SFrank Li 2745dd66d386SFabio Estevam static int __maybe_unused fec_suspend(struct device *dev) 2746793fc096SFrank Li { 2747793fc096SFrank Li struct net_device *ndev = dev_get_drvdata(dev); 2748793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2749793fc096SFrank Li 2750da1774e5SRussell King rtnl_lock(); 2751793fc096SFrank Li if (netif_running(ndev)) { 2752d76cfae9SRussell King phy_stop(fep->phy_dev); 275331a6de34SRussell King napi_disable(&fep->napi); 275431a6de34SRussell King netif_tx_lock_bh(ndev); 2755793fc096SFrank Li netif_device_detach(ndev); 275631a6de34SRussell King netif_tx_unlock_bh(ndev); 275731a6de34SRussell King fec_stop(ndev); 2758793fc096SFrank Li } 2759da1774e5SRussell King rtnl_unlock(); 2760da1774e5SRussell King 2761e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 27625bbde4d2SNimrod Andy pinctrl_pm_select_sleep_state(&fep->pdev->dev); 2763793fc096SFrank Li 2764238f7bc7SFabio Estevam if (fep->reg_phy) 2765238f7bc7SFabio Estevam regulator_disable(fep->reg_phy); 2766238f7bc7SFabio Estevam 2767793fc096SFrank Li return 0; 2768793fc096SFrank Li } 2769793fc096SFrank Li 2770dd66d386SFabio Estevam static int __maybe_unused fec_resume(struct device *dev) 2771793fc096SFrank Li { 2772793fc096SFrank Li struct net_device *ndev = dev_get_drvdata(dev); 2773793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2774238f7bc7SFabio Estevam int ret; 2775238f7bc7SFabio Estevam 2776238f7bc7SFabio Estevam if (fep->reg_phy) { 2777238f7bc7SFabio Estevam ret = regulator_enable(fep->reg_phy); 2778238f7bc7SFabio Estevam if (ret) 2779238f7bc7SFabio Estevam return ret; 2780238f7bc7SFabio Estevam } 2781793fc096SFrank Li 27825bbde4d2SNimrod Andy pinctrl_pm_select_default_state(&fep->pdev->dev); 2783e8fcfcd5SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 278413a097bdSFabio Estevam if (ret) 2785e8fcfcd5SNimrod Andy goto failed_clk; 278613a097bdSFabio Estevam 2787da1774e5SRussell King rtnl_lock(); 2788793fc096SFrank Li if (netif_running(ndev)) { 2789ef83337dSRussell King fec_restart(ndev); 279031a6de34SRussell King netif_tx_lock_bh(ndev); 2791793fc096SFrank Li netif_device_attach(ndev); 27926af42d42SRussell King netif_tx_unlock_bh(ndev); 27936af42d42SRussell King napi_enable(&fep->napi); 2794d76cfae9SRussell King phy_start(fep->phy_dev); 2795793fc096SFrank Li } 2796da1774e5SRussell King rtnl_unlock(); 2797793fc096SFrank Li 2798793fc096SFrank Li return 0; 279913a097bdSFabio Estevam 2800e8fcfcd5SNimrod Andy failed_clk: 280113a097bdSFabio Estevam if (fep->reg_phy) 280213a097bdSFabio Estevam regulator_disable(fep->reg_phy); 280313a097bdSFabio Estevam return ret; 2804793fc096SFrank Li } 2805793fc096SFrank Li 2806bf7bfd7fSFabio Estevam static SIMPLE_DEV_PM_OPS(fec_pm_ops, fec_suspend, fec_resume); 2807793fc096SFrank Li 2808793fc096SFrank Li static struct platform_driver fec_driver = { 2809793fc096SFrank Li .driver = { 2810793fc096SFrank Li .name = DRIVER_NAME, 2811793fc096SFrank Li .owner = THIS_MODULE, 2812793fc096SFrank Li .pm = &fec_pm_ops, 2813793fc096SFrank Li .of_match_table = fec_dt_ids, 2814793fc096SFrank Li }, 2815793fc096SFrank Li .id_table = fec_devtype, 2816793fc096SFrank Li .probe = fec_probe, 2817793fc096SFrank Li .remove = fec_drv_remove, 2818793fc096SFrank Li }; 2819793fc096SFrank Li 2820793fc096SFrank Li module_platform_driver(fec_driver); 2821793fc096SFrank Li 2822f8c0aca9SFabio Estevam MODULE_ALIAS("platform:"DRIVER_NAME); 2823793fc096SFrank Li MODULE_LICENSE("GPL"); 2824