1793fc096SFrank Li /* 2793fc096SFrank Li * Fast Ethernet Controller (FEC) driver for Motorola MPC8xx. 3793fc096SFrank Li * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) 4793fc096SFrank Li * 5793fc096SFrank Li * Right now, I am very wasteful with the buffers. I allocate memory 6793fc096SFrank Li * pages and then divide them into 2K frame buffers. This way I know I 7793fc096SFrank Li * have buffers large enough to hold one frame within one buffer descriptor. 8793fc096SFrank Li * Once I get this working, I will use 64 or 128 byte CPM buffers, which 9793fc096SFrank Li * will be much more memory efficient and will easily handle lots of 10793fc096SFrank Li * small packets. 11793fc096SFrank Li * 12793fc096SFrank Li * Much better multiple PHY support by Magnus Damm. 13793fc096SFrank Li * Copyright (c) 2000 Ericsson Radio Systems AB. 14793fc096SFrank Li * 15793fc096SFrank Li * Support for FEC controller of ColdFire processors. 16793fc096SFrank Li * Copyright (c) 2001-2005 Greg Ungerer (gerg@snapgear.com) 17793fc096SFrank Li * 18793fc096SFrank Li * Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be) 19793fc096SFrank Li * Copyright (c) 2004-2006 Macq Electronique SA. 20793fc096SFrank Li * 21793fc096SFrank Li * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. 22793fc096SFrank Li */ 23793fc096SFrank Li 24793fc096SFrank Li #include <linux/module.h> 25793fc096SFrank Li #include <linux/kernel.h> 26793fc096SFrank Li #include <linux/string.h> 27793fc096SFrank Li #include <linux/ptrace.h> 28793fc096SFrank Li #include <linux/errno.h> 29793fc096SFrank Li #include <linux/ioport.h> 30793fc096SFrank Li #include <linux/slab.h> 31793fc096SFrank Li #include <linux/interrupt.h> 32793fc096SFrank Li #include <linux/init.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> 404c09eed9SJim Baxter #include <linux/tcp.h> 414c09eed9SJim Baxter #include <linux/udp.h> 424c09eed9SJim Baxter #include <linux/icmp.h> 43793fc096SFrank Li #include <linux/spinlock.h> 44793fc096SFrank Li #include <linux/workqueue.h> 45793fc096SFrank Li #include <linux/bitops.h> 46793fc096SFrank Li #include <linux/io.h> 47793fc096SFrank Li #include <linux/irq.h> 48793fc096SFrank Li #include <linux/clk.h> 49793fc096SFrank Li #include <linux/platform_device.h> 50793fc096SFrank Li #include <linux/phy.h> 51793fc096SFrank Li #include <linux/fec.h> 52793fc096SFrank Li #include <linux/of.h> 53793fc096SFrank Li #include <linux/of_device.h> 54793fc096SFrank Li #include <linux/of_gpio.h> 55793fc096SFrank Li #include <linux/of_net.h> 56793fc096SFrank Li #include <linux/regulator/consumer.h> 57cdffcf1bSJim Baxter #include <linux/if_vlan.h> 58793fc096SFrank Li 59793fc096SFrank Li #include <asm/cacheflush.h> 60793fc096SFrank Li 61793fc096SFrank Li #include "fec.h" 62793fc096SFrank Li 63772e42b0SChristoph Müllner static void set_multicast_list(struct net_device *ndev); 64772e42b0SChristoph Müllner 65793fc096SFrank Li #if defined(CONFIG_ARM) 66793fc096SFrank Li #define FEC_ALIGNMENT 0xf 67793fc096SFrank Li #else 68793fc096SFrank Li #define FEC_ALIGNMENT 0x3 69793fc096SFrank Li #endif 70793fc096SFrank Li 71793fc096SFrank Li #define DRIVER_NAME "fec" 72793fc096SFrank Li #define FEC_NAPI_WEIGHT 64 73793fc096SFrank Li 74793fc096SFrank Li /* Pause frame feild and FIFO threshold */ 75793fc096SFrank Li #define FEC_ENET_FCE (1 << 5) 76793fc096SFrank Li #define FEC_ENET_RSEM_V 0x84 77793fc096SFrank Li #define FEC_ENET_RSFL_V 16 78793fc096SFrank Li #define FEC_ENET_RAEM_V 0x8 79793fc096SFrank Li #define FEC_ENET_RAFL_V 0x8 80793fc096SFrank Li #define FEC_ENET_OPD_V 0xFFF0 81793fc096SFrank Li 82793fc096SFrank Li /* Controller is ENET-MAC */ 83793fc096SFrank Li #define FEC_QUIRK_ENET_MAC (1 << 0) 84793fc096SFrank Li /* Controller needs driver to swap frame */ 85793fc096SFrank Li #define FEC_QUIRK_SWAP_FRAME (1 << 1) 86793fc096SFrank Li /* Controller uses gasket */ 87793fc096SFrank Li #define FEC_QUIRK_USE_GASKET (1 << 2) 88793fc096SFrank Li /* Controller has GBIT support */ 89793fc096SFrank Li #define FEC_QUIRK_HAS_GBIT (1 << 3) 90793fc096SFrank Li /* Controller has extend desc buffer */ 91793fc096SFrank Li #define FEC_QUIRK_HAS_BUFDESC_EX (1 << 4) 9248496255SShawn Guo /* Controller has hardware checksum support */ 9348496255SShawn Guo #define FEC_QUIRK_HAS_CSUM (1 << 5) 94cdffcf1bSJim Baxter /* Controller has hardware vlan support */ 95cdffcf1bSJim Baxter #define FEC_QUIRK_HAS_VLAN (1 << 6) 96793fc096SFrank Li 97793fc096SFrank Li static struct platform_device_id fec_devtype[] = { 98793fc096SFrank Li { 99793fc096SFrank Li /* keep it for coldfire */ 100793fc096SFrank Li .name = DRIVER_NAME, 101793fc096SFrank Li .driver_data = 0, 102793fc096SFrank Li }, { 103793fc096SFrank Li .name = "imx25-fec", 104793fc096SFrank Li .driver_data = FEC_QUIRK_USE_GASKET, 105793fc096SFrank Li }, { 106793fc096SFrank Li .name = "imx27-fec", 107793fc096SFrank Li .driver_data = 0, 108793fc096SFrank Li }, { 109793fc096SFrank Li .name = "imx28-fec", 110793fc096SFrank Li .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME, 111793fc096SFrank Li }, { 112793fc096SFrank Li .name = "imx6q-fec", 113793fc096SFrank Li .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 114cdffcf1bSJim Baxter FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 115cdffcf1bSJim Baxter FEC_QUIRK_HAS_VLAN, 116793fc096SFrank Li }, { 11736803542SShawn Guo .name = "mvf600-fec", 118ca7c4a45SJingchang Lu .driver_data = FEC_QUIRK_ENET_MAC, 119ca7c4a45SJingchang Lu }, { 120793fc096SFrank Li /* sentinel */ 121793fc096SFrank Li } 122793fc096SFrank Li }; 123793fc096SFrank Li MODULE_DEVICE_TABLE(platform, fec_devtype); 124793fc096SFrank Li 125793fc096SFrank Li enum imx_fec_type { 126793fc096SFrank Li IMX25_FEC = 1, /* runs on i.mx25/50/53 */ 127793fc096SFrank Li IMX27_FEC, /* runs on i.mx27/35/51 */ 128793fc096SFrank Li IMX28_FEC, 129793fc096SFrank Li IMX6Q_FEC, 13036803542SShawn Guo MVF600_FEC, 131793fc096SFrank Li }; 132793fc096SFrank Li 133793fc096SFrank Li static const struct of_device_id fec_dt_ids[] = { 134793fc096SFrank Li { .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], }, 135793fc096SFrank Li { .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], }, 136793fc096SFrank Li { .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], }, 137793fc096SFrank Li { .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], }, 13836803542SShawn Guo { .compatible = "fsl,mvf600-fec", .data = &fec_devtype[MVF600_FEC], }, 139793fc096SFrank Li { /* sentinel */ } 140793fc096SFrank Li }; 141793fc096SFrank Li MODULE_DEVICE_TABLE(of, fec_dt_ids); 142793fc096SFrank Li 143793fc096SFrank Li static unsigned char macaddr[ETH_ALEN]; 144793fc096SFrank Li module_param_array(macaddr, byte, NULL, 0); 145793fc096SFrank Li MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); 146793fc096SFrank Li 147793fc096SFrank Li #if defined(CONFIG_M5272) 148793fc096SFrank Li /* 149793fc096SFrank Li * Some hardware gets it MAC address out of local flash memory. 150793fc096SFrank Li * if this is non-zero then assume it is the address to get MAC from. 151793fc096SFrank Li */ 152793fc096SFrank Li #if defined(CONFIG_NETtel) 153793fc096SFrank Li #define FEC_FLASHMAC 0xf0006006 154793fc096SFrank Li #elif defined(CONFIG_GILBARCONAP) || defined(CONFIG_SCALES) 155793fc096SFrank Li #define FEC_FLASHMAC 0xf0006000 156793fc096SFrank Li #elif defined(CONFIG_CANCam) 157793fc096SFrank Li #define FEC_FLASHMAC 0xf0020000 158793fc096SFrank Li #elif defined (CONFIG_M5272C3) 159793fc096SFrank Li #define FEC_FLASHMAC (0xffe04000 + 4) 160793fc096SFrank Li #elif defined(CONFIG_MOD5272) 161793fc096SFrank Li #define FEC_FLASHMAC 0xffc0406b 162793fc096SFrank Li #else 163793fc096SFrank Li #define FEC_FLASHMAC 0 164793fc096SFrank Li #endif 165793fc096SFrank Li #endif /* CONFIG_M5272 */ 166793fc096SFrank Li 167793fc096SFrank Li #if (((RX_RING_SIZE + TX_RING_SIZE) * 32) > PAGE_SIZE) 168793fc096SFrank Li #error "FEC: descriptor ring size constants too large" 169793fc096SFrank Li #endif 170793fc096SFrank Li 171793fc096SFrank Li /* Interrupt events/masks. */ 172793fc096SFrank Li #define FEC_ENET_HBERR ((uint)0x80000000) /* Heartbeat error */ 173793fc096SFrank Li #define FEC_ENET_BABR ((uint)0x40000000) /* Babbling receiver */ 174793fc096SFrank Li #define FEC_ENET_BABT ((uint)0x20000000) /* Babbling transmitter */ 175793fc096SFrank Li #define FEC_ENET_GRA ((uint)0x10000000) /* Graceful stop complete */ 176793fc096SFrank Li #define FEC_ENET_TXF ((uint)0x08000000) /* Full frame transmitted */ 177793fc096SFrank Li #define FEC_ENET_TXB ((uint)0x04000000) /* A buffer was transmitted */ 178793fc096SFrank Li #define FEC_ENET_RXF ((uint)0x02000000) /* Full frame received */ 179793fc096SFrank Li #define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */ 180793fc096SFrank Li #define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */ 181793fc096SFrank Li #define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */ 182793fc096SFrank Li 183793fc096SFrank Li #define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII) 184793fc096SFrank Li #define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF)) 185793fc096SFrank Li 186cdffcf1bSJim Baxter /* The FEC stores dest/src/type/vlan, data, and checksum for receive packets. 187793fc096SFrank Li */ 188cdffcf1bSJim Baxter #define PKT_MAXBUF_SIZE 1522 189793fc096SFrank Li #define PKT_MINBUF_SIZE 64 190cdffcf1bSJim Baxter #define PKT_MAXBLR_SIZE 1536 191793fc096SFrank Li 1924c09eed9SJim Baxter /* FEC receive acceleration */ 1934c09eed9SJim Baxter #define FEC_RACC_IPDIS (1 << 1) 1944c09eed9SJim Baxter #define FEC_RACC_PRODIS (1 << 2) 1954c09eed9SJim Baxter #define FEC_RACC_OPTIONS (FEC_RACC_IPDIS | FEC_RACC_PRODIS) 1964c09eed9SJim Baxter 197793fc096SFrank Li /* 198793fc096SFrank Li * The 5270/5271/5280/5282/532x RX control register also contains maximum frame 199793fc096SFrank Li * size bits. Other FEC hardware does not, so we need to take that into 200793fc096SFrank Li * account when setting it. 201793fc096SFrank Li */ 202793fc096SFrank Li #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ 203793fc096SFrank Li defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) 204793fc096SFrank Li #define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16) 205793fc096SFrank Li #else 206793fc096SFrank Li #define OPT_FRAME_SIZE 0 207793fc096SFrank Li #endif 208793fc096SFrank Li 209793fc096SFrank Li /* FEC MII MMFR bits definition */ 210793fc096SFrank Li #define FEC_MMFR_ST (1 << 30) 211793fc096SFrank Li #define FEC_MMFR_OP_READ (2 << 28) 212793fc096SFrank Li #define FEC_MMFR_OP_WRITE (1 << 28) 213793fc096SFrank Li #define FEC_MMFR_PA(v) ((v & 0x1f) << 23) 214793fc096SFrank Li #define FEC_MMFR_RA(v) ((v & 0x1f) << 18) 215793fc096SFrank Li #define FEC_MMFR_TA (2 << 16) 216793fc096SFrank Li #define FEC_MMFR_DATA(v) (v & 0xffff) 217793fc096SFrank Li 218793fc096SFrank Li #define FEC_MII_TIMEOUT 30000 /* us */ 219793fc096SFrank Li 220793fc096SFrank Li /* Transmitter timeout */ 221793fc096SFrank Li #define TX_TIMEOUT (2 * HZ) 222793fc096SFrank Li 223793fc096SFrank Li #define FEC_PAUSE_FLAG_AUTONEG 0x1 224793fc096SFrank Li #define FEC_PAUSE_FLAG_ENABLE 0x2 225793fc096SFrank Li 226793fc096SFrank Li static int mii_cnt; 227793fc096SFrank Li 228793fc096SFrank Li static struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, int is_ex) 229793fc096SFrank Li { 230793fc096SFrank Li struct bufdesc_ex *ex = (struct bufdesc_ex *)bdp; 231793fc096SFrank Li if (is_ex) 232793fc096SFrank Li return (struct bufdesc *)(ex + 1); 233793fc096SFrank Li else 234793fc096SFrank Li return bdp + 1; 235793fc096SFrank Li } 236793fc096SFrank Li 237793fc096SFrank Li static struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, int is_ex) 238793fc096SFrank Li { 239793fc096SFrank Li struct bufdesc_ex *ex = (struct bufdesc_ex *)bdp; 240793fc096SFrank Li if (is_ex) 241793fc096SFrank Li return (struct bufdesc *)(ex - 1); 242793fc096SFrank Li else 243793fc096SFrank Li return bdp - 1; 244793fc096SFrank Li } 245793fc096SFrank Li 246793fc096SFrank Li static void *swap_buffer(void *bufaddr, int len) 247793fc096SFrank Li { 248793fc096SFrank Li int i; 249793fc096SFrank Li unsigned int *buf = bufaddr; 250793fc096SFrank Li 251ffed61e6SFabio Estevam for (i = 0; i < DIV_ROUND_UP(len, 4); i++, buf++) 252793fc096SFrank Li *buf = cpu_to_be32(*buf); 253793fc096SFrank Li 254793fc096SFrank Li return bufaddr; 255793fc096SFrank Li } 256793fc096SFrank Li 2574c09eed9SJim Baxter static int 2584c09eed9SJim Baxter fec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev) 2594c09eed9SJim Baxter { 2604c09eed9SJim Baxter /* Only run for packets requiring a checksum. */ 2614c09eed9SJim Baxter if (skb->ip_summed != CHECKSUM_PARTIAL) 2624c09eed9SJim Baxter return 0; 2634c09eed9SJim Baxter 2644c09eed9SJim Baxter if (unlikely(skb_cow_head(skb, 0))) 2654c09eed9SJim Baxter return -1; 2664c09eed9SJim Baxter 2674c09eed9SJim Baxter *(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) = 0; 2684c09eed9SJim Baxter 2694c09eed9SJim Baxter return 0; 2704c09eed9SJim Baxter } 2714c09eed9SJim Baxter 272793fc096SFrank Li static netdev_tx_t 273793fc096SFrank Li fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) 274793fc096SFrank Li { 275793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 276793fc096SFrank Li const struct platform_device_id *id_entry = 277793fc096SFrank Li platform_get_device_id(fep->pdev); 278793fc096SFrank Li struct bufdesc *bdp; 279793fc096SFrank Li void *bufaddr; 280793fc096SFrank Li unsigned short status; 281793fc096SFrank Li unsigned int index; 282793fc096SFrank Li 283793fc096SFrank Li if (!fep->link) { 2844c09eed9SJim Baxter /* Link is down or auto-negotiation is in progress. */ 285793fc096SFrank Li return NETDEV_TX_BUSY; 286793fc096SFrank Li } 287793fc096SFrank Li 288793fc096SFrank Li /* Fill in a Tx ring entry */ 289793fc096SFrank Li bdp = fep->cur_tx; 290793fc096SFrank Li 291793fc096SFrank Li status = bdp->cbd_sc; 292793fc096SFrank Li 293793fc096SFrank Li if (status & BD_ENET_TX_READY) { 294793fc096SFrank Li /* Ooops. All transmit buffers are full. Bail out. 295793fc096SFrank Li * This should not happen, since ndev->tbusy should be set. 296793fc096SFrank Li */ 29731b7720cSJoe Perches netdev_err(ndev, "tx queue full!\n"); 298793fc096SFrank Li return NETDEV_TX_BUSY; 299793fc096SFrank Li } 300793fc096SFrank Li 3014c09eed9SJim Baxter /* Protocol checksum off-load for TCP and UDP. */ 3024c09eed9SJim Baxter if (fec_enet_clear_csum(skb, ndev)) { 3034c09eed9SJim Baxter kfree_skb(skb); 3044c09eed9SJim Baxter return NETDEV_TX_OK; 3054c09eed9SJim Baxter } 3064c09eed9SJim Baxter 307793fc096SFrank Li /* Clear all of the status flags */ 308793fc096SFrank Li status &= ~BD_ENET_TX_STATS; 309793fc096SFrank Li 310793fc096SFrank Li /* Set buffer length and buffer pointer */ 311793fc096SFrank Li bufaddr = skb->data; 312793fc096SFrank Li bdp->cbd_datlen = skb->len; 313793fc096SFrank Li 314793fc096SFrank Li /* 315793fc096SFrank Li * On some FEC implementations data must be aligned on 316793fc096SFrank Li * 4-byte boundaries. Use bounce buffers to copy data 317793fc096SFrank Li * and get it aligned. Ugh. 318793fc096SFrank Li */ 319793fc096SFrank Li if (fep->bufdesc_ex) 320793fc096SFrank Li index = (struct bufdesc_ex *)bdp - 321793fc096SFrank Li (struct bufdesc_ex *)fep->tx_bd_base; 322793fc096SFrank Li else 323793fc096SFrank Li index = bdp - fep->tx_bd_base; 324793fc096SFrank Li 325793fc096SFrank Li if (((unsigned long) bufaddr) & FEC_ALIGNMENT) { 326793fc096SFrank Li memcpy(fep->tx_bounce[index], skb->data, skb->len); 327793fc096SFrank Li bufaddr = fep->tx_bounce[index]; 328793fc096SFrank Li } 329793fc096SFrank Li 330793fc096SFrank Li /* 331793fc096SFrank Li * Some design made an incorrect assumption on endian mode of 332793fc096SFrank Li * the system that it's running on. As the result, driver has to 333793fc096SFrank Li * swap every frame going to and coming from the controller. 334793fc096SFrank Li */ 335793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) 336793fc096SFrank Li swap_buffer(bufaddr, skb->len); 337793fc096SFrank Li 338793fc096SFrank Li /* Save skb pointer */ 339793fc096SFrank Li fep->tx_skbuff[index] = skb; 340793fc096SFrank Li 341793fc096SFrank Li /* Push the data cache so the CPM does not get stale memory 342793fc096SFrank Li * data. 343793fc096SFrank Li */ 344793fc096SFrank Li bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, bufaddr, 345793fc096SFrank Li FEC_ENET_TX_FRSIZE, DMA_TO_DEVICE); 346793fc096SFrank Li 347793fc096SFrank Li /* Send it on its way. Tell FEC it's ready, interrupt when done, 348793fc096SFrank Li * it's the last BD of the frame, and to put the CRC on the end. 349793fc096SFrank Li */ 350793fc096SFrank Li status |= (BD_ENET_TX_READY | BD_ENET_TX_INTR 351793fc096SFrank Li | BD_ENET_TX_LAST | BD_ENET_TX_TC); 352793fc096SFrank Li bdp->cbd_sc = status; 353793fc096SFrank Li 354793fc096SFrank Li if (fep->bufdesc_ex) { 355793fc096SFrank Li 356793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 357793fc096SFrank Li ebdp->cbd_bdu = 0; 358793fc096SFrank Li if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && 359793fc096SFrank Li fep->hwts_tx_en)) { 360793fc096SFrank Li ebdp->cbd_esc = (BD_ENET_TX_TS | BD_ENET_TX_INT); 361793fc096SFrank Li skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; 362793fc096SFrank Li } else { 363793fc096SFrank Li ebdp->cbd_esc = BD_ENET_TX_INT; 3644c09eed9SJim Baxter 3654c09eed9SJim Baxter /* Enable protocol checksum flags 3664c09eed9SJim Baxter * We do not bother with the IP Checksum bits as they 3674c09eed9SJim Baxter * are done by the kernel 3684c09eed9SJim Baxter */ 3694c09eed9SJim Baxter if (skb->ip_summed == CHECKSUM_PARTIAL) 3704c09eed9SJim Baxter ebdp->cbd_esc |= BD_ENET_TX_PINS; 371793fc096SFrank Li } 372793fc096SFrank Li } 373793fc096SFrank Li /* If this was the last BD in the ring, start at the beginning again. */ 374793fc096SFrank Li if (status & BD_ENET_TX_WRAP) 375793fc096SFrank Li bdp = fep->tx_bd_base; 376793fc096SFrank Li else 377793fc096SFrank Li bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex); 378793fc096SFrank Li 379793fc096SFrank Li fep->cur_tx = bdp; 380793fc096SFrank Li 381793fc096SFrank Li if (fep->cur_tx == fep->dirty_tx) 382793fc096SFrank Li netif_stop_queue(ndev); 383793fc096SFrank Li 384793fc096SFrank Li /* Trigger transmission start */ 385793fc096SFrank Li writel(0, fep->hwp + FEC_X_DES_ACTIVE); 386793fc096SFrank Li 387793fc096SFrank Li skb_tx_timestamp(skb); 388793fc096SFrank Li 389793fc096SFrank Li return NETDEV_TX_OK; 390793fc096SFrank Li } 391793fc096SFrank Li 392a210576cSDavid S. Miller /* Init RX & TX buffer descriptors 393a210576cSDavid S. Miller */ 394a210576cSDavid S. Miller static void fec_enet_bd_init(struct net_device *dev) 395a210576cSDavid S. Miller { 396a210576cSDavid S. Miller struct fec_enet_private *fep = netdev_priv(dev); 397a210576cSDavid S. Miller struct bufdesc *bdp; 398a210576cSDavid S. Miller unsigned int i; 399a210576cSDavid S. Miller 400a210576cSDavid S. Miller /* Initialize the receive buffer descriptors. */ 401a210576cSDavid S. Miller bdp = fep->rx_bd_base; 402a210576cSDavid S. Miller for (i = 0; i < RX_RING_SIZE; i++) { 403a210576cSDavid S. Miller 404a210576cSDavid S. Miller /* Initialize the BD for every fragment in the page. */ 405a210576cSDavid S. Miller if (bdp->cbd_bufaddr) 406a210576cSDavid S. Miller bdp->cbd_sc = BD_ENET_RX_EMPTY; 407a210576cSDavid S. Miller else 408a210576cSDavid S. Miller bdp->cbd_sc = 0; 409a210576cSDavid S. Miller bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex); 410a210576cSDavid S. Miller } 411a210576cSDavid S. Miller 412a210576cSDavid S. Miller /* Set the last buffer to wrap */ 413a210576cSDavid S. Miller bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex); 414a210576cSDavid S. Miller bdp->cbd_sc |= BD_SC_WRAP; 415a210576cSDavid S. Miller 416a210576cSDavid S. Miller fep->cur_rx = fep->rx_bd_base; 417a210576cSDavid S. Miller 418a210576cSDavid S. Miller /* ...and the same for transmit */ 419a210576cSDavid S. Miller bdp = fep->tx_bd_base; 420a210576cSDavid S. Miller fep->cur_tx = bdp; 421a210576cSDavid S. Miller for (i = 0; i < TX_RING_SIZE; i++) { 422a210576cSDavid S. Miller 423a210576cSDavid S. Miller /* Initialize the BD for every fragment in the page. */ 424a210576cSDavid S. Miller bdp->cbd_sc = 0; 425a210576cSDavid S. Miller if (bdp->cbd_bufaddr && fep->tx_skbuff[i]) { 426a210576cSDavid S. Miller dev_kfree_skb_any(fep->tx_skbuff[i]); 427a210576cSDavid S. Miller fep->tx_skbuff[i] = NULL; 428a210576cSDavid S. Miller } 429a210576cSDavid S. Miller bdp->cbd_bufaddr = 0; 430a210576cSDavid S. Miller bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex); 431a210576cSDavid S. Miller } 432a210576cSDavid S. Miller 433a210576cSDavid S. Miller /* Set the last buffer to wrap */ 434a210576cSDavid S. Miller bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex); 435a210576cSDavid S. Miller bdp->cbd_sc |= BD_SC_WRAP; 436a210576cSDavid S. Miller fep->dirty_tx = bdp; 437a210576cSDavid S. Miller } 438a210576cSDavid S. Miller 439793fc096SFrank Li /* This function is called to start or restart the FEC during a link 440793fc096SFrank Li * change. This only happens when switching between half and full 441793fc096SFrank Li * duplex. 442793fc096SFrank Li */ 443793fc096SFrank Li static void 444793fc096SFrank Li fec_restart(struct net_device *ndev, int duplex) 445793fc096SFrank Li { 446793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 447793fc096SFrank Li const struct platform_device_id *id_entry = 448793fc096SFrank Li platform_get_device_id(fep->pdev); 449793fc096SFrank Li int i; 4504c09eed9SJim Baxter u32 val; 451793fc096SFrank Li u32 temp_mac[2]; 452793fc096SFrank Li u32 rcntl = OPT_FRAME_SIZE | 0x04; 453793fc096SFrank Li u32 ecntl = 0x2; /* ETHEREN */ 454793fc096SFrank Li 45554309fa6SFrank Li if (netif_running(ndev)) { 45654309fa6SFrank Li netif_device_detach(ndev); 45754309fa6SFrank Li napi_disable(&fep->napi); 45854309fa6SFrank Li netif_stop_queue(ndev); 45931691344SFabio Estevam netif_tx_lock_bh(ndev); 46054309fa6SFrank Li } 46154309fa6SFrank Li 462793fc096SFrank Li /* Whack a reset. We should wait for this. */ 463793fc096SFrank Li writel(1, fep->hwp + FEC_ECNTRL); 464793fc096SFrank Li udelay(10); 465793fc096SFrank Li 466793fc096SFrank Li /* 467793fc096SFrank Li * enet-mac reset will reset mac address registers too, 468793fc096SFrank Li * so need to reconfigure it. 469793fc096SFrank Li */ 470793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) { 471793fc096SFrank Li memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN); 472793fc096SFrank Li writel(cpu_to_be32(temp_mac[0]), fep->hwp + FEC_ADDR_LOW); 473793fc096SFrank Li writel(cpu_to_be32(temp_mac[1]), fep->hwp + FEC_ADDR_HIGH); 474793fc096SFrank Li } 475793fc096SFrank Li 476793fc096SFrank Li /* Clear any outstanding interrupt. */ 477793fc096SFrank Li writel(0xffc00000, fep->hwp + FEC_IEVENT); 478793fc096SFrank Li 479772e42b0SChristoph Müllner /* Setup multicast filter. */ 480772e42b0SChristoph Müllner set_multicast_list(ndev); 481793fc096SFrank Li #ifndef CONFIG_M5272 482793fc096SFrank Li writel(0, fep->hwp + FEC_HASH_TABLE_HIGH); 483793fc096SFrank Li writel(0, fep->hwp + FEC_HASH_TABLE_LOW); 484793fc096SFrank Li #endif 485793fc096SFrank Li 486793fc096SFrank Li /* Set maximum receive buffer size. */ 487793fc096SFrank Li writel(PKT_MAXBLR_SIZE, fep->hwp + FEC_R_BUFF_SIZE); 488793fc096SFrank Li 489a210576cSDavid S. Miller fec_enet_bd_init(ndev); 490a210576cSDavid S. Miller 491793fc096SFrank Li /* Set receive and transmit descriptor base. */ 492793fc096SFrank Li writel(fep->bd_dma, fep->hwp + FEC_R_DES_START); 493793fc096SFrank Li if (fep->bufdesc_ex) 494793fc096SFrank Li writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc_ex) 495793fc096SFrank Li * RX_RING_SIZE, fep->hwp + FEC_X_DES_START); 496793fc096SFrank Li else 497793fc096SFrank Li writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc) 498793fc096SFrank Li * RX_RING_SIZE, fep->hwp + FEC_X_DES_START); 499793fc096SFrank Li 500793fc096SFrank Li 501793fc096SFrank Li for (i = 0; i <= TX_RING_MOD_MASK; i++) { 502793fc096SFrank Li if (fep->tx_skbuff[i]) { 503793fc096SFrank Li dev_kfree_skb_any(fep->tx_skbuff[i]); 504793fc096SFrank Li fep->tx_skbuff[i] = NULL; 505793fc096SFrank Li } 506793fc096SFrank Li } 507793fc096SFrank Li 508793fc096SFrank Li /* Enable MII mode */ 509793fc096SFrank Li if (duplex) { 510793fc096SFrank Li /* FD enable */ 511793fc096SFrank Li writel(0x04, fep->hwp + FEC_X_CNTRL); 512793fc096SFrank Li } else { 513793fc096SFrank Li /* No Rcv on Xmit */ 514793fc096SFrank Li rcntl |= 0x02; 515793fc096SFrank Li writel(0x0, fep->hwp + FEC_X_CNTRL); 516793fc096SFrank Li } 517793fc096SFrank Li 518793fc096SFrank Li fep->full_duplex = duplex; 519793fc096SFrank Li 520793fc096SFrank Li /* Set MII speed */ 521793fc096SFrank Li writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 522793fc096SFrank Li 523d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 5244c09eed9SJim Baxter /* set RX checksum */ 5254c09eed9SJim Baxter val = readl(fep->hwp + FEC_RACC); 5264c09eed9SJim Baxter if (fep->csum_flags & FLAG_RX_CSUM_ENABLED) 5274c09eed9SJim Baxter val |= FEC_RACC_OPTIONS; 5284c09eed9SJim Baxter else 5294c09eed9SJim Baxter val &= ~FEC_RACC_OPTIONS; 5304c09eed9SJim Baxter writel(val, fep->hwp + FEC_RACC); 531d1391930SGuenter Roeck #endif 5324c09eed9SJim Baxter 533793fc096SFrank Li /* 534793fc096SFrank Li * The phy interface and speed need to get configured 535793fc096SFrank Li * differently on enet-mac. 536793fc096SFrank Li */ 537793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) { 538793fc096SFrank Li /* Enable flow control and length check */ 539793fc096SFrank Li rcntl |= 0x40000000 | 0x00000020; 540793fc096SFrank Li 541793fc096SFrank Li /* RGMII, RMII or MII */ 542793fc096SFrank Li if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII) 543793fc096SFrank Li rcntl |= (1 << 6); 544793fc096SFrank Li else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) 545793fc096SFrank Li rcntl |= (1 << 8); 546793fc096SFrank Li else 547793fc096SFrank Li rcntl &= ~(1 << 8); 548793fc096SFrank Li 549793fc096SFrank Li /* 1G, 100M or 10M */ 550793fc096SFrank Li if (fep->phy_dev) { 551793fc096SFrank Li if (fep->phy_dev->speed == SPEED_1000) 552793fc096SFrank Li ecntl |= (1 << 5); 553793fc096SFrank Li else if (fep->phy_dev->speed == SPEED_100) 554793fc096SFrank Li rcntl &= ~(1 << 9); 555793fc096SFrank Li else 556793fc096SFrank Li rcntl |= (1 << 9); 557793fc096SFrank Li } 558793fc096SFrank Li } else { 559793fc096SFrank Li #ifdef FEC_MIIGSK_ENR 560793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_USE_GASKET) { 561793fc096SFrank Li u32 cfgr; 562793fc096SFrank Li /* disable the gasket and wait */ 563793fc096SFrank Li writel(0, fep->hwp + FEC_MIIGSK_ENR); 564793fc096SFrank Li while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4) 565793fc096SFrank Li udelay(1); 566793fc096SFrank Li 567793fc096SFrank Li /* 568793fc096SFrank Li * configure the gasket: 569793fc096SFrank Li * RMII, 50 MHz, no loopback, no echo 570793fc096SFrank Li * MII, 25 MHz, no loopback, no echo 571793fc096SFrank Li */ 572793fc096SFrank Li cfgr = (fep->phy_interface == PHY_INTERFACE_MODE_RMII) 573793fc096SFrank Li ? BM_MIIGSK_CFGR_RMII : BM_MIIGSK_CFGR_MII; 574793fc096SFrank Li if (fep->phy_dev && fep->phy_dev->speed == SPEED_10) 575793fc096SFrank Li cfgr |= BM_MIIGSK_CFGR_FRCONT_10M; 576793fc096SFrank Li writel(cfgr, fep->hwp + FEC_MIIGSK_CFGR); 577793fc096SFrank Li 578793fc096SFrank Li /* re-enable the gasket */ 579793fc096SFrank Li writel(2, fep->hwp + FEC_MIIGSK_ENR); 580793fc096SFrank Li } 581793fc096SFrank Li #endif 582793fc096SFrank Li } 583793fc096SFrank Li 584d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 585793fc096SFrank Li /* enable pause frame*/ 586793fc096SFrank Li if ((fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) || 587793fc096SFrank Li ((fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) && 588793fc096SFrank Li fep->phy_dev && fep->phy_dev->pause)) { 589793fc096SFrank Li rcntl |= FEC_ENET_FCE; 590793fc096SFrank Li 5914c09eed9SJim Baxter /* set FIFO threshold parameter to reduce overrun */ 592793fc096SFrank Li writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM); 593793fc096SFrank Li writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL); 594793fc096SFrank Li writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM); 595793fc096SFrank Li writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL); 596793fc096SFrank Li 597793fc096SFrank Li /* OPD */ 598793fc096SFrank Li writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD); 599793fc096SFrank Li } else { 600793fc096SFrank Li rcntl &= ~FEC_ENET_FCE; 601793fc096SFrank Li } 602d1391930SGuenter Roeck #endif /* !defined(CONFIG_M5272) */ 603793fc096SFrank Li 604793fc096SFrank Li writel(rcntl, fep->hwp + FEC_R_CNTRL); 605793fc096SFrank Li 606793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) { 607793fc096SFrank Li /* enable ENET endian swap */ 608793fc096SFrank Li ecntl |= (1 << 8); 609793fc096SFrank Li /* enable ENET store and forward mode */ 610793fc096SFrank Li writel(1 << 8, fep->hwp + FEC_X_WMRK); 611793fc096SFrank Li } 612793fc096SFrank Li 613793fc096SFrank Li if (fep->bufdesc_ex) 614793fc096SFrank Li ecntl |= (1 << 4); 615793fc096SFrank Li 61638ae92dcSChris Healy #ifndef CONFIG_M5272 617b9eef55cSJim Baxter /* Enable the MIB statistic event counters */ 618b9eef55cSJim Baxter writel(0 << 31, fep->hwp + FEC_MIB_CTRLSTAT); 61938ae92dcSChris Healy #endif 62038ae92dcSChris Healy 621793fc096SFrank Li /* And last, enable the transmit and receive processing */ 622793fc096SFrank Li writel(ecntl, fep->hwp + FEC_ECNTRL); 623793fc096SFrank Li writel(0, fep->hwp + FEC_R_DES_ACTIVE); 624793fc096SFrank Li 625793fc096SFrank Li if (fep->bufdesc_ex) 626793fc096SFrank Li fec_ptp_start_cyclecounter(ndev); 627793fc096SFrank Li 628793fc096SFrank Li /* Enable interrupts we wish to service */ 629793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 63054309fa6SFrank Li 63154309fa6SFrank Li if (netif_running(ndev)) { 63231691344SFabio Estevam netif_tx_unlock_bh(ndev); 63354309fa6SFrank Li netif_wake_queue(ndev); 6341ed0d56cSFabio Estevam napi_enable(&fep->napi); 6351ed0d56cSFabio Estevam netif_device_attach(ndev); 63654309fa6SFrank Li } 637793fc096SFrank Li } 638793fc096SFrank Li 639793fc096SFrank Li static void 640793fc096SFrank Li fec_stop(struct net_device *ndev) 641793fc096SFrank Li { 642793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 643793fc096SFrank Li const struct platform_device_id *id_entry = 644793fc096SFrank Li platform_get_device_id(fep->pdev); 645793fc096SFrank Li u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8); 646793fc096SFrank Li 647793fc096SFrank Li /* We cannot expect a graceful transmit stop without link !!! */ 648793fc096SFrank Li if (fep->link) { 649793fc096SFrank Li writel(1, fep->hwp + FEC_X_CNTRL); /* Graceful transmit stop */ 650793fc096SFrank Li udelay(10); 651793fc096SFrank Li if (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_GRA)) 65231b7720cSJoe Perches netdev_err(ndev, "Graceful transmit stop did not complete!\n"); 653793fc096SFrank Li } 654793fc096SFrank Li 655793fc096SFrank Li /* Whack a reset. We should wait for this. */ 656793fc096SFrank Li writel(1, fep->hwp + FEC_ECNTRL); 657793fc096SFrank Li udelay(10); 658793fc096SFrank Li writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 659793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 660793fc096SFrank Li 661793fc096SFrank Li /* We have to keep ENET enabled to have MII interrupt stay working */ 662793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) { 663793fc096SFrank Li writel(2, fep->hwp + FEC_ECNTRL); 664793fc096SFrank Li writel(rmii_mode, fep->hwp + FEC_R_CNTRL); 665793fc096SFrank Li } 666793fc096SFrank Li } 667793fc096SFrank Li 668793fc096SFrank Li 669793fc096SFrank Li static void 670793fc096SFrank Li fec_timeout(struct net_device *ndev) 671793fc096SFrank Li { 672793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 673793fc096SFrank Li 674793fc096SFrank Li ndev->stats.tx_errors++; 675793fc096SFrank Li 67654309fa6SFrank Li fep->delay_work.timeout = true; 67754309fa6SFrank Li schedule_delayed_work(&(fep->delay_work.delay_work), 0); 67854309fa6SFrank Li } 67954309fa6SFrank Li 68054309fa6SFrank Li static void fec_enet_work(struct work_struct *work) 68154309fa6SFrank Li { 68254309fa6SFrank Li struct fec_enet_private *fep = 68354309fa6SFrank Li container_of(work, 68454309fa6SFrank Li struct fec_enet_private, 68554309fa6SFrank Li delay_work.delay_work.work); 68654309fa6SFrank Li 68754309fa6SFrank Li if (fep->delay_work.timeout) { 68854309fa6SFrank Li fep->delay_work.timeout = false; 68954309fa6SFrank Li fec_restart(fep->netdev, fep->full_duplex); 69054309fa6SFrank Li netif_wake_queue(fep->netdev); 69154309fa6SFrank Li } 692793fc096SFrank Li } 693793fc096SFrank Li 694793fc096SFrank Li static void 695793fc096SFrank Li fec_enet_tx(struct net_device *ndev) 696793fc096SFrank Li { 697793fc096SFrank Li struct fec_enet_private *fep; 698793fc096SFrank Li struct bufdesc *bdp; 699793fc096SFrank Li unsigned short status; 700793fc096SFrank Li struct sk_buff *skb; 701793fc096SFrank Li int index = 0; 702793fc096SFrank Li 703793fc096SFrank Li fep = netdev_priv(ndev); 704793fc096SFrank Li bdp = fep->dirty_tx; 705793fc096SFrank Li 706793fc096SFrank Li /* get next bdp of dirty_tx */ 707793fc096SFrank Li if (bdp->cbd_sc & BD_ENET_TX_WRAP) 708793fc096SFrank Li bdp = fep->tx_bd_base; 709793fc096SFrank Li else 710793fc096SFrank Li bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex); 711793fc096SFrank Li 712793fc096SFrank Li while (((status = bdp->cbd_sc) & BD_ENET_TX_READY) == 0) { 713793fc096SFrank Li 714793fc096SFrank Li /* current queue is empty */ 715793fc096SFrank Li if (bdp == fep->cur_tx) 716793fc096SFrank Li break; 717793fc096SFrank Li 718793fc096SFrank Li if (fep->bufdesc_ex) 719793fc096SFrank Li index = (struct bufdesc_ex *)bdp - 720793fc096SFrank Li (struct bufdesc_ex *)fep->tx_bd_base; 721793fc096SFrank Li else 722793fc096SFrank Li index = bdp - fep->tx_bd_base; 723793fc096SFrank Li 724793fc096SFrank Li dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, 725793fc096SFrank Li FEC_ENET_TX_FRSIZE, DMA_TO_DEVICE); 726793fc096SFrank Li bdp->cbd_bufaddr = 0; 727793fc096SFrank Li 728793fc096SFrank Li skb = fep->tx_skbuff[index]; 729793fc096SFrank Li 730793fc096SFrank Li /* Check for errors. */ 731793fc096SFrank Li if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC | 732793fc096SFrank Li BD_ENET_TX_RL | BD_ENET_TX_UN | 733793fc096SFrank Li BD_ENET_TX_CSL)) { 734793fc096SFrank Li ndev->stats.tx_errors++; 735793fc096SFrank Li if (status & BD_ENET_TX_HB) /* No heartbeat */ 736793fc096SFrank Li ndev->stats.tx_heartbeat_errors++; 737793fc096SFrank Li if (status & BD_ENET_TX_LC) /* Late collision */ 738793fc096SFrank Li ndev->stats.tx_window_errors++; 739793fc096SFrank Li if (status & BD_ENET_TX_RL) /* Retrans limit */ 740793fc096SFrank Li ndev->stats.tx_aborted_errors++; 741793fc096SFrank Li if (status & BD_ENET_TX_UN) /* Underrun */ 742793fc096SFrank Li ndev->stats.tx_fifo_errors++; 743793fc096SFrank Li if (status & BD_ENET_TX_CSL) /* Carrier lost */ 744793fc096SFrank Li ndev->stats.tx_carrier_errors++; 745793fc096SFrank Li } else { 746793fc096SFrank Li ndev->stats.tx_packets++; 74706efce71SJim Baxter ndev->stats.tx_bytes += bdp->cbd_datlen; 748793fc096SFrank Li } 749793fc096SFrank Li 750793fc096SFrank Li if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) && 751793fc096SFrank Li fep->bufdesc_ex) { 752793fc096SFrank Li struct skb_shared_hwtstamps shhwtstamps; 753793fc096SFrank Li unsigned long flags; 754793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 755793fc096SFrank Li 756793fc096SFrank Li memset(&shhwtstamps, 0, sizeof(shhwtstamps)); 757793fc096SFrank Li spin_lock_irqsave(&fep->tmreg_lock, flags); 758793fc096SFrank Li shhwtstamps.hwtstamp = ns_to_ktime( 759793fc096SFrank Li timecounter_cyc2time(&fep->tc, ebdp->ts)); 760793fc096SFrank Li spin_unlock_irqrestore(&fep->tmreg_lock, flags); 761793fc096SFrank Li skb_tstamp_tx(skb, &shhwtstamps); 762793fc096SFrank Li } 763793fc096SFrank Li 764793fc096SFrank Li if (status & BD_ENET_TX_READY) 76531b7720cSJoe Perches netdev_err(ndev, "HEY! Enet xmit interrupt and TX_READY\n"); 766793fc096SFrank Li 767793fc096SFrank Li /* Deferred means some collisions occurred during transmit, 768793fc096SFrank Li * but we eventually sent the packet OK. 769793fc096SFrank Li */ 770793fc096SFrank Li if (status & BD_ENET_TX_DEF) 771793fc096SFrank Li ndev->stats.collisions++; 772793fc096SFrank Li 773793fc096SFrank Li /* Free the sk buffer associated with this last transmit */ 774793fc096SFrank Li dev_kfree_skb_any(skb); 775793fc096SFrank Li fep->tx_skbuff[index] = NULL; 776793fc096SFrank Li 777793fc096SFrank Li fep->dirty_tx = bdp; 778793fc096SFrank Li 779793fc096SFrank Li /* Update pointer to next buffer descriptor to be transmitted */ 780793fc096SFrank Li if (status & BD_ENET_TX_WRAP) 781793fc096SFrank Li bdp = fep->tx_bd_base; 782793fc096SFrank Li else 783793fc096SFrank Li bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex); 784793fc096SFrank Li 785793fc096SFrank Li /* Since we have freed up a buffer, the ring is no longer full 786793fc096SFrank Li */ 787793fc096SFrank Li if (fep->dirty_tx != fep->cur_tx) { 788793fc096SFrank Li if (netif_queue_stopped(ndev)) 789793fc096SFrank Li netif_wake_queue(ndev); 790793fc096SFrank Li } 791793fc096SFrank Li } 792793fc096SFrank Li return; 793793fc096SFrank Li } 794793fc096SFrank Li 795793fc096SFrank Li 796793fc096SFrank Li /* During a receive, the cur_rx points to the current incoming buffer. 797793fc096SFrank Li * When we update through the ring, if the next incoming buffer has 798793fc096SFrank Li * not been given to the system, we just set the empty indicator, 799793fc096SFrank Li * effectively tossing the packet. 800793fc096SFrank Li */ 801793fc096SFrank Li static int 802793fc096SFrank Li fec_enet_rx(struct net_device *ndev, int budget) 803793fc096SFrank Li { 804793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 805793fc096SFrank Li const struct platform_device_id *id_entry = 806793fc096SFrank Li platform_get_device_id(fep->pdev); 807793fc096SFrank Li struct bufdesc *bdp; 808793fc096SFrank Li unsigned short status; 809793fc096SFrank Li struct sk_buff *skb; 810793fc096SFrank Li ushort pkt_len; 811793fc096SFrank Li __u8 *data; 812793fc096SFrank Li int pkt_received = 0; 813cdffcf1bSJim Baxter struct bufdesc_ex *ebdp = NULL; 814cdffcf1bSJim Baxter bool vlan_packet_rcvd = false; 815cdffcf1bSJim Baxter u16 vlan_tag; 816793fc096SFrank Li 817793fc096SFrank Li #ifdef CONFIG_M532x 818793fc096SFrank Li flush_cache_all(); 819793fc096SFrank Li #endif 820793fc096SFrank Li 821793fc096SFrank Li /* First, grab all of the stats for the incoming packet. 822793fc096SFrank Li * These get messed up if we get called due to a busy condition. 823793fc096SFrank Li */ 824793fc096SFrank Li bdp = fep->cur_rx; 825793fc096SFrank Li 826793fc096SFrank Li while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) { 827793fc096SFrank Li 828793fc096SFrank Li if (pkt_received >= budget) 829793fc096SFrank Li break; 830793fc096SFrank Li pkt_received++; 831793fc096SFrank Li 832793fc096SFrank Li /* Since we have allocated space to hold a complete frame, 833793fc096SFrank Li * the last indicator should be set. 834793fc096SFrank Li */ 835793fc096SFrank Li if ((status & BD_ENET_RX_LAST) == 0) 83631b7720cSJoe Perches netdev_err(ndev, "rcv is not +last\n"); 837793fc096SFrank Li 838793fc096SFrank Li if (!fep->opened) 839793fc096SFrank Li goto rx_processing_done; 840793fc096SFrank Li 841793fc096SFrank Li /* Check for errors. */ 842793fc096SFrank Li if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | 843793fc096SFrank Li BD_ENET_RX_CR | BD_ENET_RX_OV)) { 844793fc096SFrank Li ndev->stats.rx_errors++; 845793fc096SFrank Li if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH)) { 846793fc096SFrank Li /* Frame too long or too short. */ 847793fc096SFrank Li ndev->stats.rx_length_errors++; 848793fc096SFrank Li } 849793fc096SFrank Li if (status & BD_ENET_RX_NO) /* Frame alignment */ 850793fc096SFrank Li ndev->stats.rx_frame_errors++; 851793fc096SFrank Li if (status & BD_ENET_RX_CR) /* CRC Error */ 852793fc096SFrank Li ndev->stats.rx_crc_errors++; 853793fc096SFrank Li if (status & BD_ENET_RX_OV) /* FIFO overrun */ 854793fc096SFrank Li ndev->stats.rx_fifo_errors++; 855793fc096SFrank Li } 856793fc096SFrank Li 857793fc096SFrank Li /* Report late collisions as a frame error. 858793fc096SFrank Li * On this error, the BD is closed, but we don't know what we 859793fc096SFrank Li * have in the buffer. So, just drop this frame on the floor. 860793fc096SFrank Li */ 861793fc096SFrank Li if (status & BD_ENET_RX_CL) { 862793fc096SFrank Li ndev->stats.rx_errors++; 863793fc096SFrank Li ndev->stats.rx_frame_errors++; 864793fc096SFrank Li goto rx_processing_done; 865793fc096SFrank Li } 866793fc096SFrank Li 867793fc096SFrank Li /* Process the incoming frame. */ 868793fc096SFrank Li ndev->stats.rx_packets++; 869793fc096SFrank Li pkt_len = bdp->cbd_datlen; 870793fc096SFrank Li ndev->stats.rx_bytes += pkt_len; 871793fc096SFrank Li data = (__u8*)__va(bdp->cbd_bufaddr); 872793fc096SFrank Li 873793fc096SFrank Li dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, 874793fc096SFrank Li FEC_ENET_TX_FRSIZE, DMA_FROM_DEVICE); 875793fc096SFrank Li 876793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) 877793fc096SFrank Li swap_buffer(data, pkt_len); 878793fc096SFrank Li 879cdffcf1bSJim Baxter /* Extract the enhanced buffer descriptor */ 880cdffcf1bSJim Baxter ebdp = NULL; 881cdffcf1bSJim Baxter if (fep->bufdesc_ex) 882cdffcf1bSJim Baxter ebdp = (struct bufdesc_ex *)bdp; 883cdffcf1bSJim Baxter 884cdffcf1bSJim Baxter /* If this is a VLAN packet remove the VLAN Tag */ 885cdffcf1bSJim Baxter vlan_packet_rcvd = false; 886cdffcf1bSJim Baxter if ((ndev->features & NETIF_F_HW_VLAN_CTAG_RX) && 887cdffcf1bSJim Baxter fep->bufdesc_ex && (ebdp->cbd_esc & BD_ENET_RX_VLAN)) { 888cdffcf1bSJim Baxter /* Push and remove the vlan tag */ 889cdffcf1bSJim Baxter struct vlan_hdr *vlan_header = 890cdffcf1bSJim Baxter (struct vlan_hdr *) (data + ETH_HLEN); 891cdffcf1bSJim Baxter vlan_tag = ntohs(vlan_header->h_vlan_TCI); 892cdffcf1bSJim Baxter pkt_len -= VLAN_HLEN; 893cdffcf1bSJim Baxter 894cdffcf1bSJim Baxter vlan_packet_rcvd = true; 895cdffcf1bSJim Baxter } 896cdffcf1bSJim Baxter 897793fc096SFrank Li /* This does 16 byte alignment, exactly what we need. 898793fc096SFrank Li * The packet length includes FCS, but we don't want to 899793fc096SFrank Li * include that when passing upstream as it messes up 900793fc096SFrank Li * bridging applications. 901793fc096SFrank Li */ 902793fc096SFrank Li skb = netdev_alloc_skb(ndev, pkt_len - 4 + NET_IP_ALIGN); 903793fc096SFrank Li 904793fc096SFrank Li if (unlikely(!skb)) { 905793fc096SFrank Li ndev->stats.rx_dropped++; 906793fc096SFrank Li } else { 907cdffcf1bSJim Baxter int payload_offset = (2 * ETH_ALEN); 908793fc096SFrank Li skb_reserve(skb, NET_IP_ALIGN); 909793fc096SFrank Li skb_put(skb, pkt_len - 4); /* Make room */ 910cdffcf1bSJim Baxter 911cdffcf1bSJim Baxter /* Extract the frame data without the VLAN header. */ 912cdffcf1bSJim Baxter skb_copy_to_linear_data(skb, data, (2 * ETH_ALEN)); 913cdffcf1bSJim Baxter if (vlan_packet_rcvd) 914cdffcf1bSJim Baxter payload_offset = (2 * ETH_ALEN) + VLAN_HLEN; 915cdffcf1bSJim Baxter skb_copy_to_linear_data_offset(skb, (2 * ETH_ALEN), 916cdffcf1bSJim Baxter data + payload_offset, 917cdffcf1bSJim Baxter pkt_len - 4 - (2 * ETH_ALEN)); 918cdffcf1bSJim Baxter 919793fc096SFrank Li skb->protocol = eth_type_trans(skb, ndev); 920793fc096SFrank Li 921793fc096SFrank Li /* Get receive timestamp from the skb */ 922793fc096SFrank Li if (fep->hwts_rx_en && fep->bufdesc_ex) { 923793fc096SFrank Li struct skb_shared_hwtstamps *shhwtstamps = 924793fc096SFrank Li skb_hwtstamps(skb); 925793fc096SFrank Li unsigned long flags; 926793fc096SFrank Li 927793fc096SFrank Li memset(shhwtstamps, 0, sizeof(*shhwtstamps)); 928793fc096SFrank Li 929793fc096SFrank Li spin_lock_irqsave(&fep->tmreg_lock, flags); 930793fc096SFrank Li shhwtstamps->hwtstamp = ns_to_ktime( 931793fc096SFrank Li timecounter_cyc2time(&fep->tc, ebdp->ts)); 932793fc096SFrank Li spin_unlock_irqrestore(&fep->tmreg_lock, flags); 933793fc096SFrank Li } 934793fc096SFrank Li 9354c09eed9SJim Baxter if (fep->bufdesc_ex && 9364c09eed9SJim Baxter (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { 9374c09eed9SJim Baxter if (!(ebdp->cbd_esc & FLAG_RX_CSUM_ERROR)) { 9384c09eed9SJim Baxter /* don't check it */ 9394c09eed9SJim Baxter skb->ip_summed = CHECKSUM_UNNECESSARY; 9404c09eed9SJim Baxter } else { 9414c09eed9SJim Baxter skb_checksum_none_assert(skb); 9424c09eed9SJim Baxter } 9434c09eed9SJim Baxter } 9444c09eed9SJim Baxter 945cdffcf1bSJim Baxter /* Handle received VLAN packets */ 946cdffcf1bSJim Baxter if (vlan_packet_rcvd) 947cdffcf1bSJim Baxter __vlan_hwaccel_put_tag(skb, 948cdffcf1bSJim Baxter htons(ETH_P_8021Q), 949cdffcf1bSJim Baxter vlan_tag); 950cdffcf1bSJim Baxter 951793fc096SFrank Li if (!skb_defer_rx_timestamp(skb)) 952793fc096SFrank Li napi_gro_receive(&fep->napi, skb); 953793fc096SFrank Li } 954793fc096SFrank Li 955793fc096SFrank Li bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, data, 956793fc096SFrank Li FEC_ENET_TX_FRSIZE, DMA_FROM_DEVICE); 957793fc096SFrank Li rx_processing_done: 958793fc096SFrank Li /* Clear the status flags for this buffer */ 959793fc096SFrank Li status &= ~BD_ENET_RX_STATS; 960793fc096SFrank Li 961793fc096SFrank Li /* Mark the buffer empty */ 962793fc096SFrank Li status |= BD_ENET_RX_EMPTY; 963793fc096SFrank Li bdp->cbd_sc = status; 964793fc096SFrank Li 965793fc096SFrank Li if (fep->bufdesc_ex) { 966793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 967793fc096SFrank Li 968793fc096SFrank Li ebdp->cbd_esc = BD_ENET_RX_INT; 969793fc096SFrank Li ebdp->cbd_prot = 0; 970793fc096SFrank Li ebdp->cbd_bdu = 0; 971793fc096SFrank Li } 972793fc096SFrank Li 973793fc096SFrank Li /* Update BD pointer to next entry */ 974793fc096SFrank Li if (status & BD_ENET_RX_WRAP) 975793fc096SFrank Li bdp = fep->rx_bd_base; 976793fc096SFrank Li else 977793fc096SFrank Li bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex); 978793fc096SFrank Li /* Doing this here will keep the FEC running while we process 979793fc096SFrank Li * incoming frames. On a heavily loaded network, we should be 980793fc096SFrank Li * able to keep up at the expense of system resources. 981793fc096SFrank Li */ 982793fc096SFrank Li writel(0, fep->hwp + FEC_R_DES_ACTIVE); 983793fc096SFrank Li } 984793fc096SFrank Li fep->cur_rx = bdp; 985793fc096SFrank Li 986793fc096SFrank Li return pkt_received; 987793fc096SFrank Li } 988793fc096SFrank Li 989793fc096SFrank Li static irqreturn_t 990793fc096SFrank Li fec_enet_interrupt(int irq, void *dev_id) 991793fc096SFrank Li { 992793fc096SFrank Li struct net_device *ndev = dev_id; 993793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 994793fc096SFrank Li uint int_events; 995793fc096SFrank Li irqreturn_t ret = IRQ_NONE; 996793fc096SFrank Li 997793fc096SFrank Li do { 998793fc096SFrank Li int_events = readl(fep->hwp + FEC_IEVENT); 999793fc096SFrank Li writel(int_events, fep->hwp + FEC_IEVENT); 1000793fc096SFrank Li 1001793fc096SFrank Li if (int_events & (FEC_ENET_RXF | FEC_ENET_TXF)) { 1002793fc096SFrank Li ret = IRQ_HANDLED; 1003793fc096SFrank Li 1004793fc096SFrank Li /* Disable the RX interrupt */ 1005793fc096SFrank Li if (napi_schedule_prep(&fep->napi)) { 1006793fc096SFrank Li writel(FEC_RX_DISABLED_IMASK, 1007793fc096SFrank Li fep->hwp + FEC_IMASK); 1008793fc096SFrank Li __napi_schedule(&fep->napi); 1009793fc096SFrank Li } 1010793fc096SFrank Li } 1011793fc096SFrank Li 1012793fc096SFrank Li if (int_events & FEC_ENET_MII) { 1013793fc096SFrank Li ret = IRQ_HANDLED; 1014793fc096SFrank Li complete(&fep->mdio_done); 1015793fc096SFrank Li } 1016793fc096SFrank Li } while (int_events); 1017793fc096SFrank Li 1018793fc096SFrank Li return ret; 1019793fc096SFrank Li } 1020793fc096SFrank Li 1021793fc096SFrank Li static int fec_enet_rx_napi(struct napi_struct *napi, int budget) 1022793fc096SFrank Li { 1023793fc096SFrank Li struct net_device *ndev = napi->dev; 1024793fc096SFrank Li int pkts = fec_enet_rx(ndev, budget); 1025793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1026793fc096SFrank Li 1027793fc096SFrank Li fec_enet_tx(ndev); 1028793fc096SFrank Li 1029793fc096SFrank Li if (pkts < budget) { 1030793fc096SFrank Li napi_complete(napi); 1031793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 1032793fc096SFrank Li } 1033793fc096SFrank Li return pkts; 1034793fc096SFrank Li } 1035793fc096SFrank Li 1036793fc096SFrank Li /* ------------------------------------------------------------------------- */ 1037793fc096SFrank Li static void fec_get_mac(struct net_device *ndev) 1038793fc096SFrank Li { 1039793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1040793fc096SFrank Li struct fec_platform_data *pdata = fep->pdev->dev.platform_data; 1041793fc096SFrank Li unsigned char *iap, tmpaddr[ETH_ALEN]; 1042793fc096SFrank Li 1043793fc096SFrank Li /* 1044793fc096SFrank Li * try to get mac address in following order: 1045793fc096SFrank Li * 1046793fc096SFrank Li * 1) module parameter via kernel command line in form 1047793fc096SFrank Li * fec.macaddr=0x00,0x04,0x9f,0x01,0x30,0xe0 1048793fc096SFrank Li */ 1049793fc096SFrank Li iap = macaddr; 1050793fc096SFrank Li 1051793fc096SFrank Li /* 1052793fc096SFrank Li * 2) from device tree data 1053793fc096SFrank Li */ 1054793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 1055793fc096SFrank Li struct device_node *np = fep->pdev->dev.of_node; 1056793fc096SFrank Li if (np) { 1057793fc096SFrank Li const char *mac = of_get_mac_address(np); 1058793fc096SFrank Li if (mac) 1059793fc096SFrank Li iap = (unsigned char *) mac; 1060793fc096SFrank Li } 1061793fc096SFrank Li } 1062793fc096SFrank Li 1063793fc096SFrank Li /* 1064793fc096SFrank Li * 3) from flash or fuse (via platform data) 1065793fc096SFrank Li */ 1066793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 1067793fc096SFrank Li #ifdef CONFIG_M5272 1068793fc096SFrank Li if (FEC_FLASHMAC) 1069793fc096SFrank Li iap = (unsigned char *)FEC_FLASHMAC; 1070793fc096SFrank Li #else 1071793fc096SFrank Li if (pdata) 1072793fc096SFrank Li iap = (unsigned char *)&pdata->mac; 1073793fc096SFrank Li #endif 1074793fc096SFrank Li } 1075793fc096SFrank Li 1076793fc096SFrank Li /* 1077793fc096SFrank Li * 4) FEC mac registers set by bootloader 1078793fc096SFrank Li */ 1079793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 1080793fc096SFrank Li *((unsigned long *) &tmpaddr[0]) = 1081793fc096SFrank Li be32_to_cpu(readl(fep->hwp + FEC_ADDR_LOW)); 1082793fc096SFrank Li *((unsigned short *) &tmpaddr[4]) = 1083793fc096SFrank Li be16_to_cpu(readl(fep->hwp + FEC_ADDR_HIGH) >> 16); 1084793fc096SFrank Li iap = &tmpaddr[0]; 1085793fc096SFrank Li } 1086793fc096SFrank Li 1087ff5b2fabSLucas Stach /* 1088ff5b2fabSLucas Stach * 5) random mac address 1089ff5b2fabSLucas Stach */ 1090ff5b2fabSLucas Stach if (!is_valid_ether_addr(iap)) { 1091ff5b2fabSLucas Stach /* Report it and use a random ethernet address instead */ 1092ff5b2fabSLucas Stach netdev_err(ndev, "Invalid MAC address: %pM\n", iap); 1093ff5b2fabSLucas Stach eth_hw_addr_random(ndev); 1094ff5b2fabSLucas Stach netdev_info(ndev, "Using random MAC address: %pM\n", 1095ff5b2fabSLucas Stach ndev->dev_addr); 1096ff5b2fabSLucas Stach return; 1097ff5b2fabSLucas Stach } 1098ff5b2fabSLucas Stach 1099793fc096SFrank Li memcpy(ndev->dev_addr, iap, ETH_ALEN); 1100793fc096SFrank Li 1101793fc096SFrank Li /* Adjust MAC if using macaddr */ 1102793fc096SFrank Li if (iap == macaddr) 1103793fc096SFrank Li ndev->dev_addr[ETH_ALEN-1] = macaddr[ETH_ALEN-1] + fep->dev_id; 1104793fc096SFrank Li } 1105793fc096SFrank Li 1106793fc096SFrank Li /* ------------------------------------------------------------------------- */ 1107793fc096SFrank Li 1108793fc096SFrank Li /* 1109793fc096SFrank Li * Phy section 1110793fc096SFrank Li */ 1111793fc096SFrank Li static void fec_enet_adjust_link(struct net_device *ndev) 1112793fc096SFrank Li { 1113793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1114793fc096SFrank Li struct phy_device *phy_dev = fep->phy_dev; 1115793fc096SFrank Li int status_change = 0; 1116793fc096SFrank Li 1117793fc096SFrank Li /* Prevent a state halted on mii error */ 1118793fc096SFrank Li if (fep->mii_timeout && phy_dev->state == PHY_HALTED) { 1119793fc096SFrank Li phy_dev->state = PHY_RESUMING; 112054309fa6SFrank Li return; 1121793fc096SFrank Li } 1122793fc096SFrank Li 1123793fc096SFrank Li if (phy_dev->link) { 1124793fc096SFrank Li if (!fep->link) { 1125793fc096SFrank Li fep->link = phy_dev->link; 1126793fc096SFrank Li status_change = 1; 1127793fc096SFrank Li } 1128793fc096SFrank Li 1129793fc096SFrank Li if (fep->full_duplex != phy_dev->duplex) 1130793fc096SFrank Li status_change = 1; 1131793fc096SFrank Li 1132793fc096SFrank Li if (phy_dev->speed != fep->speed) { 1133793fc096SFrank Li fep->speed = phy_dev->speed; 1134793fc096SFrank Li status_change = 1; 1135793fc096SFrank Li } 1136793fc096SFrank Li 1137793fc096SFrank Li /* if any of the above changed restart the FEC */ 1138793fc096SFrank Li if (status_change) 1139793fc096SFrank Li fec_restart(ndev, phy_dev->duplex); 1140793fc096SFrank Li } else { 1141793fc096SFrank Li if (fep->link) { 1142793fc096SFrank Li fec_stop(ndev); 11436e0895c2SDavid S. Miller fep->link = phy_dev->link; 1144793fc096SFrank Li status_change = 1; 1145793fc096SFrank Li } 1146793fc096SFrank Li } 1147793fc096SFrank Li 1148793fc096SFrank Li if (status_change) 1149793fc096SFrank Li phy_print_status(phy_dev); 1150793fc096SFrank Li } 1151793fc096SFrank Li 1152793fc096SFrank Li static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum) 1153793fc096SFrank Li { 1154793fc096SFrank Li struct fec_enet_private *fep = bus->priv; 1155793fc096SFrank Li unsigned long time_left; 1156793fc096SFrank Li 1157793fc096SFrank Li fep->mii_timeout = 0; 1158793fc096SFrank Li init_completion(&fep->mdio_done); 1159793fc096SFrank Li 1160793fc096SFrank Li /* start a read op */ 1161793fc096SFrank Li writel(FEC_MMFR_ST | FEC_MMFR_OP_READ | 1162793fc096SFrank Li FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) | 1163793fc096SFrank Li FEC_MMFR_TA, fep->hwp + FEC_MII_DATA); 1164793fc096SFrank Li 1165793fc096SFrank Li /* wait for end of transfer */ 1166793fc096SFrank Li time_left = wait_for_completion_timeout(&fep->mdio_done, 1167793fc096SFrank Li usecs_to_jiffies(FEC_MII_TIMEOUT)); 1168793fc096SFrank Li if (time_left == 0) { 1169793fc096SFrank Li fep->mii_timeout = 1; 117031b7720cSJoe Perches netdev_err(fep->netdev, "MDIO read timeout\n"); 1171793fc096SFrank Li return -ETIMEDOUT; 1172793fc096SFrank Li } 1173793fc096SFrank Li 1174793fc096SFrank Li /* return value */ 1175793fc096SFrank Li return FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA)); 1176793fc096SFrank Li } 1177793fc096SFrank Li 1178793fc096SFrank Li static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum, 1179793fc096SFrank Li u16 value) 1180793fc096SFrank Li { 1181793fc096SFrank Li struct fec_enet_private *fep = bus->priv; 1182793fc096SFrank Li unsigned long time_left; 1183793fc096SFrank Li 1184793fc096SFrank Li fep->mii_timeout = 0; 1185793fc096SFrank Li init_completion(&fep->mdio_done); 1186793fc096SFrank Li 1187793fc096SFrank Li /* start a write op */ 1188793fc096SFrank Li writel(FEC_MMFR_ST | FEC_MMFR_OP_WRITE | 1189793fc096SFrank Li FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) | 1190793fc096SFrank Li FEC_MMFR_TA | FEC_MMFR_DATA(value), 1191793fc096SFrank Li fep->hwp + FEC_MII_DATA); 1192793fc096SFrank Li 1193793fc096SFrank Li /* wait for end of transfer */ 1194793fc096SFrank Li time_left = wait_for_completion_timeout(&fep->mdio_done, 1195793fc096SFrank Li usecs_to_jiffies(FEC_MII_TIMEOUT)); 1196793fc096SFrank Li if (time_left == 0) { 1197793fc096SFrank Li fep->mii_timeout = 1; 119831b7720cSJoe Perches netdev_err(fep->netdev, "MDIO write timeout\n"); 1199793fc096SFrank Li return -ETIMEDOUT; 1200793fc096SFrank Li } 1201793fc096SFrank Li 1202793fc096SFrank Li return 0; 1203793fc096SFrank Li } 1204793fc096SFrank Li 1205793fc096SFrank Li static int fec_enet_mdio_reset(struct mii_bus *bus) 1206793fc096SFrank Li { 1207793fc096SFrank Li return 0; 1208793fc096SFrank Li } 1209793fc096SFrank Li 1210793fc096SFrank Li static int fec_enet_mii_probe(struct net_device *ndev) 1211793fc096SFrank Li { 1212793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1213793fc096SFrank Li const struct platform_device_id *id_entry = 1214793fc096SFrank Li platform_get_device_id(fep->pdev); 1215793fc096SFrank Li struct phy_device *phy_dev = NULL; 1216793fc096SFrank Li char mdio_bus_id[MII_BUS_ID_SIZE]; 1217793fc096SFrank Li char phy_name[MII_BUS_ID_SIZE + 3]; 1218793fc096SFrank Li int phy_id; 1219793fc096SFrank Li int dev_id = fep->dev_id; 1220793fc096SFrank Li 1221793fc096SFrank Li fep->phy_dev = NULL; 1222793fc096SFrank Li 1223793fc096SFrank Li /* check for attached phy */ 1224793fc096SFrank Li for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) { 1225793fc096SFrank Li if ((fep->mii_bus->phy_mask & (1 << phy_id))) 1226793fc096SFrank Li continue; 1227793fc096SFrank Li if (fep->mii_bus->phy_map[phy_id] == NULL) 1228793fc096SFrank Li continue; 1229793fc096SFrank Li if (fep->mii_bus->phy_map[phy_id]->phy_id == 0) 1230793fc096SFrank Li continue; 1231793fc096SFrank Li if (dev_id--) 1232793fc096SFrank Li continue; 1233793fc096SFrank Li strncpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE); 1234793fc096SFrank Li break; 1235793fc096SFrank Li } 1236793fc096SFrank Li 1237793fc096SFrank Li if (phy_id >= PHY_MAX_ADDR) { 123831b7720cSJoe Perches netdev_info(ndev, "no PHY, assuming direct connection to switch\n"); 1239793fc096SFrank Li strncpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE); 1240793fc096SFrank Li phy_id = 0; 1241793fc096SFrank Li } 1242793fc096SFrank Li 1243793fc096SFrank Li snprintf(phy_name, sizeof(phy_name), PHY_ID_FMT, mdio_bus_id, phy_id); 1244793fc096SFrank Li phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, 1245793fc096SFrank Li fep->phy_interface); 1246793fc096SFrank Li if (IS_ERR(phy_dev)) { 124731b7720cSJoe Perches netdev_err(ndev, "could not attach to PHY\n"); 1248793fc096SFrank Li return PTR_ERR(phy_dev); 1249793fc096SFrank Li } 1250793fc096SFrank Li 1251793fc096SFrank Li /* mask with MAC supported features */ 1252793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_HAS_GBIT) { 1253793fc096SFrank Li phy_dev->supported &= PHY_GBIT_FEATURES; 1254d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 1255793fc096SFrank Li phy_dev->supported |= SUPPORTED_Pause; 1256d1391930SGuenter Roeck #endif 1257793fc096SFrank Li } 1258793fc096SFrank Li else 1259793fc096SFrank Li phy_dev->supported &= PHY_BASIC_FEATURES; 1260793fc096SFrank Li 1261793fc096SFrank Li phy_dev->advertising = phy_dev->supported; 1262793fc096SFrank Li 1263793fc096SFrank Li fep->phy_dev = phy_dev; 1264793fc096SFrank Li fep->link = 0; 1265793fc096SFrank Li fep->full_duplex = 0; 1266793fc096SFrank Li 126731b7720cSJoe Perches netdev_info(ndev, "Freescale FEC PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n", 1268793fc096SFrank Li fep->phy_dev->drv->name, dev_name(&fep->phy_dev->dev), 1269793fc096SFrank Li fep->phy_dev->irq); 1270793fc096SFrank Li 1271793fc096SFrank Li return 0; 1272793fc096SFrank Li } 1273793fc096SFrank Li 1274793fc096SFrank Li static int fec_enet_mii_init(struct platform_device *pdev) 1275793fc096SFrank Li { 1276793fc096SFrank Li static struct mii_bus *fec0_mii_bus; 1277793fc096SFrank Li struct net_device *ndev = platform_get_drvdata(pdev); 1278793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1279793fc096SFrank Li const struct platform_device_id *id_entry = 1280793fc096SFrank Li platform_get_device_id(fep->pdev); 1281793fc096SFrank Li int err = -ENXIO, i; 1282793fc096SFrank Li 1283793fc096SFrank Li /* 1284793fc096SFrank Li * The dual fec interfaces are not equivalent with enet-mac. 1285793fc096SFrank Li * Here are the differences: 1286793fc096SFrank Li * 1287793fc096SFrank Li * - fec0 supports MII & RMII modes while fec1 only supports RMII 1288793fc096SFrank Li * - fec0 acts as the 1588 time master while fec1 is slave 1289793fc096SFrank Li * - external phys can only be configured by fec0 1290793fc096SFrank Li * 1291793fc096SFrank Li * That is to say fec1 can not work independently. It only works 1292793fc096SFrank Li * when fec0 is working. The reason behind this design is that the 1293793fc096SFrank Li * second interface is added primarily for Switch mode. 1294793fc096SFrank Li * 1295793fc096SFrank Li * Because of the last point above, both phys are attached on fec0 1296793fc096SFrank Li * mdio interface in board design, and need to be configured by 1297793fc096SFrank Li * fec0 mii_bus. 1298793fc096SFrank Li */ 1299793fc096SFrank Li if ((id_entry->driver_data & FEC_QUIRK_ENET_MAC) && fep->dev_id > 0) { 1300793fc096SFrank Li /* fec1 uses fec0 mii_bus */ 1301793fc096SFrank Li if (mii_cnt && fec0_mii_bus) { 1302793fc096SFrank Li fep->mii_bus = fec0_mii_bus; 1303793fc096SFrank Li mii_cnt++; 1304793fc096SFrank Li return 0; 1305793fc096SFrank Li } 1306793fc096SFrank Li return -ENOENT; 1307793fc096SFrank Li } 1308793fc096SFrank Li 1309793fc096SFrank Li fep->mii_timeout = 0; 1310793fc096SFrank Li 1311793fc096SFrank Li /* 1312793fc096SFrank Li * Set MII speed to 2.5 MHz (= clk_get_rate() / 2 * phy_speed) 1313793fc096SFrank Li * 1314793fc096SFrank Li * The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while 1315793fc096SFrank Li * for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'. The i.MX28 1316793fc096SFrank Li * Reference Manual has an error on this, and gets fixed on i.MX6Q 1317793fc096SFrank Li * document. 1318793fc096SFrank Li */ 1319793fc096SFrank Li fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ahb), 5000000); 1320793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) 1321793fc096SFrank Li fep->phy_speed--; 1322793fc096SFrank Li fep->phy_speed <<= 1; 1323793fc096SFrank Li writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 1324793fc096SFrank Li 1325793fc096SFrank Li fep->mii_bus = mdiobus_alloc(); 1326793fc096SFrank Li if (fep->mii_bus == NULL) { 1327793fc096SFrank Li err = -ENOMEM; 1328793fc096SFrank Li goto err_out; 1329793fc096SFrank Li } 1330793fc096SFrank Li 1331793fc096SFrank Li fep->mii_bus->name = "fec_enet_mii_bus"; 1332793fc096SFrank Li fep->mii_bus->read = fec_enet_mdio_read; 1333793fc096SFrank Li fep->mii_bus->write = fec_enet_mdio_write; 1334793fc096SFrank Li fep->mii_bus->reset = fec_enet_mdio_reset; 1335793fc096SFrank Li snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", 1336793fc096SFrank Li pdev->name, fep->dev_id + 1); 1337793fc096SFrank Li fep->mii_bus->priv = fep; 1338793fc096SFrank Li fep->mii_bus->parent = &pdev->dev; 1339793fc096SFrank Li 1340793fc096SFrank Li fep->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); 1341793fc096SFrank Li if (!fep->mii_bus->irq) { 1342793fc096SFrank Li err = -ENOMEM; 1343793fc096SFrank Li goto err_out_free_mdiobus; 1344793fc096SFrank Li } 1345793fc096SFrank Li 1346793fc096SFrank Li for (i = 0; i < PHY_MAX_ADDR; i++) 1347793fc096SFrank Li fep->mii_bus->irq[i] = PHY_POLL; 1348793fc096SFrank Li 1349793fc096SFrank Li if (mdiobus_register(fep->mii_bus)) 1350793fc096SFrank Li goto err_out_free_mdio_irq; 1351793fc096SFrank Li 1352793fc096SFrank Li mii_cnt++; 1353793fc096SFrank Li 1354793fc096SFrank Li /* save fec0 mii_bus */ 1355793fc096SFrank Li if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) 1356793fc096SFrank Li fec0_mii_bus = fep->mii_bus; 1357793fc096SFrank Li 1358793fc096SFrank Li return 0; 1359793fc096SFrank Li 1360793fc096SFrank Li err_out_free_mdio_irq: 1361793fc096SFrank Li kfree(fep->mii_bus->irq); 1362793fc096SFrank Li err_out_free_mdiobus: 1363793fc096SFrank Li mdiobus_free(fep->mii_bus); 1364793fc096SFrank Li err_out: 1365793fc096SFrank Li return err; 1366793fc096SFrank Li } 1367793fc096SFrank Li 1368793fc096SFrank Li static void fec_enet_mii_remove(struct fec_enet_private *fep) 1369793fc096SFrank Li { 1370793fc096SFrank Li if (--mii_cnt == 0) { 1371793fc096SFrank Li mdiobus_unregister(fep->mii_bus); 1372793fc096SFrank Li kfree(fep->mii_bus->irq); 1373793fc096SFrank Li mdiobus_free(fep->mii_bus); 1374793fc096SFrank Li } 1375793fc096SFrank Li } 1376793fc096SFrank Li 1377793fc096SFrank Li static int fec_enet_get_settings(struct net_device *ndev, 1378793fc096SFrank Li struct ethtool_cmd *cmd) 1379793fc096SFrank Li { 1380793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1381793fc096SFrank Li struct phy_device *phydev = fep->phy_dev; 1382793fc096SFrank Li 1383793fc096SFrank Li if (!phydev) 1384793fc096SFrank Li return -ENODEV; 1385793fc096SFrank Li 1386793fc096SFrank Li return phy_ethtool_gset(phydev, cmd); 1387793fc096SFrank Li } 1388793fc096SFrank Li 1389793fc096SFrank Li static int fec_enet_set_settings(struct net_device *ndev, 1390793fc096SFrank Li struct ethtool_cmd *cmd) 1391793fc096SFrank Li { 1392793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1393793fc096SFrank Li struct phy_device *phydev = fep->phy_dev; 1394793fc096SFrank Li 1395793fc096SFrank Li if (!phydev) 1396793fc096SFrank Li return -ENODEV; 1397793fc096SFrank Li 1398793fc096SFrank Li return phy_ethtool_sset(phydev, cmd); 1399793fc096SFrank Li } 1400793fc096SFrank Li 1401793fc096SFrank Li static void fec_enet_get_drvinfo(struct net_device *ndev, 1402793fc096SFrank Li struct ethtool_drvinfo *info) 1403793fc096SFrank Li { 1404793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1405793fc096SFrank Li 1406793fc096SFrank Li strlcpy(info->driver, fep->pdev->dev.driver->name, 1407793fc096SFrank Li sizeof(info->driver)); 1408793fc096SFrank Li strlcpy(info->version, "Revision: 1.0", sizeof(info->version)); 1409793fc096SFrank Li strlcpy(info->bus_info, dev_name(&ndev->dev), sizeof(info->bus_info)); 1410793fc096SFrank Li } 1411793fc096SFrank Li 1412793fc096SFrank Li static int fec_enet_get_ts_info(struct net_device *ndev, 1413793fc096SFrank Li struct ethtool_ts_info *info) 1414793fc096SFrank Li { 1415793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1416793fc096SFrank Li 1417793fc096SFrank Li if (fep->bufdesc_ex) { 1418793fc096SFrank Li 1419793fc096SFrank Li info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | 1420793fc096SFrank Li SOF_TIMESTAMPING_RX_SOFTWARE | 1421793fc096SFrank Li SOF_TIMESTAMPING_SOFTWARE | 1422793fc096SFrank Li SOF_TIMESTAMPING_TX_HARDWARE | 1423793fc096SFrank Li SOF_TIMESTAMPING_RX_HARDWARE | 1424793fc096SFrank Li SOF_TIMESTAMPING_RAW_HARDWARE; 1425793fc096SFrank Li if (fep->ptp_clock) 1426793fc096SFrank Li info->phc_index = ptp_clock_index(fep->ptp_clock); 1427793fc096SFrank Li else 1428793fc096SFrank Li info->phc_index = -1; 1429793fc096SFrank Li 1430793fc096SFrank Li info->tx_types = (1 << HWTSTAMP_TX_OFF) | 1431793fc096SFrank Li (1 << HWTSTAMP_TX_ON); 1432793fc096SFrank Li 1433793fc096SFrank Li info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | 1434793fc096SFrank Li (1 << HWTSTAMP_FILTER_ALL); 1435793fc096SFrank Li return 0; 1436793fc096SFrank Li } else { 1437793fc096SFrank Li return ethtool_op_get_ts_info(ndev, info); 1438793fc096SFrank Li } 1439793fc096SFrank Li } 1440793fc096SFrank Li 1441d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 1442d1391930SGuenter Roeck 1443793fc096SFrank Li static void fec_enet_get_pauseparam(struct net_device *ndev, 1444793fc096SFrank Li struct ethtool_pauseparam *pause) 1445793fc096SFrank Li { 1446793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1447793fc096SFrank Li 1448793fc096SFrank Li pause->autoneg = (fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) != 0; 1449793fc096SFrank Li pause->tx_pause = (fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) != 0; 1450793fc096SFrank Li pause->rx_pause = pause->tx_pause; 1451793fc096SFrank Li } 1452793fc096SFrank Li 1453793fc096SFrank Li static int fec_enet_set_pauseparam(struct net_device *ndev, 1454793fc096SFrank Li struct ethtool_pauseparam *pause) 1455793fc096SFrank Li { 1456793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1457793fc096SFrank Li 1458793fc096SFrank Li if (pause->tx_pause != pause->rx_pause) { 1459793fc096SFrank Li netdev_info(ndev, 1460793fc096SFrank Li "hardware only support enable/disable both tx and rx"); 1461793fc096SFrank Li return -EINVAL; 1462793fc096SFrank Li } 1463793fc096SFrank Li 1464793fc096SFrank Li fep->pause_flag = 0; 1465793fc096SFrank Li 1466793fc096SFrank Li /* tx pause must be same as rx pause */ 1467793fc096SFrank Li fep->pause_flag |= pause->rx_pause ? FEC_PAUSE_FLAG_ENABLE : 0; 1468793fc096SFrank Li fep->pause_flag |= pause->autoneg ? FEC_PAUSE_FLAG_AUTONEG : 0; 1469793fc096SFrank Li 1470793fc096SFrank Li if (pause->rx_pause || pause->autoneg) { 1471793fc096SFrank Li fep->phy_dev->supported |= ADVERTISED_Pause; 1472793fc096SFrank Li fep->phy_dev->advertising |= ADVERTISED_Pause; 1473793fc096SFrank Li } else { 1474793fc096SFrank Li fep->phy_dev->supported &= ~ADVERTISED_Pause; 1475793fc096SFrank Li fep->phy_dev->advertising &= ~ADVERTISED_Pause; 1476793fc096SFrank Li } 1477793fc096SFrank Li 1478793fc096SFrank Li if (pause->autoneg) { 1479793fc096SFrank Li if (netif_running(ndev)) 1480793fc096SFrank Li fec_stop(ndev); 1481793fc096SFrank Li phy_start_aneg(fep->phy_dev); 1482793fc096SFrank Li } 1483793fc096SFrank Li if (netif_running(ndev)) 1484793fc096SFrank Li fec_restart(ndev, 0); 1485793fc096SFrank Li 1486793fc096SFrank Li return 0; 1487793fc096SFrank Li } 1488793fc096SFrank Li 148938ae92dcSChris Healy static const struct fec_stat { 149038ae92dcSChris Healy char name[ETH_GSTRING_LEN]; 149138ae92dcSChris Healy u16 offset; 149238ae92dcSChris Healy } fec_stats[] = { 149338ae92dcSChris Healy /* RMON TX */ 149438ae92dcSChris Healy { "tx_dropped", RMON_T_DROP }, 149538ae92dcSChris Healy { "tx_packets", RMON_T_PACKETS }, 149638ae92dcSChris Healy { "tx_broadcast", RMON_T_BC_PKT }, 149738ae92dcSChris Healy { "tx_multicast", RMON_T_MC_PKT }, 149838ae92dcSChris Healy { "tx_crc_errors", RMON_T_CRC_ALIGN }, 149938ae92dcSChris Healy { "tx_undersize", RMON_T_UNDERSIZE }, 150038ae92dcSChris Healy { "tx_oversize", RMON_T_OVERSIZE }, 150138ae92dcSChris Healy { "tx_fragment", RMON_T_FRAG }, 150238ae92dcSChris Healy { "tx_jabber", RMON_T_JAB }, 150338ae92dcSChris Healy { "tx_collision", RMON_T_COL }, 150438ae92dcSChris Healy { "tx_64byte", RMON_T_P64 }, 150538ae92dcSChris Healy { "tx_65to127byte", RMON_T_P65TO127 }, 150638ae92dcSChris Healy { "tx_128to255byte", RMON_T_P128TO255 }, 150738ae92dcSChris Healy { "tx_256to511byte", RMON_T_P256TO511 }, 150838ae92dcSChris Healy { "tx_512to1023byte", RMON_T_P512TO1023 }, 150938ae92dcSChris Healy { "tx_1024to2047byte", RMON_T_P1024TO2047 }, 151038ae92dcSChris Healy { "tx_GTE2048byte", RMON_T_P_GTE2048 }, 151138ae92dcSChris Healy { "tx_octets", RMON_T_OCTETS }, 151238ae92dcSChris Healy 151338ae92dcSChris Healy /* IEEE TX */ 151438ae92dcSChris Healy { "IEEE_tx_drop", IEEE_T_DROP }, 151538ae92dcSChris Healy { "IEEE_tx_frame_ok", IEEE_T_FRAME_OK }, 151638ae92dcSChris Healy { "IEEE_tx_1col", IEEE_T_1COL }, 151738ae92dcSChris Healy { "IEEE_tx_mcol", IEEE_T_MCOL }, 151838ae92dcSChris Healy { "IEEE_tx_def", IEEE_T_DEF }, 151938ae92dcSChris Healy { "IEEE_tx_lcol", IEEE_T_LCOL }, 152038ae92dcSChris Healy { "IEEE_tx_excol", IEEE_T_EXCOL }, 152138ae92dcSChris Healy { "IEEE_tx_macerr", IEEE_T_MACERR }, 152238ae92dcSChris Healy { "IEEE_tx_cserr", IEEE_T_CSERR }, 152338ae92dcSChris Healy { "IEEE_tx_sqe", IEEE_T_SQE }, 152438ae92dcSChris Healy { "IEEE_tx_fdxfc", IEEE_T_FDXFC }, 152538ae92dcSChris Healy { "IEEE_tx_octets_ok", IEEE_T_OCTETS_OK }, 152638ae92dcSChris Healy 152738ae92dcSChris Healy /* RMON RX */ 152838ae92dcSChris Healy { "rx_packets", RMON_R_PACKETS }, 152938ae92dcSChris Healy { "rx_broadcast", RMON_R_BC_PKT }, 153038ae92dcSChris Healy { "rx_multicast", RMON_R_MC_PKT }, 153138ae92dcSChris Healy { "rx_crc_errors", RMON_R_CRC_ALIGN }, 153238ae92dcSChris Healy { "rx_undersize", RMON_R_UNDERSIZE }, 153338ae92dcSChris Healy { "rx_oversize", RMON_R_OVERSIZE }, 153438ae92dcSChris Healy { "rx_fragment", RMON_R_FRAG }, 153538ae92dcSChris Healy { "rx_jabber", RMON_R_JAB }, 153638ae92dcSChris Healy { "rx_64byte", RMON_R_P64 }, 153738ae92dcSChris Healy { "rx_65to127byte", RMON_R_P65TO127 }, 153838ae92dcSChris Healy { "rx_128to255byte", RMON_R_P128TO255 }, 153938ae92dcSChris Healy { "rx_256to511byte", RMON_R_P256TO511 }, 154038ae92dcSChris Healy { "rx_512to1023byte", RMON_R_P512TO1023 }, 154138ae92dcSChris Healy { "rx_1024to2047byte", RMON_R_P1024TO2047 }, 154238ae92dcSChris Healy { "rx_GTE2048byte", RMON_R_P_GTE2048 }, 154338ae92dcSChris Healy { "rx_octets", RMON_R_OCTETS }, 154438ae92dcSChris Healy 154538ae92dcSChris Healy /* IEEE RX */ 154638ae92dcSChris Healy { "IEEE_rx_drop", IEEE_R_DROP }, 154738ae92dcSChris Healy { "IEEE_rx_frame_ok", IEEE_R_FRAME_OK }, 154838ae92dcSChris Healy { "IEEE_rx_crc", IEEE_R_CRC }, 154938ae92dcSChris Healy { "IEEE_rx_align", IEEE_R_ALIGN }, 155038ae92dcSChris Healy { "IEEE_rx_macerr", IEEE_R_MACERR }, 155138ae92dcSChris Healy { "IEEE_rx_fdxfc", IEEE_R_FDXFC }, 155238ae92dcSChris Healy { "IEEE_rx_octets_ok", IEEE_R_OCTETS_OK }, 155338ae92dcSChris Healy }; 155438ae92dcSChris Healy 155538ae92dcSChris Healy static void fec_enet_get_ethtool_stats(struct net_device *dev, 155638ae92dcSChris Healy struct ethtool_stats *stats, u64 *data) 155738ae92dcSChris Healy { 155838ae92dcSChris Healy struct fec_enet_private *fep = netdev_priv(dev); 155938ae92dcSChris Healy int i; 156038ae92dcSChris Healy 156138ae92dcSChris Healy for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 156238ae92dcSChris Healy data[i] = readl(fep->hwp + fec_stats[i].offset); 156338ae92dcSChris Healy } 156438ae92dcSChris Healy 156538ae92dcSChris Healy static void fec_enet_get_strings(struct net_device *netdev, 156638ae92dcSChris Healy u32 stringset, u8 *data) 156738ae92dcSChris Healy { 156838ae92dcSChris Healy int i; 156938ae92dcSChris Healy switch (stringset) { 157038ae92dcSChris Healy case ETH_SS_STATS: 157138ae92dcSChris Healy for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 157238ae92dcSChris Healy memcpy(data + i * ETH_GSTRING_LEN, 157338ae92dcSChris Healy fec_stats[i].name, ETH_GSTRING_LEN); 157438ae92dcSChris Healy break; 157538ae92dcSChris Healy } 157638ae92dcSChris Healy } 157738ae92dcSChris Healy 157838ae92dcSChris Healy static int fec_enet_get_sset_count(struct net_device *dev, int sset) 157938ae92dcSChris Healy { 158038ae92dcSChris Healy switch (sset) { 158138ae92dcSChris Healy case ETH_SS_STATS: 158238ae92dcSChris Healy return ARRAY_SIZE(fec_stats); 158338ae92dcSChris Healy default: 158438ae92dcSChris Healy return -EOPNOTSUPP; 158538ae92dcSChris Healy } 158638ae92dcSChris Healy } 1587d1391930SGuenter Roeck #endif /* !defined(CONFIG_M5272) */ 158838ae92dcSChris Healy 158932bc9b46SChris Healy static int fec_enet_nway_reset(struct net_device *dev) 159032bc9b46SChris Healy { 159132bc9b46SChris Healy struct fec_enet_private *fep = netdev_priv(dev); 159232bc9b46SChris Healy struct phy_device *phydev = fep->phy_dev; 159332bc9b46SChris Healy 159432bc9b46SChris Healy if (!phydev) 159532bc9b46SChris Healy return -ENODEV; 159632bc9b46SChris Healy 159732bc9b46SChris Healy return genphy_restart_aneg(phydev); 159832bc9b46SChris Healy } 159932bc9b46SChris Healy 1600793fc096SFrank Li static const struct ethtool_ops fec_enet_ethtool_ops = { 1601d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 1602793fc096SFrank Li .get_pauseparam = fec_enet_get_pauseparam, 1603793fc096SFrank Li .set_pauseparam = fec_enet_set_pauseparam, 1604d1391930SGuenter Roeck #endif 1605793fc096SFrank Li .get_settings = fec_enet_get_settings, 1606793fc096SFrank Li .set_settings = fec_enet_set_settings, 1607793fc096SFrank Li .get_drvinfo = fec_enet_get_drvinfo, 1608793fc096SFrank Li .get_link = ethtool_op_get_link, 1609793fc096SFrank Li .get_ts_info = fec_enet_get_ts_info, 161032bc9b46SChris Healy .nway_reset = fec_enet_nway_reset, 161138ae92dcSChris Healy #ifndef CONFIG_M5272 161238ae92dcSChris Healy .get_ethtool_stats = fec_enet_get_ethtool_stats, 161338ae92dcSChris Healy .get_strings = fec_enet_get_strings, 161438ae92dcSChris Healy .get_sset_count = fec_enet_get_sset_count, 161538ae92dcSChris Healy #endif 1616793fc096SFrank Li }; 1617793fc096SFrank Li 1618793fc096SFrank Li static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) 1619793fc096SFrank Li { 1620793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1621793fc096SFrank Li struct phy_device *phydev = fep->phy_dev; 1622793fc096SFrank Li 1623793fc096SFrank Li if (!netif_running(ndev)) 1624793fc096SFrank Li return -EINVAL; 1625793fc096SFrank Li 1626793fc096SFrank Li if (!phydev) 1627793fc096SFrank Li return -ENODEV; 1628793fc096SFrank Li 1629793fc096SFrank Li if (cmd == SIOCSHWTSTAMP && fep->bufdesc_ex) 1630793fc096SFrank Li return fec_ptp_ioctl(ndev, rq, cmd); 1631793fc096SFrank Li 1632793fc096SFrank Li return phy_mii_ioctl(phydev, rq, cmd); 1633793fc096SFrank Li } 1634793fc096SFrank Li 1635793fc096SFrank Li static void fec_enet_free_buffers(struct net_device *ndev) 1636793fc096SFrank Li { 1637793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1638793fc096SFrank Li unsigned int i; 1639793fc096SFrank Li struct sk_buff *skb; 1640793fc096SFrank Li struct bufdesc *bdp; 1641793fc096SFrank Li 1642793fc096SFrank Li bdp = fep->rx_bd_base; 1643793fc096SFrank Li for (i = 0; i < RX_RING_SIZE; i++) { 1644793fc096SFrank Li skb = fep->rx_skbuff[i]; 1645793fc096SFrank Li 1646793fc096SFrank Li if (bdp->cbd_bufaddr) 1647793fc096SFrank Li dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, 1648793fc096SFrank Li FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE); 1649793fc096SFrank Li if (skb) 1650793fc096SFrank Li dev_kfree_skb(skb); 1651793fc096SFrank Li bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex); 1652793fc096SFrank Li } 1653793fc096SFrank Li 1654793fc096SFrank Li bdp = fep->tx_bd_base; 1655793fc096SFrank Li for (i = 0; i < TX_RING_SIZE; i++) 1656793fc096SFrank Li kfree(fep->tx_bounce[i]); 1657793fc096SFrank Li } 1658793fc096SFrank Li 1659793fc096SFrank Li static int fec_enet_alloc_buffers(struct net_device *ndev) 1660793fc096SFrank Li { 1661793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1662793fc096SFrank Li unsigned int i; 1663793fc096SFrank Li struct sk_buff *skb; 1664793fc096SFrank Li struct bufdesc *bdp; 1665793fc096SFrank Li 1666793fc096SFrank Li bdp = fep->rx_bd_base; 1667793fc096SFrank Li for (i = 0; i < RX_RING_SIZE; i++) { 1668793fc096SFrank Li skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE); 1669793fc096SFrank Li if (!skb) { 1670793fc096SFrank Li fec_enet_free_buffers(ndev); 1671793fc096SFrank Li return -ENOMEM; 1672793fc096SFrank Li } 1673793fc096SFrank Li fep->rx_skbuff[i] = skb; 1674793fc096SFrank Li 1675793fc096SFrank Li bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, skb->data, 1676793fc096SFrank Li FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE); 1677793fc096SFrank Li bdp->cbd_sc = BD_ENET_RX_EMPTY; 1678793fc096SFrank Li 1679793fc096SFrank Li if (fep->bufdesc_ex) { 1680793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 1681793fc096SFrank Li ebdp->cbd_esc = BD_ENET_RX_INT; 1682793fc096SFrank Li } 1683793fc096SFrank Li 1684793fc096SFrank Li bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex); 1685793fc096SFrank Li } 1686793fc096SFrank Li 1687793fc096SFrank Li /* Set the last buffer to wrap. */ 1688793fc096SFrank Li bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex); 1689793fc096SFrank Li bdp->cbd_sc |= BD_SC_WRAP; 1690793fc096SFrank Li 1691793fc096SFrank Li bdp = fep->tx_bd_base; 1692793fc096SFrank Li for (i = 0; i < TX_RING_SIZE; i++) { 1693793fc096SFrank Li fep->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL); 1694793fc096SFrank Li 1695793fc096SFrank Li bdp->cbd_sc = 0; 1696793fc096SFrank Li bdp->cbd_bufaddr = 0; 1697793fc096SFrank Li 1698793fc096SFrank Li if (fep->bufdesc_ex) { 1699793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 170096d2222bSJim Baxter ebdp->cbd_esc = BD_ENET_TX_INT; 1701793fc096SFrank Li } 1702793fc096SFrank Li 1703793fc096SFrank Li bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex); 1704793fc096SFrank Li } 1705793fc096SFrank Li 1706793fc096SFrank Li /* Set the last buffer to wrap. */ 1707793fc096SFrank Li bdp = fec_enet_get_prevdesc(bdp, fep->bufdesc_ex); 1708793fc096SFrank Li bdp->cbd_sc |= BD_SC_WRAP; 1709793fc096SFrank Li 1710793fc096SFrank Li return 0; 1711793fc096SFrank Li } 1712793fc096SFrank Li 1713793fc096SFrank Li static int 1714793fc096SFrank Li fec_enet_open(struct net_device *ndev) 1715793fc096SFrank Li { 1716793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1717793fc096SFrank Li int ret; 1718793fc096SFrank Li 1719793fc096SFrank Li napi_enable(&fep->napi); 1720793fc096SFrank Li 1721793fc096SFrank Li /* I should reset the ring buffers here, but I don't yet know 1722793fc096SFrank Li * a simple way to do that. 1723793fc096SFrank Li */ 1724793fc096SFrank Li 1725793fc096SFrank Li ret = fec_enet_alloc_buffers(ndev); 1726793fc096SFrank Li if (ret) 1727793fc096SFrank Li return ret; 1728793fc096SFrank Li 1729793fc096SFrank Li /* Probe and connect to PHY when open the interface */ 1730793fc096SFrank Li ret = fec_enet_mii_probe(ndev); 1731793fc096SFrank Li if (ret) { 1732793fc096SFrank Li fec_enet_free_buffers(ndev); 1733793fc096SFrank Li return ret; 1734793fc096SFrank Li } 1735793fc096SFrank Li phy_start(fep->phy_dev); 1736793fc096SFrank Li netif_start_queue(ndev); 1737793fc096SFrank Li fep->opened = 1; 1738793fc096SFrank Li return 0; 1739793fc096SFrank Li } 1740793fc096SFrank Li 1741793fc096SFrank Li static int 1742793fc096SFrank Li fec_enet_close(struct net_device *ndev) 1743793fc096SFrank Li { 1744793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1745793fc096SFrank Li 1746793fc096SFrank Li /* Don't know what to do yet. */ 1747793fc096SFrank Li napi_disable(&fep->napi); 1748793fc096SFrank Li fep->opened = 0; 1749793fc096SFrank Li netif_stop_queue(ndev); 1750793fc096SFrank Li fec_stop(ndev); 1751793fc096SFrank Li 1752793fc096SFrank Li if (fep->phy_dev) { 1753793fc096SFrank Li phy_stop(fep->phy_dev); 1754793fc096SFrank Li phy_disconnect(fep->phy_dev); 1755793fc096SFrank Li } 1756793fc096SFrank Li 1757793fc096SFrank Li fec_enet_free_buffers(ndev); 1758793fc096SFrank Li 1759793fc096SFrank Li return 0; 1760793fc096SFrank Li } 1761793fc096SFrank Li 1762793fc096SFrank Li /* Set or clear the multicast filter for this adaptor. 1763793fc096SFrank Li * Skeleton taken from sunlance driver. 1764793fc096SFrank Li * The CPM Ethernet implementation allows Multicast as well as individual 1765793fc096SFrank Li * MAC address filtering. Some of the drivers check to make sure it is 1766793fc096SFrank Li * a group multicast address, and discard those that are not. I guess I 1767793fc096SFrank Li * will do the same for now, but just remove the test if you want 1768793fc096SFrank Li * individual filtering as well (do the upper net layers want or support 1769793fc096SFrank Li * this kind of feature?). 1770793fc096SFrank Li */ 1771793fc096SFrank Li 1772793fc096SFrank Li #define HASH_BITS 6 /* #bits in hash */ 1773793fc096SFrank Li #define CRC32_POLY 0xEDB88320 1774793fc096SFrank Li 1775793fc096SFrank Li static void set_multicast_list(struct net_device *ndev) 1776793fc096SFrank Li { 1777793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1778793fc096SFrank Li struct netdev_hw_addr *ha; 1779793fc096SFrank Li unsigned int i, bit, data, crc, tmp; 1780793fc096SFrank Li unsigned char hash; 1781793fc096SFrank Li 1782793fc096SFrank Li if (ndev->flags & IFF_PROMISC) { 1783793fc096SFrank Li tmp = readl(fep->hwp + FEC_R_CNTRL); 1784793fc096SFrank Li tmp |= 0x8; 1785793fc096SFrank Li writel(tmp, fep->hwp + FEC_R_CNTRL); 1786793fc096SFrank Li return; 1787793fc096SFrank Li } 1788793fc096SFrank Li 1789793fc096SFrank Li tmp = readl(fep->hwp + FEC_R_CNTRL); 1790793fc096SFrank Li tmp &= ~0x8; 1791793fc096SFrank Li writel(tmp, fep->hwp + FEC_R_CNTRL); 1792793fc096SFrank Li 1793793fc096SFrank Li if (ndev->flags & IFF_ALLMULTI) { 1794793fc096SFrank Li /* Catch all multicast addresses, so set the 1795793fc096SFrank Li * filter to all 1's 1796793fc096SFrank Li */ 1797793fc096SFrank Li writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 1798793fc096SFrank Li writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 1799793fc096SFrank Li 1800793fc096SFrank Li return; 1801793fc096SFrank Li } 1802793fc096SFrank Li 1803793fc096SFrank Li /* Clear filter and add the addresses in hash register 1804793fc096SFrank Li */ 1805793fc096SFrank Li writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 1806793fc096SFrank Li writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 1807793fc096SFrank Li 1808793fc096SFrank Li netdev_for_each_mc_addr(ha, ndev) { 1809793fc096SFrank Li /* calculate crc32 value of mac address */ 1810793fc096SFrank Li crc = 0xffffffff; 1811793fc096SFrank Li 1812793fc096SFrank Li for (i = 0; i < ndev->addr_len; i++) { 1813793fc096SFrank Li data = ha->addr[i]; 1814793fc096SFrank Li for (bit = 0; bit < 8; bit++, data >>= 1) { 1815793fc096SFrank Li crc = (crc >> 1) ^ 1816793fc096SFrank Li (((crc ^ data) & 1) ? CRC32_POLY : 0); 1817793fc096SFrank Li } 1818793fc096SFrank Li } 1819793fc096SFrank Li 1820793fc096SFrank Li /* only upper 6 bits (HASH_BITS) are used 1821793fc096SFrank Li * which point to specific bit in he hash registers 1822793fc096SFrank Li */ 1823793fc096SFrank Li hash = (crc >> (32 - HASH_BITS)) & 0x3f; 1824793fc096SFrank Li 1825793fc096SFrank Li if (hash > 31) { 1826793fc096SFrank Li tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 1827793fc096SFrank Li tmp |= 1 << (hash - 32); 1828793fc096SFrank Li writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 1829793fc096SFrank Li } else { 1830793fc096SFrank Li tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_LOW); 1831793fc096SFrank Li tmp |= 1 << hash; 1832793fc096SFrank Li writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 1833793fc096SFrank Li } 1834793fc096SFrank Li } 1835793fc096SFrank Li } 1836793fc096SFrank Li 1837793fc096SFrank Li /* Set a MAC change in hardware. */ 1838793fc096SFrank Li static int 1839793fc096SFrank Li fec_set_mac_address(struct net_device *ndev, void *p) 1840793fc096SFrank Li { 1841793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1842793fc096SFrank Li struct sockaddr *addr = p; 1843793fc096SFrank Li 1844793fc096SFrank Li if (!is_valid_ether_addr(addr->sa_data)) 1845793fc096SFrank Li return -EADDRNOTAVAIL; 1846793fc096SFrank Li 1847793fc096SFrank Li memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len); 1848793fc096SFrank Li 1849793fc096SFrank Li writel(ndev->dev_addr[3] | (ndev->dev_addr[2] << 8) | 1850793fc096SFrank Li (ndev->dev_addr[1] << 16) | (ndev->dev_addr[0] << 24), 1851793fc096SFrank Li fep->hwp + FEC_ADDR_LOW); 1852793fc096SFrank Li writel((ndev->dev_addr[5] << 16) | (ndev->dev_addr[4] << 24), 1853793fc096SFrank Li fep->hwp + FEC_ADDR_HIGH); 1854793fc096SFrank Li return 0; 1855793fc096SFrank Li } 1856793fc096SFrank Li 1857793fc096SFrank Li #ifdef CONFIG_NET_POLL_CONTROLLER 1858793fc096SFrank Li /** 1859793fc096SFrank Li * fec_poll_controller - FEC Poll controller function 1860793fc096SFrank Li * @dev: The FEC network adapter 1861793fc096SFrank Li * 1862793fc096SFrank Li * Polled functionality used by netconsole and others in non interrupt mode 1863793fc096SFrank Li * 1864793fc096SFrank Li */ 1865793fc096SFrank Li static void fec_poll_controller(struct net_device *dev) 1866793fc096SFrank Li { 1867793fc096SFrank Li int i; 1868793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(dev); 1869793fc096SFrank Li 1870793fc096SFrank Li for (i = 0; i < FEC_IRQ_NUM; i++) { 1871793fc096SFrank Li if (fep->irq[i] > 0) { 1872793fc096SFrank Li disable_irq(fep->irq[i]); 1873793fc096SFrank Li fec_enet_interrupt(fep->irq[i], dev); 1874793fc096SFrank Li enable_irq(fep->irq[i]); 1875793fc096SFrank Li } 1876793fc096SFrank Li } 1877793fc096SFrank Li } 1878793fc096SFrank Li #endif 1879793fc096SFrank Li 18804c09eed9SJim Baxter static int fec_set_features(struct net_device *netdev, 18814c09eed9SJim Baxter netdev_features_t features) 18824c09eed9SJim Baxter { 18834c09eed9SJim Baxter struct fec_enet_private *fep = netdev_priv(netdev); 18844c09eed9SJim Baxter netdev_features_t changed = features ^ netdev->features; 18854c09eed9SJim Baxter 18864c09eed9SJim Baxter netdev->features = features; 18874c09eed9SJim Baxter 18884c09eed9SJim Baxter /* Receive checksum has been changed */ 18894c09eed9SJim Baxter if (changed & NETIF_F_RXCSUM) { 18904c09eed9SJim Baxter if (features & NETIF_F_RXCSUM) 18914c09eed9SJim Baxter fep->csum_flags |= FLAG_RX_CSUM_ENABLED; 18924c09eed9SJim Baxter else 18934c09eed9SJim Baxter fep->csum_flags &= ~FLAG_RX_CSUM_ENABLED; 18944c09eed9SJim Baxter 18954c09eed9SJim Baxter if (netif_running(netdev)) { 18964c09eed9SJim Baxter fec_stop(netdev); 18974c09eed9SJim Baxter fec_restart(netdev, fep->phy_dev->duplex); 18984c09eed9SJim Baxter netif_wake_queue(netdev); 18994c09eed9SJim Baxter } else { 19004c09eed9SJim Baxter fec_restart(netdev, fep->phy_dev->duplex); 19014c09eed9SJim Baxter } 19024c09eed9SJim Baxter } 19034c09eed9SJim Baxter 19044c09eed9SJim Baxter return 0; 19054c09eed9SJim Baxter } 19064c09eed9SJim Baxter 1907793fc096SFrank Li static const struct net_device_ops fec_netdev_ops = { 1908793fc096SFrank Li .ndo_open = fec_enet_open, 1909793fc096SFrank Li .ndo_stop = fec_enet_close, 1910793fc096SFrank Li .ndo_start_xmit = fec_enet_start_xmit, 1911793fc096SFrank Li .ndo_set_rx_mode = set_multicast_list, 1912793fc096SFrank Li .ndo_change_mtu = eth_change_mtu, 1913793fc096SFrank Li .ndo_validate_addr = eth_validate_addr, 1914793fc096SFrank Li .ndo_tx_timeout = fec_timeout, 1915793fc096SFrank Li .ndo_set_mac_address = fec_set_mac_address, 1916793fc096SFrank Li .ndo_do_ioctl = fec_enet_ioctl, 1917793fc096SFrank Li #ifdef CONFIG_NET_POLL_CONTROLLER 1918793fc096SFrank Li .ndo_poll_controller = fec_poll_controller, 1919793fc096SFrank Li #endif 19204c09eed9SJim Baxter .ndo_set_features = fec_set_features, 1921793fc096SFrank Li }; 1922793fc096SFrank Li 1923793fc096SFrank Li /* 1924793fc096SFrank Li * XXX: We need to clean up on failure exits here. 1925793fc096SFrank Li * 1926793fc096SFrank Li */ 1927793fc096SFrank Li static int fec_enet_init(struct net_device *ndev) 1928793fc096SFrank Li { 1929793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 193048496255SShawn Guo const struct platform_device_id *id_entry = 193148496255SShawn Guo platform_get_device_id(fep->pdev); 1932793fc096SFrank Li struct bufdesc *cbd_base; 1933793fc096SFrank Li 1934793fc096SFrank Li /* Allocate memory for buffer descriptors. */ 1935793fc096SFrank Li cbd_base = dma_alloc_coherent(NULL, PAGE_SIZE, &fep->bd_dma, 1936793fc096SFrank Li GFP_KERNEL); 1937793fc096SFrank Li if (!cbd_base) 1938793fc096SFrank Li return -ENOMEM; 1939793fc096SFrank Li 1940a210576cSDavid S. Miller memset(cbd_base, 0, PAGE_SIZE); 1941793fc096SFrank Li 1942793fc096SFrank Li fep->netdev = ndev; 1943793fc096SFrank Li 1944793fc096SFrank Li /* Get the Ethernet address */ 1945793fc096SFrank Li fec_get_mac(ndev); 1946793fc096SFrank Li 1947793fc096SFrank Li /* Set receive and transmit descriptor base. */ 1948793fc096SFrank Li fep->rx_bd_base = cbd_base; 1949793fc096SFrank Li if (fep->bufdesc_ex) 1950793fc096SFrank Li fep->tx_bd_base = (struct bufdesc *) 1951793fc096SFrank Li (((struct bufdesc_ex *)cbd_base) + RX_RING_SIZE); 1952793fc096SFrank Li else 1953793fc096SFrank Li fep->tx_bd_base = cbd_base + RX_RING_SIZE; 1954793fc096SFrank Li 1955793fc096SFrank Li /* The FEC Ethernet specific entries in the device structure */ 1956793fc096SFrank Li ndev->watchdog_timeo = TX_TIMEOUT; 1957793fc096SFrank Li ndev->netdev_ops = &fec_netdev_ops; 1958793fc096SFrank Li ndev->ethtool_ops = &fec_enet_ethtool_ops; 1959793fc096SFrank Li 1960793fc096SFrank Li writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK); 1961793fc096SFrank Li netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, FEC_NAPI_WEIGHT); 1962793fc096SFrank Li 1963cdffcf1bSJim Baxter if (id_entry->driver_data & FEC_QUIRK_HAS_VLAN) { 1964cdffcf1bSJim Baxter /* enable hw VLAN support */ 1965cdffcf1bSJim Baxter ndev->features |= NETIF_F_HW_VLAN_CTAG_RX; 1966cdffcf1bSJim Baxter ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX; 1967cdffcf1bSJim Baxter } 1968cdffcf1bSJim Baxter 196948496255SShawn Guo if (id_entry->driver_data & FEC_QUIRK_HAS_CSUM) { 19704c09eed9SJim Baxter /* enable hw accelerator */ 19714c09eed9SJim Baxter ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM 19724c09eed9SJim Baxter | NETIF_F_RXCSUM); 19734c09eed9SJim Baxter ndev->hw_features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM 19744c09eed9SJim Baxter | NETIF_F_RXCSUM); 19754c09eed9SJim Baxter fep->csum_flags |= FLAG_RX_CSUM_ENABLED; 197648496255SShawn Guo } 19774c09eed9SJim Baxter 1978793fc096SFrank Li fec_restart(ndev, 0); 1979793fc096SFrank Li 1980793fc096SFrank Li return 0; 1981793fc096SFrank Li } 1982793fc096SFrank Li 1983793fc096SFrank Li #ifdef CONFIG_OF 1984793fc096SFrank Li static void fec_reset_phy(struct platform_device *pdev) 1985793fc096SFrank Li { 1986793fc096SFrank Li int err, phy_reset; 1987793fc096SFrank Li int msec = 1; 1988793fc096SFrank Li struct device_node *np = pdev->dev.of_node; 1989793fc096SFrank Li 1990793fc096SFrank Li if (!np) 1991793fc096SFrank Li return; 1992793fc096SFrank Li 1993793fc096SFrank Li of_property_read_u32(np, "phy-reset-duration", &msec); 1994793fc096SFrank Li /* A sane reset duration should not be longer than 1s */ 1995793fc096SFrank Li if (msec > 1000) 1996793fc096SFrank Li msec = 1; 1997793fc096SFrank Li 1998793fc096SFrank Li phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0); 1999793fc096SFrank Li if (!gpio_is_valid(phy_reset)) 2000793fc096SFrank Li return; 2001793fc096SFrank Li 2002793fc096SFrank Li err = devm_gpio_request_one(&pdev->dev, phy_reset, 2003793fc096SFrank Li GPIOF_OUT_INIT_LOW, "phy-reset"); 2004793fc096SFrank Li if (err) { 2005793fc096SFrank Li dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err); 2006793fc096SFrank Li return; 2007793fc096SFrank Li } 2008793fc096SFrank Li msleep(msec); 2009793fc096SFrank Li gpio_set_value(phy_reset, 1); 2010793fc096SFrank Li } 2011793fc096SFrank Li #else /* CONFIG_OF */ 2012793fc096SFrank Li static void fec_reset_phy(struct platform_device *pdev) 2013793fc096SFrank Li { 2014793fc096SFrank Li /* 2015793fc096SFrank Li * In case of platform probe, the reset has been done 2016793fc096SFrank Li * by machine code. 2017793fc096SFrank Li */ 2018793fc096SFrank Li } 2019793fc096SFrank Li #endif /* CONFIG_OF */ 2020793fc096SFrank Li 2021793fc096SFrank Li static int 2022793fc096SFrank Li fec_probe(struct platform_device *pdev) 2023793fc096SFrank Li { 2024793fc096SFrank Li struct fec_enet_private *fep; 2025793fc096SFrank Li struct fec_platform_data *pdata; 2026793fc096SFrank Li struct net_device *ndev; 2027793fc096SFrank Li int i, irq, ret = 0; 2028793fc096SFrank Li struct resource *r; 2029793fc096SFrank Li const struct of_device_id *of_id; 2030793fc096SFrank Li static int dev_id; 2031793fc096SFrank Li 2032793fc096SFrank Li of_id = of_match_device(fec_dt_ids, &pdev->dev); 2033793fc096SFrank Li if (of_id) 2034793fc096SFrank Li pdev->id_entry = of_id->data; 2035793fc096SFrank Li 2036793fc096SFrank Li r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2037793fc096SFrank Li if (!r) 2038793fc096SFrank Li return -ENXIO; 2039793fc096SFrank Li 2040793fc096SFrank Li /* Init network device */ 2041793fc096SFrank Li ndev = alloc_etherdev(sizeof(struct fec_enet_private)); 2042793fc096SFrank Li if (!ndev) 2043793fc096SFrank Li return -ENOMEM; 2044793fc096SFrank Li 2045793fc096SFrank Li SET_NETDEV_DEV(ndev, &pdev->dev); 2046793fc096SFrank Li 2047793fc096SFrank Li /* setup board info structure */ 2048793fc096SFrank Li fep = netdev_priv(ndev); 2049793fc096SFrank Li 2050d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 2051793fc096SFrank Li /* default enable pause frame auto negotiation */ 2052793fc096SFrank Li if (pdev->id_entry && 2053793fc096SFrank Li (pdev->id_entry->driver_data & FEC_QUIRK_HAS_GBIT)) 2054793fc096SFrank Li fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG; 2055d1391930SGuenter Roeck #endif 2056793fc096SFrank Li 2057941e173aSTushar Behera fep->hwp = devm_ioremap_resource(&pdev->dev, r); 2058941e173aSTushar Behera if (IS_ERR(fep->hwp)) { 2059941e173aSTushar Behera ret = PTR_ERR(fep->hwp); 2060941e173aSTushar Behera goto failed_ioremap; 2061941e173aSTushar Behera } 2062941e173aSTushar Behera 2063793fc096SFrank Li fep->pdev = pdev; 2064793fc096SFrank Li fep->dev_id = dev_id++; 2065793fc096SFrank Li 2066793fc096SFrank Li fep->bufdesc_ex = 0; 2067793fc096SFrank Li 2068793fc096SFrank Li platform_set_drvdata(pdev, ndev); 2069793fc096SFrank Li 20706c5f7808SGuenter Roeck ret = of_get_phy_mode(pdev->dev.of_node); 2071793fc096SFrank Li if (ret < 0) { 2072793fc096SFrank Li pdata = pdev->dev.platform_data; 2073793fc096SFrank Li if (pdata) 2074793fc096SFrank Li fep->phy_interface = pdata->phy; 2075793fc096SFrank Li else 2076793fc096SFrank Li fep->phy_interface = PHY_INTERFACE_MODE_MII; 2077793fc096SFrank Li } else { 2078793fc096SFrank Li fep->phy_interface = ret; 2079793fc096SFrank Li } 2080793fc096SFrank Li 2081793fc096SFrank Li fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); 2082793fc096SFrank Li if (IS_ERR(fep->clk_ipg)) { 2083793fc096SFrank Li ret = PTR_ERR(fep->clk_ipg); 2084793fc096SFrank Li goto failed_clk; 2085793fc096SFrank Li } 2086793fc096SFrank Li 2087793fc096SFrank Li fep->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); 2088793fc096SFrank Li if (IS_ERR(fep->clk_ahb)) { 2089793fc096SFrank Li ret = PTR_ERR(fep->clk_ahb); 2090793fc096SFrank Li goto failed_clk; 2091793fc096SFrank Li } 2092793fc096SFrank Li 209338f56f33SLinus Torvalds /* enet_out is optional, depends on board */ 209438f56f33SLinus Torvalds fep->clk_enet_out = devm_clk_get(&pdev->dev, "enet_out"); 209538f56f33SLinus Torvalds if (IS_ERR(fep->clk_enet_out)) 209638f56f33SLinus Torvalds fep->clk_enet_out = NULL; 209738f56f33SLinus Torvalds 2098793fc096SFrank Li fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp"); 2099793fc096SFrank Li fep->bufdesc_ex = 2100793fc096SFrank Li pdev->id_entry->driver_data & FEC_QUIRK_HAS_BUFDESC_EX; 2101793fc096SFrank Li if (IS_ERR(fep->clk_ptp)) { 210238f56f33SLinus Torvalds fep->clk_ptp = NULL; 2103793fc096SFrank Li fep->bufdesc_ex = 0; 2104793fc096SFrank Li } 2105793fc096SFrank Li 2106*13a097bdSFabio Estevam ret = clk_prepare_enable(fep->clk_ahb); 2107*13a097bdSFabio Estevam if (ret) 2108*13a097bdSFabio Estevam goto failed_clk; 2109*13a097bdSFabio Estevam 2110*13a097bdSFabio Estevam ret = clk_prepare_enable(fep->clk_ipg); 2111*13a097bdSFabio Estevam if (ret) 2112*13a097bdSFabio Estevam goto failed_clk_ipg; 2113*13a097bdSFabio Estevam 2114*13a097bdSFabio Estevam if (fep->clk_enet_out) { 2115*13a097bdSFabio Estevam ret = clk_prepare_enable(fep->clk_enet_out); 2116*13a097bdSFabio Estevam if (ret) 2117*13a097bdSFabio Estevam goto failed_clk_enet_out; 2118*13a097bdSFabio Estevam } 2119*13a097bdSFabio Estevam 2120*13a097bdSFabio Estevam if (fep->clk_ptp) { 2121*13a097bdSFabio Estevam ret = clk_prepare_enable(fep->clk_ptp); 2122*13a097bdSFabio Estevam if (ret) 2123*13a097bdSFabio Estevam goto failed_clk_ptp; 2124*13a097bdSFabio Estevam } 2125793fc096SFrank Li 2126f4e9f3d2SFabio Estevam fep->reg_phy = devm_regulator_get(&pdev->dev, "phy"); 2127f4e9f3d2SFabio Estevam if (!IS_ERR(fep->reg_phy)) { 2128f4e9f3d2SFabio Estevam ret = regulator_enable(fep->reg_phy); 2129793fc096SFrank Li if (ret) { 2130793fc096SFrank Li dev_err(&pdev->dev, 2131793fc096SFrank Li "Failed to enable phy regulator: %d\n", ret); 2132793fc096SFrank Li goto failed_regulator; 2133793fc096SFrank Li } 2134f6a4d607SFabio Estevam } else { 2135f6a4d607SFabio Estevam fep->reg_phy = NULL; 2136793fc096SFrank Li } 2137793fc096SFrank Li 2138793fc096SFrank Li fec_reset_phy(pdev); 2139793fc096SFrank Li 2140793fc096SFrank Li if (fep->bufdesc_ex) 2141ca162a82SFabio Estevam fec_ptp_init(pdev); 2142793fc096SFrank Li 2143793fc096SFrank Li ret = fec_enet_init(ndev); 2144793fc096SFrank Li if (ret) 2145793fc096SFrank Li goto failed_init; 2146793fc096SFrank Li 2147793fc096SFrank Li for (i = 0; i < FEC_IRQ_NUM; i++) { 2148793fc096SFrank Li irq = platform_get_irq(pdev, i); 2149793fc096SFrank Li if (irq < 0) { 2150793fc096SFrank Li if (i) 2151793fc096SFrank Li break; 2152793fc096SFrank Li ret = irq; 2153793fc096SFrank Li goto failed_irq; 2154793fc096SFrank Li } 2155793fc096SFrank Li ret = request_irq(irq, fec_enet_interrupt, IRQF_DISABLED, pdev->name, ndev); 2156793fc096SFrank Li if (ret) { 2157793fc096SFrank Li while (--i >= 0) { 2158793fc096SFrank Li irq = platform_get_irq(pdev, i); 2159793fc096SFrank Li free_irq(irq, ndev); 2160793fc096SFrank Li } 2161793fc096SFrank Li goto failed_irq; 2162793fc096SFrank Li } 2163793fc096SFrank Li } 2164793fc096SFrank Li 2165793fc096SFrank Li ret = fec_enet_mii_init(pdev); 2166793fc096SFrank Li if (ret) 2167793fc096SFrank Li goto failed_mii_init; 2168793fc096SFrank Li 2169793fc096SFrank Li /* Carrier starts down, phylib will bring it up */ 2170793fc096SFrank Li netif_carrier_off(ndev); 2171793fc096SFrank Li 2172793fc096SFrank Li ret = register_netdev(ndev); 2173793fc096SFrank Li if (ret) 2174793fc096SFrank Li goto failed_register; 2175793fc096SFrank Li 2176eb1d0640SFabio Estevam if (fep->bufdesc_ex && fep->ptp_clock) 2177eb1d0640SFabio Estevam netdev_info(ndev, "registered PHC device %d\n", fep->dev_id); 2178eb1d0640SFabio Estevam 217954309fa6SFrank Li INIT_DELAYED_WORK(&(fep->delay_work.delay_work), fec_enet_work); 2180793fc096SFrank Li return 0; 2181793fc096SFrank Li 2182793fc096SFrank Li failed_register: 2183793fc096SFrank Li fec_enet_mii_remove(fep); 2184793fc096SFrank Li failed_mii_init: 21857a2bbd8dSFabio Estevam failed_irq: 2186793fc096SFrank Li for (i = 0; i < FEC_IRQ_NUM; i++) { 2187793fc096SFrank Li irq = platform_get_irq(pdev, i); 2188793fc096SFrank Li if (irq > 0) 2189793fc096SFrank Li free_irq(irq, ndev); 2190793fc096SFrank Li } 21917a2bbd8dSFabio Estevam failed_init: 2192f6a4d607SFabio Estevam if (fep->reg_phy) 2193f6a4d607SFabio Estevam regulator_disable(fep->reg_phy); 2194793fc096SFrank Li failed_regulator: 21959514fe7aSFabio Estevam if (fep->clk_ptp) 2196793fc096SFrank Li clk_disable_unprepare(fep->clk_ptp); 2197*13a097bdSFabio Estevam failed_clk_ptp: 2198d265cf48SFabio Estevam if (fep->clk_enet_out) 2199d265cf48SFabio Estevam clk_disable_unprepare(fep->clk_enet_out); 2200*13a097bdSFabio Estevam failed_clk_enet_out: 2201d265cf48SFabio Estevam clk_disable_unprepare(fep->clk_ipg); 2202*13a097bdSFabio Estevam failed_clk_ipg: 2203d265cf48SFabio Estevam clk_disable_unprepare(fep->clk_ahb); 2204793fc096SFrank Li failed_clk: 2205793fc096SFrank Li failed_ioremap: 2206793fc096SFrank Li free_netdev(ndev); 2207793fc096SFrank Li 2208793fc096SFrank Li return ret; 2209793fc096SFrank Li } 2210793fc096SFrank Li 2211793fc096SFrank Li static int 2212793fc096SFrank Li fec_drv_remove(struct platform_device *pdev) 2213793fc096SFrank Li { 2214793fc096SFrank Li struct net_device *ndev = platform_get_drvdata(pdev); 2215793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2216793fc096SFrank Li int i; 2217793fc096SFrank Li 221854309fa6SFrank Li cancel_delayed_work_sync(&(fep->delay_work.delay_work)); 2219793fc096SFrank Li unregister_netdev(ndev); 2220793fc096SFrank Li fec_enet_mii_remove(fep); 2221793fc096SFrank Li del_timer_sync(&fep->time_keep); 2222c55284e4SFabio Estevam for (i = 0; i < FEC_IRQ_NUM; i++) { 2223c55284e4SFabio Estevam int irq = platform_get_irq(pdev, i); 2224c55284e4SFabio Estevam if (irq > 0) 2225c55284e4SFabio Estevam free_irq(irq, ndev); 2226c55284e4SFabio Estevam } 2227f6a4d607SFabio Estevam if (fep->reg_phy) 2228f6a4d607SFabio Estevam regulator_disable(fep->reg_phy); 22299514fe7aSFabio Estevam if (fep->clk_ptp) 2230793fc096SFrank Li clk_disable_unprepare(fep->clk_ptp); 2231793fc096SFrank Li if (fep->ptp_clock) 2232793fc096SFrank Li ptp_clock_unregister(fep->ptp_clock); 22339514fe7aSFabio Estevam if (fep->clk_enet_out) 223438f56f33SLinus Torvalds clk_disable_unprepare(fep->clk_enet_out); 2235793fc096SFrank Li clk_disable_unprepare(fep->clk_ipg); 2236d265cf48SFabio Estevam clk_disable_unprepare(fep->clk_ahb); 2237793fc096SFrank Li free_netdev(ndev); 2238793fc096SFrank Li 2239793fc096SFrank Li return 0; 2240793fc096SFrank Li } 2241793fc096SFrank Li 2242bf7bfd7fSFabio Estevam #ifdef CONFIG_PM_SLEEP 2243793fc096SFrank Li static int 2244793fc096SFrank Li fec_suspend(struct device *dev) 2245793fc096SFrank Li { 2246793fc096SFrank Li struct net_device *ndev = dev_get_drvdata(dev); 2247793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2248793fc096SFrank Li 2249793fc096SFrank Li if (netif_running(ndev)) { 2250793fc096SFrank Li fec_stop(ndev); 2251793fc096SFrank Li netif_device_detach(ndev); 2252793fc096SFrank Li } 225379820e72SFabio Estevam if (fep->clk_ptp) 225479820e72SFabio Estevam clk_disable_unprepare(fep->clk_ptp); 22559514fe7aSFabio Estevam if (fep->clk_enet_out) 225638f56f33SLinus Torvalds clk_disable_unprepare(fep->clk_enet_out); 2257793fc096SFrank Li clk_disable_unprepare(fep->clk_ipg); 2258d265cf48SFabio Estevam clk_disable_unprepare(fep->clk_ahb); 2259793fc096SFrank Li 2260238f7bc7SFabio Estevam if (fep->reg_phy) 2261238f7bc7SFabio Estevam regulator_disable(fep->reg_phy); 2262238f7bc7SFabio Estevam 2263793fc096SFrank Li return 0; 2264793fc096SFrank Li } 2265793fc096SFrank Li 2266793fc096SFrank Li static int 2267793fc096SFrank Li fec_resume(struct device *dev) 2268793fc096SFrank Li { 2269793fc096SFrank Li struct net_device *ndev = dev_get_drvdata(dev); 2270793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2271238f7bc7SFabio Estevam int ret; 2272238f7bc7SFabio Estevam 2273238f7bc7SFabio Estevam if (fep->reg_phy) { 2274238f7bc7SFabio Estevam ret = regulator_enable(fep->reg_phy); 2275238f7bc7SFabio Estevam if (ret) 2276238f7bc7SFabio Estevam return ret; 2277238f7bc7SFabio Estevam } 2278793fc096SFrank Li 2279*13a097bdSFabio Estevam ret = clk_prepare_enable(fep->clk_ahb); 2280*13a097bdSFabio Estevam if (ret) 2281*13a097bdSFabio Estevam goto failed_clk_ahb; 2282*13a097bdSFabio Estevam 2283*13a097bdSFabio Estevam ret = clk_prepare_enable(fep->clk_ipg); 2284*13a097bdSFabio Estevam if (ret) 2285*13a097bdSFabio Estevam goto failed_clk_ipg; 2286*13a097bdSFabio Estevam 2287*13a097bdSFabio Estevam if (fep->clk_enet_out) { 2288*13a097bdSFabio Estevam ret = clk_prepare_enable(fep->clk_enet_out); 2289*13a097bdSFabio Estevam if (ret) 2290*13a097bdSFabio Estevam goto failed_clk_enet_out; 2291*13a097bdSFabio Estevam } 2292*13a097bdSFabio Estevam 2293*13a097bdSFabio Estevam if (fep->clk_ptp) { 2294*13a097bdSFabio Estevam ret = clk_prepare_enable(fep->clk_ptp); 2295*13a097bdSFabio Estevam if (ret) 2296*13a097bdSFabio Estevam goto failed_clk_ptp; 2297*13a097bdSFabio Estevam } 2298*13a097bdSFabio Estevam 2299793fc096SFrank Li if (netif_running(ndev)) { 2300793fc096SFrank Li fec_restart(ndev, fep->full_duplex); 2301793fc096SFrank Li netif_device_attach(ndev); 2302793fc096SFrank Li } 2303793fc096SFrank Li 2304793fc096SFrank Li return 0; 2305*13a097bdSFabio Estevam 2306*13a097bdSFabio Estevam failed_clk_ptp: 2307*13a097bdSFabio Estevam if (fep->clk_enet_out) 2308*13a097bdSFabio Estevam clk_disable_unprepare(fep->clk_enet_out); 2309*13a097bdSFabio Estevam failed_clk_enet_out: 2310*13a097bdSFabio Estevam clk_disable_unprepare(fep->clk_ipg); 2311*13a097bdSFabio Estevam failed_clk_ipg: 2312*13a097bdSFabio Estevam clk_disable_unprepare(fep->clk_ahb); 2313*13a097bdSFabio Estevam failed_clk_ahb: 2314*13a097bdSFabio Estevam if (fep->reg_phy) 2315*13a097bdSFabio Estevam regulator_disable(fep->reg_phy); 2316*13a097bdSFabio Estevam return ret; 2317793fc096SFrank Li } 2318bf7bfd7fSFabio Estevam #endif /* CONFIG_PM_SLEEP */ 2319793fc096SFrank Li 2320bf7bfd7fSFabio Estevam static SIMPLE_DEV_PM_OPS(fec_pm_ops, fec_suspend, fec_resume); 2321793fc096SFrank Li 2322793fc096SFrank Li static struct platform_driver fec_driver = { 2323793fc096SFrank Li .driver = { 2324793fc096SFrank Li .name = DRIVER_NAME, 2325793fc096SFrank Li .owner = THIS_MODULE, 2326793fc096SFrank Li .pm = &fec_pm_ops, 2327793fc096SFrank Li .of_match_table = fec_dt_ids, 2328793fc096SFrank Li }, 2329793fc096SFrank Li .id_table = fec_devtype, 2330793fc096SFrank Li .probe = fec_probe, 2331793fc096SFrank Li .remove = fec_drv_remove, 2332793fc096SFrank Li }; 2333793fc096SFrank Li 2334793fc096SFrank Li module_platform_driver(fec_driver); 2335793fc096SFrank Li 2336793fc096SFrank Li MODULE_LICENSE("GPL"); 2337