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> 278fff755eSAndrew Lunn #include <linux/pm_runtime.h> 28793fc096SFrank Li #include <linux/ptrace.h> 29793fc096SFrank Li #include <linux/errno.h> 30793fc096SFrank Li #include <linux/ioport.h> 31793fc096SFrank Li #include <linux/slab.h> 32793fc096SFrank Li #include <linux/interrupt.h> 33793fc096SFrank Li #include <linux/delay.h> 34793fc096SFrank Li #include <linux/netdevice.h> 35793fc096SFrank Li #include <linux/etherdevice.h> 36793fc096SFrank Li #include <linux/skbuff.h> 374c09eed9SJim Baxter #include <linux/in.h> 384c09eed9SJim Baxter #include <linux/ip.h> 394c09eed9SJim Baxter #include <net/ip.h> 4079f33912SNimrod Andy #include <net/tso.h> 414c09eed9SJim Baxter #include <linux/tcp.h> 424c09eed9SJim Baxter #include <linux/udp.h> 434c09eed9SJim Baxter #include <linux/icmp.h> 44793fc096SFrank Li #include <linux/spinlock.h> 45793fc096SFrank Li #include <linux/workqueue.h> 46793fc096SFrank Li #include <linux/bitops.h> 47793fc096SFrank Li #include <linux/io.h> 48793fc096SFrank Li #include <linux/irq.h> 49793fc096SFrank Li #include <linux/clk.h> 50793fc096SFrank Li #include <linux/platform_device.h> 517f854420SAndrew Lunn #include <linux/mdio.h> 52793fc096SFrank Li #include <linux/phy.h> 53793fc096SFrank Li #include <linux/fec.h> 54793fc096SFrank Li #include <linux/of.h> 55793fc096SFrank Li #include <linux/of_device.h> 56793fc096SFrank Li #include <linux/of_gpio.h> 57407066f8SUwe Kleine-König #include <linux/of_mdio.h> 58793fc096SFrank Li #include <linux/of_net.h> 59793fc096SFrank Li #include <linux/regulator/consumer.h> 60cdffcf1bSJim Baxter #include <linux/if_vlan.h> 61a68ab98eSFabio Estevam #include <linux/pinctrl/consumer.h> 62c259c132SFrank Li #include <linux/prefetch.h> 63793fc096SFrank Li 64793fc096SFrank Li #include <asm/cacheflush.h> 65793fc096SFrank Li 66793fc096SFrank Li #include "fec.h" 67793fc096SFrank Li 68772e42b0SChristoph Müllner static void set_multicast_list(struct net_device *ndev); 69d851b47bSFugang Duan static void fec_enet_itr_coal_init(struct net_device *ndev); 70772e42b0SChristoph Müllner 71793fc096SFrank Li #define DRIVER_NAME "fec" 72793fc096SFrank Li 734d494cdcSFugang Duan #define FEC_ENET_GET_QUQUE(_x) ((_x == 0) ? 1 : ((_x == 1) ? 2 : 0)) 744d494cdcSFugang Duan 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 828fff755eSAndrew Lunn #define FEC_MDIO_PM_TIMEOUT 100 /* ms */ 83793fc096SFrank Li 84793fc096SFrank Li static struct platform_device_id fec_devtype[] = { 85793fc096SFrank Li { 86793fc096SFrank Li /* keep it for coldfire */ 87793fc096SFrank Li .name = DRIVER_NAME, 88793fc096SFrank Li .driver_data = 0, 89793fc096SFrank Li }, { 90793fc096SFrank Li .name = "imx25-fec", 9118803495SGreg Ungerer .driver_data = FEC_QUIRK_USE_GASKET | FEC_QUIRK_HAS_RACC, 92793fc096SFrank Li }, { 93793fc096SFrank Li .name = "imx27-fec", 9418803495SGreg Ungerer .driver_data = FEC_QUIRK_HAS_RACC, 95793fc096SFrank Li }, { 96793fc096SFrank Li .name = "imx28-fec", 973d125f9cSStefan Agner .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME | 9818803495SGreg Ungerer FEC_QUIRK_SINGLE_MDIO | FEC_QUIRK_HAS_RACC, 99793fc096SFrank Li }, { 100793fc096SFrank Li .name = "imx6q-fec", 101793fc096SFrank Li .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 102cdffcf1bSJim Baxter FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 10318803495SGreg Ungerer FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR006358 | 10418803495SGreg Ungerer FEC_QUIRK_HAS_RACC, 105793fc096SFrank Li }, { 10636803542SShawn Guo .name = "mvf600-fec", 10718803495SGreg Ungerer .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_RACC, 108ca7c4a45SJingchang Lu }, { 10995a77470SFugang Duan .name = "imx6sx-fec", 11095a77470SFugang Duan .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 11195a77470SFugang Duan FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 112f88c7edeSNimrod Andy FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | 11318803495SGreg Ungerer FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | 11418803495SGreg Ungerer FEC_QUIRK_HAS_RACC, 11595a77470SFugang Duan }, { 116793fc096SFrank Li /* sentinel */ 117793fc096SFrank Li } 118793fc096SFrank Li }; 119793fc096SFrank Li MODULE_DEVICE_TABLE(platform, fec_devtype); 120793fc096SFrank Li 121793fc096SFrank Li enum imx_fec_type { 122793fc096SFrank Li IMX25_FEC = 1, /* runs on i.mx25/50/53 */ 123793fc096SFrank Li IMX27_FEC, /* runs on i.mx27/35/51 */ 124793fc096SFrank Li IMX28_FEC, 125793fc096SFrank Li IMX6Q_FEC, 12636803542SShawn Guo MVF600_FEC, 127ba593e00SFugang Duan IMX6SX_FEC, 128793fc096SFrank Li }; 129793fc096SFrank Li 130793fc096SFrank Li static const struct of_device_id fec_dt_ids[] = { 131793fc096SFrank Li { .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], }, 132793fc096SFrank Li { .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], }, 133793fc096SFrank Li { .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], }, 134793fc096SFrank Li { .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], }, 13536803542SShawn Guo { .compatible = "fsl,mvf600-fec", .data = &fec_devtype[MVF600_FEC], }, 136ba593e00SFugang Duan { .compatible = "fsl,imx6sx-fec", .data = &fec_devtype[IMX6SX_FEC], }, 137793fc096SFrank Li { /* sentinel */ } 138793fc096SFrank Li }; 139793fc096SFrank Li MODULE_DEVICE_TABLE(of, fec_dt_ids); 140793fc096SFrank Li 141793fc096SFrank Li static unsigned char macaddr[ETH_ALEN]; 142793fc096SFrank Li module_param_array(macaddr, byte, NULL, 0); 143793fc096SFrank Li MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); 144793fc096SFrank Li 145793fc096SFrank Li #if defined(CONFIG_M5272) 146793fc096SFrank Li /* 147793fc096SFrank Li * Some hardware gets it MAC address out of local flash memory. 148793fc096SFrank Li * if this is non-zero then assume it is the address to get MAC from. 149793fc096SFrank Li */ 150793fc096SFrank Li #if defined(CONFIG_NETtel) 151793fc096SFrank Li #define FEC_FLASHMAC 0xf0006006 152793fc096SFrank Li #elif defined(CONFIG_GILBARCONAP) || defined(CONFIG_SCALES) 153793fc096SFrank Li #define FEC_FLASHMAC 0xf0006000 154793fc096SFrank Li #elif defined(CONFIG_CANCam) 155793fc096SFrank Li #define FEC_FLASHMAC 0xf0020000 156793fc096SFrank Li #elif defined (CONFIG_M5272C3) 157793fc096SFrank Li #define FEC_FLASHMAC (0xffe04000 + 4) 158793fc096SFrank Li #elif defined(CONFIG_MOD5272) 159793fc096SFrank Li #define FEC_FLASHMAC 0xffc0406b 160793fc096SFrank Li #else 161793fc096SFrank Li #define FEC_FLASHMAC 0 162793fc096SFrank Li #endif 163793fc096SFrank Li #endif /* CONFIG_M5272 */ 164793fc096SFrank Li 165cdffcf1bSJim Baxter /* The FEC stores dest/src/type/vlan, data, and checksum for receive packets. 166793fc096SFrank Li */ 167cdffcf1bSJim Baxter #define PKT_MAXBUF_SIZE 1522 168793fc096SFrank Li #define PKT_MINBUF_SIZE 64 169cdffcf1bSJim Baxter #define PKT_MAXBLR_SIZE 1536 170793fc096SFrank Li 1714c09eed9SJim Baxter /* FEC receive acceleration */ 1724c09eed9SJim Baxter #define FEC_RACC_IPDIS (1 << 1) 1734c09eed9SJim Baxter #define FEC_RACC_PRODIS (1 << 2) 1744c09eed9SJim Baxter #define FEC_RACC_OPTIONS (FEC_RACC_IPDIS | FEC_RACC_PRODIS) 1754c09eed9SJim Baxter 176793fc096SFrank Li /* 177793fc096SFrank Li * The 5270/5271/5280/5282/532x RX control register also contains maximum frame 178793fc096SFrank Li * size bits. Other FEC hardware does not, so we need to take that into 179793fc096SFrank Li * account when setting it. 180793fc096SFrank Li */ 181793fc096SFrank Li #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ 182793fc096SFrank Li defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) 183793fc096SFrank Li #define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16) 184793fc096SFrank Li #else 185793fc096SFrank Li #define OPT_FRAME_SIZE 0 186793fc096SFrank Li #endif 187793fc096SFrank Li 188793fc096SFrank Li /* FEC MII MMFR bits definition */ 189793fc096SFrank Li #define FEC_MMFR_ST (1 << 30) 190793fc096SFrank Li #define FEC_MMFR_OP_READ (2 << 28) 191793fc096SFrank Li #define FEC_MMFR_OP_WRITE (1 << 28) 192793fc096SFrank Li #define FEC_MMFR_PA(v) ((v & 0x1f) << 23) 193793fc096SFrank Li #define FEC_MMFR_RA(v) ((v & 0x1f) << 18) 194793fc096SFrank Li #define FEC_MMFR_TA (2 << 16) 195793fc096SFrank Li #define FEC_MMFR_DATA(v) (v & 0xffff) 196de40ed31SNimrod Andy /* FEC ECR bits definition */ 197de40ed31SNimrod Andy #define FEC_ECR_MAGICEN (1 << 2) 198de40ed31SNimrod Andy #define FEC_ECR_SLEEP (1 << 3) 199793fc096SFrank Li 200793fc096SFrank Li #define FEC_MII_TIMEOUT 30000 /* us */ 201793fc096SFrank Li 202793fc096SFrank Li /* Transmitter timeout */ 203793fc096SFrank Li #define TX_TIMEOUT (2 * HZ) 204793fc096SFrank Li 205793fc096SFrank Li #define FEC_PAUSE_FLAG_AUTONEG 0x1 206793fc096SFrank Li #define FEC_PAUSE_FLAG_ENABLE 0x2 207de40ed31SNimrod Andy #define FEC_WOL_HAS_MAGIC_PACKET (0x1 << 0) 208de40ed31SNimrod Andy #define FEC_WOL_FLAG_ENABLE (0x1 << 1) 209de40ed31SNimrod Andy #define FEC_WOL_FLAG_SLEEP_ON (0x1 << 2) 210793fc096SFrank Li 2111b7bde6dSNimrod Andy #define COPYBREAK_DEFAULT 256 2121b7bde6dSNimrod Andy 21379f33912SNimrod Andy #define TSO_HEADER_SIZE 128 21479f33912SNimrod Andy /* Max number of allowed TCP segments for software TSO */ 21579f33912SNimrod Andy #define FEC_MAX_TSO_SEGS 100 21679f33912SNimrod Andy #define FEC_MAX_SKB_DESCS (FEC_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS) 21779f33912SNimrod Andy 21879f33912SNimrod Andy #define IS_TSO_HEADER(txq, addr) \ 21979f33912SNimrod Andy ((addr >= txq->tso_hdrs_dma) && \ 2207355f276STroy Kisky (addr < txq->tso_hdrs_dma + txq->bd.ring_size * TSO_HEADER_SIZE)) 22179f33912SNimrod Andy 222793fc096SFrank Li static int mii_cnt; 223793fc096SFrank Li 2247355f276STroy Kisky static struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, 2257355f276STroy Kisky struct bufdesc_prop *bd) 226793fc096SFrank Li { 2277355f276STroy Kisky return (bdp >= bd->last) ? bd->base 2287355f276STroy Kisky : (struct bufdesc *)(((unsigned)bdp) + bd->dsize); 229793fc096SFrank Li } 230793fc096SFrank Li 2317355f276STroy Kisky static struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, 2327355f276STroy Kisky struct bufdesc_prop *bd) 23336e24e2eSDuan Fugang-B38611 { 2347355f276STroy Kisky return (bdp <= bd->base) ? bd->last 2357355f276STroy Kisky : (struct bufdesc *)(((unsigned)bdp) - bd->dsize); 23636e24e2eSDuan Fugang-B38611 } 23736e24e2eSDuan Fugang-B38611 2387355f276STroy Kisky static int fec_enet_get_bd_index(struct bufdesc *bdp, 2397355f276STroy Kisky struct bufdesc_prop *bd) 24061a4427bSNimrod Andy { 2417355f276STroy Kisky return ((const char *)bdp - (const char *)bd->base) >> bd->dsize_log2; 24261a4427bSNimrod Andy } 24361a4427bSNimrod Andy 2447355f276STroy Kisky static int fec_enet_get_free_txdesc_num(struct fec_enet_priv_tx_q *txq) 2456e909283SNimrod Andy { 2466e909283SNimrod Andy int entries; 2476e909283SNimrod Andy 2487355f276STroy Kisky entries = (((const char *)txq->dirty_tx - 2497355f276STroy Kisky (const char *)txq->bd.cur) >> txq->bd.dsize_log2) - 1; 2506e909283SNimrod Andy 2517355f276STroy Kisky return entries >= 0 ? entries : entries + txq->bd.ring_size; 2526e909283SNimrod Andy } 2536e909283SNimrod Andy 254c20e599bSLothar Waßmann static void swap_buffer(void *bufaddr, int len) 255793fc096SFrank Li { 256793fc096SFrank Li int i; 257793fc096SFrank Li unsigned int *buf = bufaddr; 258793fc096SFrank Li 2597b487d07SLothar Waßmann for (i = 0; i < len; i += 4, buf++) 260e453789aSLothar Waßmann swab32s(buf); 261793fc096SFrank Li } 262793fc096SFrank Li 2631310b544SLothar Waßmann static void swap_buffer2(void *dst_buf, void *src_buf, int len) 2641310b544SLothar Waßmann { 2651310b544SLothar Waßmann int i; 2661310b544SLothar Waßmann unsigned int *src = src_buf; 2671310b544SLothar Waßmann unsigned int *dst = dst_buf; 2681310b544SLothar Waßmann 2691310b544SLothar Waßmann for (i = 0; i < len; i += 4, src++, dst++) 2701310b544SLothar Waßmann *dst = swab32p(src); 2711310b544SLothar Waßmann } 2721310b544SLothar Waßmann 273344756f6SRussell King static void fec_dump(struct net_device *ndev) 274344756f6SRussell King { 275344756f6SRussell King struct fec_enet_private *fep = netdev_priv(ndev); 2764d494cdcSFugang Duan struct bufdesc *bdp; 2774d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 2784d494cdcSFugang Duan int index = 0; 279344756f6SRussell King 280344756f6SRussell King netdev_info(ndev, "TX ring dump\n"); 281344756f6SRussell King pr_info("Nr SC addr len SKB\n"); 282344756f6SRussell King 2834d494cdcSFugang Duan txq = fep->tx_queue[0]; 2847355f276STroy Kisky bdp = txq->bd.base; 2854d494cdcSFugang Duan 286344756f6SRussell King do { 2875cfa3039SJohannes Berg pr_info("%3u %c%c 0x%04x 0x%08x %4u %p\n", 288344756f6SRussell King index, 2897355f276STroy Kisky bdp == txq->bd.cur ? 'S' : ' ', 2904d494cdcSFugang Duan bdp == txq->dirty_tx ? 'H' : ' ', 2915cfa3039SJohannes Berg fec16_to_cpu(bdp->cbd_sc), 2925cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 2935cfa3039SJohannes Berg fec16_to_cpu(bdp->cbd_datlen), 2944d494cdcSFugang Duan txq->tx_skbuff[index]); 2957355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 296344756f6SRussell King index++; 2977355f276STroy Kisky } while (bdp != txq->bd.base); 298344756f6SRussell King } 299344756f6SRussell King 30062a02c98SFugang Duan static inline bool is_ipv4_pkt(struct sk_buff *skb) 30162a02c98SFugang Duan { 30262a02c98SFugang Duan return skb->protocol == htons(ETH_P_IP) && ip_hdr(skb)->version == 4; 30362a02c98SFugang Duan } 30462a02c98SFugang Duan 3054c09eed9SJim Baxter static int 3064c09eed9SJim Baxter fec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev) 3074c09eed9SJim Baxter { 3084c09eed9SJim Baxter /* Only run for packets requiring a checksum. */ 3094c09eed9SJim Baxter if (skb->ip_summed != CHECKSUM_PARTIAL) 3104c09eed9SJim Baxter return 0; 3114c09eed9SJim Baxter 3124c09eed9SJim Baxter if (unlikely(skb_cow_head(skb, 0))) 3134c09eed9SJim Baxter return -1; 3144c09eed9SJim Baxter 31562a02c98SFugang Duan if (is_ipv4_pkt(skb)) 31696c50caaSNimrod Andy ip_hdr(skb)->check = 0; 3174c09eed9SJim Baxter *(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) = 0; 3184c09eed9SJim Baxter 3194c09eed9SJim Baxter return 0; 3204c09eed9SJim Baxter } 3214c09eed9SJim Baxter 322c4bc44c6SKevin Hao static struct bufdesc * 3234d494cdcSFugang Duan fec_enet_txq_submit_frag_skb(struct fec_enet_priv_tx_q *txq, 3244d494cdcSFugang Duan struct sk_buff *skb, 3254d494cdcSFugang Duan struct net_device *ndev) 3266e909283SNimrod Andy { 3276e909283SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 3287355f276STroy Kisky struct bufdesc *bdp = txq->bd.cur; 3296e909283SNimrod Andy struct bufdesc_ex *ebdp; 3306e909283SNimrod Andy int nr_frags = skb_shinfo(skb)->nr_frags; 3316e909283SNimrod Andy int frag, frag_len; 3326e909283SNimrod Andy unsigned short status; 3336e909283SNimrod Andy unsigned int estatus = 0; 3346e909283SNimrod Andy skb_frag_t *this_frag; 3356e909283SNimrod Andy unsigned int index; 3366e909283SNimrod Andy void *bufaddr; 337d6bf3143SRussell King dma_addr_t addr; 3386e909283SNimrod Andy int i; 3396e909283SNimrod Andy 3406e909283SNimrod Andy for (frag = 0; frag < nr_frags; frag++) { 3416e909283SNimrod Andy this_frag = &skb_shinfo(skb)->frags[frag]; 3427355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 3436e909283SNimrod Andy ebdp = (struct bufdesc_ex *)bdp; 3446e909283SNimrod Andy 3455cfa3039SJohannes Berg status = fec16_to_cpu(bdp->cbd_sc); 3466e909283SNimrod Andy status &= ~BD_ENET_TX_STATS; 3476e909283SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 3486e909283SNimrod Andy frag_len = skb_shinfo(skb)->frags[frag].size; 3496e909283SNimrod Andy 3506e909283SNimrod Andy /* Handle the last BD specially */ 3516e909283SNimrod Andy if (frag == nr_frags - 1) { 3526e909283SNimrod Andy status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); 3536e909283SNimrod Andy if (fep->bufdesc_ex) { 3546e909283SNimrod Andy estatus |= BD_ENET_TX_INT; 3556e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & 3566e909283SNimrod Andy SKBTX_HW_TSTAMP && fep->hwts_tx_en)) 3576e909283SNimrod Andy estatus |= BD_ENET_TX_TS; 3586e909283SNimrod Andy } 3596e909283SNimrod Andy } 3606e909283SNimrod Andy 3616e909283SNimrod Andy if (fep->bufdesc_ex) { 3626b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 363*53bb20d1STroy Kisky estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 3646e909283SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 3656e909283SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 3666e909283SNimrod Andy ebdp->cbd_bdu = 0; 3675cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(estatus); 3686e909283SNimrod Andy } 3696e909283SNimrod Andy 3706e909283SNimrod Andy bufaddr = page_address(this_frag->page.p) + this_frag->page_offset; 3716e909283SNimrod Andy 3727355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 37341ef84ceSFugang Duan if (((unsigned long) bufaddr) & fep->tx_align || 3746b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 3754d494cdcSFugang Duan memcpy(txq->tx_bounce[index], bufaddr, frag_len); 3764d494cdcSFugang Duan bufaddr = txq->tx_bounce[index]; 3776e909283SNimrod Andy 3786b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 3796e909283SNimrod Andy swap_buffer(bufaddr, frag_len); 3806e909283SNimrod Andy } 3816e909283SNimrod Andy 382d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, bufaddr, frag_len, 383d6bf3143SRussell King DMA_TO_DEVICE); 384d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 3856e909283SNimrod Andy dev_kfree_skb_any(skb); 3866e909283SNimrod Andy if (net_ratelimit()) 3876e909283SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 3886e909283SNimrod Andy goto dma_mapping_error; 3896e909283SNimrod Andy } 3906e909283SNimrod Andy 3915cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(addr); 3925cfa3039SJohannes Berg bdp->cbd_datlen = cpu_to_fec16(frag_len); 3935cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(status); 3946e909283SNimrod Andy } 3956e909283SNimrod Andy 396c4bc44c6SKevin Hao return bdp; 3976e909283SNimrod Andy dma_mapping_error: 3987355f276STroy Kisky bdp = txq->bd.cur; 3996e909283SNimrod Andy for (i = 0; i < frag; i++) { 4007355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 4015cfa3039SJohannes Berg dma_unmap_single(&fep->pdev->dev, fec32_to_cpu(bdp->cbd_bufaddr), 4025cfa3039SJohannes Berg fec16_to_cpu(bdp->cbd_datlen), DMA_TO_DEVICE); 4036e909283SNimrod Andy } 404c4bc44c6SKevin Hao return ERR_PTR(-ENOMEM); 4056e909283SNimrod Andy } 4066e909283SNimrod Andy 4074d494cdcSFugang Duan static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq, 4084d494cdcSFugang Duan struct sk_buff *skb, struct net_device *ndev) 4096e909283SNimrod Andy { 4106e909283SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 4116e909283SNimrod Andy int nr_frags = skb_shinfo(skb)->nr_frags; 4126e909283SNimrod Andy struct bufdesc *bdp, *last_bdp; 4136e909283SNimrod Andy void *bufaddr; 414d6bf3143SRussell King dma_addr_t addr; 4156e909283SNimrod Andy unsigned short status; 4166e909283SNimrod Andy unsigned short buflen; 4176e909283SNimrod Andy unsigned int estatus = 0; 4186e909283SNimrod Andy unsigned int index; 41979f33912SNimrod Andy int entries_free; 4206e909283SNimrod Andy 4217355f276STroy Kisky entries_free = fec_enet_get_free_txdesc_num(txq); 42279f33912SNimrod Andy if (entries_free < MAX_SKB_FRAGS + 1) { 42379f33912SNimrod Andy dev_kfree_skb_any(skb); 42479f33912SNimrod Andy if (net_ratelimit()) 42579f33912SNimrod Andy netdev_err(ndev, "NOT enough BD for SG!\n"); 42679f33912SNimrod Andy return NETDEV_TX_OK; 42779f33912SNimrod Andy } 42879f33912SNimrod Andy 4296e909283SNimrod Andy /* Protocol checksum off-load for TCP and UDP. */ 4306e909283SNimrod Andy if (fec_enet_clear_csum(skb, ndev)) { 4316e909283SNimrod Andy dev_kfree_skb_any(skb); 4326e909283SNimrod Andy return NETDEV_TX_OK; 4336e909283SNimrod Andy } 4346e909283SNimrod Andy 4356e909283SNimrod Andy /* Fill in a Tx ring entry */ 4367355f276STroy Kisky bdp = txq->bd.cur; 437c4bc44c6SKevin Hao last_bdp = bdp; 4385cfa3039SJohannes Berg status = fec16_to_cpu(bdp->cbd_sc); 4396e909283SNimrod Andy status &= ~BD_ENET_TX_STATS; 4406e909283SNimrod Andy 4416e909283SNimrod Andy /* Set buffer length and buffer pointer */ 4426e909283SNimrod Andy bufaddr = skb->data; 4436e909283SNimrod Andy buflen = skb_headlen(skb); 4446e909283SNimrod Andy 4457355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 44641ef84ceSFugang Duan if (((unsigned long) bufaddr) & fep->tx_align || 4476b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 4484d494cdcSFugang Duan memcpy(txq->tx_bounce[index], skb->data, buflen); 4494d494cdcSFugang Duan bufaddr = txq->tx_bounce[index]; 4506e909283SNimrod Andy 4516b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 4526e909283SNimrod Andy swap_buffer(bufaddr, buflen); 4536e909283SNimrod Andy } 4546e909283SNimrod Andy 455d6bf3143SRussell King /* Push the data cache so the CPM does not get stale memory data. */ 456d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, bufaddr, buflen, DMA_TO_DEVICE); 457d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 4586e909283SNimrod Andy dev_kfree_skb_any(skb); 4596e909283SNimrod Andy if (net_ratelimit()) 4606e909283SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 4616e909283SNimrod Andy return NETDEV_TX_OK; 4626e909283SNimrod Andy } 4636e909283SNimrod Andy 4646e909283SNimrod Andy if (nr_frags) { 465c4bc44c6SKevin Hao last_bdp = fec_enet_txq_submit_frag_skb(txq, skb, ndev); 466c4bc44c6SKevin Hao if (IS_ERR(last_bdp)) 467c4bc44c6SKevin Hao return NETDEV_TX_OK; 4686e909283SNimrod Andy } else { 4696e909283SNimrod Andy status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); 4706e909283SNimrod Andy if (fep->bufdesc_ex) { 4716e909283SNimrod Andy estatus = BD_ENET_TX_INT; 4726e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & 4736e909283SNimrod Andy SKBTX_HW_TSTAMP && fep->hwts_tx_en)) 4746e909283SNimrod Andy estatus |= BD_ENET_TX_TS; 4756e909283SNimrod Andy } 4766e909283SNimrod Andy } 4776e909283SNimrod Andy 4786e909283SNimrod Andy if (fep->bufdesc_ex) { 4796e909283SNimrod Andy 4806e909283SNimrod Andy struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 4816e909283SNimrod Andy 4826e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && 4836e909283SNimrod Andy fep->hwts_tx_en)) 4846e909283SNimrod Andy skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; 4856e909283SNimrod Andy 4866b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 487*53bb20d1STroy Kisky estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 488befe8213SNimrod Andy 4896e909283SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 4906e909283SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 4916e909283SNimrod Andy 4926e909283SNimrod Andy ebdp->cbd_bdu = 0; 4935cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(estatus); 4946e909283SNimrod Andy } 4956e909283SNimrod Andy 4967355f276STroy Kisky index = fec_enet_get_bd_index(last_bdp, &txq->bd); 4976e909283SNimrod Andy /* Save skb pointer */ 4984d494cdcSFugang Duan txq->tx_skbuff[index] = skb; 4996e909283SNimrod Andy 5005cfa3039SJohannes Berg bdp->cbd_datlen = cpu_to_fec16(buflen); 5015cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(addr); 5026e909283SNimrod Andy 5036e909283SNimrod Andy /* Send it on its way. Tell FEC it's ready, interrupt when done, 5046e909283SNimrod Andy * it's the last BD of the frame, and to put the CRC on the end. 5056e909283SNimrod Andy */ 5066e909283SNimrod Andy status |= (BD_ENET_TX_READY | BD_ENET_TX_TC); 5075cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(status); 5086e909283SNimrod Andy 509793fc096SFrank Li /* If this was the last BD in the ring, start at the beginning again. */ 5107355f276STroy Kisky bdp = fec_enet_get_nextdesc(last_bdp, &txq->bd); 511793fc096SFrank Li 5127a2a8451SEric Dumazet skb_tx_timestamp(skb); 5137a2a8451SEric Dumazet 514c4bc44c6SKevin Hao /* Make sure the update to bdp and tx_skbuff are performed before 5157355f276STroy Kisky * txq->bd.cur. 516c4bc44c6SKevin Hao */ 517c4bc44c6SKevin Hao wmb(); 5187355f276STroy Kisky txq->bd.cur = bdp; 519793fc096SFrank Li 520793fc096SFrank Li /* Trigger transmission start */ 521*53bb20d1STroy Kisky writel(0, txq->bd.reg_desc_active); 522793fc096SFrank Li 5236e909283SNimrod Andy return 0; 524793fc096SFrank Li } 525793fc096SFrank Li 52679f33912SNimrod Andy static int 5274d494cdcSFugang Duan fec_enet_txq_put_data_tso(struct fec_enet_priv_tx_q *txq, struct sk_buff *skb, 5284d494cdcSFugang Duan struct net_device *ndev, 52979f33912SNimrod Andy struct bufdesc *bdp, int index, char *data, 53079f33912SNimrod Andy int size, bool last_tcp, bool is_last) 53179f33912SNimrod Andy { 53279f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 53361cd2ebbSFabian Frederick struct bufdesc_ex *ebdp = container_of(bdp, struct bufdesc_ex, desc); 53479f33912SNimrod Andy unsigned short status; 53579f33912SNimrod Andy unsigned int estatus = 0; 536d6bf3143SRussell King dma_addr_t addr; 53779f33912SNimrod Andy 5385cfa3039SJohannes Berg status = fec16_to_cpu(bdp->cbd_sc); 53979f33912SNimrod Andy status &= ~BD_ENET_TX_STATS; 54079f33912SNimrod Andy 54179f33912SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 54279f33912SNimrod Andy 54341ef84ceSFugang Duan if (((unsigned long) data) & fep->tx_align || 5446b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 5454d494cdcSFugang Duan memcpy(txq->tx_bounce[index], data, size); 5464d494cdcSFugang Duan data = txq->tx_bounce[index]; 54779f33912SNimrod Andy 5486b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 54979f33912SNimrod Andy swap_buffer(data, size); 55079f33912SNimrod Andy } 55179f33912SNimrod Andy 552d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, data, size, DMA_TO_DEVICE); 553d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 55479f33912SNimrod Andy dev_kfree_skb_any(skb); 55579f33912SNimrod Andy if (net_ratelimit()) 55679f33912SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 55779f33912SNimrod Andy return NETDEV_TX_BUSY; 55879f33912SNimrod Andy } 55979f33912SNimrod Andy 5605cfa3039SJohannes Berg bdp->cbd_datlen = cpu_to_fec16(size); 5615cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(addr); 562d6bf3143SRussell King 56379f33912SNimrod Andy if (fep->bufdesc_ex) { 5646b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 565*53bb20d1STroy Kisky estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 56679f33912SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 56779f33912SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 56879f33912SNimrod Andy ebdp->cbd_bdu = 0; 5695cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(estatus); 57079f33912SNimrod Andy } 57179f33912SNimrod Andy 57279f33912SNimrod Andy /* Handle the last BD specially */ 57379f33912SNimrod Andy if (last_tcp) 57479f33912SNimrod Andy status |= (BD_ENET_TX_LAST | BD_ENET_TX_TC); 57579f33912SNimrod Andy if (is_last) { 57679f33912SNimrod Andy status |= BD_ENET_TX_INTR; 57779f33912SNimrod Andy if (fep->bufdesc_ex) 5785cfa3039SJohannes Berg ebdp->cbd_esc |= cpu_to_fec32(BD_ENET_TX_INT); 57979f33912SNimrod Andy } 58079f33912SNimrod Andy 5815cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(status); 58279f33912SNimrod Andy 58379f33912SNimrod Andy return 0; 58479f33912SNimrod Andy } 58579f33912SNimrod Andy 58679f33912SNimrod Andy static int 5874d494cdcSFugang Duan fec_enet_txq_put_hdr_tso(struct fec_enet_priv_tx_q *txq, 5884d494cdcSFugang Duan struct sk_buff *skb, struct net_device *ndev, 58979f33912SNimrod Andy struct bufdesc *bdp, int index) 59079f33912SNimrod Andy { 59179f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 59279f33912SNimrod Andy int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); 59361cd2ebbSFabian Frederick struct bufdesc_ex *ebdp = container_of(bdp, struct bufdesc_ex, desc); 59479f33912SNimrod Andy void *bufaddr; 59579f33912SNimrod Andy unsigned long dmabuf; 59679f33912SNimrod Andy unsigned short status; 59779f33912SNimrod Andy unsigned int estatus = 0; 59879f33912SNimrod Andy 5995cfa3039SJohannes Berg status = fec16_to_cpu(bdp->cbd_sc); 60079f33912SNimrod Andy status &= ~BD_ENET_TX_STATS; 60179f33912SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 60279f33912SNimrod Andy 6034d494cdcSFugang Duan bufaddr = txq->tso_hdrs + index * TSO_HEADER_SIZE; 6044d494cdcSFugang Duan dmabuf = txq->tso_hdrs_dma + index * TSO_HEADER_SIZE; 60541ef84ceSFugang Duan if (((unsigned long)bufaddr) & fep->tx_align || 6066b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 6074d494cdcSFugang Duan memcpy(txq->tx_bounce[index], skb->data, hdr_len); 6084d494cdcSFugang Duan bufaddr = txq->tx_bounce[index]; 60979f33912SNimrod Andy 6106b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 61179f33912SNimrod Andy swap_buffer(bufaddr, hdr_len); 61279f33912SNimrod Andy 61379f33912SNimrod Andy dmabuf = dma_map_single(&fep->pdev->dev, bufaddr, 61479f33912SNimrod Andy hdr_len, DMA_TO_DEVICE); 61579f33912SNimrod Andy if (dma_mapping_error(&fep->pdev->dev, dmabuf)) { 61679f33912SNimrod Andy dev_kfree_skb_any(skb); 61779f33912SNimrod Andy if (net_ratelimit()) 61879f33912SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 61979f33912SNimrod Andy return NETDEV_TX_BUSY; 62079f33912SNimrod Andy } 62179f33912SNimrod Andy } 62279f33912SNimrod Andy 6235cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(dmabuf); 6245cfa3039SJohannes Berg bdp->cbd_datlen = cpu_to_fec16(hdr_len); 62579f33912SNimrod Andy 62679f33912SNimrod Andy if (fep->bufdesc_ex) { 6276b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 628*53bb20d1STroy Kisky estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 62979f33912SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 63079f33912SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 63179f33912SNimrod Andy ebdp->cbd_bdu = 0; 6325cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(estatus); 63379f33912SNimrod Andy } 63479f33912SNimrod Andy 6355cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(status); 63679f33912SNimrod Andy 63779f33912SNimrod Andy return 0; 63879f33912SNimrod Andy } 63979f33912SNimrod Andy 6404d494cdcSFugang Duan static int fec_enet_txq_submit_tso(struct fec_enet_priv_tx_q *txq, 6414d494cdcSFugang Duan struct sk_buff *skb, 6424d494cdcSFugang Duan struct net_device *ndev) 64379f33912SNimrod Andy { 64479f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 64579f33912SNimrod Andy int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); 64679f33912SNimrod Andy int total_len, data_left; 6477355f276STroy Kisky struct bufdesc *bdp = txq->bd.cur; 64879f33912SNimrod Andy struct tso_t tso; 64979f33912SNimrod Andy unsigned int index = 0; 65079f33912SNimrod Andy int ret; 65179f33912SNimrod Andy 6527355f276STroy Kisky if (tso_count_descs(skb) >= fec_enet_get_free_txdesc_num(txq)) { 65379f33912SNimrod Andy dev_kfree_skb_any(skb); 65479f33912SNimrod Andy if (net_ratelimit()) 65579f33912SNimrod Andy netdev_err(ndev, "NOT enough BD for TSO!\n"); 65679f33912SNimrod Andy return NETDEV_TX_OK; 65779f33912SNimrod Andy } 65879f33912SNimrod Andy 65979f33912SNimrod Andy /* Protocol checksum off-load for TCP and UDP. */ 66079f33912SNimrod Andy if (fec_enet_clear_csum(skb, ndev)) { 66179f33912SNimrod Andy dev_kfree_skb_any(skb); 66279f33912SNimrod Andy return NETDEV_TX_OK; 66379f33912SNimrod Andy } 66479f33912SNimrod Andy 66579f33912SNimrod Andy /* Initialize the TSO handler, and prepare the first payload */ 66679f33912SNimrod Andy tso_start(skb, &tso); 66779f33912SNimrod Andy 66879f33912SNimrod Andy total_len = skb->len - hdr_len; 66979f33912SNimrod Andy while (total_len > 0) { 67079f33912SNimrod Andy char *hdr; 67179f33912SNimrod Andy 6727355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 67379f33912SNimrod Andy data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len); 67479f33912SNimrod Andy total_len -= data_left; 67579f33912SNimrod Andy 67679f33912SNimrod Andy /* prepare packet headers: MAC + IP + TCP */ 6774d494cdcSFugang Duan hdr = txq->tso_hdrs + index * TSO_HEADER_SIZE; 67879f33912SNimrod Andy tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0); 6794d494cdcSFugang Duan ret = fec_enet_txq_put_hdr_tso(txq, skb, ndev, bdp, index); 68079f33912SNimrod Andy if (ret) 68179f33912SNimrod Andy goto err_release; 68279f33912SNimrod Andy 68379f33912SNimrod Andy while (data_left > 0) { 68479f33912SNimrod Andy int size; 68579f33912SNimrod Andy 68679f33912SNimrod Andy size = min_t(int, tso.size, data_left); 6877355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 6887355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 6894d494cdcSFugang Duan ret = fec_enet_txq_put_data_tso(txq, skb, ndev, 6904d494cdcSFugang Duan bdp, index, 6914d494cdcSFugang Duan tso.data, size, 6924d494cdcSFugang Duan size == data_left, 69379f33912SNimrod Andy total_len == 0); 69479f33912SNimrod Andy if (ret) 69579f33912SNimrod Andy goto err_release; 69679f33912SNimrod Andy 69779f33912SNimrod Andy data_left -= size; 69879f33912SNimrod Andy tso_build_data(skb, &tso, size); 69979f33912SNimrod Andy } 70079f33912SNimrod Andy 7017355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 70279f33912SNimrod Andy } 70379f33912SNimrod Andy 70479f33912SNimrod Andy /* Save skb pointer */ 7054d494cdcSFugang Duan txq->tx_skbuff[index] = skb; 70679f33912SNimrod Andy 70779f33912SNimrod Andy skb_tx_timestamp(skb); 7087355f276STroy Kisky txq->bd.cur = bdp; 70979f33912SNimrod Andy 71079f33912SNimrod Andy /* Trigger transmission start */ 7116b7e4008SLothar Waßmann if (!(fep->quirks & FEC_QUIRK_ERR007885) || 712*53bb20d1STroy Kisky !readl(txq->bd.reg_desc_active) || 713*53bb20d1STroy Kisky !readl(txq->bd.reg_desc_active) || 714*53bb20d1STroy Kisky !readl(txq->bd.reg_desc_active) || 715*53bb20d1STroy Kisky !readl(txq->bd.reg_desc_active)) 716*53bb20d1STroy Kisky writel(0, txq->bd.reg_desc_active); 71779f33912SNimrod Andy 71879f33912SNimrod Andy return 0; 71979f33912SNimrod Andy 72079f33912SNimrod Andy err_release: 72179f33912SNimrod Andy /* TODO: Release all used data descriptors for TSO */ 72279f33912SNimrod Andy return ret; 72379f33912SNimrod Andy } 72479f33912SNimrod Andy 72561a4427bSNimrod Andy static netdev_tx_t 72661a4427bSNimrod Andy fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) 72761a4427bSNimrod Andy { 72861a4427bSNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 7296e909283SNimrod Andy int entries_free; 7304d494cdcSFugang Duan unsigned short queue; 7314d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 7324d494cdcSFugang Duan struct netdev_queue *nq; 73361a4427bSNimrod Andy int ret; 73461a4427bSNimrod Andy 7354d494cdcSFugang Duan queue = skb_get_queue_mapping(skb); 7364d494cdcSFugang Duan txq = fep->tx_queue[queue]; 7374d494cdcSFugang Duan nq = netdev_get_tx_queue(ndev, queue); 7384d494cdcSFugang Duan 73979f33912SNimrod Andy if (skb_is_gso(skb)) 7404d494cdcSFugang Duan ret = fec_enet_txq_submit_tso(txq, skb, ndev); 74179f33912SNimrod Andy else 7424d494cdcSFugang Duan ret = fec_enet_txq_submit_skb(txq, skb, ndev); 7436e909283SNimrod Andy if (ret) 7446e909283SNimrod Andy return ret; 74561a4427bSNimrod Andy 7467355f276STroy Kisky entries_free = fec_enet_get_free_txdesc_num(txq); 7474d494cdcSFugang Duan if (entries_free <= txq->tx_stop_threshold) 7484d494cdcSFugang Duan netif_tx_stop_queue(nq); 74961a4427bSNimrod Andy 75061a4427bSNimrod Andy return NETDEV_TX_OK; 75161a4427bSNimrod Andy } 75261a4427bSNimrod Andy 753a210576cSDavid S. Miller /* Init RX & TX buffer descriptors 754a210576cSDavid S. Miller */ 755a210576cSDavid S. Miller static void fec_enet_bd_init(struct net_device *dev) 756a210576cSDavid S. Miller { 757a210576cSDavid S. Miller struct fec_enet_private *fep = netdev_priv(dev); 7584d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 7594d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 760a210576cSDavid S. Miller struct bufdesc *bdp; 761a210576cSDavid S. Miller unsigned int i; 76259d0f746SFrank Li unsigned int q; 763a210576cSDavid S. Miller 76459d0f746SFrank Li for (q = 0; q < fep->num_rx_queues; q++) { 765a210576cSDavid S. Miller /* Initialize the receive buffer descriptors. */ 76659d0f746SFrank Li rxq = fep->rx_queue[q]; 7677355f276STroy Kisky bdp = rxq->bd.base; 7684d494cdcSFugang Duan 7697355f276STroy Kisky for (i = 0; i < rxq->bd.ring_size; i++) { 770a210576cSDavid S. Miller 771a210576cSDavid S. Miller /* Initialize the BD for every fragment in the page. */ 772a210576cSDavid S. Miller if (bdp->cbd_bufaddr) 7735cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); 774a210576cSDavid S. Miller else 7755cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(0); 7767355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 777a210576cSDavid S. Miller } 778a210576cSDavid S. Miller 779a210576cSDavid S. Miller /* Set the last buffer to wrap */ 7807355f276STroy Kisky bdp = fec_enet_get_prevdesc(bdp, &rxq->bd); 7815cfa3039SJohannes Berg bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 782a210576cSDavid S. Miller 7837355f276STroy Kisky rxq->bd.cur = rxq->bd.base; 78459d0f746SFrank Li } 785a210576cSDavid S. Miller 78659d0f746SFrank Li for (q = 0; q < fep->num_tx_queues; q++) { 787a210576cSDavid S. Miller /* ...and the same for transmit */ 78859d0f746SFrank Li txq = fep->tx_queue[q]; 7897355f276STroy Kisky bdp = txq->bd.base; 7907355f276STroy Kisky txq->bd.cur = bdp; 791a210576cSDavid S. Miller 7927355f276STroy Kisky for (i = 0; i < txq->bd.ring_size; i++) { 793a210576cSDavid S. Miller /* Initialize the BD for every fragment in the page. */ 7945cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(0); 7954d494cdcSFugang Duan if (txq->tx_skbuff[i]) { 7964d494cdcSFugang Duan dev_kfree_skb_any(txq->tx_skbuff[i]); 7974d494cdcSFugang Duan txq->tx_skbuff[i] = NULL; 798a210576cSDavid S. Miller } 7995cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(0); 8007355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 801a210576cSDavid S. Miller } 802a210576cSDavid S. Miller 803a210576cSDavid S. Miller /* Set the last buffer to wrap */ 8047355f276STroy Kisky bdp = fec_enet_get_prevdesc(bdp, &txq->bd); 8055cfa3039SJohannes Berg bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 8064d494cdcSFugang Duan txq->dirty_tx = bdp; 807a210576cSDavid S. Miller } 80859d0f746SFrank Li } 80959d0f746SFrank Li 810ce99d0d3SFrank Li static void fec_enet_active_rxring(struct net_device *ndev) 811ce99d0d3SFrank Li { 812ce99d0d3SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 813ce99d0d3SFrank Li int i; 814ce99d0d3SFrank Li 815ce99d0d3SFrank Li for (i = 0; i < fep->num_rx_queues; i++) 816*53bb20d1STroy Kisky writel(0, fep->rx_queue[i]->bd.reg_desc_active); 817ce99d0d3SFrank Li } 818ce99d0d3SFrank Li 81959d0f746SFrank Li static void fec_enet_enable_ring(struct net_device *ndev) 82059d0f746SFrank Li { 82159d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 82259d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 82359d0f746SFrank Li struct fec_enet_priv_rx_q *rxq; 82459d0f746SFrank Li int i; 82559d0f746SFrank Li 82659d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) { 82759d0f746SFrank Li rxq = fep->rx_queue[i]; 8287355f276STroy Kisky writel(rxq->bd.dma, fep->hwp + FEC_R_DES_START(i)); 829d543a762SNimrod Andy writel(PKT_MAXBLR_SIZE, fep->hwp + FEC_R_BUFF_SIZE(i)); 83059d0f746SFrank Li 83159d0f746SFrank Li /* enable DMA1/2 */ 83259d0f746SFrank Li if (i) 83359d0f746SFrank Li writel(RCMR_MATCHEN | RCMR_CMP(i), 83459d0f746SFrank Li fep->hwp + FEC_RCMR(i)); 83559d0f746SFrank Li } 83659d0f746SFrank Li 83759d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 83859d0f746SFrank Li txq = fep->tx_queue[i]; 8397355f276STroy Kisky writel(txq->bd.dma, fep->hwp + FEC_X_DES_START(i)); 84059d0f746SFrank Li 84159d0f746SFrank Li /* enable DMA1/2 */ 84259d0f746SFrank Li if (i) 84359d0f746SFrank Li writel(DMA_CLASS_EN | IDLE_SLOPE(i), 84459d0f746SFrank Li fep->hwp + FEC_DMA_CFG(i)); 84559d0f746SFrank Li } 84659d0f746SFrank Li } 84759d0f746SFrank Li 84859d0f746SFrank Li static void fec_enet_reset_skb(struct net_device *ndev) 84959d0f746SFrank Li { 85059d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 85159d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 85259d0f746SFrank Li int i, j; 85359d0f746SFrank Li 85459d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 85559d0f746SFrank Li txq = fep->tx_queue[i]; 85659d0f746SFrank Li 8577355f276STroy Kisky for (j = 0; j < txq->bd.ring_size; j++) { 85859d0f746SFrank Li if (txq->tx_skbuff[j]) { 85959d0f746SFrank Li dev_kfree_skb_any(txq->tx_skbuff[j]); 86059d0f746SFrank Li txq->tx_skbuff[j] = NULL; 86159d0f746SFrank Li } 86259d0f746SFrank Li } 86359d0f746SFrank Li } 86459d0f746SFrank Li } 865a210576cSDavid S. Miller 866dbc64a8eSRussell King /* 867dbc64a8eSRussell King * This function is called to start or restart the FEC during a link 868dbc64a8eSRussell King * change, transmit timeout, or to reconfigure the FEC. The network 869dbc64a8eSRussell King * packet processing for this device must be stopped before this call. 870793fc096SFrank Li */ 871793fc096SFrank Li static void 872ef83337dSRussell King fec_restart(struct net_device *ndev) 873793fc096SFrank Li { 874793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 8754c09eed9SJim Baxter u32 val; 876793fc096SFrank Li u32 temp_mac[2]; 877793fc096SFrank Li u32 rcntl = OPT_FRAME_SIZE | 0x04; 878793fc096SFrank Li u32 ecntl = 0x2; /* ETHEREN */ 879793fc096SFrank Li 880106c314cSFugang Duan /* Whack a reset. We should wait for this. 881106c314cSFugang Duan * For i.MX6SX SOC, enet use AXI bus, we use disable MAC 882106c314cSFugang Duan * instead of reset MAC itself. 883106c314cSFugang Duan */ 8846b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) { 885106c314cSFugang Duan writel(0, fep->hwp + FEC_ECNTRL); 886106c314cSFugang Duan } else { 887793fc096SFrank Li writel(1, fep->hwp + FEC_ECNTRL); 888793fc096SFrank Li udelay(10); 889106c314cSFugang Duan } 890793fc096SFrank Li 891793fc096SFrank Li /* 892793fc096SFrank Li * enet-mac reset will reset mac address registers too, 893793fc096SFrank Li * so need to reconfigure it. 894793fc096SFrank Li */ 8956b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_ENET_MAC) { 896793fc096SFrank Li memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN); 8975cfa3039SJohannes Berg writel((__force u32)cpu_to_be32(temp_mac[0]), 8985cfa3039SJohannes Berg fep->hwp + FEC_ADDR_LOW); 8995cfa3039SJohannes Berg writel((__force u32)cpu_to_be32(temp_mac[1]), 9005cfa3039SJohannes Berg fep->hwp + FEC_ADDR_HIGH); 901793fc096SFrank Li } 902793fc096SFrank Li 903793fc096SFrank Li /* Clear any outstanding interrupt. */ 904e17f7fecSNimrod Andy writel(0xffffffff, fep->hwp + FEC_IEVENT); 905793fc096SFrank Li 906a210576cSDavid S. Miller fec_enet_bd_init(ndev); 907a210576cSDavid S. Miller 90859d0f746SFrank Li fec_enet_enable_ring(ndev); 909793fc096SFrank Li 91059d0f746SFrank Li /* Reset tx SKB buffers. */ 91159d0f746SFrank Li fec_enet_reset_skb(ndev); 912793fc096SFrank Li 913793fc096SFrank Li /* Enable MII mode */ 914ef83337dSRussell King if (fep->full_duplex == DUPLEX_FULL) { 915793fc096SFrank Li /* FD enable */ 916793fc096SFrank Li writel(0x04, fep->hwp + FEC_X_CNTRL); 917793fc096SFrank Li } else { 918793fc096SFrank Li /* No Rcv on Xmit */ 919793fc096SFrank Li rcntl |= 0x02; 920793fc096SFrank Li writel(0x0, fep->hwp + FEC_X_CNTRL); 921793fc096SFrank Li } 922793fc096SFrank Li 923793fc096SFrank Li /* Set MII speed */ 924793fc096SFrank Li writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 925793fc096SFrank Li 926d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 92718803495SGreg Ungerer if (fep->quirks & FEC_QUIRK_HAS_RACC) { 9284c09eed9SJim Baxter /* set RX checksum */ 9294c09eed9SJim Baxter val = readl(fep->hwp + FEC_RACC); 9304c09eed9SJim Baxter if (fep->csum_flags & FLAG_RX_CSUM_ENABLED) 9314c09eed9SJim Baxter val |= FEC_RACC_OPTIONS; 9324c09eed9SJim Baxter else 9334c09eed9SJim Baxter val &= ~FEC_RACC_OPTIONS; 9344c09eed9SJim Baxter writel(val, fep->hwp + FEC_RACC); 93518803495SGreg Ungerer } 93655cd48c8STroy Kisky writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_FTRL); 937d1391930SGuenter Roeck #endif 9384c09eed9SJim Baxter 939793fc096SFrank Li /* 940793fc096SFrank Li * The phy interface and speed need to get configured 941793fc096SFrank Li * differently on enet-mac. 942793fc096SFrank Li */ 9436b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_ENET_MAC) { 944793fc096SFrank Li /* Enable flow control and length check */ 945793fc096SFrank Li rcntl |= 0x40000000 | 0x00000020; 946793fc096SFrank Li 947793fc096SFrank Li /* RGMII, RMII or MII */ 948e813bb2bSMarkus Pargmann if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII || 949e813bb2bSMarkus Pargmann fep->phy_interface == PHY_INTERFACE_MODE_RGMII_ID || 950e813bb2bSMarkus Pargmann fep->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID || 951e813bb2bSMarkus Pargmann fep->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID) 952793fc096SFrank Li rcntl |= (1 << 6); 953793fc096SFrank Li else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) 954793fc096SFrank Li rcntl |= (1 << 8); 955793fc096SFrank Li else 956793fc096SFrank Li rcntl &= ~(1 << 8); 957793fc096SFrank Li 958793fc096SFrank Li /* 1G, 100M or 10M */ 959793fc096SFrank Li if (fep->phy_dev) { 960793fc096SFrank Li if (fep->phy_dev->speed == SPEED_1000) 961793fc096SFrank Li ecntl |= (1 << 5); 962793fc096SFrank Li else if (fep->phy_dev->speed == SPEED_100) 963793fc096SFrank Li rcntl &= ~(1 << 9); 964793fc096SFrank Li else 965793fc096SFrank Li rcntl |= (1 << 9); 966793fc096SFrank Li } 967793fc096SFrank Li } else { 968793fc096SFrank Li #ifdef FEC_MIIGSK_ENR 9696b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_USE_GASKET) { 970793fc096SFrank Li u32 cfgr; 971793fc096SFrank Li /* disable the gasket and wait */ 972793fc096SFrank Li writel(0, fep->hwp + FEC_MIIGSK_ENR); 973793fc096SFrank Li while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4) 974793fc096SFrank Li udelay(1); 975793fc096SFrank Li 976793fc096SFrank Li /* 977793fc096SFrank Li * configure the gasket: 978793fc096SFrank Li * RMII, 50 MHz, no loopback, no echo 979793fc096SFrank Li * MII, 25 MHz, no loopback, no echo 980793fc096SFrank Li */ 981793fc096SFrank Li cfgr = (fep->phy_interface == PHY_INTERFACE_MODE_RMII) 982793fc096SFrank Li ? BM_MIIGSK_CFGR_RMII : BM_MIIGSK_CFGR_MII; 983793fc096SFrank Li if (fep->phy_dev && fep->phy_dev->speed == SPEED_10) 984793fc096SFrank Li cfgr |= BM_MIIGSK_CFGR_FRCONT_10M; 985793fc096SFrank Li writel(cfgr, fep->hwp + FEC_MIIGSK_CFGR); 986793fc096SFrank Li 987793fc096SFrank Li /* re-enable the gasket */ 988793fc096SFrank Li writel(2, fep->hwp + FEC_MIIGSK_ENR); 989793fc096SFrank Li } 990793fc096SFrank Li #endif 991793fc096SFrank Li } 992793fc096SFrank Li 993d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 994793fc096SFrank Li /* enable pause frame*/ 995793fc096SFrank Li if ((fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) || 996793fc096SFrank Li ((fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) && 997793fc096SFrank Li fep->phy_dev && fep->phy_dev->pause)) { 998793fc096SFrank Li rcntl |= FEC_ENET_FCE; 999793fc096SFrank Li 10004c09eed9SJim Baxter /* set FIFO threshold parameter to reduce overrun */ 1001793fc096SFrank Li writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM); 1002793fc096SFrank Li writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL); 1003793fc096SFrank Li writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM); 1004793fc096SFrank Li writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL); 1005793fc096SFrank Li 1006793fc096SFrank Li /* OPD */ 1007793fc096SFrank Li writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD); 1008793fc096SFrank Li } else { 1009793fc096SFrank Li rcntl &= ~FEC_ENET_FCE; 1010793fc096SFrank Li } 1011d1391930SGuenter Roeck #endif /* !defined(CONFIG_M5272) */ 1012793fc096SFrank Li 1013793fc096SFrank Li writel(rcntl, fep->hwp + FEC_R_CNTRL); 1014793fc096SFrank Li 101584fe6182SStefan Wahren /* Setup multicast filter. */ 101684fe6182SStefan Wahren set_multicast_list(ndev); 101784fe6182SStefan Wahren #ifndef CONFIG_M5272 101884fe6182SStefan Wahren writel(0, fep->hwp + FEC_HASH_TABLE_HIGH); 101984fe6182SStefan Wahren writel(0, fep->hwp + FEC_HASH_TABLE_LOW); 102084fe6182SStefan Wahren #endif 102184fe6182SStefan Wahren 10226b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_ENET_MAC) { 1023793fc096SFrank Li /* enable ENET endian swap */ 1024793fc096SFrank Li ecntl |= (1 << 8); 1025793fc096SFrank Li /* enable ENET store and forward mode */ 1026793fc096SFrank Li writel(1 << 8, fep->hwp + FEC_X_WMRK); 1027793fc096SFrank Li } 1028793fc096SFrank Li 1029793fc096SFrank Li if (fep->bufdesc_ex) 1030793fc096SFrank Li ecntl |= (1 << 4); 1031793fc096SFrank Li 103238ae92dcSChris Healy #ifndef CONFIG_M5272 1033b9eef55cSJim Baxter /* Enable the MIB statistic event counters */ 1034b9eef55cSJim Baxter writel(0 << 31, fep->hwp + FEC_MIB_CTRLSTAT); 103538ae92dcSChris Healy #endif 103638ae92dcSChris Healy 1037793fc096SFrank Li /* And last, enable the transmit and receive processing */ 1038793fc096SFrank Li writel(ecntl, fep->hwp + FEC_ECNTRL); 1039ce99d0d3SFrank Li fec_enet_active_rxring(ndev); 1040793fc096SFrank Li 1041793fc096SFrank Li if (fep->bufdesc_ex) 1042793fc096SFrank Li fec_ptp_start_cyclecounter(ndev); 1043793fc096SFrank Li 1044793fc096SFrank Li /* Enable interrupts we wish to service */ 10450c5a3aefSNimrod Andy if (fep->link) 1046793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 10470c5a3aefSNimrod Andy else 10480c5a3aefSNimrod Andy writel(FEC_ENET_MII, fep->hwp + FEC_IMASK); 1049d851b47bSFugang Duan 1050d851b47bSFugang Duan /* Init the interrupt coalescing */ 1051d851b47bSFugang Duan fec_enet_itr_coal_init(ndev); 1052d851b47bSFugang Duan 1053793fc096SFrank Li } 1054793fc096SFrank Li 1055793fc096SFrank Li static void 1056793fc096SFrank Li fec_stop(struct net_device *ndev) 1057793fc096SFrank Li { 1058793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1059de40ed31SNimrod Andy struct fec_platform_data *pdata = fep->pdev->dev.platform_data; 1060793fc096SFrank Li u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8); 1061de40ed31SNimrod Andy u32 val; 1062793fc096SFrank Li 1063793fc096SFrank Li /* We cannot expect a graceful transmit stop without link !!! */ 1064793fc096SFrank Li if (fep->link) { 1065793fc096SFrank Li writel(1, fep->hwp + FEC_X_CNTRL); /* Graceful transmit stop */ 1066793fc096SFrank Li udelay(10); 1067793fc096SFrank Li if (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_GRA)) 106831b7720cSJoe Perches netdev_err(ndev, "Graceful transmit stop did not complete!\n"); 1069793fc096SFrank Li } 1070793fc096SFrank Li 1071106c314cSFugang Duan /* Whack a reset. We should wait for this. 1072106c314cSFugang Duan * For i.MX6SX SOC, enet use AXI bus, we use disable MAC 1073106c314cSFugang Duan * instead of reset MAC itself. 1074106c314cSFugang Duan */ 1075de40ed31SNimrod Andy if (!(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { 10766b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) { 1077106c314cSFugang Duan writel(0, fep->hwp + FEC_ECNTRL); 1078106c314cSFugang Duan } else { 1079793fc096SFrank Li writel(1, fep->hwp + FEC_ECNTRL); 1080793fc096SFrank Li udelay(10); 1081106c314cSFugang Duan } 1082793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 1083de40ed31SNimrod Andy } else { 1084de40ed31SNimrod Andy writel(FEC_DEFAULT_IMASK | FEC_ENET_WAKEUP, fep->hwp + FEC_IMASK); 1085de40ed31SNimrod Andy val = readl(fep->hwp + FEC_ECNTRL); 1086de40ed31SNimrod Andy val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP); 1087de40ed31SNimrod Andy writel(val, fep->hwp + FEC_ECNTRL); 1088de40ed31SNimrod Andy 1089de40ed31SNimrod Andy if (pdata && pdata->sleep_mode_enable) 1090de40ed31SNimrod Andy pdata->sleep_mode_enable(true); 1091de40ed31SNimrod Andy } 1092de40ed31SNimrod Andy writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 1093793fc096SFrank Li 1094793fc096SFrank Li /* We have to keep ENET enabled to have MII interrupt stay working */ 1095de40ed31SNimrod Andy if (fep->quirks & FEC_QUIRK_ENET_MAC && 1096de40ed31SNimrod Andy !(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { 1097793fc096SFrank Li writel(2, fep->hwp + FEC_ECNTRL); 1098793fc096SFrank Li writel(rmii_mode, fep->hwp + FEC_R_CNTRL); 1099793fc096SFrank Li } 1100793fc096SFrank Li } 1101793fc096SFrank Li 1102793fc096SFrank Li 1103793fc096SFrank Li static void 1104793fc096SFrank Li fec_timeout(struct net_device *ndev) 1105793fc096SFrank Li { 1106793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1107793fc096SFrank Li 1108344756f6SRussell King fec_dump(ndev); 1109344756f6SRussell King 1110793fc096SFrank Li ndev->stats.tx_errors++; 1111793fc096SFrank Li 111236cdc743SRussell King schedule_work(&fep->tx_timeout_work); 111354309fa6SFrank Li } 111454309fa6SFrank Li 111536cdc743SRussell King static void fec_enet_timeout_work(struct work_struct *work) 111654309fa6SFrank Li { 111754309fa6SFrank Li struct fec_enet_private *fep = 111836cdc743SRussell King container_of(work, struct fec_enet_private, tx_timeout_work); 11198ce5624fSRussell King struct net_device *ndev = fep->netdev; 112054309fa6SFrank Li 1121da1774e5SRussell King rtnl_lock(); 11228ce5624fSRussell King if (netif_device_present(ndev) || netif_running(ndev)) { 1123dbc64a8eSRussell King napi_disable(&fep->napi); 1124dbc64a8eSRussell King netif_tx_lock_bh(ndev); 1125ef83337dSRussell King fec_restart(ndev); 11268ce5624fSRussell King netif_wake_queue(ndev); 11276af42d42SRussell King netif_tx_unlock_bh(ndev); 1128dbc64a8eSRussell King napi_enable(&fep->napi); 11298ce5624fSRussell King } 1130da1774e5SRussell King rtnl_unlock(); 113154309fa6SFrank Li } 1132793fc096SFrank Li 1133793fc096SFrank Li static void 1134bfd4ecddSRussell King fec_enet_hwtstamp(struct fec_enet_private *fep, unsigned ts, 1135bfd4ecddSRussell King struct skb_shared_hwtstamps *hwtstamps) 1136bfd4ecddSRussell King { 1137bfd4ecddSRussell King unsigned long flags; 1138bfd4ecddSRussell King u64 ns; 1139bfd4ecddSRussell King 1140bfd4ecddSRussell King spin_lock_irqsave(&fep->tmreg_lock, flags); 1141bfd4ecddSRussell King ns = timecounter_cyc2time(&fep->tc, ts); 1142bfd4ecddSRussell King spin_unlock_irqrestore(&fep->tmreg_lock, flags); 1143bfd4ecddSRussell King 1144bfd4ecddSRussell King memset(hwtstamps, 0, sizeof(*hwtstamps)); 1145bfd4ecddSRussell King hwtstamps->hwtstamp = ns_to_ktime(ns); 1146bfd4ecddSRussell King } 1147bfd4ecddSRussell King 1148bfd4ecddSRussell King static void 11494d494cdcSFugang Duan fec_enet_tx_queue(struct net_device *ndev, u16 queue_id) 1150793fc096SFrank Li { 1151793fc096SFrank Li struct fec_enet_private *fep; 1152a2fe37b6SFabio Estevam struct bufdesc *bdp; 1153793fc096SFrank Li unsigned short status; 1154793fc096SFrank Li struct sk_buff *skb; 11554d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 11564d494cdcSFugang Duan struct netdev_queue *nq; 1157793fc096SFrank Li int index = 0; 115879f33912SNimrod Andy int entries_free; 1159793fc096SFrank Li 1160793fc096SFrank Li fep = netdev_priv(ndev); 11614d494cdcSFugang Duan 11624d494cdcSFugang Duan queue_id = FEC_ENET_GET_QUQUE(queue_id); 11634d494cdcSFugang Duan 11644d494cdcSFugang Duan txq = fep->tx_queue[queue_id]; 11654d494cdcSFugang Duan /* get next bdp of dirty_tx */ 11664d494cdcSFugang Duan nq = netdev_get_tx_queue(ndev, queue_id); 11674d494cdcSFugang Duan bdp = txq->dirty_tx; 1168793fc096SFrank Li 1169793fc096SFrank Li /* get next bdp of dirty_tx */ 11707355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 1171793fc096SFrank Li 11727355f276STroy Kisky while (bdp != READ_ONCE(txq->bd.cur)) { 11737355f276STroy Kisky /* Order the load of bd.cur and cbd_sc */ 1174c4bc44c6SKevin Hao rmb(); 11755cfa3039SJohannes Berg status = fec16_to_cpu(READ_ONCE(bdp->cbd_sc)); 1176c4bc44c6SKevin Hao if (status & BD_ENET_TX_READY) 1177793fc096SFrank Li break; 1178793fc096SFrank Li 11797355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &txq->bd); 11802b995f63SNimrod Andy 1181a2fe37b6SFabio Estevam skb = txq->tx_skbuff[index]; 1182a2fe37b6SFabio Estevam txq->tx_skbuff[index] = NULL; 11835cfa3039SJohannes Berg if (!IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr))) 11845cfa3039SJohannes Berg dma_unmap_single(&fep->pdev->dev, 11855cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 11865cfa3039SJohannes Berg fec16_to_cpu(bdp->cbd_datlen), 11875cfa3039SJohannes Berg DMA_TO_DEVICE); 11885cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(0); 1189a2fe37b6SFabio Estevam if (!skb) { 11907355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 1191a2fe37b6SFabio Estevam continue; 11926e909283SNimrod Andy } 1193793fc096SFrank Li 1194793fc096SFrank Li /* Check for errors. */ 1195793fc096SFrank Li if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC | 1196793fc096SFrank Li BD_ENET_TX_RL | BD_ENET_TX_UN | 1197793fc096SFrank Li BD_ENET_TX_CSL)) { 1198793fc096SFrank Li ndev->stats.tx_errors++; 1199793fc096SFrank Li if (status & BD_ENET_TX_HB) /* No heartbeat */ 1200793fc096SFrank Li ndev->stats.tx_heartbeat_errors++; 1201793fc096SFrank Li if (status & BD_ENET_TX_LC) /* Late collision */ 1202793fc096SFrank Li ndev->stats.tx_window_errors++; 1203793fc096SFrank Li if (status & BD_ENET_TX_RL) /* Retrans limit */ 1204793fc096SFrank Li ndev->stats.tx_aborted_errors++; 1205793fc096SFrank Li if (status & BD_ENET_TX_UN) /* Underrun */ 1206793fc096SFrank Li ndev->stats.tx_fifo_errors++; 1207793fc096SFrank Li if (status & BD_ENET_TX_CSL) /* Carrier lost */ 1208793fc096SFrank Li ndev->stats.tx_carrier_errors++; 1209793fc096SFrank Li } else { 1210793fc096SFrank Li ndev->stats.tx_packets++; 12116e909283SNimrod Andy ndev->stats.tx_bytes += skb->len; 1212793fc096SFrank Li } 1213793fc096SFrank Li 1214793fc096SFrank Li if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) && 1215793fc096SFrank Li fep->bufdesc_ex) { 1216793fc096SFrank Li struct skb_shared_hwtstamps shhwtstamps; 1217793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 1218793fc096SFrank Li 12195cfa3039SJohannes Berg fec_enet_hwtstamp(fep, fec32_to_cpu(ebdp->ts), &shhwtstamps); 1220793fc096SFrank Li skb_tstamp_tx(skb, &shhwtstamps); 1221793fc096SFrank Li } 1222793fc096SFrank Li 1223793fc096SFrank Li /* Deferred means some collisions occurred during transmit, 1224793fc096SFrank Li * but we eventually sent the packet OK. 1225793fc096SFrank Li */ 1226793fc096SFrank Li if (status & BD_ENET_TX_DEF) 1227793fc096SFrank Li ndev->stats.collisions++; 1228793fc096SFrank Li 1229793fc096SFrank Li /* Free the sk buffer associated with this last transmit */ 1230793fc096SFrank Li dev_kfree_skb_any(skb); 1231793fc096SFrank Li 1232c4bc44c6SKevin Hao /* Make sure the update to bdp and tx_skbuff are performed 1233c4bc44c6SKevin Hao * before dirty_tx 1234c4bc44c6SKevin Hao */ 1235c4bc44c6SKevin Hao wmb(); 12364d494cdcSFugang Duan txq->dirty_tx = bdp; 1237793fc096SFrank Li 1238793fc096SFrank Li /* Update pointer to next buffer descriptor to be transmitted */ 12397355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 1240793fc096SFrank Li 1241793fc096SFrank Li /* Since we have freed up a buffer, the ring is no longer full 1242793fc096SFrank Li */ 124379f33912SNimrod Andy if (netif_queue_stopped(ndev)) { 12447355f276STroy Kisky entries_free = fec_enet_get_free_txdesc_num(txq); 12454d494cdcSFugang Duan if (entries_free >= txq->tx_wake_threshold) 12464d494cdcSFugang Duan netif_tx_wake_queue(nq); 1247793fc096SFrank Li } 124879f33912SNimrod Andy } 1249ccea2968SRussell King 1250ccea2968SRussell King /* ERR006538: Keep the transmitter going */ 12517355f276STroy Kisky if (bdp != txq->bd.cur && 1252*53bb20d1STroy Kisky readl(txq->bd.reg_desc_active) == 0) 1253*53bb20d1STroy Kisky writel(0, txq->bd.reg_desc_active); 12544d494cdcSFugang Duan } 12554d494cdcSFugang Duan 12564d494cdcSFugang Duan static void 12574d494cdcSFugang Duan fec_enet_tx(struct net_device *ndev) 12584d494cdcSFugang Duan { 12594d494cdcSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 12604d494cdcSFugang Duan u16 queue_id; 12614d494cdcSFugang Duan /* First process class A queue, then Class B and Best Effort queue */ 12624d494cdcSFugang Duan for_each_set_bit(queue_id, &fep->work_tx, FEC_ENET_MAX_TX_QS) { 12634d494cdcSFugang Duan clear_bit(queue_id, &fep->work_tx); 12644d494cdcSFugang Duan fec_enet_tx_queue(ndev, queue_id); 12654d494cdcSFugang Duan } 12664d494cdcSFugang Duan return; 1267793fc096SFrank Li } 1268793fc096SFrank Li 12691b7bde6dSNimrod Andy static int 12701b7bde6dSNimrod Andy fec_enet_new_rxbdp(struct net_device *ndev, struct bufdesc *bdp, struct sk_buff *skb) 12711b7bde6dSNimrod Andy { 12721b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 12731b7bde6dSNimrod Andy int off; 12741b7bde6dSNimrod Andy 12751b7bde6dSNimrod Andy off = ((unsigned long)skb->data) & fep->rx_align; 12761b7bde6dSNimrod Andy if (off) 12771b7bde6dSNimrod Andy skb_reserve(skb, fep->rx_align + 1 - off); 12781b7bde6dSNimrod Andy 12795cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(dma_map_single(&fep->pdev->dev, skb->data, FEC_ENET_RX_FRSIZE - fep->rx_align, DMA_FROM_DEVICE)); 12805cfa3039SJohannes Berg if (dma_mapping_error(&fep->pdev->dev, fec32_to_cpu(bdp->cbd_bufaddr))) { 12811b7bde6dSNimrod Andy if (net_ratelimit()) 12821b7bde6dSNimrod Andy netdev_err(ndev, "Rx DMA memory map failed\n"); 12831b7bde6dSNimrod Andy return -ENOMEM; 12841b7bde6dSNimrod Andy } 12851b7bde6dSNimrod Andy 12861b7bde6dSNimrod Andy return 0; 12871b7bde6dSNimrod Andy } 12881b7bde6dSNimrod Andy 12891b7bde6dSNimrod Andy static bool fec_enet_copybreak(struct net_device *ndev, struct sk_buff **skb, 12901310b544SLothar Waßmann struct bufdesc *bdp, u32 length, bool swap) 12911b7bde6dSNimrod Andy { 12921b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 12931b7bde6dSNimrod Andy struct sk_buff *new_skb; 12941b7bde6dSNimrod Andy 12951b7bde6dSNimrod Andy if (length > fep->rx_copybreak) 12961b7bde6dSNimrod Andy return false; 12971b7bde6dSNimrod Andy 12981b7bde6dSNimrod Andy new_skb = netdev_alloc_skb(ndev, length); 12991b7bde6dSNimrod Andy if (!new_skb) 13001b7bde6dSNimrod Andy return false; 13011b7bde6dSNimrod Andy 13025cfa3039SJohannes Berg dma_sync_single_for_cpu(&fep->pdev->dev, 13035cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 13041b7bde6dSNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 13051b7bde6dSNimrod Andy DMA_FROM_DEVICE); 13061310b544SLothar Waßmann if (!swap) 13071b7bde6dSNimrod Andy memcpy(new_skb->data, (*skb)->data, length); 13081310b544SLothar Waßmann else 13091310b544SLothar Waßmann swap_buffer2(new_skb->data, (*skb)->data, length); 13101b7bde6dSNimrod Andy *skb = new_skb; 13111b7bde6dSNimrod Andy 13121b7bde6dSNimrod Andy return true; 13131b7bde6dSNimrod Andy } 13141b7bde6dSNimrod Andy 13157355f276STroy Kisky /* During a receive, the bd_rx.cur points to the current incoming buffer. 1316793fc096SFrank Li * When we update through the ring, if the next incoming buffer has 1317793fc096SFrank Li * not been given to the system, we just set the empty indicator, 1318793fc096SFrank Li * effectively tossing the packet. 1319793fc096SFrank Li */ 1320793fc096SFrank Li static int 13214d494cdcSFugang Duan fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) 1322793fc096SFrank Li { 1323793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 13244d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 1325793fc096SFrank Li struct bufdesc *bdp; 1326793fc096SFrank Li unsigned short status; 13271b7bde6dSNimrod Andy struct sk_buff *skb_new = NULL; 1328793fc096SFrank Li struct sk_buff *skb; 1329793fc096SFrank Li ushort pkt_len; 1330793fc096SFrank Li __u8 *data; 1331793fc096SFrank Li int pkt_received = 0; 1332cdffcf1bSJim Baxter struct bufdesc_ex *ebdp = NULL; 1333cdffcf1bSJim Baxter bool vlan_packet_rcvd = false; 1334cdffcf1bSJim Baxter u16 vlan_tag; 1335d842a31fSDuan Fugang-B38611 int index = 0; 13361b7bde6dSNimrod Andy bool is_copybreak; 13376b7e4008SLothar Waßmann bool need_swap = fep->quirks & FEC_QUIRK_SWAP_FRAME; 1338793fc096SFrank Li 1339793fc096SFrank Li #ifdef CONFIG_M532x 1340793fc096SFrank Li flush_cache_all(); 1341793fc096SFrank Li #endif 13424d494cdcSFugang Duan queue_id = FEC_ENET_GET_QUQUE(queue_id); 13434d494cdcSFugang Duan rxq = fep->rx_queue[queue_id]; 1344793fc096SFrank Li 1345793fc096SFrank Li /* First, grab all of the stats for the incoming packet. 1346793fc096SFrank Li * These get messed up if we get called due to a busy condition. 1347793fc096SFrank Li */ 13487355f276STroy Kisky bdp = rxq->bd.cur; 1349793fc096SFrank Li 13505cfa3039SJohannes Berg while (!((status = fec16_to_cpu(bdp->cbd_sc)) & BD_ENET_RX_EMPTY)) { 1351793fc096SFrank Li 1352793fc096SFrank Li if (pkt_received >= budget) 1353793fc096SFrank Li break; 1354793fc096SFrank Li pkt_received++; 1355793fc096SFrank Li 1356ed63f1dcSRussell King writel(FEC_ENET_RXF, fep->hwp + FEC_IEVENT); 1357db3421c1SRussell King 1358793fc096SFrank Li /* Check for errors. */ 1359095098e1STroy Kisky status ^= BD_ENET_RX_LAST; 1360793fc096SFrank Li if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | 1361095098e1STroy Kisky BD_ENET_RX_CR | BD_ENET_RX_OV | BD_ENET_RX_LAST | 1362095098e1STroy Kisky BD_ENET_RX_CL)) { 1363793fc096SFrank Li ndev->stats.rx_errors++; 1364095098e1STroy Kisky if (status & BD_ENET_RX_OV) { 1365095098e1STroy Kisky /* FIFO overrun */ 1366095098e1STroy Kisky ndev->stats.rx_fifo_errors++; 1367095098e1STroy Kisky goto rx_processing_done; 1368095098e1STroy Kisky } 1369095098e1STroy Kisky if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH 1370095098e1STroy Kisky | BD_ENET_RX_LAST)) { 1371793fc096SFrank Li /* Frame too long or too short. */ 1372793fc096SFrank Li ndev->stats.rx_length_errors++; 1373095098e1STroy Kisky if (status & BD_ENET_RX_LAST) 1374095098e1STroy Kisky netdev_err(ndev, "rcv is not +last\n"); 1375793fc096SFrank Li } 1376793fc096SFrank Li if (status & BD_ENET_RX_CR) /* CRC Error */ 1377793fc096SFrank Li ndev->stats.rx_crc_errors++; 1378095098e1STroy Kisky /* Report late collisions as a frame error. */ 1379095098e1STroy Kisky if (status & (BD_ENET_RX_NO | BD_ENET_RX_CL)) 1380793fc096SFrank Li ndev->stats.rx_frame_errors++; 1381793fc096SFrank Li goto rx_processing_done; 1382793fc096SFrank Li } 1383793fc096SFrank Li 1384793fc096SFrank Li /* Process the incoming frame. */ 1385793fc096SFrank Li ndev->stats.rx_packets++; 13865cfa3039SJohannes Berg pkt_len = fec16_to_cpu(bdp->cbd_datlen); 1387793fc096SFrank Li ndev->stats.rx_bytes += pkt_len; 1388793fc096SFrank Li 13897355f276STroy Kisky index = fec_enet_get_bd_index(bdp, &rxq->bd); 13901b7bde6dSNimrod Andy skb = rxq->rx_skbuff[index]; 13911b7bde6dSNimrod Andy 13921b7bde6dSNimrod Andy /* The packet length includes FCS, but we don't want to 13931b7bde6dSNimrod Andy * include that when passing upstream as it messes up 13941b7bde6dSNimrod Andy * bridging applications. 13951b7bde6dSNimrod Andy */ 13961310b544SLothar Waßmann is_copybreak = fec_enet_copybreak(ndev, &skb, bdp, pkt_len - 4, 13971310b544SLothar Waßmann need_swap); 13981b7bde6dSNimrod Andy if (!is_copybreak) { 13991b7bde6dSNimrod Andy skb_new = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE); 14001b7bde6dSNimrod Andy if (unlikely(!skb_new)) { 14011b7bde6dSNimrod Andy ndev->stats.rx_dropped++; 14021b7bde6dSNimrod Andy goto rx_processing_done; 14031b7bde6dSNimrod Andy } 14045cfa3039SJohannes Berg dma_unmap_single(&fep->pdev->dev, 14055cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 1406b64bf4b7SNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 1407b64bf4b7SNimrod Andy DMA_FROM_DEVICE); 14081b7bde6dSNimrod Andy } 1409793fc096SFrank Li 14101b7bde6dSNimrod Andy prefetch(skb->data - NET_IP_ALIGN); 14111b7bde6dSNimrod Andy skb_put(skb, pkt_len - 4); 14121b7bde6dSNimrod Andy data = skb->data; 14131310b544SLothar Waßmann if (!is_copybreak && need_swap) 1414793fc096SFrank Li swap_buffer(data, pkt_len); 1415793fc096SFrank Li 1416cdffcf1bSJim Baxter /* Extract the enhanced buffer descriptor */ 1417cdffcf1bSJim Baxter ebdp = NULL; 1418cdffcf1bSJim Baxter if (fep->bufdesc_ex) 1419cdffcf1bSJim Baxter ebdp = (struct bufdesc_ex *)bdp; 1420cdffcf1bSJim Baxter 1421cdffcf1bSJim Baxter /* If this is a VLAN packet remove the VLAN Tag */ 1422cdffcf1bSJim Baxter vlan_packet_rcvd = false; 1423cdffcf1bSJim Baxter if ((ndev->features & NETIF_F_HW_VLAN_CTAG_RX) && 14245cfa3039SJohannes Berg fep->bufdesc_ex && 14255cfa3039SJohannes Berg (ebdp->cbd_esc & cpu_to_fec32(BD_ENET_RX_VLAN))) { 1426cdffcf1bSJim Baxter /* Push and remove the vlan tag */ 1427cdffcf1bSJim Baxter struct vlan_hdr *vlan_header = 1428cdffcf1bSJim Baxter (struct vlan_hdr *) (data + ETH_HLEN); 1429cdffcf1bSJim Baxter vlan_tag = ntohs(vlan_header->h_vlan_TCI); 1430cdffcf1bSJim Baxter 1431cdffcf1bSJim Baxter vlan_packet_rcvd = true; 14321b7bde6dSNimrod Andy 1433af5cbc98SNimrod Andy memmove(skb->data + VLAN_HLEN, data, ETH_ALEN * 2); 14341b7bde6dSNimrod Andy skb_pull(skb, VLAN_HLEN); 1435cdffcf1bSJim Baxter } 1436cdffcf1bSJim Baxter 1437793fc096SFrank Li skb->protocol = eth_type_trans(skb, ndev); 1438793fc096SFrank Li 1439793fc096SFrank Li /* Get receive timestamp from the skb */ 1440bfd4ecddSRussell King if (fep->hwts_rx_en && fep->bufdesc_ex) 14415cfa3039SJohannes Berg fec_enet_hwtstamp(fep, fec32_to_cpu(ebdp->ts), 1442bfd4ecddSRussell King skb_hwtstamps(skb)); 1443793fc096SFrank Li 14444c09eed9SJim Baxter if (fep->bufdesc_ex && 14454c09eed9SJim Baxter (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { 14465cfa3039SJohannes Berg if (!(ebdp->cbd_esc & cpu_to_fec32(FLAG_RX_CSUM_ERROR))) { 14474c09eed9SJim Baxter /* don't check it */ 14484c09eed9SJim Baxter skb->ip_summed = CHECKSUM_UNNECESSARY; 14494c09eed9SJim Baxter } else { 14504c09eed9SJim Baxter skb_checksum_none_assert(skb); 14514c09eed9SJim Baxter } 14524c09eed9SJim Baxter } 14534c09eed9SJim Baxter 1454cdffcf1bSJim Baxter /* Handle received VLAN packets */ 1455cdffcf1bSJim Baxter if (vlan_packet_rcvd) 1456cdffcf1bSJim Baxter __vlan_hwaccel_put_tag(skb, 1457cdffcf1bSJim Baxter htons(ETH_P_8021Q), 1458cdffcf1bSJim Baxter vlan_tag); 1459cdffcf1bSJim Baxter 1460793fc096SFrank Li napi_gro_receive(&fep->napi, skb); 1461793fc096SFrank Li 14621b7bde6dSNimrod Andy if (is_copybreak) { 14635cfa3039SJohannes Berg dma_sync_single_for_device(&fep->pdev->dev, 14645cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 1465b64bf4b7SNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 1466b64bf4b7SNimrod Andy DMA_FROM_DEVICE); 14671b7bde6dSNimrod Andy } else { 14681b7bde6dSNimrod Andy rxq->rx_skbuff[index] = skb_new; 14691b7bde6dSNimrod Andy fec_enet_new_rxbdp(ndev, bdp, skb_new); 14701b7bde6dSNimrod Andy } 14711b7bde6dSNimrod Andy 1472793fc096SFrank Li rx_processing_done: 1473793fc096SFrank Li /* Clear the status flags for this buffer */ 1474793fc096SFrank Li status &= ~BD_ENET_RX_STATS; 1475793fc096SFrank Li 1476793fc096SFrank Li /* Mark the buffer empty */ 1477793fc096SFrank Li status |= BD_ENET_RX_EMPTY; 14785cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(status); 1479793fc096SFrank Li 1480793fc096SFrank Li if (fep->bufdesc_ex) { 1481793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 1482793fc096SFrank Li 14835cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(BD_ENET_RX_INT); 1484793fc096SFrank Li ebdp->cbd_prot = 0; 1485793fc096SFrank Li ebdp->cbd_bdu = 0; 1486793fc096SFrank Li } 1487793fc096SFrank Li 1488793fc096SFrank Li /* Update BD pointer to next entry */ 14897355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 149036e24e2eSDuan Fugang-B38611 1491793fc096SFrank Li /* Doing this here will keep the FEC running while we process 1492793fc096SFrank Li * incoming frames. On a heavily loaded network, we should be 1493793fc096SFrank Li * able to keep up at the expense of system resources. 1494793fc096SFrank Li */ 1495*53bb20d1STroy Kisky writel(0, rxq->bd.reg_desc_active); 1496793fc096SFrank Li } 14977355f276STroy Kisky rxq->bd.cur = bdp; 1498793fc096SFrank Li return pkt_received; 1499793fc096SFrank Li } 1500793fc096SFrank Li 15014d494cdcSFugang Duan static int 15024d494cdcSFugang Duan fec_enet_rx(struct net_device *ndev, int budget) 15034d494cdcSFugang Duan { 15044d494cdcSFugang Duan int pkt_received = 0; 15054d494cdcSFugang Duan u16 queue_id; 15064d494cdcSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 15074d494cdcSFugang Duan 15084d494cdcSFugang Duan for_each_set_bit(queue_id, &fep->work_rx, FEC_ENET_MAX_RX_QS) { 15094d494cdcSFugang Duan clear_bit(queue_id, &fep->work_rx); 15104d494cdcSFugang Duan pkt_received += fec_enet_rx_queue(ndev, 15114d494cdcSFugang Duan budget - pkt_received, queue_id); 15124d494cdcSFugang Duan } 15134d494cdcSFugang Duan return pkt_received; 15144d494cdcSFugang Duan } 15154d494cdcSFugang Duan 15164d494cdcSFugang Duan static bool 15174d494cdcSFugang Duan fec_enet_collect_events(struct fec_enet_private *fep, uint int_events) 15184d494cdcSFugang Duan { 15194d494cdcSFugang Duan if (int_events == 0) 15204d494cdcSFugang Duan return false; 15214d494cdcSFugang Duan 15224d494cdcSFugang Duan if (int_events & FEC_ENET_RXF) 15234d494cdcSFugang Duan fep->work_rx |= (1 << 2); 1524ce99d0d3SFrank Li if (int_events & FEC_ENET_RXF_1) 1525ce99d0d3SFrank Li fep->work_rx |= (1 << 0); 1526ce99d0d3SFrank Li if (int_events & FEC_ENET_RXF_2) 1527ce99d0d3SFrank Li fep->work_rx |= (1 << 1); 15284d494cdcSFugang Duan 15294d494cdcSFugang Duan if (int_events & FEC_ENET_TXF) 15304d494cdcSFugang Duan fep->work_tx |= (1 << 2); 1531ce99d0d3SFrank Li if (int_events & FEC_ENET_TXF_1) 1532ce99d0d3SFrank Li fep->work_tx |= (1 << 0); 1533ce99d0d3SFrank Li if (int_events & FEC_ENET_TXF_2) 1534ce99d0d3SFrank Li fep->work_tx |= (1 << 1); 15354d494cdcSFugang Duan 15364d494cdcSFugang Duan return true; 15374d494cdcSFugang Duan } 15384d494cdcSFugang Duan 1539793fc096SFrank Li static irqreturn_t 1540793fc096SFrank Li fec_enet_interrupt(int irq, void *dev_id) 1541793fc096SFrank Li { 1542793fc096SFrank Li struct net_device *ndev = dev_id; 1543793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1544793fc096SFrank Li uint int_events; 1545793fc096SFrank Li irqreturn_t ret = IRQ_NONE; 1546793fc096SFrank Li 1547793fc096SFrank Li int_events = readl(fep->hwp + FEC_IEVENT); 154894191fd6SNimrod Andy writel(int_events, fep->hwp + FEC_IEVENT); 15494d494cdcSFugang Duan fec_enet_collect_events(fep, int_events); 1550793fc096SFrank Li 155161615cd2SFugang Duan if ((fep->work_tx || fep->work_rx) && fep->link) { 1552793fc096SFrank Li ret = IRQ_HANDLED; 1553793fc096SFrank Li 155494191fd6SNimrod Andy if (napi_schedule_prep(&fep->napi)) { 15557a16807cSRussell King /* Disable the NAPI interrupts */ 15567a16807cSRussell King writel(FEC_ENET_MII, fep->hwp + FEC_IMASK); 155794191fd6SNimrod Andy __napi_schedule(&fep->napi); 155894191fd6SNimrod Andy } 1559793fc096SFrank Li } 1560793fc096SFrank Li 1561793fc096SFrank Li if (int_events & FEC_ENET_MII) { 1562793fc096SFrank Li ret = IRQ_HANDLED; 1563793fc096SFrank Li complete(&fep->mdio_done); 1564793fc096SFrank Li } 1565793fc096SFrank Li 156681f35ffdSPhilipp Zabel if (fep->ptp_clock) 1567278d2404SLuwei Zhou fec_ptp_check_pps_event(fep); 1568278d2404SLuwei Zhou 1569793fc096SFrank Li return ret; 1570793fc096SFrank Li } 1571793fc096SFrank Li 1572793fc096SFrank Li static int fec_enet_rx_napi(struct napi_struct *napi, int budget) 1573793fc096SFrank Li { 1574793fc096SFrank Li struct net_device *ndev = napi->dev; 1575793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 15767a16807cSRussell King int pkts; 15777a16807cSRussell King 15787a16807cSRussell King pkts = fec_enet_rx(ndev, budget); 1579793fc096SFrank Li 1580793fc096SFrank Li fec_enet_tx(ndev); 1581793fc096SFrank Li 1582793fc096SFrank Li if (pkts < budget) { 1583793fc096SFrank Li napi_complete(napi); 1584793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 1585793fc096SFrank Li } 1586793fc096SFrank Li return pkts; 1587793fc096SFrank Li } 1588793fc096SFrank Li 1589793fc096SFrank Li /* ------------------------------------------------------------------------- */ 1590793fc096SFrank Li static void fec_get_mac(struct net_device *ndev) 1591793fc096SFrank Li { 1592793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 159394660ba0SJingoo Han struct fec_platform_data *pdata = dev_get_platdata(&fep->pdev->dev); 1594793fc096SFrank Li unsigned char *iap, tmpaddr[ETH_ALEN]; 1595793fc096SFrank Li 1596793fc096SFrank Li /* 1597793fc096SFrank Li * try to get mac address in following order: 1598793fc096SFrank Li * 1599793fc096SFrank Li * 1) module parameter via kernel command line in form 1600793fc096SFrank Li * fec.macaddr=0x00,0x04,0x9f,0x01,0x30,0xe0 1601793fc096SFrank Li */ 1602793fc096SFrank Li iap = macaddr; 1603793fc096SFrank Li 1604793fc096SFrank Li /* 1605793fc096SFrank Li * 2) from device tree data 1606793fc096SFrank Li */ 1607793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 1608793fc096SFrank Li struct device_node *np = fep->pdev->dev.of_node; 1609793fc096SFrank Li if (np) { 1610793fc096SFrank Li const char *mac = of_get_mac_address(np); 1611793fc096SFrank Li if (mac) 1612793fc096SFrank Li iap = (unsigned char *) mac; 1613793fc096SFrank Li } 1614793fc096SFrank Li } 1615793fc096SFrank Li 1616793fc096SFrank Li /* 1617793fc096SFrank Li * 3) from flash or fuse (via platform data) 1618793fc096SFrank Li */ 1619793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 1620793fc096SFrank Li #ifdef CONFIG_M5272 1621793fc096SFrank Li if (FEC_FLASHMAC) 1622793fc096SFrank Li iap = (unsigned char *)FEC_FLASHMAC; 1623793fc096SFrank Li #else 1624793fc096SFrank Li if (pdata) 1625793fc096SFrank Li iap = (unsigned char *)&pdata->mac; 1626793fc096SFrank Li #endif 1627793fc096SFrank Li } 1628793fc096SFrank Li 1629793fc096SFrank Li /* 1630793fc096SFrank Li * 4) FEC mac registers set by bootloader 1631793fc096SFrank Li */ 1632793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 16337d7628f3SDan Carpenter *((__be32 *) &tmpaddr[0]) = 16347d7628f3SDan Carpenter cpu_to_be32(readl(fep->hwp + FEC_ADDR_LOW)); 16357d7628f3SDan Carpenter *((__be16 *) &tmpaddr[4]) = 16367d7628f3SDan Carpenter cpu_to_be16(readl(fep->hwp + FEC_ADDR_HIGH) >> 16); 1637793fc096SFrank Li iap = &tmpaddr[0]; 1638793fc096SFrank Li } 1639793fc096SFrank Li 1640ff5b2fabSLucas Stach /* 1641ff5b2fabSLucas Stach * 5) random mac address 1642ff5b2fabSLucas Stach */ 1643ff5b2fabSLucas Stach if (!is_valid_ether_addr(iap)) { 1644ff5b2fabSLucas Stach /* Report it and use a random ethernet address instead */ 1645ff5b2fabSLucas Stach netdev_err(ndev, "Invalid MAC address: %pM\n", iap); 1646ff5b2fabSLucas Stach eth_hw_addr_random(ndev); 1647ff5b2fabSLucas Stach netdev_info(ndev, "Using random MAC address: %pM\n", 1648ff5b2fabSLucas Stach ndev->dev_addr); 1649ff5b2fabSLucas Stach return; 1650ff5b2fabSLucas Stach } 1651ff5b2fabSLucas Stach 1652793fc096SFrank Li memcpy(ndev->dev_addr, iap, ETH_ALEN); 1653793fc096SFrank Li 1654793fc096SFrank Li /* Adjust MAC if using macaddr */ 1655793fc096SFrank Li if (iap == macaddr) 1656793fc096SFrank Li ndev->dev_addr[ETH_ALEN-1] = macaddr[ETH_ALEN-1] + fep->dev_id; 1657793fc096SFrank Li } 1658793fc096SFrank Li 1659793fc096SFrank Li /* ------------------------------------------------------------------------- */ 1660793fc096SFrank Li 1661793fc096SFrank Li /* 1662793fc096SFrank Li * Phy section 1663793fc096SFrank Li */ 1664793fc096SFrank Li static void fec_enet_adjust_link(struct net_device *ndev) 1665793fc096SFrank Li { 1666793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1667793fc096SFrank Li struct phy_device *phy_dev = fep->phy_dev; 1668793fc096SFrank Li int status_change = 0; 1669793fc096SFrank Li 1670793fc096SFrank Li /* Prevent a state halted on mii error */ 1671793fc096SFrank Li if (fep->mii_timeout && phy_dev->state == PHY_HALTED) { 1672793fc096SFrank Li phy_dev->state = PHY_RESUMING; 167354309fa6SFrank Li return; 1674793fc096SFrank Li } 1675793fc096SFrank Li 16768ce5624fSRussell King /* 16778ce5624fSRussell King * If the netdev is down, or is going down, we're not interested 16788ce5624fSRussell King * in link state events, so just mark our idea of the link as down 16798ce5624fSRussell King * and ignore the event. 16808ce5624fSRussell King */ 16818ce5624fSRussell King if (!netif_running(ndev) || !netif_device_present(ndev)) { 16828ce5624fSRussell King fep->link = 0; 16838ce5624fSRussell King } else if (phy_dev->link) { 1684793fc096SFrank Li if (!fep->link) { 1685793fc096SFrank Li fep->link = phy_dev->link; 1686793fc096SFrank Li status_change = 1; 1687793fc096SFrank Li } 1688793fc096SFrank Li 1689ef83337dSRussell King if (fep->full_duplex != phy_dev->duplex) { 1690ef83337dSRussell King fep->full_duplex = phy_dev->duplex; 1691793fc096SFrank Li status_change = 1; 1692ef83337dSRussell King } 1693793fc096SFrank Li 1694793fc096SFrank Li if (phy_dev->speed != fep->speed) { 1695793fc096SFrank Li fep->speed = phy_dev->speed; 1696793fc096SFrank Li status_change = 1; 1697793fc096SFrank Li } 1698793fc096SFrank Li 1699793fc096SFrank Li /* if any of the above changed restart the FEC */ 1700dbc64a8eSRussell King if (status_change) { 1701dbc64a8eSRussell King napi_disable(&fep->napi); 1702dbc64a8eSRussell King netif_tx_lock_bh(ndev); 1703ef83337dSRussell King fec_restart(ndev); 1704dbc64a8eSRussell King netif_wake_queue(ndev); 17056af42d42SRussell King netif_tx_unlock_bh(ndev); 1706dbc64a8eSRussell King napi_enable(&fep->napi); 1707dbc64a8eSRussell King } 1708793fc096SFrank Li } else { 1709793fc096SFrank Li if (fep->link) { 1710f208ce10SRussell King napi_disable(&fep->napi); 1711f208ce10SRussell King netif_tx_lock_bh(ndev); 1712793fc096SFrank Li fec_stop(ndev); 1713f208ce10SRussell King netif_tx_unlock_bh(ndev); 1714f208ce10SRussell King napi_enable(&fep->napi); 17156e0895c2SDavid S. Miller fep->link = phy_dev->link; 1716793fc096SFrank Li status_change = 1; 1717793fc096SFrank Li } 1718793fc096SFrank Li } 1719793fc096SFrank Li 1720793fc096SFrank Li if (status_change) 1721793fc096SFrank Li phy_print_status(phy_dev); 1722793fc096SFrank Li } 1723793fc096SFrank Li 1724793fc096SFrank Li static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum) 1725793fc096SFrank Li { 1726793fc096SFrank Li struct fec_enet_private *fep = bus->priv; 17278fff755eSAndrew Lunn struct device *dev = &fep->pdev->dev; 1728793fc096SFrank Li unsigned long time_left; 17298fff755eSAndrew Lunn int ret = 0; 17308fff755eSAndrew Lunn 17318fff755eSAndrew Lunn ret = pm_runtime_get_sync(dev); 1732b0c6ce24SFabio Estevam if (ret < 0) 17338fff755eSAndrew Lunn return ret; 1734793fc096SFrank Li 1735793fc096SFrank Li fep->mii_timeout = 0; 1736aac27c7aSRussell King reinit_completion(&fep->mdio_done); 1737793fc096SFrank Li 1738793fc096SFrank Li /* start a read op */ 1739793fc096SFrank Li writel(FEC_MMFR_ST | FEC_MMFR_OP_READ | 1740793fc096SFrank Li FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) | 1741793fc096SFrank Li FEC_MMFR_TA, fep->hwp + FEC_MII_DATA); 1742793fc096SFrank Li 1743793fc096SFrank Li /* wait for end of transfer */ 1744793fc096SFrank Li time_left = wait_for_completion_timeout(&fep->mdio_done, 1745793fc096SFrank Li usecs_to_jiffies(FEC_MII_TIMEOUT)); 1746793fc096SFrank Li if (time_left == 0) { 1747793fc096SFrank Li fep->mii_timeout = 1; 174831b7720cSJoe Perches netdev_err(fep->netdev, "MDIO read timeout\n"); 17498fff755eSAndrew Lunn ret = -ETIMEDOUT; 17508fff755eSAndrew Lunn goto out; 1751793fc096SFrank Li } 1752793fc096SFrank Li 17538fff755eSAndrew Lunn ret = FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA)); 17548fff755eSAndrew Lunn 17558fff755eSAndrew Lunn out: 17568fff755eSAndrew Lunn pm_runtime_mark_last_busy(dev); 17578fff755eSAndrew Lunn pm_runtime_put_autosuspend(dev); 17588fff755eSAndrew Lunn 17598fff755eSAndrew Lunn return ret; 1760793fc096SFrank Li } 1761793fc096SFrank Li 1762793fc096SFrank Li static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum, 1763793fc096SFrank Li u16 value) 1764793fc096SFrank Li { 1765793fc096SFrank Li struct fec_enet_private *fep = bus->priv; 17668fff755eSAndrew Lunn struct device *dev = &fep->pdev->dev; 1767793fc096SFrank Li unsigned long time_left; 176842ea4457SMaciej S. Szmigiero int ret; 17698fff755eSAndrew Lunn 17708fff755eSAndrew Lunn ret = pm_runtime_get_sync(dev); 1771b0c6ce24SFabio Estevam if (ret < 0) 17728fff755eSAndrew Lunn return ret; 177342ea4457SMaciej S. Szmigiero else 177442ea4457SMaciej S. Szmigiero ret = 0; 1775793fc096SFrank Li 1776793fc096SFrank Li fep->mii_timeout = 0; 1777aac27c7aSRussell King reinit_completion(&fep->mdio_done); 1778793fc096SFrank Li 1779793fc096SFrank Li /* start a write op */ 1780793fc096SFrank Li writel(FEC_MMFR_ST | FEC_MMFR_OP_WRITE | 1781793fc096SFrank Li FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) | 1782793fc096SFrank Li FEC_MMFR_TA | FEC_MMFR_DATA(value), 1783793fc096SFrank Li fep->hwp + FEC_MII_DATA); 1784793fc096SFrank Li 1785793fc096SFrank Li /* wait for end of transfer */ 1786793fc096SFrank Li time_left = wait_for_completion_timeout(&fep->mdio_done, 1787793fc096SFrank Li usecs_to_jiffies(FEC_MII_TIMEOUT)); 1788793fc096SFrank Li if (time_left == 0) { 1789793fc096SFrank Li fep->mii_timeout = 1; 179031b7720cSJoe Perches netdev_err(fep->netdev, "MDIO write timeout\n"); 17918fff755eSAndrew Lunn ret = -ETIMEDOUT; 1792793fc096SFrank Li } 1793793fc096SFrank Li 17948fff755eSAndrew Lunn pm_runtime_mark_last_busy(dev); 17958fff755eSAndrew Lunn pm_runtime_put_autosuspend(dev); 17968fff755eSAndrew Lunn 17978fff755eSAndrew Lunn return ret; 1798793fc096SFrank Li } 1799793fc096SFrank Li 1800e8fcfcd5SNimrod Andy static int fec_enet_clk_enable(struct net_device *ndev, bool enable) 1801e8fcfcd5SNimrod Andy { 1802e8fcfcd5SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 1803e8fcfcd5SNimrod Andy int ret; 1804e8fcfcd5SNimrod Andy 1805e8fcfcd5SNimrod Andy if (enable) { 1806e8fcfcd5SNimrod Andy ret = clk_prepare_enable(fep->clk_ahb); 1807e8fcfcd5SNimrod Andy if (ret) 1808e8fcfcd5SNimrod Andy return ret; 1809e8fcfcd5SNimrod Andy if (fep->clk_enet_out) { 1810e8fcfcd5SNimrod Andy ret = clk_prepare_enable(fep->clk_enet_out); 1811e8fcfcd5SNimrod Andy if (ret) 1812e8fcfcd5SNimrod Andy goto failed_clk_enet_out; 1813e8fcfcd5SNimrod Andy } 1814e8fcfcd5SNimrod Andy if (fep->clk_ptp) { 181591c0d987SNimrod Andy mutex_lock(&fep->ptp_clk_mutex); 1816e8fcfcd5SNimrod Andy ret = clk_prepare_enable(fep->clk_ptp); 181791c0d987SNimrod Andy if (ret) { 181891c0d987SNimrod Andy mutex_unlock(&fep->ptp_clk_mutex); 1819e8fcfcd5SNimrod Andy goto failed_clk_ptp; 182091c0d987SNimrod Andy } else { 182191c0d987SNimrod Andy fep->ptp_clk_on = true; 182291c0d987SNimrod Andy } 182391c0d987SNimrod Andy mutex_unlock(&fep->ptp_clk_mutex); 1824e8fcfcd5SNimrod Andy } 18259b5330edSFugang Duan if (fep->clk_ref) { 18269b5330edSFugang Duan ret = clk_prepare_enable(fep->clk_ref); 18279b5330edSFugang Duan if (ret) 18289b5330edSFugang Duan goto failed_clk_ref; 18299b5330edSFugang Duan } 1830e8fcfcd5SNimrod Andy } else { 1831e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_ahb); 1832e8fcfcd5SNimrod Andy if (fep->clk_enet_out) 1833e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_enet_out); 183491c0d987SNimrod Andy if (fep->clk_ptp) { 183591c0d987SNimrod Andy mutex_lock(&fep->ptp_clk_mutex); 1836e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_ptp); 183791c0d987SNimrod Andy fep->ptp_clk_on = false; 183891c0d987SNimrod Andy mutex_unlock(&fep->ptp_clk_mutex); 183991c0d987SNimrod Andy } 18409b5330edSFugang Duan if (fep->clk_ref) 18419b5330edSFugang Duan clk_disable_unprepare(fep->clk_ref); 1842e8fcfcd5SNimrod Andy } 1843e8fcfcd5SNimrod Andy 1844e8fcfcd5SNimrod Andy return 0; 18459b5330edSFugang Duan 18469b5330edSFugang Duan failed_clk_ref: 18479b5330edSFugang Duan if (fep->clk_ref) 18489b5330edSFugang Duan clk_disable_unprepare(fep->clk_ref); 1849e8fcfcd5SNimrod Andy failed_clk_ptp: 1850e8fcfcd5SNimrod Andy if (fep->clk_enet_out) 1851e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_enet_out); 1852e8fcfcd5SNimrod Andy failed_clk_enet_out: 1853e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_ahb); 1854e8fcfcd5SNimrod Andy 1855e8fcfcd5SNimrod Andy return ret; 1856e8fcfcd5SNimrod Andy } 1857e8fcfcd5SNimrod Andy 1858793fc096SFrank Li static int fec_enet_mii_probe(struct net_device *ndev) 1859793fc096SFrank Li { 1860793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1861793fc096SFrank Li struct phy_device *phy_dev = NULL; 1862793fc096SFrank Li char mdio_bus_id[MII_BUS_ID_SIZE]; 1863793fc096SFrank Li char phy_name[MII_BUS_ID_SIZE + 3]; 1864793fc096SFrank Li int phy_id; 1865793fc096SFrank Li int dev_id = fep->dev_id; 1866793fc096SFrank Li 1867793fc096SFrank Li fep->phy_dev = NULL; 1868793fc096SFrank Li 1869407066f8SUwe Kleine-König if (fep->phy_node) { 1870407066f8SUwe Kleine-König phy_dev = of_phy_connect(ndev, fep->phy_node, 1871407066f8SUwe Kleine-König &fec_enet_adjust_link, 0, 1872407066f8SUwe Kleine-König fep->phy_interface); 1873213a9922SNimrod Andy if (!phy_dev) 1874213a9922SNimrod Andy return -ENODEV; 1875407066f8SUwe Kleine-König } else { 1876793fc096SFrank Li /* check for attached phy */ 1877793fc096SFrank Li for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) { 18787f854420SAndrew Lunn if (!mdiobus_is_registered_device(fep->mii_bus, phy_id)) 1879793fc096SFrank Li continue; 1880793fc096SFrank Li if (dev_id--) 1881793fc096SFrank Li continue; 1882949bdd20SRickard Strandqvist strlcpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE); 1883793fc096SFrank Li break; 1884793fc096SFrank Li } 1885793fc096SFrank Li 1886793fc096SFrank Li if (phy_id >= PHY_MAX_ADDR) { 188731b7720cSJoe Perches netdev_info(ndev, "no PHY, assuming direct connection to switch\n"); 1888949bdd20SRickard Strandqvist strlcpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE); 1889793fc096SFrank Li phy_id = 0; 1890793fc096SFrank Li } 1891793fc096SFrank Li 1892407066f8SUwe Kleine-König snprintf(phy_name, sizeof(phy_name), 1893407066f8SUwe Kleine-König PHY_ID_FMT, mdio_bus_id, phy_id); 1894793fc096SFrank Li phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, 1895793fc096SFrank Li fep->phy_interface); 1896407066f8SUwe Kleine-König } 1897407066f8SUwe Kleine-König 1898793fc096SFrank Li if (IS_ERR(phy_dev)) { 189931b7720cSJoe Perches netdev_err(ndev, "could not attach to PHY\n"); 1900793fc096SFrank Li return PTR_ERR(phy_dev); 1901793fc096SFrank Li } 1902793fc096SFrank Li 1903793fc096SFrank Li /* mask with MAC supported features */ 19046b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_GBIT) { 1905793fc096SFrank Li phy_dev->supported &= PHY_GBIT_FEATURES; 1906b44592ffSRussell King phy_dev->supported &= ~SUPPORTED_1000baseT_Half; 1907d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 1908793fc096SFrank Li phy_dev->supported |= SUPPORTED_Pause; 1909d1391930SGuenter Roeck #endif 1910793fc096SFrank Li } 1911793fc096SFrank Li else 1912793fc096SFrank Li phy_dev->supported &= PHY_BASIC_FEATURES; 1913793fc096SFrank Li 1914793fc096SFrank Li phy_dev->advertising = phy_dev->supported; 1915793fc096SFrank Li 1916793fc096SFrank Li fep->phy_dev = phy_dev; 1917793fc096SFrank Li fep->link = 0; 1918793fc096SFrank Li fep->full_duplex = 0; 1919793fc096SFrank Li 19202220943aSAndrew Lunn phy_attached_info(phy_dev); 1921793fc096SFrank Li 1922793fc096SFrank Li return 0; 1923793fc096SFrank Li } 1924793fc096SFrank Li 1925793fc096SFrank Li static int fec_enet_mii_init(struct platform_device *pdev) 1926793fc096SFrank Li { 1927793fc096SFrank Li static struct mii_bus *fec0_mii_bus; 1928793fc096SFrank Li struct net_device *ndev = platform_get_drvdata(pdev); 1929793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1930407066f8SUwe Kleine-König struct device_node *node; 1931e7f4dc35SAndrew Lunn int err = -ENXIO; 193263c60732SUwe Kleine-König u32 mii_speed, holdtime; 1933793fc096SFrank Li 1934793fc096SFrank Li /* 19353d125f9cSStefan Agner * The i.MX28 dual fec interfaces are not equal. 1936793fc096SFrank Li * Here are the differences: 1937793fc096SFrank Li * 1938793fc096SFrank Li * - fec0 supports MII & RMII modes while fec1 only supports RMII 1939793fc096SFrank Li * - fec0 acts as the 1588 time master while fec1 is slave 1940793fc096SFrank Li * - external phys can only be configured by fec0 1941793fc096SFrank Li * 1942793fc096SFrank Li * That is to say fec1 can not work independently. It only works 1943793fc096SFrank Li * when fec0 is working. The reason behind this design is that the 1944793fc096SFrank Li * second interface is added primarily for Switch mode. 1945793fc096SFrank Li * 1946793fc096SFrank Li * Because of the last point above, both phys are attached on fec0 1947793fc096SFrank Li * mdio interface in board design, and need to be configured by 1948793fc096SFrank Li * fec0 mii_bus. 1949793fc096SFrank Li */ 19503d125f9cSStefan Agner if ((fep->quirks & FEC_QUIRK_SINGLE_MDIO) && fep->dev_id > 0) { 1951793fc096SFrank Li /* fec1 uses fec0 mii_bus */ 1952793fc096SFrank Li if (mii_cnt && fec0_mii_bus) { 1953793fc096SFrank Li fep->mii_bus = fec0_mii_bus; 1954793fc096SFrank Li mii_cnt++; 1955793fc096SFrank Li return 0; 1956793fc096SFrank Li } 1957793fc096SFrank Li return -ENOENT; 1958793fc096SFrank Li } 1959793fc096SFrank Li 1960793fc096SFrank Li fep->mii_timeout = 0; 1961793fc096SFrank Li 1962793fc096SFrank Li /* 1963793fc096SFrank Li * Set MII speed to 2.5 MHz (= clk_get_rate() / 2 * phy_speed) 1964793fc096SFrank Li * 1965793fc096SFrank Li * The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while 1966793fc096SFrank Li * for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'. The i.MX28 1967793fc096SFrank Li * Reference Manual has an error on this, and gets fixed on i.MX6Q 1968793fc096SFrank Li * document. 1969793fc096SFrank Li */ 197063c60732SUwe Kleine-König mii_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 5000000); 19716b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_ENET_MAC) 197263c60732SUwe Kleine-König mii_speed--; 197363c60732SUwe Kleine-König if (mii_speed > 63) { 197463c60732SUwe Kleine-König dev_err(&pdev->dev, 197563c60732SUwe Kleine-König "fec clock (%lu) to fast to get right mii speed\n", 197663c60732SUwe Kleine-König clk_get_rate(fep->clk_ipg)); 197763c60732SUwe Kleine-König err = -EINVAL; 197863c60732SUwe Kleine-König goto err_out; 197963c60732SUwe Kleine-König } 198063c60732SUwe Kleine-König 198163c60732SUwe Kleine-König /* 198263c60732SUwe Kleine-König * The i.MX28 and i.MX6 types have another filed in the MSCR (aka 198363c60732SUwe Kleine-König * MII_SPEED) register that defines the MDIO output hold time. Earlier 198463c60732SUwe Kleine-König * versions are RAZ there, so just ignore the difference and write the 198563c60732SUwe Kleine-König * register always. 198663c60732SUwe Kleine-König * The minimal hold time according to IEE802.3 (clause 22) is 10 ns. 198763c60732SUwe Kleine-König * HOLDTIME + 1 is the number of clk cycles the fec is holding the 198863c60732SUwe Kleine-König * output. 198963c60732SUwe Kleine-König * The HOLDTIME bitfield takes values between 0 and 7 (inclusive). 199063c60732SUwe Kleine-König * Given that ceil(clkrate / 5000000) <= 64, the calculation for 199163c60732SUwe Kleine-König * holdtime cannot result in a value greater than 3. 199263c60732SUwe Kleine-König */ 199363c60732SUwe Kleine-König holdtime = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 100000000) - 1; 199463c60732SUwe Kleine-König 199563c60732SUwe Kleine-König fep->phy_speed = mii_speed << 1 | holdtime << 8; 199663c60732SUwe Kleine-König 1997793fc096SFrank Li writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 1998793fc096SFrank Li 1999793fc096SFrank Li fep->mii_bus = mdiobus_alloc(); 2000793fc096SFrank Li if (fep->mii_bus == NULL) { 2001793fc096SFrank Li err = -ENOMEM; 2002793fc096SFrank Li goto err_out; 2003793fc096SFrank Li } 2004793fc096SFrank Li 2005793fc096SFrank Li fep->mii_bus->name = "fec_enet_mii_bus"; 2006793fc096SFrank Li fep->mii_bus->read = fec_enet_mdio_read; 2007793fc096SFrank Li fep->mii_bus->write = fec_enet_mdio_write; 2008793fc096SFrank Li snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", 2009793fc096SFrank Li pdev->name, fep->dev_id + 1); 2010793fc096SFrank Li fep->mii_bus->priv = fep; 2011793fc096SFrank Li fep->mii_bus->parent = &pdev->dev; 2012793fc096SFrank Li 2013407066f8SUwe Kleine-König node = of_get_child_by_name(pdev->dev.of_node, "mdio"); 2014407066f8SUwe Kleine-König if (node) { 2015407066f8SUwe Kleine-König err = of_mdiobus_register(fep->mii_bus, node); 2016407066f8SUwe Kleine-König of_node_put(node); 2017407066f8SUwe Kleine-König } else { 2018407066f8SUwe Kleine-König err = mdiobus_register(fep->mii_bus); 2019407066f8SUwe Kleine-König } 2020407066f8SUwe Kleine-König 2021407066f8SUwe Kleine-König if (err) 2022e7f4dc35SAndrew Lunn goto err_out_free_mdiobus; 2023793fc096SFrank Li 2024793fc096SFrank Li mii_cnt++; 2025793fc096SFrank Li 2026793fc096SFrank Li /* save fec0 mii_bus */ 20273d125f9cSStefan Agner if (fep->quirks & FEC_QUIRK_SINGLE_MDIO) 2028793fc096SFrank Li fec0_mii_bus = fep->mii_bus; 2029793fc096SFrank Li 2030793fc096SFrank Li return 0; 2031793fc096SFrank Li 2032793fc096SFrank Li err_out_free_mdiobus: 2033793fc096SFrank Li mdiobus_free(fep->mii_bus); 2034793fc096SFrank Li err_out: 2035793fc096SFrank Li return err; 2036793fc096SFrank Li } 2037793fc096SFrank Li 2038793fc096SFrank Li static void fec_enet_mii_remove(struct fec_enet_private *fep) 2039793fc096SFrank Li { 2040793fc096SFrank Li if (--mii_cnt == 0) { 2041793fc096SFrank Li mdiobus_unregister(fep->mii_bus); 2042793fc096SFrank Li mdiobus_free(fep->mii_bus); 2043793fc096SFrank Li } 2044793fc096SFrank Li } 2045793fc096SFrank Li 2046793fc096SFrank Li static int fec_enet_get_settings(struct net_device *ndev, 2047793fc096SFrank Li struct ethtool_cmd *cmd) 2048793fc096SFrank Li { 2049793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2050793fc096SFrank Li struct phy_device *phydev = fep->phy_dev; 2051793fc096SFrank Li 2052793fc096SFrank Li if (!phydev) 2053793fc096SFrank Li return -ENODEV; 2054793fc096SFrank Li 2055793fc096SFrank Li return phy_ethtool_gset(phydev, cmd); 2056793fc096SFrank Li } 2057793fc096SFrank Li 2058793fc096SFrank Li static int fec_enet_set_settings(struct net_device *ndev, 2059793fc096SFrank Li struct ethtool_cmd *cmd) 2060793fc096SFrank Li { 2061793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2062793fc096SFrank Li struct phy_device *phydev = fep->phy_dev; 2063793fc096SFrank Li 2064793fc096SFrank Li if (!phydev) 2065793fc096SFrank Li return -ENODEV; 2066793fc096SFrank Li 2067793fc096SFrank Li return phy_ethtool_sset(phydev, cmd); 2068793fc096SFrank Li } 2069793fc096SFrank Li 2070793fc096SFrank Li static void fec_enet_get_drvinfo(struct net_device *ndev, 2071793fc096SFrank Li struct ethtool_drvinfo *info) 2072793fc096SFrank Li { 2073793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2074793fc096SFrank Li 2075793fc096SFrank Li strlcpy(info->driver, fep->pdev->dev.driver->name, 2076793fc096SFrank Li sizeof(info->driver)); 2077793fc096SFrank Li strlcpy(info->version, "Revision: 1.0", sizeof(info->version)); 2078793fc096SFrank Li strlcpy(info->bus_info, dev_name(&ndev->dev), sizeof(info->bus_info)); 2079793fc096SFrank Li } 2080793fc096SFrank Li 2081db65f35fSPhilippe Reynes static int fec_enet_get_regs_len(struct net_device *ndev) 2082db65f35fSPhilippe Reynes { 2083db65f35fSPhilippe Reynes struct fec_enet_private *fep = netdev_priv(ndev); 2084db65f35fSPhilippe Reynes struct resource *r; 2085db65f35fSPhilippe Reynes int s = 0; 2086db65f35fSPhilippe Reynes 2087db65f35fSPhilippe Reynes r = platform_get_resource(fep->pdev, IORESOURCE_MEM, 0); 2088db65f35fSPhilippe Reynes if (r) 2089db65f35fSPhilippe Reynes s = resource_size(r); 2090db65f35fSPhilippe Reynes 2091db65f35fSPhilippe Reynes return s; 2092db65f35fSPhilippe Reynes } 2093db65f35fSPhilippe Reynes 2094db65f35fSPhilippe Reynes /* List of registers that can be safety be read to dump them with ethtool */ 2095db65f35fSPhilippe Reynes #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ 209605f3b50eSJohannes Berg defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) 2097db65f35fSPhilippe Reynes static u32 fec_enet_register_offset[] = { 2098db65f35fSPhilippe Reynes FEC_IEVENT, FEC_IMASK, FEC_R_DES_ACTIVE_0, FEC_X_DES_ACTIVE_0, 2099db65f35fSPhilippe Reynes FEC_ECNTRL, FEC_MII_DATA, FEC_MII_SPEED, FEC_MIB_CTRLSTAT, FEC_R_CNTRL, 2100db65f35fSPhilippe Reynes FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, FEC_OPD, FEC_TXIC0, FEC_TXIC1, 2101db65f35fSPhilippe Reynes FEC_TXIC2, FEC_RXIC0, FEC_RXIC1, FEC_RXIC2, FEC_HASH_TABLE_HIGH, 2102db65f35fSPhilippe Reynes FEC_HASH_TABLE_LOW, FEC_GRP_HASH_TABLE_HIGH, FEC_GRP_HASH_TABLE_LOW, 2103db65f35fSPhilippe Reynes FEC_X_WMRK, FEC_R_BOUND, FEC_R_FSTART, FEC_R_DES_START_1, 2104db65f35fSPhilippe Reynes FEC_X_DES_START_1, FEC_R_BUFF_SIZE_1, FEC_R_DES_START_2, 2105db65f35fSPhilippe Reynes FEC_X_DES_START_2, FEC_R_BUFF_SIZE_2, FEC_R_DES_START_0, 2106db65f35fSPhilippe Reynes FEC_X_DES_START_0, FEC_R_BUFF_SIZE_0, FEC_R_FIFO_RSFL, FEC_R_FIFO_RSEM, 2107db65f35fSPhilippe Reynes FEC_R_FIFO_RAEM, FEC_R_FIFO_RAFL, FEC_RACC, FEC_RCMR_1, FEC_RCMR_2, 2108db65f35fSPhilippe Reynes FEC_DMA_CFG_1, FEC_DMA_CFG_2, FEC_R_DES_ACTIVE_1, FEC_X_DES_ACTIVE_1, 2109db65f35fSPhilippe Reynes FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_2, FEC_QOS_SCHEME, 2110db65f35fSPhilippe Reynes RMON_T_DROP, RMON_T_PACKETS, RMON_T_BC_PKT, RMON_T_MC_PKT, 2111db65f35fSPhilippe Reynes RMON_T_CRC_ALIGN, RMON_T_UNDERSIZE, RMON_T_OVERSIZE, RMON_T_FRAG, 2112db65f35fSPhilippe Reynes RMON_T_JAB, RMON_T_COL, RMON_T_P64, RMON_T_P65TO127, RMON_T_P128TO255, 2113db65f35fSPhilippe Reynes RMON_T_P256TO511, RMON_T_P512TO1023, RMON_T_P1024TO2047, 2114db65f35fSPhilippe Reynes RMON_T_P_GTE2048, RMON_T_OCTETS, 2115db65f35fSPhilippe Reynes IEEE_T_DROP, IEEE_T_FRAME_OK, IEEE_T_1COL, IEEE_T_MCOL, IEEE_T_DEF, 2116db65f35fSPhilippe Reynes IEEE_T_LCOL, IEEE_T_EXCOL, IEEE_T_MACERR, IEEE_T_CSERR, IEEE_T_SQE, 2117db65f35fSPhilippe Reynes IEEE_T_FDXFC, IEEE_T_OCTETS_OK, 2118db65f35fSPhilippe Reynes RMON_R_PACKETS, RMON_R_BC_PKT, RMON_R_MC_PKT, RMON_R_CRC_ALIGN, 2119db65f35fSPhilippe Reynes RMON_R_UNDERSIZE, RMON_R_OVERSIZE, RMON_R_FRAG, RMON_R_JAB, 2120db65f35fSPhilippe Reynes RMON_R_RESVD_O, RMON_R_P64, RMON_R_P65TO127, RMON_R_P128TO255, 2121db65f35fSPhilippe Reynes RMON_R_P256TO511, RMON_R_P512TO1023, RMON_R_P1024TO2047, 2122db65f35fSPhilippe Reynes RMON_R_P_GTE2048, RMON_R_OCTETS, 2123db65f35fSPhilippe Reynes IEEE_R_DROP, IEEE_R_FRAME_OK, IEEE_R_CRC, IEEE_R_ALIGN, IEEE_R_MACERR, 2124db65f35fSPhilippe Reynes IEEE_R_FDXFC, IEEE_R_OCTETS_OK 2125db65f35fSPhilippe Reynes }; 2126db65f35fSPhilippe Reynes #else 2127db65f35fSPhilippe Reynes static u32 fec_enet_register_offset[] = { 2128db65f35fSPhilippe Reynes FEC_ECNTRL, FEC_IEVENT, FEC_IMASK, FEC_IVEC, FEC_R_DES_ACTIVE_0, 2129db65f35fSPhilippe Reynes FEC_R_DES_ACTIVE_1, FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_0, 2130db65f35fSPhilippe Reynes FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2, FEC_MII_DATA, FEC_MII_SPEED, 2131db65f35fSPhilippe Reynes FEC_R_BOUND, FEC_R_FSTART, FEC_X_WMRK, FEC_X_FSTART, FEC_R_CNTRL, 2132db65f35fSPhilippe Reynes FEC_MAX_FRM_LEN, FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, 2133db65f35fSPhilippe Reynes FEC_GRP_HASH_TABLE_HIGH, FEC_GRP_HASH_TABLE_LOW, FEC_R_DES_START_0, 2134db65f35fSPhilippe Reynes FEC_R_DES_START_1, FEC_R_DES_START_2, FEC_X_DES_START_0, 2135db65f35fSPhilippe Reynes FEC_X_DES_START_1, FEC_X_DES_START_2, FEC_R_BUFF_SIZE_0, 2136db65f35fSPhilippe Reynes FEC_R_BUFF_SIZE_1, FEC_R_BUFF_SIZE_2 2137db65f35fSPhilippe Reynes }; 2138db65f35fSPhilippe Reynes #endif 2139db65f35fSPhilippe Reynes 2140db65f35fSPhilippe Reynes static void fec_enet_get_regs(struct net_device *ndev, 2141db65f35fSPhilippe Reynes struct ethtool_regs *regs, void *regbuf) 2142db65f35fSPhilippe Reynes { 2143db65f35fSPhilippe Reynes struct fec_enet_private *fep = netdev_priv(ndev); 2144db65f35fSPhilippe Reynes u32 __iomem *theregs = (u32 __iomem *)fep->hwp; 2145db65f35fSPhilippe Reynes u32 *buf = (u32 *)regbuf; 2146db65f35fSPhilippe Reynes u32 i, off; 2147db65f35fSPhilippe Reynes 2148db65f35fSPhilippe Reynes memset(buf, 0, regs->len); 2149db65f35fSPhilippe Reynes 2150db65f35fSPhilippe Reynes for (i = 0; i < ARRAY_SIZE(fec_enet_register_offset); i++) { 2151db65f35fSPhilippe Reynes off = fec_enet_register_offset[i] / 4; 2152db65f35fSPhilippe Reynes buf[off] = readl(&theregs[off]); 2153db65f35fSPhilippe Reynes } 2154db65f35fSPhilippe Reynes } 2155db65f35fSPhilippe Reynes 2156793fc096SFrank Li static int fec_enet_get_ts_info(struct net_device *ndev, 2157793fc096SFrank Li struct ethtool_ts_info *info) 2158793fc096SFrank Li { 2159793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2160793fc096SFrank Li 2161793fc096SFrank Li if (fep->bufdesc_ex) { 2162793fc096SFrank Li 2163793fc096SFrank Li info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | 2164793fc096SFrank Li SOF_TIMESTAMPING_RX_SOFTWARE | 2165793fc096SFrank Li SOF_TIMESTAMPING_SOFTWARE | 2166793fc096SFrank Li SOF_TIMESTAMPING_TX_HARDWARE | 2167793fc096SFrank Li SOF_TIMESTAMPING_RX_HARDWARE | 2168793fc096SFrank Li SOF_TIMESTAMPING_RAW_HARDWARE; 2169793fc096SFrank Li if (fep->ptp_clock) 2170793fc096SFrank Li info->phc_index = ptp_clock_index(fep->ptp_clock); 2171793fc096SFrank Li else 2172793fc096SFrank Li info->phc_index = -1; 2173793fc096SFrank Li 2174793fc096SFrank Li info->tx_types = (1 << HWTSTAMP_TX_OFF) | 2175793fc096SFrank Li (1 << HWTSTAMP_TX_ON); 2176793fc096SFrank Li 2177793fc096SFrank Li info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | 2178793fc096SFrank Li (1 << HWTSTAMP_FILTER_ALL); 2179793fc096SFrank Li return 0; 2180793fc096SFrank Li } else { 2181793fc096SFrank Li return ethtool_op_get_ts_info(ndev, info); 2182793fc096SFrank Li } 2183793fc096SFrank Li } 2184793fc096SFrank Li 2185d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 2186d1391930SGuenter Roeck 2187793fc096SFrank Li static void fec_enet_get_pauseparam(struct net_device *ndev, 2188793fc096SFrank Li struct ethtool_pauseparam *pause) 2189793fc096SFrank Li { 2190793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2191793fc096SFrank Li 2192793fc096SFrank Li pause->autoneg = (fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) != 0; 2193793fc096SFrank Li pause->tx_pause = (fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) != 0; 2194793fc096SFrank Li pause->rx_pause = pause->tx_pause; 2195793fc096SFrank Li } 2196793fc096SFrank Li 2197793fc096SFrank Li static int fec_enet_set_pauseparam(struct net_device *ndev, 2198793fc096SFrank Li struct ethtool_pauseparam *pause) 2199793fc096SFrank Li { 2200793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2201793fc096SFrank Li 22020b146ca8SRussell King if (!fep->phy_dev) 22030b146ca8SRussell King return -ENODEV; 22040b146ca8SRussell King 2205793fc096SFrank Li if (pause->tx_pause != pause->rx_pause) { 2206793fc096SFrank Li netdev_info(ndev, 2207793fc096SFrank Li "hardware only support enable/disable both tx and rx"); 2208793fc096SFrank Li return -EINVAL; 2209793fc096SFrank Li } 2210793fc096SFrank Li 2211793fc096SFrank Li fep->pause_flag = 0; 2212793fc096SFrank Li 2213793fc096SFrank Li /* tx pause must be same as rx pause */ 2214793fc096SFrank Li fep->pause_flag |= pause->rx_pause ? FEC_PAUSE_FLAG_ENABLE : 0; 2215793fc096SFrank Li fep->pause_flag |= pause->autoneg ? FEC_PAUSE_FLAG_AUTONEG : 0; 2216793fc096SFrank Li 2217793fc096SFrank Li if (pause->rx_pause || pause->autoneg) { 2218793fc096SFrank Li fep->phy_dev->supported |= ADVERTISED_Pause; 2219793fc096SFrank Li fep->phy_dev->advertising |= ADVERTISED_Pause; 2220793fc096SFrank Li } else { 2221793fc096SFrank Li fep->phy_dev->supported &= ~ADVERTISED_Pause; 2222793fc096SFrank Li fep->phy_dev->advertising &= ~ADVERTISED_Pause; 2223793fc096SFrank Li } 2224793fc096SFrank Li 2225793fc096SFrank Li if (pause->autoneg) { 2226793fc096SFrank Li if (netif_running(ndev)) 2227793fc096SFrank Li fec_stop(ndev); 2228793fc096SFrank Li phy_start_aneg(fep->phy_dev); 2229793fc096SFrank Li } 2230dbc64a8eSRussell King if (netif_running(ndev)) { 2231dbc64a8eSRussell King napi_disable(&fep->napi); 2232dbc64a8eSRussell King netif_tx_lock_bh(ndev); 2233ef83337dSRussell King fec_restart(ndev); 2234dbc64a8eSRussell King netif_wake_queue(ndev); 22356af42d42SRussell King netif_tx_unlock_bh(ndev); 2236dbc64a8eSRussell King napi_enable(&fep->napi); 2237dbc64a8eSRussell King } 2238793fc096SFrank Li 2239793fc096SFrank Li return 0; 2240793fc096SFrank Li } 2241793fc096SFrank Li 224238ae92dcSChris Healy static const struct fec_stat { 224338ae92dcSChris Healy char name[ETH_GSTRING_LEN]; 224438ae92dcSChris Healy u16 offset; 224538ae92dcSChris Healy } fec_stats[] = { 224638ae92dcSChris Healy /* RMON TX */ 224738ae92dcSChris Healy { "tx_dropped", RMON_T_DROP }, 224838ae92dcSChris Healy { "tx_packets", RMON_T_PACKETS }, 224938ae92dcSChris Healy { "tx_broadcast", RMON_T_BC_PKT }, 225038ae92dcSChris Healy { "tx_multicast", RMON_T_MC_PKT }, 225138ae92dcSChris Healy { "tx_crc_errors", RMON_T_CRC_ALIGN }, 225238ae92dcSChris Healy { "tx_undersize", RMON_T_UNDERSIZE }, 225338ae92dcSChris Healy { "tx_oversize", RMON_T_OVERSIZE }, 225438ae92dcSChris Healy { "tx_fragment", RMON_T_FRAG }, 225538ae92dcSChris Healy { "tx_jabber", RMON_T_JAB }, 225638ae92dcSChris Healy { "tx_collision", RMON_T_COL }, 225738ae92dcSChris Healy { "tx_64byte", RMON_T_P64 }, 225838ae92dcSChris Healy { "tx_65to127byte", RMON_T_P65TO127 }, 225938ae92dcSChris Healy { "tx_128to255byte", RMON_T_P128TO255 }, 226038ae92dcSChris Healy { "tx_256to511byte", RMON_T_P256TO511 }, 226138ae92dcSChris Healy { "tx_512to1023byte", RMON_T_P512TO1023 }, 226238ae92dcSChris Healy { "tx_1024to2047byte", RMON_T_P1024TO2047 }, 226338ae92dcSChris Healy { "tx_GTE2048byte", RMON_T_P_GTE2048 }, 226438ae92dcSChris Healy { "tx_octets", RMON_T_OCTETS }, 226538ae92dcSChris Healy 226638ae92dcSChris Healy /* IEEE TX */ 226738ae92dcSChris Healy { "IEEE_tx_drop", IEEE_T_DROP }, 226838ae92dcSChris Healy { "IEEE_tx_frame_ok", IEEE_T_FRAME_OK }, 226938ae92dcSChris Healy { "IEEE_tx_1col", IEEE_T_1COL }, 227038ae92dcSChris Healy { "IEEE_tx_mcol", IEEE_T_MCOL }, 227138ae92dcSChris Healy { "IEEE_tx_def", IEEE_T_DEF }, 227238ae92dcSChris Healy { "IEEE_tx_lcol", IEEE_T_LCOL }, 227338ae92dcSChris Healy { "IEEE_tx_excol", IEEE_T_EXCOL }, 227438ae92dcSChris Healy { "IEEE_tx_macerr", IEEE_T_MACERR }, 227538ae92dcSChris Healy { "IEEE_tx_cserr", IEEE_T_CSERR }, 227638ae92dcSChris Healy { "IEEE_tx_sqe", IEEE_T_SQE }, 227738ae92dcSChris Healy { "IEEE_tx_fdxfc", IEEE_T_FDXFC }, 227838ae92dcSChris Healy { "IEEE_tx_octets_ok", IEEE_T_OCTETS_OK }, 227938ae92dcSChris Healy 228038ae92dcSChris Healy /* RMON RX */ 228138ae92dcSChris Healy { "rx_packets", RMON_R_PACKETS }, 228238ae92dcSChris Healy { "rx_broadcast", RMON_R_BC_PKT }, 228338ae92dcSChris Healy { "rx_multicast", RMON_R_MC_PKT }, 228438ae92dcSChris Healy { "rx_crc_errors", RMON_R_CRC_ALIGN }, 228538ae92dcSChris Healy { "rx_undersize", RMON_R_UNDERSIZE }, 228638ae92dcSChris Healy { "rx_oversize", RMON_R_OVERSIZE }, 228738ae92dcSChris Healy { "rx_fragment", RMON_R_FRAG }, 228838ae92dcSChris Healy { "rx_jabber", RMON_R_JAB }, 228938ae92dcSChris Healy { "rx_64byte", RMON_R_P64 }, 229038ae92dcSChris Healy { "rx_65to127byte", RMON_R_P65TO127 }, 229138ae92dcSChris Healy { "rx_128to255byte", RMON_R_P128TO255 }, 229238ae92dcSChris Healy { "rx_256to511byte", RMON_R_P256TO511 }, 229338ae92dcSChris Healy { "rx_512to1023byte", RMON_R_P512TO1023 }, 229438ae92dcSChris Healy { "rx_1024to2047byte", RMON_R_P1024TO2047 }, 229538ae92dcSChris Healy { "rx_GTE2048byte", RMON_R_P_GTE2048 }, 229638ae92dcSChris Healy { "rx_octets", RMON_R_OCTETS }, 229738ae92dcSChris Healy 229838ae92dcSChris Healy /* IEEE RX */ 229938ae92dcSChris Healy { "IEEE_rx_drop", IEEE_R_DROP }, 230038ae92dcSChris Healy { "IEEE_rx_frame_ok", IEEE_R_FRAME_OK }, 230138ae92dcSChris Healy { "IEEE_rx_crc", IEEE_R_CRC }, 230238ae92dcSChris Healy { "IEEE_rx_align", IEEE_R_ALIGN }, 230338ae92dcSChris Healy { "IEEE_rx_macerr", IEEE_R_MACERR }, 230438ae92dcSChris Healy { "IEEE_rx_fdxfc", IEEE_R_FDXFC }, 230538ae92dcSChris Healy { "IEEE_rx_octets_ok", IEEE_R_OCTETS_OK }, 230638ae92dcSChris Healy }; 230738ae92dcSChris Healy 230838ae92dcSChris Healy static void fec_enet_get_ethtool_stats(struct net_device *dev, 230938ae92dcSChris Healy struct ethtool_stats *stats, u64 *data) 231038ae92dcSChris Healy { 231138ae92dcSChris Healy struct fec_enet_private *fep = netdev_priv(dev); 231238ae92dcSChris Healy int i; 231338ae92dcSChris Healy 231438ae92dcSChris Healy for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 231538ae92dcSChris Healy data[i] = readl(fep->hwp + fec_stats[i].offset); 231638ae92dcSChris Healy } 231738ae92dcSChris Healy 231838ae92dcSChris Healy static void fec_enet_get_strings(struct net_device *netdev, 231938ae92dcSChris Healy u32 stringset, u8 *data) 232038ae92dcSChris Healy { 232138ae92dcSChris Healy int i; 232238ae92dcSChris Healy switch (stringset) { 232338ae92dcSChris Healy case ETH_SS_STATS: 232438ae92dcSChris Healy for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 232538ae92dcSChris Healy memcpy(data + i * ETH_GSTRING_LEN, 232638ae92dcSChris Healy fec_stats[i].name, ETH_GSTRING_LEN); 232738ae92dcSChris Healy break; 232838ae92dcSChris Healy } 232938ae92dcSChris Healy } 233038ae92dcSChris Healy 233138ae92dcSChris Healy static int fec_enet_get_sset_count(struct net_device *dev, int sset) 233238ae92dcSChris Healy { 233338ae92dcSChris Healy switch (sset) { 233438ae92dcSChris Healy case ETH_SS_STATS: 233538ae92dcSChris Healy return ARRAY_SIZE(fec_stats); 233638ae92dcSChris Healy default: 233738ae92dcSChris Healy return -EOPNOTSUPP; 233838ae92dcSChris Healy } 233938ae92dcSChris Healy } 2340d1391930SGuenter Roeck #endif /* !defined(CONFIG_M5272) */ 234138ae92dcSChris Healy 234232bc9b46SChris Healy static int fec_enet_nway_reset(struct net_device *dev) 234332bc9b46SChris Healy { 234432bc9b46SChris Healy struct fec_enet_private *fep = netdev_priv(dev); 234532bc9b46SChris Healy struct phy_device *phydev = fep->phy_dev; 234632bc9b46SChris Healy 234732bc9b46SChris Healy if (!phydev) 234832bc9b46SChris Healy return -ENODEV; 234932bc9b46SChris Healy 235032bc9b46SChris Healy return genphy_restart_aneg(phydev); 235132bc9b46SChris Healy } 235232bc9b46SChris Healy 2353d851b47bSFugang Duan /* ITR clock source is enet system clock (clk_ahb). 2354d851b47bSFugang Duan * TCTT unit is cycle_ns * 64 cycle 2355d851b47bSFugang Duan * So, the ICTT value = X us / (cycle_ns * 64) 2356d851b47bSFugang Duan */ 2357d851b47bSFugang Duan static int fec_enet_us_to_itr_clock(struct net_device *ndev, int us) 2358d851b47bSFugang Duan { 2359d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2360d851b47bSFugang Duan 2361d851b47bSFugang Duan return us * (fep->itr_clk_rate / 64000) / 1000; 2362d851b47bSFugang Duan } 2363d851b47bSFugang Duan 2364d851b47bSFugang Duan /* Set threshold for interrupt coalescing */ 2365d851b47bSFugang Duan static void fec_enet_itr_coal_set(struct net_device *ndev) 2366d851b47bSFugang Duan { 2367d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2368d851b47bSFugang Duan int rx_itr, tx_itr; 2369d851b47bSFugang Duan 23706b7e4008SLothar Waßmann if (!(fep->quirks & FEC_QUIRK_HAS_AVB)) 2371d851b47bSFugang Duan return; 2372d851b47bSFugang Duan 2373d851b47bSFugang Duan /* Must be greater than zero to avoid unpredictable behavior */ 2374d851b47bSFugang Duan if (!fep->rx_time_itr || !fep->rx_pkts_itr || 2375d851b47bSFugang Duan !fep->tx_time_itr || !fep->tx_pkts_itr) 2376d851b47bSFugang Duan return; 2377d851b47bSFugang Duan 2378d851b47bSFugang Duan /* Select enet system clock as Interrupt Coalescing 2379d851b47bSFugang Duan * timer Clock Source 2380d851b47bSFugang Duan */ 2381d851b47bSFugang Duan rx_itr = FEC_ITR_CLK_SEL; 2382d851b47bSFugang Duan tx_itr = FEC_ITR_CLK_SEL; 2383d851b47bSFugang Duan 2384d851b47bSFugang Duan /* set ICFT and ICTT */ 2385d851b47bSFugang Duan rx_itr |= FEC_ITR_ICFT(fep->rx_pkts_itr); 2386d851b47bSFugang Duan rx_itr |= FEC_ITR_ICTT(fec_enet_us_to_itr_clock(ndev, fep->rx_time_itr)); 2387d851b47bSFugang Duan tx_itr |= FEC_ITR_ICFT(fep->tx_pkts_itr); 2388d851b47bSFugang Duan tx_itr |= FEC_ITR_ICTT(fec_enet_us_to_itr_clock(ndev, fep->tx_time_itr)); 2389d851b47bSFugang Duan 2390d851b47bSFugang Duan rx_itr |= FEC_ITR_EN; 2391d851b47bSFugang Duan tx_itr |= FEC_ITR_EN; 2392d851b47bSFugang Duan 2393d851b47bSFugang Duan writel(tx_itr, fep->hwp + FEC_TXIC0); 2394d851b47bSFugang Duan writel(rx_itr, fep->hwp + FEC_RXIC0); 2395d851b47bSFugang Duan writel(tx_itr, fep->hwp + FEC_TXIC1); 2396d851b47bSFugang Duan writel(rx_itr, fep->hwp + FEC_RXIC1); 2397d851b47bSFugang Duan writel(tx_itr, fep->hwp + FEC_TXIC2); 2398d851b47bSFugang Duan writel(rx_itr, fep->hwp + FEC_RXIC2); 2399d851b47bSFugang Duan } 2400d851b47bSFugang Duan 2401d851b47bSFugang Duan static int 2402d851b47bSFugang Duan fec_enet_get_coalesce(struct net_device *ndev, struct ethtool_coalesce *ec) 2403d851b47bSFugang Duan { 2404d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2405d851b47bSFugang Duan 24066b7e4008SLothar Waßmann if (!(fep->quirks & FEC_QUIRK_HAS_AVB)) 2407d851b47bSFugang Duan return -EOPNOTSUPP; 2408d851b47bSFugang Duan 2409d851b47bSFugang Duan ec->rx_coalesce_usecs = fep->rx_time_itr; 2410d851b47bSFugang Duan ec->rx_max_coalesced_frames = fep->rx_pkts_itr; 2411d851b47bSFugang Duan 2412d851b47bSFugang Duan ec->tx_coalesce_usecs = fep->tx_time_itr; 2413d851b47bSFugang Duan ec->tx_max_coalesced_frames = fep->tx_pkts_itr; 2414d851b47bSFugang Duan 2415d851b47bSFugang Duan return 0; 2416d851b47bSFugang Duan } 2417d851b47bSFugang Duan 2418d851b47bSFugang Duan static int 2419d851b47bSFugang Duan fec_enet_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *ec) 2420d851b47bSFugang Duan { 2421d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2422d851b47bSFugang Duan unsigned int cycle; 2423d851b47bSFugang Duan 24246b7e4008SLothar Waßmann if (!(fep->quirks & FEC_QUIRK_HAS_AVB)) 2425d851b47bSFugang Duan return -EOPNOTSUPP; 2426d851b47bSFugang Duan 2427d851b47bSFugang Duan if (ec->rx_max_coalesced_frames > 255) { 2428d851b47bSFugang Duan pr_err("Rx coalesced frames exceed hardware limiation"); 2429d851b47bSFugang Duan return -EINVAL; 2430d851b47bSFugang Duan } 2431d851b47bSFugang Duan 2432d851b47bSFugang Duan if (ec->tx_max_coalesced_frames > 255) { 2433d851b47bSFugang Duan pr_err("Tx coalesced frame exceed hardware limiation"); 2434d851b47bSFugang Duan return -EINVAL; 2435d851b47bSFugang Duan } 2436d851b47bSFugang Duan 2437d851b47bSFugang Duan cycle = fec_enet_us_to_itr_clock(ndev, fep->rx_time_itr); 2438d851b47bSFugang Duan if (cycle > 0xFFFF) { 2439d851b47bSFugang Duan pr_err("Rx coalesed usec exceeed hardware limiation"); 2440d851b47bSFugang Duan return -EINVAL; 2441d851b47bSFugang Duan } 2442d851b47bSFugang Duan 2443d851b47bSFugang Duan cycle = fec_enet_us_to_itr_clock(ndev, fep->tx_time_itr); 2444d851b47bSFugang Duan if (cycle > 0xFFFF) { 2445d851b47bSFugang Duan pr_err("Rx coalesed usec exceeed hardware limiation"); 2446d851b47bSFugang Duan return -EINVAL; 2447d851b47bSFugang Duan } 2448d851b47bSFugang Duan 2449d851b47bSFugang Duan fep->rx_time_itr = ec->rx_coalesce_usecs; 2450d851b47bSFugang Duan fep->rx_pkts_itr = ec->rx_max_coalesced_frames; 2451d851b47bSFugang Duan 2452d851b47bSFugang Duan fep->tx_time_itr = ec->tx_coalesce_usecs; 2453d851b47bSFugang Duan fep->tx_pkts_itr = ec->tx_max_coalesced_frames; 2454d851b47bSFugang Duan 2455d851b47bSFugang Duan fec_enet_itr_coal_set(ndev); 2456d851b47bSFugang Duan 2457d851b47bSFugang Duan return 0; 2458d851b47bSFugang Duan } 2459d851b47bSFugang Duan 2460d851b47bSFugang Duan static void fec_enet_itr_coal_init(struct net_device *ndev) 2461d851b47bSFugang Duan { 2462d851b47bSFugang Duan struct ethtool_coalesce ec; 2463d851b47bSFugang Duan 2464d851b47bSFugang Duan ec.rx_coalesce_usecs = FEC_ITR_ICTT_DEFAULT; 2465d851b47bSFugang Duan ec.rx_max_coalesced_frames = FEC_ITR_ICFT_DEFAULT; 2466d851b47bSFugang Duan 2467d851b47bSFugang Duan ec.tx_coalesce_usecs = FEC_ITR_ICTT_DEFAULT; 2468d851b47bSFugang Duan ec.tx_max_coalesced_frames = FEC_ITR_ICFT_DEFAULT; 2469d851b47bSFugang Duan 2470d851b47bSFugang Duan fec_enet_set_coalesce(ndev, &ec); 2471d851b47bSFugang Duan } 2472d851b47bSFugang Duan 24731b7bde6dSNimrod Andy static int fec_enet_get_tunable(struct net_device *netdev, 24741b7bde6dSNimrod Andy const struct ethtool_tunable *tuna, 24751b7bde6dSNimrod Andy void *data) 24761b7bde6dSNimrod Andy { 24771b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(netdev); 24781b7bde6dSNimrod Andy int ret = 0; 24791b7bde6dSNimrod Andy 24801b7bde6dSNimrod Andy switch (tuna->id) { 24811b7bde6dSNimrod Andy case ETHTOOL_RX_COPYBREAK: 24821b7bde6dSNimrod Andy *(u32 *)data = fep->rx_copybreak; 24831b7bde6dSNimrod Andy break; 24841b7bde6dSNimrod Andy default: 24851b7bde6dSNimrod Andy ret = -EINVAL; 24861b7bde6dSNimrod Andy break; 24871b7bde6dSNimrod Andy } 24881b7bde6dSNimrod Andy 24891b7bde6dSNimrod Andy return ret; 24901b7bde6dSNimrod Andy } 24911b7bde6dSNimrod Andy 24921b7bde6dSNimrod Andy static int fec_enet_set_tunable(struct net_device *netdev, 24931b7bde6dSNimrod Andy const struct ethtool_tunable *tuna, 24941b7bde6dSNimrod Andy const void *data) 24951b7bde6dSNimrod Andy { 24961b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(netdev); 24971b7bde6dSNimrod Andy int ret = 0; 24981b7bde6dSNimrod Andy 24991b7bde6dSNimrod Andy switch (tuna->id) { 25001b7bde6dSNimrod Andy case ETHTOOL_RX_COPYBREAK: 25011b7bde6dSNimrod Andy fep->rx_copybreak = *(u32 *)data; 25021b7bde6dSNimrod Andy break; 25031b7bde6dSNimrod Andy default: 25041b7bde6dSNimrod Andy ret = -EINVAL; 25051b7bde6dSNimrod Andy break; 25061b7bde6dSNimrod Andy } 25071b7bde6dSNimrod Andy 25081b7bde6dSNimrod Andy return ret; 25091b7bde6dSNimrod Andy } 25101b7bde6dSNimrod Andy 2511de40ed31SNimrod Andy static void 2512de40ed31SNimrod Andy fec_enet_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) 2513de40ed31SNimrod Andy { 2514de40ed31SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 2515de40ed31SNimrod Andy 2516de40ed31SNimrod Andy if (fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET) { 2517de40ed31SNimrod Andy wol->supported = WAKE_MAGIC; 2518de40ed31SNimrod Andy wol->wolopts = fep->wol_flag & FEC_WOL_FLAG_ENABLE ? WAKE_MAGIC : 0; 2519de40ed31SNimrod Andy } else { 2520de40ed31SNimrod Andy wol->supported = wol->wolopts = 0; 2521de40ed31SNimrod Andy } 2522de40ed31SNimrod Andy } 2523de40ed31SNimrod Andy 2524de40ed31SNimrod Andy static int 2525de40ed31SNimrod Andy fec_enet_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) 2526de40ed31SNimrod Andy { 2527de40ed31SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 2528de40ed31SNimrod Andy 2529de40ed31SNimrod Andy if (!(fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET)) 2530de40ed31SNimrod Andy return -EINVAL; 2531de40ed31SNimrod Andy 2532de40ed31SNimrod Andy if (wol->wolopts & ~WAKE_MAGIC) 2533de40ed31SNimrod Andy return -EINVAL; 2534de40ed31SNimrod Andy 2535de40ed31SNimrod Andy device_set_wakeup_enable(&ndev->dev, wol->wolopts & WAKE_MAGIC); 2536de40ed31SNimrod Andy if (device_may_wakeup(&ndev->dev)) { 2537de40ed31SNimrod Andy fep->wol_flag |= FEC_WOL_FLAG_ENABLE; 2538de40ed31SNimrod Andy if (fep->irq[0] > 0) 2539de40ed31SNimrod Andy enable_irq_wake(fep->irq[0]); 2540de40ed31SNimrod Andy } else { 2541de40ed31SNimrod Andy fep->wol_flag &= (~FEC_WOL_FLAG_ENABLE); 2542de40ed31SNimrod Andy if (fep->irq[0] > 0) 2543de40ed31SNimrod Andy disable_irq_wake(fep->irq[0]); 2544de40ed31SNimrod Andy } 2545de40ed31SNimrod Andy 2546de40ed31SNimrod Andy return 0; 2547de40ed31SNimrod Andy } 2548de40ed31SNimrod Andy 2549793fc096SFrank Li static const struct ethtool_ops fec_enet_ethtool_ops = { 2550793fc096SFrank Li .get_settings = fec_enet_get_settings, 2551793fc096SFrank Li .set_settings = fec_enet_set_settings, 2552793fc096SFrank Li .get_drvinfo = fec_enet_get_drvinfo, 2553db65f35fSPhilippe Reynes .get_regs_len = fec_enet_get_regs_len, 2554db65f35fSPhilippe Reynes .get_regs = fec_enet_get_regs, 255532bc9b46SChris Healy .nway_reset = fec_enet_nway_reset, 2556c1d7c48fSRussell King .get_link = ethtool_op_get_link, 2557d851b47bSFugang Duan .get_coalesce = fec_enet_get_coalesce, 2558d851b47bSFugang Duan .set_coalesce = fec_enet_set_coalesce, 255938ae92dcSChris Healy #ifndef CONFIG_M5272 2560c1d7c48fSRussell King .get_pauseparam = fec_enet_get_pauseparam, 2561c1d7c48fSRussell King .set_pauseparam = fec_enet_set_pauseparam, 256238ae92dcSChris Healy .get_strings = fec_enet_get_strings, 2563c1d7c48fSRussell King .get_ethtool_stats = fec_enet_get_ethtool_stats, 256438ae92dcSChris Healy .get_sset_count = fec_enet_get_sset_count, 256538ae92dcSChris Healy #endif 2566c1d7c48fSRussell King .get_ts_info = fec_enet_get_ts_info, 25671b7bde6dSNimrod Andy .get_tunable = fec_enet_get_tunable, 25681b7bde6dSNimrod Andy .set_tunable = fec_enet_set_tunable, 2569de40ed31SNimrod Andy .get_wol = fec_enet_get_wol, 2570de40ed31SNimrod Andy .set_wol = fec_enet_set_wol, 2571793fc096SFrank Li }; 2572793fc096SFrank Li 2573793fc096SFrank Li static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) 2574793fc096SFrank Li { 2575793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2576793fc096SFrank Li struct phy_device *phydev = fep->phy_dev; 2577793fc096SFrank Li 2578793fc096SFrank Li if (!netif_running(ndev)) 2579793fc096SFrank Li return -EINVAL; 2580793fc096SFrank Li 2581793fc096SFrank Li if (!phydev) 2582793fc096SFrank Li return -ENODEV; 2583793fc096SFrank Li 25841d5244d0SBen Hutchings if (fep->bufdesc_ex) { 25851d5244d0SBen Hutchings if (cmd == SIOCSHWTSTAMP) 25861d5244d0SBen Hutchings return fec_ptp_set(ndev, rq); 25871d5244d0SBen Hutchings if (cmd == SIOCGHWTSTAMP) 25881d5244d0SBen Hutchings return fec_ptp_get(ndev, rq); 25891d5244d0SBen Hutchings } 2590793fc096SFrank Li 2591793fc096SFrank Li return phy_mii_ioctl(phydev, rq, cmd); 2592793fc096SFrank Li } 2593793fc096SFrank Li 2594793fc096SFrank Li static void fec_enet_free_buffers(struct net_device *ndev) 2595793fc096SFrank Li { 2596793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2597793fc096SFrank Li unsigned int i; 2598793fc096SFrank Li struct sk_buff *skb; 2599793fc096SFrank Li struct bufdesc *bdp; 26004d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 26014d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 260259d0f746SFrank Li unsigned int q; 2603793fc096SFrank Li 260459d0f746SFrank Li for (q = 0; q < fep->num_rx_queues; q++) { 260559d0f746SFrank Li rxq = fep->rx_queue[q]; 26067355f276STroy Kisky bdp = rxq->bd.base; 26077355f276STroy Kisky for (i = 0; i < rxq->bd.ring_size; i++) { 26084d494cdcSFugang Duan skb = rxq->rx_skbuff[i]; 26094d494cdcSFugang Duan rxq->rx_skbuff[i] = NULL; 2610730ee360SRussell King if (skb) { 261159d0f746SFrank Li dma_unmap_single(&fep->pdev->dev, 26125cfa3039SJohannes Berg fec32_to_cpu(bdp->cbd_bufaddr), 2613b64bf4b7SNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 261459d0f746SFrank Li DMA_FROM_DEVICE); 2615793fc096SFrank Li dev_kfree_skb(skb); 2616730ee360SRussell King } 26177355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 261859d0f746SFrank Li } 2619793fc096SFrank Li } 2620793fc096SFrank Li 262159d0f746SFrank Li for (q = 0; q < fep->num_tx_queues; q++) { 262259d0f746SFrank Li txq = fep->tx_queue[q]; 26237355f276STroy Kisky bdp = txq->bd.base; 26247355f276STroy Kisky for (i = 0; i < txq->bd.ring_size; i++) { 26254d494cdcSFugang Duan kfree(txq->tx_bounce[i]); 26264d494cdcSFugang Duan txq->tx_bounce[i] = NULL; 26274d494cdcSFugang Duan skb = txq->tx_skbuff[i]; 26284d494cdcSFugang Duan txq->tx_skbuff[i] = NULL; 26298b7c9efaSRussell King dev_kfree_skb(skb); 26308b7c9efaSRussell King } 2631793fc096SFrank Li } 263259d0f746SFrank Li } 2633793fc096SFrank Li 263459d0f746SFrank Li static void fec_enet_free_queue(struct net_device *ndev) 263559d0f746SFrank Li { 263659d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 263759d0f746SFrank Li int i; 263859d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 263959d0f746SFrank Li 264059d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) 264159d0f746SFrank Li if (fep->tx_queue[i] && fep->tx_queue[i]->tso_hdrs) { 264259d0f746SFrank Li txq = fep->tx_queue[i]; 264359d0f746SFrank Li dma_free_coherent(NULL, 26447355f276STroy Kisky txq->bd.ring_size * TSO_HEADER_SIZE, 264559d0f746SFrank Li txq->tso_hdrs, 264659d0f746SFrank Li txq->tso_hdrs_dma); 264759d0f746SFrank Li } 264859d0f746SFrank Li 264959d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) 265059d0f746SFrank Li kfree(fep->rx_queue[i]); 265159d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) 265259d0f746SFrank Li kfree(fep->tx_queue[i]); 265359d0f746SFrank Li } 265459d0f746SFrank Li 265559d0f746SFrank Li static int fec_enet_alloc_queue(struct net_device *ndev) 265659d0f746SFrank Li { 265759d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 265859d0f746SFrank Li int i; 265959d0f746SFrank Li int ret = 0; 266059d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 266159d0f746SFrank Li 266259d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 266359d0f746SFrank Li txq = kzalloc(sizeof(*txq), GFP_KERNEL); 266459d0f746SFrank Li if (!txq) { 266559d0f746SFrank Li ret = -ENOMEM; 266659d0f746SFrank Li goto alloc_failed; 266759d0f746SFrank Li } 266859d0f746SFrank Li 266959d0f746SFrank Li fep->tx_queue[i] = txq; 26707355f276STroy Kisky txq->bd.ring_size = TX_RING_SIZE; 26717355f276STroy Kisky fep->total_tx_ring_size += fep->tx_queue[i]->bd.ring_size; 267259d0f746SFrank Li 267359d0f746SFrank Li txq->tx_stop_threshold = FEC_MAX_SKB_DESCS; 267459d0f746SFrank Li txq->tx_wake_threshold = 26757355f276STroy Kisky (txq->bd.ring_size - txq->tx_stop_threshold) / 2; 267659d0f746SFrank Li 267759d0f746SFrank Li txq->tso_hdrs = dma_alloc_coherent(NULL, 26787355f276STroy Kisky txq->bd.ring_size * TSO_HEADER_SIZE, 267959d0f746SFrank Li &txq->tso_hdrs_dma, 268059d0f746SFrank Li GFP_KERNEL); 268159d0f746SFrank Li if (!txq->tso_hdrs) { 268259d0f746SFrank Li ret = -ENOMEM; 268359d0f746SFrank Li goto alloc_failed; 268459d0f746SFrank Li } 268559d0f746SFrank Li } 268659d0f746SFrank Li 268759d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) { 268859d0f746SFrank Li fep->rx_queue[i] = kzalloc(sizeof(*fep->rx_queue[i]), 268959d0f746SFrank Li GFP_KERNEL); 269059d0f746SFrank Li if (!fep->rx_queue[i]) { 269159d0f746SFrank Li ret = -ENOMEM; 269259d0f746SFrank Li goto alloc_failed; 269359d0f746SFrank Li } 269459d0f746SFrank Li 26957355f276STroy Kisky fep->rx_queue[i]->bd.ring_size = RX_RING_SIZE; 26967355f276STroy Kisky fep->total_rx_ring_size += fep->rx_queue[i]->bd.ring_size; 269759d0f746SFrank Li } 269859d0f746SFrank Li return ret; 269959d0f746SFrank Li 270059d0f746SFrank Li alloc_failed: 270159d0f746SFrank Li fec_enet_free_queue(ndev); 270259d0f746SFrank Li return ret; 270359d0f746SFrank Li } 270459d0f746SFrank Li 270559d0f746SFrank Li static int 270659d0f746SFrank Li fec_enet_alloc_rxq_buffers(struct net_device *ndev, unsigned int queue) 2707793fc096SFrank Li { 2708793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2709793fc096SFrank Li unsigned int i; 2710793fc096SFrank Li struct sk_buff *skb; 2711793fc096SFrank Li struct bufdesc *bdp; 27124d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 2713793fc096SFrank Li 271459d0f746SFrank Li rxq = fep->rx_queue[queue]; 27157355f276STroy Kisky bdp = rxq->bd.base; 27167355f276STroy Kisky for (i = 0; i < rxq->bd.ring_size; i++) { 2717793fc096SFrank Li skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE); 2718ffdce2ccSRussell King if (!skb) 2719ffdce2ccSRussell King goto err_alloc; 2720793fc096SFrank Li 27211b7bde6dSNimrod Andy if (fec_enet_new_rxbdp(ndev, bdp, skb)) { 2722730ee360SRussell King dev_kfree_skb(skb); 2723ffdce2ccSRussell King goto err_alloc; 2724d842a31fSDuan Fugang-B38611 } 2725730ee360SRussell King 27264d494cdcSFugang Duan rxq->rx_skbuff[i] = skb; 27275cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); 2728793fc096SFrank Li 2729793fc096SFrank Li if (fep->bufdesc_ex) { 2730793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 27315cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(BD_ENET_RX_INT); 2732793fc096SFrank Li } 2733793fc096SFrank Li 27347355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 2735793fc096SFrank Li } 2736793fc096SFrank Li 2737793fc096SFrank Li /* Set the last buffer to wrap. */ 27387355f276STroy Kisky bdp = fec_enet_get_prevdesc(bdp, &rxq->bd); 27395cfa3039SJohannes Berg bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 274059d0f746SFrank Li return 0; 2741793fc096SFrank Li 274259d0f746SFrank Li err_alloc: 274359d0f746SFrank Li fec_enet_free_buffers(ndev); 274459d0f746SFrank Li return -ENOMEM; 274559d0f746SFrank Li } 274659d0f746SFrank Li 274759d0f746SFrank Li static int 274859d0f746SFrank Li fec_enet_alloc_txq_buffers(struct net_device *ndev, unsigned int queue) 274959d0f746SFrank Li { 275059d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 275159d0f746SFrank Li unsigned int i; 275259d0f746SFrank Li struct bufdesc *bdp; 275359d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 275459d0f746SFrank Li 275559d0f746SFrank Li txq = fep->tx_queue[queue]; 27567355f276STroy Kisky bdp = txq->bd.base; 27577355f276STroy Kisky for (i = 0; i < txq->bd.ring_size; i++) { 27584d494cdcSFugang Duan txq->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL); 27594d494cdcSFugang Duan if (!txq->tx_bounce[i]) 2760ffdce2ccSRussell King goto err_alloc; 2761793fc096SFrank Li 27625cfa3039SJohannes Berg bdp->cbd_sc = cpu_to_fec16(0); 27635cfa3039SJohannes Berg bdp->cbd_bufaddr = cpu_to_fec32(0); 2764793fc096SFrank Li 2765793fc096SFrank Li if (fep->bufdesc_ex) { 2766793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 27675cfa3039SJohannes Berg ebdp->cbd_esc = cpu_to_fec32(BD_ENET_TX_INT); 2768793fc096SFrank Li } 2769793fc096SFrank Li 27707355f276STroy Kisky bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 2771793fc096SFrank Li } 2772793fc096SFrank Li 2773793fc096SFrank Li /* Set the last buffer to wrap. */ 27747355f276STroy Kisky bdp = fec_enet_get_prevdesc(bdp, &txq->bd); 27755cfa3039SJohannes Berg bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 2776793fc096SFrank Li 2777793fc096SFrank Li return 0; 2778ffdce2ccSRussell King 2779ffdce2ccSRussell King err_alloc: 2780ffdce2ccSRussell King fec_enet_free_buffers(ndev); 2781ffdce2ccSRussell King return -ENOMEM; 2782793fc096SFrank Li } 2783793fc096SFrank Li 278459d0f746SFrank Li static int fec_enet_alloc_buffers(struct net_device *ndev) 278559d0f746SFrank Li { 278659d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 278759d0f746SFrank Li unsigned int i; 278859d0f746SFrank Li 278959d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) 279059d0f746SFrank Li if (fec_enet_alloc_rxq_buffers(ndev, i)) 279159d0f746SFrank Li return -ENOMEM; 279259d0f746SFrank Li 279359d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) 279459d0f746SFrank Li if (fec_enet_alloc_txq_buffers(ndev, i)) 279559d0f746SFrank Li return -ENOMEM; 279659d0f746SFrank Li return 0; 279759d0f746SFrank Li } 279859d0f746SFrank Li 2799793fc096SFrank Li static int 2800793fc096SFrank Li fec_enet_open(struct net_device *ndev) 2801793fc096SFrank Li { 2802793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2803793fc096SFrank Li int ret; 2804793fc096SFrank Li 28058fff755eSAndrew Lunn ret = pm_runtime_get_sync(&fep->pdev->dev); 2806b0c6ce24SFabio Estevam if (ret < 0) 28078fff755eSAndrew Lunn return ret; 28088fff755eSAndrew Lunn 28095bbde4d2SNimrod Andy pinctrl_pm_select_default_state(&fep->pdev->dev); 2810e8fcfcd5SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 2811e8fcfcd5SNimrod Andy if (ret) 28128fff755eSAndrew Lunn goto clk_enable; 2813e8fcfcd5SNimrod Andy 2814793fc096SFrank Li /* I should reset the ring buffers here, but I don't yet know 2815793fc096SFrank Li * a simple way to do that. 2816793fc096SFrank Li */ 2817793fc096SFrank Li 2818793fc096SFrank Li ret = fec_enet_alloc_buffers(ndev); 2819793fc096SFrank Li if (ret) 2820681d2421SFabio Estevam goto err_enet_alloc; 2821793fc096SFrank Li 282255dd2753SNimrod Andy /* Init MAC prior to mii bus probe */ 282355dd2753SNimrod Andy fec_restart(ndev); 282455dd2753SNimrod Andy 2825793fc096SFrank Li /* Probe and connect to PHY when open the interface */ 2826793fc096SFrank Li ret = fec_enet_mii_probe(ndev); 2827681d2421SFabio Estevam if (ret) 2828681d2421SFabio Estevam goto err_enet_mii_probe; 2829ce5eaf02SRussell King 2830ce5eaf02SRussell King napi_enable(&fep->napi); 2831793fc096SFrank Li phy_start(fep->phy_dev); 28324d494cdcSFugang Duan netif_tx_start_all_queues(ndev); 28334d494cdcSFugang Duan 2834de40ed31SNimrod Andy device_set_wakeup_enable(&ndev->dev, fep->wol_flag & 2835de40ed31SNimrod Andy FEC_WOL_FLAG_ENABLE); 2836de40ed31SNimrod Andy 2837793fc096SFrank Li return 0; 2838681d2421SFabio Estevam 2839681d2421SFabio Estevam err_enet_mii_probe: 2840681d2421SFabio Estevam fec_enet_free_buffers(ndev); 2841681d2421SFabio Estevam err_enet_alloc: 2842681d2421SFabio Estevam fec_enet_clk_enable(ndev, false); 28438fff755eSAndrew Lunn clk_enable: 28448fff755eSAndrew Lunn pm_runtime_mark_last_busy(&fep->pdev->dev); 28458fff755eSAndrew Lunn pm_runtime_put_autosuspend(&fep->pdev->dev); 2846681d2421SFabio Estevam pinctrl_pm_select_sleep_state(&fep->pdev->dev); 2847681d2421SFabio Estevam return ret; 2848793fc096SFrank Li } 2849793fc096SFrank Li 2850793fc096SFrank Li static int 2851793fc096SFrank Li fec_enet_close(struct net_device *ndev) 2852793fc096SFrank Li { 2853793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2854793fc096SFrank Li 2855d76cfae9SRussell King phy_stop(fep->phy_dev); 2856d76cfae9SRussell King 285731a6de34SRussell King if (netif_device_present(ndev)) { 2858793fc096SFrank Li napi_disable(&fep->napi); 2859b49cd504SRussell King netif_tx_disable(ndev); 2860793fc096SFrank Li fec_stop(ndev); 286131a6de34SRussell King } 2862793fc096SFrank Li 2863793fc096SFrank Li phy_disconnect(fep->phy_dev); 28640b146ca8SRussell King fep->phy_dev = NULL; 2865793fc096SFrank Li 2866e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 28675bbde4d2SNimrod Andy pinctrl_pm_select_sleep_state(&fep->pdev->dev); 28688fff755eSAndrew Lunn pm_runtime_mark_last_busy(&fep->pdev->dev); 28698fff755eSAndrew Lunn pm_runtime_put_autosuspend(&fep->pdev->dev); 28708fff755eSAndrew Lunn 2871793fc096SFrank Li fec_enet_free_buffers(ndev); 2872793fc096SFrank Li 2873793fc096SFrank Li return 0; 2874793fc096SFrank Li } 2875793fc096SFrank Li 2876793fc096SFrank Li /* Set or clear the multicast filter for this adaptor. 2877793fc096SFrank Li * Skeleton taken from sunlance driver. 2878793fc096SFrank Li * The CPM Ethernet implementation allows Multicast as well as individual 2879793fc096SFrank Li * MAC address filtering. Some of the drivers check to make sure it is 2880793fc096SFrank Li * a group multicast address, and discard those that are not. I guess I 2881793fc096SFrank Li * will do the same for now, but just remove the test if you want 2882793fc096SFrank Li * individual filtering as well (do the upper net layers want or support 2883793fc096SFrank Li * this kind of feature?). 2884793fc096SFrank Li */ 2885793fc096SFrank Li 2886793fc096SFrank Li #define HASH_BITS 6 /* #bits in hash */ 2887793fc096SFrank Li #define CRC32_POLY 0xEDB88320 2888793fc096SFrank Li 2889793fc096SFrank Li static void set_multicast_list(struct net_device *ndev) 2890793fc096SFrank Li { 2891793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2892793fc096SFrank Li struct netdev_hw_addr *ha; 2893793fc096SFrank Li unsigned int i, bit, data, crc, tmp; 2894793fc096SFrank Li unsigned char hash; 2895793fc096SFrank Li 2896793fc096SFrank Li if (ndev->flags & IFF_PROMISC) { 2897793fc096SFrank Li tmp = readl(fep->hwp + FEC_R_CNTRL); 2898793fc096SFrank Li tmp |= 0x8; 2899793fc096SFrank Li writel(tmp, fep->hwp + FEC_R_CNTRL); 2900793fc096SFrank Li return; 2901793fc096SFrank Li } 2902793fc096SFrank Li 2903793fc096SFrank Li tmp = readl(fep->hwp + FEC_R_CNTRL); 2904793fc096SFrank Li tmp &= ~0x8; 2905793fc096SFrank Li writel(tmp, fep->hwp + FEC_R_CNTRL); 2906793fc096SFrank Li 2907793fc096SFrank Li if (ndev->flags & IFF_ALLMULTI) { 2908793fc096SFrank Li /* Catch all multicast addresses, so set the 2909793fc096SFrank Li * filter to all 1's 2910793fc096SFrank Li */ 2911793fc096SFrank Li writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 2912793fc096SFrank Li writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 2913793fc096SFrank Li 2914793fc096SFrank Li return; 2915793fc096SFrank Li } 2916793fc096SFrank Li 2917793fc096SFrank Li /* Clear filter and add the addresses in hash register 2918793fc096SFrank Li */ 2919793fc096SFrank Li writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 2920793fc096SFrank Li writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 2921793fc096SFrank Li 2922793fc096SFrank Li netdev_for_each_mc_addr(ha, ndev) { 2923793fc096SFrank Li /* calculate crc32 value of mac address */ 2924793fc096SFrank Li crc = 0xffffffff; 2925793fc096SFrank Li 2926793fc096SFrank Li for (i = 0; i < ndev->addr_len; i++) { 2927793fc096SFrank Li data = ha->addr[i]; 2928793fc096SFrank Li for (bit = 0; bit < 8; bit++, data >>= 1) { 2929793fc096SFrank Li crc = (crc >> 1) ^ 2930793fc096SFrank Li (((crc ^ data) & 1) ? CRC32_POLY : 0); 2931793fc096SFrank Li } 2932793fc096SFrank Li } 2933793fc096SFrank Li 2934793fc096SFrank Li /* only upper 6 bits (HASH_BITS) are used 2935793fc096SFrank Li * which point to specific bit in he hash registers 2936793fc096SFrank Li */ 2937793fc096SFrank Li hash = (crc >> (32 - HASH_BITS)) & 0x3f; 2938793fc096SFrank Li 2939793fc096SFrank Li if (hash > 31) { 2940793fc096SFrank Li tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 2941793fc096SFrank Li tmp |= 1 << (hash - 32); 2942793fc096SFrank Li writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 2943793fc096SFrank Li } else { 2944793fc096SFrank Li tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_LOW); 2945793fc096SFrank Li tmp |= 1 << hash; 2946793fc096SFrank Li writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 2947793fc096SFrank Li } 2948793fc096SFrank Li } 2949793fc096SFrank Li } 2950793fc096SFrank Li 2951793fc096SFrank Li /* Set a MAC change in hardware. */ 2952793fc096SFrank Li static int 2953793fc096SFrank Li fec_set_mac_address(struct net_device *ndev, void *p) 2954793fc096SFrank Li { 2955793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2956793fc096SFrank Li struct sockaddr *addr = p; 2957793fc096SFrank Li 295844934facSLucas Stach if (addr) { 2959793fc096SFrank Li if (!is_valid_ether_addr(addr->sa_data)) 2960793fc096SFrank Li return -EADDRNOTAVAIL; 2961793fc096SFrank Li memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len); 296244934facSLucas Stach } 2963793fc096SFrank Li 29649638d19eSNimrod Andy /* Add netif status check here to avoid system hang in below case: 29659638d19eSNimrod Andy * ifconfig ethx down; ifconfig ethx hw ether xx:xx:xx:xx:xx:xx; 29669638d19eSNimrod Andy * After ethx down, fec all clocks are gated off and then register 29679638d19eSNimrod Andy * access causes system hang. 29689638d19eSNimrod Andy */ 29699638d19eSNimrod Andy if (!netif_running(ndev)) 29709638d19eSNimrod Andy return 0; 29719638d19eSNimrod Andy 2972793fc096SFrank Li writel(ndev->dev_addr[3] | (ndev->dev_addr[2] << 8) | 2973793fc096SFrank Li (ndev->dev_addr[1] << 16) | (ndev->dev_addr[0] << 24), 2974793fc096SFrank Li fep->hwp + FEC_ADDR_LOW); 2975793fc096SFrank Li writel((ndev->dev_addr[5] << 16) | (ndev->dev_addr[4] << 24), 2976793fc096SFrank Li fep->hwp + FEC_ADDR_HIGH); 2977793fc096SFrank Li return 0; 2978793fc096SFrank Li } 2979793fc096SFrank Li 2980793fc096SFrank Li #ifdef CONFIG_NET_POLL_CONTROLLER 2981793fc096SFrank Li /** 2982793fc096SFrank Li * fec_poll_controller - FEC Poll controller function 2983793fc096SFrank Li * @dev: The FEC network adapter 2984793fc096SFrank Li * 2985793fc096SFrank Li * Polled functionality used by netconsole and others in non interrupt mode 2986793fc096SFrank Li * 2987793fc096SFrank Li */ 2988793fc096SFrank Li static void fec_poll_controller(struct net_device *dev) 2989793fc096SFrank Li { 2990793fc096SFrank Li int i; 2991793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(dev); 2992793fc096SFrank Li 2993793fc096SFrank Li for (i = 0; i < FEC_IRQ_NUM; i++) { 2994793fc096SFrank Li if (fep->irq[i] > 0) { 2995793fc096SFrank Li disable_irq(fep->irq[i]); 2996793fc096SFrank Li fec_enet_interrupt(fep->irq[i], dev); 2997793fc096SFrank Li enable_irq(fep->irq[i]); 2998793fc096SFrank Li } 2999793fc096SFrank Li } 3000793fc096SFrank Li } 3001793fc096SFrank Li #endif 3002793fc096SFrank Li 30035bc26726SNimrod Andy static inline void fec_enet_set_netdev_features(struct net_device *netdev, 30044c09eed9SJim Baxter netdev_features_t features) 30054c09eed9SJim Baxter { 30064c09eed9SJim Baxter struct fec_enet_private *fep = netdev_priv(netdev); 30074c09eed9SJim Baxter netdev_features_t changed = features ^ netdev->features; 30084c09eed9SJim Baxter 30094c09eed9SJim Baxter netdev->features = features; 30104c09eed9SJim Baxter 30114c09eed9SJim Baxter /* Receive checksum has been changed */ 30124c09eed9SJim Baxter if (changed & NETIF_F_RXCSUM) { 30134c09eed9SJim Baxter if (features & NETIF_F_RXCSUM) 30144c09eed9SJim Baxter fep->csum_flags |= FLAG_RX_CSUM_ENABLED; 30154c09eed9SJim Baxter else 30164c09eed9SJim Baxter fep->csum_flags &= ~FLAG_RX_CSUM_ENABLED; 30178506fa1dSRussell King } 30185bc26726SNimrod Andy } 30194c09eed9SJim Baxter 30205bc26726SNimrod Andy static int fec_set_features(struct net_device *netdev, 30215bc26726SNimrod Andy netdev_features_t features) 30225bc26726SNimrod Andy { 30235bc26726SNimrod Andy struct fec_enet_private *fep = netdev_priv(netdev); 30245bc26726SNimrod Andy netdev_features_t changed = features ^ netdev->features; 30255bc26726SNimrod Andy 30265b40f709SFabio Estevam if (netif_running(netdev) && changed & NETIF_F_RXCSUM) { 30275bc26726SNimrod Andy napi_disable(&fep->napi); 30285bc26726SNimrod Andy netif_tx_lock_bh(netdev); 30295bc26726SNimrod Andy fec_stop(netdev); 30305bc26726SNimrod Andy fec_enet_set_netdev_features(netdev, features); 3031ef83337dSRussell King fec_restart(netdev); 30324d494cdcSFugang Duan netif_tx_wake_all_queues(netdev); 30336af42d42SRussell King netif_tx_unlock_bh(netdev); 3034dbc64a8eSRussell King napi_enable(&fep->napi); 30355bc26726SNimrod Andy } else { 30365bc26726SNimrod Andy fec_enet_set_netdev_features(netdev, features); 30374c09eed9SJim Baxter } 30384c09eed9SJim Baxter 30394c09eed9SJim Baxter return 0; 30404c09eed9SJim Baxter } 30414c09eed9SJim Baxter 3042793fc096SFrank Li static const struct net_device_ops fec_netdev_ops = { 3043793fc096SFrank Li .ndo_open = fec_enet_open, 3044793fc096SFrank Li .ndo_stop = fec_enet_close, 3045793fc096SFrank Li .ndo_start_xmit = fec_enet_start_xmit, 3046793fc096SFrank Li .ndo_set_rx_mode = set_multicast_list, 3047793fc096SFrank Li .ndo_change_mtu = eth_change_mtu, 3048793fc096SFrank Li .ndo_validate_addr = eth_validate_addr, 3049793fc096SFrank Li .ndo_tx_timeout = fec_timeout, 3050793fc096SFrank Li .ndo_set_mac_address = fec_set_mac_address, 3051793fc096SFrank Li .ndo_do_ioctl = fec_enet_ioctl, 3052793fc096SFrank Li #ifdef CONFIG_NET_POLL_CONTROLLER 3053793fc096SFrank Li .ndo_poll_controller = fec_poll_controller, 3054793fc096SFrank Li #endif 30554c09eed9SJim Baxter .ndo_set_features = fec_set_features, 3056793fc096SFrank Li }; 3057793fc096SFrank Li 3058*53bb20d1STroy Kisky static const unsigned short offset_des_active_rxq[] = { 3059*53bb20d1STroy Kisky FEC_R_DES_ACTIVE_0, FEC_R_DES_ACTIVE_1, FEC_R_DES_ACTIVE_2 3060*53bb20d1STroy Kisky }; 3061*53bb20d1STroy Kisky 3062*53bb20d1STroy Kisky static const unsigned short offset_des_active_txq[] = { 3063*53bb20d1STroy Kisky FEC_X_DES_ACTIVE_0, FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2 3064*53bb20d1STroy Kisky }; 3065*53bb20d1STroy Kisky 3066793fc096SFrank Li /* 3067793fc096SFrank Li * XXX: We need to clean up on failure exits here. 3068793fc096SFrank Li * 3069793fc096SFrank Li */ 3070793fc096SFrank Li static int fec_enet_init(struct net_device *ndev) 3071793fc096SFrank Li { 3072793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3073793fc096SFrank Li struct bufdesc *cbd_base; 30744d494cdcSFugang Duan dma_addr_t bd_dma; 307555d0218aSNimrod Andy int bd_size; 307659d0f746SFrank Li unsigned int i; 30777355f276STroy Kisky unsigned dsize = fep->bufdesc_ex ? sizeof(struct bufdesc_ex) : 30787355f276STroy Kisky sizeof(struct bufdesc); 30797355f276STroy Kisky unsigned dsize_log2 = __fls(dsize); 308055d0218aSNimrod Andy 30817355f276STroy Kisky WARN_ON(dsize != (1 << dsize_log2)); 308241ef84ceSFugang Duan #if defined(CONFIG_ARM) 308341ef84ceSFugang Duan fep->rx_align = 0xf; 308441ef84ceSFugang Duan fep->tx_align = 0xf; 308541ef84ceSFugang Duan #else 308641ef84ceSFugang Duan fep->rx_align = 0x3; 308741ef84ceSFugang Duan fep->tx_align = 0x3; 308841ef84ceSFugang Duan #endif 308941ef84ceSFugang Duan 309059d0f746SFrank Li fec_enet_alloc_queue(ndev); 309179f33912SNimrod Andy 30927355f276STroy Kisky bd_size = (fep->total_tx_ring_size + fep->total_rx_ring_size) * dsize; 3093793fc096SFrank Li 3094793fc096SFrank Li /* Allocate memory for buffer descriptors. */ 3095c0a1a0a6SLucas Stach cbd_base = dmam_alloc_coherent(&fep->pdev->dev, bd_size, &bd_dma, 3096793fc096SFrank Li GFP_KERNEL); 30974d494cdcSFugang Duan if (!cbd_base) { 30984d494cdcSFugang Duan return -ENOMEM; 30994d494cdcSFugang Duan } 3100793fc096SFrank Li 31014d494cdcSFugang Duan memset(cbd_base, 0, bd_size); 3102793fc096SFrank Li 3103793fc096SFrank Li /* Get the Ethernet address */ 3104793fc096SFrank Li fec_get_mac(ndev); 310544934facSLucas Stach /* make sure MAC we just acquired is programmed into the hw */ 310644934facSLucas Stach fec_set_mac_address(ndev, NULL); 3107793fc096SFrank Li 3108793fc096SFrank Li /* Set receive and transmit descriptor base. */ 310959d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) { 31107355f276STroy Kisky struct fec_enet_priv_rx_q *rxq = fep->rx_queue[i]; 31117355f276STroy Kisky unsigned size = dsize * rxq->bd.ring_size; 31127355f276STroy Kisky 31137355f276STroy Kisky rxq->bd.qid = i; 31147355f276STroy Kisky rxq->bd.base = cbd_base; 31157355f276STroy Kisky rxq->bd.cur = cbd_base; 31167355f276STroy Kisky rxq->bd.dma = bd_dma; 31177355f276STroy Kisky rxq->bd.dsize = dsize; 31187355f276STroy Kisky rxq->bd.dsize_log2 = dsize_log2; 3119*53bb20d1STroy Kisky rxq->bd.reg_desc_active = fep->hwp + offset_des_active_rxq[i]; 31207355f276STroy Kisky bd_dma += size; 31217355f276STroy Kisky cbd_base = (struct bufdesc *)(((void *)cbd_base) + size); 31227355f276STroy Kisky rxq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize); 312359d0f746SFrank Li } 312459d0f746SFrank Li 312559d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 31267355f276STroy Kisky struct fec_enet_priv_tx_q *txq = fep->tx_queue[i]; 31277355f276STroy Kisky unsigned size = dsize * txq->bd.ring_size; 31287355f276STroy Kisky 31297355f276STroy Kisky txq->bd.qid = i; 31307355f276STroy Kisky txq->bd.base = cbd_base; 31317355f276STroy Kisky txq->bd.cur = cbd_base; 31327355f276STroy Kisky txq->bd.dma = bd_dma; 31337355f276STroy Kisky txq->bd.dsize = dsize; 31347355f276STroy Kisky txq->bd.dsize_log2 = dsize_log2; 3135*53bb20d1STroy Kisky txq->bd.reg_desc_active = fep->hwp + offset_des_active_txq[i]; 31367355f276STroy Kisky bd_dma += size; 31377355f276STroy Kisky cbd_base = (struct bufdesc *)(((void *)cbd_base) + size); 31387355f276STroy Kisky txq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize); 313959d0f746SFrank Li } 31404d494cdcSFugang Duan 3141793fc096SFrank Li 3142793fc096SFrank Li /* The FEC Ethernet specific entries in the device structure */ 3143793fc096SFrank Li ndev->watchdog_timeo = TX_TIMEOUT; 3144793fc096SFrank Li ndev->netdev_ops = &fec_netdev_ops; 3145793fc096SFrank Li ndev->ethtool_ops = &fec_enet_ethtool_ops; 3146793fc096SFrank Li 3147793fc096SFrank Li writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK); 3148322555f5SFabio Estevam netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, NAPI_POLL_WEIGHT); 3149793fc096SFrank Li 31506b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_VLAN) 3151cdffcf1bSJim Baxter /* enable hw VLAN support */ 3152cdffcf1bSJim Baxter ndev->features |= NETIF_F_HW_VLAN_CTAG_RX; 3153cdffcf1bSJim Baxter 31546b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_CSUM) { 315579f33912SNimrod Andy ndev->gso_max_segs = FEC_MAX_TSO_SEGS; 315679f33912SNimrod Andy 31574c09eed9SJim Baxter /* enable hw accelerator */ 31584c09eed9SJim Baxter ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM 315979f33912SNimrod Andy | NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO); 31604c09eed9SJim Baxter fep->csum_flags |= FLAG_RX_CSUM_ENABLED; 316148496255SShawn Guo } 31624c09eed9SJim Baxter 31636b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) { 316441ef84ceSFugang Duan fep->tx_align = 0; 316541ef84ceSFugang Duan fep->rx_align = 0x3f; 316641ef84ceSFugang Duan } 316741ef84ceSFugang Duan 316809d1e541SNimrod Andy ndev->hw_features = ndev->features; 316909d1e541SNimrod Andy 3170ef83337dSRussell King fec_restart(ndev); 3171793fc096SFrank Li 3172793fc096SFrank Li return 0; 3173793fc096SFrank Li } 3174793fc096SFrank Li 3175793fc096SFrank Li #ifdef CONFIG_OF 3176793fc096SFrank Li static void fec_reset_phy(struct platform_device *pdev) 3177793fc096SFrank Li { 3178793fc096SFrank Li int err, phy_reset; 3179793fc096SFrank Li int msec = 1; 3180793fc096SFrank Li struct device_node *np = pdev->dev.of_node; 3181793fc096SFrank Li 3182793fc096SFrank Li if (!np) 3183793fc096SFrank Li return; 3184793fc096SFrank Li 3185793fc096SFrank Li of_property_read_u32(np, "phy-reset-duration", &msec); 3186793fc096SFrank Li /* A sane reset duration should not be longer than 1s */ 3187793fc096SFrank Li if (msec > 1000) 3188793fc096SFrank Li msec = 1; 3189793fc096SFrank Li 3190793fc096SFrank Li phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0); 3191793fc096SFrank Li if (!gpio_is_valid(phy_reset)) 3192793fc096SFrank Li return; 3193793fc096SFrank Li 3194793fc096SFrank Li err = devm_gpio_request_one(&pdev->dev, phy_reset, 3195793fc096SFrank Li GPIOF_OUT_INIT_LOW, "phy-reset"); 3196793fc096SFrank Li if (err) { 3197793fc096SFrank Li dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err); 3198793fc096SFrank Li return; 3199793fc096SFrank Li } 3200793fc096SFrank Li msleep(msec); 3201f4444574SFabio Estevam gpio_set_value_cansleep(phy_reset, 1); 3202793fc096SFrank Li } 3203793fc096SFrank Li #else /* CONFIG_OF */ 3204793fc096SFrank Li static void fec_reset_phy(struct platform_device *pdev) 3205793fc096SFrank Li { 3206793fc096SFrank Li /* 3207793fc096SFrank Li * In case of platform probe, the reset has been done 3208793fc096SFrank Li * by machine code. 3209793fc096SFrank Li */ 3210793fc096SFrank Li } 3211793fc096SFrank Li #endif /* CONFIG_OF */ 3212793fc096SFrank Li 32139fc095f1SFugang Duan static void 32149fc095f1SFugang Duan fec_enet_get_queue_num(struct platform_device *pdev, int *num_tx, int *num_rx) 32159fc095f1SFugang Duan { 32169fc095f1SFugang Duan struct device_node *np = pdev->dev.of_node; 32179fc095f1SFugang Duan 32189fc095f1SFugang Duan *num_tx = *num_rx = 1; 32199fc095f1SFugang Duan 32209fc095f1SFugang Duan if (!np || !of_device_is_available(np)) 32219fc095f1SFugang Duan return; 32229fc095f1SFugang Duan 32239fc095f1SFugang Duan /* parse the num of tx and rx queues */ 322473b1c90dSSaurabh Sengar of_property_read_u32(np, "fsl,num-tx-queues", num_tx); 3225b7bd75cfSFrank Li 322673b1c90dSSaurabh Sengar of_property_read_u32(np, "fsl,num-rx-queues", num_rx); 32279fc095f1SFugang Duan 32289fc095f1SFugang Duan if (*num_tx < 1 || *num_tx > FEC_ENET_MAX_TX_QS) { 3229b7bd75cfSFrank Li dev_warn(&pdev->dev, "Invalid num_tx(=%d), fall back to 1\n", 32309fc095f1SFugang Duan *num_tx); 32319fc095f1SFugang Duan *num_tx = 1; 32329fc095f1SFugang Duan return; 32339fc095f1SFugang Duan } 32349fc095f1SFugang Duan 32359fc095f1SFugang Duan if (*num_rx < 1 || *num_rx > FEC_ENET_MAX_RX_QS) { 3236b7bd75cfSFrank Li dev_warn(&pdev->dev, "Invalid num_rx(=%d), fall back to 1\n", 32379fc095f1SFugang Duan *num_rx); 32389fc095f1SFugang Duan *num_rx = 1; 32399fc095f1SFugang Duan return; 32409fc095f1SFugang Duan } 32419fc095f1SFugang Duan 32429fc095f1SFugang Duan } 32439fc095f1SFugang Duan 3244793fc096SFrank Li static int 3245793fc096SFrank Li fec_probe(struct platform_device *pdev) 3246793fc096SFrank Li { 3247793fc096SFrank Li struct fec_enet_private *fep; 3248793fc096SFrank Li struct fec_platform_data *pdata; 3249793fc096SFrank Li struct net_device *ndev; 3250793fc096SFrank Li int i, irq, ret = 0; 3251793fc096SFrank Li struct resource *r; 3252793fc096SFrank Li const struct of_device_id *of_id; 3253793fc096SFrank Li static int dev_id; 3254407066f8SUwe Kleine-König struct device_node *np = pdev->dev.of_node, *phy_node; 3255b7bd75cfSFrank Li int num_tx_qs; 3256b7bd75cfSFrank Li int num_rx_qs; 3257793fc096SFrank Li 32589fc095f1SFugang Duan fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs); 32599fc095f1SFugang Duan 3260793fc096SFrank Li /* Init network device */ 32619fc095f1SFugang Duan ndev = alloc_etherdev_mqs(sizeof(struct fec_enet_private), 32629fc095f1SFugang Duan num_tx_qs, num_rx_qs); 3263793fc096SFrank Li if (!ndev) 3264793fc096SFrank Li return -ENOMEM; 3265793fc096SFrank Li 3266793fc096SFrank Li SET_NETDEV_DEV(ndev, &pdev->dev); 3267793fc096SFrank Li 3268793fc096SFrank Li /* setup board info structure */ 3269793fc096SFrank Li fep = netdev_priv(ndev); 3270793fc096SFrank Li 32716b7e4008SLothar Waßmann of_id = of_match_device(fec_dt_ids, &pdev->dev); 32726b7e4008SLothar Waßmann if (of_id) 32736b7e4008SLothar Waßmann pdev->id_entry = of_id->data; 32746b7e4008SLothar Waßmann fep->quirks = pdev->id_entry->driver_data; 32756b7e4008SLothar Waßmann 32760c818594SHubert Feurstein fep->netdev = ndev; 32779fc095f1SFugang Duan fep->num_rx_queues = num_rx_qs; 32789fc095f1SFugang Duan fep->num_tx_queues = num_tx_qs; 32799fc095f1SFugang Duan 3280d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 3281793fc096SFrank Li /* default enable pause frame auto negotiation */ 32826b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_GBIT) 3283793fc096SFrank Li fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG; 3284d1391930SGuenter Roeck #endif 3285793fc096SFrank Li 32865bbde4d2SNimrod Andy /* Select default pin state */ 32875bbde4d2SNimrod Andy pinctrl_pm_select_default_state(&pdev->dev); 32885bbde4d2SNimrod Andy 3289399db75bSFabio Estevam r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3290941e173aSTushar Behera fep->hwp = devm_ioremap_resource(&pdev->dev, r); 3291941e173aSTushar Behera if (IS_ERR(fep->hwp)) { 3292941e173aSTushar Behera ret = PTR_ERR(fep->hwp); 3293941e173aSTushar Behera goto failed_ioremap; 3294941e173aSTushar Behera } 3295941e173aSTushar Behera 3296793fc096SFrank Li fep->pdev = pdev; 3297793fc096SFrank Li fep->dev_id = dev_id++; 3298793fc096SFrank Li 3299793fc096SFrank Li platform_set_drvdata(pdev, ndev); 3300793fc096SFrank Li 3301de40ed31SNimrod Andy if (of_get_property(np, "fsl,magic-packet", NULL)) 3302de40ed31SNimrod Andy fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET; 3303de40ed31SNimrod Andy 3304407066f8SUwe Kleine-König phy_node = of_parse_phandle(np, "phy-handle", 0); 3305407066f8SUwe Kleine-König if (!phy_node && of_phy_is_fixed_link(np)) { 3306407066f8SUwe Kleine-König ret = of_phy_register_fixed_link(np); 3307407066f8SUwe Kleine-König if (ret < 0) { 3308407066f8SUwe Kleine-König dev_err(&pdev->dev, 3309407066f8SUwe Kleine-König "broken fixed-link specification\n"); 3310407066f8SUwe Kleine-König goto failed_phy; 3311407066f8SUwe Kleine-König } 3312407066f8SUwe Kleine-König phy_node = of_node_get(np); 3313407066f8SUwe Kleine-König } 3314407066f8SUwe Kleine-König fep->phy_node = phy_node; 3315407066f8SUwe Kleine-König 33166c5f7808SGuenter Roeck ret = of_get_phy_mode(pdev->dev.of_node); 3317793fc096SFrank Li if (ret < 0) { 331894660ba0SJingoo Han pdata = dev_get_platdata(&pdev->dev); 3319793fc096SFrank Li if (pdata) 3320793fc096SFrank Li fep->phy_interface = pdata->phy; 3321793fc096SFrank Li else 3322793fc096SFrank Li fep->phy_interface = PHY_INTERFACE_MODE_MII; 3323793fc096SFrank Li } else { 3324793fc096SFrank Li fep->phy_interface = ret; 3325793fc096SFrank Li } 3326793fc096SFrank Li 3327793fc096SFrank Li fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); 3328793fc096SFrank Li if (IS_ERR(fep->clk_ipg)) { 3329793fc096SFrank Li ret = PTR_ERR(fep->clk_ipg); 3330793fc096SFrank Li goto failed_clk; 3331793fc096SFrank Li } 3332793fc096SFrank Li 3333793fc096SFrank Li fep->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); 3334793fc096SFrank Li if (IS_ERR(fep->clk_ahb)) { 3335793fc096SFrank Li ret = PTR_ERR(fep->clk_ahb); 3336793fc096SFrank Li goto failed_clk; 3337793fc096SFrank Li } 3338793fc096SFrank Li 3339d851b47bSFugang Duan fep->itr_clk_rate = clk_get_rate(fep->clk_ahb); 3340d851b47bSFugang Duan 334138f56f33SLinus Torvalds /* enet_out is optional, depends on board */ 334238f56f33SLinus Torvalds fep->clk_enet_out = devm_clk_get(&pdev->dev, "enet_out"); 334338f56f33SLinus Torvalds if (IS_ERR(fep->clk_enet_out)) 334438f56f33SLinus Torvalds fep->clk_enet_out = NULL; 334538f56f33SLinus Torvalds 334691c0d987SNimrod Andy fep->ptp_clk_on = false; 334791c0d987SNimrod Andy mutex_init(&fep->ptp_clk_mutex); 33489b5330edSFugang Duan 33499b5330edSFugang Duan /* clk_ref is optional, depends on board */ 33509b5330edSFugang Duan fep->clk_ref = devm_clk_get(&pdev->dev, "enet_clk_ref"); 33519b5330edSFugang Duan if (IS_ERR(fep->clk_ref)) 33529b5330edSFugang Duan fep->clk_ref = NULL; 33539b5330edSFugang Duan 33546b7e4008SLothar Waßmann fep->bufdesc_ex = fep->quirks & FEC_QUIRK_HAS_BUFDESC_EX; 3355793fc096SFrank Li fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp"); 3356793fc096SFrank Li if (IS_ERR(fep->clk_ptp)) { 335738f56f33SLinus Torvalds fep->clk_ptp = NULL; 3358217b5844SLothar Waßmann fep->bufdesc_ex = false; 3359793fc096SFrank Li } 3360793fc096SFrank Li 3361e8fcfcd5SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 336213a097bdSFabio Estevam if (ret) 336313a097bdSFabio Estevam goto failed_clk; 336413a097bdSFabio Estevam 33658fff755eSAndrew Lunn ret = clk_prepare_enable(fep->clk_ipg); 33668fff755eSAndrew Lunn if (ret) 33678fff755eSAndrew Lunn goto failed_clk_ipg; 33688fff755eSAndrew Lunn 3369f4e9f3d2SFabio Estevam fep->reg_phy = devm_regulator_get(&pdev->dev, "phy"); 3370f4e9f3d2SFabio Estevam if (!IS_ERR(fep->reg_phy)) { 3371f4e9f3d2SFabio Estevam ret = regulator_enable(fep->reg_phy); 3372793fc096SFrank Li if (ret) { 3373793fc096SFrank Li dev_err(&pdev->dev, 3374793fc096SFrank Li "Failed to enable phy regulator: %d\n", ret); 3375793fc096SFrank Li goto failed_regulator; 3376793fc096SFrank Li } 3377f6a4d607SFabio Estevam } else { 3378f6a4d607SFabio Estevam fep->reg_phy = NULL; 3379793fc096SFrank Li } 3380793fc096SFrank Li 33818fff755eSAndrew Lunn pm_runtime_set_autosuspend_delay(&pdev->dev, FEC_MDIO_PM_TIMEOUT); 33828fff755eSAndrew Lunn pm_runtime_use_autosuspend(&pdev->dev); 338314d2b7c1SLucas Stach pm_runtime_get_noresume(&pdev->dev); 33848fff755eSAndrew Lunn pm_runtime_set_active(&pdev->dev); 33858fff755eSAndrew Lunn pm_runtime_enable(&pdev->dev); 33868fff755eSAndrew Lunn 3387793fc096SFrank Li fec_reset_phy(pdev); 3388793fc096SFrank Li 3389793fc096SFrank Li if (fep->bufdesc_ex) 3390ca162a82SFabio Estevam fec_ptp_init(pdev); 3391793fc096SFrank Li 3392793fc096SFrank Li ret = fec_enet_init(ndev); 3393793fc096SFrank Li if (ret) 3394793fc096SFrank Li goto failed_init; 3395793fc096SFrank Li 3396793fc096SFrank Li for (i = 0; i < FEC_IRQ_NUM; i++) { 3397793fc096SFrank Li irq = platform_get_irq(pdev, i); 3398793fc096SFrank Li if (irq < 0) { 3399793fc096SFrank Li if (i) 3400793fc096SFrank Li break; 3401793fc096SFrank Li ret = irq; 3402793fc096SFrank Li goto failed_irq; 3403793fc096SFrank Li } 34040d9b2ab1SFabio Estevam ret = devm_request_irq(&pdev->dev, irq, fec_enet_interrupt, 340544a272ddSMichael Opdenacker 0, pdev->name, ndev); 34060d9b2ab1SFabio Estevam if (ret) 3407793fc096SFrank Li goto failed_irq; 3408de40ed31SNimrod Andy 3409de40ed31SNimrod Andy fep->irq[i] = irq; 3410793fc096SFrank Li } 3411793fc096SFrank Li 3412b4d39b53SFugang Duan init_completion(&fep->mdio_done); 3413793fc096SFrank Li ret = fec_enet_mii_init(pdev); 3414793fc096SFrank Li if (ret) 3415793fc096SFrank Li goto failed_mii_init; 3416793fc096SFrank Li 3417793fc096SFrank Li /* Carrier starts down, phylib will bring it up */ 3418793fc096SFrank Li netif_carrier_off(ndev); 3419e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 34205bbde4d2SNimrod Andy pinctrl_pm_select_sleep_state(&pdev->dev); 3421793fc096SFrank Li 3422793fc096SFrank Li ret = register_netdev(ndev); 3423793fc096SFrank Li if (ret) 3424793fc096SFrank Li goto failed_register; 3425793fc096SFrank Li 3426de40ed31SNimrod Andy device_init_wakeup(&ndev->dev, fep->wol_flag & 3427de40ed31SNimrod Andy FEC_WOL_HAS_MAGIC_PACKET); 3428de40ed31SNimrod Andy 3429eb1d0640SFabio Estevam if (fep->bufdesc_ex && fep->ptp_clock) 3430eb1d0640SFabio Estevam netdev_info(ndev, "registered PHC device %d\n", fep->dev_id); 3431eb1d0640SFabio Estevam 34321b7bde6dSNimrod Andy fep->rx_copybreak = COPYBREAK_DEFAULT; 343336cdc743SRussell King INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work); 34348fff755eSAndrew Lunn 34358fff755eSAndrew Lunn pm_runtime_mark_last_busy(&pdev->dev); 34368fff755eSAndrew Lunn pm_runtime_put_autosuspend(&pdev->dev); 34378fff755eSAndrew Lunn 3438793fc096SFrank Li return 0; 3439793fc096SFrank Li 3440793fc096SFrank Li failed_register: 3441793fc096SFrank Li fec_enet_mii_remove(fep); 3442793fc096SFrank Li failed_mii_init: 34437a2bbd8dSFabio Estevam failed_irq: 34447a2bbd8dSFabio Estevam failed_init: 344532cba57bSLucas Stach fec_ptp_stop(pdev); 3446f6a4d607SFabio Estevam if (fep->reg_phy) 3447f6a4d607SFabio Estevam regulator_disable(fep->reg_phy); 3448793fc096SFrank Li failed_regulator: 34498fff755eSAndrew Lunn clk_disable_unprepare(fep->clk_ipg); 34508fff755eSAndrew Lunn failed_clk_ipg: 3451e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 3452793fc096SFrank Li failed_clk: 3453407066f8SUwe Kleine-König failed_phy: 3454407066f8SUwe Kleine-König of_node_put(phy_node); 3455793fc096SFrank Li failed_ioremap: 3456793fc096SFrank Li free_netdev(ndev); 3457793fc096SFrank Li 3458793fc096SFrank Li return ret; 3459793fc096SFrank Li } 3460793fc096SFrank Li 3461793fc096SFrank Li static int 3462793fc096SFrank Li fec_drv_remove(struct platform_device *pdev) 3463793fc096SFrank Li { 3464793fc096SFrank Li struct net_device *ndev = platform_get_drvdata(pdev); 3465793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3466793fc096SFrank Li 346736cdc743SRussell King cancel_work_sync(&fep->tx_timeout_work); 346832cba57bSLucas Stach fec_ptp_stop(pdev); 3469793fc096SFrank Li unregister_netdev(ndev); 3470793fc096SFrank Li fec_enet_mii_remove(fep); 3471f6a4d607SFabio Estevam if (fep->reg_phy) 3472f6a4d607SFabio Estevam regulator_disable(fep->reg_phy); 3473407066f8SUwe Kleine-König of_node_put(fep->phy_node); 3474793fc096SFrank Li free_netdev(ndev); 3475793fc096SFrank Li 3476793fc096SFrank Li return 0; 3477793fc096SFrank Li } 3478793fc096SFrank Li 3479dd66d386SFabio Estevam static int __maybe_unused fec_suspend(struct device *dev) 3480793fc096SFrank Li { 3481793fc096SFrank Li struct net_device *ndev = dev_get_drvdata(dev); 3482793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3483793fc096SFrank Li 3484da1774e5SRussell King rtnl_lock(); 3485793fc096SFrank Li if (netif_running(ndev)) { 3486de40ed31SNimrod Andy if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) 3487de40ed31SNimrod Andy fep->wol_flag |= FEC_WOL_FLAG_SLEEP_ON; 3488d76cfae9SRussell King phy_stop(fep->phy_dev); 348931a6de34SRussell King napi_disable(&fep->napi); 349031a6de34SRussell King netif_tx_lock_bh(ndev); 3491793fc096SFrank Li netif_device_detach(ndev); 349231a6de34SRussell King netif_tx_unlock_bh(ndev); 349331a6de34SRussell King fec_stop(ndev); 3494e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 3495de40ed31SNimrod Andy if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) 34965bbde4d2SNimrod Andy pinctrl_pm_select_sleep_state(&fep->pdev->dev); 3497f4c4a4e0SNimrod Andy } 3498f4c4a4e0SNimrod Andy rtnl_unlock(); 3499793fc096SFrank Li 3500de40ed31SNimrod Andy if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) 3501238f7bc7SFabio Estevam regulator_disable(fep->reg_phy); 3502238f7bc7SFabio Estevam 3503858eeb7dSNimrod Andy /* SOC supply clock to phy, when clock is disabled, phy link down 3504858eeb7dSNimrod Andy * SOC control phy regulator, when regulator is disabled, phy link down 3505858eeb7dSNimrod Andy */ 3506858eeb7dSNimrod Andy if (fep->clk_enet_out || fep->reg_phy) 3507858eeb7dSNimrod Andy fep->link = 0; 3508858eeb7dSNimrod Andy 3509793fc096SFrank Li return 0; 3510793fc096SFrank Li } 3511793fc096SFrank Li 3512dd66d386SFabio Estevam static int __maybe_unused fec_resume(struct device *dev) 3513793fc096SFrank Li { 3514793fc096SFrank Li struct net_device *ndev = dev_get_drvdata(dev); 3515793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3516de40ed31SNimrod Andy struct fec_platform_data *pdata = fep->pdev->dev.platform_data; 3517238f7bc7SFabio Estevam int ret; 3518de40ed31SNimrod Andy int val; 3519238f7bc7SFabio Estevam 3520de40ed31SNimrod Andy if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) { 3521238f7bc7SFabio Estevam ret = regulator_enable(fep->reg_phy); 3522238f7bc7SFabio Estevam if (ret) 3523238f7bc7SFabio Estevam return ret; 3524238f7bc7SFabio Estevam } 3525793fc096SFrank Li 3526da1774e5SRussell King rtnl_lock(); 3527793fc096SFrank Li if (netif_running(ndev)) { 3528f4c4a4e0SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 3529f4c4a4e0SNimrod Andy if (ret) { 3530f4c4a4e0SNimrod Andy rtnl_unlock(); 3531f4c4a4e0SNimrod Andy goto failed_clk; 3532f4c4a4e0SNimrod Andy } 3533de40ed31SNimrod Andy if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) { 3534de40ed31SNimrod Andy if (pdata && pdata->sleep_mode_enable) 3535de40ed31SNimrod Andy pdata->sleep_mode_enable(false); 3536de40ed31SNimrod Andy val = readl(fep->hwp + FEC_ECNTRL); 3537de40ed31SNimrod Andy val &= ~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP); 3538de40ed31SNimrod Andy writel(val, fep->hwp + FEC_ECNTRL); 3539de40ed31SNimrod Andy fep->wol_flag &= ~FEC_WOL_FLAG_SLEEP_ON; 3540de40ed31SNimrod Andy } else { 3541de40ed31SNimrod Andy pinctrl_pm_select_default_state(&fep->pdev->dev); 3542de40ed31SNimrod Andy } 3543ef83337dSRussell King fec_restart(ndev); 354431a6de34SRussell King netif_tx_lock_bh(ndev); 3545793fc096SFrank Li netif_device_attach(ndev); 35466af42d42SRussell King netif_tx_unlock_bh(ndev); 35476af42d42SRussell King napi_enable(&fep->napi); 3548d76cfae9SRussell King phy_start(fep->phy_dev); 3549793fc096SFrank Li } 3550da1774e5SRussell King rtnl_unlock(); 3551793fc096SFrank Li 3552793fc096SFrank Li return 0; 355313a097bdSFabio Estevam 3554e8fcfcd5SNimrod Andy failed_clk: 355513a097bdSFabio Estevam if (fep->reg_phy) 355613a097bdSFabio Estevam regulator_disable(fep->reg_phy); 355713a097bdSFabio Estevam return ret; 3558793fc096SFrank Li } 3559793fc096SFrank Li 35608fff755eSAndrew Lunn static int __maybe_unused fec_runtime_suspend(struct device *dev) 35618fff755eSAndrew Lunn { 35628fff755eSAndrew Lunn struct net_device *ndev = dev_get_drvdata(dev); 35638fff755eSAndrew Lunn struct fec_enet_private *fep = netdev_priv(ndev); 35648fff755eSAndrew Lunn 35658fff755eSAndrew Lunn clk_disable_unprepare(fep->clk_ipg); 35668fff755eSAndrew Lunn 35678fff755eSAndrew Lunn return 0; 35688fff755eSAndrew Lunn } 35698fff755eSAndrew Lunn 35708fff755eSAndrew Lunn static int __maybe_unused fec_runtime_resume(struct device *dev) 35718fff755eSAndrew Lunn { 35728fff755eSAndrew Lunn struct net_device *ndev = dev_get_drvdata(dev); 35738fff755eSAndrew Lunn struct fec_enet_private *fep = netdev_priv(ndev); 35748fff755eSAndrew Lunn 35758fff755eSAndrew Lunn return clk_prepare_enable(fep->clk_ipg); 35768fff755eSAndrew Lunn } 35778fff755eSAndrew Lunn 35788fff755eSAndrew Lunn static const struct dev_pm_ops fec_pm_ops = { 35798fff755eSAndrew Lunn SET_SYSTEM_SLEEP_PM_OPS(fec_suspend, fec_resume) 35808fff755eSAndrew Lunn SET_RUNTIME_PM_OPS(fec_runtime_suspend, fec_runtime_resume, NULL) 35818fff755eSAndrew Lunn }; 3582793fc096SFrank Li 3583793fc096SFrank Li static struct platform_driver fec_driver = { 3584793fc096SFrank Li .driver = { 3585793fc096SFrank Li .name = DRIVER_NAME, 3586793fc096SFrank Li .pm = &fec_pm_ops, 3587793fc096SFrank Li .of_match_table = fec_dt_ids, 3588793fc096SFrank Li }, 3589793fc096SFrank Li .id_table = fec_devtype, 3590793fc096SFrank Li .probe = fec_probe, 3591793fc096SFrank Li .remove = fec_drv_remove, 3592793fc096SFrank Li }; 3593793fc096SFrank Li 3594793fc096SFrank Li module_platform_driver(fec_driver); 3595793fc096SFrank Li 3596f8c0aca9SFabio Estevam MODULE_ALIAS("platform:"DRIVER_NAME); 3597793fc096SFrank Li MODULE_LICENSE("GPL"); 3598