diff options
Diffstat (limited to 'drivers/net/ethernet/freescale/fec_main.c')
-rw-r--r-- | drivers/net/ethernet/freescale/fec_main.c | 482 |
1 files changed, 427 insertions, 55 deletions
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 051ecc76a7ef..2e5efe6b1469 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,22 @@ 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 | + FEC_QUIRK_DELAYED_CLKS_SUPPORT, + }, { + .name = "imx8mq-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 | + FEC_QUIRK_HAS_EEE, + }, { /* sentinel */ } }; @@ -133,6 +156,8 @@ enum imx_fec_type { MVF600_FEC, IMX6SX_FEC, IMX6UL_FEC, + IMX8QM_FEC, + IMX8MQ_FEC, }; static const struct of_device_id fec_dt_ids[] = { @@ -143,6 +168,8 @@ 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], }, + { .compatible = "fsl,imx8mq-fec", .data = &fec_devtype[IMX8MQ_FEC], }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fec_dt_ids); @@ -191,7 +218,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 @@ -220,7 +248,15 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); #define FEC_WOL_FLAG_ENABLE (0x1 << 1) #define FEC_WOL_FLAG_SLEEP_ON (0x1 << 2) -#define COPYBREAK_DEFAULT 256 +/* By default, set the copybreak to 1518, + * then the RX path always keep DMA memory unchanged, and + * allocate one new skb and copy DMA memory data to the new skb + * buffer, which can improve the performance when SMMU is enabled. + * + * The driver support .set_tunable() interface for ethtool, user + * can dynamicly change the copybreak value. + */ +#define COPYBREAK_DEFAULT 1518 #define TSO_HEADER_SIZE 128 /* Max number of allowed TCP segments for software TSO */ @@ -237,14 +273,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, @@ -904,7 +940,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 @@ -963,6 +999,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 /* @@ -1058,6 +1095,13 @@ fec_restart(struct net_device *ndev) if (fep->bufdesc_ex) ecntl |= (1 << 4); + if (fep->quirks & FEC_QUIRK_DELAYED_CLKS_SUPPORT && + fep->rgmii_txc_dly) + ecntl |= FEC_ENET_TXC_DLY; + if (fep->quirks & FEC_QUIRK_DELAYED_CLKS_SUPPORT && + fep->rgmii_rxc_dly) + ecntl |= FEC_ENET_RXC_DLY; + #ifndef CONFIG_M5272 /* Enable the MIB statistic event counters */ writel(0 << 31, fep->hwp + FEC_MIB_CTRLSTAT); @@ -1081,11 +1125,40 @@ 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; + struct device_node *np = fep->pdev->dev.of_node; + + 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); + } else { + fec_enet_ipg_stop_misc_set(np, 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; @@ -1108,15 +1181,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); @@ -1634,7 +1706,7 @@ static void fec_get_mac(struct net_device *ndev) { struct fec_enet_private *fep = netdev_priv(ndev); struct fec_platform_data *pdata = dev_get_platdata(&fep->pdev->dev); - unsigned char *iap, tmpaddr[ETH_ALEN]; + unsigned char *iap, tmpaddr[ETH_ALEN] = {0}; /* * try to get mac address in following order: @@ -1664,8 +1736,14 @@ static void fec_get_mac(struct net_device *ndev) if (FEC_FLASHMAC) iap = (unsigned char *)FEC_FLASHMAC; #else - if (pdata) + if (pdata) { iap = (unsigned char *)&pdata->mac; + } else { + struct device_node *np = fep->pdev->dev.of_node; + + fec_enet_get_mac_from_fuse(np, tmpaddr); + iap = &tmpaddr[0]; + } #endif } @@ -1769,6 +1847,7 @@ static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum) struct fec_enet_private *fep = bus->priv; struct device *dev = &fep->pdev->dev; unsigned long time_left; + uint int_events; int ret = 0; ret = pm_runtime_get_sync(dev); @@ -1788,9 +1867,12 @@ static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum) usecs_to_jiffies(FEC_MII_TIMEOUT)); if (time_left == 0) { fep->mii_timeout = 1; - netdev_err(fep->netdev, "MDIO read timeout\n"); - ret = -ETIMEDOUT; - goto out; + int_events = readl(fep->hwp + FEC_IEVENT); + if (!(int_events & FEC_ENET_MII)) { + netdev_err(fep->netdev, "MDIO read timeout\n"); + ret = -ETIMEDOUT; + goto out; + } } ret = FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA)); @@ -1846,13 +1928,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); @@ -1870,8 +1949,12 @@ static int fec_enet_clk_enable(struct net_device *ndev, bool enable) if (ret) goto failed_clk_ref; } + if (fep->clk_2x_txclk) { + ret = clk_prepare_enable(fep->clk_2x_txclk); + if (ret) + goto failed_clk_2x_txclk; + } } else { - clk_disable_unprepare(fep->clk_ahb); if (fep->clk_enet_out) clk_disable_unprepare(fep->clk_enet_out); if (fep->clk_ptp) { @@ -1882,22 +1965,44 @@ static int fec_enet_clk_enable(struct net_device *ndev, bool enable) } if (fep->clk_ref) clk_disable_unprepare(fep->clk_ref); + if (fep->clk_2x_txclk) + clk_disable_unprepare(fep->clk_2x_txclk); } return 0; -failed_clk_ref: +failed_clk_2x_txclk: if (fep->clk_ref) clk_disable_unprepare(fep->clk_ref); +failed_clk_ref: + if (fep->clk_ptp) + clk_disable_unprepare(fep->clk_ptp); 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); @@ -1965,6 +2070,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; @@ -1991,6 +2097,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; } @@ -2054,6 +2161,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); } @@ -2064,8 +2173,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; @@ -2109,7 +2220,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, @@ -2544,6 +2656,92 @@ static int fec_enet_set_tunable(struct net_device *netdev, return ret; } +/* LPI Sleep Ts count base on tx clk (clk_ref). + * The lpi sleep cnt value = X us / (cycle_ns) + */ +static int fec_enet_us_to_tx_cycle(struct net_device *ndev, int us) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + + return us * (fep->clk_ref_rate / 1000) / 1000; +} + +static int fec_enet_eee_mode_set(struct net_device *ndev, bool enable) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + struct ethtool_eee *p = &fep->eee; + unsigned int sleep_cycle, wake_cycle; + int ret = 0; + + if (enable) { + ret = phy_init_eee(ndev->phydev, 0); + if (ret) + return ret; + + sleep_cycle = fec_enet_us_to_tx_cycle(ndev, p->tx_lpi_timer); + wake_cycle = sleep_cycle; + } else { + sleep_cycle = 0; + wake_cycle = 0; + } + + p->tx_lpi_enabled = enable; + p->eee_enabled = enable; + p->eee_active = enable; + + writel(sleep_cycle, fep->hwp + FEC_LPI_SLEEP); + writel(wake_cycle, fep->hwp + FEC_LPI_WAKE); + + return 0; +} + +static int +fec_enet_get_eee(struct net_device *ndev, struct ethtool_eee *edata) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + struct ethtool_eee *p = &fep->eee; + + if (!(fep->quirks & FEC_QUIRK_HAS_EEE)) + return -EOPNOTSUPP; + + if (!netif_running(ndev)) + return -ENETDOWN; + + edata->eee_enabled = p->eee_enabled; + edata->eee_active = p->eee_active; + edata->tx_lpi_timer = p->tx_lpi_timer; + edata->tx_lpi_enabled = p->tx_lpi_enabled; + + return phy_ethtool_get_eee(ndev->phydev, edata); +} + +static int +fec_enet_set_eee(struct net_device *ndev, struct ethtool_eee *edata) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + struct ethtool_eee *p = &fep->eee; + int ret = 0; + + if (!(fep->quirks & FEC_QUIRK_HAS_EEE)) + return -EOPNOTSUPP; + + if (!netif_running(ndev)) + return -ENETDOWN; + + p->tx_lpi_timer = edata->tx_lpi_timer; + + if (!edata->eee_enabled || !edata->tx_lpi_enabled || + !edata->tx_lpi_timer) + ret = fec_enet_eee_mode_set(ndev, false); + else + ret = fec_enet_eee_mode_set(ndev, true); + + if (ret) + return ret; + + return phy_ethtool_set_eee(ndev->phydev, edata); +} + static void fec_enet_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) { @@ -2569,15 +2767,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; } @@ -2602,6 +2795,8 @@ static const struct ethtool_ops fec_enet_ethtool_ops = { .set_tunable = fec_enet_set_tunable, .get_wol = fec_enet_get_wol, .set_wol = fec_enet_set_wol, + .get_eee = fec_enet_get_eee, + .set_eee = fec_enet_set_eee, .get_link_ksettings = phy_ethtool_get_link_ksettings, .set_link_ksettings = phy_ethtool_set_link_ksettings, }; @@ -2676,7 +2871,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); @@ -2710,7 +2905,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); @@ -2832,10 +3027,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); @@ -2870,6 +3084,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); @@ -2882,7 +3106,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; } @@ -2900,6 +3125,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(); @@ -2907,7 +3133,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); @@ -3077,10 +3305,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, @@ -3117,7 +3377,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 @@ -3223,9 +3483,9 @@ static int fec_reset_phy(struct platform_device *pdev) if (!np) 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); @@ -3295,6 +3555,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) { @@ -3309,6 +3604,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 */ @@ -3357,9 +3654,17 @@ 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; + if (of_get_property(np, "fsl,rgmii_txc_dly", NULL)) + fep->rgmii_txc_dly = true; + + if (of_get_property(np, "fsl,rgmii_rxc_dly", NULL)) + fep->rgmii_rxc_dly = true; + phy_node = of_parse_phandle(np, "phy-handle", 0); if (!phy_node && of_phy_is_fixed_link(np)) { ret = of_phy_register_fixed_link(np); @@ -3369,6 +3674,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; @@ -3383,6 +3689,8 @@ fec_probe(struct platform_device *pdev) fep->phy_interface = ret; } + request_bus_freq(BUS_FREQ_HIGH); + fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(fep->clk_ipg)) { ret = PTR_ERR(fep->clk_ipg); @@ -3394,7 +3702,6 @@ fec_probe(struct platform_device *pdev) ret = PTR_ERR(fep->clk_ahb); goto failed_clk; } - fep->itr_clk_rate = clk_get_rate(fep->clk_ahb); /* enet_out is optional, depends on board */ @@ -3409,6 +3716,12 @@ fec_probe(struct platform_device *pdev) fep->clk_ref = devm_clk_get(&pdev->dev, "enet_clk_ref"); if (IS_ERR(fep->clk_ref)) fep->clk_ref = NULL; + fep->clk_ref_rate = clk_get_rate(fep->clk_ref); + + /* clk_2x_txclk is optional, depends on board */ + fep->clk_2x_txclk = devm_clk_get(&pdev->dev, "enet_2x_txclk"); + if (IS_ERR(fep->clk_2x_txclk)) + fep->clk_2x_txclk = NULL; fep->bufdesc_ex = fep->quirks & FEC_QUIRK_HAS_BUFDESC_EX; fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp"); @@ -3424,6 +3737,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)) { @@ -3431,10 +3747,13 @@ 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 { + if (PTR_ERR(fep->reg_phy) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto failed_regulator; + } fep->reg_phy = NULL; } @@ -3471,7 +3790,17 @@ 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) goto failed_mii_init; @@ -3485,6 +3814,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); @@ -3508,16 +3842,19 @@ failed_init: if (fep->reg_phy) regulator_disable(fep->reg_phy); failed_reset: - pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); failed_regulator: + clk_disable_unprepare(fep->clk_ahb); +failed_clk_ahb: + clk_disable_unprepare(fep->clk_ipg); failed_clk_ipg: fec_enet_clk_enable(ndev, false); failed_clk: if (of_phy_is_fixed_link(np)) of_phy_deregister_fixed_link(np); -failed_phy: of_node_put(phy_node); +failed_phy: + dev_id--; failed_ioremap: free_netdev(ndev); @@ -3551,6 +3888,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)) { @@ -3562,9 +3900,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(); @@ -3584,8 +3934,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)) { @@ -3596,14 +3945,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); @@ -3617,10 +3970,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) @@ -3633,7 +3990,9 @@ 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); + release_bus_freq(BUS_FREQ_HIGH); return 0; } @@ -3642,8 +4001,21 @@ 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; + + request_bus_freq(BUS_FREQ_HIGH); + 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; - return clk_prepare_enable(fep->clk_ipg); +failed_clk_ipg: + clk_disable_unprepare(fep->clk_ahb); + return ret; } static const struct dev_pm_ops fec_pm_ops = { |