162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Fast Ethernet Controller (FEC) driver for Motorola MPC8xx. 462306a36Sopenharmony_ci * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Right now, I am very wasteful with the buffers. I allocate memory 762306a36Sopenharmony_ci * pages and then divide them into 2K frame buffers. This way I know I 862306a36Sopenharmony_ci * have buffers large enough to hold one frame within one buffer descriptor. 962306a36Sopenharmony_ci * Once I get this working, I will use 64 or 128 byte CPM buffers, which 1062306a36Sopenharmony_ci * will be much more memory efficient and will easily handle lots of 1162306a36Sopenharmony_ci * small packets. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Much better multiple PHY support by Magnus Damm. 1462306a36Sopenharmony_ci * Copyright (c) 2000 Ericsson Radio Systems AB. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * Support for FEC controller of ColdFire processors. 1762306a36Sopenharmony_ci * Copyright (c) 2001-2005 Greg Ungerer (gerg@snapgear.com) 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be) 2062306a36Sopenharmony_ci * Copyright (c) 2004-2006 Macq Electronique SA. 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <linux/module.h> 2662306a36Sopenharmony_ci#include <linux/kernel.h> 2762306a36Sopenharmony_ci#include <linux/string.h> 2862306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2962306a36Sopenharmony_ci#include <linux/ptrace.h> 3062306a36Sopenharmony_ci#include <linux/errno.h> 3162306a36Sopenharmony_ci#include <linux/ioport.h> 3262306a36Sopenharmony_ci#include <linux/slab.h> 3362306a36Sopenharmony_ci#include <linux/interrupt.h> 3462306a36Sopenharmony_ci#include <linux/delay.h> 3562306a36Sopenharmony_ci#include <linux/netdevice.h> 3662306a36Sopenharmony_ci#include <linux/etherdevice.h> 3762306a36Sopenharmony_ci#include <linux/skbuff.h> 3862306a36Sopenharmony_ci#include <linux/in.h> 3962306a36Sopenharmony_ci#include <linux/ip.h> 4062306a36Sopenharmony_ci#include <net/ip.h> 4162306a36Sopenharmony_ci#include <net/page_pool/helpers.h> 4262306a36Sopenharmony_ci#include <net/selftests.h> 4362306a36Sopenharmony_ci#include <net/tso.h> 4462306a36Sopenharmony_ci#include <linux/tcp.h> 4562306a36Sopenharmony_ci#include <linux/udp.h> 4662306a36Sopenharmony_ci#include <linux/icmp.h> 4762306a36Sopenharmony_ci#include <linux/spinlock.h> 4862306a36Sopenharmony_ci#include <linux/workqueue.h> 4962306a36Sopenharmony_ci#include <linux/bitops.h> 5062306a36Sopenharmony_ci#include <linux/io.h> 5162306a36Sopenharmony_ci#include <linux/irq.h> 5262306a36Sopenharmony_ci#include <linux/clk.h> 5362306a36Sopenharmony_ci#include <linux/crc32.h> 5462306a36Sopenharmony_ci#include <linux/platform_device.h> 5562306a36Sopenharmony_ci#include <linux/mdio.h> 5662306a36Sopenharmony_ci#include <linux/phy.h> 5762306a36Sopenharmony_ci#include <linux/fec.h> 5862306a36Sopenharmony_ci#include <linux/of.h> 5962306a36Sopenharmony_ci#include <linux/of_device.h> 6062306a36Sopenharmony_ci#include <linux/of_mdio.h> 6162306a36Sopenharmony_ci#include <linux/of_net.h> 6262306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 6362306a36Sopenharmony_ci#include <linux/if_vlan.h> 6462306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h> 6562306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 6662306a36Sopenharmony_ci#include <linux/prefetch.h> 6762306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 6862306a36Sopenharmony_ci#include <linux/regmap.h> 6962306a36Sopenharmony_ci#include <soc/imx/cpuidle.h> 7062306a36Sopenharmony_ci#include <linux/filter.h> 7162306a36Sopenharmony_ci#include <linux/bpf.h> 7262306a36Sopenharmony_ci#include <linux/bpf_trace.h> 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#include <asm/cacheflush.h> 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#include "fec.h" 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic void set_multicast_list(struct net_device *ndev); 7962306a36Sopenharmony_cistatic void fec_enet_itr_coal_set(struct net_device *ndev); 8062306a36Sopenharmony_cistatic int fec_enet_xdp_tx_xmit(struct fec_enet_private *fep, 8162306a36Sopenharmony_ci int cpu, struct xdp_buff *xdp, 8262306a36Sopenharmony_ci u32 dma_sync_len); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define DRIVER_NAME "fec" 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic const u16 fec_enet_vlan_pri_to_queue[8] = {0, 0, 1, 1, 1, 2, 2, 2}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* Pause frame feild and FIFO threshold */ 8962306a36Sopenharmony_ci#define FEC_ENET_FCE (1 << 5) 9062306a36Sopenharmony_ci#define FEC_ENET_RSEM_V 0x84 9162306a36Sopenharmony_ci#define FEC_ENET_RSFL_V 16 9262306a36Sopenharmony_ci#define FEC_ENET_RAEM_V 0x8 9362306a36Sopenharmony_ci#define FEC_ENET_RAFL_V 0x8 9462306a36Sopenharmony_ci#define FEC_ENET_OPD_V 0xFFF0 9562306a36Sopenharmony_ci#define FEC_MDIO_PM_TIMEOUT 100 /* ms */ 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#define FEC_ENET_XDP_PASS 0 9862306a36Sopenharmony_ci#define FEC_ENET_XDP_CONSUMED BIT(0) 9962306a36Sopenharmony_ci#define FEC_ENET_XDP_TX BIT(1) 10062306a36Sopenharmony_ci#define FEC_ENET_XDP_REDIR BIT(2) 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistruct fec_devinfo { 10362306a36Sopenharmony_ci u32 quirks; 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic const struct fec_devinfo fec_imx25_info = { 10762306a36Sopenharmony_ci .quirks = FEC_QUIRK_USE_GASKET | FEC_QUIRK_MIB_CLEAR | 10862306a36Sopenharmony_ci FEC_QUIRK_HAS_FRREG | FEC_QUIRK_HAS_MDIO_C45, 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic const struct fec_devinfo fec_imx27_info = { 11262306a36Sopenharmony_ci .quirks = FEC_QUIRK_MIB_CLEAR | FEC_QUIRK_HAS_FRREG | 11362306a36Sopenharmony_ci FEC_QUIRK_HAS_MDIO_C45, 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic const struct fec_devinfo fec_imx28_info = { 11762306a36Sopenharmony_ci .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME | 11862306a36Sopenharmony_ci FEC_QUIRK_SINGLE_MDIO | FEC_QUIRK_HAS_RACC | 11962306a36Sopenharmony_ci FEC_QUIRK_HAS_FRREG | FEC_QUIRK_CLEAR_SETUP_MII | 12062306a36Sopenharmony_ci FEC_QUIRK_NO_HARD_RESET | FEC_QUIRK_HAS_MDIO_C45, 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic const struct fec_devinfo fec_imx6q_info = { 12462306a36Sopenharmony_ci .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 12562306a36Sopenharmony_ci FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 12662306a36Sopenharmony_ci FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR006358 | 12762306a36Sopenharmony_ci FEC_QUIRK_HAS_RACC | FEC_QUIRK_CLEAR_SETUP_MII | 12862306a36Sopenharmony_ci FEC_QUIRK_HAS_PMQOS | FEC_QUIRK_HAS_MDIO_C45, 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic const struct fec_devinfo fec_mvf600_info = { 13262306a36Sopenharmony_ci .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_RACC | 13362306a36Sopenharmony_ci FEC_QUIRK_HAS_MDIO_C45, 13462306a36Sopenharmony_ci}; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic const struct fec_devinfo fec_imx6x_info = { 13762306a36Sopenharmony_ci .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 13862306a36Sopenharmony_ci FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 13962306a36Sopenharmony_ci FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | 14062306a36Sopenharmony_ci FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | 14162306a36Sopenharmony_ci FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE | 14262306a36Sopenharmony_ci FEC_QUIRK_CLEAR_SETUP_MII | FEC_QUIRK_HAS_MULTI_QUEUES | 14362306a36Sopenharmony_ci FEC_QUIRK_HAS_MDIO_C45, 14462306a36Sopenharmony_ci}; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic const struct fec_devinfo fec_imx6ul_info = { 14762306a36Sopenharmony_ci .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 14862306a36Sopenharmony_ci FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 14962306a36Sopenharmony_ci FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR007885 | 15062306a36Sopenharmony_ci FEC_QUIRK_BUG_CAPTURE | FEC_QUIRK_HAS_RACC | 15162306a36Sopenharmony_ci FEC_QUIRK_HAS_COALESCE | FEC_QUIRK_CLEAR_SETUP_MII | 15262306a36Sopenharmony_ci FEC_QUIRK_HAS_MDIO_C45, 15362306a36Sopenharmony_ci}; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic const struct fec_devinfo fec_imx8mq_info = { 15662306a36Sopenharmony_ci .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 15762306a36Sopenharmony_ci FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 15862306a36Sopenharmony_ci FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | 15962306a36Sopenharmony_ci FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | 16062306a36Sopenharmony_ci FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE | 16162306a36Sopenharmony_ci FEC_QUIRK_CLEAR_SETUP_MII | FEC_QUIRK_HAS_MULTI_QUEUES | 16262306a36Sopenharmony_ci FEC_QUIRK_HAS_EEE | FEC_QUIRK_WAKEUP_FROM_INT2 | 16362306a36Sopenharmony_ci FEC_QUIRK_HAS_MDIO_C45, 16462306a36Sopenharmony_ci}; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic const struct fec_devinfo fec_imx8qm_info = { 16762306a36Sopenharmony_ci .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 16862306a36Sopenharmony_ci FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 16962306a36Sopenharmony_ci FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | 17062306a36Sopenharmony_ci FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | 17162306a36Sopenharmony_ci FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE | 17262306a36Sopenharmony_ci FEC_QUIRK_CLEAR_SETUP_MII | FEC_QUIRK_HAS_MULTI_QUEUES | 17362306a36Sopenharmony_ci FEC_QUIRK_DELAYED_CLKS_SUPPORT | FEC_QUIRK_HAS_MDIO_C45, 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic const struct fec_devinfo fec_s32v234_info = { 17762306a36Sopenharmony_ci .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | 17862306a36Sopenharmony_ci FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | 17962306a36Sopenharmony_ci FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | 18062306a36Sopenharmony_ci FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | 18162306a36Sopenharmony_ci FEC_QUIRK_HAS_MDIO_C45, 18262306a36Sopenharmony_ci}; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic struct platform_device_id fec_devtype[] = { 18562306a36Sopenharmony_ci { 18662306a36Sopenharmony_ci /* keep it for coldfire */ 18762306a36Sopenharmony_ci .name = DRIVER_NAME, 18862306a36Sopenharmony_ci .driver_data = 0, 18962306a36Sopenharmony_ci }, { 19062306a36Sopenharmony_ci .name = "imx25-fec", 19162306a36Sopenharmony_ci .driver_data = (kernel_ulong_t)&fec_imx25_info, 19262306a36Sopenharmony_ci }, { 19362306a36Sopenharmony_ci .name = "imx27-fec", 19462306a36Sopenharmony_ci .driver_data = (kernel_ulong_t)&fec_imx27_info, 19562306a36Sopenharmony_ci }, { 19662306a36Sopenharmony_ci .name = "imx28-fec", 19762306a36Sopenharmony_ci .driver_data = (kernel_ulong_t)&fec_imx28_info, 19862306a36Sopenharmony_ci }, { 19962306a36Sopenharmony_ci .name = "imx6q-fec", 20062306a36Sopenharmony_ci .driver_data = (kernel_ulong_t)&fec_imx6q_info, 20162306a36Sopenharmony_ci }, { 20262306a36Sopenharmony_ci .name = "mvf600-fec", 20362306a36Sopenharmony_ci .driver_data = (kernel_ulong_t)&fec_mvf600_info, 20462306a36Sopenharmony_ci }, { 20562306a36Sopenharmony_ci .name = "imx6sx-fec", 20662306a36Sopenharmony_ci .driver_data = (kernel_ulong_t)&fec_imx6x_info, 20762306a36Sopenharmony_ci }, { 20862306a36Sopenharmony_ci .name = "imx6ul-fec", 20962306a36Sopenharmony_ci .driver_data = (kernel_ulong_t)&fec_imx6ul_info, 21062306a36Sopenharmony_ci }, { 21162306a36Sopenharmony_ci .name = "imx8mq-fec", 21262306a36Sopenharmony_ci .driver_data = (kernel_ulong_t)&fec_imx8mq_info, 21362306a36Sopenharmony_ci }, { 21462306a36Sopenharmony_ci .name = "imx8qm-fec", 21562306a36Sopenharmony_ci .driver_data = (kernel_ulong_t)&fec_imx8qm_info, 21662306a36Sopenharmony_ci }, { 21762306a36Sopenharmony_ci .name = "s32v234-fec", 21862306a36Sopenharmony_ci .driver_data = (kernel_ulong_t)&fec_s32v234_info, 21962306a36Sopenharmony_ci }, { 22062306a36Sopenharmony_ci /* sentinel */ 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci}; 22362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, fec_devtype); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cienum imx_fec_type { 22662306a36Sopenharmony_ci IMX25_FEC = 1, /* runs on i.mx25/50/53 */ 22762306a36Sopenharmony_ci IMX27_FEC, /* runs on i.mx27/35/51 */ 22862306a36Sopenharmony_ci IMX28_FEC, 22962306a36Sopenharmony_ci IMX6Q_FEC, 23062306a36Sopenharmony_ci MVF600_FEC, 23162306a36Sopenharmony_ci IMX6SX_FEC, 23262306a36Sopenharmony_ci IMX6UL_FEC, 23362306a36Sopenharmony_ci IMX8MQ_FEC, 23462306a36Sopenharmony_ci IMX8QM_FEC, 23562306a36Sopenharmony_ci S32V234_FEC, 23662306a36Sopenharmony_ci}; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic const struct of_device_id fec_dt_ids[] = { 23962306a36Sopenharmony_ci { .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], }, 24062306a36Sopenharmony_ci { .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], }, 24162306a36Sopenharmony_ci { .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], }, 24262306a36Sopenharmony_ci { .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], }, 24362306a36Sopenharmony_ci { .compatible = "fsl,mvf600-fec", .data = &fec_devtype[MVF600_FEC], }, 24462306a36Sopenharmony_ci { .compatible = "fsl,imx6sx-fec", .data = &fec_devtype[IMX6SX_FEC], }, 24562306a36Sopenharmony_ci { .compatible = "fsl,imx6ul-fec", .data = &fec_devtype[IMX6UL_FEC], }, 24662306a36Sopenharmony_ci { .compatible = "fsl,imx8mq-fec", .data = &fec_devtype[IMX8MQ_FEC], }, 24762306a36Sopenharmony_ci { .compatible = "fsl,imx8qm-fec", .data = &fec_devtype[IMX8QM_FEC], }, 24862306a36Sopenharmony_ci { .compatible = "fsl,s32v234-fec", .data = &fec_devtype[S32V234_FEC], }, 24962306a36Sopenharmony_ci { /* sentinel */ } 25062306a36Sopenharmony_ci}; 25162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, fec_dt_ids); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic unsigned char macaddr[ETH_ALEN]; 25462306a36Sopenharmony_cimodule_param_array(macaddr, byte, NULL, 0); 25562306a36Sopenharmony_ciMODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci#if defined(CONFIG_M5272) 25862306a36Sopenharmony_ci/* 25962306a36Sopenharmony_ci * Some hardware gets it MAC address out of local flash memory. 26062306a36Sopenharmony_ci * if this is non-zero then assume it is the address to get MAC from. 26162306a36Sopenharmony_ci */ 26262306a36Sopenharmony_ci#if defined(CONFIG_NETtel) 26362306a36Sopenharmony_ci#define FEC_FLASHMAC 0xf0006006 26462306a36Sopenharmony_ci#elif defined(CONFIG_GILBARCONAP) || defined(CONFIG_SCALES) 26562306a36Sopenharmony_ci#define FEC_FLASHMAC 0xf0006000 26662306a36Sopenharmony_ci#elif defined(CONFIG_CANCam) 26762306a36Sopenharmony_ci#define FEC_FLASHMAC 0xf0020000 26862306a36Sopenharmony_ci#elif defined (CONFIG_M5272C3) 26962306a36Sopenharmony_ci#define FEC_FLASHMAC (0xffe04000 + 4) 27062306a36Sopenharmony_ci#elif defined(CONFIG_MOD5272) 27162306a36Sopenharmony_ci#define FEC_FLASHMAC 0xffc0406b 27262306a36Sopenharmony_ci#else 27362306a36Sopenharmony_ci#define FEC_FLASHMAC 0 27462306a36Sopenharmony_ci#endif 27562306a36Sopenharmony_ci#endif /* CONFIG_M5272 */ 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci/* The FEC stores dest/src/type/vlan, data, and checksum for receive packets. 27862306a36Sopenharmony_ci * 27962306a36Sopenharmony_ci * 2048 byte skbufs are allocated. However, alignment requirements 28062306a36Sopenharmony_ci * varies between FEC variants. Worst case is 64, so round down by 64. 28162306a36Sopenharmony_ci */ 28262306a36Sopenharmony_ci#define PKT_MAXBUF_SIZE (round_down(2048 - 64, 64)) 28362306a36Sopenharmony_ci#define PKT_MINBUF_SIZE 64 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci/* FEC receive acceleration */ 28662306a36Sopenharmony_ci#define FEC_RACC_IPDIS (1 << 1) 28762306a36Sopenharmony_ci#define FEC_RACC_PRODIS (1 << 2) 28862306a36Sopenharmony_ci#define FEC_RACC_SHIFT16 BIT(7) 28962306a36Sopenharmony_ci#define FEC_RACC_OPTIONS (FEC_RACC_IPDIS | FEC_RACC_PRODIS) 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci/* MIB Control Register */ 29262306a36Sopenharmony_ci#define FEC_MIB_CTRLSTAT_DISABLE BIT(31) 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci/* 29562306a36Sopenharmony_ci * The 5270/5271/5280/5282/532x RX control register also contains maximum frame 29662306a36Sopenharmony_ci * size bits. Other FEC hardware does not, so we need to take that into 29762306a36Sopenharmony_ci * account when setting it. 29862306a36Sopenharmony_ci */ 29962306a36Sopenharmony_ci#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ 30062306a36Sopenharmony_ci defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ 30162306a36Sopenharmony_ci defined(CONFIG_ARM64) 30262306a36Sopenharmony_ci#define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16) 30362306a36Sopenharmony_ci#else 30462306a36Sopenharmony_ci#define OPT_FRAME_SIZE 0 30562306a36Sopenharmony_ci#endif 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci/* FEC MII MMFR bits definition */ 30862306a36Sopenharmony_ci#define FEC_MMFR_ST (1 << 30) 30962306a36Sopenharmony_ci#define FEC_MMFR_ST_C45 (0) 31062306a36Sopenharmony_ci#define FEC_MMFR_OP_READ (2 << 28) 31162306a36Sopenharmony_ci#define FEC_MMFR_OP_READ_C45 (3 << 28) 31262306a36Sopenharmony_ci#define FEC_MMFR_OP_WRITE (1 << 28) 31362306a36Sopenharmony_ci#define FEC_MMFR_OP_ADDR_WRITE (0) 31462306a36Sopenharmony_ci#define FEC_MMFR_PA(v) ((v & 0x1f) << 23) 31562306a36Sopenharmony_ci#define FEC_MMFR_RA(v) ((v & 0x1f) << 18) 31662306a36Sopenharmony_ci#define FEC_MMFR_TA (2 << 16) 31762306a36Sopenharmony_ci#define FEC_MMFR_DATA(v) (v & 0xffff) 31862306a36Sopenharmony_ci/* FEC ECR bits definition */ 31962306a36Sopenharmony_ci#define FEC_ECR_MAGICEN (1 << 2) 32062306a36Sopenharmony_ci#define FEC_ECR_SLEEP (1 << 3) 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci#define FEC_MII_TIMEOUT 30000 /* us */ 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci/* Transmitter timeout */ 32562306a36Sopenharmony_ci#define TX_TIMEOUT (2 * HZ) 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci#define FEC_PAUSE_FLAG_AUTONEG 0x1 32862306a36Sopenharmony_ci#define FEC_PAUSE_FLAG_ENABLE 0x2 32962306a36Sopenharmony_ci#define FEC_WOL_HAS_MAGIC_PACKET (0x1 << 0) 33062306a36Sopenharmony_ci#define FEC_WOL_FLAG_ENABLE (0x1 << 1) 33162306a36Sopenharmony_ci#define FEC_WOL_FLAG_SLEEP_ON (0x1 << 2) 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci/* Max number of allowed TCP segments for software TSO */ 33462306a36Sopenharmony_ci#define FEC_MAX_TSO_SEGS 100 33562306a36Sopenharmony_ci#define FEC_MAX_SKB_DESCS (FEC_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS) 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci#define IS_TSO_HEADER(txq, addr) \ 33862306a36Sopenharmony_ci ((addr >= txq->tso_hdrs_dma) && \ 33962306a36Sopenharmony_ci (addr < txq->tso_hdrs_dma + txq->bd.ring_size * TSO_HEADER_SIZE)) 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic int mii_cnt; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, 34462306a36Sopenharmony_ci struct bufdesc_prop *bd) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci return (bdp >= bd->last) ? bd->base 34762306a36Sopenharmony_ci : (struct bufdesc *)(((void *)bdp) + bd->dsize); 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, 35162306a36Sopenharmony_ci struct bufdesc_prop *bd) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci return (bdp <= bd->base) ? bd->last 35462306a36Sopenharmony_ci : (struct bufdesc *)(((void *)bdp) - bd->dsize); 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic int fec_enet_get_bd_index(struct bufdesc *bdp, 35862306a36Sopenharmony_ci struct bufdesc_prop *bd) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci return ((const char *)bdp - (const char *)bd->base) >> bd->dsize_log2; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic int fec_enet_get_free_txdesc_num(struct fec_enet_priv_tx_q *txq) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci int entries; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci entries = (((const char *)txq->dirty_tx - 36862306a36Sopenharmony_ci (const char *)txq->bd.cur) >> txq->bd.dsize_log2) - 1; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci return entries >= 0 ? entries : entries + txq->bd.ring_size; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic void swap_buffer(void *bufaddr, int len) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci int i; 37662306a36Sopenharmony_ci unsigned int *buf = bufaddr; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci for (i = 0; i < len; i += 4, buf++) 37962306a36Sopenharmony_ci swab32s(buf); 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic void fec_dump(struct net_device *ndev) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 38562306a36Sopenharmony_ci struct bufdesc *bdp; 38662306a36Sopenharmony_ci struct fec_enet_priv_tx_q *txq; 38762306a36Sopenharmony_ci int index = 0; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci netdev_info(ndev, "TX ring dump\n"); 39062306a36Sopenharmony_ci pr_info("Nr SC addr len SKB\n"); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci txq = fep->tx_queue[0]; 39362306a36Sopenharmony_ci bdp = txq->bd.base; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci do { 39662306a36Sopenharmony_ci pr_info("%3u %c%c 0x%04x 0x%08x %4u %p\n", 39762306a36Sopenharmony_ci index, 39862306a36Sopenharmony_ci bdp == txq->bd.cur ? 'S' : ' ', 39962306a36Sopenharmony_ci bdp == txq->dirty_tx ? 'H' : ' ', 40062306a36Sopenharmony_ci fec16_to_cpu(bdp->cbd_sc), 40162306a36Sopenharmony_ci fec32_to_cpu(bdp->cbd_bufaddr), 40262306a36Sopenharmony_ci fec16_to_cpu(bdp->cbd_datlen), 40362306a36Sopenharmony_ci txq->tx_buf[index].buf_p); 40462306a36Sopenharmony_ci bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 40562306a36Sopenharmony_ci index++; 40662306a36Sopenharmony_ci } while (bdp != txq->bd.base); 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic inline bool is_ipv4_pkt(struct sk_buff *skb) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci return skb->protocol == htons(ETH_P_IP) && ip_hdr(skb)->version == 4; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic int 41562306a36Sopenharmony_cifec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci /* Only run for packets requiring a checksum. */ 41862306a36Sopenharmony_ci if (skb->ip_summed != CHECKSUM_PARTIAL) 41962306a36Sopenharmony_ci return 0; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (unlikely(skb_cow_head(skb, 0))) 42262306a36Sopenharmony_ci return -1; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (is_ipv4_pkt(skb)) 42562306a36Sopenharmony_ci ip_hdr(skb)->check = 0; 42662306a36Sopenharmony_ci *(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) = 0; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci return 0; 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic int 43262306a36Sopenharmony_cifec_enet_create_page_pool(struct fec_enet_private *fep, 43362306a36Sopenharmony_ci struct fec_enet_priv_rx_q *rxq, int size) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci struct bpf_prog *xdp_prog = READ_ONCE(fep->xdp_prog); 43662306a36Sopenharmony_ci struct page_pool_params pp_params = { 43762306a36Sopenharmony_ci .order = 0, 43862306a36Sopenharmony_ci .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV, 43962306a36Sopenharmony_ci .pool_size = size, 44062306a36Sopenharmony_ci .nid = dev_to_node(&fep->pdev->dev), 44162306a36Sopenharmony_ci .dev = &fep->pdev->dev, 44262306a36Sopenharmony_ci .dma_dir = xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE, 44362306a36Sopenharmony_ci .offset = FEC_ENET_XDP_HEADROOM, 44462306a36Sopenharmony_ci .max_len = FEC_ENET_RX_FRSIZE, 44562306a36Sopenharmony_ci }; 44662306a36Sopenharmony_ci int err; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci rxq->page_pool = page_pool_create(&pp_params); 44962306a36Sopenharmony_ci if (IS_ERR(rxq->page_pool)) { 45062306a36Sopenharmony_ci err = PTR_ERR(rxq->page_pool); 45162306a36Sopenharmony_ci rxq->page_pool = NULL; 45262306a36Sopenharmony_ci return err; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci err = xdp_rxq_info_reg(&rxq->xdp_rxq, fep->netdev, rxq->id, 0); 45662306a36Sopenharmony_ci if (err < 0) 45762306a36Sopenharmony_ci goto err_free_pp; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci err = xdp_rxq_info_reg_mem_model(&rxq->xdp_rxq, MEM_TYPE_PAGE_POOL, 46062306a36Sopenharmony_ci rxq->page_pool); 46162306a36Sopenharmony_ci if (err) 46262306a36Sopenharmony_ci goto err_unregister_rxq; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci return 0; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cierr_unregister_rxq: 46762306a36Sopenharmony_ci xdp_rxq_info_unreg(&rxq->xdp_rxq); 46862306a36Sopenharmony_cierr_free_pp: 46962306a36Sopenharmony_ci page_pool_destroy(rxq->page_pool); 47062306a36Sopenharmony_ci rxq->page_pool = NULL; 47162306a36Sopenharmony_ci return err; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic struct bufdesc * 47562306a36Sopenharmony_cifec_enet_txq_submit_frag_skb(struct fec_enet_priv_tx_q *txq, 47662306a36Sopenharmony_ci struct sk_buff *skb, 47762306a36Sopenharmony_ci struct net_device *ndev) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 48062306a36Sopenharmony_ci struct bufdesc *bdp = txq->bd.cur; 48162306a36Sopenharmony_ci struct bufdesc_ex *ebdp; 48262306a36Sopenharmony_ci int nr_frags = skb_shinfo(skb)->nr_frags; 48362306a36Sopenharmony_ci int frag, frag_len; 48462306a36Sopenharmony_ci unsigned short status; 48562306a36Sopenharmony_ci unsigned int estatus = 0; 48662306a36Sopenharmony_ci skb_frag_t *this_frag; 48762306a36Sopenharmony_ci unsigned int index; 48862306a36Sopenharmony_ci void *bufaddr; 48962306a36Sopenharmony_ci dma_addr_t addr; 49062306a36Sopenharmony_ci int i; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci for (frag = 0; frag < nr_frags; frag++) { 49362306a36Sopenharmony_ci this_frag = &skb_shinfo(skb)->frags[frag]; 49462306a36Sopenharmony_ci bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 49562306a36Sopenharmony_ci ebdp = (struct bufdesc_ex *)bdp; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci status = fec16_to_cpu(bdp->cbd_sc); 49862306a36Sopenharmony_ci status &= ~BD_ENET_TX_STATS; 49962306a36Sopenharmony_ci status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 50062306a36Sopenharmony_ci frag_len = skb_frag_size(&skb_shinfo(skb)->frags[frag]); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci /* Handle the last BD specially */ 50362306a36Sopenharmony_ci if (frag == nr_frags - 1) { 50462306a36Sopenharmony_ci status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); 50562306a36Sopenharmony_ci if (fep->bufdesc_ex) { 50662306a36Sopenharmony_ci estatus |= BD_ENET_TX_INT; 50762306a36Sopenharmony_ci if (unlikely(skb_shinfo(skb)->tx_flags & 50862306a36Sopenharmony_ci SKBTX_HW_TSTAMP && fep->hwts_tx_en)) 50962306a36Sopenharmony_ci estatus |= BD_ENET_TX_TS; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (fep->bufdesc_ex) { 51462306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_AVB) 51562306a36Sopenharmony_ci estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 51662306a36Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) 51762306a36Sopenharmony_ci estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci ebdp->cbd_bdu = 0; 52062306a36Sopenharmony_ci ebdp->cbd_esc = cpu_to_fec32(estatus); 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci bufaddr = skb_frag_address(this_frag); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci index = fec_enet_get_bd_index(bdp, &txq->bd); 52662306a36Sopenharmony_ci if (((unsigned long) bufaddr) & fep->tx_align || 52762306a36Sopenharmony_ci fep->quirks & FEC_QUIRK_SWAP_FRAME) { 52862306a36Sopenharmony_ci memcpy(txq->tx_bounce[index], bufaddr, frag_len); 52962306a36Sopenharmony_ci bufaddr = txq->tx_bounce[index]; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 53262306a36Sopenharmony_ci swap_buffer(bufaddr, frag_len); 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci addr = dma_map_single(&fep->pdev->dev, bufaddr, frag_len, 53662306a36Sopenharmony_ci DMA_TO_DEVICE); 53762306a36Sopenharmony_ci if (dma_mapping_error(&fep->pdev->dev, addr)) { 53862306a36Sopenharmony_ci if (net_ratelimit()) 53962306a36Sopenharmony_ci netdev_err(ndev, "Tx DMA memory map failed\n"); 54062306a36Sopenharmony_ci goto dma_mapping_error; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci bdp->cbd_bufaddr = cpu_to_fec32(addr); 54462306a36Sopenharmony_ci bdp->cbd_datlen = cpu_to_fec16(frag_len); 54562306a36Sopenharmony_ci /* Make sure the updates to rest of the descriptor are 54662306a36Sopenharmony_ci * performed before transferring ownership. 54762306a36Sopenharmony_ci */ 54862306a36Sopenharmony_ci wmb(); 54962306a36Sopenharmony_ci bdp->cbd_sc = cpu_to_fec16(status); 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci return bdp; 55362306a36Sopenharmony_cidma_mapping_error: 55462306a36Sopenharmony_ci bdp = txq->bd.cur; 55562306a36Sopenharmony_ci for (i = 0; i < frag; i++) { 55662306a36Sopenharmony_ci bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 55762306a36Sopenharmony_ci dma_unmap_single(&fep->pdev->dev, fec32_to_cpu(bdp->cbd_bufaddr), 55862306a36Sopenharmony_ci fec16_to_cpu(bdp->cbd_datlen), DMA_TO_DEVICE); 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cistatic int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq, 56462306a36Sopenharmony_ci struct sk_buff *skb, struct net_device *ndev) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 56762306a36Sopenharmony_ci int nr_frags = skb_shinfo(skb)->nr_frags; 56862306a36Sopenharmony_ci struct bufdesc *bdp, *last_bdp; 56962306a36Sopenharmony_ci void *bufaddr; 57062306a36Sopenharmony_ci dma_addr_t addr; 57162306a36Sopenharmony_ci unsigned short status; 57262306a36Sopenharmony_ci unsigned short buflen; 57362306a36Sopenharmony_ci unsigned int estatus = 0; 57462306a36Sopenharmony_ci unsigned int index; 57562306a36Sopenharmony_ci int entries_free; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci entries_free = fec_enet_get_free_txdesc_num(txq); 57862306a36Sopenharmony_ci if (entries_free < MAX_SKB_FRAGS + 1) { 57962306a36Sopenharmony_ci dev_kfree_skb_any(skb); 58062306a36Sopenharmony_ci if (net_ratelimit()) 58162306a36Sopenharmony_ci netdev_err(ndev, "NOT enough BD for SG!\n"); 58262306a36Sopenharmony_ci return NETDEV_TX_OK; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* Protocol checksum off-load for TCP and UDP. */ 58662306a36Sopenharmony_ci if (fec_enet_clear_csum(skb, ndev)) { 58762306a36Sopenharmony_ci dev_kfree_skb_any(skb); 58862306a36Sopenharmony_ci return NETDEV_TX_OK; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* Fill in a Tx ring entry */ 59262306a36Sopenharmony_ci bdp = txq->bd.cur; 59362306a36Sopenharmony_ci last_bdp = bdp; 59462306a36Sopenharmony_ci status = fec16_to_cpu(bdp->cbd_sc); 59562306a36Sopenharmony_ci status &= ~BD_ENET_TX_STATS; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* Set buffer length and buffer pointer */ 59862306a36Sopenharmony_ci bufaddr = skb->data; 59962306a36Sopenharmony_ci buflen = skb_headlen(skb); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci index = fec_enet_get_bd_index(bdp, &txq->bd); 60262306a36Sopenharmony_ci if (((unsigned long) bufaddr) & fep->tx_align || 60362306a36Sopenharmony_ci fep->quirks & FEC_QUIRK_SWAP_FRAME) { 60462306a36Sopenharmony_ci memcpy(txq->tx_bounce[index], skb->data, buflen); 60562306a36Sopenharmony_ci bufaddr = txq->tx_bounce[index]; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 60862306a36Sopenharmony_ci swap_buffer(bufaddr, buflen); 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci /* Push the data cache so the CPM does not get stale memory data. */ 61262306a36Sopenharmony_ci addr = dma_map_single(&fep->pdev->dev, bufaddr, buflen, DMA_TO_DEVICE); 61362306a36Sopenharmony_ci if (dma_mapping_error(&fep->pdev->dev, addr)) { 61462306a36Sopenharmony_ci dev_kfree_skb_any(skb); 61562306a36Sopenharmony_ci if (net_ratelimit()) 61662306a36Sopenharmony_ci netdev_err(ndev, "Tx DMA memory map failed\n"); 61762306a36Sopenharmony_ci return NETDEV_TX_OK; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci if (nr_frags) { 62162306a36Sopenharmony_ci last_bdp = fec_enet_txq_submit_frag_skb(txq, skb, ndev); 62262306a36Sopenharmony_ci if (IS_ERR(last_bdp)) { 62362306a36Sopenharmony_ci dma_unmap_single(&fep->pdev->dev, addr, 62462306a36Sopenharmony_ci buflen, DMA_TO_DEVICE); 62562306a36Sopenharmony_ci dev_kfree_skb_any(skb); 62662306a36Sopenharmony_ci return NETDEV_TX_OK; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci } else { 62962306a36Sopenharmony_ci status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); 63062306a36Sopenharmony_ci if (fep->bufdesc_ex) { 63162306a36Sopenharmony_ci estatus = BD_ENET_TX_INT; 63262306a36Sopenharmony_ci if (unlikely(skb_shinfo(skb)->tx_flags & 63362306a36Sopenharmony_ci SKBTX_HW_TSTAMP && fep->hwts_tx_en)) 63462306a36Sopenharmony_ci estatus |= BD_ENET_TX_TS; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci bdp->cbd_bufaddr = cpu_to_fec32(addr); 63862306a36Sopenharmony_ci bdp->cbd_datlen = cpu_to_fec16(buflen); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (fep->bufdesc_ex) { 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && 64562306a36Sopenharmony_ci fep->hwts_tx_en)) 64662306a36Sopenharmony_ci skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_AVB) 64962306a36Sopenharmony_ci estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) 65262306a36Sopenharmony_ci estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci ebdp->cbd_bdu = 0; 65562306a36Sopenharmony_ci ebdp->cbd_esc = cpu_to_fec32(estatus); 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci index = fec_enet_get_bd_index(last_bdp, &txq->bd); 65962306a36Sopenharmony_ci /* Save skb pointer */ 66062306a36Sopenharmony_ci txq->tx_buf[index].buf_p = skb; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci /* Make sure the updates to rest of the descriptor are performed before 66362306a36Sopenharmony_ci * transferring ownership. 66462306a36Sopenharmony_ci */ 66562306a36Sopenharmony_ci wmb(); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci /* Send it on its way. Tell FEC it's ready, interrupt when done, 66862306a36Sopenharmony_ci * it's the last BD of the frame, and to put the CRC on the end. 66962306a36Sopenharmony_ci */ 67062306a36Sopenharmony_ci status |= (BD_ENET_TX_READY | BD_ENET_TX_TC); 67162306a36Sopenharmony_ci bdp->cbd_sc = cpu_to_fec16(status); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci /* If this was the last BD in the ring, start at the beginning again. */ 67462306a36Sopenharmony_ci bdp = fec_enet_get_nextdesc(last_bdp, &txq->bd); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci skb_tx_timestamp(skb); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci /* Make sure the update to bdp is performed before txq->bd.cur. */ 67962306a36Sopenharmony_ci wmb(); 68062306a36Sopenharmony_ci txq->bd.cur = bdp; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* Trigger transmission start */ 68362306a36Sopenharmony_ci writel(0, txq->bd.reg_desc_active); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci return 0; 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic int 68962306a36Sopenharmony_cifec_enet_txq_put_data_tso(struct fec_enet_priv_tx_q *txq, struct sk_buff *skb, 69062306a36Sopenharmony_ci struct net_device *ndev, 69162306a36Sopenharmony_ci struct bufdesc *bdp, int index, char *data, 69262306a36Sopenharmony_ci int size, bool last_tcp, bool is_last) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 69562306a36Sopenharmony_ci struct bufdesc_ex *ebdp = container_of(bdp, struct bufdesc_ex, desc); 69662306a36Sopenharmony_ci unsigned short status; 69762306a36Sopenharmony_ci unsigned int estatus = 0; 69862306a36Sopenharmony_ci dma_addr_t addr; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci status = fec16_to_cpu(bdp->cbd_sc); 70162306a36Sopenharmony_ci status &= ~BD_ENET_TX_STATS; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci if (((unsigned long) data) & fep->tx_align || 70662306a36Sopenharmony_ci fep->quirks & FEC_QUIRK_SWAP_FRAME) { 70762306a36Sopenharmony_ci memcpy(txq->tx_bounce[index], data, size); 70862306a36Sopenharmony_ci data = txq->tx_bounce[index]; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 71162306a36Sopenharmony_ci swap_buffer(data, size); 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci addr = dma_map_single(&fep->pdev->dev, data, size, DMA_TO_DEVICE); 71562306a36Sopenharmony_ci if (dma_mapping_error(&fep->pdev->dev, addr)) { 71662306a36Sopenharmony_ci dev_kfree_skb_any(skb); 71762306a36Sopenharmony_ci if (net_ratelimit()) 71862306a36Sopenharmony_ci netdev_err(ndev, "Tx DMA memory map failed\n"); 71962306a36Sopenharmony_ci return NETDEV_TX_OK; 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci bdp->cbd_datlen = cpu_to_fec16(size); 72362306a36Sopenharmony_ci bdp->cbd_bufaddr = cpu_to_fec32(addr); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci if (fep->bufdesc_ex) { 72662306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_AVB) 72762306a36Sopenharmony_ci estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 72862306a36Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) 72962306a36Sopenharmony_ci estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 73062306a36Sopenharmony_ci ebdp->cbd_bdu = 0; 73162306a36Sopenharmony_ci ebdp->cbd_esc = cpu_to_fec32(estatus); 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci /* Handle the last BD specially */ 73562306a36Sopenharmony_ci if (last_tcp) 73662306a36Sopenharmony_ci status |= (BD_ENET_TX_LAST | BD_ENET_TX_TC); 73762306a36Sopenharmony_ci if (is_last) { 73862306a36Sopenharmony_ci status |= BD_ENET_TX_INTR; 73962306a36Sopenharmony_ci if (fep->bufdesc_ex) 74062306a36Sopenharmony_ci ebdp->cbd_esc |= cpu_to_fec32(BD_ENET_TX_INT); 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci bdp->cbd_sc = cpu_to_fec16(status); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci return 0; 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_cistatic int 74962306a36Sopenharmony_cifec_enet_txq_put_hdr_tso(struct fec_enet_priv_tx_q *txq, 75062306a36Sopenharmony_ci struct sk_buff *skb, struct net_device *ndev, 75162306a36Sopenharmony_ci struct bufdesc *bdp, int index) 75262306a36Sopenharmony_ci{ 75362306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 75462306a36Sopenharmony_ci int hdr_len = skb_tcp_all_headers(skb); 75562306a36Sopenharmony_ci struct bufdesc_ex *ebdp = container_of(bdp, struct bufdesc_ex, desc); 75662306a36Sopenharmony_ci void *bufaddr; 75762306a36Sopenharmony_ci unsigned long dmabuf; 75862306a36Sopenharmony_ci unsigned short status; 75962306a36Sopenharmony_ci unsigned int estatus = 0; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci status = fec16_to_cpu(bdp->cbd_sc); 76262306a36Sopenharmony_ci status &= ~BD_ENET_TX_STATS; 76362306a36Sopenharmony_ci status |= (BD_ENET_TX_TC | BD_ENET_TX_READY); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci bufaddr = txq->tso_hdrs + index * TSO_HEADER_SIZE; 76662306a36Sopenharmony_ci dmabuf = txq->tso_hdrs_dma + index * TSO_HEADER_SIZE; 76762306a36Sopenharmony_ci if (((unsigned long)bufaddr) & fep->tx_align || 76862306a36Sopenharmony_ci fep->quirks & FEC_QUIRK_SWAP_FRAME) { 76962306a36Sopenharmony_ci memcpy(txq->tx_bounce[index], skb->data, hdr_len); 77062306a36Sopenharmony_ci bufaddr = txq->tx_bounce[index]; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 77362306a36Sopenharmony_ci swap_buffer(bufaddr, hdr_len); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci dmabuf = dma_map_single(&fep->pdev->dev, bufaddr, 77662306a36Sopenharmony_ci hdr_len, DMA_TO_DEVICE); 77762306a36Sopenharmony_ci if (dma_mapping_error(&fep->pdev->dev, dmabuf)) { 77862306a36Sopenharmony_ci dev_kfree_skb_any(skb); 77962306a36Sopenharmony_ci if (net_ratelimit()) 78062306a36Sopenharmony_ci netdev_err(ndev, "Tx DMA memory map failed\n"); 78162306a36Sopenharmony_ci return NETDEV_TX_OK; 78262306a36Sopenharmony_ci } 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci bdp->cbd_bufaddr = cpu_to_fec32(dmabuf); 78662306a36Sopenharmony_ci bdp->cbd_datlen = cpu_to_fec16(hdr_len); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (fep->bufdesc_ex) { 78962306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_AVB) 79062306a36Sopenharmony_ci estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 79162306a36Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) 79262306a36Sopenharmony_ci estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; 79362306a36Sopenharmony_ci ebdp->cbd_bdu = 0; 79462306a36Sopenharmony_ci ebdp->cbd_esc = cpu_to_fec32(estatus); 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci bdp->cbd_sc = cpu_to_fec16(status); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci return 0; 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_cistatic int fec_enet_txq_submit_tso(struct fec_enet_priv_tx_q *txq, 80362306a36Sopenharmony_ci struct sk_buff *skb, 80462306a36Sopenharmony_ci struct net_device *ndev) 80562306a36Sopenharmony_ci{ 80662306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 80762306a36Sopenharmony_ci int hdr_len, total_len, data_left; 80862306a36Sopenharmony_ci struct bufdesc *bdp = txq->bd.cur; 80962306a36Sopenharmony_ci struct tso_t tso; 81062306a36Sopenharmony_ci unsigned int index = 0; 81162306a36Sopenharmony_ci int ret; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci if (tso_count_descs(skb) >= fec_enet_get_free_txdesc_num(txq)) { 81462306a36Sopenharmony_ci dev_kfree_skb_any(skb); 81562306a36Sopenharmony_ci if (net_ratelimit()) 81662306a36Sopenharmony_ci netdev_err(ndev, "NOT enough BD for TSO!\n"); 81762306a36Sopenharmony_ci return NETDEV_TX_OK; 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci /* Protocol checksum off-load for TCP and UDP. */ 82162306a36Sopenharmony_ci if (fec_enet_clear_csum(skb, ndev)) { 82262306a36Sopenharmony_ci dev_kfree_skb_any(skb); 82362306a36Sopenharmony_ci return NETDEV_TX_OK; 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci /* Initialize the TSO handler, and prepare the first payload */ 82762306a36Sopenharmony_ci hdr_len = tso_start(skb, &tso); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci total_len = skb->len - hdr_len; 83062306a36Sopenharmony_ci while (total_len > 0) { 83162306a36Sopenharmony_ci char *hdr; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci index = fec_enet_get_bd_index(bdp, &txq->bd); 83462306a36Sopenharmony_ci data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len); 83562306a36Sopenharmony_ci total_len -= data_left; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci /* prepare packet headers: MAC + IP + TCP */ 83862306a36Sopenharmony_ci hdr = txq->tso_hdrs + index * TSO_HEADER_SIZE; 83962306a36Sopenharmony_ci tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0); 84062306a36Sopenharmony_ci ret = fec_enet_txq_put_hdr_tso(txq, skb, ndev, bdp, index); 84162306a36Sopenharmony_ci if (ret) 84262306a36Sopenharmony_ci goto err_release; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci while (data_left > 0) { 84562306a36Sopenharmony_ci int size; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci size = min_t(int, tso.size, data_left); 84862306a36Sopenharmony_ci bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 84962306a36Sopenharmony_ci index = fec_enet_get_bd_index(bdp, &txq->bd); 85062306a36Sopenharmony_ci ret = fec_enet_txq_put_data_tso(txq, skb, ndev, 85162306a36Sopenharmony_ci bdp, index, 85262306a36Sopenharmony_ci tso.data, size, 85362306a36Sopenharmony_ci size == data_left, 85462306a36Sopenharmony_ci total_len == 0); 85562306a36Sopenharmony_ci if (ret) 85662306a36Sopenharmony_ci goto err_release; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci data_left -= size; 85962306a36Sopenharmony_ci tso_build_data(skb, &tso, size); 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci /* Save skb pointer */ 86662306a36Sopenharmony_ci txq->tx_buf[index].buf_p = skb; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci skb_tx_timestamp(skb); 86962306a36Sopenharmony_ci txq->bd.cur = bdp; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci /* Trigger transmission start */ 87262306a36Sopenharmony_ci if (!(fep->quirks & FEC_QUIRK_ERR007885) || 87362306a36Sopenharmony_ci !readl(txq->bd.reg_desc_active) || 87462306a36Sopenharmony_ci !readl(txq->bd.reg_desc_active) || 87562306a36Sopenharmony_ci !readl(txq->bd.reg_desc_active) || 87662306a36Sopenharmony_ci !readl(txq->bd.reg_desc_active)) 87762306a36Sopenharmony_ci writel(0, txq->bd.reg_desc_active); 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci return 0; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_cierr_release: 88262306a36Sopenharmony_ci /* TODO: Release all used data descriptors for TSO */ 88362306a36Sopenharmony_ci return ret; 88462306a36Sopenharmony_ci} 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_cistatic netdev_tx_t 88762306a36Sopenharmony_cifec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) 88862306a36Sopenharmony_ci{ 88962306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 89062306a36Sopenharmony_ci int entries_free; 89162306a36Sopenharmony_ci unsigned short queue; 89262306a36Sopenharmony_ci struct fec_enet_priv_tx_q *txq; 89362306a36Sopenharmony_ci struct netdev_queue *nq; 89462306a36Sopenharmony_ci int ret; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci queue = skb_get_queue_mapping(skb); 89762306a36Sopenharmony_ci txq = fep->tx_queue[queue]; 89862306a36Sopenharmony_ci nq = netdev_get_tx_queue(ndev, queue); 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci if (skb_is_gso(skb)) 90162306a36Sopenharmony_ci ret = fec_enet_txq_submit_tso(txq, skb, ndev); 90262306a36Sopenharmony_ci else 90362306a36Sopenharmony_ci ret = fec_enet_txq_submit_skb(txq, skb, ndev); 90462306a36Sopenharmony_ci if (ret) 90562306a36Sopenharmony_ci return ret; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci entries_free = fec_enet_get_free_txdesc_num(txq); 90862306a36Sopenharmony_ci if (entries_free <= txq->tx_stop_threshold) 90962306a36Sopenharmony_ci netif_tx_stop_queue(nq); 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci return NETDEV_TX_OK; 91262306a36Sopenharmony_ci} 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci/* Init RX & TX buffer descriptors 91562306a36Sopenharmony_ci */ 91662306a36Sopenharmony_cistatic void fec_enet_bd_init(struct net_device *dev) 91762306a36Sopenharmony_ci{ 91862306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(dev); 91962306a36Sopenharmony_ci struct fec_enet_priv_tx_q *txq; 92062306a36Sopenharmony_ci struct fec_enet_priv_rx_q *rxq; 92162306a36Sopenharmony_ci struct bufdesc *bdp; 92262306a36Sopenharmony_ci unsigned int i; 92362306a36Sopenharmony_ci unsigned int q; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci for (q = 0; q < fep->num_rx_queues; q++) { 92662306a36Sopenharmony_ci /* Initialize the receive buffer descriptors. */ 92762306a36Sopenharmony_ci rxq = fep->rx_queue[q]; 92862306a36Sopenharmony_ci bdp = rxq->bd.base; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci for (i = 0; i < rxq->bd.ring_size; i++) { 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci /* Initialize the BD for every fragment in the page. */ 93362306a36Sopenharmony_ci if (bdp->cbd_bufaddr) 93462306a36Sopenharmony_ci bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); 93562306a36Sopenharmony_ci else 93662306a36Sopenharmony_ci bdp->cbd_sc = cpu_to_fec16(0); 93762306a36Sopenharmony_ci bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci /* Set the last buffer to wrap */ 94162306a36Sopenharmony_ci bdp = fec_enet_get_prevdesc(bdp, &rxq->bd); 94262306a36Sopenharmony_ci bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci rxq->bd.cur = rxq->bd.base; 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci for (q = 0; q < fep->num_tx_queues; q++) { 94862306a36Sopenharmony_ci /* ...and the same for transmit */ 94962306a36Sopenharmony_ci txq = fep->tx_queue[q]; 95062306a36Sopenharmony_ci bdp = txq->bd.base; 95162306a36Sopenharmony_ci txq->bd.cur = bdp; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci for (i = 0; i < txq->bd.ring_size; i++) { 95462306a36Sopenharmony_ci /* Initialize the BD for every fragment in the page. */ 95562306a36Sopenharmony_ci bdp->cbd_sc = cpu_to_fec16(0); 95662306a36Sopenharmony_ci if (txq->tx_buf[i].type == FEC_TXBUF_T_SKB) { 95762306a36Sopenharmony_ci if (bdp->cbd_bufaddr && 95862306a36Sopenharmony_ci !IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr))) 95962306a36Sopenharmony_ci dma_unmap_single(&fep->pdev->dev, 96062306a36Sopenharmony_ci fec32_to_cpu(bdp->cbd_bufaddr), 96162306a36Sopenharmony_ci fec16_to_cpu(bdp->cbd_datlen), 96262306a36Sopenharmony_ci DMA_TO_DEVICE); 96362306a36Sopenharmony_ci if (txq->tx_buf[i].buf_p) 96462306a36Sopenharmony_ci dev_kfree_skb_any(txq->tx_buf[i].buf_p); 96562306a36Sopenharmony_ci } else if (txq->tx_buf[i].type == FEC_TXBUF_T_XDP_NDO) { 96662306a36Sopenharmony_ci if (bdp->cbd_bufaddr) 96762306a36Sopenharmony_ci dma_unmap_single(&fep->pdev->dev, 96862306a36Sopenharmony_ci fec32_to_cpu(bdp->cbd_bufaddr), 96962306a36Sopenharmony_ci fec16_to_cpu(bdp->cbd_datlen), 97062306a36Sopenharmony_ci DMA_TO_DEVICE); 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci if (txq->tx_buf[i].buf_p) 97362306a36Sopenharmony_ci xdp_return_frame(txq->tx_buf[i].buf_p); 97462306a36Sopenharmony_ci } else { 97562306a36Sopenharmony_ci struct page *page = txq->tx_buf[i].buf_p; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci if (page) 97862306a36Sopenharmony_ci page_pool_put_page(page->pp, page, 0, false); 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci txq->tx_buf[i].buf_p = NULL; 98262306a36Sopenharmony_ci /* restore default tx buffer type: FEC_TXBUF_T_SKB */ 98362306a36Sopenharmony_ci txq->tx_buf[i].type = FEC_TXBUF_T_SKB; 98462306a36Sopenharmony_ci bdp->cbd_bufaddr = cpu_to_fec32(0); 98562306a36Sopenharmony_ci bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 98662306a36Sopenharmony_ci } 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci /* Set the last buffer to wrap */ 98962306a36Sopenharmony_ci bdp = fec_enet_get_prevdesc(bdp, &txq->bd); 99062306a36Sopenharmony_ci bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 99162306a36Sopenharmony_ci txq->dirty_tx = bdp; 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci} 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_cistatic void fec_enet_active_rxring(struct net_device *ndev) 99662306a36Sopenharmony_ci{ 99762306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 99862306a36Sopenharmony_ci int i; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci for (i = 0; i < fep->num_rx_queues; i++) 100162306a36Sopenharmony_ci writel(0, fep->rx_queue[i]->bd.reg_desc_active); 100262306a36Sopenharmony_ci} 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_cistatic void fec_enet_enable_ring(struct net_device *ndev) 100562306a36Sopenharmony_ci{ 100662306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 100762306a36Sopenharmony_ci struct fec_enet_priv_tx_q *txq; 100862306a36Sopenharmony_ci struct fec_enet_priv_rx_q *rxq; 100962306a36Sopenharmony_ci int i; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci for (i = 0; i < fep->num_rx_queues; i++) { 101262306a36Sopenharmony_ci rxq = fep->rx_queue[i]; 101362306a36Sopenharmony_ci writel(rxq->bd.dma, fep->hwp + FEC_R_DES_START(i)); 101462306a36Sopenharmony_ci writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_R_BUFF_SIZE(i)); 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci /* enable DMA1/2 */ 101762306a36Sopenharmony_ci if (i) 101862306a36Sopenharmony_ci writel(RCMR_MATCHEN | RCMR_CMP(i), 101962306a36Sopenharmony_ci fep->hwp + FEC_RCMR(i)); 102062306a36Sopenharmony_ci } 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci for (i = 0; i < fep->num_tx_queues; i++) { 102362306a36Sopenharmony_ci txq = fep->tx_queue[i]; 102462306a36Sopenharmony_ci writel(txq->bd.dma, fep->hwp + FEC_X_DES_START(i)); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci /* enable DMA1/2 */ 102762306a36Sopenharmony_ci if (i) 102862306a36Sopenharmony_ci writel(DMA_CLASS_EN | IDLE_SLOPE(i), 102962306a36Sopenharmony_ci fep->hwp + FEC_DMA_CFG(i)); 103062306a36Sopenharmony_ci } 103162306a36Sopenharmony_ci} 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci/* 103462306a36Sopenharmony_ci * This function is called to start or restart the FEC during a link 103562306a36Sopenharmony_ci * change, transmit timeout, or to reconfigure the FEC. The network 103662306a36Sopenharmony_ci * packet processing for this device must be stopped before this call. 103762306a36Sopenharmony_ci */ 103862306a36Sopenharmony_cistatic void 103962306a36Sopenharmony_cifec_restart(struct net_device *ndev) 104062306a36Sopenharmony_ci{ 104162306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 104262306a36Sopenharmony_ci u32 temp_mac[2]; 104362306a36Sopenharmony_ci u32 rcntl = OPT_FRAME_SIZE | 0x04; 104462306a36Sopenharmony_ci u32 ecntl = 0x2; /* ETHEREN */ 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci /* Whack a reset. We should wait for this. 104762306a36Sopenharmony_ci * For i.MX6SX SOC, enet use AXI bus, we use disable MAC 104862306a36Sopenharmony_ci * instead of reset MAC itself. 104962306a36Sopenharmony_ci */ 105062306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES || 105162306a36Sopenharmony_ci ((fep->quirks & FEC_QUIRK_NO_HARD_RESET) && fep->link)) { 105262306a36Sopenharmony_ci writel(0, fep->hwp + FEC_ECNTRL); 105362306a36Sopenharmony_ci } else { 105462306a36Sopenharmony_ci writel(1, fep->hwp + FEC_ECNTRL); 105562306a36Sopenharmony_ci udelay(10); 105662306a36Sopenharmony_ci } 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci /* 105962306a36Sopenharmony_ci * enet-mac reset will reset mac address registers too, 106062306a36Sopenharmony_ci * so need to reconfigure it. 106162306a36Sopenharmony_ci */ 106262306a36Sopenharmony_ci memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN); 106362306a36Sopenharmony_ci writel((__force u32)cpu_to_be32(temp_mac[0]), 106462306a36Sopenharmony_ci fep->hwp + FEC_ADDR_LOW); 106562306a36Sopenharmony_ci writel((__force u32)cpu_to_be32(temp_mac[1]), 106662306a36Sopenharmony_ci fep->hwp + FEC_ADDR_HIGH); 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci /* Clear any outstanding interrupt, except MDIO. */ 106962306a36Sopenharmony_ci writel((0xffffffff & ~FEC_ENET_MII), fep->hwp + FEC_IEVENT); 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci fec_enet_bd_init(ndev); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci fec_enet_enable_ring(ndev); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci /* Enable MII mode */ 107662306a36Sopenharmony_ci if (fep->full_duplex == DUPLEX_FULL) { 107762306a36Sopenharmony_ci /* FD enable */ 107862306a36Sopenharmony_ci writel(0x04, fep->hwp + FEC_X_CNTRL); 107962306a36Sopenharmony_ci } else { 108062306a36Sopenharmony_ci /* No Rcv on Xmit */ 108162306a36Sopenharmony_ci rcntl |= 0x02; 108262306a36Sopenharmony_ci writel(0x0, fep->hwp + FEC_X_CNTRL); 108362306a36Sopenharmony_ci } 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci /* Set MII speed */ 108662306a36Sopenharmony_ci writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci#if !defined(CONFIG_M5272) 108962306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_RACC) { 109062306a36Sopenharmony_ci u32 val = readl(fep->hwp + FEC_RACC); 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci /* align IP header */ 109362306a36Sopenharmony_ci val |= FEC_RACC_SHIFT16; 109462306a36Sopenharmony_ci if (fep->csum_flags & FLAG_RX_CSUM_ENABLED) 109562306a36Sopenharmony_ci /* set RX checksum */ 109662306a36Sopenharmony_ci val |= FEC_RACC_OPTIONS; 109762306a36Sopenharmony_ci else 109862306a36Sopenharmony_ci val &= ~FEC_RACC_OPTIONS; 109962306a36Sopenharmony_ci writel(val, fep->hwp + FEC_RACC); 110062306a36Sopenharmony_ci writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_FTRL); 110162306a36Sopenharmony_ci } 110262306a36Sopenharmony_ci#endif 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci /* 110562306a36Sopenharmony_ci * The phy interface and speed need to get configured 110662306a36Sopenharmony_ci * differently on enet-mac. 110762306a36Sopenharmony_ci */ 110862306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_ENET_MAC) { 110962306a36Sopenharmony_ci /* Enable flow control and length check */ 111062306a36Sopenharmony_ci rcntl |= 0x40000000 | 0x00000020; 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci /* RGMII, RMII or MII */ 111362306a36Sopenharmony_ci if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII || 111462306a36Sopenharmony_ci fep->phy_interface == PHY_INTERFACE_MODE_RGMII_ID || 111562306a36Sopenharmony_ci fep->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID || 111662306a36Sopenharmony_ci fep->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID) 111762306a36Sopenharmony_ci rcntl |= (1 << 6); 111862306a36Sopenharmony_ci else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) 111962306a36Sopenharmony_ci rcntl |= (1 << 8); 112062306a36Sopenharmony_ci else 112162306a36Sopenharmony_ci rcntl &= ~(1 << 8); 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci /* 1G, 100M or 10M */ 112462306a36Sopenharmony_ci if (ndev->phydev) { 112562306a36Sopenharmony_ci if (ndev->phydev->speed == SPEED_1000) 112662306a36Sopenharmony_ci ecntl |= (1 << 5); 112762306a36Sopenharmony_ci else if (ndev->phydev->speed == SPEED_100) 112862306a36Sopenharmony_ci rcntl &= ~(1 << 9); 112962306a36Sopenharmony_ci else 113062306a36Sopenharmony_ci rcntl |= (1 << 9); 113162306a36Sopenharmony_ci } 113262306a36Sopenharmony_ci } else { 113362306a36Sopenharmony_ci#ifdef FEC_MIIGSK_ENR 113462306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_USE_GASKET) { 113562306a36Sopenharmony_ci u32 cfgr; 113662306a36Sopenharmony_ci /* disable the gasket and wait */ 113762306a36Sopenharmony_ci writel(0, fep->hwp + FEC_MIIGSK_ENR); 113862306a36Sopenharmony_ci while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4) 113962306a36Sopenharmony_ci udelay(1); 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci /* 114262306a36Sopenharmony_ci * configure the gasket: 114362306a36Sopenharmony_ci * RMII, 50 MHz, no loopback, no echo 114462306a36Sopenharmony_ci * MII, 25 MHz, no loopback, no echo 114562306a36Sopenharmony_ci */ 114662306a36Sopenharmony_ci cfgr = (fep->phy_interface == PHY_INTERFACE_MODE_RMII) 114762306a36Sopenharmony_ci ? BM_MIIGSK_CFGR_RMII : BM_MIIGSK_CFGR_MII; 114862306a36Sopenharmony_ci if (ndev->phydev && ndev->phydev->speed == SPEED_10) 114962306a36Sopenharmony_ci cfgr |= BM_MIIGSK_CFGR_FRCONT_10M; 115062306a36Sopenharmony_ci writel(cfgr, fep->hwp + FEC_MIIGSK_CFGR); 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci /* re-enable the gasket */ 115362306a36Sopenharmony_ci writel(2, fep->hwp + FEC_MIIGSK_ENR); 115462306a36Sopenharmony_ci } 115562306a36Sopenharmony_ci#endif 115662306a36Sopenharmony_ci } 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci#if !defined(CONFIG_M5272) 115962306a36Sopenharmony_ci /* enable pause frame*/ 116062306a36Sopenharmony_ci if ((fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) || 116162306a36Sopenharmony_ci ((fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) && 116262306a36Sopenharmony_ci ndev->phydev && ndev->phydev->pause)) { 116362306a36Sopenharmony_ci rcntl |= FEC_ENET_FCE; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci /* set FIFO threshold parameter to reduce overrun */ 116662306a36Sopenharmony_ci writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM); 116762306a36Sopenharmony_ci writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL); 116862306a36Sopenharmony_ci writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM); 116962306a36Sopenharmony_ci writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL); 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci /* OPD */ 117262306a36Sopenharmony_ci writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD); 117362306a36Sopenharmony_ci } else { 117462306a36Sopenharmony_ci rcntl &= ~FEC_ENET_FCE; 117562306a36Sopenharmony_ci } 117662306a36Sopenharmony_ci#endif /* !defined(CONFIG_M5272) */ 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci writel(rcntl, fep->hwp + FEC_R_CNTRL); 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci /* Setup multicast filter. */ 118162306a36Sopenharmony_ci set_multicast_list(ndev); 118262306a36Sopenharmony_ci#ifndef CONFIG_M5272 118362306a36Sopenharmony_ci writel(0, fep->hwp + FEC_HASH_TABLE_HIGH); 118462306a36Sopenharmony_ci writel(0, fep->hwp + FEC_HASH_TABLE_LOW); 118562306a36Sopenharmony_ci#endif 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_ENET_MAC) { 118862306a36Sopenharmony_ci /* enable ENET endian swap */ 118962306a36Sopenharmony_ci ecntl |= (1 << 8); 119062306a36Sopenharmony_ci /* enable ENET store and forward mode */ 119162306a36Sopenharmony_ci writel(1 << 8, fep->hwp + FEC_X_WMRK); 119262306a36Sopenharmony_ci } 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci if (fep->bufdesc_ex) 119562306a36Sopenharmony_ci ecntl |= (1 << 4); 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_DELAYED_CLKS_SUPPORT && 119862306a36Sopenharmony_ci fep->rgmii_txc_dly) 119962306a36Sopenharmony_ci ecntl |= FEC_ENET_TXC_DLY; 120062306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_DELAYED_CLKS_SUPPORT && 120162306a36Sopenharmony_ci fep->rgmii_rxc_dly) 120262306a36Sopenharmony_ci ecntl |= FEC_ENET_RXC_DLY; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci#ifndef CONFIG_M5272 120562306a36Sopenharmony_ci /* Enable the MIB statistic event counters */ 120662306a36Sopenharmony_ci writel(0 << 31, fep->hwp + FEC_MIB_CTRLSTAT); 120762306a36Sopenharmony_ci#endif 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci /* And last, enable the transmit and receive processing */ 121062306a36Sopenharmony_ci writel(ecntl, fep->hwp + FEC_ECNTRL); 121162306a36Sopenharmony_ci fec_enet_active_rxring(ndev); 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci if (fep->bufdesc_ex) 121462306a36Sopenharmony_ci fec_ptp_start_cyclecounter(ndev); 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci /* Enable interrupts we wish to service */ 121762306a36Sopenharmony_ci if (fep->link) 121862306a36Sopenharmony_ci writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 121962306a36Sopenharmony_ci else 122062306a36Sopenharmony_ci writel(0, fep->hwp + FEC_IMASK); 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci /* Init the interrupt coalescing */ 122362306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_COALESCE) 122462306a36Sopenharmony_ci fec_enet_itr_coal_set(ndev); 122562306a36Sopenharmony_ci} 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_cistatic int fec_enet_ipc_handle_init(struct fec_enet_private *fep) 122862306a36Sopenharmony_ci{ 122962306a36Sopenharmony_ci if (!(of_machine_is_compatible("fsl,imx8qm") || 123062306a36Sopenharmony_ci of_machine_is_compatible("fsl,imx8qxp") || 123162306a36Sopenharmony_ci of_machine_is_compatible("fsl,imx8dxl"))) 123262306a36Sopenharmony_ci return 0; 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci return imx_scu_get_handle(&fep->ipc_handle); 123562306a36Sopenharmony_ci} 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_cistatic void fec_enet_ipg_stop_set(struct fec_enet_private *fep, bool enabled) 123862306a36Sopenharmony_ci{ 123962306a36Sopenharmony_ci struct device_node *np = fep->pdev->dev.of_node; 124062306a36Sopenharmony_ci u32 rsrc_id, val; 124162306a36Sopenharmony_ci int idx; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci if (!np || !fep->ipc_handle) 124462306a36Sopenharmony_ci return; 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci idx = of_alias_get_id(np, "ethernet"); 124762306a36Sopenharmony_ci if (idx < 0) 124862306a36Sopenharmony_ci idx = 0; 124962306a36Sopenharmony_ci rsrc_id = idx ? IMX_SC_R_ENET_1 : IMX_SC_R_ENET_0; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci val = enabled ? 1 : 0; 125262306a36Sopenharmony_ci imx_sc_misc_set_control(fep->ipc_handle, rsrc_id, IMX_SC_C_IPG_STOP, val); 125362306a36Sopenharmony_ci} 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_cistatic void fec_enet_stop_mode(struct fec_enet_private *fep, bool enabled) 125662306a36Sopenharmony_ci{ 125762306a36Sopenharmony_ci struct fec_platform_data *pdata = fep->pdev->dev.platform_data; 125862306a36Sopenharmony_ci struct fec_stop_mode_gpr *stop_gpr = &fep->stop_gpr; 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci if (stop_gpr->gpr) { 126162306a36Sopenharmony_ci if (enabled) 126262306a36Sopenharmony_ci regmap_update_bits(stop_gpr->gpr, stop_gpr->reg, 126362306a36Sopenharmony_ci BIT(stop_gpr->bit), 126462306a36Sopenharmony_ci BIT(stop_gpr->bit)); 126562306a36Sopenharmony_ci else 126662306a36Sopenharmony_ci regmap_update_bits(stop_gpr->gpr, stop_gpr->reg, 126762306a36Sopenharmony_ci BIT(stop_gpr->bit), 0); 126862306a36Sopenharmony_ci } else if (pdata && pdata->sleep_mode_enable) { 126962306a36Sopenharmony_ci pdata->sleep_mode_enable(enabled); 127062306a36Sopenharmony_ci } else { 127162306a36Sopenharmony_ci fec_enet_ipg_stop_set(fep, enabled); 127262306a36Sopenharmony_ci } 127362306a36Sopenharmony_ci} 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_cistatic void fec_irqs_disable(struct net_device *ndev) 127662306a36Sopenharmony_ci{ 127762306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci writel(0, fep->hwp + FEC_IMASK); 128062306a36Sopenharmony_ci} 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_cistatic void fec_irqs_disable_except_wakeup(struct net_device *ndev) 128362306a36Sopenharmony_ci{ 128462306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci writel(0, fep->hwp + FEC_IMASK); 128762306a36Sopenharmony_ci writel(FEC_ENET_WAKEUP, fep->hwp + FEC_IMASK); 128862306a36Sopenharmony_ci} 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_cistatic void 129162306a36Sopenharmony_cifec_stop(struct net_device *ndev) 129262306a36Sopenharmony_ci{ 129362306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 129462306a36Sopenharmony_ci u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8); 129562306a36Sopenharmony_ci u32 val; 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci /* We cannot expect a graceful transmit stop without link !!! */ 129862306a36Sopenharmony_ci if (fep->link) { 129962306a36Sopenharmony_ci writel(1, fep->hwp + FEC_X_CNTRL); /* Graceful transmit stop */ 130062306a36Sopenharmony_ci udelay(10); 130162306a36Sopenharmony_ci if (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_GRA)) 130262306a36Sopenharmony_ci netdev_err(ndev, "Graceful transmit stop did not complete!\n"); 130362306a36Sopenharmony_ci } 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci /* Whack a reset. We should wait for this. 130662306a36Sopenharmony_ci * For i.MX6SX SOC, enet use AXI bus, we use disable MAC 130762306a36Sopenharmony_ci * instead of reset MAC itself. 130862306a36Sopenharmony_ci */ 130962306a36Sopenharmony_ci if (!(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { 131062306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) { 131162306a36Sopenharmony_ci writel(0, fep->hwp + FEC_ECNTRL); 131262306a36Sopenharmony_ci } else { 131362306a36Sopenharmony_ci writel(1, fep->hwp + FEC_ECNTRL); 131462306a36Sopenharmony_ci udelay(10); 131562306a36Sopenharmony_ci } 131662306a36Sopenharmony_ci } else { 131762306a36Sopenharmony_ci val = readl(fep->hwp + FEC_ECNTRL); 131862306a36Sopenharmony_ci val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP); 131962306a36Sopenharmony_ci writel(val, fep->hwp + FEC_ECNTRL); 132062306a36Sopenharmony_ci } 132162306a36Sopenharmony_ci writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 132262306a36Sopenharmony_ci writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci /* We have to keep ENET enabled to have MII interrupt stay working */ 132562306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_ENET_MAC && 132662306a36Sopenharmony_ci !(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { 132762306a36Sopenharmony_ci writel(2, fep->hwp + FEC_ECNTRL); 132862306a36Sopenharmony_ci writel(rmii_mode, fep->hwp + FEC_R_CNTRL); 132962306a36Sopenharmony_ci } 133062306a36Sopenharmony_ci} 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_cistatic void 133462306a36Sopenharmony_cifec_timeout(struct net_device *ndev, unsigned int txqueue) 133562306a36Sopenharmony_ci{ 133662306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci fec_dump(ndev); 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci ndev->stats.tx_errors++; 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci schedule_work(&fep->tx_timeout_work); 134362306a36Sopenharmony_ci} 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_cistatic void fec_enet_timeout_work(struct work_struct *work) 134662306a36Sopenharmony_ci{ 134762306a36Sopenharmony_ci struct fec_enet_private *fep = 134862306a36Sopenharmony_ci container_of(work, struct fec_enet_private, tx_timeout_work); 134962306a36Sopenharmony_ci struct net_device *ndev = fep->netdev; 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci rtnl_lock(); 135262306a36Sopenharmony_ci if (netif_device_present(ndev) || netif_running(ndev)) { 135362306a36Sopenharmony_ci napi_disable(&fep->napi); 135462306a36Sopenharmony_ci netif_tx_lock_bh(ndev); 135562306a36Sopenharmony_ci fec_restart(ndev); 135662306a36Sopenharmony_ci netif_tx_wake_all_queues(ndev); 135762306a36Sopenharmony_ci netif_tx_unlock_bh(ndev); 135862306a36Sopenharmony_ci napi_enable(&fep->napi); 135962306a36Sopenharmony_ci } 136062306a36Sopenharmony_ci rtnl_unlock(); 136162306a36Sopenharmony_ci} 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_cistatic void 136462306a36Sopenharmony_cifec_enet_hwtstamp(struct fec_enet_private *fep, unsigned ts, 136562306a36Sopenharmony_ci struct skb_shared_hwtstamps *hwtstamps) 136662306a36Sopenharmony_ci{ 136762306a36Sopenharmony_ci unsigned long flags; 136862306a36Sopenharmony_ci u64 ns; 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci spin_lock_irqsave(&fep->tmreg_lock, flags); 137162306a36Sopenharmony_ci ns = timecounter_cyc2time(&fep->tc, ts); 137262306a36Sopenharmony_ci spin_unlock_irqrestore(&fep->tmreg_lock, flags); 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci memset(hwtstamps, 0, sizeof(*hwtstamps)); 137562306a36Sopenharmony_ci hwtstamps->hwtstamp = ns_to_ktime(ns); 137662306a36Sopenharmony_ci} 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_cistatic void 137962306a36Sopenharmony_cifec_enet_tx_queue(struct net_device *ndev, u16 queue_id, int budget) 138062306a36Sopenharmony_ci{ 138162306a36Sopenharmony_ci struct fec_enet_private *fep; 138262306a36Sopenharmony_ci struct xdp_frame *xdpf; 138362306a36Sopenharmony_ci struct bufdesc *bdp; 138462306a36Sopenharmony_ci unsigned short status; 138562306a36Sopenharmony_ci struct sk_buff *skb; 138662306a36Sopenharmony_ci struct fec_enet_priv_tx_q *txq; 138762306a36Sopenharmony_ci struct netdev_queue *nq; 138862306a36Sopenharmony_ci int index = 0; 138962306a36Sopenharmony_ci int entries_free; 139062306a36Sopenharmony_ci struct page *page; 139162306a36Sopenharmony_ci int frame_len; 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci fep = netdev_priv(ndev); 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci txq = fep->tx_queue[queue_id]; 139662306a36Sopenharmony_ci /* get next bdp of dirty_tx */ 139762306a36Sopenharmony_ci nq = netdev_get_tx_queue(ndev, queue_id); 139862306a36Sopenharmony_ci bdp = txq->dirty_tx; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci /* get next bdp of dirty_tx */ 140162306a36Sopenharmony_ci bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci while (bdp != READ_ONCE(txq->bd.cur)) { 140462306a36Sopenharmony_ci /* Order the load of bd.cur and cbd_sc */ 140562306a36Sopenharmony_ci rmb(); 140662306a36Sopenharmony_ci status = fec16_to_cpu(READ_ONCE(bdp->cbd_sc)); 140762306a36Sopenharmony_ci if (status & BD_ENET_TX_READY) 140862306a36Sopenharmony_ci break; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci index = fec_enet_get_bd_index(bdp, &txq->bd); 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci if (txq->tx_buf[index].type == FEC_TXBUF_T_SKB) { 141362306a36Sopenharmony_ci skb = txq->tx_buf[index].buf_p; 141462306a36Sopenharmony_ci if (bdp->cbd_bufaddr && 141562306a36Sopenharmony_ci !IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr))) 141662306a36Sopenharmony_ci dma_unmap_single(&fep->pdev->dev, 141762306a36Sopenharmony_ci fec32_to_cpu(bdp->cbd_bufaddr), 141862306a36Sopenharmony_ci fec16_to_cpu(bdp->cbd_datlen), 141962306a36Sopenharmony_ci DMA_TO_DEVICE); 142062306a36Sopenharmony_ci bdp->cbd_bufaddr = cpu_to_fec32(0); 142162306a36Sopenharmony_ci if (!skb) 142262306a36Sopenharmony_ci goto tx_buf_done; 142362306a36Sopenharmony_ci } else { 142462306a36Sopenharmony_ci /* Tx processing cannot call any XDP (or page pool) APIs if 142562306a36Sopenharmony_ci * the "budget" is 0. Because NAPI is called with budget of 142662306a36Sopenharmony_ci * 0 (such as netpoll) indicates we may be in an IRQ context, 142762306a36Sopenharmony_ci * however, we can't use the page pool from IRQ context. 142862306a36Sopenharmony_ci */ 142962306a36Sopenharmony_ci if (unlikely(!budget)) 143062306a36Sopenharmony_ci break; 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci if (txq->tx_buf[index].type == FEC_TXBUF_T_XDP_NDO) { 143362306a36Sopenharmony_ci xdpf = txq->tx_buf[index].buf_p; 143462306a36Sopenharmony_ci if (bdp->cbd_bufaddr) 143562306a36Sopenharmony_ci dma_unmap_single(&fep->pdev->dev, 143662306a36Sopenharmony_ci fec32_to_cpu(bdp->cbd_bufaddr), 143762306a36Sopenharmony_ci fec16_to_cpu(bdp->cbd_datlen), 143862306a36Sopenharmony_ci DMA_TO_DEVICE); 143962306a36Sopenharmony_ci } else { 144062306a36Sopenharmony_ci page = txq->tx_buf[index].buf_p; 144162306a36Sopenharmony_ci } 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci bdp->cbd_bufaddr = cpu_to_fec32(0); 144462306a36Sopenharmony_ci if (unlikely(!txq->tx_buf[index].buf_p)) { 144562306a36Sopenharmony_ci txq->tx_buf[index].type = FEC_TXBUF_T_SKB; 144662306a36Sopenharmony_ci goto tx_buf_done; 144762306a36Sopenharmony_ci } 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci frame_len = fec16_to_cpu(bdp->cbd_datlen); 145062306a36Sopenharmony_ci } 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci /* Check for errors. */ 145362306a36Sopenharmony_ci if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC | 145462306a36Sopenharmony_ci BD_ENET_TX_RL | BD_ENET_TX_UN | 145562306a36Sopenharmony_ci BD_ENET_TX_CSL)) { 145662306a36Sopenharmony_ci ndev->stats.tx_errors++; 145762306a36Sopenharmony_ci if (status & BD_ENET_TX_HB) /* No heartbeat */ 145862306a36Sopenharmony_ci ndev->stats.tx_heartbeat_errors++; 145962306a36Sopenharmony_ci if (status & BD_ENET_TX_LC) /* Late collision */ 146062306a36Sopenharmony_ci ndev->stats.tx_window_errors++; 146162306a36Sopenharmony_ci if (status & BD_ENET_TX_RL) /* Retrans limit */ 146262306a36Sopenharmony_ci ndev->stats.tx_aborted_errors++; 146362306a36Sopenharmony_ci if (status & BD_ENET_TX_UN) /* Underrun */ 146462306a36Sopenharmony_ci ndev->stats.tx_fifo_errors++; 146562306a36Sopenharmony_ci if (status & BD_ENET_TX_CSL) /* Carrier lost */ 146662306a36Sopenharmony_ci ndev->stats.tx_carrier_errors++; 146762306a36Sopenharmony_ci } else { 146862306a36Sopenharmony_ci ndev->stats.tx_packets++; 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci if (txq->tx_buf[index].type == FEC_TXBUF_T_SKB) 147162306a36Sopenharmony_ci ndev->stats.tx_bytes += skb->len; 147262306a36Sopenharmony_ci else 147362306a36Sopenharmony_ci ndev->stats.tx_bytes += frame_len; 147462306a36Sopenharmony_ci } 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci /* Deferred means some collisions occurred during transmit, 147762306a36Sopenharmony_ci * but we eventually sent the packet OK. 147862306a36Sopenharmony_ci */ 147962306a36Sopenharmony_ci if (status & BD_ENET_TX_DEF) 148062306a36Sopenharmony_ci ndev->stats.collisions++; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci if (txq->tx_buf[index].type == FEC_TXBUF_T_SKB) { 148362306a36Sopenharmony_ci /* NOTE: SKBTX_IN_PROGRESS being set does not imply it's we who 148462306a36Sopenharmony_ci * are to time stamp the packet, so we still need to check time 148562306a36Sopenharmony_ci * stamping enabled flag. 148662306a36Sopenharmony_ci */ 148762306a36Sopenharmony_ci if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS && 148862306a36Sopenharmony_ci fep->hwts_tx_en) && fep->bufdesc_ex) { 148962306a36Sopenharmony_ci struct skb_shared_hwtstamps shhwtstamps; 149062306a36Sopenharmony_ci struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci fec_enet_hwtstamp(fep, fec32_to_cpu(ebdp->ts), &shhwtstamps); 149362306a36Sopenharmony_ci skb_tstamp_tx(skb, &shhwtstamps); 149462306a36Sopenharmony_ci } 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci /* Free the sk buffer associated with this last transmit */ 149762306a36Sopenharmony_ci napi_consume_skb(skb, budget); 149862306a36Sopenharmony_ci } else if (txq->tx_buf[index].type == FEC_TXBUF_T_XDP_NDO) { 149962306a36Sopenharmony_ci xdp_return_frame_rx_napi(xdpf); 150062306a36Sopenharmony_ci } else { /* recycle pages of XDP_TX frames */ 150162306a36Sopenharmony_ci /* The dma_sync_size = 0 as XDP_TX has already synced DMA for_device */ 150262306a36Sopenharmony_ci page_pool_put_page(page->pp, page, 0, true); 150362306a36Sopenharmony_ci } 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci txq->tx_buf[index].buf_p = NULL; 150662306a36Sopenharmony_ci /* restore default tx buffer type: FEC_TXBUF_T_SKB */ 150762306a36Sopenharmony_ci txq->tx_buf[index].type = FEC_TXBUF_T_SKB; 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_citx_buf_done: 151062306a36Sopenharmony_ci /* Make sure the update to bdp and tx_buf are performed 151162306a36Sopenharmony_ci * before dirty_tx 151262306a36Sopenharmony_ci */ 151362306a36Sopenharmony_ci wmb(); 151462306a36Sopenharmony_ci txq->dirty_tx = bdp; 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci /* Update pointer to next buffer descriptor to be transmitted */ 151762306a36Sopenharmony_ci bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci /* Since we have freed up a buffer, the ring is no longer full 152062306a36Sopenharmony_ci */ 152162306a36Sopenharmony_ci if (netif_tx_queue_stopped(nq)) { 152262306a36Sopenharmony_ci entries_free = fec_enet_get_free_txdesc_num(txq); 152362306a36Sopenharmony_ci if (entries_free >= txq->tx_wake_threshold) 152462306a36Sopenharmony_ci netif_tx_wake_queue(nq); 152562306a36Sopenharmony_ci } 152662306a36Sopenharmony_ci } 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci /* ERR006358: Keep the transmitter going */ 152962306a36Sopenharmony_ci if (bdp != txq->bd.cur && 153062306a36Sopenharmony_ci readl(txq->bd.reg_desc_active) == 0) 153162306a36Sopenharmony_ci writel(0, txq->bd.reg_desc_active); 153262306a36Sopenharmony_ci} 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_cistatic void fec_enet_tx(struct net_device *ndev, int budget) 153562306a36Sopenharmony_ci{ 153662306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 153762306a36Sopenharmony_ci int i; 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci /* Make sure that AVB queues are processed first. */ 154062306a36Sopenharmony_ci for (i = fep->num_tx_queues - 1; i >= 0; i--) 154162306a36Sopenharmony_ci fec_enet_tx_queue(ndev, i, budget); 154262306a36Sopenharmony_ci} 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_cistatic void fec_enet_update_cbd(struct fec_enet_priv_rx_q *rxq, 154562306a36Sopenharmony_ci struct bufdesc *bdp, int index) 154662306a36Sopenharmony_ci{ 154762306a36Sopenharmony_ci struct page *new_page; 154862306a36Sopenharmony_ci dma_addr_t phys_addr; 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci new_page = page_pool_dev_alloc_pages(rxq->page_pool); 155162306a36Sopenharmony_ci WARN_ON(!new_page); 155262306a36Sopenharmony_ci rxq->rx_skb_info[index].page = new_page; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci rxq->rx_skb_info[index].offset = FEC_ENET_XDP_HEADROOM; 155562306a36Sopenharmony_ci phys_addr = page_pool_get_dma_addr(new_page) + FEC_ENET_XDP_HEADROOM; 155662306a36Sopenharmony_ci bdp->cbd_bufaddr = cpu_to_fec32(phys_addr); 155762306a36Sopenharmony_ci} 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_cistatic u32 156062306a36Sopenharmony_cifec_enet_run_xdp(struct fec_enet_private *fep, struct bpf_prog *prog, 156162306a36Sopenharmony_ci struct xdp_buff *xdp, struct fec_enet_priv_rx_q *rxq, int cpu) 156262306a36Sopenharmony_ci{ 156362306a36Sopenharmony_ci unsigned int sync, len = xdp->data_end - xdp->data; 156462306a36Sopenharmony_ci u32 ret = FEC_ENET_XDP_PASS; 156562306a36Sopenharmony_ci struct page *page; 156662306a36Sopenharmony_ci int err; 156762306a36Sopenharmony_ci u32 act; 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci act = bpf_prog_run_xdp(prog, xdp); 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci /* Due xdp_adjust_tail and xdp_adjust_head: DMA sync for_device cover 157262306a36Sopenharmony_ci * max len CPU touch 157362306a36Sopenharmony_ci */ 157462306a36Sopenharmony_ci sync = xdp->data_end - xdp->data; 157562306a36Sopenharmony_ci sync = max(sync, len); 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci switch (act) { 157862306a36Sopenharmony_ci case XDP_PASS: 157962306a36Sopenharmony_ci rxq->stats[RX_XDP_PASS]++; 158062306a36Sopenharmony_ci ret = FEC_ENET_XDP_PASS; 158162306a36Sopenharmony_ci break; 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci case XDP_REDIRECT: 158462306a36Sopenharmony_ci rxq->stats[RX_XDP_REDIRECT]++; 158562306a36Sopenharmony_ci err = xdp_do_redirect(fep->netdev, xdp, prog); 158662306a36Sopenharmony_ci if (unlikely(err)) 158762306a36Sopenharmony_ci goto xdp_err; 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci ret = FEC_ENET_XDP_REDIR; 159062306a36Sopenharmony_ci break; 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci case XDP_TX: 159362306a36Sopenharmony_ci rxq->stats[RX_XDP_TX]++; 159462306a36Sopenharmony_ci err = fec_enet_xdp_tx_xmit(fep, cpu, xdp, sync); 159562306a36Sopenharmony_ci if (unlikely(err)) { 159662306a36Sopenharmony_ci rxq->stats[RX_XDP_TX_ERRORS]++; 159762306a36Sopenharmony_ci goto xdp_err; 159862306a36Sopenharmony_ci } 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci ret = FEC_ENET_XDP_TX; 160162306a36Sopenharmony_ci break; 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci default: 160462306a36Sopenharmony_ci bpf_warn_invalid_xdp_action(fep->netdev, prog, act); 160562306a36Sopenharmony_ci fallthrough; 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci case XDP_ABORTED: 160862306a36Sopenharmony_ci fallthrough; /* handle aborts by dropping packet */ 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci case XDP_DROP: 161162306a36Sopenharmony_ci rxq->stats[RX_XDP_DROP]++; 161262306a36Sopenharmony_cixdp_err: 161362306a36Sopenharmony_ci ret = FEC_ENET_XDP_CONSUMED; 161462306a36Sopenharmony_ci page = virt_to_head_page(xdp->data); 161562306a36Sopenharmony_ci page_pool_put_page(rxq->page_pool, page, sync, true); 161662306a36Sopenharmony_ci if (act != XDP_DROP) 161762306a36Sopenharmony_ci trace_xdp_exception(fep->netdev, prog, act); 161862306a36Sopenharmony_ci break; 161962306a36Sopenharmony_ci } 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci return ret; 162262306a36Sopenharmony_ci} 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci/* During a receive, the bd_rx.cur points to the current incoming buffer. 162562306a36Sopenharmony_ci * When we update through the ring, if the next incoming buffer has 162662306a36Sopenharmony_ci * not been given to the system, we just set the empty indicator, 162762306a36Sopenharmony_ci * effectively tossing the packet. 162862306a36Sopenharmony_ci */ 162962306a36Sopenharmony_cistatic int 163062306a36Sopenharmony_cifec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) 163162306a36Sopenharmony_ci{ 163262306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 163362306a36Sopenharmony_ci struct fec_enet_priv_rx_q *rxq; 163462306a36Sopenharmony_ci struct bufdesc *bdp; 163562306a36Sopenharmony_ci unsigned short status; 163662306a36Sopenharmony_ci struct sk_buff *skb; 163762306a36Sopenharmony_ci ushort pkt_len; 163862306a36Sopenharmony_ci __u8 *data; 163962306a36Sopenharmony_ci int pkt_received = 0; 164062306a36Sopenharmony_ci struct bufdesc_ex *ebdp = NULL; 164162306a36Sopenharmony_ci bool vlan_packet_rcvd = false; 164262306a36Sopenharmony_ci u16 vlan_tag; 164362306a36Sopenharmony_ci int index = 0; 164462306a36Sopenharmony_ci bool need_swap = fep->quirks & FEC_QUIRK_SWAP_FRAME; 164562306a36Sopenharmony_ci struct bpf_prog *xdp_prog = READ_ONCE(fep->xdp_prog); 164662306a36Sopenharmony_ci u32 ret, xdp_result = FEC_ENET_XDP_PASS; 164762306a36Sopenharmony_ci u32 data_start = FEC_ENET_XDP_HEADROOM; 164862306a36Sopenharmony_ci int cpu = smp_processor_id(); 164962306a36Sopenharmony_ci struct xdp_buff xdp; 165062306a36Sopenharmony_ci struct page *page; 165162306a36Sopenharmony_ci u32 sub_len = 4; 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci#if !defined(CONFIG_M5272) 165462306a36Sopenharmony_ci /*If it has the FEC_QUIRK_HAS_RACC quirk property, the bit of 165562306a36Sopenharmony_ci * FEC_RACC_SHIFT16 is set by default in the probe function. 165662306a36Sopenharmony_ci */ 165762306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_RACC) { 165862306a36Sopenharmony_ci data_start += 2; 165962306a36Sopenharmony_ci sub_len += 2; 166062306a36Sopenharmony_ci } 166162306a36Sopenharmony_ci#endif 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci#ifdef CONFIG_M532x 166462306a36Sopenharmony_ci flush_cache_all(); 166562306a36Sopenharmony_ci#endif 166662306a36Sopenharmony_ci rxq = fep->rx_queue[queue_id]; 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci /* First, grab all of the stats for the incoming packet. 166962306a36Sopenharmony_ci * These get messed up if we get called due to a busy condition. 167062306a36Sopenharmony_ci */ 167162306a36Sopenharmony_ci bdp = rxq->bd.cur; 167262306a36Sopenharmony_ci xdp_init_buff(&xdp, PAGE_SIZE, &rxq->xdp_rxq); 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci while (!((status = fec16_to_cpu(bdp->cbd_sc)) & BD_ENET_RX_EMPTY)) { 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci if (pkt_received >= budget) 167762306a36Sopenharmony_ci break; 167862306a36Sopenharmony_ci pkt_received++; 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci writel(FEC_ENET_RXF_GET(queue_id), fep->hwp + FEC_IEVENT); 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ci /* Check for errors. */ 168362306a36Sopenharmony_ci status ^= BD_ENET_RX_LAST; 168462306a36Sopenharmony_ci if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | 168562306a36Sopenharmony_ci BD_ENET_RX_CR | BD_ENET_RX_OV | BD_ENET_RX_LAST | 168662306a36Sopenharmony_ci BD_ENET_RX_CL)) { 168762306a36Sopenharmony_ci ndev->stats.rx_errors++; 168862306a36Sopenharmony_ci if (status & BD_ENET_RX_OV) { 168962306a36Sopenharmony_ci /* FIFO overrun */ 169062306a36Sopenharmony_ci ndev->stats.rx_fifo_errors++; 169162306a36Sopenharmony_ci goto rx_processing_done; 169262306a36Sopenharmony_ci } 169362306a36Sopenharmony_ci if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH 169462306a36Sopenharmony_ci | BD_ENET_RX_LAST)) { 169562306a36Sopenharmony_ci /* Frame too long or too short. */ 169662306a36Sopenharmony_ci ndev->stats.rx_length_errors++; 169762306a36Sopenharmony_ci if (status & BD_ENET_RX_LAST) 169862306a36Sopenharmony_ci netdev_err(ndev, "rcv is not +last\n"); 169962306a36Sopenharmony_ci } 170062306a36Sopenharmony_ci if (status & BD_ENET_RX_CR) /* CRC Error */ 170162306a36Sopenharmony_ci ndev->stats.rx_crc_errors++; 170262306a36Sopenharmony_ci /* Report late collisions as a frame error. */ 170362306a36Sopenharmony_ci if (status & (BD_ENET_RX_NO | BD_ENET_RX_CL)) 170462306a36Sopenharmony_ci ndev->stats.rx_frame_errors++; 170562306a36Sopenharmony_ci goto rx_processing_done; 170662306a36Sopenharmony_ci } 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci /* Process the incoming frame. */ 170962306a36Sopenharmony_ci ndev->stats.rx_packets++; 171062306a36Sopenharmony_ci pkt_len = fec16_to_cpu(bdp->cbd_datlen); 171162306a36Sopenharmony_ci ndev->stats.rx_bytes += pkt_len; 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_ci index = fec_enet_get_bd_index(bdp, &rxq->bd); 171462306a36Sopenharmony_ci page = rxq->rx_skb_info[index].page; 171562306a36Sopenharmony_ci dma_sync_single_for_cpu(&fep->pdev->dev, 171662306a36Sopenharmony_ci fec32_to_cpu(bdp->cbd_bufaddr), 171762306a36Sopenharmony_ci pkt_len, 171862306a36Sopenharmony_ci DMA_FROM_DEVICE); 171962306a36Sopenharmony_ci prefetch(page_address(page)); 172062306a36Sopenharmony_ci fec_enet_update_cbd(rxq, bdp, index); 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci if (xdp_prog) { 172362306a36Sopenharmony_ci xdp_buff_clear_frags_flag(&xdp); 172462306a36Sopenharmony_ci /* subtract 16bit shift and FCS */ 172562306a36Sopenharmony_ci xdp_prepare_buff(&xdp, page_address(page), 172662306a36Sopenharmony_ci data_start, pkt_len - sub_len, false); 172762306a36Sopenharmony_ci ret = fec_enet_run_xdp(fep, xdp_prog, &xdp, rxq, cpu); 172862306a36Sopenharmony_ci xdp_result |= ret; 172962306a36Sopenharmony_ci if (ret != FEC_ENET_XDP_PASS) 173062306a36Sopenharmony_ci goto rx_processing_done; 173162306a36Sopenharmony_ci } 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_ci /* The packet length includes FCS, but we don't want to 173462306a36Sopenharmony_ci * include that when passing upstream as it messes up 173562306a36Sopenharmony_ci * bridging applications. 173662306a36Sopenharmony_ci */ 173762306a36Sopenharmony_ci skb = build_skb(page_address(page), PAGE_SIZE); 173862306a36Sopenharmony_ci if (unlikely(!skb)) { 173962306a36Sopenharmony_ci page_pool_recycle_direct(rxq->page_pool, page); 174062306a36Sopenharmony_ci ndev->stats.rx_dropped++; 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci netdev_err_once(ndev, "build_skb failed!\n"); 174362306a36Sopenharmony_ci goto rx_processing_done; 174462306a36Sopenharmony_ci } 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_ci skb_reserve(skb, data_start); 174762306a36Sopenharmony_ci skb_put(skb, pkt_len - sub_len); 174862306a36Sopenharmony_ci skb_mark_for_recycle(skb); 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci if (unlikely(need_swap)) { 175162306a36Sopenharmony_ci data = page_address(page) + FEC_ENET_XDP_HEADROOM; 175262306a36Sopenharmony_ci swap_buffer(data, pkt_len); 175362306a36Sopenharmony_ci } 175462306a36Sopenharmony_ci data = skb->data; 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci /* Extract the enhanced buffer descriptor */ 175762306a36Sopenharmony_ci ebdp = NULL; 175862306a36Sopenharmony_ci if (fep->bufdesc_ex) 175962306a36Sopenharmony_ci ebdp = (struct bufdesc_ex *)bdp; 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci /* If this is a VLAN packet remove the VLAN Tag */ 176262306a36Sopenharmony_ci vlan_packet_rcvd = false; 176362306a36Sopenharmony_ci if ((ndev->features & NETIF_F_HW_VLAN_CTAG_RX) && 176462306a36Sopenharmony_ci fep->bufdesc_ex && 176562306a36Sopenharmony_ci (ebdp->cbd_esc & cpu_to_fec32(BD_ENET_RX_VLAN))) { 176662306a36Sopenharmony_ci /* Push and remove the vlan tag */ 176762306a36Sopenharmony_ci struct vlan_hdr *vlan_header = 176862306a36Sopenharmony_ci (struct vlan_hdr *) (data + ETH_HLEN); 176962306a36Sopenharmony_ci vlan_tag = ntohs(vlan_header->h_vlan_TCI); 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci vlan_packet_rcvd = true; 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci memmove(skb->data + VLAN_HLEN, data, ETH_ALEN * 2); 177462306a36Sopenharmony_ci skb_pull(skb, VLAN_HLEN); 177562306a36Sopenharmony_ci } 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, ndev); 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci /* Get receive timestamp from the skb */ 178062306a36Sopenharmony_ci if (fep->hwts_rx_en && fep->bufdesc_ex) 178162306a36Sopenharmony_ci fec_enet_hwtstamp(fep, fec32_to_cpu(ebdp->ts), 178262306a36Sopenharmony_ci skb_hwtstamps(skb)); 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci if (fep->bufdesc_ex && 178562306a36Sopenharmony_ci (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { 178662306a36Sopenharmony_ci if (!(ebdp->cbd_esc & cpu_to_fec32(FLAG_RX_CSUM_ERROR))) { 178762306a36Sopenharmony_ci /* don't check it */ 178862306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 178962306a36Sopenharmony_ci } else { 179062306a36Sopenharmony_ci skb_checksum_none_assert(skb); 179162306a36Sopenharmony_ci } 179262306a36Sopenharmony_ci } 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci /* Handle received VLAN packets */ 179562306a36Sopenharmony_ci if (vlan_packet_rcvd) 179662306a36Sopenharmony_ci __vlan_hwaccel_put_tag(skb, 179762306a36Sopenharmony_ci htons(ETH_P_8021Q), 179862306a36Sopenharmony_ci vlan_tag); 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci skb_record_rx_queue(skb, queue_id); 180162306a36Sopenharmony_ci napi_gro_receive(&fep->napi, skb); 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_cirx_processing_done: 180462306a36Sopenharmony_ci /* Clear the status flags for this buffer */ 180562306a36Sopenharmony_ci status &= ~BD_ENET_RX_STATS; 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_ci /* Mark the buffer empty */ 180862306a36Sopenharmony_ci status |= BD_ENET_RX_EMPTY; 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci if (fep->bufdesc_ex) { 181162306a36Sopenharmony_ci struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci ebdp->cbd_esc = cpu_to_fec32(BD_ENET_RX_INT); 181462306a36Sopenharmony_ci ebdp->cbd_prot = 0; 181562306a36Sopenharmony_ci ebdp->cbd_bdu = 0; 181662306a36Sopenharmony_ci } 181762306a36Sopenharmony_ci /* Make sure the updates to rest of the descriptor are 181862306a36Sopenharmony_ci * performed before transferring ownership. 181962306a36Sopenharmony_ci */ 182062306a36Sopenharmony_ci wmb(); 182162306a36Sopenharmony_ci bdp->cbd_sc = cpu_to_fec16(status); 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci /* Update BD pointer to next entry */ 182462306a36Sopenharmony_ci bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci /* Doing this here will keep the FEC running while we process 182762306a36Sopenharmony_ci * incoming frames. On a heavily loaded network, we should be 182862306a36Sopenharmony_ci * able to keep up at the expense of system resources. 182962306a36Sopenharmony_ci */ 183062306a36Sopenharmony_ci writel(0, rxq->bd.reg_desc_active); 183162306a36Sopenharmony_ci } 183262306a36Sopenharmony_ci rxq->bd.cur = bdp; 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci if (xdp_result & FEC_ENET_XDP_REDIR) 183562306a36Sopenharmony_ci xdp_do_flush_map(); 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci return pkt_received; 183862306a36Sopenharmony_ci} 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_cistatic int fec_enet_rx(struct net_device *ndev, int budget) 184162306a36Sopenharmony_ci{ 184262306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 184362306a36Sopenharmony_ci int i, done = 0; 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci /* Make sure that AVB queues are processed first. */ 184662306a36Sopenharmony_ci for (i = fep->num_rx_queues - 1; i >= 0; i--) 184762306a36Sopenharmony_ci done += fec_enet_rx_queue(ndev, budget - done, i); 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci return done; 185062306a36Sopenharmony_ci} 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_cistatic bool fec_enet_collect_events(struct fec_enet_private *fep) 185362306a36Sopenharmony_ci{ 185462306a36Sopenharmony_ci uint int_events; 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci int_events = readl(fep->hwp + FEC_IEVENT); 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci /* Don't clear MDIO events, we poll for those */ 185962306a36Sopenharmony_ci int_events &= ~FEC_ENET_MII; 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci writel(int_events, fep->hwp + FEC_IEVENT); 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci return int_events != 0; 186462306a36Sopenharmony_ci} 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_cistatic irqreturn_t 186762306a36Sopenharmony_cifec_enet_interrupt(int irq, void *dev_id) 186862306a36Sopenharmony_ci{ 186962306a36Sopenharmony_ci struct net_device *ndev = dev_id; 187062306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 187162306a36Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci if (fec_enet_collect_events(fep) && fep->link) { 187462306a36Sopenharmony_ci ret = IRQ_HANDLED; 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci if (napi_schedule_prep(&fep->napi)) { 187762306a36Sopenharmony_ci /* Disable interrupts */ 187862306a36Sopenharmony_ci writel(0, fep->hwp + FEC_IMASK); 187962306a36Sopenharmony_ci __napi_schedule(&fep->napi); 188062306a36Sopenharmony_ci } 188162306a36Sopenharmony_ci } 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci return ret; 188462306a36Sopenharmony_ci} 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_cistatic int fec_enet_rx_napi(struct napi_struct *napi, int budget) 188762306a36Sopenharmony_ci{ 188862306a36Sopenharmony_ci struct net_device *ndev = napi->dev; 188962306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 189062306a36Sopenharmony_ci int done = 0; 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci do { 189362306a36Sopenharmony_ci done += fec_enet_rx(ndev, budget - done); 189462306a36Sopenharmony_ci fec_enet_tx(ndev, budget); 189562306a36Sopenharmony_ci } while ((done < budget) && fec_enet_collect_events(fep)); 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci if (done < budget) { 189862306a36Sopenharmony_ci napi_complete_done(napi, done); 189962306a36Sopenharmony_ci writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 190062306a36Sopenharmony_ci } 190162306a36Sopenharmony_ci 190262306a36Sopenharmony_ci return done; 190362306a36Sopenharmony_ci} 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci/* ------------------------------------------------------------------------- */ 190662306a36Sopenharmony_cistatic int fec_get_mac(struct net_device *ndev) 190762306a36Sopenharmony_ci{ 190862306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 190962306a36Sopenharmony_ci unsigned char *iap, tmpaddr[ETH_ALEN]; 191062306a36Sopenharmony_ci int ret; 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci /* 191362306a36Sopenharmony_ci * try to get mac address in following order: 191462306a36Sopenharmony_ci * 191562306a36Sopenharmony_ci * 1) module parameter via kernel command line in form 191662306a36Sopenharmony_ci * fec.macaddr=0x00,0x04,0x9f,0x01,0x30,0xe0 191762306a36Sopenharmony_ci */ 191862306a36Sopenharmony_ci iap = macaddr; 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci /* 192162306a36Sopenharmony_ci * 2) from device tree data 192262306a36Sopenharmony_ci */ 192362306a36Sopenharmony_ci if (!is_valid_ether_addr(iap)) { 192462306a36Sopenharmony_ci struct device_node *np = fep->pdev->dev.of_node; 192562306a36Sopenharmony_ci if (np) { 192662306a36Sopenharmony_ci ret = of_get_mac_address(np, tmpaddr); 192762306a36Sopenharmony_ci if (!ret) 192862306a36Sopenharmony_ci iap = tmpaddr; 192962306a36Sopenharmony_ci else if (ret == -EPROBE_DEFER) 193062306a36Sopenharmony_ci return ret; 193162306a36Sopenharmony_ci } 193262306a36Sopenharmony_ci } 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci /* 193562306a36Sopenharmony_ci * 3) from flash or fuse (via platform data) 193662306a36Sopenharmony_ci */ 193762306a36Sopenharmony_ci if (!is_valid_ether_addr(iap)) { 193862306a36Sopenharmony_ci#ifdef CONFIG_M5272 193962306a36Sopenharmony_ci if (FEC_FLASHMAC) 194062306a36Sopenharmony_ci iap = (unsigned char *)FEC_FLASHMAC; 194162306a36Sopenharmony_ci#else 194262306a36Sopenharmony_ci struct fec_platform_data *pdata = dev_get_platdata(&fep->pdev->dev); 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci if (pdata) 194562306a36Sopenharmony_ci iap = (unsigned char *)&pdata->mac; 194662306a36Sopenharmony_ci#endif 194762306a36Sopenharmony_ci } 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_ci /* 195062306a36Sopenharmony_ci * 4) FEC mac registers set by bootloader 195162306a36Sopenharmony_ci */ 195262306a36Sopenharmony_ci if (!is_valid_ether_addr(iap)) { 195362306a36Sopenharmony_ci *((__be32 *) &tmpaddr[0]) = 195462306a36Sopenharmony_ci cpu_to_be32(readl(fep->hwp + FEC_ADDR_LOW)); 195562306a36Sopenharmony_ci *((__be16 *) &tmpaddr[4]) = 195662306a36Sopenharmony_ci cpu_to_be16(readl(fep->hwp + FEC_ADDR_HIGH) >> 16); 195762306a36Sopenharmony_ci iap = &tmpaddr[0]; 195862306a36Sopenharmony_ci } 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_ci /* 196162306a36Sopenharmony_ci * 5) random mac address 196262306a36Sopenharmony_ci */ 196362306a36Sopenharmony_ci if (!is_valid_ether_addr(iap)) { 196462306a36Sopenharmony_ci /* Report it and use a random ethernet address instead */ 196562306a36Sopenharmony_ci dev_err(&fep->pdev->dev, "Invalid MAC address: %pM\n", iap); 196662306a36Sopenharmony_ci eth_hw_addr_random(ndev); 196762306a36Sopenharmony_ci dev_info(&fep->pdev->dev, "Using random MAC address: %pM\n", 196862306a36Sopenharmony_ci ndev->dev_addr); 196962306a36Sopenharmony_ci return 0; 197062306a36Sopenharmony_ci } 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci /* Adjust MAC if using macaddr */ 197362306a36Sopenharmony_ci eth_hw_addr_gen(ndev, iap, iap == macaddr ? fep->dev_id : 0); 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_ci return 0; 197662306a36Sopenharmony_ci} 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_ci/* ------------------------------------------------------------------------- */ 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ci/* 198162306a36Sopenharmony_ci * Phy section 198262306a36Sopenharmony_ci */ 198362306a36Sopenharmony_cistatic void fec_enet_adjust_link(struct net_device *ndev) 198462306a36Sopenharmony_ci{ 198562306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 198662306a36Sopenharmony_ci struct phy_device *phy_dev = ndev->phydev; 198762306a36Sopenharmony_ci int status_change = 0; 198862306a36Sopenharmony_ci 198962306a36Sopenharmony_ci /* 199062306a36Sopenharmony_ci * If the netdev is down, or is going down, we're not interested 199162306a36Sopenharmony_ci * in link state events, so just mark our idea of the link as down 199262306a36Sopenharmony_ci * and ignore the event. 199362306a36Sopenharmony_ci */ 199462306a36Sopenharmony_ci if (!netif_running(ndev) || !netif_device_present(ndev)) { 199562306a36Sopenharmony_ci fep->link = 0; 199662306a36Sopenharmony_ci } else if (phy_dev->link) { 199762306a36Sopenharmony_ci if (!fep->link) { 199862306a36Sopenharmony_ci fep->link = phy_dev->link; 199962306a36Sopenharmony_ci status_change = 1; 200062306a36Sopenharmony_ci } 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci if (fep->full_duplex != phy_dev->duplex) { 200362306a36Sopenharmony_ci fep->full_duplex = phy_dev->duplex; 200462306a36Sopenharmony_ci status_change = 1; 200562306a36Sopenharmony_ci } 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci if (phy_dev->speed != fep->speed) { 200862306a36Sopenharmony_ci fep->speed = phy_dev->speed; 200962306a36Sopenharmony_ci status_change = 1; 201062306a36Sopenharmony_ci } 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci /* if any of the above changed restart the FEC */ 201362306a36Sopenharmony_ci if (status_change) { 201462306a36Sopenharmony_ci netif_stop_queue(ndev); 201562306a36Sopenharmony_ci napi_disable(&fep->napi); 201662306a36Sopenharmony_ci netif_tx_lock_bh(ndev); 201762306a36Sopenharmony_ci fec_restart(ndev); 201862306a36Sopenharmony_ci netif_tx_wake_all_queues(ndev); 201962306a36Sopenharmony_ci netif_tx_unlock_bh(ndev); 202062306a36Sopenharmony_ci napi_enable(&fep->napi); 202162306a36Sopenharmony_ci } 202262306a36Sopenharmony_ci } else { 202362306a36Sopenharmony_ci if (fep->link) { 202462306a36Sopenharmony_ci netif_stop_queue(ndev); 202562306a36Sopenharmony_ci napi_disable(&fep->napi); 202662306a36Sopenharmony_ci netif_tx_lock_bh(ndev); 202762306a36Sopenharmony_ci fec_stop(ndev); 202862306a36Sopenharmony_ci netif_tx_unlock_bh(ndev); 202962306a36Sopenharmony_ci napi_enable(&fep->napi); 203062306a36Sopenharmony_ci fep->link = phy_dev->link; 203162306a36Sopenharmony_ci status_change = 1; 203262306a36Sopenharmony_ci } 203362306a36Sopenharmony_ci } 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci if (status_change) 203662306a36Sopenharmony_ci phy_print_status(phy_dev); 203762306a36Sopenharmony_ci} 203862306a36Sopenharmony_ci 203962306a36Sopenharmony_cistatic int fec_enet_mdio_wait(struct fec_enet_private *fep) 204062306a36Sopenharmony_ci{ 204162306a36Sopenharmony_ci uint ievent; 204262306a36Sopenharmony_ci int ret; 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci ret = readl_poll_timeout_atomic(fep->hwp + FEC_IEVENT, ievent, 204562306a36Sopenharmony_ci ievent & FEC_ENET_MII, 2, 30000); 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_ci if (!ret) 204862306a36Sopenharmony_ci writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT); 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_ci return ret; 205162306a36Sopenharmony_ci} 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_cistatic int fec_enet_mdio_read_c22(struct mii_bus *bus, int mii_id, int regnum) 205462306a36Sopenharmony_ci{ 205562306a36Sopenharmony_ci struct fec_enet_private *fep = bus->priv; 205662306a36Sopenharmony_ci struct device *dev = &fep->pdev->dev; 205762306a36Sopenharmony_ci int ret = 0, frame_start, frame_addr, frame_op; 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(dev); 206062306a36Sopenharmony_ci if (ret < 0) 206162306a36Sopenharmony_ci return ret; 206262306a36Sopenharmony_ci 206362306a36Sopenharmony_ci /* C22 read */ 206462306a36Sopenharmony_ci frame_op = FEC_MMFR_OP_READ; 206562306a36Sopenharmony_ci frame_start = FEC_MMFR_ST; 206662306a36Sopenharmony_ci frame_addr = regnum; 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_ci /* start a read op */ 206962306a36Sopenharmony_ci writel(frame_start | frame_op | 207062306a36Sopenharmony_ci FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | 207162306a36Sopenharmony_ci FEC_MMFR_TA, fep->hwp + FEC_MII_DATA); 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_ci /* wait for end of transfer */ 207462306a36Sopenharmony_ci ret = fec_enet_mdio_wait(fep); 207562306a36Sopenharmony_ci if (ret) { 207662306a36Sopenharmony_ci netdev_err(fep->netdev, "MDIO read timeout\n"); 207762306a36Sopenharmony_ci goto out; 207862306a36Sopenharmony_ci } 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_ci ret = FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA)); 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_ciout: 208362306a36Sopenharmony_ci pm_runtime_mark_last_busy(dev); 208462306a36Sopenharmony_ci pm_runtime_put_autosuspend(dev); 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci return ret; 208762306a36Sopenharmony_ci} 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_cistatic int fec_enet_mdio_read_c45(struct mii_bus *bus, int mii_id, 209062306a36Sopenharmony_ci int devad, int regnum) 209162306a36Sopenharmony_ci{ 209262306a36Sopenharmony_ci struct fec_enet_private *fep = bus->priv; 209362306a36Sopenharmony_ci struct device *dev = &fep->pdev->dev; 209462306a36Sopenharmony_ci int ret = 0, frame_start, frame_op; 209562306a36Sopenharmony_ci 209662306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(dev); 209762306a36Sopenharmony_ci if (ret < 0) 209862306a36Sopenharmony_ci return ret; 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci frame_start = FEC_MMFR_ST_C45; 210162306a36Sopenharmony_ci 210262306a36Sopenharmony_ci /* write address */ 210362306a36Sopenharmony_ci writel(frame_start | FEC_MMFR_OP_ADDR_WRITE | 210462306a36Sopenharmony_ci FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(devad) | 210562306a36Sopenharmony_ci FEC_MMFR_TA | (regnum & 0xFFFF), 210662306a36Sopenharmony_ci fep->hwp + FEC_MII_DATA); 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_ci /* wait for end of transfer */ 210962306a36Sopenharmony_ci ret = fec_enet_mdio_wait(fep); 211062306a36Sopenharmony_ci if (ret) { 211162306a36Sopenharmony_ci netdev_err(fep->netdev, "MDIO address write timeout\n"); 211262306a36Sopenharmony_ci goto out; 211362306a36Sopenharmony_ci } 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci frame_op = FEC_MMFR_OP_READ_C45; 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_ci /* start a read op */ 211862306a36Sopenharmony_ci writel(frame_start | frame_op | 211962306a36Sopenharmony_ci FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(devad) | 212062306a36Sopenharmony_ci FEC_MMFR_TA, fep->hwp + FEC_MII_DATA); 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci /* wait for end of transfer */ 212362306a36Sopenharmony_ci ret = fec_enet_mdio_wait(fep); 212462306a36Sopenharmony_ci if (ret) { 212562306a36Sopenharmony_ci netdev_err(fep->netdev, "MDIO read timeout\n"); 212662306a36Sopenharmony_ci goto out; 212762306a36Sopenharmony_ci } 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_ci ret = FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA)); 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_ciout: 213262306a36Sopenharmony_ci pm_runtime_mark_last_busy(dev); 213362306a36Sopenharmony_ci pm_runtime_put_autosuspend(dev); 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci return ret; 213662306a36Sopenharmony_ci} 213762306a36Sopenharmony_ci 213862306a36Sopenharmony_cistatic int fec_enet_mdio_write_c22(struct mii_bus *bus, int mii_id, int regnum, 213962306a36Sopenharmony_ci u16 value) 214062306a36Sopenharmony_ci{ 214162306a36Sopenharmony_ci struct fec_enet_private *fep = bus->priv; 214262306a36Sopenharmony_ci struct device *dev = &fep->pdev->dev; 214362306a36Sopenharmony_ci int ret, frame_start, frame_addr; 214462306a36Sopenharmony_ci 214562306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(dev); 214662306a36Sopenharmony_ci if (ret < 0) 214762306a36Sopenharmony_ci return ret; 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci /* C22 write */ 215062306a36Sopenharmony_ci frame_start = FEC_MMFR_ST; 215162306a36Sopenharmony_ci frame_addr = regnum; 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_ci /* start a write op */ 215462306a36Sopenharmony_ci writel(frame_start | FEC_MMFR_OP_WRITE | 215562306a36Sopenharmony_ci FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | 215662306a36Sopenharmony_ci FEC_MMFR_TA | FEC_MMFR_DATA(value), 215762306a36Sopenharmony_ci fep->hwp + FEC_MII_DATA); 215862306a36Sopenharmony_ci 215962306a36Sopenharmony_ci /* wait for end of transfer */ 216062306a36Sopenharmony_ci ret = fec_enet_mdio_wait(fep); 216162306a36Sopenharmony_ci if (ret) 216262306a36Sopenharmony_ci netdev_err(fep->netdev, "MDIO write timeout\n"); 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci pm_runtime_mark_last_busy(dev); 216562306a36Sopenharmony_ci pm_runtime_put_autosuspend(dev); 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ci return ret; 216862306a36Sopenharmony_ci} 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_cistatic int fec_enet_mdio_write_c45(struct mii_bus *bus, int mii_id, 217162306a36Sopenharmony_ci int devad, int regnum, u16 value) 217262306a36Sopenharmony_ci{ 217362306a36Sopenharmony_ci struct fec_enet_private *fep = bus->priv; 217462306a36Sopenharmony_ci struct device *dev = &fep->pdev->dev; 217562306a36Sopenharmony_ci int ret, frame_start; 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(dev); 217862306a36Sopenharmony_ci if (ret < 0) 217962306a36Sopenharmony_ci return ret; 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_ci frame_start = FEC_MMFR_ST_C45; 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_ci /* write address */ 218462306a36Sopenharmony_ci writel(frame_start | FEC_MMFR_OP_ADDR_WRITE | 218562306a36Sopenharmony_ci FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(devad) | 218662306a36Sopenharmony_ci FEC_MMFR_TA | (regnum & 0xFFFF), 218762306a36Sopenharmony_ci fep->hwp + FEC_MII_DATA); 218862306a36Sopenharmony_ci 218962306a36Sopenharmony_ci /* wait for end of transfer */ 219062306a36Sopenharmony_ci ret = fec_enet_mdio_wait(fep); 219162306a36Sopenharmony_ci if (ret) { 219262306a36Sopenharmony_ci netdev_err(fep->netdev, "MDIO address write timeout\n"); 219362306a36Sopenharmony_ci goto out; 219462306a36Sopenharmony_ci } 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_ci /* start a write op */ 219762306a36Sopenharmony_ci writel(frame_start | FEC_MMFR_OP_WRITE | 219862306a36Sopenharmony_ci FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(devad) | 219962306a36Sopenharmony_ci FEC_MMFR_TA | FEC_MMFR_DATA(value), 220062306a36Sopenharmony_ci fep->hwp + FEC_MII_DATA); 220162306a36Sopenharmony_ci 220262306a36Sopenharmony_ci /* wait for end of transfer */ 220362306a36Sopenharmony_ci ret = fec_enet_mdio_wait(fep); 220462306a36Sopenharmony_ci if (ret) 220562306a36Sopenharmony_ci netdev_err(fep->netdev, "MDIO write timeout\n"); 220662306a36Sopenharmony_ci 220762306a36Sopenharmony_ciout: 220862306a36Sopenharmony_ci pm_runtime_mark_last_busy(dev); 220962306a36Sopenharmony_ci pm_runtime_put_autosuspend(dev); 221062306a36Sopenharmony_ci 221162306a36Sopenharmony_ci return ret; 221262306a36Sopenharmony_ci} 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_cistatic void fec_enet_phy_reset_after_clk_enable(struct net_device *ndev) 221562306a36Sopenharmony_ci{ 221662306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 221762306a36Sopenharmony_ci struct phy_device *phy_dev = ndev->phydev; 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_ci if (phy_dev) { 222062306a36Sopenharmony_ci phy_reset_after_clk_enable(phy_dev); 222162306a36Sopenharmony_ci } else if (fep->phy_node) { 222262306a36Sopenharmony_ci /* 222362306a36Sopenharmony_ci * If the PHY still is not bound to the MAC, but there is 222462306a36Sopenharmony_ci * OF PHY node and a matching PHY device instance already, 222562306a36Sopenharmony_ci * use the OF PHY node to obtain the PHY device instance, 222662306a36Sopenharmony_ci * and then use that PHY device instance when triggering 222762306a36Sopenharmony_ci * the PHY reset. 222862306a36Sopenharmony_ci */ 222962306a36Sopenharmony_ci phy_dev = of_phy_find_device(fep->phy_node); 223062306a36Sopenharmony_ci phy_reset_after_clk_enable(phy_dev); 223162306a36Sopenharmony_ci put_device(&phy_dev->mdio.dev); 223262306a36Sopenharmony_ci } 223362306a36Sopenharmony_ci} 223462306a36Sopenharmony_ci 223562306a36Sopenharmony_cistatic int fec_enet_clk_enable(struct net_device *ndev, bool enable) 223662306a36Sopenharmony_ci{ 223762306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 223862306a36Sopenharmony_ci int ret; 223962306a36Sopenharmony_ci 224062306a36Sopenharmony_ci if (enable) { 224162306a36Sopenharmony_ci ret = clk_prepare_enable(fep->clk_enet_out); 224262306a36Sopenharmony_ci if (ret) 224362306a36Sopenharmony_ci return ret; 224462306a36Sopenharmony_ci 224562306a36Sopenharmony_ci if (fep->clk_ptp) { 224662306a36Sopenharmony_ci mutex_lock(&fep->ptp_clk_mutex); 224762306a36Sopenharmony_ci ret = clk_prepare_enable(fep->clk_ptp); 224862306a36Sopenharmony_ci if (ret) { 224962306a36Sopenharmony_ci mutex_unlock(&fep->ptp_clk_mutex); 225062306a36Sopenharmony_ci goto failed_clk_ptp; 225162306a36Sopenharmony_ci } else { 225262306a36Sopenharmony_ci fep->ptp_clk_on = true; 225362306a36Sopenharmony_ci } 225462306a36Sopenharmony_ci mutex_unlock(&fep->ptp_clk_mutex); 225562306a36Sopenharmony_ci } 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ci ret = clk_prepare_enable(fep->clk_ref); 225862306a36Sopenharmony_ci if (ret) 225962306a36Sopenharmony_ci goto failed_clk_ref; 226062306a36Sopenharmony_ci 226162306a36Sopenharmony_ci ret = clk_prepare_enable(fep->clk_2x_txclk); 226262306a36Sopenharmony_ci if (ret) 226362306a36Sopenharmony_ci goto failed_clk_2x_txclk; 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_ci fec_enet_phy_reset_after_clk_enable(ndev); 226662306a36Sopenharmony_ci } else { 226762306a36Sopenharmony_ci clk_disable_unprepare(fep->clk_enet_out); 226862306a36Sopenharmony_ci if (fep->clk_ptp) { 226962306a36Sopenharmony_ci mutex_lock(&fep->ptp_clk_mutex); 227062306a36Sopenharmony_ci clk_disable_unprepare(fep->clk_ptp); 227162306a36Sopenharmony_ci fep->ptp_clk_on = false; 227262306a36Sopenharmony_ci mutex_unlock(&fep->ptp_clk_mutex); 227362306a36Sopenharmony_ci } 227462306a36Sopenharmony_ci clk_disable_unprepare(fep->clk_ref); 227562306a36Sopenharmony_ci clk_disable_unprepare(fep->clk_2x_txclk); 227662306a36Sopenharmony_ci } 227762306a36Sopenharmony_ci 227862306a36Sopenharmony_ci return 0; 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_cifailed_clk_2x_txclk: 228162306a36Sopenharmony_ci if (fep->clk_ref) 228262306a36Sopenharmony_ci clk_disable_unprepare(fep->clk_ref); 228362306a36Sopenharmony_cifailed_clk_ref: 228462306a36Sopenharmony_ci if (fep->clk_ptp) { 228562306a36Sopenharmony_ci mutex_lock(&fep->ptp_clk_mutex); 228662306a36Sopenharmony_ci clk_disable_unprepare(fep->clk_ptp); 228762306a36Sopenharmony_ci fep->ptp_clk_on = false; 228862306a36Sopenharmony_ci mutex_unlock(&fep->ptp_clk_mutex); 228962306a36Sopenharmony_ci } 229062306a36Sopenharmony_cifailed_clk_ptp: 229162306a36Sopenharmony_ci clk_disable_unprepare(fep->clk_enet_out); 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_ci return ret; 229462306a36Sopenharmony_ci} 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_cistatic int fec_enet_parse_rgmii_delay(struct fec_enet_private *fep, 229762306a36Sopenharmony_ci struct device_node *np) 229862306a36Sopenharmony_ci{ 229962306a36Sopenharmony_ci u32 rgmii_tx_delay, rgmii_rx_delay; 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci /* For rgmii tx internal delay, valid values are 0ps and 2000ps */ 230262306a36Sopenharmony_ci if (!of_property_read_u32(np, "tx-internal-delay-ps", &rgmii_tx_delay)) { 230362306a36Sopenharmony_ci if (rgmii_tx_delay != 0 && rgmii_tx_delay != 2000) { 230462306a36Sopenharmony_ci dev_err(&fep->pdev->dev, "The only allowed RGMII TX delay values are: 0ps, 2000ps"); 230562306a36Sopenharmony_ci return -EINVAL; 230662306a36Sopenharmony_ci } else if (rgmii_tx_delay == 2000) { 230762306a36Sopenharmony_ci fep->rgmii_txc_dly = true; 230862306a36Sopenharmony_ci } 230962306a36Sopenharmony_ci } 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_ci /* For rgmii rx internal delay, valid values are 0ps and 2000ps */ 231262306a36Sopenharmony_ci if (!of_property_read_u32(np, "rx-internal-delay-ps", &rgmii_rx_delay)) { 231362306a36Sopenharmony_ci if (rgmii_rx_delay != 0 && rgmii_rx_delay != 2000) { 231462306a36Sopenharmony_ci dev_err(&fep->pdev->dev, "The only allowed RGMII RX delay values are: 0ps, 2000ps"); 231562306a36Sopenharmony_ci return -EINVAL; 231662306a36Sopenharmony_ci } else if (rgmii_rx_delay == 2000) { 231762306a36Sopenharmony_ci fep->rgmii_rxc_dly = true; 231862306a36Sopenharmony_ci } 231962306a36Sopenharmony_ci } 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_ci return 0; 232262306a36Sopenharmony_ci} 232362306a36Sopenharmony_ci 232462306a36Sopenharmony_cistatic int fec_enet_mii_probe(struct net_device *ndev) 232562306a36Sopenharmony_ci{ 232662306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 232762306a36Sopenharmony_ci struct phy_device *phy_dev = NULL; 232862306a36Sopenharmony_ci char mdio_bus_id[MII_BUS_ID_SIZE]; 232962306a36Sopenharmony_ci char phy_name[MII_BUS_ID_SIZE + 3]; 233062306a36Sopenharmony_ci int phy_id; 233162306a36Sopenharmony_ci int dev_id = fep->dev_id; 233262306a36Sopenharmony_ci 233362306a36Sopenharmony_ci if (fep->phy_node) { 233462306a36Sopenharmony_ci phy_dev = of_phy_connect(ndev, fep->phy_node, 233562306a36Sopenharmony_ci &fec_enet_adjust_link, 0, 233662306a36Sopenharmony_ci fep->phy_interface); 233762306a36Sopenharmony_ci if (!phy_dev) { 233862306a36Sopenharmony_ci netdev_err(ndev, "Unable to connect to phy\n"); 233962306a36Sopenharmony_ci return -ENODEV; 234062306a36Sopenharmony_ci } 234162306a36Sopenharmony_ci } else { 234262306a36Sopenharmony_ci /* check for attached phy */ 234362306a36Sopenharmony_ci for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) { 234462306a36Sopenharmony_ci if (!mdiobus_is_registered_device(fep->mii_bus, phy_id)) 234562306a36Sopenharmony_ci continue; 234662306a36Sopenharmony_ci if (dev_id--) 234762306a36Sopenharmony_ci continue; 234862306a36Sopenharmony_ci strscpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE); 234962306a36Sopenharmony_ci break; 235062306a36Sopenharmony_ci } 235162306a36Sopenharmony_ci 235262306a36Sopenharmony_ci if (phy_id >= PHY_MAX_ADDR) { 235362306a36Sopenharmony_ci netdev_info(ndev, "no PHY, assuming direct connection to switch\n"); 235462306a36Sopenharmony_ci strscpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE); 235562306a36Sopenharmony_ci phy_id = 0; 235662306a36Sopenharmony_ci } 235762306a36Sopenharmony_ci 235862306a36Sopenharmony_ci snprintf(phy_name, sizeof(phy_name), 235962306a36Sopenharmony_ci PHY_ID_FMT, mdio_bus_id, phy_id); 236062306a36Sopenharmony_ci phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, 236162306a36Sopenharmony_ci fep->phy_interface); 236262306a36Sopenharmony_ci } 236362306a36Sopenharmony_ci 236462306a36Sopenharmony_ci if (IS_ERR(phy_dev)) { 236562306a36Sopenharmony_ci netdev_err(ndev, "could not attach to PHY\n"); 236662306a36Sopenharmony_ci return PTR_ERR(phy_dev); 236762306a36Sopenharmony_ci } 236862306a36Sopenharmony_ci 236962306a36Sopenharmony_ci /* mask with MAC supported features */ 237062306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_GBIT) { 237162306a36Sopenharmony_ci phy_set_max_speed(phy_dev, 1000); 237262306a36Sopenharmony_ci phy_remove_link_mode(phy_dev, 237362306a36Sopenharmony_ci ETHTOOL_LINK_MODE_1000baseT_Half_BIT); 237462306a36Sopenharmony_ci#if !defined(CONFIG_M5272) 237562306a36Sopenharmony_ci phy_support_sym_pause(phy_dev); 237662306a36Sopenharmony_ci#endif 237762306a36Sopenharmony_ci } 237862306a36Sopenharmony_ci else 237962306a36Sopenharmony_ci phy_set_max_speed(phy_dev, 100); 238062306a36Sopenharmony_ci 238162306a36Sopenharmony_ci fep->link = 0; 238262306a36Sopenharmony_ci fep->full_duplex = 0; 238362306a36Sopenharmony_ci 238462306a36Sopenharmony_ci phy_dev->mac_managed_pm = true; 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_ci phy_attached_info(phy_dev); 238762306a36Sopenharmony_ci 238862306a36Sopenharmony_ci return 0; 238962306a36Sopenharmony_ci} 239062306a36Sopenharmony_ci 239162306a36Sopenharmony_cistatic int fec_enet_mii_init(struct platform_device *pdev) 239262306a36Sopenharmony_ci{ 239362306a36Sopenharmony_ci static struct mii_bus *fec0_mii_bus; 239462306a36Sopenharmony_ci struct net_device *ndev = platform_get_drvdata(pdev); 239562306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 239662306a36Sopenharmony_ci bool suppress_preamble = false; 239762306a36Sopenharmony_ci struct device_node *node; 239862306a36Sopenharmony_ci int err = -ENXIO; 239962306a36Sopenharmony_ci u32 mii_speed, holdtime; 240062306a36Sopenharmony_ci u32 bus_freq; 240162306a36Sopenharmony_ci 240262306a36Sopenharmony_ci /* 240362306a36Sopenharmony_ci * The i.MX28 dual fec interfaces are not equal. 240462306a36Sopenharmony_ci * Here are the differences: 240562306a36Sopenharmony_ci * 240662306a36Sopenharmony_ci * - fec0 supports MII & RMII modes while fec1 only supports RMII 240762306a36Sopenharmony_ci * - fec0 acts as the 1588 time master while fec1 is slave 240862306a36Sopenharmony_ci * - external phys can only be configured by fec0 240962306a36Sopenharmony_ci * 241062306a36Sopenharmony_ci * That is to say fec1 can not work independently. It only works 241162306a36Sopenharmony_ci * when fec0 is working. The reason behind this design is that the 241262306a36Sopenharmony_ci * second interface is added primarily for Switch mode. 241362306a36Sopenharmony_ci * 241462306a36Sopenharmony_ci * Because of the last point above, both phys are attached on fec0 241562306a36Sopenharmony_ci * mdio interface in board design, and need to be configured by 241662306a36Sopenharmony_ci * fec0 mii_bus. 241762306a36Sopenharmony_ci */ 241862306a36Sopenharmony_ci if ((fep->quirks & FEC_QUIRK_SINGLE_MDIO) && fep->dev_id > 0) { 241962306a36Sopenharmony_ci /* fec1 uses fec0 mii_bus */ 242062306a36Sopenharmony_ci if (mii_cnt && fec0_mii_bus) { 242162306a36Sopenharmony_ci fep->mii_bus = fec0_mii_bus; 242262306a36Sopenharmony_ci mii_cnt++; 242362306a36Sopenharmony_ci return 0; 242462306a36Sopenharmony_ci } 242562306a36Sopenharmony_ci return -ENOENT; 242662306a36Sopenharmony_ci } 242762306a36Sopenharmony_ci 242862306a36Sopenharmony_ci bus_freq = 2500000; /* 2.5MHz by default */ 242962306a36Sopenharmony_ci node = of_get_child_by_name(pdev->dev.of_node, "mdio"); 243062306a36Sopenharmony_ci if (node) { 243162306a36Sopenharmony_ci of_property_read_u32(node, "clock-frequency", &bus_freq); 243262306a36Sopenharmony_ci suppress_preamble = of_property_read_bool(node, 243362306a36Sopenharmony_ci "suppress-preamble"); 243462306a36Sopenharmony_ci } 243562306a36Sopenharmony_ci 243662306a36Sopenharmony_ci /* 243762306a36Sopenharmony_ci * Set MII speed (= clk_get_rate() / 2 * phy_speed) 243862306a36Sopenharmony_ci * 243962306a36Sopenharmony_ci * The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while 244062306a36Sopenharmony_ci * for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'. The i.MX28 244162306a36Sopenharmony_ci * Reference Manual has an error on this, and gets fixed on i.MX6Q 244262306a36Sopenharmony_ci * document. 244362306a36Sopenharmony_ci */ 244462306a36Sopenharmony_ci mii_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), bus_freq * 2); 244562306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_ENET_MAC) 244662306a36Sopenharmony_ci mii_speed--; 244762306a36Sopenharmony_ci if (mii_speed > 63) { 244862306a36Sopenharmony_ci dev_err(&pdev->dev, 244962306a36Sopenharmony_ci "fec clock (%lu) too fast to get right mii speed\n", 245062306a36Sopenharmony_ci clk_get_rate(fep->clk_ipg)); 245162306a36Sopenharmony_ci err = -EINVAL; 245262306a36Sopenharmony_ci goto err_out; 245362306a36Sopenharmony_ci } 245462306a36Sopenharmony_ci 245562306a36Sopenharmony_ci /* 245662306a36Sopenharmony_ci * The i.MX28 and i.MX6 types have another filed in the MSCR (aka 245762306a36Sopenharmony_ci * MII_SPEED) register that defines the MDIO output hold time. Earlier 245862306a36Sopenharmony_ci * versions are RAZ there, so just ignore the difference and write the 245962306a36Sopenharmony_ci * register always. 246062306a36Sopenharmony_ci * The minimal hold time according to IEE802.3 (clause 22) is 10 ns. 246162306a36Sopenharmony_ci * HOLDTIME + 1 is the number of clk cycles the fec is holding the 246262306a36Sopenharmony_ci * output. 246362306a36Sopenharmony_ci * The HOLDTIME bitfield takes values between 0 and 7 (inclusive). 246462306a36Sopenharmony_ci * Given that ceil(clkrate / 5000000) <= 64, the calculation for 246562306a36Sopenharmony_ci * holdtime cannot result in a value greater than 3. 246662306a36Sopenharmony_ci */ 246762306a36Sopenharmony_ci holdtime = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 100000000) - 1; 246862306a36Sopenharmony_ci 246962306a36Sopenharmony_ci fep->phy_speed = mii_speed << 1 | holdtime << 8; 247062306a36Sopenharmony_ci 247162306a36Sopenharmony_ci if (suppress_preamble) 247262306a36Sopenharmony_ci fep->phy_speed |= BIT(7); 247362306a36Sopenharmony_ci 247462306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_CLEAR_SETUP_MII) { 247562306a36Sopenharmony_ci /* Clear MMFR to avoid to generate MII event by writing MSCR. 247662306a36Sopenharmony_ci * MII event generation condition: 247762306a36Sopenharmony_ci * - writing MSCR: 247862306a36Sopenharmony_ci * - mmfr[31:0]_not_zero & mscr[7:0]_is_zero & 247962306a36Sopenharmony_ci * mscr_reg_data_in[7:0] != 0 248062306a36Sopenharmony_ci * - writing MMFR: 248162306a36Sopenharmony_ci * - mscr[7:0]_not_zero 248262306a36Sopenharmony_ci */ 248362306a36Sopenharmony_ci writel(0, fep->hwp + FEC_MII_DATA); 248462306a36Sopenharmony_ci } 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_ci writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 248762306a36Sopenharmony_ci 248862306a36Sopenharmony_ci /* Clear any pending transaction complete indication */ 248962306a36Sopenharmony_ci writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT); 249062306a36Sopenharmony_ci 249162306a36Sopenharmony_ci fep->mii_bus = mdiobus_alloc(); 249262306a36Sopenharmony_ci if (fep->mii_bus == NULL) { 249362306a36Sopenharmony_ci err = -ENOMEM; 249462306a36Sopenharmony_ci goto err_out; 249562306a36Sopenharmony_ci } 249662306a36Sopenharmony_ci 249762306a36Sopenharmony_ci fep->mii_bus->name = "fec_enet_mii_bus"; 249862306a36Sopenharmony_ci fep->mii_bus->read = fec_enet_mdio_read_c22; 249962306a36Sopenharmony_ci fep->mii_bus->write = fec_enet_mdio_write_c22; 250062306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_MDIO_C45) { 250162306a36Sopenharmony_ci fep->mii_bus->read_c45 = fec_enet_mdio_read_c45; 250262306a36Sopenharmony_ci fep->mii_bus->write_c45 = fec_enet_mdio_write_c45; 250362306a36Sopenharmony_ci } 250462306a36Sopenharmony_ci snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", 250562306a36Sopenharmony_ci pdev->name, fep->dev_id + 1); 250662306a36Sopenharmony_ci fep->mii_bus->priv = fep; 250762306a36Sopenharmony_ci fep->mii_bus->parent = &pdev->dev; 250862306a36Sopenharmony_ci 250962306a36Sopenharmony_ci err = of_mdiobus_register(fep->mii_bus, node); 251062306a36Sopenharmony_ci if (err) 251162306a36Sopenharmony_ci goto err_out_free_mdiobus; 251262306a36Sopenharmony_ci of_node_put(node); 251362306a36Sopenharmony_ci 251462306a36Sopenharmony_ci mii_cnt++; 251562306a36Sopenharmony_ci 251662306a36Sopenharmony_ci /* save fec0 mii_bus */ 251762306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_SINGLE_MDIO) 251862306a36Sopenharmony_ci fec0_mii_bus = fep->mii_bus; 251962306a36Sopenharmony_ci 252062306a36Sopenharmony_ci return 0; 252162306a36Sopenharmony_ci 252262306a36Sopenharmony_cierr_out_free_mdiobus: 252362306a36Sopenharmony_ci mdiobus_free(fep->mii_bus); 252462306a36Sopenharmony_cierr_out: 252562306a36Sopenharmony_ci of_node_put(node); 252662306a36Sopenharmony_ci return err; 252762306a36Sopenharmony_ci} 252862306a36Sopenharmony_ci 252962306a36Sopenharmony_cistatic void fec_enet_mii_remove(struct fec_enet_private *fep) 253062306a36Sopenharmony_ci{ 253162306a36Sopenharmony_ci if (--mii_cnt == 0) { 253262306a36Sopenharmony_ci mdiobus_unregister(fep->mii_bus); 253362306a36Sopenharmony_ci mdiobus_free(fep->mii_bus); 253462306a36Sopenharmony_ci } 253562306a36Sopenharmony_ci} 253662306a36Sopenharmony_ci 253762306a36Sopenharmony_cistatic void fec_enet_get_drvinfo(struct net_device *ndev, 253862306a36Sopenharmony_ci struct ethtool_drvinfo *info) 253962306a36Sopenharmony_ci{ 254062306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 254162306a36Sopenharmony_ci 254262306a36Sopenharmony_ci strscpy(info->driver, fep->pdev->dev.driver->name, 254362306a36Sopenharmony_ci sizeof(info->driver)); 254462306a36Sopenharmony_ci strscpy(info->bus_info, dev_name(&ndev->dev), sizeof(info->bus_info)); 254562306a36Sopenharmony_ci} 254662306a36Sopenharmony_ci 254762306a36Sopenharmony_cistatic int fec_enet_get_regs_len(struct net_device *ndev) 254862306a36Sopenharmony_ci{ 254962306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 255062306a36Sopenharmony_ci struct resource *r; 255162306a36Sopenharmony_ci int s = 0; 255262306a36Sopenharmony_ci 255362306a36Sopenharmony_ci r = platform_get_resource(fep->pdev, IORESOURCE_MEM, 0); 255462306a36Sopenharmony_ci if (r) 255562306a36Sopenharmony_ci s = resource_size(r); 255662306a36Sopenharmony_ci 255762306a36Sopenharmony_ci return s; 255862306a36Sopenharmony_ci} 255962306a36Sopenharmony_ci 256062306a36Sopenharmony_ci/* List of registers that can be safety be read to dump them with ethtool */ 256162306a36Sopenharmony_ci#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ 256262306a36Sopenharmony_ci defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ 256362306a36Sopenharmony_ci defined(CONFIG_ARM64) || defined(CONFIG_COMPILE_TEST) 256462306a36Sopenharmony_cistatic __u32 fec_enet_register_version = 2; 256562306a36Sopenharmony_cistatic u32 fec_enet_register_offset[] = { 256662306a36Sopenharmony_ci FEC_IEVENT, FEC_IMASK, FEC_R_DES_ACTIVE_0, FEC_X_DES_ACTIVE_0, 256762306a36Sopenharmony_ci FEC_ECNTRL, FEC_MII_DATA, FEC_MII_SPEED, FEC_MIB_CTRLSTAT, FEC_R_CNTRL, 256862306a36Sopenharmony_ci FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, FEC_OPD, FEC_TXIC0, FEC_TXIC1, 256962306a36Sopenharmony_ci FEC_TXIC2, FEC_RXIC0, FEC_RXIC1, FEC_RXIC2, FEC_HASH_TABLE_HIGH, 257062306a36Sopenharmony_ci FEC_HASH_TABLE_LOW, FEC_GRP_HASH_TABLE_HIGH, FEC_GRP_HASH_TABLE_LOW, 257162306a36Sopenharmony_ci FEC_X_WMRK, FEC_R_BOUND, FEC_R_FSTART, FEC_R_DES_START_1, 257262306a36Sopenharmony_ci FEC_X_DES_START_1, FEC_R_BUFF_SIZE_1, FEC_R_DES_START_2, 257362306a36Sopenharmony_ci FEC_X_DES_START_2, FEC_R_BUFF_SIZE_2, FEC_R_DES_START_0, 257462306a36Sopenharmony_ci FEC_X_DES_START_0, FEC_R_BUFF_SIZE_0, FEC_R_FIFO_RSFL, FEC_R_FIFO_RSEM, 257562306a36Sopenharmony_ci FEC_R_FIFO_RAEM, FEC_R_FIFO_RAFL, FEC_RACC, FEC_RCMR_1, FEC_RCMR_2, 257662306a36Sopenharmony_ci FEC_DMA_CFG_1, FEC_DMA_CFG_2, FEC_R_DES_ACTIVE_1, FEC_X_DES_ACTIVE_1, 257762306a36Sopenharmony_ci FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_2, FEC_QOS_SCHEME, 257862306a36Sopenharmony_ci RMON_T_DROP, RMON_T_PACKETS, RMON_T_BC_PKT, RMON_T_MC_PKT, 257962306a36Sopenharmony_ci RMON_T_CRC_ALIGN, RMON_T_UNDERSIZE, RMON_T_OVERSIZE, RMON_T_FRAG, 258062306a36Sopenharmony_ci RMON_T_JAB, RMON_T_COL, RMON_T_P64, RMON_T_P65TO127, RMON_T_P128TO255, 258162306a36Sopenharmony_ci RMON_T_P256TO511, RMON_T_P512TO1023, RMON_T_P1024TO2047, 258262306a36Sopenharmony_ci RMON_T_P_GTE2048, RMON_T_OCTETS, 258362306a36Sopenharmony_ci IEEE_T_DROP, IEEE_T_FRAME_OK, IEEE_T_1COL, IEEE_T_MCOL, IEEE_T_DEF, 258462306a36Sopenharmony_ci IEEE_T_LCOL, IEEE_T_EXCOL, IEEE_T_MACERR, IEEE_T_CSERR, IEEE_T_SQE, 258562306a36Sopenharmony_ci IEEE_T_FDXFC, IEEE_T_OCTETS_OK, 258662306a36Sopenharmony_ci RMON_R_PACKETS, RMON_R_BC_PKT, RMON_R_MC_PKT, RMON_R_CRC_ALIGN, 258762306a36Sopenharmony_ci RMON_R_UNDERSIZE, RMON_R_OVERSIZE, RMON_R_FRAG, RMON_R_JAB, 258862306a36Sopenharmony_ci RMON_R_RESVD_O, RMON_R_P64, RMON_R_P65TO127, RMON_R_P128TO255, 258962306a36Sopenharmony_ci RMON_R_P256TO511, RMON_R_P512TO1023, RMON_R_P1024TO2047, 259062306a36Sopenharmony_ci RMON_R_P_GTE2048, RMON_R_OCTETS, 259162306a36Sopenharmony_ci IEEE_R_DROP, IEEE_R_FRAME_OK, IEEE_R_CRC, IEEE_R_ALIGN, IEEE_R_MACERR, 259262306a36Sopenharmony_ci IEEE_R_FDXFC, IEEE_R_OCTETS_OK 259362306a36Sopenharmony_ci}; 259462306a36Sopenharmony_ci/* for i.MX6ul */ 259562306a36Sopenharmony_cistatic u32 fec_enet_register_offset_6ul[] = { 259662306a36Sopenharmony_ci FEC_IEVENT, FEC_IMASK, FEC_R_DES_ACTIVE_0, FEC_X_DES_ACTIVE_0, 259762306a36Sopenharmony_ci FEC_ECNTRL, FEC_MII_DATA, FEC_MII_SPEED, FEC_MIB_CTRLSTAT, FEC_R_CNTRL, 259862306a36Sopenharmony_ci FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, FEC_OPD, FEC_TXIC0, FEC_RXIC0, 259962306a36Sopenharmony_ci FEC_HASH_TABLE_HIGH, FEC_HASH_TABLE_LOW, FEC_GRP_HASH_TABLE_HIGH, 260062306a36Sopenharmony_ci FEC_GRP_HASH_TABLE_LOW, FEC_X_WMRK, FEC_R_DES_START_0, 260162306a36Sopenharmony_ci FEC_X_DES_START_0, FEC_R_BUFF_SIZE_0, FEC_R_FIFO_RSFL, FEC_R_FIFO_RSEM, 260262306a36Sopenharmony_ci FEC_R_FIFO_RAEM, FEC_R_FIFO_RAFL, FEC_RACC, 260362306a36Sopenharmony_ci RMON_T_DROP, RMON_T_PACKETS, RMON_T_BC_PKT, RMON_T_MC_PKT, 260462306a36Sopenharmony_ci RMON_T_CRC_ALIGN, RMON_T_UNDERSIZE, RMON_T_OVERSIZE, RMON_T_FRAG, 260562306a36Sopenharmony_ci RMON_T_JAB, RMON_T_COL, RMON_T_P64, RMON_T_P65TO127, RMON_T_P128TO255, 260662306a36Sopenharmony_ci RMON_T_P256TO511, RMON_T_P512TO1023, RMON_T_P1024TO2047, 260762306a36Sopenharmony_ci RMON_T_P_GTE2048, RMON_T_OCTETS, 260862306a36Sopenharmony_ci IEEE_T_DROP, IEEE_T_FRAME_OK, IEEE_T_1COL, IEEE_T_MCOL, IEEE_T_DEF, 260962306a36Sopenharmony_ci IEEE_T_LCOL, IEEE_T_EXCOL, IEEE_T_MACERR, IEEE_T_CSERR, IEEE_T_SQE, 261062306a36Sopenharmony_ci IEEE_T_FDXFC, IEEE_T_OCTETS_OK, 261162306a36Sopenharmony_ci RMON_R_PACKETS, RMON_R_BC_PKT, RMON_R_MC_PKT, RMON_R_CRC_ALIGN, 261262306a36Sopenharmony_ci RMON_R_UNDERSIZE, RMON_R_OVERSIZE, RMON_R_FRAG, RMON_R_JAB, 261362306a36Sopenharmony_ci RMON_R_RESVD_O, RMON_R_P64, RMON_R_P65TO127, RMON_R_P128TO255, 261462306a36Sopenharmony_ci RMON_R_P256TO511, RMON_R_P512TO1023, RMON_R_P1024TO2047, 261562306a36Sopenharmony_ci RMON_R_P_GTE2048, RMON_R_OCTETS, 261662306a36Sopenharmony_ci IEEE_R_DROP, IEEE_R_FRAME_OK, IEEE_R_CRC, IEEE_R_ALIGN, IEEE_R_MACERR, 261762306a36Sopenharmony_ci IEEE_R_FDXFC, IEEE_R_OCTETS_OK 261862306a36Sopenharmony_ci}; 261962306a36Sopenharmony_ci#else 262062306a36Sopenharmony_cistatic __u32 fec_enet_register_version = 1; 262162306a36Sopenharmony_cistatic u32 fec_enet_register_offset[] = { 262262306a36Sopenharmony_ci FEC_ECNTRL, FEC_IEVENT, FEC_IMASK, FEC_IVEC, FEC_R_DES_ACTIVE_0, 262362306a36Sopenharmony_ci FEC_R_DES_ACTIVE_1, FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_0, 262462306a36Sopenharmony_ci FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2, FEC_MII_DATA, FEC_MII_SPEED, 262562306a36Sopenharmony_ci FEC_R_BOUND, FEC_R_FSTART, FEC_X_WMRK, FEC_X_FSTART, FEC_R_CNTRL, 262662306a36Sopenharmony_ci FEC_MAX_FRM_LEN, FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, 262762306a36Sopenharmony_ci FEC_GRP_HASH_TABLE_HIGH, FEC_GRP_HASH_TABLE_LOW, FEC_R_DES_START_0, 262862306a36Sopenharmony_ci FEC_R_DES_START_1, FEC_R_DES_START_2, FEC_X_DES_START_0, 262962306a36Sopenharmony_ci FEC_X_DES_START_1, FEC_X_DES_START_2, FEC_R_BUFF_SIZE_0, 263062306a36Sopenharmony_ci FEC_R_BUFF_SIZE_1, FEC_R_BUFF_SIZE_2 263162306a36Sopenharmony_ci}; 263262306a36Sopenharmony_ci#endif 263362306a36Sopenharmony_ci 263462306a36Sopenharmony_cistatic void fec_enet_get_regs(struct net_device *ndev, 263562306a36Sopenharmony_ci struct ethtool_regs *regs, void *regbuf) 263662306a36Sopenharmony_ci{ 263762306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 263862306a36Sopenharmony_ci u32 __iomem *theregs = (u32 __iomem *)fep->hwp; 263962306a36Sopenharmony_ci struct device *dev = &fep->pdev->dev; 264062306a36Sopenharmony_ci u32 *buf = (u32 *)regbuf; 264162306a36Sopenharmony_ci u32 i, off; 264262306a36Sopenharmony_ci int ret; 264362306a36Sopenharmony_ci#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ 264462306a36Sopenharmony_ci defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ 264562306a36Sopenharmony_ci defined(CONFIG_ARM64) || defined(CONFIG_COMPILE_TEST) 264662306a36Sopenharmony_ci u32 *reg_list; 264762306a36Sopenharmony_ci u32 reg_cnt; 264862306a36Sopenharmony_ci 264962306a36Sopenharmony_ci if (!of_machine_is_compatible("fsl,imx6ul")) { 265062306a36Sopenharmony_ci reg_list = fec_enet_register_offset; 265162306a36Sopenharmony_ci reg_cnt = ARRAY_SIZE(fec_enet_register_offset); 265262306a36Sopenharmony_ci } else { 265362306a36Sopenharmony_ci reg_list = fec_enet_register_offset_6ul; 265462306a36Sopenharmony_ci reg_cnt = ARRAY_SIZE(fec_enet_register_offset_6ul); 265562306a36Sopenharmony_ci } 265662306a36Sopenharmony_ci#else 265762306a36Sopenharmony_ci /* coldfire */ 265862306a36Sopenharmony_ci static u32 *reg_list = fec_enet_register_offset; 265962306a36Sopenharmony_ci static const u32 reg_cnt = ARRAY_SIZE(fec_enet_register_offset); 266062306a36Sopenharmony_ci#endif 266162306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(dev); 266262306a36Sopenharmony_ci if (ret < 0) 266362306a36Sopenharmony_ci return; 266462306a36Sopenharmony_ci 266562306a36Sopenharmony_ci regs->version = fec_enet_register_version; 266662306a36Sopenharmony_ci 266762306a36Sopenharmony_ci memset(buf, 0, regs->len); 266862306a36Sopenharmony_ci 266962306a36Sopenharmony_ci for (i = 0; i < reg_cnt; i++) { 267062306a36Sopenharmony_ci off = reg_list[i]; 267162306a36Sopenharmony_ci 267262306a36Sopenharmony_ci if ((off == FEC_R_BOUND || off == FEC_R_FSTART) && 267362306a36Sopenharmony_ci !(fep->quirks & FEC_QUIRK_HAS_FRREG)) 267462306a36Sopenharmony_ci continue; 267562306a36Sopenharmony_ci 267662306a36Sopenharmony_ci off >>= 2; 267762306a36Sopenharmony_ci buf[off] = readl(&theregs[off]); 267862306a36Sopenharmony_ci } 267962306a36Sopenharmony_ci 268062306a36Sopenharmony_ci pm_runtime_mark_last_busy(dev); 268162306a36Sopenharmony_ci pm_runtime_put_autosuspend(dev); 268262306a36Sopenharmony_ci} 268362306a36Sopenharmony_ci 268462306a36Sopenharmony_cistatic int fec_enet_get_ts_info(struct net_device *ndev, 268562306a36Sopenharmony_ci struct ethtool_ts_info *info) 268662306a36Sopenharmony_ci{ 268762306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 268862306a36Sopenharmony_ci 268962306a36Sopenharmony_ci if (fep->bufdesc_ex) { 269062306a36Sopenharmony_ci 269162306a36Sopenharmony_ci info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | 269262306a36Sopenharmony_ci SOF_TIMESTAMPING_RX_SOFTWARE | 269362306a36Sopenharmony_ci SOF_TIMESTAMPING_SOFTWARE | 269462306a36Sopenharmony_ci SOF_TIMESTAMPING_TX_HARDWARE | 269562306a36Sopenharmony_ci SOF_TIMESTAMPING_RX_HARDWARE | 269662306a36Sopenharmony_ci SOF_TIMESTAMPING_RAW_HARDWARE; 269762306a36Sopenharmony_ci if (fep->ptp_clock) 269862306a36Sopenharmony_ci info->phc_index = ptp_clock_index(fep->ptp_clock); 269962306a36Sopenharmony_ci else 270062306a36Sopenharmony_ci info->phc_index = -1; 270162306a36Sopenharmony_ci 270262306a36Sopenharmony_ci info->tx_types = (1 << HWTSTAMP_TX_OFF) | 270362306a36Sopenharmony_ci (1 << HWTSTAMP_TX_ON); 270462306a36Sopenharmony_ci 270562306a36Sopenharmony_ci info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | 270662306a36Sopenharmony_ci (1 << HWTSTAMP_FILTER_ALL); 270762306a36Sopenharmony_ci return 0; 270862306a36Sopenharmony_ci } else { 270962306a36Sopenharmony_ci return ethtool_op_get_ts_info(ndev, info); 271062306a36Sopenharmony_ci } 271162306a36Sopenharmony_ci} 271262306a36Sopenharmony_ci 271362306a36Sopenharmony_ci#if !defined(CONFIG_M5272) 271462306a36Sopenharmony_ci 271562306a36Sopenharmony_cistatic void fec_enet_get_pauseparam(struct net_device *ndev, 271662306a36Sopenharmony_ci struct ethtool_pauseparam *pause) 271762306a36Sopenharmony_ci{ 271862306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 271962306a36Sopenharmony_ci 272062306a36Sopenharmony_ci pause->autoneg = (fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) != 0; 272162306a36Sopenharmony_ci pause->tx_pause = (fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) != 0; 272262306a36Sopenharmony_ci pause->rx_pause = pause->tx_pause; 272362306a36Sopenharmony_ci} 272462306a36Sopenharmony_ci 272562306a36Sopenharmony_cistatic int fec_enet_set_pauseparam(struct net_device *ndev, 272662306a36Sopenharmony_ci struct ethtool_pauseparam *pause) 272762306a36Sopenharmony_ci{ 272862306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 272962306a36Sopenharmony_ci 273062306a36Sopenharmony_ci if (!ndev->phydev) 273162306a36Sopenharmony_ci return -ENODEV; 273262306a36Sopenharmony_ci 273362306a36Sopenharmony_ci if (pause->tx_pause != pause->rx_pause) { 273462306a36Sopenharmony_ci netdev_info(ndev, 273562306a36Sopenharmony_ci "hardware only support enable/disable both tx and rx"); 273662306a36Sopenharmony_ci return -EINVAL; 273762306a36Sopenharmony_ci } 273862306a36Sopenharmony_ci 273962306a36Sopenharmony_ci fep->pause_flag = 0; 274062306a36Sopenharmony_ci 274162306a36Sopenharmony_ci /* tx pause must be same as rx pause */ 274262306a36Sopenharmony_ci fep->pause_flag |= pause->rx_pause ? FEC_PAUSE_FLAG_ENABLE : 0; 274362306a36Sopenharmony_ci fep->pause_flag |= pause->autoneg ? FEC_PAUSE_FLAG_AUTONEG : 0; 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_ci phy_set_sym_pause(ndev->phydev, pause->rx_pause, pause->tx_pause, 274662306a36Sopenharmony_ci pause->autoneg); 274762306a36Sopenharmony_ci 274862306a36Sopenharmony_ci if (pause->autoneg) { 274962306a36Sopenharmony_ci if (netif_running(ndev)) 275062306a36Sopenharmony_ci fec_stop(ndev); 275162306a36Sopenharmony_ci phy_start_aneg(ndev->phydev); 275262306a36Sopenharmony_ci } 275362306a36Sopenharmony_ci if (netif_running(ndev)) { 275462306a36Sopenharmony_ci napi_disable(&fep->napi); 275562306a36Sopenharmony_ci netif_tx_lock_bh(ndev); 275662306a36Sopenharmony_ci fec_restart(ndev); 275762306a36Sopenharmony_ci netif_tx_wake_all_queues(ndev); 275862306a36Sopenharmony_ci netif_tx_unlock_bh(ndev); 275962306a36Sopenharmony_ci napi_enable(&fep->napi); 276062306a36Sopenharmony_ci } 276162306a36Sopenharmony_ci 276262306a36Sopenharmony_ci return 0; 276362306a36Sopenharmony_ci} 276462306a36Sopenharmony_ci 276562306a36Sopenharmony_cistatic const struct fec_stat { 276662306a36Sopenharmony_ci char name[ETH_GSTRING_LEN]; 276762306a36Sopenharmony_ci u16 offset; 276862306a36Sopenharmony_ci} fec_stats[] = { 276962306a36Sopenharmony_ci /* RMON TX */ 277062306a36Sopenharmony_ci { "tx_dropped", RMON_T_DROP }, 277162306a36Sopenharmony_ci { "tx_packets", RMON_T_PACKETS }, 277262306a36Sopenharmony_ci { "tx_broadcast", RMON_T_BC_PKT }, 277362306a36Sopenharmony_ci { "tx_multicast", RMON_T_MC_PKT }, 277462306a36Sopenharmony_ci { "tx_crc_errors", RMON_T_CRC_ALIGN }, 277562306a36Sopenharmony_ci { "tx_undersize", RMON_T_UNDERSIZE }, 277662306a36Sopenharmony_ci { "tx_oversize", RMON_T_OVERSIZE }, 277762306a36Sopenharmony_ci { "tx_fragment", RMON_T_FRAG }, 277862306a36Sopenharmony_ci { "tx_jabber", RMON_T_JAB }, 277962306a36Sopenharmony_ci { "tx_collision", RMON_T_COL }, 278062306a36Sopenharmony_ci { "tx_64byte", RMON_T_P64 }, 278162306a36Sopenharmony_ci { "tx_65to127byte", RMON_T_P65TO127 }, 278262306a36Sopenharmony_ci { "tx_128to255byte", RMON_T_P128TO255 }, 278362306a36Sopenharmony_ci { "tx_256to511byte", RMON_T_P256TO511 }, 278462306a36Sopenharmony_ci { "tx_512to1023byte", RMON_T_P512TO1023 }, 278562306a36Sopenharmony_ci { "tx_1024to2047byte", RMON_T_P1024TO2047 }, 278662306a36Sopenharmony_ci { "tx_GTE2048byte", RMON_T_P_GTE2048 }, 278762306a36Sopenharmony_ci { "tx_octets", RMON_T_OCTETS }, 278862306a36Sopenharmony_ci 278962306a36Sopenharmony_ci /* IEEE TX */ 279062306a36Sopenharmony_ci { "IEEE_tx_drop", IEEE_T_DROP }, 279162306a36Sopenharmony_ci { "IEEE_tx_frame_ok", IEEE_T_FRAME_OK }, 279262306a36Sopenharmony_ci { "IEEE_tx_1col", IEEE_T_1COL }, 279362306a36Sopenharmony_ci { "IEEE_tx_mcol", IEEE_T_MCOL }, 279462306a36Sopenharmony_ci { "IEEE_tx_def", IEEE_T_DEF }, 279562306a36Sopenharmony_ci { "IEEE_tx_lcol", IEEE_T_LCOL }, 279662306a36Sopenharmony_ci { "IEEE_tx_excol", IEEE_T_EXCOL }, 279762306a36Sopenharmony_ci { "IEEE_tx_macerr", IEEE_T_MACERR }, 279862306a36Sopenharmony_ci { "IEEE_tx_cserr", IEEE_T_CSERR }, 279962306a36Sopenharmony_ci { "IEEE_tx_sqe", IEEE_T_SQE }, 280062306a36Sopenharmony_ci { "IEEE_tx_fdxfc", IEEE_T_FDXFC }, 280162306a36Sopenharmony_ci { "IEEE_tx_octets_ok", IEEE_T_OCTETS_OK }, 280262306a36Sopenharmony_ci 280362306a36Sopenharmony_ci /* RMON RX */ 280462306a36Sopenharmony_ci { "rx_packets", RMON_R_PACKETS }, 280562306a36Sopenharmony_ci { "rx_broadcast", RMON_R_BC_PKT }, 280662306a36Sopenharmony_ci { "rx_multicast", RMON_R_MC_PKT }, 280762306a36Sopenharmony_ci { "rx_crc_errors", RMON_R_CRC_ALIGN }, 280862306a36Sopenharmony_ci { "rx_undersize", RMON_R_UNDERSIZE }, 280962306a36Sopenharmony_ci { "rx_oversize", RMON_R_OVERSIZE }, 281062306a36Sopenharmony_ci { "rx_fragment", RMON_R_FRAG }, 281162306a36Sopenharmony_ci { "rx_jabber", RMON_R_JAB }, 281262306a36Sopenharmony_ci { "rx_64byte", RMON_R_P64 }, 281362306a36Sopenharmony_ci { "rx_65to127byte", RMON_R_P65TO127 }, 281462306a36Sopenharmony_ci { "rx_128to255byte", RMON_R_P128TO255 }, 281562306a36Sopenharmony_ci { "rx_256to511byte", RMON_R_P256TO511 }, 281662306a36Sopenharmony_ci { "rx_512to1023byte", RMON_R_P512TO1023 }, 281762306a36Sopenharmony_ci { "rx_1024to2047byte", RMON_R_P1024TO2047 }, 281862306a36Sopenharmony_ci { "rx_GTE2048byte", RMON_R_P_GTE2048 }, 281962306a36Sopenharmony_ci { "rx_octets", RMON_R_OCTETS }, 282062306a36Sopenharmony_ci 282162306a36Sopenharmony_ci /* IEEE RX */ 282262306a36Sopenharmony_ci { "IEEE_rx_drop", IEEE_R_DROP }, 282362306a36Sopenharmony_ci { "IEEE_rx_frame_ok", IEEE_R_FRAME_OK }, 282462306a36Sopenharmony_ci { "IEEE_rx_crc", IEEE_R_CRC }, 282562306a36Sopenharmony_ci { "IEEE_rx_align", IEEE_R_ALIGN }, 282662306a36Sopenharmony_ci { "IEEE_rx_macerr", IEEE_R_MACERR }, 282762306a36Sopenharmony_ci { "IEEE_rx_fdxfc", IEEE_R_FDXFC }, 282862306a36Sopenharmony_ci { "IEEE_rx_octets_ok", IEEE_R_OCTETS_OK }, 282962306a36Sopenharmony_ci}; 283062306a36Sopenharmony_ci 283162306a36Sopenharmony_ci#define FEC_STATS_SIZE (ARRAY_SIZE(fec_stats) * sizeof(u64)) 283262306a36Sopenharmony_ci 283362306a36Sopenharmony_cistatic const char *fec_xdp_stat_strs[XDP_STATS_TOTAL] = { 283462306a36Sopenharmony_ci "rx_xdp_redirect", /* RX_XDP_REDIRECT = 0, */ 283562306a36Sopenharmony_ci "rx_xdp_pass", /* RX_XDP_PASS, */ 283662306a36Sopenharmony_ci "rx_xdp_drop", /* RX_XDP_DROP, */ 283762306a36Sopenharmony_ci "rx_xdp_tx", /* RX_XDP_TX, */ 283862306a36Sopenharmony_ci "rx_xdp_tx_errors", /* RX_XDP_TX_ERRORS, */ 283962306a36Sopenharmony_ci "tx_xdp_xmit", /* TX_XDP_XMIT, */ 284062306a36Sopenharmony_ci "tx_xdp_xmit_errors", /* TX_XDP_XMIT_ERRORS, */ 284162306a36Sopenharmony_ci}; 284262306a36Sopenharmony_ci 284362306a36Sopenharmony_cistatic void fec_enet_update_ethtool_stats(struct net_device *dev) 284462306a36Sopenharmony_ci{ 284562306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(dev); 284662306a36Sopenharmony_ci int i; 284762306a36Sopenharmony_ci 284862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 284962306a36Sopenharmony_ci fep->ethtool_stats[i] = readl(fep->hwp + fec_stats[i].offset); 285062306a36Sopenharmony_ci} 285162306a36Sopenharmony_ci 285262306a36Sopenharmony_cistatic void fec_enet_get_xdp_stats(struct fec_enet_private *fep, u64 *data) 285362306a36Sopenharmony_ci{ 285462306a36Sopenharmony_ci u64 xdp_stats[XDP_STATS_TOTAL] = { 0 }; 285562306a36Sopenharmony_ci struct fec_enet_priv_rx_q *rxq; 285662306a36Sopenharmony_ci int i, j; 285762306a36Sopenharmony_ci 285862306a36Sopenharmony_ci for (i = fep->num_rx_queues - 1; i >= 0; i--) { 285962306a36Sopenharmony_ci rxq = fep->rx_queue[i]; 286062306a36Sopenharmony_ci 286162306a36Sopenharmony_ci for (j = 0; j < XDP_STATS_TOTAL; j++) 286262306a36Sopenharmony_ci xdp_stats[j] += rxq->stats[j]; 286362306a36Sopenharmony_ci } 286462306a36Sopenharmony_ci 286562306a36Sopenharmony_ci memcpy(data, xdp_stats, sizeof(xdp_stats)); 286662306a36Sopenharmony_ci} 286762306a36Sopenharmony_ci 286862306a36Sopenharmony_cistatic void fec_enet_page_pool_stats(struct fec_enet_private *fep, u64 *data) 286962306a36Sopenharmony_ci{ 287062306a36Sopenharmony_ci#ifdef CONFIG_PAGE_POOL_STATS 287162306a36Sopenharmony_ci struct page_pool_stats stats = {}; 287262306a36Sopenharmony_ci struct fec_enet_priv_rx_q *rxq; 287362306a36Sopenharmony_ci int i; 287462306a36Sopenharmony_ci 287562306a36Sopenharmony_ci for (i = fep->num_rx_queues - 1; i >= 0; i--) { 287662306a36Sopenharmony_ci rxq = fep->rx_queue[i]; 287762306a36Sopenharmony_ci 287862306a36Sopenharmony_ci if (!rxq->page_pool) 287962306a36Sopenharmony_ci continue; 288062306a36Sopenharmony_ci 288162306a36Sopenharmony_ci page_pool_get_stats(rxq->page_pool, &stats); 288262306a36Sopenharmony_ci } 288362306a36Sopenharmony_ci 288462306a36Sopenharmony_ci page_pool_ethtool_stats_get(data, &stats); 288562306a36Sopenharmony_ci#endif 288662306a36Sopenharmony_ci} 288762306a36Sopenharmony_ci 288862306a36Sopenharmony_cistatic void fec_enet_get_ethtool_stats(struct net_device *dev, 288962306a36Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 289062306a36Sopenharmony_ci{ 289162306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(dev); 289262306a36Sopenharmony_ci 289362306a36Sopenharmony_ci if (netif_running(dev)) 289462306a36Sopenharmony_ci fec_enet_update_ethtool_stats(dev); 289562306a36Sopenharmony_ci 289662306a36Sopenharmony_ci memcpy(data, fep->ethtool_stats, FEC_STATS_SIZE); 289762306a36Sopenharmony_ci data += FEC_STATS_SIZE / sizeof(u64); 289862306a36Sopenharmony_ci 289962306a36Sopenharmony_ci fec_enet_get_xdp_stats(fep, data); 290062306a36Sopenharmony_ci data += XDP_STATS_TOTAL; 290162306a36Sopenharmony_ci 290262306a36Sopenharmony_ci fec_enet_page_pool_stats(fep, data); 290362306a36Sopenharmony_ci} 290462306a36Sopenharmony_ci 290562306a36Sopenharmony_cistatic void fec_enet_get_strings(struct net_device *netdev, 290662306a36Sopenharmony_ci u32 stringset, u8 *data) 290762306a36Sopenharmony_ci{ 290862306a36Sopenharmony_ci int i; 290962306a36Sopenharmony_ci switch (stringset) { 291062306a36Sopenharmony_ci case ETH_SS_STATS: 291162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fec_stats); i++) { 291262306a36Sopenharmony_ci memcpy(data, fec_stats[i].name, ETH_GSTRING_LEN); 291362306a36Sopenharmony_ci data += ETH_GSTRING_LEN; 291462306a36Sopenharmony_ci } 291562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fec_xdp_stat_strs); i++) { 291662306a36Sopenharmony_ci strncpy(data, fec_xdp_stat_strs[i], ETH_GSTRING_LEN); 291762306a36Sopenharmony_ci data += ETH_GSTRING_LEN; 291862306a36Sopenharmony_ci } 291962306a36Sopenharmony_ci page_pool_ethtool_stats_get_strings(data); 292062306a36Sopenharmony_ci 292162306a36Sopenharmony_ci break; 292262306a36Sopenharmony_ci case ETH_SS_TEST: 292362306a36Sopenharmony_ci net_selftest_get_strings(data); 292462306a36Sopenharmony_ci break; 292562306a36Sopenharmony_ci } 292662306a36Sopenharmony_ci} 292762306a36Sopenharmony_ci 292862306a36Sopenharmony_cistatic int fec_enet_get_sset_count(struct net_device *dev, int sset) 292962306a36Sopenharmony_ci{ 293062306a36Sopenharmony_ci int count; 293162306a36Sopenharmony_ci 293262306a36Sopenharmony_ci switch (sset) { 293362306a36Sopenharmony_ci case ETH_SS_STATS: 293462306a36Sopenharmony_ci count = ARRAY_SIZE(fec_stats) + XDP_STATS_TOTAL; 293562306a36Sopenharmony_ci count += page_pool_ethtool_stats_get_count(); 293662306a36Sopenharmony_ci return count; 293762306a36Sopenharmony_ci 293862306a36Sopenharmony_ci case ETH_SS_TEST: 293962306a36Sopenharmony_ci return net_selftest_get_count(); 294062306a36Sopenharmony_ci default: 294162306a36Sopenharmony_ci return -EOPNOTSUPP; 294262306a36Sopenharmony_ci } 294362306a36Sopenharmony_ci} 294462306a36Sopenharmony_ci 294562306a36Sopenharmony_cistatic void fec_enet_clear_ethtool_stats(struct net_device *dev) 294662306a36Sopenharmony_ci{ 294762306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(dev); 294862306a36Sopenharmony_ci struct fec_enet_priv_rx_q *rxq; 294962306a36Sopenharmony_ci int i, j; 295062306a36Sopenharmony_ci 295162306a36Sopenharmony_ci /* Disable MIB statistics counters */ 295262306a36Sopenharmony_ci writel(FEC_MIB_CTRLSTAT_DISABLE, fep->hwp + FEC_MIB_CTRLSTAT); 295362306a36Sopenharmony_ci 295462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fec_stats); i++) 295562306a36Sopenharmony_ci writel(0, fep->hwp + fec_stats[i].offset); 295662306a36Sopenharmony_ci 295762306a36Sopenharmony_ci for (i = fep->num_rx_queues - 1; i >= 0; i--) { 295862306a36Sopenharmony_ci rxq = fep->rx_queue[i]; 295962306a36Sopenharmony_ci for (j = 0; j < XDP_STATS_TOTAL; j++) 296062306a36Sopenharmony_ci rxq->stats[j] = 0; 296162306a36Sopenharmony_ci } 296262306a36Sopenharmony_ci 296362306a36Sopenharmony_ci /* Don't disable MIB statistics counters */ 296462306a36Sopenharmony_ci writel(0, fep->hwp + FEC_MIB_CTRLSTAT); 296562306a36Sopenharmony_ci} 296662306a36Sopenharmony_ci 296762306a36Sopenharmony_ci#else /* !defined(CONFIG_M5272) */ 296862306a36Sopenharmony_ci#define FEC_STATS_SIZE 0 296962306a36Sopenharmony_cistatic inline void fec_enet_update_ethtool_stats(struct net_device *dev) 297062306a36Sopenharmony_ci{ 297162306a36Sopenharmony_ci} 297262306a36Sopenharmony_ci 297362306a36Sopenharmony_cistatic inline void fec_enet_clear_ethtool_stats(struct net_device *dev) 297462306a36Sopenharmony_ci{ 297562306a36Sopenharmony_ci} 297662306a36Sopenharmony_ci#endif /* !defined(CONFIG_M5272) */ 297762306a36Sopenharmony_ci 297862306a36Sopenharmony_ci/* ITR clock source is enet system clock (clk_ahb). 297962306a36Sopenharmony_ci * TCTT unit is cycle_ns * 64 cycle 298062306a36Sopenharmony_ci * So, the ICTT value = X us / (cycle_ns * 64) 298162306a36Sopenharmony_ci */ 298262306a36Sopenharmony_cistatic int fec_enet_us_to_itr_clock(struct net_device *ndev, int us) 298362306a36Sopenharmony_ci{ 298462306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 298562306a36Sopenharmony_ci 298662306a36Sopenharmony_ci return us * (fep->itr_clk_rate / 64000) / 1000; 298762306a36Sopenharmony_ci} 298862306a36Sopenharmony_ci 298962306a36Sopenharmony_ci/* Set threshold for interrupt coalescing */ 299062306a36Sopenharmony_cistatic void fec_enet_itr_coal_set(struct net_device *ndev) 299162306a36Sopenharmony_ci{ 299262306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 299362306a36Sopenharmony_ci int rx_itr, tx_itr; 299462306a36Sopenharmony_ci 299562306a36Sopenharmony_ci /* Must be greater than zero to avoid unpredictable behavior */ 299662306a36Sopenharmony_ci if (!fep->rx_time_itr || !fep->rx_pkts_itr || 299762306a36Sopenharmony_ci !fep->tx_time_itr || !fep->tx_pkts_itr) 299862306a36Sopenharmony_ci return; 299962306a36Sopenharmony_ci 300062306a36Sopenharmony_ci /* Select enet system clock as Interrupt Coalescing 300162306a36Sopenharmony_ci * timer Clock Source 300262306a36Sopenharmony_ci */ 300362306a36Sopenharmony_ci rx_itr = FEC_ITR_CLK_SEL; 300462306a36Sopenharmony_ci tx_itr = FEC_ITR_CLK_SEL; 300562306a36Sopenharmony_ci 300662306a36Sopenharmony_ci /* set ICFT and ICTT */ 300762306a36Sopenharmony_ci rx_itr |= FEC_ITR_ICFT(fep->rx_pkts_itr); 300862306a36Sopenharmony_ci rx_itr |= FEC_ITR_ICTT(fec_enet_us_to_itr_clock(ndev, fep->rx_time_itr)); 300962306a36Sopenharmony_ci tx_itr |= FEC_ITR_ICFT(fep->tx_pkts_itr); 301062306a36Sopenharmony_ci tx_itr |= FEC_ITR_ICTT(fec_enet_us_to_itr_clock(ndev, fep->tx_time_itr)); 301162306a36Sopenharmony_ci 301262306a36Sopenharmony_ci rx_itr |= FEC_ITR_EN; 301362306a36Sopenharmony_ci tx_itr |= FEC_ITR_EN; 301462306a36Sopenharmony_ci 301562306a36Sopenharmony_ci writel(tx_itr, fep->hwp + FEC_TXIC0); 301662306a36Sopenharmony_ci writel(rx_itr, fep->hwp + FEC_RXIC0); 301762306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) { 301862306a36Sopenharmony_ci writel(tx_itr, fep->hwp + FEC_TXIC1); 301962306a36Sopenharmony_ci writel(rx_itr, fep->hwp + FEC_RXIC1); 302062306a36Sopenharmony_ci writel(tx_itr, fep->hwp + FEC_TXIC2); 302162306a36Sopenharmony_ci writel(rx_itr, fep->hwp + FEC_RXIC2); 302262306a36Sopenharmony_ci } 302362306a36Sopenharmony_ci} 302462306a36Sopenharmony_ci 302562306a36Sopenharmony_cistatic int fec_enet_get_coalesce(struct net_device *ndev, 302662306a36Sopenharmony_ci struct ethtool_coalesce *ec, 302762306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 302862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 302962306a36Sopenharmony_ci{ 303062306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 303162306a36Sopenharmony_ci 303262306a36Sopenharmony_ci if (!(fep->quirks & FEC_QUIRK_HAS_COALESCE)) 303362306a36Sopenharmony_ci return -EOPNOTSUPP; 303462306a36Sopenharmony_ci 303562306a36Sopenharmony_ci ec->rx_coalesce_usecs = fep->rx_time_itr; 303662306a36Sopenharmony_ci ec->rx_max_coalesced_frames = fep->rx_pkts_itr; 303762306a36Sopenharmony_ci 303862306a36Sopenharmony_ci ec->tx_coalesce_usecs = fep->tx_time_itr; 303962306a36Sopenharmony_ci ec->tx_max_coalesced_frames = fep->tx_pkts_itr; 304062306a36Sopenharmony_ci 304162306a36Sopenharmony_ci return 0; 304262306a36Sopenharmony_ci} 304362306a36Sopenharmony_ci 304462306a36Sopenharmony_cistatic int fec_enet_set_coalesce(struct net_device *ndev, 304562306a36Sopenharmony_ci struct ethtool_coalesce *ec, 304662306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 304762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 304862306a36Sopenharmony_ci{ 304962306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 305062306a36Sopenharmony_ci struct device *dev = &fep->pdev->dev; 305162306a36Sopenharmony_ci unsigned int cycle; 305262306a36Sopenharmony_ci 305362306a36Sopenharmony_ci if (!(fep->quirks & FEC_QUIRK_HAS_COALESCE)) 305462306a36Sopenharmony_ci return -EOPNOTSUPP; 305562306a36Sopenharmony_ci 305662306a36Sopenharmony_ci if (ec->rx_max_coalesced_frames > 255) { 305762306a36Sopenharmony_ci dev_err(dev, "Rx coalesced frames exceed hardware limitation\n"); 305862306a36Sopenharmony_ci return -EINVAL; 305962306a36Sopenharmony_ci } 306062306a36Sopenharmony_ci 306162306a36Sopenharmony_ci if (ec->tx_max_coalesced_frames > 255) { 306262306a36Sopenharmony_ci dev_err(dev, "Tx coalesced frame exceed hardware limitation\n"); 306362306a36Sopenharmony_ci return -EINVAL; 306462306a36Sopenharmony_ci } 306562306a36Sopenharmony_ci 306662306a36Sopenharmony_ci cycle = fec_enet_us_to_itr_clock(ndev, ec->rx_coalesce_usecs); 306762306a36Sopenharmony_ci if (cycle > 0xFFFF) { 306862306a36Sopenharmony_ci dev_err(dev, "Rx coalesced usec exceed hardware limitation\n"); 306962306a36Sopenharmony_ci return -EINVAL; 307062306a36Sopenharmony_ci } 307162306a36Sopenharmony_ci 307262306a36Sopenharmony_ci cycle = fec_enet_us_to_itr_clock(ndev, ec->tx_coalesce_usecs); 307362306a36Sopenharmony_ci if (cycle > 0xFFFF) { 307462306a36Sopenharmony_ci dev_err(dev, "Tx coalesced usec exceed hardware limitation\n"); 307562306a36Sopenharmony_ci return -EINVAL; 307662306a36Sopenharmony_ci } 307762306a36Sopenharmony_ci 307862306a36Sopenharmony_ci fep->rx_time_itr = ec->rx_coalesce_usecs; 307962306a36Sopenharmony_ci fep->rx_pkts_itr = ec->rx_max_coalesced_frames; 308062306a36Sopenharmony_ci 308162306a36Sopenharmony_ci fep->tx_time_itr = ec->tx_coalesce_usecs; 308262306a36Sopenharmony_ci fep->tx_pkts_itr = ec->tx_max_coalesced_frames; 308362306a36Sopenharmony_ci 308462306a36Sopenharmony_ci fec_enet_itr_coal_set(ndev); 308562306a36Sopenharmony_ci 308662306a36Sopenharmony_ci return 0; 308762306a36Sopenharmony_ci} 308862306a36Sopenharmony_ci 308962306a36Sopenharmony_ci/* LPI Sleep Ts count base on tx clk (clk_ref). 309062306a36Sopenharmony_ci * The lpi sleep cnt value = X us / (cycle_ns). 309162306a36Sopenharmony_ci */ 309262306a36Sopenharmony_cistatic int fec_enet_us_to_tx_cycle(struct net_device *ndev, int us) 309362306a36Sopenharmony_ci{ 309462306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 309562306a36Sopenharmony_ci 309662306a36Sopenharmony_ci return us * (fep->clk_ref_rate / 1000) / 1000; 309762306a36Sopenharmony_ci} 309862306a36Sopenharmony_ci 309962306a36Sopenharmony_cistatic int fec_enet_eee_mode_set(struct net_device *ndev, bool enable) 310062306a36Sopenharmony_ci{ 310162306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 310262306a36Sopenharmony_ci struct ethtool_eee *p = &fep->eee; 310362306a36Sopenharmony_ci unsigned int sleep_cycle, wake_cycle; 310462306a36Sopenharmony_ci int ret = 0; 310562306a36Sopenharmony_ci 310662306a36Sopenharmony_ci if (enable) { 310762306a36Sopenharmony_ci ret = phy_init_eee(ndev->phydev, false); 310862306a36Sopenharmony_ci if (ret) 310962306a36Sopenharmony_ci return ret; 311062306a36Sopenharmony_ci 311162306a36Sopenharmony_ci sleep_cycle = fec_enet_us_to_tx_cycle(ndev, p->tx_lpi_timer); 311262306a36Sopenharmony_ci wake_cycle = sleep_cycle; 311362306a36Sopenharmony_ci } else { 311462306a36Sopenharmony_ci sleep_cycle = 0; 311562306a36Sopenharmony_ci wake_cycle = 0; 311662306a36Sopenharmony_ci } 311762306a36Sopenharmony_ci 311862306a36Sopenharmony_ci p->tx_lpi_enabled = enable; 311962306a36Sopenharmony_ci p->eee_enabled = enable; 312062306a36Sopenharmony_ci p->eee_active = enable; 312162306a36Sopenharmony_ci 312262306a36Sopenharmony_ci writel(sleep_cycle, fep->hwp + FEC_LPI_SLEEP); 312362306a36Sopenharmony_ci writel(wake_cycle, fep->hwp + FEC_LPI_WAKE); 312462306a36Sopenharmony_ci 312562306a36Sopenharmony_ci return 0; 312662306a36Sopenharmony_ci} 312762306a36Sopenharmony_ci 312862306a36Sopenharmony_cistatic int 312962306a36Sopenharmony_cifec_enet_get_eee(struct net_device *ndev, struct ethtool_eee *edata) 313062306a36Sopenharmony_ci{ 313162306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 313262306a36Sopenharmony_ci struct ethtool_eee *p = &fep->eee; 313362306a36Sopenharmony_ci 313462306a36Sopenharmony_ci if (!(fep->quirks & FEC_QUIRK_HAS_EEE)) 313562306a36Sopenharmony_ci return -EOPNOTSUPP; 313662306a36Sopenharmony_ci 313762306a36Sopenharmony_ci if (!netif_running(ndev)) 313862306a36Sopenharmony_ci return -ENETDOWN; 313962306a36Sopenharmony_ci 314062306a36Sopenharmony_ci edata->eee_enabled = p->eee_enabled; 314162306a36Sopenharmony_ci edata->eee_active = p->eee_active; 314262306a36Sopenharmony_ci edata->tx_lpi_timer = p->tx_lpi_timer; 314362306a36Sopenharmony_ci edata->tx_lpi_enabled = p->tx_lpi_enabled; 314462306a36Sopenharmony_ci 314562306a36Sopenharmony_ci return phy_ethtool_get_eee(ndev->phydev, edata); 314662306a36Sopenharmony_ci} 314762306a36Sopenharmony_ci 314862306a36Sopenharmony_cistatic int 314962306a36Sopenharmony_cifec_enet_set_eee(struct net_device *ndev, struct ethtool_eee *edata) 315062306a36Sopenharmony_ci{ 315162306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 315262306a36Sopenharmony_ci struct ethtool_eee *p = &fep->eee; 315362306a36Sopenharmony_ci int ret = 0; 315462306a36Sopenharmony_ci 315562306a36Sopenharmony_ci if (!(fep->quirks & FEC_QUIRK_HAS_EEE)) 315662306a36Sopenharmony_ci return -EOPNOTSUPP; 315762306a36Sopenharmony_ci 315862306a36Sopenharmony_ci if (!netif_running(ndev)) 315962306a36Sopenharmony_ci return -ENETDOWN; 316062306a36Sopenharmony_ci 316162306a36Sopenharmony_ci p->tx_lpi_timer = edata->tx_lpi_timer; 316262306a36Sopenharmony_ci 316362306a36Sopenharmony_ci if (!edata->eee_enabled || !edata->tx_lpi_enabled || 316462306a36Sopenharmony_ci !edata->tx_lpi_timer) 316562306a36Sopenharmony_ci ret = fec_enet_eee_mode_set(ndev, false); 316662306a36Sopenharmony_ci else 316762306a36Sopenharmony_ci ret = fec_enet_eee_mode_set(ndev, true); 316862306a36Sopenharmony_ci 316962306a36Sopenharmony_ci if (ret) 317062306a36Sopenharmony_ci return ret; 317162306a36Sopenharmony_ci 317262306a36Sopenharmony_ci return phy_ethtool_set_eee(ndev->phydev, edata); 317362306a36Sopenharmony_ci} 317462306a36Sopenharmony_ci 317562306a36Sopenharmony_cistatic void 317662306a36Sopenharmony_cifec_enet_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) 317762306a36Sopenharmony_ci{ 317862306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 317962306a36Sopenharmony_ci 318062306a36Sopenharmony_ci if (fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET) { 318162306a36Sopenharmony_ci wol->supported = WAKE_MAGIC; 318262306a36Sopenharmony_ci wol->wolopts = fep->wol_flag & FEC_WOL_FLAG_ENABLE ? WAKE_MAGIC : 0; 318362306a36Sopenharmony_ci } else { 318462306a36Sopenharmony_ci wol->supported = wol->wolopts = 0; 318562306a36Sopenharmony_ci } 318662306a36Sopenharmony_ci} 318762306a36Sopenharmony_ci 318862306a36Sopenharmony_cistatic int 318962306a36Sopenharmony_cifec_enet_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) 319062306a36Sopenharmony_ci{ 319162306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 319262306a36Sopenharmony_ci 319362306a36Sopenharmony_ci if (!(fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET)) 319462306a36Sopenharmony_ci return -EINVAL; 319562306a36Sopenharmony_ci 319662306a36Sopenharmony_ci if (wol->wolopts & ~WAKE_MAGIC) 319762306a36Sopenharmony_ci return -EINVAL; 319862306a36Sopenharmony_ci 319962306a36Sopenharmony_ci device_set_wakeup_enable(&ndev->dev, wol->wolopts & WAKE_MAGIC); 320062306a36Sopenharmony_ci if (device_may_wakeup(&ndev->dev)) 320162306a36Sopenharmony_ci fep->wol_flag |= FEC_WOL_FLAG_ENABLE; 320262306a36Sopenharmony_ci else 320362306a36Sopenharmony_ci fep->wol_flag &= (~FEC_WOL_FLAG_ENABLE); 320462306a36Sopenharmony_ci 320562306a36Sopenharmony_ci return 0; 320662306a36Sopenharmony_ci} 320762306a36Sopenharmony_ci 320862306a36Sopenharmony_cistatic const struct ethtool_ops fec_enet_ethtool_ops = { 320962306a36Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_USECS | 321062306a36Sopenharmony_ci ETHTOOL_COALESCE_MAX_FRAMES, 321162306a36Sopenharmony_ci .get_drvinfo = fec_enet_get_drvinfo, 321262306a36Sopenharmony_ci .get_regs_len = fec_enet_get_regs_len, 321362306a36Sopenharmony_ci .get_regs = fec_enet_get_regs, 321462306a36Sopenharmony_ci .nway_reset = phy_ethtool_nway_reset, 321562306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 321662306a36Sopenharmony_ci .get_coalesce = fec_enet_get_coalesce, 321762306a36Sopenharmony_ci .set_coalesce = fec_enet_set_coalesce, 321862306a36Sopenharmony_ci#ifndef CONFIG_M5272 321962306a36Sopenharmony_ci .get_pauseparam = fec_enet_get_pauseparam, 322062306a36Sopenharmony_ci .set_pauseparam = fec_enet_set_pauseparam, 322162306a36Sopenharmony_ci .get_strings = fec_enet_get_strings, 322262306a36Sopenharmony_ci .get_ethtool_stats = fec_enet_get_ethtool_stats, 322362306a36Sopenharmony_ci .get_sset_count = fec_enet_get_sset_count, 322462306a36Sopenharmony_ci#endif 322562306a36Sopenharmony_ci .get_ts_info = fec_enet_get_ts_info, 322662306a36Sopenharmony_ci .get_wol = fec_enet_get_wol, 322762306a36Sopenharmony_ci .set_wol = fec_enet_set_wol, 322862306a36Sopenharmony_ci .get_eee = fec_enet_get_eee, 322962306a36Sopenharmony_ci .set_eee = fec_enet_set_eee, 323062306a36Sopenharmony_ci .get_link_ksettings = phy_ethtool_get_link_ksettings, 323162306a36Sopenharmony_ci .set_link_ksettings = phy_ethtool_set_link_ksettings, 323262306a36Sopenharmony_ci .self_test = net_selftest, 323362306a36Sopenharmony_ci}; 323462306a36Sopenharmony_ci 323562306a36Sopenharmony_cistatic void fec_enet_free_buffers(struct net_device *ndev) 323662306a36Sopenharmony_ci{ 323762306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 323862306a36Sopenharmony_ci unsigned int i; 323962306a36Sopenharmony_ci struct fec_enet_priv_tx_q *txq; 324062306a36Sopenharmony_ci struct fec_enet_priv_rx_q *rxq; 324162306a36Sopenharmony_ci unsigned int q; 324262306a36Sopenharmony_ci 324362306a36Sopenharmony_ci for (q = 0; q < fep->num_rx_queues; q++) { 324462306a36Sopenharmony_ci rxq = fep->rx_queue[q]; 324562306a36Sopenharmony_ci for (i = 0; i < rxq->bd.ring_size; i++) 324662306a36Sopenharmony_ci page_pool_put_full_page(rxq->page_pool, rxq->rx_skb_info[i].page, false); 324762306a36Sopenharmony_ci 324862306a36Sopenharmony_ci for (i = 0; i < XDP_STATS_TOTAL; i++) 324962306a36Sopenharmony_ci rxq->stats[i] = 0; 325062306a36Sopenharmony_ci 325162306a36Sopenharmony_ci if (xdp_rxq_info_is_reg(&rxq->xdp_rxq)) 325262306a36Sopenharmony_ci xdp_rxq_info_unreg(&rxq->xdp_rxq); 325362306a36Sopenharmony_ci page_pool_destroy(rxq->page_pool); 325462306a36Sopenharmony_ci rxq->page_pool = NULL; 325562306a36Sopenharmony_ci } 325662306a36Sopenharmony_ci 325762306a36Sopenharmony_ci for (q = 0; q < fep->num_tx_queues; q++) { 325862306a36Sopenharmony_ci txq = fep->tx_queue[q]; 325962306a36Sopenharmony_ci for (i = 0; i < txq->bd.ring_size; i++) { 326062306a36Sopenharmony_ci kfree(txq->tx_bounce[i]); 326162306a36Sopenharmony_ci txq->tx_bounce[i] = NULL; 326262306a36Sopenharmony_ci 326362306a36Sopenharmony_ci if (!txq->tx_buf[i].buf_p) { 326462306a36Sopenharmony_ci txq->tx_buf[i].type = FEC_TXBUF_T_SKB; 326562306a36Sopenharmony_ci continue; 326662306a36Sopenharmony_ci } 326762306a36Sopenharmony_ci 326862306a36Sopenharmony_ci if (txq->tx_buf[i].type == FEC_TXBUF_T_SKB) { 326962306a36Sopenharmony_ci dev_kfree_skb(txq->tx_buf[i].buf_p); 327062306a36Sopenharmony_ci } else if (txq->tx_buf[i].type == FEC_TXBUF_T_XDP_NDO) { 327162306a36Sopenharmony_ci xdp_return_frame(txq->tx_buf[i].buf_p); 327262306a36Sopenharmony_ci } else { 327362306a36Sopenharmony_ci struct page *page = txq->tx_buf[i].buf_p; 327462306a36Sopenharmony_ci 327562306a36Sopenharmony_ci page_pool_put_page(page->pp, page, 0, false); 327662306a36Sopenharmony_ci } 327762306a36Sopenharmony_ci 327862306a36Sopenharmony_ci txq->tx_buf[i].buf_p = NULL; 327962306a36Sopenharmony_ci txq->tx_buf[i].type = FEC_TXBUF_T_SKB; 328062306a36Sopenharmony_ci } 328162306a36Sopenharmony_ci } 328262306a36Sopenharmony_ci} 328362306a36Sopenharmony_ci 328462306a36Sopenharmony_cistatic void fec_enet_free_queue(struct net_device *ndev) 328562306a36Sopenharmony_ci{ 328662306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 328762306a36Sopenharmony_ci int i; 328862306a36Sopenharmony_ci struct fec_enet_priv_tx_q *txq; 328962306a36Sopenharmony_ci 329062306a36Sopenharmony_ci for (i = 0; i < fep->num_tx_queues; i++) 329162306a36Sopenharmony_ci if (fep->tx_queue[i] && fep->tx_queue[i]->tso_hdrs) { 329262306a36Sopenharmony_ci txq = fep->tx_queue[i]; 329362306a36Sopenharmony_ci dma_free_coherent(&fep->pdev->dev, 329462306a36Sopenharmony_ci txq->bd.ring_size * TSO_HEADER_SIZE, 329562306a36Sopenharmony_ci txq->tso_hdrs, 329662306a36Sopenharmony_ci txq->tso_hdrs_dma); 329762306a36Sopenharmony_ci } 329862306a36Sopenharmony_ci 329962306a36Sopenharmony_ci for (i = 0; i < fep->num_rx_queues; i++) 330062306a36Sopenharmony_ci kfree(fep->rx_queue[i]); 330162306a36Sopenharmony_ci for (i = 0; i < fep->num_tx_queues; i++) 330262306a36Sopenharmony_ci kfree(fep->tx_queue[i]); 330362306a36Sopenharmony_ci} 330462306a36Sopenharmony_ci 330562306a36Sopenharmony_cistatic int fec_enet_alloc_queue(struct net_device *ndev) 330662306a36Sopenharmony_ci{ 330762306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 330862306a36Sopenharmony_ci int i; 330962306a36Sopenharmony_ci int ret = 0; 331062306a36Sopenharmony_ci struct fec_enet_priv_tx_q *txq; 331162306a36Sopenharmony_ci 331262306a36Sopenharmony_ci for (i = 0; i < fep->num_tx_queues; i++) { 331362306a36Sopenharmony_ci txq = kzalloc(sizeof(*txq), GFP_KERNEL); 331462306a36Sopenharmony_ci if (!txq) { 331562306a36Sopenharmony_ci ret = -ENOMEM; 331662306a36Sopenharmony_ci goto alloc_failed; 331762306a36Sopenharmony_ci } 331862306a36Sopenharmony_ci 331962306a36Sopenharmony_ci fep->tx_queue[i] = txq; 332062306a36Sopenharmony_ci txq->bd.ring_size = TX_RING_SIZE; 332162306a36Sopenharmony_ci fep->total_tx_ring_size += fep->tx_queue[i]->bd.ring_size; 332262306a36Sopenharmony_ci 332362306a36Sopenharmony_ci txq->tx_stop_threshold = FEC_MAX_SKB_DESCS; 332462306a36Sopenharmony_ci txq->tx_wake_threshold = FEC_MAX_SKB_DESCS + 2 * MAX_SKB_FRAGS; 332562306a36Sopenharmony_ci 332662306a36Sopenharmony_ci txq->tso_hdrs = dma_alloc_coherent(&fep->pdev->dev, 332762306a36Sopenharmony_ci txq->bd.ring_size * TSO_HEADER_SIZE, 332862306a36Sopenharmony_ci &txq->tso_hdrs_dma, 332962306a36Sopenharmony_ci GFP_KERNEL); 333062306a36Sopenharmony_ci if (!txq->tso_hdrs) { 333162306a36Sopenharmony_ci ret = -ENOMEM; 333262306a36Sopenharmony_ci goto alloc_failed; 333362306a36Sopenharmony_ci } 333462306a36Sopenharmony_ci } 333562306a36Sopenharmony_ci 333662306a36Sopenharmony_ci for (i = 0; i < fep->num_rx_queues; i++) { 333762306a36Sopenharmony_ci fep->rx_queue[i] = kzalloc(sizeof(*fep->rx_queue[i]), 333862306a36Sopenharmony_ci GFP_KERNEL); 333962306a36Sopenharmony_ci if (!fep->rx_queue[i]) { 334062306a36Sopenharmony_ci ret = -ENOMEM; 334162306a36Sopenharmony_ci goto alloc_failed; 334262306a36Sopenharmony_ci } 334362306a36Sopenharmony_ci 334462306a36Sopenharmony_ci fep->rx_queue[i]->bd.ring_size = RX_RING_SIZE; 334562306a36Sopenharmony_ci fep->total_rx_ring_size += fep->rx_queue[i]->bd.ring_size; 334662306a36Sopenharmony_ci } 334762306a36Sopenharmony_ci return ret; 334862306a36Sopenharmony_ci 334962306a36Sopenharmony_cialloc_failed: 335062306a36Sopenharmony_ci fec_enet_free_queue(ndev); 335162306a36Sopenharmony_ci return ret; 335262306a36Sopenharmony_ci} 335362306a36Sopenharmony_ci 335462306a36Sopenharmony_cistatic int 335562306a36Sopenharmony_cifec_enet_alloc_rxq_buffers(struct net_device *ndev, unsigned int queue) 335662306a36Sopenharmony_ci{ 335762306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 335862306a36Sopenharmony_ci struct fec_enet_priv_rx_q *rxq; 335962306a36Sopenharmony_ci dma_addr_t phys_addr; 336062306a36Sopenharmony_ci struct bufdesc *bdp; 336162306a36Sopenharmony_ci struct page *page; 336262306a36Sopenharmony_ci int i, err; 336362306a36Sopenharmony_ci 336462306a36Sopenharmony_ci rxq = fep->rx_queue[queue]; 336562306a36Sopenharmony_ci bdp = rxq->bd.base; 336662306a36Sopenharmony_ci 336762306a36Sopenharmony_ci err = fec_enet_create_page_pool(fep, rxq, rxq->bd.ring_size); 336862306a36Sopenharmony_ci if (err < 0) { 336962306a36Sopenharmony_ci netdev_err(ndev, "%s failed queue %d (%d)\n", __func__, queue, err); 337062306a36Sopenharmony_ci return err; 337162306a36Sopenharmony_ci } 337262306a36Sopenharmony_ci 337362306a36Sopenharmony_ci for (i = 0; i < rxq->bd.ring_size; i++) { 337462306a36Sopenharmony_ci page = page_pool_dev_alloc_pages(rxq->page_pool); 337562306a36Sopenharmony_ci if (!page) 337662306a36Sopenharmony_ci goto err_alloc; 337762306a36Sopenharmony_ci 337862306a36Sopenharmony_ci phys_addr = page_pool_get_dma_addr(page) + FEC_ENET_XDP_HEADROOM; 337962306a36Sopenharmony_ci bdp->cbd_bufaddr = cpu_to_fec32(phys_addr); 338062306a36Sopenharmony_ci 338162306a36Sopenharmony_ci rxq->rx_skb_info[i].page = page; 338262306a36Sopenharmony_ci rxq->rx_skb_info[i].offset = FEC_ENET_XDP_HEADROOM; 338362306a36Sopenharmony_ci bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); 338462306a36Sopenharmony_ci 338562306a36Sopenharmony_ci if (fep->bufdesc_ex) { 338662306a36Sopenharmony_ci struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 338762306a36Sopenharmony_ci ebdp->cbd_esc = cpu_to_fec32(BD_ENET_RX_INT); 338862306a36Sopenharmony_ci } 338962306a36Sopenharmony_ci 339062306a36Sopenharmony_ci bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); 339162306a36Sopenharmony_ci } 339262306a36Sopenharmony_ci 339362306a36Sopenharmony_ci /* Set the last buffer to wrap. */ 339462306a36Sopenharmony_ci bdp = fec_enet_get_prevdesc(bdp, &rxq->bd); 339562306a36Sopenharmony_ci bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 339662306a36Sopenharmony_ci return 0; 339762306a36Sopenharmony_ci 339862306a36Sopenharmony_ci err_alloc: 339962306a36Sopenharmony_ci fec_enet_free_buffers(ndev); 340062306a36Sopenharmony_ci return -ENOMEM; 340162306a36Sopenharmony_ci} 340262306a36Sopenharmony_ci 340362306a36Sopenharmony_cistatic int 340462306a36Sopenharmony_cifec_enet_alloc_txq_buffers(struct net_device *ndev, unsigned int queue) 340562306a36Sopenharmony_ci{ 340662306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 340762306a36Sopenharmony_ci unsigned int i; 340862306a36Sopenharmony_ci struct bufdesc *bdp; 340962306a36Sopenharmony_ci struct fec_enet_priv_tx_q *txq; 341062306a36Sopenharmony_ci 341162306a36Sopenharmony_ci txq = fep->tx_queue[queue]; 341262306a36Sopenharmony_ci bdp = txq->bd.base; 341362306a36Sopenharmony_ci for (i = 0; i < txq->bd.ring_size; i++) { 341462306a36Sopenharmony_ci txq->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL); 341562306a36Sopenharmony_ci if (!txq->tx_bounce[i]) 341662306a36Sopenharmony_ci goto err_alloc; 341762306a36Sopenharmony_ci 341862306a36Sopenharmony_ci bdp->cbd_sc = cpu_to_fec16(0); 341962306a36Sopenharmony_ci bdp->cbd_bufaddr = cpu_to_fec32(0); 342062306a36Sopenharmony_ci 342162306a36Sopenharmony_ci if (fep->bufdesc_ex) { 342262306a36Sopenharmony_ci struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 342362306a36Sopenharmony_ci ebdp->cbd_esc = cpu_to_fec32(BD_ENET_TX_INT); 342462306a36Sopenharmony_ci } 342562306a36Sopenharmony_ci 342662306a36Sopenharmony_ci bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 342762306a36Sopenharmony_ci } 342862306a36Sopenharmony_ci 342962306a36Sopenharmony_ci /* Set the last buffer to wrap. */ 343062306a36Sopenharmony_ci bdp = fec_enet_get_prevdesc(bdp, &txq->bd); 343162306a36Sopenharmony_ci bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); 343262306a36Sopenharmony_ci 343362306a36Sopenharmony_ci return 0; 343462306a36Sopenharmony_ci 343562306a36Sopenharmony_ci err_alloc: 343662306a36Sopenharmony_ci fec_enet_free_buffers(ndev); 343762306a36Sopenharmony_ci return -ENOMEM; 343862306a36Sopenharmony_ci} 343962306a36Sopenharmony_ci 344062306a36Sopenharmony_cistatic int fec_enet_alloc_buffers(struct net_device *ndev) 344162306a36Sopenharmony_ci{ 344262306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 344362306a36Sopenharmony_ci unsigned int i; 344462306a36Sopenharmony_ci 344562306a36Sopenharmony_ci for (i = 0; i < fep->num_rx_queues; i++) 344662306a36Sopenharmony_ci if (fec_enet_alloc_rxq_buffers(ndev, i)) 344762306a36Sopenharmony_ci return -ENOMEM; 344862306a36Sopenharmony_ci 344962306a36Sopenharmony_ci for (i = 0; i < fep->num_tx_queues; i++) 345062306a36Sopenharmony_ci if (fec_enet_alloc_txq_buffers(ndev, i)) 345162306a36Sopenharmony_ci return -ENOMEM; 345262306a36Sopenharmony_ci return 0; 345362306a36Sopenharmony_ci} 345462306a36Sopenharmony_ci 345562306a36Sopenharmony_cistatic int 345662306a36Sopenharmony_cifec_enet_open(struct net_device *ndev) 345762306a36Sopenharmony_ci{ 345862306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 345962306a36Sopenharmony_ci int ret; 346062306a36Sopenharmony_ci bool reset_again; 346162306a36Sopenharmony_ci 346262306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(&fep->pdev->dev); 346362306a36Sopenharmony_ci if (ret < 0) 346462306a36Sopenharmony_ci return ret; 346562306a36Sopenharmony_ci 346662306a36Sopenharmony_ci pinctrl_pm_select_default_state(&fep->pdev->dev); 346762306a36Sopenharmony_ci ret = fec_enet_clk_enable(ndev, true); 346862306a36Sopenharmony_ci if (ret) 346962306a36Sopenharmony_ci goto clk_enable; 347062306a36Sopenharmony_ci 347162306a36Sopenharmony_ci /* During the first fec_enet_open call the PHY isn't probed at this 347262306a36Sopenharmony_ci * point. Therefore the phy_reset_after_clk_enable() call within 347362306a36Sopenharmony_ci * fec_enet_clk_enable() fails. As we need this reset in order to be 347462306a36Sopenharmony_ci * sure the PHY is working correctly we check if we need to reset again 347562306a36Sopenharmony_ci * later when the PHY is probed 347662306a36Sopenharmony_ci */ 347762306a36Sopenharmony_ci if (ndev->phydev && ndev->phydev->drv) 347862306a36Sopenharmony_ci reset_again = false; 347962306a36Sopenharmony_ci else 348062306a36Sopenharmony_ci reset_again = true; 348162306a36Sopenharmony_ci 348262306a36Sopenharmony_ci /* I should reset the ring buffers here, but I don't yet know 348362306a36Sopenharmony_ci * a simple way to do that. 348462306a36Sopenharmony_ci */ 348562306a36Sopenharmony_ci 348662306a36Sopenharmony_ci ret = fec_enet_alloc_buffers(ndev); 348762306a36Sopenharmony_ci if (ret) 348862306a36Sopenharmony_ci goto err_enet_alloc; 348962306a36Sopenharmony_ci 349062306a36Sopenharmony_ci /* Init MAC prior to mii bus probe */ 349162306a36Sopenharmony_ci fec_restart(ndev); 349262306a36Sopenharmony_ci 349362306a36Sopenharmony_ci /* Call phy_reset_after_clk_enable() again if it failed during 349462306a36Sopenharmony_ci * phy_reset_after_clk_enable() before because the PHY wasn't probed. 349562306a36Sopenharmony_ci */ 349662306a36Sopenharmony_ci if (reset_again) 349762306a36Sopenharmony_ci fec_enet_phy_reset_after_clk_enable(ndev); 349862306a36Sopenharmony_ci 349962306a36Sopenharmony_ci /* Probe and connect to PHY when open the interface */ 350062306a36Sopenharmony_ci ret = fec_enet_mii_probe(ndev); 350162306a36Sopenharmony_ci if (ret) 350262306a36Sopenharmony_ci goto err_enet_mii_probe; 350362306a36Sopenharmony_ci 350462306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_ERR006687) 350562306a36Sopenharmony_ci imx6q_cpuidle_fec_irqs_used(); 350662306a36Sopenharmony_ci 350762306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_PMQOS) 350862306a36Sopenharmony_ci cpu_latency_qos_add_request(&fep->pm_qos_req, 0); 350962306a36Sopenharmony_ci 351062306a36Sopenharmony_ci napi_enable(&fep->napi); 351162306a36Sopenharmony_ci phy_start(ndev->phydev); 351262306a36Sopenharmony_ci netif_tx_start_all_queues(ndev); 351362306a36Sopenharmony_ci 351462306a36Sopenharmony_ci device_set_wakeup_enable(&ndev->dev, fep->wol_flag & 351562306a36Sopenharmony_ci FEC_WOL_FLAG_ENABLE); 351662306a36Sopenharmony_ci 351762306a36Sopenharmony_ci return 0; 351862306a36Sopenharmony_ci 351962306a36Sopenharmony_cierr_enet_mii_probe: 352062306a36Sopenharmony_ci fec_enet_free_buffers(ndev); 352162306a36Sopenharmony_cierr_enet_alloc: 352262306a36Sopenharmony_ci fec_enet_clk_enable(ndev, false); 352362306a36Sopenharmony_ciclk_enable: 352462306a36Sopenharmony_ci pm_runtime_mark_last_busy(&fep->pdev->dev); 352562306a36Sopenharmony_ci pm_runtime_put_autosuspend(&fep->pdev->dev); 352662306a36Sopenharmony_ci pinctrl_pm_select_sleep_state(&fep->pdev->dev); 352762306a36Sopenharmony_ci return ret; 352862306a36Sopenharmony_ci} 352962306a36Sopenharmony_ci 353062306a36Sopenharmony_cistatic int 353162306a36Sopenharmony_cifec_enet_close(struct net_device *ndev) 353262306a36Sopenharmony_ci{ 353362306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 353462306a36Sopenharmony_ci 353562306a36Sopenharmony_ci phy_stop(ndev->phydev); 353662306a36Sopenharmony_ci 353762306a36Sopenharmony_ci if (netif_device_present(ndev)) { 353862306a36Sopenharmony_ci napi_disable(&fep->napi); 353962306a36Sopenharmony_ci netif_tx_disable(ndev); 354062306a36Sopenharmony_ci fec_stop(ndev); 354162306a36Sopenharmony_ci } 354262306a36Sopenharmony_ci 354362306a36Sopenharmony_ci phy_disconnect(ndev->phydev); 354462306a36Sopenharmony_ci 354562306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_ERR006687) 354662306a36Sopenharmony_ci imx6q_cpuidle_fec_irqs_unused(); 354762306a36Sopenharmony_ci 354862306a36Sopenharmony_ci fec_enet_update_ethtool_stats(ndev); 354962306a36Sopenharmony_ci 355062306a36Sopenharmony_ci fec_enet_clk_enable(ndev, false); 355162306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_PMQOS) 355262306a36Sopenharmony_ci cpu_latency_qos_remove_request(&fep->pm_qos_req); 355362306a36Sopenharmony_ci 355462306a36Sopenharmony_ci pinctrl_pm_select_sleep_state(&fep->pdev->dev); 355562306a36Sopenharmony_ci pm_runtime_mark_last_busy(&fep->pdev->dev); 355662306a36Sopenharmony_ci pm_runtime_put_autosuspend(&fep->pdev->dev); 355762306a36Sopenharmony_ci 355862306a36Sopenharmony_ci fec_enet_free_buffers(ndev); 355962306a36Sopenharmony_ci 356062306a36Sopenharmony_ci return 0; 356162306a36Sopenharmony_ci} 356262306a36Sopenharmony_ci 356362306a36Sopenharmony_ci/* Set or clear the multicast filter for this adaptor. 356462306a36Sopenharmony_ci * Skeleton taken from sunlance driver. 356562306a36Sopenharmony_ci * The CPM Ethernet implementation allows Multicast as well as individual 356662306a36Sopenharmony_ci * MAC address filtering. Some of the drivers check to make sure it is 356762306a36Sopenharmony_ci * a group multicast address, and discard those that are not. I guess I 356862306a36Sopenharmony_ci * will do the same for now, but just remove the test if you want 356962306a36Sopenharmony_ci * individual filtering as well (do the upper net layers want or support 357062306a36Sopenharmony_ci * this kind of feature?). 357162306a36Sopenharmony_ci */ 357262306a36Sopenharmony_ci 357362306a36Sopenharmony_ci#define FEC_HASH_BITS 6 /* #bits in hash */ 357462306a36Sopenharmony_ci 357562306a36Sopenharmony_cistatic void set_multicast_list(struct net_device *ndev) 357662306a36Sopenharmony_ci{ 357762306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 357862306a36Sopenharmony_ci struct netdev_hw_addr *ha; 357962306a36Sopenharmony_ci unsigned int crc, tmp; 358062306a36Sopenharmony_ci unsigned char hash; 358162306a36Sopenharmony_ci unsigned int hash_high = 0, hash_low = 0; 358262306a36Sopenharmony_ci 358362306a36Sopenharmony_ci if (ndev->flags & IFF_PROMISC) { 358462306a36Sopenharmony_ci tmp = readl(fep->hwp + FEC_R_CNTRL); 358562306a36Sopenharmony_ci tmp |= 0x8; 358662306a36Sopenharmony_ci writel(tmp, fep->hwp + FEC_R_CNTRL); 358762306a36Sopenharmony_ci return; 358862306a36Sopenharmony_ci } 358962306a36Sopenharmony_ci 359062306a36Sopenharmony_ci tmp = readl(fep->hwp + FEC_R_CNTRL); 359162306a36Sopenharmony_ci tmp &= ~0x8; 359262306a36Sopenharmony_ci writel(tmp, fep->hwp + FEC_R_CNTRL); 359362306a36Sopenharmony_ci 359462306a36Sopenharmony_ci if (ndev->flags & IFF_ALLMULTI) { 359562306a36Sopenharmony_ci /* Catch all multicast addresses, so set the 359662306a36Sopenharmony_ci * filter to all 1's 359762306a36Sopenharmony_ci */ 359862306a36Sopenharmony_ci writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 359962306a36Sopenharmony_ci writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 360062306a36Sopenharmony_ci 360162306a36Sopenharmony_ci return; 360262306a36Sopenharmony_ci } 360362306a36Sopenharmony_ci 360462306a36Sopenharmony_ci /* Add the addresses in hash register */ 360562306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, ndev) { 360662306a36Sopenharmony_ci /* calculate crc32 value of mac address */ 360762306a36Sopenharmony_ci crc = ether_crc_le(ndev->addr_len, ha->addr); 360862306a36Sopenharmony_ci 360962306a36Sopenharmony_ci /* only upper 6 bits (FEC_HASH_BITS) are used 361062306a36Sopenharmony_ci * which point to specific bit in the hash registers 361162306a36Sopenharmony_ci */ 361262306a36Sopenharmony_ci hash = (crc >> (32 - FEC_HASH_BITS)) & 0x3f; 361362306a36Sopenharmony_ci 361462306a36Sopenharmony_ci if (hash > 31) 361562306a36Sopenharmony_ci hash_high |= 1 << (hash - 32); 361662306a36Sopenharmony_ci else 361762306a36Sopenharmony_ci hash_low |= 1 << hash; 361862306a36Sopenharmony_ci } 361962306a36Sopenharmony_ci 362062306a36Sopenharmony_ci writel(hash_high, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); 362162306a36Sopenharmony_ci writel(hash_low, fep->hwp + FEC_GRP_HASH_TABLE_LOW); 362262306a36Sopenharmony_ci} 362362306a36Sopenharmony_ci 362462306a36Sopenharmony_ci/* Set a MAC change in hardware. */ 362562306a36Sopenharmony_cistatic int 362662306a36Sopenharmony_cifec_set_mac_address(struct net_device *ndev, void *p) 362762306a36Sopenharmony_ci{ 362862306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 362962306a36Sopenharmony_ci struct sockaddr *addr = p; 363062306a36Sopenharmony_ci 363162306a36Sopenharmony_ci if (addr) { 363262306a36Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 363362306a36Sopenharmony_ci return -EADDRNOTAVAIL; 363462306a36Sopenharmony_ci eth_hw_addr_set(ndev, addr->sa_data); 363562306a36Sopenharmony_ci } 363662306a36Sopenharmony_ci 363762306a36Sopenharmony_ci /* Add netif status check here to avoid system hang in below case: 363862306a36Sopenharmony_ci * ifconfig ethx down; ifconfig ethx hw ether xx:xx:xx:xx:xx:xx; 363962306a36Sopenharmony_ci * After ethx down, fec all clocks are gated off and then register 364062306a36Sopenharmony_ci * access causes system hang. 364162306a36Sopenharmony_ci */ 364262306a36Sopenharmony_ci if (!netif_running(ndev)) 364362306a36Sopenharmony_ci return 0; 364462306a36Sopenharmony_ci 364562306a36Sopenharmony_ci writel(ndev->dev_addr[3] | (ndev->dev_addr[2] << 8) | 364662306a36Sopenharmony_ci (ndev->dev_addr[1] << 16) | (ndev->dev_addr[0] << 24), 364762306a36Sopenharmony_ci fep->hwp + FEC_ADDR_LOW); 364862306a36Sopenharmony_ci writel((ndev->dev_addr[5] << 16) | (ndev->dev_addr[4] << 24), 364962306a36Sopenharmony_ci fep->hwp + FEC_ADDR_HIGH); 365062306a36Sopenharmony_ci return 0; 365162306a36Sopenharmony_ci} 365262306a36Sopenharmony_ci 365362306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 365462306a36Sopenharmony_ci/** 365562306a36Sopenharmony_ci * fec_poll_controller - FEC Poll controller function 365662306a36Sopenharmony_ci * @dev: The FEC network adapter 365762306a36Sopenharmony_ci * 365862306a36Sopenharmony_ci * Polled functionality used by netconsole and others in non interrupt mode 365962306a36Sopenharmony_ci * 366062306a36Sopenharmony_ci */ 366162306a36Sopenharmony_cistatic void fec_poll_controller(struct net_device *dev) 366262306a36Sopenharmony_ci{ 366362306a36Sopenharmony_ci int i; 366462306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(dev); 366562306a36Sopenharmony_ci 366662306a36Sopenharmony_ci for (i = 0; i < FEC_IRQ_NUM; i++) { 366762306a36Sopenharmony_ci if (fep->irq[i] > 0) { 366862306a36Sopenharmony_ci disable_irq(fep->irq[i]); 366962306a36Sopenharmony_ci fec_enet_interrupt(fep->irq[i], dev); 367062306a36Sopenharmony_ci enable_irq(fep->irq[i]); 367162306a36Sopenharmony_ci } 367262306a36Sopenharmony_ci } 367362306a36Sopenharmony_ci} 367462306a36Sopenharmony_ci#endif 367562306a36Sopenharmony_ci 367662306a36Sopenharmony_cistatic inline void fec_enet_set_netdev_features(struct net_device *netdev, 367762306a36Sopenharmony_ci netdev_features_t features) 367862306a36Sopenharmony_ci{ 367962306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(netdev); 368062306a36Sopenharmony_ci netdev_features_t changed = features ^ netdev->features; 368162306a36Sopenharmony_ci 368262306a36Sopenharmony_ci netdev->features = features; 368362306a36Sopenharmony_ci 368462306a36Sopenharmony_ci /* Receive checksum has been changed */ 368562306a36Sopenharmony_ci if (changed & NETIF_F_RXCSUM) { 368662306a36Sopenharmony_ci if (features & NETIF_F_RXCSUM) 368762306a36Sopenharmony_ci fep->csum_flags |= FLAG_RX_CSUM_ENABLED; 368862306a36Sopenharmony_ci else 368962306a36Sopenharmony_ci fep->csum_flags &= ~FLAG_RX_CSUM_ENABLED; 369062306a36Sopenharmony_ci } 369162306a36Sopenharmony_ci} 369262306a36Sopenharmony_ci 369362306a36Sopenharmony_cistatic int fec_set_features(struct net_device *netdev, 369462306a36Sopenharmony_ci netdev_features_t features) 369562306a36Sopenharmony_ci{ 369662306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(netdev); 369762306a36Sopenharmony_ci netdev_features_t changed = features ^ netdev->features; 369862306a36Sopenharmony_ci 369962306a36Sopenharmony_ci if (netif_running(netdev) && changed & NETIF_F_RXCSUM) { 370062306a36Sopenharmony_ci napi_disable(&fep->napi); 370162306a36Sopenharmony_ci netif_tx_lock_bh(netdev); 370262306a36Sopenharmony_ci fec_stop(netdev); 370362306a36Sopenharmony_ci fec_enet_set_netdev_features(netdev, features); 370462306a36Sopenharmony_ci fec_restart(netdev); 370562306a36Sopenharmony_ci netif_tx_wake_all_queues(netdev); 370662306a36Sopenharmony_ci netif_tx_unlock_bh(netdev); 370762306a36Sopenharmony_ci napi_enable(&fep->napi); 370862306a36Sopenharmony_ci } else { 370962306a36Sopenharmony_ci fec_enet_set_netdev_features(netdev, features); 371062306a36Sopenharmony_ci } 371162306a36Sopenharmony_ci 371262306a36Sopenharmony_ci return 0; 371362306a36Sopenharmony_ci} 371462306a36Sopenharmony_ci 371562306a36Sopenharmony_cistatic u16 fec_enet_select_queue(struct net_device *ndev, struct sk_buff *skb, 371662306a36Sopenharmony_ci struct net_device *sb_dev) 371762306a36Sopenharmony_ci{ 371862306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 371962306a36Sopenharmony_ci u16 vlan_tag = 0; 372062306a36Sopenharmony_ci 372162306a36Sopenharmony_ci if (!(fep->quirks & FEC_QUIRK_HAS_AVB)) 372262306a36Sopenharmony_ci return netdev_pick_tx(ndev, skb, NULL); 372362306a36Sopenharmony_ci 372462306a36Sopenharmony_ci /* VLAN is present in the payload.*/ 372562306a36Sopenharmony_ci if (eth_type_vlan(skb->protocol)) { 372662306a36Sopenharmony_ci struct vlan_ethhdr *vhdr = skb_vlan_eth_hdr(skb); 372762306a36Sopenharmony_ci 372862306a36Sopenharmony_ci vlan_tag = ntohs(vhdr->h_vlan_TCI); 372962306a36Sopenharmony_ci /* VLAN is present in the skb but not yet pushed in the payload.*/ 373062306a36Sopenharmony_ci } else if (skb_vlan_tag_present(skb)) { 373162306a36Sopenharmony_ci vlan_tag = skb->vlan_tci; 373262306a36Sopenharmony_ci } else { 373362306a36Sopenharmony_ci return vlan_tag; 373462306a36Sopenharmony_ci } 373562306a36Sopenharmony_ci 373662306a36Sopenharmony_ci return fec_enet_vlan_pri_to_queue[vlan_tag >> 13]; 373762306a36Sopenharmony_ci} 373862306a36Sopenharmony_ci 373962306a36Sopenharmony_cistatic int fec_enet_bpf(struct net_device *dev, struct netdev_bpf *bpf) 374062306a36Sopenharmony_ci{ 374162306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(dev); 374262306a36Sopenharmony_ci bool is_run = netif_running(dev); 374362306a36Sopenharmony_ci struct bpf_prog *old_prog; 374462306a36Sopenharmony_ci 374562306a36Sopenharmony_ci switch (bpf->command) { 374662306a36Sopenharmony_ci case XDP_SETUP_PROG: 374762306a36Sopenharmony_ci /* No need to support the SoCs that require to 374862306a36Sopenharmony_ci * do the frame swap because the performance wouldn't be 374962306a36Sopenharmony_ci * better than the skb mode. 375062306a36Sopenharmony_ci */ 375162306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_SWAP_FRAME) 375262306a36Sopenharmony_ci return -EOPNOTSUPP; 375362306a36Sopenharmony_ci 375462306a36Sopenharmony_ci if (!bpf->prog) 375562306a36Sopenharmony_ci xdp_features_clear_redirect_target(dev); 375662306a36Sopenharmony_ci 375762306a36Sopenharmony_ci if (is_run) { 375862306a36Sopenharmony_ci napi_disable(&fep->napi); 375962306a36Sopenharmony_ci netif_tx_disable(dev); 376062306a36Sopenharmony_ci } 376162306a36Sopenharmony_ci 376262306a36Sopenharmony_ci old_prog = xchg(&fep->xdp_prog, bpf->prog); 376362306a36Sopenharmony_ci if (old_prog) 376462306a36Sopenharmony_ci bpf_prog_put(old_prog); 376562306a36Sopenharmony_ci 376662306a36Sopenharmony_ci fec_restart(dev); 376762306a36Sopenharmony_ci 376862306a36Sopenharmony_ci if (is_run) { 376962306a36Sopenharmony_ci napi_enable(&fep->napi); 377062306a36Sopenharmony_ci netif_tx_start_all_queues(dev); 377162306a36Sopenharmony_ci } 377262306a36Sopenharmony_ci 377362306a36Sopenharmony_ci if (bpf->prog) 377462306a36Sopenharmony_ci xdp_features_set_redirect_target(dev, false); 377562306a36Sopenharmony_ci 377662306a36Sopenharmony_ci return 0; 377762306a36Sopenharmony_ci 377862306a36Sopenharmony_ci case XDP_SETUP_XSK_POOL: 377962306a36Sopenharmony_ci return -EOPNOTSUPP; 378062306a36Sopenharmony_ci 378162306a36Sopenharmony_ci default: 378262306a36Sopenharmony_ci return -EOPNOTSUPP; 378362306a36Sopenharmony_ci } 378462306a36Sopenharmony_ci} 378562306a36Sopenharmony_ci 378662306a36Sopenharmony_cistatic int 378762306a36Sopenharmony_cifec_enet_xdp_get_tx_queue(struct fec_enet_private *fep, int index) 378862306a36Sopenharmony_ci{ 378962306a36Sopenharmony_ci if (unlikely(index < 0)) 379062306a36Sopenharmony_ci return 0; 379162306a36Sopenharmony_ci 379262306a36Sopenharmony_ci return (index % fep->num_tx_queues); 379362306a36Sopenharmony_ci} 379462306a36Sopenharmony_ci 379562306a36Sopenharmony_cistatic int fec_enet_txq_xmit_frame(struct fec_enet_private *fep, 379662306a36Sopenharmony_ci struct fec_enet_priv_tx_q *txq, 379762306a36Sopenharmony_ci void *frame, u32 dma_sync_len, 379862306a36Sopenharmony_ci bool ndo_xmit) 379962306a36Sopenharmony_ci{ 380062306a36Sopenharmony_ci unsigned int index, status, estatus; 380162306a36Sopenharmony_ci struct bufdesc *bdp; 380262306a36Sopenharmony_ci dma_addr_t dma_addr; 380362306a36Sopenharmony_ci int entries_free; 380462306a36Sopenharmony_ci u16 frame_len; 380562306a36Sopenharmony_ci 380662306a36Sopenharmony_ci entries_free = fec_enet_get_free_txdesc_num(txq); 380762306a36Sopenharmony_ci if (entries_free < MAX_SKB_FRAGS + 1) { 380862306a36Sopenharmony_ci netdev_err_once(fep->netdev, "NOT enough BD for SG!\n"); 380962306a36Sopenharmony_ci return -EBUSY; 381062306a36Sopenharmony_ci } 381162306a36Sopenharmony_ci 381262306a36Sopenharmony_ci /* Fill in a Tx ring entry */ 381362306a36Sopenharmony_ci bdp = txq->bd.cur; 381462306a36Sopenharmony_ci status = fec16_to_cpu(bdp->cbd_sc); 381562306a36Sopenharmony_ci status &= ~BD_ENET_TX_STATS; 381662306a36Sopenharmony_ci 381762306a36Sopenharmony_ci index = fec_enet_get_bd_index(bdp, &txq->bd); 381862306a36Sopenharmony_ci 381962306a36Sopenharmony_ci if (ndo_xmit) { 382062306a36Sopenharmony_ci struct xdp_frame *xdpf = frame; 382162306a36Sopenharmony_ci 382262306a36Sopenharmony_ci dma_addr = dma_map_single(&fep->pdev->dev, xdpf->data, 382362306a36Sopenharmony_ci xdpf->len, DMA_TO_DEVICE); 382462306a36Sopenharmony_ci if (dma_mapping_error(&fep->pdev->dev, dma_addr)) 382562306a36Sopenharmony_ci return -ENOMEM; 382662306a36Sopenharmony_ci 382762306a36Sopenharmony_ci frame_len = xdpf->len; 382862306a36Sopenharmony_ci txq->tx_buf[index].buf_p = xdpf; 382962306a36Sopenharmony_ci txq->tx_buf[index].type = FEC_TXBUF_T_XDP_NDO; 383062306a36Sopenharmony_ci } else { 383162306a36Sopenharmony_ci struct xdp_buff *xdpb = frame; 383262306a36Sopenharmony_ci struct page *page; 383362306a36Sopenharmony_ci 383462306a36Sopenharmony_ci page = virt_to_page(xdpb->data); 383562306a36Sopenharmony_ci dma_addr = page_pool_get_dma_addr(page) + 383662306a36Sopenharmony_ci (xdpb->data - xdpb->data_hard_start); 383762306a36Sopenharmony_ci dma_sync_single_for_device(&fep->pdev->dev, dma_addr, 383862306a36Sopenharmony_ci dma_sync_len, DMA_BIDIRECTIONAL); 383962306a36Sopenharmony_ci frame_len = xdpb->data_end - xdpb->data; 384062306a36Sopenharmony_ci txq->tx_buf[index].buf_p = page; 384162306a36Sopenharmony_ci txq->tx_buf[index].type = FEC_TXBUF_T_XDP_TX; 384262306a36Sopenharmony_ci } 384362306a36Sopenharmony_ci 384462306a36Sopenharmony_ci status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); 384562306a36Sopenharmony_ci if (fep->bufdesc_ex) 384662306a36Sopenharmony_ci estatus = BD_ENET_TX_INT; 384762306a36Sopenharmony_ci 384862306a36Sopenharmony_ci bdp->cbd_bufaddr = cpu_to_fec32(dma_addr); 384962306a36Sopenharmony_ci bdp->cbd_datlen = cpu_to_fec16(frame_len); 385062306a36Sopenharmony_ci 385162306a36Sopenharmony_ci if (fep->bufdesc_ex) { 385262306a36Sopenharmony_ci struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; 385362306a36Sopenharmony_ci 385462306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_AVB) 385562306a36Sopenharmony_ci estatus |= FEC_TX_BD_FTYPE(txq->bd.qid); 385662306a36Sopenharmony_ci 385762306a36Sopenharmony_ci ebdp->cbd_bdu = 0; 385862306a36Sopenharmony_ci ebdp->cbd_esc = cpu_to_fec32(estatus); 385962306a36Sopenharmony_ci } 386062306a36Sopenharmony_ci 386162306a36Sopenharmony_ci /* Make sure the updates to rest of the descriptor are performed before 386262306a36Sopenharmony_ci * transferring ownership. 386362306a36Sopenharmony_ci */ 386462306a36Sopenharmony_ci dma_wmb(); 386562306a36Sopenharmony_ci 386662306a36Sopenharmony_ci /* Send it on its way. Tell FEC it's ready, interrupt when done, 386762306a36Sopenharmony_ci * it's the last BD of the frame, and to put the CRC on the end. 386862306a36Sopenharmony_ci */ 386962306a36Sopenharmony_ci status |= (BD_ENET_TX_READY | BD_ENET_TX_TC); 387062306a36Sopenharmony_ci bdp->cbd_sc = cpu_to_fec16(status); 387162306a36Sopenharmony_ci 387262306a36Sopenharmony_ci /* If this was the last BD in the ring, start at the beginning again. */ 387362306a36Sopenharmony_ci bdp = fec_enet_get_nextdesc(bdp, &txq->bd); 387462306a36Sopenharmony_ci 387562306a36Sopenharmony_ci /* Make sure the update to bdp are performed before txq->bd.cur. */ 387662306a36Sopenharmony_ci dma_wmb(); 387762306a36Sopenharmony_ci 387862306a36Sopenharmony_ci txq->bd.cur = bdp; 387962306a36Sopenharmony_ci 388062306a36Sopenharmony_ci /* Trigger transmission start */ 388162306a36Sopenharmony_ci writel(0, txq->bd.reg_desc_active); 388262306a36Sopenharmony_ci 388362306a36Sopenharmony_ci return 0; 388462306a36Sopenharmony_ci} 388562306a36Sopenharmony_ci 388662306a36Sopenharmony_cistatic int fec_enet_xdp_tx_xmit(struct fec_enet_private *fep, 388762306a36Sopenharmony_ci int cpu, struct xdp_buff *xdp, 388862306a36Sopenharmony_ci u32 dma_sync_len) 388962306a36Sopenharmony_ci{ 389062306a36Sopenharmony_ci struct fec_enet_priv_tx_q *txq; 389162306a36Sopenharmony_ci struct netdev_queue *nq; 389262306a36Sopenharmony_ci int queue, ret; 389362306a36Sopenharmony_ci 389462306a36Sopenharmony_ci queue = fec_enet_xdp_get_tx_queue(fep, cpu); 389562306a36Sopenharmony_ci txq = fep->tx_queue[queue]; 389662306a36Sopenharmony_ci nq = netdev_get_tx_queue(fep->netdev, queue); 389762306a36Sopenharmony_ci 389862306a36Sopenharmony_ci __netif_tx_lock(nq, cpu); 389962306a36Sopenharmony_ci 390062306a36Sopenharmony_ci /* Avoid tx timeout as XDP shares the queue with kernel stack */ 390162306a36Sopenharmony_ci txq_trans_cond_update(nq); 390262306a36Sopenharmony_ci ret = fec_enet_txq_xmit_frame(fep, txq, xdp, dma_sync_len, false); 390362306a36Sopenharmony_ci 390462306a36Sopenharmony_ci __netif_tx_unlock(nq); 390562306a36Sopenharmony_ci 390662306a36Sopenharmony_ci return ret; 390762306a36Sopenharmony_ci} 390862306a36Sopenharmony_ci 390962306a36Sopenharmony_cistatic int fec_enet_xdp_xmit(struct net_device *dev, 391062306a36Sopenharmony_ci int num_frames, 391162306a36Sopenharmony_ci struct xdp_frame **frames, 391262306a36Sopenharmony_ci u32 flags) 391362306a36Sopenharmony_ci{ 391462306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(dev); 391562306a36Sopenharmony_ci struct fec_enet_priv_tx_q *txq; 391662306a36Sopenharmony_ci int cpu = smp_processor_id(); 391762306a36Sopenharmony_ci unsigned int sent_frames = 0; 391862306a36Sopenharmony_ci struct netdev_queue *nq; 391962306a36Sopenharmony_ci unsigned int queue; 392062306a36Sopenharmony_ci int i; 392162306a36Sopenharmony_ci 392262306a36Sopenharmony_ci queue = fec_enet_xdp_get_tx_queue(fep, cpu); 392362306a36Sopenharmony_ci txq = fep->tx_queue[queue]; 392462306a36Sopenharmony_ci nq = netdev_get_tx_queue(fep->netdev, queue); 392562306a36Sopenharmony_ci 392662306a36Sopenharmony_ci __netif_tx_lock(nq, cpu); 392762306a36Sopenharmony_ci 392862306a36Sopenharmony_ci /* Avoid tx timeout as XDP shares the queue with kernel stack */ 392962306a36Sopenharmony_ci txq_trans_cond_update(nq); 393062306a36Sopenharmony_ci for (i = 0; i < num_frames; i++) { 393162306a36Sopenharmony_ci if (fec_enet_txq_xmit_frame(fep, txq, frames[i], 0, true) < 0) 393262306a36Sopenharmony_ci break; 393362306a36Sopenharmony_ci sent_frames++; 393462306a36Sopenharmony_ci } 393562306a36Sopenharmony_ci 393662306a36Sopenharmony_ci __netif_tx_unlock(nq); 393762306a36Sopenharmony_ci 393862306a36Sopenharmony_ci return sent_frames; 393962306a36Sopenharmony_ci} 394062306a36Sopenharmony_ci 394162306a36Sopenharmony_cistatic int fec_hwtstamp_get(struct net_device *ndev, 394262306a36Sopenharmony_ci struct kernel_hwtstamp_config *config) 394362306a36Sopenharmony_ci{ 394462306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 394562306a36Sopenharmony_ci 394662306a36Sopenharmony_ci if (!netif_running(ndev)) 394762306a36Sopenharmony_ci return -EINVAL; 394862306a36Sopenharmony_ci 394962306a36Sopenharmony_ci if (!fep->bufdesc_ex) 395062306a36Sopenharmony_ci return -EOPNOTSUPP; 395162306a36Sopenharmony_ci 395262306a36Sopenharmony_ci fec_ptp_get(ndev, config); 395362306a36Sopenharmony_ci 395462306a36Sopenharmony_ci return 0; 395562306a36Sopenharmony_ci} 395662306a36Sopenharmony_ci 395762306a36Sopenharmony_cistatic int fec_hwtstamp_set(struct net_device *ndev, 395862306a36Sopenharmony_ci struct kernel_hwtstamp_config *config, 395962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 396062306a36Sopenharmony_ci{ 396162306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 396262306a36Sopenharmony_ci 396362306a36Sopenharmony_ci if (!netif_running(ndev)) 396462306a36Sopenharmony_ci return -EINVAL; 396562306a36Sopenharmony_ci 396662306a36Sopenharmony_ci if (!fep->bufdesc_ex) 396762306a36Sopenharmony_ci return -EOPNOTSUPP; 396862306a36Sopenharmony_ci 396962306a36Sopenharmony_ci return fec_ptp_set(ndev, config, extack); 397062306a36Sopenharmony_ci} 397162306a36Sopenharmony_ci 397262306a36Sopenharmony_cistatic const struct net_device_ops fec_netdev_ops = { 397362306a36Sopenharmony_ci .ndo_open = fec_enet_open, 397462306a36Sopenharmony_ci .ndo_stop = fec_enet_close, 397562306a36Sopenharmony_ci .ndo_start_xmit = fec_enet_start_xmit, 397662306a36Sopenharmony_ci .ndo_select_queue = fec_enet_select_queue, 397762306a36Sopenharmony_ci .ndo_set_rx_mode = set_multicast_list, 397862306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 397962306a36Sopenharmony_ci .ndo_tx_timeout = fec_timeout, 398062306a36Sopenharmony_ci .ndo_set_mac_address = fec_set_mac_address, 398162306a36Sopenharmony_ci .ndo_eth_ioctl = phy_do_ioctl_running, 398262306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 398362306a36Sopenharmony_ci .ndo_poll_controller = fec_poll_controller, 398462306a36Sopenharmony_ci#endif 398562306a36Sopenharmony_ci .ndo_set_features = fec_set_features, 398662306a36Sopenharmony_ci .ndo_bpf = fec_enet_bpf, 398762306a36Sopenharmony_ci .ndo_xdp_xmit = fec_enet_xdp_xmit, 398862306a36Sopenharmony_ci .ndo_hwtstamp_get = fec_hwtstamp_get, 398962306a36Sopenharmony_ci .ndo_hwtstamp_set = fec_hwtstamp_set, 399062306a36Sopenharmony_ci}; 399162306a36Sopenharmony_ci 399262306a36Sopenharmony_cistatic const unsigned short offset_des_active_rxq[] = { 399362306a36Sopenharmony_ci FEC_R_DES_ACTIVE_0, FEC_R_DES_ACTIVE_1, FEC_R_DES_ACTIVE_2 399462306a36Sopenharmony_ci}; 399562306a36Sopenharmony_ci 399662306a36Sopenharmony_cistatic const unsigned short offset_des_active_txq[] = { 399762306a36Sopenharmony_ci FEC_X_DES_ACTIVE_0, FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2 399862306a36Sopenharmony_ci}; 399962306a36Sopenharmony_ci 400062306a36Sopenharmony_ci /* 400162306a36Sopenharmony_ci * XXX: We need to clean up on failure exits here. 400262306a36Sopenharmony_ci * 400362306a36Sopenharmony_ci */ 400462306a36Sopenharmony_cistatic int fec_enet_init(struct net_device *ndev) 400562306a36Sopenharmony_ci{ 400662306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 400762306a36Sopenharmony_ci struct bufdesc *cbd_base; 400862306a36Sopenharmony_ci dma_addr_t bd_dma; 400962306a36Sopenharmony_ci int bd_size; 401062306a36Sopenharmony_ci unsigned int i; 401162306a36Sopenharmony_ci unsigned dsize = fep->bufdesc_ex ? sizeof(struct bufdesc_ex) : 401262306a36Sopenharmony_ci sizeof(struct bufdesc); 401362306a36Sopenharmony_ci unsigned dsize_log2 = __fls(dsize); 401462306a36Sopenharmony_ci int ret; 401562306a36Sopenharmony_ci 401662306a36Sopenharmony_ci WARN_ON(dsize != (1 << dsize_log2)); 401762306a36Sopenharmony_ci#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) 401862306a36Sopenharmony_ci fep->rx_align = 0xf; 401962306a36Sopenharmony_ci fep->tx_align = 0xf; 402062306a36Sopenharmony_ci#else 402162306a36Sopenharmony_ci fep->rx_align = 0x3; 402262306a36Sopenharmony_ci fep->tx_align = 0x3; 402362306a36Sopenharmony_ci#endif 402462306a36Sopenharmony_ci fep->rx_pkts_itr = FEC_ITR_ICFT_DEFAULT; 402562306a36Sopenharmony_ci fep->tx_pkts_itr = FEC_ITR_ICFT_DEFAULT; 402662306a36Sopenharmony_ci fep->rx_time_itr = FEC_ITR_ICTT_DEFAULT; 402762306a36Sopenharmony_ci fep->tx_time_itr = FEC_ITR_ICTT_DEFAULT; 402862306a36Sopenharmony_ci 402962306a36Sopenharmony_ci /* Check mask of the streaming and coherent API */ 403062306a36Sopenharmony_ci ret = dma_set_mask_and_coherent(&fep->pdev->dev, DMA_BIT_MASK(32)); 403162306a36Sopenharmony_ci if (ret < 0) { 403262306a36Sopenharmony_ci dev_warn(&fep->pdev->dev, "No suitable DMA available\n"); 403362306a36Sopenharmony_ci return ret; 403462306a36Sopenharmony_ci } 403562306a36Sopenharmony_ci 403662306a36Sopenharmony_ci ret = fec_enet_alloc_queue(ndev); 403762306a36Sopenharmony_ci if (ret) 403862306a36Sopenharmony_ci return ret; 403962306a36Sopenharmony_ci 404062306a36Sopenharmony_ci bd_size = (fep->total_tx_ring_size + fep->total_rx_ring_size) * dsize; 404162306a36Sopenharmony_ci 404262306a36Sopenharmony_ci /* Allocate memory for buffer descriptors. */ 404362306a36Sopenharmony_ci cbd_base = dmam_alloc_coherent(&fep->pdev->dev, bd_size, &bd_dma, 404462306a36Sopenharmony_ci GFP_KERNEL); 404562306a36Sopenharmony_ci if (!cbd_base) { 404662306a36Sopenharmony_ci ret = -ENOMEM; 404762306a36Sopenharmony_ci goto free_queue_mem; 404862306a36Sopenharmony_ci } 404962306a36Sopenharmony_ci 405062306a36Sopenharmony_ci /* Get the Ethernet address */ 405162306a36Sopenharmony_ci ret = fec_get_mac(ndev); 405262306a36Sopenharmony_ci if (ret) 405362306a36Sopenharmony_ci goto free_queue_mem; 405462306a36Sopenharmony_ci 405562306a36Sopenharmony_ci /* Set receive and transmit descriptor base. */ 405662306a36Sopenharmony_ci for (i = 0; i < fep->num_rx_queues; i++) { 405762306a36Sopenharmony_ci struct fec_enet_priv_rx_q *rxq = fep->rx_queue[i]; 405862306a36Sopenharmony_ci unsigned size = dsize * rxq->bd.ring_size; 405962306a36Sopenharmony_ci 406062306a36Sopenharmony_ci rxq->bd.qid = i; 406162306a36Sopenharmony_ci rxq->bd.base = cbd_base; 406262306a36Sopenharmony_ci rxq->bd.cur = cbd_base; 406362306a36Sopenharmony_ci rxq->bd.dma = bd_dma; 406462306a36Sopenharmony_ci rxq->bd.dsize = dsize; 406562306a36Sopenharmony_ci rxq->bd.dsize_log2 = dsize_log2; 406662306a36Sopenharmony_ci rxq->bd.reg_desc_active = fep->hwp + offset_des_active_rxq[i]; 406762306a36Sopenharmony_ci bd_dma += size; 406862306a36Sopenharmony_ci cbd_base = (struct bufdesc *)(((void *)cbd_base) + size); 406962306a36Sopenharmony_ci rxq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize); 407062306a36Sopenharmony_ci } 407162306a36Sopenharmony_ci 407262306a36Sopenharmony_ci for (i = 0; i < fep->num_tx_queues; i++) { 407362306a36Sopenharmony_ci struct fec_enet_priv_tx_q *txq = fep->tx_queue[i]; 407462306a36Sopenharmony_ci unsigned size = dsize * txq->bd.ring_size; 407562306a36Sopenharmony_ci 407662306a36Sopenharmony_ci txq->bd.qid = i; 407762306a36Sopenharmony_ci txq->bd.base = cbd_base; 407862306a36Sopenharmony_ci txq->bd.cur = cbd_base; 407962306a36Sopenharmony_ci txq->bd.dma = bd_dma; 408062306a36Sopenharmony_ci txq->bd.dsize = dsize; 408162306a36Sopenharmony_ci txq->bd.dsize_log2 = dsize_log2; 408262306a36Sopenharmony_ci txq->bd.reg_desc_active = fep->hwp + offset_des_active_txq[i]; 408362306a36Sopenharmony_ci bd_dma += size; 408462306a36Sopenharmony_ci cbd_base = (struct bufdesc *)(((void *)cbd_base) + size); 408562306a36Sopenharmony_ci txq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize); 408662306a36Sopenharmony_ci } 408762306a36Sopenharmony_ci 408862306a36Sopenharmony_ci 408962306a36Sopenharmony_ci /* The FEC Ethernet specific entries in the device structure */ 409062306a36Sopenharmony_ci ndev->watchdog_timeo = TX_TIMEOUT; 409162306a36Sopenharmony_ci ndev->netdev_ops = &fec_netdev_ops; 409262306a36Sopenharmony_ci ndev->ethtool_ops = &fec_enet_ethtool_ops; 409362306a36Sopenharmony_ci 409462306a36Sopenharmony_ci writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK); 409562306a36Sopenharmony_ci netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi); 409662306a36Sopenharmony_ci 409762306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_VLAN) 409862306a36Sopenharmony_ci /* enable hw VLAN support */ 409962306a36Sopenharmony_ci ndev->features |= NETIF_F_HW_VLAN_CTAG_RX; 410062306a36Sopenharmony_ci 410162306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_CSUM) { 410262306a36Sopenharmony_ci netif_set_tso_max_segs(ndev, FEC_MAX_TSO_SEGS); 410362306a36Sopenharmony_ci 410462306a36Sopenharmony_ci /* enable hw accelerator */ 410562306a36Sopenharmony_ci ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM 410662306a36Sopenharmony_ci | NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO); 410762306a36Sopenharmony_ci fep->csum_flags |= FLAG_RX_CSUM_ENABLED; 410862306a36Sopenharmony_ci } 410962306a36Sopenharmony_ci 411062306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) { 411162306a36Sopenharmony_ci fep->tx_align = 0; 411262306a36Sopenharmony_ci fep->rx_align = 0x3f; 411362306a36Sopenharmony_ci } 411462306a36Sopenharmony_ci 411562306a36Sopenharmony_ci ndev->hw_features = ndev->features; 411662306a36Sopenharmony_ci 411762306a36Sopenharmony_ci if (!(fep->quirks & FEC_QUIRK_SWAP_FRAME)) 411862306a36Sopenharmony_ci ndev->xdp_features = NETDEV_XDP_ACT_BASIC | 411962306a36Sopenharmony_ci NETDEV_XDP_ACT_REDIRECT; 412062306a36Sopenharmony_ci 412162306a36Sopenharmony_ci fec_restart(ndev); 412262306a36Sopenharmony_ci 412362306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_MIB_CLEAR) 412462306a36Sopenharmony_ci fec_enet_clear_ethtool_stats(ndev); 412562306a36Sopenharmony_ci else 412662306a36Sopenharmony_ci fec_enet_update_ethtool_stats(ndev); 412762306a36Sopenharmony_ci 412862306a36Sopenharmony_ci return 0; 412962306a36Sopenharmony_ci 413062306a36Sopenharmony_cifree_queue_mem: 413162306a36Sopenharmony_ci fec_enet_free_queue(ndev); 413262306a36Sopenharmony_ci return ret; 413362306a36Sopenharmony_ci} 413462306a36Sopenharmony_ci 413562306a36Sopenharmony_ci#ifdef CONFIG_OF 413662306a36Sopenharmony_cistatic int fec_reset_phy(struct platform_device *pdev) 413762306a36Sopenharmony_ci{ 413862306a36Sopenharmony_ci struct gpio_desc *phy_reset; 413962306a36Sopenharmony_ci int msec = 1, phy_post_delay = 0; 414062306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 414162306a36Sopenharmony_ci int err; 414262306a36Sopenharmony_ci 414362306a36Sopenharmony_ci if (!np) 414462306a36Sopenharmony_ci return 0; 414562306a36Sopenharmony_ci 414662306a36Sopenharmony_ci err = of_property_read_u32(np, "phy-reset-duration", &msec); 414762306a36Sopenharmony_ci /* A sane reset duration should not be longer than 1s */ 414862306a36Sopenharmony_ci if (!err && msec > 1000) 414962306a36Sopenharmony_ci msec = 1; 415062306a36Sopenharmony_ci 415162306a36Sopenharmony_ci err = of_property_read_u32(np, "phy-reset-post-delay", &phy_post_delay); 415262306a36Sopenharmony_ci /* valid reset duration should be less than 1s */ 415362306a36Sopenharmony_ci if (!err && phy_post_delay > 1000) 415462306a36Sopenharmony_ci return -EINVAL; 415562306a36Sopenharmony_ci 415662306a36Sopenharmony_ci phy_reset = devm_gpiod_get_optional(&pdev->dev, "phy-reset", 415762306a36Sopenharmony_ci GPIOD_OUT_HIGH); 415862306a36Sopenharmony_ci if (IS_ERR(phy_reset)) 415962306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, PTR_ERR(phy_reset), 416062306a36Sopenharmony_ci "failed to get phy-reset-gpios\n"); 416162306a36Sopenharmony_ci 416262306a36Sopenharmony_ci if (!phy_reset) 416362306a36Sopenharmony_ci return 0; 416462306a36Sopenharmony_ci 416562306a36Sopenharmony_ci if (msec > 20) 416662306a36Sopenharmony_ci msleep(msec); 416762306a36Sopenharmony_ci else 416862306a36Sopenharmony_ci usleep_range(msec * 1000, msec * 1000 + 1000); 416962306a36Sopenharmony_ci 417062306a36Sopenharmony_ci gpiod_set_value_cansleep(phy_reset, 0); 417162306a36Sopenharmony_ci 417262306a36Sopenharmony_ci if (!phy_post_delay) 417362306a36Sopenharmony_ci return 0; 417462306a36Sopenharmony_ci 417562306a36Sopenharmony_ci if (phy_post_delay > 20) 417662306a36Sopenharmony_ci msleep(phy_post_delay); 417762306a36Sopenharmony_ci else 417862306a36Sopenharmony_ci usleep_range(phy_post_delay * 1000, 417962306a36Sopenharmony_ci phy_post_delay * 1000 + 1000); 418062306a36Sopenharmony_ci 418162306a36Sopenharmony_ci return 0; 418262306a36Sopenharmony_ci} 418362306a36Sopenharmony_ci#else /* CONFIG_OF */ 418462306a36Sopenharmony_cistatic int fec_reset_phy(struct platform_device *pdev) 418562306a36Sopenharmony_ci{ 418662306a36Sopenharmony_ci /* 418762306a36Sopenharmony_ci * In case of platform probe, the reset has been done 418862306a36Sopenharmony_ci * by machine code. 418962306a36Sopenharmony_ci */ 419062306a36Sopenharmony_ci return 0; 419162306a36Sopenharmony_ci} 419262306a36Sopenharmony_ci#endif /* CONFIG_OF */ 419362306a36Sopenharmony_ci 419462306a36Sopenharmony_cistatic void 419562306a36Sopenharmony_cifec_enet_get_queue_num(struct platform_device *pdev, int *num_tx, int *num_rx) 419662306a36Sopenharmony_ci{ 419762306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 419862306a36Sopenharmony_ci 419962306a36Sopenharmony_ci *num_tx = *num_rx = 1; 420062306a36Sopenharmony_ci 420162306a36Sopenharmony_ci if (!np || !of_device_is_available(np)) 420262306a36Sopenharmony_ci return; 420362306a36Sopenharmony_ci 420462306a36Sopenharmony_ci /* parse the num of tx and rx queues */ 420562306a36Sopenharmony_ci of_property_read_u32(np, "fsl,num-tx-queues", num_tx); 420662306a36Sopenharmony_ci 420762306a36Sopenharmony_ci of_property_read_u32(np, "fsl,num-rx-queues", num_rx); 420862306a36Sopenharmony_ci 420962306a36Sopenharmony_ci if (*num_tx < 1 || *num_tx > FEC_ENET_MAX_TX_QS) { 421062306a36Sopenharmony_ci dev_warn(&pdev->dev, "Invalid num_tx(=%d), fall back to 1\n", 421162306a36Sopenharmony_ci *num_tx); 421262306a36Sopenharmony_ci *num_tx = 1; 421362306a36Sopenharmony_ci return; 421462306a36Sopenharmony_ci } 421562306a36Sopenharmony_ci 421662306a36Sopenharmony_ci if (*num_rx < 1 || *num_rx > FEC_ENET_MAX_RX_QS) { 421762306a36Sopenharmony_ci dev_warn(&pdev->dev, "Invalid num_rx(=%d), fall back to 1\n", 421862306a36Sopenharmony_ci *num_rx); 421962306a36Sopenharmony_ci *num_rx = 1; 422062306a36Sopenharmony_ci return; 422162306a36Sopenharmony_ci } 422262306a36Sopenharmony_ci 422362306a36Sopenharmony_ci} 422462306a36Sopenharmony_ci 422562306a36Sopenharmony_cistatic int fec_enet_get_irq_cnt(struct platform_device *pdev) 422662306a36Sopenharmony_ci{ 422762306a36Sopenharmony_ci int irq_cnt = platform_irq_count(pdev); 422862306a36Sopenharmony_ci 422962306a36Sopenharmony_ci if (irq_cnt > FEC_IRQ_NUM) 423062306a36Sopenharmony_ci irq_cnt = FEC_IRQ_NUM; /* last for pps */ 423162306a36Sopenharmony_ci else if (irq_cnt == 2) 423262306a36Sopenharmony_ci irq_cnt = 1; /* last for pps */ 423362306a36Sopenharmony_ci else if (irq_cnt <= 0) 423462306a36Sopenharmony_ci irq_cnt = 1; /* At least 1 irq is needed */ 423562306a36Sopenharmony_ci return irq_cnt; 423662306a36Sopenharmony_ci} 423762306a36Sopenharmony_ci 423862306a36Sopenharmony_cistatic void fec_enet_get_wakeup_irq(struct platform_device *pdev) 423962306a36Sopenharmony_ci{ 424062306a36Sopenharmony_ci struct net_device *ndev = platform_get_drvdata(pdev); 424162306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 424262306a36Sopenharmony_ci 424362306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_WAKEUP_FROM_INT2) 424462306a36Sopenharmony_ci fep->wake_irq = fep->irq[2]; 424562306a36Sopenharmony_ci else 424662306a36Sopenharmony_ci fep->wake_irq = fep->irq[0]; 424762306a36Sopenharmony_ci} 424862306a36Sopenharmony_ci 424962306a36Sopenharmony_cistatic int fec_enet_init_stop_mode(struct fec_enet_private *fep, 425062306a36Sopenharmony_ci struct device_node *np) 425162306a36Sopenharmony_ci{ 425262306a36Sopenharmony_ci struct device_node *gpr_np; 425362306a36Sopenharmony_ci u32 out_val[3]; 425462306a36Sopenharmony_ci int ret = 0; 425562306a36Sopenharmony_ci 425662306a36Sopenharmony_ci gpr_np = of_parse_phandle(np, "fsl,stop-mode", 0); 425762306a36Sopenharmony_ci if (!gpr_np) 425862306a36Sopenharmony_ci return 0; 425962306a36Sopenharmony_ci 426062306a36Sopenharmony_ci ret = of_property_read_u32_array(np, "fsl,stop-mode", out_val, 426162306a36Sopenharmony_ci ARRAY_SIZE(out_val)); 426262306a36Sopenharmony_ci if (ret) { 426362306a36Sopenharmony_ci dev_dbg(&fep->pdev->dev, "no stop mode property\n"); 426462306a36Sopenharmony_ci goto out; 426562306a36Sopenharmony_ci } 426662306a36Sopenharmony_ci 426762306a36Sopenharmony_ci fep->stop_gpr.gpr = syscon_node_to_regmap(gpr_np); 426862306a36Sopenharmony_ci if (IS_ERR(fep->stop_gpr.gpr)) { 426962306a36Sopenharmony_ci dev_err(&fep->pdev->dev, "could not find gpr regmap\n"); 427062306a36Sopenharmony_ci ret = PTR_ERR(fep->stop_gpr.gpr); 427162306a36Sopenharmony_ci fep->stop_gpr.gpr = NULL; 427262306a36Sopenharmony_ci goto out; 427362306a36Sopenharmony_ci } 427462306a36Sopenharmony_ci 427562306a36Sopenharmony_ci fep->stop_gpr.reg = out_val[1]; 427662306a36Sopenharmony_ci fep->stop_gpr.bit = out_val[2]; 427762306a36Sopenharmony_ci 427862306a36Sopenharmony_ciout: 427962306a36Sopenharmony_ci of_node_put(gpr_np); 428062306a36Sopenharmony_ci 428162306a36Sopenharmony_ci return ret; 428262306a36Sopenharmony_ci} 428362306a36Sopenharmony_ci 428462306a36Sopenharmony_cistatic int 428562306a36Sopenharmony_cifec_probe(struct platform_device *pdev) 428662306a36Sopenharmony_ci{ 428762306a36Sopenharmony_ci struct fec_enet_private *fep; 428862306a36Sopenharmony_ci struct fec_platform_data *pdata; 428962306a36Sopenharmony_ci phy_interface_t interface; 429062306a36Sopenharmony_ci struct net_device *ndev; 429162306a36Sopenharmony_ci int i, irq, ret = 0; 429262306a36Sopenharmony_ci const struct of_device_id *of_id; 429362306a36Sopenharmony_ci static int dev_id; 429462306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node, *phy_node; 429562306a36Sopenharmony_ci int num_tx_qs; 429662306a36Sopenharmony_ci int num_rx_qs; 429762306a36Sopenharmony_ci char irq_name[8]; 429862306a36Sopenharmony_ci int irq_cnt; 429962306a36Sopenharmony_ci struct fec_devinfo *dev_info; 430062306a36Sopenharmony_ci 430162306a36Sopenharmony_ci fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs); 430262306a36Sopenharmony_ci 430362306a36Sopenharmony_ci /* Init network device */ 430462306a36Sopenharmony_ci ndev = alloc_etherdev_mqs(sizeof(struct fec_enet_private) + 430562306a36Sopenharmony_ci FEC_STATS_SIZE, num_tx_qs, num_rx_qs); 430662306a36Sopenharmony_ci if (!ndev) 430762306a36Sopenharmony_ci return -ENOMEM; 430862306a36Sopenharmony_ci 430962306a36Sopenharmony_ci SET_NETDEV_DEV(ndev, &pdev->dev); 431062306a36Sopenharmony_ci 431162306a36Sopenharmony_ci /* setup board info structure */ 431262306a36Sopenharmony_ci fep = netdev_priv(ndev); 431362306a36Sopenharmony_ci 431462306a36Sopenharmony_ci of_id = of_match_device(fec_dt_ids, &pdev->dev); 431562306a36Sopenharmony_ci if (of_id) 431662306a36Sopenharmony_ci pdev->id_entry = of_id->data; 431762306a36Sopenharmony_ci dev_info = (struct fec_devinfo *)pdev->id_entry->driver_data; 431862306a36Sopenharmony_ci if (dev_info) 431962306a36Sopenharmony_ci fep->quirks = dev_info->quirks; 432062306a36Sopenharmony_ci 432162306a36Sopenharmony_ci fep->netdev = ndev; 432262306a36Sopenharmony_ci fep->num_rx_queues = num_rx_qs; 432362306a36Sopenharmony_ci fep->num_tx_queues = num_tx_qs; 432462306a36Sopenharmony_ci 432562306a36Sopenharmony_ci#if !defined(CONFIG_M5272) 432662306a36Sopenharmony_ci /* default enable pause frame auto negotiation */ 432762306a36Sopenharmony_ci if (fep->quirks & FEC_QUIRK_HAS_GBIT) 432862306a36Sopenharmony_ci fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG; 432962306a36Sopenharmony_ci#endif 433062306a36Sopenharmony_ci 433162306a36Sopenharmony_ci /* Select default pin state */ 433262306a36Sopenharmony_ci pinctrl_pm_select_default_state(&pdev->dev); 433362306a36Sopenharmony_ci 433462306a36Sopenharmony_ci fep->hwp = devm_platform_ioremap_resource(pdev, 0); 433562306a36Sopenharmony_ci if (IS_ERR(fep->hwp)) { 433662306a36Sopenharmony_ci ret = PTR_ERR(fep->hwp); 433762306a36Sopenharmony_ci goto failed_ioremap; 433862306a36Sopenharmony_ci } 433962306a36Sopenharmony_ci 434062306a36Sopenharmony_ci fep->pdev = pdev; 434162306a36Sopenharmony_ci fep->dev_id = dev_id++; 434262306a36Sopenharmony_ci 434362306a36Sopenharmony_ci platform_set_drvdata(pdev, ndev); 434462306a36Sopenharmony_ci 434562306a36Sopenharmony_ci if ((of_machine_is_compatible("fsl,imx6q") || 434662306a36Sopenharmony_ci of_machine_is_compatible("fsl,imx6dl")) && 434762306a36Sopenharmony_ci !of_property_read_bool(np, "fsl,err006687-workaround-present")) 434862306a36Sopenharmony_ci fep->quirks |= FEC_QUIRK_ERR006687; 434962306a36Sopenharmony_ci 435062306a36Sopenharmony_ci ret = fec_enet_ipc_handle_init(fep); 435162306a36Sopenharmony_ci if (ret) 435262306a36Sopenharmony_ci goto failed_ipc_init; 435362306a36Sopenharmony_ci 435462306a36Sopenharmony_ci if (of_property_read_bool(np, "fsl,magic-packet")) 435562306a36Sopenharmony_ci fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET; 435662306a36Sopenharmony_ci 435762306a36Sopenharmony_ci ret = fec_enet_init_stop_mode(fep, np); 435862306a36Sopenharmony_ci if (ret) 435962306a36Sopenharmony_ci goto failed_stop_mode; 436062306a36Sopenharmony_ci 436162306a36Sopenharmony_ci phy_node = of_parse_phandle(np, "phy-handle", 0); 436262306a36Sopenharmony_ci if (!phy_node && of_phy_is_fixed_link(np)) { 436362306a36Sopenharmony_ci ret = of_phy_register_fixed_link(np); 436462306a36Sopenharmony_ci if (ret < 0) { 436562306a36Sopenharmony_ci dev_err(&pdev->dev, 436662306a36Sopenharmony_ci "broken fixed-link specification\n"); 436762306a36Sopenharmony_ci goto failed_phy; 436862306a36Sopenharmony_ci } 436962306a36Sopenharmony_ci phy_node = of_node_get(np); 437062306a36Sopenharmony_ci } 437162306a36Sopenharmony_ci fep->phy_node = phy_node; 437262306a36Sopenharmony_ci 437362306a36Sopenharmony_ci ret = of_get_phy_mode(pdev->dev.of_node, &interface); 437462306a36Sopenharmony_ci if (ret) { 437562306a36Sopenharmony_ci pdata = dev_get_platdata(&pdev->dev); 437662306a36Sopenharmony_ci if (pdata) 437762306a36Sopenharmony_ci fep->phy_interface = pdata->phy; 437862306a36Sopenharmony_ci else 437962306a36Sopenharmony_ci fep->phy_interface = PHY_INTERFACE_MODE_MII; 438062306a36Sopenharmony_ci } else { 438162306a36Sopenharmony_ci fep->phy_interface = interface; 438262306a36Sopenharmony_ci } 438362306a36Sopenharmony_ci 438462306a36Sopenharmony_ci ret = fec_enet_parse_rgmii_delay(fep, np); 438562306a36Sopenharmony_ci if (ret) 438662306a36Sopenharmony_ci goto failed_rgmii_delay; 438762306a36Sopenharmony_ci 438862306a36Sopenharmony_ci fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); 438962306a36Sopenharmony_ci if (IS_ERR(fep->clk_ipg)) { 439062306a36Sopenharmony_ci ret = PTR_ERR(fep->clk_ipg); 439162306a36Sopenharmony_ci goto failed_clk; 439262306a36Sopenharmony_ci } 439362306a36Sopenharmony_ci 439462306a36Sopenharmony_ci fep->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); 439562306a36Sopenharmony_ci if (IS_ERR(fep->clk_ahb)) { 439662306a36Sopenharmony_ci ret = PTR_ERR(fep->clk_ahb); 439762306a36Sopenharmony_ci goto failed_clk; 439862306a36Sopenharmony_ci } 439962306a36Sopenharmony_ci 440062306a36Sopenharmony_ci fep->itr_clk_rate = clk_get_rate(fep->clk_ahb); 440162306a36Sopenharmony_ci 440262306a36Sopenharmony_ci /* enet_out is optional, depends on board */ 440362306a36Sopenharmony_ci fep->clk_enet_out = devm_clk_get_optional(&pdev->dev, "enet_out"); 440462306a36Sopenharmony_ci if (IS_ERR(fep->clk_enet_out)) { 440562306a36Sopenharmony_ci ret = PTR_ERR(fep->clk_enet_out); 440662306a36Sopenharmony_ci goto failed_clk; 440762306a36Sopenharmony_ci } 440862306a36Sopenharmony_ci 440962306a36Sopenharmony_ci fep->ptp_clk_on = false; 441062306a36Sopenharmony_ci mutex_init(&fep->ptp_clk_mutex); 441162306a36Sopenharmony_ci 441262306a36Sopenharmony_ci /* clk_ref is optional, depends on board */ 441362306a36Sopenharmony_ci fep->clk_ref = devm_clk_get_optional(&pdev->dev, "enet_clk_ref"); 441462306a36Sopenharmony_ci if (IS_ERR(fep->clk_ref)) { 441562306a36Sopenharmony_ci ret = PTR_ERR(fep->clk_ref); 441662306a36Sopenharmony_ci goto failed_clk; 441762306a36Sopenharmony_ci } 441862306a36Sopenharmony_ci fep->clk_ref_rate = clk_get_rate(fep->clk_ref); 441962306a36Sopenharmony_ci 442062306a36Sopenharmony_ci /* clk_2x_txclk is optional, depends on board */ 442162306a36Sopenharmony_ci if (fep->rgmii_txc_dly || fep->rgmii_rxc_dly) { 442262306a36Sopenharmony_ci fep->clk_2x_txclk = devm_clk_get(&pdev->dev, "enet_2x_txclk"); 442362306a36Sopenharmony_ci if (IS_ERR(fep->clk_2x_txclk)) 442462306a36Sopenharmony_ci fep->clk_2x_txclk = NULL; 442562306a36Sopenharmony_ci } 442662306a36Sopenharmony_ci 442762306a36Sopenharmony_ci fep->bufdesc_ex = fep->quirks & FEC_QUIRK_HAS_BUFDESC_EX; 442862306a36Sopenharmony_ci fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp"); 442962306a36Sopenharmony_ci if (IS_ERR(fep->clk_ptp)) { 443062306a36Sopenharmony_ci fep->clk_ptp = NULL; 443162306a36Sopenharmony_ci fep->bufdesc_ex = false; 443262306a36Sopenharmony_ci } 443362306a36Sopenharmony_ci 443462306a36Sopenharmony_ci ret = fec_enet_clk_enable(ndev, true); 443562306a36Sopenharmony_ci if (ret) 443662306a36Sopenharmony_ci goto failed_clk; 443762306a36Sopenharmony_ci 443862306a36Sopenharmony_ci ret = clk_prepare_enable(fep->clk_ipg); 443962306a36Sopenharmony_ci if (ret) 444062306a36Sopenharmony_ci goto failed_clk_ipg; 444162306a36Sopenharmony_ci ret = clk_prepare_enable(fep->clk_ahb); 444262306a36Sopenharmony_ci if (ret) 444362306a36Sopenharmony_ci goto failed_clk_ahb; 444462306a36Sopenharmony_ci 444562306a36Sopenharmony_ci fep->reg_phy = devm_regulator_get_optional(&pdev->dev, "phy"); 444662306a36Sopenharmony_ci if (!IS_ERR(fep->reg_phy)) { 444762306a36Sopenharmony_ci ret = regulator_enable(fep->reg_phy); 444862306a36Sopenharmony_ci if (ret) { 444962306a36Sopenharmony_ci dev_err(&pdev->dev, 445062306a36Sopenharmony_ci "Failed to enable phy regulator: %d\n", ret); 445162306a36Sopenharmony_ci goto failed_regulator; 445262306a36Sopenharmony_ci } 445362306a36Sopenharmony_ci } else { 445462306a36Sopenharmony_ci if (PTR_ERR(fep->reg_phy) == -EPROBE_DEFER) { 445562306a36Sopenharmony_ci ret = -EPROBE_DEFER; 445662306a36Sopenharmony_ci goto failed_regulator; 445762306a36Sopenharmony_ci } 445862306a36Sopenharmony_ci fep->reg_phy = NULL; 445962306a36Sopenharmony_ci } 446062306a36Sopenharmony_ci 446162306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(&pdev->dev, FEC_MDIO_PM_TIMEOUT); 446262306a36Sopenharmony_ci pm_runtime_use_autosuspend(&pdev->dev); 446362306a36Sopenharmony_ci pm_runtime_get_noresume(&pdev->dev); 446462306a36Sopenharmony_ci pm_runtime_set_active(&pdev->dev); 446562306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 446662306a36Sopenharmony_ci 446762306a36Sopenharmony_ci ret = fec_reset_phy(pdev); 446862306a36Sopenharmony_ci if (ret) 446962306a36Sopenharmony_ci goto failed_reset; 447062306a36Sopenharmony_ci 447162306a36Sopenharmony_ci irq_cnt = fec_enet_get_irq_cnt(pdev); 447262306a36Sopenharmony_ci if (fep->bufdesc_ex) 447362306a36Sopenharmony_ci fec_ptp_init(pdev, irq_cnt); 447462306a36Sopenharmony_ci 447562306a36Sopenharmony_ci ret = fec_enet_init(ndev); 447662306a36Sopenharmony_ci if (ret) 447762306a36Sopenharmony_ci goto failed_init; 447862306a36Sopenharmony_ci 447962306a36Sopenharmony_ci for (i = 0; i < irq_cnt; i++) { 448062306a36Sopenharmony_ci snprintf(irq_name, sizeof(irq_name), "int%d", i); 448162306a36Sopenharmony_ci irq = platform_get_irq_byname_optional(pdev, irq_name); 448262306a36Sopenharmony_ci if (irq < 0) 448362306a36Sopenharmony_ci irq = platform_get_irq(pdev, i); 448462306a36Sopenharmony_ci if (irq < 0) { 448562306a36Sopenharmony_ci ret = irq; 448662306a36Sopenharmony_ci goto failed_irq; 448762306a36Sopenharmony_ci } 448862306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, fec_enet_interrupt, 448962306a36Sopenharmony_ci 0, pdev->name, ndev); 449062306a36Sopenharmony_ci if (ret) 449162306a36Sopenharmony_ci goto failed_irq; 449262306a36Sopenharmony_ci 449362306a36Sopenharmony_ci fep->irq[i] = irq; 449462306a36Sopenharmony_ci } 449562306a36Sopenharmony_ci 449662306a36Sopenharmony_ci /* Decide which interrupt line is wakeup capable */ 449762306a36Sopenharmony_ci fec_enet_get_wakeup_irq(pdev); 449862306a36Sopenharmony_ci 449962306a36Sopenharmony_ci ret = fec_enet_mii_init(pdev); 450062306a36Sopenharmony_ci if (ret) 450162306a36Sopenharmony_ci goto failed_mii_init; 450262306a36Sopenharmony_ci 450362306a36Sopenharmony_ci /* Carrier starts down, phylib will bring it up */ 450462306a36Sopenharmony_ci netif_carrier_off(ndev); 450562306a36Sopenharmony_ci fec_enet_clk_enable(ndev, false); 450662306a36Sopenharmony_ci pinctrl_pm_select_sleep_state(&pdev->dev); 450762306a36Sopenharmony_ci 450862306a36Sopenharmony_ci ndev->max_mtu = PKT_MAXBUF_SIZE - ETH_HLEN - ETH_FCS_LEN; 450962306a36Sopenharmony_ci 451062306a36Sopenharmony_ci ret = register_netdev(ndev); 451162306a36Sopenharmony_ci if (ret) 451262306a36Sopenharmony_ci goto failed_register; 451362306a36Sopenharmony_ci 451462306a36Sopenharmony_ci device_init_wakeup(&ndev->dev, fep->wol_flag & 451562306a36Sopenharmony_ci FEC_WOL_HAS_MAGIC_PACKET); 451662306a36Sopenharmony_ci 451762306a36Sopenharmony_ci if (fep->bufdesc_ex && fep->ptp_clock) 451862306a36Sopenharmony_ci netdev_info(ndev, "registered PHC device %d\n", fep->dev_id); 451962306a36Sopenharmony_ci 452062306a36Sopenharmony_ci INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work); 452162306a36Sopenharmony_ci 452262306a36Sopenharmony_ci pm_runtime_mark_last_busy(&pdev->dev); 452362306a36Sopenharmony_ci pm_runtime_put_autosuspend(&pdev->dev); 452462306a36Sopenharmony_ci 452562306a36Sopenharmony_ci return 0; 452662306a36Sopenharmony_ci 452762306a36Sopenharmony_cifailed_register: 452862306a36Sopenharmony_ci fec_enet_mii_remove(fep); 452962306a36Sopenharmony_cifailed_mii_init: 453062306a36Sopenharmony_cifailed_irq: 453162306a36Sopenharmony_cifailed_init: 453262306a36Sopenharmony_ci fec_ptp_stop(pdev); 453362306a36Sopenharmony_cifailed_reset: 453462306a36Sopenharmony_ci pm_runtime_put_noidle(&pdev->dev); 453562306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 453662306a36Sopenharmony_ci if (fep->reg_phy) 453762306a36Sopenharmony_ci regulator_disable(fep->reg_phy); 453862306a36Sopenharmony_cifailed_regulator: 453962306a36Sopenharmony_ci clk_disable_unprepare(fep->clk_ahb); 454062306a36Sopenharmony_cifailed_clk_ahb: 454162306a36Sopenharmony_ci clk_disable_unprepare(fep->clk_ipg); 454262306a36Sopenharmony_cifailed_clk_ipg: 454362306a36Sopenharmony_ci fec_enet_clk_enable(ndev, false); 454462306a36Sopenharmony_cifailed_clk: 454562306a36Sopenharmony_cifailed_rgmii_delay: 454662306a36Sopenharmony_ci if (of_phy_is_fixed_link(np)) 454762306a36Sopenharmony_ci of_phy_deregister_fixed_link(np); 454862306a36Sopenharmony_ci of_node_put(phy_node); 454962306a36Sopenharmony_cifailed_stop_mode: 455062306a36Sopenharmony_cifailed_ipc_init: 455162306a36Sopenharmony_cifailed_phy: 455262306a36Sopenharmony_ci dev_id--; 455362306a36Sopenharmony_cifailed_ioremap: 455462306a36Sopenharmony_ci free_netdev(ndev); 455562306a36Sopenharmony_ci 455662306a36Sopenharmony_ci return ret; 455762306a36Sopenharmony_ci} 455862306a36Sopenharmony_ci 455962306a36Sopenharmony_cistatic void 456062306a36Sopenharmony_cifec_drv_remove(struct platform_device *pdev) 456162306a36Sopenharmony_ci{ 456262306a36Sopenharmony_ci struct net_device *ndev = platform_get_drvdata(pdev); 456362306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 456462306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 456562306a36Sopenharmony_ci int ret; 456662306a36Sopenharmony_ci 456762306a36Sopenharmony_ci ret = pm_runtime_get_sync(&pdev->dev); 456862306a36Sopenharmony_ci if (ret < 0) 456962306a36Sopenharmony_ci dev_err(&pdev->dev, 457062306a36Sopenharmony_ci "Failed to resume device in remove callback (%pe)\n", 457162306a36Sopenharmony_ci ERR_PTR(ret)); 457262306a36Sopenharmony_ci 457362306a36Sopenharmony_ci cancel_work_sync(&fep->tx_timeout_work); 457462306a36Sopenharmony_ci fec_ptp_stop(pdev); 457562306a36Sopenharmony_ci unregister_netdev(ndev); 457662306a36Sopenharmony_ci fec_enet_mii_remove(fep); 457762306a36Sopenharmony_ci if (fep->reg_phy) 457862306a36Sopenharmony_ci regulator_disable(fep->reg_phy); 457962306a36Sopenharmony_ci 458062306a36Sopenharmony_ci if (of_phy_is_fixed_link(np)) 458162306a36Sopenharmony_ci of_phy_deregister_fixed_link(np); 458262306a36Sopenharmony_ci of_node_put(fep->phy_node); 458362306a36Sopenharmony_ci 458462306a36Sopenharmony_ci /* After pm_runtime_get_sync() failed, the clks are still off, so skip 458562306a36Sopenharmony_ci * disabling them again. 458662306a36Sopenharmony_ci */ 458762306a36Sopenharmony_ci if (ret >= 0) { 458862306a36Sopenharmony_ci clk_disable_unprepare(fep->clk_ahb); 458962306a36Sopenharmony_ci clk_disable_unprepare(fep->clk_ipg); 459062306a36Sopenharmony_ci } 459162306a36Sopenharmony_ci pm_runtime_put_noidle(&pdev->dev); 459262306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 459362306a36Sopenharmony_ci 459462306a36Sopenharmony_ci free_netdev(ndev); 459562306a36Sopenharmony_ci} 459662306a36Sopenharmony_ci 459762306a36Sopenharmony_cistatic int __maybe_unused fec_suspend(struct device *dev) 459862306a36Sopenharmony_ci{ 459962306a36Sopenharmony_ci struct net_device *ndev = dev_get_drvdata(dev); 460062306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 460162306a36Sopenharmony_ci int ret; 460262306a36Sopenharmony_ci 460362306a36Sopenharmony_ci rtnl_lock(); 460462306a36Sopenharmony_ci if (netif_running(ndev)) { 460562306a36Sopenharmony_ci if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) 460662306a36Sopenharmony_ci fep->wol_flag |= FEC_WOL_FLAG_SLEEP_ON; 460762306a36Sopenharmony_ci phy_stop(ndev->phydev); 460862306a36Sopenharmony_ci napi_disable(&fep->napi); 460962306a36Sopenharmony_ci netif_tx_lock_bh(ndev); 461062306a36Sopenharmony_ci netif_device_detach(ndev); 461162306a36Sopenharmony_ci netif_tx_unlock_bh(ndev); 461262306a36Sopenharmony_ci fec_stop(ndev); 461362306a36Sopenharmony_ci if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) { 461462306a36Sopenharmony_ci fec_irqs_disable(ndev); 461562306a36Sopenharmony_ci pinctrl_pm_select_sleep_state(&fep->pdev->dev); 461662306a36Sopenharmony_ci } else { 461762306a36Sopenharmony_ci fec_irqs_disable_except_wakeup(ndev); 461862306a36Sopenharmony_ci if (fep->wake_irq > 0) { 461962306a36Sopenharmony_ci disable_irq(fep->wake_irq); 462062306a36Sopenharmony_ci enable_irq_wake(fep->wake_irq); 462162306a36Sopenharmony_ci } 462262306a36Sopenharmony_ci fec_enet_stop_mode(fep, true); 462362306a36Sopenharmony_ci } 462462306a36Sopenharmony_ci /* It's safe to disable clocks since interrupts are masked */ 462562306a36Sopenharmony_ci fec_enet_clk_enable(ndev, false); 462662306a36Sopenharmony_ci 462762306a36Sopenharmony_ci fep->rpm_active = !pm_runtime_status_suspended(dev); 462862306a36Sopenharmony_ci if (fep->rpm_active) { 462962306a36Sopenharmony_ci ret = pm_runtime_force_suspend(dev); 463062306a36Sopenharmony_ci if (ret < 0) { 463162306a36Sopenharmony_ci rtnl_unlock(); 463262306a36Sopenharmony_ci return ret; 463362306a36Sopenharmony_ci } 463462306a36Sopenharmony_ci } 463562306a36Sopenharmony_ci } 463662306a36Sopenharmony_ci rtnl_unlock(); 463762306a36Sopenharmony_ci 463862306a36Sopenharmony_ci if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) 463962306a36Sopenharmony_ci regulator_disable(fep->reg_phy); 464062306a36Sopenharmony_ci 464162306a36Sopenharmony_ci /* SOC supply clock to phy, when clock is disabled, phy link down 464262306a36Sopenharmony_ci * SOC control phy regulator, when regulator is disabled, phy link down 464362306a36Sopenharmony_ci */ 464462306a36Sopenharmony_ci if (fep->clk_enet_out || fep->reg_phy) 464562306a36Sopenharmony_ci fep->link = 0; 464662306a36Sopenharmony_ci 464762306a36Sopenharmony_ci return 0; 464862306a36Sopenharmony_ci} 464962306a36Sopenharmony_ci 465062306a36Sopenharmony_cistatic int __maybe_unused fec_resume(struct device *dev) 465162306a36Sopenharmony_ci{ 465262306a36Sopenharmony_ci struct net_device *ndev = dev_get_drvdata(dev); 465362306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 465462306a36Sopenharmony_ci int ret; 465562306a36Sopenharmony_ci int val; 465662306a36Sopenharmony_ci 465762306a36Sopenharmony_ci if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) { 465862306a36Sopenharmony_ci ret = regulator_enable(fep->reg_phy); 465962306a36Sopenharmony_ci if (ret) 466062306a36Sopenharmony_ci return ret; 466162306a36Sopenharmony_ci } 466262306a36Sopenharmony_ci 466362306a36Sopenharmony_ci rtnl_lock(); 466462306a36Sopenharmony_ci if (netif_running(ndev)) { 466562306a36Sopenharmony_ci if (fep->rpm_active) 466662306a36Sopenharmony_ci pm_runtime_force_resume(dev); 466762306a36Sopenharmony_ci 466862306a36Sopenharmony_ci ret = fec_enet_clk_enable(ndev, true); 466962306a36Sopenharmony_ci if (ret) { 467062306a36Sopenharmony_ci rtnl_unlock(); 467162306a36Sopenharmony_ci goto failed_clk; 467262306a36Sopenharmony_ci } 467362306a36Sopenharmony_ci if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) { 467462306a36Sopenharmony_ci fec_enet_stop_mode(fep, false); 467562306a36Sopenharmony_ci if (fep->wake_irq) { 467662306a36Sopenharmony_ci disable_irq_wake(fep->wake_irq); 467762306a36Sopenharmony_ci enable_irq(fep->wake_irq); 467862306a36Sopenharmony_ci } 467962306a36Sopenharmony_ci 468062306a36Sopenharmony_ci val = readl(fep->hwp + FEC_ECNTRL); 468162306a36Sopenharmony_ci val &= ~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP); 468262306a36Sopenharmony_ci writel(val, fep->hwp + FEC_ECNTRL); 468362306a36Sopenharmony_ci fep->wol_flag &= ~FEC_WOL_FLAG_SLEEP_ON; 468462306a36Sopenharmony_ci } else { 468562306a36Sopenharmony_ci pinctrl_pm_select_default_state(&fep->pdev->dev); 468662306a36Sopenharmony_ci } 468762306a36Sopenharmony_ci fec_restart(ndev); 468862306a36Sopenharmony_ci netif_tx_lock_bh(ndev); 468962306a36Sopenharmony_ci netif_device_attach(ndev); 469062306a36Sopenharmony_ci netif_tx_unlock_bh(ndev); 469162306a36Sopenharmony_ci napi_enable(&fep->napi); 469262306a36Sopenharmony_ci phy_init_hw(ndev->phydev); 469362306a36Sopenharmony_ci phy_start(ndev->phydev); 469462306a36Sopenharmony_ci } 469562306a36Sopenharmony_ci rtnl_unlock(); 469662306a36Sopenharmony_ci 469762306a36Sopenharmony_ci return 0; 469862306a36Sopenharmony_ci 469962306a36Sopenharmony_cifailed_clk: 470062306a36Sopenharmony_ci if (fep->reg_phy) 470162306a36Sopenharmony_ci regulator_disable(fep->reg_phy); 470262306a36Sopenharmony_ci return ret; 470362306a36Sopenharmony_ci} 470462306a36Sopenharmony_ci 470562306a36Sopenharmony_cistatic int __maybe_unused fec_runtime_suspend(struct device *dev) 470662306a36Sopenharmony_ci{ 470762306a36Sopenharmony_ci struct net_device *ndev = dev_get_drvdata(dev); 470862306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 470962306a36Sopenharmony_ci 471062306a36Sopenharmony_ci clk_disable_unprepare(fep->clk_ahb); 471162306a36Sopenharmony_ci clk_disable_unprepare(fep->clk_ipg); 471262306a36Sopenharmony_ci 471362306a36Sopenharmony_ci return 0; 471462306a36Sopenharmony_ci} 471562306a36Sopenharmony_ci 471662306a36Sopenharmony_cistatic int __maybe_unused fec_runtime_resume(struct device *dev) 471762306a36Sopenharmony_ci{ 471862306a36Sopenharmony_ci struct net_device *ndev = dev_get_drvdata(dev); 471962306a36Sopenharmony_ci struct fec_enet_private *fep = netdev_priv(ndev); 472062306a36Sopenharmony_ci int ret; 472162306a36Sopenharmony_ci 472262306a36Sopenharmony_ci ret = clk_prepare_enable(fep->clk_ahb); 472362306a36Sopenharmony_ci if (ret) 472462306a36Sopenharmony_ci return ret; 472562306a36Sopenharmony_ci ret = clk_prepare_enable(fep->clk_ipg); 472662306a36Sopenharmony_ci if (ret) 472762306a36Sopenharmony_ci goto failed_clk_ipg; 472862306a36Sopenharmony_ci 472962306a36Sopenharmony_ci return 0; 473062306a36Sopenharmony_ci 473162306a36Sopenharmony_cifailed_clk_ipg: 473262306a36Sopenharmony_ci clk_disable_unprepare(fep->clk_ahb); 473362306a36Sopenharmony_ci return ret; 473462306a36Sopenharmony_ci} 473562306a36Sopenharmony_ci 473662306a36Sopenharmony_cistatic const struct dev_pm_ops fec_pm_ops = { 473762306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(fec_suspend, fec_resume) 473862306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(fec_runtime_suspend, fec_runtime_resume, NULL) 473962306a36Sopenharmony_ci}; 474062306a36Sopenharmony_ci 474162306a36Sopenharmony_cistatic struct platform_driver fec_driver = { 474262306a36Sopenharmony_ci .driver = { 474362306a36Sopenharmony_ci .name = DRIVER_NAME, 474462306a36Sopenharmony_ci .pm = &fec_pm_ops, 474562306a36Sopenharmony_ci .of_match_table = fec_dt_ids, 474662306a36Sopenharmony_ci .suppress_bind_attrs = true, 474762306a36Sopenharmony_ci }, 474862306a36Sopenharmony_ci .id_table = fec_devtype, 474962306a36Sopenharmony_ci .probe = fec_probe, 475062306a36Sopenharmony_ci .remove_new = fec_drv_remove, 475162306a36Sopenharmony_ci}; 475262306a36Sopenharmony_ci 475362306a36Sopenharmony_cimodule_platform_driver(fec_driver); 475462306a36Sopenharmony_ci 475562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 4756