1793fc096SFrank Li /* 2793fc096SFrank Li * Fast Ethernet Controller (FEC) driver for Motorola MPC8xx. 3793fc096SFrank Li * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) 4793fc096SFrank Li * 5793fc096SFrank Li * Right now, I am very wasteful with the buffers. I allocate memory 6793fc096SFrank Li * pages and then divide them into 2K frame buffers. This way I know I 7793fc096SFrank Li * have buffers large enough to hold one frame within one buffer descriptor. 8793fc096SFrank Li * Once I get this working, I will use 64 or 128 byte CPM buffers, which 9793fc096SFrank Li * will be much more memory efficient and will easily handle lots of 10793fc096SFrank Li * small packets. 11793fc096SFrank Li * 12793fc096SFrank Li * Much better multiple PHY support by Magnus Damm. 13793fc096SFrank Li * Copyright (c) 2000 Ericsson Radio Systems AB. 14793fc096SFrank Li * 15793fc096SFrank Li * Support for FEC controller of ColdFire processors. 16793fc096SFrank Li * Copyright (c) 2001-2005 Greg Ungerer (gerg@snapgear.com) 17793fc096SFrank Li * 18793fc096SFrank Li * Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be) 19793fc096SFrank Li * Copyright (c) 2004-2006 Macq Electronique SA. 20793fc096SFrank Li * 21793fc096SFrank Li * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. 22793fc096SFrank Li */ 23793fc096SFrank Li 24793fc096SFrank Li #include <linux/module.h> 25793fc096SFrank Li #include <linux/kernel.h> 26793fc096SFrank Li #include <linux/string.h> 278fff755eSAndrew Lunn #include <linux/pm_runtime.h> 28793fc096SFrank Li #include <linux/ptrace.h> 29793fc096SFrank Li #include <linux/errno.h> 30793fc096SFrank Li #include <linux/ioport.h> 31793fc096SFrank Li #include <linux/slab.h> 32793fc096SFrank Li #include <linux/interrupt.h> 33793fc096SFrank Li #include <linux/delay.h> 34793fc096SFrank Li #include <linux/netdevice.h> 35793fc096SFrank Li #include <linux/etherdevice.h> 36793fc096SFrank Li #include <linux/skbuff.h> 374c09eed9SJim Baxter #include <linux/in.h> 384c09eed9SJim Baxter #include <linux/ip.h> 394c09eed9SJim Baxter #include <net/ip.h> 4079f33912SNimrod Andy #include <net/tso.h> 414c09eed9SJim Baxter #include <linux/tcp.h> 424c09eed9SJim Baxter #include <linux/udp.h> 434c09eed9SJim Baxter #include <linux/icmp.h> 44793fc096SFrank Li #include <linux/spinlock.h> 45793fc096SFrank Li #include <linux/workqueue.h> 46793fc096SFrank Li #include <linux/bitops.h> 47793fc096SFrank Li #include <linux/io.h> 48793fc096SFrank Li #include <linux/irq.h> 49793fc096SFrank Li #include <linux/clk.h> 50793fc096SFrank Li #include <linux/platform_device.h> 51793fc096SFrank Li #include <linux/phy.h> 52793fc096SFrank Li #include <linux/fec.h> 53793fc096SFrank Li #include <linux/of.h> 54793fc096SFrank Li #include <linux/of_device.h> 55793fc096SFrank Li #include <linux/of_gpio.h> 56407066f8SUwe Kleine-König #include <linux/of_mdio.h> 57793fc096SFrank Li #include <linux/of_net.h> 58793fc096SFrank Li #include <linux/regulator/consumer.h> 59cdffcf1bSJim Baxter #include <linux/if_vlan.h> 60a68ab98eSFabio Estevam #include <linux/pinctrl/consumer.h> 61c259c132SFrank Li #include <linux/prefetch.h> 62793fc096SFrank Li 63793fc096SFrank Li #include <asm/cacheflush.h> 64793fc096SFrank Li 65793fc096SFrank Li #include "fec.h" 66793fc096SFrank Li 67772e42b0SChristoph Müllner static void set_multicast_list(struct net_device *ndev); 68d851b47bSFugang Duan static void fec_enet_itr_coal_init(struct net_device *ndev); 69772e42b0SChristoph Müllner 70793fc096SFrank Li #define DRIVER_NAME "fec" 71793fc096SFrank Li 724d494cdcSFugang Duan #define FEC_ENET_GET_QUQUE(_x) ((_x == 0) ? 1 : ((_x == 1) ? 2 : 0)) 734d494cdcSFugang Duan 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 818fff755eSAndrew Lunn #define FEC_MDIO_PM_TIMEOUT 100 /* ms */ 82793fc096SFrank Li 83793fc096SFrank Li static struct platform_device_id fec_devtype[] = { 84793fc096SFrank Li { 85793fc096SFrank Li /* keep it for coldfire */ 86793fc096SFrank Li .name = DRIVER_NAME, 87793fc096SFrank Li .driver_data = 0, 88793fc096SFrank Li }, { 89793fc096SFrank Li .name = "imx25-fec", 9018803495SGreg Ungerer .driver_data = FEC_QUIRK_USE_GASKET | FEC_QUIRK_HAS_RACC, 91793fc096SFrank Li }, { 92793fc096SFrank Li .name = "imx27-fec", 9318803495SGreg Ungerer .driver_data = FEC_QUIRK_HAS_RACC, 94793fc096SFrank Li }, { 95793fc096SFrank Li .name = "imx28-fec", 963d125f9cSStefan Agner .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME | 9718803495SGreg Ungerer FEC_QUIRK_SINGLE_MDIO | FEC_QUIRK_HAS_RACC, 98793fc096SFrank Li }, { 99793fc096SFrank Li .name = "imx6q-fec", 100793fc096SFrank Li .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 101cdffcf1bSJim Baxter FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 10218803495SGreg Ungerer FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR006358 | 10318803495SGreg Ungerer FEC_QUIRK_HAS_RACC, 104793fc096SFrank Li }, { 10536803542SShawn Guo .name = "mvf600-fec", 10618803495SGreg Ungerer .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_RACC, 107ca7c4a45SJingchang Lu }, { 10895a77470SFugang Duan .name = "imx6sx-fec", 10995a77470SFugang Duan .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 11095a77470SFugang Duan FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 111f88c7edeSNimrod Andy FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | 11218803495SGreg Ungerer FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | 11318803495SGreg Ungerer FEC_QUIRK_HAS_RACC, 11495a77470SFugang Duan }, { 115793fc096SFrank Li /* sentinel */ 116793fc096SFrank Li } 117793fc096SFrank Li }; 118793fc096SFrank Li MODULE_DEVICE_TABLE(platform, fec_devtype); 119793fc096SFrank Li 120793fc096SFrank Li enum imx_fec_type { 121793fc096SFrank Li IMX25_FEC = 1, /* runs on i.mx25/50/53 */ 122793fc096SFrank Li IMX27_FEC, /* runs on i.mx27/35/51 */ 123793fc096SFrank Li IMX28_FEC, 124793fc096SFrank Li IMX6Q_FEC, 12536803542SShawn Guo MVF600_FEC, 126ba593e00SFugang Duan IMX6SX_FEC, 127793fc096SFrank Li }; 128793fc096SFrank Li 129793fc096SFrank Li static const struct of_device_id fec_dt_ids[] = { 130793fc096SFrank Li { .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], }, 131793fc096SFrank Li { .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], }, 132793fc096SFrank Li { .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], }, 133793fc096SFrank Li { .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], }, 13436803542SShawn Guo { .compatible = "fsl,mvf600-fec", .data = &fec_devtype[MVF600_FEC], }, 135ba593e00SFugang Duan { .compatible = "fsl,imx6sx-fec", .data = &fec_devtype[IMX6SX_FEC], }, 136793fc096SFrank Li { /* sentinel */ } 137793fc096SFrank Li }; 138793fc096SFrank Li MODULE_DEVICE_TABLE(of, fec_dt_ids); 139793fc096SFrank Li 140793fc096SFrank Li static unsigned char macaddr[ETH_ALEN]; 141793fc096SFrank Li module_param_array(macaddr, byte, NULL, 0); 142793fc096SFrank Li MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); 143793fc096SFrank Li 144793fc096SFrank Li #if defined(CONFIG_M5272) 145793fc096SFrank Li /* 146793fc096SFrank Li * Some hardware gets it MAC address out of local flash memory. 147793fc096SFrank Li * if this is non-zero then assume it is the address to get MAC from. 148793fc096SFrank Li */ 149793fc096SFrank Li #if defined(CONFIG_NETtel) 150793fc096SFrank Li #define FEC_FLASHMAC 0xf0006006 151793fc096SFrank Li #elif defined(CONFIG_GILBARCONAP) || defined(CONFIG_SCALES) 152793fc096SFrank Li #define FEC_FLASHMAC 0xf0006000 153793fc096SFrank Li #elif defined(CONFIG_CANCam) 154793fc096SFrank Li #define FEC_FLASHMAC 0xf0020000 155793fc096SFrank Li #elif defined (CONFIG_M5272C3) 156793fc096SFrank Li #define FEC_FLASHMAC (0xffe04000 + 4) 157793fc096SFrank Li #elif defined(CONFIG_MOD5272) 158793fc096SFrank Li #define FEC_FLASHMAC 0xffc0406b 159793fc096SFrank Li #else 160793fc096SFrank Li #define FEC_FLASHMAC 0 161793fc096SFrank Li #endif 162793fc096SFrank Li #endif /* CONFIG_M5272 */ 163793fc096SFrank Li 164cdffcf1bSJim Baxter /* The FEC stores dest/src/type/vlan, data, and checksum for receive packets. 165793fc096SFrank Li */ 166cdffcf1bSJim Baxter #define PKT_MAXBUF_SIZE 1522 167793fc096SFrank Li #define PKT_MINBUF_SIZE 64 168cdffcf1bSJim Baxter #define PKT_MAXBLR_SIZE 1536 169793fc096SFrank Li 1704c09eed9SJim Baxter /* FEC receive acceleration */ 1714c09eed9SJim Baxter #define FEC_RACC_IPDIS (1 << 1) 1724c09eed9SJim Baxter #define FEC_RACC_PRODIS (1 << 2) 1734c09eed9SJim Baxter #define FEC_RACC_OPTIONS (FEC_RACC_IPDIS | FEC_RACC_PRODIS) 1744c09eed9SJim Baxter 175793fc096SFrank Li /* 176793fc096SFrank Li * The 5270/5271/5280/5282/532x RX control register also contains maximum frame 177793fc096SFrank Li * size bits. Other FEC hardware does not, so we need to take that into 178793fc096SFrank Li * account when setting it. 179793fc096SFrank Li */ 180793fc096SFrank Li #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ 181793fc096SFrank Li defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) 182793fc096SFrank Li #define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16) 183793fc096SFrank Li #else 184793fc096SFrank Li #define OPT_FRAME_SIZE 0 185793fc096SFrank Li #endif 186793fc096SFrank Li 187793fc096SFrank Li /* FEC MII MMFR bits definition */ 188793fc096SFrank Li #define FEC_MMFR_ST (1 << 30) 189793fc096SFrank Li #define FEC_MMFR_OP_READ (2 << 28) 190793fc096SFrank Li #define FEC_MMFR_OP_WRITE (1 << 28) 191793fc096SFrank Li #define FEC_MMFR_PA(v) ((v & 0x1f) << 23) 192793fc096SFrank Li #define FEC_MMFR_RA(v) ((v & 0x1f) << 18) 193793fc096SFrank Li #define FEC_MMFR_TA (2 << 16) 194793fc096SFrank Li #define FEC_MMFR_DATA(v) (v & 0xffff) 195de40ed31SNimrod Andy /* FEC ECR bits definition */ 196de40ed31SNimrod Andy #define FEC_ECR_MAGICEN (1 << 2) 197de40ed31SNimrod Andy #define FEC_ECR_SLEEP (1 << 3) 198793fc096SFrank Li 199793fc096SFrank Li #define FEC_MII_TIMEOUT 30000 /* us */ 200793fc096SFrank Li 201793fc096SFrank Li /* Transmitter timeout */ 202793fc096SFrank Li #define TX_TIMEOUT (2 * HZ) 203793fc096SFrank Li 204793fc096SFrank Li #define FEC_PAUSE_FLAG_AUTONEG 0x1 205793fc096SFrank Li #define FEC_PAUSE_FLAG_ENABLE 0x2 206de40ed31SNimrod Andy #define FEC_WOL_HAS_MAGIC_PACKET (0x1 << 0) 207de40ed31SNimrod Andy #define FEC_WOL_FLAG_ENABLE (0x1 << 1) 208de40ed31SNimrod Andy #define FEC_WOL_FLAG_SLEEP_ON (0x1 << 2) 209793fc096SFrank Li 2101b7bde6dSNimrod Andy #define COPYBREAK_DEFAULT 256 2111b7bde6dSNimrod Andy 21279f33912SNimrod Andy #define TSO_HEADER_SIZE 128 21379f33912SNimrod Andy /* Max number of allowed TCP segments for software TSO */ 21479f33912SNimrod Andy #define FEC_MAX_TSO_SEGS 100 21579f33912SNimrod Andy #define FEC_MAX_SKB_DESCS (FEC_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS) 21679f33912SNimrod Andy 21779f33912SNimrod Andy #define IS_TSO_HEADER(txq, addr) \ 21879f33912SNimrod Andy ((addr >= txq->tso_hdrs_dma) && \ 21979f33912SNimrod Andy (addr < txq->tso_hdrs_dma + txq->tx_ring_size * TSO_HEADER_SIZE)) 22079f33912SNimrod Andy 221793fc096SFrank Li static int mii_cnt; 222793fc096SFrank Li 22336e24e2eSDuan Fugang-B38611 static inline 2244d494cdcSFugang Duan struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, 2254d494cdcSFugang Duan struct fec_enet_private *fep, 2264d494cdcSFugang Duan int queue_id) 227793fc096SFrank Li { 22836e24e2eSDuan Fugang-B38611 struct bufdesc *new_bd = bdp + 1; 22936e24e2eSDuan Fugang-B38611 struct bufdesc_ex *ex_new_bd = (struct bufdesc_ex *)bdp + 1; 2304d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq = fep->tx_queue[queue_id]; 2314d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq = fep->rx_queue[queue_id]; 23236e24e2eSDuan Fugang-B38611 struct bufdesc_ex *ex_base; 23336e24e2eSDuan Fugang-B38611 struct bufdesc *base; 23436e24e2eSDuan Fugang-B38611 int ring_size; 23536e24e2eSDuan Fugang-B38611 2364d494cdcSFugang Duan if (bdp >= txq->tx_bd_base) { 2374d494cdcSFugang Duan base = txq->tx_bd_base; 2384d494cdcSFugang Duan ring_size = txq->tx_ring_size; 2394d494cdcSFugang Duan ex_base = (struct bufdesc_ex *)txq->tx_bd_base; 24036e24e2eSDuan Fugang-B38611 } else { 2414d494cdcSFugang Duan base = rxq->rx_bd_base; 2424d494cdcSFugang Duan ring_size = rxq->rx_ring_size; 2434d494cdcSFugang Duan ex_base = (struct bufdesc_ex *)rxq->rx_bd_base; 244793fc096SFrank Li } 245793fc096SFrank Li 24636e24e2eSDuan Fugang-B38611 if (fep->bufdesc_ex) 24736e24e2eSDuan Fugang-B38611 return (struct bufdesc *)((ex_new_bd >= (ex_base + ring_size)) ? 24836e24e2eSDuan Fugang-B38611 ex_base : ex_new_bd); 249793fc096SFrank Li else 25036e24e2eSDuan Fugang-B38611 return (new_bd >= (base + ring_size)) ? 25136e24e2eSDuan Fugang-B38611 base : new_bd; 25236e24e2eSDuan Fugang-B38611 } 25336e24e2eSDuan Fugang-B38611 25436e24e2eSDuan Fugang-B38611 static inline 2554d494cdcSFugang Duan struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, 2564d494cdcSFugang Duan struct fec_enet_private *fep, 2574d494cdcSFugang Duan int queue_id) 25836e24e2eSDuan Fugang-B38611 { 25936e24e2eSDuan Fugang-B38611 struct bufdesc *new_bd = bdp - 1; 26036e24e2eSDuan Fugang-B38611 struct bufdesc_ex *ex_new_bd = (struct bufdesc_ex *)bdp - 1; 2614d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq = fep->tx_queue[queue_id]; 2624d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq = fep->rx_queue[queue_id]; 26336e24e2eSDuan Fugang-B38611 struct bufdesc_ex *ex_base; 26436e24e2eSDuan Fugang-B38611 struct bufdesc *base; 26536e24e2eSDuan Fugang-B38611 int ring_size; 26636e24e2eSDuan Fugang-B38611 2674d494cdcSFugang Duan if (bdp >= txq->tx_bd_base) { 2684d494cdcSFugang Duan base = txq->tx_bd_base; 2694d494cdcSFugang Duan ring_size = txq->tx_ring_size; 2704d494cdcSFugang Duan ex_base = (struct bufdesc_ex *)txq->tx_bd_base; 27136e24e2eSDuan Fugang-B38611 } else { 2724d494cdcSFugang Duan base = rxq->rx_bd_base; 2734d494cdcSFugang Duan ring_size = rxq->rx_ring_size; 2744d494cdcSFugang Duan ex_base = (struct bufdesc_ex *)rxq->rx_bd_base; 27536e24e2eSDuan Fugang-B38611 } 27636e24e2eSDuan Fugang-B38611 27736e24e2eSDuan Fugang-B38611 if (fep->bufdesc_ex) 27836e24e2eSDuan Fugang-B38611 return (struct bufdesc *)((ex_new_bd < ex_base) ? 27936e24e2eSDuan Fugang-B38611 (ex_new_bd + ring_size) : ex_new_bd); 28036e24e2eSDuan Fugang-B38611 else 28136e24e2eSDuan Fugang-B38611 return (new_bd < base) ? (new_bd + ring_size) : new_bd; 282793fc096SFrank Li } 283793fc096SFrank Li 28461a4427bSNimrod Andy static int fec_enet_get_bd_index(struct bufdesc *base, struct bufdesc *bdp, 28561a4427bSNimrod Andy struct fec_enet_private *fep) 28661a4427bSNimrod Andy { 28761a4427bSNimrod Andy return ((const char *)bdp - (const char *)base) / fep->bufdesc_size; 28861a4427bSNimrod Andy } 28961a4427bSNimrod Andy 2904d494cdcSFugang Duan static int fec_enet_get_free_txdesc_num(struct fec_enet_private *fep, 2914d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq) 2926e909283SNimrod Andy { 2936e909283SNimrod Andy int entries; 2946e909283SNimrod Andy 2954d494cdcSFugang Duan entries = ((const char *)txq->dirty_tx - 2964d494cdcSFugang Duan (const char *)txq->cur_tx) / fep->bufdesc_size - 1; 2976e909283SNimrod Andy 2984d494cdcSFugang Duan return entries > 0 ? entries : entries + txq->tx_ring_size; 2996e909283SNimrod Andy } 3006e909283SNimrod Andy 301c20e599bSLothar Waßmann static void swap_buffer(void *bufaddr, int len) 302793fc096SFrank Li { 303793fc096SFrank Li int i; 304793fc096SFrank Li unsigned int *buf = bufaddr; 305793fc096SFrank Li 3067b487d07SLothar Waßmann for (i = 0; i < len; i += 4, buf++) 307e453789aSLothar Waßmann swab32s(buf); 308793fc096SFrank Li } 309793fc096SFrank Li 3101310b544SLothar Waßmann static void swap_buffer2(void *dst_buf, void *src_buf, int len) 3111310b544SLothar Waßmann { 3121310b544SLothar Waßmann int i; 3131310b544SLothar Waßmann unsigned int *src = src_buf; 3141310b544SLothar Waßmann unsigned int *dst = dst_buf; 3151310b544SLothar Waßmann 3161310b544SLothar Waßmann for (i = 0; i < len; i += 4, src++, dst++) 3171310b544SLothar Waßmann *dst = swab32p(src); 3181310b544SLothar Waßmann } 3191310b544SLothar Waßmann 320344756f6SRussell King static void fec_dump(struct net_device *ndev) 321344756f6SRussell King { 322344756f6SRussell King struct fec_enet_private *fep = netdev_priv(ndev); 3234d494cdcSFugang Duan struct bufdesc *bdp; 3244d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 3254d494cdcSFugang Duan int index = 0; 326344756f6SRussell King 327344756f6SRussell King netdev_info(ndev, "TX ring dump\n"); 328344756f6SRussell King pr_info("Nr SC addr len SKB\n"); 329344756f6SRussell King 3304d494cdcSFugang Duan txq = fep->tx_queue[0]; 3314d494cdcSFugang Duan bdp = txq->tx_bd_base; 3324d494cdcSFugang Duan 333344756f6SRussell King do { 334344756f6SRussell King pr_info("%3u %c%c 0x%04x 0x%08lx %4u %p\n", 335344756f6SRussell King index, 3364d494cdcSFugang Duan bdp == txq->cur_tx ? 'S' : ' ', 3374d494cdcSFugang Duan bdp == txq->dirty_tx ? 'H' : ' ', 338344756f6SRussell King bdp->cbd_sc, bdp->cbd_bufaddr, bdp->cbd_datlen, 3394d494cdcSFugang Duan txq->tx_skbuff[index]); 3404d494cdcSFugang Duan bdp = fec_enet_get_nextdesc(bdp, fep, 0); 341344756f6SRussell King index++; 3424d494cdcSFugang Duan } while (bdp != txq->tx_bd_base); 343344756f6SRussell King } 344344756f6SRussell King 34562a02c98SFugang Duan static inline bool is_ipv4_pkt(struct sk_buff *skb) 34662a02c98SFugang Duan { 34762a02c98SFugang Duan return skb->protocol == htons(ETH_P_IP) && ip_hdr(skb)->version == 4; 34862a02c98SFugang Duan } 34962a02c98SFugang Duan 3504c09eed9SJim Baxter static int 3514c09eed9SJim Baxter fec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev) 3524c09eed9SJim Baxter { 3534c09eed9SJim Baxter /* Only run for packets requiring a checksum. */ 3544c09eed9SJim Baxter if (skb->ip_summed != CHECKSUM_PARTIAL) 3554c09eed9SJim Baxter return 0; 3564c09eed9SJim Baxter 3574c09eed9SJim Baxter if (unlikely(skb_cow_head(skb, 0))) 3584c09eed9SJim Baxter return -1; 3594c09eed9SJim Baxter 36062a02c98SFugang Duan if (is_ipv4_pkt(skb)) 36196c50caaSNimrod Andy ip_hdr(skb)->check = 0; 3624c09eed9SJim Baxter *(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) = 0; 3634c09eed9SJim Baxter 3644c09eed9SJim Baxter return 0; 3654c09eed9SJim Baxter } 3664c09eed9SJim Baxter 367c4bc44c6SKevin Hao static struct bufdesc * 3684d494cdcSFugang Duan fec_enet_txq_submit_frag_skb(struct fec_enet_priv_tx_q *txq, 3694d494cdcSFugang Duan struct sk_buff *skb, 3704d494cdcSFugang Duan struct net_device *ndev) 3716e909283SNimrod Andy { 3726e909283SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 3734d494cdcSFugang Duan struct bufdesc *bdp = txq->cur_tx; 3746e909283SNimrod Andy struct bufdesc_ex *ebdp; 3756e909283SNimrod Andy int nr_frags = skb_shinfo(skb)->nr_frags; 3764d494cdcSFugang Duan unsigned short queue = skb_get_queue_mapping(skb); 3776e909283SNimrod Andy int frag, frag_len; 3786e909283SNimrod Andy unsigned short status; 3796e909283SNimrod Andy unsigned int estatus = 0; 3806e909283SNimrod Andy skb_frag_t *this_frag; 3816e909283SNimrod Andy unsigned int index; 3826e909283SNimrod Andy void *bufaddr; 383d6bf3143SRussell King dma_addr_t addr; 3846e909283SNimrod Andy int i; 3856e909283SNimrod Andy 3866e909283SNimrod Andy for (frag = 0; frag < nr_frags; frag++) { 3876e909283SNimrod Andy this_frag = &skb_shinfo(skb)->frags[frag]; 3884d494cdcSFugang Duan bdp = fec_enet_get_nextdesc(bdp, fep, queue); 3896e909283SNimrod Andy ebdp = (struct bufdesc_ex *)bdp; 3906e909283SNimrod Andy 3916e909283SNimrod Andy status = bdp->cbd_sc; 3926e909283SNimrod Andy status &= ~BD_ENET_TX_STATS; 3936e909283SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 3946e909283SNimrod Andy frag_len = skb_shinfo(skb)->frags[frag].size; 3956e909283SNimrod Andy 3966e909283SNimrod Andy /* Handle the last BD specially */ 3976e909283SNimrod Andy if (frag == nr_frags - 1) { 3986e909283SNimrod Andy status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); 3996e909283SNimrod Andy if (fep->bufdesc_ex) { 4006e909283SNimrod Andy estatus |= BD_ENET_TX_INT; 4016e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & 4026e909283SNimrod Andy SKBTX_HW_TSTAMP && fep->hwts_tx_en)) 4036e909283SNimrod Andy estatus |= BD_ENET_TX_TS; 4046e909283SNimrod Andy } 4056e909283SNimrod Andy } 4066e909283SNimrod Andy 4076e909283SNimrod Andy if (fep->bufdesc_ex) { 4086b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 409befe8213SNimrod Andy estatus |= FEC_TX_BD_FTYPE(queue); 4106e909283SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 4116e909283SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 4126e909283SNimrod Andy ebdp->cbd_bdu = 0; 4136e909283SNimrod Andy ebdp->cbd_esc = estatus; 4146e909283SNimrod Andy } 4156e909283SNimrod Andy 4166e909283SNimrod Andy bufaddr = page_address(this_frag->page.p) + this_frag->page_offset; 4176e909283SNimrod Andy 4184d494cdcSFugang Duan index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep); 41941ef84ceSFugang Duan if (((unsigned long) bufaddr) & fep->tx_align || 4206b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 4214d494cdcSFugang Duan memcpy(txq->tx_bounce[index], bufaddr, frag_len); 4224d494cdcSFugang Duan bufaddr = txq->tx_bounce[index]; 4236e909283SNimrod Andy 4246b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 4256e909283SNimrod Andy swap_buffer(bufaddr, frag_len); 4266e909283SNimrod Andy } 4276e909283SNimrod Andy 428d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, bufaddr, frag_len, 429d6bf3143SRussell King DMA_TO_DEVICE); 430d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 4316e909283SNimrod Andy dev_kfree_skb_any(skb); 4326e909283SNimrod Andy if (net_ratelimit()) 4336e909283SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 4346e909283SNimrod Andy goto dma_mapping_error; 4356e909283SNimrod Andy } 4366e909283SNimrod Andy 437d6bf3143SRussell King bdp->cbd_bufaddr = addr; 4386e909283SNimrod Andy bdp->cbd_datlen = frag_len; 4396e909283SNimrod Andy bdp->cbd_sc = status; 4406e909283SNimrod Andy } 4416e909283SNimrod Andy 442c4bc44c6SKevin Hao return bdp; 4436e909283SNimrod Andy dma_mapping_error: 4444d494cdcSFugang Duan bdp = txq->cur_tx; 4456e909283SNimrod Andy for (i = 0; i < frag; i++) { 4464d494cdcSFugang Duan bdp = fec_enet_get_nextdesc(bdp, fep, queue); 4476e909283SNimrod Andy dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, 4486e909283SNimrod Andy bdp->cbd_datlen, DMA_TO_DEVICE); 4496e909283SNimrod Andy } 450c4bc44c6SKevin Hao return ERR_PTR(-ENOMEM); 4516e909283SNimrod Andy } 4526e909283SNimrod Andy 4534d494cdcSFugang Duan static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq, 4544d494cdcSFugang Duan struct sk_buff *skb, struct net_device *ndev) 4556e909283SNimrod Andy { 4566e909283SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 4576e909283SNimrod Andy int nr_frags = skb_shinfo(skb)->nr_frags; 4586e909283SNimrod Andy struct bufdesc *bdp, *last_bdp; 4596e909283SNimrod Andy void *bufaddr; 460d6bf3143SRussell King dma_addr_t addr; 4616e909283SNimrod Andy unsigned short status; 4626e909283SNimrod Andy unsigned short buflen; 4634d494cdcSFugang Duan unsigned short queue; 4646e909283SNimrod Andy unsigned int estatus = 0; 4656e909283SNimrod Andy unsigned int index; 46679f33912SNimrod Andy int entries_free; 4676e909283SNimrod Andy 4684d494cdcSFugang Duan entries_free = fec_enet_get_free_txdesc_num(fep, txq); 46979f33912SNimrod Andy if (entries_free < MAX_SKB_FRAGS + 1) { 47079f33912SNimrod Andy dev_kfree_skb_any(skb); 47179f33912SNimrod Andy if (net_ratelimit()) 47279f33912SNimrod Andy netdev_err(ndev, "NOT enough BD for SG!\n"); 47379f33912SNimrod Andy return NETDEV_TX_OK; 47479f33912SNimrod Andy } 47579f33912SNimrod Andy 4766e909283SNimrod Andy /* Protocol checksum off-load for TCP and UDP. */ 4776e909283SNimrod Andy if (fec_enet_clear_csum(skb, ndev)) { 4786e909283SNimrod Andy dev_kfree_skb_any(skb); 4796e909283SNimrod Andy return NETDEV_TX_OK; 4806e909283SNimrod Andy } 4816e909283SNimrod Andy 4826e909283SNimrod Andy /* Fill in a Tx ring entry */ 4834d494cdcSFugang Duan bdp = txq->cur_tx; 484c4bc44c6SKevin Hao last_bdp = bdp; 4856e909283SNimrod Andy status = bdp->cbd_sc; 4866e909283SNimrod Andy status &= ~BD_ENET_TX_STATS; 4876e909283SNimrod Andy 4886e909283SNimrod Andy /* Set buffer length and buffer pointer */ 4896e909283SNimrod Andy bufaddr = skb->data; 4906e909283SNimrod Andy buflen = skb_headlen(skb); 4916e909283SNimrod Andy 4924d494cdcSFugang Duan queue = skb_get_queue_mapping(skb); 4934d494cdcSFugang Duan index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep); 49441ef84ceSFugang Duan if (((unsigned long) bufaddr) & fep->tx_align || 4956b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 4964d494cdcSFugang Duan memcpy(txq->tx_bounce[index], skb->data, buflen); 4974d494cdcSFugang Duan bufaddr = txq->tx_bounce[index]; 4986e909283SNimrod Andy 4996b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 5006e909283SNimrod Andy swap_buffer(bufaddr, buflen); 5016e909283SNimrod Andy } 5026e909283SNimrod Andy 503d6bf3143SRussell King /* Push the data cache so the CPM does not get stale memory data. */ 504d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, bufaddr, buflen, DMA_TO_DEVICE); 505d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 5066e909283SNimrod Andy dev_kfree_skb_any(skb); 5076e909283SNimrod Andy if (net_ratelimit()) 5086e909283SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 5096e909283SNimrod Andy return NETDEV_TX_OK; 5106e909283SNimrod Andy } 5116e909283SNimrod Andy 5126e909283SNimrod Andy if (nr_frags) { 513c4bc44c6SKevin Hao last_bdp = fec_enet_txq_submit_frag_skb(txq, skb, ndev); 514c4bc44c6SKevin Hao if (IS_ERR(last_bdp)) 515c4bc44c6SKevin Hao return NETDEV_TX_OK; 5166e909283SNimrod Andy } else { 5176e909283SNimrod Andy status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); 5186e909283SNimrod Andy if (fep->bufdesc_ex) { 5196e909283SNimrod Andy estatus = BD_ENET_TX_INT; 5206e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & 5216e909283SNimrod Andy SKBTX_HW_TSTAMP && fep->hwts_tx_en)) 5226e909283SNimrod Andy estatus |= BD_ENET_TX_TS; 5236e909283SNimrod Andy } 5246e909283SNimrod Andy } 5256e909283SNimrod Andy 5266e909283SNimrod Andy if (fep->bufdesc_ex) { 5276e909283SNimrod Andy 5286e909283SNimrod Andy struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 5296e909283SNimrod Andy 5306e909283SNimrod Andy if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && 5316e909283SNimrod Andy fep->hwts_tx_en)) 5326e909283SNimrod Andy skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; 5336e909283SNimrod Andy 5346b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 535befe8213SNimrod Andy estatus |= FEC_TX_BD_FTYPE(queue); 536befe8213SNimrod Andy 5376e909283SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 5386e909283SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 5396e909283SNimrod Andy 5406e909283SNimrod Andy ebdp->cbd_bdu = 0; 5416e909283SNimrod Andy ebdp->cbd_esc = estatus; 5426e909283SNimrod Andy } 5436e909283SNimrod Andy 5444d494cdcSFugang Duan index = fec_enet_get_bd_index(txq->tx_bd_base, last_bdp, fep); 5456e909283SNimrod Andy /* Save skb pointer */ 5464d494cdcSFugang Duan txq->tx_skbuff[index] = skb; 5476e909283SNimrod Andy 5486e909283SNimrod Andy bdp->cbd_datlen = buflen; 549d6bf3143SRussell King bdp->cbd_bufaddr = addr; 5506e909283SNimrod Andy 5516e909283SNimrod Andy /* Send it on its way. Tell FEC it's ready, interrupt when done, 5526e909283SNimrod Andy * it's the last BD of the frame, and to put the CRC on the end. 5536e909283SNimrod Andy */ 5546e909283SNimrod Andy status |= (BD_ENET_TX_READY | BD_ENET_TX_TC); 5556e909283SNimrod Andy bdp->cbd_sc = status; 5566e909283SNimrod Andy 557793fc096SFrank Li /* If this was the last BD in the ring, start at the beginning again. */ 5584d494cdcSFugang Duan bdp = fec_enet_get_nextdesc(last_bdp, fep, queue); 559793fc096SFrank Li 5607a2a8451SEric Dumazet skb_tx_timestamp(skb); 5617a2a8451SEric Dumazet 562c4bc44c6SKevin Hao /* Make sure the update to bdp and tx_skbuff are performed before 563c4bc44c6SKevin Hao * cur_tx. 564c4bc44c6SKevin Hao */ 565c4bc44c6SKevin Hao wmb(); 5664d494cdcSFugang Duan txq->cur_tx = bdp; 567793fc096SFrank Li 568793fc096SFrank Li /* Trigger transmission start */ 5694d494cdcSFugang Duan writel(0, fep->hwp + FEC_X_DES_ACTIVE(queue)); 570793fc096SFrank Li 5716e909283SNimrod Andy return 0; 572793fc096SFrank Li } 573793fc096SFrank Li 57479f33912SNimrod Andy static int 5754d494cdcSFugang Duan fec_enet_txq_put_data_tso(struct fec_enet_priv_tx_q *txq, struct sk_buff *skb, 5764d494cdcSFugang Duan struct net_device *ndev, 57779f33912SNimrod Andy struct bufdesc *bdp, int index, char *data, 57879f33912SNimrod Andy int size, bool last_tcp, bool is_last) 57979f33912SNimrod Andy { 58079f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 58161cd2ebbSFabian Frederick struct bufdesc_ex *ebdp = container_of(bdp, struct bufdesc_ex, desc); 582befe8213SNimrod Andy unsigned short queue = skb_get_queue_mapping(skb); 58379f33912SNimrod Andy unsigned short status; 58479f33912SNimrod Andy unsigned int estatus = 0; 585d6bf3143SRussell King dma_addr_t addr; 58679f33912SNimrod Andy 58779f33912SNimrod Andy status = bdp->cbd_sc; 58879f33912SNimrod Andy status &= ~BD_ENET_TX_STATS; 58979f33912SNimrod Andy 59079f33912SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 59179f33912SNimrod Andy 59241ef84ceSFugang Duan if (((unsigned long) data) & fep->tx_align || 5936b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 5944d494cdcSFugang Duan memcpy(txq->tx_bounce[index], data, size); 5954d494cdcSFugang Duan data = txq->tx_bounce[index]; 59679f33912SNimrod Andy 5976b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 59879f33912SNimrod Andy swap_buffer(data, size); 59979f33912SNimrod Andy } 60079f33912SNimrod Andy 601d6bf3143SRussell King addr = dma_map_single(&fep->pdev->dev, data, size, DMA_TO_DEVICE); 602d6bf3143SRussell King if (dma_mapping_error(&fep->pdev->dev, addr)) { 60379f33912SNimrod Andy dev_kfree_skb_any(skb); 60479f33912SNimrod Andy if (net_ratelimit()) 60579f33912SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 60679f33912SNimrod Andy return NETDEV_TX_BUSY; 60779f33912SNimrod Andy } 60879f33912SNimrod Andy 609d6bf3143SRussell King bdp->cbd_datlen = size; 610d6bf3143SRussell King bdp->cbd_bufaddr = addr; 611d6bf3143SRussell King 61279f33912SNimrod Andy if (fep->bufdesc_ex) { 6136b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 614befe8213SNimrod Andy estatus |= FEC_TX_BD_FTYPE(queue); 61579f33912SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 61679f33912SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 61779f33912SNimrod Andy ebdp->cbd_bdu = 0; 61879f33912SNimrod Andy ebdp->cbd_esc = estatus; 61979f33912SNimrod Andy } 62079f33912SNimrod Andy 62179f33912SNimrod Andy /* Handle the last BD specially */ 62279f33912SNimrod Andy if (last_tcp) 62379f33912SNimrod Andy status |= (BD_ENET_TX_LAST | BD_ENET_TX_TC); 62479f33912SNimrod Andy if (is_last) { 62579f33912SNimrod Andy status |= BD_ENET_TX_INTR; 62679f33912SNimrod Andy if (fep->bufdesc_ex) 62779f33912SNimrod Andy ebdp->cbd_esc |= BD_ENET_TX_INT; 62879f33912SNimrod Andy } 62979f33912SNimrod Andy 63079f33912SNimrod Andy bdp->cbd_sc = status; 63179f33912SNimrod Andy 63279f33912SNimrod Andy return 0; 63379f33912SNimrod Andy } 63479f33912SNimrod Andy 63579f33912SNimrod Andy static int 6364d494cdcSFugang Duan fec_enet_txq_put_hdr_tso(struct fec_enet_priv_tx_q *txq, 6374d494cdcSFugang Duan struct sk_buff *skb, struct net_device *ndev, 63879f33912SNimrod Andy struct bufdesc *bdp, int index) 63979f33912SNimrod Andy { 64079f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 64179f33912SNimrod Andy int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); 64261cd2ebbSFabian Frederick struct bufdesc_ex *ebdp = container_of(bdp, struct bufdesc_ex, desc); 643befe8213SNimrod Andy unsigned short queue = skb_get_queue_mapping(skb); 64479f33912SNimrod Andy void *bufaddr; 64579f33912SNimrod Andy unsigned long dmabuf; 64679f33912SNimrod Andy unsigned short status; 64779f33912SNimrod Andy unsigned int estatus = 0; 64879f33912SNimrod Andy 64979f33912SNimrod Andy status = bdp->cbd_sc; 65079f33912SNimrod Andy status &= ~BD_ENET_TX_STATS; 65179f33912SNimrod Andy status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 65279f33912SNimrod Andy 6534d494cdcSFugang Duan bufaddr = txq->tso_hdrs + index * TSO_HEADER_SIZE; 6544d494cdcSFugang Duan dmabuf = txq->tso_hdrs_dma + index * TSO_HEADER_SIZE; 65541ef84ceSFugang Duan if (((unsigned long)bufaddr) & fep->tx_align || 6566b7e4008SLothar Waßmann fep->quirks & FEC_QUIRK_SWAP_FRAME) { 6574d494cdcSFugang Duan memcpy(txq->tx_bounce[index], skb->data, hdr_len); 6584d494cdcSFugang Duan bufaddr = txq->tx_bounce[index]; 65979f33912SNimrod Andy 6606b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 66179f33912SNimrod Andy swap_buffer(bufaddr, hdr_len); 66279f33912SNimrod Andy 66379f33912SNimrod Andy dmabuf = dma_map_single(&fep->pdev->dev, bufaddr, 66479f33912SNimrod Andy hdr_len, DMA_TO_DEVICE); 66579f33912SNimrod Andy if (dma_mapping_error(&fep->pdev->dev, dmabuf)) { 66679f33912SNimrod Andy dev_kfree_skb_any(skb); 66779f33912SNimrod Andy if (net_ratelimit()) 66879f33912SNimrod Andy netdev_err(ndev, "Tx DMA memory map failed\n"); 66979f33912SNimrod Andy return NETDEV_TX_BUSY; 67079f33912SNimrod Andy } 67179f33912SNimrod Andy } 67279f33912SNimrod Andy 67379f33912SNimrod Andy bdp->cbd_bufaddr = dmabuf; 67479f33912SNimrod Andy bdp->cbd_datlen = hdr_len; 67579f33912SNimrod Andy 67679f33912SNimrod Andy if (fep->bufdesc_ex) { 6776b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) 678befe8213SNimrod Andy estatus |= FEC_TX_BD_FTYPE(queue); 67979f33912SNimrod Andy if (skb->ip_summed == CHECKSUM_PARTIAL) 68079f33912SNimrod Andy estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 68179f33912SNimrod Andy ebdp->cbd_bdu = 0; 68279f33912SNimrod Andy ebdp->cbd_esc = estatus; 68379f33912SNimrod Andy } 68479f33912SNimrod Andy 68579f33912SNimrod Andy bdp->cbd_sc = status; 68679f33912SNimrod Andy 68779f33912SNimrod Andy return 0; 68879f33912SNimrod Andy } 68979f33912SNimrod Andy 6904d494cdcSFugang Duan static int fec_enet_txq_submit_tso(struct fec_enet_priv_tx_q *txq, 6914d494cdcSFugang Duan struct sk_buff *skb, 6924d494cdcSFugang Duan struct net_device *ndev) 69379f33912SNimrod Andy { 69479f33912SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 69579f33912SNimrod Andy int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); 69679f33912SNimrod Andy int total_len, data_left; 6974d494cdcSFugang Duan struct bufdesc *bdp = txq->cur_tx; 6984d494cdcSFugang Duan unsigned short queue = skb_get_queue_mapping(skb); 69979f33912SNimrod Andy struct tso_t tso; 70079f33912SNimrod Andy unsigned int index = 0; 70179f33912SNimrod Andy int ret; 70279f33912SNimrod Andy 7034d494cdcSFugang Duan if (tso_count_descs(skb) >= fec_enet_get_free_txdesc_num(fep, txq)) { 70479f33912SNimrod Andy dev_kfree_skb_any(skb); 70579f33912SNimrod Andy if (net_ratelimit()) 70679f33912SNimrod Andy netdev_err(ndev, "NOT enough BD for TSO!\n"); 70779f33912SNimrod Andy return NETDEV_TX_OK; 70879f33912SNimrod Andy } 70979f33912SNimrod Andy 71079f33912SNimrod Andy /* Protocol checksum off-load for TCP and UDP. */ 71179f33912SNimrod Andy if (fec_enet_clear_csum(skb, ndev)) { 71279f33912SNimrod Andy dev_kfree_skb_any(skb); 71379f33912SNimrod Andy return NETDEV_TX_OK; 71479f33912SNimrod Andy } 71579f33912SNimrod Andy 71679f33912SNimrod Andy /* Initialize the TSO handler, and prepare the first payload */ 71779f33912SNimrod Andy tso_start(skb, &tso); 71879f33912SNimrod Andy 71979f33912SNimrod Andy total_len = skb->len - hdr_len; 72079f33912SNimrod Andy while (total_len > 0) { 72179f33912SNimrod Andy char *hdr; 72279f33912SNimrod Andy 7234d494cdcSFugang Duan index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep); 72479f33912SNimrod Andy data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len); 72579f33912SNimrod Andy total_len -= data_left; 72679f33912SNimrod Andy 72779f33912SNimrod Andy /* prepare packet headers: MAC + IP + TCP */ 7284d494cdcSFugang Duan hdr = txq->tso_hdrs + index * TSO_HEADER_SIZE; 72979f33912SNimrod Andy tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0); 7304d494cdcSFugang Duan ret = fec_enet_txq_put_hdr_tso(txq, skb, ndev, bdp, index); 73179f33912SNimrod Andy if (ret) 73279f33912SNimrod Andy goto err_release; 73379f33912SNimrod Andy 73479f33912SNimrod Andy while (data_left > 0) { 73579f33912SNimrod Andy int size; 73679f33912SNimrod Andy 73779f33912SNimrod Andy size = min_t(int, tso.size, data_left); 7384d494cdcSFugang Duan bdp = fec_enet_get_nextdesc(bdp, fep, queue); 7394d494cdcSFugang Duan index = fec_enet_get_bd_index(txq->tx_bd_base, 7404d494cdcSFugang Duan bdp, fep); 7414d494cdcSFugang Duan ret = fec_enet_txq_put_data_tso(txq, skb, ndev, 7424d494cdcSFugang Duan bdp, index, 7434d494cdcSFugang Duan tso.data, size, 7444d494cdcSFugang Duan size == data_left, 74579f33912SNimrod Andy total_len == 0); 74679f33912SNimrod Andy if (ret) 74779f33912SNimrod Andy goto err_release; 74879f33912SNimrod Andy 74979f33912SNimrod Andy data_left -= size; 75079f33912SNimrod Andy tso_build_data(skb, &tso, size); 75179f33912SNimrod Andy } 75279f33912SNimrod Andy 7534d494cdcSFugang Duan bdp = fec_enet_get_nextdesc(bdp, fep, queue); 75479f33912SNimrod Andy } 75579f33912SNimrod Andy 75679f33912SNimrod Andy /* Save skb pointer */ 7574d494cdcSFugang Duan txq->tx_skbuff[index] = skb; 75879f33912SNimrod Andy 75979f33912SNimrod Andy skb_tx_timestamp(skb); 7604d494cdcSFugang Duan txq->cur_tx = bdp; 76179f33912SNimrod Andy 76279f33912SNimrod Andy /* Trigger transmission start */ 7636b7e4008SLothar Waßmann if (!(fep->quirks & FEC_QUIRK_ERR007885) || 76437d6017bSFugang Duan !readl(fep->hwp + FEC_X_DES_ACTIVE(queue)) || 76537d6017bSFugang Duan !readl(fep->hwp + FEC_X_DES_ACTIVE(queue)) || 76637d6017bSFugang Duan !readl(fep->hwp + FEC_X_DES_ACTIVE(queue)) || 76737d6017bSFugang Duan !readl(fep->hwp + FEC_X_DES_ACTIVE(queue))) 7684d494cdcSFugang Duan writel(0, fep->hwp + FEC_X_DES_ACTIVE(queue)); 76979f33912SNimrod Andy 77079f33912SNimrod Andy return 0; 77179f33912SNimrod Andy 77279f33912SNimrod Andy err_release: 77379f33912SNimrod Andy /* TODO: Release all used data descriptors for TSO */ 77479f33912SNimrod Andy return ret; 77579f33912SNimrod Andy } 77679f33912SNimrod Andy 77761a4427bSNimrod Andy static netdev_tx_t 77861a4427bSNimrod Andy fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) 77961a4427bSNimrod Andy { 78061a4427bSNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 7816e909283SNimrod Andy int entries_free; 7824d494cdcSFugang Duan unsigned short queue; 7834d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 7844d494cdcSFugang Duan struct netdev_queue *nq; 78561a4427bSNimrod Andy int ret; 78661a4427bSNimrod Andy 7874d494cdcSFugang Duan queue = skb_get_queue_mapping(skb); 7884d494cdcSFugang Duan txq = fep->tx_queue[queue]; 7894d494cdcSFugang Duan nq = netdev_get_tx_queue(ndev, queue); 7904d494cdcSFugang Duan 79179f33912SNimrod Andy if (skb_is_gso(skb)) 7924d494cdcSFugang Duan ret = fec_enet_txq_submit_tso(txq, skb, ndev); 79379f33912SNimrod Andy else 7944d494cdcSFugang Duan ret = fec_enet_txq_submit_skb(txq, skb, ndev); 7956e909283SNimrod Andy if (ret) 7966e909283SNimrod Andy return ret; 79761a4427bSNimrod Andy 7984d494cdcSFugang Duan entries_free = fec_enet_get_free_txdesc_num(fep, txq); 7994d494cdcSFugang Duan if (entries_free <= txq->tx_stop_threshold) 8004d494cdcSFugang Duan netif_tx_stop_queue(nq); 80161a4427bSNimrod Andy 80261a4427bSNimrod Andy return NETDEV_TX_OK; 80361a4427bSNimrod Andy } 80461a4427bSNimrod Andy 805a210576cSDavid S. Miller /* Init RX & TX buffer descriptors 806a210576cSDavid S. Miller */ 807a210576cSDavid S. Miller static void fec_enet_bd_init(struct net_device *dev) 808a210576cSDavid S. Miller { 809a210576cSDavid S. Miller struct fec_enet_private *fep = netdev_priv(dev); 8104d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 8114d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 812a210576cSDavid S. Miller struct bufdesc *bdp; 813a210576cSDavid S. Miller unsigned int i; 81459d0f746SFrank Li unsigned int q; 815a210576cSDavid S. Miller 81659d0f746SFrank Li for (q = 0; q < fep->num_rx_queues; q++) { 817a210576cSDavid S. Miller /* Initialize the receive buffer descriptors. */ 81859d0f746SFrank Li rxq = fep->rx_queue[q]; 8194d494cdcSFugang Duan bdp = rxq->rx_bd_base; 8204d494cdcSFugang Duan 8214d494cdcSFugang Duan for (i = 0; i < rxq->rx_ring_size; i++) { 822a210576cSDavid S. Miller 823a210576cSDavid S. Miller /* Initialize the BD for every fragment in the page. */ 824a210576cSDavid S. Miller if (bdp->cbd_bufaddr) 825a210576cSDavid S. Miller bdp->cbd_sc = BD_ENET_RX_EMPTY; 826a210576cSDavid S. Miller else 827a210576cSDavid S. Miller bdp->cbd_sc = 0; 82859d0f746SFrank Li bdp = fec_enet_get_nextdesc(bdp, fep, q); 829a210576cSDavid S. Miller } 830a210576cSDavid S. Miller 831a210576cSDavid S. Miller /* Set the last buffer to wrap */ 83259d0f746SFrank Li bdp = fec_enet_get_prevdesc(bdp, fep, q); 833a210576cSDavid S. Miller bdp->cbd_sc |= BD_SC_WRAP; 834a210576cSDavid S. Miller 8354d494cdcSFugang Duan rxq->cur_rx = rxq->rx_bd_base; 83659d0f746SFrank Li } 837a210576cSDavid S. Miller 83859d0f746SFrank Li for (q = 0; q < fep->num_tx_queues; q++) { 839a210576cSDavid S. Miller /* ...and the same for transmit */ 84059d0f746SFrank Li txq = fep->tx_queue[q]; 8414d494cdcSFugang Duan bdp = txq->tx_bd_base; 8424d494cdcSFugang Duan txq->cur_tx = bdp; 843a210576cSDavid S. Miller 8444d494cdcSFugang Duan for (i = 0; i < txq->tx_ring_size; i++) { 845a210576cSDavid S. Miller /* Initialize the BD for every fragment in the page. */ 846a210576cSDavid S. Miller bdp->cbd_sc = 0; 8474d494cdcSFugang Duan if (txq->tx_skbuff[i]) { 8484d494cdcSFugang Duan dev_kfree_skb_any(txq->tx_skbuff[i]); 8494d494cdcSFugang Duan txq->tx_skbuff[i] = NULL; 850a210576cSDavid S. Miller } 851a210576cSDavid S. Miller bdp->cbd_bufaddr = 0; 85259d0f746SFrank Li bdp = fec_enet_get_nextdesc(bdp, fep, q); 853a210576cSDavid S. Miller } 854a210576cSDavid S. Miller 855a210576cSDavid S. Miller /* Set the last buffer to wrap */ 85659d0f746SFrank Li bdp = fec_enet_get_prevdesc(bdp, fep, q); 857a210576cSDavid S. Miller bdp->cbd_sc |= BD_SC_WRAP; 8584d494cdcSFugang Duan txq->dirty_tx = bdp; 859a210576cSDavid S. Miller } 86059d0f746SFrank Li } 86159d0f746SFrank Li 862ce99d0d3SFrank Li static void fec_enet_active_rxring(struct net_device *ndev) 863ce99d0d3SFrank Li { 864ce99d0d3SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 865ce99d0d3SFrank Li int i; 866ce99d0d3SFrank Li 867ce99d0d3SFrank Li for (i = 0; i < fep->num_rx_queues; i++) 868ce99d0d3SFrank Li writel(0, fep->hwp + FEC_R_DES_ACTIVE(i)); 869ce99d0d3SFrank Li } 870ce99d0d3SFrank Li 87159d0f746SFrank Li static void fec_enet_enable_ring(struct net_device *ndev) 87259d0f746SFrank Li { 87359d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 87459d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 87559d0f746SFrank Li struct fec_enet_priv_rx_q *rxq; 87659d0f746SFrank Li int i; 87759d0f746SFrank Li 87859d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) { 87959d0f746SFrank Li rxq = fep->rx_queue[i]; 88059d0f746SFrank Li writel(rxq->bd_dma, fep->hwp + FEC_R_DES_START(i)); 881d543a762SNimrod Andy writel(PKT_MAXBLR_SIZE, fep->hwp + FEC_R_BUFF_SIZE(i)); 88259d0f746SFrank Li 88359d0f746SFrank Li /* enable DMA1/2 */ 88459d0f746SFrank Li if (i) 88559d0f746SFrank Li writel(RCMR_MATCHEN | RCMR_CMP(i), 88659d0f746SFrank Li fep->hwp + FEC_RCMR(i)); 88759d0f746SFrank Li } 88859d0f746SFrank Li 88959d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 89059d0f746SFrank Li txq = fep->tx_queue[i]; 89159d0f746SFrank Li writel(txq->bd_dma, fep->hwp + FEC_X_DES_START(i)); 89259d0f746SFrank Li 89359d0f746SFrank Li /* enable DMA1/2 */ 89459d0f746SFrank Li if (i) 89559d0f746SFrank Li writel(DMA_CLASS_EN | IDLE_SLOPE(i), 89659d0f746SFrank Li fep->hwp + FEC_DMA_CFG(i)); 89759d0f746SFrank Li } 89859d0f746SFrank Li } 89959d0f746SFrank Li 90059d0f746SFrank Li static void fec_enet_reset_skb(struct net_device *ndev) 90159d0f746SFrank Li { 90259d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 90359d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 90459d0f746SFrank Li int i, j; 90559d0f746SFrank Li 90659d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 90759d0f746SFrank Li txq = fep->tx_queue[i]; 90859d0f746SFrank Li 90959d0f746SFrank Li for (j = 0; j < txq->tx_ring_size; j++) { 91059d0f746SFrank Li if (txq->tx_skbuff[j]) { 91159d0f746SFrank Li dev_kfree_skb_any(txq->tx_skbuff[j]); 91259d0f746SFrank Li txq->tx_skbuff[j] = NULL; 91359d0f746SFrank Li } 91459d0f746SFrank Li } 91559d0f746SFrank Li } 91659d0f746SFrank Li } 917a210576cSDavid S. Miller 918dbc64a8eSRussell King /* 919dbc64a8eSRussell King * This function is called to start or restart the FEC during a link 920dbc64a8eSRussell King * change, transmit timeout, or to reconfigure the FEC. The network 921dbc64a8eSRussell King * packet processing for this device must be stopped before this call. 922793fc096SFrank Li */ 923793fc096SFrank Li static void 924ef83337dSRussell King fec_restart(struct net_device *ndev) 925793fc096SFrank Li { 926793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 9274c09eed9SJim Baxter u32 val; 928793fc096SFrank Li u32 temp_mac[2]; 929793fc096SFrank Li u32 rcntl = OPT_FRAME_SIZE | 0x04; 930793fc096SFrank Li u32 ecntl = 0x2; /* ETHEREN */ 931793fc096SFrank Li 932106c314cSFugang Duan /* Whack a reset. We should wait for this. 933106c314cSFugang Duan * For i.MX6SX SOC, enet use AXI bus, we use disable MAC 934106c314cSFugang Duan * instead of reset MAC itself. 935106c314cSFugang Duan */ 9366b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) { 937106c314cSFugang Duan writel(0, fep->hwp + FEC_ECNTRL); 938106c314cSFugang Duan } else { 939793fc096SFrank Li writel(1, fep->hwp + FEC_ECNTRL); 940793fc096SFrank Li udelay(10); 941106c314cSFugang Duan } 942793fc096SFrank Li 943793fc096SFrank Li /* 944793fc096SFrank Li * enet-mac reset will reset mac address registers too, 945793fc096SFrank Li * so need to reconfigure it. 946793fc096SFrank Li */ 9476b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_ENET_MAC) { 948793fc096SFrank Li memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN); 949793fc096SFrank Li writel(cpu_to_be32(temp_mac[0]), fep->hwp + FEC_ADDR_LOW); 950793fc096SFrank Li writel(cpu_to_be32(temp_mac[1]), fep->hwp + FEC_ADDR_HIGH); 951793fc096SFrank Li } 952793fc096SFrank Li 953793fc096SFrank Li /* Clear any outstanding interrupt. */ 954e17f7fecSNimrod Andy writel(0xffffffff, fep->hwp + FEC_IEVENT); 955793fc096SFrank Li 956a210576cSDavid S. Miller fec_enet_bd_init(ndev); 957a210576cSDavid S. Miller 95859d0f746SFrank Li fec_enet_enable_ring(ndev); 959793fc096SFrank Li 96059d0f746SFrank Li /* Reset tx SKB buffers. */ 96159d0f746SFrank Li fec_enet_reset_skb(ndev); 962793fc096SFrank Li 963793fc096SFrank Li /* Enable MII mode */ 964ef83337dSRussell King if (fep->full_duplex == DUPLEX_FULL) { 965793fc096SFrank Li /* FD enable */ 966793fc096SFrank Li writel(0x04, fep->hwp + FEC_X_CNTRL); 967793fc096SFrank Li } else { 968793fc096SFrank Li /* No Rcv on Xmit */ 969793fc096SFrank Li rcntl |= 0x02; 970793fc096SFrank Li writel(0x0, fep->hwp + FEC_X_CNTRL); 971793fc096SFrank Li } 972793fc096SFrank Li 973793fc096SFrank Li /* Set MII speed */ 974793fc096SFrank Li writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 975793fc096SFrank Li 976d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 97718803495SGreg Ungerer if (fep->quirks & FEC_QUIRK_HAS_RACC) { 9784c09eed9SJim Baxter /* set RX checksum */ 9794c09eed9SJim Baxter val = readl(fep->hwp + FEC_RACC); 9804c09eed9SJim Baxter if (fep->csum_flags & FLAG_RX_CSUM_ENABLED) 9814c09eed9SJim Baxter val |= FEC_RACC_OPTIONS; 9824c09eed9SJim Baxter else 9834c09eed9SJim Baxter val &= ~FEC_RACC_OPTIONS; 9844c09eed9SJim Baxter writel(val, fep->hwp + FEC_RACC); 98518803495SGreg Ungerer } 986d1391930SGuenter Roeck #endif 9874c09eed9SJim Baxter 988793fc096SFrank Li /* 989793fc096SFrank Li * The phy interface and speed need to get configured 990793fc096SFrank Li * differently on enet-mac. 991793fc096SFrank Li */ 9926b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_ENET_MAC) { 993793fc096SFrank Li /* Enable flow control and length check */ 994793fc096SFrank Li rcntl |= 0x40000000 | 0x00000020; 995793fc096SFrank Li 996793fc096SFrank Li /* RGMII, RMII or MII */ 997e813bb2bSMarkus Pargmann if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII || 998e813bb2bSMarkus Pargmann fep->phy_interface == PHY_INTERFACE_MODE_RGMII_ID || 999e813bb2bSMarkus Pargmann fep->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID || 1000e813bb2bSMarkus Pargmann fep->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID) 1001793fc096SFrank Li rcntl |= (1 << 6); 1002793fc096SFrank Li else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) 1003793fc096SFrank Li rcntl |= (1 << 8); 1004793fc096SFrank Li else 1005793fc096SFrank Li rcntl &= ~(1 << 8); 1006793fc096SFrank Li 1007793fc096SFrank Li /* 1G, 100M or 10M */ 1008793fc096SFrank Li if (fep->phy_dev) { 1009793fc096SFrank Li if (fep->phy_dev->speed == SPEED_1000) 1010793fc096SFrank Li ecntl |= (1 << 5); 1011793fc096SFrank Li else if (fep->phy_dev->speed == SPEED_100) 1012793fc096SFrank Li rcntl &= ~(1 << 9); 1013793fc096SFrank Li else 1014793fc096SFrank Li rcntl |= (1 << 9); 1015793fc096SFrank Li } 1016793fc096SFrank Li } else { 1017793fc096SFrank Li #ifdef FEC_MIIGSK_ENR 10186b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_USE_GASKET) { 1019793fc096SFrank Li u32 cfgr; 1020793fc096SFrank Li /* disable the gasket and wait */ 1021793fc096SFrank Li writel(0, fep->hwp + FEC_MIIGSK_ENR); 1022793fc096SFrank Li while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4) 1023793fc096SFrank Li udelay(1); 1024793fc096SFrank Li 1025793fc096SFrank Li /* 1026793fc096SFrank Li * configure the gasket: 1027793fc096SFrank Li * RMII, 50 MHz, no loopback, no echo 1028793fc096SFrank Li * MII, 25 MHz, no loopback, no echo 1029793fc096SFrank Li */ 1030793fc096SFrank Li cfgr = (fep->phy_interface == PHY_INTERFACE_MODE_RMII) 1031793fc096SFrank Li ? BM_MIIGSK_CFGR_RMII : BM_MIIGSK_CFGR_MII; 1032793fc096SFrank Li if (fep->phy_dev && fep->phy_dev->speed == SPEED_10) 1033793fc096SFrank Li cfgr |= BM_MIIGSK_CFGR_FRCONT_10M; 1034793fc096SFrank Li writel(cfgr, fep->hwp + FEC_MIIGSK_CFGR); 1035793fc096SFrank Li 1036793fc096SFrank Li /* re-enable the gasket */ 1037793fc096SFrank Li writel(2, fep->hwp + FEC_MIIGSK_ENR); 1038793fc096SFrank Li } 1039793fc096SFrank Li #endif 1040793fc096SFrank Li } 1041793fc096SFrank Li 1042d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 1043793fc096SFrank Li /* enable pause frame*/ 1044793fc096SFrank Li if ((fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) || 1045793fc096SFrank Li ((fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) && 1046793fc096SFrank Li fep->phy_dev && fep->phy_dev->pause)) { 1047793fc096SFrank Li rcntl |= FEC_ENET_FCE; 1048793fc096SFrank Li 10494c09eed9SJim Baxter /* set FIFO threshold parameter to reduce overrun */ 1050793fc096SFrank Li writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM); 1051793fc096SFrank Li writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL); 1052793fc096SFrank Li writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM); 1053793fc096SFrank Li writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL); 1054793fc096SFrank Li 1055793fc096SFrank Li /* OPD */ 1056793fc096SFrank Li writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD); 1057793fc096SFrank Li } else { 1058793fc096SFrank Li rcntl &= ~FEC_ENET_FCE; 1059793fc096SFrank Li } 1060d1391930SGuenter Roeck #endif /* !defined(CONFIG_M5272) */ 1061793fc096SFrank Li 1062793fc096SFrank Li writel(rcntl, fep->hwp + FEC_R_CNTRL); 1063793fc096SFrank Li 106484fe6182SStefan Wahren /* Setup multicast filter. */ 106584fe6182SStefan Wahren set_multicast_list(ndev); 106684fe6182SStefan Wahren #ifndef CONFIG_M5272 106784fe6182SStefan Wahren writel(0, fep->hwp + FEC_HASH_TABLE_HIGH); 106884fe6182SStefan Wahren writel(0, fep->hwp + FEC_HASH_TABLE_LOW); 106984fe6182SStefan Wahren #endif 107084fe6182SStefan Wahren 10716b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_ENET_MAC) { 1072793fc096SFrank Li /* enable ENET endian swap */ 1073793fc096SFrank Li ecntl |= (1 << 8); 1074793fc096SFrank Li /* enable ENET store and forward mode */ 1075793fc096SFrank Li writel(1 << 8, fep->hwp + FEC_X_WMRK); 1076793fc096SFrank Li } 1077793fc096SFrank Li 1078793fc096SFrank Li if (fep->bufdesc_ex) 1079793fc096SFrank Li ecntl |= (1 << 4); 1080793fc096SFrank Li 108138ae92dcSChris Healy #ifndef CONFIG_M5272 1082b9eef55cSJim Baxter /* Enable the MIB statistic event counters */ 1083b9eef55cSJim Baxter writel(0 << 31, fep->hwp + FEC_MIB_CTRLSTAT); 108438ae92dcSChris Healy #endif 108538ae92dcSChris Healy 1086793fc096SFrank Li /* And last, enable the transmit and receive processing */ 1087793fc096SFrank Li writel(ecntl, fep->hwp + FEC_ECNTRL); 1088ce99d0d3SFrank Li fec_enet_active_rxring(ndev); 1089793fc096SFrank Li 1090793fc096SFrank Li if (fep->bufdesc_ex) 1091793fc096SFrank Li fec_ptp_start_cyclecounter(ndev); 1092793fc096SFrank Li 1093793fc096SFrank Li /* Enable interrupts we wish to service */ 10940c5a3aefSNimrod Andy if (fep->link) 1095793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 10960c5a3aefSNimrod Andy else 10970c5a3aefSNimrod Andy writel(FEC_ENET_MII, fep->hwp + FEC_IMASK); 1098d851b47bSFugang Duan 1099d851b47bSFugang Duan /* Init the interrupt coalescing */ 1100d851b47bSFugang Duan fec_enet_itr_coal_init(ndev); 1101d851b47bSFugang Duan 1102793fc096SFrank Li } 1103793fc096SFrank Li 1104793fc096SFrank Li static void 1105793fc096SFrank Li fec_stop(struct net_device *ndev) 1106793fc096SFrank Li { 1107793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1108de40ed31SNimrod Andy struct fec_platform_data *pdata = fep->pdev->dev.platform_data; 1109793fc096SFrank Li u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8); 1110de40ed31SNimrod Andy u32 val; 1111793fc096SFrank Li 1112793fc096SFrank Li /* We cannot expect a graceful transmit stop without link !!! */ 1113793fc096SFrank Li if (fep->link) { 1114793fc096SFrank Li writel(1, fep->hwp + FEC_X_CNTRL); /* Graceful transmit stop */ 1115793fc096SFrank Li udelay(10); 1116793fc096SFrank Li if (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_GRA)) 111731b7720cSJoe Perches netdev_err(ndev, "Graceful transmit stop did not complete!\n"); 1118793fc096SFrank Li } 1119793fc096SFrank Li 1120106c314cSFugang Duan /* Whack a reset. We should wait for this. 1121106c314cSFugang Duan * For i.MX6SX SOC, enet use AXI bus, we use disable MAC 1122106c314cSFugang Duan * instead of reset MAC itself. 1123106c314cSFugang Duan */ 1124de40ed31SNimrod Andy if (!(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { 11256b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) { 1126106c314cSFugang Duan writel(0, fep->hwp + FEC_ECNTRL); 1127106c314cSFugang Duan } else { 1128793fc096SFrank Li writel(1, fep->hwp + FEC_ECNTRL); 1129793fc096SFrank Li udelay(10); 1130106c314cSFugang Duan } 1131793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 1132de40ed31SNimrod Andy } else { 1133de40ed31SNimrod Andy writel(FEC_DEFAULT_IMASK | FEC_ENET_WAKEUP, fep->hwp + FEC_IMASK); 1134de40ed31SNimrod Andy val = readl(fep->hwp + FEC_ECNTRL); 1135de40ed31SNimrod Andy val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP); 1136de40ed31SNimrod Andy writel(val, fep->hwp + FEC_ECNTRL); 1137de40ed31SNimrod Andy 1138de40ed31SNimrod Andy if (pdata && pdata->sleep_mode_enable) 1139de40ed31SNimrod Andy pdata->sleep_mode_enable(true); 1140de40ed31SNimrod Andy } 1141de40ed31SNimrod Andy writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 1142793fc096SFrank Li 1143793fc096SFrank Li /* We have to keep ENET enabled to have MII interrupt stay working */ 1144de40ed31SNimrod Andy if (fep->quirks & FEC_QUIRK_ENET_MAC && 1145de40ed31SNimrod Andy !(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { 1146793fc096SFrank Li writel(2, fep->hwp + FEC_ECNTRL); 1147793fc096SFrank Li writel(rmii_mode, fep->hwp + FEC_R_CNTRL); 1148793fc096SFrank Li } 1149793fc096SFrank Li } 1150793fc096SFrank Li 1151793fc096SFrank Li 1152793fc096SFrank Li static void 1153793fc096SFrank Li fec_timeout(struct net_device *ndev) 1154793fc096SFrank Li { 1155793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1156793fc096SFrank Li 1157344756f6SRussell King fec_dump(ndev); 1158344756f6SRussell King 1159793fc096SFrank Li ndev->stats.tx_errors++; 1160793fc096SFrank Li 116136cdc743SRussell King schedule_work(&fep->tx_timeout_work); 116254309fa6SFrank Li } 116354309fa6SFrank Li 116436cdc743SRussell King static void fec_enet_timeout_work(struct work_struct *work) 116554309fa6SFrank Li { 116654309fa6SFrank Li struct fec_enet_private *fep = 116736cdc743SRussell King container_of(work, struct fec_enet_private, tx_timeout_work); 11688ce5624fSRussell King struct net_device *ndev = fep->netdev; 116954309fa6SFrank Li 1170da1774e5SRussell King rtnl_lock(); 11718ce5624fSRussell King if (netif_device_present(ndev) || netif_running(ndev)) { 1172dbc64a8eSRussell King napi_disable(&fep->napi); 1173dbc64a8eSRussell King netif_tx_lock_bh(ndev); 1174ef83337dSRussell King fec_restart(ndev); 11758ce5624fSRussell King netif_wake_queue(ndev); 11766af42d42SRussell King netif_tx_unlock_bh(ndev); 1177dbc64a8eSRussell King napi_enable(&fep->napi); 11788ce5624fSRussell King } 1179da1774e5SRussell King rtnl_unlock(); 118054309fa6SFrank Li } 1181793fc096SFrank Li 1182793fc096SFrank Li static void 1183bfd4ecddSRussell King fec_enet_hwtstamp(struct fec_enet_private *fep, unsigned ts, 1184bfd4ecddSRussell King struct skb_shared_hwtstamps *hwtstamps) 1185bfd4ecddSRussell King { 1186bfd4ecddSRussell King unsigned long flags; 1187bfd4ecddSRussell King u64 ns; 1188bfd4ecddSRussell King 1189bfd4ecddSRussell King spin_lock_irqsave(&fep->tmreg_lock, flags); 1190bfd4ecddSRussell King ns = timecounter_cyc2time(&fep->tc, ts); 1191bfd4ecddSRussell King spin_unlock_irqrestore(&fep->tmreg_lock, flags); 1192bfd4ecddSRussell King 1193bfd4ecddSRussell King memset(hwtstamps, 0, sizeof(*hwtstamps)); 1194bfd4ecddSRussell King hwtstamps->hwtstamp = ns_to_ktime(ns); 1195bfd4ecddSRussell King } 1196bfd4ecddSRussell King 1197bfd4ecddSRussell King static void 11984d494cdcSFugang Duan fec_enet_tx_queue(struct net_device *ndev, u16 queue_id) 1199793fc096SFrank Li { 1200793fc096SFrank Li struct fec_enet_private *fep; 1201a2fe37b6SFabio Estevam struct bufdesc *bdp; 1202793fc096SFrank Li unsigned short status; 1203793fc096SFrank Li struct sk_buff *skb; 12044d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 12054d494cdcSFugang Duan struct netdev_queue *nq; 1206793fc096SFrank Li int index = 0; 120779f33912SNimrod Andy int entries_free; 1208793fc096SFrank Li 1209793fc096SFrank Li fep = netdev_priv(ndev); 12104d494cdcSFugang Duan 12114d494cdcSFugang Duan queue_id = FEC_ENET_GET_QUQUE(queue_id); 12124d494cdcSFugang Duan 12134d494cdcSFugang Duan txq = fep->tx_queue[queue_id]; 12144d494cdcSFugang Duan /* get next bdp of dirty_tx */ 12154d494cdcSFugang Duan nq = netdev_get_tx_queue(ndev, queue_id); 12164d494cdcSFugang Duan bdp = txq->dirty_tx; 1217793fc096SFrank Li 1218793fc096SFrank Li /* get next bdp of dirty_tx */ 12194d494cdcSFugang Duan bdp = fec_enet_get_nextdesc(bdp, fep, queue_id); 1220793fc096SFrank Li 1221c4bc44c6SKevin Hao while (bdp != READ_ONCE(txq->cur_tx)) { 1222c4bc44c6SKevin Hao /* Order the load of cur_tx and cbd_sc */ 1223c4bc44c6SKevin Hao rmb(); 1224c4bc44c6SKevin Hao status = READ_ONCE(bdp->cbd_sc); 1225c4bc44c6SKevin Hao if (status & BD_ENET_TX_READY) 1226793fc096SFrank Li break; 1227793fc096SFrank Li 1228a2fe37b6SFabio Estevam index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep); 12292b995f63SNimrod Andy 1230a2fe37b6SFabio Estevam skb = txq->tx_skbuff[index]; 1231a2fe37b6SFabio Estevam txq->tx_skbuff[index] = NULL; 12324d494cdcSFugang Duan if (!IS_TSO_HEADER(txq, bdp->cbd_bufaddr)) 123379f33912SNimrod Andy dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, 123479f33912SNimrod Andy bdp->cbd_datlen, DMA_TO_DEVICE); 12352488a54eSSebastian Siewior bdp->cbd_bufaddr = 0; 1236a2fe37b6SFabio Estevam if (!skb) { 12374d494cdcSFugang Duan bdp = fec_enet_get_nextdesc(bdp, fep, queue_id); 1238a2fe37b6SFabio Estevam continue; 12396e909283SNimrod Andy } 1240793fc096SFrank Li 1241793fc096SFrank Li /* Check for errors. */ 1242793fc096SFrank Li if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC | 1243793fc096SFrank Li BD_ENET_TX_RL | BD_ENET_TX_UN | 1244793fc096SFrank Li BD_ENET_TX_CSL)) { 1245793fc096SFrank Li ndev->stats.tx_errors++; 1246793fc096SFrank Li if (status & BD_ENET_TX_HB) /* No heartbeat */ 1247793fc096SFrank Li ndev->stats.tx_heartbeat_errors++; 1248793fc096SFrank Li if (status & BD_ENET_TX_LC) /* Late collision */ 1249793fc096SFrank Li ndev->stats.tx_window_errors++; 1250793fc096SFrank Li if (status & BD_ENET_TX_RL) /* Retrans limit */ 1251793fc096SFrank Li ndev->stats.tx_aborted_errors++; 1252793fc096SFrank Li if (status & BD_ENET_TX_UN) /* Underrun */ 1253793fc096SFrank Li ndev->stats.tx_fifo_errors++; 1254793fc096SFrank Li if (status & BD_ENET_TX_CSL) /* Carrier lost */ 1255793fc096SFrank Li ndev->stats.tx_carrier_errors++; 1256793fc096SFrank Li } else { 1257793fc096SFrank Li ndev->stats.tx_packets++; 12586e909283SNimrod Andy ndev->stats.tx_bytes += skb->len; 1259793fc096SFrank Li } 1260793fc096SFrank Li 1261793fc096SFrank Li if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) && 1262793fc096SFrank Li fep->bufdesc_ex) { 1263793fc096SFrank Li struct skb_shared_hwtstamps shhwtstamps; 1264793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 1265793fc096SFrank Li 1266bfd4ecddSRussell King fec_enet_hwtstamp(fep, ebdp->ts, &shhwtstamps); 1267793fc096SFrank Li skb_tstamp_tx(skb, &shhwtstamps); 1268793fc096SFrank Li } 1269793fc096SFrank Li 1270793fc096SFrank Li /* Deferred means some collisions occurred during transmit, 1271793fc096SFrank Li * but we eventually sent the packet OK. 1272793fc096SFrank Li */ 1273793fc096SFrank Li if (status & BD_ENET_TX_DEF) 1274793fc096SFrank Li ndev->stats.collisions++; 1275793fc096SFrank Li 1276793fc096SFrank Li /* Free the sk buffer associated with this last transmit */ 1277793fc096SFrank Li dev_kfree_skb_any(skb); 1278793fc096SFrank Li 1279c4bc44c6SKevin Hao /* Make sure the update to bdp and tx_skbuff are performed 1280c4bc44c6SKevin Hao * before dirty_tx 1281c4bc44c6SKevin Hao */ 1282c4bc44c6SKevin Hao wmb(); 12834d494cdcSFugang Duan txq->dirty_tx = bdp; 1284793fc096SFrank Li 1285793fc096SFrank Li /* Update pointer to next buffer descriptor to be transmitted */ 12864d494cdcSFugang Duan bdp = fec_enet_get_nextdesc(bdp, fep, queue_id); 1287793fc096SFrank Li 1288793fc096SFrank Li /* Since we have freed up a buffer, the ring is no longer full 1289793fc096SFrank Li */ 129079f33912SNimrod Andy if (netif_queue_stopped(ndev)) { 12914d494cdcSFugang Duan entries_free = fec_enet_get_free_txdesc_num(fep, txq); 12924d494cdcSFugang Duan if (entries_free >= txq->tx_wake_threshold) 12934d494cdcSFugang Duan netif_tx_wake_queue(nq); 1294793fc096SFrank Li } 129579f33912SNimrod Andy } 1296ccea2968SRussell King 1297ccea2968SRussell King /* ERR006538: Keep the transmitter going */ 12984d494cdcSFugang Duan if (bdp != txq->cur_tx && 12994d494cdcSFugang Duan readl(fep->hwp + FEC_X_DES_ACTIVE(queue_id)) == 0) 13004d494cdcSFugang Duan writel(0, fep->hwp + FEC_X_DES_ACTIVE(queue_id)); 13014d494cdcSFugang Duan } 13024d494cdcSFugang Duan 13034d494cdcSFugang Duan static void 13044d494cdcSFugang Duan fec_enet_tx(struct net_device *ndev) 13054d494cdcSFugang Duan { 13064d494cdcSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 13074d494cdcSFugang Duan u16 queue_id; 13084d494cdcSFugang Duan /* First process class A queue, then Class B and Best Effort queue */ 13094d494cdcSFugang Duan for_each_set_bit(queue_id, &fep->work_tx, FEC_ENET_MAX_TX_QS) { 13104d494cdcSFugang Duan clear_bit(queue_id, &fep->work_tx); 13114d494cdcSFugang Duan fec_enet_tx_queue(ndev, queue_id); 13124d494cdcSFugang Duan } 13134d494cdcSFugang Duan return; 1314793fc096SFrank Li } 1315793fc096SFrank Li 13161b7bde6dSNimrod Andy static int 13171b7bde6dSNimrod Andy fec_enet_new_rxbdp(struct net_device *ndev, struct bufdesc *bdp, struct sk_buff *skb) 13181b7bde6dSNimrod Andy { 13191b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 13201b7bde6dSNimrod Andy int off; 13211b7bde6dSNimrod Andy 13221b7bde6dSNimrod Andy off = ((unsigned long)skb->data) & fep->rx_align; 13231b7bde6dSNimrod Andy if (off) 13241b7bde6dSNimrod Andy skb_reserve(skb, fep->rx_align + 1 - off); 13251b7bde6dSNimrod Andy 13261b7bde6dSNimrod Andy bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, skb->data, 13271b7bde6dSNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 13281b7bde6dSNimrod Andy DMA_FROM_DEVICE); 13291b7bde6dSNimrod Andy if (dma_mapping_error(&fep->pdev->dev, bdp->cbd_bufaddr)) { 13301b7bde6dSNimrod Andy if (net_ratelimit()) 13311b7bde6dSNimrod Andy netdev_err(ndev, "Rx DMA memory map failed\n"); 13321b7bde6dSNimrod Andy return -ENOMEM; 13331b7bde6dSNimrod Andy } 13341b7bde6dSNimrod Andy 13351b7bde6dSNimrod Andy return 0; 13361b7bde6dSNimrod Andy } 13371b7bde6dSNimrod Andy 13381b7bde6dSNimrod Andy static bool fec_enet_copybreak(struct net_device *ndev, struct sk_buff **skb, 13391310b544SLothar Waßmann struct bufdesc *bdp, u32 length, bool swap) 13401b7bde6dSNimrod Andy { 13411b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 13421b7bde6dSNimrod Andy struct sk_buff *new_skb; 13431b7bde6dSNimrod Andy 13441b7bde6dSNimrod Andy if (length > fep->rx_copybreak) 13451b7bde6dSNimrod Andy return false; 13461b7bde6dSNimrod Andy 13471b7bde6dSNimrod Andy new_skb = netdev_alloc_skb(ndev, length); 13481b7bde6dSNimrod Andy if (!new_skb) 13491b7bde6dSNimrod Andy return false; 13501b7bde6dSNimrod Andy 13511b7bde6dSNimrod Andy dma_sync_single_for_cpu(&fep->pdev->dev, bdp->cbd_bufaddr, 13521b7bde6dSNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 13531b7bde6dSNimrod Andy DMA_FROM_DEVICE); 13541310b544SLothar Waßmann if (!swap) 13551b7bde6dSNimrod Andy memcpy(new_skb->data, (*skb)->data, length); 13561310b544SLothar Waßmann else 13571310b544SLothar Waßmann swap_buffer2(new_skb->data, (*skb)->data, length); 13581b7bde6dSNimrod Andy *skb = new_skb; 13591b7bde6dSNimrod Andy 13601b7bde6dSNimrod Andy return true; 13611b7bde6dSNimrod Andy } 13621b7bde6dSNimrod Andy 1363793fc096SFrank Li /* During a receive, the cur_rx points to the current incoming buffer. 1364793fc096SFrank Li * When we update through the ring, if the next incoming buffer has 1365793fc096SFrank Li * not been given to the system, we just set the empty indicator, 1366793fc096SFrank Li * effectively tossing the packet. 1367793fc096SFrank Li */ 1368793fc096SFrank Li static int 13694d494cdcSFugang Duan fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) 1370793fc096SFrank Li { 1371793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 13724d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 1373793fc096SFrank Li struct bufdesc *bdp; 1374793fc096SFrank Li unsigned short status; 13751b7bde6dSNimrod Andy struct sk_buff *skb_new = NULL; 1376793fc096SFrank Li struct sk_buff *skb; 1377793fc096SFrank Li ushort pkt_len; 1378793fc096SFrank Li __u8 *data; 1379793fc096SFrank Li int pkt_received = 0; 1380cdffcf1bSJim Baxter struct bufdesc_ex *ebdp = NULL; 1381cdffcf1bSJim Baxter bool vlan_packet_rcvd = false; 1382cdffcf1bSJim Baxter u16 vlan_tag; 1383d842a31fSDuan Fugang-B38611 int index = 0; 13841b7bde6dSNimrod Andy bool is_copybreak; 13856b7e4008SLothar Waßmann bool need_swap = fep->quirks & FEC_QUIRK_SWAP_FRAME; 1386793fc096SFrank Li 1387793fc096SFrank Li #ifdef CONFIG_M532x 1388793fc096SFrank Li flush_cache_all(); 1389793fc096SFrank Li #endif 13904d494cdcSFugang Duan queue_id = FEC_ENET_GET_QUQUE(queue_id); 13914d494cdcSFugang Duan rxq = fep->rx_queue[queue_id]; 1392793fc096SFrank Li 1393793fc096SFrank Li /* First, grab all of the stats for the incoming packet. 1394793fc096SFrank Li * These get messed up if we get called due to a busy condition. 1395793fc096SFrank Li */ 13964d494cdcSFugang Duan bdp = rxq->cur_rx; 1397793fc096SFrank Li 1398793fc096SFrank Li while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) { 1399793fc096SFrank Li 1400793fc096SFrank Li if (pkt_received >= budget) 1401793fc096SFrank Li break; 1402793fc096SFrank Li pkt_received++; 1403793fc096SFrank Li 1404793fc096SFrank Li /* Since we have allocated space to hold a complete frame, 1405793fc096SFrank Li * the last indicator should be set. 1406793fc096SFrank Li */ 1407793fc096SFrank Li if ((status & BD_ENET_RX_LAST) == 0) 140831b7720cSJoe Perches netdev_err(ndev, "rcv is not +last\n"); 1409793fc096SFrank Li 1410ed63f1dcSRussell King writel(FEC_ENET_RXF, fep->hwp + FEC_IEVENT); 1411db3421c1SRussell King 1412793fc096SFrank Li /* Check for errors. */ 1413793fc096SFrank Li if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | 1414793fc096SFrank Li BD_ENET_RX_CR | BD_ENET_RX_OV)) { 1415793fc096SFrank Li ndev->stats.rx_errors++; 1416793fc096SFrank Li if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH)) { 1417793fc096SFrank Li /* Frame too long or too short. */ 1418793fc096SFrank Li ndev->stats.rx_length_errors++; 1419793fc096SFrank Li } 1420793fc096SFrank Li if (status & BD_ENET_RX_NO) /* Frame alignment */ 1421793fc096SFrank Li ndev->stats.rx_frame_errors++; 1422793fc096SFrank Li if (status & BD_ENET_RX_CR) /* CRC Error */ 1423793fc096SFrank Li ndev->stats.rx_crc_errors++; 1424793fc096SFrank Li if (status & BD_ENET_RX_OV) /* FIFO overrun */ 1425793fc096SFrank Li ndev->stats.rx_fifo_errors++; 1426793fc096SFrank Li } 1427793fc096SFrank Li 1428793fc096SFrank Li /* Report late collisions as a frame error. 1429793fc096SFrank Li * On this error, the BD is closed, but we don't know what we 1430793fc096SFrank Li * have in the buffer. So, just drop this frame on the floor. 1431793fc096SFrank Li */ 1432793fc096SFrank Li if (status & BD_ENET_RX_CL) { 1433793fc096SFrank Li ndev->stats.rx_errors++; 1434793fc096SFrank Li ndev->stats.rx_frame_errors++; 1435793fc096SFrank Li goto rx_processing_done; 1436793fc096SFrank Li } 1437793fc096SFrank Li 1438793fc096SFrank Li /* Process the incoming frame. */ 1439793fc096SFrank Li ndev->stats.rx_packets++; 1440793fc096SFrank Li pkt_len = bdp->cbd_datlen; 1441793fc096SFrank Li ndev->stats.rx_bytes += pkt_len; 1442793fc096SFrank Li 14434d494cdcSFugang Duan index = fec_enet_get_bd_index(rxq->rx_bd_base, bdp, fep); 14441b7bde6dSNimrod Andy skb = rxq->rx_skbuff[index]; 14451b7bde6dSNimrod Andy 14461b7bde6dSNimrod Andy /* The packet length includes FCS, but we don't want to 14471b7bde6dSNimrod Andy * include that when passing upstream as it messes up 14481b7bde6dSNimrod Andy * bridging applications. 14491b7bde6dSNimrod Andy */ 14501310b544SLothar Waßmann is_copybreak = fec_enet_copybreak(ndev, &skb, bdp, pkt_len - 4, 14511310b544SLothar Waßmann need_swap); 14521b7bde6dSNimrod Andy if (!is_copybreak) { 14531b7bde6dSNimrod Andy skb_new = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE); 14541b7bde6dSNimrod Andy if (unlikely(!skb_new)) { 14551b7bde6dSNimrod Andy ndev->stats.rx_dropped++; 14561b7bde6dSNimrod Andy goto rx_processing_done; 14571b7bde6dSNimrod Andy } 14581b7bde6dSNimrod Andy dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, 1459b64bf4b7SNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 1460b64bf4b7SNimrod Andy DMA_FROM_DEVICE); 14611b7bde6dSNimrod Andy } 1462793fc096SFrank Li 14631b7bde6dSNimrod Andy prefetch(skb->data - NET_IP_ALIGN); 14641b7bde6dSNimrod Andy skb_put(skb, pkt_len - 4); 14651b7bde6dSNimrod Andy data = skb->data; 14661310b544SLothar Waßmann if (!is_copybreak && need_swap) 1467793fc096SFrank Li swap_buffer(data, pkt_len); 1468793fc096SFrank Li 1469cdffcf1bSJim Baxter /* Extract the enhanced buffer descriptor */ 1470cdffcf1bSJim Baxter ebdp = NULL; 1471cdffcf1bSJim Baxter if (fep->bufdesc_ex) 1472cdffcf1bSJim Baxter ebdp = (struct bufdesc_ex *)bdp; 1473cdffcf1bSJim Baxter 1474cdffcf1bSJim Baxter /* If this is a VLAN packet remove the VLAN Tag */ 1475cdffcf1bSJim Baxter vlan_packet_rcvd = false; 1476cdffcf1bSJim Baxter if ((ndev->features & NETIF_F_HW_VLAN_CTAG_RX) && 1477cdffcf1bSJim Baxter fep->bufdesc_ex && (ebdp->cbd_esc & BD_ENET_RX_VLAN)) { 1478cdffcf1bSJim Baxter /* Push and remove the vlan tag */ 1479cdffcf1bSJim Baxter struct vlan_hdr *vlan_header = 1480cdffcf1bSJim Baxter (struct vlan_hdr *) (data + ETH_HLEN); 1481cdffcf1bSJim Baxter vlan_tag = ntohs(vlan_header->h_vlan_TCI); 1482cdffcf1bSJim Baxter 1483cdffcf1bSJim Baxter vlan_packet_rcvd = true; 14841b7bde6dSNimrod Andy 1485af5cbc98SNimrod Andy memmove(skb->data + VLAN_HLEN, data, ETH_ALEN * 2); 14861b7bde6dSNimrod Andy skb_pull(skb, VLAN_HLEN); 1487cdffcf1bSJim Baxter } 1488cdffcf1bSJim Baxter 1489793fc096SFrank Li skb->protocol = eth_type_trans(skb, ndev); 1490793fc096SFrank Li 1491793fc096SFrank Li /* Get receive timestamp from the skb */ 1492bfd4ecddSRussell King if (fep->hwts_rx_en && fep->bufdesc_ex) 1493bfd4ecddSRussell King fec_enet_hwtstamp(fep, ebdp->ts, 1494bfd4ecddSRussell King skb_hwtstamps(skb)); 1495793fc096SFrank Li 14964c09eed9SJim Baxter if (fep->bufdesc_ex && 14974c09eed9SJim Baxter (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { 14984c09eed9SJim Baxter if (!(ebdp->cbd_esc & FLAG_RX_CSUM_ERROR)) { 14994c09eed9SJim Baxter /* don't check it */ 15004c09eed9SJim Baxter skb->ip_summed = CHECKSUM_UNNECESSARY; 15014c09eed9SJim Baxter } else { 15024c09eed9SJim Baxter skb_checksum_none_assert(skb); 15034c09eed9SJim Baxter } 15044c09eed9SJim Baxter } 15054c09eed9SJim Baxter 1506cdffcf1bSJim Baxter /* Handle received VLAN packets */ 1507cdffcf1bSJim Baxter if (vlan_packet_rcvd) 1508cdffcf1bSJim Baxter __vlan_hwaccel_put_tag(skb, 1509cdffcf1bSJim Baxter htons(ETH_P_8021Q), 1510cdffcf1bSJim Baxter vlan_tag); 1511cdffcf1bSJim Baxter 1512793fc096SFrank Li napi_gro_receive(&fep->napi, skb); 1513793fc096SFrank Li 15141b7bde6dSNimrod Andy if (is_copybreak) { 1515d842a31fSDuan Fugang-B38611 dma_sync_single_for_device(&fep->pdev->dev, bdp->cbd_bufaddr, 1516b64bf4b7SNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 1517b64bf4b7SNimrod Andy DMA_FROM_DEVICE); 15181b7bde6dSNimrod Andy } else { 15191b7bde6dSNimrod Andy rxq->rx_skbuff[index] = skb_new; 15201b7bde6dSNimrod Andy fec_enet_new_rxbdp(ndev, bdp, skb_new); 15211b7bde6dSNimrod Andy } 15221b7bde6dSNimrod Andy 1523793fc096SFrank Li rx_processing_done: 1524793fc096SFrank Li /* Clear the status flags for this buffer */ 1525793fc096SFrank Li status &= ~BD_ENET_RX_STATS; 1526793fc096SFrank Li 1527793fc096SFrank Li /* Mark the buffer empty */ 1528793fc096SFrank Li status |= BD_ENET_RX_EMPTY; 1529793fc096SFrank Li bdp->cbd_sc = status; 1530793fc096SFrank Li 1531793fc096SFrank Li if (fep->bufdesc_ex) { 1532793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 1533793fc096SFrank Li 1534793fc096SFrank Li ebdp->cbd_esc = BD_ENET_RX_INT; 1535793fc096SFrank Li ebdp->cbd_prot = 0; 1536793fc096SFrank Li ebdp->cbd_bdu = 0; 1537793fc096SFrank Li } 1538793fc096SFrank Li 1539793fc096SFrank Li /* Update BD pointer to next entry */ 15404d494cdcSFugang Duan bdp = fec_enet_get_nextdesc(bdp, fep, queue_id); 154136e24e2eSDuan Fugang-B38611 1542793fc096SFrank Li /* Doing this here will keep the FEC running while we process 1543793fc096SFrank Li * incoming frames. On a heavily loaded network, we should be 1544793fc096SFrank Li * able to keep up at the expense of system resources. 1545793fc096SFrank Li */ 15464d494cdcSFugang Duan writel(0, fep->hwp + FEC_R_DES_ACTIVE(queue_id)); 1547793fc096SFrank Li } 15484d494cdcSFugang Duan rxq->cur_rx = bdp; 1549793fc096SFrank Li return pkt_received; 1550793fc096SFrank Li } 1551793fc096SFrank Li 15524d494cdcSFugang Duan static int 15534d494cdcSFugang Duan fec_enet_rx(struct net_device *ndev, int budget) 15544d494cdcSFugang Duan { 15554d494cdcSFugang Duan int pkt_received = 0; 15564d494cdcSFugang Duan u16 queue_id; 15574d494cdcSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 15584d494cdcSFugang Duan 15594d494cdcSFugang Duan for_each_set_bit(queue_id, &fep->work_rx, FEC_ENET_MAX_RX_QS) { 15604d494cdcSFugang Duan clear_bit(queue_id, &fep->work_rx); 15614d494cdcSFugang Duan pkt_received += fec_enet_rx_queue(ndev, 15624d494cdcSFugang Duan budget - pkt_received, queue_id); 15634d494cdcSFugang Duan } 15644d494cdcSFugang Duan return pkt_received; 15654d494cdcSFugang Duan } 15664d494cdcSFugang Duan 15674d494cdcSFugang Duan static bool 15684d494cdcSFugang Duan fec_enet_collect_events(struct fec_enet_private *fep, uint int_events) 15694d494cdcSFugang Duan { 15704d494cdcSFugang Duan if (int_events == 0) 15714d494cdcSFugang Duan return false; 15724d494cdcSFugang Duan 15734d494cdcSFugang Duan if (int_events & FEC_ENET_RXF) 15744d494cdcSFugang Duan fep->work_rx |= (1 << 2); 1575ce99d0d3SFrank Li if (int_events & FEC_ENET_RXF_1) 1576ce99d0d3SFrank Li fep->work_rx |= (1 << 0); 1577ce99d0d3SFrank Li if (int_events & FEC_ENET_RXF_2) 1578ce99d0d3SFrank Li fep->work_rx |= (1 << 1); 15794d494cdcSFugang Duan 15804d494cdcSFugang Duan if (int_events & FEC_ENET_TXF) 15814d494cdcSFugang Duan fep->work_tx |= (1 << 2); 1582ce99d0d3SFrank Li if (int_events & FEC_ENET_TXF_1) 1583ce99d0d3SFrank Li fep->work_tx |= (1 << 0); 1584ce99d0d3SFrank Li if (int_events & FEC_ENET_TXF_2) 1585ce99d0d3SFrank Li fep->work_tx |= (1 << 1); 15864d494cdcSFugang Duan 15874d494cdcSFugang Duan return true; 15884d494cdcSFugang Duan } 15894d494cdcSFugang Duan 1590793fc096SFrank Li static irqreturn_t 1591793fc096SFrank Li fec_enet_interrupt(int irq, void *dev_id) 1592793fc096SFrank Li { 1593793fc096SFrank Li struct net_device *ndev = dev_id; 1594793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1595793fc096SFrank Li uint int_events; 1596793fc096SFrank Li irqreturn_t ret = IRQ_NONE; 1597793fc096SFrank Li 1598793fc096SFrank Li int_events = readl(fep->hwp + FEC_IEVENT); 159994191fd6SNimrod Andy writel(int_events, fep->hwp + FEC_IEVENT); 16004d494cdcSFugang Duan fec_enet_collect_events(fep, int_events); 1601793fc096SFrank Li 160261615cd2SFugang Duan if ((fep->work_tx || fep->work_rx) && fep->link) { 1603793fc096SFrank Li ret = IRQ_HANDLED; 1604793fc096SFrank Li 160594191fd6SNimrod Andy if (napi_schedule_prep(&fep->napi)) { 16067a16807cSRussell King /* Disable the NAPI interrupts */ 16077a16807cSRussell King writel(FEC_ENET_MII, fep->hwp + FEC_IMASK); 160894191fd6SNimrod Andy __napi_schedule(&fep->napi); 160994191fd6SNimrod Andy } 1610793fc096SFrank Li } 1611793fc096SFrank Li 1612793fc096SFrank Li if (int_events & FEC_ENET_MII) { 1613793fc096SFrank Li ret = IRQ_HANDLED; 1614793fc096SFrank Li complete(&fep->mdio_done); 1615793fc096SFrank Li } 1616793fc096SFrank Li 161781f35ffdSPhilipp Zabel if (fep->ptp_clock) 1618278d2404SLuwei Zhou fec_ptp_check_pps_event(fep); 1619278d2404SLuwei Zhou 1620793fc096SFrank Li return ret; 1621793fc096SFrank Li } 1622793fc096SFrank Li 1623793fc096SFrank Li static int fec_enet_rx_napi(struct napi_struct *napi, int budget) 1624793fc096SFrank Li { 1625793fc096SFrank Li struct net_device *ndev = napi->dev; 1626793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 16277a16807cSRussell King int pkts; 16287a16807cSRussell King 16297a16807cSRussell King pkts = fec_enet_rx(ndev, budget); 1630793fc096SFrank Li 1631793fc096SFrank Li fec_enet_tx(ndev); 1632793fc096SFrank Li 1633793fc096SFrank Li if (pkts < budget) { 1634793fc096SFrank Li napi_complete(napi); 1635793fc096SFrank Li writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 1636793fc096SFrank Li } 1637793fc096SFrank Li return pkts; 1638793fc096SFrank Li } 1639793fc096SFrank Li 1640793fc096SFrank Li /* ------------------------------------------------------------------------- */ 1641793fc096SFrank Li static void fec_get_mac(struct net_device *ndev) 1642793fc096SFrank Li { 1643793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 164494660ba0SJingoo Han struct fec_platform_data *pdata = dev_get_platdata(&fep->pdev->dev); 1645793fc096SFrank Li unsigned char *iap, tmpaddr[ETH_ALEN]; 1646793fc096SFrank Li 1647793fc096SFrank Li /* 1648793fc096SFrank Li * try to get mac address in following order: 1649793fc096SFrank Li * 1650793fc096SFrank Li * 1) module parameter via kernel command line in form 1651793fc096SFrank Li * fec.macaddr=0x00,0x04,0x9f,0x01,0x30,0xe0 1652793fc096SFrank Li */ 1653793fc096SFrank Li iap = macaddr; 1654793fc096SFrank Li 1655793fc096SFrank Li /* 1656793fc096SFrank Li * 2) from device tree data 1657793fc096SFrank Li */ 1658793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 1659793fc096SFrank Li struct device_node *np = fep->pdev->dev.of_node; 1660793fc096SFrank Li if (np) { 1661793fc096SFrank Li const char *mac = of_get_mac_address(np); 1662793fc096SFrank Li if (mac) 1663793fc096SFrank Li iap = (unsigned char *) mac; 1664793fc096SFrank Li } 1665793fc096SFrank Li } 1666793fc096SFrank Li 1667793fc096SFrank Li /* 1668793fc096SFrank Li * 3) from flash or fuse (via platform data) 1669793fc096SFrank Li */ 1670793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 1671793fc096SFrank Li #ifdef CONFIG_M5272 1672793fc096SFrank Li if (FEC_FLASHMAC) 1673793fc096SFrank Li iap = (unsigned char *)FEC_FLASHMAC; 1674793fc096SFrank Li #else 1675793fc096SFrank Li if (pdata) 1676793fc096SFrank Li iap = (unsigned char *)&pdata->mac; 1677793fc096SFrank Li #endif 1678793fc096SFrank Li } 1679793fc096SFrank Li 1680793fc096SFrank Li /* 1681793fc096SFrank Li * 4) FEC mac registers set by bootloader 1682793fc096SFrank Li */ 1683793fc096SFrank Li if (!is_valid_ether_addr(iap)) { 16847d7628f3SDan Carpenter *((__be32 *) &tmpaddr[0]) = 16857d7628f3SDan Carpenter cpu_to_be32(readl(fep->hwp + FEC_ADDR_LOW)); 16867d7628f3SDan Carpenter *((__be16 *) &tmpaddr[4]) = 16877d7628f3SDan Carpenter cpu_to_be16(readl(fep->hwp + FEC_ADDR_HIGH) >> 16); 1688793fc096SFrank Li iap = &tmpaddr[0]; 1689793fc096SFrank Li } 1690793fc096SFrank Li 1691ff5b2fabSLucas Stach /* 1692ff5b2fabSLucas Stach * 5) random mac address 1693ff5b2fabSLucas Stach */ 1694ff5b2fabSLucas Stach if (!is_valid_ether_addr(iap)) { 1695ff5b2fabSLucas Stach /* Report it and use a random ethernet address instead */ 1696ff5b2fabSLucas Stach netdev_err(ndev, "Invalid MAC address: %pM\n", iap); 1697ff5b2fabSLucas Stach eth_hw_addr_random(ndev); 1698ff5b2fabSLucas Stach netdev_info(ndev, "Using random MAC address: %pM\n", 1699ff5b2fabSLucas Stach ndev->dev_addr); 1700ff5b2fabSLucas Stach return; 1701ff5b2fabSLucas Stach } 1702ff5b2fabSLucas Stach 1703793fc096SFrank Li memcpy(ndev->dev_addr, iap, ETH_ALEN); 1704793fc096SFrank Li 1705793fc096SFrank Li /* Adjust MAC if using macaddr */ 1706793fc096SFrank Li if (iap == macaddr) 1707793fc096SFrank Li ndev->dev_addr[ETH_ALEN-1] = macaddr[ETH_ALEN-1] + fep->dev_id; 1708793fc096SFrank Li } 1709793fc096SFrank Li 1710793fc096SFrank Li /* ------------------------------------------------------------------------- */ 1711793fc096SFrank Li 1712793fc096SFrank Li /* 1713793fc096SFrank Li * Phy section 1714793fc096SFrank Li */ 1715793fc096SFrank Li static void fec_enet_adjust_link(struct net_device *ndev) 1716793fc096SFrank Li { 1717793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1718793fc096SFrank Li struct phy_device *phy_dev = fep->phy_dev; 1719793fc096SFrank Li int status_change = 0; 1720793fc096SFrank Li 1721793fc096SFrank Li /* Prevent a state halted on mii error */ 1722793fc096SFrank Li if (fep->mii_timeout && phy_dev->state == PHY_HALTED) { 1723793fc096SFrank Li phy_dev->state = PHY_RESUMING; 172454309fa6SFrank Li return; 1725793fc096SFrank Li } 1726793fc096SFrank Li 17278ce5624fSRussell King /* 17288ce5624fSRussell King * If the netdev is down, or is going down, we're not interested 17298ce5624fSRussell King * in link state events, so just mark our idea of the link as down 17308ce5624fSRussell King * and ignore the event. 17318ce5624fSRussell King */ 17328ce5624fSRussell King if (!netif_running(ndev) || !netif_device_present(ndev)) { 17338ce5624fSRussell King fep->link = 0; 17348ce5624fSRussell King } else if (phy_dev->link) { 1735793fc096SFrank Li if (!fep->link) { 1736793fc096SFrank Li fep->link = phy_dev->link; 1737793fc096SFrank Li status_change = 1; 1738793fc096SFrank Li } 1739793fc096SFrank Li 1740ef83337dSRussell King if (fep->full_duplex != phy_dev->duplex) { 1741ef83337dSRussell King fep->full_duplex = phy_dev->duplex; 1742793fc096SFrank Li status_change = 1; 1743ef83337dSRussell King } 1744793fc096SFrank Li 1745793fc096SFrank Li if (phy_dev->speed != fep->speed) { 1746793fc096SFrank Li fep->speed = phy_dev->speed; 1747793fc096SFrank Li status_change = 1; 1748793fc096SFrank Li } 1749793fc096SFrank Li 1750793fc096SFrank Li /* if any of the above changed restart the FEC */ 1751dbc64a8eSRussell King if (status_change) { 1752dbc64a8eSRussell King napi_disable(&fep->napi); 1753dbc64a8eSRussell King netif_tx_lock_bh(ndev); 1754ef83337dSRussell King fec_restart(ndev); 1755dbc64a8eSRussell King netif_wake_queue(ndev); 17566af42d42SRussell King netif_tx_unlock_bh(ndev); 1757dbc64a8eSRussell King napi_enable(&fep->napi); 1758dbc64a8eSRussell King } 1759793fc096SFrank Li } else { 1760793fc096SFrank Li if (fep->link) { 1761f208ce10SRussell King napi_disable(&fep->napi); 1762f208ce10SRussell King netif_tx_lock_bh(ndev); 1763793fc096SFrank Li fec_stop(ndev); 1764f208ce10SRussell King netif_tx_unlock_bh(ndev); 1765f208ce10SRussell King napi_enable(&fep->napi); 17666e0895c2SDavid S. Miller fep->link = phy_dev->link; 1767793fc096SFrank Li status_change = 1; 1768793fc096SFrank Li } 1769793fc096SFrank Li } 1770793fc096SFrank Li 1771793fc096SFrank Li if (status_change) 1772793fc096SFrank Li phy_print_status(phy_dev); 1773793fc096SFrank Li } 1774793fc096SFrank Li 1775793fc096SFrank Li static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum) 1776793fc096SFrank Li { 1777793fc096SFrank Li struct fec_enet_private *fep = bus->priv; 17788fff755eSAndrew Lunn struct device *dev = &fep->pdev->dev; 1779793fc096SFrank Li unsigned long time_left; 17808fff755eSAndrew Lunn int ret = 0; 17818fff755eSAndrew Lunn 17828fff755eSAndrew Lunn ret = pm_runtime_get_sync(dev); 1783b0c6ce24SFabio Estevam if (ret < 0) 17848fff755eSAndrew Lunn return ret; 1785793fc096SFrank Li 1786793fc096SFrank Li fep->mii_timeout = 0; 1787aac27c7aSRussell King reinit_completion(&fep->mdio_done); 1788793fc096SFrank Li 1789793fc096SFrank Li /* start a read op */ 1790793fc096SFrank Li writel(FEC_MMFR_ST | FEC_MMFR_OP_READ | 1791793fc096SFrank Li FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) | 1792793fc096SFrank Li FEC_MMFR_TA, fep->hwp + FEC_MII_DATA); 1793793fc096SFrank Li 1794793fc096SFrank Li /* wait for end of transfer */ 1795793fc096SFrank Li time_left = wait_for_completion_timeout(&fep->mdio_done, 1796793fc096SFrank Li usecs_to_jiffies(FEC_MII_TIMEOUT)); 1797793fc096SFrank Li if (time_left == 0) { 1798793fc096SFrank Li fep->mii_timeout = 1; 179931b7720cSJoe Perches netdev_err(fep->netdev, "MDIO read timeout\n"); 18008fff755eSAndrew Lunn ret = -ETIMEDOUT; 18018fff755eSAndrew Lunn goto out; 1802793fc096SFrank Li } 1803793fc096SFrank Li 18048fff755eSAndrew Lunn ret = FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA)); 18058fff755eSAndrew Lunn 18068fff755eSAndrew Lunn out: 18078fff755eSAndrew Lunn pm_runtime_mark_last_busy(dev); 18088fff755eSAndrew Lunn pm_runtime_put_autosuspend(dev); 18098fff755eSAndrew Lunn 18108fff755eSAndrew Lunn return ret; 1811793fc096SFrank Li } 1812793fc096SFrank Li 1813793fc096SFrank Li static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum, 1814793fc096SFrank Li u16 value) 1815793fc096SFrank Li { 1816793fc096SFrank Li struct fec_enet_private *fep = bus->priv; 18178fff755eSAndrew Lunn struct device *dev = &fep->pdev->dev; 1818793fc096SFrank Li unsigned long time_left; 181942ea4457SMaciej S. Szmigiero int ret; 18208fff755eSAndrew Lunn 18218fff755eSAndrew Lunn ret = pm_runtime_get_sync(dev); 1822b0c6ce24SFabio Estevam if (ret < 0) 18238fff755eSAndrew Lunn return ret; 182442ea4457SMaciej S. Szmigiero else 182542ea4457SMaciej S. Szmigiero ret = 0; 1826793fc096SFrank Li 1827793fc096SFrank Li fep->mii_timeout = 0; 1828aac27c7aSRussell King reinit_completion(&fep->mdio_done); 1829793fc096SFrank Li 1830793fc096SFrank Li /* start a write op */ 1831793fc096SFrank Li writel(FEC_MMFR_ST | FEC_MMFR_OP_WRITE | 1832793fc096SFrank Li FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) | 1833793fc096SFrank Li FEC_MMFR_TA | FEC_MMFR_DATA(value), 1834793fc096SFrank Li fep->hwp + FEC_MII_DATA); 1835793fc096SFrank Li 1836793fc096SFrank Li /* wait for end of transfer */ 1837793fc096SFrank Li time_left = wait_for_completion_timeout(&fep->mdio_done, 1838793fc096SFrank Li usecs_to_jiffies(FEC_MII_TIMEOUT)); 1839793fc096SFrank Li if (time_left == 0) { 1840793fc096SFrank Li fep->mii_timeout = 1; 184131b7720cSJoe Perches netdev_err(fep->netdev, "MDIO write timeout\n"); 18428fff755eSAndrew Lunn ret = -ETIMEDOUT; 1843793fc096SFrank Li } 1844793fc096SFrank Li 18458fff755eSAndrew Lunn pm_runtime_mark_last_busy(dev); 18468fff755eSAndrew Lunn pm_runtime_put_autosuspend(dev); 18478fff755eSAndrew Lunn 18488fff755eSAndrew Lunn return ret; 1849793fc096SFrank Li } 1850793fc096SFrank Li 1851e8fcfcd5SNimrod Andy static int fec_enet_clk_enable(struct net_device *ndev, bool enable) 1852e8fcfcd5SNimrod Andy { 1853e8fcfcd5SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 1854e8fcfcd5SNimrod Andy int ret; 1855e8fcfcd5SNimrod Andy 1856e8fcfcd5SNimrod Andy if (enable) { 1857e8fcfcd5SNimrod Andy ret = clk_prepare_enable(fep->clk_ahb); 1858e8fcfcd5SNimrod Andy if (ret) 1859e8fcfcd5SNimrod Andy return ret; 1860e8fcfcd5SNimrod Andy if (fep->clk_enet_out) { 1861e8fcfcd5SNimrod Andy ret = clk_prepare_enable(fep->clk_enet_out); 1862e8fcfcd5SNimrod Andy if (ret) 1863e8fcfcd5SNimrod Andy goto failed_clk_enet_out; 1864e8fcfcd5SNimrod Andy } 1865e8fcfcd5SNimrod Andy if (fep->clk_ptp) { 186691c0d987SNimrod Andy mutex_lock(&fep->ptp_clk_mutex); 1867e8fcfcd5SNimrod Andy ret = clk_prepare_enable(fep->clk_ptp); 186891c0d987SNimrod Andy if (ret) { 186991c0d987SNimrod Andy mutex_unlock(&fep->ptp_clk_mutex); 1870e8fcfcd5SNimrod Andy goto failed_clk_ptp; 187191c0d987SNimrod Andy } else { 187291c0d987SNimrod Andy fep->ptp_clk_on = true; 187391c0d987SNimrod Andy } 187491c0d987SNimrod Andy mutex_unlock(&fep->ptp_clk_mutex); 1875e8fcfcd5SNimrod Andy } 18769b5330edSFugang Duan if (fep->clk_ref) { 18779b5330edSFugang Duan ret = clk_prepare_enable(fep->clk_ref); 18789b5330edSFugang Duan if (ret) 18799b5330edSFugang Duan goto failed_clk_ref; 18809b5330edSFugang Duan } 1881e8fcfcd5SNimrod Andy } else { 1882e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_ahb); 1883e8fcfcd5SNimrod Andy if (fep->clk_enet_out) 1884e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_enet_out); 188591c0d987SNimrod Andy if (fep->clk_ptp) { 188691c0d987SNimrod Andy mutex_lock(&fep->ptp_clk_mutex); 1887e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_ptp); 188891c0d987SNimrod Andy fep->ptp_clk_on = false; 188991c0d987SNimrod Andy mutex_unlock(&fep->ptp_clk_mutex); 189091c0d987SNimrod Andy } 18919b5330edSFugang Duan if (fep->clk_ref) 18929b5330edSFugang Duan clk_disable_unprepare(fep->clk_ref); 1893e8fcfcd5SNimrod Andy } 1894e8fcfcd5SNimrod Andy 1895e8fcfcd5SNimrod Andy return 0; 18969b5330edSFugang Duan 18979b5330edSFugang Duan failed_clk_ref: 18989b5330edSFugang Duan if (fep->clk_ref) 18999b5330edSFugang Duan clk_disable_unprepare(fep->clk_ref); 1900e8fcfcd5SNimrod Andy failed_clk_ptp: 1901e8fcfcd5SNimrod Andy if (fep->clk_enet_out) 1902e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_enet_out); 1903e8fcfcd5SNimrod Andy failed_clk_enet_out: 1904e8fcfcd5SNimrod Andy clk_disable_unprepare(fep->clk_ahb); 1905e8fcfcd5SNimrod Andy 1906e8fcfcd5SNimrod Andy return ret; 1907e8fcfcd5SNimrod Andy } 1908e8fcfcd5SNimrod Andy 1909793fc096SFrank Li static int fec_enet_mii_probe(struct net_device *ndev) 1910793fc096SFrank Li { 1911793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1912793fc096SFrank Li struct phy_device *phy_dev = NULL; 1913793fc096SFrank Li char mdio_bus_id[MII_BUS_ID_SIZE]; 1914793fc096SFrank Li char phy_name[MII_BUS_ID_SIZE + 3]; 1915793fc096SFrank Li int phy_id; 1916793fc096SFrank Li int dev_id = fep->dev_id; 1917793fc096SFrank Li 1918793fc096SFrank Li fep->phy_dev = NULL; 1919793fc096SFrank Li 1920407066f8SUwe Kleine-König if (fep->phy_node) { 1921407066f8SUwe Kleine-König phy_dev = of_phy_connect(ndev, fep->phy_node, 1922407066f8SUwe Kleine-König &fec_enet_adjust_link, 0, 1923407066f8SUwe Kleine-König fep->phy_interface); 1924213a9922SNimrod Andy if (!phy_dev) 1925213a9922SNimrod Andy return -ENODEV; 1926407066f8SUwe Kleine-König } else { 1927793fc096SFrank Li /* check for attached phy */ 1928793fc096SFrank Li for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) { 1929793fc096SFrank Li if ((fep->mii_bus->phy_mask & (1 << phy_id))) 1930793fc096SFrank Li continue; 1931793fc096SFrank Li if (fep->mii_bus->phy_map[phy_id] == NULL) 1932793fc096SFrank Li continue; 1933793fc096SFrank Li if (fep->mii_bus->phy_map[phy_id]->phy_id == 0) 1934793fc096SFrank Li continue; 1935793fc096SFrank Li if (dev_id--) 1936793fc096SFrank Li continue; 1937949bdd20SRickard Strandqvist strlcpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE); 1938793fc096SFrank Li break; 1939793fc096SFrank Li } 1940793fc096SFrank Li 1941793fc096SFrank Li if (phy_id >= PHY_MAX_ADDR) { 194231b7720cSJoe Perches netdev_info(ndev, "no PHY, assuming direct connection to switch\n"); 1943949bdd20SRickard Strandqvist strlcpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE); 1944793fc096SFrank Li phy_id = 0; 1945793fc096SFrank Li } 1946793fc096SFrank Li 1947407066f8SUwe Kleine-König snprintf(phy_name, sizeof(phy_name), 1948407066f8SUwe Kleine-König PHY_ID_FMT, mdio_bus_id, phy_id); 1949793fc096SFrank Li phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, 1950793fc096SFrank Li fep->phy_interface); 1951407066f8SUwe Kleine-König } 1952407066f8SUwe Kleine-König 1953793fc096SFrank Li if (IS_ERR(phy_dev)) { 195431b7720cSJoe Perches netdev_err(ndev, "could not attach to PHY\n"); 1955793fc096SFrank Li return PTR_ERR(phy_dev); 1956793fc096SFrank Li } 1957793fc096SFrank Li 1958793fc096SFrank Li /* mask with MAC supported features */ 19596b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_GBIT) { 1960793fc096SFrank Li phy_dev->supported &= PHY_GBIT_FEATURES; 1961b44592ffSRussell King phy_dev->supported &= ~SUPPORTED_1000baseT_Half; 1962d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 1963793fc096SFrank Li phy_dev->supported |= SUPPORTED_Pause; 1964d1391930SGuenter Roeck #endif 1965793fc096SFrank Li } 1966793fc096SFrank Li else 1967793fc096SFrank Li phy_dev->supported &= PHY_BASIC_FEATURES; 1968793fc096SFrank Li 1969793fc096SFrank Li phy_dev->advertising = phy_dev->supported; 1970793fc096SFrank Li 1971793fc096SFrank Li fep->phy_dev = phy_dev; 1972793fc096SFrank Li fep->link = 0; 1973793fc096SFrank Li fep->full_duplex = 0; 1974793fc096SFrank Li 197531b7720cSJoe Perches netdev_info(ndev, "Freescale FEC PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n", 1976793fc096SFrank Li fep->phy_dev->drv->name, dev_name(&fep->phy_dev->dev), 1977793fc096SFrank Li fep->phy_dev->irq); 1978793fc096SFrank Li 1979793fc096SFrank Li return 0; 1980793fc096SFrank Li } 1981793fc096SFrank Li 1982793fc096SFrank Li static int fec_enet_mii_init(struct platform_device *pdev) 1983793fc096SFrank Li { 1984793fc096SFrank Li static struct mii_bus *fec0_mii_bus; 1985793fc096SFrank Li struct net_device *ndev = platform_get_drvdata(pdev); 1986793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 1987407066f8SUwe Kleine-König struct device_node *node; 1988793fc096SFrank Li int err = -ENXIO, i; 198963c60732SUwe Kleine-König u32 mii_speed, holdtime; 1990793fc096SFrank Li 1991793fc096SFrank Li /* 19923d125f9cSStefan Agner * The i.MX28 dual fec interfaces are not equal. 1993793fc096SFrank Li * Here are the differences: 1994793fc096SFrank Li * 1995793fc096SFrank Li * - fec0 supports MII & RMII modes while fec1 only supports RMII 1996793fc096SFrank Li * - fec0 acts as the 1588 time master while fec1 is slave 1997793fc096SFrank Li * - external phys can only be configured by fec0 1998793fc096SFrank Li * 1999793fc096SFrank Li * That is to say fec1 can not work independently. It only works 2000793fc096SFrank Li * when fec0 is working. The reason behind this design is that the 2001793fc096SFrank Li * second interface is added primarily for Switch mode. 2002793fc096SFrank Li * 2003793fc096SFrank Li * Because of the last point above, both phys are attached on fec0 2004793fc096SFrank Li * mdio interface in board design, and need to be configured by 2005793fc096SFrank Li * fec0 mii_bus. 2006793fc096SFrank Li */ 20073d125f9cSStefan Agner if ((fep->quirks & FEC_QUIRK_SINGLE_MDIO) && fep->dev_id > 0) { 2008793fc096SFrank Li /* fec1 uses fec0 mii_bus */ 2009793fc096SFrank Li if (mii_cnt && fec0_mii_bus) { 2010793fc096SFrank Li fep->mii_bus = fec0_mii_bus; 2011793fc096SFrank Li mii_cnt++; 2012793fc096SFrank Li return 0; 2013793fc096SFrank Li } 2014793fc096SFrank Li return -ENOENT; 2015793fc096SFrank Li } 2016793fc096SFrank Li 2017793fc096SFrank Li fep->mii_timeout = 0; 2018793fc096SFrank Li 2019793fc096SFrank Li /* 2020793fc096SFrank Li * Set MII speed to 2.5 MHz (= clk_get_rate() / 2 * phy_speed) 2021793fc096SFrank Li * 2022793fc096SFrank Li * The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while 2023793fc096SFrank Li * for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'. The i.MX28 2024793fc096SFrank Li * Reference Manual has an error on this, and gets fixed on i.MX6Q 2025793fc096SFrank Li * document. 2026793fc096SFrank Li */ 202763c60732SUwe Kleine-König mii_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 5000000); 20286b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_ENET_MAC) 202963c60732SUwe Kleine-König mii_speed--; 203063c60732SUwe Kleine-König if (mii_speed > 63) { 203163c60732SUwe Kleine-König dev_err(&pdev->dev, 203263c60732SUwe Kleine-König "fec clock (%lu) to fast to get right mii speed\n", 203363c60732SUwe Kleine-König clk_get_rate(fep->clk_ipg)); 203463c60732SUwe Kleine-König err = -EINVAL; 203563c60732SUwe Kleine-König goto err_out; 203663c60732SUwe Kleine-König } 203763c60732SUwe Kleine-König 203863c60732SUwe Kleine-König /* 203963c60732SUwe Kleine-König * The i.MX28 and i.MX6 types have another filed in the MSCR (aka 204063c60732SUwe Kleine-König * MII_SPEED) register that defines the MDIO output hold time. Earlier 204163c60732SUwe Kleine-König * versions are RAZ there, so just ignore the difference and write the 204263c60732SUwe Kleine-König * register always. 204363c60732SUwe Kleine-König * The minimal hold time according to IEE802.3 (clause 22) is 10 ns. 204463c60732SUwe Kleine-König * HOLDTIME + 1 is the number of clk cycles the fec is holding the 204563c60732SUwe Kleine-König * output. 204663c60732SUwe Kleine-König * The HOLDTIME bitfield takes values between 0 and 7 (inclusive). 204763c60732SUwe Kleine-König * Given that ceil(clkrate / 5000000) <= 64, the calculation for 204863c60732SUwe Kleine-König * holdtime cannot result in a value greater than 3. 204963c60732SUwe Kleine-König */ 205063c60732SUwe Kleine-König holdtime = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 100000000) - 1; 205163c60732SUwe Kleine-König 205263c60732SUwe Kleine-König fep->phy_speed = mii_speed << 1 | holdtime << 8; 205363c60732SUwe Kleine-König 2054793fc096SFrank Li writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 2055793fc096SFrank Li 2056793fc096SFrank Li fep->mii_bus = mdiobus_alloc(); 2057793fc096SFrank Li if (fep->mii_bus == NULL) { 2058793fc096SFrank Li err = -ENOMEM; 2059793fc096SFrank Li goto err_out; 2060793fc096SFrank Li } 2061793fc096SFrank Li 2062793fc096SFrank Li fep->mii_bus->name = "fec_enet_mii_bus"; 2063793fc096SFrank Li fep->mii_bus->read = fec_enet_mdio_read; 2064793fc096SFrank Li fep->mii_bus->write = fec_enet_mdio_write; 2065793fc096SFrank Li snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", 2066793fc096SFrank Li pdev->name, fep->dev_id + 1); 2067793fc096SFrank Li fep->mii_bus->priv = fep; 2068793fc096SFrank Li fep->mii_bus->parent = &pdev->dev; 2069793fc096SFrank Li 2070793fc096SFrank Li fep->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); 2071793fc096SFrank Li if (!fep->mii_bus->irq) { 2072793fc096SFrank Li err = -ENOMEM; 2073793fc096SFrank Li goto err_out_free_mdiobus; 2074793fc096SFrank Li } 2075793fc096SFrank Li 2076793fc096SFrank Li for (i = 0; i < PHY_MAX_ADDR; i++) 2077793fc096SFrank Li fep->mii_bus->irq[i] = PHY_POLL; 2078793fc096SFrank Li 2079407066f8SUwe Kleine-König node = of_get_child_by_name(pdev->dev.of_node, "mdio"); 2080407066f8SUwe Kleine-König if (node) { 2081407066f8SUwe Kleine-König err = of_mdiobus_register(fep->mii_bus, node); 2082407066f8SUwe Kleine-König of_node_put(node); 2083407066f8SUwe Kleine-König } else { 2084407066f8SUwe Kleine-König err = mdiobus_register(fep->mii_bus); 2085407066f8SUwe Kleine-König } 2086407066f8SUwe Kleine-König 2087407066f8SUwe Kleine-König if (err) 2088793fc096SFrank Li goto err_out_free_mdio_irq; 2089793fc096SFrank Li 2090793fc096SFrank Li mii_cnt++; 2091793fc096SFrank Li 2092793fc096SFrank Li /* save fec0 mii_bus */ 20933d125f9cSStefan Agner if (fep->quirks & FEC_QUIRK_SINGLE_MDIO) 2094793fc096SFrank Li fec0_mii_bus = fep->mii_bus; 2095793fc096SFrank Li 2096793fc096SFrank Li return 0; 2097793fc096SFrank Li 2098793fc096SFrank Li err_out_free_mdio_irq: 2099793fc096SFrank Li kfree(fep->mii_bus->irq); 2100793fc096SFrank Li err_out_free_mdiobus: 2101793fc096SFrank Li mdiobus_free(fep->mii_bus); 2102793fc096SFrank Li err_out: 2103793fc096SFrank Li return err; 2104793fc096SFrank Li } 2105793fc096SFrank Li 2106793fc096SFrank Li static void fec_enet_mii_remove(struct fec_enet_private *fep) 2107793fc096SFrank Li { 2108793fc096SFrank Li if (--mii_cnt == 0) { 2109793fc096SFrank Li mdiobus_unregister(fep->mii_bus); 2110793fc096SFrank Li kfree(fep->mii_bus->irq); 2111793fc096SFrank Li mdiobus_free(fep->mii_bus); 2112793fc096SFrank Li } 2113793fc096SFrank Li } 2114793fc096SFrank Li 2115793fc096SFrank Li static int fec_enet_get_settings(struct net_device *ndev, 2116793fc096SFrank Li struct ethtool_cmd *cmd) 2117793fc096SFrank Li { 2118793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2119793fc096SFrank Li struct phy_device *phydev = fep->phy_dev; 2120793fc096SFrank Li 2121793fc096SFrank Li if (!phydev) 2122793fc096SFrank Li return -ENODEV; 2123793fc096SFrank Li 2124793fc096SFrank Li return phy_ethtool_gset(phydev, cmd); 2125793fc096SFrank Li } 2126793fc096SFrank Li 2127793fc096SFrank Li static int fec_enet_set_settings(struct net_device *ndev, 2128793fc096SFrank Li struct ethtool_cmd *cmd) 2129793fc096SFrank Li { 2130793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2131793fc096SFrank Li struct phy_device *phydev = fep->phy_dev; 2132793fc096SFrank Li 2133793fc096SFrank Li if (!phydev) 2134793fc096SFrank Li return -ENODEV; 2135793fc096SFrank Li 2136793fc096SFrank Li return phy_ethtool_sset(phydev, cmd); 2137793fc096SFrank Li } 2138793fc096SFrank Li 2139793fc096SFrank Li static void fec_enet_get_drvinfo(struct net_device *ndev, 2140793fc096SFrank Li struct ethtool_drvinfo *info) 2141793fc096SFrank Li { 2142793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2143793fc096SFrank Li 2144793fc096SFrank Li strlcpy(info->driver, fep->pdev->dev.driver->name, 2145793fc096SFrank Li sizeof(info->driver)); 2146793fc096SFrank Li strlcpy(info->version, "Revision: 1.0", sizeof(info->version)); 2147793fc096SFrank Li strlcpy(info->bus_info, dev_name(&ndev->dev), sizeof(info->bus_info)); 2148793fc096SFrank Li } 2149793fc096SFrank Li 2150db65f35fSPhilippe Reynes static int fec_enet_get_regs_len(struct net_device *ndev) 2151db65f35fSPhilippe Reynes { 2152db65f35fSPhilippe Reynes struct fec_enet_private *fep = netdev_priv(ndev); 2153db65f35fSPhilippe Reynes struct resource *r; 2154db65f35fSPhilippe Reynes int s = 0; 2155db65f35fSPhilippe Reynes 2156db65f35fSPhilippe Reynes r = platform_get_resource(fep->pdev, IORESOURCE_MEM, 0); 2157db65f35fSPhilippe Reynes if (r) 2158db65f35fSPhilippe Reynes s = resource_size(r); 2159db65f35fSPhilippe Reynes 2160db65f35fSPhilippe Reynes return s; 2161db65f35fSPhilippe Reynes } 2162db65f35fSPhilippe Reynes 2163db65f35fSPhilippe Reynes /* List of registers that can be safety be read to dump them with ethtool */ 2164db65f35fSPhilippe Reynes #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ 2165db65f35fSPhilippe Reynes defined(CONFIG_M520x) || defined(CONFIG_M532x) || \ 2166db65f35fSPhilippe Reynes defined(CONFIG_ARCH_MXC) || defined(CONFIG_SOC_IMX28) 2167db65f35fSPhilippe Reynes static u32 fec_enet_register_offset[] = { 2168db65f35fSPhilippe Reynes FEC_IEVENT, FEC_IMASK, FEC_R_DES_ACTIVE_0, FEC_X_DES_ACTIVE_0, 2169db65f35fSPhilippe Reynes FEC_ECNTRL, FEC_MII_DATA, FEC_MII_SPEED, FEC_MIB_CTRLSTAT, FEC_R_CNTRL, 2170db65f35fSPhilippe Reynes FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, FEC_OPD, FEC_TXIC0, FEC_TXIC1, 2171db65f35fSPhilippe Reynes FEC_TXIC2, FEC_RXIC0, FEC_RXIC1, FEC_RXIC2, FEC_HASH_TABLE_HIGH, 2172db65f35fSPhilippe Reynes FEC_HASH_TABLE_LOW, FEC_GRP_HASH_TABLE_HIGH, FEC_GRP_HASH_TABLE_LOW, 2173db65f35fSPhilippe Reynes FEC_X_WMRK, FEC_R_BOUND, FEC_R_FSTART, FEC_R_DES_START_1, 2174db65f35fSPhilippe Reynes FEC_X_DES_START_1, FEC_R_BUFF_SIZE_1, FEC_R_DES_START_2, 2175db65f35fSPhilippe Reynes FEC_X_DES_START_2, FEC_R_BUFF_SIZE_2, FEC_R_DES_START_0, 2176db65f35fSPhilippe Reynes FEC_X_DES_START_0, FEC_R_BUFF_SIZE_0, FEC_R_FIFO_RSFL, FEC_R_FIFO_RSEM, 2177db65f35fSPhilippe Reynes FEC_R_FIFO_RAEM, FEC_R_FIFO_RAFL, FEC_RACC, FEC_RCMR_1, FEC_RCMR_2, 2178db65f35fSPhilippe Reynes FEC_DMA_CFG_1, FEC_DMA_CFG_2, FEC_R_DES_ACTIVE_1, FEC_X_DES_ACTIVE_1, 2179db65f35fSPhilippe Reynes FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_2, FEC_QOS_SCHEME, 2180db65f35fSPhilippe Reynes RMON_T_DROP, RMON_T_PACKETS, RMON_T_BC_PKT, RMON_T_MC_PKT, 2181db65f35fSPhilippe Reynes RMON_T_CRC_ALIGN, RMON_T_UNDERSIZE, RMON_T_OVERSIZE, RMON_T_FRAG, 2182db65f35fSPhilippe Reynes RMON_T_JAB, RMON_T_COL, RMON_T_P64, RMON_T_P65TO127, RMON_T_P128TO255, 2183db65f35fSPhilippe Reynes RMON_T_P256TO511, RMON_T_P512TO1023, RMON_T_P1024TO2047, 2184db65f35fSPhilippe Reynes RMON_T_P_GTE2048, RMON_T_OCTETS, 2185db65f35fSPhilippe Reynes IEEE_T_DROP, IEEE_T_FRAME_OK, IEEE_T_1COL, IEEE_T_MCOL, IEEE_T_DEF, 2186db65f35fSPhilippe Reynes IEEE_T_LCOL, IEEE_T_EXCOL, IEEE_T_MACERR, IEEE_T_CSERR, IEEE_T_SQE, 2187db65f35fSPhilippe Reynes IEEE_T_FDXFC, IEEE_T_OCTETS_OK, 2188db65f35fSPhilippe Reynes RMON_R_PACKETS, RMON_R_BC_PKT, RMON_R_MC_PKT, RMON_R_CRC_ALIGN, 2189db65f35fSPhilippe Reynes RMON_R_UNDERSIZE, RMON_R_OVERSIZE, RMON_R_FRAG, RMON_R_JAB, 2190db65f35fSPhilippe Reynes RMON_R_RESVD_O, RMON_R_P64, RMON_R_P65TO127, RMON_R_P128TO255, 2191db65f35fSPhilippe Reynes RMON_R_P256TO511, RMON_R_P512TO1023, RMON_R_P1024TO2047, 2192db65f35fSPhilippe Reynes RMON_R_P_GTE2048, RMON_R_OCTETS, 2193db65f35fSPhilippe Reynes IEEE_R_DROP, IEEE_R_FRAME_OK, IEEE_R_CRC, IEEE_R_ALIGN, IEEE_R_MACERR, 2194db65f35fSPhilippe Reynes IEEE_R_FDXFC, IEEE_R_OCTETS_OK 2195db65f35fSPhilippe Reynes }; 2196db65f35fSPhilippe Reynes #else 2197db65f35fSPhilippe Reynes static u32 fec_enet_register_offset[] = { 2198db65f35fSPhilippe Reynes FEC_ECNTRL, FEC_IEVENT, FEC_IMASK, FEC_IVEC, FEC_R_DES_ACTIVE_0, 2199db65f35fSPhilippe Reynes FEC_R_DES_ACTIVE_1, FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_0, 2200db65f35fSPhilippe Reynes FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2, FEC_MII_DATA, FEC_MII_SPEED, 2201db65f35fSPhilippe Reynes FEC_R_BOUND, FEC_R_FSTART, FEC_X_WMRK, FEC_X_FSTART, FEC_R_CNTRL, 2202db65f35fSPhilippe Reynes FEC_MAX_FRM_LEN, FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, 2203db65f35fSPhilippe Reynes FEC_GRP_HASH_TABLE_HIGH, FEC_GRP_HASH_TABLE_LOW, FEC_R_DES_START_0, 2204db65f35fSPhilippe Reynes FEC_R_DES_START_1, FEC_R_DES_START_2, FEC_X_DES_START_0, 2205db65f35fSPhilippe Reynes FEC_X_DES_START_1, FEC_X_DES_START_2, FEC_R_BUFF_SIZE_0, 2206db65f35fSPhilippe Reynes FEC_R_BUFF_SIZE_1, FEC_R_BUFF_SIZE_2 2207db65f35fSPhilippe Reynes }; 2208db65f35fSPhilippe Reynes #endif 2209db65f35fSPhilippe Reynes 2210db65f35fSPhilippe Reynes static void fec_enet_get_regs(struct net_device *ndev, 2211db65f35fSPhilippe Reynes struct ethtool_regs *regs, void *regbuf) 2212db65f35fSPhilippe Reynes { 2213db65f35fSPhilippe Reynes struct fec_enet_private *fep = netdev_priv(ndev); 2214db65f35fSPhilippe Reynes u32 __iomem *theregs = (u32 __iomem *)fep->hwp; 2215db65f35fSPhilippe Reynes u32 *buf = (u32 *)regbuf; 2216db65f35fSPhilippe Reynes u32 i, off; 2217db65f35fSPhilippe Reynes 2218db65f35fSPhilippe Reynes memset(buf, 0, regs->len); 2219db65f35fSPhilippe Reynes 2220db65f35fSPhilippe Reynes for (i = 0; i < ARRAY_SIZE(fec_enet_register_offset); i++) { 2221db65f35fSPhilippe Reynes off = fec_enet_register_offset[i] / 4; 2222db65f35fSPhilippe Reynes buf[off] = readl(&theregs[off]); 2223db65f35fSPhilippe Reynes } 2224db65f35fSPhilippe Reynes } 2225db65f35fSPhilippe Reynes 2226793fc096SFrank Li static int fec_enet_get_ts_info(struct net_device *ndev, 2227793fc096SFrank Li struct ethtool_ts_info *info) 2228793fc096SFrank Li { 2229793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2230793fc096SFrank Li 2231793fc096SFrank Li if (fep->bufdesc_ex) { 2232793fc096SFrank Li 2233793fc096SFrank Li info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | 2234793fc096SFrank Li SOF_TIMESTAMPING_RX_SOFTWARE | 2235793fc096SFrank Li SOF_TIMESTAMPING_SOFTWARE | 2236793fc096SFrank Li SOF_TIMESTAMPING_TX_HARDWARE | 2237793fc096SFrank Li SOF_TIMESTAMPING_RX_HARDWARE | 2238793fc096SFrank Li SOF_TIMESTAMPING_RAW_HARDWARE; 2239793fc096SFrank Li if (fep->ptp_clock) 2240793fc096SFrank Li info->phc_index = ptp_clock_index(fep->ptp_clock); 2241793fc096SFrank Li else 2242793fc096SFrank Li info->phc_index = -1; 2243793fc096SFrank Li 2244793fc096SFrank Li info->tx_types = (1 << HWTSTAMP_TX_OFF) | 2245793fc096SFrank Li (1 << HWTSTAMP_TX_ON); 2246793fc096SFrank Li 2247793fc096SFrank Li info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | 2248793fc096SFrank Li (1 << HWTSTAMP_FILTER_ALL); 2249793fc096SFrank Li return 0; 2250793fc096SFrank Li } else { 2251793fc096SFrank Li return ethtool_op_get_ts_info(ndev, info); 2252793fc096SFrank Li } 2253793fc096SFrank Li } 2254793fc096SFrank Li 2255d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 2256d1391930SGuenter Roeck 2257793fc096SFrank Li static void fec_enet_get_pauseparam(struct net_device *ndev, 2258793fc096SFrank Li struct ethtool_pauseparam *pause) 2259793fc096SFrank Li { 2260793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2261793fc096SFrank Li 2262793fc096SFrank Li pause->autoneg = (fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) != 0; 2263793fc096SFrank Li pause->tx_pause = (fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) != 0; 2264793fc096SFrank Li pause->rx_pause = pause->tx_pause; 2265793fc096SFrank Li } 2266793fc096SFrank Li 2267793fc096SFrank Li static int fec_enet_set_pauseparam(struct net_device *ndev, 2268793fc096SFrank Li struct ethtool_pauseparam *pause) 2269793fc096SFrank Li { 2270793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2271793fc096SFrank Li 22720b146ca8SRussell King if (!fep->phy_dev) 22730b146ca8SRussell King return -ENODEV; 22740b146ca8SRussell King 2275793fc096SFrank Li if (pause->tx_pause != pause->rx_pause) { 2276793fc096SFrank Li netdev_info(ndev, 2277793fc096SFrank Li "hardware only support enable/disable both tx and rx"); 2278793fc096SFrank Li return -EINVAL; 2279793fc096SFrank Li } 2280793fc096SFrank Li 2281793fc096SFrank Li fep->pause_flag = 0; 2282793fc096SFrank Li 2283793fc096SFrank Li /* tx pause must be same as rx pause */ 2284793fc096SFrank Li fep->pause_flag |= pause->rx_pause ? FEC_PAUSE_FLAG_ENABLE : 0; 2285793fc096SFrank Li fep->pause_flag |= pause->autoneg ? FEC_PAUSE_FLAG_AUTONEG : 0; 2286793fc096SFrank Li 2287793fc096SFrank Li if (pause->rx_pause || pause->autoneg) { 2288793fc096SFrank Li fep->phy_dev->supported |= ADVERTISED_Pause; 2289793fc096SFrank Li fep->phy_dev->advertising |= ADVERTISED_Pause; 2290793fc096SFrank Li } else { 2291793fc096SFrank Li fep->phy_dev->supported &= ~ADVERTISED_Pause; 2292793fc096SFrank Li fep->phy_dev->advertising &= ~ADVERTISED_Pause; 2293793fc096SFrank Li } 2294793fc096SFrank Li 2295793fc096SFrank Li if (pause->autoneg) { 2296793fc096SFrank Li if (netif_running(ndev)) 2297793fc096SFrank Li fec_stop(ndev); 2298793fc096SFrank Li phy_start_aneg(fep->phy_dev); 2299793fc096SFrank Li } 2300dbc64a8eSRussell King if (netif_running(ndev)) { 2301dbc64a8eSRussell King napi_disable(&fep->napi); 2302dbc64a8eSRussell King netif_tx_lock_bh(ndev); 2303ef83337dSRussell King fec_restart(ndev); 2304dbc64a8eSRussell King netif_wake_queue(ndev); 23056af42d42SRussell King netif_tx_unlock_bh(ndev); 2306dbc64a8eSRussell King napi_enable(&fep->napi); 2307dbc64a8eSRussell King } 2308793fc096SFrank Li 2309793fc096SFrank Li return 0; 2310793fc096SFrank Li } 2311793fc096SFrank Li 231238ae92dcSChris Healy static const struct fec_stat { 231338ae92dcSChris Healy char name[ETH_GSTRING_LEN]; 231438ae92dcSChris Healy u16 offset; 231538ae92dcSChris Healy } fec_stats[] = { 231638ae92dcSChris Healy /* RMON TX */ 231738ae92dcSChris Healy { "tx_dropped", RMON_T_DROP }, 231838ae92dcSChris Healy { "tx_packets", RMON_T_PACKETS }, 231938ae92dcSChris Healy { "tx_broadcast", RMON_T_BC_PKT }, 232038ae92dcSChris Healy { "tx_multicast", RMON_T_MC_PKT }, 232138ae92dcSChris Healy { "tx_crc_errors", RMON_T_CRC_ALIGN }, 232238ae92dcSChris Healy { "tx_undersize", RMON_T_UNDERSIZE }, 232338ae92dcSChris Healy { "tx_oversize", RMON_T_OVERSIZE }, 232438ae92dcSChris Healy { "tx_fragment", RMON_T_FRAG }, 232538ae92dcSChris Healy { "tx_jabber", RMON_T_JAB }, 232638ae92dcSChris Healy { "tx_collision", RMON_T_COL }, 232738ae92dcSChris Healy { "tx_64byte", RMON_T_P64 }, 232838ae92dcSChris Healy { "tx_65to127byte", RMON_T_P65TO127 }, 232938ae92dcSChris Healy { "tx_128to255byte", RMON_T_P128TO255 }, 233038ae92dcSChris Healy { "tx_256to511byte", RMON_T_P256TO511 }, 233138ae92dcSChris Healy { "tx_512to1023byte", RMON_T_P512TO1023 }, 233238ae92dcSChris Healy { "tx_1024to2047byte", RMON_T_P1024TO2047 }, 233338ae92dcSChris Healy { "tx_GTE2048byte", RMON_T_P_GTE2048 }, 233438ae92dcSChris Healy { "tx_octets", RMON_T_OCTETS }, 233538ae92dcSChris Healy 233638ae92dcSChris Healy /* IEEE TX */ 233738ae92dcSChris Healy { "IEEE_tx_drop", IEEE_T_DROP }, 233838ae92dcSChris Healy { "IEEE_tx_frame_ok", IEEE_T_FRAME_OK }, 233938ae92dcSChris Healy { "IEEE_tx_1col", IEEE_T_1COL }, 234038ae92dcSChris Healy { "IEEE_tx_mcol", IEEE_T_MCOL }, 234138ae92dcSChris Healy { "IEEE_tx_def", IEEE_T_DEF }, 234238ae92dcSChris Healy { "IEEE_tx_lcol", IEEE_T_LCOL }, 234338ae92dcSChris Healy { "IEEE_tx_excol", IEEE_T_EXCOL }, 234438ae92dcSChris Healy { "IEEE_tx_macerr", IEEE_T_MACERR }, 234538ae92dcSChris Healy { "IEEE_tx_cserr", IEEE_T_CSERR }, 234638ae92dcSChris Healy { "IEEE_tx_sqe", IEEE_T_SQE }, 234738ae92dcSChris Healy { "IEEE_tx_fdxfc", IEEE_T_FDXFC }, 234838ae92dcSChris Healy { "IEEE_tx_octets_ok", IEEE_T_OCTETS_OK }, 234938ae92dcSChris Healy 235038ae92dcSChris Healy /* RMON RX */ 235138ae92dcSChris Healy { "rx_packets", RMON_R_PACKETS }, 235238ae92dcSChris Healy { "rx_broadcast", RMON_R_BC_PKT }, 235338ae92dcSChris Healy { "rx_multicast", RMON_R_MC_PKT }, 235438ae92dcSChris Healy { "rx_crc_errors", RMON_R_CRC_ALIGN }, 235538ae92dcSChris Healy { "rx_undersize", RMON_R_UNDERSIZE }, 235638ae92dcSChris Healy { "rx_oversize", RMON_R_OVERSIZE }, 235738ae92dcSChris Healy { "rx_fragment", RMON_R_FRAG }, 235838ae92dcSChris Healy { "rx_jabber", RMON_R_JAB }, 235938ae92dcSChris Healy { "rx_64byte", RMON_R_P64 }, 236038ae92dcSChris Healy { "rx_65to127byte", RMON_R_P65TO127 }, 236138ae92dcSChris Healy { "rx_128to255byte", RMON_R_P128TO255 }, 236238ae92dcSChris Healy { "rx_256to511byte", RMON_R_P256TO511 }, 236338ae92dcSChris Healy { "rx_512to1023byte", RMON_R_P512TO1023 }, 236438ae92dcSChris Healy { "rx_1024to2047byte", RMON_R_P1024TO2047 }, 236538ae92dcSChris Healy { "rx_GTE2048byte", RMON_R_P_GTE2048 }, 236638ae92dcSChris Healy { "rx_octets", RMON_R_OCTETS }, 236738ae92dcSChris Healy 236838ae92dcSChris Healy /* IEEE RX */ 236938ae92dcSChris Healy { "IEEE_rx_drop", IEEE_R_DROP }, 237038ae92dcSChris Healy { "IEEE_rx_frame_ok", IEEE_R_FRAME_OK }, 237138ae92dcSChris Healy { "IEEE_rx_crc", IEEE_R_CRC }, 237238ae92dcSChris Healy { "IEEE_rx_align", IEEE_R_ALIGN }, 237338ae92dcSChris Healy { "IEEE_rx_macerr", IEEE_R_MACERR }, 237438ae92dcSChris Healy { "IEEE_rx_fdxfc", IEEE_R_FDXFC }, 237538ae92dcSChris Healy { "IEEE_rx_octets_ok", IEEE_R_OCTETS_OK }, 237638ae92dcSChris Healy }; 237738ae92dcSChris Healy 237838ae92dcSChris Healy static void fec_enet_get_ethtool_stats(struct net_device *dev, 237938ae92dcSChris Healy struct ethtool_stats *stats, u64 *data) 238038ae92dcSChris Healy { 238138ae92dcSChris Healy struct fec_enet_private *fep = netdev_priv(dev); 238238ae92dcSChris Healy int i; 238338ae92dcSChris Healy 238438ae92dcSChris Healy for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 238538ae92dcSChris Healy data[i] = readl(fep->hwp + fec_stats[i].offset); 238638ae92dcSChris Healy } 238738ae92dcSChris Healy 238838ae92dcSChris Healy static void fec_enet_get_strings(struct net_device *netdev, 238938ae92dcSChris Healy u32 stringset, u8 *data) 239038ae92dcSChris Healy { 239138ae92dcSChris Healy int i; 239238ae92dcSChris Healy switch (stringset) { 239338ae92dcSChris Healy case ETH_SS_STATS: 239438ae92dcSChris Healy for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 239538ae92dcSChris Healy memcpy(data + i * ETH_GSTRING_LEN, 239638ae92dcSChris Healy fec_stats[i].name, ETH_GSTRING_LEN); 239738ae92dcSChris Healy break; 239838ae92dcSChris Healy } 239938ae92dcSChris Healy } 240038ae92dcSChris Healy 240138ae92dcSChris Healy static int fec_enet_get_sset_count(struct net_device *dev, int sset) 240238ae92dcSChris Healy { 240338ae92dcSChris Healy switch (sset) { 240438ae92dcSChris Healy case ETH_SS_STATS: 240538ae92dcSChris Healy return ARRAY_SIZE(fec_stats); 240638ae92dcSChris Healy default: 240738ae92dcSChris Healy return -EOPNOTSUPP; 240838ae92dcSChris Healy } 240938ae92dcSChris Healy } 2410d1391930SGuenter Roeck #endif /* !defined(CONFIG_M5272) */ 241138ae92dcSChris Healy 241232bc9b46SChris Healy static int fec_enet_nway_reset(struct net_device *dev) 241332bc9b46SChris Healy { 241432bc9b46SChris Healy struct fec_enet_private *fep = netdev_priv(dev); 241532bc9b46SChris Healy struct phy_device *phydev = fep->phy_dev; 241632bc9b46SChris Healy 241732bc9b46SChris Healy if (!phydev) 241832bc9b46SChris Healy return -ENODEV; 241932bc9b46SChris Healy 242032bc9b46SChris Healy return genphy_restart_aneg(phydev); 242132bc9b46SChris Healy } 242232bc9b46SChris Healy 2423d851b47bSFugang Duan /* ITR clock source is enet system clock (clk_ahb). 2424d851b47bSFugang Duan * TCTT unit is cycle_ns * 64 cycle 2425d851b47bSFugang Duan * So, the ICTT value = X us / (cycle_ns * 64) 2426d851b47bSFugang Duan */ 2427d851b47bSFugang Duan static int fec_enet_us_to_itr_clock(struct net_device *ndev, int us) 2428d851b47bSFugang Duan { 2429d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2430d851b47bSFugang Duan 2431d851b47bSFugang Duan return us * (fep->itr_clk_rate / 64000) / 1000; 2432d851b47bSFugang Duan } 2433d851b47bSFugang Duan 2434d851b47bSFugang Duan /* Set threshold for interrupt coalescing */ 2435d851b47bSFugang Duan static void fec_enet_itr_coal_set(struct net_device *ndev) 2436d851b47bSFugang Duan { 2437d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2438d851b47bSFugang Duan int rx_itr, tx_itr; 2439d851b47bSFugang Duan 24406b7e4008SLothar Waßmann if (!(fep->quirks & FEC_QUIRK_HAS_AVB)) 2441d851b47bSFugang Duan return; 2442d851b47bSFugang Duan 2443d851b47bSFugang Duan /* Must be greater than zero to avoid unpredictable behavior */ 2444d851b47bSFugang Duan if (!fep->rx_time_itr || !fep->rx_pkts_itr || 2445d851b47bSFugang Duan !fep->tx_time_itr || !fep->tx_pkts_itr) 2446d851b47bSFugang Duan return; 2447d851b47bSFugang Duan 2448d851b47bSFugang Duan /* Select enet system clock as Interrupt Coalescing 2449d851b47bSFugang Duan * timer Clock Source 2450d851b47bSFugang Duan */ 2451d851b47bSFugang Duan rx_itr = FEC_ITR_CLK_SEL; 2452d851b47bSFugang Duan tx_itr = FEC_ITR_CLK_SEL; 2453d851b47bSFugang Duan 2454d851b47bSFugang Duan /* set ICFT and ICTT */ 2455d851b47bSFugang Duan rx_itr |= FEC_ITR_ICFT(fep->rx_pkts_itr); 2456d851b47bSFugang Duan rx_itr |= FEC_ITR_ICTT(fec_enet_us_to_itr_clock(ndev, fep->rx_time_itr)); 2457d851b47bSFugang Duan tx_itr |= FEC_ITR_ICFT(fep->tx_pkts_itr); 2458d851b47bSFugang Duan tx_itr |= FEC_ITR_ICTT(fec_enet_us_to_itr_clock(ndev, fep->tx_time_itr)); 2459d851b47bSFugang Duan 2460d851b47bSFugang Duan rx_itr |= FEC_ITR_EN; 2461d851b47bSFugang Duan tx_itr |= FEC_ITR_EN; 2462d851b47bSFugang Duan 2463d851b47bSFugang Duan writel(tx_itr, fep->hwp + FEC_TXIC0); 2464d851b47bSFugang Duan writel(rx_itr, fep->hwp + FEC_RXIC0); 2465d851b47bSFugang Duan writel(tx_itr, fep->hwp + FEC_TXIC1); 2466d851b47bSFugang Duan writel(rx_itr, fep->hwp + FEC_RXIC1); 2467d851b47bSFugang Duan writel(tx_itr, fep->hwp + FEC_TXIC2); 2468d851b47bSFugang Duan writel(rx_itr, fep->hwp + FEC_RXIC2); 2469d851b47bSFugang Duan } 2470d851b47bSFugang Duan 2471d851b47bSFugang Duan static int 2472d851b47bSFugang Duan fec_enet_get_coalesce(struct net_device *ndev, struct ethtool_coalesce *ec) 2473d851b47bSFugang Duan { 2474d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2475d851b47bSFugang Duan 24766b7e4008SLothar Waßmann if (!(fep->quirks & FEC_QUIRK_HAS_AVB)) 2477d851b47bSFugang Duan return -EOPNOTSUPP; 2478d851b47bSFugang Duan 2479d851b47bSFugang Duan ec->rx_coalesce_usecs = fep->rx_time_itr; 2480d851b47bSFugang Duan ec->rx_max_coalesced_frames = fep->rx_pkts_itr; 2481d851b47bSFugang Duan 2482d851b47bSFugang Duan ec->tx_coalesce_usecs = fep->tx_time_itr; 2483d851b47bSFugang Duan ec->tx_max_coalesced_frames = fep->tx_pkts_itr; 2484d851b47bSFugang Duan 2485d851b47bSFugang Duan return 0; 2486d851b47bSFugang Duan } 2487d851b47bSFugang Duan 2488d851b47bSFugang Duan static int 2489d851b47bSFugang Duan fec_enet_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *ec) 2490d851b47bSFugang Duan { 2491d851b47bSFugang Duan struct fec_enet_private *fep = netdev_priv(ndev); 2492d851b47bSFugang Duan unsigned int cycle; 2493d851b47bSFugang Duan 24946b7e4008SLothar Waßmann if (!(fep->quirks & FEC_QUIRK_HAS_AVB)) 2495d851b47bSFugang Duan return -EOPNOTSUPP; 2496d851b47bSFugang Duan 2497d851b47bSFugang Duan if (ec->rx_max_coalesced_frames > 255) { 2498d851b47bSFugang Duan pr_err("Rx coalesced frames exceed hardware limiation"); 2499d851b47bSFugang Duan return -EINVAL; 2500d851b47bSFugang Duan } 2501d851b47bSFugang Duan 2502d851b47bSFugang Duan if (ec->tx_max_coalesced_frames > 255) { 2503d851b47bSFugang Duan pr_err("Tx coalesced frame exceed hardware limiation"); 2504d851b47bSFugang Duan return -EINVAL; 2505d851b47bSFugang Duan } 2506d851b47bSFugang Duan 2507d851b47bSFugang Duan cycle = fec_enet_us_to_itr_clock(ndev, fep->rx_time_itr); 2508d851b47bSFugang Duan if (cycle > 0xFFFF) { 2509d851b47bSFugang Duan pr_err("Rx coalesed usec exceeed hardware limiation"); 2510d851b47bSFugang Duan return -EINVAL; 2511d851b47bSFugang Duan } 2512d851b47bSFugang Duan 2513d851b47bSFugang Duan cycle = fec_enet_us_to_itr_clock(ndev, fep->tx_time_itr); 2514d851b47bSFugang Duan if (cycle > 0xFFFF) { 2515d851b47bSFugang Duan pr_err("Rx coalesed usec exceeed hardware limiation"); 2516d851b47bSFugang Duan return -EINVAL; 2517d851b47bSFugang Duan } 2518d851b47bSFugang Duan 2519d851b47bSFugang Duan fep->rx_time_itr = ec->rx_coalesce_usecs; 2520d851b47bSFugang Duan fep->rx_pkts_itr = ec->rx_max_coalesced_frames; 2521d851b47bSFugang Duan 2522d851b47bSFugang Duan fep->tx_time_itr = ec->tx_coalesce_usecs; 2523d851b47bSFugang Duan fep->tx_pkts_itr = ec->tx_max_coalesced_frames; 2524d851b47bSFugang Duan 2525d851b47bSFugang Duan fec_enet_itr_coal_set(ndev); 2526d851b47bSFugang Duan 2527d851b47bSFugang Duan return 0; 2528d851b47bSFugang Duan } 2529d851b47bSFugang Duan 2530d851b47bSFugang Duan static void fec_enet_itr_coal_init(struct net_device *ndev) 2531d851b47bSFugang Duan { 2532d851b47bSFugang Duan struct ethtool_coalesce ec; 2533d851b47bSFugang Duan 2534d851b47bSFugang Duan ec.rx_coalesce_usecs = FEC_ITR_ICTT_DEFAULT; 2535d851b47bSFugang Duan ec.rx_max_coalesced_frames = FEC_ITR_ICFT_DEFAULT; 2536d851b47bSFugang Duan 2537d851b47bSFugang Duan ec.tx_coalesce_usecs = FEC_ITR_ICTT_DEFAULT; 2538d851b47bSFugang Duan ec.tx_max_coalesced_frames = FEC_ITR_ICFT_DEFAULT; 2539d851b47bSFugang Duan 2540d851b47bSFugang Duan fec_enet_set_coalesce(ndev, &ec); 2541d851b47bSFugang Duan } 2542d851b47bSFugang Duan 25431b7bde6dSNimrod Andy static int fec_enet_get_tunable(struct net_device *netdev, 25441b7bde6dSNimrod Andy const struct ethtool_tunable *tuna, 25451b7bde6dSNimrod Andy void *data) 25461b7bde6dSNimrod Andy { 25471b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(netdev); 25481b7bde6dSNimrod Andy int ret = 0; 25491b7bde6dSNimrod Andy 25501b7bde6dSNimrod Andy switch (tuna->id) { 25511b7bde6dSNimrod Andy case ETHTOOL_RX_COPYBREAK: 25521b7bde6dSNimrod Andy *(u32 *)data = fep->rx_copybreak; 25531b7bde6dSNimrod Andy break; 25541b7bde6dSNimrod Andy default: 25551b7bde6dSNimrod Andy ret = -EINVAL; 25561b7bde6dSNimrod Andy break; 25571b7bde6dSNimrod Andy } 25581b7bde6dSNimrod Andy 25591b7bde6dSNimrod Andy return ret; 25601b7bde6dSNimrod Andy } 25611b7bde6dSNimrod Andy 25621b7bde6dSNimrod Andy static int fec_enet_set_tunable(struct net_device *netdev, 25631b7bde6dSNimrod Andy const struct ethtool_tunable *tuna, 25641b7bde6dSNimrod Andy const void *data) 25651b7bde6dSNimrod Andy { 25661b7bde6dSNimrod Andy struct fec_enet_private *fep = netdev_priv(netdev); 25671b7bde6dSNimrod Andy int ret = 0; 25681b7bde6dSNimrod Andy 25691b7bde6dSNimrod Andy switch (tuna->id) { 25701b7bde6dSNimrod Andy case ETHTOOL_RX_COPYBREAK: 25711b7bde6dSNimrod Andy fep->rx_copybreak = *(u32 *)data; 25721b7bde6dSNimrod Andy break; 25731b7bde6dSNimrod Andy default: 25741b7bde6dSNimrod Andy ret = -EINVAL; 25751b7bde6dSNimrod Andy break; 25761b7bde6dSNimrod Andy } 25771b7bde6dSNimrod Andy 25781b7bde6dSNimrod Andy return ret; 25791b7bde6dSNimrod Andy } 25801b7bde6dSNimrod Andy 2581de40ed31SNimrod Andy static void 2582de40ed31SNimrod Andy fec_enet_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) 2583de40ed31SNimrod Andy { 2584de40ed31SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 2585de40ed31SNimrod Andy 2586de40ed31SNimrod Andy if (fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET) { 2587de40ed31SNimrod Andy wol->supported = WAKE_MAGIC; 2588de40ed31SNimrod Andy wol->wolopts = fep->wol_flag & FEC_WOL_FLAG_ENABLE ? WAKE_MAGIC : 0; 2589de40ed31SNimrod Andy } else { 2590de40ed31SNimrod Andy wol->supported = wol->wolopts = 0; 2591de40ed31SNimrod Andy } 2592de40ed31SNimrod Andy } 2593de40ed31SNimrod Andy 2594de40ed31SNimrod Andy static int 2595de40ed31SNimrod Andy fec_enet_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) 2596de40ed31SNimrod Andy { 2597de40ed31SNimrod Andy struct fec_enet_private *fep = netdev_priv(ndev); 2598de40ed31SNimrod Andy 2599de40ed31SNimrod Andy if (!(fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET)) 2600de40ed31SNimrod Andy return -EINVAL; 2601de40ed31SNimrod Andy 2602de40ed31SNimrod Andy if (wol->wolopts & ~WAKE_MAGIC) 2603de40ed31SNimrod Andy return -EINVAL; 2604de40ed31SNimrod Andy 2605de40ed31SNimrod Andy device_set_wakeup_enable(&ndev->dev, wol->wolopts & WAKE_MAGIC); 2606de40ed31SNimrod Andy if (device_may_wakeup(&ndev->dev)) { 2607de40ed31SNimrod Andy fep->wol_flag |= FEC_WOL_FLAG_ENABLE; 2608de40ed31SNimrod Andy if (fep->irq[0] > 0) 2609de40ed31SNimrod Andy enable_irq_wake(fep->irq[0]); 2610de40ed31SNimrod Andy } else { 2611de40ed31SNimrod Andy fep->wol_flag &= (~FEC_WOL_FLAG_ENABLE); 2612de40ed31SNimrod Andy if (fep->irq[0] > 0) 2613de40ed31SNimrod Andy disable_irq_wake(fep->irq[0]); 2614de40ed31SNimrod Andy } 2615de40ed31SNimrod Andy 2616de40ed31SNimrod Andy return 0; 2617de40ed31SNimrod Andy } 2618de40ed31SNimrod Andy 2619793fc096SFrank Li static const struct ethtool_ops fec_enet_ethtool_ops = { 2620793fc096SFrank Li .get_settings = fec_enet_get_settings, 2621793fc096SFrank Li .set_settings = fec_enet_set_settings, 2622793fc096SFrank Li .get_drvinfo = fec_enet_get_drvinfo, 2623db65f35fSPhilippe Reynes .get_regs_len = fec_enet_get_regs_len, 2624db65f35fSPhilippe Reynes .get_regs = fec_enet_get_regs, 262532bc9b46SChris Healy .nway_reset = fec_enet_nway_reset, 2626c1d7c48fSRussell King .get_link = ethtool_op_get_link, 2627d851b47bSFugang Duan .get_coalesce = fec_enet_get_coalesce, 2628d851b47bSFugang Duan .set_coalesce = fec_enet_set_coalesce, 262938ae92dcSChris Healy #ifndef CONFIG_M5272 2630c1d7c48fSRussell King .get_pauseparam = fec_enet_get_pauseparam, 2631c1d7c48fSRussell King .set_pauseparam = fec_enet_set_pauseparam, 263238ae92dcSChris Healy .get_strings = fec_enet_get_strings, 2633c1d7c48fSRussell King .get_ethtool_stats = fec_enet_get_ethtool_stats, 263438ae92dcSChris Healy .get_sset_count = fec_enet_get_sset_count, 263538ae92dcSChris Healy #endif 2636c1d7c48fSRussell King .get_ts_info = fec_enet_get_ts_info, 26371b7bde6dSNimrod Andy .get_tunable = fec_enet_get_tunable, 26381b7bde6dSNimrod Andy .set_tunable = fec_enet_set_tunable, 2639de40ed31SNimrod Andy .get_wol = fec_enet_get_wol, 2640de40ed31SNimrod Andy .set_wol = fec_enet_set_wol, 2641793fc096SFrank Li }; 2642793fc096SFrank Li 2643793fc096SFrank Li static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) 2644793fc096SFrank Li { 2645793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2646793fc096SFrank Li struct phy_device *phydev = fep->phy_dev; 2647793fc096SFrank Li 2648793fc096SFrank Li if (!netif_running(ndev)) 2649793fc096SFrank Li return -EINVAL; 2650793fc096SFrank Li 2651793fc096SFrank Li if (!phydev) 2652793fc096SFrank Li return -ENODEV; 2653793fc096SFrank Li 26541d5244d0SBen Hutchings if (fep->bufdesc_ex) { 26551d5244d0SBen Hutchings if (cmd == SIOCSHWTSTAMP) 26561d5244d0SBen Hutchings return fec_ptp_set(ndev, rq); 26571d5244d0SBen Hutchings if (cmd == SIOCGHWTSTAMP) 26581d5244d0SBen Hutchings return fec_ptp_get(ndev, rq); 26591d5244d0SBen Hutchings } 2660793fc096SFrank Li 2661793fc096SFrank Li return phy_mii_ioctl(phydev, rq, cmd); 2662793fc096SFrank Li } 2663793fc096SFrank Li 2664793fc096SFrank Li static void fec_enet_free_buffers(struct net_device *ndev) 2665793fc096SFrank Li { 2666793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2667793fc096SFrank Li unsigned int i; 2668793fc096SFrank Li struct sk_buff *skb; 2669793fc096SFrank Li struct bufdesc *bdp; 26704d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 26714d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 267259d0f746SFrank Li unsigned int q; 2673793fc096SFrank Li 267459d0f746SFrank Li for (q = 0; q < fep->num_rx_queues; q++) { 267559d0f746SFrank Li rxq = fep->rx_queue[q]; 26764d494cdcSFugang Duan bdp = rxq->rx_bd_base; 26774d494cdcSFugang Duan for (i = 0; i < rxq->rx_ring_size; i++) { 26784d494cdcSFugang Duan skb = rxq->rx_skbuff[i]; 26794d494cdcSFugang Duan rxq->rx_skbuff[i] = NULL; 2680730ee360SRussell King if (skb) { 268159d0f746SFrank Li dma_unmap_single(&fep->pdev->dev, 268259d0f746SFrank Li bdp->cbd_bufaddr, 2683b64bf4b7SNimrod Andy FEC_ENET_RX_FRSIZE - fep->rx_align, 268459d0f746SFrank Li DMA_FROM_DEVICE); 2685793fc096SFrank Li dev_kfree_skb(skb); 2686730ee360SRussell King } 268759d0f746SFrank Li bdp = fec_enet_get_nextdesc(bdp, fep, q); 268859d0f746SFrank Li } 2689793fc096SFrank Li } 2690793fc096SFrank Li 269159d0f746SFrank Li for (q = 0; q < fep->num_tx_queues; q++) { 269259d0f746SFrank Li txq = fep->tx_queue[q]; 26934d494cdcSFugang Duan bdp = txq->tx_bd_base; 26944d494cdcSFugang Duan for (i = 0; i < txq->tx_ring_size; i++) { 26954d494cdcSFugang Duan kfree(txq->tx_bounce[i]); 26964d494cdcSFugang Duan txq->tx_bounce[i] = NULL; 26974d494cdcSFugang Duan skb = txq->tx_skbuff[i]; 26984d494cdcSFugang Duan txq->tx_skbuff[i] = NULL; 26998b7c9efaSRussell King dev_kfree_skb(skb); 27008b7c9efaSRussell King } 2701793fc096SFrank Li } 270259d0f746SFrank Li } 2703793fc096SFrank Li 270459d0f746SFrank Li static void fec_enet_free_queue(struct net_device *ndev) 270559d0f746SFrank Li { 270659d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 270759d0f746SFrank Li int i; 270859d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 270959d0f746SFrank Li 271059d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) 271159d0f746SFrank Li if (fep->tx_queue[i] && fep->tx_queue[i]->tso_hdrs) { 271259d0f746SFrank Li txq = fep->tx_queue[i]; 271359d0f746SFrank Li dma_free_coherent(NULL, 271459d0f746SFrank Li txq->tx_ring_size * TSO_HEADER_SIZE, 271559d0f746SFrank Li txq->tso_hdrs, 271659d0f746SFrank Li txq->tso_hdrs_dma); 271759d0f746SFrank Li } 271859d0f746SFrank Li 271959d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) 272059d0f746SFrank Li kfree(fep->rx_queue[i]); 272159d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) 272259d0f746SFrank Li kfree(fep->tx_queue[i]); 272359d0f746SFrank Li } 272459d0f746SFrank Li 272559d0f746SFrank Li static int fec_enet_alloc_queue(struct net_device *ndev) 272659d0f746SFrank Li { 272759d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 272859d0f746SFrank Li int i; 272959d0f746SFrank Li int ret = 0; 273059d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 273159d0f746SFrank Li 273259d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 273359d0f746SFrank Li txq = kzalloc(sizeof(*txq), GFP_KERNEL); 273459d0f746SFrank Li if (!txq) { 273559d0f746SFrank Li ret = -ENOMEM; 273659d0f746SFrank Li goto alloc_failed; 273759d0f746SFrank Li } 273859d0f746SFrank Li 273959d0f746SFrank Li fep->tx_queue[i] = txq; 274059d0f746SFrank Li txq->tx_ring_size = TX_RING_SIZE; 274159d0f746SFrank Li fep->total_tx_ring_size += fep->tx_queue[i]->tx_ring_size; 274259d0f746SFrank Li 274359d0f746SFrank Li txq->tx_stop_threshold = FEC_MAX_SKB_DESCS; 274459d0f746SFrank Li txq->tx_wake_threshold = 274559d0f746SFrank Li (txq->tx_ring_size - txq->tx_stop_threshold) / 2; 274659d0f746SFrank Li 274759d0f746SFrank Li txq->tso_hdrs = dma_alloc_coherent(NULL, 274859d0f746SFrank Li txq->tx_ring_size * TSO_HEADER_SIZE, 274959d0f746SFrank Li &txq->tso_hdrs_dma, 275059d0f746SFrank Li GFP_KERNEL); 275159d0f746SFrank Li if (!txq->tso_hdrs) { 275259d0f746SFrank Li ret = -ENOMEM; 275359d0f746SFrank Li goto alloc_failed; 275459d0f746SFrank Li } 275559d0f746SFrank Li } 275659d0f746SFrank Li 275759d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) { 275859d0f746SFrank Li fep->rx_queue[i] = kzalloc(sizeof(*fep->rx_queue[i]), 275959d0f746SFrank Li GFP_KERNEL); 276059d0f746SFrank Li if (!fep->rx_queue[i]) { 276159d0f746SFrank Li ret = -ENOMEM; 276259d0f746SFrank Li goto alloc_failed; 276359d0f746SFrank Li } 276459d0f746SFrank Li 276559d0f746SFrank Li fep->rx_queue[i]->rx_ring_size = RX_RING_SIZE; 276659d0f746SFrank Li fep->total_rx_ring_size += fep->rx_queue[i]->rx_ring_size; 276759d0f746SFrank Li } 276859d0f746SFrank Li return ret; 276959d0f746SFrank Li 277059d0f746SFrank Li alloc_failed: 277159d0f746SFrank Li fec_enet_free_queue(ndev); 277259d0f746SFrank Li return ret; 277359d0f746SFrank Li } 277459d0f746SFrank Li 277559d0f746SFrank Li static int 277659d0f746SFrank Li fec_enet_alloc_rxq_buffers(struct net_device *ndev, unsigned int queue) 2777793fc096SFrank Li { 2778793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2779793fc096SFrank Li unsigned int i; 2780793fc096SFrank Li struct sk_buff *skb; 2781793fc096SFrank Li struct bufdesc *bdp; 27824d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 2783793fc096SFrank Li 278459d0f746SFrank Li rxq = fep->rx_queue[queue]; 27854d494cdcSFugang Duan bdp = rxq->rx_bd_base; 27864d494cdcSFugang Duan for (i = 0; i < rxq->rx_ring_size; i++) { 2787793fc096SFrank Li skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE); 2788ffdce2ccSRussell King if (!skb) 2789ffdce2ccSRussell King goto err_alloc; 2790793fc096SFrank Li 27911b7bde6dSNimrod Andy if (fec_enet_new_rxbdp(ndev, bdp, skb)) { 2792730ee360SRussell King dev_kfree_skb(skb); 2793ffdce2ccSRussell King goto err_alloc; 2794d842a31fSDuan Fugang-B38611 } 2795730ee360SRussell King 27964d494cdcSFugang Duan rxq->rx_skbuff[i] = skb; 2797793fc096SFrank Li bdp->cbd_sc = BD_ENET_RX_EMPTY; 2798793fc096SFrank Li 2799793fc096SFrank Li if (fep->bufdesc_ex) { 2800793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 2801793fc096SFrank Li ebdp->cbd_esc = BD_ENET_RX_INT; 2802793fc096SFrank Li } 2803793fc096SFrank Li 280459d0f746SFrank Li bdp = fec_enet_get_nextdesc(bdp, fep, queue); 2805793fc096SFrank Li } 2806793fc096SFrank Li 2807793fc096SFrank Li /* Set the last buffer to wrap. */ 280859d0f746SFrank Li bdp = fec_enet_get_prevdesc(bdp, fep, queue); 2809793fc096SFrank Li bdp->cbd_sc |= BD_SC_WRAP; 281059d0f746SFrank Li return 0; 2811793fc096SFrank Li 281259d0f746SFrank Li err_alloc: 281359d0f746SFrank Li fec_enet_free_buffers(ndev); 281459d0f746SFrank Li return -ENOMEM; 281559d0f746SFrank Li } 281659d0f746SFrank Li 281759d0f746SFrank Li static int 281859d0f746SFrank Li fec_enet_alloc_txq_buffers(struct net_device *ndev, unsigned int queue) 281959d0f746SFrank Li { 282059d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 282159d0f746SFrank Li unsigned int i; 282259d0f746SFrank Li struct bufdesc *bdp; 282359d0f746SFrank Li struct fec_enet_priv_tx_q *txq; 282459d0f746SFrank Li 282559d0f746SFrank Li txq = fep->tx_queue[queue]; 28264d494cdcSFugang Duan bdp = txq->tx_bd_base; 28274d494cdcSFugang Duan for (i = 0; i < txq->tx_ring_size; i++) { 28284d494cdcSFugang Duan txq->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL); 28294d494cdcSFugang Duan if (!txq->tx_bounce[i]) 2830ffdce2ccSRussell King goto err_alloc; 2831793fc096SFrank Li 2832793fc096SFrank Li bdp->cbd_sc = 0; 2833793fc096SFrank Li bdp->cbd_bufaddr = 0; 2834793fc096SFrank Li 2835793fc096SFrank Li if (fep->bufdesc_ex) { 2836793fc096SFrank Li struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 283796d2222bSJim Baxter ebdp->cbd_esc = BD_ENET_TX_INT; 2838793fc096SFrank Li } 2839793fc096SFrank Li 284059d0f746SFrank Li bdp = fec_enet_get_nextdesc(bdp, fep, queue); 2841793fc096SFrank Li } 2842793fc096SFrank Li 2843793fc096SFrank Li /* Set the last buffer to wrap. */ 284459d0f746SFrank Li bdp = fec_enet_get_prevdesc(bdp, fep, queue); 2845793fc096SFrank Li bdp->cbd_sc |= BD_SC_WRAP; 2846793fc096SFrank Li 2847793fc096SFrank Li return 0; 2848ffdce2ccSRussell King 2849ffdce2ccSRussell King err_alloc: 2850ffdce2ccSRussell King fec_enet_free_buffers(ndev); 2851ffdce2ccSRussell King return -ENOMEM; 2852793fc096SFrank Li } 2853793fc096SFrank Li 285459d0f746SFrank Li static int fec_enet_alloc_buffers(struct net_device *ndev) 285559d0f746SFrank Li { 285659d0f746SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 285759d0f746SFrank Li unsigned int i; 285859d0f746SFrank Li 285959d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) 286059d0f746SFrank Li if (fec_enet_alloc_rxq_buffers(ndev, i)) 286159d0f746SFrank Li return -ENOMEM; 286259d0f746SFrank Li 286359d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) 286459d0f746SFrank Li if (fec_enet_alloc_txq_buffers(ndev, i)) 286559d0f746SFrank Li return -ENOMEM; 286659d0f746SFrank Li return 0; 286759d0f746SFrank Li } 286859d0f746SFrank Li 2869793fc096SFrank Li static int 2870793fc096SFrank Li fec_enet_open(struct net_device *ndev) 2871793fc096SFrank Li { 2872793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2873793fc096SFrank Li int ret; 2874793fc096SFrank Li 28758fff755eSAndrew Lunn ret = pm_runtime_get_sync(&fep->pdev->dev); 2876b0c6ce24SFabio Estevam if (ret < 0) 28778fff755eSAndrew Lunn return ret; 28788fff755eSAndrew Lunn 28795bbde4d2SNimrod Andy pinctrl_pm_select_default_state(&fep->pdev->dev); 2880e8fcfcd5SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 2881e8fcfcd5SNimrod Andy if (ret) 28828fff755eSAndrew Lunn goto clk_enable; 2883e8fcfcd5SNimrod Andy 2884793fc096SFrank Li /* I should reset the ring buffers here, but I don't yet know 2885793fc096SFrank Li * a simple way to do that. 2886793fc096SFrank Li */ 2887793fc096SFrank Li 2888793fc096SFrank Li ret = fec_enet_alloc_buffers(ndev); 2889793fc096SFrank Li if (ret) 2890681d2421SFabio Estevam goto err_enet_alloc; 2891793fc096SFrank Li 289255dd2753SNimrod Andy /* Init MAC prior to mii bus probe */ 289355dd2753SNimrod Andy fec_restart(ndev); 289455dd2753SNimrod Andy 2895793fc096SFrank Li /* Probe and connect to PHY when open the interface */ 2896793fc096SFrank Li ret = fec_enet_mii_probe(ndev); 2897681d2421SFabio Estevam if (ret) 2898681d2421SFabio Estevam goto err_enet_mii_probe; 2899ce5eaf02SRussell King 2900ce5eaf02SRussell King napi_enable(&fep->napi); 2901793fc096SFrank Li phy_start(fep->phy_dev); 29024d494cdcSFugang Duan netif_tx_start_all_queues(ndev); 29034d494cdcSFugang Duan 2904de40ed31SNimrod Andy device_set_wakeup_enable(&ndev->dev, fep->wol_flag & 2905de40ed31SNimrod Andy FEC_WOL_FLAG_ENABLE); 2906de40ed31SNimrod Andy 2907793fc096SFrank Li return 0; 2908681d2421SFabio Estevam 2909681d2421SFabio Estevam err_enet_mii_probe: 2910681d2421SFabio Estevam fec_enet_free_buffers(ndev); 2911681d2421SFabio Estevam err_enet_alloc: 2912681d2421SFabio Estevam fec_enet_clk_enable(ndev, false); 29138fff755eSAndrew Lunn clk_enable: 29148fff755eSAndrew Lunn pm_runtime_mark_last_busy(&fep->pdev->dev); 29158fff755eSAndrew Lunn pm_runtime_put_autosuspend(&fep->pdev->dev); 2916681d2421SFabio Estevam pinctrl_pm_select_sleep_state(&fep->pdev->dev); 2917681d2421SFabio Estevam return ret; 2918793fc096SFrank Li } 2919793fc096SFrank Li 2920793fc096SFrank Li static int 2921793fc096SFrank Li fec_enet_close(struct net_device *ndev) 2922793fc096SFrank Li { 2923793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2924793fc096SFrank Li 2925d76cfae9SRussell King phy_stop(fep->phy_dev); 2926d76cfae9SRussell King 292731a6de34SRussell King if (netif_device_present(ndev)) { 2928793fc096SFrank Li napi_disable(&fep->napi); 2929b49cd504SRussell King netif_tx_disable(ndev); 2930793fc096SFrank Li fec_stop(ndev); 293131a6de34SRussell King } 2932793fc096SFrank Li 2933793fc096SFrank Li phy_disconnect(fep->phy_dev); 29340b146ca8SRussell King fep->phy_dev = NULL; 2935793fc096SFrank Li 2936e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 29375bbde4d2SNimrod Andy pinctrl_pm_select_sleep_state(&fep->pdev->dev); 29388fff755eSAndrew Lunn pm_runtime_mark_last_busy(&fep->pdev->dev); 29398fff755eSAndrew Lunn pm_runtime_put_autosuspend(&fep->pdev->dev); 29408fff755eSAndrew Lunn 2941793fc096SFrank Li fec_enet_free_buffers(ndev); 2942793fc096SFrank Li 2943793fc096SFrank Li return 0; 2944793fc096SFrank Li } 2945793fc096SFrank Li 2946793fc096SFrank Li /* Set or clear the multicast filter for this adaptor. 2947793fc096SFrank Li * Skeleton taken from sunlance driver. 2948793fc096SFrank Li * The CPM Ethernet implementation allows Multicast as well as individual 2949793fc096SFrank Li * MAC address filtering. Some of the drivers check to make sure it is 2950793fc096SFrank Li * a group multicast address, and discard those that are not. I guess I 2951793fc096SFrank Li * will do the same for now, but just remove the test if you want 2952793fc096SFrank Li * individual filtering as well (do the upper net layers want or support 2953793fc096SFrank Li * this kind of feature?). 2954793fc096SFrank Li */ 2955793fc096SFrank Li 2956793fc096SFrank Li #define HASH_BITS 6 /* #bits in hash */ 2957793fc096SFrank Li #define CRC32_POLY 0xEDB88320 2958793fc096SFrank Li 2959793fc096SFrank Li static void set_multicast_list(struct net_device *ndev) 2960793fc096SFrank Li { 2961793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 2962793fc096SFrank Li struct netdev_hw_addr *ha; 2963793fc096SFrank Li unsigned int i, bit, data, crc, tmp; 2964793fc096SFrank Li unsigned char hash; 2965793fc096SFrank Li 2966793fc096SFrank Li if (ndev->flags & IFF_PROMISC) { 2967793fc096SFrank Li tmp = readl(fep->hwp + FEC_R_CNTRL); 2968793fc096SFrank Li tmp |= 0x8; 2969793fc096SFrank Li writel(tmp, fep->hwp + FEC_R_CNTRL); 2970793fc096SFrank Li return; 2971793fc096SFrank Li } 2972793fc096SFrank Li 2973793fc096SFrank Li tmp = readl(fep->hwp + FEC_R_CNTRL); 2974793fc096SFrank Li tmp &= ~0x8; 2975793fc096SFrank Li writel(tmp, fep->hwp + FEC_R_CNTRL); 2976793fc096SFrank Li 2977793fc096SFrank Li if (ndev->flags & IFF_ALLMULTI) { 2978793fc096SFrank Li /* Catch all multicast addresses, so set the 2979793fc096SFrank Li * filter to all 1's 2980793fc096SFrank Li */ 2981793fc096SFrank Li writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 2982793fc096SFrank Li writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 2983793fc096SFrank Li 2984793fc096SFrank Li return; 2985793fc096SFrank Li } 2986793fc096SFrank Li 2987793fc096SFrank Li /* Clear filter and add the addresses in hash register 2988793fc096SFrank Li */ 2989793fc096SFrank Li writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 2990793fc096SFrank Li writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 2991793fc096SFrank Li 2992793fc096SFrank Li netdev_for_each_mc_addr(ha, ndev) { 2993793fc096SFrank Li /* calculate crc32 value of mac address */ 2994793fc096SFrank Li crc = 0xffffffff; 2995793fc096SFrank Li 2996793fc096SFrank Li for (i = 0; i < ndev->addr_len; i++) { 2997793fc096SFrank Li data = ha->addr[i]; 2998793fc096SFrank Li for (bit = 0; bit < 8; bit++, data >>= 1) { 2999793fc096SFrank Li crc = (crc >> 1) ^ 3000793fc096SFrank Li (((crc ^ data) & 1) ? CRC32_POLY : 0); 3001793fc096SFrank Li } 3002793fc096SFrank Li } 3003793fc096SFrank Li 3004793fc096SFrank Li /* only upper 6 bits (HASH_BITS) are used 3005793fc096SFrank Li * which point to specific bit in he hash registers 3006793fc096SFrank Li */ 3007793fc096SFrank Li hash = (crc >> (32 - HASH_BITS)) & 0x3f; 3008793fc096SFrank Li 3009793fc096SFrank Li if (hash > 31) { 3010793fc096SFrank Li tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 3011793fc096SFrank Li tmp |= 1 << (hash - 32); 3012793fc096SFrank Li writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 3013793fc096SFrank Li } else { 3014793fc096SFrank Li tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_LOW); 3015793fc096SFrank Li tmp |= 1 << hash; 3016793fc096SFrank Li writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 3017793fc096SFrank Li } 3018793fc096SFrank Li } 3019793fc096SFrank Li } 3020793fc096SFrank Li 3021793fc096SFrank Li /* Set a MAC change in hardware. */ 3022793fc096SFrank Li static int 3023793fc096SFrank Li fec_set_mac_address(struct net_device *ndev, void *p) 3024793fc096SFrank Li { 3025793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3026793fc096SFrank Li struct sockaddr *addr = p; 3027793fc096SFrank Li 302844934facSLucas Stach if (addr) { 3029793fc096SFrank Li if (!is_valid_ether_addr(addr->sa_data)) 3030793fc096SFrank Li return -EADDRNOTAVAIL; 3031793fc096SFrank Li memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len); 303244934facSLucas Stach } 3033793fc096SFrank Li 30349638d19eSNimrod Andy /* Add netif status check here to avoid system hang in below case: 30359638d19eSNimrod Andy * ifconfig ethx down; ifconfig ethx hw ether xx:xx:xx:xx:xx:xx; 30369638d19eSNimrod Andy * After ethx down, fec all clocks are gated off and then register 30379638d19eSNimrod Andy * access causes system hang. 30389638d19eSNimrod Andy */ 30399638d19eSNimrod Andy if (!netif_running(ndev)) 30409638d19eSNimrod Andy return 0; 30419638d19eSNimrod Andy 3042793fc096SFrank Li writel(ndev->dev_addr[3] | (ndev->dev_addr[2] << 8) | 3043793fc096SFrank Li (ndev->dev_addr[1] << 16) | (ndev->dev_addr[0] << 24), 3044793fc096SFrank Li fep->hwp + FEC_ADDR_LOW); 3045793fc096SFrank Li writel((ndev->dev_addr[5] << 16) | (ndev->dev_addr[4] << 24), 3046793fc096SFrank Li fep->hwp + FEC_ADDR_HIGH); 3047793fc096SFrank Li return 0; 3048793fc096SFrank Li } 3049793fc096SFrank Li 3050793fc096SFrank Li #ifdef CONFIG_NET_POLL_CONTROLLER 3051793fc096SFrank Li /** 3052793fc096SFrank Li * fec_poll_controller - FEC Poll controller function 3053793fc096SFrank Li * @dev: The FEC network adapter 3054793fc096SFrank Li * 3055793fc096SFrank Li * Polled functionality used by netconsole and others in non interrupt mode 3056793fc096SFrank Li * 3057793fc096SFrank Li */ 3058793fc096SFrank Li static void fec_poll_controller(struct net_device *dev) 3059793fc096SFrank Li { 3060793fc096SFrank Li int i; 3061793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(dev); 3062793fc096SFrank Li 3063793fc096SFrank Li for (i = 0; i < FEC_IRQ_NUM; i++) { 3064793fc096SFrank Li if (fep->irq[i] > 0) { 3065793fc096SFrank Li disable_irq(fep->irq[i]); 3066793fc096SFrank Li fec_enet_interrupt(fep->irq[i], dev); 3067793fc096SFrank Li enable_irq(fep->irq[i]); 3068793fc096SFrank Li } 3069793fc096SFrank Li } 3070793fc096SFrank Li } 3071793fc096SFrank Li #endif 3072793fc096SFrank Li 30735bc26726SNimrod Andy static inline void fec_enet_set_netdev_features(struct net_device *netdev, 30744c09eed9SJim Baxter netdev_features_t features) 30754c09eed9SJim Baxter { 30764c09eed9SJim Baxter struct fec_enet_private *fep = netdev_priv(netdev); 30774c09eed9SJim Baxter netdev_features_t changed = features ^ netdev->features; 30784c09eed9SJim Baxter 30794c09eed9SJim Baxter netdev->features = features; 30804c09eed9SJim Baxter 30814c09eed9SJim Baxter /* Receive checksum has been changed */ 30824c09eed9SJim Baxter if (changed & NETIF_F_RXCSUM) { 30834c09eed9SJim Baxter if (features & NETIF_F_RXCSUM) 30844c09eed9SJim Baxter fep->csum_flags |= FLAG_RX_CSUM_ENABLED; 30854c09eed9SJim Baxter else 30864c09eed9SJim Baxter fep->csum_flags &= ~FLAG_RX_CSUM_ENABLED; 30878506fa1dSRussell King } 30885bc26726SNimrod Andy } 30894c09eed9SJim Baxter 30905bc26726SNimrod Andy static int fec_set_features(struct net_device *netdev, 30915bc26726SNimrod Andy netdev_features_t features) 30925bc26726SNimrod Andy { 30935bc26726SNimrod Andy struct fec_enet_private *fep = netdev_priv(netdev); 30945bc26726SNimrod Andy netdev_features_t changed = features ^ netdev->features; 30955bc26726SNimrod Andy 30965b40f709SFabio Estevam if (netif_running(netdev) && changed & NETIF_F_RXCSUM) { 30975bc26726SNimrod Andy napi_disable(&fep->napi); 30985bc26726SNimrod Andy netif_tx_lock_bh(netdev); 30995bc26726SNimrod Andy fec_stop(netdev); 31005bc26726SNimrod Andy fec_enet_set_netdev_features(netdev, features); 3101ef83337dSRussell King fec_restart(netdev); 31024d494cdcSFugang Duan netif_tx_wake_all_queues(netdev); 31036af42d42SRussell King netif_tx_unlock_bh(netdev); 3104dbc64a8eSRussell King napi_enable(&fep->napi); 31055bc26726SNimrod Andy } else { 31065bc26726SNimrod Andy fec_enet_set_netdev_features(netdev, features); 31074c09eed9SJim Baxter } 31084c09eed9SJim Baxter 31094c09eed9SJim Baxter return 0; 31104c09eed9SJim Baxter } 31114c09eed9SJim Baxter 3112793fc096SFrank Li static const struct net_device_ops fec_netdev_ops = { 3113793fc096SFrank Li .ndo_open = fec_enet_open, 3114793fc096SFrank Li .ndo_stop = fec_enet_close, 3115793fc096SFrank Li .ndo_start_xmit = fec_enet_start_xmit, 3116793fc096SFrank Li .ndo_set_rx_mode = set_multicast_list, 3117793fc096SFrank Li .ndo_change_mtu = eth_change_mtu, 3118793fc096SFrank Li .ndo_validate_addr = eth_validate_addr, 3119793fc096SFrank Li .ndo_tx_timeout = fec_timeout, 3120793fc096SFrank Li .ndo_set_mac_address = fec_set_mac_address, 3121793fc096SFrank Li .ndo_do_ioctl = fec_enet_ioctl, 3122793fc096SFrank Li #ifdef CONFIG_NET_POLL_CONTROLLER 3123793fc096SFrank Li .ndo_poll_controller = fec_poll_controller, 3124793fc096SFrank Li #endif 31254c09eed9SJim Baxter .ndo_set_features = fec_set_features, 3126793fc096SFrank Li }; 3127793fc096SFrank Li 3128793fc096SFrank Li /* 3129793fc096SFrank Li * XXX: We need to clean up on failure exits here. 3130793fc096SFrank Li * 3131793fc096SFrank Li */ 3132793fc096SFrank Li static int fec_enet_init(struct net_device *ndev) 3133793fc096SFrank Li { 3134793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 31354d494cdcSFugang Duan struct fec_enet_priv_tx_q *txq; 31364d494cdcSFugang Duan struct fec_enet_priv_rx_q *rxq; 3137793fc096SFrank Li struct bufdesc *cbd_base; 31384d494cdcSFugang Duan dma_addr_t bd_dma; 313955d0218aSNimrod Andy int bd_size; 314059d0f746SFrank Li unsigned int i; 314155d0218aSNimrod Andy 314241ef84ceSFugang Duan #if defined(CONFIG_ARM) 314341ef84ceSFugang Duan fep->rx_align = 0xf; 314441ef84ceSFugang Duan fep->tx_align = 0xf; 314541ef84ceSFugang Duan #else 314641ef84ceSFugang Duan fep->rx_align = 0x3; 314741ef84ceSFugang Duan fep->tx_align = 0x3; 314841ef84ceSFugang Duan #endif 314941ef84ceSFugang Duan 315059d0f746SFrank Li fec_enet_alloc_queue(ndev); 315179f33912SNimrod Andy 315255d0218aSNimrod Andy if (fep->bufdesc_ex) 315355d0218aSNimrod Andy fep->bufdesc_size = sizeof(struct bufdesc_ex); 315455d0218aSNimrod Andy else 315555d0218aSNimrod Andy fep->bufdesc_size = sizeof(struct bufdesc); 31564d494cdcSFugang Duan bd_size = (fep->total_tx_ring_size + fep->total_rx_ring_size) * 315755d0218aSNimrod Andy fep->bufdesc_size; 3158793fc096SFrank Li 3159793fc096SFrank Li /* Allocate memory for buffer descriptors. */ 3160c0a1a0a6SLucas Stach cbd_base = dmam_alloc_coherent(&fep->pdev->dev, bd_size, &bd_dma, 3161793fc096SFrank Li GFP_KERNEL); 31624d494cdcSFugang Duan if (!cbd_base) { 31634d494cdcSFugang Duan return -ENOMEM; 31644d494cdcSFugang Duan } 3165793fc096SFrank Li 31664d494cdcSFugang Duan memset(cbd_base, 0, bd_size); 3167793fc096SFrank Li 3168793fc096SFrank Li /* Get the Ethernet address */ 3169793fc096SFrank Li fec_get_mac(ndev); 317044934facSLucas Stach /* make sure MAC we just acquired is programmed into the hw */ 317144934facSLucas Stach fec_set_mac_address(ndev, NULL); 3172793fc096SFrank Li 3173793fc096SFrank Li /* Set receive and transmit descriptor base. */ 317459d0f746SFrank Li for (i = 0; i < fep->num_rx_queues; i++) { 317559d0f746SFrank Li rxq = fep->rx_queue[i]; 317659d0f746SFrank Li rxq->index = i; 317759d0f746SFrank Li rxq->rx_bd_base = (struct bufdesc *)cbd_base; 317859d0f746SFrank Li rxq->bd_dma = bd_dma; 317959d0f746SFrank Li if (fep->bufdesc_ex) { 318059d0f746SFrank Li bd_dma += sizeof(struct bufdesc_ex) * rxq->rx_ring_size; 318159d0f746SFrank Li cbd_base = (struct bufdesc *) 31824d494cdcSFugang Duan (((struct bufdesc_ex *)cbd_base) + rxq->rx_ring_size); 318359d0f746SFrank Li } else { 318459d0f746SFrank Li bd_dma += sizeof(struct bufdesc) * rxq->rx_ring_size; 318559d0f746SFrank Li cbd_base += rxq->rx_ring_size; 318659d0f746SFrank Li } 318759d0f746SFrank Li } 318859d0f746SFrank Li 318959d0f746SFrank Li for (i = 0; i < fep->num_tx_queues; i++) { 319059d0f746SFrank Li txq = fep->tx_queue[i]; 319159d0f746SFrank Li txq->index = i; 319259d0f746SFrank Li txq->tx_bd_base = (struct bufdesc *)cbd_base; 319359d0f746SFrank Li txq->bd_dma = bd_dma; 319459d0f746SFrank Li if (fep->bufdesc_ex) { 319559d0f746SFrank Li bd_dma += sizeof(struct bufdesc_ex) * txq->tx_ring_size; 319659d0f746SFrank Li cbd_base = (struct bufdesc *) 319759d0f746SFrank Li (((struct bufdesc_ex *)cbd_base) + txq->tx_ring_size); 319859d0f746SFrank Li } else { 319959d0f746SFrank Li bd_dma += sizeof(struct bufdesc) * txq->tx_ring_size; 320059d0f746SFrank Li cbd_base += txq->tx_ring_size; 320159d0f746SFrank Li } 320259d0f746SFrank Li } 32034d494cdcSFugang Duan 3204793fc096SFrank Li 3205793fc096SFrank Li /* The FEC Ethernet specific entries in the device structure */ 3206793fc096SFrank Li ndev->watchdog_timeo = TX_TIMEOUT; 3207793fc096SFrank Li ndev->netdev_ops = &fec_netdev_ops; 3208793fc096SFrank Li ndev->ethtool_ops = &fec_enet_ethtool_ops; 3209793fc096SFrank Li 3210793fc096SFrank Li writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK); 3211322555f5SFabio Estevam netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, NAPI_POLL_WEIGHT); 3212793fc096SFrank Li 32136b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_VLAN) 3214cdffcf1bSJim Baxter /* enable hw VLAN support */ 3215cdffcf1bSJim Baxter ndev->features |= NETIF_F_HW_VLAN_CTAG_RX; 3216cdffcf1bSJim Baxter 32176b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_CSUM) { 321879f33912SNimrod Andy ndev->gso_max_segs = FEC_MAX_TSO_SEGS; 321979f33912SNimrod Andy 32204c09eed9SJim Baxter /* enable hw accelerator */ 32214c09eed9SJim Baxter ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM 322279f33912SNimrod Andy | NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO); 32234c09eed9SJim Baxter fep->csum_flags |= FLAG_RX_CSUM_ENABLED; 322448496255SShawn Guo } 32254c09eed9SJim Baxter 32266b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_AVB) { 322741ef84ceSFugang Duan fep->tx_align = 0; 322841ef84ceSFugang Duan fep->rx_align = 0x3f; 322941ef84ceSFugang Duan } 323041ef84ceSFugang Duan 323109d1e541SNimrod Andy ndev->hw_features = ndev->features; 323209d1e541SNimrod Andy 3233ef83337dSRussell King fec_restart(ndev); 3234793fc096SFrank Li 3235793fc096SFrank Li return 0; 3236793fc096SFrank Li } 3237793fc096SFrank Li 3238793fc096SFrank Li #ifdef CONFIG_OF 3239793fc096SFrank Li static void fec_reset_phy(struct platform_device *pdev) 3240793fc096SFrank Li { 3241793fc096SFrank Li int err, phy_reset; 3242793fc096SFrank Li int msec = 1; 3243793fc096SFrank Li struct device_node *np = pdev->dev.of_node; 3244793fc096SFrank Li 3245793fc096SFrank Li if (!np) 3246793fc096SFrank Li return; 3247793fc096SFrank Li 3248793fc096SFrank Li of_property_read_u32(np, "phy-reset-duration", &msec); 3249793fc096SFrank Li /* A sane reset duration should not be longer than 1s */ 3250793fc096SFrank Li if (msec > 1000) 3251793fc096SFrank Li msec = 1; 3252793fc096SFrank Li 3253793fc096SFrank Li phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0); 3254793fc096SFrank Li if (!gpio_is_valid(phy_reset)) 3255793fc096SFrank Li return; 3256793fc096SFrank Li 3257793fc096SFrank Li err = devm_gpio_request_one(&pdev->dev, phy_reset, 3258793fc096SFrank Li GPIOF_OUT_INIT_LOW, "phy-reset"); 3259793fc096SFrank Li if (err) { 3260793fc096SFrank Li dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err); 3261793fc096SFrank Li return; 3262793fc096SFrank Li } 3263793fc096SFrank Li msleep(msec); 3264f4444574SFabio Estevam gpio_set_value_cansleep(phy_reset, 1); 3265793fc096SFrank Li } 3266793fc096SFrank Li #else /* CONFIG_OF */ 3267793fc096SFrank Li static void fec_reset_phy(struct platform_device *pdev) 3268793fc096SFrank Li { 3269793fc096SFrank Li /* 3270793fc096SFrank Li * In case of platform probe, the reset has been done 3271793fc096SFrank Li * by machine code. 3272793fc096SFrank Li */ 3273793fc096SFrank Li } 3274793fc096SFrank Li #endif /* CONFIG_OF */ 3275793fc096SFrank Li 32769fc095f1SFugang Duan static void 32779fc095f1SFugang Duan fec_enet_get_queue_num(struct platform_device *pdev, int *num_tx, int *num_rx) 32789fc095f1SFugang Duan { 32799fc095f1SFugang Duan struct device_node *np = pdev->dev.of_node; 32809fc095f1SFugang Duan 32819fc095f1SFugang Duan *num_tx = *num_rx = 1; 32829fc095f1SFugang Duan 32839fc095f1SFugang Duan if (!np || !of_device_is_available(np)) 32849fc095f1SFugang Duan return; 32859fc095f1SFugang Duan 32869fc095f1SFugang Duan /* parse the num of tx and rx queues */ 3287*73b1c90dSSaurabh Sengar of_property_read_u32(np, "fsl,num-tx-queues", num_tx); 3288b7bd75cfSFrank Li 3289*73b1c90dSSaurabh Sengar of_property_read_u32(np, "fsl,num-rx-queues", num_rx); 32909fc095f1SFugang Duan 32919fc095f1SFugang Duan if (*num_tx < 1 || *num_tx > FEC_ENET_MAX_TX_QS) { 3292b7bd75cfSFrank Li dev_warn(&pdev->dev, "Invalid num_tx(=%d), fall back to 1\n", 32939fc095f1SFugang Duan *num_tx); 32949fc095f1SFugang Duan *num_tx = 1; 32959fc095f1SFugang Duan return; 32969fc095f1SFugang Duan } 32979fc095f1SFugang Duan 32989fc095f1SFugang Duan if (*num_rx < 1 || *num_rx > FEC_ENET_MAX_RX_QS) { 3299b7bd75cfSFrank Li dev_warn(&pdev->dev, "Invalid num_rx(=%d), fall back to 1\n", 33009fc095f1SFugang Duan *num_rx); 33019fc095f1SFugang Duan *num_rx = 1; 33029fc095f1SFugang Duan return; 33039fc095f1SFugang Duan } 33049fc095f1SFugang Duan 33059fc095f1SFugang Duan } 33069fc095f1SFugang Duan 3307793fc096SFrank Li static int 3308793fc096SFrank Li fec_probe(struct platform_device *pdev) 3309793fc096SFrank Li { 3310793fc096SFrank Li struct fec_enet_private *fep; 3311793fc096SFrank Li struct fec_platform_data *pdata; 3312793fc096SFrank Li struct net_device *ndev; 3313793fc096SFrank Li int i, irq, ret = 0; 3314793fc096SFrank Li struct resource *r; 3315793fc096SFrank Li const struct of_device_id *of_id; 3316793fc096SFrank Li static int dev_id; 3317407066f8SUwe Kleine-König struct device_node *np = pdev->dev.of_node, *phy_node; 3318b7bd75cfSFrank Li int num_tx_qs; 3319b7bd75cfSFrank Li int num_rx_qs; 3320793fc096SFrank Li 33219fc095f1SFugang Duan fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs); 33229fc095f1SFugang Duan 3323793fc096SFrank Li /* Init network device */ 33249fc095f1SFugang Duan ndev = alloc_etherdev_mqs(sizeof(struct fec_enet_private), 33259fc095f1SFugang Duan num_tx_qs, num_rx_qs); 3326793fc096SFrank Li if (!ndev) 3327793fc096SFrank Li return -ENOMEM; 3328793fc096SFrank Li 3329793fc096SFrank Li SET_NETDEV_DEV(ndev, &pdev->dev); 3330793fc096SFrank Li 3331793fc096SFrank Li /* setup board info structure */ 3332793fc096SFrank Li fep = netdev_priv(ndev); 3333793fc096SFrank Li 33346b7e4008SLothar Waßmann of_id = of_match_device(fec_dt_ids, &pdev->dev); 33356b7e4008SLothar Waßmann if (of_id) 33366b7e4008SLothar Waßmann pdev->id_entry = of_id->data; 33376b7e4008SLothar Waßmann fep->quirks = pdev->id_entry->driver_data; 33386b7e4008SLothar Waßmann 33390c818594SHubert Feurstein fep->netdev = ndev; 33409fc095f1SFugang Duan fep->num_rx_queues = num_rx_qs; 33419fc095f1SFugang Duan fep->num_tx_queues = num_tx_qs; 33429fc095f1SFugang Duan 3343d1391930SGuenter Roeck #if !defined(CONFIG_M5272) 3344793fc096SFrank Li /* default enable pause frame auto negotiation */ 33456b7e4008SLothar Waßmann if (fep->quirks & FEC_QUIRK_HAS_GBIT) 3346793fc096SFrank Li fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG; 3347d1391930SGuenter Roeck #endif 3348793fc096SFrank Li 33495bbde4d2SNimrod Andy /* Select default pin state */ 33505bbde4d2SNimrod Andy pinctrl_pm_select_default_state(&pdev->dev); 33515bbde4d2SNimrod Andy 3352399db75bSFabio Estevam r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3353941e173aSTushar Behera fep->hwp = devm_ioremap_resource(&pdev->dev, r); 3354941e173aSTushar Behera if (IS_ERR(fep->hwp)) { 3355941e173aSTushar Behera ret = PTR_ERR(fep->hwp); 3356941e173aSTushar Behera goto failed_ioremap; 3357941e173aSTushar Behera } 3358941e173aSTushar Behera 3359793fc096SFrank Li fep->pdev = pdev; 3360793fc096SFrank Li fep->dev_id = dev_id++; 3361793fc096SFrank Li 3362793fc096SFrank Li platform_set_drvdata(pdev, ndev); 3363793fc096SFrank Li 3364de40ed31SNimrod Andy if (of_get_property(np, "fsl,magic-packet", NULL)) 3365de40ed31SNimrod Andy fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET; 3366de40ed31SNimrod Andy 3367407066f8SUwe Kleine-König phy_node = of_parse_phandle(np, "phy-handle", 0); 3368407066f8SUwe Kleine-König if (!phy_node && of_phy_is_fixed_link(np)) { 3369407066f8SUwe Kleine-König ret = of_phy_register_fixed_link(np); 3370407066f8SUwe Kleine-König if (ret < 0) { 3371407066f8SUwe Kleine-König dev_err(&pdev->dev, 3372407066f8SUwe Kleine-König "broken fixed-link specification\n"); 3373407066f8SUwe Kleine-König goto failed_phy; 3374407066f8SUwe Kleine-König } 3375407066f8SUwe Kleine-König phy_node = of_node_get(np); 3376407066f8SUwe Kleine-König } 3377407066f8SUwe Kleine-König fep->phy_node = phy_node; 3378407066f8SUwe Kleine-König 33796c5f7808SGuenter Roeck ret = of_get_phy_mode(pdev->dev.of_node); 3380793fc096SFrank Li if (ret < 0) { 338194660ba0SJingoo Han pdata = dev_get_platdata(&pdev->dev); 3382793fc096SFrank Li if (pdata) 3383793fc096SFrank Li fep->phy_interface = pdata->phy; 3384793fc096SFrank Li else 3385793fc096SFrank Li fep->phy_interface = PHY_INTERFACE_MODE_MII; 3386793fc096SFrank Li } else { 3387793fc096SFrank Li fep->phy_interface = ret; 3388793fc096SFrank Li } 3389793fc096SFrank Li 3390793fc096SFrank Li fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); 3391793fc096SFrank Li if (IS_ERR(fep->clk_ipg)) { 3392793fc096SFrank Li ret = PTR_ERR(fep->clk_ipg); 3393793fc096SFrank Li goto failed_clk; 3394793fc096SFrank Li } 3395793fc096SFrank Li 3396793fc096SFrank Li fep->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); 3397793fc096SFrank Li if (IS_ERR(fep->clk_ahb)) { 3398793fc096SFrank Li ret = PTR_ERR(fep->clk_ahb); 3399793fc096SFrank Li goto failed_clk; 3400793fc096SFrank Li } 3401793fc096SFrank Li 3402d851b47bSFugang Duan fep->itr_clk_rate = clk_get_rate(fep->clk_ahb); 3403d851b47bSFugang Duan 340438f56f33SLinus Torvalds /* enet_out is optional, depends on board */ 340538f56f33SLinus Torvalds fep->clk_enet_out = devm_clk_get(&pdev->dev, "enet_out"); 340638f56f33SLinus Torvalds if (IS_ERR(fep->clk_enet_out)) 340738f56f33SLinus Torvalds fep->clk_enet_out = NULL; 340838f56f33SLinus Torvalds 340991c0d987SNimrod Andy fep->ptp_clk_on = false; 341091c0d987SNimrod Andy mutex_init(&fep->ptp_clk_mutex); 34119b5330edSFugang Duan 34129b5330edSFugang Duan /* clk_ref is optional, depends on board */ 34139b5330edSFugang Duan fep->clk_ref = devm_clk_get(&pdev->dev, "enet_clk_ref"); 34149b5330edSFugang Duan if (IS_ERR(fep->clk_ref)) 34159b5330edSFugang Duan fep->clk_ref = NULL; 34169b5330edSFugang Duan 34176b7e4008SLothar Waßmann fep->bufdesc_ex = fep->quirks & FEC_QUIRK_HAS_BUFDESC_EX; 3418793fc096SFrank Li fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp"); 3419793fc096SFrank Li if (IS_ERR(fep->clk_ptp)) { 342038f56f33SLinus Torvalds fep->clk_ptp = NULL; 3421217b5844SLothar Waßmann fep->bufdesc_ex = false; 3422793fc096SFrank Li } 3423793fc096SFrank Li 3424e8fcfcd5SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 342513a097bdSFabio Estevam if (ret) 342613a097bdSFabio Estevam goto failed_clk; 342713a097bdSFabio Estevam 34288fff755eSAndrew Lunn ret = clk_prepare_enable(fep->clk_ipg); 34298fff755eSAndrew Lunn if (ret) 34308fff755eSAndrew Lunn goto failed_clk_ipg; 34318fff755eSAndrew Lunn 3432f4e9f3d2SFabio Estevam fep->reg_phy = devm_regulator_get(&pdev->dev, "phy"); 3433f4e9f3d2SFabio Estevam if (!IS_ERR(fep->reg_phy)) { 3434f4e9f3d2SFabio Estevam ret = regulator_enable(fep->reg_phy); 3435793fc096SFrank Li if (ret) { 3436793fc096SFrank Li dev_err(&pdev->dev, 3437793fc096SFrank Li "Failed to enable phy regulator: %d\n", ret); 3438793fc096SFrank Li goto failed_regulator; 3439793fc096SFrank Li } 3440f6a4d607SFabio Estevam } else { 3441f6a4d607SFabio Estevam fep->reg_phy = NULL; 3442793fc096SFrank Li } 3443793fc096SFrank Li 34448fff755eSAndrew Lunn pm_runtime_set_autosuspend_delay(&pdev->dev, FEC_MDIO_PM_TIMEOUT); 34458fff755eSAndrew Lunn pm_runtime_use_autosuspend(&pdev->dev); 344614d2b7c1SLucas Stach pm_runtime_get_noresume(&pdev->dev); 34478fff755eSAndrew Lunn pm_runtime_set_active(&pdev->dev); 34488fff755eSAndrew Lunn pm_runtime_enable(&pdev->dev); 34498fff755eSAndrew Lunn 3450793fc096SFrank Li fec_reset_phy(pdev); 3451793fc096SFrank Li 3452793fc096SFrank Li if (fep->bufdesc_ex) 3453ca162a82SFabio Estevam fec_ptp_init(pdev); 3454793fc096SFrank Li 3455793fc096SFrank Li ret = fec_enet_init(ndev); 3456793fc096SFrank Li if (ret) 3457793fc096SFrank Li goto failed_init; 3458793fc096SFrank Li 3459793fc096SFrank Li for (i = 0; i < FEC_IRQ_NUM; i++) { 3460793fc096SFrank Li irq = platform_get_irq(pdev, i); 3461793fc096SFrank Li if (irq < 0) { 3462793fc096SFrank Li if (i) 3463793fc096SFrank Li break; 3464793fc096SFrank Li ret = irq; 3465793fc096SFrank Li goto failed_irq; 3466793fc096SFrank Li } 34670d9b2ab1SFabio Estevam ret = devm_request_irq(&pdev->dev, irq, fec_enet_interrupt, 346844a272ddSMichael Opdenacker 0, pdev->name, ndev); 34690d9b2ab1SFabio Estevam if (ret) 3470793fc096SFrank Li goto failed_irq; 3471de40ed31SNimrod Andy 3472de40ed31SNimrod Andy fep->irq[i] = irq; 3473793fc096SFrank Li } 3474793fc096SFrank Li 3475b4d39b53SFugang Duan init_completion(&fep->mdio_done); 3476793fc096SFrank Li ret = fec_enet_mii_init(pdev); 3477793fc096SFrank Li if (ret) 3478793fc096SFrank Li goto failed_mii_init; 3479793fc096SFrank Li 3480793fc096SFrank Li /* Carrier starts down, phylib will bring it up */ 3481793fc096SFrank Li netif_carrier_off(ndev); 3482e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 34835bbde4d2SNimrod Andy pinctrl_pm_select_sleep_state(&pdev->dev); 3484793fc096SFrank Li 3485793fc096SFrank Li ret = register_netdev(ndev); 3486793fc096SFrank Li if (ret) 3487793fc096SFrank Li goto failed_register; 3488793fc096SFrank Li 3489de40ed31SNimrod Andy device_init_wakeup(&ndev->dev, fep->wol_flag & 3490de40ed31SNimrod Andy FEC_WOL_HAS_MAGIC_PACKET); 3491de40ed31SNimrod Andy 3492eb1d0640SFabio Estevam if (fep->bufdesc_ex && fep->ptp_clock) 3493eb1d0640SFabio Estevam netdev_info(ndev, "registered PHC device %d\n", fep->dev_id); 3494eb1d0640SFabio Estevam 34951b7bde6dSNimrod Andy fep->rx_copybreak = COPYBREAK_DEFAULT; 349636cdc743SRussell King INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work); 34978fff755eSAndrew Lunn 34988fff755eSAndrew Lunn pm_runtime_mark_last_busy(&pdev->dev); 34998fff755eSAndrew Lunn pm_runtime_put_autosuspend(&pdev->dev); 35008fff755eSAndrew Lunn 3501793fc096SFrank Li return 0; 3502793fc096SFrank Li 3503793fc096SFrank Li failed_register: 3504793fc096SFrank Li fec_enet_mii_remove(fep); 3505793fc096SFrank Li failed_mii_init: 35067a2bbd8dSFabio Estevam failed_irq: 35077a2bbd8dSFabio Estevam failed_init: 350832cba57bSLucas Stach fec_ptp_stop(pdev); 3509f6a4d607SFabio Estevam if (fep->reg_phy) 3510f6a4d607SFabio Estevam regulator_disable(fep->reg_phy); 3511793fc096SFrank Li failed_regulator: 35128fff755eSAndrew Lunn clk_disable_unprepare(fep->clk_ipg); 35138fff755eSAndrew Lunn failed_clk_ipg: 3514e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 3515793fc096SFrank Li failed_clk: 3516407066f8SUwe Kleine-König failed_phy: 3517407066f8SUwe Kleine-König of_node_put(phy_node); 3518793fc096SFrank Li failed_ioremap: 3519793fc096SFrank Li free_netdev(ndev); 3520793fc096SFrank Li 3521793fc096SFrank Li return ret; 3522793fc096SFrank Li } 3523793fc096SFrank Li 3524793fc096SFrank Li static int 3525793fc096SFrank Li fec_drv_remove(struct platform_device *pdev) 3526793fc096SFrank Li { 3527793fc096SFrank Li struct net_device *ndev = platform_get_drvdata(pdev); 3528793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3529793fc096SFrank Li 353036cdc743SRussell King cancel_work_sync(&fep->tx_timeout_work); 353132cba57bSLucas Stach fec_ptp_stop(pdev); 3532793fc096SFrank Li unregister_netdev(ndev); 3533793fc096SFrank Li fec_enet_mii_remove(fep); 3534f6a4d607SFabio Estevam if (fep->reg_phy) 3535f6a4d607SFabio Estevam regulator_disable(fep->reg_phy); 3536407066f8SUwe Kleine-König of_node_put(fep->phy_node); 3537793fc096SFrank Li free_netdev(ndev); 3538793fc096SFrank Li 3539793fc096SFrank Li return 0; 3540793fc096SFrank Li } 3541793fc096SFrank Li 3542dd66d386SFabio Estevam static int __maybe_unused fec_suspend(struct device *dev) 3543793fc096SFrank Li { 3544793fc096SFrank Li struct net_device *ndev = dev_get_drvdata(dev); 3545793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3546793fc096SFrank Li 3547da1774e5SRussell King rtnl_lock(); 3548793fc096SFrank Li if (netif_running(ndev)) { 3549de40ed31SNimrod Andy if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) 3550de40ed31SNimrod Andy fep->wol_flag |= FEC_WOL_FLAG_SLEEP_ON; 3551d76cfae9SRussell King phy_stop(fep->phy_dev); 355231a6de34SRussell King napi_disable(&fep->napi); 355331a6de34SRussell King netif_tx_lock_bh(ndev); 3554793fc096SFrank Li netif_device_detach(ndev); 355531a6de34SRussell King netif_tx_unlock_bh(ndev); 355631a6de34SRussell King fec_stop(ndev); 3557e8fcfcd5SNimrod Andy fec_enet_clk_enable(ndev, false); 3558de40ed31SNimrod Andy if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) 35595bbde4d2SNimrod Andy pinctrl_pm_select_sleep_state(&fep->pdev->dev); 3560f4c4a4e0SNimrod Andy } 3561f4c4a4e0SNimrod Andy rtnl_unlock(); 3562793fc096SFrank Li 3563de40ed31SNimrod Andy if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) 3564238f7bc7SFabio Estevam regulator_disable(fep->reg_phy); 3565238f7bc7SFabio Estevam 3566858eeb7dSNimrod Andy /* SOC supply clock to phy, when clock is disabled, phy link down 3567858eeb7dSNimrod Andy * SOC control phy regulator, when regulator is disabled, phy link down 3568858eeb7dSNimrod Andy */ 3569858eeb7dSNimrod Andy if (fep->clk_enet_out || fep->reg_phy) 3570858eeb7dSNimrod Andy fep->link = 0; 3571858eeb7dSNimrod Andy 3572793fc096SFrank Li return 0; 3573793fc096SFrank Li } 3574793fc096SFrank Li 3575dd66d386SFabio Estevam static int __maybe_unused fec_resume(struct device *dev) 3576793fc096SFrank Li { 3577793fc096SFrank Li struct net_device *ndev = dev_get_drvdata(dev); 3578793fc096SFrank Li struct fec_enet_private *fep = netdev_priv(ndev); 3579de40ed31SNimrod Andy struct fec_platform_data *pdata = fep->pdev->dev.platform_data; 3580238f7bc7SFabio Estevam int ret; 3581de40ed31SNimrod Andy int val; 3582238f7bc7SFabio Estevam 3583de40ed31SNimrod Andy if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) { 3584238f7bc7SFabio Estevam ret = regulator_enable(fep->reg_phy); 3585238f7bc7SFabio Estevam if (ret) 3586238f7bc7SFabio Estevam return ret; 3587238f7bc7SFabio Estevam } 3588793fc096SFrank Li 3589da1774e5SRussell King rtnl_lock(); 3590793fc096SFrank Li if (netif_running(ndev)) { 3591f4c4a4e0SNimrod Andy ret = fec_enet_clk_enable(ndev, true); 3592f4c4a4e0SNimrod Andy if (ret) { 3593f4c4a4e0SNimrod Andy rtnl_unlock(); 3594f4c4a4e0SNimrod Andy goto failed_clk; 3595f4c4a4e0SNimrod Andy } 3596de40ed31SNimrod Andy if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) { 3597de40ed31SNimrod Andy if (pdata && pdata->sleep_mode_enable) 3598de40ed31SNimrod Andy pdata->sleep_mode_enable(false); 3599de40ed31SNimrod Andy val = readl(fep->hwp + FEC_ECNTRL); 3600de40ed31SNimrod Andy val &= ~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP); 3601de40ed31SNimrod Andy writel(val, fep->hwp + FEC_ECNTRL); 3602de40ed31SNimrod Andy fep->wol_flag &= ~FEC_WOL_FLAG_SLEEP_ON; 3603de40ed31SNimrod Andy } else { 3604de40ed31SNimrod Andy pinctrl_pm_select_default_state(&fep->pdev->dev); 3605de40ed31SNimrod Andy } 3606ef83337dSRussell King fec_restart(ndev); 360731a6de34SRussell King netif_tx_lock_bh(ndev); 3608793fc096SFrank Li netif_device_attach(ndev); 36096af42d42SRussell King netif_tx_unlock_bh(ndev); 36106af42d42SRussell King napi_enable(&fep->napi); 3611d76cfae9SRussell King phy_start(fep->phy_dev); 3612793fc096SFrank Li } 3613da1774e5SRussell King rtnl_unlock(); 3614793fc096SFrank Li 3615793fc096SFrank Li return 0; 361613a097bdSFabio Estevam 3617e8fcfcd5SNimrod Andy failed_clk: 361813a097bdSFabio Estevam if (fep->reg_phy) 361913a097bdSFabio Estevam regulator_disable(fep->reg_phy); 362013a097bdSFabio Estevam return ret; 3621793fc096SFrank Li } 3622793fc096SFrank Li 36238fff755eSAndrew Lunn static int __maybe_unused fec_runtime_suspend(struct device *dev) 36248fff755eSAndrew Lunn { 36258fff755eSAndrew Lunn struct net_device *ndev = dev_get_drvdata(dev); 36268fff755eSAndrew Lunn struct fec_enet_private *fep = netdev_priv(ndev); 36278fff755eSAndrew Lunn 36288fff755eSAndrew Lunn clk_disable_unprepare(fep->clk_ipg); 36298fff755eSAndrew Lunn 36308fff755eSAndrew Lunn return 0; 36318fff755eSAndrew Lunn } 36328fff755eSAndrew Lunn 36338fff755eSAndrew Lunn static int __maybe_unused fec_runtime_resume(struct device *dev) 36348fff755eSAndrew Lunn { 36358fff755eSAndrew Lunn struct net_device *ndev = dev_get_drvdata(dev); 36368fff755eSAndrew Lunn struct fec_enet_private *fep = netdev_priv(ndev); 36378fff755eSAndrew Lunn 36388fff755eSAndrew Lunn return clk_prepare_enable(fep->clk_ipg); 36398fff755eSAndrew Lunn } 36408fff755eSAndrew Lunn 36418fff755eSAndrew Lunn static const struct dev_pm_ops fec_pm_ops = { 36428fff755eSAndrew Lunn SET_SYSTEM_SLEEP_PM_OPS(fec_suspend, fec_resume) 36438fff755eSAndrew Lunn SET_RUNTIME_PM_OPS(fec_runtime_suspend, fec_runtime_resume, NULL) 36448fff755eSAndrew Lunn }; 3645793fc096SFrank Li 3646793fc096SFrank Li static struct platform_driver fec_driver = { 3647793fc096SFrank Li .driver = { 3648793fc096SFrank Li .name = DRIVER_NAME, 3649793fc096SFrank Li .pm = &fec_pm_ops, 3650793fc096SFrank Li .of_match_table = fec_dt_ids, 3651793fc096SFrank Li }, 3652793fc096SFrank Li .id_table = fec_devtype, 3653793fc096SFrank Li .probe = fec_probe, 3654793fc096SFrank Li .remove = fec_drv_remove, 3655793fc096SFrank Li }; 3656793fc096SFrank Li 3657793fc096SFrank Li module_platform_driver(fec_driver); 3658793fc096SFrank Li 3659f8c0aca9SFabio Estevam MODULE_ALIAS("platform:"DRIVER_NAME); 3660793fc096SFrank Li MODULE_LICENSE("GPL"); 3661