diff options
Diffstat (limited to 'drivers/net/ethernet/freescale')
-rw-r--r-- | drivers/net/ethernet/freescale/Kconfig | 4 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/fec.h | 37 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/fec_fixup.c | 74 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/fec_main.c | 360 |
5 files changed, 410 insertions, 67 deletions
diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig index d1ca45fbb164..2204c57fcc7d 100644 --- a/drivers/net/ethernet/freescale/Kconfig +++ b/drivers/net/ethernet/freescale/Kconfig @@ -22,8 +22,8 @@ if NET_VENDOR_FREESCALE config FEC tristate "FEC ethernet controller (of ColdFire and some i.MX CPUs)" depends on (M523x || M527x || M5272 || M528x || M520x || M532x || \ - ARCH_MXC || SOC_IMX28) - default ARCH_MXC || SOC_IMX28 if ARM + ARM || ARM64) + default y select PHYLIB select PTP_1588_CLOCK ---help--- diff --git a/drivers/net/ethernet/freescale/Makefile b/drivers/net/ethernet/freescale/Makefile index cbe21dc7e37e..7f022dd178e6 100644 --- a/drivers/net/ethernet/freescale/Makefile +++ b/drivers/net/ethernet/freescale/Makefile @@ -3,7 +3,7 @@ # obj-$(CONFIG_FEC) += fec.o -fec-objs :=fec_main.o fec_ptp.o +fec-objs :=fec_main.o fec_fixup.o fec_ptp.o CFLAGS_fec_main.o := -D__CHECK_ENDIAN__ CFLAGS_fec_ptp.o := -D__CHECK_ENDIAN__ diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index 5ea740b4cf14..1d7b3cc115e4 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -15,11 +15,13 @@ #include <linux/clocksource.h> #include <linux/net_tstamp.h> +#include <linux/pm_qos.h> #include <linux/ptp_clock_kernel.h> #include <linux/timecounter.h> #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ - defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) + defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ + defined(CONFIG_ARM64) /* * Just figures, Motorola would have to change the offsets for * registers in the same peripheral device on different models @@ -194,7 +196,7 @@ * Evidently, ARM SoCs have the FEC block generated in a * little endian mode so adjust endianness accordingly. */ -#if defined(CONFIG_ARM) +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) #define fec32_to_cpu le32_to_cpu #define fec16_to_cpu le16_to_cpu #define cpu_to_fec32 cpu_to_le32 @@ -292,7 +294,7 @@ struct bufdesc_ex { /* This device has up to three irqs on some platforms */ -#define FEC_IRQ_NUM 3 +#define FEC_IRQ_NUM 4 /* Maximum number of queues supported * ENET with AVB IP can support up to 3 independent tx queues and rx queues. @@ -353,6 +355,8 @@ struct bufdesc_ex { #define FLAG_RX_CSUM_ENABLED (BD_ENET_RX_ICE | BD_ENET_RX_PCR) #define FLAG_RX_CSUM_ERROR (BD_ENET_RX_ICE | BD_ENET_RX_PCR) +#define FEC0_MII_BUS_SHARE_TRUE 1 + /* Interrupt events/masks. */ #define FEC_ENET_HBERR ((uint)0x80000000) /* Heartbeat error */ #define FEC_ENET_BABR ((uint)0x40000000) /* Babbling receiver */ @@ -378,6 +382,8 @@ struct bufdesc_ex { #define FEC_NAPI_IMASK (FEC_ENET_MII | FEC_ENET_TS_TIMER) #define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF)) +#define FEC_ENET_ETHEREN ((uint)0x00000002) + /* ENET interrupt coalescing macro define */ #define FEC_ITR_CLK_SEL (0x1 << 30) #define FEC_ITR_EN (0x1 << 31) @@ -446,6 +452,15 @@ struct bufdesc_ex { #define FEC_QUIRK_HAS_COALESCE (1 << 13) /* Interrupt doesn't wake CPU from deep idle */ #define FEC_QUIRK_ERR006687 (1 << 14) +/* + * i.MX6Q/DL ENET cannot wake up system in wait mode because ENET tx & rx + * interrupt signal don't connect to GPC. So use pm qos to avoid cpu enter + * to wait mode. + */ +#define FEC_QUIRK_BUG_WAITMODE (1 << 15) + +/* PHY fixup flag define */ +#define FEC_QUIRK_AR8031_FIXUP (1 << 0) struct bufdesc_prop { int qid; @@ -460,6 +475,12 @@ struct bufdesc_prop { unsigned char dsize_log2; }; +struct fec_enet_stop_mode { + struct regmap *gpr; + u8 req_gpr; + u8 req_bit; +}; + struct fec_enet_priv_tx_q { struct bufdesc_prop bd; unsigned char *tx_bounce[TX_RING_SIZE]; @@ -522,10 +543,13 @@ struct fec_enet_private { /* Phylib and MDIO interface */ struct mii_bus *mii_bus; int mii_timeout; + int mii_bus_share; + bool active_in_suspend; uint phy_speed; phy_interface_t phy_interface; struct device_node *phy_node; int link; + bool fixed_link; int full_duplex; int speed; struct completion mdio_done; @@ -533,7 +557,9 @@ struct fec_enet_private { bool bufdesc_ex; int pause_flag; int wol_flag; + int wake_irq; u32 quirks; + u32 fixups; struct napi_struct napi; int csum_flags; @@ -553,6 +579,7 @@ struct fec_enet_private { int hwts_tx_en; struct delayed_work time_keep; struct regulator *reg_phy; + struct pm_qos_request pm_qos_req; unsigned int tx_align; unsigned int rx_align; @@ -576,6 +603,8 @@ struct fec_enet_private { unsigned int next_counter; u64 ethtool_stats[0]; + + struct fec_enet_stop_mode gpr; }; void fec_ptp_init(struct platform_device *pdev); @@ -584,6 +613,8 @@ void fec_ptp_start_cyclecounter(struct net_device *ndev); int fec_ptp_set(struct net_device *ndev, struct ifreq *ifr); int fec_ptp_get(struct net_device *ndev, struct ifreq *ifr); uint fec_ptp_check_pps_event(struct fec_enet_private *fep); +void fec_enet_register_fixup(struct net_device *ndev); +int of_fec_enet_parse_fixup(struct device_node *np); /****************************************************************************/ #endif /* FEC_H */ diff --git a/drivers/net/ethernet/freescale/fec_fixup.c b/drivers/net/ethernet/freescale/fec_fixup.c new file mode 100644 index 000000000000..5a8497c22acd --- /dev/null +++ b/drivers/net/ethernet/freescale/fec_fixup.c @@ -0,0 +1,74 @@ +/* + * Copyright 2017 NXP + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/netdevice.h> +#include <linux/phy.h> +#include "fec.h" + +#define PHY_ID_AR8031 0x004dd074 + +static int ar8031_phy_fixup(struct phy_device *dev) +{ + u16 val; + + /* Set RGMII IO voltage to 1.8V */ + phy_write(dev, 0x1d, 0x1f); + phy_write(dev, 0x1e, 0x8); + + /* Disable phy AR8031 SmartEEE function */ + phy_write(dev, 0xd, 0x3); + phy_write(dev, 0xe, 0x805d); + phy_write(dev, 0xd, 0x4003); + val = phy_read(dev, 0xe); + val &= ~(0x1 << 8); + phy_write(dev, 0xe, val); + + /* Introduce tx clock delay */ + phy_write(dev, 0x1d, 0x5); + phy_write(dev, 0x1e, 0x100); + + return 0; +} + +void fec_enet_register_fixup(struct net_device *ndev) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + static int registered = 0; + int err; + + if (!IS_BUILTIN(CONFIG_PHYLIB)) + return; + + if (fep->fixups & FEC_QUIRK_AR8031_FIXUP) { + static int ar8031_registered = 0; + + if (ar8031_registered) + return; + err = phy_register_fixup_for_uid(PHY_ID_AR8031, 0xffffffef, + ar8031_phy_fixup); + if (err) + netdev_info(ndev, "Cannot register PHY board fixup\n"); + registered = 1; + } +} + +int of_fec_enet_parse_fixup(struct device_node *np) +{ + int fixups = 0; + + if (of_get_property(np, "fsl,ar8031-phy-fixup", NULL)) + fixups |= FEC_QUIRK_AR8031_FIXUP; + + return fixups; +} diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 12aef1b15356..41a31f2fd2fe 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -18,13 +18,14 @@ * Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be) * Copyright (c) 2004-2006 Macq Electronique SA. * - * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. + * Copyright (C) 2010-2014 Freescale Semiconductor, Inc. + * + * Copyright 2017 NXP */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/string.h> -#include <linux/pm_runtime.h> #include <linux/ptrace.h> #include <linux/errno.h> #include <linux/ioport.h> @@ -47,6 +48,7 @@ #include <linux/io.h> #include <linux/irq.h> #include <linux/clk.h> +#include <linux/clk/clk-conf.h> #include <linux/platform_device.h> #include <linux/mdio.h> #include <linux/phy.h> @@ -59,10 +61,14 @@ #include <linux/regulator/consumer.h> #include <linux/if_vlan.h> #include <linux/pinctrl/consumer.h> +#include <linux/pm_runtime.h> +#include <linux/busfreq-imx.h> #include <linux/prefetch.h> -#include <soc/imx/cpuidle.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> #include <asm/cacheflush.h> +#include <soc/imx/cpuidle.h> #include "fec.h" @@ -72,6 +78,7 @@ static void fec_enet_itr_coal_init(struct net_device *ndev); #define DRIVER_NAME "fec" #define FEC_ENET_GET_QUQUE(_x) ((_x == 0) ? 1 : ((_x == 1) ? 2 : 0)) +static const u16 fec_enet_vlan_pri_to_queue[8] = {1, 1, 1, 1, 2, 2, 2, 2}; /* Pause frame feild and FIFO threshold */ #define FEC_ENET_FCE (1 << 5) @@ -102,7 +109,7 @@ static struct platform_device_id fec_devtype[] = { .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR006358 | - FEC_QUIRK_HAS_RACC, + FEC_QUIRK_HAS_RACC | FEC_QUIRK_BUG_WAITMODE, }, { .name = "mvf600-fec", .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_RACC, @@ -120,6 +127,13 @@ static struct platform_device_id fec_devtype[] = { FEC_QUIRK_HAS_VLAN | FEC_QUIRK_BUG_CAPTURE | FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE, }, { + .name = "imx8qm-fec", + .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | + FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | + FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | + FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | + FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE, + }, { /* sentinel */ } }; @@ -133,6 +147,7 @@ enum imx_fec_type { MVF600_FEC, IMX6SX_FEC, IMX6UL_FEC, + IMX8QM_FEC, }; static const struct of_device_id fec_dt_ids[] = { @@ -143,6 +158,7 @@ static const struct of_device_id fec_dt_ids[] = { { .compatible = "fsl,mvf600-fec", .data = &fec_devtype[MVF600_FEC], }, { .compatible = "fsl,imx6sx-fec", .data = &fec_devtype[IMX6SX_FEC], }, { .compatible = "fsl,imx6ul-fec", .data = &fec_devtype[IMX6UL_FEC], }, + { .compatible = "fsl,imx8qm-fec", .data = &fec_devtype[IMX8QM_FEC], }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fec_dt_ids); @@ -189,7 +205,8 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); * account when setting it. */ #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ - defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) + defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ + defined(CONFIG_ARM64) #define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16) #else #define OPT_FRAME_SIZE 0 @@ -235,14 +252,14 @@ static struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, struct bufdesc_prop *bd) { return (bdp >= bd->last) ? bd->base - : (struct bufdesc *)(((unsigned)bdp) + bd->dsize); + : (struct bufdesc *)(((void *)bdp) + bd->dsize); } static struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, struct bufdesc_prop *bd) { return (bdp <= bd->base) ? bd->last - : (struct bufdesc *)(((unsigned)bdp) - bd->dsize); + : (struct bufdesc *)(((void *)bdp) - bd->dsize); } static int fec_enet_get_bd_index(struct bufdesc *bdp, @@ -896,7 +913,7 @@ fec_restart(struct net_device *ndev) u32 val; u32 temp_mac[2]; u32 rcntl = OPT_FRAME_SIZE | 0x04; - u32 ecntl = 0x2; /* ETHEREN */ + u32 ecntl = FEC_ENET_ETHEREN; /* ETHEREN */ /* Whack a reset. We should wait for this. * For i.MX6SX SOC, enet use AXI bus, we use disable MAC @@ -955,6 +972,7 @@ fec_restart(struct net_device *ndev) writel(val, fep->hwp + FEC_RACC); writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_FTRL); } + writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_FTRL); #endif /* @@ -1073,11 +1091,37 @@ fec_restart(struct net_device *ndev) } +static int fec_enet_stop_mode(struct fec_enet_private *fep, bool enabled) +{ + struct fec_platform_data *pdata = fep->pdev->dev.platform_data; + + if (fep->gpr.gpr) { + if (enabled) + regmap_update_bits(fep->gpr.gpr, fep->gpr.req_gpr, + 1 << fep->gpr.req_bit, + 1 << fep->gpr.req_bit); + else + regmap_update_bits(fep->gpr.gpr, fep->gpr.req_gpr, + 1 << fep->gpr.req_bit, + 0); + } else if (pdata && pdata->sleep_mode_enable) { + pdata->sleep_mode_enable(enabled); + } + + return 0; +} + +static inline void fec_irqs_disable(struct net_device *ndev) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + + writel(0, fep->hwp + FEC_IMASK); +} + static void fec_stop(struct net_device *ndev) { struct fec_enet_private *fep = netdev_priv(ndev); - struct fec_platform_data *pdata = fep->pdev->dev.platform_data; u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8); u32 val; @@ -1100,15 +1144,14 @@ fec_stop(struct net_device *ndev) writel(1, fep->hwp + FEC_ECNTRL); udelay(10); } - writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); + writel(FEC_ENET_MII, fep->hwp + FEC_IMASK); } else { - writel(FEC_DEFAULT_IMASK | FEC_ENET_WAKEUP, fep->hwp + FEC_IMASK); + writel(FEC_ENET_MII | FEC_ENET_WAKEUP, fep->hwp + FEC_IMASK); val = readl(fep->hwp + FEC_ECNTRL); val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP); writel(val, fep->hwp + FEC_ECNTRL); - if (pdata && pdata->sleep_mode_enable) - pdata->sleep_mode_enable(true); + fec_enet_stop_mode(fep, true); } writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); @@ -1838,13 +1881,10 @@ static int fec_enet_clk_enable(struct net_device *ndev, bool enable) int ret; if (enable) { - ret = clk_prepare_enable(fep->clk_ahb); - if (ret) - return ret; if (fep->clk_enet_out) { ret = clk_prepare_enable(fep->clk_enet_out); if (ret) - goto failed_clk_enet_out; + return ret; } if (fep->clk_ptp) { mutex_lock(&fep->ptp_clk_mutex); @@ -1863,7 +1903,6 @@ static int fec_enet_clk_enable(struct net_device *ndev, bool enable) goto failed_clk_ref; } } else { - clk_disable_unprepare(fep->clk_ahb); if (fep->clk_enet_out) clk_disable_unprepare(fep->clk_enet_out); if (fep->clk_ptp) { @@ -1884,12 +1923,29 @@ failed_clk_ref: failed_clk_ptp: if (fep->clk_enet_out) clk_disable_unprepare(fep->clk_enet_out); -failed_clk_enet_out: - clk_disable_unprepare(fep->clk_ahb); return ret; } +static int fec_restore_mii_bus(struct net_device *ndev) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + int ret; + + ret = pm_runtime_get_sync(&fep->pdev->dev); + if (ret < 0) + return ret; + + writel(0xffc00000, fep->hwp + FEC_IEVENT); + writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); + writel(FEC_ENET_MII, fep->hwp + FEC_IMASK); + writel(FEC_ENET_ETHEREN, fep->hwp + FEC_ECNTRL); + + pm_runtime_mark_last_busy(&fep->pdev->dev); + pm_runtime_put_autosuspend(&fep->pdev->dev); + return 0; +} + static int fec_enet_mii_probe(struct net_device *ndev) { struct fec_enet_private *fep = netdev_priv(ndev); @@ -1957,6 +2013,7 @@ static int fec_enet_mii_probe(struct net_device *ndev) static int fec_enet_mii_init(struct platform_device *pdev) { static struct mii_bus *fec0_mii_bus; + static int *fec_mii_bus_share; struct net_device *ndev = platform_get_drvdata(pdev); struct fec_enet_private *fep = netdev_priv(ndev); struct device_node *node; @@ -1983,6 +2040,7 @@ static int fec_enet_mii_init(struct platform_device *pdev) /* fec1 uses fec0 mii_bus */ if (mii_cnt && fec0_mii_bus) { fep->mii_bus = fec0_mii_bus; + *fec_mii_bus_share = FEC0_MII_BUS_SHARE_TRUE; mii_cnt++; return 0; } @@ -2046,6 +2104,8 @@ static int fec_enet_mii_init(struct platform_device *pdev) if (node) { err = of_mdiobus_register(fep->mii_bus, node); of_node_put(node); + } else if (fep->phy_node && !fep->fixed_link) { + err = -EPROBE_DEFER; } else { err = mdiobus_register(fep->mii_bus); } @@ -2056,8 +2116,10 @@ static int fec_enet_mii_init(struct platform_device *pdev) mii_cnt++; /* save fec0 mii_bus */ - if (fep->quirks & FEC_QUIRK_SINGLE_MDIO) + if (fep->quirks & FEC_QUIRK_SINGLE_MDIO) { fec0_mii_bus = fep->mii_bus; + fec_mii_bus_share = &fep->mii_bus_share; + } return 0; @@ -2101,7 +2163,8 @@ static int fec_enet_get_regs_len(struct net_device *ndev) /* List of registers that can be safety be read to dump them with ethtool */ #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ - defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) + defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ + defined(CONFIG_ARM64) static u32 fec_enet_register_offset[] = { FEC_IEVENT, FEC_IMASK, FEC_R_DES_ACTIVE_0, FEC_X_DES_ACTIVE_0, FEC_ECNTRL, FEC_MII_DATA, FEC_MII_SPEED, FEC_MIB_CTRLSTAT, FEC_R_CNTRL, @@ -2557,15 +2620,10 @@ fec_enet_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) return -EINVAL; device_set_wakeup_enable(&ndev->dev, wol->wolopts & WAKE_MAGIC); - if (device_may_wakeup(&ndev->dev)) { + if (device_may_wakeup(&ndev->dev)) fep->wol_flag |= FEC_WOL_FLAG_ENABLE; - if (fep->irq[0] > 0) - enable_irq_wake(fep->irq[0]); - } else { + else fep->wol_flag &= (~FEC_WOL_FLAG_ENABLE); - if (fep->irq[0] > 0) - disable_irq_wake(fep->irq[0]); - } return 0; } @@ -2664,7 +2722,7 @@ static void fec_enet_free_queue(struct net_device *ndev) for (i = 0; i < fep->num_tx_queues; i++) if (fep->tx_queue[i] && fep->tx_queue[i]->tso_hdrs) { txq = fep->tx_queue[i]; - dma_free_coherent(NULL, + dma_free_coherent(&fep->pdev->dev, txq->bd.ring_size * TSO_HEADER_SIZE, txq->tso_hdrs, txq->tso_hdrs_dma); @@ -2698,7 +2756,7 @@ static int fec_enet_alloc_queue(struct net_device *ndev) txq->tx_wake_threshold = (txq->bd.ring_size - txq->tx_stop_threshold) / 2; - txq->tso_hdrs = dma_alloc_coherent(NULL, + txq->tso_hdrs = dma_alloc_coherent(&fep->pdev->dev, txq->bd.ring_size * TSO_HEADER_SIZE, &txq->tso_hdrs_dma, GFP_KERNEL); @@ -2820,10 +2878,29 @@ static int fec_enet_alloc_buffers(struct net_device *ndev) return 0; } +static inline bool fec_enet_irq_workaround(struct fec_enet_private *fep) +{ + struct device_node *np = fep->pdev->dev.of_node; + struct device_node *intr_node; + + intr_node = of_parse_phandle(np, "interrupts-extended", 0); + if (intr_node && !strcmp(intr_node->name, "gpio")) { + /* + * If the interrupt controller is a GPIO node, it must have + * applied the workaround for WAIT mode bug. + */ + return true; + } + + return false; +} + static int fec_enet_open(struct net_device *ndev) { struct fec_enet_private *fep = netdev_priv(ndev); + const struct platform_device_id *id_entry = + platform_get_device_id(fep->pdev); int ret; ret = pm_runtime_get_sync(&fep->pdev->dev); @@ -2858,6 +2935,16 @@ fec_enet_open(struct net_device *ndev) phy_start(ndev->phydev); netif_tx_start_all_queues(ndev); + if ((id_entry->driver_data & FEC_QUIRK_BUG_WAITMODE) && + !fec_enet_irq_workaround(fep)) + pm_qos_add_request(&fep->pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + 0); + else + pm_qos_add_request(&fep->pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + device_set_wakeup_enable(&ndev->dev, fep->wol_flag & FEC_WOL_FLAG_ENABLE); @@ -2870,7 +2957,8 @@ err_enet_alloc: clk_enable: pm_runtime_mark_last_busy(&fep->pdev->dev); pm_runtime_put_autosuspend(&fep->pdev->dev); - pinctrl_pm_select_sleep_state(&fep->pdev->dev); + if (!fep->mii_bus_share) + pinctrl_pm_select_sleep_state(&fep->pdev->dev); return ret; } @@ -2888,6 +2976,7 @@ fec_enet_close(struct net_device *ndev) } phy_disconnect(ndev->phydev); + ndev->phydev = NULL; if (fep->quirks & FEC_QUIRK_ERR006687) imx6q_cpuidle_fec_irqs_unused(); @@ -2895,7 +2984,9 @@ fec_enet_close(struct net_device *ndev) fec_enet_update_ethtool_stats(ndev); fec_enet_clk_enable(ndev, false); - pinctrl_pm_select_sleep_state(&fep->pdev->dev); + pm_qos_remove_request(&fep->pm_qos_req); + if (!fep->mii_bus_share) + pinctrl_pm_select_sleep_state(&fep->pdev->dev); pm_runtime_mark_last_busy(&fep->pdev->dev); pm_runtime_put_autosuspend(&fep->pdev->dev); @@ -2923,6 +3014,7 @@ static void set_multicast_list(struct net_device *ndev) struct netdev_hw_addr *ha; unsigned int i, bit, data, crc, tmp; unsigned char hash; + unsigned int hash_high, hash_low; if (ndev->flags & IFF_PROMISC) { tmp = readl(fep->hwp + FEC_R_CNTRL); @@ -2945,10 +3037,10 @@ static void set_multicast_list(struct net_device *ndev) return; } - /* Clear filter and add the addresses in hash register + /* Add the addresses in hash register */ - writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); - writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW); + hash_high = 0; + hash_low = 0; netdev_for_each_mc_addr(ha, ndev) { /* calculate crc32 value of mac address */ @@ -2968,15 +3060,14 @@ static void set_multicast_list(struct net_device *ndev) hash = (crc >> (32 - FEC_HASH_BITS)) & 0x3f; if (hash > 31) { - tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_HIGH); - tmp |= 1 << (hash - 32); - writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); + hash_high |= 1 << (hash - 32); } else { - tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_LOW); - tmp |= 1 << hash; - writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_LOW); + hash_low |= 1 << hash; } } + + writel_relaxed(hash_high, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); + writel_relaxed(hash_low, fep->hwp + FEC_GRP_HASH_TABLE_LOW); } /* Set a MAC change in hardware. */ @@ -3070,10 +3161,42 @@ static int fec_set_features(struct net_device *netdev, return 0; } +u16 fec_enet_get_raw_vlan_tci(struct sk_buff *skb) +{ + struct vlan_ethhdr *vhdr; + unsigned short vlan_TCI = 0; + + if (skb->protocol == ntohs(ETH_P_ALL)) { + vhdr = (struct vlan_ethhdr *)(skb->data); + vlan_TCI = ntohs(vhdr->h_vlan_TCI); + } + + return vlan_TCI; +} + +u16 fec_enet_select_queue(struct net_device *ndev, struct sk_buff *skb, + void *accel_priv, select_queue_fallback_t fallback) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + const struct platform_device_id *id_entry = + platform_get_device_id(fep->pdev); + u16 vlan_tag; + + if (!(id_entry->driver_data & FEC_QUIRK_HAS_AVB)) + return skb_tx_hash(ndev, skb); + + vlan_tag = fec_enet_get_raw_vlan_tci(skb); + if (!vlan_tag) + return vlan_tag; + + return fec_enet_vlan_pri_to_queue[vlan_tag >> 13]; +} + static const struct net_device_ops fec_netdev_ops = { .ndo_open = fec_enet_open, .ndo_stop = fec_enet_close, .ndo_start_xmit = fec_enet_start_xmit, + .ndo_select_queue = fec_enet_select_queue, .ndo_set_rx_mode = set_multicast_list, .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, @@ -3110,7 +3233,7 @@ static int fec_enet_init(struct net_device *ndev) unsigned dsize_log2 = __fls(dsize); WARN_ON(dsize != (1 << dsize_log2)); -#if defined(CONFIG_ARM) +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) fep->rx_align = 0xf; fep->tx_align = 0xf; #else @@ -3206,7 +3329,7 @@ static int fec_enet_init(struct net_device *ndev) } #ifdef CONFIG_OF -static void fec_reset_phy(struct platform_device *pdev) +static int fec_reset_phy(struct platform_device *pdev) { int err, phy_reset; bool active_high = false; @@ -3214,16 +3337,18 @@ static void fec_reset_phy(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; if (!np) - return; + return 0; - of_property_read_u32(np, "phy-reset-duration", &msec); + err = of_property_read_u32(np, "phy-reset-duration", &msec); /* A sane reset duration should not be longer than 1s */ - if (msec > 1000) + if (!err && msec > 1000) msec = 1; phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0); - if (!gpio_is_valid(phy_reset)) - return; + if (phy_reset == -EPROBE_DEFER) + return phy_reset; + else if (!gpio_is_valid(phy_reset)) + return 0; active_high = of_property_read_bool(np, "phy-reset-active-high"); @@ -3232,7 +3357,7 @@ static void fec_reset_phy(struct platform_device *pdev) "phy-reset"); if (err) { dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err); - return; + return err; } if (msec > 20) @@ -3241,14 +3366,17 @@ static void fec_reset_phy(struct platform_device *pdev) usleep_range(msec * 1000, msec * 1000 + 1000); gpio_set_value_cansleep(phy_reset, !active_high); + + return 0; } #else /* CONFIG_OF */ -static void fec_reset_phy(struct platform_device *pdev) +static int fec_reset_phy(struct platform_device *pdev) { /* * In case of platform probe, the reset has been done * by machine code. */ + return 0; } #endif /* CONFIG_OF */ @@ -3283,6 +3411,41 @@ fec_enet_get_queue_num(struct platform_device *pdev, int *num_tx, int *num_rx) } +static void fec_enet_of_parse_stop_mode(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct device_node *np = pdev->dev.of_node; + struct fec_enet_private *fep = netdev_priv(dev); + struct device_node *node; + phandle phandle; + u32 out_val[3]; + int ret; + + ret = of_property_read_u32_array(np, "stop-mode", out_val, 3); + if (ret) { + dev_dbg(&pdev->dev, "no stop-mode property\n"); + return; + } + + phandle = *out_val; + node = of_find_node_by_phandle(phandle); + if (!node) { + dev_dbg(&pdev->dev, "could not find gpr node by phandle\n"); + return; + } + + fep->gpr.gpr = syscon_node_to_regmap(node); + if (IS_ERR(fep->gpr.gpr)) { + dev_dbg(&pdev->dev, "could not find gpr regmap\n"); + return; + } + + of_node_put(node); + + fep->gpr.req_gpr = out_val[1]; + fep->gpr.req_bit = out_val[2]; +} + static int fec_probe(struct platform_device *pdev) { @@ -3297,6 +3460,8 @@ fec_probe(struct platform_device *pdev) int num_tx_qs; int num_rx_qs; + of_dma_configure(&pdev->dev, np); + fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs); /* Init network device */ @@ -3345,6 +3510,8 @@ fec_probe(struct platform_device *pdev) !of_property_read_bool(np, "fsl,err006687-workaround-present")) fep->quirks |= FEC_QUIRK_ERR006687; + fec_enet_of_parse_stop_mode(pdev); + if (of_get_property(np, "fsl,magic-packet", NULL)) fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET; @@ -3357,6 +3524,7 @@ fec_probe(struct platform_device *pdev) goto failed_phy; } phy_node = of_node_get(np); + fep->fixed_link = true; } fep->phy_node = phy_node; @@ -3371,6 +3539,10 @@ fec_probe(struct platform_device *pdev) fep->phy_interface = ret; } +#if !defined(CONFIG_ARM64) + request_bus_freq(BUS_FREQ_HIGH); +#endif + fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(fep->clk_ipg)) { ret = PTR_ERR(fep->clk_ipg); @@ -3412,6 +3584,9 @@ fec_probe(struct platform_device *pdev) ret = clk_prepare_enable(fep->clk_ipg); if (ret) goto failed_clk_ipg; + ret = clk_prepare_enable(fep->clk_ahb); + if (ret) + goto failed_clk_ahb; fep->reg_phy = devm_regulator_get(&pdev->dev, "phy"); if (!IS_ERR(fep->reg_phy)) { @@ -3419,6 +3594,7 @@ fec_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "Failed to enable phy regulator: %d\n", ret); + clk_disable_unprepare(fep->clk_ipg); goto failed_regulator; } } else { @@ -3431,7 +3607,9 @@ fec_probe(struct platform_device *pdev) pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); - fec_reset_phy(pdev); + ret = fec_reset_phy(pdev); + if (ret) + goto failed_reset; if (fep->bufdesc_ex) fec_ptp_init(pdev); @@ -3456,10 +3634,22 @@ fec_probe(struct platform_device *pdev) fep->irq[i] = irq; } + ret = of_property_read_u32(np, "fsl,wakeup_irq", &irq); + if (!ret && irq < FEC_IRQ_NUM) + fep->wake_irq = fep->irq[irq]; + else + fep->wake_irq = fep->irq[0]; + init_completion(&fep->mdio_done); + + /* board only enable one mii bus in default */ + if (!of_get_property(np, "fsl,mii-exclusive", NULL)) + fep->quirks |= FEC_QUIRK_SINGLE_MDIO; ret = fec_enet_mii_init(pdev); - if (ret) + if (ret) { + dev_id = 0; goto failed_mii_init; + } /* Carrier starts down, phylib will bring it up */ netif_carrier_off(ndev); @@ -3470,6 +3660,11 @@ fec_probe(struct platform_device *pdev) if (ret) goto failed_register; + if (!fep->fixed_link) { + fep->fixups = of_fec_enet_parse_fixup(np); + fec_enet_register_fixup(ndev); + } + device_init_wakeup(&ndev->dev, fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET); @@ -3492,7 +3687,11 @@ failed_init: fec_ptp_stop(pdev); if (fep->reg_phy) regulator_disable(fep->reg_phy); +failed_reset: + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); failed_regulator: +failed_clk_ahb: clk_disable_unprepare(fep->clk_ipg); failed_clk_ipg: fec_enet_clk_enable(ndev, false); @@ -3532,6 +3731,7 @@ static int __maybe_unused fec_suspend(struct device *dev) { struct net_device *ndev = dev_get_drvdata(dev); struct fec_enet_private *fep = netdev_priv(ndev); + int ret = 0; rtnl_lock(); if (netif_running(ndev)) { @@ -3543,9 +3743,21 @@ static int __maybe_unused fec_suspend(struct device *dev) netif_device_detach(ndev); netif_tx_unlock_bh(ndev); fec_stop(ndev); - fec_enet_clk_enable(ndev, false); - if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) + if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) { + fec_irqs_disable(ndev); pinctrl_pm_select_sleep_state(&fep->pdev->dev); + } else { + disable_irq(fep->wake_irq); + enable_irq_wake(fep->wake_irq); + } + fec_enet_clk_enable(ndev, false); + fep->active_in_suspend = !pm_runtime_status_suspended(dev); + if (fep->active_in_suspend) + ret = pm_runtime_force_suspend(dev); + if (ret < 0) + return ret; + } else if (fep->mii_bus_share && !ndev->phydev) { + pinctrl_pm_select_sleep_state(&fep->pdev->dev); } rtnl_unlock(); @@ -3565,8 +3777,7 @@ static int __maybe_unused fec_resume(struct device *dev) { struct net_device *ndev = dev_get_drvdata(dev); struct fec_enet_private *fep = netdev_priv(ndev); - struct fec_platform_data *pdata = fep->pdev->dev.platform_data; - int ret; + int ret = 0; int val; if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) { @@ -3577,14 +3788,18 @@ static int __maybe_unused fec_resume(struct device *dev) rtnl_lock(); if (netif_running(ndev)) { + if (fep->active_in_suspend) + pm_runtime_force_resume(dev); ret = fec_enet_clk_enable(ndev, true); if (ret) { rtnl_unlock(); goto failed_clk; } + if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) { - if (pdata && pdata->sleep_mode_enable) - pdata->sleep_mode_enable(false); + disable_irq_wake(fep->wake_irq); + fec_enet_stop_mode(fep, false); + enable_irq(fep->wake_irq); val = readl(fep->hwp + FEC_ECNTRL); val &= ~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP); writel(val, fep->hwp + FEC_ECNTRL); @@ -3598,10 +3813,14 @@ static int __maybe_unused fec_resume(struct device *dev) netif_tx_unlock_bh(ndev); napi_enable(&fep->napi); phy_start(ndev->phydev); + } else if (fep->mii_bus_share && !ndev->phydev) { + pinctrl_pm_select_default_state(&fep->pdev->dev); + /* And then recovery mii bus */ + ret = fec_restore_mii_bus(ndev); } rtnl_unlock(); - return 0; + return ret; failed_clk: if (fep->reg_phy) @@ -3614,7 +3833,11 @@ static int __maybe_unused fec_runtime_suspend(struct device *dev) struct net_device *ndev = dev_get_drvdata(dev); struct fec_enet_private *fep = netdev_priv(ndev); + clk_disable_unprepare(fep->clk_ahb); clk_disable_unprepare(fep->clk_ipg); +#if !defined(CONFIG_ARM64) + release_bus_freq(BUS_FREQ_HIGH); +#endif return 0; } @@ -3623,8 +3846,23 @@ static int __maybe_unused fec_runtime_resume(struct device *dev) { struct net_device *ndev = dev_get_drvdata(dev); struct fec_enet_private *fep = netdev_priv(ndev); + int ret; - return clk_prepare_enable(fep->clk_ipg); +#if !defined(CONFIG_ARM64) + request_bus_freq(BUS_FREQ_HIGH); +#endif + ret = clk_prepare_enable(fep->clk_ahb); + if (ret) + return ret; + ret = clk_prepare_enable(fep->clk_ipg); + if (ret) + goto failed_clk_ipg; + + return 0; + +failed_clk_ipg: + clk_disable_unprepare(fep->clk_ahb); + return ret; } static const struct dev_pm_ops fec_pm_ops = { |