18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Fast Ethernet Controller (FEC) driver for Motorola MPC8xx. 48c2ecf20Sopenharmony_ci * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Right now, I am very wasteful with the buffers. I allocate memory 78c2ecf20Sopenharmony_ci * pages and then divide them into 2K frame buffers. This way I know I 88c2ecf20Sopenharmony_ci * have buffers large enough to hold one frame within one buffer descriptor. 98c2ecf20Sopenharmony_ci * Once I get this working, I will use 64 or 128 byte CPM buffers, which 108c2ecf20Sopenharmony_ci * will be much more memory efficient and will easily handle lots of 118c2ecf20Sopenharmony_ci * small packets. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Much better multiple PHY support by Magnus Damm. 148c2ecf20Sopenharmony_ci * Copyright (c) 2000 Ericsson Radio Systems AB. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Support for FEC controller of ColdFire processors. 178c2ecf20Sopenharmony_ci * Copyright (c) 2001-2005 Greg Ungerer (gerg@snapgear.com) 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be) 208c2ecf20Sopenharmony_ci * Copyright (c) 2004-2006 Macq Electronique SA. 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <linux/module.h> 268c2ecf20Sopenharmony_ci#include <linux/kernel.h> 278c2ecf20Sopenharmony_ci#include <linux/string.h> 288c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 298c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 308c2ecf20Sopenharmony_ci#include <linux/errno.h> 318c2ecf20Sopenharmony_ci#include <linux/ioport.h> 328c2ecf20Sopenharmony_ci#include <linux/slab.h> 338c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 348c2ecf20Sopenharmony_ci#include <linux/delay.h> 358c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 368c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 378c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 388c2ecf20Sopenharmony_ci#include <linux/in.h> 398c2ecf20Sopenharmony_ci#include <linux/ip.h> 408c2ecf20Sopenharmony_ci#include <net/ip.h> 418c2ecf20Sopenharmony_ci#include <net/tso.h> 428c2ecf20Sopenharmony_ci#include <linux/tcp.h> 438c2ecf20Sopenharmony_ci#include <linux/udp.h> 448c2ecf20Sopenharmony_ci#include <linux/icmp.h> 458c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 468c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 478c2ecf20Sopenharmony_ci#include <linux/bitops.h> 488c2ecf20Sopenharmony_ci#include <linux/io.h> 498c2ecf20Sopenharmony_ci#include <linux/irq.h> 508c2ecf20Sopenharmony_ci#include <linux/clk.h> 518c2ecf20Sopenharmony_ci#include <linux/crc32.h> 528c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 538c2ecf20Sopenharmony_ci#include <linux/mdio.h> 548c2ecf20Sopenharmony_ci#include <linux/phy.h> 558c2ecf20Sopenharmony_ci#include <linux/fec.h> 568c2ecf20Sopenharmony_ci#include <linux/of.h> 578c2ecf20Sopenharmony_ci#include <linux/of_device.h> 588c2ecf20Sopenharmony_ci#include <linux/of_gpio.h> 598c2ecf20Sopenharmony_ci#include <linux/of_mdio.h> 608c2ecf20Sopenharmony_ci#include <linux/of_net.h> 618c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 628c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 638c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h> 648c2ecf20Sopenharmony_ci#include <linux/prefetch.h> 658c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 668c2ecf20Sopenharmony_ci#include <linux/regmap.h> 678c2ecf20Sopenharmony_ci#include <soc/imx/cpuidle.h> 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#include "fec.h" 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void set_multicast_list(struct net_device *ndev); 748c2ecf20Sopenharmony_cistatic void fec_enet_itr_coal_init(struct net_device *ndev); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci#define DRIVER_NAME "fec" 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic const u16 fec_enet_vlan_pri_to_queue[8] = {0, 0, 1, 1, 1, 2, 2, 2}; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* Pause frame feild and FIFO threshold */ 818c2ecf20Sopenharmony_ci#define FEC_ENET_FCE (1 << 5) 828c2ecf20Sopenharmony_ci#define FEC_ENET_RSEM_V 0x84 838c2ecf20Sopenharmony_ci#define FEC_ENET_RSFL_V 16 848c2ecf20Sopenharmony_ci#define FEC_ENET_RAEM_V 0x8 858c2ecf20Sopenharmony_ci#define FEC_ENET_RAFL_V 0x8 868c2ecf20Sopenharmony_ci#define FEC_ENET_OPD_V 0xFFF0 878c2ecf20Sopenharmony_ci#define FEC_MDIO_PM_TIMEOUT 100 /* ms */ 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistruct fec_devinfo { 908c2ecf20Sopenharmony_ci u32 quirks; 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic const struct fec_devinfo fec_imx25_info = { 948c2ecf20Sopenharmony_ci .quirks = FEC_QUIRK_USE_GASKET | FEC_QUIRK_MIB_CLEAR | 958c2ecf20Sopenharmony_ci FEC_QUIRK_HAS_FRREG, 968c2ecf20Sopenharmony_ci}; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic const struct fec_devinfo fec_imx27_info = { 998c2ecf20Sopenharmony_ci .quirks = FEC_QUIRK_MIB_CLEAR | FEC_QUIRK_HAS_FRREG, 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic const struct fec_devinfo fec_imx28_info = { 1038c2ecf20Sopenharmony_ci .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME | 1048c2ecf20Sopenharmony_ci FEC_QUIRK_SINGLE_MDIO | FEC_QUIRK_HAS_RACC | 1058c2ecf20Sopenharmony_ci FEC_QUIRK_HAS_FRREG | FEC_QUIRK_CLEAR_SETUP_MII, 1068c2ecf20Sopenharmony_ci}; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic const struct fec_devinfo fec_imx6q_info = { 1098c2ecf20Sopenharmony_ci .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 1108c2ecf20Sopenharmony_ci FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 1118c2ecf20Sopenharmony_ci FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR006358 | 1128c2ecf20Sopenharmony_ci FEC_QUIRK_HAS_RACC | FEC_QUIRK_CLEAR_SETUP_MII, 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic const struct fec_devinfo fec_mvf600_info = { 1168c2ecf20Sopenharmony_ci .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_RACC, 1178c2ecf20Sopenharmony_ci}; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic const struct fec_devinfo fec_imx6x_info = { 1208c2ecf20Sopenharmony_ci .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 1218c2ecf20Sopenharmony_ci FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 1228c2ecf20Sopenharmony_ci FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | 1238c2ecf20Sopenharmony_ci FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | 1248c2ecf20Sopenharmony_ci FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE | 1258c2ecf20Sopenharmony_ci FEC_QUIRK_CLEAR_SETUP_MII, 1268c2ecf20Sopenharmony_ci}; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic const struct fec_devinfo fec_imx6ul_info = { 1298c2ecf20Sopenharmony_ci .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 1308c2ecf20Sopenharmony_ci FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 1318c2ecf20Sopenharmony_ci FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR007885 | 1328c2ecf20Sopenharmony_ci FEC_QUIRK_BUG_CAPTURE | FEC_QUIRK_HAS_RACC | 1338c2ecf20Sopenharmony_ci FEC_QUIRK_HAS_COALESCE | FEC_QUIRK_CLEAR_SETUP_MII, 1348c2ecf20Sopenharmony_ci}; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic struct platform_device_id fec_devtype[] = { 1378c2ecf20Sopenharmony_ci { 1388c2ecf20Sopenharmony_ci /* keep it for coldfire */ 1398c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 1408c2ecf20Sopenharmony_ci .driver_data = 0, 1418c2ecf20Sopenharmony_ci }, { 1428c2ecf20Sopenharmony_ci .name = "imx25-fec", 1438c2ecf20Sopenharmony_ci .driver_data = (kernel_ulong_t)&fec_imx25_info, 1448c2ecf20Sopenharmony_ci }, { 1458c2ecf20Sopenharmony_ci .name = "imx27-fec", 1468c2ecf20Sopenharmony_ci .driver_data = (kernel_ulong_t)&fec_imx27_info, 1478c2ecf20Sopenharmony_ci }, { 1488c2ecf20Sopenharmony_ci .name = "imx28-fec", 1498c2ecf20Sopenharmony_ci .driver_data = (kernel_ulong_t)&fec_imx28_info, 1508c2ecf20Sopenharmony_ci }, { 1518c2ecf20Sopenharmony_ci .name = "imx6q-fec", 1528c2ecf20Sopenharmony_ci .driver_data = (kernel_ulong_t)&fec_imx6q_info, 1538c2ecf20Sopenharmony_ci }, { 1548c2ecf20Sopenharmony_ci .name = "mvf600-fec", 1558c2ecf20Sopenharmony_ci .driver_data = (kernel_ulong_t)&fec_mvf600_info, 1568c2ecf20Sopenharmony_ci }, { 1578c2ecf20Sopenharmony_ci .name = "imx6sx-fec", 1588c2ecf20Sopenharmony_ci .driver_data = (kernel_ulong_t)&fec_imx6x_info, 1598c2ecf20Sopenharmony_ci }, { 1608c2ecf20Sopenharmony_ci .name = "imx6ul-fec", 1618c2ecf20Sopenharmony_ci .driver_data = (kernel_ulong_t)&fec_imx6ul_info, 1628c2ecf20Sopenharmony_ci }, { 1638c2ecf20Sopenharmony_ci /* sentinel */ 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci}; 1668c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, fec_devtype); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cienum imx_fec_type { 1698c2ecf20Sopenharmony_ci IMX25_FEC = 1, /* runs on i.mx25/50/53 */ 1708c2ecf20Sopenharmony_ci IMX27_FEC, /* runs on i.mx27/35/51 */ 1718c2ecf20Sopenharmony_ci IMX28_FEC, 1728c2ecf20Sopenharmony_ci IMX6Q_FEC, 1738c2ecf20Sopenharmony_ci MVF600_FEC, 1748c2ecf20Sopenharmony_ci IMX6SX_FEC, 1758c2ecf20Sopenharmony_ci IMX6UL_FEC, 1768c2ecf20Sopenharmony_ci}; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic const struct of_device_id fec_dt_ids[] = { 1798c2ecf20Sopenharmony_ci { .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], }, 1808c2ecf20Sopenharmony_ci { .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], }, 1818c2ecf20Sopenharmony_ci { .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], }, 1828c2ecf20Sopenharmony_ci { .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], }, 1838c2ecf20Sopenharmony_ci { .compatible = "fsl,mvf600-fec", .data = &fec_devtype[MVF600_FEC], }, 1848c2ecf20Sopenharmony_ci { .compatible = "fsl,imx6sx-fec", .data = &fec_devtype[IMX6SX_FEC], }, 1858c2ecf20Sopenharmony_ci { .compatible = "fsl,imx6ul-fec", .data = &fec_devtype[IMX6UL_FEC], }, 1868c2ecf20Sopenharmony_ci { /* sentinel */ } 1878c2ecf20Sopenharmony_ci}; 1888c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, fec_dt_ids); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic unsigned char macaddr[ETH_ALEN]; 1918c2ecf20Sopenharmony_cimodule_param_array(macaddr, byte, NULL, 0); 1928c2ecf20Sopenharmony_ciMODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci#if defined(CONFIG_M5272) 1958c2ecf20Sopenharmony_ci/* 1968c2ecf20Sopenharmony_ci * Some hardware gets it MAC address out of local flash memory. 1978c2ecf20Sopenharmony_ci * if this is non-zero then assume it is the address to get MAC from. 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_ci#if defined(CONFIG_NETtel) 2008c2ecf20Sopenharmony_ci#define FEC_FLASHMAC 0xf0006006 2018c2ecf20Sopenharmony_ci#elif defined(CONFIG_GILBARCONAP) || defined(CONFIG_SCALES) 2028c2ecf20Sopenharmony_ci#define FEC_FLASHMAC 0xf0006000 2038c2ecf20Sopenharmony_ci#elif defined(CONFIG_CANCam) 2048c2ecf20Sopenharmony_ci#define FEC_FLASHMAC 0xf0020000 2058c2ecf20Sopenharmony_ci#elif defined (CONFIG_M5272C3) 2068c2ecf20Sopenharmony_ci#define FEC_FLASHMAC (0xffe04000 + 4) 2078c2ecf20Sopenharmony_ci#elif defined(CONFIG_MOD5272) 2088c2ecf20Sopenharmony_ci#define FEC_FLASHMAC 0xffc0406b 2098c2ecf20Sopenharmony_ci#else 2108c2ecf20Sopenharmony_ci#define FEC_FLASHMAC 0 2118c2ecf20Sopenharmony_ci#endif 2128c2ecf20Sopenharmony_ci#endif /* CONFIG_M5272 */ 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci/* The FEC stores dest/src/type/vlan, data, and checksum for receive packets. 2158c2ecf20Sopenharmony_ci * 2168c2ecf20Sopenharmony_ci * 2048 byte skbufs are allocated. However, alignment requirements 2178c2ecf20Sopenharmony_ci * varies between FEC variants. Worst case is 64, so round down by 64. 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_ci#define PKT_MAXBUF_SIZE (round_down(2048 - 64, 64)) 2208c2ecf20Sopenharmony_ci#define PKT_MINBUF_SIZE 64 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci/* FEC receive acceleration */ 2238c2ecf20Sopenharmony_ci#define FEC_RACC_IPDIS (1 << 1) 2248c2ecf20Sopenharmony_ci#define FEC_RACC_PRODIS (1 << 2) 2258c2ecf20Sopenharmony_ci#define FEC_RACC_SHIFT16 BIT(7) 2268c2ecf20Sopenharmony_ci#define FEC_RACC_OPTIONS (FEC_RACC_IPDIS | FEC_RACC_PRODIS) 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci/* MIB Control Register */ 2298c2ecf20Sopenharmony_ci#define FEC_MIB_CTRLSTAT_DISABLE BIT(31) 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci/* 2328c2ecf20Sopenharmony_ci * The 5270/5271/5280/5282/532x RX control register also contains maximum frame 2338c2ecf20Sopenharmony_ci * size bits. Other FEC hardware does not, so we need to take that into 2348c2ecf20Sopenharmony_ci * account when setting it. 2358c2ecf20Sopenharmony_ci */ 2368c2ecf20Sopenharmony_ci#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ 2378c2ecf20Sopenharmony_ci defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ 2388c2ecf20Sopenharmony_ci defined(CONFIG_ARM64) 2398c2ecf20Sopenharmony_ci#define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16) 2408c2ecf20Sopenharmony_ci#else 2418c2ecf20Sopenharmony_ci#define OPT_FRAME_SIZE 0 2428c2ecf20Sopenharmony_ci#endif 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/* FEC MII MMFR bits definition */ 2458c2ecf20Sopenharmony_ci#define FEC_MMFR_ST (1 << 30) 2468c2ecf20Sopenharmony_ci#define FEC_MMFR_ST_C45 (0) 2478c2ecf20Sopenharmony_ci#define FEC_MMFR_OP_READ (2 << 28) 2488c2ecf20Sopenharmony_ci#define FEC_MMFR_OP_READ_C45 (3 << 28) 2498c2ecf20Sopenharmony_ci#define FEC_MMFR_OP_WRITE (1 << 28) 2508c2ecf20Sopenharmony_ci#define FEC_MMFR_OP_ADDR_WRITE (0) 2518c2ecf20Sopenharmony_ci#define FEC_MMFR_PA(v) ((v & 0x1f) << 23) 2528c2ecf20Sopenharmony_ci#define FEC_MMFR_RA(v) ((v & 0x1f) << 18) 2538c2ecf20Sopenharmony_ci#define FEC_MMFR_TA (2 << 16) 2548c2ecf20Sopenharmony_ci#define FEC_MMFR_DATA(v) (v & 0xffff) 2558c2ecf20Sopenharmony_ci/* FEC ECR bits definition */ 2568c2ecf20Sopenharmony_ci#define FEC_ECR_MAGICEN (1 << 2) 2578c2ecf20Sopenharmony_ci#define FEC_ECR_SLEEP (1 << 3) 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci#define FEC_MII_TIMEOUT 30000 /* us */ 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci/* Transmitter timeout */ 2628c2ecf20Sopenharmony_ci#define TX_TIMEOUT (2 * HZ) 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci#define FEC_PAUSE_FLAG_AUTONEG 0x1 2658c2ecf20Sopenharmony_ci#define FEC_PAUSE_FLAG_ENABLE 0x2 2668c2ecf20Sopenharmony_ci#define FEC_WOL_HAS_MAGIC_PACKET (0x1 << 0) 2678c2ecf20Sopenharmony_ci#define FEC_WOL_FLAG_ENABLE (0x1 << 1) 2688c2ecf20Sopenharmony_ci#define FEC_WOL_FLAG_SLEEP_ON (0x1 << 2) 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci#define COPYBREAK_DEFAULT 256 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci/* Max number of allowed TCP segments for software TSO */ 2738c2ecf20Sopenharmony_ci#define FEC_MAX_TSO_SEGS 100 2748c2ecf20Sopenharmony_ci#define FEC_MAX_SKB_DESCS (FEC_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS) 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci#define IS_TSO_HEADER(txq, addr) \ 2778c2ecf20Sopenharmony_ci ((addr >= txq->tso_hdrs_dma) && \ 2788c2ecf20Sopenharmony_ci (addr < txq->tso_hdrs_dma + txq->bd.ring_size * TSO_HEADER_SIZE)) 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic int mii_cnt; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, 2838c2ecf20Sopenharmony_ci struct bufdesc_prop *bd) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci return (bdp >= bd->last) ? bd->base 2868c2ecf20Sopenharmony_ci : (struct bufdesc *)(((void *)bdp) + bd->dsize); 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, 2908c2ecf20Sopenharmony_ci struct bufdesc_prop *bd) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci return (bdp <= bd->base) ? bd->last 2938c2ecf20Sopenharmony_ci : (struct bufdesc *)(((void *)bdp) - bd->dsize); 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic int fec_enet_get_bd_index(struct bufdesc *bdp, 2978c2ecf20Sopenharmony_ci struct bufdesc_prop *bd) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci return ((const char *)bdp - (const char *)bd->base) >> bd->dsize_log2; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic int fec_enet_get_free_txdesc_num(struct fec_enet_priv_tx_q *txq) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci int entries; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci entries = (((const char *)txq->dirty_tx - 3078c2ecf20Sopenharmony_ci (const char *)txq->bd.cur) >> txq->bd.dsize_log2) - 1; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci return entries >= 0 ? entries : entries + txq->bd.ring_size; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic void swap_buffer(void *bufaddr, int len) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci int i; 3158c2ecf20Sopenharmony_ci unsigned int *buf = bufaddr; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci for (i = 0; i < len; i += 4, buf++) 3188c2ecf20Sopenharmony_ci swab32s(buf); 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic void swap_buffer2(void *dst_buf, void *src_buf, int len) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci int i; 3248c2ecf20Sopenharmony_ci unsigned int *src = src_buf; 3258c2ecf20Sopenharmony_ci unsigned int *dst = dst_buf; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci for (i = 0; i < len; i += 4, src++, dst++) 3288c2ecf20Sopenharmony_ci *dst = swab32p(src); 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic void fec_dump(struct net_device *ndev) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 3348c2ecf20Sopenharmony_ci struct bufdesc *bdp; 3358c2ecf20Sopenharmony_ci struct fec_enet_priv_tx_q *txq; 3368c2ecf20Sopenharmony_ci int index = 0; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci netdev_info(ndev, "TX ring dump\n"); 3398c2ecf20Sopenharmony_ci pr_info("Nr SC addr len SKB\n"); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci txq = fep->tx_queue[0]; 3428c2ecf20Sopenharmony_ci bdp = txq->bd.base; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci do { 3458c2ecf20Sopenharmony_ci pr_info("%3u %c%c 0x%04x 0x%08x %4u %p\n", 3468c2ecf20Sopenharmony_ci index, 3478c2ecf20Sopenharmony_ci bdp == txq->bd.cur ? 'S' : ' ', 3488c2ecf20Sopenharmony_ci bdp == txq->dirty_tx ? 'H' : ' ', 3498c2ecf20Sopenharmony_ci fec16_to_cpu(bdp->cbd_sc), 3508c2ecf20Sopenharmony_ci fec32_to_cpu(bdp->cbd_bufaddr), 3518c2ecf20Sopenharmony_ci fec16_to_cpu(bdp->cbd_datlen), 3528c2ecf20Sopenharmony_ci txq->tx_skbuff[index]); 3538c2ecf20Sopenharmony_ci bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 3548c2ecf20Sopenharmony_ci index++; 3558c2ecf20Sopenharmony_ci } while (bdp != txq->bd.base); 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic inline bool is_ipv4_pkt(struct sk_buff *skb) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci return skb->protocol == htons(ETH_P_IP) && ip_hdr(skb)->version == 4; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic int 3648c2ecf20Sopenharmony_cifec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci /* Only run for packets requiring a checksum. */ 3678c2ecf20Sopenharmony_ci if (skb->ip_summed != CHECKSUM_PARTIAL) 3688c2ecf20Sopenharmony_ci return 0; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (unlikely(skb_cow_head(skb, 0))) 3718c2ecf20Sopenharmony_ci return -1; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (is_ipv4_pkt(skb)) 3748c2ecf20Sopenharmony_ci ip_hdr(skb)->check = 0; 3758c2ecf20Sopenharmony_ci *(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) = 0; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci return 0; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic struct bufdesc * 3818c2ecf20Sopenharmony_cifec_enet_txq_submit_frag_skb(struct fec_enet_priv_tx_q *txq, 3828c2ecf20Sopenharmony_ci struct sk_buff *skb, 3838c2ecf20Sopenharmony_ci struct net_device *ndev) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 3868c2ecf20Sopenharmony_ci struct bufdesc *bdp = txq->bd.cur; 3878c2ecf20Sopenharmony_ci struct bufdesc_ex *ebdp; 3888c2ecf20Sopenharmony_ci int nr_frags = skb_shinfo(skb)->nr_frags; 3898c2ecf20Sopenharmony_ci int frag, frag_len; 3908c2ecf20Sopenharmony_ci unsigned short status; 3918c2ecf20Sopenharmony_ci unsigned int estatus = 0; 3928c2ecf20Sopenharmony_ci skb_frag_t *this_frag; 3938c2ecf20Sopenharmony_ci unsigned int index; 3948c2ecf20Sopenharmony_ci void *bufaddr; 3958c2ecf20Sopenharmony_ci dma_addr_t addr; 3968c2ecf20Sopenharmony_ci int i; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci for (frag = 0; frag < nr_frags; frag++) { 3998c2ecf20Sopenharmony_ci this_frag = &skb_shinfo(skb)->frags[frag]; 4008c2ecf20Sopenharmony_ci bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 4018c2ecf20Sopenharmony_ci ebdp = (struct bufdesc_ex *)bdp; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci status = fec16_to_cpu(bdp->cbd_sc); 4048c2ecf20Sopenharmony_ci status &= ~BD_ENET_TX_STATS; 4058c2ecf20Sopenharmony_ci status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 4068c2ecf20Sopenharmony_ci frag_len = skb_frag_size(&skb_shinfo(skb)->frags[frag]); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* Handle the last BD specially */ 4098c2ecf20Sopenharmony_ci if (frag == nr_frags - 1) { 4108c2ecf20Sopenharmony_ci status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); 4118c2ecf20Sopenharmony_ci if (fep->bufdesc_ex) { 4128c2ecf20Sopenharmony_ci estatus |= BD_ENET_TX_INT; 4138c2ecf20Sopenharmony_ci if (unlikely(skb_shinfo(skb)->tx_flags & 4148c2ecf20Sopenharmony_ci SKBTX_HW_TSTAMP && fep->hwts_tx_en)) 4158c2ecf20Sopenharmony_ci estatus |= BD_ENET_TX_TS; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (fep->bufdesc_ex) { 4208c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_AVB) 4218c2ecf20Sopenharmony_ci estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 4228c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) 4238c2ecf20Sopenharmony_ci estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 4248c2ecf20Sopenharmony_ci ebdp->cbd_bdu = 0; 4258c2ecf20Sopenharmony_ci ebdp->cbd_esc = cpu_to_fec32(estatus); 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci bufaddr = skb_frag_address(this_frag); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci index = fec_enet_get_bd_index(bdp, &txq->bd); 4318c2ecf20Sopenharmony_ci if (((unsigned long) bufaddr) & fep->tx_align || 4328c2ecf20Sopenharmony_ci fep->quirks & FEC_QUIRK_SWAP_FRAME) { 4338c2ecf20Sopenharmony_ci memcpy(txq->tx_bounce[index], bufaddr, frag_len); 4348c2ecf20Sopenharmony_ci bufaddr = txq->tx_bounce[index]; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 4378c2ecf20Sopenharmony_ci swap_buffer(bufaddr, frag_len); 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci addr = dma_map_single(&fep->pdev->dev, bufaddr, frag_len, 4418c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 4428c2ecf20Sopenharmony_ci if (dma_mapping_error(&fep->pdev->dev, addr)) { 4438c2ecf20Sopenharmony_ci if (net_ratelimit()) 4448c2ecf20Sopenharmony_ci netdev_err(ndev, "Tx DMA memory map failed\n"); 4458c2ecf20Sopenharmony_ci goto dma_mapping_error; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci bdp->cbd_bufaddr = cpu_to_fec32(addr); 4498c2ecf20Sopenharmony_ci bdp->cbd_datlen = cpu_to_fec16(frag_len); 4508c2ecf20Sopenharmony_ci /* Make sure the updates to rest of the descriptor are 4518c2ecf20Sopenharmony_ci * performed before transferring ownership. 4528c2ecf20Sopenharmony_ci */ 4538c2ecf20Sopenharmony_ci wmb(); 4548c2ecf20Sopenharmony_ci bdp->cbd_sc = cpu_to_fec16(status); 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci return bdp; 4588c2ecf20Sopenharmony_cidma_mapping_error: 4598c2ecf20Sopenharmony_ci bdp = txq->bd.cur; 4608c2ecf20Sopenharmony_ci for (i = 0; i < frag; i++) { 4618c2ecf20Sopenharmony_ci bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 4628c2ecf20Sopenharmony_ci dma_unmap_single(&fep->pdev->dev, fec32_to_cpu(bdp->cbd_bufaddr), 4638c2ecf20Sopenharmony_ci fec16_to_cpu(bdp->cbd_datlen), DMA_TO_DEVICE); 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq, 4698c2ecf20Sopenharmony_ci struct sk_buff *skb, struct net_device *ndev) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 4728c2ecf20Sopenharmony_ci int nr_frags = skb_shinfo(skb)->nr_frags; 4738c2ecf20Sopenharmony_ci struct bufdesc *bdp, *last_bdp; 4748c2ecf20Sopenharmony_ci void *bufaddr; 4758c2ecf20Sopenharmony_ci dma_addr_t addr; 4768c2ecf20Sopenharmony_ci unsigned short status; 4778c2ecf20Sopenharmony_ci unsigned short buflen; 4788c2ecf20Sopenharmony_ci unsigned int estatus = 0; 4798c2ecf20Sopenharmony_ci unsigned int index; 4808c2ecf20Sopenharmony_ci int entries_free; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci entries_free = fec_enet_get_free_txdesc_num(txq); 4838c2ecf20Sopenharmony_ci if (entries_free < MAX_SKB_FRAGS + 1) { 4848c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 4858c2ecf20Sopenharmony_ci if (net_ratelimit()) 4868c2ecf20Sopenharmony_ci netdev_err(ndev, "NOT enough BD for SG!\n"); 4878c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci /* Protocol checksum off-load for TCP and UDP. */ 4918c2ecf20Sopenharmony_ci if (fec_enet_clear_csum(skb, ndev)) { 4928c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 4938c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci /* Fill in a Tx ring entry */ 4978c2ecf20Sopenharmony_ci bdp = txq->bd.cur; 4988c2ecf20Sopenharmony_ci last_bdp = bdp; 4998c2ecf20Sopenharmony_ci status = fec16_to_cpu(bdp->cbd_sc); 5008c2ecf20Sopenharmony_ci status &= ~BD_ENET_TX_STATS; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* Set buffer length and buffer pointer */ 5038c2ecf20Sopenharmony_ci bufaddr = skb->data; 5048c2ecf20Sopenharmony_ci buflen = skb_headlen(skb); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci index = fec_enet_get_bd_index(bdp, &txq->bd); 5078c2ecf20Sopenharmony_ci if (((unsigned long) bufaddr) & fep->tx_align || 5088c2ecf20Sopenharmony_ci fep->quirks & FEC_QUIRK_SWAP_FRAME) { 5098c2ecf20Sopenharmony_ci memcpy(txq->tx_bounce[index], skb->data, buflen); 5108c2ecf20Sopenharmony_ci bufaddr = txq->tx_bounce[index]; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 5138c2ecf20Sopenharmony_ci swap_buffer(bufaddr, buflen); 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci /* Push the data cache so the CPM does not get stale memory data. */ 5178c2ecf20Sopenharmony_ci addr = dma_map_single(&fep->pdev->dev, bufaddr, buflen, DMA_TO_DEVICE); 5188c2ecf20Sopenharmony_ci if (dma_mapping_error(&fep->pdev->dev, addr)) { 5198c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 5208c2ecf20Sopenharmony_ci if (net_ratelimit()) 5218c2ecf20Sopenharmony_ci netdev_err(ndev, "Tx DMA memory map failed\n"); 5228c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci if (nr_frags) { 5268c2ecf20Sopenharmony_ci last_bdp = fec_enet_txq_submit_frag_skb(txq, skb, ndev); 5278c2ecf20Sopenharmony_ci if (IS_ERR(last_bdp)) { 5288c2ecf20Sopenharmony_ci dma_unmap_single(&fep->pdev->dev, addr, 5298c2ecf20Sopenharmony_ci buflen, DMA_TO_DEVICE); 5308c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 5318c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci } else { 5348c2ecf20Sopenharmony_ci status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); 5358c2ecf20Sopenharmony_ci if (fep->bufdesc_ex) { 5368c2ecf20Sopenharmony_ci estatus = BD_ENET_TX_INT; 5378c2ecf20Sopenharmony_ci if (unlikely(skb_shinfo(skb)->tx_flags & 5388c2ecf20Sopenharmony_ci SKBTX_HW_TSTAMP && fep->hwts_tx_en)) 5398c2ecf20Sopenharmony_ci estatus |= BD_ENET_TX_TS; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci bdp->cbd_bufaddr = cpu_to_fec32(addr); 5438c2ecf20Sopenharmony_ci bdp->cbd_datlen = cpu_to_fec16(buflen); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci if (fep->bufdesc_ex) { 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && 5508c2ecf20Sopenharmony_ci fep->hwts_tx_en)) 5518c2ecf20Sopenharmony_ci skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_AVB) 5548c2ecf20Sopenharmony_ci estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) 5578c2ecf20Sopenharmony_ci estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci ebdp->cbd_bdu = 0; 5608c2ecf20Sopenharmony_ci ebdp->cbd_esc = cpu_to_fec32(estatus); 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci index = fec_enet_get_bd_index(last_bdp, &txq->bd); 5648c2ecf20Sopenharmony_ci /* Save skb pointer */ 5658c2ecf20Sopenharmony_ci txq->tx_skbuff[index] = skb; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci /* Make sure the updates to rest of the descriptor are performed before 5688c2ecf20Sopenharmony_ci * transferring ownership. 5698c2ecf20Sopenharmony_ci */ 5708c2ecf20Sopenharmony_ci wmb(); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci /* Send it on its way. Tell FEC it's ready, interrupt when done, 5738c2ecf20Sopenharmony_ci * it's the last BD of the frame, and to put the CRC on the end. 5748c2ecf20Sopenharmony_ci */ 5758c2ecf20Sopenharmony_ci status |= (BD_ENET_TX_READY | BD_ENET_TX_TC); 5768c2ecf20Sopenharmony_ci bdp->cbd_sc = cpu_to_fec16(status); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci /* If this was the last BD in the ring, start at the beginning again. */ 5798c2ecf20Sopenharmony_ci bdp = fec_enet_get_nextdesc(last_bdp, &txq->bd); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci skb_tx_timestamp(skb); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci /* Make sure the update to bdp and tx_skbuff are performed before 5848c2ecf20Sopenharmony_ci * txq->bd.cur. 5858c2ecf20Sopenharmony_ci */ 5868c2ecf20Sopenharmony_ci wmb(); 5878c2ecf20Sopenharmony_ci txq->bd.cur = bdp; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci /* Trigger transmission start */ 5908c2ecf20Sopenharmony_ci writel(0, txq->bd.reg_desc_active); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci return 0; 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic int 5968c2ecf20Sopenharmony_cifec_enet_txq_put_data_tso(struct fec_enet_priv_tx_q *txq, struct sk_buff *skb, 5978c2ecf20Sopenharmony_ci struct net_device *ndev, 5988c2ecf20Sopenharmony_ci struct bufdesc *bdp, int index, char *data, 5998c2ecf20Sopenharmony_ci int size, bool last_tcp, bool is_last) 6008c2ecf20Sopenharmony_ci{ 6018c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 6028c2ecf20Sopenharmony_ci struct bufdesc_ex *ebdp = container_of(bdp, struct bufdesc_ex, desc); 6038c2ecf20Sopenharmony_ci unsigned short status; 6048c2ecf20Sopenharmony_ci unsigned int estatus = 0; 6058c2ecf20Sopenharmony_ci dma_addr_t addr; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci status = fec16_to_cpu(bdp->cbd_sc); 6088c2ecf20Sopenharmony_ci status &= ~BD_ENET_TX_STATS; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if (((unsigned long) data) & fep->tx_align || 6138c2ecf20Sopenharmony_ci fep->quirks & FEC_QUIRK_SWAP_FRAME) { 6148c2ecf20Sopenharmony_ci memcpy(txq->tx_bounce[index], data, size); 6158c2ecf20Sopenharmony_ci data = txq->tx_bounce[index]; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 6188c2ecf20Sopenharmony_ci swap_buffer(data, size); 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci addr = dma_map_single(&fep->pdev->dev, data, size, DMA_TO_DEVICE); 6228c2ecf20Sopenharmony_ci if (dma_mapping_error(&fep->pdev->dev, addr)) { 6238c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 6248c2ecf20Sopenharmony_ci if (net_ratelimit()) 6258c2ecf20Sopenharmony_ci netdev_err(ndev, "Tx DMA memory map failed\n"); 6268c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci bdp->cbd_datlen = cpu_to_fec16(size); 6308c2ecf20Sopenharmony_ci bdp->cbd_bufaddr = cpu_to_fec32(addr); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci if (fep->bufdesc_ex) { 6338c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_AVB) 6348c2ecf20Sopenharmony_ci estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 6358c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) 6368c2ecf20Sopenharmony_ci estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 6378c2ecf20Sopenharmony_ci ebdp->cbd_bdu = 0; 6388c2ecf20Sopenharmony_ci ebdp->cbd_esc = cpu_to_fec32(estatus); 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci /* Handle the last BD specially */ 6428c2ecf20Sopenharmony_ci if (last_tcp) 6438c2ecf20Sopenharmony_ci status |= (BD_ENET_TX_LAST | BD_ENET_TX_TC); 6448c2ecf20Sopenharmony_ci if (is_last) { 6458c2ecf20Sopenharmony_ci status |= BD_ENET_TX_INTR; 6468c2ecf20Sopenharmony_ci if (fep->bufdesc_ex) 6478c2ecf20Sopenharmony_ci ebdp->cbd_esc |= cpu_to_fec32(BD_ENET_TX_INT); 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci bdp->cbd_sc = cpu_to_fec16(status); 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci return 0; 6538c2ecf20Sopenharmony_ci} 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_cistatic int 6568c2ecf20Sopenharmony_cifec_enet_txq_put_hdr_tso(struct fec_enet_priv_tx_q *txq, 6578c2ecf20Sopenharmony_ci struct sk_buff *skb, struct net_device *ndev, 6588c2ecf20Sopenharmony_ci struct bufdesc *bdp, int index) 6598c2ecf20Sopenharmony_ci{ 6608c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 6618c2ecf20Sopenharmony_ci int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); 6628c2ecf20Sopenharmony_ci struct bufdesc_ex *ebdp = container_of(bdp, struct bufdesc_ex, desc); 6638c2ecf20Sopenharmony_ci void *bufaddr; 6648c2ecf20Sopenharmony_ci unsigned long dmabuf; 6658c2ecf20Sopenharmony_ci unsigned short status; 6668c2ecf20Sopenharmony_ci unsigned int estatus = 0; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci status = fec16_to_cpu(bdp->cbd_sc); 6698c2ecf20Sopenharmony_ci status &= ~BD_ENET_TX_STATS; 6708c2ecf20Sopenharmony_ci status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci bufaddr = txq->tso_hdrs + index * TSO_HEADER_SIZE; 6738c2ecf20Sopenharmony_ci dmabuf = txq->tso_hdrs_dma + index * TSO_HEADER_SIZE; 6748c2ecf20Sopenharmony_ci if (((unsigned long)bufaddr) & fep->tx_align || 6758c2ecf20Sopenharmony_ci fep->quirks & FEC_QUIRK_SWAP_FRAME) { 6768c2ecf20Sopenharmony_ci memcpy(txq->tx_bounce[index], skb->data, hdr_len); 6778c2ecf20Sopenharmony_ci bufaddr = txq->tx_bounce[index]; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 6808c2ecf20Sopenharmony_ci swap_buffer(bufaddr, hdr_len); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci dmabuf = dma_map_single(&fep->pdev->dev, bufaddr, 6838c2ecf20Sopenharmony_ci hdr_len, DMA_TO_DEVICE); 6848c2ecf20Sopenharmony_ci if (dma_mapping_error(&fep->pdev->dev, dmabuf)) { 6858c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 6868c2ecf20Sopenharmony_ci if (net_ratelimit()) 6878c2ecf20Sopenharmony_ci netdev_err(ndev, "Tx DMA memory map failed\n"); 6888c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci } 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci bdp->cbd_bufaddr = cpu_to_fec32(dmabuf); 6938c2ecf20Sopenharmony_ci bdp->cbd_datlen = cpu_to_fec16(hdr_len); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci if (fep->bufdesc_ex) { 6968c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_AVB) 6978c2ecf20Sopenharmony_ci estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 6988c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) 6998c2ecf20Sopenharmony_ci estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 7008c2ecf20Sopenharmony_ci ebdp->cbd_bdu = 0; 7018c2ecf20Sopenharmony_ci ebdp->cbd_esc = cpu_to_fec32(estatus); 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci bdp->cbd_sc = cpu_to_fec16(status); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci return 0; 7078c2ecf20Sopenharmony_ci} 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_cistatic int fec_enet_txq_submit_tso(struct fec_enet_priv_tx_q *txq, 7108c2ecf20Sopenharmony_ci struct sk_buff *skb, 7118c2ecf20Sopenharmony_ci struct net_device *ndev) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 7148c2ecf20Sopenharmony_ci int hdr_len, total_len, data_left; 7158c2ecf20Sopenharmony_ci struct bufdesc *bdp = txq->bd.cur; 7168c2ecf20Sopenharmony_ci struct tso_t tso; 7178c2ecf20Sopenharmony_ci unsigned int index = 0; 7188c2ecf20Sopenharmony_ci int ret; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci if (tso_count_descs(skb) >= fec_enet_get_free_txdesc_num(txq)) { 7218c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 7228c2ecf20Sopenharmony_ci if (net_ratelimit()) 7238c2ecf20Sopenharmony_ci netdev_err(ndev, "NOT enough BD for TSO!\n"); 7248c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci /* Protocol checksum off-load for TCP and UDP. */ 7288c2ecf20Sopenharmony_ci if (fec_enet_clear_csum(skb, ndev)) { 7298c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 7308c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci /* Initialize the TSO handler, and prepare the first payload */ 7348c2ecf20Sopenharmony_ci hdr_len = tso_start(skb, &tso); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci total_len = skb->len - hdr_len; 7378c2ecf20Sopenharmony_ci while (total_len > 0) { 7388c2ecf20Sopenharmony_ci char *hdr; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci index = fec_enet_get_bd_index(bdp, &txq->bd); 7418c2ecf20Sopenharmony_ci data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len); 7428c2ecf20Sopenharmony_ci total_len -= data_left; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci /* prepare packet headers: MAC + IP + TCP */ 7458c2ecf20Sopenharmony_ci hdr = txq->tso_hdrs + index * TSO_HEADER_SIZE; 7468c2ecf20Sopenharmony_ci tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0); 7478c2ecf20Sopenharmony_ci ret = fec_enet_txq_put_hdr_tso(txq, skb, ndev, bdp, index); 7488c2ecf20Sopenharmony_ci if (ret) 7498c2ecf20Sopenharmony_ci goto err_release; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci while (data_left > 0) { 7528c2ecf20Sopenharmony_ci int size; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci size = min_t(int, tso.size, data_left); 7558c2ecf20Sopenharmony_ci bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 7568c2ecf20Sopenharmony_ci index = fec_enet_get_bd_index(bdp, &txq->bd); 7578c2ecf20Sopenharmony_ci ret = fec_enet_txq_put_data_tso(txq, skb, ndev, 7588c2ecf20Sopenharmony_ci bdp, index, 7598c2ecf20Sopenharmony_ci tso.data, size, 7608c2ecf20Sopenharmony_ci size == data_left, 7618c2ecf20Sopenharmony_ci total_len == 0); 7628c2ecf20Sopenharmony_ci if (ret) 7638c2ecf20Sopenharmony_ci goto err_release; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci data_left -= size; 7668c2ecf20Sopenharmony_ci tso_build_data(skb, &tso, size); 7678c2ecf20Sopenharmony_ci } 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci /* Save skb pointer */ 7738c2ecf20Sopenharmony_ci txq->tx_skbuff[index] = skb; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci skb_tx_timestamp(skb); 7768c2ecf20Sopenharmony_ci txq->bd.cur = bdp; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci /* Trigger transmission start */ 7798c2ecf20Sopenharmony_ci if (!(fep->quirks & FEC_QUIRK_ERR007885) || 7808c2ecf20Sopenharmony_ci !readl(txq->bd.reg_desc_active) || 7818c2ecf20Sopenharmony_ci !readl(txq->bd.reg_desc_active) || 7828c2ecf20Sopenharmony_ci !readl(txq->bd.reg_desc_active) || 7838c2ecf20Sopenharmony_ci !readl(txq->bd.reg_desc_active)) 7848c2ecf20Sopenharmony_ci writel(0, txq->bd.reg_desc_active); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci return 0; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_cierr_release: 7898c2ecf20Sopenharmony_ci /* TODO: Release all used data descriptors for TSO */ 7908c2ecf20Sopenharmony_ci return ret; 7918c2ecf20Sopenharmony_ci} 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_cistatic netdev_tx_t 7948c2ecf20Sopenharmony_cifec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) 7958c2ecf20Sopenharmony_ci{ 7968c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 7978c2ecf20Sopenharmony_ci int entries_free; 7988c2ecf20Sopenharmony_ci unsigned short queue; 7998c2ecf20Sopenharmony_ci struct fec_enet_priv_tx_q *txq; 8008c2ecf20Sopenharmony_ci struct netdev_queue *nq; 8018c2ecf20Sopenharmony_ci int ret; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci queue = skb_get_queue_mapping(skb); 8048c2ecf20Sopenharmony_ci txq = fep->tx_queue[queue]; 8058c2ecf20Sopenharmony_ci nq = netdev_get_tx_queue(ndev, queue); 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci if (skb_is_gso(skb)) 8088c2ecf20Sopenharmony_ci ret = fec_enet_txq_submit_tso(txq, skb, ndev); 8098c2ecf20Sopenharmony_ci else 8108c2ecf20Sopenharmony_ci ret = fec_enet_txq_submit_skb(txq, skb, ndev); 8118c2ecf20Sopenharmony_ci if (ret) 8128c2ecf20Sopenharmony_ci return ret; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci entries_free = fec_enet_get_free_txdesc_num(txq); 8158c2ecf20Sopenharmony_ci if (entries_free <= txq->tx_stop_threshold) 8168c2ecf20Sopenharmony_ci netif_tx_stop_queue(nq); 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 8198c2ecf20Sopenharmony_ci} 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci/* Init RX & TX buffer descriptors 8228c2ecf20Sopenharmony_ci */ 8238c2ecf20Sopenharmony_cistatic void fec_enet_bd_init(struct net_device *dev) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(dev); 8268c2ecf20Sopenharmony_ci struct fec_enet_priv_tx_q *txq; 8278c2ecf20Sopenharmony_ci struct fec_enet_priv_rx_q *rxq; 8288c2ecf20Sopenharmony_ci struct bufdesc *bdp; 8298c2ecf20Sopenharmony_ci unsigned int i; 8308c2ecf20Sopenharmony_ci unsigned int q; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci for (q = 0; q < fep->num_rx_queues; q++) { 8338c2ecf20Sopenharmony_ci /* Initialize the receive buffer descriptors. */ 8348c2ecf20Sopenharmony_ci rxq = fep->rx_queue[q]; 8358c2ecf20Sopenharmony_ci bdp = rxq->bd.base; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci for (i = 0; i < rxq->bd.ring_size; i++) { 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci /* Initialize the BD for every fragment in the page. */ 8408c2ecf20Sopenharmony_ci if (bdp->cbd_bufaddr) 8418c2ecf20Sopenharmony_ci bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); 8428c2ecf20Sopenharmony_ci else 8438c2ecf20Sopenharmony_ci bdp->cbd_sc = cpu_to_fec16(0); 8448c2ecf20Sopenharmony_ci bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci /* Set the last buffer to wrap */ 8488c2ecf20Sopenharmony_ci bdp = fec_enet_get_prevdesc(bdp, &rxq->bd); 8498c2ecf20Sopenharmony_ci bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci rxq->bd.cur = rxq->bd.base; 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci for (q = 0; q < fep->num_tx_queues; q++) { 8558c2ecf20Sopenharmony_ci /* ...and the same for transmit */ 8568c2ecf20Sopenharmony_ci txq = fep->tx_queue[q]; 8578c2ecf20Sopenharmony_ci bdp = txq->bd.base; 8588c2ecf20Sopenharmony_ci txq->bd.cur = bdp; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci for (i = 0; i < txq->bd.ring_size; i++) { 8618c2ecf20Sopenharmony_ci /* Initialize the BD for every fragment in the page. */ 8628c2ecf20Sopenharmony_ci bdp->cbd_sc = cpu_to_fec16(0); 8638c2ecf20Sopenharmony_ci if (bdp->cbd_bufaddr && 8648c2ecf20Sopenharmony_ci !IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr))) 8658c2ecf20Sopenharmony_ci dma_unmap_single(&fep->pdev->dev, 8668c2ecf20Sopenharmony_ci fec32_to_cpu(bdp->cbd_bufaddr), 8678c2ecf20Sopenharmony_ci fec16_to_cpu(bdp->cbd_datlen), 8688c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 8698c2ecf20Sopenharmony_ci if (txq->tx_skbuff[i]) { 8708c2ecf20Sopenharmony_ci dev_kfree_skb_any(txq->tx_skbuff[i]); 8718c2ecf20Sopenharmony_ci txq->tx_skbuff[i] = NULL; 8728c2ecf20Sopenharmony_ci } 8738c2ecf20Sopenharmony_ci bdp->cbd_bufaddr = cpu_to_fec32(0); 8748c2ecf20Sopenharmony_ci bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci /* Set the last buffer to wrap */ 8788c2ecf20Sopenharmony_ci bdp = fec_enet_get_prevdesc(bdp, &txq->bd); 8798c2ecf20Sopenharmony_ci bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 8808c2ecf20Sopenharmony_ci txq->dirty_tx = bdp; 8818c2ecf20Sopenharmony_ci } 8828c2ecf20Sopenharmony_ci} 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_cistatic void fec_enet_active_rxring(struct net_device *ndev) 8858c2ecf20Sopenharmony_ci{ 8868c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 8878c2ecf20Sopenharmony_ci int i; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci for (i = 0; i < fep->num_rx_queues; i++) 8908c2ecf20Sopenharmony_ci writel(0, fep->rx_queue[i]->bd.reg_desc_active); 8918c2ecf20Sopenharmony_ci} 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_cistatic void fec_enet_enable_ring(struct net_device *ndev) 8948c2ecf20Sopenharmony_ci{ 8958c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 8968c2ecf20Sopenharmony_ci struct fec_enet_priv_tx_q *txq; 8978c2ecf20Sopenharmony_ci struct fec_enet_priv_rx_q *rxq; 8988c2ecf20Sopenharmony_ci int i; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci for (i = 0; i < fep->num_rx_queues; i++) { 9018c2ecf20Sopenharmony_ci rxq = fep->rx_queue[i]; 9028c2ecf20Sopenharmony_ci writel(rxq->bd.dma, fep->hwp + FEC_R_DES_START(i)); 9038c2ecf20Sopenharmony_ci writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_R_BUFF_SIZE(i)); 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci /* enable DMA1/2 */ 9068c2ecf20Sopenharmony_ci if (i) 9078c2ecf20Sopenharmony_ci writel(RCMR_MATCHEN | RCMR_CMP(i), 9088c2ecf20Sopenharmony_ci fep->hwp + FEC_RCMR(i)); 9098c2ecf20Sopenharmony_ci } 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci for (i = 0; i < fep->num_tx_queues; i++) { 9128c2ecf20Sopenharmony_ci txq = fep->tx_queue[i]; 9138c2ecf20Sopenharmony_ci writel(txq->bd.dma, fep->hwp + FEC_X_DES_START(i)); 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci /* enable DMA1/2 */ 9168c2ecf20Sopenharmony_ci if (i) 9178c2ecf20Sopenharmony_ci writel(DMA_CLASS_EN | IDLE_SLOPE(i), 9188c2ecf20Sopenharmony_ci fep->hwp + FEC_DMA_CFG(i)); 9198c2ecf20Sopenharmony_ci } 9208c2ecf20Sopenharmony_ci} 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_cistatic void fec_enet_reset_skb(struct net_device *ndev) 9238c2ecf20Sopenharmony_ci{ 9248c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 9258c2ecf20Sopenharmony_ci struct fec_enet_priv_tx_q *txq; 9268c2ecf20Sopenharmony_ci int i, j; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci for (i = 0; i < fep->num_tx_queues; i++) { 9298c2ecf20Sopenharmony_ci txq = fep->tx_queue[i]; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci for (j = 0; j < txq->bd.ring_size; j++) { 9328c2ecf20Sopenharmony_ci if (txq->tx_skbuff[j]) { 9338c2ecf20Sopenharmony_ci dev_kfree_skb_any(txq->tx_skbuff[j]); 9348c2ecf20Sopenharmony_ci txq->tx_skbuff[j] = NULL; 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci } 9378c2ecf20Sopenharmony_ci } 9388c2ecf20Sopenharmony_ci} 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci/* 9418c2ecf20Sopenharmony_ci * This function is called to start or restart the FEC during a link 9428c2ecf20Sopenharmony_ci * change, transmit timeout, or to reconfigure the FEC. The network 9438c2ecf20Sopenharmony_ci * packet processing for this device must be stopped before this call. 9448c2ecf20Sopenharmony_ci */ 9458c2ecf20Sopenharmony_cistatic void 9468c2ecf20Sopenharmony_cifec_restart(struct net_device *ndev) 9478c2ecf20Sopenharmony_ci{ 9488c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 9498c2ecf20Sopenharmony_ci u32 val; 9508c2ecf20Sopenharmony_ci u32 temp_mac[2]; 9518c2ecf20Sopenharmony_ci u32 rcntl = OPT_FRAME_SIZE | 0x04; 9528c2ecf20Sopenharmony_ci u32 ecntl = 0x2; /* ETHEREN */ 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci /* Whack a reset. We should wait for this. 9558c2ecf20Sopenharmony_ci * For i.MX6SX SOC, enet use AXI bus, we use disable MAC 9568c2ecf20Sopenharmony_ci * instead of reset MAC itself. 9578c2ecf20Sopenharmony_ci */ 9588c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_AVB) { 9598c2ecf20Sopenharmony_ci writel(0, fep->hwp + FEC_ECNTRL); 9608c2ecf20Sopenharmony_ci } else { 9618c2ecf20Sopenharmony_ci writel(1, fep->hwp + FEC_ECNTRL); 9628c2ecf20Sopenharmony_ci udelay(10); 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci /* 9668c2ecf20Sopenharmony_ci * enet-mac reset will reset mac address registers too, 9678c2ecf20Sopenharmony_ci * so need to reconfigure it. 9688c2ecf20Sopenharmony_ci */ 9698c2ecf20Sopenharmony_ci memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN); 9708c2ecf20Sopenharmony_ci writel((__force u32)cpu_to_be32(temp_mac[0]), 9718c2ecf20Sopenharmony_ci fep->hwp + FEC_ADDR_LOW); 9728c2ecf20Sopenharmony_ci writel((__force u32)cpu_to_be32(temp_mac[1]), 9738c2ecf20Sopenharmony_ci fep->hwp + FEC_ADDR_HIGH); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci /* Clear any outstanding interrupt, except MDIO. */ 9768c2ecf20Sopenharmony_ci writel((0xffffffff & ~FEC_ENET_MII), fep->hwp + FEC_IEVENT); 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci fec_enet_bd_init(ndev); 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci fec_enet_enable_ring(ndev); 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci /* Reset tx SKB buffers. */ 9838c2ecf20Sopenharmony_ci fec_enet_reset_skb(ndev); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci /* Enable MII mode */ 9868c2ecf20Sopenharmony_ci if (fep->full_duplex == DUPLEX_FULL) { 9878c2ecf20Sopenharmony_ci /* FD enable */ 9888c2ecf20Sopenharmony_ci writel(0x04, fep->hwp + FEC_X_CNTRL); 9898c2ecf20Sopenharmony_ci } else { 9908c2ecf20Sopenharmony_ci /* No Rcv on Xmit */ 9918c2ecf20Sopenharmony_ci rcntl |= 0x02; 9928c2ecf20Sopenharmony_ci writel(0x0, fep->hwp + FEC_X_CNTRL); 9938c2ecf20Sopenharmony_ci } 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci /* Set MII speed */ 9968c2ecf20Sopenharmony_ci writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci#if !defined(CONFIG_M5272) 9998c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_RACC) { 10008c2ecf20Sopenharmony_ci val = readl(fep->hwp + FEC_RACC); 10018c2ecf20Sopenharmony_ci /* align IP header */ 10028c2ecf20Sopenharmony_ci val |= FEC_RACC_SHIFT16; 10038c2ecf20Sopenharmony_ci if (fep->csum_flags & FLAG_RX_CSUM_ENABLED) 10048c2ecf20Sopenharmony_ci /* set RX checksum */ 10058c2ecf20Sopenharmony_ci val |= FEC_RACC_OPTIONS; 10068c2ecf20Sopenharmony_ci else 10078c2ecf20Sopenharmony_ci val &= ~FEC_RACC_OPTIONS; 10088c2ecf20Sopenharmony_ci writel(val, fep->hwp + FEC_RACC); 10098c2ecf20Sopenharmony_ci writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_FTRL); 10108c2ecf20Sopenharmony_ci } 10118c2ecf20Sopenharmony_ci#endif 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci /* 10148c2ecf20Sopenharmony_ci * The phy interface and speed need to get configured 10158c2ecf20Sopenharmony_ci * differently on enet-mac. 10168c2ecf20Sopenharmony_ci */ 10178c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_ENET_MAC) { 10188c2ecf20Sopenharmony_ci /* Enable flow control and length check */ 10198c2ecf20Sopenharmony_ci rcntl |= 0x40000000 | 0x00000020; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci /* RGMII, RMII or MII */ 10228c2ecf20Sopenharmony_ci if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII || 10238c2ecf20Sopenharmony_ci fep->phy_interface == PHY_INTERFACE_MODE_RGMII_ID || 10248c2ecf20Sopenharmony_ci fep->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID || 10258c2ecf20Sopenharmony_ci fep->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID) 10268c2ecf20Sopenharmony_ci rcntl |= (1 << 6); 10278c2ecf20Sopenharmony_ci else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) 10288c2ecf20Sopenharmony_ci rcntl |= (1 << 8); 10298c2ecf20Sopenharmony_ci else 10308c2ecf20Sopenharmony_ci rcntl &= ~(1 << 8); 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci /* 1G, 100M or 10M */ 10338c2ecf20Sopenharmony_ci if (ndev->phydev) { 10348c2ecf20Sopenharmony_ci if (ndev->phydev->speed == SPEED_1000) 10358c2ecf20Sopenharmony_ci ecntl |= (1 << 5); 10368c2ecf20Sopenharmony_ci else if (ndev->phydev->speed == SPEED_100) 10378c2ecf20Sopenharmony_ci rcntl &= ~(1 << 9); 10388c2ecf20Sopenharmony_ci else 10398c2ecf20Sopenharmony_ci rcntl |= (1 << 9); 10408c2ecf20Sopenharmony_ci } 10418c2ecf20Sopenharmony_ci } else { 10428c2ecf20Sopenharmony_ci#ifdef FEC_MIIGSK_ENR 10438c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_USE_GASKET) { 10448c2ecf20Sopenharmony_ci u32 cfgr; 10458c2ecf20Sopenharmony_ci /* disable the gasket and wait */ 10468c2ecf20Sopenharmony_ci writel(0, fep->hwp + FEC_MIIGSK_ENR); 10478c2ecf20Sopenharmony_ci while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4) 10488c2ecf20Sopenharmony_ci udelay(1); 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci /* 10518c2ecf20Sopenharmony_ci * configure the gasket: 10528c2ecf20Sopenharmony_ci * RMII, 50 MHz, no loopback, no echo 10538c2ecf20Sopenharmony_ci * MII, 25 MHz, no loopback, no echo 10548c2ecf20Sopenharmony_ci */ 10558c2ecf20Sopenharmony_ci cfgr = (fep->phy_interface == PHY_INTERFACE_MODE_RMII) 10568c2ecf20Sopenharmony_ci ? BM_MIIGSK_CFGR_RMII : BM_MIIGSK_CFGR_MII; 10578c2ecf20Sopenharmony_ci if (ndev->phydev && ndev->phydev->speed == SPEED_10) 10588c2ecf20Sopenharmony_ci cfgr |= BM_MIIGSK_CFGR_FRCONT_10M; 10598c2ecf20Sopenharmony_ci writel(cfgr, fep->hwp + FEC_MIIGSK_CFGR); 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci /* re-enable the gasket */ 10628c2ecf20Sopenharmony_ci writel(2, fep->hwp + FEC_MIIGSK_ENR); 10638c2ecf20Sopenharmony_ci } 10648c2ecf20Sopenharmony_ci#endif 10658c2ecf20Sopenharmony_ci } 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci#if !defined(CONFIG_M5272) 10688c2ecf20Sopenharmony_ci /* enable pause frame*/ 10698c2ecf20Sopenharmony_ci if ((fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) || 10708c2ecf20Sopenharmony_ci ((fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) && 10718c2ecf20Sopenharmony_ci ndev->phydev && ndev->phydev->pause)) { 10728c2ecf20Sopenharmony_ci rcntl |= FEC_ENET_FCE; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci /* set FIFO threshold parameter to reduce overrun */ 10758c2ecf20Sopenharmony_ci writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM); 10768c2ecf20Sopenharmony_ci writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL); 10778c2ecf20Sopenharmony_ci writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM); 10788c2ecf20Sopenharmony_ci writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL); 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci /* OPD */ 10818c2ecf20Sopenharmony_ci writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD); 10828c2ecf20Sopenharmony_ci } else { 10838c2ecf20Sopenharmony_ci rcntl &= ~FEC_ENET_FCE; 10848c2ecf20Sopenharmony_ci } 10858c2ecf20Sopenharmony_ci#endif /* !defined(CONFIG_M5272) */ 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci writel(rcntl, fep->hwp + FEC_R_CNTRL); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci /* Setup multicast filter. */ 10908c2ecf20Sopenharmony_ci set_multicast_list(ndev); 10918c2ecf20Sopenharmony_ci#ifndef CONFIG_M5272 10928c2ecf20Sopenharmony_ci writel(0, fep->hwp + FEC_HASH_TABLE_HIGH); 10938c2ecf20Sopenharmony_ci writel(0, fep->hwp + FEC_HASH_TABLE_LOW); 10948c2ecf20Sopenharmony_ci#endif 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_ENET_MAC) { 10978c2ecf20Sopenharmony_ci /* enable ENET endian swap */ 10988c2ecf20Sopenharmony_ci ecntl |= (1 << 8); 10998c2ecf20Sopenharmony_ci /* enable ENET store and forward mode */ 11008c2ecf20Sopenharmony_ci writel(1 << 8, fep->hwp + FEC_X_WMRK); 11018c2ecf20Sopenharmony_ci } 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci if (fep->bufdesc_ex) 11048c2ecf20Sopenharmony_ci ecntl |= (1 << 4); 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci#ifndef CONFIG_M5272 11078c2ecf20Sopenharmony_ci /* Enable the MIB statistic event counters */ 11088c2ecf20Sopenharmony_ci writel(0 << 31, fep->hwp + FEC_MIB_CTRLSTAT); 11098c2ecf20Sopenharmony_ci#endif 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci /* And last, enable the transmit and receive processing */ 11128c2ecf20Sopenharmony_ci writel(ecntl, fep->hwp + FEC_ECNTRL); 11138c2ecf20Sopenharmony_ci fec_enet_active_rxring(ndev); 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci if (fep->bufdesc_ex) 11168c2ecf20Sopenharmony_ci fec_ptp_start_cyclecounter(ndev); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci /* Enable interrupts we wish to service */ 11198c2ecf20Sopenharmony_ci if (fep->link) 11208c2ecf20Sopenharmony_ci writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 11218c2ecf20Sopenharmony_ci else 11228c2ecf20Sopenharmony_ci writel(0, fep->hwp + FEC_IMASK); 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci /* Init the interrupt coalescing */ 11258c2ecf20Sopenharmony_ci fec_enet_itr_coal_init(ndev); 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci} 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_cistatic void fec_enet_stop_mode(struct fec_enet_private *fep, bool enabled) 11308c2ecf20Sopenharmony_ci{ 11318c2ecf20Sopenharmony_ci struct fec_platform_data *pdata = fep->pdev->dev.platform_data; 11328c2ecf20Sopenharmony_ci struct fec_stop_mode_gpr *stop_gpr = &fep->stop_gpr; 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci if (stop_gpr->gpr) { 11358c2ecf20Sopenharmony_ci if (enabled) 11368c2ecf20Sopenharmony_ci regmap_update_bits(stop_gpr->gpr, stop_gpr->reg, 11378c2ecf20Sopenharmony_ci BIT(stop_gpr->bit), 11388c2ecf20Sopenharmony_ci BIT(stop_gpr->bit)); 11398c2ecf20Sopenharmony_ci else 11408c2ecf20Sopenharmony_ci regmap_update_bits(stop_gpr->gpr, stop_gpr->reg, 11418c2ecf20Sopenharmony_ci BIT(stop_gpr->bit), 0); 11428c2ecf20Sopenharmony_ci } else if (pdata && pdata->sleep_mode_enable) { 11438c2ecf20Sopenharmony_ci pdata->sleep_mode_enable(enabled); 11448c2ecf20Sopenharmony_ci } 11458c2ecf20Sopenharmony_ci} 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_cistatic void 11488c2ecf20Sopenharmony_cifec_stop(struct net_device *ndev) 11498c2ecf20Sopenharmony_ci{ 11508c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 11518c2ecf20Sopenharmony_ci u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8); 11528c2ecf20Sopenharmony_ci u32 val; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci /* We cannot expect a graceful transmit stop without link !!! */ 11558c2ecf20Sopenharmony_ci if (fep->link) { 11568c2ecf20Sopenharmony_ci writel(1, fep->hwp + FEC_X_CNTRL); /* Graceful transmit stop */ 11578c2ecf20Sopenharmony_ci udelay(10); 11588c2ecf20Sopenharmony_ci if (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_GRA)) 11598c2ecf20Sopenharmony_ci netdev_err(ndev, "Graceful transmit stop did not complete!\n"); 11608c2ecf20Sopenharmony_ci } 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci /* Whack a reset. We should wait for this. 11638c2ecf20Sopenharmony_ci * For i.MX6SX SOC, enet use AXI bus, we use disable MAC 11648c2ecf20Sopenharmony_ci * instead of reset MAC itself. 11658c2ecf20Sopenharmony_ci */ 11668c2ecf20Sopenharmony_ci if (!(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { 11678c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_AVB) { 11688c2ecf20Sopenharmony_ci writel(0, fep->hwp + FEC_ECNTRL); 11698c2ecf20Sopenharmony_ci } else { 11708c2ecf20Sopenharmony_ci writel(1, fep->hwp + FEC_ECNTRL); 11718c2ecf20Sopenharmony_ci udelay(10); 11728c2ecf20Sopenharmony_ci } 11738c2ecf20Sopenharmony_ci writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 11748c2ecf20Sopenharmony_ci } else { 11758c2ecf20Sopenharmony_ci writel(FEC_DEFAULT_IMASK | FEC_ENET_WAKEUP, fep->hwp + FEC_IMASK); 11768c2ecf20Sopenharmony_ci val = readl(fep->hwp + FEC_ECNTRL); 11778c2ecf20Sopenharmony_ci val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP); 11788c2ecf20Sopenharmony_ci writel(val, fep->hwp + FEC_ECNTRL); 11798c2ecf20Sopenharmony_ci fec_enet_stop_mode(fep, true); 11808c2ecf20Sopenharmony_ci } 11818c2ecf20Sopenharmony_ci writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci /* We have to keep ENET enabled to have MII interrupt stay working */ 11848c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_ENET_MAC && 11858c2ecf20Sopenharmony_ci !(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { 11868c2ecf20Sopenharmony_ci writel(2, fep->hwp + FEC_ECNTRL); 11878c2ecf20Sopenharmony_ci writel(rmii_mode, fep->hwp + FEC_R_CNTRL); 11888c2ecf20Sopenharmony_ci } 11898c2ecf20Sopenharmony_ci} 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_cistatic void 11938c2ecf20Sopenharmony_cifec_timeout(struct net_device *ndev, unsigned int txqueue) 11948c2ecf20Sopenharmony_ci{ 11958c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci fec_dump(ndev); 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci ndev->stats.tx_errors++; 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci schedule_work(&fep->tx_timeout_work); 12028c2ecf20Sopenharmony_ci} 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_cistatic void fec_enet_timeout_work(struct work_struct *work) 12058c2ecf20Sopenharmony_ci{ 12068c2ecf20Sopenharmony_ci struct fec_enet_private *fep = 12078c2ecf20Sopenharmony_ci container_of(work, struct fec_enet_private, tx_timeout_work); 12088c2ecf20Sopenharmony_ci struct net_device *ndev = fep->netdev; 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci rtnl_lock(); 12118c2ecf20Sopenharmony_ci if (netif_device_present(ndev) || netif_running(ndev)) { 12128c2ecf20Sopenharmony_ci napi_disable(&fep->napi); 12138c2ecf20Sopenharmony_ci netif_tx_lock_bh(ndev); 12148c2ecf20Sopenharmony_ci fec_restart(ndev); 12158c2ecf20Sopenharmony_ci netif_tx_wake_all_queues(ndev); 12168c2ecf20Sopenharmony_ci netif_tx_unlock_bh(ndev); 12178c2ecf20Sopenharmony_ci napi_enable(&fep->napi); 12188c2ecf20Sopenharmony_ci } 12198c2ecf20Sopenharmony_ci rtnl_unlock(); 12208c2ecf20Sopenharmony_ci} 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_cistatic void 12238c2ecf20Sopenharmony_cifec_enet_hwtstamp(struct fec_enet_private *fep, unsigned ts, 12248c2ecf20Sopenharmony_ci struct skb_shared_hwtstamps *hwtstamps) 12258c2ecf20Sopenharmony_ci{ 12268c2ecf20Sopenharmony_ci unsigned long flags; 12278c2ecf20Sopenharmony_ci u64 ns; 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci spin_lock_irqsave(&fep->tmreg_lock, flags); 12308c2ecf20Sopenharmony_ci ns = timecounter_cyc2time(&fep->tc, ts); 12318c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fep->tmreg_lock, flags); 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci memset(hwtstamps, 0, sizeof(*hwtstamps)); 12348c2ecf20Sopenharmony_ci hwtstamps->hwtstamp = ns_to_ktime(ns); 12358c2ecf20Sopenharmony_ci} 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_cistatic void 12388c2ecf20Sopenharmony_cifec_enet_tx_queue(struct net_device *ndev, u16 queue_id) 12398c2ecf20Sopenharmony_ci{ 12408c2ecf20Sopenharmony_ci struct fec_enet_private *fep; 12418c2ecf20Sopenharmony_ci struct bufdesc *bdp; 12428c2ecf20Sopenharmony_ci unsigned short status; 12438c2ecf20Sopenharmony_ci struct sk_buff *skb; 12448c2ecf20Sopenharmony_ci struct fec_enet_priv_tx_q *txq; 12458c2ecf20Sopenharmony_ci struct netdev_queue *nq; 12468c2ecf20Sopenharmony_ci int index = 0; 12478c2ecf20Sopenharmony_ci int entries_free; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci fep = netdev_priv(ndev); 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci txq = fep->tx_queue[queue_id]; 12528c2ecf20Sopenharmony_ci /* get next bdp of dirty_tx */ 12538c2ecf20Sopenharmony_ci nq = netdev_get_tx_queue(ndev, queue_id); 12548c2ecf20Sopenharmony_ci bdp = txq->dirty_tx; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci /* get next bdp of dirty_tx */ 12578c2ecf20Sopenharmony_ci bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci while (bdp != READ_ONCE(txq->bd.cur)) { 12608c2ecf20Sopenharmony_ci /* Order the load of bd.cur and cbd_sc */ 12618c2ecf20Sopenharmony_ci rmb(); 12628c2ecf20Sopenharmony_ci status = fec16_to_cpu(READ_ONCE(bdp->cbd_sc)); 12638c2ecf20Sopenharmony_ci if (status & BD_ENET_TX_READY) 12648c2ecf20Sopenharmony_ci break; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci index = fec_enet_get_bd_index(bdp, &txq->bd); 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci skb = txq->tx_skbuff[index]; 12698c2ecf20Sopenharmony_ci txq->tx_skbuff[index] = NULL; 12708c2ecf20Sopenharmony_ci if (!IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr))) 12718c2ecf20Sopenharmony_ci dma_unmap_single(&fep->pdev->dev, 12728c2ecf20Sopenharmony_ci fec32_to_cpu(bdp->cbd_bufaddr), 12738c2ecf20Sopenharmony_ci fec16_to_cpu(bdp->cbd_datlen), 12748c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 12758c2ecf20Sopenharmony_ci bdp->cbd_bufaddr = cpu_to_fec32(0); 12768c2ecf20Sopenharmony_ci if (!skb) 12778c2ecf20Sopenharmony_ci goto skb_done; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci /* Check for errors. */ 12808c2ecf20Sopenharmony_ci if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC | 12818c2ecf20Sopenharmony_ci BD_ENET_TX_RL | BD_ENET_TX_UN | 12828c2ecf20Sopenharmony_ci BD_ENET_TX_CSL)) { 12838c2ecf20Sopenharmony_ci ndev->stats.tx_errors++; 12848c2ecf20Sopenharmony_ci if (status & BD_ENET_TX_HB) /* No heartbeat */ 12858c2ecf20Sopenharmony_ci ndev->stats.tx_heartbeat_errors++; 12868c2ecf20Sopenharmony_ci if (status & BD_ENET_TX_LC) /* Late collision */ 12878c2ecf20Sopenharmony_ci ndev->stats.tx_window_errors++; 12888c2ecf20Sopenharmony_ci if (status & BD_ENET_TX_RL) /* Retrans limit */ 12898c2ecf20Sopenharmony_ci ndev->stats.tx_aborted_errors++; 12908c2ecf20Sopenharmony_ci if (status & BD_ENET_TX_UN) /* Underrun */ 12918c2ecf20Sopenharmony_ci ndev->stats.tx_fifo_errors++; 12928c2ecf20Sopenharmony_ci if (status & BD_ENET_TX_CSL) /* Carrier lost */ 12938c2ecf20Sopenharmony_ci ndev->stats.tx_carrier_errors++; 12948c2ecf20Sopenharmony_ci } else { 12958c2ecf20Sopenharmony_ci ndev->stats.tx_packets++; 12968c2ecf20Sopenharmony_ci ndev->stats.tx_bytes += skb->len; 12978c2ecf20Sopenharmony_ci } 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci /* NOTE: SKBTX_IN_PROGRESS being set does not imply it's we who 13008c2ecf20Sopenharmony_ci * are to time stamp the packet, so we still need to check time 13018c2ecf20Sopenharmony_ci * stamping enabled flag. 13028c2ecf20Sopenharmony_ci */ 13038c2ecf20Sopenharmony_ci if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS && 13048c2ecf20Sopenharmony_ci fep->hwts_tx_en) && 13058c2ecf20Sopenharmony_ci fep->bufdesc_ex) { 13068c2ecf20Sopenharmony_ci struct skb_shared_hwtstamps shhwtstamps; 13078c2ecf20Sopenharmony_ci struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci fec_enet_hwtstamp(fep, fec32_to_cpu(ebdp->ts), &shhwtstamps); 13108c2ecf20Sopenharmony_ci skb_tstamp_tx(skb, &shhwtstamps); 13118c2ecf20Sopenharmony_ci } 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci /* Deferred means some collisions occurred during transmit, 13148c2ecf20Sopenharmony_ci * but we eventually sent the packet OK. 13158c2ecf20Sopenharmony_ci */ 13168c2ecf20Sopenharmony_ci if (status & BD_ENET_TX_DEF) 13178c2ecf20Sopenharmony_ci ndev->stats.collisions++; 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci /* Free the sk buffer associated with this last transmit */ 13208c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 13218c2ecf20Sopenharmony_ciskb_done: 13228c2ecf20Sopenharmony_ci /* Make sure the update to bdp and tx_skbuff are performed 13238c2ecf20Sopenharmony_ci * before dirty_tx 13248c2ecf20Sopenharmony_ci */ 13258c2ecf20Sopenharmony_ci wmb(); 13268c2ecf20Sopenharmony_ci txq->dirty_tx = bdp; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci /* Update pointer to next buffer descriptor to be transmitted */ 13298c2ecf20Sopenharmony_ci bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci /* Since we have freed up a buffer, the ring is no longer full 13328c2ecf20Sopenharmony_ci */ 13338c2ecf20Sopenharmony_ci if (netif_tx_queue_stopped(nq)) { 13348c2ecf20Sopenharmony_ci entries_free = fec_enet_get_free_txdesc_num(txq); 13358c2ecf20Sopenharmony_ci if (entries_free >= txq->tx_wake_threshold) 13368c2ecf20Sopenharmony_ci netif_tx_wake_queue(nq); 13378c2ecf20Sopenharmony_ci } 13388c2ecf20Sopenharmony_ci } 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci /* ERR006358: Keep the transmitter going */ 13418c2ecf20Sopenharmony_ci if (bdp != txq->bd.cur && 13428c2ecf20Sopenharmony_ci readl(txq->bd.reg_desc_active) == 0) 13438c2ecf20Sopenharmony_ci writel(0, txq->bd.reg_desc_active); 13448c2ecf20Sopenharmony_ci} 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_cistatic void fec_enet_tx(struct net_device *ndev) 13478c2ecf20Sopenharmony_ci{ 13488c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 13498c2ecf20Sopenharmony_ci int i; 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci /* Make sure that AVB queues are processed first. */ 13528c2ecf20Sopenharmony_ci for (i = fep->num_tx_queues - 1; i >= 0; i--) 13538c2ecf20Sopenharmony_ci fec_enet_tx_queue(ndev, i); 13548c2ecf20Sopenharmony_ci} 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_cistatic int 13578c2ecf20Sopenharmony_cifec_enet_new_rxbdp(struct net_device *ndev, struct bufdesc *bdp, struct sk_buff *skb) 13588c2ecf20Sopenharmony_ci{ 13598c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 13608c2ecf20Sopenharmony_ci int off; 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci off = ((unsigned long)skb->data) & fep->rx_align; 13638c2ecf20Sopenharmony_ci if (off) 13648c2ecf20Sopenharmony_ci skb_reserve(skb, fep->rx_align + 1 - off); 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci bdp->cbd_bufaddr = cpu_to_fec32(dma_map_single(&fep->pdev->dev, skb->data, FEC_ENET_RX_FRSIZE - fep->rx_align, DMA_FROM_DEVICE)); 13678c2ecf20Sopenharmony_ci if (dma_mapping_error(&fep->pdev->dev, fec32_to_cpu(bdp->cbd_bufaddr))) { 13688c2ecf20Sopenharmony_ci if (net_ratelimit()) 13698c2ecf20Sopenharmony_ci netdev_err(ndev, "Rx DMA memory map failed\n"); 13708c2ecf20Sopenharmony_ci return -ENOMEM; 13718c2ecf20Sopenharmony_ci } 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci return 0; 13748c2ecf20Sopenharmony_ci} 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_cistatic bool fec_enet_copybreak(struct net_device *ndev, struct sk_buff **skb, 13778c2ecf20Sopenharmony_ci struct bufdesc *bdp, u32 length, bool swap) 13788c2ecf20Sopenharmony_ci{ 13798c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 13808c2ecf20Sopenharmony_ci struct sk_buff *new_skb; 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci if (length > fep->rx_copybreak) 13838c2ecf20Sopenharmony_ci return false; 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci new_skb = netdev_alloc_skb(ndev, length); 13868c2ecf20Sopenharmony_ci if (!new_skb) 13878c2ecf20Sopenharmony_ci return false; 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(&fep->pdev->dev, 13908c2ecf20Sopenharmony_ci fec32_to_cpu(bdp->cbd_bufaddr), 13918c2ecf20Sopenharmony_ci FEC_ENET_RX_FRSIZE - fep->rx_align, 13928c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 13938c2ecf20Sopenharmony_ci if (!swap) 13948c2ecf20Sopenharmony_ci memcpy(new_skb->data, (*skb)->data, length); 13958c2ecf20Sopenharmony_ci else 13968c2ecf20Sopenharmony_ci swap_buffer2(new_skb->data, (*skb)->data, length); 13978c2ecf20Sopenharmony_ci *skb = new_skb; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci return true; 14008c2ecf20Sopenharmony_ci} 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci/* During a receive, the bd_rx.cur points to the current incoming buffer. 14038c2ecf20Sopenharmony_ci * When we update through the ring, if the next incoming buffer has 14048c2ecf20Sopenharmony_ci * not been given to the system, we just set the empty indicator, 14058c2ecf20Sopenharmony_ci * effectively tossing the packet. 14068c2ecf20Sopenharmony_ci */ 14078c2ecf20Sopenharmony_cistatic int 14088c2ecf20Sopenharmony_cifec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) 14098c2ecf20Sopenharmony_ci{ 14108c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 14118c2ecf20Sopenharmony_ci struct fec_enet_priv_rx_q *rxq; 14128c2ecf20Sopenharmony_ci struct bufdesc *bdp; 14138c2ecf20Sopenharmony_ci unsigned short status; 14148c2ecf20Sopenharmony_ci struct sk_buff *skb_new = NULL; 14158c2ecf20Sopenharmony_ci struct sk_buff *skb; 14168c2ecf20Sopenharmony_ci ushort pkt_len; 14178c2ecf20Sopenharmony_ci __u8 *data; 14188c2ecf20Sopenharmony_ci int pkt_received = 0; 14198c2ecf20Sopenharmony_ci struct bufdesc_ex *ebdp = NULL; 14208c2ecf20Sopenharmony_ci bool vlan_packet_rcvd = false; 14218c2ecf20Sopenharmony_ci u16 vlan_tag; 14228c2ecf20Sopenharmony_ci int index = 0; 14238c2ecf20Sopenharmony_ci bool is_copybreak; 14248c2ecf20Sopenharmony_ci bool need_swap = fep->quirks & FEC_QUIRK_SWAP_FRAME; 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci#ifdef CONFIG_M532x 14278c2ecf20Sopenharmony_ci flush_cache_all(); 14288c2ecf20Sopenharmony_ci#endif 14298c2ecf20Sopenharmony_ci rxq = fep->rx_queue[queue_id]; 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci /* First, grab all of the stats for the incoming packet. 14328c2ecf20Sopenharmony_ci * These get messed up if we get called due to a busy condition. 14338c2ecf20Sopenharmony_ci */ 14348c2ecf20Sopenharmony_ci bdp = rxq->bd.cur; 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci while (!((status = fec16_to_cpu(bdp->cbd_sc)) & BD_ENET_RX_EMPTY)) { 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci if (pkt_received >= budget) 14398c2ecf20Sopenharmony_ci break; 14408c2ecf20Sopenharmony_ci pkt_received++; 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci writel(FEC_ENET_RXF_GET(queue_id), fep->hwp + FEC_IEVENT); 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci /* Check for errors. */ 14458c2ecf20Sopenharmony_ci status ^= BD_ENET_RX_LAST; 14468c2ecf20Sopenharmony_ci if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | 14478c2ecf20Sopenharmony_ci BD_ENET_RX_CR | BD_ENET_RX_OV | BD_ENET_RX_LAST | 14488c2ecf20Sopenharmony_ci BD_ENET_RX_CL)) { 14498c2ecf20Sopenharmony_ci ndev->stats.rx_errors++; 14508c2ecf20Sopenharmony_ci if (status & BD_ENET_RX_OV) { 14518c2ecf20Sopenharmony_ci /* FIFO overrun */ 14528c2ecf20Sopenharmony_ci ndev->stats.rx_fifo_errors++; 14538c2ecf20Sopenharmony_ci goto rx_processing_done; 14548c2ecf20Sopenharmony_ci } 14558c2ecf20Sopenharmony_ci if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH 14568c2ecf20Sopenharmony_ci | BD_ENET_RX_LAST)) { 14578c2ecf20Sopenharmony_ci /* Frame too long or too short. */ 14588c2ecf20Sopenharmony_ci ndev->stats.rx_length_errors++; 14598c2ecf20Sopenharmony_ci if (status & BD_ENET_RX_LAST) 14608c2ecf20Sopenharmony_ci netdev_err(ndev, "rcv is not +last\n"); 14618c2ecf20Sopenharmony_ci } 14628c2ecf20Sopenharmony_ci if (status & BD_ENET_RX_CR) /* CRC Error */ 14638c2ecf20Sopenharmony_ci ndev->stats.rx_crc_errors++; 14648c2ecf20Sopenharmony_ci /* Report late collisions as a frame error. */ 14658c2ecf20Sopenharmony_ci if (status & (BD_ENET_RX_NO | BD_ENET_RX_CL)) 14668c2ecf20Sopenharmony_ci ndev->stats.rx_frame_errors++; 14678c2ecf20Sopenharmony_ci goto rx_processing_done; 14688c2ecf20Sopenharmony_ci } 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci /* Process the incoming frame. */ 14718c2ecf20Sopenharmony_ci ndev->stats.rx_packets++; 14728c2ecf20Sopenharmony_ci pkt_len = fec16_to_cpu(bdp->cbd_datlen); 14738c2ecf20Sopenharmony_ci ndev->stats.rx_bytes += pkt_len; 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci index = fec_enet_get_bd_index(bdp, &rxq->bd); 14768c2ecf20Sopenharmony_ci skb = rxq->rx_skbuff[index]; 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci /* The packet length includes FCS, but we don't want to 14798c2ecf20Sopenharmony_ci * include that when passing upstream as it messes up 14808c2ecf20Sopenharmony_ci * bridging applications. 14818c2ecf20Sopenharmony_ci */ 14828c2ecf20Sopenharmony_ci is_copybreak = fec_enet_copybreak(ndev, &skb, bdp, pkt_len - 4, 14838c2ecf20Sopenharmony_ci need_swap); 14848c2ecf20Sopenharmony_ci if (!is_copybreak) { 14858c2ecf20Sopenharmony_ci skb_new = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE); 14868c2ecf20Sopenharmony_ci if (unlikely(!skb_new)) { 14878c2ecf20Sopenharmony_ci ndev->stats.rx_dropped++; 14888c2ecf20Sopenharmony_ci goto rx_processing_done; 14898c2ecf20Sopenharmony_ci } 14908c2ecf20Sopenharmony_ci dma_unmap_single(&fep->pdev->dev, 14918c2ecf20Sopenharmony_ci fec32_to_cpu(bdp->cbd_bufaddr), 14928c2ecf20Sopenharmony_ci FEC_ENET_RX_FRSIZE - fep->rx_align, 14938c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 14948c2ecf20Sopenharmony_ci } 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci prefetch(skb->data - NET_IP_ALIGN); 14978c2ecf20Sopenharmony_ci skb_put(skb, pkt_len - 4); 14988c2ecf20Sopenharmony_ci data = skb->data; 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci if (!is_copybreak && need_swap) 15018c2ecf20Sopenharmony_ci swap_buffer(data, pkt_len); 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci#if !defined(CONFIG_M5272) 15048c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_RACC) 15058c2ecf20Sopenharmony_ci data = skb_pull_inline(skb, 2); 15068c2ecf20Sopenharmony_ci#endif 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci /* Extract the enhanced buffer descriptor */ 15098c2ecf20Sopenharmony_ci ebdp = NULL; 15108c2ecf20Sopenharmony_ci if (fep->bufdesc_ex) 15118c2ecf20Sopenharmony_ci ebdp = (struct bufdesc_ex *)bdp; 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci /* If this is a VLAN packet remove the VLAN Tag */ 15148c2ecf20Sopenharmony_ci vlan_packet_rcvd = false; 15158c2ecf20Sopenharmony_ci if ((ndev->features & NETIF_F_HW_VLAN_CTAG_RX) && 15168c2ecf20Sopenharmony_ci fep->bufdesc_ex && 15178c2ecf20Sopenharmony_ci (ebdp->cbd_esc & cpu_to_fec32(BD_ENET_RX_VLAN))) { 15188c2ecf20Sopenharmony_ci /* Push and remove the vlan tag */ 15198c2ecf20Sopenharmony_ci struct vlan_hdr *vlan_header = 15208c2ecf20Sopenharmony_ci (struct vlan_hdr *) (data + ETH_HLEN); 15218c2ecf20Sopenharmony_ci vlan_tag = ntohs(vlan_header->h_vlan_TCI); 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci vlan_packet_rcvd = true; 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci memmove(skb->data + VLAN_HLEN, data, ETH_ALEN * 2); 15268c2ecf20Sopenharmony_ci skb_pull(skb, VLAN_HLEN); 15278c2ecf20Sopenharmony_ci } 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, ndev); 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci /* Get receive timestamp from the skb */ 15328c2ecf20Sopenharmony_ci if (fep->hwts_rx_en && fep->bufdesc_ex) 15338c2ecf20Sopenharmony_ci fec_enet_hwtstamp(fep, fec32_to_cpu(ebdp->ts), 15348c2ecf20Sopenharmony_ci skb_hwtstamps(skb)); 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci if (fep->bufdesc_ex && 15378c2ecf20Sopenharmony_ci (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { 15388c2ecf20Sopenharmony_ci if (!(ebdp->cbd_esc & cpu_to_fec32(FLAG_RX_CSUM_ERROR))) { 15398c2ecf20Sopenharmony_ci /* don't check it */ 15408c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 15418c2ecf20Sopenharmony_ci } else { 15428c2ecf20Sopenharmony_ci skb_checksum_none_assert(skb); 15438c2ecf20Sopenharmony_ci } 15448c2ecf20Sopenharmony_ci } 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci /* Handle received VLAN packets */ 15478c2ecf20Sopenharmony_ci if (vlan_packet_rcvd) 15488c2ecf20Sopenharmony_ci __vlan_hwaccel_put_tag(skb, 15498c2ecf20Sopenharmony_ci htons(ETH_P_8021Q), 15508c2ecf20Sopenharmony_ci vlan_tag); 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci skb_record_rx_queue(skb, queue_id); 15538c2ecf20Sopenharmony_ci napi_gro_receive(&fep->napi, skb); 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci if (is_copybreak) { 15568c2ecf20Sopenharmony_ci dma_sync_single_for_device(&fep->pdev->dev, 15578c2ecf20Sopenharmony_ci fec32_to_cpu(bdp->cbd_bufaddr), 15588c2ecf20Sopenharmony_ci FEC_ENET_RX_FRSIZE - fep->rx_align, 15598c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 15608c2ecf20Sopenharmony_ci } else { 15618c2ecf20Sopenharmony_ci rxq->rx_skbuff[index] = skb_new; 15628c2ecf20Sopenharmony_ci fec_enet_new_rxbdp(ndev, bdp, skb_new); 15638c2ecf20Sopenharmony_ci } 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_cirx_processing_done: 15668c2ecf20Sopenharmony_ci /* Clear the status flags for this buffer */ 15678c2ecf20Sopenharmony_ci status &= ~BD_ENET_RX_STATS; 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci /* Mark the buffer empty */ 15708c2ecf20Sopenharmony_ci status |= BD_ENET_RX_EMPTY; 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci if (fep->bufdesc_ex) { 15738c2ecf20Sopenharmony_ci struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci ebdp->cbd_esc = cpu_to_fec32(BD_ENET_RX_INT); 15768c2ecf20Sopenharmony_ci ebdp->cbd_prot = 0; 15778c2ecf20Sopenharmony_ci ebdp->cbd_bdu = 0; 15788c2ecf20Sopenharmony_ci } 15798c2ecf20Sopenharmony_ci /* Make sure the updates to rest of the descriptor are 15808c2ecf20Sopenharmony_ci * performed before transferring ownership. 15818c2ecf20Sopenharmony_ci */ 15828c2ecf20Sopenharmony_ci wmb(); 15838c2ecf20Sopenharmony_ci bdp->cbd_sc = cpu_to_fec16(status); 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci /* Update BD pointer to next entry */ 15868c2ecf20Sopenharmony_ci bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci /* Doing this here will keep the FEC running while we process 15898c2ecf20Sopenharmony_ci * incoming frames. On a heavily loaded network, we should be 15908c2ecf20Sopenharmony_ci * able to keep up at the expense of system resources. 15918c2ecf20Sopenharmony_ci */ 15928c2ecf20Sopenharmony_ci writel(0, rxq->bd.reg_desc_active); 15938c2ecf20Sopenharmony_ci } 15948c2ecf20Sopenharmony_ci rxq->bd.cur = bdp; 15958c2ecf20Sopenharmony_ci return pkt_received; 15968c2ecf20Sopenharmony_ci} 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_cistatic int fec_enet_rx(struct net_device *ndev, int budget) 15998c2ecf20Sopenharmony_ci{ 16008c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 16018c2ecf20Sopenharmony_ci int i, done = 0; 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci /* Make sure that AVB queues are processed first. */ 16048c2ecf20Sopenharmony_ci for (i = fep->num_rx_queues - 1; i >= 0; i--) 16058c2ecf20Sopenharmony_ci done += fec_enet_rx_queue(ndev, budget - done, i); 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci return done; 16088c2ecf20Sopenharmony_ci} 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_cistatic bool fec_enet_collect_events(struct fec_enet_private *fep) 16118c2ecf20Sopenharmony_ci{ 16128c2ecf20Sopenharmony_ci uint int_events; 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci int_events = readl(fep->hwp + FEC_IEVENT); 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci /* Don't clear MDIO events, we poll for those */ 16178c2ecf20Sopenharmony_ci int_events &= ~FEC_ENET_MII; 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci writel(int_events, fep->hwp + FEC_IEVENT); 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci return int_events != 0; 16228c2ecf20Sopenharmony_ci} 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_cistatic irqreturn_t 16258c2ecf20Sopenharmony_cifec_enet_interrupt(int irq, void *dev_id) 16268c2ecf20Sopenharmony_ci{ 16278c2ecf20Sopenharmony_ci struct net_device *ndev = dev_id; 16288c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 16298c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_ci if (fec_enet_collect_events(fep) && fep->link) { 16328c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci if (napi_schedule_prep(&fep->napi)) { 16358c2ecf20Sopenharmony_ci /* Disable interrupts */ 16368c2ecf20Sopenharmony_ci writel(0, fep->hwp + FEC_IMASK); 16378c2ecf20Sopenharmony_ci __napi_schedule(&fep->napi); 16388c2ecf20Sopenharmony_ci } 16398c2ecf20Sopenharmony_ci } 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci return ret; 16428c2ecf20Sopenharmony_ci} 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_cistatic int fec_enet_rx_napi(struct napi_struct *napi, int budget) 16458c2ecf20Sopenharmony_ci{ 16468c2ecf20Sopenharmony_ci struct net_device *ndev = napi->dev; 16478c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 16488c2ecf20Sopenharmony_ci int done = 0; 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci do { 16518c2ecf20Sopenharmony_ci done += fec_enet_rx(ndev, budget - done); 16528c2ecf20Sopenharmony_ci fec_enet_tx(ndev); 16538c2ecf20Sopenharmony_ci } while ((done < budget) && fec_enet_collect_events(fep)); 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_ci if (done < budget) { 16568c2ecf20Sopenharmony_ci napi_complete_done(napi, done); 16578c2ecf20Sopenharmony_ci writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 16588c2ecf20Sopenharmony_ci } 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci return done; 16618c2ecf20Sopenharmony_ci} 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 16648c2ecf20Sopenharmony_cistatic void fec_get_mac(struct net_device *ndev) 16658c2ecf20Sopenharmony_ci{ 16668c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 16678c2ecf20Sopenharmony_ci struct fec_platform_data *pdata = dev_get_platdata(&fep->pdev->dev); 16688c2ecf20Sopenharmony_ci unsigned char *iap, tmpaddr[ETH_ALEN]; 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci /* 16718c2ecf20Sopenharmony_ci * try to get mac address in following order: 16728c2ecf20Sopenharmony_ci * 16738c2ecf20Sopenharmony_ci * 1) module parameter via kernel command line in form 16748c2ecf20Sopenharmony_ci * fec.macaddr=0x00,0x04,0x9f,0x01,0x30,0xe0 16758c2ecf20Sopenharmony_ci */ 16768c2ecf20Sopenharmony_ci iap = macaddr; 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci /* 16798c2ecf20Sopenharmony_ci * 2) from device tree data 16808c2ecf20Sopenharmony_ci */ 16818c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(iap)) { 16828c2ecf20Sopenharmony_ci struct device_node *np = fep->pdev->dev.of_node; 16838c2ecf20Sopenharmony_ci if (np) { 16848c2ecf20Sopenharmony_ci const char *mac = of_get_mac_address(np); 16858c2ecf20Sopenharmony_ci if (!IS_ERR(mac)) 16868c2ecf20Sopenharmony_ci iap = (unsigned char *) mac; 16878c2ecf20Sopenharmony_ci } 16888c2ecf20Sopenharmony_ci } 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci /* 16918c2ecf20Sopenharmony_ci * 3) from flash or fuse (via platform data) 16928c2ecf20Sopenharmony_ci */ 16938c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(iap)) { 16948c2ecf20Sopenharmony_ci#ifdef CONFIG_M5272 16958c2ecf20Sopenharmony_ci if (FEC_FLASHMAC) 16968c2ecf20Sopenharmony_ci iap = (unsigned char *)FEC_FLASHMAC; 16978c2ecf20Sopenharmony_ci#else 16988c2ecf20Sopenharmony_ci if (pdata) 16998c2ecf20Sopenharmony_ci iap = (unsigned char *)&pdata->mac; 17008c2ecf20Sopenharmony_ci#endif 17018c2ecf20Sopenharmony_ci } 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci /* 17048c2ecf20Sopenharmony_ci * 4) FEC mac registers set by bootloader 17058c2ecf20Sopenharmony_ci */ 17068c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(iap)) { 17078c2ecf20Sopenharmony_ci *((__be32 *) &tmpaddr[0]) = 17088c2ecf20Sopenharmony_ci cpu_to_be32(readl(fep->hwp + FEC_ADDR_LOW)); 17098c2ecf20Sopenharmony_ci *((__be16 *) &tmpaddr[4]) = 17108c2ecf20Sopenharmony_ci cpu_to_be16(readl(fep->hwp + FEC_ADDR_HIGH) >> 16); 17118c2ecf20Sopenharmony_ci iap = &tmpaddr[0]; 17128c2ecf20Sopenharmony_ci } 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci /* 17158c2ecf20Sopenharmony_ci * 5) random mac address 17168c2ecf20Sopenharmony_ci */ 17178c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(iap)) { 17188c2ecf20Sopenharmony_ci /* Report it and use a random ethernet address instead */ 17198c2ecf20Sopenharmony_ci dev_err(&fep->pdev->dev, "Invalid MAC address: %pM\n", iap); 17208c2ecf20Sopenharmony_ci eth_hw_addr_random(ndev); 17218c2ecf20Sopenharmony_ci dev_info(&fep->pdev->dev, "Using random MAC address: %pM\n", 17228c2ecf20Sopenharmony_ci ndev->dev_addr); 17238c2ecf20Sopenharmony_ci return; 17248c2ecf20Sopenharmony_ci } 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci memcpy(ndev->dev_addr, iap, ETH_ALEN); 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci /* Adjust MAC if using macaddr */ 17298c2ecf20Sopenharmony_ci if (iap == macaddr) 17308c2ecf20Sopenharmony_ci ndev->dev_addr[ETH_ALEN-1] = macaddr[ETH_ALEN-1] + fep->dev_id; 17318c2ecf20Sopenharmony_ci} 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_ci/* 17368c2ecf20Sopenharmony_ci * Phy section 17378c2ecf20Sopenharmony_ci */ 17388c2ecf20Sopenharmony_cistatic void fec_enet_adjust_link(struct net_device *ndev) 17398c2ecf20Sopenharmony_ci{ 17408c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 17418c2ecf20Sopenharmony_ci struct phy_device *phy_dev = ndev->phydev; 17428c2ecf20Sopenharmony_ci int status_change = 0; 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci /* 17458c2ecf20Sopenharmony_ci * If the netdev is down, or is going down, we're not interested 17468c2ecf20Sopenharmony_ci * in link state events, so just mark our idea of the link as down 17478c2ecf20Sopenharmony_ci * and ignore the event. 17488c2ecf20Sopenharmony_ci */ 17498c2ecf20Sopenharmony_ci if (!netif_running(ndev) || !netif_device_present(ndev)) { 17508c2ecf20Sopenharmony_ci fep->link = 0; 17518c2ecf20Sopenharmony_ci } else if (phy_dev->link) { 17528c2ecf20Sopenharmony_ci if (!fep->link) { 17538c2ecf20Sopenharmony_ci fep->link = phy_dev->link; 17548c2ecf20Sopenharmony_ci status_change = 1; 17558c2ecf20Sopenharmony_ci } 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci if (fep->full_duplex != phy_dev->duplex) { 17588c2ecf20Sopenharmony_ci fep->full_duplex = phy_dev->duplex; 17598c2ecf20Sopenharmony_ci status_change = 1; 17608c2ecf20Sopenharmony_ci } 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci if (phy_dev->speed != fep->speed) { 17638c2ecf20Sopenharmony_ci fep->speed = phy_dev->speed; 17648c2ecf20Sopenharmony_ci status_change = 1; 17658c2ecf20Sopenharmony_ci } 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci /* if any of the above changed restart the FEC */ 17688c2ecf20Sopenharmony_ci if (status_change) { 17698c2ecf20Sopenharmony_ci netif_stop_queue(ndev); 17708c2ecf20Sopenharmony_ci napi_disable(&fep->napi); 17718c2ecf20Sopenharmony_ci netif_tx_lock_bh(ndev); 17728c2ecf20Sopenharmony_ci fec_restart(ndev); 17738c2ecf20Sopenharmony_ci netif_tx_wake_all_queues(ndev); 17748c2ecf20Sopenharmony_ci netif_tx_unlock_bh(ndev); 17758c2ecf20Sopenharmony_ci napi_enable(&fep->napi); 17768c2ecf20Sopenharmony_ci } 17778c2ecf20Sopenharmony_ci } else { 17788c2ecf20Sopenharmony_ci if (fep->link) { 17798c2ecf20Sopenharmony_ci netif_stop_queue(ndev); 17808c2ecf20Sopenharmony_ci napi_disable(&fep->napi); 17818c2ecf20Sopenharmony_ci netif_tx_lock_bh(ndev); 17828c2ecf20Sopenharmony_ci fec_stop(ndev); 17838c2ecf20Sopenharmony_ci netif_tx_unlock_bh(ndev); 17848c2ecf20Sopenharmony_ci napi_enable(&fep->napi); 17858c2ecf20Sopenharmony_ci fep->link = phy_dev->link; 17868c2ecf20Sopenharmony_ci status_change = 1; 17878c2ecf20Sopenharmony_ci } 17888c2ecf20Sopenharmony_ci } 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci if (status_change) 17918c2ecf20Sopenharmony_ci phy_print_status(phy_dev); 17928c2ecf20Sopenharmony_ci} 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_cistatic int fec_enet_mdio_wait(struct fec_enet_private *fep) 17958c2ecf20Sopenharmony_ci{ 17968c2ecf20Sopenharmony_ci uint ievent; 17978c2ecf20Sopenharmony_ci int ret; 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci ret = readl_poll_timeout_atomic(fep->hwp + FEC_IEVENT, ievent, 18008c2ecf20Sopenharmony_ci ievent & FEC_ENET_MII, 2, 30000); 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci if (!ret) 18038c2ecf20Sopenharmony_ci writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT); 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci return ret; 18068c2ecf20Sopenharmony_ci} 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_cistatic int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum) 18098c2ecf20Sopenharmony_ci{ 18108c2ecf20Sopenharmony_ci struct fec_enet_private *fep = bus->priv; 18118c2ecf20Sopenharmony_ci struct device *dev = &fep->pdev->dev; 18128c2ecf20Sopenharmony_ci int ret = 0, frame_start, frame_addr, frame_op; 18138c2ecf20Sopenharmony_ci bool is_c45 = !!(regnum & MII_ADDR_C45); 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci ret = pm_runtime_resume_and_get(dev); 18168c2ecf20Sopenharmony_ci if (ret < 0) 18178c2ecf20Sopenharmony_ci return ret; 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci if (is_c45) { 18208c2ecf20Sopenharmony_ci frame_start = FEC_MMFR_ST_C45; 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci /* write address */ 18238c2ecf20Sopenharmony_ci frame_addr = (regnum >> 16); 18248c2ecf20Sopenharmony_ci writel(frame_start | FEC_MMFR_OP_ADDR_WRITE | 18258c2ecf20Sopenharmony_ci FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | 18268c2ecf20Sopenharmony_ci FEC_MMFR_TA | (regnum & 0xFFFF), 18278c2ecf20Sopenharmony_ci fep->hwp + FEC_MII_DATA); 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci /* wait for end of transfer */ 18308c2ecf20Sopenharmony_ci ret = fec_enet_mdio_wait(fep); 18318c2ecf20Sopenharmony_ci if (ret) { 18328c2ecf20Sopenharmony_ci netdev_err(fep->netdev, "MDIO address write timeout\n"); 18338c2ecf20Sopenharmony_ci goto out; 18348c2ecf20Sopenharmony_ci } 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci frame_op = FEC_MMFR_OP_READ_C45; 18378c2ecf20Sopenharmony_ci 18388c2ecf20Sopenharmony_ci } else { 18398c2ecf20Sopenharmony_ci /* C22 read */ 18408c2ecf20Sopenharmony_ci frame_op = FEC_MMFR_OP_READ; 18418c2ecf20Sopenharmony_ci frame_start = FEC_MMFR_ST; 18428c2ecf20Sopenharmony_ci frame_addr = regnum; 18438c2ecf20Sopenharmony_ci } 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci /* start a read op */ 18468c2ecf20Sopenharmony_ci writel(frame_start | frame_op | 18478c2ecf20Sopenharmony_ci FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | 18488c2ecf20Sopenharmony_ci FEC_MMFR_TA, fep->hwp + FEC_MII_DATA); 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci /* wait for end of transfer */ 18518c2ecf20Sopenharmony_ci ret = fec_enet_mdio_wait(fep); 18528c2ecf20Sopenharmony_ci if (ret) { 18538c2ecf20Sopenharmony_ci netdev_err(fep->netdev, "MDIO read timeout\n"); 18548c2ecf20Sopenharmony_ci goto out; 18558c2ecf20Sopenharmony_ci } 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci ret = FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA)); 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ciout: 18608c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dev); 18618c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dev); 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci return ret; 18648c2ecf20Sopenharmony_ci} 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_cistatic int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum, 18678c2ecf20Sopenharmony_ci u16 value) 18688c2ecf20Sopenharmony_ci{ 18698c2ecf20Sopenharmony_ci struct fec_enet_private *fep = bus->priv; 18708c2ecf20Sopenharmony_ci struct device *dev = &fep->pdev->dev; 18718c2ecf20Sopenharmony_ci int ret, frame_start, frame_addr; 18728c2ecf20Sopenharmony_ci bool is_c45 = !!(regnum & MII_ADDR_C45); 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci ret = pm_runtime_resume_and_get(dev); 18758c2ecf20Sopenharmony_ci if (ret < 0) 18768c2ecf20Sopenharmony_ci return ret; 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci if (is_c45) { 18798c2ecf20Sopenharmony_ci frame_start = FEC_MMFR_ST_C45; 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci /* write address */ 18828c2ecf20Sopenharmony_ci frame_addr = (regnum >> 16); 18838c2ecf20Sopenharmony_ci writel(frame_start | FEC_MMFR_OP_ADDR_WRITE | 18848c2ecf20Sopenharmony_ci FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | 18858c2ecf20Sopenharmony_ci FEC_MMFR_TA | (regnum & 0xFFFF), 18868c2ecf20Sopenharmony_ci fep->hwp + FEC_MII_DATA); 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci /* wait for end of transfer */ 18898c2ecf20Sopenharmony_ci ret = fec_enet_mdio_wait(fep); 18908c2ecf20Sopenharmony_ci if (ret) { 18918c2ecf20Sopenharmony_ci netdev_err(fep->netdev, "MDIO address write timeout\n"); 18928c2ecf20Sopenharmony_ci goto out; 18938c2ecf20Sopenharmony_ci } 18948c2ecf20Sopenharmony_ci } else { 18958c2ecf20Sopenharmony_ci /* C22 write */ 18968c2ecf20Sopenharmony_ci frame_start = FEC_MMFR_ST; 18978c2ecf20Sopenharmony_ci frame_addr = regnum; 18988c2ecf20Sopenharmony_ci } 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci /* start a write op */ 19018c2ecf20Sopenharmony_ci writel(frame_start | FEC_MMFR_OP_WRITE | 19028c2ecf20Sopenharmony_ci FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | 19038c2ecf20Sopenharmony_ci FEC_MMFR_TA | FEC_MMFR_DATA(value), 19048c2ecf20Sopenharmony_ci fep->hwp + FEC_MII_DATA); 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_ci /* wait for end of transfer */ 19078c2ecf20Sopenharmony_ci ret = fec_enet_mdio_wait(fep); 19088c2ecf20Sopenharmony_ci if (ret) 19098c2ecf20Sopenharmony_ci netdev_err(fep->netdev, "MDIO write timeout\n"); 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ciout: 19128c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dev); 19138c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dev); 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci return ret; 19168c2ecf20Sopenharmony_ci} 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_cistatic void fec_enet_phy_reset_after_clk_enable(struct net_device *ndev) 19198c2ecf20Sopenharmony_ci{ 19208c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 19218c2ecf20Sopenharmony_ci struct phy_device *phy_dev = ndev->phydev; 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ci if (phy_dev) { 19248c2ecf20Sopenharmony_ci phy_reset_after_clk_enable(phy_dev); 19258c2ecf20Sopenharmony_ci } else if (fep->phy_node) { 19268c2ecf20Sopenharmony_ci /* 19278c2ecf20Sopenharmony_ci * If the PHY still is not bound to the MAC, but there is 19288c2ecf20Sopenharmony_ci * OF PHY node and a matching PHY device instance already, 19298c2ecf20Sopenharmony_ci * use the OF PHY node to obtain the PHY device instance, 19308c2ecf20Sopenharmony_ci * and then use that PHY device instance when triggering 19318c2ecf20Sopenharmony_ci * the PHY reset. 19328c2ecf20Sopenharmony_ci */ 19338c2ecf20Sopenharmony_ci phy_dev = of_phy_find_device(fep->phy_node); 19348c2ecf20Sopenharmony_ci phy_reset_after_clk_enable(phy_dev); 19358c2ecf20Sopenharmony_ci put_device(&phy_dev->mdio.dev); 19368c2ecf20Sopenharmony_ci } 19378c2ecf20Sopenharmony_ci} 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_cistatic int fec_enet_clk_enable(struct net_device *ndev, bool enable) 19408c2ecf20Sopenharmony_ci{ 19418c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 19428c2ecf20Sopenharmony_ci int ret; 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_ci if (enable) { 19458c2ecf20Sopenharmony_ci ret = clk_prepare_enable(fep->clk_enet_out); 19468c2ecf20Sopenharmony_ci if (ret) 19478c2ecf20Sopenharmony_ci return ret; 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci if (fep->clk_ptp) { 19508c2ecf20Sopenharmony_ci mutex_lock(&fep->ptp_clk_mutex); 19518c2ecf20Sopenharmony_ci ret = clk_prepare_enable(fep->clk_ptp); 19528c2ecf20Sopenharmony_ci if (ret) { 19538c2ecf20Sopenharmony_ci mutex_unlock(&fep->ptp_clk_mutex); 19548c2ecf20Sopenharmony_ci goto failed_clk_ptp; 19558c2ecf20Sopenharmony_ci } else { 19568c2ecf20Sopenharmony_ci fep->ptp_clk_on = true; 19578c2ecf20Sopenharmony_ci } 19588c2ecf20Sopenharmony_ci mutex_unlock(&fep->ptp_clk_mutex); 19598c2ecf20Sopenharmony_ci } 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci ret = clk_prepare_enable(fep->clk_ref); 19628c2ecf20Sopenharmony_ci if (ret) 19638c2ecf20Sopenharmony_ci goto failed_clk_ref; 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci fec_enet_phy_reset_after_clk_enable(ndev); 19668c2ecf20Sopenharmony_ci } else { 19678c2ecf20Sopenharmony_ci clk_disable_unprepare(fep->clk_enet_out); 19688c2ecf20Sopenharmony_ci if (fep->clk_ptp) { 19698c2ecf20Sopenharmony_ci mutex_lock(&fep->ptp_clk_mutex); 19708c2ecf20Sopenharmony_ci clk_disable_unprepare(fep->clk_ptp); 19718c2ecf20Sopenharmony_ci fep->ptp_clk_on = false; 19728c2ecf20Sopenharmony_ci mutex_unlock(&fep->ptp_clk_mutex); 19738c2ecf20Sopenharmony_ci } 19748c2ecf20Sopenharmony_ci clk_disable_unprepare(fep->clk_ref); 19758c2ecf20Sopenharmony_ci } 19768c2ecf20Sopenharmony_ci 19778c2ecf20Sopenharmony_ci return 0; 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_cifailed_clk_ref: 19808c2ecf20Sopenharmony_ci if (fep->clk_ptp) { 19818c2ecf20Sopenharmony_ci mutex_lock(&fep->ptp_clk_mutex); 19828c2ecf20Sopenharmony_ci clk_disable_unprepare(fep->clk_ptp); 19838c2ecf20Sopenharmony_ci fep->ptp_clk_on = false; 19848c2ecf20Sopenharmony_ci mutex_unlock(&fep->ptp_clk_mutex); 19858c2ecf20Sopenharmony_ci } 19868c2ecf20Sopenharmony_cifailed_clk_ptp: 19878c2ecf20Sopenharmony_ci clk_disable_unprepare(fep->clk_enet_out); 19888c2ecf20Sopenharmony_ci 19898c2ecf20Sopenharmony_ci return ret; 19908c2ecf20Sopenharmony_ci} 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_cistatic int fec_enet_mii_probe(struct net_device *ndev) 19938c2ecf20Sopenharmony_ci{ 19948c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 19958c2ecf20Sopenharmony_ci struct phy_device *phy_dev = NULL; 19968c2ecf20Sopenharmony_ci char mdio_bus_id[MII_BUS_ID_SIZE]; 19978c2ecf20Sopenharmony_ci char phy_name[MII_BUS_ID_SIZE + 3]; 19988c2ecf20Sopenharmony_ci int phy_id; 19998c2ecf20Sopenharmony_ci int dev_id = fep->dev_id; 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci if (fep->phy_node) { 20028c2ecf20Sopenharmony_ci phy_dev = of_phy_connect(ndev, fep->phy_node, 20038c2ecf20Sopenharmony_ci &fec_enet_adjust_link, 0, 20048c2ecf20Sopenharmony_ci fep->phy_interface); 20058c2ecf20Sopenharmony_ci if (!phy_dev) { 20068c2ecf20Sopenharmony_ci netdev_err(ndev, "Unable to connect to phy\n"); 20078c2ecf20Sopenharmony_ci return -ENODEV; 20088c2ecf20Sopenharmony_ci } 20098c2ecf20Sopenharmony_ci } else { 20108c2ecf20Sopenharmony_ci /* check for attached phy */ 20118c2ecf20Sopenharmony_ci for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) { 20128c2ecf20Sopenharmony_ci if (!mdiobus_is_registered_device(fep->mii_bus, phy_id)) 20138c2ecf20Sopenharmony_ci continue; 20148c2ecf20Sopenharmony_ci if (dev_id--) 20158c2ecf20Sopenharmony_ci continue; 20168c2ecf20Sopenharmony_ci strlcpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE); 20178c2ecf20Sopenharmony_ci break; 20188c2ecf20Sopenharmony_ci } 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci if (phy_id >= PHY_MAX_ADDR) { 20218c2ecf20Sopenharmony_ci netdev_info(ndev, "no PHY, assuming direct connection to switch\n"); 20228c2ecf20Sopenharmony_ci strlcpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE); 20238c2ecf20Sopenharmony_ci phy_id = 0; 20248c2ecf20Sopenharmony_ci } 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci snprintf(phy_name, sizeof(phy_name), 20278c2ecf20Sopenharmony_ci PHY_ID_FMT, mdio_bus_id, phy_id); 20288c2ecf20Sopenharmony_ci phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, 20298c2ecf20Sopenharmony_ci fep->phy_interface); 20308c2ecf20Sopenharmony_ci } 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci if (IS_ERR(phy_dev)) { 20338c2ecf20Sopenharmony_ci netdev_err(ndev, "could not attach to PHY\n"); 20348c2ecf20Sopenharmony_ci return PTR_ERR(phy_dev); 20358c2ecf20Sopenharmony_ci } 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_ci /* mask with MAC supported features */ 20388c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_GBIT) { 20398c2ecf20Sopenharmony_ci phy_set_max_speed(phy_dev, 1000); 20408c2ecf20Sopenharmony_ci phy_remove_link_mode(phy_dev, 20418c2ecf20Sopenharmony_ci ETHTOOL_LINK_MODE_1000baseT_Half_BIT); 20428c2ecf20Sopenharmony_ci#if !defined(CONFIG_M5272) 20438c2ecf20Sopenharmony_ci phy_support_sym_pause(phy_dev); 20448c2ecf20Sopenharmony_ci#endif 20458c2ecf20Sopenharmony_ci } 20468c2ecf20Sopenharmony_ci else 20478c2ecf20Sopenharmony_ci phy_set_max_speed(phy_dev, 100); 20488c2ecf20Sopenharmony_ci 20498c2ecf20Sopenharmony_ci fep->link = 0; 20508c2ecf20Sopenharmony_ci fep->full_duplex = 0; 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_ci phy_attached_info(phy_dev); 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_ci return 0; 20558c2ecf20Sopenharmony_ci} 20568c2ecf20Sopenharmony_ci 20578c2ecf20Sopenharmony_cistatic int fec_enet_mii_init(struct platform_device *pdev) 20588c2ecf20Sopenharmony_ci{ 20598c2ecf20Sopenharmony_ci static struct mii_bus *fec0_mii_bus; 20608c2ecf20Sopenharmony_ci struct net_device *ndev = platform_get_drvdata(pdev); 20618c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 20628c2ecf20Sopenharmony_ci bool suppress_preamble = false; 20638c2ecf20Sopenharmony_ci struct device_node *node; 20648c2ecf20Sopenharmony_ci int err = -ENXIO; 20658c2ecf20Sopenharmony_ci u32 mii_speed, holdtime; 20668c2ecf20Sopenharmony_ci u32 bus_freq; 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_ci /* 20698c2ecf20Sopenharmony_ci * The i.MX28 dual fec interfaces are not equal. 20708c2ecf20Sopenharmony_ci * Here are the differences: 20718c2ecf20Sopenharmony_ci * 20728c2ecf20Sopenharmony_ci * - fec0 supports MII & RMII modes while fec1 only supports RMII 20738c2ecf20Sopenharmony_ci * - fec0 acts as the 1588 time master while fec1 is slave 20748c2ecf20Sopenharmony_ci * - external phys can only be configured by fec0 20758c2ecf20Sopenharmony_ci * 20768c2ecf20Sopenharmony_ci * That is to say fec1 can not work independently. It only works 20778c2ecf20Sopenharmony_ci * when fec0 is working. The reason behind this design is that the 20788c2ecf20Sopenharmony_ci * second interface is added primarily for Switch mode. 20798c2ecf20Sopenharmony_ci * 20808c2ecf20Sopenharmony_ci * Because of the last point above, both phys are attached on fec0 20818c2ecf20Sopenharmony_ci * mdio interface in board design, and need to be configured by 20828c2ecf20Sopenharmony_ci * fec0 mii_bus. 20838c2ecf20Sopenharmony_ci */ 20848c2ecf20Sopenharmony_ci if ((fep->quirks & FEC_QUIRK_SINGLE_MDIO) && fep->dev_id > 0) { 20858c2ecf20Sopenharmony_ci /* fec1 uses fec0 mii_bus */ 20868c2ecf20Sopenharmony_ci if (mii_cnt && fec0_mii_bus) { 20878c2ecf20Sopenharmony_ci fep->mii_bus = fec0_mii_bus; 20888c2ecf20Sopenharmony_ci mii_cnt++; 20898c2ecf20Sopenharmony_ci return 0; 20908c2ecf20Sopenharmony_ci } 20918c2ecf20Sopenharmony_ci return -ENOENT; 20928c2ecf20Sopenharmony_ci } 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ci bus_freq = 2500000; /* 2.5MHz by default */ 20958c2ecf20Sopenharmony_ci node = of_get_child_by_name(pdev->dev.of_node, "mdio"); 20968c2ecf20Sopenharmony_ci if (node) { 20978c2ecf20Sopenharmony_ci of_property_read_u32(node, "clock-frequency", &bus_freq); 20988c2ecf20Sopenharmony_ci suppress_preamble = of_property_read_bool(node, 20998c2ecf20Sopenharmony_ci "suppress-preamble"); 21008c2ecf20Sopenharmony_ci } 21018c2ecf20Sopenharmony_ci 21028c2ecf20Sopenharmony_ci /* 21038c2ecf20Sopenharmony_ci * Set MII speed (= clk_get_rate() / 2 * phy_speed) 21048c2ecf20Sopenharmony_ci * 21058c2ecf20Sopenharmony_ci * The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while 21068c2ecf20Sopenharmony_ci * for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'. The i.MX28 21078c2ecf20Sopenharmony_ci * Reference Manual has an error on this, and gets fixed on i.MX6Q 21088c2ecf20Sopenharmony_ci * document. 21098c2ecf20Sopenharmony_ci */ 21108c2ecf20Sopenharmony_ci mii_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), bus_freq * 2); 21118c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_ENET_MAC) 21128c2ecf20Sopenharmony_ci mii_speed--; 21138c2ecf20Sopenharmony_ci if (mii_speed > 63) { 21148c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 21158c2ecf20Sopenharmony_ci "fec clock (%lu) too fast to get right mii speed\n", 21168c2ecf20Sopenharmony_ci clk_get_rate(fep->clk_ipg)); 21178c2ecf20Sopenharmony_ci err = -EINVAL; 21188c2ecf20Sopenharmony_ci goto err_out; 21198c2ecf20Sopenharmony_ci } 21208c2ecf20Sopenharmony_ci 21218c2ecf20Sopenharmony_ci /* 21228c2ecf20Sopenharmony_ci * The i.MX28 and i.MX6 types have another filed in the MSCR (aka 21238c2ecf20Sopenharmony_ci * MII_SPEED) register that defines the MDIO output hold time. Earlier 21248c2ecf20Sopenharmony_ci * versions are RAZ there, so just ignore the difference and write the 21258c2ecf20Sopenharmony_ci * register always. 21268c2ecf20Sopenharmony_ci * The minimal hold time according to IEE802.3 (clause 22) is 10 ns. 21278c2ecf20Sopenharmony_ci * HOLDTIME + 1 is the number of clk cycles the fec is holding the 21288c2ecf20Sopenharmony_ci * output. 21298c2ecf20Sopenharmony_ci * The HOLDTIME bitfield takes values between 0 and 7 (inclusive). 21308c2ecf20Sopenharmony_ci * Given that ceil(clkrate / 5000000) <= 64, the calculation for 21318c2ecf20Sopenharmony_ci * holdtime cannot result in a value greater than 3. 21328c2ecf20Sopenharmony_ci */ 21338c2ecf20Sopenharmony_ci holdtime = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 100000000) - 1; 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci fep->phy_speed = mii_speed << 1 | holdtime << 8; 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_ci if (suppress_preamble) 21388c2ecf20Sopenharmony_ci fep->phy_speed |= BIT(7); 21398c2ecf20Sopenharmony_ci 21408c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_CLEAR_SETUP_MII) { 21418c2ecf20Sopenharmony_ci /* Clear MMFR to avoid to generate MII event by writing MSCR. 21428c2ecf20Sopenharmony_ci * MII event generation condition: 21438c2ecf20Sopenharmony_ci * - writing MSCR: 21448c2ecf20Sopenharmony_ci * - mmfr[31:0]_not_zero & mscr[7:0]_is_zero & 21458c2ecf20Sopenharmony_ci * mscr_reg_data_in[7:0] != 0 21468c2ecf20Sopenharmony_ci * - writing MMFR: 21478c2ecf20Sopenharmony_ci * - mscr[7:0]_not_zero 21488c2ecf20Sopenharmony_ci */ 21498c2ecf20Sopenharmony_ci writel(0, fep->hwp + FEC_MII_DATA); 21508c2ecf20Sopenharmony_ci } 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_ci writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 21538c2ecf20Sopenharmony_ci 21548c2ecf20Sopenharmony_ci /* Clear any pending transaction complete indication */ 21558c2ecf20Sopenharmony_ci writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT); 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ci fep->mii_bus = mdiobus_alloc(); 21588c2ecf20Sopenharmony_ci if (fep->mii_bus == NULL) { 21598c2ecf20Sopenharmony_ci err = -ENOMEM; 21608c2ecf20Sopenharmony_ci goto err_out; 21618c2ecf20Sopenharmony_ci } 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci fep->mii_bus->name = "fec_enet_mii_bus"; 21648c2ecf20Sopenharmony_ci fep->mii_bus->read = fec_enet_mdio_read; 21658c2ecf20Sopenharmony_ci fep->mii_bus->write = fec_enet_mdio_write; 21668c2ecf20Sopenharmony_ci snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", 21678c2ecf20Sopenharmony_ci pdev->name, fep->dev_id + 1); 21688c2ecf20Sopenharmony_ci fep->mii_bus->priv = fep; 21698c2ecf20Sopenharmony_ci fep->mii_bus->parent = &pdev->dev; 21708c2ecf20Sopenharmony_ci 21718c2ecf20Sopenharmony_ci err = of_mdiobus_register(fep->mii_bus, node); 21728c2ecf20Sopenharmony_ci if (err) 21738c2ecf20Sopenharmony_ci goto err_out_free_mdiobus; 21748c2ecf20Sopenharmony_ci of_node_put(node); 21758c2ecf20Sopenharmony_ci 21768c2ecf20Sopenharmony_ci mii_cnt++; 21778c2ecf20Sopenharmony_ci 21788c2ecf20Sopenharmony_ci /* save fec0 mii_bus */ 21798c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_SINGLE_MDIO) 21808c2ecf20Sopenharmony_ci fec0_mii_bus = fep->mii_bus; 21818c2ecf20Sopenharmony_ci 21828c2ecf20Sopenharmony_ci return 0; 21838c2ecf20Sopenharmony_ci 21848c2ecf20Sopenharmony_cierr_out_free_mdiobus: 21858c2ecf20Sopenharmony_ci mdiobus_free(fep->mii_bus); 21868c2ecf20Sopenharmony_cierr_out: 21878c2ecf20Sopenharmony_ci of_node_put(node); 21888c2ecf20Sopenharmony_ci return err; 21898c2ecf20Sopenharmony_ci} 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_cistatic void fec_enet_mii_remove(struct fec_enet_private *fep) 21928c2ecf20Sopenharmony_ci{ 21938c2ecf20Sopenharmony_ci if (--mii_cnt == 0) { 21948c2ecf20Sopenharmony_ci mdiobus_unregister(fep->mii_bus); 21958c2ecf20Sopenharmony_ci mdiobus_free(fep->mii_bus); 21968c2ecf20Sopenharmony_ci } 21978c2ecf20Sopenharmony_ci} 21988c2ecf20Sopenharmony_ci 21998c2ecf20Sopenharmony_cistatic void fec_enet_get_drvinfo(struct net_device *ndev, 22008c2ecf20Sopenharmony_ci struct ethtool_drvinfo *info) 22018c2ecf20Sopenharmony_ci{ 22028c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 22038c2ecf20Sopenharmony_ci 22048c2ecf20Sopenharmony_ci strlcpy(info->driver, fep->pdev->dev.driver->name, 22058c2ecf20Sopenharmony_ci sizeof(info->driver)); 22068c2ecf20Sopenharmony_ci strlcpy(info->bus_info, dev_name(&ndev->dev), sizeof(info->bus_info)); 22078c2ecf20Sopenharmony_ci} 22088c2ecf20Sopenharmony_ci 22098c2ecf20Sopenharmony_cistatic int fec_enet_get_regs_len(struct net_device *ndev) 22108c2ecf20Sopenharmony_ci{ 22118c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 22128c2ecf20Sopenharmony_ci struct resource *r; 22138c2ecf20Sopenharmony_ci int s = 0; 22148c2ecf20Sopenharmony_ci 22158c2ecf20Sopenharmony_ci r = platform_get_resource(fep->pdev, IORESOURCE_MEM, 0); 22168c2ecf20Sopenharmony_ci if (r) 22178c2ecf20Sopenharmony_ci s = resource_size(r); 22188c2ecf20Sopenharmony_ci 22198c2ecf20Sopenharmony_ci return s; 22208c2ecf20Sopenharmony_ci} 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_ci/* List of registers that can be safety be read to dump them with ethtool */ 22238c2ecf20Sopenharmony_ci#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ 22248c2ecf20Sopenharmony_ci defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ 22258c2ecf20Sopenharmony_ci defined(CONFIG_ARM64) || defined(CONFIG_COMPILE_TEST) 22268c2ecf20Sopenharmony_cistatic __u32 fec_enet_register_version = 2; 22278c2ecf20Sopenharmony_cistatic u32 fec_enet_register_offset[] = { 22288c2ecf20Sopenharmony_ci FEC_IEVENT, FEC_IMASK, FEC_R_DES_ACTIVE_0, FEC_X_DES_ACTIVE_0, 22298c2ecf20Sopenharmony_ci FEC_ECNTRL, FEC_MII_DATA, FEC_MII_SPEED, FEC_MIB_CTRLSTAT, FEC_R_CNTRL, 22308c2ecf20Sopenharmony_ci FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, FEC_OPD, FEC_TXIC0, FEC_TXIC1, 22318c2ecf20Sopenharmony_ci FEC_TXIC2, FEC_RXIC0, FEC_RXIC1, FEC_RXIC2, FEC_HASH_TABLE_HIGH, 22328c2ecf20Sopenharmony_ci FEC_HASH_TABLE_LOW, FEC_GRP_HASH_TABLE_HIGH, FEC_GRP_HASH_TABLE_LOW, 22338c2ecf20Sopenharmony_ci FEC_X_WMRK, FEC_R_BOUND, FEC_R_FSTART, FEC_R_DES_START_1, 22348c2ecf20Sopenharmony_ci FEC_X_DES_START_1, FEC_R_BUFF_SIZE_1, FEC_R_DES_START_2, 22358c2ecf20Sopenharmony_ci FEC_X_DES_START_2, FEC_R_BUFF_SIZE_2, FEC_R_DES_START_0, 22368c2ecf20Sopenharmony_ci FEC_X_DES_START_0, FEC_R_BUFF_SIZE_0, FEC_R_FIFO_RSFL, FEC_R_FIFO_RSEM, 22378c2ecf20Sopenharmony_ci FEC_R_FIFO_RAEM, FEC_R_FIFO_RAFL, FEC_RACC, FEC_RCMR_1, FEC_RCMR_2, 22388c2ecf20Sopenharmony_ci FEC_DMA_CFG_1, FEC_DMA_CFG_2, FEC_R_DES_ACTIVE_1, FEC_X_DES_ACTIVE_1, 22398c2ecf20Sopenharmony_ci FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_2, FEC_QOS_SCHEME, 22408c2ecf20Sopenharmony_ci RMON_T_DROP, RMON_T_PACKETS, RMON_T_BC_PKT, RMON_T_MC_PKT, 22418c2ecf20Sopenharmony_ci RMON_T_CRC_ALIGN, RMON_T_UNDERSIZE, RMON_T_OVERSIZE, RMON_T_FRAG, 22428c2ecf20Sopenharmony_ci RMON_T_JAB, RMON_T_COL, RMON_T_P64, RMON_T_P65TO127, RMON_T_P128TO255, 22438c2ecf20Sopenharmony_ci RMON_T_P256TO511, RMON_T_P512TO1023, RMON_T_P1024TO2047, 22448c2ecf20Sopenharmony_ci RMON_T_P_GTE2048, RMON_T_OCTETS, 22458c2ecf20Sopenharmony_ci IEEE_T_DROP, IEEE_T_FRAME_OK, IEEE_T_1COL, IEEE_T_MCOL, IEEE_T_DEF, 22468c2ecf20Sopenharmony_ci IEEE_T_LCOL, IEEE_T_EXCOL, IEEE_T_MACERR, IEEE_T_CSERR, IEEE_T_SQE, 22478c2ecf20Sopenharmony_ci IEEE_T_FDXFC, IEEE_T_OCTETS_OK, 22488c2ecf20Sopenharmony_ci RMON_R_PACKETS, RMON_R_BC_PKT, RMON_R_MC_PKT, RMON_R_CRC_ALIGN, 22498c2ecf20Sopenharmony_ci RMON_R_UNDERSIZE, RMON_R_OVERSIZE, RMON_R_FRAG, RMON_R_JAB, 22508c2ecf20Sopenharmony_ci RMON_R_RESVD_O, RMON_R_P64, RMON_R_P65TO127, RMON_R_P128TO255, 22518c2ecf20Sopenharmony_ci RMON_R_P256TO511, RMON_R_P512TO1023, RMON_R_P1024TO2047, 22528c2ecf20Sopenharmony_ci RMON_R_P_GTE2048, RMON_R_OCTETS, 22538c2ecf20Sopenharmony_ci IEEE_R_DROP, IEEE_R_FRAME_OK, IEEE_R_CRC, IEEE_R_ALIGN, IEEE_R_MACERR, 22548c2ecf20Sopenharmony_ci IEEE_R_FDXFC, IEEE_R_OCTETS_OK 22558c2ecf20Sopenharmony_ci}; 22568c2ecf20Sopenharmony_ci/* for i.MX6ul */ 22578c2ecf20Sopenharmony_cistatic u32 fec_enet_register_offset_6ul[] = { 22588c2ecf20Sopenharmony_ci FEC_IEVENT, FEC_IMASK, FEC_R_DES_ACTIVE_0, FEC_X_DES_ACTIVE_0, 22598c2ecf20Sopenharmony_ci FEC_ECNTRL, FEC_MII_DATA, FEC_MII_SPEED, FEC_MIB_CTRLSTAT, FEC_R_CNTRL, 22608c2ecf20Sopenharmony_ci FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, FEC_OPD, FEC_TXIC0, FEC_RXIC0, 22618c2ecf20Sopenharmony_ci FEC_HASH_TABLE_HIGH, FEC_HASH_TABLE_LOW, FEC_GRP_HASH_TABLE_HIGH, 22628c2ecf20Sopenharmony_ci FEC_GRP_HASH_TABLE_LOW, FEC_X_WMRK, FEC_R_DES_START_0, 22638c2ecf20Sopenharmony_ci FEC_X_DES_START_0, FEC_R_BUFF_SIZE_0, FEC_R_FIFO_RSFL, FEC_R_FIFO_RSEM, 22648c2ecf20Sopenharmony_ci FEC_R_FIFO_RAEM, FEC_R_FIFO_RAFL, FEC_RACC, 22658c2ecf20Sopenharmony_ci RMON_T_DROP, RMON_T_PACKETS, RMON_T_BC_PKT, RMON_T_MC_PKT, 22668c2ecf20Sopenharmony_ci RMON_T_CRC_ALIGN, RMON_T_UNDERSIZE, RMON_T_OVERSIZE, RMON_T_FRAG, 22678c2ecf20Sopenharmony_ci RMON_T_JAB, RMON_T_COL, RMON_T_P64, RMON_T_P65TO127, RMON_T_P128TO255, 22688c2ecf20Sopenharmony_ci RMON_T_P256TO511, RMON_T_P512TO1023, RMON_T_P1024TO2047, 22698c2ecf20Sopenharmony_ci RMON_T_P_GTE2048, RMON_T_OCTETS, 22708c2ecf20Sopenharmony_ci IEEE_T_DROP, IEEE_T_FRAME_OK, IEEE_T_1COL, IEEE_T_MCOL, IEEE_T_DEF, 22718c2ecf20Sopenharmony_ci IEEE_T_LCOL, IEEE_T_EXCOL, IEEE_T_MACERR, IEEE_T_CSERR, IEEE_T_SQE, 22728c2ecf20Sopenharmony_ci IEEE_T_FDXFC, IEEE_T_OCTETS_OK, 22738c2ecf20Sopenharmony_ci RMON_R_PACKETS, RMON_R_BC_PKT, RMON_R_MC_PKT, RMON_R_CRC_ALIGN, 22748c2ecf20Sopenharmony_ci RMON_R_UNDERSIZE, RMON_R_OVERSIZE, RMON_R_FRAG, RMON_R_JAB, 22758c2ecf20Sopenharmony_ci RMON_R_RESVD_O, RMON_R_P64, RMON_R_P65TO127, RMON_R_P128TO255, 22768c2ecf20Sopenharmony_ci RMON_R_P256TO511, RMON_R_P512TO1023, RMON_R_P1024TO2047, 22778c2ecf20Sopenharmony_ci RMON_R_P_GTE2048, RMON_R_OCTETS, 22788c2ecf20Sopenharmony_ci IEEE_R_DROP, IEEE_R_FRAME_OK, IEEE_R_CRC, IEEE_R_ALIGN, IEEE_R_MACERR, 22798c2ecf20Sopenharmony_ci IEEE_R_FDXFC, IEEE_R_OCTETS_OK 22808c2ecf20Sopenharmony_ci}; 22818c2ecf20Sopenharmony_ci#else 22828c2ecf20Sopenharmony_cistatic __u32 fec_enet_register_version = 1; 22838c2ecf20Sopenharmony_cistatic u32 fec_enet_register_offset[] = { 22848c2ecf20Sopenharmony_ci FEC_ECNTRL, FEC_IEVENT, FEC_IMASK, FEC_IVEC, FEC_R_DES_ACTIVE_0, 22858c2ecf20Sopenharmony_ci FEC_R_DES_ACTIVE_1, FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_0, 22868c2ecf20Sopenharmony_ci FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2, FEC_MII_DATA, FEC_MII_SPEED, 22878c2ecf20Sopenharmony_ci FEC_R_BOUND, FEC_R_FSTART, FEC_X_WMRK, FEC_X_FSTART, FEC_R_CNTRL, 22888c2ecf20Sopenharmony_ci FEC_MAX_FRM_LEN, FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, 22898c2ecf20Sopenharmony_ci FEC_GRP_HASH_TABLE_HIGH, FEC_GRP_HASH_TABLE_LOW, FEC_R_DES_START_0, 22908c2ecf20Sopenharmony_ci FEC_R_DES_START_1, FEC_R_DES_START_2, FEC_X_DES_START_0, 22918c2ecf20Sopenharmony_ci FEC_X_DES_START_1, FEC_X_DES_START_2, FEC_R_BUFF_SIZE_0, 22928c2ecf20Sopenharmony_ci FEC_R_BUFF_SIZE_1, FEC_R_BUFF_SIZE_2 22938c2ecf20Sopenharmony_ci}; 22948c2ecf20Sopenharmony_ci#endif 22958c2ecf20Sopenharmony_ci 22968c2ecf20Sopenharmony_cistatic void fec_enet_get_regs(struct net_device *ndev, 22978c2ecf20Sopenharmony_ci struct ethtool_regs *regs, void *regbuf) 22988c2ecf20Sopenharmony_ci{ 22998c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 23008c2ecf20Sopenharmony_ci u32 __iomem *theregs = (u32 __iomem *)fep->hwp; 23018c2ecf20Sopenharmony_ci struct device *dev = &fep->pdev->dev; 23028c2ecf20Sopenharmony_ci u32 *buf = (u32 *)regbuf; 23038c2ecf20Sopenharmony_ci u32 i, off; 23048c2ecf20Sopenharmony_ci int ret; 23058c2ecf20Sopenharmony_ci#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ 23068c2ecf20Sopenharmony_ci defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ 23078c2ecf20Sopenharmony_ci defined(CONFIG_ARM64) || defined(CONFIG_COMPILE_TEST) 23088c2ecf20Sopenharmony_ci u32 *reg_list; 23098c2ecf20Sopenharmony_ci u32 reg_cnt; 23108c2ecf20Sopenharmony_ci 23118c2ecf20Sopenharmony_ci if (!of_machine_is_compatible("fsl,imx6ul")) { 23128c2ecf20Sopenharmony_ci reg_list = fec_enet_register_offset; 23138c2ecf20Sopenharmony_ci reg_cnt = ARRAY_SIZE(fec_enet_register_offset); 23148c2ecf20Sopenharmony_ci } else { 23158c2ecf20Sopenharmony_ci reg_list = fec_enet_register_offset_6ul; 23168c2ecf20Sopenharmony_ci reg_cnt = ARRAY_SIZE(fec_enet_register_offset_6ul); 23178c2ecf20Sopenharmony_ci } 23188c2ecf20Sopenharmony_ci#else 23198c2ecf20Sopenharmony_ci /* coldfire */ 23208c2ecf20Sopenharmony_ci static u32 *reg_list = fec_enet_register_offset; 23218c2ecf20Sopenharmony_ci static const u32 reg_cnt = ARRAY_SIZE(fec_enet_register_offset); 23228c2ecf20Sopenharmony_ci#endif 23238c2ecf20Sopenharmony_ci ret = pm_runtime_resume_and_get(dev); 23248c2ecf20Sopenharmony_ci if (ret < 0) 23258c2ecf20Sopenharmony_ci return; 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_ci regs->version = fec_enet_register_version; 23288c2ecf20Sopenharmony_ci 23298c2ecf20Sopenharmony_ci memset(buf, 0, regs->len); 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ci for (i = 0; i < reg_cnt; i++) { 23328c2ecf20Sopenharmony_ci off = reg_list[i]; 23338c2ecf20Sopenharmony_ci 23348c2ecf20Sopenharmony_ci if ((off == FEC_R_BOUND || off == FEC_R_FSTART) && 23358c2ecf20Sopenharmony_ci !(fep->quirks & FEC_QUIRK_HAS_FRREG)) 23368c2ecf20Sopenharmony_ci continue; 23378c2ecf20Sopenharmony_ci 23388c2ecf20Sopenharmony_ci off >>= 2; 23398c2ecf20Sopenharmony_ci buf[off] = readl(&theregs[off]); 23408c2ecf20Sopenharmony_ci } 23418c2ecf20Sopenharmony_ci 23428c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dev); 23438c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dev); 23448c2ecf20Sopenharmony_ci} 23458c2ecf20Sopenharmony_ci 23468c2ecf20Sopenharmony_cistatic int fec_enet_get_ts_info(struct net_device *ndev, 23478c2ecf20Sopenharmony_ci struct ethtool_ts_info *info) 23488c2ecf20Sopenharmony_ci{ 23498c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 23508c2ecf20Sopenharmony_ci 23518c2ecf20Sopenharmony_ci if (fep->bufdesc_ex) { 23528c2ecf20Sopenharmony_ci 23538c2ecf20Sopenharmony_ci info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | 23548c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_RX_SOFTWARE | 23558c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_SOFTWARE | 23568c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_TX_HARDWARE | 23578c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_RX_HARDWARE | 23588c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_RAW_HARDWARE; 23598c2ecf20Sopenharmony_ci if (fep->ptp_clock) 23608c2ecf20Sopenharmony_ci info->phc_index = ptp_clock_index(fep->ptp_clock); 23618c2ecf20Sopenharmony_ci else 23628c2ecf20Sopenharmony_ci info->phc_index = -1; 23638c2ecf20Sopenharmony_ci 23648c2ecf20Sopenharmony_ci info->tx_types = (1 << HWTSTAMP_TX_OFF) | 23658c2ecf20Sopenharmony_ci (1 << HWTSTAMP_TX_ON); 23668c2ecf20Sopenharmony_ci 23678c2ecf20Sopenharmony_ci info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | 23688c2ecf20Sopenharmony_ci (1 << HWTSTAMP_FILTER_ALL); 23698c2ecf20Sopenharmony_ci return 0; 23708c2ecf20Sopenharmony_ci } else { 23718c2ecf20Sopenharmony_ci return ethtool_op_get_ts_info(ndev, info); 23728c2ecf20Sopenharmony_ci } 23738c2ecf20Sopenharmony_ci} 23748c2ecf20Sopenharmony_ci 23758c2ecf20Sopenharmony_ci#if !defined(CONFIG_M5272) 23768c2ecf20Sopenharmony_ci 23778c2ecf20Sopenharmony_cistatic void fec_enet_get_pauseparam(struct net_device *ndev, 23788c2ecf20Sopenharmony_ci struct ethtool_pauseparam *pause) 23798c2ecf20Sopenharmony_ci{ 23808c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 23818c2ecf20Sopenharmony_ci 23828c2ecf20Sopenharmony_ci pause->autoneg = (fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) != 0; 23838c2ecf20Sopenharmony_ci pause->tx_pause = (fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) != 0; 23848c2ecf20Sopenharmony_ci pause->rx_pause = pause->tx_pause; 23858c2ecf20Sopenharmony_ci} 23868c2ecf20Sopenharmony_ci 23878c2ecf20Sopenharmony_cistatic int fec_enet_set_pauseparam(struct net_device *ndev, 23888c2ecf20Sopenharmony_ci struct ethtool_pauseparam *pause) 23898c2ecf20Sopenharmony_ci{ 23908c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 23918c2ecf20Sopenharmony_ci 23928c2ecf20Sopenharmony_ci if (!ndev->phydev) 23938c2ecf20Sopenharmony_ci return -ENODEV; 23948c2ecf20Sopenharmony_ci 23958c2ecf20Sopenharmony_ci if (pause->tx_pause != pause->rx_pause) { 23968c2ecf20Sopenharmony_ci netdev_info(ndev, 23978c2ecf20Sopenharmony_ci "hardware only support enable/disable both tx and rx"); 23988c2ecf20Sopenharmony_ci return -EINVAL; 23998c2ecf20Sopenharmony_ci } 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_ci fep->pause_flag = 0; 24028c2ecf20Sopenharmony_ci 24038c2ecf20Sopenharmony_ci /* tx pause must be same as rx pause */ 24048c2ecf20Sopenharmony_ci fep->pause_flag |= pause->rx_pause ? FEC_PAUSE_FLAG_ENABLE : 0; 24058c2ecf20Sopenharmony_ci fep->pause_flag |= pause->autoneg ? FEC_PAUSE_FLAG_AUTONEG : 0; 24068c2ecf20Sopenharmony_ci 24078c2ecf20Sopenharmony_ci phy_set_sym_pause(ndev->phydev, pause->rx_pause, pause->tx_pause, 24088c2ecf20Sopenharmony_ci pause->autoneg); 24098c2ecf20Sopenharmony_ci 24108c2ecf20Sopenharmony_ci if (pause->autoneg) { 24118c2ecf20Sopenharmony_ci if (netif_running(ndev)) 24128c2ecf20Sopenharmony_ci fec_stop(ndev); 24138c2ecf20Sopenharmony_ci phy_start_aneg(ndev->phydev); 24148c2ecf20Sopenharmony_ci } 24158c2ecf20Sopenharmony_ci if (netif_running(ndev)) { 24168c2ecf20Sopenharmony_ci napi_disable(&fep->napi); 24178c2ecf20Sopenharmony_ci netif_tx_lock_bh(ndev); 24188c2ecf20Sopenharmony_ci fec_restart(ndev); 24198c2ecf20Sopenharmony_ci netif_tx_wake_all_queues(ndev); 24208c2ecf20Sopenharmony_ci netif_tx_unlock_bh(ndev); 24218c2ecf20Sopenharmony_ci napi_enable(&fep->napi); 24228c2ecf20Sopenharmony_ci } 24238c2ecf20Sopenharmony_ci 24248c2ecf20Sopenharmony_ci return 0; 24258c2ecf20Sopenharmony_ci} 24268c2ecf20Sopenharmony_ci 24278c2ecf20Sopenharmony_cistatic const struct fec_stat { 24288c2ecf20Sopenharmony_ci char name[ETH_GSTRING_LEN]; 24298c2ecf20Sopenharmony_ci u16 offset; 24308c2ecf20Sopenharmony_ci} fec_stats[] = { 24318c2ecf20Sopenharmony_ci /* RMON TX */ 24328c2ecf20Sopenharmony_ci { "tx_dropped", RMON_T_DROP }, 24338c2ecf20Sopenharmony_ci { "tx_packets", RMON_T_PACKETS }, 24348c2ecf20Sopenharmony_ci { "tx_broadcast", RMON_T_BC_PKT }, 24358c2ecf20Sopenharmony_ci { "tx_multicast", RMON_T_MC_PKT }, 24368c2ecf20Sopenharmony_ci { "tx_crc_errors", RMON_T_CRC_ALIGN }, 24378c2ecf20Sopenharmony_ci { "tx_undersize", RMON_T_UNDERSIZE }, 24388c2ecf20Sopenharmony_ci { "tx_oversize", RMON_T_OVERSIZE }, 24398c2ecf20Sopenharmony_ci { "tx_fragment", RMON_T_FRAG }, 24408c2ecf20Sopenharmony_ci { "tx_jabber", RMON_T_JAB }, 24418c2ecf20Sopenharmony_ci { "tx_collision", RMON_T_COL }, 24428c2ecf20Sopenharmony_ci { "tx_64byte", RMON_T_P64 }, 24438c2ecf20Sopenharmony_ci { "tx_65to127byte", RMON_T_P65TO127 }, 24448c2ecf20Sopenharmony_ci { "tx_128to255byte", RMON_T_P128TO255 }, 24458c2ecf20Sopenharmony_ci { "tx_256to511byte", RMON_T_P256TO511 }, 24468c2ecf20Sopenharmony_ci { "tx_512to1023byte", RMON_T_P512TO1023 }, 24478c2ecf20Sopenharmony_ci { "tx_1024to2047byte", RMON_T_P1024TO2047 }, 24488c2ecf20Sopenharmony_ci { "tx_GTE2048byte", RMON_T_P_GTE2048 }, 24498c2ecf20Sopenharmony_ci { "tx_octets", RMON_T_OCTETS }, 24508c2ecf20Sopenharmony_ci 24518c2ecf20Sopenharmony_ci /* IEEE TX */ 24528c2ecf20Sopenharmony_ci { "IEEE_tx_drop", IEEE_T_DROP }, 24538c2ecf20Sopenharmony_ci { "IEEE_tx_frame_ok", IEEE_T_FRAME_OK }, 24548c2ecf20Sopenharmony_ci { "IEEE_tx_1col", IEEE_T_1COL }, 24558c2ecf20Sopenharmony_ci { "IEEE_tx_mcol", IEEE_T_MCOL }, 24568c2ecf20Sopenharmony_ci { "IEEE_tx_def", IEEE_T_DEF }, 24578c2ecf20Sopenharmony_ci { "IEEE_tx_lcol", IEEE_T_LCOL }, 24588c2ecf20Sopenharmony_ci { "IEEE_tx_excol", IEEE_T_EXCOL }, 24598c2ecf20Sopenharmony_ci { "IEEE_tx_macerr", IEEE_T_MACERR }, 24608c2ecf20Sopenharmony_ci { "IEEE_tx_cserr", IEEE_T_CSERR }, 24618c2ecf20Sopenharmony_ci { "IEEE_tx_sqe", IEEE_T_SQE }, 24628c2ecf20Sopenharmony_ci { "IEEE_tx_fdxfc", IEEE_T_FDXFC }, 24638c2ecf20Sopenharmony_ci { "IEEE_tx_octets_ok", IEEE_T_OCTETS_OK }, 24648c2ecf20Sopenharmony_ci 24658c2ecf20Sopenharmony_ci /* RMON RX */ 24668c2ecf20Sopenharmony_ci { "rx_packets", RMON_R_PACKETS }, 24678c2ecf20Sopenharmony_ci { "rx_broadcast", RMON_R_BC_PKT }, 24688c2ecf20Sopenharmony_ci { "rx_multicast", RMON_R_MC_PKT }, 24698c2ecf20Sopenharmony_ci { "rx_crc_errors", RMON_R_CRC_ALIGN }, 24708c2ecf20Sopenharmony_ci { "rx_undersize", RMON_R_UNDERSIZE }, 24718c2ecf20Sopenharmony_ci { "rx_oversize", RMON_R_OVERSIZE }, 24728c2ecf20Sopenharmony_ci { "rx_fragment", RMON_R_FRAG }, 24738c2ecf20Sopenharmony_ci { "rx_jabber", RMON_R_JAB }, 24748c2ecf20Sopenharmony_ci { "rx_64byte", RMON_R_P64 }, 24758c2ecf20Sopenharmony_ci { "rx_65to127byte", RMON_R_P65TO127 }, 24768c2ecf20Sopenharmony_ci { "rx_128to255byte", RMON_R_P128TO255 }, 24778c2ecf20Sopenharmony_ci { "rx_256to511byte", RMON_R_P256TO511 }, 24788c2ecf20Sopenharmony_ci { "rx_512to1023byte", RMON_R_P512TO1023 }, 24798c2ecf20Sopenharmony_ci { "rx_1024to2047byte", RMON_R_P1024TO2047 }, 24808c2ecf20Sopenharmony_ci { "rx_GTE2048byte", RMON_R_P_GTE2048 }, 24818c2ecf20Sopenharmony_ci { "rx_octets", RMON_R_OCTETS }, 24828c2ecf20Sopenharmony_ci 24838c2ecf20Sopenharmony_ci /* IEEE RX */ 24848c2ecf20Sopenharmony_ci { "IEEE_rx_drop", IEEE_R_DROP }, 24858c2ecf20Sopenharmony_ci { "IEEE_rx_frame_ok", IEEE_R_FRAME_OK }, 24868c2ecf20Sopenharmony_ci { "IEEE_rx_crc", IEEE_R_CRC }, 24878c2ecf20Sopenharmony_ci { "IEEE_rx_align", IEEE_R_ALIGN }, 24888c2ecf20Sopenharmony_ci { "IEEE_rx_macerr", IEEE_R_MACERR }, 24898c2ecf20Sopenharmony_ci { "IEEE_rx_fdxfc", IEEE_R_FDXFC }, 24908c2ecf20Sopenharmony_ci { "IEEE_rx_octets_ok", IEEE_R_OCTETS_OK }, 24918c2ecf20Sopenharmony_ci}; 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_ci#define FEC_STATS_SIZE (ARRAY_SIZE(fec_stats) * sizeof(u64)) 24948c2ecf20Sopenharmony_ci 24958c2ecf20Sopenharmony_cistatic void fec_enet_update_ethtool_stats(struct net_device *dev) 24968c2ecf20Sopenharmony_ci{ 24978c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(dev); 24988c2ecf20Sopenharmony_ci int i; 24998c2ecf20Sopenharmony_ci 25008c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 25018c2ecf20Sopenharmony_ci fep->ethtool_stats[i] = readl(fep->hwp + fec_stats[i].offset); 25028c2ecf20Sopenharmony_ci} 25038c2ecf20Sopenharmony_ci 25048c2ecf20Sopenharmony_cistatic void fec_enet_get_ethtool_stats(struct net_device *dev, 25058c2ecf20Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 25068c2ecf20Sopenharmony_ci{ 25078c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(dev); 25088c2ecf20Sopenharmony_ci 25098c2ecf20Sopenharmony_ci if (netif_running(dev)) 25108c2ecf20Sopenharmony_ci fec_enet_update_ethtool_stats(dev); 25118c2ecf20Sopenharmony_ci 25128c2ecf20Sopenharmony_ci memcpy(data, fep->ethtool_stats, FEC_STATS_SIZE); 25138c2ecf20Sopenharmony_ci} 25148c2ecf20Sopenharmony_ci 25158c2ecf20Sopenharmony_cistatic void fec_enet_get_strings(struct net_device *netdev, 25168c2ecf20Sopenharmony_ci u32 stringset, u8 *data) 25178c2ecf20Sopenharmony_ci{ 25188c2ecf20Sopenharmony_ci int i; 25198c2ecf20Sopenharmony_ci switch (stringset) { 25208c2ecf20Sopenharmony_ci case ETH_SS_STATS: 25218c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 25228c2ecf20Sopenharmony_ci memcpy(data + i * ETH_GSTRING_LEN, 25238c2ecf20Sopenharmony_ci fec_stats[i].name, ETH_GSTRING_LEN); 25248c2ecf20Sopenharmony_ci break; 25258c2ecf20Sopenharmony_ci } 25268c2ecf20Sopenharmony_ci} 25278c2ecf20Sopenharmony_ci 25288c2ecf20Sopenharmony_cistatic int fec_enet_get_sset_count(struct net_device *dev, int sset) 25298c2ecf20Sopenharmony_ci{ 25308c2ecf20Sopenharmony_ci switch (sset) { 25318c2ecf20Sopenharmony_ci case ETH_SS_STATS: 25328c2ecf20Sopenharmony_ci return ARRAY_SIZE(fec_stats); 25338c2ecf20Sopenharmony_ci default: 25348c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 25358c2ecf20Sopenharmony_ci } 25368c2ecf20Sopenharmony_ci} 25378c2ecf20Sopenharmony_ci 25388c2ecf20Sopenharmony_cistatic void fec_enet_clear_ethtool_stats(struct net_device *dev) 25398c2ecf20Sopenharmony_ci{ 25408c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(dev); 25418c2ecf20Sopenharmony_ci int i; 25428c2ecf20Sopenharmony_ci 25438c2ecf20Sopenharmony_ci /* Disable MIB statistics counters */ 25448c2ecf20Sopenharmony_ci writel(FEC_MIB_CTRLSTAT_DISABLE, fep->hwp + FEC_MIB_CTRLSTAT); 25458c2ecf20Sopenharmony_ci 25468c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 25478c2ecf20Sopenharmony_ci writel(0, fep->hwp + fec_stats[i].offset); 25488c2ecf20Sopenharmony_ci 25498c2ecf20Sopenharmony_ci /* Don't disable MIB statistics counters */ 25508c2ecf20Sopenharmony_ci writel(0, fep->hwp + FEC_MIB_CTRLSTAT); 25518c2ecf20Sopenharmony_ci} 25528c2ecf20Sopenharmony_ci 25538c2ecf20Sopenharmony_ci#else /* !defined(CONFIG_M5272) */ 25548c2ecf20Sopenharmony_ci#define FEC_STATS_SIZE 0 25558c2ecf20Sopenharmony_cistatic inline void fec_enet_update_ethtool_stats(struct net_device *dev) 25568c2ecf20Sopenharmony_ci{ 25578c2ecf20Sopenharmony_ci} 25588c2ecf20Sopenharmony_ci 25598c2ecf20Sopenharmony_cistatic inline void fec_enet_clear_ethtool_stats(struct net_device *dev) 25608c2ecf20Sopenharmony_ci{ 25618c2ecf20Sopenharmony_ci} 25628c2ecf20Sopenharmony_ci#endif /* !defined(CONFIG_M5272) */ 25638c2ecf20Sopenharmony_ci 25648c2ecf20Sopenharmony_ci/* ITR clock source is enet system clock (clk_ahb). 25658c2ecf20Sopenharmony_ci * TCTT unit is cycle_ns * 64 cycle 25668c2ecf20Sopenharmony_ci * So, the ICTT value = X us / (cycle_ns * 64) 25678c2ecf20Sopenharmony_ci */ 25688c2ecf20Sopenharmony_cistatic int fec_enet_us_to_itr_clock(struct net_device *ndev, int us) 25698c2ecf20Sopenharmony_ci{ 25708c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 25718c2ecf20Sopenharmony_ci 25728c2ecf20Sopenharmony_ci return us * (fep->itr_clk_rate / 64000) / 1000; 25738c2ecf20Sopenharmony_ci} 25748c2ecf20Sopenharmony_ci 25758c2ecf20Sopenharmony_ci/* Set threshold for interrupt coalescing */ 25768c2ecf20Sopenharmony_cistatic void fec_enet_itr_coal_set(struct net_device *ndev) 25778c2ecf20Sopenharmony_ci{ 25788c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 25798c2ecf20Sopenharmony_ci int rx_itr, tx_itr; 25808c2ecf20Sopenharmony_ci 25818c2ecf20Sopenharmony_ci /* Must be greater than zero to avoid unpredictable behavior */ 25828c2ecf20Sopenharmony_ci if (!fep->rx_time_itr || !fep->rx_pkts_itr || 25838c2ecf20Sopenharmony_ci !fep->tx_time_itr || !fep->tx_pkts_itr) 25848c2ecf20Sopenharmony_ci return; 25858c2ecf20Sopenharmony_ci 25868c2ecf20Sopenharmony_ci /* Select enet system clock as Interrupt Coalescing 25878c2ecf20Sopenharmony_ci * timer Clock Source 25888c2ecf20Sopenharmony_ci */ 25898c2ecf20Sopenharmony_ci rx_itr = FEC_ITR_CLK_SEL; 25908c2ecf20Sopenharmony_ci tx_itr = FEC_ITR_CLK_SEL; 25918c2ecf20Sopenharmony_ci 25928c2ecf20Sopenharmony_ci /* set ICFT and ICTT */ 25938c2ecf20Sopenharmony_ci rx_itr |= FEC_ITR_ICFT(fep->rx_pkts_itr); 25948c2ecf20Sopenharmony_ci rx_itr |= FEC_ITR_ICTT(fec_enet_us_to_itr_clock(ndev, fep->rx_time_itr)); 25958c2ecf20Sopenharmony_ci tx_itr |= FEC_ITR_ICFT(fep->tx_pkts_itr); 25968c2ecf20Sopenharmony_ci tx_itr |= FEC_ITR_ICTT(fec_enet_us_to_itr_clock(ndev, fep->tx_time_itr)); 25978c2ecf20Sopenharmony_ci 25988c2ecf20Sopenharmony_ci rx_itr |= FEC_ITR_EN; 25998c2ecf20Sopenharmony_ci tx_itr |= FEC_ITR_EN; 26008c2ecf20Sopenharmony_ci 26018c2ecf20Sopenharmony_ci writel(tx_itr, fep->hwp + FEC_TXIC0); 26028c2ecf20Sopenharmony_ci writel(rx_itr, fep->hwp + FEC_RXIC0); 26038c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_AVB) { 26048c2ecf20Sopenharmony_ci writel(tx_itr, fep->hwp + FEC_TXIC1); 26058c2ecf20Sopenharmony_ci writel(rx_itr, fep->hwp + FEC_RXIC1); 26068c2ecf20Sopenharmony_ci writel(tx_itr, fep->hwp + FEC_TXIC2); 26078c2ecf20Sopenharmony_ci writel(rx_itr, fep->hwp + FEC_RXIC2); 26088c2ecf20Sopenharmony_ci } 26098c2ecf20Sopenharmony_ci} 26108c2ecf20Sopenharmony_ci 26118c2ecf20Sopenharmony_cistatic int 26128c2ecf20Sopenharmony_cifec_enet_get_coalesce(struct net_device *ndev, struct ethtool_coalesce *ec) 26138c2ecf20Sopenharmony_ci{ 26148c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 26158c2ecf20Sopenharmony_ci 26168c2ecf20Sopenharmony_ci if (!(fep->quirks & FEC_QUIRK_HAS_COALESCE)) 26178c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 26188c2ecf20Sopenharmony_ci 26198c2ecf20Sopenharmony_ci ec->rx_coalesce_usecs = fep->rx_time_itr; 26208c2ecf20Sopenharmony_ci ec->rx_max_coalesced_frames = fep->rx_pkts_itr; 26218c2ecf20Sopenharmony_ci 26228c2ecf20Sopenharmony_ci ec->tx_coalesce_usecs = fep->tx_time_itr; 26238c2ecf20Sopenharmony_ci ec->tx_max_coalesced_frames = fep->tx_pkts_itr; 26248c2ecf20Sopenharmony_ci 26258c2ecf20Sopenharmony_ci return 0; 26268c2ecf20Sopenharmony_ci} 26278c2ecf20Sopenharmony_ci 26288c2ecf20Sopenharmony_cistatic int 26298c2ecf20Sopenharmony_cifec_enet_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *ec) 26308c2ecf20Sopenharmony_ci{ 26318c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 26328c2ecf20Sopenharmony_ci struct device *dev = &fep->pdev->dev; 26338c2ecf20Sopenharmony_ci unsigned int cycle; 26348c2ecf20Sopenharmony_ci 26358c2ecf20Sopenharmony_ci if (!(fep->quirks & FEC_QUIRK_HAS_COALESCE)) 26368c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 26378c2ecf20Sopenharmony_ci 26388c2ecf20Sopenharmony_ci if (ec->rx_max_coalesced_frames > 255) { 26398c2ecf20Sopenharmony_ci dev_err(dev, "Rx coalesced frames exceed hardware limitation\n"); 26408c2ecf20Sopenharmony_ci return -EINVAL; 26418c2ecf20Sopenharmony_ci } 26428c2ecf20Sopenharmony_ci 26438c2ecf20Sopenharmony_ci if (ec->tx_max_coalesced_frames > 255) { 26448c2ecf20Sopenharmony_ci dev_err(dev, "Tx coalesced frame exceed hardware limitation\n"); 26458c2ecf20Sopenharmony_ci return -EINVAL; 26468c2ecf20Sopenharmony_ci } 26478c2ecf20Sopenharmony_ci 26488c2ecf20Sopenharmony_ci cycle = fec_enet_us_to_itr_clock(ndev, ec->rx_coalesce_usecs); 26498c2ecf20Sopenharmony_ci if (cycle > 0xFFFF) { 26508c2ecf20Sopenharmony_ci dev_err(dev, "Rx coalesced usec exceed hardware limitation\n"); 26518c2ecf20Sopenharmony_ci return -EINVAL; 26528c2ecf20Sopenharmony_ci } 26538c2ecf20Sopenharmony_ci 26548c2ecf20Sopenharmony_ci cycle = fec_enet_us_to_itr_clock(ndev, ec->tx_coalesce_usecs); 26558c2ecf20Sopenharmony_ci if (cycle > 0xFFFF) { 26568c2ecf20Sopenharmony_ci dev_err(dev, "Tx coalesced usec exceed hardware limitation\n"); 26578c2ecf20Sopenharmony_ci return -EINVAL; 26588c2ecf20Sopenharmony_ci } 26598c2ecf20Sopenharmony_ci 26608c2ecf20Sopenharmony_ci fep->rx_time_itr = ec->rx_coalesce_usecs; 26618c2ecf20Sopenharmony_ci fep->rx_pkts_itr = ec->rx_max_coalesced_frames; 26628c2ecf20Sopenharmony_ci 26638c2ecf20Sopenharmony_ci fep->tx_time_itr = ec->tx_coalesce_usecs; 26648c2ecf20Sopenharmony_ci fep->tx_pkts_itr = ec->tx_max_coalesced_frames; 26658c2ecf20Sopenharmony_ci 26668c2ecf20Sopenharmony_ci fec_enet_itr_coal_set(ndev); 26678c2ecf20Sopenharmony_ci 26688c2ecf20Sopenharmony_ci return 0; 26698c2ecf20Sopenharmony_ci} 26708c2ecf20Sopenharmony_ci 26718c2ecf20Sopenharmony_cistatic void fec_enet_itr_coal_init(struct net_device *ndev) 26728c2ecf20Sopenharmony_ci{ 26738c2ecf20Sopenharmony_ci struct ethtool_coalesce ec; 26748c2ecf20Sopenharmony_ci 26758c2ecf20Sopenharmony_ci ec.rx_coalesce_usecs = FEC_ITR_ICTT_DEFAULT; 26768c2ecf20Sopenharmony_ci ec.rx_max_coalesced_frames = FEC_ITR_ICFT_DEFAULT; 26778c2ecf20Sopenharmony_ci 26788c2ecf20Sopenharmony_ci ec.tx_coalesce_usecs = FEC_ITR_ICTT_DEFAULT; 26798c2ecf20Sopenharmony_ci ec.tx_max_coalesced_frames = FEC_ITR_ICFT_DEFAULT; 26808c2ecf20Sopenharmony_ci 26818c2ecf20Sopenharmony_ci fec_enet_set_coalesce(ndev, &ec); 26828c2ecf20Sopenharmony_ci} 26838c2ecf20Sopenharmony_ci 26848c2ecf20Sopenharmony_cistatic int fec_enet_get_tunable(struct net_device *netdev, 26858c2ecf20Sopenharmony_ci const struct ethtool_tunable *tuna, 26868c2ecf20Sopenharmony_ci void *data) 26878c2ecf20Sopenharmony_ci{ 26888c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(netdev); 26898c2ecf20Sopenharmony_ci int ret = 0; 26908c2ecf20Sopenharmony_ci 26918c2ecf20Sopenharmony_ci switch (tuna->id) { 26928c2ecf20Sopenharmony_ci case ETHTOOL_RX_COPYBREAK: 26938c2ecf20Sopenharmony_ci *(u32 *)data = fep->rx_copybreak; 26948c2ecf20Sopenharmony_ci break; 26958c2ecf20Sopenharmony_ci default: 26968c2ecf20Sopenharmony_ci ret = -EINVAL; 26978c2ecf20Sopenharmony_ci break; 26988c2ecf20Sopenharmony_ci } 26998c2ecf20Sopenharmony_ci 27008c2ecf20Sopenharmony_ci return ret; 27018c2ecf20Sopenharmony_ci} 27028c2ecf20Sopenharmony_ci 27038c2ecf20Sopenharmony_cistatic int fec_enet_set_tunable(struct net_device *netdev, 27048c2ecf20Sopenharmony_ci const struct ethtool_tunable *tuna, 27058c2ecf20Sopenharmony_ci const void *data) 27068c2ecf20Sopenharmony_ci{ 27078c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(netdev); 27088c2ecf20Sopenharmony_ci int ret = 0; 27098c2ecf20Sopenharmony_ci 27108c2ecf20Sopenharmony_ci switch (tuna->id) { 27118c2ecf20Sopenharmony_ci case ETHTOOL_RX_COPYBREAK: 27128c2ecf20Sopenharmony_ci fep->rx_copybreak = *(u32 *)data; 27138c2ecf20Sopenharmony_ci break; 27148c2ecf20Sopenharmony_ci default: 27158c2ecf20Sopenharmony_ci ret = -EINVAL; 27168c2ecf20Sopenharmony_ci break; 27178c2ecf20Sopenharmony_ci } 27188c2ecf20Sopenharmony_ci 27198c2ecf20Sopenharmony_ci return ret; 27208c2ecf20Sopenharmony_ci} 27218c2ecf20Sopenharmony_ci 27228c2ecf20Sopenharmony_cistatic void 27238c2ecf20Sopenharmony_cifec_enet_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) 27248c2ecf20Sopenharmony_ci{ 27258c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 27268c2ecf20Sopenharmony_ci 27278c2ecf20Sopenharmony_ci if (fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET) { 27288c2ecf20Sopenharmony_ci wol->supported = WAKE_MAGIC; 27298c2ecf20Sopenharmony_ci wol->wolopts = fep->wol_flag & FEC_WOL_FLAG_ENABLE ? WAKE_MAGIC : 0; 27308c2ecf20Sopenharmony_ci } else { 27318c2ecf20Sopenharmony_ci wol->supported = wol->wolopts = 0; 27328c2ecf20Sopenharmony_ci } 27338c2ecf20Sopenharmony_ci} 27348c2ecf20Sopenharmony_ci 27358c2ecf20Sopenharmony_cistatic int 27368c2ecf20Sopenharmony_cifec_enet_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) 27378c2ecf20Sopenharmony_ci{ 27388c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 27398c2ecf20Sopenharmony_ci 27408c2ecf20Sopenharmony_ci if (!(fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET)) 27418c2ecf20Sopenharmony_ci return -EINVAL; 27428c2ecf20Sopenharmony_ci 27438c2ecf20Sopenharmony_ci if (wol->wolopts & ~WAKE_MAGIC) 27448c2ecf20Sopenharmony_ci return -EINVAL; 27458c2ecf20Sopenharmony_ci 27468c2ecf20Sopenharmony_ci device_set_wakeup_enable(&ndev->dev, wol->wolopts & WAKE_MAGIC); 27478c2ecf20Sopenharmony_ci if (device_may_wakeup(&ndev->dev)) { 27488c2ecf20Sopenharmony_ci fep->wol_flag |= FEC_WOL_FLAG_ENABLE; 27498c2ecf20Sopenharmony_ci if (fep->irq[0] > 0) 27508c2ecf20Sopenharmony_ci enable_irq_wake(fep->irq[0]); 27518c2ecf20Sopenharmony_ci } else { 27528c2ecf20Sopenharmony_ci fep->wol_flag &= (~FEC_WOL_FLAG_ENABLE); 27538c2ecf20Sopenharmony_ci if (fep->irq[0] > 0) 27548c2ecf20Sopenharmony_ci disable_irq_wake(fep->irq[0]); 27558c2ecf20Sopenharmony_ci } 27568c2ecf20Sopenharmony_ci 27578c2ecf20Sopenharmony_ci return 0; 27588c2ecf20Sopenharmony_ci} 27598c2ecf20Sopenharmony_ci 27608c2ecf20Sopenharmony_cistatic const struct ethtool_ops fec_enet_ethtool_ops = { 27618c2ecf20Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_USECS | 27628c2ecf20Sopenharmony_ci ETHTOOL_COALESCE_MAX_FRAMES, 27638c2ecf20Sopenharmony_ci .get_drvinfo = fec_enet_get_drvinfo, 27648c2ecf20Sopenharmony_ci .get_regs_len = fec_enet_get_regs_len, 27658c2ecf20Sopenharmony_ci .get_regs = fec_enet_get_regs, 27668c2ecf20Sopenharmony_ci .nway_reset = phy_ethtool_nway_reset, 27678c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 27688c2ecf20Sopenharmony_ci .get_coalesce = fec_enet_get_coalesce, 27698c2ecf20Sopenharmony_ci .set_coalesce = fec_enet_set_coalesce, 27708c2ecf20Sopenharmony_ci#ifndef CONFIG_M5272 27718c2ecf20Sopenharmony_ci .get_pauseparam = fec_enet_get_pauseparam, 27728c2ecf20Sopenharmony_ci .set_pauseparam = fec_enet_set_pauseparam, 27738c2ecf20Sopenharmony_ci .get_strings = fec_enet_get_strings, 27748c2ecf20Sopenharmony_ci .get_ethtool_stats = fec_enet_get_ethtool_stats, 27758c2ecf20Sopenharmony_ci .get_sset_count = fec_enet_get_sset_count, 27768c2ecf20Sopenharmony_ci#endif 27778c2ecf20Sopenharmony_ci .get_ts_info = fec_enet_get_ts_info, 27788c2ecf20Sopenharmony_ci .get_tunable = fec_enet_get_tunable, 27798c2ecf20Sopenharmony_ci .set_tunable = fec_enet_set_tunable, 27808c2ecf20Sopenharmony_ci .get_wol = fec_enet_get_wol, 27818c2ecf20Sopenharmony_ci .set_wol = fec_enet_set_wol, 27828c2ecf20Sopenharmony_ci .get_link_ksettings = phy_ethtool_get_link_ksettings, 27838c2ecf20Sopenharmony_ci .set_link_ksettings = phy_ethtool_set_link_ksettings, 27848c2ecf20Sopenharmony_ci}; 27858c2ecf20Sopenharmony_ci 27868c2ecf20Sopenharmony_cistatic int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) 27878c2ecf20Sopenharmony_ci{ 27888c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 27898c2ecf20Sopenharmony_ci struct phy_device *phydev = ndev->phydev; 27908c2ecf20Sopenharmony_ci 27918c2ecf20Sopenharmony_ci if (!netif_running(ndev)) 27928c2ecf20Sopenharmony_ci return -EINVAL; 27938c2ecf20Sopenharmony_ci 27948c2ecf20Sopenharmony_ci if (!phydev) 27958c2ecf20Sopenharmony_ci return -ENODEV; 27968c2ecf20Sopenharmony_ci 27978c2ecf20Sopenharmony_ci if (fep->bufdesc_ex) { 27988c2ecf20Sopenharmony_ci bool use_fec_hwts = !phy_has_hwtstamp(phydev); 27998c2ecf20Sopenharmony_ci 28008c2ecf20Sopenharmony_ci if (cmd == SIOCSHWTSTAMP) { 28018c2ecf20Sopenharmony_ci if (use_fec_hwts) 28028c2ecf20Sopenharmony_ci return fec_ptp_set(ndev, rq); 28038c2ecf20Sopenharmony_ci fec_ptp_disable_hwts(ndev); 28048c2ecf20Sopenharmony_ci } else if (cmd == SIOCGHWTSTAMP) { 28058c2ecf20Sopenharmony_ci if (use_fec_hwts) 28068c2ecf20Sopenharmony_ci return fec_ptp_get(ndev, rq); 28078c2ecf20Sopenharmony_ci } 28088c2ecf20Sopenharmony_ci } 28098c2ecf20Sopenharmony_ci 28108c2ecf20Sopenharmony_ci return phy_mii_ioctl(phydev, rq, cmd); 28118c2ecf20Sopenharmony_ci} 28128c2ecf20Sopenharmony_ci 28138c2ecf20Sopenharmony_cistatic void fec_enet_free_buffers(struct net_device *ndev) 28148c2ecf20Sopenharmony_ci{ 28158c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 28168c2ecf20Sopenharmony_ci unsigned int i; 28178c2ecf20Sopenharmony_ci struct sk_buff *skb; 28188c2ecf20Sopenharmony_ci struct bufdesc *bdp; 28198c2ecf20Sopenharmony_ci struct fec_enet_priv_tx_q *txq; 28208c2ecf20Sopenharmony_ci struct fec_enet_priv_rx_q *rxq; 28218c2ecf20Sopenharmony_ci unsigned int q; 28228c2ecf20Sopenharmony_ci 28238c2ecf20Sopenharmony_ci for (q = 0; q < fep->num_rx_queues; q++) { 28248c2ecf20Sopenharmony_ci rxq = fep->rx_queue[q]; 28258c2ecf20Sopenharmony_ci bdp = rxq->bd.base; 28268c2ecf20Sopenharmony_ci for (i = 0; i < rxq->bd.ring_size; i++) { 28278c2ecf20Sopenharmony_ci skb = rxq->rx_skbuff[i]; 28288c2ecf20Sopenharmony_ci rxq->rx_skbuff[i] = NULL; 28298c2ecf20Sopenharmony_ci if (skb) { 28308c2ecf20Sopenharmony_ci dma_unmap_single(&fep->pdev->dev, 28318c2ecf20Sopenharmony_ci fec32_to_cpu(bdp->cbd_bufaddr), 28328c2ecf20Sopenharmony_ci FEC_ENET_RX_FRSIZE - fep->rx_align, 28338c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 28348c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 28358c2ecf20Sopenharmony_ci } 28368c2ecf20Sopenharmony_ci bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 28378c2ecf20Sopenharmony_ci } 28388c2ecf20Sopenharmony_ci } 28398c2ecf20Sopenharmony_ci 28408c2ecf20Sopenharmony_ci for (q = 0; q < fep->num_tx_queues; q++) { 28418c2ecf20Sopenharmony_ci txq = fep->tx_queue[q]; 28428c2ecf20Sopenharmony_ci for (i = 0; i < txq->bd.ring_size; i++) { 28438c2ecf20Sopenharmony_ci kfree(txq->tx_bounce[i]); 28448c2ecf20Sopenharmony_ci txq->tx_bounce[i] = NULL; 28458c2ecf20Sopenharmony_ci skb = txq->tx_skbuff[i]; 28468c2ecf20Sopenharmony_ci txq->tx_skbuff[i] = NULL; 28478c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 28488c2ecf20Sopenharmony_ci } 28498c2ecf20Sopenharmony_ci } 28508c2ecf20Sopenharmony_ci} 28518c2ecf20Sopenharmony_ci 28528c2ecf20Sopenharmony_cistatic void fec_enet_free_queue(struct net_device *ndev) 28538c2ecf20Sopenharmony_ci{ 28548c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 28558c2ecf20Sopenharmony_ci int i; 28568c2ecf20Sopenharmony_ci struct fec_enet_priv_tx_q *txq; 28578c2ecf20Sopenharmony_ci 28588c2ecf20Sopenharmony_ci for (i = 0; i < fep->num_tx_queues; i++) 28598c2ecf20Sopenharmony_ci if (fep->tx_queue[i] && fep->tx_queue[i]->tso_hdrs) { 28608c2ecf20Sopenharmony_ci txq = fep->tx_queue[i]; 28618c2ecf20Sopenharmony_ci dma_free_coherent(&fep->pdev->dev, 28628c2ecf20Sopenharmony_ci txq->bd.ring_size * TSO_HEADER_SIZE, 28638c2ecf20Sopenharmony_ci txq->tso_hdrs, 28648c2ecf20Sopenharmony_ci txq->tso_hdrs_dma); 28658c2ecf20Sopenharmony_ci } 28668c2ecf20Sopenharmony_ci 28678c2ecf20Sopenharmony_ci for (i = 0; i < fep->num_rx_queues; i++) 28688c2ecf20Sopenharmony_ci kfree(fep->rx_queue[i]); 28698c2ecf20Sopenharmony_ci for (i = 0; i < fep->num_tx_queues; i++) 28708c2ecf20Sopenharmony_ci kfree(fep->tx_queue[i]); 28718c2ecf20Sopenharmony_ci} 28728c2ecf20Sopenharmony_ci 28738c2ecf20Sopenharmony_cistatic int fec_enet_alloc_queue(struct net_device *ndev) 28748c2ecf20Sopenharmony_ci{ 28758c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 28768c2ecf20Sopenharmony_ci int i; 28778c2ecf20Sopenharmony_ci int ret = 0; 28788c2ecf20Sopenharmony_ci struct fec_enet_priv_tx_q *txq; 28798c2ecf20Sopenharmony_ci 28808c2ecf20Sopenharmony_ci for (i = 0; i < fep->num_tx_queues; i++) { 28818c2ecf20Sopenharmony_ci txq = kzalloc(sizeof(*txq), GFP_KERNEL); 28828c2ecf20Sopenharmony_ci if (!txq) { 28838c2ecf20Sopenharmony_ci ret = -ENOMEM; 28848c2ecf20Sopenharmony_ci goto alloc_failed; 28858c2ecf20Sopenharmony_ci } 28868c2ecf20Sopenharmony_ci 28878c2ecf20Sopenharmony_ci fep->tx_queue[i] = txq; 28888c2ecf20Sopenharmony_ci txq->bd.ring_size = TX_RING_SIZE; 28898c2ecf20Sopenharmony_ci fep->total_tx_ring_size += fep->tx_queue[i]->bd.ring_size; 28908c2ecf20Sopenharmony_ci 28918c2ecf20Sopenharmony_ci txq->tx_stop_threshold = FEC_MAX_SKB_DESCS; 28928c2ecf20Sopenharmony_ci txq->tx_wake_threshold = 28938c2ecf20Sopenharmony_ci (txq->bd.ring_size - txq->tx_stop_threshold) / 2; 28948c2ecf20Sopenharmony_ci 28958c2ecf20Sopenharmony_ci txq->tso_hdrs = dma_alloc_coherent(&fep->pdev->dev, 28968c2ecf20Sopenharmony_ci txq->bd.ring_size * TSO_HEADER_SIZE, 28978c2ecf20Sopenharmony_ci &txq->tso_hdrs_dma, 28988c2ecf20Sopenharmony_ci GFP_KERNEL); 28998c2ecf20Sopenharmony_ci if (!txq->tso_hdrs) { 29008c2ecf20Sopenharmony_ci ret = -ENOMEM; 29018c2ecf20Sopenharmony_ci goto alloc_failed; 29028c2ecf20Sopenharmony_ci } 29038c2ecf20Sopenharmony_ci } 29048c2ecf20Sopenharmony_ci 29058c2ecf20Sopenharmony_ci for (i = 0; i < fep->num_rx_queues; i++) { 29068c2ecf20Sopenharmony_ci fep->rx_queue[i] = kzalloc(sizeof(*fep->rx_queue[i]), 29078c2ecf20Sopenharmony_ci GFP_KERNEL); 29088c2ecf20Sopenharmony_ci if (!fep->rx_queue[i]) { 29098c2ecf20Sopenharmony_ci ret = -ENOMEM; 29108c2ecf20Sopenharmony_ci goto alloc_failed; 29118c2ecf20Sopenharmony_ci } 29128c2ecf20Sopenharmony_ci 29138c2ecf20Sopenharmony_ci fep->rx_queue[i]->bd.ring_size = RX_RING_SIZE; 29148c2ecf20Sopenharmony_ci fep->total_rx_ring_size += fep->rx_queue[i]->bd.ring_size; 29158c2ecf20Sopenharmony_ci } 29168c2ecf20Sopenharmony_ci return ret; 29178c2ecf20Sopenharmony_ci 29188c2ecf20Sopenharmony_cialloc_failed: 29198c2ecf20Sopenharmony_ci fec_enet_free_queue(ndev); 29208c2ecf20Sopenharmony_ci return ret; 29218c2ecf20Sopenharmony_ci} 29228c2ecf20Sopenharmony_ci 29238c2ecf20Sopenharmony_cistatic int 29248c2ecf20Sopenharmony_cifec_enet_alloc_rxq_buffers(struct net_device *ndev, unsigned int queue) 29258c2ecf20Sopenharmony_ci{ 29268c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 29278c2ecf20Sopenharmony_ci unsigned int i; 29288c2ecf20Sopenharmony_ci struct sk_buff *skb; 29298c2ecf20Sopenharmony_ci struct bufdesc *bdp; 29308c2ecf20Sopenharmony_ci struct fec_enet_priv_rx_q *rxq; 29318c2ecf20Sopenharmony_ci 29328c2ecf20Sopenharmony_ci rxq = fep->rx_queue[queue]; 29338c2ecf20Sopenharmony_ci bdp = rxq->bd.base; 29348c2ecf20Sopenharmony_ci for (i = 0; i < rxq->bd.ring_size; i++) { 29358c2ecf20Sopenharmony_ci skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE); 29368c2ecf20Sopenharmony_ci if (!skb) 29378c2ecf20Sopenharmony_ci goto err_alloc; 29388c2ecf20Sopenharmony_ci 29398c2ecf20Sopenharmony_ci if (fec_enet_new_rxbdp(ndev, bdp, skb)) { 29408c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 29418c2ecf20Sopenharmony_ci goto err_alloc; 29428c2ecf20Sopenharmony_ci } 29438c2ecf20Sopenharmony_ci 29448c2ecf20Sopenharmony_ci rxq->rx_skbuff[i] = skb; 29458c2ecf20Sopenharmony_ci bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); 29468c2ecf20Sopenharmony_ci 29478c2ecf20Sopenharmony_ci if (fep->bufdesc_ex) { 29488c2ecf20Sopenharmony_ci struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 29498c2ecf20Sopenharmony_ci ebdp->cbd_esc = cpu_to_fec32(BD_ENET_RX_INT); 29508c2ecf20Sopenharmony_ci } 29518c2ecf20Sopenharmony_ci 29528c2ecf20Sopenharmony_ci bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 29538c2ecf20Sopenharmony_ci } 29548c2ecf20Sopenharmony_ci 29558c2ecf20Sopenharmony_ci /* Set the last buffer to wrap. */ 29568c2ecf20Sopenharmony_ci bdp = fec_enet_get_prevdesc(bdp, &rxq->bd); 29578c2ecf20Sopenharmony_ci bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 29588c2ecf20Sopenharmony_ci return 0; 29598c2ecf20Sopenharmony_ci 29608c2ecf20Sopenharmony_ci err_alloc: 29618c2ecf20Sopenharmony_ci fec_enet_free_buffers(ndev); 29628c2ecf20Sopenharmony_ci return -ENOMEM; 29638c2ecf20Sopenharmony_ci} 29648c2ecf20Sopenharmony_ci 29658c2ecf20Sopenharmony_cistatic int 29668c2ecf20Sopenharmony_cifec_enet_alloc_txq_buffers(struct net_device *ndev, unsigned int queue) 29678c2ecf20Sopenharmony_ci{ 29688c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 29698c2ecf20Sopenharmony_ci unsigned int i; 29708c2ecf20Sopenharmony_ci struct bufdesc *bdp; 29718c2ecf20Sopenharmony_ci struct fec_enet_priv_tx_q *txq; 29728c2ecf20Sopenharmony_ci 29738c2ecf20Sopenharmony_ci txq = fep->tx_queue[queue]; 29748c2ecf20Sopenharmony_ci bdp = txq->bd.base; 29758c2ecf20Sopenharmony_ci for (i = 0; i < txq->bd.ring_size; i++) { 29768c2ecf20Sopenharmony_ci txq->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL); 29778c2ecf20Sopenharmony_ci if (!txq->tx_bounce[i]) 29788c2ecf20Sopenharmony_ci goto err_alloc; 29798c2ecf20Sopenharmony_ci 29808c2ecf20Sopenharmony_ci bdp->cbd_sc = cpu_to_fec16(0); 29818c2ecf20Sopenharmony_ci bdp->cbd_bufaddr = cpu_to_fec32(0); 29828c2ecf20Sopenharmony_ci 29838c2ecf20Sopenharmony_ci if (fep->bufdesc_ex) { 29848c2ecf20Sopenharmony_ci struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 29858c2ecf20Sopenharmony_ci ebdp->cbd_esc = cpu_to_fec32(BD_ENET_TX_INT); 29868c2ecf20Sopenharmony_ci } 29878c2ecf20Sopenharmony_ci 29888c2ecf20Sopenharmony_ci bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 29898c2ecf20Sopenharmony_ci } 29908c2ecf20Sopenharmony_ci 29918c2ecf20Sopenharmony_ci /* Set the last buffer to wrap. */ 29928c2ecf20Sopenharmony_ci bdp = fec_enet_get_prevdesc(bdp, &txq->bd); 29938c2ecf20Sopenharmony_ci bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 29948c2ecf20Sopenharmony_ci 29958c2ecf20Sopenharmony_ci return 0; 29968c2ecf20Sopenharmony_ci 29978c2ecf20Sopenharmony_ci err_alloc: 29988c2ecf20Sopenharmony_ci fec_enet_free_buffers(ndev); 29998c2ecf20Sopenharmony_ci return -ENOMEM; 30008c2ecf20Sopenharmony_ci} 30018c2ecf20Sopenharmony_ci 30028c2ecf20Sopenharmony_cistatic int fec_enet_alloc_buffers(struct net_device *ndev) 30038c2ecf20Sopenharmony_ci{ 30048c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 30058c2ecf20Sopenharmony_ci unsigned int i; 30068c2ecf20Sopenharmony_ci 30078c2ecf20Sopenharmony_ci for (i = 0; i < fep->num_rx_queues; i++) 30088c2ecf20Sopenharmony_ci if (fec_enet_alloc_rxq_buffers(ndev, i)) 30098c2ecf20Sopenharmony_ci return -ENOMEM; 30108c2ecf20Sopenharmony_ci 30118c2ecf20Sopenharmony_ci for (i = 0; i < fep->num_tx_queues; i++) 30128c2ecf20Sopenharmony_ci if (fec_enet_alloc_txq_buffers(ndev, i)) 30138c2ecf20Sopenharmony_ci return -ENOMEM; 30148c2ecf20Sopenharmony_ci return 0; 30158c2ecf20Sopenharmony_ci} 30168c2ecf20Sopenharmony_ci 30178c2ecf20Sopenharmony_cistatic int 30188c2ecf20Sopenharmony_cifec_enet_open(struct net_device *ndev) 30198c2ecf20Sopenharmony_ci{ 30208c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 30218c2ecf20Sopenharmony_ci int ret; 30228c2ecf20Sopenharmony_ci bool reset_again; 30238c2ecf20Sopenharmony_ci 30248c2ecf20Sopenharmony_ci ret = pm_runtime_resume_and_get(&fep->pdev->dev); 30258c2ecf20Sopenharmony_ci if (ret < 0) 30268c2ecf20Sopenharmony_ci return ret; 30278c2ecf20Sopenharmony_ci 30288c2ecf20Sopenharmony_ci pinctrl_pm_select_default_state(&fep->pdev->dev); 30298c2ecf20Sopenharmony_ci ret = fec_enet_clk_enable(ndev, true); 30308c2ecf20Sopenharmony_ci if (ret) 30318c2ecf20Sopenharmony_ci goto clk_enable; 30328c2ecf20Sopenharmony_ci 30338c2ecf20Sopenharmony_ci /* During the first fec_enet_open call the PHY isn't probed at this 30348c2ecf20Sopenharmony_ci * point. Therefore the phy_reset_after_clk_enable() call within 30358c2ecf20Sopenharmony_ci * fec_enet_clk_enable() fails. As we need this reset in order to be 30368c2ecf20Sopenharmony_ci * sure the PHY is working correctly we check if we need to reset again 30378c2ecf20Sopenharmony_ci * later when the PHY is probed 30388c2ecf20Sopenharmony_ci */ 30398c2ecf20Sopenharmony_ci if (ndev->phydev && ndev->phydev->drv) 30408c2ecf20Sopenharmony_ci reset_again = false; 30418c2ecf20Sopenharmony_ci else 30428c2ecf20Sopenharmony_ci reset_again = true; 30438c2ecf20Sopenharmony_ci 30448c2ecf20Sopenharmony_ci /* I should reset the ring buffers here, but I don't yet know 30458c2ecf20Sopenharmony_ci * a simple way to do that. 30468c2ecf20Sopenharmony_ci */ 30478c2ecf20Sopenharmony_ci 30488c2ecf20Sopenharmony_ci ret = fec_enet_alloc_buffers(ndev); 30498c2ecf20Sopenharmony_ci if (ret) 30508c2ecf20Sopenharmony_ci goto err_enet_alloc; 30518c2ecf20Sopenharmony_ci 30528c2ecf20Sopenharmony_ci /* Init MAC prior to mii bus probe */ 30538c2ecf20Sopenharmony_ci fec_restart(ndev); 30548c2ecf20Sopenharmony_ci 30558c2ecf20Sopenharmony_ci /* Call phy_reset_after_clk_enable() again if it failed during 30568c2ecf20Sopenharmony_ci * phy_reset_after_clk_enable() before because the PHY wasn't probed. 30578c2ecf20Sopenharmony_ci */ 30588c2ecf20Sopenharmony_ci if (reset_again) 30598c2ecf20Sopenharmony_ci fec_enet_phy_reset_after_clk_enable(ndev); 30608c2ecf20Sopenharmony_ci 30618c2ecf20Sopenharmony_ci /* Probe and connect to PHY when open the interface */ 30628c2ecf20Sopenharmony_ci ret = fec_enet_mii_probe(ndev); 30638c2ecf20Sopenharmony_ci if (ret) 30648c2ecf20Sopenharmony_ci goto err_enet_mii_probe; 30658c2ecf20Sopenharmony_ci 30668c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_ERR006687) 30678c2ecf20Sopenharmony_ci imx6q_cpuidle_fec_irqs_used(); 30688c2ecf20Sopenharmony_ci 30698c2ecf20Sopenharmony_ci napi_enable(&fep->napi); 30708c2ecf20Sopenharmony_ci phy_start(ndev->phydev); 30718c2ecf20Sopenharmony_ci netif_tx_start_all_queues(ndev); 30728c2ecf20Sopenharmony_ci 30738c2ecf20Sopenharmony_ci device_set_wakeup_enable(&ndev->dev, fep->wol_flag & 30748c2ecf20Sopenharmony_ci FEC_WOL_FLAG_ENABLE); 30758c2ecf20Sopenharmony_ci 30768c2ecf20Sopenharmony_ci return 0; 30778c2ecf20Sopenharmony_ci 30788c2ecf20Sopenharmony_cierr_enet_mii_probe: 30798c2ecf20Sopenharmony_ci fec_enet_free_buffers(ndev); 30808c2ecf20Sopenharmony_cierr_enet_alloc: 30818c2ecf20Sopenharmony_ci fec_enet_clk_enable(ndev, false); 30828c2ecf20Sopenharmony_ciclk_enable: 30838c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(&fep->pdev->dev); 30848c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(&fep->pdev->dev); 30858c2ecf20Sopenharmony_ci pinctrl_pm_select_sleep_state(&fep->pdev->dev); 30868c2ecf20Sopenharmony_ci return ret; 30878c2ecf20Sopenharmony_ci} 30888c2ecf20Sopenharmony_ci 30898c2ecf20Sopenharmony_cistatic int 30908c2ecf20Sopenharmony_cifec_enet_close(struct net_device *ndev) 30918c2ecf20Sopenharmony_ci{ 30928c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 30938c2ecf20Sopenharmony_ci 30948c2ecf20Sopenharmony_ci phy_stop(ndev->phydev); 30958c2ecf20Sopenharmony_ci 30968c2ecf20Sopenharmony_ci if (netif_device_present(ndev)) { 30978c2ecf20Sopenharmony_ci napi_disable(&fep->napi); 30988c2ecf20Sopenharmony_ci netif_tx_disable(ndev); 30998c2ecf20Sopenharmony_ci fec_stop(ndev); 31008c2ecf20Sopenharmony_ci } 31018c2ecf20Sopenharmony_ci 31028c2ecf20Sopenharmony_ci phy_disconnect(ndev->phydev); 31038c2ecf20Sopenharmony_ci 31048c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_ERR006687) 31058c2ecf20Sopenharmony_ci imx6q_cpuidle_fec_irqs_unused(); 31068c2ecf20Sopenharmony_ci 31078c2ecf20Sopenharmony_ci fec_enet_update_ethtool_stats(ndev); 31088c2ecf20Sopenharmony_ci 31098c2ecf20Sopenharmony_ci fec_enet_clk_enable(ndev, false); 31108c2ecf20Sopenharmony_ci pinctrl_pm_select_sleep_state(&fep->pdev->dev); 31118c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(&fep->pdev->dev); 31128c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(&fep->pdev->dev); 31138c2ecf20Sopenharmony_ci 31148c2ecf20Sopenharmony_ci fec_enet_free_buffers(ndev); 31158c2ecf20Sopenharmony_ci 31168c2ecf20Sopenharmony_ci return 0; 31178c2ecf20Sopenharmony_ci} 31188c2ecf20Sopenharmony_ci 31198c2ecf20Sopenharmony_ci/* Set or clear the multicast filter for this adaptor. 31208c2ecf20Sopenharmony_ci * Skeleton taken from sunlance driver. 31218c2ecf20Sopenharmony_ci * The CPM Ethernet implementation allows Multicast as well as individual 31228c2ecf20Sopenharmony_ci * MAC address filtering. Some of the drivers check to make sure it is 31238c2ecf20Sopenharmony_ci * a group multicast address, and discard those that are not. I guess I 31248c2ecf20Sopenharmony_ci * will do the same for now, but just remove the test if you want 31258c2ecf20Sopenharmony_ci * individual filtering as well (do the upper net layers want or support 31268c2ecf20Sopenharmony_ci * this kind of feature?). 31278c2ecf20Sopenharmony_ci */ 31288c2ecf20Sopenharmony_ci 31298c2ecf20Sopenharmony_ci#define FEC_HASH_BITS 6 /* #bits in hash */ 31308c2ecf20Sopenharmony_ci 31318c2ecf20Sopenharmony_cistatic void set_multicast_list(struct net_device *ndev) 31328c2ecf20Sopenharmony_ci{ 31338c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 31348c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 31358c2ecf20Sopenharmony_ci unsigned int crc, tmp; 31368c2ecf20Sopenharmony_ci unsigned char hash; 31378c2ecf20Sopenharmony_ci unsigned int hash_high = 0, hash_low = 0; 31388c2ecf20Sopenharmony_ci 31398c2ecf20Sopenharmony_ci if (ndev->flags & IFF_PROMISC) { 31408c2ecf20Sopenharmony_ci tmp = readl(fep->hwp + FEC_R_CNTRL); 31418c2ecf20Sopenharmony_ci tmp |= 0x8; 31428c2ecf20Sopenharmony_ci writel(tmp, fep->hwp + FEC_R_CNTRL); 31438c2ecf20Sopenharmony_ci return; 31448c2ecf20Sopenharmony_ci } 31458c2ecf20Sopenharmony_ci 31468c2ecf20Sopenharmony_ci tmp = readl(fep->hwp + FEC_R_CNTRL); 31478c2ecf20Sopenharmony_ci tmp &= ~0x8; 31488c2ecf20Sopenharmony_ci writel(tmp, fep->hwp + FEC_R_CNTRL); 31498c2ecf20Sopenharmony_ci 31508c2ecf20Sopenharmony_ci if (ndev->flags & IFF_ALLMULTI) { 31518c2ecf20Sopenharmony_ci /* Catch all multicast addresses, so set the 31528c2ecf20Sopenharmony_ci * filter to all 1's 31538c2ecf20Sopenharmony_ci */ 31548c2ecf20Sopenharmony_ci writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 31558c2ecf20Sopenharmony_ci writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 31568c2ecf20Sopenharmony_ci 31578c2ecf20Sopenharmony_ci return; 31588c2ecf20Sopenharmony_ci } 31598c2ecf20Sopenharmony_ci 31608c2ecf20Sopenharmony_ci /* Add the addresses in hash register */ 31618c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, ndev) { 31628c2ecf20Sopenharmony_ci /* calculate crc32 value of mac address */ 31638c2ecf20Sopenharmony_ci crc = ether_crc_le(ndev->addr_len, ha->addr); 31648c2ecf20Sopenharmony_ci 31658c2ecf20Sopenharmony_ci /* only upper 6 bits (FEC_HASH_BITS) are used 31668c2ecf20Sopenharmony_ci * which point to specific bit in the hash registers 31678c2ecf20Sopenharmony_ci */ 31688c2ecf20Sopenharmony_ci hash = (crc >> (32 - FEC_HASH_BITS)) & 0x3f; 31698c2ecf20Sopenharmony_ci 31708c2ecf20Sopenharmony_ci if (hash > 31) 31718c2ecf20Sopenharmony_ci hash_high |= 1 << (hash - 32); 31728c2ecf20Sopenharmony_ci else 31738c2ecf20Sopenharmony_ci hash_low |= 1 << hash; 31748c2ecf20Sopenharmony_ci } 31758c2ecf20Sopenharmony_ci 31768c2ecf20Sopenharmony_ci writel(hash_high, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 31778c2ecf20Sopenharmony_ci writel(hash_low, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 31788c2ecf20Sopenharmony_ci} 31798c2ecf20Sopenharmony_ci 31808c2ecf20Sopenharmony_ci/* Set a MAC change in hardware. */ 31818c2ecf20Sopenharmony_cistatic int 31828c2ecf20Sopenharmony_cifec_set_mac_address(struct net_device *ndev, void *p) 31838c2ecf20Sopenharmony_ci{ 31848c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 31858c2ecf20Sopenharmony_ci struct sockaddr *addr = p; 31868c2ecf20Sopenharmony_ci 31878c2ecf20Sopenharmony_ci if (addr) { 31888c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 31898c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 31908c2ecf20Sopenharmony_ci memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len); 31918c2ecf20Sopenharmony_ci } 31928c2ecf20Sopenharmony_ci 31938c2ecf20Sopenharmony_ci /* Add netif status check here to avoid system hang in below case: 31948c2ecf20Sopenharmony_ci * ifconfig ethx down; ifconfig ethx hw ether xx:xx:xx:xx:xx:xx; 31958c2ecf20Sopenharmony_ci * After ethx down, fec all clocks are gated off and then register 31968c2ecf20Sopenharmony_ci * access causes system hang. 31978c2ecf20Sopenharmony_ci */ 31988c2ecf20Sopenharmony_ci if (!netif_running(ndev)) 31998c2ecf20Sopenharmony_ci return 0; 32008c2ecf20Sopenharmony_ci 32018c2ecf20Sopenharmony_ci writel(ndev->dev_addr[3] | (ndev->dev_addr[2] << 8) | 32028c2ecf20Sopenharmony_ci (ndev->dev_addr[1] << 16) | (ndev->dev_addr[0] << 24), 32038c2ecf20Sopenharmony_ci fep->hwp + FEC_ADDR_LOW); 32048c2ecf20Sopenharmony_ci writel((ndev->dev_addr[5] << 16) | (ndev->dev_addr[4] << 24), 32058c2ecf20Sopenharmony_ci fep->hwp + FEC_ADDR_HIGH); 32068c2ecf20Sopenharmony_ci return 0; 32078c2ecf20Sopenharmony_ci} 32088c2ecf20Sopenharmony_ci 32098c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 32108c2ecf20Sopenharmony_ci/** 32118c2ecf20Sopenharmony_ci * fec_poll_controller - FEC Poll controller function 32128c2ecf20Sopenharmony_ci * @dev: The FEC network adapter 32138c2ecf20Sopenharmony_ci * 32148c2ecf20Sopenharmony_ci * Polled functionality used by netconsole and others in non interrupt mode 32158c2ecf20Sopenharmony_ci * 32168c2ecf20Sopenharmony_ci */ 32178c2ecf20Sopenharmony_cistatic void fec_poll_controller(struct net_device *dev) 32188c2ecf20Sopenharmony_ci{ 32198c2ecf20Sopenharmony_ci int i; 32208c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(dev); 32218c2ecf20Sopenharmony_ci 32228c2ecf20Sopenharmony_ci for (i = 0; i < FEC_IRQ_NUM; i++) { 32238c2ecf20Sopenharmony_ci if (fep->irq[i] > 0) { 32248c2ecf20Sopenharmony_ci disable_irq(fep->irq[i]); 32258c2ecf20Sopenharmony_ci fec_enet_interrupt(fep->irq[i], dev); 32268c2ecf20Sopenharmony_ci enable_irq(fep->irq[i]); 32278c2ecf20Sopenharmony_ci } 32288c2ecf20Sopenharmony_ci } 32298c2ecf20Sopenharmony_ci} 32308c2ecf20Sopenharmony_ci#endif 32318c2ecf20Sopenharmony_ci 32328c2ecf20Sopenharmony_cistatic inline void fec_enet_set_netdev_features(struct net_device *netdev, 32338c2ecf20Sopenharmony_ci netdev_features_t features) 32348c2ecf20Sopenharmony_ci{ 32358c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(netdev); 32368c2ecf20Sopenharmony_ci netdev_features_t changed = features ^ netdev->features; 32378c2ecf20Sopenharmony_ci 32388c2ecf20Sopenharmony_ci netdev->features = features; 32398c2ecf20Sopenharmony_ci 32408c2ecf20Sopenharmony_ci /* Receive checksum has been changed */ 32418c2ecf20Sopenharmony_ci if (changed & NETIF_F_RXCSUM) { 32428c2ecf20Sopenharmony_ci if (features & NETIF_F_RXCSUM) 32438c2ecf20Sopenharmony_ci fep->csum_flags |= FLAG_RX_CSUM_ENABLED; 32448c2ecf20Sopenharmony_ci else 32458c2ecf20Sopenharmony_ci fep->csum_flags &= ~FLAG_RX_CSUM_ENABLED; 32468c2ecf20Sopenharmony_ci } 32478c2ecf20Sopenharmony_ci} 32488c2ecf20Sopenharmony_ci 32498c2ecf20Sopenharmony_cistatic int fec_set_features(struct net_device *netdev, 32508c2ecf20Sopenharmony_ci netdev_features_t features) 32518c2ecf20Sopenharmony_ci{ 32528c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(netdev); 32538c2ecf20Sopenharmony_ci netdev_features_t changed = features ^ netdev->features; 32548c2ecf20Sopenharmony_ci 32558c2ecf20Sopenharmony_ci if (netif_running(netdev) && changed & NETIF_F_RXCSUM) { 32568c2ecf20Sopenharmony_ci napi_disable(&fep->napi); 32578c2ecf20Sopenharmony_ci netif_tx_lock_bh(netdev); 32588c2ecf20Sopenharmony_ci fec_stop(netdev); 32598c2ecf20Sopenharmony_ci fec_enet_set_netdev_features(netdev, features); 32608c2ecf20Sopenharmony_ci fec_restart(netdev); 32618c2ecf20Sopenharmony_ci netif_tx_wake_all_queues(netdev); 32628c2ecf20Sopenharmony_ci netif_tx_unlock_bh(netdev); 32638c2ecf20Sopenharmony_ci napi_enable(&fep->napi); 32648c2ecf20Sopenharmony_ci } else { 32658c2ecf20Sopenharmony_ci fec_enet_set_netdev_features(netdev, features); 32668c2ecf20Sopenharmony_ci } 32678c2ecf20Sopenharmony_ci 32688c2ecf20Sopenharmony_ci return 0; 32698c2ecf20Sopenharmony_ci} 32708c2ecf20Sopenharmony_ci 32718c2ecf20Sopenharmony_cistatic u16 fec_enet_select_queue(struct net_device *ndev, struct sk_buff *skb, 32728c2ecf20Sopenharmony_ci struct net_device *sb_dev) 32738c2ecf20Sopenharmony_ci{ 32748c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 32758c2ecf20Sopenharmony_ci u16 vlan_tag = 0; 32768c2ecf20Sopenharmony_ci 32778c2ecf20Sopenharmony_ci if (!(fep->quirks & FEC_QUIRK_HAS_AVB)) 32788c2ecf20Sopenharmony_ci return netdev_pick_tx(ndev, skb, NULL); 32798c2ecf20Sopenharmony_ci 32808c2ecf20Sopenharmony_ci /* VLAN is present in the payload.*/ 32818c2ecf20Sopenharmony_ci if (eth_type_vlan(skb->protocol)) { 32828c2ecf20Sopenharmony_ci struct vlan_ethhdr *vhdr = skb_vlan_eth_hdr(skb); 32838c2ecf20Sopenharmony_ci 32848c2ecf20Sopenharmony_ci vlan_tag = ntohs(vhdr->h_vlan_TCI); 32858c2ecf20Sopenharmony_ci /* VLAN is present in the skb but not yet pushed in the payload.*/ 32868c2ecf20Sopenharmony_ci } else if (skb_vlan_tag_present(skb)) { 32878c2ecf20Sopenharmony_ci vlan_tag = skb->vlan_tci; 32888c2ecf20Sopenharmony_ci } else { 32898c2ecf20Sopenharmony_ci return vlan_tag; 32908c2ecf20Sopenharmony_ci } 32918c2ecf20Sopenharmony_ci 32928c2ecf20Sopenharmony_ci return fec_enet_vlan_pri_to_queue[vlan_tag >> 13]; 32938c2ecf20Sopenharmony_ci} 32948c2ecf20Sopenharmony_ci 32958c2ecf20Sopenharmony_cistatic const struct net_device_ops fec_netdev_ops = { 32968c2ecf20Sopenharmony_ci .ndo_open = fec_enet_open, 32978c2ecf20Sopenharmony_ci .ndo_stop = fec_enet_close, 32988c2ecf20Sopenharmony_ci .ndo_start_xmit = fec_enet_start_xmit, 32998c2ecf20Sopenharmony_ci .ndo_select_queue = fec_enet_select_queue, 33008c2ecf20Sopenharmony_ci .ndo_set_rx_mode = set_multicast_list, 33018c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 33028c2ecf20Sopenharmony_ci .ndo_tx_timeout = fec_timeout, 33038c2ecf20Sopenharmony_ci .ndo_set_mac_address = fec_set_mac_address, 33048c2ecf20Sopenharmony_ci .ndo_do_ioctl = fec_enet_ioctl, 33058c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 33068c2ecf20Sopenharmony_ci .ndo_poll_controller = fec_poll_controller, 33078c2ecf20Sopenharmony_ci#endif 33088c2ecf20Sopenharmony_ci .ndo_set_features = fec_set_features, 33098c2ecf20Sopenharmony_ci}; 33108c2ecf20Sopenharmony_ci 33118c2ecf20Sopenharmony_cistatic const unsigned short offset_des_active_rxq[] = { 33128c2ecf20Sopenharmony_ci FEC_R_DES_ACTIVE_0, FEC_R_DES_ACTIVE_1, FEC_R_DES_ACTIVE_2 33138c2ecf20Sopenharmony_ci}; 33148c2ecf20Sopenharmony_ci 33158c2ecf20Sopenharmony_cistatic const unsigned short offset_des_active_txq[] = { 33168c2ecf20Sopenharmony_ci FEC_X_DES_ACTIVE_0, FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2 33178c2ecf20Sopenharmony_ci}; 33188c2ecf20Sopenharmony_ci 33198c2ecf20Sopenharmony_ci /* 33208c2ecf20Sopenharmony_ci * XXX: We need to clean up on failure exits here. 33218c2ecf20Sopenharmony_ci * 33228c2ecf20Sopenharmony_ci */ 33238c2ecf20Sopenharmony_cistatic int fec_enet_init(struct net_device *ndev) 33248c2ecf20Sopenharmony_ci{ 33258c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 33268c2ecf20Sopenharmony_ci struct bufdesc *cbd_base; 33278c2ecf20Sopenharmony_ci dma_addr_t bd_dma; 33288c2ecf20Sopenharmony_ci int bd_size; 33298c2ecf20Sopenharmony_ci unsigned int i; 33308c2ecf20Sopenharmony_ci unsigned dsize = fep->bufdesc_ex ? sizeof(struct bufdesc_ex) : 33318c2ecf20Sopenharmony_ci sizeof(struct bufdesc); 33328c2ecf20Sopenharmony_ci unsigned dsize_log2 = __fls(dsize); 33338c2ecf20Sopenharmony_ci int ret; 33348c2ecf20Sopenharmony_ci 33358c2ecf20Sopenharmony_ci WARN_ON(dsize != (1 << dsize_log2)); 33368c2ecf20Sopenharmony_ci#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) 33378c2ecf20Sopenharmony_ci fep->rx_align = 0xf; 33388c2ecf20Sopenharmony_ci fep->tx_align = 0xf; 33398c2ecf20Sopenharmony_ci#else 33408c2ecf20Sopenharmony_ci fep->rx_align = 0x3; 33418c2ecf20Sopenharmony_ci fep->tx_align = 0x3; 33428c2ecf20Sopenharmony_ci#endif 33438c2ecf20Sopenharmony_ci 33448c2ecf20Sopenharmony_ci /* Check mask of the streaming and coherent API */ 33458c2ecf20Sopenharmony_ci ret = dma_set_mask_and_coherent(&fep->pdev->dev, DMA_BIT_MASK(32)); 33468c2ecf20Sopenharmony_ci if (ret < 0) { 33478c2ecf20Sopenharmony_ci dev_warn(&fep->pdev->dev, "No suitable DMA available\n"); 33488c2ecf20Sopenharmony_ci return ret; 33498c2ecf20Sopenharmony_ci } 33508c2ecf20Sopenharmony_ci 33518c2ecf20Sopenharmony_ci ret = fec_enet_alloc_queue(ndev); 33528c2ecf20Sopenharmony_ci if (ret) 33538c2ecf20Sopenharmony_ci return ret; 33548c2ecf20Sopenharmony_ci 33558c2ecf20Sopenharmony_ci bd_size = (fep->total_tx_ring_size + fep->total_rx_ring_size) * dsize; 33568c2ecf20Sopenharmony_ci 33578c2ecf20Sopenharmony_ci /* Allocate memory for buffer descriptors. */ 33588c2ecf20Sopenharmony_ci cbd_base = dmam_alloc_coherent(&fep->pdev->dev, bd_size, &bd_dma, 33598c2ecf20Sopenharmony_ci GFP_KERNEL); 33608c2ecf20Sopenharmony_ci if (!cbd_base) { 33618c2ecf20Sopenharmony_ci ret = -ENOMEM; 33628c2ecf20Sopenharmony_ci goto free_queue_mem; 33638c2ecf20Sopenharmony_ci } 33648c2ecf20Sopenharmony_ci 33658c2ecf20Sopenharmony_ci /* Get the Ethernet address */ 33668c2ecf20Sopenharmony_ci fec_get_mac(ndev); 33678c2ecf20Sopenharmony_ci /* make sure MAC we just acquired is programmed into the hw */ 33688c2ecf20Sopenharmony_ci fec_set_mac_address(ndev, NULL); 33698c2ecf20Sopenharmony_ci 33708c2ecf20Sopenharmony_ci /* Set receive and transmit descriptor base. */ 33718c2ecf20Sopenharmony_ci for (i = 0; i < fep->num_rx_queues; i++) { 33728c2ecf20Sopenharmony_ci struct fec_enet_priv_rx_q *rxq = fep->rx_queue[i]; 33738c2ecf20Sopenharmony_ci unsigned size = dsize * rxq->bd.ring_size; 33748c2ecf20Sopenharmony_ci 33758c2ecf20Sopenharmony_ci rxq->bd.qid = i; 33768c2ecf20Sopenharmony_ci rxq->bd.base = cbd_base; 33778c2ecf20Sopenharmony_ci rxq->bd.cur = cbd_base; 33788c2ecf20Sopenharmony_ci rxq->bd.dma = bd_dma; 33798c2ecf20Sopenharmony_ci rxq->bd.dsize = dsize; 33808c2ecf20Sopenharmony_ci rxq->bd.dsize_log2 = dsize_log2; 33818c2ecf20Sopenharmony_ci rxq->bd.reg_desc_active = fep->hwp + offset_des_active_rxq[i]; 33828c2ecf20Sopenharmony_ci bd_dma += size; 33838c2ecf20Sopenharmony_ci cbd_base = (struct bufdesc *)(((void *)cbd_base) + size); 33848c2ecf20Sopenharmony_ci rxq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize); 33858c2ecf20Sopenharmony_ci } 33868c2ecf20Sopenharmony_ci 33878c2ecf20Sopenharmony_ci for (i = 0; i < fep->num_tx_queues; i++) { 33888c2ecf20Sopenharmony_ci struct fec_enet_priv_tx_q *txq = fep->tx_queue[i]; 33898c2ecf20Sopenharmony_ci unsigned size = dsize * txq->bd.ring_size; 33908c2ecf20Sopenharmony_ci 33918c2ecf20Sopenharmony_ci txq->bd.qid = i; 33928c2ecf20Sopenharmony_ci txq->bd.base = cbd_base; 33938c2ecf20Sopenharmony_ci txq->bd.cur = cbd_base; 33948c2ecf20Sopenharmony_ci txq->bd.dma = bd_dma; 33958c2ecf20Sopenharmony_ci txq->bd.dsize = dsize; 33968c2ecf20Sopenharmony_ci txq->bd.dsize_log2 = dsize_log2; 33978c2ecf20Sopenharmony_ci txq->bd.reg_desc_active = fep->hwp + offset_des_active_txq[i]; 33988c2ecf20Sopenharmony_ci bd_dma += size; 33998c2ecf20Sopenharmony_ci cbd_base = (struct bufdesc *)(((void *)cbd_base) + size); 34008c2ecf20Sopenharmony_ci txq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize); 34018c2ecf20Sopenharmony_ci } 34028c2ecf20Sopenharmony_ci 34038c2ecf20Sopenharmony_ci 34048c2ecf20Sopenharmony_ci /* The FEC Ethernet specific entries in the device structure */ 34058c2ecf20Sopenharmony_ci ndev->watchdog_timeo = TX_TIMEOUT; 34068c2ecf20Sopenharmony_ci ndev->netdev_ops = &fec_netdev_ops; 34078c2ecf20Sopenharmony_ci ndev->ethtool_ops = &fec_enet_ethtool_ops; 34088c2ecf20Sopenharmony_ci 34098c2ecf20Sopenharmony_ci writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK); 34108c2ecf20Sopenharmony_ci netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, NAPI_POLL_WEIGHT); 34118c2ecf20Sopenharmony_ci 34128c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_VLAN) 34138c2ecf20Sopenharmony_ci /* enable hw VLAN support */ 34148c2ecf20Sopenharmony_ci ndev->features |= NETIF_F_HW_VLAN_CTAG_RX; 34158c2ecf20Sopenharmony_ci 34168c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_CSUM) { 34178c2ecf20Sopenharmony_ci ndev->gso_max_segs = FEC_MAX_TSO_SEGS; 34188c2ecf20Sopenharmony_ci 34198c2ecf20Sopenharmony_ci /* enable hw accelerator */ 34208c2ecf20Sopenharmony_ci ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM 34218c2ecf20Sopenharmony_ci | NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO); 34228c2ecf20Sopenharmony_ci fep->csum_flags |= FLAG_RX_CSUM_ENABLED; 34238c2ecf20Sopenharmony_ci } 34248c2ecf20Sopenharmony_ci 34258c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_AVB) { 34268c2ecf20Sopenharmony_ci fep->tx_align = 0; 34278c2ecf20Sopenharmony_ci fep->rx_align = 0x3f; 34288c2ecf20Sopenharmony_ci } 34298c2ecf20Sopenharmony_ci 34308c2ecf20Sopenharmony_ci ndev->hw_features = ndev->features; 34318c2ecf20Sopenharmony_ci 34328c2ecf20Sopenharmony_ci fec_restart(ndev); 34338c2ecf20Sopenharmony_ci 34348c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_MIB_CLEAR) 34358c2ecf20Sopenharmony_ci fec_enet_clear_ethtool_stats(ndev); 34368c2ecf20Sopenharmony_ci else 34378c2ecf20Sopenharmony_ci fec_enet_update_ethtool_stats(ndev); 34388c2ecf20Sopenharmony_ci 34398c2ecf20Sopenharmony_ci return 0; 34408c2ecf20Sopenharmony_ci 34418c2ecf20Sopenharmony_cifree_queue_mem: 34428c2ecf20Sopenharmony_ci fec_enet_free_queue(ndev); 34438c2ecf20Sopenharmony_ci return ret; 34448c2ecf20Sopenharmony_ci} 34458c2ecf20Sopenharmony_ci 34468c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 34478c2ecf20Sopenharmony_cistatic int fec_reset_phy(struct platform_device *pdev) 34488c2ecf20Sopenharmony_ci{ 34498c2ecf20Sopenharmony_ci int err, phy_reset; 34508c2ecf20Sopenharmony_ci bool active_high = false; 34518c2ecf20Sopenharmony_ci int msec = 1, phy_post_delay = 0; 34528c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 34538c2ecf20Sopenharmony_ci 34548c2ecf20Sopenharmony_ci if (!np) 34558c2ecf20Sopenharmony_ci return 0; 34568c2ecf20Sopenharmony_ci 34578c2ecf20Sopenharmony_ci err = of_property_read_u32(np, "phy-reset-duration", &msec); 34588c2ecf20Sopenharmony_ci /* A sane reset duration should not be longer than 1s */ 34598c2ecf20Sopenharmony_ci if (!err && msec > 1000) 34608c2ecf20Sopenharmony_ci msec = 1; 34618c2ecf20Sopenharmony_ci 34628c2ecf20Sopenharmony_ci phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0); 34638c2ecf20Sopenharmony_ci if (phy_reset == -EPROBE_DEFER) 34648c2ecf20Sopenharmony_ci return phy_reset; 34658c2ecf20Sopenharmony_ci else if (!gpio_is_valid(phy_reset)) 34668c2ecf20Sopenharmony_ci return 0; 34678c2ecf20Sopenharmony_ci 34688c2ecf20Sopenharmony_ci err = of_property_read_u32(np, "phy-reset-post-delay", &phy_post_delay); 34698c2ecf20Sopenharmony_ci /* valid reset duration should be less than 1s */ 34708c2ecf20Sopenharmony_ci if (!err && phy_post_delay > 1000) 34718c2ecf20Sopenharmony_ci return -EINVAL; 34728c2ecf20Sopenharmony_ci 34738c2ecf20Sopenharmony_ci active_high = of_property_read_bool(np, "phy-reset-active-high"); 34748c2ecf20Sopenharmony_ci 34758c2ecf20Sopenharmony_ci err = devm_gpio_request_one(&pdev->dev, phy_reset, 34768c2ecf20Sopenharmony_ci active_high ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, 34778c2ecf20Sopenharmony_ci "phy-reset"); 34788c2ecf20Sopenharmony_ci if (err) { 34798c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err); 34808c2ecf20Sopenharmony_ci return err; 34818c2ecf20Sopenharmony_ci } 34828c2ecf20Sopenharmony_ci 34838c2ecf20Sopenharmony_ci if (msec > 20) 34848c2ecf20Sopenharmony_ci msleep(msec); 34858c2ecf20Sopenharmony_ci else 34868c2ecf20Sopenharmony_ci usleep_range(msec * 1000, msec * 1000 + 1000); 34878c2ecf20Sopenharmony_ci 34888c2ecf20Sopenharmony_ci gpio_set_value_cansleep(phy_reset, !active_high); 34898c2ecf20Sopenharmony_ci 34908c2ecf20Sopenharmony_ci if (!phy_post_delay) 34918c2ecf20Sopenharmony_ci return 0; 34928c2ecf20Sopenharmony_ci 34938c2ecf20Sopenharmony_ci if (phy_post_delay > 20) 34948c2ecf20Sopenharmony_ci msleep(phy_post_delay); 34958c2ecf20Sopenharmony_ci else 34968c2ecf20Sopenharmony_ci usleep_range(phy_post_delay * 1000, 34978c2ecf20Sopenharmony_ci phy_post_delay * 1000 + 1000); 34988c2ecf20Sopenharmony_ci 34998c2ecf20Sopenharmony_ci return 0; 35008c2ecf20Sopenharmony_ci} 35018c2ecf20Sopenharmony_ci#else /* CONFIG_OF */ 35028c2ecf20Sopenharmony_cistatic int fec_reset_phy(struct platform_device *pdev) 35038c2ecf20Sopenharmony_ci{ 35048c2ecf20Sopenharmony_ci /* 35058c2ecf20Sopenharmony_ci * In case of platform probe, the reset has been done 35068c2ecf20Sopenharmony_ci * by machine code. 35078c2ecf20Sopenharmony_ci */ 35088c2ecf20Sopenharmony_ci return 0; 35098c2ecf20Sopenharmony_ci} 35108c2ecf20Sopenharmony_ci#endif /* CONFIG_OF */ 35118c2ecf20Sopenharmony_ci 35128c2ecf20Sopenharmony_cistatic void 35138c2ecf20Sopenharmony_cifec_enet_get_queue_num(struct platform_device *pdev, int *num_tx, int *num_rx) 35148c2ecf20Sopenharmony_ci{ 35158c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 35168c2ecf20Sopenharmony_ci 35178c2ecf20Sopenharmony_ci *num_tx = *num_rx = 1; 35188c2ecf20Sopenharmony_ci 35198c2ecf20Sopenharmony_ci if (!np || !of_device_is_available(np)) 35208c2ecf20Sopenharmony_ci return; 35218c2ecf20Sopenharmony_ci 35228c2ecf20Sopenharmony_ci /* parse the num of tx and rx queues */ 35238c2ecf20Sopenharmony_ci of_property_read_u32(np, "fsl,num-tx-queues", num_tx); 35248c2ecf20Sopenharmony_ci 35258c2ecf20Sopenharmony_ci of_property_read_u32(np, "fsl,num-rx-queues", num_rx); 35268c2ecf20Sopenharmony_ci 35278c2ecf20Sopenharmony_ci if (*num_tx < 1 || *num_tx > FEC_ENET_MAX_TX_QS) { 35288c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "Invalid num_tx(=%d), fall back to 1\n", 35298c2ecf20Sopenharmony_ci *num_tx); 35308c2ecf20Sopenharmony_ci *num_tx = 1; 35318c2ecf20Sopenharmony_ci return; 35328c2ecf20Sopenharmony_ci } 35338c2ecf20Sopenharmony_ci 35348c2ecf20Sopenharmony_ci if (*num_rx < 1 || *num_rx > FEC_ENET_MAX_RX_QS) { 35358c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "Invalid num_rx(=%d), fall back to 1\n", 35368c2ecf20Sopenharmony_ci *num_rx); 35378c2ecf20Sopenharmony_ci *num_rx = 1; 35388c2ecf20Sopenharmony_ci return; 35398c2ecf20Sopenharmony_ci } 35408c2ecf20Sopenharmony_ci 35418c2ecf20Sopenharmony_ci} 35428c2ecf20Sopenharmony_ci 35438c2ecf20Sopenharmony_cistatic int fec_enet_get_irq_cnt(struct platform_device *pdev) 35448c2ecf20Sopenharmony_ci{ 35458c2ecf20Sopenharmony_ci int irq_cnt = platform_irq_count(pdev); 35468c2ecf20Sopenharmony_ci 35478c2ecf20Sopenharmony_ci if (irq_cnt > FEC_IRQ_NUM) 35488c2ecf20Sopenharmony_ci irq_cnt = FEC_IRQ_NUM; /* last for pps */ 35498c2ecf20Sopenharmony_ci else if (irq_cnt == 2) 35508c2ecf20Sopenharmony_ci irq_cnt = 1; /* last for pps */ 35518c2ecf20Sopenharmony_ci else if (irq_cnt <= 0) 35528c2ecf20Sopenharmony_ci irq_cnt = 1; /* At least 1 irq is needed */ 35538c2ecf20Sopenharmony_ci return irq_cnt; 35548c2ecf20Sopenharmony_ci} 35558c2ecf20Sopenharmony_ci 35568c2ecf20Sopenharmony_cistatic int fec_enet_init_stop_mode(struct fec_enet_private *fep, 35578c2ecf20Sopenharmony_ci struct device_node *np) 35588c2ecf20Sopenharmony_ci{ 35598c2ecf20Sopenharmony_ci struct device_node *gpr_np; 35608c2ecf20Sopenharmony_ci u32 out_val[3]; 35618c2ecf20Sopenharmony_ci int ret = 0; 35628c2ecf20Sopenharmony_ci 35638c2ecf20Sopenharmony_ci gpr_np = of_parse_phandle(np, "fsl,stop-mode", 0); 35648c2ecf20Sopenharmony_ci if (!gpr_np) 35658c2ecf20Sopenharmony_ci return 0; 35668c2ecf20Sopenharmony_ci 35678c2ecf20Sopenharmony_ci ret = of_property_read_u32_array(np, "fsl,stop-mode", out_val, 35688c2ecf20Sopenharmony_ci ARRAY_SIZE(out_val)); 35698c2ecf20Sopenharmony_ci if (ret) { 35708c2ecf20Sopenharmony_ci dev_dbg(&fep->pdev->dev, "no stop mode property\n"); 35718c2ecf20Sopenharmony_ci goto out; 35728c2ecf20Sopenharmony_ci } 35738c2ecf20Sopenharmony_ci 35748c2ecf20Sopenharmony_ci fep->stop_gpr.gpr = syscon_node_to_regmap(gpr_np); 35758c2ecf20Sopenharmony_ci if (IS_ERR(fep->stop_gpr.gpr)) { 35768c2ecf20Sopenharmony_ci dev_err(&fep->pdev->dev, "could not find gpr regmap\n"); 35778c2ecf20Sopenharmony_ci ret = PTR_ERR(fep->stop_gpr.gpr); 35788c2ecf20Sopenharmony_ci fep->stop_gpr.gpr = NULL; 35798c2ecf20Sopenharmony_ci goto out; 35808c2ecf20Sopenharmony_ci } 35818c2ecf20Sopenharmony_ci 35828c2ecf20Sopenharmony_ci fep->stop_gpr.reg = out_val[1]; 35838c2ecf20Sopenharmony_ci fep->stop_gpr.bit = out_val[2]; 35848c2ecf20Sopenharmony_ci 35858c2ecf20Sopenharmony_ciout: 35868c2ecf20Sopenharmony_ci of_node_put(gpr_np); 35878c2ecf20Sopenharmony_ci 35888c2ecf20Sopenharmony_ci return ret; 35898c2ecf20Sopenharmony_ci} 35908c2ecf20Sopenharmony_ci 35918c2ecf20Sopenharmony_cistatic int 35928c2ecf20Sopenharmony_cifec_probe(struct platform_device *pdev) 35938c2ecf20Sopenharmony_ci{ 35948c2ecf20Sopenharmony_ci struct fec_enet_private *fep; 35958c2ecf20Sopenharmony_ci struct fec_platform_data *pdata; 35968c2ecf20Sopenharmony_ci phy_interface_t interface; 35978c2ecf20Sopenharmony_ci struct net_device *ndev; 35988c2ecf20Sopenharmony_ci int i, irq, ret = 0; 35998c2ecf20Sopenharmony_ci const struct of_device_id *of_id; 36008c2ecf20Sopenharmony_ci static int dev_id; 36018c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node, *phy_node; 36028c2ecf20Sopenharmony_ci int num_tx_qs; 36038c2ecf20Sopenharmony_ci int num_rx_qs; 36048c2ecf20Sopenharmony_ci char irq_name[8]; 36058c2ecf20Sopenharmony_ci int irq_cnt; 36068c2ecf20Sopenharmony_ci struct fec_devinfo *dev_info; 36078c2ecf20Sopenharmony_ci 36088c2ecf20Sopenharmony_ci fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs); 36098c2ecf20Sopenharmony_ci 36108c2ecf20Sopenharmony_ci /* Init network device */ 36118c2ecf20Sopenharmony_ci ndev = alloc_etherdev_mqs(sizeof(struct fec_enet_private) + 36128c2ecf20Sopenharmony_ci FEC_STATS_SIZE, num_tx_qs, num_rx_qs); 36138c2ecf20Sopenharmony_ci if (!ndev) 36148c2ecf20Sopenharmony_ci return -ENOMEM; 36158c2ecf20Sopenharmony_ci 36168c2ecf20Sopenharmony_ci SET_NETDEV_DEV(ndev, &pdev->dev); 36178c2ecf20Sopenharmony_ci 36188c2ecf20Sopenharmony_ci /* setup board info structure */ 36198c2ecf20Sopenharmony_ci fep = netdev_priv(ndev); 36208c2ecf20Sopenharmony_ci 36218c2ecf20Sopenharmony_ci of_id = of_match_device(fec_dt_ids, &pdev->dev); 36228c2ecf20Sopenharmony_ci if (of_id) 36238c2ecf20Sopenharmony_ci pdev->id_entry = of_id->data; 36248c2ecf20Sopenharmony_ci dev_info = (struct fec_devinfo *)pdev->id_entry->driver_data; 36258c2ecf20Sopenharmony_ci if (dev_info) 36268c2ecf20Sopenharmony_ci fep->quirks = dev_info->quirks; 36278c2ecf20Sopenharmony_ci 36288c2ecf20Sopenharmony_ci fep->netdev = ndev; 36298c2ecf20Sopenharmony_ci fep->num_rx_queues = num_rx_qs; 36308c2ecf20Sopenharmony_ci fep->num_tx_queues = num_tx_qs; 36318c2ecf20Sopenharmony_ci 36328c2ecf20Sopenharmony_ci#if !defined(CONFIG_M5272) 36338c2ecf20Sopenharmony_ci /* default enable pause frame auto negotiation */ 36348c2ecf20Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_GBIT) 36358c2ecf20Sopenharmony_ci fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG; 36368c2ecf20Sopenharmony_ci#endif 36378c2ecf20Sopenharmony_ci 36388c2ecf20Sopenharmony_ci /* Select default pin state */ 36398c2ecf20Sopenharmony_ci pinctrl_pm_select_default_state(&pdev->dev); 36408c2ecf20Sopenharmony_ci 36418c2ecf20Sopenharmony_ci fep->hwp = devm_platform_ioremap_resource(pdev, 0); 36428c2ecf20Sopenharmony_ci if (IS_ERR(fep->hwp)) { 36438c2ecf20Sopenharmony_ci ret = PTR_ERR(fep->hwp); 36448c2ecf20Sopenharmony_ci goto failed_ioremap; 36458c2ecf20Sopenharmony_ci } 36468c2ecf20Sopenharmony_ci 36478c2ecf20Sopenharmony_ci fep->pdev = pdev; 36488c2ecf20Sopenharmony_ci fep->dev_id = dev_id++; 36498c2ecf20Sopenharmony_ci 36508c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, ndev); 36518c2ecf20Sopenharmony_ci 36528c2ecf20Sopenharmony_ci if ((of_machine_is_compatible("fsl,imx6q") || 36538c2ecf20Sopenharmony_ci of_machine_is_compatible("fsl,imx6dl")) && 36548c2ecf20Sopenharmony_ci !of_property_read_bool(np, "fsl,err006687-workaround-present")) 36558c2ecf20Sopenharmony_ci fep->quirks |= FEC_QUIRK_ERR006687; 36568c2ecf20Sopenharmony_ci 36578c2ecf20Sopenharmony_ci if (of_get_property(np, "fsl,magic-packet", NULL)) 36588c2ecf20Sopenharmony_ci fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET; 36598c2ecf20Sopenharmony_ci 36608c2ecf20Sopenharmony_ci ret = fec_enet_init_stop_mode(fep, np); 36618c2ecf20Sopenharmony_ci if (ret) 36628c2ecf20Sopenharmony_ci goto failed_stop_mode; 36638c2ecf20Sopenharmony_ci 36648c2ecf20Sopenharmony_ci phy_node = of_parse_phandle(np, "phy-handle", 0); 36658c2ecf20Sopenharmony_ci if (!phy_node && of_phy_is_fixed_link(np)) { 36668c2ecf20Sopenharmony_ci ret = of_phy_register_fixed_link(np); 36678c2ecf20Sopenharmony_ci if (ret < 0) { 36688c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 36698c2ecf20Sopenharmony_ci "broken fixed-link specification\n"); 36708c2ecf20Sopenharmony_ci goto failed_phy; 36718c2ecf20Sopenharmony_ci } 36728c2ecf20Sopenharmony_ci phy_node = of_node_get(np); 36738c2ecf20Sopenharmony_ci } 36748c2ecf20Sopenharmony_ci fep->phy_node = phy_node; 36758c2ecf20Sopenharmony_ci 36768c2ecf20Sopenharmony_ci ret = of_get_phy_mode(pdev->dev.of_node, &interface); 36778c2ecf20Sopenharmony_ci if (ret) { 36788c2ecf20Sopenharmony_ci pdata = dev_get_platdata(&pdev->dev); 36798c2ecf20Sopenharmony_ci if (pdata) 36808c2ecf20Sopenharmony_ci fep->phy_interface = pdata->phy; 36818c2ecf20Sopenharmony_ci else 36828c2ecf20Sopenharmony_ci fep->phy_interface = PHY_INTERFACE_MODE_MII; 36838c2ecf20Sopenharmony_ci } else { 36848c2ecf20Sopenharmony_ci fep->phy_interface = interface; 36858c2ecf20Sopenharmony_ci } 36868c2ecf20Sopenharmony_ci 36878c2ecf20Sopenharmony_ci fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); 36888c2ecf20Sopenharmony_ci if (IS_ERR(fep->clk_ipg)) { 36898c2ecf20Sopenharmony_ci ret = PTR_ERR(fep->clk_ipg); 36908c2ecf20Sopenharmony_ci goto failed_clk; 36918c2ecf20Sopenharmony_ci } 36928c2ecf20Sopenharmony_ci 36938c2ecf20Sopenharmony_ci fep->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); 36948c2ecf20Sopenharmony_ci if (IS_ERR(fep->clk_ahb)) { 36958c2ecf20Sopenharmony_ci ret = PTR_ERR(fep->clk_ahb); 36968c2ecf20Sopenharmony_ci goto failed_clk; 36978c2ecf20Sopenharmony_ci } 36988c2ecf20Sopenharmony_ci 36998c2ecf20Sopenharmony_ci fep->itr_clk_rate = clk_get_rate(fep->clk_ahb); 37008c2ecf20Sopenharmony_ci 37018c2ecf20Sopenharmony_ci /* enet_out is optional, depends on board */ 37028c2ecf20Sopenharmony_ci fep->clk_enet_out = devm_clk_get(&pdev->dev, "enet_out"); 37038c2ecf20Sopenharmony_ci if (IS_ERR(fep->clk_enet_out)) 37048c2ecf20Sopenharmony_ci fep->clk_enet_out = NULL; 37058c2ecf20Sopenharmony_ci 37068c2ecf20Sopenharmony_ci fep->ptp_clk_on = false; 37078c2ecf20Sopenharmony_ci mutex_init(&fep->ptp_clk_mutex); 37088c2ecf20Sopenharmony_ci 37098c2ecf20Sopenharmony_ci /* clk_ref is optional, depends on board */ 37108c2ecf20Sopenharmony_ci fep->clk_ref = devm_clk_get(&pdev->dev, "enet_clk_ref"); 37118c2ecf20Sopenharmony_ci if (IS_ERR(fep->clk_ref)) 37128c2ecf20Sopenharmony_ci fep->clk_ref = NULL; 37138c2ecf20Sopenharmony_ci 37148c2ecf20Sopenharmony_ci fep->bufdesc_ex = fep->quirks & FEC_QUIRK_HAS_BUFDESC_EX; 37158c2ecf20Sopenharmony_ci fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp"); 37168c2ecf20Sopenharmony_ci if (IS_ERR(fep->clk_ptp)) { 37178c2ecf20Sopenharmony_ci fep->clk_ptp = NULL; 37188c2ecf20Sopenharmony_ci fep->bufdesc_ex = false; 37198c2ecf20Sopenharmony_ci } 37208c2ecf20Sopenharmony_ci 37218c2ecf20Sopenharmony_ci ret = fec_enet_clk_enable(ndev, true); 37228c2ecf20Sopenharmony_ci if (ret) 37238c2ecf20Sopenharmony_ci goto failed_clk; 37248c2ecf20Sopenharmony_ci 37258c2ecf20Sopenharmony_ci ret = clk_prepare_enable(fep->clk_ipg); 37268c2ecf20Sopenharmony_ci if (ret) 37278c2ecf20Sopenharmony_ci goto failed_clk_ipg; 37288c2ecf20Sopenharmony_ci ret = clk_prepare_enable(fep->clk_ahb); 37298c2ecf20Sopenharmony_ci if (ret) 37308c2ecf20Sopenharmony_ci goto failed_clk_ahb; 37318c2ecf20Sopenharmony_ci 37328c2ecf20Sopenharmony_ci fep->reg_phy = devm_regulator_get_optional(&pdev->dev, "phy"); 37338c2ecf20Sopenharmony_ci if (!IS_ERR(fep->reg_phy)) { 37348c2ecf20Sopenharmony_ci ret = regulator_enable(fep->reg_phy); 37358c2ecf20Sopenharmony_ci if (ret) { 37368c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 37378c2ecf20Sopenharmony_ci "Failed to enable phy regulator: %d\n", ret); 37388c2ecf20Sopenharmony_ci goto failed_regulator; 37398c2ecf20Sopenharmony_ci } 37408c2ecf20Sopenharmony_ci } else { 37418c2ecf20Sopenharmony_ci if (PTR_ERR(fep->reg_phy) == -EPROBE_DEFER) { 37428c2ecf20Sopenharmony_ci ret = -EPROBE_DEFER; 37438c2ecf20Sopenharmony_ci goto failed_regulator; 37448c2ecf20Sopenharmony_ci } 37458c2ecf20Sopenharmony_ci fep->reg_phy = NULL; 37468c2ecf20Sopenharmony_ci } 37478c2ecf20Sopenharmony_ci 37488c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(&pdev->dev, FEC_MDIO_PM_TIMEOUT); 37498c2ecf20Sopenharmony_ci pm_runtime_use_autosuspend(&pdev->dev); 37508c2ecf20Sopenharmony_ci pm_runtime_get_noresume(&pdev->dev); 37518c2ecf20Sopenharmony_ci pm_runtime_set_active(&pdev->dev); 37528c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 37538c2ecf20Sopenharmony_ci 37548c2ecf20Sopenharmony_ci ret = fec_reset_phy(pdev); 37558c2ecf20Sopenharmony_ci if (ret) 37568c2ecf20Sopenharmony_ci goto failed_reset; 37578c2ecf20Sopenharmony_ci 37588c2ecf20Sopenharmony_ci irq_cnt = fec_enet_get_irq_cnt(pdev); 37598c2ecf20Sopenharmony_ci if (fep->bufdesc_ex) 37608c2ecf20Sopenharmony_ci fec_ptp_init(pdev, irq_cnt); 37618c2ecf20Sopenharmony_ci 37628c2ecf20Sopenharmony_ci ret = fec_enet_init(ndev); 37638c2ecf20Sopenharmony_ci if (ret) 37648c2ecf20Sopenharmony_ci goto failed_init; 37658c2ecf20Sopenharmony_ci 37668c2ecf20Sopenharmony_ci for (i = 0; i < irq_cnt; i++) { 37678c2ecf20Sopenharmony_ci snprintf(irq_name, sizeof(irq_name), "int%d", i); 37688c2ecf20Sopenharmony_ci irq = platform_get_irq_byname_optional(pdev, irq_name); 37698c2ecf20Sopenharmony_ci if (irq < 0) 37708c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, i); 37718c2ecf20Sopenharmony_ci if (irq < 0) { 37728c2ecf20Sopenharmony_ci ret = irq; 37738c2ecf20Sopenharmony_ci goto failed_irq; 37748c2ecf20Sopenharmony_ci } 37758c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, fec_enet_interrupt, 37768c2ecf20Sopenharmony_ci 0, pdev->name, ndev); 37778c2ecf20Sopenharmony_ci if (ret) 37788c2ecf20Sopenharmony_ci goto failed_irq; 37798c2ecf20Sopenharmony_ci 37808c2ecf20Sopenharmony_ci fep->irq[i] = irq; 37818c2ecf20Sopenharmony_ci } 37828c2ecf20Sopenharmony_ci 37838c2ecf20Sopenharmony_ci ret = fec_enet_mii_init(pdev); 37848c2ecf20Sopenharmony_ci if (ret) 37858c2ecf20Sopenharmony_ci goto failed_mii_init; 37868c2ecf20Sopenharmony_ci 37878c2ecf20Sopenharmony_ci /* Carrier starts down, phylib will bring it up */ 37888c2ecf20Sopenharmony_ci netif_carrier_off(ndev); 37898c2ecf20Sopenharmony_ci fec_enet_clk_enable(ndev, false); 37908c2ecf20Sopenharmony_ci pinctrl_pm_select_sleep_state(&pdev->dev); 37918c2ecf20Sopenharmony_ci 37928c2ecf20Sopenharmony_ci ndev->max_mtu = PKT_MAXBUF_SIZE - ETH_HLEN - ETH_FCS_LEN; 37938c2ecf20Sopenharmony_ci 37948c2ecf20Sopenharmony_ci ret = register_netdev(ndev); 37958c2ecf20Sopenharmony_ci if (ret) 37968c2ecf20Sopenharmony_ci goto failed_register; 37978c2ecf20Sopenharmony_ci 37988c2ecf20Sopenharmony_ci device_init_wakeup(&ndev->dev, fep->wol_flag & 37998c2ecf20Sopenharmony_ci FEC_WOL_HAS_MAGIC_PACKET); 38008c2ecf20Sopenharmony_ci 38018c2ecf20Sopenharmony_ci if (fep->bufdesc_ex && fep->ptp_clock) 38028c2ecf20Sopenharmony_ci netdev_info(ndev, "registered PHC device %d\n", fep->dev_id); 38038c2ecf20Sopenharmony_ci 38048c2ecf20Sopenharmony_ci fep->rx_copybreak = COPYBREAK_DEFAULT; 38058c2ecf20Sopenharmony_ci INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work); 38068c2ecf20Sopenharmony_ci 38078c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(&pdev->dev); 38088c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(&pdev->dev); 38098c2ecf20Sopenharmony_ci 38108c2ecf20Sopenharmony_ci return 0; 38118c2ecf20Sopenharmony_ci 38128c2ecf20Sopenharmony_cifailed_register: 38138c2ecf20Sopenharmony_ci fec_enet_mii_remove(fep); 38148c2ecf20Sopenharmony_cifailed_mii_init: 38158c2ecf20Sopenharmony_cifailed_irq: 38168c2ecf20Sopenharmony_cifailed_init: 38178c2ecf20Sopenharmony_ci fec_ptp_stop(pdev); 38188c2ecf20Sopenharmony_cifailed_reset: 38198c2ecf20Sopenharmony_ci pm_runtime_put_noidle(&pdev->dev); 38208c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 38218c2ecf20Sopenharmony_ci if (fep->reg_phy) 38228c2ecf20Sopenharmony_ci regulator_disable(fep->reg_phy); 38238c2ecf20Sopenharmony_cifailed_regulator: 38248c2ecf20Sopenharmony_ci clk_disable_unprepare(fep->clk_ahb); 38258c2ecf20Sopenharmony_cifailed_clk_ahb: 38268c2ecf20Sopenharmony_ci clk_disable_unprepare(fep->clk_ipg); 38278c2ecf20Sopenharmony_cifailed_clk_ipg: 38288c2ecf20Sopenharmony_ci fec_enet_clk_enable(ndev, false); 38298c2ecf20Sopenharmony_cifailed_clk: 38308c2ecf20Sopenharmony_ci if (of_phy_is_fixed_link(np)) 38318c2ecf20Sopenharmony_ci of_phy_deregister_fixed_link(np); 38328c2ecf20Sopenharmony_ci of_node_put(phy_node); 38338c2ecf20Sopenharmony_cifailed_stop_mode: 38348c2ecf20Sopenharmony_cifailed_phy: 38358c2ecf20Sopenharmony_ci dev_id--; 38368c2ecf20Sopenharmony_cifailed_ioremap: 38378c2ecf20Sopenharmony_ci free_netdev(ndev); 38388c2ecf20Sopenharmony_ci 38398c2ecf20Sopenharmony_ci return ret; 38408c2ecf20Sopenharmony_ci} 38418c2ecf20Sopenharmony_ci 38428c2ecf20Sopenharmony_cistatic int 38438c2ecf20Sopenharmony_cifec_drv_remove(struct platform_device *pdev) 38448c2ecf20Sopenharmony_ci{ 38458c2ecf20Sopenharmony_ci struct net_device *ndev = platform_get_drvdata(pdev); 38468c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 38478c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 38488c2ecf20Sopenharmony_ci int ret; 38498c2ecf20Sopenharmony_ci 38508c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(&pdev->dev); 38518c2ecf20Sopenharmony_ci if (ret < 0) 38528c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 38538c2ecf20Sopenharmony_ci "Failed to resume device in remove callback (%pe)\n", 38548c2ecf20Sopenharmony_ci ERR_PTR(ret)); 38558c2ecf20Sopenharmony_ci 38568c2ecf20Sopenharmony_ci cancel_work_sync(&fep->tx_timeout_work); 38578c2ecf20Sopenharmony_ci fec_ptp_stop(pdev); 38588c2ecf20Sopenharmony_ci unregister_netdev(ndev); 38598c2ecf20Sopenharmony_ci fec_enet_mii_remove(fep); 38608c2ecf20Sopenharmony_ci if (fep->reg_phy) 38618c2ecf20Sopenharmony_ci regulator_disable(fep->reg_phy); 38628c2ecf20Sopenharmony_ci 38638c2ecf20Sopenharmony_ci if (of_phy_is_fixed_link(np)) 38648c2ecf20Sopenharmony_ci of_phy_deregister_fixed_link(np); 38658c2ecf20Sopenharmony_ci of_node_put(fep->phy_node); 38668c2ecf20Sopenharmony_ci 38678c2ecf20Sopenharmony_ci /* After pm_runtime_get_sync() failed, the clks are still off, so skip 38688c2ecf20Sopenharmony_ci * disabling them again. 38698c2ecf20Sopenharmony_ci */ 38708c2ecf20Sopenharmony_ci if (ret >= 0) { 38718c2ecf20Sopenharmony_ci clk_disable_unprepare(fep->clk_ahb); 38728c2ecf20Sopenharmony_ci clk_disable_unprepare(fep->clk_ipg); 38738c2ecf20Sopenharmony_ci } 38748c2ecf20Sopenharmony_ci pm_runtime_put_noidle(&pdev->dev); 38758c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 38768c2ecf20Sopenharmony_ci 38778c2ecf20Sopenharmony_ci free_netdev(ndev); 38788c2ecf20Sopenharmony_ci return 0; 38798c2ecf20Sopenharmony_ci} 38808c2ecf20Sopenharmony_ci 38818c2ecf20Sopenharmony_cistatic int __maybe_unused fec_suspend(struct device *dev) 38828c2ecf20Sopenharmony_ci{ 38838c2ecf20Sopenharmony_ci struct net_device *ndev = dev_get_drvdata(dev); 38848c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 38858c2ecf20Sopenharmony_ci 38868c2ecf20Sopenharmony_ci rtnl_lock(); 38878c2ecf20Sopenharmony_ci if (netif_running(ndev)) { 38888c2ecf20Sopenharmony_ci if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) 38898c2ecf20Sopenharmony_ci fep->wol_flag |= FEC_WOL_FLAG_SLEEP_ON; 38908c2ecf20Sopenharmony_ci phy_stop(ndev->phydev); 38918c2ecf20Sopenharmony_ci napi_disable(&fep->napi); 38928c2ecf20Sopenharmony_ci netif_tx_lock_bh(ndev); 38938c2ecf20Sopenharmony_ci netif_device_detach(ndev); 38948c2ecf20Sopenharmony_ci netif_tx_unlock_bh(ndev); 38958c2ecf20Sopenharmony_ci fec_stop(ndev); 38968c2ecf20Sopenharmony_ci fec_enet_clk_enable(ndev, false); 38978c2ecf20Sopenharmony_ci if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) 38988c2ecf20Sopenharmony_ci pinctrl_pm_select_sleep_state(&fep->pdev->dev); 38998c2ecf20Sopenharmony_ci } 39008c2ecf20Sopenharmony_ci rtnl_unlock(); 39018c2ecf20Sopenharmony_ci 39028c2ecf20Sopenharmony_ci if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) 39038c2ecf20Sopenharmony_ci regulator_disable(fep->reg_phy); 39048c2ecf20Sopenharmony_ci 39058c2ecf20Sopenharmony_ci /* SOC supply clock to phy, when clock is disabled, phy link down 39068c2ecf20Sopenharmony_ci * SOC control phy regulator, when regulator is disabled, phy link down 39078c2ecf20Sopenharmony_ci */ 39088c2ecf20Sopenharmony_ci if (fep->clk_enet_out || fep->reg_phy) 39098c2ecf20Sopenharmony_ci fep->link = 0; 39108c2ecf20Sopenharmony_ci 39118c2ecf20Sopenharmony_ci return 0; 39128c2ecf20Sopenharmony_ci} 39138c2ecf20Sopenharmony_ci 39148c2ecf20Sopenharmony_cistatic int __maybe_unused fec_resume(struct device *dev) 39158c2ecf20Sopenharmony_ci{ 39168c2ecf20Sopenharmony_ci struct net_device *ndev = dev_get_drvdata(dev); 39178c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 39188c2ecf20Sopenharmony_ci int ret; 39198c2ecf20Sopenharmony_ci int val; 39208c2ecf20Sopenharmony_ci 39218c2ecf20Sopenharmony_ci if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) { 39228c2ecf20Sopenharmony_ci ret = regulator_enable(fep->reg_phy); 39238c2ecf20Sopenharmony_ci if (ret) 39248c2ecf20Sopenharmony_ci return ret; 39258c2ecf20Sopenharmony_ci } 39268c2ecf20Sopenharmony_ci 39278c2ecf20Sopenharmony_ci rtnl_lock(); 39288c2ecf20Sopenharmony_ci if (netif_running(ndev)) { 39298c2ecf20Sopenharmony_ci ret = fec_enet_clk_enable(ndev, true); 39308c2ecf20Sopenharmony_ci if (ret) { 39318c2ecf20Sopenharmony_ci rtnl_unlock(); 39328c2ecf20Sopenharmony_ci goto failed_clk; 39338c2ecf20Sopenharmony_ci } 39348c2ecf20Sopenharmony_ci if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) { 39358c2ecf20Sopenharmony_ci fec_enet_stop_mode(fep, false); 39368c2ecf20Sopenharmony_ci 39378c2ecf20Sopenharmony_ci val = readl(fep->hwp + FEC_ECNTRL); 39388c2ecf20Sopenharmony_ci val &= ~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP); 39398c2ecf20Sopenharmony_ci writel(val, fep->hwp + FEC_ECNTRL); 39408c2ecf20Sopenharmony_ci fep->wol_flag &= ~FEC_WOL_FLAG_SLEEP_ON; 39418c2ecf20Sopenharmony_ci } else { 39428c2ecf20Sopenharmony_ci pinctrl_pm_select_default_state(&fep->pdev->dev); 39438c2ecf20Sopenharmony_ci } 39448c2ecf20Sopenharmony_ci fec_restart(ndev); 39458c2ecf20Sopenharmony_ci netif_tx_lock_bh(ndev); 39468c2ecf20Sopenharmony_ci netif_device_attach(ndev); 39478c2ecf20Sopenharmony_ci netif_tx_unlock_bh(ndev); 39488c2ecf20Sopenharmony_ci napi_enable(&fep->napi); 39498c2ecf20Sopenharmony_ci phy_start(ndev->phydev); 39508c2ecf20Sopenharmony_ci } 39518c2ecf20Sopenharmony_ci rtnl_unlock(); 39528c2ecf20Sopenharmony_ci 39538c2ecf20Sopenharmony_ci return 0; 39548c2ecf20Sopenharmony_ci 39558c2ecf20Sopenharmony_cifailed_clk: 39568c2ecf20Sopenharmony_ci if (fep->reg_phy) 39578c2ecf20Sopenharmony_ci regulator_disable(fep->reg_phy); 39588c2ecf20Sopenharmony_ci return ret; 39598c2ecf20Sopenharmony_ci} 39608c2ecf20Sopenharmony_ci 39618c2ecf20Sopenharmony_cistatic int __maybe_unused fec_runtime_suspend(struct device *dev) 39628c2ecf20Sopenharmony_ci{ 39638c2ecf20Sopenharmony_ci struct net_device *ndev = dev_get_drvdata(dev); 39648c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 39658c2ecf20Sopenharmony_ci 39668c2ecf20Sopenharmony_ci clk_disable_unprepare(fep->clk_ahb); 39678c2ecf20Sopenharmony_ci clk_disable_unprepare(fep->clk_ipg); 39688c2ecf20Sopenharmony_ci 39698c2ecf20Sopenharmony_ci return 0; 39708c2ecf20Sopenharmony_ci} 39718c2ecf20Sopenharmony_ci 39728c2ecf20Sopenharmony_cistatic int __maybe_unused fec_runtime_resume(struct device *dev) 39738c2ecf20Sopenharmony_ci{ 39748c2ecf20Sopenharmony_ci struct net_device *ndev = dev_get_drvdata(dev); 39758c2ecf20Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 39768c2ecf20Sopenharmony_ci int ret; 39778c2ecf20Sopenharmony_ci 39788c2ecf20Sopenharmony_ci ret = clk_prepare_enable(fep->clk_ahb); 39798c2ecf20Sopenharmony_ci if (ret) 39808c2ecf20Sopenharmony_ci return ret; 39818c2ecf20Sopenharmony_ci ret = clk_prepare_enable(fep->clk_ipg); 39828c2ecf20Sopenharmony_ci if (ret) 39838c2ecf20Sopenharmony_ci goto failed_clk_ipg; 39848c2ecf20Sopenharmony_ci 39858c2ecf20Sopenharmony_ci return 0; 39868c2ecf20Sopenharmony_ci 39878c2ecf20Sopenharmony_cifailed_clk_ipg: 39888c2ecf20Sopenharmony_ci clk_disable_unprepare(fep->clk_ahb); 39898c2ecf20Sopenharmony_ci return ret; 39908c2ecf20Sopenharmony_ci} 39918c2ecf20Sopenharmony_ci 39928c2ecf20Sopenharmony_cistatic const struct dev_pm_ops fec_pm_ops = { 39938c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(fec_suspend, fec_resume) 39948c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(fec_runtime_suspend, fec_runtime_resume, NULL) 39958c2ecf20Sopenharmony_ci}; 39968c2ecf20Sopenharmony_ci 39978c2ecf20Sopenharmony_cistatic struct platform_driver fec_driver = { 39988c2ecf20Sopenharmony_ci .driver = { 39998c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 40008c2ecf20Sopenharmony_ci .pm = &fec_pm_ops, 40018c2ecf20Sopenharmony_ci .of_match_table = fec_dt_ids, 40028c2ecf20Sopenharmony_ci .suppress_bind_attrs = true, 40038c2ecf20Sopenharmony_ci }, 40048c2ecf20Sopenharmony_ci .id_table = fec_devtype, 40058c2ecf20Sopenharmony_ci .probe = fec_probe, 40068c2ecf20Sopenharmony_ci .remove = fec_drv_remove, 40078c2ecf20Sopenharmony_ci}; 40088c2ecf20Sopenharmony_ci 40098c2ecf20Sopenharmony_cimodule_platform_driver(fec_driver); 40108c2ecf20Sopenharmony_ci 40118c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:"DRIVER_NAME); 40128c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4013